use std::{
env, fs,
path::{Path, PathBuf},
process::Command,
};
fn collect_cpp_files(dir: &Path) -> Vec<PathBuf> {
let mut files = fs::read_dir(dir)
.unwrap_or_else(|error| panic!("failed to read {}: {error}", dir.display()))
.filter_map(|entry| {
let path = entry.ok()?.path();
(path.extension().and_then(|ext| ext.to_str()) == Some("cpp")).then_some(path)
})
.collect::<Vec<_>>();
files.sort();
files
}
fn bento4_include_dirs(source_root: &Path) -> [PathBuf; 4] {
[
source_root.join("Core"),
source_root.join("Codecs"),
source_root.join("Crypto"),
source_root.join("MetaData"),
]
}
fn add_bento4_sources(build: &mut cc::Build, source_root: &Path) {
for component in ["Codecs", "Core", "Crypto", "MetaData"] {
for file in collect_cpp_files(&source_root.join(component)) {
build.file(file);
}
}
build.file(
source_root
.join("System")
.join("StdC")
.join("Ap4StdCFileByteStream.cpp"),
);
let random_source = if env::var("CARGO_CFG_TARGET_OS")
.expect("CARGO_CFG_TARGET_OS env variable not set")
== "windows"
{
source_root
.join("System")
.join("Win32")
.join("Ap4Win32Random.cpp")
} else {
source_root
.join("System")
.join("Posix")
.join("Ap4PosixRandom.cpp")
};
build.file(random_source);
}
fn add_include_flags(command: &mut Command, compiler: &cc::Tool, include_dirs: &[PathBuf]) {
for dir in include_dirs {
if compiler.is_like_msvc() {
command.arg(format!("/I{}", dir.display()));
} else {
command.arg("-I").arg(dir);
}
}
}
fn compiled_library_path(out_dir: &Path) -> PathBuf {
if env::var("CARGO_CFG_TARGET_ENV").expect("CARGO_CFG_TARGET_ENV env variable not set")
== "msvc"
{
out_dir.join("rsmp4decrypt.lib")
} else {
out_dir.join("librsmp4decrypt.a")
}
}
fn worker_binary_name() -> &'static str {
if env::var("CARGO_CFG_TARGET_OS").expect("CARGO_CFG_TARGET_OS env variable not set")
== "windows"
{
"rsmp4decrypt-worker.exe"
} else {
"rsmp4decrypt-worker"
}
}
fn compile_worker_executable(manifest_dir: &Path, source_root: &Path, out_dir: &Path) {
let include_dirs = bento4_include_dirs(source_root);
let native_dir = manifest_dir.join("src").join("native");
let library_path = compiled_library_path(out_dir);
let worker_source = native_dir.join("rsmp4decrypt_worker.cpp");
let worker_output = out_dir.join(worker_binary_name());
let worker_object = if env::var("CARGO_CFG_TARGET_ENV")
.expect("CARGO_CFG_TARGET_ENV env variable not set")
== "msvc"
{
out_dir.join("rsmp4decrypt_worker.obj")
} else {
out_dir.join("rsmp4decrypt_worker.o")
};
if !library_path.exists() {
panic!(
"native library archive is missing. Expected {}",
library_path.display()
);
}
let mut build = cc::Build::new();
build
.cpp(true)
.warnings(false)
.extra_warnings(false)
.includes(&include_dirs)
.include(&native_dir);
let compiler = build.get_compiler();
let mut command = compiler.to_command();
command.current_dir(out_dir);
add_include_flags(&mut command, &compiler, &include_dirs);
if compiler.is_like_msvc() {
command.arg(format!("/I{}", native_dir.display()));
command.arg(format!("/Fo:{}", worker_object.display()));
command.arg(&worker_source);
command.arg(&library_path);
command.arg(format!("/Fe:{}", worker_output.display()));
} else {
command.arg("-I").arg(&native_dir);
command.arg(&worker_source);
command.arg(&library_path);
command.arg("-o").arg(&worker_output);
}
let status = command
.status()
.unwrap_or_else(|error| panic!("failed to invoke {}: {error}", compiler.path().display()));
if !status.success() {
panic!(
"failed to build native worker executable with {}",
compiler.path().display()
);
}
println!(
"cargo:rustc-env=RSMP4DECRYPT_WORKER_EMBED={}",
worker_output.display()
);
}
fn main() {
let manifest_dir = PathBuf::from(
env::var_os("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR env variable not set"),
);
let bento4_root = manifest_dir.join("vendor").join("bento4-src");
let source_root = bento4_root.join("Source").join("C++");
let out_dir = PathBuf::from(env::var_os("OUT_DIR").expect("OUT_DIR env variable not set"));
if !source_root.join("Core").join("Ap4.h").exists() {
panic!(
"Bento4 source snapshot is missing. Expected sources under {}",
manifest_dir.join("vendor").join("bento4-src").display()
);
}
println!("cargo:rerun-if-changed={}", bento4_root.display());
println!("cargo:rerun-if-changed=src/native/rsmp4decrypt.cpp");
println!("cargo:rerun-if-changed=src/native/rsmp4decrypt.h");
println!("cargo:rerun-if-changed=src/native/rsmp4decrypt_worker.cpp");
let include_dirs = bento4_include_dirs(&source_root);
let mut build = cc::Build::new();
build
.cpp(true)
.warnings(false)
.extra_warnings(false)
.includes(&include_dirs);
add_bento4_sources(&mut build, &source_root);
build.file(
manifest_dir
.join("src")
.join("native")
.join("rsmp4decrypt.cpp"),
);
build.compile("rsmp4decrypt");
compile_worker_executable(&manifest_dir, &source_root, &out_dir);
}