diff --git a/src/org/rascalmpl/library/util/Maven.java b/src/org/rascalmpl/library/util/Maven.java new file mode 100644 index 0000000000..f71ceb2875 --- /dev/null +++ b/src/org/rascalmpl/library/util/Maven.java @@ -0,0 +1,84 @@ +package org.rascalmpl.library.util; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Collections; +import java.util.List; + +import org.apache.maven.cli.CliRequest; +import org.apache.maven.cli.MavenCli; +import org.codehaus.plexus.classworlds.ClassWorld; +import org.rascalmpl.uri.URIResolverRegistry; +import org.rascalmpl.uri.URIUtil; + +import io.usethesource.vallang.ISourceLocation; + +public class Maven { + /** + * Calls maven with the provided arguments. The working directory will be set to `manifestRoot`, + * which should contain a `pom.xml` file. If `outputFile` refers to an existing file, its contents + * will the read and returned after Maven concludes. + * + * Maven's output is fully suppressed. However, it is often possible to redirect (parts of) the output + * to a file. For instance, the output of `mvn dependency:build-classpath` can be redicted to a file + * by providing an additional argument `-Dmdep.outputFile=/path/to/file`. + */ + public static List runCommand(List args, ISourceLocation manifestRoot, Path outputFile) { + try { + ISourceLocation pomxml = URIUtil.getChildLocation(manifestRoot, "pom.xml"); + pomxml = URIResolverRegistry.getInstance().logicalToPhysical(pomxml); + manifestRoot = URIResolverRegistry.getInstance().logicalToPhysical(manifestRoot); + + if (!"file".equals(manifestRoot.getScheme())) { + throw new IllegalArgumentException("`manifestRoot` could not be resolved"); + } + + if (!URIResolverRegistry.getInstance().exists(pomxml)) { + throw new IllegalArgumentException("`manifestRoot` does not contain pom.xml"); + } + + var maven = new MavenCli(); + maven.doMain(buildRequest(args.toArray(String[]::new), manifestRoot)); + + if (outputFile != null && Files.exists(outputFile)) { + return Files.readAllLines(outputFile); + } + } catch (IOException | ReflectiveOperationException e) { + // Fall through to return the empty list + } + + return Collections.emptyList(); + } + + /** + * Calls maven with the provided arguments. The working directory will be set to `manifestRoot`, + * which should contain a `pom.xml` file. Maven's output is fully suppressed. + */ + public static void runCommand(List args, ISourceLocation manifestRoot) { + runCommand(args, manifestRoot, null); + } + + private static void setField(CliRequest req, String fieldName, Object value) throws ReflectiveOperationException { + var field = CliRequest.class.getDeclaredField(fieldName); + field.setAccessible(true); + field.set(req, value); + } + + private static CliRequest buildRequest(String[] args, ISourceLocation manifestRoot) throws ReflectiveOperationException { + // we need to set a field that the default class doesn't set + // it's a work around around a bug in the MavenCli code + var cons = CliRequest.class.getDeclaredConstructor(String[].class, ClassWorld.class); + cons.setAccessible(true); + var result = cons.newInstance(args, null); + var manifestRootFile = new File(manifestRoot.getPath()); + setField(result, "workingDirectory", manifestRootFile.getPath()); + setField(result, "multiModuleProjectDirectory", manifestRootFile); + return result; + } + + public static Path getTempFile(String kind) throws IOException { + return Files.createTempFile("rascal-maven-" + kind + "-", ".tmp"); + } +} diff --git a/src/org/rascalmpl/library/util/PathConfig.java b/src/org/rascalmpl/library/util/PathConfig.java index ea99e935a9..ccb479d824 100644 --- a/src/org/rascalmpl/library/util/PathConfig.java +++ b/src/org/rascalmpl/library/util/PathConfig.java @@ -6,7 +6,6 @@ import java.io.StringReader; import java.io.StringWriter; import java.net.URISyntaxException; -import java.nio.file.Files; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -16,9 +15,6 @@ import java.util.Objects; import java.util.Set; -import org.apache.maven.cli.CliRequest; -import org.apache.maven.cli.MavenCli; -import org.codehaus.plexus.classworlds.ClassWorld; import org.rascalmpl.interpreter.Configuration; import org.rascalmpl.interpreter.utils.RascalManifest; import org.rascalmpl.uri.ILogicalSourceLocationResolver; @@ -639,26 +635,11 @@ private static ISourceLocation setTargetScheme(ISourceLocation projectLoc) { */ private static IList getPomXmlCompilerClasspath(ISourceLocation manifestRoot) { try { - ISourceLocation pomxml = URIUtil.getChildLocation(manifestRoot, "pom.xml"); - pomxml = URIResolverRegistry.getInstance().logicalToPhysical(pomxml); - manifestRoot = URIResolverRegistry.getInstance().logicalToPhysical(manifestRoot); + var tempFile = Maven.getTempFile("classpath"); + var mavenOutput = Maven.runCommand(List.of("-quiet", "-o", "dependency:build-classpath", "-DincludeScope=compile", "-Dmdep.outputFile=" + tempFile.toString()), manifestRoot, tempFile); - if (!"file".equals(manifestRoot.getScheme())) { - return vf.list(); - } - - if (!URIResolverRegistry.getInstance().exists(pomxml)) { - return vf.list(); - } - - var maven = new MavenCli(); - var tempFile = Files.createTempFile("rascal-classpath-", ".tmp"); - - maven.doMain(buildRequest(new String[] {"-quiet", "-o", "dependency:build-classpath", "-DincludeScope=compile", "-Dmdep.outputFile=" + tempFile.toString()}, manifestRoot)); - - var foundClassPath = Files.readAllLines(tempFile).get(0); - - return Arrays.stream(foundClassPath.split(File.pathSeparator)) + // The classpath will be written to the temp file on a single line + return Arrays.stream(mavenOutput.get(0).split(File.pathSeparator)) .filter(fileName -> new File(fileName).exists()) .map(elem -> { try { @@ -671,28 +652,11 @@ private static IList getPomXmlCompilerClasspath(ISourceLocation manifestRoot) { .filter(Objects::nonNull) .collect(vf.listWriter()); } - catch (IOException | RuntimeException | ReflectiveOperationException e) { + catch (IOException | RuntimeException e) { return vf.list(); } } - private static void setField(CliRequest req, String fieldName, Object value) throws ReflectiveOperationException { - var field = CliRequest.class.getDeclaredField(fieldName); - field.setAccessible(true); - field.set(req, value); - } - - private static CliRequest buildRequest(String[] args, ISourceLocation manifestRoot) throws ReflectiveOperationException { - // we need to set a field that the default class doesn't set - // it's a work around around a bug in the MavenCli code - var cons = CliRequest.class.getDeclaredConstructor(String[].class, ClassWorld.class); - cons.setAccessible(true); - var result = cons.newInstance(args, null); - setField(result, "workingDirectory", new File(manifestRoot.getPath()).getPath()); - setField(result, "multiModuleProjectDirectory", new File(manifestRoot.getPath())); - return result; - } - public ISourceLocation getBin() { return bin; }