fbcode_builder: getdeps: add run_cmd() function

Summary:
This runs a command, raising an exception if it exits with a non-zero error status.

It prints out the arguments in a mostly copy-and-pasteable form, with PATH-like
env vars pretty printed to make it easier to see what is being invoked; here's
an example of how cmake is being invoked later in this stack:

```
 ---
+ CMAKE_PREFIX_PATH=\
+      /data/users/wez/scratch/dataZusersZwezZfbsource/fbcode_builder_getdeps/installed/ninja-5d7ec7:\
+      /data/users/wez/scratch/dataZusersZwezZfbsource/fbcode_builder_getdeps/installed/cmake-91dc9a:\
+ PKG_CONFIG_PATH=\
+      /data/users/wez/scratch/dataZusersZwezZfbsource/fbcode_builder_getdeps/installed/ninja-5d7ec7/lib/pkgconfig:\
+      /data/users/wez/scratch/dataZusersZwezZfbsource/fbcode_builder_getdeps/installed/cmake-91dc9a/lib/pkgconfig:\
+ cd /data/users/wez/scratch/dataZusersZwezZfbsource/fbcode_builder_getdeps/build/zstd-470344 && \
+ cmake configure /data/users/wez/scratch/dataZusersZwezZfbsource/fbcode_builder_getdeps/repos/github.com-facebook-zstd.git/build/cmake -DCMAKE_INST
ALL_PREFIX=/data/users/wez/scratch/dataZusersZwezZfbsource/fbcode_builder_getdeps/installed/zstd-470344 -DBUILD_SHARED_LIBS=OFF -DCMAKE_BUILD_TYPE=R
elWithDebInfo -G Ninja
```

Reviewed By: simpkins

Differential Revision: D14690999

fbshipit-source-id: cdb0c681c7dfdfdc6e8c96bf4830bfbcf666411b
This commit is contained in:
Wez Furlong 2019-05-03 15:52:39 -07:00 committed by Facebook Github Bot
parent 0bc46905d0
commit 3d0d9e6d53

View File

@ -0,0 +1,87 @@
# Copyright (c) 2019-present, Facebook, Inc.
# All rights reserved.
#
# This source code is licensed under the BSD-style license found in the
# LICENSE file in the root directory of this source tree. An additional grant
# of patent rights can be found in the PATENTS file in the same directory.
from __future__ import absolute_import, division, print_function, unicode_literals
import os
import subprocess
from .envfuncs import Env
from .platform import is_windows
try:
from shlex import quote as shellquote
except ImportError:
from pipes import quote as shellquote
class RunCommandError(Exception):
pass
def _print_env_diff(env):
current_keys = set(os.environ.keys())
wanted_env = set(env.keys())
unset_keys = current_keys.difference(wanted_env)
for k in sorted(unset_keys):
print("+ unset %s" % k)
added_keys = wanted_env.difference(current_keys)
for k in wanted_env.intersection(current_keys):
if os.environ[k] != env[k]:
added_keys.add(k)
for k in sorted(added_keys):
if ("PATH" in k) and (os.pathsep in env[k]):
print("+ %s=\\" % k)
for elem in env[k].split(os.pathsep):
print("+ %s%s\\" % (shellquote(elem), os.pathsep))
else:
print("+ %s=%s \\" % (k, shellquote(env[k])))
def run_cmd(cmd, env=None, cwd=None, allow_fail=False):
print("---")
try:
cmd_str = " \\\n+ ".join(shellquote(arg) for arg in cmd)
except TypeError:
# eg: one of the elements is None
raise RunCommandError("problem quoting cmd: %r" % cmd)
if env:
assert isinstance(env, Env)
_print_env_diff(env)
# Convert from our Env type to a regular dict.
# This is needed because python3 looks up b'PATH' and 'PATH'
# and emits an error if both are present. In our Env type
# we'll return the same value for both requests, but we don't
# have duplicate potentially conflicting values which is the
# spirit of the check.
env = dict(env.items())
if cwd:
print("+ cd %s && \\" % shellquote(cwd))
# Our long path escape sequence may confuse cmd.exe, so if the cwd
# is short enough, strip that off.
if is_windows() and (len(cwd) < 250) and cwd.startswith("\\\\?\\"):
cwd = cwd[4:]
print("+ %s" % cmd_str)
if allow_fail:
return subprocess.call(cmd, env=env, cwd=cwd)
try:
return subprocess.check_call(cmd, env=env, cwd=cwd)
except (TypeError, ValueError, OSError) as exc:
raise RunCommandError(
"%s while running `%s` with env=%r\nos.environ=%r"
% (str(exc), cmd_str, env, os.environ)
)