view libpam-sys/libpam-sys-test/build.rs @ 113:178310336596

Fix up more constants, make things i32 rather than u32.
author Paul Fisher <paul@pfish.zone>
date Sun, 29 Jun 2025 03:11:33 -0400
parents 04105e9a7de8
children 93d423b65555
line wrap: on
line source

use bindgen::MacroTypeVariation;
use libpam_sys_impls::cfg_pam_impl;
use quote::{format_ident, ToTokens};
use std::path::PathBuf;
use std::{env, fs};
use syn::{Ident, Item, ItemConst, Path, Type, TypePath};

fn main() {
    generate_const_test();
}

#[cfg_pam_impl("LinuxPam")]
fn test_config() -> TestConfig {
    TestConfig {
        headers: vec![
            "security/_pam_types.h".into(),
            "security/pam_appl.h".into(),
            "security/pam_ext.h".into(),
            "security/pam_modules.h".into(),
        ],
        ignore_consts: vec!["__LINUX_PAM__".into(), "__LINUX_PAM_MINOR__".into()],
    }
}

#[cfg_pam_impl("OpenPam")]
fn test_config() -> TestConfig {
    TestConfig {
        headers: vec![
            "security/pam_types.h".into(),
            "security/openpam.h".into(),
            "security/pam_appl.h".into(),
            "security/pam_constants.h".into(),
        ],
        ignore_consts: vec!["OPENPAM_VERSION"],
    }
}

#[cfg_pam_impl(not(any("LinuxPam", "OpenPam")))]
fn test_config() -> TestConfig {
    panic!("This PAM implementation is not yet tested.")
}

fn generate_const_test() {
    let config = test_config();
    let builder = bindgen::Builder::default()
        .header_contents("_.h", &config.header_contents())
        .merge_extern_blocks(true)
        .parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
        .blocklist_type(".*")
        .blocklist_function(".*")
        .allowlist_var(".*")
        .default_macro_constant_type(MacroTypeVariation::Unsigned);

    let generated = builder.generate().unwrap().to_string();
    let file = syn::parse_file(&generated).unwrap();
    let mut tests = vec![];
    tests.push("{".into());
    tests.extend(
        file.items
            .iter()
            .filter_map(|item| {
                if let Item::Const(item) = item {
                    Some(item)
                } else {
                    None
                }
            })
            .filter(|item| config.should_check_const(item))
            .cloned()
            .map(|mut item| {
                item.ty = Box::new(Type::Path(TypePath {
                    qself: None,
                    path: format_ident!("i32").into(),
                }));
                format!(
                    "assert_eq!({tokens}, libpam_sys::{name});",
                    tokens = item.expr.to_token_stream(),
                    name = item.ident
                )
            }),
    );
    tests.push("}".into());
    fs::write(
        PathBuf::from(env::var("OUT_DIR").unwrap()).join("constant_test.rs"),
        tests.join("\n"),
    )
    .unwrap();
}

struct TestConfig {
    headers: Vec<String>,
    ignore_consts: Vec<String>,
}

impl TestConfig {
    fn header_contents(&self) -> String {
        let vec: Vec<_> = self
            .headers
            .iter()
            .map(|h| format!("#include <{h}>\n"))
            .collect();
        vec.join("")
    }

    fn should_check_const(&self, item: &ItemConst) -> bool {
        !self.ignore_consts.contains(&item.ident.to_string())
    }
}