Rework Gradle plugin (#1666)

* [gradle-plugin] Basic implementation for the new plugin

* [gradle-plugin] Use one component for both libraries and executables

* [gradle-plugin] Support library compilation and publishing

* [gradle-plugin] Support compilation for different targets

* [gradle-plugin] Use different output paths for libs and kexe

* [gradle-plugin] Implement ProductionComponent

* [gradle-plugin] Add smoke tests for the new Gradle plugin

* [gradle-plugin] Backport to Gradle 4.7 and rename plugin package

* [gradle-plugin] Implement our own BuildType

* Remove the 'publish-release.sh' script

* [gradle-plugin] Support tests in new plugin

* [gradle-plugin] Refactor the new plugin

    * Rename sourceSets.main.common -> sourceSets.main.kotlin.
    * Generate dummy tasks for unsupported test targets.
    * Add identities in publication for unsupported on the current
      host targets.
    * Provide a component DSL method for setting targets.

* [gradle-plugin] Codestyle fixes

* [gradle-plugin] Add checks for Gradle version and metadata

* [gradle-plugin] Support compiler downloading in the new plugin

* [gradle-plugin] Support MPP in the new Gradle plugin

* [gradle-plugin] Fix tests after removing implicit GRADLE_METADATA
This commit is contained in:
ilmat192
2018-06-19 10:09:46 +03:00
committed by Vasily Levchenko
parent 3b8cb2a542
commit 67279c4148
30 changed files with 1821 additions and 135 deletions
@@ -166,5 +166,21 @@ gradlePlugin {
id = 'konan'
implementationClass = 'org.jetbrains.kotlin.gradle.plugin.KonanPlugin'
}
create("kotlin-native") {
id = 'kotlin-native'
implementationClass = 'org.jetbrains.kotlin.gradle.plugin.experimental.plugins.KotlinNativePlugin'
}
create("kotlin-platform-native") {
id = 'kotlin-platform-native'
implementationClass = 'org.jetbrains.kotlin.gradle.plugin.experimental.plugins.KotlinPlatformNativePlugin'
}
create("org.jetbrains.kotlin.native"){
id = 'org.jetbrains.kotlin.native'
implementationClass = 'org.jetbrains.kotlin.gradle.plugin.experimental.plugins.KotlinNativePlugin'
}
create("org.jetbrains.kotlin.platform.native") {
id = 'org.jetbrains.kotlin.platform.native'
implementationClass = 'org.jetbrains.kotlin.gradle.plugin.experimental.plugins.KotlinPlatformNativePlugin'
}
}
}
@@ -34,7 +34,12 @@ internal interface KonanToolRunner: Named {
fun run(vararg args: String) = run(args.toList())
}
internal abstract class KonanCliRunner(val toolName: String, val fullName: String, val project: Project): KonanToolRunner {
internal abstract class KonanCliRunner(
val toolName: String,
val fullName: String,
val project: Project,
private val additionalJvmArgs: List<String>
): KonanToolRunner {
override val mainClass = "org.jetbrains.kotlin.cli.utilities.MainKt"
override fun getName() = toolName
@@ -51,11 +56,11 @@ internal abstract class KonanCliRunner(val toolName: String, val fullName: Strin
.apply { include("*.jar") }
override val jvmArgs = mutableListOf("-ea").apply {
if (project.konanExtension.jvmArgs.none { it.startsWith("-Xmx") } &&
if (additionalJvmArgs.none { it.startsWith("-Xmx") } &&
project.jvmArgs.none { it.startsWith("-Xmx") }) {
add("-Xmx3G")
}
addAll(project.konanExtension.jvmArgs)
addAll(additionalJvmArgs)
addAll(project.jvmArgs)
}
@@ -87,8 +92,8 @@ internal abstract class KonanCliRunner(val toolName: String, val fullName: Strin
}
}
internal class KonanInteropRunner(project: Project)
: KonanCliRunner("cinterop", "Kotlin/Native cinterop tool", project)
internal class KonanInteropRunner(project: Project, additionalJvmArgs: List<String> = emptyList())
: KonanCliRunner("cinterop", "Kotlin/Native cinterop tool", project, additionalJvmArgs)
{
init {
if (HostManager.host == KonanTarget.MINGW_X64) {
@@ -100,5 +105,8 @@ internal class KonanInteropRunner(project: Project)
}
}
internal class KonanCompilerRunner(project: Project) : KonanCliRunner("konanc", "Kotlin/Native compiler", project)
internal class KonanKlibRunner(project: Project) : KonanCliRunner("klib", "Klib management tool", project)
internal class KonanCompilerRunner(project: Project, additionalJvmArgs: List<String> = emptyList())
: KonanCliRunner("konanc", "Kotlin/Native compiler", project, additionalJvmArgs)
internal class KonanKlibRunner(project: Project, additionalJvmArgs: List<String> = emptyList())
: KonanCliRunner("klib", "Klib management tool", project, additionalJvmArgs)
@@ -0,0 +1,8 @@
package org.jetbrains.kotlin.gradle.plugin.experimental
import org.gradle.api.component.SoftwareComponent
import org.gradle.api.provider.Provider
interface ComponentWithBaseName: SoftwareComponent {
fun getBaseName(): Provider<String>
}
@@ -0,0 +1,84 @@
package org.jetbrains.kotlin.gradle.plugin.experimental
import org.gradle.api.attributes.Attribute
import org.gradle.api.component.BuildableComponent
import org.gradle.api.component.PublishableComponent
import org.gradle.api.file.FileCollection
import org.gradle.api.provider.Provider
import org.gradle.language.ComponentWithDependencies
import org.gradle.language.ComponentWithOutputs
import org.gradle.language.nativeplatform.internal.ConfigurableComponentWithLinkUsage
import org.gradle.language.nativeplatform.internal.ConfigurableComponentWithRuntimeUsage
import org.gradle.nativeplatform.test.TestComponent
import org.jetbrains.kotlin.gradle.plugin.experimental.internal.KotlinNativePlatform
import org.jetbrains.kotlin.gradle.plugin.experimental.tasks.KotlinNativeCompile
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
import org.jetbrains.kotlin.konan.target.KonanTarget
// TODO: implement ComponentWithObjectFiles when we build klibs as objects
interface KotlinNativeBinary: ComponentWithDependencies, BuildableComponent {
/** Returns the source files of this binary. */
val sources: FileCollection
/**
* Konan target the library is built for
*/
val konanTarget: KonanTarget
/**
* Gradle NativePlatform object the binary is built for.
*/
fun getTargetPlatform(): KotlinNativePlatform
/** Compile task for this library */
val compileTask: Provider<KotlinNativeCompile>
// TODO: Support native link and runtime libraries here.
// Looks like we need at least 3 file collections here: for klibs, for linktime native libraries and for runtime native libraries.
/**
* The link libraries (klibs only!) used to link this binary.
* Includes the link libraries of the component's dependencies.
*/
val klibraries: FileCollection
/**
* Binary kind in terms of the KN compiler (program, library, dynamic etc).
*/
val kind: CompilerOutputKind
/**
* Additinal command line options passed to the compiler when this binary is compiled.
*/
val additionalCompilerOptions: MutableCollection<String>
companion object {
val KONAN_TARGET_ATTRIBUTE = Attribute.of("org.gradle.native.kotlin.platform", String::class.java)
}
}
/**
* Represents Kotlin/Native executable.
*/
// TODO: Consider implementing ComponentWithExecutable and ComponentWithInstallation.
interface KotlinNativeExecutable : KotlinNativeBinary,
ComponentWithOutputs,
PublishableComponent,
ConfigurableComponentWithRuntimeUsage
/**
* A component representing a klibrary.
*/
// TODO: Consider implementing ComponentWithLinkFile.
interface KotlinNativeKLibrary : KotlinNativeBinary,
ComponentWithOutputs,
PublishableComponent,
ConfigurableComponentWithLinkUsage
/**
* Represents a test executable.
*/
// TODO: Consider implementing ComponentWithExecutable and ComponentWithInstallation.
interface KotlinNativeTestExecutable : KotlinNativeBinary,
ComponentWithOutputs,
TestComponent
@@ -0,0 +1,50 @@
package org.jetbrains.kotlin.gradle.plugin.experimental
import org.gradle.api.artifacts.Configuration
import org.gradle.api.provider.Provider
import org.gradle.api.provider.SetProperty
import org.gradle.language.BinaryCollection
import org.gradle.language.ComponentWithBinaries
import org.gradle.language.ComponentWithDependencies
import org.gradle.nativeplatform.test.TestSuiteComponent
import org.jetbrains.kotlin.gradle.plugin.experimental.sourcesets.KotlinNativeSourceSet
import org.jetbrains.kotlin.konan.target.KonanTarget
/**
* Class representing a Kotlin/Native component: application or library (both klib and dynamic)
* built for different targets.
*/
interface KotlinNativeComponent: ComponentWithBinaries, ComponentWithDependencies {
/**
* Defines the source files or directories of this component. You can add files or directories to this collection.
* When a directory is added, all source files are included for compilation. When this collection is empty,
* the directory src/main/kotlin is used by default.
*/
val sources: KotlinNativeSourceSet
/** Specifies Kotlin/Native targets used to build this component. */
val konanTargets: SetProperty<KonanTarget>
/** Returns the binaries for this library. */
override fun getBinaries(): BinaryCollection<out KotlinNativeBinary>
/** Returns the implementation dependencies of this component. */
fun getImplementationDependencies(): Configuration
// region DSL
/** Set native targets for this component. */
fun target(vararg targets: String)
// endregion
}
/**
* Class representing a test suite for Kotlin/Native
*/
interface KotlinNativeTestComponent : KotlinNativeComponent, TestSuiteComponent {
val testedComponent: KotlinNativeComponent
override fun getTestBinary(): Provider<KotlinNativeTestExecutable>
}
@@ -0,0 +1,117 @@
package org.jetbrains.kotlin.gradle.plugin.experimental.internal
import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.ConfigurationContainer
import org.gradle.api.attributes.Usage
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.FileCollection
import org.gradle.api.file.ProjectLayout
import org.gradle.api.internal.file.FileOperations
import org.gradle.api.model.ObjectFactory
import org.gradle.api.provider.Property
import org.gradle.api.provider.Provider
import org.gradle.language.ComponentDependencies
import org.gradle.language.ComponentWithDependencies
import org.gradle.language.ComponentWithOutputs
import org.gradle.language.cpp.CppBinary
import org.gradle.language.internal.DefaultComponentDependencies
import org.gradle.language.nativeplatform.internal.ComponentWithNames
import org.gradle.language.nativeplatform.internal.Names
import org.gradle.nativeplatform.OperatingSystemFamily
import org.gradle.nativeplatform.toolchain.NativeToolChain
import org.jetbrains.kotlin.gradle.plugin.experimental.ComponentWithBaseName
import org.jetbrains.kotlin.gradle.plugin.experimental.KotlinNativeBinary
import org.jetbrains.kotlin.gradle.plugin.experimental.sourcesets.KotlinNativeSourceSet
import org.jetbrains.kotlin.gradle.plugin.experimental.tasks.KotlinNativeCompile
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
import org.jetbrains.kotlin.konan.target.KonanTarget
/*
* We use the same configuration hierarchy as Gradle native:
*
* componentImplementation (dependencies for the whole component ( = sourceSet): something like 'foo:bar:1.0')
* ^
* |
* binaryImplementation (dependnecies of a particular target/flavor) (= getDependencies.implementationDependencies)
* ^ ^ ^
* | | |
* linkLibraries runtimeLibraries klibs (dependencies by type: klib, static lib, shared lib etc)
*
*/
abstract class AbstractKotlinNativeBinary(
private val name: String,
private val baseName: Provider<String>,
val sourceSet: KotlinNativeSourceSet,
val identity: KotlinNativeVariantIdentity,
val projectLayout: ProjectLayout,
override val kind: CompilerOutputKind,
objects: ObjectFactory,
componentImplementation: Configuration,
configurations: ConfigurationContainer,
val fileOperations: FileOperations
) : KotlinNativeBinary,
ComponentWithNames,
ComponentWithDependencies,
ComponentWithBaseName,
ComponentWithOutputs
{
private val names = Names.of(name)
override fun getNames(): Names = names
override fun getName(): String = name
override val konanTarget: KonanTarget
get() = identity.konanTarget
override fun getTargetPlatform(): KotlinNativePlatform = identity.targetPlatform
open val debuggable: Boolean get() = identity.isDebuggable
open val optimized: Boolean get() = identity.isOptimized
override val sources: FileCollection
get() = sourceSet.getAllSources(konanTarget)
private val dependencies = objects.newInstance<DefaultComponentDependencies>(
DefaultComponentDependencies::class.java,
name + "Implementation"
).apply {
implementationDependencies.extendsFrom(componentImplementation)
}
override fun getDependencies(): ComponentDependencies = dependencies
fun getImplementationDependencies(): Configuration = dependencies.implementationDependencies
// A configuration containing klibraries
override val klibraries = configurations.create(names.withPrefix("klibraries")).apply {
isCanBeConsumed = false
attributes.attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage::class.java, KotlinNativeUsage.KLIB))
attributes.attribute(CppBinary.DEBUGGABLE_ATTRIBUTE, debuggable)
attributes.attribute(CppBinary.OPTIMIZED_ATTRIBUTE, optimized)
attributes.attribute(KotlinNativeBinary.KONAN_TARGET_ATTRIBUTE, konanTarget.name)
attributes.attribute(OperatingSystemFamily.OPERATING_SYSTEM_ATTRIBUTE, konanTarget.getGradleOSFamily(objects))
extendsFrom(getImplementationDependencies())
}
override fun getBaseName(): Provider<String> = baseName
override val compileTask: Property<KotlinNativeCompile> = objects.property(KotlinNativeCompile::class.java)
open fun isDebuggable(): Boolean = debuggable
open fun isOptimized(): Boolean = optimized
// TODO: Support native libraries
fun getLinkLibraries(): FileCollection = fileOperations.files()
fun getRuntimeLibraries(): FileCollection = fileOperations.files()
fun getToolChain(): NativeToolChain =
throw NotImplementedError("Kotlin/Native doesn't support the Gradle's toolchain model.")
/** A name of a root folder for this binary's output under the build directory. */
internal abstract val outputRootName: String
private val outputs: ConfigurableFileCollection = fileOperations.files()
override fun getOutputs() = outputs
override val additionalCompilerOptions = mutableListOf<String>()
}
@@ -0,0 +1,63 @@
package org.jetbrains.kotlin.gradle.plugin.experimental.internal
import org.gradle.api.artifacts.Configuration
import org.gradle.api.internal.file.FileOperations
import org.gradle.api.internal.provider.LockableSetProperty
import org.gradle.api.model.ObjectFactory
import org.gradle.api.provider.Property
import org.gradle.language.internal.DefaultBinaryCollection
import org.gradle.language.internal.DefaultComponentDependencies
import org.gradle.language.nativeplatform.internal.ComponentWithNames
import org.gradle.language.nativeplatform.internal.DefaultNativeComponent
import org.gradle.language.nativeplatform.internal.Names
import org.jetbrains.kotlin.gradle.plugin.experimental.KotlinNativeBinary
import org.jetbrains.kotlin.gradle.plugin.experimental.KotlinNativeComponent
import org.jetbrains.kotlin.gradle.plugin.experimental.sourcesets.KotlinNativeSourceSetImpl
import org.jetbrains.kotlin.konan.target.HostManager
import org.jetbrains.kotlin.konan.target.KonanTarget
import javax.inject.Inject
abstract class AbstractKotlinNativeComponent @Inject constructor(
private val name: String,
override val sources: KotlinNativeSourceSetImpl,
val objectFactory: ObjectFactory,
fileOperations: FileOperations
) : DefaultNativeComponent(fileOperations),
KotlinNativeComponent,
ComponentWithNames {
private val baseName: Property<String> = objectFactory.property(String::class.java).apply { set(name) }
fun getBaseName(): Property<String> = baseName
override val konanTargets: LockableSetProperty<KonanTarget> =
LockableSetProperty(objectFactory.setProperty(KonanTarget::class.java)).apply {
set(mutableSetOf(HostManager.host))
}
@Suppress("UNCHECKED_CAST")
private val binaries = objectFactory.newInstance(DefaultBinaryCollection::class.java, KotlinNativeBinary::class.java)
as DefaultBinaryCollection<KotlinNativeBinary>
override fun getBinaries(): DefaultBinaryCollection<KotlinNativeBinary> = binaries
override fun getName(): String = name
private val names = Names.of(name)
override fun getNames(): Names = names
private val dependencies: DefaultComponentDependencies = objectFactory.newInstance(
DefaultComponentDependencies::class.java,
names.withSuffix("implementation"))
override fun getDependencies() = dependencies
override fun getImplementationDependencies(): Configuration = dependencies.implementationDependencies
// region DSL
override fun target(vararg targets: String) {
val hostManager = HostManager()
konanTargets.set(targets.map { hostManager.targetByName(it) })
}
// endregion
}
@@ -0,0 +1,19 @@
package org.jetbrains.kotlin.gradle.plugin.experimental.internal
import org.gradle.api.Named
import java.util.*
class KotlinNativeBuildType(
private val name: String,
val debuggable: Boolean,
val optimized: Boolean
) : Named {
override fun getName() = name
companion object {
val DEBUG = KotlinNativeBuildType("debug", true, false)
val RELEASE = KotlinNativeBuildType("release", true, true)
val DEFAULT_BUILD_TYPES: Collection<KotlinNativeBuildType> = Arrays.asList(DEBUG, RELEASE)
}
}
@@ -0,0 +1,68 @@
package org.jetbrains.kotlin.gradle.plugin.experimental.internal
import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.ConfigurationContainer
import org.gradle.api.artifacts.ModuleVersionIdentifier
import org.gradle.api.attributes.AttributeContainer
import org.gradle.api.component.PublishableComponent
import org.gradle.api.file.ProjectLayout
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.internal.component.SoftwareComponentInternal
import org.gradle.api.internal.component.UsageContext
import org.gradle.api.internal.file.FileOperations
import org.gradle.api.model.ObjectFactory
import org.gradle.api.provider.Property
import org.gradle.api.provider.Provider
import org.gradle.language.cpp.internal.DefaultUsageContext
import org.gradle.nativeplatform.Linkage
import org.jetbrains.kotlin.gradle.plugin.experimental.KotlinNativeExecutable
import org.jetbrains.kotlin.gradle.plugin.experimental.sourcesets.KotlinNativeSourceSet
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
import javax.inject.Inject
open class KotlinNativeExecutableImpl @Inject constructor(
name: String,
baseName: Provider<String>,
componentImplementation: Configuration,
sources: KotlinNativeSourceSet,
identity: KotlinNativeVariantIdentity,
objects: ObjectFactory,
projectLayout: ProjectLayout,
configurations: ConfigurationContainer,
fileOperations: FileOperations
) : AbstractKotlinNativeBinary(name,
baseName,
sources,
identity,
projectLayout,
CompilerOutputKind.PROGRAM,
objects,
componentImplementation,
configurations,
fileOperations),
KotlinNativeExecutable,
SoftwareComponentInternal, // TODO: SoftwareComponentInternal will be replaced with ComponentWithVariants by Gradle
PublishableComponent
{
override fun getCoordinates(): ModuleVersionIdentifier = identity.coordinates
// Properties
// Runtime elements configuration is created by the NativeBase plugin
private val runtimeElementsProperty: Property<Configuration> = objects.property(Configuration::class.java)
private val runtimeFileProperty: RegularFileProperty = projectLayout.fileProperty()
// Interface Implementation
override fun getRuntimeElements() = runtimeElementsProperty
override fun getRuntimeFile() = runtimeFileProperty
override fun hasRuntimeFile() = true
override fun getRuntimeAttributes(): AttributeContainer = identity.runtimeUsageContext.attributes
override fun getLinkage(): Linkage? = null
override fun getUsages(): Set<UsageContext> = runtimeElementsProperty.get().let {
setOf(DefaultUsageContext(identity.runtimeUsageContext, it.allArtifacts, it))
}
override val outputRootName = "exe"
}
@@ -0,0 +1,70 @@
package org.jetbrains.kotlin.gradle.plugin.experimental.internal
import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.ConfigurationContainer
import org.gradle.api.artifacts.ModuleVersionIdentifier
import org.gradle.api.attributes.AttributeContainer
import org.gradle.api.component.PublishableComponent
import org.gradle.api.file.ProjectLayout
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.internal.component.SoftwareComponentInternal
import org.gradle.api.internal.component.UsageContext
import org.gradle.api.internal.file.FileOperations
import org.gradle.api.model.ObjectFactory
import org.gradle.api.provider.Property
import org.gradle.api.provider.Provider
import org.gradle.language.cpp.internal.DefaultUsageContext
import org.gradle.nativeplatform.Linkage
import org.jetbrains.kotlin.gradle.plugin.experimental.KotlinNativeKLibrary
import org.jetbrains.kotlin.gradle.plugin.experimental.sourcesets.KotlinNativeSourceSet
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
import javax.inject.Inject
open class KotlinNativeKLibraryImpl @Inject constructor(
name: String,
baseName: Provider<String>,
componentImplementation: Configuration,
sources: KotlinNativeSourceSet,
identity: KotlinNativeVariantIdentity,
objects: ObjectFactory,
projectLayout: ProjectLayout,
configurations: ConfigurationContainer,
fileOperations: FileOperations
) : AbstractKotlinNativeBinary(name,
baseName,
sources,
identity,
projectLayout,
CompilerOutputKind.LIBRARY,
objects,
componentImplementation,
configurations,
fileOperations),
KotlinNativeKLibrary,
SoftwareComponentInternal, // TODO: SoftwareComponentInternal will be replaced with ComponentWithVariants by Gradle
PublishableComponent
{
override fun getCoordinates(): ModuleVersionIdentifier = identity.coordinates
// Properties
// The link elements configuration is created by the NativeBase plugin.
private val linkElementsProperty: Property<Configuration> = objects.property(Configuration::class.java)
private val linkFileProperty: RegularFileProperty = projectLayout.fileProperty()
// Interface
override fun getLinkElements() = linkElementsProperty
override fun getLinkFile() = linkFileProperty
override fun getUsages(): Set<UsageContext> = linkElementsProperty.get().let {
setOf(DefaultUsageContext(identity.linkUsageContext, it.allArtifacts, it))
}
override fun getLinkAttributes(): AttributeContainer = identity.linkUsageContext.attributes
// TODO: Does Klib really match a static linkage in Gradle's terms?
override fun getLinkage(): Linkage? = Linkage.STATIC
override val outputRootName = "lib"
}
@@ -0,0 +1,86 @@
package org.jetbrains.kotlin.gradle.plugin.experimental.internal
import org.gradle.api.component.ComponentWithVariants
import org.gradle.api.component.SoftwareComponent
import org.gradle.api.internal.component.SoftwareComponentInternal
import org.gradle.api.internal.component.UsageContext
import org.gradle.api.internal.file.FileOperations
import org.gradle.api.internal.provider.LockableSetProperty
import org.gradle.api.model.ObjectFactory
import org.gradle.api.provider.Property
import org.gradle.internal.Describables
import org.gradle.internal.DisplayName
import org.gradle.language.ProductionComponent
import org.gradle.language.cpp.internal.NativeVariantIdentity
import org.gradle.language.nativeplatform.internal.PublicationAwareComponent
import org.jetbrains.kotlin.gradle.plugin.experimental.KotlinNativeBinary
import org.jetbrains.kotlin.gradle.plugin.experimental.sourcesets.KotlinNativeSourceSetImpl
import javax.inject.Inject
open class KotlinNativeMainComponent @Inject constructor(
name: String,
sources: KotlinNativeSourceSetImpl,
objectFactory: ObjectFactory,
fileOperations: FileOperations
) : AbstractKotlinNativeComponent(name, sources, objectFactory, fileOperations),
PublicationAwareComponent,
ProductionComponent {
override fun getDisplayName(): DisplayName = Describables.withTypeAndName("Kotlin/Native component", name)
val outputKinds = LockableSetProperty(objectFactory.setProperty(OutputKind::class.java)).apply {
set(mutableSetOf(OutputKind.KLIBRARY))
}
private val mainPublication = KotlinNativeVariant()
override fun getMainPublication(): KotlinNativeVariant = mainPublication
private val developmentBinaryProperty: Property<KotlinNativeBinary> =
objectFactory.property(KotlinNativeBinary::class.java)
override fun getDevelopmentBinary() = developmentBinaryProperty
private fun <T : KotlinNativeBinary> addBinary(type: Class<T>, identity: NativeVariantIdentity): T =
objectFactory.newInstance(
type,
"$name${identity.name.capitalize()}",
baseName,
getImplementationDependencies(),
sources,
identity
).apply {
binaries.add(this)
}
private inline fun <reified T : KotlinNativeBinary> addBinary(identity: NativeVariantIdentity): T =
addBinary(T::class.java, identity)
fun addExecutable(identity: NativeVariantIdentity) = addBinary<KotlinNativeExecutableImpl>(identity)
fun addKLibrary(identity: NativeVariantIdentity) = addBinary<KotlinNativeKLibraryImpl>(identity)
fun addBinary(kind: OutputKind, identity: NativeVariantIdentity) = addBinary(kind.binaryClass, identity)
// region Kotlin/Native variant
// TODO: SoftwareComponentInternal will be replaced with ComponentWithVariants by Gradle
inner class KotlinNativeVariant: ComponentWithVariants, SoftwareComponentInternal {
private val variants = mutableSetOf<SoftwareComponent>()
override fun getVariants() = variants
override fun getName(): String = this@KotlinNativeMainComponent.name
override fun getUsages(): Set<UsageContext> = emptySet()
}
// endregion
companion object {
@JvmStatic
val EXECUTABLE = OutputKind.EXECUTABLE
@JvmStatic
val KLIBRARY = OutputKind.KLIBRARY
}
}
@@ -0,0 +1,38 @@
package org.jetbrains.kotlin.gradle.plugin.experimental.internal
import org.gradle.api.model.ObjectFactory
import org.gradle.internal.os.OperatingSystem
import org.gradle.nativeplatform.OperatingSystemFamily
import org.gradle.nativeplatform.platform.NativePlatform
import org.gradle.nativeplatform.platform.internal.*
import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.konan.util.visibleName
interface KotlinNativePlatform: NativePlatform {
val target: KonanTarget
}
fun KonanTarget.getGradleOS(): OperatingSystemInternal = family.visibleName.let {
DefaultOperatingSystem(it, OperatingSystem.forName(it))
}
fun KonanTarget.getGradleOSFamily(objectFactory: ObjectFactory): OperatingSystemFamily {
return objectFactory.named(OperatingSystemFamily::class.java, family.visibleName)
}
fun KonanTarget.getGradleCPU(): ArchitectureInternal = architecture.visibleName.let {
Architectures.forInput(it)
}
class DefaultKotlinNativePlatform(name: String, override val target: KonanTarget):
DefaultNativePlatform(name, target.getGradleOS(), target.getGradleCPU()),
KotlinNativePlatform
{
constructor(target: KonanTarget): this(target.visibleName, target)
// TODO: Extend ImmutableDefaultNativePlatform and get rid of these methods after switch to Gradle 4.8
private fun notImplemented(): Nothing = throw NotImplementedError("Not Implemented in Kotlin/Native plugin")
override fun operatingSystem(name: String?) = notImplemented()
override fun withArchitecture(architecture: ArchitectureInternal?) = notImplemented()
override fun architecture(name: String?) = notImplemented()
}
@@ -0,0 +1,52 @@
package org.jetbrains.kotlin.gradle.plugin.experimental.internal
import org.gradle.api.Task
import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.ConfigurationContainer
import org.gradle.api.file.FileCollection
import org.gradle.api.file.ProjectLayout
import org.gradle.api.internal.file.FileOperations
import org.gradle.api.model.ObjectFactory
import org.gradle.api.provider.Property
import org.gradle.api.provider.Provider
import org.jetbrains.kotlin.gradle.plugin.experimental.KotlinNativeTestExecutable
import org.jetbrains.kotlin.gradle.plugin.experimental.sourcesets.KotlinNativeSourceSet
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
import javax.inject.Inject
open class KotlinNativeTestExecutableImpl @Inject constructor(
name: String,
baseName: Provider<String>,
componentImplementation: Configuration,
testSources: KotlinNativeSourceSet,
val mainSources: KotlinNativeSourceSet,
identity: KotlinNativeVariantIdentity,
objects: ObjectFactory,
projectLayout: ProjectLayout,
configurations: ConfigurationContainer,
fileOperations: FileOperations
) : AbstractKotlinNativeBinary(name,
baseName,
testSources,
identity,
projectLayout,
CompilerOutputKind.PROGRAM,
objects,
componentImplementation,
configurations,
fileOperations),
KotlinNativeTestExecutable {
init {
additionalCompilerOptions.add("-tr")
}
private val runTaskProperty : Property<Task> = objects.property(Task::class.java)
override fun getRunTask() = runTaskProperty
override val sources: FileCollection
get() = super.sources + mainSources.getAllSources(konanTarget)
override val outputRootName: String = "exe"
}
@@ -0,0 +1,47 @@
package org.jetbrains.kotlin.gradle.plugin.experimental.internal
import org.gradle.api.internal.file.FileOperations
import org.gradle.api.model.ObjectFactory
import org.gradle.api.provider.Property
import org.gradle.internal.Describables
import org.gradle.internal.DisplayName
import org.gradle.language.cpp.internal.NativeVariantIdentity
import org.jetbrains.kotlin.gradle.plugin.experimental.KotlinNativeComponent
import org.jetbrains.kotlin.gradle.plugin.experimental.KotlinNativeTestComponent
import org.jetbrains.kotlin.gradle.plugin.experimental.KotlinNativeTestExecutable
import org.jetbrains.kotlin.gradle.plugin.experimental.sourcesets.KotlinNativeSourceSetImpl
import javax.inject.Inject
open class KotlinNativeTestSuite @Inject constructor(
name: String,
sources: KotlinNativeSourceSetImpl,
override val testedComponent: KotlinNativeComponent,
objectFactory: ObjectFactory,
fileOperations: FileOperations
) : AbstractKotlinNativeComponent(name, sources, objectFactory, fileOperations),
KotlinNativeTestComponent {
init {
getImplementationDependencies().extendsFrom(testedComponent.getImplementationDependencies())
}
override fun getDisplayName(): DisplayName = Describables.withTypeAndName("Kotlin/Native test suite", name)
private val testBinaryProperty: Property<KotlinNativeTestExecutable> =
objectFactory.property(KotlinNativeTestExecutable::class.java)
override fun getTestBinary() = testBinaryProperty
fun addTestExecutable(identity: NativeVariantIdentity): KotlinNativeTestExecutable =
objectFactory.newInstance(
KotlinNativeTestExecutableImpl::class.java,
"$name${identity.name.capitalize()}",
getBaseName(),
getImplementationDependencies(),
sources,
testedComponent.sources,
identity
).apply {
binaries.add(this)
}
}
@@ -0,0 +1,5 @@
package org.jetbrains.kotlin.gradle.plugin.experimental.internal
object KotlinNativeUsage {
const val KLIB = "kotlin-native-klib"
}
@@ -0,0 +1,32 @@
package org.jetbrains.kotlin.gradle.plugin.experimental.internal
import org.gradle.api.internal.component.UsageContext
import org.gradle.api.model.ObjectFactory
import org.gradle.api.provider.Provider
import org.gradle.language.cpp.internal.NativeVariantIdentity
import org.jetbrains.kotlin.konan.target.KonanTarget
open class KotlinNativeVariantIdentity(
name: String,
baseName: Provider<String>,
group: Provider<String>,
version: Provider<String>,
val konanTarget: KonanTarget,
debuggable: Boolean,
optimized: Boolean,
linkUsage: UsageContext?,
runtimeUsage: UsageContext?,
objects: ObjectFactory
) : NativeVariantIdentity(
name,
baseName,
group,
version,
debuggable,
optimized,
konanTarget.getGradleOSFamily(objects),
linkUsage,
runtimeUsage
) {
val targetPlatform = DefaultKotlinNativePlatform(konanTarget)
}
@@ -0,0 +1,30 @@
package org.jetbrains.kotlin.gradle.plugin.experimental.internal
import org.gradle.api.attributes.Usage
import org.jetbrains.kotlin.gradle.plugin.experimental.KotlinNativeBinary
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
enum class OutputKind(
val compilerOutputKind: CompilerOutputKind,
val binaryClass: Class<out KotlinNativeBinary>,
private val developmentBinaryPriority: Int,
val runtimeUsageName: String? = null,
val linkUsageName: String? = null
) {
EXECUTABLE(CompilerOutputKind.PROGRAM,
KotlinNativeExecutableImpl::class.java,
1,
Usage.NATIVE_RUNTIME,
null
),
KLIBRARY(CompilerOutputKind.LIBRARY,
KotlinNativeKLibraryImpl::class.java,
0,
null,
KotlinNativeUsage.KLIB
);
companion object {
internal fun Collection<OutputKind>.getDevelopmentKind() = maxBy { it.developmentBinaryPriority }
}
}
@@ -0,0 +1,161 @@
package org.jetbrains.kotlin.gradle.plugin.experimental.plugins
import org.gradle.api.DefaultTask
import org.gradle.api.Plugin
import org.gradle.api.component.SoftwareComponentContainer
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.internal.FeaturePreviews
import org.gradle.api.internal.project.ProjectInternal
import org.gradle.api.plugins.BasePlugin
import org.gradle.api.provider.ProviderFactory
import org.gradle.api.tasks.TaskContainer
import org.gradle.language.base.plugins.LifecycleBasePlugin
import org.gradle.language.plugins.NativeBasePlugin
import org.gradle.nativeplatform.test.tasks.RunTestExecutable
import org.gradle.util.GradleVersion
import org.jetbrains.kotlin.gradle.plugin.KonanPlugin
import org.jetbrains.kotlin.gradle.plugin.experimental.internal.AbstractKotlinNativeBinary
import org.jetbrains.kotlin.gradle.plugin.experimental.internal.KotlinNativeExecutableImpl
import org.jetbrains.kotlin.gradle.plugin.experimental.internal.KotlinNativeKLibraryImpl
import org.jetbrains.kotlin.gradle.plugin.experimental.internal.KotlinNativeTestExecutableImpl
import org.jetbrains.kotlin.gradle.plugin.experimental.tasks.KotlinNativeCompile
import org.jetbrains.kotlin.gradle.plugin.hasProperty
import org.jetbrains.kotlin.gradle.plugin.konanCompilerDownloadDir
import org.jetbrains.kotlin.gradle.plugin.setProperty
import org.jetbrains.kotlin.gradle.plugin.tasks.KonanCompilerDownloadTask
import org.jetbrains.kotlin.konan.target.HostManager
import org.jetbrains.kotlin.konan.target.KonanTarget
class KotlinNativeBasePlugin: Plugin<ProjectInternal> {
private fun TaskContainer.createRunTestTask(
taskName: String,
binary: KotlinNativeTestExecutableImpl,
compileTask: KotlinNativeCompile
) = create(taskName, RunTestExecutable::class.java).apply {
group = LifecycleBasePlugin.VERIFICATION_GROUP
description = "Executes Kotlin/Native unit tests."
val testExecutableProperty = compileTask.outputFile
executable = testExecutableProperty.asFile.get().absolutePath
onlyIf { testExecutableProperty.asFile.get().exists() }
inputs.file(testExecutableProperty)
dependsOn(testExecutableProperty)
// TODO: Find or implement some mechanism for test result saving.
outputDir = project.layout.buildDirectory.dir("test-results/" + binary.names.dirName).get().asFile
}
private fun TaskContainer.createDummyTestTask(taskName: String, target: KonanTarget) =
create(taskName, DefaultTask::class.java).apply {
doLast {
logger.warn("""
The current host doesn't support running executables for target '${target.name}'.
Skipping tests.
""".trimIndent())
}
}
private val KonanTarget.canRunOnHost: Boolean
get() = this == HostManager.host
private fun addCompilationTasks(
tasks: TaskContainer,
components: SoftwareComponentContainer,
buildDirectory: DirectoryProperty,
providers: ProviderFactory
) {
components.withType(AbstractKotlinNativeBinary::class.java) { binary ->
val names = binary.names
val target = binary.konanTarget
val kind = binary.kind
val compileTask = tasks.create(
names.getCompileTaskName(LANGUAGE_NAME),
KotlinNativeCompile::class.java
).apply {
this.binary = binary
outputFile.set(buildDirectory.file(providers.provider {
val root = binary.outputRootName
val prefix = kind.prefix(target)
val suffix = kind.suffix(target)
val baseName = binary.getBaseName().get()
"$root/${names.dirName}/${prefix}${baseName}${suffix}"
}))
group = BasePlugin.BUILD_GROUP
description = "Compiles Kotlin/Native source set '${binary.sourceSet.name}' into a ${binary.kind.name.toLowerCase()}"
}
binary.compileTask.set(compileTask)
binary.outputs.from(compileTask.outputFile)
when(binary) {
is KotlinNativeExecutableImpl -> binary.runtimeFile.set(compileTask.outputFile)
is KotlinNativeKLibraryImpl -> binary.linkFile.set(compileTask.outputFile)
}
if (binary is KotlinNativeTestExecutableImpl) {
val taskName = binary.names.getTaskName("run")
val testTask = if (target.canRunOnHost) {
tasks.createRunTestTask(taskName, binary, compileTask)
} else {
// We create dummy tasks that just write an error message to avoid having an unset
// runTask property for targets that cannot be executed on the current host.
tasks.createDummyTestTask(taskName, target)
}
binary.runTask.set(testTask)
}
}
}
private fun ProjectInternal.checkGradleMetadataFeature() {
val metadataEnabled = gradle.services
.get(FeaturePreviews::class.java)
.isFeatureEnabled(FeaturePreviews.Feature.GRADLE_METADATA)
if (!metadataEnabled) {
logger.warn(
"The GRADLE_METADATA feature is not enabled: publication and external dependencies will not work properly."
)
}
}
private fun checkGradleVersion() = GradleVersion.current().let { current ->
check(current >= KonanPlugin.REQUIRED_GRADLE_VERSION) {
"Kotlin/Native Gradle plugin is incompatible with this version of Gradle.\n" +
"The minimal required version is ${KonanPlugin.REQUIRED_GRADLE_VERSION}\n" +
"Current version is ${current}"
}
}
private fun ProjectInternal.addCompilerDownloadingTask(): KonanCompilerDownloadTask {
val result = tasks.create(KonanPlugin.KONAN_DOWNLOAD_TASK_NAME, KonanCompilerDownloadTask::class.java)
if (!hasProperty(KonanPlugin.ProjectProperty.KONAN_HOME)) {
setProperty(KonanPlugin.ProjectProperty.KONAN_HOME, project.konanCompilerDownloadDir())
setProperty(KonanPlugin.ProjectProperty.DOWNLOAD_COMPILER, true)
}
return result
}
override fun apply(project: ProjectInternal): Unit = with(project) {
// TODO: Deal with compiler downloading.
// Apply base plugins
project.pluginManager.apply(LifecycleBasePlugin::class.java)
project.pluginManager.apply(NativeBasePlugin::class.java)
checkGradleMetadataFeature()
checkGradleVersion()
addCompilerDownloadingTask()
// Create compile tasks
addCompilationTasks(tasks, components, layout.buildDirectory, providers)
}
companion object {
const val LANGUAGE_NAME = "KotlinNative"
const val SOURCE_SETS_EXTENSION = "sourceSets"
}
}
@@ -0,0 +1,212 @@
package org.jetbrains.kotlin.gradle.plugin.experimental.plugins
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.attributes.Usage
import org.gradle.api.internal.FactoryNamedDomainObjectContainer
import org.gradle.api.internal.attributes.ImmutableAttributesFactory
import org.gradle.api.internal.project.ProjectInternal
import org.gradle.api.model.ObjectFactory
import org.gradle.api.provider.Provider
import org.gradle.internal.reflect.Instantiator
import org.gradle.language.cpp.CppBinary.DEBUGGABLE_ATTRIBUTE
import org.gradle.language.cpp.CppBinary.OPTIMIZED_ATTRIBUTE
import org.gradle.language.cpp.internal.DefaultUsageContext
import org.gradle.nativeplatform.test.plugins.NativeTestingBasePlugin
import org.jetbrains.kotlin.gradle.plugin.experimental.KotlinNativeBinary.Companion.KONAN_TARGET_ATTRIBUTE
import org.jetbrains.kotlin.gradle.plugin.experimental.internal.*
import org.jetbrains.kotlin.gradle.plugin.experimental.internal.OutputKind.Companion.getDevelopmentKind
import org.jetbrains.kotlin.gradle.plugin.experimental.sourcesets.KotlinNativeSourceSetFactory
import org.jetbrains.kotlin.gradle.plugin.experimental.sourcesets.KotlinNativeSourceSetImpl
import org.jetbrains.kotlin.konan.target.HostManager
import org.jetbrains.kotlin.konan.target.KonanTarget
import javax.inject.Inject
class KotlinNativePlugin @Inject constructor(val attributesFactory: ImmutableAttributesFactory)
: Plugin<ProjectInternal> {
val hostManager = HostManager()
private val Collection<*>.isDimensionVisible: Boolean
get() = size > 1
private fun createDimensionSuffix(dimensionName: String, multivalueProperty: Collection<*>): String =
if (multivalueProperty.isDimensionVisible) {
dimensionName.toLowerCase().capitalize()
} else {
""
}
private fun createUsageContext(
usageName: String,
variantName: String,
usageContextSuffix: String,
buildType: KotlinNativeBuildType,
target: KonanTarget,
objectFactory: ObjectFactory
): DefaultUsageContext {
val usage = objectFactory.named(Usage::class.java, usageName)
val attributes = attributesFactory.mutable().apply {
attribute(Usage.USAGE_ATTRIBUTE, usage)
attribute(DEBUGGABLE_ATTRIBUTE, buildType.debuggable)
attribute(OPTIMIZED_ATTRIBUTE, buildType.optimized)
attribute(KONAN_TARGET_ATTRIBUTE, target.name)
}
return DefaultUsageContext(variantName + usageContextSuffix, usage, attributes)
}
private fun AbstractKotlinNativeComponent.getAndLockTargets(): Set<KonanTarget> {
konanTargets.lockNow()
return konanTargets.get().also {
require(it.isNotEmpty()) { "A Kotlin/Native target needs to be specified for the component." }
}
}
private fun KotlinNativeMainComponent.getAndLockOutputKinds(): Set<OutputKind> {
outputKinds.lockNow()
return outputKinds.get().also {
require(it.isNotEmpty()) { "An output kind needs to be specified for the component." }
}
}
private fun Collection<KonanTarget>.getDevelopmentTarget(): KonanTarget =
if (contains(HostManager.host)) HostManager.host else first()
private fun Project.addBinariesForMainComponents(group: Provider<String>, version: Provider<String>) {
for (component in components.withType(KotlinNativeMainComponent::class.java)) {
val targets = component.getAndLockTargets()
val outputKinds = component.getAndLockOutputKinds()
val developmentKind = outputKinds.getDevelopmentKind()
val developmentTarget = targets.getDevelopmentTarget()
val objectFactory = objects
val hostManager = HostManager()
for (kind in outputKinds) {
for (buildType in KotlinNativeBuildType.DEFAULT_BUILD_TYPES) {
for (target in targets) {
val buildTypeSuffix = buildType.name
val outputKindSuffix = createDimensionSuffix(kind.name, outputKinds)
val targetSuffix = createDimensionSuffix(target.name, targets)
val variantName = "${buildTypeSuffix}${outputKindSuffix}${targetSuffix}"
val linkUsageContext: DefaultUsageContext? = kind.linkUsageName?.let {
createUsageContext(it, variantName, "Link", buildType, target, objectFactory)
}
val runtimeUsageContext = kind.runtimeUsageName?.let {
createUsageContext(it, variantName, "Runtime", buildType, target, objectFactory)
}
// TODO: Do we need something like klibUsageContext?
val variantIdentity = KotlinNativeVariantIdentity(
variantName,
component.baseName,
group, version, target,
buildType.debuggable,
buildType.optimized,
linkUsageContext,
runtimeUsageContext,
objects
)
val binary = component.addBinary(kind, variantIdentity)
if (hostManager.isEnabled(target)) {
component.mainPublication.variants.add(binary)
} else {
// Known but not buildable.
// It allows us to publish different parts of a multitarget library from differnt hosts.
component.mainPublication.variants.add(variantIdentity)
}
if (kind == developmentKind &&
buildType == KotlinNativeBuildType.DEBUG &&
target == developmentTarget) {
component.developmentBinary.set(binary)
}
}
}
}
component.binaries.realizeNow()
}
}
private fun Project.addBinariesForTestComponents(group: Provider<String>, version: Provider<String>) {
for (component in components.withType(KotlinNativeTestSuite::class.java)) {
val targets = component.getAndLockTargets()
val buildType = KotlinNativeBuildType.DEBUG
for (target in targets) {
val buildTypeSuffix = buildType.name
val targetSuffix = createDimensionSuffix(target.name, targets)
val variantName = "${buildTypeSuffix}${targetSuffix}"
val variantIdentity = KotlinNativeVariantIdentity(
variantName,
component.getBaseName(),
group, version, target,
buildType.debuggable,
buildType.optimized,
null,
null,
objects
)
val binary = component.addTestExecutable(variantIdentity)
if (target == HostManager.host) {
component.testBinary.set(binary)
}
}
component.binaries.realizeNow()
}
}
override fun apply(project: ProjectInternal): Unit = with(project) {
pluginManager.apply(KotlinNativeBasePlugin::class.java)
pluginManager.apply(NativeTestingBasePlugin::class.java)
val instantiator = services.get(Instantiator::class.java)
val objectFactory = objects
@Suppress("UNCHECKED_CAST")
val sourceSets = project.extensions.create(
KotlinNativeBasePlugin.SOURCE_SETS_EXTENSION,
FactoryNamedDomainObjectContainer::class.java,
KotlinNativeSourceSetImpl::class.java,
instantiator,
KotlinNativeSourceSetFactory(this)
) as FactoryNamedDomainObjectContainer<KotlinNativeSourceSetImpl>
// TODO: We should be able to create a component automatically once
// a source set is created by user. So we need a user DSL or some another way
// to determine which component class should be instantiated (main or test).
val mainSourceSet = sourceSets.create("main").apply {
kotlin.srcDir("src/main/kotlin")
component = objectFactory
.newInstance(KotlinNativeMainComponent::class.java, name, this)
.apply {
// Override the default component base name.
baseName.set(project.name)
project.components.add(this)
}
}
sourceSets.create("test").apply {
kotlin.srcDir("src/test/kotlin")
component = objectFactory
.newInstance(KotlinNativeTestSuite::class.java, name, this, mainSourceSet.component)
.apply {
project.components.add(this)
}
}
// Create binaries for host
afterEvaluate {
val group = project.provider { project.group.toString() }
val version = project.provider { project.version.toString() }
addBinariesForMainComponents(group, version)
addBinariesForTestComponents(group, version)
}
}
}
@@ -0,0 +1,28 @@
package org.jetbrains.kotlin.gradle.plugin.experimental.plugins
import org.gradle.api.Project
import org.gradle.api.tasks.SourceSet
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformImplementationPluginBase
class KotlinPlatformNativePlugin : KotlinPlatformImplementationPluginBase("native") {
override fun apply(project: Project) = with(project) {
pluginManager.apply(KotlinNativePlugin::class.java)
// This configuration is necessary for correct work of the base platform plugin.
configurations.create("compile")
super.apply(project)
}
override fun addCommonSourceSetToPlatformSourceSet(commonSourceSet: SourceSet, platformProject: Project) {
val platformSourceSet = platformProject.kotlinNativeSourceSets.findByName(commonSourceSet.name)
val commonSources = commonSourceSet.kotlin
if (platformSourceSet != null && commonSources != null) {
platformSourceSet.commonSources.from(commonSources)
} else {
platformProject.logger.warn("""
Cannot add a common source set '${commonSourceSet.name}' in the project '${platformProject.path}'.
No such platform source set or the common source set has no Kotlin sources.
""".trimIndent())
}
}
}
@@ -0,0 +1,10 @@
package org.jetbrains.kotlin.gradle.plugin.experimental.plugins
import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.Project
import org.jetbrains.kotlin.gradle.plugin.experimental.sourcesets.KotlinNativeSourceSetImpl
@Suppress("UNCHECKED_CAST")
val Project.kotlinNativeSourceSets: NamedDomainObjectContainer<KotlinNativeSourceSetImpl>
get() = extensions.getByName(KotlinNativeBasePlugin.SOURCE_SETS_EXTENSION)
as NamedDomainObjectContainer<KotlinNativeSourceSetImpl>
@@ -0,0 +1,46 @@
package org.jetbrains.kotlin.gradle.plugin.experimental.sourcesets
import groovy.lang.Closure
import org.gradle.api.Action
import org.gradle.api.Named
import org.gradle.api.file.FileCollection
import org.gradle.api.file.SourceDirectorySet
import org.jetbrains.kotlin.gradle.plugin.experimental.KotlinNativeComponent
import org.jetbrains.kotlin.gradle.plugin.experimental.internal.AbstractKotlinNativeComponent
import org.jetbrains.kotlin.konan.target.KonanTarget
interface KotlinNativeSourceSet: Named {
val kotlin: SourceDirectorySet
val component: KotlinNativeComponent
/** Returns sources added in the source set via an expectedBy dependency. */
fun getCommonMultiplatformSources(): FileCollection
/** Returns sources common for all native targets. */
fun getCommonNativeSources(): SourceDirectorySet
/** Returns native sources specific for the given target. */
fun getPlatformSources(target: KonanTarget): SourceDirectorySet
/** Returns all (common multiplatform, common native and target-specific) sources for the given target. */
fun getAllSources(target: KonanTarget): FileCollection
fun kotlin(configureClosure: Closure<*>): KotlinNativeSourceSet
fun kotlin(configureAction: Action<in SourceDirectorySet>): KotlinNativeSourceSet
fun kotlin(configureLambda: SourceDirectorySet.() -> Unit): KotlinNativeSourceSet
fun component(configureClosure: Closure<*>): KotlinNativeSourceSet
fun component(configureAction: Action<in AbstractKotlinNativeComponent>): KotlinNativeSourceSet
fun component(configureLambda: AbstractKotlinNativeComponent.() -> Unit): KotlinNativeSourceSet
fun target(target: String): SourceDirectorySet
fun target(vararg targets: String): KotlinNativeSourceSet
fun target(vararg targets: String, configureClosure: Closure<*>): KotlinNativeSourceSet
fun target(vararg targets: String, configureAction: Action<in SourceDirectorySet>): KotlinNativeSourceSet
fun target(vararg targets: String, configureLambda: SourceDirectorySet.() -> Unit): KotlinNativeSourceSet
fun target(targets: Iterable<String>): KotlinNativeSourceSet
fun target(targets: Iterable<String>, configureClosure: Closure<*>): KotlinNativeSourceSet
fun target(targets: Iterable<String>, configureAction: Action<in SourceDirectorySet>): KotlinNativeSourceSet
fun target(targets: Iterable<String>, configureLambda: SourceDirectorySet.() -> Unit): KotlinNativeSourceSet
}
@@ -0,0 +1,16 @@
package org.jetbrains.kotlin.gradle.plugin.experimental.sourcesets
import org.gradle.api.NamedDomainObjectFactory
import org.gradle.api.internal.file.SourceDirectorySetFactory
import org.gradle.api.internal.project.ProjectInternal
class KotlinNativeSourceSetFactory(val project: ProjectInternal) : NamedDomainObjectFactory<KotlinNativeSourceSet> {
override fun create(name: String): KotlinNativeSourceSet =
project.objects.newInstance(
KotlinNativeSourceSetImpl::class.java,
name,
project.services.get(SourceDirectorySetFactory::class.java),
project
)
}
@@ -0,0 +1,93 @@
package org.jetbrains.kotlin.gradle.plugin.experimental.sourcesets
import groovy.lang.Closure
import org.gradle.api.Action
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.FileCollection
import org.gradle.api.file.SourceDirectorySet
import org.gradle.api.internal.file.SourceDirectorySetFactory
import org.gradle.api.internal.project.ProjectInternal
import org.gradle.util.ConfigureUtil.configure
import org.jetbrains.kotlin.gradle.plugin.experimental.internal.AbstractKotlinNativeComponent
import org.jetbrains.kotlin.konan.target.HostManager
import org.jetbrains.kotlin.konan.target.KonanTarget
import javax.inject.Inject
open class KotlinNativeSourceSetImpl @Inject constructor(
private val name: String,
val sourceDirectorySetFactory: SourceDirectorySetFactory,
val project: ProjectInternal
) : KotlinNativeSourceSet {
override lateinit var component: AbstractKotlinNativeComponent
override val kotlin: SourceDirectorySet
get() = nativeSources
internal val commonSources: ConfigurableFileCollection = project.files()
private val nativeSources: SourceDirectorySet = newSourceDirectorySet("kotlin")
private val platformSources = mutableMapOf<KonanTarget, SourceDirectorySet>()
private fun newSourceDirectorySet(name: String) = sourceDirectorySetFactory.create(name).apply {
filter.include("**/*.kt")
}
override fun getPlatformSources(target: KonanTarget) = platformSources.getOrPut(target) {
newSourceDirectorySet(target.name)
}
override fun getCommonMultiplatformSources(): FileCollection = commonSources
override fun getCommonNativeSources(): SourceDirectorySet = nativeSources
override fun getAllSources(target: KonanTarget): FileCollection =
commonSources + nativeSources + getPlatformSources(target)
override fun getName(): String = name
// region DSL
// Common source directory set configuration.
override fun kotlin(configureClosure: Closure<*>) = apply { configure(configureClosure, nativeSources) }
override fun kotlin(configureAction: Action<in SourceDirectorySet>) = apply { configureAction.execute(nativeSources) }
override fun kotlin(configureLambda: SourceDirectorySet.() -> Unit) = apply { nativeSources.configureLambda() }
// Configuration of the corresponding software component.
override fun component(configureClosure: Closure<*>) = apply { configure(configureClosure, component) }
override fun component(configureAction: Action<in AbstractKotlinNativeComponent>) =
apply { configureAction.execute(component) }
override fun component(configureLambda: AbstractKotlinNativeComponent.() -> Unit) =
apply { component.configureLambda() }
// Adding new targets and configuration of target-specific source directory sets.
override fun target(target: String): SourceDirectorySet {
val konanTarget = HostManager().targetByName(target)
component.konanTargets.add(konanTarget)
return getPlatformSources(konanTarget)
}
override fun target(targets: Iterable<String>) = target(targets) {}
override fun target(targets: Iterable<String>, configureLambda: SourceDirectorySet.() -> Unit) = apply {
targets.forEach { target(it).configureLambda() }
}
override fun target(targets: Iterable<String>, configureClosure: Closure<*>) =
target(targets) { configure(configureClosure, this) }
override fun target(targets: Iterable<String>, configureAction: Action<in SourceDirectorySet>) =
target(targets) { configureAction.execute(this) }
override fun target(vararg targets: String) = target(targets.toList())
override fun target(vararg targets: String, configureLambda: SourceDirectorySet.() -> Unit) =
target(targets.toList(), configureLambda)
override fun target(vararg targets: String, configureClosure: Closure<*>) =
target(targets.toList(), configureClosure)
override fun target(vararg targets: String, configureAction: Action<in SourceDirectorySet>) =
target(targets.toList(), configureAction)
// endregion
}
@@ -0,0 +1,75 @@
package org.jetbrains.kotlin.gradle.plugin.experimental.tasks
import org.gradle.api.DefaultTask
import org.gradle.api.artifacts.Configuration
import org.gradle.api.file.FileCollection
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputFiles
import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.TaskAction
import org.jetbrains.kotlin.gradle.plugin.KonanCompilerRunner
import org.jetbrains.kotlin.gradle.plugin.KonanPlugin
import org.jetbrains.kotlin.gradle.plugin.addArg
import org.jetbrains.kotlin.gradle.plugin.addKey
import org.jetbrains.kotlin.gradle.plugin.experimental.internal.AbstractKotlinNativeBinary
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
open class KotlinNativeCompile: DefaultTask() {
init {
super.dependsOn(KonanPlugin.KONAN_DOWNLOAD_TASK_NAME)
}
// TODO: May be replace with Gradle's property
internal lateinit var binary: AbstractKotlinNativeBinary
// Inputs and outputs
val sources: FileCollection
@InputFiles get() = binary.sources
val libraries: Configuration
@InputFiles get() = binary.klibraries
val optimized: Boolean @Input get() = binary.optimized
val debuggable: Boolean @Input get() = binary.debuggable
val kind: CompilerOutputKind @Input get() = binary.kind
val target: String @Input get() = binary.konanTarget.name
@OutputFile
val outputFile: RegularFileProperty = newOutputFile()
// Task action
@TaskAction
fun compile() {
val output = outputFile.asFile.get()
output.parentFile.mkdirs()
val args = mutableListOf<String>().apply {
addArg("-o", outputFile.get().asFile.absolutePath)
addKey("-opt", optimized)
addKey("-g", debuggable)
addKey("-ea", debuggable)
addArg("-target", target)
addArg("-p", kind.name.toLowerCase())
add("-Xmulti-platform")
addAll(binary.additionalCompilerOptions)
libraries.files.forEach {library ->
library.parent?.let { addArg("-r", it) }
addArg("-l", library.nameWithoutExtension)
}
addAll(sources.files.map { it.absolutePath })
}
KonanCompilerRunner(project).run(args)
}
}
@@ -36,7 +36,7 @@ import java.io.File
*/
abstract class KonanCompileTask: KonanBuildingTask(), KonanCompileSpec {
@Internal override val toolRunner = KonanCompilerRunner(project)
@Internal override val toolRunner = KonanCompilerRunner(project, project.konanExtension.jvmArgs)
abstract val produce: CompilerOutputKind
@Internal get
@@ -72,7 +72,7 @@ open class KonanCompilerDownloadTask : DefaultTask() {
// Download dependencies if a user said so.
if (downloadDependencies) {
val runner = KonanCompilerRunner(project)
val runner = KonanCompilerRunner(project, project.konanExtension.jvmArgs)
project.konanTargets.forEach {
runner.run("--check_dependencies", "-target", it.visibleName)
}
@@ -36,7 +36,7 @@ import java.io.File
*/
open class KonanInteropTask: KonanBuildingTask(), KonanInteropSpec {
@Internal override val toolRunner: KonanToolRunner = KonanInteropRunner(project)
@Internal override val toolRunner: KonanToolRunner = KonanInteropRunner(project, project.konanExtension.jvmArgs)
override fun init(config: KonanBuildingConfig<*>, destinationDir: File, artifactName: String, target: KonanTarget) {
super.init(config, destinationDir, artifactName, target)
@@ -0,0 +1,377 @@
package org.jetbrains.kotlin.gradle.plugin.test
import org.gradle.testkit.runner.TaskOutcome
import org.jetbrains.kotlin.konan.target.HostManager
import org.jetbrains.kotlin.konan.target.KonanTarget
import org.junit.Rule
import org.junit.rules.TemporaryFolder
import java.io.File
import java.nio.file.Paths
import java.util.concurrent.TimeUnit
import kotlin.test.*
class ExperimentalPluginTests {
val tmpFolder = TemporaryFolder()
@Rule get
val projectDirectory: File
get() = tmpFolder.root
val exeSuffix = HostManager.host.family.exeSuffix
@Test
fun `Plugin should compile one executable`() {
val project = KonanProject.create(projectDirectory).apply {
settingsFile.appendText("\nrootProject.name = 'test'")
buildFile.writeText("""
plugins { id 'kotlin-native' }
sourceSets.main.component {
outputKinds = [ EXECUTABLE, KLIBRARY ]
}
""".trimIndent())
}
val assembleResult = project.createRunner().withArguments("assemble").build()
assertEquals(TaskOutcome.SUCCESS, assembleResult.task(":compileDebugKotlinNative")?.outcome)
assertTrue(projectDirectory.resolve("build/exe/main/debug/test.$exeSuffix").exists())
}
@Test
fun `Plugin should build a klibrary and support a project dependency on it`() {
val libraryDir = tmpFolder.newFolder("library")
val libraryProject = KonanProject.createEmpty(libraryDir).apply {
buildFile.writeText("""
plugins { id 'kotlin-native' }
""".trimIndent())
generateSrcFile("library.kt", "fun foo() = 42")
}
val project = KonanProject.createEmpty(projectDirectory).apply {
settingsFile.writeText("""
include ':library'
rootProject.name = 'test'
""".trimIndent())
buildFile.writeText("""
plugins { id 'kotlin-native' }
dependencies {
implementation project(':library')
}
sourceSets.main.component {
outputKinds = [ EXECUTABLE ]
}
""".trimIndent())
generateSrcFile("main.kt", "fun main(args: Array<String>) { println(foo()) }")
}
val compileDebugResult = project.createRunner().withArguments("compileDebugKotlinNative").build()
assertEquals(TaskOutcome.SUCCESS, compileDebugResult.task(":compileDebugKotlinNative")?.outcome)
assertEquals(TaskOutcome.SUCCESS, compileDebugResult.task(":library:compileDebugKotlinNative")?.outcome)
assertTrue(projectDirectory.resolve("build/exe/main/debug/test.$exeSuffix").exists())
assertTrue(libraryDir.resolve("build/lib/main/debug/library.klib").exists())
val compileReleaseResult = project.createRunner().withArguments("compileReleaseKotlinNative").build()
assertEquals(TaskOutcome.SUCCESS, compileReleaseResult.task(":compileReleaseKotlinNative")?.outcome)
assertEquals(TaskOutcome.SUCCESS, compileReleaseResult.task(":library:compileReleaseKotlinNative")?.outcome)
assertTrue(projectDirectory.resolve("build/exe/main/release/test.$exeSuffix").exists())
assertTrue(libraryDir.resolve("build/lib/main/release/library.klib").exists())
}
@Test
fun `Plugin should be able to publish a component and support a maven dependency on it`() {
val libraryDir = tmpFolder.newFolder("library")
val libraryProject = KonanProject.createEmpty(libraryDir).apply {
buildFile.writeText("""
plugins {
id 'kotlin-native'
id 'maven-publish'
}
group 'test'
version '1.0'
sourceSets.main.component {
target 'host', 'wasm32'
}
publishing {
repositories {
maven {
url = '../repo'
}
}
}
""".trimIndent())
generateSrcFile("library.kt", "fun foo() = 42")
}
val project = KonanProject.createEmpty(projectDirectory).apply {
settingsFile.writeText("""
enableFeaturePreview('GRADLE_METADATA')
include ':library'
rootProject.name = 'test'
""".trimIndent())
buildFile.writeText("""
plugins { id 'kotlin-native' }
repositories {
maven {
url = 'repo'
}
}
dependencies {
implementation 'test:library:1.0'
}
sourceSets.main.component {
target 'host', 'wasm32'
outputKinds = [ EXECUTABLE ]
}
""".trimIndent())
generateSrcFile("main.kt", "fun main(args: Array<String>) { println(foo()) }")
}
val publishResult = project.createRunner().withArguments("library:publish").build()
assertEquals(TaskOutcome.SUCCESS, publishResult.task(":library:compileDebugWasm32KotlinNative")?.outcome)
assertEquals(TaskOutcome.SUCCESS,
publishResult.task(":library:compileDebug${HostManager.hostName.capitalize()}KotlinNative")?.outcome)
project.createRunner().withArguments(":assemble").build()
project.createRunner().withArguments(":compileDebugWasm32KotlinNative").build()
val wasm32ExeSuffix = HostManager().targetByName("wasm32").family.exeSuffix
assertTrue(projectDirectory.resolve("build/exe/main/debug/${HostManager.hostName}/test.$exeSuffix").exists())
assertTrue(projectDirectory.resolve("build/exe/main/debug/wasm32/test.$wasm32ExeSuffix").exists())
}
@Test
fun `Plugin should be able to build a component for different targets with target-specific sources`() {
val project = KonanProject.createEmpty(projectDirectory).apply {
settingsFile.appendText("\nrootProject.name = 'test'")
buildFile.writeText("""
plugins { id 'kotlin-native' }
sourceSets.main {
target('macos_x64').srcDir 'src/main/macos_x64'
target('linux_x64').srcDir 'src/main/linux_x64'
target('mingw_x64').srcDir 'src/main/mingw_x64'
component {
outputKinds = [ EXECUTABLE ]
}
}
""".trimIndent())
generateSrcFile("main.kt", """
fun main(args: Array<String>) {
print(foo())
}
expect fun foo(): String
""".trimIndent())
listOf(KonanTarget.MACOS_X64, KonanTarget.LINUX_X64, KonanTarget.MINGW_X64).forEach {
val target = it.name
generateSrcFile(Paths.get("src/main/$target"),"foo.kt", "actual fun foo() = \"$target\"")
}
}
project.createRunner().withArguments("assemble").build()
val process = ProcessBuilder(
projectDirectory.resolve("build/exe/main/debug/${HostManager.hostName}/test.$exeSuffix").absolutePath
).start()
process.waitFor(10, TimeUnit.SECONDS)
assertEquals(HostManager.hostName, process.inputStream.reader().readText())
}
@Test
fun `Plugin should support transitive project klib dependencies`() {
val fooDir = tmpFolder.newFolder("foo")
val barDir = tmpFolder.newFolder("bar")
val foo = KonanProject.createEmpty(fooDir).apply {
buildFile.writeText("""
plugins { id 'kotlin-native' }
sourceSets.main.component {
target 'host', 'wasm32'
}
dependencies {
implementation project(':bar')
}
""".trimIndent())
generateSrcFile("foo.kt", "fun foo() = bar()")
}
val bar = KonanProject.createEmpty(barDir).apply {
buildFile.writeText("""
plugins { id 'kotlin-native'}
sourceSets.main.component {
target 'host', 'wasm32'
}
""".trimIndent())
generateSrcFile("bar.kt", "fun bar() = \"Bar\"")
}
val project = KonanProject.createEmpty(projectDirectory).apply {
settingsFile.writeText("""
include ':foo'
include ':bar'
""".trimIndent())
buildFile.writeText("""
plugins { id 'kotlin-native' }
sourceSets.main.component {
target 'host', 'wasm32'
outputKinds = [ EXECUTABLE ]
}
dependencies {
implementation project('foo')
}
""".trimIndent())
generateSrcFile("main.kt", "fun main(args: Array<String>) { println(foo()) }")
}
project.createRunner().withArguments(
":assembleReleaseWasm32",
":assembleRelease${HostManager.hostName.capitalize()}"
).build()
}
@Test
fun `Project should support transitive maven klib dependencies`() {
val fooDir = tmpFolder.newFolder("foo")
val barDir = tmpFolder.newFolder("bar")
val foo = KonanProject.createEmpty(fooDir).apply {
buildFile.writeText("""
plugins {
id 'kotlin-native'
id 'maven-publish'
}
repositories {
maven {
url = '../repo'
}
}
group 'test'
version '1.0'
sourceSets.main.component {
target 'host', 'wasm32'
}
dependencies {
implementation 'test:bar:1.0'
}
publishing {
repositories {
maven {
url = '../repo'
}
}
}
""".trimIndent())
generateSrcFile("foo.kt", "fun foo() = bar()")
}
val bar = KonanProject.createEmpty(barDir).apply {
buildFile.writeText("""
plugins {
id 'kotlin-native'
id 'maven-publish'
}
group 'test'
version '1.0'
sourceSets.main.component {
target 'host', 'wasm32'
}
publishing {
repositories {
maven {
url = '../repo'
}
}
}
""".trimIndent())
generateSrcFile("bar.kt", "fun bar() = \"Bar\"")
}
val project = KonanProject.createEmpty(projectDirectory).apply {
settingsFile.writeText("""
enableFeaturePreview('GRADLE_METADATA')
include ':foo'
include ':bar'
rootProject.name = 'test'
""".trimIndent())
buildFile.writeText("""
plugins { id 'kotlin-native' }
repositories {
maven {
url = 'repo'
}
}
dependencies {
implementation 'test:foo:1.0'
}
sourceSets.main.component {
target 'host', 'wasm32'
outputKinds = [ EXECUTABLE ]
}
""".trimIndent())
generateSrcFile("main.kt", "fun main(args: Array<String>) { println(foo()) }")
}
project.createRunner().withArguments(":bar:publish").build()
project.createRunner().withArguments(":foo:publish").build()
project.createRunner().withArguments(
":assembleReleaseWasm32",
":assembleRelease${HostManager.hostName.capitalize()}"
).build()
}
@Test
@Ignore
fun `Plugin should allow creating components by creating source sets`() {
val project = KonanProject.create(projectDirectory).apply {
settingsFile.appendText("\nrootProject.name = 'test'")
buildFile.writeText("""
plugins { id 'kotlin-native' }
sourceSets {
foo {
kotlin.srcDir 'src/foo/kotlin'
component {
outputKinds = [ EXECUTABLE ]
}
}
}
""".trimIndent())
generateSrcFile(
Paths.get("src/foo/kotlin"),
"foo.kt",
"fun main(args: Array<String>) { println(\"Foo\") }"
)
}
val assembleResult = project.createRunner().withArguments("assembleFooDebug", "assembleFooRelease").build()
assertEquals(TaskOutcome.SUCCESS, assembleResult.task(":compileFooDebugKotlinNative")?.outcome)
assertEquals(TaskOutcome.SUCCESS, assembleResult.task(":compileFooReleaseKotlinNative")?.outcome)
assertTrue(projectDirectory.resolve("build/exe/foo/debug/foo.$exeSuffix").exists())
assertTrue(projectDirectory.resolve("build/exe/foo/release/foo.$exeSuffix").exists())
}
}
-125
View File
@@ -1,125 +0,0 @@
#!/bin/bash
# Release publisher. Downloads the working tree in the working directory (it must be empty),
# builds bundle and gradle-plugin, check them and upload to bintray/CDN.
# Usage: publish-release.sh [--no-upload] <version>
# --no-upload - don't upload the built binaries (gradle plugin and bundle)
# to bintray/CDN, only test them.
# The script uses the following environment variables:
# BINTRAY_USER and BINTRAY_KEY to upload gradle plugin to bintray;
# CDN_USER and CDN_PASS to upload the bundle to CDN.
function stage {
echo
echo "==================================="
date
echo "$1"
}
set -eu
if [[ $# -lt 1 ]]; then
echo "No version is set. Usage: `basename $0` [--no-upload] [--no-build] <version>"
exit 1
fi
UPLOAD=true
BUILD=true
# Prepare command line args.
while [[ $# -gt 1 ]]; do
arg="$1"
case $arg in
--no-upload)
UPLOAD=false
;;
--no-build)
BUILD=false
;;
*)
echo "Unknown option: $arg. Usage: `basename $0` [--no-upload] <version>"
exit 1
;;
esac
shift
done
# The variables used.
case "$OSTYPE" in
darwin*) OS=macos ;;
linux*) OS=linux ;;
*) echo "unknown: $OSTYPE" && exit 1;;
esac
VERSION=${1}
BRANCH="v${VERSION}-fixes"
REPO="https://github.com/JetBrains/kotlin-native.git"
TREE_DIR=${PWD}
BUNDLE_TAR="$TREE_DIR/kotlin-native-$OS-$VERSION.tar.gz"
BUNDLE_DIR="$TREE_DIR/kotlin-native-$OS-$VERSION"
WAIT_TIME=120
if [ "$BUILD" == "true" ]; then
# Build and test.
# 1. Download kotlin-native sources
stage "Cloning repo: $REPO (branch: $BRANCH)"
git clone "$REPO" .
git checkout "$BRANCH"
# 2.1 Update dependencies
stage "Building bundle: $BUNDLE_TAR"
./gradlew dependencies:update
# 2.2 Build the bundle
./gradlew clean bundle
tar -xf "$BUNDLE_TAR"
fi
# 3. Check the bundle in commandline mode
stage "Checking commandline build"
cd "$BUNDLE_DIR/samples"
./build.sh
# 4. Build the gradle plugin
stage "Building gradle plugin"
cd "$TREE_DIR"
./gradlew tools:kotlin-native-gradle-plugin:jar
# 5. Build samples in the tree with the plugin built
stage "Building samples with the plugin built"
cd "$TREE_DIR/samples"
./gradlew build --refresh-dependencies
if [ "$UPLOAD" == "true" ]; then
# 6. Upload the plugin
stage "Uploading the gradle plugin to bintray"
cd "$TREE_DIR"
export BINTRAY_USER
export BINTRAY_KEY
./gradlew :tools:kotlin-native-gradle-plugin:bintrayUpload -Poverride
sleep 10 # Wait some time to ensure that the plugin can be downloaded on the following step.
# 5. Build the bundle samples with the plugin
stage "Building samples with gradle plugin and bundle compiler"
cd "$BUNDLE_DIR/samples"
./gradlew build --refresh-dependencies
# 6. Upload the bundle to the CDN
stage "Uploading the bundle to CDN: $BUNDLE_TAR -> ftp://uploadcds.labs.intellij.net/builds/releases/$VERSION/$OS"
curl --upload-file "$BUNDLE_TAR" "ftp://$CDN_USER:$CDN_PASS@uploadcds.labs.intellij.net/builds/releases/$VERSION/$OS"
echo "Available at https://download.jetbrains.com/kotlin/native/builds/releases/$VERSION/$OS/$BUNDLE_TAR"
echo "Wait ${WAIT_TIME} seconds to ensure that the bundle can be downloaded."
sleep ${WAIT_TIME}
# 7. Remove gradle.properties and build the bundle samples with gradle plugin.
stage "Building samples with gradle plugin and downloaded compiler"
cd "$BUNDLE_DIR/samples"
rm -rf gradle.properties
./gradlew build --refresh-dependencies
fi
date
echo "Done."