diff --git a/tools/kotlin-native-gradle-plugin/build.gradle b/tools/kotlin-native-gradle-plugin/build.gradle index af85d27f51f..3f120802ed6 100644 --- a/tools/kotlin-native-gradle-plugin/build.gradle +++ b/tools/kotlin-native-gradle-plugin/build.gradle @@ -83,6 +83,9 @@ dependencies { testCompile gradleTestKit() testCompile 'junit:junit:4.12' + testCompile "org.jetbrains.kotlin:kotlin-test:$buildKotlinVersion" + testCompile "org.jetbrains.kotlin:kotlin-test-junit:$buildKotlinVersion" + testCompile "org.tools4j:tools4j-spockito:1.6" testCompile('org.spockframework:spock-core:1.1-groovy-2.4') { exclude module: 'groovy-all' } @@ -124,6 +127,10 @@ processResources { from(file("$rootBuildDirectory/utilities/env_blacklist")) } +compileTestGroovy.dependsOn.remove('compileTestJava') +compileTestKotlin.dependsOn compileTestGroovy +compileTestKotlin.classpath += files(compileTestGroovy.destinationDir) + // TODO: Get rid of manual pom generation. publishing { publications { diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/EnvironmentVariables.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/EnvironmentVariables.kt index 40b0f70f9ac..6c032aa0865 100644 --- a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/EnvironmentVariables.kt +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/EnvironmentVariables.kt @@ -70,12 +70,40 @@ internal class EnvironmentVariablesImpl: EnvironmentVariables { get() = System.getenv("KONAN_ENABLE_OPTIMIZATIONS")?.toUpperCase() == "YES" } +/** + * Due to https://github.com/gradle/gradle/issues/3468 we cannot use environment + * variables in Java 9. Until Gradle API for environment variables is provided + * we use project properties instead of them. TODO: Return to using env vars when the issue is fixed. + */ +internal class EnvironmentVariablesFromProperties(val project: Project): EnvironmentVariables { + override val configurationBuildDir: File? + get() = project.findProperty(ProjectProperty.KONAN_CONFIGURATION_BUILD_DIR)?.let { + project.file(it) + } + + override val debuggingSymbols: Boolean + get() = project.findProperty(ProjectProperty.KONAN_DEBUGGING_SYMBOLS)?.toString()?.toUpperCase().let { + it == "YES" || it == "TRUE" + } + + override val enableOptimizations: Boolean + get() = project.findProperty(ProjectProperty.KONAN_OPTIMIZATIONS_ENABLE)?.toString()?.toUpperCase().let { + it == "YES" || it == "TRUE" + } +} + internal val Project.useEnvironmentVariables: Boolean get() = findProperty(ProjectProperty.KONAN_USE_ENVIRONMENT_VARIABLES)?.toString()?.toBoolean() ?: false +/* + TODO: Return to using env vars when the issue is fixed. + Take into account the useEnvironmentVariables property (and may be rename it) in the following way: + + if (useEnvironmentVariables) { + EnvironmentVariablesImpl(project) + } else { + EnvironmentVariablesUnused() + } +*/ internal val Project.environmentVariables: EnvironmentVariables - get() = if (useEnvironmentVariables) { - EnvironmentVariablesImpl() - } else { - EnvironmentVariablesUnused() - } + get() = EnvironmentVariablesFromProperties(project) diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KonanPlugin.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KonanPlugin.kt index 33b9d7f1b5c..e7ec6652368 100644 --- a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KonanPlugin.kt +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KonanPlugin.kt @@ -277,7 +277,13 @@ class KonanPlugin @Inject constructor(private val registry: ToolingModelBuilderR KONAN_BUILD_TARGETS ("konan.build.targets"), KONAN_JVM_ARGS ("konan.jvmArgs"), KONAN_USE_ENVIRONMENT_VARIABLES("konan.useEnvironmentVariables"), - DOWNLOAD_COMPILER ("download.compiler") + DOWNLOAD_COMPILER ("download.compiler"), + + // Properties used instead of env vars until https://github.com/gradle/gradle/issues/3468 is fixed. + // TODO: Remove them when an API for env vars is provided. + KONAN_CONFIGURATION_BUILD_DIR ("konan.configuration.build.dir"), + KONAN_DEBUGGING_SYMBOLS ("konan.debugging.symbols"), + KONAN_OPTIMIZATIONS_ENABLE ("konan.optimizations.enable") } companion object { diff --git a/tools/kotlin-native-gradle-plugin/src/test/groovy/org/jetbrains/kotlin/gradle/plugin/test/EnvVariableSpecification.groovy b/tools/kotlin-native-gradle-plugin/src/test/groovy/org/jetbrains/kotlin/gradle/plugin/test/EnvVariableSpecification.groovy index 34927791ac9..43fb1ad8275 100644 --- a/tools/kotlin-native-gradle-plugin/src/test/groovy/org/jetbrains/kotlin/gradle/plugin/test/EnvVariableSpecification.groovy +++ b/tools/kotlin-native-gradle-plugin/src/test/groovy/org/jetbrains/kotlin/gradle/plugin/test/EnvVariableSpecification.groovy @@ -19,6 +19,7 @@ package org.jetbrains.kotlin.gradle.plugin.test import org.jetbrains.kotlin.konan.target.Family import org.jetbrains.kotlin.konan.target.HostManager import org.jetbrains.kotlin.konan.target.KonanTarget +import spock.lang.Ignore import spock.lang.Unroll import static org.jetbrains.kotlin.gradle.plugin.test.KonanProject.escapeBackSlashes @@ -109,10 +110,13 @@ class EnvVariableSpecification extends BaseKonanSpecification { prefix = target.family.dynamicPrefix suffix = target.family.dynamicSuffix break + case ArtifactType.FRAMEWORK: + suffix = "framework" } return "$prefix${baseName}.$suffix" } + @Ignore("The plugin doesn't use env vars until https://github.com/gradle/gradle/issues/3468 is fixed.") @Unroll("Plugin should support #action via an env variable") def 'Plugin should support enabling/disabling debug/opt via an env variable'() { when: @@ -126,7 +130,7 @@ class EnvVariableSpecification extends BaseKonanSpecification { task assertEnableDebug { doLast { konanArtifacts.main.forEach { - if (!$assertion) throw new AssertionError("$message for \${it.name}") + if (!($assertion)) throw new AssertionError("$message for \${it.name}") } } } @@ -146,6 +150,7 @@ class EnvVariableSpecification extends BaseKonanSpecification { "disabling opt" |"KONAN_ENABLE_OPTIMIZATIONS" |"NO" |"!it.enableOptimizations" |"Opts should be disabled" } + @Ignore("The plugin doesn't use env vars until https://github.com/gradle/gradle/issues/3468 is fixed.") def 'Plugin should support setting destination directory via an env variable'() { when: def project = createProjectWithWrapper() @@ -194,6 +199,7 @@ class EnvVariableSpecification extends BaseKonanSpecification { } } + @Ignore("The plugin doesn't use env vars until https://github.com/gradle/gradle/issues/3468 is fixed.") def 'Plugin should throw an exception if CONFIGURATION_BUILD_DIR contains a relative path'() { when: def project = createProjectWithWrapper() @@ -210,6 +216,7 @@ class EnvVariableSpecification extends BaseKonanSpecification { wrapperResult.getStderr().contains("A path passed using CONFIGURATION_BUILD_DIR should be absolute") } + @Ignore("The plugin doesn't use env vars until https://github.com/gradle/gradle/issues/3468 is fixed.") def 'Plugin should rerun tasks if CONFIGURATION_BUILD_DIR has been changed'() { when: def project = createProjectWithWrapper() @@ -241,6 +248,7 @@ class EnvVariableSpecification extends BaseKonanSpecification { files2.contains(artifactFileName("main", ArtifactType.LIBRARY)) } + @Ignore("The plugin doesn't use env vars until https://github.com/gradle/gradle/issues/3468 is fixed.") def 'Plugin should ignore environmentVariables if konan.useEnvironmentVariables is false or is not set'() { when: def project = createProjectWithWrapper() @@ -292,6 +300,7 @@ class EnvVariableSpecification extends BaseKonanSpecification { resultFalseValue == 0 } + @Ignore("The plugin doesn't use env vars until https://github.com/gradle/gradle/issues/3468 is fixed.") def 'Up-to-date checks should work with different directories for different targets'() { when: def project = createProjectWithWrapper() diff --git a/tools/kotlin-native-gradle-plugin/src/test/kotlin/PropertiesAsEnvVariables.kt b/tools/kotlin-native-gradle-plugin/src/test/kotlin/PropertiesAsEnvVariables.kt new file mode 100644 index 00000000000..83d5dd19fb5 --- /dev/null +++ b/tools/kotlin-native-gradle-plugin/src/test/kotlin/PropertiesAsEnvVariables.kt @@ -0,0 +1,183 @@ +package org.jetbrains.kotlin.gradle.plugin.test + +import org.jetbrains.kotlin.gradle.plugin.test.KonanProject.escapeBackSlashes +import org.jetbrains.kotlin.konan.target.HostManager +import org.jetbrains.kotlin.konan.target.KonanTarget +import org.junit.Rule +import org.junit.rules.TemporaryFolder +import org.junit.runner.RunWith +import org.tools4j.spockito.Spockito +import java.io.File +import kotlin.test.* + +@RunWith(Spockito::class) +open class PropertiesAsEnvVariables { + + val tmpFolder = TemporaryFolder() + @Rule get + + val projectDirectory: File + get() = tmpFolder.root + + private fun artifactFileName(baseName: String, type: ArtifactType, target: KonanTarget = HostManager.host): String { + var suffix = "" + var prefix = "" + when (type) { + ArtifactType.PROGRAM -> suffix = target.family.exeSuffix + ArtifactType.INTEROP, + ArtifactType.LIBRARY -> suffix = "klib" + ArtifactType.BITCODE -> suffix = "bc" + ArtifactType.FRAMEWORK -> suffix = "framework" + ArtifactType.DYNAMIC -> { + prefix = target.family.dynamicPrefix + suffix = target.family.dynamicSuffix + } + + } + return "$prefix${baseName}.$suffix" + } + + private fun assertFileExists(directory: File, filename: String) = assert(directory.list().contains(filename)) { + "No such file: $filename in directory: ${directory.absolutePath}" + } + + @Test + @Spockito.Unroll( + "|property |value |assertion |message |", + "|konan.debugging.symbols |YES |it.enableDebug |Debug should be enabled |", + "|konan.debugging.symbols |true |it.enableDebug |Debug should be enabled |", + "|konan.debugging.symbols |NO |!it.enableDebug |Debug should be disabled |", + "|konan.debugging.symbols |false |!it.enableDebug |Debug should be disabled |", + "|konan.optimizations.enable |YES |it.enableOptimizations |Opts should be enabled |", + "|konan.optimizations.enable |true |it.enableOptimizations |Opts should be enabled |", + "|konan.optimizations.enable |NO |!it.enableOptimizations |Opts should be disabled |", + "|konan.optimizations.enable |false |!it.enableOptimizations |Opts should be disabled |" + ) + @Spockito.Name("[{row}]: {variable}={value}") + fun `Plugin should support enabling and disabling debug and opt options via a project property`( + property: String, + value: String, + assertion: String, + message: String + ) { + val project = KonanProject.createEmpty(projectDirectory) + project.buildFile.appendText(""" + + apply plugin: 'konan' + konanArtifacts { + library('main') + } + + task assertEnableDebug { + doLast { + konanArtifacts.main.forEach { + if (!($assertion)) throw new AssertionError("$message for ${'$'}it.name") + } + } + } + """.trimIndent()) + project.createRunner() + .withArguments("assertEnableDebug", "-P${property}=${value}") + .build() + } + + @Test + fun `Plugin should support setting destination directory via a project property`() { + val project = KonanProject.createEmpty(projectDirectory) + val newDestinationDir = project.createSubDir("newDestination") + val newDestinationPath = newDestinationDir.absolutePath + project.buildFile.appendText(""" + apply plugin: 'konan' + konanArtifacts { + program('program') + library('library') + dynamic('dynamic') + framework('framework') + } + + task assertDestinationDir { + doLast { + konanArtifacts.forEach { artifact -> + artifact.forEach { + if (it.destinationDir.absolutePath != '${escapeBackSlashes(newDestinationPath)}'){ + throw new AssertionError("Unexpected destination dir for ${'$'}it.name\\n" + + "expected: ${escapeBackSlashes(newDestinationPath)}\\n" + + "actual: ${'$'}it.destinationDir") + } + } + } + } + } + """.trimIndent()) + project.generateSrcFile("main.kt") + project.createRunner() + .withArguments("assertDestinationDir", "build", "-Pkonan.configuration.build.dir=$newDestinationPath") + .build() + + assertFileExists(newDestinationDir, artifactFileName("program", ArtifactType.PROGRAM)) + assertFileExists(newDestinationDir, artifactFileName("library", ArtifactType.LIBRARY)) + assertFileExists(newDestinationDir, artifactFileName("dynamic", ArtifactType.DYNAMIC)) + if (HostManager.hostIsMac) { + assertFileExists(newDestinationDir, artifactFileName("framework", ArtifactType.FRAMEWORK)) + } + } + + @Test + fun `Plugin should rerun tasks if konan_configuration_build_dir has been changed`() { + val project = KonanProject.createEmpty(projectDirectory) + val destination1 = project.createSubDir("destination1", "subdir") + val destination2 = project.createSubDir("destination2", "subdir") + + project.buildFile.appendText(""" + apply plugin: 'konan' + konanArtifacts { + library('main') + } + """.trimIndent()) + project.generateSrcFile("main.kt") + + project.createRunner() + .withArguments("build", "-Pkonan.configuration.build.dir=${destination1.absolutePath}") + .build() + project.createRunner() + .withArguments("build", "-Pkonan.configuration.build.dir=${destination2.absolutePath}") + .build() + + assertFileExists(destination1, artifactFileName("main", ArtifactType.LIBRARY)) + assertFileExists(destination2, artifactFileName("main", ArtifactType.LIBRARY)) + } + + fun `Up-to-date checks should work with different directories for different targets`() { + val project = KonanProject.createEmpty(projectDirectory) + val fooDir = project.createSubDir("foo") + val barDir = project.createSubDir("bar") + project.buildFile.appendText(""" + apply plugin: 'konan' + + konanArtifacts { + library('foo') + library('bar') + } + + task assertUpToDate { + dependsOn 'compileKonanFoo' + doLast { + if (!konanArtifacts.foo.getByTarget('host').state.upToDate) { + throw new AssertionError("Compilation task is not up-to-date") + } + } + } + """.trimIndent()) + project.generateSrcFile("main.kt") + + project.createRunner() + .withArguments("compileKonanFoo", "-Pkonan.configuration.build.dir=${fooDir.absolutePath}") + .build() + project.createRunner() + .withArguments("compileKonanBar", "-Pkonan.configuration.build.dir=${barDir.absolutePath}") + .build() + project.createRunner() + .withArguments("assertUpToDate", "-Pkonan.configuration.build.dir=${fooDir.absolutePath}") + .build() + } +} \ No newline at end of file