Hide output of pw_exec programs by default
This change updates the pw_exec GN template and wrapper script to hide
the output of the program unless it exits unsuccessfully.
Sample output:
20200103 13:40:06 ERR
20200103 13:40:06 ERR Command failed with exit code 2 in GN build.
20200103 13:40:06 ERR
20200103 13:40:06 ERR Build target:
20200103 13:40:06 ERR
20200103 13:40:06 ERR pw_target_runner_client_pw_go_get
20200103 13:40:06 ERR
20200103 13:40:06 ERR Full command:
20200103 13:40:06 ERR
20200103 13:40:06 ERR go got github.com/golang/protobuf/proto google.golang.org/grpc
20200103 13:40:06 ERR
20200103 13:40:06 ERR Process output:
go got: unknown command
Run 'go help' for usage.
20200103 13:40:06 ERR
Bug: 34
Change-Id: I7e7b2a6d359ecf1eeff6918d56defb909141cbfd
diff --git a/pw_build/exec.gni b/pw_build/exec.gni
index 7c8765d..3e51d57 100644
--- a/pw_build/exec.gni
+++ b/pw_build/exec.gni
@@ -45,6 +45,9 @@
# running the program if the file is empty. Used to avoid running commands
# which error when called without arguments.
#
+# capture_output: If true, output from the program is hidden unless the program
+# exits with an error. Defaults to true.
+#
# Example:
#
# pw_exec("hello_world") {
@@ -61,7 +64,10 @@
template("pw_exec") {
assert(defined(invoker.program), "pw_exec requires a program to run")
- _script_args = []
+ _script_args = [
+ "--target",
+ target_name,
+ ]
if (defined(invoker.env_file)) {
_script_args += [
@@ -90,6 +96,10 @@
}
}
+ if (!defined(invoker.capture_output) || invoker.capture_output) {
+ _script_args += [ "--capture-output" ]
+ }
+
_script_args += [
"--",
invoker.program,
diff --git a/pw_build/py/exec.py b/pw_build/py/exec.py
index 823e275..e05330e 100644
--- a/pw_build/py/exec.py
+++ b/pw_build/py/exec.py
@@ -14,12 +14,18 @@
"""Python wrapper that runs a program. For use in GN."""
import argparse
+import logging
import os
import re
+import shlex
import subprocess
import sys
from typing import Dict, Optional
+import pw_cli.log
+
+_LOG = logging.getLogger(__name__)
+
def argument_parser(
parser: Optional[argparse.ArgumentParser] = None
@@ -35,6 +41,11 @@
help='File containing extra positional arguments to the program',
)
parser.add_argument(
+ '--capture-output',
+ action='store_true',
+ help='Hide output from the program unless it fails',
+ )
+ parser.add_argument(
'-e',
'--env',
action='append',
@@ -52,6 +63,10 @@
help='Don\'t run the program if --args-file is empty',
)
parser.add_argument(
+ '--target',
+ help='GN build target that runs the program',
+ )
+ parser.add_argument(
'command',
nargs=argparse.REMAINDER,
help='Program to run with arguments',
@@ -85,6 +100,7 @@
def main() -> int:
+ """Runs a program specified by command-line arguments."""
args = argument_parser().parse_args()
if not args.command or args.command[0] != '--':
return 1
@@ -111,8 +127,35 @@
for string in args.env:
apply_env_var(string, env)
- return subprocess.call(command, env=env)
+ if args.capture_output:
+ output_args = {'stdout': subprocess.PIPE, 'stderr': subprocess.STDOUT}
+ else:
+ output_args = {}
+
+ process = subprocess.run(command, env=env, **output_args)
+
+ if process.returncode != 0 and args.capture_output:
+ _LOG.error('')
+ _LOG.error('Command failed with exit code %d in GN build.',
+ process.returncode)
+ _LOG.error('')
+ _LOG.error('Build target:')
+ _LOG.error('')
+ _LOG.error(' %s', args.target)
+ _LOG.error('')
+ _LOG.error('Full command:')
+ _LOG.error('')
+ _LOG.error(' %s', shlex.join(command))
+ _LOG.error('')
+ _LOG.error('Process output:')
+ print(flush=True)
+ sys.stdout.buffer.write(process.stdout)
+ print(flush=True)
+ _LOG.error('')
+
+ return process.returncode
if __name__ == '__main__':
+ pw_cli.log.install()
sys.exit(main())