Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP Add feature to pass parameters by file #144

Closed
wants to merge 14 commits into from
Closed
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
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ You can configure arguments to be passed to GraalVM via `arguments(String...)` m
- `arguments {}` - Configure `native-image` command's arguments in configuration block.
- `add(String)` - Equivalent to `arguments(String...)`.
- `add(Provider<String>)` - Equivalent to `arguments(Provider<String>...)`.
- `preferByFile(File)` - pass arguments/parameters to `native-image` command by file. The file will be created by this plugin.
- `preferByFile(Path)` - ibid.
- `preferByFile(RegularFile)` - ibid.

For more information, please see appendix at the bottom of this README.

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package org.mikeneck.graalvm;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertAll;

import org.gradle.testkit.runner.BuildResult;
import org.gradle.testkit.runner.TaskOutcome;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;

@ExtendWith(TestProjectSetup.class)
class Case143UsingFileTest {

@Test
@TestProject("case-143-using-file")
void preferByFile(@NotNull Gradlew gradlew, @NotNull FunctionalTestContext context) {
BuildResult result = gradlew.invoke("nativeImage", "--info");

assertAll(
() ->
assertThat(result.tasks(TaskOutcome.SUCCESS))
.anyMatch(task -> task.getPath().equals("nativeImage")),
() -> assertThat(context.file("build/tmp/native-image-args/arguments.txt")).exists());
}
}
19 changes: 19 additions & 0 deletions src/functionalTest/resources/case-143-using-file/build-gradle.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
plugins {
id 'java'
id 'org.mikeneck.graalvm-native-image'
}

nativeImage {
graalVmHome = System.getenv('JAVA_HOME')
mainClass = 'com.example.App'
classpath = sourceSets.main.output
executableName = 'test-app'
runtimeClasspath = configurations.runtimeClasspath
arguments {
preferByFile()
add '--no-fallback'
add options.traceClassInitialization('com.example.MessageProvider')
add '--initialize-at-build-time=com.example.Printer'
add '-H:+ReportExceptionStackTraces'
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.example;

class App {
public static void main(String[] args) {
MessageProvider messageProvider = () -> "hello";
System.out.println(messageProvider.getMessage());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.example;

public interface MessageProvider {
String getMessage();
}
99 changes: 55 additions & 44 deletions src/main/java/org/mikeneck/graalvm/DefaultNativeImageTask.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
package org.mikeneck.graalvm;

import java.io.File;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import javax.inject.Inject;
import org.gradle.api.Action;
import org.gradle.api.DefaultTask;
Expand All @@ -16,20 +18,19 @@
import org.gradle.api.artifacts.Configuration;
import org.gradle.api.file.ConfigurableFileCollection;
import org.gradle.api.file.Directory;
import org.gradle.api.file.DirectoryProperty;
import org.gradle.api.file.FileCollection;
import org.gradle.api.file.ProjectLayout;
import org.gradle.api.file.RegularFileProperty;
import org.gradle.api.model.ObjectFactory;
import org.gradle.api.provider.ListProperty;
import org.gradle.api.provider.Property;
import org.gradle.api.provider.Provider;
import org.gradle.api.tasks.Input;
import org.gradle.api.tasks.Internal;
import org.gradle.api.tasks.Nested;
import org.gradle.api.tasks.OutputFile;
import org.gradle.api.tasks.TaskAction;
import org.gradle.api.tasks.bundling.Jar;
import org.jetbrains.annotations.NotNull;
import org.mikeneck.graalvm.nativeimage.ConfigurationFiles;
import org.mikeneck.graalvm.nativeimage.NativeImageArguments;
import org.mikeneck.graalvm.nativeimage.NativeImageArgumentsFactory;
import org.mikeneck.graalvm.nativeimage.options.DefaultOptions;
Expand All @@ -44,50 +45,50 @@ public class DefaultNativeImageTask extends DefaultTask implements NativeImageTa

@NotNull private final NativeImageArguments nativeImageArguments;

@SuppressWarnings("UnstableApiUsage")
@Inject
public DefaultNativeImageTask(
@NotNull Project project,
@NotNull Property<GraalVmHome> graalVmHome,
@NotNull Property<String> mainClass,
@NotNull Property<Configuration> runtimeClasspath,
@NotNull ConfigurableFileCollection jarFile) {
ObjectFactory objectFactory = project.getObjects();
ProjectLayout projectLayout = project.getLayout();
this.graalVmHome = graalVmHome;
@NotNull Property<String> executableName = objectFactory.property(String.class);
@NotNull ListProperty<String> additionalArguments = objectFactory.listProperty(String.class);
@NotNull
DirectoryProperty outputDirectory =
objectFactory
.directoryProperty()
.value(projectLayout.getBuildDirectory().dir(DEFAULT_OUTPUT_DIRECTORY_NAME));
NativeImageArgumentsFactory nativeImageArgumentsFactory =
NativeImageArgumentsFactory.getInstance();
this.graalVmHome = graalVmHome;
this.nativeImageArguments =
nativeImageArgumentsFactory.create(
runtimeClasspath,
mainClass,
jarFile,
outputDirectory,
executableName,
additionalArguments,
new ConfigurationFiles(project));
nativeImageArgumentsFactory.create(project, mainClass, runtimeClasspath, jarFile);
}

@TaskAction
public void createNativeImage() {
createOutputDirectoryIfNotExisting();
Path nativeImageCommand = nativeImageCommand();
Optional<Path> argumentsFile = prepareArgumentsFile();

List<String> arguments =
argumentsFile
.map(file -> String.format("@%s", file))
.map(Arrays::asList)
.orElseGet(this::arguments);

getProject()
.exec(
execSpec -> {
getLogger().info("run native-image binary.");
execSpec.setExecutable(nativeImageCommand);
execSpec.args(arguments());
execSpec.args(arguments);
});
}

@SuppressWarnings("ResultOfMethodCallIgnored")
private void createOutputDirectoryIfNotExisting() {
File outputDir = outputDirectory();
getLogger().info("create output directory if not exists: {}", outputDir);
if (!outputDir.exists()) {
outputDir.mkdirs();
}
}

private Path nativeImageCommand() {
GraalVmHome graalVmHome = graalVmHome();
Optional<Path> nativeImage = graalVmHome.nativeImage();
Expand All @@ -99,6 +100,26 @@ private Path nativeImageCommand() {
return nativeImage.get();
}

private Optional<Path> prepareArgumentsFile() {
RegularFileProperty file = nativeImageArguments.argumentsFile();
if (!file.isPresent()) {
return Optional.empty();
}
try {
Path argumentsFile = file.get().getAsFile().toPath();
Path directory = argumentsFile.getParent();
if (!Files.exists(directory)) {
Files.createDirectories(directory);
}
List<String> arguments = arguments();
Files.write(argumentsFile, arguments);
return Optional.of(argumentsFile);
} catch (IOException e) {
throw new UncheckedIOException(
"error while preparing arguments file for native-image task", e);
}
}

private GraalVmHome graalVmHome() {
return graalVmHome.get();
}
Expand Down Expand Up @@ -127,13 +148,9 @@ public File outputDirectory() {
return nativeImageArguments.getOutputDirectory().getAsFile().get();
}

@SuppressWarnings("ResultOfMethodCallIgnored")
private void createOutputDirectoryIfNotExisting() {
File outputDir = outputDirectory();
getLogger().info("create output directory if not exists: {}", outputDir);
if (!outputDir.exists()) {
outputDir.mkdirs();
}
@OutputFile
public Provider<File> getArgumentsFile() {
return nativeImageArguments.argumentsFile().getAsFile();
}

private List<String> arguments() {
Expand Down Expand Up @@ -208,23 +225,17 @@ public void withConfigFiles(@NotNull Action<NativeImageConfigurationFiles> confi

@Override
public void arguments(String... arguments) {
Project project = getProject();
nativeImageArguments.addArguments(
project.provider(
() ->
Arrays.stream(arguments).filter(it -> !it.isEmpty()).collect(Collectors.toList())));
nativeImageArguments.addArguments(arguments);
}

@SafeVarargs
@Override
public final void arguments(Provider<String>... arguments) {
Project project = getProject();
ListProperty<String> listProperty = project.getObjects().listProperty(String.class);
for (Provider<String> argument : arguments) {
listProperty.add(argument);
}
nativeImageArguments.addArguments(
listProperty.map(
list -> list.stream().filter(it -> !it.isEmpty()).collect(Collectors.toList())));
nativeImageArguments.addArguments(arguments);
}

@Override
public void arguments(@NotNull Action<? super NativeImageArgumentsConfig> config) {
nativeImageArguments.applyArgumentsConfig(config);
}
}
61 changes: 61 additions & 0 deletions src/main/java/org/mikeneck/graalvm/NativeImageArgumentsConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package org.mikeneck.graalvm;

import java.io.File;
import java.nio.file.Path;
import org.gradle.api.file.RegularFile;
import org.gradle.api.provider.Provider;
import org.jetbrains.annotations.NotNull;

/**
* Represents native-image command options. You can configure command options via this interface.
*/
public interface NativeImageArgumentsConfig {
/**
* Pass parameters by file to native-image, not by command line arguments. The file will be
* created by {@link NativeImageTask}. The path of the file is {@code
* buildDir/tmp/native-image-args/arguments.txt}.
*/
void preferByFile();
/**
* Pass parameters by file to native-image, not by command line arguments. The given file will be
* created by {@link NativeImageTask}.
*
* @param file - the file to pass command line parameters.
*/
void preferByFile(@NotNull File file);
/**
* Pass parameters by file to native-image, not by command line arguments. The given file will be
* created by {@link NativeImageTask}.
*
* @param file - the file to pass command line parameters.
*/
void preferByFile(@NotNull Path file);
/**
* Pass parameters by file to native-image, not by command line arguments. The given file will be
* created by {@link NativeImageTask}.
*
* @param file - the file to pass command line parameters.
*/
void preferByFile(@NotNull RegularFile file);
/**
* Pass parameters by file to native-image, not by command line arguments. The given file will be
* created by {@link NativeImageTask}.
*
* @param file - the file to pass command line parameters.
*/
void preferByFile(@NotNull Provider<? extends RegularFile> file);

/**
* Passes an option to native-image one by one.
*
* @param argument - native-image option argument.
*/
void add(String argument);

/**
* Passes an option to native-image one by one.
*
* @param argument - native-image option argument.
*/
void add(Provider<String> argument);
}
38 changes: 3 additions & 35 deletions src/main/java/org/mikeneck/graalvm/NativeImageConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,40 +46,8 @@ public interface NativeImageConfig {
@SuppressWarnings("unchecked")
void arguments(Provider<String>... arguments);

/**
* Represents native-image command options. You can configure command options via this interface.
*/
interface ArgumentsConfig {
/**
* Passes an option to native-image one by one.
*
* @param argument - native-image option argument.
*/
void add(String argument);

/**
* Passes an option to native-image one by one.
*
* @param argument - native-image option argument.
*/
void add(Provider<String> argument);
}
/** {@inheritDoc} */
interface ArgumentsConfig extends NativeImageArgumentsConfig {}

@SuppressWarnings("unchecked")
default void arguments(@NotNull Action<ArgumentsConfig> config) {
NativeImageConfig thisConfig = this;
ArgumentsConfig argumentsConfig =
new ArgumentsConfig() {
@Override
public void add(String argument) {
thisConfig.arguments(argument);
}

@Override
public void add(Provider<String> argument) {
thisConfig.arguments(argument);
}
};
config.execute(argumentsConfig);
}
void arguments(@NotNull Action<? super NativeImageArgumentsConfig> config);
}
Loading