pw_build: Support building Python wheels
Implement the .wheel subtarget for pw_python_package. This builds a
wheel for that Python package. Python wheels can be collected in a
directory using pw_mirror_tree's path_data_keys option for the "wheels"
key.
Fixed: 239
Change-Id: I43d756ce9714ba834b4590de702efeafc666b9ee
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/36762
Commit-Queue: Wyatt Hepler <hepler@google.com>
Reviewed-by: Keir Mierle <keir@google.com>
Reviewed-by: Joe Ethier <jethier@google.com>
diff --git a/docs/python_build.rst b/docs/python_build.rst
index 4bc4fcd..b89c788 100644
--- a/docs/python_build.rst
+++ b/docs/python_build.rst
@@ -278,20 +278,14 @@
Python build supports creating wheels for individual packages and groups of
packages. Building the ``.wheel`` subtarget creates a ``.whl`` file for the
package using the PyPA's `build <https://pypa-build.readthedocs.io/en/stable/>`_
-tool. The location of this file is recorded with `GN metadata
+tool.
+
+The ``.wheel`` subtarget of ``pw_python_package`` records the location of
+the generated wheel with `GN metadata
<https://gn.googlesource.com/gn/+/master/docs/reference.md#var_metadata>`_.
-
-The ``pw_python_wheels`` template creates a collection of wheels from a list of
-``pw_python_package`` targets and their dependencies. It uses GN metadata to
-locate the wheels for all transitive dependencies. This collection can be used
-to deploy packages to different Python environments without requiring the
-original source repository.
-
-.. admonition:: Under construction
-
- Pigweed's wheel building is not yet fully implemented. ``pw_python_wheels``
- currently only supports listing individual ``setup.py`` files. This will be
- updated to automatically collect wheels for all transitive dependencies.
+Wheels for a Python package and its transitive dependencies can be collected
+from the ``pw_python_package_wheels`` key. See
+:ref:`module-pw_build-python-wheels`.
Protocol buffers
^^^^^^^^^^^^^^^^
diff --git a/pw_build/BUILD.gn b/pw_build/BUILD.gn
index c9399fb..7c4be4a 100644
--- a/pw_build/BUILD.gn
+++ b/pw_build/BUILD.gn
@@ -132,6 +132,7 @@
# Requirements for the pw_python_package lint targets.
pw_python_requirements("python_lint") {
requirements = [
+ "build",
"mypy==0.800",
"pylint==2.6.0",
]
diff --git a/pw_build/python.gni b/pw_build/python.gni
index d04d027..2b1ce22 100644
--- a/pw_build/python.gni
+++ b/pw_build/python.gni
@@ -1,4 +1,4 @@
-# Copyright 2020 The Pigweed Authors
+# Copyright 2021 The Pigweed Authors
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
@@ -38,13 +38,11 @@
# - $name.lint.pylint - Runs pylint (if enabled).
# - $name.tests - Runs all tests for this package.
# - $name.install - Installs the package in a venv.
-# - $name.wheel - Builds a Python wheel for the package. (Not implemented.)
+# - $name.wheel - Builds a Python wheel for the package.
#
# All Python packages are instantiated with the default toolchain, regardless of
# the current toolchain.
#
-# TODO(pwbug/239): Implement wheel building.
-#
# Args:
# setup: List of setup file paths (setup.py or pyproject.toml & setup.cfg),
# which must all be in the same directory.
@@ -68,6 +66,7 @@
# provided, mypy's default configuration file search is used. mypy is
# executed from the package's setup directory, so mypy.ini files in that
# directory will take precedence over others.
+#
template("pw_python_package") {
# The Python targets are always instantiated in the default toolchain. Use
# fully qualified labels so that the toolchain is not lost.
@@ -340,18 +339,27 @@
}
}
- # TODO(pwbug/239): Add support for building groups of wheels. The code below
- # is incomplete and untested.
+ # Builds a Python wheel for this package. Records the output directory
+ # in the pw_python_package_wheels metadata key.
pw_python_action("$target_name.wheel") {
- script = "$dir_pw_build/py/pw_build/python_wheels.py"
+ metadata = {
+ pw_python_package_wheels = [ "$target_out_dir/$target_name" ]
+ }
+
+ module = "build"
args = [
- "--out_dir",
- rebase_path(target_out_dir),
- ]
- args += rebase_path(_all_py_files)
+ rebase_path(_setup_dir),
+ "--wheel",
+ "--no-isolation",
+ "--outdir",
+ ] + rebase_path(metadata.pw_python_package_wheels)
- deps = [ ":${invoker.target_name}.install" ]
+ deps = [ ":${invoker.target_name}" ]
+ foreach(dep, _python_deps) {
+ deps += [ string_replace(dep, "(", ".wheel(") ]
+ }
+
stamp = true
}
} else {
@@ -467,6 +475,9 @@
inputs = [ invoker.mypy_ini ]
}
}
+
+ # Generated packages with linting disabled never need the whole file list.
+ not_needed([ "_all_py_files" ])
}
} else {
# Create groups with the public target names ($target_name, $target_name.lint,
diff --git a/pw_build/python.rst b/pw_build/python.rst
index 7157746..520b333 100644
--- a/pw_build/python.rst
+++ b/pw_build/python.rst
@@ -75,6 +75,30 @@
pylintrc = "$dir_pigweed/.pylintrc"
}
+
+.. _module-pw_build-python-wheels:
+
+Collecting Python wheels for distribution
+-----------------------------------------
+The ``.wheel`` subtarget generates a wheel (``.whl``) for the Python package.
+Wheels for a package and its transitive dependencies can be collected by
+traversing the ``pw_python_package_wheels`` `GN metadata
+<https://gn.googlesource.com/gn/+/master/docs/reference.md#var_metadata>`_ key,
+which lists the output directory for each wheel.
+
+The ``pw_mirror_tree`` template can be used to collect wheels in an output
+directory:
+
+.. code-block::
+
+ import("$dir_pw_build/mirror_tree.gni")
+
+ pw_mirror_tree("my_wheels") {
+ path_data_keys = [ "pw_python_package_wheels" ]
+ deps = [ ":python_packages" ]
+ directory = "$root_out_dir/the_wheels"
+ }
+
pw_python_script
================
A ``pw_python_script`` represents a set of standalone Python scripts and/or
@@ -88,13 +112,6 @@
These targets do not add any files. Their subtargets simply forward to those of
their dependencies.
-pw_python_wheels
-================
-Builds and collects Python wheels for one or more ``pw_python_package`` targets.
-A package's ``.wheel`` subtarget builds the wheel for just that package.
-``pw_python_package`` collects wheels from all of its transitive dependencies
-and collects them in a specified directory.
-
pw_python_requirements
======================
Represents a set of local and PyPI requirements, with no associated source