-
Notifications
You must be signed in to change notification settings - Fork 188
/
StandardExecutionEnvironment.java
326 lines (298 loc) · 13.7 KB
/
StandardExecutionEnvironment.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
/*******************************************************************************
* Copyright (c) 2010, 2022 SAP AG and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* SAP AG - initial API and implementation
* Christoph Läubrich - #496 - ResolutionArguments#hashcode is not stable
*******************************************************************************/
package org.eclipse.tycho.core.ee;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;
import java.util.Set;
import java.util.stream.Collectors;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.apache.commons.io.FileUtils;
import org.apache.maven.toolchain.Toolchain;
import org.codehaus.plexus.logging.Logger;
import org.eclipse.osgi.internal.framework.EquinoxConfiguration;
import org.eclipse.osgi.util.ManifestElement;
import org.eclipse.tycho.core.ee.EEVersion.EEType;
import org.eclipse.tycho.core.ee.shared.ExecutionEnvironment;
import org.osgi.framework.BundleException;
import org.osgi.framework.Constants;
import org.osgi.framework.Version;
/**
* Represents a standard OSGi execution environment profile. See the list of known OSGi profiles in
* bundle org.eclipse.osgi, file profile.list.
*
* Standard execution environment capabilities can be referenced in MANIFEST.MF using the header
* "Bundle-RequiredExecutionEnvironment". In order to pick the minimal, required profile from the
* alternatives listed in a BREE header, instances of this class have a total ordering.
*
* TODO This class both represents an EE profile (i.e. the concrete EE implementation used by the
* compiler) and an EE capability (i.e. the capability required via the BREE). This should be
* separate classes. (An EE profile typically provides multiple EE capabilities.)
*/
public class StandardExecutionEnvironment implements Comparable<StandardExecutionEnvironment>, ExecutionEnvironment {
private static final Map<String, String> TARGET_ALIASES;
static {
// https://help.eclipse.org/juno/topic/org.eclipse.jdt.doc.user/tasks/task-using_batch_compiler.htm
Map<String, String> targetAliases = new HashMap<>();
targetAliases.put("jsr14", "1.4");
targetAliases.put("5", "1.5");
targetAliases.put("5.0", "1.5");
targetAliases.put("6", "1.6");
targetAliases.put("6.0", "1.6");
targetAliases.put("7", "1.7");
targetAliases.put("7.0", "1.7");
targetAliases.put("8", "1.8");
targetAliases.put("8.0", "1.8");
targetAliases.put("9", "1.9");
targetAliases.put("9.0", "1.9");
targetAliases.put("10", "1.10");
targetAliases.put("10.0", "1.10");
TARGET_ALIASES = Collections.unmodifiableMap(targetAliases);
}
private final String profileName;
private final String compilerSourceLevel;
private final String compilerTargetLevel;
private List<SystemPackageEntry> systemPackages;
private final EEVersion eeVersion;
private final Properties profileProperties;
private final Toolchain toolchain;
private Logger logger;
/**
* Do no instantiate. Use factory method instead
* {@link ExecutionEnvironmentUtils#getExecutionEnvironment(String)}.
*/
@Deprecated
StandardExecutionEnvironment(@Nonnull Properties profileProperties) {
this(profileProperties, null, null);
}
/* package */ StandardExecutionEnvironment(@Nonnull Properties profileProperties, @Nullable Toolchain toolchain,
@Nullable Logger logger) {
this.toolchain = toolchain;
this.profileName = profileProperties.getProperty(EquinoxConfiguration.PROP_OSGI_JAVA_PROFILE_NAME);
this.compilerSourceLevel = profileProperties.getProperty("org.eclipse.jdt.core.compiler.source");
this.compilerTargetLevel = profileProperties
.getProperty("org.eclipse.jdt.core.compiler.codegen.targetPlatform");
this.eeVersion = parseEEVersion(profileProperties.getProperty(Constants.FRAMEWORK_SYSTEMCAPABILITIES));
this.profileProperties = new Properties();
this.profileProperties.putAll(profileProperties);
this.logger = logger;
}
static JavaInfo readFromToolchains(Toolchain toolchain, Logger logger) {
if (toolchain == null) {
return new JavaInfo(-1, Collections.emptySet());
}
String java = toolchain.findTool("java");
if (java == null) {
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 = reader.readLine();
try {
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 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 {
File companionFile = File.createTempFile("tycho-system-packages-companion", ".jar");
companionFile.deleteOnExit();
try (InputStream contents = ListSystemPackages.class.getClassLoader()
.getResourceAsStream("system-packages-companion.jar")) {
FileUtils.copyToFile(contents, companionFile);
}
return companionFile;
}
private static EEVersion parseEEVersion(String systemCaps) {
List<EEVersion> eeVersions = new ArrayList<>();
try {
ManifestElement[] systemCapValues = ManifestElement.parseHeader("org.osgi.framework.system.capabilities",
systemCaps);
for (ManifestElement systemCapValue : systemCapValues) {
Version version;
String singleVersion = systemCapValue.getAttribute("version:Version");
if (singleVersion != null) {
version = Version.parseVersion(singleVersion);
} else {
String[] versions = systemCapValue.getAttribute("version:List<Version>").split(",");
List<Version> osgiVersions = new ArrayList<>(versions.length);
for (String currentVersion : versions) {
osgiVersions.add(Version.parseVersion(currentVersion));
}
version = Collections.max(osgiVersions);
}
String execEnv = systemCapValue.getAttribute("osgi.ee");
EEType eeType = EEType.fromName(execEnv);
if (eeType != null) {
eeVersions.add(new EEVersion(version, eeType));
}
}
return Collections.max(eeVersions);
} catch (BundleException e) {
throw new RuntimeException(e);
}
}
@Override
public String getProfileName() {
return profileName;
}
@Override
public String getCompilerSourceLevelDefault() {
return compilerSourceLevel;
}
@Override
public String getCompilerTargetLevelDefault() {
return compilerTargetLevel;
}
/*
* for debug purposes
*/
@Override
public String toString() {
return "OSGi profile '" + getProfileName() + "' { source level: " + compilerSourceLevel + ", target level: "
+ compilerTargetLevel + "}";
}
@Override
public synchronized Collection<SystemPackageEntry> getSystemPackages() {
if (systemPackages == null) {
// EE definitions in Tycho for JVMs 11+ will no longer contain system packages as with modular JVMs it's not sure
// all packages will be available at runtime
if (profileProperties.containsKey(Constants.FRAMEWORK_SYSTEMPACKAGES)) {
logger.debug("Found system.packages in profile definition file for " + profileName + ",");
String systemPackagesValue = profileProperties.getProperty(Constants.FRAMEWORK_SYSTEMPACKAGES);
if (systemPackagesValue.isBlank()) {
this.systemPackages = Collections.emptyList();
} else {
try {
this.systemPackages = Arrays.stream(
ManifestElement.parseHeader(Constants.FRAMEWORK_SYSTEMPACKAGES, systemPackagesValue))
.map(jrePackage -> {
String packageName = jrePackage.getValue();
String version = jrePackage.getAttribute("version");
return new SystemPackageEntry(packageName, version);
}).collect(Collectors.toList());
} catch (BundleException e) {
logger.error(e.getMessage(), e);
this.systemPackages = Collections.emptyList();
}
}
} else if (toolchain != null) {
logger.debug(
"No system.packages in profile definition file for " + profileName + "; checking toolchain.");
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()
+ "; current JRE system packages are used.");
this.systemPackages = ListSystemPackages.getCurrentJREPackages().stream()
.map(packageName -> new SystemPackageEntry(packageName, null)).collect(Collectors.toList());
}
if (this.systemPackages == null || this.systemPackages.isEmpty()) {
logger.warn("No system packages found in profile nor toolchain for " + profileName
+ ", using current JRE system packages.\n"
+ "This can cause faulty dependency resolution, consider adding a definition for a 'jdk' with id="
+ profileName + " in your toolchains.xml");
this.systemPackages = ListSystemPackages.getCurrentJREPackages().stream()
.map(packageName -> new SystemPackageEntry(packageName, null)).collect(Collectors.toList());
}
}
return systemPackages;
}
@Override
public int compareTo(StandardExecutionEnvironment otherEnv) {
return eeVersion.compareTo(otherEnv.eeVersion);
}
@Override
public int hashCode() {
return Objects.hash(compilerSourceLevel, compilerTargetLevel, eeVersion, profileName, profileProperties);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof StandardExecutionEnvironment)) {
return false;
}
StandardExecutionEnvironment other = (StandardExecutionEnvironment) obj;
return Objects.equals(this.compilerSourceLevel, other.compilerSourceLevel)
&& Objects.equals(this.compilerTargetLevel, other.compilerTargetLevel)
&& Objects.equals(this.eeVersion, other.eeVersion)
&& Objects.equals(this.profileName, other.profileName)
&& Objects.equals(this.profileProperties, other.profileProperties);
}
@Override
public Properties getProfileProperties() {
return profileProperties;
}
@Override
public boolean isCompatibleCompilerTargetLevel(String target) {
if (target == null) {
throw new IllegalArgumentException();
}
if (compilerTargetLevel == null) {
return true;
}
try {
Version thisTargetVersion = toTargetVersion(compilerTargetLevel);
Version targetVersion = toTargetVersion(target);
return thisTargetVersion.compareTo(targetVersion) >= 0;
} catch (IllegalArgumentException e) {
// we could not parse one or both of the provided target level, assume they are incompatible
return false;
}
}
private static Version toTargetVersion(String target) {
String targetAlias = TARGET_ALIASES.get(target.trim().toLowerCase());
if (targetAlias != null) {
target = targetAlias;
}
return Version.parseVersion(target);
}
}