diff libpam-sys/libpam-sys-consts/build.rs @ 158:d5b7b28d754e

Add `__TARGET_DEFAULT__` PamImpl and set up for docsrs build. Also fixes some formatting stuff.
author Paul Fisher <paul@pfish.zone>
date Sat, 12 Jul 2025 17:17:37 -0400
parents 4b3a5095f68c
children 09dff285ff5e
line wrap: on
line diff
--- a/libpam-sys/libpam-sys-consts/build.rs	Wed Jul 09 16:59:30 2025 -0400
+++ b/libpam-sys/libpam-sys-consts/build.rs	Sat Jul 12 17:17:37 2025 -0400
@@ -6,18 +6,43 @@
 
 include!("src/pam_impl.rs");
 
+/// The strategy to use to detect PAM.
+enum Detect {
+    /// Automatically detect PAM, using the installed implementation if present
+    /// or the OS default if not.
+    Auto,
+    /// Use the default PAM implementation based on the OS.
+    TargetDefault,
+    /// Use the named version of PAM.
+    Specified(PamImpl),
+}
+
+const TARGET_DEFAULT: &str = "__TARGET_DEFAULT__";
+
 fn main() {
-    let pam_impl = match option_env!("LIBPAMSYS_IMPL") {
-        // The default option: Guess what PAM impl we're using based on OS.
-        None | Some("") => LibPam::detect(),
-        Some(other) => match PamImpl::try_from(other) {
-            Ok(i) => i,
-            Err(_) => {
-                panic!(
-                    "unknown PAM implementation {other:?}. valid LIBPAMSYS_IMPLs are {:?}, or unset to detect", PamImpl::items()
-                )
-            }
+    let detection = match option_env!("LIBPAMSYS_IMPL") {
+        None | Some("") => match option_env!("DOCS_RS") {
+            // docs.rs cross-compiles, so we don't want to look at
+            // its currently-installed PAM; instead we want to use the OS.
+            Some(_) => Detect::TargetDefault,
+            // In other cases, just auto-detect the actual installed library.
+            None => Detect::Auto,
         },
+        Some(TARGET_DEFAULT) => Detect::TargetDefault,
+        Some(val) => Detect::Specified(PamImpl::try_from(val).unwrap_or_else(|_| {
+            panic!(
+                "unknown PAM implementation {val:?}. \
+                valid LIBPAMSYS_IMPLs are {:?}, \
+                {TARGET_DEFAULT:?} to use the OS default, \
+                or unset to detect",
+                PamImpl::items()
+            )
+        })),
+    };
+    let pam_impl = match detection {
+        Detect::Auto => LibPam::probe_detect(),
+        Detect::TargetDefault => LibPam::os_default(),
+        Detect::Specified(other) => other,
     };
     let impl_str = format!("{pam_impl:?}");
     println!("{}", generate_cfg(&impl_str));
@@ -48,7 +73,9 @@
 struct LibPam(NonNull<c_void>);
 
 impl LibPam {
-    fn detect() -> PamImpl {
+    /// Look at the currently-installed LibPAM, or use [`Self::os_default`]
+    /// if absent.
+    fn probe_detect() -> PamImpl {
         if let Some(lib) = Self::open() {
             if lib.has("pam_syslog") {
                 return PamImpl::LinuxPam;
@@ -58,6 +85,11 @@
                 return PamImpl::Sun;
             }
         }
+        Self::os_default()
+    }
+
+    /// Guess the PAM implementation based on the current OS.
+    fn os_default() -> PamImpl {
         if cfg!(target_os = "linux") {
             PamImpl::LinuxPam
         } else if cfg!(any(
@@ -76,7 +108,9 @@
     }
 
     fn open() -> Option<Self> {
-        NonNull::new(unsafe { libc::dlopen(b"libpam.so\0".as_ptr().cast(), libc::RTLD_LAZY) })
+        let dlopen = |s: &[u8]| unsafe { libc::dlopen(s.as_ptr().cast(), libc::RTLD_LAZY) };
+        NonNull::new(dlopen(b"libpam.so\0"))
+            .or_else(|| NonNull::new(dlopen(b"libpam.dylib\0")))
             .map(Self)
     }