blob: d4507509eaf0fcd0634b72b660248bdb608d4b95 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2019 Lablicate GmbH and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Christoph Läubrich - initial API and implementation derived from TychoModelReader
*******************************************************************************/
package org.eclipse.tycho.pomless;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.util.Properties;
import java.util.jar.Attributes;
import java.util.jar.Manifest;
import org.apache.maven.model.Model;
import org.apache.maven.model.Organization;
import org.apache.maven.model.io.ModelParseException;
import org.codehaus.plexus.component.annotations.Component;
import org.sonatype.maven.polyglot.mapping.Mapping;
@Component(role = Mapping.class, hint = TychoBundleMapping.PACKAGING)
public class TychoBundleMapping extends AbstractTychoMapping {
private static final String BUNDLE_SYMBOLIC_NAME = "Bundle-SymbolicName";
public static final String PACKAGING = "eclipse-plugin";
private static final String PACKAGING_TEST = "eclipse-test-plugin";
private static final String MANIFEST_MF = "META-INF/MANIFEST.MF";
public static final String MANIFEST_MF_MARKER = ".META-INF_MANIFEST.MF";
@Override
protected boolean isValidLocation(String location) {
return location.endsWith(MANIFEST_MF_MARKER);
}
@Override
protected File getPrimaryArtifact(File dir) {
File manifestFile = new File(dir, MANIFEST_MF);
if (manifestFile.isFile()) {
File markerFile = new File(dir, MANIFEST_MF_MARKER);
try {
markerFile.createNewFile();
} catch (IOException e) {
throw new RuntimeException("can't create markerfile", e);
}
markerFile.deleteOnExit();
return markerFile;
}
return null;
}
@Override
protected String getPackaging() {
return PACKAGING;
}
@Override
protected void initModel(Model model, Reader artifactReader, File artifactFile)
throws ModelParseException, IOException {
File bundleRoot = artifactFile.getParentFile();
File manifestFile = new File(bundleRoot, MANIFEST_MF);
Attributes manifestHeaders = readManifestHeaders(manifestFile);
String bundleSymbolicName = getBundleSymbolicName(manifestHeaders, manifestFile);
// groupId is inherited from parent pom
model.setArtifactId(bundleSymbolicName);
String bundleVersion = getRequiredHeaderValue("Bundle-Version", manifestHeaders, manifestFile);
model.setVersion(getPomVersion(bundleVersion));
if (isTestBundle(bundleSymbolicName, manifestHeaders, bundleRoot)) {
model.setPackaging(PACKAGING_TEST);
}
String bundleName = getManifestAttributeValue(manifestHeaders, "Bundle-Name", manifestFile);
if (bundleName != null) {
model.setName(bundleName);
} else {
model.setName(bundleSymbolicName);
}
String vendorName = getManifestAttributeValue(manifestHeaders, "Bundle-Vendor", manifestFile);
if (vendorName != null) {
Organization organization = new Organization();
organization.setName(vendorName);
model.setOrganization(organization);
}
String description = getManifestAttributeValue(manifestHeaders, "Bundle-Description", manifestFile);
if (description != null) {
model.setDescription(description);
}
}
@Override
protected File getRealArtifactFile(File polyglotArtifactFile) {
if (polyglotArtifactFile.getName().equals(MANIFEST_MF_MARKER)) {
return new File(polyglotArtifactFile.getParentFile(), MANIFEST_MF);
}
return super.getRealArtifactFile(polyglotArtifactFile);
}
private Attributes readManifestHeaders(File manifestFile) throws IOException {
Manifest manifest = new Manifest();
try (FileInputStream stream = new FileInputStream(manifestFile)) {
manifest.read(stream);
}
return manifest.getMainAttributes();
}
private String getBundleSymbolicName(Attributes headers, File manifestFile) throws ModelParseException {
String symbolicName = getRequiredHeaderValue(BUNDLE_SYMBOLIC_NAME, headers, manifestFile);
// strip off any directives/attributes
int semicolonIndex = symbolicName.indexOf(';');
if (semicolonIndex > 0) {
symbolicName = symbolicName.substring(0, semicolonIndex);
}
return symbolicName;
}
private String getRequiredHeaderValue(String headerKey, Attributes headers, File manifestFile)
throws ModelParseException {
String value = headers.getValue(headerKey);
if (value == null) {
throw new ModelParseException("Required header " + headerKey + " missing in " + manifestFile, -1, -1);
}
return value;
}
private boolean isTestBundle(String bundleSymbolicName, Attributes manifestHeaders, File bundleRoot)
throws IOException {
Properties buildProperties = getBuildProperties(bundleRoot);
String property = buildProperties.getProperty("tycho.pomless.testbundle");
if (property != null) {
//if property is given it take precedence over our guesses...
return Boolean.valueOf(property);
}
if (bundleSymbolicName.endsWith(".tests") || bundleSymbolicName.endsWith(".test")) {
//TODO can we improve this? maybe also if the import/require bundle contains junit we should assume its a test bundle?
// or should we search for Test classes annotations?
return true;
}
return false;
}
private static String getManifestAttributeValue(Attributes headers, String attributeName, File manifestFile)
throws IOException {
String location = headers.getValue("Bundle-Localization");
if (location == null || location.isEmpty()) {
location = "OSGI-INF/l10n/bundle";
}
String rawValue = headers.getValue(attributeName);
if (rawValue != null && !rawValue.isEmpty()) {
if (rawValue.startsWith("%")) {
String key = rawValue.substring(1);
//we always use the default here to have consistent build regardless of locale settings
File l10nFile = new File(manifestFile.getParentFile().getParentFile(), location + ".properties");
if (l10nFile.exists()) {
Properties properties = new Properties();
try (InputStream stream = new FileInputStream(l10nFile)) {
properties.load(stream);
}
String translation = properties.getProperty(key);
if (translation != null && !translation.isEmpty()) {
return translation;
}
}
return key;
}
return rawValue;
}
return null;
}
}