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 { |