GaloisInc / crucible

Crucible is a library for symbolic simulation of imperative programs
628 stars 42 forks source link

`crux-mir`: Automate generation of `translate_libs.sh` script #1152

Open RyanGlScott opened 9 months ago

RyanGlScott commented 9 months ago

Currently, crux-mir's translate_libs.sh script is written by hand. This makes it tedious to maintain, especially when new rustc toolchains alter the dependencies used in the Rust standard libraries. We should include a program that generates the translate_libs.sh script in a more declarative way. @m10f started work on this with this program:

```py # gen_build_sh.py import sys import shlex import os import shutil from typing import Iterator def crate_loc(path: str): (parent, _) = os.path.split(path) (parent2, parent_name) = os.path.split(parent) if parent_name == "src": return parent2 return parent def has_arg(args: list[str], n: str) -> bool: return n in args def get_args(args: list[str], n: str) -> Iterator[str]: for arg_idx in range(0, len(args)): arg = args[arg_idx] if arg == n and arg_idx + 1 < len(args): yield args[arg_idx + 1] def get_arg(args: list[str], n: str) -> str: for x in get_args(args, n): return x raise RuntimeError(f"Missing required arg: {n} args: {args}") def parse(inp): for line in inp: line = str(line) if "--target" not in line: continue line = line.removeprefix("Running ").strip(" ").strip("`") args = shlex.split(line) crate_name = get_arg(args, "--crate-name") edition = None if has_arg(args, "--edition=2021"): edition = 2021 if has_arg(args, "--edition=2018"): edition = 2018 if has_arg(args, "--edition=2015"): edition = 2015 cfgs = get_args(args, "--cfg") externs = [ x.split("=")[0] for x in get_args(args, "--extern") ] # this is kinda brittle but it's not really tagged lib_path = next(a for a in args if a.endswith(".rs")) crate_path = crate_loc(lib_path) yield { "edition": edition, "crate_name": crate_name, "cfgs": cfgs, "externs": externs, "crate_path": crate_path, "lib_path": lib_path, } def copy(crate, srcpath): crate_path = crate["crate_path"] shutil.copytree(crate_path, os.path.join(srcpath, crate["crate_name"])) def build_cmd(crate, libdir, srcpath): cfg_args = [f"--cfg {shlex.quote(c)}" for c in crate["cfgs"]] edition = crate["edition"] externs = crate["externs"] crate_name = crate["crate_name"] crate_path = crate["crate_path"] lib_path = crate["lib_path"] lib_rel_path = os.path.relpath(lib_path, crate_path) target_path = os.path.join(srcpath, crate_name, lib_rel_path) extern_args = [f"--extern {e}={os.path.join(libdir, 'lib' + e + '.rlib') }" for e in externs] cmd = [ "mir-json", target_path, f"--edition={edition}" if edition is not None else "", f"--crate-name {crate_name}", f"-L {libdir}", f"--out-dir {libdir}", f"--crate-type rlib", ] + cfg_args + extern_args return " ".join(cmd) crates = parse(sys.stdin) if sys.argv[1] == "copy": for crate in crates: copy(crate, "lib") elif sys.argv[1] == "buildscript": print("set -e") for crate in crates: print(f"echo 'Building {crate['crate_name']}...'") print(build_cmd(crate, "rlibs", "lib")) print() ```

Where it is intended to be used with gen_build_sh.py buildscript < log_from_building_an_empty_project, where "log_from_building_an_empty_project" is the result of running something like cargo build -Z build-std --verbose.

The script also includes a copy mode that can be used to copy the source code for the Rust standard libraries into a central location. This could be used to automate the process of updating crux-mir/lib to a new rustc toolchain.