From 12a457c18e8c9ef83a1ab2a7ea9911b4643350a8 Mon Sep 17 00:00:00 2001 From: Mihailo Date: Mon, 29 Sep 2025 20:07:31 +0200 Subject: [PATCH] GenerateMetadataTask introduction. Created MetadataGenerationUtils utility class for operations shared by ContributionTask and NonInteractiveContributionTask. --- .../org.graalvm.internal.tck-harness.gradle | 6 + .../internal/tck/ContributionTask.java | 178 ++++-------------- .../internal/tck/GenerateMetadataTask.java | 66 +++++++ .../tck/utils/MetadataGenerationUtils.java | 126 +++++++++++++ 4 files changed, 238 insertions(+), 138 deletions(-) create mode 100644 tests/tck-build-logic/src/main/java/org/graalvm/internal/tck/GenerateMetadataTask.java create mode 100644 tests/tck-build-logic/src/main/java/org/graalvm/internal/tck/utils/MetadataGenerationUtils.java diff --git a/tests/tck-build-logic/src/main/groovy/org.graalvm.internal.tck-harness.gradle b/tests/tck-build-logic/src/main/groovy/org.graalvm.internal.tck-harness.gradle index 14ab8cd4a..53bb43eb1 100644 --- a/tests/tck-build-logic/src/main/groovy/org.graalvm.internal.tck-harness.gradle +++ b/tests/tck-build-logic/src/main/groovy/org.graalvm.internal.tck-harness.gradle @@ -15,6 +15,7 @@ import org.graalvm.internal.tck.DockerTask import org.graalvm.internal.tck.ConfigFilesChecker import org.graalvm.internal.tck.ScaffoldTask import org.graalvm.internal.tck.GrypeTask +import org.graalvm.internal.tck.GenerateMetadataTask import org.graalvm.internal.tck.TestedVersionUpdaterTask import org.graalvm.internal.tck.harness.tasks.TestInvocationTask import org.graalvm.internal.tck.harness.tasks.CheckstyleInvocationTask @@ -217,6 +218,11 @@ tasks.register("contribute", ContributionTask.class) { task -> task.setGroup(METADATA_GROUP) } +tasks.register("generateMetadata", GenerateMetadataTask.class) { task -> + task.setDescription("Generates metadata and prepares pull request for contibuting on metadata repository based on provided tests.") + task.setGroup(METADATA_GROUP) +} + tasks.register("checkConfigFiles", ConfigFilesChecker.class) { task -> task.setDescription("Checks content of config files for a new library.") task.setGroup(METADATA_GROUP) diff --git a/tests/tck-build-logic/src/main/java/org/graalvm/internal/tck/ContributionTask.java b/tests/tck-build-logic/src/main/java/org/graalvm/internal/tck/ContributionTask.java index c0b76b306..1acd1aa69 100644 --- a/tests/tck-build-logic/src/main/java/org/graalvm/internal/tck/ContributionTask.java +++ b/tests/tck-build-logic/src/main/java/org/graalvm/internal/tck/ContributionTask.java @@ -2,8 +2,6 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.core.util.DefaultIndenter; -import com.fasterxml.jackson.core.util.DefaultPrettyPrinter; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import org.graalvm.internal.tck.exceptions.ContributingException; @@ -12,6 +10,7 @@ import org.graalvm.internal.tck.utils.ConfigurationStringBuilder; import org.graalvm.internal.tck.utils.FilesUtils; import org.graalvm.internal.tck.utils.InteractiveTaskUtils; +import org.graalvm.internal.tck.utils.MetadataGenerationUtils; import org.gradle.api.DefaultTask; import org.gradle.api.file.FileSystemOperations; import org.gradle.api.file.ProjectLayout; @@ -19,16 +18,12 @@ import org.gradle.process.ExecOperations; import javax.inject.Inject; -import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; -import java.io.InputStream; -import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.util.ArrayList; -import java.util.Arrays; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; @@ -40,8 +35,7 @@ public abstract class ContributionTask extends DefaultTask { private static final String BRANCH_NAME_PREFIX = "add-support-for-"; private static final String METADATA_INDEX = "metadata/index.json"; - private static final String BUILD_FILE = "build.gradle"; - private static final String USER_CODE_FILTER_FILE = "user-code-filter.json"; + private static final String GRADLEW = "gradlew"; private static final String REQUIRED_DOCKER_IMAGES_FILE = "required-docker-images.txt"; @Inject @@ -55,24 +49,15 @@ public abstract class ContributionTask extends DefaultTask { private final ObjectMapper objectMapper = new ObjectMapper().enable(SerializationFeature.INDENT_OUTPUT).setSerializationInclusion(JsonInclude.Include.NON_NULL); - private Path gradlew; + private Path gradlewPath; private Path testsDirectory; - private Path metadataDirectory; - private Coordinates coordinates; + String coordinates; - private record ContributingQuestion(String question, String help) {} - private final Map questions = new HashMap<>(); - - private void initializeWorkingDirectories(){ - testsDirectory = getPathFromProject(CoordinateUtils.replace("tests/src/$group$/$artifact$/$version$", coordinates)); - metadataDirectory = getPathFromProject(CoordinateUtils.replace("metadata/$group$/$artifact$/$version$", coordinates)); - gradlew = getPathFromProject("gradlew"); + private record ContributingQuestion(String question, String help) { } - private Path getPathFromProject(String fileName) { - return Path.of(getProjectFile(fileName).getAbsolutePath()); - } + private final Map questions = new HashMap<>(); private File getProjectFile(String fileName) { return getLayout().getProjectDirectory().file(fileName).getAsFile(); @@ -96,7 +81,7 @@ void run() throws IOException { coordinates = getCoordinates(); InteractiveTaskUtils.closeSection(); - Path coordinatesMetadataRoot = getPathFromProject(CoordinateUtils.replace("metadata/$group$/$artifact$", coordinates)); + Path coordinatesMetadataRoot = MetadataGenerationUtils.getPathFromProject(getLayout(), CoordinateUtils.replace("metadata/$group$/$artifact$", CoordinateUtils.fromString(coordinates))); boolean isExistingLibrary = Files.exists(coordinatesMetadataRoot); Path testsLocation = getTestsLocation(); @@ -111,11 +96,13 @@ void run() throws IOException { List packages = getAllowedPackages(); InteractiveTaskUtils.closeSection(); - List additionalTestImplementationDependencies = getAdditionalDependencies(); + List additionalTestImplementationDependencies = getAdditionalDependencies(); InteractiveTaskUtils.closeSection(); // initialize project - initializeWorkingDirectories(); + gradlewPath = MetadataGenerationUtils.getPathFromProject(getLayout(), GRADLEW); + testsDirectory = MetadataGenerationUtils.computeTestsDirectory(getLayout(), coordinates); + createStubs(isExistingLibrary); updateAllowedPackages(packages, isExistingLibrary); @@ -123,17 +110,17 @@ void run() throws IOException { addTests(testsLocation); addResources(resourcesLocation); addDockerImages(dockerImages); - addUserCodeFilterFile(packages); + MetadataGenerationUtils.addUserCodeFilterFile(testsDirectory, packages); addAdditionalDependencies(additionalTestImplementationDependencies); - addAgentConfigBlock(); + MetadataGenerationUtils.addAgentConfigBlock(testsDirectory); // run agent in conditional mode - collectMetadata(); + MetadataGenerationUtils.collectMetadata(getExecOperations(), testsDirectory, getLayout(), coordinates, gradlewPath); // create a PR boolean shouldCreatePR = shouldCreatePullRequest(); if (shouldCreatePR) { - String branch = BRANCH_NAME_PREFIX + coordinates.toString().replace(':', '-'); + String branch = BRANCH_NAME_PREFIX + coordinates.replace(':', '-'); createPullRequest(branch); InteractiveTaskUtils.printUserInfo("After your pull requests gets generated, please update the pull request description to mention all places where your pull request" + @@ -143,15 +130,9 @@ void run() throws IOException { InteractiveTaskUtils.printSuccessfulStatement("Contribution successfully completed! Thank you!"); } - private Coordinates getCoordinates() { + private String getCoordinates() { ContributingQuestion question = questions.get("coordinates"); - return InteractiveTaskUtils.askQuestion(question.question(), question.help(), (answer) -> { - try { - return CoordinateUtils.fromString(answer); - } catch (IllegalArgumentException ex) { - throw new ContributingException(ex.getMessage()); - } - }); + return InteractiveTaskUtils.askQuestion(question.question(), question.help(), (answer) -> answer); } private Path getTestsLocation() { @@ -200,7 +181,7 @@ private void checkPackages(Path testsPath) throws ContributingException { } } - private Path getResourcesLocation(){ + private Path getResourcesLocation() { ContributingQuestion question = questions.get("resourcesLocation"); return InteractiveTaskUtils.askQuestion(question.question(), question.help(), (answer) -> { if (answer.equalsIgnoreCase("-")) { @@ -241,21 +222,15 @@ private List getAllowedPackages() { return InteractiveTaskUtils.askRecurringQuestions(question.question(), question.help(), 1, answer -> answer); } - private List getAdditionalDependencies() { + private List getAdditionalDependencies() { ContributingQuestion question = questions.get("additionalDependencies"); - return InteractiveTaskUtils.askRecurringQuestions(question.question(), question.help(), 0, answer -> { - try { - return CoordinateUtils.fromString(answer); - } catch (IllegalArgumentException ex) { - throw new ContributingException(ex.getMessage()); - } - }); + return InteractiveTaskUtils.askRecurringQuestions(question.question(), question.help(), 0, answer -> answer); } private void createStubs(boolean shouldUpdate) { - InteractiveTaskUtils.printUserInfo("Generating stubs for: " + coordinates ); + InteractiveTaskUtils.printUserInfo("Generating stubs for: " + coordinates); String opt = shouldUpdate ? "--update" : ""; - invokeCommand(gradlew + " scaffold --coordinates " + coordinates + " --skipTests " + opt, "Cannot generate stubs for: " + coordinates); + MetadataGenerationUtils.invokeCommand(getExecOperations(), gradlewPath + " scaffold --coordinates " + coordinates + " --skipTests " + opt, "Cannot generate stubs for: " + coordinates); } private void updateAllowedPackages(List allowedPackages, boolean libraryAlreadyExists) throws IOException { @@ -264,8 +239,9 @@ private void updateAllowedPackages(List allowedPackages, boolean library List entries = objectMapper.readValue(metadataIndex, new TypeReference<>() {}); int replaceEntryIndex = -1; + Coordinates dependencyCoordinates = CoordinateUtils.fromString(coordinates); for (int i = 0; i < entries.size(); i++) { - if (entries.get(i).module().equals(coordinates.group() + ":" + coordinates.artifact())) { + if (entries.get(i).module().equals(dependencyCoordinates.group() + ":" + dependencyCoordinates.artifact())) { replaceEntryIndex = i; break; } @@ -289,7 +265,7 @@ private void updateAllowedPackages(List allowedPackages, boolean library objectMapper.writeValue(metadataIndex, entries); } - private void addTests(Path originalTestsLocation){ + private void addTests(Path originalTestsLocation) { Path destination = testsDirectory.resolve("src").resolve("test").resolve("java"); Path allTests = originalTestsLocation.resolve("."); @@ -301,7 +277,7 @@ private void addTests(Path originalTestsLocation){ }); } - private void addResources(Path originalResourcesDirectory){ + private void addResources(Path originalResourcesDirectory) { if (originalResourcesDirectory == null) { return; } @@ -311,8 +287,8 @@ private void addResources(Path originalResourcesDirectory){ InteractiveTaskUtils.printUserInfo("Copying resources from: " + originalResourcesDirectory + " to " + destination); getFileSystemOperations().copy(copySpec -> { - copySpec.from(originalResourcesDirectory); - copySpec.into(destination); + copySpec.from(originalResourcesDirectory); + copySpec.into(destination); }); } @@ -326,39 +302,24 @@ private void addDockerImages(List images) throws IOException { ensureFileBelongsToProject(destination); if (!Files.exists(destination)) { - Files.createFile(destination); + Files.createDirectories(destination.getParent()); } for (String image : images) { - writeToFile(destination, image.concat(System.lineSeparator()), StandardOpenOption.APPEND); + MetadataGenerationUtils.writeToFile(destination, image.concat(System.lineSeparator()), StandardOpenOption.APPEND); } } - private void addUserCodeFilterFile(List packages) throws IOException { - InteractiveTaskUtils.printUserInfo("Generating " + USER_CODE_FILTER_FILE); - List> filterFileRules = new ArrayList<>(); - - // add exclude classes - filterFileRules.add(Map.of("excludeClasses", "**")); - - // add include classes - packages.forEach(p -> filterFileRules.add(Map.of("includeClasses", p + ".**"))); - - DefaultPrettyPrinter prettyPrinter = new DefaultPrettyPrinter(); - prettyPrinter.indentArraysWith(DefaultIndenter.SYSTEM_LINEFEED_INSTANCE); - objectMapper.writer(prettyPrinter).writeValue(testsDirectory.resolve(USER_CODE_FILTER_FILE).toFile(), Map.of("rules", filterFileRules)); - } - - private void addAdditionalDependencies(List dependencies) throws IOException { + private void addAdditionalDependencies(List dependencies) throws IOException { if (dependencies.isEmpty()) { return; } - Path buildFilePath = testsDirectory.resolve(BUILD_FILE); - InteractiveTaskUtils.printUserInfo("Adding following dependencies to " + BUILD_FILE + " file: " + dependencies); + Path buildFilePath = testsDirectory.resolve(MetadataGenerationUtils.BUILD_FILE); + InteractiveTaskUtils.printUserInfo("Adding following dependencies to " + MetadataGenerationUtils.BUILD_FILE + " file: " + dependencies); if (!Files.isRegularFile(buildFilePath)) { - throw new RuntimeException("Cannot add additional dependencies to " + buildFilePath + ". Please check if a " + BUILD_FILE + " exists on that location."); + throw new RuntimeException("Cannot add additional dependencies to " + buildFilePath + ". Please check if a " + MetadataGenerationUtils.BUILD_FILE + " exists on that location."); } ConfigurationStringBuilder sb = new ConfigurationStringBuilder(); @@ -368,39 +329,13 @@ private void addAdditionalDependencies(List dependencies) throws IO if (line.trim().equalsIgnoreCase("dependencies {")) { sb.indent(); for (var dependency : dependencies) { - sb.append("testImplementation").space().quote(dependency.toString()).newLine(); + sb.append("testImplementation").space().quote(dependency).newLine(); } sb.unindent(); } } - writeToFile(buildFilePath, sb.toString(), StandardOpenOption.WRITE); - } - - private void addAgentConfigBlock() throws IOException { - Path buildFilePath = testsDirectory.resolve(BUILD_FILE); - InteractiveTaskUtils.printUserInfo("Configuring agent block in: " + BUILD_FILE); - - if (!Files.isRegularFile(buildFilePath)) { - throw new RuntimeException("Cannot add agent block to " + buildFilePath + ". Please check if a " + BUILD_FILE + " exists on that location."); - } - - try(InputStream stream = ContributionTask.class.getResourceAsStream("/contributing/agent.template")) { - if (stream == null) { - throw new RuntimeException("Cannot find template for the graalvm configuration block"); - } - - String content = System.lineSeparator() + (new String(stream.readAllBytes(), StandardCharsets.UTF_8)); - writeToFile(buildFilePath, content, StandardOpenOption.APPEND); - } - } - - private void collectMetadata() { - InteractiveTaskUtils.printUserInfo("Generating metadata"); - invokeCommand(gradlew + " -Pagent test", "Cannot generate metadata", testsDirectory); - - InteractiveTaskUtils.printUserInfo("Performing metadata copy"); - invokeCommand(gradlew + " metadataCopy --task test --dir " + metadataDirectory, "Cannot perform metadata copy", testsDirectory); + MetadataGenerationUtils.writeToFile(buildFilePath, sb.toString(), StandardOpenOption.WRITE); } private boolean shouldCreatePullRequest() { @@ -410,53 +345,20 @@ private boolean shouldCreatePullRequest() { private void createPullRequest(String branch) { InteractiveTaskUtils.printUserInfo("Creating new branch: " + branch); - invokeCommand("git switch -C " + branch, "Cannot create a new branch"); + MetadataGenerationUtils.invokeCommand(getExecOperations(), "git switch -C " + branch, "Cannot create a new branch"); InteractiveTaskUtils.printUserInfo("Staging changes"); - invokeCommand("git add .", "Cannot add changes"); + MetadataGenerationUtils.invokeCommand(getExecOperations(), "git add .", "Cannot add changes"); InteractiveTaskUtils.printUserInfo("Committing changes"); - invokeCommand("git", List.of("commit", "-m", "Add metadata for " + coordinates), "Cannot commit changes", null); + MetadataGenerationUtils.invokeCommand(getExecOperations(), "git", List.of("commit", "-m", "Add metadata for " + coordinates), "Cannot commit changes", null); InteractiveTaskUtils.printUserInfo("Pushing changes"); - invokeCommand("git push origin " + branch, "Cannot push to origin"); + MetadataGenerationUtils.invokeCommand(getExecOperations(), "git push origin " + branch, "Cannot push to origin"); InteractiveTaskUtils.printUserInfo("Complete pull request creation on the above link"); } - private void writeToFile(Path path, String content, StandardOpenOption writeOption) throws IOException { - Files.createDirectories(path.getParent()); - Files.writeString(path, content, StandardCharsets.UTF_8, writeOption); - } - - private void invokeCommand(String command, String errorMessage) { - invokeCommand(command, errorMessage, null); - } - - private void invokeCommand(String command, String errorMessage, Path workingDirectory) { - String[] commandParts = command.split(" "); - String executable = commandParts[0]; - - List args = List.of(Arrays.copyOfRange(commandParts, 1, commandParts.length)); - invokeCommand(executable, args, errorMessage, workingDirectory); - } - - private void invokeCommand(String executable, List args, String errorMessage, Path workingDirectory) { - ByteArrayOutputStream execOutput = new ByteArrayOutputStream(); - var result = getExecOperations().exec(execSpec -> { - if (workingDirectory != null) { - execSpec.setWorkingDir(workingDirectory); - } - execSpec.setExecutable(executable); - execSpec.setArgs(args); - execSpec.setStandardOutput(execOutput); - }); - - if (result.getExitValue() != 0) { - throw new RuntimeException(errorMessage + ". See: " + execOutput); - } - } - private void ensureFileBelongsToProject(Path file) { if (!file.isAbsolute() || !file.startsWith(getLayout().getProjectDirectory().getAsFile().getAbsolutePath())) { throw new RuntimeException("The following file doesn't belong to the metadata repository: " + file); diff --git a/tests/tck-build-logic/src/main/java/org/graalvm/internal/tck/GenerateMetadataTask.java b/tests/tck-build-logic/src/main/java/org/graalvm/internal/tck/GenerateMetadataTask.java new file mode 100644 index 000000000..37e315d19 --- /dev/null +++ b/tests/tck-build-logic/src/main/java/org/graalvm/internal/tck/GenerateMetadataTask.java @@ -0,0 +1,66 @@ +package org.graalvm.internal.tck; + +import org.graalvm.internal.tck.utils.MetadataGenerationUtils; +import org.gradle.api.DefaultTask; +import org.gradle.api.file.ProjectLayout; +import org.gradle.api.tasks.Input; +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.IOException; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +public abstract class GenerateMetadataTask extends DefaultTask { + private static final String GRADLEW = "gradlew"; + + private String coordinates; + private String packages; + + @Inject + protected abstract ProjectLayout getLayout(); + + @Inject + protected abstract ExecOperations getExecOperations(); + + @Option(option = "coordinates", description = "Coordinates in the form of group:artifact:version") + public void setCoordinates(String coordinates) { + this.coordinates = coordinates; + } + + @Input + public String getCoordinates() { + return coordinates; + } + + @Option(option = "packages", description = "Comma separated allowed packages (or - for none)") + public void setPackages(String packages) { + this.packages = packages; + } + + @Input + public String getPackages() { + return packages; + } + + @TaskAction + public void run() throws IOException { + Path testsDirectory = MetadataGenerationUtils.computeTestsDirectory(getLayout(), coordinates); + Path gradlewPath = MetadataGenerationUtils.getPathFromProject(getLayout(), GRADLEW); + + List packageList = (packages == null || packages.isBlank() || packages.equals("-")) + ? List.of() + : Arrays.stream(packages.split(",")) + .map(String::trim) + .filter(s -> !s.isEmpty()) + .collect(Collectors.toList()); + + MetadataGenerationUtils.addUserCodeFilterFile(testsDirectory, packageList); + MetadataGenerationUtils.addAgentConfigBlock(testsDirectory); + MetadataGenerationUtils.collectMetadata(getExecOperations(), testsDirectory, getLayout(), coordinates, gradlewPath); + } +} diff --git a/tests/tck-build-logic/src/main/java/org/graalvm/internal/tck/utils/MetadataGenerationUtils.java b/tests/tck-build-logic/src/main/java/org/graalvm/internal/tck/utils/MetadataGenerationUtils.java new file mode 100644 index 000000000..e92ebf0e6 --- /dev/null +++ b/tests/tck-build-logic/src/main/java/org/graalvm/internal/tck/utils/MetadataGenerationUtils.java @@ -0,0 +1,126 @@ +package org.graalvm.internal.tck.utils; + +import com.fasterxml.jackson.core.util.DefaultIndenter; +import com.fasterxml.jackson.core.util.DefaultPrettyPrinter; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import org.graalvm.internal.tck.CoordinateUtils; +import org.gradle.api.file.ProjectLayout; +import org.gradle.process.ExecOperations; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardOpenOption; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.ArrayList; + +/** + * Static utility class for operations shared by ContributionTask and GenerateMetadataTask. + */ +public final class MetadataGenerationUtils { + + public static final String BUILD_FILE = "build.gradle"; + private static final String USER_CODE_FILTER_FILE = "user-code-filter.json"; + + private static final ObjectMapper objectMapper = new ObjectMapper().enable(SerializationFeature.INDENT_OUTPUT); + + private MetadataGenerationUtils() { + // utility + } + + public static Path getPathFromProject(ProjectLayout layout, String fileName) { + return layout.getProjectDirectory().file(fileName).getAsFile().toPath(); + } + + public static void addUserCodeFilterFile(Path testsDirectory, List packages) throws IOException { + InteractiveTaskUtils.printUserInfo("Generating " + USER_CODE_FILTER_FILE); + List> filterFileRules = new ArrayList<>(); + + // add exclude classes + filterFileRules.add(Map.of("excludeClasses", "**")); + + // add include classes + packages.forEach(p -> filterFileRules.add(Map.of("includeClasses", p + ".**"))); + + DefaultPrettyPrinter prettyPrinter = new DefaultPrettyPrinter(); + prettyPrinter.indentArraysWith(DefaultIndenter.SYSTEM_LINEFEED_INSTANCE); + File out = testsDirectory.resolve(USER_CODE_FILTER_FILE).toFile(); + objectMapper.writer(prettyPrinter).writeValue(out, Map.of("rules", filterFileRules)); + } + + public static void addAgentConfigBlock(Path testsDirectory) throws IOException { + Path buildFilePath = testsDirectory.resolve(BUILD_FILE); + InteractiveTaskUtils.printUserInfo("Configuring agent block in: " + BUILD_FILE); + + if (!Files.isRegularFile(buildFilePath)) { + throw new RuntimeException("Cannot add agent block to " + buildFilePath + ". Please check if a " + BUILD_FILE + " exists on that location."); + } + + try (InputStream stream = MetadataGenerationUtils.class.getResourceAsStream("/contributing/agent.template")) { + if (stream == null) { + throw new RuntimeException("Cannot find template for the graalvm configuration block"); + } + + String content = System.lineSeparator() + (new String(stream.readAllBytes(), StandardCharsets.UTF_8)); + writeToFile(buildFilePath, content, StandardOpenOption.APPEND); + } + } + + public static void collectMetadata(ExecOperations execOps, Path testsDirectory, ProjectLayout layout, String coordinates, Path gradlew) { + Path metadataDirectory = MetadataGenerationUtils.computeMetadataDirectory(layout, coordinates); + + InteractiveTaskUtils.printUserInfo("Generating metadata"); + invokeCommand(execOps, gradlew.toString(), List.of("-Pagent", "test"), "Cannot generate metadata", testsDirectory); + + InteractiveTaskUtils.printUserInfo("Performing metadata copy"); + invokeCommand(execOps, gradlew + " metadataCopy --task test --dir " + metadataDirectory, "Cannot perform metadata copy", testsDirectory); + } + + public static void writeToFile(Path path, String content, StandardOpenOption writeOption) throws IOException { + Files.createDirectories(path.getParent()); + Files.writeString(path, content, StandardCharsets.UTF_8, writeOption); + } + + public static void invokeCommand(ExecOperations execOps, String command, String errorMessage) { + invokeCommand(execOps, command, errorMessage, null); + } + + public static void invokeCommand(ExecOperations execOps, String command, String errorMessage, Path workingDirectory) { + String[] commandParts = command.split(" "); + String executable = commandParts[0]; + + List args = List.of(Arrays.copyOfRange(commandParts, 1, commandParts.length)); + invokeCommand(execOps, executable, args, errorMessage, workingDirectory); + } + + public static void invokeCommand(ExecOperations execOps, String executable, List args, String errorMessage, Path workingDirectory) { + ByteArrayOutputStream execOutput = new ByteArrayOutputStream(); + var result = execOps.exec(execSpec -> { + if (workingDirectory != null) { + execSpec.setWorkingDir(workingDirectory); + } + execSpec.setExecutable(executable); + execSpec.setArgs(args); + execSpec.setStandardOutput(execOutput); + }); + + if (result.getExitValue() != 0) { + throw new RuntimeException(errorMessage + ". See: " + execOutput); + } + } + + public static Path computeTestsDirectory(ProjectLayout layout, String coordinates) { + return getPathFromProject(layout, CoordinateUtils.replace("tests/src/$group$/$artifact$/$version$", CoordinateUtils.fromString(coordinates))); + } + + public static Path computeMetadataDirectory(ProjectLayout layout, String coordinates) { + return getPathFromProject(layout, CoordinateUtils.replace("metadata/$group$/$artifact$/$version$", CoordinateUtils.fromString(coordinates))); + } +}