|
3 | 3 | */
|
4 | 4 |
|
5 | 5 | import org.gradle.api.*
|
| 6 | +import org.gradle.api.file.* |
| 7 | +import org.gradle.api.provider.* |
| 8 | +import org.gradle.api.tasks.* |
6 | 9 | import org.gradle.api.tasks.bundling.*
|
7 | 10 | import org.gradle.api.tasks.compile.*
|
| 11 | +import org.gradle.jvm.toolchain.* |
8 | 12 | import org.gradle.kotlin.dsl.*
|
| 13 | +import org.gradle.language.base.plugins.LifecycleBasePlugin.* |
| 14 | +import org.gradle.process.* |
9 | 15 | import org.jetbrains.kotlin.gradle.dsl.*
|
| 16 | +import org.jetbrains.kotlin.gradle.plugin.* |
10 | 17 | import org.jetbrains.kotlin.gradle.plugin.mpp.*
|
11 |
| -import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.* |
12 | 18 | import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.util.*
|
13 | 19 | import org.jetbrains.kotlin.gradle.targets.jvm.*
|
14 | 20 | import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
| 21 | +import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile |
15 | 22 | import java.io.*
|
16 | 23 |
|
17 | 24 | object Java9Modularity {
|
@@ -42,70 +49,154 @@ object Java9Modularity {
|
42 | 49 |
|
43 | 50 | // derive the names of the source set and compile module task
|
44 | 51 | val sourceSetName = defaultSourceSet.name + "Module"
|
45 |
| - val compileModuleTaskName = compileKotlinTask.name + "Module" |
46 | 52 |
|
47 | 53 | kotlin.sourceSets.create(sourceSetName) {
|
48 | 54 | val sourceFile = this.kotlin.find { it.name == "module-info.java" }
|
49 |
| - val targetFile = compileKotlinTask.destinationDirectory.file("../module-info.class").get().asFile |
| 55 | + val targetDirectory = compileKotlinTask.destinationDirectory.map { |
| 56 | + it.dir("../${it.asFile.name}Module") |
| 57 | + } |
50 | 58 |
|
51 | 59 | // only configure the compilation if necessary
|
52 | 60 | if (sourceFile != null) {
|
53 |
| - // the default source set depends on this new source set |
54 |
| - defaultSourceSet.dependsOn(this) |
| 61 | + // register and wire a task to verify module-info.java content |
| 62 | + // |
| 63 | + // this will compile the whole sources again with a JPMS-aware target Java version, |
| 64 | + // so that the Kotlin compiler can do the necessary verifications |
| 65 | + // while compiling with `jdk-release=1.8` those verifications are not done |
| 66 | + // |
| 67 | + // this task is only going to be executed when running with `check` or explicitly, |
| 68 | + // not during normal build operations |
| 69 | + val verifyModuleTask = registerVerifyModuleTask( |
| 70 | + compileKotlinTask, |
| 71 | + sourceFile |
| 72 | + ) |
| 73 | + tasks.named("check") { |
| 74 | + dependsOn(verifyModuleTask) |
| 75 | + } |
55 | 76 |
|
56 | 77 | // register a new compile module task
|
57 |
| - val compileModuleTask = registerCompileModuleTask(compileModuleTaskName, compileKotlinTask, sourceFile, targetFile) |
| 78 | + val compileModuleTask = registerCompileModuleTask( |
| 79 | + compileKotlinTask, |
| 80 | + sourceFile, |
| 81 | + targetDirectory |
| 82 | + ) |
58 | 83 |
|
59 | 84 | // add the resulting module descriptor to this target's artifact
|
60 |
| - artifactTask.dependsOn(compileModuleTask) |
61 |
| - artifactTask.from(targetFile) { |
| 85 | + artifactTask.from(compileModuleTask) { |
62 | 86 | if (multiRelease) {
|
63 | 87 | into("META-INF/versions/9/")
|
64 | 88 | }
|
65 | 89 | }
|
66 | 90 | } else {
|
67 | 91 | logger.info("No module-info.java file found in ${this.kotlin.srcDirs}, can't configure compilation of module-info!")
|
68 |
| - // remove the source set to prevent Gradle warnings |
69 |
| - kotlin.sourceSets.remove(this) |
70 | 92 | }
|
| 93 | + |
| 94 | + // remove the source set to prevent Gradle warnings |
| 95 | + kotlin.sourceSets.remove(this) |
71 | 96 | }
|
72 | 97 | }
|
73 | 98 | }
|
74 | 99 | }
|
75 | 100 |
|
76 |
| - private fun Project.registerCompileModuleTask(taskName: String, compileTask: KotlinCompile, sourceFile: File, targetFile: File) = |
77 |
| - tasks.register(taskName, JavaCompile::class) { |
78 |
| - // Also add the module-info.java source file to the Kotlin compile task; |
79 |
| - // the Kotlin compiler will parse and check module dependencies, |
80 |
| - // but it currently won't compile to a module-info.class file. |
81 |
| - compileTask.source(sourceFile) |
| 101 | + /** |
| 102 | + * Add a Kotlin compile task that compiles `module-info.java` source file and Kotlin sources together, |
| 103 | + * the Kotlin compiler will parse and check module dependencies, |
| 104 | + * but it currently won't compile to a module-info.class file. |
| 105 | + */ |
| 106 | + private fun Project.registerVerifyModuleTask( |
| 107 | + compileTask: KotlinCompile, |
| 108 | + sourceFile: File |
| 109 | + ): TaskProvider<out KotlinJvmCompile> { |
| 110 | + apply<KotlinBaseApiPlugin>() |
| 111 | + val verifyModuleTaskName = "verify${compileTask.name.removePrefix("compile").capitalize()}Module" |
| 112 | + // work-around for https://youtrack.jetbrains.com/issue/KT-60542 |
| 113 | + val verifyModuleTask = plugins |
| 114 | + .findPlugin(KotlinBaseApiPlugin::class)!! |
| 115 | + .registerKotlinJvmCompileTask(verifyModuleTaskName) |
| 116 | + verifyModuleTask { |
| 117 | + group = VERIFICATION_GROUP |
| 118 | + description = "Verify Kotlin sources for JPMS problems" |
| 119 | + libraries.from(compileTask.libraries) |
| 120 | + source(compileTask.sources) |
| 121 | + source(compileTask.javaSources) |
| 122 | + // part of work-around for https://youtrack.jetbrains.com/issue/KT-60541 |
| 123 | + @Suppress("INVISIBLE_MEMBER") |
| 124 | + source(compileTask.scriptSources) |
| 125 | + source(sourceFile) |
| 126 | + destinationDirectory.set(temporaryDir) |
| 127 | + multiPlatformEnabled.set(compileTask.multiPlatformEnabled) |
| 128 | + kotlinOptions { |
| 129 | + moduleName = compileTask.kotlinOptions.moduleName |
| 130 | + jvmTarget = "9" |
| 131 | + freeCompilerArgs += "-Xjdk-release=9" |
| 132 | + } |
| 133 | + // work-around for https://youtrack.jetbrains.com/issue/KT-60583 |
| 134 | + inputs.files( |
| 135 | + libraries.asFileTree.elements.map { libs -> |
| 136 | + libs |
| 137 | + .filter { it.asFile.exists() } |
| 138 | + .map { |
| 139 | + zipTree(it.asFile).filter { it.name == "module-info.class" } |
| 140 | + } |
| 141 | + } |
| 142 | + ).withPropertyName("moduleInfosOfLibraries") |
| 143 | + this as KotlinCompile |
| 144 | + // part of work-around for https://youtrack.jetbrains.com/issue/KT-60541 |
| 145 | + @Suppress("DEPRECATION") |
| 146 | + ownModuleName.set(compileTask.kotlinOptions.moduleName) |
| 147 | + // part of work-around for https://youtrack.jetbrains.com/issue/KT-60541 |
| 148 | + @Suppress("INVISIBLE_MEMBER") |
| 149 | + commonSourceSet.from(compileTask.commonSourceSet) |
| 150 | + // part of work-around for https://youtrack.jetbrains.com/issue/KT-60541 |
| 151 | + // and work-around for https://youtrack.jetbrains.com/issue/KT-60582 |
| 152 | + incremental = false |
| 153 | + } |
| 154 | + return verifyModuleTask |
| 155 | + } |
| 156 | + |
| 157 | + private fun Project.registerCompileModuleTask( |
| 158 | + compileTask: KotlinCompile, |
| 159 | + sourceFile: File, |
| 160 | + targetDirectory: Provider<out Directory> |
| 161 | + ) = tasks.register("${compileTask.name}Module", JavaCompile::class) { |
| 162 | + // Configure the module compile task. |
| 163 | + source(sourceFile) |
| 164 | + classpath = files() |
| 165 | + destinationDirectory.set(targetDirectory) |
| 166 | + // use a Java 11 toolchain with release 9 option |
| 167 | + // because for some OS / architecture combinations |
| 168 | + // there are no Java 9 builds available |
| 169 | + javaCompiler.set( |
| 170 | + this@registerCompileModuleTask.the<JavaToolchainService>().compilerFor { |
| 171 | + languageVersion.set(JavaLanguageVersion.of(11)) |
| 172 | + } |
| 173 | + ) |
| 174 | + options.release.set(9) |
82 | 175 |
|
| 176 | + options.compilerArgumentProviders.add(object : CommandLineArgumentProvider { |
| 177 | + @get:CompileClasspath |
| 178 | + val compileClasspath = compileTask.libraries |
83 | 179 |
|
84 |
| - // Configure the module compile task. |
85 |
| - dependsOn(compileTask) |
86 |
| - source(sourceFile) |
87 |
| - outputs.file(targetFile) |
88 |
| - classpath = files() |
89 |
| - destinationDirectory.set(compileTask.destinationDirectory) |
90 |
| - sourceCompatibility = JavaVersion.VERSION_1_9.toString() |
91 |
| - targetCompatibility = JavaVersion.VERSION_1_9.toString() |
| 180 | + @get:CompileClasspath |
| 181 | + val compiledClasses = compileTask.destinationDirectory |
92 | 182 |
|
93 |
| - doFirst { |
| 183 | + @get:Input |
| 184 | + val moduleName = sourceFile |
| 185 | + .readLines() |
| 186 | + .single { it.contains("module ") } |
| 187 | + .substringAfter("module ") |
| 188 | + .substringBefore(' ') |
| 189 | + .trim() |
| 190 | + |
| 191 | + override fun asArguments() = mutableListOf( |
94 | 192 | // Provide the module path to the compiler instead of using a classpath.
|
95 | 193 | // The module path should be the same as the classpath of the compiler.
|
96 |
| - options.compilerArgs = listOf( |
97 |
| - "--release", "9", |
98 |
| - "--module-path", compileTask.libraries.asPath, |
99 |
| - "-Xlint:-requires-transitive-automatic" |
100 |
| - ) |
101 |
| - } |
102 |
| - |
103 |
| - doLast { |
104 |
| - // Move the compiled file out of the Kotlin compile task's destination dir, |
105 |
| - // so it won't disturb Gradle's caching mechanisms. |
106 |
| - val compiledFile = destinationDirectory.file(targetFile.name).get().asFile |
107 |
| - targetFile.parentFile.mkdirs() |
108 |
| - compiledFile.renameTo(targetFile) |
109 |
| - } |
110 |
| - } |
| 194 | + "--module-path", |
| 195 | + compileClasspath.asPath, |
| 196 | + "--patch-module", |
| 197 | + "$moduleName=${compiledClasses.get()}", |
| 198 | + "-Xlint:-requires-transitive-automatic" |
| 199 | + ) |
| 200 | + }) |
| 201 | + } |
111 | 202 | }
|
0 commit comments