view libpam-sys/libpam-sys-impls/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 2346fd501b7a
children
line wrap: on
line source

//! This absurd build script basically sets up everything for libpam-sys-impl.
//!
//!  1. It's the definition site for the [`PamImpl`] enum, which then gets
//!     output to the `OUT_DIR/pam_impl_enum.rs` file for parsing/inclusion
//!     into the `__pam_impl_enum__` macro.
//!  2. It detects the current PAM implementation and sets an env var for
//!     the macros in `libpam-sys-impl`.

use proc_macro2::TokenStream;
use quote::quote;
use std::{env, fs};
use strum::EnumString;

fn main() {
    let pam_impl = match option_env!("LIBPAMSYS_IMPL") {
        // The default option: Guess what PAM impl we're using based on OS.
        None => {
            if cfg!(target_os = "linux") {
                PamImpl::LinuxPam
            } else if cfg!(any(
                target_os = "macos",
                target_os = "freebsd",
                target_os = "netbsd",
                target_os = "dragonfly",
                target_os = "openbsd"
            )) {
                PamImpl::OpenPam
            } else if cfg!(any(target_os = "illumos", target_os = "solaris",)) {
                PamImpl::Sun
            } else {
                PamImpl::MinimalOpenPam
            }
        }
        Some("_detect") => {
            // Detect which impl it is from system headers.
            if header_exists("security/_pam_types.h") {
                PamImpl::LinuxPam
            } else if header_exists("security/openpam.h") {
                PamImpl::OpenPam
            } else if header_exists("security/pam_appl.h") {
                // We figure we're *probably* on a Sun derivative.
                PamImpl::Sun
            } else {
                // If all else fails, assume the bare minimum.
                PamImpl::MinimalOpenPam
            }
        }
        Some(other) => match PamImpl::try_from(other) {
            Ok(i) => i,
            Err(_) => panic!("unknown PAM implementation {other:?}"),
        },
    };
    fs::write(
        format!("{}/pam_impl_enum.rs", env::var("OUT_DIR").unwrap()),
        PamImpl::enum_tokens().to_string(),
    )
    .unwrap();
    println!("cargo:rustc-env=LIBPAMSYS_IMPL={pam_impl:?}");
}

/// This defines a local enum with an `enum_tokens()` method that can spit out
/// its own contents.
macro_rules! self_aware_enum {
    (
        $(#here[$here:meta])*
        $(#[$attr:meta])*
        $name:ident {
            $($tt:tt)*
        }
    ) => {
        $(#[$here])*
        $(#[$attr])*
        pub enum $name {
            $($tt)*
        }

        impl $name {
            fn enum_tokens() -> TokenStream {
                quote!(
                    $(#[$attr])*
                    pub enum $name {
                        $($tt)*
                    }
                )
            }
        }
    }
}

self_aware_enum!(
    #here[derive(EnumString, strum::Display)]
    /// The PAM implementations supported by `libpam-sys`.
    #[derive(Clone, Copy, Debug, PartialEq)]
    PamImpl {
        /// [Linux-PAM] is provided by most Linux distributions.
        ///
        /// [Linux-PAM]: https://github.com/linux-pam/linux-pam
        LinuxPam,
        /// [OpenPAM] is used by most BSDs, including Mac OS X.
        ///
        /// [OpenPAM]: https://git.des.dev/OpenPAM/OpenPAM
        OpenPam,
        /// Illumos and Solaris use a derivative of [Sun's implementation][sun].
        ///
        /// [sun]: https://code.illumos.org/plugins/gitiles/illumos-gate/+/refs/heads/master/usr/src/lib/libpam
        Sun,
        /// Only the functionality in [the PAM spec], with OpenPAM/Sun consts.
        ///
        /// [the PAM spec]: https://pubs.opengroup.org/onlinepubs/8329799/toc.htm
        MinimalOpenPam,
    }
);

fn header_exists(header: &str) -> bool {
    bindgen::Builder::default()
        .blocklist_item(".*")
        .header_contents("header.h", &format!("#include <{header}>"))
        .generate()
        .is_ok()
}