load("//dist:generated.bzl", "DIST") load(":defs.bzl", "hermetic_cxx_toolchain", "hermetic_rust_toolchain") load("@prelude//toolchains:genrule.bzl", "system_genrule_toolchain") load("@prelude//toolchains:python.bzl", "system_python_bootstrap_toolchain") # Tools come from the DotSlash files in scripts/, so the build depends on # pinned upstream releases rather than whatever the host happens to have. # # The paths are absolute, and read out of .buckconfig.local rather than written # here, because neither of the obvious spellings works. A build script runs in # a directory of its own, and the prelude re-execs the compiler from there, so # a project-relative `scripts/zcc` is not found; and the shim that does the # re-exec calls os.execl rather than os.execlp, so a bare `zcc` on PATH is not # found either. aws-lc-sys is where both show up. The `buck` recipe in justfile # writes that file, so the absolute paths are generated rather than committed. # # rustc does not come this way any more — see the toolchain at the bottom of # this file. These three are what is left to make into dependencies. _SCRIPTS = read_root_config("jolt", "scripts", "scripts") # The compiler's version, written by the `buck` recipe alongside the paths. # # It has to reach the actions somehow, because the prelude names rustc as the # bare string "rustc" — the binary is not an input, so its identity is not in # any action's cache digest. Two compilers therefore produce artifacts under # the same digest, and a shared cache will hand one build the other's rlibs; # the failure is E0514, a long way from the cause. Naming the version in a cfg # nothing reads is enough to keep the digests apart. # The C toolchain's version, written by the `buck` recipe alongside the paths. # # rustc no longer needs this: it is a dependency now, so its identity is in # every action's digest and two compilers cannot collide on one cache entry. # zig still does. It is named by an absolute path that does not change when the # binary behind it does, so a version bump would silently reuse entries built # by the old one — the E0514 failure, one toolchain over. Naming the version in # a cfg nothing reads keeps the digests apart until zig is a dependency too. _ZIG_VERSION = read_root_config("jolt", "zig_version", "unknown") # The NDK's, for the same reason and read from the pin rather than the machine. # The C toolchain does not need it — the archive is a dependency there, so it # is in every cxx action's digest already. rustc does: it links an Android # target through scripts/android-cc, an absolute path that does not change when # the NDK behind it does. _NDK_VERSION = DIST["android-ndk"]["linux-x86_64"]["strip_prefix"] # # This is the same arrangement .cargo/config.toml used to describe: zig cc # carries its own glibc sysroot, which is why this repo builds on a machine # with no `cc` at all. # # Android is the one target it cannot serve. zig brings a glibc sysroot and no # bionic, so there is nothing there to link an Android object against; the # NDK's own clang is the driver that knows where libc.so and the platform stubs # live. Only the archive and the wrapper change — the toolchain either side of # them is the same construction, which is the point of asking a wrapper for a # mode rather than naming binaries here. hermetic_cxx_toolchain( name = "cxx", dist = select({ "DEFAULT": "toolchains//dist:zig", # DotSlash, not the NDK: ndk.sh fetches the NDK where it runs, which is # the difference between a worker downloading three gigabytes from # Google and this machine uploading them to the CAS first. "prelude//os:android": "toolchains//dist:dotslash", }), manifest = select({ "DEFAULT": None, "prelude//os:android": "root//scripts:android-ndk.dotslash", }), wrapper = select({ "DEFAULT": "toolchains//:zcc.sh", "prelude//os:android": "toolchains//:ndk.sh", }), platform_name = select({ "DEFAULT": "x86_64", "prelude//os:android": "arm64", }), visibility = ["PUBLIC"], ) system_genrule_toolchain( name = "genrule", visibility = ["PUBLIC"], ) system_python_bootstrap_toolchain( name = "python_bootstrap", visibility = ["PUBLIC"], ) # The compiler is the dist tarball in toolchains//dist, not a name resolved # against the machine. See defs.bzl for why that distinction is the whole # point, and dist.bzl for where the tarball comes from. hermetic_rust_toolchain( name = "rust", dist = "toolchains//dist:rust", wrapper = "toolchains//:rustc.sh", default_edition = "2021", # Pipelined builds compile each crate against its dependencies' # `-Zno-codegen` metadata rather than their rlibs. On the Android graph the # two disagree for android-activity, and what comes out the far side is a # link that cannot find winit — or glutin-winit, or anything else that # reaches the event loop through it. Build real rlibs there. The desktop # graph pipelines fine and keeps the parallelism. nightly_features = select({ "DEFAULT": True, "prelude//os:android": False, }), # Which libstd rustc is given. The desktop configuration takes the # component the dist tarball carries for its own host; Android needs two # triples in one directory, so it takes the stitched one. sysroot = select({ "DEFAULT": None, "prelude//os:android": "toolchains//dist:android-sysroot", }), rustc_flags = select({ "DEFAULT": ["-Clinker=" + _SCRIPTS + "/zcc"], # zig has no bionic to link against; the NDK's clang does. The cfg # nothing reads is what keeps the digests apart, as above. "prelude//os:android": [ "-Clinker=" + _SCRIPTS + "/android-cc", "--cfg=jolt_ndk=\"" + _NDK_VERSION + "\"", ], }) + [ # What [profile.release] in Cargo.toml says, and for the reason given # there: the tree walk and the texture uploads are hot every frame, and # the media plane decodes H.264. buck2 optimises nothing by default, so # without this the two objects are debug builds wearing release names. # OPT_LEVEL=2 in the buildscript fixups is the same setting for the C. "-Copt-level=2", "--cfg=jolt_cc=\"" + _ZIG_VERSION + "\"", ], # Only triples there is an archive for can be named here: the desktop one # comes out of the dist tarball, the Android one out of the libstd fetched # beside it. rustc_target_triple = select({ "DEFAULT": "x86_64-unknown-linux-gnu", "prelude//os:android": "aarch64-linux-android", }), visibility = ["PUBLIC"], ) # The wrapper the Rust toolchain runs rustc through, as an artifact so it is an # input to every action that compiles anything. export_file( name = "zcc.sh", mode = "reference", visibility = ["PUBLIC"], ) export_file( name = "ndk.sh", mode = "reference", visibility = ["PUBLIC"], ) export_file( name = "rustc.sh", mode = "reference", visibility = ["PUBLIC"], )