#[cfg(any(
feature = "autobuild",
feature = "conan2",
feature = "vcpkg",
feature = "system",
feature = "vendored",
))]
use std::path::Path;
#[cfg(feature = "autobuild")]
fn main() -> anyhow::Result<()> {
autobuild()
}
#[cfg(all(feature = "conan2", not(feature = "autobuild")))]
fn main() -> anyhow::Result<()> {
conan2_build()
}
#[cfg(all(feature = "vcpkg", not(feature = "autobuild")))]
fn main() -> anyhow::Result<()> {
vcpkg_build()
}
#[cfg(all(feature = "system", not(feature = "autobuild")))]
fn main() -> anyhow::Result<()> {
system_build()
}
#[cfg(all(feature = "vendored", not(feature = "autobuild")))]
fn main() -> anyhow::Result<()> {
vendored_build()
}
#[cfg(not(any(
feature = "autobuild",
feature = "conan2",
feature = "vcpkg",
feature = "system",
feature = "vendored",
)))]
fn main() -> anyhow::Result<()> {
anyhow::bail!(
"hypervectorscan-sys: 未启用构建模式 feature。\n\
请启用以下之一: autobuild, conan2, vcpkg, system, vendored\n\
\n\
推荐用法 (通过 hypervectorscan 顶层 crate, 默认 autobuild):\n\
hypervectorscan = \"0.1\"\n\
\n\
或显式指定 vendored 模式:\n\
hypervectorscan = {{ default-features = false, features = [\"vendored\"] }}"
);
}
#[cfg(feature = "autobuild")]
fn autobuild() -> anyhow::Result<()> {
if conan_available() {
println!("cargo:warning=autobuild: 检测到 conan,使用 conan2 构建");
return conan2_build();
}
if vcpkg_available() {
println!("cargo:warning=autobuild: 检测到 vcpkg,使用 vcpkg 构建");
return vcpkg_build();
}
if system_available() {
println!("cargo:warning=autobuild: 检测到系统已安装库,使用 system 构建");
return system_build();
}
println!("cargo:warning=autobuild: 回退到 vendored 构建(bundled 源码 cmake 编译)");
vendored_build()
}
#[cfg(any(feature = "autobuild", feature = "conan2"))]
fn conan_available() -> bool {
std::process::Command::new("conan")
.arg("--version")
.stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::null())
.status()
.map(|s| s.success())
.unwrap_or(false)
}
#[cfg(any(feature = "autobuild", feature = "vcpkg"))]
fn vcpkg_available() -> bool {
std::process::Command::new("vcpkg")
.arg("version")
.stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::null())
.status()
.map(|s| s.success())
.unwrap_or(false)
}
#[cfg(any(feature = "autobuild", feature = "system"))]
fn system_available() -> bool {
if cfg!(target_os = "windows") {
return false;
}
let target = std::env::var("TARGET").unwrap_or_default();
let pc = if is_x86_64(&target) {
"libhs"
} else {
"vectorscan"
};
std::process::Command::new("pkg-config")
.args(["--exists", pc])
.stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::null())
.status()
.map(|s| s.success())
.unwrap_or(false)
}
#[cfg(any(feature = "system", feature = "autobuild"))]
fn system_build() -> anyhow::Result<()> {
let target = std::env::var("TARGET").unwrap_or_default();
let package = if is_x86_64(&target) {
"libhs"
} else {
"vectorscan"
};
println!("cargo:warning=system: 通过 pkg-config 查找 {package}");
let lib = pkg_config::Config::new()
.statik(true)
.probe(package)
.map_err(|_| anyhow::anyhow!("pkg-config 未找到 {package},请安装对应的 dev 包"))?;
let include_paths: Vec<&Path> = lib.include_paths.iter().map(|p| p.as_path()).collect();
generate_bindings(&include_paths, "wrapper.h")?;
link_stdcpp();
Ok(())
}
#[cfg(any(feature = "vendored", feature = "autobuild"))]
fn vendored_build() -> anyhow::Result<()> {
println!("cargo:warning=vendored: 从 bundled 源码编译(无需外部包管理器)");
let target = std::env::var("TARGET").unwrap_or_default();
if is_x86_64(&target) {
println!("cargo:warning=vendored 后端: hyperscan-src (x86_64)");
let mut build = hyperscan_src::Build::new();
#[cfg(feature = "avx512")]
build.avx512(true);
#[cfg(feature = "avxvnni")]
build.avxvnni(true);
let artifacts = build.build();
generate_bindings(&[&artifacts.include_dir], "wrapper.h")?;
} else {
println!("cargo:warning=vendored 后端: vectorscan-src (非 x86_64)");
let artifacts = vectorscan_src::Build::new().build();
generate_bindings(&[&artifacts.include_dir], "wrapper.h")?;
}
link_stdcpp();
Ok(())
}
#[cfg(any(
feature = "autobuild",
feature = "conan2",
feature = "vcpkg",
feature = "system",
feature = "vendored",
))]
fn is_x86_64(target: &str) -> bool {
target.contains("x86_64")
}
#[cfg(any(feature = "vcpkg", feature = "autobuild"))]
fn vcpkg_build() -> anyhow::Result<()> {
println!("cargo:warning=vcpkg 构建模式");
let target = std::env::var("TARGET").unwrap_or_default();
let package = if is_x86_64(&target) {
"hyperscan"
} else {
"vectorscan"
};
println!("cargo:warning=vcpkg: 查找包 {package}");
let lib = match vcpkg::find_package(package) {
Ok(lib) => lib,
Err(_) => {
println!("cargo:warning=vcpkg: 未找到 {package},执行 vcpkg install...");
vcpkg_install(package)?;
vcpkg::find_package(package)?
}
};
let include_paths: Vec<&Path> = lib.include_paths.iter().map(|p| p.as_path()).collect();
generate_bindings(&include_paths, "wrapper.h")?;
link_stdcpp();
Ok(())
}
#[cfg(any(feature = "vcpkg", feature = "autobuild"))]
fn vcpkg_install(package: &str) -> anyhow::Result<()> {
let status = std::process::Command::new("vcpkg")
.args(["install", package])
.status()?;
anyhow::ensure!(status.success(), "vcpkg install {package} 失败");
Ok(())
}
#[cfg(any(feature = "conan2", feature = "autobuild"))]
use conan2_rs::command::BuildPolicy;
#[cfg(any(feature = "conan2", feature = "autobuild"))]
use conan2_rs::command::install::ConanInstall;
#[cfg(any(feature = "conan2", feature = "autobuild"))]
struct Backend {
conanfile: &'static str,
cpp_info_deps: &'static [&'static str],
options: &'static [&'static str],
force_build: Option<&'static str>,
}
#[cfg(any(feature = "conan2", feature = "autobuild"))]
fn detect_backend() -> anyhow::Result<Backend> {
let target = std::env::var("TARGET").unwrap_or_default();
let os = if target.contains("linux") {
"linux"
} else if target.contains("windows") {
"windows"
} else if target.contains("apple-darwin") {
"macos"
} else {
anyhow::bail!("conan2 不支持的目标平台: {target}")
};
let arch = if target.contains("aarch64") || target.contains("arm64") {
"arm64"
} else if target.contains("x86_64") {
"x86_64"
} else {
anyhow::bail!("conan2 不支持的目标架构: {target}")
};
match (os, arch) {
("linux", "x86_64") => Ok(Backend {
conanfile: "conanfile-hyperscan.txt",
cpp_info_deps: &["hyperscan::hs", "hyperscan::hs_runtime"],
options: &["hyperscan/*:fat_runtime=True"],
force_build: Some("hyperscan*"),
}),
("windows", "x86_64") | ("macos", "x86_64") => Ok(Backend {
conanfile: "conanfile-hyperscan.txt",
cpp_info_deps: &["hyperscan::hs", "hyperscan::hs_runtime"],
options: &[],
force_build: None,
}),
("linux", "arm64") | ("macos", "arm64") => Ok(Backend {
conanfile: "conanfile-vectorscan.txt",
cpp_info_deps: &["vectorscan::hs", "vectorscan::hs_runtime"],
options: &[
"vectorscan/*:with_fat_runtime=True",
"vectorscan/*:with_cpu_native=False",
"vectorscan/*:shared=False",
],
force_build: Some("vectorscan*"),
}),
_ => anyhow::bail!("conan2 不支持的目标: {os}/{arch}"),
}
}
#[cfg(any(feature = "conan2", feature = "autobuild"))]
fn conan2_build() -> anyhow::Result<()> {
let backend = detect_backend()?;
println!(
"cargo:warning=conan2 backend: conanfile={}, force_build={:?}",
backend.conanfile, backend.force_build
);
println!("cargo:rerun-if-env-changed=TARGET");
let build_policy = match backend.force_build {
Some(pattern) => BuildPolicy::Pattern(pattern.to_string()),
None => BuildPolicy::Missing(None),
};
let mut builder = ConanInstall::builder()?
.profile("default")
.build(build_policy)
.path(backend.conanfile);
for opt in backend.options {
builder = builder.options(*opt);
}
let command = builder.into_command();
let root = command.run()?;
let cpp_info = root.get_cpp_info(backend.cpp_info_deps)?;
for libdir in cpp_info.lib_dirs.iter() {
println!("cargo:rustc-link-search={}", libdir);
}
for lib in cpp_info.libs.iter() {
println!("cargo:rustc-link-lib=static={}", lib);
}
link_stdcpp();
let include_dirs: Vec<String> = cpp_info.include_dirs.iter().cloned().collect();
let include_paths: Vec<&Path> = include_dirs.iter().map(|d| Path::new(d)).collect();
generate_bindings(&include_paths, "wrapper.h")?;
Ok(())
}
#[cfg(any(
feature = "autobuild",
feature = "conan2",
feature = "vcpkg",
feature = "system",
feature = "vendored",
))]
fn link_stdcpp() {
#[cfg(target_os = "linux")]
println!("cargo:rustc-link-lib=stdc++");
#[cfg(all(target_os = "linux", not(debug_assertions)))]
{
println!("cargo:rustc-target-feature=+crt-static");
println!("cargo:rustc-link-arg=-static-libstdc++");
}
}
#[cfg(any(
feature = "autobuild",
feature = "conan2",
feature = "vcpkg",
feature = "system",
feature = "vendored",
))]
fn generate_bindings(include_dirs: &[&Path], header_file: &str) -> anyhow::Result<()> {
let out_dir = std::env::var("OUT_DIR")?;
let out_path = format!("{out_dir}/hyperscan.rs");
println!("cargo:rerun-if-changed={header_file}");
let mut bindgen = bindgen::builder();
for include in include_dirs {
bindgen = bindgen.clang_arg(format!("-I{}", include.display()));
}
let bindgen = bindgen.header(header_file);
let bindgen = bindgen
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
.derive_partialeq(true)
.allowlist_function("hs_.*")
.allowlist_type("hs_.*")
.allowlist_var("HS_.*");
let bindings = bindgen.generate()?;
bindings.write_to_file(out_path)?;
Ok(())
}