|
7 | 7 | import java.util.Objects;
|
8 | 8 | import java.util.Optional;
|
9 | 9 |
|
| 10 | +import org.apache.maven.model.Developer; |
| 11 | +import org.apache.maven.model.License; |
10 | 12 | import org.apache.maven.model.Plugin;
|
11 | 13 | import org.apache.maven.model.PluginExecution;
|
12 | 14 | import org.apache.maven.model.PluginManagement;
|
13 | 15 | import org.apache.maven.plugin.MojoExecution;
|
14 | 16 | import org.apache.maven.project.MavenProject;
|
| 17 | +import org.codehaus.plexus.util.StringUtils; |
15 | 18 | import org.codehaus.plexus.util.xml.Xpp3Dom;
|
16 | 19 | import org.slf4j.Logger;
|
17 | 20 | import org.slf4j.LoggerFactory;
|
18 | 21 |
|
19 | 22 | import aQute.bnd.build.Project;
|
| 23 | +import aQute.bnd.header.OSGiHeader; |
| 24 | +import aQute.bnd.osgi.Constants; |
20 | 25 | import aQute.bnd.osgi.Processor;
|
| 26 | +import aQute.bnd.version.MavenVersion; |
| 27 | +import aQute.bnd.version.Version; |
21 | 28 | import aQute.lib.io.IO;
|
| 29 | +import aQute.lib.strings.Strings; |
22 | 30 | import aQute.lib.utf8properties.UTF8Properties;
|
23 | 31 |
|
24 | 32 | /**
|
25 | 33 | * A helper to read Bnd configuration for maven plugins consistently over the
|
26 | 34 | * various Mojos.
|
27 | 35 | */
|
28 | 36 | public class BndConfiguration {
|
| 37 | + static final String TSTAMP = "${tstamp}"; |
| 38 | + static final String SNAPSHOT = "SNAPSHOT"; |
29 | 39 | private final static Logger logger = LoggerFactory.getLogger(BndConfiguration.class);
|
30 | 40 |
|
31 | 41 | private final MavenProject project;
|
32 | 42 | private final MojoExecution mojoExecution;
|
| 43 | + private Xpp3Dom configuration; |
33 | 44 |
|
34 | 45 | public BndConfiguration(MavenProject project, MojoExecution mojoExecution) {
|
35 | 46 | this.project = requireNonNull(project);
|
36 | 47 | this.mojoExecution = requireNonNull(mojoExecution);
|
| 48 | + configuration = Optional.ofNullable(project.getBuildPlugins()) |
| 49 | + .flatMap(this::getConfiguration) |
| 50 | + .orElseGet(this::defaultConfiguration); |
37 | 51 | }
|
38 | 52 |
|
39 | 53 | public File loadProperties(Processor processor) throws Exception {
|
40 | 54 | // Load parent project properties first
|
41 | 55 | loadParentProjectProperties(processor, project);
|
42 | 56 |
|
43 |
| - // Load current project properties |
44 |
| - Xpp3Dom configuration = Optional.ofNullable(project.getBuildPlugins()) |
45 |
| - .flatMap(this::getConfiguration) |
46 |
| - .orElseGet(this::defaultConfiguration); |
47 | 57 | return loadProjectProperties(processor, project, project, configuration);
|
48 | 58 | }
|
49 | 59 |
|
| 60 | + public void inheritPropertiesDefaults(Processor processor) throws Exception { |
| 61 | + String outputTimestamp = configuration.getAttribute("outputTimestamp"); |
| 62 | + // https://maven.apache.org/guides/mini/guide-reproducible-builds.html |
| 63 | + boolean isReproducible = Strings.nonNullOrEmpty(outputTimestamp) |
| 64 | + // no timestamp configured (1 character configuration is useful |
| 65 | + // to override a full value during pom inheritance) |
| 66 | + && ((outputTimestamp.length() > 1) || Character.isDigit(outputTimestamp.charAt(0))); |
| 67 | + if (isReproducible) { |
| 68 | + processor.setProperty(Constants.REPRODUCIBLE, outputTimestamp); |
| 69 | + if (processor.getProperty(Constants.NOEXTRAHEADERS) == null) { |
| 70 | + processor.setProperty(Constants.NOEXTRAHEADERS, Boolean.TRUE.toString()); |
| 71 | + } |
| 72 | + } |
| 73 | + // Set Bundle-SymbolicName |
| 74 | + if (processor.getProperty(Constants.BUNDLE_SYMBOLICNAME) == null) { |
| 75 | + processor.setProperty(Constants.BUNDLE_SYMBOLICNAME, project.getArtifactId()); |
| 76 | + } |
| 77 | + // Set Bundle-Name |
| 78 | + if (processor.getProperty(Constants.BUNDLE_NAME) == null) { |
| 79 | + processor.setProperty(Constants.BUNDLE_NAME, project.getName()); |
| 80 | + } |
| 81 | + // Set Bundle-Version |
| 82 | + String snapshot = isReproducible ? SNAPSHOT : null; |
| 83 | + if (processor.getProperty(Constants.BUNDLE_VERSION) == null) { |
| 84 | + Version version = new MavenVersion(project.getVersion()).getOSGiVersion(); |
| 85 | + processor.setProperty(Constants.BUNDLE_VERSION, version.toString()); |
| 86 | + if (snapshot == null) { |
| 87 | + snapshot = TSTAMP; |
| 88 | + } |
| 89 | + } |
| 90 | + if (snapshot != null) { |
| 91 | + if (processor.getProperty(Constants.SNAPSHOT) == null) { |
| 92 | + processor.setProperty(Constants.SNAPSHOT, snapshot); |
| 93 | + } |
| 94 | + } |
| 95 | + |
| 96 | + // Set Bundle-Description |
| 97 | + if (processor.getProperty(Constants.BUNDLE_DESCRIPTION) == null) { |
| 98 | + // may be null |
| 99 | + if (StringUtils.isNotBlank(project.getDescription())) { |
| 100 | + processor.setProperty(Constants.BUNDLE_DESCRIPTION, project.getDescription()); |
| 101 | + } |
| 102 | + } |
| 103 | + |
| 104 | + // Set Bundle-Vendor |
| 105 | + if (processor.getProperty(Constants.BUNDLE_VENDOR) == null) { |
| 106 | + if (project.getOrganization() != null && StringUtils.isNotBlank(project.getOrganization() |
| 107 | + .getName())) { |
| 108 | + processor.setProperty(Constants.BUNDLE_VENDOR, project.getOrganization() |
| 109 | + .getName()); |
| 110 | + } |
| 111 | + } |
| 112 | + |
| 113 | + // Set Bundle-License |
| 114 | + if (processor.getProperty(Constants.BUNDLE_LICENSE) == null) { |
| 115 | + StringBuilder licenses = new StringBuilder(); |
| 116 | + for (License license : project.getLicenses()) { |
| 117 | + addHeaderValue(licenses, license.getName(), ','); |
| 118 | + // link is optional |
| 119 | + if (StringUtils.isNotBlank(license.getUrl())) { |
| 120 | + addHeaderAttribute(licenses, "link", license.getUrl(), ';'); |
| 121 | + } |
| 122 | + // comment is optional |
| 123 | + if (StringUtils.isNotBlank(license.getComments())) { |
| 124 | + addHeaderAttribute(licenses, "description", license.getComments(), ';'); |
| 125 | + } |
| 126 | + } |
| 127 | + if (licenses.length() > 0) { |
| 128 | + processor.setProperty(Constants.BUNDLE_LICENSE, licenses.toString()); |
| 129 | + } |
| 130 | + } |
| 131 | + |
| 132 | + // Set Bundle-SCM |
| 133 | + if (processor.getProperty(Constants.BUNDLE_SCM) == null) { |
| 134 | + StringBuilder scm = new StringBuilder(); |
| 135 | + if (project.getScm() != null) { |
| 136 | + if (StringUtils.isNotBlank(project.getScm() |
| 137 | + .getUrl())) { |
| 138 | + addHeaderAttribute(scm, "url", project.getScm() |
| 139 | + .getUrl(), ','); |
| 140 | + } |
| 141 | + if (StringUtils.isNotBlank(project.getScm() |
| 142 | + .getConnection())) { |
| 143 | + addHeaderAttribute(scm, "connection", project.getScm() |
| 144 | + .getConnection(), ','); |
| 145 | + } |
| 146 | + if (StringUtils.isNotBlank(project.getScm() |
| 147 | + .getDeveloperConnection())) { |
| 148 | + addHeaderAttribute(scm, "developer-connection", project.getScm() |
| 149 | + .getDeveloperConnection(), ','); |
| 150 | + } |
| 151 | + if (StringUtils.isNotBlank(project.getScm() |
| 152 | + .getTag())) { |
| 153 | + addHeaderAttribute(scm, "tag", project.getScm() |
| 154 | + .getTag(), ','); |
| 155 | + } |
| 156 | + if (scm.length() > 0) { |
| 157 | + processor.setProperty(Constants.BUNDLE_SCM, scm.toString()); |
| 158 | + } |
| 159 | + } |
| 160 | + } |
| 161 | + |
| 162 | + // Set Bundle-Developers |
| 163 | + if (processor.getProperty(Constants.BUNDLE_DEVELOPERS) == null) { |
| 164 | + StringBuilder developers = new StringBuilder(); |
| 165 | + // this is never null |
| 166 | + for (Developer developer : project.getDevelopers()) { |
| 167 | + // id is mandatory for OSGi but not enforced in the pom.xml |
| 168 | + if (StringUtils.isNotBlank(developer.getId())) { |
| 169 | + addHeaderValue(developers, developer.getId(), ','); |
| 170 | + // all attributes are optional |
| 171 | + if (StringUtils.isNotBlank(developer.getEmail())) { |
| 172 | + addHeaderAttribute(developers, "email", developer.getEmail(), ';'); |
| 173 | + } |
| 174 | + if (StringUtils.isNotBlank(developer.getName())) { |
| 175 | + addHeaderAttribute(developers, "name", developer.getName(), ';'); |
| 176 | + } |
| 177 | + if (StringUtils.isNotBlank(developer.getOrganization())) { |
| 178 | + addHeaderAttribute(developers, "organization", developer.getOrganization(), ';'); |
| 179 | + } |
| 180 | + if (StringUtils.isNotBlank(developer.getOrganizationUrl())) { |
| 181 | + addHeaderAttribute(developers, "organizationUrl", developer.getOrganizationUrl(), ';'); |
| 182 | + } |
| 183 | + if (!developer.getRoles() |
| 184 | + .isEmpty()) { |
| 185 | + addHeaderAttribute(developers, "roles", StringUtils.join(developer.getRoles() |
| 186 | + .iterator(), ","), ';'); |
| 187 | + } |
| 188 | + if (StringUtils.isNotBlank(developer.getTimezone())) { |
| 189 | + addHeaderAttribute(developers, "timezone", developer.getTimezone(), ';'); |
| 190 | + } |
| 191 | + } else { |
| 192 | + logger.warn( |
| 193 | + "Cannot consider developer in line '{}' of file '{}' for bundle header '{}' as it does not contain the mandatory id.", |
| 194 | + developer.getLocation("") |
| 195 | + .getLineNumber(), |
| 196 | + developer.getLocation("") |
| 197 | + .getSource() |
| 198 | + .getLocation(), |
| 199 | + Constants.BUNDLE_DEVELOPERS); |
| 200 | + } |
| 201 | + } |
| 202 | + if (developers.length() > 0) { |
| 203 | + processor.setProperty(Constants.BUNDLE_DEVELOPERS, developers.toString()); |
| 204 | + } |
| 205 | + } |
| 206 | + |
| 207 | + // Set Bundle-DocURL |
| 208 | + if (processor.getProperty(Constants.BUNDLE_DOCURL) == null) { |
| 209 | + if (StringUtils.isNotBlank(project.getUrl())) { |
| 210 | + processor.setProperty(Constants.BUNDLE_DOCURL, project.getUrl()); |
| 211 | + } |
| 212 | + } |
| 213 | + } |
| 214 | + |
50 | 215 | private void loadParentProjectProperties(Processor builder, MavenProject currentProject) throws Exception {
|
51 | 216 | MavenProject parentProject = currentProject.getParent();
|
52 | 217 | if (parentProject == null) {
|
@@ -126,4 +291,24 @@ private Optional<Xpp3Dom> getConfiguration(List<Plugin> plugins) {
|
126 | 291 | private Xpp3Dom defaultConfiguration() {
|
127 | 292 | return new Xpp3Dom("configuration");
|
128 | 293 | }
|
| 294 | + |
| 295 | + private static StringBuilder addHeaderValue(StringBuilder builder, String value, char separator) { |
| 296 | + if (builder.length() > 0) { |
| 297 | + builder.append(separator); |
| 298 | + } |
| 299 | + // use quoted string if necessary |
| 300 | + OSGiHeader.quote(builder, value); |
| 301 | + return builder; |
| 302 | + } |
| 303 | + |
| 304 | + private static StringBuilder addHeaderAttribute(StringBuilder builder, String key, String value, char separator) { |
| 305 | + if (builder.length() > 0) { |
| 306 | + builder.append(separator); |
| 307 | + } |
| 308 | + builder.append(key) |
| 309 | + .append("="); |
| 310 | + // use quoted string if necessary |
| 311 | + OSGiHeader.quote(builder, value); |
| 312 | + return builder; |
| 313 | + } |
129 | 314 | }
|
0 commit comments