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
29 changes: 28 additions & 1 deletion uv/private/extension.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -265,10 +265,20 @@ def _parse_annotations(module_ctx, hub_specs, venv_specs):

Dep = TypedDict({"name": str})
record(
per_package=Dict[str, List[Dep]],
per_package=Dict[str, Annotation],
default_build_deps=List[Dep],
)

The annotations file is structured as follows:

```
version = 0.0.0
[[package]]
name = "foo"
native = true
build-dependencies = [ {name = "bar"} ]
```

Args:
module_ctx (module_ctx): The Bazel module context
hub_specs (dict): The previously parsed hub specs
Expand Down Expand Up @@ -551,6 +561,22 @@ def _sbuild_repos(_module_ctx, lock_specs, annotation_specs, override_specs):
for it in build_deps + venv_anns.default_build_deps
}

# FIXME: This is placeholder code. We need to decide if the
# sdist we're building contains native extensions. For now we're
# relying on annotations to do that. It would be better for the
# sdist_build repo rule to inspect the sdist's contents and
# search for well known file types such as `pyx` and `cxx`. That
# would also make it easier to support for instance Rust
# extensions.
is_native = (
# Cythonized code
"cython" in build_deps or
# Mypyc code
"mypy" in build_deps or
# User has annotated the package as using C extensions or such
venv_anns.per_package.get(package["name"], {}).get("native", False)
)

# print("Creating sdist repo", name)
sdist_build(
name = name,
Expand All @@ -560,6 +586,7 @@ def _sbuild_repos(_module_ctx, lock_specs, annotation_specs, override_specs):
"@" + _venv_target(hub_name, venv_name, package["name"])
for package in build_deps.values()
],
is_native = is_native,
)

def _whl_install_repo_name(hub, venv, package):
Expand Down
17 changes: 14 additions & 3 deletions uv/private/sdist_build/build_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import shutil
import sys
from os import getenv, listdir, path
from subprocess import call
from subprocess import check_call

# Under Bazel, the source dir of a sdist to build is immutable. `build` and
# other tools however are constitutionally incapable of not writing to the
Expand All @@ -19,6 +19,8 @@
PARSER = ArgumentParser()
PARSER.add_argument("srcdir")
PARSER.add_argument("outdir")
PARSER.add_argument("--validate-anyarch", action="store_true")
PARSER.add_argument("--sandbox", action="store_true")
opts, args = PARSER.parse_known_args()

t = getenv("TMPDIR") # Provided by Bazel
Expand All @@ -29,12 +31,21 @@

outdir = path.abspath(opts.outdir)

call([
check_call([
sys.executable,
"-m", "build",
"--wheel",
"--no-isolation",
"--outdir", outdir,
], cwd=t)

print(listdir(outdir), file=sys.stderr)
inventory = listdir(outdir)
print(inventory, file=sys.stderr)

if len(inventory) > 1:
print("Error: Built more than one wheel!", file=sys.stderr)
exit(1)

if opts.validate_anyarch and not inventory[0].endswith("-none-any.whl"):
print("Error: Target was anyarch but built a none-any wheel!")
exit(1)
9 changes: 7 additions & 2 deletions uv/private/sdist_build/repository.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -8,29 +8,34 @@ sibling `rule.bzl` file for the implementation of `sdist_build`.

def _sdist_build_impl(repository_ctx):
repository_ctx.file("BUILD.bazel", content = """
load("@aspect_rules_py//uv/private/sdist_build:rule.bzl", "sdist_build")
load("@aspect_rules_py//uv/private/sdist_build:rule.bzl", "{rule}")
load("@aspect_rules_py//py/unstable:defs.bzl", "py_venv")

py_venv(
name = "build_venv",
deps = {deps},
)

sdist_build(
{rule}(
name = "whl",
src = "{src}",
venv = ":build_venv",
args = {args},
visibility = ["//visibility:public"],
)
""".format(
src = repository_ctx.attr.src,
deps = repr([str(it) for it in repository_ctx.attr.deps]),
# FIXME: This should probably be inferred by looking at the inventory of the sdist
rule = "sdist_build" if not repository_ctx.attr.is_native else "sdist_native_build",
args = repr(["--validate-anyarch"] if not repository_ctx.attr.is_native else []),
))

sdist_build = repository_rule(
implementation = _sdist_build_impl,
attrs = {
"src": attr.label(),
"deps": attr.label_list(),
"is_native": attr.bool(),
},
)
32 changes: 32 additions & 0 deletions uv/private/sdist_build/rule.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ load("//py/private/py_venv:types.bzl", "VirtualenvInfo")

# buildifier: disable=bzl-visibility
load("//py/private/toolchain:types.bzl", "PY_TOOLCHAIN")
load("//uv/private:transitions.bzl", "transition_to_target")

TAR_TOOLCHAIN = "@tar.bzl//tar/toolchain:type"
# UV_TOOLCHAIN = "@multitool//tools/uv:toolchain_type"
Expand Down Expand Up @@ -59,6 +60,7 @@ def _sdist_build(ctx):
executable = venv[VirtualenvInfo].home.path + "/bin/python3",
arguments = [
ctx.file._helper.path,
] + ctx.attr.args + [
unpacked_sdist.path,
wheel_dir.path,
],
Expand Down Expand Up @@ -87,12 +89,42 @@ sdist_build = rule(
Consumes a sdist artifact and performs a build of that artifact with the
specified Python dependencies under the configured Python toochain.

""",
attrs = {
"src": attr.label(),
"venv": attr.label(),
"args": attr.string_list(),
"_helper": attr.label(allow_single_file = True, default = Label(":build_helper.py")),
},
toolchains = [
# TODO: Py toolchain needs to be in the `host` configuration, not the
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why host? That feels like almost always the wrong thing?

# `exec` configuration. May need to split toolchains or use a different
# one here. Ditto for the other tools.
PY_TOOLCHAIN,
TAR_TOOLCHAIN,
# UV_TOOLCHAIN,
# FIXME: Add in a cc toolchain here
],
)

sdist_native_build = rule(
implementation = _sdist_build,
doc = """Sdist to whl build rule _with native extensions_.

Consumes a sdist artifact and performs a build of that artifact with the
specified Python dependencies under the configured Python toochain.

Forces the exec platform to match the target platform so that we can safely
build packages with native code.

""",
attrs = {
"src": attr.label(doc = ""),
"venv": attr.label(doc = ""),
"args": attr.string_list(),
"_helper": attr.label(allow_single_file = True, default = Label(":build_helper.py")),
},
cfg = transition_to_target,
toolchains = [
# TODO: Py toolchain needs to be in the `host` configuration, not the
# `exec` configuration. May need to split toolchains or use a different
Expand Down
17 changes: 17 additions & 0 deletions uv/private/transitions.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Dirty awful hack thanks Ed.
# https://github.com/bazel-contrib/rules_oci/pull/590/files

def _transition_to_target_impl(settings, attr):
return {
# String conversion is needed to prevent a crash with Bazel 6.x.
"//command_line_option:extra_execution_platforms": [
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

uhhh this is super sketch. Is it possible to use exec_compatible_with constraints to guide it to a platform matching the target instead?

str(platform)
for platform in settings["//command_line_option:platforms"]
],
}

transition_to_target = transition(
implementation = _transition_to_target_impl,
inputs = ["//command_line_option:platforms"],
outputs = ["//command_line_option:extra_execution_platforms"],
)
4 changes: 2 additions & 2 deletions uv/private/whl_install/rule.bzl
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
""
""
"""
"""

load("@rules_python//python:defs.bzl", "PyInfo")
load("//py/private/toolchain:types.bzl", "PY_TOOLCHAIN", "UNPACK_TOOLCHAIN")
Expand Down