@@ -260,6 +260,27 @@ def updateEnv(env, args):
260
260
env .env [key ] = val
261
261
return args [arg_idx_next :]
262
262
263
+ def executeBuiltinCd (cmd , shenv ):
264
+ """executeBuiltinCd - Change the current directory."""
265
+ if len (cmd .args ) != 2 :
266
+ raise InternalShellError ("'cd' supports only one argument" )
267
+ newdir = cmd .args [1 ]
268
+ # Update the cwd in the parent environment.
269
+ if os .path .isabs (newdir ):
270
+ shenv .cwd = newdir
271
+ else :
272
+ shenv .cwd = os .path .realpath (os .path .join (shenv .cwd , newdir ))
273
+ # The cd builtin always succeeds. If the directory does not exist, the
274
+ # following Popen calls will fail instead.
275
+ return ShellCommandResult (cmd , "" , "" , 0 , False )
276
+
277
+ def executeBuiltinExport (cmd , shenv ):
278
+ """executeBuiltinExport - Set an environment variable."""
279
+ if len (cmd .args ) != 2 :
280
+ raise InternalShellError ("'export' supports only one argument" )
281
+ updateEnv (shenv , cmd .args )
282
+ return ShellCommandResult (cmd , "" , "" , 0 , False )
283
+
263
284
def executeBuiltinEcho (cmd , shenv ):
264
285
"""Interpret a redirected echo command"""
265
286
opened_files = []
@@ -319,9 +340,8 @@ def maybeUnescape(arg):
319
340
for (name , mode , f , path ) in opened_files :
320
341
f .close ()
321
342
322
- if not is_redirected :
323
- return stdout .getvalue ()
324
- return ""
343
+ output = "" if is_redirected else stdout .getvalue ()
344
+ return ShellCommandResult (cmd , output , "" , 0 , False )
325
345
326
346
def executeBuiltinMkdir (cmd , cmd_shenv ):
327
347
"""executeBuiltinMkdir - Create new directories."""
@@ -456,6 +476,10 @@ class SHFILEOPSTRUCTW(Structure):
456
476
exitCode = 1
457
477
return ShellCommandResult (cmd , "" , stderr .getvalue (), exitCode , False )
458
478
479
+ def executeBuiltinColon (cmd , cmd_shenv ):
480
+ """executeBuiltinColon - Discard arguments and exit with status 0."""
481
+ return ShellCommandResult (cmd , "" , "" , 0 , False )
482
+
459
483
def processRedirects (cmd , stdin_source , cmd_shenv , opened_files ):
460
484
"""Return the standard fds for cmd after applying redirects
461
485
@@ -581,71 +605,19 @@ def _executeShCmd(cmd, shenv, results, timeoutHelper):
581
605
raise ValueError ('Unknown shell command: %r' % cmd .op )
582
606
assert isinstance (cmd , ShUtil .Pipeline )
583
607
584
- # Handle shell builtins first.
585
- if cmd .commands [0 ].args [0 ] == 'cd' :
586
- if len (cmd .commands ) != 1 :
587
- raise ValueError ("'cd' cannot be part of a pipeline" )
588
- if len (cmd .commands [0 ].args ) != 2 :
589
- raise ValueError ("'cd' supports only one argument" )
590
- newdir = cmd .commands [0 ].args [1 ]
591
- # Update the cwd in the parent environment.
592
- if os .path .isabs (newdir ):
593
- shenv .cwd = newdir
594
- else :
595
- shenv .cwd = os .path .realpath (os .path .join (shenv .cwd , newdir ))
596
- # The cd builtin always succeeds. If the directory does not exist, the
597
- # following Popen calls will fail instead.
598
- return 0
599
-
600
- # Handle "echo" as a builtin if it is not part of a pipeline. This greatly
601
- # speeds up tests that construct input files by repeatedly echo-appending to
602
- # a file.
603
- # FIXME: Standardize on the builtin echo implementation. We can use a
604
- # temporary file to sidestep blocking pipe write issues.
605
- if cmd .commands [0 ].args [0 ] == 'echo' and len (cmd .commands ) == 1 :
606
- output = executeBuiltinEcho (cmd .commands [0 ], shenv )
607
- results .append (ShellCommandResult (cmd .commands [0 ], output , "" , 0 ,
608
- False ))
609
- return 0
610
-
611
- if cmd .commands [0 ].args [0 ] == 'export' :
612
- if len (cmd .commands ) != 1 :
613
- raise ValueError ("'export' cannot be part of a pipeline" )
614
- if len (cmd .commands [0 ].args ) != 2 :
615
- raise ValueError ("'export' supports only one argument" )
616
- updateEnv (shenv , cmd .commands [0 ].args )
617
- return 0
618
-
619
- if cmd .commands [0 ].args [0 ] == 'mkdir' :
620
- if len (cmd .commands ) != 1 :
621
- raise InternalShellError (cmd .commands [0 ], "Unsupported: 'mkdir' "
622
- "cannot be part of a pipeline" )
623
- cmdResult = executeBuiltinMkdir (cmd .commands [0 ], shenv )
624
- results .append (cmdResult )
625
- return cmdResult .exitCode
626
-
627
- if cmd .commands [0 ].args [0 ] == 'rm' :
628
- if len (cmd .commands ) != 1 :
629
- raise InternalShellError (cmd .commands [0 ], "Unsupported: 'rm' "
630
- "cannot be part of a pipeline" )
631
- cmdResult = executeBuiltinRm (cmd .commands [0 ], shenv )
632
- results .append (cmdResult )
633
- return cmdResult .exitCode
634
-
635
- if cmd .commands [0 ].args [0 ] == ':' :
636
- if len (cmd .commands ) != 1 :
637
- raise InternalShellError (cmd .commands [0 ], "Unsupported: ':' "
638
- "cannot be part of a pipeline" )
639
- results .append (ShellCommandResult (cmd .commands [0 ], '' , '' , 0 , False ))
640
- return 0 ;
641
-
642
608
procs = []
643
609
default_stdin = subprocess .PIPE
644
610
stderrTempFiles = []
645
611
opened_files = []
646
612
named_temp_files = []
647
613
builtin_commands = set (['cat' , 'diff' ])
648
614
builtin_commands_dir = os .path .join (os .path .dirname (os .path .abspath (__file__ )), "builtin_commands" )
615
+ inproc_builtins = {'cd' : executeBuiltinCd ,
616
+ 'export' : executeBuiltinExport ,
617
+ 'echo' : executeBuiltinEcho ,
618
+ 'mkdir' : executeBuiltinMkdir ,
619
+ 'rm' : executeBuiltinRm ,
620
+ ':' : executeBuiltinColon }
649
621
# To avoid deadlock, we use a single stderr stream for piped
650
622
# output. This is null until we have seen some output using
651
623
# stderr.
@@ -663,6 +635,27 @@ def _executeShCmd(cmd, shenv, results, timeoutHelper):
663
635
raise InternalShellError (j ,
664
636
"Error: 'env' requires a subcommand" )
665
637
638
+ # Handle in-process builtins.
639
+ #
640
+ # Handle "echo" as a builtin if it is not part of a pipeline. This
641
+ # greatly speeds up tests that construct input files by repeatedly
642
+ # echo-appending to a file.
643
+ # FIXME: Standardize on the builtin echo implementation. We can use a
644
+ # temporary file to sidestep blocking pipe write issues.
645
+ inproc_builtin = inproc_builtins .get (args [0 ], None )
646
+ if inproc_builtin and (args [0 ] != 'echo' or len (cmd .commands ) == 1 ):
647
+ # env calling an in-process builtin is useless, so we take the safe
648
+ # approach of complaining.
649
+ if not cmd_shenv is shenv :
650
+ raise InternalShellError (j , "Error: 'env' cannot call '{}'"
651
+ .format (args [0 ]))
652
+ if len (cmd .commands ) != 1 :
653
+ raise InternalShellError (j , "Unsupported: '{}' cannot be part"
654
+ " of a pipeline" .format (args [0 ]))
655
+ result = inproc_builtin (j , cmd_shenv )
656
+ results .append (result )
657
+ return result .exitCode
658
+
666
659
stdin , stdout , stderr = processRedirects (j , default_stdin , cmd_shenv ,
667
660
opened_files )
668
661
0 commit comments