diff --git a/build-logic/aggregator/src/main/kotlin/org/graalvm/build/tasks/GitAdd.kt b/build-logic/aggregator/src/main/kotlin/org/graalvm/build/tasks/GitAdd.kt index 192c40449..f6dd0ec84 100644 --- a/build-logic/aggregator/src/main/kotlin/org/graalvm/build/tasks/GitAdd.kt +++ b/build-logic/aggregator/src/main/kotlin/org/graalvm/build/tasks/GitAdd.kt @@ -42,12 +42,8 @@ package org.graalvm.build.tasks import org.eclipse.jgit.api.Git -import org.gradle.api.DefaultTask -import org.gradle.api.file.DirectoryProperty import org.gradle.api.provider.Property import org.gradle.api.tasks.Input -import org.gradle.api.tasks.InputDirectory -import org.gradle.api.tasks.OutputDirectory import org.gradle.api.tasks.TaskAction abstract class GitAdd : AbstractGitTask() { diff --git a/build-logic/aggregator/src/main/kotlin/org/graalvm/build/tasks/GitClone.kt b/build-logic/aggregator/src/main/kotlin/org/graalvm/build/tasks/GitClone.kt index b4a0ac23d..6d0616da0 100644 --- a/build-logic/aggregator/src/main/kotlin/org/graalvm/build/tasks/GitClone.kt +++ b/build-logic/aggregator/src/main/kotlin/org/graalvm/build/tasks/GitClone.kt @@ -42,9 +42,7 @@ package org.graalvm.build.tasks import org.eclipse.jgit.api.Git -import org.eclipse.jgit.api.TransportConfigCallback import org.eclipse.jgit.transport.SshTransport -import org.eclipse.jgit.transport.Transport import org.eclipse.jgit.util.FileUtils import org.gradle.api.file.DirectoryProperty import org.gradle.api.provider.Property diff --git a/build-logic/aggregator/src/main/kotlin/org/graalvm/build/tasks/GitCommit.kt b/build-logic/aggregator/src/main/kotlin/org/graalvm/build/tasks/GitCommit.kt index 04016c7cb..776cf7f6b 100644 --- a/build-logic/aggregator/src/main/kotlin/org/graalvm/build/tasks/GitCommit.kt +++ b/build-logic/aggregator/src/main/kotlin/org/graalvm/build/tasks/GitCommit.kt @@ -42,12 +42,8 @@ package org.graalvm.build.tasks import org.eclipse.jgit.api.Git -import org.gradle.api.DefaultTask -import org.gradle.api.file.DirectoryProperty import org.gradle.api.provider.Property import org.gradle.api.tasks.Input -import org.gradle.api.tasks.InputDirectory -import org.gradle.api.tasks.OutputDirectory import org.gradle.api.tasks.TaskAction abstract class GitCommit : AbstractGitTask() { diff --git a/build-logic/aggregator/src/main/kotlin/org/graalvm/build/tasks/GitPush.kt b/build-logic/aggregator/src/main/kotlin/org/graalvm/build/tasks/GitPush.kt index 3a14c4fe7..d57630eac 100644 --- a/build-logic/aggregator/src/main/kotlin/org/graalvm/build/tasks/GitPush.kt +++ b/build-logic/aggregator/src/main/kotlin/org/graalvm/build/tasks/GitPush.kt @@ -43,12 +43,8 @@ package org.graalvm.build.tasks import org.eclipse.jgit.api.Git import org.eclipse.jgit.transport.SshTransport -import org.gradle.api.DefaultTask -import org.gradle.api.file.DirectoryProperty import org.gradle.api.provider.Property import org.gradle.api.tasks.Input -import org.gradle.api.tasks.InputDirectory -import org.gradle.api.tasks.OutputDirectory import org.gradle.api.tasks.TaskAction abstract class GitPush : AbstractGitTask() { diff --git a/build-logic/aggregator/src/main/kotlin/org/graalvm/build/tasks/GitReset.kt b/build-logic/aggregator/src/main/kotlin/org/graalvm/build/tasks/GitReset.kt index b8f39f23f..0dd59a3e6 100644 --- a/build-logic/aggregator/src/main/kotlin/org/graalvm/build/tasks/GitReset.kt +++ b/build-logic/aggregator/src/main/kotlin/org/graalvm/build/tasks/GitReset.kt @@ -43,12 +43,8 @@ package org.graalvm.build.tasks import org.eclipse.jgit.api.Git import org.eclipse.jgit.api.ResetCommand -import org.gradle.api.DefaultTask -import org.gradle.api.file.DirectoryProperty import org.gradle.api.provider.Property import org.gradle.api.tasks.Input -import org.gradle.api.tasks.InputDirectory -import org.gradle.api.tasks.OutputDirectory import org.gradle.api.tasks.TaskAction abstract class GitReset : AbstractGitTask() { diff --git a/common/utils/src/main/java/org/graalvm/buildtools/agent/AgentConfiguration.java b/common/utils/src/main/java/org/graalvm/buildtools/agent/AgentConfiguration.java new file mode 100644 index 000000000..7e3dfe404 --- /dev/null +++ b/common/utils/src/main/java/org/graalvm/buildtools/agent/AgentConfiguration.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2022, 2022 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.agent; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +public class AgentConfiguration implements Serializable { + + private final Collection callerFilterFiles; + private final Collection accessFilterFiles; + private final boolean builtinCallerFilter; + private final boolean builtinHeuristicFilter; + private final boolean experimentalPredefinedClasses; + private final boolean experimentalUnsafeAllocationTracing; + private final boolean trackReflectionMetadata; + + private final AgentMode agentMode; + + public AgentConfiguration(Collection callerFilterFiles, + Collection accessFilterFiles, + boolean builtinCallerFilter, + boolean builtinHeuristicFilter, + boolean experimentalPredefinedClasses, + boolean experimentalUnsafeAllocationTracing, + boolean trackReflectionMetadata, + AgentMode agentMode) { + this.callerFilterFiles = callerFilterFiles; + this.accessFilterFiles = accessFilterFiles; + this.builtinCallerFilter = builtinCallerFilter; + this.builtinHeuristicFilter = builtinHeuristicFilter; + this.experimentalPredefinedClasses = experimentalPredefinedClasses; + this.experimentalUnsafeAllocationTracing = experimentalUnsafeAllocationTracing; + this.trackReflectionMetadata = trackReflectionMetadata; + this.agentMode = agentMode; + } + + public List getAgentCommandLine() { + List cmdLine = new ArrayList<>(agentMode.getAgentCommandLine()); + appendOptionToValues("caller-filter-file=", callerFilterFiles, cmdLine); + appendOptionToValues("access-filter-file=", accessFilterFiles, cmdLine); + cmdLine.add("builtin-caller-filter=" + builtinCallerFilter); + cmdLine.add("builtin-heuristic-filter=" + builtinHeuristicFilter); + cmdLine.add("experimental-class-define-support=" + experimentalPredefinedClasses); + cmdLine.add("experimental-unsafe-allocation-support=" + experimentalUnsafeAllocationTracing); + cmdLine.add("track-reflection-metadata=" + trackReflectionMetadata); + return cmdLine; + } + + public Collection getAgentFiles() { + List files = new ArrayList<>(callerFilterFiles.size() + accessFilterFiles.size()); + files.addAll(callerFilterFiles); + files.addAll(accessFilterFiles); + files.addAll(agentMode.getInputFiles()); + return files; + } + + public boolean isEnabled() { + return !(agentMode instanceof DisabledAgentMode); + } + + public static void appendOptionToValues(String option, Collection values, Collection target) { + values.stream().map(value -> option + value).forEach(target::add); + } + + public AgentMode getAgentMode() { + return agentMode; + } +} diff --git a/common/utils/src/main/java/org/graalvm/buildtools/agent/AgentMode.java b/common/utils/src/main/java/org/graalvm/buildtools/agent/AgentMode.java new file mode 100644 index 000000000..6c9fafae4 --- /dev/null +++ b/common/utils/src/main/java/org/graalvm/buildtools/agent/AgentMode.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2022, 2022 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.agent; + +import java.io.Serializable; +import java.util.List; + +public interface AgentMode extends Serializable { + List getAgentCommandLine(); + + List getNativeImageConfigureOptions(List inputDirectories, List outputDirectories); + + List getInputFiles(); +} diff --git a/common/utils/src/main/java/org/graalvm/buildtools/agent/ConditionalAgentMode.java b/common/utils/src/main/java/org/graalvm/buildtools/agent/ConditionalAgentMode.java new file mode 100644 index 000000000..2dae05fa0 --- /dev/null +++ b/common/utils/src/main/java/org/graalvm/buildtools/agent/ConditionalAgentMode.java @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2022, 2022 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.agent; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import static org.graalvm.buildtools.utils.SharedConstants.AGENT_OUTPUT_DIRECTORY_MARKER; +import static org.graalvm.buildtools.utils.SharedConstants.AGENT_OUTPUT_DIRECTORY_OPTION; + +public class ConditionalAgentMode implements AgentMode { + + private final String userCodeFilterPath; + private final String extraFilterPath; + private final boolean parallel; + + public ConditionalAgentMode(String userCodeFilterPath, String extraFilterPath, boolean parallel) { + this.userCodeFilterPath = userCodeFilterPath; + this.extraFilterPath = extraFilterPath; + this.parallel = parallel; + } + + @Override + public List getAgentCommandLine() { + List cmdLine = new ArrayList<>(); + cmdLine.add(AGENT_OUTPUT_DIRECTORY_OPTION + AGENT_OUTPUT_DIRECTORY_MARKER); + if (parallel) { + cmdLine.add("experimental-conditional-config-part"); + } else { + cmdLine.add("experimental-conditional-config-filter-file=" + userCodeFilterPath); + if (!extraFilterPath.isEmpty()) { + cmdLine.add("conditional-config-class-filter-file=" + extraFilterPath); + } + } + return cmdLine; + } + + @Override + public List getNativeImageConfigureOptions(List inputDirectories, List outputDirectories) { + int inputDirCount = inputDirectories.size(); + int outputDirCount = outputDirectories.size(); + if (inputDirCount > 0 && outputDirCount > 0) { + List cmdLine = new ArrayList<>(inputDirCount + outputDirCount + 3); + if (parallel) { + cmdLine.add("generate-conditional"); + cmdLine.add("--user-code-filter=" + userCodeFilterPath); + if (!extraFilterPath.isEmpty()) { + cmdLine.add("--class-name-filter=" + extraFilterPath); + } + } else { + cmdLine.add("generate"); + } + AgentConfiguration.appendOptionToValues("--input-dir=", inputDirectories, cmdLine); + AgentConfiguration.appendOptionToValues("--output-dir=", outputDirectories, cmdLine); + return cmdLine; + } + return Collections.emptyList(); + } + + @Override + public List getInputFiles() { + List files = new ArrayList<>(); + files.add(userCodeFilterPath); + if (!extraFilterPath.isEmpty()) { + files.add(extraFilterPath); + } + return files; + } +} diff --git a/common/utils/src/main/java/org/graalvm/buildtools/agent/DirectAgentMode.java b/common/utils/src/main/java/org/graalvm/buildtools/agent/DirectAgentMode.java new file mode 100644 index 000000000..f6a4dc573 --- /dev/null +++ b/common/utils/src/main/java/org/graalvm/buildtools/agent/DirectAgentMode.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2022, 2022 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.agent; + +import java.util.Collections; +import java.util.List; + +public class DirectAgentMode implements AgentMode{ + private final List agentCmdLine; + + public DirectAgentMode(List agentCmdLine) { + this.agentCmdLine = agentCmdLine; + } + + @Override + public List getAgentCommandLine() { + return agentCmdLine; + } + + @Override + public List getNativeImageConfigureOptions(List inputDirectories, List outputDirectories) { + return Collections.emptyList(); + } + + @Override + public List getInputFiles() { + return Collections.emptyList(); + } +} diff --git a/common/utils/src/main/java/org/graalvm/buildtools/agent/DisabledAgentMode.java b/common/utils/src/main/java/org/graalvm/buildtools/agent/DisabledAgentMode.java new file mode 100644 index 000000000..78bc79061 --- /dev/null +++ b/common/utils/src/main/java/org/graalvm/buildtools/agent/DisabledAgentMode.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2022, 2022 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.agent; + +import java.util.Collections; +import java.util.List; + +public class DisabledAgentMode implements AgentMode { + @Override + public List getAgentCommandLine() { + return Collections.emptyList(); + } + + @Override + public List getNativeImageConfigureOptions(List inputDirectories, List outputDirectories) { + return Collections.emptyList(); + } + + @Override + public List getInputFiles() { + return Collections.emptyList(); + } +} diff --git a/common/utils/src/main/java/org/graalvm/buildtools/agent/StandardAgentMode.java b/common/utils/src/main/java/org/graalvm/buildtools/agent/StandardAgentMode.java new file mode 100644 index 000000000..fc63e6ae6 --- /dev/null +++ b/common/utils/src/main/java/org/graalvm/buildtools/agent/StandardAgentMode.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2022, 2022 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.agent; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import static org.graalvm.buildtools.utils.SharedConstants.AGENT_OUTPUT_DIRECTORY_MARKER; +import static org.graalvm.buildtools.utils.SharedConstants.AGENT_OUTPUT_DIRECTORY_OPTION; + +public class StandardAgentMode implements AgentMode { + @Override + public List getAgentCommandLine() { + return Collections.singletonList(AGENT_OUTPUT_DIRECTORY_OPTION + AGENT_OUTPUT_DIRECTORY_MARKER); + } + + @Override + public List getNativeImageConfigureOptions(List inputDirectories, List outputDirectories) { + int inputDirCount = inputDirectories.size(); + int outputDirCount = outputDirectories.size(); + if (inputDirCount > 0 && outputDirCount > 0) { + List cmdLine = new ArrayList<>(inputDirCount + outputDirectories.size() + 1); + cmdLine.add("generate"); + AgentConfiguration.appendOptionToValues("--input-dir=", inputDirectories, cmdLine); + AgentConfiguration.appendOptionToValues("--output-dir=", outputDirectories, cmdLine); + return cmdLine; + } + return Collections.emptyList(); + } + + @Override + public List getInputFiles() { + return Collections.emptyList(); + } +} diff --git a/common/utils/src/main/java/org/graalvm/buildtools/utils/SharedConstants.java b/common/utils/src/main/java/org/graalvm/buildtools/utils/SharedConstants.java index 31ddf9963..2911e96f0 100644 --- a/common/utils/src/main/java/org/graalvm/buildtools/utils/SharedConstants.java +++ b/common/utils/src/main/java/org/graalvm/buildtools/utils/SharedConstants.java @@ -70,4 +70,6 @@ public interface SharedConstants { ".*/package.html" )); String AGENT_SESSION_SUBDIR = "session-{pid}-{datetime}"; + String AGENT_OUTPUT_DIRECTORY_MARKER = "{output_dir}"; + String AGENT_OUTPUT_DIRECTORY_OPTION = "config-output-dir="; } diff --git a/docs/src/docs/asciidoc/gradle-plugin.adoc b/docs/src/docs/asciidoc/gradle-plugin.adoc index d864ea551..65e3f0954 100644 --- a/docs/src/docs/asciidoc/gradle-plugin.adoc +++ b/docs/src/docs/asciidoc/gradle-plugin.adoc @@ -285,24 +285,33 @@ The same mechanism can be used if you have multiple test tasks for a single test If your project requires reflection, classpath resources, dynamic proxies or other features requiring explicit native configuration, it may prove helpful to first run your application or tests using the https://www.graalvm.org/reference-manual/native-image/Agent/[`native-image-agent`]. -The Native Image Gradle plugin simplifies generation of the required configuration files by injecting the agent automatically for you (this includes, but is not limited to the reflection file). +The Native Image Gradle plugin simplifies generation of the required metadata files by injecting the agent automatically for you (this includes, but is not limited to the reflection file). -This should be as easy as appending `-Pagent` to the `run` and `nativeBuild`, or `test` and `nativeTest` task invocations: +Any task that extends `JavaForkOptions` (like `test`, `run` etc) can be instrumented by passing `-Pagent` to gradle when running said tasks. + +The agent can run in multiple modes that dictate how the metadata is collected and merged. + +Once the metadata is collected, it can be copied into the project using the `metadataCopy` task. [source,bash] ---- ./gradlew -Pagent run # Runs on JVM with native-image-agent. -./gradlew -Pagent nativeCompile # Builds image using configuration acquired by agent. +./gradlew metadataCopy --task run --dir src/main/resources/META-INF/native-image # Copies the metadata collected by the agent into the project sources +./gradlew nativeCompile # Builds image using metadata acquired by the agent. # For testing -./gradlew -Pagent test # Runs on JVM with native-image-agent. -./gradlew -Pagent nativeTest # Builds image using configuration acquired by agent. +./gradlew -Pagent nativeTest # Runs on JVM with the native-image agent, collects the metadata and uses it for testing on native-image. ---- -The agent can also be enabled by setting the corresponding DSL flag; however, that is not recommended since this is a development mode feature only. +The agent can run in multiple modes: +* Standard - Collects metadata without conditions. This is recommended if you are building an executable. +* Conditional - Collects metadata with conditions. This is recommended if you are creating conditional metadata for a library intended for further use. +* Direct - For advanced users only. This mode allows directly controlling the command line passed to the agent. + +The default mode is specified in the DSL but can be changed by passing the mode name to Gradle when using the agent: `-Pagent=conditional` The generated configuration files will be found in the `${buildDir}/native/agent-output/${taskName}` directory, for example, `build/native/agent-output/run`. -Although those files will be automatically used if you run your build with the agent enabled, you should consider reviewing them and adding them to your sources instead. +The plugin will also substitute `{output_dir}` in the agent options to point to this directory during the agent run. [[agent-support-configuring-options]] === Configuring agent options @@ -310,7 +319,7 @@ Although those files will be automatically used if you run your build with the a The native agent can be configured https://www.graalvm.org/reference-manual/native-image/Agent/[with additional options]. This can be done using the `agent` configuration block: -.Configuring agent options for every binary +.Configuring agent options [source, groovy, role="multi-language-sample"] ---- include::../snippets/gradle/groovy/build.gradle[tags=add-agent-options] @@ -321,20 +330,6 @@ include::../snippets/gradle/groovy/build.gradle[tags=add-agent-options] include::../snippets/gradle/kotlin/build.gradle.kts[tags=add-agent-options] ---- -You may also define distinct agent options for different images. -In the following example, the agent used for instrumentation for the main image has distinct options from the test ones: - -.Configuring agent options specifically -[source, groovy, role="multi-language-sample"] ----- -include::../snippets/gradle/groovy/build.gradle[tags=add-agent-options-individual] ----- - -[source, kotlin, role="multi-language-sample"] ----- -include::../snippets/gradle/kotlin/build.gradle.kts[tags=add-agent-options-individual] ----- - [[metadata-support]] == JVM Reachability Metadata support diff --git a/docs/src/docs/asciidoc/index.adoc b/docs/src/docs/asciidoc/index.adoc index d25f3bdb0..091352487 100644 --- a/docs/src/docs/asciidoc/index.adoc +++ b/docs/src/docs/asciidoc/index.adoc @@ -14,6 +14,16 @@ If you are interested in contributing, please refer to our https://github.com/gr [[changelog]] == Changelog +=== Release 0.9.12 + +==== Gradle plugin +* Completely reworked agent support - **BREAKING CHANGE** +* The agent block is no longer tied to the target binary. +* The agent can now instrument any task that extends `JavaForkOptions`. +* Introduced the `metadataCopy` task. +* Introduced the concept of agent modes. +** Under the hood, the agent mode dictates what options are passed to the agent and how metadata produced by multiple runs get merged. + === Release 0.9.11 ==== Maven plugin diff --git a/docs/src/docs/snippets/gradle/groovy/build.gradle b/docs/src/docs/snippets/gradle/groovy/build.gradle index 91c9f9fbd..b83d64ad0 100644 --- a/docs/src/docs/snippets/gradle/groovy/build.gradle +++ b/docs/src/docs/snippets/gradle/groovy/build.gradle @@ -67,6 +67,49 @@ if (providers.environmentVariable("DISABLE_TOOLCHAIN").isPresent()) { // tag::all-config-options[] graalvmNative { + // Injects the native-image-agent into supported tasks if `-Pagent` is specified + agent { + defaultMode = "standard" // Default agent mode if one isn't specified using `-Pagent=mode_name` + + modes { + // The standard agent mode generates metadata without conditions. + standard { + } + // The conditional agent mode generates metadata with conditions. + conditional { + userCodeFilterPath = "path-to-filter.json" // Path to a filter file that determines classes which will be used in the metadata conditions. + extrFilterPath = "path-to-another-filter.json" // Optional, extra filter used to further filter the collected metadata. + } + // The direct agent mode allows users to directly pass options to the agent. + direct { + // {output_dir} is a special string expanded by the plugin to where the agent files would usually be output. + options.add("config-output-dir={output_dir}") + options.add("experimental-configuration-with-origins") + } + } + + callerFilterFiles.from("filter.json") + accessFilterFiles.from("filter.json") + builtinCallerFilter = true + builtinHeuristicFilter = true + enableExperimentalPredefinedClasses = false + enableExperimentalUnsafeAllocationTracing = false + trackReflectionMetadata = true + + // Copies metadata collected from tasks into the specified directories. + metadataCopy { + inputTaskNames.add("test") // Tasks previously executed with the agent attached. + outputDirectories.add("src/main/resources/META-INF/native-image") + mergeWithExisting = true // Instead of copying, merge with existing metadata in the output directories. + } + + /* + By default, if `-Pagent` is specified, all tasks that extend JavaForkOptions are instrumented. + This can be limited to only specific tasks that match this predicate. + */ + tasksToInstrumentPredicate = t -> true + } + binaries { main { // Main options @@ -87,11 +130,6 @@ graalvmNative { // Runtime options runtimeArgs.add('--help') // Passes '--help' to built image, during "nativeRun" task - // Development options - agent { - enabled = true // Enables the reflection agent. Can be also set on command line using '-Pagent' - } - useFatJar = true // Instead of passing each jar individually, builds a fat jar } } diff --git a/docs/src/docs/snippets/gradle/kotlin/build.gradle.kts b/docs/src/docs/snippets/gradle/kotlin/build.gradle.kts index 78db3e99a..70380760d 100644 --- a/docs/src/docs/snippets/gradle/kotlin/build.gradle.kts +++ b/docs/src/docs/snippets/gradle/kotlin/build.gradle.kts @@ -88,11 +88,6 @@ graalvmNative { // Runtime options runtimeArgs.add("--help") // Passes '--help' to built image, during "nativeRun" task - // Development options - agent { - enabled.set(true) // Enables the reflection agent. Can be also set on command line using '-Pagent' - } - useFatJar.set(true) // Instead of passing each jar individually, builds a fat jar } } @@ -138,31 +133,12 @@ graalvmNative { // tag::add-agent-options[] graalvmNative { - binaries.configureEach { - agent { - options.add("experimental-class-loader-support") - } + agent { + enableExperimentalPredefinedClasses = true } } // end::add-agent-options[] -// tag::add-agent-options-individual[] -graalvmNative { - binaries { - named("main") { - agent { - options.add("experimental-class-loader-support") - } - } - named("test") { - agent { - options.add("access-filter-file=${projectDir}/src/test/resources/access-filter.json") - } - } - } -} -// end::add-agent-options-individual[] - // tag::enable-metadata-repository[] graalvmNative { metadataRepository { 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 d06a46156..e98659f09 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 @@ -47,19 +47,33 @@ import spock.lang.Unroll class JavaApplicationWithAgentFunctionalTest extends AbstractFunctionalTest { - @Unroll("agent is passed and generates resources files with JUnit Platform #junitVersion") + @Unroll("agent is not passed and the application fails with JUnit Platform #junitVersion") + def "agent is not passed"() { + given: + withSample("java-application-with-reflection") + + when: + fails 'nativeTest' + + then: + outputContains "expected: but was: " + + where: + junitVersion = System.getProperty('versions.junit') + } + + @Unroll("agent is passed, generates metadata files and copies metadata with JUnit Platform #junitVersion") def "agent is passed"() { debug = true given: withSample("java-application-with-reflection") when: - run 'nativeTest' + run 'nativeTest', '-Pagent' then: tasks { succeeded ':jar', - ':filterAgentTestResources', ':nativeTest' doesNotContain ':build' } @@ -85,6 +99,15 @@ class JavaApplicationWithAgentFunctionalTest extends AbstractFunctionalTest { assert file("build/native/agent-output/test/${name}-config.json").exists() } + when: + run 'metadataCopy' + + then: + ['jni', 'proxy', 'reflect', 'resource', 'serialization'].each { name -> + assert file("build/native/metadataCopyTest/${name}-config.json").exists() + } + + where: junitVersion = System.getProperty('versions.junit') } @@ -95,24 +118,29 @@ class JavaApplicationWithAgentFunctionalTest extends AbstractFunctionalTest { withSample("java-application-with-reflection") when: - fails 'nativeTest', '-Pagent=false' + run 'nativeTest', '-Pagent=conditional' then: - outputContains """org.graalvm.demo.ApplicationTest > message is hello native FAILED""" + tasks { + succeeded ':nativeTest' + } + + and: + assert file("build/native/agent-output/test/reflect-config.json").text.contains("\"condition\"") where: junitVersion = System.getProperty('versions.junit') } - @Issue("https://github.com/graalvm/native-build-tools/issues/134") - @Unroll("generated agent files are added when building native image with JUnit Platform #junitVersion") - def "generated agent files are used when building native image"() { + @Unroll("agent instruments run task, metadata is copied and merged, and the app runs JUnit Platform #junitVersion") + def "agent instruments run task"() { debug = true + var metadata_dir = 'src/main/resources/META-INF/native-image' given: withSample("java-application-with-reflection") when: - run '-Pagent=true', 'run' + run 'run', '-Pagent=standard' then: tasks { @@ -126,55 +154,23 @@ class JavaApplicationWithAgentFunctionalTest extends AbstractFunctionalTest { } when: - run '-Pagent=true', 'nativeRun' - - then: - outputContains "Application message: Hello, native!" - - where: - junitVersion = System.getProperty('versions.junit') - } - - def "can configure extra options to the agent"() { - debug = true - given: - withSample("java-application-with-reflection") - - when: - fails 'nativeTest', '-DagentOptions=will-fail' + run'metadataCopy', '--task', 'run', '--dir', metadata_dir then: - errorOutputContains "native-image-agent: unknown option: 'will-fail'." - - where: - junitVersion = System.getProperty('versions.junit') - } - - def "reasonable error message if the user provides themselves an output directory"() { - debug = true - given: - withSample("java-application-with-reflection") - - when: - fails 'nativeTest', '-DagentOptions=config-output-dir' - - then: - errorOutputContains "config-output-dir cannot be supplied as an agent option" - - where: - junitVersion = System.getProperty('versions.junit') - } + ['jni', 'proxy', 'reflect', 'resource', 'serialization'].each { name -> + assert file("${metadata_dir}/${name}-config.json").exists() + } - def "reasonable error message if the user provides themselves an output directory value"() { - debug = true - given: - withSample("java-application-with-reflection") + and: + var reflect_config = file("${metadata_dir}/reflect-config.json") + var reflect_config_contents = reflect_config.text + assert reflect_config_contents.contains("DummyClass") && reflect_config_contents.contains("org.graalvm.demo.Message") when: - fails 'nativeTest', '-DagentOptions=config-output-dir=nope!' + run 'nativeRun' then: - errorOutputContains "config-output-dir cannot be supplied as an agent option" + outputContains "Application message: Hello, native!" 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 e35e67179..d70ed079b 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 @@ -42,10 +42,12 @@ package org.graalvm.buildtools.gradle; import org.graalvm.buildtools.VersionInfo; -import org.graalvm.buildtools.gradle.dsl.AgentConfiguration; +import org.graalvm.buildtools.agent.AgentConfiguration; +import org.graalvm.buildtools.agent.AgentMode; import org.graalvm.buildtools.gradle.dsl.GraalVMExtension; import org.graalvm.buildtools.gradle.dsl.JvmReachabilityMetadataRepositoryExtension; import org.graalvm.buildtools.gradle.dsl.NativeImageOptions; +import org.graalvm.buildtools.gradle.dsl.agent.AgentOptions; import org.graalvm.buildtools.gradle.internal.AgentCommandLineProvider; import org.graalvm.buildtools.gradle.internal.BaseNativeImageOptions; import org.graalvm.buildtools.gradle.internal.DefaultGraalVmExtension; @@ -55,10 +57,14 @@ import org.graalvm.buildtools.gradle.internal.GradleUtils; import org.graalvm.buildtools.gradle.internal.JvmReachabilityMetadataService; import org.graalvm.buildtools.gradle.internal.NativeConfigurations; -import org.graalvm.buildtools.gradle.internal.ProcessGeneratedGraalResourceFiles; +import org.graalvm.buildtools.gradle.internal.agent.AgentConfigurationFactory; import org.graalvm.buildtools.gradle.tasks.BuildNativeImageTask; import org.graalvm.buildtools.gradle.tasks.GenerateResourcesConfigFile; +import org.graalvm.buildtools.gradle.tasks.MetadataCopyTask; import org.graalvm.buildtools.gradle.tasks.NativeRunTask; +import org.graalvm.buildtools.gradle.tasks.actions.CleanupAgentFilesAction; +import org.graalvm.buildtools.gradle.tasks.actions.MergeAgentFilesAction; +import org.graalvm.buildtools.gradle.tasks.actions.ProcessGeneratedGraalResourceFilesAction; import org.graalvm.buildtools.utils.SharedConstants; import org.gradle.api.Action; import org.gradle.api.NamedDomainObjectContainer; @@ -80,15 +86,14 @@ import org.gradle.api.file.FileSystemOperations; import org.gradle.api.logging.LogLevel; import org.gradle.api.model.ObjectFactory; -import org.gradle.api.plugins.ApplicationPlugin; import org.gradle.api.plugins.ExtensionAware; import org.gradle.api.plugins.JavaApplication; +import org.gradle.api.plugins.JavaLibraryPlugin; import org.gradle.api.plugins.JavaPlugin; import org.gradle.api.plugins.JavaPluginConvention; import org.gradle.api.provider.ListProperty; import org.gradle.api.provider.Property; import org.gradle.api.provider.Provider; -import org.gradle.api.tasks.JavaExec; import org.gradle.api.tasks.OutputDirectory; import org.gradle.api.tasks.SourceSet; import org.gradle.api.tasks.SourceSetContainer; @@ -111,32 +116,25 @@ import java.nio.file.Path; import java.util.Arrays; import java.util.Collections; +import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; -import java.util.concurrent.Callable; -import java.util.function.Consumer; +import java.util.function.Predicate; import java.util.stream.Collectors; 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_OUTPUT_FOLDER; import static org.graalvm.buildtools.utils.SharedConstants.AGENT_PROPERTY; /** * Gradle plugin for GraalVM Native Image. */ -@SuppressWarnings("unused") public class NativeImagePlugin implements Plugin { public static final String NATIVE_COMPILE_TASK_NAME = "nativeCompile"; public static final String NATIVE_TEST_COMPILE_TASK_NAME = "nativeTestCompile"; public static final String NATIVE_TEST_TASK_NAME = "nativeTest"; - public static final String NATIVE_TEST_EXTENSION = "test"; public static final String NATIVE_MAIN_EXTENSION = "main"; - public static final String PROCESS_AGENT_RESOURCES_TASK_NAME_PREFIX = "filterAgent"; - public static final String PROCESS_AGENT_RESOURCES_TASK_NAME_SUFFIX = "Resources"; - public static final String GENERATE_RESOURCES_CONFIG_FILE_TASK_NAME = "generateResourcesConfigFile"; - public static final String GENERATE_TEST_RESOURCES_CONFIG_FILE_TASK_NAME = "generateTestResourcesConfigFile"; public static final String DEPRECATED_NATIVE_BUILD_EXTENSION = "nativeBuild"; public static final String DEPRECATED_NATIVE_TEST_EXTENSION = "nativeTest"; @@ -145,14 +143,6 @@ public class NativeImagePlugin implements Plugin { public static final String CONFIG_REPO_LOGLEVEL = "org.graalvm.internal.gradle.configrepo.logging"; - /** - * This looks strange, but it is used to force the configuration of a dependent - * task during the configuration of another one. This is a workaround for a bug - * when applying the Kotlin plugin, where the test task is configured too late - * for some reason. - */ - private static final Consumer FORCE_CONFIG = t -> { - }; private static final String JUNIT_PLATFORM_LISTENERS_UID_TRACKING_ENABLED = "junit.platform.listeners.uid.tracking.enabled"; private static final String JUNIT_PLATFORM_LISTENERS_UID_TRACKING_OUTPUT_DIR = "junit.platform.listeners.uid.tracking.output.dir"; @@ -183,17 +173,34 @@ public void apply(Project project) { graalExtension.getUseArgFile().convention(true); project.getPlugins() .withType(JavaPlugin.class, javaPlugin -> configureJavaProject(project, nativeImageServiceProvider, graalExtension)); - project.afterEvaluate(p -> { - Map> agents = graalExtension.getAgentProperties(); - graalExtension.getBinaries().all(options -> { - AgentConfiguration agentConfiguration = options.getAgent(); - if (agentConfiguration.getInstrumentedTask().isPresent()) { - configureAgent(p, agents, graalExtension.getToolchainDetection().map(b -> !b), options, getExecOperations(), getFileOperations()); + + project.getPlugins().withType(JavaLibraryPlugin.class, javaLibraryPlugin -> graalExtension.getAgent().getDefaultMode().convention("conditional")); + + project.afterEvaluate(p -> instrumentTasksWithAgent(project, graalExtension)); + } + + private void instrumentTasksWithAgent(Project project, DefaultGraalVmExtension graalExtension) { + Provider agentMode = agentProperty(project, graalExtension.getAgent()); + Predicate taskPredicate = graalExtension.getAgent().getTasksToInstrumentPredicate().get(); + project.getTasks().configureEach(t -> { + if (isTaskInstrumentableByAgent(t) && taskPredicate.test(t)) { + configureAgent(project, agentMode, graalExtension, getExecOperations(), getFileOperations(), t, (JavaForkOptions) t); + } else { + String reason; + if (isTaskInstrumentableByAgent(t)) { + reason = "Not instrumentable by agent as it does not extend JavaForkOptions."; + } else { + reason = "Task does not match user predicate"; } - }); + logger.log("Not instrumenting task with native-image-agent: " + t.getName() + ". Reason: " + reason); + } }); } + private static boolean isTaskInstrumentableByAgent(Task task) { + return task instanceof JavaForkOptions; + } + private static String deriveTaskName(String name, String prefix, String suffix) { if ("main".equals(name)) { return prefix + suffix; @@ -206,8 +213,6 @@ private void configureJavaProject(Project project, Provider logger.log("Initializing project: " + project.getName()); logger.log("===================="); - Map> agents = graalExtension.getAgentProperties(); - // Add DSL extensions for building and testing NativeImageOptions mainOptions = createMainOptions(graalExtension, project); deprecateExtension(project, mainOptions, DEPRECATED_NATIVE_BUILD_EXTENSION, "main"); @@ -223,30 +228,31 @@ private void configureJavaProject(Project project, Provider // Register Native Image tasks TaskContainer tasks = project.getTasks(); JavaPluginConvention javaConvention = project.getConvention().getPlugin(JavaPluginConvention.class); - configureAutomaticTaskCreation(project, graalExtension, agents, tasks, javaConvention.getSourceSets()); + configureAutomaticTaskCreation(project, graalExtension, tasks, javaConvention.getSourceSets()); TaskProvider imageBuilder = tasks.named(NATIVE_COMPILE_TASK_NAME, BuildNativeImageTask.class); - TaskProvider deprecatedTask = tasks.register(DEPRECATED_NATIVE_BUILD_TASK, t -> { + tasks.register(DEPRECATED_NATIVE_BUILD_TASK, t -> { t.dependsOn(imageBuilder); t.doFirst("Warn about deprecation", task -> task.getLogger().warn("Task " + DEPRECATED_NATIVE_BUILD_TASK + " is deprecated. Use " + NATIVE_COMPILE_TASK_NAME + " instead.")); }); - // We want to add agent invocation to "run" task, but it is only available when - // Application Plugin is initialized. - project.getPlugins().withType(ApplicationPlugin.class, applicationPlugin -> { - TaskProvider runTask = tasks.withType(JavaExec.class).named(ApplicationPlugin.TASK_RUN_NAME); - mainOptions.getAgent().getInstrumentedTask().convention(runTask); - }); - graalExtension.registerTestBinary("test", config -> { config.forTestTask(tasks.named("test", Test.class)); config.usingSourceSet(GradleUtils.findSourceSet(project, SourceSet.TEST_SOURCE_SET_NAME)); }); + + project.getTasks().register("metadataCopy", MetadataCopyTask.class, task -> { + task.setGroup(LifecycleBasePlugin.BUILD_GROUP); + task.setDescription("Copies metadata collected from tasks instrumented with the agent into target directories."); + task.getInputTaskNames().set(graalExtension.getAgent().getMetadataCopy().getInputTaskNames()); + task.getOutputDirectories().set(graalExtension.getAgent().getMetadataCopy().getOutputDirectories()); + task.getMergeWithExisting().set(graalExtension.getAgent().getMetadataCopy().getMergeWithExisting()); + task.getToolchainDetection().set(graalExtension.getToolchainDetection()); + }); } private void configureAutomaticTaskCreation(Project project, GraalVMExtension graalExtension, - Map> agents, TaskContainer tasks, SourceSetContainer sourceSets) { graalExtension.getBinaries().configureEach(options -> { @@ -255,14 +261,11 @@ private void configureAutomaticTaskCreation(Project project, if ("main".equals(binaryName)) { compileTaskName = NATIVE_COMPILE_TASK_NAME; } - Provider agent = agentPropertyOverride(project, options); - agents.put(binaryName, agent); TaskProvider imageBuilder = tasks.register(compileTaskName, BuildNativeImageTask.class, builder -> { builder.setDescription("Compiles a native image for the " + options.getName() + " binary"); builder.setGroup(LifecycleBasePlugin.BUILD_GROUP); builder.getOptions().convention(options); - builder.getAgentEnabled().set(agent); builder.getUseArgFile().convention(graalExtension.getUseArgFile()); }); String runTaskName = deriveTaskName(binaryName, "native", "Run"); @@ -309,18 +312,18 @@ private void configureJvmReachabilityConfigurationDirectories(Project project, G Set excludedModules = repositoryExtension.getExcludedModules().getOrElse(Collections.emptySet()); Map forcedVersions = repositoryExtension.getModuleToConfigVersion().getOrElse(Collections.emptyMap()); return serviceProvider.map(repo -> repo.findConfigurationDirectoriesFor(query -> classpath.getIncoming().getResolutionResult().allComponents(component -> { - ModuleVersionIdentifier moduleVersion = component.getModuleVersion(); - String module = moduleVersion.getGroup() + ":" + moduleVersion.getName(); - if (!excludedModules.contains(module)) { - query.forArtifact(artifact -> { - artifact.gav(module + ":" + moduleVersion.getVersion()); - if (forcedVersions.containsKey(module)) { - artifact.forceConfigVersion(forcedVersions.get(module)); - } - }); + ModuleVersionIdentifier moduleVersion = component.getModuleVersion(); + String module = moduleVersion.getGroup() + ":" + moduleVersion.getName(); + if (!excludedModules.contains(module)) { + query.forArtifact(artifact -> { + artifact.gav(module + ":" + moduleVersion.getVersion()); + if (forcedVersions.containsKey(module)) { + artifact.forceConfigVersion(forcedVersions.get(module)); } - query.useLatestConfigWhenVersionIsUntested(); - })).stream() + }); + } + query.useLatestConfigWhenVersionIsUntested(); + })).stream() .map(Path::toAbsolutePath) .map(Path::toFile) .collect(Collectors.toList())); @@ -347,7 +350,6 @@ private void deprecateExtension(Project project, NativeImageOptions delegate, String name, String substitute) { - JavaToolchainService toolchains = project.getExtensions().findByType(JavaToolchainService.class); ObjectFactory objects = project.getObjects(); project.getExtensions().add(name, objects.newInstance(DeprecatedNativeImageOptions.class, name, @@ -448,13 +450,12 @@ public void registerTestBinary(Project project, DirectoryProperty testListDirectory = project.getObjects().directoryProperty(); // Add DSL extension for testing - NativeImageOptions testOptions = createTestOptions(graalExtension, name, project, mainOptions, testListDirectory, config.getSourceSet()); + NativeImageOptions testOptions = createTestOptions(graalExtension, name, project, mainOptions, config.getSourceSet()); if (isPrimaryTest) { deprecateExtension(project, testOptions, DEPRECATED_NATIVE_TEST_EXTENSION, "test"); } TaskProvider testTask = config.validate().getTestTask(); - testOptions.getAgent().getInstrumentedTask().set(testTask); testTask.configure(test -> { File testList = new File(testResultsDir, test.getName() + "/testlist"); testListDirectory.set(testList); @@ -474,6 +475,9 @@ public void registerTestBinary(Project project, task.setOnlyIf(t -> graalExtension.getTestSupport().get()); task.getTestListDirectory().set(testListDirectory); testTask.get(); + if (!agentProperty(project, graalExtension.getAgent()).get().equals("disabled")) { + testOptions.getConfigurationFileDirectories().from(AgentConfigurationFactory.getAgentOutputDirectoryForTask(project.getLayout(), testTask.getName())); + } ConfigurableFileCollection testList = project.getObjects().fileCollection(); // Later this will be replaced by a dedicated task not requiring execution of tests testList.from(testListDirectory).builtBy(testTask); @@ -496,27 +500,19 @@ public void registerTestBinary(Project project, * Returns a provider which prefers the CLI arguments over the configured * extension value. */ - private static Provider agentPropertyOverride(Project project, NativeImageOptions extension) { + private static Provider agentProperty(Project project, AgentOptions options) { return project.getProviders() .gradleProperty(AGENT_PROPERTY) .forUseAtConfigurationTime() .map(v -> { if (!v.isEmpty()) { - return Boolean.valueOf(v); + return v; } - return true; + return options.getDefaultMode().get(); }) - .orElse(extension.getAgent().getEnabled()); - } - - private static TaskProvider registerProcessAgentFilesTask(Project project, String name) { - return project.getTasks().register(name, ProcessGeneratedGraalResourceFiles.class, task -> { - task.getFilterableEntries().convention(Arrays.asList("org.gradle.", "java.")); - task.getOutputDirectory().convention(project.getLayout().getBuildDirectory().dir("native/processed/agent/" + name)); - }); + .orElse(project.provider(() -> "disabled")); } - @SuppressWarnings("UnstableApiUsage") private static void registerServiceProvider(Project project, Provider nativeImageServiceProvider) { project.getTasks() .withType(BuildNativeImageTask.class) @@ -575,7 +571,6 @@ private static NativeImageOptions createTestOptions(GraalVMExtension graalExtens String binaryName, Project project, NativeImageOptions mainExtension, - DirectoryProperty testListDirectory, SourceSet sourceSet) { NativeImageOptions testExtension = graalExtension.getBinaries().create(binaryName); NativeConfigurations configs = createNativeConfigurations( @@ -597,46 +592,53 @@ private static NativeImageOptions createTestOptions(GraalVMExtension graalExtens return testExtension; } - private static void configureAgent(Project project, - Map> agents, - Provider disableToolchainDetection, - NativeImageOptions nativeImageOptions, - ExecOperations execOperations, - FileSystemOperations fileOperations) { - String postProcessTaskName = PROCESS_AGENT_RESOURCES_TASK_NAME_PREFIX + capitalize(nativeImageOptions.getName()) + PROCESS_AGENT_RESOURCES_TASK_NAME_SUFFIX; - TaskProvider postProcessingTask = registerProcessAgentFilesTask(project, postProcessTaskName); - TaskProvider instrumentedTask = nativeImageOptions.getAgent().getInstrumentedTask().get(); + private static List agentSessionDirectories(Directory outputDirectory) { + return Arrays.stream(outputDirectory.getAsFile().listFiles(file -> file.isDirectory() && file.getName().startsWith("session-"))).map(File::getAbsolutePath).collect(Collectors.toList()); + } + + private void configureAgent(Project project, + Provider agentMode, + GraalVMExtension graalExtension, + ExecOperations execOperations, + FileSystemOperations fileOperations, + Task taskToInstrument, + JavaForkOptions javaForkOptions) { + logger.lifecycle("Instrumenting task with the native-image-agent: " + taskToInstrument.getName()); + Provider agentConfiguration = AgentConfigurationFactory.getAgentConfiguration(agentMode, graalExtension.getAgent()); + AgentCommandLineProvider cliProvider = project.getObjects().newInstance(AgentCommandLineProvider.class); - Provider agent = agents.get(nativeImageOptions.getName()); - cliProvider.getEnabled().set(agent); - Provider outputDir = project.getLayout().getBuildDirectory().dir(AGENT_OUTPUT_FOLDER + "/" + instrumentedTask.getName()); + cliProvider.getInputFiles().from(agentConfiguration.map(AgentConfiguration::getAgentFiles)); + cliProvider.getEnabled().set(agentConfiguration.map(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); cliProvider.getOutputDirectory().set(outputDir); - cliProvider.getAgentOptions().set(nativeImageOptions.getAgent().getOptions()); - instrumentedTask.get().getJvmArgumentProviders().add(cliProvider); - instrumentedTask.configure(task -> task.doLast(new MergeAgentFiles( - agent, + cliProvider.getAgentOptions().set(agentConfiguration.map(AgentConfiguration::getAgentCommandLine)); + javaForkOptions.getJvmArgumentProviders().add(cliProvider); + + taskToInstrument.doLast(new MergeAgentFilesAction( + isMergingEnabled, + agentModeProvider, + project.provider(() -> false), + project.getObjects(), graalvmHomeProvider(project.getProviders()), - outputDir, - disableToolchainDetection, - nativeImageOptions, + mergeInputDirs, + mergeOutputDirs, + graalExtension.getToolchainDetection(), execOperations, - fileOperations, - project.getLogger()))); - // Gradle won't let us configure from configure so we have to eagerly create the post-processing task :( - postProcessingTask.get().getGeneratedFilesDir().set( - instrumentedTask.map(t -> outputDir.get()) - ); - // We can't set from(postProcessingTask) directly, otherwise a task - // dependency would be introduced even if the agent is not enabled. - // We should be able to write this: - // nativeImageOptions.getConfigurationFileDirectories().from( - // agent.map(enabled -> enabled ? postProcessingTask : project.files()) - // ) - // but Gradle won't track the postProcessingTask dependency so we have to write this: - ConfigurableFileCollection files = project.getObjects().fileCollection(); - files.from(agent.map(enabled -> enabled ? postProcessingTask : project.files())); - files.builtBy((Callable) () -> agent.get() ? postProcessingTask.get() : null); - nativeImageOptions.getConfigurationFileDirectories().from(files); + project.getLogger())); + + taskToInstrument.doLast(new CleanupAgentFilesAction(mergeInputDirs, fileOperations)); + + taskToInstrument.doLast(new ProcessGeneratedGraalResourceFilesAction( + outputDir, + graalExtension.getAgent().getFilterableEntries() + )); } private static void injectTestPluginDependencies(Project project, Property testSupportEnabled) { diff --git a/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/dsl/GraalVMExtension.java b/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/dsl/GraalVMExtension.java index fb574b07b..e9f1ee2b3 100644 --- a/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/dsl/GraalVMExtension.java +++ b/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/dsl/GraalVMExtension.java @@ -41,10 +41,12 @@ package org.graalvm.buildtools.gradle.dsl; +import org.graalvm.buildtools.gradle.dsl.agent.AgentOptions; import org.gradle.api.Action; import org.gradle.api.NamedDomainObjectContainer; import org.gradle.api.file.DirectoryProperty; import org.gradle.api.provider.Property; +import org.gradle.api.tasks.Nested; import org.gradle.api.tasks.SourceSet; import org.gradle.api.tasks.TaskProvider; import org.gradle.api.tasks.testing.Test; @@ -60,10 +62,14 @@ public interface GraalVMExtension { * to disable automatic test support, especially in cases * where the test framework doesn't allow testing within * a native image. - * */ Property getTestSupport(); + @Nested + AgentOptions getAgent(); + + void agent(Action spec); + DirectoryProperty getGeneratedResourcesDirectory(); /** @@ -80,6 +86,7 @@ public interface GraalVMExtension { /** * Registers a new native image binary with testing support. + * * @param spec the test image configuration */ void registerTestBinary(String name, Action spec); @@ -94,6 +101,7 @@ public interface GraalVMExtension { * 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 getUseArgFile(); @@ -103,6 +111,7 @@ interface TestBinaryConfig { /** * Sets the JVM test task which corresponds to the * native test that we're configuring. + * * @param jvmTestTask an existing JVM test task */ void forTestTask(TaskProvider jvmTestTask); diff --git a/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/dsl/NativeImageOptions.java b/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/dsl/NativeImageOptions.java index d19585cf5..359279e80 100644 --- a/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/dsl/NativeImageOptions.java +++ b/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/dsl/NativeImageOptions.java @@ -153,21 +153,6 @@ public interface NativeImageOptions extends Named { @Input Property getVerbose(); - /** - * Returns the GraalVM agent configuration. - * @return the configuration. - */ - @Nested - AgentConfiguration getAgent(); - - /** - * Configures the GraalVM agent options. - * @param spec the agent configuration. - */ - default void agent(Action spec) { - spec.execute(getAgent()); - } - /** * Gets the value which determines if shared library is being built. * diff --git a/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/dsl/agent/AgentModeOptions.java b/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/dsl/agent/AgentModeOptions.java new file mode 100644 index 000000000..a6d356198 --- /dev/null +++ b/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/dsl/agent/AgentModeOptions.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2022, 2022 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.dsl.agent; + +import org.gradle.api.Action; +import org.gradle.api.tasks.Nested; + +@SuppressWarnings({"unused"}) +public interface AgentModeOptions { + @Nested + StandardAgentModeOptions getStandard(); + + default void standard(Action spec) { + spec.execute(getStandard()); + } + + @Nested + ConditionalAgentModeOptions getConditional(); + + default void conditional(Action spec) { + spec.execute(getConditional()); + } + + @Nested + DirectAgentModeOptions getDirect(); + + default void direct(Action spec) { + spec.execute(getDirect()); + } +} diff --git a/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/dsl/agent/AgentOptions.java b/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/dsl/agent/AgentOptions.java new file mode 100644 index 000000000..590e5e274 --- /dev/null +++ b/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/dsl/agent/AgentOptions.java @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2020, 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.dsl.agent; + +import org.gradle.api.Action; +import org.gradle.api.Task; +import org.gradle.api.file.ConfigurableFileCollection; +import org.gradle.api.provider.ListProperty; +import org.gradle.api.provider.Property; +import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.InputFiles; +import org.gradle.api.tasks.Internal; +import org.gradle.api.tasks.Nested; +import org.gradle.api.tasks.Optional; + +import java.util.function.Predicate; + +@SuppressWarnings({"unused"}) +public interface AgentOptions { + /** + * Contains configuration of supported agent modes. + */ + @Nested + AgentModeOptions getModes(); + + default void modes(Action spec) { + spec.execute(getModes()); + } + + /** + * The default agent mode name when the agent is in use. + */ + @Input + @Optional + Property getDefaultMode(); + + /** + * Caller-filter files that will be passed to the agent. + */ + @InputFiles + @Optional + ConfigurableFileCollection getCallerFilterFiles(); + + /** + * Access-filter files that will be passed to the agent. + */ + @InputFiles + @Optional + ConfigurableFileCollection getAccessFilterFiles(); + + /** + * Toggles the builtin agent caller filter. + */ + @Optional + Property getBuiltinCallerFilter(); + + /** + * Toggles the builtin agent heuristic filter. + */ + @Optional + Property getBuiltinHeuristicFilter(); + + + /** + * Toggles the experimental support for predefined classes. + */ + @Optional + Property getEnableExperimentalPredefinedClasses(); + + + /** + * Toggles the experimental support for unsafe allocation tracing. + */ + @Optional + Property getEnableExperimentalUnsafeAllocationTracing(); + + + /** + * Toggles the distinction between queried and used metadata. + */ + @Optional + Property getTrackReflectionMetadata(); + + /** + * Configuration of the metadata copy task. + */ + @Nested + MetadataCopyOptions getMetadataCopy(); + + default void metadataCopy(Action spec) { + spec.execute(getMetadataCopy()); + } + + /** + * Specifies prefixes that will be used to further filter files produced by the agent. + */ + @Input + @Optional + ListProperty getFilterableEntries(); + + /** + * Selects tasks that should be instrumented with the agent. + * + * @return Task predicate that accepts tasks during task configuration. + */ + @Internal + Property> getTasksToInstrumentPredicate(); + +} diff --git a/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/dsl/agent/ConditionalAgentModeOptions.java b/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/dsl/agent/ConditionalAgentModeOptions.java new file mode 100644 index 000000000..711c3405c --- /dev/null +++ b/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/dsl/agent/ConditionalAgentModeOptions.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2022, 2022 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.dsl.agent; + +import org.gradle.api.provider.Property; +import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.InputFile; +import org.gradle.api.tasks.Optional; + +public interface ConditionalAgentModeOptions { + + @InputFile + @Optional + Property getUserCodeFilterPath(); + + @Input + @Optional + Property getExtraFilterPath(); + + @Input + @Optional + Property getParallel(); + +} diff --git a/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/dsl/agent/DirectAgentModeOptions.java b/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/dsl/agent/DirectAgentModeOptions.java new file mode 100644 index 000000000..7fab6e902 --- /dev/null +++ b/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/dsl/agent/DirectAgentModeOptions.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2022, 2022 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.dsl.agent; + +import org.gradle.api.provider.ListProperty; +import org.gradle.api.tasks.Input; + +public interface DirectAgentModeOptions { + + @Input + ListProperty getOptions(); + +} diff --git a/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/dsl/AgentConfiguration.java b/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/dsl/agent/MetadataCopyOptions.java similarity index 71% rename from native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/dsl/AgentConfiguration.java rename to native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/dsl/agent/MetadataCopyOptions.java index 7e0576cc9..d07e20dc4 100644 --- a/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/dsl/AgentConfiguration.java +++ b/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/dsl/agent/MetadataCopyOptions.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2021 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2022, 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 @@ -38,38 +38,24 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -package org.graalvm.buildtools.gradle.dsl; +package org.graalvm.buildtools.gradle.dsl.agent; import org.gradle.api.provider.ListProperty; import org.gradle.api.provider.Property; import org.gradle.api.tasks.Input; -import org.gradle.api.tasks.Internal; import org.gradle.api.tasks.Optional; -import org.gradle.api.tasks.TaskProvider; -import org.gradle.process.JavaForkOptions; -public interface AgentConfiguration { - /** - * Gets the value which toggles the native-image-agent usage. - * - * @return The value which toggles the native-image-agent usage. - */ - @Input - Property getEnabled(); +public interface MetadataCopyOptions { - /** - * Gets the native agent options. Only used when {@link #getEnabled()} is true. - * @return the native agent options. - */ @Input @Optional - ListProperty getOptions(); + ListProperty getInputTaskNames(); - /** - * Configures the task which needs to be instrumented. - * @return the instrumented task. - */ - @Internal - Property> getInstrumentedTask(); + @Input + @Optional + ListProperty getOutputDirectories(); + @Input + @Optional + Property getMergeWithExisting(); } diff --git a/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/dsl/agent/StandardAgentModeOptions.java b/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/dsl/agent/StandardAgentModeOptions.java new file mode 100644 index 000000000..973e9a1e9 --- /dev/null +++ b/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/dsl/agent/StandardAgentModeOptions.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2022, 2022 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.dsl.agent; + +public interface StandardAgentModeOptions { +} diff --git a/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/internal/AgentCommandLineProvider.java b/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/internal/AgentCommandLineProvider.java index a2c024ba3..02f22ea30 100644 --- a/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/internal/AgentCommandLineProvider.java +++ b/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/internal/AgentCommandLineProvider.java @@ -42,27 +42,32 @@ package org.graalvm.buildtools.gradle.internal; import org.graalvm.buildtools.utils.SharedConstants; +import org.gradle.api.file.ConfigurableFileCollection; import org.gradle.api.file.DirectoryProperty; import org.gradle.api.provider.ListProperty; import org.gradle.api.provider.Property; import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.InputFiles; import org.gradle.api.tasks.Optional; import org.gradle.api.tasks.OutputDirectory; +import org.gradle.api.tasks.PathSensitive; +import org.gradle.api.tasks.PathSensitivity; import org.gradle.process.CommandLineArgumentProvider; import javax.inject.Inject; import java.io.File; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; + +import static org.graalvm.buildtools.utils.SharedConstants.AGENT_OUTPUT_DIRECTORY_MARKER; public abstract class AgentCommandLineProvider implements CommandLineArgumentProvider { @Inject @SuppressWarnings("checkstyle:redundantmodifier") public AgentCommandLineProvider() { - } @Input @@ -71,6 +76,16 @@ public AgentCommandLineProvider() { @OutputDirectory public abstract DirectoryProperty getOutputDirectory(); + @InputFiles + @PathSensitive(PathSensitivity.RELATIVE) + public abstract ConfigurableFileCollection getInputFiles(); + + @Input + public abstract ListProperty getFilterableEntries(); + + @Input + public abstract Property getAgentMode(); + @Input @Optional public abstract ListProperty getAgentOptions(); @@ -78,12 +93,8 @@ public AgentCommandLineProvider() { @Override public Iterable asArguments() { if (getEnabled().get()) { - File outputDir = getOutputDirectory().getAsFile().get(); - List agentOptions = new ArrayList<>(getAgentOptions().getOrElse(Collections.emptyList())); - if (agentOptions.stream().map(s -> s.split("=")[0]).anyMatch(s -> s.contains("config-output-dir"))) { - throw new IllegalStateException("config-output-dir cannot be supplied as an agent option"); - } - agentOptions.add("config-output-dir=" + outputDir.getAbsolutePath() + File.separator + SharedConstants.AGENT_SESSION_SUBDIR); + String outputDirPath = getOutputDirectory().getAsFile().get().getAbsolutePath() + File.separator + SharedConstants.AGENT_SESSION_SUBDIR; + List agentOptions = getAgentOptions().get().stream().map(opt -> opt.replace(AGENT_OUTPUT_DIRECTORY_MARKER, outputDirPath)).collect(Collectors.toList()); return Arrays.asList( "-agentlib:native-image-agent=" + String.join(",", agentOptions), "-Dorg.graalvm.nativeimage.imagecode=agent" diff --git a/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/internal/BaseNativeImageOptions.java b/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/internal/BaseNativeImageOptions.java index cc4a82d6c..2e6d690be 100644 --- a/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/internal/BaseNativeImageOptions.java +++ b/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/internal/BaseNativeImageOptions.java @@ -211,7 +211,6 @@ public BaseNativeImageOptions(String name, getDebug().convention(false); getFallback().convention(false); getVerbose().convention(false); - getAgent().getEnabled().convention(false); getSharedLibrary().convention(false); getImageName().convention(defaultImageName); getUseFatJar().convention(false); 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 cc69c4b74..96e377b37 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 @@ -44,26 +44,24 @@ import org.graalvm.buildtools.gradle.NativeImagePlugin; import org.graalvm.buildtools.gradle.dsl.GraalVMExtension; import org.graalvm.buildtools.gradle.dsl.NativeImageOptions; +import org.graalvm.buildtools.gradle.dsl.agent.AgentOptions; import org.gradle.api.Action; import org.gradle.api.JavaVersion; import org.gradle.api.NamedDomainObjectContainer; import org.gradle.api.Project; import org.gradle.api.provider.Property; -import org.gradle.api.provider.Provider; import org.gradle.jvm.toolchain.JavaLanguageVersion; import org.gradle.jvm.toolchain.JavaLauncher; import org.gradle.jvm.toolchain.JavaToolchainService; import org.gradle.jvm.toolchain.JvmVendorSpec; import javax.inject.Inject; -import java.util.HashMap; -import java.util.Map; +import java.util.Arrays; public abstract class DefaultGraalVmExtension implements GraalVMExtension { private final NamedDomainObjectContainer nativeImages; private final NativeImagePlugin plugin; private final Project project; - private final Map> agentProperties = new HashMap<>(); private final Property defaultJavaLauncher; @Inject @@ -77,6 +75,17 @@ public DefaultGraalVmExtension(NamedDomainObjectContainer na getToolchainDetection().convention(true); nativeImages.configureEach(options -> options.getJavaLauncher().convention(defaultJavaLauncher)); getTestSupport().convention(true); + AgentOptions agentOpts = getAgent(); + agentOpts.getTasksToInstrumentPredicate().convention(t -> true); + agentOpts.getDefaultMode().convention("standard"); + agentOpts.getModes().getConditional().getParallel().convention(true); + agentOpts.getMetadataCopy().getMergeWithExisting().convention(false); + agentOpts.getFilterableEntries().convention(Arrays.asList("org.gradle.", "java.", "org.junit.")); + agentOpts.getBuiltinHeuristicFilter().convention(true); + agentOpts.getBuiltinCallerFilter().convention(true); + agentOpts.getEnableExperimentalPredefinedClasses().convention(false); + agentOpts.getEnableExperimentalUnsafeAllocationTracing().convention(true); + agentOpts.getTrackReflectionMetadata().convention(true); configureToolchain(); } @@ -105,12 +114,13 @@ public NamedDomainObjectContainer getBinaries() { } @Override - public void binaries(Action> spec) { - spec.execute(nativeImages); + public void agent(Action spec) { + spec.execute(getAgent()); } - public Map> getAgentProperties() { - return agentProperties; + @Override + public void binaries(Action> spec) { + spec.execute(nativeImages); } @Override diff --git a/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/internal/DeprecatedNativeImageOptions.java b/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/internal/DeprecatedNativeImageOptions.java index 754055125..e566526bb 100644 --- a/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/internal/DeprecatedNativeImageOptions.java +++ b/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/internal/DeprecatedNativeImageOptions.java @@ -40,7 +40,6 @@ */ package org.graalvm.buildtools.gradle.internal; -import org.graalvm.buildtools.gradle.dsl.AgentConfiguration; import org.graalvm.buildtools.gradle.dsl.NativeImageOptions; import org.graalvm.buildtools.gradle.dsl.NativeResourcesOptions; import org.gradle.api.Action; @@ -166,12 +165,6 @@ public Property getVerbose() { return warnAboutDeprecation(delegate::getVerbose); } - @Override - @Nested - public AgentConfiguration getAgent() { - return warnAboutDeprecation(delegate::getAgent); - } - @Override @Input public Property getSharedLibrary() { diff --git a/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/internal/NativeImageCommandLineProvider.java b/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/internal/NativeImageCommandLineProvider.java index 9d0b80394..7a12094f4 100644 --- a/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/internal/NativeImageCommandLineProvider.java +++ b/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/internal/NativeImageCommandLineProvider.java @@ -62,20 +62,17 @@ public class NativeImageCommandLineProvider implements CommandLineArgumentProvid private static final Transformer NEGATE = b -> !b; private final Provider options; - private final Provider agentEnabled; private final Provider executableName; private final Provider outputDirectory; private final Provider classpathJar; private final Provider useArgFile; public NativeImageCommandLineProvider(Provider options, - Provider agentEnabled, Provider executableName, Provider outputDirectory, Provider classpathJar, Provider useArgFile) { this.options = options; - this.agentEnabled = agentEnabled; this.executableName = executableName; this.outputDirectory = outputDirectory; this.classpathJar = classpathJar; @@ -87,11 +84,6 @@ public Provider getOptions() { return options; } - @Input - public Provider getAgentEnabled() { - return agentEnabled; - } - @Input public Provider getExecutableName() { return executableName; @@ -142,9 +134,6 @@ public List asArguments() { if (!configFiles.isEmpty()) { cliArgs.add("-H:ConfigurationFileDirectories=" + configFiles); } - if (getAgentEnabled().get()) { - cliArgs.add("--allow-incomplete-classpath"); - } if (options.getMainClass().isPresent()) { cliArgs.add("-H:Class=" + options.getMainClass().get()); } @@ -163,6 +152,7 @@ public List asArguments() { * to build images within a docker container, which makes it so * that the paths in the options are invalid (they would be prefixed * by a Windows path). + * * @param options the native options * @return the classpath string */ diff --git a/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/internal/NativeImageExecutableLocator.java b/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/internal/NativeImageExecutableLocator.java index 1cb3a41f9..1b528bd0c 100644 --- a/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/internal/NativeImageExecutableLocator.java +++ b/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/internal/NativeImageExecutableLocator.java @@ -40,11 +40,12 @@ */ package org.graalvm.buildtools.gradle.internal; -import org.graalvm.buildtools.gradle.dsl.NativeImageOptions; import org.gradle.api.GradleException; +import org.gradle.api.provider.Property; import org.gradle.api.provider.Provider; import org.gradle.api.provider.ProviderFactory; import org.gradle.jvm.toolchain.JavaInstallationMetadata; +import org.gradle.jvm.toolchain.JavaLauncher; import org.gradle.process.ExecOperations; import org.gradle.process.ExecResult; @@ -62,13 +63,13 @@ public static Provider graalvmHomeProvider(ProviderFactory providers) { .orElse(providers.environmentVariable("JAVA_HOME").forUseAtConfigurationTime()); } - public static File findNativeImageExecutable(NativeImageOptions options, + public static File findNativeImageExecutable(Property javaLauncher, Provider disableToolchainDetection, Provider graalvmHomeProvider, ExecOperations execOperations, GraalVMLogger logger) { File executablePath = null; - if (disableToolchainDetection.get() || !options.getJavaLauncher().isPresent()) { + if (disableToolchainDetection.get() || !javaLauncher.isPresent()) { if (graalvmHomeProvider.isPresent()) { String graalvmHome = graalvmHomeProvider.get(); logger.lifecycle("Toolchain detection is disabled, will use GraalVM from {}.", graalvmHome); @@ -76,7 +77,7 @@ public static File findNativeImageExecutable(NativeImageOptions options, } } if (executablePath == null) { - JavaInstallationMetadata metadata = options.getJavaLauncher().get().getMetadata(); + JavaInstallationMetadata metadata = javaLauncher.get().getMetadata(); executablePath = metadata.getInstallationPath().file("bin/" + NATIVE_IMAGE_EXE).getAsFile(); if (!executablePath.exists() && graalvmHomeProvider.isPresent()) { executablePath = Paths.get(graalvmHomeProvider.get()).resolve("bin").resolve(NATIVE_IMAGE_EXE).toFile(); @@ -101,8 +102,8 @@ public static File findNativeImageExecutable(NativeImageOptions options, } } catch (GradleException e) { throw new GradleException("Determining GraalVM installation failed with message: " + e.getMessage() + "\n\n" - + "Make sure to declare the GRAALVM_HOME environment variable or install GraalVM with " + - "native-image in a standard location recognized by Gradle Java toolchain support"); + + "Make sure to declare the GRAALVM_HOME environment variable or install GraalVM with " + + "native-image in a standard location recognized by Gradle Java toolchain support"); } return executablePath; } 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 new file mode 100644 index 000000000..277f175af --- /dev/null +++ b/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/internal/agent/AgentConfigurationFactory.java @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2022, 2022 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.agent; + +import org.graalvm.buildtools.agent.AgentConfiguration; +import org.graalvm.buildtools.agent.AgentMode; +import org.graalvm.buildtools.agent.ConditionalAgentMode; +import org.graalvm.buildtools.agent.DirectAgentMode; +import org.graalvm.buildtools.agent.DisabledAgentMode; +import org.graalvm.buildtools.agent.StandardAgentMode; +import org.graalvm.buildtools.gradle.dsl.agent.AgentOptions; +import org.graalvm.buildtools.gradle.dsl.agent.ConditionalAgentModeOptions; +import org.gradle.api.GradleException; +import org.gradle.api.file.ConfigurableFileCollection; +import org.gradle.api.file.Directory; +import org.gradle.api.file.ProjectLayout; +import org.gradle.api.provider.Provider; + +import java.io.File; +import java.util.Collection; +import java.util.Collections; +import java.util.stream.Collectors; + +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 -> { + AgentMode agentMode; + ConfigurableFileCollection callerFilterFiles = options.getCallerFilterFiles(); + ConfigurableFileCollection accessFilterFiles = options.getAccessFilterFiles(); + switch (name) { + case "standard": + agentMode = new StandardAgentMode(); + break; + case "disabled": + agentMode = new DisabledAgentMode(); + break; + case "conditional": + ConditionalAgentModeOptions opts = options.getModes().getConditional(); + if (!opts.getUserCodeFilterPath().isPresent()) { + throw new GradleException("Missing property userCodeFilterPath in agent conditional configuration"); + } + agentMode = new ConditionalAgentMode(opts.getUserCodeFilterPath().get(), opts.getExtraFilterPath().getOrElse(""), opts.getParallel().get()); + break; + case "direct": + agentMode = new DirectAgentMode(options.getModes().getDirect().getOptions().getOrElse(Collections.emptyList())); + break; + default: + throw new GradleException("Unknown agent mode selected: " + name); + } + return new AgentConfiguration(getFilePaths(callerFilterFiles), + getFilePaths(accessFilterFiles), + options.getBuiltinCallerFilter().get(), + options.getBuiltinHeuristicFilter().get(), + options.getEnableExperimentalPredefinedClasses().get(), + options.getEnableExperimentalUnsafeAllocationTracing().get(), + options.getTrackReflectionMetadata().get(), + agentMode); + }); + } + + private static Collection getFilePaths(ConfigurableFileCollection configurableFileCollection) { + return configurableFileCollection.getFiles().stream().map(File::getAbsolutePath).collect(Collectors.toList()); + } + + public static Provider getAgentOutputDirectoryForTask(ProjectLayout layout, String taskName) { + return layout.getBuildDirectory().dir(AGENT_OUTPUT_FOLDER + "/" + taskName); + } +} diff --git a/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/tasks/BuildNativeImageTask.java b/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/tasks/BuildNativeImageTask.java index 4a75130e6..2588c2f23 100644 --- a/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/tasks/BuildNativeImageTask.java +++ b/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/tasks/BuildNativeImageTask.java @@ -117,9 +117,6 @@ public Provider getOutputFile() { return getOutputDirectory().map(dir -> dir.file(getExecutableName()).get()); } - @Input - public abstract Property getAgentEnabled(); - @Input public abstract Property getDisableToolchainDetection(); @@ -153,7 +150,6 @@ private List buildActualCommandLineArgs() { getOptions().finalizeValue(); return new NativeImageCommandLineProvider( getOptions(), - getAgentEnabled(), getExecutableShortName(), // 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... @@ -178,7 +174,7 @@ public void exec() { logger.lifecycle("Args are: " + args); } File executablePath = NativeImageExecutableLocator.findNativeImageExecutable( - options, + options.getJavaLauncher(), getDisableToolchainDetection(), getGraalVMHome(), getExecOperations(), 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 new file mode 100644 index 000000000..1efc510a3 --- /dev/null +++ b/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/tasks/MetadataCopyTask.java @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2022, 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.tasks; + +import org.graalvm.buildtools.agent.AgentMode; +import org.graalvm.buildtools.agent.StandardAgentMode; +import org.graalvm.buildtools.gradle.internal.GraalVMLogger; +import org.graalvm.buildtools.gradle.internal.agent.AgentConfigurationFactory; +import org.graalvm.buildtools.gradle.tasks.actions.MergeAgentFilesAction; +import org.gradle.api.DefaultTask; +import org.gradle.api.GradleException; +import org.gradle.api.file.ProjectLayout; +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.provider.ProviderFactory; +import org.gradle.api.tasks.Internal; +import org.gradle.api.tasks.TaskAction; +import org.gradle.api.tasks.options.Option; +import org.gradle.process.ExecOperations; + +import javax.inject.Inject; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.List; + +import static org.graalvm.buildtools.gradle.internal.NativeImageExecutableLocator.graalvmHomeProvider; + +public abstract class MetadataCopyTask extends DefaultTask { + + private final GraalVMLogger logger; + + private final ProjectLayout layout; + private final ProviderFactory providerFactory; + private final ObjectFactory objectFactory; + private final ExecOperations execOperations; + + @Inject + public MetadataCopyTask(ProjectLayout layout, + ProviderFactory providerFactory, + ObjectFactory objectFactory, + ExecOperations execOperations) { + this.logger = GraalVMLogger.of(getLogger()); + this.layout = layout; + this.providerFactory = providerFactory; + this.objectFactory = objectFactory; + this.execOperations = execOperations; + } + + @Internal + public abstract ListProperty getInputTaskNames(); + + @Internal + public abstract ListProperty getOutputDirectories(); + + @Internal + public abstract Property getMergeWithExisting(); + + @Internal + public abstract Property getToolchainDetection(); + + @Option(option = "task", description = "Executed task previously instrumented with the agent whose metadata should be copied.") + public void overrideInputTaskNames(List inputTaskNames) { + getInputTaskNames().set(inputTaskNames); + } + + @Option(option = "dir", description = "Directory to which the metadata will be copied.") + public void overrideOutputDirectories(List outputDirectories) { + getOutputDirectories().set(outputDirectories); + } + + @TaskAction + public void exec() { + StringBuilder builder = new StringBuilder(); + ListProperty inputDirectories = objectFactory.listProperty(String.class); + + for (String taskName : getInputTaskNames().get()) { + File dir = AgentConfigurationFactory.getAgentOutputDirectoryForTask(layout, taskName).get().getAsFile(); + if (!dir.exists()) { + builder.append("Could not find configuration for task: ").append(taskName).append(". Please run the task with the agent."); + } else if (!dir.isDirectory()) { + builder.append("Expected a directory with configuration for task: ").append(taskName).append(" but found a regular file at ").append(dir.getAbsolutePath()).append(". Was the output directory manually modified?"); + } + inputDirectories.add(dir.getAbsolutePath()); + } + String errorString = builder.toString(); + if (!errorString.isEmpty()) { + throw new GradleException(errorString); + } + + ListProperty outputDirectories = objectFactory.listProperty(String.class); + for (String dirName : getOutputDirectories().get()) { + File dir = layout.dir(providerFactory.provider(() -> new File(dirName))).get().getAsFile(); + outputDirectories.add(dir.getAbsolutePath()); + if (dir.exists()) { + if (!dir.isDirectory()) { + builder.append("Specified output path must either not exist or be a directory: ").append(dirName); + } + } else { + try { + logger.log("Creating output directory: " + dirName); + Files.createDirectories(dir.toPath()); + } catch (IOException e) { + throw new GradleException("Could not create output directory: " + dirName, e); + } + } + } + + Provider isMergeEnabled = providerFactory.provider(() -> true); + Provider agentModeProvider = providerFactory.provider(StandardAgentMode::new); + + new MergeAgentFilesAction( + isMergeEnabled, + agentModeProvider, + getMergeWithExisting(), + objectFactory, + graalvmHomeProvider(providerFactory), + inputDirectories, + outputDirectories, + getToolchainDetection(), + execOperations, + getLogger()).execute(this); + } +} diff --git a/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/tasks/NativeRunTask.java b/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/tasks/NativeRunTask.java index 4129bc83e..be71af965 100644 --- a/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/tasks/NativeRunTask.java +++ b/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/tasks/NativeRunTask.java @@ -84,7 +84,7 @@ public void exec() { spec.setExecutable(getImage().get().getAsFile().getAbsolutePath()); spec.args(getRuntimeArgs().get()); if (getEnvironment().isPresent()) { - Map env = (Map) getEnvironment().get(); + Map env = getEnvironment().get(); for (Map.Entry entry : env.entrySet()) { spec.environment(entry.getKey(), entry.getValue()); } 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 new file mode 100644 index 000000000..a9dd139dd --- /dev/null +++ b/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/tasks/actions/CleanupAgentFilesAction.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2022, 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.tasks.actions; + +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; + +public class CleanupAgentFilesAction implements Action { + + private final Provider> directoriesToCleanup; + private final FileSystemOperations fileOperations; + + public CleanupAgentFilesAction(Provider> directoriesToCleanup, FileSystemOperations fileOperations) { + this.directoriesToCleanup = directoriesToCleanup; + this.fileOperations = fileOperations; + } + + @Override + public void execute(Task task) { + fileOperations.delete(spec -> spec.delete(directoriesToCleanup.get())); + } +} diff --git a/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/MergeAgentFiles.java b/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/tasks/actions/MergeAgentFilesAction.java similarity index 52% rename from native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/MergeAgentFiles.java rename to native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/tasks/actions/MergeAgentFilesAction.java index 45f771eb2..4a4e43959 100644 --- a/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/MergeAgentFiles.java +++ b/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/tasks/actions/MergeAgentFilesAction.java @@ -38,61 +38,72 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -package org.graalvm.buildtools.gradle; +package org.graalvm.buildtools.gradle.tasks.actions; -import org.graalvm.buildtools.gradle.dsl.NativeImageOptions; +import org.graalvm.buildtools.agent.AgentMode; import org.graalvm.buildtools.gradle.internal.GraalVMLogger; import org.graalvm.buildtools.utils.NativeImageUtils; import org.gradle.api.Action; import org.gradle.api.Task; -import org.gradle.api.file.Directory; -import org.gradle.api.file.FileSystemOperations; import org.gradle.api.logging.Logger; +import org.gradle.api.model.ObjectFactory; +import org.gradle.api.provider.Property; import org.gradle.api.provider.Provider; +import org.gradle.jvm.toolchain.JavaLauncher; import org.gradle.process.ExecOperations; import org.gradle.process.ExecResult; import java.io.File; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; -import java.util.stream.Stream; import static org.graalvm.buildtools.gradle.internal.NativeImageExecutableLocator.findNativeImageExecutable; import static org.graalvm.buildtools.utils.NativeImageUtils.nativeImageConfigureFileName; -class MergeAgentFiles implements Action { - private final Provider agent; +public class MergeAgentFilesAction implements Action { + private final Provider isMergingEnabled; + private final Provider agentMode; + private final Provider mergeWithOutputs; private final Provider graalvmHomeProvider; - private final Provider outputDir; + private final Provider> inputDirs; + private final Provider> outputDirs; private final Provider disableToolchainDetection; + private final Property noLauncherProperty; private final ExecOperations execOperations; - private final NativeImageOptions options; - private final FileSystemOperations fileOperations; private final Logger logger; - MergeAgentFiles(Provider agent, - Provider graalvmHomeProvider, - Provider outputDir, - Provider disableToolchainDetection, - NativeImageOptions options, - ExecOperations execOperations, - FileSystemOperations fileOperations, - Logger logger) { - this.agent = agent; + public MergeAgentFilesAction(Provider isMergingEnabled, + Provider agentMode, + Provider mergeWithOutputs, + ObjectFactory objectFactory, + Provider graalvmHomeProvider, + Provider> inputDirs, + Provider> outputDirs, + Provider disableToolchainDetection, + ExecOperations execOperations, + Logger logger) { + this.isMergingEnabled = isMergingEnabled; + this.agentMode = agentMode; + this.mergeWithOutputs = mergeWithOutputs; this.graalvmHomeProvider = graalvmHomeProvider; - this.outputDir = outputDir; + this.inputDirs = inputDirs; + this.outputDirs = outputDirs; this.disableToolchainDetection = disableToolchainDetection; - this.options = options; this.execOperations = execOperations; - this.fileOperations = fileOperations; this.logger = logger; + this.noLauncherProperty = objectFactory.property(JavaLauncher.class); + } + + private static boolean isConfigDir(String dir) { + return Arrays.stream(new File(dir).listFiles()).anyMatch(file -> file.getName().equals("reflect-config.json")); } @Override public void execute(Task task) { - if (agent.get()) { - File nativeImage = findNativeImageExecutable(options, disableToolchainDetection, graalvmHomeProvider, execOperations, GraalVMLogger.of(logger)); + if (isMergingEnabled.get()) { + File nativeImage = findNativeImageExecutable(noLauncherProperty, disableToolchainDetection, graalvmHomeProvider, execOperations, GraalVMLogger.of(logger)); File workingDir = nativeImage.getParentFile(); File launcher = new File(workingDir, nativeImageConfigureFileName()); if (!launcher.exists()) { @@ -104,26 +115,25 @@ public void execute(Task task) { NativeImageUtils.maybeCreateConfigureUtilSymlink(launcher, nativeImage.toPath()); } if (launcher.exists()) { - File[] files = outputDir.get().getAsFile().listFiles(); - List args = new ArrayList<>(files.length + 2); - args.add("generate"); - sessionDirectoriesFrom(files) - .map(f -> "--input-dir=" + f.getAbsolutePath()) - .forEach(args::add); - if (args.size() > 1) { - logger.info("Merging agent files"); - args.add("--output-dir=" + outputDir.get().getAsFile().getAbsolutePath()); - ExecResult exec = execOperations.exec(spec -> { - spec.executable(launcher); - spec.args(args); - spec.setStandardOutput(System.out); - spec.setErrorOutput(System.err); - }); - if (exec.getExitValue() == 0) { - fileOperations.delete(spec -> sessionDirectoriesFrom(files).forEach(spec::delete)); - } else { - exec.rethrowFailure(); + if (mergeWithOutputs.get()) { + List inputs = inputDirs.get(); + List leftoverOutputDirs = new ArrayList<>(); + for (String outputDir : outputDirs.get()) { + if (isConfigDir(outputDir)) { + List newInputs = new ArrayList<>(inputs.size() + 1); + newInputs.addAll(inputs); + newInputs.add(outputDir); + mergeAgentFiles(launcher, newInputs, Collections.singletonList(outputDir)); + } else { + leftoverOutputDirs.add(outputDir); + } + } + + if (leftoverOutputDirs.size() > 0) { + mergeAgentFiles(launcher, inputs, leftoverOutputDirs); } + } else { + 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."); @@ -131,9 +141,19 @@ public void execute(Task task) { } } - private Stream sessionDirectoriesFrom(File[] files) { - return Arrays.stream(files) - .filter(File::isDirectory) - .filter(f -> f.getName().startsWith("session-")); + private void mergeAgentFiles(File launcher, List inputDirs, List outputDirs) { + List nicCommandLine = agentMode.get().getNativeImageConfigureOptions(inputDirs, outputDirs); + + if (nicCommandLine.size() > 0) { + ExecResult exec = execOperations.exec(spec -> { + spec.executable(launcher); + spec.args(nicCommandLine); + spec.setStandardOutput(System.out); + spec.setErrorOutput(System.err); + }); + if (exec.getExitValue() != 0) { + exec.rethrowFailure(); + } + } } } diff --git a/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/internal/ProcessGeneratedGraalResourceFiles.java b/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/tasks/actions/ProcessGeneratedGraalResourceFilesAction.java similarity index 65% rename from native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/internal/ProcessGeneratedGraalResourceFiles.java rename to native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/tasks/actions/ProcessGeneratedGraalResourceFilesAction.java index 81cc86ecb..aaefbd3a0 100644 --- a/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/internal/ProcessGeneratedGraalResourceFiles.java +++ b/native-gradle-plugin/src/main/java/org/graalvm/buildtools/gradle/tasks/actions/ProcessGeneratedGraalResourceFilesAction.java @@ -39,23 +39,17 @@ * SOFTWARE. */ -package org.graalvm.buildtools.gradle.internal; +package org.graalvm.buildtools.gradle.tasks.actions; import groovy.json.JsonGenerator; import groovy.json.JsonOutput; import groovy.json.JsonSlurper; -import org.gradle.api.DefaultTask; -import org.gradle.api.file.DirectoryProperty; +import org.gradle.api.Action; +import org.gradle.api.GradleException; +import org.gradle.api.Task; +import org.gradle.api.file.Directory; import org.gradle.api.provider.ListProperty; -import org.gradle.api.tasks.CacheableTask; -import org.gradle.api.tasks.Input; -import org.gradle.api.tasks.InputDirectory; -import org.gradle.api.tasks.OutputDirectory; -import org.gradle.api.tasks.PathSensitive; -import org.gradle.api.tasks.PathSensitivity; -import org.gradle.api.tasks.TaskAction; -import org.gradle.work.FileChange; -import org.gradle.work.InputChanges; +import org.gradle.api.provider.Provider; import java.io.File; import java.io.FileOutputStream; @@ -63,8 +57,6 @@ import java.io.OutputStreamWriter; import java.io.Writer; import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.StandardCopyOption; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -76,66 +68,40 @@ * the GraalVM agent, in particular to filter out entries which are * inherited from the Gradle environment itself. */ -@CacheableTask -public abstract class ProcessGeneratedGraalResourceFiles extends DefaultTask { - /** - * The directory which contains the files generated by the GraalVM agent. - */ - @InputDirectory - @PathSensitive(PathSensitivity.NAME_ONLY) - public abstract DirectoryProperty getGeneratedFilesDir(); - - @OutputDirectory - public abstract DirectoryProperty getOutputDirectory(); +public class ProcessGeneratedGraalResourceFilesAction implements Action { + private final Provider inputDirectory; + private final ListProperty filterableEntries; - @Input - public abstract ListProperty getFilterableEntries(); + public ProcessGeneratedGraalResourceFilesAction(Provider inputDirectory, ListProperty filterableEntries) { + this.inputDirectory = inputDirectory; + this.filterableEntries = filterableEntries; + } - @TaskAction - public void filterResources(InputChanges inputChanges) throws IOException { - File outputDir = getOutputDirectory().get().getAsFile(); - if (outputDir.isDirectory() || outputDir.mkdirs()) { - if (inputChanges.isIncremental()) { - Iterable fileChanges = inputChanges.getFileChanges(getGeneratedFilesDir()); - for (FileChange change : fileChanges) { - switch (change.getChangeType()) { - case ADDED: - case MODIFIED: - processFile(change.getFile(), outputDir); - break; - case REMOVED: - new File(outputDir, change.getFile().getName()).delete(); - break; - } - } - } else { - for (File resourceFile : getGeneratedFilesDir().getAsFileTree().getFiles()) { - processFile(resourceFile, outputDir); - } + @Override + public void execute(Task task) { + try { + for (File resourceFile : inputDirectory.get().getAsFileTree()) { + processFile(resourceFile); } + } catch (IOException e) { + throw new GradleException("An IO error occured when processing the agent generated files", e); } } - protected void processFile(File file, File outputDir) throws IOException { + protected void processFile(File file) throws IOException { if (file.getName().endsWith(".json")) { - processJsonFile(file, outputDir); - } else { - copyFile(file, outputDir); + processJsonFile(file); } } - private static void copyFile(File file, File outputDir) throws IOException { - Files.copy(file.toPath(), outputDir.toPath().resolve(file.getName()), StandardCopyOption.REPLACE_EXISTING); - } - - protected void processJsonFile(File jsonFile, File outputDir) throws IOException { + protected void processJsonFile(File jsonFile) throws IOException { JsonSlurper json = new JsonSlurper(); Object result = json.parse(jsonFile); Object filtered = filter(result); JsonGenerator generator = new JsonGenerator.Options() .build(); String processed = JsonOutput.prettyPrint(generator.toJson(filtered)); - try (Writer writer = new OutputStreamWriter(new FileOutputStream(new File(outputDir, jsonFile.getName())), StandardCharsets.UTF_8)) { + try (Writer writer = new OutputStreamWriter(new FileOutputStream(jsonFile), StandardCharsets.UTF_8)) { writer.write(processed); } } @@ -181,7 +147,7 @@ private Map filterMap(Map map) { private boolean shouldFilterString(Object value) { if (value instanceof CharSequence) { String string = value.toString(); - return getFilterableEntries().get().stream().anyMatch(string::startsWith); + return filterableEntries.get().stream().anyMatch(string::startsWith); } return false; } diff --git a/samples/java-application-with-reflection/build.gradle b/samples/java-application-with-reflection/build.gradle index 1fd07d1e8..c36340782 100644 --- a/samples/java-application-with-reflection/build.gradle +++ b/samples/java-application-with-reflection/build.gradle @@ -66,13 +66,25 @@ test { } graalvmNative { + agent { + defaultMode = "standard" + + modes { + conditional { + userCodeFilterPath = "user-code-filter.json" + } + } + + metadataCopy { + mergeWithExisting = true + inputTaskNames.add("test") + outputDirectories.add("build/native/metadataCopyTest") + } + } binaries { test { verbose = true - agent { - enabled = true - options.add(providers.systemProperty("agentOptions").forUseAtConfigurationTime()) - } + buildArgs('--allow-incomplete-classpath') } } } diff --git a/samples/java-application-with-reflection/src/main/resources/META-INF/native-image/jni-config.json b/samples/java-application-with-reflection/src/main/resources/META-INF/native-image/jni-config.json new file mode 100644 index 000000000..0d4f101c7 --- /dev/null +++ b/samples/java-application-with-reflection/src/main/resources/META-INF/native-image/jni-config.json @@ -0,0 +1,2 @@ +[ +] diff --git a/samples/java-application-with-reflection/src/main/resources/META-INF/native-image/predefined-classes-config.json b/samples/java-application-with-reflection/src/main/resources/META-INF/native-image/predefined-classes-config.json new file mode 100644 index 000000000..df6f9356f --- /dev/null +++ b/samples/java-application-with-reflection/src/main/resources/META-INF/native-image/predefined-classes-config.json @@ -0,0 +1,8 @@ +[ + { + "type": "agent-extracted", + "classes": [ + + ] + } +] diff --git a/samples/java-application-with-reflection/src/main/resources/META-INF/native-image/proxy-config.json b/samples/java-application-with-reflection/src/main/resources/META-INF/native-image/proxy-config.json new file mode 100644 index 000000000..41b42e677 --- /dev/null +++ b/samples/java-application-with-reflection/src/main/resources/META-INF/native-image/proxy-config.json @@ -0,0 +1,3 @@ +[ + +] diff --git a/samples/java-application-with-reflection/src/main/resources/META-INF/native-image/reflect-config.json b/samples/java-application-with-reflection/src/main/resources/META-INF/native-image/reflect-config.json new file mode 100644 index 000000000..eca7589aa --- /dev/null +++ b/samples/java-application-with-reflection/src/main/resources/META-INF/native-image/reflect-config.json @@ -0,0 +1,5 @@ +[ + { + "name": "DummyClass" + } +] diff --git a/samples/java-application-with-reflection/src/main/resources/META-INF/native-image/resource-config.json b/samples/java-application-with-reflection/src/main/resources/META-INF/native-image/resource-config.json new file mode 100644 index 000000000..28680f37d --- /dev/null +++ b/samples/java-application-with-reflection/src/main/resources/META-INF/native-image/resource-config.json @@ -0,0 +1,9 @@ +{ + "resources": { + "includes": [ + ] + }, + "bundles": [ + + ] +} diff --git a/samples/java-application-with-reflection/src/main/resources/META-INF/native-image/serialization-config.json b/samples/java-application-with-reflection/src/main/resources/META-INF/native-image/serialization-config.json new file mode 100644 index 000000000..bb47bac64 --- /dev/null +++ b/samples/java-application-with-reflection/src/main/resources/META-INF/native-image/serialization-config.json @@ -0,0 +1,8 @@ +{ + "lambdaCapturingTypes": [ + + ], + "types": [ + + ] +} diff --git a/samples/java-application-with-reflection/user-code-filter.json b/samples/java-application-with-reflection/user-code-filter.json new file mode 100644 index 000000000..e261fe506 --- /dev/null +++ b/samples/java-application-with-reflection/user-code-filter.json @@ -0,0 +1,7 @@ +{ + "rules": [ + {"excludeClasses": "**"}, + {"includeClasses": "org.graalvm.demo.**"}, + {"includeClasses": "org.graalvm.demo.**"} + ] +} \ No newline at end of file