[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") { tasks.matching { it is Zip && it.name == "mainBenchmarkJar" }.configureEach {
doLast { this as Zip
tasks.named("mainBenchmarkJar", Zip::class.java) { isZip64 = true
isZip64 = true archiveFileName.set("benchmarks.jar")
archiveName = "benchmarks.jar" }
}
listOf("mainBenchmark", "mainFirBenchmark", "mainNiBenchmark").forEach { val benchmarkTasks = listOf("mainBenchmark", "mainFirBenchmark", "mainNiBenchmark")
tasks.named(it, JavaExec::class.java) { tasks.matching { it is JavaExec && it.name in benchmarkTasks }.configureEach {
systemProperty("idea.home.path", intellijRootDir().canonicalPath) this as JavaExec
} systemProperty("idea.home.path", intellijRootDir().canonicalPath)
}
}
} }
tasks.register<JavaExec>("runBenchmark") { tasks.register<JavaExec>("runBenchmark") {
+4 -9
View File
@@ -621,17 +621,12 @@ val ideaPlugin by task<Task> {
} }
tasks { tasks {
named("clean") { named<Delete>("clean") {
doLast { delete += setOf("$buildDir/repo", distDir)
delete("$buildDir/repo")
delete(distDir)
}
} }
register("cleanupArtifacts") { register<Delete>("cleanupArtifacts") {
doLast { delete = setOf(artifactsDir)
delete(artifactsDir)
}
} }
listOf("clean", "assemble", "install").forEach { taskName -> 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) { project.tasks.register(sourceSetParam.getTaskName("instrument", "classes"), IntelliJInstrumentCodeTask::class.java) {
dependsOn(sourceSetParam.classesTaskName).onlyIf { !classesDirsCopy.isEmpty } dependsOn(sourceSetParam.classesTaskName).onlyIf { !classesDirsCopy.isEmpty }
sourceSet = sourceSetParam sourceSet = sourceSetParam
instrumentationClasspath = instrumentationClasspathCfg instrumentationClasspathConfiguration = instrumentationClasspathCfg
originalClassesDirs = classesDirsCopy originalClassesDirs = classesDirsCopy
output = instrumentedClassesDir output = instrumentedClassesDir
outputs.dir(instrumentedClassesDir) outputs.dir(instrumentedClassesDir)
@@ -111,8 +111,14 @@ open class IntelliJInstrumentCodeTask : ConventionTask() {
private const val LOADER_REF = "java2.loader" private const val LOADER_REF = "java2.loader"
} }
@Classpath @Transient
var instrumentationClasspath: Configuration? = null @Internal
lateinit var instrumentationClasspathConfiguration: Configuration
@get:Classpath
val instrumentationClasspath: String by lazy {
instrumentationClasspathConfiguration.asPath
}
@InputFiles @InputFiles
@PathSensitive(PathSensitivity.RELATIVE) @PathSensitive(PathSensitivity.RELATIVE)
@@ -121,13 +127,19 @@ open class IntelliJInstrumentCodeTask : ConventionTask() {
@get:Input @get:Input
var instrumentNotNull: Boolean = false var instrumentNotNull: Boolean = false
@Transient
@Internal @Internal
var sourceSet: SourceSet? = null lateinit var sourceSet: SourceSet
private val compileClasspath by lazy {
sourceSet.compileClasspath
}
@get:InputFiles @get:InputFiles
@get:PathSensitive(PathSensitivity.RELATIVE) @get:PathSensitive(PathSensitivity.RELATIVE)
val sourceDirs: FileCollection val sourceDirs: FileCollection by lazy {
get() = project.files(sourceSet!!.allSource.srcDirs.filter { !sourceSet!!.resources.contains(it) && it.exists() }) project.files(sourceSet.allSource.srcDirs.filter { !sourceSet.resources.contains(it) && it.exists() })
}
@get:OutputDirectory @get:OutputDirectory
lateinit var output: File lateinit var output: File
@@ -142,12 +154,12 @@ open class IntelliJInstrumentCodeTask : ConventionTask() {
output.deleteRecursively() output.deleteRecursively()
copyOriginalClasses() copyOriginalClasses()
val classpath = instrumentationClasspath!! val classpath = instrumentationClasspath
ant.withGroovyBuilder { ant.withGroovyBuilder {
"taskdef"( "taskdef"(
"name" to "instrumentIdeaExtensions", "name" to "instrumentIdeaExtensions",
"classpath" to classpath.asPath, "classpath" to classpath,
"loaderref" to LOADER_REF, "loaderref" to LOADER_REF,
"classname" to "com.intellij.ant.InstrumentIdeaExtensions" "classname" to "com.intellij.ant.InstrumentIdeaExtensions"
) )
@@ -155,7 +167,7 @@ open class IntelliJInstrumentCodeTask : ConventionTask() {
logger.info("Compiling forms and instrumenting code with nullability preconditions") logger.info("Compiling forms and instrumenting code with nullability preconditions")
if (instrumentNotNull) { if (instrumentNotNull) {
prepareNotNullInstrumenting(classpath.asPath) prepareNotNullInstrumenting(classpath)
} }
instrumentCode(sourceDirs, instrumentNotNull) instrumentCode(sourceDirs, instrumentNotNull)
@@ -186,7 +198,7 @@ open class IntelliJInstrumentCodeTask : ConventionTask() {
val depSourceDirectorySets = project.configurations["compile"].dependencies.withType(ProjectDependency::class.java) val depSourceDirectorySets = project.configurations["compile"].dependencies.withType(ProjectDependency::class.java)
.map { p -> p.dependencyProject.mainSourceSet.allSource.sourceDirectories } .map { p -> p.dependencyProject.mainSourceSet.allSource.sourceDirectories }
val instrumentationClasspath = 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") logger.info("Using following dependency source dirs: $it")
} }
+3 -2
View File
@@ -25,8 +25,9 @@ tasks.withType<JavaCompile> {
} }
tasks.named<ProcessResources>("processResources") { tasks.named<ProcessResources>("processResources") {
inputs.property("compilerVersion", kotlinVersion) val kotlinVersionLocal = kotlinVersion
inputs.property("compilerVersion", kotlinVersionLocal)
filesMatching("META-INF/compiler.version") { 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 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 toolsJarStubs by tasks.registering {
val toolsJarFile = toolsJarFile(jdkHome = File(JDK_18)) ?: error("Couldn't find tools.jar in $JDK_18")
inputs.file(toolsJarFile) inputs.file(toolsJarFile)
val outDir = buildDir.resolve(name) val outDir = buildDir.resolve(name)
outputs.dir(outDir) outputs.dir(outDir)
val usedInternalApiPackages = listOf(
"com/sun/tools/javac" // Used in KAPT
)
doLast { doLast {
outDir.deleteRecursively() outDir.deleteRecursively()
val zipFile = ZipFile(toolsJarFile) val zipFile = ZipFile(toolsJarFile)
+2 -36
View File
@@ -24,42 +24,8 @@ fun Project.configureJavaInstrumentation() {
includeJars("javac2", "jdom", "asm-all", rootProject = rootProject) includeJars("javac2", "jdom", "asm-all", rootProject = rootProject)
} }
} }
for (sourceSet in listOf(mainSourceSet, testSourceSet)) {
listOf(mainSourceSet, testSourceSet).forEach { sourceSet -> tasks.named(sourceSet.compileJavaTaskName, InstrumentJava(javaInstrumentator, 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
)
} }
} }
} }
+2 -1
View File
@@ -61,10 +61,11 @@ ext.compileJava9Sources = { Project project, String moduleName, Collection<FileC
it.options.fork = true it.options.fork = true
it.options.forkOptions.javaHome = file(JDK_9) it.options.forkOptions.javaHome = file(JDK_9)
it.options.sourcepath = files(java9SourceSet.srcDirs) it.options.sourcepath = files(java9SourceSet.srcDirs)
def compileClasspath = project.configurations.java9CompileClasspath
doFirst { doFirst {
def moduleFiles = files(*moduleOutputs) def moduleFiles = files(*moduleOutputs)
def modulePath = project.configurations.java9CompileClasspath.filter { !(it in moduleFiles.files) } def modulePath = compileClasspath.filter { !(it in moduleFiles.files) }
options.compilerArgs = [ options.compilerArgs = [
'--module-path', modulePath.asPath, '--module-path', modulePath.asPath,
+14 -10
View File
@@ -21,22 +21,26 @@ artifacts.add(buildVersion.name, file(buildVersionFilePath)) {
builtBy(writeBuildNumber) 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 writeStdlibVersion by tasks.registering {
val kotlinVersionLocal = kotlinVersion
val versionFile = rootDir.resolve("libraries/stdlib/src/kotlin/util/KotlinVersion.kt") val versionFile = rootDir.resolve("libraries/stdlib/src/kotlin/util/KotlinVersion.kt")
inputs.property("version", kotlinVersion) inputs.property("version", kotlinVersionLocal)
outputs.file(versionFile) 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 { doLast {
replaceVersion(versionFile, """fun get\(\): KotlinVersion = KotlinVersion\((\d+, \d+, \d+)\)""") { 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" }" val newVersion = "$major, $minor, ${optPatch.takeIf { it.isNotEmpty() } ?: "0" }"
logger.lifecycle("Writing new standard library version components: $newVersion") logger.lifecycle("Writing new standard library version components: $newVersion")
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 // dummy is used for rewriting dependencies to the shaded packages in the embeddable compiler
compilerDummyJar(compilerDummyForDependenciesRewriting("compilerDummy") { compilerDummyJar(compilerDummyForDependenciesRewriting("compilerDummy") {
classifier = "dummy" archiveClassifier.set("dummy")
}) })
class CoreXmlShadingTransformer : Transformer { class CoreXmlShadingTransformer : Transformer {
+6 -3
View File
@@ -39,9 +39,12 @@ def buildProperties = BuildPropertiesKt.getKotlinBuildPropertiesForSettings(sett
def projectVersions = file("./gradle/versions.properties").text def projectVersions = file("./gradle/versions.properties").text
gradleEnterprise { gradleEnterprise {
def buildScanServer = buildProperties.buildScanServer
def isTeamCity = buildProperties.isTeamcityBuild
buildScan { buildScan {
if (buildProperties.buildScanServer != null) { if (buildScanServer != null) {
server = buildProperties.buildScanServer server = buildScanServer
captureTaskInputFiles = true captureTaskInputFiles = true
publishAlways() publishAlways()
} else { } else {
@@ -52,7 +55,7 @@ gradleEnterprise {
obfuscation { obfuscation {
ipAddresses { addresses -> ["0.0.0.0"] } ipAddresses { addresses -> ["0.0.0.0"] }
hostname { host -> "concealed" } hostname { host -> "concealed" }
username { name -> buildProperties.isTeamcityBuild ? "TeamCity" : "concealed" } username { name -> isTeamCity ? "TeamCity" : "concealed" }
} }
} }
} }