Skip to content

Commit 23f3aee

Browse files
committed
Indicate static linker flag for static libraries
1 parent 3278d78 commit 23f3aee

File tree

6 files changed

+329
-204
lines changed

6 files changed

+329
-204
lines changed

build.rs

Lines changed: 8 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ mod docs;
2323
#[path = "build/generator.rs"]
2424
mod generator;
2525
#[path = "build/library.rs"]
26-
mod library;
26+
pub mod library;
2727

2828
type Result<T, E = Box<dyn std::error::Error>> = std::result::Result<T, E>;
2929

@@ -94,43 +94,6 @@ fn files_with_extension<'e>(dir: &Path, extension: impl AsRef<OsStr> + 'e) -> Re
9494
})
9595
}
9696

97-
/// Returns Some(new_file_name) if some parts of the filename were removed, None otherwise
98-
fn cleanup_lib_filename(filename: &OsStr) -> Option<&OsStr> {
99-
let mut new_filename = filename;
100-
// used to check for the file extension (with dots stripped) and for the part of the filename
101-
const LIB_EXTS: [&str; 7] = [".so.", ".a.", ".dll.", ".lib.", ".dylib.", ".framework.", ".tbd."];
102-
let filename_path = Path::new(new_filename);
103-
// strip lib extension from the filename
104-
if let (Some(stem), Some(extension)) = (filename_path.file_stem(), filename_path.extension().and_then(OsStr::to_str)) {
105-
if LIB_EXTS.iter().any(|e| e.trim_matches('.').eq_ignore_ascii_case(extension)) {
106-
new_filename = stem;
107-
}
108-
}
109-
if let Some(mut file) = new_filename.to_str() {
110-
let orig_len = file.len();
111-
112-
// strip "lib" prefix from the filename unless targeting MSVC
113-
if !*TARGET_ENV_MSVC {
114-
file = file.strip_prefix("lib").unwrap_or(file);
115-
}
116-
117-
// strip lib extension + suffix (e.g. .so.4.6.0) from the filename
118-
LIB_EXTS.iter().for_each(|&inner_ext| {
119-
if let Some(inner_ext_idx) = file.find(inner_ext) {
120-
file = &file[..inner_ext_idx];
121-
}
122-
});
123-
if orig_len != file.len() {
124-
new_filename = OsStr::new(file);
125-
}
126-
}
127-
if new_filename.len() != filename.len() {
128-
Some(new_filename)
129-
} else {
130-
None
131-
}
132-
}
133-
13497
fn get_module_header_dir(header_dir: &Path) -> Option<PathBuf> {
13598
let mut out = header_dir.join("opencv2.framework/Headers");
13699
if out.exists() {
@@ -269,18 +232,18 @@ fn build_compiler(opencv: &Library) -> cc::Build {
269232

270233
fn setup_rerun() -> Result<()> {
271234
for &v in AFFECTING_ENV_VARS.iter() {
272-
println!("cargo:rerun-if-env-changed={v}");
235+
println!("cargo:rerun-if-env-changed={v}"); // replace with cargo:: syntax when MSRV is 1.77
273236
}
274237

275238
let include_exts = &[OsStr::new("cpp"), OsStr::new("hpp")];
276239
let files_with_include_exts =
277240
files_with_predicate(&SRC_CPP_DIR, |p| p.extension().map_or(false, |e| include_exts.contains(&e)))?;
278241
for path in files_with_include_exts {
279242
if let Some(path) = path.to_str() {
280-
println!("cargo:rerun-if-changed={path}");
243+
println!("cargo:rerun-if-changed={path}"); // replace with cargo:: syntax when MSRV is 1.77
281244
}
282245
}
283-
println!("cargo:rerun-if-changed=Cargo.toml");
246+
println!("cargo:rerun-if-changed=Cargo.toml"); // replace with cargo:: syntax when MSRV is 1.77
284247
Ok(())
285248
}
286249

@@ -289,7 +252,7 @@ fn build_wrapper(opencv: &Library) {
289252
eprintln!("=== Compiler information: {:#?}", cc.get_compiler());
290253
let modules = MODULES.get().expect("MODULES not initialized");
291254
for module in modules.iter() {
292-
println!("cargo:rustc-cfg=ocvrs_has_module_{module}");
255+
println!("cargo:rustc-cfg=ocvrs_has_module_{module}"); // replace with cargo:: syntax when MSRV is 1.77
293256
cc.file(OUT_DIR.join(format!("{module}.cpp")));
294257
let manual_cpp = SRC_CPP_DIR.join(format!("manual-{module}.cpp"));
295258
if manual_cpp.exists() {
@@ -337,11 +300,11 @@ fn main() -> Result<()> {
337300
let opencv = Library::probe()?;
338301
eprintln!("=== OpenCV library configuration: {opencv:#?}");
339302
if OPENCV_BRANCH_4.matches(&opencv.version) {
340-
println!("cargo:rustc-cfg=ocvrs_opencv_branch_4");
303+
println!("cargo:rustc-cfg=ocvrs_opencv_branch_4"); // replace with cargo:: syntax when MSRV is 1.77
341304
} else if OPENCV_BRANCH_34.matches(&opencv.version) {
342-
println!("cargo:rustc-cfg=ocvrs_opencv_branch_34");
305+
println!("cargo:rustc-cfg=ocvrs_opencv_branch_34"); // replace with cargo:: syntax when MSRV is 1.77
343306
} else if OPENCV_BRANCH_32.matches(&opencv.version) {
344-
println!("cargo:rustc-cfg=ocvrs_opencv_branch_32");
307+
println!("cargo:rustc-cfg=ocvrs_opencv_branch_32"); // replace with cargo:: syntax when MSRV is 1.77
345308
} else {
346309
panic!(
347310
"Unsupported OpenCV version: {}, must be from 3.2, 3.4 or 4.x branch",

build/cmake_probe.rs

Lines changed: 112 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,95 @@ use std::process::{Command, Output};
88
use semver::Version;
99
use shlex::Shlex;
1010

11-
use super::Result;
11+
use super::library::Linkage;
12+
use super::{Result, TARGET_ENV_MSVC};
13+
14+
#[derive(Debug, PartialEq, Eq)]
15+
pub struct LinkLib(pub Linkage, pub String);
16+
17+
impl LinkLib {
18+
#[inline]
19+
pub fn emit_cargo_rustc_link(&self) -> String {
20+
format!("cargo:rustc-link-lib={}{}", self.0.as_cargo_rustc_link_spec(), self.1) // replace with cargo:: syntax when MSRV is 1.77
21+
}
22+
23+
/// Returns Some(new_file_name) if some parts of the filename were removed, None otherwise
24+
pub fn cleanup_lib_filename(filename: &OsStr) -> Option<&OsStr> {
25+
let mut new_filename = filename;
26+
// used to check for the file extension (with dots stripped) and for the part of the filename
27+
const LIB_EXTS: [&str; 7] = [".so.", ".a.", ".dll.", ".lib.", ".dylib.", ".framework.", ".tbd."];
28+
let filename_path = Path::new(new_filename);
29+
// strip lib extension from the filename
30+
if let (Some(stem), Some(extension)) = (filename_path.file_stem(), filename_path.extension().and_then(OsStr::to_str)) {
31+
if LIB_EXTS.iter().any(|e| e.trim_matches('.').eq_ignore_ascii_case(extension)) {
32+
new_filename = stem;
33+
}
34+
}
35+
if let Some(mut file) = new_filename.to_str() {
36+
let orig_len = file.len();
37+
38+
// strip "lib" prefix from the filename unless targeting MSVC
39+
if !*TARGET_ENV_MSVC {
40+
file = file.strip_prefix("lib").unwrap_or(file);
41+
}
42+
43+
// strip lib extension + suffix (e.g. .so.4.6.0) from the filename
44+
LIB_EXTS.iter().for_each(|&inner_ext| {
45+
if let Some(inner_ext_idx) = file.find(inner_ext) {
46+
file = &file[..inner_ext_idx];
47+
}
48+
});
49+
if orig_len != file.len() {
50+
new_filename = OsStr::new(file);
51+
}
52+
}
53+
if new_filename.len() != filename.len() {
54+
Some(new_filename)
55+
} else {
56+
None
57+
}
58+
}
59+
}
60+
61+
impl From<&str> for LinkLib {
62+
fn from(value: &str) -> Self {
63+
let (linkage, value) = Linkage::from_prefixed_str(value);
64+
let path = Path::new(value);
65+
let value = path
66+
.file_name()
67+
.and_then(Self::cleanup_lib_filename)
68+
.and_then(OsStr::to_str)
69+
.unwrap_or(value);
70+
Self(linkage, value.to_string())
71+
}
72+
}
73+
74+
#[derive(Debug, PartialEq, Eq)]
75+
pub struct LinkSearch(pub Linkage, pub PathBuf);
76+
77+
impl LinkSearch {
78+
#[inline]
79+
pub fn emit_cargo_rustc_link_search(&self) -> String {
80+
format!(
81+
"cargo:rustc-link-search={}{}",
82+
self.0.as_cargo_rustc_link_search_spec(),
83+
self.1.to_str().expect("Can't convert link search path to UTF-8 string")
84+
) // replace with cargo:: syntax when MSRV is 1.77
85+
}
86+
}
87+
88+
impl From<&str> for LinkSearch {
89+
fn from(value: &str) -> Self {
90+
let (linkage, value) = Linkage::from_prefixed_str(value);
91+
Self(linkage, value.into())
92+
}
93+
}
1294

1395
pub struct ProbeResult {
1496
pub version: Option<Version>,
1597
pub include_paths: Vec<PathBuf>,
16-
pub link_paths: Vec<PathBuf>,
17-
pub link_libs: Vec<String>,
98+
pub link_paths: Vec<LinkSearch>,
99+
pub link_libs: Vec<LinkLib>,
18100
}
19101

20102
pub struct CmakeProbe<'r> {
@@ -117,12 +199,16 @@ impl<'r> CmakeProbe<'r> {
117199

118200
pub(crate) fn extract_from_cmdline(
119201
cmdline: &str,
202+
skip_cmd: bool,
120203
include_paths: &mut Vec<PathBuf>,
121-
link_paths: &mut Vec<PathBuf>,
122-
link_libs: &mut Vec<String>,
204+
link_paths: &mut Vec<LinkSearch>,
205+
link_libs: &mut Vec<LinkLib>,
123206
) {
124207
eprintln!("=== Extracting build arguments from: {cmdline}");
125208
let mut args = Shlex::new(cmdline.trim());
209+
if skip_cmd {
210+
args.next();
211+
}
126212
while let Some(arg) = args.next() {
127213
let arg = arg.trim();
128214
if let Some(path) = arg.strip_prefix("-I") {
@@ -131,14 +217,14 @@ impl<'r> CmakeProbe<'r> {
131217
include_paths.push(path);
132218
}
133219
} else if let Some(path) = arg.strip_prefix("-L").or_else(|| arg.strip_prefix("-Wl,-rpath,")) {
134-
let path = PathBuf::from(path.trim_start());
220+
let path = LinkSearch(Linkage::Default, PathBuf::from(path.trim_start()));
135221
if !link_paths.contains(&path) {
136222
link_paths.push(path);
137223
}
138224
} else if let Some(lib) = arg.strip_prefix("-l") {
139225
// unresolved cmake dependency specification like Qt5::Core
140-
if !lib.contains("::") {
141-
link_libs.push(lib.trim_start().to_string());
226+
if !lib.contains("::") && lib != "gflags_shared" {
227+
link_libs.push(LinkLib(Linkage::Default, lib.trim_start().to_string()));
142228
}
143229
} else if let Some(framework) = arg.strip_prefix("-framework") {
144230
let framework = framework.trim_start();
@@ -147,45 +233,41 @@ impl<'r> CmakeProbe<'r> {
147233
} else {
148234
framework.to_string()
149235
};
150-
let framework_path = Path::new(&framework);
151-
let has_extension = framework_path
152-
.extension()
153-
.and_then(OsStr::to_str)
154-
.map_or(false, |ext| ext.eq_ignore_ascii_case("framework"));
155-
if has_extension {
156-
link_libs.push(framework);
157-
} else {
158-
link_libs.push(format!("{}.framework", framework));
159-
}
236+
link_libs.push(LinkLib(Linkage::Framework, framework));
160237
} else if !arg.starts_with('-') {
161238
let path = Path::new(arg);
162-
if let Some(file) = path.file_name().and_then(super::cleanup_lib_filename) {
239+
if let Some(cleaned_lib_filename) = path.file_name().and_then(LinkLib::cleanup_lib_filename) {
240+
let linkage = Linkage::from_path(path);
163241
if let Some(parent) = path.parent().map(|p| p.to_owned()) {
164-
if !link_paths.contains(&parent) {
165-
link_paths.push(parent);
242+
let search_path = LinkSearch(linkage, parent);
243+
if !link_paths.contains(&search_path) {
244+
link_paths.push(search_path);
166245
}
167246
} else {
168247
panic!("{}", arg.to_string());
169248
}
170-
link_libs.push(file.to_str().expect("Non-UTF8 filename").to_string());
249+
link_libs.push(LinkLib(
250+
linkage,
251+
cleaned_lib_filename.to_str().expect("Non-UTF8 filename").to_string(),
252+
));
171253
}
172254
} else {
173255
eprintln!("=== Unexpected cmake compiler argument found: {arg}");
174256
}
175257
}
176258
}
177259

178-
fn extract_from_makefile(&self, link_paths: &mut Vec<PathBuf>, link_libs: &mut Vec<String>) -> Result<()> {
260+
fn extract_from_makefile(&self, link_paths: &mut Vec<LinkSearch>, link_libs: &mut Vec<LinkLib>) -> Result<()> {
179261
let link_cmdline = fs::read_to_string(self.build_dir.join("CMakeFiles/ocvrs_probe.dir/link.txt"))?;
180-
Self::extract_from_cmdline(&link_cmdline, &mut vec![], link_paths, link_libs);
262+
Self::extract_from_cmdline(&link_cmdline, true, &mut vec![], link_paths, link_libs);
181263
Ok(())
182264
}
183265

184266
fn extract_from_ninja(
185267
&self,
186268
include_paths: &mut Vec<PathBuf>,
187-
link_paths: &mut Vec<PathBuf>,
188-
link_libs: &mut Vec<String>,
269+
link_paths: &mut Vec<LinkSearch>,
270+
link_libs: &mut Vec<LinkLib>,
189271
) -> Result<()> {
190272
let mut link_cmdline = BufReader::new(File::open(self.build_dir.join("build.ninja"))?);
191273
let mut line = String::with_capacity(2048);
@@ -208,9 +290,9 @@ impl<'r> CmakeProbe<'r> {
208290
State::Reading => {
209291
let trimmed_line = line.trim_start();
210292
if let Some(paths) = trimmed_line.strip_prefix("LINK_PATH = ") {
211-
Self::extract_from_cmdline(paths, include_paths, link_paths, link_libs);
293+
Self::extract_from_cmdline(paths, false, include_paths, link_paths, link_libs);
212294
} else if let Some(libs) = trimmed_line.strip_prefix("LINK_LIBRARIES = ") {
213-
Self::extract_from_cmdline(libs, include_paths, link_paths, link_libs);
295+
Self::extract_from_cmdline(libs, false, include_paths, link_paths, link_libs);
214296
}
215297
}
216298
}
@@ -292,7 +374,7 @@ impl<'r> CmakeProbe<'r> {
292374
if output.status.success() {
293375
let stdout = String::from_utf8(output.stdout)?;
294376
eprintln!("=== cmake include arguments: {stdout:#?}");
295-
Self::extract_from_cmdline(&stdout, &mut include_paths, &mut link_paths, &mut link_libs);
377+
Self::extract_from_cmdline(&stdout, false, &mut include_paths, &mut link_paths, &mut link_libs);
296378
Ok(())
297379
} else {
298380
Err(
@@ -314,7 +396,7 @@ impl<'r> CmakeProbe<'r> {
314396
if output.status.success() {
315397
let stdout = String::from_utf8(output.stdout)?;
316398
eprintln!("=== cmake link arguments: {stdout:#?}");
317-
Self::extract_from_cmdline(&stdout, &mut include_paths, &mut link_paths, &mut link_libs);
399+
Self::extract_from_cmdline(&stdout, false, &mut include_paths, &mut link_paths, &mut link_libs);
318400
Ok(())
319401
} else {
320402
Err(

build/docs.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@ pub fn handle_running_in_docsrs() -> GenerateFullBindings {
88
if env::var_os("DOCS_RS").is_some() {
99
let docs_dir = MANIFEST_DIR.join("docs");
1010
// fake setup for docs.rs
11-
println!(r#"cargo:rustc-cfg=ocvrs_opencv_branch_4"#);
11+
println!("cargo:rustc-cfg=ocvrs_opencv_branch_4"); // replace with cargo:: syntax when MSRV is 1.77
1212
transfer_bindings_from_docs(&docs_dir, &OUT_DIR);
1313
for path in files_with_extension(&docs_dir, "rs").expect("Can't read hub dir") {
1414
if let Some(module) = path.file_stem().and_then(OsStr::to_str) {
15-
println!("cargo:rustc-cfg=ocvrs_has_module_{module}");
15+
println!("cargo:rustc-cfg=ocvrs_has_module_{module}"); // replace with cargo:: syntax when MSRV is 1.77
1616
}
1717
}
1818
GenerateFullBindings::Stop

0 commit comments

Comments
 (0)