load("@nix//toolchains:python.bzl", "nix_python_bootstrap_toolchain")
load("@prelude//toolchains:genrule.bzl", "system_genrule_toolchain")
load("@prelude//toolchains:remote_test_execution.bzl", "remote_test_execution_toolchain")
load("@prelude//tests:test_toolchain.bzl", "noop_test_toolchain")
load(":flake.bzl", "flake")
load(":lints.bzl", "ALLOW", "DENY", "DENY_ON_CHECK")
load(":rust.bzl", "rust_toolchain")
load(":cxx.bzl", "clang_toolchain")
load(":qemu.bzl", "qemu_toolchain")

flake.package(
    name = "python",
    binary = "python3",
    package = "python3",
    path = "root//:flake",
)

flake.package(
    name = "rust_toolchain",
    package = "rustToolchain",
    binaries = ["rustc", "cargo", "cargo-miri", "rustdoc", "clippy-driver", "miri", "rustfmt"],
    path = "root//:flake",
)

flake.package(
    name = "clang",
    binaries = [
        "ar",
        "cc",
        "c++",
        "ld",
        "nm",
        "objcopy",
        "ranlib",
        "strip",
    ],
    package = "clang_20",
    path = "root//:flake",
    visibility = ["PUBLIC"],
)

flake.package(
    name = "lld",
    package = "lld_20",
    binaries = ["ld.lld", "ld64.lld"],
    path = "root//:flake",
)

# Target-agnostic, unlike the GNU bintools clang_20's cc-wrapper pulls in.
flake.package(
    name = "llvm_bintools",
    package = "llvmBintools_20",
    binaries = ["llvm-objcopy", "llvm-strip"],
    path = "root//:flake",
    visibility = ["PUBLIC"],
)

flake.package(
    name = "cxx_runtime_lib",
    package = "cxxRuntimeLib",
    output = "lib",
    path = "root//:flake",
)

flake.package(
    name = "mdbook",
    binary = "mdbook",
    package = "mdbook",
    path = "root//:flake",
    visibility = ["PUBLIC"],
)

flake.package(
    name = "qemu_bin",
    binaries = [
        "qemu-system-riscv64",
        "qemu-system-aarch64",
        "qemu-system-x86_64",
        # add more emulators here if we ever support them
    ],
    package = "qemu",
    path = "root//:flake",
)

system_genrule_toolchain(
    name = "genrule",
    visibility = ["PUBLIC"],
)

nix_python_bootstrap_toolchain(
    name = "python_bootstrap",
    python = ":python",
    visibility = ["PUBLIC"],
)

rust_library(
    name = "rust_core",
    crate = "core",
    srcs = [":rust_toolchain"],
    crate_root = "out.link/lib/rustlib/src/rust/library/core/src/lib.rs",
    incoming_transition = "root//build:rust_bootstrap",
    visibility = ["PUBLIC"],
)

rust_library(
    name = "rust_compiler_builtins",
    crate = "compiler-builtins",
    srcs = [":rust_toolchain"],
    crate_root = "out.link/lib/rustlib/src/rust/library/compiler-builtins/compiler-builtins/src/lib.rs",
    features = ["compiler-builtins", "mem", "unmangled-names"],
    deps = [
        ":rust_core"
    ],
    incoming_transition = "root//build:rust_bootstrap",
    visibility = ["PUBLIC"],
)

rust_library(
    name = "rust_alloc",
    crate = "alloc",
    srcs = [":rust_toolchain"],
    crate_root = "out.link/lib/rustlib/src/rust/library/alloc/src/lib.rs",
    deps = [
        ":rust_core",
        ":rust_compiler_builtins"
    ],
    incoming_transition = "root//build:rust_bootstrap",
    visibility = ["PUBLIC"],
)

filegroup(
  name = 'rust_sysroot',
  srcs = [":rust_toolchain"],
)

# Miri can only interpret crates whose std/core/alloc carry full MIR
# (`-Zalways-encode-mir`). `cargo miri setup` rebuilds the standard library
# from the toolchain's bundled `rust-src` into a Miri-compatible sysroot.
#
# `cargo miri setup` shells out to cargo-miri, miri and rustc, so the flake
# toolchain's bin dir goes on PATH. buck hands genrules repo-relative paths
# and those subprocesses change directory, so $PWD absolutizes them.
genrule(
    name = "miri_sysroot",
    out = "sysroot",
    cmd = " && ".join([
        'export CARGO_HOME="$TMP/cargo"',
        'export PATH="$PWD/$(location :rust_toolchain)/bin:$PATH"',
        'export MIRI_SYSROOT="$PWD/$OUT"',
        '$(exe :rust_toolchain[cargo]) miri setup',
    ]),
)

export_file(
    name = "clippy.toml",
    src = "clippy.toml",
)

rust_toolchain(
    name = "rust",
    rustc = ":rust_toolchain[rustc]",
    clippy = ":rust_toolchain[clippy-driver]",
    clippy_toml = ":clippy.toml",
    rustdoc = ":rust_toolchain[rustdoc]",
    miri_driver = ":rust_toolchain[miri]",
    miri_sysroot_path = ":miri_sysroot",
    # Forwarded into miri tests that set this var in their target `env`
    # (mem-core uses it to disable proptest's getcwd-based persistence).
    miri_flags = ["-Zmiri-env-forward=PROPTEST_DISABLE_FAILURE_PERSISTENCE", "-Zmiri-env-set=PROPTEST_CASES=16"],
    default_edition = "2024",
    nightly_features = select({
        "constraints//:rust-std[bootstrap]": True,
        "DEFAULT": False,
    }),
    panic_runtime = select({
        "constraints//:env[loader]": "abort",
        "DEFAULT": "unwind",
    }),
    rustc_target_triple = select({
        "prelude//os:none": select({
            "constraints//:env[kernel]": select({
                "prelude//cpu:riscv64": "riscv64gc-k23-none-kernel",
                "prelude//cpu:arm64": "aarch64-unknown-none",
                "prelude//cpu:x86_64": "x86_64-unknown-none",
            }),
            "DEFAULT": select({
                "prelude//cpu:riscv64": "riscv64gc-unknown-none-elf",
                "prelude//cpu:arm64": "aarch64-unknown-none",
                "prelude//cpu:x86_64": "x86_64-unknown-none",
            }),
        }),
        "DEFAULT": None
    }),
    rust_target_path = "@root//build:targets",
    deny_lints = DENY,
    deny_on_check_lints = DENY_ON_CHECK,
    allow_lints = ALLOW,
    rustc_flags = [
        "-Zunstable-options",
    ] + select({
        "constraints//:env[loader]": [],
        "DEFAULT": ["-Cpanic=unwind"],
    }) + select({
        "constraints//:opt-level[0]": ["-Copt-level=0"],
        "constraints//:opt-level[3]": ["-Copt-level=3"],
    }) + select({
        "constraints//:debuginfo[none]": ["-Cdebuginfo=none"],
        "constraints//:debuginfo[line-directives-only]": ["-Cdebuginfo=line-directives-only"],
        "constraints//:debuginfo[line-tables-only]": ["-Cdebuginfo=line-tables-only"],
        "constraints//:debuginfo[limited]": ["-Cdebuginfo=limited"],
        "constraints//:debuginfo[full]": ["-Cdebuginfo=full"],
    }) + select({
        "constraints//:strip[none]": ["-Cstrip=none"],
        "constraints//:strip[debuginfo]": ["-Cstrip=debuginfo"],
        "constraints//:strip[symbols]": ["-Cstrip=symbols"],
    }) + select({
        "constraints//:lto[off]": [],
        "constraints//:lto[thin]": ["-Clto=thin"],
    }) + select({
        "constraints//:sanitizer[address]": [
            "-Zsanitizer=address",
            "-Cpasses=sancov-module",
            "-Cllvm-args=-sanitizer-coverage-level=4",
            "-Cllvm-args=-sanitizer-coverage-trace-compares",
            "-Cllvm-args=-sanitizer-coverage-inline-8bit-counters",
            "-Cllvm-args=-sanitizer-coverage-pc-table",
            # Keep dead code so sancov-instrumented functions aren't
            # eliminated by the linker before the fuzzer can reach them.
            "-Clink-dead-code",
        ] + select({
            "prelude//os:macos": ["-Clink-arg=-lc++"],
            "prelude//os:linux": ["-Clink-arg=-lstdc++"],
            "DEFAULT": [],
        }),
        "constraints//:sanitizer[none]": [],
    }),
    explicit_sysroot_deps = select({
        "constraints//:rust-std[bootstrap]": {},
        "constraints//:rust-std[source]": {
            "core": ":rust_core",
            "compiler-builtins": ":rust_compiler_builtins",
            "alloc": ":rust_alloc",
        },
        "constraints//:rust-std[prebuilt]": None,
    }),
    visibility = ["PUBLIC"],
)

clang_toolchain(
    name = "cxx",
    clang = ":clang",
    linker = select({
        "prelude//os:macos": ":lld[ld64.lld]",
        "DEFAULT": ":lld[ld.lld]",
    }),
    linker_dispatch = select({
        "prelude//os:none": "direct",
        "DEFAULT": "driver",
    }),
    cxx_runtime_lib = select({
        "prelude//os:none": None,
        "DEFAULT": ":cxx_runtime_lib",
    }),
    c_flags = select({
        "constraints//:opt-level[0]": ["-O0"],
        "constraints//:opt-level[3]": ["-O3"],
        "DEFAULT": [],
    }) + select({
        "constraints//:sanitizer[address]": ["-fsanitize=address"],
        "constraints//:sanitizer[none]": [],
    }),
    cxx_flags = select({
        "prelude//os:linux": ["-stdlib=libstdc++"],
        "DEFAULT": [],
    }) + select({
        "constraints//:opt-level[0]": ["-O0"],
        "constraints//:opt-level[3]": ["-O3"],
        "DEFAULT": [],
    }) + select({
        "constraints//:sanitizer[address]": ["-fsanitize=address"],
        "constraints//:sanitizer[none]": [],
    }),

    link_flags = select({
        "prelude//os:macos": [
            "-isysroot",
            "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk",
        ],
        "DEFAULT": [],
    }),
    visibility = ["PUBLIC"],
)

# The prelude's cxx toolchain selects between this and `:cxx` on a branch that can
# never be taken, but unconfigured graph queries still resolve both — without this
# target `uquery` cannot traverse the deps of any target that needs a cxx toolchain.
alias(name = "cxx_no_default_deps", actual = ":cxx", visibility = ["PUBLIC"])

qemu_toolchain(
    name = "qemu",
    qemu = ":qemu_bin",
    qemu_binary = select({
        "prelude//cpu:riscv64": "qemu-system-riscv64",
        "prelude//cpu:arm64": "qemu-system-aarch64",
        "prelude//cpu:x86_64": "qemu-system-x86_64",
    }),
    qemu_args = [
        # general
        "-display",
        "none",
        "-serial",
        "mon:stdio",
        "-d",
        "guest_errors,int",
        "-monitor",
        "unix:qemu-monitor-socket,server,nowait",

        # machine configuration
        "-m",
        "512M",
        "-smp",
        "cpus=8",
        "-object",
        "memory-backend-ram,size=128M,id=m0",
        "-object",
        "memory-backend-ram,size=128M,id=m1",
        # "-numa",
        # "node,cpus=0-3,nodeid=0,memdev=m0",
        # "-numa",
        # "node,cpus=4-7,nodeid=1,memdev=m1",
        # "-numa",
        # "dist,src=0,dst=1,val=20"
    ] + select({
        "prelude//cpu:riscv64": ["-machine", "virt,acpi=off", "-cpu", "rv64", "-semihosting-config", "enable=on,target=native"],
        "DEFAULT": []
    }),
    firmware_code_path = select({
        "prelude//cpu/constraints:riscv64": "share/qemu/edk2-riscv-code.fd",
        "DEFAULT": None
    }),
    firmware_vars_path = select({
        "prelude//cpu/constraints:riscv64": "share/qemu/edk2-riscv-vars.fd",
        "DEFAULT": None
    }),
    visibility = ["PUBLIC"],
)

noop_test_toolchain(
    name = "test",
    visibility = ["PUBLIC"],
)

remote_test_execution_toolchain(
    name = "remote_test_execution",
    visibility = ["PUBLIC"],
)
