| # Copyright 2026 The Fuchsia Authors. All rights reserved. | |
| # Use of this source code is governed by a BSD-style license that can be | |
| # found in the LICENSE file. | |
| import("//build/config/python_interpreter.gni") | |
| import("//build/python/python_action.gni") | |
| # Defines a Python wheel. | |
| # | |
| # Parameters | |
| # | |
| # deps (required) | |
| # List of python_library or python_c_extension targets to include in the wheel. | |
| # | |
| # pyproject_toml (required) | |
| # Path to the pyproject.toml file for the wheel. Note that the packaging | |
| # script generates a setuptools-based setup.py, so the pyproject.toml must | |
| # configure setuptools.build_meta as its build backend. | |
| # | |
| # version (optional) | |
| # Version of the wheel. Defaults to "0.0.1". | |
| # | |
| # package_name (optional) | |
| # Name of the package. Defaults to target_name. | |
| # | |
| # packages (optional) | |
| # List of packages (directories) to include in the wheel. | |
| # | |
| # wheel_tags (optional) | |
| # Compatibility tags for the wheel adhering to PEP 427 | |
| # ({python tag}-{abi tag}-{platform tag}). Defaults to "py3-none-any" for | |
| # pure Python wheels. For wheels containing C extensions or native shared | |
| # libraries, specify the target platform tags (e.g., | |
| # "cp311-cp311-linux_x86_64"). | |
| # | |
| # output_name (optional) | |
| # Name of the output wheel file. Defaults to | |
| # "${_normalized_package_name}-${_normalized_version}-${_wheel_tags}.whl" to | |
| # conform to the PEP 427 wheel filename specification. | |
| # | |
| # wheel_name (optional) | |
| # Alias for output_name. | |
| # | |
| # output_dir (optional) | |
| # Directory where the wheel will be placed. Defaults to | |
| # ${target_out_dir}/${target_name}. | |
| # | |
| # testonly | |
| # visibility | |
| template("python_wheel") { | |
| assert(defined(invoker.deps), "deps is required") | |
| assert(defined(invoker.pyproject_toml), "pyproject_toml is required") | |
| _version = "0.0.1" | |
| if (defined(invoker.version)) { | |
| _version = invoker.version | |
| } | |
| _package_name = target_name | |
| if (defined(invoker.package_name)) { | |
| _package_name = invoker.package_name | |
| } | |
| # PEP 427 specifies distribution name normalization rules where runs of | |
| # non-alphanumeric characters (like hyphens) are replaced with a single | |
| # underscore. Ensure package_name does not contain whitespace. | |
| assert(string_replace(_package_name, " ", "") == _package_name, | |
| "package_name must not contain whitespace: '${_package_name}'") | |
| _wheel_tags = "py3-none-any" | |
| if (defined(invoker.wheel_tags)) { | |
| _wheel_tags = invoker.wheel_tags | |
| } | |
| # PEP 427 specifies distribution name normalization rules where runs of | |
| # non-alphanumeric characters (like hyphens, dots) are replaced with a single | |
| # underscore. GN lacks regex support, so we chain string_replace. | |
| _normalized_package_name = string_replace(_package_name, "-", "_") | |
| _normalized_package_name = string_replace(_normalized_package_name, ".", "_") | |
| _normalized_package_name = | |
| string_replace(_normalized_package_name, "___", "_") | |
| _normalized_package_name = string_replace(_normalized_package_name, "__", "_") | |
| # Per PEP 427 / PEP 440, version numbers retain periods and only replace | |
| # hyphens with underscores. | |
| _normalized_version = string_replace(_version, "-", "_") | |
| _wheel_name = | |
| "${_normalized_package_name}-${_normalized_version}-${_wheel_tags}.whl" | |
| if (defined(invoker.wheel_name)) { | |
| _wheel_name = invoker.wheel_name | |
| } else if (defined(invoker.output_name)) { | |
| _wheel_name = invoker.output_name | |
| } | |
| _output_dir = "${target_out_dir}/${target_name}" | |
| if (defined(invoker.output_dir)) { | |
| _output_dir = invoker.output_dir | |
| } | |
| _wheel_file = "${_output_dir}/${_wheel_name}" | |
| _library_infos_target = "${target_name}.library_infos" | |
| _library_infos_json = "${target_gen_dir}/${target_name}.library_infos.json" | |
| generated_file(_library_infos_target) { | |
| forward_variables_from(invoker, [ "testonly" ]) | |
| visibility = [ ":*" ] | |
| public_deps = invoker.deps | |
| outputs = [ _library_infos_json ] | |
| output_conversion = "json" | |
| data_keys = [ "library_info" ] | |
| walk_keys = [ "library_info_barrier" ] | |
| } | |
| python_action(target_name) { | |
| forward_variables_from(invoker, | |
| [ | |
| "testonly", | |
| "visibility", | |
| ]) | |
| binary_label = "//build/python:package_python_wheel" | |
| depfile = "${target_out_dir}/${target_name}.d" | |
| # Explicitly declare the stamp file and predictable wheel filename in | |
| # outputs so they are tracked and downloaded by remote execution/caching | |
| # systems (e.g., Siso). | |
| _stamp_file = "${target_gen_dir}/${target_name}.wheel.stamp" | |
| outputs = [ | |
| _stamp_file, | |
| _wheel_file, | |
| ] | |
| inputs = [ | |
| _library_infos_json, | |
| invoker.pyproject_toml, | |
| ] | |
| deps = [ ":${_library_infos_target}" ] + invoker.deps | |
| args = [ | |
| "--target_name", | |
| target_name, | |
| "--python_interpreter", | |
| rebase_path(python_exe_src, root_build_dir), | |
| "--gen_dir", | |
| rebase_path(target_gen_dir, root_build_dir), | |
| "--wheel_file", | |
| rebase_path(_wheel_file, root_build_dir), | |
| "--stamp_file", | |
| rebase_path(_stamp_file, root_build_dir), | |
| "--library_infos", | |
| rebase_path(_library_infos_json, root_build_dir), | |
| "--pyproject_toml", | |
| rebase_path(invoker.pyproject_toml, root_build_dir), | |
| "--depfile", | |
| rebase_path(depfile, root_build_dir), | |
| "--version", | |
| _version, | |
| "--package_name", | |
| _package_name, | |
| ] | |
| if (defined(invoker.packages)) { | |
| args += [ "--packages" ] + invoker.packages | |
| } | |
| } | |
| } |