linux-syscalls 0.4.0

A library to easily invoke linux system calls.
Documentation
use std::fmt;

#[allow(clippy::needless_return)]
fn main() {
    println!("cargo:rerun-if-changed=build.rs");
    println!("cargo:rerun-if-env-changed=TARGET");
    println!("cargo:rerun-if-env-changed=CARGO_CFG_TARGET_OS");

    println!("cargo:rerun-if-env-changed=CARGO_CFG_DOCS_RS");
    if std::env::var("CARGO_CFG_DOCS_RS").is_ok() {
        use_feature("outline_syscalls");
        return;
    }

    let os = std::env::var("CARGO_CFG_TARGET_OS").unwrap();
    if !["linux", "android"].contains(&os.as_str()) {
        return;
    }

    println!("cargo:rerun-if-env-changed=CARGO_CFG_TARGET_ARCH");
    println!("cargo:rerun-if-env-changed=RUSTFLAGS");

    if std::env::var("CARGO_CFG_OUTLINE_SYSCALLS").is_ok()
        && std::env::var("CARGO_CFG_FORCE_INLINE_SYSCALLS").is_ok()
    {
        panic!("`--cfg outline_syscalls` and `--cfg force_inline_syscalls` are mutually exclusive");
    }

    let arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap();
    let pointer_width = std::env::var("CARGO_CFG_TARGET_POINTER_WIDTH").unwrap();
    let endian = std::env::var("CARGO_CFG_TARGET_ENDIAN").unwrap();

    match arch.as_str() {
        "x86_64" if pointer_width == "64" && endian == "little" => main_x86_64(),
        "x86" if pointer_width == "32" && endian == "little" => main_x86(),
        "aarch64" if pointer_width == "64" && endian == "little" => main_aarch64(),
        "arm" if pointer_width == "32" && endian == "little" => main_arm(),
        "riscv32" if pointer_width == "32" && endian == "little" && os == "linux" => main_riscv(),
        "riscv64" if pointer_width == "64" && endian == "little" && os == "linux" => main_riscv(),
        "powerpc" if pointer_width == "32" && endian == "big" && os == "linux" => main_powerpc(),
        "powerpc64" if pointer_width == "64" && os == "linux" => main_powerpc64(),
        "mips" if pointer_width == "32" && os == "linux" => main_mips(),
        "mips64" if pointer_width == "64" && os == "linux" => main_mips64(),
        "s390x" if pointer_width == "64" && endian == "big" && os == "linux" => main_s390x(),
        "loongarch64" if pointer_width == "64" && endian == "little" && os == "linux" => {
            main_loongarch64()
        }
        _ => {
            panic!(
                "arch {} {}-bits {} endian unsupported on {}",
                arch, pointer_width, endian, os
            );
        }
    }
}

fn main_x86_64() {
    if needs_outline_asm() {
        build_trampoline("x86_64")
    }
}

fn main_x86() {
    if needs_outline_asm() {
        build_trampoline("x86")
    }
}

fn main_aarch64() {
    if needs_outline_asm() {
        build_trampoline("aarch64")
    }
}

fn main_arm() {
    if has_thumb_mode() {
        use_feature("thumb_mode");
    }
    if needs_outline_asm() {
        build_trampoline("arm")
    }
}

fn main_riscv() {
    if needs_outline_asm() {
        build_trampoline("riscv")
    }
}

fn main_powerpc() {
    if needs_outline_asm() {
        build_trampoline("powerpc")
    }
}

fn main_powerpc64() {
    if needs_outline_asm() {
        build_trampoline("powerpc64")
    }
}

fn main_mips() {
    if needs_outline_asm() {
        build_trampoline("mips")
    }
}

fn main_mips64() {
    if needs_outline_asm() {
        build_trampoline("mips64")
    }
}

fn main_s390x() {
    if needs_outline_asm() {
        build_trampoline("s390x")
    }
}

fn main_loongarch64() {
    if needs_outline_asm() {
        build_trampoline("loongarch64")
    }
}

fn has_thumb_mode() -> bool {
    let target = std::env::var("TARGET").unwrap();
    target == "armv7-linux-androideabi" || target.starts_with("thumb")
}

fn build_trampoline(arch: &str) {
    println!("cargo:rerun-if-changed=src/outline/{}.s", arch);
    cc::Build::new()
        .cargo_metadata(true)
        .emit_rerun_if_env_changed(true)
        .file(format!("src/outline/{}.s", arch))
        .compile("liblinux_syscalls_rs.a");
}

fn needs_outline_asm() -> bool {
    const STABLE_59: [&str; 5] = ["x86", "arm", "x86_64", "aarch64", "riscv64"];

    if std::env::var("CARGO_CFG_OUTLINE_SYSCALLS").is_ok() {
        use_feature("outline_syscalls");
        true
    } else {
        let arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap();
        let version = rustc_version::version().unwrap();
        assert_eq!(version.major, 1);

        if version.minor >= 59 && STABLE_59.contains(&arch.as_str()) {
            return false;
        }

        let version_meta = rustc_version::version_meta().unwrap();

        if version_meta.channel == rustc_version::Channel::Nightly {
            use_feature("asm_experimental_arch");
            false
        } else {
            if std::env::var("CARGO_CFG_FORCE_INLINE_SYSCALLS").is_err() {
                use_feature("outline_syscalls");
            }
            true
        }
    }
}

fn use_feature<T: fmt::Display>(feat: T) {
    println!("cargo:rustc-cfg={}", feat);
}