#[cfg(feature = "generate_binding")]
use std::path::PathBuf;
use std::{env, fmt::Display, path::Path};
const fn rustc_linking_word(is_static_link: bool) -> &'static str {
if is_static_link {
"static"
} else {
"dylib"
}
}
#[cfg(feature = "generate_binding")]
fn generate_binding() {
const ALLOW_UNCONVENTIONALS: &'static str = "#![allow(non_upper_case_globals)]\n\
#![allow(non_camel_case_types)]\n\
#![allow(non_snake_case)]\n";
let bindings = bindgen::Builder::default()
.header("src/wrapper.h")
.raw_line(ALLOW_UNCONVENTIONALS)
.generate()
.expect("Unable to generate binding");
let binding_target_path = PathBuf::new().join("src").join("lib.rs");
bindings
.write_to_file(binding_target_path)
.expect("Could not write binding to the file at `src/lib.rs`");
println!("cargo:info=Successfully generated binding.");
}
fn build_opus(is_static: bool) {
let opus_path = Path::new("opus");
if !opus_path.exists() {
panic!(
"'opus/' source directory not found. To build without a system lib, enable the 'bundled' feature or set OPUS_LIB_DIR/LIBOPUS_LIB_DIR.\n\
- Enable feature: cargo build --features bundled\n\
- Or install libopus and set OPUS_LIB_DIR/LIBOPUS_LIB_DIR to its prefix (containing 'lib')."
);
}
let display_path = opus_path
.canonicalize()
.ok()
.map(|p| p.display().to_string())
.unwrap_or_else(|| opus_path.display().to_string());
println!("cargo:info=Opus source path used: {}.", display_path);
println!("cargo:info=Building Opus via CMake.");
let mut cfg = cmake::Config::new(opus_path);
cfg.profile("Release");
if is_static {
cfg.define("BUILD_SHARED_LIBS", "OFF")
.define("OPUS_BUILD_SHARED_LIBRARY", "OFF")
.define("OPUS_BUILD_STATIC_LIBRARY", "ON");
} else {
cfg.define("BUILD_SHARED_LIBS", "ON")
.define("OPUS_BUILD_SHARED_LIBRARY", "ON")
.define("OPUS_BUILD_STATIC_LIBRARY", "OFF");
}
if let Ok(explicit_version) = env::var("OPUS_PACKAGE_VERSION") {
if !explicit_version.trim().is_empty() {
cfg.define("OPUS_PACKAGE_VERSION", &explicit_version);
cfg.define("PACKAGE_VERSION", &explicit_version);
}
} else if let Ok(contents) = std::fs::read_to_string(opus_path.join("package_version"))
.or_else(|_| std::fs::read_to_string(opus_path.join("cmake").join("package_version")))
{
if let Some(v) = contents.lines().find_map(|l| {
let l = l.trim();
if l.starts_with("PACKAGE_VERSION=") {
Some(
l.trim_start_matches("PACKAGE_VERSION=")
.trim_matches('"')
.to_string(),
)
} else {
None
}
}) {
if !v.is_empty() {
cfg.define("OPUS_PACKAGE_VERSION", &v);
cfg.define("PACKAGE_VERSION", &v);
}
}
}
cfg.define("OPUS_BUILD_TESTING", "OFF")
.define("OPUS_ENABLE_DOC", "OFF")
.define("OPUS_INSTALL_PKG_CONFIG_MODULE", "OFF");
let opus_build_dir = cfg.build();
link_opus(is_static, opus_build_dir.display())
}
fn link_opus(is_static: bool, opus_build_dir: impl Display) {
let is_static_text = rustc_linking_word(is_static);
println!(
"cargo:info=Linking Opus as {} lib: {}",
is_static_text, opus_build_dir
);
println!("cargo:rustc-link-lib={}=opus", is_static_text);
println!("cargo:rustc-link-search=native={}/lib", opus_build_dir);
if is_static && (cfg!(unix) || cfg!(target_env = "gnu")) {
println!("cargo:rustc-link-lib=m");
}
}
#[cfg(target_os = "macos")]
fn add_homebrew_opus_search_path() {
use std::process::Command;
if let Ok(output) = Command::new("brew").args(["--prefix", "opus"]).output() {
if output.status.success() {
let prefix = String::from_utf8_lossy(&output.stdout).trim().to_string();
if !prefix.is_empty() {
let lib_dir = format!("{}/lib", prefix);
println!("cargo:info=Adding Homebrew Opus search path: {}", lib_dir);
println!("cargo:rustc-link-search=native={}", lib_dir);
}
}
}
}
#[cfg(not(target_os = "macos"))]
fn add_homebrew_opus_search_path() {}
#[cfg(any(unix, target_env = "gnu"))]
fn find_via_pkg_config(is_static: bool) -> bool {
pkg_config::Config::new()
.statik(is_static)
.probe("opus")
.is_ok()
}
fn default_library_linking() -> bool {
#[cfg(any(windows, target_os = "macos", target_env = "musl"))]
{
true
}
#[cfg(any(target_os = "freebsd", all(unix, target_env = "gnu")))]
{
false
}
}
fn find_installed_opus() -> Option<String> {
if let Ok(lib_directory) = env::var("LIBOPUS_LIB_DIR") {
Some(lib_directory)
} else if let Ok(lib_directory) = env::var("OPUS_LIB_DIR") {
Some(lib_directory)
} else {
None
}
}
fn is_static_build() -> bool {
if cfg!(feature = "static") && cfg!(feature = "dynamic") {
default_library_linking()
} else if cfg!(feature = "static")
|| env::var("LIBOPUS_STATIC").is_ok()
|| env::var("OPUS_STATIC").is_ok()
{
println!("cargo:info=Static feature or environment variable found.");
true
} else if cfg!(feature = "dynamic") {
println!("cargo:info=Dynamic feature enabled.");
false
} else {
println!("cargo:info=No feature or environment variable found, linking by default.");
default_library_linking()
}
}
fn bundled_enabled() -> bool {
cfg!(feature = "bundled")
|| env::var("LIBOPUS_BUNDLED").is_ok()
|| env::var("OPUS_BUNDLED").is_ok()
}
fn main() {
println!("cargo:rerun-if-changed=opus");
println!("cargo:rerun-if-env-changed=OPUS_LIB_DIR");
println!("cargo:rerun-if-env-changed=LIBOPUS_LIB_DIR");
println!("cargo:rerun-if-env-changed=OPUS_BUNDLED");
println!("cargo:rerun-if-env-changed=LIBOPUS_BUNDLED");
println!("cargo:rerun-if-env-changed=OPUS_STATIC");
println!("cargo:rerun-if-env-changed=LIBOPUS_STATIC");
#[cfg(feature = "generate_binding")]
generate_binding();
add_homebrew_opus_search_path();
let is_static = is_static_build();
if bundled_enabled() {
build_opus(is_static);
return;
}
#[cfg(any(unix, target_env = "gnu"))]
{
if std::env::var("LIBOPUS_NO_PKG").is_ok() || std::env::var("OPUS_NO_PKG").is_ok() {
println!("cargo:info=Bypassed `pkg-config`.");
} else if find_via_pkg_config(is_static) {
println!("cargo:info=Found `Opus` via `pkg_config`.");
return;
} else {
println!("cargo:info=`pkg_config` could not find `Opus`.");
}
}
if let Some(installed_opus) = find_installed_opus() {
link_opus(is_static, installed_opus);
} else if Path::new("opus").exists() {
build_opus(is_static);
} else {
panic!(
"Could not locate a system libopus and no vendored 'opus/' was found.\n\
Options to resolve:\n\
1) Enable the 'bundled' feature to build the vendored lib (cargo build --features bundled)\n\
2) Install libopus and set OPUS_LIB_DIR/LIBOPUS_LIB_DIR to its prefix (containing 'lib')\n\
3) On Unix (gnu), ensure pkg-config can find 'opus'"
);
}
}