/*******************************************************************************
* 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.internal.actor;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
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 org.eclipse.buckminster.core.CorePlugin;
import org.eclipse.buckminster.core.KeyConstants;
import org.eclipse.buckminster.core.TargetPlatform;
import org.eclipse.buckminster.core.actor.AbstractActor;
import org.eclipse.buckminster.core.actor.IActionContext;
import org.eclipse.buckminster.core.actor.MissingPrerequisiteException;
import org.eclipse.buckminster.core.common.model.ExpandingProperties;
import org.eclipse.buckminster.core.cspec.model.Action;
import org.eclipse.buckminster.core.cspec.model.Attribute;
import org.eclipse.buckminster.core.cspec.model.CSpec;
import org.eclipse.buckminster.core.cspec.model.ComponentIdentifier;
import org.eclipse.buckminster.core.cspec.model.Prerequisite;
import org.eclipse.buckminster.core.ctype.IComponentType;
import org.eclipse.buckminster.core.helpers.BMProperties;
import org.eclipse.buckminster.core.metadata.model.IModelCache;
import org.eclipse.buckminster.core.mspec.ConflictResolution;
import org.eclipse.buckminster.pde.IPDEConstants;
import org.eclipse.buckminster.pde.Messages;
import org.eclipse.buckminster.pde.PDEPlugin;
import org.eclipse.buckminster.pde.cspecgen.CSpecGenerator;
import org.eclipse.buckminster.pde.internal.PDETargetPlatform;
import org.eclipse.buckminster.pde.tasks.BundlesAction;
import org.eclipse.buckminster.pde.tasks.CategoriesAction;
import org.eclipse.buckminster.pde.tasks.FeaturesAction;
import org.eclipse.buckminster.pde.tasks.MirrorsSiteAction;
import org.eclipse.buckminster.pde.tasks.ProductAction;
import org.eclipse.buckminster.pde.tasks.RemoveUnpackedSiblingsAction;
import org.eclipse.buckminster.pde.tasks.SiteReferencesAction;
import org.eclipse.buckminster.pde.tasks.VersionConsolidator;
import org.eclipse.buckminster.runtime.BuckminsterException;
import org.eclipse.buckminster.runtime.IOUtils;
import org.eclipse.buckminster.runtime.Logger;
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.artifact.repository.simple.SimpleArtifactRepositoryFactory;
import org.eclipse.equinox.internal.p2.core.helpers.FileUtils;
import org.eclipse.equinox.internal.p2.metadata.repository.SimpleMetadataRepositoryFactory;
import org.eclipse.equinox.internal.p2.publisher.eclipse.FeatureManifestParser;
import org.eclipse.equinox.internal.p2.publisher.eclipse.IProductDescriptor;
import org.eclipse.equinox.internal.p2.publisher.eclipse.ProductFile;
import org.eclipse.equinox.p2.core.IProvisioningAgent;
import org.eclipse.equinox.p2.metadata.IVersionedId;
import org.eclipse.equinox.p2.metadata.Version;
import org.eclipse.equinox.p2.metadata.VersionedId;
import org.eclipse.equinox.p2.publisher.IPublisherAction;
import org.eclipse.equinox.p2.publisher.IPublisherInfo;
import org.eclipse.equinox.p2.publisher.Publisher;
import org.eclipse.equinox.p2.publisher.PublisherInfo;
import org.eclipse.equinox.p2.publisher.eclipse.BundleShapeAdvice;
import org.eclipse.equinox.p2.publisher.eclipse.Feature;
import org.eclipse.equinox.p2.publisher.eclipse.FeatureEntry;
import org.eclipse.equinox.p2.publisher.eclipse.IBundleShapeAdvice;
import org.eclipse.equinox.p2.publisher.eclipse.URLEntry;
import org.eclipse.equinox.p2.repository.IRepository;
import org.eclipse.equinox.p2.repository.artifact.IArtifactRepository;
import org.eclipse.equinox.p2.repository.metadata.IMetadataRepository;
import org.eclipse.equinox.spi.p2.publisher.LocalizationHelper;
import org.eclipse.osgi.util.NLS;
import org.eclipse.pde.internal.core.ifeature.IFeatureModel;
import org.xml.sax.SAXException;
@SuppressWarnings("restriction")
public class P2SiteGenerator extends AbstractActor {
public static final String ID = "p2SiteGenerator"; //$NON-NLS-1$
public static final String ALIAS_SITE = "site"; //$NON-NLS-1$
public static final String ALIAS_SITE_DEFINER = "site.definer"; //$NON-NLS-1$
public static final String ALIAS_PRODUCT_CONFIGS = "product.configs"; //$NON-NLS-1$
private final static String MATCH_ALL = "*"; //$NON-NLS-1$
private static final String WS_CARBON = "carbon"; //$NON-NLS-1$
private static final String WS_COCOA = "cocoa"; //$NON-NLS-1$
private static final String WS_GTK = "gtk"; //$NON-NLS-1$
private static final String WS_MOTIF = "motif"; //$NON-NLS-1$
private static final String WS_WIN32 = "win32"; //$NON-NLS-1$
private static final String OS_AIX = "aix"; //$NON-NLS-1$
private static final String OS_HPUX = "hpux"; //$NON-NLS-1$
private static final String OS_LINUX = "linux"; //$NON-NLS-1$
private static final String OS_MACOSX = "macosx"; //$NON-NLS-1$
private static final String OS_SOLARIS = "solaris"; //$NON-NLS-1$
private static final String OS_WIN32 = "win32"; //$NON-NLS-1$
private static final String ARCH_IA64_32 = "ia64_32"; //$NON-NLS-1$
private static final String ARCH_PPC = "ppc"; //$NON-NLS-1$
private static final String ARCH_PPC64 = "ppc64"; //$NON-NLS-1$
private static final String ARCH_SPARC = "sparc"; //$NON-NLS-1$
private static final String ARCH_X86 = "x86"; //$NON-NLS-1$
private static final String ARCH_X86_64 = "x86_64"; //$NON-NLS-1$
private final static String[][] defaultKnownConfigs = { //
{ WS_CARBON, OS_MACOSX, ARCH_PPC }, //
{ WS_CARBON, OS_MACOSX, ARCH_X86 }, //
{ WS_COCOA, OS_MACOSX, ARCH_X86 }, //
{ WS_COCOA, OS_MACOSX, ARCH_X86_64 }, //
{ WS_GTK, OS_LINUX, ARCH_PPC }, //
{ WS_GTK, OS_LINUX, ARCH_PPC64 }, //
{ WS_GTK, OS_LINUX, ARCH_X86 }, //
{ WS_GTK, OS_LINUX, ARCH_X86_64 }, //
{ WS_GTK, OS_SOLARIS, ARCH_SPARC }, //
{ WS_GTK, OS_SOLARIS, ARCH_X86 }, //
{ WS_MOTIF, OS_AIX, ARCH_PPC }, //
{ WS_MOTIF, OS_HPUX, ARCH_IA64_32 }, //
{ WS_MOTIF, OS_LINUX, ARCH_X86 }, //
{ WS_MOTIF, OS_SOLARIS, ARCH_SPARC }, //
{ WS_WIN32, OS_WIN32, ARCH_X86 }, //
{ WS_WIN32, OS_WIN32, ARCH_X86_64 }, //
};
public static String[] getConfigurations(Map<String, ? extends Object> props) {
// Return a list of configurations. Typically only one but might be
// several if one or several of ws, os, or arch contains a wildcard
//
String targetWS = props.get(TargetPlatform.TARGET_WS).toString();
if (targetWS == null)
targetWS = org.eclipse.pde.core.plugin.TargetPlatform.getWS();
String targetOS = props.get(TargetPlatform.TARGET_OS).toString();
if (targetOS == null)
targetOS = org.eclipse.pde.core.plugin.TargetPlatform.getOS();
String targetArch = props.get(TargetPlatform.TARGET_ARCH).toString();
if (targetArch == null)
targetArch = org.eclipse.pde.core.plugin.TargetPlatform.getOSArch();
if (!(MATCH_ALL.equals(targetOS) || MATCH_ALL.equals(targetWS) || MATCH_ALL.equals(targetArch)))
return new String[] { targetWS + '.' + targetOS + '.' + targetArch };
// TODO: Add a way to extend the list of known configurations. Or,
// possibly
// use what's in the executable feature.
//
ArrayList<String> possibleMatches = new ArrayList<String>();
for (String[] config : defaultKnownConfigs) {
if (isMatch(config, targetWS, targetOS, targetArch))
possibleMatches.add(config[0] + '.' + config[1] + '.' + config[2]);
}
return possibleMatches.toArray(new String[possibleMatches.size()]);
}
private static void addProductAction(IActionContext ctx, File sourceFolder, List<IPublisherAction> actions, IProductDescriptor product,
Map<String, String> buildProperties, boolean asSiteDefiner) throws CoreException {
String flavor = buildProperties.get("org.eclipse.p2.flavor"); //$NON-NLS-1$
if (flavor == null)
flavor = "tooling"; //$NON-NLS-1$
File exeFeature = null;
IFeatureModel launcherFeature = PDETargetPlatform.getBestFeature(CSpecGenerator.LAUNCHER_FEATURE, null, null);
if (launcherFeature == null)
// The rcp feature includes all launchers
launcherFeature = PDETargetPlatform.getBestFeature(CSpecGenerator.RCP_FEATURE, null, null);
if (launcherFeature == null)
launcherFeature = PDETargetPlatform.getBestFeature(CSpecGenerator.LAUNCHER_FEATURE_3_2, null, null);
if (launcherFeature != null)
exeFeature = new File(launcherFeature.getInstallLocation());
if (asSiteDefiner && product.useFeatures()) {
List<IVersionedId> features = product.getFeatures();
actions.add(new CategoriesAction(sourceFolder, buildProperties, features));
}
actions.add(new ProductAction(ctx, null, product, flavor, exeFeature));
}
private static IProductDescriptor getProductDescriptor(File productFile) throws CoreException {
try {
return new ProductFile(productFile.getAbsolutePath());
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw BuckminsterException.wrap(e);
}
}
private static boolean isMatch(String[] config, String targetWS, String targetOS, String targetArch) {
return (MATCH_ALL.equals(targetWS) || config[0].equals(targetWS)) && (MATCH_ALL.equals(targetOS) || config[1].equals(targetOS))
&& (MATCH_ALL.equals(targetArch) || config[2].equals(targetArch));
}
private static Feature parse(File sourceFolder, File featureFile) throws CoreException {
InputStream input = null;
try {
input = new BufferedInputStream(new FileInputStream(featureFile));
FeatureManifestParser parser = new FeatureManifestParser();
Feature feature = parser.parse(input, featureFile.toURI().toURL());
if (feature == null)
throw BuckminsterException.fromMessage(NLS.bind(Messages.unable_to_parse_feature_manifest_file_0, featureFile));
List<String> messageKeys = parser.getMessageKeys();
String[] keyStrings = messageKeys.toArray(new String[messageKeys.size()]);
feature.setLocalizations(LocalizationHelper.getDirPropertyLocalizations(sourceFolder, "feature", null, keyStrings)); //$NON-NLS-1$
feature.setLocation(sourceFolder.toString());
return feature;
} catch (IOException e) {
throw BuckminsterException.fromMessage(e, NLS.bind(Messages.unable_to_parse_feature_manifest_file_0, featureFile));
} catch (SAXException e) {
throw BuckminsterException.fromMessage(e, NLS.bind(Messages.unable_to_parse_feature_manifest_file_0, featureFile));
} finally {
IOUtils.close(input);
}
}
private static Map<String, String> readBuildProperties(File sourceFolder) throws CoreException {
InputStream input = null;
try {
File buildProps = new File(sourceFolder, IPDEConstants.BUILD_PROPERTIES_FILE);
input = new BufferedInputStream(new FileInputStream(buildProps));
return new BMProperties(input);
} catch (FileNotFoundException e) {
// This is OK. The build.properties file is not required
return Collections.emptyMap();
} catch (IOException e) {
throw BuckminsterException.wrap(e);
} finally {
IOUtils.close(input);
}
}
@Override
public boolean isUpToDate(Action action, IModelCache ctx, long prerequisiteAge, long oldestTarget) throws CoreException {
if (!super.isUpToDate(action, ctx, prerequisiteAge, oldestTarget))
// Prerequisite is younger
return false;
IPath outputDir = action.getProductBase();
if (outputDir == null)
throw BuckminsterException.fromMessage(NLS.bind(Messages.missing_product_base_in_0_actor, ID));
Map<String, ? extends Object> properties = ctx.getProperties();
outputDir = new Path(ExpandingProperties.expand(properties, outputDir.toPortableString(), 0));
// We could of course check that all files referenced by the
// artifacts.jar exists too but
// we trust that the output is consistent. If it isn't, someone has
// manually removed things
// from it and then reverted the timestamp of the folder. It would be
// somewhat paranoid to
// check for that.
return outputDir.append("content.jar").toFile().exists() && outputDir.append("artifacts.jar").toFile().exists(); //$NON-NLS-1$//$NON-NLS-2$
}
public void run(IActionContext ctx, File siteDefiner, File sourceFolder, List<File> productConfigs, File siteFolder,
Map<String, ? extends Object> properties) throws CoreException {
if (siteDefiner == null || siteFolder == null)
// Nothing to do
return;
// The site can be defined by a feature or a product.
//
String fileName = siteDefiner.getName();
String siteDescriptorName;
PublisherInfo info = new PublisherInfo();
info.setConfigurations(getConfigurations(properties));
Object siteDescriptor;
if (fileName.equals("feature.xml")) //$NON-NLS-1$
{
Feature feature = parse(sourceFolder, siteDefiner);
if (feature == null)
throw BuckminsterException.fromMessage(NLS.bind(Messages.unable_to_parse_feature_from_0, siteDefiner));
siteDescriptor = feature;
siteDescriptorName = feature.getLabel();
if (siteDescriptorName == null)
siteDescriptorName = feature.getId();
} else {
IProductDescriptor productDesc = getProductDescriptor(siteDefiner);
siteDescriptorName = productDesc.getProductName();
if (siteDescriptorName == null)
siteDescriptorName = productDesc.getId();
siteDescriptor = productDesc;
}
IProvisioningAgent agent = CorePlugin.getDefault().getResolverAgent();
URI siteURI = siteFolder.toURI();
SimpleArtifactRepositoryFactory arFactory = new SimpleArtifactRepositoryFactory();
arFactory.setAgent(agent);
IArtifactRepository ar = arFactory.create(siteURI, siteDescriptorName + " - Artifact Repository", null, null); //$NON-NLS-1$
String trueStr = Boolean.toString(true);
ar.setProperty(IRepository.PROP_COMPRESSED, trueStr);
ar.setProperty(Publisher.PUBLISH_PACK_FILES_AS_SIBLINGS, trueStr);
info.setArtifactRepository(ar);
info.setArtifactOptions(IPublisherInfo.A_PUBLISH | IPublisherInfo.A_INDEX);
SimpleMetadataRepositoryFactory mdrFactory = new SimpleMetadataRepositoryFactory();
mdrFactory.setAgent(agent);
IMetadataRepository mdr = mdrFactory.create(siteURI, siteDescriptorName, null, null);
mdr.setProperty(IRepository.PROP_COMPRESSED, trueStr);
info.setMetadataRepository(mdr);
addAdvice(siteDescriptor, info);
IPublisherAction[] actions = createActions(ctx, sourceFolder, siteDescriptor, siteFolder, productConfigs);
Publisher publisher = new Publisher(info);
IStatus result = publisher.publish(actions, new NullProgressMonitor());
if (result.getSeverity() == IStatus.ERROR)
throw new CoreException(result);
}
@Override
protected IStatus internalPerform(IActionContext ctx, IProgressMonitor monitor) throws CoreException {
Action action = ctx.getAction();
IPath outputPath = AbstractActor.getSingleAttributePath(ctx, action, false);
IPath site = null;
IPath siteDefiner = null;
CSpec cspec = action.getCSpec();
List<IPath> productConfigs = null;
Logger logger = PDEPlugin.getLogger();
for (Prerequisite preq : action.getPrerequisites()) {
if (ALIAS_SITE.equals(preq.getAlias())) {
// This prerequisite should appoint the site as a folder
//
Attribute rt = preq.getReferencedAttribute(cspec, ctx);
if (rt == null)
continue;
site = AbstractActor.getSingleAttributePath(ctx, rt, true);
continue;
}
if (ALIAS_SITE_DEFINER.equals(preq.getAlias())) {
// This prerequisite should appoint the site defining feature as
// a folder
//
Attribute rt = preq.getReferencedAttribute(cspec, ctx);
if (rt != null)
siteDefiner = AbstractActor.getSingleAttributePath(ctx, rt, false);
continue;
}
if (ALIAS_PRODUCT_CONFIGS.equals(preq.getAlias())) {
// This prerequisite should appoint the site defining feature as
// a folder
//
Attribute rt = preq.getReferencedAttribute(cspec, ctx);
productConfigs = AbstractActor.getPathList(ctx, rt, false);
continue;
}
if (preq.isContributor())
logger.warning("prerequisite with name %s is ignored by action %s " //$NON-NLS-1$
+ "(set contributor=\"false\" on the prerequisite to get rid of this warning)", preq.getName(), action.getQualifiedName()); //$NON-NLS-1$
}
if (site == null)
throw new MissingPrerequisiteException(action, ALIAS_SITE);
if (siteDefiner == null)
throw new MissingPrerequisiteException(action, ALIAS_SITE_DEFINER);
if (!outputPath.hasTrailingSeparator())
throw new IllegalArgumentException(NLS.bind(org.eclipse.buckminster.core.Messages.output_of_action_0_must_be_folder,
action.getQualifiedName()));
File outputDir = outputPath.toFile().getAbsoluteFile();
outputDir.mkdirs();
Map<String, ? extends Object> props = ctx.getProperties();
File siteDir = null;
File siteFile = site.toFile().getAbsoluteFile();
if (siteFile.isDirectory()) {
// If input is a folder, then output should be a mirror of that.
// Just
// copy the structure.
//
org.eclipse.buckminster.core.helpers.FileUtils.deepCopy(siteFile, outputDir, ConflictResolution.REPLACE, monitor);
siteDir = outputDir;
} else if (siteFile.getName().endsWith(".zip")) //$NON-NLS-1$
{
// We need a temporary folder where we expand the site since we want
// the output
// to contain a zip when the input is a zip.
siteDir = new File(props.get(KeyConstants.ACTION_TEMP).toString());
try {
FileUtils.unzipFile(siteFile, siteDir);
} catch (IOException e) {
throw BuckminsterException.wrap(e);
}
}
File siteDefinerFile = siteDefiner.toFile().getAbsoluteFile();
List<File> productConfigFiles;
if (productConfigs == null)
productConfigFiles = Collections.emptyList();
else {
productConfigFiles = new ArrayList<File>(productConfigs.size());
for (IPath path : productConfigs) {
File productConfigFile = path.toFile().getAbsoluteFile();
if (!productConfigFile.equals(siteDefinerFile))
productConfigFiles.add(productConfigFile);
}
}
run(ctx, siteDefinerFile, ctx.getComponentLocation().toFile(), productConfigFiles, siteDir, ctx.getProperties());
if (siteDir != null && siteDir != outputDir) {
// Zip the content of the siteDir. The name of the zip should
// be the same as the name of the input zip.
//
File outputZip = new File(outputDir, siteFile.getName());
try {
FileUtils.zip(siteDir.listFiles(), null, outputZip, FileUtils.createRootPathComputer(siteDir));
} catch (IOException e) {
throw BuckminsterException.wrap(e);
}
}
return Status.OK_STATUS;
}
private void addAdvice(Object siteDescriptor, PublisherInfo info) {
if (siteDescriptor instanceof Feature) {
Feature feature = (Feature) siteDescriptor;
FeatureEntry entries[] = feature.getEntries();
for (int i = 0; i < entries.length; i++) {
FeatureEntry entry = entries[i];
if (entry.isUnpack() && entry.isPlugin() && !entry.isRequires())
info.addAdvice(new BundleShapeAdvice(entry.getId(), Version.parseVersion(entry.getVersion()), IBundleShapeAdvice.DIR));
}
}
}
private void collectBundles(CSpec cspec, Map<IVersionedId, CSpec> cspecs, Set<ComponentIdentifier> cis, IActionContext ctx) throws CoreException {
ComponentIdentifier ci = cspec.getComponentIdentifier();
if (!cis.add(ci))
return;
if (IComponentType.OSGI_BUNDLE.equals(ci.getComponentTypeID()))
if (cspecs.put(new VersionedId(ci.getName(), ci.getVersion()), cspec) != null)
return;
Attribute refs = cspec.getAttribute(IPDEConstants.ATTRIBUTE_BUNDLE_JARS);
if (refs == null)
return;
for (Prerequisite preq : refs.getPrerequisites()) {
Attribute ref = preq.getReferencedAttribute(cspec, ctx);
if (ref != null)
collectBundles(ref.getCSpec(), cspecs, cis, ctx);
}
}
private Map<IVersionedId, CSpec> collectBundles(IActionContext ctx) throws CoreException {
CSpec cspec = ctx.getAction().getCSpec();
Map<IVersionedId, CSpec> cspecs = new HashMap<IVersionedId, CSpec>();
Set<ComponentIdentifier> cis = new HashSet<ComponentIdentifier>();
collectBundles(cspec, cspecs, cis, ctx);
return cspecs;
}
private void collectFeatures(CSpec cspec, Map<IVersionedId, CSpec> cspecs, IActionContext ctx) throws CoreException {
ComponentIdentifier ci = cspec.getComponentIdentifier();
if (cspecs.put(new VersionedId(ci.getName(), ci.getVersion()), cspec) != null)
return;
Attribute refs = cspec.getAttribute(IPDEConstants.ATTRIBUTE_FEATURE_REFS);
if (refs == null)
return;
for (Prerequisite preq : refs.getPrerequisites()) {
Attribute ref = preq.getReferencedAttribute(cspec, ctx);
if (ref != null)
collectFeatures(ref.getCSpec(), cspecs, ctx);
}
}
private Map<IVersionedId, CSpec> collectFeatures(IActionContext ctx) throws CoreException {
CSpec cspec = ctx.getAction().getCSpec();
Map<IVersionedId, CSpec> cspecs = new HashMap<IVersionedId, CSpec>();
collectFeatures(cspec, cspecs, ctx);
return cspecs;
}
private IPublisherAction[] createActions(IActionContext ctx, File sourceFolder, Object siteDescriptor, File siteFolder, List<File> productConfigs)
throws CoreException {
ArrayList<IPublisherAction> actions = new ArrayList<IPublisherAction>();
actions.add(new FeaturesAction(new File[] { new File(siteFolder, "features") }, collectFeatures(ctx))); //$NON-NLS-1$
actions.add(new BundlesAction(new File[] { new File(siteFolder, "plugins") }, collectBundles(ctx))); //$NON-NLS-1$
Map<String, String> buildProperties = readBuildProperties(sourceFolder);
if (siteDescriptor instanceof Feature) {
Feature topFeature = (Feature) siteDescriptor;
URLEntry[] siteRefs = topFeature.getDiscoverySites();
if (siteRefs.length > 0)
actions.add(new SiteReferencesAction(siteRefs));
URLEntry mirrorsSite = topFeature.getUpdateSite();
if (mirrorsSite != null && mirrorsSite.getURL() != null)
actions.add(new MirrorsSiteAction(mirrorsSite.getURL()));
ArrayList<IVersionedId> featureList = new ArrayList<IVersionedId>();
for (FeatureEntry fe : topFeature.getEntries()) {
if (fe.isPatch() || fe.isPlugin() || fe.isRequires())
continue;
featureList.add(new VersionedId(fe.getId(), fe.getVersion()));
}
actions.add(new CategoriesAction(sourceFolder, buildProperties, featureList));
} else {
IProductDescriptor product = (IProductDescriptor) siteDescriptor;
addProductAction(ctx, sourceFolder, actions, product, buildProperties, true);
}
for (File productConfigFile : productConfigs) {
File productSourceFolder = productConfigFile.getParentFile();
addProductAction(ctx, productSourceFolder, actions, getProductDescriptor(productConfigFile), readBuildProperties(productSourceFolder),
false);
}
if (!VersionConsolidator.getBooleanProperty(ctx.getProperties(), "site.retain.unpacked", false)) //$NON-NLS-1$
actions.add(new RemoveUnpackedSiblingsAction());
return actions.toArray(new IPublisherAction[actions.size()]);
}
}