diff --git a/build.gradle.kts b/build.gradle.kts index ed7b22a0..16d8a2ac 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -11,6 +11,7 @@ configurations.compileClasspath { dependencies { implementation("org.ow2.asm:asm:9.9") + implementation("com.github.javaparser:javaparser-core:3.28.0") compileOnly("org.gradlex:extra-java-module-info:1.13.1") compileOnly("com.autonomousapps:dependency-analysis-gradle-plugin:3.5.1") } diff --git a/gradle/verification-keyring.keys b/gradle/verification-keyring.keys index cba99fe6..5bd5d14b 100644 --- a/gradle/verification-keyring.keys +++ b/gradle/verification-keyring.keys @@ -1754,3 +1754,16 @@ CgkQuhmUn6JgkhZmBQEA3b5QhIg4LhToSGJ0sI3mPr270z+Sefyl/L8s2i7ZJKEA /1su4aPLl+FaeuZHpInOy991PXFh+IJICL1irc2DfV4G =stfL -----END PGP PUBLIC KEY BLOCK----- + +id 6DE9B8077FBB2F8A019F4904BD17A565509DEE20 +uid jean-pierre.lerbscher@jperf.com +-----BEGIN PGP PUBLIC KEY BLOCK----- + +xjMEZl2j9RYJKwYBBAHaRw8BAQdAPzZyNkr92xKYzBrTOsN8Fwy9l2W0ez4Hu0t3 +/MoohtG0H2plYW4tcGllcnJlLmxlcmJzY2hlckBqcGVyZi5jb23OOARmXaP1Egor +BgEEAZdVAQUBAQdAoTUzjZPhQLyzRo9jIO8TrgC+mfazNL5gB+fOWhB//T4DAQgH +wn4EGBYKACYWIQRt6bgHf7svigGfSQS9F6VlUJ3uIAUCZl2j9QIbDAUJBaOKqwAK +CRC9F6VlUJ3uIIpxAP9MihNcqlK2wPp2uURiLmw16dN3o50gxWeLMjBxethg0gD/ +VLRHao8huHsPY9XMrgbHSNLZOT7geBJOdvwrTRTy3A0= +=BzAO +-----END PGP PUBLIC KEY BLOCK----- diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index aee68149..e816a246 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -17,9 +17,11 @@ - + + + diff --git a/src/main/java/org/gradlex/javamodule/dependencies/internal/utils/ModuleInfo.java b/src/main/java/org/gradlex/javamodule/dependencies/internal/utils/ModuleInfo.java index c3fe5232..0a640f5a 100644 --- a/src/main/java/org/gradlex/javamodule/dependencies/internal/utils/ModuleInfo.java +++ b/src/main/java/org/gradlex/javamodule/dependencies/internal/utils/ModuleInfo.java @@ -3,12 +3,25 @@ import static org.gradlex.javamodule.dependencies.internal.utils.ModuleNamingUtil.sourceSetToModuleName; +import com.github.javaparser.JavaParser; +import com.github.javaparser.ast.CompilationUnit; +import com.github.javaparser.ast.Node; +import com.github.javaparser.ast.expr.Name; +import com.github.javaparser.ast.modules.ModuleDeclaration; +import com.github.javaparser.ast.modules.ModuleDirective; +import com.github.javaparser.ast.modules.ModuleProvidesDirective; +import com.github.javaparser.ast.modules.ModuleRequiresDirective; +import com.github.javaparser.ast.nodeTypes.NodeWithIdentifier; +import com.github.javaparser.ast.nodeTypes.NodeWithName; import java.io.Serializable; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; import org.jspecify.annotations.Nullable; public class ModuleInfo implements Serializable { @@ -29,18 +42,34 @@ public String literal() { public static final ModuleInfo EMPTY = new ModuleInfo(""); - private String moduleName = ""; + private final String moduleName; + private final Map imports; private final List requires = new ArrayList<>(); private final List requiresTransitive = new ArrayList<>(); private final List requiresStatic = new ArrayList<>(); private final List requiresStaticTransitive = new ArrayList<>(); private final List requiresRuntime = new ArrayList<>(); + private final Map> provides = new LinkedHashMap<>(); public ModuleInfo(String moduleInfoFileContent) { - boolean insideComment = false; - for (String line : moduleInfoFileContent.split("\n")) { - insideComment = parse(line, insideComment); - } + Optional result = + new JavaParser().parse(moduleInfoFileContent).getResult(); + if (!result.isPresent() || !result.get().getModule().isPresent()) { + moduleName = ""; + imports = Collections.emptyMap(); + return; + } + + ModuleDeclaration moduleDeclaration = result.get().getModule().get(); + moduleName = moduleDeclaration.getNameAsString(); + imports = processImports(result.get()); + processDirectives(moduleDeclaration.getDirectives()); + } + + private Map processImports(CompilationUnit cu) { + return cu.getImports().stream() + .map(NodeWithName::getName) + .collect(Collectors.toMap(NodeWithIdentifier::getId, Node::toString)); } public String getModuleName() { @@ -66,6 +95,10 @@ public List get(Directive directive) { return Collections.emptyList(); } + public Map> getProvides() { + return provides; + } + @Nullable public String moduleNamePrefix(String projectName, String sourceSetName, boolean fail) { if (moduleName.equals(projectName)) { @@ -90,44 +123,47 @@ public String moduleNamePrefix(String projectName, String sourceSetName, boolean return null; } - /** - * @return true, if we are inside a multi-line comment after this line - */ - private boolean parse(String moduleLine, boolean insideComment) { - if (insideComment) { - return !moduleLine.contains("*/"); + private void processDirectives(List directives) { + for (ModuleDirective d : directives) { + if (d instanceof ModuleRequiresDirective) { + ModuleRequiresDirective directive = (ModuleRequiresDirective) d; + String identifier = directive.getNameAsString(); + if (directive.isStatic() && directive.isTransitive()) { + requiresStaticTransitive.add(identifier); + } else if (directive.isTransitive()) { + requiresTransitive.add(identifier); + } else if (directive.isStatic()) { + requiresStatic.add(identifier); + } else if (isRuntime(directive)) { + requiresRuntime.add(identifier); + } else { + requires.add(identifier); + } + } + if (d instanceof ModuleProvidesDirective) { + ModuleProvidesDirective directive = (ModuleProvidesDirective) d; + String name = qualifiedName(directive.getName()); + List with = provides.computeIfAbsent(name, k -> new ArrayList<>()); + with.addAll( + directive.getWith().stream().map(this::qualifiedName).collect(Collectors.toList())); + } } + } - List tokens = Arrays.asList(moduleLine - .replace(";", "") - .replace("{", "") - .replace("}", "") - .replace(RUNTIME_KEYWORD, "runtime") - .replaceAll("/\\*.*?\\*/", " ") - .trim() - .split("\\s+")); - int singleLineCommentStartIndex = tokens.indexOf("//"); - if (singleLineCommentStartIndex >= 0) { - tokens = tokens.subList(0, singleLineCommentStartIndex); - } + private static boolean isRuntime(ModuleRequiresDirective directive) { + return directive + .getName() + .getComment() + .map(c -> "runtime".equals(c.getContent().trim())) + .orElse(false); + } - if (tokens.contains("module")) { - moduleName = tokens.get(tokens.size() - 1); - } - if (tokens.size() > 1 && tokens.get(0).equals("requires")) { - if (tokens.size() > 3 && tokens.contains("static") && tokens.contains("transitive")) { - requiresStaticTransitive.add(tokens.get(3)); - } else if (tokens.size() > 2 && tokens.contains("transitive")) { - requiresTransitive.add(tokens.get(2)); - } else if (tokens.size() > 2 && tokens.contains("static")) { - requiresStatic.add(tokens.get(2)); - } else if (tokens.size() > 2 && tokens.contains("runtime")) { - requiresRuntime.add(tokens.get(2)); - } else { - requires.add(tokens.get(1)); - } + private String qualifiedName(Name name) { + if (imports.containsKey(name.getId())) { + return imports.get(name.getId()); + } else { + return name.toString(); } - return moduleLine.lastIndexOf("/*") > moduleLine.lastIndexOf("*/"); } @Override diff --git a/src/main/java/org/gradlex/javamodule/dependencies/internal/utils/ModuleInfoCache.java b/src/main/java/org/gradlex/javamodule/dependencies/internal/utils/ModuleInfoCache.java index bb17c1ed..3073a020 100644 --- a/src/main/java/org/gradlex/javamodule/dependencies/internal/utils/ModuleInfoCache.java +++ b/src/main/java/org/gradlex/javamodule/dependencies/internal/utils/ModuleInfoCache.java @@ -101,11 +101,12 @@ public Collection getAllLocalModules() { } private boolean maybePutModuleInfo(File folder, ProviderFactory providers) { + if (moduleInfo.containsKey(folder)) { + return true; + } Provider moduleInfoProvider = provideModuleInfo(folder, providers); if (moduleInfoProvider.isPresent()) { - if (!moduleInfo.containsKey(folder)) { - moduleInfo.put(folder, moduleInfoProvider.get()); - } + moduleInfo.put(folder, moduleInfoProvider.get()); return true; } return false;