Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add actions that publish IUs used in the build #354

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
}

}
Loading