Revert not specifying export package versions
[cc0/argeo-build.git] / src / org / argeo / build / Repackage.java
index c72810bff625b9970bcefd28df0705b14b1d054f..b594f3a19b44e4aff5ee3d62a772e845a8928225 100644 (file)
@@ -98,13 +98,14 @@ public class Repackage {
 
                List<CompletableFuture<Void>> toDos = new ArrayList<>();
                for (int i = 1; i < args.length; i++) {
-                       Path p = Paths.get(args[i]);
-                       if (sequential)
-                               factory.processCategory(p);
+                       Path categoryPath = Paths.get(args[i]);
+                       factory.cleanPreviousFailedBuild(categoryPath);
+                       if (sequential) // sequential processing happens here
+                               factory.processCategory(categoryPath);
                        else
-                               toDos.add(CompletableFuture.runAsync(() -> factory.processCategory(p)));
+                               toDos.add(CompletableFuture.runAsync(() -> factory.processCategory(categoryPath)));
                }
-               if (!sequential)
+               if (!sequential)// parallel processing
                        CompletableFuture.allOf(toDos.toArray(new CompletableFuture[toDos.size()])).join();
 
                // Summary
@@ -115,6 +116,23 @@ public class Repackage {
                logger.log(INFO, "# License summary:\n" + sb);
        }
 
+       /** Deletes remaining sub directories. */
+       void cleanPreviousFailedBuild(Path categoryPath) {
+               Path outputCategoryPath = a2Base.resolve(categoryPath);
+               // clean previous failed build
+               try {
+                       for (Path subDir : Files.newDirectoryStream(outputCategoryPath, (d) -> Files.isDirectory(d))) {
+                               if (Files.exists(subDir)) {
+                                       logger.log(WARNING, "Bundle dir " + subDir
+                                                       + " already exists, probably from a previous failed build, deleting it...");
+                                       deleteDirectory(subDir);
+                               }
+                       }
+               } catch (IOException e) {
+                       logger.log(ERROR, "Cannot clean previous build", e);
+               }
+       }
+
        /** MANIFEST headers. */
        enum ManifestHeader {
                // OSGi
@@ -180,15 +198,35 @@ public class Repackage {
                ARGEO_ORIGIN_SOURCES_URI("Argeo-Origin-Sources-URI"), //
                ;
 
-               final String value;
+               final String headerName;
 
-               private ManifestHeader(String value) {
-                       this.value = value;
+               private ManifestHeader(String headerName) {
+                       this.headerName = headerName;
                }
 
                @Override
                public String toString() {
-                       return value;
+                       return headerName;
+               }
+
+               /** Get the value from either a {@link Manifest} or a {@link Properties}. */
+               String get(Object map) {
+                       if (map instanceof Manifest manifest)
+                               return manifest.getMainAttributes().getValue(headerName);
+                       else if (map instanceof Properties props)
+                               return props.getProperty(headerName);
+                       else
+                               throw new IllegalArgumentException("Unsupported mapping " + map.getClass());
+               }
+
+               /** Put the value into either a {@link Manifest} or a {@link Properties}. */
+               void put(Object map, String value) {
+                       if (map instanceof Manifest manifest)
+                               manifest.getMainAttributes().putValue(headerName, value);
+                       else if (map instanceof Properties props)
+                               props.setProperty(headerName, value);
+                       else
+                               throw new IllegalArgumentException("Unsupported mapping " + map.getClass());
                }
        }
 
@@ -564,6 +602,10 @@ public class Repackage {
                                                } else if (entry.getName().startsWith("org/apache/batik/")) {
                                                        logger.log(TRACE, "Skip " + entry.getName());
                                                        continue entries;
+                                               } else if (entry.getName().startsWith("META-INF/NOTICE")) {
+                                                       logger.log(WARNING, "Skip " + entry.getName() + " from " + artifact);
+                                                       // TODO merge them?
+                                                       continue entries;
                                                } else {
                                                        throw new IllegalStateException("File " + target + " from " + artifact + " already exists");
                                                }
@@ -717,7 +759,7 @@ public class Repackage {
                try (JarInputStream jarIn = new JarInputStream(Files.newInputStream(unmodifiedTarget))) {
                        manifest = jarIn.getManifest();
                }
-               createSourceJar(bundleDir, manifest, true);
+               createSourceJar(bundleDir, manifest, fileProps);
        }
 
        /** Download and integrates sources for a single Maven artifact. */
@@ -757,24 +799,26 @@ public class Repackage {
                                String relPath = entry.getName();
                                if (entry.isDirectory())
                                        continue entries;
-                               if (entry.getName().startsWith("META-INF")) {// skip META-INF entries
-                                       origin.deleted.add("META-INF directory from the sources" + mergingMsg);
+                               if (entry.getName().equals("META-INF/MANIFEST.MF")) {// skip META-INF entries
+                                       origin.deleted.add("MANIFEST.MF from the sources" + mergingMsg);
                                        continue entries;
                                }
-                               if (entry.getName().startsWith("module-info.java")) {// skip Java module information
-                                       origin.deleted.add("Java module information from the sources (module-info.java)" + mergingMsg);
-                                       continue entries;
-                               }
-                               if (entry.getName().startsWith("/")) { // absolute paths
-                                       int metaInfIndex = entry.getName().indexOf("META-INF");
-                                       if (metaInfIndex >= 0) {
-                                               relPath = entry.getName().substring(metaInfIndex);
-                                               origin.moved.add(" to " + relPath + " entry with absolute path " + entry.getName());
-                                       } else {
-                                               logger.log(WARNING, entry.getName() + " has an absolute path");
-                                               origin.deleted.add(entry.getName() + " from the sources" + mergingMsg);
+                               if (!unmodified) {
+                                       if (entry.getName().startsWith("module-info.java")) {// skip Java module information
+                                               origin.deleted.add("Java module information from the sources (module-info.java)" + mergingMsg);
+                                               continue entries;
+                                       }
+                                       if (entry.getName().startsWith("/")) { // absolute paths
+                                               int metaInfIndex = entry.getName().indexOf("META-INF");
+                                               if (metaInfIndex >= 0) {
+                                                       relPath = entry.getName().substring(metaInfIndex);
+                                                       origin.moved.add(" to " + relPath + " entry with absolute path " + entry.getName());
+                                               } else {
+                                                       logger.log(WARNING, entry.getName() + " has an absolute path");
+                                                       origin.deleted.add(entry.getName() + " from the sources" + mergingMsg);
+                                               }
+                                               continue entries;
                                        }
-                                       continue entries;
                                }
                                Path target = sourceDir.resolve(relPath);
                                Files.createDirectories(target.getParent());
@@ -787,7 +831,7 @@ public class Repackage {
                        }
                }
                // write the changes
-               if (separateSources) {
+               if (separateSources || unmodified) {
                        origin.appendChanges(sourceDir);
                } else {
                        origin.added.add("source code under OSGI-OPT/src");
@@ -1003,6 +1047,7 @@ public class Repackage {
                                        nameVersion.setName(ourSymbolicName);
                                }
                        }
+
                        bundleDir = targetBase.resolve(nameVersion.getName() + "." + nameVersion.getBranch());
 
                        // copy original MANIFEST
@@ -1028,7 +1073,6 @@ public class Repackage {
                                arch = libRelativePath.getName(1).toString();
                        }
 
-//                     if (!embed) {
                        // copy entries
                        JarEntry entry;
                        entries: while ((entry = jarIn.getNextJarEntry()) != null) {
@@ -1095,7 +1139,6 @@ public class Repackage {
                                        origin.deleted.add(bundleDir.relativize(target).toString());
                                }
                                logger.log(TRACE, () -> "Copied " + target);
-//                             }
                        }
                }
 
@@ -1108,11 +1151,6 @@ public class Repackage {
                                        entries.get(BUNDLE_SYMBOLICNAME.toString()) + ";singleton:=true");
                }
 
-//             if (embed) {// copy embedded jar
-//                     Files.copy(file, bundleDir.resolve(file.getFileName()));
-//                     entries.put(ManifestHeader.BUNDLE_CLASSPATH.toString(), file.getFileName().toString());
-//             }
-
                // Final MANIFEST decisions
                // We also check the original OSGi metadata and compare with our changes
                for (String key : entries.keySet()) {
@@ -1361,13 +1399,14 @@ public class Repackage {
                deleteDirectory(bundleDir);
 
                if (separateSources)
-                       createSourceJar(bundleDir, manifest, false);
+                       createSourceJar(bundleDir, manifest, null);
 
                return jarPath;
        }
 
        /** Package sources separately, in the Eclipse-SourceBundle format. */
-       void createSourceJar(Path bundleDir, Manifest manifest, boolean notModified) throws IOException {
+       void createSourceJar(Path bundleDir, Manifest manifest, Properties props) throws IOException {
+               boolean unmodified = props != null;
                Path bundleCategoryDir = bundleDir.getParent();
                Path sourceDir = bundleCategoryDir.resolve(bundleDir.toString() + ".src");
                if (!Files.exists(sourceDir)) {
@@ -1375,9 +1414,6 @@ public class Repackage {
                        return;
                }
 
-               if (!notModified)
-                       createReadMe(sourceDir, manifest);
-
                Path relPath = a2Base.relativize(bundleCategoryDir);
                Path srcCategoryDir = a2SrcBase.resolve(relPath);
                Path srcJarP = srcCategoryDir.resolve(sourceDir.getFileName() + ".jar");
@@ -1388,14 +1424,16 @@ public class Repackage {
                bundleSymbolicName = bundleSymbolicName.split(";")[0];
                Manifest srcManifest = new Manifest();
                srcManifest.getMainAttributes().put(MANIFEST_VERSION, "1.0");
-               srcManifest.getMainAttributes().putValue(BUNDLE_SYMBOLICNAME.toString(), bundleSymbolicName + ".src");
-               srcManifest.getMainAttributes().putValue(BUNDLE_VERSION.toString(),
-                               manifest.getMainAttributes().getValue(BUNDLE_VERSION.toString()).toString());
-               srcManifest.getMainAttributes().putValue(ECLIPSE_SOURCE_BUNDLE.toString(), bundleSymbolicName + ";version=\""
-                               + manifest.getMainAttributes().getValue(BUNDLE_VERSION.toString()) + "\"");
-
+               BUNDLE_SYMBOLICNAME.put(srcManifest, bundleSymbolicName + ".src");
+               BUNDLE_VERSION.put(srcManifest, BUNDLE_VERSION.get(manifest));
+               ECLIPSE_SOURCE_BUNDLE.put(srcManifest,
+                               bundleSymbolicName + ";version=\"" + BUNDLE_VERSION.get(manifest) + "\"");
+
+               // metadata
+               createReadMe(sourceDir, unmodified ? props : manifest);
+               // create jar
                try (JarOutputStream srcJarOut = new JarOutputStream(Files.newOutputStream(srcJarP), srcManifest)) {
-                       srcJarOut.setLevel(Deflater.BEST_COMPRESSION);
+                       // srcJarOut.setLevel(Deflater.BEST_COMPRESSION);
                        Files.walkFileTree(sourceDir, new SimpleFileVisitor<Path>() {
 
                                @Override
@@ -1418,10 +1456,10 @@ public class Repackage {
         * Generate a readme clarifying and prominently notifying of the repackaging and
         * modifications.
         */
-       void createReadMe(Path jarDir, Manifest manifest) throws IOException {
+       void createReadMe(Path jarDir, Object mapping) throws IOException {
                // write repackaged README
                try (BufferedWriter writer = Files.newBufferedWriter(jarDir.resolve(README_REPACKAGED))) {
-                       boolean merged = manifest.getMainAttributes().getValue(ARGEO_ORIGIN_M2_MERGE.toString()) != null;
+                       boolean merged = ARGEO_ORIGIN_M2_MERGE.get(mapping) != null;
                        if (merged)
                                writer.append("This component is a merging of third party components"
                                                + " in order to comply with A2 packaging standards.\n");
@@ -1430,7 +1468,7 @@ public class Repackage {
                                                + " in order to comply with A2 packaging standards.\n");
 
                        // license
-                       String spdxLicenseId = manifest.getMainAttributes().getValue(SPDX_LICENSE_IDENTIFIER.toString());
+                       String spdxLicenseId = SPDX_LICENSE_IDENTIFIER.get(mapping);
                        if (spdxLicenseId == null)
                                throw new IllegalStateException("An SPDX license id must have beend defined at this stage.");
                        writer.append("\nIt is redistributed under the following license:\n\n");
@@ -1447,31 +1485,35 @@ public class Repackage {
                                        writer.append("which is available here: https://spdx.org/licenses/" + spdxLicenseId + "\n");
                                }
                        } else {
-                               String url = manifest.getMainAttributes().getValue(BUNDLE_LICENSE.toString());
+                               String url = BUNDLE_LICENSE.get(mapping);
                                if (url != null) {
                                        writer.write("which is available here: " + url + "\n");
                                } else {
-                                       logger.log(ERROR, "No licne URL for " + jarDir);
+                                       logger.log(ERROR, "No licence URL for " + jarDir);
                                }
                        }
-                       writer.write("\n");
 
                        // origin
-                       String m2Repo = manifest.getMainAttributes().getValue(ARGEO_ORIGIN_M2_REPO.toString());
-                       String originDesc = manifest.getMainAttributes().getValue(ARGEO_ORIGIN_M2.toString());
+                       String originDesc = ARGEO_ORIGIN_URI.get(mapping);
                        if (originDesc != null)
-                               writer.append("The original component has M2 coordinates:\n" + originDesc.replace(',', '\n') + "\n"
-                                               + (m2Repo != null ? "\nin M2 repository " + m2Repo + "\n" : ""));
+                               writer.append("\nThe original component comes from " + originDesc + ".\n");
                        else {
-                               originDesc = manifest.getMainAttributes().getValue(ARGEO_ORIGIN_URI.toString());
+                               String m2Repo = ARGEO_ORIGIN_M2_REPO.get(mapping);
+                               originDesc = ARGEO_ORIGIN_M2.get(mapping);
                                if (originDesc != null)
-                                       writer.append("The original component comes from " + originDesc + ".\n");
+                                       writer.append("\nThe original component has M2 coordinates:\n" + originDesc.replace(',', '\n')
+                                                       + "\n" + (m2Repo != null ? "\nin M2 repository " + m2Repo + "\n" : ""));
                                else
                                        logger.log(ERROR, "Cannot find origin information in " + jarDir);
                        }
+                       String originSources = ARGEO_ORIGIN_SOURCES_URI.get(mapping);
+                       if (originSources != null)
+                               writer.append("\nThe original sources come from " + originSources + ".\n");
+
+                       if (Files.exists(jarDir.resolve(CHANGES)))
+                               writer.append("\nA detailed list of changes is available under " + CHANGES + ".\n");
 
-                       writer.append("\nA detailed list of changes is available under " + CHANGES + ".\n");
-                       if (!jarDir.getFileName().endsWith(".src")) {// binary archive
+                       if (!jarDir.getFileName().toString().endsWith(".src")) {// binary archive
                                if (separateSources)
                                        writer.append("Corresponding sources are available in the related archive named "
                                                        + jarDir.toString() + ".src.jar.\n");
@@ -1479,7 +1521,6 @@ public class Repackage {
                                        writer.append("Corresponding sources are available under OSGI-OPT/src.\n");
                        }
                }
-
        }
 
        /**
@@ -1498,6 +1539,8 @@ public class Repackage {
 
                /** Append changes to the A2-ORIGIN/changes file. */
                void appendChanges(Path baseDirectory) throws IOException {
+                       if (modified.isEmpty() && deleted.isEmpty() && added.isEmpty() && moved.isEmpty())
+                               return; // no changes
                        Path changesFile = baseDirectory.resolve(CHANGES);
                        Files.createDirectories(changesFile.getParent());
                        try (BufferedWriter writer = Files.newBufferedWriter(changesFile, APPEND, CREATE)) {