org.eclipse.tycho.pomless.AbstractTychoMapping Maven / Gradle / Ivy
Go to download
A build extension which allows to omit pom.xml files for all Eclipse PDE projects, Plug-ins(Bundles) and Features
as well as Eclipse Products, Target-Platforms and p2-repository definitions (category.xml).
/*******************************************************************************
* Copyright (c) 2019, 2020 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 methods setLocation/findParent/getPomVersion from TychoModelReader
*******************************************************************************/
package org.eclipse.tycho.pomless;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.apache.maven.model.InputLocation;
import org.apache.maven.model.InputSource;
import org.apache.maven.model.Model;
import org.apache.maven.model.Parent;
import org.apache.maven.model.building.FileModelSource;
import org.apache.maven.model.building.ModelProcessor;
import org.apache.maven.model.io.ModelParseException;
import org.apache.maven.model.io.ModelReader;
import org.apache.maven.model.io.ModelWriter;
import org.codehaus.plexus.PlexusContainer;
import org.codehaus.plexus.component.annotations.Requirement;
import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
import org.codehaus.plexus.logging.Logger;
import org.sonatype.maven.polyglot.PolyglotModelUtil;
import org.sonatype.maven.polyglot.mapping.Mapping;
/**
* Base implementation for a {@link Mapping} and {@link ModelReader} that handles all the low-level
* stuff, implementations must only handle a small subset
*
*/
public abstract class AbstractTychoMapping implements Mapping, ModelReader {
private static final String TYCHO_POMLESS_PARENT = "tycho.pomless.parent";
private static final String PARENT_POM_DEFAULT_VALUE = System.getProperty(TYCHO_POMLESS_PARENT, "..");
private static final String QUALIFIER_SUFFIX = ".qualifier";
private static final String MODEL_PARENT = "TychoMapping.model.parent";
@Requirement
protected PlexusContainer container;
@Requirement
protected Logger logger;
private ModelWriter writer;
@Override
public File locatePom(File dir) {
File file = new File(dir, "pom.xml");
if (file.exists()) {
//we want that pom.xml takes precedence over our generated one
return null;
}
return getPrimaryArtifact(dir);
}
@Override
public boolean accept(Map options) {
String location = PolyglotModelUtil.getLocation(options);
if (location == null || location.endsWith("pom.xml")) {
//we want that pom.xml takes precedence over our generated one
return false;
}
return isValidLocation(location);
}
@Override
public ModelReader getReader() {
return this;
}
@Override
public ModelWriter getWriter() {
if (writer == null) {
try {
assert container != null;
writer = container.lookup(ModelWriter.class, getFlavour());
} catch (ComponentLookupException e) {
throw new RuntimeException(e);
}
}
return writer;
}
@Override
public Model read(InputStream input, Map options) throws IOException, ModelParseException {
String location = PolyglotModelUtil.getLocation(options);
File file = new File(location);
try (InputStreamReader stream = new InputStreamReader(input, getPrimaryArtifactCharset())) {
return read(stream, file, options);
}
}
@Override
public Model read(File input, Map options) throws IOException, ModelParseException {
File artifactFile = getRealArtifactFile(input);
if (artifactFile.exists()) {
try (InputStreamReader stream = new InputStreamReader(new FileInputStream(artifactFile),
getPrimaryArtifactCharset())) {
return read(stream, input, options);
}
} else {
//we support the case here, that actually there is no real primary artifact, instead of creating a dummy file and forcing I/O we simply simulate to read a 0-byte file
return read(new StringReader(""), input, options);
}
}
@Override
public Model read(Reader input, Map options) throws IOException, ModelParseException {
File file = new File(PolyglotModelUtil.getLocation(options));
return read(input, file, options);
}
private Model read(Reader artifactReader, File artifactFile, Map options)
throws ModelParseException, IOException {
Model model = new Model();
model.setModelVersion("4.0.0");
model.setPackaging(getPackaging());
initModel(model, artifactReader, artifactFile);
if (model.getParent() == null) {
model.setParent(findParent(artifactFile.getParentFile(), options));
}
if (model.getVersion() == null) {
//inherit version from parent if not given
model.setVersion(model.getParent().getVersion());
}
if (model.getName() == null) {
model.setName(model.getArtifactId());
}
setLocation(model, getRealArtifactFile(artifactFile));
return model;
}
protected File getRealArtifactFile(File polyglotArtifactFile) {
return polyglotArtifactFile;
}
protected Parent findParent(File projectRoot, Map projectOptions)
throws ModelParseException, IOException {
Parent parent = (Parent) projectOptions.get(MODEL_PARENT);
if (parent != null) {
//if the parent is given by the options we don't need to search it!
return parent;
}
Properties buildProperties = getBuildProperties(projectRoot);
// assumption parent pom must be physically located in parent directory if not given by build.properties
String parentRef = buildProperties.getProperty(TYCHO_POMLESS_PARENT, PARENT_POM_DEFAULT_VALUE);
File fileOrFolder = new File(projectRoot, parentRef).getCanonicalFile();
PomReference parentPom;
if (fileOrFolder.isFile()) {
parentPom = locatePomReference(fileOrFolder.getParentFile(), fileOrFolder.getName());
} else if (fileOrFolder.isDirectory()) {
parentPom = locatePomReference(fileOrFolder, null);
} else {
throw new FileNotFoundException("parent pom file/folder " + fileOrFolder + " is not accessible");
}
if (parentPom == null) {
throw new FileNotFoundException("No parent pom file found in " + fileOrFolder.getCanonicalPath());
}
Map options = new HashMap<>(1);
options.put(ModelProcessor.SOURCE, new FileModelSource(parentPom.getPomFile()));
Model parentModel = parentPom.getReader().read(parentPom.getPomFile(), options);
Parent parentReference = new Parent();
String groupId = parentModel.getGroupId();
if (groupId == null) {
// must be inherited from grandparent
groupId = parentModel.getParent().getGroupId();
}
parentReference.setGroupId(groupId);
parentReference.setArtifactId(parentModel.getArtifactId());
String version = parentModel.getVersion();
if (version == null) {
// must be inherited from grandparent
version = parentModel.getParent().getVersion();
}
parentReference.setVersion(version);
parentReference.setRelativePath(
projectRoot.getCanonicalFile().toPath().relativize(parentPom.getPomFile().toPath()).toString());
logger.debug("Derived parent for path " + projectRoot + " is goupId: " + parentReference.getGroupId()
+ ", artifactId: " + parentReference.getArtifactId() + ", relativePath: "
+ parentReference.getRelativePath());
return parentReference;
}
/**
* Locates the {@link PomReference} for the given folder and the given nameHint
*
* @param folder
* the folder to search
* @param nameHint
* the name hint to use
* @return the {@link PomReference} or null
*/
protected PomReference locatePomReference(File folder, String nameHint) {
PomReference reference = null;
try {
List lookupList = container.lookupList(ModelProcessor.class);
for (ModelProcessor processor : lookupList) {
try {
File pom = processor.locatePom(folder);
if (pom != null && pom.exists()) {
if (reference == null || pom.getName().equals(nameHint)) {
reference = new PomReference(pom, processor);
}
}
} catch (AssertionError e) {
//polyglot common 0.4.4 throws AssertionError instead of returning null pom
}
}
} catch (ComponentLookupException e) {
}
return reference;
}
@Override
public float getPriority() {
return 0;
}
@Override
public String getFlavour() {
return getPackaging();
}
protected abstract boolean isValidLocation(String location);
protected abstract File getPrimaryArtifact(File dir);
protected abstract String getPackaging();
/**
* returns the charset that should be used when reading artifact, default is UTF-8 might be
* overridden by subclasses
*
* @return the charset
*/
protected Charset getPrimaryArtifactCharset() {
return StandardCharsets.UTF_8;
}
protected abstract void initModel(Model model, Reader artifactReader, File artifactFile)
throws ModelParseException, IOException;
protected static Properties getBuildProperties(File dir) throws IOException {
Properties properties = new Properties();
File buildProperties = new File(dir, "build.properties");
if (buildProperties.exists()) {
try (FileInputStream stream = new FileInputStream(buildProperties)) {
properties.load(stream);
}
}
return properties;
}
private static void setLocation(Model model, File modelSource) {
InputSource inputSource = new InputSource();
inputSource.setLocation(modelSource.toString());
inputSource.setModelId(model.getParent().getGroupId() + ":" + model.getArtifactId() + ":" + model.getVersion());
model.setLocation("", new InputLocation(0, 0, inputSource));
}
protected static String getPomVersion(String pdeVersion) {
String pomVersion = pdeVersion;
if (pdeVersion.endsWith(QUALIFIER_SUFFIX)) {
pomVersion = pdeVersion.substring(0, pdeVersion.length() - QUALIFIER_SUFFIX.length()) + "-SNAPSHOT";
}
return pomVersion;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy