Tweak script, fix on linux

This commit is contained in:
Alex Crichton 2015-09-10 10:56:31 -07:00
parent f522b139f8
commit 310d623acf

View File

@ -1,8 +1,7 @@
#![allow(unused_must_use)]
extern crate gcc;
extern crate syntex_syntax as syntax;
use std::collections::HashSet;
use std::env;
use std::fs::File;
use std::io::BufWriter;
@ -16,127 +15,195 @@ use syntax::attr::{self, ReprAttr};
use syntax::parse::{self, ParseSess};
use syntax::visit::{self, Visitor};
macro_rules! t {
($e:expr) => (match $e {
Ok(e) => e,
Err(e) => panic!("{} failed with {}", stringify!($e), e),
})
}
struct TestGenerator<'a> {
target: String,
rust: Box<Write>,
c: Box<Write>,
sh: &'a SpanHandler,
structs: HashSet<String>,
}
struct StructFinder {
structs: HashSet<String>,
}
impl<'a> TestGenerator<'a> {
fn headers(&self) -> Vec<&'static str> {
let mut base = vec![
"glob.h",
"ifaddrs.h",
"netdb.h",
"netinet/in.h",
"netinet/ip.h",
"pthread.h",
"signal.h",
"stdalign.h",
"stddef.h",
"stdint.h",
"sys/resource.h",
"sys/socket.h",
"sys/stat.h",
"sys/time.h",
"sys/types.h",
"sys/un.h",
"time.h",
"unistd.h",
"utime.h",
"wchar.h",
];
if self.target.contains("apple-darwin") {
base.push("mach/mach_time.h");
}
if self.target.contains("unknown-linux") {
base.push("linux/if_packet.h");
base.push("net/ethernet.h");
}
return base
}
fn rust2c(&self, ty: &str) -> String {
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(),
t if self.structs.contains(t) => format!("struct {}", t),
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", "")
} 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() {
let target = env::var("TARGET").unwrap();
// 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();
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
let src = Path::new("../src/lib.rs");
let cfg = Vec::new();
let mut krate = parse::parse_crate_from_file(src, cfg, &sess);
build_cfg(&mut krate.config, &target);
// 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)),
});
}
let mut gated_cfgs = Vec::new();
let krate = syntax::config::strip_unconfigured_items(&sess.span_diagnostic,
krate,
&mut gated_cfgs);
let out = PathBuf::from(env::var_os("OUT_DIR").unwrap());
let rust_out = BufWriter::new(File::create(out.join("all.rs")).unwrap());
let mut c_out = BufWriter::new(File::create(out.join("all.c")).unwrap());
// 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;
writeln!(c_out, "
#include <glob.h>
#include <ifaddrs.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <pthread.h>
#include <signal.h>
#include <stdalign.h>
#include <stddef.h>
#include <stdint.h>
#include <sys/resource.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/un.h>
#include <time.h>
#include <utime.h>
#include <wchar.h>
");
if target.contains("apple-darwin") {
writeln!(c_out, "
#include <mach/mach_time.h>
");
// Walk the crate, emitting test cases for everything found
for header in tg.headers() {
t!(writeln!(tg.c, "#include <{}>", header));
}
visit::walk_crate(&mut tg, &krate);
drop(tg);
visit::walk_crate(&mut TestGenerator {
rust: Box::new(rust_out),
c: Box::new(c_out),
sh: &sess.span_diagnostic,
}, &krate);
// Compile our C shim to be linked into tests
gcc::Config::new()
.file(out.join("all.c"))
.compile("liball.a");
}
fn build_cfg(cfg: &mut ast::CrateConfig, target: &str) {
let (arch, target_pointer_width) = if target.starts_with("x86_64") {
("x86_64", "64")
} else if target.starts_with("i686") {
("x86", "32")
} else {
panic!("unknown arch/pointer width: {}", target)
};
let (os, family, env) = if target.contains("unknown-linux") {
("linux", "unix", "gnu")
} else if target.contains("apple-darwin") {
("macos", "unix", "")
} else {
panic!("unknown os/family width: {}", target)
};
let mk = attr::mk_name_value_item_str;
let s = InternedString::new;
cfg.push(attr::mk_word_item(s(family)));
cfg.push(mk(s("target_os"), s(os)));
cfg.push(mk(s("target_family"), s(family)));
cfg.push(mk(s("target_arch"), s(arch)));
// skip endianness
cfg.push(mk(s("target_pointer_width"), s(target_pointer_width)));
cfg.push(mk(s("target_env"), s(env)));
}
impl<'a> TestGenerator<'a> {
fn test_type(&mut self, ty: &str) {
let cty = if ty.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(),
}
} else {
(match ty {
"sighandler_t" => return,
ty => ty,
}).to_string()
};
self.test_size_align(ty, &cty);
match ty {
"sighandler_t" => return,
_ => {}
}
let c = self.rust2c(ty);
self.test_size_align(ty, &c);
}
fn test_struct(&mut self, ty: &str, s: &ast::StructDef) {
let cty = match ty {
t if ty.starts_with("pthread") => t.to_string(),
"glob_t" => "glob_t".to_string(),
"ip6_mreq" => "struct ipv6_mreq".to_string(),
s => format!("struct {}", s),
};
let cty = self.rust2c(ty);
self.test_size_align(ty, &cty);
writeln!(self.rust, r#"
t!(writeln!(self.rust, r#"
#[test]
fn field_offset_size_{ty}() {{
"#, ty = ty);
"#, ty = ty));
for field in s.fields.iter() {
let name = match field.node.kind {
ast::NamedField(name, ast::Public) => name,
@ -144,14 +211,9 @@ impl<'a> TestGenerator<'a> {
ast::UnnamedField(..) => panic!("no tuple structs in FFI"),
};
let cname = match &name.to_string()[..] {
s if s.ends_with("_nsec") && ty == "stat" => {
s.replace("_nsec", "spec.tv_nsec")
}
s => s.to_string(),
};
let cfield = self.rust2cfield(ty, &name.to_string());
writeln!(self.c, r#"
t!(writeln!(self.c, r#"
uint64_t __test_offset_{ty}_{rust_field}() {{
return offsetof({cty}, {c_field});
}}
@ -159,8 +221,8 @@ impl<'a> TestGenerator<'a> {
{cty}* foo = NULL;
return sizeof(foo->{c_field});
}}
"#, ty = ty, cty = cty, rust_field = name, c_field = cname);
writeln!(self.rust, r#"
"#, ty = ty, cty = cty, rust_field = name, c_field = cfield));
t!(writeln!(self.rust, r#"
extern {{
fn __test_offset_{ty}_{field}() -> u64;
fn __test_size_{ty}_{field}() -> u64;
@ -174,19 +236,19 @@ impl<'a> TestGenerator<'a> {
__test_size_{ty}_{field}(),
"field size {field} of {ty}");
}}
"#, ty = ty, field = name);
"#, ty = ty, field = name));
}
writeln!(self.rust, r#"
t!(writeln!(self.rust, r#"
}}
"#);
"#));
}
fn test_size_align(&mut self, rust: &str, c: &str) {
writeln!(self.c, r#"
t!(writeln!(self.c, r#"
uint64_t __test_size_{ty}() {{ return sizeof({cty}); }}
uint64_t __test_align_{ty}() {{ return alignof({cty}); }}
"#, ty = rust, cty = c);
writeln!(self.rust, r#"
"#, ty = rust, cty = c));
t!(writeln!(self.rust, r#"
#[test]
fn size_align_{ty}() {{
extern {{
@ -200,7 +262,7 @@ impl<'a> TestGenerator<'a> {
__test_align_{ty}(), "align");
}}
}}
"#, ty = rust);
"#, ty = rust));
}
fn assert_no_generics(&self, _i: &ast::Item, generics: &ast::Generics) {
@ -236,3 +298,16 @@ impl<'a, 'v> Visitor<'v> for TestGenerator<'a> {
visit::walk_item(self, i)
}
}
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)
}
}