001: /*******************************************************************************
002: * Copyright (c) 2006, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM - Initial API and implementation
010: *******************************************************************************/package org.eclipse.pde.internal.build;
011:
012: import java.io.*;
013: import java.util.*;
014: import org.eclipse.core.runtime.*;
015: import org.eclipse.osgi.service.resolver.*;
016: import org.eclipse.osgi.util.NLS;
017: import org.eclipse.pde.build.Constants;
018: import org.eclipse.pde.internal.build.site.PDEState;
019: import org.eclipse.update.core.IFeature;
020: import org.osgi.framework.Version;
021:
022: public class FeatureGenerator extends AbstractScriptGenerator {
023:
024: private static final String FEATURE_PLATFORM_LAUNCHERS = "org.eclipse.platform.launchers"; //$NON-NLS-1$
025: private static final String FEATURE_EXECUTABLE = "org.eclipse.equinox.executable"; //$NON-NLS-1$
026: private static final String BUNDLE_OSGI = "org.eclipse.osgi"; //$NON-NLS-1$
027: private static final String BUNDLE_LAUNCHER = "org.eclipse.equinox.launcher"; //$NON-NLS-1$
028:
029: private String featureId = null;
030: private String productFile = null;
031: private String[] pluginList = null;
032: private String[] fragmentList = null;
033: private String[] featureList = null;
034:
035: private boolean includeLaunchers = true;
036:
037: private ProductFile product = null;
038:
039: private boolean verify = false;
040:
041: private Properties antProperties;
042:
043: /*
044: * Create and return a new Set with the given contents. If the arg
045: * is null then return an empty set.
046: */
047: private static Set createSet(String[] contents) {
048: if (contents == null)
049: return new LinkedHashSet(0);
050: Set result = new LinkedHashSet(contents.length);
051: for (int i = 0; i < contents.length; i++)
052: if (contents[i] != null)
053: result.add(contents[i]);
054: return result;
055: }
056:
057: /* (non-Javadoc)
058: * @see org.eclipse.pde.internal.build.AbstractScriptGenerator#generate()
059: */
060: public void generate() throws CoreException {
061: AbstractScriptGenerator.setStaticAntProperties(antProperties);
062: try {
063: initialize();
064: Set plugins = createSet(pluginList);
065: Set features = createSet(featureList);
066: Set fragments = createSet(fragmentList);
067: if (product != null) {
068: if (product.useFeatures()) {
069: features.addAll(product.getFeatures());
070: } else {
071: plugins.addAll(product.getPlugins(false));
072: fragments.addAll(product.getFragments());
073: }
074: }
075: try {
076: createFeature(featureId, plugins, fragments, features);
077: } catch (FileNotFoundException e) {
078: IStatus status = new Status(IStatus.ERROR,
079: IPDEBuildConstants.PI_PDEBUILD,
080: EXCEPTION_PRODUCT_FORMAT, NLS.bind(
081: Messages.error_creatingFeature, e
082: .getLocalizedMessage()), e);
083: throw new CoreException(status);
084: }
085: } finally {
086: AbstractScriptGenerator.setStaticAntProperties(null);
087: }
088: }
089:
090: public void setProductFile(String productFile) {
091: this .productFile = productFile;
092: }
093:
094: public void setPluginList(String[] pluginList) {
095: this .pluginList = pluginList;
096: }
097:
098: public void setFeatureList(String[] featureList) {
099: this .featureList = featureList;
100: }
101:
102: public void setFragmentList(String[] fragmentList) {
103: this .fragmentList = fragmentList;
104: }
105:
106: public void setFeatureId(String featureId) {
107: this .featureId = featureId;
108: }
109:
110: public void setIncludeLaunchers(boolean includeLaunchers) {
111: this .includeLaunchers = includeLaunchers;
112: }
113:
114: private void initialize() throws CoreException {
115: //get rid of old feature that we will be overwriting, we don't want it in the state accidently.
116: File dir = new File(getWorkingDirectory(),
117: IPDEBuildConstants.DEFAULT_FEATURE_LOCATION + '/'
118: + featureId);
119: File xml = new File(dir, Constants.FEATURE_FILENAME_DESCRIPTOR);
120: if (xml.exists()) {
121: xml.delete();
122: }
123:
124: if (productFile != null
125: && !productFile.startsWith("${") && productFile.length() > 0) { //$NON-NLS-1$
126: String productPath = findFile(productFile, false);
127: File f = null;
128: if (productPath != null) {
129: f = new File(productPath);
130: } else {
131: // couldn't find productFile, try it as a path directly
132: f = new File(productFile);
133: if (!f.exists() || !f.isFile()) {
134: // doesn't exist, try it as a path relative to the working directory
135: f = new File(getWorkingDirectory(), productFile);
136: if (!f.exists() || !f.isFile()) {
137: f = new File(
138: getWorkingDirectory()
139: + "/" + DEFAULT_PLUGIN_LOCATION, productFile); //$NON-NLS-1$
140: }
141: }
142: }
143: if (f.exists() && f.isFile()) {
144: product = new ProductFile(f.getAbsolutePath(), null);
145: } else {
146: IStatus error = new Status(IStatus.ERROR, PI_PDEBUILD,
147: EXCEPTION_PRODUCT_FILE, NLS.bind(
148: Messages.exception_missingElement,
149: productFile), null);
150: throw new CoreException(error);
151: }
152: }
153: }
154:
155: /*
156: * Based on the version of OSGi that we have in our state, add the appropriate plug-ins/fragments/features
157: * for the launcher.
158: */
159: private void addLauncher(PDEState state, Set plugins,
160: Set fragments, Set features) {
161: BundleDescription bundle = state.getResolvedBundle(BUNDLE_OSGI);
162: if (bundle == null)
163: return;
164: Version version = bundle.getVersion();
165: if (version.compareTo(new Version("3.3")) < 0) { //$NON-NLS-1$
166: // we have an OSGi version that is less than 3.3 so add the old launcher
167: features.add(FEATURE_PLATFORM_LAUNCHERS);
168: } else {
169: // we have OSGi version 3.3 or greater so add the executable feature
170: // and the launcher plug-in and fragments
171: IFeature executableFeature = null;
172: try {
173: executableFeature = getSite(false).findFeature(
174: FEATURE_EXECUTABLE, null, false);
175: } catch (CoreException e) {
176: // ignore
177: }
178: if (executableFeature != null) {
179: /* the executable feature includes the launcher and fragments already */
180: features.add(FEATURE_EXECUTABLE);
181: } else {
182: // We don't have the executable feature, at least try and get the launcher jar and fragments
183: plugins.add(BUNDLE_LAUNCHER);
184: List configs = getConfigInfos();
185: // only include the fragments for the platforms we are attempting to build, since the others
186: // probably aren't around
187: for (Iterator iterator = configs.iterator(); iterator
188: .hasNext();) {
189: Config config = (Config) iterator.next();
190: String fragment = BUNDLE_LAUNCHER + '.'
191: + config.getWs() + '.' + config.getOs();
192: //macosx doesn't have the arch on its fragment
193: if (config.getOs().compareToIgnoreCase("macosx") != 0) //$NON-NLS-1$
194: fragment += '.' + config.getArch();
195:
196: fragments.add(fragment);
197: }
198: }
199: }
200: }
201:
202: /**
203: * Generate a feature that includes the given plug-ins, fragments and features.
204: * Feature order matters at compile time if there is dependencies between the features' contents.
205: * Make sure to pass an ordered set if this matters.
206: * @param feature - Name of the feature to generate
207: * @param plugins - plug-ins to include
208: * @param fragments - fragments to include
209: * @param features - An ordered set of features to include
210: * @throws CoreException
211: * @throws FileNotFoundException
212: */
213: protected void createFeature(String feature, Set plugins,
214: Set fragments, Set features) throws CoreException,
215: FileNotFoundException {
216: String location = IPDEBuildConstants.DEFAULT_FEATURE_LOCATION
217: + '/' + feature;
218: File directory = new File(getWorkingDirectory(), location);
219: if (!directory.exists())
220: directory.mkdirs();
221:
222: PDEState state = verify ? getSite(false).getRegistry() : null;
223: BundleHelper helper = BundleHelper.getDefault();
224:
225: if (verify && includeLaunchers)
226: addLauncher(state, plugins, fragments, features);
227:
228: //Create feature.xml
229: File file = new File(directory,
230: Constants.FEATURE_FILENAME_DESCRIPTOR);
231: OutputStream output = new BufferedOutputStream(
232: new FileOutputStream(file));
233: XMLWriter writer = null;
234: try {
235: writer = new XMLWriter(output);
236: } catch (UnsupportedEncodingException e) {
237: //should not happen
238: return;
239: }
240: try {
241: Map parameters = new HashMap();
242: Dictionary environment = new Hashtable(3);
243:
244: parameters.put("id", feature); //$NON-NLS-1$
245: parameters.put("version", "1.0.0"); //$NON-NLS-1$ //$NON-NLS-2$
246: writer.startTag("feature", parameters, true); //$NON-NLS-1$
247:
248: boolean fragment = false;
249: List configs = getConfigInfos();
250: //we do the generic config first as a special case
251: configs.remove(Config.genericConfig());
252: Iterator configIterator = configs.iterator();
253: Iterator listIter = plugins.iterator();
254: if (!listIter.hasNext()) {
255: // no plugins, do fragments
256: fragment = true;
257: listIter = fragments.iterator();
258: }
259: for (Config currentConfig = Config.genericConfig(); currentConfig != null; currentConfig = (Config) configIterator
260: .next()) {
261: environment.put("osgi.os", currentConfig.getOs()); //$NON-NLS-1$
262: environment.put("osgi.ws", currentConfig.getWs()); //$NON-NLS-1$
263: environment.put("osgi.arch", currentConfig.getArch()); //$NON-NLS-1$
264: for (; listIter.hasNext();) {
265: String name = (String) listIter.next();
266: boolean unpack = true;
267: boolean writeBundle = !verify;
268: if (verify) {
269: BundleDescription bundle = state
270: .getResolvedBundle(name);
271: if (bundle != null) {
272: //Bundle resolved, write it out if it matches the current config
273: String filterSpec = bundle
274: .getPlatformFilter();
275: if (filterSpec == null
276: || helper.createFilter(filterSpec)
277: .match(environment)) {
278: writeBundle = true;
279: unpack = guessUnpack(
280: bundle,
281: (String[]) state
282: .getExtraData()
283: .get(
284: new Long(
285: bundle
286: .getBundleId())));
287: if (currentConfig.equals(Config
288: .genericConfig())) {
289: listIter.remove();
290: }
291: }
292: } else {
293: //Bundle did not resolve, only ok if it was because of the platform filter
294: BundleDescription[] bundles = state
295: .getState().getBundles(name);
296: boolean error = true;
297: if (bundles != null && bundles.length > 0) {
298: ResolverError[] errors = state
299: .getState().getResolverErrors(
300: bundles[0]);
301: for (int i = 0; i < errors.length; i++) {
302: if ((errors[i].getType() & ResolverError.PLATFORM_FILTER) != 0) {
303: //didn't match config, this is ok
304: error = false;
305: break;
306: }
307: }
308: }
309: if (error) {
310: //throw error
311: String message = NLS
312: .bind(
313: Messages.exception_missingPlugin,
314: name);
315: throw new CoreException(new Status(
316: IStatus.ERROR, PI_PDEBUILD,
317: EXCEPTION_PLUGIN_MISSING,
318: message, null));
319: }
320: }
321: }
322:
323: if (writeBundle) {
324: parameters.clear();
325: parameters.put("id", name); //$NON-NLS-1$
326: parameters.put("version", "0.0.0"); //$NON-NLS-1$//$NON-NLS-2$
327: parameters.put(
328: "unpack", unpack ? "true" : "false"); //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$
329: if (!currentConfig.equals(Config
330: .genericConfig())) {
331: parameters.put("os", currentConfig.getOs()); //$NON-NLS-1$
332: parameters.put("ws", currentConfig.getWs()); //$NON-NLS-1$
333: parameters.put(
334: "arch", currentConfig.getArch()); //$NON-NLS-1$
335: }
336: if (fragment)
337: parameters.put("fragment", "true"); //$NON-NLS-1$ //$NON-NLS-2$
338: writer.printTag(
339: "plugin", parameters, true, true, true); //$NON-NLS-1$
340: }
341:
342: if (!fragment && !listIter.hasNext()
343: && fragments.size() > 0) {
344: //finished the list of plugins, do the fragments now
345: fragment = true;
346: listIter = fragments.iterator();
347: }
348: }
349: if (!verify || !configIterator.hasNext()) {
350: break;
351: } else if (plugins.size() > 0) {
352: fragment = false;
353: listIter = plugins.iterator();
354: } else {
355: listIter = fragments.iterator();
356: }
357: }
358:
359: for (Iterator iter = features.iterator(); iter.hasNext();) {
360: String name = (String) iter.next();
361: if (verify) {
362: //this will throw an exception if the feature is not found.
363: getSite(false).findFeature(name, null, true);
364: }
365: parameters.clear();
366: parameters.put("id", name); //$NON-NLS-1$
367: parameters.put("version", "0.0.0"); //$NON-NLS-1$//$NON-NLS-2$
368: writer.printTag(
369: "includes", parameters, true, true, true); //$NON-NLS-1$
370: }
371: writer.endTag("feature"); //$NON-NLS-1$
372: } finally {
373: writer.close();
374: }
375:
376: //create build.properties
377: file = new File(directory, IPDEBuildConstants.PROPERTIES_FILE);
378: Properties prop = new Properties();
379: prop.put("pde", "marker"); //$NON-NLS-1$ //$NON-NLS-2$
380: OutputStream stream = null;
381: try {
382: stream = new BufferedOutputStream(
383: new FileOutputStream(file));
384: prop.store(stream,
385: "Marker File so that the file gets written"); //$NON-NLS-1$
386: stream.flush();
387: } catch (IOException e) {
388: // nothing for now
389: } finally {
390: if (stream != null) {
391: try {
392: stream.close();
393: } catch (IOException e1) {
394: // nothing
395: }
396: }
397: }
398: }
399:
400: public void setVerify(boolean verify) {
401: this .verify = verify;
402: reportResolutionErrors = verify;
403: }
404:
405: public boolean guessUnpack(BundleDescription bundle,
406: String[] classpath) {
407: if (bundle == null)
408: return true;
409:
410: // launcher fragments are a special case, they have no bundle-classpath and they must
411: //be unpacked
412: if (bundle.getHost() != null
413: && bundle.getName().startsWith(BUNDLE_LAUNCHER))
414: return true;
415:
416: if (new File(bundle.getLocation()).isFile())
417: return false;
418:
419: if (classpath.length == 0)
420: return false;
421:
422: for (int i = 0; i < classpath.length; i++) {
423: if (classpath[i].equals(".")) //$NON-NLS-1$
424: return false;
425: }
426: return true;
427: }
428:
429: public void setImmutableAntProperties(Properties properties) {
430: antProperties = properties;
431: }
432: }
|