| /******************************************************************************* |
| * Copyright (c) 2008, 2017 Code 9 and others. |
| * |
| * This |
| * program and the accompanying materials are made available under the terms of |
| * the Eclipse Public License 2.0 which accompanies this distribution, and is |
| * available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * Code 9 - initial API and implementation |
| * IBM - ongoing development |
| * Pascal Rapicault - Support for bundled macosx http://bugs.eclipse.org/57349 |
| ******************************************************************************/ |
| package org.eclipse.equinox.p2.publisher.eclipse; |
| |
| import java.io.File; |
| import java.util.*; |
| import org.eclipse.core.runtime.*; |
| import org.eclipse.equinox.internal.p2.core.helpers.FileUtils; |
| import org.eclipse.equinox.internal.p2.publisher.eclipse.BrandingIron; |
| import org.eclipse.equinox.internal.p2.publisher.eclipse.ExecutablesDescriptor; |
| import org.eclipse.equinox.p2.metadata.*; |
| import org.eclipse.equinox.p2.metadata.MetadataFactory.InstallableUnitDescription; |
| import org.eclipse.equinox.p2.metadata.MetadataFactory.InstallableUnitFragmentDescription; |
| import org.eclipse.equinox.p2.metadata.expression.IMatchExpression; |
| import org.eclipse.equinox.p2.publisher.*; |
| import org.eclipse.equinox.p2.repository.artifact.IArtifactDescriptor; |
| import org.eclipse.equinox.spi.p2.publisher.PublisherHelper; |
| import org.eclipse.osgi.service.environment.Constants; |
| |
| /** |
| * Given the description of an executable, this action publishes optionally |
| * non-destructively brands the executable, publishes the resultant artifacts |
| * and publishes the required IUs to identify the branded executable, configure |
| * the executable and set it up as the launcher for a profile. |
| * <p> |
| * This action works on one platform configuration only. |
| * <p> |
| * This action consults the following types of advice: |
| * </ul> |
| * <li>{@link IBrandingAdvice}</li> |
| * </ul> |
| */ |
| public class EquinoxExecutableAction extends AbstractPublisherAction { |
| private static String TYPE = "executable"; //$NON-NLS-1$ |
| |
| protected String configSpec; |
| protected String idBase; |
| protected Version version; |
| protected ExecutablesDescriptor executables; |
| protected String flavor; |
| |
| protected EquinoxExecutableAction() { |
| //hidden |
| } |
| |
| public EquinoxExecutableAction(ExecutablesDescriptor executables, String configSpec, String idBase, Version version, String flavor) { |
| this.executables = executables; |
| this.configSpec = configSpec; |
| this.idBase = idBase == null ? "org.eclipse" : idBase; //$NON-NLS-1$ |
| this.version = version; |
| this.flavor = flavor; |
| } |
| |
| @Override |
| public IStatus perform(IPublisherInfo publisherinfo, IPublisherResult result, IProgressMonitor monitor) { |
| setPublisherInfo(publisherinfo); |
| ExecutablesDescriptor brandedExecutables = brandExecutables(executables); |
| try { |
| if (publishExecutableIU(brandedExecutables, result)) |
| publishExecutableCU(brandedExecutables, result); |
| publishExecutableSetter(brandedExecutables, result); |
| } finally { |
| if (brandedExecutables.isTemporary()) |
| FileUtils.deleteAll(brandedExecutables.getLocation()); |
| } |
| return Status.OK_STATUS; |
| } |
| |
| /** |
| * Publishes the IUs that cause the executable to be actually set as the launcher for |
| * the profile |
| */ |
| private void publishExecutableSetter(ExecutablesDescriptor brandedExecutables, IPublisherResult result) { |
| InstallableUnitDescription iud = new MetadataFactory.InstallableUnitDescription(); |
| String executableName = brandedExecutables.getExecutableName(); |
| String id = getExecutableId() + '.' + executableName; |
| iud.setId(id); |
| iud.setVersion(version); |
| iud.setTouchpointType(PublisherHelper.TOUCHPOINT_OSGI); |
| iud.setCapabilities(new IProvidedCapability[] {createSelfCapability(id, version)}); |
| iud.setFilter(createFilterSpec(configSpec)); |
| Map<String, String> touchpointData = new HashMap<>(); |
| touchpointData.put("configure", "setLauncherName(name:" + executableName + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| touchpointData.put("unconfigure", "setLauncherName()"); //$NON-NLS-1$ //$NON-NLS-2$ |
| iud.addTouchpointData(MetadataFactory.createTouchpointData(touchpointData)); |
| result.addIU(MetadataFactory.createInstallableUnit(iud), IPublisherResult.ROOT); |
| } |
| |
| /** |
| * Publishes IUs and CUs for the files that make up the launcher for a given |
| * ws/os/arch combination. |
| */ |
| protected boolean publishExecutableIU(ExecutablesDescriptor execDescriptor, IPublisherResult result) { |
| String[] config = parseConfigSpec(configSpec); |
| if (execDescriptor.getFiles().length == 0 && (config.length == 0 || CONFIG_ANY.equalsIgnoreCase(config[0]))) { |
| return false; //no cu required |
| } |
| boolean publishCU = true; |
| |
| // Create the IU for the executable |
| InstallableUnitDescription iu = new MetadataFactory.InstallableUnitDescription(); |
| String id = getExecutableId(); |
| iu.setId(id); |
| iu.setVersion(version); |
| IMatchExpression<IInstallableUnit> filter = createFilterSpec(configSpec); |
| iu.setFilter(filter); |
| iu.setSingleton(true); |
| iu.setTouchpointType(PublisherHelper.TOUCHPOINT_NATIVE); |
| String namespace = ConfigCUsAction.getAbstractCUCapabilityNamespace(idBase, TYPE, flavor, configSpec); |
| String capabilityId = ConfigCUsAction.getAbstractCUCapabilityId(idBase, TYPE, flavor, configSpec); |
| IProvidedCapability executableCapability = MetadataFactory.createProvidedCapability(namespace, capabilityId, version); |
| IProvidedCapability selfCapability = createSelfCapability(id, version); |
| iu.setCapabilities(new IProvidedCapability[] {selfCapability, executableCapability}); |
| |
| //Create the artifact descriptor. we have several files so no path on disk |
| if (execDescriptor.getFiles().length == 0) { |
| publishCU = false; |
| } else { |
| IArtifactKey key = PublisherHelper.createBinaryArtifactKey(id, version); |
| iu.setArtifacts(new IArtifactKey[] {key}); |
| IArtifactDescriptor descriptor = PublisherHelper.createArtifactDescriptor(info, key, null); |
| publishArtifact(descriptor, execDescriptor.getFiles(), null, info, createRootPrefixComputer(execDescriptor.getLocation())); |
| if (execDescriptor.isTemporary()) |
| FileUtils.deleteAll(execDescriptor.getLocation()); |
| } |
| // setup a requirement between the executable and the launcher fragment that has the shared library |
| if (config.length > 0 && !CONFIG_ANY.equalsIgnoreCase(config[0])) { |
| String ws = config[0]; |
| String os = config[1]; |
| String arch = config[2]; |
| String launcherFragment = EquinoxLauncherCUAction.ORG_ECLIPSE_EQUINOX_LAUNCHER + '.' + ws + '.' + os; |
| if (!(Constants.OS_MACOSX.equals(os) && !Constants.ARCH_X86_64.equals(arch))) |
| launcherFragment += '.' + arch; |
| iu.setRequirements(new IRequirement[] {MetadataFactory.createRequirement(IInstallableUnit.NAMESPACE_IU_ID, launcherFragment, VersionRange.emptyRange, filter, false, false)}); |
| } |
| result.addIU(MetadataFactory.createInstallableUnit(iu), IPublisherResult.ROOT); |
| return publishCU; |
| } |
| |
| private String getExecutableId() { |
| return createCUIdString(idBase, TYPE, "", configSpec); //$NON-NLS-1$ |
| } |
| |
| // Create the CU that installs (e.g., unzips) the executable |
| private void publishExecutableCU(ExecutablesDescriptor execDescriptor, IPublisherResult result) { |
| InstallableUnitFragmentDescription cu = createSkeletonExecutableCU(execDescriptor); |
| String[] config = parseConfigSpec(configSpec); |
| String os = config[1]; |
| Map<String, String> touchpointData = computeInstallActions(execDescriptor, os); |
| cu.addTouchpointData(MetadataFactory.createTouchpointData(touchpointData)); |
| IInstallableUnit unit = MetadataFactory.createInstallableUnit(cu); |
| result.addIU(unit, IPublisherResult.ROOT); |
| |
| } |
| |
| private InstallableUnitFragmentDescription createSkeletonExecutableCU(ExecutablesDescriptor execDescriptor) { |
| InstallableUnitFragmentDescription cu = new InstallableUnitFragmentDescription(); |
| String id = createCUIdString(idBase, TYPE, flavor, configSpec); |
| cu.setId(id); |
| cu.setVersion(version); |
| cu.setFilter(createFilterSpec(configSpec)); |
| String executableId = getExecutableId(); |
| cu.setHost(new IRequirement[] {MetadataFactory.createRequirement(IInstallableUnit.NAMESPACE_IU_ID, executableId, new VersionRange(version, true, version, true), null, false, false)}); |
| cu.setProperty(InstallableUnitDescription.PROP_TYPE_FRAGMENT, Boolean.TRUE.toString()); |
| //TODO bug 218890, would like the fragment to provide the launcher capability as well, but can't right now. |
| cu.setCapabilities(new IProvidedCapability[] {PublisherHelper.createSelfCapability(id, version)}); |
| cu.setTouchpointType(PublisherHelper.TOUCHPOINT_NATIVE); |
| return cu; |
| } |
| |
| private Map<String, String> computeInstallActions(ExecutablesDescriptor execDescriptor, String os) { |
| if (Constants.OS_MACOSX.equals(os)) |
| return computeMacInstallActions(execDescriptor); |
| |
| Map<String, String> touchpointData = new HashMap<>(); |
| String configurationData = "unzip(source:@artifact, target:${installFolder});"; //$NON-NLS-1$ |
| if (!Constants.OS_WIN32.equals(os)) { |
| // We are on linux/unix. by default set all of the files to be executable. |
| File[] fileList = execDescriptor.getFiles(); |
| for (File fileList1 : fileList) { |
| configurationData += " chmod(targetDir:${installFolder}, targetFile:" + fileList1.getName() + ", permissions:755);"; //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| } |
| touchpointData.put("install", configurationData); //$NON-NLS-1$ |
| String unConfigurationData = "cleanupzip(source:@artifact, target:${installFolder});"; //$NON-NLS-1$ |
| touchpointData.put("uninstall", unConfigurationData); //$NON-NLS-1$ |
| return touchpointData; |
| } |
| |
| private Map<String, String> computeMacInstallActions(ExecutablesDescriptor execDescriptor) { |
| Map<String, String> touchpointData = new HashMap<>(); |
| String configurationData = "unzip(source:@artifact, target:${installFolder}/../);"; //$NON-NLS-1$ |
| String execName = execDescriptor.getExecutableName(); |
| configurationData += " chmod(targetDir:${installFolder}/../MacOS/, targetFile:" + execName + ", permissions:755);"; //$NON-NLS-1$ //$NON-NLS-2$ |
| touchpointData.put("install", configurationData); //$NON-NLS-1$ |
| String unConfigurationData = "cleanupzip(source:@artifact, target:${installFolder}/../);"; //$NON-NLS-1$ |
| touchpointData.put("uninstall", unConfigurationData); //$NON-NLS-1$ |
| return touchpointData; |
| } |
| |
| private String guessMacAppName(String execName) { |
| // magic moved here from BrandingIron.brandMac for bug 342550; PDE build requires appName == execName |
| // TODO the application name for Mac really should be a parameter of the product configuration |
| String appName = execName; |
| if (appName.equals("eclipse")) //$NON-NLS-1$ |
| appName = "Eclipse"; //$NON-NLS-1$ |
| else if (appName.equals("launcher")) //$NON-NLS-1$ |
| appName = "Launcher"; //$NON-NLS-1$ |
| return appName; |
| } |
| |
| /** |
| * Brands a copy of the given executable descriptor with the information in the |
| * current product definition. The files described in the descriptor are also copied |
| * to a temporary location to avoid destructive modification. |
| * |
| * @param descriptor the executable descriptor to brand. |
| * @return the new descriptor |
| */ |
| protected ExecutablesDescriptor brandExecutables(ExecutablesDescriptor descriptor) { |
| ExecutablesDescriptor result = new ExecutablesDescriptor(descriptor); |
| result.makeTemporaryCopy(); |
| IBrandingAdvice advice = getBrandingAdvice(); |
| if (advice == null) |
| partialBrandExecutables(result); |
| else |
| fullBrandExecutables(result, advice); |
| return result; |
| } |
| |
| private IBrandingAdvice getBrandingAdvice() { |
| // there is expected to only be one branding advice for a given configspec so |
| // just return the first one we find. |
| for (IBrandingAdvice advice : info.getAdvice(configSpec, true, null, null, IBrandingAdvice.class)) |
| return advice; |
| return null; |
| } |
| |
| protected void fullBrandExecutables(ExecutablesDescriptor descriptor, IBrandingAdvice advice) { |
| BrandingIron iron = new BrandingIron(); |
| iron.setId(idBase); |
| iron.setVersion(version); |
| iron.setIcons(advice.getIcons()); |
| String name = advice.getExecutableName(); |
| if (name == null) |
| name = "eclipse"; //$NON-NLS-1$ |
| iron.setName(name); |
| iron.setApplicationName(guessMacAppName(name)); |
| iron.setOS(advice.getOS()); |
| // FIXME: use product's aboutText as description? |
| // iron.setDescription(advice.getAboutText()); |
| try { |
| iron.brand(descriptor); |
| } catch (Exception e) { |
| // TODO Auto-generated catch block |
| e.printStackTrace(); |
| } |
| } |
| |
| protected void partialBrandExecutables(ExecutablesDescriptor descriptor) { |
| File[] list = descriptor.getFiles(); |
| for (File list1 : list) { |
| mungeExecutableFileName(list1, descriptor); |
| } |
| descriptor.setExecutableName("eclipse", true); //$NON-NLS-1$ |
| } |
| |
| // TODO This method is a temporary hack to rename the launcher.exe files |
| // to eclipse.exe (or "launcher" to "eclipse"). Eventually we will either hand-craft |
| // metadata/artifacts for launchers, or alter the delta pack to contain eclipse-branded |
| // launchers. |
| private void mungeExecutableFileName(File file, ExecutablesDescriptor descriptor) { |
| if (file.getName().equals("launcher")) { //$NON-NLS-1$ |
| File newFile = new File(file.getParentFile(), "eclipse"); //$NON-NLS-1$ |
| file.renameTo(newFile); |
| descriptor.replace(file, newFile); |
| } else if (file.getName().equals("launcher.exe")) { //$NON-NLS-1$ |
| File newFile = new File(file.getParentFile(), "eclipse.exe"); //$NON-NLS-1$ |
| file.renameTo(newFile); |
| descriptor.replace(file, newFile); |
| } |
| } |
| } |