use std::env;
use std::path::PathBuf;
use std::process::Command;
const RPICAM_APP_MIN_VERSION: [u32; 3] = [1, 9, 1];
fn target_env(name: &str) -> Option<String> {
let target = env::var("TARGET").unwrap_or_default();
[
format!("{name}_{target}"),
format!("{name}_{}", target.replace('-', "_")),
name.to_string(),
]
.iter()
.find_map(|var| {
println!("cargo:rerun-if-env-changed={var}");
env::var(var).ok()
})
}
fn rpicam_app_version() -> Option<String> {
let mut cmd = Command::new(target_env("PKG_CONFIG").unwrap_or_else(|| "pkg-config".into()));
for var in ["PKG_CONFIG_LIBDIR", "PKG_CONFIG_PATH", "PKG_CONFIG_SYSROOT_DIR"] {
if let Some(value) = target_env(var) {
cmd.env(var, value);
}
}
let out = cmd.args(["--modversion", "rpicam_app"]).output().ok()?;
out.status.success().then(|| String::from_utf8_lossy(&out.stdout).trim().to_string())
}
fn parse_version(v: &str) -> [u32; 3] {
let mut parts = v.split(|c: char| !c.is_ascii_digit()).filter(|p| !p.is_empty());
let mut next = || parts.next().and_then(|p| p.parse().ok()).unwrap_or(0);
[next(), next(), next()]
}
fn check_rpicam_app_version() {
let min = RPICAM_APP_MIN_VERSION.map(|n| n.to_string()).join(".");
match rpicam_app_version() {
Some(version) if parse_version(&version) < RPICAM_APP_MIN_VERSION => panic!(
"rpicam-apps {version} is too old: libcamlite needs >= {min}. \
Update it (sudo apt full-upgrade) or build rpicam-apps from source."
),
Some(_) => {}
None => println!(
"cargo:warning=could not determine the rpicam-apps version (no rpicam_app.pc); \
libcamlite needs rpicam-apps >= {min}"
),
}
}
fn main() {
check_rpicam_app_version();
let manifest = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap());
let libcamlite = manifest.join("submodules/libcamlite/src");
let probe = |name: &str| {
pkg_config::Config::new()
.probe(name)
.unwrap_or_else(|err| panic!("pkg-config could not find {name}: {err}"))
};
let mut includes: Vec<PathBuf> = Vec::new();
for lib in ["libcamera", "libavformat", "libavcodec", "libavutil"] {
includes.extend(probe(lib).include_paths);
}
match pkg_config::Config::new().probe("rpicam_app") {
Ok(lib) => includes.extend(lib.include_paths),
Err(_) => println!("cargo:rustc-link-lib=rpicam_app"),
}
let rpicam_inc: Vec<PathBuf> = includes
.iter()
.cloned()
.chain(["/usr/include".into(), "/usr/local/include".into()])
.map(|p| p.join("rpicam-apps"))
.filter(|p| p.join("core/rpicam_app.hpp").exists())
.take(1)
.collect();
assert!(!rpicam_inc.is_empty(), "rpicam-apps headers not found (install librpicam-app-dev)");
println!("cargo:rustc-link-lib=boost_program_options");
let sources = ["libcamlite.cpp", "yuv.cpp", "stream_info.cpp", "post_proc.cpp"];
cxx_build::bridge("src/lib.rs")
.file("cxx/wrap.cxx")
.file("cxx/stream_info.c")
.files(sources.iter().map(|s| libcamlite.join(s)))
.includes(&includes)
.includes(&rpicam_inc)
.include(&libcamlite)
.std("c++20")
.flag_if_supported("-Wno-unused-parameter")
.flag_if_supported("-Wno-sign-compare")
.compile("rslibcamlite");
println!("cargo:rerun-if-changed=cxx");
println!("cargo:rerun-if-changed=src/lib.rs");
println!("cargo:rerun-if-changed=submodules/libcamlite/src");
}