Skip to content

Commit 6aff28a

Browse files
committed
Add a tycho-wrap mojo
in maven jars/artifacts can be build in numerous ways, not all include the maven-jar-plugin (e.g. maven-assembly-plugin) and not all are easily combined with maven-bundle or bnd-maven plugin. This adds a new tycho-wrap-plugin that closes this gap by allowing to specify an arbitrary input and output, some bnd instructions and an option to attach the result to the project. This has also the advantage that projects are able to publish two "flavors" of their artifact a plain one and an OSGi-fied one that could help to convince projects to provide such things as it has zero influence to their build and ways how they build artifacts.
1 parent 85e1103 commit 6aff28a

File tree

5 files changed

+258
-9
lines changed

5 files changed

+258
-9
lines changed

RELEASE_NOTES.md

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,15 @@ Specifying no version is equivalent to `0.0.0` which resolves to the latest vers
1313
All of the following variants to specify a version are now possible:
1414
```
1515
<target name="my-target">
16-
<locations>
17-
<location includeAllPlatforms="false" includeConfigurePhase="true" includeMode="planner" includeSource="true" type="InstallableUnit">
18-
<repository location="https://download.eclipse.org/releases/2024-09/"/>
19-
<unit id="org.eclipse.pde.feature.group" version="3.16.0.v20240903-0240"/>
20-
<unit id="jakarta.annotation-api" version="0.0.0"/>
21-
<unit id="org.eclipse.sdk"/>
22-
<unit id="jakarta.inject.jakarta.inject-api" version="[1.0,2)"/>
23-
</location>
24-
</locations>
16+
<locations>
17+
<location includeAllPlatforms="false" includeConfigurePhase="true" includeMode="planner" includeSource="true" type="InstallableUnit">
18+
<repository location="https://download.eclipse.org/releases/2024-09/"/>
19+
<unit id="org.eclipse.pde.feature.group" version="3.16.0.v20240903-0240"/>
20+
<unit id="jakarta.annotation-api" version="0.0.0"/>
21+
<unit id="org.eclipse.sdk"/>
22+
<unit id="jakarta.inject.jakarta.inject-api" version="[1.0,2)"/>
23+
</location>
24+
</locations>
2525
</target>
2626
```
2727

@@ -34,6 +34,35 @@ that manually can be a daunting task.
3434
There is now a new `tycho-version-bump:update-manifest` mojo that helps in calculate the
3535
lower bound and update the manifest accordingly.
3636

37+
## new `wrap` mojo
38+
39+
With maven, jars (or more general artifacts) can be build in numerous ways, not all include
40+
the maven-jar-plugin (e.g. maven-assembly-plugin) and not all are easily
41+
combined with maven-bundle or bnd-maven plugin.
42+
43+
Tycho now provides a new `tycho-wrap:wrap mojo` that closes this gap by allowing to
44+
specify an arbitrary input and output, some bnd instructions and (optionally) attach the result to the maven project.
45+
46+
This has the advantage that projects are able to publish two "flavors" of their artifact a plain one and an OSGi-fied one that could
47+
help to convince projects to provide such things as it has zero influence to their build and ways how they build artifacts.
48+
49+
In the simplest form it can be used like this:
50+
51+
```xml
52+
<plugin>
53+
<groupId>org.eclipse.tycho</groupId>
54+
<artifactId>tycho-wrap-plugin</artifactId>
55+
<version>5.0.0-SNAPSHOT</version>
56+
<executions>
57+
<execution>
58+
<id>make-bundle</id>
59+
<goals>
60+
<goal>wrap</goal>
61+
</goals>
62+
</execution>
63+
</executions>
64+
</plugin>
65+
```
3766

3867
## support bumping maven target locations
3968

pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -594,6 +594,7 @@
594594
<module>tycho-bnd-plugin</module>
595595
<module>tycho-repository-plugin</module>
596596
<module>tycho-eclipse-plugin</module>
597+
<module>tycho-wrap-plugin</module>
597598
</modules>
598599
<profiles>
599600
<profile>
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
eclipse.preferences.version=1
2+
org.eclipse.jdt.core.compiler.codegen.targetPlatform=17
3+
org.eclipse.jdt.core.compiler.compliance=17
4+
org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
5+
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
6+
org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=ignore
7+
org.eclipse.jdt.core.compiler.release=enabled
8+
org.eclipse.jdt.core.compiler.source=17

tycho-wrap-plugin/pom.xml

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<project xmlns="http://maven.apache.org/POM/4.0.0"
2+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
4+
<modelVersion>4.0.0</modelVersion>
5+
<parent>
6+
<groupId>org.eclipse.tycho</groupId>
7+
<artifactId>tycho</artifactId>
8+
<version>5.0.0-SNAPSHOT</version>
9+
</parent>
10+
<artifactId>tycho-wrap-plugin</artifactId>
11+
<name>Tycho Wrap Plugin</name>
12+
<description>Support wrapping of plain jars into OSGi bundles</description>
13+
<packaging>maven-plugin</packaging>
14+
<prerequisites>
15+
<maven>${minimal-maven-version}</maven>
16+
</prerequisites>
17+
<dependencies>
18+
<dependency>
19+
<groupId>org.apache.maven</groupId>
20+
<artifactId>maven-core</artifactId>
21+
</dependency>
22+
<dependency>
23+
<groupId>org.apache.maven</groupId>
24+
<artifactId>maven-plugin-api</artifactId>
25+
</dependency>
26+
<dependency>
27+
<groupId>org.apache.maven.plugin-tools</groupId>
28+
<artifactId>maven-plugin-annotations</artifactId>
29+
</dependency>
30+
<dependency>
31+
<groupId>biz.aQute.bnd</groupId>
32+
<artifactId>biz.aQute.bndlib</artifactId>
33+
</dependency>
34+
<dependency>
35+
<groupId>biz.aQute.bnd</groupId>
36+
<artifactId>bnd-maven-plugin</artifactId>
37+
<version>7.0.0</version>
38+
</dependency>
39+
</dependencies>
40+
<build>
41+
<plugins>
42+
<plugin>
43+
<groupId>org.apache.maven.plugins</groupId>
44+
<artifactId>maven-plugin-plugin</artifactId>
45+
<!-- workaround for
46+
https://issues.apache.org/jira/browse/MPLUGIN-504 -->
47+
<configuration>
48+
<goalPrefix>tycho-wrap</goalPrefix>
49+
</configuration>
50+
</plugin>
51+
</plugins>
52+
</build>
53+
</project>
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2024 Christoph Läubrich and others.
3+
* This program and the accompanying materials
4+
* are made available under the terms of the Eclipse Public License 2.0
5+
* which accompanies this distribution, and is available at
6+
* https://www.eclipse.org/legal/epl-2.0/
7+
*
8+
* SPDX-License-Identifier: EPL-2.0
9+
*
10+
* Contributors:
11+
* Christoph Läubrich - initial API and implementation
12+
******************************************************************************/
13+
package org.eclipse.tycho.wrap;
14+
15+
import java.io.File;
16+
import java.util.Set;
17+
import java.util.jar.Attributes;
18+
import java.util.jar.JarFile;
19+
import java.util.jar.Manifest;
20+
import java.util.regex.Pattern;
21+
22+
import org.apache.maven.artifact.Artifact;
23+
import org.apache.maven.plugin.AbstractMojo;
24+
import org.apache.maven.plugin.MojoExecution;
25+
import org.apache.maven.plugin.MojoExecutionException;
26+
import org.apache.maven.plugin.MojoFailureException;
27+
import org.apache.maven.plugins.annotations.Component;
28+
import org.apache.maven.plugins.annotations.LifecyclePhase;
29+
import org.apache.maven.plugins.annotations.Mojo;
30+
import org.apache.maven.plugins.annotations.Parameter;
31+
import org.apache.maven.project.MavenProject;
32+
import org.apache.maven.project.MavenProjectHelper;
33+
import org.apache.maven.settings.Settings;
34+
import org.osgi.framework.Constants;
35+
36+
import aQute.bnd.build.Project;
37+
import aQute.bnd.maven.lib.configuration.BndConfiguration;
38+
import aQute.bnd.osgi.Analyzer;
39+
import aQute.bnd.osgi.Jar;
40+
import aQute.bnd.print.JarPrinter;
41+
import aQute.bnd.version.MavenVersion;
42+
import aQute.bnd.version.Version;
43+
44+
@Mojo(name = "wrap", requiresProject = true, threadSafe = true, defaultPhase = LifecyclePhase.PACKAGE)
45+
public class WrapMojo extends AbstractMojo {
46+
47+
private static final String[] HEADERS = { Constants.BUNDLE_SYMBOLICNAME, Constants.BUNDLE_VERSION };
48+
49+
@Component
50+
private MavenProject project;
51+
52+
@Parameter(defaultValue = "${settings}", readonly = true)
53+
Settings settings;
54+
55+
@Component
56+
private MojoExecution mojoExecution;
57+
58+
@Component
59+
private MavenProjectHelper helper;
60+
61+
/**
62+
* File path to a bnd file containing bnd instructions for this project.
63+
* Defaults to {@code bnd.bnd}. The file path can be an absolute or relative to
64+
* the project directory.
65+
* <p>
66+
* The bnd instructions for this project are merged with the bnd instructions,
67+
* if any, for the parent project.
68+
*/
69+
// This is not used and is for doc only; see
70+
// BndConfiguration#loadProperties and
71+
// AbstractBndMavenPlugin for reference
72+
@Parameter(defaultValue = Project.BNDFILE)
73+
String bndfile;
74+
75+
/**
76+
* Bnd instructions for this project specified directly in the pom file. This is
77+
* generally be done using a {@code <![CDATA[]]>} section. If the project has a
78+
* {@link #bndfile}, then this configuration element is ignored.
79+
* <p>
80+
* The bnd instructions for this project are merged with the bnd instructions,
81+
* if any, for the parent project.
82+
*/
83+
// This is not used and is for doc only; see
84+
// BndConfiguration#loadProperties and
85+
// AbstractBndMavenPlugin for reference
86+
@Parameter
87+
String bnd;
88+
89+
@Parameter(required = true, property = "input", defaultValue = "${project.build.directory}/${project.build.finalName}.${project.packaging}")
90+
private File input;
91+
92+
@Parameter(required = true, property = "output", defaultValue = "${project.build.directory}/${project.build.finalName}-bundle.${project.packaging}")
93+
private File output;
94+
95+
/**
96+
* If enabled attach the generated file as an artifact to the project
97+
*/
98+
@Parameter(required = false, defaultValue = "true", property = "attach")
99+
private boolean attach;
100+
101+
/**
102+
* The classifier to use when attach this to the project
103+
*/
104+
@Parameter(defaultValue = "bundle", property = "classifier")
105+
private String classifier;
106+
107+
@Override
108+
public void execute() throws MojoExecutionException, MojoFailureException {
109+
BndConfiguration configuration = new BndConfiguration(project, mojoExecution);
110+
111+
try (Jar jar = new Jar(output.getName(), input, Pattern.compile(JarFile.MANIFEST_NAME));
112+
Analyzer analyzer = new Analyzer(jar)) {
113+
configuration.loadProperties(analyzer);
114+
if (analyzer.getProperty(Constants.BUNDLE_VERSION) == null) {
115+
Version version = new MavenVersion(project.getVersion()).getOSGiVersion();
116+
analyzer.setProperty(Constants.BUNDLE_VERSION, version.toString());
117+
}
118+
if (analyzer.getProperty(Constants.BUNDLE_SYMBOLICNAME) == null) {
119+
analyzer.setProperty(Constants.BUNDLE_SYMBOLICNAME, project.getArtifactId());
120+
}
121+
if (analyzer.getProperty(Constants.BUNDLE_NAME) == null) {
122+
analyzer.setProperty(Constants.BUNDLE_NAME, project.getName());
123+
}
124+
Set<Artifact> artifacts = project.getArtifacts();
125+
for (Artifact artifact : artifacts) {
126+
File cpe = artifact.getFile();
127+
try {
128+
analyzer.addClasspath(cpe);
129+
} catch (Exception e) {
130+
// just go on... it might be not a jar or something else not usable
131+
}
132+
}
133+
Manifest manifest = analyzer.calcManifest();
134+
jar.setManifest(manifest);
135+
jar.write(output);
136+
analyzer.getWarnings().forEach(getLog()::warn);
137+
analyzer.getErrors().forEach(getLog()::error);
138+
Attributes mainAttributes = manifest.getMainAttributes();
139+
for (String header : HEADERS) {
140+
getLog().info(header + ": " + mainAttributes.getValue(header));
141+
}
142+
try (JarPrinter jarPrinter = new JarPrinter()) {
143+
jarPrinter.doPrint(jar, JarPrinter.IMPEXP, false, false);
144+
getLog().info(jarPrinter.toString());
145+
}
146+
} catch (MojoFailureException e) {
147+
throw e;
148+
} catch (MojoExecutionException e) {
149+
throw e;
150+
} catch (Exception e) {
151+
throw new MojoFailureException("wrapping input " + input + " failed: " + e, e);
152+
}
153+
if (attach) {
154+
helper.attachArtifact(project, output, classifier);
155+
}
156+
}
157+
158+
}

0 commit comments

Comments
 (0)