Skip to content

Commit

Permalink
fix: implement configurable module path (#5325)
Browse files Browse the repository at this point in the history
  • Loading branch information
SirYwell authored and I-Al-Istannen committed Apr 3, 2024
1 parent 93a1ac6 commit 4a8df19
Show file tree
Hide file tree
Showing 11 changed files with 129 additions and 8 deletions.
19 changes: 19 additions & 0 deletions src/main/java/spoon/SpoonModelBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,25 @@ interface InputType {
*/
void setSourceClasspath(String... classpath);

/**
* Gets the module path used for sourcing the input modules.
* The returned list is immutable and does not contain null values.
*
* @return A list of strings representing the module path. Each string element
* is the path to a directory or a module jar file.
*/
List<String> getSourceModulePath();

/**
* Sets the module path that is used to build/compile the input sources.
* This is the equivalent to the {@code --module-path} option of {@code javac} and {@code java} executables.
*
* @param sourceModulePath The new module path to be set. Each string element
* should be the path to a directory or a module jar file.
* @throws NullPointerException if the argument is null or an element of the list is null.
*/
void setSourceModulePath(List<String> sourceModulePath);

/**
* Gets the classpath that is used to build the template sources.
*
Expand Down
20 changes: 20 additions & 0 deletions src/main/java/spoon/compiler/Environment.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

import java.io.File;
import java.nio.charset.Charset;
import java.util.List;
import java.util.function.Supplier;

import org.jspecify.annotations.Nullable;
Expand Down Expand Up @@ -308,6 +309,25 @@ public interface Environment {
*/
void setSourceClasspath(String[] sourceClasspath);

/**
* Gets the module path used for sourcing the input modules.
* The returned list is immutable and does not contain null values.
*
* @return A list of strings representing the module path. Each string element
* is the path to a directory or a module jar file.
*/
List<String> getSourceModulePath();

/**
* Sets the module path that is used to build/compile the input sources.
* This is the equivalent to the {@code --module-path} option of {@code javac} and {@code java} executables.
*
* @param sourceModulePath The new module path to be set. Each string element
* should be the path to a directory or a module jar file.
* @throws NullPointerException if the argument is null or an element of the list is null.
*/
void setSourceModulePath(List<String> sourceModulePath);

/**
* Sets the option "noclasspath", use with caution (see explanation below).
*
Expand Down
25 changes: 25 additions & 0 deletions src/main/java/spoon/compiler/builder/ClasspathOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
package spoon.compiler.builder;

import java.io.File;
import java.util.List;

public class ClasspathOptions<T extends ClasspathOptions<T>> extends Options<T> {
public ClasspathOptions() {
Expand All @@ -30,6 +31,30 @@ public T classpath(String... classpaths) {
return classpath(join(File.pathSeparator, classpaths));
}

/**
* Adds the specified module path to the list of arguments.
*
* @param modulePath the module path to add
* @return the instance of the class calling this method
*/
public T modulePath(String modulePath) {
args.add("--module-path");
args.add(modulePath);
return myself;
}

/**
* Adds the specified list of module paths to the list of arguments.
*
* @param modulePaths the list of module paths to add
* @return the instance of the class calling this method
*/
public T modulePath(List<String> modulePaths) {
args.add("--module-path");
args.add(String.join(File.pathSeparator, modulePaths));
return myself;
}

public T bootclasspath(String bootclasspath) {
if (bootclasspath == null) {
return myself;
Expand Down
11 changes: 11 additions & 0 deletions src/main/java/spoon/support/StandardEnvironment.java
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ public void setPrettyPrintingMode(PRETTY_PRINTING_MODE prettyPrintingMode) {
private int warningCount = 0;

private String[] sourceClasspath = null;
private List<String> sourceModulePath = List.of();

private boolean preserveLineNumbers = false;

Expand Down Expand Up @@ -489,6 +490,16 @@ public void setSourceClasspath(String[] sourceClasspath) {
this.inputClassloader = null;
}

@Override
public List<String> getSourceModulePath() {
return this.sourceModulePath;
}

@Override
public void setSourceModulePath(List<String> sourceModulePath) {
this.sourceModulePath = List.copyOf(sourceModulePath); // implicit null check on list and its elements
}

private void verifySourceClasspath(String[] sourceClasspath) throws InvalidClassPathException {
for (String classPathElem : sourceClasspath) {
// preconditions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,16 @@ public void setSourceClasspath(String... classpath) {
getEnvironment().setSourceClasspath(classpath);
}

@Override
public List<String> getSourceModulePath() {
return getEnvironment().getSourceModulePath();
}

@Override
public void setSourceModulePath(List<String> sourceModulePath) {
getEnvironment().setSourceModulePath(sourceModulePath);
}

@Override
public String[] getTemplateClasspath() {
return templateClasspath;
Expand All @@ -333,7 +343,7 @@ public Factory getFactory() {
}

protected boolean buildSources(JDTBuilder jdtBuilder) {
return buildUnitsAndModel(jdtBuilder, sources, getSourceClasspath(), "");
return buildUnitsAndModel(jdtBuilder, sources, getSourceClasspath(), getSourceModulePath(), "");
}

protected JDTBatchCompiler createBatchCompiler() {
Expand All @@ -353,7 +363,7 @@ protected JDTBatchCompiler createBatchCompiler(InputType... types) {
}

protected boolean buildTemplates(JDTBuilder jdtBuilder) {
CompilationUnitDeclaration[] units = buildUnits(jdtBuilder, templates, getTemplateClasspath(), "template ");
CompilationUnitDeclaration[] units = buildUnits(jdtBuilder, templates, getTemplateClasspath(), List.of(), "template ");
buildModel(units, factory.Templates());
return true;
}
Expand All @@ -366,8 +376,13 @@ protected boolean buildTemplates(JDTBuilder jdtBuilder) {
* @param debugMessagePrefix Useful to help debugging
* @return true if the model has been built without errors
*/
protected boolean buildUnitsAndModel(JDTBuilder jdtBuilder, SpoonFolder sourcesFolder, String[] classpath, String debugMessagePrefix) {
CompilationUnitDeclaration[] units = buildUnits(jdtBuilder, sourcesFolder, classpath, debugMessagePrefix);
protected boolean buildUnitsAndModel(
JDTBuilder jdtBuilder,
SpoonFolder sourcesFolder,
String[] classpath,
List<String> modulePath,
String debugMessagePrefix) {
CompilationUnitDeclaration[] units = buildUnits(jdtBuilder, sourcesFolder, classpath, modulePath, debugMessagePrefix);

// here we build the model in the template factory
buildModel(units, factory);
Expand All @@ -385,7 +400,12 @@ protected boolean buildUnitsAndModel(JDTBuilder jdtBuilder, SpoonFolder sourcesF
* @param debugMessagePrefix Useful to help debugging
* @return All compilationUnitDeclaration from JDT found in source folder
*/
protected CompilationUnitDeclaration[] buildUnits(JDTBuilder jdtBuilder, SpoonFolder sourcesFolder, String[] classpath, String debugMessagePrefix) {
protected CompilationUnitDeclaration[] buildUnits(
JDTBuilder jdtBuilder,
SpoonFolder sourcesFolder,
String[] classpath,
List<String> modulePath,
String debugMessagePrefix) {
List<SpoonFile> sourceFiles = Collections.unmodifiableList(sourcesFolder.getAllJavaFiles());
if (sourceFiles.isEmpty()) {
return EMPTY_RESULT;
Expand All @@ -395,7 +415,10 @@ protected CompilationUnitDeclaration[] buildUnits(JDTBuilder jdtBuilder, SpoonFo

String[] args;
if (jdtBuilder == null) {
ClasspathOptions classpathOptions = new ClasspathOptions().encoding(this.getEnvironment().getEncoding().displayName()).classpath(classpath);
ClasspathOptions<?> classpathOptions = new ClasspathOptions<>()
.encoding(this.getEnvironment().getEncoding().displayName())
.classpath(classpath)
.modulePath(modulePath);
ComplianceOptions complianceOptions = new ComplianceOptions().compliance(javaCompliance);
if (factory.getEnvironment().isPreviewFeaturesEnabled()) {
complianceOptions.enablePreview();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ public boolean build(JDTBuilder builder) {

@Override
protected boolean buildSources(JDTBuilder jdtBuilder) {
return buildUnitsAndModel(jdtBuilder, sources, getSourceClasspath(), "snippet ");
return buildUnitsAndModel(jdtBuilder, sources, getSourceClasspath(), getSourceModulePath(), "snippet ");
}

@Override
Expand Down
16 changes: 16 additions & 0 deletions src/test/java/spoon/LauncherTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,13 @@
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.junit.jupiter.api.Test;
import spoon.compiler.Environment;
import spoon.reflect.CtModel;
import spoon.reflect.declaration.CtModule;
import spoon.reflect.visitor.DefaultJavaPrettyPrinter;
import spoon.support.JavaOutputProcessor;
import spoon.support.compiler.VirtualFile;
Expand Down Expand Up @@ -161,4 +163,18 @@ public void testClasspathURLWithSpaces() throws MalformedURLException {

assertTrue(model.getAllTypes().stream().anyMatch(ct -> ct.getQualifiedName().equals("Foo")), "CtTxpe 'Foo' not present in model");
}

@Test
void testModulesInJars() {
Launcher spoon = new Launcher();
Environment environment = spoon.getEnvironment();
environment.setSourceModulePath(List.of("src/test/resources/modules/error-reporting-java-1.0.1.jar"));
environment.setNoClasspath(false);
environment.setComplianceLevel(11);
spoon.addInputResource(Path.of("src/test/resources/modules/5324").toString());
CtModel ctModel = assertDoesNotThrow(spoon::buildModel);
// unnamed and dummy.module
assertEquals(2, ctModel.getAllModules().size());

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public void testOrderCompilationUnits() {
launcher.addInputResource("./src/main/java");
JDTBasedSpoonCompiler spoonCompiler = (JDTBasedSpoonCompiler) launcher.getModelBuilder();

CompilationUnitDeclaration[] compilationUnitDeclarations = spoonCompiler.buildUnits(null, spoonCompiler.sources, spoonCompiler.getSourceClasspath(), "");
CompilationUnitDeclaration[] compilationUnitDeclarations = spoonCompiler.buildUnits(null, spoonCompiler.sources, spoonCompiler.getSourceClasspath(), spoonCompiler.getSourceModulePath(), "");

List<CompilationUnitDeclaration> compilationUnitDeclarations1 = spoonCompiler.sortCompilationUnits(compilationUnitDeclarations);

Expand Down
3 changes: 3 additions & 0 deletions src/test/resources/modules/5324/module-info.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module dummy.module {
requires error.reporting.java;
}
4 changes: 4 additions & 0 deletions src/test/resources/modules/5324/mypkg/Dummy.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package mypkg;

class Dummy {
}
Binary file not shown.

0 comments on commit 4a8df19

Please sign in to comment.