nandi/jolt-nativepublic Fork 0
6391496
Commits
Clone
git clone https://git.rickub.com/nandi/jolt-native.git
git clone ssh://git@rickub.com/nandi/jolt-native.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

Make the C toolchain a dependency too, so zig travels with the action

system_cxx_toolchain cannot be made hermetic. It takes its tools as plain
strings, so nothing it names is an input to anything, and it hardcodes nm,
objcopy, objdump, ranlib and strip as bare PATH lookups whatever is passed to
it. This is the same construction with zig behind every tool this graph
actually reaches, and a wrapper carrying the vendor-field rewrite scripts/zcc
does — cc-rs builds a --target= out of the Rust triple, and zig refuses the
whole query rather than the part of it that it does not want.

nm, objdump and strip stay bare names. Nothing here reaches them: shared
library interfaces are disabled and nothing is stripped. A build that did
reach them would want an llvm dist fetched beside zig, rather than a PATH
lookup that happens to answer on the machine that ran it.

Verified the way the rust half was: aws-lc-sys, which is thousands of C files
and hand-written assembly driven by cc-rs out of a build script, built with
scripts/ off PATH entirely. That was the action least likely to survive
losing the host, and the one that decides whether the hard part of this graph
could ever run somewhere else.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
nandi committed 2026-08-30T22:07:12-07:00 Browse files
6391496 parent: 8cf1b33
modified toolchains/BUCK +10 -10
@@ -1,7 +1,6 @@
1-load("@prelude//toolchains:cxx.bzl", "system_cxx_toolchain")
1+load(":defs.bzl", "hermetic_cxx_toolchain", "hermetic_rust_toolchain")
22 load("@prelude//toolchains:genrule.bzl", "system_genrule_toolchain")
33 load("@prelude//toolchains:python.bzl", "system_python_bootstrap_toolchain")
4-load(":defs.bzl", "hermetic_rust_toolchain")
54
65 # Tools come from the DotSlash files in scripts/, so the build depends on
76 # pinned upstream releases rather than whatever the host happens to have.
@@ -41,15 +40,10 @@ _ZIG_VERSION = read_root_config("jolt", "zig_version", "unknown")
4140 # with no `cc` at all. Unlike vidya there is no Android row here — both shared
4241 # objects this repo ships are desktop, and the APK takes its libvidya from
4342 # vidya's own buck2 build.
44-system_cxx_toolchain(
43+hermetic_cxx_toolchain(
4544 name = "cxx",
46- # `ar` would otherwise come off the host, which is the one thing this
47- # toolchain is arranged not to do.
48- archiver = _SCRIPTS + "/zar",
49- compiler = _SCRIPTS + "/zcc",
50- compiler_type = "clang",
51- cxx_compiler = _SCRIPTS + "/zxx",
52- linker = _SCRIPTS + "/zcc",
45+ dist = "toolchains//dist:zig",
46+ wrapper = "toolchains//:zcc.sh",
5347 visibility = ["PUBLIC"],
5448 )
5549
@@ -90,6 +84,12 @@ hermetic_rust_toolchain(
9084
9185 # The wrapper the Rust toolchain runs rustc through, as an artifact so it is an
9286 # input to every action that compiles anything.
87+export_file(
88+ name = "zcc.sh",
89+ mode = "reference",
90+ visibility = ["PUBLIC"],
91+)
92+
9393 export_file(
9494 name = "rustc.sh",
9595 mode = "reference",
@@ -1,7 +1,6 @@
1-load("@prelude//toolchains:cxx.bzl", "system_cxx_toolchain")1+load(":defs.bzl", "hermetic_cxx_toolchain", "hermetic_rust_toolchain")
2 load("@prelude//toolchains:genrule.bzl", "system_genrule_toolchain")2 load("@prelude//toolchains:genrule.bzl", "system_genrule_toolchain")
3 load("@prelude//toolchains:python.bzl", "system_python_bootstrap_toolchain")3 load("@prelude//toolchains:python.bzl", "system_python_bootstrap_toolchain")
4-load(":defs.bzl", "hermetic_rust_toolchain")
5 4
6 # Tools come from the DotSlash files in scripts/, so the build depends on5 # Tools come from the DotSlash files in scripts/, so the build depends on
7 # pinned upstream releases rather than whatever the host happens to have.6 # pinned upstream releases rather than whatever the host happens to have.
@@ -41,15 +40,10 @@ _ZIG_VERSION = read_root_config("jolt", "zig_version", "unknown")
41 # with no `cc` at all. Unlike vidya there is no Android row here — both shared40 # with no `cc` at all. Unlike vidya there is no Android row here — both shared
42 # objects this repo ships are desktop, and the APK takes its libvidya from41 # objects this repo ships are desktop, and the APK takes its libvidya from
43 # vidya's own buck2 build.42 # vidya's own buck2 build.
44-system_cxx_toolchain(43+hermetic_cxx_toolchain(
45 name = "cxx",44 name = "cxx",
46- # `ar` would otherwise come off the host, which is the one thing this45+ dist = "toolchains//dist:zig",
47- # toolchain is arranged not to do.46+ wrapper = "toolchains//:zcc.sh",
48- archiver = _SCRIPTS + "/zar",
49- compiler = _SCRIPTS + "/zcc",
50- compiler_type = "clang",
51- cxx_compiler = _SCRIPTS + "/zxx",
52- linker = _SCRIPTS + "/zcc",
53 visibility = ["PUBLIC"],47 visibility = ["PUBLIC"],
54 )48 )
55 49
@@ -90,6 +84,12 @@ hermetic_rust_toolchain(
90 84
91 # The wrapper the Rust toolchain runs rustc through, as an artifact so it is an85 # The wrapper the Rust toolchain runs rustc through, as an artifact so it is an
92 # input to every action that compiles anything.86 # input to every action that compiles anything.
87+export_file(
88+ name = "zcc.sh",
89+ mode = "reference",
90+ visibility = ["PUBLIC"],
91+)
92+
93 export_file(93 export_file(
94 name = "rustc.sh",94 name = "rustc.sh",
95 mode = "reference",95 mode = "reference",
modified toolchains/defs.bzl +116 -0
@@ -71,3 +71,119 @@ hermetic_rust_toolchain = rule(
7171 },
7272 is_toolchain_rule = True,
7373 )
74+
75+# A C toolchain whose tools are dependencies rather than names.
76+#
77+# The prelude's system_cxx_toolchain cannot be made hermetic: it takes its
78+# tools as plain strings, so nothing it names becomes an input, and it
79+# hardcodes nm/objcopy/objdump/ranlib/strip as bare PATH lookups regardless of
80+# what is passed. This is the same construction with zig behind every tool that
81+# this graph actually runs.
82+load("@prelude//cxx:cxx_toolchain_types.bzl", "BinaryUtilitiesInfo", "CCompilerInfo", "CxxCompilerInfo", "CxxInternalTools", "CxxPlatformInfo", "CxxToolchainInfo", "DepTrackingMode", "LinkerInfo", "LinkerType", "PicBehavior", "RuntimeDependencyHandling", "ShlibInterfacesMode")
83+load("@prelude//cxx:headers.bzl", "HeaderMode")
84+load("@prelude//linking:link_info.bzl", "LinkOrdering", "LinkStyle")
85+load("@prelude//linking:lto.bzl", "LtoMode")
86+
87+def _hermetic_cxx_toolchain_impl(ctx: AnalysisContext):
88+ dist = ctx.attrs.dist[DefaultInfo].default_outputs[0]
89+ wrapper = ctx.attrs.wrapper[DefaultInfo].default_outputs[0]
90+
91+ def zig(mode):
92+ return RunInfo(args = cmd_args(wrapper, dist, mode))
93+
94+ cc = zig("cc")
95+
96+ return [
97+ DefaultInfo(),
98+ CxxToolchainInfo(
99+ internal_tools = ctx.attrs.internal_tools[CxxInternalTools],
100+ linker_info = LinkerInfo(
101+ linker = cc,
102+ linker_flags = ["-fuse-ld=lld"] + ctx.attrs.link_flags,
103+ post_linker_flags = [],
104+ archiver = zig("ar"),
105+ archiver_type = "gnu",
106+ archiver_supports_argfiles = True,
107+ generate_linker_maps = False,
108+ lto_mode = LtoMode("none"),
109+ type = LinkerType("gnu"),
110+ link_binaries_locally = True,
111+ link_libraries_locally = True,
112+ archive_objects_locally = True,
113+ use_archiver_flags = True,
114+ static_dep_runtime_ld_flags = [],
115+ static_pic_dep_runtime_ld_flags = [],
116+ shared_dep_runtime_ld_flags = [],
117+ independent_shlib_interface_linker_flags = [],
118+ shlib_interfaces = ShlibInterfacesMode("disabled"),
119+ link_style = LinkStyle(ctx.attrs.link_style),
120+ link_weight = 1,
121+ binary_extension = "",
122+ object_file_extension = "o",
123+ shared_library_name_default_prefix = "lib",
124+ shared_library_name_format = "{}.so",
125+ shared_library_versioned_name_format = "{}.so.{}",
126+ static_library_extension = "a",
127+ force_full_hybrid_if_capable = False,
128+ is_pdb_generated = False,
129+ link_ordering = ctx.attrs.link_ordering,
130+ ),
131+ bolt_enabled = False,
132+ # zig carries ar, ranlib and objcopy and nothing else. nm, objdump
133+ # and strip stay bare names: this graph never reaches them — shared
134+ # library interfaces are disabled and nothing is stripped — and a
135+ # build that did would need an llvm dist fetched alongside zig
136+ # rather than a PATH lookup that happens to work.
137+ binary_utilities_info = BinaryUtilitiesInfo(
138+ nm = RunInfo(args = ["nm"]),
139+ objcopy = zig("objcopy"),
140+ objdump = RunInfo(args = ["objdump"]),
141+ ranlib = zig("ranlib"),
142+ strip = RunInfo(args = ["strip"]),
143+ dwp = None,
144+ bolt_msdk = None,
145+ ),
146+ cxx_compiler_info = CxxCompilerInfo(
147+ compiler = zig("c++"),
148+ preprocessor_flags = [],
149+ compiler_flags = ctx.attrs.cxx_flags,
150+ compiler_type = "clang",
151+ ),
152+ c_compiler_info = CCompilerInfo(
153+ compiler = cc,
154+ preprocessor_flags = [],
155+ compiler_flags = ctx.attrs.c_flags,
156+ compiler_type = "clang",
157+ ),
158+ as_compiler_info = CCompilerInfo(
159+ compiler = cc,
160+ compiler_type = "clang",
161+ ),
162+ asm_compiler_info = CCompilerInfo(
163+ compiler = cc,
164+ compiler_type = "clang",
165+ ),
166+ header_mode = HeaderMode("symlink_tree_only"),
167+ cpp_dep_tracking_mode = DepTrackingMode("show_headers"),
168+ pic_behavior = PicBehavior("supported"),
169+ llvm_link = None,
170+ use_dep_files = True,
171+ runtime_dependency_handling = RuntimeDependencyHandling("no_symlink"),
172+ ),
173+ CxxPlatformInfo(name = "x86_64"),
174+ ]
175+
176+hermetic_cxx_toolchain = rule(
177+ impl = _hermetic_cxx_toolchain_impl,
178+ attrs = {
179+ "c_flags": attrs.list(attrs.arg(), default = []),
180+ "cxx_flags": attrs.list(attrs.arg(), default = []),
181+ "dist": attrs.dep(providers = [DefaultInfo]),
182+ "internal_tools": attrs.default_only(attrs.dep(providers = [CxxInternalTools], default = "prelude//cxx/tools:internal_tools")),
183+ "link_flags": attrs.list(attrs.arg(), default = []),
184+ "link_ordering": attrs.option(attrs.enum(LinkOrdering.values()), default = None),
185+ "link_style": attrs.string(default = "shared"),
186+ "wrapper": attrs.dep(providers = [DefaultInfo]),
187+ },
188+ is_toolchain_rule = True,
189+)
@@ -71,3 +71,119 @@ hermetic_rust_toolchain = rule(
71 },71 },
72 is_toolchain_rule = True,72 is_toolchain_rule = True,
73 )73 )
74+
75+# A C toolchain whose tools are dependencies rather than names.
76+#
77+# The prelude's system_cxx_toolchain cannot be made hermetic: it takes its
78+# tools as plain strings, so nothing it names becomes an input, and it
79+# hardcodes nm/objcopy/objdump/ranlib/strip as bare PATH lookups regardless of
80+# what is passed. This is the same construction with zig behind every tool that
81+# this graph actually runs.
82+load("@prelude//cxx:cxx_toolchain_types.bzl", "BinaryUtilitiesInfo", "CCompilerInfo", "CxxCompilerInfo", "CxxInternalTools", "CxxPlatformInfo", "CxxToolchainInfo", "DepTrackingMode", "LinkerInfo", "LinkerType", "PicBehavior", "RuntimeDependencyHandling", "ShlibInterfacesMode")
83+load("@prelude//cxx:headers.bzl", "HeaderMode")
84+load("@prelude//linking:link_info.bzl", "LinkOrdering", "LinkStyle")
85+load("@prelude//linking:lto.bzl", "LtoMode")
86+
87+def _hermetic_cxx_toolchain_impl(ctx: AnalysisContext):
88+ dist = ctx.attrs.dist[DefaultInfo].default_outputs[0]
89+ wrapper = ctx.attrs.wrapper[DefaultInfo].default_outputs[0]
90+
91+ def zig(mode):
92+ return RunInfo(args = cmd_args(wrapper, dist, mode))
93+
94+ cc = zig("cc")
95+
96+ return [
97+ DefaultInfo(),
98+ CxxToolchainInfo(
99+ internal_tools = ctx.attrs.internal_tools[CxxInternalTools],
100+ linker_info = LinkerInfo(
101+ linker = cc,
102+ linker_flags = ["-fuse-ld=lld"] + ctx.attrs.link_flags,
103+ post_linker_flags = [],
104+ archiver = zig("ar"),
105+ archiver_type = "gnu",
106+ archiver_supports_argfiles = True,
107+ generate_linker_maps = False,
108+ lto_mode = LtoMode("none"),
109+ type = LinkerType("gnu"),
110+ link_binaries_locally = True,
111+ link_libraries_locally = True,
112+ archive_objects_locally = True,
113+ use_archiver_flags = True,
114+ static_dep_runtime_ld_flags = [],
115+ static_pic_dep_runtime_ld_flags = [],
116+ shared_dep_runtime_ld_flags = [],
117+ independent_shlib_interface_linker_flags = [],
118+ shlib_interfaces = ShlibInterfacesMode("disabled"),
119+ link_style = LinkStyle(ctx.attrs.link_style),
120+ link_weight = 1,
121+ binary_extension = "",
122+ object_file_extension = "o",
123+ shared_library_name_default_prefix = "lib",
124+ shared_library_name_format = "{}.so",
125+ shared_library_versioned_name_format = "{}.so.{}",
126+ static_library_extension = "a",
127+ force_full_hybrid_if_capable = False,
128+ is_pdb_generated = False,
129+ link_ordering = ctx.attrs.link_ordering,
130+ ),
131+ bolt_enabled = False,
132+ # zig carries ar, ranlib and objcopy and nothing else. nm, objdump
133+ # and strip stay bare names: this graph never reaches them — shared
134+ # library interfaces are disabled and nothing is stripped — and a
135+ # build that did would need an llvm dist fetched alongside zig
136+ # rather than a PATH lookup that happens to work.
137+ binary_utilities_info = BinaryUtilitiesInfo(
138+ nm = RunInfo(args = ["nm"]),
139+ objcopy = zig("objcopy"),
140+ objdump = RunInfo(args = ["objdump"]),
141+ ranlib = zig("ranlib"),
142+ strip = RunInfo(args = ["strip"]),
143+ dwp = None,
144+ bolt_msdk = None,
145+ ),
146+ cxx_compiler_info = CxxCompilerInfo(
147+ compiler = zig("c++"),
148+ preprocessor_flags = [],
149+ compiler_flags = ctx.attrs.cxx_flags,
150+ compiler_type = "clang",
151+ ),
152+ c_compiler_info = CCompilerInfo(
153+ compiler = cc,
154+ preprocessor_flags = [],
155+ compiler_flags = ctx.attrs.c_flags,
156+ compiler_type = "clang",
157+ ),
158+ as_compiler_info = CCompilerInfo(
159+ compiler = cc,
160+ compiler_type = "clang",
161+ ),
162+ asm_compiler_info = CCompilerInfo(
163+ compiler = cc,
164+ compiler_type = "clang",
165+ ),
166+ header_mode = HeaderMode("symlink_tree_only"),
167+ cpp_dep_tracking_mode = DepTrackingMode("show_headers"),
168+ pic_behavior = PicBehavior("supported"),
169+ llvm_link = None,
170+ use_dep_files = True,
171+ runtime_dependency_handling = RuntimeDependencyHandling("no_symlink"),
172+ ),
173+ CxxPlatformInfo(name = "x86_64"),
174+ ]
175+
176+hermetic_cxx_toolchain = rule(
177+ impl = _hermetic_cxx_toolchain_impl,
178+ attrs = {
179+ "c_flags": attrs.list(attrs.arg(), default = []),
180+ "cxx_flags": attrs.list(attrs.arg(), default = []),
181+ "dist": attrs.dep(providers = [DefaultInfo]),
182+ "internal_tools": attrs.default_only(attrs.dep(providers = [CxxInternalTools], default = "prelude//cxx/tools:internal_tools")),
183+ "link_flags": attrs.list(attrs.arg(), default = []),
184+ "link_ordering": attrs.option(attrs.enum(LinkOrdering.values()), default = None),
185+ "link_style": attrs.string(default = "shared"),
186+ "wrapper": attrs.dep(providers = [DefaultInfo]),
187+ },
188+ is_toolchain_rule = True,
189+)
added toolchains/zcc.sh +30 -0
new file mode 100755
@@ -0,0 +1,30 @@
1+#!/bin/sh
2+# zig, as the C toolchain, out of the tarball buck fetched.
3+#
4+# The buck-side twin of scripts/zcc, and the same argument rewriting: cc-rs
5+# builds a `--target=` from the Rust triple, which carries a vendor field —
6+# `x86_64-unknown-linux-gnu`. zig's own triples have no vendor, and it refuses
7+# the whole query rather than ignoring the part it does not want, reporting
8+# "unable to parse target query: UnknownOperatingSystem" as though Linux were
9+# the problem.
10+#
11+# $1 is the directory the tarball unpacked into and $2 the zig subcommand — cc,
12+# c++, ar or ranlib. buck passes both; nothing here can work them out.
13+set -e
14+zig=$1/zig
15+mode=$2
16+shift 2
17+
18+for arg do
19+ case $arg in
20+ --target=*-unknown-linux-*)
21+ set -- "$@" "$(printf '%s' "$arg" | sed 's/-unknown-linux-/-linux-/')"
22+ shift
23+ continue
24+ ;;
25+ esac
26+ set -- "$@" "$arg"
27+ shift
28+done
29+
30+exec "$zig" "$mode" "$@"
new file mode 100755
@@ -0,0 +1,30 @@
1+#!/bin/sh
2+# zig, as the C toolchain, out of the tarball buck fetched.
3+#
4+# The buck-side twin of scripts/zcc, and the same argument rewriting: cc-rs
5+# builds a `--target=` from the Rust triple, which carries a vendor field —
6+# `x86_64-unknown-linux-gnu`. zig's own triples have no vendor, and it refuses
7+# the whole query rather than ignoring the part it does not want, reporting
8+# "unable to parse target query: UnknownOperatingSystem" as though Linux were
9+# the problem.
10+#
11+# $1 is the directory the tarball unpacked into and $2 the zig subcommand — cc,
12+# c++, ar or ranlib. buck passes both; nothing here can work them out.
13+set -e
14+zig=$1/zig
15+mode=$2
16+shift 2
17+
18+for arg do
19+ case $arg in
20+ --target=*-unknown-linux-*)
21+ set -- "$@" "$(printf '%s' "$arg" | sed 's/-unknown-linux-/-linux-/')"
22+ shift
23+ continue
24+ ;;
25+ esac
26+ set -- "$@" "$arg"
27+ shift
28+done
29+
30+exec "$zig" "$mode" "$@"