Skip to content

Commit

Permalink
feat: create cc_toolchains for multiple exec platforms (#196)
Browse files Browse the repository at this point in the history
This change aims to allow hermetic_cc_toolchain to create toolchains for
multiple exec platforms. It now generates repositories of the format
{exec}-zig_sdk and registers each toolchain to only be
exec_compatible_with those specific platform constraints.

I want this PR to be a WIP as we discuss how we want this to look and if
you're happy with it's general flow.

This is an attempt at fixing
#148

So far I have tested that my local client (mac) can build c code using
the toolchains registered on a `x86_64` remote executor and have seen
success. It'll will be hard for me to test every combination; but I will
be able to local (x86_64) -> remote (arm64) tests as well.
  • Loading branch information
BrandonThomasJonesARM authored Nov 28, 2024
1 parent 0f53271 commit aefb7a1
Show file tree
Hide file tree
Showing 9 changed files with 170 additions and 55 deletions.
36 changes: 23 additions & 13 deletions MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -30,26 +30,36 @@ use_repo(
)

toolchains = use_extension("//toolchain:ext.bzl", "toolchains")
use_repo(toolchains, "zig_sdk")
use_repo(toolchains, "zig_sdk", "zig_sdk-linux-amd64", "zig_sdk-linux-arm64", "zig_sdk-macos-amd64", "zig_sdk-macos-arm64", "zig_sdk-windows-amd64")

register_toolchains(
_COMMON_EXEC_PLATFORMS = [
("linux", "amd64"),
("linux", "arm64"),
("windows", "amd64"),
("macos", "arm64"),
("macos", "amd64"),
]

[register_toolchains(
# if no `--platform` is specified, these toolchains will be used for
# (linux,darwin,windows)x(amd64,arm64)
"@zig_sdk//toolchain:linux_amd64_gnu.2.28",
"@zig_sdk//toolchain:linux_arm64_gnu.2.28",
"@zig_sdk//toolchain:windows_amd64",
"@zig_sdk//toolchain:windows_arm64",
"@zig_sdk-{}-{}//toolchain:linux_amd64_gnu.2.28".format(os, arch),
"@zig_sdk-{}-{}//toolchain:linux_arm64_gnu.2.28".format(os, arch),
"@zig_sdk-{}-{}//toolchain:windows_amd64".format(os, arch),
"@zig_sdk-{}-{}//toolchain:windows_arm64".format(os, arch),
# "@zig_sdk-{}-{}//toolchain:darwin_amd64".format(os, arch),
# "@zig_sdk-{}-{}//toolchain:darwin_arm64".format(os, arch),

# amd64 toolchains for libc-aware platforms:
"@zig_sdk//libc_aware/toolchain:linux_amd64_gnu.2.28",
"@zig_sdk//libc_aware/toolchain:linux_amd64_gnu.2.31",
"@zig_sdk//libc_aware/toolchain:linux_amd64_musl",
"@zig_sdk-{}-{}//libc_aware/toolchain:linux_amd64_gnu.2.28".format(os, arch),
"@zig_sdk-{}-{}//libc_aware/toolchain:linux_amd64_gnu.2.31".format(os, arch),
"@zig_sdk-{}-{}//libc_aware/toolchain:linux_amd64_musl".format(os, arch),
# arm64 toolchains for libc-aware platforms:
"@zig_sdk//libc_aware/toolchain:linux_arm64_gnu.2.28",
"@zig_sdk//libc_aware/toolchain:linux_arm64_musl",
"@zig_sdk-{}-{}//libc_aware/toolchain:linux_arm64_gnu.2.28".format(os, arch),
"@zig_sdk-{}-{}//libc_aware/toolchain:linux_arm64_musl".format(os, arch),
# wasm/wasi toolchains
"@zig_sdk//toolchain:wasip1_wasm",
"@zig_sdk-{}-{}//toolchain:wasip1_wasm".format(os, arch),

# These toolchains are only registered locally.
dev_dependency = True,
)
) for os, arch in _COMMON_EXEC_PLATFORMS]
2 changes: 1 addition & 1 deletion rules/platform.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
def _platform_transition_impl(settings, attr):
_ignore = settings
return {
"//command_line_option:platforms": "@zig_sdk{}".format(attr.platform),
"//command_line_option:platforms": "@zig_sdk-linux-amd64{}".format(attr.platform),
}

_platform_transition = transition(
Expand Down
10 changes: 10 additions & 0 deletions toolchain/BUILD.sdk.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,13 @@ declare_cc_toolchains(
os = {os},
zig_sdk_path = {zig_sdk_path},
)

alias(
name = "exec_os",
actual = "@platforms//os:{exec_os}",
)

alias(
name = "exec_cpu",
actual = "@platforms//cpu:{exec_cpu}",
)
127 changes: 104 additions & 23 deletions toolchain/defs.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,9 @@ def toolchains(
version = VERSION,
url_formats = [],
host_platform_sha256 = HOST_PLATFORM_SHA256,
host_platform_ext = _HOST_PLATFORM_EXT):
host_platform_ext = _HOST_PLATFORM_EXT,
exec_os = "HOST",
exec_arch = "HOST"):
"""
Download zig toolchain and declare bazel toolchains.
The platforms are not registered automatically, that should be done by
Expand All @@ -77,30 +79,65 @@ def toolchains(
mirror_format = original_format.replace("https://ziglang.org/", "https://mirror.bazel.build/ziglang.org/")
url_formats = [mirror_format, original_format]

# If these are not specified by user in WORKSPACE, fall back to the old
# behavior of assuming HOST is the only supported platform.
if exec_os == "HOST" and exec_arch == "HOST":
zig_repository(
name = "zig_sdk",
version = version,
url_formats = url_formats,
host_platform_sha256 = host_platform_sha256,
host_platform_ext = host_platform_ext,
exec_os = exec_os,
exec_arch = exec_arch,
)
return

zig_repository(
name = "zig_sdk",
name = "zig_sdk-{}-{}".format(exec_os, exec_arch),
version = version,
url_formats = url_formats,
host_platform_sha256 = host_platform_sha256,
host_platform_ext = host_platform_ext,
exec_os = exec_os,
exec_arch = exec_arch,
)

def _quote(s):
return "'" + s.replace("'", "'\\''") + "'"

def _zig_repository_impl(repository_ctx):
arch = repository_ctx.os.arch
if arch == "amd64":
arch = "x86_64"

os = repository_ctx.os.name.lower()
if os.startswith("mac os"):
os = "macos"

if os.startswith("windows"):
os = "windows"

host_platform = "{}-{}".format(os, arch)
exec_os = repository_ctx.attr.exec_os
exec_arch = repository_ctx.attr.exec_arch
host_os = repository_ctx.os.name
host_arch = repository_ctx.os.arch

if exec_os == "HOST":
exec_os = host_os
if exec_arch == "HOST":
exec_arch = host_arch

if exec_arch == "amd64":
exec_arch = "x86_64"
if host_arch == "amd64":
host_arch = "x86_64"
if host_arch == "arm64":
host_arch = "aarch64"
if exec_arch == "arm64":
exec_arch = "aarch64"

# os = repository_ctx.os.name.lower()
if exec_os.startswith("mac os"):
exec_os = "macos"
if host_os.startswith("mac os"):
host_os = "macos"
if exec_os.startswith("windows"):
exec_os = "windows"
if host_os.startswith("windows"):
host_os = "windows"

host_platform = "{}-{}".format(host_os, host_arch)
exec_platform = "{}-{}".format(exec_os, exec_arch)

zig_sha256 = repository_ctx.attr.host_platform_sha256[host_platform]
zig_ext = repository_ctx.attr.host_platform_ext[host_platform]
Expand All @@ -109,6 +146,11 @@ def _zig_repository_impl(repository_ctx):
"version": repository_ctx.attr.version,
"host_platform": host_platform,
}
format_vars_exec = {
"_ext": repository_ctx.attr.host_platform_ext[exec_platform],
"version": repository_ctx.attr.version,
"host_platform": exec_platform,
}

# Fetch Label dependencies before doing download/extract.
# The Bazel docs are not very clear about this behavior but see:
Expand All @@ -133,7 +175,9 @@ def _zig_repository_impl(repository_ctx):
executable = False,
substitutions = {
"{zig_sdk_path}": _quote("external/zig_sdk"),
"{os}": _quote(os),
"{os}": _quote(exec_os),
"{exec_os}": exec_os,
"{exec_cpu}": exec_arch,
},
)

Expand All @@ -147,14 +191,14 @@ def _zig_repository_impl(repository_ctx):

cache_prefix = repository_ctx.os.environ.get("HERMETIC_CC_TOOLCHAIN_CACHE_PREFIX", "")
if cache_prefix == "":
if os == "windows":
if host_os == "windows":
cache_prefix = "C:\\\\Temp\\\\zig-cache"
elif os == "macos":
elif host_os == "macos":
cache_prefix = "/var/tmp/zig-cache"
elif os == "linux":
elif host_os == "linux":
cache_prefix = "/tmp/zig-cache"
else:
fail("unknown os: {}".format(os))
fail("unknown os: {}".format(host_os))

repository_ctx.template(
"tools/zig-wrapper.zig",
Expand All @@ -169,12 +213,13 @@ def _zig_repository_impl(repository_ctx):
"ZIG_LOCAL_CACHE_DIR": cache_prefix,
"ZIG_GLOBAL_CACHE_DIR": cache_prefix,
}

compile_cmd = [
_paths_join("..", "zig"),
"build-exe",
"-target",
_TARGET_MCPU[host_platform][0],
"-mcpu={}".format(_TARGET_MCPU[host_platform][1]),
_TARGET_MCPU[exec_platform][0],
"-mcpu={}".format(_TARGET_MCPU[exec_platform][1]),
"-fstrip",
"-OReleaseSafe",
"zig-wrapper.zig",
Expand Down Expand Up @@ -213,12 +258,21 @@ def _zig_repository_impl(repository_ctx):
if not zig_wrapper_success:
fail(zig_wrapper_err_msg)

exe = ".exe" if os == "windows" else ""
exe = ".exe" if exec_os == "windows" else ""
for t in _BUILTIN_TOOLS:
repository_ctx.symlink("tools/zig-wrapper{}".format(exe), "tools/{}{}".format(t, exe))

urls = [uf.format(**format_vars_exec) for uf in repository_ctx.attr.url_formats]

repository_ctx.download_and_extract(
auth = use_netrc(read_user_netrc(repository_ctx), urls, {}),
url = urls,
stripPrefix = "zig-{host_platform}-{version}/".format(**format_vars_exec),
sha256 = repository_ctx.attr.host_platform_sha256[exec_platform],
)

for target_config in target_structs():
tool_path = zig_tool_path(os).format(
tool_path = zig_tool_path(exec_os).format(
zig_tool = "c++",
zigtarget = target_config.zigtarget,
)
Expand All @@ -230,11 +284,38 @@ zig_repository = repository_rule(
"host_platform_sha256": attr.string_dict(),
"url_formats": attr.string_list(allow_empty = False),
"host_platform_ext": attr.string_dict(),
"exec_os": attr.string(),
"exec_arch": attr.string(),
},
environ = ["HERMETIC_CC_TOOLCHAIN_CACHE_PREFIX"],
implementation = _zig_repository_impl,
)

def _host_zig_repository_impl(repository_ctx):
host_os = repository_ctx.os.name
host_arch = repository_ctx.os.arch
if host_os.startswith("mac os"):
host_os = "macos"

if host_os.startswith("windows"):
host_os = "windows"

if host_arch.startswith("aarch64"):
host_arch = "arm64"

if host_arch.startswith("x86_64"):
host_arch = "amd64"

host_platform_compatible_zig = Label("@zig_sdk-{}-{}//:WORKSPACE".format(host_os, host_arch))
compatible_zig_path = repository_ctx.path(host_platform_compatible_zig)
repository_ctx.delete(".")
repository_ctx.symlink(str(compatible_zig_path) + "/..", ".")

host_zig_repository = repository_rule(
implementation = _host_zig_repository_impl,
local = True,
)

def filegroup(name, **kwargs):
native.filegroup(name = name, **kwargs)
return ":" + name
Expand Down
19 changes: 17 additions & 2 deletions toolchain/ext.bzl
Original file line number Diff line number Diff line change
@@ -1,6 +1,21 @@
load("@hermetic_cc_toolchain//toolchain:defs.bzl", zig_toolchains = "toolchains")
load("@hermetic_cc_toolchain//toolchain:defs.bzl", "host_zig_repository", zig_toolchains = "toolchains")

_COMMON_EXEC_PLATFORMS = [
("linux", "amd64"),
("linux", "arm64"),
("windows", "amd64"),
("macos", "arm64"),
("macos", "amd64"),
]

def _toolchains_impl(ctx):
zig_toolchains()
for os, arch in _COMMON_EXEC_PLATFORMS:
zig_toolchains(exec_os = os, exec_arch = arch)

host_zig_repository(name = "zig_sdk")
return ctx.extension_metadata(
root_module_direct_deps = ["zig_sdk"] + ["zig_sdk-{}-{}".format(os, arch) for os, arch in _COMMON_EXEC_PLATFORMS],
root_module_direct_dev_deps = [],
)

toolchains = module_extension(implementation = _toolchains_impl)
2 changes: 1 addition & 1 deletion toolchain/platform/defs.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ def declare_libc_aware_platforms():
"linux",
"linux",
suffix = "_{}".format(libc),
extra_constraints = ["@zig_sdk//libc:{}".format(libc)],
extra_constraints = ["//libc:{}".format(libc)],
)

def declare_platform(gocpu, zigcpu, bzlos, os, suffix = "", extra_constraints = []):
Expand Down
15 changes: 7 additions & 8 deletions toolchain/private/cc_toolchains.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ load("@hermetic_cc_toolchain//toolchain:zig_toolchain.bzl", "zig_cc_toolchain_co

def declare_cc_toolchains(os, zig_sdk_path):
exe = ".exe" if os == "windows" else ""

for target_config in target_structs():
gotarget = target_config.gotarget
zigtarget = target_config.zigtarget
Expand Down Expand Up @@ -59,13 +58,13 @@ def declare_cc_toolchains(os, zig_sdk_path):
name = zigtarget + "_cc",
toolchain_identifier = zigtarget + "-toolchain",
toolchain_config = ":%s_cc_config" % zigtarget,
all_files = "@zig_sdk//:%s_all_files" % zigtarget,
ar_files = "@zig_sdk//:%s_ar_files" % zigtarget,
compiler_files = "@zig_sdk//:%s_compiler_files" % zigtarget,
linker_files = "@zig_sdk//:%s_linker_files" % zigtarget,
dwp_files = "@zig_sdk//:empty",
objcopy_files = "@zig_sdk//:empty",
strip_files = "@zig_sdk//:empty",
all_files = "//:{}_all_files".format(zigtarget),
ar_files = "//:{}_ar_files".format(zigtarget),
compiler_files = "//:{}_compiler_files".format(zigtarget),
linker_files = "//:{}_linker_files".format(zigtarget),
dwp_files = "//:empty",
objcopy_files = "//:empty",
strip_files = "//:empty",
supports_param_files = 0,
visibility = ["//visibility:private"],
)
4 changes: 2 additions & 2 deletions toolchain/private/defs.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ def _target_linux_gnu(gocpu, zigcpu, glibc_version):
"@platforms//os:linux",
"@platforms//cpu:{}".format(zigcpu),
],
libc_constraint = "@zig_sdk//libc:{}".format(glibc_suffix),
libc_constraint = "//libc:{}".format(glibc_suffix),
ld_zig_subcmd = "ld.lld",
artifact_name_patterns = [],
)
Expand Down Expand Up @@ -196,7 +196,7 @@ def _target_linux_musl(gocpu, zigcpu):
"@platforms//os:linux",
"@platforms//cpu:{}".format(zigcpu),
],
libc_constraint = "@zig_sdk//libc:musl",
libc_constraint = "//libc:musl",
ld_zig_subcmd = "ld.lld",
artifact_name_patterns = [],
)
Expand Down
10 changes: 5 additions & 5 deletions toolchain/toolchain/defs.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ def declare_toolchains():
# only selected if the specific libc variant is selected.
extra_constraints = []
if hasattr(target_config, "libc_constraint"):
extra_constraints = ["@zig_sdk//libc:unconstrained"]
extra_constraints = ["//libc:unconstrained"]

_declare_toolchain(gotarget, zigtarget, target_config.constraint_values + extra_constraints)

Expand All @@ -30,17 +30,17 @@ def _declare_toolchain(gotarget, zigtarget, target_compatible_with):
# Go convention: amd64/arm64, linux/darwin
native.toolchain(
name = gotarget,
exec_compatible_with = None,
exec_compatible_with = ["//:exec_os", "//:exec_cpu"],
target_compatible_with = target_compatible_with,
toolchain = "@zig_sdk//:%s_cc" % zigtarget,
toolchain = "//:{}_cc".format(zigtarget),
toolchain_type = "@bazel_tools//tools/cpp:toolchain_type",
)

# Zig convention: x86_64/aarch64, linux/macos
native.toolchain(
name = zigtarget,
exec_compatible_with = None,
exec_compatible_with = ["//:exec_os", "//:exec_cpu"],
target_compatible_with = target_compatible_with,
toolchain = "@zig_sdk//:%s_cc" % zigtarget,
toolchain = "//:{}_cc".format(zigtarget),
toolchain_type = "@bazel_tools//tools/cpp:toolchain_type",
)

0 comments on commit aefb7a1

Please sign in to comment.