pw_build: Add working_directory arg to pw_exec
When executing external programs, allow for setting a working directory
the program will be started with.
Also document pw_exec.
Change-Id: I977060b51823c62f254f56dc3af36ea3ca9c88b9
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/78480
Reviewed-by: Alexei Frolov <frolv@google.com>
Commit-Queue: Austin Foxley <afoxley@google.com>
diff --git a/pw_build/docs.rst b/pw_build/docs.rst
index 72fdbd9..44d1fa5 100644
--- a/pw_build/docs.rst
+++ b/pw_build/docs.rst
@@ -293,6 +293,56 @@
stamp = true
}
+pw_exec
+-------
+``pw_exec`` allows for execution of arbitrary programs. It is a wrapper around
+``pw_python_action`` but allows for specifying the program to execute.
+
+.. note:: Prefer to use ``pw_python_action`` instead of calling out to shell
+ scripts, as the python will be more portable. ``pw_exec`` should generally
+ only be used for interacting with legacy/existing scripts.
+
+**Arguments**
+
+* ``program``: The program to run. Can be a full path or just a name (in which
+ case $PATH is searched).
+* ``args``: Optional list of arguments to the program.
+* ``deps``: Dependencies for this target.
+* ``inputs``: Optional list of build inputs to the program.
+* ``outputs``: Optional list of artifacts produced by the program's execution.
+* ``env``: Optional list of key-value pairs defining environment variables for
+ the program.
+* ``env_file``: Optional path to a file containing a list of newline-separated
+ key-value pairs defining environment variables for the program.
+* ``args_file``: Optional path to a file containing additional positional
+ arguments to the program. Each line of the file is appended to the
+ invocation. Useful for specifying arguments from GN metadata.
+* ``skip_empty_args``: If args_file is provided, boolean indicating whether to
+ skip 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.
+* ``working_directory``: The working directory to execute the subprocess with.
+ If not specified it will not be set and the subprocess will have whatever
+ the parent current working directory is.
+
+**Example**
+
+.. code-block::
+
+ import("$dir_pw_build/exec.gni")
+
+ pw_exec("hello_world") {
+ program = "/bin/sh"
+ args = [
+ "-c",
+ "echo hello \$WORLD",
+ ]
+ env = [
+ "WORLD=world",
+ ]
+ }
+
pw_input_group
--------------
``pw_input_group`` defines a group of input files which are not directly
diff --git a/pw_build/exec.gni b/pw_build/exec.gni
index 269709a..26f3d62 100644
--- a/pw_build/exec.gni
+++ b/pw_build/exec.gni
@@ -48,6 +48,10 @@
# capture_output: If true, output from the program is hidden unless the program
# exits with an error. Defaults to true.
#
+# working_directory: The working directory to execute the subprocess with. If
+# not specified it will not be set and the subprocess will have whatever the
+# parent current working directory is.
+#
# Example:
#
# pw_exec("hello_world") {
@@ -103,6 +107,13 @@
_capture_output = false
}
+ if (defined(invoker.working_directory)) {
+ _script_args += [
+ "--working-directory",
+ invoker.working_directory,
+ ]
+ }
+
_script_args += [
"--",
invoker.program,
diff --git a/pw_build/py/pw_build/exec.py b/pw_build/py/pw_build/exec.py
index b956c13..aac800b 100644
--- a/pw_build/py/pw_build/exec.py
+++ b/pw_build/py/pw_build/exec.py
@@ -20,6 +20,7 @@
import shlex
import subprocess
import sys
+import pathlib
from typing import Dict, Optional
# Need to be able to run without pw_cli installed in the virtualenv.
@@ -70,6 +71,9 @@
'--target',
help='GN build target that runs the program',
)
+ parser.add_argument('--working-directory',
+ type=pathlib.Path,
+ help='Directory to execute program in')
parser.add_argument(
'command',
nargs=argparse.REMAINDER,
@@ -113,6 +117,7 @@
# Command starts after the "--".
command = args.command[1:]
+ extra_kw_args = {}
if args.args_file is not None:
empty = True
@@ -132,11 +137,13 @@
apply_env_var(string, env)
if args.capture_output:
- output_args = {'stdout': subprocess.PIPE, 'stderr': subprocess.STDOUT}
- else:
- output_args = {}
+ extra_kw_args['stdout'] = subprocess.PIPE
+ extra_kw_args['stderr'] = subprocess.STDOUT
- process = subprocess.run(command, env=env, **output_args) # type: ignore
+ if args.working_directory:
+ extra_kw_args['cwd'] = args.working_directory
+
+ process = subprocess.run(command, env=env, **extra_kw_args) # type: ignore
if process.returncode != 0 and args.capture_output:
_LOG.error('')