Use your Poetry or PDM or UV lock files with Bazel and enabling cross-platform builds.
- A single lock file for all target platforms, thanks to Poetry, PDM and UV
- Builds that happen in build actions, not during WORKSPACE initialization
- Standard Bazel
http_filerules used for fetching dependencies.pipis not a build-time dependency.
Notice: UV is still experimental
See the examples.
The current Bazel rules for working with Python external dependencies have a couple of issues that make cross-platform usage difficult (see bazel-contrib/rules_python#260):
- they're based on
pipandpip-compilewhich do not generate cross-platform lock files. For example, IPython depends onappnopeonly on MacOS. Lock files generated bypip-compilewill differ based on whether they're created on Linux or MacOS. Thepip-compilesolution to this problem is to generate lock files for different systems, on different systems. - They use
pip installduring theWORKSPACEphase to fetch and possibly build packages (including native libraries).WORKSPACEoperations lack many of the things that Bazel's build actions provide such as sandboxing and remote execution.
A pip install operation can be roughly broken down into these parts:
- determine the environment (OS and Python version/implementation)
- resolve the dependencies of the package to install, some of which may be platform-specific (optionally constrained by a pre-compiled lock file)
- figure out which files to download - either pre-built wheels matching the current platform or sdists to build locally
- download sdists and wheels
- build and install sdists; install wheels
rules_pycross attempts to deconstruct this operation into its constituent parts and glue them together with Bazel:
pycross_target_environmentis used to specify target environments ahead of time provided with ABI, platform, and implementation parameters (similar to pip's--abi,--platform, and--implementationflags). These environments are selected using Bazel's own platform/constraint system.pycross_lock_filegenerates a "lock".bzlfile from an inputpoetry.lock. This.bzlfile contains a mix ofhttp_filerepositories andpycross_*targets.pycross_wheel_buildbuildssdist.tar.gzarchives into Python wheels. This is a build action, not aWORKSPACEoperation.pycross_wheel_library"installs" (extracts) a Python wheel - either downloaded or built from an sdist - and provides it as apy_library.
See the generated docs.
rules_pycross is compatible with rules_python_gazelle_plugin, a plugin for Gazelle
that generates BUILD files content for rules_python rules, but requires additional configuration.
rules_python_gazelle_plugin is originally designed for rules_python rules that uses custom name normalization, whereas
rules_pycross uses the Python name normalization.
To switch name normalization, use the following Gazelle directives:
# gazelle:python_label_convention :$distribution_name$
# gazelle:python_label_normalization pep503
Other than these options, the configuration is identical to a setup with rules_python.
Read more here.
Example BUILD.bazel
load("@gazelle//:def.bzl", "DEFAULT_LANGUAGES", "gazelle", "gazelle_binary")
load("@pip//:requirements.bzl", "all_whl_requirements")
load("@rules_python_gazelle_plugin//manifest:defs.bzl", "gazelle_python_manifest")
load("@rules_python_gazelle_plugin//modules_mapping:def.bzl", "modules_mapping")
# gazelle:python_root
# gazelle:python_label_convention :$distribution_name$
# gazelle:python_label_normalization pep503
gazelle_binary(
name = "gazelle_bin",
languages = DEFAULT_LANGUAGES + [
"@rules_python_gazelle_plugin//python",
],
)
gazelle(
name = "gazelle.update",
gazelle = ":gazelle_bin",
)
gazelle(
name = "gazelle.check",
args = ["-mode=diff"],
gazelle = ":gazelle_bin",
)
modules_mapping(
name = "gazelle.metadata",
tags = ["manual"],
wheels = all_whl_requirements,
)
gazelle_python_manifest(
name = "gazelle.mapping",
modules_mapping = ":gazelle.metadata",
pip_repository_name = "pip",
tags = ["manual"],
)
> bazel run //:gazelle.update # Update gazelle_python.yaml used by Gazelle
> bazel run //:gazelle.check # Show changes needed to build scripts per Gazelle
> bazel run //:gazelle.update # Apply changes needed to build scripts per Gazelle