diff --git a/tools/kotlin-native-gradle-plugin/build.gradle b/tools/kotlin-native-gradle-plugin/build.gradle index 8699951bbee..66f4ca51dd0 100644 --- a/tools/kotlin-native-gradle-plugin/build.gradle +++ b/tools/kotlin-native-gradle-plugin/build.gradle @@ -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' + } } } diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KonanToolRunner.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KonanToolRunner.kt index b8b33773906..7cf9a9dd56a 100644 --- a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KonanToolRunner.kt +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KonanToolRunner.kt @@ -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 +): 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 = 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 = emptyList()) + : KonanCliRunner("konanc", "Kotlin/Native compiler", project, additionalJvmArgs) + +internal class KonanKlibRunner(project: Project, additionalJvmArgs: List = emptyList()) + : KonanCliRunner("klib", "Klib management tool", project, additionalJvmArgs) diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/ComponentWithBaseName.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/ComponentWithBaseName.kt new file mode 100644 index 00000000000..42c32af99a1 --- /dev/null +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/ComponentWithBaseName.kt @@ -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 +} diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/KotlinNativeBinaries.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/KotlinNativeBinaries.kt new file mode 100644 index 00000000000..6bb6ea59032 --- /dev/null +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/KotlinNativeBinaries.kt @@ -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 + + // 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 + + 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 \ No newline at end of file diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/KotlinNativeComponent.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/KotlinNativeComponent.kt new file mode 100644 index 00000000000..d3bc6b8194b --- /dev/null +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/KotlinNativeComponent.kt @@ -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 + + /** Returns the binaries for this library. */ + override fun getBinaries(): BinaryCollection + + /** 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 +} \ No newline at end of file diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/AbstractKotlinNativeBinary.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/AbstractKotlinNativeBinary.kt new file mode 100644 index 00000000000..76a43b4a9ba --- /dev/null +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/AbstractKotlinNativeBinary.kt @@ -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, + 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::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 = baseName + + override val compileTask: Property = 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() +} \ No newline at end of file diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/AbstractKotlinNativeComponent.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/AbstractKotlinNativeComponent.kt new file mode 100644 index 00000000000..d5ea4aa5f7d --- /dev/null +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/AbstractKotlinNativeComponent.kt @@ -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 = objectFactory.property(String::class.java).apply { set(name) } + fun getBaseName(): Property = baseName + + override val konanTargets: LockableSetProperty = + 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 + override fun getBinaries(): DefaultBinaryCollection = 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 +} \ No newline at end of file diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/KotlinNativeBuildType.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/KotlinNativeBuildType.kt new file mode 100644 index 00000000000..c646d748edd --- /dev/null +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/KotlinNativeBuildType.kt @@ -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 = Arrays.asList(DEBUG, RELEASE) + } +} \ No newline at end of file diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/KotlinNativeExecutableImpl.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/KotlinNativeExecutableImpl.kt new file mode 100644 index 00000000000..d248473f12a --- /dev/null +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/KotlinNativeExecutableImpl.kt @@ -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, + 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 = 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 = runtimeElementsProperty.get().let { + setOf(DefaultUsageContext(identity.runtimeUsageContext, it.allArtifacts, it)) + } + + override val outputRootName = "exe" +} diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/KotlinNativeKLibraryImpl.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/KotlinNativeKLibraryImpl.kt new file mode 100644 index 00000000000..fb57d319c85 --- /dev/null +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/KotlinNativeKLibraryImpl.kt @@ -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, + 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 = objects.property(Configuration::class.java) + private val linkFileProperty: RegularFileProperty = projectLayout.fileProperty() + + // Interface + + override fun getLinkElements() = linkElementsProperty + override fun getLinkFile() = linkFileProperty + + override fun getUsages(): Set = 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" +} diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/KotlinNativeMainComponent.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/KotlinNativeMainComponent.kt new file mode 100644 index 00000000000..0992b94b084 --- /dev/null +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/KotlinNativeMainComponent.kt @@ -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 = + objectFactory.property(KotlinNativeBinary::class.java) + + override fun getDevelopmentBinary() = developmentBinaryProperty + + private fun addBinary(type: Class, identity: NativeVariantIdentity): T = + objectFactory.newInstance( + type, + "$name${identity.name.capitalize()}", + baseName, + getImplementationDependencies(), + sources, + identity + ).apply { + binaries.add(this) + } + + private inline fun addBinary(identity: NativeVariantIdentity): T = + addBinary(T::class.java, identity) + + fun addExecutable(identity: NativeVariantIdentity) = addBinary(identity) + fun addKLibrary(identity: NativeVariantIdentity) = addBinary(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() + override fun getVariants() = variants + + override fun getName(): String = this@KotlinNativeMainComponent.name + + override fun getUsages(): Set = emptySet() + + } + // endregion + + companion object { + @JvmStatic + val EXECUTABLE = OutputKind.EXECUTABLE + + @JvmStatic + val KLIBRARY = OutputKind.KLIBRARY + } +} diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/KotlinNativePlatform.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/KotlinNativePlatform.kt new file mode 100644 index 00000000000..42034490cc6 --- /dev/null +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/KotlinNativePlatform.kt @@ -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() +} \ No newline at end of file diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/KotlinNativeTestExecutableImpl.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/KotlinNativeTestExecutableImpl.kt new file mode 100644 index 00000000000..679440f01a3 --- /dev/null +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/KotlinNativeTestExecutableImpl.kt @@ -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, + 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 = objects.property(Task::class.java) + override fun getRunTask() = runTaskProperty + + override val sources: FileCollection + get() = super.sources + mainSources.getAllSources(konanTarget) + + override val outputRootName: String = "exe" +} \ No newline at end of file diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/KotlinNativeTestSuite.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/KotlinNativeTestSuite.kt new file mode 100644 index 00000000000..e766aae7551 --- /dev/null +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/KotlinNativeTestSuite.kt @@ -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 = + 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) + } +} diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/KotlinNativeUsage.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/KotlinNativeUsage.kt new file mode 100644 index 00000000000..48248b3a5d5 --- /dev/null +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/KotlinNativeUsage.kt @@ -0,0 +1,5 @@ +package org.jetbrains.kotlin.gradle.plugin.experimental.internal + +object KotlinNativeUsage { + const val KLIB = "kotlin-native-klib" +} diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/KotlinNativeVariantIdentity.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/KotlinNativeVariantIdentity.kt new file mode 100644 index 00000000000..9ccad52fa0b --- /dev/null +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/KotlinNativeVariantIdentity.kt @@ -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, + group: Provider, + version: Provider, + 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) +} \ No newline at end of file diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/OutputKind.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/OutputKind.kt new file mode 100644 index 00000000000..01688420c23 --- /dev/null +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/internal/OutputKind.kt @@ -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, + 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.getDevelopmentKind() = maxBy { it.developmentBinaryPriority } + } +} \ No newline at end of file diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/plugins/KotlinNativeBasePlugin.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/plugins/KotlinNativeBasePlugin.kt new file mode 100644 index 00000000000..c34445d4587 --- /dev/null +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/plugins/KotlinNativeBasePlugin.kt @@ -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 { + + 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" + } + +} diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/plugins/KotlinNativePlugin.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/plugins/KotlinNativePlugin.kt new file mode 100644 index 00000000000..74f89616cda --- /dev/null +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/plugins/KotlinNativePlugin.kt @@ -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 { + + 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 { + 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 { + outputKinds.lockNow() + return outputKinds.get().also { + require(it.isNotEmpty()) { "An output kind needs to be specified for the component." } + } + } + + private fun Collection.getDevelopmentTarget(): KonanTarget = + if (contains(HostManager.host)) HostManager.host else first() + + private fun Project.addBinariesForMainComponents(group: Provider, version: Provider) { + 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, version: Provider) { + 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 + + // 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) + } + } +} diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/plugins/KotlinPlatformNativePlugin.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/plugins/KotlinPlatformNativePlugin.kt new file mode 100644 index 00000000000..756adbe1445 --- /dev/null +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/plugins/KotlinPlatformNativePlugin.kt @@ -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()) + } + } +} diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/plugins/ProjectExtensions.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/plugins/ProjectExtensions.kt new file mode 100644 index 00000000000..dc8327dec5d --- /dev/null +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/plugins/ProjectExtensions.kt @@ -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 + get() = extensions.getByName(KotlinNativeBasePlugin.SOURCE_SETS_EXTENSION) + as NamedDomainObjectContainer diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/sourcesets/KotlinNativeSourceSet.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/sourcesets/KotlinNativeSourceSet.kt new file mode 100644 index 00000000000..0f2f9f2bac5 --- /dev/null +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/sourcesets/KotlinNativeSourceSet.kt @@ -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): KotlinNativeSourceSet + fun kotlin(configureLambda: SourceDirectorySet.() -> Unit): KotlinNativeSourceSet + + fun component(configureClosure: Closure<*>): KotlinNativeSourceSet + fun component(configureAction: Action): 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): KotlinNativeSourceSet + fun target(vararg targets: String, configureLambda: SourceDirectorySet.() -> Unit): KotlinNativeSourceSet + fun target(targets: Iterable): KotlinNativeSourceSet + fun target(targets: Iterable, configureClosure: Closure<*>): KotlinNativeSourceSet + fun target(targets: Iterable, configureAction: Action): KotlinNativeSourceSet + fun target(targets: Iterable, configureLambda: SourceDirectorySet.() -> Unit): KotlinNativeSourceSet +} diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/sourcesets/KotlinNativeSourceSetFactory.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/sourcesets/KotlinNativeSourceSetFactory.kt new file mode 100644 index 00000000000..aa1738ec10d --- /dev/null +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/sourcesets/KotlinNativeSourceSetFactory.kt @@ -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 { + + override fun create(name: String): KotlinNativeSourceSet = + project.objects.newInstance( + KotlinNativeSourceSetImpl::class.java, + name, + project.services.get(SourceDirectorySetFactory::class.java), + project + ) +} diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/sourcesets/KotlinNativeSourceSetImpl.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/sourcesets/KotlinNativeSourceSetImpl.kt new file mode 100644 index 00000000000..61fba62c4b9 --- /dev/null +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/sourcesets/KotlinNativeSourceSetImpl.kt @@ -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() + + 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) = 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) = + 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) = target(targets) {} + + override fun target(targets: Iterable, configureLambda: SourceDirectorySet.() -> Unit) = apply { + targets.forEach { target(it).configureLambda() } + } + + override fun target(targets: Iterable, configureClosure: Closure<*>) = + target(targets) { configure(configureClosure, this) } + + override fun target(targets: Iterable, configureAction: Action) = + 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) = + target(targets.toList(), configureAction) + // endregion +} \ No newline at end of file diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/tasks/KotlinNativeCompile.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/tasks/KotlinNativeCompile.kt new file mode 100644 index 00000000000..e296ab424c8 --- /dev/null +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/experimental/tasks/KotlinNativeCompile.kt @@ -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().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) + } +} diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/tasks/KonanCompileTask.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/tasks/KonanCompileTask.kt index b746c244f7b..4287df02b05 100644 --- a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/tasks/KonanCompileTask.kt +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/tasks/KonanCompileTask.kt @@ -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 diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/tasks/KonanCompilerDownloadTask.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/tasks/KonanCompilerDownloadTask.kt index da3e095002b..a66a13662e2 100644 --- a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/tasks/KonanCompilerDownloadTask.kt +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/tasks/KonanCompilerDownloadTask.kt @@ -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) } diff --git a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/tasks/KonanInteropTask.kt b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/tasks/KonanInteropTask.kt index f725413af99..e891c52d830 100644 --- a/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/tasks/KonanInteropTask.kt +++ b/tools/kotlin-native-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/tasks/KonanInteropTask.kt @@ -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) diff --git a/tools/kotlin-native-gradle-plugin/src/test/kotlin/ExperimentalPluginTests.kt b/tools/kotlin-native-gradle-plugin/src/test/kotlin/ExperimentalPluginTests.kt new file mode 100644 index 00000000000..42c3caff14c --- /dev/null +++ b/tools/kotlin-native-gradle-plugin/src/test/kotlin/ExperimentalPluginTests.kt @@ -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) { 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) { 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) { + 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) { 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) { 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) { 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()) + } + +} \ No newline at end of file diff --git a/tools/publish-release.sh b/tools/publish-release.sh deleted file mode 100755 index 65be73b3f70..00000000000 --- a/tools/publish-release.sh +++ /dev/null @@ -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] -# --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] " - 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] " - 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."