build.rs 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  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. if let Ok(vcpkg_root) = std::env::var("VCPKG_INSTALLED_ROOT") {
  56. path = vcpkg_root.into();
  57. } else {
  58. path.push("installed");
  59. }
  60. path.push(target);
  61. println!(
  62. "{}",
  63. format!(
  64. "cargo:rustc-link-lib=static={}",
  65. name.trim_start_matches("lib")
  66. )
  67. );
  68. println!(
  69. "{}",
  70. format!(
  71. "cargo:rustc-link-search={}",
  72. path.join("lib").to_str().unwrap()
  73. )
  74. );
  75. let include = path.join("include");
  76. println!("{}", format!("cargo:include={}", include.to_str().unwrap()));
  77. include
  78. }
  79. /// Link homebrew package(for Mac M1).
  80. fn link_homebrew_m1(name: &str) -> PathBuf {
  81. let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap();
  82. let target_arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap();
  83. if target_os != "macos" || target_arch != "aarch64" {
  84. panic!("Couldn't find VCPKG_ROOT, also can't fallback to homebrew because it's only for macos aarch64.");
  85. }
  86. let mut path = PathBuf::from("/opt/homebrew/Cellar");
  87. path.push(name);
  88. let entries = if let Ok(dir) = std::fs::read_dir(&path) {
  89. dir
  90. } else {
  91. panic!("Could not find package in {}. Make sure your homebrew and package {} are all installed.", path.to_str().unwrap(),&name);
  92. };
  93. let mut directories = entries
  94. .into_iter()
  95. .filter(|x| x.is_ok())
  96. .map(|x| x.unwrap().path())
  97. .filter(|x| x.is_dir())
  98. .collect::<Vec<_>>();
  99. // Find the newest version.
  100. directories.sort_unstable();
  101. if directories.is_empty() {
  102. panic!(
  103. "There's no installed version of {} in /opt/homebrew/Cellar",
  104. name
  105. );
  106. }
  107. path.push(directories.pop().unwrap());
  108. // Link the library.
  109. println!(
  110. "{}",
  111. format!(
  112. "cargo:rustc-link-lib=static={}",
  113. name.trim_start_matches("lib")
  114. )
  115. );
  116. // Add the library path.
  117. println!(
  118. "{}",
  119. format!(
  120. "cargo:rustc-link-search={}",
  121. path.join("lib").to_str().unwrap()
  122. )
  123. );
  124. // Add the include path.
  125. let include = path.join("include");
  126. println!("{}", format!("cargo:include={}", include.to_str().unwrap()));
  127. include
  128. }
  129. /// Find package. By default, it will try to find vcpkg first, then homebrew(currently only for Mac M1).
  130. /// If building for linux and feature "linux-pkg-config" is enabled, will try to use pkg-config
  131. /// unless check fails (e.g. NO_PKG_CONFIG_libyuv=1)
  132. fn find_package(name: &str) -> Vec<PathBuf> {
  133. let no_pkg_config_var_name = format!("NO_PKG_CONFIG_{name}");
  134. println!("cargo:rerun-if-env-changed={no_pkg_config_var_name}");
  135. if cfg!(all(target_os = "linux", feature = "linux-pkg-config"))
  136. && std::env::var(no_pkg_config_var_name).as_deref() != Ok("1")
  137. {
  138. link_pkg_config(name)
  139. } else if let Ok(vcpkg_root) = std::env::var("VCPKG_ROOT") {
  140. vec![link_vcpkg(vcpkg_root.into(), name)]
  141. } else {
  142. // Try using homebrew
  143. vec![link_homebrew_m1(name)]
  144. }
  145. }
  146. fn generate_bindings(
  147. ffi_header: &Path,
  148. include_paths: &[PathBuf],
  149. ffi_rs: &Path,
  150. exact_file: &Path,
  151. regex: &str,
  152. ) {
  153. let mut b = bindgen::builder()
  154. .header(ffi_header.to_str().unwrap())
  155. .allowlist_type(regex)
  156. .allowlist_var(regex)
  157. .allowlist_function(regex)
  158. .rustified_enum(regex)
  159. .trust_clang_mangling(false)
  160. .layout_tests(false) // breaks 32/64-bit compat
  161. .generate_comments(false); // comments have prefix /*!\
  162. for dir in include_paths {
  163. b = b.clang_arg(format!("-I{}", dir.display()));
  164. }
  165. b.generate().unwrap().write_to_file(ffi_rs).unwrap();
  166. fs::copy(ffi_rs, exact_file).ok(); // ignore failure
  167. }
  168. fn gen_vcpkg_package(package: &str, ffi_header: &str, generated: &str, regex: &str) {
  169. let includes = find_package(package);
  170. let src_dir = env::var_os("CARGO_MANIFEST_DIR").unwrap();
  171. let src_dir = Path::new(&src_dir);
  172. let out_dir = env::var_os("OUT_DIR").unwrap();
  173. let out_dir = Path::new(&out_dir);
  174. let ffi_header = src_dir.join("src").join("bindings").join(ffi_header);
  175. println!("rerun-if-changed={}", ffi_header.display());
  176. for dir in &includes {
  177. println!("rerun-if-changed={}", dir.display());
  178. }
  179. let ffi_rs = out_dir.join(generated);
  180. let exact_file = src_dir.join("generated").join(generated);
  181. generate_bindings(&ffi_header, &includes, &ffi_rs, &exact_file, regex);
  182. }
  183. // If you have problems installing ffmpeg, you can download $VCPKG_ROOT/installed from ci
  184. // Linux require link in hwcodec
  185. /*
  186. fn ffmpeg() {
  187. // ffmpeg
  188. let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap();
  189. let target_arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap();
  190. let static_libs = vec!["avcodec", "avutil", "avformat"];
  191. static_libs.iter().for_each(|lib| {
  192. find_package(lib);
  193. });
  194. if target_os == "windows" {
  195. println!("cargo:rustc-link-lib=static=libmfx");
  196. }
  197. // os
  198. let dyn_libs: Vec<&str> = if target_os == "windows" {
  199. ["User32", "bcrypt", "ole32", "advapi32"].to_vec()
  200. } else if target_os == "linux" {
  201. let mut v = ["va", "va-drm", "va-x11", "vdpau", "X11", "stdc++"].to_vec();
  202. if target_arch == "x86_64" {
  203. v.push("z");
  204. }
  205. v
  206. } else if target_os == "macos" || target_os == "ios" {
  207. ["c++", "m"].to_vec()
  208. } else if target_os == "android" {
  209. ["z", "m", "android", "atomic"].to_vec()
  210. } else {
  211. panic!("unsupported os");
  212. };
  213. dyn_libs
  214. .iter()
  215. .map(|lib| println!("cargo:rustc-link-lib={}", lib))
  216. .count();
  217. if target_os == "macos" || target_os == "ios" {
  218. println!("cargo:rustc-link-lib=framework=CoreFoundation");
  219. println!("cargo:rustc-link-lib=framework=CoreVideo");
  220. println!("cargo:rustc-link-lib=framework=CoreMedia");
  221. println!("cargo:rustc-link-lib=framework=VideoToolbox");
  222. println!("cargo:rustc-link-lib=framework=AVFoundation");
  223. }
  224. }
  225. */
  226. fn main() {
  227. // note: all link symbol names in x86 (32-bit) are prefixed wth "_".
  228. // run "rustup show" to show current default toolchain, if it is stable-x86-pc-windows-msvc,
  229. // please install x64 toolchain by "rustup toolchain install stable-x86_64-pc-windows-msvc",
  230. // then set x64 to default by "rustup default stable-x86_64-pc-windows-msvc"
  231. let target = target_build_utils::TargetInfo::new();
  232. if target.unwrap().target_pointer_width() != "64" {
  233. // panic!("Only support 64bit system");
  234. }
  235. env::remove_var("CARGO_CFG_TARGET_FEATURE");
  236. env::set_var("CARGO_CFG_TARGET_FEATURE", "crt-static");
  237. find_package("libyuv");
  238. gen_vcpkg_package("libvpx", "vpx_ffi.h", "vpx_ffi.rs", "^[vV].*");
  239. gen_vcpkg_package("aom", "aom_ffi.h", "aom_ffi.rs", "^(aom|AOM|OBU|AV1).*");
  240. gen_vcpkg_package("libyuv", "yuv_ffi.h", "yuv_ffi.rs", ".*");
  241. // ffmpeg();
  242. // there is problem with cfg(target_os) in build.rs, so use our workaround
  243. let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap();
  244. if target_os == "ios" {
  245. // nothing
  246. } else if target_os == "android" {
  247. println!("cargo:rustc-cfg=android");
  248. } else if cfg!(windows) {
  249. // The first choice is Windows because DXGI is amazing.
  250. println!("cargo:rustc-cfg=dxgi");
  251. } else if cfg!(target_os = "macos") {
  252. // Quartz is second because macOS is the (annoying) exception.
  253. println!("cargo:rustc-cfg=quartz");
  254. } else if cfg!(unix) {
  255. // On UNIX we pray that X11 (with XCB) is available.
  256. println!("cargo:rustc-cfg=x11");
  257. }
  258. }