You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
157 lines
5.5 KiB
157 lines
5.5 KiB
//! 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 |
|
} |
|
} |
|
}
|
|
|