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>
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") | |
| 2 | 2 | load("@prelude//toolchains:genrule.bzl", "system_genrule_toolchain") |
| 3 | 3 | load("@prelude//toolchains:python.bzl", "system_python_bootstrap_toolchain") |
| 4 | -load(":defs.bzl", "hermetic_rust_toolchain") | |
| 5 | 4 | |
| 6 | 5 | # Tools come from the DotSlash files in scripts/, so the build depends on |
| 7 | 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 | 40 | # with no `cc` at all. Unlike vidya there is no Android row here — both shared |
| 42 | 41 | # objects this repo ships are desktop, and the APK takes its libvidya from |
| 43 | 42 | # vidya's own buck2 build. |
| 44 | -system_cxx_toolchain( | |
| 43 | +hermetic_cxx_toolchain( | |
| 45 | 44 | 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", | |
| 53 | 47 | visibility = ["PUBLIC"], |
| 54 | 48 | ) |
| 55 | 49 | |
| @@ -90,6 +84,12 @@ hermetic_rust_toolchain( | ||
| 90 | 84 | |
| 91 | 85 | # The wrapper the Rust toolchain runs rustc through, as an artifact so it is an |
| 92 | 86 | # input to every action that compiles anything. |
| 87 | +export_file( | |
| 88 | + name = "zcc.sh", | |
| 89 | + mode = "reference", | |
| 90 | + visibility = ["PUBLIC"], | |
| 91 | +) | |
| 92 | + | |
| 93 | 93 | export_file( |
| 94 | 94 | name = "rustc.sh", |
| 95 | 95 | 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 on | 5 | # 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 shared | 40 | # 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 from | 41 | # 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 this | 45 | + 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 an | 85 | # 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( | ||
| 71 | 71 | }, |
| 72 | 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 | +) | |
| @@ -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" "$@" | ||