malvin 0.2.7

Non-interactive research and coding agent
use std::env;
use std::fs;
use std::path::{Path, PathBuf};
use std::process::Command;

pub(super) fn emit_fast_bin_linker_args() {
    if env::var_os("MALVIN_DISABLE_LLD").is_some() {
        return;
    }
    if emit_mold_bin_linker_args() {
        return;
    }
    emit_rustup_lld_bin_linker_args();
}

pub(super) fn emit_dev_dynamic_rpaths() {
    if env::var_os("MALVIN_DISABLE_DYNAMIC_RPATH").is_some() {
        return;
    }
    println!("cargo:rerun-if-env-changed=MALVIN_DISABLE_DYNAMIC_RPATH");
    println!("cargo:rustc-link-arg=-Wl,-rpath,$ORIGIN");
    println!("cargo:rustc-link-arg=-Wl,-rpath,$ORIGIN/deps");
    let Some(libdir) = rustc_target_libdir() else {
        return;
    };
    println!("cargo:rustc-link-arg=-Wl,-rpath,{}", libdir.display());
    stage_libstd_for_rpath(&libdir);
}

fn stage_libstd_for_rpath(libdir: &Path) {
    let Ok(entries) = fs::read_dir(libdir) else {
        return;
    };
    let Some(deps_dir) = cargo_deps_dir_from_out_dir() else {
        return;
    };
    let _ = fs::create_dir_all(&deps_dir);
    for entry in entries.flatten() {
        let name = entry.file_name();
        let Some(name) = name.to_str() else {
            continue;
        };
        if !is_libstd_shared_object(name) {
            continue;
        }
        copy_libstd_so_if_missing(&entry.path(), &deps_dir.join(name));
    }
}

fn cargo_deps_dir_from_out_dir() -> Option<PathBuf> {
    let out_dir = PathBuf::from(env::var_os("OUT_DIR")?);
    out_dir
        .parent()
        .and_then(|p| p.parent())
        .and_then(|p| p.parent())
        .map(|p| p.join("deps"))
}

fn is_libstd_shared_object(name: &str) -> bool {
    name.starts_with("libstd-")
        && Path::new(name)
            .extension()
            .is_some_and(|ext| ext.eq_ignore_ascii_case("so"))
}

fn copy_libstd_so_if_missing(src: &Path, dest: &Path) {
    if dest.exists() {
        return;
    }
    if let Err(e) = fs::copy(src, dest) {
        eprintln!(
            "cargo:warning=malvin: stage {} for rpath: {e}",
            src.display()
        );
    }
}

fn rustc_target_libdir() -> Option<PathBuf> {
    let rustc = env::var_os("RUSTC").map_or_else(|| PathBuf::from("rustc"), PathBuf::from);
    let output = Command::new(rustc)
        .args(["--print", "target-libdir"])
        .output()
        .ok()?;
    if !output.status.success() {
        return None;
    }
    let libdir = String::from_utf8_lossy(&output.stdout);
    let libdir = PathBuf::from(libdir.trim());
    libdir.is_dir().then_some(libdir)
}

fn emit_mold_bin_linker_args() -> bool {
    let Some(mold) = resolve_mold_bin() else {
        return false;
    };
    let Some(shim_dir) = mold_shim_dir() else {
        return false;
    };
    if let Err(e) = fs::create_dir_all(&shim_dir) {
        eprintln!(
            "cargo:warning=malvin: mold shim dir {}: {e}",
            shim_dir.display()
        );
        return false;
    }
    let ld_shim = shim_dir.join("ld");
    if !ld_shim.exists() {
        #[cfg(unix)]
        {
            if let Err(e) = std::os::unix::fs::symlink(&mold, &ld_shim) {
                eprintln!(
                    "cargo:warning=malvin: mold shim symlink {}: {e}",
                    ld_shim.display()
                );
                return false;
            }
        }
        #[cfg(not(unix))]
        {
            let _ = mold;
            return false;
        }
    }
    println!("cargo:rustc-link-arg-bins=-B{}", shim_dir.display());
    true
}

fn emit_rustup_lld_bin_linker_args() {
    let Some(gcc_ld) = rustc_gcc_ld_dir() else {
        return;
    };
    if !gcc_ld.join("ld.lld").is_file() {
        return;
    }
    println!("cargo:rustc-link-arg-bins=-B{}", gcc_ld.display());
    println!("cargo:rustc-link-arg-bins=-fuse-ld=lld");
}

fn resolve_mold_bin() -> Option<PathBuf> {
    if let Some(path) = env::var_os("MOLD") {
        let path = PathBuf::from(path);
        if path.is_file() {
            return Some(path);
        }
    }
    let output = Command::new("sh")
        .args(["-c", "command -v mold"])
        .output()
        .ok()?;
    if !output.status.success() {
        return None;
    }
    let path = String::from_utf8_lossy(&output.stdout);
    let path = PathBuf::from(path.trim());
    path.is_file().then_some(path)
}

fn mold_shim_dir() -> Option<PathBuf> {
    let out = env::var_os("OUT_DIR")?;
    Some(PathBuf::from(out).join("mold-bin-shim"))
}

pub(super) fn rustc_gcc_ld_dir() -> Option<PathBuf> {
    let sysroot = rustc_sysroot()?;
    let target = cargo_target_triple();
    Some(
        PathBuf::from(sysroot)
            .join("lib/rustlib")
            .join(target)
            .join("bin/gcc-ld"),
    )
}

fn rustc_sysroot() -> Option<String> {
    let rustc = env::var_os("RUSTC").map_or_else(|| PathBuf::from("rustc"), PathBuf::from);
    let output = Command::new(rustc)
        .args(["--print", "sysroot"])
        .output()
        .ok()?;
    if !output.status.success() {
        return None;
    }
    let sysroot = String::from_utf8_lossy(&output.stdout);
    let sysroot = sysroot.trim();
    if sysroot.is_empty() {
        None
    } else {
        Some(sysroot.to_string())
    }
}

fn cargo_target_triple() -> String {
    env::var("TARGET")
        .or_else(|_| env::var("HOST"))
        .unwrap_or_else(|_| "x86_64-unknown-linux-gnu".to_string())
}