File tree

15 files changed

+81
-24
lines changed

15 files changed

+81
-24
lines changed
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,12 @@
4242

4343
import java.io.File;
4444
import java.io.IOException;
45+
import java.nio.charset.StandardCharsets;
4546
import java.nio.file.Files;
4647
import java.nio.file.Path;
48+
import java.nio.file.StandardOpenOption;
49+
import java.util.Collections;
50+
import java.util.List;
4751

4852
import static org.graalvm.buildtools.utils.SharedConstants.GRAALVM_EXE_EXTENSION;
4953

@@ -66,4 +70,16 @@ public static void maybeCreateConfigureUtilSymlink(File configureUtilFile, Path
6670
public static String nativeImageConfigureFileName() {
6771
return "native-image-configure" + GRAALVM_EXE_EXTENSION;
6872
}
73+
74+
public static List<String> convertToArgsFile(List<String> cliArgs) {
75+
try {
76+
File tmpFile = File.createTempFile("native-image", "args");
77+
tmpFile.deleteOnExit();
78+
Files.write(tmpFile.toPath(), cliArgs, StandardCharsets.UTF_8, StandardOpenOption.CREATE);
79+
return Collections.singletonList("@" + tmpFile.getAbsolutePath());
80+
} catch (IOException e) {
81+
82+
return Collections.unmodifiableList(cliArgs);
83+
}
84+
}
6985
}
Original file line numberDiff line numberDiff line change
@@ -174,27 +174,25 @@ include::../snippets/gradle/kotlin/build.gradle.kts[tags=all-config-options]
174174
NOTE: For options that can be set using command-line, if both DSL and command-line options are present, command-line options take precedence.
175175

176176
[[long_classpath_and_fat_jar_support]]
177-
==== Long classpath and fat jar support
177+
==== Long classpath, @argument file and fat jar support
178178

179-
Under Windows, https://.com/graalvm/native-build-tools/issues/85[it is possible that the length of the classpath exceeds what the operating system supports] when invoking the CLI to build a native image.
180-
As a consequence, if you are running under Windows, the plugin will automatically shorten the classpath of your project by building a so called "fat jar", which includes all entries from the classpath automatically.
179+
Since release 0.9.10, the plugin will automatically pass arguments to the `native-image` tool using an argument file, which should prevent all https://.com/graalvm/native-build-tools/issues/85[long classpath issues] under Windows.
180+
However, if you are using an older GraalVM release (older than 21.3) which doesn't support argument files, you will need to rely on creating a "fat jar", which includes all entries from the classpath automatically, to workaround the problem:
181181

182-
In case this behavior is not required, you can disable the fat jar creation by calling:
183-
184-
.Disabling the fat jar creation
182+
.Enabling the fat jar creation
185183
[source, groovy, role="multi-language-sample"]
186184
----
187-
include::../snippets/gradle/groovy/build.gradle[tags=disable-fatjar]
185+
include::../snippets/gradle/groovy/build.gradle[tags=enable-fatjar]
188186
----
189187

190188
[source,kotlin,role="multi-language-sample"]
191189
----
192-
include::../snippets/gradle/kotlin/build.gradle.kts[tags=disable-fatjar]
190+
include::../snippets/gradle/kotlin/build.gradle.kts[tags=enable-fatjar]
193191
----
194192

195193
Alternatively, it is possible to use your own fat jar (for example created using the https://imperceptiblethoughts.com/shadow/[Shadow plugin]) by setting the `classpathJar` property directly on the _task_:
196194

197-
.Disabling the fat jar creation
195+
.Enabling a custom fat jar creation
198196
[source, groovy, role="multi-language-sample"]
199197
----
200198
include::../snippets/gradle/groovy/build.gradle[tags=custom-fatjar]
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,13 @@ If you are interested in contributing, please refer to our https://.com/gr
2424
* Fixed race condition which prevented the agent files to be generated properly if tests were executed concurrently
2525
* Documented version compatibility for the JUnit Platform and Maven Surefire plugin.
2626
- See <<maven-plugin.adoc#testing-support-version-compatibility, Version compatibility>> for details.
27+
* Add support for long classpath by using an argument file when invoking `native-image`
2728

2829
==== Gradle plugin
2930

3031
* Fixed `nativeRun` not working properly under Windows
3132
* Fixed race condition which prevented the agent files to be generated properly if tests were executed concurrently
33+
* Add support for long classpath by using an argument file when invoking `native-image`
3234

3335
=== Release 0.9.9
3436

Original file line numberDiff line numberDiff line change
@@ -258,11 +258,15 @@ use cases such as the following:
258258
wish to run those same tests in native mode.
259259

260260
[[long_classpath_and_shading_support]]
261-
== Long classpath and shading support
261+
== Long classpath, @argument file and shading support
262262

263263
Under Windows, https://.com/graalvm/native-build-tools/issues/85[it is possible that the length of the classpath exceeds what the operating system supports] when invoking the CLI to build a native image.
264264

265-
If this happens, one option is to use a https://maven.apache.org/plugins/maven-shade-plugin[shaded jar] and use it instead of individual jars on classpath.
265+
To avoid this, since release 0.9.10, the plugin will use an argument file to pass the arguments to the `native-image` tool, instead of passing them directly.
266+
267+
In case you are using a GraalVM version older than 21.3, you will however have to use a workaround, since the argument file wasn't supported.
268+
269+
One option is to use a https://maven.apache.org/plugins/maven-shade-plugin[shaded jar] and use it instead of individual jars on classpath.
266270

267271
First, you'll need to setup the https://maven.apache.org/plugins/maven-shade-plugin[Maven Shade plugin]:
268272

Original file line numberDiff line numberDiff line change
@@ -98,15 +98,16 @@ graalvmNative {
9898
}
9999
// end::all-config-options[]
100100

101-
// tag::disable-fatjar[]
101+
// tag::enable-fatjar[]
102102
graalvmNative {
103+
useArgFile = false // required for older GraalVM releases
103104
binaries {
104105
main {
105-
useFatJar = false
106+
useFatJar = true
106107
}
107108
}
108109
}
109-
// end::disable-fatjar[]
110+
// end::enable-fatjar[]
110111

111112
def myFatJar = tasks.register("myFatJar", Jar)
112113

Original file line numberDiff line numberDiff line change
@@ -99,15 +99,16 @@ graalvmNative {
9999
}
100100
// end::all-config-options[]
101101

102-
// tag::disable-fatjar[]
102+
// tag::enable-fatjar[]
103103
graalvmNative {
104+
useFatJar.set(false) // required for older GraalVM releases
104105
binaries {
105106
named("main") {
106-
useFatJar.set(false)
107+
useFatJar.set(true)
107108
}
108109
}
109110
}
110-
// end::disable-fatjar[]
111+
// end::enable-fatjar[]
111112

112113
val myFatJar = tasks.register<Jar>("myFatJar")
113114

Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@ public void apply(Project project) {
171171

172172
logger = GraalVMLogger.of(project.getLogger());
173173
DefaultGraalVmExtension graalExtension = (DefaultGraalVmExtension) registerGraalVMExtension(project);
174+
graalExtension.getUseArgFile().convention(true);
174175
project.getPlugins()
175176
.withType(JavaPlugin.class, javaPlugin -> configureJavaProject(project, nativeImageServiceProvider, graalExtension));
176177
project.afterEvaluate(p -> {
@@ -253,6 +254,7 @@ private void configureAutomaticTaskCreation(Project project,
253254
builder.setGroup(LifecycleBasePlugin.BUILD_GROUP);
254255
builder.getOptions().convention(options);
255256
builder.getAgentEnabled().set(agent);
257+
builder.getUseArgFile().convention(graalExtension.getUseArgFile());
256258
});
257259
String runTaskName = deriveTaskName(binaryName, "native", "Run");
258260
if ("main".equals(binaryName)) {
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,14 @@ public interface GraalVMExtension {
9090
*/
9191
Property<Boolean> getToolchainDetection();
9292

93+
/**
94+
* Property driving the use of @-arg files when invoking native image.
95+
* This is enabled by default. For older native-image versions, this
96+
* needs to be disabled.
97+
* @return the argument file property
98+
*/
99+
Property<Boolean> getUseArgFile();
100+
93101

94102
interface TestBinaryConfig {
95103
/**
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@
4343

4444
import org.graalvm.buildtools.gradle.dsl.NativeImageOptions;
4545
import org.graalvm.buildtools.gradle.dsl.NativeResourcesOptions;
46-
import org.graalvm.buildtools.utils.SharedConstants;
4746
import org.gradle.api.Action;
4847
import org.gradle.api.file.ConfigurableFileCollection;
4948
import org.gradle.api.model.ObjectFactory;
@@ -215,7 +214,7 @@ public BaseNativeImageOptions(String name,
215214
getAgent().getEnabled().convention(false);
216215
getSharedLibrary().convention(false);
217216
getImageName().convention(defaultImageName);
218-
getUseFatJar().convention(SharedConstants.IS_WINDOWS);
217+
getUseFatJar().convention(false);
219218
}
220219

221220
private static Provider<Boolean> property(ProviderFactory providers, String name) {
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
package org.graalvm.buildtools.gradle.internal;
4343

4444
import org.graalvm.buildtools.gradle.dsl.NativeImageOptions;
45+
import org.graalvm.buildtools.utils.NativeImageUtils;
4546
import org.gradle.api.Transformer;
4647
import org.gradle.api.file.FileSystemLocation;
4748
import org.gradle.api.file.RegularFile;
@@ -65,17 +66,20 @@ public class NativeImageCommandLineProvider implements CommandLineArgumentProvid
6566
private final Provider<String> executableName;
6667
private final Provider<String> outputDirectory;
6768
private final Provider<RegularFile> classpathJar;
69+
private final Provider<Boolean> useArgFile;
6870

6971
public NativeImageCommandLineProvider(Provider<NativeImageOptions> options,
7072
Provider<Boolean> agentEnabled,
7173
Provider<String> executableName,
7274
Provider<String> outputDirectory,
73-
Provider<RegularFile> classpathJar) {
75+
Provider<RegularFile> classpathJar,
76+
Provider<Boolean> useArgFile) {
7477
this.options = options;
7578
this.agentEnabled = agentEnabled;
7679
this.executableName = executableName;
7780
this.outputDirectory = outputDirectory;
7881
this.classpathJar = classpathJar;
82+
this.useArgFile = useArgFile;
7983
}
8084

8185
@Nested
@@ -145,7 +149,11 @@ public List<String> asArguments() {
145149
cliArgs.add("-H:Class=" + options.getMainClass().get());
146150
}
147151
cliArgs.addAll(options.getBuildArgs().get());
152+
if (useArgFile.getOrElse(true)) {
153+
return NativeImageUtils.convertToArgsFile(cliArgs);
154+
}
148155
return Collections.unmodifiableList(cliArgs);
156+
149157
}
150158

151159
/**
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,10 @@ public Provider<RegularFile> getOutputFile() {
133133
@Optional
134134
public abstract RegularFileProperty getClasspathJar();
135135

136+
@Input
137+
@Optional
138+
public abstract Property<Boolean> getUseArgFile();
139+
136140
public BuildNativeImageTask() {
137141
DirectoryProperty buildDir = getProject().getLayout().getBuildDirectory();
138142
Provider<Directory> outputDir = buildDir.dir("native/" + getName());
@@ -154,7 +158,8 @@ private List<String> buildActualCommandLineArgs() {
154158
// Can't use getOutputDirectory().map(...) because Gradle would complain that we use
155159
// a mapped value before the task was called, when we are actually calling it...
156160
getProviders().provider(() -> getOutputDirectory().getAsFile().get().getAbsolutePath()),
157-
getClasspathJar()).asArguments();
161+
getClasspathJar(),
162+
getUseArgFile()).asArguments();
158163
}
159164

160165
// This property provides access to the service instance
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ class JavaLibraryFunctionalTest extends AbstractGraalVMMavenFunctionalTest {
5757
def library = file("target/java-library" + libExt)
5858

5959
when:
60-
mvn '-Pnative', '-DskipTests', 'package'
60+
mvn '-Pnative', '-DskipTests', 'package', '-DuseArgFile=false'
6161

6262
then:
6363
buildSucceeded
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException;
5656
import org.codehaus.plexus.util.xml.Xpp3Dom;
5757
import org.graalvm.buildtools.Utils;
58+
import org.graalvm.buildtools.utils.NativeImageUtils;
5859

5960
import java.io.File;
6061
import java.io.IOException;
@@ -98,6 +99,9 @@ public class NativeBuildMojo extends AbstractNativeMojo {
9899
@Parameter(property = "classpath")
99100
private List<String> classpath;
100101

102+
@Parameter(property = "useArgFile", defaultValue = "true")
103+
private boolean useArgFile;
104+
101105
private final List<Path> imageClasspath = new ArrayList<>();
102106

103107
private PluginParameterExpressionEvaluator evaluator;
@@ -128,8 +132,15 @@ public void execute() throws MojoExecutionException {
128132
maybeAddGeneratedResourcesConfig(buildArgs);
129133

130134
try {
131-
ProcessBuilder processBuilder = new ProcessBuilder(nativeImageExecutable.toString(), "-cp", classpathStr);
132-
processBuilder.command().addAll(getBuildArgs());
135+
List<String> cliArgs = new ArrayList<>();
136+
cliArgs.add("-cp");
137+
cliArgs.add(classpathStr);
138+
cliArgs.addAll(getBuildArgs());
139+
if (useArgFile) {
140+
cliArgs = NativeImageUtils.convertToArgsFile(cliArgs);
141+
}
142+
ProcessBuilder processBuilder = new ProcessBuilder(nativeImageExecutable.toString());
143+
processBuilder.command().addAll(cliArgs);
133144
processBuilder.directory(getWorkingDirectory().toFile());
134145
processBuilder.inheritIO();
135146

Original file line numberDiff line numberDiff line change
@@ -205,6 +205,7 @@
205205
</executions>
206206
<configuration>
207207
<skip>false</skip>
208+
<useArgFile>false</useArgFile>
208209
<imageName>${imageName}</imageName>
209210
<buildArgs>
210211
<buildArg>--no-fallback</buildArg>
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@
127127
</executions>
128128
<configuration>
129129
<skip>false</skip>
130+
<useArgFile>false</useArgFile>
130131
<imageName>${imageName}</imageName>
131132
<buildArgs>
132133
<buildArg>--no-fallback</buildArg>

0 commit comments

Comments
 (0)