Mercurial > crates > nonstick
comparison libpam-sys/libpam-sys-test/build.rs @ 131:a632a8874131
Get all the Linux-PAM functions into libpam-sys, and get tests right.
| author | Paul Fisher <paul@pfish.zone> |
|---|---|
| date | Wed, 02 Jul 2025 02:24:21 -0400 |
| parents | 80c07e5ab22f |
| children | 0b6a17f8c894 |
comparison
equal
deleted
inserted
replaced
| 130:80c07e5ab22f | 131:a632a8874131 |
|---|---|
| 4 use quote::{format_ident, ToTokens}; | 4 use quote::{format_ident, ToTokens}; |
| 5 use std::path::Path; | 5 use std::path::Path; |
| 6 use std::process::Command; | 6 use std::process::Command; |
| 7 use std::str::FromStr; | 7 use std::str::FromStr; |
| 8 use std::{env, fs}; | 8 use std::{env, fs}; |
| 9 use syn::{Item, ItemConst, Type, TypePath}; | 9 use syn::{Item, ItemConst}; |
| 10 | 10 |
| 11 // We're using the macro directly so we can match exhaustively. | 11 // We're using the macro directly so we can match exhaustively. |
| 12 __pam_impl_enum__!(); | 12 __pam_impl_enum__!(); |
| 13 | |
| 14 const REDIR_FD: &str = "pam_modutil_redirect_fd"; | |
| 13 | 15 |
| 14 fn main() { | 16 fn main() { |
| 15 let config = match PamImpl::CURRENT { | 17 let config = match PamImpl::CURRENT { |
| 16 PamImpl::LinuxPam => TestConfig { | 18 PamImpl::LinuxPam => TestConfig { |
| 17 headers: vec![ | 19 headers: vec![ |
| 18 "<security/_pam_types.h>", | 20 "<security/_pam_types.h>", |
| 19 "<security/pam_appl.h>", | 21 "<security/pam_appl.h>", |
| 20 "<security/pam_ext.h>", | 22 "<security/pam_ext.h>", |
| 21 "<security/pam_modules.h>", | 23 "<security/pam_modules.h>", |
| 24 "<security/pam_modutil.h>", | |
| 22 ], | 25 ], |
| 26 allow_types: vec![REDIR_FD], | |
| 23 ignore_consts: vec![ | 27 ignore_consts: vec![ |
| 24 "__LINUX_PAM__", | 28 "__LINUX_PAM__", |
| 25 "__LINUX_PAM_MINOR__", | 29 "__LINUX_PAM_MINOR__", |
| 26 "PAM_AUTHTOK_RECOVER_ERR", | 30 "PAM_AUTHTOK_RECOVER_ERR", |
| 27 ], | 31 ], |
| 37 ignore_consts: vec!["OPENPAM_VERSION", "OPENPAM_RELEASE", "PAM_SOEXT"], | 41 ignore_consts: vec!["OPENPAM_VERSION", "OPENPAM_RELEASE", "PAM_SOEXT"], |
| 38 ..Default::default() | 42 ..Default::default() |
| 39 }, | 43 }, |
| 40 PamImpl::Sun => TestConfig { | 44 PamImpl::Sun => TestConfig { |
| 41 headers: vec!["<security/pam_appl.h>", "<security/pam_modules.h>"], | 45 headers: vec!["<security/pam_appl.h>", "<security/pam_modules.h>"], |
| 42 block_headers: vec!["sys/.*"], | |
| 43 ..Default::default() | 46 ..Default::default() |
| 44 }, | 47 }, |
| 45 PamImpl::XSso => TestConfig { | 48 PamImpl::XSso => TestConfig { |
| 46 headers: vec!["\"xsso_pam_appl.h\""], | 49 headers: vec!["\"xsso_pam_appl.h\""], |
| 47 ignore_consts: vec!["PAM_CRED_PRELIM_CHECK"], | |
| 48 ..Default::default() | 50 ..Default::default() |
| 49 }, | 51 }, |
| 50 }; | 52 }; |
| 51 generate_const_test(&config); | 53 generate_const_test(&config); |
| 52 generate_ctest(&config); | 54 generate_ctest(&config); |
| 55 fn generate_const_test(config: &TestConfig) { | 57 fn generate_const_test(config: &TestConfig) { |
| 56 let mut builder = bindgen::Builder::default() | 58 let mut builder = bindgen::Builder::default() |
| 57 .header_contents("_.h", &config.header_contents()) | 59 .header_contents("_.h", &config.header_contents()) |
| 58 .merge_extern_blocks(true) | 60 .merge_extern_blocks(true) |
| 59 .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) | 61 .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) |
| 60 .blocklist_type(".*") | 62 .allowlist_var("(OPEN)?PAM_.*") |
| 61 .blocklist_function(".*") | |
| 62 .allowlist_var(".*") | |
| 63 .default_macro_constant_type(MacroTypeVariation::Signed); | 63 .default_macro_constant_type(MacroTypeVariation::Signed); |
| 64 for hdr in config.block_headers.iter() { | 64 |
| 65 builder = builder.blocklist_file(".*?/".to_owned() + hdr) | 65 for &typ in config.allow_types.iter() { |
| 66 } | 66 builder = builder.allowlist_type(typ); |
| 67 | 67 } |
| 68 let generated = builder.generate().unwrap().to_string(); | 68 |
| 69 let file = syn::parse_file(&generated).unwrap(); | 69 let generated = builder.generate().unwrap(); |
| 70 generated.write_to_file(test_file("bindgen.rs")).unwrap(); | |
| 71 let file = syn::parse_file(&generated.to_string()).unwrap(); | |
| 70 let mut tests = vec![ | 72 let mut tests = vec![ |
| 71 "use libpam_sys::*;".into(), | 73 "\ |
| 72 "#[allow(deprecated, overflowing_literals)]".into(), | 74 #[allow(dead_code, non_camel_case_types, non_upper_case_globals)] |
| 73 "fn main() {".into(), | 75 mod generated { |
| 76 include!(\"bindgen.rs\"); | |
| 77 } | |
| 78 #[allow(deprecated, overflowing_literals)] | |
| 79 fn main() { | |
| 80 " | |
| 81 .into(), | |
| 74 format!( | 82 format!( |
| 75 "assert_eq!(PamImpl::CURRENT, PamImpl::{:?});", | 83 "assert_eq!(libpam_sys::PamImpl::CURRENT, libpam_sys::PamImpl::{:?});", |
| 76 PamImpl::CURRENT | 84 PamImpl::CURRENT |
| 77 ), | 85 ), |
| 78 ]; | 86 ]; |
| 79 tests.extend( | 87 tests.extend( |
| 80 file.items | 88 file.items |
| 84 Some(item) | 92 Some(item) |
| 85 } else { | 93 } else { |
| 86 None | 94 None |
| 87 } | 95 } |
| 88 }) | 96 }) |
| 89 .filter(|item| config.should_check_const(item)) | 97 .filter(|&item| config.should_check_const(item)) |
| 90 .cloned() | 98 .map(|item| { |
| 91 .map(|mut item| { | 99 let name = item.ident.to_string(); |
| 92 item.ty = Box::new(Type::Path(TypePath { | 100 if let Some(stripped) = name.strip_prefix(&format!("{REDIR_FD}_")) { |
| 93 qself: None, | 101 format!("assert_eq!(generated::{name} as i32, libpam_sys::{REDIR_FD}::{stripped}.into());") |
| 94 path: format_ident!("i32").into(), | 102 } else { |
| 95 })); | 103 format!("assert_eq!(generated::{name}, libpam_sys::{name});") |
| 96 format!( | 104 } |
| 97 "assert_eq!({tokens}, {name});", | |
| 98 tokens = item.expr.to_token_stream(), | |
| 99 name = item.ident | |
| 100 ) | |
| 101 }), | 105 }), |
| 102 ); | 106 ); |
| 103 tests.push("}".into()); | 107 tests.push("}".into()); |
| 104 let const_test = test_file("constant_test.rs"); | 108 let const_test = test_file("constant_test.rs"); |
| 105 fs::write(&const_test, tests.join("\n")).unwrap(); | 109 fs::write(&const_test, tests.join("\n")).unwrap(); |
| 106 rustfmt(&const_test); | 110 rustfmt(&const_test); |
| 107 } | 111 } |
| 108 | 112 |
| 109 fn generate_ctest(config: &TestConfig) { | 113 fn generate_ctest(config: &TestConfig) { |
| 110 let mut test = ctest::TestGenerator::new(); | 114 let mut test = ctest::TestGenerator::new(); |
| 111 | 115 test.cfg("_hack_impl", Some(&format!("{:?}", PamImpl::CURRENT))); |
| 112 for header in config.headers.iter() { | 116 |
| 117 for &header in config.headers.iter() { | |
| 113 if header.starts_with('"') { | 118 if header.starts_with('"') { |
| 114 test.include(env::var("CARGO_MANIFEST_DIR").unwrap()); | 119 test.include(env::var("CARGO_MANIFEST_DIR").unwrap()); |
| 115 } | 120 } |
| 116 test.header(&header[1..header.len() - 1]); | 121 test.header(&header[1..header.len() - 1]); |
| 117 } | 122 } |
| 118 // These are opaque structs. | 123 // These are opaque structs. |
| 119 test.skip_struct(|name| matches!(name, "pam_handle" | "AppData")); | 124 test.skip_struct(|name| matches!(name, "pam_handle" | "AppData")); |
| 120 test.skip_type(|name| matches!(name, "ConversationCallback" | "CleanupCallback")); | 125 test.skip_type(|name| matches!(name, "ConversationCallback" | "CleanupCallback")); |
| 121 test.type_name(|name, _is_struct, is_union| { | 126 test.type_name(|name, is_struct, is_union| { |
| 122 assert!(!is_union); // we scabbin' | 127 assert!(!is_union); // we scabbin' |
| 123 match name { | 128 match (name, is_struct) { |
| 124 "pam_handle" => "struct pam_handle", | 129 ("AppData", _) => "void".into(), |
| 125 "pam_conv" => "struct pam_conv", | 130 (REDIR_FD, _) => format!("enum {REDIR_FD}"), |
| 126 "pam_message" => "struct pam_message", | 131 ("passwd", _) => "struct passwd".into(), |
| 127 "pam_response" => "struct pam_response", | 132 ("group", _) => "struct group".into(), |
| 128 "AppData" => "void", | 133 ("spwd", _) => "struct spwd".into(), |
| 129 other => other, | 134 (name, true) => format!("struct {name}"), |
| 135 (other, false) => other.into(), | |
| 130 } | 136 } |
| 131 .into() | |
| 132 }); | 137 }); |
| 133 | 138 |
| 134 // | 139 // |
| 135 // Welcome to THE HACK ZONE. | 140 // Welcome to THE HACK ZONE. |
| 136 // | 141 // |
| 184 } | 189 } |
| 185 | 190 |
| 186 #[derive(Default)] | 191 #[derive(Default)] |
| 187 struct TestConfig { | 192 struct TestConfig { |
| 188 headers: Vec<&'static str>, | 193 headers: Vec<&'static str>, |
| 189 block_headers: Vec<&'static str>, | 194 allow_types: Vec<&'static str>, |
| 190 ignore_consts: Vec<&'static str>, | 195 ignore_consts: Vec<&'static str>, |
| 191 } | 196 } |
| 192 | 197 |
| 193 impl TestConfig { | 198 impl TestConfig { |
| 194 fn header_contents(&self) -> String { | 199 fn header_contents(&self) -> String { |
