diff --git a/native-gradle-plugin/src/functionalTest/groovy/org/graalvm/buildtools/gradle/JavaApplicationWithAgentFunctionalTest.groovy b/native-gradle-plugin/src/functionalTest/groovy/org/graalvm/buildtools/gradle/JavaApplicationWithAgentFunctionalTest.groovy index e98659f09..6ec8ace24 100644 --- a/native-gradle-plugin/src/functionalTest/groovy/org/graalvm/buildtools/gradle/JavaApplicationWithAgentFunctionalTest.groovy +++ b/native-gradle-plugin/src/functionalTest/groovy/org/graalvm/buildtools/gradle/JavaApplicationWithAgentFunctionalTest.groovy @@ -42,7 +42,6 @@ package org.graalvm.buildtools.gradle import org.graalvm.buildtools.gradle.fixtures.AbstractFunctionalTest -import spock.lang.Issue import spock.lang.Unroll class JavaApplicationWithAgentFunctionalTest extends AbstractFunctionalTest { @@ -175,4 +174,39 @@ class JavaApplicationWithAgentFunctionalTest extends AbstractFunctionalTest { where: junitVersion = System.getProperty('versions.junit') } + + @Unroll("plugin supports configuration cache (JUnit Platform #junitVersion)") + def "supports configuration cache"() { + debug = true + var metadata_dir = 'src/main/resources/META-INF/native-image' + given: + withSample("java-application-with-reflection") + + when: + run 'run', '-Pagent', '--configuration-cache' + + then: + tasks { + succeeded ':run' + doesNotContain ':jar' + } + + and: + ['jni', 'proxy', 'reflect', 'resource', 'serialization'].each { name -> + assert file("build/native/agent-output/run/${name}-config.json").exists() + } + + when: + run'run', '-Pagent', '--configuration-cache', '--rerun-tasks' + + then: + tasks { + succeeded ':run' + doesNotContain ':jar' + } + outputContains "Reusing configuration cache" + + where: + junitVersion = System.getProperty('versions.junit') + } } diff --git a/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/NativeImagePlugin.java b/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/NativeImagePlugin.java index 332df0445..27a6a9f4a 100644 --- a/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/NativeImagePlugin.java +++ b/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/NativeImagePlugin.java @@ -54,8 +54,8 @@ import org.graalvm.buildtools.gradle.internal.DefaultTestBinaryConfig; import org.graalvm.buildtools.gradle.internal.DeprecatedNativeImageOptions; import org.graalvm.buildtools.gradle.internal.GraalVMLogger; -import org.graalvm.buildtools.gradle.internal.GradleUtils; import org.graalvm.buildtools.gradle.internal.GraalVMReachabilityMetadataService; +import org.graalvm.buildtools.gradle.internal.GradleUtils; import org.graalvm.buildtools.gradle.internal.NativeConfigurations; import org.graalvm.buildtools.gradle.internal.agent.AgentConfigurationFactory; import org.graalvm.buildtools.gradle.tasks.BuildNativeImageTask; @@ -127,9 +127,13 @@ import java.util.Objects; import java.util.Set; import java.util.function.Predicate; +import java.util.function.Supplier; import java.util.regex.Pattern; import java.util.stream.Collectors; +import static org.graalvm.buildtools.gradle.internal.ConfigurationCacheSupport.serializablePredicateOf; +import static org.graalvm.buildtools.gradle.internal.ConfigurationCacheSupport.serializableSupplierOf; +import static org.graalvm.buildtools.gradle.internal.ConfigurationCacheSupport.serializableTransformerOf; import static org.graalvm.buildtools.gradle.internal.GradleUtils.transitiveProjectArtifacts; import static org.graalvm.buildtools.gradle.internal.NativeImageExecutableLocator.graalvmHomeProvider; import static org.graalvm.buildtools.utils.SharedConstants.AGENT_PROPERTY; @@ -199,7 +203,7 @@ public void apply(@Nonnull Project project) { private void instrumentTasksWithAgent(Project project, DefaultGraalVmExtension graalExtension) { Provider agentMode = agentProperty(project, graalExtension.getAgent()); - Predicate taskPredicate = graalExtension.getAgent().getTasksToInstrumentPredicate().get(); + Predicate taskPredicate = graalExtension.getAgent().getTasksToInstrumentPredicate().getOrElse(serializablePredicateOf(t -> true)); project.getTasks().configureEach(t -> { if (isTaskInstrumentableByAgent(t) && taskPredicate.test(t)) { configureAgent(project, agentMode, graalExtension, getExecOperations(), getFileOperations(), t, (JavaForkOptions) t); @@ -565,13 +569,13 @@ private static Provider agentProperty(Project project, AgentOptions opti return project.getProviders() .gradleProperty(AGENT_PROPERTY) .forUseAtConfigurationTime() - .map(v -> { + .map(serializableTransformerOf(v -> { if (!v.isEmpty()) { return v; } return options.getDefaultMode().get(); - }) - .orElse(options.getEnabled().map(enabled -> enabled ? options.getDefaultMode().get() : "disabled")); + })) + .orElse(options.getEnabled().map(serializableTransformerOf(enabled -> enabled ? options.getDefaultMode().get() : "disabled"))); } private static void registerServiceProvider(Project project, Provider nativeImageServiceProvider) { @@ -726,18 +730,18 @@ public void execute(@Nonnull Task task) { } }); AgentCommandLineProvider cliProvider = project.getObjects().newInstance(AgentCommandLineProvider.class); - cliProvider.getInputFiles().from(agentConfiguration.map(AgentConfiguration::getAgentFiles)); - cliProvider.getEnabled().set(agentConfiguration.map(AgentConfiguration::isEnabled)); + cliProvider.getInputFiles().from(agentConfiguration.map(serializableTransformerOf(AgentConfiguration::getAgentFiles))); + cliProvider.getEnabled().set(agentConfiguration.map(serializableTransformerOf(AgentConfiguration::isEnabled))); cliProvider.getFilterableEntries().set(graalExtension.getAgent().getFilterableEntries()); cliProvider.getAgentMode().set(agentMode); Provider outputDir = AgentConfigurationFactory.getAgentOutputDirectoryForTask(project.getLayout(), taskToInstrument.getName()); - Provider isMergingEnabled = agentConfiguration.map(AgentConfiguration::isEnabled); - Provider agentModeProvider = agentConfiguration.map(AgentConfiguration::getAgentMode); - Provider> mergeOutputDirs = outputDir.map(dir -> Collections.singletonList(dir.getAsFile().getAbsolutePath())); - Provider> mergeInputDirs = outputDir.map(NativeImagePlugin::agentSessionDirectories); + Provider isMergingEnabled = agentConfiguration.map(serializableTransformerOf(AgentConfiguration::isEnabled)); + Provider agentModeProvider = agentConfiguration.map(serializableTransformerOf(AgentConfiguration::getAgentMode)); + Supplier> mergeInputDirs = serializableSupplierOf(() -> outputDir.map(serializableTransformerOf(NativeImagePlugin::agentSessionDirectories)).get()); + Supplier> mergeOutputDirs = serializableSupplierOf(() -> outputDir.map(serializableTransformerOf(dir -> Collections.singletonList(dir.getAsFile().getAbsolutePath()))).get()); cliProvider.getOutputDirectory().set(outputDir); - cliProvider.getAgentOptions().set(agentConfiguration.map(AgentConfiguration::getAgentCommandLine)); + cliProvider.getAgentOptions().set(agentConfiguration.map(serializableTransformerOf(AgentConfiguration::getAgentCommandLine))); javaForkOptions.getJvmArgumentProviders().add(cliProvider); taskToInstrument.doLast(new MergeAgentFilesAction( @@ -749,8 +753,7 @@ public void execute(@Nonnull Task task) { mergeInputDirs, mergeOutputDirs, graalExtension.getToolchainDetection(), - execOperations, - project.getLogger())); + execOperations)); taskToInstrument.doLast(new CleanupAgentFilesAction(mergeInputDirs, fileOperations)); diff --git a/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/internal/ConfigurationCacheSupport.java b/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/internal/ConfigurationCacheSupport.java new file mode 100644 index 000000000..ffc491755 --- /dev/null +++ b/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/internal/ConfigurationCacheSupport.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.graalvm.buildtools.gradle.internal; + +import org.gradle.api.Transformer; + +import java.io.Serializable; +import java.util.function.Predicate; +import java.util.function.Supplier; + +/** + * Helper class to deal with Gradle configuration cache. + */ +public class ConfigurationCacheSupport { + /** + * Generates a serializable supplier lambda. + * @param supplier the supplier + * @return a serializable supplier + * @param the type of the supplier + */ + public static Supplier serializableSupplierOf(SerializableSupplier supplier) { + return supplier; + } + + /** + * Generates a serializable predicate lambda. + * @param predicate the predicate + * @return a serializable predicate + * @param the type of the predicate + */ + public static Predicate serializablePredicateOf(SerializablePredicate predicate) { + return predicate; + } + + /** + * Generates a serializable transformer lambda. + * @param transformer the transformer + * @return a serializable transformer + * @param the output type of the transformer + * @param the input type of the transformer + */ + public static Transformer serializableTransformerOf(SerializableTransformer transformer) { + return transformer; + } + + public interface SerializableSupplier extends Supplier, Serializable { + + } + + public interface SerializablePredicate extends Predicate, Serializable { + + } + + public interface SerializableTransformer extends Transformer, Serializable { + + } + +} diff --git a/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/internal/DefaultGraalVmExtension.java b/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/internal/DefaultGraalVmExtension.java index c3655647b..2e91a9fa4 100644 --- a/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/internal/DefaultGraalVmExtension.java +++ b/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/internal/DefaultGraalVmExtension.java @@ -59,9 +59,9 @@ import java.util.Arrays; public abstract class DefaultGraalVmExtension implements GraalVMExtension { - private final NamedDomainObjectContainer nativeImages; - private final NativeImagePlugin plugin; - private final Project project; + private final transient NamedDomainObjectContainer nativeImages; + private final transient NativeImagePlugin plugin; + private final transient Project project; private final Property defaultJavaLauncher; @Inject @@ -76,7 +76,6 @@ public DefaultGraalVmExtension(NamedDomainObjectContainer na nativeImages.configureEach(options -> options.getJavaLauncher().convention(defaultJavaLauncher)); getTestSupport().convention(true); AgentOptions agentOpts = getAgent(); - agentOpts.getTasksToInstrumentPredicate().convention(t -> true); agentOpts.getDefaultMode().convention("standard"); agentOpts.getEnabled().convention(false); agentOpts.getModes().getConditional().getParallel().convention(true); diff --git a/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/internal/agent/AgentConfigurationFactory.java b/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/internal/agent/AgentConfigurationFactory.java index 277f175af..e744bb92e 100644 --- a/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/internal/agent/AgentConfigurationFactory.java +++ b/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/internal/agent/AgentConfigurationFactory.java @@ -59,11 +59,12 @@ import java.util.Collections; import java.util.stream.Collectors; +import static org.graalvm.buildtools.gradle.internal.ConfigurationCacheSupport.serializableTransformerOf; import static org.graalvm.buildtools.utils.SharedConstants.AGENT_OUTPUT_FOLDER; public class AgentConfigurationFactory { public static Provider getAgentConfiguration(Provider modeName, AgentOptions options) { - return modeName.map(name -> { + return modeName.map(serializableTransformerOf(name -> { AgentMode agentMode; ConfigurableFileCollection callerFilterFiles = options.getCallerFilterFiles(); ConfigurableFileCollection accessFilterFiles = options.getAccessFilterFiles(); @@ -95,7 +96,7 @@ public static Provider getAgentConfiguration(Provider getFilePaths(ConfigurableFileCollection configurableFileCollection) { diff --git a/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/tasks/MetadataCopyTask.java b/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/tasks/MetadataCopyTask.java index 1efc510a3..b5ec053cc 100644 --- a/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/tasks/MetadataCopyTask.java +++ b/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/tasks/MetadataCopyTask.java @@ -62,6 +62,7 @@ import java.io.File; import java.io.IOException; import java.nio.file.Files; +import java.util.ArrayList; import java.util.List; import static org.graalvm.buildtools.gradle.internal.NativeImageExecutableLocator.graalvmHomeProvider; @@ -112,7 +113,7 @@ public void overrideOutputDirectories(List outputDirectories) { @TaskAction public void exec() { StringBuilder builder = new StringBuilder(); - ListProperty inputDirectories = objectFactory.listProperty(String.class); + List inputDirectories = new ArrayList<>(); for (String taskName : getInputTaskNames().get()) { File dir = AgentConfigurationFactory.getAgentOutputDirectoryForTask(layout, taskName).get().getAsFile(); @@ -128,7 +129,7 @@ public void exec() { throw new GradleException(errorString); } - ListProperty outputDirectories = objectFactory.listProperty(String.class); + List outputDirectories = new ArrayList<>(); for (String dirName : getOutputDirectories().get()) { File dir = layout.dir(providerFactory.provider(() -> new File(dirName))).get().getAsFile(); outputDirectories.add(dir.getAbsolutePath()); @@ -155,10 +156,9 @@ public void exec() { getMergeWithExisting(), objectFactory, graalvmHomeProvider(providerFactory), - inputDirectories, - outputDirectories, + () -> inputDirectories, + () -> outputDirectories, getToolchainDetection(), - execOperations, - getLogger()).execute(this); + execOperations).execute(this); } } diff --git a/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/tasks/actions/CleanupAgentFilesAction.java b/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/tasks/actions/CleanupAgentFilesAction.java index a9dd139dd..c1d0ef03d 100644 --- a/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/tasks/actions/CleanupAgentFilesAction.java +++ b/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/tasks/actions/CleanupAgentFilesAction.java @@ -43,16 +43,16 @@ import org.gradle.api.Action; import org.gradle.api.Task; import org.gradle.api.file.FileSystemOperations; -import org.gradle.api.provider.Provider; import java.util.List; +import java.util.function.Supplier; public class CleanupAgentFilesAction implements Action { - private final Provider> directoriesToCleanup; + private final Supplier> directoriesToCleanup; private final FileSystemOperations fileOperations; - public CleanupAgentFilesAction(Provider> directoriesToCleanup, FileSystemOperations fileOperations) { + public CleanupAgentFilesAction(Supplier> directoriesToCleanup, FileSystemOperations fileOperations) { this.directoriesToCleanup = directoriesToCleanup; this.fileOperations = fileOperations; } diff --git a/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/tasks/actions/MergeAgentFilesAction.java b/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/tasks/actions/MergeAgentFilesAction.java index 4a4e43959..0a049a741 100644 --- a/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/tasks/actions/MergeAgentFilesAction.java +++ b/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/tasks/actions/MergeAgentFilesAction.java @@ -45,7 +45,6 @@ import org.graalvm.buildtools.utils.NativeImageUtils; import org.gradle.api.Action; import org.gradle.api.Task; -import org.gradle.api.logging.Logger; import org.gradle.api.model.ObjectFactory; import org.gradle.api.provider.Property; import org.gradle.api.provider.Provider; @@ -58,6 +57,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.function.Supplier; import static org.graalvm.buildtools.gradle.internal.NativeImageExecutableLocator.findNativeImageExecutable; import static org.graalvm.buildtools.utils.NativeImageUtils.nativeImageConfigureFileName; @@ -67,23 +67,21 @@ public class MergeAgentFilesAction implements Action { private final Provider agentMode; private final Provider mergeWithOutputs; private final Provider graalvmHomeProvider; - private final Provider> inputDirs; - private final Provider> outputDirs; + private final Supplier> inputDirs; + private final Supplier> outputDirs; private final Provider disableToolchainDetection; private final Property noLauncherProperty; private final ExecOperations execOperations; - private final Logger logger; public MergeAgentFilesAction(Provider isMergingEnabled, Provider agentMode, Provider mergeWithOutputs, ObjectFactory objectFactory, Provider graalvmHomeProvider, - Provider> inputDirs, - Provider> outputDirs, + Supplier> inputDirs, + Supplier> outputDirs, Provider disableToolchainDetection, - ExecOperations execOperations, - Logger logger) { + ExecOperations execOperations) { this.isMergingEnabled = isMergingEnabled; this.agentMode = agentMode; this.mergeWithOutputs = mergeWithOutputs; @@ -92,7 +90,6 @@ public MergeAgentFilesAction(Provider isMergingEnabled, this.outputDirs = outputDirs; this.disableToolchainDetection = disableToolchainDetection; this.execOperations = execOperations; - this.logger = logger; this.noLauncherProperty = objectFactory.property(JavaLauncher.class); } @@ -103,11 +100,11 @@ private static boolean isConfigDir(String dir) { @Override public void execute(Task task) { if (isMergingEnabled.get()) { - File nativeImage = findNativeImageExecutable(noLauncherProperty, disableToolchainDetection, graalvmHomeProvider, execOperations, GraalVMLogger.of(logger)); + File nativeImage = findNativeImageExecutable(noLauncherProperty, disableToolchainDetection, graalvmHomeProvider, execOperations, GraalVMLogger.of(task.getLogger())); File workingDir = nativeImage.getParentFile(); File launcher = new File(workingDir, nativeImageConfigureFileName()); if (!launcher.exists()) { - logger.info("Installing native-image-configure"); + task.getLogger().info("Installing native-image-configure"); execOperations.exec(spec -> { spec.executable(nativeImage); spec.args("--macro:native-image-configure-launcher"); @@ -136,7 +133,7 @@ public void execute(Task task) { mergeAgentFiles(launcher, inputDirs.get(), outputDirs.get()); } } else { - logger.warn("Cannot merge agent files because native-image-configure is not installed. Please upgrade to a newer version of GraalVM."); + task.getLogger().warn("Cannot merge agent files because native-image-configure is not installed. Please upgrade to a newer version of GraalVM."); } } } diff --git a/native-gradle-plugin/src/testFixtures/groovy/org/graalvm/buildtools/gradle/fixtures/AbstractFunctionalTest.groovy b/native-gradle-plugin/src/testFixtures/groovy/org/graalvm/buildtools/gradle/fixtures/AbstractFunctionalTest.groovy index a6a4f2e8f..e580a9774 100644 --- a/native-gradle-plugin/src/testFixtures/groovy/org/graalvm/buildtools/gradle/fixtures/AbstractFunctionalTest.groovy +++ b/native-gradle-plugin/src/testFixtures/groovy/org/graalvm/buildtools/gradle/fixtures/AbstractFunctionalTest.groovy @@ -210,7 +210,8 @@ abstract class AbstractFunctionalTest extends Specification { private void assertInitScript() { initScript = file("init.gradle") - initScript << """ + if (!initScript.exists()) { + initScript << """ allprojects { repositories { maven { @@ -220,6 +221,7 @@ abstract class AbstractFunctionalTest extends Specification { } } """ + } } private class TaskExecutionGraph {