pw_build: Rename pw_python_script to pw_python_action

- Rename pw_python_script to pw_python_action, which more clearly
  describes the intent of the template (wrapper around action) and
  avoids confusion with the new pw_python_package template.
- Make pw_python_script as an alias for pw_python_action.
- Update the documentation and comments, and do some minor cleanup.
- Create python_script.gni file for backwards compatibility.

Change-Id: I98ab99021c57b1357c18ec23b50c59272c61c7b9
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/22001
Reviewed-by: Alexei Frolov <frolv@google.com>
diff --git a/pw_bloat/bloat.gni b/pw_bloat/bloat.gni
index b8f1166..7302bae 100644
--- a/pw_bloat/bloat.gni
+++ b/pw_bloat/bloat.gni
@@ -156,7 +156,7 @@
       not_needed("*")
       not_needed(invoker, "*")
 
-      pw_python_script(target_name) {
+      pw_python_action(target_name) {
         metadata = {
           pw_doc_sources = rebase_path([ _doc_rst_output ], root_build_dir)
         }
@@ -171,7 +171,7 @@
     } else {
       # Create an action which runs the size report script on the provided
       # targets.
-      pw_python_script(target_name) {
+      pw_python_action(target_name) {
         metadata = {
           pw_doc_sources = rebase_path([ _doc_rst_output ], root_build_dir)
         }
@@ -319,7 +319,7 @@
     not_needed(invoker, "*")
 
     _doc_rst_output = "$target_gen_dir/$target_name"
-    pw_python_script(target_name) {
+    pw_python_action(target_name) {
       metadata = {
         pw_doc_sources = rebase_path([ _doc_rst_output ], root_build_dir)
       }
diff --git a/pw_build/docs.rst b/pw_build/docs.rst
index dc101d4..64c67f9 100644
--- a/pw_build/docs.rst
+++ b/pw_build/docs.rst
@@ -77,22 +77,22 @@
 
 .. _module-pw_build-python-script:
 
-pw_python_script
+pw_python_action
 ^^^^^^^^^^^^^^^^
-The ``pw_python_script`` template is a convenience wrapper around ``action`` for
+The ``pw_python_action`` template is a convenience wrapper around ``action`` for
 running Python scripts. The main benefit it provides is resolution of GN target
 labels to compiled binary files. This allows Python scripts to be written
 independently of GN, taking only filesystem paths as arguments.
 
 Another convenience provided by the template is to allow running scripts without
 any outputs. Sometimes scripts run in a build do not directly produce output
-files, but GN requires that all actions have an output. ``pw_python_script``
+files, but GN requires that all actions have an output. ``pw_python_action``
 solves this by accepting a boolean ``stamp`` argument which tells it to create a
 dummy output file for the action.
 
 **Arguments**
 
-``pw_python_script`` accepts all of the arguments of a regular ``action``
+``pw_python_action`` accepts all of the arguments of a regular ``action``
 target. Additionally, it has some of its own arguments:
 
 * ``module``: Run the specified Python module instead of a script. Either
@@ -111,7 +111,7 @@
 
 **Expressions**
 
-``pw_python_script`` evaluates expressions in ``args``, the arguments passed to
+``pw_python_action`` evaluates expressions in ``args``, the arguments passed to
 the script. These expressions function similarly to generator expressions in
 CMake. Expressions may be passed as a standalone argument or as part of another
 argument. A single argument may contain multiple expressions.
@@ -199,7 +199,7 @@
 
   import("$dir_pw_build/python_action.gni")
 
-  pw_python_script("postprocess_main_image") {
+  pw_python_action("postprocess_main_image") {
     script = "py/postprocess_binary.py"
     args = [
       "--database",
diff --git a/pw_build/exec.gni b/pw_build/exec.gni
index d4cec4f..d2b4e69 100644
--- a/pw_build/exec.gni
+++ b/pw_build/exec.gni
@@ -108,7 +108,7 @@
     _script_args += invoker.args
   }
 
-  pw_python_script(target_name) {
+  pw_python_action(target_name) {
     script = "$dir_pw_build/py/pw_build/exec.py"
     args = _script_args
 
diff --git a/pw_build/facade.gni b/pw_build/facade.gni
index 3c5b5b4..684a4f7 100644
--- a/pw_build/facade.gni
+++ b/pw_build/facade.gni
@@ -90,7 +90,7 @@
     # error message.
     _main_target_name = target_name
 
-    pw_python_script(_main_target_name + "_NO_BACKEND_SET") {
+    pw_python_action(_main_target_name + "_NO_BACKEND_SET") {
       stamp = true
       script = "$dir_pw_build/py/pw_build/null_backend.py"
       args = [ _main_target_name ]
diff --git a/pw_build/host_tool.gni b/pw_build/host_tool.gni
index 74157fa..cac3f23 100644
--- a/pw_build/host_tool.gni
+++ b/pw_build/host_tool.gni
@@ -46,7 +46,7 @@
       ]
     }
 
-    pw_python_script(target_name) {
+    pw_python_action(target_name) {
       script = "$dir_pw_build/py/pw_build/host_tool.py"
       args = _script_args
       deps = [ invoker.tool ]
diff --git a/pw_build/input_group.gni b/pw_build/input_group.gni
index 4a6d49f..a7b93b4 100644
--- a/pw_build/input_group.gni
+++ b/pw_build/input_group.gni
@@ -23,7 +23,7 @@
 template("pw_input_group") {
   assert(defined(invoker.inputs), "pw_input_group requires some inputs")
 
-  pw_python_script(target_name) {
+  pw_python_action(target_name) {
     ignore_vars = [
       "args",
       "script",
diff --git a/pw_build/py/pw_build/nop.py b/pw_build/py/pw_build/nop.py
index ffe4a59..9fbc5f0 100644
--- a/pw_build/py/pw_build/nop.py
+++ b/pw_build/py/pw_build/nop.py
@@ -16,7 +16,7 @@
 The purpose of this script is to allow for source file dependencies within GN
 to be attached to targets that do not typically support them, such as groups.
 
-For example, instead of creating a group target, a pw_python_script target to
+For example, instead of creating a group target, a pw_python_action target to
 run this script can be created. The script can be given a list of input files,
 causing GN to rebuild the target and everything that depends on it whenever any
 input file is modified.
diff --git a/pw_build/python.gni b/pw_build/python.gni
index 0b4d0cf..b317428 100644
--- a/pw_build/python.gni
+++ b/pw_build/python.gni
@@ -95,7 +95,7 @@
   # TODO(pwbug/239): Add support for installing this package and dependencies
   #     with correct dependency ordering in a virtual environment. The code
   #     below is incomplete and untested.
-  pw_python_script("$target_name.install") {
+  pw_python_action("$target_name.install") {
     module = "pip"
     args = [
       "install",
@@ -113,7 +113,7 @@
 
   # TODO(pwbug/239): Add support for building groups of wheels. The code below
   #     is incomplete and untested.
-  pw_python_script("$target_name.wheel") {
+  pw_python_action("$target_name.wheel") {
     script = "$dir_pw_build/py/pw_build/python_wheels.py"
 
     args = [
@@ -134,7 +134,7 @@
     ]
   }
 
-  pw_python_script_foreach("$target_name.lint.mypy") {
+  pw_python_action_foreach("$target_name.lint.mypy") {
     module = "mypy"
     args = [
       "{{source}}",
@@ -156,7 +156,7 @@
     }
   }
 
-  pw_python_script_foreach("$target_name.lint.pylint") {
+  pw_python_action_foreach("$target_name.lint.pylint") {
     module = "pylint"
     args = [
       "{{source_root_relative_dir}}/{{source_file_part}}",
@@ -183,7 +183,7 @@
     _test_name = string_replace(test, "/", "_")
     _test_target = "$target_name.tests.$_test_name"
 
-    pw_python_script(_test_target) {
+    pw_python_action(_test_target) {
       script = test
       stamp = true
 
diff --git a/pw_build/python_action.gni b/pw_build/python_action.gni
index 1af9464..537cb48 100644
--- a/pw_build/python_action.gni
+++ b/pw_build/python_action.gni
@@ -1,4 +1,4 @@
-# Copyright 2019 The Pigweed Authors
+# Copyright 2020 The Pigweed Authors
 #
 # Licensed under the Apache License, Version 2.0 (the "License"); you may not
 # use this file except in compliance with the License. You may obtain a copy of
@@ -14,76 +14,43 @@
 
 import("//build_overrides/pigweed.gni")
 
-# Defines an action to run a Python script.
+# Defines an action that runs a Python script.
 #
-# This wraps a regular Python script action with an invocation of a script-
-# runner script which resolves GN paths to filesystem paths and locates output
-# files for binary targets.
+# This wraps a regular Python script GN action with an invocation of a script-
+# runner script that adds useful features. pw_python_action() uses the same
+# actions as GN's action(), with the following additions or changes:
 #
-# The interface to this template is identical to that of a regular "action"
-# which runs a Python script, except for two key differences:
+#   module          May be used in place of the script argument to run the
+#                   provided Python module with `python -m` instead of a script.
+#                   Either script or module must be provided.
 #
-#   1. Regular GN actions typically require a call to rebase_path to resolve
-#      GN paths to filesystem paths. This template requires that all paths
-#      remain GN paths, but are made absolute.
+#   capture_output  If true, script output is hidden unless the script fails
+#                   with an error. Defaults to true.
 #
-#      This means that an "action" argument of the form:
+#   stamp           File to touch if the script is successful. Actions that
+#                   don't create output files can use this stamp file instead of
+#                   creating their own dummy file. If true, a generic file is
+#                   used. If false or not set, no file is touched.
 #
-#        rebase_path("my/relative/path:optional_target", root_build_dir)
+#   directory       The directory from which to execute the Python script. Paths
+#                   in args may need to be adjusted to be relative to this
+#                   directory.
 #
-#      Becomes:
+#   environment     Environment variables to set, passed as a list of NAME=VALUE
+#                   strings.
 #
-#        get_path_info("my/relative/path:optional_target", "abspath")
+#   args            Same as the standard action args, except special expressions
+#                   may be used to extract information not normally accessible
+#                   in GN. These include the following:
 #
-#   2. The behavior of the runner script depends on whether a provided path is a
-#      regular build path or an output path (starting with "$root_out_dir").
-#      If an output path is provided and the path has a target, the script
-#      assumes that the target refers to a file built by Ninja and tries to
-#      locate it within the output directory.
+#                     <TARGET_FILE(//some/label:here)> - expands to the
+#                         output file (such as a .a or .elf) from a GN target
+#                     <TARGET_FILE_IF_EXISTS(//some/label:here)> - expands to
+#                         the output file if the target exists, or nothing
+#                     <TARGET_OBJECTS(//some/label:here)> - expands to the
+#                         object files produced by the provided GN target
 #
-# Additionally, this template can accept a boolean "stamp" argument. If set to
-# true, the script runner will touch a file to indicate the success of the run.
-# This is provided so that individual Python scripts are not required to define
-# an output file if they do not have one.
-#
-# Path resolution examples (assuming the build directory is //out):
-#
-#           BEFORE                     AFTER
-#
-#   //my_module              ../my_module
-#   //my_module:foo          ../my_module:foo
-#   //my_module/file.txt     ../my_module/file.txt
-#   $root_out_dir/my_module  ../out/obj/my_module
-#   $target_out_dir          ../out/obj/my_module      (in //my_module/BUILD.gn)
-#   $target_out_dir/out.json ../out/obj/my_module/out.json
-#   $target_out_dir:foo      ../out/obj/my_module/foo.elf  (toolchain-dependent)
-#   $target_out_dir:foo      ../out/obj/my_module/foo.exe  (toolchain-dependent)
-#
-# Arguments beyond normal action() target arguments:
-#
-#   module                  Used in place of the script argument to run the
-#                           provided Python module with `python -m` instead of a
-#                           script. Either script or module must be provided.
-#
-#   capture_output (=true)  If true, script output is hidden unless the script
-#                           fails with an error. Defaults to true.
-#
-#   stamp                   File to touch if the script is successful. If set to
-#                           true, a generic file is used. If false or not set,
-#                           no file is touched.
-#
-#   directory               The directory from which to execute the Python
-#                           script. Paths in args may need to be adjusted to be
-#                           relative to this directory.
-#
-#   environment             Environment variables to set, passed as a list of
-#                           NAME=VALUE strings.
-#
-template("pw_python_script") {
-  assert(defined(invoker.script) != defined(invoker.module),
-         "pw_python_script must have either a script or module to run, " +
-             "but not both")
-
+template("pw_python_action") {
   _script_args = [
     # GN root directory relative to the build directory (in which the runner
     # script is invoked).
@@ -144,14 +111,8 @@
     ]
   }
 
-  # Capture output or not.
-  # Note: capture defaults to true.
-  if (defined(invoker.capture_output)) {
-    forward_variables_from(invoker, [ "capture_output" ])
-  } else {
-    capture_output = true
-  }
-  if (capture_output) {
+  # Capture output or not (defaults to true).
+  if (!defined(invoker.capture_output) || invoker.capture_output) {
     _script_args += [ "--capture-output" ]
   }
 
@@ -175,8 +136,8 @@
     _script_args += invoker.args
   }
 
-  if (defined(invoker._pw_action_foreach) && invoker._pw_action_foreach) {
-    _action_type = "action_foreach"
+  if (defined(invoker._pw_action_type)) {
+    _action_type = invoker._pw_action_type
   } else {
     _action_type = "action"
   }
@@ -197,18 +158,18 @@
   }
 }
 
-# Runs pw_python_script once per file over a set of sources.
+# Runs pw_python_action once per file over a set of sources.
 #
-# This template brings pw_python_script's features to action_foreach. Usage is
-# the same as pw_python_script, except that sources must be provided and source
+# This template brings pw_python_action's features to action_foreach. Usage is
+# the same as pw_python_action, except that sources must be provided and source
 # expansion (e.g. "{{source}}") may be used in args and outputs.
 #
-# See the pw_python_script and action_foreach documentation for full details.
-template("pw_python_script_foreach") {
+# See the pw_python_action and action_foreach documentation for full details.
+template("pw_python_action_foreach") {
   assert(defined(invoker.sources) && invoker.sources != [],
-         "pw_python_script_foreach requires a list of one or more sources")
+         "pw_python_action_foreach requires a list of one or more sources")
 
-  pw_python_script(target_name) {
+  pw_python_action(target_name) {
     if (defined(invoker.stamp) && invoker.stamp != false) {
       if (invoker.stamp == true) {
         # Use source file names in the generated stamp file path so they are
@@ -223,6 +184,20 @@
 
     forward_variables_from(invoker, "*", [ "stamp" ])
 
-    _pw_action_foreach = true
+    _pw_action_type = "action_foreach"
+  }
+}
+
+# Compatibilty aliases for pw_python_action and pw_python_action_foreach. Will
+# be removed once all projects have migrated to the new name.
+template("pw_python_script") {
+  pw_python_action(target_name) {
+    forward_variables_from(invoker, "*")
+  }
+}
+
+template("pw_python_script_foreach") {
+  pw_python_action_foreach(target_name) {
+    forward_variables_from(invoker, "*")
   }
 }
diff --git a/pw_build/python_script.gni b/pw_build/python_script.gni
new file mode 100644
index 0000000..8bd59fc
--- /dev/null
+++ b/pw_build/python_script.gni
@@ -0,0 +1,15 @@
+# Copyright 2020 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+#     https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+import("python_action.gni")
diff --git a/pw_build/python_wheels.gni b/pw_build/python_wheels.gni
index 9507a64..22210da 100644
--- a/pw_build/python_wheels.gni
+++ b/pw_build/python_wheels.gni
@@ -18,7 +18,7 @@
 
 # Builds a .whl from a Python package.
 template("pw_python_wheels") {
-  pw_python_script(target_name) {
+  pw_python_action(target_name) {
     forward_variables_from(invoker, [ "deps" ])
 
     script = "$dir_pw_build/py/pw_build/python_wheels.py"
diff --git a/pw_build/zip.gni b/pw_build/zip.gni
index 0934fd6..653b0cd 100644
--- a/pw_build/zip.gni
+++ b/pw_build/zip.gni
@@ -89,7 +89,7 @@
 #
 template("pw_zip") {
   _delimiter = ">"
-  pw_python_script(target_name) {
+  pw_python_action(target_name) {
     forward_variables_from(invoker, [ "deps" ])
     script = "$dir_pw_build/py/pw_build/zip.py"
 
diff --git a/pw_docgen/docs.gni b/pw_docgen/docs.gni
index cb1c7e3..2dda55c 100644
--- a/pw_docgen/docs.gni
+++ b/pw_docgen/docs.gni
@@ -107,7 +107,7 @@
   _script_args += rebase_path(invoker.sources)
 
   if (pw_docgen_BUILD_DOCS) {
-    pw_python_script(target_name) {
+    pw_python_action(target_name) {
       script = "$dir_pw_docgen/py/docgen.py"
       args = _script_args
       deps = [ ":$_metadata_file_target" ]
diff --git a/pw_protobuf_compiler/proto.gni b/pw_protobuf_compiler/proto.gni
index c14d51b..01521d1 100644
--- a/pw_protobuf_compiler/proto.gni
+++ b/pw_protobuf_compiler/proto.gni
@@ -58,7 +58,7 @@
   }
 
   _gen_target = "${target_name}_gen"
-  pw_python_script(_gen_target) {
+  pw_python_action(_gen_target) {
     forward_variables_from(invoker, _forwarded_vars)
     script = _gen_script_path
     args = [
@@ -121,7 +121,7 @@
   # Create a target which runs protoc configured with the nanopb_rpc plugin to
   # generate the C++ proto RPC headers.
   _gen_target = "${target_name}_gen"
-  pw_python_script(_gen_target) {
+  pw_python_action(_gen_target) {
     forward_variables_from(invoker, _forwarded_vars)
     script = _gen_script_path
     args = [
@@ -202,7 +202,7 @@
   # Create a target which runs protoc configured with the nanopb plugin to
   # generate the C proto sources.
   _gen_target = "${target_name}_gen"
-  pw_python_script(_gen_target) {
+  pw_python_action(_gen_target) {
     forward_variables_from(invoker, _forwarded_vars)
     script = _gen_script_path
     args = [
@@ -264,7 +264,7 @@
   _proto_gen_dir = "$_proto_gopath/src"
   _rebased_gopath = rebase_path(_proto_gopath)
 
-  pw_python_script(target_name) {
+  pw_python_action(target_name) {
     forward_variables_from(invoker, _forwarded_vars)
     metadata = {
       gopath = [ "GOPATH+=$_rebased_gopath" ]
@@ -439,7 +439,7 @@
 
   # Create stub versions of the proto library for other protobuf generators.
   foreach(_gen, _protobuf_generators - _generators) {
-    pw_python_script("${target_name}_${_gen}") {
+    pw_python_action("${target_name}_${_gen}") {
       forward_variables_from(invoker, _forwarded_vars)
       script = string_join("/",
                            [
@@ -461,7 +461,7 @@
 
   # If the user attempts to use the target directly instead of one of the
   # generator targets, run a script which prints a nice error message.
-  pw_python_script(target_name) {
+  pw_python_action(target_name) {
     script = string_join("/",
                          [
                            dir_pw_protobuf_compiler,
diff --git a/pw_tokenizer/database.gni b/pw_tokenizer/database.gni
index 6cdbe30..6e03597 100644
--- a/pw_tokenizer/database.gni
+++ b/pw_tokenizer/database.gni
@@ -88,7 +88,7 @@
     _domain = ""
   }
 
-  pw_python_script(target_name) {
+  pw_python_action(target_name) {
     script = "$dir_pw_tokenizer/py/pw_tokenizer/database.py"
 
     inputs = _input_databases
diff --git a/pw_unit_test/test.gni b/pw_unit_test/test.gni
index e7854538..f7408d3 100644
--- a/pw_unit_test/test.gni
+++ b/pw_unit_test/test.gni
@@ -50,7 +50,7 @@
     # If the target is disabled, create an empty target in its place. Use an
     # action with the original target's sources as inputs to ensure that
     # the source files exist (even if they don't compile).
-    pw_python_script(target_name) {
+    pw_python_action(target_name) {
       script = "$dir_pw_build/py/pw_build/nop.py"
       stamp = true
 
@@ -161,7 +161,7 @@
       }
     }
 
-    pw_python_script(_test_to_run + "_run") {
+    pw_python_action(_test_to_run + "_run") {
       deps = [ ":$_test_target_name" ]
       inputs = [ pw_unit_test_AUTOMATIC_RUNNER ]
       script = "$dir_pw_unit_test/py/pw_unit_test/test_runner.py"