cargo-xwin drives the Windows MSVC cross-compile via clang-cl, under which CMake sets MSVC=1 — causing libopus 1.3.1's `if(NOT MSVC)` guards to skip the per-file `-msse4.1` / `-mssse3` COMPILE_FLAGS that its x86 SIMD source files need. Clang-cl (unlike real cl.exe) still honors Clang's target-feature system, so those files then fail to compile with "always_inline function '_mm_cvtepi16_epi32' requires target feature 'sse4.1'" errors across silk/NSQ_sse4_1.c, NSQ_del_dec_sse4_1.c, and VQ_WMat_EC_sse4_1.c. Earlier attempts to fix this downstream (cargo-xwin toolchain file, override.cmake CMAKE_C_COMPILE_OBJECT <FLAGS> replace, CFLAGS env vars) all failed because cargo-xwin rewrites override.cmake from scratch on every `cargo xwin build` invocation and cmake-rs's -DCMAKE_C_FLAGS= assembly happens before toolchain FORCE sets propagate. Fixing it upstream at the source: vendor audiopus_sys 0.2.2 into vendor/audiopus_sys, patch its bundled opus/CMakeLists.txt to introduce an MSVC_CL var (true only when CMAKE_C_COMPILER_ID == "MSVC", i.e. real cl.exe), and flip the eight `if(NOT MSVC)` SIMD guards to `if(NOT MSVC_CL)`. Clang-cl then gets the GCC-style per-file flags and the SSE4.1 sources build cleanly. Also flip the `if(MSVC)` global /arch block at line 445 to `if(MSVC_CL)` so only cl.exe applies /arch:AVX and clang-cl relies purely on per-file flags (no global/per-file mixing). Wire via [patch.crates-io] in the workspace root Cargo.toml; the patch is resolved relative to the workspace root as `vendor/audiopus_sys`. Upstream context: xiph/opus#256, xiph/opus PR #257 (both stale). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
150 lines
4.4 KiB
Rust
150 lines
4.4 KiB
Rust
#![deny(rust_2018_idioms)]
|
|
|
|
#[cfg(feature = "generate_binding")]
|
|
use std::path::PathBuf;
|
|
use std::{env, fmt::Display, path::Path};
|
|
|
|
/// Outputs the library-file's prefix as word usable for actual arguments on
|
|
/// commands or paths.
|
|
const fn rustc_linking_word(is_static_link: bool) -> &'static str {
|
|
if is_static_link {
|
|
"static"
|
|
} else {
|
|
"dylib"
|
|
}
|
|
}
|
|
|
|
/// Generates a new binding at `src/lib.rs` using `src/wrapper.h`.
|
|
#[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");
|
|
|
|
println!(
|
|
"cargo:info=Opus source path used: {:?}.",
|
|
opus_path
|
|
.canonicalize()
|
|
.expect("Could not canonicalise to absolute path")
|
|
);
|
|
|
|
println!("cargo:info=Building Opus via CMake.");
|
|
let opus_build_dir = cmake::build(opus_path);
|
|
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);
|
|
}
|
|
|
|
#[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()
|
|
}
|
|
|
|
/// Based on the OS or target environment we are building for,
|
|
/// this function will return an expected default library linking method.
|
|
///
|
|
/// If we build for Windows, MacOS, or Linux with musl, we will link statically.
|
|
/// However, if you build for Linux without musl, we will link dynamically.
|
|
///
|
|
/// **Info**:
|
|
/// This is a helper-function and may not be called if
|
|
/// if the `static`-feature is enabled, the environment variable
|
|
/// `LIBOPUS_STATIC` or `OPUS_STATIC` is set.
|
|
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 main() {
|
|
#[cfg(feature = "generate_binding")]
|
|
generate_binding();
|
|
|
|
let is_static = is_static_build();
|
|
|
|
#[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 {
|
|
build_opus(is_static);
|
|
}
|
|
}
|