001: /*******************************************************************************
002: * Copyright (c) 2000, 2006 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.builder;
011:
012: import java.io.IOException;
013: import java.net.MalformedURLException;
014: import java.net.URL;
015: import java.util.*;
016: import org.eclipse.core.internal.boot.PlatformURLHandler;
017: import org.eclipse.core.internal.runtime.PlatformURLFragmentConnection;
018: import org.eclipse.core.internal.runtime.PlatformURLPluginConnection;
019: import org.eclipse.core.runtime.*;
020: import org.eclipse.osgi.service.resolver.BundleDescription;
021: import org.eclipse.osgi.service.resolver.HostSpecification;
022: import org.eclipse.osgi.util.NLS;
023: import org.eclipse.pde.internal.build.*;
024: import org.eclipse.pde.internal.build.site.PDEState;
025:
026: public class ClasspathComputer2_1 implements IClasspathComputer,
027: IPDEBuildConstants, IXMLConstants, IBuildPropertiesConstants {
028: private ModelBuildScriptGenerator generator;
029:
030: public ClasspathComputer2_1(ModelBuildScriptGenerator modelGenerator) {
031: this .generator = modelGenerator;
032: }
033:
034: /**
035: * Compute the classpath for the given jar.
036: * The path returned conforms to Parent / Self / Prerequisite
037: *
038: * @param model the plugin containing the jar compiled
039: * @param jar the jar for which the classpath is being compiled
040: * @return String the classpath
041: * @throws CoreException
042: */
043: public List getClasspath(BundleDescription model,
044: ModelBuildScriptGenerator.CompiledEntry jar)
045: throws CoreException {
046: List classpath = new ArrayList(20);
047: List pluginChain = new ArrayList(10);
048: Set addedPlugins = new HashSet(20);
049: String location = generator.getLocation(model);
050:
051: //PARENT
052: addPlugin(getPlugin(PI_BOOT, null), classpath, location);
053:
054: //SELF
055: addSelf(model, jar, classpath, location, pluginChain,
056: addedPlugins);
057:
058: //PREREQUISITE
059: addPrerequisites(model, classpath, location, pluginChain,
060: addedPlugins);
061:
062: return classpath;
063:
064: }
065:
066: /**
067: * Add the specified plugin (including its jars) and its fragments
068: * @param plugin
069: * @param classpath
070: * @param location
071: * @throws CoreException
072: */
073: private void addPlugin(BundleDescription plugin, List classpath,
074: String location) throws CoreException {
075: addRuntimeLibraries(plugin, classpath, location);
076: addFragmentsLibraries(plugin, classpath, location);
077: }
078:
079: /**
080: * Add the runtime libraries for the specified plugin.
081: * @param model
082: * @param classpath
083: * @param baseLocation
084: * @throws CoreException
085: */
086: private void addRuntimeLibraries(BundleDescription model,
087: List classpath, String baseLocation) throws CoreException {
088: String[] libraries = getClasspathEntries(model);
089: String root = generator.getLocation(model);
090: IPath base = Utils.makeRelative(new Path(root), new Path(
091: baseLocation));
092: Properties modelProps = getBuildPropertiesFor(model);
093: for (int i = 0; i < libraries.length; i++) {
094: addDevEntries(model, baseLocation, classpath, Utils
095: .getArrayFromString(generator.getBuildProperties()
096: .getProperty(
097: PROPERTY_OUTPUT_PREFIX
098: + libraries[i])));
099: addPathAndCheck(model.getSymbolicName(), base,
100: libraries[i], modelProps, classpath);
101: }
102: }
103:
104: /**
105: * Return the plug-in model object from the plug-in registry for the given
106: * plug-in identifier and version. If the plug-in is not in the registry then
107: * throw an exception.
108: *
109: * @param id the plug-in identifier
110: * @param version the plug-in version
111: * @return BundleDescription
112: * @throws CoreException if the specified plug-in version does not exist in the registry
113: */
114: private BundleDescription getPlugin(String id, String version)
115: throws CoreException {
116: return generator.getSite(false).getRegistry()
117: .getResolvedBundle(id, version);
118: }
119:
120: /**
121: * Add all fragments of the given plugin
122: * @param plugin
123: * @param classpath
124: * @param baseLocation
125: * @throws CoreException
126: */
127: private void addFragmentsLibraries(BundleDescription plugin,
128: List classpath, String baseLocation) throws CoreException {
129: // if plugin is not a plugin, it's a fragment and there is no fragment for a fragment. So we return.
130: BundleDescription[] fragments = plugin.getFragments();
131: if (fragments == null)
132: return;
133:
134: for (int i = 0; i < fragments.length; i++) {
135: if (fragments[i] == generator.getModel())
136: continue;
137: addPluginLibrariesToFragmentLocations(plugin, fragments[i],
138: classpath, baseLocation);
139: addRuntimeLibraries(fragments[i], classpath, baseLocation);
140: }
141: }
142:
143: /**
144: * There are cases where the plug-in only declares a library but the real JAR is under
145: * a fragment location. This method gets all the plugin libraries and place them in the
146: * possible fragment location.
147: *
148: * @param plugin
149: * @param fragment
150: * @param classpath
151: * @param baseLocation
152: * @throws CoreException
153: */
154: private void addPluginLibrariesToFragmentLocations(
155: BundleDescription plugin, BundleDescription fragment,
156: List classpath, String baseLocation) throws CoreException {
157: //TODO This methods causes the addition of a lot of useless entries. See bug #35544
158: //If we reintroduce the test below, we reintroduce the problem 35544
159: // if (fragment.getRuntime() != null)
160: // return;
161:
162: String[] libraries = getClasspathEntries(plugin);
163: String root = generator.getLocation(fragment);
164: IPath base = Utils.makeRelative(new Path(root), new Path(
165: baseLocation));
166: Properties modelProps = getBuildPropertiesFor(fragment);
167: for (int i = 0; i < libraries.length; i++) {
168: addPathAndCheck(fragment.getSymbolicName(), base,
169: libraries[i], modelProps, classpath);
170: }
171: }
172:
173: private Properties getBuildPropertiesFor(BundleDescription bundle) {
174: try {
175: return AbstractScriptGenerator.readProperties(generator
176: .getLocation(bundle), PROPERTIES_FILE, IStatus.OK);
177: } catch (CoreException e) {
178: //ignore
179: }
180: return null;
181: }
182:
183: // Add a path into the classpath for a given model
184: // path : The path to add
185: // classpath : The classpath in which we want to add this path
186: private void addPathAndCheck(String pluginId, IPath basePath,
187: String libraryName, Properties modelProperties,
188: List classpath) {
189: String path = basePath.append(libraryName).toString();
190: path = generator.replaceVariables(path,
191: pluginId == null ? false : generator
192: .getCompiledElements().contains(pluginId));
193: if (generator.getCompiledElements().contains(pluginId)) {
194: if (modelProperties == null
195: || modelProperties
196: .getProperty("source." + libraryName) != null) //$NON-NLS-1$
197: path = Utils
198: .getPropertyFormat(PROPERTY_BUILD_RESULT_FOLDER)
199: + '/' + path;
200: }
201: if (!classpath.contains(path))
202: classpath.add(path);
203: }
204:
205: private void addSelf(BundleDescription model,
206: ModelBuildScriptGenerator.CompiledEntry jar,
207: List classpath, String location, List pluginChain,
208: Set addedPlugins) throws CoreException {
209: // If model is a fragment, we need to add in the classpath the plugin to which it is related
210: HostSpecification host = model.getHost();
211: if (host != null) {
212: BundleDescription[] hosts = host.getHosts();
213: for (int i = 0; i < hosts.length; i++)
214: addPluginAndPrerequisites(hosts[i], classpath,
215: location, pluginChain, addedPlugins);
216: }
217:
218: // Add the libraries
219: Properties modelProperties = generator.getBuildProperties();
220: String jarOrder = (String) modelProperties
221: .get(PROPERTY_JAR_ORDER);
222: if (jarOrder == null) {
223: // if no jar order was specified in build.properties, we add all the libraries but the current one
224: // based on the order specified by the plugin.xml. Both library that we compile and .jar provided are processed
225: String[] libraries = getClasspathEntries(model);
226: if (libraries != null) {
227: for (int i = 0; i < libraries.length; i++) {
228: String libraryName = libraries[i];
229: if (jar.getName(false).equals(libraryName))
230: continue;
231:
232: boolean isSource = (modelProperties
233: .getProperty(PROPERTY_SOURCE_PREFIX
234: + libraryName) != null);
235: if (isSource) {
236: addDevEntries(
237: model,
238: location,
239: classpath,
240: Utils
241: .getArrayFromString(modelProperties
242: .getProperty(PROPERTY_OUTPUT_PREFIX
243: + libraryName)));
244: }
245: //Potential pb: here there maybe a nasty case where the libraries variable may refer to something which is part of the base
246: //but $xx$ will replace it by the $xx instead of $basexx. The solution is for the user to use the explicitly set the content
247: // of its build.property file
248: addPathAndCheck(model.getSymbolicName(),
249: Path.EMPTY, libraryName, modelProperties,
250: classpath);
251: }
252: }
253: } else {
254: // otherwise we add all the predecessor jars
255: String[] order = Utils.getArrayFromString(jarOrder);
256: for (int i = 0; i < order.length; i++) {
257: if (order[i].equals(jar.getName(false)))
258: break;
259: addDevEntries(
260: model,
261: location,
262: classpath,
263: Utils
264: .getArrayFromString((String) modelProperties
265: .get(PROPERTY_OUTPUT_PREFIX
266: + order[i])));
267: addPathAndCheck(model.getSymbolicName(), Path.EMPTY,
268: order[i], modelProperties, classpath);
269: }
270: // Then we add all the "pure libraries" (the one that does not contain source)
271: String[] libraries = getClasspathEntries(model);
272: for (int i = 0; i < libraries.length; i++) {
273: String libraryName = libraries[i];
274: if (modelProperties.get(PROPERTY_SOURCE_PREFIX
275: + libraryName) == null) {
276: //Potential pb: if the pure library is something that is being compiled (which is supposetly not the case, but who knows...)
277: //the user will get $basexx instead of $ws
278: addPathAndCheck(model.getSymbolicName(),
279: Path.EMPTY, libraryName, modelProperties,
280: classpath);
281: }
282: }
283: }
284:
285: // add extra classpath if it exists. this code is kept for backward compatibility
286: String extraClasspath = (String) modelProperties
287: .get(PROPERTY_JAR_EXTRA_CLASSPATH);
288: if (extraClasspath != null) {
289: String[] extra = Utils.getArrayFromString(extraClasspath,
290: ";,"); //$NON-NLS-1$
291:
292: for (int i = 0; i < extra.length; i++) {
293: //Potential pb: if the path refers to something that is being compiled (which is supposetly not the case, but who knows...)
294: //the user will get $basexx instead of $ws
295: addPathAndCheck(null, new Path(computeExtraPath(
296: extra[i], location)),
297: "", modelProperties, classpath); //$NON-NLS-1$
298: }
299: }
300:
301: // add extra classpath if it is specified for the given jar
302: String[] jarSpecificExtraClasspath = jar.getExtraClasspath();
303: for (int i = 0; i < jarSpecificExtraClasspath.length; i++) {
304: //Potential pb: if the path refers to something that is being compiled (which is supposetly not the case, but who knows...)
305: //the user will get $basexx instead of $ws
306: addPathAndCheck(null, new Path(computeExtraPath(
307: jarSpecificExtraClasspath[i], location)),
308: "", modelProperties, classpath); //$NON-NLS-1$
309: }
310: }
311:
312: /**
313: * Convenience method that compute the relative classpath of extra.classpath entries
314: * @param url a url
315: * @param location location used as a base location to compute the relative path
316: * @return String the relative path
317: * @throws CoreException
318: */
319: private String computeExtraPath(String url, String location)
320: throws CoreException {
321: String relativePath = null;
322:
323: String[] urlfragments = Utils.getArrayFromString(url, "/"); //$NON-NLS-1$
324:
325: // A valid platform url for a plugin has a leat 3 segments.
326: if (urlfragments.length > 2
327: && urlfragments[0].equals(PlatformURLHandler.PROTOCOL
328: + PlatformURLHandler.PROTOCOL_SEPARATOR)) {
329: String modelLocation = null;
330: if (urlfragments[1]
331: .equalsIgnoreCase(PlatformURLPluginConnection.PLUGIN))
332: modelLocation = generator.getLocation(generator
333: .getSite(false).getRegistry()
334: .getResolvedBundle(urlfragments[2]));
335:
336: if (urlfragments[1]
337: .equalsIgnoreCase(PlatformURLFragmentConnection.FRAGMENT))
338: modelLocation = generator.getLocation(generator
339: .getSite(false).getRegistry()
340: .getResolvedBundle(urlfragments[2]));
341:
342: if (urlfragments[1].equalsIgnoreCase("resource")) { //$NON-NLS-1$
343: String message = NLS.bind(Messages.exception_url,
344: generator.getPropertiesFileName() + "::" + url); //$NON-NLS-1$
345: throw new CoreException(new Status(IStatus.ERROR,
346: PI_PDEBUILD, EXCEPTION_MALFORMED_URL, message,
347: null));
348: }
349: if (modelLocation != null) {
350: for (int i = 3; i < urlfragments.length; i++) {
351: if (i == 3)
352: modelLocation += urlfragments[i];
353: else
354: modelLocation += '/' + urlfragments[i];
355: }
356: return relativePath = Utils.makeRelative(
357: new Path(modelLocation), new Path(location))
358: .toOSString();
359: }
360: }
361:
362: // Then it's just a regular URL, or just something that will be added at the end of the classpath for backward compatibility.......
363: try {
364: URL extraURL = new URL(url);
365: try {
366: relativePath = Utils.makeRelative(
367: new Path(Platform.resolve(extraURL).getFile()),
368: new Path(location)).toOSString();
369: } catch (IOException e) {
370: String message = NLS.bind(Messages.exception_url,
371: generator.getPropertiesFileName() + "::" + url); //$NON-NLS-1$
372: throw new CoreException(new Status(IStatus.ERROR,
373: PI_PDEBUILD, EXCEPTION_MALFORMED_URL, message,
374: e));
375: }
376: } catch (MalformedURLException e) {
377: String message = NLS.bind(Messages.exception_url,
378: PROPERTIES_FILE + "::" + url); //$NON-NLS-1$
379: throw new CoreException(new Status(IStatus.ERROR,
380: PI_PDEBUILD,
381: IPDEBuildConstants.EXCEPTION_MALFORMED_URL,
382: message, e));
383: }
384: return relativePath;
385: }
386:
387: //Add the prerequisite of a given plugin (target)
388: private void addPrerequisites(BundleDescription target,
389: List classpath, String baseLocation, List pluginChain,
390: Set addedPlugins) throws CoreException {
391:
392: if (pluginChain.contains(target)) {
393: if (target == getPlugin(PI_RUNTIME, null))
394: return;
395: String cycleString = ""; //$NON-NLS-1$
396: for (Iterator iter = pluginChain.iterator(); iter.hasNext();)
397: cycleString += iter.next().toString() + ", "; //$NON-NLS-1$
398: cycleString += target.toString();
399: String message = NLS.bind(Messages.error_pluginCycle,
400: cycleString);
401: throw new CoreException(new Status(IStatus.ERROR,
402: PI_PDEBUILD, EXCEPTION_CLASSPATH_CYCLE, message,
403: null));
404: }
405:
406: if (addedPlugins.contains(target)) {
407: return;
408: }
409:
410: // The first prerequisite is ALWAYS runtime
411: if (target != getPlugin(PI_RUNTIME, null))
412: addPluginAndPrerequisites(getPlugin(PI_RUNTIME, null),
413: classpath, baseLocation, pluginChain, addedPlugins);
414:
415: // add libraries from pre-requisite plug-ins. Don't worry about the export flag
416: // as all required plugins may be required for compilation.
417: BundleDescription[] requires = PDEState
418: .getDependentBundles(target);
419: if (requires != null) {
420: pluginChain.add(target);
421: for (int i = 0; i < requires.length; i++) {
422: BundleDescription plugin = getPlugin(requires[i]
423: .getSymbolicName(), requires[i].getVersion()
424: .toString());
425: if (plugin != null)
426: addPluginAndPrerequisites(plugin, classpath,
427: baseLocation, pluginChain, addedPlugins);
428: }
429: pluginChain.remove(target);
430: addedPlugins.add(target);
431: }
432:
433: }
434:
435: /**
436: * The pluginChain parameter is used to keep track of possible cycles. If prerequisite is already
437: * present in the chain it is not included in the classpath.
438: *
439: * @param target : the plugin for which we are going to introduce
440: * @param classpath
441: * @param baseLocation
442: * @param pluginChain
443: * @param addedPlugins
444: * @throws CoreException
445: */
446: private void addPluginAndPrerequisites(BundleDescription target,
447: List classpath, String baseLocation, List pluginChain,
448: Set addedPlugins) throws CoreException {
449: addPlugin(target, classpath, baseLocation);
450: addPrerequisites(target, classpath, baseLocation, pluginChain,
451: addedPlugins);
452: }
453:
454: /**
455: *
456: * @param model
457: * @param baseLocation
458: * @param classpath
459: */
460: private void addDevEntries(BundleDescription model,
461: String baseLocation, List classpath,
462: String[] jarSpecificEntries) {
463: if (generator.devEntries == null
464: && (jarSpecificEntries == null || jarSpecificEntries.length == 0))
465: return;
466:
467: String[] entries;
468: // if jarSpecificEntries is given, then it overrides devEntries
469: if (jarSpecificEntries != null && jarSpecificEntries.length > 0)
470: entries = jarSpecificEntries;
471: else
472: entries = generator.devEntries.getDevClassPath(model
473: .getSymbolicName());
474:
475: IPath root = Utils.makeRelative(new Path(generator
476: .getLocation(model)), new Path(baseLocation));
477: for (int i = 0; i < entries.length; i++) {
478: addPathAndCheck(model.getSymbolicName(), root, entries[i],
479: null, classpath);
480: }
481: }
482:
483: //Return the jar name from the classpath
484: private String[] getClasspathEntries(BundleDescription bundle)
485: throws CoreException {
486: return generator.getClasspathEntries(bundle);
487: }
488: }
|