<lambda>null1 import org.gradle.api.*
2 import org.gradle.api.attributes.*
3 import org.gradle.api.file.*
4 import org.gradle.api.tasks.*
5 import org.gradle.api.tasks.bundling.*
6 import org.gradle.api.tasks.compile.*
7 import org.gradle.jvm.toolchain.*
8 import org.gradle.kotlin.dsl.*
9 import org.gradle.work.*
10 import org.jetbrains.kotlin.gradle.dsl.*
11
12 /**
13 * This object configures the Java compilation of a JPMS (aka Jigsaw) module descriptor.
14 * The source file for the module descriptor is expected at <project-dir>/src/module-info.java.
15 *
16 * To maintain backwards compatibility with Java 8, the jvm JAR is marked as a multi-release JAR
17 * with the module-info.class being moved to META-INF/versions/9/module-info.class.
18 *
19 * The Java toolchains feature of Gradle is used to detect or provision a JDK 11,
20 * which is used to compile the module descriptor.
21 */
22 object Java9Modularity {
23
24 /**
25 * Task that patches `module-info.java` and removes `requires kotlinx.atomicfu` directive.
26 *
27 * To have JPMS properly supported, Kotlin compiler **must** be supplied with the correct `module-info.java`.
28 * The correct module info has to contain `atomicfu` requirement because atomicfu plugin kicks-in **after**
29 * the compilation process. But `atomicfu` is compile-only dependency that shouldn't be present in the final
30 * `module-info.java` and that's exactly what this task ensures.
31 */
32 abstract class ProcessModuleInfoFile : DefaultTask() {
33 @get:InputFile
34 @get:NormalizeLineEndings
35 abstract val moduleInfoFile: RegularFileProperty
36
37 @get:OutputFile
38 abstract val processedModuleInfoFile: RegularFileProperty
39
40 private val projectPath = project.path
41
42 @TaskAction
43 fun process() {
44 val sourceFile = moduleInfoFile.get().asFile
45 if (!sourceFile.exists()) {
46 throw IllegalStateException("$sourceFile not found in $projectPath")
47 }
48 val outputFile = processedModuleInfoFile.get().asFile
49 sourceFile.useLines { lines ->
50 outputFile.outputStream().bufferedWriter().use { writer ->
51 for (line in lines) {
52 if ("kotlinx.atomicfu" in line) continue
53 writer.write(line)
54 writer.newLine()
55 }
56 }
57 }
58 }
59 }
60
61 @JvmStatic
62 fun configure(project: Project) = with(project) {
63 val javaToolchains = extensions.findByType(JavaToolchainService::class.java)
64 ?: error("Gradle JavaToolchainService is not available")
65 val target = when (val kotlin = extensions.getByName("kotlin")) {
66 is KotlinJvmProjectExtension -> kotlin.target
67 is KotlinMultiplatformExtension -> kotlin.targets.getByName("jvm")
68 else -> throw IllegalStateException("Unknown Kotlin project extension in $project")
69 }
70 val compilation = target.compilations.getByName("main")
71
72 // Force the use of JARs for compile dependencies, so any JPMS descriptors are picked up.
73 // For more details, see https://github.com/gradle/gradle/issues/890#issuecomment-623392772
74 configurations.getByName(compilation.compileDependencyConfigurationName).attributes {
75 attribute(
76 LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE,
77 objects.named(LibraryElements::class, LibraryElements.JAR)
78 )
79 }
80
81 val processModuleInfoFile by tasks.registering(ProcessModuleInfoFile::class) {
82 moduleInfoFile = file("${target.name.ifEmpty { "." }}/src/module-info.java")
83 processedModuleInfoFile = project.layout.buildDirectory.file("generated-sources/module-info-processor/module-info.java")
84 }
85
86 val compileJavaModuleInfo = tasks.register("compileModuleInfoJava", JavaCompile::class.java) {
87 val moduleName = project.name.replace('-', '.') // this module's name
88 val compileKotlinTask =
89 compilation.compileTaskProvider.get() as? org.jetbrains.kotlin.gradle.tasks.KotlinCompile
90 ?: error("Cannot access Kotlin compile task ${compilation.compileKotlinTaskName}")
91 val targetDir = compileKotlinTask.destinationDirectory.dir("../java9")
92
93 // Use a Java 11 compiler for the module-info.
94 javaCompiler = javaToolchains.compilerFor {
95 languageVersion = JavaLanguageVersion.of(11)
96 }
97
98 // Always compile kotlin classes before the module descriptor.
99 dependsOn(compileKotlinTask)
100
101 // Add the module-info source file.
102 // Note that we use the parent dir and an include filter,
103 // this is needed for Gradle's module detection to work in
104 // org.gradle.api.tasks.compile.JavaCompile.createSpec
105 source(processModuleInfoFile.map { it.processedModuleInfoFile.asFile.get().parentFile })
106 val generatedModuleInfoFile = processModuleInfoFile.flatMap { it.processedModuleInfoFile.asFile }
107 include { it.file == generatedModuleInfoFile.get() }
108
109 // Set the task outputs and destination directory
110 outputs.dir(targetDir)
111 destinationDirectory = targetDir
112
113 // Configure JVM compatibility
114 sourceCompatibility = JavaVersion.VERSION_1_9.toString()
115 targetCompatibility = JavaVersion.VERSION_1_9.toString()
116
117 // Set the Java release version.
118 options.release = 9
119
120 // Ignore warnings about using 'requires transitive' on automatic modules.
121 // not needed when compiling with recent JDKs, e.g. 17
122 options.compilerArgs.add("-Xlint:-requires-transitive-automatic")
123
124 // Patch the compileKotlinJvm output classes into the compilation so exporting packages works correctly.
125 val destinationDirProperty = compileKotlinTask.destinationDirectory.asFile
126 options.compilerArgumentProviders.add {
127 val kotlinCompileDestinationDir = destinationDirProperty.get()
128 listOf("--patch-module", "$moduleName=$kotlinCompileDestinationDir")
129 }
130
131 // Use the classpath of the compileKotlinJvm task.
132 // Also ensure that the module path is used instead of classpath.
133 classpath = compileKotlinTask.libraries
134 modularity.inferModulePath = true
135 }
136
137 tasks.named<Jar>(target.artifactsTaskName) {
138 manifest {
139 attributes("Multi-Release" to true)
140 }
141 from(compileJavaModuleInfo) {
142 // Include **only** file we are interested in as JavaCompile output also contains some tmp files
143 include("module-info.class")
144 into("META-INF/versions/9/")
145 }
146 }
147 }
148 }
149