1 # Copyright 2019 The Pigweed Authors
3 # Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 # use this file except in compliance with the License. You may obtain a copy of
7 # https://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 # License for the specific language governing permissions and limitations under
15 import("//build_overrides/pigweed.gni")
17 # Defines an action to run a Python script.
19 # This wraps a regular Python script action with an invocation of a script-
20 # runner script which resolves GN paths to filesystem paths and locates output
21 # files for binary targets.
23 # The interface to this template is identical to that of a regular "action"
24 # which runs a Python script, except for two key differences:
26 # 1. Regular GN actions typically require a call to rebase_path to resolve
27 # GN paths to filesystem paths. This template requires that all paths
28 # remain GN paths, but are made absolute.
30 # This means that an "action" argument of the form:
32 # rebase_path("my/relative/path:optional_target", root_build_dir)
36 # get_path_info("my/relative/path:optional_target", "abspath")
38 # 2. The behavior of the runner script depends on whether a provided path is a
39 # regular build path or an output path (starting with "$root_out_dir").
40 # If an output path is provided and the path has a target, the script
41 # assumes that the target refers to a file built by Ninja and tries to
42 # locate it within the output directory.
44 # Additionally, this template can accept a boolean "stamp" argument. If set to
45 # true, the script runner will touch a file to indicate the success of the run.
46 # This is provided so that individual Python scripts are not required to define
47 # an output file if they do not have one.
49 # Path resolution examples (assuming the build directory is //out):
53 # //my_module ../my_module
54 # //my_module:foo ../my_module:foo
55 # //my_module/file.txt ../my_module/file.txt
56 # $root_out_dir/my_module ../out/obj/my_module
57 # $target_out_dir ../out/obj/my_module (in //my_module/BUILD.gn)
58 # $target_out_dir/out.json ../out/obj/my_module/out.json
59 # $target_out_dir:foo ../out/obj/my_module/foo.elf (toolchain-dependent)
60 # $target_out_dir:foo ../out/obj/my_module/foo.exe (toolchain-dependent)
62 # Arguments beyond normal action() target arguments:
64 # module Used in place of the script argument to run the
65 # provided Python module with `python -m` instead of a
66 # script. Either script or module must be provided.
68 # capture_output (=true) If true, script output is hidden unless the script
69 # fails with an error. Defaults to true.
71 # stamp File to touch if the script is successful. If set to
72 # true, a generic file is used. If false or not set,
75 # directory The directory from which to execute the Python
76 # script. Paths in args may need to be adjusted to be
77 # relative to this directory.
79 # environment Environment variables to set, passed as a list of
82 template("pw_python_script") {
83 assert(defined(invoker.script) != defined(invoker.module),
84 "pw_python_script must have either a script or module to run, " +
88 # GN root directory relative to the build directory (in which the runner
93 # Current directory, used to resolve relative paths.
97 "--default-toolchain=$default_toolchain",
98 "--current-toolchain=$current_toolchain",
101 if (defined(invoker.directory)) {
104 rebase_path(invoker.directory),
108 if (defined(invoker.environment)) {
109 foreach(variable, invoker.environment) {
110 _script_args += [ "--env=$variable" ]
114 if (defined(invoker.inputs)) {
115 _inputs = invoker.inputs
120 # List the script to run as an input so that the action is re-run when it is
122 if (defined(invoker.script)) {
123 _inputs += [ invoker.script ]
126 if (defined(invoker.outputs)) {
127 _outputs = invoker.outputs
132 # If a stamp file is requested, add it as an output of the runner script.
133 if (defined(invoker.stamp) && invoker.stamp != false) {
134 if (invoker.stamp == true) {
135 _stamp_file = "$target_gen_dir/$target_name.pw_pystamp"
137 _stamp_file = invoker.stamp
140 _outputs += [ _stamp_file ]
143 rebase_path(_stamp_file),
147 # Capture output or not.
148 # Note: capture defaults to true.
149 if (defined(invoker.capture_output)) {
150 forward_variables_from(invoker, [ "capture_output" ])
152 capture_output = true
154 if (capture_output) {
155 _script_args += [ "--capture-output" ]
158 if (defined(invoker.module)) {
165 # "--" indicates the end of arguments to the runner script.
166 # Everything beyond this point is interpreted as the command and arguments
167 # of the Python script to run.
168 _script_args += [ "--" ]
170 if (defined(invoker.script)) {
171 _script_args += [ rebase_path(invoker.script) ]
174 if (defined(invoker.args)) {
175 _script_args += invoker.args
178 if (defined(invoker._pw_action_foreach) && invoker._pw_action_foreach) {
179 _action_type = "action_foreach"
181 _action_type = "action"
184 target(_action_type, target_name) {
191 forward_variables_from(invoker, "*", _ignore_vars)
193 script = "$dir_pw_build/py/pw_build/python_runner.py"
200 # Runs pw_python_script once per file over a set of sources.
202 # This template brings pw_python_script's features to action_foreach. Usage is
203 # the same as pw_python_script, except that sources must be provided and source
204 # expansion (e.g. "{{source}}") may be used in args and outputs.
206 # See the pw_python_script and action_foreach documentation for full details.
207 template("pw_python_script_foreach") {
208 assert(defined(invoker.sources) && invoker.sources != [],
209 "pw_python_script_foreach requires a list of one or more sources")
211 pw_python_script(target_name) {
212 if (defined(invoker.stamp) && invoker.stamp != false) {
213 if (invoker.stamp == true) {
214 # Use source file names in the generated stamp file path so they are
215 # unique for each source.
216 stamp = "$target_gen_dir/{{source_file_part}}.pw_pystamp"
218 stamp = invoker.stamp
224 forward_variables_from(invoker, "*", [ "stamp" ])
226 _pw_action_foreach = true