diff --git a/bundles/org.eclipse.equinox.p2.publisher.eclipse/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.p2.publisher.eclipse/META-INF/MANIFEST.MF
index de94c0b13f..8fcbe9faf9 100644
--- a/bundles/org.eclipse.equinox.p2.publisher.eclipse/META-INF/MANIFEST.MF
+++ b/bundles/org.eclipse.equinox.p2.publisher.eclipse/META-INF/MANIFEST.MF
@@ -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,
diff --git a/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/BndInstructionsAction.java b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/BndInstructionsAction.java
new file mode 100644
index 0000000000..2cee14cb41
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/BndInstructionsAction.java
@@ -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
+ * building, so this action will usually used in the context of Tycho,
+ * Oomph or similar.
+ */
+public class BndInstructionsAction extends AbstractPublisherAction {
+
+ private static final List 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 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;
+ }
+
+}
diff --git a/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/BuildPropertiesAction.java b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/BuildPropertiesAction.java
new file mode 100644
index 0000000000..723eed65a9
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/BuildPropertiesAction.java
@@ -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
+ * building, 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 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;
+ }
+
+}
diff --git a/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/PdeAction.java b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/PdeAction.java
new file mode 100644
index 0000000000..b350f8ec70
--- /dev/null
+++ b/bundles/org.eclipse.equinox.p2.publisher.eclipse/src/org/eclipse/equinox/p2/publisher/eclipse/PdeAction.java
@@ -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
+ * building, 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 headers = new CaseInsensitiveDictionaryMap<>(
+ mainAttributes.size());
+ Set> entrySet = mainAttributes.entrySet();
+ for (Entry