270 lines
8.3 KiB
Rust
270 lines
8.3 KiB
Rust
use std::process::Command;
|
||
use std::env;
|
||
use std::fs;
|
||
use std::path::Path;
|
||
|
||
fn main() {
|
||
// 检测当前是否在树莓派上
|
||
let is_raspberry_pi = detect_raspberry_pi();
|
||
|
||
// 自动启用GPIO功能(仅当是树莓派时)
|
||
if is_raspberry_pi {
|
||
println!("cargo:rustc-cfg=feature=\"gpio\"");
|
||
println!("cargo:warning=Raspberry Pi detected, enabling gpio feature");
|
||
} else {
|
||
println!("cargo:warning=Not running on Raspberry Pi, gpio feature NOT enabled");
|
||
}
|
||
|
||
// 通过包含的 OpenCV 库检测版本
|
||
let opencv_version = detect_opencv_version();
|
||
|
||
println!("cargo:rustc-env=OPENCV_VERSION={}", opencv_version);
|
||
|
||
// 解析版本
|
||
let version_parts: Vec<&str> = opencv_version.split('.').collect();
|
||
let major = version_parts.get(0).and_then(|s| s.parse::<u32>().ok()).unwrap_or(0);
|
||
let minor = version_parts.get(1).and_then(|s| s.parse::<u32>().ok()).unwrap_or(0);
|
||
|
||
// 如果版本 >= 4.11.0,启用特性
|
||
if major > 4 || (major == 4 && minor >= 11) {
|
||
println!("cargo:rustc-cfg=feature=\"opencv-4-11-plus\"");
|
||
println!("cargo:warning=Detected OpenCV {} >= 4.11.0, enabling opencv-4-11-plus feature", opencv_version);
|
||
} else {
|
||
println!("cargo:warning=Detected OpenCV {}, opencv-4-11-plus feature NOT enabled", opencv_version);
|
||
}
|
||
}
|
||
|
||
fn detect_raspberry_pi() -> bool {
|
||
// 方法1: 检查是否是Linux系统(必要条件)
|
||
let is_linux = env::consts::OS == "linux";
|
||
if !is_linux {
|
||
return false;
|
||
}
|
||
|
||
// 方法2: 检查/proc/cpuinfo是否包含Raspberry Pi的特征
|
||
if let Ok(cpuinfo) = fs::read_to_string("/proc/cpuinfo") {
|
||
if cpuinfo.contains("Raspberry Pi") ||
|
||
cpuinfo.contains("BCM2708") ||
|
||
cpuinfo.contains("BCM2709") ||
|
||
cpuinfo.contains("BCM2710") ||
|
||
cpuinfo.contains("BCM2711") ||
|
||
cpuinfo.contains("BCM2835") ||
|
||
cpuinfo.contains("BCM2836") ||
|
||
cpuinfo.contains("BCM2837") ||
|
||
cpuinfo.contains("BCM2838") {
|
||
return true;
|
||
}
|
||
}
|
||
|
||
// 方法3: 检查树莓派特有的设备树模型文件
|
||
if let Ok(model) = fs::read_to_string("/sys/firmware/devicetree/base/model") {
|
||
if model.contains("Raspberry Pi") {
|
||
return true;
|
||
}
|
||
}
|
||
|
||
// 方法4: 检查是否存在树莓派特有的库
|
||
let lib_check = Command::new("ldconfig")
|
||
.args(["-p"])
|
||
.output()
|
||
.ok()
|
||
.and_then(|output| {
|
||
if output.status.success() {
|
||
let libraries = String::from_utf8_lossy(&output.stdout);
|
||
if libraries.contains("librpgpio") || libraries.contains("libraspberrypi") {
|
||
Some(true)
|
||
} else {
|
||
None
|
||
}
|
||
} else {
|
||
None
|
||
}
|
||
});
|
||
|
||
if lib_check.is_some() {
|
||
return true;
|
||
}
|
||
|
||
// 方法5: 检查树莓派特有的配置文件
|
||
if Path::new("/boot/config.txt").exists() || Path::new("/boot/firmware/config.txt").exists() {
|
||
// 这个文件在大多数树莓派系统上存在
|
||
return true;
|
||
}
|
||
|
||
// 检查是否在CARGO_FEATURE_GPIO环境变量中强制启用
|
||
// 这允许用户手动启用GPIO功能(覆盖自动检测)
|
||
if env::var("CARGO_FEATURE_GPIO").is_ok() {
|
||
return true;
|
||
}
|
||
|
||
// 否则默认为false
|
||
false
|
||
}
|
||
|
||
fn detect_opencv_version() -> String {
|
||
// 方法 1: 尝试使用 pkg-config
|
||
if let Some(version) = try_pkg_config() {
|
||
return version;
|
||
}
|
||
|
||
// 方法 2: 尝试使用 opencv_version 命令
|
||
if let Some(version) = try_opencv_version_command() {
|
||
return version;
|
||
}
|
||
|
||
// 方法 3: 尝试编译并运行一个小程序来检测版本
|
||
if let Some(version) = try_compile_check() {
|
||
return version;
|
||
}
|
||
|
||
// 方法 4: 检查 OpenCV 头文件
|
||
if let Some(version) = try_header_check() {
|
||
return version;
|
||
}
|
||
|
||
// 回退到默认版本
|
||
let default_version = "4.0.0".to_string();
|
||
println!("cargo:warning=Could not detect OpenCV version, assuming {}", default_version);
|
||
default_version
|
||
}
|
||
|
||
fn try_pkg_config() -> Option<String> {
|
||
// 尝试使用 pkg-config 来获取 OpenCV 版本
|
||
let output = Command::new("pkg-config")
|
||
.args(["--modversion", "opencv4"])
|
||
.output()
|
||
.ok()?;
|
||
|
||
if output.status.success() {
|
||
let version = String::from_utf8(output.stdout).ok()?;
|
||
let version = version.trim().to_string();
|
||
if !version.is_empty() {
|
||
return Some(version);
|
||
}
|
||
}
|
||
|
||
// 尝试 opencv 包(旧版本)
|
||
let output = Command::new("pkg-config")
|
||
.args(["--modversion", "opencv"])
|
||
.output()
|
||
.ok()?;
|
||
|
||
if output.status.success() {
|
||
let version = String::from_utf8(output.stdout).ok()?;
|
||
let version = version.trim().to_string();
|
||
if !version.is_empty() {
|
||
return Some(version);
|
||
}
|
||
}
|
||
|
||
None
|
||
}
|
||
|
||
fn try_opencv_version_command() -> Option<String> {
|
||
// 尝试运行 opencv_version 命令
|
||
let output = Command::new("opencv_version")
|
||
.output()
|
||
.ok()?;
|
||
|
||
if output.status.success() {
|
||
let version = String::from_utf8(output.stdout).ok()?;
|
||
let version = version.trim().to_string();
|
||
if !version.is_empty() {
|
||
return Some(version);
|
||
}
|
||
}
|
||
|
||
None
|
||
}
|
||
|
||
fn try_compile_check() -> Option<String> {
|
||
// 创建一个临时目录
|
||
let out_dir = env::var("OUT_DIR").ok()?;
|
||
let temp_file = format!("{}/version_check.cpp", out_dir);
|
||
let temp_exe = format!("{}/version_check", out_dir);
|
||
|
||
// 写入一个简单的 C++ 程序来打印 OpenCV 版本
|
||
std::fs::write(&temp_file, r#"
|
||
#include <opencv2/core.hpp>
|
||
#include <iostream>
|
||
|
||
int main() {
|
||
std::cout << CV_VERSION << std::endl;
|
||
return 0;
|
||
}
|
||
"#).ok()?;
|
||
|
||
// 尝试编译
|
||
let status = Command::new("c++")
|
||
.args([&temp_file, "-o", &temp_exe, "-lopencv_core"])
|
||
.status()
|
||
.ok()?;
|
||
|
||
if !status.success() {
|
||
return None;
|
||
}
|
||
|
||
// 运行程序获取版本
|
||
let output = Command::new(&temp_exe)
|
||
.output()
|
||
.ok()?;
|
||
|
||
if output.status.success() {
|
||
let version = String::from_utf8(output.stdout).ok()?;
|
||
let version = version.trim().to_string();
|
||
if !version.is_empty() {
|
||
return Some(version);
|
||
}
|
||
}
|
||
|
||
None
|
||
}
|
||
|
||
fn try_header_check() -> Option<String> {
|
||
// 尝试在常见位置查找 OpenCV 头文件
|
||
let paths = [
|
||
"/usr/include/opencv4/opencv2/core/version.hpp",
|
||
"/usr/local/include/opencv4/opencv2/core/version.hpp",
|
||
"/usr/include/opencv2/core/version.hpp",
|
||
"/usr/local/include/opencv2/core/version.hpp",
|
||
"/opt/homebrew/include/opencv4/opencv2/core/version.hpp",
|
||
"/opt/homebrew/include/opencv2/core/version.hpp",
|
||
];
|
||
|
||
for path in paths.iter() {
|
||
if let Ok(content) = std::fs::read_to_string(path) {
|
||
// 查找版本定义 #define CV_VERSION_MAJOR 4
|
||
let mut major = None;
|
||
let mut minor = None;
|
||
let mut patch = None;
|
||
|
||
for line in content.lines() {
|
||
if let Some(value) = extract_define(line, "CV_VERSION_MAJOR") {
|
||
major = Some(value);
|
||
} else if let Some(value) = extract_define(line, "CV_VERSION_MINOR") {
|
||
minor = Some(value);
|
||
} else if let Some(value) = extract_define(line, "CV_VERSION_REVISION") {
|
||
patch = Some(value);
|
||
}
|
||
|
||
if major.is_some() && minor.is_some() && patch.is_some() {
|
||
return Some(format!("{}.{}.{}", major.unwrap(), minor.unwrap(), patch.unwrap()));
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
None
|
||
}
|
||
|
||
fn extract_define(line: &str, define_name: &str) -> Option<String> {
|
||
let line = line.trim();
|
||
if line.starts_with("#define") && line.contains(define_name) {
|
||
let parts: Vec<&str> = line.split_whitespace().collect();
|
||
if parts.len() >= 3 && parts[1] == define_name {
|
||
return Some(parts[2].to_string());
|
||
}
|
||
}
|
||
None
|
||
}
|