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 248fb3b82be..39d430c0b34 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 @@ -1,13 +1,53 @@ package org.jetbrains.kotlin.gradle.plugin +import org.gradle.api.Project +import org.jetbrains.kotlin.gradle.plugin.KonanPlugin.ProjectProperty import java.io.File -object EnvironmentVariables { +/** + * The plugin allows an IDE to specify some building parameters. These parameters + * are passed to the plugin via environment variables. Two variables are supported: + * - CONFIGURATION_BUILD_DIR - an absolute path to a destination directory for all compilation tasks. + * The IDE should take care about specifying different directories + * for different targets. This setting has less priority than + * an explicitly specified destination directory in the build script. + * - DEBUGGING_SYMBOLS - If YES, the debug support will be enabled for all artifacts. This option has less + * priority than explicitly specified enableDebug option in the build script and + * enableDebug project property. + * + * Support for environment variables should be explicitly enabled by setting a project property: + * konan.useEnvironmentVariables = true. + */ + +internal interface EnvironmentVariables { val configurationBuildDir: File? + val debuggingSymbols: Boolean +} + +internal class EnvironmentVariablesUnused: EnvironmentVariables { + override val configurationBuildDir: File? + get() = null + + override val debuggingSymbols: Boolean + get() = false +} + +internal class EnvironmentVariablesImpl: EnvironmentVariables { + override val configurationBuildDir: File? get() = System.getenv("CONFIGURATION_BUILD_DIR")?.let { - File(it).apply {check(isAbsolute) { "A path passed using CONFIGURATION_BUILD_DIR should be absolute" } } + File(it).apply { check(isAbsolute) { "A path passed using CONFIGURATION_BUILD_DIR should be absolute" } } } - val debuggingSymbols: Boolean + override val debuggingSymbols: Boolean get() = System.getenv("DEBUGGING_SYMBOLS")?.toUpperCase() == "YES" -} \ No newline at end of file +} + +internal val Project.useEnvironmentVariables: Boolean + get() = findProperty(ProjectProperty.KONAN_USE_ENVIRONMENT_VARIABLES)?.toString()?.toBoolean() ?: false + +internal val Project.environmentVariables: EnvironmentVariables + get() = if (useEnvironmentVariables) { + EnvironmentVariablesImpl() + } else { + EnvironmentVariablesUnused() + } diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KonanBuildingConfig.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KonanBuildingConfig.kt index 3876f68f3a2..ff55f643374 100644 --- a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KonanBuildingConfig.kt +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KonanBuildingConfig.kt @@ -100,12 +100,12 @@ abstract class KonanBuildingConfig(private val name_: Stri // In this case all produced files has the same name but are placed in different directories // depending on their targets (e.g. linux/foo.kexe and macbook/foo.kexe). // 2. Custom path provided by IDE. In this case CONFIGURATION_BUILD_DIR environment variable should - // contain a path to a destination directory. All produced files are placed in this directory and have - // different names depending on their target (e.g. foo/bar/baz_linux.kexe and foo/bar/baz_macbook.kexe). + // contain a path to a destination directory. All produced files are placed in this directory so IDE + // should take care about setting different CONFIGURATION_BUILD_DIR for different targets. protected fun determineOutputPlacement(target: KonanTarget): OutputPlacement { - val configurationBuildDir = EnvironmentVariables.configurationBuildDir + val configurationBuildDir = project.environmentVariables.configurationBuildDir return if (configurationBuildDir != null) { - OutputPlacement(configurationBuildDir, "${name}_${target.visibleName}") + OutputPlacement(configurationBuildDir, name) } else { OutputPlacement(defaultBaseDir.targetSubdir(target), name) } 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 4567b58ba80..f92a541ed53 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 @@ -245,11 +245,12 @@ class KonanPlugin @Inject constructor(private val registry: ToolingModelBuilderR : Plugin { enum class ProjectProperty(val propertyName: String) { - KONAN_HOME ("konan.home"), - KONAN_VERSION ("konan.version"), - KONAN_BUILD_TARGETS ("konan.build.targets"), - KONAN_JVM_ARGS ("konan.jvmArgs"), - DOWNLOAD_COMPILER ("download.compiler") + KONAN_HOME ("konan.home"), + KONAN_VERSION ("konan.version"), + KONAN_BUILD_TARGETS ("konan.build.targets"), + KONAN_JVM_ARGS ("konan.jvmArgs"), + KONAN_USE_ENVIRONMENT_VARIABLES("konan.useEnvironmentVariables"), + DOWNLOAD_COMPILER ("download.compiler") } companion object { diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/tasks/KonanCompileTask.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/tasks/KonanCompileTask.kt index 235a5270f17..e4d75de1093 100644 --- a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/tasks/KonanCompileTask.kt +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/tasks/KonanCompileTask.kt @@ -70,7 +70,7 @@ abstract class KonanCompileTask: KonanBuildingTask(), KonanCompileSpec { @Input val linkerOpts = mutableListOf() @Input var enableDebug = project.findProperty("enableDebug")?.toString()?.toBoolean() - ?: EnvironmentVariables.debuggingSymbols + ?: project.environmentVariables.debuggingSymbols @Input var noStdLib = false @Input var noMain = false 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 ac778984385..23362f3f358 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 @@ -1,3 +1,19 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.jetbrains.kotlin.gradle.plugin.test import org.jetbrains.kotlin.konan.target.Family @@ -7,6 +23,8 @@ import spock.lang.Unroll import static org.jetbrains.kotlin.gradle.plugin.test.KonanProject.escapeBackSlashes +// TODO: Rewrite tests using Kotlin. + class EnvVariableSpecification extends BaseKonanSpecification { class WrapperResult { @@ -49,10 +67,14 @@ class EnvVariableSpecification extends BaseKonanSpecification { return project } - private WrapperResult runWrapper(KonanProject project, List tasks, Map environment = [:]) { + private WrapperResult runWrapper(KonanProject project, + List tasks, + Map environment = [:], + Map properties = ["konan.useEnvironmentVariables": 'true']) { def wrapper = (HostManager.host.family == Family.WINDOWS) ? "gradlew.bat" : "gradlew" def command = ["$project.projectDir.absolutePath/$wrapper".toString()] command.addAll(tasks) + command.addAll(properties.collect { "-P${it.key}=${it.value}".toString() }) def projectBuilder = new ProcessBuilder() .directory(project.projectDir) .command(command) @@ -62,8 +84,11 @@ class EnvVariableSpecification extends BaseKonanSpecification { return new WrapperResult(process) } - private WrapperResult runWrapper(KonanProject project, String task, Map environment = [:]) { - return runWrapper(project, [task], environment) + private WrapperResult runWrapper(KonanProject project, + String task, + Map environment = [:], + Map properties = ["konan.useEnvironmentVariables": 'true']) { + return runWrapper(project, [task], environment, properties) } private String artifactFileName(String baseName, ArtifactType type, KonanTarget target = HostManager.host) { @@ -85,7 +110,7 @@ class EnvVariableSpecification extends BaseKonanSpecification { suffix = target.family.dynamicSuffix break } - return "$prefix${baseName}_${target.visibleName}.$suffix" + return "$prefix${baseName}.$suffix" } @Unroll("Plugin should support #action debug via an env variable") @@ -214,4 +239,99 @@ class EnvVariableSpecification extends BaseKonanSpecification { files2.contains(artifactFileName("main", ArtifactType.LIBRARY)) } + def 'Plugin should ignore environmentVariables if konan.useEnvironmentVariables is false or is not set'() { + when: + def project = createProjectWithWrapper() + def newDestinationDir = project.createSubDir("newDestination") + def newDestinationPath = newDestinationDir.absolutePath + project.buildFile.append("""\ + apply plugin: 'konan' + konanArtifacts { + program('program') + library('library') + dynamic('dynamic') + framework('framework') + } + + task assertNoOverrides { + doLast { + konanArtifacts.forEach { artifact -> + artifact.forEach { + if (it.destinationDir.absolutePath == '${escapeBackSlashes(newDestinationPath)}'){ + throw new AssertionError("CONFIGURATION_BUILD_DIR overrides a default output path " + + "when it shouldn't.\\n" + + "Task: \${it.name}, Path: \${it.destinationDir}") + } + + if (it.enableDebug) { + throw new AssertionError("DEBUGGING_SYMBOLS overrides a default value " + + "when it shouldn't\\n" + + "Task: \${it.name}") + } + } + } + } + } + """.stripIndent()) + def resultNoProp = runWrapper(project, + "assertNoOverrides", + ["DEBUGGING_SYMBOLS": "true", "CONFIGURATION_BUILD_DIR": newDestinationPath], [:]) + .printStderr() + .getExitValue() + def resultFalseValue = runWrapper(project, + "assertNoOverrides", + ["DEBUGGING_SYMBOLS": "true", "CONFIGURATION_BUILD_DIR": newDestinationPath], + ["konan.useEnvironmentVariables": "false"]) + .printStderr() + .getExitValue() + + then: + resultNoProp == 0 + resultFalseValue == 0 + } + + def 'Up-to-date checks should work with different directories for different targets'() { + when: + def project = createProjectWithWrapper() + def fooDir = project.createSubDir("foo") + def barDir = project.createSubDir("bar") + project.buildFile.append("""\ + 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") + } + } + } + """.stripIndent()) + project.generateSrcFile("main.kt") + + def buildResult1 = runWrapper(project, "compileKonanFoo", + ["CONFIGURATION_BUILD_DIR": fooDir.absolutePath]) + .printStderr() + .getExitValue() + def buildResult2 = runWrapper(project, "compileKonanBar", + ["CONFIGURATION_BUILD_DIR": barDir.absolutePath]) + .printStderr() + .getExitValue() + def buildResult3 = runWrapper(project, + "assertUpToDate", + ["CONFIGURATION_BUILD_DIR": fooDir.absolutePath]) + .printStderr() + .getExitValue() + + then: + buildResult1 == 0 + buildResult2 == 0 + buildResult3 == 0 + } + }