use std::env; use std::error::Error; use std::process::{Command, Output, Stdio}; fn main() { let dkp_path = env::var("DEVKITPRO").unwrap(); let profile = env::var("PROFILE").unwrap(); println!("cargo:rerun-if-changed=build.rs"); println!("cargo:rerun-if-env-changed=DEVKITPRO"); println!("cargo:rustc-link-search=native={dkp_path}/libctru/lib"); println!( "cargo:rustc-link-lib=static={}", match profile.as_str() { "debug" => "ctrud", _ => "ctru", } ); match check_libctru_version() { Ok((maj, min, patch)) => { eprintln!("using libctru version {maj}.{min}.{patch}"); // These are accessible by the crate during build with `env!()`. // We might consider exporting some public constants or something. println!("cargo:rustc-env=LIBCTRU_VERSION={maj}.{min}.{patch}"); println!("cargo:rustc-env=LIBCTRU_MAJOR={maj}"); println!("cargo:rustc-env=LIBCTRU_MINOR={min}"); println!("cargo:rustc-env=LIBCTRU_PATCH={patch}"); // It would be nice if we could rerun-if-changed on libctru itself, // maybe we can write a file to OUT_DIR and check that file? } Err(err) => println!("cargo:warning=failed to check libctru version: {err}"), } } fn parse_version(version: &str) -> Result<(String, String, String), &str> { let versions: Vec<_> = version .split(|c| c == '.' || c == '-') .map(String::from) .collect(); match &versions[..] { [major, minor, patch, _build] => Ok((major.clone(), minor.clone(), patch.clone())), _ => Err("unexpected number of version segments"), } } fn check_libctru_version() -> Result<(String, String, String), Box> { let pacman = which::which("dkp-pacman").or_else(|_| which::which("pacman"))?; let Output { stdout, .. } = Command::new(pacman) .args(["--query", "libctru"]) .stderr(Stdio::inherit()) .output()?; let output_str = String::from_utf8_lossy(&stdout); let (_pkg, lib_version) = output_str .split_once(char::is_whitespace) .ok_or("unexpected output format")?; let cargo_pkg_version = env::var("CARGO_PKG_VERSION").unwrap(); let (_, crate_built_version) = cargo_pkg_version .split_once('+') .expect("crate version should have '+' delimeter"); let (lib_major, lib_minor, lib_patch) = parse_version(lib_version)?; let (crate_supported_major, crate_supported_minor, _crate_supported_patch) = parse_version(crate_built_version)?; // TODO: does == comparison make sense, or should we use >= or something? if crate_supported_major != lib_major || crate_supported_minor != lib_minor { // TODO: should this be a panic (i.e. induce build failure)? Maybe only for major version? return Err(format!( "libctru version is {lib_major}.{lib_minor}.{lib_patch}, \ but this crate only supports {crate_supported_major}.{crate_supported_minor}.x" ))?; } Ok((lib_major, lib_minor, lib_patch)) }