diff --git a/buildSrc/src/main/kotlin/instrument.kt b/buildSrc/src/main/kotlin/instrument.kt index 3fc37c4f7fb..27e2a29a3f0 100644 --- a/buildSrc/src/main/kotlin/instrument.kt +++ b/buildSrc/src/main/kotlin/instrument.kt @@ -31,46 +31,6 @@ import java.io.File import javax.inject.Inject fun Project.configureFormInstrumentation() { - plugins.matching { it::class.java.canonicalName.startsWith("org.jetbrains.kotlin.gradle.plugin") }.all { - // When we change the output classes directory, Gradle will automatically configure - // the test compile tasks to use the instrumented classes. Normally this is fine, - // however, it causes problems for Kotlin projects: - - // The "internal" modifier can be used to restrict access to the same module. - // To make it possible to use internal methods from the main source set in test classes, - // the Kotlin Gradle plugin adds the original output directory of the Java task - // as "friendly directory" which makes it possible to access internal members - // of the main module. Also this directory should be available on classpath during compilation - - // This fails when we change the classes dir. The easiest fix is to prepend the - // classes from the "friendly directory" to the compile classpath. - val testCompile = tasks.findByName("compileTestKotlin") as AbstractCompile? - testCompile?.doFirst { - val originalClassesDirs = files((mainSourceSet as ExtensionAware).extra.get("classesDirsCopy")) - - testCompile.classpath = (testCompile.classpath - - mainSourceSet.output.classesDirs - + originalClassesDirs) - - // Since Kotlin 1.3.60, the friend paths available to the test compile task are calculated as the main source set's - // output.classesDirs. Since the classesDirs are excluded from the classpath (replaced by the originalClassesDirs), - // in order to be able to access the internals of 'main', tests need to receive the original classes dirs as a - // -Xfriend-paths compiler argument as well. - fun addFreeCompilerArgs(kotlinCompileTask: AbstractCompile, vararg args: String) { - val getKotlinOptions = kotlinCompileTask::class.java.getMethod("getKotlinOptions") - val kotlinOptions = getKotlinOptions(kotlinCompileTask) - - val getFreeCompilerArgs = kotlinOptions::class.java.getMethod("getFreeCompilerArgs") - val freeCompilerArgs = getFreeCompilerArgs(kotlinOptions) as List<*> - - val setFreeCompilerArgs = kotlinOptions::class.java.getMethod("setFreeCompilerArgs", List::class.java) - setFreeCompilerArgs(kotlinOptions, freeCompilerArgs + args) - } - addFreeCompilerArgs(testCompile, "-Xfriend-paths=" + originalClassesDirs.joinToString(",") { it.absolutePath }) - } - - } - val instrumentationClasspathCfg = configurations.create("instrumentationClasspath") dependencies { @@ -101,6 +61,46 @@ fun Project.configureFormInstrumentation() { // Ensure that our task is invoked when the source set is built sourceSetParam.compiledBy(instrumentTask) } + + plugins.matching { it::class.java.canonicalName.startsWith("org.jetbrains.kotlin.gradle.plugin") }.configureEach { + // When we change the output classes directory, Gradle will automatically configure + // the test compile tasks to use the instrumented classes. Normally this is fine, + // however, it causes problems for Kotlin projects: + + // The "internal" modifier can be used to restrict access to the same module. + // To make it possible to use internal methods from the main source set in test classes, + // the Kotlin Gradle plugin adds the original output directory of the Java task + // as "friendly directory" which makes it possible to access internal members + // of the main module. Also this directory should be available on classpath during compilation + + // This fails when we change the classes dir. The easiest fix is to prepend the + // classes from the "friendly directory" to the compile classpath. + if (!tasks.names.contains("compileTestKotlin")) return@configureEach + + tasks.named("compileTestKotlin").configure { + val originalClassesDirs = project.files((project.mainSourceSet as ExtensionAware).extra.get("classesDirsCopy")) + + classpath = (classpath + - project.mainSourceSet.output.classesDirs + + originalClassesDirs) + + // Since Kotlin 1.3.60, the friend paths available to the test compile task are calculated as the main source set's + // output.classesDirs. Since the classesDirs are excluded from the classpath (replaced by the originalClassesDirs), + // in order to be able to access the internals of 'main', tests need to receive the original classes dirs as a + // -Xfriend-paths compiler argument as well. + fun addFreeCompilerArgs(kotlinCompileTask: AbstractCompile, vararg args: String) { + val getKotlinOptions = kotlinCompileTask::class.java.getMethod("getKotlinOptions") + val kotlinOptions = getKotlinOptions(kotlinCompileTask) + + val getFreeCompilerArgs = kotlinOptions::class.java.getMethod("getFreeCompilerArgs") + val freeCompilerArgs = getFreeCompilerArgs(kotlinOptions) as List<*> + + val setFreeCompilerArgs = kotlinOptions::class.java.getMethod("setFreeCompilerArgs", List::class.java) + setFreeCompilerArgs(kotlinOptions, freeCompilerArgs + args) + } + addFreeCompilerArgs(this, "-Xfriend-paths=" + originalClassesDirs.joinToString(",") { it.absolutePath }) + } + } } } diff --git a/buildSrc/src/main/kotlin/tasks.kt b/buildSrc/src/main/kotlin/tasks.kt index 92b2781f41e..5e41e885271 100644 --- a/buildSrc/src/main/kotlin/tasks.kt +++ b/buildSrc/src/main/kotlin/tasks.kt @@ -9,6 +9,7 @@ import org.gradle.api.Project import org.gradle.api.Task +import org.gradle.api.artifacts.ProjectDependency import org.gradle.api.file.FileSystemOperations import org.gradle.api.internal.tasks.testing.filter.DefaultTestFilter import org.gradle.api.tasks.TaskProvider @@ -243,18 +244,25 @@ private fun Task.useAndroidConfiguration(systemPropertyName: String, configName: val configuration = with(project) { configurations.getOrCreate(configName) .also { - dependencies.add( - configName, - dependencies.project(":dependencies:android-sdk", configuration = configName) - ) + 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, configuration.singleFile.canonicalPath) + systemProperty(systemPropertyName, androidFilePath) } } } diff --git a/dependencies/android-sdk/build.gradle.kts b/dependencies/android-sdk/build.gradle.kts index 9503c4bc95e..7117662c672 100644 --- a/dependencies/android-sdk/build.gradle.kts +++ b/dependencies/android-sdk/build.gradle.kts @@ -1,3 +1,4 @@ +import org.gradle.configurationcache.extensions.serviceOf import org.gradle.internal.os.OperatingSystem import java.net.URI @@ -72,22 +73,27 @@ fun unzipSdkTask( additionalConfig: Configuration? = null, dirLevelsToSkipOnUnzip: Int = 0, ext: String = "zip", prepareTask: TaskProvider = prepareSdk, unzipFilter: CopySpec.() -> Unit = {} -): Task { +): TaskProvider { val id = "${sdkName}_$sdkVer" - val cfg = configurations.create(id) + val createdCfg = configurations.create(id) val dependency = "google:$sdkName:$sdkVer${coordinatesSuffix.takeIf { it.isNotEmpty() }?.let { ":$it" } ?: ""}@$ext" - dependencies.add(cfg.name, dependency) + dependencies.add(createdCfg.name, dependency) - val unzipTask = task("unzip_$id") { + val sdkDestDir = sdkDestDir + val unzipTask = tasks.register("unzip_$id") { + val cfg = project.configurations.getByName(id) dependsOn(cfg) inputs.files(cfg) - val targetDir = file("$sdkDestDir/$destinationSubdir") + val targetDir = project.file("$sdkDestDir/$destinationSubdir") outputs.dirs(targetDir) + val fs = project.serviceOf() + val archiveOperations = project.serviceOf() + val file = cfg.singleFile doFirst { - project.copy { + fs.copy { when (ext) { - "zip" -> from(zipTree(cfg.singleFile)) - "tar.gz" -> from(tarTree(resources.gzip(cfg.singleFile))) + "zip" -> from(archiveOperations.zipTree(file)) + "tar.gz" -> from(archiveOperations.tarTree(project.resources.gzip(file))) else -> throw GradleException("Don't know how to handle the extension \"$ext\"") } unzipFilter.invoke(this) diff --git a/libraries/examples/annotation-processor-example/build.gradle.kts b/libraries/examples/annotation-processor-example/build.gradle.kts index 2f6049a0b33..b9b1b951af3 100644 --- a/libraries/examples/annotation-processor-example/build.gradle.kts +++ b/libraries/examples/annotation-processor-example/build.gradle.kts @@ -18,5 +18,4 @@ dependencies { sourceSets { "test" {} -} - +} \ No newline at end of file