Skip to content

Commit

Permalink
Add actions that publish IUs used in the build
Browse files Browse the repository at this point in the history
This adds actions for code that want to transform an PDE project into a
set of requirements/capabilities for example to compute the required
units of a target platform for such project.
  • Loading branch information
laeubi authored and merks committed Dec 13, 2023
1 parent f108a32 commit 6abe0c6
Show file tree
Hide file tree
Showing 4 changed files with 238 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ Bundle-ActivationPolicy: lazy
Bundle-Vendor: %providerName
Bundle-Localization: plugin
Bundle-RequiredExecutionEnvironment: JavaSE-17
Import-Package: org.eclipse.equinox.app;version="[1.0.0,2.0.0)",
Import-Package: aQute.bnd.header;version="2.6.0",
aQute.bnd.osgi;version="7.0.0",
aQute.bnd.stream;version="1.4.0",
org.eclipse.equinox.app;version="[1.0.0,2.0.0)",
org.eclipse.equinox.frameworkadmin;version="[2.0.0,3.0.0)",
org.eclipse.equinox.internal.frameworkadmin.equinox,
org.eclipse.equinox.internal.frameworkadmin.utils,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*******************************************************************************
* Copyright (c) 2023 Christoph Läubrich 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:
* Christoph Läubrich - initial API and implementation
*******************************************************************************/
package org.eclipse.equinox.p2.publisher.eclipse;

import aQute.bnd.osgi.Constants;
import aQute.bnd.osgi.Processor;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.UUID;
import org.eclipse.core.runtime.*;
import org.eclipse.equinox.p2.metadata.*;
import org.eclipse.equinox.p2.metadata.MetadataFactory.InstallableUnitDescription;
import org.eclipse.equinox.p2.publisher.*;

/**
* This publisher action can handle bnd instruction files and transform these
* into InstallableUnits that can be used to provision a system that is used for
* <b>building</b>, so this action will usually used in the context of Tycho,
* Oomph or similar.
*/
public class BndInstructionsAction extends AbstractPublisherAction {

private static final List<String> DEPENDENCY_INSTRUCTIONS = List.of(Constants.BUILDPATH, Constants.TESTPATH);

private final File bndFile;

public BndInstructionsAction(File bndFile) {
this.bndFile = bndFile;
}

@Override
public IStatus perform(IPublisherInfo publisherInfo, IPublisherResult results, IProgressMonitor monitor) {
try (Processor processor = new Processor()) {
processor.setProperties(bndFile);
List<IRequirement> requirements = DEPENDENCY_INSTRUCTIONS.stream()
.flatMap(instr -> processor.getMergedParameters(instr).stream().mapToObj((key, attr) -> {
return MetadataFactory.createRequirement(BundlesAction.CAPABILITY_NS_OSGI_BUNDLE, key,
VersionRange.emptyRange, null, false, true);
})).toList();
if (!requirements.isEmpty()) {
InstallableUnitDescription result = new MetadataFactory.InstallableUnitDescription();
result.setId("bnd-bundle-requirements-" + UUID.randomUUID()); //$NON-NLS-1$
result.setVersion(Version.createOSGi(0, 0, 0, String.valueOf(System.currentTimeMillis())));
result.addRequirements(requirements);
results.addIU(MetadataFactory.createInstallableUnit(result), IPublisherResult.NON_ROOT);
}
} catch (IOException e) {
return Status.error(bndFile.getAbsolutePath() + " can not be processed", e); //$NON-NLS-1$
}
return Status.OK_STATUS;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*******************************************************************************
* Copyright (c) 2023 Christoph Läubrich 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:
* Christoph Läubrich - initial API and implementation
*******************************************************************************/
package org.eclipse.equinox.p2.publisher.eclipse;

import java.io.*;
import java.util.*;
import java.util.function.Predicate;
import org.eclipse.core.runtime.*;
import org.eclipse.equinox.p2.metadata.*;
import org.eclipse.equinox.p2.metadata.MetadataFactory.InstallableUnitDescription;
import org.eclipse.equinox.p2.publisher.*;

/**
* This publisher action can handle build.properties files and transform these
* into InstallableUnits that can be used to provision a system that is used for
* <b>building</b>, so this action will usually used in the context of Tycho,
* Oomph or similar.
*/
public class BuildPropertiesAction extends AbstractPublisherAction {

private static final String KEY_ADDITIONAL_BUNDLES = "additional.bundles"; //$NON-NLS-1$
private static final String SEPERATOR = ","; //$NON-NLS-1$

private final File buildPropertiesFile;

public BuildPropertiesAction(File buildPropertiesFile) {
this.buildPropertiesFile = buildPropertiesFile;
}

@Override
public IStatus perform(IPublisherInfo publisherInfo, IPublisherResult results, IProgressMonitor monitor) {
if (buildPropertiesFile.exists()) {
Properties properties = new Properties();
try (FileInputStream stream = new FileInputStream(buildPropertiesFile)) {
properties.load(stream);
} catch (IOException e) {
return Status.error("Can't read build.properties file: " + buildPropertiesFile.getAbsolutePath(), e); //$NON-NLS-1$
}
String property = properties.getProperty(KEY_ADDITIONAL_BUNDLES);
if (property != null) {
List<IRequirement> requirements = Arrays.stream(property.split(SEPERATOR)).map(String::strip)
.filter(Predicate.not(String::isBlank))
.map(bsn -> MetadataFactory.createRequirement(BundlesAction.CAPABILITY_NS_OSGI_BUNDLE, bsn,
VersionRange.emptyRange, null, true, true))
.toList();
if (!requirements.isEmpty()) {
InstallableUnitDescription result = new MetadataFactory.InstallableUnitDescription();
result.setId("additional-bundle-requirements-" + UUID.randomUUID()); //$NON-NLS-1$
result.setVersion(Version.createOSGi(0, 0, 0, String.valueOf(System.currentTimeMillis())));
result.addRequirements(requirements);
results.addIU(MetadataFactory.createInstallableUnit(result), IPublisherResult.NON_ROOT);
}
}
}
return Status.OK_STATUS;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*******************************************************************************
* Copyright (c) 2023 Christoph Läubrich 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:
* Christoph Läubrich - initial API and implementation
*******************************************************************************/
package org.eclipse.equinox.p2.publisher.eclipse;

import java.io.*;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import java.util.jar.*;
import org.eclipse.core.runtime.*;
import org.eclipse.equinox.p2.publisher.*;
import org.eclipse.osgi.framework.util.CaseInsensitiveDictionaryMap;
import org.eclipse.osgi.service.resolver.BundleDescription;

/**
* This publisher action can handle a PDE project root and transform this into
* InstallableUnits that can be used to provision a system that is used for
* <b>building</b>, so this action will usually used in the context of Tycho,
* Oomph or similar.
*/
@SuppressWarnings("restriction")
public class PdeAction extends AbstractPublisherAction {

private final File basedir;

public PdeAction(File basedir) {
this.basedir = basedir;
}

@Override
public IStatus perform(IPublisherInfo publisherInfo, IPublisherResult results, IProgressMonitor monitor) {
SubMonitor subMonitor = SubMonitor.convert(monitor, 3);
File buildProperties = new File(basedir, "build.properties"); //$NON-NLS-1$
if (buildProperties.isFile()) {
new BuildPropertiesAction(buildProperties).perform(publisherInfo, results, subMonitor.split(1));
}
subMonitor.setWorkRemaining(2);
File pdeBnd = new File(basedir, "pde.bnd"); //$NON-NLS-1$
if (pdeBnd.isFile()) {
new BndInstructionsAction(pdeBnd).perform(publisherInfo, results, subMonitor.split(1));
}
subMonitor.setWorkRemaining(1);
File manifest = getManifestFile();
if (manifest.isFile()) {
Attributes mainAttributes;
try (FileInputStream stream = new FileInputStream(manifest)) {
mainAttributes = new Manifest(stream).getMainAttributes();
} catch (IOException e) {
return Status.error("Can't parse manifest", e); //$NON-NLS-1$
}
CaseInsensitiveDictionaryMap<String, String> headers = new CaseInsensitiveDictionaryMap<>(
mainAttributes.size());
Set<Entry<Object, Object>> entrySet = mainAttributes.entrySet();
for (Entry<Object, Object> entry : entrySet) {
headers.put(entry.getKey().toString(), entry.getValue().toString());
}
BundleDescription bundleDescription = BundlesAction.createBundleDescription(headers, basedir);
new BundlesAction(new BundleDescription[] { bundleDescription }).perform(publisherInfo, results,
subMonitor.split(1));
}
return Status.OK_STATUS;
}

private File getManifestFile() {
File pdePreferences = new File(basedir, ".settings/org.eclipse.pde.core.prefs"); //$NON-NLS-1$
if (pdePreferences.isFile()) {
Properties properties = new Properties();
try {
properties.load(new FileInputStream(pdePreferences));
} catch (IOException e) {
// if loading fails, handle this as if we have no special settings...
}
String property = properties.getProperty("BUNDLE_ROOT_PATH"); //$NON-NLS-1$
if (property != null) {
File file = new File(new File(basedir, property), JarFile.MANIFEST_NAME);
if (file.isFile()) {
// even though configured, the manifest might not be there (e.g. not generated
// yet) in such case, still fall back to the default location, it can only
// happen that noting is there as well so things are not getting worser this
// way...
return file;
}
}
}
// return (possibly non existent) default location...
return new File(basedir, JarFile.MANIFEST_NAME);
}

}

0 comments on commit 6abe0c6

Please sign in to comment.