From fa98b0afacca8e17543016112f80f92d3976c513 Mon Sep 17 00:00:00 2001 From: Ilya Chernikov Date: Fri, 31 Aug 2018 13:33:07 +0300 Subject: [PATCH] Implement main-kts project and test --- .../kotlin-main-kts-test/build.gradle.kts | 20 ++++ .../kotlin/mainKts/test/mainKtsTest.kt | 63 ++++++++++++ .../testData/hello-resolve-error.main.kts | 5 + .../testData/hello-resolve-junit.main.kts | 7 ++ .../testData/hello-unresolved-junit.main.kts | 5 + .../tools/kotlin-main-kts/build.gradle.kts | 98 +++++++++++++++++++ libraries/tools/kotlin-main-kts/main-kts.pro | 20 ++++ ...org.jetbrains.kotlin.mainKts.MainKtsScript | 0 .../org/jetbrains/kotlin/mainKts/scriptDef.kt | 67 +++++++++++++ settings.gradle | 4 + 10 files changed, 289 insertions(+) create mode 100644 libraries/tools/kotlin-main-kts-test/build.gradle.kts create mode 100644 libraries/tools/kotlin-main-kts-test/test/org/jetbrains/kotlin/mainKts/test/mainKtsTest.kt create mode 100644 libraries/tools/kotlin-main-kts-test/testData/hello-resolve-error.main.kts create mode 100644 libraries/tools/kotlin-main-kts-test/testData/hello-resolve-junit.main.kts create mode 100644 libraries/tools/kotlin-main-kts-test/testData/hello-unresolved-junit.main.kts create mode 100644 libraries/tools/kotlin-main-kts/build.gradle.kts create mode 100644 libraries/tools/kotlin-main-kts/main-kts.pro create mode 100644 libraries/tools/kotlin-main-kts/resources/META-INF/kotlin/script/templates/org.jetbrains.kotlin.mainKts.MainKtsScript create mode 100644 libraries/tools/kotlin-main-kts/src/org/jetbrains/kotlin/mainKts/scriptDef.kt diff --git a/libraries/tools/kotlin-main-kts-test/build.gradle.kts b/libraries/tools/kotlin-main-kts-test/build.gradle.kts new file mode 100644 index 00000000000..3c9a21f9d5c --- /dev/null +++ b/libraries/tools/kotlin-main-kts-test/build.gradle.kts @@ -0,0 +1,20 @@ + +description = "Kotlin \"main\" script definition tests" + +plugins { + kotlin("jvm") + id("jps-compatible") +} + +dependencies { + testCompile(project(":kotlin-main-kts")) + testCompile(project(":kotlin-scripting-jvm-host")) + testCompile(commonDep("junit")) + testRuntime("org.apache.ivy:ivy:2.4.0") +} + +sourceSets { + "main" { } + "test" { projectDefault() } +} + diff --git a/libraries/tools/kotlin-main-kts-test/test/org/jetbrains/kotlin/mainKts/test/mainKtsTest.kt b/libraries/tools/kotlin-main-kts-test/test/org/jetbrains/kotlin/mainKts/test/mainKtsTest.kt new file mode 100644 index 00000000000..f7a091b8e10 --- /dev/null +++ b/libraries/tools/kotlin-main-kts-test/test/org/jetbrains/kotlin/mainKts/test/mainKtsTest.kt @@ -0,0 +1,63 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ +package org.jetbrains.kotlin.mainKts.test + +import org.jetbrains.kotlin.mainKts.MainKtsScript +import org.junit.Assert +import org.junit.Test +import java.io.File +import kotlin.script.experimental.api.EvaluationResult +import kotlin.script.experimental.api.ResultWithDiagnostics +import kotlin.script.experimental.api.ScriptEvaluationConfiguration +import kotlin.script.experimental.api.constructorArgs +import kotlin.script.experimental.host.toScriptSource +import kotlin.script.experimental.jvmhost.BasicJvmScriptingHost +import kotlin.script.experimental.jvmhost.createJvmCompilationConfigurationFromTemplate + +fun evalFile(scriptFile: File): ResultWithDiagnostics { + + val scriptDefinition = createJvmCompilationConfigurationFromTemplate() + + val evaluationEnv = ScriptEvaluationConfiguration { + constructorArgs(emptyArray()) + } + + return BasicJvmScriptingHost().eval(scriptFile.toScriptSource(), scriptDefinition, evaluationEnv) +} + +class MainKtsTest { + + @Test + fun testResolveJunit() { + val res = evalFile(File("testData/hello-resolve-junit.main.kts")) + + Assert.assertTrue( + "test failed:\n ${res.reports.joinToString("\n ") { it.message + if (it.exception == null) "" else ": ${it.exception}" }}", + res is ResultWithDiagnostics.Success + ) + } + + @Test + fun testUnresolvedJunit() { + val res = evalFile(File("testData/hello-unresolved-junit.main.kts")) + + Assert.assertTrue( + "test failed - expecting a failure with the message \"Unresolved reference: junit\" but received " + + (if (res is ResultWithDiagnostics.Failure) "failure" else "success") + + ":\n ${res.reports.joinToString("\n ") { it.message + if (it.exception == null) "" else ": ${it.exception}" }}", + res is ResultWithDiagnostics.Failure && res.reports.any { it.message.contains("Unresolved reference: junit") }) + } + + @Test + fun testResolveError() { + val res = evalFile(File("testData/hello-resolve-error.main.kts")) + + Assert.assertTrue( + "test failed - expecting a failure with the message \"Unknown set of arguments to maven resolver: abracadabra\" but received " + + (if (res is ResultWithDiagnostics.Failure) "failure" else "success") + + ":\n ${res.reports.joinToString("\n ") { it.message + if (it.exception == null) "" else ": ${it.exception}" }}", + res is ResultWithDiagnostics.Failure && res.reports.any { it.message.contains("Unknown set of arguments to maven resolver: abracadabra") }) + } +} \ No newline at end of file diff --git a/libraries/tools/kotlin-main-kts-test/testData/hello-resolve-error.main.kts b/libraries/tools/kotlin-main-kts-test/testData/hello-resolve-error.main.kts new file mode 100644 index 00000000000..48086038dd1 --- /dev/null +++ b/libraries/tools/kotlin-main-kts-test/testData/hello-resolve-error.main.kts @@ -0,0 +1,5 @@ + +@file:DependsOn("abracadabra") + +println("Hello, World!") + diff --git a/libraries/tools/kotlin-main-kts-test/testData/hello-resolve-junit.main.kts b/libraries/tools/kotlin-main-kts-test/testData/hello-resolve-junit.main.kts new file mode 100644 index 00000000000..5f420006960 --- /dev/null +++ b/libraries/tools/kotlin-main-kts-test/testData/hello-resolve-junit.main.kts @@ -0,0 +1,7 @@ + +@file:DependsOn("junit:junit:4.11") + +org.junit.Assert.assertTrue(true) + +println("Hello, World!") + diff --git a/libraries/tools/kotlin-main-kts-test/testData/hello-unresolved-junit.main.kts b/libraries/tools/kotlin-main-kts-test/testData/hello-unresolved-junit.main.kts new file mode 100644 index 00000000000..389ca9a0af9 --- /dev/null +++ b/libraries/tools/kotlin-main-kts-test/testData/hello-unresolved-junit.main.kts @@ -0,0 +1,5 @@ + +org.junit.Assert.assertTrue(true) + +println("Hello, World!") + diff --git a/libraries/tools/kotlin-main-kts/build.gradle.kts b/libraries/tools/kotlin-main-kts/build.gradle.kts new file mode 100644 index 00000000000..24b87f6e57b --- /dev/null +++ b/libraries/tools/kotlin-main-kts/build.gradle.kts @@ -0,0 +1,98 @@ +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar +import org.gradle.api.internal.artifacts.publish.ArchivePublishArtifact +import proguard.gradle.ProGuardTask + +description = "Kotlin \"main\" script definition" + +plugins { + kotlin("jvm") + id("jps-compatible") +} + +// You can run Gradle with "-Pkotlin.build.proguard=true" to enable ProGuard run on the jar (on TeamCity, ProGuard always runs) +val shrink = + findProperty("kotlin.build.proguard")?.toString()?.toBoolean() + ?: hasProperty("teamcity") + +val jarBaseName = property("archivesBaseName") as String + +val fatJarContents by configurations.creating +val proguardLibraryJars by configurations.creating +val fatJar by configurations.creating +val default by configurations +val runtimeJar by configurations.creating + +default.apply { + extendsFrom(runtimeJar) +} + +val projectsDependencies = listOf( + ":kotlin-scripting-common", + ":kotlin-scripting-jvm", + ":kotlin-script-util", + ":kotlin-script-runtime") + +dependencies { + projectsDependencies.forEach { + compileOnly(project(it)) + fatJarContents(project(it)) { isTransitive = false } + testCompile(project(it)) + } + runtime(project(":kotlin-compiler")) + runtime(project(":kotlin-reflect")) + fatJarContents("org.apache.ivy:ivy:2.4.0") + fatJarContents(commonDep("org.jetbrains.kotlinx", "kotlinx-coroutines-core")) { isTransitive = false } + proguardLibraryJars(files(firstFromJavaHomeThatExists("jre/lib/rt.jar", "../Classes/classes.jar"), + firstFromJavaHomeThatExists("jre/lib/jsse.jar", "../Classes/jsse.jar"), + toolsJar())) + proguardLibraryJars(project(":kotlin-stdlib")) + proguardLibraryJars(project(":kotlin-reflect")) +} + +sourceSets { + "main" { projectDefault() } + "test" { } +} + +noDefaultJar() + +val packJar by task { + configurations = listOf(fatJar) + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + destinationDir = File(buildDir, "libs") + + setupPublicJar(project.the().archivesBaseName, "before-proguard") + + from(mainSourceSet.output) + from(fatJarContents) +} + +val proguard by task { + dependsOn(packJar) + configuration("main-kts.pro") + + injars(mapOf("filter" to "!META-INF/versions/**"), packJar.outputs.files) + + val outputJar = fileFrom(buildDir, "libs", "$jarBaseName-$version-after-proguard.jar") + + outjars(outputJar) + + inputs.files(packJar.outputs.files.singleFile) + outputs.file(outputJar) + + libraryjars(mapOf("filter" to "!META-INF/versions/**"), proguardLibraryJars) + printconfiguration("$buildDir/compiler.pro.dump") +} + +val pack = if (shrink) proguard else packJar + +runtimeJarArtifactBy(pack, pack.outputs.files.singleFile) { + name = jarBaseName + classifier = "" +} + +sourcesJar() +javadocJar() + +publish() + diff --git a/libraries/tools/kotlin-main-kts/main-kts.pro b/libraries/tools/kotlin-main-kts/main-kts.pro new file mode 100644 index 00000000000..d61337f5fdb --- /dev/null +++ b/libraries/tools/kotlin-main-kts/main-kts.pro @@ -0,0 +1,20 @@ +-target 1.8 +-dontoptimize +-dontobfuscate +# -dontshrink + +-dontnote ** +-dontwarn org.sonatype.aether.** +-dontwarn org.jetbrains.kotlin.** +-dontwarn org.apache.commons.** +-dontwarn com.jcraft.** +-dontwarn org.apache.tools.ant.** +-dontwarn org.apache.oro.text.** +-dontwarn org.bouncycastle.** +-dontwarn org.apache.ivy.ant.** +-dontwarn kotlin.annotations.jvm.** + +-keep class org.jetbrains.kotlin.mainKts.** { *; } +-keep class kotlin.script.experimental.** { *; } +-keep class org.jetbrains.kotlin.script.util.impl.PathUtilKt { *; } + diff --git a/libraries/tools/kotlin-main-kts/resources/META-INF/kotlin/script/templates/org.jetbrains.kotlin.mainKts.MainKtsScript b/libraries/tools/kotlin-main-kts/resources/META-INF/kotlin/script/templates/org.jetbrains.kotlin.mainKts.MainKtsScript new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libraries/tools/kotlin-main-kts/src/org/jetbrains/kotlin/mainKts/scriptDef.kt b/libraries/tools/kotlin-main-kts/src/org/jetbrains/kotlin/mainKts/scriptDef.kt new file mode 100644 index 00000000000..685d80fe275 --- /dev/null +++ b/libraries/tools/kotlin-main-kts/src/org/jetbrains/kotlin/mainKts/scriptDef.kt @@ -0,0 +1,67 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.mainKts + +import org.jetbrains.kotlin.script.util.DependsOn +import org.jetbrains.kotlin.script.util.FilesAndIvyResolver +import org.jetbrains.kotlin.script.util.Repository +import java.io.File +import kotlin.script.dependencies.ScriptContents +import kotlin.script.dependencies.ScriptDependenciesResolver +import kotlin.script.experimental.annotations.KotlinScript +import kotlin.script.experimental.api.* +import kotlin.script.experimental.jvm.JvmDependency +import kotlin.script.experimental.jvm.compat.mapLegacyDiagnosticSeverity +import kotlin.script.experimental.jvm.compat.mapLegacyScriptPosition +import kotlin.script.experimental.jvm.dependenciesFromCurrentContext +import kotlin.script.experimental.jvm.jvm + +@Suppress("unused") +@KotlinScript(extension = "main.kts", compilationConfiguration = MainKtsScriptDefinition::class) +abstract class MainKtsScript(val args: Array) + +object MainKtsScriptDefinition : ScriptCompilationConfiguration( + { + defaultImports(DependsOn::class, Repository::class) + jvm { + dependenciesFromCurrentContext("kotlin-main-kts") + } + // variant: dependencies(collectDependenciesFromCurrentContext(... + refineConfiguration { + onAnnotations(DependsOn::class, Repository::class, handler = MainKtsConfigurator()) + } + }) + +class MainKtsConfigurator : RefineScriptCompilationConfigurationHandler { + private val resolver = FilesAndIvyResolver() + + override operator fun invoke(context: ScriptConfigurationRefinementContext): ResultWithDiagnostics { + val annotations = context.collectedData?.get(ScriptCollectedData.foundAnnotations)?.takeIf { it.isNotEmpty() } + ?: return context.compilationConfiguration.asSuccess() + val scriptContents = object : ScriptContents { + override val annotations: Iterable = annotations + override val file: File? = null + override val text: CharSequence? = null + } + val diagnostics = arrayListOf() + fun report(severity: ScriptDependenciesResolver.ReportSeverity, message: String, position: ScriptContents.Position?) { + diagnostics.add(ScriptDiagnostic(message, mapLegacyDiagnosticSeverity(severity), mapLegacyScriptPosition(position))) + } + return try { + val newDepsFromResolver = resolver.resolve(scriptContents, emptyMap(), ::report, null).get() + ?: return context.compilationConfiguration.asSuccess(diagnostics) // TODO: failure + val resolvedClasspath = newDepsFromResolver.classpath.toList().takeIf { it.isNotEmpty() } + ?: return context.compilationConfiguration.asSuccess(diagnostics) // TODO: failure + + ScriptCompilationConfiguration(context.compilationConfiguration) { + dependencies.append(JvmDependency(resolvedClasspath)) + }.asSuccess(diagnostics) + } catch (e: Throwable) { + ResultWithDiagnostics.Failure(*diagnostics.toTypedArray(), e.asDiagnostics()) + } + } +} + diff --git a/settings.gradle b/settings.gradle index 2d979e90fc2..4d8b64cee8a 100644 --- a/settings.gradle +++ b/settings.gradle @@ -166,6 +166,8 @@ include ":kotlin-build-common", ":kotlin-scripting-compiler", ":kotlin-scripting-idea", ":kotlin-scripting-compiler-embeddable", + ":kotlin-main-kts", + ":kotlin-main-kts-test", ":examples:scripting-jvm-simple-script", ":examples:scripting-jvm-simple-script-host", ":examples:scripting-jvm-maven-deps", @@ -278,6 +280,8 @@ project(':kotlin-scripting-jvm-host').projectDir = "$rootDir/libraries/scripting project(':kotlin-scripting-compiler').projectDir = "$rootDir/plugins/scripting/scripting-cli" as File project(':kotlin-scripting-compiler-embeddable').projectDir = "$rootDir/plugins/scripting/scripting-embeddable" as File project(':kotlin-scripting-idea').projectDir = "$rootDir/plugins/scripting/scripting-idea" as File +project(':kotlin-main-kts').projectDir = "$rootDir/libraries/tools/kotlin-main-kts" as File +project(':kotlin-main-kts-test').projectDir = "$rootDir/libraries/tools/kotlin-main-kts-test" as File project(':examples:scripting-jvm-simple-script').projectDir = "$rootDir/libraries/examples/scripting/jvm-simple-script/script" as File project(':examples:scripting-jvm-simple-script-host').projectDir = "$rootDir/libraries/examples/scripting/jvm-simple-script/host" as File project(':examples:scripting-jvm-maven-deps').projectDir = "$rootDir/libraries/examples/scripting/jvm-maven-deps/script" as File