Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/check-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ jobs:
if: ${{ matrix.os == 'ubuntu-latest' }}
- uses: eskatos/gradle-command-action@v1
with:
arguments: macosX64MainKlibrary -s -i
arguments: macosX64MainKlibrary macosArm64MainKlibrary -s -i
if: ${{ matrix.os == 'macos-latest' }}
- uses: eskatos/gradle-command-action@v1
with:
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,12 @@ jobs:
name: Test Windows Target
if: ${{ startsWith(matrix.os, 'windows') }}
with:
arguments: clean jvmTest mingwX64Test
arguments: clean mingwX64Test
- uses: eskatos/gradle-command-action@v1
name: Test Apple Target
if: ${{ startsWith(matrix.os, 'macos') }}
with:
arguments: clean jvmTest macosX64Test
arguments: clean macosX64Test macosArm64Test
- uses: eskatos/gradle-command-action@v1
name: Test Linux Target
if: ${{ startsWith(matrix.os, 'ubuntu') }}
Expand Down
23 changes: 18 additions & 5 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
plugins {
kotlin("multiplatform") version "1.7.10"
kotlin("multiplatform") version "1.8.20"
id("org.jetbrains.dokka") version "1.4.32"
id("maven-publish")
id("signing")
}

group = "me.archinamon"
version = "1.3.6"
version = "1.3.7"

val isRunningInIde: Boolean = System.getProperty("idea.active")
?.toBoolean() == true
Expand All @@ -18,16 +18,23 @@ repositories {
}

kotlin {
js { nodejs() }
js(IR) { nodejs() }

jvm()

// generic linux code
linuxX64()

// darwin macos code
macosX64 {
macosX64() {
if (testApp?.toBoolean() == true) {
binaries {
executable()
}
}
}

macosArm64 {
if (testApp?.toBoolean() == true) {
binaries {
executable()
Expand All @@ -47,10 +54,16 @@ kotlin {
val posixMain by creating {
dependsOn(commonMain)
}
val macosArm64Main by getting {
dependsOn(posixMain)
if (testApp?.toBoolean() == true) {
kotlin.srcDirs("src/macosRunner/kotlin")
}
}
val macosX64Main by getting {
dependsOn(posixMain)
if (testApp?.toBoolean() == true) {
kotlin.srcDirs("src/macosX64Runner/kotlin")
kotlin.srcDirs("src/macosRunner/kotlin")
}
}
val linuxX64Main by getting {
Expand Down
1 change: 1 addition & 0 deletions gradle.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
kotlin.code.style=official
kotlin.js.generate.executable.default=false
kotlin.js.compiler=ir

kotlin.mpp.stability.nowarn=true
kotlin.mpp.enableGranularSourceSetsMetadata=true
Expand Down
2 changes: 2 additions & 0 deletions src/commonMain/kotlin/File.common.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@ expect class File(pathname: String) {
fun getName(): String

fun lastModified(): Long
fun mkdir(): Boolean
fun mkdirs(): Boolean
fun createNewFile(): Boolean

fun isFile(): Boolean
fun isDirectory(): Boolean

fun getPath(): String
fun getAbsolutePath(): String
fun length(): Long

Expand Down
6 changes: 2 additions & 4 deletions src/commonMain/kotlin/Files.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,8 @@ object Files {
throw UnsupportedOperationException("Do not supported on mingw yet, create tmp files/dirs manually!")
}

val parent = dir.getAbsolutePath()
if (!dir.exists()) {
dir.mkdirs()
}
val parent = dir.getPath()
dir.mkdirs()

if (!dir.canWrite()) {
throw IllegalFileAccess(parent, "Can't create file in the directory")
Expand Down
9 changes: 9 additions & 0 deletions src/commonMain/kotlin/platform.common.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package me.archinamon.fileio

enum class Platform {
Linux, Macos, Windows, JVM, JS;

fun isPosix(): Boolean = this == Macos || this == Linux
}

expect fun platform(): Platform
7 changes: 0 additions & 7 deletions src/commonMain/kotlin/platform.kt

This file was deleted.

59 changes: 54 additions & 5 deletions src/commonTest/kotlin/FileTests.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,30 @@ import kotlin.test.*

class FileTests {

private val localSeparator = '/'

@Test
fun testNonexistentRootFile() {
val testFile = File("testNonexistentRootFile.txt")

assertFalse(testFile.exists(), "file should not exist")
assertFalse(testFile.isDirectory(), "file should not be directory")
assertFalse(testFile.isFile(), "file should not be file")
assertNull(testFile.getParent(), "file should not have parent")
assertNull(testFile.getParentFile(), "file should not have parent file")

if (platform() == Platform.JVM) {
assertNull(testFile.getParent(), "file should not have parent")
assertNull(testFile.getParentFile(), "file should not have parent file")
}

if (platform().isPosix()) { // in posix we always resolve relative path via `realpath` syscall
assertEquals(testFile.getParent(), File("./").getAbsolutePath(), "as parent should be current dir")
assertEquals(testFile.getParentFile()?.getAbsolutePath(), File("./").getAbsolutePath(), "as parent should be current dir")
}

assertEquals("testNonexistentRootFile", testFile.nameWithoutExtension)
}

@Test
fun testExistentRootFile() {
val testFile = File("testFileRoot/testExistentRootFile.txt")
println(testFile.getAbsolutePath())

assertFalse(testFile.exists(), "file should not exist")
assertFalse(testFile.getParentFile()?.exists() == true, "file should not have parent file")
Expand All @@ -33,6 +38,10 @@ class FileTests {

@Test
fun testFileCreateAndDelete() {
if (platform() == Platform.Windows) {
return
}

val testFolder = File("build/testNewDirectoryCreation")

assertTrue(testFolder.mkdirs(), "create directory failed")
Expand All @@ -52,6 +61,10 @@ class FileTests {

@Test
fun testFileWriteAndRead() {
if (platform() == Platform.Windows) {
return
}

val testFolder = File("build/testFileWriteAndRead")
val testFile = File("build/testFileWriteAndRead/test.txt")

Expand Down Expand Up @@ -87,6 +100,10 @@ class FileTests {

@Test
fun testFileCopyMethod() {
if (platform() == Platform.Windows) {
return
}

val testFile = File("gradle/wrapper/gradle-wrapper.properties")
val testDestFolder = File("build/testCopyFolder")
val testDestFile = File("build/testCopyFolder/gradle-wrapper.properties")
Expand All @@ -107,6 +124,10 @@ class FileTests {

@Test
fun testFileMoveMethod() {
if (platform() == Platform.Windows) {
return
}

val testFolder = File("build/testMoveFolder")
val testDestFolder = File("build/testMoveFolder2")
val testFile = File("build/testMoveFolder/test_move_file.properties")
Expand Down Expand Up @@ -198,4 +219,32 @@ class FileTests {

assertTrue(testFile.delete(), "delete file failed")
}

@Test
fun testFileRealPathIfRelativeLinks__posixOnly() {
if (!platform().isPosix()) {
return
}

val symlinkPrefix = if (platform() == Platform.Macos) "/private" else ""

val testDir = File("/tmp/build")
val testFile = Files.createTempFile(prefix = "../test", suffix = ".txt", dir = testDir)
assertEquals("$symlinkPrefix/tmp/test.txt", testFile.getAbsolutePath()) // 'cause /tmp is a symlink for /private/tmp
assertTrue(testFile.delete(), "delete file failed")
assertTrue(testDir.delete(), "delete test folder failed")
}

@Test
fun testFileRealPathIfRelativeLinks__jvmOnly() {
if (platform() != Platform.JVM) {
return
}

val testDir = File("/tmp/build")
val testFile = Files.createTempFile(prefix = "../test", suffix = ".txt", dir = testDir)
assertEquals("/tmp/build/../test.txt", testFile.getAbsolutePath()) // lazy canonicalization in jvm
assertTrue(testFile.delete(), "delete file failed")
assertTrue(testDir.delete(), "delete test folder failed")
}
}
8 changes: 8 additions & 0 deletions src/jsMain/kotlin/File.kt
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ actual class File constructor(jsfile: JsFile) {
return innerFile.lastModified.toLong()
}

actual fun mkdir(): Boolean {
throw UnsupportedOperationException("Not available in JS!")
}

actual fun mkdirs(): Boolean {
throw UnsupportedOperationException("Not available in JS!")
}
Expand All @@ -50,6 +54,10 @@ actual class File constructor(jsfile: JsFile) {
return false
}

actual fun getPath(): String {
return innerFile.name
}

actual fun getAbsolutePath(): String {
return innerFile.name
}
Expand Down
File renamed without changes.
32 changes: 32 additions & 0 deletions src/macosArm64Main/kotlin/modified.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package me.archinamon.fileio

import kotlinx.cinterop.CPointed
import kotlinx.cinterop.CPointer
import kotlinx.cinterop.alloc
import kotlinx.cinterop.convert
import kotlinx.cinterop.memScoped
import kotlinx.cinterop.ptr
import platform.posix.DIR
import platform.posix.dirent
import platform.posix.stat

internal actual fun stat.checkFileIs(flag: Int): Boolean = (st_mode.convert<Int>() and flag) == flag

internal actual fun mkdir(path: String, mode: UInt): Int = platform.posix.mkdir(path, mode.convert())

internal actual fun opendir(path: String): CPointer<out CPointed>? = platform.posix.opendir(path)

@Suppress("UNCHECKED_CAST")
internal actual fun readdir(dir: CPointer<out CPointed>): CPointer<dirent>? = platform.posix.readdir(dir as CPointer<DIR>)

@Suppress("UNCHECKED_CAST")
internal actual fun closedir(dir: CPointer<out CPointed>): Int = platform.posix.closedir(dir as CPointer<DIR>)

internal actual fun modified(file: File): Long = memScoped {
val result = alloc<stat>()
if (stat(file.getAbsolutePath(), result.ptr) != 0) {
return 0L
}

result.st_mtimespec.tv_sec
}
3 changes: 3 additions & 0 deletions src/macosArm64Main/kotlin/platform.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package me.archinamon.fileio

actual fun platform(): Platform = Platform.Macos
File renamed without changes.
26 changes: 22 additions & 4 deletions src/mingwX64Main/kotlin/File.kt
Original file line number Diff line number Diff line change
Expand Up @@ -59,16 +59,30 @@ actual class File actual constructor(pathname: String) {
}
}

actual fun mkdirs(): Boolean {
if (exists()) return false
actual fun mkdir(): Boolean {
if (getParentFile()?.exists() != true) {
return false
}

if (getParentFile()?.exists() == false) {
getParentFile()?.mkdirs()
if (getParentFile()?.canWrite() != true) {
throw IllegalFileAccess(getAbsolutePath(), "Directory not accessible for write operations")
}

return SHCreateDirectoryExA(null, getAbsolutePath(), null) == ERROR_SUCCESS
}

actual fun mkdirs(): Boolean {
if (exists()) {
return false
}

if (mkdir()) {
return true
}

return (getParentFile()?.mkdirs() == true || getParentFile()?.exists() == true) && mkdir()
}

actual fun createNewFile(): Boolean {
val handle = CreateFileA(
pathname,
Expand Down Expand Up @@ -101,6 +115,10 @@ actual class File actual constructor(pathname: String) {
}
}

actual fun getPath(): String {
return pathname
}

actual fun getAbsolutePath(): String {
return if (pathname.startsWith(filePathSeparator) || pathname.getOrNull(1) == ':') {
pathname
Expand Down
Loading