Skip to content

Commit 7db1818

Browse files
committed
Add generateCdsArchive configuration property
1 parent a22e928 commit 7db1818

9 files changed

+312
-158
lines changed

README.md

+6-5
Original file line numberDiff line numberDiff line change
@@ -54,16 +54,17 @@ jlinkApplication {
5454
## Optional configuration
5555

5656
```groovy
57-
58-
application {
59-
applicationDefaultJvmArgs = ['-Xmx8G', '-Xms8G']
60-
}
61-
6257
jlinkApplication {
58+
applicationDefaultJvmArgs = ['-Xmx8G', '-Xms8G']
6359
compress = 'zip-9'
6460
noHeaderFiles = true
6561
noManPages = true
6662
stripDebug = true
63+
generateCdsArchive = true
64+
dedupLegalNoticesErrorIfNotSameContent = true
65+
disablePlugins = [
66+
"add-options" // Neutralizes applicationDefaultJvmArgs
67+
]
6768
}
6869
6970
```

src/main/java/com/github/iherasymenko/jlink/JlinkApplicationPlugin.java

+1
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ public void apply(Project project) {
7676
task.getStripDebug().convention(jlinkApplication.getStripDebug());
7777
task.getVerbose().convention(jlinkApplication.getVerbose());
7878
task.getDedupLegalNoticesErrorIfNotSameContent().convention(jlinkApplication.getDedupLegalNoticesErrorIfNotSameContent());
79+
task.getGenerateCdsArchive().convention(jlinkApplication.getGenerateCdsArchive());
7980
};
8081

8182
TaskProvider<JlinkImageTask> imageTask = tasks.register("image", JlinkImageTask.class, task -> {

src/main/java/com/github/iherasymenko/jlink/JlinkApplicationPluginExtension.java

+2
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,6 @@ public abstract class JlinkApplicationPluginExtension {
4444

4545
public abstract Property<Boolean> getDedupLegalNoticesErrorIfNotSameContent();
4646

47+
public abstract Property<Boolean> getGenerateCdsArchive();
48+
4749
}

src/main/java/com/github/iherasymenko/jlink/JlinkImageTask.java

+7
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,10 @@ public JlinkImageTask() {
112112
@Optional
113113
public abstract Property<Boolean> getDedupLegalNoticesErrorIfNotSameContent();
114114

115+
@Input
116+
@Optional
117+
public abstract Property<Boolean> getGenerateCdsArchive();
118+
115119
@Inject
116120
protected abstract FileSystemOperations getFileSystemOperations();
117121

@@ -157,6 +161,9 @@ public void execute() throws IOException {
157161
if (getDedupLegalNoticesErrorIfNotSameContent().getOrElse(false)) {
158162
args.addAll(List.of("--dedup-legal-notices", "error-if-not-same-content"));
159163
}
164+
if (getGenerateCdsArchive().getOrElse(false)) {
165+
args.add("--generate-cds-archive");
166+
}
160167
String jvmArgsLine = String.join(" ", getJvmArgs().get());
161168
if (!jvmArgsLine.isEmpty()) {
162169
args.add("--add-options=" + jvmArgsLine);

src/test/java/com/github/iherasymenko/jlink/test/CrossTargetJdkImageFunctionalTest.java

+16-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,18 @@
1+
/*
2+
* Copyright 2023 Ihor Herasymenko.
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+
*/
116
package com.github.iherasymenko.jlink.test;
217

318
import org.gradle.testkit.runner.BuildResult;
@@ -9,7 +24,7 @@
924

1025
import static org.assertj.core.api.Assertions.assertThat;
1126

12-
public class CrossTargetJdkImageFunctionalTest extends AbstractTestBase {
27+
class CrossTargetJdkImageFunctionalTest extends AbstractTestBase {
1328

1429
@Test
1530
public void can_create_image_with_a_cross_target_jdk() throws IOException {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
/*
2+
* Copyright 2023 Ihor Herasymenko.
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+
package com.github.iherasymenko.jlink.test;
17+
18+
import org.assertj.core.api.InstanceOfAssertFactories;
19+
import org.gradle.testkit.runner.BuildResult;
20+
import org.junit.jupiter.api.BeforeEach;
21+
import org.junit.jupiter.api.Test;
22+
23+
import java.io.IOException;
24+
import java.nio.file.Files;
25+
import java.nio.file.Path;
26+
import java.util.spi.ToolProvider;
27+
28+
import static org.assertj.core.api.Assertions.assertThat;
29+
30+
class DedupLegalNoticesPluginFunctionalTest extends AbstractTestBase {
31+
32+
@BeforeEach
33+
void setUp() throws IOException {
34+
ToolProvider javac = ToolProvider.findFirst("javac").orElseThrow(() -> new IllegalStateException("javac not found"));
35+
ToolProvider jmod = ToolProvider.findFirst("jmod").orElseThrow(() -> new IllegalStateException("jmod not found"));
36+
37+
Path src = Files.createDirectories(build.projectDir.resolve("foo-module/src"));
38+
Path classes = Files.createDirectories(build.projectDir.resolve("foo-module/classes"));
39+
Path legal = Files.createDirectories(build.projectDir.resolve("foo-module/legal"));
40+
Path libs = Files.createDirectories(build.projectDir.resolve("libs"));
41+
42+
Path moduleInfo = src.resolve("module-info.java");
43+
Files.writeString(moduleInfo, "module foo {}");
44+
Files.writeString(legal.resolve("LICENSE"), "DUMMY LICENSE TEXT");
45+
46+
Path fooJmod = libs.resolve("foo.jmod");
47+
48+
javac.run(System.out, System.err, "-d", classes.toString(), "--release", "11", moduleInfo.toString());
49+
jmod.run(System.out, System.err, "create", "--class-path", classes.toString(), "--legal-notices", legal.toString(), fooJmod.toString());
50+
51+
build.settingsFile = """
52+
rootProject.name = 'demo'
53+
dependencyResolutionManagement {
54+
repositories {
55+
mavenCentral()
56+
}
57+
}
58+
""";
59+
build.mainClass = """
60+
package com.example.demo;
61+
62+
public class DemoApplication {
63+
public static void main(String[] args) {
64+
65+
}
66+
}
67+
""";
68+
build.moduleInfo = """
69+
module demo.main {
70+
requires foo;
71+
}
72+
""";
73+
}
74+
75+
@Test
76+
void dedup_legal_notices_error_if_not_same_content() throws IOException {
77+
build.buildFile = """
78+
plugins {
79+
id 'application'
80+
id 'com.github.iherasymenko.jlink'
81+
}
82+
83+
group = 'com.example'
84+
version = '0.0.1-SNAPSHOT'
85+
86+
java {
87+
toolchain {
88+
languageVersion = JavaLanguageVersion.of(System.getenv().getOrDefault('TESTING_AGAINST_JDK', '21'))
89+
vendor = JvmVendorSpec.AZUL
90+
}
91+
}
92+
93+
application {
94+
mainClass = 'com.example.demo.DemoApplication'
95+
mainModule = 'demo.main'
96+
}
97+
98+
dependencies {
99+
implementation files("libs/foo.jmod")
100+
}
101+
102+
jlinkApplication {
103+
dedupLegalNoticesErrorIfNotSameContent = true
104+
}
105+
106+
// Gradle does not recognize *.jmod files as modules ¯\\_(ツ)_/¯
107+
tasks.withType(JavaCompile).configureEach { task ->
108+
task.doFirst {
109+
task.options.compilerArgs += ["--module-path", task.classpath.asPath]
110+
classpath = files()
111+
}
112+
}
113+
""";
114+
BuildResult buildResult = build.runner("image").buildAndFail();
115+
assertThat(buildResult)
116+
.extracting(BuildResult::getOutput, InstanceOfAssertFactories.STRING)
117+
.contains("Error: /java.base/legal/java.base/LICENSE /foo/legal/foo/LICENSE contain different content");
118+
assertThat(build.projectDir.resolve("build/images/demo/bin")).doesNotExist();
119+
assertThat(build.projectDir.resolve("build/images/demo/release")).doesNotExist();
120+
}
121+
122+
@Test
123+
void dedup_legal_notices_error_if_not_same_content_is_disabled_by_default() throws IOException {
124+
build.buildFile = """
125+
plugins {
126+
id 'application'
127+
id 'com.github.iherasymenko.jlink'
128+
}
129+
130+
group = 'com.example'
131+
version = '0.0.1-SNAPSHOT'
132+
133+
java {
134+
toolchain {
135+
languageVersion = JavaLanguageVersion.of(System.getenv().getOrDefault('TESTING_AGAINST_JDK', '21'))
136+
vendor = JvmVendorSpec.AZUL
137+
}
138+
}
139+
140+
application {
141+
mainClass = 'com.example.demo.DemoApplication'
142+
mainModule = 'demo.main'
143+
}
144+
145+
dependencies {
146+
implementation files("libs/foo.jmod")
147+
}
148+
149+
// Gradle does not recognize *.jmod files as modules ¯\\_(ツ)_/¯
150+
tasks.withType(JavaCompile).configureEach { task ->
151+
task.doFirst {
152+
task.options.compilerArgs += ["--module-path", task.classpath.asPath]
153+
classpath = files()
154+
}
155+
}
156+
""";
157+
build.runner("image").build();
158+
assertThat(build.projectDir.resolve("build/images/demo/legal/foo/LICENSE"))
159+
.content()
160+
.isEqualTo("DUMMY LICENSE TEXT");
161+
assertThat(build.projectDir.resolve("build/images/demo/legal/java.base/LICENSE"))
162+
.content()
163+
.startsWith("The GNU General Public License (GPL)");
164+
}
165+
166+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/*
2+
* Copyright 2023 Ihor Herasymenko.
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+
package com.github.iherasymenko.jlink.test;
17+
18+
import org.assertj.core.api.InstanceOfAssertFactories;
19+
import org.gradle.testkit.runner.BuildResult;
20+
import org.junit.jupiter.api.Test;
21+
22+
import java.io.IOException;
23+
24+
import static org.assertj.core.api.Assertions.assertThat;
25+
26+
class GenerateCdsArchivePluginFunctionalTest extends AbstractTestBase {
27+
28+
@Test
29+
void can_generate_cds_archive() throws IOException {
30+
build.buildFile = """
31+
plugins {
32+
id 'java'
33+
id 'com.github.iherasymenko.jlink'
34+
}
35+
36+
group = 'com.example'
37+
version = '0.0.1-SNAPSHOT'
38+
39+
java {
40+
toolchain {
41+
languageVersion = JavaLanguageVersion.of(System.getenv().getOrDefault('TESTING_AGAINST_JDK', '21'))
42+
vendor = JvmVendorSpec.AZUL
43+
}
44+
}
45+
46+
jlinkApplication {
47+
mainClass = 'com.example.demo.DemoApplication'
48+
mainModule = 'demo.main'
49+
generateCdsArchive = true
50+
}
51+
""";
52+
build.settingsFile = """
53+
rootProject.name = 'demo'
54+
dependencyResolutionManagement {
55+
repositories {
56+
mavenCentral()
57+
}
58+
}
59+
""";
60+
build.mainClass = """
61+
package com.example.demo;
62+
63+
public class DemoApplication {
64+
public static void main(String[] args) {
65+
66+
}
67+
}
68+
""";
69+
build.moduleInfo = """
70+
module demo.main {
71+
requires java.sql;
72+
}
73+
""";
74+
75+
BuildResult buildResult = build.runner("image").build();
76+
77+
assertThat(build.projectDir.resolve("build/images/demo/lib/server/classes.jsa")).exists();
78+
assertThat(build.projectDir.resolve("build/images/demo/lib/server/classes_nocoops.jsa")).exists();
79+
assertThat(buildResult)
80+
.extracting(BuildResult::getOutput, InstanceOfAssertFactories.STRING)
81+
.contains("Created CDS archive successfully");
82+
}
83+
84+
}

src/test/java/com/github/iherasymenko/jlink/test/ImageFunctionalTest.java

-56
Original file line numberDiff line numberDiff line change
@@ -374,60 +374,4 @@ public static void main(String[] args) {
374374
);
375375
}
376376

377-
@Test
378-
void can_disable_a_jlink_plugin() throws IOException {
379-
build.buildFile = """
380-
plugins {
381-
id 'java'
382-
id 'com.github.iherasymenko.jlink'
383-
}
384-
385-
group = 'com.example'
386-
version = '0.0.1-SNAPSHOT'
387-
388-
java {
389-
toolchain {
390-
languageVersion = JavaLanguageVersion.of(System.getenv().getOrDefault('TESTING_AGAINST_JDK', '21'))
391-
vendor = JvmVendorSpec.AZUL
392-
}
393-
}
394-
395-
jlinkApplication {
396-
mainClass = 'com.example.demo.DemoApplication'
397-
mainModule = 'demo.main'
398-
applicationDefaultJvmArgs = ["-Dtest_arg1=hello", "-Dtest_arg2=world"]
399-
disablePlugins = ["add-options"]
400-
}
401-
""";
402-
build.settingsFile = """
403-
rootProject.name = 'demo'
404-
dependencyResolutionManagement {
405-
repositories {
406-
mavenCentral()
407-
}
408-
}
409-
""";
410-
build.mainClass = """
411-
package com.example.demo;
412-
413-
public class DemoApplication {
414-
public static void main(String[] args) {
415-
System.out.println("Arg1: " + System.getProperty("test_arg1"));
416-
System.out.println("Arg2: " + System.getProperty("test_arg2"));
417-
}
418-
}
419-
""";
420-
build.moduleInfo = """
421-
module demo.main {
422-
423-
}
424-
""";
425-
BuildResult buildResult = build.runner("imageRun")
426-
.build();
427-
String[] taskOutput = Text.linesBetweenTags(buildResult.getOutput(), "> Task :imageRun", "BUILD SUCCESSFUL");
428-
assertThat(taskOutput).hasSize(2);
429-
assertThat(taskOutput[0]).isEqualTo("Arg1: null");
430-
assertThat(taskOutput[1]).isEqualTo("Arg2: null");
431-
}
432-
433377
}

0 commit comments

Comments
 (0)