| :::{default-domain} bzl |
| ::: |
| |
| # How to expose headers from a PyPI package |
| |
| When you depend on a PyPI package that includes C headers (like `numpy`), you |
| need to make those headers available to your `cc_library` or |
| `cc_binary` targets. |
| |
| The recommended way to do this is to inject a `BUILD.bazel` file into the |
| external repository for the package. This `BUILD` file will create |
| a `cc_library` target that exposes the header files. |
| |
| First, create a `.bzl` file that has the extra logic we'll inject. Putting it |
| in a separate bzl file avoids having to redownload and extract the whl file |
| when our logic changes. |
| |
| ```bzl |
| |
| # pypi_extra_targets.bzl |
| load("@rules_cc//cc:cc_library.bzl", "cc_library") |
| |
| def extra_numpy_targets(): |
| cc_library( |
| name = "headers", |
| hdrs = glob(["**/*.h"]), |
| visibility = ["//visibility:public"], |
| ) |
| ``` |
| |
| ## Bzlmod setup |
| |
| In your `MODULE.bazel` file, use the `build_file_content` attribute of |
| `pip.parse` to inject the `BUILD` file content for the `numpy` package. |
| |
| ```bazel |
| # MODULE.bazel |
| load("@rules_python//python/extensions:pip.bzl", "parse", "whl_mods") |
| pip = use_extension("@rules_python//python/extensions:pip.bzl", "pip") |
| whl_mods = use_extension("@rules_python//python/extensions:pip.bzl", "whl_mods") |
| |
| |
| # Define a specific modification for a wheel |
| whl_mods( |
| hub_name = "pypi_mods", |
| whl_name = "numpy-1.0.0-py3-none-any.whl", # The exact wheel filename |
| additive_build_content = """ |
| load("@//:pypi_extra_targets.bzl", "numpy_hdrs") |
| |
| extra_numpy_targets() |
| """, |
| ) |
| pip.parse( |
| hub_name = "pypi", |
| wheel_name = "numpy", |
| requirements_lock = "//:requirements.txt", |
| whl_modifications = { |
| "@pypi_mods//:numpy.json": "numpy", |
| }, |
| extra_hub_aliases = { |
| "numpy": ["headers"], |
| } |
| ) |
| ``` |
| |
| ## WORKSPACE setup |
| |
| In your `WORKSPACE` file, use the `annotations` attribute of `pip_parse` to |
| inject additional `BUILD` file content, then use `extra_hub_targets` to expose |
| that target in the `@pypi` hub repo. |
| |
| The {obj}`package_annotation` helper can be used to construct the value for the |
| `annotations` attribute. |
| |
| ```starlark |
| # WORKSPACE |
| load("@rules_python//python:pip.bzl", "package_annotation", "pip_parse") |
| |
| pip_parse( |
| name = "pypi", |
| requirements_lock = "//:requirements.txt", |
| annotations = { |
| "numpy": package_annotation( |
| additive_build_content = """\ |
| load("@//:pypi_extra_targets.bzl", "numpy_hdrs") |
| |
| extra_numpy_targets() |
| """ |
| ), |
| }, |
| extra_hub_targets = { |
| "numpy": ["headers"], |
| }, |
| ) |
| ``` |
| |
| ## Using the headers |
| |
| In your `BUILD.bazel` file, you can now depend on the generated `headers` |
| target. |
| |
| ```bazel |
| # BUILD.bazel |
| cc_library( |
| name = "my_c_extension", |
| srcs = ["my_c_extension.c"], |
| deps = ["@pypi//numpy:headers"], |
| ) |
| ``` |