Skip to content

Commit

Permalink
analyzer: bnd does not support Setting Package Version information `N…
Browse files Browse the repository at this point in the history
…ame` sections in the manifest

fixes #4148

Signed-off-by: Raymond Augé <raymond.auge@liferay.com>

Signed-off-by: BJ Hargrave <bj@bjhargrave.com>
  • Loading branch information
rotty3000 authored and bjhargrave committed Jun 9, 2020
1 parent 3bec7fd commit d2b6a20
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 66 deletions.
44 changes: 44 additions & 0 deletions biz.aQute.bndlib.tests/test/test/BuilderTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -1124,6 +1124,50 @@ public void testNamesection() throws Exception {

}

@Test
public void testPackageNamesection() throws Exception {
Builder b = new Builder();
try {
b.addClasspath(IO.getFile("jar/osgi.jar"));
b.setProperty(Constants.NAMESECTION,
"org/osgi/service/event/;Foo=bar");
b.setProperty(Constants.PRIVATEPACKAGE, "org.osgi.service.event");
Jar build = b.build();
assertOk(b);
assertTrue(b.check());
Manifest m = build.getManifest();
m.write(System.err);

assertNotNull(m.getAttributes(
"org/osgi/service/event/")
.getValue("Foo"));
} finally {
b.close();
}

}

@Test
public void testGlobPackageNamesection() throws Exception {
Builder b = new Builder();
try {
b.addClasspath(IO.getFile("jar/osgi.jar"));
b.setProperty(Constants.NAMESECTION, "org/osgi/service/*/;Foo=bar");
b.setProperty(Constants.PRIVATEPACKAGE, "org.osgi.service.event");
Jar build = b.build();
assertOk(b);
assertTrue(b.check());
Manifest m = build.getManifest();
m.write(System.err);

assertNotNull(m.getAttributes("org/osgi/service/event/")
.getValue("Foo"));
} finally {
b.close();
}

}

/**
* Test the digests
*/
Expand Down
5 changes: 3 additions & 2 deletions biz.aQute.bndlib/src/aQute/bnd/help/Syntax.java
Original file line number Diff line number Diff line change
Expand Up @@ -557,8 +557,9 @@ null, null, new Syntax("name", "The display name of the developer", "name='Peter
new Syntax(NOEE, "Do not calculate the osgi.ee name space Execution Environment from the class file version.",
NOEE + "=true", "true,false", Verifier.TRUEORFALSEPATTERN),
new Syntax(NAMESECTION,
"Create a name section (second part of manifest) with optional property expansion and addition of custom attributes.",
NAMESECTION + "=*;baz=true, abc/def/bar/X.class=3", null, null),
"Create a name section (second part of manifest) with optional property expansion and addition of custom attributes. Patterns not ending with \"/\" target resources. Those ending with \"/\" target packages.",
NAMESECTION + "=*;baz=true, abc/def/bar/X.class;bar=3", null,
null),
new Syntax(OUTPUT, "Specify the output directory or file.", OUTPUT + "=my_directory", null, null),
new Syntax(OUTPUTMASK,
"If set, is used a template to calculate the output file. It can use any macro but the ${@bsn} and ${@version} macros refer to the current JAR being saved. The default is bsn + \".jar\".",
Expand Down
16 changes: 14 additions & 2 deletions biz.aQute.bndlib/src/aQute/bnd/osgi/Analyzer.java
Original file line number Diff line number Diff line change
Expand Up @@ -1295,6 +1295,14 @@ private void doNamesection(Jar dot, Manifest manifest) {
Instructions instructions = new Instructions(namesection);
Set<String> resources = new HashSet<>(dot.getResources()
.keySet());
// Support package attributes. See
// https://docs.oracle.com/javase/8/docs/technotes/guides/versioning/spec/versioning2.html#wp89936
MapStream.of(dot.getDirectories())
.filterValue(mdir -> Objects.nonNull(mdir) && !mdir.isEmpty())
.keys()
.map(d -> d.concat(
"/"))
.forEach(resources::add);

//
// For each instruction, iterator over the resources and filter
Expand All @@ -1312,8 +1320,12 @@ private void doNamesection(Jar dot, Manifest manifest) {
String path = i.next();
// For each resource

if (instr.getKey()
.matches(path)) {
Instruction instruction = instr.getKey();

if (path.endsWith("/") && !instruction.toString()
.endsWith("/")) {
// continue
} else if (instruction.matches(path)) {

// Instruction matches the resource

Expand Down
106 changes: 44 additions & 62 deletions docs/_instructions/namesection.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,81 +2,63 @@
layout: default
class: Builder
title: -namesection RESOURCE-SPEC ( ',' RESOURCE-SPEC ) *
summary: Create a name section (second part of manifest) with optional property expansion and addition of custom attributes.
summary: Create a name section (second part of manifest) with optional property expansion and addition of custom attributes. Patterns not ending with \"/\" target resources. Those ending with \"/\" target packages.
---

/**
* Parse the namesection as instructions and then match them against the
* current set of resources For example:
*
* <pre>
* -namesection: *;baz=true, abc/def/bar/X.class=3
* </pre>
*
* The raw value of {@link Constants#NAMESECTION} is used but the values of
* the attributes are replaced where @ is set to the resource name. This
* allows macro to operate on the resource
*/
Create a name section (second part of manifest) with optional property expansion and addition of custom attributes.

private void doNamesection(Jar dot, Manifest manifest) {
### Matching
The key of the `-namesection` instruction is an _ant style_ glob. And there are two target groups for matching:

Parameters namesection = parseHeader(getProperties().getProperty(NAMESECTION));
Instructions instructions = new Instructions(namesection);
Set<String> resources = new HashSet<String>(dot.getResources().keySet());
* **resources** - the pattern not ending with `/` or is an exact match for a resource path
* **packages** - the pattern ends with `/` or is an exact match for a package path

//
// For each instruction, iterator over the resources and filter
// them. If a resource matches, it must be removed even if the
// instruction is negative. If positive, add a name section
// to the manifest for the given resource name. Then add all
// attributes from the instruction to that name section.
//
for (Map.Entry<Instruction,Attrs> instr : instructions.entrySet()) {
boolean matched = false;
#### Custom attributes

// For each instruction
The goal of named sections is to provide attributes over a specific subset of resources and paths in the jar file. Attributes are specified using the same syntax used elsewhere (such as package attributes). Attributes can contain properties and macros for expansion and replacement.

for (Iterator<String> i = resources.iterator(); i.hasNext();) {
String path = i.next();
// For each resource
Each attribute is processed by bnd and the matching value is passed using the `@` property.

if (instr.getKey().matches(path)) {
#### Resources
Resources are targeted by using a glob pattern not ending with `/`.

// Instruction matches the resource
For example, the following instruction sets the content type attribute for `png` files:

matched = true;
if (!instr.getKey().isNegated()) {
```properties
-namesection: com/foo/*.png; Content-Type=image/png
```

// Positive match, add the attributes
This produces a result like the following:
```properties
Name: org/foo/icon_12x12.png
Content-Type: image/png

Attributes attrs = manifest.getAttributes(path);
if (attrs == null) {
attrs = new Attributes();
manifest.getEntries().put(path, attrs);
}
Name: org/foo/icon_48x48.png
Content-Type: image/png
```

//
// Add all the properties from the instruction to the
// name section
//
#### Packages
Packages are targeted by using a glob pattern that ends with `/`.

for (Map.Entry<String,String> property : instr.getValue().entrySet()) {
setProperty("@", path);
try {
String processed = getReplacer().process(property.getValue());
attrs.putValue(property.getKey(), processed);
}
finally {
unsetProperty("@");
}
}
}
i.remove();
}
}
For example, to produce a Java [Package Version Information](https://docs.oracle.com/javase/tutorial/deployment/jar/packageman.html) section use an instruction like this one:
```properties
-namesection: jakarta/annotation/*/;\
Specification-Title=Jakarta Annotations;\
Specification-Version=${annotation.spec.version};\
Specification-Vendor=Eclipse Foundation;\
Implementation-Title=jakarta.annotation;\
Implementation-Version=${annotation.spec.version}.${annotation.revision};\
Implementation-Vendor=Apache Software Foundation
```

if (!matched && resources.size() > 0)
warning("The instruction %s in %s did not match any resources", instr.getKey(), NAMESECTION);
}
This produces a result like the following:
```properties
Name: jakarta/annotation/
Implementation-Title: jakarta.annotation
Implementation-Vendor: Apache Software Foundation
Implementation-Version: 2.0.0-M1
Specification-Title: Jakarta Annotations
Specification-Vendor: Eclipse Foundation
Specification-Version: 2.0
```

}

0 comments on commit d2b6a20

Please sign in to comment.