Skip to content

Commit

Permalink
Rework the handling of profiles
Browse files Browse the repository at this point in the history
- do not embed any static profile
- compute the java packages based on the running vm and toolchains
- fall back to "higher" profiles if an exact match is not found
- do not validate BREEs against static list of names
  • Loading branch information
laeubi committed Jun 4, 2022
1 parent 9495069 commit 247665b
Show file tree
Hide file tree
Showing 15 changed files with 235 additions and 308 deletions.
Expand Up @@ -392,7 +392,8 @@ public StandardExecutionEnvironment[] getBREE() {
return null;
}
}).filter(Objects::nonNull).collect(Collectors.toList());
manifestBREEs = ExecutionEnvironmentUtils.getProfileNames().stream() //
manifestBREEs = ExecutionEnvironmentUtils.getProfileNames(toolchainManager, session, logger)
.stream() //
.map(name -> name.split("-")) //
.map(segments -> Map.of(ExecutionEnvironmentNamespace.EXECUTION_ENVIRONMENT_NAMESPACE,
segments[0], "version", segments[1]))
Expand Down
Expand Up @@ -106,7 +106,9 @@ public boolean isCustomProfile() {
return false;
}
String profileName = getProfileName();
boolean profileExists = ExecutionEnvironmentUtils.getProfileNames().contains(profileName);
Collection<String> profileNames = ExecutionEnvironmentUtils.getProfileNames(toolchainManager, session, logger);
boolean profileExists = profileNames
.contains(profileName);
if (!profileExists && ignoredByResolver) {
throw new BuildFailureException(
"When using a custom execution environment profile, resolveWithExecutionEnvironmentConstraints must not be set to false");
Expand Down Expand Up @@ -162,7 +164,7 @@ public boolean isIgnoredByResolver() {

@Override
public Collection<ExecutionEnvironment> getAllKnownEEs() {
return ExecutionEnvironmentUtils.getProfileNames().stream() //
return ExecutionEnvironmentUtils.getProfileNames(toolchainManager, session, logger).stream() //
.map(profileName -> ExecutionEnvironmentUtils.getExecutionEnvironment(profileName, toolchainManager,
session, logger)) //
.collect(Collectors.toList());
Expand Down
Expand Up @@ -14,20 +14,20 @@
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

import org.apache.maven.execution.MavenSession;
import org.apache.maven.toolchain.Toolchain;
import org.apache.maven.toolchain.ToolchainManager;
import org.codehaus.plexus.logging.Logger;
import org.eclipse.osgi.internal.framework.EquinoxConfiguration;
import org.eclipse.tycho.core.ee.StandardExecutionEnvironment.JavaInfo;
import org.eclipse.tycho.core.ee.shared.ExecutionEnvironment;
import org.eclipse.tycho.core.ee.shared.ExecutionEnvironment.SystemPackageEntry;
import org.osgi.framework.BundleActivator;
Expand All @@ -41,23 +41,7 @@
*/
public class ExecutionEnvironmentUtils {

private static final Map<String, Properties> profilesProperties = fillEnvironmentsMap();
private static final Map<String, StandardExecutionEnvironment> executionEnvironmentsMap = new ConcurrentHashMap<>(
profilesProperties.size(), 1.f);

private static Map<String, Properties> fillEnvironmentsMap() {
Properties listProps = readProperties(findInSystemBundle("profile.list"));
List<String> profileFiles = new ArrayList<>(Arrays.asList(listProps.getProperty("java.profiles").split(",")));
profileFiles.add("JavaSE-11.profile");
profileFiles.add("JavaSE-17.profile");
profileFiles.add("JavaSE-18.profile");
Map<String, Properties> envMap = new LinkedHashMap<>(profileFiles.size(), 1.f);
for (String profileFile : profileFiles) {
Properties props = readProperties(findInSystemBundle(profileFile.trim()));
envMap.put(props.getProperty(EquinoxConfiguration.PROP_OSGI_JAVA_PROFILE_NAME).trim(), props);
}
return envMap;
}
private static Map<String, StandardExecutionEnvironment> executionEnvironmentsMap;

private static Properties readProperties(final URL url) {
Properties listProps = new Properties();
Expand All @@ -81,20 +65,104 @@ private static Properties readProperties(final URL url) {
*/
public static StandardExecutionEnvironment getExecutionEnvironment(String profileName, ToolchainManager manager,
MavenSession session, Logger logger) throws UnknownEnvironmentException {
if (!profilesProperties.containsKey(profileName)) {
throw new UnknownEnvironmentException(profileName);
Map<String, StandardExecutionEnvironment> map = getExecutionEnvironmentsMap(manager, session, logger);
StandardExecutionEnvironment ee = map.get(profileName);
if (ee != null) {
return ee;
}
int version = getVersion(profileName);
if (version > 8) {
//try find newer version...
StandardExecutionEnvironment higherEE = map.keySet().stream()
.mapToInt(ExecutionEnvironmentUtils::getVersion).filter(v -> v > version).min().stream()
.mapToObj(v -> {
String[] split = profileName.split("-");
return split[0] + "-" + v;
}).map(map::get).findFirst().orElse(null);
if (higherEE != null) {
logger.warn("Using " + higherEE.getProfileName() + " to fulfill requested profile of " + profileName
+ " this might lead to faulty dependency resolution, consider define a suitable VM in the toolchains.");
return higherEE;
}
}
throw new UnknownEnvironmentException(profileName);
}

public static Collection<String> getProfileNames(ToolchainManager manager, MavenSession session, Logger logger) {

return new ArrayList<>(getExecutionEnvironmentsMap(manager, session, logger).keySet());
}

private static synchronized Map<String, StandardExecutionEnvironment> getExecutionEnvironmentsMap(
ToolchainManager manager, MavenSession session, Logger logger) {
if (executionEnvironmentsMap == null) {
executionEnvironmentsMap = new HashMap<String, StandardExecutionEnvironment>();
Properties listProps = readProperties(findInSystemBundle("profile.list"));
//first read all profiles that are part of the system...
for (String profileFile : listProps.getProperty("java.profiles").split(",")) {
Properties props = readProperties(findInSystemBundle(profileFile.trim()));
if (props == null) {
logger.warn("can't read profile " + profileFile + " from system path");
continue;
}
String name = props.getProperty(EquinoxConfiguration.PROP_OSGI_JAVA_PROFILE_NAME).trim();
executionEnvironmentsMap.put(name,
new StandardExecutionEnvironment(props, getToolchainFor(name, manager, session), logger));
}
//derive from the toolchains...
if (manager != null) {
List<Toolchain> jdks = manager.getToolchains(session, "jdk", null);
for (Toolchain jdk : jdks) {
JavaInfo javaInfo = StandardExecutionEnvironment.readFromToolchains(jdk, logger);
if (javaInfo.version > 8) {
Properties toolchainJvm = createProfileJvm(javaInfo.version, javaInfo.packages);
String name = toolchainJvm.getProperty(EquinoxConfiguration.PROP_OSGI_JAVA_PROFILE_NAME).trim();
executionEnvironmentsMap.put(name, new StandardExecutionEnvironment(toolchainJvm, jdk, logger));
}
}
}
//derive from the running jvm...
int javaVersion = Runtime.version().feature();
if (!executionEnvironmentsMap.containsKey("JavaSE-" + javaVersion)) {
Properties runningVm = createProfileJvm(javaVersion, ListSystemPackages.getCurrentJREPackages());
String name = runningVm.getProperty(EquinoxConfiguration.PROP_OSGI_JAVA_PROFILE_NAME).trim();
executionEnvironmentsMap.put(name,
new StandardExecutionEnvironment(runningVm, getToolchainFor(name, manager, session), logger));
}
}
return executionEnvironmentsMap.computeIfAbsent(profileName, name -> {
List<Toolchain> toolchains = manager != null && session != null
? manager.getToolchains(session, "jdk", Collections.singletonMap("id", profileName))
: Collections.emptyList();
return new StandardExecutionEnvironment(profilesProperties.get(name),
toolchains.isEmpty() ? null : toolchains.iterator().next(), logger);
});
return executionEnvironmentsMap;
}

public static List<String> getProfileNames() {
return new ArrayList<>(profilesProperties.keySet());
private static Toolchain getToolchainFor(String profileName, ToolchainManager manager, MavenSession session) {
if (manager != null) {
//First try to find it by ID
for (Toolchain toolchain : manager.getToolchains(session, "jdk",
Collections.singletonMap("id", profileName))) {
return toolchain;
}
//Try find by version
int version = getVersion(profileName);
if (version > 8) {
for (Toolchain toolchain : manager.getToolchains(session, "jdk",
Collections.singletonMap("version", String.valueOf(version)))) {
return toolchain;
}
}
}
return null;
}

private static int getVersion(String profileName) {
String[] split = profileName.split("-");
if (split.length == 2) {
try {
return (int) Double.parseDouble(split[split.length - 1]);
} catch (NumberFormatException e) {
//can't check then...
}
}
return -1;

}

public static void applyProfileProperties(Properties properties, ExecutionEnvironment executionEnvironment) {
Expand Down Expand Up @@ -137,4 +205,46 @@ private static URL findInSystemBundle(String entry) {
ClassLoader loader = BundleActivator.class.getClassLoader();
return loader == null ? ClassLoader.getSystemResource(entry) : loader.getResource(entry);
}

//This is derived from org.eclipse.equinox.p2.publisher.actions.JREAction.createDefaultProfileFromRunningJvm()
private static Properties createProfileJvm(int javaVersion, Collection<String> packages) {
String profileName = "JavaSE-" + javaVersion;
Properties props = new Properties();
// add systempackages
props.setProperty("org.osgi.framework.system.packages", packages.stream().collect(Collectors.joining(",")));
// add EE
StringBuilder ee = new StringBuilder(
"OSGi/Minimum-1.0,OSGi/Minimum-1.1,OSGi/Minimum-1.2,JavaSE/compact1-1.8,JavaSE/compact2-1.8,JavaSE/compact3-1.8,JRE-1.1,J2SE-1.2,J2SE-1.3,J2SE-1.4,J2SE-1.5,JavaSE-1.6,JavaSE-1.7,JavaSE-1.8,");
for (int i = 9; i < javaVersion; i++) {
ee.append("JavaSE-" + String.valueOf(i) + ",");
}
ee.append(profileName);
props.setProperty("org.osgi.framework.executionenvironment", ee.toString());
// add capabilities
StringBuilder versionList = new StringBuilder();
versionList.append("1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8");
for (int i = 9; i <= javaVersion; i++) {
versionList.append(", " + String.valueOf(i) + ".0");
}
props.setProperty("org.osgi.framework.system.capabilities",
" osgi.ee; osgi.ee=\"OSGi/Minimum\"; version:List<Version>=\"1.0, 1.1, 1.2\",\\\n"
+ " osgi.ee; osgi.ee=\"JRE\"; version:List<Version>=\"1.0, 1.1\",\\\n"
+ " osgi.ee; osgi.ee=\"JavaSE\"; version:List<Version>=\"" + versionList.toString() + "\",\\\n"
+ " osgi.ee; osgi.ee=\"JavaSE/compact1\"; version:List<Version>=\"1.8,"
+ String.valueOf(javaVersion) + ".0\",\\\n"
+ " osgi.ee; osgi.ee=\"JavaSE/compact2\"; version:List<Version>=\"1.8,"
+ String.valueOf(javaVersion) + ".0\",\\\n"
+ " osgi.ee; osgi.ee=\"JavaSE/compact3\"; version:List<Version>=\"1.8,"
+ String.valueOf(javaVersion) + ".0\"");

// add profile name and compiler options
props.setProperty("osgi.java.profile.name", profileName);
props.setProperty("org.eclipse.jdt.core.compiler.compliance", String.valueOf(javaVersion));
props.setProperty("org.eclipse.jdt.core.compiler.source", String.valueOf(javaVersion));
props.setProperty("org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode", "enabled");
props.setProperty("org.eclipse.jdt.core.compiler.codegen.targetPlatform", String.valueOf(javaVersion));
props.setProperty("org.eclipse.jdt.core.compiler.problem.assertIdentifier", "error");
props.setProperty("org.eclipse.jdt.core.compiler.problem.enumIdentifier", "error");
return props;
}
}
Expand Up @@ -19,6 +19,7 @@
public class ListSystemPackages {

public static void main(String[] args) {
System.out.println(Runtime.version().feature());
getCurrentJREPackages().forEach(System.out::println);
}

Expand Down
Expand Up @@ -110,29 +110,49 @@ public class StandardExecutionEnvironment implements Comparable<StandardExecutio
this.logger = logger;
}

private Set<String> readFromToolchains(Toolchain toolchain) {
static JavaInfo readFromToolchains(Toolchain toolchain, Logger logger) {
if (toolchain == null) {
return Collections.emptySet();
return new JavaInfo(-1, Collections.emptySet());
}
String java = toolchain.findTool("java");
if (java == null) {
return Collections.emptySet();
return new JavaInfo(-1, Collections.emptySet());
}
Set<String> res = new HashSet<>();
int version = -1;
try {
ProcessBuilder builder = new ProcessBuilder(java, "-jar",
getSystemPackagesCompanionJar().getAbsolutePath());
try (BufferedReader reader = new BufferedReader(
new java.io.InputStreamReader(builder.start().getInputStream(), Charset.defaultCharset()))) {
String line = null;
String line = reader.readLine();
try {
if (line != null) {
//for old vms < java 9 we might get no response at all
version = Integer.parseInt(line);
}
} catch (NumberFormatException e) {
logger.error(e.getMessage(), e);
}
while ((line = reader.readLine()) != null) {
res.add(line);
}
}
} catch (IOException e) {
logger.error(e.getMessage(), e);
}
return res;
return new JavaInfo(version, res);
}

static final class JavaInfo {
final int version;
final Collection<String> packages;

private JavaInfo(int version, Collection<String> packages) {
this.version = version;
this.packages = Collections.unmodifiableCollection(packages);
}

}

static File getSystemPackagesCompanionJar() throws IOException {
Expand Down Expand Up @@ -226,7 +246,7 @@ public synchronized Collection<SystemPackageEntry> getSystemPackages() {
} else if (toolchain != null) {
logger.debug(
"No system.packages in profile definition file for " + profileName + "; checking toolchain.");
this.systemPackages = readFromToolchains(toolchain).stream()
this.systemPackages = readFromToolchains(toolchain, logger).packages.stream()
.map(packageName -> new SystemPackageEntry(packageName, null)).collect(Collectors.toList());
} else if (Integer.parseInt(compilerSourceLevel) == Runtime.version().feature()) {
logger.debug("Currently running JRE matches source level for " + getProfileName()
Expand Down
Expand Up @@ -242,7 +242,7 @@ protected Properties getPlatformProperties(Properties properties, MavenSession m
// ignoring EE by adding all known EEs
StringJoiner allSystemPackages = new StringJoiner(",");
StringJoiner allSystemCapabilities = new StringJoiner(",");
for (String profile : ExecutionEnvironmentUtils.getProfileNames()) {
for (String profile : ExecutionEnvironmentUtils.getProfileNames(toolchainManager, mavenSession, logger)) {
StandardExecutionEnvironment executionEnvironment = ExecutionEnvironmentUtils
.getExecutionEnvironment(profile, toolchainManager, mavenSession, logger);
String currentSystemPackages = (String) executionEnvironment.getProfileProperties()
Expand Down
Expand Up @@ -13,8 +13,6 @@
import org.eclipse.tycho.ArtifactKey;
import org.eclipse.tycho.ArtifactType;
import org.eclipse.tycho.DefaultArtifactKey;
import org.eclipse.tycho.core.ee.ExecutionEnvironmentUtils;
import org.eclipse.tycho.core.ee.UnknownEnvironmentException;
import org.osgi.framework.BundleException;
import org.osgi.framework.Constants;
import org.osgi.framework.Version;
Expand Down Expand Up @@ -61,12 +59,8 @@ private String[] parseExecutionEnvironments() {
}
String[] envs = new String[brees.length];
for (int i = 0; i < brees.length; i++) {
String ee = brees[i].getValue();
if (ExecutionEnvironmentUtils.getProfileNames().contains(ee)) {
envs[i] = ee;
} else {
throw new OsgiManifestParserException(location, new UnknownEnvironmentException(ee));
}
//BREE already has no real meaning for modular vms so matching them here does not really offer much...
envs[i] = brees[i].getValue();
}
return envs;
}
Expand Down
32 changes: 0 additions & 32 deletions tycho-core/src/main/resources/JavaSE-11.profile

This file was deleted.

0 comments on commit 247665b

Please sign in to comment.