| Fetch rustc the way buck can ship it, from what DotSlash already pins 8cf1b33 nandi 19d ago | 1 | # A Rust toolchain whose compiler is a dependency rather than a name. |
| 2 | # |
| 3 | # The prelude's system_rust_toolchain hands RustToolchainInfo |
| 4 | # `RunInfo(args = ["rustc"])` — a bare string. Nothing about the compiler is an |
| 5 | # input to any action, so nothing about it reaches the action's cache digest, |
| 6 | # and there is nothing for a remote worker to materialise. Both of this repo's |
| 7 | # problems come from that one line: a shared cache cannot tell two compilers |
| 8 | # apart, and remote execution has no compiler to run. |
| 9 | # |
| 10 | # Here the dist tarball is a dep. cmd_args carries the archive's directory into |
| 11 | # every rustc invocation, so buck knows the action depends on it. |
| 12 | load("@prelude//rust:rust_toolchain.bzl", "PanicRuntime", "RustToolchainInfo") |
| 13 | |
| 14 | def _hermetic_rust_toolchain_impl(ctx): |
| 15 | dist = ctx.attrs.dist[DefaultInfo].default_outputs[0] |
| 16 | |
| 17 | # The dist tarball keeps its components in sibling directories, so the |
| 18 | # compiler's default sysroot (rustc/) holds no libstd — it lives under |
| 19 | # rust-std-<triple>/. The same stitching scripts/rustc does by hand, said |
| 20 | # to buck instead. The compiler's own codegen libraries are found by rpath, |
| 21 | # relative to the binary, so only the sysroot has to be named. |
| 22 | # rustc goes through a wrapper because the sysroot cannot travel any other |
| 23 | # way: rustc_flags takes plain strings, so an artifact path cannot be put |
| 24 | # there, and putting --sysroot on the compiler unconditionally breaks |
| Cross-compile for Android, with an NDK nobody has to install 14a30c0 nandi 19d ago | 25 | # buck's own rustc_cfg action, which passes an empty one. The wrapper, the |
| 26 | # tarball and the sysroot are all inputs, which is the point. |
| Fetch rustc the way buck can ship it, from what DotSlash already pins 8cf1b33 nandi 19d ago | 27 | wrapper = ctx.attrs.wrapper[DefaultInfo].default_outputs[0] |
| Cross-compile for Android, with an NDK nobody has to install 14a30c0 nandi 19d ago | 28 | |
| 29 | # Cross-compiling needs a sysroot holding two triples, which no single |
| 30 | # archive carries, so the Android configuration passes one stitched |
| 31 | # together in toolchains//dist. Everything else takes the component the |
| 32 | # dist tarball already has for the host it runs on. |
| 33 | if ctx.attrs.sysroot != None: |
| 34 | sysroot = ctx.attrs.sysroot[DefaultInfo].default_outputs[0] |
| 35 | else: |
| 36 | sysroot = cmd_args(dist, format = "{}/rust-std-" + ctx.attrs.rustc_target_triple) |
| 37 | compiler = cmd_args(wrapper, dist, sysroot) |
| Fetch rustc the way buck can ship it, from what DotSlash already pins 8cf1b33 nandi 19d ago | 38 | rustdoc = cmd_args(dist, format = "{}/rustc/bin/rustdoc") |
| 39 | |
| 40 | return [ |
| 41 | DefaultInfo(), |
| 42 | RustToolchainInfo( |
| 43 | allow_lints = ctx.attrs.allow_lints, |
| 44 | clippy_driver = RunInfo(args = cmd_args(dist, format = "{}/clippy/bin/clippy-driver")), |
| 45 | clippy_toml = None, |
| 46 | compiler = RunInfo(args = compiler), |
| 47 | default_edition = ctx.attrs.default_edition, |
| 48 | panic_runtime = PanicRuntime("unwind"), |
| 49 | deny_lints = ctx.attrs.deny_lints, |
| 50 | doctests = ctx.attrs.doctests, |
| 51 | nightly_features = ctx.attrs.nightly_features, |
| 52 | report_unused_deps = ctx.attrs.report_unused_deps, |
| 53 | rustc_binary_flags = ctx.attrs.rustc_binary_flags, |
| 54 | rustc_flags = ctx.attrs.rustc_flags, |
| 55 | rustc_target_triple = ctx.attrs.rustc_target_triple, |
| 56 | rustc_test_flags = ctx.attrs.rustc_test_flags, |
| 57 | rustdoc = RunInfo(args = rustdoc), |
| 58 | rustdoc_flags = ctx.attrs.rustdoc_flags, |
| 59 | warn_lints = ctx.attrs.warn_lints, |
| 60 | ), |
| 61 | ] |
| 62 | |
| 63 | hermetic_rust_toolchain = rule( |
| 64 | impl = _hermetic_rust_toolchain_impl, |
| 65 | attrs = { |
| 66 | "allow_lints": attrs.list(attrs.string(), default = []), |
| 67 | "default_edition": attrs.option(attrs.string(), default = None), |
| 68 | "deny_lints": attrs.list(attrs.string(), default = []), |
| 69 | "dist": attrs.dep(providers = [DefaultInfo]), |
| 70 | "wrapper": attrs.dep(providers = [DefaultInfo]), |
| 71 | "doctests": attrs.bool(default = False), |
| 72 | "nightly_features": attrs.bool(default = True), |
| 73 | "report_unused_deps": attrs.bool(default = False), |
| 74 | "rustc_binary_flags": attrs.list(attrs.arg(), default = []), |
| 75 | "rustc_flags": attrs.list(attrs.arg(), default = []), |
| 76 | "rustc_target_triple": attrs.string(), |
| Cross-compile for Android, with an NDK nobody has to install 14a30c0 nandi 19d ago | 77 | "sysroot": attrs.option(attrs.dep(providers = [DefaultInfo]), default = None), |
| Fetch rustc the way buck can ship it, from what DotSlash already pins 8cf1b33 nandi 19d ago | 78 | "rustc_test_flags": attrs.list(attrs.arg(), default = []), |
| 79 | "rustdoc_flags": attrs.list(attrs.arg(), default = []), |
| 80 | "warn_lints": attrs.list(attrs.string(), default = []), |
| 81 | }, |
| 82 | is_toolchain_rule = True, |
| 83 | ) |
| Make the C toolchain a dependency too, so zig travels with the action 6391496 nandi 19d ago | 84 | |
| 85 | # A C toolchain whose tools are dependencies rather than names. |
| 86 | # |
| 87 | # The prelude's system_cxx_toolchain cannot be made hermetic: it takes its |
| 88 | # tools as plain strings, so nothing it names becomes an input, and it |
| 89 | # hardcodes nm/objcopy/objdump/ranlib/strip as bare PATH lookups regardless of |
| Cross-compile for Android, with an NDK nobody has to install 14a30c0 nandi 19d ago | 90 | # what is passed. This is the same construction with a fetched archive behind |
| 91 | # every tool that this graph actually runs. |
| 92 | # |
| 93 | # Which archive is the caller's business: the wrapper is asked for a mode — cc, |
| 94 | # c++, ar, ranlib or objcopy — and what it runs for that mode is between it and |
| 95 | # the dist beside it. zcc.sh answers with zig, ndk.sh with the NDK's clang. |
| Make the C toolchain a dependency too, so zig travels with the action 6391496 nandi 19d ago | 96 | load("@prelude//cxx:cxx_toolchain_types.bzl", "BinaryUtilitiesInfo", "CCompilerInfo", "CxxCompilerInfo", "CxxInternalTools", "CxxPlatformInfo", "CxxToolchainInfo", "DepTrackingMode", "LinkerInfo", "LinkerType", "PicBehavior", "RuntimeDependencyHandling", "ShlibInterfacesMode") |
| 97 | load("@prelude//cxx:headers.bzl", "HeaderMode") |
| 98 | load("@prelude//linking:link_info.bzl", "LinkOrdering", "LinkStyle") |
| 99 | load("@prelude//linking:lto.bzl", "LtoMode") |
| 100 | |
| 101 | def _hermetic_cxx_toolchain_impl(ctx: AnalysisContext): |
| 102 | dist = ctx.attrs.dist[DefaultInfo].default_outputs[0] |
| 103 | wrapper = ctx.attrs.wrapper[DefaultInfo].default_outputs[0] |
| 104 | |
| Cross-compile for Android, with an NDK nobody has to install 14a30c0 nandi 19d ago | 105 | # A wrapper takes the directory it was given, then whatever else it needs |
| 106 | # to find its tools, then the mode. zcc.sh needs nothing else — zig is in |
| 107 | # the directory. ndk.sh is handed a manifest, because what it runs is not |
| 108 | # an input at all: see the note there. |
| Make the C toolchain a dependency too, so zig travels with the action 6391496 nandi 19d ago | 109 | def zig(mode): |
| Cross-compile for Android, with an NDK nobody has to install 14a30c0 nandi 19d ago | 110 | if ctx.attrs.manifest != None: |
| 111 | manifest = ctx.attrs.manifest[DefaultInfo].default_outputs[0] |
| 112 | return RunInfo(args = cmd_args(wrapper, dist, manifest, mode)) |
| Make the C toolchain a dependency too, so zig travels with the action 6391496 nandi 19d ago | 113 | return RunInfo(args = cmd_args(wrapper, dist, mode)) |
| 114 | |
| 115 | cc = zig("cc") |
| 116 | |
| 117 | return [ |
| 118 | DefaultInfo(), |
| 119 | CxxToolchainInfo( |
| 120 | internal_tools = ctx.attrs.internal_tools[CxxInternalTools], |
| 121 | linker_info = LinkerInfo( |
| 122 | linker = cc, |
| 123 | linker_flags = ["-fuse-ld=lld"] + ctx.attrs.link_flags, |
| 124 | post_linker_flags = [], |
| 125 | archiver = zig("ar"), |
| 126 | archiver_type = "gnu", |
| 127 | archiver_supports_argfiles = True, |
| 128 | generate_linker_maps = False, |
| 129 | lto_mode = LtoMode("none"), |
| 130 | type = LinkerType("gnu"), |
| 131 | link_binaries_locally = True, |
| 132 | link_libraries_locally = True, |
| 133 | archive_objects_locally = True, |
| 134 | use_archiver_flags = True, |
| 135 | static_dep_runtime_ld_flags = [], |
| 136 | static_pic_dep_runtime_ld_flags = [], |
| 137 | shared_dep_runtime_ld_flags = [], |
| 138 | independent_shlib_interface_linker_flags = [], |
| 139 | shlib_interfaces = ShlibInterfacesMode("disabled"), |
| 140 | link_style = LinkStyle(ctx.attrs.link_style), |
| 141 | link_weight = 1, |
| 142 | binary_extension = "", |
| 143 | object_file_extension = "o", |
| 144 | shared_library_name_default_prefix = "lib", |
| 145 | shared_library_name_format = "{}.so", |
| 146 | shared_library_versioned_name_format = "{}.so.{}", |
| 147 | static_library_extension = "a", |
| 148 | force_full_hybrid_if_capable = False, |
| 149 | is_pdb_generated = False, |
| 150 | link_ordering = ctx.attrs.link_ordering, |
| 151 | ), |
| 152 | bolt_enabled = False, |
| 153 | # zig carries ar, ranlib and objcopy and nothing else. nm, objdump |
| 154 | # and strip stay bare names: this graph never reaches them — shared |
| 155 | # library interfaces are disabled and nothing is stripped — and a |
| 156 | # build that did would need an llvm dist fetched alongside zig |
| 157 | # rather than a PATH lookup that happens to work. |
| 158 | binary_utilities_info = BinaryUtilitiesInfo( |
| 159 | nm = RunInfo(args = ["nm"]), |
| 160 | objcopy = zig("objcopy"), |
| 161 | objdump = RunInfo(args = ["objdump"]), |
| 162 | ranlib = zig("ranlib"), |
| 163 | strip = RunInfo(args = ["strip"]), |
| 164 | dwp = None, |
| 165 | bolt_msdk = None, |
| 166 | ), |
| 167 | cxx_compiler_info = CxxCompilerInfo( |
| 168 | compiler = zig("c++"), |
| 169 | preprocessor_flags = [], |
| 170 | compiler_flags = ctx.attrs.cxx_flags, |
| 171 | compiler_type = "clang", |
| 172 | ), |
| 173 | c_compiler_info = CCompilerInfo( |
| 174 | compiler = cc, |
| 175 | preprocessor_flags = [], |
| 176 | compiler_flags = ctx.attrs.c_flags, |
| 177 | compiler_type = "clang", |
| 178 | ), |
| 179 | as_compiler_info = CCompilerInfo( |
| 180 | compiler = cc, |
| 181 | compiler_type = "clang", |
| 182 | ), |
| 183 | asm_compiler_info = CCompilerInfo( |
| 184 | compiler = cc, |
| 185 | compiler_type = "clang", |
| 186 | ), |
| 187 | header_mode = HeaderMode("symlink_tree_only"), |
| 188 | cpp_dep_tracking_mode = DepTrackingMode("show_headers"), |
| 189 | pic_behavior = PicBehavior("supported"), |
| 190 | llvm_link = None, |
| 191 | use_dep_files = True, |
| 192 | runtime_dependency_handling = RuntimeDependencyHandling("no_symlink"), |
| 193 | ), |
| Cross-compile for Android, with an NDK nobody has to install 14a30c0 nandi 19d ago | 194 | # Names the target architecture, not the host's: this is what the |
| 195 | # prelude stamps into per-platform output paths, so an Android build |
| 196 | # and a desktop one cannot land on the same one. |
| 197 | CxxPlatformInfo(name = ctx.attrs.platform_name), |
| Make the C toolchain a dependency too, so zig travels with the action 6391496 nandi 19d ago | 198 | ] |
| 199 | |
| 200 | hermetic_cxx_toolchain = rule( |
| 201 | impl = _hermetic_cxx_toolchain_impl, |
| 202 | attrs = { |
| 203 | "c_flags": attrs.list(attrs.arg(), default = []), |
| 204 | "cxx_flags": attrs.list(attrs.arg(), default = []), |
| 205 | "dist": attrs.dep(providers = [DefaultInfo]), |
| 206 | "internal_tools": attrs.default_only(attrs.dep(providers = [CxxInternalTools], default = "prelude//cxx/tools:internal_tools")), |
| 207 | "link_flags": attrs.list(attrs.arg(), default = []), |
| 208 | "link_ordering": attrs.option(attrs.enum(LinkOrdering.values()), default = None), |
| 209 | "link_style": attrs.string(default = "shared"), |
| Cross-compile for Android, with an NDK nobody has to install 14a30c0 nandi 19d ago | 210 | "manifest": attrs.option(attrs.dep(providers = [DefaultInfo]), default = None), |
| 211 | "platform_name": attrs.string(default = "x86_64"), |
| Make the C toolchain a dependency too, so zig travels with the action 6391496 nandi 19d ago | 212 | "wrapper": attrs.dep(providers = [DefaultInfo]), |
| 213 | }, |
| 214 | is_toolchain_rule = True, |
| 215 | ) |