/*****************************************************************************
* 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;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.types.selectors.SelectorUtils;
import org.eclipse.buckminster.ant.actor.AntActor;
import org.eclipse.buckminster.core.RMContext;
import org.eclipse.buckminster.core.TargetPlatform;
import org.eclipse.buckminster.core.common.model.ExpandingProperties;
import org.eclipse.buckminster.core.cspec.IComponentRequest;
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.GroupBuilder;
import org.eclipse.buckminster.core.cspec.builder.PrerequisiteBuilder;
import org.eclipse.buckminster.core.cspec.model.ComponentName;
import org.eclipse.buckminster.core.cspec.model.ComponentRequest;
import org.eclipse.buckminster.core.cspec.model.UpToDatePolicy;
import org.eclipse.buckminster.core.ctype.IComponentType;
import org.eclipse.buckminster.core.helpers.FileHandle;
import org.eclipse.buckminster.core.helpers.FilterUtils;
import org.eclipse.buckminster.core.helpers.PropertiesParser;
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.resolver.NodeQuery;
import org.eclipse.buckminster.core.version.VersionHelper;
import org.eclipse.buckminster.jarprocessor.JarProcessorActor;
import org.eclipse.buckminster.osgi.filter.Filter;
import org.eclipse.buckminster.osgi.filter.FilterFactory;
import org.eclipse.buckminster.pde.IPDEConstants;
import org.eclipse.buckminster.pde.MatchRule;
import org.eclipse.buckminster.pde.Messages;
import org.eclipse.buckminster.pde.PDEPlugin;
import org.eclipse.buckminster.pde.internal.actor.P2SiteGenerator;
import org.eclipse.buckminster.runtime.BuckminsterException;
import org.eclipse.buckminster.runtime.MonitorUtils;
import org.eclipse.buckminster.runtime.Trivial;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.equinox.internal.p2.core.helpers.StringHelper;
import org.eclipse.equinox.internal.p2.metadata.VersionFormat;
import org.eclipse.equinox.internal.p2.publisher.eclipse.IProductDescriptor;
import org.eclipse.equinox.internal.p2.publisher.eclipse.ProductFile;
import org.eclipse.equinox.p2.metadata.IVersionedId;
import org.eclipse.equinox.p2.metadata.Version;
import org.eclipse.equinox.p2.metadata.VersionRange;
import org.eclipse.osgi.service.resolver.BundleDescription;
import org.eclipse.osgi.service.resolver.BundleSpecification;
import org.eclipse.osgi.util.ManifestElement;
import org.eclipse.osgi.util.NLS;
import org.eclipse.pde.core.build.IBuild;
import org.eclipse.pde.core.build.IBuildEntry;
import org.eclipse.pde.core.plugin.IFragment;
import org.eclipse.pde.core.plugin.IFragmentModel;
import org.eclipse.pde.core.plugin.IMatchRules;
import org.eclipse.pde.core.plugin.IPluginBase;
import org.eclipse.pde.core.plugin.IPluginModelBase;
import org.eclipse.pde.internal.build.IBuildPropertiesConstants;
import org.eclipse.pde.internal.build.IXMLConstants;
import org.eclipse.pde.internal.core.ICoreConstants;
import org.eclipse.pde.internal.core.ibundle.IBundleModel;
import org.eclipse.pde.internal.core.ibundle.IBundlePluginModelBase;
import org.eclipse.pde.internal.core.text.bundle.ManifestHeader;
import org.eclipse.pde.internal.core.text.bundle.RequireBundleObject;
import org.osgi.framework.BundleException;
import org.osgi.framework.Constants;
import org.osgi.framework.InvalidSyntaxException;
/**
* @author Thomas Hallgren
*
*/
@SuppressWarnings("restriction")
public abstract class CSpecGenerator implements IBuildPropertiesConstants, IPDEConstants, IXMLConstants {
public static class ImportSpecification {
private final String name;
private final VersionRange range;
private final boolean exported;
private final boolean optional;
ImportSpecification(BundleSpecification bundleSpec) {
name = bundleSpec.getName();
org.eclipse.osgi.service.resolver.VersionRange osgiRange = bundleSpec.getVersionRange();
range = osgiRange == null ? null : new VersionRange(osgiRange.toString());
exported = bundleSpec.isExported();
optional = bundleSpec.isOptional();
}
ImportSpecification(String name, VersionRange range, boolean exported, boolean optional) {
this.name = name;
this.range = range;
this.exported = exported;
this.optional = optional;
}
public String getName() {
return name;
}
public VersionRange getVersionRange() {
return range;
}
public boolean isExported() {
return exported;
}
public boolean isOptional() {
return optional;
}
}
public static final IPath OUTPUT_DIR_JAR = OUTPUT_DIR.append("jar"); //$NON-NLS-1$
public static final IPath OUTPUT_DIR_SOURCE_JAR = OUTPUT_DIR.append("source.jar"); //$NON-NLS-1$
public static final IPath OUTPUT_DIR_FRAGMENTS = OUTPUT_DIR.append("fragments"); //$NON-NLS-1$
public static final IPath OUTPUT_DIR_FRAGMENTS_SOURCE = OUTPUT_DIR.append("fragments.source"); //$NON-NLS-1$
public static final IPath OUTPUT_DIR_SITE = OUTPUT_DIR.append("site"); //$NON-NLS-1$
public static final IPath OUTPUT_DIR_SITE_REPACKED = OUTPUT_DIR.append("site.repacked"); //$NON-NLS-1$
public static final IPath OUTPUT_DIR_SITE_PACKED = OUTPUT_DIR.append("site.packed"); //$NON-NLS-1$
public static final IPath OUTPUT_DIR_SITE_SIGNED = OUTPUT_DIR.append("site.signed"); //$NON-NLS-1$
public static final IPath OUTPUT_DIR_TEMP = OUTPUT_DIR.append("temp"); //$NON-NLS-1$
public static final String LAUNCHER_BUNDLE = "org.eclipse.equinox.launcher"; //$NON-NLS-1$
public static final String LAUNCHER_FEATURE_3_2 = "org.eclipse.platform.launchers"; //$NON-NLS-1$
public static final String LAUNCHER_FEATURE = "org.eclipse.equinox.executable"; //$NON-NLS-1$
public static final String RCP_FEATURE = "org.eclipse.rcp"; //$NON-NLS-1$
public static final Filter SOURCE_FILTER;
public static final Filter SIGNING_ENABLED;
public static final Filter SIGNING_DISABLED;
public static final Filter PACK_ENABLED;
public static final Filter PACK_DISABLED;
public static final Filter SIGNING_AND_PACK_DISABLED;
public static final Filter SIGNING_ENABLED_AND_PACK_DISABLED;
public static final Filter INCLUDE_TOP_FILTER;
public static final Filter INCLUDE_TOP_SOURCE_FILTER;
static {
try {
SOURCE_FILTER = FilterFactory.newInstance("(!(cbi.include.source=false))"); //$NON-NLS-1$
INCLUDE_TOP_FILTER = FilterFactory.newInstance("(site.include.top=true)"); //$NON-NLS-1$
INCLUDE_TOP_SOURCE_FILTER = FilterFactory.newInstance("(&(site.include.top=true)(!(cbi.include.source=false)))"); //$NON-NLS-1$
SIGNING_ENABLED = FilterFactory.newInstance("(site.signing=true)"); //$NON-NLS-1$
SIGNING_DISABLED = FilterFactory.newInstance("(!(site.signing=true))"); //$NON-NLS-1$
PACK_ENABLED = FilterFactory.newInstance("(site.pack200=true)"); //$NON-NLS-1$
PACK_DISABLED = FilterFactory.newInstance("(!(site.pack200=true))"); //$NON-NLS-1$
SIGNING_AND_PACK_DISABLED = FilterFactory.newInstance("(&(!(site.pack200=true))(!(site.signing=true)))"); //$NON-NLS-1$
SIGNING_ENABLED_AND_PACK_DISABLED = FilterFactory.newInstance("(&(!(site.pack200=true))(site.signing=true))"); //$NON-NLS-1$
} catch (InvalidSyntaxException e) {
throw new ExceptionInInitializerError(e);
}
}
private static final ImportSpecification[] noRequiredBundles = new ImportSpecification[0];
/**
* Create a version range based on a PDE <code>matchRule</code> and a
* <code>version</code>.
*
* @param matchRule
* The rule. Must be one declared in {@link IMatchRules}
* @param retainLowerBound
* Retain the exact lower bound. Implied for rule
* {@link IMatchRules#PERFECT}.
* @param version
* The version
* @return A version range.
*/
public static VersionRange createRuleBasedRange(MatchRule matchRule, MatchRule retainLowerBound, Version version) {
if (version == null || Version.emptyVersion.equals(version))
return VersionRange.emptyRange;
boolean qualifierTag = "qualifier".equals(VersionHelper.getQualifier(version)); //$NON-NLS-1$
Version lower = version;
if (qualifierTag)
lower = VersionHelper.replaceQualifier(version, null);
Version upper = limitUpperWithMatchRule(version, matchRule, qualifierTag);
if (matchRule == MatchRule.PERFECT || matchRule == MatchRule.NONE)
return new VersionRange(lower, true, upper, !qualifierTag);
switch (retainLowerBound) {
case PERFECT:
break;
case UNQUALIFIED:
case EQUIVALENT:
lower = limitLowerWithMatchRule(version, retainLowerBound);
break;
default:
lower = limitLowerWithMatchRule(version, MatchRule.COMPATIBLE);
}
return new VersionRange(lower, true, upper, matchRule == MatchRule.GREATER_OR_EQUAL);
}
public static Version limitLowerWithMatchRule(Version v, MatchRule matchRule) {
if (v == null || matchRule == MatchRule.NONE || matchRule == MatchRule.PERFECT)
return v;
org.osgi.framework.Version ov = new org.osgi.framework.Version(v.toString());
switch (matchRule) {
case UNQUALIFIED:
v = Version.createOSGi(ov.getMajor(), ov.getMinor(), ov.getMicro());
break;
case EQUIVALENT:
v = Version.createOSGi(ov.getMajor(), ov.getMinor(), 0);
break;
case COMPATIBLE:
v = Version.createOSGi(ov.getMajor(), 0, 0);
break;
default:
v = Version.createOSGi(ov.getMajor(), ov.getMinor(), ov.getMicro());
}
return v;
}
public static Version limitUpperWithMatchRule(Version v, MatchRule matchRule, boolean qualifierTag) {
org.osgi.framework.Version ov = new org.osgi.framework.Version(v.toString());
switch (matchRule) {
case UNQUALIFIED:
v = Version.createOSGi(ov.getMajor(), ov.getMinor(), ov.getMicro() + 1);
break;
case EQUIVALENT:
v = Version.createOSGi(ov.getMajor(), ov.getMinor() + 1, 0);
break;
case COMPATIBLE:
v = Version.createOSGi(ov.getMajor() + 1, 0, 0);
break;
case NONE:
case PERFECT:
if (qualifierTag)
// A non yet expanded qualifier was encountered.
v = Version.createOSGi(ov.getMajor(), ov.getMinor(), ov.getMicro() + 1);
break;
default:
v = Version.MAX_VERSION;
}
return v;
}
protected static String buildArtifactName(String id, String ver, boolean asJar) {
StringBuilder bld = new StringBuilder();
bld.append(id);
if (ver != null) {
bld.append('_');
bld.append(ver);
}
if (asJar)
bld.append(".jar"); //$NON-NLS-1$
else
bld.append('/');
return bld.toString();
}
private static Pattern convertIncludeToPattern(String include) {
int len = include.length();
StringBuilder bld = new StringBuilder(len + 4);
for (int idx = 0; idx < len; ++idx) {
char c = include.charAt(idx);
switch (c) {
case '?':
bld.append('.');
break;
case '*':
bld.append('.');
bld.append('*');
break;
case '.':
bld.append('\\');
bld.append('.');
default:
bld.append(c);
}
}
return Pattern.compile(bld.toString());
}
protected static ImportSpecification[] getImports(IPluginBase plugin) throws CoreException {
IPluginModelBase model = plugin.getPluginModel();
BundleDescription bundleDesc = model.getBundleDescription();
BundleSpecification[] imports;
if (bundleDesc != null) {
imports = bundleDesc.getRequiredBundles();
if (imports == null)
return noRequiredBundles;
int sz = imports.length;
if (sz == 0)
return noRequiredBundles;
ImportSpecification[] importSpecs = new ImportSpecification[sz];
while (--sz >= 0)
importSpecs[sz] = new ImportSpecification(imports[sz]);
return importSpecs;
}
if (!(model instanceof IBundlePluginModelBase))
return noRequiredBundles;
IBundleModel bundleModel = ((IBundlePluginModelBase) model).getBundleModel();
ManifestHeader header = (ManifestHeader) bundleModel.getBundle().getManifestHeader(Constants.REQUIRE_BUNDLE);
if (header == null)
return noRequiredBundles;
ManifestElement[] elems = null;
try {
elems = ManifestElement.parseHeader(header.getKey(), header.getValue());
} catch (BundleException e) {
}
if (elems == null)
return noRequiredBundles;
int sz = elems.length;
if (sz == 0)
return noRequiredBundles;
ImportSpecification[] importSpecs = new ImportSpecification[sz];
while (--sz >= 0) {
RequireBundleObject r = new RequireBundleObject(header, elems[sz]);
importSpecs[sz] = new ImportSpecification(r.getId(), new VersionRange(r.getVersion()), r.isReexported(), r.isOptional());
}
return importSpecs;
}
private final CSpecBuilder cspecBuilder;
private final ICatalogReader reader;
private Map<String, String> properties;
protected CSpecGenerator(CSpecBuilder cspecBuilder, ICatalogReader reader) {
this.cspecBuilder = cspecBuilder;
this.reader = reader;
}
public VersionRange convertMatchRule(MatchRule pdeMatchRule, String version) throws CoreException {
version = Trivial.trim(version);
if (version == null || version.equals("0.0.0")) //$NON-NLS-1$
return null;
char c = version.charAt(0);
if (c == '[' || c == '(')
//
// Already an OSGi range, just ignore the rule then.
//
return new VersionRange(version);
return createRuleBasedRange(pdeMatchRule, MatchRule.NONE, Version.parseVersion(version));
}
public abstract void generate(IProgressMonitor monitor) throws CoreException;
public CSpecBuilder getCSpec() {
return cspecBuilder;
}
public ICatalogReader getReader() {
return reader;
}
protected ActionBuilder addAntAction(String actionName, String targetName, boolean asPublic) throws CoreException {
ActionBuilder action = cspecBuilder.addAction(actionName, asPublic, AntActor.ACTOR_ID, false);
action.addActorProperty(AntActor.PROP_TARGETS, targetName, false);
action.addActorProperty(AntActor.PROP_BUILD_FILE_ID, BUILD_FILE_ID, false);
return action;
}
protected void addBundleHostDependency(IFragmentModel fragmentModel) throws CoreException {
IFragment fragment = fragmentModel.getFragment();
ComponentRequestBuilder bundleHostDep = cspecBuilder.createDependencyBuilder();
bundleHostDep.setName(fragment.getPluginId());
bundleHostDep.setVersionRange(VersionHelper.createRange(VersionFormat.OSGI_FORMAT, fragment.getPluginVersion()));
bundleHostDep.setComponentTypeID(IComponentType.OSGI_BUNDLE);
try {
bundleHostDep.setFilter(FilterFactory.newInstance("(bundleHost=true)")); //$NON-NLS-1$
} catch (InvalidSyntaxException e) {
// This won't happen on that particular filter
}
addDependency(bundleHostDep);
}
protected boolean addDependency(ComponentRequestBuilder dependency) throws CoreException {
return cspecBuilder.addDependency(dependency);
}
protected void addExternalPrerequisite(GroupBuilder group, IComponentRequest dependency, String name) throws CoreException {
PrerequisiteBuilder pqBld = group.createPrerequisiteBuilder();
pqBld.setComponentName(dependency.getName());
pqBld.setComponentType(dependency.getComponentTypeID());
pqBld.setVersionRange(dependency.getVersionRange());
pqBld.setName(name);
group.addPrerequisite(pqBld);
}
/**
* @deprecated Use
* {@link #addExternalPrerequisite(GroupBuilder, IComponentRequest, String)}
*/
@Deprecated
protected void addExternalPrerequisite(GroupBuilder group, String component, String type, String name) throws CoreException {
PrerequisiteBuilder pqBld = group.createPrerequisiteBuilder();
pqBld.setComponentName(component);
pqBld.setComponentType(type);
pqBld.setName(name);
group.addPrerequisite(pqBld);
}
protected void addProductBundles(IProductDescriptor productDescriptor) throws CoreException {
if (productDescriptor.useFeatures())
return;
List<IVersionedId> bundles = productDescriptor.getBundles(true);
if (bundles.size() == 0)
return;
ComponentQuery query = getReader().getNodeQuery().getComponentQuery();
CSpecBuilder cspec = getCSpec();
GroupBuilder fullClean = cspec.getRequiredGroup(ATTRIBUTE_FULL_CLEAN);
GroupBuilder bundleJars = cspec.getRequiredGroup(ATTRIBUTE_BUNDLE_JARS);
String self = null;
if (IComponentType.OSGI_BUNDLE.equals(cspec.getComponentTypeID()))
self = cspec.getName();
for (IVersionedId bundle : bundles) {
String pluginId = bundle.getId();
if (self != null && self.equals(pluginId))
continue;
if (pluginId.equals("system.bundle")) //$NON-NLS-1$
continue;
ComponentRequestBuilder dependency = createDependency(bundle, IComponentType.OSGI_BUNDLE);
if (skipComponent(query, dependency) || !addDependency(dependency))
continue;
fullClean.addExternalPrerequisite(dependency, ATTRIBUTE_FULL_CLEAN);
bundleJars.addExternalPrerequisite(dependency, ATTRIBUTE_BUNDLE_JARS);
}
}
protected void addProductFeatures(IProductDescriptor productDescriptor) throws CoreException {
// Only the feature generator will act on this since adding features to
// a bundle
// would cause circular dependencies. Buckminster requires that a
// feature based
// product is placed in a feature.
//
// TODO: Perhaps print a warning?
}
protected boolean addProducts(final IProgressMonitor monitor) throws CoreException {
monitor.beginTask(null, 2000);
try {
List<FileHandle> productConfigs = reader.getRootFiles(PRODUCT_CONFIGURATION_FILE_PATTERN, MonitorUtils.subMonitor(monitor, 500));
if (productConfigs.size() == 0)
return false;
boolean theOneAndOnly = productConfigs.size() == 1;
int ticksPerConfig = 1500 / productConfigs.size();
for (FileHandle productConfig : productConfigs)
addProduct(productConfig, theOneAndOnly, MonitorUtils.subMonitor(monitor, ticksPerConfig));
return theOneAndOnly;
} catch (IOException e) {
throw BuckminsterException.wrap(e);
} finally {
monitor.done();
}
}
protected ActionBuilder createCopyPluginsAction() throws CoreException {
// Copy all plug-ins that all features (including this one) is
// including.
//
ActionBuilder copyPlugins = addAntAction(ACTION_COPY_PLUGINS, TASK_COPY_GROUP, false);
copyPlugins.addLocalPrerequisite(ATTRIBUTE_BUNDLE_JARS);
if (isFeature())
copyPlugins.addLocalPrerequisite(ATTRIBUTE_SOURCE_BUNDLE_JARS);
copyPlugins.setPrerequisitesAlias(ALIAS_REQUIREMENTS);
copyPlugins.setProductAlias(ALIAS_OUTPUT);
copyPlugins.setProductBase(OUTPUT_DIR_SITE.append(PLUGINS_FOLDER));
copyPlugins.setUpToDatePolicy(UpToDatePolicy.MAPPER);
return copyPlugins;
}
protected ComponentRequestBuilder createDependency(ImportSpecification pluginImport, String category) throws CoreException {
Filter filter = null;
if (pluginImport.isOptional())
filter = ComponentRequest.P2_OPTIONAL_FILTER;
return createDependency(pluginImport.getName(), category, pluginImport.getVersionRange(), filter);
}
protected ComponentRequestBuilder createDependency(IVersionedId versionedName, String componentType) throws CoreException {
String versionRange = null;
Version v = versionedName.getVersion();
if (!(v == null || Version.emptyVersion.equals(v)))
versionRange = new VersionRange(v, true, v, true).toString();
return createDependency(versionedName.getId(), componentType, versionRange, null);
}
protected ComponentRequestBuilder createDependency(String name, String componentType, String versionDesignator, Filter filter)
throws CoreException {
versionDesignator = Trivial.trim(versionDesignator);
if (versionDesignator != null && versionDesignator.equals("0.0.0")) //$NON-NLS-1$
versionDesignator = null;
return createDependency(name, componentType, VersionHelper.createRange(VersionFormat.OSGI_FORMAT, versionDesignator), filter);
}
protected ComponentRequestBuilder createDependency(String name, String componentType, String version, MatchRule pdeMatchRule, Filter filter)
throws CoreException {
return createDependency(name, componentType, convertMatchRule(pdeMatchRule, version), filter);
}
protected ComponentRequestBuilder createDependency(String name, String componentType, VersionRange versionRange, Filter filter)
throws CoreException {
ComponentRequestBuilder bld = getCSpec().createDependencyBuilder();
bld.setName(name);
bld.setComponentTypeID(componentType);
bld.setVersionRange(versionRange);
bld.setFilter(filter);
return bld;
}
protected void createSiteAction(String rawSiteAttribute, String siteDefiningAttribute) throws CoreException {
ActionBuilder siteBuilder = getCSpec().addAction(ATTRIBUTE_SITE_P2, true, ACTOR_P2_SITE_GENERATOR, false);
siteBuilder.addLocalPrerequisite(rawSiteAttribute, P2SiteGenerator.ALIAS_SITE, SIGNING_AND_PACK_DISABLED);
siteBuilder.addLocalPrerequisite(ATTRIBUTE_SITE_PACKED, P2SiteGenerator.ALIAS_SITE, PACK_ENABLED);
siteBuilder.addLocalPrerequisite(ATTRIBUTE_SITE_SIGNED, P2SiteGenerator.ALIAS_SITE, SIGNING_ENABLED_AND_PACK_DISABLED);
siteBuilder.addLocalPrerequisite(siteDefiningAttribute, P2SiteGenerator.ALIAS_SITE_DEFINER);
siteBuilder.addLocalPrerequisite(ATTRIBUTE_PRODUCT_CONFIG_EXPORTS, P2SiteGenerator.ALIAS_PRODUCT_CONFIGS);
siteBuilder.setProductBase(IPDEConstants.OUTPUT_DIR_SITE_P2);
siteBuilder.setUpToDatePolicy(UpToDatePolicy.ACTOR);
}
protected void createSitePackAction(String rawSiteAttribute) throws CoreException {
ActionBuilder siteBuilder = getCSpec().addAction(ATTRIBUTE_SITE_PACKED, true, JarProcessorActor.ACTOR_ID, false);
siteBuilder.addLocalPrerequisite(rawSiteAttribute, JarProcessorActor.ALIAS_JAR_FOLDER, SIGNING_DISABLED);
siteBuilder.addLocalPrerequisite(ATTRIBUTE_SITE_SIGNED, JarProcessorActor.ALIAS_JAR_FOLDER, SIGNING_ENABLED);
siteBuilder.getProperties().put(JarProcessorActor.PROP_COMMAND, JarProcessorActor.COMMAND_PACK);
siteBuilder.setProductBase(OUTPUT_DIR_SITE_PACKED);
siteBuilder.setUpToDatePolicy(UpToDatePolicy.MAPPER);
}
protected void createSiteRepackAction(String rawSiteAttribute) throws CoreException {
ActionBuilder siteBuilder = getCSpec().addAction(ATTRIBUTE_SITE_REPACKED, false, JarProcessorActor.ACTOR_ID, false);
siteBuilder.addLocalPrerequisite(rawSiteAttribute, JarProcessorActor.ALIAS_JAR_FOLDER);
siteBuilder.getProperties().put(JarProcessorActor.PROP_COMMAND, JarProcessorActor.COMMAND_REPACK);
siteBuilder.setProductBase(OUTPUT_DIR_SITE_REPACKED);
siteBuilder.setUpToDatePolicy(UpToDatePolicy.MAPPER);
}
protected void createSiteSignAction(String rawSiteAttribute) throws CoreException {
ActionBuilder siteBuilder = getCSpec().addAction(ATTRIBUTE_SITE_SIGNED, true, AntActor.ACTOR_ID, false);
Map<String, String> actorProps = siteBuilder.getActorProperties();
actorProps.put(AntActor.PROP_BUILD_FILE_ID, "buckminster.signing"); //$NON-NLS-1$
actorProps.put(AntActor.PROP_TARGETS, "sign.jars"); //$NON-NLS-1$
siteBuilder.setPrerequisitesAlias(ALIAS_REQUIREMENTS);
siteBuilder.addLocalPrerequisite(ATTRIBUTE_SITE_REPACKED, null, PACK_ENABLED);
siteBuilder.addLocalPrerequisite(rawSiteAttribute, null, PACK_DISABLED);
siteBuilder.setProductBase(OUTPUT_DIR_SITE_SIGNED);
siteBuilder.setProductAlias(ALIAS_OUTPUT);
siteBuilder.setUpToDatePolicy(UpToDatePolicy.MAPPER);
}
protected void createSiteZipAction() throws CoreException {
ActionBuilder siteZip = addAntAction(ATTRIBUTE_SITE_ZIP, TASK_CREATE_SITE_ZIP, true);
siteZip.addLocalPrerequisite(ATTRIBUTE_MANIFEST, ALIAS_MANIFEST);
siteZip.addLocalPrerequisite(ATTRIBUTE_SITE_P2, ALIAS_REQUIREMENTS);
siteZip.setProductBase(OUTPUT_DIR_SITE_ZIP);
siteZip.setProductAlias(ALIAS_OUTPUT);
siteZip.setUpToDatePolicy(UpToDatePolicy.COUNT);
siteZip.setProductFileCount(1);
}
protected String expand(String value) throws CoreException {
value = TextUtils.notEmptyTrimmedString(value);
if (value == null)
return null;
if (value.charAt(0) != '%')
return value;
String expValue = getProperties().get(value.substring(1));
if (expValue != null)
value = expValue;
return value;
}
protected List<String> expandIncludes(String[] tokens) throws CoreException {
if (tokens == null || tokens.length == 0)
return Collections.emptyList();
ArrayList<String> result = new ArrayList<String>(tokens.length);
for (String token : tokens) {
if (token.indexOf('*') >= 0) {
Pattern pattern = convertIncludeToPattern(token);
try {
for (FileHandle matchingFile : reader.getRootFiles(pattern, new NullProgressMonitor()))
result.add(matchingFile.getName());
} catch (IOException e) {
throw BuckminsterException.wrap(e);
}
} else
result.add(token);
}
return result;
}
protected List<String> expandBinFiles(File baseDir, IBuild build) throws CoreException {
IBuildEntry includes = build.getEntry(PROPERTY_BIN_INCLUDES);
IBuildEntry excludes = build.getEntry(PROPERTY_BIN_EXCLUDES);
return expandFiles(baseDir, includes, excludes);
}
protected List<String> expandBinFiles(File baseDir, Map<String, String> props) throws CoreException {
String includes = props.get(PROPERTY_BIN_INCLUDES);
String excludes = props.get(PROPERTY_BIN_EXCLUDES);
return expandFiles(baseDir, StringHelper.getArrayFromString(includes, ','), StringHelper.getArrayFromString(excludes, ','));
}
protected List<String> expandSrcFiles(File baseDir, IBuild build) throws CoreException {
IBuildEntry includes = build.getEntry(PROPERTY_SRC_INCLUDES);
IBuildEntry excludes = build.getEntry(PROPERTY_SRC_EXCLUDES);
return expandFiles(baseDir, includes, excludes);
}
protected AttributeBuilder generateRemoveDirAction(String dirTag, IPath dirPath, boolean publ) throws CoreException {
return generateRemoveDirAction(dirTag, dirPath, publ, "buckminster.rm." + dirTag + ".dir"); //$NON-NLS-1$ //$NON-NLS-2$
}
protected AttributeBuilder generateRemoveDirAction(String dirTag, IPath dirPath, boolean publ, String actionName) throws CoreException {
ActionBuilder rmDir = addAntAction(actionName, TASK_DELETE_DIR, publ);
rmDir.addProperty(PROP_DELETE_DIR, dirPath.toPortableString(), false);
return rmDir;
}
protected abstract String getProductOutputFolder(String productId);
protected Map<String, String> getProperties() throws CoreException {
if (properties == null) {
try {
properties = reader.readFile(getPropertyFileName(), new PropertiesParser(), new NullProgressMonitor());
} catch (FileNotFoundException e) {
properties = Collections.emptyMap();
} catch (IOException e) {
throw BuckminsterException.wrap(e);
}
}
return properties;
}
protected abstract String getPropertyFileName();
protected boolean isFeature() {
return false;
}
protected void setFilter(String filterStr) throws CoreException {
filterStr = TextUtils.notEmptyTrimmedString(filterStr);
if (filterStr == null)
return;
try {
Filter filter = FilterFactory.newInstance(filterStr);
getCSpec().setFilter(FilterUtils.replaceAttributeNames(filter, "osgi", TargetPlatform.TARGET_PREFIX)); //$NON-NLS-1$
} catch (InvalidSyntaxException e) {
NodeQuery query = reader.getNodeQuery();
IStatus status = new Status(IStatus.WARNING, PDEPlugin.getPluginId(), Messages.manifest_has_malformed_LDAP_rule_for
+ ICoreConstants.PLATFORM_FILTER + ": " + e.getMessage()); //$NON-NLS-1$
RMContext ctx = query.getContext();
if (!ctx.isContinueOnError())
throw new CoreException(status);
ctx.addRequestStatus(query.getComponentRequest(), status);
}
}
protected boolean skipComponent(ComponentQuery query, ComponentRequestBuilder bld) {
return query.skipComponent(new ComponentName(bld.getName(), bld.getComponentTypeID()), getReader().getNodeQuery().getContext());
}
private void addProduct(FileHandle productConfig, boolean theOneAndOnly, IProgressMonitor monitor) throws CoreException, IOException {
try {
File productConfigFile = productConfig.getFile();
IProductDescriptor productDescriptor;
try {
productDescriptor = new ProductFile(productConfigFile.getAbsolutePath());
} catch (RuntimeException e) {
throw e;
} catch (IOException e) {
throw e;
} catch (Exception e) {
throw BuckminsterException.wrap(e);
}
CSpecBuilder cspec = getCSpec();
String productId = productDescriptor.getId();
if (productId == null || productId.length() == 0) {
PDEPlugin.getLogger().warning(NLS.bind(Messages.No_productId_found_in_0, productConfigFile.getAbsolutePath()));
productId = "<undefined product ID>"; //$NON-NLS-1$
}
ArtifactBuilder productConfigArtifact = cspec.addArtifact(productId, false, null);
productConfigArtifact.addPath(Path.fromOSString(productConfigFile.getName()));
GroupBuilder productConfigs = cspec.getGroup(ATTRIBUTE_PRODUCT_CONFIGS);
if (productConfigs == null) {
productConfigs = cspec.addGroup(ATTRIBUTE_PRODUCT_CONFIGS, false);
cspec.getRequiredGroup(ATTRIBUTE_PRODUCT_CONFIG_EXPORTS).addLocalPrerequisite(productConfigs);
}
productConfigs.addLocalPrerequisite(productConfigArtifact);
if (productDescriptor.useFeatures())
addProductFeatures(productDescriptor);
else
addProductBundles(productDescriptor);
if (!theOneAndOnly)
// We're done here.
return;
if (!isFeature()) {
// This bundle must be able to create a site for its product
//
GroupBuilder featureExports = cspec.addGroup(ATTRIBUTE_FEATURE_EXPORTS, true);
featureExports.addLocalPrerequisite(createCopyPluginsAction());
featureExports.setPrerequisiteRebase(OUTPUT_DIR_SITE);
}
createSiteRepackAction(ATTRIBUTE_FEATURE_EXPORTS);
createSiteSignAction(ATTRIBUTE_FEATURE_EXPORTS);
createSitePackAction(ATTRIBUTE_FEATURE_EXPORTS);
createSiteAction(ATTRIBUTE_FEATURE_EXPORTS, productConfigArtifact.getName());
createSiteZipAction();
} finally {
if (productConfig.isTemporary())
productConfig.getFile().delete();
}
}
private List<String> expandFiles(File baseDir, IBuildEntry includes, IBuildEntry excludes) throws CoreException {
if (includes == null && excludes == null)
return Collections.emptyList();
String[] incTokens = includes == null ? StringHelper.EMPTY_ARRAY : includes.getTokens();
String[] excTokens = excludes == null ? StringHelper.EMPTY_ARRAY : excludes.getTokens();
return expandFiles(baseDir, incTokens, excTokens);
}
private List<String> expandFiles(File baseDir, String[] includes, String[] excludes) throws CoreException {
includes = replaceVariables(includes);
excludes = replaceVariables(excludes);
DirectoryScanner scanner = new DirectoryScanner();
scanner.setBasedir(baseDir);
scanner.setIncludes(includes.length == 0 ? null : includes);
scanner.setExcludes(excludes.length == 0 ? null : excludes);
scanner.addDefaultExcludes();
scanner.scan();
// If we have includes that end with a '/' that are unaffected by
// excludes, then we want to retain the directory notion and
// not get all files.
boolean replaceSep = File.separatorChar != '/';
String[] excludedFiles = scanner.getExcludedFiles();
if (excludedFiles.length > 0) {
String[] defaultExcludes = DirectoryScanner.getDefaultExcludes();
ArrayList<String> prunedExcludes = new ArrayList<String>();
for (String excludedFile : excludedFiles) {
if (replaceSep)
// Scanner will report files using native separator char. We
// don't want that here.
excludedFile = excludedFile.replace(File.separatorChar, '/');
boolean excludedByDefault = false;
for (String defaultExclude : defaultExcludes) {
if (SelectorUtils.matchPath(defaultExclude, excludedFile)) {
excludedByDefault = true;
break;
}
}
if (!excludedByDefault)
prunedExcludes.add(excludedFile);
}
excludedFiles = prunedExcludes.toArray(new String[prunedExcludes.size()]);
}
ArrayList<String> plainDirs = null;
for (String include : includes) {
if (include.indexOf('*') >= 0 || include.charAt(include.length() - 1) != '/')
continue;
int idx = excludedFiles.length;
while (--idx >= 0) {
if (excludedFiles[idx].startsWith(include))
break;
}
if (idx < 0) {
if (plainDirs == null)
plainDirs = new ArrayList<String>();
plainDirs.add(include);
}
}
HashSet<String> plainDirsWithFiles = null;
ArrayList<String> result = new ArrayList<String>();
String[] includedFiles = scanner.getIncludedFiles();
int top = includedFiles.length;
for (int idx = 0; idx < top; ++idx) {
String includedFile = Path.fromOSString(includedFiles[idx]).toPortableString();
if (plainDirs != null) {
int ndx = plainDirs.size();
while (--ndx >= 0) {
String plainDir = plainDirs.get(ndx);
if (includedFile.startsWith(plainDir)) {
if (plainDirsWithFiles == null)
plainDirsWithFiles = new HashSet<String>();
plainDirsWithFiles.add(plainDir);
break;
}
}
if (ndx >= 0)
continue;
}
result.add(includedFile);
}
if (plainDirsWithFiles != null)
result.addAll(plainDirsWithFiles);
return result;
}
private String[] replaceVariables(String sources[]) throws CoreException {
if (sources == null)
return StringHelper.EMPTY_ARRAY;
int top = sources.length;
ArrayList<String> result = new ArrayList<String>(top);
for (int idx = 0; idx < top; ++idx) {
String source = replaceVariables(sources[idx]);
if (source != null)
result.add(source);
}
return result.toArray(new String[result.size()]);
}
private String replaceVariables(String source) throws CoreException {
if (source == null)
return null;
if (source.length() >= 4) {
source = source.replace(DESCRIPTION_VARIABLE_WS, "${target.ws}"); //$NON-NLS-1$
source = source.replace(DESCRIPTION_VARIABLE_OS, "${target.os}"); //$NON-NLS-1$
source = source.replace(DESCRIPTION_VARIABLE_ARCH, "${target.arch}"); //$NON-NLS-1$
source = source.replace(DESCRIPTION_VARIABLE_NL, "${target.nl}"); //$NON-NLS-1$
}
source = Trivial.trim(ExpandingProperties.expand(getProperties(), source, 0));
if (source != null && File.separatorChar != '/')
// We do all comparison using the forward slash
source = source.replace('\\', '/');
// Our objective is to create a fileset so we don't want the . or ./
if (source != null && (".".equals(source) || "./".equals(source))) //$NON-NLS-1$//$NON-NLS-2$
source = null;
return source;
}
}