def main(argv=None): # Makes ANSI color escapes work on Windows, and strips them when # stdout/stderr isn't a terminal colorama.init() if argv is None: argv = sys.argv[1:] args, unknown = parse_args(argv) for_stack_trace = 'run as "west -v ... {} ..." for a stack trace'.format( args.command) try: args.handler(args, unknown) except WestUpdated: # West has been automatically updated. Restart ourselves to run the # latest version, with the same arguments that we were given. os.execv(sys.executable, [sys.executable] + sys.argv) except KeyboardInterrupt: sys.exit(0) except CalledProcessError as cpe: log.err('command exited with status {}: {}'.format( cpe.args[0], quote_sh_list(cpe.args[1]))) if args.verbose: raise else: log.inf(for_stack_trace) except CommandContextError as cce: log.die('command', args.command, 'cannot be run in this context:', *cce.args) except Exception as exc: log.err(*exc.args, fatal=True) if args.verbose: raise else: log.inf(for_stack_trace)
def call(self, cmd): '''Subclass subprocess.call() wrapper. Subclasses should use this method to run command in a subprocess and get its return code, rather than using subprocess directly, to keep accurate debug logs. ''' quoted = quote_sh_list(cmd) if JUST_PRINT: log.inf(quoted) return 0 log.dbg(quoted) return subprocess.call(cmd)
def check_output(self, cmd): '''Subclass subprocess.check_output() wrapper. Subclasses should use this method to run command in a subprocess and check that it executed correctly, rather than using subprocess directly, to keep accurate debug logs. ''' quoted = quote_sh_list(cmd) if JUST_PRINT: log.inf(quoted) return b'' log.dbg(quoted) try: return subprocess.check_output(cmd) except subprocess.CalledProcessError: raise
def popen_ignore_int(self, cmd): '''Spawn a child command, ensuring it ignores SIGINT. The returned subprocess.Popen object must be manually terminated.''' cflags = 0 preexec = None system = platform.system() quoted = quote_sh_list(cmd) if system == 'Windows': cflags |= subprocess.CREATE_NEW_PROCESS_GROUP elif system in {'Linux', 'Darwin'}: preexec = os.setsid if JUST_PRINT: log.inf(quoted) return _DebugDummyPopen() log.dbg(quoted) return subprocess.Popen(cmd, creationflags=cflags, preexec_fn=preexec)
def _git_helper(project, cmd, extra_args, cwd, capture_stdout, check): # Runs a git command. # # project: # The Project instance for the project, derived from the manifest file. # # cmd: # String with git arguments. Supports some "(foo)" shorthands. See below. # # extra_args: # List of additional arguments to pass to the git command (e.g. from the # user). # # cwd: # Directory to switch to first (None = current directory) # # capture_stdout: # True if stdout should be captured into the returned # subprocess.CompletedProcess instance instead of being printed. # # We never capture stderr, to prevent error messages from being eaten. # # check: # True if an error should be raised if the git command finishes with a # non-zero return code. # # Returns a subprocess.CompletedProcess instance. # TODO: Run once somewhere? if shutil.which('git') is None: log.die('Git is not installed or cannot be found') args = (('git', ) + tuple(_expand_shorthands(project, arg) for arg in cmd.split()) + tuple(extra_args)) cmd_str = util.quote_sh_list(args) log.dbg("running '{}'".format(cmd_str), 'in', cwd, level=log.VERBOSE_VERY) popen = subprocess.Popen( args, stdout=subprocess.PIPE if capture_stdout else None, cwd=cwd) stdout, _ = popen.communicate() dbg_msg = "'{}' in {} finished with exit status {}" \ .format(cmd_str, cwd, popen.returncode) if capture_stdout: dbg_msg += " and wrote {} to stdout".format(stdout) log.dbg(dbg_msg, level=log.VERBOSE_VERY) if check and popen.returncode: _die(project, "Command '{}' failed for (name-and-path)".format(cmd_str)) if capture_stdout: # Manual UTF-8 decoding and universal newlines. Before Python 3.6, # Popen doesn't seem to allow using universal newlines mode (which # enables decoding) with a specific encoding (because the encoding= # parameter is missing). # # Also strip all trailing newlines as convenience. The splitlines() # already means we lose a final '\n' anyway. stdout = "\n".join(stdout.decode('utf-8').splitlines()).rstrip("\n") return CompletedProcess(popen.args, popen.returncode, stdout)