|
1 |
| -use std::ffi::OsStr; |
2 |
| -use std::fs::{File, OpenOptions}; |
3 |
| -use std::io::{BufRead, BufReader, BufWriter, Write}; |
4 | 1 | use std::ops::Deref;
|
5 | 2 | use std::path::{Path, PathBuf};
|
6 | 3 | use std::process::Command;
|
7 | 4 | use std::time::Instant;
|
8 |
| -use std::{env, fs, io, thread}; |
| 5 | +use std::{env, fs, thread}; |
9 | 6 |
|
| 7 | +use collector::Collector; |
10 | 8 | use opencv_binding_generator::{Generator, IteratorExt};
|
11 | 9 |
|
12 | 10 | use super::docs::transfer_bindings_to_docs;
|
13 |
| -use super::{files_with_extension, files_with_predicate, Library, Result, MODULES, OUT_DIR, SRC_CPP_DIR, SRC_DIR}; |
| 11 | +use super::{files_with_predicate, Library, Result, MODULES, OUT_DIR, SRC_CPP_DIR, SRC_DIR}; |
| 12 | + |
| 13 | +#[path = "generator/collector.rs"] |
| 14 | +mod collector; |
14 | 15 |
|
15 | 16 | pub struct BindingGenerator {
|
16 | 17 | build_script_path: PathBuf,
|
@@ -47,7 +48,8 @@ impl BindingGenerator {
|
47 | 48 |
|
48 | 49 | self.run(modules, opencv_header_dir, opencv)?;
|
49 | 50 |
|
50 |
| - collect_generated_bindings(modules, &target_module_dir, &manual_dir)?; |
| 51 | + let collector = Collector::new(modules, &target_module_dir, &manual_dir, &OUT_DIR); |
| 52 | + collector.collect_bindings()?; |
51 | 53 |
|
52 | 54 | if let Some(target_docs_dir) = target_docs_dir {
|
53 | 55 | if !target_docs_dir.exists() {
|
@@ -82,7 +84,7 @@ impl BindingGenerator {
|
82 | 84 | .into_iter()
|
83 | 85 | .map(|p| p.to_str().expect("Can't convert additional include dir to UTF-8 string"))
|
84 | 86 | .join(",");
|
85 |
| - let job_server = build_job_server()?; |
| 87 | + let job_server = Jobserver::build()?; |
86 | 88 | let start = Instant::now();
|
87 | 89 | eprintln!("=== Generating {} modules", modules.len());
|
88 | 90 | thread::scope(|scope| {
|
@@ -123,233 +125,48 @@ impl BindingGenerator {
|
123 | 125 | }
|
124 | 126 | }
|
125 | 127 |
|
126 |
| -fn is_type_file(path: &Path, module: &str) -> bool { |
127 |
| - path.file_stem().and_then(OsStr::to_str).map_or(false, |stem| { |
128 |
| - let mut stem_chars = stem.chars(); |
129 |
| - (&mut stem_chars).take(3).all(|c| c.is_ascii_digit()) && // first 3 chars are digits |
130 |
| - matches!(stem_chars.next(), Some('-')) && // dash |
131 |
| - module.chars().zip(&mut stem_chars).all(|(m, s)| m == s) && // module name |
132 |
| - matches!(stem_chars.next(), Some('-')) && // dash |
133 |
| - stem.ends_with(".type") // ends with ".type" |
134 |
| - }) |
135 |
| -} |
136 |
| - |
137 |
| -fn is_type_externs_file(path: &Path, module: &str) -> bool { |
138 |
| - path.file_stem().and_then(OsStr::to_str).map_or(false, |stem| { |
139 |
| - let mut stem_chars = stem.chars(); |
140 |
| - (&mut stem_chars).take(3).all(|c| c.is_ascii_digit()) && // first 3 chars are digits |
141 |
| - matches!(stem_chars.next(), Some('-')) && // dash |
142 |
| - module.chars().zip(&mut stem_chars).all(|(m, s)| m == s) && // module name |
143 |
| - matches!(stem_chars.next(), Some('-')) && // dash |
144 |
| - stem.ends_with(".type.externs") // ends with ".type" |
145 |
| - }) |
146 |
| -} |
147 |
| - |
148 |
| -fn copy_indent(mut read: impl BufRead, mut write: impl Write, indent: &str) -> Result<()> { |
149 |
| - let mut line = Vec::with_capacity(100); |
150 |
| - while read.read_until(b'\n', &mut line)? != 0 { |
151 |
| - write.write_all(indent.as_bytes())?; |
152 |
| - write.write_all(&line)?; |
153 |
| - line.clear(); |
154 |
| - } |
155 |
| - Ok(()) |
| 128 | +pub struct Jobserver { |
| 129 | + client: jobserver::Client, |
| 130 | + reacquire_token_on_drop: bool, |
156 | 131 | }
|
157 | 132 |
|
158 |
| -fn collect_generated_bindings(modules: &[String], target_module_dir: &Path, manual_dir: &Path) -> Result<()> { |
159 |
| - if !target_module_dir.exists() { |
160 |
| - fs::create_dir(target_module_dir)?; |
161 |
| - } |
162 |
| - for path in files_with_extension(target_module_dir, "rs")? { |
163 |
| - let _ = fs::remove_file(path); |
164 |
| - } |
165 |
| - |
166 |
| - fn write_has_module(mut write: impl Write, module: &str) -> Result<()> { |
167 |
| - Ok(writeln!(write, "#[cfg(ocvrs_has_module_{module})]")?) |
168 |
| - } |
169 |
| - |
170 |
| - fn write_module_include(write: &mut BufWriter<File>, module: &str) -> Result<()> { |
171 |
| - // Use include instead of #[path] attribute because rust-analyzer doesn't handle #[path] inside other include! too well: |
172 |
| - // https://github.com/twistedfall/opencv-rust/issues/418 |
173 |
| - // https://github.com/rust-lang/rust-analyzer/issues/11682 |
174 |
| - Ok(writeln!( |
175 |
| - write, |
176 |
| - r#"include!(concat!(env!("OUT_DIR"), "/opencv/{module}.rs"));"# |
177 |
| - )?) |
178 |
| - } |
179 |
| - |
180 |
| - let add_manual = |file: &mut BufWriter<File>, module: &str| -> Result<bool> { |
181 |
| - if manual_dir.join(format!("{module}.rs")).exists() { |
182 |
| - writeln!(file, "pub use crate::manual::{module}::*;")?; |
183 |
| - Ok(true) |
184 |
| - } else { |
185 |
| - Ok(false) |
186 |
| - } |
187 |
| - }; |
188 |
| - |
189 |
| - let start = Instant::now(); |
190 |
| - let mut hub_rs = BufWriter::new(File::create(target_module_dir.join("hub.rs"))?); |
191 |
| - |
192 |
| - let mut types_rs = BufWriter::new(File::create(target_module_dir.join("types.rs"))?); |
193 |
| - writeln!(types_rs)?; |
194 |
| - |
195 |
| - let mut sys_rs = BufWriter::new(File::create(target_module_dir.join("sys.rs"))?); |
196 |
| - writeln!(sys_rs, "use crate::{{mod_prelude_sys::*, core}};")?; |
197 |
| - writeln!(sys_rs)?; |
198 |
| - |
199 |
| - for module in modules { |
200 |
| - // merge multiple *-type.cpp files into a single module_types.hpp |
201 |
| - let module_cpp = OUT_DIR.join(format!("{module}.cpp")); |
202 |
| - if module_cpp.is_file() { |
203 |
| - let module_types_cpp = OUT_DIR.join(format!("{module}_types.hpp")); |
204 |
| - let mut module_types_file = BufWriter::new( |
205 |
| - OpenOptions::new() |
206 |
| - .create(true) |
207 |
| - .truncate(true) |
208 |
| - .write(true) |
209 |
| - .open(module_types_cpp)?, |
210 |
| - ); |
211 |
| - let mut type_files = files_with_extension(&OUT_DIR, "cpp")? |
212 |
| - .filter(|f| is_type_file(f, module)) |
213 |
| - .collect::<Vec<_>>(); |
214 |
| - type_files.sort_unstable(); |
215 |
| - for entry in type_files { |
216 |
| - io::copy(&mut BufReader::new(File::open(&entry)?), &mut module_types_file)?; |
217 |
| - let _ = fs::remove_file(entry); |
218 |
| - } |
219 |
| - } |
220 |
| - |
221 |
| - // add module entry to hub.rs and move the module file into opencv/ |
222 |
| - write_has_module(&mut hub_rs, module)?; |
223 |
| - write_module_include(&mut hub_rs, module)?; |
224 |
| - let module_filename = format!("{module}.rs"); |
225 |
| - let module_src_file = OUT_DIR.join(&module_filename); |
226 |
| - let mut module_rs = BufWriter::new(File::create(target_module_dir.join(&module_filename))?); |
227 |
| - // Need to wrap modules inside `mod { }` because they have top-level comments (//!) and those don't play well when |
228 |
| - // module file is include!d (as opposed to connecting the module with `mod` from the parent module). |
229 |
| - // The same doesn't apply to `sys` and `types` below because they don't contain top-level comments. |
230 |
| - writeln!(module_rs, "pub mod {module} {{")?; |
231 |
| - copy_indent(BufReader::new(File::open(&module_src_file)?), &mut module_rs, "\t")?; |
232 |
| - add_manual(&mut module_rs, module)?; |
233 |
| - writeln!(module_rs, "}}")?; |
234 |
| - let _ = fs::remove_file(module_src_file); |
235 |
| - |
236 |
| - // merge multiple *-.type.rs files into a single types.rs |
237 |
| - let mut header_written = false; |
238 |
| - let mut type_files = files_with_extension(&OUT_DIR, "rs")? |
239 |
| - .filter(|f| is_type_file(f, module)) |
240 |
| - .collect::<Vec<_>>(); |
241 |
| - type_files.sort_unstable(); |
242 |
| - for entry in type_files { |
243 |
| - if entry.metadata().map(|meta| meta.len()).unwrap_or(0) > 0 { |
244 |
| - if !header_written { |
245 |
| - write_has_module(&mut types_rs, module)?; |
246 |
| - writeln!(types_rs, "mod {module}_types {{")?; |
247 |
| - writeln!(types_rs, "\tuse crate::{{mod_prelude::*, core, types, sys}};")?; |
248 |
| - writeln!(types_rs)?; |
249 |
| - header_written = true; |
| 133 | +impl Jobserver { |
| 134 | + pub fn build() -> Result<Self> { |
| 135 | + unsafe { jobserver::Client::from_env() } |
| 136 | + .and_then(|client| { |
| 137 | + let own_token_released = client.release_raw().is_ok(); |
| 138 | + let available_jobs = client.available().unwrap_or(0); |
| 139 | + if available_jobs > 0 { |
| 140 | + eprintln!("=== Using environment job server with the the amount of available jobs: {available_jobs}"); |
| 141 | + Some(Jobserver { |
| 142 | + client, |
| 143 | + reacquire_token_on_drop: own_token_released, |
| 144 | + }) |
| 145 | + } else { |
| 146 | + if own_token_released { |
| 147 | + client.acquire_raw().expect("Can't reacquire build script thread token"); |
| 148 | + } |
| 149 | + eprintln!( |
| 150 | + "=== Available jobs from the environment created jobserver is: {available_jobs} or there is an error reading that value" |
| 151 | + ); |
| 152 | + None |
250 | 153 | }
|
251 |
| - copy_indent(BufReader::new(File::open(&entry)?), &mut types_rs, "\t")?; |
252 |
| - } |
253 |
| - let _ = fs::remove_file(entry); |
254 |
| - } |
255 |
| - if header_written { |
256 |
| - writeln!(types_rs, "}}")?; |
257 |
| - write_has_module(&mut types_rs, module)?; |
258 |
| - writeln!(types_rs, "pub use {module}_types::*;")?; |
259 |
| - writeln!(types_rs)?; |
260 |
| - } |
261 |
| - |
262 |
| - // merge module-specific *.externs.rs and generated type-specific *.type.externs.rs into a single sys.rs |
263 |
| - let externs_rs = OUT_DIR.join(format!("{module}.externs.rs")); |
264 |
| - write_has_module(&mut sys_rs, module)?; |
265 |
| - writeln!(sys_rs, "mod {module}_sys {{")?; |
266 |
| - writeln!(sys_rs, "\tuse super::*;")?; |
267 |
| - writeln!(sys_rs)?; |
268 |
| - writeln!(sys_rs, "\textern \"C\" {{")?; |
269 |
| - copy_indent(BufReader::new(File::open(&externs_rs)?), &mut sys_rs, "\t\t")?; |
270 |
| - let _ = fs::remove_file(externs_rs); |
271 |
| - let mut type_extern_files = files_with_extension(&OUT_DIR, "rs")? |
272 |
| - .filter(|f| is_type_externs_file(f, module)) |
273 |
| - .collect::<Vec<_>>(); |
274 |
| - type_extern_files.sort_unstable(); |
275 |
| - for entry in type_extern_files { |
276 |
| - if entry.metadata().map(|meta| meta.len()).unwrap_or(0) > 0 { |
277 |
| - copy_indent(BufReader::new(File::open(&entry)?), &mut sys_rs, "\t\t")?; |
278 |
| - } |
279 |
| - let _ = fs::remove_file(entry); |
280 |
| - } |
281 |
| - writeln!(sys_rs, "\t}}")?; |
282 |
| - writeln!(sys_rs, "}}")?; |
283 |
| - write_has_module(&mut sys_rs, module)?; |
284 |
| - writeln!(sys_rs, "pub use {module}_sys::*;")?; |
285 |
| - writeln!(sys_rs)?; |
286 |
| - } |
287 |
| - writeln!(hub_rs, "pub mod types {{")?; |
288 |
| - write!(hub_rs, "\t")?; |
289 |
| - write_module_include(&mut hub_rs, "types")?; |
290 |
| - writeln!(hub_rs, "}}")?; |
291 |
| - writeln!(hub_rs, "#[doc(hidden)]")?; |
292 |
| - writeln!(hub_rs, "pub mod sys {{")?; |
293 |
| - write!(hub_rs, "\t")?; |
294 |
| - write_module_include(&mut hub_rs, "sys")?; |
295 |
| - writeln!(hub_rs, "}}")?; |
296 |
| - |
297 |
| - add_manual(&mut types_rs, "types")?; |
298 |
| - |
299 |
| - add_manual(&mut sys_rs, "sys")?; |
300 |
| - |
301 |
| - // write hub_prelude that imports all module-specific preludes |
302 |
| - writeln!(hub_rs, "pub mod hub_prelude {{")?; |
303 |
| - for module in modules { |
304 |
| - write!(hub_rs, "\t")?; |
305 |
| - write_has_module(&mut hub_rs, module)?; |
306 |
| - writeln!(hub_rs, "\tpub use super::{module}::prelude::*;")?; |
307 |
| - } |
308 |
| - writeln!(hub_rs, "}}")?; |
309 |
| - eprintln!("=== Total binding collection time: {:?}", start.elapsed()); |
310 |
| - Ok(()) |
311 |
| -} |
312 |
| - |
313 |
| -fn build_job_server() -> Result<Jobserver> { |
314 |
| - unsafe { jobserver::Client::from_env() } |
315 |
| - .and_then(|client| { |
316 |
| - let own_token_released = client.release_raw().is_ok(); |
317 |
| - let available_jobs = client.available().unwrap_or(0); |
318 |
| - if available_jobs > 0 { |
319 |
| - eprintln!("=== Using environment job server with the the amount of available jobs: {available_jobs}"); |
320 |
| - Some(Jobserver { |
| 154 | + }) |
| 155 | + .or_else(|| { |
| 156 | + let num_jobs = env::var("NUM_JOBS") |
| 157 | + .ok() |
| 158 | + .and_then(|jobs| jobs.parse().ok()) |
| 159 | + .or_else(|| thread::available_parallelism().map(|p| p.get()).ok()) |
| 160 | + .unwrap_or(2) |
| 161 | + .max(1); |
| 162 | + eprintln!("=== Creating a new job server with num_jobs: {num_jobs}"); |
| 163 | + jobserver::Client::new(num_jobs).ok().map(|client| Jobserver { |
321 | 164 | client,
|
322 |
| - reacquire_token_on_drop: own_token_released, |
| 165 | + reacquire_token_on_drop: false, |
323 | 166 | })
|
324 |
| - } else { |
325 |
| - if own_token_released { |
326 |
| - client.acquire_raw().expect("Can't reacquire build script thread token"); |
327 |
| - } |
328 |
| - eprintln!( |
329 |
| - "=== Available jobs from the environment created jobserver is: {available_jobs} or there is an error reading that value" |
330 |
| - ); |
331 |
| - None |
332 |
| - } |
333 |
| - }) |
334 |
| - .or_else(|| { |
335 |
| - let num_jobs = env::var("NUM_JOBS") |
336 |
| - .ok() |
337 |
| - .and_then(|jobs| jobs.parse().ok()) |
338 |
| - .or_else(|| thread::available_parallelism().map(|p| p.get()).ok()) |
339 |
| - .unwrap_or(2) |
340 |
| - .max(1); |
341 |
| - eprintln!("=== Creating a new job server with num_jobs: {num_jobs}"); |
342 |
| - jobserver::Client::new(num_jobs).ok().map(|client| Jobserver { |
343 |
| - client, |
344 |
| - reacquire_token_on_drop: false, |
345 | 167 | })
|
346 |
| - }) |
347 |
| - .ok_or_else(|| "Can't create job server".into()) |
348 |
| -} |
349 |
| - |
350 |
| -pub struct Jobserver { |
351 |
| - client: jobserver::Client, |
352 |
| - reacquire_token_on_drop: bool, |
| 168 | + .ok_or_else(|| "Can't create job server".into()) |
| 169 | + } |
353 | 170 | }
|
354 | 171 |
|
355 | 172 | impl Drop for Jobserver {
|
|
0 commit comments