# Copyright 2018 The Chromium Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. # Creates a group() that lists Python sources as |data|. # Having such targets serves two purposes: # 1) Causes files to be included in runtime_deps, so that they are uploaded to # swarming when running tests remotely. # 2) Causes "gn analyze" to know about all Python inputs so that tests will be # re-run when relevant Python files change. # # All non-trivial Python scripts should use a "pydeps" file to track their # sources. To create a .pydep file for a target in //example: # # build/print_python_deps.py \ # --root example \ # --output example/$target_name.pydeps \ # path/to/your/script.py # # Keep the .pydep file up-to-date by adding to //PRESUBMIT.py under one of: # _ANDROID_SPECIFIC_PYDEPS_FILES, _GENERIC_PYDEPS_FILES # # Variables # pydeps_file: Path to .pydeps file to read sources from (optional). # data: Additional files to include in data. E.g. non-.py files needed by the # library, or .py files that are conditionally / lazily imported. # # Example # python_library("my_library_py") { # pydeps_file = "my_library.pydeps" # data = [ "foo.dat" ] # } template("python_library") { group(target_name) { forward_variables_from(invoker, [ "data_deps", "deps", "testonly", "visibility", ]) if (defined(invoker.pydeps_file)) { # Read and filter out comments. _pydeps_lines = read_file(invoker.pydeps_file, "list lines") _pydeps_entries = filter_exclude(_pydeps_lines, [ "#*" ]) # Dependencies are listed relative to the pydeps file directory, but data # parameter expects paths that are relative to the current BUILD.gn _script_dir = get_path_info(invoker.pydeps_file, "dir") _rebased_pydeps_entries = rebase_path(_pydeps_entries, ".", _script_dir) # Even though the .pydep file is not used at runtime, it must be added # so that "gn analyze" will mark the target as changed when .py files # are removed but none are added or modified. data = _rebased_pydeps_entries + [ invoker.pydeps_file ] } else { data = [] } if (defined(invoker.data)) { data += invoker.data } } } # A template used for actions that execute a Python script, which has an # associated .pydeps file. In other words: # # - This is very similar to just an action(), except that |script| must point # to a Python script (e.g. "//build/.../foo.py") that has a corresponding # .pydeps file in the source tree (e.g. "//build/.../foo.pydeps"). # # - The .pydeps file contains a list of python dependencies (imports really) # and is generated _manually_ by using a command like: # # build/print_python_deps.py --inplace build/android/gyp/foo.py # # Example # action_with_pydeps("create_foo") { # script = "myscript.py" # args = [...] # } template("action_with_pydeps") { action(target_name) { # Ensure that testonly and visibility are forwarded # explicitly, since this performs recursive scope lookups, which is # required to ensure their definition from scopes above the caller are # properly handled. All other variables are forwarded with "*", which # doesn't perform recursive lookups at all. See https://crbug.com/862232 forward_variables_from(invoker, [ "testonly", "visibility", ]) forward_variables_from(invoker, "*", [ "testonly", "visibility", ]) # Read and filter out comments. # Happens every time the template is instantiated, but benchmarking shows no # perceivable impact on overall 'gn gen' speed. _pydeps_file = invoker.script + "deps" _pydeps_lines = read_file(_pydeps_file, "list lines") # https://crbug.com/1102058 _pydeps_entries = filter_exclude(_pydeps_lines, [ "#*" ]) if (!defined(inputs)) { inputs = [] } # Dependencies are listed relative to the script directory, but inputs # expects paths that are relative to the current BUILD.gn _script_dir = get_path_info(_pydeps_file, "dir") inputs += rebase_path(_pydeps_entries, ".", _script_dir) } } template("action_foreach_with_pydeps") { action_foreach(target_name) { # Ensure that testonly and visibility are forwarded # explicitly, since this performs recursive scope lookups, which is # required to ensure their definition from scopes above the caller are # properly handled. All other variables are forwarded with "*", which # doesn't perform recursive lookups at all. See https://crbug.com/862232 forward_variables_from(invoker, [ "testonly", "visibility", ]) forward_variables_from(invoker, "*", [ "testonly", "visibility", ]) # Read and filter out comments. # Happens every time the template is instantiated, but benchmarking shows no # perceivable impact on overall 'gn gen' speed. if (defined(invoker.deps_file)) { _pydeps_file = invoker.deps_file } else { _pydeps_file = invoker.script + "deps" } _pydeps_lines = read_file(_pydeps_file, "list lines") _pydeps_entries = filter_exclude(_pydeps_lines, [ "#*" ]) if (!defined(inputs)) { inputs = [] } # Dependencies are listed relative to the script directory, but inputs # expects paths that are relative to the current BUILD.gn _script_dir = get_path_info(script, "dir") inputs += rebase_path(_pydeps_entries, ".", _script_dir) } }