|
12 | 12 | use prebindgen::{get_prebindgen_out_dir, Record, RecordKind, SourceLocation, DEFAULT_GROUP_NAME}; |
13 | 13 | use proc_macro::TokenStream; |
14 | 14 | use quote::quote; |
15 | | -use std::fs::{metadata, OpenOptions}; |
16 | | -use std::io::Write; |
17 | | -use std::path::Path; |
| 15 | +use std::fs::OpenOptions; |
18 | 16 | use syn::parse::{Parse, ParseStream}; |
19 | 17 | use syn::spanned::Spanned; |
20 | 18 | use syn::{DeriveInput, ItemConst, ItemFn, ItemType}; |
@@ -109,18 +107,38 @@ impl Parse for PrebindgenArgs { |
109 | 107 | } |
110 | 108 | } |
111 | 109 |
|
| 110 | +thread_local! { |
| 111 | + static PREBINDGEN_JSONL_PATH: std::cell::RefCell<Option<std::path::PathBuf>> = std::cell::RefCell::new(None); |
| 112 | +} |
| 113 | + |
112 | 114 | /// Get the full path to `{group}_{pid}_{thread_id}.jsonl` generated in OUT_DIR. |
| 115 | +/// Use another approach: make unique file name, create it and in case of sucess |
| 116 | +/// store the name in Option<PathBuf> threadlocal variable. |
| 117 | +/// If variable is already some, use this value. |
| 118 | +/// Form file name by calling get_prebindgen_jsonl_path() + random value |
113 | 119 | fn get_prebindgen_jsonl_path(name: &str) -> std::path::PathBuf { |
114 | | - let thread_id = std::thread::current().id(); |
| 120 | + if let Some(p) = PREBINDGEN_JSONL_PATH.with(|path| path.borrow().as_ref().map(|p| p.clone())) { |
| 121 | + return p; |
| 122 | + } |
115 | 123 | let process_id = std::process::id(); |
116 | | - // Extract numeric thread ID from ThreadId debug representation |
117 | | - let thread_id_str = format!("{thread_id:?}"); |
118 | | - let thread_id_num = thread_id_str |
119 | | - .strip_prefix("ThreadId(") |
120 | | - .and_then(|s| s.strip_suffix(")")) |
121 | | - .unwrap_or("0"); |
122 | | - |
123 | | - get_prebindgen_out_dir().join(format!("{name}_{process_id}_{thread_id_num}.jsonl")) |
| 124 | + // Try to really create file and return it only if successful |
| 125 | + let new_path = loop { |
| 126 | + let random_value = rand::random::<u64>(); |
| 127 | + let path = |
| 128 | + get_prebindgen_out_dir().join(format!("{name}_{process_id}_{random_value}.jsonl")); |
| 129 | + if OpenOptions::new() |
| 130 | + .create_new(true) |
| 131 | + .write(true) |
| 132 | + .open(&path) |
| 133 | + .is_ok() |
| 134 | + { |
| 135 | + break path; |
| 136 | + } |
| 137 | + }; |
| 138 | + PREBINDGEN_JSONL_PATH.with(|path| { |
| 139 | + *path.borrow_mut() = Some(new_path.clone()); |
| 140 | + }); |
| 141 | + new_path |
124 | 142 | } |
125 | 143 |
|
126 | 144 | /// Attribute macro that exports FFI definitions for use in language-specific binding crates. |
@@ -174,10 +192,6 @@ pub fn prebindgen(args: TokenStream, input: TokenStream) -> TokenStream { |
174 | 192 |
|
175 | 193 | let group = parsed_args.group; |
176 | 194 |
|
177 | | - // Get the full path to the JSONL file |
178 | | - let file_path = get_prebindgen_jsonl_path(&group); |
179 | | - let dest_path = Path::new(&file_path); |
180 | | - |
181 | 195 | // Try to parse as different item types |
182 | 196 | let (kind, name, content, span) = if let Ok(parsed) = syn::parse::<DeriveInput>(input.clone()) { |
183 | 197 | // Handle struct, enum, union |
@@ -241,21 +255,12 @@ pub fn prebindgen(args: TokenStream, input: TokenStream) -> TokenStream { |
241 | 255 | parsed_args.cfg.clone(), |
242 | 256 | ); |
243 | 257 |
|
244 | | - // Convert record to JSON and append to file in JSON-lines format |
245 | | - if let Ok(json_content) = serde_json::to_string(&new_record) { |
246 | | - if let Ok(mut file) = OpenOptions::new().create(true).append(true).open(dest_path) { |
247 | | - // Check if file is empty (just created or was deleted) |
248 | | - let is_empty = metadata(dest_path).map(|m| m.len() == 0).unwrap_or(true); |
249 | | - |
250 | | - if is_empty { |
251 | | - #[cfg(feature = "debug")] |
252 | | - println!("Creating jsonl file: {}", dest_path.display()); |
253 | | - } |
254 | | - |
255 | | - // Write the record as a single line (JSON-lines format) |
256 | | - let _ = writeln!(file, "{json_content}"); |
257 | | - let _ = file.flush(); |
258 | | - } |
| 258 | + // Get the full path to the JSONL file |
| 259 | + let file_path = get_prebindgen_jsonl_path(&group); |
| 260 | + if let Err(_) = prebindgen::write_to_jsonl_file(&file_path, &[&new_record]) { |
| 261 | + return TokenStream::from(quote! { |
| 262 | + compile_error!("Failed to write prebindgen record"); |
| 263 | + }); |
259 | 264 | } |
260 | 265 |
|
261 | 266 | // Apply cfg attribute to the original code if specified |
|
0 commit comments