hyperscan_src/lib.rs
1//! hyperscan-src: 将 intel/hyperscan C++ 源码打包为 Rust crate,通过 cmake 从源码编译。
2//!
3//! # 设计原理
4//!
5//! 本 crate 使用 **openssl-src 模式**:cmake 调用放在 `lib.rs` 而非 `build.rs`。
6//! 原因:将 cmake 调用放入 lib.rs 后,下游 crate(hypervectorscan-sys)在其 `build.rs`
7//! 中通过函数调用执行。此时所有 `cargo:rustc-link-*` 指令都在 hypervectorscan-sys 的
8//! build script 进程内发出 → Cargo 能正确将这些链接指令应用到最终产物。
9//!
10//! 如果放在本 crate 的 build.rs,link 指令会因为 crate 间隔离而无法传递到最终二进制。
11//!
12//! # 系统依赖
13//!
14//! 编译本 crate 需要:
15//! - cmake ≥ 3.x
16//! - C++ 编译器(gcc / clang)
17//! - ragel
18//! - boost-dev
19//!
20//! 这些依赖仅在启用 `vendored` feature 时需要。缺少时 cmake 的 configure 阶段会报错,
21//! 错误信息会通过 cargo 输出清晰的提示。
22
23use std::path::PathBuf;
24
25/// 构建配置
26pub struct Build {
27 fat_runtime: bool,
28 build_avx512: bool,
29 build_avx512vbmi: bool,
30}
31
32/// 构建产物路径信息
33pub struct Artifacts {
34 /// 头文件目录(包含 hs/hs.h)
35 pub include_dir: PathBuf,
36 /// 库文件目录(libhs.a, libhs_runtime.a)
37 pub lib_dir: PathBuf,
38 /// 链接的库名列表
39 pub libs: Vec<String>,
40}
41
42impl Build {
43 /// 创建默认构建配置。
44 ///
45 /// 默认启用 FAT_RUNTIME(运行时 CPU 特性检测),避免在不支持 AVX-512 的 CPU 上
46 /// 因预编译二进制包含 AVX-512 指令而导致 SIGILL。
47 pub fn new() -> Self {
48 Build {
49 fat_runtime: true,
50 build_avx512: false,
51 build_avx512vbmi: false,
52 }
53 }
54
55 /// 启用/禁用 FAT_RUNTIME(运行时 CPU 分发,默认启用)
56 pub fn fat_runtime(&mut self, v: bool) -> &mut Self {
57 self.fat_runtime = v;
58 self
59 }
60 /// 启用 AVX512 指令集支持(x86_64 可选)
61 pub fn avx512(&mut self, v: bool) -> &mut Self {
62 self.build_avx512 = v;
63 self
64 }
65 /// 启用 AVX512VBMI 指令集支持(x86_64 可选,依赖 BUILD_AVX512)
66 pub fn avxvnni(&mut self, v: bool) -> &mut Self {
67 self.build_avx512vbmi = v;
68 self
69 }
70
71 /// 执行 cmake 构建,编译 Hyperscan 静态库。
72 ///
73 /// 此函数内部:
74 /// 1. 调用 `cmake::Config::new("hyperscan")` 配置构建
75 /// 2. 设置必要的 cmake flags(静态库、Release、FAT_RUNTIME 等)
76 /// 3. 调用 `config.build()` 执行编译+安装
77 /// 4. 通过 `println!("cargo:rustc-link-*")` 输出链接指令
78 /// (这些指令由调用者进程发出,因此能正确应用到最终二进制)
79 /// 5. 返回包含头文件/库文件路径的 Artifacts
80 pub fn build(&mut self) -> Artifacts {
81 println!(
82 "cargo:warning=Vendored Hyperscan build — requires: cmake, C++ compiler, ragel, boost-dev"
83 );
84 let source_dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("hyperscan");
85 let mut config = cmake::Config::new(source_dir);
86 config
87 .define("BUILD_STATIC_LIBS", "ON")
88 .define("BUILD_SHARED_LIBS", "OFF")
89 .define("CMAKE_BUILD_TYPE", "Release")
90 .define("CMAKE_INSTALL_LIBDIR", "lib")
91 .define("BUILD_EXAMPLES", "OFF")
92 .define("BUILD_BENCHMARKS", "OFF");
93
94 if self.fat_runtime {
95 config.define("FAT_RUNTIME", "ON");
96 }
97 if self.build_avx512 {
98 config.define("BUILD_AVX512", "ON");
99 }
100 if self.build_avx512vbmi {
101 config.define("BUILD_AVX512VBMI", "ON");
102 }
103
104 let dst = config.build();
105
106 // Some cmake versions install to lib64 or multiarch paths;
107 // search lib/, lib64/, lib/<arch>-linux-gnu/ — first containing libhs.a wins.
108 let lib_dir = {
109 let candidates: Vec<PathBuf> = vec![
110 dst.join("lib"),
111 dst.join("lib64"),
112 dst.join(format!("lib/{}-linux-gnu", std::env::consts::ARCH)),
113 ];
114 candidates
115 .into_iter()
116 .find(|d| d.join("libhs.a").exists())
117 .unwrap_or_else(|| dst.join("lib"))
118 };
119
120 println!("cargo:rustc-link-search=native={}", lib_dir.display());
121 println!("cargo:rustc-link-lib=static=hs");
122 println!("cargo:rustc-link-lib=static=hs_runtime");
123
124 Artifacts {
125 include_dir: dst.join("include"),
126 lib_dir,
127 libs: vec!["hs".into(), "hs_runtime".into()],
128 }
129 }
130}