Looks like find_package of cmake wasn't so useless after all (fixes
MODIFY
build.rs
build.rs
@@ -359,24 +359,27 @@ impl Library {
toolchain,
env::var_os("PROFILE").map_or(false, |p| p == "release"),
);
- let (
- version,
- opencv_include_paths,
- opencv_link_paths,
- opencv_link_libs,
- ) = cmake.probe_ninja(ninja_bin)
+ let mut probe_result = cmake.probe_ninja(ninja_bin)
.or_else(|e| {
eprintln!("=== Probing with cmake ninja generator failed, will try makefile generator, error: {}", e);
cmake.probe_makefile()
+ })
+ .or_else(|e| {
+ eprintln!("=== Probing with cmake Makefile generator failed, will try deprecated find_package, error: {}", e);
+ cmake.probe_find_package()
})?;
- let mut cargo_metadata = Vec::with_capacity(opencv_link_paths.len() + opencv_link_libs.len());
- cargo_metadata.extend(Self::process_link_paths(link_paths, opencv_link_paths.into_iter().collect(), None));
- cargo_metadata.extend(Self::process_link_libs(link_libs, opencv_link_libs, None));
+ if probe_result.version.is_none() {
+ probe_result.version = Self::version_from_include_paths(&probe_result.include_paths);
+ }
+
+ let mut cargo_metadata = Vec::with_capacity(probe_result.link_paths.len() + probe_result.link_libs.len());
+ cargo_metadata.extend(Self::process_link_paths(link_paths, probe_result.link_paths, None));
+ cargo_metadata.extend(Self::process_link_libs(link_libs, probe_result.link_libs, None));
Ok(Self {
- include_paths: Self::process_env_var_list(include_paths, opencv_include_paths),
- version: version.unwrap_or_else(|| "0.0.0".to_string()),
+ include_paths: Self::process_env_var_list(include_paths, probe_result.include_paths),
+ version: probe_result.version.unwrap_or_else(|| "0.0.0".to_string()),
cargo_metadata,
})
}
@@ -466,6 +469,9 @@ impl Library {
|| env::var_os("CMAKE_PREFIX_PATH").is_some()
|| env::var_os("OPENCV_CMAKE_BIN").is_some();
let explicit_vcpkg = env::var_os("VCPKG_ROOT").is_some() || cfg!(target_os = "windows");
+ eprintln!("=== Detected probe priority based on environment vars: pkg_config: {}, cmake: {}, vcpkg: {}",
+ explicit_pkg_config, explicit_cmake, explicit_vcpkg
+ );
let disabled_probes = env::var("OPENCV_DISABLE_PROBES");
let disabled_probes = disabled_probes.as_ref()
@@ -480,23 +486,33 @@ impl Library {
("vcpkg", &probe_vcpkg),
];
+ let mut prioritize = |probe: &str, over: &str| {
+ let (probe_idx, over_idx) = probes.iter().position(|(name, _)| name == &probe)
+ .and_then(|probe_idx| probes.iter().position(|(name, _)| name == &over)
+ .map(|over_idx| (probe_idx, over_idx))
+ )
+ .expect("Can't find probe to swap");
+ if probe_idx > over_idx {
+ for i in (over_idx..probe_idx).rev() {
+ probes.swap(i, i + 1);
+ }
+ }
+ };
+
if explicit_pkg_config {
- if explicit_vcpkg {
- probes.swap(2, 3);
- probes.swap(3, 4);
+ if explicit_vcpkg && !explicit_cmake {
+ prioritize("vcpkg_cmake", "cmake");
+ prioritize("vcpkg", "cmake");
}
} else if explicit_cmake {
- probes.swap(1, 2);
+ prioritize("cmake", "pkg_config");
if explicit_vcpkg {
- probes.swap(2, 3);
- probes.swap(3, 4);
+ prioritize("vcpkg_cmake", "pkg_config");
+ prioritize("vcpkg", "pkg_config");
}
} else if explicit_vcpkg {
- probes.swap(2, 3);
- probes.swap(3, 4);
-
- probes.swap(1, 2);
- probes.swap(2, 3);
+ prioritize("vcpkg_cmake", "pkg_config");
+ prioritize("vcpkg", "pkg_config");
}
let probe_list = probes.iter()

MODIFY
build_cmake_probe.rs
build_cmake_probe.rs
use std::{
- collections::HashSet,
fs::{self, File},
io::{BufRead, BufReader},
path::{Path, PathBuf},
@@ -11,6 +10,13 @@ use shlex::Shlex;
use super::Result;
+pub struct ProbeResult {
+ pub version: Option<String>,
+ pub include_paths: Vec<PathBuf>,
+ pub link_paths: Vec<PathBuf>,
+ pub link_libs: Vec<String>,
+}
+
pub struct CmakeProbe<'r> {
cmake_bin: PathBuf,
build_dir: PathBuf,
@@ -105,37 +111,46 @@ impl<'r> CmakeProbe<'r> {
}
}
- fn extract_from_cmdline(cmdline: &str, link_paths: &mut HashSet<PathBuf>, link_libs: &mut Vec<String>) {
+ fn extract_from_cmdline(cmdline: &str, include_paths: &mut Vec<PathBuf>, link_paths: &mut Vec<PathBuf>, link_libs: &mut Vec<String>) {
for arg in Shlex::new(cmdline.trim()) {
let arg = arg.trim();
- if let Some(path) = arg.strip_prefix("-L") {
- link_paths.insert(PathBuf::from(path.trim_start()));
+ if let Some(path) = arg.strip_prefix("-I") {
+ let path = PathBuf::from(path.trim_start());
+ if !include_paths.contains(&path) {
+ include_paths.push(path);
+ }
+ } else if let Some(path) = arg.strip_prefix("-L").or_else(|| arg.strip_prefix("-Wl,-rpath,")) {
+ let path = PathBuf::from(path.trim_start());
+ if !link_paths.contains(&path) {
+ link_paths.push(path);
+ }
} else if let Some(lib) = arg.strip_prefix("-l") {
link_libs.push(lib.trim_start().to_string());
} else if !arg.starts_with('-') {
let path = Path::new(arg);
- let filename = path.file_name().and_then(super::cleanup_lib_filename);
- if let Some(file) = filename {
- if let Some(parent) = path.parent() {
- if !link_paths.contains(parent) {
- link_paths.insert(parent.to_owned());
+ if let Some(file) = path.file_name().and_then(super::cleanup_lib_filename) {
+ if let Some(parent) = path.parent().map(|p| p.to_owned()) {
+ if !link_paths.contains(&parent) {
+ link_paths.push(parent);
}
} else {
panic!(arg.to_string());
}
link_libs.push(file.to_str().expect("Non-UTF8 filename").to_string());
}
+ } else {
+ eprintln!("=== Unexpected cmake compiler argument found: {}", arg);
}
}
}
- fn extract_from_makefile(&self, link_paths: &mut HashSet<PathBuf>, link_libs: &mut Vec<String>) -> Result<()> {
+ fn extract_from_makefile(&self, link_paths: &mut Vec<PathBuf>, link_libs: &mut Vec<String>) -> Result<()> {
let link_cmdline = fs::read_to_string(self.build_dir.join("CMakeFiles/ocvrs_probe.dir/link.txt"))?;
- Self::extract_from_cmdline(link_cmdline.trim(), link_paths, link_libs);
+ Self::extract_from_cmdline(link_cmdline.trim(), &mut vec![], link_paths, link_libs);
Ok(())
}
- fn extract_from_ninja(&self, link_paths: &mut HashSet<PathBuf>, link_libs: &mut Vec<String>) -> Result<()> {
+ fn extract_from_ninja(&self, include_paths: &mut Vec<PathBuf>, link_paths: &mut Vec<PathBuf>, link_libs: &mut Vec<String>) -> Result<()> {
let mut link_cmdline = BufReader::new(File::open(self.build_dir.join("build.ninja"))?);
let mut line = String::new();
#[derive(Copy, Clone)]
@@ -156,9 +171,9 @@ impl<'r> CmakeProbe<'r> {
}
State::Reading => {
if let Some(paths) = line.trim_start().strip_prefix("LINK_PATH = ") {
- Self::extract_from_cmdline(paths, link_paths, link_libs);
+ Self::extract_from_cmdline(paths, include_paths, link_paths, link_libs);
} else if let Some(libs) = line.trim_start().strip_prefix("LINK_LIBRARIES = ") {
- Self::extract_from_cmdline(libs, link_paths, link_libs);
+ Self::extract_from_cmdline(libs, include_paths, link_paths, link_libs);
}
}
}
@@ -166,29 +181,29 @@ impl<'r> CmakeProbe<'r> {
}
Ok(())
}
- pub fn probe_makefile(&self) -> Result<(Option<String>, Vec<PathBuf>, Vec<PathBuf>, Vec<String>)> {
+ pub fn probe_makefile(&self) -> Result<ProbeResult> {
self.prepare()?;
let mut cmd = self.make_cmd();
cmd.args(&["-G", "Unix Makefiles"]);
let mut version = None;
- let mut opencv_include_paths = Vec::with_capacity(2);
- let mut opencv_link_paths = HashSet::with_capacity(2);
- let mut opencv_link_libs = Vec::with_capacity(64);
+ let mut include_paths = Vec::with_capacity(2);
+ let mut link_paths = Vec::with_capacity(2);
+ let mut link_libs = Vec::with_capacity(64);
eprintln!("=== cmake makefiles probe command: {:?}", cmd);
cmd.output()
.map_err(Box::<dyn std::error::Error>::from)
- .and_then(|output| Self::extract_from_output(&output, &mut version, &mut opencv_include_paths))?;
+ .and_then(|output| Self::extract_from_output(&output, &mut version, &mut include_paths))?;
- self.extract_from_makefile(&mut opencv_link_paths, &mut opencv_link_libs)?;
+ self.extract_from_makefile(&mut link_paths, &mut link_libs)?;
self.cleanup()?;
- Ok((version, opencv_include_paths, opencv_link_paths.into_iter().collect(), opencv_link_libs))
+ Ok(ProbeResult { version, include_paths, link_paths, link_libs })
}
- pub fn probe_ninja(&self, ninja_bin: Option<&Path>) -> Result<(Option<String>, Vec<PathBuf>, Vec<PathBuf>, Vec<String>)> {
+ pub fn probe_ninja(&self, ninja_bin: Option<&Path>) -> Result<ProbeResult> {
self.prepare()?;
let mut cmd = self.make_cmd();
@@ -198,18 +213,73 @@ impl<'r> CmakeProbe<'r> {
}
let mut version = None;
- let mut opencv_include_paths = Vec::with_capacity(2);
- let mut opencv_link_paths = HashSet::with_capacity(2);
- let mut opencv_link_libs = Vec::with_capacity(64);
+ let mut include_paths = Vec::with_capacity(2);
+ let mut link_paths = Vec::with_capacity(2);
+ let mut link_libs = Vec::with_capacity(64);
eprintln!("=== cmake ninja probe command: {:?}", cmd);
cmd.output()
.map_err(Box::<dyn std::error::Error>::from)
- .and_then(|output| Self::extract_from_output(&output, &mut version, &mut opencv_include_paths))?;
+ .and_then(|output| Self::extract_from_output(&output, &mut version, &mut include_paths))?;
- self.extract_from_ninja(&mut opencv_link_paths, &mut opencv_link_libs)?;
+ self.extract_from_ninja(&mut include_paths, &mut link_paths, &mut link_libs)?;
self.cleanup()?;
- Ok((version, opencv_include_paths, opencv_link_paths.into_iter().collect(), opencv_link_libs))
+ Ok(ProbeResult { version, include_paths, link_paths, link_libs })
+ }
+
+ pub fn probe_find_package(&self) -> Result<ProbeResult> {
+ self.prepare()?;
+
+ let mut include_paths = Vec::with_capacity(2);
+ let mut link_paths = Vec::with_capacity(2);
+ let mut link_libs = Vec::with_capacity(64);
+ self.make_cmd()
+ .args(&[
+ "--find-package",
+ "-DCOMPILER_ID=GNU",
+ "-DLANGUAGE=CXX",
+ "-DMODE=COMPILE",
+ ])
+ .arg(format!("-DNAME={}", self.package_name))
+ .output()
+ .map_err(Box::<dyn std::error::Error>::from)
+ .and_then(|output| if output.status.success() {
+ let stdout = String::from_utf8(output.stdout)?;
+ eprintln!("=== cmake include arguments: {:#?}", stdout);
+ Self::extract_from_cmdline(&stdout, &mut include_paths, &mut link_paths, &mut link_libs);
+ Ok(())
+ } else {
+ Err(format!(
+ "cmake returned an error\n stdout: {:?}\n stderr: {:?}",
+ String::from_utf8_lossy(&output.stdout),
+ String::from_utf8_lossy(&output.stderr)
+ ).into())
+ })?;
+
+ self.make_cmd()
+ .args(&[
+ "--find-package",
+ "-DCOMPILER_ID=GNU",
+ "-DLANGUAGE=CXX",
+ "-DMODE=LINK",
+ ])
+ .arg(format!("-DNAME={}", self.package_name))
+ .output()
+ .map_err(Box::<dyn std::error::Error>::from)
+ .and_then(|output| if output.status.success() {
+ let stdout = String::from_utf8(output.stdout)?;
+ eprintln!("=== cmake link arguments: {:#?}", stdout);
+ Self::extract_from_cmdline(&stdout, &mut include_paths, &mut link_paths, &mut link_libs);
+ Ok(())
+ } else {
+ Err(format!(
+ "cmake returned an error\n stdout: {:?}\n stderr: {:?}",
+ String::from_utf8_lossy(&output.stdout),
+ String::from_utf8_lossy(&output.stderr)
+ ).into())
+ })?;
+
+ Ok(ProbeResult { version: None, include_paths, link_paths, link_libs })
}
}

Looks like find_package of cmake wasn't so useless after all (fixes #177)