Skip to content

Commit

Permalink
Use argument file when invoking native-image
Browse files Browse the repository at this point in the history
With this, it is no longer necessary to build a fat jar (or shaded jar)
under Windows in case of long classpath issues. Using the argument
file is enabled by default. If the user uses a version older than 21.3,
then they can use the fat jar support (and disable arg-file usage).

Fixes #203
  • Loading branch information
melix committed Feb 10, 2022
1 parent f47e3f8 commit cbcc526
Show file tree
Hide file tree
Showing 14 changed files with 87 additions and 23 deletions.
Expand Up @@ -42,8 +42,12 @@

import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.Collections;
import java.util.List;

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

Expand All @@ -66,4 +70,16 @@ public static void maybeCreateConfigureUtilSymlink(File configureUtilFile, Path
public static String nativeImageConfigureFileName() {
return "native-image-configure" + GRAALVM_EXE_EXTENSION;
}

public static List<String> convertToArgsFile(List<String> cliArgs) {
try {
File tmpFile = File.createTempFile("native-image", "args");
tmpFile.deleteOnExit();
Files.write(tmpFile.toPath(), cliArgs, StandardCharsets.UTF_8, StandardOpenOption.CREATE);
return Collections.singletonList("@" + tmpFile.getAbsolutePath());
} catch (IOException e) {

return Collections.unmodifiableList(cliArgs);
}
}
}
16 changes: 7 additions & 9 deletions docs/src/docs/asciidoc/gradle-plugin.adoc
Expand Up @@ -174,27 +174,25 @@ include::../snippets/gradle/kotlin/build.gradle.kts[tags=all-config-options]
NOTE: For options that can be set using command-line, if both DSL and command-line options are present, command-line options take precedence.

[[long_classpath_and_fat_jar_support]]
==== Long classpath and fat jar support
==== Long classpath, @argument file and fat jar support

Under Windows, https://github.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.
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.
Since release 0.9.11, the plugin will automatically pass arguments to the `native-image` tool using an argument file, which should prevent all https://github.com/graalvm/native-build-tools/issues/85[long classpath issues] under Windows.
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:

In case this behavior is not required, you can disable the fat jar creation by calling:

.Disabling the fat jar creation
.Enabling the fat jar creation
[source, groovy, role="multi-language-sample"]
----
include::../snippets/gradle/groovy/build.gradle[tags=disable-fatjar]
include::../snippets/gradle/groovy/build.gradle[tags=enable-fatjar]
----

[source,kotlin,role="multi-language-sample"]
----
include::../snippets/gradle/kotlin/build.gradle.kts[tags=disable-fatjar]
include::../snippets/gradle/kotlin/build.gradle.kts[tags=enable-fatjar]
----

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_:

.Disabling the fat jar creation
.Enabling a custom fat jar creation
[source, groovy, role="multi-language-sample"]
----
include::../snippets/gradle/groovy/build.gradle[tags=custom-fatjar]
Expand Down
9 changes: 9 additions & 0 deletions docs/src/docs/asciidoc/index.adoc
Expand Up @@ -14,6 +14,15 @@ If you are interested in contributing, please refer to our https://github.com/gr

[[changelog]]
== Changelog
=== Release 0.9.11

==== Maven plugin

- Add support for long classpath by using an argument file when invoking `native-image`

==== Gradle plugin

- Add support for long classpath by using an argument file when invoking `native-image`

=== Release 0.9.10

Expand Down
8 changes: 6 additions & 2 deletions docs/src/docs/asciidoc/maven-plugin.adoc
Expand Up @@ -258,11 +258,15 @@ use cases such as the following:
wish to run those same tests in native mode.

[[long_classpath_and_shading_support]]
== Long classpath and shading support
== Long classpath, @argument file and shading support

Under Windows, https://github.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.

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.
To avoid this, since release 0.9.11, the plugin will use an argument file to pass the arguments to the `native-image` tool, instead of passing them directly.

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.

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.

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

Expand Down
7 changes: 4 additions & 3 deletions docs/src/docs/snippets/gradle/groovy/build.gradle
Expand Up @@ -98,15 +98,16 @@ graalvmNative {
}
// end::all-config-options[]

// tag::disable-fatjar[]
// tag::enable-fatjar[]
graalvmNative {
useArgFile = false // required for older GraalVM releases
binaries {
main {
useFatJar = false
useFatJar = true
}
}
}
// end::disable-fatjar[]
// end::enable-fatjar[]

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

Expand Down
7 changes: 4 additions & 3 deletions docs/src/docs/snippets/gradle/kotlin/build.gradle.kts
Expand Up @@ -99,15 +99,16 @@ graalvmNative {
}
// end::all-config-options[]

// tag::disable-fatjar[]
// tag::enable-fatjar[]
graalvmNative {
useFatJar.set(false) // required for older GraalVM releases
binaries {
named("main") {
useFatJar.set(false)
useFatJar.set(true)
}
}
}
// end::disable-fatjar[]
// end::enable-fatjar[]

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

Expand Down
Expand Up @@ -171,6 +171,7 @@ public void apply(Project project) {

logger = GraalVMLogger.of(project.getLogger());
DefaultGraalVmExtension graalExtension = (DefaultGraalVmExtension) registerGraalVMExtension(project);
graalExtension.getUseArgFile().convention(true);
project.getPlugins()
.withType(JavaPlugin.class, javaPlugin -> configureJavaProject(project, nativeImageServiceProvider, graalExtension));
project.afterEvaluate(p -> {
Expand Down Expand Up @@ -253,6 +254,7 @@ private void configureAutomaticTaskCreation(Project project,
builder.setGroup(LifecycleBasePlugin.BUILD_GROUP);
builder.getOptions().convention(options);
builder.getAgentEnabled().set(agent);
builder.getUseArgFile().convention(graalExtension.getUseArgFile());
});
String runTaskName = deriveTaskName(binaryName, "native", "Run");
if ("main".equals(binaryName)) {
Expand Down
Expand Up @@ -90,6 +90,14 @@ public interface GraalVMExtension {
*/
Property<Boolean> getToolchainDetection();

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


interface TestBinaryConfig {
/**
Expand Down
Expand Up @@ -43,7 +43,6 @@

import org.graalvm.buildtools.gradle.dsl.NativeImageOptions;
import org.graalvm.buildtools.gradle.dsl.NativeResourcesOptions;
import org.graalvm.buildtools.utils.SharedConstants;
import org.gradle.api.Action;
import org.gradle.api.file.ConfigurableFileCollection;
import org.gradle.api.model.ObjectFactory;
Expand Down Expand Up @@ -215,7 +214,7 @@ public BaseNativeImageOptions(String name,
getAgent().getEnabled().convention(false);
getSharedLibrary().convention(false);
getImageName().convention(defaultImageName);
getUseFatJar().convention(SharedConstants.IS_WINDOWS);
getUseFatJar().convention(false);
}

private static Provider<Boolean> property(ProviderFactory providers, String name) {
Expand Down
Expand Up @@ -42,6 +42,7 @@
package org.graalvm.buildtools.gradle.internal;

import org.graalvm.buildtools.gradle.dsl.NativeImageOptions;
import org.graalvm.buildtools.utils.NativeImageUtils;
import org.gradle.api.Transformer;
import org.gradle.api.file.FileSystemLocation;
import org.gradle.api.file.RegularFile;
Expand All @@ -65,17 +66,20 @@ public class NativeImageCommandLineProvider implements CommandLineArgumentProvid
private final Provider<String> executableName;
private final Provider<String> outputDirectory;
private final Provider<RegularFile> classpathJar;
private final Provider<Boolean> useArgFile;

public NativeImageCommandLineProvider(Provider<NativeImageOptions> options,
Provider<Boolean> agentEnabled,
Provider<String> executableName,
Provider<String> outputDirectory,
Provider<RegularFile> classpathJar) {
Provider<RegularFile> classpathJar,
Provider<Boolean> useArgFile) {
this.options = options;
this.agentEnabled = agentEnabled;
this.executableName = executableName;
this.outputDirectory = outputDirectory;
this.classpathJar = classpathJar;
this.useArgFile = useArgFile;
}

@Nested
Expand Down Expand Up @@ -145,7 +149,11 @@ public List<String> asArguments() {
cliArgs.add("-H:Class=" + options.getMainClass().get());
}
cliArgs.addAll(options.getBuildArgs().get());
if (useArgFile.getOrElse(true)) {
return NativeImageUtils.convertToArgsFile(cliArgs);
}
return Collections.unmodifiableList(cliArgs);

}

/**
Expand Down
Expand Up @@ -133,6 +133,10 @@ public Provider<RegularFile> getOutputFile() {
@Optional
public abstract RegularFileProperty getClasspathJar();

@Input
@Optional
public abstract Property<Boolean> getUseArgFile();

public BuildNativeImageTask() {
DirectoryProperty buildDir = getProject().getLayout().getBuildDirectory();
Provider<Directory> outputDir = buildDir.dir("native/" + getName());
Expand All @@ -154,7 +158,8 @@ private List<String> buildActualCommandLineArgs() {
// Can't use getOutputDirectory().map(...) because Gradle would complain that we use
// a mapped value before the task was called, when we are actually calling it...
getProviders().provider(() -> getOutputDirectory().getAsFile().get().getAbsolutePath()),
getClasspathJar()).asArguments();
getClasspathJar(),
getUseArgFile()).asArguments();
}

// This property provides access to the service instance
Expand Down
Expand Up @@ -55,6 +55,7 @@
import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException;
import org.codehaus.plexus.util.xml.Xpp3Dom;
import org.graalvm.buildtools.Utils;
import org.graalvm.buildtools.utils.NativeImageUtils;

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

@Parameter(property = "useArgFile", defaultValue = "true")
private boolean useArgFile;

private final List<Path> imageClasspath = new ArrayList<>();

private PluginParameterExpressionEvaluator evaluator;
Expand Down Expand Up @@ -128,8 +132,15 @@ public void execute() throws MojoExecutionException {
maybeAddGeneratedResourcesConfig(buildArgs);

try {
ProcessBuilder processBuilder = new ProcessBuilder(nativeImageExecutable.toString(), "-cp", classpathStr);
processBuilder.command().addAll(getBuildArgs());
List<String> cliArgs = new ArrayList<>();
cliArgs.add("-cp");
cliArgs.add(classpathStr);
cliArgs.addAll(getBuildArgs());
if (useArgFile) {
cliArgs = NativeImageUtils.convertToArgsFile(cliArgs);
}
ProcessBuilder processBuilder = new ProcessBuilder(nativeImageExecutable.toString());
processBuilder.command().addAll(cliArgs);
processBuilder.directory(getWorkingDirectory().toFile());
processBuilder.inheritIO();

Expand Down
1 change: 1 addition & 0 deletions samples/java-application-with-tests/pom.xml
Expand Up @@ -205,6 +205,7 @@
</executions>
<configuration>
<skip>false</skip>
<useArgFile>false</useArgFile>
<imageName>${imageName}</imageName>
<buildArgs>
<buildArg>--no-fallback</buildArg>
Expand Down
1 change: 1 addition & 0 deletions samples/java-application/pom.xml
Expand Up @@ -127,6 +127,7 @@
</executions>
<configuration>
<skip>false</skip>
<useArgFile>false</useArgFile>
<imageName>${imageName}</imageName>
<buildArgs>
<buildArg>--no-fallback</buildArg>
Expand Down

0 comments on commit cbcc526

Please sign in to comment.