/***************************************************************************** * 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.feature; import java.util.List; import java.util.Map; import java.util.StringTokenizer; import org.eclipse.buckminster.core.cspec.IComponentRequest; import org.eclipse.buckminster.core.cspec.builder.ActionBuilder; 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.model.ComponentRequest; import org.eclipse.buckminster.core.cspec.model.UpToDatePolicy; import org.eclipse.buckminster.core.ctype.IComponentType; import org.eclipse.buckminster.core.helpers.FilterUtils; import org.eclipse.buckminster.core.query.model.ComponentQuery; import org.eclipse.buckminster.core.reader.ICatalogReader; import org.eclipse.buckminster.core.version.VersionHelper; import org.eclipse.buckminster.osgi.filter.Filter; import org.eclipse.buckminster.pde.MatchRule; import org.eclipse.buckminster.pde.cspecgen.CSpecGenerator; import org.eclipse.buckminster.pde.internal.EclipsePlatformReaderType; import org.eclipse.buckminster.pde.tasks.VersionConsolidator; import org.eclipse.buckminster.runtime.MonitorUtils; import org.eclipse.buckminster.runtime.Trivial; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.equinox.internal.p2.publisher.eclipse.IProductDescriptor; import org.eclipse.equinox.p2.metadata.IVersionedId; import org.eclipse.equinox.p2.metadata.Version; import org.eclipse.pde.internal.core.PDECore; import org.eclipse.pde.internal.core.PluginModelManager; import org.eclipse.pde.internal.core.ifeature.IFeature; import org.eclipse.pde.internal.core.ifeature.IFeatureChild; import org.eclipse.pde.internal.core.ifeature.IFeatureModel; import org.eclipse.pde.internal.core.ifeature.IFeaturePlugin; @SuppressWarnings("restriction") public abstract class CSpecFromFeature extends CSpecGenerator { private static final String SOURCE_SUFFIX = ".source"; //$NON-NLS-1$ private static final String SOURCE_FEATURE_SUFFIX = ".source.feature"; //$NON-NLS-1$ private static final IFeatureChild[] noFeatures = new IFeatureChild[0]; public static String getIdWithoutSource(String sourceId) { if (sourceId.endsWith(SOURCE_SUFFIX)) return sourceId.substring(0, sourceId.length() - SOURCE_SUFFIX.length()); if (sourceId.endsWith(SOURCE_FEATURE_SUFFIX)) return sourceId.substring(0, sourceId.length() - SOURCE_FEATURE_SUFFIX.length()) + ".feature"; //$NON-NLS-1$ return null; } private static boolean isListOK(String list, Object item) { if (list == null || list.length() == 0) return true; StringTokenizer tokens = new StringTokenizer(list, ","); //$NON-NLS-1$ while (tokens.hasMoreTokens()) if (item.equals("*") || item.equals(tokens.nextElement())) //$NON-NLS-1$ return true; return false; } protected final IFeature feature; protected CSpecFromFeature(CSpecBuilder cspecBuilder, ICatalogReader reader, IFeature feature) { super(cspecBuilder, reader); this.feature = feature; } @Override public void generate(IProgressMonitor monitor) throws CoreException { CSpecBuilder cspec = getCSpec(); cspec.setName(feature.getId()); cspec.setVersion(VersionHelper.parseVersion(feature.getVersion())); cspec.setComponentTypeID(IComponentType.ECLIPSE_FEATURE); cspec.setFilter(FilterUtils.createFilter(feature.getOS(), feature.getWS(), feature.getArch(), feature.getNL())); // This feature and all included features. Does not imply copying since // the group will reference the features where they are found. // cspec.addGroup(ATTRIBUTE_FEATURE_REFS, true); // without self cspec.addGroup(ATTRIBUTE_SOURCE_FEATURE_REFS, true).setFilter(SOURCE_FILTER); // All bundles imported by this feature and all included features. Does // not imply copying since the group will reference the bundles where // they // are found. // cspec.addGroup(ATTRIBUTE_BUNDLE_JARS, true); // Source of all bundles imported by this feature and all included // features. // cspec.addGroup(ATTRIBUTE_SOURCE_BUNDLE_JARS, true).setFilter(SOURCE_FILTER); // This feature, its imported bundles, all included features, and their // imported bundles copied into a site structure (with a plugins and a // features folder). // cspec.addGroup(ATTRIBUTE_FEATURE_EXPORTS, true); cspec.addGroup(ATTRIBUTE_SITE_FEATURE_EXPORTS, true); cspec.addGroup(ATTRIBUTE_PRODUCT_CONFIG_EXPORTS, true); generateRemoveDirAction("build", OUTPUT_DIR, true, ATTRIBUTE_FULL_CLEAN); //$NON-NLS-1$ IComponentRequest licenseFeature = addFeatures(); addPlugins(); MonitorUtils.begin(monitor, 100); createFeatureJarAction(licenseFeature, MonitorUtils.subMonitor(monitor, 20)); createFeatureSourceJarAction(); createFeatureExportsAction(); GroupBuilder featureJars = cspec.addGroup(ATTRIBUTE_FEATURE_JARS, true); // including // self featureJars.addLocalPrerequisite(ATTRIBUTE_FEATURE_JAR); featureJars.addLocalPrerequisite(ATTRIBUTE_FEATURE_REFS); GroupBuilder sourceFeatureJars = cspec.addGroup(ATTRIBUTE_SOURCE_FEATURE_JARS, true); // including // self sourceFeatureJars.setFilter(SOURCE_FILTER); sourceFeatureJars.addLocalPrerequisite(ATTRIBUTE_SOURCE_FEATURE_JAR); sourceFeatureJars.addLocalPrerequisite(ATTRIBUTE_SOURCE_FEATURE_REFS); createSiteActions(MonitorUtils.subMonitor(monitor, 80)); MonitorUtils.done(monitor); } @Override protected void addProductFeatures(IProductDescriptor productDescriptor) throws CoreException { if (!productDescriptor.useFeatures()) return; List<IVersionedId> features = productDescriptor.getFeatures(); if (features.size() == 0) return; ComponentQuery query = getReader().getNodeQuery().getComponentQuery(); CSpecBuilder cspec = getCSpec(); ActionBuilder fullClean = cspec.getRequiredAction(ATTRIBUTE_FULL_CLEAN); GroupBuilder featureRefs = cspec.getRequiredGroup(ATTRIBUTE_FEATURE_REFS); GroupBuilder featureSourceRefs = cspec.getRequiredGroup(ATTRIBUTE_SOURCE_FEATURE_REFS); GroupBuilder bundleJars = cspec.getRequiredGroup(ATTRIBUTE_BUNDLE_JARS); GroupBuilder sourceBundleJars = cspec.getRequiredGroup(ATTRIBUTE_SOURCE_BUNDLE_JARS); String self = cspec.getName(); for (IVersionedId productFeature : features) { if (productFeature.getId().equals(self)) continue; ComponentRequestBuilder dep = createDependency(productFeature, IComponentType.ECLIPSE_FEATURE); if (skipComponent(query, dep)) continue; cspec.addDependency(dep); featureRefs.addExternalPrerequisite(dep, ATTRIBUTE_FEATURE_JARS); featureSourceRefs.addExternalPrerequisite(dep, ATTRIBUTE_SOURCE_FEATURE_JARS); bundleJars.addExternalPrerequisite(dep, ATTRIBUTE_BUNDLE_JARS); sourceBundleJars.addExternalPrerequisite(dep, ATTRIBUTE_SOURCE_BUNDLE_JARS); fullClean.addExternalPrerequisite(dep, ATTRIBUTE_FULL_CLEAN); } } @Override protected String getPropertyFileName() { return FEATURE_PROPERTIES_FILE; } @Override protected boolean isFeature() { return true; } /** * Adds all feature dependencies. If a license feature is referenced, it is * also added. * * @return The license feature if any, otherwise <code>null</code>. * @throws CoreException */ IComponentRequest addFeatures() throws CoreException { String licenseFeatureID = Trivial.trim(feature.getLicenseFeatureID()); IFeatureChild[] features = feature.getIncludedFeatures(); if (features == null) features = noFeatures; if (features.length == 0 && licenseFeatureID == null) return null; ComponentQuery query = getReader().getNodeQuery().getComponentQuery(); CSpecBuilder cspec = getCSpec(); ActionBuilder fullClean = cspec.getRequiredAction(ATTRIBUTE_FULL_CLEAN); GroupBuilder featureRefs = cspec.getRequiredGroup(ATTRIBUTE_FEATURE_REFS); GroupBuilder featureSourceRefs = cspec.getRequiredGroup(ATTRIBUTE_SOURCE_FEATURE_REFS); GroupBuilder bundleJars = cspec.getRequiredGroup(ATTRIBUTE_BUNDLE_JARS); GroupBuilder sourceBundleJars = cspec.getRequiredGroup(ATTRIBUTE_SOURCE_BUNDLE_JARS); GroupBuilder productConfigExports = cspec.getRequiredGroup(ATTRIBUTE_PRODUCT_CONFIG_EXPORTS); for (IFeatureChild includedFeature : features) { ComponentRequestBuilder dep = createDependency(includedFeature); if (skipComponent(query, dep)) continue; boolean sourceInTP = false; cspec.addDependency(dep); String idWithoutSource = getIdWithoutSource(dep.getName()); if (idWithoutSource == null) { featureRefs.addExternalPrerequisite(dep, ATTRIBUTE_FEATURE_JARS); bundleJars.addExternalPrerequisite(dep, ATTRIBUTE_BUNDLE_JARS); fullClean.addExternalPrerequisite(dep, ATTRIBUTE_FULL_CLEAN); productConfigExports.addExternalPrerequisite(dep, ATTRIBUTE_PRODUCT_CONFIG_EXPORTS); } else { // Watch out for source for self // if (idWithoutSource.equals(cspec.getName())) { continue; } IFeatureModel sourceFeature = EclipsePlatformReaderType.getBestFeature(dep.getName(), dep.getVersionRange(), null); if (sourceFeature != null) { // This one is present in the target platform so we include // it just like any // other feature. featureRefs.addExternalPrerequisite(dep, ATTRIBUTE_FEATURE_JARS); bundleJars.addExternalPrerequisite(dep, ATTRIBUTE_BUNDLE_JARS); sourceInTP = true; } } if (!sourceInTP) { // For source generation sourceBundleJars.addExternalPrerequisite(dep, ATTRIBUTE_SOURCE_BUNDLE_JARS); featureSourceRefs.addExternalPrerequisite(dep, ATTRIBUTE_SOURCE_FEATURE_JARS); } } if (licenseFeatureID == null) return null; ComponentRequestBuilder dep = new ComponentRequestBuilder(); dep.setName(licenseFeatureID); dep.setComponentTypeID(IComponentType.ECLIPSE_FEATURE); Version licenseFeatureVersion = VersionHelper.parseVersion(feature.getLicenseFeatureVersion()); if (licenseFeatureVersion != null) dep.setVersionRange(VersionHelper.exactRange(licenseFeatureVersion)); if (skipComponent(query, dep)) return null; cspec.addDependency(dep); featureRefs.addExternalPrerequisite(dep, ATTRIBUTE_FEATURE_JARS); bundleJars.addExternalPrerequisite(dep, ATTRIBUTE_BUNDLE_JARS); fullClean.addExternalPrerequisite(dep, ATTRIBUTE_FULL_CLEAN); return dep; } void addPlugins() throws CoreException { String brandingPluginName = feature.getPlugin(); IFeaturePlugin[] plugins = feature.getPlugins(); if (plugins != null && plugins.length > 0) { Map<String, ? extends Object> props = getReader().getNodeQuery().getProperties(); Object os = props.get(org.eclipse.buckminster.core.TargetPlatform.TARGET_OS); Object ws = props.get(org.eclipse.buckminster.core.TargetPlatform.TARGET_WS); Object arch = props.get(org.eclipse.buckminster.core.TargetPlatform.TARGET_ARCH); ComponentQuery query = getReader().getNodeQuery().getComponentQuery(); CSpecBuilder cspec = getCSpec(); ActionBuilder fullClean = cspec.getRequiredAction(ATTRIBUTE_FULL_CLEAN); GroupBuilder bundleJars = cspec.getRequiredGroup(ATTRIBUTE_BUNDLE_JARS); GroupBuilder sourceBundleJars = cspec.getRequiredGroup(ATTRIBUTE_SOURCE_BUNDLE_JARS); GroupBuilder productConfigExports = cspec.getRequiredGroup(ATTRIBUTE_PRODUCT_CONFIG_EXPORTS); PluginModelManager manager = PDECore.getDefault().getModelManager(); boolean hasBogusFragments = false; String id = feature.getId(); if (VersionConsolidator.getBooleanProperty(getProperties(), "buckminster.handle.incomplete.platform.features", false)) { //$NON-NLS-1$ hasBogusFragments = "org.eclipse.platform".equals(id) //$NON-NLS-1$ || "org.eclipse.equinox.executable".equals(id) //$NON-NLS-1$ || "org.eclipse.rcp".equals(id); //$NON-NLS-1$ } else { // We still need this here due to // https://bugs.eclipse.org/bugs/show_bug.cgi?id=319345 hasBogusFragments = "org.eclipse.equinox.executable".equals(id); //$NON-NLS-1$ } for (IFeaturePlugin plugin : plugins) { if (!(isListOK(plugin.getOS(), os) && isListOK(plugin.getWS(), ws) && isListOK(plugin.getArch(), arch))) { // Only include this if we can find it in the target // platform // if (manager.findEntry(plugin.getId()) == null) continue; } if (hasBogusFragments && (plugin.getOS() != null || plugin.getWS() != null || plugin.getArch() != null)) { // Only include this if we can find it in the target // platform. // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=213437 // if (manager.findEntry(plugin.getId()) == null) continue; } ComponentRequestBuilder dep = createDependency(plugin); if (brandingPluginName != null && brandingPluginName.equals(dep.getName())) brandingPluginName = null; if (skipComponent(query, dep)) continue; if (!addDependency(dep)) continue; bundleJars.addExternalPrerequisite(dep, ATTRIBUTE_BUNDLE_AND_FRAGMENTS); // We either add this bundles action to generate it's own // source, or we add an already existing source bundle. Let's // check with the target platform if (!plugin.getId().endsWith(SOURCE_SUFFIX)) { String sourceId = plugin.getId() + SOURCE_SUFFIX; if (manager.findEntry(sourceId) == null) sourceBundleJars.addExternalPrerequisite(dep, ATTRIBUTE_BUNDLE_AND_FRAGMENTS_SOURCE); else { ComponentRequestBuilder sourceDep = new ComponentRequestBuilder(); sourceDep.setName(sourceId); sourceDep.setComponentTypeID(IComponentType.OSGI_BUNDLE); sourceDep.setVersionRange(dep.getVersionRange()); addDependency(sourceDep); sourceBundleJars.addExternalPrerequisite(sourceDep, ATTRIBUTE_BUNDLE_JAR); } } fullClean.addExternalPrerequisite(dep, ATTRIBUTE_FULL_CLEAN); productConfigExports.addExternalPrerequisite(dep, ATTRIBUTE_PRODUCT_CONFIG_EXPORTS); } } if (brandingPluginName != null) { // We have a branding plugin that we have no other dependency to. // This is also a valid dependency so let's add it. ComponentRequestBuilder dep = getCSpec().createDependencyBuilder(); dep.setName(brandingPluginName); dep.setComponentTypeID(IComponentType.OSGI_BUNDLE); addDependency(dep); } } ComponentRequestBuilder createDependency(IFeatureChild featureChild) throws CoreException { Filter filter = FilterUtils.createFilter(featureChild.getOS(), featureChild.getWS(), featureChild.getArch(), featureChild.getNL()); if (featureChild.isOptional()) filter = ComponentRequest.P2_OPTIONAL_FILTER.addFilterWithAnd(filter); return createDependency(featureChild.getId(), IComponentType.ECLIPSE_FEATURE, featureChild.getVersion(), MatchRule.fromPDE(featureChild.getMatch()), filter); } ComponentRequestBuilder createDependency(IFeaturePlugin plugin) throws CoreException { Filter filter = FilterUtils.createFilter(plugin.getOS(), plugin.getWS(), plugin.getArch(), plugin.getNL()); return createDependency(plugin.getId(), IComponentType.OSGI_BUNDLE, plugin.getVersion(), MatchRule.NONE, filter); } abstract void createFeatureJarAction(IComponentRequest licenseFeature, IProgressMonitor monitor) throws CoreException; abstract void createFeatureSourceJarAction() throws CoreException; abstract void createSiteActions(IProgressMonitor monitor) throws CoreException; private ActionBuilder createCopyFeaturesAction() throws CoreException { // Copy all features (including this one) to the features directory. // ActionBuilder copyFeatures = addAntAction(ACTION_COPY_FEATURES, TASK_COPY_GROUP, false); copyFeatures.addLocalPrerequisite(ATTRIBUTE_FEATURE_JARS); copyFeatures.addLocalPrerequisite(ATTRIBUTE_SOURCE_FEATURE_JARS); copyFeatures.setPrerequisitesAlias(ALIAS_REQUIREMENTS); copyFeatures.setProductAlias(ALIAS_OUTPUT); copyFeatures.setProductBase(OUTPUT_DIR_SITE.append(FEATURES_FOLDER)); copyFeatures.setUpToDatePolicy(UpToDatePolicy.MAPPER); return copyFeatures; } private void createFeatureExportsAction() throws CoreException { GroupBuilder featureExports = getCSpec().getRequiredGroup(ATTRIBUTE_FEATURE_EXPORTS); featureExports.addLocalPrerequisite(createCopyFeaturesAction()); featureExports.addLocalPrerequisite(createCopyPluginsAction()); featureExports.setPrerequisiteRebase(OUTPUT_DIR_SITE); } }