X-Git-Url: https://git.argeo.org/?a=blobdiff_plain;f=src%2Forg%2Fargeo%2Fbuild%2FRepackage.java;h=02158ad147231f3a5e7e552d8549e2c31416e3c2;hb=refs%2Fheads%2Funstable;hp=6ec6b623f5c124caf160aeee90d0ba59a4c54ced;hpb=be94cc57782eedf36477f7980a9625fe9dddd707;p=cc0%2Fargeo-build.git diff --git a/src/org/argeo/build/Repackage.java b/src/org/argeo/build/Repackage.java index 6ec6b62..ee5072c 100644 --- a/src/org/argeo/build/Repackage.java +++ b/src/org/argeo/build/Repackage.java @@ -9,7 +9,7 @@ import static java.nio.file.FileVisitResult.CONTINUE; import static java.nio.file.StandardOpenOption.APPEND; import static java.nio.file.StandardOpenOption.CREATE; import static java.util.jar.Attributes.Name.MANIFEST_VERSION; -import static org.argeo.build.Repackage.ManifestHeader.ARGEO_DO_NOT_MODIFY; +import static org.argeo.build.Repackage.ManifestHeader.ARGEO_ORIGIN_DO_NOT_MODIFY; import static org.argeo.build.Repackage.ManifestHeader.ARGEO_ORIGIN_M2; import static org.argeo.build.Repackage.ManifestHeader.ARGEO_ORIGIN_M2_MERGE; import static org.argeo.build.Repackage.ManifestHeader.ARGEO_ORIGIN_M2_REPO; @@ -23,6 +23,8 @@ import static org.argeo.build.Repackage.ManifestHeader.BUNDLE_VERSION; import static org.argeo.build.Repackage.ManifestHeader.ECLIPSE_SOURCE_BUNDLE; import static org.argeo.build.Repackage.ManifestHeader.EXPORT_PACKAGE; import static org.argeo.build.Repackage.ManifestHeader.IMPORT_PACKAGE; +import static org.argeo.build.Repackage.ManifestHeader.REQUIRE_CAPABILITY; +//import static org.argeo.build.Repackage.ManifestHeader.REQUIRE_BUNDLE; import static org.argeo.build.Repackage.ManifestHeader.SPDX_LICENSE_IDENTIFIER; import java.io.BufferedWriter; @@ -32,8 +34,8 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.lang.System.Logger; -import java.net.MalformedURLException; -import java.net.URL; +import java.net.URI; +import java.net.URISyntaxException; import java.nio.charset.StandardCharsets; import java.nio.file.DirectoryStream; import java.nio.file.FileSystem; @@ -148,8 +150,12 @@ public class Repackage { EXPORT_PACKAGE("Export-Package"), // /** OSGi imported packages list. */ IMPORT_PACKAGE("Import-Package"), // - /** OSGi path to embedded jar. */ - BUNDLE_CLASSPATH("Bundle-Classpath"), // + /** Require capability. */ + REQUIRE_CAPABILITY("Require-Capability"), // +// /** OSGi required bundles. */ +// REQUIRE_BUNDLE("Require-Bundle"), // +// /** OSGi path to embedded jar. */ +// BUNDLE_CLASSPATH("Bundle-Classpath"), // // Java /** Java module name. */ AUTOMATIC_MODULE_NAME("Automatic-Module-Name"), // @@ -174,10 +180,12 @@ public class Repackage { /** Maven repository, if not the default one. */ ARGEO_ORIGIN_M2_REPO("Argeo-Origin-M2-Repo"), // /** - * Do not perform BND analysis of the origin component. Typically Import_package + * Do not perform BND analysis of the origin component. Typically Import-Package * and Export-Package will be kept untouched. */ ARGEO_ORIGIN_NO_METADATA_GENERATION("Argeo-Origin-NoMetadataGeneration"), // + /** Keep JPMS module-info */ + ARGEO_ORIGIN_KEEP_MODULE_INFO("Argeo-Origin-KeepModuleInfo"), // // /** // * Embed the original jar without modifying it (may be required by some // * proprietary licenses, such as JCR Day License). @@ -187,7 +195,7 @@ public class Repackage { * Do not modify original jar (may be required by some proprietary licenses, * such as JCR Day License). */ - ARGEO_DO_NOT_MODIFY("Argeo-Origin-Do-Not-Modify"), // + ARGEO_ORIGIN_DO_NOT_MODIFY("Argeo-Origin-Do-Not-Modify"), // /** * Origin (non-Maven) URI of the component. It may be anything (jar, archive, * etc.). @@ -328,19 +336,22 @@ public class Repackage { void processCategory(Path categoryRelativePath) { try { Path targetCategoryBase = descriptorsBase.resolve(categoryRelativePath); - DirectoryStream bnds = Files.newDirectoryStream(targetCategoryBase, + try (DirectoryStream bnds = Files.newDirectoryStream(targetCategoryBase, (p) -> p.getFileName().toString().endsWith(".bnd") && !p.getFileName().toString().equals(COMMON_BND) - && !p.getFileName().toString().equals(MERGE_BND)); - for (Path p : bnds) { - processSingleM2ArtifactDistributionUnit(p); + && !p.getFileName().toString().equals(MERGE_BND))) { + for (Path p : bnds) { + processSingleM2ArtifactDistributionUnit(p); + } } - DirectoryStream dus = Files.newDirectoryStream(targetCategoryBase, (p) -> Files.isDirectory(p)); - for (Path duDir : dus) { - if (duDir.getFileName().toString().startsWith("eclipse-")) { - processEclipseArchive(duDir); - } else { - processM2BasedDistributionUnit(duDir); + try (DirectoryStream dus = Files.newDirectoryStream(targetCategoryBase, + (p) -> Files.isDirectory(p))) { + for (Path duDir : dus) { + if (duDir.getFileName().toString().startsWith("eclipse-")) { + processEclipseArchive(duDir); + } else { + processM2BasedDistributionUnit(duDir); + } } } } catch (IOException e) { @@ -373,7 +384,7 @@ public class Repackage { Path downloaded = downloadMaven(fileProps, artifact); boolean doNotModify = Boolean - .parseBoolean(fileProps.getOrDefault(ARGEO_DO_NOT_MODIFY.toString(), "false").toString()); + .parseBoolean(fileProps.getOrDefault(ARGEO_ORIGIN_DO_NOT_MODIFY.toString(), "false").toString()); if (doNotModify) { processNotModified(targetCategoryBase, downloaded, fileProps, artifact); return; @@ -425,57 +436,59 @@ public class Repackage { } m2Version = m2Version.substring(1); - DirectoryStream ds = Files.newDirectoryStream(duDir, + try (DirectoryStream ds = Files.newDirectoryStream(duDir, (p) -> p.getFileName().toString().endsWith(".bnd") && !p.getFileName().toString().equals(COMMON_BND) - && !p.getFileName().toString().equals(MERGE_BND)); - for (Path p : ds) { - Properties fileProps = new Properties(); - try (InputStream in = Files.newInputStream(p)) { - fileProps.load(in); - } - String m2Coordinates = fileProps.getProperty(ARGEO_ORIGIN_M2.toString()); - M2Artifact artifact = new M2Artifact(m2Coordinates); - if (artifact.getVersion() == null) { - artifact.setVersion(m2Version); - } else { - logger.log(WARNING, p.getFileName() + " : Using version " + artifact.getVersion() - + " specified in descriptor rather than " + m2Version + " specified in " + COMMON_BND); - } - - // prepare manifest entries - Properties mergedProps = new Properties(); - mergedProps.putAll(commonProps); + && !p.getFileName().toString().equals(MERGE_BND))) { + for (Path p : ds) { + Properties fileProps = new Properties(); + try (InputStream in = Files.newInputStream(p)) { + fileProps.load(in); + } + String m2Coordinates = fileProps.getProperty(ARGEO_ORIGIN_M2.toString()); + M2Artifact artifact = new M2Artifact(m2Coordinates); + if (artifact.getVersion() == null) { + artifact.setVersion(m2Version); + } else { + logger.log(DEBUG, p.getFileName() + " : Using version " + artifact.getVersion() + + " specified in descriptor rather than " + m2Version + " specified in " + COMMON_BND); + } - fileEntries: for (Object key : fileProps.keySet()) { - if (ARGEO_ORIGIN_M2.toString().equals(key)) - continue fileEntries; - String value = fileProps.getProperty(key.toString()); - Object previousValue = mergedProps.put(key.toString(), value); - if (previousValue != null) { - logger.log(WARNING, - commonBnd + ": " + key + " was " + previousValue + ", overridden with " + value); + // prepare manifest entries + Properties mergedProps = new Properties(); + mergedProps.putAll(commonProps); + + fileEntries: for (Object key : fileProps.keySet()) { + if (ARGEO_ORIGIN_M2.toString().equals(key)) + continue fileEntries; + String value = fileProps.getProperty(key.toString()); + Object previousValue = mergedProps.put(key.toString(), value); + if (previousValue != null) { + logger.log(WARNING, + commonBnd + ": " + key + " was " + previousValue + ", overridden with " + value); + } + } + mergedProps.put(ARGEO_ORIGIN_M2.toString(), artifact.toM2Coordinates()); + if (!mergedProps.containsKey(BUNDLE_SYMBOLICNAME.toString())) { + // use file name as symbolic name + String symbolicName = p.getFileName().toString(); + symbolicName = symbolicName.substring(0, symbolicName.length() - ".bnd".length()); + mergedProps.put(BUNDLE_SYMBOLICNAME.toString(), symbolicName); } - } - mergedProps.put(ARGEO_ORIGIN_M2.toString(), artifact.toM2Coordinates()); - if (!mergedProps.containsKey(BUNDLE_SYMBOLICNAME.toString())) { - // use file name as symbolic name - String symbolicName = p.getFileName().toString(); - symbolicName = symbolicName.substring(0, symbolicName.length() - ".bnd".length()); - mergedProps.put(BUNDLE_SYMBOLICNAME.toString(), symbolicName); - } - // download - Path downloaded = downloadMaven(mergedProps, artifact); + // download + Path downloaded = downloadMaven(mergedProps, artifact); - boolean doNotModify = Boolean - .parseBoolean(mergedProps.getOrDefault(ARGEO_DO_NOT_MODIFY.toString(), "false").toString()); - if (doNotModify) { - processNotModified(targetCategoryBase, downloaded, mergedProps, artifact); - } else { - A2Origin origin = new A2Origin(); - Path targetBundleDir = processBndJar(downloaded, targetCategoryBase, mergedProps, artifact, origin); - downloadAndProcessM2Sources(mergedProps, artifact, targetBundleDir, false, false); - createJar(targetBundleDir, origin); + boolean doNotModify = Boolean.parseBoolean( + mergedProps.getOrDefault(ARGEO_ORIGIN_DO_NOT_MODIFY.toString(), "false").toString()); + if (doNotModify) { + processNotModified(targetCategoryBase, downloaded, mergedProps, artifact); + } else { + A2Origin origin = new A2Origin(); + Path targetBundleDir = processBndJar(downloaded, targetCategoryBase, mergedProps, artifact, + origin); + downloadAndProcessM2Sources(mergedProps, artifact, targetBundleDir, false, false); + createJar(targetBundleDir, origin); + } } } } catch (IOException e) { @@ -578,9 +591,9 @@ public class Repackage { } if (entry.getName().endsWith("NOTICE") || entry.getName().endsWith("NOTICE.txt") - || entry.getName().endsWith("LICENSE") || entry.getName().endsWith("LICENSE.md") - || entry.getName().endsWith("LICENSE-notice.md") || entry.getName().endsWith("COPYING") - || entry.getName().endsWith("COPYING.LESSER")) { + || entry.getName().endsWith("NOTICE.md") || entry.getName().endsWith("LICENSE") + || entry.getName().endsWith("LICENSE.md") || entry.getName().endsWith("LICENSE-notice.md") + || entry.getName().endsWith("COPYING") || entry.getName().endsWith("COPYING.LESSER")) { Path artifactOriginDir = bundleDir.resolve(ARGEO_ORIGIN).resolve(artifact.getGroupId()) .resolve(artifact.getArtifactId()); Path target = artifactOriginDir.resolve(entry.getName()); @@ -653,7 +666,7 @@ public class Repackage { case "Created-By": continue keys; } - if ("Require-Capability".equals(key.toString()) + if (REQUIRE_CAPABILITY.toString().equals(key.toString()) && value.toString().equals("osgi.ee;filter:=\"(&(osgi.ee=JavaSE)(version=1.1))\"")) { origin.deleted.add("MANIFEST header " + key); continue keys;// hack for very old classes @@ -727,7 +740,7 @@ public class Repackage { case "Created-By": continue keys; } - if ("Require-Capability".equals(key.toString()) + if (REQUIRE_CAPABILITY.toString().equals(key.toString()) && value.toString().equals("osgi.ee;filter:=\"(&(osgi.ee=JavaSE)(version=1.1))\"")) { origin.deleted.add("MANIFEST header " + key); continue keys;// !! hack for very old classes @@ -773,7 +786,7 @@ public class Repackage { : null; String alternateUri = props.getProperty(ARGEO_ORIGIN_SOURCES_URI.toString()); M2Artifact sourcesArtifact = new M2Artifact(artifact.toM2Coordinates(), "sources"); - URL sourcesUrl = alternateUri != null ? new URL(alternateUri) + URI sourcesUrl = alternateUri != null ? new URI(alternateUri) : M2ConventionsUtils.mavenRepoUrl(repoStr, sourcesArtifact); Path sourcesDownloaded = downloadMaven(sourcesUrl, sourcesArtifact); processM2SourceJar(sourcesDownloaded, targetBundleDir, merging ? artifact : null, unmodified); @@ -847,13 +860,17 @@ public class Repackage { ? props.getProperty(ARGEO_ORIGIN_M2_REPO.toString()) : null; String alternateUri = props.getProperty(ARGEO_ORIGIN_URI.toString()); - URL url = alternateUri != null ? new URL(alternateUri) : M2ConventionsUtils.mavenRepoUrl(repoStr, artifact); - return downloadMaven(url, artifact); + try { + URI uri = alternateUri != null ? new URI(alternateUri) : M2ConventionsUtils.mavenRepoUrl(repoStr, artifact); + return downloadMaven(uri, artifact); + } catch (URISyntaxException e) { + throw new IllegalArgumentException("Wrong aritfact URI", e); + } } /** Download a Maven artifact. */ - Path downloadMaven(URL url, M2Artifact artifact) throws IOException { - return download(url, mavenBase, M2ConventionsUtils.artifactPath("", artifact)); + Path downloadMaven(URI uri, M2Artifact artifact) throws IOException { + return download(uri, mavenBase, M2ConventionsUtils.artifactPath("", artifact)); } /* @@ -948,12 +965,13 @@ public class Repackage { } }); - DirectoryStream dirs = Files.newDirectoryStream(targetCategoryBase, (p) -> Files.isDirectory(p) - && p.getFileName().toString().indexOf('.') >= 0 && !p.getFileName().toString().endsWith(".src")); - for (Path bundleDir : dirs) { - A2Origin origin = origins.get(bundleDir); - Objects.requireNonNull(origin, "No A2 origin found for " + bundleDir); - createJar(bundleDir, origin); + try (DirectoryStream dirs = Files.newDirectoryStream(targetCategoryBase, (p) -> Files.isDirectory(p) + && p.getFileName().toString().indexOf('.') >= 0 && !p.getFileName().toString().endsWith(".src"))) { + for (Path bundleDir : dirs) { + A2Origin origin = origins.get(bundleDir); + Objects.requireNonNull(origin, "No A2 origin found for " + bundleDir); + createJar(bundleDir, origin); + } } } catch (IOException e) { throw new RuntimeException("Cannot process " + duDir, e); @@ -1011,8 +1029,10 @@ public class Repackage { /** Normalise a single (that is, non-merged) bundle. */ Path processBundleJar(Path file, Path targetBase, Map entries, A2Origin origin) throws IOException { // boolean embed = Boolean.parseBoolean(entries.getOrDefault(ARGEO_ORIGIN_EMBED.toString(), "false").toString()); - boolean doNotModify = Boolean - .parseBoolean(entries.getOrDefault(ManifestHeader.ARGEO_DO_NOT_MODIFY.toString(), "false").toString()); + boolean doNotModify = Boolean.parseBoolean( + entries.getOrDefault(ManifestHeader.ARGEO_ORIGIN_DO_NOT_MODIFY.toString(), "false").toString()); + boolean keepModuleInfo = Boolean.parseBoolean( + entries.getOrDefault(ManifestHeader.ARGEO_ORIGIN_KEEP_MODULE_INFO.toString(), "false").toString()); NameVersion nameVersion; Path bundleDir; // singleton @@ -1045,13 +1065,8 @@ public class Repackage { nameVersion = new NameVersion(ourSymbolicName, ourVersion); } else { nameVersion = nameVersionFromManifest(manifest); - if (nameVersion == null) { - logger.log(WARNING, file + " has no symbolic name, trying name/version based on its name"); - // hack for weird issue with JNA jar in Eclipse - String[] arr_ = file.getFileName().toString().split("_"); - String[] arrDot = arr_[1].split("\\."); - nameVersion = new NameVersion(arr_[0], arrDot[0]); - } + if (nameVersion == null) + throw new IllegalStateException("Could not compute name/version from Manifest"); if (ourVersion != null && !nameVersion.getVersion().equals(ourVersion)) { logger.log(WARNING, "Original version is " + nameVersion.getVersion() + " while new version is " + ourVersion); @@ -1076,7 +1091,7 @@ public class Repackage { } // force Java 9 module name - entries.put(ManifestHeader.AUTOMATIC_MODULE_NAME.toString(), nameVersion.getName()); + entries.put(AUTOMATIC_MODULE_NAME.toString(), nameVersion.getName()); boolean isNative = false; String os = null; @@ -1100,8 +1115,12 @@ public class Repackage { continue entries; } if (entry.getName().endsWith("module-info.class")) { // skip Java 9 module info - origin.deleted.add("Java module information (module-info.class)"); - continue entries; + if (keepModuleInfo) { + entries.remove(AUTOMATIC_MODULE_NAME.toString()); + } else { + origin.deleted.add("Java module information (module-info.class)"); + continue entries; + } } if (entry.getName().startsWith("META-INF/versions/")) { // skip multi-version origin.deleted.add("additional Java versions (META-INF/versions)"); @@ -1127,8 +1146,9 @@ public class Repackage { Files.copy(jarIn, target); // native libraries + boolean removeDllFromJar = true; if (isNative && (entry.getName().endsWith(".so") || entry.getName().endsWith(".dll") - || entry.getName().endsWith(".jnilib"))) { + || entry.getName().endsWith(".jnilib") || entry.getName().endsWith(".a"))) { Path categoryDir = bundleDir.getParent(); boolean copyDll = false; Path targetDll = categoryDir.resolve(bundleDir.relativize(target)); @@ -1150,8 +1170,11 @@ public class Repackage { Files.delete(targetDll); Files.copy(target, targetDll); } - Files.delete(target); - origin.deleted.add(bundleDir.relativize(target).toString()); + + if (removeDllFromJar) { + Files.delete(target); + origin.deleted.add(bundleDir.relativize(target).toString()); + } } logger.log(TRACE, () -> "Copied " + target); } @@ -1176,6 +1199,8 @@ public class Repackage { if (wasDifferent) { if (SPDX_LICENSE_IDENTIFIER.toString().equals(key) && previousValue != null) keepPrevious = true; + if (REQUIRE_CAPABILITY.toString().equals(key) && previousValue != null) + keepPrevious = true; else if (BUNDLE_VERSION.toString().equals(key) && wasDifferent) if (previousValue.equals(value + ".0")) // typically a Maven first release keepPrevious = true; @@ -1202,7 +1227,7 @@ public class Repackage { } // !! hack to remove unresolvable - if (key.equals("Provide-Capability") || key.equals("Require-Capability")) + if (key.equals("Provide-Capability") || key.equals(REQUIRE_CAPABILITY.toString())) if (nameVersion.getName().equals("osgi.core") || nameVersion.getName().equals("osgi.cmpn")) { manifest.getMainAttributes().remove(key); origin.deleted.add("MANIFEST header " + key); @@ -1213,7 +1238,11 @@ public class Repackage { for (Iterator> manifestEntries = manifest.getMainAttributes().entrySet() .iterator(); manifestEntries.hasNext();) { Map.Entry manifestEntry = manifestEntries.next(); - switch (manifestEntry.getKey().toString()) { + String key = manifestEntry.getKey().toString(); + // TODO make it more generic +// if (key.equals(REQUIRE_BUNDLE.toString()) && nameVersion.getName().equals("com.sun.jna.platform")) +// manifestEntries.remove(); + switch (key) { case "Archiver-Version": case "Build-By": case "Created-By": @@ -1314,12 +1343,12 @@ public class Repackage { } /** Try to download from an URI. */ - Path tryDownloadArchive(String uri, Path dir) throws IOException { + Path tryDownloadArchive(String uriStr, Path dir) throws IOException { // find mirror List urlBases = null; String uriPrefix = null; uriPrefixes: for (String uriPref : mirrors.keySet()) { - if (uri.startsWith(uriPref)) { + if (uriStr.startsWith(uriPref)) { if (mirrors.get(uriPref).size() > 0) { urlBases = mirrors.get(uriPref); uriPrefix = uriPref; @@ -1329,54 +1358,56 @@ public class Repackage { } if (urlBases == null) try { - return downloadArchive(new URL(uri), dir); - } catch (FileNotFoundException e) { - throw new FileNotFoundException("Cannot find " + uri); + return downloadArchive(new URI(uriStr), dir); + } catch (FileNotFoundException | URISyntaxException e) { + throw new FileNotFoundException("Cannot find " + uriStr); } // try to download for (String urlBase : urlBases) { - String relativePath = uri.substring(uriPrefix.length()); - URL url = new URL(urlBase + relativePath); + String relativePath = uriStr.substring(uriPrefix.length()); + String uStr = urlBase + relativePath; try { - return downloadArchive(url, dir); - } catch (FileNotFoundException e) { - logger.log(WARNING, "Cannot download " + url + ", trying another mirror"); + return downloadArchive(new URI(uStr), dir); + } catch (FileNotFoundException | URISyntaxException e) { + logger.log(WARNING, "Cannot download " + uStr + ", trying another mirror"); } } - throw new FileNotFoundException("Cannot find " + uri); + throw new FileNotFoundException("Cannot find " + uriStr); } /** - * Effectively download. Synchronised in order to avoid downloading twice in - * parallel. + * Effectively download an archive. */ - synchronized Path downloadArchive(URL url, Path dir) throws IOException { - return download(url, dir, (String) null); + Path downloadArchive(URI uri, Path dir) throws IOException { + return download(uri, dir, (String) null); } - /** Effectively download. */ - Path download(URL url, Path dir, String name) throws IOException { + /** + * Effectively download. Synchronised in order to avoid downloading twice in + * parallel. + */ + synchronized Path download(URI uri, Path dir, String name) throws IOException { Path dest; if (name == null) { // We use also use parent directory in case the archive itself has a fixed name - String[] segments = url.getPath().split("/"); + String[] segments = uri.getPath().split("/"); name = segments.length > 1 ? segments[segments.length - 2] + '-' + segments[segments.length - 1] : segments[segments.length - 1]; } dest = dir.resolve(name); if (Files.exists(dest)) { - logger.log(TRACE, () -> "File " + dest + " already exists for " + url + ", not downloading again"); + logger.log(TRACE, () -> "File " + dest + " already exists for " + uri + ", not downloading again"); return dest; } else { Files.createDirectories(dest.getParent()); } - try (InputStream in = url.openStream()) { + try (InputStream in = uri.toURL().openStream()) { Files.copy(in, dest); - logger.log(DEBUG, () -> "Downloaded " + dest + " from " + url); + logger.log(DEBUG, () -> "Downloaded " + dest + " from " + uri); } return dest; } @@ -1636,14 +1667,9 @@ class M2ConventionsUtils { } /** Absolute path to the file */ - static URL mavenRepoUrl(String repoBase, M2Artifact artifact) { - String url = artifactUrl(repoBase == null ? MAVEN_CENTRAL_BASE_URL : repoBase, artifact); - try { - return new URL(url); - } catch (MalformedURLException e) { - // it should not happen - throw new IllegalStateException(e); - } + static URI mavenRepoUrl(String repoBase, M2Artifact artifact) throws URISyntaxException { + String uri = artifactUrl(repoBase == null ? MAVEN_CENTRAL_BASE_URL : repoBase, artifact); + return new URI(uri); } /** Absolute path to the directories where the files will be stored */