Skip to content

Commit 9e15d17

Browse files
authored
Merge pull request #62 from gradlex-org/preview
Implement new features for next release
2 parents baf31c0 + 20f3000 commit 9e15d17

15 files changed

+716
-146
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
# Java Module Packaging Gradle Plugin - Changelog
22

3+
## Version 1.1
4+
- Configuration option for `--jlink-options`
5+
- Configuration option for `--verbose`
6+
- Configuration option to build packages in one step (interesting for MacOS signing)
7+
- More options to add custom resources to a package
8+
- Option to explicitly build the 'app-image' folder only (or in addition)
9+
- By convention, set default target to current operating system and architecture
10+
311
## Version 1.0.1
412
* Do not bind platform-specific assemble tasks to a lifecycle
513

README.md

Lines changed: 38 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ Add this to the build file of your convention plugin's build
5050

5151
```
5252
dependencies {
53-
implementation("org.gradlex:java-module-packaging:1.0.1")
53+
implementation("org.gradlex:java-module-packaging:1.1")
5454
}
5555
```
5656

@@ -80,32 +80,56 @@ javaModulePackaging {
8080
operatingSystem = OperatingSystemFamily.WINDOWS
8181
architecture = MachineArchitecture.X86_64
8282
}
83-
84-
primaryTarget(target("macos-14"))
8583
}
8684
```
8785

8886
You can now run _target-specific_ builds:
8987

90-
```
91-
./gradlew assembleWindows
88+
```shell
89+
./gradlew jpackageWindows
9290
```
9391

94-
```
92+
```shell
9593
./gradlew runWindows
9694
```
9795

98-
There are some additional configuration options that can be used:
96+
Or, for convenience, let the plugin pick the target fitting the machine you run on:
9997

98+
```shell
99+
./gradlew jpackage
100100
```
101+
102+
```shell
103+
./gradlew run
104+
```
105+
106+
There are some additional configuration options that can be used if needed.
107+
All options have a default. Only configure what you need in addition.
108+
109+
```kotlin
101110
javaModulePackaging {
102-
applicationName();
103-
applicationVersion();
104-
applicationDescription = ""
105-
vendor = "My Company"
106-
copyright = "(c) My Company"
107-
jpackageResources.setFrom(...);
108-
resources.from(...)
111+
// global options
112+
applicationName = "app" // defaults to project name
113+
applicationVersion = "1.0" // defaults to project version
114+
applicationDescription = "Awesome App"
115+
vendor = "My Company"
116+
copyright = "(c) My Company"
117+
jlinkOptions.addAll("--no-header-files", "--no-man-pages", "--bind-services")
118+
addModules.addAll("additional.module.to.include")
119+
jpackageResources = layout.projectDirectory.dir("res") // defaults to 'src/main/resourcesPackage'
120+
resources.from(layout.projectDirectory.dir("extra-res"))
121+
verbose = false
122+
123+
// target specific options
124+
targetsWithOs("windows") {
125+
options.addAll("--win-dir-chooser", "--win-shortcut", "--win-menu")
126+
appImageOptions.addAll("--win-console")
127+
targetResources.from("windows-res")
128+
}
129+
targetsWithOs("macos") {
130+
options.addAll("--mac-sign", "--mac-signing-key-user-name", "gradlex")
131+
singleStepPackaging = true
132+
}
109133
}
110134
```
111135

build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ plugins {
33
}
44

55
group = "org.gradlex"
6-
version = "1.0.1"
6+
version = "1.1"
77

88
java {
99
toolchain.languageVersion = JavaLanguageVersion.of(17)

src/main/java/org/gradlex/javamodule/packaging/JavaModulePackagingExtension.java

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import org.gradle.api.Action;
2020
import org.gradle.api.NamedDomainObjectContainer;
21+
import org.gradle.api.NamedDomainObjectSet;
2122
import org.gradle.api.Project;
2223
import org.gradle.api.Task;
2324
import org.gradle.api.artifacts.Configuration;
@@ -35,6 +36,7 @@
3536
import org.gradle.api.plugins.JavaApplication;
3637
import org.gradle.api.plugins.JavaPluginExtension;
3738
import org.gradle.api.plugins.jvm.JvmTestSuite;
39+
import org.gradle.api.provider.ListProperty;
3840
import org.gradle.api.provider.Property;
3941
import org.gradle.api.tasks.JavaExec;
4042
import org.gradle.api.tasks.SourceSet;
@@ -45,6 +47,7 @@
4547
import org.gradle.nativeplatform.MachineArchitecture;
4648
import org.gradle.nativeplatform.OperatingSystemFamily;
4749
import org.gradle.testing.base.TestSuite;
50+
import org.gradlex.javamodule.packaging.internal.HostIdentification;
4851
import org.gradlex.javamodule.packaging.model.Target;
4952
import org.gradlex.javamodule.packaging.tasks.Jpackage;
5053
import org.gradlex.javamodule.packaging.tasks.ValidateHostSystemAction;
@@ -63,14 +66,18 @@
6366
abstract public class JavaModulePackagingExtension {
6467
private static final Attribute<Boolean> JAVA_MODULE_ATTRIBUTE = Attribute.of("javaModule", Boolean.class);
6568
private static final String INTERNAL = "internal";
69+
private static final String JPACKAGE = "jpackage";
6670

6771
abstract public Property<String> getApplicationName();
6872
abstract public Property<String> getApplicationVersion();
6973
abstract public Property<String> getApplicationDescription();
7074
abstract public Property<String> getVendor();
7175
abstract public Property<String> getCopyright();
76+
abstract public ListProperty<String> getJlinkOptions();
77+
abstract public ListProperty<String> getAddModules();
7278
abstract public DirectoryProperty getJpackageResources();
7379
abstract public ConfigurableFileCollection getResources();
80+
abstract public Property<Boolean> getVerbose();
7481

7582
private final NamedDomainObjectContainer<Target> targets = getObjects().domainObjectContainer(Target.class);
7683

@@ -109,6 +116,16 @@ public Target target(String label, Action<? super Target> action) {
109116
return target;
110117
}
111118

119+
/**
120+
* Configure all targets for the given OS.
121+
*/
122+
@SuppressWarnings("unused")
123+
public void targetsWithOs(String operatingSystem, Action<? super Target> action) {
124+
NamedDomainObjectSet<Target> matches = targets.matching(t ->
125+
t.getOperatingSystem().isPresent() && t.getOperatingSystem().get().equals(operatingSystem));
126+
matches.all(action);
127+
}
128+
112129
/**
113130
* Set a 'primary target'. Standard Gradle tasks that are not bound to a specific target – like 'assemble' – use
114131
* this 'primary target'.
@@ -132,6 +149,7 @@ public Target primaryTarget(Target target) {
132149
/**
133150
* Set a test suite to be 'multi-target'. This registers an additional 'test' task for each target.
134151
*/
152+
@SuppressWarnings({"unused", "UnstableApiUsage"})
135153
public TestSuite multiTargetTestSuite(TestSuite testSuite) {
136154
if (!(testSuite instanceof JvmTestSuite)) {
137155
return testSuite;
@@ -220,7 +238,7 @@ private void registerTargetSpecificTasks(Target target, String applicationJarTas
220238
JavaPluginExtension java = getProject().getExtensions().getByType(JavaPluginExtension.class);
221239
JavaApplication application = getProject().getExtensions().getByType(JavaApplication.class);
222240

223-
TaskProvider<Jpackage> jpackage = tasks.register("jpackage" + capitalize(target.getName()), Jpackage.class, t -> {
241+
TaskProvider<Jpackage> jpackage = tasks.register(JPACKAGE + capitalize(target.getName()), Jpackage.class, t -> {
224242
t.getJavaInstallation().convention(getJavaToolchains().compilerFor(java.getToolchain()).get().getMetadata());
225243
t.getOperatingSystem().convention(target.getOperatingSystem());
226244
t.getArchitecture().convention(target.getArchitecture());
@@ -230,14 +248,20 @@ private void registerTargetSpecificTasks(Target target, String applicationJarTas
230248
t.getModulePath().from(runtimeClasspath);
231249

232250
t.getApplicationName().convention(getApplicationName());
233-
t.getJpackageResources().convention(getJpackageResources().dir(target.getOperatingSystem()));
251+
t.getJpackageResources().from(getJpackageResources().dir(target.getOperatingSystem()));
234252
t.getApplicationDescription().convention(getApplicationDescription());
235253
t.getVendor().convention(getVendor());
236254
t.getCopyright().convention(getCopyright());
237255
t.getJavaOptions().convention(application.getApplicationDefaultJvmArgs());
256+
t.getJlinkOptions().convention(getJlinkOptions());
257+
t.getAddModules().convention(getAddModules());
238258
t.getOptions().convention(target.getOptions());
259+
t.getAppImageOptions().convention(target.getAppImageOptions());
239260
t.getPackageTypes().convention(target.getPackageTypes());
261+
t.getSingleStepPackaging().convention(target.getSingleStepPackaging());
240262
t.getResources().from(getResources());
263+
t.getTargetResources().from(target.getTargetResources());
264+
t.getVerbose().convention(getVerbose());
241265

242266
t.getDestination().convention(getProject().getLayout().getBuildDirectory().dir("packages/" + target.getName()));
243267
t.getTempDirectory().convention(getProject().getLayout().getBuildDirectory().dir("tmp/jpackage/" + target.getName()));
@@ -252,15 +276,25 @@ private void registerTargetSpecificTasks(Target target, String applicationJarTas
252276
t.setJvmArgs(application.getApplicationDefaultJvmArgs());
253277
t.classpath(tasks.named("jar"), runtimeClasspath);
254278
});
279+
maybeAddJpackageLifecycleTask(tasks, target, jpackage);
280+
}
255281

256-
String targetAssembleLifecycle = "assemble" + capitalize(target.getName());
257-
if (!tasks.getNames().contains(targetAssembleLifecycle)) {
258-
TaskProvider<Task> lifecycleTask = tasks.register(targetAssembleLifecycle, t -> {
282+
private void maybeAddJpackageLifecycleTask(TaskContainer tasks, Target target, TaskProvider<Jpackage> targetJpackage) {
283+
// if a task already exists, do nothing to avoid conflciting with other plugins
284+
TaskProvider<Task> jpackage;
285+
if (tasks.getNames().contains(JPACKAGE)) {
286+
jpackage = tasks.named(JPACKAGE);
287+
} else {
288+
jpackage = tasks.register(JPACKAGE, t -> {
259289
t.setGroup(BUILD_GROUP);
260-
t.setDescription("Builds this project for " + target.getName());
290+
t.setDescription("Build the package for the current host system");
261291
});
262292
}
263-
tasks.named(targetAssembleLifecycle, t -> t.dependsOn(jpackage));
293+
jpackage.configure(t -> {
294+
if (HostIdentification.isHostTarget(target)) {
295+
t.dependsOn(targetJpackage);
296+
}
297+
});
264298
}
265299

266300
private Configuration maybeCreateInternalConfiguration() {

src/main/java/org/gradlex/javamodule/packaging/JavaModulePackagingPlugin.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import org.gradle.api.tasks.SourceSetContainer;
2424
import org.gradle.jvm.toolchain.JavaToolchainService;
2525
import org.gradle.util.GradleVersion;
26+
import org.gradlex.javamodule.packaging.internal.HostIdentification;
2627

2728
import javax.inject.Inject;
2829

@@ -47,5 +48,7 @@ public void apply(Project project) {
4748
javaModulePackaging.getApplicationVersion().convention(project.provider(() -> (String) project.getVersion()));
4849
javaModulePackaging.getJpackageResources().convention(project.provider(() ->
4950
project.getLayout().getProjectDirectory().dir(mainResources.getSrcDirs().iterator().next().getParent() + "/resourcesPackage")));
51+
javaModulePackaging.getVerbose().convention(false);
52+
javaModulePackaging.primaryTarget(HostIdentification.hostTarget(project.getObjects()));
5053
}
5154
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/*
2+
* Copyright the GradleX team.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.gradlex.javamodule.packaging.internal;
18+
19+
import org.gradle.api.model.ObjectFactory;
20+
import org.gradle.nativeplatform.MachineArchitecture;
21+
import org.gradle.nativeplatform.OperatingSystemFamily;
22+
import org.gradlex.javamodule.packaging.model.Target;
23+
24+
import java.util.Locale;
25+
26+
public class HostIdentification {
27+
28+
public static void validateHostSystem(String arch, String os) {
29+
String hostOs = hostOs();
30+
String hostArch = hostArch();
31+
32+
if (!normalizeOs(hostOs).equals(normalizeOs(os))) {
33+
wrongHostSystemError(hostOs, os);
34+
}
35+
if (!normalizeArch(hostArch).equals(normalizeArch(arch))) {
36+
wrongHostSystemError(hostArch, arch);
37+
}
38+
}
39+
40+
public static Target hostTarget(ObjectFactory objects) {
41+
Target host = objects.newInstance(Target.class, "host");
42+
host.getOperatingSystem().convention(hostOs());
43+
host.getArchitecture().convention(normalizeArch(System.getProperty("os.arch")));
44+
return host;
45+
}
46+
47+
public static boolean isHostTarget(Target target) {
48+
return target.getOperatingSystem().isPresent()
49+
&& target.getArchitecture().isPresent()
50+
&& target.getOperatingSystem().get().equals(hostOs())
51+
&& target.getArchitecture().get().equals(normalizeArch(hostArch()));
52+
}
53+
54+
private static String hostOs() {
55+
return normalizeOs(System.getProperty("os.name"));
56+
}
57+
58+
private static String hostArch() {
59+
return System.getProperty("os.arch");
60+
}
61+
62+
private static String normalizeOs(String name) {
63+
String os = name.toLowerCase(Locale.ROOT).replace(" ", "");
64+
if (os.contains("windows")) {
65+
return OperatingSystemFamily.WINDOWS;
66+
}
67+
if (os.contains("macos") || os.contains("darwin") || os.contains("osx")) {
68+
return OperatingSystemFamily.MACOS;
69+
}
70+
return OperatingSystemFamily.LINUX;
71+
}
72+
73+
private static String normalizeArch(String name) {
74+
String arch = name.toLowerCase(Locale.ROOT);
75+
if (arch.contains("aarch")) {
76+
return MachineArchitecture.ARM64;
77+
}
78+
if (arch.contains("64")) {
79+
return MachineArchitecture.X86_64;
80+
}
81+
return MachineArchitecture.X86;
82+
}
83+
84+
private static void wrongHostSystemError(String hostOs, String os) {
85+
throw new RuntimeException("Running on " + hostOs + "; cannot build for " + os);
86+
}
87+
}

src/main/java/org/gradlex/javamodule/packaging/internal/Validator.java

Lines changed: 0 additions & 52 deletions
This file was deleted.

0 commit comments

Comments
 (0)