46
46
import java .util .Collections ;
47
47
import java .util .HashMap ;
48
48
import java .util .HashSet ;
49
+ import java .util .IdentityHashMap ;
49
50
import java .util .Iterator ;
50
51
import java .util .LinkedHashMap ;
51
52
import java .util .LinkedHashSet ;
52
53
import java .util .List ;
53
- import java .util .ListIterator ;
54
54
import java .util .Locale ;
55
55
import java .util .Map ;
56
56
import java .util .Optional ;
@@ -272,6 +272,7 @@ private static <T> String oR(OptionKey<T> option) {
272
272
273
273
final Map <String , String > imageBuilderEnvironment = new HashMap <>();
274
274
private final ArrayList <String > imageBuilderArgs = new ArrayList <>();
275
+ private final Set <String > imageBuilderUniqueLeftoverArgs = Collections .newSetFromMap (new IdentityHashMap <>());
275
276
private final LinkedHashSet <Path > imageBuilderModulePath = new LinkedHashSet <>();
276
277
private final LinkedHashSet <Path > imageBuilderClasspath = new LinkedHashSet <>();
277
278
private final LinkedHashSet <Path > imageProvidedJars = new LinkedHashSet <>();
@@ -1124,7 +1125,7 @@ private Stream<Path> resolveTargetSpecificPaths(Path base) {
1124
1125
}
1125
1126
1126
1127
private int completeImageBuild () {
1127
- List < String > leftoverArgs = processNativeImageArgs ();
1128
+ processNativeImageArgs ();
1128
1129
apiOptionHandler .validateExperimentalOptions ();
1129
1130
1130
1131
config .getBuilderClasspath ().forEach (this ::addImageBuilderClasspath );
@@ -1220,7 +1221,8 @@ private int completeImageBuild() {
1220
1221
1221
1222
imageBuilderJavaArgs .addAll (getAgentArguments ());
1222
1223
1223
- mainClass = getHostedOptionArgumentValue (imageBuilderArgs , oHClass );
1224
+ Optional <ArgumentEntry > lastMainClass = getHostedOptionArgument (imageBuilderArgs , oHClass );
1225
+ mainClass = lastMainClass .map (ArgumentEntry ::value ).orElse (null );
1224
1226
buildExecutable = imageBuilderArgs .stream ().noneMatch (arg -> arg .startsWith (oHEnableSharedLibraryFlagPrefix ) || arg .startsWith (oHEnableImageLayerFlagPrefix ));
1225
1227
staticExecutable = imageBuilderArgs .stream ().anyMatch (arg -> arg .contains (oHEnableStaticExecutable ));
1226
1228
boolean listModules = imageBuilderArgs .stream ().anyMatch (arg -> arg .contains (oH + "+" + "ListModules" ));
@@ -1230,21 +1232,18 @@ private int completeImageBuild() {
1230
1232
/* Ensure name for bundle support */
1231
1233
addPlainImageBuilderArg (oHName + "dummy-image" );
1232
1234
} 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 ));
1240
1240
}
1241
1241
}
1242
1242
1243
+ Optional <ArgumentEntry > lastImageName = getHostedOptionArgument (imageBuilderArgs , oHName );
1244
+
1243
1245
if (!jarOptionMode ) {
1244
- /* Main-class from customImageBuilderArgs counts as explicitMainClass */
1245
- boolean explicitMainClass = getHostedOptionArgumentValue (imageBuilderArgs , oHClass ) != null ;
1246
1246
mainClassModule = getHostedOptionArgumentValue (imageBuilderArgs , oHModule );
1247
-
1248
1247
boolean hasMainClassModule = mainClassModule != null && !mainClassModule .isEmpty ();
1249
1248
boolean hasMainClass = mainClass != null && !mainClass .isEmpty ();
1250
1249
if (extraImageArgs .isEmpty ()) {
@@ -1254,18 +1253,22 @@ private int completeImageBuild() {
1254
1253
}
1255
1254
} else if (!moduleOptionMode ) {
1256
1255
/* 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
+ }
1260
1263
}
1261
1264
1262
1265
if (extraImageArgs .isEmpty ()) {
1263
1266
/* No explicit image name, define image name by other means */
1264
- if (getHostedOptionArgumentValue ( imageBuilderArgs , oHName ) == null ) {
1267
+ if (lastImageName . isEmpty () ) {
1265
1268
/* Also no explicit image name given as customImageBuilderArgs */
1266
- if (explicitMainClass ) {
1269
+ if (hasMainClass ) {
1267
1270
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 {
1269
1272
if (hasMainClassModule ) {
1270
1273
imageBuilderArgs .add (oH (SubstrateOptions .Name , "image-name from module-name" ) + mainClassModule .toLowerCase (Locale .ROOT ));
1271
1274
} else if (!listModules ) {
@@ -1275,19 +1278,31 @@ private int completeImageBuild() {
1275
1278
}
1276
1279
}
1277
1280
} 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
+ }
1280
1287
}
1281
- } else {
1288
+ } else { /* jarOptionMode */
1282
1289
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
+ }
1285
1296
}
1286
1297
}
1287
1298
1288
1299
if (!extraImageArgs .isEmpty ()) {
1289
- showError ("Unknown argument (s): " + StringUtil .joinSingleQuoted (extraImageArgs ));
1300
+ showError ("Unrecognized option (s): " + StringUtil .joinSingleQuoted (extraImageArgs . stream (). map ( ArgumentEntry :: value ). toList () ));
1290
1301
}
1302
+
1303
+ /* Remove consumed extraImageArgs from imageBuilderArgs */
1304
+ imageBuilderArgs .removeIf (imageBuilderUniqueLeftoverArgs ::contains );
1305
+ imageBuilderUniqueLeftoverArgs .clear ();
1291
1306
}
1292
1307
1293
1308
ArgumentEntry imageNameEntry = getHostedOptionArgument (imageBuilderArgs , oHName ).orElseThrow ();
@@ -1342,10 +1357,6 @@ private int completeImageBuild() {
1342
1357
}
1343
1358
addPlainImageBuilderArg (oH (SubstrateOptions .ImageBuildID , OptionOrigin .originDriver ) + imageBuildID );
1344
1359
1345
- if (!leftoverArgs .isEmpty ()) {
1346
- showError ("Unrecognized option(s): " + StringUtil .joinSingleQuoted (leftoverArgs ));
1347
- }
1348
-
1349
1360
LinkedHashSet <Path > finalImageModulePath = new LinkedHashSet <>(imageModulePath );
1350
1361
LinkedHashSet <Path > finalImageClasspath = new LinkedHashSet <>(imageClasspath );
1351
1362
@@ -1406,7 +1417,7 @@ private static String getHostedOptionArgumentValue(List<String> args, String arg
1406
1417
1407
1418
private static Optional <ArgumentEntry > getHostedOptionArgument (List <String > args , String argPrefix ) {
1408
1419
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 ( ));
1410
1421
}
1411
1422
1412
1423
private static List <ArgumentEntry > getHostedOptionArgumentValues (List <String > args , String argPrefix ) {
@@ -2026,6 +2037,7 @@ void addImageBuilderJavaArgs(Collection<String> javaArgs) {
2026
2037
}
2027
2038
2028
2039
class NativeImageArgsProcessor implements Consumer <String > {
2040
+
2029
2041
private final ArgumentQueue args ;
2030
2042
2031
2043
NativeImageArgsProcessor (String argumentOrigin ) {
@@ -2037,7 +2049,7 @@ public void accept(String arg) {
2037
2049
args .add (arg );
2038
2050
}
2039
2051
2040
- List < String > apply (boolean strict ) {
2052
+ void apply (boolean strict ) {
2041
2053
2042
2054
ArgumentQueue queue = new ArgumentQueue (args .argumentOrigin );
2043
2055
while (!args .isEmpty ()) {
@@ -2051,11 +2063,9 @@ List<String> apply(boolean strict) {
2051
2063
2052
2064
apiOptionHandler .ensureConsistentUnlockScopes (queue );
2053
2065
2054
- List <String > leftoverArgs = new ArrayList <>();
2055
2066
while (!queue .isEmpty ()) {
2056
2067
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 ()) {
2059
2069
int numArgs = queue .size ();
2060
2070
if (handler .consume (queue )) {
2061
2071
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) {
2067
2077
if (strict ) {
2068
2078
showError ("Property 'Args' contains invalid entry '" + queue .peek () + "'" );
2069
2079
} 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 );
2071
2086
}
2072
2087
}
2073
2088
}
2074
-
2075
- return leftoverArgs ;
2076
2089
}
2077
2090
}
2078
2091
@@ -2390,12 +2403,12 @@ protected static List<Path> getJars(Path dir, String... jarBaseNames) {
2390
2403
}
2391
2404
}
2392
2405
2393
- private List < String > processNativeImageArgs () {
2406
+ private void processNativeImageArgs () {
2394
2407
NativeImageArgsProcessor argsProcessor = new NativeImageArgsProcessor (OptionOrigin .originUser );
2395
2408
for (String arg : getNativeImageArgs ()) {
2396
2409
argsProcessor .accept (arg );
2397
2410
}
2398
- return argsProcessor .apply (false );
2411
+ argsProcessor .apply (false );
2399
2412
}
2400
2413
2401
2414
List <String > getNativeImageArgs () {
0 commit comments