Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce Java 9 module compatibility #5543

Merged
merged 5 commits into from
Apr 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -61,17 +61,23 @@ tasks.withType(Jar) {
tasks.trimShadedJar.doLast {
// outjars is a file, so only one jar generated for sure
def trimmed = tasks.trimShadedJar.outJarFiles[0].toPath()
ant.jar(destfile: trimmed.toString(), update: true) {

ant.jar(destfile: trimmed.toString(), update: true, duplicate: 'fail') {
zipfileset(src: tasks.shadedJar.archivePath) {
include(name: 'META-INF/versions/**')
}

// Do not let Ant put the properties that harm the build reproducibility, such as JDK version.
delegate.manifest {
attribute(name: 'Created-By', value: "Gradle ${gradle.gradleVersion}")
}
}
}

tasks.shadedTest.exclude 'META-INF/versions/**'

dependencies {
mrJarVersions.each { version->
mrJarVersions.each { version ->
// Common to reference classes in main sourceset from Java 9 one (e.g., to return a common interface)
"java${version}Implementation" files(sourceSets.main.output.classesDirs) { builtBy compileJava }

Expand Down
1 change: 1 addition & 0 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ publishUrlForSnapshot=https://oss.sonatype.org/content/repositories/snapshots/
publishUsernameProperty=ossrhUsername
publishPasswordProperty=ossrhPassword
publishSignatureRequired=true
automaticModuleNames=true

# Gradle options
org.gradle.jvmargs=-Xmx2048m -XX:+HeapDumpOnOutOfMemoryError
Expand Down
28 changes: 28 additions & 0 deletions gradle/scripts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ sensible defaults. By applying them, you can:
- [Shading a multi-module project with `relocate` flag](#shading-a-multi-module-project-with-relocate-flag)
- [Setting a Java target version with the `java(\\d+)` flag](#setting-a-java-target-version-with-the-javad-flag)
- [Setting a Kotlin target version with the `kotlin(\\d+\\.\\d+)` flag](#setting-a-koltin-target-version-with-the-kotlindd-flag)
- [Automatic module names](#automatic-module-names)
- [Tagging conveniently with `release` task](#tagging-conveniently-with-release-task)

<!-- /MarkdownTOC -->
Expand Down Expand Up @@ -98,6 +99,7 @@ sensible defaults. By applying them, you can:
```
group=com.doe.john.myexample
version=0.0.1-SNAPSHOT
versionPattern=^[0-9]+\\.[0-9]+\\.[0-9]+$
projectName=My Example
projectUrl=https://www.example.com/
projectDescription=My example project
Expand All @@ -118,6 +120,7 @@ sensible defaults. By applying them, you can:
googleAnalyticsId=UA-XXXXXXXX
javaSourceCompatibility=1.8
javaTargetCompatibility=1.8
automaticModuleNames=false
```

5. That's all. You now have two Java subprojects with sensible defaults.
Expand Down Expand Up @@ -672,6 +675,31 @@ However, if you want to compile a Kotlin module with a different language versio

For example, `kotlin1.6` flag makes your Kotlin module compatible with language version 1.6 and API version 1.6.

## Automatic module names

By specifying the `automaticModuleNames=true` property in `settings.gradle`, every `java` project's JAR
file will contain the `Automatic-Module-Name` property in its `MANIFEST.MF`, auto-generated from the group ID
and artifact ID. For example:

- groupId: `com.example`, artifactId: `foo-bar`
- module name: `com.example.foo.bar`
- groupId: `com.example.foo`, artifactId: `foo-bar`
- module name: `com.example.foo.bar`

If enabled, each project with `java` flag will have the `automaticModuleName` property.

You can override the automatic module name of a certain project via the `automaticModuleNameOverrides`
extension property:

```groovy
ext {
// Change the automatic module name of project ':bar' to 'com.example.fubar'.
automaticModuleNameOverrides = [
':bar': 'com.example.fubar'
]
}
```

## Tagging conveniently with `release` task

The task called `release` is added at the top level project. It will update the
Expand Down
60 changes: 49 additions & 11 deletions gradle/scripts/lib/common-info.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -42,17 +42,9 @@ allprojects {
ext {
artifactId = {
// Use the overridden one if available.
if (rootProject.ext.has('artifactIdOverrides')) {
def overrides = rootProject.ext.artifactIdOverrides
if (!(overrides instanceof Map)) {
throw new IllegalStateException("artifactIdOverrides must be a Map: ${overrides}")
}

for (Map.Entry<String, String> e : overrides.entrySet()) {
if (rootProject.project(e.key) == project) {
return e.value
}
}
def overriddenArtifactId = findOverridden('artifactIdOverrides', project)
if (overriddenArtifactId != null) {
return overriddenArtifactId
}

// Generate from the project names otherwise.
Expand All @@ -70,3 +62,49 @@ allprojects {
}.call()
}
}

// Check whether to enable automatic module names.
def isAutomaticModuleNameEnabled = 'true' == rootProject.findProperty('automaticModuleNames')

allprojects {
ext {
automaticModuleName = {
if (!isAutomaticModuleNameEnabled) {
return null
}

// Use the overridden one if available.
def overriddenAutomaticModuleName = findOverridden('automaticModuleNameOverrides', project)
if (overriddenAutomaticModuleName != null) {
return overriddenAutomaticModuleName
}

// Generate from the groupId and artifactId otherwise.
def groupIdComponents = String.valueOf(rootProject.group).split("\\.").toList()
def artifactIdComponents =
String.valueOf(project.ext.artifactId).replace('-', '.').split("\\.").toList()
if (groupIdComponents.last() == artifactIdComponents.first()) {
return String.join('.', groupIdComponents + artifactIdComponents.drop(1))
} else {
return String.join('.', groupIdComponents + artifactIdComponents)
}
}.call()
}
}

def findOverridden(String overridesPropertyName, Project project) {
if (rootProject.ext.has(overridesPropertyName)) {
def overrides = rootProject.ext.get(overridesPropertyName)
if (!(overrides instanceof Map)) {
throw new IllegalStateException("rootProject.ext.${overridesPropertyName} must be a Map: ${overrides}")
}

for (Map.Entry<String, String> e : overrides.entrySet()) {
if (rootProject.project(e.key) == project) {
return String.valueOf(e.value)
}
}
}

return null
}
11 changes: 11 additions & 0 deletions gradle/scripts/lib/java-shade.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,23 @@ configure(relocatedProjects) {
configureShadowTask(project, delegate, true)
archiveBaseName.set("${project.archivesBaseName}-shaded")

// Exclude the legacy file listing.
exclude '/META-INF/INDEX.LIST'
// Exclude the class signature files.
exclude '/META-INF/*.SF'
exclude '/META-INF/*.DSA'
exclude '/META-INF/*.RSA'
// Exclude the files generated by Maven
exclude '/META-INF/maven/**'
// Exclude the module metadata that'll become invalid after relocation.
exclude '**/module-info.class'

// Set the 'Automatic-Module-Name' property in MANIFEST.MF.
if (project.ext.automaticModuleName != null) {
manifest {
attributes('Automatic-Module-Name': project.ext.automaticModuleName)
}
}
}
tasks.assemble.dependsOn tasks.shadedJar

Expand Down
16 changes: 16 additions & 0 deletions gradle/scripts/lib/java.gradle
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import java.util.regex.Pattern

// Determine which version of JDK should be used for builds.
def buildJdkVersion = Integer.parseInt(JavaVersion.current().getMajorVersion())
if (rootProject.hasProperty('buildJdkVersion')) {
def jdkVersion = Integer.parseInt(String.valueOf(rootProject.findProperty('buildJdkVersion')))
Expand Down Expand Up @@ -139,6 +140,12 @@ configure(projectsWithFlags('java')) {
registerFeature('optional') {
usingSourceSet(sourceSets.main)
}

// Do not let Gradle infer the module path if automatic module name is enabled,
// because it means the JAR will rely on JDK's automatic module metadata generation.
if (project.ext.automaticModuleName != null) {
modularity.inferModulePath = false
}
}

// Set the sensible compiler options.
Expand All @@ -154,6 +161,15 @@ configure(projectsWithFlags('java')) {
options.compilerArgs += '-parameters'
}

// Set the 'Automatic-Module-Name' property in 'MANIFEST.MF' if `automaticModuleName` is not null.
if (project.ext.automaticModuleName != null) {
tasks.named('jar') {
manifest {
attributes('Automatic-Module-Name': project.ext.automaticModuleName)
}
}
}

project.ext.configureFlakyTests = { Test testTask ->
def flakyTests = rootProject.findProperty('flakyTests')
if (flakyTests == 'true') {
Expand Down
10 changes: 9 additions & 1 deletion gradle/scripts/lib/prerequisite.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,15 @@ plugins {
''')
}

['projectName', 'projectUrl', 'inceptionYear', 'licenseName', 'licenseUrl', 'scmUrl', 'scmConnection',
['group', 'version', 'projectName', 'projectUrl', 'inceptionYear', 'licenseName', 'licenseUrl', 'scmUrl', 'scmConnection',
'scmDeveloperConnection', 'publishUrlForRelease', 'publishUrlForSnapshot', 'publishUsernameProperty',
'publishPasswordProperty'].each {
if (rootProject.findProperty(it) == null) {
throw new IllegalStateException('''Add project info properties to gradle.properties:

group=com.doe.john.myexample
version=0.0.1-SNAPSHOT
versionPattern=^[0-9]+\\\\.[0-9]+\\\\.[0-9]+$
projectName=My Example
projectUrl=https://www.example.com/
projectDescription=My example project
Expand All @@ -31,7 +34,12 @@ publishUrlForRelease=https://oss.sonatype.org/service/local/staging/deploy/maven
publishUrlForSnapshot=https://oss.sonatype.org/content/repositories/snapshots/
publishUsernameProperty=ossrhUsername
publishPasswordProperty=ossrhPassword
publishSignatureRequired=true
versionPattern=^[0-9]+\\\\.[0-9]+\\\\.[0-9]+$
googleAnalyticsId=UA-XXXXXXXX
javaSourceCompatibility=1.8
javaTargetCompatibility=1.8
automaticModuleNames=false
''')
}
}
Loading