Skip to content
Open
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
2 changes: 2 additions & 0 deletions e2e/cases/uv-sdist-native-build/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

load("@aspect_rules_py//py:defs.bzl", "py_test")

exports_files(["require_cxx_driver.patch"])

py_test(
name = "test",
srcs = ["test_geohash.py"],
Expand Down
11 changes: 11 additions & 0 deletions e2e/cases/uv-sdist-native-build/require_cxx_driver.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
diff --git a/src/geohash.cpp b/src/geohash.cpp
--- a/src/geohash.cpp
+++ b/src/geohash.cpp
@@ -1,0 +1,7 @@
Comment thread
tamird marked this conversation as resolved.
+#include <string>
+
+extern "C" const char *rules_py_cxx_runtime_probe(void) {
+ static const std::string value = "rules_py";
+ return value.c_str();
+}
+
2 changes: 2 additions & 0 deletions e2e/cases/uv-sdist-native-build/setup.MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ uv.project(
uv.override_package(
name = "python-geohash",
lock = "//cases/uv-sdist-native-build:uv.lock",
pre_build_patch_strip = 1,
pre_build_patches = ["//cases/uv-sdist-native-build:require_cxx_driver.patch"],
resource_set = "mem_4g",
)

Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 15 additions & 1 deletion uv/private/pep517_whl/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@ load("@tar.bzl//tar:mtree.bzl", "mtree_mutate", "mtree_spec")
load("@tar.bzl//tar:tar.bzl", "tar_rule")
load("//py:defs.bzl", "py_binary", "py_library", "py_test")
load(":rule.bzl", "pep517_native_whl", "pep517_whl")
load(":test.bzl", "hostile_python_env_target", "pep517_native_whl_toolchain_env_test")
load(
":test.bzl",
"compiler_driver_test_suite",
"hostile_python_env_target",
"pep517_native_whl_toolchain_env_test",
)

package(default_visibility = [
"//uv/private:__subpackages__",
Expand All @@ -21,6 +26,7 @@ bzl_library(
name = "rule",
srcs = ["rule.bzl"],
deps = [
":compiler",
"//py/private/toolchain:types",
"@bazel_lib//lib:resource_sets",
],
Expand Down Expand Up @@ -75,11 +81,14 @@ bzl_library(
srcs = ["test.bzl"],
visibility = ["//uv:__subpackages__"],
deps = [
":compiler",
":rule",
"@bazel_skylib//lib:unittest",
],
)

compiler_driver_test_suite(name = "compiler_driver_test")

# --- Analysis test fixtures ----------------------------------------------
#
# pep517_native_whl only needs its attrs to type-check at analysis time;
Expand Down Expand Up @@ -164,3 +173,8 @@ pep517_native_whl_toolchain_env_test(
name = "toolchain_env_test",
target_under_test = ":__toolchain_env_fixture",
)

bzl_library(
name = "compiler",
srcs = ["compiler.bzl"],
)
29 changes: 29 additions & 0 deletions uv/private/pep517_whl/compiler.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
"""Strict C/C++ driver matching for private PEP 517 wheel actions."""

_COMPILER_PAIRS = [
("clang", "clang++"),
("gcc", "g++"),
]

def compiler_driver_paths(compiler_path, available_paths):
"""Return selected C/C++ paths for a recognized C compiler, or None."""
basename = compiler_path.split("/")[-1]
for cc_basename, cxx_basename in _COMPILER_PAIRS:
suffix = None
if basename == cc_basename:
suffix = ""
elif basename.startswith(cc_basename + "-"):
version = basename[len(cc_basename) + 1:]
if version and version.isdigit():
suffix = "-" + version
if suffix == None:
continue

cxx_path = cxx_basename + suffix
dirname_index = compiler_path.rfind("/")
if dirname_index != -1:
cxx_path = compiler_path[:dirname_index] + "/" + cxx_path
if cxx_path not in available_paths:
cxx_path = compiler_path
return struct(cxx = cxx_path)
return None
35 changes: 18 additions & 17 deletions uv/private/pep517_whl/rule.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ build backend the sdist declares in its `[build-system]` table.

load("@bazel_lib//lib:resource_sets.bzl", "resource_set", "resource_set_attr")
load("//py/private/toolchain:types.bzl", "NATIVE_BUILD_TOOLCHAIN", "PY_TOOLCHAIN")
load("//uv/private/pep517_whl:compiler.bzl", "compiler_driver_paths")

_CC_TOOLCHAIN_TYPE = Label("@bazel_tools//tools/cpp:toolchain_type")
_TARGET_EXEC_GROUP = "target"
Expand Down Expand Up @@ -83,36 +84,35 @@ def _collect_toolchain_inputs_and_vars(ctx):
known_variables.update(target[platform_common.TemplateVariableInfo].variables)
return extra_inputs, known_variables

_BAZEL_CC_WRAPPER_BASENAMES = ["gcc", "g++", "clang", "clang++"]

def _cc_toolchain_inputs_and_compiler(ctx):
"""Return the target execution group's C++ files and compiler path."""
def _cc_toolchain_inputs_and_compilers(ctx):
"""Return the target execution group's C++ files and C/C++ drivers."""
cc_toolchain = ctx.exec_groups[_TARGET_EXEC_GROUP].toolchains[_CC_TOOLCHAIN_TYPE]
if hasattr(cc_toolchain, "cc_provider_in_toolchain") and hasattr(cc_toolchain, "cc"):
cc_toolchain = cc_toolchain.cc
if not cc_toolchain or not hasattr(cc_toolchain, "all_files"):
return None, None
return None, None, None
files = cc_toolchain.all_files
files_list = files.to_list()
files_by_path = {f.path: f for f in files_list}
compiler_file = None
if hasattr(cc_toolchain, "compiler_executable"):
compiler_basename = cc_toolchain.compiler_executable.split("/")[-1]
for f in files.to_list():
for f in files_list:
if f.basename == compiler_basename:
compiler_file = f
break
if not compiler_file:
for f in files.to_list():
if f.basename in _BAZEL_CC_WRAPPER_BASENAMES:
compiler_file = f
break
if not compiler_file:
for f in files.to_list():
if (f.basename.startswith("clang-") or f.basename.startswith("gcc-") or
f.basename.startswith("g++-")):
for f in files_list:
if compiler_driver_paths(f.path, files_by_path) != None:
compiler_file = f
break

# Preserve the current same-driver behavior when the selected toolchain
# files do not expose a matching same-directory C++ companion.
compiler_path = compiler_file.path if compiler_file else None
return files, compiler_path
driver_paths = compiler_driver_paths(compiler_path, files_by_path) if compiler_path else None
cxx_path = driver_paths.cxx if driver_paths else compiler_path
return files, compiler_path, cxx_path

def _pep517_whl(ctx):
archive = ctx.file.src
Expand Down Expand Up @@ -151,7 +151,7 @@ def _pep517_native_whl(ctx):
env = _common_env(ctx)
extra_inputs, known_variables = _collect_toolchain_inputs_and_vars(ctx)

cc_files, cc_compiler = _cc_toolchain_inputs_and_compiler(ctx)
cc_files, cc_compiler, cxx_compiler = _cc_toolchain_inputs_and_compilers(ctx)
if cc_files:
extra_inputs.append(cc_files)

Expand All @@ -160,7 +160,8 @@ def _pep517_native_whl(ctx):

if cc_compiler:
env["CC"] = cc_compiler
env["CXX"] = cc_compiler
if cxx_compiler:
env["CXX"] = cxx_compiler

ctx.actions.run(
mnemonic = "PySdistNativeBuild",
Expand Down
49 changes: 43 additions & 6 deletions uv/private/pep517_whl/test.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ Inspecting the action at analysis time avoids actually running a PEP 517
build to verify the env wiring.
"""

load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts")
load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts", "unittest")
load("//uv/private/pep517_whl:compiler.bzl", "compiler_driver_paths")

_ACTION_ENV = "//command_line_option:action_env"
_HOST_ENV = [
Expand Down Expand Up @@ -84,14 +85,19 @@ def _toolchain_env_test_impl(ctx):
"${} should resolve to a non-empty toolchain path".format(key),
)

# CC and CXX both derive from cc_toolchain's $(CC) make variable today.
# If we ever switch CXX to a c++ compile driver (e.g. via a custom
# TemplateVariableInfo shim), this assertion can be relaxed.
action_inputs = [f.path for f in build_actions[0].inputs.to_list()]
asserts.true(
env,
action_env.get("CXX") in action_inputs,
"CXX should come from the selected toolchain inputs",
)
cc = action_env.get("CC")
driver_paths = compiler_driver_paths(cc, {path: True for path in action_inputs})
asserts.equals(
env,
action_env.get("CC"),
driver_paths.cxx if driver_paths else cc,
action_env.get("CXX"),
"CC and CXX should point at the same compiler driver",
"CXX should use the declared companion or selected compiler fallback",
)

# JAR is constructed from $(JAVABASE)/bin/jar — sanity-check the suffix.
Expand All @@ -104,3 +110,34 @@ def _toolchain_env_test_impl(ctx):
return analysistest.end(env)

pep517_native_whl_toolchain_env_test = analysistest.make(_toolchain_env_test_impl)

def _compiler_driver_paths_test_impl(ctx):
env = unittest.begin(ctx)
exact = compiler_driver_paths("gcc", {"gcc": True, "g++": True})
asserts.equals(env, "g++", exact.cxx)

versioned = compiler_driver_paths(
"toolchain/bin/clang-22",
{
"toolchain/bin/clang-22": True,
"toolchain/bin/clang++-22": True,
},
)
asserts.equals(env, "toolchain/bin/clang++-22", versioned.cxx)

for near_miss in ["clang-cl", "clang-format", "gcc-ar"]:
asserts.equals(
env,
None,
compiler_driver_paths(near_miss, {near_miss: True}),
"{} should not be treated as a compiler driver".format(near_miss),
)

fallback = compiler_driver_paths("toolchain/bin/gcc", {"toolchain/bin/gcc": True})
asserts.equals(env, "toolchain/bin/gcc", fallback.cxx)
return unittest.end(env)

compiler_driver_paths_test = unittest.make(_compiler_driver_paths_test_impl)

def compiler_driver_test_suite(name):
unittest.suite(name, compiler_driver_paths_test)
5 changes: 2 additions & 3 deletions uv/private/sdist_build/repository.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -259,9 +259,8 @@ def _sdist_build_impl(repository_ctx):
# the env values below are make-variable references resolved at
# action analysis time.
#
# CXX defaults to $(CC) because most clang/gcc-based toolchains use
# a single driver binary for both languages, and meson-python /
# cmake-based backends look for CXX independently of CC.
# CXX starts at $(CC); pep517_native_whl replaces it with a matching
# same-directory clang++ / g++ from the selected toolchain when present.
#
# `extra_toolchains` and `extra_env` augment (do not replace) the
# defaults — set via `uv.override_package(toolchains = [...],
Expand Down
Loading