]> git.argeo.org Git - cc0/argeo-build.git/blobdiff - src/org/argeo/build/Repackage.java
Improve error reporting
[cc0/argeo-build.git] / src / org / argeo / build / Repackage.java
index 1ea7d536531ea298a0783463f471a4cd0ac4ac09..c72810bff625b9970bcefd28df0705b14b1d054f 100644 (file)
@@ -9,11 +9,12 @@ 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_ORIGIN_EMBED;
+import static org.argeo.build.Repackage.ManifestHeader.ARGEO_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;
 import static org.argeo.build.Repackage.ManifestHeader.ARGEO_ORIGIN_NO_METADATA_GENERATION;
+import static org.argeo.build.Repackage.ManifestHeader.ARGEO_ORIGIN_SOURCES_URI;
 import static org.argeo.build.Repackage.ManifestHeader.ARGEO_ORIGIN_URI;
 import static org.argeo.build.Repackage.ManifestHeader.AUTOMATIC_MODULE_NAME;
 import static org.argeo.build.Repackage.ManifestHeader.BUNDLE_LICENSE;
@@ -157,11 +158,11 @@ public class Repackage {
                 * and Export-Package will be kept untouched.
                 */
                ARGEO_ORIGIN_NO_METADATA_GENERATION("Argeo-Origin-NoMetadataGeneration"), //
-               /**
-                * Embed the original jar without modifying it (may be required by some
-                * proprietary licenses, such as JCR Day License).
-                */
-               ARGEO_ORIGIN_EMBED("Argeo-Origin-Embed"), //
+//             /**
+//              * Embed the original jar without modifying it (may be required by some
+//              * proprietary licenses, such as JCR Day License).
+//              */
+//             ARGEO_ORIGIN_EMBED("Argeo-Origin-Embed"), //
                /**
                 * Do not modify original jar (may be required by some proprietary licenses,
                 * such as JCR Day License).
@@ -172,6 +173,11 @@ public class Repackage {
                 * etc.).
                 */
                ARGEO_ORIGIN_URI("Argeo-Origin-URI"), //
+               /**
+                * Origin (non-Maven) URI of the source of the component. It may be anything
+                * (jar, archive, code repository, etc.).
+                */
+               ARGEO_ORIGIN_SOURCES_URI("Argeo-Origin-Sources-URI"), //
                ;
 
                final String value;
@@ -226,12 +232,12 @@ public class Repackage {
        final Map<String, List<String>> mirrors = new HashMap<String, List<String>>();
 
        /** Whether sources should be packaged separately */
-       final boolean sourceBundles;
+       final boolean separateSources;
 
        /** Constructor initialises the various variables */
        public Repackage(Path a2Base, Path descriptorsBase) {
-               sourceBundles = Boolean.parseBoolean(System.getenv(ENV_SOURCE_BUNDLES));
-               if (sourceBundles)
+               separateSources = Boolean.parseBoolean(System.getenv(ENV_SOURCE_BUNDLES));
+               if (separateSources)
                        logger.log(INFO, "Sources will be packaged separately");
 
                Objects.requireNonNull(a2Base);
@@ -241,7 +247,7 @@ public class Repackage {
 
                // TODO define and use a build base
                this.a2Base = a2Base;
-               this.a2SrcBase = a2Base.getParent().resolve(a2Base.getFileName() + ".src");
+               this.a2SrcBase = separateSources ? a2Base.getParent().resolve(a2Base.getFileName() + ".src") : a2Base;
                this.a2LibBase = a2Base.resolve("lib");
                this.descriptorsBase = descriptorsBase;
                if (!Files.exists(this.descriptorsBase))
@@ -312,10 +318,6 @@ public class Repackage {
                        try (InputStream in = Files.newInputStream(bndFile)) {
                                fileProps.load(in);
                        }
-                       String repoStr = fileProps.containsKey(ARGEO_ORIGIN_M2_REPO.toString())
-                                       ? fileProps.getProperty(ARGEO_ORIGIN_M2_REPO.toString())
-                                       : null;
-
                        // use file name as symbolic name
                        if (!fileProps.containsKey(BUNDLE_SYMBOLICNAME.toString())) {
                                String symbolicName = bndFile.getFileName().toString();
@@ -327,32 +329,20 @@ public class Repackage {
                        if (m2Coordinates == null)
                                throw new IllegalArgumentException("No M2 coordinates available for " + bndFile);
                        M2Artifact artifact = new M2Artifact(m2Coordinates);
-                       URL url = M2ConventionsUtils.mavenRepoUrl(repoStr, artifact);
-                       Path downloaded = downloadMaven(url, artifact);
-
-                       // some proprietary artifacts do not allow any modification
-                       // when releasing (with separate sources) we just copy it
-                       boolean doNotModify = Boolean.parseBoolean(
-                                       fileProps.getOrDefault(ManifestHeader.ARGEO_DO_NOT_MODIFY.toString(), "false").toString());
-                       if (doNotModify && sourceBundles) {
-                               Path unmodifiedTarget = targetCategoryBase.resolve(
-                                               fileProps.getProperty(BUNDLE_SYMBOLICNAME.toString()) + "." + artifact.getBranch() + ".jar");
-                               Files.copy(downloaded, unmodifiedTarget, StandardCopyOption.REPLACE_EXISTING);
-                               Path bundleDir = targetCategoryBase
-                                               .resolve(fileProps.getProperty(BUNDLE_SYMBOLICNAME.toString()) + "." + artifact.getBranch());
-                               downloadAndProcessM2Sources(repoStr, artifact, bundleDir, false);
-                               Manifest manifest;
-                               try (JarInputStream jarIn = new JarInputStream(Files.newInputStream(unmodifiedTarget))) {
-                                       manifest = jarIn.getManifest();
-                               }
-                               createSourceJar(bundleDir, manifest);
+
+                       Path downloaded = downloadMaven(fileProps, artifact);
+
+                       boolean doNotModify = Boolean
+                                       .parseBoolean(fileProps.getOrDefault(ARGEO_DO_NOT_MODIFY.toString(), "false").toString());
+                       if (doNotModify) {
+                               processNotModified(targetCategoryBase, downloaded, fileProps, artifact);
                                return;
                        }
 
                        // regular processing
                        A2Origin origin = new A2Origin();
                        Path bundleDir = processBndJar(downloaded, targetCategoryBase, fileProps, artifact, origin);
-                       downloadAndProcessM2Sources(repoStr, artifact, bundleDir, false);
+                       downloadAndProcessM2Sources(fileProps, artifact, bundleDir, false, false);
                        createJar(bundleDir, origin);
                } catch (Exception e) {
                        throw new RuntimeException("Cannot process " + bndFile, e);
@@ -413,39 +403,40 @@ public class Repackage {
                                }
 
                                // prepare manifest entries
-                               Properties mergeProps = new Properties();
-                               mergeProps.putAll(commonProps);
+                               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 = mergeProps.put(key.toString(), value);
+                                       Object previousValue = mergedProps.put(key.toString(), value);
                                        if (previousValue != null) {
                                                logger.log(WARNING,
                                                                commonBnd + ": " + key + " was " + previousValue + ", overridden with " + value);
                                        }
                                }
-                               mergeProps.put(ARGEO_ORIGIN_M2.toString(), artifact.toM2Coordinates());
-                               if (!mergeProps.containsKey(BUNDLE_SYMBOLICNAME.toString())) {
+                               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());
-                                       mergeProps.put(BUNDLE_SYMBOLICNAME.toString(), symbolicName);
+                                       mergedProps.put(BUNDLE_SYMBOLICNAME.toString(), symbolicName);
                                }
 
-                               String repoStr = mergeProps.containsKey(ARGEO_ORIGIN_M2_REPO.toString())
-                                               ? mergeProps.getProperty(ARGEO_ORIGIN_M2_REPO.toString())
-                                               : null;
-
                                // download
-                               URL url = M2ConventionsUtils.mavenRepoUrl(repoStr, artifact);
-                               Path downloaded = downloadMaven(url, artifact);
+                               Path downloaded = downloadMaven(mergedProps, artifact);
 
-                               A2Origin origin = new A2Origin();
-                               Path targetBundleDir = processBndJar(downloaded, targetCategoryBase, mergeProps, artifact, origin);
-                               downloadAndProcessM2Sources(repoStr, artifact, targetBundleDir, false);
-                               createJar(targetBundleDir, origin);
+                               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);
+                               }
                        }
                } catch (IOException e) {
                        throw new RuntimeException("Cannot process " + duDir, e);
@@ -485,10 +476,6 @@ public class Repackage {
                if (artifactsStr == null)
                        throw new IllegalArgumentException(mergeBnd + ": " + ARGEO_ORIGIN_M2_MERGE + " must be set");
 
-               String repoStr = mergeProps.containsKey(ARGEO_ORIGIN_M2_REPO.toString())
-                               ? mergeProps.getProperty(ARGEO_ORIGIN_M2_REPO.toString())
-                               : null;
-
                String bundleSymbolicName = mergeProps.getProperty(BUNDLE_SYMBOLICNAME.toString());
                if (bundleSymbolicName == null)
                        throw new IllegalArgumentException("Bundle-SymbolicName must be set in " + mergeBnd);
@@ -507,8 +494,7 @@ public class Repackage {
                        if (artifact.getVersion() == null)
                                artifact.setVersion(m2Version);
                        originDesc.add(artifact.toString());
-                       URL url = M2ConventionsUtils.mavenRepoUrl(repoStr, artifact);
-                       Path downloaded = downloadMaven(url, artifact);
+                       Path downloaded = downloadMaven(mergeProps, artifact);
                        JarEntry entry;
                        try (JarInputStream jarIn = new JarInputStream(Files.newInputStream(downloaded), false)) {
                                entries: while ((entry = jarIn.getNextJarEntry()) != null) {
@@ -588,7 +574,7 @@ public class Repackage {
                        origin.added.add("binary content of " + artifact);
 
                        // process sources
-                       downloadAndProcessM2Sources(repoStr, artifact, bundleDir, true);
+                       downloadAndProcessM2Sources(mergeProps, artifact, bundleDir, true, false);
                }
 
                // additional service files
@@ -624,8 +610,10 @@ public class Repackage {
                                        continue keys;
                                }
                                if ("Require-Capability".equals(key.toString())
-                                               && value.toString().equals("osgi.ee;filter:=\"(&(osgi.ee=JavaSE)(version=1.1))\""))
+                                               && 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
+                               }
                                entries.put(key.toString(), value.toString());
                        }
                } catch (Exception e) {
@@ -713,14 +701,38 @@ public class Repackage {
 
        }
 
-       /** Download and integrates sources for a single Maven artifact. */
-       void downloadAndProcessM2Sources(String repoStr, M2Artifact artifact, Path targetBundleDir, boolean merging)
+       /** Process an artifact that should not be modified. */
+       void processNotModified(Path targetCategoryBase, Path downloaded, Properties fileProps, M2Artifact artifact)
                        throws IOException {
+               // Some proprietary or signed artifacts do not allow any modification
+               // When releasing (with separate sources), we just copy it
+               Path unmodifiedTarget = targetCategoryBase
+                               .resolve(fileProps.getProperty(BUNDLE_SYMBOLICNAME.toString()) + "." + artifact.getBranch() + ".jar");
+               Files.createDirectories(unmodifiedTarget.getParent());
+               Files.copy(downloaded, unmodifiedTarget, StandardCopyOption.REPLACE_EXISTING);
+               Path bundleDir = targetCategoryBase
+                               .resolve(fileProps.getProperty(BUNDLE_SYMBOLICNAME.toString()) + "." + artifact.getBranch());
+               downloadAndProcessM2Sources(fileProps, artifact, bundleDir, false, true);
+               Manifest manifest;
+               try (JarInputStream jarIn = new JarInputStream(Files.newInputStream(unmodifiedTarget))) {
+                       manifest = jarIn.getManifest();
+               }
+               createSourceJar(bundleDir, manifest, true);
+       }
+
+       /** Download and integrates sources for a single Maven artifact. */
+       void downloadAndProcessM2Sources(Properties props, M2Artifact artifact, Path targetBundleDir, boolean merging,
+                       boolean unmodified) throws IOException {
                try {
+                       String repoStr = props.containsKey(ARGEO_ORIGIN_M2_REPO.toString())
+                                       ? props.getProperty(ARGEO_ORIGIN_M2_REPO.toString())
+                                       : null;
+                       String alternateUri = props.getProperty(ARGEO_ORIGIN_SOURCES_URI.toString());
                        M2Artifact sourcesArtifact = new M2Artifact(artifact.toM2Coordinates(), "sources");
-                       URL sourcesUrl = M2ConventionsUtils.mavenRepoUrl(repoStr, sourcesArtifact);
-                       Path sourcesDownloaded = downloadMaven(sourcesUrl, artifact, true);
-                       processM2SourceJar(sourcesDownloaded, targetBundleDir, merging ? artifact : null);
+                       URL sourcesUrl = alternateUri != null ? new URL(alternateUri)
+                                       : M2ConventionsUtils.mavenRepoUrl(repoStr, sourcesArtifact);
+                       Path sourcesDownloaded = downloadMaven(sourcesUrl, sourcesArtifact);
+                       processM2SourceJar(sourcesDownloaded, targetBundleDir, merging ? artifact : null, unmodified);
                        logger.log(TRACE, () -> "Processed source " + sourcesDownloaded);
                } catch (Exception e) {
                        logger.log(ERROR, () -> "Cannot download source for  " + artifact);
@@ -729,9 +741,9 @@ public class Repackage {
        }
 
        /** Integrate sources from a downloaded jar file. */
-       void processM2SourceJar(Path file, Path bundleDir, M2Artifact mergingFrom) throws IOException {
+       void processM2SourceJar(Path file, Path bundleDir, M2Artifact mergingFrom, boolean unmodified) throws IOException {
                A2Origin origin = new A2Origin();
-               Path sourceDir = sourceBundles ? bundleDir.getParent().resolve(bundleDir.toString() + ".src")
+               Path sourceDir = separateSources || unmodified ? bundleDir.getParent().resolve(bundleDir.toString() + ".src")
                                : bundleDir.resolve("OSGI-OPT/src");
                try (JarInputStream jarIn = new JarInputStream(Files.newInputStream(file), false)) {
 
@@ -775,7 +787,7 @@ public class Repackage {
                        }
                }
                // write the changes
-               if (sourceBundles) {
+               if (separateSources) {
                        origin.appendChanges(sourceDir);
                } else {
                        origin.added.add("source code under OSGI-OPT/src");
@@ -784,15 +796,18 @@ public class Repackage {
        }
 
        /** Download a Maven artifact. */
-       Path downloadMaven(URL url, M2Artifact artifact) throws IOException {
-               return downloadMaven(url, artifact, false);
+       Path downloadMaven(Properties props, M2Artifact artifact) throws IOException {
+               String repoStr = props.containsKey(ARGEO_ORIGIN_M2_REPO.toString())
+                               ? 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);
        }
 
        /** Download a Maven artifact. */
-       Path downloadMaven(URL url, M2Artifact artifact, boolean sources) throws IOException {
-               return download(url, mavenBase, artifact.getGroupId().replace(".", "/") //
-                               + '/' + artifact.getArtifactId() + '/' + artifact.getVersion() //
-                               + '/' + artifact.getArtifactId() + "-" + artifact.getVersion() + (sources ? "-sources" : "") + ".jar");
+       Path downloadMaven(URL url, M2Artifact artifact) throws IOException {
+               return download(url, mavenBase, M2ConventionsUtils.artifactPath("", artifact));
        }
 
        /*
@@ -911,7 +926,7 @@ public class Repackage {
                                NameVersion nameVersion = new NameVersion(relatedBundle[0], version);
                                bundleDir = targetBase.resolve(nameVersion.getName() + "." + nameVersion.getBranch());
 
-                               Path sourceDir = sourceBundles ? bundleDir.getParent().resolve(bundleDir.toString() + ".src")
+                               Path sourceDir = separateSources ? bundleDir.getParent().resolve(bundleDir.toString() + ".src")
                                                : bundleDir.resolve("OSGI-OPT/src");
 
                                Files.createDirectories(sourceDir);
@@ -928,7 +943,7 @@ public class Repackage {
                                }
 
                                // write the changes
-                               if (sourceBundles) {
+                               if (separateSources) {
                                        origin.appendChanges(sourceDir);
                                } else {
                                        origin.added.add("source code under OSGI-OPT/src");
@@ -945,7 +960,9 @@ public class Repackage {
         */
        /** Normalise a single (that is, non-merged) bundle. */
        Path processBundleJar(Path file, Path targetBase, Map<String, String> entries, A2Origin origin) throws IOException {
-               boolean embed = Boolean.parseBoolean(entries.getOrDefault(ARGEO_ORIGIN_EMBED.toString(), "false").toString());
+//             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());
                NameVersion nameVersion;
                Path bundleDir;
                // singleton
@@ -1011,12 +1028,13 @@ public class Repackage {
                                arch = libRelativePath.getName(1).toString();
                        }
 
-                       if (!embed) {
-                               // copy entries
-                               JarEntry entry;
-                               entries: while ((entry = jarIn.getNextJarEntry()) != null) {
-                                       if (entry.isDirectory())
-                                               continue entries;
+//                     if (!embed) {
+                       // copy entries
+                       JarEntry entry;
+                       entries: while ((entry = jarIn.getNextJarEntry()) != null) {
+                               if (entry.isDirectory())
+                                       continue entries;
+                               if (!doNotModify) {
                                        if (entry.getName().endsWith(".RSA") || entry.getName().endsWith(".DSA")
                                                        || entry.getName().endsWith(".SF")) {
                                                origin.deleted.add("cryptographic signatures");
@@ -1040,43 +1058,44 @@ public class Repackage {
                                                                .add("file system providers (META-INF/services/java.nio.file.spi.FileSystemProvider)");
                                                continue entries;
                                        }
-                                       if (entry.getName().startsWith("OSGI-OPT/src/")) { // skip embedded sources
-                                               origin.deleted.add("embedded sources");
-                                               continue entries;
-                                       }
-                                       Path target = bundleDir.resolve(entry.getName());
-                                       Files.createDirectories(target.getParent());
-                                       Files.copy(jarIn, target);
-
-                                       // native libraries
-                                       if (isNative && (entry.getName().endsWith(".so") || entry.getName().endsWith(".dll")
-                                                       || entry.getName().endsWith(".jnilib"))) {
-                                               Path categoryDir = bundleDir.getParent();
-                                               boolean copyDll = false;
-                                               Path targetDll = categoryDir.resolve(bundleDir.relativize(target));
-                                               if (nameVersion.getName().equals("com.sun.jna")) {
-                                                       if (arch.equals("x86_64"))
-                                                               arch = "x86-64";
-                                                       if (os.equals("macosx"))
-                                                               os = "darwin";
-                                                       if (target.getParent().getFileName().toString().equals(os + "-" + arch)) {
-                                                               copyDll = true;
-                                                       }
-                                                       targetDll = categoryDir.resolve(target.getFileName());
-                                               } else {
+                               }
+                               if (entry.getName().startsWith("OSGI-OPT/src/")) { // skip embedded sources
+                                       origin.deleted.add("embedded sources");
+                                       continue entries;
+                               }
+                               Path target = bundleDir.resolve(entry.getName());
+                               Files.createDirectories(target.getParent());
+                               Files.copy(jarIn, target);
+
+                               // native libraries
+                               if (isNative && (entry.getName().endsWith(".so") || entry.getName().endsWith(".dll")
+                                               || entry.getName().endsWith(".jnilib"))) {
+                                       Path categoryDir = bundleDir.getParent();
+                                       boolean copyDll = false;
+                                       Path targetDll = categoryDir.resolve(bundleDir.relativize(target));
+                                       if (nameVersion.getName().equals("com.sun.jna")) {
+                                               if (arch.equals("x86_64"))
+                                                       arch = "x86-64";
+                                               if (os.equals("macosx"))
+                                                       os = "darwin";
+                                               if (target.getParent().getFileName().toString().equals(os + "-" + arch)) {
                                                        copyDll = true;
                                                }
-                                               if (copyDll) {
-                                                       Files.createDirectories(targetDll.getParent());
-                                                       if (Files.exists(targetDll))
-                                                               Files.delete(targetDll);
-                                                       Files.copy(target, targetDll);
-                                               }
-                                               Files.delete(target);
-                                               origin.deleted.add(bundleDir.relativize(target).toString());
+                                               targetDll = categoryDir.resolve(target.getFileName());
+                                       } else {
+                                               copyDll = true;
                                        }
-                                       logger.log(TRACE, () -> "Copied " + target);
+                                       if (copyDll) {
+                                               Files.createDirectories(targetDll.getParent());
+                                               if (Files.exists(targetDll))
+                                                       Files.delete(targetDll);
+                                               Files.copy(target, targetDll);
+                                       }
+                                       Files.delete(target);
+                                       origin.deleted.add(bundleDir.relativize(target).toString());
                                }
+                               logger.log(TRACE, () -> "Copied " + target);
+//                             }
                        }
                }
 
@@ -1089,10 +1108,10 @@ 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());
-               }
+//             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
@@ -1341,21 +1360,23 @@ public class Repackage {
                }
                deleteDirectory(bundleDir);
 
-               if (sourceBundles)
-                       createSourceJar(bundleDir, manifest);
+               if (separateSources)
+                       createSourceJar(bundleDir, manifest, false);
 
                return jarPath;
        }
 
        /** Package sources separately, in the Eclipse-SourceBundle format. */
-       void createSourceJar(Path bundleDir, Manifest manifest) throws IOException {
+       void createSourceJar(Path bundleDir, Manifest manifest, boolean notModified) throws IOException {
                Path bundleCategoryDir = bundleDir.getParent();
                Path sourceDir = bundleCategoryDir.resolve(bundleDir.toString() + ".src");
                if (!Files.exists(sourceDir)) {
                        logger.log(WARNING, sourceDir + " does not exist, skipping...");
                        return;
                }
-               createReadMe(sourceDir, manifest);
+
+               if (!notModified)
+                       createReadMe(sourceDir, manifest);
 
                Path relPath = a2Base.relativize(bundleCategoryDir);
                Path srcCategoryDir = a2SrcBase.resolve(relPath);
@@ -1451,7 +1472,7 @@ public class Repackage {
 
                        writer.append("\nA detailed list of changes is available under " + CHANGES + ".\n");
                        if (!jarDir.getFileName().endsWith(".src")) {// binary archive
-                               if (sourceBundles)
+                               if (separateSources)
                                        writer.append("Corresponding sources are available in the related archive named "
                                                        + jarDir.toString() + ".src.jar.\n");
                                else
@@ -1569,7 +1590,8 @@ class M2ConventionsUtils {
 
        /** Absolute path to the directories where the files will be stored */
        static String artifactParentPath(String artifactBasePath, M2Artifact artifact) {
-               return artifactBasePath + (artifactBasePath.endsWith("/") ? "" : "/") + artifactParentPath(artifact);
+               return artifactBasePath + (artifactBasePath.endsWith("/") || artifactBasePath.equals("") ? "" : "/")
+                               + artifactParentPath(artifact);
        }
 
        /** Relative path to the directories where the files will be stored */