build.rs 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. use std::{
  2. env, fs,
  3. path::{Path, PathBuf},
  4. println,
  5. };
  6. #[cfg(all(target_os = "linux", feature = "linux-pkg-config"))]
  7. fn link_pkg_config(name: &str) -> Vec<PathBuf> {
  8. // sometimes an override is needed
  9. let pc_name = match name {
  10. "libvpx" => "vpx",
  11. _ => name,
  12. };
  13. let lib = pkg_config::probe_library(pc_name)
  14. .expect(format!(
  15. "unable to find '{pc_name}' development headers with pkg-config (feature linux-pkg-config is enabled).
  16. try installing '{pc_name}-dev' from your system package manager.").as_str());
  17. lib.include_paths
  18. }
  19. #[cfg(not(all(target_os = "linux", feature = "linux-pkg-config")))]
  20. fn link_pkg_config(_name: &str) -> Vec<PathBuf> {
  21. unimplemented!()
  22. }
  23. /// Link vcpkg package.
  24. fn link_vcpkg(mut path: PathBuf, name: &str) -> PathBuf {
  25. let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap();
  26. let mut target_arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap();
  27. if target_arch == "x86_64" {
  28. target_arch = "x64".to_owned();
  29. } else if target_arch == "x86" {
  30. target_arch = "x86".to_owned();
  31. } else if target_arch == "loongarch64" {
  32. target_arch = "loongarch64".to_owned();
  33. } else if target_arch == "aarch64" {
  34. target_arch = "arm64".to_owned();
  35. } else {
  36. target_arch = "arm".to_owned();
  37. }
  38. let mut target = if target_os == "macos" {
  39. if target_arch == "x64" {
  40. "x64-osx".to_owned()
  41. } else if target_arch == "arm64" {
  42. "arm64-osx".to_owned()
  43. } else {
  44. format!("{}-{}", target_arch, target_os)
  45. }
  46. } else if target_os == "windows" {
  47. "x64-windows-static".to_owned()
  48. } else {
  49. format!("{}-{}", target_arch, target_os)
  50. };
  51. if target_arch == "x86" {
  52. target = target.replace("x64", "x86");
  53. }
  54. println!("cargo:info={}", target);
  55. path.push("installed");
  56. path.push(target);
  57. println!(
  58. "{}",
  59. format!(
  60. "cargo:rustc-link-lib=static={}",
  61. name.trim_start_matches("lib")
  62. )
  63. );
  64. println!(
  65. "{}",
  66. format!(
  67. "cargo:rustc-link-search={}",
  68. path.join("lib").to_str().unwrap()
  69. )
  70. );
  71. let include = path.join("include");
  72. println!("{}", format!("cargo:include={}", include.to_str().unwrap()));
  73. include
  74. }
  75. /// Link homebrew package(for Mac M1).
  76. fn link_homebrew_m1(name: &str) -> PathBuf {
  77. let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap();
  78. let target_arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap();
  79. if target_os != "macos" || target_arch != "aarch64" {
  80. panic!("Couldn't find VCPKG_ROOT, also can't fallback to homebrew because it's only for macos aarch64.");
  81. }
  82. let mut path = PathBuf::from("/opt/homebrew/Cellar");
  83. path.push(name);
  84. let entries = if let Ok(dir) = std::fs::read_dir(&path) {
  85. dir
  86. } else {
  87. panic!("Could not find package in {}. Make sure your homebrew and package {} are all installed.", path.to_str().unwrap(),&name);
  88. };
  89. let mut directories = entries
  90. .into_iter()
  91. .filter(|x| x.is_ok())
  92. .map(|x| x.unwrap().path())
  93. .filter(|x| x.is_dir())
  94. .collect::<Vec<_>>();
  95. // Find the newest version.
  96. directories.sort_unstable();
  97. if directories.is_empty() {
  98. panic!(
  99. "There's no installed version of {} in /opt/homebrew/Cellar",
  100. name
  101. );
  102. }
  103. path.push(directories.pop().unwrap());
  104. // Link the library.
  105. println!(
  106. "{}",
  107. format!(
  108. "cargo:rustc-link-lib=static={}",
  109. name.trim_start_matches("lib")
  110. )
  111. );
  112. // Add the library path.
  113. println!(
  114. "{}",
  115. format!(
  116. "cargo:rustc-link-search={}",
  117. path.join("lib").to_str().unwrap()
  118. )
  119. );
  120. // Add the include path.
  121. let include = path.join("include");
  122. println!("{}", format!("cargo:include={}", include.to_str().unwrap()));
  123. include
  124. }
  125. /// Find package. By default, it will try to find vcpkg first, then homebrew(currently only for Mac M1).
  126. /// If building for linux and feature "linux-pkg-config" is enabled, will try to use pkg-config
  127. /// unless check fails (e.g. NO_PKG_CONFIG_libyuv=1)
  128. fn find_package(name: &str) -> Vec<PathBuf> {
  129. let no_pkg_config_var_name = format!("NO_PKG_CONFIG_{name}");
  130. println!("cargo:rerun-if-env-changed={no_pkg_config_var_name}");
  131. if cfg!(all(target_os = "linux", feature = "linux-pkg-config"))
  132. && std::env::var(no_pkg_config_var_name).as_deref() != Ok("1")
  133. {
  134. link_pkg_config(name)
  135. } else if let Ok(vcpkg_root) = std::env::var("VCPKG_ROOT") {
  136. vec![link_vcpkg(vcpkg_root.into(), name)]
  137. } else {
  138. // Try using homebrew
  139. vec![link_homebrew_m1(name)]
  140. }
  141. }
  142. fn generate_bindings(
  143. ffi_header: &Path,
  144. include_paths: &[PathBuf],
  145. ffi_rs: &Path,
  146. exact_file: &Path,
  147. regex: &str,
  148. ) {
  149. let mut b = bindgen::builder()
  150. .header(ffi_header.to_str().unwrap())
  151. .allowlist_type(regex)
  152. .allowlist_var(regex)
  153. .allowlist_function(regex)
  154. .rustified_enum(regex)
  155. .trust_clang_mangling(false)
  156. .layout_tests(false) // breaks 32/64-bit compat
  157. .generate_comments(false); // comments have prefix /*!\
  158. for dir in include_paths {
  159. b = b.clang_arg(format!("-I{}", dir.display()));
  160. }
  161. b.generate().unwrap().write_to_file(ffi_rs).unwrap();
  162. fs::copy(ffi_rs, exact_file).ok(); // ignore failure
  163. }
  164. fn gen_vcpkg_package(package: &str, ffi_header: &str, generated: &str, regex: &str) {
  165. let includes = find_package(package);
  166. let src_dir = env::var_os("CARGO_MANIFEST_DIR").unwrap();
  167. let src_dir = Path::new(&src_dir);
  168. let out_dir = env::var_os("OUT_DIR").unwrap();
  169. let out_dir = Path::new(&out_dir);
  170. let ffi_header = src_dir.join("src").join("bindings").join(ffi_header);
  171. println!("rerun-if-changed={}", ffi_header.display());
  172. for dir in &includes {
  173. println!("rerun-if-changed={}", dir.display());
  174. }
  175. let ffi_rs = out_dir.join(generated);
  176. let exact_file = src_dir.join("generated").join(generated);
  177. generate_bindings(&ffi_header, &includes, &ffi_rs, &exact_file, regex);
  178. }
  179. fn main() {
  180. // note: all link symbol names in x86 (32-bit) are prefixed wth "_".
  181. // run "rustup show" to show current default toolchain, if it is stable-x86-pc-windows-msvc,
  182. // please install x64 toolchain by "rustup toolchain install stable-x86_64-pc-windows-msvc",
  183. // then set x64 to default by "rustup default stable-x86_64-pc-windows-msvc"
  184. let target = target_build_utils::TargetInfo::new();
  185. if target.unwrap().target_pointer_width() != "64" {
  186. // panic!("Only support 64bit system");
  187. }
  188. env::remove_var("CARGO_CFG_TARGET_FEATURE");
  189. env::set_var("CARGO_CFG_TARGET_FEATURE", "crt-static");
  190. find_package("libyuv");
  191. gen_vcpkg_package("libvpx", "vpx_ffi.h", "vpx_ffi.rs", "^[vV].*");
  192. gen_vcpkg_package("aom", "aom_ffi.h", "aom_ffi.rs", "^(aom|AOM|OBU|AV1).*");
  193. gen_vcpkg_package("libyuv", "yuv_ffi.h", "yuv_ffi.rs", ".*");
  194. // there is problem with cfg(target_os) in build.rs, so use our workaround
  195. let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap();
  196. if target_os == "ios" {
  197. // nothing
  198. } else if target_os == "android" {
  199. println!("cargo:rustc-cfg=android");
  200. } else if cfg!(windows) {
  201. // The first choice is Windows because DXGI is amazing.
  202. println!("cargo:rustc-cfg=dxgi");
  203. } else if cfg!(target_os = "macos") {
  204. // Quartz is second because macOS is the (annoying) exception.
  205. println!("cargo:rustc-cfg=quartz");
  206. } else if cfg!(unix) {
  207. // On UNIX we pray that X11 (with XCB) is available.
  208. println!("cargo:rustc-cfg=x11");
  209. }
  210. }