39
39
import java .util .Set ;
40
40
41
41
import org .graalvm .launcher .AbstractLanguageLauncher ;
42
+ import org .graalvm .nativeimage .ImageInfo ;
42
43
import org .graalvm .nativeimage .ProcessProperties ;
43
44
import org .graalvm .options .OptionCategory ;
44
45
import org .graalvm .polyglot .Context ;
@@ -72,11 +73,15 @@ public static void main(String[] args) {
72
73
private boolean runLLI = false ;
73
74
private VersionAction versionAction = VersionAction .None ;
74
75
private String sulongLibraryPath = null ;
76
+ private List <String > givenArguments ;
75
77
76
78
@ Override
77
- protected List <String > preprocessArguments (List <String > arguments , Map <String , String > polyglotOptions ) {
79
+ protected List <String > preprocessArguments (List <String > givenArgs , Map <String , String > polyglotOptions ) {
78
80
ArrayList <String > unrecognized = new ArrayList <>();
79
- List <String > inputArgs = new ArrayList <>(arguments );
81
+ ArrayList <String > inputArgs = new ArrayList <>(getDefaultEnvironmentArgs ());
82
+ inputArgs .addAll (givenArgs );
83
+ givenArguments = new ArrayList <>(inputArgs );
84
+ List <String > arguments = new ArrayList <>(inputArgs );
80
85
List <String > subprocessArgs = new ArrayList <>();
81
86
programArgs = new ArrayList <>();
82
87
for (int i = 0 ; i < arguments .size (); i ++) {
@@ -201,6 +206,60 @@ protected List<String> preprocessArguments(List<String> arguments, Map<String, S
201
206
return unrecognized ;
202
207
}
203
208
209
+ private static enum State {
210
+ NORMAL ,
211
+ SINGLE_QUOTE ,
212
+ DOUBLE_QUOTE ,
213
+ ESCAPE_SINGLE_QUOTE ,
214
+ ESCAPE_DOUBLE_QUOTE ,
215
+ }
216
+
217
+ private static List <String > getDefaultEnvironmentArgs () {
218
+ String envArgsOpt = System .getenv ("GRAAL_PYTHON_OPTIONS" );
219
+ ArrayList <String > envArgs = new ArrayList <>();
220
+ State s = State .NORMAL ;
221
+ StringBuilder sb = new StringBuilder ();
222
+ if (envArgsOpt != null ) {
223
+ for (char x : envArgsOpt .toCharArray ()) {
224
+ if (s == State .NORMAL && Character .isWhitespace (x )) {
225
+ if (sb .length () > 0 ) {
226
+ envArgs .add (sb .toString ());
227
+ sb .setLength (0 );
228
+ }
229
+ } else {
230
+ if (x == '"' ) {
231
+ if (s == State .NORMAL ) {
232
+ s = State .DOUBLE_QUOTE ;
233
+ } else if (s == State .DOUBLE_QUOTE ) {
234
+ s = State .NORMAL ;
235
+ } else if (s == State .ESCAPE_DOUBLE_QUOTE ) {
236
+ s = State .DOUBLE_QUOTE ;
237
+ sb .append (x );
238
+ }
239
+ } else if (x == '\'' ) {
240
+ if (s == State .NORMAL ) {
241
+ s = State .SINGLE_QUOTE ;
242
+ } else if (s == State .SINGLE_QUOTE ) {
243
+ s = State .NORMAL ;
244
+ } else if (s == State .ESCAPE_SINGLE_QUOTE ) {
245
+ s = State .SINGLE_QUOTE ;
246
+ sb .append (x );
247
+ }
248
+ } else if (x == '\\' ) {
249
+ if (s == State .SINGLE_QUOTE ) {
250
+ s = State .ESCAPE_SINGLE_QUOTE ;
251
+ } else if (s == State .DOUBLE_QUOTE ) {
252
+ s = State .ESCAPE_DOUBLE_QUOTE ;
253
+ }
254
+ } else {
255
+ sb .append (x );
256
+ }
257
+ }
258
+ }
259
+ }
260
+ return envArgs ;
261
+ }
262
+
204
263
private static void printShortHelp () {
205
264
print ("usage: python [option] ... [-c cmd | -m mod | file | -] [arg] ...\n " +
206
265
"Try `python -h' for more information." );
@@ -210,6 +269,43 @@ private static void print(String string) {
210
269
System .out .println (string );
211
270
}
212
271
272
+ private static String [] getExecutableList () {
273
+ if (ImageInfo .inImageCode ()) {
274
+ return new String []{getExecutable ()};
275
+ } else {
276
+ StringBuilder sb = new StringBuilder ();
277
+ ArrayList <String > exec_list = new ArrayList <>();
278
+ sb .append (System .getProperty ("java.home" )).append (File .separator ).append ("bin" ).append (File .separator ).append ("java" );
279
+ exec_list .add (sb .toString ());
280
+ for (String arg : ManagementFactory .getRuntimeMXBean ().getInputArguments ()) {
281
+ if (arg .matches ("-Xrunjdwp:transport=dt_socket,server=y,address=\\ d+,suspend=y" )) {
282
+ arg = arg .replace ("suspend=y" , "suspend=n" );
283
+ }
284
+ exec_list .add (arg );
285
+ }
286
+ exec_list .add ("-classpath" );
287
+ exec_list .add (System .getProperty ("java.class.path" ));
288
+ exec_list .add (GraalPythonMain .class .getName ());
289
+ return exec_list .toArray (new String [exec_list .size ()]);
290
+ }
291
+ }
292
+
293
+ private static String getExecutable () {
294
+ if (ImageInfo .inImageRuntimeCode ()) {
295
+ return ProcessProperties .getExecutableName ();
296
+ } else if (ImageInfo .inImageBuildtimeCode ()) {
297
+ return "" ;
298
+ } else {
299
+ String [] executableList = getExecutableList ();
300
+ for (int i = 0 ; i < executableList .length ; i ++) {
301
+ if (executableList [i ].matches ("\\ s" )) {
302
+ executableList [i ] = "'" + executableList [i ].replace ("'" , "\\ '" ) + "'" ;
303
+ }
304
+ }
305
+ return String .join (" " , executableList );
306
+ }
307
+ }
308
+
213
309
@ Override
214
310
protected void launch (Builder contextBuilder ) {
215
311
if (runLLI ) {
@@ -233,7 +329,11 @@ protected void launch(Builder contextBuilder) {
233
329
noUserSite = noUserSite || System .getenv ("PYTHONNOUSERSITE" ) != null ;
234
330
verboseFlag = verboseFlag || System .getenv ("PYTHONVERBOSE" ) != null ;
235
331
}
236
- sulongLibraryPath = System .getenv ("SULONG_LIBRARY_PATH" );
332
+
333
+ // The unlikely separator is used because options need to be strings. See
334
+ // PythonOptions.getExecutableList()
335
+ contextBuilder .option ("python.ExecutableList" , String .join ("🏆" , getExecutableList ()));
336
+ setContextOptionIfUnset (contextBuilder , "python.Executable" , getExecutable ());
237
337
238
338
// setting this to make sure our TopLevelExceptionHandler calls the excepthook
239
339
// to print Python exceptions
@@ -246,6 +346,9 @@ protected void launch(Builder contextBuilder) {
246
346
contextBuilder .option ("python.QuietFlag" , Boolean .toString (quietFlag ));
247
347
contextBuilder .option ("python.NoUserSiteFlag" , Boolean .toString (noUserSite ));
248
348
contextBuilder .option ("python.NoSiteFlag" , Boolean .toString (noSite ));
349
+ contextBuilder .option ("python.IgnoreEnvironmentFlag" , Boolean .toString (ignoreEnv ));
350
+
351
+ sulongLibraryPath = System .getenv ("SULONG_LIBRARY_PATH" );
249
352
if (sulongLibraryPath != null ) {
250
353
contextBuilder .option ("llvm.libraryPath" , sulongLibraryPath );
251
354
}
@@ -302,6 +405,18 @@ protected void launch(Builder contextBuilder) {
302
405
System .exit (rc );
303
406
}
304
407
408
+ private void setContextOptionIfUnset (Builder contextBuilder , String key , String value ) {
409
+ if (System .getProperty ("polyglot." + key ) != null ) {
410
+ return ;
411
+ }
412
+ for (String f : givenArguments ) {
413
+ if (f .startsWith ("--" + key )) {
414
+ return ;
415
+ }
416
+ }
417
+ contextBuilder .option (key , value );
418
+ }
419
+
305
420
private static void printFileNotFoundException (NoSuchFileException e ) {
306
421
String reason = e .getReason ();
307
422
if (reason == null ) {
@@ -423,7 +538,10 @@ protected void printHelp(OptionCategory maxCategory) {
423
538
" str, bytes and datetime objects. It can also be set to an integer\n " +
424
539
" in the range [0,4294967295] to get hash values with a predictable seed.\n " +
425
540
"SULONG_LIBRARY_PATH: Specifies the library path for Sulong.\n " +
426
- " This is required when starting subprocesses of python." );
541
+ " This is required when starting subprocesses of python.\n " +
542
+ "GRAAL_PYTHON_OPTIONS: This environment variable can include default options that\n " +
543
+ " are always passed to the launcher. These are not shell expanded and given to\n " +
544
+ " the launcher as-is." );
427
545
if (maxCategory .compareTo (OptionCategory .DEBUG ) >= 0 ) {
428
546
print ("\n GraalPython performance debugging options:\n " +
429
547
"-debug-perf : Enable tracing of Truffle compilations and its warnings\n " +
0 commit comments