[pure-refactor] Make everything incremental, fix custom version bugs (#17)
This commit is contained in:
parent
6e94f257c2
commit
7d2d672697
12 changed files with 384 additions and 199 deletions
|
@ -4,7 +4,7 @@ A simple wrapper around GraalVM tooling that will download and locally cache a G
|
||||||
available select parts of the GraalVM compiler for use in Gradle builds.
|
available select parts of the GraalVM compiler for use in Gradle builds.
|
||||||
|
|
||||||
To use this plugin, apply `com.palantir.graal`. See a full example in the
|
To use this plugin, apply `com.palantir.graal`. See a full example in the
|
||||||
[integration tests](src/test/groovy/com/palantir/gradle/graal/GradleGraalPluginIntegrationSpec.groovy).
|
[ETE tests](src/test/groovy/com/palantir/gradle/graal/GradleGraalEndToEndSpec.groovy).
|
||||||
|
|
||||||
Gradle Tasks
|
Gradle Tasks
|
||||||
------------
|
------------
|
||||||
|
|
|
@ -65,6 +65,7 @@ dependencies {
|
||||||
|
|
||||||
testCompile gradleTestKit()
|
testCompile gradleTestKit()
|
||||||
testCompile 'com.netflix.nebula:nebula-test'
|
testCompile 'com.netflix.nebula:nebula-test'
|
||||||
|
testCompile 'com.squareup.okhttp3:mockwebserver'
|
||||||
|
|
||||||
baseline 'com.palantir.baseline:gradle-baseline-java-config:0.26.1@zip'
|
baseline 'com.palantir.baseline:gradle-baseline-java-config:0.26.1@zip'
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,63 +23,65 @@ import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.StandardCopyOption;
|
import java.nio.file.StandardCopyOption;
|
||||||
import org.gradle.api.DefaultTask;
|
import org.gradle.api.DefaultTask;
|
||||||
import org.gradle.api.internal.TaskInternal;
|
import org.gradle.api.file.RegularFile;
|
||||||
|
import org.gradle.api.provider.Property;
|
||||||
import org.gradle.api.provider.Provider;
|
import org.gradle.api.provider.Provider;
|
||||||
import org.gradle.api.specs.Spec;
|
|
||||||
import org.gradle.api.tasks.Input;
|
import org.gradle.api.tasks.Input;
|
||||||
import org.gradle.api.tasks.OutputFile;
|
import org.gradle.api.tasks.OutputFile;
|
||||||
import org.gradle.api.tasks.TaskAction;
|
import org.gradle.api.tasks.TaskAction;
|
||||||
|
|
||||||
/** Downloads GraalVM binaries to {@link GradleGraalPlugin#CACHE_DIR}. */
|
/** Downloads GraalVM binaries. */
|
||||||
public class DownloadGraalTask extends DefaultTask {
|
public class DownloadGraalTask extends DefaultTask {
|
||||||
|
|
||||||
private static final String ARTIFACT_PATTERN = "[url]/vm-[version]/graalvm-ce-[version]-[os]-[arch].tar.gz";
|
private static final String ARTIFACT_PATTERN = "[url]/vm-[version]/graalvm-ce-[version]-[os]-[arch].tar.gz";
|
||||||
private static final String FILENAME_PATTERN = "graalvm-ce-[version]-[arch].tar.gz";
|
private static final String FILENAME_PATTERN = "graalvm-ce-[version]-[arch].tar.gz";
|
||||||
|
|
||||||
@Input private Provider<String> graalVersion;
|
private final Property<String> graalVersion = getProject().getObjects().property(String.class);
|
||||||
@Input private Provider<String> downloadBaseUrl;
|
private final Property<String> downloadBaseUrl = getProject().getObjects().property(String.class);
|
||||||
|
private final Property<Path> cacheDir = getProject().getObjects().property(Path.class);
|
||||||
|
|
||||||
|
public DownloadGraalTask() {
|
||||||
|
setGroup(GradleGraalPlugin.TASK_GROUP);
|
||||||
|
setDescription("Downloads and caches GraalVM binaries.");
|
||||||
|
|
||||||
|
onlyIf(task -> !getTgz().get().getAsFile().exists());
|
||||||
|
}
|
||||||
|
|
||||||
@TaskAction
|
@TaskAction
|
||||||
public final void downloadGraal() throws IOException {
|
public final void downloadGraal() throws IOException {
|
||||||
Path cache = getCache();
|
Path cache = getCacheSubdirectory().get();
|
||||||
if (!(cache.toFile().mkdirs() || cache.toFile().exists())) {
|
Files.createDirectories(cache);
|
||||||
throw new IllegalStateException(
|
try (InputStream in = new URL(render(ARTIFACT_PATTERN)).openStream()) {
|
||||||
"Unable to make cache directory, and the cache directory does not already exist: " + cache);
|
Files.copy(in, getTgz().get().getAsFile().toPath(), StandardCopyOption.REPLACE_EXISTING);
|
||||||
}
|
}
|
||||||
|
|
||||||
InputStream in = new URL(render(ARTIFACT_PATTERN)).openStream();
|
|
||||||
Files.copy(in, getOutput(), StandardCopyOption.REPLACE_EXISTING);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final String getGroup() {
|
|
||||||
return GradleGraalPlugin.TASK_GROUP;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final String getDescription() {
|
|
||||||
return "Downloads and caches GraalVM binaries.";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@OutputFile
|
@OutputFile
|
||||||
public final Path getOutput() {
|
public final Provider<RegularFile> getTgz() {
|
||||||
return getCache().resolve(render(FILENAME_PATTERN));
|
return getProject().getLayout()
|
||||||
|
.file(getCacheSubdirectory().map(dir -> dir.resolve(render(FILENAME_PATTERN)).toFile()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns a lambda that prevents this task from running if the download target already exists in the cache. */
|
@Input
|
||||||
@Override
|
public final Provider<String> getGraalVersion() {
|
||||||
public final Spec<? super TaskInternal> getOnlyIf() {
|
return graalVersion;
|
||||||
return spec -> !getOutput().toFile().exists();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("checkstyle:hiddenfield")
|
public final void setGraalVersion(Provider<String> provider) {
|
||||||
public final void configure(Provider<String> graalVersion, Provider<String> downloadBaseUrl) {
|
graalVersion.set(provider);
|
||||||
this.graalVersion = graalVersion;
|
|
||||||
this.downloadBaseUrl = downloadBaseUrl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Path getCache() {
|
@Input
|
||||||
return GradleGraalPlugin.CACHE_DIR.resolve(graalVersion.get());
|
public final Provider<String> getDownloadBaseUrl() {
|
||||||
|
return downloadBaseUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final void setDownloadBaseUrl(Provider<String> provider) {
|
||||||
|
downloadBaseUrl.set(provider);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Provider<Path> getCacheSubdirectory() {
|
||||||
|
return cacheDir.map(dir -> dir.resolve(graalVersion.get()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private String render(String pattern) {
|
private String render(String pattern) {
|
||||||
|
@ -109,4 +111,8 @@ public class DownloadGraalTask extends DefaultTask {
|
||||||
throw new IllegalStateException("No GraalVM support for " + Platform.architecture());
|
throw new IllegalStateException("No GraalVM support for " + Platform.architecture());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final void setCacheDir(Path value) {
|
||||||
|
cacheDir.set(value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,21 +16,38 @@
|
||||||
|
|
||||||
package com.palantir.gradle.graal;
|
package com.palantir.gradle.graal;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import org.gradle.api.DefaultTask;
|
import org.gradle.api.DefaultTask;
|
||||||
import org.gradle.api.internal.TaskInternal;
|
import org.gradle.api.file.Directory;
|
||||||
|
import org.gradle.api.file.DirectoryProperty;
|
||||||
|
import org.gradle.api.file.RegularFile;
|
||||||
|
import org.gradle.api.file.RegularFileProperty;
|
||||||
|
import org.gradle.api.provider.Property;
|
||||||
import org.gradle.api.provider.Provider;
|
import org.gradle.api.provider.Provider;
|
||||||
import org.gradle.api.specs.Spec;
|
|
||||||
import org.gradle.api.tasks.Input;
|
import org.gradle.api.tasks.Input;
|
||||||
|
import org.gradle.api.tasks.InputFile;
|
||||||
import org.gradle.api.tasks.OutputDirectory;
|
import org.gradle.api.tasks.OutputDirectory;
|
||||||
import org.gradle.api.tasks.TaskAction;
|
import org.gradle.api.tasks.TaskAction;
|
||||||
|
|
||||||
/** Extracts GraalVM tooling from downloaded tgz archive using the system's tar command. */
|
/** Extracts GraalVM tooling from downloaded tgz archive using the system's tar command. */
|
||||||
public class ExtractGraalTask extends DefaultTask {
|
public class ExtractGraalTask extends DefaultTask {
|
||||||
|
|
||||||
@Input private Provider<File> inputTgz;
|
private final RegularFileProperty inputTgz = newInputFile();
|
||||||
@Input private Provider<String> graalVersion;
|
private final Property<String> graalVersion = getProject().getObjects().property(String.class);
|
||||||
|
private final DirectoryProperty outputDirectory = newOutputDirectory();
|
||||||
|
private final Property<Path> cacheDir = getProject().getObjects().property(Path.class);
|
||||||
|
|
||||||
|
public ExtractGraalTask() {
|
||||||
|
setGroup(GradleGraalPlugin.TASK_GROUP);
|
||||||
|
setDescription("Extracts GraalVM tooling from downloaded tgz archive using the system's tar command.");
|
||||||
|
|
||||||
|
onlyIf(task -> !getOutputDirectory().get().getAsFile().exists());
|
||||||
|
outputDirectory.set(graalVersion.map(v ->
|
||||||
|
getProject().getLayout().getProjectDirectory()
|
||||||
|
.dir(cacheDir.get().toFile().getAbsolutePath())
|
||||||
|
.dir(v)
|
||||||
|
.dir("graalvm-ce-" + v)));
|
||||||
|
}
|
||||||
|
|
||||||
@TaskAction
|
@TaskAction
|
||||||
public final void extractGraal() {
|
public final void extractGraal() {
|
||||||
|
@ -41,34 +58,35 @@ public class ExtractGraalTask extends DefaultTask {
|
||||||
// ideally this would be a CopyTask, but through Gradle 4.9 CopyTask fails to correctly extract symlinks
|
// ideally this would be a CopyTask, but through Gradle 4.9 CopyTask fails to correctly extract symlinks
|
||||||
getProject().exec(spec -> {
|
getProject().exec(spec -> {
|
||||||
spec.executable("tar");
|
spec.executable("tar");
|
||||||
spec.args("-xzf", inputTgz.get().getAbsolutePath());
|
spec.args("-xzf", inputTgz.get().getAsFile().getAbsolutePath());
|
||||||
spec.workingDir(GradleGraalPlugin.CACHE_DIR.resolve(graalVersion.get()));
|
spec.workingDir(cacheDir.get().resolve(graalVersion.get()));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@InputFile
|
||||||
|
public final Provider<RegularFile> getInputTgz() {
|
||||||
|
return inputTgz;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final void setInputTgz(Provider<RegularFile> value) {
|
||||||
|
this.inputTgz.set(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Input
|
||||||
|
public final Provider<String> getGraalVersion() {
|
||||||
|
return graalVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final void setGraalVersion(Provider<String> provider) {
|
||||||
|
graalVersion.set(provider);
|
||||||
|
}
|
||||||
|
|
||||||
@OutputDirectory
|
@OutputDirectory
|
||||||
public final Path getOutputDirectory() {
|
public final Provider<Directory> getOutputDirectory() {
|
||||||
return GradleGraalPlugin.CACHE_DIR.resolve(graalVersion.get()).resolve("graalvm-ce-" + graalVersion.get());
|
return outputDirectory;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
final void setCacheDir(Path value) {
|
||||||
public final Spec<? super TaskInternal> getOnlyIf() {
|
cacheDir.set(value);
|
||||||
return spec -> !getOutputDirectory().toFile().exists();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final String getGroup() {
|
|
||||||
return GradleGraalPlugin.TASK_GROUP;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final String getDescription() {
|
|
||||||
return "Extracts GraalVM tooling from downloaded tgz archive using the system's tar command.";
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("checkstyle:hiddenfield")
|
|
||||||
public final void configure(Provider<File> inputTgz, Provider<String> graalVersion) {
|
|
||||||
this.inputTgz = inputTgz;
|
|
||||||
this.graalVersion = graalVersion;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,10 +26,10 @@ public class GraalExtension {
|
||||||
private static final String DEFAULT_DOWNLOAD_BASE_URL = "https://github.com/oracle/graal/releases/download/";
|
private static final String DEFAULT_DOWNLOAD_BASE_URL = "https://github.com/oracle/graal/releases/download/";
|
||||||
private static final String DEFAULT_GRAAL_VERSION = "1.0.0-rc6";
|
private static final String DEFAULT_GRAAL_VERSION = "1.0.0-rc6";
|
||||||
|
|
||||||
private Property<String> downloadBaseUrl;
|
private final Property<String> downloadBaseUrl;
|
||||||
private Property<String> graalVersion;
|
private final Property<String> graalVersion;
|
||||||
private Property<String> mainClass;
|
private final Property<String> mainClass;
|
||||||
private Property<String> outputName;
|
private final Property<String> outputName;
|
||||||
|
|
||||||
public GraalExtension(Project project) {
|
public GraalExtension(Project project) {
|
||||||
downloadBaseUrl = project.getObjects().property(String.class);
|
downloadBaseUrl = project.getObjects().property(String.class);
|
||||||
|
@ -86,7 +86,7 @@ public class GraalExtension {
|
||||||
*
|
*
|
||||||
* <p>Defaults to {@link #DEFAULT_GRAAL_VERSION}</p>
|
* <p>Defaults to {@link #DEFAULT_GRAAL_VERSION}</p>
|
||||||
*/
|
*/
|
||||||
public final Property<String> getGraalVersion() {
|
public final Provider<String> getGraalVersion() {
|
||||||
return graalVersion;
|
return graalVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,12 +18,12 @@ package com.palantir.gradle.graal;
|
||||||
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.util.function.Supplier;
|
import java.util.Optional;
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import org.gradle.api.Plugin;
|
import org.gradle.api.Plugin;
|
||||||
import org.gradle.api.Project;
|
import org.gradle.api.Project;
|
||||||
import org.gradle.api.Transformer;
|
import org.gradle.api.plugins.JavaPlugin;
|
||||||
import org.gradle.api.provider.Provider;
|
import org.gradle.api.tasks.TaskProvider;
|
||||||
|
import org.gradle.jvm.tasks.Jar;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds tasks to download, extract and interact with GraalVM tooling.
|
* Adds tasks to download, extract and interact with GraalVM tooling.
|
||||||
|
@ -44,61 +44,51 @@ import org.gradle.api.provider.Provider;
|
||||||
*/
|
*/
|
||||||
public class GradleGraalPlugin implements Plugin<Project> {
|
public class GradleGraalPlugin implements Plugin<Project> {
|
||||||
|
|
||||||
/** Location to cache downloaded and extracted GraalVM tooling. */
|
static final String TASK_GROUP = "Graal";
|
||||||
public static final Path CACHE_DIR =
|
|
||||||
Paths.get(System.getProperty("user.home"), ".gradle", "caches", "com.palantir.graal");
|
|
||||||
|
|
||||||
public static final String TASK_GROUP = "Graal";
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final void apply(Project project) {
|
public final void apply(Project project) {
|
||||||
|
project.getPluginManager().apply(JavaPlugin.class);
|
||||||
GraalExtension extension = project.getExtensions().create("graal", GraalExtension.class, project);
|
GraalExtension extension = project.getExtensions().create("graal", GraalExtension.class, project);
|
||||||
|
|
||||||
DownloadGraalTask downloadGraal = project.getTasks().create(
|
Path cacheDir = Optional.ofNullable((String) project.getProperties().get("com.palantir.graal.cache.dir"))
|
||||||
"downloadGraalTooling", DownloadGraalTask.class, task -> {
|
.map(Paths::get)
|
||||||
task.configure(extension.getGraalVersion(), extension.getDownloadBaseUrl());
|
.orElse(project.getGradle().getGradleUserHomeDir().toPath()
|
||||||
|
.resolve("caches")
|
||||||
|
.resolve("com.palantir.graal"));
|
||||||
|
|
||||||
|
TaskProvider<DownloadGraalTask> downloadGraal = project.getTasks().register(
|
||||||
|
"downloadGraalTooling",
|
||||||
|
DownloadGraalTask.class,
|
||||||
|
task -> {
|
||||||
|
task.setGraalVersion(extension.getGraalVersion());
|
||||||
|
task.setDownloadBaseUrl(extension.getDownloadBaseUrl());
|
||||||
|
task.setCacheDir(cacheDir);
|
||||||
});
|
});
|
||||||
|
|
||||||
ExtractGraalTask extractGraal = project.getTasks().create(
|
TaskProvider<ExtractGraalTask> extractGraal = project.getTasks().register(
|
||||||
"extractGraalTooling", ExtractGraalTask.class, task -> {
|
"extractGraalTooling",
|
||||||
|
ExtractGraalTask.class,
|
||||||
|
task -> {
|
||||||
|
task.setGraalVersion(extension.getGraalVersion());
|
||||||
|
task.setInputTgz(downloadGraal.get().getTgz());
|
||||||
|
task.setCacheDir(cacheDir);
|
||||||
task.dependsOn(downloadGraal);
|
task.dependsOn(downloadGraal);
|
||||||
task.configure(asProvider(() -> downloadGraal.getOutput().toFile()), extension.getGraalVersion());
|
|
||||||
});
|
});
|
||||||
|
|
||||||
project.getTasks().create("nativeImage", NativeImageTask.class, task -> {
|
TaskProvider<Jar> jar = project.getTasks().withType(Jar.class).named("jar");
|
||||||
|
project.getTasks().register(
|
||||||
|
"nativeImage",
|
||||||
|
NativeImageTask.class,
|
||||||
|
task -> {
|
||||||
|
task.setMainClass(extension.getMainClass());
|
||||||
|
task.setOutputName(extension.getOutputName());
|
||||||
|
task.setGraalVersion(extension.getGraalVersion());
|
||||||
|
task.setJarFile(jar.map(j -> j.getOutputs().getFiles().getSingleFile()));
|
||||||
|
task.setClasspath(project.getConfigurations().named("runtimeClasspath"));
|
||||||
|
task.setCacheDir(cacheDir);
|
||||||
task.dependsOn(extractGraal);
|
task.dependsOn(extractGraal);
|
||||||
task.dependsOn("jar");
|
task.dependsOn(jar);
|
||||||
task.configure(extension.getMainClass(), extension.getOutputName(), extension.getGraalVersion());
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private static <T> Provider<T> asProvider(Supplier<T> supplier) {
|
|
||||||
return new Provider<T>() {
|
|
||||||
@Override
|
|
||||||
public T get() {
|
|
||||||
return supplier.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public T getOrNull() {
|
|
||||||
return supplier.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public T getOrElse(T other) {
|
|
||||||
return supplier.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <S> Provider<S> map(Transformer<? extends S, ? super T> transformer) {
|
|
||||||
return asProvider(() -> transformer.transform(get()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isPresent() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
package com.palantir.gradle.graal;
|
package com.palantir.gradle.graal;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -25,90 +27,82 @@ import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import org.gradle.api.DefaultTask;
|
import org.gradle.api.DefaultTask;
|
||||||
|
import org.gradle.api.artifacts.Configuration;
|
||||||
|
import org.gradle.api.file.RegularFile;
|
||||||
|
import org.gradle.api.file.RegularFileProperty;
|
||||||
|
import org.gradle.api.provider.Property;
|
||||||
import org.gradle.api.provider.Provider;
|
import org.gradle.api.provider.Provider;
|
||||||
|
import org.gradle.api.tasks.Classpath;
|
||||||
import org.gradle.api.tasks.Input;
|
import org.gradle.api.tasks.Input;
|
||||||
|
import org.gradle.api.tasks.InputFile;
|
||||||
|
import org.gradle.api.tasks.InputFiles;
|
||||||
|
import org.gradle.api.tasks.OutputFile;
|
||||||
import org.gradle.api.tasks.TaskAction;
|
import org.gradle.api.tasks.TaskAction;
|
||||||
|
|
||||||
/** Runs GraalVM's native-image command with configured options and parameters. */
|
/** Runs GraalVM's native-image command with configured options and parameters. */
|
||||||
public class NativeImageTask extends DefaultTask {
|
public class NativeImageTask extends DefaultTask {
|
||||||
|
|
||||||
@Input private Provider<String> mainClass;
|
private final Property<String> mainClass = getProject().getObjects().property(String.class);
|
||||||
@Input private Provider<String> outputName;
|
private final Property<String> outputName = getProject().getObjects().property(String.class);
|
||||||
@Input private Provider<String> graalVersion;
|
private final Property<String> graalVersion = getProject().getObjects().property(String.class);
|
||||||
|
private final Property<Configuration> classpath = getProject().getObjects().property(Configuration.class);
|
||||||
|
private final RegularFileProperty jarFile = newInputFile();
|
||||||
|
private final RegularFileProperty outputFile = newOutputFile();
|
||||||
|
private final Property<Path> cacheDir = getProject().getObjects().property(Path.class);
|
||||||
|
|
||||||
|
public NativeImageTask() {
|
||||||
|
setGroup(GradleGraalPlugin.TASK_GROUP);
|
||||||
|
setDescription("Runs GraalVM's native-image command with configured options and parameters.");
|
||||||
|
|
||||||
|
this.outputFile.set(getProject().getLayout().getBuildDirectory()
|
||||||
|
.dir("graal")
|
||||||
|
.map(d -> d.file(outputName.get())));
|
||||||
|
|
||||||
|
doLast(t -> {
|
||||||
|
getLogger().warn("native-image available at {} ({}MB)",
|
||||||
|
getProject().relativePath(outputFile.get().getAsFile()),
|
||||||
|
fileSizeMegabytes(outputFile.get()));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@TaskAction
|
@TaskAction
|
||||||
public final void nativeImage() {
|
public final void nativeImage() throws IOException {
|
||||||
// TODO(rfink): declare classpath list as @Input in order to unlock incremental builds
|
|
||||||
getProject().exec(spec -> {
|
|
||||||
if (!mainClass.isPresent()) {
|
|
||||||
throw new IllegalArgumentException("nativeImage requires graal.mainClass to be defined.");
|
|
||||||
}
|
|
||||||
if (!graalVersion.isPresent()) {
|
|
||||||
throw new IllegalStateException("nativeImage requires graal.version to be defined.");
|
|
||||||
}
|
|
||||||
|
|
||||||
List<String> args = new ArrayList<>();
|
List<String> args = new ArrayList<>();
|
||||||
args.add("-cp");
|
args.add("-cp");
|
||||||
args.add(generateClasspathArgument());
|
args.add(generateClasspathArgument());
|
||||||
|
args.add("-H:Path=" + maybeCreateOutputDirectory().getAbsolutePath());
|
||||||
args.add("-H:Path=" + getOutputDirectory());
|
|
||||||
|
|
||||||
if (outputName.isPresent()) {
|
if (outputName.isPresent()) {
|
||||||
args.add("-H:Name=" + outputName.get());
|
args.add("-H:Name=" + outputName.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
args.add(mainClass.get());
|
args.add(mainClass.get());
|
||||||
|
|
||||||
spec.executable(getExecutable(graalVersion.get()));
|
getProject().exec(spec -> {
|
||||||
|
spec.executable(getExecutable());
|
||||||
spec.args(args);
|
spec.args(args);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private File maybeCreateOutputDirectory() throws IOException {
|
||||||
public final String getGroup() {
|
File directory = getOutputFile().get().getAsFile().getParentFile();
|
||||||
return GradleGraalPlugin.TASK_GROUP;
|
Files.createDirectories(directory.toPath());
|
||||||
|
return directory;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private String getExecutable() {
|
||||||
public final String getDescription() {
|
return cacheDir.get()
|
||||||
return "Runs GraalVM's native-image command with configured options and parameters.";
|
.resolve(Paths.get(graalVersion.get(), "graalvm-ce-" + graalVersion.get()))
|
||||||
}
|
|
||||||
|
|
||||||
private String getOutputDirectory() {
|
|
||||||
File outputDirectory = getProject().getProjectDir().toPath().resolve(Paths.get("build", "graal")).toFile();
|
|
||||||
|
|
||||||
if (!(outputDirectory.mkdirs() || outputDirectory.exists())) {
|
|
||||||
throw new IllegalStateException(
|
|
||||||
"Output directory does not exist and cannot be created: " + outputDirectory);
|
|
||||||
}
|
|
||||||
|
|
||||||
return outputDirectory.getAbsolutePath();
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getExecutable(String version) {
|
|
||||||
return GradleGraalPlugin.CACHE_DIR
|
|
||||||
.resolve(Paths.get(version, "graalvm-ce-" + version))
|
|
||||||
.resolve(getArchitectureSpecifiedBinaryPath())
|
.resolve(getArchitectureSpecifiedBinaryPath())
|
||||||
.toFile()
|
.toFile()
|
||||||
.getAbsolutePath();
|
.getAbsolutePath();
|
||||||
}
|
}
|
||||||
|
|
||||||
private String generateClasspathArgument() {
|
private String generateClasspathArgument() {
|
||||||
Set<File> classpath = new LinkedHashSet<>();
|
Set<File> classpathArgument = new LinkedHashSet<>();
|
||||||
classpath.addAll(getProject().getConfigurations().getByName("runtimeClasspath").getFiles());
|
|
||||||
classpath.addAll(getProject().getTasks().getByName("jar").getOutputs().getFiles().getFiles());
|
|
||||||
|
|
||||||
return classpath.stream()
|
classpathArgument.addAll(classpath.get().getFiles());
|
||||||
.map(File::getAbsolutePath)
|
classpathArgument.add(jarFile.getAsFile().get());
|
||||||
.collect(Collectors.joining(":"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("checkstyle:hiddenfield")
|
return classpathArgument.stream().map(File::getAbsolutePath).collect(Collectors.joining(":"));
|
||||||
public final void configure(Provider<String> mainClass, Provider<String> outputName,
|
|
||||||
Provider<String> graalVersion) {
|
|
||||||
this.mainClass = mainClass;
|
|
||||||
this.outputName = outputName;
|
|
||||||
this.graalVersion = graalVersion;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Path getArchitectureSpecifiedBinaryPath() {
|
private Path getArchitectureSpecifiedBinaryPath() {
|
||||||
|
@ -120,4 +114,66 @@ public class NativeImageTask extends DefaultTask {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private long fileSizeMegabytes(RegularFile regularFile) {
|
||||||
|
try {
|
||||||
|
return Files.size(regularFile.getAsFile().toPath()) / (1000 * 1000);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Input
|
||||||
|
public final Provider<String> getMainClass() {
|
||||||
|
return mainClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final void setMainClass(Provider<String> provider) {
|
||||||
|
mainClass.set(provider);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Input
|
||||||
|
public final Provider<String> getOutputName() {
|
||||||
|
return outputName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Input
|
||||||
|
public final Provider<String> getGraalVersion() {
|
||||||
|
return graalVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final void setGraalVersion(Provider<String> provider) {
|
||||||
|
graalVersion.set(provider);
|
||||||
|
}
|
||||||
|
|
||||||
|
@InputFiles
|
||||||
|
@Classpath
|
||||||
|
public final Provider<Configuration> getClasspath() {
|
||||||
|
return classpath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final void setClasspath(Provider<Configuration> provider) {
|
||||||
|
classpath.set(provider);
|
||||||
|
}
|
||||||
|
|
||||||
|
@InputFile
|
||||||
|
public final Provider<RegularFile> getJarFiles() {
|
||||||
|
return jarFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final void setJarFile(Provider<File> provider) {
|
||||||
|
jarFile.set(getProject().getLayout().file(provider));
|
||||||
|
}
|
||||||
|
|
||||||
|
@OutputFile
|
||||||
|
public final Provider<RegularFile> getOutputFile() {
|
||||||
|
return outputFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final void setOutputName(Provider<String> provider) {
|
||||||
|
outputName.set(provider);
|
||||||
|
}
|
||||||
|
|
||||||
|
final void setCacheDir(Path value) {
|
||||||
|
cacheDir.set(value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
implementation-class=com.palantir.gradle.graal.GradleGraalPlugin
|
|
@ -0,0 +1,80 @@
|
||||||
|
/*
|
||||||
|
* (c) Copyright 2018 Palantir Technologies Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.palantir.gradle.graal
|
||||||
|
|
||||||
|
import nebula.test.IntegrationSpec
|
||||||
|
import nebula.test.functional.ExecutionResult
|
||||||
|
|
||||||
|
class GradleGraalEndToEndSpec extends IntegrationSpec {
|
||||||
|
|
||||||
|
def 'test default version nativeImage'() {
|
||||||
|
setup:
|
||||||
|
directory("src/main/java/com/palantir/test")
|
||||||
|
file("src/main/java/com/palantir/test/Main.java") << '''
|
||||||
|
package com.palantir.test;
|
||||||
|
|
||||||
|
public final class Main {
|
||||||
|
public static final void main(String[] args) {
|
||||||
|
System.out.println("hello, world!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'''
|
||||||
|
|
||||||
|
buildFile << '''
|
||||||
|
apply plugin: 'com.palantir.graal'
|
||||||
|
|
||||||
|
graal {
|
||||||
|
mainClass 'com.palantir.test.Main'
|
||||||
|
outputName 'hello-world'
|
||||||
|
graalVersion '1.0.0-rc5'
|
||||||
|
}
|
||||||
|
'''
|
||||||
|
|
||||||
|
when:
|
||||||
|
ExecutionResult result = runTasksSuccessfully('nativeImage') // note, this accesses your real ~/.gradle cache
|
||||||
|
println "Gradle Standard Out:\n" + result.standardOutput
|
||||||
|
println "Gradle Standard Error:\n" + result.standardError
|
||||||
|
File output = new File(getProjectDir(), "build/graal/hello-world");
|
||||||
|
|
||||||
|
then:
|
||||||
|
output.exists()
|
||||||
|
output.getAbsolutePath().execute().text.equals("hello, world!\n")
|
||||||
|
|
||||||
|
when:
|
||||||
|
ExecutionResult result2 = runTasksSuccessfully('nativeImage')
|
||||||
|
|
||||||
|
then:
|
||||||
|
result2.wasUpToDate(':nativeImage')
|
||||||
|
|
||||||
|
when:
|
||||||
|
new File(getProjectDir(), "src/main/java/com/palantir/test/Main.java").text = '''
|
||||||
|
package com.palantir.test;
|
||||||
|
|
||||||
|
public final class Main {
|
||||||
|
public static final void main(String[] args) {
|
||||||
|
System.out.println("hello, world (modified)!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'''
|
||||||
|
ExecutionResult result3 = runTasksSuccessfully('nativeImage')
|
||||||
|
|
||||||
|
then:
|
||||||
|
println result3.standardOutput
|
||||||
|
!result3.wasUpToDate(':nativeImage')
|
||||||
|
output.getAbsolutePath().execute().text.equals("hello, world (modified)!\n")
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,12 +18,20 @@ package com.palantir.gradle.graal
|
||||||
|
|
||||||
import nebula.test.IntegrationSpec
|
import nebula.test.IntegrationSpec
|
||||||
import nebula.test.functional.ExecutionResult
|
import nebula.test.functional.ExecutionResult
|
||||||
|
import okhttp3.mockwebserver.MockResponse
|
||||||
|
import okhttp3.mockwebserver.MockWebServer
|
||||||
|
import org.junit.Rule
|
||||||
|
|
||||||
class GradleGraalPluginIntegrationSpec extends IntegrationSpec {
|
class GradleGraalPluginIntegrationSpec extends IntegrationSpec {
|
||||||
def 'test default version nativeImage'() {
|
|
||||||
setup:
|
@Rule MockWebServer server = new MockWebServer()
|
||||||
new File(getProjectDir(), "src/main/java/com/palantir/test").mkdirs()
|
String fakeBaseUrl
|
||||||
new File(getProjectDir(), "src/main/java/com/palantir/test/Main.java") << '''
|
|
||||||
|
def setup() {
|
||||||
|
fakeBaseUrl = String.format("http://localhost:%s/oracle/graal/releases/download/", server.getPort())
|
||||||
|
|
||||||
|
directory("src/main/java/com/palantir/test")
|
||||||
|
file("src/main/java/com/palantir/test/Main.java") << '''
|
||||||
package com.palantir.test;
|
package com.palantir.test;
|
||||||
|
|
||||||
public final class Main {
|
public final class Main {
|
||||||
|
@ -33,26 +41,53 @@ class GradleGraalPluginIntegrationSpec extends IntegrationSpec {
|
||||||
}
|
}
|
||||||
'''
|
'''
|
||||||
|
|
||||||
buildFile << '''
|
file('gradle.properties') << "com.palantir.graal.cache.dir=${getProjectDir().toPath().resolve("cacheDir").toAbsolutePath()}"
|
||||||
apply plugin: 'java'
|
}
|
||||||
|
|
||||||
|
def 'allows specifying different graal version'() {
|
||||||
|
setup:
|
||||||
|
buildFile << """
|
||||||
apply plugin: 'com.palantir.graal'
|
apply plugin: 'com.palantir.graal'
|
||||||
|
|
||||||
graal {
|
graal {
|
||||||
mainClass 'com.palantir.test.Main'
|
graalVersion '1.0.0-rc3'
|
||||||
outputName 'hello-world'
|
downloadBaseUrl '${fakeBaseUrl}'
|
||||||
}
|
}
|
||||||
'''
|
"""
|
||||||
|
server.enqueue(new MockResponse().setBody('<<tgz>>'));
|
||||||
|
|
||||||
when:
|
when:
|
||||||
ExecutionResult result = runTasks('nativeImage')
|
ExecutionResult result = runTasksSuccessfully('downloadGraalTooling')
|
||||||
// capture output from Gradle runs
|
|
||||||
println "Gradle Standard Out:\n" + result.standardOutput
|
|
||||||
println "Gradle Standard Error:\n" + result.standardError
|
|
||||||
File output = new File(getProjectDir(), "build/graal/hello-world");
|
|
||||||
|
|
||||||
then:
|
then:
|
||||||
result.success
|
println result.getStandardOutput()
|
||||||
output.exists()
|
result.wasExecuted(':downloadGraalTooling')
|
||||||
output.getAbsolutePath().execute().text.equals("hello, world!\n")
|
!result.wasUpToDate(':downloadGraalTooling')
|
||||||
|
!result.wasSkipped(':downloadGraalTooling')
|
||||||
|
|
||||||
|
server.takeRequest().requestUrl.toString() =~ "http://localhost:${server.port}" +
|
||||||
|
"/oracle/graal/releases/download//vm-1.0.0-rc3/graalvm-ce-1.0.0-rc3-(macos|linux)-amd64.tar.gz"
|
||||||
|
|
||||||
|
file("cacheDir/1.0.0-rc3/graalvm-ce-1.0.0-rc3-amd64.tar.gz").text == '<<tgz>>'
|
||||||
|
}
|
||||||
|
|
||||||
|
def 'downloadGraalTooling behaves incrementally'() {
|
||||||
|
setup:
|
||||||
|
buildFile << """
|
||||||
|
apply plugin: 'com.palantir.graal'
|
||||||
|
|
||||||
|
graal {
|
||||||
|
downloadBaseUrl '${fakeBaseUrl}'
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
server.enqueue(new MockResponse().setBody('<<tgz>>'));
|
||||||
|
|
||||||
|
when:
|
||||||
|
ExecutionResult result1 = runTasksSuccessfully('downloadGraalTooling')
|
||||||
|
ExecutionResult result2 = runTasksSuccessfully('downloadGraalTooling')
|
||||||
|
|
||||||
|
then:
|
||||||
|
result1.wasSkipped(':downloadGraalTooling') == false
|
||||||
|
result2.wasSkipped(':downloadGraalTooling') == true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,9 +18,6 @@ package com.palantir.gradle.graal
|
||||||
|
|
||||||
import nebula.test.PluginProjectSpec
|
import nebula.test.PluginProjectSpec
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests for {@link GradleGraalPlugin}.
|
|
||||||
*/
|
|
||||||
class GradleGraalPluginProjectSpec extends PluginProjectSpec {
|
class GradleGraalPluginProjectSpec extends PluginProjectSpec {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
com.netflix.nebula:nebula-test = 6.7.1
|
com.netflix.nebula:nebula-test = 6.7.1
|
||||||
|
com.squareup.okhttp3:* = 3.11.0
|
||||||
|
|
Loading…
Reference in a new issue