/* * Copyright 2010-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import org.jetbrains.kotlin.konan.target.* import org.jetbrains.kotlin.konan.util.* import org.jetbrains.kotlin.CopySamples import org.jetbrains.kotlin.PlatformInfo import org.jetbrains.kotlin.cpp.CompilationDatabasePlugin import org.jetbrains.kotlin.cpp.CppUsage import org.jetbrains.kotlin.cpp.GitClangFormatPlugin import org.jetbrains.kotlin.CompareDistributionSignatures import org.jetbrains.kotlin.xcode.XcodeOverridePlugin import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar import org.apache.tools.ant.filters.ReplaceTokens import org.jetbrains.kotlin.UtilsKt import java.util.concurrent.ConcurrentHashMap import plugins.KotlinBuildPublishingPluginKt import java.security.MessageDigest buildscript { apply from: "gradle/kotlinGradlePlugin.gradle" repositories { mavenCentral() gradlePluginPortal() } dependencies { classpath 'com.github.johnrengelman:shadow:8.1.1' } } defaultTasks 'clean', 'dist' apply plugin: XcodeOverridePlugin if (PlatformInfo.isMac()) { PlatformInfo.checkXcodeVersion(project) } apply plugin: "kotlin.native.build-tools-conventions" apply plugin: "platform-manager" allprojects { repositories { mavenCentral() maven { url project.bootstrapKotlinRepo } } } def platformManager = project.extensions.platformManager def distDir = UtilsKt.getKotlinNativeDist(project) def hostName = PlatformInfo.hostName def targetList = EnabledTargetsKt.enabledTargets(platformManager).collect { it.visibleName } as List def cacheableTargetNames = platformManager.hostPlatform.cacheableTargets configurations { ftpAntTask distPack commonSources runtimeBitcode { canBeConsumed = false canBeResolved = true attributes { attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage, CppUsage.LLVM_BITCODE)) } } objcExportApi { canBeConsumed = false canBeResolved = true attributes { attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage, CppUsage.API)) } } embeddableJar { canBeConsumed = false canBeResolved = true } nativeLibs { canBeConsumed = false canBeResolved = true } trove4j_jar { canBeConsumed = false canBeResolved = true } } apply plugin: CompilationDatabasePlugin dependencies { ftpAntTask 'org.apache.ant:ant-commons-net:1.9.9' distPack project(':kotlin-native:Interop:Skia') distPack project(path: ':kotlin-native:utilities:cli-runner', configuration: 'runtimeElements') commonSources project(path: ':kotlin-stdlib', configuration: 'metadataSourcesElements') commonSources project(path: ':kotlin-test', configuration: 'metadataSourcesElements') compilationDatabase project(":kotlin-native:common") compilationDatabase project(":kotlin-native:runtime") runtimeBitcode project(":kotlin-native:runtime") objcExportApi project(":kotlin-native:runtime") embeddableJar project(path: ':kotlin-native:prepare:kotlin-native-compiler-embeddable', configuration: 'runtimeElements') nativeLibs project(path: ':kotlin-native:common', configuration: 'nativeLibs') nativeLibs project(path: ':kotlin-native:llvmInterop', configuration: 'nativeLibs') use(RepoDependencies) { trove4j_jar commonDependency("org.jetbrains.intellij.deps:trove4j") } } apply plugin: GitClangFormatPlugin apply plugin: 'maven-publish' apply plugin: BasePlugin tasks.register("dist_compiler") { dependsOn("distCompiler") } tasks.register("dist_runtime") { dependsOn("distRuntime") } tasks.register("cross_dist") { dependsOn("crossDist") } tasks.register("list_dist") { dependsOn("listDist") } tasks.named("build") { dependsOn 'dist', 'distPlatformLibs' } tasks.register("distNativeSources", Zip) { dependsOn(configurations.commonSources) duplicatesStrategy = DuplicatesStrategy.FAIL destinationDirectory = file("$distDir/sources") archiveFileName = "kotlin-stdlib-native-sources.zip" includeEmptyDirs = false include('**/*.kt') from { configurations.commonSources .collect { zipTree(it) } .inject { t1, t2 -> t1 + t2 } } into('nativeWasmMain') { from(project(":kotlin-stdlib").file('native-wasm/src/')) } into('nativeMain') { from(project(':kotlin-native:runtime').file('src/main/kotlin')) from(project(':kotlin-native:Interop:Runtime').file('src/main/kotlin')) from(project(':kotlin-native:Interop:Runtime').file('src/native/kotlin')) from(project(':kotlin-native:Interop:JsRuntime').file('src/main/kotlin')) { into('kotlinx/wasm/jsinterop') } } } tasks.register("distSources") { dependsOn(distNativeSources) } tasks.register("shadowJar", ShadowJar) { mergeServiceFiles() destinationDirectory.set(file("$distDir/konan/lib")) archiveBaseName.set("kotlin-native") archiveVersion.set("") archiveClassifier.set("") // Exclude trove4j because of license agreement. exclude "*trove4j*" exclude("META-INF/versions/9/module-info.class") configurations = [project.configurations.distPack] outputs.upToDateWhen { archiveFile.getOrNull()?.asFile?.exists() ?: false } } tasks.register("distCompiler", Copy) { // Workaround: make distCompiler no-op if we are using custom dist: // the dist is already in place and has the compiler, so we don't have to // build and copy the compiler to dist. // Moreover, if we do copy it, it might overwrite the compiler files already loaded // by this Gradle process (including the jar loaded to the custom classloader), // causing hard-to-debug errors. if (!UtilsKt.isDefaultNativeHome(project)) { enabled = false } else { dependsOn ':kotlin-native:shadowJar' dependsOn configurations.embeddableJar } destinationDir distDir from(configurations.nativeLibs) { into('konan/nativelib') } from(configurations.trove4j_jar) { include "trove4j*.jar" rename "trove4j(.*).jar", "trove4j.jar" into("konan/lib") } from(configurations.embeddableJar) { rename { "kotlin-native-compiler-embeddable.jar" } into("konan/lib") } from(project(':kotlin-native:Interop').file('Indexer/build/nativelibs')) { into('konan/nativelib') } from(project(':kotlin-native:Interop').file('Runtime/build/nativelibs')) { into('konan/nativelib') } from(project(':kotlin-native:libllvmext').file('build/libs/llvmext/shared')) { into('konan/nativelib') } from(project(':kotlin-native:llvmDebugInfoC').file('build/libs/debugInfo/shared')) { into('konan/nativelib') } from(project(':kotlin-native:llvmDebugInfoC').file('src/scripts/konan_lldb.py')) { into('tools') } from(project(':kotlin-native:utilities').file('env_blacklist')) { into('tools') } from(file('cmd')) { fileMode(0755) into('bin') if (!PlatformInfo.isWindows()) { exclude('**/*.bat') } } from(project.file('konan')) { into('konan') exclude('**/*.properties') } from(configurations.objcExportApi) { into('konan/swift_export/kotlin_runtime') } from(project.file('konan')) { into('konan') include('**/*.properties') filter(ReplaceTokens, tokens: [compilerVersion: kotlinVersion]) // Set LLVM variant that will be used by default in the distribution. // `user` - smaller, includes only necessary components. // `dev` - bigger, includes (almost) every possible component. Use this one, // if your feature requires component that is not a part of user distribution (yet). // Note: by default konan.properties use `dev` variant because it is needed for building our LLVM dynamic library. String llvmVariant = 'user' filter(ReplaceTokens, beginToken: '$', endToken: '', tokens: [("llvm.${hostName}.dev".toString()): "\$llvm.${hostName}.${llvmVariant}".toString()] ) } delete('konan/experimentalTargetsEnabled') } tasks.register("distDef", Copy) { destinationDir project.file("$distDir/konan/platformDef/") platformManager.targetValues.each { target -> from(project(":kotlin-native:platformLibs").file("src/platform/${target.family.name().toLowerCase()}")) { into target.visibleName include '**/*.def' } } } tasks.register("listDist", Exec) { commandLine 'find', distDir } tasks.register("distRuntime", Copy) { dependsOn "${hostName}CrossDistRuntime" } tasks.register("distStdlibCache") { if (hostName in cacheableTargetNames) { dependsOn("${hostName}StdlibCache") } } def stdlib = 'klib/common/stdlib' def stdlibDefaultComponent = "$stdlib/default" tasks.register("distStdlib", Copy) { from(project(":kotlin-native:runtime") .tasks .named("nativeStdlib") .map { it.outputs.files } ) destinationDir project.file("$distDir/$stdlib") } tasks.register("crossDistRuntime") { dependsOn.addAll(targetList.collect { "${it}CrossDistRuntime" }) } tasks.register("crossDistPlatformLibs") { dependsOn.addAll(targetList.collect { "${it}PlatformLibs" }) } tasks.register("crossDistStdlibCache") { dependsOn.addAll(targetList.findAll { it in cacheableTargetNames }.collect { "${it}StdlibCache" }) } targetList.each { target -> tasks.register("${target}CrossDistBitcodeCopy", Copy) { def bitcodeFiles = configurations.runtimeBitcode.incoming.artifactView { attributes { attribute(TargetWithSanitizer.TARGET_ATTRIBUTE, new TargetWithSanitizer(platformManager.targetByName(target), null)) } }.files destinationDir project.file("$distDir/konan/targets/$target/native") from(bitcodeFiles) { include("*.bc") } } tasks.register("${target}CrossDistRuntime", Copy) { dependsOn ":kotlin-native:distStdlib" dependsOn "${target}CrossDistBitcodeCopy" destinationDir project.file("$distDir/$stdlibDefaultComponent") } tasks.register("${target}PlatformLibs") { dependsOn ":kotlin-native:platformLibs:${target}Install" if (target in cacheableTargetNames) { dependsOn(":kotlin-native:platformLibs:${target}Cache") } } if (target in cacheableTargetNames) { tasks.register("${target}StdlibCache", Copy) { dependsOn "distStdlib" dependsOn ":kotlin-native:runtime:${target}StdlibCache" destinationDir project.file("$distDir/klib/cache/") from(project(":kotlin-native:runtime").layout.buildDirectory.dir("cache/$target")) { include('**') } } } tasks.register("${target}CrossDist") { dependsOn "${target}CrossDistRuntime", "distCompiler" if (target in cacheableTargetNames) { dependsOn "${target}StdlibCache" } } } tasks.register("distPlatformLibs") { dependsOn("${hostName}PlatformLibs") } tasks.register("dist") { dependsOn "distCompiler", "distRuntime", "distDef", "distStdlib", "distStdlibCache" } tasks.register("crossDist") { dependsOn "distCompiler", "crossDistRuntime", "distDef", "distStdlib", "crossDistStdlibCache" } tasks.register("bundle") { dependsOn 'bundleRegular', 'bundlePrebuilt' } def sbomBundleRegular = SbomKt.configureSbom(project, "BundleRegular", "Kotlin/Native bundle", [].toSet(), null) def sbomBundleRegularForPublish = tasks.register("sbomBundleRegularForPublish", Copy) { dependsOn(sbomBundleRegular) destinationDir = file("$buildDir/spdx/regular") from(sbomBundleRegular) { rename(".*", "kotlin-native-${HostManager.platformName()}-${kotlinVersion}.spdx.json") } } tasks.register("bundleRegular", (PlatformInfo.isWindows()) ? Zip : Tar) { dependsOn(sbomBundleRegularForPublish) def simpleOsName = HostManager.platformName() archiveBaseName.set("kotlin-native-$simpleOsName") archiveVersion.set(kotlinVersion) from(UtilsKt.getKotlinNativeDist(project)) { include '**' exclude 'dependencies' exclude 'klib/testLibrary' // Don't include platform libraries into the bundle (generate them at the user side instead). exclude 'klib/platform' // Exclude platform libraries caches too. Keep caches for stdlib. exclude 'klib/cache/*/org.jetbrains.kotlin.native.platform.*/**' into "${archiveBaseName.get()}-${archiveVersion.get()}" } } def sbomBundlePrebuilt = SbomKt.configureSbom(project, "BundlePrebuilt", "Kotlin/Native bundle (prebuilt platform libs)", [].toSet(), null) def sbomBundlePrebuiltForPublish = tasks.register("sbomBundlePrebuiltForPublish", Copy) { dependsOn(sbomBundlePrebuilt) destinationDir = file("$buildDir/spdx/prebuilt") from(sbomBundlePrebuilt) { rename(".*", "kotlin-native-prebuilt-${HostManager.platformName()}-${kotlinVersion}.spdx.json") } } tasks.register("bundlePrebuilt", (PlatformInfo.isWindows()) ? Zip : Tar) { dependsOn(sbomBundlePrebuiltForPublish) dependsOn("crossDistPlatformLibs") def simpleOsName = HostManager.platformName() archiveBaseName.set("kotlin-native-prebuilt-$simpleOsName") archiveVersion.set(kotlinVersion) from(UtilsKt.getKotlinNativeDist(project)) { include '**' exclude 'dependencies' exclude 'klib/testLibrary' into "${archiveBaseName.get()}-${archiveVersion.get()}" } } void configurePackingLicensesToBundle(AbstractArchiveTask task, boolean containsPlatformLibraries) { task.from(project.projectDir) { include 'licenses/**' if (!containsPlatformLibraries || !PlatformInfo.isMac()) { exclude '**/xcode_license.pdf' } if (!containsPlatformLibraries) { exclude '**/mingw-w64-headers_LICENSE.txt' } into "${task.archiveBaseName.get()}-${task.archiveVersion.get()}" } task.from(project.rootProject.file("license")) { into "${task.archiveBaseName.get()}-${task.archiveVersion.get()}/licenses" } } tasks.named("bundleRegular").configure { configurePackingLicensesToBundle(it, /* containsPlatformLibraries = */ false) } tasks.named("bundlePrebuilt").configure { configurePackingLicensesToBundle(it, /* containsPlatformLibraries = */ true) } configure([bundleRegular, bundlePrebuilt]) { dependsOn("crossDist") dependsOn("crossDistStdlibCache") dependsOn("distSources") dependsOn("distDef") destinationDirectory.set(file('.')) if (PlatformInfo.isWindows()) { zip64 true } else { archiveExtension.set('tar.gz') compression = Compression.GZIP } // Calculating SHA-256 checksums for bundle artifacts def archiveExtension = PlatformInfo.isWindows() ? 'zip' : 'tar.gz' def checksumFile = archiveBaseName.zip(archiveVersion) { name, version -> file("${buildDir}/${name}-${version}.${archiveExtension}.sha256") } outputs.file(checksumFile).withPropertyName("checksumFile") doLast { File bundleFile = archiveFile.get().asFile if (bundleFile.exists()) { String checksum = calculateChecksum(bundleFile, "SHA-256") checksumFile.get().write(checksum) } } } def calculateChecksum(File file, String algorithm) { MessageDigest digest = MessageDigest.getInstance(algorithm) FileInputStream fis = new FileInputStream(file) byte[] byteArray = new byte[1024] int bytesCount while ((bytesCount = fis.read(byteArray)) != -1) { digest.update(byteArray, 0, bytesCount) } fis.close() byte[] bytes = digest.digest() StringBuilder sb = new StringBuilder() for (byte aByte : bytes) { sb.append(Integer.toString((aByte & 0xff) + 0x100, 16).substring(1)) } return sb.toString() } tasks.register("tc-dist", (PlatformInfo.isWindows()) ? Zip : Tar) { dependsOn('dist') dependsOn('distSources') def simpleOsName = HostManager.platformName() archiveBaseName.set("kotlin-native-dist-$simpleOsName") archiveVersion.set(kotlinVersion) from(UtilsKt.getKotlinNativeDist(project)) { include '**' exclude 'dependencies' into "${archiveBaseName.get()}-${archiveVersion.get()}" } destinationDirectory.set(file('.')) if (PlatformInfo.isWindows()) { zip64 true } else { archiveExtension.set('tar.gz') compression = Compression.GZIP } } tasks.register("samples") { dependsOn 'samplesZip', 'samplesTar' } tasks.register("samplesZip", Zip) tasks.register("samplesTar", Tar) { archiveExtension = 'tar.gz' compression = Compression.GZIP } configure([samplesZip, samplesTar]) { archiveBaseName = "kotlin-native-samples-$kotlinVersion" destinationDirectory = projectDir into(archiveBaseName) from(file('samples')) { // Process properties files separately. exclude '**/gradle.properties' } from(project.projectDir) { include 'licenses/**' } from(file('samples')) { include '**/gradle.properties' filter { it.startsWith('org.jetbrains.kotlin.native.home=') || it.startsWith('# Use custom Kotlin/Native home:') ? null : it } filter(org.apache.tools.ant.filters.FixCrLfFilter, "eol": org.apache.tools.ant.filters.FixCrLfFilter.CrLf.newInstance("lf")) } // Exclude build artifacts. exclude '**/build' exclude '**/.gradle' exclude '**/.idea' exclude '**/*.kt.bc-build/' } tasks.register("copy_samples") { dependsOn 'copySamples' } tasks.register("copySamples", CopySamples) { destinationDir file('build/samples-under-test') } compilationDatabase { allTargets {} } // TODO: Replace with a more convenient user-facing task that can build for a specific target. // like compilationDatabase with optional argument --target. tasks.register("compdb", Copy) { from compilationDatabase.hostTarget.task into layout.projectDirectory group = compilationDatabase.TASK_GROUP description = "Copy host compilation database to kotlin-native/" } targetList.each { targetName -> tasks.register("${targetName}CheckPlatformAbiCompatibility", CompareDistributionSignatures) { dependsOn "${targetName}PlatformLibs" libraries = new CompareDistributionSignatures.Libraries.Platform(targetName) if (project.hasProperty("anotherDistro")) { oldDistribution = project.findProperty("anotherDistro") } onMismatchMode = CompareDistributionSignatures.OnMismatchMode.FAIL } } tasks.register("checkStdlibAbiCompatibility", CompareDistributionSignatures) { dependsOn "distRuntime" libraries = CompareDistributionSignatures.Libraries.Standard.INSTANCE if (project.hasProperty("anotherDistro")) { oldDistribution = project.findProperty("anotherDistro") } onMismatchMode = CompareDistributionSignatures.OnMismatchMode.FAIL } // FIXME: should be a part of Host/TargetManager String platformName(KonanTarget target) { def result switch (target) { case KonanTarget.LINUX_X64: result ="linux-x86_64" break case KonanTarget.MACOS_X64: result = "macos-x86_64" break case KonanTarget.MACOS_ARM64: result = "macos-aarch64" break case KonanTarget.MINGW_X64: result = "windows-x86_64" break default: throw TargetSupportException("Unknown host target") } return result } Map createConfigurations(List bundles) { def hostTargets = platformManager.enabledByHost.keySet() def result = hostTargets.collectEntries { target -> [ (target): bundles.find { it.name.contains(platformName(target)) }] } result.retainAll { it.value != null } def missingBundles = hostTargets - result.keySet() if (!missingBundles.isEmpty()) { println("Some of the archive bundles are missing for targets $missingBundles:") println(result) throw new IllegalArgumentException("Bundle archives are missing for $missingBundles") } result.each { target, file -> if (!file.name.contains(kotlinVersion)) { throw new IllegalArgumentException("Incorrect version specified for the publish: ${file.name}") } } return result } def bundlesLocationFiles = UtilsKt.getNativeBundlesLocation(project) .listFiles() .toList() KotlinBuildPublishingPluginKt.configureDefaultPublishing( /* receiver = */ project, /* signingRequired = */ KotlinBuildPublishingPluginKt.getSignLibraryPublication(project) ) tasks.named("clean", Delete) { dependsOn subprojects.collect { it.tasks.matching { it.name == "clean" } } doFirst { delete distDir delete layout.buildDirectory delete bundle.outputs.files delete "${projectDir}/compile_commands.json" } } publishing { publications { def publishBundlesFromLocation = UtilsKt.getNativeBundlesLocation(project) != project.projectDir register("Bundle", MavenPublication) { mvn -> mvn.groupId = project.group.toString() mvn.artifactId = project.name mvn.version = kotlinVersion if (publishBundlesFromLocation) { def bundleArchives = bundlesLocationFiles .findAll { it.name.startsWith("kotlin-native") && !it.name.contains("prebuilt") && (it.name.endsWith("zip") || it.name.endsWith("tar.gz")) } def bundleConfigs = createConfigurations(bundleArchives) bundleConfigs.forEach { target, file -> def archiveExtension = (target.family == Family.MINGW) ? 'zip' : 'tar.gz' mvn.artifact(file) { classifier = platformName(target) extension = archiveExtension } mvn.artifact("${UtilsKt.getNativeBundlesLocation(project)}/kotlin-native-${platformName(target)}-${kotlinVersion}.spdx.json") { classifier = platformName(target) extension = "spdx.json" } } } else { mvn.artifact(bundleRegular) { classifier = HostManager.platformName() extension = (PlatformInfo.isWindows()) ? 'zip' : 'tar.gz' } mvn.artifact(sbomBundleRegular) { classifier = HostManager.platformName() extension = "spdx.json" } } KotlinBuildPublishingPluginKt.configureKotlinPomAttributes( /* receiver = */ mvn, /* project = */ project, /* explicitDescription = */ "Kotlin/Native bundle", /* packaging = */ "pom" ) } register("BundlePrebuilt", MavenPublication) { mvn -> mvn.groupId = project.group.toString() mvn.artifactId = project.name + "-prebuilt" mvn.version = kotlinVersion if (publishBundlesFromLocation) { def prebuiltBundleArchives = bundlesLocationFiles .findAll { it.name.startsWith("kotlin-native-prebuilt") && (it.name.endsWith("zip") || it.name.endsWith("tar.gz")) } def bundlePrebuiltConfigs = createConfigurations(prebuiltBundleArchives) bundlePrebuiltConfigs.forEach { target, file -> def archiveExtension = (target.family == Family.MINGW) ? 'zip' : 'tar.gz' mvn.artifact(file) { classifier = platformName(target) extension = archiveExtension } mvn.artifact("${UtilsKt.getNativeBundlesLocation(project)}/kotlin-native-prebuilt-${platformName(target)}-${kotlinVersion}.spdx.json") { classifier = platformName(target) extension = "spdx.json" } } } else { mvn.artifact(bundlePrebuilt) { classifier = HostManager.platformName() extension = (PlatformInfo.isWindows()) ? 'zip' : 'tar.gz' } mvn.artifact(sbomBundlePrebuilt) { classifier = HostManager.platformName() extension = "spdx.json" } } KotlinBuildPublishingPluginKt.configureKotlinPomAttributes( /* receiver = */ mvn, /* project = */ project, /* explicitDescription = */ "Kotlin/Native bundle (prebuilt platform libs)", /* packaging = */ "pom" ) } } }