Skip to content
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
5 changes: 4 additions & 1 deletion .github/workflows/ci-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ jobs:
strategy:
matrix:
os: [ubuntu, macos, windows]
fail-fast: false
runs-on: ${{ matrix.os }}-latest
steps:
- name: standardize git line endings
Expand All @@ -21,7 +22,9 @@ jobs:
- uses: actions/setup-java@v5
with:
distribution: temurin
java-version: 17
java-version: |
25.0.3
17.0.19
- uses: gradle/actions/setup-gradle@v6
with:
build-scan-terms-of-use-url: "https://gradle.com/help/legal-terms-of-use"
Expand Down
4 changes: 3 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ jobs:
- uses: actions/setup-java@v5
with:
distribution: temurin
java-version: 17
java-version: |
25.0.3
17.0.19
- uses: gradle/actions/setup-gradle@v6
with:
build-scan-terms-of-use-url: "https://gradle.com/help/legal-terms-of-use"
Expand Down
10 changes: 9 additions & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,15 @@ publishingConventions {

testingConventions { testGradleVersions("7.4", "7.6.5", "8.0.2", "8.14.2") }

tasks.test {
tasks.withType<Test>().configureEach {
inputs.property("operatingSystemName", System.getProperty("os.name"))
inputs.property("operatingSystemArch", System.getProperty("os.arch"))
environment["JAVA_25"] =
javaToolchains
.launcherFor { languageVersion = JavaLanguageVersion.of(25) }
.get()
.metadata
.installationPath
.asFile
.absolutePath
}
1 change: 1 addition & 0 deletions gradle/gradle-daemon-jvm.properties
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
# 17.0.19
toolchainVersion=17
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
import org.gradle.util.GradleVersion;
import org.gradlex.javamodule.packaging.internal.HostIdentification;
import org.gradlex.javamodule.packaging.model.Target;
import org.gradlex.javamodule.packaging.tasks.FatModuleJar;
import org.gradlex.javamodule.packaging.tasks.Jpackage;
import org.gradlex.javamodule.packaging.tasks.ValidateHostSystemAction;
import org.jspecify.annotations.NullMarked;
Expand All @@ -56,6 +57,7 @@ public abstract class JavaModulePackagingExtension {
private static final Attribute<Boolean> JAVA_MODULE_ATTRIBUTE = Attribute.of("javaModule", Boolean.class);
private static final String INTERNAL = "internal";
private static final String JPACKAGE = "jpackage";
private static final String FAT_MODULE_JAR = "fatModuleJar";

private final Project project;

Expand Down Expand Up @@ -95,7 +97,6 @@ public JavaModulePackagingExtension(Project project) {
/**
* Retrieve the target with the given 'label'. If the target does not yet exist, it will be created.
*/
@SuppressWarnings("unused")
public Target target(String label) {
return target(label, target -> {});
}
Expand All @@ -120,7 +121,6 @@ public Target target(String label, Action<? super Target> action) {
/**
* Configure all targets for the given OS.
*/
@SuppressWarnings("unused")
public void targetsWithOs(String operatingSystem, Action<? super Target> action) {
NamedDomainObjectSet<Target> matches =
targets.matching(t -> t.getOperatingSystem().isPresent()
Expand All @@ -132,7 +132,6 @@ public void targetsWithOs(String operatingSystem, Action<? super Target> action)
* Set a 'primary target'. Standard Gradle tasks that are not bound to a specific target – like 'assemble' – use
* this 'primary target'.
*/
@SuppressWarnings("unused")
public Target primaryTarget(Target target) {
SourceSetContainer sourceSets = project.getExtensions().getByType(SourceSetContainer.class);
ConfigurationContainer configurations = project.getConfigurations();
Expand All @@ -155,7 +154,6 @@ public Target primaryTarget(Target target) {
/**
* Set a test suite to be 'multi-target'. This registers an additional 'test' task for each target.
*/
@SuppressWarnings({"unused", "UnstableApiUsage"})
public TestSuite multiTargetTestSuite(TestSuite testSuite) {
if (!(testSuite instanceof JvmTestSuite)) {
return testSuite;
Expand Down Expand Up @@ -183,7 +181,18 @@ public TestSuite multiTargetTestSuite(TestSuite testSuite) {
return testSuite;
}

void maybeAddSingleDefaultTarget(Target target) {
if (targets.isEmpty()) {
targets.add(target);
newTarget(target, true);
}
}

private void newTarget(Target target) {
newTarget(target, false);
}

private void newTarget(Target target, boolean singleDefault) {
target.getPackageTypes().convention(target.getOperatingSystem().map(os -> {
switch (os) {
case WINDOWS:
Expand All @@ -200,35 +209,41 @@ private void newTarget(Target target) {
SourceSetContainer sourceSets = project.getExtensions().getByType(SourceSetContainer.class);

sourceSets.all(sourceSet -> {
Configuration internal = maybeCreateInternalConfiguration();
configurations.create(
target.getName() + capitalize(sourceSet.getCompileClasspathConfigurationName()), c -> {
c.setCanBeConsumed(false);
setInvisible(c);
configureJavaStandardAttributes(c, Usage.JAVA_API);
configureTargetAttributes(c, target);
c.extendsFrom(
configurations.getByName(sourceSet.getImplementationConfigurationName()),
configurations.getByName(sourceSet.getCompileOnlyConfigurationName()),
internal);
});
Configuration runtimeClasspath = configurations.create(
target.getName() + capitalize(sourceSet.getRuntimeClasspathConfigurationName()), c -> {
c.setCanBeConsumed(false);
setInvisible(c);
configureJavaStandardAttributes(c, Usage.JAVA_RUNTIME);
configureTargetAttributes(c, target);
c.extendsFrom(
configurations.getByName(sourceSet.getImplementationConfigurationName()),
configurations.getByName(sourceSet.getRuntimeOnlyConfigurationName()),
internal);
});
Configuration runtimeClasspath;
if (singleDefault) {
runtimeClasspath = configurations.getByName(sourceSet.getRuntimeClasspathConfigurationName());
} else {
Configuration internal = maybeCreateInternalConfiguration();
configurations.create(
target.getName() + capitalize(sourceSet.getCompileClasspathConfigurationName()), c -> {
c.setCanBeConsumed(false);
setInvisible(c);
configureJavaStandardAttributes(c, Usage.JAVA_API);
configureTargetAttributes(c, target);
c.extendsFrom(
configurations.getByName(sourceSet.getImplementationConfigurationName()),
configurations.getByName(sourceSet.getCompileOnlyConfigurationName()),
internal);
});
runtimeClasspath = configurations.create(
target.getName() + capitalize(sourceSet.getRuntimeClasspathConfigurationName()), c -> {
c.setCanBeConsumed(false);
setInvisible(c);
configureJavaStandardAttributes(c, Usage.JAVA_RUNTIME);
configureTargetAttributes(c, target);
c.extendsFrom(
configurations.getByName(sourceSet.getImplementationConfigurationName()),
configurations.getByName(sourceSet.getRuntimeOnlyConfigurationName()),
internal);
});
}

if (SourceSet.isMain(sourceSet)) {
project.getPlugins()
.withType(
ApplicationPlugin.class,
p -> registerTargetSpecificTasks(target, sourceSet.getJarTaskName(), runtimeClasspath));
p -> registerTargetSpecificTasks(
target, singleDefault, sourceSet.getJarTaskName(), runtimeClasspath));
}
});
}
Expand Down Expand Up @@ -268,13 +283,16 @@ private void configureTargetAttributes(Configuration resolvable, Target target)
target.getArchitecture().map(name -> getObjects().named(MachineArchitecture.class, name)));
}

private void registerTargetSpecificTasks(Target target, String applicationJarTask, Configuration runtimeClasspath) {
private void registerTargetSpecificTasks(
Target target, boolean singleDefaultTarget, String applicationJarTask, Configuration runtimeClasspath) {
TaskContainer tasks = project.getTasks();

JavaPluginExtension java = project.getExtensions().getByType(JavaPluginExtension.class);
JavaApplication application = project.getExtensions().getByType(JavaApplication.class);

TaskProvider<Jpackage> jpackage = tasks.register(JPACKAGE + capitalize(target.getName()), Jpackage.class, t -> {
t.setDescription("Runs 'jpackage' command to create an image or s package.");

t.getJavaInstallation()
.convention(getJavaToolchains()
.compilerFor(java.getToolchain())
Expand Down Expand Up @@ -318,12 +336,43 @@ private void registerTargetSpecificTasks(Target target, String applicationJarTas
t.setJvmArgs(application.getApplicationDefaultJvmArgs());
t.classpath(tasks.named("jar"), runtimeClasspath);
});
maybeAddJpackageLifecycleTask(tasks, target, jpackage);

registerFatModuleJarTask(target, singleDefaultTarget, applicationJarTask, runtimeClasspath);

maybeAddJpackageLifecycleTask(target, jpackage);
}

private void registerFatModuleJarTask(
Target target, boolean singleDefaultTarget, String applicationJarTask, Configuration runtimeClasspath) {
TaskContainer tasks = project.getTasks();
JavaApplication application = project.getExtensions().getByType(JavaApplication.class);
ConfigurationContainer configurations = project.getConfigurations();

String taskName = FAT_MODULE_JAR + (singleDefaultTarget ? "" : capitalize(target.getName()));
tasks.register(taskName, FatModuleJar.class, t -> {
String classifier = singleDefaultTarget ? "all" : "all-" + target.getName();

t.setDescription("Assembles a fat jar archive containing the complete module path and a launcher.");

t.getMainModule().convention(application.getMainModule());
t.getMainClass().convention(application.getMainClass());

t.getLauncherPath().from(configurations.named("fatModuleJarLauncherPath"));
t.getLauncherMainClass().convention("build.jenesis.launcher.Launcher");

t.getModulePath().from(tasks.named(applicationJarTask));
t.getModulePath().from(runtimeClasspath);

t.setZip64(true);

t.getArchiveClassifier().set(classifier);
});
}

private void maybeAddJpackageLifecycleTask(
TaskContainer tasks, Target target, TaskProvider<Jpackage> targetJpackage) {
// if a task already exists, do nothing to avoid conflciting with other plugins
private void maybeAddJpackageLifecycleTask(Target target, TaskProvider<Jpackage> targetJpackage) {
TaskContainer tasks = project.getTasks();

// if a task already exists, do nothing to avoid conflict with other plugins
TaskProvider<Task> jpackage;
if (tasks.getNames().contains(JPACKAGE)) {
jpackage = tasks.named(JPACKAGE);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,26 @@
// SPDX-License-Identifier: Apache-2.0
package org.gradlex.javamodule.packaging;

import javax.inject.Inject;
import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.artifacts.ConfigurationContainer;
import org.gradle.api.artifacts.dsl.DependencyHandler;
import org.gradle.api.attributes.Usage;
import org.gradle.api.file.SourceDirectorySet;
import org.gradle.api.model.ObjectFactory;
import org.gradle.api.plugins.JavaPlugin;
import org.gradle.api.provider.Provider;
import org.gradle.api.tasks.SourceSetContainer;
import org.gradle.jvm.toolchain.JavaToolchainService;
import org.gradle.util.GradleVersion;
import org.gradlex.javamodule.packaging.internal.HostIdentification;
import org.gradlex.javamodule.packaging.model.Target;
import org.jspecify.annotations.NullMarked;

@NullMarked
public abstract class JavaModulePackagingPlugin implements Plugin<Project> {

@Inject
protected abstract JavaToolchainService getJavaToolchains();
private static final String DEFAULT_FATJAR_LAUNCHER = "build.jenesis:build.jenesis.launcher:0.3.0";

@Override
public void apply(Project project) {
Expand All @@ -36,6 +40,34 @@ public void apply(Project project) {
.getProjectDirectory()
.dir(mainResources.getSrcDirs().iterator().next().getParent() + "/resourcesPackage")));
javaModulePackaging.getVerbose().convention(false);
javaModulePackaging.primaryTarget(HostIdentification.hostTarget(project.getObjects()));

Target hostTarget = HostIdentification.hostTarget(project.getObjects());
javaModulePackaging.primaryTarget(hostTarget);
project.afterEvaluate(__ -> javaModulePackaging.maybeAddSingleDefaultTarget(hostTarget));

registerFatModuleJarLauncherScope(project);
}

private void registerFatModuleJarLauncherScope(Project project) {
ObjectFactory objects = project.getObjects();
ConfigurationContainer configurations = project.getConfigurations();
DependencyHandler dependencies = project.getDependencies();

Provider<Configuration> fatModuleJarLauncher = configurations.register("fatModuleJarLauncher", c -> {
c.setCanBeConsumed(false);
c.setCanBeResolved(false);
c.withDependencies(deps -> {
if (deps.isEmpty()) {
deps.add(dependencies.create(DEFAULT_FATJAR_LAUNCHER));
}
});
});

configurations.register("fatModuleJarLauncherPath", c -> {
c.setCanBeConsumed(false);
c.setCanBeResolved(true);
c.extendsFrom(fatModuleJarLauncher.get());
c.getAttributes().attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage.class, Usage.JAVA_RUNTIME));
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public static void validateHostSystem(String arch, String os) {
}

public static Target hostTarget(ObjectFactory objects) {
Target host = objects.newInstance(Target.class, "host");
Target host = objects.newInstance(Target.class, hostOs());
host.getOperatingSystem().convention(hostOs());
host.getArchitecture().convention(normalizeArch(System.getProperty("os.arch")));
return host;
Expand Down
Loading
Loading