1af9464a695b48e821112415a5b5b606c72fb6bc
[platform/upstream/connectedhomeip.git] / third_party / pigweed / repo / pw_build / python_script.gni
1 # 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
15 import("//build_overrides/pigweed.gni")
16
17 # 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 #
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.
48 #
49 # 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 #
62 # Arguments beyond normal action() target arguments:
63 #
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.
67 #
68 #   capture_output (=true)  If true, script output is hidden unless the script
69 #                           fails with an error. Defaults to true.
70 #
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,
73 #                           no file is touched.
74 #
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.
78 #
79 #   environment             Environment variables to set, passed as a list of
80 #                           NAME=VALUE strings.
81 #
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, " +
85              "but not both")
86
87   _script_args = [
88     # GN root directory relative to the build directory (in which the runner
89     # script is invoked).
90     "--gn-root",
91     rebase_path("//"),
92
93     # Current directory, used to resolve relative paths.
94     "--current-path",
95     rebase_path("."),
96
97     "--default-toolchain=$default_toolchain",
98     "--current-toolchain=$current_toolchain",
99   ]
100
101   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
114   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.
122   if (defined(invoker.script)) {
123     _inputs += [ invoker.script ]
124   }
125
126   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.
133   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
140     _outputs += [ _stamp_file ]
141     _script_args += [
142       "--touch",
143       rebase_path(_stamp_file),
144     ]
145   }
146
147   # Capture output or not.
148   # Note: capture defaults to true.
149   if (defined(invoker.capture_output)) {
150     forward_variables_from(invoker, [ "capture_output" ])
151   } else {
152     capture_output = true
153   }
154   if (capture_output) {
155     _script_args += [ "--capture-output" ]
156   }
157
158   if (defined(invoker.module)) {
159     _script_args += [
160       "--module",
161       invoker.module,
162     ]
163   }
164
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 += [ "--" ]
169
170   if (defined(invoker.script)) {
171     _script_args += [ rebase_path(invoker.script) ]
172   }
173
174   if (defined(invoker.args)) {
175     _script_args += invoker.args
176   }
177
178   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) {
185     _ignore_vars = [
186       "script",
187       "args",
188       "inputs",
189       "outputs",
190     ]
191     forward_variables_from(invoker, "*", _ignore_vars)
192
193     script = "$dir_pw_build/py/pw_build/python_runner.py"
194     args = _script_args
195     inputs = _inputs
196     outputs = _outputs
197   }
198 }
199
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.
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")
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 }