// SPDX-FileCopyrightText: 2024 Julia DeMille Vec { let mut r = Vec::new(); assert!( cfg!(feature = "XPLM200"), "Please set a desired SDK version!" ); let xplm_level = env::vars() .filter_map(|(k, _)| match k.strip_prefix("CARGO_FEATURE_")? { "XPLM400" => Some(400), "XPLM303" => Some(303), "XPLM302" => Some(302), "XPLM301" => Some(301), "XPLM300" => Some(300), "XPLM210" => Some(210), "XPLM200" => Some(200), _ => None, }) .max() .unwrap(); r.push(format!("-DXPLM_LEVEL={xplm_level}")); r.push("-DXPLM_FORCE_ENUM_TYPE".to_owned()); if cfg!(feature = "fmod") { r.push("-D_FMOD_STUB_".to_string()); } let xplmheaders = crate_path.join("sdk/xplm/include"); let widgetheaders = crate_path.join("sdk/xpwidgets/include"); r.push(format!("-I{}", xplmheaders.to_str().unwrap())); r.push(format!("-I{}", widgetheaders.to_str().unwrap())); r } const ALLOWED_TARGETS_TEXT: &str = r"Supported targets for xplane-sys are: - x86_64-pc-windows-msvc - x86_64-apple-darwin - aarch64-apple-darwin - x86_64-unknown-linux-gnu"; fn handle_platform(crate_path: &Path) { let target = env::var("TARGET").unwrap(); match target.as_str() { "x86_64-pc-windows-msvc" => { let xplm_path = crate_path.join("sdk/xplm/lib"); let xpw_path = crate_path.join("sdk/xpwidgets/lib"); println!("cargo:rustc-link-search={}", xplm_path.to_str().unwrap()); println!("cargo:rustc-link-search={}", xpw_path.to_str().unwrap()); println!("cargo:rustc-link-lib=XPLM_64"); println!("cargo:rustc-link-lib=XPWidgets_64"); } "x86_64-apple-darwin" | "aarch64-apple-darwin" => { let xplm_path = crate_path.join("sdk/xplm/Frameworks"); println!( "cargo:rustc-link-search-framework=framework={}", xplm_path.to_str().unwrap() ); let xpw_path = crate_path.join("sdk/xpwidgets/Frameworks"); println!( "cargo:rustc-link-search-framework=framework={}", xpw_path.to_str().unwrap() ); println!("cargo:rustc-link-lib=framework=XPLM"); println!("cargo:rustc-link-lib=framework=XPWidgets"); } "x86_64-unknown-linux-gnu" => { // so that we don't try to resolve cmake on other OSes #[cfg(target_os = "linux")] if cfg!(feature = "stub-linux") { println!("cargo:rustc-link-arg=--no-allow-shlib-undefined"); let dst = cmake::Config::new("sdk") .build_target("all") .very_verbose(true) .build(); println!( "cargo:rustc-link-search={}", dst.join("build/xplm/src").to_str().unwrap() ); println!( "cargo:rustc-link-search={}", dst.join("build/xpwidgets/src").to_str().unwrap() ); println!("cargo:rustc-link-lib=dylib:+verbatim=XPLM_64.so"); println!("cargo:rustc-link-lib=dylib:+verbatim=XPWidgets_64.so"); } } // No need to link libraries on Linux. _ => { eprintln!("{}", ALLOWED_TARGETS_TEXT); panic!("unsupported target: {target}") } }; } #[derive(Debug)] struct NamingHandler; impl ParseCallbacks for NamingHandler { fn int_macro(&self, name: &str, _value: i64) -> Option { if name.starts_with("XPLM_VK") || name.starts_with("XPLM_KEY") { Some(IntKind::U32) } else { None } } fn enum_variant_name( &self, enum_name: Option<&str>, original_variant_name: &str, _variant_value: bindgen::callbacks::EnumVariantValue, ) -> Option { let mut out = original_variant_name .trim_start_matches("xplm") .trim_start_matches('_'); let Some(enum_name) = enum_name else { return Some(out.to_string()); }; out = match enum_name { "XPLMCameraControlDuration" => out.trim_start_matches("ControlCamera"), "XPLMDataTypeID" => out.trim_start_matches("Type"), "XPLMKeyFlags" => out.trim_end_matches("Flag"), "XPLMDrawingPhase" => out.trim_start_matches("Phase"), "XPLMDeviceID" => { out = if out.ends_with("_1") || out.ends_with("_2") || out.ends_with("_3") { let side = out.chars().last().unwrap(); // We know this string ends with something. let side = match side { '1' => "Pilot", '2' => "Copilot", '3' => "Center", _ => unreachable!(), }; out = out.trim_start_matches("device_"); return Some(out[0..out.len() - 1].to_string() + side); } else { out }; out.trim_start_matches("device") } "XPLMMouseStatus" => out.trim_start_matches("Mouse"), "XPLMCursorStatus" => out.trim_start_matches("Cursor"), "XPLMWindowLayer" => out.trim_start_matches("WindowLayer"), "XPLMWindowDecoration" => out.trim_start_matches("WindowDecoration"), "XPLMWindowPositioningMode" => out.trim_start_matches("Window"), "XPLMFontID" => out.trim_start_matches("Font"), "XPLMMapStyle" => out.trim_start_matches("MapStyle"), "XPLMMapLayerType" => out.trim_start_matches("MapLayer"), "XPLMMapOrientation" => out.trim_start_matches("MapOrientation"), "XPLMMenuCheck" => out.trim_start_matches("Menu"), "XPLMNavType" => out.trim_start_matches("Nav"), "XPLMFlightLoopPhaseType" => out.trim_start_matches("FlightLoop_Phase"), "XPLMProbeType" | "XPLMProbeResult" => out.trim_start_matches("Probe"), "XPLMAudioBus" => out.trim_start_matches("Audio"), "XPLMBankID" => out.trim_end_matches("Bank"), "XPLMCommandPhase" => out.trim_start_matches("Command"), "XPLMDataFileType" => out.trim_start_matches("DataFile"), "XPLMHostApplicationID" => out.trim_start_matches("Host"), "XPLMLanguageCode" => out.trim_start_matches("Language"), "XPWindowStyle" => out.trim_start_matches("xpWindow"), "XPElementStyle" => out.trim_start_matches("xpElement"), "XPTrackStyle" => out.trim_start_matches("xpTrack"), "XPWidgetPropertyID" => out.trim_start_matches("xpProperty"), "XPDispatchMode" => out.trim_start_matches("xpMode"), "XPWidgetMessage" => out.trim_start_matches("xpMsg"), enum_name => { if !enum_name.starts_with("XPLM") && enum_name.starts_with("XP") { out = out.trim_start_matches("xp").trim_start_matches('_'); if enum_name.ends_with("Property") { out = out.trim_start_matches("Property"); } else if enum_name.ends_with("Message") { out = out.trim_start_matches("Message").trim_start_matches("Msg"); } else if enum_name == "XPScrollBarType" { out = out.trim_start_matches("ScrollBarType"); } else if enum_name == "XPTextFieldType" { out = out.trim_start_matches("Text"); } else if enum_name == "XPButtonBehavior" { out = out.trim_start_matches("ButtonBehavior"); } out.trim_start_matches('_') } else { out } } }; out = out.trim_start_matches('_'); Some(out.to_string()) } } fn main() { // Get the absolute path to this crate, so that linking will work when done in another folder let crate_path = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap()); let bindings_builder = bindgen::Builder::default() .header("src/combined.h") .use_core() .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())) .clang_args(get_clang_args(&crate_path)); let bindings_except_fns = bindings_builder .clone() .prepend_enum_name(false) .default_enum_style(EnumVariation::NewType { is_bitfield: false, is_global: false, }) .bitfield_enum("(XPLMDataTypeID|XPLMKeyFlags|XPLMNavType)") .no_debug("(XPLMDataTypeID|XPLMKeyFlags|XPLMNavType)") .parse_callbacks(Box::new(NamingHandler)) .default_macro_constant_type(bindgen::MacroTypeVariation::Signed) .ignore_functions() .generate() .expect("Unable to generate bindings!") .to_string() .replace(r#"extern "C""#, r#"extern "C-unwind""#); let bindings_fns_only = bindings_builder .with_codegen_config(bindgen::CodegenConfig::FUNCTIONS) .blocklist_function("__va_start") // This symbol breaks builds on Windows, and is unneeded. .blocklist_function("__report_gsfailure") // Likewise. .override_abi(bindgen::Abi::CUnwind, ".*") .generate() .expect("Unable to generate bindings!") .to_string() .replace(r#"extern "C""#, r#"extern "C-unwind""#); let bindings = &[ r#"#[cfg(feature = "mockall")] use mockall::automock; #[cfg_attr(feature = "mockall", automock)] #[cfg_attr(feature = "mockall", allow(dead_code))] // Don't warn on not using mocked functions. mod functions { use super::*; "#, &bindings_fns_only, r#"} #[cfg(not(feature = "mockall"))] #[doc(inline)] pub use functions::*; #[cfg(feature = "mockall")] #[doc(inline)] pub use mock_functions::*; "#, &bindings_except_fns, ] .join(""); let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()).join("bindings.rs"); fs::write(out_path, bindings.as_bytes()).expect("Could not write bindings!"); if !cfg!(feature = "mockall") { handle_platform(&crate_path); } }