[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
This commit is contained in:
Alexander Likhachev
2021-01-29 05:44:51 +03:00
parent 111e67dc8d
commit 27956adf3f
11 changed files with 120 additions and 88 deletions
+10 -12
View File
@@ -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<JavaExec>("runBenchmark") {
+4 -9
View File
@@ -621,17 +621,12 @@ val ideaPlugin by task<Task> {
}
tasks {
named("clean") {
doLast {
delete("$buildDir/repo")
delete(distDir)
}
named<Delete>("clean") {
delete += setOf("$buildDir/repo", distDir)
}
register("cleanupArtifacts") {
doLast {
delete(artifactsDir)
}
register<Delete>("cleanupArtifacts") {
delete = setOf(artifactsDir)
}
listOf("clean", "assemble", "install").forEach { taskName ->
@@ -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<Task> {
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
)
}
}
}
}
}
+22 -10
View File
@@ -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")
}
+3 -2
View File
@@ -25,8 +25,9 @@ tasks.withType<JavaCompile> {
}
tasks.named<ProcessResources>("processResources") {
inputs.property("compilerVersion", kotlinVersion)
val kotlinVersionLocal = kotlinVersion
inputs.property("compilerVersion", kotlinVersionLocal)
filesMatching("META-INF/compiler.version") {
filter<ReplaceTokens>("tokens" to mapOf("snapshot" to kotlinVersion))
filter<ReplaceTokens>("tokens" to mapOf("snapshot" to kotlinVersionLocal))
}
}
+5 -4
View File
@@ -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)
+2 -36
View File
@@ -24,42 +24,8 @@ fun Project.configureJavaInstrumentation() {
includeJars("javac2", "jdom", "asm-all", rootProject = rootProject)
}
}
listOf(mainSourceSet, testSourceSet).forEach { sourceSet ->
tasks.named<JavaCompile>(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))
}
}
}
+2 -1
View File
@@ -61,10 +61,11 @@ ext.compileJava9Sources = { Project project, String moduleName, Collection<FileC
it.options.fork = true
it.options.forkOptions.javaHome = file(JDK_9)
it.options.sourcepath = files(java9SourceSet.srcDirs)
def compileClasspath = project.configurations.java9CompileClasspath
doFirst {
def moduleFiles = files(*moduleOutputs)
def modulePath = project.configurations.java9CompileClasspath.filter { !(it in moduleFiles.files) }
def modulePath = compileClasspath.filter { !(it in moduleFiles.files) }
options.compilerArgs = [
'--module-path', modulePath.asPath,
+14 -10
View File
@@ -21,22 +21,26 @@ artifacts.add(buildVersion.name, file(buildVersionFilePath)) {
builtBy(writeBuildNumber)
}
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))
}
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
+1 -1
View File
@@ -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 {
+6 -3
View File
@@ -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" }
}
}
}