@@ -8,10 +8,20 @@ use std::path::PathBuf;
88use std:: process:: Command ;
99use std:: sync:: Once ;
1010
11+ use std:: sync:: Mutex ;
12+
1113static INIT : Once = Once :: new ( ) ;
1214static COMPILE : Once = Once :: new ( ) ;
1315static REGISTER_CLEANUP : Once = Once :: new ( ) ;
1416
17+ lazy_static ! {
18+ static ref COMPILE_DEBUG_RESULT : Mutex <Option <anyhow:: Result <( ) >>> = Mutex :: new( None ) ;
19+ static ref COMPILE_OPT_RESULT : Mutex <Option <anyhow:: Result <( ) >>> = Mutex :: new( None ) ;
20+ static ref COMPILE_COMPLEX_DEBUG_RESULT : Mutex <Option <anyhow:: Result <( ) >>> = Mutex :: new( None ) ;
21+ static ref COMPILE_COMPLEX_OPT_RESULT : Mutex <Option <anyhow:: Result <( ) >>> = Mutex :: new( None ) ;
22+ static ref COMPILE_COMPLEX_NOPIE_RESULT : Mutex <Option <anyhow:: Result <( ) >>> = Mutex :: new( None ) ;
23+ }
24+
1525/// Initialize logging for tests (call once per test)
1626pub fn init ( ) {
1727 INIT . call_once ( || {
@@ -94,146 +104,151 @@ pub fn ensure_test_program_compiled() -> anyhow::Result<()> {
94104
95105/// Compile test program with specific optimization level
96106pub fn ensure_test_program_compiled_with_opt ( opt_level : OptimizationLevel ) -> anyhow:: Result < ( ) > {
97- let mut result = Ok ( ( ) ) ;
98-
99- let compile_fn = || {
100- let fixtures_path = PathBuf :: from ( env ! ( "CARGO_MANIFEST_DIR" ) ) . join ( "tests/fixtures" ) ;
101- let sample_program_dir = fixtures_path. join ( "sample_program" ) ;
102-
103- println ! (
104- "Compiling sample_program {} in {:?}" ,
105- opt_level. description( ) ,
106- sample_program_dir
107- ) ;
108-
109- // Clean first (only for debug builds to avoid conflicts)
110- if opt_level == OptimizationLevel :: Debug {
111- let clean_output = Command :: new ( "make" )
112- . arg ( "clean" )
113- . current_dir ( & sample_program_dir)
114- . output ( ) ;
115-
116- match clean_output {
117- Ok ( _) => println ! ( "✓ Cleaned sample_program build directory" ) ,
118- Err ( e) => {
119- result = Err ( anyhow:: anyhow!( "Failed to clean sample_program: {}" , e) ) ;
120- return ;
121- }
122- }
123- }
124-
125- // Compile specific optimization level
126- let compile_output = Command :: new ( "make" )
127- . arg ( opt_level. as_make_target ( ) )
128- . current_dir ( & sample_program_dir)
129- . output ( ) ;
130-
131- match compile_output {
132- Ok ( output) => {
133- if output. status . success ( ) {
134- println ! (
135- "✓ Successfully compiled sample_program {}" ,
136- opt_level. description( )
137- ) ;
138- } else {
139- let stderr = String :: from_utf8_lossy ( & output. stderr ) ;
140- result = Err ( anyhow:: anyhow!(
141- "Failed to compile sample_program {}: {}" ,
142- opt_level. description( ) ,
143- stderr
144- ) ) ;
145- }
146- }
147- Err ( e) => {
148- result = Err ( anyhow:: anyhow!(
149- "Failed to run make for sample_program {}: {}" ,
150- opt_level. description( ) ,
151- e
152- ) ) ;
153- }
154- }
155- } ;
156-
157107 match opt_level {
158108 OptimizationLevel :: Debug => {
159- COMPILE . call_once ( compile_fn) ;
109+ COMPILE . call_once ( || {
110+ let compile_result = compile_sample_program ( opt_level) ;
111+ * COMPILE_DEBUG_RESULT . lock ( ) . unwrap ( ) = Some ( compile_result) ;
112+ } ) ;
113+ match COMPILE_DEBUG_RESULT . lock ( ) . unwrap ( ) . as_ref ( ) {
114+ Some ( Ok ( ( ) ) ) => Ok ( ( ) ) ,
115+ Some ( Err ( e) ) => Err ( anyhow:: anyhow!( "{}" , e) ) ,
116+ None => panic ! ( "Compilation result should be set after call_once" ) ,
117+ }
160118 }
161119 _ => {
162- COMPILE_OPTIMIZED . call_once ( compile_fn) ;
120+ COMPILE_OPTIMIZED . call_once ( || {
121+ let compile_result = compile_sample_program ( opt_level) ;
122+ * COMPILE_OPT_RESULT . lock ( ) . unwrap ( ) = Some ( compile_result) ;
123+ } ) ;
124+ match COMPILE_OPT_RESULT . lock ( ) . unwrap ( ) . as_ref ( ) {
125+ Some ( Ok ( ( ) ) ) => Ok ( ( ) ) ,
126+ Some ( Err ( e) ) => Err ( anyhow:: anyhow!( "{}" , e) ) ,
127+ None => panic ! ( "Compilation result should be set after call_once" ) ,
128+ }
163129 }
164130 }
131+ }
165132
166- result
133+ fn compile_sample_program ( opt_level : OptimizationLevel ) -> anyhow:: Result < ( ) > {
134+ let fixtures_path = PathBuf :: from ( env ! ( "CARGO_MANIFEST_DIR" ) ) . join ( "tests/fixtures" ) ;
135+ let sample_program_dir = fixtures_path. join ( "sample_program" ) ;
136+
137+ println ! (
138+ "Compiling sample_program {} in {:?}" ,
139+ opt_level. description( ) ,
140+ sample_program_dir
141+ ) ;
142+
143+ // Clean first (only for debug builds to avoid conflicts)
144+ if opt_level == OptimizationLevel :: Debug {
145+ Command :: new ( "make" )
146+ . arg ( "clean" )
147+ . current_dir ( & sample_program_dir)
148+ . output ( )
149+ . map_err ( |e| anyhow:: anyhow!( "Failed to clean sample_program: {}" , e) ) ?;
150+ println ! ( "✓ Cleaned sample_program build directory" ) ;
151+ }
152+
153+ // Compile specific optimization level
154+ let output = Command :: new ( "make" )
155+ . arg ( opt_level. as_make_target ( ) )
156+ . current_dir ( & sample_program_dir)
157+ . output ( )
158+ . map_err ( |e| anyhow:: anyhow!( "Failed to run make for sample_program {}: {}" , opt_level. description( ) , e) ) ?;
159+
160+ if output. status . success ( ) {
161+ println ! (
162+ "✓ Successfully compiled sample_program {}" ,
163+ opt_level. description( )
164+ ) ;
165+ Ok ( ( ) )
166+ } else {
167+ let stderr = String :: from_utf8_lossy ( & output. stderr ) ;
168+ Err ( anyhow:: anyhow!(
169+ "Failed to compile sample_program {}: {}" ,
170+ opt_level. description( ) ,
171+ stderr
172+ ) )
173+ }
167174}
168175
169176static COMPILE_COMPLEX_DEBUG : Once = Once :: new ( ) ;
170177static COMPILE_COMPLEX_OPT : Once = Once :: new ( ) ;
171178static COMPILE_COMPLEX_NOPIE : Once = Once :: new ( ) ;
172179
173180fn ensure_complex_program_compiled_with_opt ( opt_level : OptimizationLevel ) -> anyhow:: Result < ( ) > {
174- let mut result = Ok ( ( ) ) ;
175- let compile_fn = || {
176- let fixtures_path = PathBuf :: from ( env ! ( "CARGO_MANIFEST_DIR" ) ) . join ( "tests/fixtures" ) ;
177- let program_dir = fixtures_path. join ( "complex_types_program" ) ;
178-
179- println ! (
180- "Compiling complex_types_program {} in {:?}" ,
181- opt_level. description( ) ,
182- program_dir
183- ) ;
184-
185- // Clean first for debug builds
186- if opt_level == OptimizationLevel :: Debug {
187- let _ = Command :: new ( "make" )
188- . arg ( "clean" )
189- . current_dir ( & program_dir)
190- . output ( ) ;
181+ match opt_level {
182+ OptimizationLevel :: Debug => {
183+ COMPILE_COMPLEX_DEBUG . call_once ( || {
184+ let compile_result = compile_complex_program ( opt_level) ;
185+ * COMPILE_COMPLEX_DEBUG_RESULT . lock ( ) . unwrap ( ) = Some ( compile_result) ;
186+ } ) ;
187+ match COMPILE_COMPLEX_DEBUG_RESULT . lock ( ) . unwrap ( ) . as_ref ( ) {
188+ Some ( Ok ( ( ) ) ) => Ok ( ( ) ) ,
189+ Some ( Err ( e) ) => Err ( anyhow:: anyhow!( "{}" , e) ) ,
190+ None => panic ! ( "Compilation result should be set after call_once" ) ,
191+ }
192+ }
193+ _ => {
194+ COMPILE_COMPLEX_OPT . call_once ( || {
195+ let compile_result = compile_complex_program ( opt_level) ;
196+ * COMPILE_COMPLEX_OPT_RESULT . lock ( ) . unwrap ( ) = Some ( compile_result) ;
197+ } ) ;
198+ match COMPILE_COMPLEX_OPT_RESULT . lock ( ) . unwrap ( ) . as_ref ( ) {
199+ Some ( Ok ( ( ) ) ) => Ok ( ( ) ) ,
200+ Some ( Err ( e) ) => Err ( anyhow:: anyhow!( "{}" , e) ) ,
201+ None => panic ! ( "Compilation result should be set after call_once" ) ,
202+ }
191203 }
204+ }
205+ }
192206
193- let target = match opt_level {
194- OptimizationLevel :: Debug => "complex_types_program" ,
195- OptimizationLevel :: O1 => "complex_types_program_o1" ,
196- OptimizationLevel :: O2 => "complex_types_program_o2" ,
197- OptimizationLevel :: O3 => "complex_types_program_o3" ,
198- } ;
207+ fn compile_complex_program ( opt_level : OptimizationLevel ) -> anyhow:: Result < ( ) > {
208+ let fixtures_path = PathBuf :: from ( env ! ( "CARGO_MANIFEST_DIR" ) ) . join ( "tests/fixtures" ) ;
209+ let program_dir = fixtures_path. join ( "complex_types_program" ) ;
210+
211+ println ! (
212+ "Compiling complex_types_program {} in {:?}" ,
213+ opt_level. description( ) ,
214+ program_dir
215+ ) ;
199216
200- let compile_output = Command :: new ( "make" )
201- . arg ( target)
217+ // Clean first for debug builds
218+ if opt_level == OptimizationLevel :: Debug {
219+ let _ = Command :: new ( "make" )
220+ . arg ( "clean" )
202221 . current_dir ( & program_dir)
203222 . output ( ) ;
223+ }
204224
205- match compile_output {
206- Ok ( output) => {
207- if output. status . success ( ) {
208- println ! (
209- "✓ Successfully compiled complex_types_program {}" ,
210- opt_level. description( )
211- ) ;
212- } else {
213- let stderr = String :: from_utf8_lossy ( & output. stderr ) ;
214- result = Err ( anyhow:: anyhow!(
215- "Failed to compile complex_types_program {}: {}" ,
216- opt_level. description( ) ,
217- stderr
218- ) ) ;
219- }
220- }
221- Err ( e) => {
222- result = Err ( anyhow:: anyhow!(
223- "Failed to run make for complex_types_program {}: {}" ,
224- opt_level. description( ) ,
225- e
226- ) ) ;
227- }
228- }
225+ let target = match opt_level {
226+ OptimizationLevel :: Debug => "complex_types_program" ,
227+ OptimizationLevel :: O1 => "complex_types_program_o1" ,
228+ OptimizationLevel :: O2 => "complex_types_program_o2" ,
229+ OptimizationLevel :: O3 => "complex_types_program_o3" ,
229230 } ;
230231
231- match opt_level {
232- OptimizationLevel :: Debug => COMPILE_COMPLEX_DEBUG . call_once ( compile_fn) ,
233- _ => COMPILE_COMPLEX_OPT . call_once ( compile_fn) ,
234- }
232+ let output = Command :: new ( "make" )
233+ . arg ( target)
234+ . current_dir ( & program_dir)
235+ . output ( )
236+ . map_err ( |e| anyhow:: anyhow!( "Failed to run make for complex_types_program {}: {}" , opt_level. description( ) , e) ) ?;
235237
236- result
238+ if output. status . success ( ) {
239+ println ! (
240+ "✓ Successfully compiled complex_types_program {}" ,
241+ opt_level. description( )
242+ ) ;
243+ Ok ( ( ) )
244+ } else {
245+ let stderr = String :: from_utf8_lossy ( & output. stderr ) ;
246+ Err ( anyhow:: anyhow!(
247+ "Failed to compile complex_types_program {}: {}" ,
248+ opt_level. description( ) ,
249+ stderr
250+ ) )
251+ }
237252}
238253
239254/// Test fixtures manager
@@ -289,43 +304,47 @@ impl TestFixtures {
289304
290305 /// Build and return the non-PIE variant of complex_types_program
291306 pub fn get_test_binary_complex_nopie ( & self ) -> anyhow:: Result < PathBuf > {
292- // Ensure build once
293307 let program_dir = self . base_path . join ( "complex_types_program" ) ;
294- let mut result = Ok ( ( ) ) ;
308+
295309 COMPILE_COMPLEX_NOPIE . call_once ( || {
296- println ! (
297- "Compiling complex_types_program Non-PIE (ET_EXEC) in {:?}" ,
298- program_dir
299- ) ;
300- let _ = Command :: new ( "make" )
301- . arg ( "clean" )
302- . current_dir ( & program_dir)
303- . output ( ) ;
304- let compile_output = Command :: new ( "make" )
305- . arg ( "complex_types_program_nopie" )
306- . current_dir ( & program_dir)
307- . output ( ) ;
308- match compile_output {
309- Ok ( out) => {
310- if out. status . success ( ) {
311- println ! ( "✓ Successfully compiled complex_types_program Non-PIE" ) ;
312- } else {
313- let stderr = String :: from_utf8_lossy ( & out. stderr ) ;
314- result = Err ( anyhow:: anyhow!(
315- "Failed to compile complex_types_program Non-PIE: {}" ,
316- stderr
317- ) ) ;
318- }
319- }
320- Err ( e) => {
321- result = Err ( anyhow:: anyhow!(
322- "Failed to run make for complex_types_program Non-PIE: {}" ,
323- e
324- ) ) ;
310+ let compile_result = ( || -> anyhow:: Result < ( ) > {
311+ println ! (
312+ "Compiling complex_types_program Non-PIE (ET_EXEC) in {:?}" ,
313+ program_dir
314+ ) ;
315+ let _ = Command :: new ( "make" )
316+ . arg ( "clean" )
317+ . current_dir ( & program_dir)
318+ . output ( ) ;
319+
320+ let output = Command :: new ( "make" )
321+ . arg ( "complex_types_program_nopie" )
322+ . current_dir ( & program_dir)
323+ . output ( )
324+ . map_err ( |e| anyhow:: anyhow!( "Failed to run make for complex_types_program Non-PIE: {}" , e) ) ?;
325+
326+ if output. status . success ( ) {
327+ println ! ( "✓ Successfully compiled complex_types_program Non-PIE" ) ;
328+ Ok ( ( ) )
329+ } else {
330+ let stderr = String :: from_utf8_lossy ( & output. stderr ) ;
331+ Err ( anyhow:: anyhow!(
332+ "Failed to compile complex_types_program Non-PIE: {}" ,
333+ stderr
334+ ) )
325335 }
326- }
336+ } ) ( ) ;
337+
338+ * COMPILE_COMPLEX_NOPIE_RESULT . lock ( ) . unwrap ( ) = Some ( compile_result) ;
327339 } ) ;
328- result?;
340+
341+ // Check compilation result
342+ match COMPILE_COMPLEX_NOPIE_RESULT . lock ( ) . unwrap ( ) . as_ref ( ) {
343+ Some ( Ok ( ( ) ) ) => { } ,
344+ Some ( Err ( e) ) => return Err ( anyhow:: anyhow!( "{}" , e) ) ,
345+ None => panic ! ( "Compilation result should be set after call_once" ) ,
346+ }
347+
329348 let bin_path = program_dir. join ( "complex_types_program_nopie" ) ;
330349 if !bin_path. exists ( ) {
331350 anyhow:: bail!( "Non-PIE binary not found: {}" , bin_path. display( ) ) ;
0 commit comments