Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
Aleksandar Gradinac committed May 19, 2022
1 parent e2e54c4 commit 5bb0697
Show file tree
Hide file tree
Showing 15 changed files with 502 additions and 161 deletions.
Expand Up @@ -42,15 +42,16 @@

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

public class AgentConfiguration implements Serializable {

private final List<String> callerFilterFiles;
private final List<String> accessFilterFiles;
private final Collection<String> callerFilterFiles;
private final Collection<String> accessFilterFiles;
private final AgentMode agentMode;

public AgentConfiguration(List<String> callerFilterFiles, List<String> accessFilterFiles, AgentMode agentMode) {
public AgentConfiguration(Collection<String> callerFilterFiles, Collection<String> accessFilterFiles, AgentMode agentMode) {
this.callerFilterFiles = callerFilterFiles;
this.accessFilterFiles = accessFilterFiles;
this.agentMode = agentMode;
Expand All @@ -63,15 +64,22 @@ public List<String> getAgentCommandLine() {
return cmdLine;
}

public Collection<String> getAgentFiles() {
List<String> files = new ArrayList<>(callerFilterFiles.size() + accessFilterFiles.size());
files.addAll(callerFilterFiles);
files.addAll(accessFilterFiles);
return files;
}

public boolean isEnabled() {
return !(agentMode instanceof DisabledAgentMode);
}

public static void appendOptionToValues(String option, List<String> values, List<String> target) {
public static void appendOptionToValues(String option, Collection<String> values, Collection<String> target) {
values.stream().map(value -> option + value).forEach(target::add);
}

public List<String> getNativeImageConfigureOptions(List<String> inputDirectories, List<String> outputDirectories) {
return agentMode.getNativeImageConfigureOptions(inputDirectories, outputDirectories);
public AgentMode getAgentMode() {
return agentMode;
}
}
Expand Up @@ -43,6 +43,8 @@

import org.graalvm.buildtools.VersionInfo;
import org.graalvm.buildtools.agent.AgentConfiguration;
import org.graalvm.buildtools.agent.AgentMode;
import org.graalvm.buildtools.agent.StandardAgentMode;
import org.graalvm.buildtools.gradle.dsl.GraalVMExtension;
import org.graalvm.buildtools.gradle.dsl.JvmReachabilityMetadataRepositoryExtension;
import org.graalvm.buildtools.gradle.dsl.NativeImageOptions;
Expand All @@ -56,11 +58,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.tasks.actions.CleanupAgentFilesAction;
import org.graalvm.buildtools.gradle.tasks.actions.ProcessGeneratedGraalResourceFilesAction;
import org.graalvm.buildtools.gradle.internal.agent.AgentConfigurationFactory;
import org.graalvm.buildtools.gradle.tasks.BuildNativeImageTask;
import org.graalvm.buildtools.gradle.tasks.CopyMetadataTask;
import org.graalvm.buildtools.gradle.tasks.GenerateResourcesConfigFile;
import org.graalvm.buildtools.gradle.tasks.NativeRunTask;
import org.graalvm.buildtools.gradle.tasks.actions.MergeAgentFilesAction;
import org.graalvm.buildtools.utils.SharedConstants;
import org.gradle.api.Action;
import org.gradle.api.NamedDomainObjectContainer;
Expand Down Expand Up @@ -92,7 +97,6 @@
import org.gradle.api.tasks.OutputDirectory;
import org.gradle.api.tasks.SourceSet;
import org.gradle.api.tasks.SourceSetContainer;
import org.gradle.api.tasks.TaskCollection;
import org.gradle.api.tasks.TaskContainer;
import org.gradle.api.tasks.TaskProvider;
import org.gradle.api.tasks.bundling.AbstractArchiveTask;
Expand All @@ -112,16 +116,16 @@
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;

/**
Expand Down Expand Up @@ -193,8 +197,18 @@ public void apply(Project project) {
private void instrumentTasksWithAgent(Project project, DefaultGraalVmExtension graalExtension) {
Provider<String> agentMode = agentProperty(project, graalExtension.getAgent());
Provider<AgentConfiguration> agentConfiguration = AgentConfigurationFactory.getAgentConfiguration(agentMode, graalExtension.getAgent());
TaskCollection<? extends JavaForkOptions> tasksToInstrument = graalExtension.getAgent().getInstrumentedTasks().get();
tasksToInstrument.configureEach(t -> configureAgent(project, agentConfiguration, graalExtension.getToolchainDetection(), getExecOperations(), getFileOperations(), t, t));
Predicate<? super Task> taskPredicate = graalExtension.getAgent().getTasksToInstrumentPredicate().get();
project.getTasks().configureEach(t -> {
if (isTaskInstrumentableByAgent(t) && taskPredicate.test(t)) {
configureAgent(project, agentConfiguration, graalExtension.getToolchainDetection(), getExecOperations(), getFileOperations(), t, (JavaForkOptions) t);
} else {
logger.lifecycle("Skipping task: " + t.getName());
}
});
}

private static boolean isTaskInstrumentableByAgent(Task task) {
return task instanceof JavaForkOptions;
}

private static String deriveTaskName(String name, String prefix, String suffix) {
Expand Down Expand Up @@ -236,6 +250,34 @@ private void configureJavaProject(Project project, Provider<NativeImageService>
config.forTestTask(tasks.named("test", Test.class));
config.usingSourceSet(GradleUtils.findSourceSet(project, SourceSet.TEST_SOURCE_SET_NAME));
});

TaskProvider<CopyMetadataTask> copyMetadataTask = project.getTasks().register("copyMetadata", CopyMetadataTask.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.getCopyMetadata().getInputTaskNames());
task.getOutputDirectories().set(graalExtension.getCopyMetadata().getOutputDirectories());
task.getMergeWithExisting().set(graalExtension.getCopyMetadata().getMergeWithExisting());
Provider<Boolean> isMergeEnabled = project.provider(() -> true);
Provider<AgentMode> agentModeProvider = project.provider(StandardAgentMode::new);

Provider<List<String>> inputDirectories = task.getInputTaskNames().map(list ->
list.stream()
.map(taskName -> AgentConfigurationFactory.getAgentOutputDirectoryForTask(project, taskName))
.map(dir -> dir.get().getAsFile().getAbsolutePath())
.collect(Collectors.toList()));

task.doLast(new MergeAgentFilesAction(
isMergeEnabled,
agentModeProvider,
task.getMergeWithExisting(),
project,
graalvmHomeProvider(project.getProviders()),
inputDirectories,
task.getOutputDirectories(),
graalExtension.getToolchainDetection(),
getExecOperations(),
task.getLogger()));
});
}

private void configureAutomaticTaskCreation(Project project,
Expand Down Expand Up @@ -299,18 +341,18 @@ private void configureJvmReachabilityConfigurationDirectories(Project project, G
Set<String> excludedModules = repositoryExtension.getExcludedModules().getOrElse(Collections.emptySet());
Map<String, String> 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()));
Expand Down Expand Up @@ -463,6 +505,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(getProcessedAgentOutputFilesDirectory(project, testTask.get()));
}
ConfigurableFileCollection testList = project.getObjects().fileCollection();
// Later this will be replaced by a dedicated task not requiring execution of tests
testList.from(testListDirectory).builtBy(testTask);
Expand Down Expand Up @@ -498,13 +543,6 @@ private static Provider<String> agentProperty(Project project, AgentOptions opti
.orElse(project.provider(() -> "disabled"));
}

private static TaskProvider<ProcessGeneratedGraalResourceFiles> 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));
});
}

@SuppressWarnings("UnstableApiUsage")
private static void registerServiceProvider(Project project, Provider<NativeImageService> nativeImageServiceProvider) {
project.getTasks()
Expand Down Expand Up @@ -590,31 +628,55 @@ private static String postProcessTaskName(String taskName) {
return PROCESS_AGENT_RESOURCES_TASK_NAME_PREFIX + capitalize(taskName) + PROCESS_AGENT_RESOURCES_TASK_NAME_SUFFIX;
}

private static void configureAgent(Project project,
Provider<AgentConfiguration> agentConfiguration,
Provider<Boolean> disableToolchainDetection,
ExecOperations execOperations,
FileSystemOperations fileOperations,
Task taskToInstrument,
JavaForkOptions javaForkOptions) {
TaskProvider<ProcessGeneratedGraalResourceFiles> postProcessingTask = registerProcessAgentFilesTask(project, postProcessTaskName(taskToInstrument.getName()));
private static List<String> 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<AgentConfiguration> agentConfiguration,
Provider<Boolean> disableToolchainDetection,
ExecOperations execOperations,
FileSystemOperations fileOperations,
Task taskToInstrument,
JavaForkOptions javaForkOptions) {
logger.lifecycle("Instrumenting task: " + taskToInstrument.getName());

AgentCommandLineProvider cliProvider = project.getObjects().newInstance(AgentCommandLineProvider.class);
cliProvider.getInputFiles().from(agentConfiguration.map(AgentConfiguration::getAgentFiles));
cliProvider.getEnabled().set(agentConfiguration.map(AgentConfiguration::isEnabled));
Provider<Directory> outputDir = project.getLayout().getBuildDirectory().dir(AGENT_OUTPUT_FOLDER + "/" + taskToInstrument.getName());

Provider<Directory> outputDir = AgentConfigurationFactory.getAgentOutputDirectoryForTask(project, taskToInstrument.getName());
Provider<Boolean> isMergingEnabled = agentConfiguration.map(AgentConfiguration::isEnabled);
Provider<AgentMode> agentModeProvider = agentConfiguration.map(AgentConfiguration::getAgentMode);
Provider<List<String>> mergeOutputDirs = outputDir.map(dir -> Collections.singletonList(dir.getAsFile().getAbsolutePath()));
Provider<List<String>> mergeInputDirs = outputDir.map(NativeImagePlugin::agentSessionDirectories);
cliProvider.getOutputDirectory().set(outputDir);
cliProvider.getAgentOptions().set(agentConfiguration.map(AgentConfiguration::getAgentCommandLine));
javaForkOptions.getJvmArgumentProviders().add(cliProvider);
taskToInstrument.doLast(new MergeAgentFiles(
agentConfiguration,

taskToInstrument.doLast(new MergeAgentFilesAction(
isMergingEnabled,
agentModeProvider,
project.provider(() -> false),
project,
graalvmHomeProvider(project.getProviders()),
outputDir,
mergeInputDirs,
mergeOutputDirs,
disableToolchainDetection,
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(outputDir);
taskToInstrument.doLast(new CleanupAgentFilesAction(mergeInputDirs, fileOperations));

Provider<Directory> processedOutputDirectory = getProcessedAgentOutputFilesDirectory(project, taskToInstrument);
taskToInstrument.doLast(new ProcessGeneratedGraalResourceFilesAction(
outputDir,
processedOutputDirectory,
Arrays.asList("org.gradle.", "java.", "org.junit.")));
}

private static Provider<Directory> getProcessedAgentOutputFilesDirectory(Project project, Task taskToInstrument) {
String name = postProcessTaskName(taskToInstrument.getName());
return project.getLayout().getBuildDirectory().dir("native/processed/agent/" + name);
}

private static void injectTestPluginDependencies(Project project, Property<Boolean> testSupportEnabled) {
Expand Down
Expand Up @@ -42,6 +42,7 @@
package org.graalvm.buildtools.gradle.dsl;

import org.graalvm.buildtools.gradle.dsl.agent.AgentOptions;
import org.graalvm.buildtools.gradle.dsl.agent.CopyMetadataOptions;
import org.gradle.api.Action;
import org.gradle.api.NamedDomainObjectContainer;
import org.gradle.api.file.DirectoryProperty;
Expand Down Expand Up @@ -69,6 +70,9 @@ public interface GraalVMExtension {
@Nested
AgentOptions getAgent();

@Nested
CopyMetadataOptions getCopyMetadata();

void agent(Action<? super AgentOptions> spec);

DirectoryProperty getGeneratedResourcesDirectory();
Expand Down
Expand Up @@ -41,7 +41,6 @@

package org.graalvm.buildtools.gradle.dsl;

import org.graalvm.buildtools.gradle.dsl.agent.AgentOptions;
import org.gradle.api.Action;
import org.gradle.api.Named;
import org.gradle.api.file.ConfigurableFileCollection;
Expand Down
Expand Up @@ -41,16 +41,16 @@
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.provider.SetProperty;
import org.gradle.api.tasks.Input;
import org.gradle.api.tasks.Internal;
import org.gradle.api.tasks.InputFiles;
import org.gradle.api.tasks.Nested;
import org.gradle.api.tasks.Optional;
import org.gradle.api.tasks.TaskCollection;
import org.gradle.api.tasks.TaskProvider;
import org.gradle.process.JavaForkOptions;

import java.util.function.Predicate;

@SuppressWarnings({"unused"})
public interface AgentOptions {
Expand All @@ -65,20 +65,20 @@ default void modes(Action<? super AgentModeOptions> spec) {
@Optional
Property<String> getDefaultMode();

@Input
@InputFiles
@Optional
ListProperty<String> getCallerFilterFiles();
ConfigurableFileCollection getCallerFilterFiles();

@Input
@InputFiles
@Optional
ListProperty<String> getAccessFilterFiles();
ConfigurableFileCollection getAccessFilterFiles();

/**
* Configures the task which needs to be instrumented.
* Configures the tasks which needs to be instrumented
*
* @return the instrumented task.
* @return .
*/
@Input
Property<TaskCollection<? extends JavaForkOptions>> getInstrumentedTasks();
Property<Predicate<? super Task>> getTasksToInstrumentPredicate();

}

0 comments on commit 5bb0697

Please sign in to comment.