Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
0fefc88
empty impl
rickeylev Sep 23, 2025
02a9dc1
basic attr builder usage
rickeylev Sep 23, 2025
bd16c3c
add stub tests
rickeylev Sep 23, 2025
c1f5a03
vibe coded py_extension impl. looks roughly valid, but untested
rickeylev Sep 23, 2025
b12c295
making more vibe coded progress
rickeylev Sep 24, 2025
e7d21b1
gave up vibe coding. confidence isn't correctness.
rickeylev Sep 24, 2025
959a773
Merge branch 'main' of https://github.com/bazel-contrib/rules_python …
rickeylev Sep 26, 2025
4494b28
try cc_shared_library impl
rickeylev Oct 16, 2025
e8f4792
feat: updates to the py_extension rule for building C extensions (#3851)
rsartor-cmd Jun 26, 2026
9fb7ce7
feat: updates to the py_extension rule for building C extensionsPy ex…
rsartor-cmd Jul 16, 2026
702152f
Merge upstream/main into py-extension and resolve compatibility issues
rickeylev Jul 28, 2026
989ad95
Merge upstream/py-extension into current branch
rickeylev Jul 28, 2026
9730d1d
docs(cc): update py_extension docstring to denote volatile API and us…
rickeylev Jul 29, 2026
7c100a8
refactor: updates to the py_extension rule for building C extensions …
rsartor-cmd Jul 29, 2026
f852df7
build(cc): add missing :flags dep to py_cc_toolchain_rule bzl_library
rickeylev Jul 29, 2026
d770178
Merge remote-tracking branch 'upstream/py-extension' into prepare_py_…
rickeylev Jul 29, 2026
dff329f
build(cc): add missing pep508_env dep to py_cc_toolchain_macro bzl_li…
rickeylev Jul 29, 2026
aa6c26c
Merge remote-tracking branch 'upstream/main' into prepare_py_extensio…
rickeylev Jul 29, 2026
0f9f5fb
build(cc): fix bzl_library deps for py_extension and format test BUIL…
rickeylev Jul 29, 2026
93e61ac
build(cc): fix rules_cc bzl_library target names for WORKSPACE mode
rickeylev Jul 29, 2026
65886db
fix(cc): add default macOS dynamic lookup link flags for py_extension
rickeylev Jul 29, 2026
931c3f2
style(cc): format py_extension_pkg_test.py with ruff
rickeylev Jul 29, 2026
340a42a
build(cc): remove superseded py_extension_bzl target
rickeylev Jul 29, 2026
80527d8
test(cc): skip test_inspect_elf on non-Linux platforms
rickeylev Jul 29, 2026
f61c0a4
test(cc): fix analysis test assertions for cross-platform extension n…
rickeylev Jul 29, 2026
750c882
test(cc): relax musl platform test filename predicate for macOS
rickeylev Jul 29, 2026
c992193
fix(cc): export PyInit module symbol on Windows MSVC
rickeylev Jul 29, 2026
0b614eb
fix(cc): include current_py_cc_libs in cc_shared_library on Windows
rickeylev Jul 29, 2026
33be93e
fix(cc): pass current_py_cc_libs to implicit cc_library impl on Windows
rickeylev Jul 29, 2026
ab7c5d8
fix(cc): default exports_filter on Windows to include external depend…
rickeylev Jul 29, 2026
e154dff
ci: trigger Buildkite test run for exports_filter fix
rickeylev Jul 29, 2026
32edbcc
fix(cc): use native.package_name() in default exports_filter on Windows
rickeylev Jul 29, 2026
7166c9e
fix(cc): specify canonical Label for current_py_cc_libs in exports_fi…
rickeylev Jul 29, 2026
d196be4
fix(cc): include root package in default exports_filter on Windows
rickeylev Jul 29, 2026
c4a6211
fix(cc): specify //python/cc:__subpackages__ in default exports_filte…
rickeylev Jul 29, 2026
9ef63d8
fix(cc): pass current_py_cc_libs via additional_linker_inputs on Windows
rickeylev Jul 29, 2026
2c48b15
fix(cc): include @rules_python//python/cc:current_py_cc_libs in defau…
rickeylev Jul 29, 2026
b87896e
fix(cc): provide library files in DefaultInfo for current_py_cc_libs
rickeylev Jul 29, 2026
a7fcd7b
fix(cc): safely extract CcInfo and filter DefaultInfo in current_py_c…
rickeylev Jul 29, 2026
4b3bb6e
fix(cc): locate CcInfo via linking_context attribute for Bazel 7 comp…
rickeylev Jul 29, 2026
08cb747
ci: trigger fresh CI run for Bazel 7 fix
rickeylev Jul 29, 2026
7db5ef9
fix(cc): preserve runfiles in DefaultInfo returned by current_py_cc_libs
rickeylev Jul 29, 2026
476a018
fix(cc): include original target files in DefaultInfo for current_py_…
rickeylev Jul 29, 2026
26ae553
fix(cc): pass current_py_cc_libs locations to user_link_flags on Windows
rickeylev Jul 29, 2026
0ce2eb2
fix(cc): exclude dynamic libraries when interface library exists in c…
rickeylev Jul 29, 2026
1d40f33
fix(cc): filter out .dll files from DefaultInfo.files in current_py_c…
rickeylev Jul 29, 2026
b06062f
fix(tests): export add_one symbol from add_one_shared on Windows
rickeylev Jul 29, 2026
ee7c533
fix(cc): export PyInit module entry points on Windows
rickeylev Jul 29, 2026
90b0848
fix(tests): add Windows DLL search path resolution in py_extension tests
rickeylev Jul 29, 2026
b1ddb6d
fix(cc): pass /WHOLEARCHIVE on Windows to preserve static library obj…
rickeylev Jul 30, 2026
0574927
fix(cc): set default exports_filter to :all in py_extension_macro
rickeylev Jul 30, 2026
5a462fa
fix(cc): improve Windows platform detection and test DLL path resolution
rickeylev Jul 30, 2026
6635627
fix(tests): support .pyd extension in py_limited_api_tests
rickeylev Jul 30, 2026
cf02503
fix(cc): use cp prefix for Windows abi_tag
rickeylev Jul 30, 2026
c1b8569
style(cc): address review feedback for py_extension and platform helpers
rickeylev Jul 30, 2026
0f6029c
fix(cc): deduplicate headers and libs in py_extension macro
rickeylev Jul 30, 2026
18dfd3a
fix(cc): use private alias targets and narrow exports_filter in py_ex…
rickeylev Jul 30, 2026
6a719ca
style(cc): normalize copts/deps, use private_alias suffix, and add py…
rickeylev Jul 30, 2026
dd510ea
style(cc): update comments and remove copyright in test helper
rickeylev Jul 30, 2026
c8f679a
style(cc): reformat copts comment and rephrase empty target comment
rickeylev Jul 30, 2026
efc3a94
merge: upstream/main into branch
rickeylev Jul 30, 2026
2c3f49c
style(cc): duplicate comment above both _private_alias targets
rickeylev Jul 30, 2026
71df325
refactor(cc): remove user_link_flags for Windows and simplify curren…
rickeylev Jul 30, 2026
54c6e78
Revert "refactor(cc): remove user_link_flags for Windows and simplif…
rickeylev Jul 30, 2026
68d46d3
style(cc): move comments above select entries and remove os:osx alias
rickeylev Jul 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions python/cc/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,9 @@ alias(
deprecation = "Use //python/cc:py_cc_toolchain_info instead",
visibility = ["//visibility:public"],
)

bzl_library(
name = "py_extension",
srcs = ["py_extension.bzl"],
deps = ["//python/private/cc:py_extension_macro"],
)
12 changes: 12 additions & 0 deletions python/cc/py_extension.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
"""Public API for py_extension.

:::{include} /_includes/experimental_api.md
:::
"""

load(
"//python/private/cc:py_extension_macro.bzl",
_py_extension = "py_extension",
)

py_extension = _py_extension
2 changes: 2 additions & 0 deletions python/private/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -506,6 +506,7 @@ bzl_library(
deps = [
":py_cc_toolchain_rule",
":util",
"//python/private/pypi:pep508_env",
],
)

Expand All @@ -514,6 +515,7 @@ bzl_library(
srcs = ["py_cc_toolchain_rule.bzl"],
deps = [
":common_labels",
":flags",
":py_cc_toolchain_info",
":sentinel_impl",
"@bazel_skylib//rules:common_settings",
Expand Down
66 changes: 66 additions & 0 deletions python/private/cc/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
load("@rules_cc//cc:cc_library.bzl", "cc_library")
load("//python/private:visibility.bzl", "NOT_ACTUALLY_PUBLIC")

Expand All @@ -18,3 +19,68 @@ cc_library(
name = "empty",
visibility = NOT_ACTUALLY_PUBLIC,
)

# Private alias to avoid "duplicate dependency label" errors when users
# explicitly pass //python/cc:current_py_cc_headers or //python/cc:current_py_cc_libs
# in the deps attribute (including when deps is a select() expression).
alias(
name = "current_py_cc_headers_private_alias",
actual = "//python/cc:current_py_cc_headers",
visibility = NOT_ACTUALLY_PUBLIC,
)

# Private alias to avoid "duplicate dependency label" errors when users
# explicitly pass //python/cc:current_py_cc_headers or //python/cc:current_py_cc_libs
# in the deps attribute (including when deps is a select() expression).
alias(
name = "current_py_cc_libs_private_alias",
actual = "//python/cc:current_py_cc_libs",
visibility = NOT_ACTUALLY_PUBLIC,
)

bzl_library(
name = "py_extension_macro_bzl",
deps = [
":py_extension_rule_bzl",
"//python/private:util",
"@rules_cc//cc:core_rules",
],
)

bzl_library(
name = "py_extension_rule_bzl",
deps = [
"//python/private:attr_builders",
"//python/private:attributes",
"//python/private:py_info",
"//python/private:rule_builders",
"//python/private:toolchain_types",
"@rules_cc//cc/common",
],
)

bzl_library(
name = "py_extension_macro",
srcs = ["py_extension_macro.bzl"],
deps = [
":py_extension_rule",
"//python/private:util",
"@rules_cc//cc:core_rules",
],
)

bzl_library(
name = "py_extension_rule",
srcs = ["py_extension_rule.bzl"],
deps = [
"//python/private:attr_builders",
"//python/private:attributes",
"//python/private:builders",
"//python/private:py_info",
"//python/private:reexports",
"//python/private:rule_builders",
"//python/private:toolchain_types",
"@bazel_skylib//lib:dicts",
"@rules_cc//cc/common",
],
)
186 changes: 186 additions & 0 deletions python/private/cc/py_extension_macro.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
"""Macro for creating Python extensions.

:::{include} /_includes/experimental_api.md
:::
"""

load("@rules_cc//cc:cc_library.bzl", "cc_library")
load("@rules_cc//cc:cc_shared_library.bzl", "cc_shared_library")
load("//python/private:util.bzl", "add_tag", "copy_propagating_kwargs")
load(":py_extension_rule.bzl", "py_extension_wrapper")

def py_extension(
name,
srcs = None,
hdrs = None,
copts = None,
defines = None,
includes = None,
linkopts = None,
linkshared = None,
linkstatic = None,
deps = None,
dynamic_deps = None,
exports_filter = None,
user_link_flags = None,
visibility = None,
data = None,
**kwargs):
"""Creates a Python extension module.

:::{include} /_includes/experimental_api.md
:::

By default, extensions are created within their workspace package directory
(e.g., `pkg/ext.so`) and imported using standard Python package paths
(e.g., `from pkg import ext`).

To customize import path behavior:
- `imports`: Pass `imports = ["..."]` to append custom search directories to
`sys.path` (matching {attr}`py_library.imports`).
- `module_name`: Pass `module_name = "custom_name"` to override the base
module filename.

Args:
name: {type}`str` Target name.
srcs: {type}`list[Label | str] | None` C/C++ source files to compile
directly for this extension.
hdrs: {type}`list[Label | str] | None` Header files for `srcs`.
copts: {type}`list[str] | None` Compiler flags for `srcs`.
defines: {type}`list[str] | None` Preprocessor defines for `srcs`.
includes: {type}`list[str] | None` Header include search paths passed
to internal `cc_library`.
linkopts: {type}`list[str] | None` Link options passed to internal
`cc_library` and `cc_shared_library`.
linkshared: {type}`bool | None` Deprecated and ignored. Extensions are
always linked dynamically.
linkstatic: {type}`bool | None` The `linkstatic` flag passed to
internal `cc_library`.
deps: {type}`list[Label | str] | None` `cc_library` targets to
statically link into the extension.
dynamic_deps: {type}`list[Label | str] | None` `cc_shared_library`
targets to dynamically link.
exports_filter: {type}`list[str] | None` Filter for exported symbols
passed to `cc_shared_library`.
user_link_flags: {type}`list[str] | None` Additional link flags passed
to `cc_shared_library`.
visibility: {type}`list[Label | str] | None` Target visibility.
data: {type}`list[Label | str] | None` List of files or targets needed
by this extension at runtime.
**kwargs: {type}`dict` Additional arguments passed to the underlying
wrapper rule.
"""
add_tag(kwargs, "@rules_python//python/cc:py_extension")
_ = linkshared # buildifier: disable=unused-variable

csl_deps = []

copts = (copts or []) + [
# -fPIC (Position Independent Code) is required when compiling C/C++ sources into
# dynamic/shared libraries (.so/.dylib/.pyd) so code can be loaded at arbitrary addresses.
"-fPIC",
]

# Private alias targets are appended to avoid "duplicate dependency label" errors
# if a user explicitly passes //python/cc:current_py_cc_headers or //python/cc:current_py_cc_libs
# in their deps attribute (including when deps is a select() expression).
py_cc_headers_and_win_libs = [
"//python/private/cc:current_py_cc_headers_private_alias",
] + select({
"@platforms//os:windows": ["//python/private/cc:current_py_cc_libs_private_alias"],
"//conditions:default": [],
})
deps = (deps or []) + py_cc_headers_and_win_libs

# 1. If srcs or hdrs are specified, create an implicit cc_library for them
if srcs or hdrs:
impl_lib_name = "_" + name + "_impl"
impl_lib_kwargs = copy_propagating_kwargs(kwargs)
if includes:
impl_lib_kwargs["includes"] = includes
if linkopts:
impl_lib_kwargs["linkopts"] = linkopts
if linkstatic != None:
impl_lib_kwargs["linkstatic"] = linkstatic
cc_library(
name = impl_lib_name,
srcs = srcs,
hdrs = hdrs,
copts = copts,
defines = defines,
deps = deps,
visibility = ["//visibility:private"],
**impl_lib_kwargs
)
csl_deps.append(":" + impl_lib_name)

if csl_deps:
final_csl_deps = csl_deps
elif deps:
final_csl_deps = deps
else:
# cc_shared_library requires a dependency, so use an empty library when none are given.
final_csl_deps = ["//python/private/cc:empty"]

# 4. Create the underlying cc_shared_library
csl_name = "_" + name + "_csl"
csl_kwargs = copy_propagating_kwargs(kwargs)

# exports_filter specifies which target dependencies should have their exported
# symbols exposed by cc_shared_library. On Windows MSVC, cc_shared_library uses
# exports_filter to inspect the .obj files of matching targets and generate a .def
# file containing all __declspec(dllexport) symbols (such as PyInit_<name>).
# Defaulting to final_csl_deps ensures only the targets in this extension (e.g. _impl)
# are inspected, rather than all targets in the package. Note: current_py_cc_libs
# provides an import library (.lib), not object files (.obj), so MSVC link.exe
# does not re-export CPython runtime symbols.
csl_kwargs["exports_filter"] = exports_filter if exports_filter != None else final_csl_deps

effective_user_link_flags = (user_link_flags or linkopts or []) + select({
# On macOS, Apple's ld64 linker requires '-undefined dynamic_lookup' so CPython
# C-API symbols (e.g. PyModule_Create) remain unresolved at link time and are
# dynamically resolved at runtime when CPython loads the shared library (.so).
"@platforms//os:macos": ["-undefined", "dynamic_lookup"],
# On Windows, link.exe receives the CPython import library (.lib) passed via
# current_py_cc_libs.
"@platforms//os:windows": ["$(locations @rules_python//python/cc:current_py_cc_libs)"],
"//conditions:default": [],
})
csl_kwargs["user_link_flags"] = effective_user_link_flags

csl_additional_linker_inputs = select({
"@platforms//os:windows": ["@rules_python//python/cc:current_py_cc_libs"],
"//conditions:default": [],
})

cc_shared_library(
name = csl_name,
deps = final_csl_deps,
additional_linker_inputs = csl_additional_linker_inputs,
dynamic_deps = dynamic_deps,
visibility = ["//visibility:private"],
**csl_kwargs
)

# 5. Select default libc constraint if not provided
if "libc" not in kwargs:
kwargs["libc"] = select({
"@rules_python//python/config_settings:_is_py_linux_libc_glibc": "glibc",
"@rules_python//python/config_settings:_is_py_linux_libc_musl": "musl",
"//conditions:default": "glibc",
})

if data != None:
kwargs["data"] = data

# 6. Filter out C++ specific compilation/linking attributes before invoking wrapper rule
for cc_attr in ("includes", "linkopts", "linkshared", "linkstatic", "features"):
kwargs.pop(cc_attr, None)

# 7. Wrap with py_extension_wrapper for PEP 3149 naming & PyInfo
py_extension_wrapper(
name = name,
src = ":" + csl_name,
visibility = visibility,
**kwargs
)
Loading
Loading