blob: 1af9464a695b48e821112415a5b5b606c72fb6bc [file] [log] [blame]
Alexei Frolovd1f98fa2019-11-08 15:41:37 -08001# Copyright 2019 The Pigweed Authors
2#
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
5# the License at
6#
7# https://www.apache.org/licenses/LICENSE-2.0
8#
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
13# the License.
14
Armando Montanezfb3d3fb2020-06-09 18:12:12 -070015import("//build_overrides/pigweed.gni")
16
Alexei Frolovd1f98fa2019-11-08 15:41:37 -080017# Defines an action to run a Python script.
18#
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.
22#
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:
25#
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.
29#
30# This means that an "action" argument of the form:
31#
32# rebase_path("my/relative/path:optional_target", root_build_dir)
33#
34# Becomes:
35#
36# get_path_info("my/relative/path:optional_target", "abspath")
37#
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.
43#
Alexei Frolov917756d2019-11-12 14:34:34 -080044# 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.
48#
Alexei Frolovd1f98fa2019-11-08 15:41:37 -080049# Path resolution examples (assuming the build directory is //out):
50#
51# BEFORE AFTER
52#
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)
61#
Keir Mierlefa2fbe62019-12-23 11:13:02 -080062# Arguments beyond normal action() target arguments:
63#
Wyatt Hepler79d983f2020-10-12 08:46:34 -070064# 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.
67#
Keir Mierlefa2fbe62019-12-23 11:13:02 -080068# capture_output (=true) If true, script output is hidden unless the script
69# fails with an error. Defaults to true.
70#
Wyatt Heplera74f7b02020-07-23 14:10:56 -070071# stamp File to touch if the script is successful. If set to
72# true, a generic file is used. If false or not set,
73# no file is touched.
Keir Mierlefa2fbe62019-12-23 11:13:02 -080074#
Wyatt Hepler79d983f2020-10-12 08:46:34 -070075# 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.
78#
79# environment Environment variables to set, passed as a list of
80# NAME=VALUE strings.
81#
Alexei Frolovd1f98fa2019-11-08 15:41:37 -080082template("pw_python_script") {
Wyatt Hepler79d983f2020-10-12 08:46:34 -070083 assert(defined(invoker.script) != defined(invoker.module),
84 "pw_python_script must have either a script or module to run, " +
85 "but not both")
Alexei Frolovbaaa2d62019-11-12 16:20:51 -080086
Alexei Frolovd1f98fa2019-11-08 15:41:37 -080087 _script_args = [
88 # GN root directory relative to the build directory (in which the runner
89 # script is invoked).
90 "--gn-root",
Wyatt Hepler8224a642020-07-29 08:55:56 -070091 rebase_path("//"),
Alexei Frolovd1f98fa2019-11-08 15:41:37 -080092
Wyatt Hepler8224a642020-07-29 08:55:56 -070093 # Current directory, used to resolve relative paths.
94 "--current-path",
95 rebase_path("."),
96
97 "--default-toolchain=$default_toolchain",
98 "--current-toolchain=$current_toolchain",
Alexei Frolovd1f98fa2019-11-08 15:41:37 -080099 ]
100
Wyatt Hepler79d983f2020-10-12 08:46:34 -0700101 if (defined(invoker.directory)) {
102 _script_args += [
103 "--directory",
104 rebase_path(invoker.directory),
105 ]
106 }
107
108 if (defined(invoker.environment)) {
109 foreach(variable, invoker.environment) {
110 _script_args += [ "--env=$variable" ]
111 }
112 }
113
Alexei Frolovbaaa2d62019-11-12 16:20:51 -0800114 if (defined(invoker.inputs)) {
115 _inputs = invoker.inputs
116 } else {
117 _inputs = []
118 }
119
120 # List the script to run as an input so that the action is re-run when it is
121 # modified.
Wyatt Hepler79d983f2020-10-12 08:46:34 -0700122 if (defined(invoker.script)) {
123 _inputs += [ invoker.script ]
124 }
Alexei Frolovbaaa2d62019-11-12 16:20:51 -0800125
Alexei Frolov917756d2019-11-12 14:34:34 -0800126 if (defined(invoker.outputs)) {
127 _outputs = invoker.outputs
128 } else {
129 _outputs = []
130 }
131
132 # If a stamp file is requested, add it as an output of the runner script.
Wyatt Heplera74f7b02020-07-23 14:10:56 -0700133 if (defined(invoker.stamp) && invoker.stamp != false) {
134 if (invoker.stamp == true) {
135 _stamp_file = "$target_gen_dir/$target_name.pw_pystamp"
136 } else {
137 _stamp_file = invoker.stamp
138 }
139
Alexei Frolov917756d2019-11-12 14:34:34 -0800140 _outputs += [ _stamp_file ]
141 _script_args += [
142 "--touch",
Wyatt Hepler8224a642020-07-29 08:55:56 -0700143 rebase_path(_stamp_file),
Alexei Frolov917756d2019-11-12 14:34:34 -0800144 ]
145 }
146
Keir Mierlefa2fbe62019-12-23 11:13:02 -0800147 # Capture output or not.
148 # Note: capture defaults to true.
Wyatt Hepler8224a642020-07-29 08:55:56 -0700149 if (defined(invoker.capture_output)) {
Keir Mierlefa2fbe62019-12-23 11:13:02 -0800150 forward_variables_from(invoker, [ "capture_output" ])
Wyatt Hepler8224a642020-07-29 08:55:56 -0700151 } else {
152 capture_output = true
Keir Mierlefa2fbe62019-12-23 11:13:02 -0800153 }
154 if (capture_output) {
155 _script_args += [ "--capture-output" ]
156 }
157
Wyatt Hepler79d983f2020-10-12 08:46:34 -0700158 if (defined(invoker.module)) {
159 _script_args += [
160 "--module",
161 invoker.module,
162 ]
163 }
164
Alexei Frolov917756d2019-11-12 14:34:34 -0800165 # "--" 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 += [ "--" ]
169
Wyatt Hepler79d983f2020-10-12 08:46:34 -0700170 if (defined(invoker.script)) {
171 _script_args += [ rebase_path(invoker.script) ]
172 }
Wyatt Hepler8224a642020-07-29 08:55:56 -0700173
Alexei Frolovd1f98fa2019-11-08 15:41:37 -0800174 if (defined(invoker.args)) {
175 _script_args += invoker.args
176 }
177
Wyatt Hepler79d983f2020-10-12 08:46:34 -0700178 if (defined(invoker._pw_action_foreach) && invoker._pw_action_foreach) {
179 _action_type = "action_foreach"
180 } else {
181 _action_type = "action"
182 }
183
184 target(_action_type, target_name) {
Alexei Frolovd1f98fa2019-11-08 15:41:37 -0800185 _ignore_vars = [
186 "script",
187 "args",
Alexei Frolovbaaa2d62019-11-12 16:20:51 -0800188 "inputs",
Alexei Frolov917756d2019-11-12 14:34:34 -0800189 "outputs",
Alexei Frolovd1f98fa2019-11-08 15:41:37 -0800190 ]
191 forward_variables_from(invoker, "*", _ignore_vars)
192
Wyatt Hepler27f69f02020-07-27 09:06:21 -0700193 script = "$dir_pw_build/py/pw_build/python_runner.py"
Alexei Frolovd1f98fa2019-11-08 15:41:37 -0800194 args = _script_args
Alexei Frolovbaaa2d62019-11-12 16:20:51 -0800195 inputs = _inputs
Alexei Frolov917756d2019-11-12 14:34:34 -0800196 outputs = _outputs
Alexei Frolovd1f98fa2019-11-08 15:41:37 -0800197 }
198}
Wyatt Hepler79d983f2020-10-12 08:46:34 -0700199
200# Runs pw_python_script once per file over a set of sources.
201#
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.
205#
206# See the pw_python_script and action_foreach documentation for full details.
207template("pw_python_script_foreach") {
208 assert(defined(invoker.sources) && invoker.sources != [],
209 "pw_python_script_foreach requires a list of one or more sources")
210
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"
217 } else {
218 stamp = invoker.stamp
219 }
220 } else {
221 stamp = false
222 }
223
224 forward_variables_from(invoker, "*", [ "stamp" ])
225
226 _pw_action_foreach = true
227 }
228}