Skip to content
Closed
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
14 changes: 11 additions & 3 deletions bazel/MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,9 @@ use_repo(reachability_test_ext, "apparent_excluded_repo", "apparent_excluded_tra
http_archive = use_repo_rule("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")

http_archive(
dev_dependency = True,
name = "llvm_source",
build_file_content = """filegroup(name = "all", srcs = glob(["**"]), visibility = ["//visibility:public"])""",
dev_dependency = True,
sha256 = "ad18b70e287954c3d62bc7e0b86e7b7af2adf87bcfce21c15fe717f101d7aace",
strip_prefix = "llvm-project-llvmorg-22.1.8",
url = "https://github.com/llvm/llvm-project/archive/llvmorg-22.1.8.tar.gz",
Expand All @@ -107,6 +107,14 @@ sysroot_ext = use_extension("//sysroot:extensions.bzl", "sysroot_extension", dev
sysroot_ext.setup()
use_repo(sysroot_ext, "sysroot_linux_amd64", "sysroot_linux_arm64")

macos_sysroot_test_repo = use_repo_rule("//sysroot:sysroot.bzl", "macos_sysroot")

macos_sysroot_test_repo(
name = "sysroot_macos_empty",
dev_dependency = True,
version = "test",
)

macos_sysroot_build_ext = use_extension("//sysroot:extensions.bzl", "macos_sysroot_build_extension", dev_dependency = True)
use_repo(macos_sysroot_build_ext, "macos_sdk_pkg", "pkgutil_linux_amd64")

Expand Down Expand Up @@ -192,12 +200,12 @@ llvm.toolchain_root(
)
llvm.sysroot(
name = "llvm_toolchain",
label = "@sysroot_linux_amd64//:sysroot",
label = "@sysroot_linux_amd64//sysroot:sysroot",
targets = ["linux-x86_64"],
)
llvm.sysroot(
name = "llvm_toolchain",
label = "@sysroot_linux_arm64//:sysroot",
label = "@sysroot_linux_arm64//sysroot:sysroot",
targets = ["linux-aarch64"],
)
llvm.extra_exec_compatible_with(
Expand Down
27 changes: 27 additions & 0 deletions bazel/sysroot/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,33 @@ This ensures that even older glibc sysroots can compile code using modern kernel

## Usage in Bazel

### Repository layout

Archives are extracted into the repository's `sysroot/` directory. The public
`:sysroot` target is an alias to `//sysroot:sysroot`, a single source-directory
artifact rather than a filegroup expanding every file. This gives compile actions
one sandbox symlink and one merkle-tree node, avoiding Bazel 9's multi-file sysroot
warning. Files named with `:` and `*.pl` files are removed after extraction because
a directory artifact cannot exclude individual files.

The Linux `:headers`, `:libs`, and `:toolchain_sysroot` targets remain public:
headers and libraries still provide individual files, while `:toolchain_sysroot`
wraps the directory artifact. Their filegroups live inside the `sysroot` package
because Bazel globs cannot cross package boundaries.

Raw file labels now use the `sysroot` package, for example
`@sysroot_linux_amd64//sysroot:usr/include/stdio.h` instead of
`@sysroot_linux_amd64//:usr/include/stdio.h`. Downstream Envoy's
`source/extensions/dynamic_modules/sdk/rust/BUILD` must update its raw `stdio.h`
label accordingly; `@sysroot_linux_amd64//:headers` is unchanged.

Configure LLVM's `llvm.sysroot` with
`@sysroot_linux_amd64//sysroot:sysroot` (and the arm64 equivalent).
LLVM derives its sysroot path from the label's package, not the alias's resolved
artifact, so the direct label is required to get
`external/<canonical-repository-name>/sysroot`. macOS repositories use the same
directory-and-alias layout, including when the SDK is empty.

### Direct `.bzl` load

To use these sysroots via a direct `.bzl` load:
Expand Down
73 changes: 48 additions & 25 deletions bazel/sysroot/sysroot.bzl
Original file line number Diff line number Diff line change
@@ -1,7 +1,22 @@
"""Repository rules for sysroots."""
"""Repository rules exposing sysroots as single source-directory artifacts."""

load("//:versions.bzl", "VERSIONS")

_SYSROOT_ALIAS_BUILD = """\
package(default_visibility = ["//visibility:public"])

alias(
name = "sysroot",
actual = "//sysroot",
)
"""

def _prune_sysroot(ctx):
# Source directories cannot filter inputs, unlike the previous glob.
result = ctx.execute(["find", "sysroot", "-depth", "(", "-name", "*:*", "-o", "-name", "*.pl", ")", "-delete"])
if result.return_code:
fail("Failed to prune sysroot: {}".format(result.stderr))

def _get_platform_arch(ctx):
"""Get the platform architecture for sysroot selection."""
arch = ctx.os.arch
Expand All @@ -13,7 +28,7 @@ def _get_platform_arch(ctx):
fail("Unsupported architecture: {}".format(arch))

def _sysroot_impl(ctx):
"""Implementation for sysroot repository rule."""
"""Download a sysroot with one sandbox symlink and one merkle-tree node."""
arch = ctx.attr.arch or _get_platform_arch(ctx)
glibc_version = ctx.attr.glibc_version
stdcc_version = ctx.attr.stdcc_version
Expand All @@ -37,19 +52,16 @@ def _sysroot_impl(ctx):
url = url,
sha256 = ctx.attr.sha256,
stripPrefix = "",
output = "sysroot",
)
ctx.file("BUILD.bazel", """
_prune_sysroot(ctx)
ctx.file("sysroot/BUILD.bazel", """
package(default_visibility = ["//visibility:public"])

# A single source directory avoids thousands of individual action inputs.
filegroup(
name = "sysroot",
srcs = glob(
["**"],
exclude = [
"**/*:*",
"**/*.pl",
],
),
srcs = ["."],
)

filegroup(
Expand All @@ -72,6 +84,19 @@ filegroup(
exclude = ["**/*:*"],
),
)
""")

# Globs cannot cross the sysroot subpackage boundary.
ctx.file("BUILD.bazel", _SYSROOT_ALIAS_BUILD + """
filegroup(
name = "headers",
srcs = ["//sysroot:headers"],
)

filegroup(
name = "libs",
srcs = ["//sysroot:libs"],
)

filegroup(
name = "toolchain_sysroot",
Expand Down Expand Up @@ -161,35 +186,33 @@ package(default_visibility = ["//visibility:public"])

filegroup(
name = "sysroot",
srcs = glob(
["**"],
exclude = ["**/*:*"],
),
srcs = ["."],
)
"""

def _macos_sysroot_impl(ctx):
"""Implementation for macOS SDK sysroot repository rule."""
if ctx.os.name.startswith("mac"):
# On macOS, no cross-compilation sysroot needed.
ctx.file("BUILD.bazel", _MACOS_SYSROOT_BUILD)
return

"""Expose the macOS SDK as one source directory, including empty SDKs."""
sha256 = ctx.attr.sha256
if sha256:
if sha256 and not ctx.os.name.startswith("mac"):
url = "https://github.com/envoyproxy/toolshed/releases/download/bins-v{version}/sysroot-macos-{arch}.tar.xz".format(
version = ctx.attr.version,
arch = ctx.attr.arch,
)
ctx.download_and_extract(
url,
sha256 = sha256,
output = "sysroot",
)
_prune_sysroot(ctx)
else:
# No hash available yet — create an empty sysroot.
ctx.execute(["mkdir", "-p", "usr/include"])

ctx.file("BUILD.bazel", _MACOS_SYSROOT_BUILD)
# Native macOS or no hash available yet — create an empty sysroot.
result = ctx.execute(["mkdir", "-p", "sysroot/usr/include"])
if result.return_code:
fail("Failed to create empty sysroot: {}".format(result.stderr))

# One sandbox symlink and one merkle-tree node, just like the Linux SDK.
ctx.file("sysroot/BUILD.bazel", _MACOS_SYSROOT_BUILD)
ctx.file("BUILD.bazel", _SYSROOT_ALIAS_BUILD)

macos_sysroot = repository_rule(
implementation = _macos_sysroot_impl,
Expand Down
21 changes: 21 additions & 0 deletions bazel/sysroot/test/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
load(":sysroot_test.bzl", "sysroot_test")

[
sysroot_test(
name = "linux_%s_test" % arch,
headers = "@sysroot_linux_%s//:headers" % arch,
libs = "@sysroot_linux_%s//:libs" % arch,
stdio = "@sysroot_linux_%s//sysroot:usr/include/stdio.h" % arch,
sysroot = "@sysroot_linux_%s//:sysroot" % arch,
toolchain_sysroot = "@sysroot_linux_%s//:toolchain_sysroot" % arch,
)
for arch in [
"amd64",
"arm64",
]
]

sysroot_test(
name = "macos_empty_test",
sysroot = "@sysroot_macos_empty//:sysroot",
)
34 changes: 34 additions & 0 deletions bazel/sysroot/test/sysroot_test.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
"""Regression tests for directory sysroots and their file-level targets."""

load("@bazel_skylib//lib:unittest.bzl", "asserts", "unittest")

def _sysroot_test_impl(ctx):
env = unittest.begin(ctx)
files = ctx.attr.sysroot[DefaultInfo].files.to_list()
asserts.equals(env, 1, len(files))
if len(files) == 1:
root = files[0]
asserts.equals(env, "sysroot", root.basename)
if ctx.attr.toolchain_sysroot:
asserts.equals(env, files, ctx.attr.toolchain_sysroot[DefaultInfo].files.to_list())
if ctx.attr.headers:
headers = ctx.attr.headers[DefaultInfo].files.to_list()
libs = ctx.attr.libs[DefaultInfo].files.to_list()
asserts.true(env, len(headers) > 0)
asserts.true(env, len(libs) > 0)
asserts.true(env, ctx.file.stdio in headers)
for file in headers + libs:
asserts.true(env, file.path.startswith(root.path + "/"))
asserts.false(env, ":" in file.basename or file.basename.endswith(".pl"))
return unittest.end(env)

sysroot_test = unittest.make(
_sysroot_test_impl,
attrs = {
"sysroot": attr.label(mandatory = True),
"toolchain_sysroot": attr.label(),
"headers": attr.label(),
"libs": attr.label(),
"stdio": attr.label(allow_single_file = True),
},
)
Loading