diff --git a/build.rs b/build.rs index 07db940c..ff9e1680 100644 --- a/build.rs +++ b/build.rs @@ -22,7 +22,7 @@ mod docs; #[path = "build/generator.rs"] mod generator; #[path = "build/library.rs"] -mod library; +pub mod library; type Result> = std::result::Result; @@ -94,43 +94,6 @@ fn files_with_extension<'e>(dir: &Path, extension: impl AsRef + 'e) -> Re }) } -/// Returns Some(new_file_name) if some parts of the filename were removed, None otherwise -fn cleanup_lib_filename(filename: &OsStr) -> Option<&OsStr> { - let mut new_filename = filename; - // used to check for the file extension (with dots stripped) and for the part of the filename - const LIB_EXTS: [&str; 7] = [".so.", ".a.", ".dll.", ".lib.", ".dylib.", ".framework.", ".tbd."]; - let filename_path = Path::new(new_filename); - // strip lib extension from the filename - if let (Some(stem), Some(extension)) = (filename_path.file_stem(), filename_path.extension().and_then(OsStr::to_str)) { - if LIB_EXTS.iter().any(|e| e.trim_matches('.').eq_ignore_ascii_case(extension)) { - new_filename = stem; - } - } - if let Some(mut file) = new_filename.to_str() { - let orig_len = file.len(); - - // strip "lib" prefix from the filename unless targeting MSVC - if !*TARGET_ENV_MSVC { - file = file.strip_prefix("lib").unwrap_or(file); - } - - // strip lib extension + suffix (e.g. .so.4.6.0) from the filename - LIB_EXTS.iter().for_each(|&inner_ext| { - if let Some(inner_ext_idx) = file.find(inner_ext) { - file = &file[..inner_ext_idx]; - } - }); - if orig_len != file.len() { - new_filename = OsStr::new(file); - } - } - if new_filename.len() != filename.len() { - Some(new_filename) - } else { - None - } -} - fn get_module_header_dir(header_dir: &Path) -> Option { let mut out = header_dir.join("opencv2.framework/Headers"); if out.exists() { diff --git a/build/cmake_probe.rs b/build/cmake_probe.rs index 58bc9957..3d61f1ca 100644 --- a/build/cmake_probe.rs +++ b/build/cmake_probe.rs @@ -8,13 +8,99 @@ use std::process::{Command, Output}; use semver::Version; use shlex::Shlex; -use super::Result; +use super::library::Linkage; +use super::{Result, TARGET_ENV_MSVC}; + +#[derive(Debug, PartialEq, Eq)] +pub struct LinkLib(pub Linkage, pub String); + +impl LinkLib { + #[inline] + pub fn emit_cargo_rustc_link(&self) -> String { + format!( + "cargo:rustc-link-lib={}{}", + self.0.as_cargo_rustc_link_spec_no_static(), + self.1 + ) // replace with cargo:: syntax when MSRV is 1.77 + } + + /// Returns Some(new_file_name) if some parts of the filename were removed, None otherwise + pub fn cleanup_lib_filename(filename: &OsStr) -> Option<&OsStr> { + let mut new_filename = filename; + // used to check for the file extension (with dots stripped) and for the part of the filename + const LIB_EXTS: [&str; 6] = [".so.", ".a.", ".dll.", ".lib.", ".dylib.", ".tbd."]; + let filename_path = Path::new(new_filename); + // strip lib extension from the filename + if let (Some(stem), Some(extension)) = (filename_path.file_stem(), filename_path.extension().and_then(OsStr::to_str)) { + if LIB_EXTS.iter().any(|e| e.trim_matches('.').eq_ignore_ascii_case(extension)) { + new_filename = stem; + } + } + if let Some(mut file) = new_filename.to_str() { + let orig_len = file.len(); + + // strip "lib" prefix from the filename unless targeting MSVC + if !*TARGET_ENV_MSVC { + file = file.strip_prefix("lib").unwrap_or(file); + } + + // strip lib extension + suffix (e.g. .so.4.6.0) from the filename + LIB_EXTS.iter().for_each(|&inner_ext| { + if let Some(inner_ext_idx) = file.find(inner_ext) { + file = &file[..inner_ext_idx]; + } + }); + if orig_len != file.len() { + new_filename = OsStr::new(file); + } + } + if new_filename.len() != filename.len() { + Some(new_filename) + } else { + None + } + } +} + +impl From<&str> for LinkLib { + fn from(value: &str) -> Self { + let (linkage, value) = Linkage::from_prefixed_str(value); + let path = Path::new(value); + let value = path + .file_name() + .and_then(Self::cleanup_lib_filename) + .and_then(OsStr::to_str) + .unwrap_or(value); + Self(linkage, value.to_string()) + } +} + +#[derive(Debug, PartialEq, Eq)] +pub struct LinkSearch(pub Linkage, pub PathBuf); + +impl LinkSearch { + #[inline] + pub fn emit_cargo_rustc_link_search(&self) -> String { + format!( + "cargo:rustc-link-search={}{}", + self.0.as_cargo_rustc_link_search_spec(), + self.1.to_str().expect("Can't convert link search path to UTF-8 string") + ) // replace with cargo:: syntax when MSRV is 1.77 + } +} + +impl From<&str> for LinkSearch { + fn from(value: &str) -> Self { + let (linkage, value) = Linkage::from_prefixed_str(value); + Self(linkage, value.into()) + } +} pub struct ProbeResult { pub version: Option, pub include_paths: Vec, - pub link_paths: Vec, - pub link_libs: Vec, + pub link_paths: Vec, + pub link_libs: Vec, } pub struct CmakeProbe<'r> { @@ -117,12 +203,16 @@ impl<'r> CmakeProbe<'r> { pub(crate) fn extract_from_cmdline( cmdline: &str, + skip_cmd: bool, include_paths: &mut Vec, - link_paths: &mut Vec, - link_libs: &mut Vec, + link_paths: &mut Vec, + link_libs: &mut Vec, ) { eprintln!("=== Extracting build arguments from: {cmdline}"); let mut args = Shlex::new(cmdline.trim()); + if skip_cmd { + args.next(); + } while let Some(arg) = args.next() { let arg = arg.trim(); if let Some(path) = arg.strip_prefix("-I") { @@ -131,14 +221,14 @@ impl<'r> CmakeProbe<'r> { 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()); + let path = LinkSearch(Linkage::Default, PathBuf::from(path.trim_start())); if !link_paths.contains(&path) { link_paths.push(path); } } else if let Some(lib) = arg.strip_prefix("-l") { // unresolved cmake dependency specification like Qt5::Core - if !lib.contains("::") { - link_libs.push(lib.trim_start().to_string()); + if !lib.contains("::") && lib != "gflags_shared" { + link_libs.push(LinkLib(Linkage::Default, lib.trim_start().to_string())); } } else if let Some(framework) = arg.strip_prefix("-framework") { let framework = framework.trim_start(); @@ -147,27 +237,27 @@ impl<'r> CmakeProbe<'r> { } else { framework.to_string() }; - let framework_path = Path::new(&framework); - let has_extension = framework_path - .extension() - .and_then(OsStr::to_str) - .map_or(false, |ext| ext.eq_ignore_ascii_case("framework")); - if has_extension { - link_libs.push(framework); - } else { - link_libs.push(format!("{}.framework", framework)); + link_libs.push(LinkLib(Linkage::Framework, framework)); + } else if let Some(output_file) = arg.strip_prefix("-o") { + if output_file.trim().is_empty() { + args.next().expect("No output file after -o"); } } else if !arg.starts_with('-') { let path = Path::new(arg); - if let Some(file) = path.file_name().and_then(super::cleanup_lib_filename) { + if let Some(cleaned_lib_filename) = path.file_name().and_then(LinkLib::cleanup_lib_filename) { + let linkage = Linkage::from_path(path); if let Some(parent) = path.parent().map(|p| p.to_owned()) { - if !link_paths.contains(&parent) { - link_paths.push(parent); + let search_path = LinkSearch(linkage, parent); + if !link_paths.contains(&search_path) { + link_paths.push(search_path); } } else { panic!("{}", arg.to_string()); } - link_libs.push(file.to_str().expect("Non-UTF8 filename").to_string()); + link_libs.push(LinkLib( + linkage, + cleaned_lib_filename.to_str().expect("Non-UTF8 filename").to_string(), + )); } } else { eprintln!("=== Unexpected cmake compiler argument found: {arg}"); @@ -175,17 +265,17 @@ impl<'r> CmakeProbe<'r> { } } - fn extract_from_makefile(&self, link_paths: &mut Vec, link_libs: &mut Vec) -> Result<()> { + fn extract_from_makefile(&self, link_paths: &mut Vec, link_libs: &mut Vec) -> Result<()> { let link_cmdline = fs::read_to_string(self.build_dir.join("CMakeFiles/ocvrs_probe.dir/link.txt"))?; - Self::extract_from_cmdline(&link_cmdline, &mut vec![], link_paths, link_libs); + Self::extract_from_cmdline(&link_cmdline, true, &mut vec![], link_paths, link_libs); Ok(()) } fn extract_from_ninja( &self, include_paths: &mut Vec, - link_paths: &mut Vec, - link_libs: &mut Vec, + link_paths: &mut Vec, + link_libs: &mut Vec, ) -> Result<()> { let mut link_cmdline = BufReader::new(File::open(self.build_dir.join("build.ninja"))?); let mut line = String::with_capacity(2048); @@ -208,9 +298,9 @@ impl<'r> CmakeProbe<'r> { State::Reading => { let trimmed_line = line.trim_start(); if let Some(paths) = trimmed_line.strip_prefix("LINK_PATH = ") { - Self::extract_from_cmdline(paths, include_paths, link_paths, link_libs); + Self::extract_from_cmdline(paths, false, include_paths, link_paths, link_libs); } else if let Some(libs) = trimmed_line.strip_prefix("LINK_LIBRARIES = ") { - Self::extract_from_cmdline(libs, include_paths, link_paths, link_libs); + Self::extract_from_cmdline(libs, false, include_paths, link_paths, link_libs); } } } @@ -292,7 +382,7 @@ impl<'r> CmakeProbe<'r> { 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); + Self::extract_from_cmdline(&stdout, false, &mut include_paths, &mut link_paths, &mut link_libs); Ok(()) } else { Err( @@ -314,7 +404,7 @@ impl<'r> CmakeProbe<'r> { 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); + Self::extract_from_cmdline(&stdout, false, &mut include_paths, &mut link_paths, &mut link_libs); Ok(()) } else { Err( diff --git a/build/library.rs b/build/library.rs index 56d43633..56c23c44 100644 --- a/build/library.rs +++ b/build/library.rs @@ -7,8 +7,8 @@ use std::{env, fmt, iter}; use dunce::canonicalize; use semver::Version; -use super::cmake_probe::CmakeProbe; -use super::{cleanup_lib_filename, get_version_from_headers, Result, MANIFEST_DIR, OUT_DIR, TARGET_VENDOR_APPLE}; +use super::cmake_probe::{CmakeProbe, LinkLib, LinkSearch}; +use super::{get_version_from_headers, Result, MANIFEST_DIR, OUT_DIR, TARGET_VENDOR_APPLE}; struct PackageName; @@ -86,6 +86,65 @@ impl fmt::Display for EnvList<'_> { } } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Linkage { + Default, + Dynamic, + Static, + Framework, +} + +impl Linkage { + pub fn as_cargo_rustc_link_spec(self) -> &'static str { + match self { + Self::Default => "", + Self::Dynamic => "dylib=", + Self::Static => "static=", + Self::Framework => "framework=", + } + } + + pub fn as_cargo_rustc_link_spec_no_static(self) -> &'static str { + // fixme: specifying static linkage breaks things in CI + match self { + Self::Default | Self::Dynamic | Self::Static => "", + Self::Framework => "framework=", + } + } + + pub fn as_cargo_rustc_link_search_spec(self) -> &'static str { + match self { + Self::Default => "", + Self::Dynamic | Self::Static => "native=", + Self::Framework => "framework=", + } + } + + pub fn from_path(path: &Path) -> Self { + let ext = path.extension(); + if Self::is_static_archive(ext) { + Self::Static + } else { + Self::Default + } + } + + pub fn from_prefixed_str(s: &str) -> (Self, &str) { + // for backwards compatibility to allow specifying as "OpenCL.framework" in addition to "framework=OpenCL" + if let Some(name) = s.strip_suffix(".framework") { + return (Self::Framework, name); + } + [Self::Dynamic, Self::Static, Self::Framework] + .iter() + .find_map(|l| s.strip_prefix(l.as_cargo_rustc_link_spec()).map(|s| (*l, s))) + .unwrap_or((Self::Default, s)) + } + + fn is_static_archive(ext: Option<&OsStr>) -> bool { + ext.map_or(false, |ext| ext.eq_ignore_ascii_case("a")) + } +} + #[derive(Debug)] pub struct Library { pub include_paths: Vec, @@ -94,60 +153,10 @@ pub struct Library { } impl Library { - fn process_library_list(libs: impl IntoIterator>) -> impl Iterator { - libs.into_iter().filter_map(|x| { - let path = x.as_ref(); - let is_framework = path - .extension() - .and_then(OsStr::to_str) - .map_or(false, |e| e.eq_ignore_ascii_case("framework")); - if let Some(filename) = path.file_name() { - let filename = cleanup_lib_filename(filename).unwrap_or(filename); - filename.to_str().map(|f| { - if is_framework { - format!("framework={f}") - } else { - f.to_owned() - } - }) - } else { - None - } - }) - } - fn version_from_include_paths(include_paths: impl IntoIterator>) -> Option { include_paths.into_iter().find_map(|x| get_version_from_headers(x.as_ref())) } - #[inline] - fn emit_link_search(path: &Path, typ: Option<&str>) -> String { - format!( - "cargo:rustc-link-search={}{}", - typ.map_or_else(|| "".to_string(), |t| format!("{t}=")), - path.to_str().expect("Can't convert link search path to UTF-8 string") - ) - } - - #[inline] - fn emit_link_lib(lib: &str, typ: Option<&str>) -> String { - format!( - "cargo:rustc-link-lib={}{}", - typ.map_or_else( - || "".to_string(), - |t| { - let prefix = format!("{t}="); - if lib.starts_with(&prefix) { - "".to_string() - } else { - prefix - } - } - ), - lib - ) - } - fn process_env_var_list<'a, T: From<&'a str>>(env_list: Option>, sys_list: Vec) -> Vec { if let Some(env_list) = env_list { let mut paths = if env_list.is_extend() { @@ -162,25 +171,21 @@ impl Library { } } - fn process_link_paths<'a>( - link_paths: Option, - sys_link_paths: Vec, - typ: Option<&'a str>, - ) -> impl Iterator + 'a { + fn process_link_paths<'a>(link_paths: Option, sys_link_paths: Vec) -> impl Iterator + 'a { Self::process_env_var_list(link_paths, sys_link_paths) .into_iter() .flat_map(move |path| { - iter::once(Self::emit_link_search(&path, typ)) - .chain(TARGET_VENDOR_APPLE.then(|| Self::emit_link_search(&path, Some("framework")))) + iter::once(path.emit_cargo_rustc_link_search()).chain( + (*TARGET_VENDOR_APPLE && path.0 != Linkage::Framework) + .then(|| LinkSearch(Linkage::Framework, path.1).emit_cargo_rustc_link_search()), + ) }) } - fn process_link_libs<'a>( - link_libs: Option, - sys_link_libs: Vec, - typ: Option<&'a str>, - ) -> impl Iterator + 'a { - Self::process_library_list(Self::process_env_var_list(link_libs, sys_link_libs)).map(move |l| Self::emit_link_lib(&l, typ)) + fn process_link_libs<'a>(link_libs: Option, sys_link_libs: Vec) -> impl Iterator + 'a { + Self::process_env_var_list(link_libs, sys_link_libs) + .into_iter() + .map(|l| l.emit_cargo_rustc_link()) } fn find_vcpkg_tool(vcpkg_root: &Path, tool_name: &str) -> Option { @@ -228,8 +233,8 @@ impl Library { let version = Self::version_from_include_paths(&include_paths).ok_or("Could not OpenCV version from include_paths")?; - cargo_metadata.extend(Self::process_link_paths(Some(link_paths), vec![], None)); - cargo_metadata.extend(Self::process_link_libs(Some(link_libs), vec![], None)); + cargo_metadata.extend(Self::process_link_paths(Some(link_paths), vec![])); + cargo_metadata.extend(Self::process_link_libs(Some(link_libs), vec![])); Ok(Self { include_paths, @@ -266,14 +271,38 @@ impl Library { let opencv = opencv.ok_or_else(|| errors.join(", "))?; let mut cargo_metadata = Vec::with_capacity(64); - cargo_metadata.extend(Self::process_link_paths(link_paths, opencv.link_paths, None)); + cargo_metadata.extend(Self::process_link_paths( + link_paths, + opencv + .link_paths + .into_iter() + .map(|p| LinkSearch(Linkage::Default, p)) + .collect(), + )); if link_paths.map_or(true, |link_paths| link_paths.is_extend()) { - cargo_metadata.extend(Self::process_link_paths(None, opencv.framework_paths, Some("framework"))); + cargo_metadata.extend(Self::process_link_paths( + None, + opencv + .framework_paths + .into_iter() + .map(|p| LinkSearch(Linkage::Framework, p)) + .collect(), + )); } - cargo_metadata.extend(Self::process_link_libs(link_libs, opencv.libs, None)); + cargo_metadata.extend(Self::process_link_libs( + link_libs, + opencv.libs.into_iter().map(|l| LinkLib(Linkage::Default, l)).collect(), + )); if link_libs.map_or(false, |link_libs| link_libs.is_extend()) { - cargo_metadata.extend(Self::process_link_libs(None, opencv.frameworks, Some("framework"))); + cargo_metadata.extend(Self::process_link_libs( + None, + opencv + .frameworks + .into_iter() + .map(|f| LinkLib(Linkage::Framework, f)) + .collect(), + )); } let include_paths = Self::process_env_var_list(include_paths, opencv.include_paths); @@ -326,8 +355,8 @@ impl Library { } 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)); + cargo_metadata.extend(Self::process_link_paths(link_paths, probe_result.link_paths)); + cargo_metadata.extend(Self::process_link_libs(link_libs, probe_result.link_libs)); Ok(Self { include_paths: Self::process_env_var_list(include_paths, probe_result.include_paths), @@ -367,12 +396,12 @@ impl Library { if link_paths.as_ref().map_or(false, |lp| !lp.is_extend()) { cargo_metadata.retain(|p| !p.starts_with("cargo:rustc-link-search=") && !p.starts_with("cargo::rustc-link-search=")); } - cargo_metadata.extend(Self::process_link_paths(link_paths, vec![], None)); + cargo_metadata.extend(Self::process_link_paths(link_paths, vec![])); if link_libs.as_ref().map_or(false, |ll| !ll.is_extend()) { cargo_metadata.retain(|p| !p.starts_with("cargo:rustc-link-lib=") && !p.starts_with("cargo::rustc-link-lib=")); } - cargo_metadata.extend(Self::process_link_libs(link_libs, vec![], None)); + cargo_metadata.extend(Self::process_link_libs(link_libs, vec![])); Ok(Self { include_paths, diff --git a/tests/build.rs b/tests/build.rs index 4cce0922..db347e56 100644 --- a/tests/build.rs +++ b/tests/build.rs @@ -1,6 +1,7 @@ use std::path::PathBuf; -use build::cmake_probe::CmakeProbe; +use build::cmake_probe::{CmakeProbe, LinkLib, LinkSearch}; +use build::library::Linkage; #[allow(dead_code)] #[path = "../build.rs"] @@ -8,56 +9,111 @@ mod build; #[test] fn test_extract_from_cmdline() { - let cmdline = "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/c++ -g -arch arm64 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX13.3.sdk -Wl,-search_paths_first -Wl,-headerpad_max_install_names CMakeFiles/ocvrs_probe.dir/ocvrs_probe.cpp.o -o ocvrs_probe /vcpkg/installed/arm64-osx/debug/lib/libopencv_calib3d4d.a /vcpkg/installed/arm64-osx/debug/lib/libopencv_core4d.a /vcpkg/installed/arm64-osx/debug/lib/libopencv_features2d4d.a /vcpkg/installed/arm64-osx/debug/lib/libopencv_flann4d.a /vcpkg/installed/arm64-osx/debug/lib/libopencv_highgui4d.a /vcpkg/installed/arm64-osx/debug/lib/libopencv_imgcodecs4d.a /vcpkg/installed/arm64-osx/debug/lib/libopencv_imgproc4d.a /vcpkg/installed/arm64-osx/debug/lib/libopencv_ml4d.a /vcpkg/installed/arm64-osx/debug/lib/libopencv_objdetect4d.a /vcpkg/installed/arm64-osx/debug/lib/libopencv_photo4d.a /vcpkg/installed/arm64-osx/debug/lib/libopencv_stitching4d.a /vcpkg/installed/arm64-osx/debug/lib/libopencv_video4d.a /vcpkg/installed/arm64-osx/debug/lib/libopencv_videoio4d.a /vcpkg/installed/arm64-osx/debug/lib/libopencv_imgcodecs4d.a /vcpkg/installed/arm64-osx/debug/lib/libjpeg.a /vcpkg/installed/arm64-osx/debug/lib/libtiffd.a /vcpkg/installed/arm64-osx/debug/lib/liblzma.a /vcpkg/installed/arm64-osx/debug/lib/libjpeg.a /vcpkg/installed/arm64-osx/debug/lib/libtiffd.a /vcpkg/installed/arm64-osx/debug/lib/liblzma.a -lm -framework AppKit -framework Accelerate -framework AVFoundation -framework CoreGraphics -framework CoreMedia -framework CoreVideo -framework QuartzCore -framework Cocoa /vcpkg/installed/arm64-osx/debug/lib/libopencv_calib3d4d.a /vcpkg/installed/arm64-osx/debug/lib/libopencv_features2d4d.a /vcpkg/installed/arm64-osx/debug/lib/libopencv_flann4d.a /vcpkg/installed/arm64-osx/debug/lib/libopencv_imgproc4d.a /vcpkg/installed/arm64-osx/debug/lib/libopencv_core4d.a /vcpkg/installed/arm64-osx/debug/lib/libz.a -framework OpenCL /vcpkg/installed/arm64-osx/debug/lib/manual-link/opencv4_thirdparty/libtegra_hald.a"; - let mut include_paths = Vec::new(); - let mut link_paths = Vec::new(); - let mut link_libs = Vec::new(); - CmakeProbe::extract_from_cmdline(cmdline, &mut include_paths, &mut link_paths, &mut link_libs); - let expect_include_paths: Vec = vec![]; + { + let cmdline = "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/c++ -g -arch arm64 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX13.3.sdk -Wl,-search_paths_first -Wl,-headerpad_max_install_names CMakeFiles/ocvrs_probe.dir/ocvrs_probe.cpp.o -o ocvrs_probe /vcpkg/installed/arm64-osx/debug/lib/libopencv_calib3d4d.a /vcpkg/installed/arm64-osx/debug/lib/libopencv_core4d.a /vcpkg/installed/arm64-osx/debug/lib/libjpeg.a /vcpkg/installed/arm64-osx/debug/lib/libtiffd.a /vcpkg/installed/arm64-osx/debug/lib/liblzma.a /vcpkg/installed/arm64-osx/debug/lib/libjpeg.a /vcpkg/installed/arm64-osx/debug/lib/libtiffd.a -lm -framework AppKit -framework Accelerate -framework AVFoundation -framework CoreGraphics /vcpkg/installed/arm64-osx/debug/lib/libopencv_calib3d4d.a /vcpkg/installed/arm64-osx/debug/lib/libopencv_features2d4d.a /vcpkg/installed/arm64-osx/debug/lib/libz.a -framework OpenCL /vcpkg/installed/arm64-osx/debug/lib/manual-link/opencv4_thirdparty/libtegra_hald.a"; + let expect_include_paths: Vec = vec![]; + let expect_link_paths = vec![ + LinkSearch(Linkage::Static, PathBuf::from("/vcpkg/installed/arm64-osx/debug/lib")), + LinkSearch( + Linkage::Static, + PathBuf::from("/vcpkg/installed/arm64-osx/debug/lib/manual-link/opencv4_thirdparty"), + ), + ]; + let expect_link_libs = vec![ + LinkLib(Linkage::Static, "opencv_calib3d4d".to_string()), + LinkLib(Linkage::Static, "opencv_core4d".to_string()), + LinkLib(Linkage::Static, "jpeg".to_string()), + LinkLib(Linkage::Static, "tiffd".to_string()), + LinkLib(Linkage::Static, "lzma".to_string()), + LinkLib(Linkage::Static, "jpeg".to_string()), + LinkLib(Linkage::Static, "tiffd".to_string()), + LinkLib(Linkage::Default, "m".to_string()), + LinkLib(Linkage::Framework, "AppKit".to_string()), + LinkLib(Linkage::Framework, "Accelerate".to_string()), + LinkLib(Linkage::Framework, "AVFoundation".to_string()), + LinkLib(Linkage::Framework, "CoreGraphics".to_string()), + LinkLib(Linkage::Static, "opencv_calib3d4d".to_string()), + LinkLib(Linkage::Static, "opencv_features2d4d".to_string()), + LinkLib(Linkage::Static, "z".to_string()), + LinkLib(Linkage::Framework, "OpenCL".to_string()), + LinkLib(Linkage::Static, "tegra_hald".to_string()), + ]; + assert_extract_from_cmdline(cmdline, true, expect_include_paths, expect_link_paths, expect_link_libs); + } + + { + let cmdline = "/usr/bin/c++ -g CMakeFiles/ocvrs_probe.dir/ocvrs_probe.cpp.o -o ocvrs_probe /usr/lib/x86_64-linux-gnu/libopencv_stitching.so.3.4.20 /usr/lib/x86_64-linux-gnu/libopencv_superres.so.3.4.20 /usr/lib/x86_64-linux-gnu/libopencv_videostab.so.3.4.20"; + let expect_include_paths: Vec = vec![]; + let expect_link_paths = vec![LinkSearch(Linkage::Default, PathBuf::from("/usr/lib/x86_64-linux-gnu"))]; + let expect_link_libs = vec![ + LinkLib(Linkage::Default, "opencv_stitching".to_string()), + LinkLib(Linkage::Default, "opencv_superres".to_string()), + LinkLib(Linkage::Default, "opencv_videostab".to_string()), + ]; + assert_extract_from_cmdline(cmdline, true, expect_include_paths, expect_link_paths, expect_link_libs); + } + + { + let cmdline = "/home/pro/projects/opencv-lib/opencv-4/install-static/lib64/libopencv_calib3d.a /home/pro/projects/opencv-lib/opencv-4/install-static/lib64/libopencv_core.a /home/pro/projects/opencv-lib/opencv-4/install-static/lib64/libopencv_dnn.a /home/pro/projects/opencv-lib/opencv-4/install-static/lib64/libopencv_features2d.a /home/pro/projects/opencv-lib/opencv-4/install-static/lib64/libopencv_flann.a /home/pro/projects/opencv-lib/opencv-4/install-static/lib64/opencv4/3rdparty/libade.a /usr/lib64/libfreetype.so /usr/lib64/libharfbuzz.so -lm -lOgreBites -lOgreMeshLodGenerator /home/pro/projects/opencv-lib/opencv-4/install-static/lib64/opencv4/3rdparty/libopencv.sfm.correspondence.a /home/pro/projects/opencv-lib/opencv-4/install-static/lib64/opencv4/3rdparty/libopencv.sfm.multiview.a -lglog::glog -lgflags_shared /home/pro/projects/opencv-lib/opencv-4/install-static/lib64/libopencv_phase_unwrapping.a -lVTK::FiltersExtraction -lVTK::FiltersSources /home/pro/projects/opencv-lib/opencv-4/install-static/lib64/libopencv_videoio.a -lIconv::Iconv /home/pro/projects/opencv-lib/opencv-4/install-static/lib64/opencv4/3rdparty/liblibpng.a /home/pro/projects/opencv-lib/opencv-4/install-static/lib64/opencv4/3rdparty/liblibtiff.a /home/pro/projects/opencv-lib/opencv-4/install-static/lib64/opencv4/3rdparty/liblibopenjp2.a -lva -lva-drm /usr/lib64/libOpenGL.so /home/pro/projects/opencv-lib/opencv-4/install-static/lib64/opencv4/3rdparty/libittnotify.a -ldl -lm -lpthread -lrt /home/pro/projects/opencv-lib/opencv-4/install-static/lib64/opencv4/3rdparty/libippiw.a /home/pro/projects/opencv-lib/opencv-4/install-static/lib64/opencv4/3rdparty/libippicv.a -lEigen3::Eigen "; + let expect_include_paths: Vec = vec![]; + let expect_link_paths = vec![ + LinkSearch( + Linkage::Static, + PathBuf::from("/home/pro/projects/opencv-lib/opencv-4/install-static/lib64"), + ), + LinkSearch( + Linkage::Static, + PathBuf::from("/home/pro/projects/opencv-lib/opencv-4/install-static/lib64/opencv4/3rdparty"), + ), + LinkSearch(Linkage::Default, PathBuf::from("/usr/lib64")), + ]; + let expect_link_libs = vec![ + LinkLib(Linkage::Static, "opencv_calib3d".to_string()), + LinkLib(Linkage::Static, "opencv_core".to_string()), + LinkLib(Linkage::Static, "opencv_dnn".to_string()), + LinkLib(Linkage::Static, "opencv_features2d".to_string()), + LinkLib(Linkage::Static, "opencv_flann".to_string()), + LinkLib(Linkage::Static, "ade".to_string()), + LinkLib(Linkage::Default, "freetype".to_string()), + LinkLib(Linkage::Default, "harfbuzz".to_string()), + LinkLib(Linkage::Default, "m".to_string()), + LinkLib(Linkage::Default, "OgreBites".to_string()), + LinkLib(Linkage::Default, "OgreMeshLodGenerator".to_string()), + LinkLib(Linkage::Static, "opencv.sfm.correspondence".to_string()), + LinkLib(Linkage::Static, "opencv.sfm.multiview".to_string()), + LinkLib(Linkage::Static, "opencv_phase_unwrapping".to_string()), + LinkLib(Linkage::Static, "opencv_videoio".to_string()), + LinkLib(Linkage::Static, "libpng".to_string()), + LinkLib(Linkage::Static, "libtiff".to_string()), + LinkLib(Linkage::Static, "libopenjp2".to_string()), + LinkLib(Linkage::Default, "va".to_string()), + LinkLib(Linkage::Default, "va-drm".to_string()), + LinkLib(Linkage::Default, "OpenGL".to_string()), + LinkLib(Linkage::Static, "ittnotify".to_string()), + LinkLib(Linkage::Default, "dl".to_string()), + LinkLib(Linkage::Default, "m".to_string()), + LinkLib(Linkage::Default, "pthread".to_string()), + LinkLib(Linkage::Default, "rt".to_string()), + LinkLib(Linkage::Static, "ippiw".to_string()), + LinkLib(Linkage::Static, "ippicv".to_string()), + ]; + assert_extract_from_cmdline(cmdline, false, expect_include_paths, expect_link_paths, expect_link_libs); + } +} + +#[track_caller] +fn assert_extract_from_cmdline( + cmdline: &str, + skip_cmd: bool, + expect_include_paths: Vec, + expect_link_paths: Vec, + expect_link_libs: Vec, +) { + let mut include_paths = vec![]; + let mut link_paths = vec![]; + let mut link_libs = vec![]; + CmakeProbe::extract_from_cmdline(cmdline, skip_cmd, &mut include_paths, &mut link_paths, &mut link_libs); assert_eq!(expect_include_paths, include_paths); - let expect_link_paths = vec![ - PathBuf::from("/vcpkg/installed/arm64-osx/debug/lib"), - PathBuf::from("/vcpkg/installed/arm64-osx/debug/lib/manual-link/opencv4_thirdparty"), - ]; assert_eq!(expect_link_paths, link_paths); - let expect_link_libs = vec![ - "opencv_calib3d4d", - "opencv_core4d", - "opencv_features2d4d", - "opencv_flann4d", - "opencv_highgui4d", - "opencv_imgcodecs4d", - "opencv_imgproc4d", - "opencv_ml4d", - "opencv_objdetect4d", - "opencv_photo4d", - "opencv_stitching4d", - "opencv_video4d", - "opencv_videoio4d", - "opencv_imgcodecs4d", - "jpeg", - "tiffd", - "lzma", - "jpeg", - "tiffd", - "lzma", - "m", - "AppKit.framework", - "Accelerate.framework", - "AVFoundation.framework", - "CoreGraphics.framework", - "CoreMedia.framework", - "CoreVideo.framework", - "QuartzCore.framework", - "Cocoa.framework", - "opencv_calib3d4d", - "opencv_features2d4d", - "opencv_flann4d", - "opencv_imgproc4d", - "opencv_core4d", - "z", - "OpenCL.framework", - "tegra_hald", - ]; assert_eq!(expect_link_libs, link_libs); }