libc-rs/libc-test/build.rs

426 lines
14 KiB
Rust
Raw Normal View History

2015-09-10 07:46:19 +02:00
extern crate gcc;
extern crate syntex_syntax as syntax;
2015-09-10 19:56:31 +02:00
use std::collections::HashSet;
2015-09-10 07:46:19 +02:00
use std::env;
use std::fs::File;
use std::io::BufWriter;
use std::io::prelude::*;
2015-09-11 01:35:37 +02:00
use std::iter;
2015-09-10 07:46:19 +02:00
use std::path::{Path, PathBuf};
use syntax::ast;
2015-09-10 08:21:27 +02:00
use syntax::diagnostic::SpanHandler;
2015-09-10 07:46:19 +02:00
use syntax::parse::token::InternedString;
2015-09-10 08:21:27 +02:00
use syntax::attr::{self, ReprAttr};
2015-09-10 07:46:19 +02:00
use syntax::parse::{self, ParseSess};
use syntax::visit::{self, Visitor};
2015-09-10 19:56:31 +02:00
macro_rules! t {
($e:expr) => (match $e {
Ok(e) => e,
Err(e) => panic!("{} failed with {}", stringify!($e), e),
})
}
2015-09-10 08:21:27 +02:00
struct TestGenerator<'a> {
2015-09-10 19:56:31 +02:00
target: String,
2015-09-10 07:46:19 +02:00
rust: Box<Write>,
c: Box<Write>,
2015-09-10 08:21:27 +02:00
sh: &'a SpanHandler,
2015-09-10 19:56:31 +02:00
structs: HashSet<String>,
2015-09-10 07:46:19 +02:00
}
2015-09-10 19:56:31 +02:00
struct StructFinder {
structs: HashSet<String>,
}
impl<'a> TestGenerator<'a> {
2015-09-11 01:35:37 +02:00
fn defines(&self) -> Vec<&'static str> {
let mut ret = Vec::new();
if self.target.contains("unknown-linux") {
ret.push("_GNU_SOURCE");
}
2015-09-11 02:21:20 +02:00
if self.target.contains("msvc") {
ret.push("alignof __alignof");
}
2015-09-11 01:35:37 +02:00
return ret
}
2015-09-10 19:56:31 +02:00
fn headers(&self) -> Vec<&'static str> {
2015-09-11 02:21:20 +02:00
let mut base = Vec::new();
base.extend(&[
2015-09-11 01:35:37 +02:00
"errno.h",
"fcntl.h",
"limits.h",
2015-09-10 19:56:31 +02:00
"stddef.h",
"stdint.h",
2015-09-11 01:35:37 +02:00
"stdio.h",
"stdlib.h",
2015-09-10 19:56:31 +02:00
"sys/stat.h",
"sys/types.h",
"time.h",
"wchar.h",
2015-09-11 02:21:20 +02:00
]);
2015-09-10 19:56:31 +02:00
if self.target.contains("apple-darwin") {
base.push("mach/mach_time.h");
}
2015-09-11 02:21:20 +02:00
2015-09-10 19:56:31 +02:00
if self.target.contains("unknown-linux") {
base.push("linux/if_packet.h");
base.push("net/ethernet.h");
}
2015-09-10 07:46:19 +02:00
2015-09-11 02:21:20 +02:00
if self.target.contains("windows") {
2015-09-11 03:10:58 +02:00
base.push("winsock2.h");
base.push("ws2ipdef.h");
base.push("windows.h");
base.push("sys/utime.h");
2015-09-11 02:21:20 +02:00
} else {
base.push("glob.h");
base.push("ifaddrs.h");
base.push("net/if.h");
base.push("netdb.h");
base.push("netinet/in.h");
base.push("netinet/ip.h");
base.push("netinet/tcp.h");
base.push("pthread.h");
base.push("signal.h");
base.push("stdalign.h");
base.push("sys/mman.h");
base.push("sys/resource.h");
base.push("sys/socket.h");
base.push("sys/time.h");
base.push("sys/un.h");
base.push("utime.h");
base.push("unistd.h");
}
2015-09-10 19:56:31 +02:00
return base
}
fn rust2c(&self, ty: &str) -> String {
2015-09-11 03:10:58 +02:00
let windows = self.target.contains("windows");
2015-09-10 19:56:31 +02:00
match ty {
t if t.starts_with("c_") => {
match &ty[2..].replace("long", " long")[..] {
s if s.starts_with("u") => format!("unsigned {}", &s[1..]),
"short" => format!("short"),
s if s.starts_with("s") => format!("signed {}", &s[1..]),
s => s.to_string(),
}
}
"ip6_mreq" => "struct ipv6_mreq".to_string(),
"glob_t" => "glob_t".to_string(),
t if t.starts_with("pthread") => t.to_string(),
2015-09-11 03:10:58 +02:00
t if self.structs.contains(t) => {
if windows && ty.chars().next().unwrap().is_uppercase() {
t.to_string()
} else if windows && t == "stat" {
"struct __stat64".to_string()
} else {
format!("struct {}", t)
}
}
2015-09-10 19:56:31 +02:00
2015-09-11 03:10:58 +02:00
"time64_t" if windows => "__time64_t".to_string(),
"ssize_t" if windows => "SSIZE_T".to_string(),
2015-09-10 19:56:31 +02:00
t => t.to_string(),
}
}
fn rust2cfield(&self, struct_: &str, field: &str) -> String {
match field {
s if s.ends_with("_nsec") && struct_ == "stat" => {
if self.target.contains("apple-darwin") {
s.replace("_nsec", "spec.tv_nsec")
} else {
s.replace("e_nsec", ".tv_nsec")
}
}
s => s.to_string(),
}
}
fn cfg_list(&self) -> Vec<(&'static str, Option<&'static str>)> {
let mut ret = Vec::new();
let (arch, target_pointer_width) = if self.target.starts_with("x86_64") {
("x86_64", "64")
} else if self.target.starts_with("i686") {
("x86", "32")
} else {
panic!("unknown arch/pointer width: {}", self.target)
};
let (os, family, env) = if self.target.contains("unknown-linux") {
("linux", "unix", "gnu")
} else if self.target.contains("apple-darwin") {
("macos", "unix", "")
2015-09-11 02:21:20 +02:00
} else if self.target.contains("windows-msvc") {
("windows", "windows", "msvc")
} else if self.target.contains("windows-gnu") {
("windows", "windows", "gnu")
2015-09-10 19:56:31 +02:00
} else {
panic!("unknown os/family width: {}", self.target)
};
ret.push((family, None));
ret.push(("target_os", Some(os)));
ret.push(("target_family", Some(family)));
ret.push(("target_arch", Some(arch)));
// skip endianness
ret.push(("target_pointer_width", Some(target_pointer_width)));
ret.push(("target_env", Some(env)));
return ret
}
}
fn main() {
// Prep the test generator
let target = t!(env::var("TARGET"));
let out = PathBuf::from(env::var_os("OUT_DIR").unwrap());
let rust_out = BufWriter::new(t!(File::create(out.join("all.rs"))));
let c_out = BufWriter::new(t!(File::create(out.join("all.c"))));
let sess = ParseSess::new();
2015-09-10 19:56:31 +02:00
let mut tg = TestGenerator {
target: target,
rust: Box::new(rust_out),
c: Box::new(c_out),
sh: &sess.span_diagnostic,
structs: HashSet::new(),
};
// Parse the libc crate
2015-09-10 07:46:19 +02:00
let src = Path::new("../src/lib.rs");
let cfg = Vec::new();
let mut krate = parse::parse_crate_from_file(src, cfg, &sess);
2015-09-10 19:56:31 +02:00
// Strip the crate down to just what's configured for our target
for (k, v) in tg.cfg_list() {
let s = InternedString::new;
krate.config.push(match v {
Some(v) => attr::mk_name_value_item_str(s(k), s(v)),
None => attr::mk_word_item(s(k)),
});
}
2015-09-10 07:46:19 +02:00
let mut gated_cfgs = Vec::new();
let krate = syntax::config::strip_unconfigured_items(&sess.span_diagnostic,
krate,
&mut gated_cfgs);
2015-09-10 19:56:31 +02:00
// Probe the crate to find all structs (used to convert type names to names
// in C).
let mut structs = StructFinder {
structs: HashSet::new(),
};
visit::walk_crate(&mut structs, &krate);
tg.structs = structs.structs;
2015-09-10 07:46:19 +02:00
2015-09-11 01:35:37 +02:00
// Prep the C file by emitting header stuff
for define in tg.defines() {
t!(writeln!(tg.c, "#define {}", define));
}
2015-09-10 19:56:31 +02:00
for header in tg.headers() {
t!(writeln!(tg.c, "#include <{}>", header));
}
2015-09-11 01:35:37 +02:00
// Walk the crate, emitting test cases for everything found
2015-09-10 19:56:31 +02:00
visit::walk_crate(&mut tg, &krate);
2015-09-10 07:46:19 +02:00
2015-09-10 19:56:31 +02:00
// Compile our C shim to be linked into tests
2015-09-11 02:21:20 +02:00
let mut cfg = gcc::Config::new();
cfg.file(out.join("all.c"));
if tg.target.contains("msvc") {
2015-09-11 03:10:58 +02:00
cfg.flag("/W3").flag("/Wall").flag("/WX")
.flag("/wd4820") // weird warning about adding padding?
.flag("/wd4100"); // don't warn about unused parameters
2015-09-11 02:21:20 +02:00
} else {
2015-09-11 02:37:22 +02:00
cfg.flag("-Wall").flag("-Wextra").flag("-Werror")
2015-09-11 02:39:21 +02:00
.flag("-Wno-unused-parameter");
2015-09-11 02:21:20 +02:00
}
drop(tg);
cfg.compile("liball.a");
2015-09-10 07:46:19 +02:00
}
2015-09-10 08:21:27 +02:00
impl<'a> TestGenerator<'a> {
fn test_type(&mut self, ty: &str) {
2015-09-10 19:56:31 +02:00
match ty {
"sighandler_t" => return,
_ => {}
}
let c = self.rust2c(ty);
self.test_size_align(ty, &c);
2015-09-10 08:21:27 +02:00
}
2015-09-10 08:46:19 +02:00
fn test_struct(&mut self, ty: &str, s: &ast::StructDef) {
2015-09-10 19:56:31 +02:00
let cty = self.rust2c(ty);
2015-09-10 08:21:27 +02:00
self.test_size_align(ty, &cty);
2015-09-10 08:46:19 +02:00
2015-09-10 19:56:31 +02:00
t!(writeln!(self.rust, r#"
2015-09-10 08:46:19 +02:00
#[test]
fn field_offset_size_{ty}() {{
2015-09-10 19:56:31 +02:00
"#, ty = ty));
2015-09-10 08:46:19 +02:00
for field in s.fields.iter() {
let name = match field.node.kind {
ast::NamedField(name, ast::Public) => name,
ast::NamedField(_, ast::Inherited) => continue,
ast::UnnamedField(..) => panic!("no tuple structs in FFI"),
};
2015-09-10 19:56:31 +02:00
let cfield = self.rust2cfield(ty, &name.to_string());
2015-09-10 08:46:19 +02:00
2015-09-10 19:56:31 +02:00
t!(writeln!(self.c, r#"
2015-09-11 02:39:03 +02:00
uint64_t __test_offset_{ty}_{rust_field}(void) {{
2015-09-10 08:46:19 +02:00
return offsetof({cty}, {c_field});
}}
2015-09-11 02:39:03 +02:00
uint64_t __test_size_{ty}_{rust_field}(void) {{
2015-09-10 08:46:19 +02:00
{cty}* foo = NULL;
return sizeof(foo->{c_field});
}}
2015-09-10 19:56:31 +02:00
"#, ty = ty, cty = cty, rust_field = name, c_field = cfield));
t!(writeln!(self.rust, r#"
2015-09-10 08:46:19 +02:00
extern {{
fn __test_offset_{ty}_{field}() -> u64;
fn __test_size_{ty}_{field}() -> u64;
}}
unsafe {{
let foo = 0 as *const {ty};
same(offset_of!({ty}, {field}),
__test_offset_{ty}_{field}(),
"field offset {field} of {ty}");
same(mem::size_of_val(&(*foo).{field}) as u64,
__test_size_{ty}_{field}(),
"field size {field} of {ty}");
}}
2015-09-10 19:56:31 +02:00
"#, ty = ty, field = name));
2015-09-10 08:46:19 +02:00
}
2015-09-10 19:56:31 +02:00
t!(writeln!(self.rust, r#"
2015-09-10 08:46:19 +02:00
}}
2015-09-10 19:56:31 +02:00
"#));
2015-09-10 08:21:27 +02:00
}
2015-09-10 08:21:27 +02:00
fn test_size_align(&mut self, rust: &str, c: &str) {
2015-09-10 19:56:31 +02:00
t!(writeln!(self.c, r#"
2015-09-11 02:39:03 +02:00
uint64_t __test_size_{ty}(void) {{ return sizeof({cty}); }}
uint64_t __test_align_{ty}(void) {{ return alignof({cty}); }}
2015-09-10 19:56:31 +02:00
"#, ty = rust, cty = c));
t!(writeln!(self.rust, r#"
2015-09-10 07:46:19 +02:00
#[test]
2015-09-10 08:21:27 +02:00
fn size_align_{ty}() {{
extern {{
2015-09-10 08:21:27 +02:00
fn __test_size_{ty}() -> u64;
fn __test_align_{ty}() -> u64;
}}
unsafe {{
2015-09-10 08:46:19 +02:00
same(mem::size_of::<{ty}>() as u64,
__test_size_{ty}(), "size");
2015-09-10 22:24:15 +02:00
same(align::<{ty}>() as u64,
2015-09-10 08:46:19 +02:00
__test_align_{ty}(), "align");
}}
2015-09-10 07:46:19 +02:00
}}
2015-09-10 19:56:31 +02:00
"#, ty = rust));
2015-09-10 08:21:27 +02:00
}
2015-09-11 01:35:37 +02:00
fn test_const(&mut self, name: &str, rust_ty: &str) {
let cty = self.rust2c(&rust_ty.replace("*mut ", "")
.replace("*const ", ""));
let ptrs = rust_ty.matches("*").count();
let cty = format!("{}{}", cty,
iter::repeat("*").take(ptrs).collect::<String>());
let cast = if name == "SIG_IGN" {"(size_t)"} else {""};
t!(writeln!(self.c, r#"
2015-09-11 03:10:58 +02:00
int __test_const_{name}({cty} *outptr) {{
*outptr = {cast}({name});
return 1;
2015-09-11 02:15:20 +02:00
}}
2015-09-11 01:35:37 +02:00
"#, name = name, cast = cast, cty = cty));
t!(writeln!(self.rust, r#"
#[test]
fn const_{name}() {{
extern {{
2015-09-11 02:15:20 +02:00
fn __test_const_{name}(out: *mut {ty}) -> c_int;
2015-09-11 01:35:37 +02:00
}}
unsafe {{
2015-09-11 02:15:20 +02:00
let mut o = mem::zeroed();
if __test_const_{name}(&mut o) == 0 {{
panic!("not defined");
}} else {{
same({name}, o, "value");
}}
2015-09-11 01:35:37 +02:00
}}
}}
"#, ty = rust_ty, name = name));
}
2015-09-10 08:21:27 +02:00
fn assert_no_generics(&self, _i: &ast::Item, generics: &ast::Generics) {
assert!(generics.lifetimes.len() == 0);
assert!(generics.ty_params.len() == 0);
assert!(generics.where_clause.predicates.len() == 0);
2015-09-10 07:46:19 +02:00
}
2015-09-11 01:35:37 +02:00
fn ty2name(&self, ty: &ast::Ty) -> String {
match ty.node {
ast::TyPath(_, ref path) => {
path.segments.last().unwrap().identifier.to_string()
}
ast::TyPtr(ref t) => {
format!("*{} {}", match t.mutbl {
ast::MutImmutable => "const",
ast::MutMutable => "mut",
}, self.ty2name(&t.ty))
}
_ => panic!("unknown ty {:?}", ty),
}
}
2015-09-10 07:46:19 +02:00
}
2015-09-10 08:21:27 +02:00
impl<'a, 'v> Visitor<'v> for TestGenerator<'a> {
2015-09-10 07:46:19 +02:00
fn visit_item(&mut self, i: &'v ast::Item) {
match i.node {
ast::ItemTy(_, ref generics) => {
2015-09-10 08:21:27 +02:00
self.assert_no_generics(i, generics);
self.test_type(&i.ident.to_string());
}
ast::ItemStruct(ref s, ref generics) => {
self.assert_no_generics(i, generics);
let is_c = i.attrs.iter().any(|a| {
attr::find_repr_attrs(self.sh, a).iter().any(|a| {
*a == ReprAttr::ReprExtern
})
});
if !is_c {
panic!("{} is not marked #[repr(C)]", i.ident);
}
self.test_struct(&i.ident.to_string(), s);
2015-09-10 07:46:19 +02:00
}
2015-09-11 01:35:37 +02:00
ast::ItemConst(ref ty, _) => {
let ty = self.ty2name(ty);
self.test_const(&i.ident.to_string(), &ty);
}
2015-09-10 07:46:19 +02:00
_ => {}
}
visit::walk_item(self, i)
}
}
2015-09-10 19:56:31 +02:00
impl<'v> Visitor<'v> for StructFinder {
fn visit_item(&mut self, i: &'v ast::Item) {
match i.node {
ast::ItemStruct(..) => {
self.structs.insert(i.ident.to_string());
}
_ => {}
}
visit::walk_item(self, i)
}
}