1*57b5a4a6SAndroid Build Coastguard Worker /* <lambda>null2*57b5a4a6SAndroid Build Coastguard Worker * Copyright 2017-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. 3*57b5a4a6SAndroid Build Coastguard Worker */ 4*57b5a4a6SAndroid Build Coastguard Worker 5*57b5a4a6SAndroid Build Coastguard Worker import org.gradle.api.* 6*57b5a4a6SAndroid Build Coastguard Worker import org.gradle.api.file.* 7*57b5a4a6SAndroid Build Coastguard Worker import org.gradle.api.provider.* 8*57b5a4a6SAndroid Build Coastguard Worker import org.gradle.api.tasks.* 9*57b5a4a6SAndroid Build Coastguard Worker import org.gradle.api.tasks.bundling.* 10*57b5a4a6SAndroid Build Coastguard Worker import org.gradle.api.tasks.compile.* 11*57b5a4a6SAndroid Build Coastguard Worker import org.gradle.jvm.toolchain.* 12*57b5a4a6SAndroid Build Coastguard Worker import org.gradle.kotlin.dsl.* 13*57b5a4a6SAndroid Build Coastguard Worker import org.gradle.language.base.plugins.LifecycleBasePlugin.* 14*57b5a4a6SAndroid Build Coastguard Worker import org.gradle.process.* 15*57b5a4a6SAndroid Build Coastguard Worker import org.jetbrains.kotlin.gradle.* 16*57b5a4a6SAndroid Build Coastguard Worker import org.jetbrains.kotlin.gradle.dsl.* 17*57b5a4a6SAndroid Build Coastguard Worker import org.jetbrains.kotlin.gradle.plugin.* 18*57b5a4a6SAndroid Build Coastguard Worker import org.jetbrains.kotlin.gradle.plugin.mpp.* 19*57b5a4a6SAndroid Build Coastguard Worker import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.util.* 20*57b5a4a6SAndroid Build Coastguard Worker import org.jetbrains.kotlin.gradle.targets.jvm.* 21*57b5a4a6SAndroid Build Coastguard Worker import org.jetbrains.kotlin.gradle.tasks.* 22*57b5a4a6SAndroid Build Coastguard Worker import org.jetbrains.kotlin.gradle.tasks.KotlinCompile 23*57b5a4a6SAndroid Build Coastguard Worker import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile 24*57b5a4a6SAndroid Build Coastguard Worker import org.jetbrains.kotlin.tooling.core.* 25*57b5a4a6SAndroid Build Coastguard Worker import java.io.* 26*57b5a4a6SAndroid Build Coastguard Worker import kotlin.reflect.* 27*57b5a4a6SAndroid Build Coastguard Worker import kotlin.reflect.full.* 28*57b5a4a6SAndroid Build Coastguard Worker 29*57b5a4a6SAndroid Build Coastguard Worker object Java9Modularity { 30*57b5a4a6SAndroid Build Coastguard Worker 31*57b5a4a6SAndroid Build Coastguard Worker @JvmStatic 32*57b5a4a6SAndroid Build Coastguard Worker @JvmOverloads 33*57b5a4a6SAndroid Build Coastguard Worker fun Project.configureJava9ModuleInfo(multiRelease: Boolean = true) { 34*57b5a4a6SAndroid Build Coastguard Worker val disableJPMS = this.rootProject.extra.has("disableJPMS") 35*57b5a4a6SAndroid Build Coastguard Worker val ideaActive = System.getProperty("idea.active") == "true" 36*57b5a4a6SAndroid Build Coastguard Worker if (disableJPMS || ideaActive) return 37*57b5a4a6SAndroid Build Coastguard Worker val kotlin = extensions.findByType<KotlinProjectExtension>() ?: return 38*57b5a4a6SAndroid Build Coastguard Worker val jvmTargets = kotlin.targets.filter { it is KotlinJvmTarget || it is KotlinWithJavaTarget<*, *> } 39*57b5a4a6SAndroid Build Coastguard Worker if (jvmTargets.isEmpty()) { 40*57b5a4a6SAndroid Build Coastguard Worker logger.warn("No Kotlin JVM targets found, can't configure compilation of module-info!") 41*57b5a4a6SAndroid Build Coastguard Worker } 42*57b5a4a6SAndroid Build Coastguard Worker jvmTargets.forEach { target -> 43*57b5a4a6SAndroid Build Coastguard Worker val artifactTask = tasks.getByName<Jar>(target.artifactsTaskName) { 44*57b5a4a6SAndroid Build Coastguard Worker if (multiRelease) { 45*57b5a4a6SAndroid Build Coastguard Worker manifest { 46*57b5a4a6SAndroid Build Coastguard Worker attributes("Multi-Release" to true) 47*57b5a4a6SAndroid Build Coastguard Worker } 48*57b5a4a6SAndroid Build Coastguard Worker } 49*57b5a4a6SAndroid Build Coastguard Worker } 50*57b5a4a6SAndroid Build Coastguard Worker 51*57b5a4a6SAndroid Build Coastguard Worker target.compilations.forEach { compilation -> 52*57b5a4a6SAndroid Build Coastguard Worker val compileKotlinTask = compilation.compileKotlinTask as KotlinCompile 53*57b5a4a6SAndroid Build Coastguard Worker val defaultSourceSet = compilation.defaultSourceSet 54*57b5a4a6SAndroid Build Coastguard Worker 55*57b5a4a6SAndroid Build Coastguard Worker // derive the names of the source set and compile module task 56*57b5a4a6SAndroid Build Coastguard Worker val sourceSetName = defaultSourceSet.name + "Module" 57*57b5a4a6SAndroid Build Coastguard Worker 58*57b5a4a6SAndroid Build Coastguard Worker kotlin.sourceSets.create(sourceSetName) { 59*57b5a4a6SAndroid Build Coastguard Worker val sourceFile = this.kotlin.find { it.name == "module-info.java" } 60*57b5a4a6SAndroid Build Coastguard Worker val targetDirectory = compileKotlinTask.destinationDirectory.map { 61*57b5a4a6SAndroid Build Coastguard Worker it.dir("../${it.asFile.name}Module") 62*57b5a4a6SAndroid Build Coastguard Worker } 63*57b5a4a6SAndroid Build Coastguard Worker 64*57b5a4a6SAndroid Build Coastguard Worker // only configure the compilation if necessary 65*57b5a4a6SAndroid Build Coastguard Worker if (sourceFile != null) { 66*57b5a4a6SAndroid Build Coastguard Worker // register and wire a task to verify module-info.java content 67*57b5a4a6SAndroid Build Coastguard Worker // 68*57b5a4a6SAndroid Build Coastguard Worker // this will compile the whole sources again with a JPMS-aware target Java version, 69*57b5a4a6SAndroid Build Coastguard Worker // so that the Kotlin compiler can do the necessary verifications 70*57b5a4a6SAndroid Build Coastguard Worker // while compiling with `jdk-release=1.8` those verifications are not done 71*57b5a4a6SAndroid Build Coastguard Worker // 72*57b5a4a6SAndroid Build Coastguard Worker // this task is only going to be executed when running with `check` or explicitly, 73*57b5a4a6SAndroid Build Coastguard Worker // not during normal build operations 74*57b5a4a6SAndroid Build Coastguard Worker val verifyModuleTask = registerVerifyModuleTask( 75*57b5a4a6SAndroid Build Coastguard Worker compileKotlinTask, 76*57b5a4a6SAndroid Build Coastguard Worker sourceFile 77*57b5a4a6SAndroid Build Coastguard Worker ) 78*57b5a4a6SAndroid Build Coastguard Worker tasks.named("check") { 79*57b5a4a6SAndroid Build Coastguard Worker dependsOn(verifyModuleTask) 80*57b5a4a6SAndroid Build Coastguard Worker } 81*57b5a4a6SAndroid Build Coastguard Worker 82*57b5a4a6SAndroid Build Coastguard Worker // register a new compile module task 83*57b5a4a6SAndroid Build Coastguard Worker val compileModuleTask = registerCompileModuleTask( 84*57b5a4a6SAndroid Build Coastguard Worker compileKotlinTask, 85*57b5a4a6SAndroid Build Coastguard Worker sourceFile, 86*57b5a4a6SAndroid Build Coastguard Worker targetDirectory 87*57b5a4a6SAndroid Build Coastguard Worker ) 88*57b5a4a6SAndroid Build Coastguard Worker 89*57b5a4a6SAndroid Build Coastguard Worker // add the resulting module descriptor to this target's artifact 90*57b5a4a6SAndroid Build Coastguard Worker artifactTask.from(compileModuleTask.map { it.destinationDirectory }) { 91*57b5a4a6SAndroid Build Coastguard Worker if (multiRelease) { 92*57b5a4a6SAndroid Build Coastguard Worker into("META-INF/versions/9/") 93*57b5a4a6SAndroid Build Coastguard Worker } 94*57b5a4a6SAndroid Build Coastguard Worker } 95*57b5a4a6SAndroid Build Coastguard Worker } else { 96*57b5a4a6SAndroid Build Coastguard Worker logger.info("No module-info.java file found in ${this.kotlin.srcDirs}, can't configure compilation of module-info!") 97*57b5a4a6SAndroid Build Coastguard Worker } 98*57b5a4a6SAndroid Build Coastguard Worker 99*57b5a4a6SAndroid Build Coastguard Worker // remove the source set to prevent Gradle warnings 100*57b5a4a6SAndroid Build Coastguard Worker kotlin.sourceSets.remove(this) 101*57b5a4a6SAndroid Build Coastguard Worker } 102*57b5a4a6SAndroid Build Coastguard Worker } 103*57b5a4a6SAndroid Build Coastguard Worker } 104*57b5a4a6SAndroid Build Coastguard Worker } 105*57b5a4a6SAndroid Build Coastguard Worker 106*57b5a4a6SAndroid Build Coastguard Worker /** 107*57b5a4a6SAndroid Build Coastguard Worker * Add a Kotlin compile task that compiles `module-info.java` source file and Kotlin sources together, 108*57b5a4a6SAndroid Build Coastguard Worker * the Kotlin compiler will parse and check module dependencies, 109*57b5a4a6SAndroid Build Coastguard Worker * but it currently won't compile to a module-info.class file. 110*57b5a4a6SAndroid Build Coastguard Worker */ 111*57b5a4a6SAndroid Build Coastguard Worker private fun Project.registerVerifyModuleTask( 112*57b5a4a6SAndroid Build Coastguard Worker compileTask: KotlinCompile, 113*57b5a4a6SAndroid Build Coastguard Worker sourceFile: File 114*57b5a4a6SAndroid Build Coastguard Worker ): TaskProvider<out KotlinJvmCompile> { 115*57b5a4a6SAndroid Build Coastguard Worker apply<KotlinApiPlugin>() 116*57b5a4a6SAndroid Build Coastguard Worker val verifyModuleTaskName = "verify${compileTask.name.removePrefix("compile").capitalize()}Module" 117*57b5a4a6SAndroid Build Coastguard Worker // work-around for https://youtrack.jetbrains.com/issue/KT-60542 118*57b5a4a6SAndroid Build Coastguard Worker val kotlinApiPlugin = plugins.getPlugin(KotlinApiPlugin::class) 119*57b5a4a6SAndroid Build Coastguard Worker val verifyModuleTask = kotlinApiPlugin.registerKotlinJvmCompileTask( 120*57b5a4a6SAndroid Build Coastguard Worker verifyModuleTaskName, 121*57b5a4a6SAndroid Build Coastguard Worker compileTask.compilerOptions.moduleName.get() 122*57b5a4a6SAndroid Build Coastguard Worker ) 123*57b5a4a6SAndroid Build Coastguard Worker verifyModuleTask { 124*57b5a4a6SAndroid Build Coastguard Worker group = VERIFICATION_GROUP 125*57b5a4a6SAndroid Build Coastguard Worker description = "Verify Kotlin sources for JPMS problems" 126*57b5a4a6SAndroid Build Coastguard Worker libraries.from(compileTask.libraries) 127*57b5a4a6SAndroid Build Coastguard Worker source(compileTask.sources) 128*57b5a4a6SAndroid Build Coastguard Worker source(compileTask.javaSources) 129*57b5a4a6SAndroid Build Coastguard Worker // part of work-around for https://youtrack.jetbrains.com/issue/KT-60541 130*57b5a4a6SAndroid Build Coastguard Worker @Suppress("INVISIBLE_MEMBER") 131*57b5a4a6SAndroid Build Coastguard Worker source(compileTask.scriptSources) 132*57b5a4a6SAndroid Build Coastguard Worker source(sourceFile) 133*57b5a4a6SAndroid Build Coastguard Worker destinationDirectory.set(temporaryDir) 134*57b5a4a6SAndroid Build Coastguard Worker multiPlatformEnabled.set(compileTask.multiPlatformEnabled) 135*57b5a4a6SAndroid Build Coastguard Worker compilerOptions { 136*57b5a4a6SAndroid Build Coastguard Worker jvmTarget.set(JvmTarget.JVM_9) 137*57b5a4a6SAndroid Build Coastguard Worker // To support LV override when set in aggregate builds 138*57b5a4a6SAndroid Build Coastguard Worker languageVersion.set(compileTask.compilerOptions.languageVersion) 139*57b5a4a6SAndroid Build Coastguard Worker freeCompilerArgs.addAll( 140*57b5a4a6SAndroid Build Coastguard Worker listOf("-Xjdk-release=9", "-Xsuppress-version-warnings", "-Xexpect-actual-classes") 141*57b5a4a6SAndroid Build Coastguard Worker ) 142*57b5a4a6SAndroid Build Coastguard Worker optIn.addAll(compileTask.kotlinOptions.options.optIn) 143*57b5a4a6SAndroid Build Coastguard Worker } 144*57b5a4a6SAndroid Build Coastguard Worker // work-around for https://youtrack.jetbrains.com/issue/KT-60583 145*57b5a4a6SAndroid Build Coastguard Worker inputs.files( 146*57b5a4a6SAndroid Build Coastguard Worker libraries.asFileTree.elements.map { libs -> 147*57b5a4a6SAndroid Build Coastguard Worker libs 148*57b5a4a6SAndroid Build Coastguard Worker .filter { it.asFile.exists() } 149*57b5a4a6SAndroid Build Coastguard Worker .map { 150*57b5a4a6SAndroid Build Coastguard Worker zipTree(it.asFile).filter { it.name == "module-info.class" } 151*57b5a4a6SAndroid Build Coastguard Worker } 152*57b5a4a6SAndroid Build Coastguard Worker } 153*57b5a4a6SAndroid Build Coastguard Worker ).withPropertyName("moduleInfosOfLibraries") 154*57b5a4a6SAndroid Build Coastguard Worker this as KotlinCompile 155*57b5a4a6SAndroid Build Coastguard Worker val kotlinPluginVersion = KotlinToolingVersion(kotlinApiPlugin.pluginVersion) 156*57b5a4a6SAndroid Build Coastguard Worker if (kotlinPluginVersion <= KotlinToolingVersion("1.9.255")) { 157*57b5a4a6SAndroid Build Coastguard Worker // part of work-around for https://youtrack.jetbrains.com/issue/KT-60541 158*57b5a4a6SAndroid Build Coastguard Worker @Suppress("UNCHECKED_CAST") 159*57b5a4a6SAndroid Build Coastguard Worker val ownModuleNameProp = (this::class.superclasses.first() as KClass<AbstractKotlinCompile<*>>) 160*57b5a4a6SAndroid Build Coastguard Worker .declaredMemberProperties 161*57b5a4a6SAndroid Build Coastguard Worker .find { it.name == "ownModuleName" } 162*57b5a4a6SAndroid Build Coastguard Worker ?.get(this) as? Property<String> 163*57b5a4a6SAndroid Build Coastguard Worker ownModuleNameProp?.set(compileTask.kotlinOptions.moduleName) 164*57b5a4a6SAndroid Build Coastguard Worker } 165*57b5a4a6SAndroid Build Coastguard Worker 166*57b5a4a6SAndroid Build Coastguard Worker val taskKotlinLanguageVersion = compilerOptions.languageVersion.orElse(KotlinVersion.DEFAULT) 167*57b5a4a6SAndroid Build Coastguard Worker @OptIn(InternalKotlinGradlePluginApi::class) 168*57b5a4a6SAndroid Build Coastguard Worker if (taskKotlinLanguageVersion.get() < KotlinVersion.KOTLIN_2_0) { 169*57b5a4a6SAndroid Build Coastguard Worker // part of work-around for https://youtrack.jetbrains.com/issue/KT-60541 170*57b5a4a6SAndroid Build Coastguard Worker @Suppress("INVISIBLE_MEMBER") 171*57b5a4a6SAndroid Build Coastguard Worker commonSourceSet.from(compileTask.commonSourceSet) 172*57b5a4a6SAndroid Build Coastguard Worker } else { 173*57b5a4a6SAndroid Build Coastguard Worker multiplatformStructure.refinesEdges.set(compileTask.multiplatformStructure.refinesEdges) 174*57b5a4a6SAndroid Build Coastguard Worker multiplatformStructure.fragments.set(compileTask.multiplatformStructure.fragments) 175*57b5a4a6SAndroid Build Coastguard Worker } 176*57b5a4a6SAndroid Build Coastguard Worker // part of work-around for https://youtrack.jetbrains.com/issue/KT-60541 177*57b5a4a6SAndroid Build Coastguard Worker // and work-around for https://youtrack.jetbrains.com/issue/KT-60582 178*57b5a4a6SAndroid Build Coastguard Worker incremental = false 179*57b5a4a6SAndroid Build Coastguard Worker } 180*57b5a4a6SAndroid Build Coastguard Worker return verifyModuleTask 181*57b5a4a6SAndroid Build Coastguard Worker } 182*57b5a4a6SAndroid Build Coastguard Worker 183*57b5a4a6SAndroid Build Coastguard Worker private fun Project.registerCompileModuleTask( 184*57b5a4a6SAndroid Build Coastguard Worker compileTask: KotlinCompile, 185*57b5a4a6SAndroid Build Coastguard Worker sourceFile: File, 186*57b5a4a6SAndroid Build Coastguard Worker targetDirectory: Provider<out Directory> 187*57b5a4a6SAndroid Build Coastguard Worker ) = tasks.register("${compileTask.name}Module", JavaCompile::class) { 188*57b5a4a6SAndroid Build Coastguard Worker // Configure the module compile task. 189*57b5a4a6SAndroid Build Coastguard Worker source(sourceFile) 190*57b5a4a6SAndroid Build Coastguard Worker classpath = files() 191*57b5a4a6SAndroid Build Coastguard Worker destinationDirectory.set(targetDirectory) 192*57b5a4a6SAndroid Build Coastguard Worker // use a Java 11 toolchain with release 9 option 193*57b5a4a6SAndroid Build Coastguard Worker // because for some OS / architecture combinations 194*57b5a4a6SAndroid Build Coastguard Worker // there are no Java 9 builds available 195*57b5a4a6SAndroid Build Coastguard Worker javaCompiler.set( 196*57b5a4a6SAndroid Build Coastguard Worker this@registerCompileModuleTask.the<JavaToolchainService>().compilerFor { 197*57b5a4a6SAndroid Build Coastguard Worker languageVersion.set(JavaLanguageVersion.of(11)) 198*57b5a4a6SAndroid Build Coastguard Worker } 199*57b5a4a6SAndroid Build Coastguard Worker ) 200*57b5a4a6SAndroid Build Coastguard Worker options.release.set(9) 201*57b5a4a6SAndroid Build Coastguard Worker 202*57b5a4a6SAndroid Build Coastguard Worker options.compilerArgumentProviders.add(object : CommandLineArgumentProvider { 203*57b5a4a6SAndroid Build Coastguard Worker @get:CompileClasspath 204*57b5a4a6SAndroid Build Coastguard Worker val compileClasspath = compileTask.libraries 205*57b5a4a6SAndroid Build Coastguard Worker 206*57b5a4a6SAndroid Build Coastguard Worker @get:CompileClasspath 207*57b5a4a6SAndroid Build Coastguard Worker val compiledClasses = compileTask.destinationDirectory 208*57b5a4a6SAndroid Build Coastguard Worker 209*57b5a4a6SAndroid Build Coastguard Worker @get:Input 210*57b5a4a6SAndroid Build Coastguard Worker val moduleName = sourceFile 211*57b5a4a6SAndroid Build Coastguard Worker .readLines() 212*57b5a4a6SAndroid Build Coastguard Worker .single { it.contains("module ") } 213*57b5a4a6SAndroid Build Coastguard Worker .substringAfter("module ") 214*57b5a4a6SAndroid Build Coastguard Worker .substringBefore(' ') 215*57b5a4a6SAndroid Build Coastguard Worker .trim() 216*57b5a4a6SAndroid Build Coastguard Worker 217*57b5a4a6SAndroid Build Coastguard Worker override fun asArguments() = mutableListOf( 218*57b5a4a6SAndroid Build Coastguard Worker // Provide the module path to the compiler instead of using a classpath. 219*57b5a4a6SAndroid Build Coastguard Worker // The module path should be the same as the classpath of the compiler. 220*57b5a4a6SAndroid Build Coastguard Worker "--module-path", 221*57b5a4a6SAndroid Build Coastguard Worker compileClasspath.asPath, 222*57b5a4a6SAndroid Build Coastguard Worker "--patch-module", 223*57b5a4a6SAndroid Build Coastguard Worker "$moduleName=${compiledClasses.get()}", 224*57b5a4a6SAndroid Build Coastguard Worker "-Xlint:-requires-transitive-automatic" 225*57b5a4a6SAndroid Build Coastguard Worker ) 226*57b5a4a6SAndroid Build Coastguard Worker }) 227*57b5a4a6SAndroid Build Coastguard Worker } 228*57b5a4a6SAndroid Build Coastguard Worker } 229