[K/N][build] Move old Konan plugin to native-build-tools

This commit is contained in:
Pavel Punegov
2023-08-31 15:27:19 +02:00
committed by Space Team
parent 04c5ac0eb6
commit b1a85d45c6
34 changed files with 76 additions and 2916 deletions
@@ -92,7 +92,6 @@ kotlin {
sourceSets {
main {
kotlin.srcDir("src/main/kotlin")
kotlin.srcDir("../../kotlin-native/tools/kotlin-native-gradle-plugin/src/main/kotlin")
}
}
}
@@ -1,17 +1,6 @@
/*
* 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.
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.gradle.plugin.konan
@@ -58,7 +47,7 @@ internal class EnvironmentVariablesUnused: EnvironmentVariables {
get() = false
}
internal class EnvironmentVariablesImpl(val project: Project): EnvironmentVariables {
internal class EnvironmentVariablesImpl(val project: Project): EnvironmentVariables {
override val configurationBuildDir: File?
get() = System.getenv("CONFIGURATION_BUILD_DIR")?.let {
project.file(it)
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2017 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.
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.gradle.plugin.konan
@@ -67,9 +56,9 @@ open class KonanArtifactContainer(val project: ProjectInternal) : DefaultPolymor
}
private fun <T: KonanBuildingConfig<*>> create(name: String,
configClass: KClass<T>,
args: Map<String, Any?>,
configureAction: Action<T>) {
configClass: KClass<T>,
args: Map<String, Any?>,
configureAction: Action<T>) {
determineTargets(configClass, args)
super.create(name, configClass.java, configureAction)
}
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2017 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.
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.gradle.plugin.konan
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2017 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.
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.gradle.plugin.konan
@@ -81,9 +70,9 @@ open class KonanProgram(name: String,
project: ProjectInternal,
targets: Iterable<String> = project.konanExtension.targets
) : KonanCompileConfig<KonanCompileProgramTask>(name,
KonanCompileProgramTask::class.java,
project,
targets
KonanCompileProgramTask::class.java,
project,
targets
) {
override val typeForDescription: String
get() = "executable"
@@ -96,9 +85,9 @@ open class KonanDynamic(name: String,
project: ProjectInternal,
targets: Iterable<String> = project.konanExtension.targets)
: KonanCompileConfig<KonanCompileDynamicTask>(name,
KonanCompileDynamicTask::class.java,
project,
targets
KonanCompileDynamicTask::class.java,
project,
targets
) {
override val typeForDescription: String
get() = "dynamic library"
@@ -113,9 +102,9 @@ open class KonanFramework(name: String,
project: ProjectInternal,
targets: Iterable<String> = project.konanExtension.targets)
: KonanCompileConfig<KonanCompileFrameworkTask>(name,
KonanCompileFrameworkTask::class.java,
project,
targets
KonanCompileFrameworkTask::class.java,
project,
targets
) {
override val typeForDescription: String
get() = "framework"
@@ -131,9 +120,9 @@ open class KonanLibrary(name: String,
project: ProjectInternal,
targets: Iterable<String> = project.konanExtension.targets)
: KonanCompileConfig<KonanCompileLibraryTask>(name,
KonanCompileLibraryTask::class.java,
project,
targets
KonanCompileLibraryTask::class.java,
project,
targets
) {
override val typeForDescription: String
get() = "library"
@@ -146,9 +135,9 @@ open class KonanBitcode(name: String,
project: ProjectInternal,
targets: Iterable<String> = project.konanExtension.targets)
: KonanCompileConfig<KonanCompileBitcodeTask>(name,
KonanCompileBitcodeTask::class.java,
project,
targets
KonanCompileBitcodeTask::class.java,
project,
targets
) {
override val typeForDescription: String
get() = "bitcode"
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2017 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.
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.gradle.plugin.konan
@@ -29,7 +18,7 @@ open class KonanInteropLibrary(name: String,
project: ProjectInternal,
targets: Iterable<String> = project.konanExtension.targets
) : KonanBuildingConfig<KonanInteropTask>(name, KonanInteropTask::class.java, project, targets),
KonanInteropSpec
KonanInteropSpec
{
override fun generateTaskDescription(task: KonanInteropTask) =
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2017 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.
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.gradle.plugin.konan
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2017 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.
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.gradle.plugin.konan
@@ -352,10 +341,10 @@ class KonanPlugin @Inject constructor(private val registry: ToolingModelBuilderR
// Create necessary tasks and extensions.
project.extensions.create(KONAN_EXTENSION_NAME, KonanExtension::class.java)
val container = project.extensions.create(
KonanArtifactContainer::class.java,
ARTIFACTS_CONTAINER_NAME,
KonanArtifactContainer::class.java,
project
KonanArtifactContainer::class.java,
ARTIFACTS_CONTAINER_NAME,
KonanArtifactContainer::class.java,
project
)
project.setProperty(ProjectProperty.KONAN_JVM_LAUNCHER, getJavaLauncher(project))
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2017 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.
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.gradle.plugin.konan
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2017 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.
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.gradle.plugin.konan
@@ -1,3 +1,8 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.gradle.plugin.konan
import org.gradle.api.Named
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2017 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.
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.gradle.plugin.tasks
@@ -35,6 +24,8 @@ import org.jetbrains.kotlin.gradle.plugin.experimental.internal.compatibleVarian
import org.jetbrains.kotlin.gradle.plugin.konan.*
import org.jetbrains.kotlin.konan.target.HostManager
import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.gradle.plugin.konan.*
import org.jetbrains.kotlin.gradle.plugin.konan.konanTargets
import java.io.File
import java.util.*
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2017 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.
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.gradle.plugin.tasks
@@ -24,6 +13,10 @@ import org.jetbrains.kotlin.gradle.plugin.konan.*
import org.jetbrains.kotlin.konan.target.KonanTarget
import java.io.File
import org.jetbrains.kotlin.dependsOnDist
import org.jetbrains.kotlin.gradle.plugin.konan.KonanBuildingConfig
import org.jetbrains.kotlin.gradle.plugin.konan.KonanBuildingSpec
import org.jetbrains.kotlin.gradle.plugin.konan.KonanToolRunner
import org.jetbrains.kotlin.gradle.plugin.konan.konanHome
/** Base class for both interop and compiler tasks. */
abstract class KonanBuildingTask: KonanArtifactWithLibrariesTask(), KonanBuildingSpec {
@@ -1,3 +1,8 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.gradle.plugin.konan.tasks
import org.gradle.api.DefaultTask
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2019 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.
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.gradle.plugin.tasks
@@ -23,11 +12,15 @@ import org.gradle.api.file.ConfigurableFileTree
import org.gradle.api.file.FileCollection
import org.gradle.api.tasks.*
import org.gradle.process.CommandLineArgumentProvider
import org.jetbrains.kotlin.gradle.plugin.konan.*
import org.jetbrains.kotlin.konan.library.defaultResolver
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
import org.jetbrains.kotlin.konan.target.Distribution
import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.gradle.plugin.konan.*
import org.jetbrains.kotlin.gradle.plugin.konan.dumpProperties
import org.jetbrains.kotlin.gradle.plugin.konan.environmentVariables
import org.jetbrains.kotlin.gradle.plugin.konan.konanBuildRoot
import org.jetbrains.kotlin.gradle.plugin.konan.targetSubdir
import java.io.File
/**
@@ -1,17 +1,6 @@
/*
* Copyright 2010-2017 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.
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* 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.gradle.plugin.tasks
@@ -26,9 +15,13 @@ import org.gradle.workers.WorkerExecutor
import org.jetbrains.kotlin.gradle.plugin.konan.*
import org.jetbrains.kotlin.gradle.plugin.konan.KonanInteropSpec.IncludeDirectoriesSpec
import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.gradle.plugin.konan.dumpProperties
import org.jetbrains.kotlin.gradle.plugin.konan.konanDefaultDefFile
import org.jetbrains.kotlin.gradle.plugin.konan.konanExtension
import java.io.File
import java.util.concurrent.ConcurrentHashMap
import javax.inject.Inject
import kotlin.run
/**
* A task executing cinterop tool with the given args and compiling the stubs produced by this tool.
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
@@ -1,196 +0,0 @@
/*
* Copyright 2010-2017 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.
*/
import org.jetbrains.kotlin.konan.*
buildscript {
ext.rootBuildDirectory = file('../../')
apply from: "$rootBuildDirectory/gradle/loadRootProperties.gradle"
apply from: "$rootBuildDirectory/gradle/kotlinGradlePlugin.gradle"
repositories {
mavenCentral()
maven {
url "https://cache-redirector.jetbrains.com/plugins.gradle.org/m2/"
}
gradlePluginPortal()
}
dependencies {
classpath 'com.github.johnrengelman:shadow:8.1.1'
classpath "org.jetbrains.kotlin:kotlin-native-shared:$kotlinVersion"
}
}
apply plugin: 'java-gradle-plugin'
apply plugin: 'kotlin'
apply plugin: 'groovy'
apply plugin: 'com.github.johnrengelman.shadow'
group = 'org.jetbrains.kotlin'
version = CompilerVersionGeneratedKt.getCurrentCompilerVersion()
repositories {
mavenCentral()
}
configurations {
bundleDependencies {
transitive = false
}
implementation.extendsFrom shadow
compileOnly.extendsFrom bundleDependencies
testImplementation.extendsFrom bundleDependencies
}
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach {
kotlinOptions.freeCompilerArgs = ["-Xskip-prerelease-check"]
}
dependencies {
shadow "org.jetbrains.kotlin:kotlin-stdlib:1.3.0"
// Bundle the serialization plugin into the final jar because we shade classes of the kotlin plugin
// while the serialization one extends them.
bundleDependencies "org.jetbrains.kotlin:kotlin-serialization:$kotlinVersion"
bundleDependencies "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
bundleDependencies "org.jetbrains.kotlin:kotlin-gradle-plugin-api:$kotlinVersion"
bundleDependencies "org.jetbrains.kotlin:kotlin-native-utils:$kotlinVersion"
bundleDependencies "org.jetbrains.kotlin:kotlin-native-shared:$kotlinVersion"
bundleDependencies "org.jetbrains.kotlin:kotlin-util-io:$kotlinVersion"
bundleDependencies "org.jetbrains.kotlin:kotlin-util-klib:$kotlinVersion"
testImplementation DependenciesKt.commonDependency(project, "junit")
testImplementation "org.jetbrains.kotlin:kotlin-test:${project.bootstrapKotlinVersion}"
testImplementation "org.jetbrains.kotlin:kotlin-test-junit:${project.bootstrapKotlinVersion}"
testImplementation "org.tools4j:tools4j-spockito:1.6"
testImplementation('org.spockframework:spock-core:1.1-groovy-2.4') {
exclude module: 'groovy-all'
}
}
shadowJar {
from sourceSets.main.output
configurations = [project.configurations.bundleDependencies]
archiveClassifier.set(null)
relocate('org.jetbrains.kotlinx', 'shadow.org.jetbrains.kotlinx')
relocate('org.jetbrains.kotlin.compilerRunner', 'shadow.org.jetbrains.kotlin.compilerRunner')
relocate('org.jetbrains.kotlin.konan', 'shadow.org.jetbrains.kotlin.konan')
relocate('org.jetbrains.kotlin.gradle', 'shadow.org.jetbrains.kotlin.gradle') {
exclude('org.jetbrains.kotlin.gradle.plugin.experimental.**')
exclude('org.jetbrains.kotlin.gradle.plugin.konan.**')
exclude('org.jetbrains.kotlin.gradle.plugin.model.**')
}
exclude {
def path = it.relativePath.pathString
if (path.startsWith("META-INF/gradle-plugins") && path.endsWith(".properties")) {
def fileName = it.name
def id = fileName.take(fileName.lastIndexOf('.'))
return project.gradlePlugin.plugins.findByName(id) == null
}
return false
}
exclude('META-INF/services/org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar')
exclude('META-INF/services/org.jetbrains.kotlin.gradle.plugin.KotlinGradleSubplugin')
}
jar {
dependsOn shadowJar
enabled = false
}
pluginUnderTestMetadata {
dependsOn shadowJar
doLast {
// Since Gradle 4.10 it isn't possible to edit the pluginUnderTest classpath.
// So we have to manually set the implementation-classpath to get the output fat-jar.
def pluginMetadata = outputDirectory.get().file(PluginUnderTestMetadata.METADATA_FILE_NAME).getAsFile()
def classpath = files(shadowJar.archivePath) + configurations.shadow
new Properties().with { properties ->
pluginMetadata.withInputStream {
properties.load(it)
}
properties.setProperty(PluginUnderTestMetadata.IMPLEMENTATION_CLASSPATH_PROP_KEY , classpath.asPath)
pluginMetadata.withOutputStream {
properties.store(it, null)
}
}
}
}
test {
dependsOn shadowJar
systemProperty("kotlin.version", kotlinVersion)
systemProperty("kotlin.repo", project.bootstrapKotlinRepo)
if (project.hasProperty("konan.home")) {
systemProperty("konan.home", project.property("konan.home"))
systemProperty("org.jetbrains.kotlin.native.home", project.property("konan.home"))
} else if (project.hasProperty("org.jetbrains.kotlin.native.home")) {
systemProperty("org.jetbrains.kotlin.native.home", project.property("org.jetbrains.kotlin.native.home"))
} else {
// The Koltin/Native compiler must be built before test execution.
systemProperty("konan.home", distDir.absolutePath)
systemProperty("org.jetbrains.kotlin.native.home", distDir.absolutePath)
}
if (project.hasProperty("konan.jvmArgs")) {
systemProperty("konan.jvmArgs", project.property("konan.jvmArgs"))
}
// Uncomment for debugging.
//testLogging.showStandardStreams = true
if (project.hasProperty("maxParallelForks")) {
maxParallelForks=project.property("maxParallelForks")
}
if (project.hasProperty("filter")) {
filter.includeTestsMatching project.property("filter")
}
if (project.hasProperty("gradleVersion")) {
systemProperty("gradleVersion", project.property("gradleVersion"))
}
}
processResources {
from(file("$rootBuildDirectory/utilities/env_blacklist"))
}
tasks.named('compileTestGroovy') {
classpath = sourceSets.test.compileClasspath
}
tasks.named('compileTestKotlin') {
classpath += files(sourceSets.test.groovy.classesDirectory)
}
gradlePlugin {
plugins {
create('konan') {
id = 'konan'
implementationClass = 'org.jetbrains.kotlin.gradle.plugin.konan.KonanPlugin'
}
// We bundle a shaded version of kotlinx-serialization plugin
create('kotlinx-serialization-native') {
id = 'kotlinx-serialization-native'
implementationClass = 'shadow.org.jetbrains.kotlinx.serialization.gradle.SerializationGradleSubplugin'
}
create('org.jetbrains.kotlin.konan') {
id = 'org.jetbrains.kotlin.konan'
implementationClass = 'org.jetbrains.kotlin.gradle.plugin.konan.KonanPlugin'
}
}
}
@@ -1,2 +0,0 @@
rootProject.name = "kotlin-native-gradle-plugin"
includeBuild '../../shared'
@@ -1,17 +0,0 @@
#
# Copyright 2010-2017 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.
#
shadow.org.jetbrains.kotlinx.serialization.gradle.SerializationKotlinGradleSubplugin
@@ -1,29 +0,0 @@
/*
* 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.junit.Rule
import org.junit.rules.TemporaryFolder
import spock.lang.Specification
class BaseKonanSpecification extends Specification {
@Rule
TemporaryFolder tmpFolder = new TemporaryFolder()
File getProjectDirectory() { return tmpFolder.root }
}
@@ -1,42 +0,0 @@
/*
* 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.gradle.testkit.runner.TaskOutcome
class DefaultSpecification extends BaseKonanSpecification {
def 'Plugin should build a project without additional settings'() {
when:
def project = KonanProject.createEmpty(projectDirectory) { KonanProject it ->
it.buildFile.write("""
plugins { id 'konan' }
konanArtifacts {
interop('stdio')
library('main')
}
""".stripIndent())
it.generateDefFile("stdio.def", "")
it.generateSrcFile("main.kt")
}
def result = project.createRunner().withArguments('build').build()
then:
!result.tasks.collect { it.outcome }.contains(TaskOutcome.FAILED)
}
}
@@ -1,336 +0,0 @@
/*
* 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
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
// TODO: Rewrite tests using Kotlin.
class EnvVariableSpecification extends BaseKonanSpecification {
class WrapperResult {
private int exitValue;
private String stdout;
private String stderr;
WrapperResult(Process process) {
exitValue = process.exitValue()
stdout = process.getInputStream().readLines().join("\n")
stderr = process.getErrorStream().readLines().join("\n")
}
int getExitValue() { return exitValue }
String getStdout() { return stdout }
String getStderr() { return stderr }
WrapperResult printStdout() { println(stdout); return this }
WrapperResult printStderr() { println(stderr); return this }
}
private KonanProject createProjectWithWrapper() {
def project = KonanProject.createEmpty(projectDirectory)
def runner = project.createRunner()
// Gradle TestKit doesn't support setting environment variables for runners.
// So we use the following hack: we create a gradle wrapper, start it as a separate
// process with custom environment variables and check its exit code and output.
runner.withArguments("wrapper").build()
def classpath = runner.pluginClasspath.collect { "'${escapeBackSlashes(it.absolutePath)}'" }.join(", ")
project.buildFile.write("""\
buildscript {
dependencies {
classpath files($classpath)
}
}
""".stripIndent())
return project
}
private WrapperResult runWrapper(KonanProject project,
List<String> tasks,
Map<String, String> environment = [:],
Map<String, String> properties = ["konan.useEnvironmentVariables": 'true']) {
def wrapper = (HostManager.host.family == Family.MINGW) ? "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)
projectBuilder.environment().putAll(environment)
def process = projectBuilder.start()
process.waitFor()
return new WrapperResult(process)
}
private WrapperResult runWrapper(KonanProject project,
String task,
Map<String, String> environment = [:],
Map<String, String> properties = ["konan.useEnvironmentVariables": 'true']) {
return runWrapper(project, [task], environment, properties)
}
private String artifactFileName(String baseName, ArtifactType type, KonanTarget target = HostManager.host) {
String suffix = ""
String prefix = ""
switch (type) {
case ArtifactType.PROGRAM:
suffix = target.family.exeSuffix
break
case ArtifactType.INTEROP:
case ArtifactType.LIBRARY:
suffix = "klib"
break
case ArtifactType.BITCODE:
suffix = "bc"
break;
case ArtifactType.DYNAMIC:
prefix = target.family.dynamicPrefix
suffix = target.family.dynamicSuffix
break
case ArtifactType.STATIC:
prefix = target.family.staticPrefix
suffix = target.family.staticSuffix
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:
def project = createProjectWithWrapper()
project.buildFile.append("""\
apply plugin: 'konan'
konanArtifacts {
library('main')
}
task assertEnableDebug {
doLast {
konanArtifacts.main.forEach {
if (!($assertion)) throw new AssertionError("$message for \${it.name}")
}
}
}
""".stripIndent())
def result = runWrapper(project,"assertEnableDebug", [(variable): value])
.printStderr()
.getExitValue()
then:
result == 0
where:
action |variable |value |assertion |message
"enabling debug" |"DEBUGGING_SYMBOLS" |"YES" |"it.enableDebug" |"Debug should be enabled"
"disabling debug" |"DEBUGGING_SYMBOLS" |"NO" |"!it.enableDebug" |"Debug should be disabled"
"enabling opt" |"KONAN_ENABLE_OPTIMIZATIONS" |"YES" |"it.enableOptimizations" |"Opts should be enabled"
"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()
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 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")
}
}
}
}
}
""".stripIndent())
project.generateSrcFile("main.kt")
def assertResult = runWrapper(project, "assertDestinationDir", ["CONFIGURATION_BUILD_DIR": newDestinationPath])
.printStderr()
.getExitValue()
def buildResult = runWrapper(project, "build", ["CONFIGURATION_BUILD_DIR": newDestinationPath])
.printStderr()
.getExitValue()
def files = newDestinationDir.list()
then:
assertResult == 0
buildResult == 0
files.contains(artifactFileName("program", ArtifactType.PROGRAM))
files.contains(artifactFileName("library", ArtifactType.LIBRARY))
files.contains(artifactFileName("dynamic", ArtifactType.DYNAMIC))
files.contains(artifactFileName("static", ArtifactType.STATIC))
if (HostManager.hostIsMac) {
files.contains(artifactFileName("framework", ArtifactType.FRAMEWORK))
}
}
@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()
def destination1 = project.createSubDir("destination1", "subdir")
def destination2 = project.createSubDir("destination2", "subdir")
project.buildFile.append("""\
apply plugin: 'konan'
konanArtifacts {
library('main')
}
""".stripIndent())
project.generateSrcFile("main.kt")
def buildResult1 = runWrapper(project, "build", ["CONFIGURATION_BUILD_DIR": destination1.absolutePath])
.printStderr()
.getExitValue()
def buildResult2 = runWrapper(project, "build", ["CONFIGURATION_BUILD_DIR": destination2.absolutePath])
.printStderr()
.getExitValue()
def files1 = destination1.list()
def files2 = destination2.list()
then:
buildResult1 == 0
buildResult2 == 0
destination1.exists()
destination2.exists()
files1.contains(artifactFileName("main", ArtifactType.LIBRARY))
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()
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
}
@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()
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
}
}
@@ -1,312 +0,0 @@
/*
* 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.gradle.testkit.runner.BuildResult
import org.gradle.testkit.runner.TaskOutcome
import spock.lang.Unroll
class IncrementalSpecification extends BaseKonanSpecification {
Tuple buildTwice(KonanProject project, String task = 'build', Closure change) {
def runner = project.createRunner().withArguments(task)
def firstResult = runner.build()
change(project)
def secondResult = runner.build()
return new Tuple(project, firstResult, secondResult)
}
Tuple buildTwice(ArtifactType mainArtifactType = ArtifactType.LIBRARY, String task = 'build', Closure change) {
return buildTwice(KonanProject.createWithInterop(projectDirectory, mainArtifactType), change)
}
Boolean noRecompilationHappened(KonanProject project, BuildResult firstResult, BuildResult secondResult) {
return project.with {
firstResult.tasks.collect { it.path }.containsAll(buildingTasks) &&
firstResult.taskPaths(TaskOutcome.SUCCESS).containsAll(buildingTasks) &&
secondResult.taskPaths(TaskOutcome.UP_TO_DATE).containsAll(buildingTasks) &&
firstResult.task(downloadTask).outcome == TaskOutcome.SUCCESS &&
secondResult.task(downloadTask).outcome == TaskOutcome.SUCCESS
}
}
Boolean onlyRecompilationHappened(KonanProject project, BuildResult firstResult, BuildResult secondResult) {
return project.with {
firstResult.taskPaths(TaskOutcome.SUCCESS).containsAll(buildingTasks) &&
secondResult.taskPaths(TaskOutcome.SUCCESS).containsAll(compilationTasks) &&
secondResult.taskPaths(TaskOutcome.UP_TO_DATE).containsAll(interopTasks)
}
}
Boolean recompilationAndInteropProcessingHappened(KonanProject project, BuildResult firstResult, BuildResult secondResult) {
return project.with {
firstResult.taskPaths(TaskOutcome.SUCCESS).containsAll(buildingTasks) &&
secondResult.taskPaths(TaskOutcome.SUCCESS).containsAll(buildingTasks)
}
}
//region tests =====================================================================================================
def 'Compilation is up-to-date if there is no changes'() {
when:
def results = buildTwice {}
then:
noRecompilationHappened(*results)
}
def 'Source change should cause only recompilation'() {
when:
def results = buildTwice { KonanProject project ->
project.srcFiles[0].append("\n // Some change in the source file")
}
then:
onlyRecompilationHappened(*results)
}
def 'Def-file change should cause recompilation and interop reprocessing'() {
when:
def results = buildTwice { KonanProject project ->
project.defFiles[0].append("\n # Some change in the def-file")
}
then:
recompilationAndInteropProcessingHappened(*results)
}
@Unroll("#parameter change for a compilation task should cause only recompilation")
def 'Parameter changes should cause only recompilaton'() {
when:
def results = buildTwice { KonanProject project ->
project.addSetting("main", parameter, value)
}
then:
onlyRecompilationHappened(*results)
where:
parameter | value
"baseDir" | "'build/new/outputDir'"
"enableOptimizations" | "true"
"linkerOpts" | "'--help'"
"enableAssertions" | "true"
"enableDebug" | "true"
"artifactName" | "'foo'"
"extraOpts" | "'-Xtime'"
"noDefaultLibs" | "true"
"noEndorsedLibs" | "true"
}
def 'Plugin should support a custom entry point and recompile an artifact if it changes'() {
when:
def project = KonanProject.createEmpty(projectDirectory) { KonanProject it ->
it.addCompilerArtifact("main", """
|fun main(args: Array<String>) { println("default main") }
|
""".stripMargin())
}
def results = buildTwice(project) { KonanProject it ->
it.srcFiles[0].write("""
|package foo
|
|fun bar(args: Array<String>) { println("changed main") }
|
""".stripMargin())
it.addSetting("main", "entryPoint", "'foo.bar'")
}
then:
onlyRecompilationHappened(*results)
}
def 'srcFiles change for a compilation task should cause only recompilation'() {
when:
def project = KonanProject.createWithInterop(projectDirectory, ArtifactType.LIBRARY) { KonanProject it ->
it.generateSrcFile(["src", "foo", "kotlin"], 'bar.kt', """
fun foo(args: Array<String>) { println("Hello!") }
""".stripIndent())
}
def results = buildTwice(project) { KonanProject it ->
it.addSetting("main", "srcFiles", "project.fileTree('src/foo/kotlin')")
}
then:
onlyRecompilationHappened(*results)
}
def 'Library change for a compilation task should cause only recompilation'() {
when:
def project = KonanProject.create(projectDirectory, ArtifactType.LIBRARY) { KonanProject it ->
it.generateSrcFile(["src", "lib", "kotlin"], "lib.kt", "fun bar() { println(\"Hello!\") }")
it.buildFile.append("""
konanArtifacts {
library('lib') {
srcFiles fileTree('src/lib/kotlin')
}
}
""".stripIndent())
}
def results = buildTwice(project) { KonanProject it ->
it.addLibraryToArtifact("main", 'lib')
}
then:
onlyRecompilationHappened(*results)
}
def 'Native library change for a compilation task should cause only recompilaton'() {
when:
def project = KonanProject.createWithInterop(projectDirectory, ArtifactType.LIBRARY) { KonanProject it ->
it.generateSrcFile(["src", "lib", "kotlin"], "lib.kt", "fun bar() { println(\"Hello!\") }")
it.buildFile.append("""
konanArtifacts {
bitcode('lib') {
srcFiles fileTree('src/lib/kotlin')
}
}
""".stripIndent())
}
def results = buildTwice(project) { KonanProject it ->
it.addSetting("main", "nativeLibrary", "compileKonanLib${KonanProject.HOST.capitalize()}.artifact")
}
then:
onlyRecompilationHappened(*results)
}
// TODO: Test library for incremental compilation.
@Unroll("#parameter change for an interop task should cause recompilation and interop reprocessing")
def 'Parameter change for an interop task should cause recompilation and interop reprocessing'() {
when:
def results = buildTwice { KonanProject project ->
project.addSetting("stdio", parameter, value)
}
then:
recompilationAndInteropProcessingHappened(*results)
where:
parameter | value
"packageName" | "'org.sample'"
"compilerOpts" | "'-g'"
"linkerOpts" | "'--help'"
"includeDirs" | "'src'"
"includeDirs.allHeaders" | "'src'"
"extraOpts" | "'-verbose'"
"noDefaultLibs" | "true"
"noEndorsedLibs" | "true"
}
def 'includeDirs.headerFilterOnly change should cause recompilation and interop reprocessing'() {
when:
def project = KonanProject.createWithInterop(projectDirectory) { KonanProject it ->
it.defFiles.first().write("headers = stdio.h\nheaderFilter = stdio.h")
}
def results = buildTwice(project) { KonanProject it ->
it.addSetting(KonanProject.DEFAULT_INTEROP_NAME, "includeDirs.headerFilterOnly", "'.'")
}
then:
recompilationAndInteropProcessingHappened(*results)
}
def 'defFile change for an interop task should cause recompilation and interop reprocessing'() {
when:
def project = KonanProject.createWithInterop(projectDirectory, ArtifactType.LIBRARY)
def defFile = project.generateDefFile("foo.def", "#some content")
def results = buildTwice(project) { KonanProject it ->
it.addSetting("stdio", "defFile", defFile)
}
then:
recompilationAndInteropProcessingHappened(*results)
}
def 'header change for an interop task should cause recompilation and interop reprocessing'() {
when:
def project = KonanProject.createWithInterop(projectDirectory, ArtifactType.LIBRARY)
def header = project.generateSrcFile('header.h', "#define CONST 1")
def results = buildTwice(project) { KonanProject it ->
it.addSetting("stdio", "headers", header)
}
then:
recompilationAndInteropProcessingHappened(*results)
}
def 'link change for an interop task should cause recompilation and interop reprocessing'() {
when:
def project = KonanProject.createWithInterop(projectDirectory, ArtifactType.LIBRARY) { KonanProject it ->
it.generateSrcFile(["src", "lib", "kotlin"], 'lib.kt', 'fun foo() { println(42) }')
it.buildFile.append("""
konanArtifacts {
bitcode('lib') {
srcFiles fileTree('src/lib/kotlin')
}
}
""".stripIndent())
}
def results = buildTwice(project) { KonanProject it ->
it.addSetting("stdio", "dependsOn", "konanArtifacts.lib.${KonanProject.HOST}")
it.addSetting("stdio", "link", "files(konanArtifacts.lib.${KonanProject.HOST}.artifactPath)")
}
then:
recompilationAndInteropProcessingHappened(*results)
}
def 'Common source change should cause recompilation'() {
when:
File commonSource
def project = KonanProject.createEmpty(projectDirectory) { KonanProject it ->
def commonDirectory = MultiplatformSpecification.createCommonProject(it)
commonSource = MultiplatformSpecification.createCommonSource(commonDirectory,
["src", "main", "kotlin"],
"common.kt",
"expect fun foo(): Int")
println(it.settingsFile.text)
it.generateSrcFile("actual.kt", "actual fun foo() = 42")
it.buildFile.append("""
konanArtifacts {
library('foo') {
enableMultiplatform true
}
}
dependencies {
expectedBy project(':common')
}
""".stripIndent())
it.buildingTasks.addAll([":compileKonanFoo", ":compileKonanFoo${KonanProject.HOST}}"])
}
def results = buildTwice(project, ':build') { KonanProject ->
commonSource.append("\nfun bar() = 43")
}
then:
onlyRecompilationHappened(*results)
}
// TODO: Add incremental tests for the 'libraries' block.
//endregion
}
@@ -1,377 +0,0 @@
/*
* 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.gradle.testkit.runner.GradleRunner
import org.gradle.util.GradleVersion
import org.jetbrains.kotlin.konan.target.HostManager
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths
enum ArtifactType {
PROGRAM("program"),
LIBRARY("library"),
BITCODE("bitcode"),
INTEROP("interop"),
DYNAMIC("dynamic"),
STATIC("static"),
FRAMEWORK("framework")
String type
ArtifactType(String type) { this.type = type }
String toString() { return type }
}
class KonanProject {
static String DEFAULT_ARTIFACT_NAME = 'main'
static String DEFAULT_INTEROP_NAME = "stdio"
static String HOST = HostManager.hostName
File projectDir
Path projectPath
File konanBuildDir
String konanHome
String gradleVersion
File buildFile
File propertiesFile
File settingsFile
Set<File> srcFiles = []
Set<File> defFiles = []
List<String> interopTasks = []
List<String> compilationTasks = []
String downloadTask = ":checkKonanCompiler"
List<String> targets
List<String> getBuildingTasks() { return compilationTasks + interopTasks }
List<String> getKonanTasks() { return getBuildingTasks() + downloadTask }
static String DEFAULT_SRC_CONTENT = """
fun main(args: Array<String>) {
println(42)
}
"""
static String DEFAULT_DEF_CONTENT = """
headers = stdio.h
""".stripIndent()
protected KonanProject(File projectDir){
this(projectDir, [HOST])
}
protected KonanProject(File projectDir, List<String> targets) {
this.projectDir = projectDir
this.targets = targets
projectPath = projectDir.toPath()
konanBuildDir = projectPath.resolve('build/konan').toFile()
def konanHomeDir = new File(getKonanHome())
if (!konanHomeDir.exists() || !konanHomeDir.directory) {
throw new IllegalStateException("konan.home doesn't exist or is not a directory: $konanHomeDir.canonicalPath")
}
// Escape windows path separator
this.konanHome = escapeBackSlashes(konanHomeDir.canonicalPath)
this.gradleVersion = System.getProperty("gradleVersion") ?: GradleVersion.current().version
}
GradleRunner createRunner(boolean withDebug = true) {
return GradleRunner.create()
.withProjectDir(projectDir)
.withPluginClasspath()
.withDebug(withDebug)
.withGradleVersion(gradleVersion)
}
/** Creates a subdirectory specified by the given path. */
File createSubDir(String ... path) {
return createSubDir(Paths.get(*path))
}
/** Creates a subdirectory specified by the given path. */
File createSubDir(Path path) {
return Files.createDirectories(projectPath.resolve(path)).toFile()
}
/** Creates a file with the given content in project subdirectory specified by parentDirectory. */
File createFile(Path parentDirectory = projectPath, String fileName, String content) {
def parent = projectPath.resolve(parentDirectory)
Files.createDirectories(parent)
def result = parent.resolve(fileName).toFile()
result.createNewFile()
result.write(content)
return result
}
/** Creates a file with the given content in project subdirectory specified by parentPath. */
File createFile(List<String> parentPath, String fileName, String content) {
return createFile(Paths.get(*parentPath), fileName, content)
}
/** Creates a folder for project source files (src/main/kotlin). */
void generateFolders() {
createSubDir("src", "main", "kotlin")
createSubDir("src", "main", "c_interop")
}
/** Generates a build.gradle file in the root project directory with the given content. */
File generateBuildFile(String content) {
buildFile = createFile(projectPath, "build.gradle", content)
return buildFile
}
/** Generates a settings.gradle file in the root project directory with the given content. */
File generateSettingsFile(String content) {
settingsFile = createFile(projectPath, "settings.gradle", content)
return settingsFile
}
/**
* Generates a build.gradle file in root project directory with the default content (see below)
* and fills the compilationTasks array.
*
* plugins { id 'konan' }
*
* konanArtifacts {
* program('$DEFAULT_ARTIFACT_NAME')
* }
*/
File generateBuildFile() {
def result = generateBuildFile("""
|plugins { id 'konan' }
|
|konan.targets = [${targets.collect { "'$it'" }.join(", ")}]
|""".stripMargin()
)
compilationTasks = [":compileKonan", ":build"]
return result
}
/** Generates a source file with the given name and content in the given directory and adds it into srcFiles */
File generateSrcFile(Path parentDirectory, String fileName, String content) {
def result = createFile(parentDirectory, fileName, content)
srcFiles.add(result)
return result
}
/** Generates a source file with the given name and content in the given directory and adds it into srcFiles */
File generateSrcFile(List<String> parentPath, String fileName, String content) {
return generateSrcFile(Paths.get(*parentPath), fileName, content)
}
/** Generates a source file with the given name and content in 'src/main/kotlin' and adds it into srcFiles */
File generateSrcFile(String fileName, String content) {
return generateSrcFile(["src", "main", "kotlin"], fileName, content)
}
/**
* Generates a source file with the given name and default content (see below) in src/main/kotlin
* and adds it into srcFiles.
*
* fun main(args: Array<String>) {
* println(42)
* }
*/
File generateSrcFile(String fileName) {
return generateSrcFile(fileName, DEFAULT_SRC_CONTENT)
}
/** Creates a def-file with the given name and content in src/main/c_interop directory and adds it to defFiles. */
File generateDefFile(String fileName, String content) {
def result = createFile(["src", "main", "c_interop"], fileName, content)
defFiles.add(result)
return result
}
/**
* Creates a def-file with the given name and the default content (see below) in src/main/c_interop directory
* and adds it to defFiles.
*
* headers = stdio.h stdlib.h string.h
*/
File generateDefFile(String fileName = "${DEFAULT_INTEROP_NAME}.def") {
return generateDefFile(fileName, DEFAULT_DEF_CONTENT)
}
/** Generates gradle.properties file with the konan.home and konan.jvmArgs properties set. */
File generatePropertiesFile(String konanHome, String konanJvmArgs = System.getProperty("konan.jvmArgs") ?: "") {
propertiesFile = createFile(projectPath, "gradle.properties", """\
org.jetbrains.kotlin.native.home=$konanHome
${!konanJvmArgs.isEmpty() ? "konan.jvmArgs=$konanJvmArgs\n" : ""}
""".stripIndent())
return propertiesFile
}
/**
* Sets the given setting of the given project extension.
* In other words adds the following string in the build file:
*
* $container.$section.$parameter $value
*/
protected void addSetting(String container, String section, String parameter, String value) {
buildFile.append("$container.$section.$parameter $value\n")
}
/**
* Sets the given setting of the given project extension using the path of the file as a value.
* In other words adds the following string in the build file:
*
* $container.$section.$parameter ${value.canonicalPath.replace(\, \\)}
*/
protected void addSetting(String container, String section, String parameter, File value) {
addSetting(container, section, parameter, "'${escapeBackSlashes(value.canonicalPath)}'")
}
/** Sets the given setting of the given konanArtifact */
void addSetting(String artifactName = DEFAULT_ARTIFACT_NAME, String parameter, String value) {
addSetting("konanArtifacts", artifactName, parameter, value)
}
/** Sets the given setting of the given konanArtifact using the path of the file as a value. */
void addSetting(String artifactName = DEFAULT_ARTIFACT_NAME, String parameter, File value) {
addSetting("konanArtifacts", artifactName, parameter, value)
}
void addLibraryToArtifact(String artifactName = DEFAULT_ARTIFACT_NAME, String library = DEFAULT_INTEROP_NAME) {
addLibraryToArtifactCustom(artifactName, "artifact '$library'")
}
void addLibraryToArtifactCustom(String artifactName = DEFAULT_ARTIFACT_NAME, String closureContent) {
buildFile.append("konanArtifacts.${artifactName}.libraries { $closureContent }\n")
}
/** Returns the path of compileKonan... task for the default artifact. */
static String defaultCompilationTask(String target = HOST) {
return compilationTask(DEFAULT_ARTIFACT_NAME, target)
}
static String defaultInteropTask(String target = HOST) {
return compilationTask(DEFAULT_INTEROP_NAME, target)
}
/** Returns the path of compileKonan... task for the artifact specified. */
static String compilationTask(String artifactName, String target = HOST) {
return ":compileKonan${artifactName.capitalize()}${target.capitalize()}"
}
static String defaultCompilationConfig() {
return artifactConfig(DEFAULT_ARTIFACT_NAME)
}
static String defaultInteropConfig() {
return artifactConfig(DEFAULT_INTEROP_NAME)
}
static String artifactConfig(String artifactName) {
return "konanArtifacts.$artifactName"
}
static String outputAccessCode(String artifact, String target = HOST) {
return "${artifactConfig(artifact)}.${target}.artifact"
}
void addCompilerArtifact(String name, String content = "", ArtifactType type = ArtifactType.PROGRAM) {
def newTasks = targets.collect { compilationTask(name, it) } + ":compileKonan${name.capitalize()}".toString()
buildFile.append("konanArtifacts { $type('$name') }\n")
if (type == ArtifactType.INTEROP) {
defFiles += generateDefFile("${name}.def", content)
interopTasks += newTasks
} else {
def src = generateSrcFile(projectPath.resolve("src/$name/kotlin"), "source.kt", content)
addSetting(name, "srcFiles", src)
srcFiles += src
compilationTasks += newTasks
}
}
/** Creates a project with default build and source files. */
static KonanProject create(File projectDir,
ArtifactType artifactType = ArtifactType.PROGRAM,
List<String> targets = [HOST]) {
return createEmpty(projectDir, targets) { KonanProject p ->
p.addCompilerArtifact(DEFAULT_ARTIFACT_NAME, DEFAULT_SRC_CONTENT, artifactType)
}
}
/** Creates a project with default build and source files. */
static KonanProject create(File projectDir,
ArtifactType artifactType = ArtifactType.PROGRAM,
List<String> targets = [HOST],
Closure config) {
def result = create(projectDir, artifactType, targets)
config(result)
return result
}
static KonanProject createWithInterop(File projectDir,
ArtifactType mainArtifactType = ArtifactType.PROGRAM,
List<String> targets = [HOST]) {
return create(projectDir, mainArtifactType, targets) { KonanProject p ->
p.addCompilerArtifact(DEFAULT_INTEROP_NAME, DEFAULT_DEF_CONTENT, ArtifactType.INTEROP)
p.addLibraryToArtifact()
}
}
static KonanProject createWithInterop(File projectDir,
ArtifactType mainArtifactType = ArtifactType.PROGRAM,
List<String> targets = [HOST],
Closure config) {
def result = createWithInterop(projectDir, mainArtifactType, targets)
config(result)
return result
}
/** Creates a project with the default build file and without any source files. */
static KonanProject createEmpty(File projectDir, List<String> targets = [HOST]) {
def result = new KonanProject(projectDir, targets)
result.with {
generateFolders()
generateBuildFile()
generatePropertiesFile(konanHome)
generateSettingsFile("")
}
return result
}
/** Creates a project with the default build file and without any source files. */
static KonanProject createEmpty(File projectDir, List<String> targets = [HOST], Closure config) {
def result = createEmpty(projectDir, targets)
config(result)
return result
}
static String escapeBackSlashes(String value) {
return value.replace('\\', '\\\\')
}
static String getKonanHome() {
def konanHome = System.getProperty("konan.home") ?: System.getProperty("org.jetbrains.kotlin.native.home")
if (konanHome == null) {
throw new IllegalStateException("konan.home isn't specified")
}
return konanHome
}
}
@@ -1,330 +0,0 @@
/*
* 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
class LibrarySpecification extends BaseKonanSpecification {
def libraries = [
[manualDependsOn: true , code: { l1, l2 ->
"file ${KonanProject.outputAccessCode(l1)}\nfile ${KonanProject.outputAccessCode(l2)}"
}],
[manualDependsOn: true , code: { l1, l2 ->
"files ${KonanProject.outputAccessCode(l1)}, ${KonanProject.outputAccessCode(l2)}"
}],
[manualDependsOn: true , code: { l1, l2 ->
"files project.files(${KonanProject.outputAccessCode(l1)}, ${KonanProject.outputAccessCode(l2)})"
}],
[manualDependsOn: true , code: { l1, l2 -> "klib '$l1'\nklib '$l2'" }],
[manualDependsOn: true , code: { l1, l2 -> "klibs '$l1', '$l2'" }],
[manualDependsOn: false, code: { l1, l2 -> "artifact '$l1'\nartifact '$l2'" }],
[manualDependsOn: false, code: { l1, l2 -> "artifact konanArtifacts.$l1\nartifact konanArtifacts.$l2" }],
]
String createMainWithCalls(List<Tuple2<String, String>> functions, Closure<String> callBuilder) {
def result = new StringBuilder("""
|fun main(args: Array<String>) {\n
""".stripMargin())
functions.forEach {
result.append(callBuilder(it.first))
result.append(callBuilder(it.second))
}
result.append("}")
return result.toString()
}
void createLibraryWithFunction(KonanProject project, String name) {
project.addCompilerArtifact(name, """
|package $name
|
|fun $name() {
| println("$name")
|}
""".stripMargin(), ArtifactType.LIBRARY)
project.addSetting(name, "noDefaultLibs", "true")
project.addSetting(name, "noEndorsedLibs", "true")
}
void createInteropLibrary(KonanProject project, String name) {
project.addCompilerArtifact(name, "headers = math.h", ArtifactType.INTEROP)
project.addSetting(name, "noDefaultLibs", "true")
project.addSetting(name, "noEndorsedLibs", "true")
}
KonanProject createProjectWithLibraries(Closure createLibraryFunction, Closure callBuilder) {
def result = KonanProject.createEmpty(projectDirectory)
def libraryNames = new ArrayList<Tuple2<String, String>>()
for (int i = 0; i < libraries.size(); i++) {
libraryNames.add(new Tuple2("foo$i", "bar$i"))
}
libraryNames.forEach {
createLibraryFunction(result, it.first)
createLibraryFunction(result, it.second)
}
result.addCompilerArtifact("main", createMainWithCalls(libraryNames, callBuilder))
result.addSetting("main", "noDefaultLibs", "true")
result.addSetting("main", "noEndorsedLibs", "true")
for (int i = 0; i < libraries.size(); i++) {
def foo = libraryNames[i].first
def bar = libraryNames[i].second
result.addLibraryToArtifactCustom("main", libraries[i].code(foo, bar) )
if (libraries[i].manualDependsOn) {
result.addSetting("main", "dependsOn", "konanArtifacts.$foo")
result.addSetting("main", "dependsOn", "konanArtifacts.$bar")
}
}
return result
}
KonanProject createProjectWithSimpleLibraries() {
return createProjectWithLibraries(
{ p, n -> createLibraryWithFunction(p, n) },
{ "$it.$it()\n" } )
}
KonanProject createProjectWithInteropLibraries() {
return createProjectWithLibraries(
{ p, n -> createInteropLibrary(p, n) },
{ "println(${it}.cos(0.0))\n" }
)
}
def 'Plugin should support libraries from the same project'() {
expect:
createProjectWithSimpleLibraries()
.createRunner()
.withArguments(KonanProject.compilationTask("main"))
.build()
}
def 'Plugin should support interop libraries from the same project'() {
expect:
createProjectWithInteropLibraries()
.createRunner()
.withArguments(KonanProject.compilationTask("main"))
.build()
}
def 'Plugin should support allLibrariesFrom method for the current project'() {
expect:
def project = KonanProject.createEmpty(projectDirectory) { KonanProject it ->
it.addCompilerArtifact("foo", "fun foo() { println(42) }", ArtifactType.LIBRARY)
it.addSetting("foo", "noDefaultLibs", "true")
it.addSetting("foo", "noEndorsedLibs", "true")
it.addCompilerArtifact("bar", "fun bar() { println(43) }", ArtifactType.LIBRARY)
it.addSetting("bar", "noDefaultLibs", "true")
it.addSetting("bar", "noEndorsedLibs", "true")
it.addCompilerArtifact("main" ,"fun main(args: Array<String>) { foo(); bar() }")
it.addSetting("main", "noDefaultLibs", "true" )
it.addSetting("main", "noEndorsedLibs", "true" )
it.addLibraryToArtifactCustom("main", "allLibrariesFrom project")
}
project.createRunner()
.withArguments(KonanProject.compilationTask("main"))
.build()
}
def 'Plugin should support allLibrariesFrom method for another project'() {
expect:
def project = KonanProject.createEmpty(projectDirectory)
def subproject = KonanProject.createEmpty(project.createSubDir("subproject")) { KonanProject it ->
it.buildFile.write("apply plugin: 'konan'\n")
}
project.settingsFile.append("include ':subproject'")
project.addCompilerArtifact("wrongFoo","fun foo() { println(24) }", ArtifactType.LIBRARY)
project.addSetting("wrongFoo", "noDefaultLibs", "true")
project.addSetting("wrongFoo", "noEndorsedLibs", "true")
subproject.addCompilerArtifact("foo", "fun foo() { println(42) }", ArtifactType.LIBRARY)
subproject.addSetting("foo", "noDefaultLibs", "true")
subproject.addSetting("foo", "noEndorsedLibs", "true")
subproject.addCompilerArtifact("bar", "fun bar() { println(43) }", ArtifactType.LIBRARY)
subproject.addSetting("bar", "noDefaultLibs", "true")
subproject.addSetting("bar", "noEndorsedLibs", "true")
project.addCompilerArtifact("main" ,"fun main(args: Array<String>) { foo(); bar() }")
project.addSetting("main", "noDefaultLibs", "true" )
project.addSetting("main", "noEndorsedLibs", "true" )
project.addLibraryToArtifactCustom("main", "allLibrariesFrom project('subproject')")
project.createRunner()
.withArguments(KonanProject.compilationTask("main"))
.build()
}
def 'Plugin should support allInteropLibrariesFrom method for the current project'() {
expect:
def project = KonanProject.createEmpty(projectDirectory)
def subproject = KonanProject.createEmpty(project.createSubDir("subproject")) { KonanProject it ->
it.buildFile.write("apply plugin: 'konan'\n")
}
project.settingsFile.append("include ':subproject'")
project.addCompilerArtifact("wrongFoo1", "fun foo() { println(42) }", ArtifactType.LIBRARY)
project.addSetting("wrongFoo1", "noDefaultLibs", "true")
project.addSetting("wrongFoo1", "noEndorsedLibs", "true")
subproject.addCompilerArtifact("wrongFoo2", "fun foo() { println(42) }", ArtifactType.LIBRARY)
subproject.addSetting("wrongFoo2", "noDefaultLibs", "true")
subproject.addSetting("wrongFoo2", "noEndorsedLibs", "true")
subproject.addCompilerArtifact("math1", "headers = math.h", ArtifactType.INTEROP)
subproject.addSetting("math1", "noDefaultLibs", "true")
subproject.addSetting("math1", "noEndorsedLibs", "true")
subproject.addCompilerArtifact("math2", "headers = math.h", ArtifactType.INTEROP)
subproject.addSetting("math2", "noDefaultLibs", "true")
subproject.addSetting("math2", "noEndorsedLibs", "true")
project.addCompilerArtifact("main" ,"""
|fun foo() {}
|
|fun main(args: Array<String>) { foo(); math1.cos(0.0); math2.cos(0.0) }
""".stripMargin())
project.addSetting("main", "noDefaultLibs", "true" )
project.addSetting("main", "noEndorsedLibs", "true" )
project.addLibraryToArtifactCustom("main", "allInteropLibrariesFrom project('subproject')")
project.createRunner()
.withArguments(KonanProject.compilationTask("main"))
.build()
}
def 'Plugin should support allInteropLibrariesFrom method for another projecct'() {
expect:
def project = KonanProject.createEmpty(projectDirectory) { KonanProject it ->
it.addCompilerArtifact("wrongFoo", "fun foo() { println(42) }", ArtifactType.LIBRARY)
it.addSetting("wrongFoo", "noDefaultLibs", "true")
it.addSetting("wrongFoo", "noEndorsedLibs", "true")
it.addCompilerArtifact("math1", "headers = math.h", ArtifactType.INTEROP)
it.addSetting("math1", "noDefaultLibs", "true")
it.addSetting("math1", "noEndorsedLibs", "true")
it.addCompilerArtifact("math2", "headers = math.h", ArtifactType.INTEROP)
it.addSetting("math2", "noDefaultLibs", "true")
it.addSetting("math2", "noEndorsedLibs", "true")
it.addCompilerArtifact("main" ,"""
|fun foo() {}
|
|fun main(args: Array<String>) { foo(); math1.cos(0.0); math2.cos(0.0) }
""".stripMargin())
it.addSetting("main", "noDefaultLibs", "true" )
it.addSetting("main", "noEndorsedLibs", "true" )
it.addLibraryToArtifactCustom("main", "allInteropLibrariesFrom project")
}
project.createRunner()
.withArguments(KonanProject.compilationTask("main"))
.build()
}
def 'Plugin should support custom repositories for libraries'() {
expect:
def project = KonanProject.createEmpty(projectDirectory) { KonanProject it ->
it.addCompilerArtifact("foo", "fun foo() { println(42) }", ArtifactType.LIBRARY)
it.addSetting("foo", "noDefaultLibs", "true")
it.addSetting("foo", "noEndorsedLibs", "true")
it.addSetting("foo", "baseDir", "file('out')")
it.addCompilerArtifact("main" ,"fun main(args: Array<String>) { foo() }")
it.addSetting("main", "noDefaultLibs", "true" )
it.addSetting("main", "noEndorsedLibs", "true" )
it.addSetting("main", "dependsOn", "konanArtifacts.foo.$KonanProject.HOST")
it.addLibraryToArtifactCustom("main", "klib 'foo'")
it.addLibraryToArtifactCustom("main", "useRepo 'out/$KonanProject.HOST'")
}
project.createRunner()
.withArguments(KonanProject.compilationTask("main"), "-i")
.build()
}
def 'Plugin should support library dependencies in the same project'() {
expect:
def project = KonanProject.createEmpty(projectDirectory) { KonanProject it ->
it.addCompilerArtifact("foo", "fun foo() { println(42) }", ArtifactType.LIBRARY)
it.addSetting("foo", "noDefaultLibs", "true")
it.addSetting("foo", "noEndorsedLibs", "true")
it.addCompilerArtifact("bar", "fun bar() { println(43) }", ArtifactType.LIBRARY)
it.addSetting("bar", "noDefaultLibs", "true")
it.addSetting("bar", "noEndorsedLibs", "true")
it.addLibraryToArtifact("bar", "foo")
it.addCompilerArtifact("main" ,"fun main(args: Array<String>) { foo(); bar() }")
it.addSetting("main", "noDefaultLibs", "true" )
it.addSetting("main", "noEndorsedLibs", "true" )
it.addLibraryToArtifact("main", "bar")
}
project.createRunner()
.withArguments(KonanProject.compilationTask("main"), "-i")
.build()
}
def 'Plugin should support library dependencies from other projects'() {
expect:
def project = KonanProject.createEmpty(projectDirectory)
def subproject1 = KonanProject.createEmpty(project.createSubDir("subproject1")) { KonanProject it ->
it.buildFile.write("apply plugin: 'konan'\n")
}
def subproject2 = KonanProject.createEmpty(project.createSubDir("subproject2")) { KonanProject it ->
it.buildFile.write("apply plugin: 'konan'\n")
}
project.settingsFile.append("include ':subproject1'\ninclude ':subproject2'")
subproject1.addCompilerArtifact("foo", "fun foo() { println(42) }", ArtifactType.LIBRARY)
subproject1.addSetting("foo", "noDefaultLibs", "true")
subproject1.addSetting("foo", "noEndorsedLibs", "true")
subproject2.addCompilerArtifact("bar", "fun bar() { println(43) }", ArtifactType.LIBRARY)
subproject2.addSetting("bar", "noDefaultLibs", "true")
subproject2.addSetting("bar", "noEndorsedLibs", "true")
subproject2.addLibraryToArtifactCustom(
"bar", "artifact rootProject.project('subproject1'), 'foo'"
)
project.addCompilerArtifact("main" ,"fun main(args: Array<String>) { foo(); bar() }")
project.addSetting("main", "noDefaultLibs", "true" )
project.addSetting("main", "noEndorsedLibs", "true" )
project.addLibraryToArtifactCustom(
"main", "artifact project('subproject2'), 'bar'"
)
project.createRunner()
.withArguments(KonanProject.compilationTask("main"))
.build()
}
// TODO: Add tests for incorrect cases (e.g. attempt to use an executable as a library)
}
@@ -1,390 +0,0 @@
/*
* 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 spock.lang.Ignore
import java.nio.file.Files
import java.nio.file.Paths
class MultiplatformSpecification extends BaseKonanSpecification {
public static final String KOTLIN_VERSION = System.getProperty("kotlin.version")
public static final String KOTLIN_REPO = System.getProperty("kotlin.repo")
public static final String DEFAULT_COMMON_BUILD_FILE_CONTENT = """\
buildscript {
repositories {
maven {
url = '$KOTLIN_REPO'
}
maven {
url = 'https://cache-redirector.jetbrains.com/maven-central'
}
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$KOTLIN_VERSION"
}
}
apply plugin: 'kotlin-platform-common'
repositories {
maven {
url = '$KOTLIN_REPO'
}
maven {
url = 'https://cache-redirector.jetbrains.com/maven-central'
}
}
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib-common:$KOTLIN_VERSION"
}
""".stripIndent()
static File createCommonProject(KonanProject platformProject,
String commonProjectName = "common",
String commonBuildFileContent = DEFAULT_COMMON_BUILD_FILE_CONTENT) {
def commonDirectory = platformProject.createSubDir(commonProjectName)
def commonBuildFile = Paths.get(commonDirectory.absolutePath, "build.gradle")
commonBuildFile.write(commonBuildFileContent)
platformProject.settingsFile.append("include ':$commonProjectName'\n")
return commonDirectory
}
static File createCommonSource(File commonDirectory,
Iterable<String> subdirectory,
String fileName,
String content) {
def commonSrcDir = commonDirectory.toPath().resolve(Paths.get(*subdirectory))
def commonSource = commonSrcDir.resolve(fileName)
Files.createDirectories(commonSrcDir)
commonSource.write(content)
return commonSource.toFile()
}
def 'Plugin should support multiplatform projects'() {
expect:
def project = KonanProject.createEmpty(projectDirectory) { KonanProject it ->
def commonDirectory = createCommonProject(it)
createCommonSource(commonDirectory,
["src", "main", "kotlin"],
"common.kt",
"""\
@file:Suppress("OPT_IN_USAGE_ERROR")
@OptionalExpectation
expect annotation class Optional()
@Optional
fun opt() = 42
expect fun foo(): Int
""".stripIndent()
)
it.generateSrcFile("platform.kt", "actual fun foo() = 42")
it.buildFile.append("""
konanArtifacts {
library('foo') {
enableMultiplatform true
}
}
dependencies {
expectedBy project(':common')
}
""".stripIndent())
}
project.createRunner().withArguments(":build").build()
}
def 'Multiplatform projects should be disabled by default'() {
expect:
def project = KonanProject.createEmpty(projectDirectory) { KonanProject it ->
def commonDirectory = createCommonProject(it)
createCommonSource(commonDirectory,
["src", "main", "kotlin"],
"common.kt",
"expect fun foo(): Int")
it.generateSrcFile("platform.kt", "fun foo() = 42")
it.buildFile.append("""
konanArtifacts {
library('foo') {}
}
dependencies {
expectedBy project(':common')
}
""".stripIndent())
}
project.createRunner().withArguments(":build").build()
}
def 'Plugin should use the \'main\' source set as a default common source set'() {
expect:
def project = KonanProject.createEmpty(projectDirectory) { KonanProject it ->
def commonDirectory = createCommonProject(it)
Paths.get(commonDirectory.absolutePath, "build.gradle").append("""
sourceSets {
common.kotlin.srcDir 'src/common/kotlin'
}
""".stripIndent())
createCommonSource(commonDirectory,
["src", "main", "kotlin"],
"common.kt",
"expect fun foo(): Int")
createCommonSource(commonDirectory,
["src", "common", "kotlin"],
"common.kt",
"expect fun bar(): Int")
it.generateSrcFile("platform.kt", "actual fun foo() = 42")
it.buildFile.append("""
konanArtifacts {
library('foo') {
enableMultiplatform true
}
}
dependencies {
expectedBy project(':common')
}
""".stripIndent())
}
project.createRunner().withArguments(":build").build()
}
def 'Plugin should allow a user to specify custom common source set'() {
expect:
def project = KonanProject.createEmpty(projectDirectory) { KonanProject it ->
def commonDirectory = createCommonProject(it)
Paths.get(commonDirectory.absolutePath, "build.gradle").append("""
sourceSets {
common.kotlin.srcDir 'src/common/kotlin'
}
""".stripIndent())
createCommonSource(commonDirectory,
["src", "common", "kotlin"],
"common.kt",
"expect fun bar(): Int")
it.generateSrcFile("platform.kt", "actual fun bar() = 42")
it.buildFile.append("""
konanArtifacts {
library('foo') {
enableMultiplatform true
commonSourceSets 'common'
}
}
dependencies {
expectedBy project(':common')
}
""".stripIndent())
}
project.createRunner().withArguments(":build").build()
}
def 'Plugin should allow setting several common source sets'() {
expect:
def project = KonanProject.createEmpty(projectDirectory) { KonanProject it ->
def commonDirectory = createCommonProject(it)
Paths.get(commonDirectory.absolutePath, "build.gradle").append("""
sourceSets {
common.kotlin.srcDir 'src/common/kotlin'
}
""".stripIndent())
createCommonSource(commonDirectory,
["src", "common", "kotlin"],
"common.kt",
"expect fun bar(): Int")
createCommonSource(commonDirectory,
["src", "main", "kotlin"],
"main.kt",
"expect fun foo() : Int")
it.generateSrcFile("platform.kt", "actual fun bar() = 42\nactual fun foo() = 43")
it.buildFile.append("""
konanArtifacts {
library('foo') {
enableMultiplatform true
commonSourceSets 'common', 'main'
}
}
dependencies {
expectedBy project(':common')
}
""".stripIndent())
}
project.createRunner().withArguments(":build").build()
}
def 'Build should fail if the expectedBy dependency is not a project one'() {
when:
def project = KonanProject.createEmpty(projectDirectory) { KonanProject it ->
def commonDirectory = createCommonProject(it)
createCommonSource(commonDirectory,
["src", "main", "kotlin"],
"common.kt",
"expect fun foo(): Int")
it.generateSrcFile("platform.kt", "actual fun foo() = 42")
it.buildFile.append("""
konanArtifacts {
library('foo') {
enableMultiplatform true
}
}
dependencies {
expectedBy files('common/src/main/kotlin/common.kt')
}
""".stripIndent())
}
def result = project.createRunner().withArguments(":build").buildAndFail()
then:
result.output.contains("dependency is not a project: ")
}
def 'Build should support several expectedBy-dependencies'() {
expect:
def project = KonanProject.createEmpty(projectDirectory) { KonanProject it ->
def commonDirectory = createCommonProject(it, "commonFoo")
createCommonSource(commonDirectory,
["src", "main", "kotlin"],
"common.kt",
"expect fun foo(): Int")
commonDirectory = createCommonProject(it, "commonBar")
createCommonSource(commonDirectory,
["src", "main", "kotlin"],
"common.kt",
"expect fun bar(): Int")
it.generateSrcFile("platform.kt", "actual fun foo() = 0\nactual fun bar() = 0")
it.buildFile.append("""
konanArtifacts {
library('foo') {
enableMultiplatform true
}
}
dependencies {
expectedBy project(':commonFoo')
expectedBy project(':commonBar')
}
""".stripIndent())
}
project.createRunner().withArguments(":build").build()
}
def 'Build should fail if the common project has no common plugin'() {
when:
def project = KonanProject.createEmpty(projectDirectory) { KonanProject it ->
def commonDirectory = createCommonProject(it,"common", "")
createCommonSource(commonDirectory,
["src", "main", "kotlin"],
"common.kt",
"expect fun foo(): Int")
it.generateSrcFile("platform.kt", "actual fun bar() = 42")
it.buildFile.append("""
konanArtifacts {
library('foo') {
enableMultiplatform true
}
}
dependencies {
expectedBy project(':common')
}
""".stripIndent())
}
def result = project.createRunner().withArguments(":build").buildAndFail()
then:
result.output.contains("has an 'expectedBy' dependency to non-common project")
}
@Ignore("TODO in the Big Kotlin plugin")
def 'Build should fail if custom common source set doesn\'t exist'() {
when:
def project = KonanProject.createEmpty(projectDirectory) { KonanProject it ->
def commonDirectory = createCommonProject(it)
createCommonSource(commonDirectory,
["src", "main", "kotlin"],
"common.kt",
"expect fun foo(): Int")
it.generateSrcFile("platform.kt", "actual fun bar() = 42")
it.buildFile.append("""
konanArtifacts {
library('foo') {
enableMultiplatform true
commonSourceSets 'common'
}
}
dependencies {
expectedBy project(':common')
}
""".stripIndent())
}
def result = project.createRunner().withArguments(":build").buildAndFail()
then:
result.output.contains("Cannot find a source set with name 'common' in a common project")
}
def 'Setting custom source set should enable the multiplatform support'() {
expect:
def project = KonanProject.createEmpty(projectDirectory) { KonanProject it ->
def commonDirectory = createCommonProject(it)
Paths.get(commonDirectory.absolutePath, "build.gradle").append("""
sourceSets {
common.kotlin.srcDir 'src/common/kotlin'
}
""".stripIndent())
createCommonSource(commonDirectory,
["src", "common", "kotlin"],
"common.kt",
"expect fun bar(): Int")
it.generateSrcFile("platform.kt", "actual fun bar() = 42")
it.buildFile.append("""
konanArtifacts {
library('foo') {
commonSourceSets 'common'
}
}
dependencies {
expectedBy project(':common')
}
""".stripIndent())
}
project.createRunner().withArguments(":build").build()
}
}
@@ -1,130 +0,0 @@
/*
* 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.gradle.testkit.runner.TaskOutcome
import org.jetbrains.kotlin.konan.target.Distribution
import org.jetbrains.kotlin.konan.target.PlatformManager
class PathSpecification extends BaseKonanSpecification {
boolean fileExists(KonanProject project, String path) {
project.konanBuildDir.toPath().resolve(path).toFile().exists()
}
def platformManager = new PlatformManager(new Distribution(KonanProject.konanHome, false, null), false)
def 'Plugin should provide a correct path to the artifacts created'() {
expect:
def project = KonanProject.createEmpty(
projectDirectory,
platformManager.enabled.collect { t -> t.visibleName }
) { KonanProject it ->
it.generateSrcFile("main.kt")
it.generateDefFile("interop.def", "")
it.buildFile.append("""
konanArtifacts {
program('program')
library('library')
bitcode('bitcode')
interop('interop')
framework('framework')
dynamic('dynamic')
}
tasks.register("checkArtifacts", DefaultTask) {
dependsOn(':build')
doLast {
for(artifact in konanArtifacts) {
for (target in artifact) {
if (!target.artifact.exists()) throw new Exception("Artifact doesn't exist. Type: \${artifact.name}, target: \${target.target}")
}
}
for (target in konanArtifacts.dynamic) {
if (!target.headerFile.exists()) throw new Exception("Header file doesn't exist. Target: \${target.target}")
}
}
}
""".stripIndent())
}
project.createRunner().withArguments("checkArtifacts").build()
}
def 'Plugin should create all necessary directories'() {
when:
def project = KonanProject.createWithInterop(projectDirectory)
project.addCompilerArtifact("lib", "fun foo() {}", ArtifactType.LIBRARY)
project.addCompilerArtifact("bit", "fun bar() {}", ArtifactType.BITCODE)
project.createRunner().withArguments('build').build()
then:
project.konanBuildDir.toPath().resolve("bin/$KonanProject.HOST").toFile().listFiles().findAll {
File it -> it.file && it.name.matches("^${KonanProject.DEFAULT_ARTIFACT_NAME}\\.[^.]+")
}.size() > 0
fileExists(project, "libs/$KonanProject.HOST/${KonanProject.DEFAULT_INTEROP_NAME}.klib")
fileExists(project, "libs/$KonanProject.HOST/lib.klib")
fileExists(project, "bitcode/$KonanProject.HOST/bit.bc")
}
def 'Plugin should stop building if the compiler classpath is empty'() {
when:
def project = KonanProject.create(projectDirectory)
project.propertiesFile.write("konan.home=fakepath")
def result = project.createRunner().withArguments('build').buildAndFail()
def task = result.task(project.defaultCompilationTask())
then:
task == null || task.outcome == TaskOutcome.FAILED
}
def 'Plugin should stop building if the stub generator classpath is empty'() {
when:
def project = KonanProject.createWithInterop(projectDirectory)
project.propertiesFile.write("konan.home=fakepath")
def result = project.createRunner().withArguments('build').buildAndFail()
def task = result.task(project.compilationTask(KonanProject.DEFAULT_INTEROP_NAME))
then:
task == null || task.outcome == TaskOutcome.FAILED
}
def 'Plugin should remove custom output directories'() {
when:
def customOutputDir = projectDirectory.toPath().resolve("foo").toFile()
def project = KonanProject.create(projectDirectory, ArtifactType.LIBRARY) { KonanProject it ->
it.addSetting("baseDir", customOutputDir)
}
def res1 = project.createRunner().withArguments("build").build()
def artifactExistsAfterBuild = customOutputDir.toPath()
.resolve("${KonanProject.HOST}/${KonanProject.DEFAULT_ARTIFACT_NAME}.klib").toFile()
.exists()
def res2 = project.createRunner().withArguments("clean").build()
def artifactExistsAfterClean = customOutputDir.toPath()
.resolve("${KonanProject.HOST}/${KonanProject.DEFAULT_ARTIFACT_NAME}.klib").toFile()
.exists()
then:
res1.taskPaths(TaskOutcome.SUCCESS).containsAll(project.buildingTasks)
res2.taskPaths(TaskOutcome.SUCCESS).contains(":clean")
artifactExistsAfterBuild
!artifactExistsAfterClean
}
}
@@ -1,73 +0,0 @@
/*
* 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.gradle.testkit.runner.TaskOutcome
class RegressionSpecification extends BaseKonanSpecification {
def 'KT-19916'() {
when:
def project = KonanProject.createEmpty(getProjectDirectory()) { KonanProject prj ->
prj.generateSettingsFile("include ':subproject'")
def subprojectDir = prj.projectPath.resolve("subproject").toFile()
subprojectDir.mkdirs()
subprojectDir.toPath().resolve("build.gradle").write("""
dependencies {
libs gradleApi()
}
""".stripIndent())
prj.buildFile.append("""
subprojects {
apply plugin: 'konan'
apply plugin: Foo
}
class Foo implements Plugin<Project> {
void apply(Project project) {
project.configurations.maybeCreate("libs")
}
}
""".stripIndent())
}
def result = project.createRunner().withArguments('tasks').build()
then:
result.task(':tasks').outcome == TaskOutcome.SUCCESS
}
// Ensure gradle plugin fails in case of linker errors.
def 'KT-20192'() {
when:
def project = KonanProject.createEmpty(getProjectDirectory()) { KonanProject prj ->
prj.addCompilerArtifact(KonanProject.DEFAULT_ARTIFACT_NAME,"""
external fun foo()
fun main(args: Array<String>) {
foo()
}
""", ArtifactType.PROGRAM)
}
def result = project.createRunner().withArguments('build').buildAndFail()
then:
result.taskPaths(TaskOutcome.FAILED).contains(project.defaultCompilationTask())
}
}
@@ -1,167 +0,0 @@
/*
* 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.gradle.testkit.runner.BuildResult
import org.gradle.testkit.runner.TaskOutcome
import org.jetbrains.kotlin.konan.target.HostManager
import org.jetbrains.kotlin.konan.target.KonanTarget
import spock.lang.Requires
import spock.lang.Unroll
class TaskSpecification extends BaseKonanSpecification {
def 'Configs should allow user to add dependencies to them'() {
when:
def project = KonanProject.createWithInterop(projectDirectory, ArtifactType.LIBRARY)
project.buildFile.append("""
tasks.register("beforeInterop", DefaultTask) { doLast { println("Before Interop") } }
tasks.register("beforeCompilation", DefaultTask) { doLast { println("Before compilation") } }
""".stripIndent())
project.addSetting(KonanProject.DEFAULT_INTEROP_NAME,"dependsOn", "beforeInterop")
project.addSetting("dependsOn", "beforeCompilation")
def result = project.createRunner().withArguments('build').build()
then:
def beforeInterop = result.task(":beforeInterop")
beforeInterop != null && beforeInterop.outcome == TaskOutcome.SUCCESS
def beforeCompilation = result.task(":beforeCompilation")
beforeCompilation != null && beforeCompilation.outcome == TaskOutcome.SUCCESS
}
def 'Compiler should print time measurements if measureTime flag is set'() {
when:
def project = KonanProject.create(projectDirectory, ArtifactType.LIBRARY)
project.addSetting("measureTime", "true")
def result = project.createRunner().withArguments('build').build()
then:
result.output.findAll(~/Frontend builds AST:\s+\d+\s+msec/).size() == 1
result.output.findAll(~/IR Lowering:\s+\d+\s+msec/).size() == 1
}
@Unroll('Plugin should support #option option for cinterop')
def 'Plugin should support includeDir option for cinterop'() {
expect:
def project = KonanProject.createEmpty(projectDirectory) { KonanProject it ->
it.addCompilerArtifact("interopLib", "headers=foo.h\n$headerFilter", ArtifactType.INTEROP)
it.generateSrcFile(it.projectPath, "foo.h", "#include <bar.h>")
def fooDir = it.projectPath.resolve("foo")
it.generateSrcFile(fooDir, "bar.h", "const int foo = 5;")
it.addSetting("interopLib", option, fooDir.toFile())
it.addSetting("interopLib", option, it.projectDir)
}
project.createRunner().withArguments("build").build()
where:
option | headerFilter
"includeDirs.headerFilterOnly" | "headerFilter=foo.h bar.h"
"includeDirs.allHeaders" | ""
"includeDirs" | ""
}
@Requires({ HostManager.host instanceof KonanTarget.MACOS_X64 })
def 'Plugin should create framework tasks only for Apple targets'() {
when:
def project = KonanProject.createEmpty(projectDirectory) { KonanProject it ->
it.buildFile.append("""
konan.targets = ['wasm32', 'macbook', 'iphone', 'iphone_sim']
konanArtifacts {
framework('foo')
}
""".stripIndent())
}
def result = project.createRunner().withArguments('tasks', '--all').build()
then:
!compilationTaskExists(result,'foo', 'wasm32')
compilationTaskExists (result,'foo', 'macbook')
compilationTaskExists (result,'foo', 'iphone')
compilationTaskExists (result,'foo', 'iphone_sim')
}
def 'Plugin should support different targets for different artifacts'() {
when:
def project = KonanProject.createEmpty(projectDirectory, ['host']) { KonanProject it ->
it.buildFile.append("""
konanArtifacts {
program('defaultTarget')
program('customTarget', targets: ['wasm32'])
program('customTargets', targets: ['host', 'wasm32'])
}
""".stripIndent())
}
def result = project.createRunner().withArguments('tasks', '--all').build()
def hostName = HostManager.hostName
then:
compilationTaskExists (result, 'defaultTarget', hostName)
!compilationTaskExists(result, 'defaultTarget', 'wasm32')
!compilationTaskExists(result, 'customTarget', hostName)
compilationTaskExists (result, 'customTarget', 'wasm32')
compilationTaskExists (result, 'customTargets', hostName)
compilationTaskExists (result, 'customTargets', 'wasm32')
}
def 'Plugin should not create dynamic task for wasm'() {
when:
def project = KonanProject.createEmpty(projectDirectory) { KonanProject it ->
it.buildFile.append("""
konan.targets = ['wasm32']
konanArtifacts {
dynamic('foo')
}
""".stripIndent())
}
def result = project.createRunner().withArguments('tasks', '--all').build()
then:
!compilationTaskExists(result, 'foo', 'wasm32')
}
boolean taskExists(BuildResult result, String taskName) {
def taskNameForSearch = taskName.startsWith(':') ? taskName.substring(1) : taskName
return result.output =~ "\\s$taskNameForSearch\\s"
}
boolean compilationTaskExists(BuildResult result, String artifactName, String targetName) {
return taskExists(result, KonanProject.compilationTask(artifactName, targetName))
}
BuildResult failOnPropertyAccess(KonanProject project, String property) {
project.buildFile.append("""
tasks.register("testTask", DefaultTask) {
doLast {
println(${project.defaultInteropConfig()}.$property)
}
}
""".stripIndent())
return project.createRunner().withArguments("testTask").buildAndFail()
}
BuildResult failOnTaskAccess(KonanProject project, String task) {
project.buildFile.append("""
tasks.register("testTask", DefaultTask) {
dependsOn $task
}
""".stripIndent())
return project.createRunner().withArguments("testTask").buildAndFail()
}
}
@@ -1,48 +0,0 @@
/*
* 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.junit.Rule
import org.junit.rules.TemporaryFolder
import java.io.File
import java.net.URI
import kotlin.test.Test
import kotlin.test.assertTrue
open class CompatibilityTests {
val tmpFolder = TemporaryFolder()
@Rule get
val projectDirectory: File
get() = tmpFolder.root
@Test
fun `Plugin should fail if running with Gradle prior to the required one`() {
val project = KonanProject.createEmpty(projectDirectory)
val result = project
.createRunner()
.withGradleDistribution(URI.create(
"https://cache-redirector.jetbrains.com/services.gradle.org/distributions/gradle-4.5-bin.zip"
))
.withArguments("tasks")
.buildAndFail()
println(result.output)
assertTrue("Build doesn't show the warning message") {
result.output.contains("Kotlin/Native Gradle plugin is incompatible with this version of Gradle.")
}
}
}
@@ -1,204 +0,0 @@
/*
* 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.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.Test
@RunWith(Spockito::class)
open class PropertiesAsEnvVariablesTest {
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
}
ArtifactType.STATIC -> {
prefix = target.family.staticPrefix
suffix = target.family.staticSuffix
}
}
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))
}
@Test
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()
}
}
@@ -1,67 +0,0 @@
/*
* 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.junit.Rule
import org.junit.rules.TemporaryFolder
import java.io.File
import kotlin.test.Test
import kotlin.test.assertFalse
import kotlin.test.assertTrue
class TaskTests {
val tmpFolder = TemporaryFolder()
@Rule get
val projectDirectory: File
get() = tmpFolder.root
@Test
fun `Plugin should support separate run tasks for different binaries`() {
val project = KonanProject.createEmpty(projectDirectory).apply {
buildFile.appendText("""
konanArtifacts {
program('foo') {
srcDir 'src/foo/kotlin'
}
program('bar') {
srcDir 'src/bar/kotlin'
}
}
""".trimIndent())
}
project.generateSrcFile(
listOf("src", "foo", "kotlin"),
"main.kt",
"fun main(args: Array<String>) = println(\"Run Foo: \${args[0]}, \${args[1]}\")")
project.generateSrcFile(
listOf("src", "bar", "kotlin"),
"main.kt",
"fun main(args: Array<String>) = println(\"Run Bar: \${args[0]}, \${args[1]}\")")
val resultFoo = project.createRunner()
.withArguments("runFoo", "-PrunArgs=arg1 arg2")
.build()
val resultAll = project.createRunner()
.withArguments("run", "-PrunArgs=arg1 arg2")
.build()
assertTrue(resultFoo.output.contains("Run Foo: arg1, arg2"), "No Foo output for 'runFoo'")
assertFalse(resultFoo.output.contains("Run Bar: "), "There is Bar output for 'runFoo'")
assertTrue(resultAll.output.contains("Run Foo: arg1, arg2"), "No Foo output for 'run'")
assertTrue(resultAll.output.contains("Run Bar: arg1, arg2"), "No Bar output for 'run'")
}
}