/*****************************************************************************
* Copyright (c) 2006-2013, Cloudsmith Inc.
* The code, documentation and other materials contained herein have been
* licensed under the Eclipse Public License - v 1.0 by the copyright holder
* listed above, as the Initial Contributor under such license. The text of
* such license is available at www.eclipse.org.
*****************************************************************************/
package org.eclipse.buckminster.pde.cspecgen.bundle;
import java.io.File;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import org.eclipse.buckminster.ant.actor.CopyActor;
import org.eclipse.buckminster.ant.tasks.VersionQualifierTask;
import org.eclipse.buckminster.core.KeyConstants;
import org.eclipse.buckminster.core.cspec.WellknownActions;
import org.eclipse.buckminster.core.cspec.builder.ActionBuilder;
import org.eclipse.buckminster.core.cspec.builder.ArtifactBuilder;
import org.eclipse.buckminster.core.cspec.builder.AttributeBuilder;
import org.eclipse.buckminster.core.cspec.builder.CSpecBuilder;
import org.eclipse.buckminster.core.cspec.builder.ComponentRequestBuilder;
import org.eclipse.buckminster.core.cspec.builder.GeneratorBuilder;
import org.eclipse.buckminster.core.cspec.builder.GroupBuilder;
import org.eclipse.buckminster.core.cspec.builder.PrerequisiteBuilder;
import org.eclipse.buckminster.core.cspec.model.UpToDatePolicy;
import org.eclipse.buckminster.core.ctype.IComponentType;
import org.eclipse.buckminster.core.helpers.TextUtils;
import org.eclipse.buckminster.core.query.model.ComponentQuery;
import org.eclipse.buckminster.core.reader.ICatalogReader;
import org.eclipse.buckminster.core.reader.ProjectDescReader;
import org.eclipse.buckminster.core.version.VersionHelper;
import org.eclipse.buckminster.jdt.ClasspathReader;
import org.eclipse.buckminster.pde.cspecgen.CSpecGenerator;
import org.eclipse.buckminster.pde.internal.actor.FragmentsActor;
import org.eclipse.buckminster.pde.tasks.VersionConsolidator;
import org.eclipse.buckminster.runtime.MonitorUtils;
import org.eclipse.buckminster.runtime.Trivial;
import org.eclipse.core.internal.resources.LinkDescription;
import org.eclipse.core.internal.resources.ProjectDescription;
import org.eclipse.core.internal.utils.FileUtil;
import org.eclipse.core.resources.IProjectDescription;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.equinox.p2.metadata.Version;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.internal.core.ClasspathEntry;
import org.eclipse.pde.core.build.IBuild;
import org.eclipse.pde.core.build.IBuildEntry;
import org.eclipse.pde.core.build.IBuildModel;
import org.eclipse.pde.core.plugin.IFragmentModel;
import org.eclipse.pde.core.plugin.IPluginBase;
import org.eclipse.pde.core.plugin.IPluginModelBase;
import org.eclipse.pde.internal.build.IPDEBuildConstants;
import org.eclipse.pde.internal.core.ICoreConstants;
import org.eclipse.pde.internal.core.bundle.BundlePlugin;
import org.eclipse.pde.internal.core.ibundle.IBundle;
import org.osgi.framework.Constants;
/**
* A CSpec builder that creates a cspec using the META-INF/MANIFEST.MF,
* plugin.xml and fragment.xml files.
*
* @author Thomas Hallgren
*/
@SuppressWarnings("restriction")
public class CSpecFromSource extends CSpecGenerator {
private static final String ATTRIBUTE_BUNDLE_EXTRAJARS = "bundle.extrajars"; //$NON-NLS-1$
private static final String ATTRIBUTE_ECLIPSE_BUILD = WellknownActions.ECLIPSE.BUILD.toString();
private static final String ATTRIBUTE_ECLIPSE_BUILD_REQUIREMENTS = ATTRIBUTE_ECLIPSE_BUILD + ".requirements"; //$NON-NLS-1$
private static final String ATTRIBUTE_ECLIPSE_BUILD_SOURCE = ATTRIBUTE_ECLIPSE_BUILD + ".source"; //$NON-NLS-1$
private static final String ATTRIBUTE_ECLIPSE_CLEAN = WellknownActions.ECLIPSE.CLEAN.toString();
private static final String PREFIX_CREATE_JAR = "create."; //$NON-NLS-1$
private static final String PREFIX_ECLIPSE_BUILD_OUTPUT = "eclipse.build.output."; //$NON-NLS-1$
private static final String PREFIX_ROUGE_SOURCE = "rouge.sources."; //$NON-NLS-1$
private static final IClasspathEntry[] emptyClasspath = new IClasspathEntry[0];
private static String getArtifactName(IPath buildOutput) {
return PREFIX_ECLIPSE_BUILD_OUTPUT + pathToName(buildOutput);
}
private static String pathToName(IPath path) {
path = path.setDevice(null).makeRelative();
if (path.segmentCount() > 2)
path = path.removeFirstSegments(path.segmentCount() - 2);
return path.removeTrailingSeparator().toPortableString().replace('/', '.');
}
private final IBuildModel buildModel;
private final IPluginBase plugin;
private IProjectDescription projectDesc;
public CSpecFromSource(CSpecBuilder cspecBuilder, ICatalogReader reader, IPluginBase plugin, IBuildModel buildModel) {
super(cspecBuilder, reader);
this.plugin = plugin;
this.buildModel = buildModel;
}
@Override
public void generate(IProgressMonitor monitor) throws CoreException {
monitor.beginTask(null, 100);
CSpecBuilder cspec = getCSpec();
GroupBuilder classpath = cspec.addGroup(ATTRIBUTE_JAVA_BINARIES, true);
GroupBuilder fullClean = cspec.addGroup(ATTRIBUTE_FULL_CLEAN, true);
GroupBuilder bundleJars = cspec.addGroup(ATTRIBUTE_BUNDLE_JARS, true);
cspec.addGroup(ATTRIBUTE_PRODUCT_CONFIG_EXPORTS, true);
if (getReader().isFileSystemReader())
projectDesc = ProjectDescReader.getProjectDescription(getReader(), MonitorUtils.subMonitor(monitor, 15));
else {
projectDesc = null;
MonitorUtils.worked(monitor, 15);
}
addImports();
MonitorUtils.worked(monitor, 5);
IClasspathEntry[] classPath;
try {
classPath = ClasspathReader.getClasspath(getReader(), MonitorUtils.subMonitor(monitor, 45));
} catch (CoreException e) {
classPath = null;
}
if (classPath == null)
classPath = new IClasspathEntry[0];
fullClean.addLocalPrerequisite(generateRemoveDirAction("build", OUTPUT_DIR, false)); //$NON-NLS-1$
fullClean.addLocalPrerequisite(cspec.addInternalAction(ATTRIBUTE_ECLIPSE_CLEAN, false));
boolean simpleBundle = false;
IBuild build = buildModel.getBuild();
ArrayList<String> jarsToCompile = null;
Map<IPath, IPath> outputMap = new HashMap<IPath, IPath>();
for (IBuildEntry entry : build.getBuildEntries()) {
String name = entry.getName();
if (name.startsWith("source.")) //$NON-NLS-1$
{
if (name.length() == 8 && name.charAt(7) == '.') {
simpleBundle = true;
} else if (name.endsWith(".jar") && name.length() > 11) { //$NON-NLS-1$
if (jarsToCompile == null)
jarsToCompile = new ArrayList<String>();
jarsToCompile.add(name.substring(7));
}
} else if (name.startsWith("output.") && name.length() > 7) { //$NON-NLS-1$
String[] tokens = entry.getTokens();
if (tokens.length == 1)
outputMap.put(Path.fromPortableString(tokens[0]), Path.fromPortableString(name.substring(7)));
}
}
// Exported entries in the classpath must be added to the
// java.binaries export
//
ActionBuilder eclipseBuild = getAttributeEclipseBuild();
Set<IPath> derivedArtifacts = new HashSet<IPath>();
IPath[] projectRootReplacement = new IPath[1];
HashMap<IPath, AttributeBuilder> eclipseBuildProducts = new HashMap<IPath, AttributeBuilder>();
IPath componentHome = Path.fromPortableString(KeyConstants.ACTION_HOME_REF);
IPath defaultOutputLocation = null;
GroupBuilder ebSrcBld = null;
int cnt = 0;
for (IClasspathEntry cpe : classPath) {
if (cpe.getEntryKind() != IClasspathEntry.CPE_SOURCE)
continue;
// add the class path entry to build sources
IPath cpePath = asProjectRelativeFolder(cpe.getPath(), projectRootReplacement);
ArtifactBuilder ab = cspec.addArtifact(ATTRIBUTE_ECLIPSE_BUILD_SOURCE + '_' + cnt++, false, projectRootReplacement[0]);
ab.setBase(cpePath);
if (ebSrcBld == null)
ebSrcBld = getGroupEclipseBuildSource(true);
ebSrcBld.addLocalPrerequisite(ab);
// The output declared in the source entry is a product of the
// Eclipse build. If there's no output declared, we use the
// default.
//
IPath output = cpe.getOutputLocation();
if (output == null) {
if (defaultOutputLocation == null)
defaultOutputLocation = getDefaultOutputLocation(classPath, projectRootReplacement);
output = defaultOutputLocation;
} else
output = asProjectRelativeFolder(output, projectRootReplacement);
if (output == null)
continue;
// Several source may contribute to the same output directory.
// Make sure we only add it once.
//
if (eclipseBuildProducts.containsKey(output))
continue;
// Products use ${buckminster.output} as the default base so we
// need
// to prefix the project relative output here
//
IPath base;
IPath relPath = null;
if (output.isAbsolute()) {
base = output;
} else {
base = projectRootReplacement[0];
if (base == null)
base = componentHome;
base = base.append(output);
// Find the expected location in the
// bundle jar. We don't care about
// '.' since that is handled elsewhere
relPath = outputMap.get(output);
if (relPath != null && relPath.segmentCount() == 1 && ".".equals(relPath.segment(0))) //$NON-NLS-1$
relPath = null;
if (relPath != null)
derivedArtifacts.add(relPath);
}
GroupBuilder productRebase = null;
String artifactName = getArtifactName(output);
if (relPath != null && base.segmentCount() > relPath.segmentCount()) {
// Rebase if possible. relPath must be a suffix of base
IPath cmp = base.removeFirstSegments(base.segmentCount() - relPath.segmentCount());
if (cmp.equals(relPath)) {
productRebase = cspec.addGroup(artifactName, false);
productRebase.setName(artifactName);
// Change the name of the actual product and use
// it as a prerequisite to this group
artifactName += ".raw"; //$NON-NLS-1$
productRebase.addLocalPrerequisite(artifactName);
productRebase.setPrerequisiteRebase(base.removeLastSegments(relPath.segmentCount()));
}
}
ArtifactBuilder ab2 = eclipseBuild.addProductArtifact(artifactName, false, base);
// Add either the rebased group or the product as the
// built product for later perusal
eclipseBuildProducts.put(output, productRebase == null ? ab2 : productRebase);
}
AttributeBuilder buildSource = null;
if (ebSrcBld != null)
buildSource = normalizeGroup(ebSrcBld);
// The classpath already contains all the re-exported stuff (it was
// added when the imports were added). Only thing missing is the
// output from the Eclipse build and the optional extra jar files.
// TODO: We want to limit the 'eclipseBuild' prerequisite to
// what this bundle actually exports
//
classpath.addLocalPrerequisite(eclipseBuild);
// The bundle classpath can contain artifacts that can stem from three
// different locations:
// 1. The bundle itself, i.e. a simpleBundle containing .class files
// rooted
// at the bundle root
// 2. Jars compiled from .class files produced by the eclipse build
// (build entries)
// 3. Pre-built extra jar files present in the bundle.
//
List<String> binIncludes;
List<String> srcIncludes;
if (getReader().isFileSystemReader()) {
File baseDir = getReader().getLocation();
binIncludes = expandBinFiles(baseDir, build);
srcIncludes = expandSrcFiles(baseDir, build);
} else {
IBuildEntry binIncludesEntry = build.getEntry(PROPERTY_BIN_INCLUDES);
if (binIncludesEntry != null) {
binIncludes = expandIncludes(binIncludesEntry.getTokens());
} else {
binIncludes = Collections.emptyList();
}
IBuildEntry srcIncludesEntry = build.getEntry(PROPERTY_SRC_INCLUDES);
if (srcIncludesEntry != null) {
srcIncludes = expandIncludes(srcIncludesEntry.getTokens());
} else {
srcIncludes = Collections.emptyList();
}
}
String bundleClassPath = null;
if (plugin instanceof BundlePlugin) {
IBundle bundle = ((BundlePlugin) plugin).getBundle();
setFilter(bundle.getHeader(ICoreConstants.PLATFORM_FILTER));
cspec.setShortDesc(expand(bundle.getHeader(Constants.BUNDLE_NAME)));
bundleClassPath = bundle.getHeader(Constants.BUNDLE_CLASSPATH);
if (bundleClassPath != null) {
cnt = 0;
GroupBuilder eaBld = null;
StringTokenizer tokens = new StringTokenizer(bundleClassPath, ","); //$NON-NLS-1$
while (tokens.hasMoreTokens()) {
String token = tokens.nextToken().trim();
if (simpleBundle && token.equals(".") || token.equals("./")) //$NON-NLS-1$ //$NON-NLS-2$
continue;
if (jarsToCompile != null && jarsToCompile.contains(token))
//
// Assume that this jar is produced by the eclipse build
// and thus covered by inclusion from the project
// classpath above.
//
continue;
if (binIncludes.contains(token))
// This one is covered by bin.includes so don't add it
// here
continue;
// We don't know how this entry came about. Chances are it
// has been checked in with the source.
//
ArtifactBuilder ab = cspec.addArtifact(ATTRIBUTE_BUNDLE_EXTRAJARS + '_' + cnt++, false, null);
IPath eaPath = resolveLink(Path.fromPortableString(token), projectRootReplacement);
ab.setBase(projectRootReplacement[0]);
ab.addPath(eaPath);
if (eaBld == null)
eaBld = getGroupExtraJars();
eaBld.addLocalPrerequisite(ab);
}
if (eaBld != null)
normalizeGroup(eaBld);
}
}
IBuildEntry secondaryDeps = build.getEntry(IBuildEntry.SECONDARY_DEPENDENCIES);
if (secondaryDeps != null) {
// Add dependencies unless they have been added as imports already
//
for (String depName : secondaryDeps.getTokens()) {
ComponentRequestBuilder dep = cspec.createDependencyBuilder();
dep.setName(depName);
dep.setComponentTypeID(IComponentType.OSGI_BUNDLE);
addDependency(dep);
}
}
// The manifest version may end with "qualifier" in which case it needs
// to be expanded.
// The expansion will create a new copy in a different location. In case
// there is no
// expansion, we can use the original file.
//
IPath manifestFolder = resolveLink(new Path(IPDEBuildConstants.MANIFEST_FOLDER).append(MANIFEST), null).removeLastSegments(1)
.addTrailingSeparator();
AttributeBuilder manifest = null;
boolean versionExpansion = false;
Version version = cspec.getVersion();
if (version != null) {
String versionQualifier = VersionHelper.getQualifier(version);
if (versionQualifier != null)
versionExpansion = versionQualifier.endsWith(VersionQualifierTask.QUALIFIER_SUFFIX);
}
// Add the build.properties artifact. We want to manage that separately
// since it
// is one of the requirements for expanding the bundle version
//
cspec.addArtifact(ATTRIBUTE_BUILD_PROPERTIES, true, null).addPath(new Path(BUILD_PROPERTIES_FILE));
if (versionExpansion) {
// Add the action that will create the manifest copy with the
// version expanded
// Another action that will do the same for the manifest used for
// the source bundle
//
IPath manifestPath = new Path(MANIFEST);
ArtifactBuilder rawManifest = cspec.addArtifact(ATTRIBUTE_RAW_MANIFEST, false, manifestFolder);
rawManifest.addPath(manifestPath);
ActionBuilder versionExpansionAction = addAntAction(ATTRIBUTE_MANIFEST, TASK_EXPAND_BUNDLE_VERSION, false);
versionExpansionAction.addLocalPrerequisite(ATTRIBUTE_RAW_MANIFEST, ALIAS_MANIFEST);
versionExpansionAction.addLocalPrerequisite(ATTRIBUTE_BUILD_PROPERTIES, ALIAS_PROPERTIES);
versionExpansionAction.setProductAlias(ALIAS_OUTPUT);
versionExpansionAction.setProductBase(OUTPUT_DIR_TEMP);
versionExpansionAction.addProductPath(manifestPath);
manifest = versionExpansionAction;
} else {
// No expansion needed, use original file.
//
ArtifactBuilder rawManifest = cspec.addArtifact(ATTRIBUTE_MANIFEST, true, manifestFolder);
rawManifest.addPath(new Path(MANIFEST));
manifest = rawManifest;
}
// Create an action for building each jar.
//
if (jarsToCompile != null) {
for (String jarName : jarsToCompile)
derivedArtifacts.add(createJarAction(jarName, classPath, build, outputMap, simpleBundle ? eclipseBuildProducts : null));
}
if (simpleBundle) {
derivedArtifacts.add(new Path(".")); //$NON-NLS-1$
derivedArtifacts.add(new Path("./")); // Uncertain which one is used //$NON-NLS-1$
}
// The jar contents group represents all contents of the final jar
// except the
// manifest
//
GroupBuilder jarContents = getAttributeJarContents();
// Add additional artifacts to be included in the bundle
//
boolean includeBuildProps = false;
boolean considerAboutMappings = VersionConsolidator.getBooleanProperty(getReader().getNodeQuery().getProperties(),
PROP_PDE_CONSIDER_ABOUT_MAPPINGS, true);
if (!binIncludes.isEmpty()) {
GroupBuilder binIncludesSource = null;
cnt = 0;
for (String token : binIncludes) {
if (BUNDLE_FILE.equalsIgnoreCase(token))
//
// Handled separately (might be derived)
//
continue;
if (versionExpansion && BUILD_PROPERTIES_FILE.equals(token)) {
includeBuildProps = true;
continue;
}
IPath binInclude = new Path(token);
if (derivedArtifacts.contains(binInclude))
//
// Not an artifact. This is produced by some action
//
continue;
if (binIncludesSource == null)
binIncludesSource = cspec.addGroup(ATTRIBUTE_BIN_INCLUDES, false);
if (considerAboutMappings && ABOUT_MAPPINGS_FILE.equalsIgnoreCase(token)) {
AttributeBuilder aboutMappings = addAboutMappingsAction(binInclude, projectRootReplacement[0]);
binIncludesSource.addLocalPrerequisite(aboutMappings);
} else {
IPath biPath = resolveLink(binInclude, projectRootReplacement);
ArtifactBuilder ab = cspec.addArtifact(ATTRIBUTE_BIN_INCLUDES + '_' + cnt++, false, projectRootReplacement[0]);
ab.addPath(biPath);
binIncludesSource.addLocalPrerequisite(ab);
}
}
if (binIncludesSource != null) {
normalizeGroup(binIncludesSource);
jarContents.addLocalPrerequisite(ATTRIBUTE_BIN_INCLUDES);
}
if (includeBuildProps)
jarContents.addLocalPrerequisite(ATTRIBUTE_BUILD_PROPERTIES);
}
GroupBuilder srcIncludesSource = null;
if (!srcIncludes.isEmpty()) {
cnt = 0;
for (String token : srcIncludes) {
IPath srcInclude = new Path(token);
if (srcIncludesSource == null)
srcIncludesSource = cspec.addGroup(ATTRIBUTE_SRC_INCLUDES, false);
IPath biPath = resolveLink(srcInclude, projectRootReplacement);
ArtifactBuilder ab = cspec.addArtifact(ATTRIBUTE_SRC_INCLUDES + '_' + cnt++, false, projectRootReplacement[0]);
ab.addPath(biPath);
srcIncludesSource.addLocalPrerequisite(ab);
}
}
if (buildSource != null) {
if (srcIncludesSource == null)
srcIncludesSource = cspec.addGroup(IBuildEntry.SRC_INCLUDES, false);
srcIncludesSource.addLocalPrerequisite(buildSource);
}
if (simpleBundle && !binIncludes.isEmpty()) {
// These products from the eclipse.build will contain the .class
// files for the bundle
//
String excludesStr = null;
IBuildEntry excludes = build.getEntry(PROPERTY_EXCLUDE_PREFIX + '.');
if (excludes != null)
excludesStr = TextUtils.concat(excludes.getTokens(), ","); //$NON-NLS-1$
for (AttributeBuilder product : eclipseBuildProducts.values()) {
if (excludesStr == null)
jarContents.addLocalPrerequisite(product);
else {
String pruneName = product.getName() + ".pruned"; //$NON-NLS-1$
ActionBuilder prune = cspec.addAction(pruneName, false, CopyActor.ID, false);
prune.addLocalPrerequisite(product);
prune.addProperty(CopyActor.PROP_EXCLUDES, excludesStr, false);
prune.setProductBase(OUTPUT_DIR_TEMP.append(pruneName));
jarContents.addLocalPrerequisite(prune);
}
}
}
ActionBuilder buildPlugin;
String jarName = plugin.getId() + '_' + plugin.getVersion() + ".jar"; //$NON-NLS-1$
IPath jarPath = Path.fromPortableString(jarName);
if (getReader().isFileSystemReader()
&& (getReader().exists(jarName, new NullProgressMonitor()) || getLinkDescriptions().containsKey(jarPath))) {
buildPlugin = addAntAction(ATTRIBUTE_BUNDLE_JAR, TASK_COPY_GROUP, true);
buildPlugin.setPrerequisitesAlias(ALIAS_REQUIREMENTS);
IPath resolvedJarPath = resolveLink(jarPath, projectRootReplacement);
ArtifactBuilder importedJar = cspec.addArtifact(ATTRIBUTE_IMPORTED_JAR, false, projectRootReplacement[0]);
importedJar.addPath(resolvedJarPath);
buildPlugin.getPrerequisitesBuilder().addLocalPrerequisite(importedJar);
} else {
buildPlugin = addAntAction(ATTRIBUTE_BUNDLE_JAR, TASK_CREATE_BUNDLE_JAR, true);
buildPlugin.addLocalPrerequisite(manifest.getName(), ALIAS_MANIFEST);
buildPlugin.addLocalPrerequisite(jarContents.getName(), ALIAS_REQUIREMENTS);
}
addGenerateApiDescription(build);
buildPlugin.setProductAlias(ALIAS_OUTPUT);
buildPlugin.setProductBase(OUTPUT_DIR_JAR);
buildPlugin.setUpToDatePolicy(UpToDatePolicy.COUNT);
buildPlugin.setProductFileCount(1);
GroupBuilder bundleAndFragments = cspec.addGroup(ATTRIBUTE_BUNDLE_AND_FRAGMENTS, true);
IPluginModelBase model = plugin.getPluginModel();
if (model instanceof IFragmentModel)
addBundleHostDependency((IFragmentModel) model);
else {
ActionBuilder copyTargetFragments = cspec.addAction(ATTRIBUTE_TARGET_FRAGMENTS, false, ACTOR_COPY_TARGET_FRAGMENTS, false);
copyTargetFragments.setProductAlias(ALIAS_OUTPUT);
copyTargetFragments.setProductBase(OUTPUT_DIR_FRAGMENTS);
copyTargetFragments.addLocalPrerequisite(getAttributeEclipseBuild());
copyTargetFragments.setUpToDatePolicy(UpToDatePolicy.ACTOR);
copyTargetFragments.addProperty(FragmentsActor.PROP_FRAGMENT_ATTRIBUTE, ATTRIBUTE_BUNDLE_JAR, false);
bundleAndFragments.addLocalPrerequisite(copyTargetFragments);
}
bundleAndFragments.addLocalPrerequisite(buildPlugin);
bundleJars.addLocalPrerequisite(bundleAndFragments);
GroupBuilder bundleAndFragmentsSource = cspec.addGroup(ATTRIBUTE_BUNDLE_AND_FRAGMENTS_SOURCE, true);
if (!(model instanceof IFragmentModel)) {
ActionBuilder copyTargetFragmentsSource = cspec.addAction(ATTRIBUTE_TARGET_FRAGMENTS_SOURCE, false, ACTOR_COPY_TARGET_FRAGMENTS, false);
copyTargetFragmentsSource.setProductAlias(ALIAS_OUTPUT);
copyTargetFragmentsSource.setProductBase(OUTPUT_DIR_FRAGMENTS_SOURCE);
copyTargetFragmentsSource.setUpToDatePolicy(UpToDatePolicy.ACTOR);
copyTargetFragmentsSource.addProperty(FragmentsActor.PROP_FRAGMENT_ATTRIBUTE, ATTRIBUTE_SOURCE_BUNDLE_JAR, false);
bundleAndFragmentsSource.addLocalPrerequisite(copyTargetFragmentsSource);
}
if (srcIncludesSource != null) {
// Add Actions to create a source bundle jar
// this is for the source manifest
ActionBuilder sourceManifestAction = addAntAction(ATTRIBUTE_SOURCE_MANIFEST, TASK_CREATE_SOURCE_MANIFEST, false);
sourceManifestAction.addLocalPrerequisite(ATTRIBUTE_MANIFEST, ALIAS_MANIFEST);
sourceManifestAction.addLocalPrerequisite(ATTRIBUTE_BUILD_PROPERTIES, ALIAS_PROPERTIES);
sourceManifestAction.setProductAlias(ALIAS_OUTPUT);
sourceManifestAction.setProductBase(OUTPUT_DIR_TEMP);
IPath sourceManifestPath = new Path(SOURCE_MANIFEST);
sourceManifestAction.addProductPath(sourceManifestPath);
// this is for the source bundle
ActionBuilder sourceBundleAction = addAntAction(ATTRIBUTE_SOURCE_BUNDLE_JAR, TASK_CREATE_BUNDLE_JAR, true);
sourceBundleAction.addLocalPrerequisite(IBuildEntry.SRC_INCLUDES, ALIAS_REQUIREMENTS);
sourceBundleAction.addLocalPrerequisite(ATTRIBUTE_SOURCE_MANIFEST, ALIAS_MANIFEST);
sourceBundleAction.setProductAlias(ALIAS_OUTPUT);
sourceBundleAction.setProductBase(OUTPUT_DIR_SOURCE_JAR);
bundleAndFragmentsSource.addLocalPrerequisite(sourceBundleAction);
GeneratorBuilder genBld = cspec.createGeneratorBuilder();
genBld.setAttribute(ATTRIBUTE_SOURCE_BUNDLE_JAR);
genBld.setGeneratesType(IComponentType.OSGI_BUNDLE);
genBld.setName(cspec.getName() + ".source"); //$NON-NLS-1$
cspec.addGenerator(genBld);
} else {
// Add an empty group so that it can be referenced
cspec.addGroup(ATTRIBUTE_SOURCE_BUNDLE_JAR, true);
}
addProducts(MonitorUtils.subMonitor(monitor, 20));
monitor.done();
}
protected AttributeBuilder addAboutMappingsAction(IPath rawAboutMappingPath, IPath projectRoot) throws CoreException {
ArtifactBuilder rawAboutMappings = getCSpec().addArtifact(ATTRIBUTE_RAW_ABOUT_MAPPINGS, false, projectRoot);
rawAboutMappings.addPath(rawAboutMappingPath);
ActionBuilder mappingAction = addAntAction(ACTION_ABOUT_MAPPINGS, TASK_REPLACE_TWO_TOKENS, false);
mappingAction.addProperty("token1", "@build@", false); //$NON-NLS-1$ //$NON-NLS-2$
mappingAction.addProperty("value1", "${build.id}", false); //$NON-NLS-1$ //$NON-NLS-2$
mappingAction.addProperty("token2", "@version@", false); //$NON-NLS-1$ //$NON-NLS-2$
mappingAction.addProperty("value2", "${" + getCSpec().getName() + ".unqualified.owner.version}", false); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
mappingAction.addLocalPrerequisite(ATTRIBUTE_RAW_ABOUT_MAPPINGS, "action.input"); //$NON-NLS-1$
mappingAction.addLocalPrerequisite(getCSpec().addAction("ownerVersion", false, "ownerVersionExtractor", false)); //$NON-NLS-1$//$NON-NLS-2$
mappingAction.setProductAlias(ALIAS_OUTPUT);
mappingAction.setProductBase(OUTPUT_DIR_TEMP);
mappingAction.addProductPath(Path.fromPortableString(ABOUT_MAPPINGS_FILE));
return mappingAction;
}
protected void addGenerateApiDescription(IBuild build) throws CoreException {
IBuildEntry apiDescription = build.getEntry("generateAPIDescription"); //$NON-NLS-1$
if (apiDescription == null)
return;
String[] tokens = apiDescription.getTokens();
if (tokens.length != 1 || !Boolean.parseBoolean(tokens[0]))
return;
CSpecBuilder cspec = getCSpec();
GroupBuilder jarContentsOld = getAttributeJarContents();
cspec.removeAttribute(ATTRIBUTE_JAR_CONTENTS);
jarContentsOld.setName(ATTRIBUTE_JAR_CONTENTS + ".wo_apidesc"); //$NON-NLS-1$
cspec.addAttribute(jarContentsOld);
ActionBuilder apiGeneration = addAntAction(ACTION_GENERATE_API_DESCRIPTION, TASK_API_GENERATION, true);
apiGeneration.addLocalPrerequisite(ATTRIBUTE_MANIFEST, ALIAS_MANIFEST);
apiGeneration.addLocalPrerequisite(jarContentsOld.getName(), ALIAS_BINARY);
apiGeneration.addLocalPrerequisite(ATTRIBUTE_ECLIPSE_BUILD_SOURCE, ALIAS_SOURCE);
apiGeneration.setProductAlias(ALIAS_OUTPUT);
apiGeneration.setProductBase(OUTPUT_DIR.append("api_description")); //$NON-NLS-1$
apiGeneration.addProductPath(Path.fromPortableString(".api_description")); //$NON-NLS-1$
GroupBuilder jarContents = getAttributeJarContents();
jarContents.addLocalPrerequisite(jarContentsOld);
jarContents.addLocalPrerequisite(apiGeneration);
}
protected void addImports() throws CoreException {
ImportSpecification[] imports = getImports(plugin);
if (imports.length == 0)
return;
ComponentQuery query = getReader().getNodeQuery().getComponentQuery();
CSpecBuilder cspec = getCSpec();
GroupBuilder fullClean = cspec.getRequiredGroup(ATTRIBUTE_FULL_CLEAN);
GroupBuilder reExports = cspec.getRequiredGroup(ATTRIBUTE_JAVA_BINARIES);
GroupBuilder bundleJars = cspec.getRequiredGroup(ATTRIBUTE_BUNDLE_JARS);
GroupBuilder productConfigExports = cspec.getRequiredGroup(ATTRIBUTE_PRODUCT_CONFIG_EXPORTS);
for (ImportSpecification pluginImport : imports) {
String pluginId = pluginImport.getName();
if (pluginId.equals(Constants.SYSTEM_BUNDLE_SYMBOLICNAME))
continue;
ComponentRequestBuilder dependency = createDependency(pluginImport, IComponentType.OSGI_BUNDLE);
if (skipComponent(query, dependency) || !addDependency(dependency))
continue;
addExternalPrerequisite(fullClean, dependency, ATTRIBUTE_FULL_CLEAN);
addExternalPrerequisite(bundleJars, dependency, ATTRIBUTE_BUNDLE_JARS);
addExternalPrerequisite(getAttributeBuildRequirements(), dependency, ATTRIBUTE_JAVA_BINARIES);
if (pluginImport.isExported())
addExternalPrerequisite(reExports, dependency, ATTRIBUTE_JAVA_BINARIES);
addExternalPrerequisite(productConfigExports, dependency, ATTRIBUTE_PRODUCT_CONFIG_EXPORTS);
}
}
@Override
protected String getProductOutputFolder(String productId) {
IBuildEntry entry = buildModel.getBuild().getEntry(productId + TOP_FOLDER_SUFFIX);
if (entry != null) {
String[] tokens = entry.getTokens();
if (tokens.length == 1)
return tokens[0];
}
return null;
}
@Override
protected String getPropertyFileName() {
return PLUGIN_PROPERTIES_FILE;
}
private IPath asProjectRelativeFolder(IPath classpathEntryPath, IPath[] projectRootReplacement) {
return resolveLink(classpathEntryPath.removeFirstSegments(1).addTrailingSeparator(), projectRootReplacement);
}
private IPath createJarAction(String jarName, IClasspathEntry[] classPath, IBuild build, Map<IPath, IPath> outputMap,
Map<IPath, AttributeBuilder> eclipseBuildProducts) throws CoreException {
CSpecBuilder cspec = getCSpec();
IPath jarPath = new Path(jarName);
String jarFlatName = pathToName(jarPath);
ActionBuilder action = addAntAction(PREFIX_CREATE_JAR + jarFlatName, TASK_CREATE_JAR, false);
action.setProductBase(OUTPUT_DIR_TEMP);
action.addProductPath(jarPath);
action.setProductAlias(ALIAS_OUTPUT);
action.setPrerequisitesAlias(ALIAS_REQUIREMENTS);
getAttributeJarContents().addLocalPrerequisite(action);
// Check if the source for this jar is included as a source in the
// development classpath. If it is, then assume that we can use
// the eclipse.build to produce the needed .class files.
//
IPath[][] missingEntriesRet = new IPath[1][];
IClasspathEntry[] srcEntries = getSourceEntries(classPath, jarName, build, missingEntriesRet);
IPath[] missingEntries = missingEntriesRet[0];
if (missingEntries.length > 0) {
// We have sources that are not input to the eclipse.build. We need
// some custom action here in order to deal with them.
// TODO: investigate
ArtifactBuilder rougeSources = cspec.addArtifact(PREFIX_ROUGE_SOURCE + jarFlatName, false, null);
for (IPath notFound : missingEntries)
rougeSources.addPath(notFound);
action.addLocalPrerequisite(rougeSources);
}
// Remaining sources corresponds to IClasspathEntries in the development
// classpath so let's trust the output from the eclipse.build
//
IPath[] projectRootReplacement = new IPath[1];
IPath defaultOutputLocation = getDefaultOutputLocation(classPath, projectRootReplacement);
for (IClasspathEntry cpe : srcEntries) {
IPath output = cpe.getOutputLocation();
if (output == null) {
output = defaultOutputLocation;
if (output == null)
continue;
} else
output = asProjectRelativeFolder(output, projectRootReplacement);
// Several source entries might share the same output folder
//
String artifactName = getArtifactName(output);
IPath targetOutput = outputMap.get(output);
if (targetOutput != null) {
IBuildEntry excludes = build.getEntry(PROPERTY_EXCLUDE_PREFIX + targetOutput.toPortableString());
if (excludes != null) {
String pruneName = artifactName + ".pruned"; //$NON-NLS-1$
if (cspec.getAttribute(pruneName) != null)
continue;
String excludesString = TextUtils.concat(excludes.getTokens(), ","); //$NON-NLS-1$
ActionBuilder prune = cspec.addAction(pruneName, false, CopyActor.ID, false);
prune.addProperty(CopyActor.PROP_EXCLUDES, excludesString, false);
prune.addLocalPrerequisite(artifactName);
prune.setProductBase(OUTPUT_DIR_TEMP.append(pruneName));
action.addLocalPrerequisite(pruneName);
if (eclipseBuildProducts != null)
eclipseBuildProducts.remove(output);
continue;
}
}
if (action.getPrerequisite(artifactName) == null) {
action.addLocalPrerequisite(artifactName);
if (eclipseBuildProducts != null)
eclipseBuildProducts.remove(output);
}
}
return jarPath;
}
private GroupBuilder getAttributeBuildRequirements() throws CoreException {
CSpecBuilder cspec = getCSpec();
GroupBuilder eclipseBuildReqs = cspec.getGroup(ATTRIBUTE_ECLIPSE_BUILD_REQUIREMENTS);
if (eclipseBuildReqs == null) {
eclipseBuildReqs = cspec.addGroup(ATTRIBUTE_ECLIPSE_BUILD_REQUIREMENTS, false);
getAttributeEclipseBuild().addLocalPrerequisite(eclipseBuildReqs);
}
return eclipseBuildReqs;
}
private ActionBuilder getAttributeEclipseBuild() throws CoreException {
CSpecBuilder cspec = getCSpec();
ActionBuilder eclipseBuild = cspec.getActionBuilder(ATTRIBUTE_ECLIPSE_BUILD);
if (eclipseBuild == null)
eclipseBuild = cspec.addInternalAction(ATTRIBUTE_ECLIPSE_BUILD, false);
return eclipseBuild;
}
private GroupBuilder getAttributeJarContents() throws CoreException {
CSpecBuilder cspec = getCSpec();
GroupBuilder jarContent = cspec.getGroup(ATTRIBUTE_JAR_CONTENTS);
if (jarContent == null)
jarContent = cspec.addGroup(ATTRIBUTE_JAR_CONTENTS, false);
return jarContent;
}
private IPath getDefaultOutputLocation(IClasspathEntry[] classPath, IPath[] projectRootReplacement) {
for (IClasspathEntry cpe : classPath) {
if (cpe.getContentKind() == ClasspathEntry.K_OUTPUT)
return asProjectRelativeFolder(cpe.getPath(), projectRootReplacement);
}
return null;
}
private GroupBuilder getGroupEclipseBuildSource(boolean createIfMissing) throws CoreException {
CSpecBuilder cspec = getCSpec();
GroupBuilder buildSource = cspec.getGroup(ATTRIBUTE_ECLIPSE_BUILD_SOURCE);
if (buildSource == null && createIfMissing) {
buildSource = cspec.addGroup(ATTRIBUTE_ECLIPSE_BUILD_SOURCE, true);
getAttributeEclipseBuild().addLocalPrerequisite(ATTRIBUTE_ECLIPSE_BUILD_SOURCE);
}
return buildSource;
}
private GroupBuilder getGroupExtraJars() throws CoreException {
CSpecBuilder cspec = getCSpec();
GroupBuilder extraJars = cspec.getGroup(ATTRIBUTE_BUNDLE_EXTRAJARS);
if (extraJars == null) {
extraJars = cspec.addGroup(ATTRIBUTE_BUNDLE_EXTRAJARS, false);
cspec.getRequiredGroup(ATTRIBUTE_JAVA_BINARIES).addLocalPrerequisite(ATTRIBUTE_BUNDLE_EXTRAJARS);
getAttributeBuildRequirements().addLocalPrerequisite(ATTRIBUTE_BUNDLE_EXTRAJARS);
}
return extraJars;
}
private Map<IPath, LinkDescription> getLinkDescriptions() {
Map<IPath, LinkDescription> linkDescriptors = null;
if (projectDesc instanceof ProjectDescription)
linkDescriptors = ((ProjectDescription) projectDesc).getLinks();
if (linkDescriptors == null)
linkDescriptors = Collections.emptyMap();
return linkDescriptors;
}
private IClasspathEntry[] getSourceEntries(IClasspathEntry[] classPath, String jarName, IBuild build, IPath[][] notFound) {
IBuildEntry srcIncl = build.getEntry(IBuildEntry.JAR_PREFIX + jarName);
if (srcIncl == null) {
notFound[0] = Trivial.EMPTY_PATH_ARRAY;
return emptyClasspath;
}
List<IClasspathEntry> cpEntries = null;
List<IPath> missingEntries = null;
if (classPath == null) {
for (String src : srcIncl.getTokens()) {
if (missingEntries == null)
missingEntries = new ArrayList<IPath>();
missingEntries.add(resolveLink(Path.fromPortableString(src).addTrailingSeparator(), null));
}
} else {
for (String src : srcIncl.getTokens()) {
boolean found = false;
IPath srcPath = resolveLink(Path.fromPortableString(src), null).addTrailingSeparator();
for (IClasspathEntry ce : classPath) {
if (ce.getEntryKind() != IClasspathEntry.CPE_SOURCE)
continue;
IPath cePath = asProjectRelativeFolder(ce.getPath(), null);
if (cePath.equals(srcPath)) {
found = true;
if (cpEntries == null)
cpEntries = new ArrayList<IClasspathEntry>();
cpEntries.add(ce);
break;
}
}
if (!found) {
if (missingEntries == null)
missingEntries = new ArrayList<IPath>();
missingEntries.add(srcPath);
}
}
}
notFound[0] = missingEntries == null ? Trivial.EMPTY_PATH_ARRAY : missingEntries.toArray(new IPath[missingEntries.size()]);
return cpEntries == null ? emptyClasspath : cpEntries.toArray(new IClasspathEntry[cpEntries.size()]);
}
private AttributeBuilder normalizeGroup(GroupBuilder bld) throws CoreException {
if (bld == null)
return null;
List<PrerequisiteBuilder> preqs = bld.getPrerequisites();
if (preqs.size() == 0)
return null;
boolean singleCanReplace = true;
CSpecBuilder cspec = getCSpec();
HashMap<IPath, ArtifactBuilder> byBase = new HashMap<IPath, ArtifactBuilder>();
for (PrerequisiteBuilder pq : new ArrayList<PrerequisiteBuilder>(preqs)) {
if (pq.getComponentName() != null) {
singleCanReplace = false;
continue;
}
AttributeBuilder ab = cspec.getAttribute(pq.getName());
if (!(ab instanceof ArtifactBuilder)) {
singleCanReplace = false;
continue;
}
ArtifactBuilder arb = (ArtifactBuilder) ab;
ArtifactBuilder prev = byBase.get(arb.getBase());
if (prev == null) {
byBase.put(arb.getBase(), arb);
continue;
}
for (IPath path : arb.getPaths())
prev.addPath(path);
bld.removePrerequisite(pq.toString());
cspec.removeAttribute(arb.getName());
}
if (singleCanReplace && byBase.size() == 1) {
ArtifactBuilder ab = byBase.values().iterator().next();
String name = bld.getName();
cspec.removeAttribute(name);
cspec.removeAttribute(ab.getName());
ab.setName(name);
getCSpec().addAttribute(ab);
return ab;
}
return bld;
}
private IPath resolveLink(IPath path, IPath[] projectRootReplacement) {
if (projectRootReplacement != null)
projectRootReplacement[0] = null;
if (path == null || path.isAbsolute() || path.isEmpty())
return path;
for (Map.Entry<IPath, LinkDescription> entry : getLinkDescriptions().entrySet()) {
IPath linkSource = entry.getKey();
if (linkSource.isPrefixOf(path)) {
URI locationURI = entry.getValue().getLocationURI();
/*
* Explicitly resolve relative path via workspace linked
* resources
*/
URI resolvedURI = ResourcesPlugin.getWorkspace().getPathVariableManager().resolveURI(locationURI);
IPath linkTarget = FileUtil.toPath(resolvedURI);
int sourceSegs = linkSource.segmentCount();
if (projectRootReplacement != null) {
if (linkTarget.setDevice(null).removeFirstSegments(linkTarget.segmentCount() - sourceSegs).equals(linkSource)) {
projectRootReplacement[0] = linkTarget.removeLastSegments(sourceSegs);
break;
}
}
if (sourceSegs == path.segmentCount()) {
if (path.hasTrailingSeparator())
path = linkTarget.addTrailingSeparator();
else
path = linkTarget;
} else
path = linkTarget.append(path.removeFirstSegments(sourceSegs));
break;
}
}
return path;
}
}