Ensure functions have docstrings for documentation
def o(*args):
1#!/usr/bin/env python23# ignore-tidy-file-linelength45from __future__ import absolute_import, division, print_function6import shlex7import sys8import os9import re1011rust_dir = os.path.dirname(os.path.abspath(__file__))12rust_dir = os.path.dirname(rust_dir)13rust_dir = os.path.dirname(rust_dir)14sys.path.append(os.path.join(rust_dir, "src", "bootstrap"))15import bootstrap # noqa: E402161718class Option(object):19 def __init__(self, name, rustbuild, desc, value):20 self.name = name21 self.rustbuild = rustbuild22 self.desc = desc23 self.value = value242526options = []272829def o(*args):30 options.append(Option(*args, value=False))313233def v(*args):34 options.append(Option(*args, value=True))353637o(38 "debug",39 "rust.debug",40 "enables debugging environment; does not affect optimization of bootstrapped code",41)42o("docs", "build.docs", "build standard library documentation")43o("compiler-docs", "build.compiler-docs", "build compiler documentation")44o("optimize-tests", "rust.optimize-tests", "build tests with optimizations")45o("verbose-tests", "rust.verbose-tests", "enable verbose output when running tests")46o(47 "ccache",48 "build.ccache",49 "invoke gcc/clang/rustc via ccache to reuse object files between builds",50)51o(52 "sccache",53 None,54 "invoke gcc/clang/rustc via sccache to reuse object files between builds",55)56o("local-rust", None, "use an installed rustc rather than downloading a snapshot")57v("local-rust-root", None, "set prefix for local rust binary")58o(59 "local-rebuild",60 "build.local-rebuild",61 "assume local-rust matches the current version, for rebuilds; implies local-rust, and is implied if local-rust already matches the current version",62)63o(64 "llvm-static-stdcpp",65 "llvm.static-libstdcpp",66 "statically link to libstdc++ for LLVM",67)68o(69 "llvm-link-shared",70 "llvm.link-shared",71 "prefer shared linking to LLVM (llvm-config --link-shared)",72)73o("rpath", "rust.rpath", "build rpaths into rustc itself")74o("codegen-tests", "rust.codegen-tests", "run the tests/codegen tests")75o(76 "ninja",77 "llvm.ninja",78 "build LLVM using the Ninja generator (for MSVC, requires building in the correct environment)",79)80o("locked-deps", "build.locked-deps", "force Cargo.lock to be up to date")81o("vendor", "build.vendor", "enable usage of vendored Rust crates")82o(83 "sanitizers",84 "build.sanitizers",85 "build the sanitizer runtimes (asan, dfsan, lsan, msan, tsan, hwasan)",86)87o(88 "dist-src",89 "rust.dist-src",90 "when building tarballs enables building a source tarball",91)92o(93 "cargo-native-static",94 "build.cargo-native-static",95 "static native libraries in cargo",96)97o("profiler", "build.profiler", "build the profiler runtime")98o("full-tools", None, "enable all tools")99o("lld", "rust.lld", "build lld")100o("llvm-bitcode-linker", "rust.llvm-bitcode-linker", "build llvm bitcode linker")101o("clang", "llvm.clang", "build clang")102o("use-libcxx", "llvm.use-libcxx", "build LLVM with libc++")103o("control-flow-guard", "rust.control-flow-guard", "Enable Control Flow Guard")104o(105 "patch-binaries-for-nix",106 "build.patch-binaries-for-nix",107 "whether patch binaries for usage with Nix toolchains",108)109o("new-symbol-mangling", "rust.new-symbol-mangling", "use symbol-mangling-version v0")110111v("llvm-cflags", "llvm.cflags", "build LLVM with these extra compiler flags")112v("llvm-cxxflags", "llvm.cxxflags", "build LLVM with these extra compiler flags")113v("llvm-ldflags", "llvm.ldflags", "build LLVM with these extra linker flags")114115v("llvm-libunwind", "rust.llvm-libunwind", "use LLVM libunwind")116117# Optimization and debugging options. These may be overridden by the release118# channel, etc.119o("optimize-llvm", "llvm.optimize", "build optimized LLVM")120o("llvm-assertions", "llvm.assertions", "build LLVM with assertions")121o("llvm-enzyme", "llvm.enzyme", "build LLVM with enzyme")122o("llvm-offload", "llvm.offload", "build LLVM with gpu offload support")123o(124 "llvm-offload-clang-dir",125 "llvm.offload-clang-dir",126 "pass the absolute directory of ClangConfig.cmake",127)128o("llvm-plugins", "llvm.plugins", "build LLVM with plugin interface")129o("debug-assertions", "rust.debug-assertions", "build with debugging assertions")130o(131 "debug-assertions-std",132 "rust.debug-assertions-std",133 "build the standard library with debugging assertions",134)135o("overflow-checks", "rust.overflow-checks", "build with overflow checks")136o(137 "overflow-checks-std",138 "rust.overflow-checks-std",139 "build the standard library with overflow checks",140)141o(142 "llvm-release-debuginfo",143 "llvm.release-debuginfo",144 "build LLVM with debugger metadata",145)146v("debuginfo-level", "rust.debuginfo-level", "debuginfo level for Rust code")147v(148 "debuginfo-level-rustc",149 "rust.debuginfo-level-rustc",150 "debuginfo level for the compiler",151)152v(153 "debuginfo-level-std",154 "rust.debuginfo-level-std",155 "debuginfo level for the standard library",156)157v(158 "debuginfo-level-tools",159 "rust.debuginfo-level-tools",160 "debuginfo level for the tools",161)162v(163 "debuginfo-level-tests",164 "rust.debuginfo-level-tests",165 "debuginfo level for the test suites run with compiletest",166)167v(168 "save-toolstates",169 "rust.save-toolstates",170 "save build and test status of external tools into this file",171)172173v("prefix", "install.prefix", "set installation prefix")174v("localstatedir", "install.localstatedir", "local state directory")175v("datadir", "install.datadir", "install data")176v("sysconfdir", "install.sysconfdir", "install system configuration files")177v("infodir", "install.infodir", "install additional info")178v("libdir", "install.libdir", "install libraries")179v("mandir", "install.mandir", "install man pages in PATH")180v("docdir", "install.docdir", "install documentation in PATH")181v("bindir", "install.bindir", "install binaries")182183v("llvm-root", None, "set LLVM root")184v("llvm-config", None, "set path to llvm-config")185v("llvm-filecheck", None, "set path to LLVM's FileCheck utility")186v("python", "build.python", "set path to python")187v("android-ndk", "build.android-ndk", "set path to Android NDK")188v(189 "musl-root",190 "target.x86_64-unknown-linux-musl.musl-root",191 "MUSL root installation directory (deprecated)",192)193v(194 "musl-root-x86_64",195 "target.x86_64-unknown-linux-musl.musl-root",196 "x86_64-unknown-linux-musl install directory",197)198v(199 "musl-root-i586",200 "target.i586-unknown-linux-musl.musl-root",201 "i586-unknown-linux-musl install directory",202)203v(204 "musl-root-i686",205 "target.i686-unknown-linux-musl.musl-root",206 "i686-unknown-linux-musl install directory",207)208v(209 "musl-root-arm",210 "target.arm-unknown-linux-musleabi.musl-root",211 "arm-unknown-linux-musleabi install directory",212)213v(214 "musl-root-armhf",215 "target.arm-unknown-linux-musleabihf.musl-root",216 "arm-unknown-linux-musleabihf install directory",217)218v(219 "musl-root-armv5te",220 "target.armv5te-unknown-linux-musleabi.musl-root",221 "armv5te-unknown-linux-musleabi install directory",222)223v(224 "musl-root-armv7",225 "target.armv7-unknown-linux-musleabi.musl-root",226 "armv7-unknown-linux-musleabi install directory",227)228v(229 "musl-root-armv7hf",230 "target.armv7-unknown-linux-musleabihf.musl-root",231 "armv7-unknown-linux-musleabihf install directory",232)233v(234 "musl-root-aarch64",235 "target.aarch64-unknown-linux-musl.musl-root",236 "aarch64-unknown-linux-musl install directory",237)238v(239 "musl-root-mips",240 "target.mips-unknown-linux-musl.musl-root",241 "mips-unknown-linux-musl install directory",242)243v(244 "musl-root-mipsel",245 "target.mipsel-unknown-linux-musl.musl-root",246 "mipsel-unknown-linux-musl install directory",247)248v(249 "musl-root-mips64",250 "target.mips64-unknown-linux-muslabi64.musl-root",251 "mips64-unknown-linux-muslabi64 install directory",252)253v(254 "musl-root-mips64el",255 "target.mips64el-unknown-linux-muslabi64.musl-root",256 "mips64el-unknown-linux-muslabi64 install directory",257)258v(259 "musl-root-powerpc64",260 "target.powerpc64-unknown-linux-musl.musl-root",261 "powerpc64-unknown-linux-musl install directory",262)263v(264 "musl-root-powerpc64le",265 "target.powerpc64le-unknown-linux-musl.musl-root",266 "powerpc64le-unknown-linux-musl install directory",267)268v(269 "musl-root-riscv32gc",270 "target.riscv32gc-unknown-linux-musl.musl-root",271 "riscv32gc-unknown-linux-musl install directory",272)273v(274 "musl-root-riscv64gc",275 "target.riscv64gc-unknown-linux-musl.musl-root",276 "riscv64gc-unknown-linux-musl install directory",277)278v(279 "musl-root-loongarch64",280 "target.loongarch64-unknown-linux-musl.musl-root",281 "loongarch64-unknown-linux-musl install directory",282)283v(284 "musl-root-wali-wasm32",285 "target.wasm32-wali-linux-musl.musl-root",286 "wasm32-wali-linux-musl install directory",287)288v(289 "qemu-armhf-rootfs",290 "target.arm-unknown-linux-gnueabihf.qemu-rootfs",291 "rootfs in qemu testing, you probably don't want to use this",292)293v(294 "qemu-aarch64-rootfs",295 "target.aarch64-unknown-linux-gnu.qemu-rootfs",296 "rootfs in qemu testing, you probably don't want to use this",297)298v(299 "qemu-riscv64-rootfs",300 "target.riscv64gc-unknown-linux-gnu.qemu-rootfs",301 "rootfs in qemu testing, you probably don't want to use this",302)303v(304 "experimental-targets",305 "llvm.experimental-targets",306 "experimental LLVM targets to build",307)308v("release-channel", "rust.channel", "the name of the release channel to build")309v(310 "release-description",311 "build.description",312 "optional descriptive string for version output",313)314v("dist-compression-formats", None, "List of compression formats to use")315316# Used on systems where "cc" is unavailable317v("default-linker", "rust.default-linker", "the default linker")318319# Many of these are saved below during the "writing configuration" step320# (others are conditionally saved).321o("manage-submodules", "build.submodules", "let the build manage the git submodules")322o(323 "full-bootstrap",324 "build.full-bootstrap",325 "build three compilers instead of two (not recommended except for testing reproducible builds)",326)327o("extended", "build.extended", "build an extended rust tool set")328329v("bootstrap-cache-path", None, "use provided path for the bootstrap cache")330v("tools", None, "List of extended tools will be installed")331v("codegen-backends", None, "List of codegen backends to build")332v("build", "build.build", "GNUs ./configure syntax LLVM build triple")333v("host", None, "List of GNUs ./configure syntax LLVM host triples")334v("target", None, "List of GNUs ./configure syntax LLVM target triples")335336# Options specific to this configure script337o(338 "option-checking",339 None,340 "complain about unrecognized options in this configure script",341)342o(343 "verbose-configure",344 None,345 "don't truncate options when printing them in this configure script",346)347v("set", None, "set arbitrary key/value pairs in TOML configuration")348v(349 "parallel-frontend-threads",350 "rust.parallel-frontend-threads",351 "number of parallel threads for rustc compilation",352)353354355def p(msg):356 print("configure: " + msg)357358359def err(msg):360 print("\nconfigure: ERROR: " + msg + "\n")361 sys.exit(1)362363364def is_value_list(key):365 for option in options:366 if option.name == key and option.desc.startswith("List of"):367 return True368 return False369370371if "--help" in sys.argv or "-h" in sys.argv:372 print("Usage: ./configure [options]")373 print("")374 print("Options")375 for option in options:376 if "android" in option.name:377 # no one needs to know about these obscure options378 continue379 if option.value:380 print("\t{:30} {}".format("--{}=VAL".format(option.name), option.desc))381 else:382 print("\t--enable-{:25} OR --disable-{}".format(option.name, option.name))383 print("\t\t" + option.desc)384 print("")385 print("This configure script is a thin configuration shim over the true")386 print("configuration system, `bootstrap.toml`. You can explore the comments")387 print("in `bootstrap.example.toml` next to this configure script to see")388 print("more information about what each option is. Additionally you can")389 print("pass `--set` as an argument to set arbitrary key/value pairs")390 print("in the TOML configuration if desired")391 print("")392 print("Also note that all options which take `--enable` can similarly")393 print("be passed with `--disable-foo` to forcibly disable the option")394 sys.exit(0)395396VERBOSE = False397398399# Parse all command line arguments into one of these three lists, handling400# boolean and value-based options separately401def parse_args(args):402 unknown_args = []403 need_value_args = []404 known_args = {}405406 i = 0407 while i < len(args):408 arg = args[i]409 i += 1410 if not arg.startswith("--"):411 unknown_args.append(arg)412 continue413414 found = False415 for option in options:416 value = None417 if option.value:418 keyval = arg[2:].split("=", 1)419 key = keyval[0]420 if option.name != key:421 continue422423 if len(keyval) > 1:424 value = keyval[1]425 elif i < len(args):426 value = args[i]427 i += 1428 else:429 need_value_args.append(arg)430 continue431 else:432 if arg[2:] == "enable-" + option.name:433 value = True434 elif arg[2:] == "disable-" + option.name:435 value = False436 else:437 continue438439 found = True440 if option.name not in known_args:441 known_args[option.name] = []442 known_args[option.name].append((option, value))443 break444445 if not found:446 unknown_args.append(arg)447448 # NOTE: here and a few other places, we use [-1] to apply the *last* value449 # passed. But if option-checking is enabled, then the known_args loop will450 # also assert that options are only passed once.451 option_checking = (452 "option-checking" not in known_args or known_args["option-checking"][-1][1]453 )454 if option_checking:455 if len(unknown_args) > 0:456 err("Option '" + unknown_args[0] + "' is not recognized")457 if len(need_value_args) > 0:458 err("Option '{0}' needs a value ({0}=val)".format(need_value_args[0]))459460 global VERBOSE461 VERBOSE = "verbose-configure" in known_args462463 config = {}464465 set("build.configure-args", args, config)466 apply_args(known_args, option_checking, config)467 return parse_example_config(known_args, config)468469470def build(known_args):471 if "build" in known_args:472 return known_args["build"][-1][1]473 return bootstrap.default_build_triple(verbose=False)474475476def set(key, value, config):477 if isinstance(value, list):478 # Remove empty values, which value.split(',') tends to generate and479 # replace single quotes for double quotes to ensure correct parsing.480 value = [v.replace("'", '"') for v in value if v]481482 s = "{:20} := {}".format(key, value)483 if len(s) < 70 or VERBOSE:484 p(s)485 else:486 p(s[:70] + " ...")487488 arr = config489490 # Split `key` on periods using shell semantics.491 lexer = shlex.shlex(key, posix=True)492 lexer.whitespace = "."493 lexer.wordchars += "-"494 parts = list(lexer)495496 for i, part in enumerate(parts):497 if i == len(parts) - 1:498 if is_value_list(part) and isinstance(value, str):499 value = value.split(",")500 arr[part] = value501 else:502 if part not in arr:503 arr[part] = {}504 arr = arr[part]505506507def apply_args(known_args, option_checking, config):508 for key in known_args:509 # The `set` option is special and can be passed a bunch of times510 if key == "set":511 for _option, value in known_args[key]:512 keyval = value.split("=", 1)513 if len(keyval) == 1 or keyval[1] == "true":514 value = True515 elif keyval[1] == "false":516 value = False517 else:518 value = keyval[1]519 set(keyval[0], value, config)520 continue521522 # Ensure each option is only passed once523 arr = known_args[key]524 if option_checking and len(arr) > 1:525 err("Option '{}' provided more than once".format(key))526 option, value = arr[-1]527528 # If we have a clear avenue to set our value in rustbuild, do so529 if option.rustbuild is not None:530 set(option.rustbuild, value, config)531 continue532533 # Otherwise we're a "special" option and need some extra handling, so do534 # that here.535 build_triple = build(known_args)536537 if option.name == "sccache":538 set("build.ccache", "sccache", config)539 elif option.name == "local-rust":540 for path in os.environ["PATH"].split(os.pathsep):541 if os.path.exists(path + "/rustc"):542 set("build.rustc", path + "/rustc", config)543 break544 for path in os.environ["PATH"].split(os.pathsep):545 if os.path.exists(path + "/cargo"):546 set("build.cargo", path + "/cargo", config)547 break548 elif option.name == "local-rust-root":549 set("build.rustc", value + "/bin/rustc", config)550 set("build.cargo", value + "/bin/cargo", config)551 elif option.name == "llvm-root":552 set(553 "target.{}.llvm-config".format(build_triple),554 value + "/bin/llvm-config",555 config,556 )557 elif option.name == "llvm-config":558 set("target.{}.llvm-config".format(build_triple), value, config)559 elif option.name == "llvm-filecheck":560 set("target.{}.llvm-filecheck".format(build_triple), value, config)561 elif option.name == "tools":562 set("build.tools", value.split(","), config)563 elif option.name == "bootstrap-cache-path":564 set("build.bootstrap-cache-path", value, config)565 elif option.name == "codegen-backends":566 set("rust.codegen-backends", value.split(","), config)567 elif option.name == "host":568 set("build.host", value.split(","), config)569 elif option.name == "target":570 set("build.target", value.split(","), config)571 elif option.name == "full-tools":572 set("rust.codegen-backends", ["llvm"], config)573 set("rust.lld", True, config)574 set("rust.llvm-tools", True, config)575 set("rust.llvm-bitcode-linker", True, config)576 set("build.extended", True, config)577 elif option.name in ["option-checking", "verbose-configure"]:578 # this was handled above579 pass580 elif option.name == "dist-compression-formats":581 set("dist.compression-formats", value.split(","), config)582 else:583 raise RuntimeError("unhandled option {}".format(option.name))584585586# "Parse" the `bootstrap.example.toml` file into the various sections, and we'll587# use this as a template of a `bootstrap.toml` to write out which preserves588# all the various comments and whatnot.589#590# Note that the `target` section is handled separately as we'll duplicate it591# per configured target, so there's a bit of special handling for that here.592def parse_example_config(known_args, config):593 sections = {}594 cur_section = None595 sections[None] = []596 section_order = [None]597 targets = {}598 top_level_keys = []599 comment_lines = []600601 with open(rust_dir + "/bootstrap.example.toml", encoding="utf-8") as example_config:602 example_lines = example_config.read().split("\n")603 for line in example_lines:604 if line.count("=") >= 1 and not line.startswith("# "):605 key = line.split("=")[0]606 key = key.strip(" #")607 parts = key.split(".")608 if len(parts) > 1:609 cur_section = parts[0]610 if cur_section not in sections:611 sections[cur_section] = ["[" + cur_section + "]"]612 section_order.append(cur_section)613 elif cur_section is None:614 top_level_keys.append(key)615 # put the comment lines within the start of616 # a new section, not outside it.617 sections[cur_section] += comment_lines618 comment_lines = []619 # remove just the `section.` part from the line, if present.620 sections[cur_section].append(621 re.sub("(#?)([a-zA-Z_-]+\\.)?(.*)", "\\1\\3", line)622 )623 elif line.startswith("["):624 cur_section = line[1:-1]625 if cur_section.startswith("target"):626 cur_section = "target"627 elif "." in cur_section:628 raise RuntimeError(629 "don't know how to deal with section: {}".format(cur_section)630 )631 sections[cur_section] = [line]632 section_order.append(cur_section)633 else:634 comment_lines.append(line)635636 sections[cur_section] += comment_lines637 # Fill out the `targets` array by giving all configured targets a copy of the638 # `target` section we just loaded from the example config639 configured_targets = [build(known_args)]640 if "build" in config:641 if "host" in config["build"]:642 configured_targets += config["build"]["host"]643 if "target" in config["build"]:644 configured_targets += config["build"]["target"]645 if "target" in config:646 for target in config["target"]:647 configured_targets.append(target)648 for target in configured_targets:649 targets[target] = sections["target"][:]650 # For `.` to be valid TOML, it needs to be quoted. But `bootstrap.py` doesn't use a proper TOML parser and fails to parse the target.651 # Avoid using quotes unless it's necessary.652 targets[target][0] = targets[target][0].replace(653 "x86_64-unknown-linux-gnu",654 "'{}'".format(target) if "." in target else target,655 )656657 if "profile" not in config:658 set("profile", "dist", config)659 configure_file(sections, top_level_keys, targets, config)660 return section_order, sections, targets661662663def is_number(value):664 try:665 float(value)666 return True667 except ValueError:668 return False669670671# Here we walk through the constructed configuration we have from the parsed672# command line arguments. We then apply each piece of configuration by673# basically just doing a `sed` to change the various configuration line to what674# we've got configure.675def to_toml(value):676 if isinstance(value, bool):677 if value:678 return "true"679 else:680 return "false"681 elif isinstance(value, list):682 return "[" + ", ".join(map(to_toml, value)) + "]"683 elif isinstance(value, str):684 # Don't put quotes around numeric values685 if is_number(value):686 return value687 else:688 return "'" + value + "'"689 elif isinstance(value, dict):690 return (691 "{"692 + ", ".join(693 map(694 lambda a: "{} = {}".format(to_toml(a[0]), to_toml(a[1])),695 value.items(),696 )697 )698 + "}"699 )700 else:701 raise RuntimeError("no toml")702703704def configure_section(lines, config):705 for key in config:706 value = config[key]707 found = False708 for i, line in enumerate(lines):709 if not line.startswith("#" + key + " = "):710 continue711 found = True712 lines[i] = "{} = {}".format(key, to_toml(value))713 break714 if not found:715 # These are used by rpm, but aren't accepted by x.py.716 # Give a warning that they're ignored, but not a hard error.717 if key in ["infodir", "localstatedir"]:718 print("WARNING: {} will be ignored".format(key))719 else:720 raise RuntimeError("failed to find config line for {}".format(key))721722723def configure_top_level_key(lines, top_level_key, value):724 for i, line in enumerate(lines):725 if line.startswith("#" + top_level_key + " = ") or line.startswith(726 top_level_key + " = "727 ):728 lines[i] = "{} = {}".format(top_level_key, to_toml(value))729 return730731 raise RuntimeError("failed to find config line for {}".format(top_level_key))732733734# Modify `sections` to reflect the parsed arguments and example configs.735def configure_file(sections, top_level_keys, targets, config):736 for section_key, section_config in config.items():737 if section_key not in sections and section_key not in top_level_keys:738 raise RuntimeError(739 "config key {} not in sections or top_level_keys".format(section_key)740 )741 if section_key in top_level_keys:742 configure_top_level_key(sections[None], section_key, section_config)743744 elif section_key == "target":745 for target in section_config:746 configure_section(targets[target], section_config[target])747 else:748 configure_section(sections[section_key], section_config)749750751def write_uncommented(target, f):752 """Writes each block in 'target' that is not composed entirely of comments to 'f'.753754 A block is a sequence of non-empty lines separated by empty lines.755 """756 block = []757758 def flush(last):759 # If the block is entirely made of comments, ignore it760 entire_block_comments = all(ln.startswith("#") or ln == "" for ln in block)761 if not entire_block_comments and len(block) > 0:762 for line in block:763 f.write(line + "\n")764 # Required to output a newline before the start of a new section765 if last:766 f.write("\n")767 block.clear()768769 for line in target:770 block.append(line)771 if len(line) == 0:772 flush(last=False)773774 flush(last=True)775 return f776777778def write_config_toml(writer, section_order, targets, sections):779 for section in section_order:780 if section == "target":781 for target in targets:782 writer = write_uncommented(targets[target], writer)783 else:784 writer = write_uncommented(sections[section], writer)785786787def quit_if_file_exists(file):788 if os.path.isfile(file):789 msg = "Existing '{}' detected. Exiting".format(file)790791 # If the output object directory isn't empty, we can get these errors792 host_objdir = os.environ.get("OBJDIR_ON_HOST")793 if host_objdir is not None:794 msg += "\nIs objdir '{}' clean?".format(host_objdir)795796 err(msg)797798799if __name__ == "__main__":800 # If 'bootstrap.toml' already exists, exit the script at this point801 quit_if_file_exists("bootstrap.toml")802803 if "GITHUB_ACTIONS" in os.environ:804 print("::group::Configure the build")805 p("processing command line")806 # Parse all known arguments into a configuration structure that reflects the807 # TOML we're going to write out808 p("")809 section_order, sections, targets = parse_args(sys.argv[1:])810811 # Now that we've built up our `bootstrap.toml`, write it all out in the same812 # order that we read it in.813 p("")814 p("writing `bootstrap.toml` in current directory")815 with bootstrap.output("bootstrap.toml") as f:816 write_config_toml(f, section_order, targets, sections)817818 with bootstrap.output("Makefile") as f:819 contents = os.path.join(rust_dir, "src", "bootstrap", "mk", "Makefile.in")820 contents = open(contents).read()821 contents = contents.replace("$(CFG_SRC_DIR)", rust_dir + "/")822 contents = contents.replace("$(CFG_PYTHON)", sys.executable)823 f.write(contents)824825 p("")826 p("run `{} {}/x.py --help`".format(os.path.basename(sys.executable), rust_dir))827 if "GITHUB_ACTIONS" in os.environ:828 print("::endgroup::")
Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.