1eba8b6baSopenharmony_ci//! A program which generates a linux-headers installation and runs bindgen 2eba8b6baSopenharmony_ci//! over the headers, for each supported architecture. 3eba8b6baSopenharmony_ci 4eba8b6baSopenharmony_ciuse bindgen::{builder, EnumVariation}; 5eba8b6baSopenharmony_ciuse std::collections::HashSet; 6eba8b6baSopenharmony_ciuse std::fs::File; 7eba8b6baSopenharmony_ciuse std::io::{BufRead, BufReader, Read, Write}; 8eba8b6baSopenharmony_ciuse std::path::Path; 9eba8b6baSopenharmony_ciuse std::process::Command; 10eba8b6baSopenharmony_ciuse std::{env, fs}; 11eba8b6baSopenharmony_ci 12eba8b6baSopenharmony_ci#[allow(unused_doc_comments)] 13eba8b6baSopenharmony_ciconst LINUX_VERSION: &str = "v5.17"; 14eba8b6baSopenharmony_ci 15eba8b6baSopenharmony_ci/// Some commonly used features. 16eba8b6baSopenharmony_ciconst DEFAULT_FEATURES: &str = "\"general\", \"errno\""; 17eba8b6baSopenharmony_ci 18eba8b6baSopenharmony_cifn main() { 19eba8b6baSopenharmony_ci let mut args = env::args(); 20eba8b6baSopenharmony_ci let _exe = args.next().unwrap(); 21eba8b6baSopenharmony_ci let cmd = args.next(); 22eba8b6baSopenharmony_ci 23eba8b6baSopenharmony_ci // This is the main invocation path. 24eba8b6baSopenharmony_ci assert!(cmd.is_none()); 25eba8b6baSopenharmony_ci assert!(args.next().is_none()); 26eba8b6baSopenharmony_ci 27eba8b6baSopenharmony_ci git_init(); 28eba8b6baSopenharmony_ci 29eba8b6baSopenharmony_ci let out = tempdir::TempDir::new("linux-raw-sys").unwrap(); 30eba8b6baSopenharmony_ci let out_dir = out.path(); 31eba8b6baSopenharmony_ci let linux_headers = out_dir.join("linux-headers"); 32eba8b6baSopenharmony_ci let linux_include = linux_headers.join("include"); 33eba8b6baSopenharmony_ci 34eba8b6baSopenharmony_ci // Clean up any modules from previous builds. 35eba8b6baSopenharmony_ci for entry in fs::read_dir("../src").unwrap() { 36eba8b6baSopenharmony_ci let entry = entry.unwrap(); 37eba8b6baSopenharmony_ci assert!(!entry.path().to_str().unwrap().ends_with(".")); 38eba8b6baSopenharmony_ci if entry.file_type().unwrap().is_dir() { 39eba8b6baSopenharmony_ci fs::remove_dir_all(entry.path()).ok(); 40eba8b6baSopenharmony_ci } 41eba8b6baSopenharmony_ci } 42eba8b6baSopenharmony_ci 43eba8b6baSopenharmony_ci // Edit ../src/lib.rs 44eba8b6baSopenharmony_ci let mut src_lib_rs_in = File::open("../src/lib.rs").unwrap(); 45eba8b6baSopenharmony_ci let mut src_lib_rs_contents = String::new(); 46eba8b6baSopenharmony_ci src_lib_rs_in 47eba8b6baSopenharmony_ci .read_to_string(&mut src_lib_rs_contents) 48eba8b6baSopenharmony_ci .unwrap(); 49eba8b6baSopenharmony_ci let edit_at = src_lib_rs_contents 50eba8b6baSopenharmony_ci .find("// The rest of this file is auto-generated!\n") 51eba8b6baSopenharmony_ci .unwrap(); 52eba8b6baSopenharmony_ci src_lib_rs_contents = src_lib_rs_contents[..edit_at].to_owned(); 53eba8b6baSopenharmony_ci 54eba8b6baSopenharmony_ci let mut src_lib_rs = File::create("../src/lib.rs").unwrap(); 55eba8b6baSopenharmony_ci src_lib_rs 56eba8b6baSopenharmony_ci .write_all(src_lib_rs_contents.as_bytes()) 57eba8b6baSopenharmony_ci .unwrap(); 58eba8b6baSopenharmony_ci src_lib_rs 59eba8b6baSopenharmony_ci .write_all("// The rest of this file is auto-generated!\n".as_bytes()) 60eba8b6baSopenharmony_ci .unwrap(); 61eba8b6baSopenharmony_ci 62eba8b6baSopenharmony_ci // Edit ../Cargo.toml 63eba8b6baSopenharmony_ci let mut cargo_toml_in = File::open("../Cargo.toml").unwrap(); 64eba8b6baSopenharmony_ci let mut cargo_toml_contents = String::new(); 65eba8b6baSopenharmony_ci cargo_toml_in 66eba8b6baSopenharmony_ci .read_to_string(&mut cargo_toml_contents) 67eba8b6baSopenharmony_ci .unwrap(); 68eba8b6baSopenharmony_ci let edit_at = cargo_toml_contents 69eba8b6baSopenharmony_ci .find("# The rest of this file is auto-generated!\n") 70eba8b6baSopenharmony_ci .unwrap(); 71eba8b6baSopenharmony_ci cargo_toml_contents = cargo_toml_contents[..edit_at].to_owned(); 72eba8b6baSopenharmony_ci 73eba8b6baSopenharmony_ci // Generate Cargo.toml 74eba8b6baSopenharmony_ci let mut cargo_toml = File::create("../Cargo.toml").unwrap(); 75eba8b6baSopenharmony_ci cargo_toml 76eba8b6baSopenharmony_ci .write_all(cargo_toml_contents.as_bytes()) 77eba8b6baSopenharmony_ci .unwrap(); 78eba8b6baSopenharmony_ci cargo_toml 79eba8b6baSopenharmony_ci .write_all("# The rest of this file is auto-generated!\n".as_bytes()) 80eba8b6baSopenharmony_ci .unwrap(); 81eba8b6baSopenharmony_ci writeln!(cargo_toml, "[features]").unwrap(); 82eba8b6baSopenharmony_ci 83eba8b6baSopenharmony_ci let mut features: HashSet<String> = HashSet::new(); 84eba8b6baSopenharmony_ci 85eba8b6baSopenharmony_ci let linux_version = LINUX_VERSION; 86eba8b6baSopenharmony_ci // Checkout a specific version of Linux. 87eba8b6baSopenharmony_ci git_checkout(linux_version); 88eba8b6baSopenharmony_ci 89eba8b6baSopenharmony_ci let mut linux_archs = fs::read_dir(&format!("linux/arch")) 90eba8b6baSopenharmony_ci .unwrap() 91eba8b6baSopenharmony_ci .map(|entry| entry.unwrap()) 92eba8b6baSopenharmony_ci .collect::<Vec<_>>(); 93eba8b6baSopenharmony_ci // Sort archs list as filesystem iteration order is non-deterministic 94eba8b6baSopenharmony_ci linux_archs.sort_by_key(|entry| entry.file_name()); 95eba8b6baSopenharmony_ci for linux_arch_entry in linux_archs { 96eba8b6baSopenharmony_ci if !linux_arch_entry.file_type().unwrap().is_dir() { 97eba8b6baSopenharmony_ci continue; 98eba8b6baSopenharmony_ci } 99eba8b6baSopenharmony_ci let linux_arch = linux_arch_entry.file_name().to_str().unwrap().to_owned(); 100eba8b6baSopenharmony_ci 101eba8b6baSopenharmony_ci let rust_arches = rust_arches(&linux_arch); 102eba8b6baSopenharmony_ci if rust_arches.is_empty() { 103eba8b6baSopenharmony_ci continue; 104eba8b6baSopenharmony_ci } 105eba8b6baSopenharmony_ci 106eba8b6baSopenharmony_ci fs::create_dir_all(&linux_headers).unwrap(); 107eba8b6baSopenharmony_ci 108eba8b6baSopenharmony_ci let mut headers_made = false; 109eba8b6baSopenharmony_ci for rust_arch in rust_arches { 110eba8b6baSopenharmony_ci if !headers_made { 111eba8b6baSopenharmony_ci make_headers_install(&linux_arch, &linux_headers); 112eba8b6baSopenharmony_ci headers_made = true; 113eba8b6baSopenharmony_ci } 114eba8b6baSopenharmony_ci 115eba8b6baSopenharmony_ci eprintln!( 116eba8b6baSopenharmony_ci "Generating all bindings for Linux {} architecture {}", 117eba8b6baSopenharmony_ci linux_version, rust_arch 118eba8b6baSopenharmony_ci ); 119eba8b6baSopenharmony_ci 120eba8b6baSopenharmony_ci let src_arch = format!("../src/{}", rust_arch); 121eba8b6baSopenharmony_ci 122eba8b6baSopenharmony_ci fs::create_dir_all(&src_arch).unwrap(); 123eba8b6baSopenharmony_ci 124eba8b6baSopenharmony_ci let mut modules = fs::read_dir("modules") 125eba8b6baSopenharmony_ci .unwrap() 126eba8b6baSopenharmony_ci .map(|entry| entry.unwrap()) 127eba8b6baSopenharmony_ci .collect::<Vec<_>>(); 128eba8b6baSopenharmony_ci // Sort module list as filesystem iteration order is non-deterministic 129eba8b6baSopenharmony_ci modules.sort_by_key(|entry| entry.file_name()); 130eba8b6baSopenharmony_ci for mod_entry in modules { 131eba8b6baSopenharmony_ci let header_name = mod_entry.path(); 132eba8b6baSopenharmony_ci let mod_name = header_name.file_stem().unwrap().to_str().unwrap(); 133eba8b6baSopenharmony_ci let mod_rs = format!("{}/{}.rs", src_arch, mod_name); 134eba8b6baSopenharmony_ci 135eba8b6baSopenharmony_ci writeln!(src_lib_rs, "#[cfg(feature = \"{}\")]", mod_name).unwrap(); 136eba8b6baSopenharmony_ci if *rust_arch == "x32" { 137eba8b6baSopenharmony_ci writeln!(src_lib_rs, "#[cfg(all(target_arch = \"x86_64\", target_pointer_width = \"32\"))]").unwrap(); 138eba8b6baSopenharmony_ci } else if *rust_arch == "x86_64" { 139eba8b6baSopenharmony_ci writeln!(src_lib_rs, "#[cfg(all(target_arch = \"x86_64\", target_pointer_width = \"64\"))]").unwrap(); 140eba8b6baSopenharmony_ci } else { 141eba8b6baSopenharmony_ci writeln!(src_lib_rs, "#[cfg(target_arch = \"{}\")]", rust_arch).unwrap(); 142eba8b6baSopenharmony_ci } 143eba8b6baSopenharmony_ci writeln!(src_lib_rs, "#[path = \"{}/{}.rs\"]", rust_arch, mod_name).unwrap(); 144eba8b6baSopenharmony_ci writeln!(src_lib_rs, "pub mod {};", mod_name).unwrap(); 145eba8b6baSopenharmony_ci 146eba8b6baSopenharmony_ci run_bindgen( 147eba8b6baSopenharmony_ci linux_include.to_str().unwrap(), 148eba8b6baSopenharmony_ci header_name.to_str().unwrap(), 149eba8b6baSopenharmony_ci &mod_rs, 150eba8b6baSopenharmony_ci mod_name, 151eba8b6baSopenharmony_ci rust_arch, 152eba8b6baSopenharmony_ci linux_version, 153eba8b6baSopenharmony_ci ); 154eba8b6baSopenharmony_ci 155eba8b6baSopenharmony_ci // Collect all unique feature names across all architectures. 156eba8b6baSopenharmony_ci if features.insert(mod_name.to_owned()) { 157eba8b6baSopenharmony_ci writeln!(cargo_toml, "{} = []", mod_name).unwrap(); 158eba8b6baSopenharmony_ci } 159eba8b6baSopenharmony_ci } 160eba8b6baSopenharmony_ci } 161eba8b6baSopenharmony_ci 162eba8b6baSopenharmony_ci fs::remove_dir_all(&linux_headers).unwrap(); 163eba8b6baSopenharmony_ci } 164eba8b6baSopenharmony_ci 165eba8b6baSopenharmony_ci writeln!(cargo_toml, "default = [\"std\", {}]", DEFAULT_FEATURES).unwrap(); 166eba8b6baSopenharmony_ci writeln!(cargo_toml, "std = []").unwrap(); 167eba8b6baSopenharmony_ci writeln!(cargo_toml, "no_std = []").unwrap(); 168eba8b6baSopenharmony_ci writeln!( 169eba8b6baSopenharmony_ci cargo_toml, 170eba8b6baSopenharmony_ci "rustc-dep-of-std = [\"core\", \"compiler_builtins\", \"no_std\"]" 171eba8b6baSopenharmony_ci ) 172eba8b6baSopenharmony_ci .unwrap(); 173eba8b6baSopenharmony_ci 174eba8b6baSopenharmony_ci eprintln!("All bindings generated!"); 175eba8b6baSopenharmony_ci} 176eba8b6baSopenharmony_ci 177eba8b6baSopenharmony_cifn git_init() { 178eba8b6baSopenharmony_ci // Clone the linux kernel source repo if necessary. Ignore exit code as it will 179eba8b6baSopenharmony_ci // be non-zero in case it was already cloned. 180eba8b6baSopenharmony_ci // 181eba8b6baSopenharmony_ci // Use a treeless partial clone to save disk space and clone time. 182eba8b6baSopenharmony_ci // See <https://github.blog/2020-12-21-get-up-to-speed-with-partial-clone-and-shallow-clone/> 183eba8b6baSopenharmony_ci // for more info on partial clones. 184eba8b6baSopenharmony_ci // 185eba8b6baSopenharmony_ci // Note: this is not using the official repo 186eba8b6baSopenharmony_ci // <git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git> but 187eba8b6baSopenharmony_ci // the github fork as the server of the official repo doesn't recognize 188eba8b6baSopenharmony_ci // filtering. 189eba8b6baSopenharmony_ci if !Path::new("linux/.git").exists() { 190eba8b6baSopenharmony_ci assert!(Command::new("git") 191eba8b6baSopenharmony_ci .arg("clone") 192eba8b6baSopenharmony_ci .arg("https://github.com/torvalds/linux.git") 193eba8b6baSopenharmony_ci .arg("--filter=tree:0") 194eba8b6baSopenharmony_ci .arg("--no-checkout") 195eba8b6baSopenharmony_ci .status() 196eba8b6baSopenharmony_ci .unwrap() 197eba8b6baSopenharmony_ci .success()); 198eba8b6baSopenharmony_ci } 199eba8b6baSopenharmony_ci 200eba8b6baSopenharmony_ci // Setup sparse checkout. This greatly reduces the amount of objects necessary 201eba8b6baSopenharmony_ci // to checkout the tree. 202eba8b6baSopenharmony_ci assert!(Command::new("git") 203eba8b6baSopenharmony_ci .arg("sparse-checkout") 204eba8b6baSopenharmony_ci .arg("init") 205eba8b6baSopenharmony_ci .current_dir("linux") 206eba8b6baSopenharmony_ci .status() 207eba8b6baSopenharmony_ci .unwrap() 208eba8b6baSopenharmony_ci .success()); 209eba8b6baSopenharmony_ci 210eba8b6baSopenharmony_ci fs::write( 211eba8b6baSopenharmony_ci "linux/.git/info/sparse-checkout", 212eba8b6baSopenharmony_ci "/* 213eba8b6baSopenharmony_ci!/*/ 214eba8b6baSopenharmony_ci/include/ 215eba8b6baSopenharmony_ci/arch/ 216eba8b6baSopenharmony_ci/scripts/ 217eba8b6baSopenharmony_ci/tools/", 218eba8b6baSopenharmony_ci ) 219eba8b6baSopenharmony_ci .unwrap(); 220eba8b6baSopenharmony_ci} 221eba8b6baSopenharmony_ci 222eba8b6baSopenharmony_cifn git_checkout(rev: &str) { 223eba8b6baSopenharmony_ci // Delete any generated files from previous versions. 224eba8b6baSopenharmony_ci assert!(Command::new("git") 225eba8b6baSopenharmony_ci .arg("clean") 226eba8b6baSopenharmony_ci .arg("-f") 227eba8b6baSopenharmony_ci .arg("-d") 228eba8b6baSopenharmony_ci .current_dir("linux") 229eba8b6baSopenharmony_ci .status() 230eba8b6baSopenharmony_ci .unwrap() 231eba8b6baSopenharmony_ci .success()); 232eba8b6baSopenharmony_ci 233eba8b6baSopenharmony_ci // Check out the given revision. 234eba8b6baSopenharmony_ci assert!(Command::new("git") 235eba8b6baSopenharmony_ci .arg("checkout") 236eba8b6baSopenharmony_ci .arg(rev) 237eba8b6baSopenharmony_ci .arg("-f") 238eba8b6baSopenharmony_ci .current_dir("linux") 239eba8b6baSopenharmony_ci .status() 240eba8b6baSopenharmony_ci .unwrap() 241eba8b6baSopenharmony_ci .success()); 242eba8b6baSopenharmony_ci 243eba8b6baSopenharmony_ci // Delete any untracked generated files from previous versions. 244eba8b6baSopenharmony_ci assert!(Command::new("git") 245eba8b6baSopenharmony_ci .arg("clean") 246eba8b6baSopenharmony_ci .arg("-f") 247eba8b6baSopenharmony_ci .arg("-d") 248eba8b6baSopenharmony_ci .current_dir("linux") 249eba8b6baSopenharmony_ci .status() 250eba8b6baSopenharmony_ci .unwrap() 251eba8b6baSopenharmony_ci .success()); 252eba8b6baSopenharmony_ci} 253eba8b6baSopenharmony_ci 254eba8b6baSopenharmony_cifn make_headers_install(linux_arch: &str, linux_headers: &Path) { 255eba8b6baSopenharmony_ci assert!(Command::new("make") 256eba8b6baSopenharmony_ci .arg(format!("headers_install")) 257eba8b6baSopenharmony_ci .arg(format!("ARCH={}", linux_arch)) 258eba8b6baSopenharmony_ci .arg(format!( 259eba8b6baSopenharmony_ci "INSTALL_HDR_PATH={}", 260eba8b6baSopenharmony_ci fs::canonicalize(&linux_headers).unwrap().to_str().unwrap() 261eba8b6baSopenharmony_ci )) 262eba8b6baSopenharmony_ci .current_dir("linux") 263eba8b6baSopenharmony_ci .status() 264eba8b6baSopenharmony_ci .unwrap() 265eba8b6baSopenharmony_ci .success()); 266eba8b6baSopenharmony_ci} 267eba8b6baSopenharmony_ci 268eba8b6baSopenharmony_cifn rust_arches(linux_arch: &str) -> &[&str] { 269eba8b6baSopenharmony_ci match linux_arch { 270eba8b6baSopenharmony_ci "arm" => &["arm"], 271eba8b6baSopenharmony_ci "arm64" => &["aarch64"], 272eba8b6baSopenharmony_ci "avr32" => &["avr"], 273eba8b6baSopenharmony_ci // hexagon gets build errors; disable it for now 274eba8b6baSopenharmony_ci "hexagon" => &[], 275eba8b6baSopenharmony_ci "mips" => &["mips", "mips64"], 276eba8b6baSopenharmony_ci "powerpc" => &["powerpc", "powerpc64"], 277eba8b6baSopenharmony_ci "riscv" => &["riscv32", "riscv64"], 278eba8b6baSopenharmony_ci "s390" => &["s390x"], 279eba8b6baSopenharmony_ci "sparc" => &["sparc", "sparc64"], 280eba8b6baSopenharmony_ci "x86" => &["x86", "x86_64", "x32"], 281eba8b6baSopenharmony_ci "alpha" | "cris" | "h8300" | "m68k" | "microblaze" | "mn10300" | "score" | "blackfin" 282eba8b6baSopenharmony_ci | "frv" | "ia64" | "m32r" | "m68knommu" | "parisc" | "sh" | "um" | "xtensa" 283eba8b6baSopenharmony_ci | "unicore32" | "c6x" | "nios2" | "openrisc" | "csky" | "arc" | "nds32" | "metag" 284eba8b6baSopenharmony_ci | "tile" => &[], 285eba8b6baSopenharmony_ci _ => panic!("unrecognized arch: {}", linux_arch), 286eba8b6baSopenharmony_ci } 287eba8b6baSopenharmony_ci} 288eba8b6baSopenharmony_ci 289eba8b6baSopenharmony_cifn run_bindgen( 290eba8b6baSopenharmony_ci linux_include: &str, 291eba8b6baSopenharmony_ci header_name: &str, 292eba8b6baSopenharmony_ci mod_rs: &str, 293eba8b6baSopenharmony_ci mod_name: &str, 294eba8b6baSopenharmony_ci rust_arch: &str, 295eba8b6baSopenharmony_ci linux_version: &str, 296eba8b6baSopenharmony_ci) { 297eba8b6baSopenharmony_ci let clang_target = compute_clang_target(rust_arch); 298eba8b6baSopenharmony_ci 299eba8b6baSopenharmony_ci eprintln!( 300eba8b6baSopenharmony_ci "Generating bindings for {} on Linux {} architecture {}", 301eba8b6baSopenharmony_ci mod_name, linux_version, rust_arch 302eba8b6baSopenharmony_ci ); 303eba8b6baSopenharmony_ci 304eba8b6baSopenharmony_ci let mut builder = builder() 305eba8b6baSopenharmony_ci // The generated bindings are quite large, so use a few simple options 306eba8b6baSopenharmony_ci // to keep the file sizes down. 307eba8b6baSopenharmony_ci .rustfmt_configuration_file(Some(Path::new("bindgen-rustfmt.toml").to_owned())) 308eba8b6baSopenharmony_ci .layout_tests(false) 309eba8b6baSopenharmony_ci .generate_comments(false) 310eba8b6baSopenharmony_ci .default_enum_style(EnumVariation::Rust { 311eba8b6baSopenharmony_ci non_exhaustive: true, 312eba8b6baSopenharmony_ci }) 313eba8b6baSopenharmony_ci .array_pointers_in_arguments(true) 314eba8b6baSopenharmony_ci .derive_debug(true) 315eba8b6baSopenharmony_ci .clang_arg(&format!("--target={}", clang_target)) 316eba8b6baSopenharmony_ci .clang_arg("-DBITS_PER_LONG=(__SIZEOF_LONG__*__CHAR_BIT__)") 317eba8b6baSopenharmony_ci .clang_arg("-nostdinc") 318eba8b6baSopenharmony_ci .clang_arg("-I") 319eba8b6baSopenharmony_ci .clang_arg(linux_include) 320eba8b6baSopenharmony_ci .clang_arg("-I") 321eba8b6baSopenharmony_ci .clang_arg("include") 322eba8b6baSopenharmony_ci .blocklist_item("NULL"); 323eba8b6baSopenharmony_ci 324eba8b6baSopenharmony_ci // Avoid duplicating ioctl names in the `general` module. 325eba8b6baSopenharmony_ci if mod_name == "general" { 326eba8b6baSopenharmony_ci for ioctl in BufReader::new(File::open("ioctl/generated.txt").unwrap()).lines() { 327eba8b6baSopenharmony_ci builder = builder.blocklist_item(ioctl.unwrap()); 328eba8b6baSopenharmony_ci } 329eba8b6baSopenharmony_ci } 330eba8b6baSopenharmony_ci 331eba8b6baSopenharmony_ci let bindings = builder 332eba8b6baSopenharmony_ci .use_core() 333eba8b6baSopenharmony_ci .ctypes_prefix("crate::ctypes") 334eba8b6baSopenharmony_ci .header(header_name) 335eba8b6baSopenharmony_ci .generate() 336eba8b6baSopenharmony_ci .expect(&format!("generate bindings for {}", mod_name)); 337eba8b6baSopenharmony_ci bindings 338eba8b6baSopenharmony_ci .write_to_file(mod_rs) 339eba8b6baSopenharmony_ci .expect(&format!("write_to_file for {}", mod_name)); 340eba8b6baSopenharmony_ci} 341eba8b6baSopenharmony_ci 342eba8b6baSopenharmony_cifn compute_clang_target(rust_arch: &str) -> String { 343eba8b6baSopenharmony_ci if rust_arch == "x86" { 344eba8b6baSopenharmony_ci format!("i686-unknown-linux") 345eba8b6baSopenharmony_ci } else if rust_arch == "x32" { 346eba8b6baSopenharmony_ci format!("x86_64-unknown-linux-gnux32") 347eba8b6baSopenharmony_ci } else { 348eba8b6baSopenharmony_ci format!("{}-unknown-linux", rust_arch) 349eba8b6baSopenharmony_ci } 350eba8b6baSopenharmony_ci} 351