diff --git a/ctru-sys/Cargo.toml b/ctru-sys/Cargo.toml index 6367b13..f7bf67b 100644 --- a/ctru-sys/Cargo.toml +++ b/ctru-sys/Cargo.toml @@ -1,10 +1,13 @@ [package] name = "ctru-sys" -version = "0.4.1" +version = "21.2.0+2.1.2-1" authors = ["Ronald Kinard "] -license = "https://en.wikipedia.org/wiki/Zlib_License" +license = "Zlib" links = "ctru" edition = "2021" [dependencies] libc = { version = "0.2.121", default-features = false } + +[build-dependencies] +which = "4.4.0" diff --git a/ctru-sys/build.rs b/ctru-sys/build.rs index 90d97e6..e6c17f2 100644 --- a/ctru-sys/build.rs +++ b/ctru-sys/build.rs @@ -1,4 +1,6 @@ use std::env; +use std::error::Error; +use std::process::{Command, Output, Stdio}; fn main() { let dkp_path = env::var("DEVKITPRO").unwrap(); @@ -14,4 +16,69 @@ fn main() { _ => "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)) }