1# Copyright 2018 The Chromium Authors 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 # Read and filter out comments. 45 _pydeps_lines = read_file(invoker.pydeps_file, "list lines") 46 _pydeps_entries = filter_exclude(_pydeps_lines, [ "#*" ]) 47 48 # Dependencies are listed relative to the pydeps file directory, but data 49 # parameter expects paths that are relative to the current BUILD.gn 50 _script_dir = get_path_info(invoker.pydeps_file, "dir") 51 _rebased_pydeps_entries = rebase_path(_pydeps_entries, ".", _script_dir) 52 53 # Even though the .pydep file is not used at runtime, it must be added 54 # so that "gn analyze" will mark the target as changed when .py files 55 # are removed but none are added or modified. 56 data = _rebased_pydeps_entries + [ invoker.pydeps_file ] 57 } else { 58 data = [] 59 } 60 if (defined(invoker.data)) { 61 data += invoker.data 62 } 63 } 64} 65 66# A template used for actions that execute a Python script, which has an 67# associated .pydeps file. In other words: 68# 69# - This is very similar to just an action(), except that |script| must point 70# to a Python script (e.g. "//build/.../foo.py") that has a corresponding 71# .pydeps file in the source tree (e.g. "//build/.../foo.pydeps"). 72# 73# - The .pydeps file contains a list of python dependencies (imports really) 74# and is generated _manually_ by using a command like: 75# 76# build/print_python_deps.py --inplace build/android/gyp/foo.py 77# 78# Example 79# action_with_pydeps("create_foo") { 80# script = "myscript.py" 81# args = [...] 82# } 83template("action_with_pydeps") { 84 action(target_name) { 85 # Ensure that testonly and visibility are forwarded 86 # explicitly, since this performs recursive scope lookups, which is 87 # required to ensure their definition from scopes above the caller are 88 # properly handled. All other variables are forwarded with "*", which 89 # doesn't perform recursive lookups at all. See https://crbug.com/862232 90 forward_variables_from(invoker, 91 [ 92 "testonly", 93 "visibility", 94 ]) 95 forward_variables_from(invoker, 96 "*", 97 [ 98 "testonly", 99 "visibility", 100 ]) 101 102 # Read and filter out comments. 103 # Happens every time the template is instantiated, but benchmarking shows no 104 # perceivable impact on overall 'gn gen' speed. 105 _pydeps_file = invoker.script + "deps" 106 107 _pydeps_lines = 108 read_file(_pydeps_file, "list lines") # https://crbug.com/1102058 109 _pydeps_entries = filter_exclude(_pydeps_lines, [ "#*" ]) 110 111 if (!defined(inputs)) { 112 inputs = [] 113 } 114 115 # Dependencies are listed relative to the script directory, but inputs 116 # expects paths that are relative to the current BUILD.gn 117 _script_dir = get_path_info(_pydeps_file, "dir") 118 inputs += rebase_path(_pydeps_entries, ".", _script_dir) 119 } 120} 121 122template("action_foreach_with_pydeps") { 123 action_foreach(target_name) { 124 # Ensure that testonly and visibility are forwarded 125 # explicitly, since this performs recursive scope lookups, which is 126 # required to ensure their definition from scopes above the caller are 127 # properly handled. All other variables are forwarded with "*", which 128 # doesn't perform recursive lookups at all. See https://crbug.com/862232 129 forward_variables_from(invoker, 130 [ 131 "testonly", 132 "visibility", 133 ]) 134 forward_variables_from(invoker, 135 "*", 136 [ 137 "testonly", 138 "visibility", 139 ]) 140 141 # Read and filter out comments. 142 # Happens every time the template is instantiated, but benchmarking shows no 143 # perceivable impact on overall 'gn gen' speed. 144 if (defined(invoker.deps_file)) { 145 _pydeps_file = invoker.deps_file 146 } else { 147 _pydeps_file = invoker.script + "deps" 148 } 149 _pydeps_lines = read_file(_pydeps_file, "list lines") 150 _pydeps_entries = filter_exclude(_pydeps_lines, [ "#*" ]) 151 152 if (!defined(inputs)) { 153 inputs = [] 154 } 155 156 # Dependencies are listed relative to the script directory, but inputs 157 # expects paths that are relative to the current BUILD.gn 158 _script_dir = get_path_info(script, "dir") 159 inputs += rebase_path(_pydeps_entries, ".", _script_dir) 160 } 161} 162