/***************************************************************************** * 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.ant.actor; import java.io.IOException; import java.io.PrintStream; import java.net.URL; import java.util.Collections; import java.util.Map; import org.eclipse.buckminster.ant.AntBuilderConstants; import org.eclipse.buckminster.ant.AntRunner; import org.eclipse.buckminster.ant.Messages; import org.eclipse.buckminster.core.CorePlugin; import org.eclipse.buckminster.core.actor.AbstractActor; import org.eclipse.buckminster.core.actor.IActionContext; import org.eclipse.buckminster.core.common.model.ExpandingProperties; import org.eclipse.buckminster.core.cspec.PathGroup; import org.eclipse.buckminster.core.helpers.FileUtils; import org.eclipse.buckminster.core.helpers.TextUtils; import org.eclipse.buckminster.runtime.BuckminsterException; import org.eclipse.buckminster.runtime.MonitorUtils; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.FileLocator; import org.eclipse.core.runtime.IConfigurationElement; import org.eclipse.core.runtime.IExtensionRegistry; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; import org.eclipse.osgi.util.NLS; import org.osgi.framework.Bundle; /** * @author ken1 * @author Thomas Hallgren */ public class AntActor extends AbstractActor { public static final String ACTOR_ID = "ant"; //$NON-NLS-1$ public static final String PROP_BUILD_FILE_ID = "buildFileId"; //$NON-NLS-1$ public static final String PROP_TARGETS = "targets"; //$NON-NLS-1$ public static final String PROP_BUILD_FILE = "buildFile"; //$NON-NLS-1$ private static final String BUILD_SCRIPT_POINT = AntBuilderConstants.PLUGIN_ID + ".buildScripts"; //$NON-NLS-1$ private final static String BUILD_SCRIPT_ID = "id"; //$NON-NLS-1$ private final static String BUILD_SCRIPT_RESOURCE = "resource"; //$NON-NLS-1$ public static IPath getBuildFileExtension(String buildFileId) throws CoreException { IConfigurationElement resourceElem = null; IExtensionRegistry er = Platform.getExtensionRegistry(); for (IConfigurationElement elem : er.getConfigurationElementsFor(BUILD_SCRIPT_POINT)) { if (elem.getAttribute(BUILD_SCRIPT_ID).equals(buildFileId)) { resourceElem = elem; break; } } if (resourceElem == null) throw BuckminsterException.fromMessage(NLS.bind(Messages.AntActor_No_extension_found_defines_0_1, AntActor.PROP_BUILD_FILE_ID, buildFileId)); // The resource must be loaded by the bundle that contributes it // String contributor = resourceElem.getContributor().getName(); Bundle contributorBundle = Platform.getBundle(contributor); if (contributorBundle == null) throw BuckminsterException.fromMessage(NLS.bind(Messages.AntActor_Unable_to_load_bundle_0, contributor)); URL rsURL = contributorBundle.getResource(resourceElem.getAttribute(BUILD_SCRIPT_RESOURCE)); if (rsURL == null) throw BuckminsterException.fromMessage(NLS.bind(Messages.AntActor_Extension_found_using_0_1_appoints_non_existing_resource, AntActor.PROP_BUILD_FILE_ID, buildFileId)); try { rsURL = FileLocator.toFileURL(rsURL); } catch (IOException e) { throw BuckminsterException.wrap(e); } if (!"file".equalsIgnoreCase(rsURL.getProtocol())) //$NON-NLS-1$ // // This should never happen. It's a resource in an active plug-in // right? // throw BuckminsterException.fromMessage(NLS.bind(Messages.AntActor_Unexpected_protocol_0, rsURL.getProtocol())); return FileUtils.getFileAsPath(rsURL); } private static void addPathGroupArraysToProperties(Map<String, PathGroup[]> namedPGA, Map<String, String> props) { if (namedPGA == null) return; StringBuilder sp_bld = new StringBuilder(); StringBuilder fs_bld = new StringBuilder(); StringBuilder key_bld = new StringBuilder(); for (Map.Entry<String, PathGroup[]> namedPG : namedPGA.entrySet()) { PathGroup[] pathGroups = namedPG.getValue(); boolean singleton = (pathGroups.length == 1); fs_bld.setLength(0); sp_bld.setLength(0); for (PathGroup pathGroup : pathGroups) { IPath basePath = pathGroup.getBase(); String base = basePath.toOSString(); fs_bld.append('?'); // Start of path group marker fs_bld.append(base); IPath[] paths = pathGroup.getPaths(); if (paths.length > 1) singleton = false; if (singleton) sp_bld.append(base); for (IPath path : paths) { String osPath = path.toOSString(); fs_bld.append(FileUtils.PATH_SEP); fs_bld.append(osPath); if (singleton) { if (!basePath.hasTrailingSeparator()) sp_bld.append(FileUtils.FILE_SEP); sp_bld.append(osPath); } } } String propKey = namedPG.getKey(); key_bld.setLength(0); key_bld.append("fs:"); //$NON-NLS-1$ key_bld.append(propKey); props.put(key_bld.toString(), fs_bld.toString()); if (singleton) { key_bld.setLength(0); key_bld.append("sp:"); //$NON-NLS-1$ key_bld.append(propKey); props.put(key_bld.toString(), sp_bld.toString()); } } } protected void addActorPathGroups(IActionContext ctx, Map<String, PathGroup[]> namedPathGroupArrays) throws CoreException { } protected final IPath getBuildFile(IActionContext ctx) throws CoreException { // script name must always be relative to project root // String buildFileId = getBuildFileIdProperty(ctx); String buildFile = getBuildFileProperty(ctx); if (buildFile == null) { if (buildFileId == null) throw BuckminsterException.fromMessage(NLS.bind(Messages.AntActor_Property_not_set_0, AntActor.PROP_BUILD_FILE)); buildFileId = ExpandingProperties.expand(ctx.getProperties(), buildFileId, 0); return getBuildFileExtension(buildFileId); } if (buildFileId != null) throw BuckminsterException.fromMessage(NLS.bind(Messages.AntActor_Properties_0_and_1_are_mutually_exclusive, AntActor.PROP_BUILD_FILE, AntActor.PROP_BUILD_FILE_ID)); buildFile = ExpandingProperties.expand(ctx.getProperties(), buildFile, 0); IPath buildFilePath = new Path(buildFile); if (!buildFilePath.isAbsolute()) buildFilePath = ctx.getComponentLocation().append(buildFilePath); return buildFilePath; } protected String getBuildFileIdProperty(IActionContext ctx) throws CoreException { return TextUtils.notEmptyTrimmedString(getActorProperty(AntActor.PROP_BUILD_FILE_ID)); } protected String getBuildFileProperty(IActionContext ctx) throws CoreException { return TextUtils.notEmptyTrimmedString(getActorProperty(AntActor.PROP_BUILD_FILE)); } protected Map<String, String> getDefaultProperties(IActionContext ctx) throws CoreException { return Collections.emptyMap(); } protected final String[] getTargets(IActionContext ctx) throws CoreException { // if the user has explicitly entered a blank field, return null to // indicate 'use the default target' // String tlist = getTargetsString(ctx); if (tlist.length() == 0) return null; // otherwise assume it's a ws separated list of targets // it's the users responsibility to ensure that the targets are supposed // to run in that specific order // most commonly, it's just a single target name, of course // // split on ws and return it. it's already trimmed around the edges so // there can be no strings that are empty or with embedded/surrounding // ws // return tlist.split("\\s+"); //$NON-NLS-1$ } protected final String getTargetsString(IActionContext ctx) { String tlist = getActorProperty(AntActor.PROP_TARGETS); // if no targets field has been defined, use the action name // return tlist == null ? ctx.getAction().getName() : tlist.trim(); } @Override protected IStatus internalPerform(IActionContext ctx, IProgressMonitor monitor) throws CoreException { monitor = MonitorUtils.ensureNotNull(monitor); monitor.beginTask(null, 100); monitor.subTask(ctx.getAction().getQualifiedName()); PrintStream origOut = System.out; PrintStream origErr = System.err; try { IPath buildFile = getBuildFile(ctx); // We add the installer hints onto the context properties. // ExpandingProperties<String> props = new ExpandingProperties<String>(); for (Map.Entry<String, ? extends Object> entry : ctx.getProperties().entrySet()) { Object val = entry.getValue(); props.put(entry.getKey(), val == null ? null : val.toString()); } props.putAll(getDefaultProperties(ctx)); Map<String, PathGroup[]> namedPathGroupArrays = ctx.getNamedPathGroupArrays(); addActorPathGroups(ctx, namedPathGroupArrays); addPathGroupArraysToProperties(namedPathGroupArrays, props); props.put("basedir", ctx.getComponentLocation().toOSString()); //$NON-NLS-1$ MonitorUtils.worked(monitor, 10); System.setOut(ctx.getOutputStream()); System.setErr(ctx.getErrorStream()); AntRunner runner = new AntRunner(); runner.setBuildFileLocation(buildFile); runner.setExecutionTargets(getTargets(ctx)); runner.setBuildLogger("org.eclipse.buckminster.ant.support.AntBuildLogger"); //$NON-NLS-1$ runner.addUserProperties(props); runner.run(MonitorUtils.subMonitor(monitor, 90)); return Status.OK_STATUS; } catch (OperationCanceledException e) { return Status.CANCEL_STATUS; } catch (Error e) { Throwable t = BuckminsterException.unwind(e); CorePlugin.getLogger().error(t, t.toString()); throw e; } catch (RuntimeException e) { Throwable t = BuckminsterException.unwind(e); CorePlugin.getLogger().error(t, t.toString()); throw e; } catch (CoreException e) { Throwable t = BuckminsterException.unwind(e); CorePlugin.getLogger().error(t, t.toString()); throw e; } finally { System.setOut(origOut); System.setErr(origErr); monitor.done(); } } }