ax-posix-api 0.5.13

POSIX-compatible APIs for ArceOS modules
fn main() {
    use std::io::Write;

    fn pthread_mutex_layout(
        has_multitask: bool,
        has_smp: bool,
        has_lockdep: bool,
    ) -> (usize, &'static str) {
        if !has_multitask {
            return (1, "{0}");
        }

        if has_lockdep {
            if has_smp {
                // `lockdep` expands the underlying mutex with lock maps in both
                // `RawMutex` and the embedded wait-queue spinlock. Keep the C
                // pthread mutex layout exactly aligned with the Rust side.
                // Static initialization under lockdep uses a sentinel and is
                // completed lazily in Rust on first use.
                return (10, "{-1, 0, 0, 0, 0, 0, 0, 0, 0, 0}");
            }
            return (9, "{-1, 0, 0, 0, 0, 0, 0, 0, 0}");
        }

        if has_smp {
            (6, "{0, 0, 8, 0, 0, 0}")
        } else {
            (5, "{0, 8, 0, 0, 0}")
        }
    }

    fn gen_pthread_mutex(out_file: &str) -> std::io::Result<()> {
        println!("cargo:rerun-if-env-changed=CARGO_FEATURE_MULTITASK");
        println!("cargo:rerun-if-env-changed=CARGO_FEATURE_SMP");
        println!("cargo:rerun-if-env-changed=CARGO_FEATURE_LOCKDEP");
        let has_multitask = std::env::var_os("CARGO_FEATURE_MULTITASK").is_some();
        let has_smp = std::env::var_os("CARGO_FEATURE_SMP").is_some();
        let has_lockdep = std::env::var_os("CARGO_FEATURE_LOCKDEP").is_some();
        let (mutex_size, mutex_init) = pthread_mutex_layout(has_multitask, has_smp, has_lockdep);

        let mut output = Vec::new();
        writeln!(
            output,
            "// Generated by ax-posix-api/build.rs - DO NOT edit!"
        )?;
        writeln!(
            output,
            r#"
typedef struct {{
    long __l[{mutex_size}];
}} pthread_mutex_t;

#define PTHREAD_MUTEX_INITIALIZER {{ .__l = {mutex_init} }}
"#
        )?;
        std::fs::write(out_file, output)?;
        Ok(())
    }

    fn gen_c_to_rust_bindings(in_file: &str, out_file: &std::path::Path) {
        println!("cargo:rerun-if-changed={in_file}");

        let allow_types = [
            "stat",
            "size_t",
            "ssize_t",
            "off_t",
            "mode_t",
            "sock.*",
            "fd_set",
            "timeval",
            "pthread_t",
            "pthread_attr_t",
            "pthread_mutex_t",
            "pthread_mutexattr_t",
            "epoll_event",
            "iovec",
            "clockid_t",
            "rlimit",
            "aibuf",
            "pollfd",
            "nfds_t",
        ];

        let allow_vars = [
            "CLOCK_.*",
            "O_.*",
            "AF_.*",
            "SOCK_.*",
            "IPPROTO_.*",
            "FD_.*",
            "F_.*",
            "_SC_.*",
            "EPOLL_CTL_.*",
            "EPOLL.*",
            "RLIMIT_.*",
            "EAI_.*",
            "MAXADDRS",
            "SO_.*",
            "SOL_.*",
            "TCP_.*",
            "POLL.*",
        ];

        #[derive(Debug)]
        struct MyCallbacks;

        impl bindgen::callbacks::ParseCallbacks for MyCallbacks {
            fn include_file(&self, fname: &str) {
                if !fname.contains("ax_pthread_mutex.h") {
                    println!("cargo:rerun-if-changed={fname}");
                }
            }
        }

        let target = std::env::var("TARGET").unwrap();
        let mut builder = bindgen::Builder::default()
            .header(in_file)
            .clang_arg("-I./../../ulib/axlibc/include")
            .parse_callbacks(Box::new(MyCallbacks))
            .derive_default(true)
            .size_t_is_usize(false)
            .use_core();
        for feature in ["MULTITASK", "SMP", "LOCKDEP"] {
            println!("cargo:rerun-if-env-changed=CARGO_FEATURE_{feature}");
            if std::env::var_os(format!("CARGO_FEATURE_{feature}")).is_some() {
                builder = builder.clang_arg(format!("-DAX_CONFIG_{feature}"));
            }
        }

        // Remove the "-softfloat" suffix for some targets.
        if let Some(llvm_target) = target.strip_suffix("-softfloat") {
            builder = builder.clang_arg(format!("--target={llvm_target}"));
        }

        for ty in allow_types {
            builder = builder.allowlist_type(ty);
        }

        for var in allow_vars {
            builder = builder.allowlist_var(var);
        }

        builder
            .generate()
            .expect("Unable to generate C-to-Rust bindings")
            .write_to_file(out_file)
            .expect("Couldn't write bindings!");
    }

    let axlibc_include = std::path::Path::new("../../ulib/axlibc/include");
    let out_dir = std::path::PathBuf::from(std::env::var_os("OUT_DIR").unwrap());
    let out_file = out_dir.join("ctypes_gen.rs");
    if axlibc_include.exists() {
        gen_pthread_mutex(axlibc_include.join("ax_pthread_mutex.h").to_str().unwrap()).unwrap();
        gen_c_to_rust_bindings("ctypes.h", &out_file);
    } else {
        // During `cargo publish` verification, the external headers are not
        // available. Use the pre-generated ctypes_gen.rs shipped in the crate.
        eprintln!(
            "cargo:warning=axlibc include directory not found, skipping ctypes_gen.rs regeneration"
        );
        std::fs::copy("src/ctypes_gen.rs", &out_file)
            .expect("pre-generated ctypes_gen.rs is not available");
    }
}