Skip to content

Commit

Permalink
Add multi-release processing support
Browse files Browse the repository at this point in the history
Currently bnd is not a capable of processing multi-release jars, this
adds support of multi-release processing by a new directive -release
where one can select what release version should be processed by bnd.

Fixes #5346

Signed-off-by: Christoph Läubrich <laeubi@laeubi-soft.de>
  • Loading branch information
laeubi committed Aug 24, 2022
1 parent b545b94 commit e9e1618
Show file tree
Hide file tree
Showing 4 changed files with 246 additions and 42 deletions.
76 changes: 51 additions & 25 deletions biz.aQute.bndlib/src/aQute/bnd/osgi/Analyzer.java
Expand Up @@ -200,12 +200,13 @@ public static Properties getManifest(File dirOrJar) throws Exception {
*/
public void analyze() throws Exception {
if (!analyzed) {
int release = getRelease();
analyzed = true;
analyzeContent();
analyzeContent(release);

// Execute any plugins
// TODO handle better reanalyze
doPlugins();
doPlugins(release);

//
// calculate class versions in use
Expand Down Expand Up @@ -293,7 +294,7 @@ public void analyze() throws Exception {
getHostPackages().ifPresent(hostPackages -> referredAndExported.keySet()
.removeAll(hostPackages));

getRequireBundlePackages().ifPresent(hostPackages -> referredAndExported.keySet()
getRequireBundlePackages(release).ifPresent(hostPackages -> referredAndExported.keySet()
.removeAll(hostPackages));

String h = getProperty(IMPORT_PACKAGE);
Expand Down Expand Up @@ -423,6 +424,19 @@ public void analyze() throws Exception {
}
}

private int getRelease() {
String property = getProperty(JAVA_RELEASE);
if (property != null) {
try {
return Integer.parseInt(property.trim());
} catch (NumberFormatException e) {
throw new IllegalArgumentException(
JAVA_RELEASE + " must be a valid integer but was " + property + " (" + e.getMessage() + ")", e);
}
}
return -1;
}

private void reset() {
contained.clear();
classspace.clear();
Expand All @@ -436,10 +450,10 @@ private void reset() {
bcpTypes.clear();
}

private void analyzeContent() throws Exception {
private void analyzeContent(int release) throws Exception {
// Parse all the classes in the
// the jar according to the OSGi Bundle-ClassPath
analyzeBundleClasspath();
analyzeBundleClasspath(release);

//
// Get exported packages from the
Expand All @@ -448,9 +462,10 @@ private void analyzeContent() throws Exception {
//

for (Jar current : getClasspath()) {
getManifestInfoFromClasspath(current, classpathExports, contracts);
getManifestInfoFromClasspath(current, classpathExports, contracts, release);

Manifest m = current.getManifest();
Manifest m = current.getManifest(release)
.orElse(null);
if (m == null) {
for (String dir : current.getDirectories()
.keySet()) {
Expand All @@ -472,7 +487,7 @@ private void analyzeContent() throws Exception {

// Conditional packages

doConditionalPackages();
doConditionalPackages(release);
}

/**
Expand Down Expand Up @@ -504,7 +519,12 @@ public Optional<Set<PackageRef>> getHostPackages() {
* @return the packages from the required bundles, with no Require-Bundle
* return an empty Optional
*/

public Optional<Set<PackageRef>> getRequireBundlePackages() {
return getRequireBundlePackages(Jar.MULTI_RELEASE_DEFAULT_VERSION);
}

public Optional<Set<PackageRef>> getRequireBundlePackages(int release) {

Parameters required = getRequireBundle();
if (required.isEmpty())
Expand All @@ -514,7 +534,8 @@ public Optional<Set<PackageRef>> getRequireBundlePackages() {
.stream()
.map(this::toJar)
.filter(Objects::nonNull)
.map(asFunctionOrElse(Jar::getManifest, null))
.map(asFunctionOrElse(jar -> jar.getManifest(release)
.orElse(null), null))
.filter(Objects::nonNull)
.flatMap(manifest -> {
Domain domain = Domain.domain(manifest);
Expand Down Expand Up @@ -625,7 +646,7 @@ public Clazz getPackageInfo(PackageRef packageRef) {
}
}

private void doConditionalPackages() throws Exception {
private void doConditionalPackages(int release) throws Exception {
//
// We need to find out the contained packages
// again ... so we need to clear any visited
Expand All @@ -636,7 +657,7 @@ private void doConditionalPackages() throws Exception {

for (Jar extra; (extra = getExtra()) != null;) {
dot.addAll(extra);
analyzeJar(extra, "", true, null);
analyzeJar(extra, "", true, null, release);
}
}

Expand Down Expand Up @@ -980,8 +1001,10 @@ protected Jar getExtra() throws Exception {

/**
* Call AnalyzerPlugins to analyze the content.
*
* @param release the release flag for that content should be analyzed
*/
private void doPlugins() {
private void doPlugins(int release) {
List<AnalyzerPlugin> plugins = getPlugins(AnalyzerPlugin.class);
plugins.sort(Comparator.comparingInt(AnalyzerPlugin::ordering));
for (AnalyzerPlugin plugin : plugins) {
Expand All @@ -1000,7 +1023,7 @@ private void doPlugins() {
.filterValue(Objects::nonNull)
.collect(MapStream.toMap());
reset();
analyzeContent();
analyzeContent(release);
// Restore -internal-source information
// if the package still exists
sourceInformation.forEach((pkgRef, source) -> {
Expand Down Expand Up @@ -1913,10 +1936,11 @@ public boolean referred(PackageRef packageName) {
return result;
}

private void getManifestInfoFromClasspath(Jar jar, Packages classpathExports, Contracts contracts) {
private void getManifestInfoFromClasspath(Jar jar, Packages classpathExports, Contracts contracts, int release) {
logger.debug("get Manifest Info From Classpath for {}", jar);
try {
Manifest m = jar.getManifest();
Manifest m = jar.getManifest(release)
.orElse(null);
if (m != null) {
Domain domain = Domain.domain(m);
Parameters exported = domain.getExportPackage();
Expand Down Expand Up @@ -2577,11 +2601,11 @@ public Jar getTarget() {
return getJar();
}

private void analyzeBundleClasspath() throws Exception {
private void analyzeBundleClasspath(int release) throws Exception {
Parameters bcp = getBundleClasspath();

if (bcp.isEmpty()) {
analyzeJar(dot, "", true, null);
analyzeJar(dot, "", true, null, release);
} else {
// Cleanup entries
bcp = bcp.stream()
Expand All @@ -2593,7 +2617,7 @@ private void analyzeBundleClasspath() throws Exception {

for (String path : bcp.keySet()) {
if (path.equals(".")) {
analyzeJar(dot, "", okToIncludeDirs, null);
analyzeJar(dot, "", okToIncludeDirs, null, release);
continue;
}
//
Expand All @@ -2610,7 +2634,7 @@ private void analyzeBundleClasspath() throws Exception {
if (!(resource instanceof JarResource)) {
addClose(jar);
}
analyzeJar(jar, "", true, path);
analyzeJar(jar, "", true, path, release);
} catch (Exception e) {
warning("Invalid bundle classpath entry: %s: %s", path, e);
}
Expand All @@ -2623,7 +2647,7 @@ private void analyzeBundleClasspath() throws Exception {
warning(Constants.BUNDLE_CLASSPATH
+ " uses a directory '%s' as well as '.'. This means bnd does not know if a directory is a package.",
path);
analyzeJar(dot, path.concat("/"), true, path);
analyzeJar(dot, path.concat("/"), true, path, release);
} else {
Attrs info = bcp.get(path);
if (!"optional".equals(info.get(RESOLUTION_DIRECTIVE)))
Expand All @@ -2639,16 +2663,19 @@ private void analyzeBundleClasspath() throws Exception {
* contained and referred set and uses. This method ignores the Bundle
* classpath.
*/
private boolean analyzeJar(Jar jar, String prefix, boolean okToIncludeDirs, String bcpEntry) throws Exception {
private boolean analyzeJar(Jar jar, String prefix, boolean okToIncludeDirs, String bcpEntry, int release)
throws Exception {
Map<String, Clazz> mismatched = new HashMap<>();

Parameters importPackage = Optional.ofNullable(jar.getManifest())
Parameters importPackage = jar.getManifest(release)
.map(Domain::domain)
.map(Domain::getImportPackage)
.orElseGet(() -> new Parameters());

next: for (String path : jar.getResources()
.keySet()) {
Map<String, Resource> resources = jar.getVersionedResources(release);
next: for (Entry<String, Resource> entry : resources.entrySet()) {
String path = entry.getKey();
Resource resource = entry.getValue();
if (path.startsWith(prefix)) {

String relativePath = path.substring(prefix.length());
Expand All @@ -2665,7 +2692,6 @@ private boolean analyzeJar(Jar jar, String prefix, boolean okToIncludeDirs, Stri

// Check class resources, we need to analyze them
if (path.endsWith(".class")) {
Resource resource = jar.getResource(path);
Clazz clazz;

try {
Expand Down
3 changes: 2 additions & 1 deletion biz.aQute.bndlib/src/aQute/bnd/osgi/Constants.java
Expand Up @@ -293,6 +293,7 @@ public interface Constants {
String WORKINGSET = "-workingset";
String WORKINGSET_MEMBER = "member";
String REQUIRE_BND = "-require-bnd";
String JAVA_RELEASE = "-release";

// Deprecated
String CLASSPATH = "-classpath";
Expand All @@ -314,7 +315,7 @@ public interface Constants {
CONNECTION_SETTINGS, RUNPROVIDEDCAPABILITIES, WORKINGSET, RUNSTORAGE, REPRODUCIBLE, INCLUDEPACKAGE,
CDIANNOTATIONS, REMOTEWORKSPACE, MAVEN_DEPENDENCIES, BUILDERIGNORE, STALECHECK, MAVEN_SCOPE, RUNSTARTLEVEL,
RUNOPTIONS, NOCLASSFORNAME, EXPORT_APIGUARDIAN, RESOLVE, DEFINE_CONTRACT, GENERATE, RUNFRAMEWORKRESTART,
NOIMPORTJAVA, VERSIONDEFAULTS, LIBRARY);
NOIMPORTJAVA, VERSIONDEFAULTS, LIBRARY, JAVA_RELEASE);

// Ignore bundle specific headers. These headers do not make a lot of sense
// to inherit
Expand Down

0 comments on commit e9e1618

Please sign in to comment.