Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package plexus-languages for 
openSUSE:Factory checked in at 2026-11-06 19:58:46
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/plexus-languages (Old)
 and      /work/SRC/openSUSE:Factory/.plexus-languages.new.3759290 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "plexus-languages"

Fri Nov  6 19:58:46 2026 rev:11 rq:1395848 version:1.7.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/plexus-languages/plexus-languages.changes        
2026-09-29 17:50:58.349195354 +0200
+++ 
/work/SRC/openSUSE:Factory/.plexus-languages.new.3759290/plexus-languages.changes
   2026-11-06 20:06:59.721920080 +0100
@@ -1,0 +2,28 @@
+Fri Nov  6 13:02:18 UTC 2026 - Fridrich Strba <[email protected]>
+
+- Upgrade to upstream version 1.7.0
+  * New features and improvements
+    + Read module descriptors without ASM
+    + Choose the versioned module descriptor by the target release
+  * Bug Fixes
+    + Read the module descriptor of a multi-release output directory
+    + Read module descriptors newer than ASM supports
+  * Documentation updates
+    + Drop the Source Xref menu item
+    + Stop pinning a version in the README snippet
+  * Maintenance
+    + Target 1.7.0 for the next release
+    + Drop the Source Xref menu item
+  * Build
+    + Let the extra matrix entries take the default Maven version
+    + Use the shared release-drafter workflow
+    + Build on JDK 26, 27 and 28-ea
+    + Ignore Mockito 5 in Dependabot
+    + Draft the version the labels imply, not always a patch
+  * Dependency updates
+    + Bump org.ow2.asm:asm from 9.10.1 to 9.11
+- Modified patch:
+  * plexus-languages-atinject.patch
+    + rediff
+
+-------------------------------------------------------------------
@@ -65 +93 @@
-    + Fix typo module (#125) @Thihup
+    + Fix typo module

Old:
----
  plexus-languages-1.6.0.tar.xz

New:
----
  plexus-languages-1.7.0.tar.xz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ plexus-languages.spec ++++++
--- /var/tmp/diff_new_pack.ULOayA/_old  2026-11-06 20:07:09.451327809 +0100
+++ /var/tmp/diff_new_pack.ULOayA/_new  2026-11-06 20:07:09.456328018 +0100
@@ -20,7 +20,7 @@
 # binaries are java 8 compatible
 %define __requires_exclude java-headless
 Name:           plexus-languages
-Version:        1.6.0
+Version:        1.7.0
 Release:        0
 Summary:        Plexus Languages
 License:        Apache-2.0

++++++ _scmsync.obsinfo ++++++
--- /var/tmp/diff_new_pack.ULOayA/_old  2026-11-06 20:07:09.583333341 +0100
+++ /var/tmp/diff_new_pack.ULOayA/_new  2026-11-06 20:07:09.593333760 +0100
@@ -1,6 +1,6 @@
-mtime: 1790601217
-commit: f4accaa25348904f295a1231a8a4fb5f89ce36df37fcb8180261e981937b6a96
+mtime: 1793970328
+commit: 32d4aa6175503a05b35ec4eb2eb251993ac4890c00f58b59cb7ef808c8ae0a66
 url: https://src.opensuse.org/java-packages/plexus-languages
-revision: f4accaa25348904f295a1231a8a4fb5f89ce36df37fcb8180261e981937b6a96
+revision: 32d4aa6175503a05b35ec4eb2eb251993ac4890c00f58b59cb7ef808c8ae0a66
 projectscmsync: https://src.opensuse.org/java-packages/_ObsPrj
 

++++++ _service ++++++
--- /var/tmp/diff_new_pack.ULOayA/_old  2026-11-06 20:07:09.648336065 +0100
+++ /var/tmp/diff_new_pack.ULOayA/_new  2026-11-06 20:07:09.655336358 +0100
@@ -2,7 +2,7 @@
        <service name="tar_scm" mode="disabled">
                <param name="scm">git</param>
                <param 
name="url">https://github.com/codehaus-plexus/plexus-languages.git</param>
-               <param name="revision">plexus-languages-1.6.0</param>
+               <param name="revision">plexus-languages-1.7.0</param>
                <param name="match-tag">plexus-languages-*</param>
                <param name="versionformat">@PARENT_TAG@</param>
                <param 
name="versionrewrite-pattern">plexus-languages-(.*)</param>

++++++ build.specials.obscpio ++++++

++++++ build.specials.obscpio ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/.gitignore new/.gitignore
--- old/.gitignore      1970-01-01 01:00:00.000000000 +0100
+++ new/.gitignore      2026-11-06 14:05:28.000000000 +0100
@@ -0,0 +1 @@
+.osc

++++++ plexus-java-build.xml ++++++
--- /var/tmp/diff_new_pack.ULOayA/_old  2026-11-06 20:07:09.973349685 +0100
+++ /var/tmp/diff_new_pack.ULOayA/_new  2026-11-06 20:07:09.983350104 +0100
@@ -10,7 +10,7 @@
 
   <property name="project.groupId" value="org.codehaus.plexus"/>
   <property name="project.artifactId" value="plexus-java"/>
-  <property name="project.version" value="1.6.0"/>
+  <property name="project.version" value="1.7.0"/>
 
   <property name="compiler.release" value="8"/>
   <property name="compiler.source" value="1.${compiler.release}"/>

++++++ plexus-languages-1.6.0.tar.xz -> plexus-languages-1.7.0.tar.xz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/plexus-languages-1.6.0/.github/dependabot.yml 
new/plexus-languages-1.7.0/.github/dependabot.yml
--- old/plexus-languages-1.6.0/.github/dependabot.yml   2026-08-19 
10:11:21.000000000 +0200
+++ new/plexus-languages-1.7.0/.github/dependabot.yml   2026-11-06 
11:37:41.000000000 +0100
@@ -4,6 +4,10 @@
     directory: "/"
     schedule:
       interval: "daily"
+    ignore:
+      # Mockito 5 requires Java 11; the build still runs on JDK 8
+      - dependency-name: "org.mockito:*"
+        versions: [">=5"]
   - package-ecosystem: "github-actions"
     directory: "/"
     schedule:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/plexus-languages-1.6.0/.github/release-drafter.yml 
new/plexus-languages-1.7.0/.github/release-drafter.yml
--- old/plexus-languages-1.6.0/.github/release-drafter.yml      1970-01-01 
01:00:00.000000000 +0100
+++ new/plexus-languages-1.7.0/.github/release-drafter.yml      2026-11-06 
11:37:41.000000000 +0100
@@ -0,0 +1,10 @@
+# Configuration for Release Drafter: 
https://github.com/release-drafter/release-drafter
+# Categories, labels and the version-resolver come from the shared org config.
+_extends: .github
+
+# Tags here are prefixed, so tag-prefix is what lets the drafter parse the
+# version out of the last tag. Without it the drafter reads what it can from 
the
+# tag string, and plexus-i18n once drafted 18.0.1 by finding the 18 in its 
name.
+tag-prefix: plexus-languages-
+tag-template: plexus-languages-$RESOLVED_VERSION
+name-template: $RESOLVED_VERSION
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/plexus-languages-1.6.0/.github/workflows/maven.yml 
new/plexus-languages-1.7.0/.github/workflows/maven.yml
--- old/plexus-languages-1.6.0/.github/workflows/maven.yml      2026-08-19 
10:11:21.000000000 +0200
+++ new/plexus-languages-1.7.0/.github/workflows/maven.yml      2026-11-06 
11:37:41.000000000 +0100
@@ -17,12 +17,19 @@
 
 name: GitHub CI
 
-on: [push, pull_request]
+on: [push, pull_request, workflow_dispatch]
 
 jobs:
   build:
     name: Build it
     uses: codehaus-plexus/.github/.github/workflows/maven.yml@master
+    with:
+      matrix-include: >
+        [
+          { "os": "ubuntu-latest", "jdk": "26", "distribution": "zulu" },
+          { "os": "ubuntu-latest", "jdk": "27", "distribution": "zulu" },
+          { "os": "ubuntu-latest", "jdk": "28-ea", "distribution": "temurin" }
+        ]
 
 #  deploy:
 #    name: Deploy
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/plexus-languages-1.6.0/.github/workflows/release-drafter.yml 
new/plexus-languages-1.7.0/.github/workflows/release-drafter.yml
--- old/plexus-languages-1.6.0/.github/workflows/release-drafter.yml    
2026-08-19 10:11:21.000000000 +0200
+++ new/plexus-languages-1.7.0/.github/workflows/release-drafter.yml    
2026-11-06 11:37:41.000000000 +0100
@@ -3,10 +3,8 @@
   push:
     branches:
       - master
+  workflow_dispatch:
+
 jobs:
   update_release_draft:
-    runs-on: ubuntu-latest
-    steps:
-      - uses: release-drafter/[email protected]
-        env:
-          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+    uses: codehaus-plexus/.github/.github/workflows/release-drafter.yml@master
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/plexus-languages-1.6.0/README.md 
new/plexus-languages-1.7.0/README.md
--- old/plexus-languages-1.6.0/README.md        2026-08-19 10:11:21.000000000 
+0200
+++ new/plexus-languages-1.7.0/README.md        2026-11-06 11:37:41.000000000 
+0100
@@ -26,7 +26,6 @@
 <dependency>
   <groupId>org.codehaus.plexus</groupId>
   <artifactId>plexus-java</artifactId>
-  <version>1.5.2</version>
 </dependency>
 ```
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/plexus-languages-1.6.0/plexus-java/pom.xml 
new/plexus-languages-1.7.0/plexus-java/pom.xml
--- old/plexus-languages-1.6.0/plexus-java/pom.xml      2026-08-19 
10:11:21.000000000 +0200
+++ new/plexus-languages-1.7.0/plexus-java/pom.xml      2026-11-06 
11:37:41.000000000 +0100
@@ -5,7 +5,7 @@
   <parent>
     <groupId>org.codehaus.plexus</groupId>
     <artifactId>plexus-languages</artifactId>
-    <version>1.6.0</version>
+    <version>1.7.0</version>
   </parent>
   <artifactId>plexus-java</artifactId>
 
@@ -13,11 +13,6 @@
 
   <dependencies>
     <dependency>
-      <groupId>org.ow2.asm</groupId>
-      <artifactId>asm</artifactId>
-      <version>9.10.1</version>
-    </dependency>
-    <dependency>
       <groupId>com.github.javaparser</groupId>
       <artifactId>javaparser-core</artifactId>
       <version>3.28.2</version>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/plexus-languages-1.6.0/plexus-java/src/main/java/org/codehaus/plexus/languages/java/jpms/AbstractBinaryModuleInfoParser.java
 
new/plexus-languages-1.7.0/plexus-java/src/main/java/org/codehaus/plexus/languages/java/jpms/AbstractBinaryModuleInfoParser.java
--- 
old/plexus-languages-1.6.0/plexus-java/src/main/java/org/codehaus/plexus/languages/java/jpms/AbstractBinaryModuleInfoParser.java
    2026-08-19 10:11:21.000000000 +0200
+++ 
new/plexus-languages-1.7.0/plexus-java/src/main/java/org/codehaus/plexus/languages/java/jpms/AbstractBinaryModuleInfoParser.java
    2026-11-06 11:37:41.000000000 +0100
@@ -39,7 +39,14 @@
     public JavaModuleDescriptor getModuleDescriptor(Path modulePath, 
JavaVersion jdkVersion) throws IOException {
         JavaModuleDescriptor descriptor;
         if (Files.isDirectory(modulePath)) {
-            try (InputStream in = 
Files.newInputStream(modulePath.resolve("module-info.class"))) {
+            Path moduleInfo = modulePath.resolve("module-info.class");
+            if (!Files.exists(moduleInfo)) {
+                Path versionedModuleInfo = findVersionedModuleInfo(modulePath, 
jdkVersion);
+                if (versionedModuleInfo != null) {
+                    moduleInfo = versionedModuleInfo;
+                }
+            }
+            try (InputStream in = Files.newInputStream(moduleInfo)) {
                 descriptor = parse(in);
             }
         } else {
@@ -57,8 +64,7 @@
                                 && "true"
                                         .equalsIgnoreCase(
                                                 
manifest.getMainAttributes().getValue("Multi-Release"))) {
-                            int javaVersion =
-                                    
Integer.parseInt(jdkVersion.asMajor().getValue(1));
+                            int javaVersion = jdkVersion.getMajorVersion();
 
                             for (int version = javaVersion; version >= 9; 
version--) {
                                 String resource = "META-INF/versions/" + 
version + "/module-info.class";
@@ -82,5 +88,25 @@
         return descriptor;
     }
 
+    /**
+     * Finds the module descriptor of a multi-release output directory, which 
has no {@code module-info.class} at its
+     * root but under {@code META-INF/versions/<N>}, the way a multi-release 
jar does.
+     *
+     * @param directory the output directory
+     * @param jdkVersion the highest version to consider
+     * @return the path of the highest applicable versioned descriptor, or 
{@code null} if there is none
+     */
+    static Path findVersionedModuleInfo(Path directory, JavaVersion 
jdkVersion) {
+        int javaVersion = jdkVersion.getMajorVersion();
+
+        for (int version = javaVersion; version >= 9; version--) {
+            Path moduleInfo = directory.resolve("META-INF/versions/" + version 
+ "/module-info.class");
+            if (Files.exists(moduleInfo)) {
+                return moduleInfo;
+            }
+        }
+        return null;
+    }
+
     abstract JavaModuleDescriptor parse(InputStream in) throws IOException;
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/plexus-languages-1.6.0/plexus-java/src/main/java/org/codehaus/plexus/languages/java/jpms/AsmModuleInfoParser.java
 
new/plexus-languages-1.7.0/plexus-java/src/main/java/org/codehaus/plexus/languages/java/jpms/AsmModuleInfoParser.java
--- 
old/plexus-languages-1.6.0/plexus-java/src/main/java/org/codehaus/plexus/languages/java/jpms/AsmModuleInfoParser.java
       2026-08-19 10:11:21.000000000 +0200
+++ 
new/plexus-languages-1.7.0/plexus-java/src/main/java/org/codehaus/plexus/languages/java/jpms/AsmModuleInfoParser.java
       1970-01-01 01:00:00.000000000 +0100
@@ -1,107 +0,0 @@
-package org.codehaus.plexus.languages.java.jpms;
-
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Set;
-
-import org.objectweb.asm.ClassReader;
-import org.objectweb.asm.ClassVisitor;
-import org.objectweb.asm.ModuleVisitor;
-import org.objectweb.asm.Opcodes;
-
-/**
- * Extract information from module with ASM
- *
- *
- * @author Robert Scholte
- * @since 1.0.0
- */
-class AsmModuleInfoParser extends AbstractBinaryModuleInfoParser {
-    @Override
-    JavaModuleDescriptor parse(InputStream in) throws IOException {
-        final JavaModuleDescriptorWrapper wrapper = new 
JavaModuleDescriptorWrapper();
-
-        ClassReader reader = new ClassReader(in);
-        reader.accept(
-                new ClassVisitor(Opcodes.ASM9) {
-                    @Override
-                    public ModuleVisitor visitModule(String name, int arg1, 
String arg2) {
-                        wrapper.builder = JavaModuleDescriptor.newModule(name);
-
-                        return new ModuleVisitor(Opcodes.ASM9) {
-                            @Override
-                            public void visitRequire(String module, int 
access, String version) {
-                                if ((access & (Opcodes.ACC_STATIC_PHASE | 
Opcodes.ACC_TRANSITIVE)) != 0) {
-                                    
Set<JavaModuleDescriptor.JavaRequires.JavaModifier> modifiers =
-                                            new LinkedHashSet<>();
-                                    if ((access & Opcodes.ACC_STATIC_PHASE) != 
0) {
-                                        
modifiers.add(JavaModuleDescriptor.JavaRequires.JavaModifier.STATIC);
-                                    }
-                                    if ((access & Opcodes.ACC_TRANSITIVE) != 
0) {
-                                        
modifiers.add(JavaModuleDescriptor.JavaRequires.JavaModifier.TRANSITIVE);
-                                    }
-
-                                    wrapper.builder.requires(modifiers, 
module);
-                                } else {
-                                    wrapper.builder.requires(module);
-                                }
-                            }
-
-                            @Override
-                            public void visitExport(String pn, int ms, 
String... targets) {
-                                if (targets == null || targets.length == 0) {
-                                    wrapper.builder.exports(pn.replace('/', 
'.'));
-                                } else {
-                                    wrapper.builder.exports(
-                                            pn.replace('/', '.'), new 
HashSet<>(Arrays.asList(targets)));
-                                }
-                            }
-
-                            @Override
-                            public void visitUse(String service) {
-                                wrapper.builder.uses(service.replace('/', 
'.'));
-                            }
-
-                            @Override
-                            public void visitProvide(String service, String... 
providers) {
-                                List<String> renamedProvides = new 
ArrayList<>(providers.length);
-                                for (String provider : providers) {
-                                    renamedProvides.add(provider.replace('/', 
'.'));
-                                }
-                                wrapper.builder.provides(service.replace('/', 
'.'), renamedProvides);
-                            }
-                        };
-                    }
-                },
-                0);
-        return wrapper.builder.build();
-    }
-
-    private static class JavaModuleDescriptorWrapper {
-        private JavaModuleDescriptor.Builder builder;
-    }
-}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/plexus-languages-1.6.0/plexus-java/src/main/java/org/codehaus/plexus/languages/java/jpms/BinaryModuleInfoParser.java
 
new/plexus-languages-1.7.0/plexus-java/src/main/java/org/codehaus/plexus/languages/java/jpms/BinaryModuleInfoParser.java
--- 
old/plexus-languages-1.6.0/plexus-java/src/main/java/org/codehaus/plexus/languages/java/jpms/BinaryModuleInfoParser.java
    2026-08-19 10:11:21.000000000 +0200
+++ 
new/plexus-languages-1.7.0/plexus-java/src/main/java/org/codehaus/plexus/languages/java/jpms/BinaryModuleInfoParser.java
    2026-11-06 11:37:41.000000000 +0100
@@ -18,4 +18,4 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-class BinaryModuleInfoParser extends AsmModuleInfoParser {}
+class BinaryModuleInfoParser extends ModuleInfoClassParser {}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/plexus-languages-1.6.0/plexus-java/src/main/java/org/codehaus/plexus/languages/java/jpms/LocationManager.java
 
new/plexus-languages-1.7.0/plexus-java/src/main/java/org/codehaus/plexus/languages/java/jpms/LocationManager.java
--- 
old/plexus-languages-1.6.0/plexus-java/src/main/java/org/codehaus/plexus/languages/java/jpms/LocationManager.java
   2026-08-19 10:11:21.000000000 +0200
+++ 
new/plexus-languages-1.7.0/plexus-java/src/main/java/org/codehaus/plexus/languages/java/jpms/LocationManager.java
   2026-11-06 11:37:41.000000000 +0100
@@ -36,6 +36,7 @@
 import java.util.Set;
 
 import 
org.codehaus.plexus.languages.java.jpms.JavaModuleDescriptor.JavaProvides;
+import org.codehaus.plexus.languages.java.version.JavaVersion;
 
 /**
  * Maps artifacts to modules and analyzes the type of required modules
@@ -121,7 +122,8 @@
         return resolvePath(
                 request.toPath(request.getPathElement()),
                 filenameExtractor,
-                getBinaryModuleInfoParser(request.getJdkHome()));
+                getBinaryModuleInfoParser(request.getJdkHome()),
+                request.getTargetRelease());
     }
 
     /**
@@ -172,7 +174,8 @@
             };
 
             try {
-                ResolvePathResult resolvedPath = 
resolvePath(request.toPath(t), nameExtractor, binaryParser);
+                ResolvePathResult resolvedPath =
+                        resolvePath(request.toPath(t), nameExtractor, 
binaryParser, request.getTargetRelease());
 
                 moduleDescriptor = resolvedPath.getModuleDescriptor();
 
@@ -276,7 +279,9 @@
 
     /**
      * If the jdkHome is specified, its version it considered higher than the 
runtime java version.
-     * In that case ASM must be used to read the module descriptor
+     * In that case the version-agnostic {@link ModuleInfoClassParser} must be 
used to read the module
+     * descriptor, since the runtime's own {@code 
java.lang.module.ModuleDescriptor} cannot be trusted
+     * to read a module-info.class produced by a newer JDK than the one it 
runs on.
      *
      * @param jdkHome
      * @return
@@ -286,7 +291,7 @@
         if (jdkHome == null) {
             binaryParser = new BinaryModuleInfoParser();
         } else {
-            binaryParser = new AsmModuleInfoParser();
+            binaryParser = new ModuleInfoClassParser();
         }
         return binaryParser;
     }
@@ -301,7 +306,8 @@
             if (descriptorPath.endsWith("module-info.java")) {
                 mainModuleDescriptor = 
sourceParser.fromSourcePath(descriptorPath);
             } else if (descriptorPath.endsWith("module-info.class")) {
-                mainModuleDescriptor = 
binaryParser.getModuleDescriptor(descriptorPath.getParent());
+                mainModuleDescriptor =
+                        
binaryParser.getModuleDescriptor(descriptorPath.getParent(), 
request.getTargetRelease());
             } else {
                 throw new IOException("Invalid path to module descriptor: " + 
descriptorPath);
             }
@@ -312,7 +318,11 @@
     }
 
     private ResolvePathResult resolvePath(
-            Path path, ModuleNameExtractor fileModulenameExtractor, 
ModuleInfoParser binaryParser) throws IOException {
+            Path path,
+            ModuleNameExtractor fileModulenameExtractor,
+            ModuleInfoParser binaryParser,
+            JavaVersion targetRelease)
+            throws IOException {
         ResolvePathResult result = new ResolvePathResult();
 
         JavaModuleDescriptor moduleDescriptor = null;
@@ -323,8 +333,10 @@
                     "'" + path + "' not allowed on the path, only 
outputDirectories and jars are accepted");
         }
 
-        if (Files.isRegularFile(path) || 
Files.exists(path.resolve("module-info.class"))) {
-            moduleDescriptor = binaryParser.getModuleDescriptor(path);
+        if (Files.isRegularFile(path)
+                || Files.exists(path.resolve("module-info.class"))
+                || 
AbstractBinaryModuleInfoParser.findVersionedModuleInfo(path, targetRelease) != 
null) {
+            moduleDescriptor = binaryParser.getModuleDescriptor(path, 
targetRelease);
         }
 
         if (moduleDescriptor != null) {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/plexus-languages-1.6.0/plexus-java/src/main/java/org/codehaus/plexus/languages/java/jpms/ModuleInfoClassParser.java
 
new/plexus-languages-1.7.0/plexus-java/src/main/java/org/codehaus/plexus/languages/java/jpms/ModuleInfoClassParser.java
--- 
old/plexus-languages-1.6.0/plexus-java/src/main/java/org/codehaus/plexus/languages/java/jpms/ModuleInfoClassParser.java
     1970-01-01 01:00:00.000000000 +0100
+++ 
new/plexus-languages-1.7.0/plexus-java/src/main/java/org/codehaus/plexus/languages/java/jpms/ModuleInfoClassParser.java
     2026-11-06 11:37:41.000000000 +0100
@@ -0,0 +1,302 @@
+package org.codehaus.plexus.languages.java.jpms;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Reads the {@code Module} attribute (JVMS 4.7.25) directly out of a {@code 
module-info.class} file,
+ * without depending on a bytecode library or on the class-file support of the 
running JDK.
+ *
+ * <p>Only the class file's overall shape (constant pool, then fields, then 
methods, then attributes)
+ * needs to be understood to reach the {@code Module} attribute; every other 
section is skipped using
+ * its own declared length rather than parsed. The {@code Module} attribute 
format has been stable
+ * since it was introduced in Java 9, and this parser never looks at the class 
file's major/minor
+ * version, so it reads a {@code module-info.class} produced by any future JDK 
just as well as one
+ * from Java 9.
+ *
+ * @author Robert Scholte
+ * @since 1.7.0
+ */
+class ModuleInfoClassParser extends AbstractBinaryModuleInfoParser {
+
+    private static final int MAGIC = 0xCAFEBABE;
+
+    // Constant pool tags, JVMS 4.4, Table 4.4-A.
+    private static final int CONSTANT_UTF8 = 1;
+    private static final int CONSTANT_INTEGER = 3;
+    private static final int CONSTANT_FLOAT = 4;
+    private static final int CONSTANT_LONG = 5;
+    private static final int CONSTANT_DOUBLE = 6;
+    private static final int CONSTANT_CLASS = 7;
+    private static final int CONSTANT_STRING = 8;
+    private static final int CONSTANT_FIELDREF = 9;
+    private static final int CONSTANT_METHODREF = 10;
+    private static final int CONSTANT_INTERFACE_METHODREF = 11;
+    private static final int CONSTANT_NAME_AND_TYPE = 12;
+    private static final int CONSTANT_METHOD_HANDLE = 15;
+    private static final int CONSTANT_METHOD_TYPE = 16;
+    private static final int CONSTANT_DYNAMIC = 17;
+    private static final int CONSTANT_INVOKE_DYNAMIC = 18;
+    private static final int CONSTANT_MODULE = 19;
+    private static final int CONSTANT_PACKAGE = 20;
+
+    private static final String MODULE_ATTRIBUTE_NAME = "Module";
+
+    // Module_attribute requires_flags, JVMS 4.7.25.
+    private static final int ACC_TRANSITIVE = 0x0020;
+    private static final int ACC_STATIC_PHASE = 0x0040;
+
+    @Override
+    JavaModuleDescriptor parse(InputStream in) throws IOException {
+        DataInputStream dis = (in instanceof DataInputStream) ? 
(DataInputStream) in : new DataInputStream(in);
+
+        int magic = dis.readInt();
+        if (magic != MAGIC) {
+            throw new IOException("Not a class file, bad magic number: 0x" + 
Integer.toHexString(magic));
+        }
+
+        dis.readUnsignedShort(); // minor_version, unused
+        dis.readUnsignedShort(); // major_version, deliberately unchecked: the 
Module attribute below
+        // has the same layout no matter which major version produced the file.
+
+        ConstantPool pool = ConstantPool.read(dis);
+
+        dis.readUnsignedShort(); // access_flags
+        dis.readUnsignedShort(); // this_class
+        dis.readUnsignedShort(); // super_class
+
+        int interfacesCount = dis.readUnsignedShort();
+        skipFully(dis, 2L * interfacesCount);
+
+        skipMembers(dis); // fields
+        skipMembers(dis); // methods
+
+        int attributesCount = dis.readUnsignedShort();
+        for (int i = 0; i < attributesCount; i++) {
+            int attributeNameIndex = dis.readUnsignedShort();
+            long attributeLength = readU4(dis);
+            if (MODULE_ATTRIBUTE_NAME.equals(pool.utf8(attributeNameIndex))) {
+                return readModuleAttribute(dis, pool);
+            }
+            skipFully(dis, attributeLength);
+        }
+
+        throw new IOException("Not a module: no Module attribute found");
+    }
+
+    /**
+     * Skips over a {@code fields} or {@code methods} array: same {@code 
field_info}/{@code method_info}
+     * shape, each with its own list of attributes that are skipped by their 
declared length.
+     */
+    private static void skipMembers(DataInputStream dis) throws IOException {
+        int count = dis.readUnsignedShort();
+        for (int i = 0; i < count; i++) {
+            skipFully(dis, 6); // access_flags, name_index, descriptor_index
+            int attributesCount = dis.readUnsignedShort();
+            for (int j = 0; j < attributesCount; j++) {
+                skipFully(dis, 2); // attribute_name_index
+                long attributeLength = readU4(dis);
+                skipFully(dis, attributeLength);
+            }
+        }
+    }
+
+    /**
+     * Reads the body of the {@code Module} attribute (JVMS 4.7.25), mapping 
it onto a
+     * {@link JavaModuleDescriptor} the same way {@code 
java.lang.module.ModuleDescriptor} does.
+     */
+    private static JavaModuleDescriptor readModuleAttribute(DataInputStream 
dis, ConstantPool pool) throws IOException {
+        String moduleName = pool.moduleName(dis.readUnsignedShort());
+        JavaModuleDescriptor.Builder builder = 
JavaModuleDescriptor.newModule(moduleName);
+
+        dis.readUnsignedShort(); // module_flags
+        dis.readUnsignedShort(); // module_version_index
+
+        int requiresCount = dis.readUnsignedShort();
+        for (int i = 0; i < requiresCount; i++) {
+            int requiresIndex = dis.readUnsignedShort();
+            int requiresFlags = dis.readUnsignedShort();
+            dis.readUnsignedShort(); // requires_version_index
+
+            String requiredModule = pool.moduleName(requiresIndex);
+            if ((requiresFlags & (ACC_STATIC_PHASE | ACC_TRANSITIVE)) != 0) {
+                Set<JavaModuleDescriptor.JavaRequires.JavaModifier> modifiers 
= new LinkedHashSet<>();
+                if ((requiresFlags & ACC_STATIC_PHASE) != 0) {
+                    
modifiers.add(JavaModuleDescriptor.JavaRequires.JavaModifier.STATIC);
+                }
+                if ((requiresFlags & ACC_TRANSITIVE) != 0) {
+                    
modifiers.add(JavaModuleDescriptor.JavaRequires.JavaModifier.TRANSITIVE);
+                }
+                builder.requires(modifiers, requiredModule);
+            } else {
+                builder.requires(requiredModule);
+            }
+        }
+
+        int exportsCount = dis.readUnsignedShort();
+        for (int i = 0; i < exportsCount; i++) {
+            int exportsIndex = dis.readUnsignedShort();
+            dis.readUnsignedShort(); // exports_flags
+            int exportsToCount = dis.readUnsignedShort();
+            String pkg = pool.packageName(exportsIndex);
+            if (exportsToCount == 0) {
+                builder.exports(pkg);
+            } else {
+                Set<String> targets = new HashSet<>();
+                for (int j = 0; j < exportsToCount; j++) {
+                    targets.add(pool.moduleName(dis.readUnsignedShort()));
+                }
+                builder.exports(pkg, targets);
+            }
+        }
+
+        int opensCount = dis.readUnsignedShort();
+        for (int i = 0; i < opensCount; i++) {
+            dis.readUnsignedShort(); // opens_index
+            dis.readUnsignedShort(); // opens_flags
+            int opensToCount = dis.readUnsignedShort();
+            skipFully(dis, 2L * opensToCount);
+        }
+
+        int usesCount = dis.readUnsignedShort();
+        for (int i = 0; i < usesCount; i++) {
+            builder.uses(pool.className(dis.readUnsignedShort()));
+        }
+
+        int providesCount = dis.readUnsignedShort();
+        for (int i = 0; i < providesCount; i++) {
+            String service = pool.className(dis.readUnsignedShort());
+            int withCount = dis.readUnsignedShort();
+            List<String> providers = new ArrayList<>(withCount);
+            for (int j = 0; j < withCount; j++) {
+                providers.add(pool.className(dis.readUnsignedShort()));
+            }
+            builder.provides(service, providers);
+        }
+
+        return builder.build();
+    }
+
+    private static long readU4(DataInputStream dis) throws IOException {
+        return dis.readInt() & 0xFFFFFFFFL;
+    }
+
+    private static void skipFully(DataInputStream dis, long length) throws 
IOException {
+        long remaining = length;
+        while (remaining > 0) {
+            long skipped = dis.skip(remaining);
+            if (skipped <= 0) {
+                // skip() may legitimately return 0 near the end of the 
stream; fall back to read()
+                if (dis.read() < 0) {
+                    throw new IOException("Unexpected end of class file");
+                }
+                skipped = 1;
+            }
+            remaining -= skipped;
+        }
+    }
+
+    /**
+     * The subset of the constant pool this parser needs: the raw UTF-8 
entries, and, for the
+     * Class/Module/Package entries, which UTF-8 entry holds their name. All 
three of those tags
+     * have the same one-field shape (JVMS 4.4.1, 4.4.8, 4.4.9): {@code u1 
tag; u2 name_index;}.
+     */
+    private static final class ConstantPool {
+        private final String[] utf8;
+        private final int[] classLikeNameIndex;
+
+        private ConstantPool(String[] utf8, int[] classLikeNameIndex) {
+            this.utf8 = utf8;
+            this.classLikeNameIndex = classLikeNameIndex;
+        }
+
+        static ConstantPool read(DataInputStream dis) throws IOException {
+            int constantPoolCount = dis.readUnsignedShort();
+            String[] utf8 = new String[constantPoolCount];
+            int[] classLikeNameIndex = new int[constantPoolCount];
+
+            for (int i = 1; i < constantPoolCount; i++) {
+                int tag = dis.readUnsignedByte();
+                switch (tag) {
+                    case CONSTANT_UTF8:
+                        utf8[i] = dis.readUTF();
+                        break;
+                    case CONSTANT_CLASS:
+                    case CONSTANT_MODULE:
+                    case CONSTANT_PACKAGE:
+                        classLikeNameIndex[i] = dis.readUnsignedShort();
+                        break;
+                    case CONSTANT_STRING:
+                        skipFully(dis, 2);
+                        break;
+                    case CONSTANT_INTEGER:
+                    case CONSTANT_FLOAT:
+                    case CONSTANT_FIELDREF:
+                    case CONSTANT_METHODREF:
+                    case CONSTANT_INTERFACE_METHODREF:
+                    case CONSTANT_NAME_AND_TYPE:
+                    case CONSTANT_DYNAMIC:
+                    case CONSTANT_INVOKE_DYNAMIC:
+                        skipFully(dis, 4);
+                        break;
+                    case CONSTANT_LONG:
+                    case CONSTANT_DOUBLE:
+                        skipFully(dis, 8);
+                        // long/double take up two consecutive constant pool 
entries; the second is unused
+                        i++;
+                        break;
+                    case CONSTANT_METHOD_HANDLE:
+                        skipFully(dis, 3);
+                        break;
+                    case CONSTANT_METHOD_TYPE:
+                        skipFully(dis, 2);
+                        break;
+                    default:
+                        throw new IOException("Unsupported constant pool tag " 
+ tag + " at index " + i);
+                }
+            }
+            return new ConstantPool(utf8, classLikeNameIndex);
+        }
+
+        String utf8(int index) {
+            return utf8[index];
+        }
+
+        String moduleName(int index) {
+            return utf8[classLikeNameIndex[index]];
+        }
+
+        String packageName(int index) {
+            return utf8[classLikeNameIndex[index]].replace('/', '.');
+        }
+
+        String className(int index) {
+            return utf8[classLikeNameIndex[index]].replace('/', '.');
+        }
+    }
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/plexus-languages-1.6.0/plexus-java/src/main/java/org/codehaus/plexus/languages/java/jpms/ResolvePathRequest.java
 
new/plexus-languages-1.7.0/plexus-java/src/main/java/org/codehaus/plexus/languages/java/jpms/ResolvePathRequest.java
--- 
old/plexus-languages-1.6.0/plexus-java/src/main/java/org/codehaus/plexus/languages/java/jpms/ResolvePathRequest.java
        2026-08-19 10:11:21.000000000 +0200
+++ 
new/plexus-languages-1.7.0/plexus-java/src/main/java/org/codehaus/plexus/languages/java/jpms/ResolvePathRequest.java
        2026-11-06 11:37:41.000000000 +0100
@@ -22,6 +22,9 @@
 import java.io.File;
 import java.nio.file.Path;
 import java.nio.file.Paths;
+import java.util.Objects;
+
+import org.codehaus.plexus.languages.java.version.JavaVersion;
 
 /**
  *
@@ -33,6 +36,8 @@
 
     private T path;
 
+    private JavaVersion targetRelease = JavaVersion.JAVA_SPECIFICATION_VERSION;
+
     private ResolvePathRequest() {}
 
     public static ResolvePathRequest<File> ofFile(File file) {
@@ -88,4 +93,29 @@
     public Path getJdkHome() {
         return jdkHome;
     }
+
+    /**
+     * The Java release the module descriptor must be resolved for - the 
{@code javac --release} value, or
+     * {@code -target} when {@code --release} is unset. Accepts {@code 
"1.8"}-style values as well as bare
+     * major versions. This picks the applicable versioned {@code 
module-info.class} of a multi-release jar or
+     * output directory, i.e. the highest {@code META-INF/versions/<N>} whose 
{@code N} does not exceed this
+     * release. Defaults to the version of the running JDK, so callers that 
compile for a different release
+     * should set this explicitly.
+     *
+     * @param targetRelease the target release, never {@code null}
+     * @return this request
+     * @since 1.7.0
+     */
+    public ResolvePathRequest<T> setTargetRelease(JavaVersion targetRelease) {
+        this.targetRelease = Objects.requireNonNull(targetRelease, 
"targetRelease");
+        return this;
+    }
+
+    /**
+     * @return the Java release the module descriptor is resolved for, 
defaults to the running JDK's version
+     * @since 1.7.0
+     */
+    public JavaVersion getTargetRelease() {
+        return targetRelease;
+    }
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/plexus-languages-1.6.0/plexus-java/src/main/java/org/codehaus/plexus/languages/java/jpms/ResolvePathsRequest.java
 
new/plexus-languages-1.7.0/plexus-java/src/main/java/org/codehaus/plexus/languages/java/jpms/ResolvePathsRequest.java
--- 
old/plexus-languages-1.6.0/plexus-java/src/main/java/org/codehaus/plexus/languages/java/jpms/ResolvePathsRequest.java
       2026-08-19 10:11:21.000000000 +0200
+++ 
new/plexus-languages-1.7.0/plexus-java/src/main/java/org/codehaus/plexus/languages/java/jpms/ResolvePathsRequest.java
       2026-11-06 11:37:41.000000000 +0100
@@ -25,6 +25,9 @@
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.Objects;
+
+import org.codehaus.plexus.languages.java.version.JavaVersion;
 
 /**
  * Contains all information required to analyze the project
@@ -47,6 +50,8 @@
 
     private boolean includeStatic;
 
+    private JavaVersion targetRelease = JavaVersion.JAVA_SPECIFICATION_VERSION;
+
     private ResolvePathsRequest() {}
 
     /**
@@ -226,4 +231,29 @@
         this.includeStatic = includeStatic;
         return this;
     }
+
+    /**
+     * The Java release the module descriptors must be resolved for - the 
{@code javac --release} value, or
+     * {@code -target} when {@code --release} is unset. Accepts {@code 
"1.8"}-style values as well as bare
+     * major versions. This picks the applicable versioned {@code 
module-info.class} of a multi-release jar or
+     * output directory, i.e. the highest {@code META-INF/versions/<N>} whose 
{@code N} does not exceed this
+     * release. Defaults to the version of the running JDK, so callers that 
compile for a different release
+     * should set this explicitly.
+     *
+     * @param targetRelease the target release, never {@code null}
+     * @return this request
+     * @since 1.7.0
+     */
+    public ResolvePathsRequest<T> setTargetRelease(JavaVersion targetRelease) {
+        this.targetRelease = Objects.requireNonNull(targetRelease, 
"targetRelease");
+        return this;
+    }
+
+    /**
+     * @return the Java release module descriptors are resolved for, defaults 
to the running JDK's version
+     * @since 1.7.0
+     */
+    public JavaVersion getTargetRelease() {
+        return targetRelease;
+    }
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/plexus-languages-1.6.0/plexus-java/src/main/java/org/codehaus/plexus/languages/java/version/JavaVersion.java
 
new/plexus-languages-1.7.0/plexus-java/src/main/java/org/codehaus/plexus/languages/java/version/JavaVersion.java
--- 
old/plexus-languages-1.6.0/plexus-java/src/main/java/org/codehaus/plexus/languages/java/version/JavaVersion.java
    2026-08-19 10:11:21.000000000 +0200
+++ 
new/plexus-languages-1.7.0/plexus-java/src/main/java/org/codehaus/plexus/languages/java/version/JavaVersion.java
    2026-11-06 11:37:41.000000000 +0100
@@ -219,6 +219,23 @@
         return value.toString();
     }
 
+    /**
+     * The major version as an integer, parsed from the leading digits of 
{@link #asMajor()}'s value, so a
+     * pre-release suffix such as {@code "-ea"} does not prevent parsing, e.g. 
both {@code "21"} and
+     * {@code "21-ea"} return {@code 21}, and {@code "1.8"} returns {@code 8}.
+     *
+     * @return the major version number
+     * @throws NumberFormatException if the version does not start with a digit
+     * @since 1.7.0
+     */
+    public int getMajorVersion() {
+        Matcher matcher = startingDigits.matcher(asMajor().getValue(1));
+        if (matcher.find()) {
+            return Integer.parseInt(matcher.group(1));
+        }
+        throw new NumberFormatException("Cannot parse a major version from '" 
+ rawVersion + "'");
+    }
+
     @Override
     public String toString() {
         return rawVersion;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/plexus-languages-1.6.0/plexus-java/src/main/java9/module-info.java 
new/plexus-languages-1.7.0/plexus-java/src/main/java9/module-info.java
--- old/plexus-languages-1.6.0/plexus-java/src/main/java9/module-info.java      
2026-08-19 10:11:21.000000000 +0200
+++ new/plexus-languages-1.7.0/plexus-java/src/main/java9/module-info.java      
2026-11-06 11:37:41.000000000 +0100
@@ -18,7 +18,6 @@
  */
 module org.codehaus.plexus.languages.java {
     requires com.github.javaparser.core;
-    requires org.objectweb.asm;
 
     exports org.codehaus.plexus.languages.java.jpms;
     exports org.codehaus.plexus.languages.java.version;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/plexus-languages-1.6.0/plexus-java/src/site/site.xml 
new/plexus-languages-1.7.0/plexus-java/src/site/site.xml
--- old/plexus-languages-1.6.0/plexus-java/src/site/site.xml    2026-08-19 
10:11:21.000000000 +0200
+++ new/plexus-languages-1.7.0/plexus-java/src/site/site.xml    2026-11-06 
11:37:41.000000000 +0100
@@ -27,7 +27,6 @@
       <item name="Usage" href="usage.html"/>
       <item name="LocationManager" href="locationmanager.html"/>
       <item name="JavaDocs" href="apidocs/index.html"/>
-      <item name="Source Xref" href="xref/index.html"/>
     </menu>
 
     <menu ref="parent"/>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/plexus-languages-1.6.0/plexus-java/src/test/java/org/codehaus/plexus/languages/java/jpms/BinaryModuleInfoParserTest.java
 
new/plexus-languages-1.7.0/plexus-java/src/test/java/org/codehaus/plexus/languages/java/jpms/BinaryModuleInfoParserTest.java
--- 
old/plexus-languages-1.6.0/plexus-java/src/test/java/org/codehaus/plexus/languages/java/jpms/BinaryModuleInfoParserTest.java
        2026-08-19 10:11:21.000000000 +0200
+++ 
new/plexus-languages-1.7.0/plexus-java/src/test/java/org/codehaus/plexus/languages/java/jpms/BinaryModuleInfoParserTest.java
        2026-11-06 11:37:41.000000000 +0100
@@ -94,6 +94,16 @@
     }
 
     @Test
+    void multiReleaseOutputDirectoryDescriptor() throws Exception {
+        JavaModuleDescriptor descriptor = parser.getModuleDescriptor(
+                Paths.get("src/test/test-data/dir.mr.descriptor/out"), 
JavaVersion.parse("17"));
+
+        assertNotNull(descriptor);
+        assertEquals("org.codehaus.plexus.languages.java.demo", 
descriptor.name());
+        assertFalse(descriptor.isAutomatic());
+    }
+
+    @Test
     void outputDirectoryDescriptor() throws Exception {
         JavaModuleDescriptor descriptor =
                 
parser.getModuleDescriptor(Paths.get("src/test/test-data/dir.descriptor/out"));
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/plexus-languages-1.6.0/plexus-java/src/test/java/org/codehaus/plexus/languages/java/jpms/LocationManagerTargetReleaseTest.java
 
new/plexus-languages-1.7.0/plexus-java/src/test/java/org/codehaus/plexus/languages/java/jpms/LocationManagerTargetReleaseTest.java
--- 
old/plexus-languages-1.6.0/plexus-java/src/test/java/org/codehaus/plexus/languages/java/jpms/LocationManagerTargetReleaseTest.java
  1970-01-01 01:00:00.000000000 +0100
+++ 
new/plexus-languages-1.7.0/plexus-java/src/test/java/org/codehaus/plexus/languages/java/jpms/LocationManagerTargetReleaseTest.java
  2026-11-06 11:37:41.000000000 +0100
@@ -0,0 +1,103 @@
+package org.codehaus.plexus.languages.java.jpms;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Collections;
+
+import org.codehaus.plexus.languages.java.version.JavaVersion;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+/**
+ * End-to-end coverage of {@link 
ResolvePathsRequest#setTargetRelease(JavaVersion)}, using the real
+ * {@link BinaryModuleInfoParser} against a directory that has a versioned 
module descriptor for both Java 11
+ * and Java 17. Runs on every JDK, including Java 8, since the whole point of 
the target release setting is to
+ * pick the applicable versioned descriptor without relying on the version of 
the JDK that runs the build.
+ */
+class LocationManagerTargetReleaseTest {
+    private final LocationManager locationManager = new LocationManager();
+
+    private final Path dir = 
Paths.get("src/test/test-data/dir.mr.target.release/out");
+
+    @Test
+    void targetReleaseSelectsItsOwnVersionedDescriptor() throws Exception {
+        ResolvePathsRequest<Path> request =
+                
ResolvePathsRequest.ofPaths(Collections.singletonList(dir)).setTargetRelease(JavaVersion.parse("11"));
+
+        ResolvePathsResult<Path> result = 
locationManager.resolvePaths(request);
+
+        
assertThat(result.getPathElements().get(dir).name()).isEqualTo("dir.mr.target.release.v11");
+    }
+
+    @Test
+    void targetReleaseWorksIndependentlyOfTheRunningJdk() throws Exception {
+        // On a Java 8 runtime compiling through a Java 9+ toolchain, the 
running JDK's version must not be used
+        // to pick the versioned descriptor - this is the scenario from #237 
(Maven 3.9 on JDK 8, --release 17).
+        ResolvePathsRequest<Path> request =
+                
ResolvePathsRequest.ofPaths(Collections.singletonList(dir)).setTargetRelease(JavaVersion.parse("17"));
+
+        ResolvePathsResult<Path> result = 
locationManager.resolvePaths(request);
+
+        
assertThat(result.getPathElements().get(dir).name()).isEqualTo("dir.mr.target.release.v17");
+    }
+
+    @Test
+    void targetReleaseBelowEveryVersionedDescriptorSelectsNone() throws 
Exception {
+        ResolvePathsRequest<Path> request =
+                
ResolvePathsRequest.ofPaths(Collections.singletonList(dir)).setTargetRelease(JavaVersion.parse("9"));
+
+        ResolvePathsResult<Path> result = 
locationManager.resolvePaths(request);
+
+        assertThat(result.getPathElements().get(dir)).isNull();
+    }
+
+    @Test
+    void preReleaseTargetRelease() throws Exception {
+        ResolvePathsRequest<Path> request = 
ResolvePathsRequest.ofPaths(Collections.singletonList(dir))
+                .setTargetRelease(JavaVersion.parse("11-ea"));
+
+        ResolvePathsResult<Path> result = 
locationManager.resolvePaths(request);
+
+        assertThat(result.getPathExceptions()).isEmpty();
+        
assertThat(result.getPathElements().get(dir).name()).isEqualTo("dir.mr.target.release.v11");
+    }
+
+    @Test
+    void singlePathTargetRelease() throws Exception {
+        ResolvePathRequest<Path> request = 
ResolvePathRequest.ofPath(dir).setTargetRelease(JavaVersion.parse("11"));
+
+        ResolvePathResult result = locationManager.resolvePath(request);
+
+        
assertThat(result.getModuleDescriptor().name()).isEqualTo("dir.mr.target.release.v11");
+    }
+
+    @Test
+    void nullTargetReleaseIsRejected() {
+        assertThatThrownBy(() -> 
ResolvePathsRequest.ofPaths(Collections.singletonList(dir))
+                        .setTargetRelease(null))
+                .isInstanceOf(NullPointerException.class);
+        assertThatThrownBy(() -> 
ResolvePathRequest.ofPath(dir).setTargetRelease(null))
+                .isInstanceOf(NullPointerException.class);
+    }
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/plexus-languages-1.6.0/plexus-java/src/test/java/org/codehaus/plexus/languages/java/jpms/LocationManagerTest.java
 
new/plexus-languages-1.7.0/plexus-java/src/test/java/org/codehaus/plexus/languages/java/jpms/LocationManagerTest.java
--- 
old/plexus-languages-1.6.0/plexus-java/src/test/java/org/codehaus/plexus/languages/java/jpms/LocationManagerTest.java
       2026-08-19 10:11:21.000000000 +0200
+++ 
new/plexus-languages-1.7.0/plexus-java/src/test/java/org/codehaus/plexus/languages/java/jpms/LocationManagerTest.java
       2026-11-06 11:37:41.000000000 +0100
@@ -27,12 +27,17 @@
 import java.util.HashSet;
 
 import 
org.codehaus.plexus.languages.java.jpms.JavaModuleDescriptor.JavaRequires.JavaModifier;
+import org.codehaus.plexus.languages.java.version.JavaVersion;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.DisabledOnJre;
+import org.junit.jupiter.api.condition.JRE;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 class LocationManagerTest {
@@ -45,7 +50,7 @@
     final Path mockModuleInfoJava = 
Paths.get("src/test/test-data/mock/module-info.java");
 
     @BeforeEach
-    void onSetup() {
+    void onSetup() throws Exception {
         asmParser = mock(BinaryModuleInfoParser.class);
         sourceParser = mock(SourceModuleInfoParser.class);
         locationManager = new LocationManager(sourceParser) {
@@ -54,6 +59,13 @@
                 return asmParser;
             }
         };
+
+        // LocationManager always calls the 2-arg overload now; delegate it to 
the 1-arg overload so every
+        // existing when(asmParser.getModuleDescriptor(x)) stub below keeps 
working regardless of which
+        // JavaVersion production code passes. Tests that care about the 
JavaVersion argument itself stub the
+        // 2-arg overload explicitly, which takes precedence over this 
catch-all.
+        when(asmParser.getModuleDescriptor(any(Path.class), 
any(JavaVersion.class)))
+                .thenAnswer(invocation -> 
asmParser.getModuleDescriptor(invocation.getArgument(0, Path.class)));
     }
 
     @Test
@@ -107,6 +119,28 @@
     }
 
     @Test
+    @DisabledOnJre(value = JRE.JAVA_8, disabledReason = "Versioned descriptors 
are only read on Java 9+")
+    void multiReleaseDirDescriptor() throws Exception {
+        Path abc = Paths.get("src/test/test-data/dir.mr.descriptor/out");
+        JavaModuleDescriptor descriptor = 
JavaModuleDescriptor.newModule("base")
+                .requires("dir.mr.descriptor")
+                .build();
+        
when(sourceParser.fromSourcePath(any(Path.class))).thenReturn(descriptor);
+        ResolvePathsRequest<Path> request =
+                
ResolvePathsRequest.ofPaths(Collections.singletonList(abc)).setMainModuleDescriptor(mockModuleInfoJava);
+
+        when(asmParser.getModuleDescriptor(abc))
+                
.thenReturn(JavaModuleDescriptor.newModule("dir.mr.descriptor").build());
+
+        ResolvePathsResult<Path> result = 
locationManager.resolvePaths(request);
+
+        assertThat(result.getModulepathElements()).hasSize(1);
+        
assertThat(result.getModulepathElements().get(abc)).isEqualTo(ModuleNameSource.MODULEDESCRIPTOR);
+        assertThat(result.getClasspathElements()).isEmpty();
+        assertThat(result.getPathExceptions()).isEmpty();
+    }
+
+    @Test
     void dirDescriptorWithReflectRequires() throws Exception {
         Path abc = Paths.get("src/test/test-data/dir.descriptor/out");
         JavaModuleDescriptor descriptor = 
JavaModuleDescriptor.newModule("base")
@@ -291,6 +325,38 @@
     }
 
     @Test
+    void unsetTargetReleaseDefaultsToRunningJdk() throws Exception {
+        Path p = Paths.get("src/test/test-data/mock/jar0.jar");
+        ResolvePathsRequest<Path> request = 
ResolvePathsRequest.ofPaths(Collections.singletonList(p));
+
+        
assertThat(request.getTargetRelease()).isEqualTo(JavaVersion.JAVA_SPECIFICATION_VERSION);
+
+        doReturn(JavaModuleDescriptor.newModule("org.objectweb.asm").build())
+                .when(asmParser)
+                .getModuleDescriptor(p, 
JavaVersion.JAVA_SPECIFICATION_VERSION);
+
+        locationManager.resolvePaths(request);
+
+        verify(asmParser).getModuleDescriptor(p, 
JavaVersion.JAVA_SPECIFICATION_VERSION);
+    }
+
+    @Test
+    void targetReleaseIsPassedToBinaryParser() throws Exception {
+        Path p = Paths.get("src/test/test-data/mock/jar0.jar");
+        JavaVersion targetRelease = JavaVersion.parse("11");
+        ResolvePathsRequest<Path> request =
+                
ResolvePathsRequest.ofPaths(Collections.singletonList(p)).setTargetRelease(targetRelease);
+
+        doReturn(JavaModuleDescriptor.newModule("org.objectweb.asm").build())
+                .when(asmParser)
+                .getModuleDescriptor(p, targetRelease);
+
+        locationManager.resolvePaths(request);
+
+        verify(asmParser).getModuleDescriptor(p, targetRelease);
+    }
+
+    @Test
     void noMatchingProviders() throws Exception {
         Path abc = Paths.get("src/test/test-data/mock/module-info.java"); // 
some file called module-info.java
         Path def = Paths.get("src/test/test-data/mock/jar0.jar"); // any 
existing file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/plexus-languages-1.6.0/plexus-java/src/test/java/org/codehaus/plexus/languages/java/jpms/ModuleInfoClassParserTest.java
 
new/plexus-languages-1.7.0/plexus-java/src/test/java/org/codehaus/plexus/languages/java/jpms/ModuleInfoClassParserTest.java
--- 
old/plexus-languages-1.6.0/plexus-java/src/test/java/org/codehaus/plexus/languages/java/jpms/ModuleInfoClassParserTest.java
 1970-01-01 01:00:00.000000000 +0100
+++ 
new/plexus-languages-1.7.0/plexus-java/src/test/java/org/codehaus/plexus/languages/java/jpms/ModuleInfoClassParserTest.java
 2026-11-06 11:37:41.000000000 +0100
@@ -0,0 +1,103 @@
+package org.codehaus.plexus.languages.java.jpms;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.ByteArrayInputStream;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import 
org.codehaus.plexus.languages.java.jpms.JavaModuleDescriptor.JavaRequires;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+class ModuleInfoClassParserTest {
+    private final ModuleInfoClassParser parser = new ModuleInfoClassParser();
+
+    /**
+     * This parser never looks at the class file's major/minor version, so a 
module-info.class compiled
+     * by a JDK newer than any released today (e.g. major version 72 for JDK 
28, see
+     * codehaus-plexus/plexus-languages#165, or an arbitrarily higher one) 
must still parse, with the same
+     * descriptor as the unpatched file (see {@code 
BinaryModuleInfoParserTest#requires()}).
+     */
+    @ParameterizedTest
+    @ValueSource(ints = {72, 99})
+    void parsesModuleInfoWithMajorVersionNewerThanAnyKnownJdk(int 
majorVersion) throws Exception {
+        byte[] classBytes =
+                
Files.readAllBytes(Paths.get("src/test/test-data/dir.descriptor.requires/out/module-info.class"));
+
+        // bytes 6-7 are the big-endian major version
+        classBytes[6] = (byte) (majorVersion >>> 8);
+        classBytes[7] = (byte) majorVersion;
+
+        JavaModuleDescriptor descriptor;
+        try (ByteArrayInputStream is = new ByteArrayInputStream(classBytes)) {
+            descriptor = parser.parse(is);
+        }
+
+        assertNotNull(descriptor);
+        assertThat(descriptor.requires()).hasSize(5);
+
+        Set<JavaRequires> expectedRequires = 
JavaModuleDescriptor.newAutomaticModule("_")
+                .requires("java.base")
+                .requires("mod_r")
+                
.requires(Collections.singleton(JavaRequires.JavaModifier.STATIC), "mod_r_s")
+                
.requires(Collections.singleton(JavaRequires.JavaModifier.TRANSITIVE), 
"mod_r_t")
+                .requires(
+                        new HashSet<>(
+                                
Arrays.asList(JavaRequires.JavaModifier.STATIC, 
JavaRequires.JavaModifier.TRANSITIVE)),
+                        "mod_r_s_t")
+                .build()
+                .requires();
+
+        assertEquals(expectedRequires, descriptor.requires());
+    }
+
+    @Test
+    void rejectsBadMagic() throws Exception {
+        byte[] classBytes =
+                
Files.readAllBytes(Paths.get("src/test/test-data/dir.descriptor.requires/out/module-info.class"));
+        classBytes[0] = 0x00;
+
+        try (ByteArrayInputStream is = new ByteArrayInputStream(classBytes)) {
+            assertThrows(java.io.IOException.class, () -> parser.parse(is));
+        }
+    }
+
+    @Test
+    void rejectsClassFileWithoutModuleAttribute() throws Exception {
+        // an ordinary compiled class, i.e. one without a Module attribute, 
must be rejected rather than
+        // silently producing a bogus descriptor
+        byte[] classBytes = 
Files.readAllBytes(Paths.get("src/test/test-data/classfile.version/helloworld-17.class"));
+
+        try (ByteArrayInputStream is = new ByteArrayInputStream(classBytes)) {
+            assertThrows(java.io.IOException.class, () -> parser.parse(is));
+        }
+    }
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/plexus-languages-1.6.0/plexus-java/src/test/java/org/codehaus/plexus/languages/java/version/JavaVersionTest.java
 
new/plexus-languages-1.7.0/plexus-java/src/test/java/org/codehaus/plexus/languages/java/version/JavaVersionTest.java
--- 
old/plexus-languages-1.6.0/plexus-java/src/test/java/org/codehaus/plexus/languages/java/version/JavaVersionTest.java
        2026-08-19 10:11:21.000000000 +0200
+++ 
new/plexus-languages-1.7.0/plexus-java/src/test/java/org/codehaus/plexus/languages/java/version/JavaVersionTest.java
        2026-11-06 11:37:41.000000000 +0100
@@ -166,4 +166,12 @@
         assertThat(JavaVersion.parse("3.2.1").getValue(2)).isEqualTo("3.2");
         assertThat(JavaVersion.parse("3.2.1").getValue(3)).isEqualTo("3.2.1");
     }
+
+    @Test
+    void getMajorVersion() {
+        assertEquals(8, JavaVersion.parse("1.8").getMajorVersion());
+        assertEquals(11, JavaVersion.parse("11.0.2").getMajorVersion());
+        assertEquals(21, JavaVersion.parse("21-ea").getMajorVersion());
+        assertEquals(25, JavaVersion.parse("25").getMajorVersion());
+    }
 }
Binary files 
old/plexus-languages-1.6.0/plexus-java/src/test/test-data/dir.mr.descriptor/out/META-INF/versions/9/module-info.class
 and 
new/plexus-languages-1.7.0/plexus-java/src/test/test-data/dir.mr.descriptor/out/META-INF/versions/9/module-info.class
 differ
Binary files 
old/plexus-languages-1.6.0/plexus-java/src/test/test-data/dir.mr.target.release/out/META-INF/versions/11/module-info.class
 and 
new/plexus-languages-1.7.0/plexus-java/src/test/test-data/dir.mr.target.release/out/META-INF/versions/11/module-info.class
 differ
Binary files 
old/plexus-languages-1.6.0/plexus-java/src/test/test-data/dir.mr.target.release/out/META-INF/versions/17/module-info.class
 and 
new/plexus-languages-1.7.0/plexus-java/src/test/test-data/dir.mr.target.release/out/META-INF/versions/17/module-info.class
 differ
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/plexus-languages-1.6.0/pom.xml 
new/plexus-languages-1.7.0/pom.xml
--- old/plexus-languages-1.6.0/pom.xml  2026-08-19 10:11:21.000000000 +0200
+++ new/plexus-languages-1.7.0/pom.xml  2026-11-06 11:37:41.000000000 +0100
@@ -9,7 +9,7 @@
   </parent>
 
   <artifactId>plexus-languages</artifactId>
-  <version>1.6.0</version>
+  <version>1.7.0</version>
   <packaging>pom</packaging>
 
   <name>Plexus Languages</name>
@@ -22,7 +22,7 @@
   <scm>
     
<connection>scm:git:https://github.com/codehaus-plexus/plexus-languages.git</connection>
     
<developerConnection>scm:git:https://github.com/codehaus-plexus/plexus-languages.git</developerConnection>
-    <tag>plexus-languages-1.6.0</tag>
+    <tag>plexus-languages-1.7.0</tag>
     
<url>https://github.com/codehaus-plexus/plexus-languages/tree/plexus-languages</url>
   </scm>
   <issueManagement>
@@ -42,7 +42,7 @@
 
   <properties>
     
<scm.url>scm:git:https://github.com/codehaus-plexus/plexus-languages.git</scm.url>
-    
<project.build.outputTimestamp>2026-08-19T08:11:15Z</project.build.outputTimestamp>
+    
<project.build.outputTimestamp>2026-11-06T10:37:26Z</project.build.outputTimestamp>
   </properties>
 
   <build>
@@ -120,5 +120,16 @@
         </plugins>
       </build>
     </profile>
+    <profile>
+      <!-- palantir-java-format cannot run on JDK 27+: 
https://github.com/palantir/palantir-java-format/issues/1785 -->
+      <id>jdk27-skip-spotless</id>
+      <activation>
+        <jdk>[27,)</jdk>
+      </activation>
+      <properties>
+        <spotless.check.skip>true</spotless.check.skip>
+        <spotless.apply.skip>true</spotless.apply.skip>
+      </properties>
+    </profile>
   </profiles>
 </project>

++++++ plexus-languages-atinject.patch ++++++
--- /var/tmp/diff_new_pack.ULOayA/_old  2026-11-06 20:07:10.252361378 +0100
+++ /var/tmp/diff_new_pack.ULOayA/_new  2026-11-06 20:07:10.256361545 +0100
@@ -1,9 +1,11 @@
+diff --git a/plexus-java/src/main/java9/module-info.java 
b/plexus-java/src/main/java9/module-info.java
+index 1a68b2a..c8c1229 100644
 --- a/plexus-java/src/main/java9/module-info.java
 +++ b/plexus-java/src/main/java9/module-info.java
-@@ -19,6 +19,7 @@
+@@ -18,6 +18,7 @@
+  */
  module org.codehaus.plexus.languages.java {
      requires com.github.javaparser.core;
-     requires org.objectweb.asm;
 +    requires java.inject;
  
      exports org.codehaus.plexus.languages.java.jpms;

Reply via email to