Skip to content

Commit 1c7b41a

Browse files
authored
Merge pull request #1050 from pbor/c_abi
codegen: rework ABI tests to use a single C program for each test
2 parents 7edc5fb + 11538b2 commit 1c7b41a

File tree

1 file changed

+138
-141
lines changed

1 file changed

+138
-141
lines changed

src/codegen/sys/tests.rs

Lines changed: 138 additions & 141 deletions
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,12 @@ pub fn generate(env: &Env, crate_name: &str) {
4646

4747
let layout_c = tests.join("layout.c");
4848
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)
5050
});
5151

5252
let constant_c = tests.join("constant.c");
5353
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)
5555
});
5656

5757
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<()
202202
}
203203

204204
#[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<()> {
206211
info!("Generating file {:?}", path);
207212
general::start_comments(w, &env.config)?;
208213
writeln!(w)?;
209214
writeln!(w, "#include \"manual.h\"")?;
210215
writeln!(w, "#include <stdalign.h>")?;
211216
writeln!(w, "#include <stdio.h>")?;
217+
writeln!(w)?;
218+
writeln!(w, "{}", r"int main() {")?;
212219

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"}")
222230
}
223231

224232
#[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<()> {
226239
info!("Generating file {:?}", path);
227240
general::start_comments(w, &env.config)?;
228241
writeln!(w)?;
229242
writeln!(w, "#include \"manual.h\"")?;
230243
writeln!(w, "#include <stdio.h>")?;
231-
232244
writeln!(
233245
w,
234246
"{}",
235247
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"}")
257279
}
258280

259281
#[allow(clippy::write_literal)]
@@ -295,6 +317,8 @@ impl Compiler {
295317
pub fn new() -> Result<Compiler, Box<dyn Error>> {
296318
let mut args = get_var("CC", "cc")?;
297319
args.push("-Wno-deprecated-declarations".to_owned());
320+
// For _Generic
321+
args.push("-std=c11".to_owned());
298322
// For %z support in printf when using MinGW.
299323
args.push("-D__USE_MINGW_ANSI_STDIO".to_owned());
300324
args.extend(get_var("CFLAGS", "")?);
@@ -303,23 +327,14 @@ impl Compiler {
303327
Ok(Compiler { args })
304328
}
305329
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-
314330
pub fn compile(&self, src: &Path, out: &Path) -> Result<(), Box<dyn Error>> {
315331
let mut cmd = self.to_command();
316332
cmd.arg(src);
317333
cmd.arg("-o");
318334
cmd.arg(out);
319335
let status = cmd.spawn()?.wait()?;
320336
if !status.success() {
321-
return Err(format!("compilation command {:?} failed, {}",
322-
&cmd, status).into());
337+
return Err(format!("compilation command {:?} failed, {}", &cmd, status).into());
323338
}
324339
Ok(())
325340
}
@@ -370,8 +385,6 @@ struct Results {
370385
passed: usize,
371386
/// Total number of failed tests (including those that failed to compile).
372387
failed: usize,
373-
/// Number of tests that failed to compile.
374-
failed_to_compile: usize,
375388
}
376389
377390
impl Results {
@@ -381,16 +394,8 @@ impl Results {
381394
fn record_failed(&mut self) {
382395
self.failed += 1;
383396
}
384-
fn record_failed_to_compile(&mut self) {
385-
self.failed += 1;
386-
self.failed_to_compile += 1;
387-
}
388397
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)
394399
}
395400
fn expect_total_success(&self) {
396401
if self.failed == 0 {
@@ -403,118 +408,110 @@ impl Results {
403408
404409
#[test]
405410
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+
}
408422
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;
432432
}
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();
433444
}
445+
434446
results.expect_total_success();
435447
}
436448
437449
#[test]
438450
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+
}
441466
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;
465476
}
466-
}
467-
results.expect_total_success();
468-
}
469477
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+
}
475486
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();
481488
}
482489
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();
488491
}
489492
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)?;
495500
496501
let mut abi_cmd = Command::new(exe);
497502
let output = abi_cmd.output()?;
498503
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());
508505
}
509506
510-
Ok(String::from(&output[14..(output.len() - 14)]))
507+
Ok(String::from_utf8(output.stdout)?)
511508
}
512509
513510
const RUST_LAYOUTS: &[(&str, Layout)] = &["####
514511
)?;
515512
for ctype in ctypes {
516513
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}>()}}),",
518515
ctype=ctype.name)?;
519516
}
520517
writeln!(
@@ -527,7 +524,7 @@ const RUST_CONSTANTS: &[(&str, &str)] = &["##
527524
for cconst in cconsts {
528525
writeln!(
529526
w,
530-
"\t(\"{name}\", \"{value}\"),",
527+
" (\"{name}\", \"{value}\"),",
531528
name = cconst.name,
532529
value = &general::escape_string(&cconst.value)
533530
)?;

0 commit comments

Comments
 (0)