From 27956adf3f998585007ba94fb9ded0a6541a4635 Mon Sep 17 00:00:00 2001 From: Alexander Likhachev Date: Fri, 29 Jan 2021 05:44:51 +0300 Subject: [PATCH] [Build] Fix configuration cache issues (part 1) * Make `clean` task compatible with configuration cache * Make Java compile instrumentation compatible with configuration cache * Make settings.gradle compatible with configuration cache * Initial work on making IntelliJInstrumentCodeTask compatible with configuration cache * Make writeStdlibVersion task compatible with configuration cache * Copy some properties to not capture it's owning object into lambda to support configuration cache Relates to #KT-44611 --- benchmarks/build.gradle.kts | 22 ++++----- build.gradle.kts | 13 ++--- buildSrc/src/main/kotlin/InstrumentJava.kt | 51 ++++++++++++++++++++ buildSrc/src/main/kotlin/instrument.kt | 32 ++++++++---- compiler/compiler.version/build.gradle.kts | 5 +- dependencies/tools-jar-api/build.gradle.kts | 9 ++-- gradle/javaInstrumentation.gradle.kts | 38 +-------------- libraries/commonConfiguration.gradle | 3 +- prepare/build.version/build.gradle.kts | 24 +++++---- prepare/compiler-embeddable/build.gradle.kts | 2 +- settings.gradle | 9 ++-- 11 files changed, 120 insertions(+), 88 deletions(-) create mode 100644 buildSrc/src/main/kotlin/InstrumentJava.kt diff --git a/benchmarks/build.gradle.kts b/benchmarks/build.gradle.kts index da1f8219e70..1d77b9ab5f6 100644 --- a/benchmarks/build.gradle.kts +++ b/benchmarks/build.gradle.kts @@ -92,18 +92,16 @@ benchmark { } } -tasks.named("classes") { - doLast { - tasks.named("mainBenchmarkJar", Zip::class.java) { - isZip64 = true - archiveName = "benchmarks.jar" - } - listOf("mainBenchmark", "mainFirBenchmark", "mainNiBenchmark").forEach { - tasks.named(it, JavaExec::class.java) { - systemProperty("idea.home.path", intellijRootDir().canonicalPath) - } - } - } +tasks.matching { it is Zip && it.name == "mainBenchmarkJar" }.configureEach { + this as Zip + isZip64 = true + archiveFileName.set("benchmarks.jar") +} + +val benchmarkTasks = listOf("mainBenchmark", "mainFirBenchmark", "mainNiBenchmark") +tasks.matching { it is JavaExec && it.name in benchmarkTasks }.configureEach { + this as JavaExec + systemProperty("idea.home.path", intellijRootDir().canonicalPath) } tasks.register("runBenchmark") { diff --git a/build.gradle.kts b/build.gradle.kts index 436c87125ed..c91f68211ae 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -621,17 +621,12 @@ val ideaPlugin by task { } tasks { - named("clean") { - doLast { - delete("$buildDir/repo") - delete(distDir) - } + named("clean") { + delete += setOf("$buildDir/repo", distDir) } - register("cleanupArtifacts") { - doLast { - delete(artifactsDir) - } + register("cleanupArtifacts") { + delete = setOf(artifactsDir) } listOf("clean", "assemble", "install").forEach { taskName -> diff --git a/buildSrc/src/main/kotlin/InstrumentJava.kt b/buildSrc/src/main/kotlin/InstrumentJava.kt new file mode 100644 index 00000000000..5bd0fc3f902 --- /dev/null +++ b/buildSrc/src/main/kotlin/InstrumentJava.kt @@ -0,0 +1,51 @@ +import org.gradle.api.Action +import org.gradle.api.Task +import org.gradle.api.artifacts.Configuration +import org.gradle.api.file.FileCollection +import org.gradle.api.tasks.SourceSet +import org.gradle.api.tasks.compile.JavaCompile +import org.gradle.kotlin.dsl.provideDelegate +import org.gradle.kotlin.dsl.withGroovyBuilder + +/* + * 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, @Transient val sourceSet: SourceSet) : Action { + private val instrumentatorClasspath: String by lazy { + javaInstrumentator.asPath + } + + private val srcDirs: FileCollection by lazy { + sourceSet.allJava.sourceDirectories + } + + override fun execute(task: Task) { + require(task is JavaCompile) { "$task is not of type JavaCompile!" } + task.doLast { + task.ant.withGroovyBuilder { + "taskdef"( + "name" to "instrumentIdeaExtensions", + "classpath" to instrumentatorClasspath, + "loaderref" to "java2.loader", + "classname" to "com.intellij.ant.InstrumentIdeaExtensions" + ) + } + + val javaSourceDirectories = srcDirs.filter { it.exists() } + + task.ant.withGroovyBuilder { + javaSourceDirectories.forEach { directory -> + "instrumentIdeaExtensions"( + "srcdir" to directory, + "destdir" to task.destinationDir, + "classpath" to task.classpath.asPath, + "includeantruntime" to false, + "instrumentNotNull" to true + ) + } + } + } + } +} \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/instrument.kt b/buildSrc/src/main/kotlin/instrument.kt index e1a23d810b0..76a24a5c1d1 100644 --- a/buildSrc/src/main/kotlin/instrument.kt +++ b/buildSrc/src/main/kotlin/instrument.kt @@ -90,7 +90,7 @@ fun Project.configureFormInstrumentation() { project.tasks.register(sourceSetParam.getTaskName("instrument", "classes"), IntelliJInstrumentCodeTask::class.java) { dependsOn(sourceSetParam.classesTaskName).onlyIf { !classesDirsCopy.isEmpty } sourceSet = sourceSetParam - instrumentationClasspath = instrumentationClasspathCfg + instrumentationClasspathConfiguration = instrumentationClasspathCfg originalClassesDirs = classesDirsCopy output = instrumentedClassesDir outputs.dir(instrumentedClassesDir) @@ -111,8 +111,14 @@ open class IntelliJInstrumentCodeTask : ConventionTask() { private const val LOADER_REF = "java2.loader" } - @Classpath - var instrumentationClasspath: Configuration? = null + @Transient + @Internal + lateinit var instrumentationClasspathConfiguration: Configuration + + @get:Classpath + val instrumentationClasspath: String by lazy { + instrumentationClasspathConfiguration.asPath + } @InputFiles @PathSensitive(PathSensitivity.RELATIVE) @@ -121,13 +127,19 @@ open class IntelliJInstrumentCodeTask : ConventionTask() { @get:Input var instrumentNotNull: Boolean = false + @Transient @Internal - var sourceSet: SourceSet? = null + lateinit var sourceSet: SourceSet + + private val compileClasspath by lazy { + sourceSet.compileClasspath + } @get:InputFiles @get:PathSensitive(PathSensitivity.RELATIVE) - val sourceDirs: FileCollection - get() = project.files(sourceSet!!.allSource.srcDirs.filter { !sourceSet!!.resources.contains(it) && it.exists() }) + val sourceDirs: FileCollection by lazy { + project.files(sourceSet.allSource.srcDirs.filter { !sourceSet.resources.contains(it) && it.exists() }) + } @get:OutputDirectory lateinit var output: File @@ -142,12 +154,12 @@ open class IntelliJInstrumentCodeTask : ConventionTask() { output.deleteRecursively() copyOriginalClasses() - val classpath = instrumentationClasspath!! + val classpath = instrumentationClasspath ant.withGroovyBuilder { "taskdef"( "name" to "instrumentIdeaExtensions", - "classpath" to classpath.asPath, + "classpath" to classpath, "loaderref" to LOADER_REF, "classname" to "com.intellij.ant.InstrumentIdeaExtensions" ) @@ -155,7 +167,7 @@ open class IntelliJInstrumentCodeTask : ConventionTask() { logger.info("Compiling forms and instrumenting code with nullability preconditions") if (instrumentNotNull) { - prepareNotNullInstrumenting(classpath.asPath) + prepareNotNullInstrumenting(classpath) } instrumentCode(sourceDirs, instrumentNotNull) @@ -186,7 +198,7 @@ open class IntelliJInstrumentCodeTask : ConventionTask() { val depSourceDirectorySets = project.configurations["compile"].dependencies.withType(ProjectDependency::class.java) .map { p -> p.dependencyProject.mainSourceSet.allSource.sourceDirectories } val instrumentationClasspath = - depSourceDirectorySets.fold(sourceSet!!.compileClasspath) { acc, v -> acc + v }.asPath.also { + depSourceDirectorySets.fold(compileClasspath) { acc, v -> acc + v }.asPath.also { logger.info("Using following dependency source dirs: $it") } diff --git a/compiler/compiler.version/build.gradle.kts b/compiler/compiler.version/build.gradle.kts index 9d69fc176ca..7ff0df88716 100644 --- a/compiler/compiler.version/build.gradle.kts +++ b/compiler/compiler.version/build.gradle.kts @@ -25,8 +25,9 @@ tasks.withType { } tasks.named("processResources") { - inputs.property("compilerVersion", kotlinVersion) + val kotlinVersionLocal = kotlinVersion + inputs.property("compilerVersion", kotlinVersionLocal) filesMatching("META-INF/compiler.version") { - filter("tokens" to mapOf("snapshot" to kotlinVersion)) + filter("tokens" to mapOf("snapshot" to kotlinVersionLocal)) } } \ No newline at end of file diff --git a/dependencies/tools-jar-api/build.gradle.kts b/dependencies/tools-jar-api/build.gradle.kts index 5bd79972988..c975a5e4250 100644 --- a/dependencies/tools-jar-api/build.gradle.kts +++ b/dependencies/tools-jar-api/build.gradle.kts @@ -16,17 +16,18 @@ val runtimeElements by configurations.creating { } val JDK_18: String by rootProject.extra -val toolsJarFile = toolsJarFile(jdkHome = File(JDK_18)) ?: error("Couldn't find tools.jar in $JDK_18") -val usedInternalApiPackages = listOf( - "com/sun/tools/javac" // Used in KAPT -) val toolsJarStubs by tasks.registering { + val toolsJarFile = toolsJarFile(jdkHome = File(JDK_18)) ?: error("Couldn't find tools.jar in $JDK_18") inputs.file(toolsJarFile) val outDir = buildDir.resolve(name) outputs.dir(outDir) + val usedInternalApiPackages = listOf( + "com/sun/tools/javac" // Used in KAPT + ) + doLast { outDir.deleteRecursively() val zipFile = ZipFile(toolsJarFile) diff --git a/gradle/javaInstrumentation.gradle.kts b/gradle/javaInstrumentation.gradle.kts index 3d6c2b34185..3ade0e1a54c 100644 --- a/gradle/javaInstrumentation.gradle.kts +++ b/gradle/javaInstrumentation.gradle.kts @@ -24,42 +24,8 @@ fun Project.configureJavaInstrumentation() { includeJars("javac2", "jdom", "asm-all", rootProject = rootProject) } } - - listOf(mainSourceSet, testSourceSet).forEach { sourceSet -> - tasks.named(sourceSet.compileJavaTaskName) javaCompile@ { - doLast { - instrumentClasses(javaInstrumentator.asPath, this@javaCompile, sourceSet) - } - } - } - } -} - -fun instrumentClasses( - instrumentatorClasspath: String, - javaCompile: JavaCompile, - sourceSet: SourceSet -) { - javaCompile.ant.withGroovyBuilder { - "taskdef"( - "name" to "instrumentIdeaExtensions", - "classpath" to instrumentatorClasspath, - "loaderref" to "java2.loader", - "classname" to "com.intellij.ant.InstrumentIdeaExtensions" - ) - } - - val javaSourceDirectories = sourceSet.allJava.sourceDirectories.filter { it.exists() } - - javaCompile.ant.withGroovyBuilder { - javaSourceDirectories.forEach { directory -> - "instrumentIdeaExtensions"( - "srcdir" to directory, - "destdir" to javaCompile.destinationDir, - "classpath" to javaCompile.classpath.asPath, - "includeantruntime" to false, - "instrumentNotNull" to true - ) + for (sourceSet in listOf(mainSourceSet, testSourceSet)) { + tasks.named(sourceSet.compileJavaTaskName, InstrumentJava(javaInstrumentator, sourceSet)) } } } \ No newline at end of file diff --git a/libraries/commonConfiguration.gradle b/libraries/commonConfiguration.gradle index fb226d640b2..5758e72f22a 100644 --- a/libraries/commonConfiguration.gradle +++ b/libraries/commonConfiguration.gradle @@ -61,10 +61,11 @@ ext.compileJava9Sources = { Project project, String moduleName, Collection String) { - check(versionFile.isFile) { "Version file $versionFile is not found" } - val text = versionFile.readText() - val pattern = Regex(versionPattern) - val match = pattern.find(text) ?: error("Version pattern is missing in file $versionFile") - val newValue = replacement(match) - versionFile.writeText(text.replaceRange(match.groups[1]!!.range, newValue)) -} + val writeStdlibVersion by tasks.registering { + val kotlinVersionLocal = kotlinVersion val versionFile = rootDir.resolve("libraries/stdlib/src/kotlin/util/KotlinVersion.kt") - inputs.property("version", kotlinVersion) + inputs.property("version", kotlinVersionLocal) outputs.file(versionFile) + + fun replaceVersion(versionFile: File, versionPattern: String, replacement: (MatchResult) -> String) { + check(versionFile.isFile) { "Version file $versionFile is not found" } + val text = versionFile.readText() + val pattern = Regex(versionPattern) + val match = pattern.find(text) ?: error("Version pattern is missing in file $versionFile") + val newValue = replacement(match) + versionFile.writeText(text.replaceRange(match.groups[1]!!.range, newValue)) + } + doLast { replaceVersion(versionFile, """fun get\(\): KotlinVersion = KotlinVersion\((\d+, \d+, \d+)\)""") { - val (major, minor, _, optPatch) = Regex("""^(\d+)\.(\d+)(\.(\d+))?""").find(kotlinVersion)?.destructured ?: error("Cannot parse current version $kotlinVersion") + val (major, minor, _, optPatch) = Regex("""^(\d+)\.(\d+)(\.(\d+))?""").find(kotlinVersionLocal)?.destructured ?: error("Cannot parse current version $kotlinVersionLocal") val newVersion = "$major, $minor, ${optPatch.takeIf { it.isNotEmpty() } ?: "0" }" logger.lifecycle("Writing new standard library version components: $newVersion") newVersion diff --git a/prepare/compiler-embeddable/build.gradle.kts b/prepare/compiler-embeddable/build.gradle.kts index 4b1fb351605..68fc008b5be 100644 --- a/prepare/compiler-embeddable/build.gradle.kts +++ b/prepare/compiler-embeddable/build.gradle.kts @@ -42,7 +42,7 @@ noDefaultJar() // dummy is used for rewriting dependencies to the shaded packages in the embeddable compiler compilerDummyJar(compilerDummyForDependenciesRewriting("compilerDummy") { - classifier = "dummy" + archiveClassifier.set("dummy") }) class CoreXmlShadingTransformer : Transformer { diff --git a/settings.gradle b/settings.gradle index 619cf70843c..4655268fbc0 100644 --- a/settings.gradle +++ b/settings.gradle @@ -39,9 +39,12 @@ def buildProperties = BuildPropertiesKt.getKotlinBuildPropertiesForSettings(sett def projectVersions = file("./gradle/versions.properties").text gradleEnterprise { + def buildScanServer = buildProperties.buildScanServer + def isTeamCity = buildProperties.isTeamcityBuild + buildScan { - if (buildProperties.buildScanServer != null) { - server = buildProperties.buildScanServer + if (buildScanServer != null) { + server = buildScanServer captureTaskInputFiles = true publishAlways() } else { @@ -52,7 +55,7 @@ gradleEnterprise { obfuscation { ipAddresses { addresses -> ["0.0.0.0"] } hostname { host -> "concealed" } - username { name -> buildProperties.isTeamcityBuild ? "TeamCity" : "concealed" } + username { name -> isTeamCity ? "TeamCity" : "concealed" } } } }