xref: /aosp_15_r20/external/kotlinx.serialization/buildSrc/src/main/kotlin/Java9Modularity.kt (revision 57b5a4a64c534cf7f27ac9427ceab07f3d8ed3d8)
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