/********************************************************************** * Copyright (c) 2005-2009 ant4eclipse project team. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Nils Hartmann, Daniel Kasmeroglu, Gerd Wuetherich **********************************************************************/ package org.ant4eclipse.ant.pde; import org.ant4eclipse.ant.core.FileListHelper; import org.ant4eclipse.ant.platform.core.MacroExecutionValues; import org.ant4eclipse.ant.platform.core.ScopedMacroDefinition; import org.ant4eclipse.ant.platform.core.delegate.MacroExecutionValuesProvider; import org.ant4eclipse.ant.platform.core.task.AbstractExecuteProjectTask; import org.ant4eclipse.lib.core.exception.Ant4EclipseException; import org.ant4eclipse.lib.core.util.Utilities; import org.ant4eclipse.lib.pde.PdeExceptionCode; import org.ant4eclipse.lib.pde.model.pluginproject.BundleSource; import org.ant4eclipse.lib.pde.model.product.ProductDefinition; import org.ant4eclipse.lib.pde.model.product.ProductDefinitionParser; import org.ant4eclipse.lib.pde.model.product.ProductOs; import org.ant4eclipse.lib.pde.tools.PlatformConfiguration; import org.ant4eclipse.lib.pde.tools.TargetPlatform; import org.ant4eclipse.lib.platform.PlatformExceptionCode; import org.ant4eclipse.lib.platform.model.resource.EclipseProject; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.taskdefs.MacroDef; import org.apache.tools.ant.types.FileList; import org.eclipse.osgi.service.resolver.BundleDescription; import org.osgi.framework.Version; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; /** * <p> * The {@link ExecuteProductTask} can be used to iterate over a product. It implements a loop over all the bundles * and/or plug-in-projects contained in a <code>.product</code> file. * <p> * * @author Daniel Kasmeroglu (Daniel.Kasmeroglu@Kasisoft.net) */ public class ExecuteProductTask extends AbstractExecuteProjectTask implements PdeExecutorValues, TargetPlatformAwareComponent { /** - */ private static enum Scope { /** - */ ForProduct, /** - */ ForEachFeature, /** - */ ForEachPlugin } /** - */ private static final String FRAGMENT_ORG_ECLIPSE_EQUINOX_LAUNCHER = "org.eclipse.equinox.launcher.%s.%s.%s"; /** - */ private static final String BUNDLE_ORG_ECLIPSE_EQUINOX_LAUNCHER = "org.eclipse.equinox.launcher"; /** - */ private static final String PROP_PRODUCTID = "product.id"; /** - */ private static final String PROP_PRODUCTNAME = "product.name"; /** - */ private static final String PROP_BASEDONFEATURES = "product.basedonfeatures"; /** - */ private static final String PROP_APPLICATIONID = "product.applicationid"; /** - */ private static final String PROP_LAUNCHERNAME = "product.launchername"; /** - */ private static final String PROP_VERSION = "product.version"; /** - */ private static final String PROP_VMARGS = "product.vmargs"; /** - */ private static final String PROP_PROGRAMARGS = "product.programargs"; /** - */ private static final String PROP_CONFIGINI = "product.configini"; /** - */ private static final String PROP_SPLASH_PLUGIN = "product.splashplugin"; /** - */ private static final String REF_NATIVE_LAUNCHER_FILELIST = "product.nativelauncher.filelist"; /** - */ private static final String REF_LAUNCHER_PLUGIN_FILELIST = "product.launcherplugin.filelist"; /** - */ private static final String REF_LAUNCHER_FRAGMENT_FILELIST = "product.launcherfragment.filelist"; /** - */ private static final String PROP_FEATUREID = "feature.id"; /** - */ private static final String PROP_FEATUREVERSION = "feature.version"; /** - */ private static final String PROP_PLUGINID = "plugin.id"; /** - */ private static final String PROP_PLUGINISSOURCE = "plugin.isSource"; /** - */ private static final String PROP_OSGIBUNDLES = "osgi.bundles"; /** - */ private static final String PROP_PLUGINPROJECTNAME = "plugin.projectName"; /** - */ private static final String PROP_PLUGINFILE = "plugin.file"; /** - */ private static final String PROP_PLUGINFILELIST = "plugin.filelist"; /** - */ private TargetPlatformAwareDelegate _targetPlatformAwareDelegate; /** - */ private File _product; /** - */ private ProductOs _os; /** * <p> * Creates a new instance of type {@link ExecuteProductTask}. * </p> */ public ExecuteProductTask() { super("executeProduct"); this._targetPlatformAwareDelegate = new TargetPlatformAwareDelegate(); } /** * Changes the location of the product configuration file. * * @param newproduct * The new location of the product file. Not <code>null</code>. */ public void setProduct(File newproduct) { this._product = newproduct; } /** * Changes the currently used os. * * @param newos * The os to be used for the execution process. Not <code>null</code>. */ public void setOs(ProductOs newos) { this._os = newos; } /** * {@inheritDoc} */ public String getTargetPlatformId() { return this._targetPlatformAwareDelegate.getTargetPlatformId(); } /** * {@inheritDoc} */ public boolean isTargetPlatformIdSet() { return this._targetPlatformAwareDelegate.isTargetPlatformIdSet(); } /** * {@inheritDoc} */ public void requireTargetPlatformIdSet() { this._targetPlatformAwareDelegate.requireTargetPlatformIdSet(); } /** * {@inheritDoc} */ public void setTargetPlatformId(String targetPlatformId) { this._targetPlatformAwareDelegate.setTargetPlatformId(targetPlatformId); } /** * {@inheritDoc} */ public String getPlatformConfigurationId() { return this._targetPlatformAwareDelegate.getPlatformConfigurationId(); } /** * {@inheritDoc} */ public boolean isPlatformConfigurationIdSet() { return this._targetPlatformAwareDelegate.isPlatformConfigurationIdSet(); } /** * {@inheritDoc} */ public void setPlatformConfigurationId(String platformConfigurationId) { this._targetPlatformAwareDelegate.setPlatformConfigurationId(platformConfigurationId); } /** * {@inheritDoc} */ public Object createDynamicElement(String name) { for (Scope scope : Scope.values()) { if (scope.name().equalsIgnoreCase(name)) { return createScopedMacroDefinition(scope.name()); } } return null; } /** * Loads the product definition. * * @return The loaded product definition. Not <code>null</code>. */ private ProductDefinition loadProductDefinition() { InputStream instream = null; try { instream = new FileInputStream(this._product); return ProductDefinitionParser.parseProductDefinition(instream); } catch (Ant4EclipseException ex) { if (ex.getExceptionCode() == PdeExceptionCode.INVALID_CONFIGURATION_VALUE) { throw new Ant4EclipseException(PdeExceptionCode.INVALID_PRODUCT_DEFINITION, this._product, ex.getMessage()); } else { throw ex; } } catch (IOException ex) { throw new BuildException(ex); } finally { Utilities.close(instream); } } /** * {@inheritDoc} */ @Override public void doExecute() { ProductDefinition productdef = loadProductDefinition(); // create a new target platform configuration PlatformConfiguration configuration = new PlatformConfiguration(); configuration.setPreferProjects(true); // fetch the target platform TargetPlatform targetplatform = this._targetPlatformAwareDelegate.getTargetPlatform(getWorkspace()); // StringMap properties = new StringMap(); // contributeForAll(properties, null, productdef, targetplatform); // execute scoped macro definitions for (ScopedMacroDefinition<String> scopedmacro : getScopedMacroDefinitions()) { Scope scope = Scope.valueOf(scopedmacro.getScope()); switch (scope) { case ForProduct: executeForProduct(productdef, scopedmacro.getMacroDef(), targetplatform); break; case ForEachFeature: executeForEachFeature(productdef, scopedmacro.getMacroDef(), targetplatform); break; // case ForEachTargetFeature: // executeForEachTargetFeature(productdef, scopedmacro.getMacroDef(), targetplatform); // break; case ForEachPlugin: executeForEachPlugin(productdef, scopedmacro.getMacroDef(), targetplatform); break; } } } /** * Executes the macro for the scope: {@link Scope#ForEachPlugin}. * * @param productdef * The productdefinition to be used. Not <code>null</code>. * @param macrodef * The macro constituting the scope. Not <code>null</code>. * @param forall * A bunch of properties used for all execution macros. Not <code>null</code>. * @param targetplatform * The TargetPlatform used to resolve the bundles against. Not <code>null</code>. */ private void executeForEachPlugin(final ProductDefinition productdef, MacroDef macrodef, final TargetPlatform targetplatform) { // iterate over all plug-in identifiers for (final String id : productdef.getPluginAndFragmentIds()) { // check if bundle exists if (!targetplatform.hasBundleDescription(id)) { throw new Ant4EclipseException(PdeExceptionCode.INVALID_PRODUCT_DEFINITION, productdef.getApplication(), "plugin '" + id + "' not found in workspace/target platform"); } if (targetplatform.matchesPlatformFilter(id)) { executeMacroInstance(macrodef, new MacroExecutionValuesProvider() { public MacroExecutionValues provideMacroExecutionValues(MacroExecutionValues values) { // set 'general' properties contributeForAll(values, productdef, targetplatform); // set the plugin id values.getProperties().put(PROP_PLUGINID, id); BundleDescription bundledesc = targetplatform.getBundleDescription(id); BundleSource bundlesource = (BundleSource) bundledesc.getUserObject(); if (bundlesource.isEclipseProject()) { // Plug-in is a source project contained in the workspace EclipseProject project = bundlesource.getAsEclipseProject(); File location = project.getFolder(); values.getProperties().put(PROP_PLUGINISSOURCE, "true"); values.getProperties().put(PROP_PLUGINFILE, location.getAbsolutePath()); values.getProperties().put(PROP_PLUGINPROJECTNAME, project.getSpecifiedName()); } else { // Plug-in comes from the target platform File location = bundlesource.getAsFile(); values.getProperties().put(PROP_PLUGINISSOURCE, "false"); values.getProperties().put(PROP_PLUGINFILE, location.getAbsolutePath()); values.getReferences().put(PROP_PLUGINFILELIST, FileListHelper.getFileList(location)); } // return the result return values; } }); } } } /** * Executes the macro for the scope: {@link Scope#ForEachFeature}. * * @param productdef * The productdefinition to be used. Not <code>null</code>. * @param macrodef * The macro constituting the scope. Not <code>null</code>. * @param forall * A bunch of properties used for all execution macros. Not <code>null</code>. * @param targetplatform * The TargetPlatform used to resolve the bundles against. Not <code>null</code>. */ private void executeForEachFeature(final ProductDefinition productdef, MacroDef macrodef, final TargetPlatform targetplatform) { for (final String featureid : productdef.getFeatureIds()) { executeMacroInstance(macrodef, new MacroExecutionValuesProvider() { public MacroExecutionValues provideMacroExecutionValues(MacroExecutionValues values) { // set 'general' properties contributeForAll(values, productdef, targetplatform); // set feature id values.getProperties().put(PROP_FEATUREID, featureid); // set version Version version = productdef.getFeatureVersion(featureid); values.getProperties().put(PROP_FEATUREVERSION, String.valueOf(version)); // return result return values; } }); } } // /** // * Executes the macro for the scope: {@link Scope#ForEachTargetFeature}. // * // * @param productdef // * The productdefinition to be used. Not <code>null</code>. // * @param macrodef // * The macro constituting the scope. Not <code>null</code>. // * @param targetplatform // * The TargetPlatform used to resolve the bundles against. Not <code>null</code>. // */ // private void executeForEachTargetFeature(ProductDefinition productdef, MacroDef macrodef, // TargetPlatform targetplatform) { // // StringMap properties = new StringMap(); // contributeForAll(properties, productdef); // // String[] featureids = productdef.getFeatureIds(); // for (String featureid : featureids) { // contributeForFeature(properties, productdef, featureid); // executeMacroInstance(macrodef, new LocalMacroExecutionValuesProvider(properties)); // } // // } /** * Executes the macro for the scope: {@link Scope#ForProduct}. * * @param productdef * The productdefinition to be used. Not <code>null</code>. * @param macrodef * The macro constituting the scope. Not <code>null</code>. * @param forall * A bunch of properties used for all execution macros. Not <code>null</code>. * @param targetplatform * The TargetPlatform used to resolve the bundles against. Not <code>null</code>. */ private void executeForProduct(final ProductDefinition productdef, MacroDef macrodef, final TargetPlatform targetplatform) { // execute the macro executeMacroInstance(macrodef, new MacroExecutionValuesProvider() { public MacroExecutionValues provideMacroExecutionValues(MacroExecutionValues values) { // set 'general' properties contributeForAll(values, productdef, targetplatform); // return result return values; } }); } /** * {@inheritDoc} */ @Override protected void preconditions() throws BuildException { super.preconditions(); requireWorkspaceDirectoryOrWorkspaceIdSet(); requireTargetPlatformIdSet(); if (this._product == null) { throw new BuildException("The attribute 'product' has to be set."); } if (!this._product.isFile()) { throw new BuildException(String.format("The product configuration '%s' is not a regular file.", this._product)); } if (this._os == null) { throw new BuildException("The attribute 'os' has to be set."); } // check if all scopes are known. that way the execution doesn't start long during operations // while an intermediate step fails. for (ScopedMacroDefinition<String> scopedmacro : getScopedMacroDefinitions()) { try { Scope.valueOf(scopedmacro.getScope()); } catch (IllegalArgumentException ex) { throw new Ant4EclipseException(PlatformExceptionCode.UNKNOWN_EXECUTION_SCOPE, scopedmacro.getScope()); } } } /** * <p> * Contributes general settings to the macro execution properties. * </p> * * @param values * @param productdef * The product definition instance providing the necessary information. Not <code>null</code>. * @param targetplatform * The TargetPlatform used to resolve the bundles against. Not <code>null</code>. */ private void contributeForAll(MacroExecutionValues values, ProductDefinition productdef, TargetPlatform targetplatform) { values.getProperties().put(PROP_PRODUCTNAME, productdef.getName()); values.getProperties().put(PROP_BASEDONFEATURES, String.valueOf(productdef.isBasedOnFeatures())); if (productdef.hasId()) { values.getProperties().put(PROP_PRODUCTID, productdef.getId()); } if (productdef.hasApplication()) { values.getProperties().put(PROP_APPLICATIONID, productdef.getApplication()); } values.getProperties().put(PROP_LAUNCHERNAME, productdef.hasLaunchername() ? productdef.getLaunchername() : "eclipse"); if (productdef.hasSplashplugin()) { values.getProperties().put(PROP_SPLASH_PLUGIN, productdef.getSplashplugin()); } values.getProperties().put(PROP_VERSION, String.valueOf(productdef.getVersion())); values.getProperties().put(PROP_VMARGS, productdef.getVmArgs(this._os)); values.getProperties().put(PROP_PROGRAMARGS, productdef.getProgramArgs(this._os)); values.getProperties().put(PROP_OSGIBUNDLES, ConfigurationHelper.getOsgiBundles(productdef, targetplatform)); String configini = productdef.getConfigIni(this._os); if (configini == null) { values.getProperties().put(PROP_CONFIGINI, ""); } else { int endofprojectname = configini.indexOf('/', 1); String projectname = configini.substring(1, endofprojectname); String childname = configini.substring(endofprojectname + 1); EclipseProject project = getWorkspace().getProject(projectname); File path = project.getChild(childname); values.getProperties().put(PROP_CONFIGINI, path.getAbsolutePath()); } // get executables FileList fileList = NativeLauncherHelper.getNativeLauncher(targetplatform); values.getReferences().put(REF_NATIVE_LAUNCHER_FILELIST, fileList); // set the launcher jars // - set the host launcher jar BundleDescription launcherHost = targetplatform.getBundleDescription(BUNDLE_ORG_ECLIPSE_EQUINOX_LAUNCHER); if (launcherHost == null) { throw new Ant4EclipseException(PdeExceptionCode.LAUNCHER_BUNDLE_DOES_NOT_EXIST, BUNDLE_ORG_ECLIPSE_EQUINOX_LAUNCHER); } BundleSource launcherHostSource = (BundleSource) launcherHost.getUserObject(); if (launcherHostSource.isEclipseProject()) { // TODO throw new RuntimeException(""); } FileList launcherHostFileList = FileListHelper.getFileList(launcherHostSource.getAsFile()); values.getReferences().put(REF_LAUNCHER_PLUGIN_FILELIST, launcherHostFileList); // - set the fragment launcher jar PlatformConfiguration configuration = targetplatform.getTargetPlatformConfiguration(); String launcherFragmentName = String.format(FRAGMENT_ORG_ECLIPSE_EQUINOX_LAUNCHER, configuration .getWindowingSystem(), configuration.getOperatingSystem(), configuration.getArchitecture()); BundleDescription launcherFragment = targetplatform.getBundleDescription(launcherFragmentName); if (launcherFragment == null) { throw new Ant4EclipseException(PdeExceptionCode.LAUNCHER_BUNDLE_DOES_NOT_EXIST, launcherFragmentName); } BundleSource launcherFragmentSource = (BundleSource) launcherFragment.getUserObject(); if (launcherFragmentSource.isEclipseProject()) { // TODO throw new RuntimeException(""); } FileList launcherFragmentFileList = FileListHelper.getFileList(launcherFragmentSource.getAsFile()); values.getReferences().put(REF_LAUNCHER_FRAGMENT_FILELIST, launcherFragmentFileList); } // /** // * Contributes feature specific settings to the macro execution properties. // * // * @param properties // * The properties used to be filled with the settings. Not <code>null</code>. // * @param productdef // * The product definition instance providing the necessary information. Not <code>null</code>. // * @param featureid // * The id of the feature which specific settings shall be applied. Neither <code>null</code> nor empty. // */ // private void contributeForFeature(StringMap properties, ProductDefinition productdef, String featureid) { // // Version version = productdef.getFeatureVersion(featureid); // // properties.put(PROP_FEATUREID, featureid); // properties.put(PROP_FEATUREVERSION, String.valueOf(version)); // // } // /** // * Contributes plugin specific settings to the macro execution properties. // * // * @param properties // * The properties used to be filled with the settings. Not <code>null</code>. // * @param references // * The references that will be accessible by the macro. Not <code>null</code>. // * @param productdef // * The product definition instance providing the necessary information. Not <code>null</code>. // * @param pluginid // * The id of the plugin which specific settings shall be applied. Neither <code>null</code> nor empty. // * @param targetplatform // * The TargetPlatform used to resolve the bundles against. Not <code>null</code>. // */ // private void contributeForPlugin(StringMap properties, Map<String, Object> references, ProductDefinition // productdef, // String pluginid, TargetPlatform targetplatform) { // A4ELogging.info("contributeForPlugin pluginId: '%s'", pluginid); // properties.put(PROP_PLUGINID, pluginid); // // if (!targetplatform.hasBundleDescription(pluginid)) { // throw new Ant4EclipseException(PdeExceptionCode.INVALID_PRODUCT_DEFINITION, productdef.getApplication(), // "plugin '" + pluginid + "' not found in workspace/target platform"); // } // // BundleDescription bundledesc = targetplatform.getBundleDescription(pluginid); // // BundleSource bundlesource = (BundleSource) bundledesc.getUserObject(); // // if (bundlesource.isEclipseProject()) { // // Plug-in is a source project contained in the workspace // EclipseProject project = bundlesource.getAsEclipseProject(); // File location = project.getFolder(); // properties.put(PROP_PLUGINISSOURCE, "true"); // properties.put(PROP_PLUGINFILE, location.getAbsolutePath()); // properties.put(PROP_PLUGINPROJECTNAME, project.getSpecifiedName()); // } else { // // Plug-in comes from the target platform // A4ELogging.info("contributeForPlugin bundlesource: '%s'", bundlesource); // File location = bundlesource.getAsFile(); // properties.put(PROP_PLUGINISSOURCE, "false"); // properties.put(PROP_PLUGINFILE, location.getAbsolutePath()); // references.put(PROP_PLUGINFILELIST, FileListHelper.getFileList(location)); // } // // } // /** // * Implementation of a custom MacroExecutionValuesProvider. It's purpose is to provide the several properties for // the // * scoped macros to make them functional. // */ // private static class LocalMacroExecutionValuesProvider implements MacroExecutionValuesProvider { // // /** - */ // private StringMap _properties; // // /** - */ // private Map<String, Object> _references; // // /** // * Initialises this provider using a specified product definition file. // * // * @param properties // * The properties to be set. Not <code>null</code>. // */ // public LocalMacroExecutionValuesProvider(StringMap properties) { // this(properties, null); // } // // /** // * Initialises this provider using a specified product definition file. // * // * @param properties // * The properties to be set. Not <code>null</code>. // * @param references // * The references that have to be applied. Maybe <code>null</code>. // */ // public LocalMacroExecutionValuesProvider(StringMap properties, Map<String, Object> references) { // this._properties = properties; // this._references = references; // } // // /** // * {@inheritDoc} // */ // public MacroExecutionValues provideMacroExecutionValues(MacroExecutionValues values) { // values.getProperties().putAll(this._properties); // if (this._references != null) { // values.getReferences().putAll(this._references); // } // return values; // } // // } /* ENDCLASS */ } /* ENDCLASS */