|
|
@ -1,5 +1,6 @@ |
|
|
|
use bindgen::callbacks::ParseCallbacks; |
|
|
|
use bindgen::callbacks::ParseCallbacks; |
|
|
|
use bindgen::{Builder, RustTarget}; |
|
|
|
use bindgen::{Builder, RustTarget}; |
|
|
|
|
|
|
|
use itertools::Itertools; |
|
|
|
|
|
|
|
|
|
|
|
use std::env; |
|
|
|
use std::env; |
|
|
|
use std::error::Error; |
|
|
|
use std::error::Error; |
|
|
@ -18,33 +19,39 @@ impl ParseCallbacks for CustomCallbacks { |
|
|
|
fn main() { |
|
|
|
fn main() { |
|
|
|
let devkitpro = env::var("DEVKITPRO").unwrap(); |
|
|
|
let devkitpro = env::var("DEVKITPRO").unwrap(); |
|
|
|
let devkitarm = env::var("DEVKITARM").unwrap(); |
|
|
|
let devkitarm = env::var("DEVKITARM").unwrap(); |
|
|
|
let profile = env::var("PROFILE").unwrap(); |
|
|
|
|
|
|
|
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); |
|
|
|
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); |
|
|
|
|
|
|
|
|
|
|
|
println!("cargo:rerun-if-changed=build.rs"); |
|
|
|
println!("cargo:rerun-if-changed=build.rs"); |
|
|
|
println!("cargo:rerun-if-env-changed=DEVKITPRO"); |
|
|
|
println!("cargo:rerun-if-env-changed=DEVKITPRO"); |
|
|
|
println!("cargo:rustc-link-search=native={devkitpro}/libctru/lib"); |
|
|
|
println!("cargo:rustc-link-search=native={devkitpro}/libctru/lib"); |
|
|
|
println!( |
|
|
|
|
|
|
|
"cargo:rustc-link-lib=static={}", |
|
|
|
// https://github.com/rust3ds/cargo-3ds/issues/14#issuecomment-1783991872
|
|
|
|
match profile.as_str() { |
|
|
|
// To link properly, this must be the same as the library linked by cargo-3ds when building
|
|
|
|
"debug" => "ctrud", |
|
|
|
// the standard library, so if `-lctru[d]` is found in RUSTFLAGS we always defer to that
|
|
|
|
_ => "ctru", |
|
|
|
// https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-build-scripts
|
|
|
|
|
|
|
|
let cargo_rustflags = env::var("CARGO_ENCODED_RUSTFLAGS").unwrap(); |
|
|
|
|
|
|
|
let rustflags_libctru = cargo_rustflags |
|
|
|
|
|
|
|
.split('\x1F') |
|
|
|
|
|
|
|
// Technically this could also be `-l ctru`, or `-lstatic=ctru` etc.
|
|
|
|
|
|
|
|
// but for now we'll just rely on cargo-3ds implementation to pass it like this
|
|
|
|
|
|
|
|
.find(|flag| flag.starts_with("-lctru")) |
|
|
|
|
|
|
|
.and_then(|flag| flag.strip_prefix("-l")); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let linked_libctru = rustflags_libctru.unwrap_or_else(|| { |
|
|
|
|
|
|
|
let debuginfo = env::var("DEBUG").unwrap(); |
|
|
|
|
|
|
|
match debuginfo.as_str() { |
|
|
|
|
|
|
|
// Normally this should just be "true" or "false", but just in case,
|
|
|
|
|
|
|
|
// we don't support all the different options documented in
|
|
|
|
|
|
|
|
// https://doc.rust-lang.org/cargo/reference/profiles.html#debug
|
|
|
|
|
|
|
|
// so just default to linking with debuginfo if it wasn't disabled
|
|
|
|
|
|
|
|
"0" | "false" | "none" => "ctru", |
|
|
|
|
|
|
|
_ => "ctrud", |
|
|
|
} |
|
|
|
} |
|
|
|
); |
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
match check_libctru_version() { |
|
|
|
println!("cargo:rustc-link-lib=static={linked_libctru}"); |
|
|
|
Ok((maj, min, patch)) => { |
|
|
|
|
|
|
|
eprintln!("using libctru version {maj}.{min}.{patch}"); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// These are accessible by the crate during build with `env!()`.
|
|
|
|
detect_and_track_libctru(); |
|
|
|
// 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}"); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
Err(err) => println!("cargo:warning=failed to check libctru version: {err}"), |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let gcc_version = get_gcc_version(PathBuf::from(&devkitarm).join("bin/arm-none-eabi-gcc")); |
|
|
|
let gcc_version = get_gcc_version(PathBuf::from(&devkitarm).join("bin/arm-none-eabi-gcc")); |
|
|
|
|
|
|
|
|
|
|
@ -135,22 +142,40 @@ fn get_gcc_version(path_to_gcc: PathBuf) -> String { |
|
|
|
.to_string() |
|
|
|
.to_string() |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fn parse_libctru_version(version: &str) -> Result<(String, String, String), &str> { |
|
|
|
fn detect_and_track_libctru() { |
|
|
|
let versions: Vec<_> = version |
|
|
|
let pacman = match which::which("dkp-pacman") |
|
|
|
.split(|c| c == '.' || c == '-') |
|
|
|
.or_else(|err1| which::which("pacman").map_err(|err2| format!("{err1}; {err2}"))) |
|
|
|
.map(String::from) |
|
|
|
{ |
|
|
|
.collect(); |
|
|
|
Ok(pacman) => pacman, |
|
|
|
|
|
|
|
Err(err) => { |
|
|
|
|
|
|
|
println!("cargo:warning=unable to find `pacman` or `dkp-pacman`: {err}"); |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
match &versions[..] { |
|
|
|
match get_libctru_version(&pacman) { |
|
|
|
[major, minor, patch, _build] => Ok((major.clone(), minor.clone(), patch.clone())), |
|
|
|
Ok((maj, min, patch, rel)) => { |
|
|
|
_ => Err("unexpected number of version segments"), |
|
|
|
let version = format!("{maj}.{min}.{patch}-{rel}"); |
|
|
|
|
|
|
|
eprintln!("using libctru version {version}"); |
|
|
|
|
|
|
|
// These are exported as build script output variables, accessible
|
|
|
|
|
|
|
|
// via `env::var("DEP_CTRU_<key>")` in other crates' build scripts.
|
|
|
|
|
|
|
|
// https://doc.rust-lang.org/cargo/reference/build-scripts.html#the-links-manifest-key
|
|
|
|
|
|
|
|
println!("cargo:VERSION={version}"); |
|
|
|
|
|
|
|
println!("cargo:MAJOR_VERSION={maj}"); |
|
|
|
|
|
|
|
println!("cargo:MINOR_VERSION={min}"); |
|
|
|
|
|
|
|
println!("cargo:PATCH_VERSION={patch}"); |
|
|
|
|
|
|
|
println!("cargo:RELEASE={rel}"); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
Err(err) => println!("cargo:warning=unknown libctru version: {err}"), |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
fn check_libctru_version() -> Result<(String, String, String), Box<dyn Error>> { |
|
|
|
if let Err(err) = track_libctru_files(&pacman) { |
|
|
|
let pacman = which::which("dkp-pacman").or_else(|_| which::which("pacman"))?; |
|
|
|
println!("cargo:warning=unable to track `libctru` files for changes: {err}"); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
let Output { stdout, .. } = Command::new(&pacman) |
|
|
|
fn get_libctru_version(pacman: &Path) -> Result<(String, String, String, String), Box<dyn Error>> { |
|
|
|
|
|
|
|
let Output { stdout, .. } = Command::new(pacman) |
|
|
|
.args(["--query", "libctru"]) |
|
|
|
.args(["--query", "libctru"]) |
|
|
|
.stderr(Stdio::inherit()) |
|
|
|
.stderr(Stdio::inherit()) |
|
|
|
.output()?; |
|
|
|
.output()?; |
|
|
@ -159,35 +184,44 @@ fn check_libctru_version() -> Result<(String, String, String), Box<dyn Error>> { |
|
|
|
|
|
|
|
|
|
|
|
let (_pkg, lib_version) = output_str |
|
|
|
let (_pkg, lib_version) = output_str |
|
|
|
.split_once(char::is_whitespace) |
|
|
|
.split_once(char::is_whitespace) |
|
|
|
.ok_or("unexpected pacman output format")?; |
|
|
|
.ok_or_else(|| format!("unexpected pacman output format: {output_str:?}"))?; |
|
|
|
|
|
|
|
|
|
|
|
let lib_version = lib_version.trim(); |
|
|
|
let lib_version = lib_version.trim(); |
|
|
|
|
|
|
|
|
|
|
|
let cargo_pkg_version = env::var("CARGO_PKG_VERSION").unwrap(); |
|
|
|
parse_libctru_version(lib_version).map_err(Into::into) |
|
|
|
let (_, crate_built_version) = cargo_pkg_version |
|
|
|
} |
|
|
|
.split_once('+') |
|
|
|
|
|
|
|
.expect("crate version should have '+' delimeter"); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if lib_version != crate_built_version { |
|
|
|
fn parse_libctru_version(version: &str) -> Result<(String, String, String, String), String> { |
|
|
|
return Err(format!( |
|
|
|
version |
|
|
|
"libctru version is {lib_version} but this crate was built for {crate_built_version}" |
|
|
|
.split(|c| c == '.' || c == '-') |
|
|
|
) |
|
|
|
.map(String::from) |
|
|
|
.into()); |
|
|
|
.collect_tuple() |
|
|
|
|
|
|
|
.ok_or_else(|| format!("unexpected number of version segments: {version:?}")) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
let Output { stdout, .. } = Command::new(pacman) |
|
|
|
fn track_libctru_files(pacman: &Path) -> Result<(), String> { |
|
|
|
|
|
|
|
let stdout = match Command::new(pacman) |
|
|
|
.args(["--query", "--list", "libctru"]) |
|
|
|
.args(["--query", "--list", "libctru"]) |
|
|
|
.stderr(Stdio::inherit()) |
|
|
|
.stderr(Stdio::inherit()) |
|
|
|
.output()?; |
|
|
|
.output() |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
Ok(Output { stdout, status, .. }) if status.success() => stdout, |
|
|
|
|
|
|
|
Ok(Output { status, .. }) => { |
|
|
|
|
|
|
|
return Err(format!("pacman query failed with status {status}")); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
Err(err) => { |
|
|
|
|
|
|
|
return Err(format!("pacman query failed: {err}")); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
for line in String::from_utf8_lossy(&stdout).split('\n') { |
|
|
|
for line in String::from_utf8_lossy(&stdout).trim().split('\n') { |
|
|
|
let Some((_pkg, file)) = line.split_once(char::is_whitespace) else { |
|
|
|
let Some((_pkg, file)) = line.split_once(char::is_whitespace) else { |
|
|
|
|
|
|
|
println!("cargo:warning=unexpected line from pacman query: {line:?}"); |
|
|
|
continue; |
|
|
|
continue; |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
println!("cargo:rerun-if-changed={file}"); |
|
|
|
println!("cargo:rerun-if-changed={file}"); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
let (lib_major, lib_minor, lib_patch) = parse_libctru_version(lib_version)?; |
|
|
|
Ok(()) |
|
|
|
Ok((lib_major, lib_minor, lib_patch)) |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|