Skip to content

Commit edc1423

Browse files
committed
[GR-58214] Respect order of arguments for explict main class and image name.
PullRequest: graal/18896
2 parents e8defa1 + d7ef851 commit edc1423

File tree

1 file changed

+52
-39
lines changed

1 file changed

+52
-39
lines changed

substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/NativeImage.java

Lines changed: 52 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,11 @@
4646
import java.util.Collections;
4747
import java.util.HashMap;
4848
import java.util.HashSet;
49+
import java.util.IdentityHashMap;
4950
import java.util.Iterator;
5051
import java.util.LinkedHashMap;
5152
import java.util.LinkedHashSet;
5253
import java.util.List;
53-
import java.util.ListIterator;
5454
import java.util.Locale;
5555
import java.util.Map;
5656
import java.util.Optional;
@@ -272,6 +272,7 @@ private static <T> String oR(OptionKey<T> option) {
272272

273273
final Map<String, String> imageBuilderEnvironment = new HashMap<>();
274274
private final ArrayList<String> imageBuilderArgs = new ArrayList<>();
275+
private final Set<String> imageBuilderUniqueLeftoverArgs = Collections.newSetFromMap(new IdentityHashMap<>());
275276
private final LinkedHashSet<Path> imageBuilderModulePath = new LinkedHashSet<>();
276277
private final LinkedHashSet<Path> imageBuilderClasspath = new LinkedHashSet<>();
277278
private final LinkedHashSet<Path> imageProvidedJars = new LinkedHashSet<>();
@@ -1124,7 +1125,7 @@ private Stream<Path> resolveTargetSpecificPaths(Path base) {
11241125
}
11251126

11261127
private int completeImageBuild() {
1127-
List<String> leftoverArgs = processNativeImageArgs();
1128+
processNativeImageArgs();
11281129
apiOptionHandler.validateExperimentalOptions();
11291130

11301131
config.getBuilderClasspath().forEach(this::addImageBuilderClasspath);
@@ -1220,7 +1221,8 @@ private int completeImageBuild() {
12201221

12211222
imageBuilderJavaArgs.addAll(getAgentArguments());
12221223

1223-
mainClass = getHostedOptionArgumentValue(imageBuilderArgs, oHClass);
1224+
Optional<ArgumentEntry> lastMainClass = getHostedOptionArgument(imageBuilderArgs, oHClass);
1225+
mainClass = lastMainClass.map(ArgumentEntry::value).orElse(null);
12241226
buildExecutable = imageBuilderArgs.stream().noneMatch(arg -> arg.startsWith(oHEnableSharedLibraryFlagPrefix) || arg.startsWith(oHEnableImageLayerFlagPrefix));
12251227
staticExecutable = imageBuilderArgs.stream().anyMatch(arg -> arg.contains(oHEnableStaticExecutable));
12261228
boolean listModules = imageBuilderArgs.stream().anyMatch(arg -> arg.contains(oH + "+" + "ListModules"));
@@ -1230,21 +1232,18 @@ private int completeImageBuild() {
12301232
/* Ensure name for bundle support */
12311233
addPlainImageBuilderArg(oHName + "dummy-image");
12321234
} else {
1233-
List<String> extraImageArgs = new ArrayList<>();
1234-
ListIterator<String> leftoverArgsItr = leftoverArgs.listIterator();
1235-
while (leftoverArgsItr.hasNext()) {
1236-
String leftoverArg = leftoverArgsItr.next();
1237-
if (!leftoverArg.startsWith("-")) {
1238-
leftoverArgsItr.remove();
1239-
extraImageArgs.add(leftoverArg);
1235+
List<ArgumentEntry> extraImageArgs = new ArrayList<>();
1236+
for (int i = 0, imageBuilderArgsSize = imageBuilderArgs.size(); i < imageBuilderArgsSize; i++) {
1237+
String builderArg = imageBuilderArgs.get(i);
1238+
if (imageBuilderUniqueLeftoverArgs.contains(builderArg)) {
1239+
extraImageArgs.add(new ArgumentEntry(i, builderArg));
12401240
}
12411241
}
12421242

1243+
Optional<ArgumentEntry> lastImageName = getHostedOptionArgument(imageBuilderArgs, oHName);
1244+
12431245
if (!jarOptionMode) {
1244-
/* Main-class from customImageBuilderArgs counts as explicitMainClass */
1245-
boolean explicitMainClass = getHostedOptionArgumentValue(imageBuilderArgs, oHClass) != null;
12461246
mainClassModule = getHostedOptionArgumentValue(imageBuilderArgs, oHModule);
1247-
12481247
boolean hasMainClassModule = mainClassModule != null && !mainClassModule.isEmpty();
12491248
boolean hasMainClass = mainClass != null && !mainClass.isEmpty();
12501249
if (extraImageArgs.isEmpty()) {
@@ -1254,18 +1253,22 @@ private int completeImageBuild() {
12541253
}
12551254
} else if (!moduleOptionMode) {
12561255
/* extraImageArgs main-class overrules previous main-class specification */
1257-
explicitMainClass = true;
1258-
mainClass = extraImageArgs.remove(0);
1259-
imageBuilderArgs.add(oH(SubstrateOptions.Class, "explicit main-class") + mainClass);
1256+
ArgumentEntry extraMainClass = extraImageArgs.removeFirst();
1257+
boolean extraMainClassIsLast = lastMainClass.isEmpty() || lastMainClass.get().index < extraMainClass.index;
1258+
if (extraMainClassIsLast) {
1259+
hasMainClass = true;
1260+
mainClass = extraMainClass.value;
1261+
imageBuilderArgs.add(oH(SubstrateOptions.Class, "explicit main-class") + mainClass);
1262+
}
12601263
}
12611264

12621265
if (extraImageArgs.isEmpty()) {
12631266
/* No explicit image name, define image name by other means */
1264-
if (getHostedOptionArgumentValue(imageBuilderArgs, oHName) == null) {
1267+
if (lastImageName.isEmpty()) {
12651268
/* Also no explicit image name given as customImageBuilderArgs */
1266-
if (explicitMainClass) {
1269+
if (hasMainClass) {
12671270
imageBuilderArgs.add(oH(SubstrateOptions.Name, "main-class lower case as image name") + mainClass.toLowerCase(Locale.ROOT));
1268-
} else if (getHostedOptionArgumentValue(imageBuilderArgs, oHName) == null) {
1271+
} else {
12691272
if (hasMainClassModule) {
12701273
imageBuilderArgs.add(oH(SubstrateOptions.Name, "image-name from module-name") + mainClassModule.toLowerCase(Locale.ROOT));
12711274
} else if (!listModules) {
@@ -1275,19 +1278,31 @@ private int completeImageBuild() {
12751278
}
12761279
}
12771280
} else {
1278-
/* extraImageArgs executable name overrules previous specification */
1279-
imageBuilderArgs.add(oH(SubstrateOptions.Name, "explicit image name") + extraImageArgs.remove(0));
1281+
ArgumentEntry extraImageName = extraImageArgs.removeFirst();
1282+
boolean extraNameIsLast = lastImageName.isEmpty() || lastImageName.get().index < extraImageName.index;
1283+
if (extraNameIsLast) {
1284+
/* extraImageArg that comes after lastImageName wins */
1285+
imageBuilderArgs.add(oH(SubstrateOptions.Name, "explicit image name") + extraImageName.value);
1286+
}
12801287
}
1281-
} else {
1288+
} else { /* jarOptionMode */
12821289
if (!extraImageArgs.isEmpty()) {
1283-
/* extraImageArgs library name overrules previous specification */
1284-
imageBuilderArgs.add(oH(SubstrateOptions.Name, "explicit image name") + extraImageArgs.remove(0));
1290+
ArgumentEntry extraImageName = extraImageArgs.removeFirst();
1291+
boolean extraNameIsLast = lastImageName.isEmpty() || lastImageName.get().index < extraImageName.index;
1292+
if (extraNameIsLast) {
1293+
/* extraImageArg that comes after lastImageName wins */
1294+
imageBuilderArgs.add(oH(SubstrateOptions.Name, "explicit image name") + extraImageName.value);
1295+
}
12851296
}
12861297
}
12871298

12881299
if (!extraImageArgs.isEmpty()) {
1289-
showError("Unknown argument(s): " + StringUtil.joinSingleQuoted(extraImageArgs));
1300+
showError("Unrecognized option(s): " + StringUtil.joinSingleQuoted(extraImageArgs.stream().map(ArgumentEntry::value).toList()));
12901301
}
1302+
1303+
/* Remove consumed extraImageArgs from imageBuilderArgs */
1304+
imageBuilderArgs.removeIf(imageBuilderUniqueLeftoverArgs::contains);
1305+
imageBuilderUniqueLeftoverArgs.clear();
12911306
}
12921307

12931308
ArgumentEntry imageNameEntry = getHostedOptionArgument(imageBuilderArgs, oHName).orElseThrow();
@@ -1342,10 +1357,6 @@ private int completeImageBuild() {
13421357
}
13431358
addPlainImageBuilderArg(oH(SubstrateOptions.ImageBuildID, OptionOrigin.originDriver) + imageBuildID);
13441359

1345-
if (!leftoverArgs.isEmpty()) {
1346-
showError("Unrecognized option(s): " + StringUtil.joinSingleQuoted(leftoverArgs));
1347-
}
1348-
13491360
LinkedHashSet<Path> finalImageModulePath = new LinkedHashSet<>(imageModulePath);
13501361
LinkedHashSet<Path> finalImageClasspath = new LinkedHashSet<>(imageClasspath);
13511362

@@ -1406,7 +1417,7 @@ private static String getHostedOptionArgumentValue(List<String> args, String arg
14061417

14071418
private static Optional<ArgumentEntry> getHostedOptionArgument(List<String> args, String argPrefix) {
14081419
List<ArgumentEntry> values = getHostedOptionArgumentValues(args, argPrefix);
1409-
return values.isEmpty() ? Optional.empty() : Optional.of(values.get(values.size() - 1));
1420+
return values.isEmpty() ? Optional.empty() : Optional.of(values.getLast());
14101421
}
14111422

14121423
private static List<ArgumentEntry> getHostedOptionArgumentValues(List<String> args, String argPrefix) {
@@ -2026,6 +2037,7 @@ void addImageBuilderJavaArgs(Collection<String> javaArgs) {
20262037
}
20272038

20282039
class NativeImageArgsProcessor implements Consumer<String> {
2040+
20292041
private final ArgumentQueue args;
20302042

20312043
NativeImageArgsProcessor(String argumentOrigin) {
@@ -2037,7 +2049,7 @@ public void accept(String arg) {
20372049
args.add(arg);
20382050
}
20392051

2040-
List<String> apply(boolean strict) {
2052+
void apply(boolean strict) {
20412053

20422054
ArgumentQueue queue = new ArgumentQueue(args.argumentOrigin);
20432055
while (!args.isEmpty()) {
@@ -2051,11 +2063,9 @@ List<String> apply(boolean strict) {
20512063

20522064
apiOptionHandler.ensureConsistentUnlockScopes(queue);
20532065

2054-
List<String> leftoverArgs = new ArrayList<>();
20552066
while (!queue.isEmpty()) {
20562067
boolean consumed = false;
2057-
for (int index = optionHandlers.size() - 1; index >= 0; --index) {
2058-
OptionHandler<? extends NativeImage> handler = optionHandlers.get(index);
2068+
for (OptionHandler<? extends NativeImage> handler : optionHandlers.reversed()) {
20592069
int numArgs = queue.size();
20602070
if (handler.consume(queue)) {
20612071
assert queue.size() < numArgs : "OptionHandler pretends to consume argument(s) but isn't: " + handler.getClass().getName();
@@ -2067,12 +2077,15 @@ List<String> apply(boolean strict) {
20672077
if (strict) {
20682078
showError("Property 'Args' contains invalid entry '" + queue.peek() + "'");
20692079
} else {
2070-
leftoverArgs.add(queue.poll());
2080+
/* Ensure unique object identity for leftover arg */
2081+
String uniqueLeftoverArg = new String(queue.poll());
2082+
/* Remember this exact leftover by adding to IdentityHashSet */
2083+
imageBuilderUniqueLeftoverArgs.add(uniqueLeftoverArg);
2084+
/* Insert leftover into imageBuilderArgs for further processing */
2085+
imageBuilderArgs.add(uniqueLeftoverArg);
20712086
}
20722087
}
20732088
}
2074-
2075-
return leftoverArgs;
20762089
}
20772090
}
20782091

@@ -2390,12 +2403,12 @@ protected static List<Path> getJars(Path dir, String... jarBaseNames) {
23902403
}
23912404
}
23922405

2393-
private List<String> processNativeImageArgs() {
2406+
private void processNativeImageArgs() {
23942407
NativeImageArgsProcessor argsProcessor = new NativeImageArgsProcessor(OptionOrigin.originUser);
23952408
for (String arg : getNativeImageArgs()) {
23962409
argsProcessor.accept(arg);
23972410
}
2398-
return argsProcessor.apply(false);
2411+
argsProcessor.apply(false);
23992412
}
24002413

24012414
List<String> getNativeImageArgs() {

0 commit comments

Comments
 (0)