diff --git a/IntegrationTest345.main.kts b/IntegrationTest345.main.kts deleted file mode 100644 index b0e6b4a7..00000000 --- a/IntegrationTest345.main.kts +++ /dev/null @@ -1,43 +0,0 @@ -@file:Repository("https://repo.maven.apache.org/maven2") - -@file:DependsOn("org.jetbrains.kotlin:kotlin-scripting-jvm:1.6.20") -@file:DependsOn("org.jetbrains.kotlin:kotlin-scripting-dependencies:1.6.20") -@file:DependsOn("org.jetbrains.kotlin:kotlin-scripting-dependencies-maven:1.6.20") -@file:DependsOn("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0") -//@file:DependsOn("org.jetbrains.kotlin:kotlin-scripting-jvm") - -import java.io.File -import kotlin.script.experimental.api.valueOrNull -import kotlin.script.experimental.dependencies.CompoundDependenciesResolver -import kotlin.script.experimental.dependencies.FileSystemDependenciesResolver -import kotlin.script.experimental.dependencies.RepositoryCoordinates -import kotlin.script.experimental.dependencies.maven.MavenDependenciesResolver -import kotlinx.coroutines.runBlocking - - -// clear .m2 cache -// val log4jCached = File(System.getProperty("user.home"), ".m2/repository/log4j/log4j/1.2.14/") -val cachedM2 = File(System.getProperty("user.home"), ".m2/repository/com/beust") - -if (cachedM2.isDirectory) { - System.err.println("Cleaning up cached .m2 copy of klaxon") - cachedM2.deleteRecursively() -} - -val mvnResolver = MavenDependenciesResolver().apply { - addRepository(RepositoryCoordinates("https://repo.maven.apache.org/maven2")) -} - -val resolver = CompoundDependenciesResolver(FileSystemDependenciesResolver(), mvnResolver) - -// val resolve = resolver.resolve("log4j:log4j:1.2.14") -runBlocking { -val resolve = resolver.resolve("com.beust:klaxon:5.5") - -println(resolve.valueOrNull()) - -// require(File(System.getProperty("user.home"), ".m2/repository/log4j/log4j/1.2.14/log4j-1.2.14.jar").exists()){ -require(File(System.getProperty("user.home"), ".m2/repository/com/beust/klaxon/5.5/klaxon-5.5.jar").exists()){ - "failed to resolve dependency" -} -} \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index 7af1780a..fbab5624 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,13 +1,14 @@ import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar +import com.github.jengelman.gradle.plugins.shadow.transformers.ComponentsXmlResourceTransformer import org.gradle.api.tasks.testing.logging.TestExceptionFormat import org.gradle.api.tasks.testing.logging.TestLogEvent -val kotlinVersion: String = "1.6.20" +val kotlinVersion: String = "1.6.21" plugins { - kotlin("jvm") version "1.6.20" + kotlin("jvm") version "1.6.21" application - id("com.github.johnrengelman.shadow") version "6.1.0" + id("com.github.johnrengelman.shadow") version "7.1.2" } repositories { @@ -20,44 +21,48 @@ tasks.test { useJUnitPlatform() testLogging { - events(TestLogEvent.FAILED); exceptionFormat = TestExceptionFormat.FULL + events(TestLogEvent.FAILED) + exceptionFormat = TestExceptionFormat.FULL } } tasks.withType { addTestListener(object : TestListener { - override fun beforeSuite(suite: TestDescriptor) { logger.quiet("\nTest class: ${suite.displayName}") } + override fun beforeSuite(suite: TestDescriptor) { + logger.quiet("\nTest class: ${suite.displayName}") + } + override fun beforeTest(testDescriptor: TestDescriptor) {} override fun afterTest(testDescriptor: TestDescriptor, result: TestResult) { - logger.quiet("${String.format( "%-60s - %-10s", testDescriptor.name, result.resultType )} ") + logger.quiet("${String.format("%-60s - %-10s", testDescriptor.name, result.resultType)} ") } override fun afterSuite(suite: TestDescriptor, result: TestResult) {} }) } -val launcherClassName: String ="kscript.app.KscriptKt" +val launcherClassName: String = "kscript.app.KscriptKt" dependencies { implementation("com.offbytwo:docopt:0.6.0.20150202") - implementation("org.jetbrains.kotlin:kotlin-scripting-common:$kotlinVersion") implementation("org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion") - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0-RC3") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.1") + implementation("org.jetbrains.kotlin:kotlin-scripting-common:$kotlinVersion") implementation("org.jetbrains.kotlin:kotlin-scripting-jvm:$kotlinVersion") implementation("org.jetbrains.kotlin:kotlin-scripting-dependencies:$kotlinVersion") - implementation("org.jetbrains.kotlin:kotlin-scripting-dependencies-maven:$kotlinVersion") + implementation("org.jetbrains.kotlin:kotlin-scripting-dependencies-maven-all:$kotlinVersion") implementation("commons-io:commons-io:2.11.0") implementation("commons-codec:commons-codec:1.15") - implementation("org.slf4j:slf4j-nop:1.7.32") + implementation("org.slf4j:slf4j-nop:1.7.36") testImplementation("org.junit.jupiter:junit-jupiter-engine:5.8.2") testImplementation("org.junit.jupiter:junit-jupiter-params:5.8.2") testImplementation("com.willowtreeapps.assertk:assertk-jvm:0.25") - testImplementation("io.mockk:mockk:1.12.1") + testImplementation("io.mockk:mockk:1.12.3") testImplementation(kotlin("script-runtime")) } @@ -65,6 +70,8 @@ dependencies { val shadowJar by tasks.getting(ShadowJar::class) { // set empty string to classifier and version to get predictable jar file name: build/libs/kscript.jar archiveFileName.set("kscript.jar") + transform(ComponentsXmlResourceTransformer()) + doLast { copy { from(File(projectDir, "src/kscript")) @@ -74,7 +81,7 @@ val shadowJar by tasks.getting(ShadowJar::class) { } application { - mainClassName = launcherClassName + mainClass.set(launcherClassName) } // Disable standard jar task to avoid building non-shadow jars diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index e750102e..aa991fce 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/src/main/kotlin/kscript/app/Kscript.kt b/src/main/kotlin/kscript/app/Kscript.kt index 217b3b51..ad3ac3e2 100644 --- a/src/main/kotlin/kscript/app/Kscript.kt +++ b/src/main/kotlin/kscript/app/Kscript.kt @@ -19,7 +19,7 @@ import org.docopt.DocOptWrapper * @author Marcin Kuszczak */ -const val KSCRIPT_VERSION = "4.0.0" +const val KSCRIPT_VERSION = "4.0.1" fun main(args: Array) { try { diff --git a/src/main/kotlin/kscript/app/KscriptHandler.kt b/src/main/kotlin/kscript/app/KscriptHandler.kt index 2c4c8636..fefec01d 100644 --- a/src/main/kotlin/kscript/app/KscriptHandler.kt +++ b/src/main/kotlin/kscript/app/KscriptHandler.kt @@ -14,7 +14,6 @@ import kscript.app.util.Logger.infoMsg import org.docopt.DocOptWrapper import java.net.URI - class KscriptHandler(private val config: Config, private val docopt: DocOptWrapper) { fun handle(userArgs: List) { @@ -24,6 +23,11 @@ class KscriptHandler(private val config: Config, private val docopt: DocOptWrapp devMsg("KScript configuration:") devMsg(config.toString()) + if (Logger.devMode) { + devMsg("Classpath:") + devMsg(System.getProperty("java.class.path")) + } + // create kscript dir if it does not yet exist val appDir = AppDir(config.kscriptDir) diff --git a/src/main/kotlin/kscript/app/code/Templates.kt b/src/main/kotlin/kscript/app/code/Templates.kt index d0e6be80..2235901b 100644 --- a/src/main/kotlin/kscript/app/code/Templates.kt +++ b/src/main/kotlin/kscript/app/code/Templates.kt @@ -1,9 +1,6 @@ package kscript.app.code -import kscript.app.model.Dependency import kscript.app.model.PackageName -import kscript.app.model.Repository -import kscript.app.model.Script import kscript.app.util.ScriptUtils.dropExtension import org.intellij.lang.annotations.Language @@ -33,7 +30,7 @@ object Templates { exec java -jar ${'$'}0 "${'$'}@" """.trimIndent() - fun wrapperForScript(packageName: PackageName, className: String) : String { + fun wrapperForScript(packageName: PackageName, className: String): String { val classReference = packageName.value + "." + className return """ diff --git a/src/main/kotlin/kscript/app/model/Config.kt b/src/main/kotlin/kscript/app/model/Config.kt index d2241cad..2bba3094 100644 --- a/src/main/kotlin/kscript/app/model/Config.kt +++ b/src/main/kotlin/kscript/app/model/Config.kt @@ -9,6 +9,7 @@ data class Config( val intellijCommand: String, val gradleCommand: String, val kotlinHome: Path?, + val osName: String, val classPathSeparator: String, val separatorChar: Char, val homeDir: Path, diff --git a/src/main/kotlin/kscript/app/model/ConfigBuilder.kt b/src/main/kotlin/kscript/app/model/ConfigBuilder.kt index 2c72b000..fec17ccb 100644 --- a/src/main/kotlin/kscript/app/model/ConfigBuilder.kt +++ b/src/main/kotlin/kscript/app/model/ConfigBuilder.kt @@ -13,9 +13,10 @@ class ConfigBuilder internal constructor() { var intellijCommand: String = System.getenv("KSCRIPT_IDEA_COMMAND") ?: "idea" var gradleCommand: String = System.getenv("KSCRIPT_GRADLE_COMMAND") ?: "gradle" var kotlinHome: Path? = (System.getenv("KOTLIN_HOME") ?: guessKotlinHome())?.let { Paths.get(it).absolute() } - var classPathSeparator: String = if (System.getProperty("os.name").lowercase().contains("windows")) ";" else ":" - var separatorChar: Char = File.separatorChar + var osName: String = System.getProperty("os.name") var homeDir: Path = Paths.get(System.getProperty("user.home")!!) + var classPathSeparator: String = if (osName.lowercase().contains("windows")) ";" else ":" + var separatorChar: Char = File.separatorChar var kotlinOptsEnvVariable = System.getenv("KSCRIPT_KOTLIN_OPTS") ?: "" var repositoryUrlEnvVariable = System.getenv("KSCRIPT_REPOSITORY_URL") ?: "" var repositoryUserEnvVariable = System.getenv("KSCRIPT_REPOSITORY_USER") ?: "" @@ -29,6 +30,7 @@ class ConfigBuilder internal constructor() { intellijCommand, gradleCommand, kotlinHome, + osName, classPathSeparator, separatorChar, homeDir, diff --git a/src/main/kotlin/kscript/app/resolver/CommandResolver.kt b/src/main/kotlin/kscript/app/resolver/CommandResolver.kt index b3eb853d..af3bb396 100644 --- a/src/main/kotlin/kscript/app/resolver/CommandResolver.kt +++ b/src/main/kotlin/kscript/app/resolver/CommandResolver.kt @@ -11,13 +11,21 @@ import kotlin.io.path.absolutePathString import kotlin.io.path.div class CommandResolver(private val config: Config, private val script: Script) { + //Syntax for different OS-es: + //LINUX: kotlin -classpath "/home/vagrant/workspace/Kod/Repos/kscript/test:/home/vagrant/.kscript/cache/jar_2ccd53e06b0355d3573a4ae8698398fe/scriplet.jar:/usr/local/sdkman/candidates/kotlin/1.6.21/lib/kotlin-script-runtime.jar" Main_Scriplet + //GIT-BASH: kotlin -classpath "C:\Users\Admin;C:\Users\Admin\.kscript\cache\jar_2ccd53e06b0355d3573a4ae8698398fe\scriplet.jar;C:\Users\Admin\.sdkman\candidates\kotlin\current\lib\kotlin-script-runtime.jar" Main_Scriplet + //CYGWIN: kotlin -classpath "C:\Users\Admin;C:\Users\Admin\.kscript\cache\jar_2ccd53e06b0355d3573a4ae8698398fe\scriplet.jar;C:\Users\Admin\.sdkman\candidates\kotlin\current\lib\kotlin-script-runtime.jar" Main_Scriplet + //WINDOWS: kotlin -classpath "C:\Users\Admin;C:\Users\Admin\.kscript\cache\jar_2ccd53e06b0355d3573a4ae8698398fe\scriplet.jar;C:\Users\Admin\.sdkman\candidates\kotlin\current\lib\kotlin-script-runtime.jar" Main_Scriplet + + //Path conversion (Cygwin/mingw): cygpath -u "c:\Users\Admin"; /cygdrive/c/ - Cygwin; /c/ - Mingw + //uname --> CYGWIN_NT-10.0 or MINGW64_NT-10.0-19043 + //How to find if mingw/cyg/win (second part): https://stackoverflow.com/questions/40877323/quickly-find-if-java-was-launched-from-windows-cmd-or-cygwin-terminal + fun compileKotlin(jar: Path, dependencies: Set, filePaths: Set): String { val compilerOptsStr = resolveCompilerOpts(script.compilerOpts) val classpath = resolveClasspath(dependencies) val files = filePaths.joinToString(" ") { "'${it.absolutePathString()}'" } - - val kotlinc = - if (config.kotlinHome != null) (config.kotlinHome / "bin" / "kotlinc").absolutePathString() else "kotlinc" + val kotlinc = resolveKotlinBinary("kotlinc") return "'$kotlinc' $compilerOptsStr $classpath -d '${jar.absolutePathString()}' $files" } @@ -35,8 +43,7 @@ class CommandResolver(private val config: Config, private val script: Script) { } val classpath = resolveClasspath(dependenciesSet) - - val kotlin = if (config.kotlinHome != null) (config.kotlinHome / "bin" / "kotlin").absolutePathString() else "kotlin" + val kotlin = resolveKotlinBinary("kotlin") return "$kotlin $kotlinOptsStr $classpath ${jarArtifact.execClassName} $userArgsStr" } @@ -45,8 +52,7 @@ class CommandResolver(private val config: Config, private val script: Script) { val compilerOptsStr = resolveCompilerOpts(script.compilerOpts) val kotlinOptsStr = resolveKotlinOpts(script.kotlinOpts) val classpath = resolveClasspath(dependencies) - - val kotlinc = if (config.kotlinHome != null) (config.kotlinHome / "bin" / "kotlinc").absolutePathString() else "kotlinc" + val kotlinc = resolveKotlinBinary("kotlinc") return "$kotlinc $compilerOptsStr $kotlinOptsStr $classpath" } @@ -60,10 +66,15 @@ class CommandResolver(private val config: Config, private val script: Script) { } private fun resolveKotlinOpts(kotlinOpts: Set) = kotlinOpts.joinToString(" ") { it.value } + private fun resolveCompilerOpts(compilerOpts: Set) = compilerOpts.joinToString(" ") { it.value } + private fun resolveUserArgs(userArgs: List) = userArgs.joinToString(" ") { "\"${it.replace("\"", "\\\"")}\"" } private fun resolveClasspath(dependencies: Set) = if (dependencies.isEmpty()) "" - else "-classpath " + dependencies.joinToString(config.classPathSeparator) { "'${it.absolutePathString()}'" } + else "-classpath \"" + dependencies.joinToString(config.classPathSeparator) { it.absolutePathString() } + "\"" + + private fun resolveKotlinBinary(binary: String) = + if (config.kotlinHome != null) (config.kotlinHome / "bin" / binary).absolutePathString() else binary } diff --git a/src/main/kotlin/kscript/app/resolver/DependencyResolver.kt b/src/main/kotlin/kscript/app/resolver/DependencyResolver.kt index 83471154..cc3d2314 100644 --- a/src/main/kotlin/kscript/app/resolver/DependencyResolver.kt +++ b/src/main/kotlin/kscript/app/resolver/DependencyResolver.kt @@ -46,12 +46,16 @@ class DependencyResolver(private val customRepos: Set) { devMsg("Resolved in: ${System.currentTimeMillis() - start}") resolved } - }.map { - it.valueOr { - throw IllegalStateException("Failed while connecting to the server. Check the connection (http/https, port, proxy, credentials, etc.) of your maven dependency locators. If you suspect this is a bug, you can create an issue on https://github.com/holgerbrandl/kscript" + it.reports.joinToString( - "\n" - ) { it.exception?.toString() ?: it.message }, it.reports.find { it.exception != null }?.exception - ) + }.asSequence().map { result -> + result.valueOr { failure -> + val details = failure.reports.joinToString("\n") { scriptDiagnostic -> + scriptDiagnostic.exception?.stackTraceToString() ?: scriptDiagnostic.message + } + + val firstException = + failure.reports.find { scriptDiagnostic -> scriptDiagnostic.exception != null }?.exception + + throw IllegalStateException(exceptionMessage + "\n" + details, firstException) } }.flatten().map { it.toPath() @@ -61,4 +65,13 @@ class DependencyResolver(private val customRepos: Set) { return resolvedDependencies } + + companion object { + //@formatter:off + private const val exceptionMessage = + "Failed while connecting to the server. Check the connection (http/https, port, proxy, credentials, etc.)" + + "of your maven dependency locators. If you suspect this is a bug, " + + "you can create an issue on https://github.com/holgerbrandl/kscript" + //@formatter:on + } } diff --git a/src/test/kotlin/kscript/app/parser/LineParserTest.kt b/src/test/kotlin/kscript/app/parser/LineParserTest.kt index 5562dce7..e51ad46a 100644 --- a/src/test/kotlin/kscript/app/parser/LineParserTest.kt +++ b/src/test/kotlin/kscript/app/parser/LineParserTest.kt @@ -1,14 +1,16 @@ package kscript.app.parser import assertk.assertThat -import assertk.assertions.* +import assertk.assertions.containsExactly +import assertk.assertions.containsExactlyInAnyOrder +import assertk.assertions.isFailure +import assertk.assertions.messageContains import kscript.app.model.* import kscript.app.parser.LineParser.parseDependency import kscript.app.parser.LineParser.parseEntry import kscript.app.parser.LineParser.parseImport import kscript.app.parser.LineParser.parseKotlinOpts import kscript.app.parser.LineParser.parseRepository -import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.Arguments @@ -45,11 +47,11 @@ class LineParserTest { " //DEPS $listWithoutQuotesStrangelyFormatted", )) { println("Case: '$line'") - assertThat(parseDependency(line)).containsExactlyInAnyOrder(*list.map { Dependency(it.trim()) }.toTypedArray()) + assertThat(parseDependency(line)).containsExactlyInAnyOrder(*list.map { Dependency(it.trim()) } + .toTypedArray()) } } - @ParameterizedTest @MethodSource("dynamicDependencies") fun `Dependency parsing - dynamic dependencies`(list: List) { diff --git a/test/resources/depends_on_klaxon.kts b/test/resources/depends_on_klaxon.kts new file mode 100644 index 00000000..6f443a2e --- /dev/null +++ b/test/resources/depends_on_klaxon.kts @@ -0,0 +1,6 @@ +@file:DependsOn("com.beust:klaxon:5.5") + +import com.beust.klaxon.Parser +val p = Parser() + +println("Successfully resolved klaxon") diff --git a/test/test_suite.sh b/test/test_suite.sh index df29611e..09581a36 100755 --- a/test/test_suite.sh +++ b/test/test_suite.sh @@ -98,10 +98,6 @@ if start_suite $SUITE $REQUESTED_SUITES; then assert "kscript https://raw.githubusercontent.com/holgerbrandl/kscript/master/test/resources/url_test.kts" "I came from the internet" assert "kscript https://git.io/fxHBv" "main was called" - ## there are some dependencies which are not jar, but maybe pom, aar, .. - ## make sure they work, too - assert "kscript ${PROJECT_DIR}/test/resources/depends_on_with_type.kts" "getBigDecimal(1L): 1" - # repeated compilation of buggy same script should end up in error again assert_raises "kscript '1-'; kscript '1-'" 1 @@ -145,6 +141,9 @@ fi ######################################################################################################################## SUITE="annotation_driven_configuration" if start_suite $SUITE $REQUESTED_SUITES; then + ## there are some dependencies which are not jar, but maybe pom, aar,... make sure they work, too + assert "kscript ${PROJECT_DIR}/test/resources/depends_on_with_type.kts" "getBigDecimal(1L): 1" + # make sure that @file:DependsOn is parsed correctly assert "kscript ${PROJECT_DIR}/test/resources/depends_on_annot.kts" "kscript with annotations rocks!" @@ -161,6 +160,9 @@ if start_suite $SUITE $REQUESTED_SUITES; then # make sure that @file:MavenRepository is parsed correctly assert "kscript ${PROJECT_DIR}/test/resources/script_with_compile_flags.kts" "hoo_ray" + ## Ensure dependencies are solved correctly #345 + rm -rf ~/.m2/repository/com/beust + assert "kscript ${PROJECT_DIR}/test/resources/depends_on_klaxon.kts" "Successfully resolved klaxon" assert_end "$SUITE" fi