Skip to content

Commit 6ce14a0

Browse files
author
Curtis Windatt
committed
Bug 250126: [p2] Use p2 to provision for launching, support self-hosting
https://bugs.eclipse.org/bugs/show_bug.cgi?id=250126
1 parent a155a38 commit 6ce14a0

File tree

8 files changed

+298
-26
lines changed

8 files changed

+298
-26
lines changed

ui/org.eclipse.pde.core/src/org/eclipse/pde/internal/core/P2Utils.java

Lines changed: 152 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,24 @@
1717
import java.net.URL;
1818
import java.util.*;
1919
import org.eclipse.core.runtime.*;
20+
import org.eclipse.equinox.internal.p2.engine.*;
2021
import org.eclipse.equinox.internal.provisional.frameworkadmin.BundleInfo;
22+
import org.eclipse.equinox.internal.provisional.p2.director.PlannerHelper;
23+
import org.eclipse.equinox.internal.provisional.p2.engine.*;
24+
import org.eclipse.equinox.internal.provisional.p2.metadata.*;
25+
import org.eclipse.equinox.internal.provisional.p2.metadata.VersionRange;
26+
import org.eclipse.equinox.internal.provisional.p2.metadata.MetadataFactory.InstallableUnitDescription;
2127
import org.eclipse.equinox.internal.provisional.simpleconfigurator.manipulator.SimpleConfiguratorManipulator;
28+
import org.eclipse.osgi.service.resolver.*;
2229
import org.eclipse.pde.core.plugin.IPluginBase;
2330
import org.eclipse.pde.core.plugin.IPluginModelBase;
2431
import org.eclipse.pde.internal.build.BundleHelper;
2532
import org.eclipse.pde.internal.build.IPDEBuildConstants;
2633
import org.eclipse.pde.internal.core.plugin.PluginBase;
34+
import org.osgi.framework.Constants;
2735

2836
/**
29-
* Utilities to read and write bundle and source information files.
37+
* Utilities to read and write p2 files
3038
*
3139
* @since 3.4
3240
*/
@@ -39,6 +47,14 @@ public class P2Utils {
3947

4048
public static final String P2_FLAVOR_DEFAULT = "tooling"; //$NON-NLS-1$
4149

50+
public static final ITouchpointType TOUCHPOINT_OSGI = MetadataFactory.createTouchpointType("org.eclipse.equinox.p2.osgi", Version.createOSGi(1, 0, 0)); //$NON-NLS-1$
51+
private static final String CAPABILITY_NS_OSGI_BUNDLE = "osgi.bundle"; //$NON-NLS-1$
52+
private static final String CAPABILITY_NS_OSGI_FRAGMENT = "osgi.fragment"; //$NON-NLS-1$
53+
public static final String TYPE_ECLIPSE_BUNDLE = "bundle"; //$NON-NLS-1$
54+
public static final String NAMESPACE_ECLIPSE_TYPE = "org.eclipse.equinox.p2.eclipse.type"; //$NON-NLS-1$
55+
public static final IProvidedCapability BUNDLE_CAPABILITY = MetadataFactory.createProvidedCapability(NAMESPACE_ECLIPSE_TYPE, TYPE_ECLIPSE_BUNDLE, Version.createOSGi(1, 0, 0));
56+
public static final String CAPABILITY_NS_JAVA_PACKAGE = "java.package"; //$NON-NLS-1$
57+
4258
/**
4359
* Returns bundles defined by the 'bundles.info' file in the
4460
* specified location, or <code>null</code> if none. The "bundles.info" file
@@ -296,4 +312,139 @@ public static URL writeBundlesTxt(Map bundles, int defaultStartLevel, boolean de
296312
}
297313
}
298314

315+
/**
316+
* Returns whether a profile with the given ID exists in a profile registry
317+
* stored in the give p2 data area.
318+
*
319+
* @param profileID id of the profile to check
320+
* @param p2DataArea data area where the profile registry is
321+
* @return whether the profile exists
322+
*/
323+
public static boolean profileExists(String profileID, File p2DataArea) {
324+
// Create a custom registry that checks the profile in the proper location
325+
File engineArea = new File(p2DataArea, EngineActivator.ID);
326+
File registryArea = new File(engineArea, SimpleProfileRegistry.DEFAULT_STORAGE_DIR);
327+
registryArea.mkdirs();
328+
SimpleProfileRegistry customRegistry = new SimpleProfileRegistry(registryArea);
329+
330+
return customRegistry.containsProfile(profileID);
331+
}
332+
333+
/**
334+
* Generates a profile containing metadata for all of the bundles in the provided collection.
335+
* The profile will have the given profile ID and will be persisted in the profile registry
336+
* directory inside the given p2 data area.
337+
*
338+
* @param profileID the ID to be used when creating the profile, if a profile with the same name exists, it will be overwritten
339+
* @param p2DataArea the directory which contains p2 data including the profile registry, if the directory path doesn't exist it will be created
340+
* @param bundles the collection of bundles to create metadata for and add to the profile
341+
*
342+
* @throws CoreException if the profile cannot be generated
343+
*/
344+
public static void createProfile(String profileID, File p2DataArea, Collection bundles) throws CoreException {
345+
// TODO Could avoid using internal p2 code if multiple instances of the p2 servers could be run on the same vm (being looked at in 3.6)
346+
347+
// Create a custom registry that stores the profile in the proper location
348+
File engineArea = new File(p2DataArea, EngineActivator.ID);
349+
File registryArea = new File(engineArea, SimpleProfileRegistry.DEFAULT_STORAGE_DIR);
350+
registryArea.mkdirs();
351+
SimpleProfileRegistry customRegistry = new SimpleProfileRegistry(registryArea);
352+
353+
// Delete any previous profiles with the same ID
354+
customRegistry.removeProfile(profileID);
355+
356+
// Create the profile
357+
IProfile profile = null;
358+
Properties props = new Properties();
359+
props.setProperty(IProfile.PROP_INSTALL_FOLDER, registryArea.getAbsolutePath());
360+
profile = customRegistry.addProfile(profileID, props);
361+
362+
// Create metadata for the bundles
363+
Collection ius = new ArrayList(bundles.size());
364+
for (Iterator iterator = bundles.iterator(); iterator.hasNext();) {
365+
IPluginModelBase model = (IPluginModelBase) iterator.next();
366+
BundleDescription bundle = model.getBundleDescription();
367+
ius.add(createBundleIU(bundle));
368+
}
369+
370+
// Create operands to install the metadata
371+
Operand[] operands = new Operand[ius.size() * 2];
372+
int i = 0;
373+
for (Iterator iter = ius.iterator(); iter.hasNext();) {
374+
IInstallableUnit iu = (IInstallableUnit) iter.next();
375+
operands[i++] = new InstallableUnitOperand(null, iu);
376+
operands[i++] = new InstallableUnitPropertyOperand(iu, "org.eclipse.equinox.p2.internal.inclusion.rules", null, PlannerHelper.createOptionalInclusionRule(iu));
377+
}
378+
379+
// Add the metadata to the profile
380+
ProvisioningContext context = new ProvisioningContext();
381+
PhaseSet phaseSet = DefaultPhaseSet.createDefaultPhaseSet(DefaultPhaseSet.PHASE_CHECK_TRUST | DefaultPhaseSet.PHASE_COLLECT | DefaultPhaseSet.PHASE_CONFIGURE | DefaultPhaseSet.PHASE_UNCONFIGURE | DefaultPhaseSet.PHASE_UNINSTALL);
382+
File profileDataDirectory = customRegistry.getProfileDataDirectory(profile.getProfileId());
383+
EngineSession session = new EngineSession(profile, profileDataDirectory, context);
384+
IStatus status = phaseSet.perform(new ActionManager(), session, profile, operands, context, new NullProgressMonitor());
385+
386+
if (!status.isOK() && status.getSeverity() != IStatus.CANCEL) {
387+
throw new CoreException(status);
388+
}
389+
}
390+
391+
/**
392+
* Creates an installable unit from a bundle description
393+
*
394+
* @param bd bundle description to create metadata for
395+
* @return an installable unit
396+
*/
397+
private static IInstallableUnit createBundleIU(BundleDescription bd) {
398+
InstallableUnitDescription iu = new MetadataFactory.InstallableUnitDescription();
399+
iu.setSingleton(bd.isSingleton());
400+
iu.setId(bd.getSymbolicName());
401+
iu.setVersion(Version.fromOSGiVersion(bd.getVersion()));
402+
iu.setFilter(bd.getPlatformFilter());
403+
iu.setTouchpointType(TOUCHPOINT_OSGI);
404+
405+
boolean isFragment = bd.getHost() != null;
406+
407+
//Process the required bundles
408+
BundleSpecification requiredBundles[] = bd.getRequiredBundles();
409+
ArrayList reqsDeps = new ArrayList();
410+
if (isFragment)
411+
reqsDeps.add(MetadataFactory.createRequiredCapability(CAPABILITY_NS_OSGI_BUNDLE, bd.getHost().getName(), VersionRange.fromOSGiVersionRange(bd.getHost().getVersionRange()), null, false, false));
412+
for (int j = 0; j < requiredBundles.length; j++)
413+
reqsDeps.add(MetadataFactory.createRequiredCapability(CAPABILITY_NS_OSGI_BUNDLE, requiredBundles[j].getName(), VersionRange.fromOSGiVersionRange(requiredBundles[j].getVersionRange()), null, requiredBundles[j].isOptional(), false));
414+
415+
// Process the import packages
416+
ImportPackageSpecification osgiImports[] = bd.getImportPackages();
417+
for (int i = 0; i < osgiImports.length; i++) {
418+
// TODO we need to sort out how we want to handle wild-carded dynamic imports - for now we ignore them
419+
ImportPackageSpecification importSpec = osgiImports[i];
420+
String importPackageName = importSpec.getName();
421+
if (importPackageName.indexOf('*') != -1)
422+
continue;
423+
VersionRange versionRange = VersionRange.fromOSGiVersionRange(importSpec.getVersionRange());
424+
//TODO this needs to be refined to take into account all the attribute handled by imports
425+
boolean isOptional = importSpec.getDirective(Constants.RESOLUTION_DIRECTIVE).equals(ImportPackageSpecification.RESOLUTION_DYNAMIC) || importSpec.getDirective(Constants.RESOLUTION_DIRECTIVE).equals(ImportPackageSpecification.RESOLUTION_OPTIONAL);
426+
reqsDeps.add(MetadataFactory.createRequiredCapability(CAPABILITY_NS_JAVA_PACKAGE, importPackageName, versionRange, null, isOptional, false));
427+
}
428+
iu.setRequiredCapabilities((IRequiredCapability[]) reqsDeps.toArray(new IRequiredCapability[reqsDeps.size()]));
429+
430+
// Create set of provided capabilities
431+
ArrayList providedCapabilities = new ArrayList();
432+
providedCapabilities.add(MetadataFactory.createProvidedCapability(IInstallableUnit.NAMESPACE_IU_ID, bd.getSymbolicName(), Version.fromOSGiVersion(bd.getVersion())));
433+
providedCapabilities.add(MetadataFactory.createProvidedCapability(CAPABILITY_NS_OSGI_BUNDLE, bd.getSymbolicName(), Version.fromOSGiVersion(bd.getVersion())));
434+
435+
// Process the export package
436+
ExportPackageDescription exports[] = bd.getExportPackages();
437+
for (int i = 0; i < exports.length; i++) {
438+
//TODO make sure that we support all the refinement on the exports
439+
providedCapabilities.add(MetadataFactory.createProvidedCapability(CAPABILITY_NS_JAVA_PACKAGE, exports[i].getName(), Version.fromOSGiVersion(exports[i].getVersion())));
440+
}
441+
// Here we add a bundle capability to identify bundles
442+
providedCapabilities.add(BUNDLE_CAPABILITY);
443+
if (isFragment)
444+
providedCapabilities.add(MetadataFactory.createProvidedCapability(CAPABILITY_NS_OSGI_FRAGMENT, bd.getHost().getName(), Version.fromOSGiVersion(bd.getVersion())));
445+
446+
iu.setCapabilities((IProvidedCapability[]) providedCapabilities.toArray(new IProvidedCapability[providedCapabilities.size()]));
447+
return MetadataFactory.createInstallableUnit(iu);
448+
}
449+
299450
}

ui/org.eclipse.pde.launching/src/org/eclipse/pde/internal/launching/launcher/LaunchConfigurationHelper.java

Lines changed: 35 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,6 @@
1010
*******************************************************************************/
1111
package org.eclipse.pde.internal.launching.launcher;
1212

13-
import org.eclipse.pde.launching.IPDELauncherConstants;
14-
15-
import org.eclipse.pde.internal.launching.PDELaunchingPlugin;
16-
17-
1813
import java.io.*;
1914
import java.net.URL;
2015
import java.util.*;
@@ -28,6 +23,9 @@
2823
import org.eclipse.pde.core.plugin.TargetPlatform;
2924
import org.eclipse.pde.internal.build.IPDEBuildConstants;
3025
import org.eclipse.pde.internal.core.*;
26+
import org.eclipse.pde.internal.launching.IPDEConstants;
27+
import org.eclipse.pde.internal.launching.PDELaunchingPlugin;
28+
import org.eclipse.pde.launching.IPDELauncherConstants;
3129

3230
/**
3331
* Contains helper methods for launching an Eclipse Runtime Workbench
@@ -37,6 +35,12 @@ public class LaunchConfigurationHelper {
3735
private static final String PROP_OSGI_FRAMEWORK = "osgi.framework"; //$NON-NLS-1$
3836
private static final String PROP_OSGI_BUNDLES = "osgi.bundles"; //$NON-NLS-1$
3937
private static final String PROP_P2_DATA_AREA = "eclipse.p2.data.area"; //$NON-NLS-1$
38+
private static final String DEFAULT_PROFILE_NAME = "SelfHostingProfile"; //$NON-NLS-1$
39+
40+
/**
41+
* The p2 data area will be set to a directory with this name inside the configuration folder
42+
*/
43+
private static final String DEFAULT_P2_DIRECTORY = ".p2"; //$NON-NLS-1$
4044

4145
public static void synchronizeManifests(ILaunchConfiguration config, File configDir) {
4246
try {
@@ -83,7 +87,7 @@ private static String getSubstitutedString(String text) throws CoreException {
8387
return mgr.performStringSubstitution(text);
8488
}
8589

86-
public static Properties createConfigIniFile(ILaunchConfiguration configuration, String productID, Map bundles, Map bundlesWithStartLevels, File directory) throws CoreException {
90+
public static Properties createConfigIniFile(ILaunchConfiguration configuration, String productID, Map bundles, Map bundlesWithStartLevels, File configurationDirectory) throws CoreException {
8791
Properties properties = null;
8892
// if we are to generate a config.ini, start with the values in the target platform's config.ini - bug 141918
8993
if (configuration.getAttribute(IPDELauncherConstants.CONFIG_GENERATE_DEFAULT, true)) {
@@ -98,12 +102,6 @@ else if (productID == null || !productID.equals(properties.get("eclipse.product"
98102
String bundleList = properties.getProperty(PROP_OSGI_BUNDLES);
99103
if (bundleList != null)
100104
properties.setProperty(PROP_OSGI_BUNDLES, computeOSGiBundles(TargetPlatformHelper.stripPathInformation(bundleList), bundles, bundlesWithStartLevels));
101-
String dataArea = properties.getProperty(PROP_P2_DATA_AREA);
102-
if (dataArea != null) {
103-
// Make the p2 data area in the configuration area itself, rather than a sibling of the configuration
104-
// area (which is a the root pde.core shared metadata area) @see bug 272810
105-
properties.setProperty(PROP_P2_DATA_AREA, "@config.dir/.p2"); //$NON-NLS-1$
106-
}
107105
} else {
108106
String templateLoc = configuration.getAttribute(IPDELauncherConstants.CONFIG_TEMPLATE_LOCATION, (String) null);
109107
if (templateLoc != null) {
@@ -120,37 +118,56 @@ else if (productID == null || !productID.equals(properties.get("eclipse.product"
120118
} else {
121119
properties = new Properties();
122120
}
123-
if (!directory.exists()) {
124-
directory.mkdirs();
121+
if (!configurationDirectory.exists()) {
122+
configurationDirectory.mkdirs();
125123
}
126124
String osgiBundles = properties.getProperty(PROP_OSGI_BUNDLES);
127125
int start = configuration.getAttribute(IPDELauncherConstants.DEFAULT_START_LEVEL, 4);
128126
properties.put("osgi.bundles.defaultStartLevel", Integer.toString(start)); //$NON-NLS-1$
129127
boolean autostart = configuration.getAttribute(IPDELauncherConstants.DEFAULT_AUTO_START, false);
130128

131-
// if we are launching using P2, write out P2 files (bundles.txt) and add P2 property to config.ini
129+
// Special processing for launching with p2
132130
if (osgiBundles != null && osgiBundles.indexOf(IPDEBuildConstants.BUNDLE_SIMPLE_CONFIGURATOR) != -1 && bundles.containsKey(IPDEBuildConstants.BUNDLE_SIMPLE_CONFIGURATOR)) {
131+
132+
// Write out P2 files (bundles.txt)
133133
URL bundlesTxt = null;
134134
boolean usedefault = configuration.getAttribute(IPDELauncherConstants.USE_DEFAULT, true);
135135
boolean useFeatures = configuration.getAttribute(IPDELauncherConstants.USEFEATURES, false);
136136
if (usedefault || useFeatures) {
137-
bundlesTxt = P2Utils.writeBundlesTxt(bundlesWithStartLevels, 4, false, directory, osgiBundles);
137+
bundlesTxt = P2Utils.writeBundlesTxt(bundlesWithStartLevels, 4, false, configurationDirectory, osgiBundles);
138138
} else {
139-
bundlesTxt = P2Utils.writeBundlesTxt(bundlesWithStartLevels, start, autostart, directory, null);
139+
bundlesTxt = P2Utils.writeBundlesTxt(bundlesWithStartLevels, start, autostart, configurationDirectory, null);
140140
}
141141

142+
// Add bundles.txt as p2 config data
142143
if (bundlesTxt != null) {
143144
properties.setProperty("org.eclipse.equinox.simpleconfigurator.configUrl", bundlesTxt.toString()); //$NON-NLS-1$
144145
// if we have simple configurator and update configurator together, ensure update doesn't reconcile
145146
if (bundles.get(IPDEBuildConstants.BUNDLE_UPDATE_CONFIGURATOR) != null) {
146147
properties.setProperty("org.eclipse.update.reconcile", "false"); //$NON-NLS-1$ //$NON-NLS-2$
147148
}
148149
}
150+
151+
// Make the p2 data area in the configuration area itself, rather than a sibling of the configuration
152+
// area (which is a the root pde.core shared metadata area) @see bug 272810
153+
properties.setProperty(PROP_P2_DATA_AREA, "@config.dir/".concat(DEFAULT_P2_DIRECTORY)); //$NON-NLS-1$
154+
155+
// Generate a profile to launch with, set the profile id as the default
156+
if (configuration.getAttribute(IPDELauncherConstants.GENERATE_PROFILE, false)) {
157+
String profileID = DEFAULT_PROFILE_NAME;
158+
File p2DataArea = new File(configurationDirectory, DEFAULT_P2_DIRECTORY);
159+
160+
// Unless we are restarting an existing profile, generate/overwrite the profile
161+
if (!configuration.getAttribute(IPDEConstants.RESTART, false) || !P2Utils.profileExists(profileID, p2DataArea)) {
162+
P2Utils.createProfile(profileID, p2DataArea, bundles.values());
163+
}
164+
properties.setProperty("eclipse.p2.profile", profileID); //$NON-NLS-1$
165+
}
149166
}
150167

151168
setBundleLocations(bundles, properties, autostart);
152169

153-
save(new File(directory, "config.ini"), properties); //$NON-NLS-1$
170+
save(new File(configurationDirectory, "config.ini"), properties); //$NON-NLS-1$
154171
return properties;
155172
}
156173

ui/org.eclipse.pde.launching/src/org/eclipse/pde/launching/AbstractPDELaunchConfiguration.java

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,7 @@ public void launch(ILaunchConfiguration configuration, String mode, ILaunch laun
6161
}
6262
throw e;
6363
}
64-
// if restarting, remove the restart flag from the launch config
65-
if (configuration.getAttribute(IPDEConstants.RESTART, false) && configuration instanceof ILaunchConfigurationWorkingCopy) {
66-
((ILaunchConfigurationWorkingCopy) configuration).setAttribute(IPDEConstants.RESTART, false);
67-
((ILaunchConfigurationWorkingCopy) configuration).doSave();
68-
}
64+
6965
VMRunnerConfiguration runnerConfig = new VMRunnerConfiguration(getMainClass(), getClasspath(configuration));
7066
runnerConfig.setVMArguments(getVMArguments(configuration));
7167
runnerConfig.setProgramArguments(getProgramArguments(configuration));

ui/org.eclipse.pde.launching/src/org/eclipse/pde/launching/IPDELauncherConstants.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,4 +393,12 @@ public interface IPDELauncherConstants {
393393
* @since 3.6
394394
*/
395395
String ECLIPSE_APPLICATION_LAUNCH_CONFIGURATION_TYPE = "org.eclipse.pde.ui.RuntimeWorkbench"; //$NON-NLS-1$
396+
397+
/**
398+
* Launch configuration attribute key. The value is a boolean specifying
399+
* whether a p2 profile should be
400+
*
401+
* This attribute could be promoted as API in IPDELauncherConstants
402+
*/
403+
String GENERATE_PROFILE = "generateProfile"; //$NON-NLS-1$
396404
}

ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/PDEUIMessages.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2677,6 +2677,10 @@ public class PDEUIMessages extends NLS {
26772677

26782678
public static String ProductInfoSection_features;
26792679

2680+
public static String ProfileBlock_0;
2681+
2682+
public static String ProfileBlock_1;
2683+
26802684
public static String ImportPackageSection_goToPackage;
26812685

26822686
public static String ExportPackageSection_findReferences;

0 commit comments

Comments
 (0)