Added automatic JPMS support if module file is present in src dir.
This commit is contained in:
parent
8d94f7536b
commit
9b5fef1987
@ -16,15 +16,11 @@
|
||||
package dorkbox.gradle
|
||||
|
||||
import dorkbox.gradle.deps.DependencyScanner
|
||||
import dorkbox.gradle.jpms.JavaXConfiguration
|
||||
import dorkbox.gradle.jpms.SourceSetContainer2
|
||||
import dorkbox.gradle.jpms.JpmsMultiRelease
|
||||
import dorkbox.gradle.jpms.JpmsOnly
|
||||
import dorkbox.gradle.jpms.JpmsSourceSetContainer
|
||||
import dorkbox.os.OS
|
||||
import org.gradle.api.Action
|
||||
import org.gradle.api.GradleException
|
||||
import org.gradle.api.JavaVersion
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.Task
|
||||
import org.gradle.api.*
|
||||
import org.gradle.api.file.DuplicatesStrategy
|
||||
import org.gradle.api.specs.Specs
|
||||
import org.gradle.api.tasks.SourceSetContainer
|
||||
@ -115,7 +111,6 @@ open class StaticMethodsAndTools(private val project: Project) {
|
||||
* Shows info if kotlin is enabled, shows exact information as to what the source-set directories are for java and kotlin
|
||||
*/
|
||||
fun debug() {
|
||||
println("\tEnabling debug")
|
||||
debug = true
|
||||
|
||||
project.afterEvaluate {
|
||||
@ -687,6 +682,9 @@ open class StaticMethodsAndTools(private val project: Project) {
|
||||
kotlinActions(task.kotlinOptions)
|
||||
}
|
||||
})
|
||||
|
||||
// now we auto-check if it's necessary to enable JPMS support for PRIMARY locations (ie, not the src9 location)
|
||||
JpmsOnly.runIfNecessary(javaVersion, project, this)
|
||||
} catch (ignored: Exception) {
|
||||
}
|
||||
}
|
||||
@ -762,16 +760,16 @@ open class StaticMethodsAndTools(private val project: Project) {
|
||||
/**
|
||||
* Load JPMS for a specific java version using the default configuration
|
||||
*/
|
||||
fun jpms(javaVersion: JavaVersion): JavaXConfiguration {
|
||||
return JavaXConfiguration(javaVersion, project, this)
|
||||
fun jpms(javaVersion: JavaVersion): JpmsMultiRelease {
|
||||
return JpmsMultiRelease(javaVersion, project, this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Load and configure JPMS for a specific java version
|
||||
*/
|
||||
fun jpms(javaVersion: JavaVersion, block: SourceSetContainer2.() -> Unit): JavaXConfiguration {
|
||||
val javaX = JavaXConfiguration(javaVersion, project, this)
|
||||
block(SourceSetContainer2(javaX))
|
||||
fun jpms(javaVersion: JavaVersion, block: JpmsSourceSetContainer.() -> Unit): JpmsMultiRelease {
|
||||
val javaX = JpmsMultiRelease(javaVersion, project, this)
|
||||
block(JpmsSourceSetContainer(javaX))
|
||||
return javaX
|
||||
}
|
||||
|
||||
|
@ -17,11 +17,7 @@
|
||||
package dorkbox.gradle.jpms
|
||||
|
||||
import dorkbox.gradle.StaticMethodsAndTools
|
||||
import org.gradle.api.Action
|
||||
import org.gradle.api.GradleException
|
||||
import org.gradle.api.JavaVersion
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.Task
|
||||
import org.gradle.api.*
|
||||
import org.gradle.api.file.SourceDirectorySet
|
||||
import org.gradle.api.tasks.SourceSet
|
||||
import org.gradle.api.tasks.SourceSetContainer
|
||||
@ -42,13 +38,10 @@ import java.io.File
|
||||
//target/classes-java9 .
|
||||
|
||||
@Suppress("MemberVisibilityCanBePrivate", "ObjectLiteralToLambda")
|
||||
class JavaXConfiguration(javaVersion: JavaVersion, private val project: Project, gradleUtils: StaticMethodsAndTools) {
|
||||
class JpmsMultiRelease(javaVersion: JavaVersion, private val project: Project, gradleUtils: StaticMethodsAndTools) {
|
||||
val ver: String = javaVersion.majorVersion
|
||||
|
||||
// this cannot be ONLY a number, there must be something else -- intellij will *not* pickup the name if it's only a number
|
||||
val nameX = "_$ver"
|
||||
|
||||
// If the kotlin plugin is applied, and there is a compileKotlin task.. Then kotlin is enabled
|
||||
// If the kotlin plugin is applied, and there is a compileKotlin task. Then kotlin is enabled
|
||||
val hasKotlin = gradleUtils.hasKotlin
|
||||
|
||||
val moduleFile = project.projectDir.walkTopDown().find { it.name == "module-info.java" }
|
||||
@ -65,17 +58,19 @@ class JavaXConfiguration(javaVersion: JavaVersion, private val project: Project,
|
||||
lateinit var compileMainKotlin: KotlinCompile
|
||||
lateinit var compileTestKotlin: KotlinCompile
|
||||
|
||||
|
||||
val lower = "jpms"
|
||||
val upper = "Jpms"
|
||||
|
||||
// plugin provided
|
||||
val mainX: SourceSet = sourceSets.maybeCreate("main$nameX")
|
||||
val testX: SourceSet = sourceSets.maybeCreate("test$nameX")
|
||||
// These generate warnings in Gradle 8.x -- Fixed in 8.4
|
||||
val mainX: SourceSet = sourceSets.maybeCreate("${lower}Main")
|
||||
val testX: SourceSet = sourceSets.maybeCreate("${lower}Test")
|
||||
|
||||
// the compile task NAME must match the source-set name
|
||||
val compileMainXJava: JavaCompile = project.tasks.named("compileMain${nameX}Java", JavaCompile::class.java).get()
|
||||
val compileTestXJava: JavaCompile = project.tasks.named("compileTest${nameX}Java", JavaCompile::class.java).get()
|
||||
val compileMainXJava: JavaCompile = project.tasks.named("compile${upper}MainJava", JavaCompile::class.java).get()
|
||||
val compileTestXJava: JavaCompile = project.tasks.named("compile${upper}TestJava", JavaCompile::class.java).get()
|
||||
|
||||
val compileModuleInfoX: JavaCompile = project.tasks.create("compileModuleInfo$nameX", JavaCompile::class.java)
|
||||
val compileModuleInfoX: JavaCompile = project.tasks.create("compileJpmsModuleInfo", JavaCompile::class.java)
|
||||
|
||||
lateinit var compileMainXKotlin: TaskProvider<KotlinCompile>
|
||||
lateinit var compileTestXKotlin: TaskProvider<KotlinCompile>
|
||||
@ -83,7 +78,7 @@ class JavaXConfiguration(javaVersion: JavaVersion, private val project: Project,
|
||||
// have to create a task in order to the files to get "picked up" by intellij/gradle. No *test* task? Then gradle/intellij won't be able run
|
||||
// the tests, even if you MANUALLY tell intellij to run a test from the sources dir
|
||||
// https://docs.gradle.org/current/dsl/org.gradle.api.tasks.testing.Test.html
|
||||
val runTestX: Test = project.tasks.create("test${nameX}Test", Test::class.java)
|
||||
val runTestX: Test = project.tasks.create("${lower}Test", Test::class.java)
|
||||
|
||||
init {
|
||||
if (moduleFile == null) {
|
||||
@ -135,7 +130,7 @@ class JavaXConfiguration(javaVersion: JavaVersion, private val project: Project,
|
||||
})
|
||||
|
||||
if (hasKotlin) {
|
||||
val kotlin = project.extensions.getByType(org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension::class.java).sourceSets.getByName("main$nameX")
|
||||
val kotlin = project.extensions.getByType(org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension::class.java).sourceSets.getByName("${lower}Main")
|
||||
|
||||
kotlin.kotlin.apply {
|
||||
// I don't like the opinionated sonatype directory structure.
|
||||
@ -174,7 +169,7 @@ class JavaXConfiguration(javaVersion: JavaVersion, private val project: Project,
|
||||
})
|
||||
|
||||
if (hasKotlin) {
|
||||
val kotlin = project.extensions.getByType(org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension::class.java).sourceSets.getByName("test$nameX")
|
||||
val kotlin = project.extensions.getByType(org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension::class.java).sourceSets.getByName("${lower}Test")
|
||||
|
||||
kotlin.kotlin.apply {
|
||||
// I don't like the opinionated sonatype directory structure.
|
||||
@ -219,20 +214,20 @@ class JavaXConfiguration(javaVersion: JavaVersion, private val project: Project,
|
||||
//////////////
|
||||
|
||||
if (hasKotlin) {
|
||||
compileMainXKotlin = project.tasks.named("compileMain${nameX}Kotlin", KotlinCompile::class.java)
|
||||
compileTestXKotlin = project.tasks.named("compileTest${nameX}Kotlin", KotlinCompile::class.java)
|
||||
compileMainXKotlin = project.tasks.named("compile${upper}MainKotlin", KotlinCompile::class.java)
|
||||
compileTestXKotlin = project.tasks.named("compile${upper}TestKotlin", KotlinCompile::class.java)
|
||||
}
|
||||
|
||||
// have to setup the configurations, so dependencies work correctly
|
||||
val configs = project.configurations
|
||||
|
||||
configs.maybeCreate("main${nameX}Implementation").extendsFrom(configs.getByName("implementation")).extendsFrom(configs.getByName("compileOnly"))
|
||||
configs.maybeCreate("main${nameX}Runtime").extendsFrom(configs.getByName("implementation")).extendsFrom(configs.getByName("runtimeOnly"))
|
||||
configs.maybeCreate("main${nameX}CompileOnly").extendsFrom(configs.getByName("compileOnly"))
|
||||
configs.maybeCreate("${lower}MainImplementation").extendsFrom(configs.getByName("implementation")).extendsFrom(configs.getByName("compileOnly"))
|
||||
configs.maybeCreate("${lower}MainRuntime").extendsFrom(configs.getByName("implementation")).extendsFrom(configs.getByName("runtimeOnly"))
|
||||
configs.maybeCreate("${lower}MainCompileOnly").extendsFrom(configs.getByName("compileOnly"))
|
||||
|
||||
configs.maybeCreate("test${nameX}Implementation").extendsFrom(configs.getByName("testImplementation")).extendsFrom(configs.getByName("testCompileOnly"))
|
||||
configs.maybeCreate("test${nameX}Runtime").extendsFrom(configs.getByName("testImplementation")).extendsFrom(configs.getByName("testRuntimeOnly"))
|
||||
configs.maybeCreate("test${nameX}CompileOnly").extendsFrom(configs.getByName("testCompileOnly"))
|
||||
configs.maybeCreate("${upper}TestImplementation").extendsFrom(configs.getByName("testImplementation")).extendsFrom(configs.getByName("testCompileOnly"))
|
||||
configs.maybeCreate("${upper}TestRuntime").extendsFrom(configs.getByName("testImplementation")).extendsFrom(configs.getByName("testRuntimeOnly"))
|
||||
configs.maybeCreate("${upper}TestCompileOnly").extendsFrom(configs.getByName("testCompileOnly"))
|
||||
|
||||
|
||||
// setup task graph and compile version
|
||||
@ -278,7 +273,7 @@ class JavaXConfiguration(javaVersion: JavaVersion, private val project: Project,
|
||||
dependsOn(compileMainKotlin)
|
||||
}
|
||||
|
||||
val proj = this@JavaXConfiguration.project
|
||||
val proj = this@JpmsMultiRelease.project
|
||||
|
||||
val allSource = proj.files(
|
||||
main.allSource.srcDirs,
|
||||
@ -294,7 +289,7 @@ class JavaXConfiguration(javaVersion: JavaVersion, private val project: Project,
|
||||
inputs.property("moduleName", moduleName)
|
||||
|
||||
destinationDirectory.set(compileMainXJava.destinationDirectory.asFile.orNull)
|
||||
classpath = this@JavaXConfiguration.project.files() // this resets the classpath. we use the module-path instead!
|
||||
classpath = this@JpmsMultiRelease.project.files() // this resets the classpath. we use the module-path instead!
|
||||
|
||||
|
||||
// modules require this!
|
||||
@ -323,7 +318,7 @@ class JavaXConfiguration(javaVersion: JavaVersion, private val project: Project,
|
||||
override fun execute(task: Task) {
|
||||
task as JavaCompile
|
||||
|
||||
val intellijClasses = File("${this@JavaXConfiguration.project.buildDir}/classes-intellij")
|
||||
val intellijClasses = File("${this@JpmsMultiRelease.project.buildDir}/classes-intellij")
|
||||
if (intellijClasses.exists()) {
|
||||
// copy everything to intellij also. FORTUNATELY, we know it's only going to be the `module-info` and `package-info` classes!
|
||||
val directory = task.destinationDirectory.asFile.get()
|
162
src/dorkbox/gradle/jpms/JpmsOnly.kt
Normal file
162
src/dorkbox/gradle/jpms/JpmsOnly.kt
Normal file
@ -0,0 +1,162 @@
|
||||
/*
|
||||
* Copyright 2021 dorkbox, llc
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package dorkbox.gradle.jpms
|
||||
|
||||
import dorkbox.gradle.StaticMethodsAndTools
|
||||
import org.gradle.api.*
|
||||
import org.gradle.api.tasks.SourceSet
|
||||
import org.gradle.api.tasks.SourceSetContainer
|
||||
import org.gradle.api.tasks.compile.JavaCompile
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
import java.io.File
|
||||
|
||||
// from: http://mail.openjdk.java.net/pipermail/jigsaw-dev/2017-February/011306.html
|
||||
/// If you move the module-info.java to the top-level directory directory then I would expect this should work:
|
||||
//
|
||||
//javac --release 8 -d target/classes src/main/java/com/example/A.java src/main/java/com/example/Version.java
|
||||
//javac -d target/classes src/main/java/module-info.java
|
||||
//javac -d target/classes-java9 -cp target/classes src/main/java9/com/example/A.java
|
||||
//jar --create --file mr.jar -C target/classes . --release 9 -C
|
||||
//target/classes-java9 .
|
||||
|
||||
object JpmsOnly {
|
||||
|
||||
// check to see if we have a module-info.java file.
|
||||
// if we do, then we are "JPMS" enabled, and require some fiddling.
|
||||
// it is INCREDIBLY stupid this is as difficult as this is.
|
||||
// https://youtrack.jetbrains.com/issue/KT-55389/Gradle-plugin-should-expose-an-extension-method-to-automatically-enable-JPMS-for-Kotlin-compiled-output
|
||||
fun runIfNecessary(javaVersion: JavaVersion, project: Project, gradleUtils: StaticMethodsAndTools) {
|
||||
val ver = javaVersion.majorVersion.toIntOrNull()
|
||||
|
||||
if (ver == null || ver < 9) {
|
||||
// obviously not going to happen!
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// If the kotlin plugin is applied, and there is a compileKotlin task.. Then kotlin is enabled
|
||||
val hasKotlin = gradleUtils.hasKotlin
|
||||
|
||||
val moduleFile = project.projectDir.walkTopDown().find { it.name == "module-info.java" }
|
||||
var moduleName: String
|
||||
|
||||
if (moduleFile == null) {
|
||||
// Cannot manage JPMS build without a `module-info.java` file....
|
||||
return
|
||||
}
|
||||
|
||||
val srcName = moduleFile.parentFile.name
|
||||
if (srcName.startsWith("src") && srcName.last().isDigit()) {
|
||||
// if the module-file is in our srcX directory, we ignore these steps, since we would be building a multi-release jar
|
||||
return
|
||||
}
|
||||
|
||||
// also the source dirs have been configured/setup.
|
||||
moduleName = moduleFile.readLines()[0].split(" ")[1].trimEnd('{')
|
||||
if (moduleName.isEmpty()) {
|
||||
throw GradleException("The module name must be specified in the module-info file! Verify file: $moduleFile")
|
||||
}
|
||||
|
||||
val info = when {
|
||||
hasKotlin -> "Initializing JPMS $ver, Java/Kotlin [$moduleName]"
|
||||
else -> "Initializing JPMS $ver, Java [$moduleName]"
|
||||
}
|
||||
println("\t$info")
|
||||
|
||||
|
||||
|
||||
|
||||
val sourceSets = project.extensions.getByName("sourceSets") as SourceSetContainer
|
||||
|
||||
val main: SourceSet = sourceSets.named("main", org.gradle.api.tasks.SourceSet::class.java).get()
|
||||
val compileMainJava: JavaCompile = project.tasks.named("compileJava", JavaCompile::class.java).get()
|
||||
|
||||
lateinit var compileMainKotlin: KotlinCompile
|
||||
|
||||
if (hasKotlin) {
|
||||
// can only setup the NORMAL tasks first
|
||||
compileMainKotlin = project.tasks.named("compileKotlin", KotlinCompile::class.java).get()
|
||||
}
|
||||
|
||||
// make sure defaults are loaded
|
||||
StaticMethodsAndTools.idea(project) {
|
||||
if (module.sourceDirs == null) {
|
||||
module.sourceDirs = setOf<File>()
|
||||
}
|
||||
|
||||
// make sure there is something there!
|
||||
module.testSources.from(setOf<File>())
|
||||
module.testResources.from(setOf<File>())
|
||||
}
|
||||
|
||||
project.tasks.named("compileJava", JavaCompile::class.java) {
|
||||
// modules require this!
|
||||
it.doFirst(object: Action<Task> {
|
||||
override fun execute(task: Task) {
|
||||
task as JavaCompile
|
||||
|
||||
val allCompiled = if (hasKotlin) {
|
||||
project.files(compileMainJava.destinationDirectory.asFile.orNull, compileMainKotlin.destinationDirectory.asFile.orNull)
|
||||
} else {
|
||||
project.files(compileMainJava.destinationDirectory.asFile.orNull)
|
||||
}
|
||||
|
||||
// the SOURCE of the module-info.java file. It uses **EVERYTHING**
|
||||
task.options.compilerArgs.addAll(listOf(
|
||||
"-implicit:none",
|
||||
"-Xpkginfo:always", // compile the package-info.java files as well (normally it does not)
|
||||
"--module-path", main.compileClasspath.asPath,
|
||||
"--patch-module", "$moduleName=" + allCompiled.asPath // add our existing, compiled classes so module-info can find them
|
||||
))
|
||||
}
|
||||
})
|
||||
|
||||
it.doLast(object: Action<Task> {
|
||||
override fun execute(task: Task) {
|
||||
task as JavaCompile
|
||||
|
||||
val intellijClasses = File("${project.layout.buildDirectory.locationOnly.get()}/classes-intellij")
|
||||
if (intellijClasses.exists()) {
|
||||
// copy everything to intellij also. FORTUNATELY, we know it's only going to be the `module-info` and `package-info` classes!
|
||||
val directory = task.destinationDirectory.asFile.get()
|
||||
|
||||
val moduleInfo = directory.walkTopDown().filter { it.name == "module-info.class" }.toList()
|
||||
val packageInfo = directory.walkTopDown().filter { it.name == "package-info.class" }.toList()
|
||||
|
||||
val name = when {
|
||||
moduleInfo.isNotEmpty() && packageInfo.isNotEmpty() -> "module-info and package-info"
|
||||
moduleInfo.isNotEmpty() && packageInfo.isEmpty() -> "module-info"
|
||||
else -> "package-info"
|
||||
}
|
||||
|
||||
println("\tCopying $name files into the intellij classes location...")
|
||||
|
||||
moduleInfo.forEach {
|
||||
val newLocation = File(intellijClasses, it.relativeTo(directory).path)
|
||||
it.copyTo(newLocation, overwrite = true)
|
||||
}
|
||||
|
||||
packageInfo.forEach {
|
||||
val newLocation = File(intellijClasses, it.relativeTo(directory).path)
|
||||
it.copyTo(newLocation, overwrite = true)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
@ -18,7 +18,7 @@ package dorkbox.gradle.jpms
|
||||
|
||||
import org.gradle.api.tasks.SourceSet
|
||||
|
||||
class SourceSetContainer2(private val javaX: JavaXConfiguration) {
|
||||
class JpmsSourceSetContainer(private val javaX: JpmsMultiRelease) {
|
||||
fun main(block: SourceSet.() -> Unit) {
|
||||
block(javaX.mainX)
|
||||
}
|
Loading…
Reference in New Issue
Block a user