Bazel support
Description
Bazel is popular. As is CMake, Meson, make, etc.
It would be great to have an official hatch plugin focussing on Bazel (what I care about right now) and maybe these others also (as others care about the others). Solving for one is expected to solve for all.
Use case/motivation
Hatch should have a clear path to including dependencies with their own non-Python-world build systems in a regular Python worktree; primarily for building Python wrapper packages.
This absolves Hatch of including ways of directly providing linker flags and compiler flags and resolving dependencies and packaging, and absolves the ecosystem of having to rewrite-in-Hatch.
Related issues
https://github.com/pypa/hatch/issues/1817 and his whole project https://github.com/eguiraud/hatch-bazel-and-pybind11
Related also: https://github.com/rmorshea/hatch-build-scripts (for executing arbitrary commands and storing their assets for usages in later stages)
Are you willing to submit a PR?
- [x] Yes I am willing to submit a PR!
Code of Conduct
- [x] I agree to follow the Python Software Foundation's Code of Conduct
Hey there! Generally speaking, I'm interested in this. Can you explain what's required in particular?
Hey!
Great that you're interested. Ok well I'll need to deep-dive into hatch. Will probably implement the whole thing with no LLMs or other AI-tools.
But for documentation purposes and in response to your interest, here's a quick run-through of what an implementation is likely required to implement (from three popular LLMs):
| Complexity | Feature | Description |
|---|---|---|
| 🟢 Basic | Bazel Build Invocation | Add a Hatch builder that runs bazel build //target and collects outputs. |
| 🟢 Basic | Pyproject Config Mapping | Allow [tool.hatch.build.bazel] section in pyproject.toml to specify Bazel targets. |
| 🟡 Intermediate | Artifact Packaging | Automatically copy Bazel outputs (e.g., .so, .dll) into the wheel structure. |
| 🟡 Intermediate | Bazel Clean Integration | Map Hatch’s clean command to bazel clean. |
| 🟡 Intermediate | Bazel Test Hook | Run bazel test //... when Hatch’s test lifecycle is invoked. |
| 🟠 Advanced | Dependency Bridging | Sync Python dependencies declared in Hatch with Bazel’s requirements.txt or pip_install. |
| 🟠 Advanced | Multi-Target Builds | Support multiple Bazel targets per Hatch project, merging outputs into one wheel. |
| 🔴 Expert | Cross-Platform Support | Handle Bazel builds across Linux, macOS, and Windows with platform-specific rules. |
| 🔴 Expert | Incremental Build Caching | Integrate Bazel’s cache with Hatch’s build cache for faster rebuilds. |
| 🔴 Expert | Hybrid Testing | Unify pytest with Bazel test results into a single Hatch report. |
| 🚀 Cutting Edge | Full Bazel Workspace Integration | Allow Hatch to manage Bazel workspaces directly, bridging Python + native code seamlessly. |
| Complexity | Feature | Implementation Scope | Key Technical Challenges |
|---|---|---|---|
| Low | Basic Bazel BUILD file generation | Generate minimal BUILD files for pure Python packages | - Parsing pyproject.toml metadata - Basic Python library rule generation |
| Low-Medium | Detect and use existing Bazel configurations | Read and integrate pre-existing Bazel BUILD files | - Metadata compatibility - Configuration file parsing |
| Medium | Native extension compilation support | Add rules for compiling C/C++/Rust extensions | - Cross-compilation support - Toolchain detection - Dependency resolution |
| Medium-High | Dependency mapping | Map Python package dependencies to Bazel targets | - Complex dependency graph parsing - Version constraint handling - Multiple repository support |
| High | Cross-platform build configuration | Generate platform-specific Bazel build configurations | - OS-specific compilation flags - Multi-architecture support - Conditional compilation rules |
| High-Expert | Advanced toolchain integration | Support custom toolchains and complex build scenarios | - Hermetic build environments - Remote execution support - Advanced caching mechanisms |
| # | Feature | Complexity | Description | Prompt Concept to use |
|---|---|---|---|---|
| 1 | Plugin Skeleton | Low | distinct Python package structure required to register a Hatch plugin entry point. | "Generate the directory structure and pyproject.toml for a new package named hatch-bazel, including the hatch entry point registration pointing to a blank class." |
| 2 | The Build Hook Interface | Low | The Python class inheriting from BuildHookInterface with the stubs for initialize and finalize. |
"Write a Python class BazelBuildHook that inherits from hatchling...BuildHookInterface. Add a logger and print a message during the initialize phase to verify it runs." |
| 3 | Configuration Parsing | Low | Reading the [tool.hatch.build.hooks.bazel] section from the user's pyproject.toml. |
"Implement logic in the initialize method to read a targets list and a basel_bin_path string from the plugin configuration dictionary, supplying defaults if missing." |
| 4 | Subprocess Execution | Medium | Securely invoking the Bazel binary to build the requested targets. | "Write the code to construct a subprocess.run command that calls bazel build on the configured targets. Ensure it captures stdout/stderr and raises an error on failure." |
| 5 | Symlink Resolution | Medium | Bazel produces symlinks in bazel-bin. These must be resolved to real paths to be zipped into a wheel. |
"Create a helper function that takes a Bazel target string (e.g., //src:lib), locates the resulting file in the bazel-bin directory, and resolves the symlink to the absolute real path." |
| 6 | Artifact Injection | High | Telling Hatch to include the compiled binary in the final wheel using force_include. |
"Update the hook to map the resolved binary paths from the previous step into the build_data['force_include'] dictionary, placing them inside the specific project package folder." |
| 7 | Wheel Metadata (Pure vs Binary) | Medium | Ensuring the resulting wheel is marked as binary (platform-specific) rather than generic Python. | "Write code to modify build_data['pure_python'] to False and ensure the wheel is tagged correctly so pip doesn't treat it as a cross-platform Any/Any wheel." |
| 8 | Cleanup Hook | Low | Removing temporary artifacts or bazel-bin hints after the build completes. |
"Implement the finalize method in the class to perform executed cleanup, such as creating a clean command or verifying the state of the workspace after the build." |
| 9 | Advanced Argument Passing | Medium | Allowing the user to pass extra flags (e.g., --compilation_mode=opt) via Pyproject or CLI. |
"Extend the configuration parser to accept startup_args (before 'build') and build_args (after 'build') and inject them into the subprocess call." |
| 10 | Protos & Generated Sources | High | Handling generated .py files (like protobufs) that Bazel creates but need to be treated as source code, not binaries. |
"Add logic to detect if a Bazel output is a .py file. If so, copy it into the local source tree temporarily instead of using force_include, so Hatchling treats it as a standard source file." |
Let me know how accurate/inaccurate this is and whether you want this implemented directly or as a plugin as these^ propose.
I appreciate your willingness to get feedback (I'd like to see that prompt!) but unfortunately I have limited time and it wasn't extremely helpful upon a quick skim. Let me approach this in a different way. As a user without knowledge of any implementation details:
- What would one want out of such an integration?
- What are the practical UX implications of such support?
- What is difficult or impossible to achieve today?
Also, it may not be immediately relevant but it should be known that I have a strong bias against what rules_python does by default (what Google happens to do/require) and I would only ever personally use https://github.com/aspect-build/rules_py
So I think the main thing is that every build system tries to replace all other build systems; like that old xkcd comic. My development philosophy is aggressive [open-source] interoperability.
bazel can replace all other build systems. cmake can replace all other build systems. Makefiles can replace all custom written workflows, including to replace all other build systems.
pip/hatch can replace all other build systems?
Now it'd be annoying to actually change the upstream code that relies on something else in favour of native python toolchain ecosystem; especially whence that codebase lacks Python.
It is well-known at Google that BUILD files (bazel) permeates every language and just about every internal and external codebase. Projects with public facing pyproject.toml exclusive codebases will internally have BUILD files and a copybara process that utilises them.
Bazel has many advantages, like: concurrency; caching; lingua franca-ness, &etc.
But it's not native. Explicitly.
pip is native. hatch is about as native as you can get; being situated within the pypa org. And hatch works with uv and regular Python (system, pyenv, etc.) also.
So let's use the regular Python setup workflow for both Python and whatever is needed to get Python wrappers working (including third-party .dlls / .sos / .dylibs etc. ; managing language toolchains; configuring linker and build flags; percolating env vars) all from one pyproject.toml interface.
TL;DR I don't want care what build systems people use for Python. Each are theoretically interchangeable; let's make them practically so!