diff --git a/build.gradle.kts b/build.gradle.kts index b161b79..331d25b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,7 +1,6 @@ -import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet - val ktor_version: String by project val kotlinx_cli_version: String by project +val mordant_version: String by project plugins { kotlin("multiplatform") version "1.5.31" @@ -26,12 +25,13 @@ kotlin { compilations["main"].enableEndorsedLibs = true } mingwX64 { + compilations["main"].enableEndorsedLibs = true + binaries { executable { entryPoint = "main" } } - compilations["main"].enableEndorsedLibs = true } macosX64("macosX64") { binaries { @@ -41,6 +41,7 @@ kotlin { } compilations["main"].enableEndorsedLibs = true } + sourceSets { val nativeMain by creating { dependencies { @@ -50,6 +51,7 @@ kotlin { implementation("com.squareup.okio:okio:3.0.0") implementation("io.ktor:ktor-client-curl:$ktor_version") implementation("org.jetbrains.kotlinx:kotlinx-cli:$kotlinx_cli_version") + implementation("com.github.ajalt.mordant:mordant:$mordant_version") } } val nativeTest by creating { diff --git a/gradle.properties b/gradle.properties index 919af0f..a0b4022 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,5 +3,6 @@ kotlin.mpp.enableGranularSourceSetsMetadata=true kotlin.native.enableDependencyPropagation=false ktor_version=1.6.4 kotlinx_cli_version=0.3.3 +mordant_version=2.0.0-beta4 kotlin.mpp.enableCInteropCommonization=true kotlin.internal.mpp.hierarchicalStructureByDefault=true \ No newline at end of file diff --git a/src/linuxX64Main/kotlin/io/ktor/generator/cli/installer/Utils.kt b/src/linuxX64Main/kotlin/io/ktor/generator/cli/installer/Utils.kt index 0b1f493..647ebbb 100644 --- a/src/linuxX64Main/kotlin/io/ktor/generator/cli/installer/Utils.kt +++ b/src/linuxX64Main/kotlin/io/ktor/generator/cli/installer/Utils.kt @@ -11,7 +11,13 @@ actual val jdkArchiveName: String = "openjdk-11.tar.gz" actual fun unpackJdk(archive: File, outputDir: Directory) { val tempPath = Directory.current().path runProcess("tar -xvf ${archive.path} -C $tempPath") - FileSystem.SYSTEM.atomicMove("$tempPath/jdk-11.jdk".toPath(), outputDir.path.toPath()) + FileSystem.SYSTEM.atomicMove("$tempPath/jdk-11".toPath(), outputDir.path.toPath()) } -actual fun isGradleWrapper(file: File): Boolean = file.name.contains("gradlew") \ No newline at end of file +actual fun isGradleWrapper(file: File): Boolean = file.name.contains("gradlew") + +actual fun setEnv(varName: String, value: String) { + platform.posix.setenv(varName, value, 1) +} + +actual fun getJdkContentsHome(directory: Directory?): Directory? = directory \ No newline at end of file diff --git a/src/linuxX64Main/kotlin/io/ktor/generator/cli/utils/FileSystemUtils.kt b/src/linuxX64Main/kotlin/io/ktor/generator/cli/utils/FileSystemUtils.kt index ebd53f5..67be890 100644 --- a/src/linuxX64Main/kotlin/io/ktor/generator/cli/utils/FileSystemUtils.kt +++ b/src/linuxX64Main/kotlin/io/ktor/generator/cli/utils/FileSystemUtils.kt @@ -4,11 +4,13 @@ import io.ktor.client.* import io.ktor.client.features.json.* import io.ktor.client.features.json.serializer.* import io.ktor.generator.cli.installer.* -import kotlinx.cinterop.pointed -import kotlinx.cinterop.toKString +import kotlinx.cinterop.* +import platform.posix.getcwd import platform.posix.getpwuid import platform.posix.getuid +import platform.posix.realpath import kotlin.text.* +import kotlinx.cinterop.allocArray actual val FS_DELIMETER: String = "/" @@ -21,4 +23,10 @@ actual fun homePath(): String = actual fun addExecutablePermissions(file: File) { runProcess("chmod +x ${file.path}") -} \ No newline at end of file +} + +actual fun realPath(path: String, buffer: CPointer): String? { + return realpath(path, buffer)?.toKString() +} + +actual fun getCwd(buffer: CPointer, size: Int) = getcwd(buffer, size.toULong()) \ No newline at end of file diff --git a/src/linuxX64Main/kotlin/io/ktor/generator/cli/utils/ProcessUtils.kt b/src/linuxX64Main/kotlin/io/ktor/generator/cli/utils/ProcessUtils.kt new file mode 100644 index 0000000..6f2c5c6 --- /dev/null +++ b/src/linuxX64Main/kotlin/io/ktor/generator/cli/utils/ProcessUtils.kt @@ -0,0 +1,9 @@ +package io.ktor.generator.cli.utils + +import platform.posix.pclose +import platform.posix.popen +import kotlinx.cinterop.CPointer +import platform.posix.FILE + +actual fun openPipe(command: String, access: String): CPointer? = popen(command, access) +actual fun closePipe(filePtr: CPointer): Int = pclose(filePtr) \ No newline at end of file diff --git a/src/macosX64Main/kotlin/io/ktor/generator/cli/installer/Utils.kt b/src/macosX64Main/kotlin/io/ktor/generator/cli/installer/Utils.kt index dd0f00d..5076a4a 100644 --- a/src/macosX64Main/kotlin/io/ktor/generator/cli/installer/Utils.kt +++ b/src/macosX64Main/kotlin/io/ktor/generator/cli/installer/Utils.kt @@ -1,11 +1,22 @@ package io.ktor.generator.cli.installer +import io.ktor.generator.cli.installer.Architecture.* import io.ktor.generator.cli.utils.* +import kotlinx.cinterop.alloc +import kotlinx.cinterop.memScoped +import kotlinx.cinterop.ptr +import kotlinx.cinterop.toKString import okio.FileSystem import okio.Path.Companion.toPath +import platform.posix.uname +import platform.posix.utsname actual val rootKtorDirName: String = ".ktor" -actual val jdkDownloadUrl: String = "https://download.java.net/java/ga/jdk11/openjdk-11_osx-x64_bin.tar.gz" +actual val jdkDownloadUrl: String + get() = when (getArchitecture()) { + X86_64 -> "https://download.java.net/java/ga/jdk11/openjdk-11_osx-x64_bin.tar.gz" + ARM_64 -> "https://cdn.azul.com/zulu/bin/zulu11.54.25-ca-jdk11.0.14.1-macosx_aarch64.tar.gz" + } actual val jdkArchiveName: String = "openjdk-11.tar.gz" actual fun unpackJdk(archive: File, outputDir: Directory) { @@ -14,4 +25,29 @@ actual fun unpackJdk(archive: File, outputDir: Directory) { FileSystem.SYSTEM.atomicMove("$tempPath/jdk-11.jdk".toPath(), outputDir.path.toPath()) } -actual fun isGradleWrapper(file: File): Boolean = file.name.contains("gradlew") \ No newline at end of file +actual fun isGradleWrapper(file: File): Boolean = file.name.contains("gradlew") + +actual fun setEnv(varName: String, value: String) { + platform.posix.setenv(varName, value, 1) +} + +actual fun getJdkContentsHome(directory: Directory?): Directory? = + directory + ?.subdir(KtorInstaller.JAVA_CONTENTS) + ?.subdir(KtorInstaller.JAVA_CONTENTS_HOME) + +private enum class Architecture { + X86_64, ARM_64 +} + +private fun getArchitecture(): Architecture { + val architectureName = memScoped { + val systemInfo = alloc() + uname(systemInfo.ptr) + systemInfo.machine.toKString() + } + return when (architectureName) { + "x86_64" -> X86_64 + else -> ARM_64 + } +} \ No newline at end of file diff --git a/src/macosX64Main/kotlin/io/ktor/generator/cli/utils/FileSystemUtils.kt b/src/macosX64Main/kotlin/io/ktor/generator/cli/utils/FileSystemUtils.kt index 2c36c80..9aa85e4 100644 --- a/src/macosX64Main/kotlin/io/ktor/generator/cli/utils/FileSystemUtils.kt +++ b/src/macosX64Main/kotlin/io/ktor/generator/cli/utils/FileSystemUtils.kt @@ -1,9 +1,14 @@ package io.ktor.generator.cli.utils +import kotlinx.cinterop.ByteVar +import kotlinx.cinterop.CPointer import kotlinx.cinterop.pointed import kotlinx.cinterop.toKString import platform.posix.getpwuid import platform.posix.getuid +import platform.posix.realpath +import kotlinx.cinterop.allocArray +import platform.posix.getcwd actual val FS_DELIMETER: String = "/" @@ -16,4 +21,10 @@ actual fun homePath(): String = actual fun addExecutablePermissions(file: File) { runProcess("chmod +x ${file.path}") -} \ No newline at end of file +} + +actual fun realPath(path: String, buffer: CPointer): String? { + return realpath(path, buffer)?.toKString() +} + +actual fun getCwd(buffer: CPointer, size: Int) = getcwd(buffer, size.toULong()) \ No newline at end of file diff --git a/src/macosX64Main/kotlin/io/ktor/generator/cli/utils/ProcessUtils.kt b/src/macosX64Main/kotlin/io/ktor/generator/cli/utils/ProcessUtils.kt new file mode 100644 index 0000000..6f2c5c6 --- /dev/null +++ b/src/macosX64Main/kotlin/io/ktor/generator/cli/utils/ProcessUtils.kt @@ -0,0 +1,9 @@ +package io.ktor.generator.cli.utils + +import platform.posix.pclose +import platform.posix.popen +import kotlinx.cinterop.CPointer +import platform.posix.FILE + +actual fun openPipe(command: String, access: String): CPointer? = popen(command, access) +actual fun closePipe(filePtr: CPointer): Int = pclose(filePtr) \ No newline at end of file diff --git a/src/mingwX64Main/c_interop/WinApi.def b/src/mingwX64Main/c_interop/WinApi.def deleted file mode 100644 index 8263b1e..0000000 --- a/src/mingwX64Main/c_interop/WinApi.def +++ /dev/null @@ -1,6 +0,0 @@ -headers = direct.h -headerFilter = curl/* - -compilerOpts.linux = -I/usr/include -I/usr/include/x86_64-linux-gnu -linkerOpts.osx = -L/opt/local/lib -L/usr/local/opt/curl/lib -lcurl -linkerOpts.linux = -L/usr/lib/x86_64-linux-gnu -lcurl \ No newline at end of file diff --git a/src/mingwX64Main/interop/libcurl.def b/src/mingwX64Main/interop/libcurl.def new file mode 100644 index 0000000..73daacc --- /dev/null +++ b/src/mingwX64Main/interop/libcurl.def @@ -0,0 +1,9 @@ +package = libcurl +headers = curl.h +headerFilter = * +linkerOpts.osx = -L/opt/local/lib -L/usr/local/opt/curl/lib -lcurl -L/opt/homebrew/opt/curl/include/curl +compilerOpts.osx = -I/opt/local/include/curl -I/usr/bin/curl -I/usr/local/include/curl -I/usr/include/curl -I/usr/local/Cellar/curl/7.81.0/include/curl -I/usr/local/Cellar/curl/7.80.0_1/include/curl -I/usr/local/Cellar/curl/7.80.0/include/curl -I/usr/local/Cellar/curl/7.62.0/include/curl -I/usr/local/Cellar/curl/7.63.0/include/curl -I/usr/local/Cellar/curl/7.65.3/include/curl -I/usr/local/Cellar/curl/7.66.0/include/curl -I/opt/homebrew/opt/curl/include/curl +linkerOpts.linux = -L/usr/lib64 -L/usr/lib/x86_64-linux-gnu -lcurl -L/opt/homebrew/opt/curl/include/curl +compilerOpts.linux = -I/usr/include/curl -I/usr/include/x86_64-linux-gnu/curl -I/opt/homebrew/opt/curl/include/curl +linkerOpts.mingw_x64 = -LC:/msys64/mingw64/lib -LC:/Tools/msys64/mingw64/lib -LC:/Tools/msys2/mingw64/lib -lcurl -L/opt/homebrew/opt/curl/include/curl +compilerOpts.mingw_x64 = -I/usr/include/curl -I/usr/include/x86_64-linux-gnu/curl -I/opt/homebrew/opt/curl/include/curl \ No newline at end of file diff --git a/src/mingwX64Main/kotlin/io/ktor/generator/cli/installer/Utils.kt b/src/mingwX64Main/kotlin/io/ktor/generator/cli/installer/Utils.kt index 29db778..bcb4135 100644 --- a/src/mingwX64Main/kotlin/io/ktor/generator/cli/installer/Utils.kt +++ b/src/mingwX64Main/kotlin/io/ktor/generator/cli/installer/Utils.kt @@ -3,6 +3,7 @@ package io.ktor.generator.cli.installer import io.ktor.generator.cli.utils.* import okio.FileSystem import okio.Path.Companion.toPath +import platform.windows.* actual val rootKtorDirName: String = ".ktor." actual val jdkDownloadUrl: String = "https://download.java.net/java/ga/jdk11/openjdk-11_windows-x64_bin.zip" @@ -14,4 +15,10 @@ actual fun unpackJdk(archive: File, outputDir: Directory) { FileSystem.SYSTEM.atomicMove("${Directory.current().path}\\jdk-11".toPath(), outputDir.path.toPath()) } -actual fun isGradleWrapper(file: File): Boolean = file.name.contains("gradlew") \ No newline at end of file +actual fun isGradleWrapper(file: File): Boolean = file.name.contains("gradlew") + +actual fun setEnv(varName: String, value: String) { + SetEnvironmentVariableA(varName, value) +} + +actual fun getJdkContentsHome(directory: Directory?): Directory? = directory \ No newline at end of file diff --git a/src/mingwX64Main/kotlin/io/ktor/generator/cli/utils/FileSystemUtils.kt b/src/mingwX64Main/kotlin/io/ktor/generator/cli/utils/FileSystemUtils.kt index 9316574..d2c093b 100644 --- a/src/mingwX64Main/kotlin/io/ktor/generator/cli/utils/FileSystemUtils.kt +++ b/src/mingwX64Main/kotlin/io/ktor/generator/cli/utils/FileSystemUtils.kt @@ -1,6 +1,12 @@ package io.ktor.generator.cli.utils import io.ktor.generator.cli.installer.* +import io.ktor.utils.io.core.* +import kotlinx.cinterop.ByteVar +import kotlinx.cinterop.CPointer +import kotlinx.cinterop.allocArray +import kotlinx.cinterop.toKString +import platform.posix.* actual val FS_DELIMETER: String = "\\" @@ -11,4 +17,10 @@ actual fun unzip(zipFile: File, outputDir: Directory) { actual fun homePath(): String = getEnv("USERPROFILE") ?: throw Exception("Couldn't locate user home path") -actual fun addExecutablePermissions(file: File) {} \ No newline at end of file +actual fun addExecutablePermissions(file: File) {} + +actual fun realPath(path: String, buffer: CPointer): String? { + return _fullpath(buffer, path, PATH_MAX)?.toKString() +} + +actual fun getCwd(buffer: CPointer, size: Int) = _getcwd(buffer, size) \ No newline at end of file diff --git a/src/mingwX64Main/kotlin/io/ktor/generator/cli/utils/ProcessUtils.kt b/src/mingwX64Main/kotlin/io/ktor/generator/cli/utils/ProcessUtils.kt new file mode 100644 index 0000000..5f2fd3e --- /dev/null +++ b/src/mingwX64Main/kotlin/io/ktor/generator/cli/utils/ProcessUtils.kt @@ -0,0 +1,9 @@ +package io.ktor.generator.cli.utils + +import platform.posix._pclose +import platform.posix._popen +import kotlinx.cinterop.CPointer +import platform.posix.FILE + +actual fun openPipe(command: String, access: String): CPointer? = _popen(command, access) +actual fun closePipe(filePtr: CPointer): Int = _pclose(filePtr) \ No newline at end of file diff --git a/src/nativeMain/kotlin/CliGeneratorMain.kt b/src/nativeMain/kotlin/CliGeneratorMain.kt index b328ea1..107e589 100644 --- a/src/nativeMain/kotlin/CliGeneratorMain.kt +++ b/src/nativeMain/kotlin/CliGeneratorMain.kt @@ -33,11 +33,11 @@ class GenerateProject(client: HttpClient) : KtorCommand( } class RunProject(client: HttpClient) : KtorCommand( - "run", description = PropertiesBundle.message("run.command.description"), client = client + "start", description = PropertiesBundle.message("run.command.description"), client = client ) { private val args: List by argument( ArgType.String, fullName = "args", description = PropertiesBundle.message("run.arguments.description") - ).vararg() + ).optional().vararg() override fun execute() { ktorInstaller.runKtorProject(projectName, args) diff --git a/src/nativeMain/kotlin/io/ktor/generator/bundle/PropertiesBundle.kt b/src/nativeMain/kotlin/io/ktor/generator/bundle/PropertiesBundle.kt index f8e1019..d2b3dbe 100644 --- a/src/nativeMain/kotlin/io/ktor/generator/bundle/PropertiesBundle.kt +++ b/src/nativeMain/kotlin/io/ktor/generator/bundle/PropertiesBundle.kt @@ -1,5 +1,8 @@ package io.ktor.generator.bundle +import com.github.ajalt.mordant.rendering.TextColors.green +import com.github.ajalt.mordant.rendering.TextColors.red +import com.github.ajalt.mordant.terminal.Terminal import kotlin.test.assertNotNull // TODO: figure out how to read properties from resources and deploy with project in Kotlin/Native @@ -18,8 +21,9 @@ object PropertiesBundle { "project.already.exists" to "Project with name {0} already exists", "generating.project" to "Generating your ktor project", "project.downloaded" to "Project \"{0}\" was downloaded. Running gradle setup...", - "project.generated" to "Project \"{0}\" was successfully generated.\nYou can execute `ktor run {0}` to run it", - "project.not.exists" to "Project {0} does not exist" + "project.generated" to "Project \"{0}\" was successfully generated.\nYou can execute `ktor start {0}` to start it", + "project.not.exists" to "Project {0} does not exist", + "project.not.have.gradlew" to "Invalid project. Project \"{0}\" does not have gradlew file" ) private val argumentRegex = "\\{\\d+}".toRegex() @@ -44,4 +48,12 @@ object PropertiesBundle { } fun writeMessage(property: String, vararg args: String) = println(message(property, *args)) + fun writeErrorMessage(property: String, vararg args: String) { + println() + Terminal().println(red(message(property, *args))) + } + fun writeSuccessMessage(property: String, vararg args: String) { + println() + Terminal().println(green(message(property, *args))) + } } \ No newline at end of file diff --git a/src/nativeMain/kotlin/io/ktor/generator/cli/installer/KtorInstaller.kt b/src/nativeMain/kotlin/io/ktor/generator/cli/installer/KtorInstaller.kt index df860d1..fdd3139 100644 --- a/src/nativeMain/kotlin/io/ktor/generator/cli/installer/KtorInstaller.kt +++ b/src/nativeMain/kotlin/io/ktor/generator/cli/installer/KtorInstaller.kt @@ -6,14 +6,13 @@ import io.ktor.generator.cli.utils.* import io.ktor.generator.configuration.json.* import kotlinx.coroutines.runBlocking import platform.posix.chdir -import platform.posix.setenv class KtorInstaller(private val service: KtorGeneratorWeb) { private val ktorRootDir: Directory by lazy { Directory.home().createDirIfNeeded(rootKtorDirName) } private val ktorRcFile: File by lazy { ktorRootDir.createFileIfNeeded(KTOR_RC_FILENAME) } private fun runGradle(gradleFile: File, task: String, javaHome: String, args: List = emptyList()) { - setenv(JAVA_HOME, javaHome, 1) + setEnv(JAVA_HOME, javaHome) addExecutablePermissions(gradleFile) runProcess("${gradleFile.path} $task ${args.joinToString(" ")}") } @@ -41,8 +40,7 @@ class KtorInstaller(private val service: KtorGeneratorWeb) { .content() .filterIsInstance() .find { it.name == JDK_INSTALLED_DIR_PATH } - ?.subdir(JAVA_CONTENTS) - ?.subdir(JAVA_CONTENTS_HOME) + ?.let(::getJdkContentsHome) private fun customJdkIsInstalled(): Boolean = findCustomJdk() != null @@ -87,7 +85,7 @@ class KtorInstaller(private val service: KtorGeneratorWeb) { val currentDir = Directory.current() if (currentDir.subdir(projectName).exists()) { - PropertiesBundle.writeMessage("project.already.exists", projectName) + PropertiesBundle.writeErrorMessage("project.already.exists", projectName) return } @@ -119,13 +117,17 @@ class KtorInstaller(private val service: KtorGeneratorWeb) { PropertiesBundle.writeMessage("project.downloaded", projectName) val ktorJavaHome = getRcProperty(JAVA_HOME)!! - val gradleFile = projectDir.gradleWrapper() ?: return + val gradleFile = projectDir.gradleWrapper() + if (gradleFile == null) { + PropertiesBundle.writeErrorMessage("project.not.have.gradlew", projectDir.name) + return + } chdir(projectDir.path) runGradle(gradleFile, GRADLE_BUILD, ktorJavaHome) chdir(currentDir.path) - PropertiesBundle.writeMessage("project.generated", projectName) + PropertiesBundle.writeSuccessMessage("project.generated", projectName) } fun runKtorProject(path: String, args: List) { @@ -135,11 +137,15 @@ class KtorInstaller(private val service: KtorGeneratorWeb) { val currentDir = Directory.current() val projectDir = currentDir.subdir(path) if (!projectDir.exists()) { - PropertiesBundle.writeMessage("project.not.exists", path) + PropertiesBundle.writeErrorMessage("project.not.exists", path) return } - val gradleFile = projectDir.gradleWrapper() ?: return + val gradleFile = projectDir.gradleWrapper() + if (gradleFile == null) { + PropertiesBundle.writeErrorMessage("project.not.have.gradlew", projectDir.name) + return + } chdir(projectDir.path) runGradle(gradleFile, GRADLE_RUN, ktorJavaHome, args) chdir(currentDir.path) diff --git a/src/nativeMain/kotlin/io/ktor/generator/cli/installer/Utils.kt b/src/nativeMain/kotlin/io/ktor/generator/cli/installer/Utils.kt index ab9c788..92c777e 100644 --- a/src/nativeMain/kotlin/io/ktor/generator/cli/installer/Utils.kt +++ b/src/nativeMain/kotlin/io/ktor/generator/cli/installer/Utils.kt @@ -11,4 +11,6 @@ expect val jdkDownloadUrl: String expect val jdkArchiveName: String expect fun unpackJdk(archive: File, outputDir: Directory) -expect fun isGradleWrapper(file: File): Boolean \ No newline at end of file +expect fun isGradleWrapper(file: File): Boolean +expect fun setEnv(varName: String, value: String) +expect fun getJdkContentsHome(directory: Directory?): Directory? \ No newline at end of file diff --git a/src/nativeMain/kotlin/io/ktor/generator/cli/utils/Common.kt b/src/nativeMain/kotlin/io/ktor/generator/cli/utils/Common.kt index 945db84..1cf6b5d 100644 --- a/src/nativeMain/kotlin/io/ktor/generator/cli/utils/Common.kt +++ b/src/nativeMain/kotlin/io/ktor/generator/cli/utils/Common.kt @@ -4,7 +4,6 @@ import kotlinx.cinterop.addressOf import kotlinx.cinterop.toKString import kotlinx.cinterop.usePinned import platform.posix.PATH_MAX -import platform.posix.realpath expect val RESOURCES_PATH: String @@ -13,7 +12,7 @@ fun getResourcePath(path: String): String { // Remove all '..' and '.' val buffer = ByteArray(PATH_MAX) val standardized = buffer.usePinned { - realpath(filePath, it.addressOf(0))?.toKString() + realPath(filePath, it.addressOf(0)) } return standardized ?: filePath } diff --git a/src/nativeMain/kotlin/io/ktor/generator/cli/utils/FileSystemUtils.kt b/src/nativeMain/kotlin/io/ktor/generator/cli/utils/FileSystemUtils.kt index fee4b2a..e9a8167 100644 --- a/src/nativeMain/kotlin/io/ktor/generator/cli/utils/FileSystemUtils.kt +++ b/src/nativeMain/kotlin/io/ktor/generator/cli/utils/FileSystemUtils.kt @@ -9,7 +9,7 @@ import okio.Path.Companion.toPath import okio.buffer import platform.posix.FILE import platform.posix.fgets -import platform.posix.getcwd +import kotlinx.cinterop.allocArray private const val READ_MODE = "r" @@ -22,6 +22,18 @@ expect fun addExecutablePermissions(file: File) expect fun homePath(): String +expect fun realPath(path: String, buffer: CPointer): String? + +expect fun getCwd(buffer: CPointer, size: Int): CPointer? + +internal fun pwd(): String = memScoped { + val pathBufferSize = 1024 + val pathBuffer: CArrayPointer = allocArray(pathBufferSize) + getCwd(pathBuffer, pathBufferSize) ?: throw Exception("Failed to locate working dir") + + return@memScoped pathBuffer.toKString() +} + interface FsUnit { val path: String val name: String get() = path.split(FS_DELIMETER).last() @@ -81,13 +93,7 @@ data class Directory(override val path: String) : FsUnit { companion object { fun home(): Directory = Directory(homePath()) - fun current(): Directory = memScoped { - val pathBufferSize = 1024 - val pathBuffer = allocArray(pathBufferSize) - getcwd(pathBuffer, pathBufferSize.toULong()) ?: throw Exception("Failed to locate working dir") - - Directory(pathBuffer.toKString()) - } + fun current(): Directory = memScoped { Directory(pwd()) } } } diff --git a/src/nativeMain/kotlin/io/ktor/generator/cli/utils/ProcessUtils.kt b/src/nativeMain/kotlin/io/ktor/generator/cli/utils/ProcessUtils.kt index fb29ca5..85496df 100644 --- a/src/nativeMain/kotlin/io/ktor/generator/cli/utils/ProcessUtils.kt +++ b/src/nativeMain/kotlin/io/ktor/generator/cli/utils/ProcessUtils.kt @@ -1,14 +1,17 @@ package io.ktor.generator.cli.utils import io.ktor.generator.bundle.* +import kotlinx.cinterop.CPointer import kotlinx.cinterop.memScoped -import platform.posix.pclose -import platform.posix.popen +import platform.posix.FILE + +expect fun openPipe(command: String, access: String): CPointer? +expect fun closePipe(filePtr: CPointer): Int internal fun runProcess(command: String) { - val filePtr = popen(command, "r") + val filePtr = openPipe(command, "r") if (filePtr == null) { - PropertiesBundle.writeMessage("unable.to.run.command", command) + PropertiesBundle.writeErrorMessage("unable.to.run.command", command) return } @@ -16,8 +19,8 @@ internal fun runProcess(command: String) { handleOutput(filePtr, ::print) } - val status = pclose(filePtr) + val status = closePipe(filePtr) if (status == -1) { - PropertiesBundle.writeMessage("error.running.command", command) + PropertiesBundle.writeErrorMessage("error.running.command", command) } } \ No newline at end of file diff --git a/src/nativeTest/kotlin/io/ktor/generator/CommonTest.kt b/src/nativeTest/kotlin/io/ktor/generator/CommonTest.kt index c245ca5..ac462da 100644 --- a/src/nativeTest/kotlin/io/ktor/generator/CommonTest.kt +++ b/src/nativeTest/kotlin/io/ktor/generator/CommonTest.kt @@ -6,12 +6,10 @@ import io.ktor.generator.api.* import io.ktor.generator.cli.installer.* import io.ktor.generator.cli.utils.* import io.ktor.generator.configuration.json.* -import kotlinx.coroutines.TimeoutCancellationException import kotlinx.coroutines.runBlocking -import kotlinx.coroutines.withTimeout import kotlin.test.Test import kotlin.test.assertEquals -import kotlin.test.assertFailsWith +import kotlin.test.assertNotNull import kotlin.test.assertTrue private fun nativeResource(name: String): File = File("src/nativeTest/resources/$name") @@ -25,7 +23,8 @@ class GeneratorIntegrationTests { @Test fun testUnzipWorks() { - unzip(nativeResource("file.zip"), Directory.current()) + val zip = nativeResource("file.zip") + unzip(zip, Directory.current()) val unzippedFile = Directory.current().file("file.txt") assertEquals(unzippedFile.readText(), nativeResource("file.txt").readText()) unzippedFile.delete() @@ -48,7 +47,8 @@ class GeneratorIntegrationTests { jdkFile.delete() assertTrue(outputDir.content().isNotEmpty(), "Directory with JDK should be not empty after unpack") - val contentsHome = outputDir.subdir(KtorInstaller.JAVA_CONTENTS).subdir(KtorInstaller.JAVA_CONTENTS_HOME) + val contentsHome = outputDir.let(::getJdkContentsHome) + assertNotNull(contentsHome, "JDK contents home must not be null") assertTrue(contentsHome.exists(), "JDK contains /Contents/Home dir") assertTrue(contentsHome.content().isNotEmpty(), "JDK Contents/Home must be not empty") outputDir.delete() diff --git a/src/nativeTest/kotlin/io/ktor/generator/bundle/BundleParsingTest.kt b/src/nativeTest/kotlin/io/ktor/generator/bundle/BundleParsingTest.kt index 3b82c57..b043ebb 100644 --- a/src/nativeTest/kotlin/io/ktor/generator/bundle/BundleParsingTest.kt +++ b/src/nativeTest/kotlin/io/ktor/generator/bundle/BundleParsingTest.kt @@ -22,7 +22,7 @@ class BundleParsingTest { ) assertEquals( PropertiesBundle.message("project.generated", "project-name"), - "Project \"project-name\" was successfully generated.\nYou can execute `ktor run project-name` to run it" + "Project \"project-name\" was successfully generated.\nYou can execute `ktor start project-name` to start it" ) assertEquals(PropertiesBundle.message("project.not.exists", "p0"), "Project p0 does not exist") } diff --git a/src/nativeTest/resources/file.zip b/src/nativeTest/resources/file.zip new file mode 100644 index 0000000..050cdd3 Binary files /dev/null and b/src/nativeTest/resources/file.zip differ diff --git a/src/nativeTest/resources/test-project.zip b/src/nativeTest/resources/test-project.zip new file mode 100644 index 0000000..22e16a6 Binary files /dev/null and b/src/nativeTest/resources/test-project.zip differ