[repo] Move buildSrc into repo/gradle-build-conventions/buildsrc-compat project

Include new project as build logic included build. Such change will
allow us to start migration into build convention plugins by splitting
buildSrc logic into subprojects.
This commit is contained in:
Yahor Berdnikau
2023-06-23 19:48:53 +02:00
committed by Space Team
parent 4a598afc36
commit 5b5dae9249
53 changed files with 36 additions and 33 deletions
@@ -26,7 +26,6 @@ class CodeConformanceTest : TestCase() {
".idea",
"build/js",
"build/tmp",
"buildSrc",
"compiler/build",
"compiler/fir/lightTree/testData",
"compiler/testData/psi/kdoc",
@@ -80,8 +79,6 @@ class CodeConformanceTest : TestCase() {
File("."),
listOf(
"build",
"buildSrc/build/generated-sources",
"buildSrc/prepare-deps/build",
"compiler/ir/serialization.js/build/fullRuntime",
"compiler/ir/serialization.js/build/reducedRuntime/src/libraries/stdlib/js-ir/runtime/longjs.kt",
"dependencies",
@@ -108,6 +105,7 @@ class CodeConformanceTest : TestCase() {
"libraries/stdlib/js-v1/.gradle",
"libraries/stdlib/js-v1/build",
"libraries/stdlib/js-v1/node_modules",
"libraries/stdlib/jvm/build",
"libraries/stdlib/jvm-minimal-for-test/build",
"libraries/stdlib/wasm/build",
"libraries/tools/atomicfu/build",
@@ -142,6 +140,7 @@ class CodeConformanceTest : TestCase() {
"repo/gradle-settings-conventions/jvm-toolchain-provisioning/build/generated-sources",
"repo/gradle-settings-conventions/gradle-enterprise/build/generated-sources",
"repo/gradle-settings-conventions/kotlin-daemon-config/build/generated-sources",
"repo/gradle-build-conventions/buildsrc-compat/build/generated-sources",
".gradle/expanded",
)
)
+7
View File
@@ -0,0 +1,7 @@
## Description
Provides common build convention plugins for the repo.
### List of plugins
- "buildsrc-compat" — migrated 'buildSrc' project. Will exist until it will be separated into different convention plugins.
@@ -0,0 +1,149 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
buildscript {
// workaround for KGP build metrics reports: https://github.com/gradle/gradle/issues/20001
project.extensions.extraProperties["kotlin.build.report.output"] = null
val versionPropertiesFile = project.rootProject.projectDir.parentFile.resolve("../gradle/versions.properties")
val versionProperties = java.util.Properties()
versionPropertiesFile.inputStream().use { propInput ->
versionProperties.load(propInput)
}
configurations.all {
resolutionStrategy.eachDependency {
if (requested.group == "com.google.code.gson" && requested.name == "gson") {
useVersion(versionProperties["versions.gson"] as String)
because("Force using same gson version because of https://github.com/google/gson/pull/1991")
}
}
}
}
logger.info("buildSrcKotlinVersion: " + extra["bootstrapKotlinVersion"])
logger.info("buildSrc kotlin compiler version: " + org.jetbrains.kotlin.config.KotlinCompilerVersion.VERSION)
logger.info("buildSrc stdlib version: " + KotlinVersion.CURRENT)
apply {
from("../../../gradle/checkCacheability.gradle.kts")
}
plugins {
`kotlin-dsl`
`java-gradle-plugin`
id("org.jetbrains.kotlin.jvm")
}
gradlePlugin {
plugins {
register("jps-compatible") {
id = "jps-compatible"
implementationClass = "org.jetbrains.kotlin.pill.JpsCompatiblePlugin"
}
register("kotlin-build-publishing") {
id = "kotlin-build-publishing"
implementationClass = "plugins.KotlinBuildPublishingPlugin"
}
}
}
fun Project.getBooleanProperty(name: String): Boolean? = this.findProperty(name)?.let {
val v = it.toString()
if (v.isBlank()) true
else v.toBoolean()
}
project.apply {
from(rootProject.file("../../gradle/versions.gradle.kts"))
}
val isTeamcityBuild = kotlinBuildProperties.isTeamcityBuild
val intellijSeparateSdks by extra(project.getBooleanProperty("intellijSeparateSdks") ?: false)
extra["intellijReleaseType"] = when {
extra["versions.intellijSdk"]?.toString()?.contains("-EAP-") == true -> "snapshots"
extra["versions.intellijSdk"]?.toString()?.endsWith("SNAPSHOT") == true -> "nightly"
else -> "releases"
}
extra["versions.androidDxSources"] = "5.0.0_r2"
extra["customDepsOrg"] = "kotlin.build"
repositories {
mavenCentral()
google()
maven("https://packages.jetbrains.team/maven/p/ij/intellij-dependencies")
maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/kotlin-dependencies")
gradlePluginPortal()
extra["bootstrapKotlinRepo"]?.let {
maven(url = it)
}
}
java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(8))
}
}
tasks.validatePlugins.configure {
enabled = false
}
java {
disableAutoTargetJvm()
}
dependencies {
implementation(kotlin("stdlib", embeddedKotlinVersion))
implementation("org.jetbrains.kotlin:kotlin-build-gradle-plugin:${kotlinBuildProperties.buildGradlePluginVersion}")
implementation("com.gradle.publish:plugin-publish-plugin:1.0.0")
implementation("org.jetbrains.dokka:dokka-gradle-plugin:1.8.20")
implementation("org.spdx:spdx-gradle-plugin:0.1.0-dev-8")
implementation("com.jakewharton.dex:dex-member-list:4.1.1")
implementation("gradle.plugin.com.github.johnrengelman:shadow:${project.extra["versions.shadow"]}") {
// https://github.com/johnrengelman/shadow/issues/807
exclude("org.ow2.asm")
}
implementation("net.sf.proguard:proguard-gradle:6.2.2")
// Version should be in sync with <root>/build.gradle.kts
implementation("gradle.plugin.org.jetbrains.gradle.plugin.idea-ext:gradle-idea-ext:1.0.1")
implementation("io.ktor:ktor-client-core:${project.extra["versions.ktor-client-core"]}")
implementation("io.ktor:ktor-client-cio:${project.extra["versions.ktor-client-cio"]}")
compileOnly("com.gradle:gradle-enterprise-gradle-plugin:3.12.4")
compileOnly(gradleApi())
// See https://github.com/gradle/gradle/issues/22510
implementation("org.gradle.kotlin:gradle-kotlin-dsl-plugins:2.4.1")
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:${project.bootstrapKotlinVersion}")
implementation("org.jetbrains.kotlin:kotlin-stdlib:${project.bootstrapKotlinVersion}")
implementation("org.jetbrains.kotlin:kotlin-reflect:${project.bootstrapKotlinVersion}")
implementation("com.google.code.gson:gson:2.8.9") // Workaround for Gradle dependency resolution error
implementation("org.jetbrains.kotlinx:kotlinx-metadata-jvm:0.6.2")
}
samWithReceiver {
annotation("org.gradle.api.HasImplicitReceiver")
}
fun Project.samWithReceiver(configure: org.jetbrains.kotlin.samWithReceiver.gradle.SamWithReceiverExtension.() -> Unit): Unit =
extensions.configure("samWithReceiver", configure)
tasks.withType<KotlinCompile>().configureEach {
compilerOptions {
allWarningsAsErrors.set(true)
optIn.add("kotlin.ExperimentalStdlibApi")
freeCompilerArgs.add("-Xsuppress-version-warnings")
}
}
allprojects {
tasks.register("checkBuild")
}
@@ -0,0 +1,462 @@
@file:Suppress("PropertyName", "HasPlatformType", "UnstableApiUsage")
import org.gradle.internal.os.OperatingSystem
import org.jetbrains.kotlin.gradle.tasks.internal.CleanableStore
import java.io.Closeable
import java.io.OutputStreamWriter
import java.net.URI
import java.text.SimpleDateFormat
import java.time.Duration
import java.time.Instant
import java.util.*
import javax.xml.stream.XMLOutputFactory
plugins {
base
}
val intellijReleaseType: String by rootProject.extra
val intellijVersion = rootProject.extra["versions.intellijSdk"] as String
val intellijVersionForIde = rootProject.intellijSdkVersionForIde()
val asmVersion = rootProject.findProperty("versions.jar.asm-all") as String?
val androidStudioRelease = rootProject.findProperty("versions.androidStudioRelease") as String?
val androidStudioBuild = rootProject.findProperty("versions.androidStudioBuild") as String?
val intellijSeparateSdks: Boolean by rootProject.extra
fun checkIntellijVersion(intellijVersion: String) {
val intellijVersionDelimiterIndex = intellijVersion.indexOfAny(charArrayOf('.', '-'))
if (intellijVersionDelimiterIndex == -1) {
error("Invalid IDEA version $intellijVersion")
}
}
checkIntellijVersion(intellijVersion)
intellijVersionForIde?.let { checkIntellijVersion(it) }
logger.info("intellijVersion: $intellijVersion")
logger.info("intellijVersionForIde: $intellijVersionForIde")
logger.info("androidStudioRelease: $androidStudioRelease")
logger.info("androidStudioBuild: $androidStudioBuild")
logger.info("intellijSeparateSdks: $intellijSeparateSdks")
val androidStudioOs by lazy {
when {
OperatingSystem.current().isWindows -> "windows"
OperatingSystem.current().isMacOsX -> "mac"
OperatingSystem.current().isLinux -> "linux"
else -> {
logger.error("Unknown operating system for android tools: ${OperatingSystem.current().name}")
""
}
}
}
repositories {
if (androidStudioRelease != null) {
ivy {
url = URI("https://dl.google.com/dl/android/studio/ide-zips/$androidStudioRelease")
patternLayout {
artifact("[artifact]-[revision]-$androidStudioOs.[ext]")
}
metadataSources {
artifact()
}
}
}
maven("https://www.jetbrains.com/intellij-repository/$intellijReleaseType")
maven("https://plugins.jetbrains.com/maven")
maven("https://packages.jetbrains.team/maven/p/ij/intellij-dependencies")
}
val intellij by configurations.creating
val intellijForIde by configurations.creating
val androidStudio by configurations.creating
val sources by configurations.creating
val sourcesForIde by configurations.creating
val jpsStandalone by configurations.creating
val jpsStandaloneForIde by configurations.creating
val intellijCore by configurations.creating
val intellijCoreForIde by configurations.creating
/**
* Special repository for annotations.jar required for idea runtime only.
*
* See IntellijDependenciesKt.intellijRuntimeAnnotations for more details.
*/
val intellijRuntimeAnnotations = "intellij-runtime-annotations"
val dependenciesDir = (findProperty("kotlin.build.dependencies.dir") as String?)?.let(::File)
?: rootProject.gradle.gradleUserHomeDir.resolve("kotlin-build-dependencies")
val customDepsRepoDir = dependenciesDir.resolve("repo")
val customDepsOrg: String by rootProject.extra
val repoDir = File(customDepsRepoDir, customDepsOrg)
dependencies {
if (androidStudioRelease != null) {
val extension = if (androidStudioOs == "linux")
"tar.gz"
else
"zip"
androidStudio("google:android-studio-ide:$androidStudioBuild@$extension")
} else {
intellij("com.jetbrains.intellij.idea:ideaIC:$intellijVersion")
intellijVersionForIde?.let { intellijForIde("com.jetbrains.intellij.idea:ideaIC:$it") }
}
if (asmVersion != null) {
sources("org.jetbrains.intellij.deps:asm-all:$asmVersion:sources@jar")
}
sources("com.jetbrains.intellij.idea:ideaIC:$intellijVersion:sources@jar")
intellijVersionForIde?.let { sourcesForIde("com.jetbrains.intellij.idea:ideaIC:$it:sources@jar") }
jpsStandalone("com.jetbrains.intellij.idea:jps-standalone:$intellijVersion")
intellijVersionForIde?.let { jpsStandaloneForIde("com.jetbrains.intellij.idea:jps-standalone:$it") }
intellijCore("com.jetbrains.intellij.idea:intellij-core:$intellijVersion")
intellijVersionForIde?.let { intellijCoreForIde("com.jetbrains.intellij.idea:intellij-core:$it") }
}
fun prepareDeps(
intellij: Configuration,
intellijCore: Configuration,
sources: Configuration,
jpsStandalone: Configuration,
intellijVersion: String
) {
val makeIntellijCore = buildIvyRepositoryTask(intellijCore, customDepsOrg, customDepsRepoDir)
val makeIntellijAnnotations = tasks.register("makeIntellijAnnotations${intellij.name.replaceFirstChar(Char::uppercase)}", Copy::class) {
dependsOn(makeIntellijCore)
val intellijCoreRepo = CleanableStore[repoDir.resolve("intellij-core").absolutePath][intellijVersion].use()
from(intellijCoreRepo.resolve("artifacts/annotations.jar"))
val annotationsStore = CleanableStore[repoDir.resolve(intellijRuntimeAnnotations).absolutePath]
val targetDir = annotationsStore[intellijVersion].use()
into(targetDir)
val ivyFile = File(targetDir, "$intellijRuntimeAnnotations.ivy.xml")
outputs.files(ivyFile)
doFirst {
annotationsStore.cleanStore()
}
doLast {
writeIvyXml(
customDepsOrg,
intellijRuntimeAnnotations,
intellijVersion,
intellijRuntimeAnnotations,
targetDir,
targetDir,
targetDir,
allowAnnotations = true
)
}
}
val mergeSources = tasks.create("mergeSources${intellij.name.replaceFirstChar(Char::uppercase)}", Jar::class.java) {
dependsOn(sources)
isPreserveFileTimestamps = false
isReproducibleFileOrder = true
isZip64 = true
if (!kotlinBuildProperties.isTeamcityBuild) {
from(provider { sources.map(::zipTree) })
}
destinationDirectory.set(File(repoDir, sources.name))
archiveBaseName.set("intellij")
archiveClassifier.set("sources")
archiveVersion.set(intellijVersion)
}
val sourcesFile = mergeSources.outputs.files.singleFile
val makeIde = if (androidStudioBuild != null) {
buildIvyRepositoryTask(
androidStudio,
customDepsOrg,
customDepsRepoDir,
if (androidStudioOs == "mac")
::skipContentsDirectory
else
::skipToplevelDirectory
)
} else {
val task = buildIvyRepositoryTask(intellij, customDepsOrg, customDepsRepoDir, null, sourcesFile)
task.configure {
dependsOn(mergeSources)
}
task
}
val buildJpsStandalone = buildIvyRepositoryTask(jpsStandalone, customDepsOrg, customDepsRepoDir, null, sourcesFile)
tasks.named("build") {
dependsOn(
makeIntellijCore,
makeIde,
buildJpsStandalone,
makeIntellijAnnotations
)
}
}
when(kotlinBuildProperties.getOrNull("attachedIntellijVersion")) {
null -> {}
"master" -> {} // for intellij/kt-master, intellij maven artifacts are used instead of manual unpacked dependencies
else -> {
val intellijVersionForIde = intellijVersionForIde
?: error("intellijVersionForIde should not be null as attachedIntellijVersion is present")
prepareDeps(intellijForIde, intellijCoreForIde, sourcesForIde, jpsStandaloneForIde, intellijVersionForIde)
}
}
tasks.named<Delete>("clean") {
delete(customDepsRepoDir)
}
fun buildIvyRepositoryTask(
configuration: Configuration,
organization: String,
repoDirectory: File,
pathRemap: ((String) -> String)? = null,
sources: File? = null
): TaskProvider<Task> {
fun ResolvedArtifact.storeDirectory(): CleanableStore =
CleanableStore[repoDirectory.resolve("$organization/${moduleVersion.id.name}").absolutePath]
fun ResolvedArtifact.moduleDirectory(): File =
storeDirectory()[moduleVersion.id.version].use()
return tasks.register("buildIvyRepositoryFor${configuration.name.replaceFirstChar(Char::uppercase)}") {
dependsOn(configuration)
inputs.files(configuration)
outputs.upToDateWhen {
val repoMarker = configuration.resolvedConfiguration.resolvedArtifacts.single().moduleDirectory().resolve(".marker")
repoMarker.exists()
}
doFirst {
val artifact = configuration.resolvedConfiguration.resolvedArtifacts.single()
val moduleDirectory = artifact.moduleDirectory()
artifact.storeDirectory().cleanStore()
val repoMarker = File(moduleDirectory, ".marker")
if (repoMarker.exists()) {
logger.info("Path ${repoMarker.absolutePath} already exists, skipping unpacking.")
return@doFirst
}
with(artifact) {
val artifactsDirectory = File(moduleDirectory, "artifacts")
logger.info("Unpacking ${file.name} into ${artifactsDirectory.absolutePath}")
copy {
val fileTree = when (extension) {
"tar.gz" -> tarTree(file)
"zip" -> zipTree(file)
else -> error("Unsupported artifact extension: $extension")
}
from(
fileTree.matching {
exclude("**/plugins/Kotlin/**")
}
)
into(artifactsDirectory)
if (pathRemap != null) {
eachFile {
path = pathRemap(path)
}
}
includeEmptyDirs = false
}
writeIvyXml(
organization,
moduleVersion.id.name,
moduleVersion.id.version,
moduleVersion.id.name,
File(artifactsDirectory, "lib"),
File(artifactsDirectory, "lib"),
File(moduleDirectory, "ivy"),
*listOfNotNull(sources).toTypedArray()
)
val pluginsDirectory = File(artifactsDirectory, "plugins")
if (pluginsDirectory.exists()) {
file(File(artifactsDirectory, "plugins"))
.listFiles { file: File -> file.isDirectory }
.forEach {
writeIvyXml(
organization,
it.name,
moduleVersion.id.version,
it.name,
File(it, "lib"),
File(it, "lib"),
File(moduleDirectory, "ivy"),
*listOfNotNull(sources).toTypedArray()
)
}
}
repoMarker.createNewFile()
}
}
}
}
fun CleanableStore.cleanStore() = cleanDir(Instant.now().minus(Duration.ofDays(30)))
fun writeIvyXml(
organization: String,
moduleName: String,
version: String,
fileName: String,
baseDir: File,
artifactDir: File,
targetDir: File,
vararg sourcesJar: File,
allowAnnotations: Boolean = false
) {
fun shouldIncludeIntellijJar(jar: File) =
jar.isFile
&& jar.extension == "jar"
&& !jar.name.startsWith("kotlin-")
&& (allowAnnotations || jar.name != "annotations.jar") // see comments for [intellijAnnotations] above
val ivyFile = targetDir.resolve("$fileName.ivy.xml")
ivyFile.parentFile.mkdirs()
with(XMLWriter(ivyFile.writer())) {
document("UTF-8", "1.0") {
element("ivy-module") {
attribute("version", "2.0")
attribute("xmlns:m", "http://ant.apache.org/ivy/maven")
emptyElement("info") {
attributes(
"organisation" to organization,
"module" to moduleName,
"revision" to version,
"publication" to SimpleDateFormat("yyyyMMddHHmmss").format(Date())
)
}
element("configurations") {
listOf("default", "sources").forEach { configurationName ->
emptyElement("conf") {
attributes("name" to configurationName, "visibility" to "public")
}
}
}
element("publications") {
artifactDir.listFiles()
?.filter(::shouldIncludeIntellijJar)
?.sortedBy { it.name.lowercase() }
?.forEach { jarFile ->
val relativeName = jarFile.toRelativeString(baseDir).removeSuffix(".jar")
emptyElement("artifact") {
attributes(
"name" to relativeName,
"type" to "jar",
"ext" to "jar",
"conf" to "default"
)
}
}
sourcesJar.forEach { jarFile ->
emptyElement("artifact") {
val sourcesArtifactName = jarFile.name.substringBefore("-$version")
attributes(
"name" to sourcesArtifactName,
"type" to "jar",
"ext" to "jar",
"conf" to "sources",
"m:classifier" to "sources"
)
}
}
}
}
}
close()
}
}
fun skipToplevelDirectory(path: String) = path.substringAfter('/')
fun skipContentsDirectory(path: String) = path.substringAfter("Contents/")
fun Project.intellijSdkVersionForIde(): String? {
val majorVersion = kotlinBuildProperties.getOrNull("attachedIntellijVersion") as? String ?: return null
return rootProject.findProperty("versions.intellijSdk.forIde.$majorVersion") as? String
}
class XMLWriter(private val outputStreamWriter: OutputStreamWriter) : Closeable {
private val xmlStreamWriter = XMLOutputFactory.newInstance().createXMLStreamWriter(outputStreamWriter)
private var depth = 0
private val indent = " "
fun document(encoding: String, version: String, init: XMLWriter.() -> Unit) = apply {
xmlStreamWriter.writeStartDocument(encoding, version)
init()
xmlStreamWriter.writeEndDocument()
}
fun element(name: String, init: XMLWriter.() -> Unit) = apply {
writeIndent()
xmlStreamWriter.writeStartElement(name)
depth += 1
init()
depth -= 1
writeIndent()
xmlStreamWriter.writeEndElement()
}
fun emptyElement(name: String, init: XMLWriter.() -> Unit) = apply {
writeIndent()
xmlStreamWriter.writeEmptyElement(name)
init()
}
fun attribute(name: String, value: String): Unit = xmlStreamWriter.writeAttribute(name, value)
fun attributes(vararg attributes: Pair<String, String>) {
attributes.forEach { attribute(it.first, it.second) }
}
private fun writeIndent() {
xmlStreamWriter.writeCharacters("\n")
repeat(depth) {
xmlStreamWriter.writeCharacters(indent)
}
}
override fun close() {
xmlStreamWriter.flush()
xmlStreamWriter.close()
outputStreamWriter.close()
}
}
@@ -0,0 +1,72 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
import org.gradle.api.GradleException
import org.gradle.api.tasks.*
import org.gradle.api.tasks.testing.Test
import java.io.File
// You can see "How To" via link: https://jetbrains.quip.com/xQ2WAUy9bZmy/How-to-use-AggregateTest-task
open class AggregateTest : Test() { // Inherit from Test to see test results in IDEA Test viewer
private var patterns: MutableMap<String, MutableList<String>> = mutableMapOf()
@InputFile
lateinit var testPatternFile: File
init {
// Set empty FileCollection to avoid NPE when initializing a base 'Test' class
classpath = project.objects.fileCollection()
testClassesDirs = project.objects.fileCollection()
project.gradle.taskGraph.whenReady {
if (allTasks.filterIsInstance<AggregateTest>().isNotEmpty()) {
initPatterns()
allTasks.filterIsInstance<Test>().forEach { testTask -> subTaskConfigure(testTask) }
if (!project.gradle.startParameter.taskNames.all { project.tasks.findByPath(it) is AggregateTest }) {
logger.warn("Please, don't use AggregateTest and non-AggregateTest test tasks together. You can get incorrect results.")
}
}
}
}
private fun initPatterns() {
if (!testPatternFile.exists())
throw GradleException("File with test patterns is not found")
testPatternFile
.readLines()
.asSequence()
.filter { it.isNotEmpty() }
.forEach { line ->
// patternType is exclude or include value
val (pattern, patternType) = line.split(',').map { it.trim() }
patterns.getOrPut(patternType) { mutableListOf() }.add(pattern)
}
}
private fun subTaskConfigure(testTask: Test) {
testTask.doNotTrackState("State is tracked by AggregateTest task")
testTask.ignoreFailures = true
testTask.filter {
isFailOnNoMatchingTests = false
patterns["include"]?.let {
it.forEach { pattern ->
includeTestsMatching(pattern)
}
}
patterns["exclude"]?.let {
it.forEach { pattern ->
excludeTestsMatching(pattern)
}
}
}
}
@Override
@TaskAction
override fun executeTests() {
// Do nothing
}
}
@@ -0,0 +1,61 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
val KotlinBuildProperties.includeJava9: Boolean
get() = !isInJpsBuildIdeaSync && getBoolean("kotlin.build.java9", true)
val KotlinBuildProperties.useBootstrapStdlib: Boolean
get() = isInJpsBuildIdeaSync || getBoolean("kotlin.build.useBootstrapStdlib", false)
val KotlinBuildProperties.postProcessing: Boolean get() = isTeamcityBuild || getBoolean("kotlin.build.postprocessing", true)
val KotlinBuildProperties.relocation: Boolean get() = postProcessing
val KotlinBuildProperties.proguard: Boolean get() = postProcessing && getBoolean("kotlin.build.proguard", isTeamcityBuild)
val KotlinBuildProperties.jarCompression: Boolean get() = getBoolean("kotlin.build.jar.compression", isTeamcityBuild)
val KotlinBuildProperties.ignoreTestFailures: Boolean get() = getBoolean("ignoreTestFailures", isTeamcityBuild)
val KotlinBuildProperties.disableWerror: Boolean
get() = getBoolean("kotlin.build.disable.werror") || useFir || isInJpsBuildIdeaSync || getBoolean("test.progressive.mode")
val KotlinBuildProperties.generateModularizedConfigurations: Boolean
get() = getBoolean("kotlin.fir.modularized.mt.configurations", false)
val KotlinBuildProperties.generateFullPipelineConfigurations: Boolean
get() = getBoolean("kotlin.fir.modularized.fp.configurations", false)
val KotlinBuildProperties.pathToKotlinModularizedTestData: String?
get() = getOrNull("kotlin.fir.modularized.testdata.kotlin") as? String
val KotlinBuildProperties.pathToIntellijModularizedTestData: String?
get() = getOrNull("kotlin.fir.modularized.testdata.intellij") as? String
val KotlinBuildProperties.pathToYoutrackModularizedTestData: String?
get() = getOrNull("kotlin.fir.modularized.testdata.youtrack") as? String
val KotlinBuildProperties.pathToSpaceModularizedTestData: String?
get() = getOrNull("kotlin.fir.modularized.testdata.space") as? String
val KotlinBuildProperties.isObsoleteJdkOverrideEnabled: Boolean
get() = getBoolean("kotlin.build.isObsoleteJdkOverrideEnabled", false)
val KotlinBuildProperties.isNativeRuntimeDebugInfoEnabled: Boolean
get() = getBoolean("kotlin.native.isNativeRuntimeDebugInfoEnabled", false)
val KotlinBuildProperties.junit5NumberOfThreadsForParallelExecution: Int?
get() = (getOrNull("kotlin.test.junit5.maxParallelForks") as? String)?.toInt()
// Enabling publishing docs jars only on CI build by default
// Currently dokka task runs non-incrementally and takes big amount of time
val KotlinBuildProperties.publishGradlePluginsJavadoc: Boolean
get() = getBoolean("kotlin.build.gradle.publish.javadocs", isTeamcityBuild)
val KotlinBuildProperties.useFirWithLightTree: Boolean
get() = getBoolean("kotlin.build.useFirLT")
val KotlinBuildProperties.useFirTightIC: Boolean
get() = getBoolean("kotlin.build.useFirIC")
@@ -0,0 +1,389 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
import groovy.lang.Closure
import org.gradle.api.JavaVersion
import org.gradle.api.file.FileCollection
import org.gradle.api.provider.Property
import org.gradle.api.provider.Provider
import org.gradle.api.tasks.*
import org.gradle.jvm.toolchain.JavaLauncher
import org.gradle.kotlin.dsl.property
import proguard.ClassSpecification
import java.io.File
@CacheableTask
open class CacheableProguardTask : proguard.gradle.ProGuardTask() {
@get:Internal
val javaLauncher: Property<JavaLauncher> = project.objects.property()
@get:Internal
val jdkHomePath: Provider<File> = javaLauncher.map { it.metadata.installationPath.asFile }
@get:Optional
@get:Input
internal val jdkMajorVersion: Provider<JavaVersion> = javaLauncher.map {
JavaVersion.toVersion(it.metadata.languageVersion.toString())
}
@CompileClasspath
override fun getLibraryJarFileCollection(): FileCollection = super.getLibraryJarFileCollection()
.filter { libraryFile ->
jdkHomePath.orNull?.let { !libraryFile.absoluteFile.startsWith(it.absoluteFile) } ?: true
}
@InputFiles
@PathSensitive(PathSensitivity.RELATIVE)
override fun getConfigurationFileCollection(): FileCollection = super.getConfigurationFileCollection()
@InputFiles
@Classpath
override fun getInJarFileCollection(): FileCollection = super.getInJarFileCollection()
@Optional
@OutputFiles
override fun getOutJarFileCollection(): FileCollection = super.getOutJarFileCollection()
@get:Optional
@get:OutputFile
internal val printConfigurationFile: File?
get() = configuration.printConfiguration?.takeIf { it.path.isNotEmpty() }
@Input
override fun getOutJarFilters(): MutableList<Any?> = super.getOutJarFilters()
@Input
override fun getInJarFilters(): MutableList<Any?> = super.getInJarFilters()
@Input
override fun getLibraryJarFilters(): MutableList<Any?> = super.getLibraryJarFilters()
@Internal
override fun getOutJarFiles(): MutableList<Any?> = super.getOutJarFiles()
@Internal
override fun getInJarFiles(): MutableList<Any?> = super.getInJarFiles()
@Internal
override fun getInJarCounts(): MutableList<Any?> = super.getInJarCounts()
@Internal
override fun getLibraryJarFiles(): MutableList<Any?> = super.getLibraryJarFiles()
/*
* Inputs properly declared these methods so we don't override them
*
* configuration(configurationFiles: Any?)
* libraryjars(libraryJarFiles: Any?)
* libraryjars(filterArgs: MutableMap<Any?, Any?>?, libraryJarFiles: Any?)
* injars(inJarFiles: Any?)
* injars(filterArgs: MutableMap<Any?, Any?>?, inJarFiles: Any?)
* outjars(outJarFiles: Any?)
* outjars(filterArgs: MutableMap<Any?, Any?>?, outJarFiles: Any?)
* printconfiguration()
* printconfiguration(printConfiguration: Any?)
*/
override fun renamesourcefileattribute() = throw NotImplementedError()
override fun renamesourcefileattribute(newSourceFileAttribute: String?) = throw NotImplementedError()
override fun dontshrink() = throw NotImplementedError()
override fun assumenosideeffects(classSpecificationString: String?) = throw NotImplementedError()
override fun assumenosideeffects(classSpecificationArgs: MutableMap<Any?, Any?>?, classMembersClosure: Closure<*>?) = throw NotImplementedError()
override fun keepnames(classSpecificationString: String?) = throw NotImplementedError()
override fun keepnames(keepArgs: MutableMap<Any?, Any?>?, classSpecificationString: String?) = throw NotImplementedError()
override fun keepnames(keepClassSpecificationArgs: MutableMap<Any?, Any?>?) = throw NotImplementedError()
override fun keepnames(keepClassSpecificationArgs: MutableMap<Any?, Any?>?, classMembersClosure: Closure<*>?) = throw NotImplementedError()
override fun printmapping() = throw NotImplementedError()
override fun printmapping(printMapping: Any?) = throw NotImplementedError()
override fun keep(classSpecificationString: String?) = throw NotImplementedError()
override fun keep(keepArgs: MutableMap<Any?, Any?>?, classSpecificationString: String?) = throw NotImplementedError()
override fun keep(keepClassSpecificationArgs: MutableMap<Any?, Any?>?) = throw NotImplementedError()
override fun keep(keepClassSpecificationArgs: MutableMap<Any?, Any?>?, classMembersClosure: Closure<*>?) = throw NotImplementedError()
override fun keepdirectories() = throw NotImplementedError()
override fun keepdirectories(filter: String?) = throw NotImplementedError()
override fun dontpreverify() = throw NotImplementedError()
override fun dontnote() = throw NotImplementedError()
override fun dontnote(filter: String?) = throw NotImplementedError()
@Internal
override fun getrenamesourcefileattribute(): Any? = throw NotImplementedError()
override fun useuniqueclassmembernames() = throw NotImplementedError()
override fun overloadaggressively() = throw NotImplementedError()
@Internal
override fun getprintusage(): Any? = throw NotImplementedError()
@Internal
override fun getforceprocessing(): Any? = throw NotImplementedError()
override fun whyareyoukeeping(classSpecificationString: String?) = throw NotImplementedError()
override fun whyareyoukeeping(classSpecificationArgs: MutableMap<Any?, Any?>?) = throw NotImplementedError()
override fun whyareyoukeeping(classSpecificationArgs: MutableMap<Any?, Any?>?, classMembersClosure: Closure<*>?) = throw NotImplementedError()
override fun obfuscationdictionary(obfuscationDictionary: Any?) = throw NotImplementedError()
override fun adaptclassstrings() = throw NotImplementedError()
override fun adaptclassstrings(filter: String?) = throw NotImplementedError()
override fun applymapping(applyMapping: Any?) = throw NotImplementedError()
override fun mergeinterfacesaggressively() = throw NotImplementedError()
@Internal
override fun getdontwarn(): Any? = throw NotImplementedError()
override fun ignorewarnings() = throw NotImplementedError()
@Internal
override fun getaddconfigurationdebugging(): Any? = throw NotImplementedError()
override fun field(memberSpecificationArgs: MutableMap<Any?, Any?>?) = throw NotImplementedError()
@Internal
override fun getallowaccessmodification(): Any? = throw NotImplementedError()
override fun constructor(memberSpecificationArgs: MutableMap<Any?, Any?>?) = throw NotImplementedError()
override fun dontusemixedcaseclassnames() = throw NotImplementedError()
@Internal
override fun getignorewarnings(): Any? = throw NotImplementedError()
@Internal
override fun getkeepdirectories(): Any? = throw NotImplementedError()
override fun classobfuscationdictionary(classObfuscationDictionary: Any?) = throw NotImplementedError()
override fun verbose() = throw NotImplementedError()
override fun optimizations(filter: String?) = throw NotImplementedError()
@Internal
override fun getuseuniqueclassmembernames(): Any? = throw NotImplementedError()
@Internal
override fun getmicroedition(): Any? = throw NotImplementedError()
override fun assumenoescapingparameters(classSpecificationString: String?) = throw NotImplementedError()
override fun assumenoescapingparameters(classSpecificationArgs: MutableMap<Any?, Any?>?, classMembersClosure: Closure<*>?) = throw NotImplementedError()
@Internal
override fun getandroid(): Any? = throw NotImplementedError()
override fun keeppackagenames() = throw NotImplementedError()
override fun keeppackagenames(filter: String?) = throw NotImplementedError()
@Internal
override fun getoverloadaggressively(): Any? = throw NotImplementedError()
override fun skipnonpubliclibraryclasses() = throw NotImplementedError()
@Internal
override fun getdontusemixedcaseclassnames(): Any? = throw NotImplementedError()
@Internal
override fun getdontnote(): Any? = throw NotImplementedError()
override fun assumenoexternalreturnvalues(classSpecificationString: String?) = throw NotImplementedError()
override fun assumenoexternalreturnvalues(classSpecificationArgs: MutableMap<Any?, Any?>?, classMembersClosure: Closure<*>?) = throw NotImplementedError()
override fun target(targetClassVersion: String?) = throw NotImplementedError()
override fun keepattributes() = throw NotImplementedError()
override fun keepattributes(filter: String?) = throw NotImplementedError()
override fun keepclassmembernames(classSpecificationString: String?) = throw NotImplementedError()
override fun keepclassmembernames(keepArgs: MutableMap<Any?, Any?>?, classSpecificationString: String?) = throw NotImplementedError()
override fun keepclassmembernames(keepClassSpecificationArgs: MutableMap<Any?, Any?>?) = throw NotImplementedError()
override fun keepclassmembernames(keepClassSpecificationArgs: MutableMap<Any?, Any?>?, classMembersClosure: Closure<*>?) = throw NotImplementedError()
@Internal
override fun getdontpreverify(): Any? = throw NotImplementedError()
@Internal
override fun getverbose(): Any? = throw NotImplementedError()
@Internal
override fun getskipnonpubliclibraryclasses(): Any? = throw NotImplementedError()
@Internal
override fun getdontoptimize(): Any? = throw NotImplementedError()
override fun keepclasseswithmembernames(classSpecificationString: String?) = throw NotImplementedError()
override fun keepclasseswithmembernames(keepArgs: MutableMap<Any?, Any?>?, classSpecificationString: String?) = throw NotImplementedError()
override fun keepclasseswithmembernames(keepClassSpecificationArgs: MutableMap<Any?, Any?>?) = throw NotImplementedError()
override fun keepclasseswithmembernames(keepClassSpecificationArgs: MutableMap<Any?, Any?>?, classMembersClosure: Closure<*>?) = throw NotImplementedError()
override fun keepclasseswithmembers(classSpecificationString: String?) = throw NotImplementedError()
override fun keepclasseswithmembers(keepArgs: MutableMap<Any?, Any?>?, classSpecificationString: String?) = throw NotImplementedError()
override fun keepclasseswithmembers(keepClassSpecificationArgs: MutableMap<Any?, Any?>?) = throw NotImplementedError()
override fun keepclasseswithmembers(keepClassSpecificationArgs: MutableMap<Any?, Any?>?, classMembersClosure: Closure<*>?) = throw NotImplementedError()
@Internal
override fun getdump(): Any? = throw NotImplementedError()
override fun printseeds() = throw NotImplementedError()
override fun printseeds(printSeeds: Any?) = throw NotImplementedError()
override fun dontoptimize() = throw NotImplementedError()
override fun dontobfuscate() = throw NotImplementedError()
override fun extendClassSpecifications(
classSpecifications: MutableList<Any?>?,
classSpecification: ClassSpecification?
): MutableList<Any?> = throw NotImplementedError()
override fun allowaccessmodification() = throw NotImplementedError()
@Internal
override fun getdontobfuscate(): Any? = throw NotImplementedError()
@Internal
override fun getprintmapping(): Any? = throw NotImplementedError()
override fun flattenpackagehierarchy() = throw NotImplementedError()
override fun flattenpackagehierarchy(flattenPackageHierarchy: String?) = throw NotImplementedError()
override fun android() = throw NotImplementedError()
override fun dump() = throw NotImplementedError()
override fun dump(dump: Any?) = throw NotImplementedError()
@Internal
override fun getdontshrink(): Any? = throw NotImplementedError()
@Internal
override fun getkeepattributes(): Any? = throw NotImplementedError()
override fun microedition() = throw NotImplementedError()
override fun keepparameternames() = throw NotImplementedError()
override fun addconfigurationdebugging() = throw NotImplementedError()
override fun packageobfuscationdictionary(packageObfuscationDictionary: Any?) = throw NotImplementedError()
@Internal
override fun getdontskipnonpubliclibraryclassmembers(): Any? = throw NotImplementedError()
override fun dontskipnonpubliclibraryclassmembers() = throw NotImplementedError()
@Internal
override fun getprintconfiguration(): Any? = throw NotImplementedError()
override fun forceprocessing() = throw NotImplementedError()
override fun keepclassmembers(classSpecificationString: String?) = throw NotImplementedError()
override fun keepclassmembers(keepArgs: MutableMap<Any?, Any?>?, classSpecificationString: String?) = throw NotImplementedError()
override fun keepclassmembers(keepClassSpecificationArgs: MutableMap<Any?, Any?>?) = throw NotImplementedError()
override fun keepclassmembers(keepClassSpecificationArgs: MutableMap<Any?, Any?>?, classMembersClosure: Closure<*>?) = throw NotImplementedError()
@Internal
override fun getmergeinterfacesaggressively(): Any? = throw NotImplementedError()
@Internal
override fun getConfigurationFiles(): MutableList<Any?> = throw NotImplementedError()
@Internal
override fun getkeeppackagenames(): Any? = throw NotImplementedError()
override fun assumevalues(classSpecificationString: String?) = throw NotImplementedError()
override fun assumevalues(classSpecificationArgs: MutableMap<Any?, Any?>?, classMembersClosure: Closure<*>?) = throw NotImplementedError()
override fun printusage() = throw NotImplementedError()
override fun printusage(printUsage: Any?) = throw NotImplementedError()
@Internal
override fun getprintseeds(): Any? = throw NotImplementedError()
@Internal
override fun getadaptresourcefilenames(): Any? = throw NotImplementedError()
override fun assumenoexternalsideeffects(classSpecificationString: String?) = throw NotImplementedError()
override fun assumenoexternalsideeffects(classSpecificationArgs: MutableMap<Any?, Any?>?, classMembersClosure: Closure<*>?) = throw NotImplementedError()
override fun dontwarn() = throw NotImplementedError()
override fun dontwarn(filter: String?) = throw NotImplementedError()
@Internal
override fun getrepackageclasses(): Any? = throw NotImplementedError()
@Internal
override fun getadaptresourcefilecontents(): Any? = throw NotImplementedError()
@Internal
override fun getflattenpackagehierarchy(): Any? = throw NotImplementedError()
override fun optimizationpasses(optimizationPasses: Int) = throw NotImplementedError()
override fun adaptresourcefilenames() = throw NotImplementedError()
override fun adaptresourcefilenames(filter: String?) = throw NotImplementedError()
override fun method(memberSpecificationArgs: MutableMap<Any?, Any?>?) = throw NotImplementedError()
@Internal
override fun getadaptclassstrings(): Any? = throw NotImplementedError()
override fun repackageclasses() = throw NotImplementedError()
override fun repackageclasses(repackageClasses: String?) = throw NotImplementedError()
@Internal
override fun getkeepparameternames(): Any? = throw NotImplementedError()
override fun adaptresourcefilecontents() = throw NotImplementedError()
override fun adaptresourcefilecontents(filter: String?) = throw NotImplementedError()
}
@@ -0,0 +1,99 @@
// usages in build scripts are not tracked properly
@file:Suppress("unused")
import groovy.lang.Closure
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.file.CopySourceSpec
import org.gradle.api.file.SourceDirectorySet
import org.gradle.api.plugins.JavaPluginExtension
import org.gradle.api.tasks.JavaExec
import org.gradle.api.tasks.SourceSet
import org.gradle.api.tasks.SourceSetOutput
import org.gradle.api.tasks.bundling.AbstractArchiveTask
import org.gradle.kotlin.dsl.*
import proguard.gradle.ProGuardTask
import java.io.File
import java.util.*
import java.util.concurrent.Callable
inline fun <reified T : Task> Project.task(noinline configuration: T.() -> Unit) = tasks.registering(T::class, configuration)
inline fun <reified T : Task> Project.eagerTask(noinline configuration: T.() -> Unit) = tasks.creating(T::class, configuration)
fun Project.callGroovy(name: String, vararg args: Any?): Any? {
return (property(name) as Closure<*>).call(*args)
}
inline fun <T : Any> Project.withJavaPlugin(crossinline body: () -> T?): T? {
var res: T? = null
pluginManager.withPlugin("java") {
res = body()
}
return res
}
fun Project.getCompiledClasses(): SourceSetOutput? = withJavaPlugin { mainSourceSet.output }
fun Project.getSources(): SourceDirectorySet? = withJavaPlugin { mainSourceSet.allSource }
fun Project.getResourceFiles(): SourceDirectorySet? = withJavaPlugin { mainSourceSet.resources }
fun fileFrom(root: File, vararg children: String): File = children.fold(root) { f, c -> File(f, c) }
fun fileFrom(root: String, vararg children: String): File = children.fold(File(root)) { f, c -> File(f, c) }
var Project.jvmTarget: String?
get() = extra.takeIf { it.has("jvmTarget") }?.get("jvmTarget") as? String
set(v) {
extra["jvmTarget"] = v
}
var Project.javaHome: String?
get() = extra.takeIf { it.has("javaHome") }?.get("javaHome") as? String
set(v) {
extra["javaHome"] = v
}
fun Project.generator(fqName: String, sourceSet: SourceSet? = null, configure: JavaExec.() -> Unit = {}) = smartJavaExec {
group = "Generate"
classpath = (sourceSet ?: testSourceSet).runtimeClasspath
mainClass.set(fqName)
workingDir = rootDir
systemProperty("line.separator", "\n")
systemProperty("idea.ignore.disabled.plugins", "true")
configure()
}
fun Project.getBooleanProperty(name: String): Boolean? = this.findProperty(name)?.let {
val v = it.toString()
if (v.isBlank()) true
else v.toBoolean()
}
inline fun CopySourceSpec.from(crossinline filesProvider: () -> Any?): CopySourceSpec = from(Callable { filesProvider() })
fun Project.javaPluginExtension(): JavaPluginExtension = extensions.getByType()
fun Project.findJavaPluginExtension(): JavaPluginExtension? = extensions.findByType()
fun JavaExec.pathRelativeToWorkingDir(file: File): String = file.relativeTo(workingDir).invariantSeparatorsPath
fun Task.singleOutputFile(): File = when (this) {
is AbstractArchiveTask -> archiveFile.get().asFile
is ProGuardTask -> project.file(outJarFiles.single()!!)
else -> outputs.files.singleFile
}
val Project.isConfigurationCacheDisabled
get() = (gradle.startParameter as? org.gradle.api.internal.StartParameterInternal)?.configurationCache?.get() != true
val Project.isIdeaActive
get() = providers.systemProperty("idea.active").isPresent
val Project.intellijCommunityDir: File
get() = rootDir.resolve("intellij/community").takeIf { it.isDirectory } ?: rootDir.resolve("intellij")
fun String.capitalize(): String = capitalize(Locale.ROOT)
fun String.capitalize(locale: Locale): String = replaceFirstChar { if (it.isLowerCase()) it.titlecase(locale) else it.toString() }
@@ -0,0 +1,754 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
import org.gradle.api.Action
import org.gradle.api.GradleException
import org.gradle.api.Project
import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.ModuleDependency
import org.gradle.api.artifacts.type.ArtifactTypeDefinition
import org.gradle.api.attributes.*
import org.gradle.api.attributes.java.TargetJvmEnvironment
import org.gradle.api.attributes.java.TargetJvmVersion
import org.gradle.api.attributes.plugin.GradlePluginApiVersion
import org.gradle.api.component.AdhocComponentWithVariants
import org.gradle.api.plugins.JavaLibraryPlugin
import org.gradle.api.plugins.JavaPlugin
import org.gradle.api.plugins.JavaPluginExtension
import org.gradle.api.publish.PublishingExtension
import org.gradle.api.publish.maven.MavenPublication
import org.gradle.api.tasks.Copy
import org.gradle.api.tasks.SourceSet
import org.gradle.api.tasks.compile.JavaCompile
import org.gradle.jvm.tasks.Jar
import org.gradle.kotlin.dsl.*
import org.gradle.plugin.devel.plugins.JavaGradlePluginPlugin
import org.jetbrains.dokka.DokkaVersion
import org.jetbrains.dokka.gradle.DokkaTask
import org.jetbrains.dokka.gradle.GradleExternalDocumentationLinkBuilder
import org.jetbrains.kotlin.gradle.dsl.KotlinSingleJavaTargetExtension
import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import plugins.configureDefaultPublishing
import plugins.configureKotlinPomAttributes
import java.net.URL
/**
* Gradle's plugins common variants.
*
* [minimalSupportedGradleVersion] - minimal Gradle version that is supported in this variant
* [gradleApiVersion] - Gradle API dependency version. Usually should be the same as [minimalSupportedGradleVersion].
*/
enum class GradlePluginVariant(
val sourceSetName: String,
val minimalSupportedGradleVersion: String,
val gradleApiVersion: String,
val gradleApiJavadocUrl: String
) {
GRADLE_MIN("main", "6.8.3", "6.9", "https://docs.gradle.org/6.9.3/javadoc/"),
GRADLE_70("gradle70", "7.0", "7.0", "https://docs.gradle.org/7.0.2/javadoc/"),
GRADLE_71("gradle71", "7.1", "7.1", "https://docs.gradle.org/7.1.1/javadoc/"),
GRADLE_74("gradle74", "7.4", "7.4", "https://docs.gradle.org/7.4.2/javadoc/"),
GRADLE_75("gradle75", "7.5", "7.5", "https://docs.gradle.org/7.5.1/javadoc/"),
GRADLE_76("gradle76", "7.6", "7.6", "https://docs.gradle.org/7.6.1/javadoc/"),
GRADLE_80("gradle80", "8.0", "8.0", "https://docs.gradle.org/8.0.2/javadoc/"),
GRADLE_81("gradle81", "8.1", "8.1", "https://docs.gradle.org/8.1.1/javadoc/"),
}
val commonSourceSetName = "common"
/**
* Configures common pom configuration parameters
*/
fun Project.configureCommonPublicationSettingsForGradle(
signingRequired: Boolean,
) {
plugins.withId("maven-publish") {
configureDefaultPublishing(signingRequired)
extensions.configure<PublishingExtension> {
publications
.withType<MavenPublication>()
.configureEach {
configureKotlinPomAttributes(project)
}
}
}
}
/**
* These dependencies will be provided by Gradle, and we should prevent version conflict
*/
fun Configuration.excludeGradleCommonDependencies() {
dependencies
.withType<ModuleDependency>()
.configureEach {
exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib")
exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib-jdk7")
exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib-jdk8")
exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib-common")
exclude(group = "org.jetbrains.kotlin", module = "kotlin-reflect")
exclude(group = "org.jetbrains.kotlin", module = "kotlin-script-runtime")
}
}
/**
* Exclude Gradle runtime from given SourceSet configurations.
*/
fun Project.excludeGradleCommonDependencies(sourceSet: SourceSet) {
configurations[sourceSet.implementationConfigurationName].excludeGradleCommonDependencies()
configurations[sourceSet.apiConfigurationName].excludeGradleCommonDependencies()
configurations[sourceSet.runtimeOnlyConfigurationName].excludeGradleCommonDependencies()
}
private val testPlugins = setOf(
"kotlin-gradle-plugin-api",
"android-test-fixes",
"gradle-warnings-detector",
"kotlin-compiler-args-properties",
"kotlin-gradle-plugin",
)
/**
* Common sources for all variants.
* Should contain classes that are independent of Gradle API version or using minimal supported Gradle api.
*/
fun Project.createGradleCommonSourceSet(): SourceSet {
val commonSourceSet = sourceSets.create(commonSourceSetName) {
excludeGradleCommonDependencies(this)
// Adding Gradle API to separate configuration, so version will not leak into variants
val commonGradleApiConfiguration = configurations.create("commonGradleApiCompileOnly") {
isVisible = false
isCanBeConsumed = false
isCanBeResolved = true
}
configurations[compileClasspathConfigurationName].extendsFrom(commonGradleApiConfiguration)
dependencies {
compileOnlyConfigurationName(kotlinStdlib())
"commonGradleApiCompileOnly"("dev.gradleplugins:gradle-api:8.1")
if (this@createGradleCommonSourceSet.name !in testPlugins) {
compileOnlyConfigurationName(project(":kotlin-gradle-plugin-api")) {
capabilities {
requireCapability("org.jetbrains.kotlin:kotlin-gradle-plugin-api-common")
}
}
}
}
}
plugins.withType<JavaLibraryPlugin>().configureEach {
this@createGradleCommonSourceSet.extensions.configure<JavaPluginExtension> {
registerFeature(commonSourceSet.name) {
usingSourceSet(commonSourceSet)
disablePublication()
}
}
}
// Common outputs will also produce '${project.name}.kotlin_module' file, so we need to avoid
// files clash
tasks.named<KotlinCompile>("compile${commonSourceSet.name.replaceFirstChar { it.uppercase() }}Kotlin") {
@Suppress("DEPRECATION")
kotlinOptions {
moduleName = "${this@createGradleCommonSourceSet.name}_${commonSourceSet.name}"
}
}
return commonSourceSet
}
/**
* Fixes wired SourceSet does not expose compiled common classes and common resources as secondary variant
* which is used in the Kotlin Project compilation.
*/
private fun Project.fixWiredSourceSetSecondaryVariants(
wireSourceSet: SourceSet,
commonSourceSet: SourceSet,
) {
configurations
.matching {
it.name == wireSourceSet.apiElementsConfigurationName ||
it.name == wireSourceSet.runtimeElementsConfigurationName
}
.configureEach {
outgoing {
variants.maybeCreate("classes").apply {
attributes {
attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(LibraryElements.CLASSES))
}
(commonSourceSet.output.classesDirs.files + wireSourceSet.output.classesDirs.files)
.toSet()
.forEach {
if (!artifacts.files.contains(it)) {
artifact(it) {
type = ArtifactTypeDefinition.JVM_CLASS_DIRECTORY
}
}
}
}
}
}
configurations
.matching { it.name == wireSourceSet.runtimeElementsConfigurationName }
.configureEach {
outgoing {
val resourcesDirectories = listOfNotNull(
commonSourceSet.output.resourcesDir,
wireSourceSet.output.resourcesDir
)
if (resourcesDirectories.isNotEmpty()) {
variants.maybeCreate("resources").apply {
attributes {
attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(LibraryElements.RESOURCES))
}
resourcesDirectories.forEach {
if (!artifacts.files.contains(it)) {
artifact(it) {
type = ArtifactTypeDefinition.JVM_RESOURCES_DIRECTORY
}
}
}
}
}
}
}
}
/**
* Make [wireSourceSet] to extend [commonSourceSet].
*/
fun Project.wireGradleVariantToCommonGradleVariant(
wireSourceSet: SourceSet,
commonSourceSet: SourceSet,
) {
wireSourceSet.compileClasspath += commonSourceSet.output
wireSourceSet.runtimeClasspath += commonSourceSet.output
// Allowing to use 'internal' classes/methods from common source code
(extensions.getByName("kotlin") as KotlinSingleJavaTargetExtension).target.compilations.run {
getByName(wireSourceSet.name).associateWith(getByName(commonSourceSet.name))
}
configurations[wireSourceSet.apiConfigurationName].extendsFrom(
configurations[commonSourceSet.apiConfigurationName]
)
configurations[wireSourceSet.implementationConfigurationName].extendsFrom(
configurations[commonSourceSet.implementationConfigurationName]
)
configurations[wireSourceSet.runtimeOnlyConfigurationName].extendsFrom(
configurations[commonSourceSet.runtimeOnlyConfigurationName]
)
configurations[wireSourceSet.compileOnlyConfigurationName].extendsFrom(
configurations[commonSourceSet.compileOnlyConfigurationName]
)
fixWiredSourceSetSecondaryVariants(wireSourceSet, commonSourceSet)
tasks.withType<Jar>().configureEach {
if (name == wireSourceSet.jarTaskName) {
from(wireSourceSet.output, commonSourceSet.output)
setupPublicJar(archiveBaseName.get())
addEmbeddedRuntime()
addEmbeddedRuntime(wireSourceSet.embeddedConfigurationName)
} else if (name == wireSourceSet.sourcesJarTaskName) {
from(wireSourceSet.allSource, commonSourceSet.allSource)
}
}
}
private const val FIXED_CONFIGURATION_SUFFIX = "WithFixedAttribute"
/**
* 'main' sources are used for minimal supported Gradle versions (6.7) up to Gradle 7.0.
*/
fun Project.reconfigureMainSourcesSetForGradlePlugin(
commonSourceSet: SourceSet,
) {
sourceSets.named(SourceSet.MAIN_SOURCE_SET_NAME) {
plugins.withType<JavaGradlePluginPlugin>().configureEach {
// Removing Gradle api default dependency added by 'java-gradle-plugin'
configurations[apiConfigurationName].dependencies.remove(dependencies.gradleApi())
}
dependencies {
"compileOnly"(kotlinStdlib())
// Decoupling gradle-api artifact from current project Gradle version. Later would be useful for
// gradle plugin variants
"compileOnly"("dev.gradleplugins:gradle-api:${GradlePluginVariant.GRADLE_MIN.gradleApiVersion}")
if (this@reconfigureMainSourcesSetForGradlePlugin.name !in testPlugins) {
"api"(project(":kotlin-gradle-plugin-api"))
}
}
excludeGradleCommonDependencies(this)
wireGradleVariantToCommonGradleVariant(this, commonSourceSet)
// https://youtrack.jetbrains.com/issue/KT-51913
// Remove workaround after bootstrap update
if (configurations["default"].attributes.contains(TargetJvmEnvironment.TARGET_JVM_ENVIRONMENT_ATTRIBUTE)) {
configurations["default"].attributes.attribute(
TargetJvmEnvironment.TARGET_JVM_ENVIRONMENT_ATTRIBUTE,
objects.named(TargetJvmEnvironment::class, "no-op")
)
}
plugins.withType<JavaLibraryPlugin>().configureEach {
this@reconfigureMainSourcesSetForGradlePlugin
.extensions
.configure<JavaPluginExtension> {
withSourcesJar()
if (kotlinBuildProperties.publishGradlePluginsJavadoc) {
withJavadocJar()
}
}
configurations.create(sourceSets.getByName("main").embeddedConfigurationName) {
isCanBeConsumed = false
isCanBeResolved = true
attributes {
attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.JAVA_RUNTIME))
attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(LibraryElements.JAR))
}
}
}
// Workaround for https://youtrack.jetbrains.com/issue/KT-52987
val javaComponent = project.components["java"] as AdhocComponentWithVariants
listOf(
runtimeElementsConfigurationName,
apiElementsConfigurationName
)
.map { configurations[it] }
.forEach { originalConfiguration ->
configurations.create("${originalConfiguration.name}$FIXED_CONFIGURATION_SUFFIX") {
isCanBeResolved = originalConfiguration.isCanBeResolved
isCanBeConsumed = originalConfiguration.isCanBeConsumed
isVisible = originalConfiguration.isVisible
setExtendsFrom(originalConfiguration.extendsFrom)
artifacts {
originalConfiguration.artifacts.forEach {
add(name, it)
}
}
// Removing 'org.jetbrains.kotlin.platform.type' attribute
// as it brings issues with Gradle variant resolve on Gradle 7.6+ versions
attributes {
originalConfiguration.attributes.keySet()
.filter { it.name != KotlinPlatformType.attribute.name }
.forEach { originalAttribute ->
@Suppress("UNCHECKED_CAST")
attribute(
originalAttribute as Attribute<Any>,
originalConfiguration.attributes.getAttribute(originalAttribute)!!
)
}
plugins.withType<JavaPlugin> {
tasks.named<JavaCompile>(compileJavaTaskName).get().apply {
attribute(
TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE,
when (targetCompatibility) {
"1.8" -> 8
else -> targetCompatibility.toInt()
}
)
}
}
}
val expectedAttributes = setOf(
Category.CATEGORY_ATTRIBUTE,
Bundling.BUNDLING_ATTRIBUTE,
Usage.USAGE_ATTRIBUTE,
LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE,
TargetJvmEnvironment.TARGET_JVM_ENVIRONMENT_ATTRIBUTE,
TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE
)
if (attributes.keySet() != expectedAttributes) {
error(
"Wrong set of attributes:\n" +
" Expected: ${expectedAttributes.joinToString(", ")}\n" +
" Actual: ${attributes.keySet().joinToString(", ") { "${it.name}=${attributes.getAttribute(it)}" }}"
)
}
javaComponent.addVariantsFromConfiguration(this) {
mapToMavenScope(
when (originalConfiguration.name) {
runtimeElementsConfigurationName -> "runtime"
apiElementsConfigurationName -> "compile"
else -> error("Unsupported configuration name")
}
)
}
// Make original configuration unpublishable and not visible
originalConfiguration.isCanBeConsumed = false
originalConfiguration.isVisible = false
javaComponent.withVariantsFromConfiguration(originalConfiguration) {
skip()
}
}
}
}
// Fix common sources visibility for tests
sourceSets.named(SourceSet.TEST_SOURCE_SET_NAME) {
compileClasspath += commonSourceSet.output
runtimeClasspath += commonSourceSet.output
}
// Allowing to use 'internal' classes/methods from common source code
(extensions.getByName("kotlin") as KotlinSingleJavaTargetExtension).target.compilations.run {
getByName(SourceSet.TEST_SOURCE_SET_NAME).associateWith(getByName(commonSourceSet.name))
}
}
/**
* Adding plugin variants: https://docs.gradle.org/current/userguide/implementing_gradle_plugins.html#plugin-with-variants
*/
fun Project.createGradlePluginVariant(
variant: GradlePluginVariant,
commonSourceSet: SourceSet,
isGradlePlugin: Boolean = true,
): SourceSet {
val variantSourceSet = sourceSets.create(variant.sourceSetName) {
excludeGradleCommonDependencies(this)
wireGradleVariantToCommonGradleVariant(this, commonSourceSet)
}
plugins.withType<JavaLibraryPlugin>().configureEach {
extensions.configure<JavaPluginExtension> {
registerFeature(variantSourceSet.name) {
usingSourceSet(variantSourceSet)
if (isGradlePlugin) {
capability(project.group.toString(), project.name, project.version.toString())
}
if (kotlinBuildProperties.publishGradlePluginsJavadoc) {
withJavadocJar()
}
withSourcesJar()
}
configurations.named(variantSourceSet.apiElementsConfigurationName, commonVariantAttributes())
configurations.named(variantSourceSet.runtimeElementsConfigurationName, commonVariantAttributes())
configurations.create(variantSourceSet.embeddedConfigurationName) {
isCanBeConsumed = false
isCanBeResolved = true
attributes {
attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.JAVA_RUNTIME))
attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(LibraryElements.JAR))
}
}
}
tasks.named<Jar>(variantSourceSet.sourcesJarTaskName) {
addEmbeddedSources()
addEmbeddedSources(variantSourceSet.embeddedConfigurationName)
}
}
plugins.withId("java-gradle-plugin") {
tasks.named<Copy>(variantSourceSet.processResourcesTaskName) {
val copyPluginDescriptors = rootSpec.addChild()
copyPluginDescriptors.into("META-INF/gradle-plugins")
copyPluginDescriptors.from(tasks.named("pluginDescriptors"))
}
}
configurations.configureEach {
if (this@configureEach.name.startsWith(variantSourceSet.name)) {
attributes {
attribute(
GradlePluginApiVersion.GRADLE_PLUGIN_API_VERSION_ATTRIBUTE,
objects.named(variant.minimalSupportedGradleVersion)
)
}
}
}
// KT-52138: Make module name the same for all variants, so KSP could access internal methods/properties
tasks.named<KotlinCompile>("compile${variantSourceSet.name.replaceFirstChar { it.uppercase() }}Kotlin") {
@Suppress("DEPRECATION")
kotlinOptions {
moduleName = this@createGradlePluginVariant.name
}
}
dependencies {
variantSourceSet.compileOnlyConfigurationName(kotlinStdlib())
variantSourceSet.compileOnlyConfigurationName("dev.gradleplugins:gradle-api:${variant.gradleApiVersion}")
if (this@createGradlePluginVariant.name !in testPlugins) {
variantSourceSet.apiConfigurationName(project(":kotlin-gradle-plugin-api")) {
capabilities {
requireCapability("org.jetbrains.kotlin:kotlin-gradle-plugin-api-${variant.sourceSetName}")
}
}
}
}
return variantSourceSet
}
/**
* All additional configuration attributes in plugin variant should be the same as in the 'main' variant.
* Otherwise, Gradle <7.0 will fail to select plugin variant.
*/
private fun Project.commonVariantAttributes(): Action<Configuration> = Action<Configuration> {
attributes {
attribute(
TargetJvmEnvironment.TARGET_JVM_ENVIRONMENT_ATTRIBUTE,
objects.named(TargetJvmEnvironment.STANDARD_JVM)
)
}
}
fun Project.configureKotlinCompileTasksGradleCompatibility() {
tasks.withType<KotlinCompile>().configureEach {
compilerOptions {
// check https://docs.gradle.org/current/userguide/compatibility.html#kotlin for Kotlin-Gradle versions matrix
@Suppress("DEPRECATION") // we can't use language version greater than 1.5 as minimal supported Gradle embeds Kotlin 1.4
languageVersion.set(KotlinVersion.KOTLIN_1_5)
@Suppress("DEPRECATION") // we can't use api version greater than 1.4 as minimal supported Gradle version uses kotlin-stdlib 1.4
apiVersion.set(KotlinVersion.KOTLIN_1_4)
freeCompilerArgs.addAll(
listOf(
"-Xskip-prerelease-check",
"-Xsuppress-version-warnings",
// We have to override the default value for `-Xsam-conversions` to `class`
// otherwise the compiler would compile lambdas using invokedynamic,
// such lambdas are not serializable so are not compatible with Gradle configuration cache.
// It doesn't lead to a significant difference in binaries sizes, and previously (before LV 1.5) the `class` value was set by default.
"-Xsam-conversions=class",
)
)
}
}
}
// Will allow combining outputs of multiple SourceSets
fun Project.publishShadowedJar(
sourceSet: SourceSet,
commonSourceSet: SourceSet,
) {
val jarTask = tasks.named<Jar>(sourceSet.jarTaskName)
val shadowJarTask = embeddableCompilerDummyForDependenciesRewriting(
taskName = "$EMBEDDABLE_COMPILER_TASK_NAME${sourceSet.jarTaskName.replaceFirstChar { it.uppercase() }}"
) {
setupPublicJar(
jarTask.flatMap { it.archiveBaseName },
jarTask.flatMap { it.archiveClassifier }
)
addEmbeddedRuntime()
addEmbeddedRuntime(sourceSet.embeddedConfigurationName)
from(sourceSet.output)
from(commonSourceSet.output)
// When Gradle traverses the inputs, reject the shaded compiler JAR,
// which leads to the content of that JAR being excluded as well:
exclude {
// Docstring says `file` never returns null, but it does
@Suppress("UNNECESSARY_SAFE_CALL", "SAFE_CALL_WILL_CHANGE_NULLABILITY")
it.file?.name?.startsWith("kotlin-compiler-embeddable") ?: false
}
}
// Removing artifact produced by Jar task
if (sourceSet.name == SourceSet.MAIN_SOURCE_SET_NAME) {
configurations["${sourceSet.runtimeElementsConfigurationName}$FIXED_CONFIGURATION_SUFFIX"]
.artifacts.removeAll { true }
configurations["${sourceSet.apiElementsConfigurationName}$FIXED_CONFIGURATION_SUFFIX"]
.artifacts.removeAll { true }
} else {
configurations[sourceSet.runtimeElementsConfigurationName]
.artifacts.removeAll { true }
configurations[sourceSet.apiElementsConfigurationName]
.artifacts.removeAll { true }
}
// Adding instead artifact from shadow jar task
configurations {
artifacts {
if (sourceSet.name == SourceSet.MAIN_SOURCE_SET_NAME) {
add("${sourceSet.runtimeElementsConfigurationName}$FIXED_CONFIGURATION_SUFFIX", shadowJarTask)
add("${sourceSet.apiElementsConfigurationName}$FIXED_CONFIGURATION_SUFFIX", shadowJarTask)
} else {
add(sourceSet.apiElementsConfigurationName, shadowJarTask)
add(sourceSet.runtimeElementsConfigurationName, shadowJarTask)
}
}
}
}
fun Project.addBomCheckTask() {
val checkBomTask = tasks.register("checkGradlePluginsBom") {
group = "Validation"
description = "Check if project is added into Kotlin Gradle Plugins bom"
val bomBuildFile = project(":kotlin-gradle-plugins-bom").projectDir.resolve("build.gradle.kts")
val exceptions = listOf(
project(":gradle:android-test-fixes").path,
project(":gradle:gradle-warnings-detector").path,
project(":gradle:kotlin-compiler-args-properties").path,
project(":kotlin-gradle-build-metrics").path,
project(":kotlin-gradle-statistics").path,
)
val projectPath = this@addBomCheckTask.path
doLast {
if (projectPath in exceptions) return@doLast
val constraintsLines = bomBuildFile.readText()
.substringAfter("constraints {")
.substringBefore("}")
.split("\n")
.map { it.trim() }
val isContainingThisProject = constraintsLines.contains(
"api(project(\"$projectPath\"))"
)
if (!isContainingThisProject) {
throw GradleException(":kotlin-gradle-plugins-bom does not contain $projectPath project constraint!")
}
}
}
tasks.named("check") {
dependsOn(checkBomTask)
}
}
fun Project.configureDokkaPublication(
shouldLinkGradleApi: Boolean = false,
configurePublishingToKotlinlang: Boolean = false,
) {
val dokkaVersioningPluginVersion = "1.8.10"
dependencies {
implicitDependencies("org.jetbrains.dokka:javadoc-plugin:${DokkaVersion.version}")
implicitDependencies("org.jetbrains.dokka:versioning-plugin:$dokkaVersioningPluginVersion")
}
if (!kotlinBuildProperties.publishGradlePluginsJavadoc) return
plugins.apply("org.jetbrains.dokka")
plugins.withId("org.jetbrains.dokka") {
val commonSourceSet = sourceSets.getByName(commonSourceSetName)
GradlePluginVariant.values().forEach { pluginVariant ->
val variantSourceSet = sourceSets.getByName(pluginVariant.sourceSetName)
val dokkaTaskName = "dokka${variantSourceSet.javadocTaskName.replaceFirstChar { it.uppercase() }}"
val dokkaTask = if (tasks.names.contains(dokkaTaskName)) {
tasks.named<DokkaTask>(dokkaTaskName)
} else {
tasks.register<DokkaTask>(dokkaTaskName)
}
dokkaTask.configure {
description = "Generates documentation in 'javadoc' format for '${variantSourceSet.javadocTaskName}' variant"
plugins.dependencies.add(
project.dependencies.create("org.jetbrains.dokka:javadoc-plugin:${DokkaVersion.version}")
)
dokkaSourceSets {
named(commonSourceSet.name) {
suppress.set(false)
jdkVersion.set(8)
}
named(variantSourceSet.name) {
dependsOn(commonSourceSet)
suppress.set(false)
jdkVersion.set(8)
if (shouldLinkGradleApi) {
externalDocumentationLink {
url.set(URL(pluginVariant.gradleApiJavadocUrl))
addWorkaroundForElementList(pluginVariant)
}
}
}
}
}
tasks.named<Jar>(variantSourceSet.javadocJarTaskName) {
from(dokkaTask.flatMap { it.outputDirectory })
}
}
if (configurePublishingToKotlinlang) {
val latestVariant = GradlePluginVariant.values().last()
val olderVersionsDir = layout.buildDirectory.dir("dokka/kotlinlangDocumentationOld")
project.dependencies {
// Version is required due to https://github.com/Kotlin/dokka/issues/2812
"dokkaHtmlPlugin"("org.jetbrains.dokka:versioning-plugin:$dokkaVersioningPluginVersion")
}
tasks.register<DokkaTask>("dokkaKotlinlangDocumentation") {
description = "Generates documentation reference for Kotlinlang"
dokkaSourceSets {
pluginsMapConfiguration.put(
"org.jetbrains.dokka.base.DokkaBase",
"{ \"templatesDir\": \"${layout.projectDirectory.dir("dokka-template")}\" }"
)
pluginsMapConfiguration.put(
"org.jetbrains.dokka.versioning.VersioningPlugin",
olderVersionsDir.map { olderVersionsDir ->
"{ \"version\":\"$version\", \"olderVersionsDir\":\"${olderVersionsDir.asFile}\" }"
}
)
named(commonSourceSet.name) {
suppress.set(false)
jdkVersion.set(8)
}
named(latestVariant.sourceSetName) {
dependsOn(commonSourceSet)
suppress.set(false)
jdkVersion.set(8)
if (shouldLinkGradleApi) {
externalDocumentationLink {
url.set(URL(latestVariant.gradleApiJavadocUrl))
addWorkaroundForElementList(latestVariant)
}
}
}
}
}
}
}
}
// Workaround for https://github.com/Kotlin/dokka/issues/2097
// Gradle 7.6 javadoc does not have published 'package-list' file
private fun GradleExternalDocumentationLinkBuilder.addWorkaroundForElementList(pluginVariant: GradlePluginVariant) {
if (pluginVariant == GradlePluginVariant.GRADLE_76 ||
pluginVariant == GradlePluginVariant.GRADLE_80 ||
pluginVariant == GradlePluginVariant.GRADLE_81
) {
packageListUrl.set(URL("${pluginVariant.gradleApiJavadocUrl}element-list"))
}
}
private val SourceSet.embeddedConfigurationName get() = "${name}Embedded"
@@ -0,0 +1,96 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
import org.gradle.api.Project
interface CompatibilityPredicate {
fun matches(ide: Ide): Boolean
operator fun invoke(): Boolean = matches(IdeVersionConfigurator.currentIde)
operator fun invoke(block: () -> Unit): Unit {
if (matches(IdeVersionConfigurator.currentIde)) {
block()
}
}
}
val CompatibilityPredicate.not: CompatibilityPredicate get() = object : CompatibilityPredicate {
override fun matches(ide: Ide) = !this@not.matches(ide)
}
fun CompatibilityPredicate.or(other: CompatibilityPredicate): CompatibilityPredicate = object : CompatibilityPredicate {
override fun matches(ide: Ide) = this@or.matches(ide) || other.matches(ide)
}
enum class Platform : CompatibilityPredicate {
P213;
val version: Int = name.drop(1).toInt()
val displayVersion: String = "20${name.drop(1).dropLast(1)}.${name.last()}"
override fun matches(ide: Ide) = ide.platform == this
companion object {
operator fun get(version: Int): Platform {
return Platform.values().firstOrNull { it.version == version }
?: error("Can't find platform $version")
}
}
}
enum class Ide(val platform: Platform) : CompatibilityPredicate {
IJ213(Platform.P213);
val kind = Kind.values().first { it.shortName == name.take(2) }
val version = name.dropWhile { !it.isDigit() }.toInt()
val displayVersion: String = when (kind) {
Kind.IntelliJ -> "IJ${platform.displayVersion}"
Kind.AndroidStudio -> "Studio${name.substringAfter("AS").toCharArray().joinToString(separator = ".")}"
}
override fun matches(ide: Ide) = ide == this
enum class Kind(val shortName: String) {
AndroidStudio("AS"), IntelliJ("IJ")
}
companion object {
val IJ: CompatibilityPredicate = IdeKindPredicate(Kind.IntelliJ)
val AS: CompatibilityPredicate = IdeKindPredicate(Kind.AndroidStudio)
}
}
val Platform.orHigher get() = object : CompatibilityPredicate {
override fun matches(ide: Ide) = ide.platform.version >= version
}
val Platform.orLower get() = object : CompatibilityPredicate {
override fun matches(ide: Ide) = ide.platform.version <= version
}
val Ide.orHigher get() = object : CompatibilityPredicate {
override fun matches(ide: Ide) = ide.kind == kind && ide.version >= version
}
val Ide.orLower get() = object : CompatibilityPredicate {
override fun matches(ide: Ide) = ide.kind == kind && ide.version <= version
}
object IdeVersionConfigurator {
lateinit var currentIde: Ide
fun setCurrentIde(project: Project) {
val platformVersion = project.rootProject.extensions.extraProperties["versions.platform"].toString()
val ideName = if (platformVersion.startsWith("AS")) platformVersion else "IJ$platformVersion"
currentIde = Ide.valueOf(ideName)
}
}
private class IdeKindPredicate(val kind: Ide.Kind) : CompatibilityPredicate {
override fun matches(ide: Ide) = ide.kind == kind
}
@@ -0,0 +1,57 @@
import org.gradle.api.Action
import org.gradle.api.Task
import org.gradle.api.artifacts.Configuration
import org.gradle.api.tasks.compile.JavaCompile
import org.gradle.kotlin.dsl.provideDelegate
import org.gradle.kotlin.dsl.withGroovyBuilder
import java.io.File
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
class InstrumentJava(@Transient val javaInstrumentator: Configuration) : Action<Task> {
private val instrumentatorClasspath: String by lazy {
javaInstrumentator.asPath
}
override fun execute(task: Task) {
require(task is JavaCompile) { "$task is not of type JavaCompile!" }
// There is a requirement for Javac.execute() to have an existen non-empty directory as a source dir for the compilation
// even if compilation is disabled.
//
// So we create an empty dummy directory during the execution.
//
// See:
// Javac.execute() - https://github.com/apache/ant/blob/9943641/src/main/org/apache/tools/ant/taskdefs/Javac.java#L1086
// InstrumentIdeaExtensions - https://github.com/JetBrains/intellij-community/blob/9c40bdd/java/compiler/javac2/src/com/intellij/ant/InstrumentIdeaExtensions.java
// Javac2.compile() - https://github.com/JetBrains/intellij-community/blob/9c40bdd/java/compiler/javac2/src/com/intellij/ant/Javac2.java#L237
val dummyInstrumentSrcDir = File(task.project.buildDir, "instrument_dummy_src")
val dummyInstrumentSrcRelativePath = dummyInstrumentSrcDir.relativeTo(task.project.projectDir).path.replace("\\", "/")
task.doLast {
task.ant.withGroovyBuilder {
"taskdef"(
"name" to "instrumentIdeaExtensions",
"classpath" to instrumentatorClasspath,
"loaderref" to "java2.loader",
"classname" to "com.intellij.ant.InstrumentIdeaExtensions"
)
}
dummyInstrumentSrcDir.mkdirs()
task.ant.withGroovyBuilder {
"instrumentIdeaExtensions"(
"srcdir" to dummyInstrumentSrcRelativePath,
"destdir" to task.destinationDirectory.asFile.get(),
"classpath" to task.classpath.asPath,
"includeantruntime" to false,
"instrumentNotNull" to true
)
}
}
}
}
@@ -0,0 +1,213 @@
@file:JvmName("JvmToolchain")
import org.gradle.api.Project
import org.gradle.api.plugins.JavaPluginExtension
import org.gradle.api.provider.Provider
import org.gradle.api.tasks.compile.JavaCompile
import org.gradle.jvm.toolchain.*
import org.gradle.kotlin.dsl.getByType
import org.gradle.kotlin.dsl.withType
import org.jetbrains.kotlin.gradle.dsl.KotlinTopLevelExtension
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
enum class JdkMajorVersion(
val majorVersion: Int,
val targetName: String = majorVersion.toString(),
private val overrideMajorVersion: Int? = null,
private val mandatory: Boolean = true
) {
JDK_1_6(6, targetName = "1.6", overrideMajorVersion = 8),
JDK_1_7(7, targetName = "1.7", overrideMajorVersion = 8),
JDK_1_8(8, targetName = "1.8"),
JDK_9_0(9, overrideMajorVersion = 11),
JDK_10_0(10, mandatory = false, overrideMajorVersion = 11),
JDK_11_0(11, mandatory = false),
JDK_16_0(16, mandatory = false),
JDK_17_0(17, mandatory = false),
JDK_21_0(21, mandatory = false);
fun isMandatory(): Boolean = mandatory
val overrideVersion by lazy {
if (overrideMajorVersion != null) {
values().firstOrNull() { it.majorVersion == overrideMajorVersion }
?: error("Can't find the value with majorVersion=$overrideMajorVersion")
} else {
null
}
}
val envName = name
}
val DEFAULT_JVM_TOOLCHAIN = JdkMajorVersion.JDK_1_8
fun Project.configureJvmDefaultToolchain() {
configureJvmToolchain(DEFAULT_JVM_TOOLCHAIN)
}
fun Project.shouldOverrideObsoleteJdk(jdkVersion: JdkMajorVersion): Boolean =
kotlinBuildProperties.isObsoleteJdkOverrideEnabled && jdkVersion.overrideVersion != null
fun Project.configureJvmToolchain(jdkVersion: JdkMajorVersion) {
@Suppress("NAME_SHADOWING")
val jdkVersion = chooseJdk_1_8ForJpsBuild(jdkVersion)
// Ensure java only modules also set default toolchain
configureJavaOnlyToolchain(jdkVersion)
plugins.withId("org.jetbrains.kotlin.jvm") {
val kotlinExtension = extensions.getByType<KotlinTopLevelExtension>()
if (shouldOverrideObsoleteJdk(jdkVersion)) {
kotlinExtension.jvmToolchain {
setupToolchain(jdkVersion.overrideVersion ?: error("Substitution version should be defined for override mode"))
}
updateJvmTarget(jdkVersion.targetName)
} else {
kotlinExtension.jvmToolchain {
setupToolchain(jdkVersion)
}
}
tasks
.matching { it.name != "compileJava9Java" && it is JavaCompile }
.configureEach {
with(this as JavaCompile) {
options.compilerArgs.add("-proc:none")
options.encoding = "UTF-8"
}
}
}
}
fun JavaToolchainSpec.setupToolchain(jdkVersion: JdkMajorVersion) {
languageVersion.set(JavaLanguageVersion.of(jdkVersion.majorVersion))
}
fun Project.configureJavaOnlyToolchain(
jdkVersion: JdkMajorVersion
) {
@Suppress("NAME_SHADOWING")
val jdkVersion = chooseJdk_1_8ForJpsBuild(jdkVersion)
plugins.withId("java-base") {
val javaExtension = extensions.getByType<JavaPluginExtension>()
if (shouldOverrideObsoleteJdk(jdkVersion)) {
javaExtension.toolchain {
setupToolchain(jdkVersion.overrideVersion ?: error("Substitution version should be defined for override mode"))
}
tasks.withType<JavaCompile>().configureEach {
targetCompatibility = jdkVersion.targetName
sourceCompatibility = jdkVersion.targetName
}
} else {
javaExtension.toolchain {
setupToolchain(jdkVersion)
}
}
}
}
fun Project.chooseJdk_1_8ForJpsBuild(jdkVersion: JdkMajorVersion): JdkMajorVersion {
return if (kotlinBuildProperties.isInJpsBuildIdeaSync) {
maxOf(jdkVersion, JdkMajorVersion.JDK_1_8)
} else {
jdkVersion
}
}
fun KotlinCompile.configureTaskToolchain(
jdkVersion: JdkMajorVersion
) {
if (project.shouldOverrideObsoleteJdk(jdkVersion)) {
kotlinJavaToolchain.toolchain.use(
project.getToolchainLauncherFor(
jdkVersion.overrideVersion ?: error("Substitution version should be defined for override mode")
)
)
@Suppress("DEPRECATION")
kotlinOptions {
jvmTarget = jdkVersion.targetName
}
} else {
kotlinJavaToolchain.toolchain.use(
project.getToolchainLauncherFor(jdkVersion)
)
}
}
fun JavaCompile.configureTaskToolchain(
jdkVersion: JdkMajorVersion
) {
if (project.shouldOverrideObsoleteJdk(jdkVersion)) {
javaCompiler.set(
project.getToolchainCompilerFor(
jdkVersion.overrideVersion ?: error("Substitution version should be defined for override mode")
)
)
targetCompatibility = jdkVersion.targetName
sourceCompatibility = jdkVersion.targetName
} else {
javaCompiler.set(project.getToolchainCompilerFor(jdkVersion))
}
}
fun Project.updateJvmTarget(
jvmTarget: String
) {
@Suppress("NAME_SHADOWING")
val jvmTarget = if (kotlinBuildProperties.isInJpsBuildIdeaSync && jvmTarget == "1.6") {
"1.8"
} else {
jvmTarget
}
// Java 9 tasks are exceptions that are configured in configureJava9Compilation
tasks
.withType<KotlinCompile>()
.matching { it.name != "compileJava9Kotlin" }
.configureEach {
@Suppress("DEPRECATION")
kotlinOptions.jvmTarget = jvmTarget
}
tasks
.withType<JavaCompile>()
.matching { it.name != "compileJava9Java" }
.configureEach {
sourceCompatibility = jvmTarget
targetCompatibility = jvmTarget
}
}
private fun Project.getToolchainCompilerFor(
jdkVersion: JdkMajorVersion
): Provider<JavaCompiler> {
val service = project.extensions.getByType<JavaToolchainService>()
return service.compilerFor {
this.languageVersion.set(JavaLanguageVersion.of(jdkVersion.majorVersion))
}
}
fun Project.getToolchainLauncherFor(
jdkVersion: JdkMajorVersion
): Provider<JavaLauncher> {
val service = project.extensions.getByType<JavaToolchainService>()
val jdkVersionWithOverride = project.getJdkVersionWithOverride(jdkVersion)
return service.launcherFor {
this.languageVersion.set(JavaLanguageVersion.of(jdkVersionWithOverride.majorVersion))
}
}
fun Project.getToolchainJdkHomeFor(jdkVersion: JdkMajorVersion): Provider<String> {
return getToolchainLauncherFor(jdkVersion).map {
it.metadata.installationPath.asFile.absolutePath
}
}
fun Project.getJdkVersionWithOverride(jdkVersion: JdkMajorVersion): JdkMajorVersion {
return if (project.shouldOverrideObsoleteJdk(jdkVersion)) {
jdkVersion.overrideVersion ?: error("Substitution version should be defined for override mode")
} else {
jdkVersion
}
}
@@ -0,0 +1,110 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
@file:JvmName("LibrariesCommon")
import org.gradle.api.JavaVersion
import org.gradle.api.Project
import org.gradle.api.file.FileCollection
import org.gradle.api.java.archives.Manifest
import org.gradle.api.plugins.BasePluginExtension
import org.gradle.api.tasks.compile.JavaCompile
import org.gradle.kotlin.dsl.*
import org.gradle.process.CommandLineArgumentProvider
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile
@JvmOverloads
fun Project.configureJava9Compilation(
moduleName: String,
moduleOutputs: Collection<FileCollection> = setOf(sourceSets["main"].output)
) {
configurations["java9CompileClasspath"].extendsFrom(configurations["compileClasspath"])
tasks.named("compileJava9Kotlin", KotlinCompile::class.java) {
configureTaskToolchain(JdkMajorVersion.JDK_9_0)
@Suppress("DEPRECATION")
kotlinOptions.jvmTarget = JdkMajorVersion.JDK_9_0.targetName
}
tasks.named("compileJava9Java", JavaCompile::class.java) {
dependsOn(moduleOutputs)
targetCompatibility = JavaVersion.VERSION_1_9.toString()
sourceCompatibility = JavaVersion.VERSION_1_9.toString()
configureTaskToolchain(JdkMajorVersion.JDK_9_0)
// module-info.java should be in java9 source set by convention
val java9SourceSet = sourceSets["java9"].java
destinationDirectory.set(java9SourceSet.destinationDirectory.asFile.get().resolve("META-INF/versions/9"))
options.sourcepath = files(java9SourceSet.srcDirs)
val compileClasspath = configurations["java9CompileClasspath"]
val moduleFiles = objects.fileCollection().from(moduleOutputs)
val modulePath = compileClasspath.filter { it !in moduleFiles.files }
classpath = objects.fileCollection().from()
options.compilerArgumentProviders.add(
Java9AdditionalArgumentsProvider(
moduleName,
moduleFiles,
modulePath
)
)
}
}
private class Java9AdditionalArgumentsProvider(
private val moduleName: String,
private val moduleFiles: FileCollection,
private val modulePath: FileCollection
) : CommandLineArgumentProvider {
override fun asArguments(): Iterable<String> = listOf(
"--module-path", modulePath.asPath,
"--patch-module", "$moduleName=${moduleFiles.asPath}",
"-Xlint:-requires-transitive-automatic" // suppress automatic module transitive dependencies in kotlin.test
)
}
fun Project.configureFrontendIr() = tasks.withType<KotlinJvmCompile>().configureEach {
compilerOptions {
if (project.kotlinBuildProperties.useFirForLibraries) {
freeCompilerArgs.add("-Xuse-k2")
allWarningsAsErrors.set(false)
} else if (project.kotlinBuildProperties.useFir) {
freeCompilerArgs.add("-Xskip-prerelease-check")
}
val renderDiagnosticNames by extra(project.kotlinBuildProperties.renderDiagnosticNames)
if (renderDiagnosticNames) {
freeCompilerArgs.add("-Xrender-internal-diagnostic-names")
}
}
}
@JvmOverloads
fun Project.manifestAttributes(
manifest: Manifest,
component: String? = null,
multiRelease: Boolean = false
) {
manifest.attributes(
"Implementation-Vendor" to "JetBrains",
"Implementation-Title" to project.extensions.getByType<BasePluginExtension>().archivesName,
"Implementation-Version" to project.rootProject.extra["buildNumber"] as String
)
if (component != null) {
val kotlinLanguageVersion: String by rootProject.extra
manifest.attributes(
"Kotlin-Runtime-Component" to component,
"Kotlin-Version" to kotlinLanguageVersion
)
}
if (multiRelease) {
manifest.attributes(
"Multi-Release" to "true"
)
}
}
@@ -0,0 +1,19 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
import org.gradle.api.tasks.JavaExec
/**
* Workaround for IDEA-200192:
* IDEA makes all JavaExec tasks not up-to-date and attaches debugger making our breakpoints trigger during irrelevant task execution
*/
open class NoDebugJavaExec : JavaExec() {
private fun String.isDebuggerArgument(): Boolean =
startsWith("-agentlib:jdwp=transport=dt_socket,server=n,suspend=y,address=")
override fun setJvmArgs(arguments: MutableList<String>?) {
super.setJvmArgs(arguments?.filterNot { it.isDebuggerArgument() })
}
}
@@ -0,0 +1,50 @@
import org.gradle.api.Project
import org.gradle.api.tasks.JavaExec
import org.gradle.jvm.tasks.Jar
import org.gradle.kotlin.dsl.creating
import org.gradle.kotlin.dsl.task
/*
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
// creating class eagerly here: using register causes problems due to quite complicated relationships between these tasks
fun Project.smartJavaExec(configure: JavaExec.() -> Unit) = tasks.creating(JavaExec::class) {
configure()
passClasspathInJar()
}
// Moves the classpath into a jar metadata, to shorten the command line length and to avoid hitting the limit on Windows
fun JavaExec.passClasspathInJar() {
val jarTask = project.task("${name}WriteClassPath", Jar::class) {
val classpath = classpath
val main = mainClass.get()
dependsOn(classpath)
inputs.files(classpath)
inputs.property("main", main)
archiveFileName.set("$main.${this@passClasspathInJar.name}.classpath.container.jar")
destinationDirectory.set(temporaryDir)
doFirst {
val classPathString = classpath.joinToString(" ") {
it.toURI().toString()
}
manifest {
attributes(
mapOf(
"Class-Path" to classPathString,
"Main-Class" to main
)
)
}
}
}
dependsOn(jarTask)
mainClass.set("-jar")
classpath = project.files()
args = listOf(jarTask.outputs.files.singleFile.path) + args.orEmpty()
}
@@ -0,0 +1,74 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
import org.gradle.api.Action
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.execution.TaskExecutionGraph
import org.gradle.api.tasks.TaskProvider
/* This file is solely needed to suppress implicit dependencies on npm and yarn tasks registered by Kotlin Gradle Plugin */
private val rootNpmRelatedTasks = setOf("kotlinNpmInstall", "kotlinStoreYarnLock")
private val allowImplicitDependOnNpmForTasks = setOf("kotlinUpgradeYarnLock", "compileTestKotlinJs", "compileTestKotlinWasm")
private fun findRootTasks(taskGraph: TaskExecutionGraph): List<Task> {
val allDependentTasksPaths = mutableSetOf<String>()
val allTasksPaths = mutableSetOf<String>()
taskGraph.allTasks.forEach { task ->
allTasksPaths.add(task.path)
for (dependency in task.taskDependencies.getDependencies(task)) {
allDependentTasksPaths.add(dependency.path)
}
}
val rootTasksPaths = allTasksPaths - allDependentTasksPaths
return taskGraph.allTasks.filter { task -> task.path in rootTasksPaths }
}
private fun tasksGraphString(taskGraph: TaskExecutionGraph, ident: String = "", nextIdent: (String) -> String = { "$it|-"}): String {
return buildString {
val alreadyPrintedTasks = mutableSetOf<String>()
fun Task.printTree(indent: String) {
append(indent)
if (path in alreadyPrintedTasks) {
appendLine("$path *")
} else {
alreadyPrintedTasks.add(path)
appendLine(path)
val nextIndent = nextIdent(indent)
for (dependency in taskDependencies.getDependencies(this)) {
dependency.printTree(nextIndent)
}
}
}
for (rootTask in findRootTasks(taskGraph)) {
rootTask.printTree(ident)
}
}
}
val Project.checkYarnAndNPMSuppressed: Action<TaskExecutionGraph> get() {
return Action<TaskExecutionGraph> {
val disableNpmYarnCheck = providers.gradleProperty("kotlin.build.disable.npmyarn.suppress.check")
.orNull?.toBoolean() ?: false
if (disableNpmYarnCheck) return@Action
val executeTaskNames = allTasks.filter { it.enabled }.map { it.name }.toSet()
val npmYarnTasks = rootNpmRelatedTasks.filter { it in executeTaskNames }
val allowedTask = allowImplicitDependOnNpmForTasks.filter { it in executeTaskNames }
if (npmYarnTasks.isNotEmpty()) {
if (allowedTask.isEmpty()) {
error("$npmYarnTasks tasks shouldn't be present in the task graph: $npmYarnTasks " +
"as $allowImplicitDependOnNpmForTasks tasks were not activated\n" +
"Graph:\n${tasksGraphString(this)}")
}
}
}
}
@@ -0,0 +1,79 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
import GradlePropertyIssue.MissingProperty
import GradlePropertyIssue.UnexpectedPropertyValue
import org.gradle.api.Project
/**
* Mechanism to warn developers when a given Gradle property does not match the developer's expectation.
*
* There may be some Gradle properties, that are defined in the project and will change over time (e.g. defaultSnapshotVersion).
* Some developers (and QA) will need to be very clear about the value of this property.
*
* In order to get notified about the value of the property changing, it is possible to define the same property in
* ~/.gradle/gradle.properties with a given `.kotlin_build.expected_value` suffix to ensure the value.
*
* e.g. if a developer set's
*
* `defaultSnapshotVersion.kotlin_build.expected_value=1.6.255-SNAPSHOT` and the value gets bumped to `1.9.255-SNAPSHOT` after pulling from master,
* the developer will notice this during project configuration phase.
*/
fun Project.checkExpectedGradlePropertyValues() {
val expectSuffix = ".kotlin_build.expected_value"
val expectKeys = properties.keys.filter { it.endsWith(expectSuffix) }
val issues = expectKeys.mapNotNull { expectKey ->
val actualKey = expectKey.removeSuffix(expectSuffix)
val expectedValue = properties[expectKey]?.toString() ?: return@mapNotNull null
if (!properties.containsKey(actualKey))
return@mapNotNull MissingProperty(actualKey, expectedValue)
val actualValue = properties[actualKey].toString()
if (expectedValue != actualValue)
return@mapNotNull UnexpectedPropertyValue(actualKey, expectedValue, actualValue)
null
}.toSet()
if (issues.isEmpty()) {
return
}
val unexpectedPropertyValues = issues.filterIsInstance<UnexpectedPropertyValue>()
val missingProperties = issues.filterIsInstance<MissingProperty>()
throw IllegalArgumentException(
buildString {
if (unexpectedPropertyValues.isNotEmpty()) {
appendLine("Unexpected Gradle property values found in ${project.displayName}:")
unexpectedPropertyValues.forEach { issue ->
appendLine("Expected ${issue.key} to be '${issue.expectedValue}', but found '${issue.actualValue}'")
}
}
if (missingProperties.isNotEmpty()) {
if (unexpectedPropertyValues.isNotEmpty()) appendLine()
appendLine("Missing Gradle properties found in ${project.displayName}:")
missingProperties.forEach { issue ->
appendLine("Expected ${issue.key} to be '${issue.expectedValue}', but the property is missing")
}
}
}
)
}
private sealed class GradlePropertyIssue {
data class UnexpectedPropertyValue(
val key: String, val expectedValue: String, val actualValue: String
) : GradlePropertyIssue()
data class MissingProperty(
val key: String, val expectedValue: String
) : GradlePropertyIssue()
}
@@ -0,0 +1,310 @@
import org.gradle.internal.deprecation.DeprecatableConfiguration
import org.jetbrains.kotlin.gradle.plugin.KotlinBasePluginWrapper
// Contains common configuration that should be applied to all projects
// Common Group and version
val kotlinVersion: String by rootProject.extra
group = "org.jetbrains.kotlin"
version = kotlinVersion
// Forcing minimal gson dependency version
val gsonVersion = rootProject.extra["versions.gson"] as String
dependencies {
constraints {
configurations.all {
if (isCanBeResolved && !isCanBeConsumed) {
allDependencies.configureEach {
if (group == "com.google.code.gson" && name == "gson" && this@all.isCanBeDeclared) {
this@constraints.add(this@all.name, "com.google.code.gson:gson") {
version {
require(gsonVersion)
}
because("Force using same gson version because of https://github.com/google/gson/pull/1991")
}
}
}
}
}
}
}
project.configureJvmDefaultToolchain()
project.addEmbeddedConfigurations()
project.addImplicitDependenciesConfiguration()
project.configureJavaCompile()
project.configureJavaBasePlugin()
project.configureKotlinCompilationOptions()
project.configureArtifacts()
project.configureTests()
// There are problems with common build dir:
// - some tests (in particular js and binary-compatibility-validator depend on the fixed (default) location
// - idea seems unable to exclude common buildDir from indexing
// therefore it is disabled by default
// buildDir = File(commonBuildDir, project.name)
afterEvaluate {
run configureCompilerClasspath@{
val bootstrapCompilerClasspath by rootProject.buildscript.configurations
configurations.findByName("kotlinCompilerClasspath")?.let {
dependencies.add(it.name, files(bootstrapCompilerClasspath))
}
configurations.findByName("kotlinCompilerPluginClasspath")
?.exclude("org.jetbrains.kotlin", "kotlin-scripting-compiler-embeddable")
}
}
fun Project.addImplicitDependenciesConfiguration() {
configurations.maybeCreate("implicitDependencies").apply {
isCanBeConsumed = false
isCanBeResolved = false
}
}
fun Project.addEmbeddedConfigurations() {
configurations.maybeCreate("embedded").apply {
isCanBeConsumed = false
isCanBeResolved = true
attributes {
attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.JAVA_RUNTIME))
attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(LibraryElements.JAR))
}
}
configurations.maybeCreate("embeddedElements").apply {
extendsFrom(configurations["embedded"])
isCanBeConsumed = true
isCanBeResolved = false
attributes {
attribute(Usage.USAGE_ATTRIBUTE, objects.named("embedded-java-runtime"))
}
}
}
fun Project.configureJavaCompile() {
plugins.withType<JavaPlugin> {
tasks.withType<JavaCompile>().configureEach {
options.compilerArgs.add("-Xlint:deprecation")
options.compilerArgs.add("-Xlint:unchecked")
options.compilerArgs.add("-Werror")
}
}
}
fun Project.configureJavaBasePlugin() {
plugins.withId("java-base") {
fun File.toProjectRootRelativePathOrSelf() = (relativeToOrNull(rootDir)?.takeUnless { it.startsWith("..") } ?: this).path
fun FileCollection.printClassPath(role: String) =
println("${project.path} $role classpath:\n ${joinToString("\n ") { it.toProjectRootRelativePathOrSelf() }}")
val javaExtension = javaPluginExtension()
tasks {
register("printCompileClasspath") { doFirst { javaExtension.sourceSets["main"].compileClasspath.printClassPath("compile") } }
register("printRuntimeClasspath") { doFirst { javaExtension.sourceSets["main"].runtimeClasspath.printClassPath("runtime") } }
register("printTestCompileClasspath") { doFirst { javaExtension.sourceSets["test"].compileClasspath.printClassPath("test compile") } }
register("printTestRuntimeClasspath") { doFirst { javaExtension.sourceSets["test"].runtimeClasspath.printClassPath("test runtime") } }
}
}
}
fun Project.configureKotlinCompilationOptions() {
plugins.withType<KotlinBasePluginWrapper> {
val commonCompilerArgs = listOfNotNull(
"-opt-in=kotlin.RequiresOptIn",
"-progressive".takeIf { getBooleanProperty("test.progressive.mode") ?: false }
)
val kotlinLanguageVersion: String by rootProject.extra
val useJvmFir by extra(project.kotlinBuildProperties.useFir)
val useFirLT by extra(project.kotlinBuildProperties.useFirWithLightTree)
val useFirIC by extra(project.kotlinBuildProperties.useFirTightIC)
val renderDiagnosticNames by extra(project.kotlinBuildProperties.renderDiagnosticNames)
@Suppress("DEPRECATION")
tasks.withType<org.jetbrains.kotlin.gradle.dsl.KotlinCompile<*>>().configureEach {
kotlinOptions {
languageVersion = kotlinLanguageVersion
apiVersion = kotlinLanguageVersion
freeCompilerArgs += commonCompilerArgs
}
val relativePathBaseArg: String? =
"-Xklib-relative-path-base=$buildDir,$projectDir,$rootDir".takeIf {
!kotlinBuildProperties.getBoolean("kotlin.build.use.absolute.paths.in.klib")
}
// Workaround to avoid remote build cache misses due to absolute paths in relativePathBaseArg
doFirst {
if (relativePathBaseArg != null) {
@Suppress("DEPRECATION")
kotlinOptions.freeCompilerArgs += relativePathBaseArg
}
}
}
val jvmCompilerArgs = listOf(
"-Xno-optimized-callable-references",
"-Xno-kotlin-nothing-value-exception",
)
val coreLibProjects: List<String> by rootProject.extra
val projectsWithDisabledFirBootstrap = coreLibProjects + listOf(
":kotlin-gradle-plugin",
":kotlinx-metadata",
":kotlinx-metadata-jvm",
// For some reason stdlib isn't imported correctly for this module
// Probably it's related to kotlin-test module usage
":kotlin-gradle-statistics",
// Requires serialization plugin
":wasm:wasm.ir",
// Uses multiplatform
":kotlin-stdlib-jvm-minimal-for-test",
":kotlin-native:endorsedLibraries:kotlinx.cli",
":kotlin-native:klib",
// Requires serialization plugin
":js:js.tests",
)
// TODO: fix remaining warnings and remove this property.
val tasksWithWarnings = listOf(
":kotlin-gradle-plugin:compileCommonKotlin",
":kotlin-native:build-tools:compileKotlin"
)
val projectsWithEnabledContextReceivers: List<String> by rootProject.extra
val projectsWithOptInToUnsafeCastFunctionsFromAddToStdLib: List<String> by rootProject.extra
@Suppress("SuspiciousCollectionReassignment", "DEPRECATION")
tasks.withType<org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompile>().configureEach {
kotlinOptions {
freeCompilerArgs += jvmCompilerArgs
if (useJvmFir) {
if (project.path !in projectsWithDisabledFirBootstrap) {
freeCompilerArgs += "-Xuse-k2"
freeCompilerArgs += "-Xabi-stability=stable"
if (useFirLT) {
freeCompilerArgs += "-Xuse-fir-lt"
}
if (useFirIC) {
freeCompilerArgs += "-Xuse-fir-ic"
}
} else {
freeCompilerArgs += "-Xskip-prerelease-check"
}
}
if (renderDiagnosticNames) {
freeCompilerArgs += "-Xrender-internal-diagnostic-names"
}
if (path !in tasksWithWarnings) {
allWarningsAsErrors = !kotlinBuildProperties.disableWerror
}
if (project.path in projectsWithEnabledContextReceivers) {
freeCompilerArgs += "-Xcontext-receivers"
}
if (project.path in projectsWithOptInToUnsafeCastFunctionsFromAddToStdLib) {
freeCompilerArgs += "-opt-in=org.jetbrains.kotlin.utils.addToStdlib.UnsafeCastFunction"
}
if (project.path == ":kotlin-util-klib") {
// This is a temporary workaround for a configuration problem in kotlin-native. Namely, module `:kotlin-native-shared`
// depends on kotlin-util-klib from bootstrap for some reason (see `kotlin-native/shared/build.gradle.kts`), but when
// we're packing dependencies for the use in the IDE, we pass paths to the newly built libraries to Proguard
// (see `prepare/ide-plugin-dependencies/kotlin-backend-native-for-ide/build.gradle.kts`).
//
// So the code which was compiled against one version of a library, is analyzed by Proguard against another version.
//
// This is a bad situation for JVM default flag behavior specifically. If kotlin-util-klib from bootstrap is compiled
// in the old mode (with DefaultImpls for interfaces), then subclasses in kotlin-native-shared will also be generated
// in the old mode (with DefaultImpls). But then Proguard will analyze these subclasses and their DefaultImpls classes,
// and will observe calls to non-existing methods from DefaultImpls of the interfaces in kotlin-util-klib, and report
// an error.
//
// This change will most likely not be needed after the bootstrap, as soon as kotlin-util-klib is compiled with
// `-Xjvm-default=all`.
freeCompilerArgs += "-Xjvm-default=all-compatibility"
} else if (!skipJvmDefaultAllForModule(project.path)) {
freeCompilerArgs += "-Xjvm-default=all"
}
}
}
}
}
fun Project.configureArtifacts() {
tasks.withType<Javadoc>().configureEach {
enabled = false
}
tasks.withType<Jar>().configureEach {
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}
tasks.withType<AbstractArchiveTask>().configureEach {
isPreserveFileTimestamps = false
isReproducibleFileOrder = true
val `rw-r--r--` = 0b110100100
val `rwxr-xr-x` = 0b111101101
fileMode = `rw-r--r--`
dirMode = `rwxr-xr-x`
filesMatching("**/bin/*") { mode = `rwxr-xr-x` }
filesMatching("**/bin/*.bat") { mode = `rw-r--r--` }
}
normalization {
runtimeClasspath {
ignore("META-INF/MANIFEST.MF")
ignore("META-INF/compiler.version")
ignore("META-INF/plugin.xml")
ignore("kotlin/KotlinVersionCurrentValue.class")
}
}
fun Task.listConfigurationContents(configName: String) {
doFirst {
project.configurations.findByName(configName)?.let {
println("$configName configuration files:\n${it.allArtifacts.files.files.joinToString("\n ", " ")}")
}
}
}
tasks.register("listArchives") { listConfigurationContents("archives") }
tasks.register("listDistJar") { listConfigurationContents("distJar") }
}
fun Project.configureTests() {
val ignoreTestFailures: Boolean by rootProject.extra
tasks.configureEach {
if (this is VerificationTask) {
ignoreFailures = ignoreTestFailures
}
}
tasks.withType<Test>().configureEach {
outputs.doNotCacheIf("https://youtrack.jetbrains.com/issue/KTI-112") { true }
}
// Aggregate task for build related checks
tasks.register("checkBuild")
configureTestRetriesForTestTasks()
}
// TODO: migrate remaining modules to the new JVM default scheme.
fun skipJvmDefaultAllForModule(path: String): Boolean =
// Gradle plugin modules are disabled because different Gradle versions bundle different Kotlin compilers,
// and not all of them support the new JVM default scheme.
"-gradle" in path || "-runtime" in path || path == ":kotlin-project-model" ||
// Visitor/transformer interfaces in ir.tree are very sensitive to the way interface methods are implemented.
// Enabling default method generation results in a performance loss of several % on full pipeline test on Kotlin.
// TODO: investigate the performance difference and enable new mode for ir.tree.
path == ":compiler:ir.tree" ||
// Workaround a Proguard issue:
// java.lang.IllegalAccessError: tried to access method kotlin.reflect.jvm.internal.impl.types.checker.ClassicTypeSystemContext$substitutionSupertypePolicy$2.<init>(
// Lkotlin/reflect/jvm/internal/impl/types/checker/ClassicTypeSystemContext;Lkotlin/reflect/jvm/internal/impl/types/TypeSubstitutor;
// )V from class kotlin.reflect.jvm.internal.impl.resolve.OverridingUtilTypeSystemContext
// KT-54749
path == ":core:descriptors"
@@ -0,0 +1,28 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
import org.gradle.api.Action
import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.NamedDomainObjectProvider
import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.Dependency
import org.gradle.api.artifacts.ExternalModuleDependency
import org.gradle.api.artifacts.ModuleDependency
import org.gradle.api.artifacts.dsl.DependencyHandler
import org.gradle.kotlin.dsl.accessors.runtime.addDependencyTo
import org.gradle.kotlin.dsl.accessors.runtime.addExternalModuleDependencyTo
import org.gradle.kotlin.dsl.add
val NamedDomainObjectContainer<Configuration>.embedded: NamedDomainObjectProvider<Configuration>
get() = named("embedded")
fun DependencyHandler.embedded(dependencyNotation: Any): Dependency? =
add("embedded", dependencyNotation)
val NamedDomainObjectContainer<Configuration>.implicitDependencies: NamedDomainObjectProvider<Configuration>
get() = named("implicitDependencies")
fun DependencyHandler.implicitDependencies(dependencyNotation: Any): Dependency? =
add("implicitDependencies", dependencyNotation)
@@ -0,0 +1,177 @@
@file:Suppress("unused") // usages in build scripts are not tracked properly
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
import org.gradle.api.Project
import org.gradle.api.artifacts.DependencySubstitution
import org.gradle.api.artifacts.component.ProjectComponentSelector
import org.gradle.api.attributes.LibraryElements
import org.gradle.api.attributes.Usage
import org.gradle.api.file.DuplicatesStrategy
import org.gradle.api.file.FileCollection
import org.gradle.api.tasks.TaskProvider
import org.gradle.jvm.tasks.Jar
import org.gradle.kotlin.dsl.exclude
import org.gradle.kotlin.dsl.named
import org.gradle.kotlin.dsl.project
import org.gradle.kotlin.dsl.register
import java.io.File
const val kotlinEmbeddableRootPackage = "org.jetbrains.kotlin"
val packagesToRelocate =
listOf(
"com.intellij",
"com.google",
"com.sampullara",
"org.apache",
"org.jdom",
"org.picocontainer",
"org.jline",
"org.fusesource",
"net.jpountz",
"one.util.streamex",
"it.unimi.dsi.fastutil",
"kotlinx.collections.immutable",
"com.fasterxml",
"org.codehaus"
)
// The shaded compiler "dummy" is used to rewrite dependencies in projects that are used with the embeddable compiler
// on the runtime and use some shaded dependencies from the compiler
// To speed-up rewriting process we want to have this dummy as small as possible.
// But due to the shadow plugin bug (https://github.com/johnrengelman/shadow/issues/262) it is not possible to use
// packagesToRelocate list to for the include list. Therefore the exclude list has to be created.
val packagesToExcludeFromDummy =
listOf(
"org/jetbrains/kotlin/**",
"org/intellij/lang/annotations/**",
"org/jetbrains/jps/**",
"META-INF/**",
"com/sun/jna/**",
"com/thoughtworks/xstream/**",
"javaslang/**",
"*.proto",
"messages/**",
"net/sf/cglib/**",
"one/util/streamex/**",
"org/iq80/snappy/**",
"org/jline/**",
"org/xmlpull/**",
"*.txt"
)
private fun ShadowJar.configureEmbeddableCompilerRelocation(withJavaxInject: Boolean = true) {
relocate("com.google.protobuf", "org.jetbrains.kotlin.protobuf")
packagesToRelocate.forEach {
relocate(it, "$kotlinEmbeddableRootPackage.$it")
}
if (withJavaxInject) {
relocate("javax.inject", "$kotlinEmbeddableRootPackage.javax.inject")
}
relocate("org.fusesource", "$kotlinEmbeddableRootPackage.org.fusesource") {
// TODO: remove "it." after #KT-12848 get addressed
exclude("org.fusesource.jansi.internal.CLibrary")
}
}
private fun Project.compilerShadowJar(taskName: String, body: ShadowJar.() -> Unit): TaskProvider<ShadowJar> {
val compilerJar = configurations.getOrCreate("compilerJar").apply {
isCanBeConsumed = false
isCanBeResolved = true
attributes {
attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.JAVA_RUNTIME))
attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(LibraryElements.JAR))
}
}
dependencies.add(compilerJar.name, dependencies.project(":kotlin-compiler")) { isTransitive = false }
return tasks.register<ShadowJar>(taskName) {
destinationDirectory.set(project.file(File(buildDir, "libs")))
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
from(compilerJar)
body()
}
}
fun Project.embeddableCompiler(taskName: String = "embeddable", body: ShadowJar.() -> Unit = {}): TaskProvider<ShadowJar> =
compilerShadowJar(taskName) {
configureEmbeddableCompilerRelocation()
body()
}
fun Project.compilerDummyForDependenciesRewriting(
taskName: String = "compilerDummy", body: ShadowJar.() -> Unit = {}
): TaskProvider<out Jar> =
compilerShadowJar(taskName) {
exclude(packagesToExcludeFromDummy)
body()
}
const val COMPILER_DUMMY_JAR_CONFIGURATION_NAME = "compilerDummyJar"
fun Project.compilerDummyJar(task: TaskProvider<out Jar>, body: Jar.() -> Unit = {}) {
configurations.getOrCreate(COMPILER_DUMMY_JAR_CONFIGURATION_NAME).apply {
isCanBeResolved = false
isCanBeConsumed = true
}
task.configure(body)
addArtifact(COMPILER_DUMMY_JAR_CONFIGURATION_NAME, task)
}
const val EMBEDDABLE_COMPILER_TASK_NAME = "embeddable"
fun Project.embeddableCompilerDummyForDependenciesRewriting(
taskName: String = EMBEDDABLE_COMPILER_TASK_NAME,
body: ShadowJar.() -> Unit = {}
): TaskProvider<ShadowJar> {
val compilerDummyJar = configurations.getOrCreate(COMPILER_DUMMY_JAR_CONFIGURATION_NAME).apply {
isCanBeResolved = true
isCanBeConsumed = false
}
dependencies.add(
compilerDummyJar.name,
dependencies.project(":kotlin-compiler-embeddable", configuration = COMPILER_DUMMY_JAR_CONFIGURATION_NAME)
)
return tasks.register<ShadowJar>(taskName) {
destinationDirectory.set(project.file(File(buildDir, "libs")))
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
from(compilerDummyJar)
configureEmbeddableCompilerRelocation(withJavaxInject = false)
body()
}
}
fun Project.rewriteDepsToShadedJar(
originalJarTask: TaskProvider<out Jar>, shadowJarTask: TaskProvider<ShadowJar>, body: Jar.() -> Unit = {}
): TaskProvider<ShadowJar> {
originalJarTask.configure {
archiveClassifier.set("original")
}
shadowJarTask.configure {
dependsOn(originalJarTask)
from(originalJarTask)// { include("**") }
// When Gradle traverses the inputs, reject the shaded compiler JAR,
// which leads to the content of that JAR being excluded as well:
exclude {
// Docstring says `file` never returns null, but it does
@Suppress("UNNECESSARY_SAFE_CALL", "SAFE_CALL_WILL_CHANGE_NULLABILITY")
it.file?.name?.startsWith("kotlin-compiler-embeddable") ?: false
}
archiveClassifier.set("original")
body()
}
return shadowJarTask
}
fun Project.rewriteDepsToShadedCompiler(originalJarTask: TaskProvider<out Jar>, body: Jar.() -> Unit = {}): TaskProvider<ShadowJar> =
rewriteDepsToShadedJar(originalJarTask, embeddableCompilerDummyForDependenciesRewriting(), body)
fun Project.rewriteDefaultJarDepsToShadedCompiler(body: Jar.() -> Unit = {}): TaskProvider<ShadowJar> =
rewriteDepsToShadedJar(tasks.named<Jar>("jar"), embeddableCompilerDummyForDependenciesRewriting(), body)
@@ -0,0 +1,113 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
import com.gradle.publish.PluginBundleExtension
import plugins.signLibraryPublication
plugins {
kotlin("jvm")
`java-gradle-plugin`
`maven-publish`
id("com.gradle.plugin-publish")
}
// Enable signing for publications into Gradle Plugin Portal
val signPublication = !version.toString().contains("-SNAPSHOT") &&
(project.gradle.startParameter.taskNames.contains("publishPlugins") || signLibraryPublication)
configureCommonPublicationSettingsForGradle(signPublication)
configureKotlinCompileTasksGradleCompatibility()
addBomCheckTask()
extensions.extraProperties["kotlin.stdlib.default.dependency"] = "false"
// common plugin bundle configuration
gradlePlugin {
website.set("https://kotlinlang.org/")
vcsUrl.set("https://github.com/jetbrains/kotlin")
plugins.configureEach {
tags.add("kotlin")
}
}
publishing {
publications {
withType<MavenPublication>().configureEach {
if (name.endsWith("PluginMarkerMaven")) {
pom {
// https://github.com/gradle/gradle/issues/8754
// and https://github.com/gradle/gradle/issues/6155
packaging = "pom"
}
}
}
}
}
tasks {
named("install") {
dependsOn(named("validatePlugins"))
}
}
val commonSourceSet = createGradleCommonSourceSet()
reconfigureMainSourcesSetForGradlePlugin(commonSourceSet)
publishShadowedJar(sourceSets[SourceSet.MAIN_SOURCE_SET_NAME], commonSourceSet)
// Disabling this task, so "com.gradle.plugin-publish" will not publish unshadowed jar into Gradle Plugin Portal
// Without it 'jar' task is asked to run by "com.gradle.plugin-publish" even if artifacts are removed. The problem
// is that 'jar' task runs after shadow task plus their outputs has the same name leading to '.jar' file overwrite.
tasks.named("jar") {
enabled = false
}
if (!kotlinBuildProperties.isInJpsBuildIdeaSync) {
// Used for Gradle 7.0 version
val gradle70SourceSet = createGradlePluginVariant(
GradlePluginVariant.GRADLE_70,
commonSourceSet = commonSourceSet
)
publishShadowedJar(gradle70SourceSet, commonSourceSet)
// Used for Gradle 7.1+ versions
val gradle71SourceSet = createGradlePluginVariant(
GradlePluginVariant.GRADLE_71,
commonSourceSet = commonSourceSet
)
publishShadowedJar(gradle71SourceSet, commonSourceSet)
// Used for Gradle 7.4+ versions
val gradle74SourceSet = createGradlePluginVariant(
GradlePluginVariant.GRADLE_74,
commonSourceSet = commonSourceSet
)
publishShadowedJar(gradle74SourceSet, commonSourceSet)
// Used for Gradle 7.5+ versions
val gradle75SourceSet = createGradlePluginVariant(
GradlePluginVariant.GRADLE_75,
commonSourceSet = commonSourceSet
)
publishShadowedJar(gradle75SourceSet, commonSourceSet)
// Used for Gradle 7.6+ versions
val gradle76SourceSet = createGradlePluginVariant(
GradlePluginVariant.GRADLE_76,
commonSourceSet = commonSourceSet
)
publishShadowedJar(gradle76SourceSet, commonSourceSet)
// Used for Gradle 8.0+ versions
val gradle80SourceSet = createGradlePluginVariant(
GradlePluginVariant.GRADLE_80,
commonSourceSet = commonSourceSet
)
publishShadowedJar(gradle80SourceSet, commonSourceSet)
// Used for Gradle 8.1+ versions
val gradle81SourceSet = createGradlePluginVariant(
GradlePluginVariant.GRADLE_81,
commonSourceSet = commonSourceSet
)
publishShadowedJar(gradle81SourceSet, commonSourceSet)
}
@@ -0,0 +1,79 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
import plugins.KotlinBuildPublishingPlugin.Companion.DEFAULT_MAIN_PUBLICATION_NAME
import plugins.signLibraryPublication
plugins {
`java-library`
kotlin("jvm")
`maven-publish`
}
configureCommonPublicationSettingsForGradle(signLibraryPublication)
configureKotlinCompileTasksGradleCompatibility()
addBomCheckTask()
extensions.extraProperties["kotlin.stdlib.default.dependency"] = "false"
val commonSourceSet = createGradleCommonSourceSet()
reconfigureMainSourcesSetForGradlePlugin(commonSourceSet)
// Used for Gradle 7.0 version
createGradlePluginVariant(
GradlePluginVariant.GRADLE_70,
commonSourceSet = commonSourceSet,
isGradlePlugin = false
)
// Used for Gradle 7.1+ versions
createGradlePluginVariant(
GradlePluginVariant.GRADLE_71,
commonSourceSet = commonSourceSet,
isGradlePlugin = false
)
// Used for Gradle 7.4+ versions
createGradlePluginVariant(
GradlePluginVariant.GRADLE_74,
commonSourceSet = commonSourceSet,
isGradlePlugin = false
)
// Used for Gradle 7.5+ versions
createGradlePluginVariant(
GradlePluginVariant.GRADLE_75,
commonSourceSet = commonSourceSet,
isGradlePlugin = false
)
// Used for Gradle 7.6+ versions
createGradlePluginVariant(
GradlePluginVariant.GRADLE_76,
commonSourceSet = commonSourceSet,
isGradlePlugin = false
)
// Used for Gradle 8.0+ versions
createGradlePluginVariant(
GradlePluginVariant.GRADLE_80,
commonSourceSet = commonSourceSet,
isGradlePlugin = false
)
// Used for Gradle 8.1+ versions
createGradlePluginVariant(
GradlePluginVariant.GRADLE_81,
commonSourceSet = commonSourceSet,
isGradlePlugin = false
)
publishing {
publications {
register<MavenPublication>(DEFAULT_MAIN_PUBLICATION_NAME) {
from(components["java"])
suppressAllPomMetadataWarnings() // Don't warn about additional published variants
}
}
}
@@ -0,0 +1,40 @@
@file:Suppress("PackageDirectoryMismatch")
package org.jetbrains.kotlin.ideaExt
import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.plugins.ExtensionAware
import org.gradle.kotlin.dsl.configure
import org.gradle.plugins.ide.idea.model.IdeaProject
import org.jetbrains.gradle.ext.*
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
fun org.gradle.api.Project.idea(configure: org.gradle.plugins.ide.idea.model.IdeaModel.() -> Unit): Unit =
(this as org.gradle.api.plugins.ExtensionAware).extensions.configure("idea", configure)
fun IdeaProject.settings(block: ProjectSettings.() -> Unit) =
(this@settings as ExtensionAware).extensions.configure(block)
fun ProjectSettings.compiler(block: IdeaCompilerConfiguration.() -> Unit) =
(this@compiler as ExtensionAware).extensions.configure(block)
fun ProjectSettings.delegateActions(block: ActionDelegationConfig.() -> Unit) =
(this@delegateActions as ExtensionAware).extensions.configure(block)
fun ProjectSettings.runConfigurations(block: RunConfigurationContainer.() -> Unit) =
(this@runConfigurations as ExtensionAware).extensions.configure("runConfigurations", block)
inline fun <reified T: RunConfiguration> RunConfigurationContainer.defaults(noinline block: T.() -> Unit) =
defaults(T::class.java, block)
fun RunConfigurationContainer.junit(name: String, block: JUnit.() -> Unit) =
create(name, JUnit::class.java, block)
fun RunConfigurationContainer.application(name: String, block: Application.() -> Unit) =
create(name, Application::class.java, block)
fun ProjectSettings.ideArtifacts(block: NamedDomainObjectContainer<org.jetbrains.gradle.ext.TopLevelArtifact>.() -> Unit) =
(this@ideArtifacts as ExtensionAware).extensions.configure("ideArtifacts", block)
@@ -0,0 +1,39 @@
import org.gradle.api.Project
import org.gradle.kotlin.dsl.extra
import org.gradle.kotlin.dsl.project
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
val Project.intellijVersion
get() = rootProject.extra["versions.intellijSdk"]
fun Project.intellijCore() = dependencies.project(":dependencies:intellij-core")
fun Project.intellijUtilRt() = "com.jetbrains.intellij.platform:util-rt:$intellijVersion"
fun Project.jpsModel() = "com.jetbrains.intellij.platform:jps-model:$intellijVersion"
fun Project.jpsModelSerialization() = "com.jetbrains.intellij.platform:jps-model-serialization:$intellijVersion"
fun Project.jpsModelImpl() = "com.jetbrains.intellij.platform:jps-model-impl:$intellijVersion"
fun Project.jpsBuildTest() = "com.jetbrains.intellij.idea:jps-build-test:$intellijVersion"
fun Project.jpsBuild() = "com.jetbrains.intellij.platform:jps-build:$intellijVersion"
fun Project.testFramework() = "com.jetbrains.intellij.platform:test-framework:$intellijVersion"
fun Project.devKitJps() = "com.jetbrains.intellij.devkit:devkit-jps:$intellijVersion"
fun Project.intellijPlatformUtil() = "com.jetbrains.intellij.platform:util:$intellijVersion"
fun Project.intellijJavaRt() = "com.jetbrains.intellij.java:java-rt:$intellijVersion"
fun Project.intellijAnalysis() = "com.jetbrains.intellij.platform:analysis:$intellijVersion"
fun Project.intellijResources() = "com.jetbrains.intellij.platform:resources:$intellijVersion"
/**
* Runtime version of annotations that are already in Kotlin stdlib (historically Kotlin has older version of this one).
*
* SHOULD NOT BE USED IN COMPILE CLASSPATH!
*
* `@NonNull`, `@Nullabe` from `idea/annotations.jar` has `TYPE` target which leads to different types treatment in Kotlin compiler.
* On the other hand, `idea/annotations.jar` contains org/jetbrains/annotations/Async annations which is required for IDEA debugger.
*
* So, we are excluding `annotaions.jar` from all other `kotlin.build` and using this one for runtime only
* to avoid accidentally including `annotations.jar` by calling `intellijDep()`.
*/
fun Project.intellijRuntimeAnnotations() = "org.jetbrains:annotations:${rootProject.extra["versions.annotations"]}"
@@ -0,0 +1,136 @@
/*
* 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.
*/
// usages in build scripts are not tracked properly
@file:Suppress("unused")
import org.gradle.api.Project
import org.gradle.api.artifacts.ModuleDependency
import org.gradle.api.artifacts.dsl.RepositoryHandler
import org.gradle.api.artifacts.repositories.IvyArtifactRepository
import org.gradle.kotlin.dsl.extra
import java.io.File
private fun Project.kotlinBuildLocalDependenciesDir(): File =
(findProperty("kotlin.build.dependencies.dir") as String?)?.let(::File)
?: rootProject.gradle.gradleUserHomeDir.resolve("kotlin-build-dependencies")
private fun Project.kotlinBuildLocalRepoDir(): File = kotlinBuildLocalDependenciesDir().resolve("repo")
fun Project.ideModuleName() = when (IdeVersionConfigurator.currentIde.kind) {
Ide.Kind.AndroidStudio -> "android-studio-ide"
Ide.Kind.IntelliJ -> "ideaIC"
}
private fun Project.ideModuleVersion(forIde: Boolean) = when (IdeVersionConfigurator.currentIde.kind) {
Ide.Kind.AndroidStudio -> rootProject.findProperty("versions.androidStudioBuild")
Ide.Kind.IntelliJ -> {
if (forIde) {
intellijSdkVersionForIde()
?: error("Please specify 'attachedIntellijVersion' in your local.properties")
} else {
rootProject.findProperty("versions.intellijSdk")
}
}
}
fun Project.intellijSdkVersionForIde(): String? {
val majorVersion = kotlinBuildProperties.getOrNull("attachedIntellijVersion") as? String ?: return null
return rootProject.findProperty("versions.intellijSdk.forIde.$majorVersion") as? String
}
fun RepositoryHandler.kotlinBuildLocalRepo(project: Project): IvyArtifactRepository = ivy {
val baseDir = project.kotlinBuildLocalRepoDir()
url = baseDir.toURI()
patternLayout {
ivy("[organisation]/[module]/[revision]/[module].ivy.xml")
ivy("[organisation]/[module]/[revision]/ivy/[module].ivy.xml")
ivy("[organisation]/${project.ideModuleName()}/[revision]/ivy/[module].ivy.xml") // bundled plugins
artifact("[organisation]/[module]/[revision]/artifacts/lib/[artifact](-[classifier]).[ext]")
artifact("[organisation]/[module]/[revision]/artifacts/[artifact](-[classifier]).[ext]")
artifact("[organisation]/intellij-core/[revision]/artifacts/[artifact](-[classifier]).[ext]")
artifact("[organisation]/${project.ideModuleName()}/[revision]/artifacts/plugins/[module]/lib/[artifact](-[classifier]).[ext]") // bundled plugins
artifact("[organisation]/sources/[artifact]-[revision](-[classifier]).[ext]")
artifact("[organisation]/[module]/[revision]/[artifact](-[classifier]).[ext]")
}
metadataSources {
ivyDescriptor()
}
}
/* <used only for cooperative development for non kt-master branches> */
@JvmOverloads
fun Project.intellijDep(module: String? = null, forIde: Boolean = false) =
"kotlin.build:${module ?: ideModuleName()}:${ideModuleVersion(forIde)}"
fun Project.intellijCoreDep() = "kotlin.build:intellij-core:${rootProject.extra["versions.intellijSdk"]}"
fun Project.intellijPluginDep(plugin: String, forIde: Boolean = false) = intellijDep(plugin, forIde)
/* </used only for cooperative development for non kt-master branches> */
/* <used only for cooperative development for kt-master branch> */
fun Project.intellijMavenDep(subsystem: String, artifact: String) =
"com.jetbrains.$subsystem:$artifact:${ideModuleVersion(forIde = true)}"
/* </used only for cooperative development for kt-master branch> */
fun ModuleDependency.includeJars(vararg names: String, rootProject: Project? = null) {
names.forEach {
var baseName = it.removeSuffix(".jar")
if (rootProject != null && rootProject.extra.has("ignore.$baseName")) {
return@forEach
}
if (rootProject != null && rootProject.extra.has("versions.$baseName")) {
baseName += "-${rootProject.extra["versions.$baseName"]}"
}
artifact {
name = baseName
type = "jar"
extension = "jar"
}
}
}
// Workaround. Top-level Kotlin function in a default package can't be called from a non-default package
object IntellijRootUtils {
fun getRepositoryRootDir(project: Project): File = with(project.rootProject) {
return File(kotlinBuildLocalRepoDir(), "kotlin.build")
}
fun getIntellijRootDir(project: Project): File = with(project.rootProject) {
return File(
getRepositoryRootDir(this),
"${ideModuleName()}/${ideModuleVersion(forIde = false)}/artifacts"
)
}
}
fun Project.ideaHomePathForTests() = rootProject.buildDir.resolve("ideaHomeForTests")
fun Project.ideaBuildNumberFileForTests() = File(ideaHomePathForTests(), "build.txt")
fun Project.writeIdeaBuildNumberForTests() {
ideaHomePathForTests().mkdirs()
ideaBuildNumberFileForTests().writeText("IC-${rootProject.extra["versions.intellijSdk"]}")
}
@@ -0,0 +1,213 @@
import TestProperty.*
import org.gradle.api.GradleException
import org.gradle.api.Project
import org.gradle.api.artifacts.Configuration
import org.gradle.api.tasks.testing.Test
import org.gradle.kotlin.dsl.project
import java.io.File
private enum class TestProperty(shortName: String) {
// Use a separate Gradle property to pass Kotlin/Native home to tests: "kotlin.internal.native.test.nativeHome".
// Don't use "kotlin.native.home" and similar properties for this purpose, as these properties may have undesired
// effect on other Gradle tasks (ex: :kotlin-native:dist) that might be executed along with test task.
KOTLIN_NATIVE_HOME("nativeHome"),
COMPILER_CLASSPATH("compilerClasspath"),
CUSTOM_KLIBS("customKlibs"),
TEST_TARGET("target"),
TEST_MODE("mode"),
FORCE_STANDALONE("forceStandalone"),
COMPILE_ONLY("compileOnly"),
OPTIMIZATION_MODE("optimizationMode"),
USE_THREAD_STATE_CHECKER("useThreadStateChecker"),
GC_TYPE("gcType"),
GC_SCHEDULER("gcScheduler"),
ALLOCATOR("alloc"),
CACHE_MODE("cacheMode"),
EXECUTION_TIMEOUT("executionTimeout"),
SANITIZER("sanitizer"),
TEAMCITY("teamcity");
val fullName = "kotlin.internal.native.test.$shortName"
}
private sealed class ComputedTestProperty {
abstract val name: String
abstract val value: String?
class Normal(override val name: String, override val value: String?) : ComputedTestProperty()
class Lazy(override val name: String, private val lazyValue: kotlin.Lazy<String?>) : ComputedTestProperty() {
override val value get() = lazyValue.value
}
}
private class ComputedTestProperties(private val task: Test) {
private val computedProperties = arrayListOf<ComputedTestProperty>()
fun Project.compute(property: TestProperty, defaultValue: () -> String? = { null }) {
val gradleValue = readFromGradle(property)
computedProperties += ComputedTestProperty.Normal(property.fullName, gradleValue ?: defaultValue())
}
fun Project.computeLazy(property: TestProperty, defaultLazyValue: () -> Lazy<String?>) {
val gradleValue = readFromGradle(property)
computedProperties += if (gradleValue != null)
ComputedTestProperty.Normal(property.fullName, gradleValue)
else
ComputedTestProperty.Lazy(property.fullName, defaultLazyValue())
}
// Do not attempt to read the property from Gradle. Instead, set it based on the lambda return value.
fun computePrivate(property: TestProperty, value: () -> String) {
computedProperties += ComputedTestProperty.Normal(property.fullName, value())
}
fun lazyClassPath(builder: MutableList<File>.() -> Unit): Lazy<String?> = lazy(LazyThreadSafetyMode.NONE) {
buildList(builder).takeIf { it.isNotEmpty() }?.joinToString(File.pathSeparator) { it.absolutePath }
}
fun Project.readFromGradle(property: TestProperty): String? = findProperty(property.fullName)?.toString()
fun resolveAndApplyToTask() {
computedProperties.forEach { computedProperty ->
task.systemProperty(computedProperty.name, computedProperty.value ?: return@forEach)
}
}
}
private fun Test.ComputedTestProperties(init: ComputedTestProperties.() -> Unit): ComputedTestProperties =
ComputedTestProperties(this).apply { init() }
fun Project.nativeTest(
taskName: String,
tag: String?,
requirePlatformLibs: Boolean = false,
customDependencies: List<Configuration> = emptyList(),
customKlibDependencies: List<Configuration> = emptyList()
) = projectTest(
taskName,
jUnitMode = JUnitMode.JUnit5,
maxHeapSizeMb = 3072 // Extra heap space for Kotlin/Native compiler.
) {
group = "verification"
if (kotlinBuildProperties.isKotlinNativeEnabled) {
workingDir = rootDir
outputs.upToDateWhen {
// Don't treat any test task as up-to-date, no matter what.
// Note: this project should contain only test tasks, including ones that build binaries, and ones that run binaries.
false
}
// Effectively remove the limit for the amount of stack trace elements in Throwable.
jvmArgs("-XX:MaxJavaStackTraceDepth=1000000")
// Double the stack size. This is needed to compile some marginal tests with extra-deep IR tree, which requires a lot of stack frames
// for visiting it. Example: codegen/box/strings/concatDynamicWithConstants.kt
// Such tests are successfully compiled in old test infra with the default 1 MB stack just by accident. New test infra requires ~55
// additional stack frames more compared to the old one because of another launcher, etc. and it turns out this is not enough.
jvmArgs("-Xss2m")
val availableCpuCores: Int = Runtime.getRuntime().availableProcessors()
if (!kotlinBuildProperties.isTeamcityBuild
&& minOf(kotlinBuildProperties.junit5NumberOfThreadsForParallelExecution ?: 16, availableCpuCores) > 4
) {
logger.info("$path JIT C2 compiler has been disabled")
jvmArgs("-XX:TieredStopAtLevel=1") // Disable C2 if there are more than 4 CPUs at the host machine.
}
// Compute test properties in advance. Make sure that the necessary dependencies are settled.
// But do not resolve any configurations until the execution phase.
val computedTestProperties = ComputedTestProperties {
compute(KOTLIN_NATIVE_HOME) {
val testTarget = readFromGradle(TEST_TARGET)
if (testTarget != null) {
dependsOn(":kotlin-native:${testTarget}CrossDist")
if (requirePlatformLibs) dependsOn(":kotlin-native:${testTarget}PlatformLibs")
} else {
dependsOn(":kotlin-native:dist")
if (requirePlatformLibs) dependsOn(":kotlin-native:distPlatformLibs")
}
project(":kotlin-native").projectDir.resolve("dist").absolutePath
}
computeLazy(COMPILER_CLASSPATH) {
val customNativeHome = readFromGradle(KOTLIN_NATIVE_HOME)
val kotlinNativeCompilerEmbeddable = if (customNativeHome == null)
configurations.detachedConfiguration(
dependencies.project(":kotlin-native-compiler-embeddable"),
dependencies.module(commonDependency("org.jetbrains.intellij.deps:trove4j"))
).also { dependsOn(it) }
else
null
customDependencies.forEach(::dependsOn)
lazyClassPath {
if (customNativeHome == null) {
addAll(kotlinNativeCompilerEmbeddable!!.files)
} else {
this += file(customNativeHome).resolve("konan/lib/kotlin-native-compiler-embeddable.jar")
this += file(customNativeHome).resolve("konan/lib/trove4j.jar")
}
customDependencies.flatMapTo(this) { it.files }
}
}
computeLazy(CUSTOM_KLIBS) {
customKlibDependencies.forEach(::dependsOn)
lazyClassPath { customKlibDependencies.flatMapTo(this) { it.files } }
}
// Pass Gradle properties as JVM properties so test process can read them.
compute(TEST_TARGET)
compute(TEST_MODE)
compute(FORCE_STANDALONE)
compute(COMPILE_ONLY)
compute(OPTIMIZATION_MODE)
compute(USE_THREAD_STATE_CHECKER)
compute(GC_TYPE)
compute(GC_SCHEDULER)
compute(ALLOCATOR)
compute(CACHE_MODE)
compute(EXECUTION_TIMEOUT)
compute(SANITIZER)
// Pass whether tests are running at TeamCity.
computePrivate(TEAMCITY) { kotlinBuildProperties.isTeamcityBuild.toString() }
}
// Pass the current Gradle task name so test can use it in logging.
environment("GRADLE_TASK_NAME", path)
useJUnitPlatform {
tag?.let { includeTags(it) }
}
doFirst {
logger.info(
buildString {
appendLine("$path parallel test execution parameters:")
append(" Available CPU cores = $availableCpuCores")
systemProperties.filterKeys { it.startsWith("junit.jupiter") }.toSortedMap().forEach { (key, value) ->
append("\n $key = $value")
}
}
)
// Compute lazy properties and apply them all as JVM process properties.
// This forces to resolve the necessary configurations.
computedTestProperties.resolveAndApplyToTask()
}
} else
doFirst {
throw GradleException(
"""
Can't run task $path. The Kotlin/Native part of the project is currently disabled.
Make sure that "kotlin.native.enabled" is set to "true" in local.properties file, or is passed
as a Gradle command-line parameter via "-Pkotlin.native.enabled=true".
""".trimIndent()
)
}
}
@@ -0,0 +1,36 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
@file:Suppress("PackageDirectoryMismatch")
package org.jetbrains.kotlin.pill
import org.gradle.api.Plugin
import org.gradle.api.Project
@Suppress("unused")
class JpsCompatiblePlugin : Plugin<Project> {
override fun apply(project: Project) {
project.configurations.maybeCreate(EmbeddedComponents.CONFIGURATION_NAME)
project.extensions.create("pill", PillExtension::class.java)
// 'jpsTest' does not require the 'tests-jar' artifact
project.configurations.create("jpsTest")
if (project == project.rootProject) {
project.tasks.register("pill") {
dependsOn(":pill:pill-importer:pill")
if (System.getProperty("pill.android.tests", "false") == "true") {
TaskUtils.useAndroidSdk(this)
TaskUtils.useAndroidJar(this)
}
}
project.tasks.register("unpill") {
dependsOn(":pill:pill-importer:unpill")
}
}
}
}
@@ -0,0 +1,36 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
@file:Suppress("PackageDirectoryMismatch")
package org.jetbrains.kotlin.pill
import java.io.File
import org.gradle.api.Project
open class PillExtension {
/*
* Here's how you can specify a custom variant:
* `./gradlew pill -Dpill.variant=<NAME>`
*/
enum class Variant {
BASE, // Includes compiler and IDE (default)
FULL, // Includes compiler, IDE and Gradle plugin
}
open var variant: Variant? = null
open var excludedDirs: List<File> = emptyList()
@Suppress("unused")
fun Project.excludedDirs(vararg dirs: String) {
excludedDirs = excludedDirs + dirs.map { File(projectDir, it) }
}
@Suppress("unused")
fun serialize() = mapOf<String, Any?>(
"variant" to variant?.name,
"excludedDirs" to excludedDirs
)
}
@@ -0,0 +1,172 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
import com.jakewharton.dex.DexMethod
import com.jakewharton.dex.DexParser.Companion.toDexParser
import org.gradle.api.DefaultTask
import org.gradle.api.Project
import org.gradle.api.file.ProjectLayout
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.model.ObjectFactory
import org.gradle.api.provider.ListProperty
import org.gradle.api.provider.Property
import org.gradle.api.tasks.*
import org.gradle.jvm.tasks.Jar
import org.gradle.kotlin.dsl.property
import javax.inject.Inject
@CacheableTask
abstract class DexMethodCount @Inject constructor(objectFactory: ObjectFactory, layout: ProjectLayout) : DefaultTask() {
data class Counts(
val total: Int,
val totalOwnPackages: Int?,
val totalOtherPackages: Int?,
val byPackage: Map<String, Int>,
val byClass: Map<String, Int>
)
@get:InputFile
@get:Classpath
abstract val jarFile: RegularFileProperty
@get:Optional
@get:Input
abstract val ownPackages: ListProperty<String>
private val projectName: String = project.name
@get:Input
val artifactOrArchiveName: Property<String> = objectFactory.property<String>().convention(projectName)
fun from(jar: TaskProvider<Jar>) {
jarFile.set(jar.flatMap { it.archiveFile })
artifactOrArchiveName.set(jar.flatMap { it.archiveBaseName.orElse(projectName) })
}
@Internal // plain output properties are not supported, mark as internal to suppress warning from validatePlugins
lateinit var counts: Counts
@get:OutputFile
val detailOutputFile: RegularFileProperty = objectFactory.fileProperty().value(artifactOrArchiveName.flatMap { layout.buildDirectory.file("$it-method-count.txt") })
@TaskAction
fun invoke() {
val methods = jarFile.get().asFile.toDexParser().listMethods()
val counts = methods.getCounts().also { this.counts = it }
outputDetails(counts)
}
private fun List<DexMethod>.getCounts(): Counts {
val byPackage = this.groupingBy { it.`package` }.eachCount()
val byClass = this.groupingBy { it.declaringTypeFqn }.eachCount()
val ownPackages = ownPackages.map { list -> list.map { "$it." } }
val byOwnPackages = if (ownPackages.isPresent) {
this.partition { method -> ownPackages.get().any { method.declaringTypeFqn.startsWith(it) } }.let {
it.first.size to it.second.size
}
} else (null to null)
return Counts(
total = this.size,
totalOwnPackages = byOwnPackages.first,
totalOtherPackages = byOwnPackages.second,
byPackage = byPackage,
byClass = byClass
)
}
private fun outputDetails(counts: Counts) {
detailOutputFile.get().asFile.printWriter().use { writer ->
writer.println("${counts.total.padRight()}\tTotal methods")
ownPackages.orNull?.let { packages ->
writer.println("${counts.totalOwnPackages?.padRight()}\tTotal methods from packages ${packages.joinToString { "$it.*" }}")
writer.println("${counts.totalOtherPackages?.padRight()}\tTotal methods from other packages")
}
writer.println()
writer.println("Method count by package:")
counts.byPackage.forEach { (name, count) ->
writer.println("${count.padRight()}\t$name")
}
writer.println()
writer.println("Method count by class:")
counts.byClass.forEach { (name, count) ->
writer.println("${count.padRight()}\t$name")
}
}
}
}
abstract class DexMethodCountStats : DefaultTask() {
@get:InputFile
internal abstract val inputFile: RegularFileProperty
@get:Input
internal abstract val artifactOrArchiveName: Property<String>
@get:Input
@get:Optional
internal abstract val ownPackages: ListProperty<String>
private val isTeamCityBuild = project.kotlinBuildProperties.isTeamcityBuild
@TaskAction
private fun printStats() {
val artifactOrArchiveName = artifactOrArchiveName.get()
inputFile.get().asFile.reader().useLines { lines ->
fun String.getStatValue() = substringBefore("\t").trim()
val statsLineCount = if (!ownPackages.isPresent) 1 else 3
val stats = lines.take(statsLineCount).map { it.getStatValue() }.toList()
val total = stats[0]
logger.lifecycle("Artifact $artifactOrArchiveName, total methods: $total")
if (isTeamCityBuild) {
println("##teamcity[buildStatisticValue key='DexMethodCount_${artifactOrArchiveName}' value='$total']")
}
ownPackages.map { packages ->
val totalOwnPackages = stats[1]
val totalOtherPackages = stats[2]
logger.lifecycle("Artifact $artifactOrArchiveName, total methods from packages ${packages.joinToString { "$it.*" }}: $totalOwnPackages")
logger.lifecycle("Artifact $artifactOrArchiveName, total methods from other packages: $totalOtherPackages")
if (project.kotlinBuildProperties.isTeamcityBuild) {
println("##teamcity[buildStatisticValue key='DexMethodCount_${artifactOrArchiveName}_OwnPackages' value='$totalOwnPackages']")
println("##teamcity[buildStatisticValue key='DexMethodCount_${artifactOrArchiveName}_OtherPackages' value='$totalOtherPackages']")
}
}
}
}
}
fun Project.printStats(dexMethodCount: TaskProvider<DexMethodCount>) {
val dexMethodCountStats = tasks.register("dexMethodCountStats", DexMethodCountStats::class.java) {
inputFile.set(dexMethodCount.flatMap { it.detailOutputFile })
artifactOrArchiveName.set(dexMethodCount.flatMap { it.artifactOrArchiveName })
ownPackages.set(dexMethodCount.flatMap { it.ownPackages })
}
dexMethodCount.configure {
finalizedBy(dexMethodCountStats)
}
}
fun Project.dexMethodCount(action: DexMethodCount.() -> Unit): TaskProvider<DexMethodCount> {
val dexMethodCount = tasks.register("dexMethodCount", DexMethodCount::class.java, action)
printStats(dexMethodCount)
tasks.getByName("check").dependsOn(dexMethodCount)
return dexMethodCount
}
private val DexMethod.`package`: String get() = declaringTypeFqn.substringBeforeLast('.')
private fun Int.padRight() = toString().padStart(5, ' ')
private val DexMethod.declaringTypeFqn: String get() {
return this.render(false).substringBefore(' ')
}
@@ -0,0 +1,204 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package plugins
import capitalize
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.attributes.Usage
import org.gradle.api.component.SoftwareComponentFactory
import org.gradle.api.plugins.JavaBasePlugin
import org.gradle.api.publish.PublishingExtension
import org.gradle.api.publish.maven.MavenPublication
import org.gradle.api.publish.maven.plugins.MavenPublishPlugin
import org.gradle.api.publish.maven.tasks.PublishToMavenRepository
import org.gradle.api.tasks.TaskProvider
import org.gradle.kotlin.dsl.*
import org.gradle.plugins.signing.SigningExtension
import org.gradle.plugins.signing.SigningPlugin
import java.util.*
import javax.inject.Inject
class KotlinBuildPublishingPlugin @Inject constructor(
private val componentFactory: SoftwareComponentFactory
) : Plugin<Project> {
override fun apply(target: Project): Unit = with(target) {
apply<MavenPublishPlugin>()
val publishedRuntime = configurations.maybeCreate(RUNTIME_CONFIGURATION).apply {
isCanBeConsumed = false
isCanBeResolved = false
attributes {
attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.JAVA_RUNTIME))
}
}
val publishedCompile = configurations.maybeCreate(COMPILE_CONFIGURATION).apply {
isCanBeConsumed = false
isCanBeResolved = false
attributes {
attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.JAVA_API))
}
}
val kotlinLibraryComponent = componentFactory.adhoc(ADHOC_COMPONENT_NAME)
components.add(kotlinLibraryComponent)
kotlinLibraryComponent.addVariantsFromConfiguration(publishedCompile) { mapToMavenScope("compile") }
kotlinLibraryComponent.addVariantsFromConfiguration(publishedRuntime) { mapToMavenScope("runtime") }
pluginManager.withPlugin("java-base") {
val runtimeElements by configurations
val apiElements by configurations
publishedRuntime.extendsFrom(runtimeElements)
publishedCompile.extendsFrom(apiElements)
kotlinLibraryComponent.addVariantsFromConfiguration(runtimeElements) {
mapToMavenScope("runtime")
if (configurationVariant.artifacts.any { JavaBasePlugin.UNPUBLISHABLE_VARIANT_ARTIFACTS.contains(it.type) }) {
skip()
}
}
}
configure<PublishingExtension> {
publications {
create<MavenPublication>(project.mainPublicationName) {
from(kotlinLibraryComponent)
configureKotlinPomAttributes(project)
}
}
}
configureDefaultPublishing()
}
companion object {
const val DEFAULT_MAIN_PUBLICATION_NAME = "Main"
const val MAIN_PUBLICATION_NAME_PROPERTY = "MainPublicationName"
const val REPOSITORY_NAME = "Maven"
const val ADHOC_COMPONENT_NAME = "kotlinLibrary"
const val COMPILE_CONFIGURATION = "publishedCompile"
const val RUNTIME_CONFIGURATION = "publishedRuntime"
}
}
var Project.mainPublicationName: String
get() {
return if (project.extra.has(KotlinBuildPublishingPlugin.MAIN_PUBLICATION_NAME_PROPERTY))
project.extra.get(KotlinBuildPublishingPlugin.MAIN_PUBLICATION_NAME_PROPERTY) as String
else KotlinBuildPublishingPlugin.DEFAULT_MAIN_PUBLICATION_NAME
}
set(value) {
project.extra.set(KotlinBuildPublishingPlugin.MAIN_PUBLICATION_NAME_PROPERTY, value)
}
@OptIn(ExperimentalStdlibApi::class)
private fun humanReadableName(name: String) =
name.split("-").joinToString(separator = " ") { it.capitalize(Locale.ROOT) }
fun MavenPublication.configureKotlinPomAttributes(project: Project, explicitDescription: String? = null, packaging: String = "jar") {
val publication = this
pom {
this.packaging = packaging
name.set(humanReadableName(publication.artifactId))
description.set(explicitDescription ?: project.description ?: humanReadableName(publication.artifactId))
url.set("https://kotlinlang.org/")
licenses {
license {
name.set("The Apache License, Version 2.0")
url.set("http://www.apache.org/licenses/LICENSE-2.0.txt")
}
}
scm {
url.set("https://github.com/JetBrains/kotlin")
connection.set("scm:git:https://github.com/JetBrains/kotlin.git")
developerConnection.set("scm:git:https://github.com/JetBrains/kotlin.git")
}
developers {
developer {
name.set("Kotlin Team")
organization.set("JetBrains")
organizationUrl.set("https://www.jetbrains.com")
}
}
}
}
val Project.signLibraryPublication: Boolean
get() = project.providers.gradleProperty("signingRequired").orNull?.toBoolean()
?: project.providers.gradleProperty("isSonatypeRelease").orNull?.toBoolean()
?: false
fun Project.configureDefaultPublishing(
signingRequired: Boolean = signLibraryPublication
) {
configure<PublishingExtension> {
repositories {
maven {
name = KotlinBuildPublishingPlugin.REPOSITORY_NAME
url = file("${project.rootDir}/build/repo").toURI()
}
}
}
if (signingRequired) {
apply<SigningPlugin>()
configureSigning()
}
tasks.register("install") {
dependsOn(tasks.named("publishToMavenLocal"))
}
tasks.withType<PublishToMavenRepository>()
.matching { it.name.endsWith("PublicationTo${KotlinBuildPublishingPlugin.REPOSITORY_NAME}Repository") }
.all { configureRepository() }
}
private fun Project.getSensitiveProperty(name: String): String? {
return project.findProperty(name) as? String ?: System.getenv(name)
}
private fun Project.configureSigning() {
configure<SigningExtension> {
sign(extensions.getByType<PublishingExtension>().publications) // all publications
val signKeyId = project.getSensitiveProperty("signKeyId")
if (!signKeyId.isNullOrBlank()) {
val signKeyPrivate = project.getSensitiveProperty("signKeyPrivate") ?: error("Parameter `signKeyPrivate` not found")
val signKeyPassphrase = project.getSensitiveProperty("signKeyPassphrase") ?: error("Parameter `signKeyPassphrase` not found")
useInMemoryPgpKeys(signKeyId, signKeyPrivate, signKeyPassphrase)
} else {
useGpgCmd()
}
}
}
fun TaskProvider<PublishToMavenRepository>.configureRepository() =
configure { configureRepository() }
private fun PublishToMavenRepository.configureRepository() {
dependsOn(project.rootProject.tasks.named("preparePublication"))
doFirst {
val preparePublication = project.rootProject.tasks.named("preparePublication").get()
val username: String? by preparePublication.extra
val password: String? by preparePublication.extra
val repoUrl: String by preparePublication.extra
repository.apply {
url = project.uri(repoUrl)
if (url.scheme != "file" && username != null && password != null) {
credentials {
this.username = username
this.password = password
}
}
}
}
}
@@ -0,0 +1,89 @@
import io.ktor.client.*
import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.ktor.http.*
import kotlinx.coroutines.runBlocking
import org.gradle.api.GradleException
import org.gradle.api.Project
import org.gradle.kotlin.dsl.*
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
fun Project.preparePublication() {
tasks.register("preparePublication") {
assert(project.version != "unspecified")
val repositoryProviders = mapOf<String?, String?>(
"sonatype-nexus-staging" to "sonatype",
"sonatype-nexus-snapshots" to "sonatype"
)
val isRelease: Boolean by extra(!project.version.toString().contains("-SNAPSHOT"))
val repo: String? = properties["deployRepo"]?.toString() ?: properties["deploy-repo"]?.toString()
val repoProvider = repositoryProviders.getOrDefault(repo, repo)
val isSonatypePublish: Boolean by extra(repoProvider == "sonatype")
val isSonatypeRelease: Boolean by extra(isSonatypePublish && isRelease)
val deployRepoUrl = properties["deployRepoUrl"]?.toString() ?: properties["deploy-url"]?.toString()
val deployFolder = properties["deployRepoFolder"]?.toString()
?.let { "file://${rootProject.buildDir}/$it" }
val sonatypeSnapshotsUrl = if (isSonatypePublish && !isRelease) {
"https://oss.sonatype.org/content/repositories/snapshots/"
} else {
null
}
val deployUrlFromParameters = deployRepoUrl ?: deployFolder ?: sonatypeSnapshotsUrl
val isDeployStagingRepoGenerationRequired: Boolean by extra(isSonatypeRelease && deployUrlFromParameters == null)
var repoUrl: String by extra((deployUrlFromParameters ?: "file://${rootProject.buildDir}/repo").toString())
logger.info("Deployment repository preliminary url: $repoUrl ($repoProvider)")
val username: String? by extra(
properties["deployRepoUsername"]?.toString() ?: properties["kotlin.${repoProvider}.user"]?.toString()
)
val password: String? by extra(
properties["deployRepoPassword"]?.toString() ?: properties["kotlin.${repoProvider}.password"]?.toString()
)
if (isDeployStagingRepoGenerationRequired) {
doFirst {
HttpClient().use { client ->
runBlocking {
val sonatypeUsername = requireNotNull(username) {
"Username to authenticate on sonatype staging was not provided!"
}
val sonatypePassword = requireNotNull(password) {
"Password to authenticate on sonatype staging was not provided!"
}
val response = client.post("https://oss.sonatype.org/service/local/staging/profiles/169b36e205a64e/start") {
basicAuth(sonatypeUsername, sonatypePassword)
contentType(ContentType.Application.Xml)
accept(ContentType.Application.Xml)
setBody("<promoteRequest><data><description>Repository for publishing $version</description></data></promoteRequest>")
}
if (response.status.value in 200..299) {
val responseText = response.bodyAsText()
val repoId = responseText
.substringAfter("<stagingRepositoryId>")
.substringBefore("</stagingRepositoryId>")
repoUrl = "https://oss.sonatype.org/service/local/staging/deployByRepositoryId/$repoId/"
logger.warn("##teamcity[setParameter name='system.deploy-url' value='$repoUrl']")
} else {
throw GradleException("Failed to connect to sonatype API: ${response.status.description}")
}
}
}
}
}
doLast {
logger.warn("Deployment repository url: $repoUrl")
}
}
}
@@ -0,0 +1,37 @@
import org.gradle.api.Action
import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.NamedDomainObjectProvider
import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.Dependency
import org.gradle.api.artifacts.ExternalModuleDependency
import org.gradle.api.artifacts.dsl.DependencyHandler
import org.gradle.kotlin.dsl.accessors.runtime.addDependencyTo
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
val NamedDomainObjectContainer<Configuration>.publishedRuntime: NamedDomainObjectProvider<Configuration> get() = named("publishedRuntime")
fun DependencyHandler.publishedRuntime(dependencyNotation: Any): Dependency? =
add("publishedRuntime", dependencyNotation)
fun DependencyHandler.publishedRuntime(
dependencyNotation: String,
dependencyConfiguration: Action<ExternalModuleDependency>
): ExternalModuleDependency =
addDependencyTo(this, "publishedRuntime", dependencyNotation, dependencyConfiguration)
val NamedDomainObjectContainer<Configuration>.publishedCompile: NamedDomainObjectProvider<Configuration> get() = named("publishedCompile")
fun DependencyHandler.publishedCompile(dependencyNotation: Any): Dependency? =
add("publishedCompile", dependencyNotation)
fun DependencyHandler.publishedCompile(
dependencyNotation: String,
dependencyConfiguration: Action<ExternalModuleDependency>
): ExternalModuleDependency =
addDependencyTo(this, "publishedCompile", dependencyNotation, dependencyConfiguration)
@@ -0,0 +1,484 @@
@file:Suppress("unused") // usages in build scripts are not tracked properly
@file:JvmName("RepoArtifacts")
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.artifacts.ConfigurablePublishArtifact
import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.ConfigurationContainer
import org.gradle.api.artifacts.PublishArtifact
import org.gradle.api.artifacts.component.ProjectComponentIdentifier
import org.gradle.api.component.AdhocComponentWithVariants
import org.gradle.api.file.ArchiveOperations
import org.gradle.api.file.DuplicatesStrategy
import org.gradle.api.plugins.BasePluginExtension
import org.gradle.api.plugins.JavaPlugin
import org.gradle.api.plugins.JavaPlugin.JAVADOC_ELEMENTS_CONFIGURATION_NAME
import org.gradle.api.plugins.JavaPlugin.SOURCES_ELEMENTS_CONFIGURATION_NAME
import org.gradle.api.plugins.JavaPluginExtension
import org.gradle.api.provider.Provider
import org.gradle.api.publish.PublishingExtension
import org.gradle.api.publish.maven.MavenPublication
import org.gradle.api.publish.tasks.GenerateModuleMetadata
import org.gradle.api.tasks.TaskProvider
import org.gradle.api.tasks.javadoc.Javadoc
import org.gradle.configurationcache.extensions.serviceOf
import org.gradle.jvm.tasks.Jar
import org.gradle.kotlin.dsl.*
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSetContainer
import plugins.KotlinBuildPublishingPlugin
import plugins.mainPublicationName
import java.io.File
private const val MAGIC_DO_NOT_CHANGE_TEST_JAR_TASK_NAME = "testJar"
fun Project.testsJar(body: Jar.() -> Unit = {}): Jar {
val testsJarCfg = configurations.getOrCreate("tests-jar").extendsFrom(configurations["testApi"])
return task<Jar>(MAGIC_DO_NOT_CHANGE_TEST_JAR_TASK_NAME) {
dependsOn("testClasses")
pluginManager.withPlugin("java") {
from(testSourceSet.output)
}
archiveClassifier.set("tests")
body()
project.addArtifact(testsJarCfg, this, this)
}
}
fun Project.setPublishableArtifact(
jarTask: TaskProvider<out Jar>
) {
addArtifact("runtimeElements", jarTask)
addArtifact("apiElements", jarTask)
addArtifact("archives", jarTask)
}
fun removeJarTaskArtifact(
jarTask: TaskProvider<out Jar>
): Configuration.() -> Unit = {
val jarFile = jarTask.get().archiveFile.get().asFile
artifacts.removeIf { it.file == jarFile }
}
fun Project.noDefaultJar() {
val jarTask = tasks.named<Jar>("jar") {
enabled = false
}
configurations.named("apiElements", removeJarTaskArtifact(jarTask))
configurations.named("runtimeElements", removeJarTaskArtifact(jarTask))
configurations.named("archives", removeJarTaskArtifact(jarTask))
}
@JvmOverloads
fun Jar.addEmbeddedRuntime(embeddedConfigurationName: String = "embedded") {
project.configurations.findByName(embeddedConfigurationName)?.let { embedded ->
dependsOn(embedded)
val archiveOperations = project.serviceOf<ArchiveOperations>()
from {
embedded.map { dependency: File ->
check(!dependency.path.contains("kotlin-stdlib")) {
"""
|There's an attempt to have an embedded kotlin-stdlib in $project which is likely a misconfiguration
|All embedded dependencies:
| ${embedded.files.joinToString(separator = "\n| ")}
""".trimMargin()
}
if (dependency.extension.equals("jar", ignoreCase = true)) {
archiveOperations.zipTree(dependency)
} else {
dependency
}
}
}
}
}
fun Project.runtimeJar(body: Jar.() -> Unit = {}): TaskProvider<out Jar> {
val jarTask = tasks.named<Jar>("jar")
jarTask.configure {
addEmbeddedRuntime()
setupPublicJar(project.extensions.getByType<BasePluginExtension>().archivesName.get())
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
body()
}
return jarTask
}
fun Project.runtimeJarWithRelocation(body: ShadowJar.() -> Unit = {}): TaskProvider<out Jar> {
noDefaultJar()
val shadowJarTask = tasks.register<ShadowJar>("shadowJar") {
archiveClassifier.set("shadow")
configurations = configurations + listOf(project.configurations["embedded"])
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
body()
}
val runtimeJarTask = tasks.register<Jar>("runtimeJar") {
dependsOn(shadowJarTask)
from {
zipTree(shadowJarTask.get().outputs.files.singleFile)
}
setupPublicJar(project.extensions.getByType<BasePluginExtension>().archivesName.get())
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}
project.addArtifact("archives", runtimeJarTask, runtimeJarTask)
project.addArtifact("runtimeElements", runtimeJarTask, runtimeJarTask)
project.addArtifact("apiElements", runtimeJarTask, runtimeJarTask)
return runtimeJarTask
}
fun Project.runtimeJar(task: TaskProvider<ShadowJar>, body: ShadowJar.() -> Unit = {}): TaskProvider<out Jar> {
noDefaultJar()
task.configure {
configurations = configurations + listOf(project.configurations["embedded"])
setupPublicJar(project.extensions.getByType<BasePluginExtension>().archivesName.get())
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
body()
}
project.addArtifact("archives", task, task)
project.addArtifact("runtimeElements", task, task)
project.addArtifact("apiElements", task, task)
return task
}
private fun Project.mainJavaPluginSourceSet() = findJavaPluginExtension()?.sourceSets?.findByName("main")
private fun Project.mainKotlinSourceSet() =
(extensions.findByName("kotlin") as? KotlinSourceSetContainer)?.sourceSets?.findByName("main")
private fun Project.sources() = mainJavaPluginSourceSet()?.allSource ?: mainKotlinSourceSet()?.kotlin
@JvmOverloads
fun Project.sourcesJar(body: Jar.() -> Unit = {}): TaskProvider<Jar> {
configure<JavaPluginExtension> {
withSourcesJar()
}
val sourcesJar = getOrCreateTask<Jar>("sourcesJar") {
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
archiveClassifier.set("sources")
from(project.sources())
addEmbeddedSources()
body()
}
addArtifact("archives", sourcesJar)
addArtifact("sources", sourcesJar)
configurePublishedComponent {
addVariantsFromConfiguration(configurations[SOURCES_ELEMENTS_CONFIGURATION_NAME]) { }
}
return sourcesJar
}
/**
* Also embeds into final '-sources.jar' file source files from embedded dependencies.
*/
fun Project.sourcesJarWithSourcesFromEmbedded(
vararg embeddedDepSourcesJarTasks: TaskProvider<out Jar>,
body: Jar.() -> Unit = {},
): TaskProvider<Jar> {
val sourcesJarTask = sourcesJar(body)
sourcesJarTask.configure {
val archiveOperations = serviceOf<ArchiveOperations>()
embeddedDepSourcesJarTasks.forEach { embeddedSourceJarTask ->
dependsOn(embeddedSourceJarTask)
from(embeddedSourceJarTask.map { archiveOperations.zipTree(it.archiveFile) })
}
}
return sourcesJarTask
}
@JvmOverloads
fun Jar.addEmbeddedSources(configurationName: String = "embedded") {
project.configurations.findByName(configurationName)?.let { embedded ->
val allSources by lazy {
embedded.resolvedConfiguration
.resolvedArtifacts
.map { it.id.componentIdentifier }
.filterIsInstance<ProjectComponentIdentifier>()
.mapNotNull {
project.project(it.projectPath).sources()
}
}
from({ allSources })
}
}
@JvmOverloads
fun Project.javadocJar(body: Jar.() -> Unit = {}): TaskProvider<Jar> {
configure<JavaPluginExtension> {
withJavadocJar()
}
val javadocTask = getOrCreateTask<Jar>("javadocJar") {
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
archiveClassifier.set("javadoc")
tasks.findByName("javadoc")?.let { it as Javadoc }?.takeIf { it.enabled }?.let {
dependsOn(it)
from(it.destinationDir)
}
body()
}
addArtifact("archives", javadocTask)
configurePublishedComponent {
addVariantsFromConfiguration(configurations[JAVADOC_ELEMENTS_CONFIGURATION_NAME]) { }
}
return javadocTask
}
/**
* Also embeds into final '-javadoc.jar' file javadoc files from embedded dependencies.
*/
fun Project.javadocJarWithJavadocFromEmbedded(
vararg embeddedDepJavadocJarTasks: TaskProvider<out Jar>,
body: Jar.() -> Unit = {},
): TaskProvider<Jar> {
val javadocJarTask = javadocJar(body)
javadocJarTask.configure {
val archiveOperations = serviceOf<ArchiveOperations>()
embeddedDepJavadocJarTasks.forEach { embeddedJavadocJarTask ->
dependsOn(embeddedJavadocJarTask)
from(embeddedJavadocJarTask.map { archiveOperations.zipTree(it.archiveFile) })
}
}
return javadocJarTask
}
fun Project.standardPublicJars() {
runtimeJar()
sourcesJar()
javadocJar()
}
@JvmOverloads
fun Project.publish(moduleMetadata: Boolean = false, sbom: Boolean = true, configure: MavenPublication.() -> Unit = { }) {
apply<KotlinBuildPublishingPlugin>()
if (!moduleMetadata) {
tasks.withType<GenerateModuleMetadata> {
enabled = false
}
}
val publication = extensions.findByType<PublishingExtension>()
?.publications
?.findByName(mainPublicationName) as MavenPublication
publication.configure()
if (sbom) {
configureSbom()
}
}
fun Project.idePluginDependency(block: () -> Unit) {
val shouldActivate = rootProject.findProperty("publish.ide.plugin.dependencies")?.toString()?.toBoolean() == true
if (shouldActivate) {
block()
}
}
fun Project.publishJarsForIde(projects: List<String>, libraryDependencies: List<String> = emptyList()) {
idePluginDependency {
publishProjectJars(projects, libraryDependencies)
}
configurations.all {
// Don't allow `ideaIC` from compiler to leak into Kotlin plugin modules. Compiler and
// plugin may depend on different versions of IDEA and it will lead to version conflict
exclude(module = ideModuleName())
}
dependencies {
projects.forEach {
jpsLikeJarDependency(project(it), JpsDepScope.COMPILE, { isTransitive = false }, exported = true)
}
libraryDependencies.forEach {
jpsLikeJarDependency(it, JpsDepScope.COMPILE, exported = true)
}
}
}
fun Project.publishTestJarsForIde(projectNames: List<String>) {
idePluginDependency {
// Compiler test infrastructure should not affect test running in IDE.
// If required, the components should be registered on the IDE plugin side.
val excludedPaths = listOf("junit-platform.properties", "META-INF/services/**/*")
publishTestJar(projectNames, excludedPaths)
}
configurations.all {
// Don't allow `ideaIC` from compiler to leak into Kotlin plugin modules. Compiler and
// plugin may depend on different versions of IDEA and it will lead to version conflict
exclude(module = ideModuleName())
}
dependencies {
for (projectName in projectNames) {
jpsLikeJarDependency(projectTests(projectName), JpsDepScope.COMPILE, exported = true)
}
}
}
fun Project.publishProjectJars(projects: List<String>, libraryDependencies: List<String> = emptyList()) {
apply<JavaPlugin>()
val fatJarContents by configurations.creating
dependencies {
for (projectName in projects) {
fatJarContents(project(projectName)) { isTransitive = false }
}
for (libraryDependency in libraryDependencies) {
fatJarContents(libraryDependency) { isTransitive = false }
}
}
publish()
val jar: Jar by tasks
jar.apply {
dependsOn(fatJarContents)
val archiveOperations = project.serviceOf<ArchiveOperations>()
from {
fatJarContents.map(archiveOperations::zipTree)
}
}
sourcesJar {
for (projectPath in projects) {
val projectTasks = project(projectPath).tasks
if (projectTasks.names.any { it == "compileKotlin" }) {
// this is needed in order to declare explicit dependency on code generation tasks
dependsOn(projectTasks.named("compileKotlin").map { it.dependsOn })
}
}
from {
projects.map {
project(it).mainSourceSet.allSource
}
}
}
javadocJar()
}
fun Project.publishTestJar(projects: List<String>, excludedPaths: List<String>) {
apply<JavaPlugin>()
val fatJarContents by configurations.creating
dependencies {
for (projectName in projects) {
fatJarContents(project(projectName, configuration = "tests-jar")) { isTransitive = false }
}
}
publish(sbom = false)
val jar: Jar by tasks
jar.apply {
dependsOn(fatJarContents)
val archiveOperations = project.serviceOf<ArchiveOperations>()
from {
fatJarContents.map(archiveOperations::zipTree)
}
exclude(excludedPaths)
}
sourcesJar {
from {
projects.map { project(it).testSourceSet.allSource }
}
}
javadocJar()
}
fun ConfigurationContainer.getOrCreate(name: String): Configuration = findByName(name) ?: create(name)
fun Jar.setupPublicJar(
baseName: String,
classifier: String = ""
) = setupPublicJar(
project.provider { baseName },
project.provider { classifier }
)
fun Jar.setupPublicJar(
baseName: Provider<String>,
classifier: Provider<String> = project.provider { "" }
) {
val buildNumber = project.rootProject.extra["buildNumber"] as String
this.archiveBaseName.set(baseName)
this.archiveClassifier.set(classifier)
manifest.attributes.apply {
put("Implementation-Vendor", "JetBrains")
put("Implementation-Title", baseName.get())
put("Implementation-Version", buildNumber)
}
}
fun Project.addArtifact(configuration: Configuration, task: Task, artifactRef: Any, body: ConfigurablePublishArtifact.() -> Unit = {}) {
artifacts.add(configuration.name, artifactRef) {
builtBy(task)
body()
}
}
fun Project.addArtifact(configurationName: String, task: Task, artifactRef: Any, body: ConfigurablePublishArtifact.() -> Unit = {}) =
addArtifact(configurations.getOrCreate(configurationName), task, artifactRef, body)
fun <T : Task> Project.addArtifact(
configurationName: String,
task: TaskProvider<T>,
body: ConfigurablePublishArtifact.() -> Unit = {}
): PublishArtifact {
configurations.maybeCreate(configurationName)
return artifacts.add(configurationName, task, body)
}
fun <T : Task> Project.addArtifact(
configurationName: String,
task: TaskProvider<T>,
artifactRef: Any,
body: ConfigurablePublishArtifact.() -> Unit = {}
): PublishArtifact {
configurations.maybeCreate(configurationName)
return artifacts.add(configurationName, artifactRef) {
builtBy(task)
body()
}
}
fun Project.cleanArtifacts() {
configurations["archives"].artifacts.let { artifacts ->
artifacts.forEach {
artifacts.remove(it)
}
}
}
fun Project.configurePublishedComponent(configure: AdhocComponentWithVariants.() -> Unit) =
(components.findByName(KotlinBuildPublishingPlugin.ADHOC_COMPONENT_NAME) as AdhocComponentWithVariants?)?.apply(configure)
@@ -0,0 +1,298 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
@file:JvmName("RepoDependencies")
import org.gradle.api.GradleException
import org.gradle.api.Project
import org.gradle.api.artifacts.*
import org.gradle.api.artifacts.dsl.DependencyHandler
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.FileCollection
import org.gradle.internal.jvm.Jvm
import org.gradle.kotlin.dsl.closureOf
import org.gradle.kotlin.dsl.extra
import org.gradle.kotlin.dsl.project
import java.io.File
private val Project.isEAPIntellij get() = rootProject.extra["versions.intellijSdk"].toString().contains("-EAP-")
private val Project.isNightlyIntellij get() = rootProject.extra["versions.intellijSdk"].toString().endsWith("SNAPSHOT") && !isEAPIntellij
val Project.intellijRepo
get() =
when {
isEAPIntellij -> "https://www.jetbrains.com/intellij-repository/snapshots"
isNightlyIntellij -> "https://www.jetbrains.com/intellij-repository/nightly"
else -> "https://www.jetbrains.com/intellij-repository/releases"
}
fun Project.commonDependency(coordinates: String): String {
val parts = coordinates.split(':')
return when (parts.size) {
1 -> "$coordinates:$coordinates:${commonDependencyVersion(coordinates, coordinates)}"
2 -> "${parts[0]}:${parts[1]}:${commonDependencyVersion(parts[0], parts[1])}"
3 -> coordinates
else -> throw IllegalArgumentException("Illegal maven coordinates: $coordinates")
}
}
fun Project.commonDependency(group: String, artifact: String, vararg suffixesAndClassifiers: String): String {
val (classifiers, artifactSuffixes) = suffixesAndClassifiers.partition { it.startsWith(':') }
return "$group:$artifact${artifactSuffixes.joinToString("")}:${commonDependencyVersion(group, artifact)}${classifiers.joinToString("")}"
}
fun Project.commonDependencyVersion(group: String, artifact: String): String =
when {
rootProject.extra.has("versions.$artifact") -> rootProject.extra["versions.$artifact"]
rootProject.extra.has("versions.$group") -> rootProject.extra["versions.$group"]
else -> throw GradleException("Neither versions.$artifact nor versions.$group is defined in the root project's extra")
} as String
fun Project.preloadedDeps(
vararg artifactBaseNames: String,
baseDir: File = File(rootDir, "dependencies"),
subDir: String? = null,
optional: Boolean = false
): ConfigurableFileCollection {
val dir = if (subDir != null) File(baseDir, subDir) else baseDir
if (!dir.exists() || !dir.isDirectory) {
if (optional) return files()
throw GradleException("Invalid base directory $dir")
}
val matchingFiles = dir.listFiles { file -> artifactBaseNames.any { file.matchMaybeVersionedArtifact(it) } }
if (matchingFiles == null || matchingFiles.size < artifactBaseNames.size) {
throw GradleException(
"Not all matching artifacts '${artifactBaseNames.joinToString()}' found in the '$dir' " +
"(missing: ${
artifactBaseNames.filterNot { request ->
matchingFiles?.any {
it.matchMaybeVersionedArtifact(
request
)
} ?: false
}.joinToString()
};" +
" found: ${matchingFiles?.joinToString { it.name }})"
)
}
return files(*matchingFiles.map { it.canonicalPath }.toTypedArray())
}
fun kotlinDep(artifactBaseName: String, version: String, classifier: String? = null): String =
listOfNotNull("org.jetbrains.kotlin:kotlin-$artifactBaseName:$version", classifier).joinToString(":")
@JvmOverloads
fun Project.kotlinStdlib(suffix: String? = null, classifier: String? = null): Any {
return if (kotlinBuildProperties.useBootstrapStdlib)
kotlinDep(listOfNotNull("stdlib", suffix).joinToString("-"), bootstrapKotlinVersion, classifier)
else
dependencies.project(listOfNotNull(":kotlin-stdlib", suffix).joinToString("-"), classifier)
}
fun Project.kotlinBuiltins(): Any = kotlinBuiltins(forJvm = false)
fun Project.kotlinBuiltins(forJvm: Boolean): Any =
if (kotlinBuildProperties.useBootstrapStdlib) "org.jetbrains.kotlin:builtins:$bootstrapKotlinVersion"
else dependencies.project(":core:builtins", configuration = "runtimeElementsJvm".takeIf { forJvm })
fun DependencyHandler.projectTests(name: String): ProjectDependency = project(name, configuration = "tests-jar")
enum class JpsDepScope {
COMPILE, TEST, RUNTIME, PROVIDED
}
fun DependencyHandler.add(configurationName: String, dependencyNotation: Any, configure: (ModuleDependency.() -> Unit)?) {
// Avoid `dependencyNotation` to `ModuleDependency` class cast exception if possible
if (configure != null) {
add(configurationName, dependencyNotation, closureOf(configure))
} else {
add(configurationName, dependencyNotation)
}
}
@Suppress("unused") // Used in cooperative mode with IDEA Kotlin plugin
fun Project.disableDependencyVerification() {
configurations.all {
resolutionStrategy {
disableDependencyVerification()
}
}
}
fun DependencyHandler.jpsLikeJarDependency(
dependencyNotation: Any,
scope: JpsDepScope,
dependencyConfiguration: (ModuleDependency.() -> Unit)? = null,
exported: Boolean = false
) {
when (scope) {
JpsDepScope.COMPILE -> {
if (exported) {
add("api", dependencyNotation, dependencyConfiguration)
add("testApi", dependencyNotation, dependencyConfiguration)
} else {
add("implementation", dependencyNotation, dependencyConfiguration)
}
}
JpsDepScope.TEST -> {
if (exported) {
add("testApi", dependencyNotation, dependencyConfiguration)
} else {
add("testImplementation", dependencyNotation, dependencyConfiguration)
}
}
JpsDepScope.RUNTIME -> {
add("testRuntimeOnly", dependencyNotation, dependencyConfiguration)
}
JpsDepScope.PROVIDED -> {
if (exported) {
add("compileOnlyApi", dependencyNotation, dependencyConfiguration)
add("testApi", dependencyNotation, dependencyConfiguration)
} else {
add("compileOnly", dependencyNotation, dependencyConfiguration)
add("testImplementation", dependencyNotation, dependencyConfiguration)
}
}
}
}
@Suppress("unused") // Used in cooperative mode with IDEA Kotlin plugin
fun DependencyHandler.jpsLikeModuleDependency(moduleName: String, scope: JpsDepScope, exported: Boolean = false) {
jpsLikeJarDependency(project(moduleName), scope, exported = exported)
when (scope) {
JpsDepScope.COMPILE -> {
if (exported) {
add("testApi", projectTests(moduleName))
} else {
add("testImplementation", projectTests(moduleName))
}
}
JpsDepScope.TEST -> {
if (exported) {
add("testApi", projectTests(moduleName))
} else {
add("testImplementation", projectTests(moduleName))
}
}
JpsDepScope.RUNTIME -> {
add("runtimeOnly", projectTests(moduleName))
}
JpsDepScope.PROVIDED -> {
if (exported) {
add("testApi", projectTests(moduleName))
} else {
add("testImplementation", projectTests(moduleName))
}
}
}
}
fun Project.testApiJUnit5(
vintageEngine: Boolean = false,
runner: Boolean = false,
suiteApi: Boolean = false,
jupiterParams: Boolean = false
) {
with(dependencies) {
val platformVersion = commonDependencyVersion("org.junit", "junit-bom")
testApi(platform("org.junit:junit-bom:$platformVersion"))
testApi("org.junit.jupiter:junit-jupiter")
if (vintageEngine) {
testApi("org.junit.vintage:junit-vintage-engine:$platformVersion")
}
if (jupiterParams) {
testApi("org.junit.jupiter:junit-jupiter-params:$platformVersion")
}
val componentsVersion = commonDependencyVersion("org.junit.platform", "")
val components = mutableListOf(
"org.junit.platform:junit-platform-commons",
"org.junit.platform:junit-platform-launcher"
)
if (runner) {
components += "org.junit.platform:junit-platform-runner"
}
if (suiteApi) {
components += "org.junit.platform:junit-platform-suite-api"
}
for (component in components) {
testApi("$component:$componentsVersion")
}
// This dependency is needed only for FileComparisonFailure
add("testImplementation", intellijJavaRt())
// This is needed only for using FileComparisonFailure, which relies on JUnit 3 classes
add("testRuntimeOnly", commonDependency("junit:junit"))
}
}
private fun DependencyHandler.testApi(dependencyNotation: Any) {
add("testApi", dependencyNotation)
}
val Project.protobufRelocatedVersion: String get() = findProperty("versions.protobuf-relocated") as String
fun Project.protobufLite(): String = "org.jetbrains.kotlin:protobuf-lite:$protobufRelocatedVersion"
fun Project.protobufFull(): String = "org.jetbrains.kotlin:protobuf-relocated:$protobufRelocatedVersion"
fun Project.kotlinxCollectionsImmutable() =
"org.jetbrains.kotlinx:kotlinx-collections-immutable-jvm:${rootProject.extra["versions.kotlinx-collections-immutable"]}"
val Project.kotlinNativeVersion: String get() = property("versions.kotlin-native") as String
val Project.nodejsVersion: String get() = property("versions.nodejs") as String
val Project.v8Version: String get() = property("versions.v8") as String
fun File.matchMaybeVersionedArtifact(baseName: String) = name.matches(baseName.toMaybeVersionedJarRegex())
private val wildcardsRe = """[^*?]+|(\*)|(\?)""".toRegex()
private fun String.wildcardsToEscapedRegexString(): String = buildString {
wildcardsRe.findAll(this@wildcardsToEscapedRegexString).forEach {
when {
it.groups[1] != null -> append(".*")
it.groups[2] != null -> append(".")
else -> append("\\Q${it.groups[0]!!.value}\\E")
}
}
}
private fun String.toMaybeVersionedJarRegex(): Regex {
val hasJarExtension = endsWith(".jar")
val escaped = this.wildcardsToEscapedRegexString()
return Regex(if (hasJarExtension) escaped else "$escaped(-\\d.*)?\\.jar") // TODO: consider more precise version part of the regex
}
fun Project.firstFromJavaHomeThatExists(
vararg paths: String,
jdkHome: File = File((this.property("JDK_1_8") ?: this.property("JDK_18") ?: error("Can't find JDK_1_8 property")) as String)
): File? =
paths.map { File(jdkHome, it) }.firstOrNull { it.exists() }.also {
if (it == null)
logger.warn("Cannot find file by paths: ${paths.toList()} in $jdkHome")
}
fun Project.toolsJarApi(): Any =
if (kotlinBuildProperties.isInJpsBuildIdeaSync)
toolsJar()
else
dependencies.project(":dependencies:tools-jar-api")
fun Project.toolsJar(): FileCollection = files(
getToolchainLauncherFor(DEFAULT_JVM_TOOLCHAIN)
.map {
Jvm.forHome(it.metadata.installationPath.asFile).toolsJar ?: throw GradleException("tools.jar not found!")
}
)
val compilerManifestClassPath
get() = "annotations-13.0.jar kotlin-stdlib.jar kotlin-reflect.jar kotlin-script-runtime.jar trove4j.jar"
object EmbeddedComponents {
const val CONFIGURATION_NAME = "embedded"
}
@@ -0,0 +1,91 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
import org.gradle.api.NamedDomainObjectProvider
import org.gradle.api.Project
import org.gradle.api.artifacts.ModuleVersionIdentifier
import org.gradle.api.publish.PublishingExtension
import org.gradle.api.publish.maven.MavenPublication
import org.gradle.api.tasks.TaskProvider
import org.gradle.kotlin.dsl.*
import org.spdx.sbom.gradle.SpdxSbomExtension
import org.spdx.sbom.gradle.SpdxSbomPlugin
import org.spdx.sbom.gradle.SpdxSbomTask
import org.spdx.sbom.gradle.extensions.DefaultSpdxSbomTaskExtension
import plugins.mainPublicationName
import java.net.URI
import java.util.*
fun Project.configureSbom(
target: String? = null,
documentName: String? = null,
gradleConfigurations: Iterable<String> = setOf("runtimeClasspath"),
publication: NamedDomainObjectProvider<MavenPublication>? = null,
): TaskProvider<SpdxSbomTask> {
assert(target == null && publication != null) { "provided publication will be ignored when target is null" }
val project = this
val targetName = target ?: "${mainPublicationName}Publication"
apply<SpdxSbomPlugin>()
configure<SpdxSbomExtension> {
targets.create(targetName) {
configurations.set(gradleConfigurations)
scm {
tool.set("git")
uri.set("https://github.com/JetBrains/kotlin.git")
revision.set("v${project.version}")
}
// adjust properties of the document
document {
name.set(documentName ?: project.name)
// NOTE: The URI does not have to be accessible. It is only intended to provide a unique ID.
// In many cases, the URI will point to a Web accessible document, but this should not be assumed to be the case.
namespace.set("https://www.jetbrains.com/spdxdocs/${UUID.randomUUID()}")
creator.set("Organization: JetBrains s.r.o.")
packageSupplier.set("Organization: JetBrains s.r.o.")
}
}
}
val NOASSERTION = URI.create("NOASSERTION")
tasks.withType<SpdxSbomTask>().configureEach {
taskExtension.set(object : DefaultSpdxSbomTaskExtension() {
override fun mapRepoUri(input: URI?, moduleId: ModuleVersionIdentifier): URI {
return NOASSERTION
}
})
}
val sbomOutputDirectory = layout.buildDirectory.dir("spdx/$targetName")
val spdxSbomTask = tasks.named<SpdxSbomTask>("spdxSbomFor$targetName") {
outputDirectory.set(sbomOutputDirectory)
}
val sbomFile = sbomOutputDirectory.map { it.file("$targetName.spdx.json") }
val sbomCfg = configurations.maybeCreate("sbomFor$targetName").apply {
isCanBeResolved = false
isCanBeConsumed = true
}
val sbomArtifact = artifacts.add(sbomCfg.name, sbomFile) {
type = "sbom"
extension = "spdx.json"
builtBy(spdxSbomTask)
}
if (target == null) {
pluginManager.withPlugin("kotlin-build-publishing") {
val mainPublication = the<PublishingExtension>().publications[mainPublicationName] as MavenPublication
mainPublication.artifact(sbomArtifact)
}
} else if (publication != null) {
publication.configure {
artifact(sbomArtifact)
}
}
return spdxSbomTask
}
@@ -0,0 +1,24 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
import org.gradle.api.tasks.testing.Test
fun Test.useJsIrBoxTests(
version: Any,
buildDir: String = "",
fullStdLib: String = "libraries/stdlib/js-ir/build/classes/kotlin/js/main",
reducedStdlibPath: String = "libraries/stdlib/js-ir-minimal-for-test/build/classes/kotlin/js/main",
kotlinJsTestPath: String = "libraries/kotlin.test/js-ir/build/classes/kotlin/js/main"
) {
setupV8()
dependsOn(":kotlin-stdlib-js-ir:compileKotlinJs")
dependsOn(":kotlin-test:kotlin-test-js-ir:compileKotlinJs")
dependsOn(":kotlin-stdlib-js-ir-minimal-for-test:compileKotlinJs")
systemProperty("kotlin.js.test.root.out.dir", buildDir)
systemProperty("kotlin.js.full.stdlib.path", fullStdLib)
systemProperty("kotlin.js.reduced.stdlib.path", reducedStdlibPath)
systemProperty("kotlin.js.kotlin.test.path", kotlinJsTestPath)
systemProperty("kotlin.js.stdlib.klib.path", "libraries/stdlib/js-ir/build/libs/kotlin-stdlib-js-ir-js-$version.klib")
}
@@ -0,0 +1,32 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
import org.gradle.api.Project
import org.gradle.api.tasks.testing.Test
import org.jetbrains.kotlin.gradle.targets.js.d8.D8RootExtension
import org.jetbrains.kotlin.gradle.targets.js.d8.D8RootPlugin
private object V8Utils {
lateinit var d8Plugin: D8RootExtension
fun useD8Plugin(project: Project) {
d8Plugin = D8RootPlugin.apply(project.rootProject)
d8Plugin.version = project.v8Version
}
}
fun Project.useD8Plugin() {
V8Utils.useD8Plugin(this)
}
fun Test.setupV8() {
dependsOn(V8Utils.d8Plugin.setupTaskProvider)
val v8ExecutablePath = project.provider {
V8Utils.d8Plugin.requireConfigured().executablePath.absolutePath
}
doFirst {
systemProperty("javascript.engine.path.V8", v8ExecutablePath.get())
}
}
@@ -0,0 +1,75 @@
import org.gradle.api.Project
import org.gradle.api.plugins.JavaPluginExtension
import org.gradle.api.tasks.SourceSet
import org.gradle.api.tasks.SourceSetContainer
import org.gradle.kotlin.dsl.apply
import org.jetbrains.kotlin.ideaExt.idea
inline fun Project.sourceSets(crossinline body: SourceSetsBuilder.() -> Unit) = SourceSetsBuilder(this).body()
class SourceSetsBuilder(val project: Project) {
inline operator fun String.invoke(crossinline body: SourceSet.() -> Unit): SourceSet {
val sourceSetName = this
return project.sourceSets.maybeCreate(sourceSetName).apply {
none()
body()
}
}
}
fun SourceSet.none() {
java.setSrcDirs(emptyList<String>())
resources.setSrcDirs(emptyList<String>())
}
val SourceSet.projectDefault: Project.() -> Unit
get() = {
when (this@projectDefault.name) {
"main" -> {
java.srcDirs("src")
this@projectDefault.resources.srcDir("resources")
}
"test" -> {
java.srcDirs("test", "tests")
this@projectDefault.resources.srcDir("testResources")
}
}
}
val SourceSet.generatedDir: Project.() -> Unit
get() = {
generatedDir(this, "gen")
}
val SourceSet.generatedTestDir: Project.() -> Unit
get() = {
generatedDir(this, "tests-gen")
}
private fun SourceSet.generatedDir(project: Project, dirName: String) {
val generationRoot = project.projectDir.resolve(dirName)
java.srcDir(generationRoot.name)
if (project.kotlinBuildProperties.isInJpsBuildIdeaSync) {
project.apply(plugin = "idea")
project.idea {
this.module.generatedSourceDirs.add(generationRoot)
}
}
}
val Project.sourceSets: SourceSetContainer
get() = javaPluginExtension().sourceSets
val Project.mainSourceSet: SourceSet
get() = javaPluginExtension().mainSourceSet
val Project.testSourceSet: SourceSet
get() = javaPluginExtension().testSourceSet
val JavaPluginExtension.mainSourceSet: SourceSet
get() = sourceSets.getByName("main")
val JavaPluginExtension.testSourceSet: SourceSet
get() = sourceSets.getByName("test")
@@ -0,0 +1,70 @@
@file:Suppress("unused") // usages in build scripts are not tracked properly
import org.gradle.api.logging.Logger
import org.jetbrains.org.objectweb.asm.*
import java.io.BufferedOutputStream
import java.io.File
import java.io.FileOutputStream
import java.util.*
import java.util.jar.JarFile
import java.util.zip.ZipEntry
import java.util.zip.ZipOutputStream
private val CONSTANT_TIME_FOR_ZIP_ENTRIES = GregorianCalendar(1980, 1, 1, 0, 0, 0).timeInMillis
/**
* Removes @kotlin.Metadata annotations from compiled Kotlin classes
*/
fun stripMetadata(logger: Logger, classNamePattern: String, inFile: File, outFile: File, preserveFileTimestamps: Boolean = true) {
val classRegex = classNamePattern.toRegex()
assert(inFile.exists()) { "Input file not found at $inFile" }
fun transform(entryName: String, bytes: ByteArray): ByteArray {
if (!entryName.endsWith(".class")) return bytes
if (!classRegex.matches(entryName.removeSuffix(".class"))) return bytes
var changed = false
val classWriter = ClassWriter(0)
val classVisitor = object : ClassVisitor(Opcodes.API_VERSION, classWriter) {
override fun visitAnnotation(desc: String, visible: Boolean): AnnotationVisitor? {
if (Type.getType(desc).internalName == "kotlin/Metadata") {
changed = true
return null
}
return super.visitAnnotation(desc, visible)
}
}
ClassReader(bytes).accept(classVisitor, 0)
if (!changed) return bytes
return classWriter.toByteArray()
}
ZipOutputStream(BufferedOutputStream(FileOutputStream(outFile))).use { outJar ->
JarFile(inFile).use { inJar ->
for (entry in inJar.entries()) {
val inBytes = inJar.getInputStream(entry).readBytes()
val outBytes = transform(entry.name, inBytes)
if (inBytes.size < outBytes.size) {
error("Size increased for ${entry.name}: was ${inBytes.size} bytes, became ${outBytes.size} bytes")
}
val newEntry = ZipEntry(entry.name)
if (!preserveFileTimestamps) {
newEntry.time = CONSTANT_TIME_FOR_ZIP_ENTRIES
}
outJar.putNextEntry(newEntry)
outJar.write(outBytes)
outJar.closeEntry()
}
}
}
logger.info("Stripping @kotlin.Metadata annotations from all classes in $inFile")
logger.info("Class name pattern: $classNamePattern")
logger.info("Input file size: ${inFile.length()} bytes")
logger.info("Output written to $outFile")
logger.info("Output file size: ${outFile.length()} bytes")
}
@@ -0,0 +1,411 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
// usages in build scripts are not tracked properly
@file:Suppress("unused")
import com.sun.management.OperatingSystemMXBean
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.ProjectDependency
import org.gradle.api.attributes.Usage
import org.gradle.api.file.FileCollection
import org.gradle.api.file.FileSystemOperations
import org.gradle.api.internal.tasks.testing.filter.DefaultTestFilter
import org.gradle.api.tasks.TaskProvider
import org.gradle.api.tasks.testing.Test
import org.gradle.kotlin.dsl.*
import org.gradle.kotlin.dsl.support.serviceOf
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinUsages
import java.io.File
import java.lang.Character.isLowerCase
import java.lang.Character.isUpperCase
import java.lang.management.ManagementFactory
import java.nio.file.Files
import java.nio.file.Path
val kotlinGradlePluginAndItsRequired = arrayOf(
":kotlin-assignment",
":kotlin-allopen",
":kotlin-noarg",
":kotlin-sam-with-receiver",
":kotlin-lombok",
":kotlin-serialization",
":kotlin-android-extensions",
":kotlin-android-extensions-runtime",
":kotlin-parcelize-compiler",
":kotlin-build-common",
":kotlin-compiler-embeddable",
":native:kotlin-native-utils",
":kotlin-util-klib",
":kotlin-util-io",
":kotlin-compiler-runner",
":kotlin-daemon-embeddable",
":kotlin-daemon-client",
":kotlin-project-model",
":kotlin-gradle-plugins-bom",
":kotlin-gradle-plugin-api",
":kotlin-gradle-plugin-annotations",
":kotlin-gradle-plugin-idea",
":kotlin-gradle-plugin-idea-proto",
":kotlin-gradle-plugin",
":kotlin-gradle-plugin-model",
":kotlin-tooling-metadata",
":kotlin-tooling-core",
":kotlin-reflect",
":kotlin-test",
":kotlin-gradle-subplugin-example",
":kotlin-stdlib-common",
":kotlin-stdlib",
":kotlin-stdlib-jdk7",
":kotlin-stdlib-jdk8",
":kotlin-stdlib-js",
":kotlin-stdlib-wasm",
":examples:annotation-processor-example",
":kotlin-assignment-compiler-plugin.embeddable",
":kotlin-allopen-compiler-plugin.embeddable",
":kotlin-noarg-compiler-plugin.embeddable",
":kotlin-sam-with-receiver-compiler-plugin.embeddable",
":kotlin-lombok-compiler-plugin.embeddable",
":kotlinx-serialization-compiler-plugin.embeddable",
":kotlin-annotation-processing-embeddable",
":kotlin-script-runtime",
":kotlin-scripting-common",
":kotlin-scripting-jvm",
":kotlin-scripting-compiler-embeddable",
":kotlin-scripting-compiler-impl-embeddable",
":kotlin-test-js-runner",
":native:kotlin-klib-commonizer-embeddable",
":native:kotlin-klib-commonizer-api",
":compiler:build-tools:kotlin-build-tools-api",
":compiler:build-tools:kotlin-build-tools-impl",
)
fun Task.dependsOnKotlinGradlePluginInstall() {
kotlinGradlePluginAndItsRequired.forEach {
dependsOn("${it}:install")
}
}
fun Task.dependsOnKotlinGradlePluginPublish() {
kotlinGradlePluginAndItsRequired.forEach {
project.rootProject.tasks.findByPath("${it}:publish")?.let { task ->
dependsOn(task)
}
}
}
// Mixing JUnit4 and Junit5 in one module proved to be problematic, consider using separate modules instead
enum class JUnitMode {
JUnit4, JUnit5
}
/**
* @param parallel is redundant if @param jUnit5Enabled is true, because
* JUnit5 supports parallel test execution by itself, without gradle help
*/
fun Project.projectTest(
taskName: String = "test",
parallel: Boolean = false,
shortenTempRootName: Boolean = false,
jUnitMode: JUnitMode = JUnitMode.JUnit4,
maxHeapSizeMb: Int? = null,
minHeapSizeMb: Int? = null,
reservedCodeCacheSizeMb: Int = 256,
defineJDKEnvVariables: List<JdkMajorVersion> = emptyList(),
body: Test.() -> Unit = {}
): TaskProvider<Test> {
val shouldInstrument = project.providers.gradleProperty("kotlin.test.instrumentation.disable")
.orNull?.toBoolean() != true
if (shouldInstrument) {
evaluationDependsOn(":test-instrumenter")
}
return getOrCreateTask<Test>(taskName) {
dependsOn(":createIdeaHomeForTests")
doFirst {
if (jUnitMode == JUnitMode.JUnit5) return@doFirst
val commandLineIncludePatterns = commandLineIncludePatterns.toMutableSet()
val patterns = filter.includePatterns + commandLineIncludePatterns
if (patterns.isEmpty() || patterns.any { '*' in it }) return@doFirst
patterns.forEach { pattern ->
var isClassPattern = false
val maybeMethodName = pattern.substringAfterLast('.')
val maybeClassFqName = if (maybeMethodName.isFirstChar(::isLowerCase)) {
pattern.substringBeforeLast('.')
} else {
isClassPattern = true
pattern
}
if (!maybeClassFqName.substringAfterLast('.').isFirstChar(::isUpperCase)) {
return@forEach
}
val classFileNameWithoutExtension = maybeClassFqName.replace('.', '/')
val classFileName = "$classFileNameWithoutExtension.class"
if (isClassPattern) {
val innerClassPattern = "$pattern$*"
if (pattern in commandLineIncludePatterns) {
commandLineIncludePatterns.add(innerClassPattern)
(filter as? DefaultTestFilter)?.setCommandLineIncludePatterns(commandLineIncludePatterns)
} else {
filter.includePatterns.add(innerClassPattern)
}
}
include { treeElement ->
val path = treeElement.path
if (treeElement.isDirectory) {
classFileNameWithoutExtension.startsWith(path)
} else {
if (path == classFileName) return@include true
if (!path.endsWith(".class")) return@include false
path.startsWith("$classFileNameWithoutExtension$")
}
}
}
}
if (shouldInstrument) {
val instrumentationArgsProperty = project.providers.gradleProperty("kotlin.test.instrumentation.args")
val testInstrumenterOutputs = project.tasks.findByPath(":test-instrumenter:jar")!!.outputs.files
doFirst {
val agent = testInstrumenterOutputs.singleFile
val args = instrumentationArgsProperty.orNull?.let { "=$it" }.orEmpty()
jvmArgs("-javaagent:$agent$args")
}
dependsOn(":test-instrumenter:jar")
}
jvmArgs(
"-ea",
"-XX:+HeapDumpOnOutOfMemoryError",
"-XX:+UseCodeCacheFlushing",
"-XX:ReservedCodeCacheSize=${reservedCodeCacheSizeMb}m",
"-Djna.nosys=true"
)
val junit5ParallelTestWorkers =
project.kotlinBuildProperties.junit5NumberOfThreadsForParallelExecution ?: Runtime.getRuntime().availableProcessors()
val memoryPerTestProcessMb = maxHeapSizeMb ?: if (jUnitMode == JUnitMode.JUnit5)
totalMaxMemoryForTestsMb.coerceIn(defaultMaxMemoryPerTestWorkerMb, defaultMaxMemoryPerTestWorkerMb * junit5ParallelTestWorkers)
else
defaultMaxMemoryPerTestWorkerMb
maxHeapSize = "${memoryPerTestProcessMb}m"
if (minHeapSizeMb != null) {
minHeapSize = "${minHeapSizeMb}m"
}
systemProperty("idea.is.unit.test", "true")
systemProperty("idea.home.path", project.ideaHomePathForTests().canonicalPath)
systemProperty("idea.use.native.fs.for.win", false)
systemProperty("java.awt.headless", "true")
environment("NO_FS_ROOTS_ACCESS_CHECK", "true")
environment("PROJECT_CLASSES_DIRS", project.testSourceSet.output.classesDirs.asPath)
environment("PROJECT_BUILD_DIR", project.buildDir)
systemProperty("jps.kotlin.home", project.rootProject.extra["distKotlinHomeDir"]!!)
systemProperty("org.jetbrains.kotlin.skip.muted.tests", if (project.rootProject.hasProperty("skipMutedTests")) "true" else "false")
systemProperty("cacheRedirectorEnabled", project.rootProject.findProperty("cacheRedirectorEnabled")?.toString() ?: "false")
project.kotlinBuildProperties.junit5NumberOfThreadsForParallelExecution?.let { n ->
systemProperty("junit.jupiter.execution.parallel.config.strategy", "fixed")
systemProperty("junit.jupiter.execution.parallel.config.fixed.parallelism", n)
}
systemProperty("idea.ignore.disabled.plugins", "true")
var subProjectTempRoot: Path? = null
val projectName = project.name
val teamcity = project.rootProject.findProperty("teamcity") as? Map<*, *>
doFirst {
val systemTempRoot =
// TC by default doesn't switch `teamcity.build.tempDir` to 'java.io.tmpdir' so it could cause to wasted disk space
// Should be fixed soon on Teamcity side
(teamcity?.get("teamcity.build.tempDir") as? String)
?: System.getProperty("java.io.tmpdir")
systemTempRoot.let {
val prefix = (projectName + "Project_" + taskName + "_").takeUnless { shortenTempRootName }
subProjectTempRoot = Files.createTempDirectory(File(systemTempRoot).toPath(), prefix)
systemProperty("java.io.tmpdir", subProjectTempRoot.toString())
}
}
val fs = project.serviceOf<FileSystemOperations>()
doLast {
subProjectTempRoot?.let {
try {
fs.delete {
delete(it)
}
} catch (e: Exception) {
logger.warn("Can't delete test temp root folder $it", e.printStackTrace())
}
}
}
if (parallel && jUnitMode != JUnitMode.JUnit5) {
val forks = (totalMaxMemoryForTestsMb / memoryPerTestProcessMb).coerceAtMost(16)
maxParallelForks =
project.providers.gradleProperty("kotlin.test.maxParallelForks").orNull?.toInt()
?: forks.coerceIn(1, Runtime.getRuntime().availableProcessors())
}
defineJDKEnvVariables.forEach { version ->
val jdkHome = project.getToolchainJdkHomeFor(version).orNull ?: error("Can't find toolchain for $version")
environment(version.envName, jdkHome)
}
}.apply { configure(body) }
}
val defaultMaxMemoryPerTestWorkerMb = 1600
val reservedMemoryMb = 9000 // system processes, gradle daemon, kotlin daemon, etc ...
val totalMaxMemoryForTestsMb: Int
get() {
val mxbean = ManagementFactory.getOperatingSystemMXBean() as OperatingSystemMXBean
return (mxbean.totalPhysicalMemorySize / 1048576 - reservedMemoryMb).toInt()
}
val Test.commandLineIncludePatterns: Set<String>
get() = (filter as? DefaultTestFilter)?.commandLineIncludePatterns.orEmpty()
private inline fun String.isFirstChar(f: (Char) -> Boolean) = isNotEmpty() && f(first())
inline fun <reified T : Task> Project.getOrCreateTask(taskName: String, noinline body: T.() -> Unit): TaskProvider<T> =
if (tasks.names.contains(taskName)) tasks.named(taskName, T::class.java).apply { configure(body) }
else tasks.register(taskName, T::class.java, body)
object TaskUtils {
fun useAndroidSdk(task: Task) {
task.useAndroidConfiguration(systemPropertyName = "android.sdk", configName = "androidSdk")
}
fun useAndroidJar(task: Task) {
task.useAndroidConfiguration(systemPropertyName = "android.jar", configName = "androidJar")
}
fun useAndroidEmulator(task: Task) {
task.useAndroidConfiguration(systemPropertyName = "android.sdk", configName = "androidEmulator")
}
}
private fun Task.useAndroidConfiguration(systemPropertyName: String, configName: String) {
val configuration = with(project) {
configurations.getOrCreate(configName)
.also {
if (it.allDependencies.matching { dep ->
dep is ProjectDependency &&
dep.targetConfiguration == configName &&
dep.dependencyProject.path == ":dependencies:android-sdk"
}.count() == 0) {
dependencies.add(
configName,
dependencies.project(":dependencies:android-sdk", configuration = configName)
)
}
}
}
dependsOn(configuration)
if (this is Test) {
val androidFilePath = configuration.singleFile.canonicalPath
doFirst {
systemProperty(systemPropertyName, androidFilePath)
}
}
}
fun Task.useAndroidSdk() {
TaskUtils.useAndroidSdk(this)
}
fun Task.useAndroidJar() {
TaskUtils.useAndroidJar(this)
}
fun Task.acceptAndroidSdkLicenses() {
val separator = System.lineSeparator()
with(project) {
val androidSdk = configurations["androidSdk"].singleFile
val sdkLicensesDir = androidSdk.resolve("licenses").also {
if (!it.exists()) it.mkdirs()
}
val sdkLicenses = listOf(
"8933bad161af4178b1185d1a37fbf41ea5269c55",
"d56f5187479451eabf01fb78af6dfcb131a6481e",
"24333f8a63b6825ea9c5514f83c2829b004d1fee",
)
val sdkPreviewLicense = "84831b9409646a918e30573bab4c9c91346d8abd"
val sdkLicenseFile = sdkLicensesDir.resolve("android-sdk-license")
if (!sdkLicenseFile.exists()) {
sdkLicenseFile.createNewFile()
sdkLicenseFile.writeText(
sdkLicenses.joinToString(separator = separator)
)
} else {
sdkLicenses
.subtract(
sdkLicenseFile.readText().lines()
)
.forEach {
sdkLicenseFile.appendText("$it$separator")
}
}
val sdkPreviewLicenseFile = sdkLicensesDir.resolve("android-sdk-preview-license")
if (!sdkPreviewLicenseFile.exists()) {
sdkPreviewLicenseFile.writeText(sdkPreviewLicense)
} else {
if (sdkPreviewLicense != sdkPreviewLicenseFile.readText().trim()) {
sdkPreviewLicenseFile.writeText(sdkPreviewLicense)
}
}
}
}
fun Project.confugureFirPluginAnnotationsDependency(testTask: TaskProvider<Test>) {
val firPluginJvmAnnotations: Configuration by configurations.creating
val firPluginJsAnnotations: Configuration by configurations.creating {
attributes {
attribute(Usage.USAGE_ATTRIBUTE, objects.named(org.jetbrains.kotlin.gradle.plugin.mpp.KotlinUsages.KOTLIN_RUNTIME))
attribute(org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType.attribute, org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType.js)
}
}
dependencies {
firPluginJvmAnnotations(project(":plugins:fir-plugin-prototype:plugin-annotations")) { isTransitive = false }
firPluginJsAnnotations(project(":plugins:fir-plugin-prototype:plugin-annotations")) { isTransitive = false }
}
testTask.configure {
dependsOn(firPluginJvmAnnotations, firPluginJsAnnotations)
val localFirPluginJvmAnnotations: FileCollection = firPluginJvmAnnotations
val localFirPluginJsAnnotations: FileCollection = firPluginJsAnnotations
doFirst {
systemProperty("firPluginAnnotations.jvm.path", localFirPluginJvmAnnotations.singleFile.canonicalPath)
systemProperty("firPluginAnnotations.js.path", localFirPluginJsAnnotations.singleFile.canonicalPath)
}
}
}
fun Project.optInToExperimentalCompilerApi() {
@Suppress("DEPRECATION")
tasks.withType<org.jetbrains.kotlin.gradle.dsl.KotlinCompile<*>>().configureEach {
kotlinOptions {
freeCompilerArgs += "-opt-in=org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi"
}
}
}
@@ -0,0 +1,30 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
import com.gradle.enterprise.gradleplugin.testdistribution.TestDistributionExtension
import org.gradle.api.tasks.testing.Test
import org.gradle.internal.os.OperatingSystem
fun Test.configureTestDistribution(configure: TestDistributionExtension.() -> Unit = {}) {
val isTeamcityBuild = project.kotlinBuildProperties.isTeamcityBuild
val testDistributionEnabled =
project.findProperty("kotlin.build.test.distribution.enabled")?.toString()?.toBoolean() ?: false
useJUnitPlatform()
extensions.configure(TestDistributionExtension::class.java) {
enabled.set(testDistributionEnabled)
maxRemoteExecutors.set(20)
if (isTeamcityBuild) {
requirements.set(setOf("os=${OperatingSystem.current().familyName}"))
} else {
maxLocalExecutors.set(0)
}
configure()
}
}
fun Test.isTestDistributionEnabled(): Boolean =
extensions.findByType(TestDistributionExtension::class.java)?.enabled?.orNull ?: false
@@ -0,0 +1,23 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
import org.gradle.api.Project
import org.gradle.api.tasks.testing.Test
import com.gradle.enterprise.gradleplugin.testretry.retry
import org.gradle.kotlin.dsl.withType
fun Project.configureTestRetriesForTestTasks() {
val testRetryMaxRetries = findProperty("kotlin.build.testRetry.maxRetries")
?.toString()?.toInt()
?: (if (kotlinBuildProperties.isTeamcityBuild) 3 else 0)
tasks.withType<Test>().configureEach {
retry {
maxRetries.set(testRetryMaxRetries)
maxFailures.set(20)
failOnPassedAfterRetry.set(false)
}
}
}
@@ -0,0 +1,20 @@
org.gradle.jvmargs=-Duser.country=US -Dfile.encoding=UTF-8
org.gradle.kotlin.dsl.allWarningsAsErrors=true
cacheRedirectorEnabled=true
kotlin.build.gradlePlugin.version=0.0.39
kotlin.options.suppressFreeCompilerArgsModificationWarning=true
# Please keep it in sync with root gradle.properties.
# It's currently needed for proper configuration cache work, the reason will be investigated later
org.gradle.java.installations.fromEnv=\
JDK_1_6,JDK_16,\
JDK_1_7,JDK_17,\
JDK_1_8,JDK_18,\
JDK_9_0,JDK_9,\
JDK_10_0,JDK_10,\
JDK_11_0,JDK_11,\
JDK_15_0,JDK_15,\
JDK_16_0,\
JDK_17_0
@@ -0,0 +1,45 @@
pluginManagement {
apply from: '../scripts/cache-redirector.settings.gradle.kts'
apply from: '../scripts/kotlin-bootstrap.settings.gradle.kts'
includeBuild '../gradle-settings-conventions'
repositories {
maven { url "https://maven.pkg.jetbrains.space/kotlin/p/kotlin/kotlin-dependencies" }
mavenCentral()
google()
gradlePluginPortal()
}
}
plugins {
id "build-cache"
id "gradle-enterprise"
id "jvm-toolchain-provisioning"
id "kotlin-daemon-config"
}
File versionPropertiesFile = new File(rootProject.projectDir.parentFile, "../gradle/versions.properties")
def versionProperties = new Properties()
versionPropertiesFile.withInputStream {
versionProperties.load(it)
}
dependencyResolutionManagement {
components {
withModule("com.google.code.gson:gson") {
allVariants {
withDependencies {
add("com.google.code.gson:gson") {
version {
it.require(versionProperties['versions.gson'])
}
because("Force using same gson version because of https://github.com/google/gson/pull/1991")
}
}
}
}
}
}
include ":buildsrc-compat"
include ":prepare-deps"
@@ -64,7 +64,7 @@ fun getRootSettings(
val gradleInternal = (gradle as GradleInternal)
return when {
gradleInternal.isRootBuild() ||
settings.rootProject.name == "gradle-settings-conventions" -> {
setOf("gradle-settings-conventions", "gradle-build-conventions").contains(settings.rootProject.name) -> {
settings
}
else -> {
@@ -91,6 +91,7 @@ val kotlinRootDir: File = when (rootSettings.rootProject.name) {
}
"benchmarksAnalyzer", "performance-server" -> rootSettings.rootDir.parentFile.parentFile.parentFile
"gradle-settings-conventions" -> rootSettings.rootDir.parentFile.parentFile
"gradle-build-conventions" -> rootSettings.rootDir.parentFile.parentFile
"performance" -> rootSettings.rootDir.parentFile.parentFile
"ui" -> rootSettings.rootDir.parentFile.parentFile.parentFile.parentFile
else -> rootSettings.rootDir