|
|
|
//! This build script generates bindings from `citro3d` on the fly at compilation
|
|
|
|
//! time into `OUT_DIR`, from which they can be included into `lib.rs`.
|
|
|
|
|
|
|
|
use std::env;
|
|
|
|
use std::iter::FromIterator;
|
|
|
|
use std::path::{Path, PathBuf};
|
|
|
|
|
|
|
|
use bindgen::callbacks::{DeriveTrait, ImplementsTrait, ParseCallbacks};
|
|
|
|
use bindgen::{Builder, RustTarget};
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
let devkitpro = env::var("DEVKITPRO").expect("DEVKITPRO not set in environment");
|
|
|
|
println!("cargo:rerun-if-env-changed=DEVKITPRO");
|
|
|
|
|
|
|
|
let devkitarm = std::env::var("DEVKITARM").expect("DEVKITARM not set in environment");
|
|
|
|
println!("cargo:rerun-if-env-changed=DEVKITARM");
|
|
|
|
|
|
|
|
let debug_symbols = env::var("DEBUG").unwrap();
|
|
|
|
println!("cargo:rerun-if-env-changed=DEBUG");
|
|
|
|
|
|
|
|
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
|
|
|
|
println!("cargo:rerun-if-env-changed=OUT_DIR");
|
|
|
|
|
|
|
|
println!("cargo:rerun-if-changed=build.rs");
|
|
|
|
println!("cargo:rustc-link-search=native={devkitpro}/libctru/lib");
|
|
|
|
println!(
|
|
|
|
"cargo:rustc-link-lib=static={}",
|
|
|
|
match debug_symbols.as_str() {
|
|
|
|
// Based on valid values described in
|
|
|
|
// https://doc.rust-lang.org/cargo/reference/profiles.html#debug
|
|
|
|
"0" | "false" | "none" => "citro3d",
|
|
|
|
_ => "citro3dd",
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
let include_path = PathBuf::from_iter([devkitpro.as_str(), "libctru", "include"]);
|
|
|
|
let tex3ds_h = include_path.join("tex3ds.h");
|
|
|
|
let citro3d_h = include_path.join("citro3d.h");
|
|
|
|
let three_ds_h = include_path.join("3ds.h");
|
|
|
|
|
|
|
|
let sysroot = Path::new(devkitarm.as_str()).join("arm-none-eabi");
|
|
|
|
let system_include = sysroot.join("include");
|
|
|
|
let static_fns_path = Path::new("citro3d_statics_wrapper");
|
|
|
|
|
|
|
|
let gcc_dir = PathBuf::from_iter([devkitarm.as_str(), "lib", "gcc", "arm-none-eabi"]);
|
|
|
|
|
|
|
|
let gcc_include = gcc_dir
|
|
|
|
.read_dir()
|
|
|
|
.unwrap()
|
|
|
|
// Assuming that there is only one gcc version of libs under the devkitARM dir
|
|
|
|
.next()
|
|
|
|
.unwrap()
|
|
|
|
.unwrap()
|
|
|
|
.path()
|
|
|
|
.join("include");
|
|
|
|
|
|
|
|
let bindings = Builder::default()
|
|
|
|
.header(three_ds_h.to_str().unwrap())
|
|
|
|
.header(citro3d_h.to_str().unwrap())
|
|
|
|
.header(tex3ds_h.to_str().unwrap())
|
|
|
|
.rust_target(RustTarget::Nightly)
|
|
|
|
.use_core()
|
|
|
|
.trust_clang_mangling(false)
|
|
|
|
.layout_tests(false)
|
|
|
|
.ctypes_prefix("::libc")
|
|
|
|
.prepend_enum_name(false)
|
|
|
|
.fit_macro_constants(true)
|
|
|
|
.raw_line("use ctru_sys::*;")
|
|
|
|
.must_use_type("Result")
|
|
|
|
.blocklist_type("u(8|16|32|64)")
|
|
|
|
.opaque_type("(GPU|GFX)_.*")
|
|
|
|
.opaque_type("float24Uniform_s")
|
|
|
|
.allowlist_file(".*/c3d/.*[.]h")
|
|
|
|
.allowlist_file(".*/tex3ds[.]h")
|
|
|
|
.blocklist_file(".*/3ds/.*[.]h")
|
|
|
|
.blocklist_file(".*/sys/.*[.]h")
|
|
|
|
.wrap_static_fns(true)
|
|
|
|
.wrap_static_fns_path(out_dir.join(static_fns_path))
|
|
|
|
.clang_args([
|
|
|
|
"--target=arm-none-eabi",
|
|
|
|
"--sysroot",
|
|
|
|
sysroot.to_str().unwrap(),
|
|
|
|
"-isystem",
|
|
|
|
system_include.to_str().unwrap(),
|
|
|
|
"-isystem",
|
|
|
|
gcc_include.to_str().unwrap(),
|
|
|
|
"-I",
|
|
|
|
include_path.to_str().unwrap(),
|
|
|
|
"-mfloat-abi=hard",
|
|
|
|
"-march=armv6k",
|
|
|
|
"-mtune=mpcore",
|
|
|
|
"-mfpu=vfp",
|
|
|
|
"-DARM11 ",
|
|
|
|
"-D_3DS ",
|
|
|
|
"-D__3DS__ ",
|
|
|
|
])
|
|
|
|
.parse_callbacks(Box::new(CustomCallbacks))
|
|
|
|
.generate()
|
|
|
|
.expect("Unable to generate bindings");
|
|
|
|
|
|
|
|
bindings
|
|
|
|
.write_to_file(out_dir.join("bindings.rs"))
|
|
|
|
.expect("failed to write bindings");
|
|
|
|
|
|
|
|
// Compile static inline fns wrapper
|
|
|
|
let cc = Path::new(devkitarm.as_str()).join("bin/arm-none-eabi-gcc");
|
|
|
|
let ar = Path::new(devkitarm.as_str()).join("bin/arm-none-eabi-ar");
|
|
|
|
|
|
|
|
cc::Build::new()
|
|
|
|
.compiler(cc)
|
|
|
|
.archiver(ar)
|
|
|
|
.include(&include_path)
|
|
|
|
.file(out_dir.join(static_fns_path.with_extension("c")))
|
|
|
|
.flag("-march=armv6k")
|
|
|
|
.flag("-mtune=mpcore")
|
|
|
|
.flag("-mfloat-abi=hard")
|
|
|
|
.flag("-mfpu=vfp")
|
|
|
|
.flag("-mtp=soft")
|
|
|
|
.flag("-Wno-deprecated-declarations")
|
|
|
|
.compile("citro3d_statics_wrapper");
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Custom callback struct to allow us to mark some "known good types" as
|
|
|
|
/// [`Copy`], which in turn allows using Rust `union` instead of bindgen union types. See
|
|
|
|
/// <https://rust-lang.github.io/rust-bindgen/using-unions.html#which-union-type-will-bindgen-generate>
|
|
|
|
/// for more info.
|
|
|
|
///
|
|
|
|
/// We do the same for [`Debug`] just for the convenience of derived Debug impls
|
|
|
|
/// on some `citro3d` types.
|
|
|
|
///
|
|
|
|
/// Finally, we use [`doxygen_rs`] to transform the doc comments into something
|
|
|
|
/// easier to read in the generated documentation / hover documentation.
|
|
|
|
#[derive(Debug)]
|
|
|
|
struct CustomCallbacks;
|
|
|
|
|
|
|
|
impl ParseCallbacks for CustomCallbacks {
|
|
|
|
fn process_comment(&self, comment: &str) -> Option<String> {
|
|
|
|
Some(doxygen_rs::transform(comment))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn blocklisted_type_implements_trait(
|
|
|
|
&self,
|
|
|
|
name: &str,
|
|
|
|
derive_trait: DeriveTrait,
|
|
|
|
) -> Option<ImplementsTrait> {
|
|
|
|
if let DeriveTrait::Copy | DeriveTrait::Debug = derive_trait {
|
|
|
|
match name {
|
|
|
|
"u64_" | "u32_" | "u16_" | "u8_" | "u64" | "u32" | "u16" | "u8" | "gfxScreen_t"
|
|
|
|
| "gfx3dSide_t" => Some(ImplementsTrait::Yes),
|
|
|
|
_ if name.starts_with("GPU_") => Some(ImplementsTrait::Yes),
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|