Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
23 changes: 15 additions & 8 deletions python/private/cc/py_extension_macro.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ load("@rules_cc//cc:cc_library.bzl", "cc_library")
load("@rules_cc//cc:cc_shared_library.bzl", "cc_shared_library")
load("//python/private:common_labels.bzl", "labels")
load("//python/private:util.bzl", "add_tag", "copy_propagating_kwargs")
load(":py_extension_rule.bzl", "py_extension_wrapper")
load(":py_extension_rule.bzl", "py_extension_libs", "py_extension_wrapper")

_EMPTY_CANONICAL_TARGET = str(Label("//python/private/cc:empty"))

Expand All @@ -18,9 +18,6 @@ _PY_CC_HEADERS_ALIAS_CANONICAL_TARGET = str(Label(_PY_CC_HEADERS_ALIAS_BASE_TARG
_PY_CC_LIBS_ALIAS_BASE_TARGET = "//python/private/cc:current_py_cc_libs_private_alias"
_PY_CC_LIBS_ALIAS_CANONICAL_TARGET = str(Label(_PY_CC_LIBS_ALIAS_BASE_TARGET))

_PY_CC_LIBS_ACTUAL_BASE_TARGET = "//python/cc:current_py_cc_libs"
_PY_CC_LIBS_ACTUAL_CANONICAL_TARGET = str(Label(_PY_CC_LIBS_ACTUAL_BASE_TARGET))

def py_extension(
name,
srcs = None,
Expand Down Expand Up @@ -148,22 +145,32 @@ def py_extension(
"//conditions:default": [],
})

win_libs_name = "_" + name + "_win_libs"

# On Windows, create a private target using toolchain resolution to extract .lib files
# from CcInfo in py_cc_toolchain because system_provided=True in cc_import leaves DefaultInfo empty.
py_extension_libs(
name = win_libs_name,
tags = ["manual"],
visibility = ["//visibility:private"],
)

# Windows-specific CPython linking requirements:
# 1. Windows requires .lib files when linking, so they must be added to deps.
# 2. CPython import libraries (python3xx.lib) are declared with system_provided = True
# in cc_import, suppressing automatic propagation of the .lib file path to link.exe.
# We explicitly pass $(locations ...) to provide the path of the CPython import library to MSVC link.exe.
# 3. We pass current_py_cc_libs as an additional linker input to ensure the .lib file is available to the link action.
# We explicitly pass $(locations ...) from win_libs_name to provide the path to MSVC link.exe.
# 3. We pass win_libs_name as an additional linker input to ensure the .lib file is available to the link action.
deps = deps + select({
labels.PLATFORMS_OS_WINDOWS: [_PY_CC_LIBS_ALIAS_CANONICAL_TARGET],
"//conditions:default": [],
})
user_link_flags = user_link_flags + select({
labels.PLATFORMS_OS_WINDOWS: ["$(locations " + _PY_CC_LIBS_ACTUAL_BASE_TARGET + ")"],
labels.PLATFORMS_OS_WINDOWS: ["$(locations :" + win_libs_name + ")"],
"//conditions:default": [],
})
additional_linker_inputs = additional_linker_inputs + select({
labels.PLATFORMS_OS_WINDOWS: [_PY_CC_LIBS_ACTUAL_CANONICAL_TARGET],
labels.PLATFORMS_OS_WINDOWS: [":" + win_libs_name],
"//conditions:default": [],
})

Expand Down
24 changes: 24 additions & 0 deletions python/private/cc/py_extension_rule.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -133,3 +133,27 @@ def _get_platform(ctx):
),
)
return py_cc_toolchain.platform_tag

def _py_extension_libs_impl(ctx):
py_toolchain = ctx.toolchains[PY_CC_TOOLCHAIN_TYPE]
py_cc_toolchain = py_toolchain.py_cc_toolchain
cc_info = py_cc_toolchain.libs.providers_map["CcInfo"]
files = []
for input in cc_info.linking_context.linker_inputs.to_list():
for lib in input.libraries:
if lib.interface_library:
files.append(lib.interface_library)
elif lib.static_library:
files.append(lib.static_library)
elif lib.dynamic_library:
files.append(lib.dynamic_library)
link_files = [f for f in files if not f.path.endswith(".dll")]
return [DefaultInfo(files = depset(link_files))]

py_extension_libs = rule(
implementation = _py_extension_libs_impl,
toolchains = [PY_CC_TOOLCHAIN_TYPE],
doc = """\
Private internal helper rule for extracting Windows C/C++ library files from toolchain.
""",
)
34 changes: 1 addition & 33 deletions python/private/current_py_cc_libs.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -18,39 +18,7 @@ load("@rules_cc//cc/common:cc_info.bzl", "CcInfo")

def _current_py_cc_libs_impl(ctx):
py_cc_toolchain = ctx.toolchains["//python/cc:toolchain_type"].py_cc_toolchain
providers = [p for p in py_cc_toolchain.libs.providers_map.values() if not hasattr(p, "data_runfiles")]
default_runfiles = None
data_runfiles = None
files = []
for p in py_cc_toolchain.libs.providers_map.values():
if hasattr(p, "data_runfiles"):
default_runfiles = p.default_runfiles
data_runfiles = p.data_runfiles

cc_infos = [p for p in py_cc_toolchain.libs.providers_map.values() if hasattr(p, "linking_context")]
if cc_infos:
cc_info = cc_infos[0]
for input in cc_info.linking_context.linker_inputs.to_list():
for lib in input.libraries:
if lib.static_library:
files.append(lib.static_library)
if lib.interface_library:
files.append(lib.interface_library)
elif lib.dynamic_library:
files.append(lib.dynamic_library)

# On Windows MSVC, user_link_flags passes $(locations @rules_python//python/cc:current_py_cc_libs)
# to link.exe. MSVC link.exe accepts import libraries (.lib) but fails with
# LNK1107 if passed raw DLL binaries (.dll). We filter out .dll files so
# DefaultInfo.files only contains linkable library files (.lib / .a).
link_files = [f for f in files if not f.path.endswith(".dll")]

providers.append(DefaultInfo(
files = depset(link_files),
default_runfiles = default_runfiles,
data_runfiles = data_runfiles,
))
return providers
return py_cc_toolchain.libs.providers_map.values()

current_py_cc_libs = rule(
implementation = _current_py_cc_libs_impl,
Expand Down