1*61c4878aSAndroid Build Coastguard Worker# Copyright 2021 The Pigweed Authors 2*61c4878aSAndroid Build Coastguard Worker# 3*61c4878aSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); you may not 4*61c4878aSAndroid Build Coastguard Worker# use this file except in compliance with the License. You may obtain a copy of 5*61c4878aSAndroid Build Coastguard Worker# the License at 6*61c4878aSAndroid Build Coastguard Worker# 7*61c4878aSAndroid Build Coastguard Worker# https://www.apache.org/licenses/LICENSE-2.0 8*61c4878aSAndroid Build Coastguard Worker# 9*61c4878aSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 10*61c4878aSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11*61c4878aSAndroid Build Coastguard Worker# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12*61c4878aSAndroid Build Coastguard Worker# License for the specific language governing permissions and limitations under 13*61c4878aSAndroid Build Coastguard Worker# the License. 14*61c4878aSAndroid Build Coastguard Worker 15*61c4878aSAndroid Build Coastguard Workerimport("//build_overrides/pigweed.gni") 16*61c4878aSAndroid Build Coastguard Worker 17*61c4878aSAndroid Build Coastguard Workerimport("$dir_pw_build/input_group.gni") 18*61c4878aSAndroid Build Coastguard Workerimport("$dir_pw_build/mirror_tree.gni") 19*61c4878aSAndroid Build Coastguard Workerimport("$dir_pw_build/python_action.gni") 20*61c4878aSAndroid Build Coastguard Workerimport("$dir_pw_build/python_gn_args.gni") 21*61c4878aSAndroid Build Coastguard Workerimport("$dir_pw_protobuf_compiler/toolchain.gni") 22*61c4878aSAndroid Build Coastguard Worker 23*61c4878aSAndroid Build Coastguard Workerdeclare_args() { 24*61c4878aSAndroid Build Coastguard Worker # Constraints file selection (arguments to pip install --constraint). 25*61c4878aSAndroid Build Coastguard Worker # See pip help install. 26*61c4878aSAndroid Build Coastguard Worker pw_build_PIP_CONSTRAINTS = 27*61c4878aSAndroid Build Coastguard Worker [ "$dir_pw_env_setup/py/pw_env_setup/virtualenv_setup/constraint.list" ] 28*61c4878aSAndroid Build Coastguard Worker 29*61c4878aSAndroid Build Coastguard Worker # Default pip requirements file for all Pigweed based projects. 30*61c4878aSAndroid Build Coastguard Worker pw_build_PIP_REQUIREMENTS = [] 31*61c4878aSAndroid Build Coastguard Worker 32*61c4878aSAndroid Build Coastguard Worker # DOCSTAG: [python-static-analysis-tools] 33*61c4878aSAndroid Build Coastguard Worker # Default set of Python static alaysis tools to run for pw_python_package targets. 34*61c4878aSAndroid Build Coastguard Worker pw_build_PYTHON_STATIC_ANALYSIS_TOOLS = [ 35*61c4878aSAndroid Build Coastguard Worker "pylint", 36*61c4878aSAndroid Build Coastguard Worker "mypy", 37*61c4878aSAndroid Build Coastguard Worker ] 38*61c4878aSAndroid Build Coastguard Worker 39*61c4878aSAndroid Build Coastguard Worker # DOCSTAG: [python-static-analysis-tools] 40*61c4878aSAndroid Build Coastguard Worker 41*61c4878aSAndroid Build Coastguard Worker # If true, GN will run each Python test using the coverage command. A separate 42*61c4878aSAndroid Build Coastguard Worker # coverage data file for each test will be saved. To generate reports from 43*61c4878aSAndroid Build Coastguard Worker # this information run: pw presubmit --step gn_python_test_coverage 44*61c4878aSAndroid Build Coastguard Worker pw_build_PYTHON_TEST_COVERAGE = false 45*61c4878aSAndroid Build Coastguard Worker 46*61c4878aSAndroid Build Coastguard Worker # Output format for pylint. Options include "text" and "colorized". 47*61c4878aSAndroid Build Coastguard Worker pw_build_PYLINT_OUTPUT_FORMAT = "colorized" 48*61c4878aSAndroid Build Coastguard Worker 49*61c4878aSAndroid Build Coastguard Worker # Whether or not to lint/test transitive deps of pw_python_package targets. 50*61c4878aSAndroid Build Coastguard Worker # 51*61c4878aSAndroid Build Coastguard Worker # For example: if lib_a depends on lib_b, lib_a.tests will run after first 52*61c4878aSAndroid Build Coastguard Worker # running lib_b.tests if pw_build_TEST_TRANSITIVE_PYTHON_DEPS is true. 53*61c4878aSAndroid Build Coastguard Worker # 54*61c4878aSAndroid Build Coastguard Worker # If pw_build_TEST_TRANSITIVE_PYTHON_DEPS is false, tests for a 55*61c4878aSAndroid Build Coastguard Worker # pw_python_package will run if you directly build the target (e.g. 56*61c4878aSAndroid Build Coastguard Worker # lib_b.tests) OR if the pw_python_package is placed in a pw_python_group AND 57*61c4878aSAndroid Build Coastguard Worker # you build the group.tests target. 58*61c4878aSAndroid Build Coastguard Worker # 59*61c4878aSAndroid Build Coastguard Worker # This applies to mypy, pylint, ruff and tests. 60*61c4878aSAndroid Build Coastguard Worker # 61*61c4878aSAndroid Build Coastguard Worker # While this defaults to true for compatibility reasons, it's strongly 62*61c4878aSAndroid Build Coastguard Worker # recommended to turn this off so you're not linting and testing all of your 63*61c4878aSAndroid Build Coastguard Worker # external dependencies. 64*61c4878aSAndroid Build Coastguard Worker pw_build_TEST_TRANSITIVE_PYTHON_DEPS = true 65*61c4878aSAndroid Build Coastguard Worker} 66*61c4878aSAndroid Build Coastguard Worker 67*61c4878aSAndroid Build Coastguard Worker# Python packages provide the following targets as $target_name.$subtarget. 68*61c4878aSAndroid Build Coastguard Workerpw_python_package_subtargets = [ 69*61c4878aSAndroid Build Coastguard Worker "tests", 70*61c4878aSAndroid Build Coastguard Worker "lint", 71*61c4878aSAndroid Build Coastguard Worker "lint.mypy", 72*61c4878aSAndroid Build Coastguard Worker "lint.pylint", 73*61c4878aSAndroid Build Coastguard Worker "lint.ruff", 74*61c4878aSAndroid Build Coastguard Worker "install", 75*61c4878aSAndroid Build Coastguard Worker "wheel", 76*61c4878aSAndroid Build Coastguard Worker 77*61c4878aSAndroid Build Coastguard Worker # Internal targets that directly depend on one another. 78*61c4878aSAndroid Build Coastguard Worker "_build_wheel", 79*61c4878aSAndroid Build Coastguard Worker] 80*61c4878aSAndroid Build Coastguard Worker 81*61c4878aSAndroid Build Coastguard Worker# Create aliases for subsargets when the target name matches the directory name. 82*61c4878aSAndroid Build Coastguard Worker# This allows //foo:foo.tests to be accessed as //foo:tests, for example. 83*61c4878aSAndroid Build Coastguard Workertemplate("_pw_create_aliases_if_name_matches_directory") { 84*61c4878aSAndroid Build Coastguard Worker not_needed([ "invoker" ]) 85*61c4878aSAndroid Build Coastguard Worker 86*61c4878aSAndroid Build Coastguard Worker if (get_label_info(":$target_name", "name") == 87*61c4878aSAndroid Build Coastguard Worker get_path_info(get_label_info(":$target_name", "dir"), "name")) { 88*61c4878aSAndroid Build Coastguard Worker foreach(subtarget, pw_python_package_subtargets) { 89*61c4878aSAndroid Build Coastguard Worker group(subtarget) { 90*61c4878aSAndroid Build Coastguard Worker public_deps = [ ":${invoker.target_name}.$subtarget" ] 91*61c4878aSAndroid Build Coastguard Worker } 92*61c4878aSAndroid Build Coastguard Worker } 93*61c4878aSAndroid Build Coastguard Worker } 94*61c4878aSAndroid Build Coastguard Worker} 95*61c4878aSAndroid Build Coastguard Worker 96*61c4878aSAndroid Build Coastguard Worker# Internal template that runs Mypy. 97*61c4878aSAndroid Build Coastguard Workertemplate("_pw_python_static_analysis_mypy") { 98*61c4878aSAndroid Build Coastguard Worker pw_python_action(target_name) { 99*61c4878aSAndroid Build Coastguard Worker module = "mypy" 100*61c4878aSAndroid Build Coastguard Worker 101*61c4878aSAndroid Build Coastguard Worker # DOCSTAG: [default-mypy-args] 102*61c4878aSAndroid Build Coastguard Worker args = [ 103*61c4878aSAndroid Build Coastguard Worker "--pretty", 104*61c4878aSAndroid Build Coastguard Worker "--show-error-codes", 105*61c4878aSAndroid Build Coastguard Worker 106*61c4878aSAndroid Build Coastguard Worker # Use a mypy cache dir for this target only to avoid cache conflicts in 107*61c4878aSAndroid Build Coastguard Worker # parallel mypy invocations. 108*61c4878aSAndroid Build Coastguard Worker "--cache-dir", 109*61c4878aSAndroid Build Coastguard Worker rebase_path(target_out_dir, root_build_dir) + "/.mypy_cache", 110*61c4878aSAndroid Build Coastguard Worker ] 111*61c4878aSAndroid Build Coastguard Worker 112*61c4878aSAndroid Build Coastguard Worker # Use this environment variable to force mypy to colorize output. 113*61c4878aSAndroid Build Coastguard Worker # See https://github.com/python/mypy/issues/7771 114*61c4878aSAndroid Build Coastguard Worker environment = [ "MYPY_FORCE_COLOR=1" ] 115*61c4878aSAndroid Build Coastguard Worker 116*61c4878aSAndroid Build Coastguard Worker # DOCSTAG: [default-mypy-args] 117*61c4878aSAndroid Build Coastguard Worker 118*61c4878aSAndroid Build Coastguard Worker if (defined(invoker.mypy_ini)) { 119*61c4878aSAndroid Build Coastguard Worker args += 120*61c4878aSAndroid Build Coastguard Worker [ "--config-file=" + rebase_path(invoker.mypy_ini, root_build_dir) ] 121*61c4878aSAndroid Build Coastguard Worker inputs = [ invoker.mypy_ini ] 122*61c4878aSAndroid Build Coastguard Worker } 123*61c4878aSAndroid Build Coastguard Worker 124*61c4878aSAndroid Build Coastguard Worker args += rebase_path(invoker.sources, root_build_dir) 125*61c4878aSAndroid Build Coastguard Worker 126*61c4878aSAndroid Build Coastguard Worker stamp = true 127*61c4878aSAndroid Build Coastguard Worker 128*61c4878aSAndroid Build Coastguard Worker deps = invoker.deps 129*61c4878aSAndroid Build Coastguard Worker 130*61c4878aSAndroid Build Coastguard Worker if (defined(invoker.python_deps)) { 131*61c4878aSAndroid Build Coastguard Worker python_deps = invoker.python_deps 132*61c4878aSAndroid Build Coastguard Worker if (pw_build_TEST_TRANSITIVE_PYTHON_DEPS) { 133*61c4878aSAndroid Build Coastguard Worker foreach(dep, invoker.python_deps) { 134*61c4878aSAndroid Build Coastguard Worker deps += [ string_replace(dep, "(", ".lint.mypy(") ] 135*61c4878aSAndroid Build Coastguard Worker } 136*61c4878aSAndroid Build Coastguard Worker } 137*61c4878aSAndroid Build Coastguard Worker } 138*61c4878aSAndroid Build Coastguard Worker if (defined(invoker.python_metadata_deps)) { 139*61c4878aSAndroid Build Coastguard Worker python_metadata_deps = invoker.python_metadata_deps 140*61c4878aSAndroid Build Coastguard Worker } 141*61c4878aSAndroid Build Coastguard Worker } 142*61c4878aSAndroid Build Coastguard Worker} 143*61c4878aSAndroid Build Coastguard Worker 144*61c4878aSAndroid Build Coastguard Worker# Internal template that runs Pylint. 145*61c4878aSAndroid Build Coastguard Workertemplate("_pw_python_static_analysis_pylint") { 146*61c4878aSAndroid Build Coastguard Worker # Create a target to run pylint on each of the Python files in this 147*61c4878aSAndroid Build Coastguard Worker # package and its dependencies. 148*61c4878aSAndroid Build Coastguard Worker pw_python_action_foreach(target_name) { 149*61c4878aSAndroid Build Coastguard Worker module = "pylint" 150*61c4878aSAndroid Build Coastguard Worker args = [ 151*61c4878aSAndroid Build Coastguard Worker rebase_path(".", root_build_dir) + "/{{source_target_relative}}", 152*61c4878aSAndroid Build Coastguard Worker "--jobs=1", 153*61c4878aSAndroid Build Coastguard Worker "--output-format=$pw_build_PYLINT_OUTPUT_FORMAT", 154*61c4878aSAndroid Build Coastguard Worker ] 155*61c4878aSAndroid Build Coastguard Worker 156*61c4878aSAndroid Build Coastguard Worker if (defined(invoker.pylintrc)) { 157*61c4878aSAndroid Build Coastguard Worker args += [ "--rcfile=" + rebase_path(invoker.pylintrc, root_build_dir) ] 158*61c4878aSAndroid Build Coastguard Worker inputs = [ invoker.pylintrc ] 159*61c4878aSAndroid Build Coastguard Worker } 160*61c4878aSAndroid Build Coastguard Worker 161*61c4878aSAndroid Build Coastguard Worker if (host_os == "win") { 162*61c4878aSAndroid Build Coastguard Worker # Allow CRLF on Windows, in case Git is set to switch line endings. 163*61c4878aSAndroid Build Coastguard Worker args += [ "--disable=unexpected-line-ending-format" ] 164*61c4878aSAndroid Build Coastguard Worker } 165*61c4878aSAndroid Build Coastguard Worker 166*61c4878aSAndroid Build Coastguard Worker sources = invoker.sources 167*61c4878aSAndroid Build Coastguard Worker 168*61c4878aSAndroid Build Coastguard Worker stamp = "$target_gen_dir/{{source_target_relative}}.pylint.passed" 169*61c4878aSAndroid Build Coastguard Worker 170*61c4878aSAndroid Build Coastguard Worker public_deps = invoker.deps 171*61c4878aSAndroid Build Coastguard Worker 172*61c4878aSAndroid Build Coastguard Worker if (defined(invoker.python_deps)) { 173*61c4878aSAndroid Build Coastguard Worker python_deps = invoker.python_deps 174*61c4878aSAndroid Build Coastguard Worker if (pw_build_TEST_TRANSITIVE_PYTHON_DEPS) { 175*61c4878aSAndroid Build Coastguard Worker foreach(dep, invoker.python_deps) { 176*61c4878aSAndroid Build Coastguard Worker public_deps += [ string_replace(dep, "(", ".lint.pylint(") ] 177*61c4878aSAndroid Build Coastguard Worker } 178*61c4878aSAndroid Build Coastguard Worker } 179*61c4878aSAndroid Build Coastguard Worker } 180*61c4878aSAndroid Build Coastguard Worker if (defined(invoker.python_metadata_deps)) { 181*61c4878aSAndroid Build Coastguard Worker python_metadata_deps = invoker.python_metadata_deps 182*61c4878aSAndroid Build Coastguard Worker } 183*61c4878aSAndroid Build Coastguard Worker } 184*61c4878aSAndroid Build Coastguard Worker} 185*61c4878aSAndroid Build Coastguard Worker 186*61c4878aSAndroid Build Coastguard Workertemplate("_pw_python_static_analysis_ruff") { 187*61c4878aSAndroid Build Coastguard Worker # Create a target to run ruff on each of the Python files in this 188*61c4878aSAndroid Build Coastguard Worker # package and its dependencies. 189*61c4878aSAndroid Build Coastguard Worker pw_python_action_foreach(target_name) { 190*61c4878aSAndroid Build Coastguard Worker script = "$dir_pw_build/py/pw_build/exec.py" 191*61c4878aSAndroid Build Coastguard Worker args = [ 192*61c4878aSAndroid Build Coastguard Worker "--", 193*61c4878aSAndroid Build Coastguard Worker "ruff", 194*61c4878aSAndroid Build Coastguard Worker "check", 195*61c4878aSAndroid Build Coastguard Worker rebase_path(".", root_build_dir) + "/{{source_target_relative}}", 196*61c4878aSAndroid Build Coastguard Worker ] 197*61c4878aSAndroid Build Coastguard Worker 198*61c4878aSAndroid Build Coastguard Worker if (defined(invoker.ruff_toml)) { 199*61c4878aSAndroid Build Coastguard Worker args += [ 200*61c4878aSAndroid Build Coastguard Worker "--config", 201*61c4878aSAndroid Build Coastguard Worker rebase_path(invoker.ruff_toml, root_build_dir), 202*61c4878aSAndroid Build Coastguard Worker ] 203*61c4878aSAndroid Build Coastguard Worker inputs = [ invoker.ruff_toml ] 204*61c4878aSAndroid Build Coastguard Worker } 205*61c4878aSAndroid Build Coastguard Worker 206*61c4878aSAndroid Build Coastguard Worker environment = [ "CLICOLOR_FORCE=1" ] 207*61c4878aSAndroid Build Coastguard Worker 208*61c4878aSAndroid Build Coastguard Worker sources = invoker.sources 209*61c4878aSAndroid Build Coastguard Worker 210*61c4878aSAndroid Build Coastguard Worker stamp = "$target_gen_dir/{{source_target_relative}}.ruff.passed" 211*61c4878aSAndroid Build Coastguard Worker 212*61c4878aSAndroid Build Coastguard Worker public_deps = invoker.deps 213*61c4878aSAndroid Build Coastguard Worker 214*61c4878aSAndroid Build Coastguard Worker if (defined(invoker.python_deps)) { 215*61c4878aSAndroid Build Coastguard Worker python_deps = [] 216*61c4878aSAndroid Build Coastguard Worker foreach(dep, invoker.python_deps) { 217*61c4878aSAndroid Build Coastguard Worker public_deps += [ string_replace(dep, "(", ".lint.ruff(") ] 218*61c4878aSAndroid Build Coastguard Worker python_deps += [ dep ] 219*61c4878aSAndroid Build Coastguard Worker } 220*61c4878aSAndroid Build Coastguard Worker } 221*61c4878aSAndroid Build Coastguard Worker if (defined(invoker.python_metadata_deps)) { 222*61c4878aSAndroid Build Coastguard Worker python_metadata_deps = invoker.python_metadata_deps 223*61c4878aSAndroid Build Coastguard Worker } 224*61c4878aSAndroid Build Coastguard Worker } 225*61c4878aSAndroid Build Coastguard Worker} 226*61c4878aSAndroid Build Coastguard Worker 227*61c4878aSAndroid Build Coastguard Worker# Defines a Python package. GN Python packages contain several GN targets: 228*61c4878aSAndroid Build Coastguard Worker# 229*61c4878aSAndroid Build Coastguard Worker# - $name - Provides the Python files in the build, but does not take any 230*61c4878aSAndroid Build Coastguard Worker# actions. All subtargets depend on this target. 231*61c4878aSAndroid Build Coastguard Worker# - $name.lint - Runs static analyis tools on the Python code. This is a group 232*61c4878aSAndroid Build Coastguard Worker# of two subtargets: 233*61c4878aSAndroid Build Coastguard Worker# - $name.lint.mypy - Runs mypy (if enabled). 234*61c4878aSAndroid Build Coastguard Worker# - $name.lint.pylint - Runs pylint (if enabled). 235*61c4878aSAndroid Build Coastguard Worker# - $name.lint.ruff - Runs ruff (if enabled). 236*61c4878aSAndroid Build Coastguard Worker# - $name.tests - Runs all tests for this package. 237*61c4878aSAndroid Build Coastguard Worker# - $name.install - Installs the package in a venv. 238*61c4878aSAndroid Build Coastguard Worker# - $name.wheel - Builds a Python wheel for the package. 239*61c4878aSAndroid Build Coastguard Worker# 240*61c4878aSAndroid Build Coastguard Worker# All Python packages are instantiated with in pw_build_PYTHON_TOOLCHAIN, 241*61c4878aSAndroid Build Coastguard Worker# regardless of the current toolchain. This prevents Python-specific work, like 242*61c4878aSAndroid Build Coastguard Worker# running Pylint, from occurring multiple times in a build. 243*61c4878aSAndroid Build Coastguard Worker# 244*61c4878aSAndroid Build Coastguard Worker# Args: 245*61c4878aSAndroid Build Coastguard Worker# setup: List of setup file paths (setup.py or pyproject.toml & setup.cfg), 246*61c4878aSAndroid Build Coastguard Worker# which must all be in the same directory. 247*61c4878aSAndroid Build Coastguard Worker# generate_setup: As an alternative to 'setup', generate setup files with the 248*61c4878aSAndroid Build Coastguard Worker# keywords in this scope. 'name' is required. 249*61c4878aSAndroid Build Coastguard Worker# sources: Python sources files in the package. 250*61c4878aSAndroid Build Coastguard Worker# tests: Test files for this Python package. 251*61c4878aSAndroid Build Coastguard Worker# python_deps: Dependencies on other pw_python_packages in the GN build. 252*61c4878aSAndroid Build Coastguard Worker# python_test_deps: Test-only pw_python_package dependencies. 253*61c4878aSAndroid Build Coastguard Worker# other_deps: Dependencies on GN targets that are not pw_python_packages. 254*61c4878aSAndroid Build Coastguard Worker# inputs: Other files to track, such as package_data. 255*61c4878aSAndroid Build Coastguard Worker# proto_library: A pw_proto_library target to embed in this Python package. 256*61c4878aSAndroid Build Coastguard Worker# generate_setup is required in place of setup if proto_library is used. 257*61c4878aSAndroid Build Coastguard Worker# static_analysis: List of static analysis tools to run; "*" (default) runs 258*61c4878aSAndroid Build Coastguard Worker# all tools. The supported tools are "mypy", "pylint" and "ruff". 259*61c4878aSAndroid Build Coastguard Worker# pylintrc: Path to a pylintrc configuration file to use. If not 260*61c4878aSAndroid Build Coastguard Worker# provided, Pylint's default rcfile search is used. As this may 261*61c4878aSAndroid Build Coastguard Worker# use the the local user's configuration file, it is highly 262*61c4878aSAndroid Build Coastguard Worker# recommended to pass this option to specify the rcfile explicitly. 263*61c4878aSAndroid Build Coastguard Worker# mypy_ini: Optional path to a mypy configuration file to use. If not 264*61c4878aSAndroid Build Coastguard Worker# provided, mypy's default configuration file search is used. mypy is 265*61c4878aSAndroid Build Coastguard Worker# executed from the package's setup directory, so mypy.ini files in that 266*61c4878aSAndroid Build Coastguard Worker# directory will take precedence over others. 267*61c4878aSAndroid Build Coastguard Worker# ruff_toml: Path to a ruff.toml configuration file to use. 268*61c4878aSAndroid Build Coastguard Worker# 269*61c4878aSAndroid Build Coastguard Workertemplate("pw_python_package") { 270*61c4878aSAndroid Build Coastguard Worker # The Python targets are always instantiated in pw_build_PYTHON_TOOLCHAIN. Use 271*61c4878aSAndroid Build Coastguard Worker # fully qualified labels so that the toolchain is not lost. 272*61c4878aSAndroid Build Coastguard Worker _other_deps = [] 273*61c4878aSAndroid Build Coastguard Worker if (defined(invoker.other_deps)) { 274*61c4878aSAndroid Build Coastguard Worker foreach(dep, invoker.other_deps) { 275*61c4878aSAndroid Build Coastguard Worker _other_deps += [ get_label_info(dep, "label_with_toolchain") ] 276*61c4878aSAndroid Build Coastguard Worker } 277*61c4878aSAndroid Build Coastguard Worker } 278*61c4878aSAndroid Build Coastguard Worker 279*61c4878aSAndroid Build Coastguard Worker _python_deps = [] 280*61c4878aSAndroid Build Coastguard Worker if (defined(invoker.python_deps)) { 281*61c4878aSAndroid Build Coastguard Worker foreach(dep, invoker.python_deps) { 282*61c4878aSAndroid Build Coastguard Worker _python_deps += [ get_label_info(dep, "label_with_toolchain") ] 283*61c4878aSAndroid Build Coastguard Worker } 284*61c4878aSAndroid Build Coastguard Worker } 285*61c4878aSAndroid Build Coastguard Worker 286*61c4878aSAndroid Build Coastguard Worker # pw_python_script uses pw_python_package, but with a limited set of features. 287*61c4878aSAndroid Build Coastguard Worker # _pw_standalone signals that this target is actually a pw_python_script. 288*61c4878aSAndroid Build Coastguard Worker _is_package = !(defined(invoker._pw_standalone) && invoker._pw_standalone) 289*61c4878aSAndroid Build Coastguard Worker 290*61c4878aSAndroid Build Coastguard Worker _generate_package = false 291*61c4878aSAndroid Build Coastguard Worker 292*61c4878aSAndroid Build Coastguard Worker _pydeplabel = get_label_info(":$target_name", "label_with_toolchain") 293*61c4878aSAndroid Build Coastguard Worker 294*61c4878aSAndroid Build Coastguard Worker # If a package does not run static analysis or if it does but doesn't have 295*61c4878aSAndroid Build Coastguard Worker # any tests then this variable is not used. 296*61c4878aSAndroid Build Coastguard Worker not_needed([ "_pydeplabel" ]) 297*61c4878aSAndroid Build Coastguard Worker 298*61c4878aSAndroid Build Coastguard Worker # Check the generate_setup and import_protos args to determine if this package 299*61c4878aSAndroid Build Coastguard Worker # is generated. 300*61c4878aSAndroid Build Coastguard Worker if (_is_package) { 301*61c4878aSAndroid Build Coastguard Worker assert(defined(invoker.generate_setup) != defined(invoker.setup), 302*61c4878aSAndroid Build Coastguard Worker "Either 'setup' or 'generate_setup' (but not both) must provided") 303*61c4878aSAndroid Build Coastguard Worker 304*61c4878aSAndroid Build Coastguard Worker if (defined(invoker.proto_library)) { 305*61c4878aSAndroid Build Coastguard Worker assert(invoker.proto_library != "", "'proto_library' cannot be empty") 306*61c4878aSAndroid Build Coastguard Worker assert(defined(invoker.generate_setup), 307*61c4878aSAndroid Build Coastguard Worker "Python packages that import protos with 'proto_library' must " + 308*61c4878aSAndroid Build Coastguard Worker "use 'generate_setup' instead of 'setup'") 309*61c4878aSAndroid Build Coastguard Worker 310*61c4878aSAndroid Build Coastguard Worker _import_protos = [ invoker.proto_library ] 311*61c4878aSAndroid Build Coastguard Worker 312*61c4878aSAndroid Build Coastguard Worker # Depend on the dependencies of the proto library. 313*61c4878aSAndroid Build Coastguard Worker _proto = get_label_info(invoker.proto_library, "label_no_toolchain") 314*61c4878aSAndroid Build Coastguard Worker _toolchain = get_label_info(invoker.proto_library, "toolchain") 315*61c4878aSAndroid Build Coastguard Worker _python_deps += [ "$_proto.python._deps($_toolchain)" ] 316*61c4878aSAndroid Build Coastguard Worker } else if (defined(invoker.generate_setup)) { 317*61c4878aSAndroid Build Coastguard Worker _import_protos = [] 318*61c4878aSAndroid Build Coastguard Worker } 319*61c4878aSAndroid Build Coastguard Worker 320*61c4878aSAndroid Build Coastguard Worker if (defined(invoker.generate_setup)) { 321*61c4878aSAndroid Build Coastguard Worker _generate_package = true 322*61c4878aSAndroid Build Coastguard Worker _setup_dir = "$target_gen_dir/$target_name.generated_python_package" 323*61c4878aSAndroid Build Coastguard Worker 324*61c4878aSAndroid Build Coastguard Worker if (defined(invoker.strip_prefix)) { 325*61c4878aSAndroid Build Coastguard Worker _source_root = invoker.strip_prefix 326*61c4878aSAndroid Build Coastguard Worker } else { 327*61c4878aSAndroid Build Coastguard Worker _source_root = "." 328*61c4878aSAndroid Build Coastguard Worker } 329*61c4878aSAndroid Build Coastguard Worker } else { 330*61c4878aSAndroid Build Coastguard Worker # Non-generated packages with sources provided need an __init__.py. 331*61c4878aSAndroid Build Coastguard Worker assert(!defined(invoker.sources) || invoker.sources == [] || 332*61c4878aSAndroid Build Coastguard Worker filter_include(invoker.sources, [ "*\b__init__.py" ]) != [], 333*61c4878aSAndroid Build Coastguard Worker "Python packages must have at least one __init__.py file") 334*61c4878aSAndroid Build Coastguard Worker 335*61c4878aSAndroid Build Coastguard Worker # Get the directories of the setup files. All must be in the same dir. 336*61c4878aSAndroid Build Coastguard Worker _setup_dirs = get_path_info(invoker.setup, "dir") 337*61c4878aSAndroid Build Coastguard Worker _setup_dir = _setup_dirs[0] 338*61c4878aSAndroid Build Coastguard Worker 339*61c4878aSAndroid Build Coastguard Worker foreach(dir, _setup_dirs) { 340*61c4878aSAndroid Build Coastguard Worker assert(dir == _setup_dir, 341*61c4878aSAndroid Build Coastguard Worker "All files in 'setup' must be in the same directory") 342*61c4878aSAndroid Build Coastguard Worker } 343*61c4878aSAndroid Build Coastguard Worker 344*61c4878aSAndroid Build Coastguard Worker assert(!defined(invoker.strip_prefix), 345*61c4878aSAndroid Build Coastguard Worker "'strip_prefix' may only be given if 'generate_setup' is provided") 346*61c4878aSAndroid Build Coastguard Worker } 347*61c4878aSAndroid Build Coastguard Worker } 348*61c4878aSAndroid Build Coastguard Worker 349*61c4878aSAndroid Build Coastguard Worker # Process arguments defaults and set defaults. 350*61c4878aSAndroid Build Coastguard Worker 351*61c4878aSAndroid Build Coastguard Worker _supported_static_analysis_tools = [ 352*61c4878aSAndroid Build Coastguard Worker "mypy", 353*61c4878aSAndroid Build Coastguard Worker "pylint", 354*61c4878aSAndroid Build Coastguard Worker "ruff", 355*61c4878aSAndroid Build Coastguard Worker ] 356*61c4878aSAndroid Build Coastguard Worker not_needed([ "_supported_static_analysis_tools" ]) 357*61c4878aSAndroid Build Coastguard Worker 358*61c4878aSAndroid Build Coastguard Worker # Argument: static_analysis (list of tool names or "*"); default = "*" (all) 359*61c4878aSAndroid Build Coastguard Worker if (!defined(invoker.static_analysis) || invoker.static_analysis == "*") { 360*61c4878aSAndroid Build Coastguard Worker _static_analysis = pw_build_PYTHON_STATIC_ANALYSIS_TOOLS 361*61c4878aSAndroid Build Coastguard Worker } else { 362*61c4878aSAndroid Build Coastguard Worker _static_analysis = invoker.static_analysis 363*61c4878aSAndroid Build Coastguard Worker } 364*61c4878aSAndroid Build Coastguard Worker 365*61c4878aSAndroid Build Coastguard Worker foreach(_tool, _static_analysis) { 366*61c4878aSAndroid Build Coastguard Worker assert(_supported_static_analysis_tools + [ _tool ] - [ _tool ] != 367*61c4878aSAndroid Build Coastguard Worker _supported_static_analysis_tools, 368*61c4878aSAndroid Build Coastguard Worker "'$_tool' is not a supported static analysis tool") 369*61c4878aSAndroid Build Coastguard Worker } 370*61c4878aSAndroid Build Coastguard Worker 371*61c4878aSAndroid Build Coastguard Worker # Argument: sources (list) 372*61c4878aSAndroid Build Coastguard Worker _sources = [] 373*61c4878aSAndroid Build Coastguard Worker if (defined(invoker.sources)) { 374*61c4878aSAndroid Build Coastguard Worker if (_generate_package) { 375*61c4878aSAndroid Build Coastguard Worker foreach(source, rebase_path(invoker.sources, _source_root)) { 376*61c4878aSAndroid Build Coastguard Worker _sources += [ "$_setup_dir/$source" ] 377*61c4878aSAndroid Build Coastguard Worker } 378*61c4878aSAndroid Build Coastguard Worker } else { 379*61c4878aSAndroid Build Coastguard Worker _sources += invoker.sources 380*61c4878aSAndroid Build Coastguard Worker } 381*61c4878aSAndroid Build Coastguard Worker } 382*61c4878aSAndroid Build Coastguard Worker 383*61c4878aSAndroid Build Coastguard Worker # Argument: tests (list) 384*61c4878aSAndroid Build Coastguard Worker _test_sources = [] 385*61c4878aSAndroid Build Coastguard Worker if (defined(invoker.tests)) { 386*61c4878aSAndroid Build Coastguard Worker if (_generate_package) { 387*61c4878aSAndroid Build Coastguard Worker foreach(source, rebase_path(invoker.tests, _source_root)) { 388*61c4878aSAndroid Build Coastguard Worker _test_sources += [ "$_setup_dir/$source" ] 389*61c4878aSAndroid Build Coastguard Worker } 390*61c4878aSAndroid Build Coastguard Worker } else { 391*61c4878aSAndroid Build Coastguard Worker _test_sources += invoker.tests 392*61c4878aSAndroid Build Coastguard Worker } 393*61c4878aSAndroid Build Coastguard Worker } 394*61c4878aSAndroid Build Coastguard Worker 395*61c4878aSAndroid Build Coastguard Worker # Argument: setup (list) 396*61c4878aSAndroid Build Coastguard Worker _setup_sources = [] 397*61c4878aSAndroid Build Coastguard Worker if (defined(invoker.setup)) { 398*61c4878aSAndroid Build Coastguard Worker _setup_sources = invoker.setup 399*61c4878aSAndroid Build Coastguard Worker } else if (_generate_package) { 400*61c4878aSAndroid Build Coastguard Worker _setup_sources = [ 401*61c4878aSAndroid Build Coastguard Worker "$_setup_dir/pyproject.toml", 402*61c4878aSAndroid Build Coastguard Worker "$_setup_dir/setup.cfg", 403*61c4878aSAndroid Build Coastguard Worker ] 404*61c4878aSAndroid Build Coastguard Worker } 405*61c4878aSAndroid Build Coastguard Worker 406*61c4878aSAndroid Build Coastguard Worker # Argument: python_test_deps (list) 407*61c4878aSAndroid Build Coastguard Worker _python_test_deps = _python_deps # include all deps in test deps 408*61c4878aSAndroid Build Coastguard Worker if (defined(invoker.python_test_deps)) { 409*61c4878aSAndroid Build Coastguard Worker foreach(dep, invoker.python_test_deps) { 410*61c4878aSAndroid Build Coastguard Worker _python_test_deps += [ get_label_info(dep, "label_with_toolchain") ] 411*61c4878aSAndroid Build Coastguard Worker } 412*61c4878aSAndroid Build Coastguard Worker } 413*61c4878aSAndroid Build Coastguard Worker 414*61c4878aSAndroid Build Coastguard Worker if (_test_sources == []) { 415*61c4878aSAndroid Build Coastguard Worker assert(!defined(invoker.python_test_deps), 416*61c4878aSAndroid Build Coastguard Worker "python_test_deps was provided, but there are no tests in " + 417*61c4878aSAndroid Build Coastguard Worker get_label_info(":$target_name", "label_no_toolchain")) 418*61c4878aSAndroid Build Coastguard Worker not_needed([ "_python_test_deps" ]) 419*61c4878aSAndroid Build Coastguard Worker } 420*61c4878aSAndroid Build Coastguard Worker 421*61c4878aSAndroid Build Coastguard Worker _all_py_files = 422*61c4878aSAndroid Build Coastguard Worker _sources + _test_sources + filter_include(_setup_sources, [ "*.py" ]) 423*61c4878aSAndroid Build Coastguard Worker 424*61c4878aSAndroid Build Coastguard Worker # The pw_python_package subtargets are only instantiated in 425*61c4878aSAndroid Build Coastguard Worker # pw_build_PYTHON_TOOLCHAIN. Targets in other toolchains just refer to the 426*61c4878aSAndroid Build Coastguard Worker # targets in this toolchain. 427*61c4878aSAndroid Build Coastguard Worker if (current_toolchain == pw_build_PYTHON_TOOLCHAIN) { 428*61c4878aSAndroid Build Coastguard Worker # Create the package_metadata.json file. This is used by the 429*61c4878aSAndroid Build Coastguard Worker # pw_python_distribution template. 430*61c4878aSAndroid Build Coastguard Worker _package_metadata_json_file = 431*61c4878aSAndroid Build Coastguard Worker "$target_gen_dir/$target_name/package_metadata.json" 432*61c4878aSAndroid Build Coastguard Worker 433*61c4878aSAndroid Build Coastguard Worker # Get Python package metadata and write to disk as JSON. 434*61c4878aSAndroid Build Coastguard Worker _package_metadata = { 435*61c4878aSAndroid Build Coastguard Worker gn_target_name = 436*61c4878aSAndroid Build Coastguard Worker get_label_info(":${invoker.target_name}", "label_no_toolchain") 437*61c4878aSAndroid Build Coastguard Worker 438*61c4878aSAndroid Build Coastguard Worker # Get package source files 439*61c4878aSAndroid Build Coastguard Worker sources = rebase_path(_sources, root_build_dir) 440*61c4878aSAndroid Build Coastguard Worker 441*61c4878aSAndroid Build Coastguard Worker # Get setup.cfg, pyproject.toml, or setup.py file 442*61c4878aSAndroid Build Coastguard Worker setup_sources = rebase_path(_setup_sources, root_build_dir) 443*61c4878aSAndroid Build Coastguard Worker 444*61c4878aSAndroid Build Coastguard Worker # Get test source files 445*61c4878aSAndroid Build Coastguard Worker tests = rebase_path(_test_sources, root_build_dir) 446*61c4878aSAndroid Build Coastguard Worker 447*61c4878aSAndroid Build Coastguard Worker # Get package input files (package data) 448*61c4878aSAndroid Build Coastguard Worker inputs = [] 449*61c4878aSAndroid Build Coastguard Worker if (defined(invoker.inputs)) { 450*61c4878aSAndroid Build Coastguard Worker inputs = rebase_path(invoker.inputs, root_build_dir) 451*61c4878aSAndroid Build Coastguard Worker } 452*61c4878aSAndroid Build Coastguard Worker 453*61c4878aSAndroid Build Coastguard Worker # Get generate_setup 454*61c4878aSAndroid Build Coastguard Worker if (defined(invoker.generate_setup)) { 455*61c4878aSAndroid Build Coastguard Worker generate_setup = invoker.generate_setup 456*61c4878aSAndroid Build Coastguard Worker } 457*61c4878aSAndroid Build Coastguard Worker } 458*61c4878aSAndroid Build Coastguard Worker 459*61c4878aSAndroid Build Coastguard Worker # Finally, write out the json 460*61c4878aSAndroid Build Coastguard Worker write_file(_package_metadata_json_file, _package_metadata, "json") 461*61c4878aSAndroid Build Coastguard Worker 462*61c4878aSAndroid Build Coastguard Worker # Create a target group for the Python package metadata only. This is a 463*61c4878aSAndroid Build Coastguard Worker # python_action so the setup sources can be included as inputs. 464*61c4878aSAndroid Build Coastguard Worker pw_python_action("$target_name._package_metadata") { 465*61c4878aSAndroid Build Coastguard Worker metadata = { 466*61c4878aSAndroid Build Coastguard Worker pw_python_package_metadata_json = [ _package_metadata_json_file ] 467*61c4878aSAndroid Build Coastguard Worker } 468*61c4878aSAndroid Build Coastguard Worker 469*61c4878aSAndroid Build Coastguard Worker script = "$dir_pw_build/py/pw_build/nop.py" 470*61c4878aSAndroid Build Coastguard Worker 471*61c4878aSAndroid Build Coastguard Worker if (_generate_package) { 472*61c4878aSAndroid Build Coastguard Worker inputs = [ "$_setup_dir/setup.json" ] 473*61c4878aSAndroid Build Coastguard Worker } else { 474*61c4878aSAndroid Build Coastguard Worker inputs = _setup_sources 475*61c4878aSAndroid Build Coastguard Worker } 476*61c4878aSAndroid Build Coastguard Worker 477*61c4878aSAndroid Build Coastguard Worker _pw_internal_run_in_venv = false 478*61c4878aSAndroid Build Coastguard Worker stamp = true 479*61c4878aSAndroid Build Coastguard Worker 480*61c4878aSAndroid Build Coastguard Worker # Forward the package_metadata subtarget for all python_deps. 481*61c4878aSAndroid Build Coastguard Worker public_deps = [] 482*61c4878aSAndroid Build Coastguard Worker foreach(dep, _python_deps) { 483*61c4878aSAndroid Build Coastguard Worker public_deps += [ get_label_info(dep, "label_no_toolchain") + 484*61c4878aSAndroid Build Coastguard Worker "._package_metadata($pw_build_PYTHON_TOOLCHAIN)" ] 485*61c4878aSAndroid Build Coastguard Worker } 486*61c4878aSAndroid Build Coastguard Worker } 487*61c4878aSAndroid Build Coastguard Worker 488*61c4878aSAndroid Build Coastguard Worker # Declare the main Python package group. This represents the Python files, 489*61c4878aSAndroid Build Coastguard Worker # but does not take any actions. GN targets can depend on the package name 490*61c4878aSAndroid Build Coastguard Worker # to run when any files in the package change. 491*61c4878aSAndroid Build Coastguard Worker if (_generate_package) { 492*61c4878aSAndroid Build Coastguard Worker # If this package is generated, mirror the sources to the final directory. 493*61c4878aSAndroid Build Coastguard Worker pw_mirror_tree("$target_name._mirror_sources_to_out_dir") { 494*61c4878aSAndroid Build Coastguard Worker directory = _setup_dir 495*61c4878aSAndroid Build Coastguard Worker 496*61c4878aSAndroid Build Coastguard Worker sources = [] 497*61c4878aSAndroid Build Coastguard Worker if (defined(invoker.sources)) { 498*61c4878aSAndroid Build Coastguard Worker sources += invoker.sources 499*61c4878aSAndroid Build Coastguard Worker } 500*61c4878aSAndroid Build Coastguard Worker if (defined(invoker.tests)) { 501*61c4878aSAndroid Build Coastguard Worker sources += invoker.tests 502*61c4878aSAndroid Build Coastguard Worker } 503*61c4878aSAndroid Build Coastguard Worker if (defined(invoker.inputs)) { 504*61c4878aSAndroid Build Coastguard Worker sources += invoker.inputs 505*61c4878aSAndroid Build Coastguard Worker } 506*61c4878aSAndroid Build Coastguard Worker 507*61c4878aSAndroid Build Coastguard Worker source_root = _source_root 508*61c4878aSAndroid Build Coastguard Worker public_deps = _python_deps + _other_deps 509*61c4878aSAndroid Build Coastguard Worker } 510*61c4878aSAndroid Build Coastguard Worker 511*61c4878aSAndroid Build Coastguard Worker # Get generated_setup scope and write it to disk as JSON. 512*61c4878aSAndroid Build Coastguard Worker 513*61c4878aSAndroid Build Coastguard Worker # Expected setup.cfg structure: 514*61c4878aSAndroid Build Coastguard Worker # https://setuptools.readthedocs.io/en/latest/userguide/declarative_config.html 515*61c4878aSAndroid Build Coastguard Worker _gen_setup = invoker.generate_setup 516*61c4878aSAndroid Build Coastguard Worker assert(defined(_gen_setup.metadata), 517*61c4878aSAndroid Build Coastguard Worker "'metadata = {}' is required in generate_package") 518*61c4878aSAndroid Build Coastguard Worker 519*61c4878aSAndroid Build Coastguard Worker # Get metadata which should contain at least name. 520*61c4878aSAndroid Build Coastguard Worker _gen_metadata = { 521*61c4878aSAndroid Build Coastguard Worker } 522*61c4878aSAndroid Build Coastguard Worker _gen_metadata = _gen_setup.metadata 523*61c4878aSAndroid Build Coastguard Worker assert( 524*61c4878aSAndroid Build Coastguard Worker defined(_gen_metadata.name), 525*61c4878aSAndroid Build Coastguard Worker "metadata = { name = 'package_name' } is required in generate_package") 526*61c4878aSAndroid Build Coastguard Worker 527*61c4878aSAndroid Build Coastguard Worker # Get options which should not have packages or package_data. 528*61c4878aSAndroid Build Coastguard Worker if (defined(_gen_setup.options)) { 529*61c4878aSAndroid Build Coastguard Worker _gen_options = { 530*61c4878aSAndroid Build Coastguard Worker } 531*61c4878aSAndroid Build Coastguard Worker _gen_options = _gen_setup.options 532*61c4878aSAndroid Build Coastguard Worker assert(!defined(_gen_options.packages) && 533*61c4878aSAndroid Build Coastguard Worker !defined(_gen_options.package_data), 534*61c4878aSAndroid Build Coastguard Worker "'packages' and 'package_data' may not be provided " + 535*61c4878aSAndroid Build Coastguard Worker "in 'generate_package' options.") 536*61c4878aSAndroid Build Coastguard Worker } 537*61c4878aSAndroid Build Coastguard Worker 538*61c4878aSAndroid Build Coastguard Worker write_file("$_setup_dir/setup.json", _gen_setup, "json") 539*61c4878aSAndroid Build Coastguard Worker 540*61c4878aSAndroid Build Coastguard Worker # Generate the setup.py, py.typed, and __init__.py files as needed. 541*61c4878aSAndroid Build Coastguard Worker action(target_name) { 542*61c4878aSAndroid Build Coastguard Worker metadata = { 543*61c4878aSAndroid Build Coastguard Worker pw_python_package_metadata_json = [ _package_metadata_json_file ] 544*61c4878aSAndroid Build Coastguard Worker } 545*61c4878aSAndroid Build Coastguard Worker 546*61c4878aSAndroid Build Coastguard Worker script = "$dir_pw_build/py/pw_build/generate_python_package.py" 547*61c4878aSAndroid Build Coastguard Worker args = [ 548*61c4878aSAndroid Build Coastguard Worker "--label", 549*61c4878aSAndroid Build Coastguard Worker get_label_info(":$target_name", "label_no_toolchain"), 550*61c4878aSAndroid Build Coastguard Worker "--generated-root", 551*61c4878aSAndroid Build Coastguard Worker rebase_path(_setup_dir, root_build_dir), 552*61c4878aSAndroid Build Coastguard Worker "--setup-json", 553*61c4878aSAndroid Build Coastguard Worker rebase_path("$_setup_dir/setup.json", root_build_dir), 554*61c4878aSAndroid Build Coastguard Worker ] + rebase_path(_sources, root_build_dir) 555*61c4878aSAndroid Build Coastguard Worker 556*61c4878aSAndroid Build Coastguard Worker # Pass in the .json information files for the imported proto libraries. 557*61c4878aSAndroid Build Coastguard Worker foreach(proto, _import_protos) { 558*61c4878aSAndroid Build Coastguard Worker _label = get_label_info(proto, "label_no_toolchain") + 559*61c4878aSAndroid Build Coastguard Worker ".python($pw_protobuf_compiler_TOOLCHAIN)" 560*61c4878aSAndroid Build Coastguard Worker _file = get_label_info(_label, "target_gen_dir") + "/" + 561*61c4878aSAndroid Build Coastguard Worker get_label_info(_label, "name") + ".json" 562*61c4878aSAndroid Build Coastguard Worker args += [ 563*61c4878aSAndroid Build Coastguard Worker "--proto-library", 564*61c4878aSAndroid Build Coastguard Worker rebase_path(_file, root_build_dir), 565*61c4878aSAndroid Build Coastguard Worker ] 566*61c4878aSAndroid Build Coastguard Worker } 567*61c4878aSAndroid Build Coastguard Worker 568*61c4878aSAndroid Build Coastguard Worker if (defined(invoker._pw_module_as_package) && 569*61c4878aSAndroid Build Coastguard Worker invoker._pw_module_as_package) { 570*61c4878aSAndroid Build Coastguard Worker args += [ "--module-as-package" ] 571*61c4878aSAndroid Build Coastguard Worker } 572*61c4878aSAndroid Build Coastguard Worker 573*61c4878aSAndroid Build Coastguard Worker inputs = [ "$_setup_dir/setup.json" ] 574*61c4878aSAndroid Build Coastguard Worker 575*61c4878aSAndroid Build Coastguard Worker public_deps = [ ":$target_name._mirror_sources_to_out_dir" ] 576*61c4878aSAndroid Build Coastguard Worker 577*61c4878aSAndroid Build Coastguard Worker outputs = _setup_sources 578*61c4878aSAndroid Build Coastguard Worker } 579*61c4878aSAndroid Build Coastguard Worker } else { 580*61c4878aSAndroid Build Coastguard Worker # If the package is not generated, use an input group for the sources. 581*61c4878aSAndroid Build Coastguard Worker pw_input_group(target_name) { 582*61c4878aSAndroid Build Coastguard Worker metadata = { 583*61c4878aSAndroid Build Coastguard Worker pw_python_package_metadata_json = [ _package_metadata_json_file ] 584*61c4878aSAndroid Build Coastguard Worker } 585*61c4878aSAndroid Build Coastguard Worker inputs = _all_py_files 586*61c4878aSAndroid Build Coastguard Worker if (defined(invoker.inputs)) { 587*61c4878aSAndroid Build Coastguard Worker inputs += invoker.inputs 588*61c4878aSAndroid Build Coastguard Worker } 589*61c4878aSAndroid Build Coastguard Worker 590*61c4878aSAndroid Build Coastguard Worker public_deps = _python_deps + _other_deps 591*61c4878aSAndroid Build Coastguard Worker } 592*61c4878aSAndroid Build Coastguard Worker } 593*61c4878aSAndroid Build Coastguard Worker 594*61c4878aSAndroid Build Coastguard Worker if (_is_package) { 595*61c4878aSAndroid Build Coastguard Worker # Builds a Python wheel for this package. Records the output directory 596*61c4878aSAndroid Build Coastguard Worker # in the pw_python_package_wheels metadata key. 597*61c4878aSAndroid Build Coastguard Worker 598*61c4878aSAndroid Build Coastguard Worker pw_python_action("$target_name._build_wheel") { 599*61c4878aSAndroid Build Coastguard Worker _wheel_out_dir = "$target_out_dir/$target_name" 600*61c4878aSAndroid Build Coastguard Worker _wheel_requirement = "$_wheel_out_dir/requirements.txt" 601*61c4878aSAndroid Build Coastguard Worker metadata = { 602*61c4878aSAndroid Build Coastguard Worker pw_python_package_wheels = [ _wheel_out_dir ] 603*61c4878aSAndroid Build Coastguard Worker } 604*61c4878aSAndroid Build Coastguard Worker 605*61c4878aSAndroid Build Coastguard Worker script = "$dir_pw_build/py/pw_build/generate_python_wheel.py" 606*61c4878aSAndroid Build Coastguard Worker 607*61c4878aSAndroid Build Coastguard Worker args = [ 608*61c4878aSAndroid Build Coastguard Worker "--package-dir", 609*61c4878aSAndroid Build Coastguard Worker rebase_path(_setup_dir, root_build_dir), 610*61c4878aSAndroid Build Coastguard Worker "--out-dir", 611*61c4878aSAndroid Build Coastguard Worker rebase_path(_wheel_out_dir, root_build_dir), 612*61c4878aSAndroid Build Coastguard Worker ] 613*61c4878aSAndroid Build Coastguard Worker 614*61c4878aSAndroid Build Coastguard Worker # Add hashes to the _wheel_requirement output. 615*61c4878aSAndroid Build Coastguard Worker if (pw_build_PYTHON_PIP_INSTALL_REQUIRE_HASHES) { 616*61c4878aSAndroid Build Coastguard Worker args += [ "--generate-hashes" ] 617*61c4878aSAndroid Build Coastguard Worker } 618*61c4878aSAndroid Build Coastguard Worker 619*61c4878aSAndroid Build Coastguard Worker deps = [ ":${invoker.target_name}" ] 620*61c4878aSAndroid Build Coastguard Worker foreach(dep, _python_deps) { 621*61c4878aSAndroid Build Coastguard Worker deps += [ string_replace(dep, "(", ".wheel(") ] 622*61c4878aSAndroid Build Coastguard Worker } 623*61c4878aSAndroid Build Coastguard Worker 624*61c4878aSAndroid Build Coastguard Worker outputs = [ _wheel_requirement ] 625*61c4878aSAndroid Build Coastguard Worker } 626*61c4878aSAndroid Build Coastguard Worker } else { 627*61c4878aSAndroid Build Coastguard Worker # Stub for non-package targets. 628*61c4878aSAndroid Build Coastguard Worker group("$target_name._build_wheel") { 629*61c4878aSAndroid Build Coastguard Worker } 630*61c4878aSAndroid Build Coastguard Worker } 631*61c4878aSAndroid Build Coastguard Worker 632*61c4878aSAndroid Build Coastguard Worker # Create the .install and .wheel targets. To limit unnecessary pip 633*61c4878aSAndroid Build Coastguard Worker # executions, non-generated packages are only reinstalled when their 634*61c4878aSAndroid Build Coastguard Worker # setup.py changes. However, targets that depend on the .install subtarget 635*61c4878aSAndroid Build Coastguard Worker # re-run whenever any source files change. 636*61c4878aSAndroid Build Coastguard Worker # 637*61c4878aSAndroid Build Coastguard Worker # These targets just represent the source files if this isn't a package. 638*61c4878aSAndroid Build Coastguard Worker group("$target_name.install") { 639*61c4878aSAndroid Build Coastguard Worker public_deps = [ ":${invoker.target_name}" ] 640*61c4878aSAndroid Build Coastguard Worker 641*61c4878aSAndroid Build Coastguard Worker foreach(dep, _python_deps) { 642*61c4878aSAndroid Build Coastguard Worker public_deps += [ string_replace(dep, "(", ".install(") ] 643*61c4878aSAndroid Build Coastguard Worker } 644*61c4878aSAndroid Build Coastguard Worker } 645*61c4878aSAndroid Build Coastguard Worker 646*61c4878aSAndroid Build Coastguard Worker group("$target_name.wheel") { 647*61c4878aSAndroid Build Coastguard Worker public_deps = [ ":${invoker.target_name}.install" ] 648*61c4878aSAndroid Build Coastguard Worker 649*61c4878aSAndroid Build Coastguard Worker if (_is_package) { 650*61c4878aSAndroid Build Coastguard Worker public_deps += [ ":${invoker.target_name}._build_wheel" ] 651*61c4878aSAndroid Build Coastguard Worker } 652*61c4878aSAndroid Build Coastguard Worker 653*61c4878aSAndroid Build Coastguard Worker foreach(dep, _python_deps) { 654*61c4878aSAndroid Build Coastguard Worker public_deps += [ string_replace(dep, "(", ".wheel(") ] 655*61c4878aSAndroid Build Coastguard Worker } 656*61c4878aSAndroid Build Coastguard Worker } 657*61c4878aSAndroid Build Coastguard Worker 658*61c4878aSAndroid Build Coastguard Worker # Define the static analysis targets for this package. 659*61c4878aSAndroid Build Coastguard Worker group("$target_name.lint") { 660*61c4878aSAndroid Build Coastguard Worker deps = [] 661*61c4878aSAndroid Build Coastguard Worker foreach(_tool, _supported_static_analysis_tools) { 662*61c4878aSAndroid Build Coastguard Worker deps += [ ":${invoker.target_name}.lint.$_tool" ] 663*61c4878aSAndroid Build Coastguard Worker } 664*61c4878aSAndroid Build Coastguard Worker } 665*61c4878aSAndroid Build Coastguard Worker 666*61c4878aSAndroid Build Coastguard Worker if (_static_analysis != [] || _test_sources != []) { 667*61c4878aSAndroid Build Coastguard Worker # All packages to install for either general use or test running. 668*61c4878aSAndroid Build Coastguard Worker _test_install_deps = [ ":$target_name.install" ] 669*61c4878aSAndroid Build Coastguard Worker 670*61c4878aSAndroid Build Coastguard Worker foreach(dep, _python_test_deps) { 671*61c4878aSAndroid Build Coastguard Worker _test_install_deps += [ string_replace(dep, "(", ".install(") ] 672*61c4878aSAndroid Build Coastguard Worker _test_install_deps += [ dep ] 673*61c4878aSAndroid Build Coastguard Worker } 674*61c4878aSAndroid Build Coastguard Worker } 675*61c4878aSAndroid Build Coastguard Worker 676*61c4878aSAndroid Build Coastguard Worker # For packages that are not generated, create targets to run mypy and pylint. 677*61c4878aSAndroid Build Coastguard Worker foreach(_tool, _static_analysis) { 678*61c4878aSAndroid Build Coastguard Worker # Run lint tools from the setup or target directory so that the tools detect 679*61c4878aSAndroid Build Coastguard Worker # config files (e.g. pylintrc or mypy.ini) in that directory. Config files 680*61c4878aSAndroid Build Coastguard Worker # may be explicitly specified with the pylintrc or mypy_ini arguments. 681*61c4878aSAndroid Build Coastguard Worker target("_pw_python_static_analysis_$_tool", "$target_name.lint.$_tool") { 682*61c4878aSAndroid Build Coastguard Worker sources = _all_py_files 683*61c4878aSAndroid Build Coastguard Worker deps = _test_install_deps 684*61c4878aSAndroid Build Coastguard Worker python_deps = _python_deps + _python_test_deps 685*61c4878aSAndroid Build Coastguard Worker 686*61c4878aSAndroid Build Coastguard Worker if (_is_package) { 687*61c4878aSAndroid Build Coastguard Worker python_metadata_deps = [ _pydeplabel ] 688*61c4878aSAndroid Build Coastguard Worker } 689*61c4878aSAndroid Build Coastguard Worker 690*61c4878aSAndroid Build Coastguard Worker _optional_variables = [ 691*61c4878aSAndroid Build Coastguard Worker "mypy_ini", 692*61c4878aSAndroid Build Coastguard Worker "pylintrc", 693*61c4878aSAndroid Build Coastguard Worker "ruff_toml", 694*61c4878aSAndroid Build Coastguard Worker ] 695*61c4878aSAndroid Build Coastguard Worker forward_variables_from(invoker, _optional_variables) 696*61c4878aSAndroid Build Coastguard Worker not_needed(_optional_variables) 697*61c4878aSAndroid Build Coastguard Worker } 698*61c4878aSAndroid Build Coastguard Worker } 699*61c4878aSAndroid Build Coastguard Worker 700*61c4878aSAndroid Build Coastguard Worker foreach(_unused_tool, _supported_static_analysis_tools - _static_analysis) { 701*61c4878aSAndroid Build Coastguard Worker pw_input_group("$target_name.lint.$_unused_tool") { 702*61c4878aSAndroid Build Coastguard Worker inputs = [] 703*61c4878aSAndroid Build Coastguard Worker if (defined(invoker.pylintrc)) { 704*61c4878aSAndroid Build Coastguard Worker inputs += [ invoker.pylintrc ] 705*61c4878aSAndroid Build Coastguard Worker } 706*61c4878aSAndroid Build Coastguard Worker if (defined(invoker.mypy_ini)) { 707*61c4878aSAndroid Build Coastguard Worker inputs += [ invoker.mypy_ini ] 708*61c4878aSAndroid Build Coastguard Worker } 709*61c4878aSAndroid Build Coastguard Worker if (defined(invoker.ruff_toml)) { 710*61c4878aSAndroid Build Coastguard Worker inputs += [ invoker.ruff_toml ] 711*61c4878aSAndroid Build Coastguard Worker } 712*61c4878aSAndroid Build Coastguard Worker } 713*61c4878aSAndroid Build Coastguard Worker 714*61c4878aSAndroid Build Coastguard Worker # Generated packages with linting disabled never need the whole file list. 715*61c4878aSAndroid Build Coastguard Worker not_needed([ "_all_py_files" ]) 716*61c4878aSAndroid Build Coastguard Worker } 717*61c4878aSAndroid Build Coastguard Worker } else { 718*61c4878aSAndroid Build Coastguard Worker # Create groups with the public target names ($target_name, $target_name.lint, 719*61c4878aSAndroid Build Coastguard Worker # $target_name.install, etc.). These are actually wrappers around internal 720*61c4878aSAndroid Build Coastguard Worker # Python actions instantiated with the default toolchain. This ensures there 721*61c4878aSAndroid Build Coastguard Worker # is only a single copy of each Python action in the build. 722*61c4878aSAndroid Build Coastguard Worker # 723*61c4878aSAndroid Build Coastguard Worker # The $target_name.tests group is created separately below. 724*61c4878aSAndroid Build Coastguard Worker group("$target_name") { 725*61c4878aSAndroid Build Coastguard Worker deps = [ ":$target_name($pw_build_PYTHON_TOOLCHAIN)" ] 726*61c4878aSAndroid Build Coastguard Worker } 727*61c4878aSAndroid Build Coastguard Worker 728*61c4878aSAndroid Build Coastguard Worker foreach(subtarget, pw_python_package_subtargets - [ "tests" ]) { 729*61c4878aSAndroid Build Coastguard Worker group("$target_name.$subtarget") { 730*61c4878aSAndroid Build Coastguard Worker deps = 731*61c4878aSAndroid Build Coastguard Worker [ ":${invoker.target_name}.$subtarget($pw_build_PYTHON_TOOLCHAIN)" ] 732*61c4878aSAndroid Build Coastguard Worker } 733*61c4878aSAndroid Build Coastguard Worker } 734*61c4878aSAndroid Build Coastguard Worker 735*61c4878aSAndroid Build Coastguard Worker # Everything Python-related is only instantiated in the default toolchain. 736*61c4878aSAndroid Build Coastguard Worker # Silence not-needed warnings except for in the default toolchain. 737*61c4878aSAndroid Build Coastguard Worker not_needed("*") 738*61c4878aSAndroid Build Coastguard Worker not_needed(invoker, "*") 739*61c4878aSAndroid Build Coastguard Worker } 740*61c4878aSAndroid Build Coastguard Worker 741*61c4878aSAndroid Build Coastguard Worker # Create a target for each test file. 742*61c4878aSAndroid Build Coastguard Worker _test_targets = [] 743*61c4878aSAndroid Build Coastguard Worker 744*61c4878aSAndroid Build Coastguard Worker foreach(test, _test_sources) { 745*61c4878aSAndroid Build Coastguard Worker if (_is_package) { 746*61c4878aSAndroid Build Coastguard Worker _name = rebase_path(test, _setup_dir) 747*61c4878aSAndroid Build Coastguard Worker } else { 748*61c4878aSAndroid Build Coastguard Worker _name = test 749*61c4878aSAndroid Build Coastguard Worker } 750*61c4878aSAndroid Build Coastguard Worker 751*61c4878aSAndroid Build Coastguard Worker _test_target = "$target_name.tests." + string_replace(_name, "/", "_") 752*61c4878aSAndroid Build Coastguard Worker 753*61c4878aSAndroid Build Coastguard Worker if (current_toolchain == pw_build_PYTHON_TOOLCHAIN) { 754*61c4878aSAndroid Build Coastguard Worker pw_python_action(_test_target) { 755*61c4878aSAndroid Build Coastguard Worker if (pw_build_PYTHON_TEST_COVERAGE) { 756*61c4878aSAndroid Build Coastguard Worker module = "coverage" 757*61c4878aSAndroid Build Coastguard Worker working_directory = 758*61c4878aSAndroid Build Coastguard Worker rebase_path(get_path_info(test, "dir"), root_build_dir) 759*61c4878aSAndroid Build Coastguard Worker args = [ 760*61c4878aSAndroid Build Coastguard Worker "run", 761*61c4878aSAndroid Build Coastguard Worker "--branch", 762*61c4878aSAndroid Build Coastguard Worker 763*61c4878aSAndroid Build Coastguard Worker # Include all source files in the working_directory when calculating coverage. 764*61c4878aSAndroid Build Coastguard Worker "--source=.", 765*61c4878aSAndroid Build Coastguard Worker 766*61c4878aSAndroid Build Coastguard Worker # Test file to run. 767*61c4878aSAndroid Build Coastguard Worker get_path_info(test, "file"), 768*61c4878aSAndroid Build Coastguard Worker ] 769*61c4878aSAndroid Build Coastguard Worker 770*61c4878aSAndroid Build Coastguard Worker # Set the coverage file to a location in out/python/gen/ 771*61c4878aSAndroid Build Coastguard Worker _coverage_data_file = "$target_gen_dir/$target_name.coverage" 772*61c4878aSAndroid Build Coastguard Worker outputs = [ _coverage_data_file ] 773*61c4878aSAndroid Build Coastguard Worker 774*61c4878aSAndroid Build Coastguard Worker # The coverage tool only allows setting the output with an environment variable. 775*61c4878aSAndroid Build Coastguard Worker environment = 776*61c4878aSAndroid Build Coastguard Worker [ "COVERAGE_FILE=" + 777*61c4878aSAndroid Build Coastguard Worker rebase_path(_coverage_data_file, get_path_info(test, "dir")) ] 778*61c4878aSAndroid Build Coastguard Worker } else { 779*61c4878aSAndroid Build Coastguard Worker script = test 780*61c4878aSAndroid Build Coastguard Worker } 781*61c4878aSAndroid Build Coastguard Worker 782*61c4878aSAndroid Build Coastguard Worker stamp = true 783*61c4878aSAndroid Build Coastguard Worker 784*61c4878aSAndroid Build Coastguard Worker # Make sure the python test deps are added to the PYTHONPATH. 785*61c4878aSAndroid Build Coastguard Worker python_metadata_deps = _python_test_deps 786*61c4878aSAndroid Build Coastguard Worker 787*61c4878aSAndroid Build Coastguard Worker # If this is a test for a package, add it to PYTHONPATH as well. This is 788*61c4878aSAndroid Build Coastguard Worker # required if the test source file isn't in the same directory as the 789*61c4878aSAndroid Build Coastguard Worker # folder containing the package sources to allow local Python imports. 790*61c4878aSAndroid Build Coastguard Worker if (_is_package) { 791*61c4878aSAndroid Build Coastguard Worker python_metadata_deps += [ _pydeplabel ] 792*61c4878aSAndroid Build Coastguard Worker } 793*61c4878aSAndroid Build Coastguard Worker 794*61c4878aSAndroid Build Coastguard Worker deps = _test_install_deps 795*61c4878aSAndroid Build Coastguard Worker 796*61c4878aSAndroid Build Coastguard Worker if (pw_build_TEST_TRANSITIVE_PYTHON_DEPS) { 797*61c4878aSAndroid Build Coastguard Worker foreach(dep, _python_test_deps) { 798*61c4878aSAndroid Build Coastguard Worker deps += [ string_replace(dep, "(", ".tests(") ] 799*61c4878aSAndroid Build Coastguard Worker } 800*61c4878aSAndroid Build Coastguard Worker } 801*61c4878aSAndroid Build Coastguard Worker } 802*61c4878aSAndroid Build Coastguard Worker } else { 803*61c4878aSAndroid Build Coastguard Worker # Create a public version of each test target, so tests can be executed as 804*61c4878aSAndroid Build Coastguard Worker # //path/to:package.tests.foo.py. 805*61c4878aSAndroid Build Coastguard Worker group(_test_target) { 806*61c4878aSAndroid Build Coastguard Worker deps = [ ":$_test_target($pw_build_PYTHON_TOOLCHAIN)" ] 807*61c4878aSAndroid Build Coastguard Worker } 808*61c4878aSAndroid Build Coastguard Worker } 809*61c4878aSAndroid Build Coastguard Worker 810*61c4878aSAndroid Build Coastguard Worker _test_targets += [ ":$_test_target" ] 811*61c4878aSAndroid Build Coastguard Worker } 812*61c4878aSAndroid Build Coastguard Worker 813*61c4878aSAndroid Build Coastguard Worker group("$target_name.tests") { 814*61c4878aSAndroid Build Coastguard Worker deps = _test_targets 815*61c4878aSAndroid Build Coastguard Worker } 816*61c4878aSAndroid Build Coastguard Worker 817*61c4878aSAndroid Build Coastguard Worker _pw_create_aliases_if_name_matches_directory(target_name) { 818*61c4878aSAndroid Build Coastguard Worker } 819*61c4878aSAndroid Build Coastguard Worker} 820*61c4878aSAndroid Build Coastguard Worker 821*61c4878aSAndroid Build Coastguard Worker# Declares a group of Python packages or other Python groups. pw_python_groups 822*61c4878aSAndroid Build Coastguard Worker# expose the same set of subtargets as pw_python_package (e.g. 823*61c4878aSAndroid Build Coastguard Worker# "$group_name.lint" and "$group_name.tests"), but these apply to all packages 824*61c4878aSAndroid Build Coastguard Worker# in deps and their dependencies. 825*61c4878aSAndroid Build Coastguard Workertemplate("pw_python_group") { 826*61c4878aSAndroid Build Coastguard Worker if (defined(invoker.python_deps)) { 827*61c4878aSAndroid Build Coastguard Worker _python_deps = invoker.python_deps 828*61c4878aSAndroid Build Coastguard Worker } else { 829*61c4878aSAndroid Build Coastguard Worker _python_deps = [] 830*61c4878aSAndroid Build Coastguard Worker not_needed([ "invoker" ]) # Allow empty groups. 831*61c4878aSAndroid Build Coastguard Worker } 832*61c4878aSAndroid Build Coastguard Worker 833*61c4878aSAndroid Build Coastguard Worker group(target_name) { 834*61c4878aSAndroid Build Coastguard Worker deps = _python_deps 835*61c4878aSAndroid Build Coastguard Worker 836*61c4878aSAndroid Build Coastguard Worker if (defined(invoker.other_deps)) { 837*61c4878aSAndroid Build Coastguard Worker deps += invoker.other_deps 838*61c4878aSAndroid Build Coastguard Worker } 839*61c4878aSAndroid Build Coastguard Worker } 840*61c4878aSAndroid Build Coastguard Worker 841*61c4878aSAndroid Build Coastguard Worker # Create a target group for the Python package metadata only. 842*61c4878aSAndroid Build Coastguard Worker group("$target_name._package_metadata") { 843*61c4878aSAndroid Build Coastguard Worker # Forward the package_metadata subtarget for all python_deps. 844*61c4878aSAndroid Build Coastguard Worker public_deps = [] 845*61c4878aSAndroid Build Coastguard Worker foreach(dep, _python_deps) { 846*61c4878aSAndroid Build Coastguard Worker public_deps += [ get_label_info(dep, "label_no_toolchain") + 847*61c4878aSAndroid Build Coastguard Worker "._package_metadata($pw_build_PYTHON_TOOLCHAIN)" ] 848*61c4878aSAndroid Build Coastguard Worker } 849*61c4878aSAndroid Build Coastguard Worker } 850*61c4878aSAndroid Build Coastguard Worker 851*61c4878aSAndroid Build Coastguard Worker foreach(subtarget, pw_python_package_subtargets) { 852*61c4878aSAndroid Build Coastguard Worker group("$target_name.$subtarget") { 853*61c4878aSAndroid Build Coastguard Worker public_deps = [] 854*61c4878aSAndroid Build Coastguard Worker foreach(dep, _python_deps) { 855*61c4878aSAndroid Build Coastguard Worker # Split out the toolchain to support deps with a toolchain specified. 856*61c4878aSAndroid Build Coastguard Worker _target = get_label_info(dep, "label_no_toolchain") 857*61c4878aSAndroid Build Coastguard Worker _toolchain = get_label_info(dep, "toolchain") 858*61c4878aSAndroid Build Coastguard Worker public_deps += [ "$_target.$subtarget($_toolchain)" ] 859*61c4878aSAndroid Build Coastguard Worker } 860*61c4878aSAndroid Build Coastguard Worker } 861*61c4878aSAndroid Build Coastguard Worker } 862*61c4878aSAndroid Build Coastguard Worker 863*61c4878aSAndroid Build Coastguard Worker _pw_create_aliases_if_name_matches_directory(target_name) { 864*61c4878aSAndroid Build Coastguard Worker } 865*61c4878aSAndroid Build Coastguard Worker} 866*61c4878aSAndroid Build Coastguard Worker 867*61c4878aSAndroid Build Coastguard Worker# Declares Python scripts or tests that are not part of a Python package. 868*61c4878aSAndroid Build Coastguard Worker# Similar to pw_python_package, but only supports a subset of its features. 869*61c4878aSAndroid Build Coastguard Worker# 870*61c4878aSAndroid Build Coastguard Worker# pw_python_script accepts the same arguments as pw_python_package, except 871*61c4878aSAndroid Build Coastguard Worker# `setup` cannot be provided. 872*61c4878aSAndroid Build Coastguard Worker# 873*61c4878aSAndroid Build Coastguard Worker# pw_python_script provides the same subtargets as pw_python_package, but 874*61c4878aSAndroid Build Coastguard Worker# $target_name.install and $target_name.wheel only affect the python_deps of 875*61c4878aSAndroid Build Coastguard Worker# this GN target, not the target itself. 876*61c4878aSAndroid Build Coastguard Worker# 877*61c4878aSAndroid Build Coastguard Worker# pw_python_script allows creating a pw_python_action associated with the 878*61c4878aSAndroid Build Coastguard Worker# script. This is provided by passing an 'action' scope to pw_python_script. 879*61c4878aSAndroid Build Coastguard Worker# This functions like a normal action, with a few additions: the action uses the 880*61c4878aSAndroid Build Coastguard Worker# pw_python_script's python_deps and defaults to using the source file as its 881*61c4878aSAndroid Build Coastguard Worker# 'script' argument, if there is only a single source file. 882*61c4878aSAndroid Build Coastguard Workertemplate("pw_python_script") { 883*61c4878aSAndroid Build Coastguard Worker _package_variables = [ 884*61c4878aSAndroid Build Coastguard Worker "sources", 885*61c4878aSAndroid Build Coastguard Worker "tests", 886*61c4878aSAndroid Build Coastguard Worker "python_deps", 887*61c4878aSAndroid Build Coastguard Worker "python_test_deps", 888*61c4878aSAndroid Build Coastguard Worker "python_metadata_deps", 889*61c4878aSAndroid Build Coastguard Worker "other_deps", 890*61c4878aSAndroid Build Coastguard Worker "inputs", 891*61c4878aSAndroid Build Coastguard Worker "pylintrc", 892*61c4878aSAndroid Build Coastguard Worker "mypy_ini", 893*61c4878aSAndroid Build Coastguard Worker "ruff_toml", 894*61c4878aSAndroid Build Coastguard Worker "static_analysis", 895*61c4878aSAndroid Build Coastguard Worker ] 896*61c4878aSAndroid Build Coastguard Worker 897*61c4878aSAndroid Build Coastguard Worker pw_python_package(target_name) { 898*61c4878aSAndroid Build Coastguard Worker _pw_standalone = true 899*61c4878aSAndroid Build Coastguard Worker forward_variables_from(invoker, _package_variables) 900*61c4878aSAndroid Build Coastguard Worker } 901*61c4878aSAndroid Build Coastguard Worker 902*61c4878aSAndroid Build Coastguard Worker _pw_create_aliases_if_name_matches_directory(target_name) { 903*61c4878aSAndroid Build Coastguard Worker } 904*61c4878aSAndroid Build Coastguard Worker 905*61c4878aSAndroid Build Coastguard Worker if (defined(invoker.action)) { 906*61c4878aSAndroid Build Coastguard Worker pw_python_action("$target_name.action") { 907*61c4878aSAndroid Build Coastguard Worker forward_variables_from(invoker.action, "*", [ "python_deps" ]) 908*61c4878aSAndroid Build Coastguard Worker forward_variables_from(invoker, [ "testonly" ]) 909*61c4878aSAndroid Build Coastguard Worker python_deps = [ ":${invoker.target_name}" ] 910*61c4878aSAndroid Build Coastguard Worker 911*61c4878aSAndroid Build Coastguard Worker if (!defined(script) && !defined(module) && defined(invoker.sources)) { 912*61c4878aSAndroid Build Coastguard Worker _sources = invoker.sources 913*61c4878aSAndroid Build Coastguard Worker assert(_sources != [] && _sources == [ _sources[0] ], 914*61c4878aSAndroid Build Coastguard Worker "'script' must be specified unless there is only one source " + 915*61c4878aSAndroid Build Coastguard Worker "in 'sources'") 916*61c4878aSAndroid Build Coastguard Worker script = _sources[0] 917*61c4878aSAndroid Build Coastguard Worker } 918*61c4878aSAndroid Build Coastguard Worker } 919*61c4878aSAndroid Build Coastguard Worker } 920*61c4878aSAndroid Build Coastguard Worker} 921*61c4878aSAndroid Build Coastguard Worker 922*61c4878aSAndroid Build Coastguard Worker# Represents a list of Python requirements, as in a requirements.txt. 923*61c4878aSAndroid Build Coastguard Worker# 924*61c4878aSAndroid Build Coastguard Worker# Args: 925*61c4878aSAndroid Build Coastguard Worker# files: One or more requirements.txt files. 926*61c4878aSAndroid Build Coastguard Worker# requirements: A list of requirements.txt-style requirements. 927*61c4878aSAndroid Build Coastguard Workertemplate("pw_python_requirements") { 928*61c4878aSAndroid Build Coastguard Worker assert(defined(invoker.files) || defined(invoker.requirements), 929*61c4878aSAndroid Build Coastguard Worker "pw_python_requirements requires a list of requirements.txt files " + 930*61c4878aSAndroid Build Coastguard Worker "in the 'files' arg or requirements in 'requirements'") 931*61c4878aSAndroid Build Coastguard Worker 932*61c4878aSAndroid Build Coastguard Worker _requirements_files = [] 933*61c4878aSAndroid Build Coastguard Worker 934*61c4878aSAndroid Build Coastguard Worker if (defined(invoker.files)) { 935*61c4878aSAndroid Build Coastguard Worker _requirements_files += invoker.files 936*61c4878aSAndroid Build Coastguard Worker } 937*61c4878aSAndroid Build Coastguard Worker 938*61c4878aSAndroid Build Coastguard Worker if (defined(invoker.requirements)) { 939*61c4878aSAndroid Build Coastguard Worker _requirements_file = "$target_gen_dir/$target_name.requirements.txt" 940*61c4878aSAndroid Build Coastguard Worker write_file(_requirements_file, invoker.requirements) 941*61c4878aSAndroid Build Coastguard Worker _requirements_files += [ _requirements_file ] 942*61c4878aSAndroid Build Coastguard Worker } 943*61c4878aSAndroid Build Coastguard Worker 944*61c4878aSAndroid Build Coastguard Worker # The default target represents the requirements themselves. 945*61c4878aSAndroid Build Coastguard Worker pw_input_group(target_name) { 946*61c4878aSAndroid Build Coastguard Worker inputs = _requirements_files 947*61c4878aSAndroid Build Coastguard Worker } 948*61c4878aSAndroid Build Coastguard Worker 949*61c4878aSAndroid Build Coastguard Worker # Use the same subtargets as pw_python_package so these targets can be listed 950*61c4878aSAndroid Build Coastguard Worker # as python_deps of pw_python_packages. 951*61c4878aSAndroid Build Coastguard Worker group("$target_name.install") { 952*61c4878aSAndroid Build Coastguard Worker # TODO: b/232800695 - Remove reliance on this subtarget existing. 953*61c4878aSAndroid Build Coastguard Worker } 954*61c4878aSAndroid Build Coastguard Worker 955*61c4878aSAndroid Build Coastguard Worker # Create stubs for the unused subtargets so that pw_python_requirements can be 956*61c4878aSAndroid Build Coastguard Worker # used as python_deps. 957*61c4878aSAndroid Build Coastguard Worker foreach(subtarget, pw_python_package_subtargets - [ "install" ]) { 958*61c4878aSAndroid Build Coastguard Worker group("$target_name.$subtarget") { 959*61c4878aSAndroid Build Coastguard Worker } 960*61c4878aSAndroid Build Coastguard Worker } 961*61c4878aSAndroid Build Coastguard Worker 962*61c4878aSAndroid Build Coastguard Worker # Create a target group for the Python package metadata only. 963*61c4878aSAndroid Build Coastguard Worker group("$target_name._package_metadata") { 964*61c4878aSAndroid Build Coastguard Worker # Forward the package_metadata subtarget for all python_deps. 965*61c4878aSAndroid Build Coastguard Worker public_deps = [] 966*61c4878aSAndroid Build Coastguard Worker if (defined(invoker.python_deps)) { 967*61c4878aSAndroid Build Coastguard Worker foreach(dep, invoker.python_deps) { 968*61c4878aSAndroid Build Coastguard Worker public_deps += [ get_label_info(dep, "label_no_toolchain") + 969*61c4878aSAndroid Build Coastguard Worker "._package_metadata($pw_build_PYTHON_TOOLCHAIN)" ] 970*61c4878aSAndroid Build Coastguard Worker } 971*61c4878aSAndroid Build Coastguard Worker } 972*61c4878aSAndroid Build Coastguard Worker } 973*61c4878aSAndroid Build Coastguard Worker 974*61c4878aSAndroid Build Coastguard Worker _pw_create_aliases_if_name_matches_directory(target_name) { 975*61c4878aSAndroid Build Coastguard Worker } 976*61c4878aSAndroid Build Coastguard Worker} 977