Skip to content

Commit 80bda34

Browse files
committed
Indicate static linker flag for static libraries
1 parent 3278d78 commit 80bda34

File tree

6 files changed

+332
-202
lines changed

6 files changed

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

13100
pub struct ProbeResult {
14101
pub version: Option<Version>,
15102
pub include_paths: Vec<PathBuf>,
16-
pub link_paths: Vec<PathBuf>,
17-
pub link_libs: Vec<String>,
103+
pub link_paths: Vec<LinkSearch>,
104+
pub link_libs: Vec<LinkLib>,
18105
}
19106

20107
pub struct CmakeProbe<'r> {
@@ -117,12 +204,16 @@ impl<'r> CmakeProbe<'r> {
117204

118205
pub(crate) fn extract_from_cmdline(
119206
cmdline: &str,
207+
skip_cmd: bool,
120208
include_paths: &mut Vec<PathBuf>,
121-
link_paths: &mut Vec<PathBuf>,
122-
link_libs: &mut Vec<String>,
209+
link_paths: &mut Vec<LinkSearch>,
210+
link_libs: &mut Vec<LinkLib>,
123211
) {
124212
eprintln!("=== Extracting build arguments from: {cmdline}");
125213
let mut args = Shlex::new(cmdline.trim());
214+
if skip_cmd {
215+
args.next();
216+
}
126217
while let Some(arg) = args.next() {
127218
let arg = arg.trim();
128219
if let Some(path) = arg.strip_prefix("-I") {
@@ -131,14 +222,14 @@ impl<'r> CmakeProbe<'r> {
131222
include_paths.push(path);
132223
}
133224
} else if let Some(path) = arg.strip_prefix("-L").or_else(|| arg.strip_prefix("-Wl,-rpath,")) {
134-
let path = PathBuf::from(path.trim_start());
225+
let path = LinkSearch(Linkage::Default, PathBuf::from(path.trim_start()));
135226
if !link_paths.contains(&path) {
136227
link_paths.push(path);
137228
}
138229
} else if let Some(lib) = arg.strip_prefix("-l") {
139230
// unresolved cmake dependency specification like Qt5::Core
140-
if !lib.contains("::") {
141-
link_libs.push(lib.trim_start().to_string());
231+
if !lib.contains("::") && lib != "gflags_shared" {
232+
link_libs.push(LinkLib(Linkage::Default, lib.trim_start().to_string()));
142233
}
143234
} else if let Some(framework) = arg.strip_prefix("-framework") {
144235
let framework = framework.trim_start();
@@ -147,45 +238,41 @@ impl<'r> CmakeProbe<'r> {
147238
} else {
148239
framework.to_string()
149240
};
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-
}
241+
link_libs.push(LinkLib(Linkage::Framework, framework));
160242
} else if !arg.starts_with('-') {
161243
let path = Path::new(arg);
162-
if let Some(file) = path.file_name().and_then(super::cleanup_lib_filename) {
244+
if let Some(cleaned_lib_filename) = path.file_name().and_then(LinkLib::cleanup_lib_filename) {
245+
let linkage = Linkage::from_path(path);
163246
if let Some(parent) = path.parent().map(|p| p.to_owned()) {
164-
if !link_paths.contains(&parent) {
165-
link_paths.push(parent);
247+
let search_path = LinkSearch(linkage, parent);
248+
if !link_paths.contains(&search_path) {
249+
link_paths.push(search_path);
166250
}
167251
} else {
168252
panic!("{}", arg.to_string());
169253
}
170-
link_libs.push(file.to_str().expect("Non-UTF8 filename").to_string());
254+
link_libs.push(LinkLib(
255+
linkage,
256+
cleaned_lib_filename.to_str().expect("Non-UTF8 filename").to_string(),
257+
));
171258
}
172259
} else {
173260
eprintln!("=== Unexpected cmake compiler argument found: {arg}");
174261
}
175262
}
176263
}
177264

178-
fn extract_from_makefile(&self, link_paths: &mut Vec<PathBuf>, link_libs: &mut Vec<String>) -> Result<()> {
265+
fn extract_from_makefile(&self, link_paths: &mut Vec<LinkSearch>, link_libs: &mut Vec<LinkLib>) -> Result<()> {
179266
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);
267+
Self::extract_from_cmdline(&link_cmdline, true, &mut vec![], link_paths, link_libs);
181268
Ok(())
182269
}
183270

184271
fn extract_from_ninja(
185272
&self,
186273
include_paths: &mut Vec<PathBuf>,
187-
link_paths: &mut Vec<PathBuf>,
188-
link_libs: &mut Vec<String>,
274+
link_paths: &mut Vec<LinkSearch>,
275+
link_libs: &mut Vec<LinkLib>,
189276
) -> Result<()> {
190277
let mut link_cmdline = BufReader::new(File::open(self.build_dir.join("build.ninja"))?);
191278
let mut line = String::with_capacity(2048);
@@ -208,9 +295,9 @@ impl<'r> CmakeProbe<'r> {
208295
State::Reading => {
209296
let trimmed_line = line.trim_start();
210297
if let Some(paths) = trimmed_line.strip_prefix("LINK_PATH = ") {
211-
Self::extract_from_cmdline(paths, include_paths, link_paths, link_libs);
298+
Self::extract_from_cmdline(paths, false, include_paths, link_paths, link_libs);
212299
} else if let Some(libs) = trimmed_line.strip_prefix("LINK_LIBRARIES = ") {
213-
Self::extract_from_cmdline(libs, include_paths, link_paths, link_libs);
300+
Self::extract_from_cmdline(libs, false, include_paths, link_paths, link_libs);
214301
}
215302
}
216303
}
@@ -292,7 +379,7 @@ impl<'r> CmakeProbe<'r> {
292379
if output.status.success() {
293380
let stdout = String::from_utf8(output.stdout)?;
294381
eprintln!("=== cmake include arguments: {stdout:#?}");
295-
Self::extract_from_cmdline(&stdout, &mut include_paths, &mut link_paths, &mut link_libs);
382+
Self::extract_from_cmdline(&stdout, false, &mut include_paths, &mut link_paths, &mut link_libs);
296383
Ok(())
297384
} else {
298385
Err(
@@ -314,7 +401,7 @@ impl<'r> CmakeProbe<'r> {
314401
if output.status.success() {
315402
let stdout = String::from_utf8(output.stdout)?;
316403
eprintln!("=== cmake link arguments: {stdout:#?}");
317-
Self::extract_from_cmdline(&stdout, &mut include_paths, &mut link_paths, &mut link_libs);
404+
Self::extract_from_cmdline(&stdout, false, &mut include_paths, &mut link_paths, &mut link_libs);
318405
Ok(())
319406
} else {
320407
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)