diff --git a/tycho-its/projects/eeProfile.java25/bundle/META-INF/MANIFEST.MF b/tycho-its/projects/eeProfile.java25/bundle/META-INF/MANIFEST.MF
new file mode 100644
index 0000000000..fed5129671
--- /dev/null
+++ b/tycho-its/projects/eeProfile.java25/bundle/META-INF/MANIFEST.MF
@@ -0,0 +1,6 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: Bundle
+Bundle-SymbolicName: java25.bundle
+Bundle-Version: 1.0.0.qualifier
+Bundle-RequiredExecutionEnvironment: JavaSE-25
diff --git a/tycho-its/projects/eeProfile.java25/bundle/build.properties b/tycho-its/projects/eeProfile.java25/bundle/build.properties
new file mode 100644
index 0000000000..5f22cdd448
--- /dev/null
+++ b/tycho-its/projects/eeProfile.java25/bundle/build.properties
@@ -0,0 +1 @@
+bin.includes = META-INF/
diff --git a/tycho-its/projects/eeProfile.java25/bundle/pom.xml b/tycho-its/projects/eeProfile.java25/bundle/pom.xml
new file mode 100644
index 0000000000..0dc2658903
--- /dev/null
+++ b/tycho-its/projects/eeProfile.java25/bundle/pom.xml
@@ -0,0 +1,15 @@
+
+
+ 4.0.0
+
+
+ tycho-its-project.eeProfile.java25
+ 1.0.0-SNAPSHOT
+ parent
+
+
+ java25.bundle
+ eclipse-plugin
+
+
diff --git a/tycho-its/projects/eeProfile.java25/pom.xml b/tycho-its/projects/eeProfile.java25/pom.xml
new file mode 100644
index 0000000000..e22e33d1d7
--- /dev/null
+++ b/tycho-its/projects/eeProfile.java25/pom.xml
@@ -0,0 +1,33 @@
+
+
+ 4.0.0
+
+ tycho-its-project.eeProfile.java25
+ 1.0.0-SNAPSHOT
+ parent
+ pom
+
+
+ bundle
+
+
+
+
+
+ org.eclipse.tycho
+ tycho-maven-plugin
+ ${tycho-version}
+ true
+
+
+ org.eclipse.tycho
+ tycho-compiler-plugin
+ ${tycho-version}
+
+ javac
+
+
+
+
+
diff --git a/tycho-its/src/test/java/org/eclipse/tycho/test/eeProfile/Java25ResolutionTest.java b/tycho-its/src/test/java/org/eclipse/tycho/test/eeProfile/Java25ResolutionTest.java
new file mode 100644
index 0000000000..3af6dd3617
--- /dev/null
+++ b/tycho-its/src/test/java/org/eclipse/tycho/test/eeProfile/Java25ResolutionTest.java
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * Copyright (c) 2025 Contributors 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:
+ * Contributors - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tycho.test.eeProfile;
+
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+
+import org.apache.maven.it.Verifier;
+import org.eclipse.tycho.test.AbstractTychoIntegrationTest;
+import org.junit.Ignore;
+import org.junit.Test;
+
+@Ignore("unless java 25 jvm is available to tycho build")
+public class Java25ResolutionTest extends AbstractTychoIntegrationTest {
+
+ @Test
+ public void testBundleBuildForJava25() throws Exception {
+ // Test that a bundle with JavaSE-25 BREE can be built with javac compiler
+ Verifier verifier = getVerifier("eeProfile.java25", false);
+ verifier.executeGoal("verify");
+ verifier.verifyErrorFreeLog();
+ verifier.verifyTextInLog("Building jar:");
+ File buildResult = new File(verifier.getBasedir());
+ File bundleJar = new File(buildResult, "bundle/target/java25.bundle-1.0.0-SNAPSHOT.jar");
+ assertTrue("Bundle JAR should exist", bundleJar.exists());
+ }
+
+}
diff --git a/tycho-packaging-plugin/src/main/java/org/eclipse/tycho/packaging/BndManifestProcessor.java b/tycho-packaging-plugin/src/main/java/org/eclipse/tycho/packaging/BndManifestProcessor.java
index a95778a730..de3015e0c7 100644
--- a/tycho-packaging-plugin/src/main/java/org/eclipse/tycho/packaging/BndManifestProcessor.java
+++ b/tycho-packaging-plugin/src/main/java/org/eclipse/tycho/packaging/BndManifestProcessor.java
@@ -131,6 +131,23 @@ private void enhanceReqCap(Attributes mainAttributes, Attributes calcAttributes)
String existingValue = mainAttributes.getValue(Constants.REQUIRE_CAPABILITY);
String newValue = calcAttributes.getValue(Constants.REQUIRE_CAPABILITY);
if (newValue != null) {
+ // Fix for JavaSE-25 and future Java versions not recognized by bndlib
+ // Bnd's Analyzer may generate osgi.ee=UNKNOWN for unrecognized BREE values
+ // We need to detect and fix this before it gets into the manifest
+ // See https://github.com/bndtools/bnd/issues/6858
+ if (newValue.contains("UNKNOWN")) {
+ String breeHeader = mainAttributes.getValue(Constants.BUNDLE_REQUIREDEXECUTIONENVIRONMENT);
+ if (breeHeader != null && !breeHeader.isEmpty()) {
+ String fixedFilter = convertBreeToOsgiEeFilter(breeHeader);
+ if (fixedFilter != null) {
+ // Replace the UNKNOWN filter with the correct one
+ newValue = newValue.replaceAll("osgi\\.ee;\\s*filter:=\"[^\"]*UNKNOWN[^\"]*\"",
+ "osgi.ee;filter:=\"" + fixedFilter + "\"");
+ logger.debug("Fixed UNKNOWN osgi.ee filter to: " + fixedFilter);
+ }
+ }
+ }
+
Parameters additional = OSGiHeader.parseHeader(newValue);
if (additional.containsKey(ExecutionEnvironmentNamespace.EXECUTION_ENVIRONMENT_NAMESPACE)) {
// remove deprecated header but use the ee namespace
@@ -164,4 +181,47 @@ private void enhanceReqCap(Attributes mainAttributes, Attributes calcAttributes)
}
}
+ /**
+ * Converts a Bundle-RequiredExecutionEnvironment (BREE) value to an osgi.ee filter.
+ * This is a workaround for bndlib not recognizing newer Java versions (like JavaSE-25).
+ *
+ * @param breeHeader the BREE header value (e.g., "JavaSE-25" or "JavaSE-17,JavaSE-25")
+ * @return an osgi.ee filter string, or null if the BREE cannot be parsed
+ */
+ private String convertBreeToOsgiEeFilter(String breeHeader) {
+ // BREE can contain multiple comma-separated values, we need to pick the highest version
+ String[] brees = breeHeader.split(",");
+ int highestVersion = -1;
+
+ for (String bree : brees) {
+ bree = bree.trim();
+ // Parse JavaSE-XX or J2SE-XX format
+ if (bree.startsWith("JavaSE-") || bree.startsWith("J2SE-")) {
+ try {
+ String prefix = bree.startsWith("JavaSE-") ? "JavaSE-" : "J2SE-";
+ String versionStr = bree.substring(prefix.length());
+ // Remove "1." prefix if present (e.g., "1.8" -> "8")
+ if (versionStr.startsWith("1.")) {
+ versionStr = versionStr.substring(2);
+ }
+ int version = Integer.parseInt(versionStr);
+ if (version > highestVersion) {
+ highestVersion = version;
+ }
+ } catch (NumberFormatException e) {
+ // Ignore malformed BREE entries
+ }
+ }
+ }
+
+ if (highestVersion > 0) {
+ // Convert to osgi.ee filter format: (&(osgi.ee=JavaSE)(version=XX))
+ // Note: J2SE is mapped to JavaSE for the filter
+ return "(&(osgi.ee=JavaSE)(version=" + highestVersion + "))";
+ }
+
+ return null;
+ }
+
}
+