blob: 4e71e87886b0e00a876012ff8bc4b21f2ee55cc5 [file] [log] [blame]
Andrew Grievea7f1ee902018-05-18 16:17:221# Copyright 2018 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5# Creates a group() that lists Python sources as |data|.
6# Having such targets serves two purposes:
7# 1) Causes files to be included in runtime_deps, so that they are uploaded to
8# swarming when running tests remotely.
9# 2) Causes "gn analyze" to know about all Python inputs so that tests will be
10# re-run when relevant Python files change.
11#
12# All non-trivial Python scripts should use a "pydeps" file to track their
13# sources. To create a .pydep file for a target in //example:
14#
15# build/print_python_deps.py \
16# --root example \
17# --output example/$target_name.pydeps \
18# path/to/your/script.py
19#
20# Keep the .pydep file up-to-date by adding to //PRESUBMIT.py under one of:
21# _ANDROID_SPECIFIC_PYDEPS_FILES, _GENERIC_PYDEPS_FILES
22#
23# Variables
24# pydeps_file: Path to .pydeps file to read sources from (optional).
25# data: Additional files to include in data. E.g. non-.py files needed by the
26# library, or .py files that are conditionally / lazily imported.
27#
28# Example
29# python_library("my_library_py") {
30# pydeps_file = "my_library.pydeps"
31# data = [ "foo.dat" ]
32# }
33template("python_library") {
34 group(target_name) {
35 forward_variables_from(invoker,
36 [
37 "data_deps",
38 "deps",
39 "testonly",
40 "visibility",
41 ])
42
43 if (defined(invoker.pydeps_file)) {
44 _py_files = read_file(invoker.pydeps_file, "list lines")
45
46 # Filter out comments.
47 set_sources_assignment_filter([ "#*" ])
48 sources = _py_files
49
50 # Even though the .pydep file is not used at runtime, it must be added
51 # so that "gn analyze" will mark the target as changed when .py files
52 # are removed but none are added or modified.
53 data = sources + [ invoker.pydeps_file ]
54 } else {
55 data = []
56 }
57 if (defined(invoker.data)) {
58 data += invoker.data
59 }
60 }
61}
David 'Digit' Turner0006f4732018-08-07 07:12:3662
63# A template used for actions that execute a Python script, which has an
64# associated .pydeps file. In other words:
65#
66# - This is very similar to just an action(), except that |script| must point
67# to a Python script (e.g. "//build/.../foo.py") that has a corresponding
68# .pydeps file in the source tree (e.g. "//build/.../foo.pydeps").
69#
70# - The .pydeps file contains a list of python dependencies (imports really)
71# and is generated _manually_ by using a command like:
72#
73# build/print_python_deps.py --inplace build/android/gyp/foo.py
74#
75template("action_with_pydeps") {
76 # Read the .pydeps file now. Note that this is done everytime this
77 # template is called, but benchmarking doesn't show any impact on overall
78 # 'gn gen' speed anyway.
79 _pydeps_file = invoker.script + "deps"
80 _pydeps_raw = read_file(_pydeps_file, "list lines")
81
82 # Filter out comments.
83 # This is a bit convoluted to preserve the value of sources if defined.
84 _old_sources = []
85 if (defined(sources)) {
86 _old_sources = sources
87 }
88 set_sources_assignment_filter([ "#*" ])
89 sources = _pydeps_raw
90 _pydeps = sources
91 set_sources_assignment_filter([])
92 sources = _old_sources
93
94 action(target_name) {
95 # Forward all variables. Ensure that testonly and visibility are forwarded
96 # explicitly, since this performs recursive scope lookups, which is
97 # required to ensure their definition from scopes above the caller are
98 # properly handled. All other variables are forwarded with "*", which
99 # doesn't perform recursive lookups at all. See https://crbug.com/862232
100 forward_variables_from(invoker,
101 [
102 "testonly",
103 "visibility",
104 ])
105 forward_variables_from(invoker,
106 "*",
107 [
108 "testonly",
109 "visibility",
110 ])
111
112 if (!defined(inputs)) {
113 inputs = []
114 }
115
116 # Dependencies are listed relative to the script directory, but inputs
117 # expects paths that are relative to the current BUILD.gn
118 _script_dir = get_path_info(script, "dir")
119 inputs += rebase_path(_pydeps, ".", _script_dir)
120 }
121}
122
123template("action_foreach_with_pydeps") {
124 _pydeps_file = invoker.script + "deps"
125 _pydeps_raw = read_file(_pydeps_file, "list lines")
126
127 # Filter out comments.
128 # This is a bit convoluted to preserve the value of sources if defined.
129 _old_sources = []
130 if (defined(sources)) {
131 _old_sources = sources
132 }
133 set_sources_assignment_filter([ "#*" ])
134 sources = _pydeps_raw
135 _pydeps = sources
136 set_sources_assignment_filter([])
137 sources = _old_sources
138
139 action_foreach(target_name) {
140 # Forward all variables. Ensure that testonly and visibility are forwarded
141 # explicitly, since this performs recursive scope lookups, which is
142 # required to ensure their definition from scopes above the caller are
143 # properly handled. All other variables are forwarded with "*", which
144 # doesn't perform recursive lookups at all. See https://crbug.com/862232
145 forward_variables_from(invoker,
146 [
147 "testonly",
148 "visibility",
149 ])
150 forward_variables_from(invoker,
151 "*",
152 [
153 "testonly",
154 "visibility",
155 ])
156
157 if (!defined(inputs)) {
158 inputs = []
159 }
160
161 # Dependencies are listed relative to the script directory, but inputs
162 # expects paths that are relative to the current BUILD.gn
163 _script_dir = get_path_info(script, "dir")
164 inputs += rebase_path(_pydeps, ".", _script_dir)
165 }
166}