@@ -46,12 +46,12 @@ pub fn generate(env: &Env, crate_name: &str) {
46
46
47
47
let layout_c = tests. join ( "layout.c" ) ;
48
48
save_to_file ( & layout_c, env. config . make_backup , |w| {
49
- generate_layout_c ( env, & layout_c, w)
49
+ generate_layout_c ( env, & layout_c, w, & ctypes )
50
50
} ) ;
51
51
52
52
let constant_c = tests. join ( "constant.c" ) ;
53
53
save_to_file ( & constant_c, env. config . make_backup , |w| {
54
- generate_constant_c ( env, & constant_c, w)
54
+ generate_constant_c ( env, & constant_c, w, & cconsts )
55
55
} ) ;
56
56
57
57
let abi_rs = tests. join ( "abi.rs" ) ;
@@ -202,58 +202,80 @@ fn generate_manual_h(env: &Env, path: &Path, w: &mut dyn Write) -> io::Result<()
202
202
}
203
203
204
204
#[ allow( clippy:: write_literal) ]
205
- fn generate_layout_c ( env : & Env , path : & Path , w : & mut dyn Write ) -> io:: Result < ( ) > {
205
+ fn generate_layout_c (
206
+ env : & Env ,
207
+ path : & Path ,
208
+ w : & mut dyn Write ,
209
+ ctypes : & [ CType ] ,
210
+ ) -> io:: Result < ( ) > {
206
211
info ! ( "Generating file {:?}" , path) ;
207
212
general:: start_comments ( w, & env. config ) ?;
208
213
writeln ! ( w) ?;
209
214
writeln ! ( w, "#include \" manual.h\" " ) ?;
210
215
writeln ! ( w, "#include <stdalign.h>" ) ?;
211
216
writeln ! ( w, "#include <stdio.h>" ) ?;
217
+ writeln ! ( w) ?;
218
+ writeln ! ( w, "{}" , r"int main() {" ) ?;
212
219
213
- writeln ! (
214
- w,
215
- "{}" ,
216
- r##"
217
- int main() {
218
- printf("%zu\n%zu", sizeof(ABI_TYPE_NAME), alignof(ABI_TYPE_NAME));
219
- return 0;
220
- }"##
221
- )
220
+ for ctype in ctypes {
221
+ writeln ! (
222
+ w,
223
+ " printf(\" %s;%zu;%zu\\ n\" , \" {ctype}\" , sizeof({ctype}), alignof({ctype}));" ,
224
+ ctype = ctype. name
225
+ ) ?;
226
+ }
227
+
228
+ writeln ! ( w, " return 0;" ) ?;
229
+ writeln ! ( w, "{}" , r"}" )
222
230
}
223
231
224
232
#[ allow( clippy:: write_literal) ]
225
- fn generate_constant_c ( env : & Env , path : & Path , w : & mut dyn Write ) -> io:: Result < ( ) > {
233
+ fn generate_constant_c (
234
+ env : & Env ,
235
+ path : & Path ,
236
+ w : & mut dyn Write ,
237
+ cconsts : & [ CConstant ] ,
238
+ ) -> io:: Result < ( ) > {
226
239
info ! ( "Generating file {:?}" , path) ;
227
240
general:: start_comments ( w, & env. config ) ?;
228
241
writeln ! ( w) ?;
229
242
writeln ! ( w, "#include \" manual.h\" " ) ?;
230
243
writeln ! ( w, "#include <stdio.h>" ) ?;
231
-
232
244
writeln ! (
233
245
w,
234
246
"{}" ,
235
247
r####"
236
- int main() {
237
- printf(_Generic((ABI_CONSTANT_NAME),
238
- char *: "###gir test###%s###gir test###\n",
239
- const char *: "###gir test###%s###gir test###\n",
240
- char: "###gir test###%c###gir test###\n",
241
- signed char: "###gir test###%hhd###gir test###\n",
242
- unsigned char: "###gir test###%hhu###gir test###\n",
243
- short int: "###gir test###%hd###gir test###\n",
244
- unsigned short int: "###gir test###%hu###gir test###\n",
245
- int: "###gir test###%d###gir test###\n",
246
- unsigned int: "###gir test###%u###gir test###\n",
247
- long: "###gir test###%ld###gir test###\n",
248
- unsigned long: "###gir test###%lu###gir test###\n",
249
- long long: "###gir test###%lld###gir test###\n",
250
- unsigned long long: "###gir test###%llu###gir test###\n",
251
- double: "###gir test###%f###gir test###\n",
252
- long double: "###gir test###%ld###gir test###\n"),
253
- ABI_CONSTANT_NAME);
254
- return 0;
255
- }"####
256
- )
248
+ #define PRINT_CONSTANT(CONSTANT_NAME) \
249
+ printf("%s;", #CONSTANT_NAME); \
250
+ printf(_Generic((CONSTANT_NAME), \
251
+ char *: "%s", \
252
+ const char *: "%s", \
253
+ char: "%c", \
254
+ signed char: "%hhd", \
255
+ unsigned char: "%hhu", \
256
+ short int: "%hd", \
257
+ unsigned short int: "%hu", \
258
+ int: "%d", \
259
+ unsigned int: "%u", \
260
+ long: "%ld", \
261
+ unsigned long: "%lu", \
262
+ long long: "%lld", \
263
+ unsigned long long: "%llu", \
264
+ double: "%f", \
265
+ long double: "%ld"), \
266
+ CONSTANT_NAME); \
267
+ printf("\n");
268
+ "####
269
+ ) ?;
270
+
271
+ writeln ! ( w, "{}" , r"int main() {" ) ?;
272
+
273
+ for cconst in cconsts {
274
+ writeln ! ( w, " PRINT_CONSTANT({name});" , name = cconst. name, ) ?;
275
+ }
276
+
277
+ writeln ! ( w, " return 0;" ) ?;
278
+ writeln ! ( w, "{}" , r"}" )
257
279
}
258
280
259
281
#[ allow( clippy:: write_literal) ]
@@ -295,6 +317,8 @@ impl Compiler {
295
317
pub fn new() -> Result<Compiler, Box<dyn Error>> {
296
318
let mut args = get_var("CC", "cc")?;
297
319
args.push("-Wno-deprecated-declarations".to_owned());
320
+ // For _Generic
321
+ args.push("-std=c11".to_owned());
298
322
// For %z support in printf when using MinGW.
299
323
args.push("-D__USE_MINGW_ANSI_STDIO".to_owned());
300
324
args.extend(get_var("CFLAGS", "")?);
@@ -303,23 +327,14 @@ impl Compiler {
303
327
Ok(Compiler { args })
304
328
}
305
329
306
- pub fn define<'a, V: Into<Option<&'a str>>>(&mut self, var: &str, val: V) {
307
- let arg = match val.into() {
308
- None => format!("-D{}", var),
309
- Some(val) => format!("-D{}={}", var, val),
310
- };
311
- self.args.push(arg);
312
- }
313
-
314
330
pub fn compile(&self, src: &Path, out: &Path) -> Result<(), Box<dyn Error>> {
315
331
let mut cmd = self.to_command();
316
332
cmd.arg(src);
317
333
cmd.arg("-o");
318
334
cmd.arg(out);
319
335
let status = cmd.spawn()?.wait()?;
320
336
if !status.success() {
321
- return Err(format!("compilation command {:?} failed, {}",
322
- &cmd, status).into());
337
+ return Err(format!("compilation command {:?} failed, {}", &cmd, status).into());
323
338
}
324
339
Ok(())
325
340
}
@@ -370,8 +385,6 @@ struct Results {
370
385
passed: usize,
371
386
/// Total number of failed tests (including those that failed to compile).
372
387
failed: usize,
373
- /// Number of tests that failed to compile.
374
- failed_to_compile: usize,
375
388
}
376
389
377
390
impl Results {
@@ -381,16 +394,8 @@ impl Results {
381
394
fn record_failed(&mut self) {
382
395
self.failed += 1;
383
396
}
384
- fn record_failed_to_compile(&mut self) {
385
- self.failed += 1;
386
- self.failed_to_compile += 1;
387
- }
388
397
fn summary(&self) -> String {
389
- format!(
390
- "{} passed; {} failed (compilation errors: {})",
391
- self.passed,
392
- self.failed,
393
- self.failed_to_compile)
398
+ format!("{} passed; {} failed", self.passed, self.failed)
394
399
}
395
400
fn expect_total_success(&self) {
396
401
if self.failed == 0 {
@@ -403,118 +408,110 @@ impl Results {
403
408
404
409
#[test]
405
410
fn cross_validate_constants_with_c() {
406
- let tmpdir = Builder::new().prefix("abi").tempdir().expect("temporary directory");
407
- let cc = Compiler::new().expect("configured compiler");
411
+ let mut c_constants: Vec<(String, String)> = Vec::new();
412
+
413
+ for l in get_c_output("constant").unwrap().lines() {
414
+ let mut words = l.trim().split(";");
415
+ let name = words.next().expect("Failed to parse name").to_owned();
416
+ let value = words
417
+ .next()
418
+ .and_then(|s| s.parse().ok())
419
+ .expect("Failed to parse value");
420
+ c_constants.push((name, value));
421
+ }
408
422
409
- assert_eq!("1",
410
- get_c_value(tmpdir.path(), &cc, "1").expect("C constant"),
411
- "failed to obtain correct constant value for 1");
412
-
413
- let mut results : Results = Default::default();
414
- for (i, &(name, rust_value)) in RUST_CONSTANTS.iter().enumerate() {
415
- match get_c_value(tmpdir.path(), &cc, name) {
416
- Err(e) => {
417
- results.record_failed_to_compile();
418
- eprintln!("{}", e);
419
- },
420
- Ok(ref c_value) => {
421
- if rust_value == c_value {
422
- results.record_passed();
423
- } else {
424
- results.record_failed();
425
- eprintln!("Constant value mismatch for {}\nRust: {:?}\nC: {:?}",
426
- name, rust_value, c_value);
427
- }
428
- }
429
- };
430
- if (i + 1) % 25 == 0 {
431
- println!("constants ... {}", results.summary());
423
+ let mut results = Results::default();
424
+
425
+ for ((rust_name, rust_value), (c_name, c_value)) in
426
+ RUST_CONSTANTS.iter().zip(c_constants.iter())
427
+ {
428
+ if rust_name != c_name {
429
+ results.record_failed();
430
+ eprintln!("Name mismatch:\nRust: {:?}\nC: {:?}", rust_name, c_name,);
431
+ continue;
432
432
}
433
+
434
+ if rust_value != c_value {
435
+ results.record_failed();
436
+ eprintln!(
437
+ "Constant value mismatch for {}\nRust: {:?}\nC: {:?}",
438
+ rust_name, rust_value, &c_value
439
+ );
440
+ continue;
441
+ }
442
+
443
+ results.record_passed();
433
444
}
445
+
434
446
results.expect_total_success();
435
447
}
436
448
437
449
#[test]
438
450
fn cross_validate_layout_with_c() {
439
- let tmpdir = Builder::new().prefix("abi").tempdir().expect("temporary directory");
440
- let cc = Compiler::new().expect("configured compiler");
451
+ let mut c_layouts = Vec::new();
452
+
453
+ for l in get_c_output("layout").unwrap().lines() {
454
+ let mut words = l.trim().split(";");
455
+ let name = words.next().expect("Failed to parse name").to_owned();
456
+ let size = words
457
+ .next()
458
+ .and_then(|s| s.parse().ok())
459
+ .expect("Failed to parse size");
460
+ let alignment = words
461
+ .next()
462
+ .and_then(|s| s.parse().ok())
463
+ .expect("Failed to parse alignment");
464
+ c_layouts.push((name, Layout { size, alignment }));
465
+ }
441
466
442
- assert_eq!(Layout {size: 1, alignment: 1},
443
- get_c_layout(tmpdir.path(), &cc, "char").expect("C layout"),
444
- "failed to obtain correct layout for char type");
445
-
446
- let mut results : Results = Default::default();
447
- for (i, &(name, rust_layout)) in RUST_LAYOUTS.iter().enumerate() {
448
- match get_c_layout(tmpdir.path(), &cc, name) {
449
- Err(e) => {
450
- results.record_failed_to_compile();
451
- eprintln!("{}", e);
452
- },
453
- Ok(c_layout) => {
454
- if rust_layout == c_layout {
455
- results.record_passed();
456
- } else {
457
- results.record_failed();
458
- eprintln!("Layout mismatch for {}\nRust: {:?}\nC: {:?}",
459
- name, rust_layout, &c_layout);
460
- }
461
- }
462
- };
463
- if (i + 1) % 25 == 0 {
464
- println!("layout ... {}", results.summary());
467
+ let mut results = Results::default();
468
+
469
+ for ((rust_name, rust_layout), (c_name, c_layout)) in
470
+ RUST_LAYOUTS.iter().zip(c_layouts.iter())
471
+ {
472
+ if rust_name != c_name {
473
+ results.record_failed();
474
+ eprintln!("Name mismatch:\nRust: {:?}\nC: {:?}", rust_name, c_name,);
475
+ continue;
465
476
}
466
- }
467
- results.expect_total_success();
468
- }
469
477
470
- fn get_c_layout(dir: &Path, cc: &Compiler, name: &str) -> Result<Layout, Box<dyn Error>> {
471
- let exe = dir.join("layout");
472
- let mut cc = cc.clone();
473
- cc.define("ABI_TYPE_NAME", name);
474
- cc.compile(Path::new("tests/layout.c"), &exe)?;
478
+ if rust_layout != c_layout {
479
+ results.record_failed();
480
+ eprintln!(
481
+ "Layout mismatch for {}\nRust: {:?}\nC: {:?}",
482
+ rust_name, rust_layout, &c_layout
483
+ );
484
+ continue;
485
+ }
475
486
476
- let mut abi_cmd = Command::new(exe);
477
- let output = abi_cmd.output()?;
478
- if !output.status.success() {
479
- return Err(format!("command {:?} failed, {:?}",
480
- &abi_cmd, &output).into());
487
+ results.record_passed();
481
488
}
482
489
483
- let stdout = str::from_utf8(&output.stdout)?;
484
- let mut words = stdout.trim().split_whitespace();
485
- let size = words.next().unwrap().parse().unwrap();
486
- let alignment = words.next().unwrap().parse().unwrap();
487
- Ok(Layout {size, alignment})
490
+ results.expect_total_success();
488
491
}
489
492
490
- fn get_c_value(dir: &Path, cc: &Compiler, name: &str) -> Result<String, Box<dyn Error>> {
491
- let exe = dir.join("constant");
492
- let mut cc = cc.clone();
493
- cc.define("ABI_CONSTANT_NAME", name);
494
- cc.compile(Path::new("tests/constant.c"), &exe)?;
493
+ fn get_c_output(name: &str) -> Result<String, Box<dyn Error>> {
494
+ let tmpdir = Builder::new().prefix("abi").tempdir()?;
495
+ let exe = tmpdir.path().join(name);
496
+ let c_file = Path::new("tests").join(name).with_extension("c");
497
+
498
+ let cc = Compiler::new().expect("configured compiler");
499
+ cc.compile(&c_file, &exe)?;
495
500
496
501
let mut abi_cmd = Command::new(exe);
497
502
let output = abi_cmd.output()?;
498
503
if !output.status.success() {
499
- return Err(format!("command {:?} failed, {:?}",
500
- &abi_cmd, &output).into());
501
- }
502
-
503
- let output = str::from_utf8(&output.stdout)?.trim();
504
- if !output.starts_with("###gir test###") ||
505
- !output.ends_with("###gir test###") {
506
- return Err(format!("command {:?} return invalid output, {:?}",
507
- &abi_cmd, &output).into());
504
+ return Err(format!("command {:?} failed, {:?}", &abi_cmd, &output).into());
508
505
}
509
506
510
- Ok(String::from(& output[14..(output.len() - 14)]) )
507
+ Ok(String::from_utf8( output.stdout)? )
511
508
}
512
509
513
510
const RUST_LAYOUTS: &[(&str, Layout)] = &["####
514
511
) ?;
515
512
for ctype in ctypes {
516
513
general:: cfg_condition ( w, & ctype. cfg_condition , false , 1 ) ?;
517
- writeln ! ( w, "\t (\" {ctype}\" , Layout {{size: size_of::<{ctype}>(), alignment: align_of::<{ctype}>()}})," ,
514
+ writeln ! ( w, " (\" {ctype}\" , Layout {{size: size_of::<{ctype}>(), alignment: align_of::<{ctype}>()}})," ,
518
515
ctype=ctype. name) ?;
519
516
}
520
517
writeln ! (
@@ -527,7 +524,7 @@ const RUST_CONSTANTS: &[(&str, &str)] = &["##
527
524
for cconst in cconsts {
528
525
writeln ! (
529
526
w,
530
- "\t (\" {name}\" , \" {value}\" )," ,
527
+ " (\" {name}\" , \" {value}\" )," ,
531
528
name = cconst. name,
532
529
value = & general:: escape_string( & cconst. value)
533
530
) ?;
0 commit comments