use std::io;
use std::path::{Path, PathBuf};
fn main() {
if std::env::var("DOCS_RS").is_ok() {
return;
}
println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rerun-if-changed=wrapper.h");
#[allow(unused_assignments, unused_variables)]
let include_paths;
#[cfg(not(target_os = "windows"))]
#[allow(unused_assignments)]
{
include_paths = find_libde265();
}
#[cfg(target_os = "windows")]
#[allow(unused_assignments)]
{
include_paths = install_libde265_by_vcpkg();
}
#[cfg(feature = "use-bindgen")]
run_bindgen(include_paths);
}
#[allow(dead_code)]
fn prepare_libde265_src() -> PathBuf {
let out_path = PathBuf::from(std::env::var("OUT_DIR").unwrap());
let crate_dir = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap());
let libde265_dir = crate_dir.join("vendor/libde265");
let dst_dir = out_path.join("libde265");
copy_dir_all(libde265_dir, &dst_dir).unwrap();
dst_dir
}
#[cfg(feature = "embedded-libde265")]
fn compile_libde265() -> String {
use std::path::PathBuf;
let out_path = PathBuf::from(std::env::var("OUT_DIR").unwrap());
let libde265_dir = prepare_libde265_src();
let mut build_config = cmake::Config::new(libde265_dir);
build_config.out_dir(out_path.join("libde265_build"));
build_config.define("CMAKE_INSTALL_LIBDIR", "lib");
for key in [
"BUILD_SHARED_LIBS",
"ENABLE_SDL",
"ENABLE_DECODER",
"ENABLE_ENCODER",
] {
build_config.define(key, "OFF");
}
let libde265_build = build_config.build();
libde265_build
.join("lib/pkgconfig")
.to_string_lossy()
.to_string()
}
#[cfg(not(target_os = "windows"))]
fn find_libde265() -> Vec<String> {
#[allow(unused_mut)]
let mut config = system_deps::Config::new();
#[cfg(feature = "embedded-libde265")]
{
unsafe {
std::env::set_var("SYSTEM_DEPS_LIBDE265_BUILD_INTERNAL", "always");
}
config = config.add_build_internal("libde265", |lib, version| {
let pc_file_path = compile_libde265();
system_deps::Library::from_internal_pkg_config(pc_file_path, lib, version)
});
}
use system_deps::Error;
match config.probe() {
Ok(dependencies) => {
let lib = dependencies.get_by_name("libde265").unwrap();
lib.include_paths
.iter()
.map(|p| p.to_string_lossy().to_string())
.collect()
}
Err(err) => {
let err_msg = match &err {
Error::InvalidMetadata(msg) => {
if msg.contains("No version") && msg.contains("libde265") {
"You MUST enable one of the crate features to specify \
minimal supported version of 'libde265' API (e.g. v1_0)."
.to_string()
} else {
err.to_string()
}
}
_ => err.to_string(),
};
println!("cargo:error={}", err_msg);
std::process::exit(1)
}
}
}
#[cfg(target_os = "windows")]
fn install_libde265_by_vcpkg() -> Vec<String> {
let vcpkg_lib = vcpkg::Config::new()
.emit_includes(true)
.find_package("libde265");
match vcpkg_lib {
Ok(lib) => lib
.include_paths
.iter()
.map(|p| p.to_string_lossy().to_string())
.collect(),
Err(err) => {
println!("cargo:warning={}", err);
std::process::exit(1)
}
}
}
#[cfg(feature = "use-bindgen")]
fn run_bindgen(include_paths: Vec<String>) {
#[derive(Debug)]
struct ParseFixer;
impl bindgen::callbacks::ParseCallbacks for ParseFixer {
fn process_comment(&self, _comment: &str) -> Option<String> {
const REMOVE: &[&str] = &[
"=== error codes ===",
"--- warnings ---",
"obsolet",
"currently: 1",
];
for substr in REMOVE {
if _comment.contains(substr) {
return Some("".to_string());
}
}
None
}
}
let mut base_builder = bindgen::Builder::default()
.header("wrapper.h")
.generate_comments(true)
.formatter(bindgen::Formatter::Prettyplease)
.wrap_unsafe_ops(true)
.generate_cstr(true)
.disable_name_namespacing()
.array_pointers_in_arguments(true)
.allowlist_function("(de|en)265_.*")
.allowlist_type("(de|en)265_.*")
.allowlist_var("LIBDE265_.*")
.size_t_is_usize(true)
.clang_args([
"-fparse-all-comments",
"-fretain-comments-from-system-headers",
])
.default_enum_style(bindgen::EnumVariation::ModuleConsts)
.prepend_enum_name(false)
.clang_arg("-std=c++14")
.clang_arg("-x")
.clang_arg("c++")
.parse_callbacks(Box::new(ParseFixer));
for path in include_paths {
base_builder = base_builder.clang_arg(format!("-I{}", path));
}
for struct_name in ["de265_image", "en265_packet"] {
base_builder = base_builder.no_copy(struct_name);
}
let bindings = base_builder
.clone()
.generate()
.expect("Unable to generate bindings");
let out_path = PathBuf::from(std::env::var("OUT_DIR").unwrap());
bindings
.write_to_file(out_path.join("bindings.rs"))
.expect("Couldn't write bindings.rs!");
let code = bindings.to_string();
let mut func_names = Vec::new();
for line in code.lines() {
if !line.contains("pub fn de265_") && !line.contains("pub fn en265_") {
continue;
}
let line = line.trim();
let res: Vec<&str> = line.split(&[' ', '(']).collect();
if res.len() > 3 {
if let &["pub", "fn", name] = &res[..3] {
func_names.push(name)
}
}
}
let mut result = vec![
"use super::*;\n\n",
"#[test]\n",
"fn is_all_functions_exists_in_libde265() {\n",
" let fn_pointers = [\n",
];
for name in func_names {
result.push(" ");
result.push(name);
result.push(" as *const fn(),\n")
}
result.extend(vec![
" ];\n",
" for pointer in fn_pointers.iter() {\n",
" assert!(!pointer.is_null());\n",
" }\n",
"}\n",
]);
let test_module = result.join("");
let test_path = out_path.join("linker_test.rs");
std::fs::write(&test_path, test_module).expect("Couldn't write test module!");
let bindings = base_builder
.layout_tests(false)
.generate()
.expect("Unable to generate bindings without tests");
bindings
.write_to_file(out_path.join("bindings_wo_tests.rs"))
.expect("Couldn't write bindings_wo_tests.rs!");
}
fn copy_dir_all(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> io::Result<()> {
std::fs::create_dir_all(&dst)?;
for entry in std::fs::read_dir(src)? {
let entry = entry?;
let ty = entry.file_type()?;
if ty.is_dir() {
copy_dir_all(entry.path(), dst.as_ref().join(entry.file_name()))?;
} else {
std::fs::copy(entry.path(), dst.as_ref().join(entry.file_name()))?;
}
}
Ok(())
}