11package de .jjohannes .gradle .javamodules ;
22
3+ import org .gradle .api .NonNullApi ;
34import org .gradle .api .artifacts .CacheableRule ;
45import org .gradle .api .artifacts .transform .InputArtifact ;
56import org .gradle .api .artifacts .transform .TransformAction ;
67import org .gradle .api .artifacts .transform .TransformOutputs ;
78import org .gradle .api .artifacts .transform .TransformParameters ;
8- import org .gradle .api .file .ConfigurableFileCollection ;
99import org .gradle .api .file .FileSystemLocation ;
10+ import org .gradle .api .file .RegularFile ;
11+ import org .gradle .api .provider .ListProperty ;
1012import org .gradle .api .provider .MapProperty ;
1113import org .gradle .api .provider .Property ;
1214import org .gradle .api .provider .Provider ;
1618import org .objectweb .asm .ModuleVisitor ;
1719import org .objectweb .asm .Opcodes ;
1820
19- import javax .annotation .Nonnull ;
20- import java .io .*;
21+ import javax .annotation .Nullable ;
22+ import java .io .BufferedReader ;
23+ import java .io .ByteArrayInputStream ;
24+ import java .io .File ;
25+ import java .io .IOException ;
26+ import java .io .InputStreamReader ;
27+ import java .io .OutputStream ;
2128import java .nio .charset .StandardCharsets ;
2229import java .nio .file .Files ;
23- import java .util . LinkedHashMap ;
30+ import java .nio . file . Path ;
2431import java .util .Collection ;
32+ import java .util .LinkedHashMap ;
33+ import java .util .List ;
2534import java .util .Map ;
26- import java .util .Optional ;
27- import java .util .jar .*;
35+ import java .util .jar .JarEntry ;
36+ import java .util .jar .JarInputStream ;
37+ import java .util .jar .JarOutputStream ;
38+ import java .util .jar .Manifest ;
2839import java .util .regex .Pattern ;
29- import java .util .stream .Stream ;
3040import java .util .zip .ZipEntry ;
3141import java .util .zip .ZipException ;
3242
3646 * was defined for it. This way we make sure that all Jars are turned into modules.
3747 */
3848@ CacheableRule
49+ @ NonNullApi
3950abstract public class ExtraModuleInfoTransform implements TransformAction <ExtraModuleInfoTransform .Parameter > {
4051
4152 private static final Pattern MODULE_INFO_CLASS_MRJAR_PATH = Pattern .compile ("META-INF/versions/\\ d+/module-info.class" );
@@ -47,39 +58,90 @@ public interface Parameter extends TransformParameters {
4758 MapProperty <String , ModuleSpec > getModuleSpecs ();
4859 @ Input
4960 Property <Boolean > getFailOnMissingModuleInfo ();
61+ @ Input
62+ ListProperty <String > getMergeJarIds ();
5063 @ InputFiles
51- ConfigurableFileCollection getMergeJars ();
64+ ListProperty < RegularFile > getMergeJars ();
5265 }
5366
5467 @ InputArtifact
5568 protected abstract Provider <FileSystemLocation > getInputArtifact ();
5669
5770 @ Override
58- public void transform (@ Nonnull TransformOutputs outputs ) {
71+ public void transform (TransformOutputs outputs ) {
5972 Map <String , ModuleSpec > moduleSpecs = getParameters ().getModuleSpecs ().get ();
6073 File originalJar = getInputArtifact ().get ().getAsFile ();
61- String originalJarName = originalJar .getName ();
6274
63- if (moduleSpecs .containsKey (originalJarName ) && moduleSpecs .get (originalJarName ) instanceof ModuleInfo ) {
64- addModuleDescriptor (originalJar , getModuleJar (outputs , originalJar ), (ModuleInfo ) moduleSpecs .get (originalJarName ));
65- } else if (moduleSpecs .containsKey (originalJarName ) && moduleSpecs .get (originalJarName ) instanceof AutomaticModuleName ) {
66- addAutomaticModuleName (originalJar , getModuleJar (outputs , originalJar ), (AutomaticModuleName ) moduleSpecs .get (originalJarName ));
75+ ModuleSpec moduleSpec = findModuleSpec (originalJar );
76+
77+ if (moduleSpec instanceof ModuleInfo ) {
78+ addModuleDescriptor (originalJar , getModuleJar (outputs , originalJar ), (ModuleInfo ) moduleSpec );
79+ } else if (moduleSpec instanceof AutomaticModuleName ) {
80+ addAutomaticModuleName (originalJar , getModuleJar (outputs , originalJar ), (AutomaticModuleName ) moduleSpec );
6781 } else if (isModule (originalJar )) {
6882 outputs .file (originalJar );
6983 } else if (isAutoModule (originalJar )) {
7084 outputs .file (originalJar );
7185 } else if (!willBeMerged (originalJar , moduleSpecs .values ())) { // No output if this Jar will be merged
7286 if (getParameters ().getFailOnMissingModuleInfo ().get ()) {
73- throw new RuntimeException ("Not a module and no mapping defined: " + originalJarName );
87+ throw new RuntimeException ("Not a module and no mapping defined: " + originalJar . getName () );
7488 } else {
7589 outputs .file (originalJar );
7690 }
7791 }
7892 }
7993
94+ @ Nullable
95+ private ModuleSpec findModuleSpec (File originalJar ) {
96+ Map <String , ModuleSpec > moduleSpecs = getParameters ().getModuleSpecs ().get ();
97+
98+ String gaCoordinates = gaCoordinatesFromFilePath (originalJar .toPath ());
99+ String originalJarName = originalJar .getName ();
100+
101+ if (moduleSpecs .containsKey (gaCoordinates )) {
102+ return moduleSpecs .get (gaCoordinates );
103+ }
104+ if (moduleSpecs .containsKey (originalJarName )) {
105+ return moduleSpecs .get (originalJarName );
106+ }
107+
108+ return null ;
109+ }
110+
80111 private boolean willBeMerged (File originalJar , Collection <ModuleSpec > modules ) {
81- return modules .stream ().anyMatch (module ->
82- module .getMergedJars ().stream ().anyMatch (toMerge -> toMerge .equals (originalJar .getName ())));
112+ return modules .stream ().anyMatch (module -> module .getMergedJars ().stream ().anyMatch (toMerge ->
113+ toMerge .equals (gaCoordinatesFromFilePath (originalJar .toPath ())) || toMerge .equals (originalJar .getName ())));
114+ }
115+
116+ /**
117+ * Attempts to parse 'group' and 'name' coordinates from a path like:
118+ * .gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.36/6c62681a2f655b49963a5983b8b0950a6120ae14/slf4j-api-1.7.36.jar
119+ */
120+ @ Nullable
121+ private String gaCoordinatesFromFilePath (Path path ) {
122+ String version = versionFromFilePath (path );
123+ if (version == null ) {
124+ return null ;
125+ }
126+
127+ String group = path .getName (path .getNameCount () - 5 ).toString ();
128+ String name = path .getName (path .getNameCount () - 4 ).toString ();
129+
130+ return group + ":" + name ;
131+ }
132+
133+ @ Nullable
134+ private String versionFromFilePath (Path path ) {
135+ if (path .getNameCount () < 5 ) {
136+ return null ;
137+ }
138+
139+ String version = path .getName (path .getNameCount () - 3 ).toString ();
140+ if (!path .getFileName ().toString ().contains (version )) {
141+ return null ;
142+ }
143+
144+ return version ;
83145 }
84146
85147 private boolean isModule (File jar ) {
@@ -156,15 +218,15 @@ private void addModuleDescriptor(File originalJar, File moduleJar, ModuleInfo mo
156218 Map <String , String []> providers = copyAndExtractProviders (inputStream , outputStream );
157219 mergeJars (moduleInfo , outputStream );
158220 outputStream .putNextEntry (new JarEntry ("module-info.class" ));
159- outputStream .write (addModuleInfo (moduleInfo , providers ));
221+ outputStream .write (addModuleInfo (moduleInfo , providers , versionFromFilePath ( originalJar . toPath ()) ));
160222 outputStream .closeEntry ();
161223 }
162224 } catch (IOException e ) {
163225 throw new RuntimeException (e );
164226 }
165227 }
166228
167- private static JarOutputStream newJarOutputStream (OutputStream out , Manifest manifest ) throws IOException {
229+ private static JarOutputStream newJarOutputStream (OutputStream out , @ Nullable Manifest manifest ) throws IOException {
168230 return manifest == null ? new JarOutputStream (out ) : new JarOutputStream (out , manifest );
169231 }
170232
@@ -199,10 +261,11 @@ private static String[] extractImplementations(byte[] content) {
199261 .toArray (String []::new );
200262 }
201263
202- private static byte [] addModuleInfo (ModuleInfo moduleInfo , Map <String , String []> providers ) {
264+ private byte [] addModuleInfo (ModuleInfo moduleInfo , Map <String , String []> providers , @ Nullable String version ) {
203265 ClassWriter classWriter = new ClassWriter (0 );
204266 classWriter .visit (Opcodes .V9 , Opcodes .ACC_MODULE , "module-info" , null , null , null );
205- ModuleVisitor moduleVisitor = classWriter .visitModule (moduleInfo .getModuleName (), Opcodes .ACC_OPEN , moduleInfo .moduleVersion );
267+ String moduleVersion = moduleInfo .getModuleVersion () == null ? version : moduleInfo .getModuleVersion ();
268+ ModuleVisitor moduleVisitor = classWriter .visitModule (moduleInfo .getModuleName (), Opcodes .ACC_OPEN , moduleVersion );
206269 for (String packageName : moduleInfo .exports ) {
207270 moduleVisitor .visitExport (packageName .replace ('.' , '/' ), 0 );
208271 }
@@ -229,14 +292,29 @@ private static byte[] addModuleInfo(ModuleInfo moduleInfo, Map<String, String[]>
229292 }
230293
231294 private void mergeJars (ModuleSpec moduleSpec , JarOutputStream outputStream ) throws IOException {
232- for (String mergeJar : moduleSpec .getMergedJars ()) {
233- Optional <File > mergeJarFile = getParameters ().getMergeJars ().getFiles ().stream ().filter (f -> f .getName ().equals (mergeJar )).findFirst ();
234- if (mergeJarFile .isPresent ()) {
235- try (JarInputStream toMergeInputStream = new JarInputStream (Files .newInputStream (mergeJarFile .get ().toPath ()))) {
295+ RegularFile mergeJarFile = null ;
296+ for (String identifier : moduleSpec .getMergedJars ()) {
297+ List <String > ids = getParameters ().getMergeJarIds ().get ();
298+ List <RegularFile > jarFiles = getParameters ().getMergeJars ().get ();
299+ for (int i = 0 ; i < ids .size (); i ++) {
300+ // referenced by 'group:version'
301+ if (ids .get (i ).equals (identifier )) {
302+ mergeJarFile = jarFiles .get (i );
303+ break ;
304+ }
305+ // referenced by 'jar file name'
306+ if (jarFiles .get (i ).getAsFile ().getName ().equals (identifier )) {
307+ mergeJarFile = jarFiles .get (i );
308+ break ;
309+ }
310+ }
311+
312+ if (mergeJarFile != null ) {
313+ try (JarInputStream toMergeInputStream = new JarInputStream (Files .newInputStream (mergeJarFile .getAsFile ().toPath ()))) {
236314 copyEntries (toMergeInputStream , outputStream );
237315 }
238316 } else {
239- throw new RuntimeException ("Jar not found: " + mergeJar );
317+ throw new RuntimeException ("Jar not found: " + identifier );
240318 }
241319 }
242320 }
0 commit comments