2015-09-10 07:46:19 +02:00
|
|
|
#![allow(unused_must_use)]
|
|
|
|
|
|
|
|
extern crate gcc;
|
|
|
|
extern crate syntex_syntax as syntax;
|
|
|
|
|
|
|
|
use std::env;
|
|
|
|
use std::fs::File;
|
|
|
|
use std::io::BufWriter;
|
|
|
|
use std::io::prelude::*;
|
|
|
|
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 08:21:27 +02:00
|
|
|
struct TestGenerator<'a> {
|
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 07:46:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
fn main() {
|
2015-09-10 07:59:24 +02:00
|
|
|
let target = env::var("TARGET").unwrap();
|
2015-09-10 07:46:19 +02:00
|
|
|
|
2015-09-10 07:59:24 +02:00
|
|
|
let sess = ParseSess::new();
|
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 07:59:24 +02:00
|
|
|
build_cfg(&mut krate.config, &target);
|
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);
|
|
|
|
|
|
|
|
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());
|
|
|
|
|
|
|
|
writeln!(c_out, "
|
2015-09-10 08:21:27 +02:00
|
|
|
#include <glob.h>
|
|
|
|
#include <ifaddrs.h>
|
|
|
|
#include <netdb.h>
|
2015-09-10 07:46:19 +02:00
|
|
|
#include <netinet/in.h>
|
|
|
|
#include <netinet/ip.h>
|
2015-09-10 08:21:27 +02:00
|
|
|
#include <pthread.h>
|
2015-09-10 07:46:19 +02:00
|
|
|
#include <signal.h>
|
2015-09-10 07:59:24 +02:00
|
|
|
#include <stdalign.h>
|
|
|
|
#include <stddef.h>
|
2015-09-10 07:46:19 +02:00
|
|
|
#include <stdint.h>
|
2015-09-10 07:59:24 +02:00
|
|
|
#include <sys/resource.h>
|
2015-09-10 08:21:27 +02:00
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/time.h>
|
2015-09-10 07:46:19 +02:00
|
|
|
#include <sys/types.h>
|
2015-09-10 08:21:27 +02:00
|
|
|
#include <sys/un.h>
|
2015-09-10 07:46:19 +02:00
|
|
|
#include <time.h>
|
2015-09-10 08:21:27 +02:00
|
|
|
#include <utime.h>
|
2015-09-10 07:59:24 +02:00
|
|
|
#include <wchar.h>
|
|
|
|
");
|
|
|
|
|
|
|
|
if target.contains("apple-darwin") {
|
|
|
|
writeln!(c_out, "
|
|
|
|
#include <mach/mach_time.h>
|
2015-09-10 07:46:19 +02:00
|
|
|
");
|
2015-09-10 07:59:24 +02:00
|
|
|
}
|
2015-09-10 07:46:19 +02:00
|
|
|
|
|
|
|
visit::walk_crate(&mut TestGenerator {
|
|
|
|
rust: Box::new(rust_out),
|
|
|
|
c: Box::new(c_out),
|
2015-09-10 08:21:27 +02:00
|
|
|
sh: &sess.span_diagnostic,
|
2015-09-10 07:46:19 +02:00
|
|
|
}, &krate);
|
|
|
|
|
|
|
|
gcc::Config::new()
|
|
|
|
.file(out.join("all.c"))
|
|
|
|
.compile("liball.a");
|
|
|
|
}
|
|
|
|
|
2015-09-10 07:59:24 +02:00
|
|
|
fn build_cfg(cfg: &mut ast::CrateConfig, target: &str) {
|
2015-09-10 07:46:19 +02:00
|
|
|
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")
|
2015-09-10 07:59:24 +02:00
|
|
|
} else if target.contains("apple-darwin") {
|
|
|
|
("macos", "unix", "")
|
2015-09-10 07:46:19 +02:00
|
|
|
} 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)));
|
|
|
|
}
|
|
|
|
|
2015-09-10 08:21:27 +02:00
|
|
|
impl<'a> TestGenerator<'a> {
|
|
|
|
fn test_type(&mut self, ty: &str) {
|
2015-09-10 07:46:19 +02:00
|
|
|
let cty = if ty.starts_with("c_") {
|
2015-09-10 08:46:19 +02:00
|
|
|
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(),
|
2015-09-10 07:46:19 +02:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
(match ty {
|
2015-09-10 07:59:24 +02:00
|
|
|
"sighandler_t" => return,
|
2015-09-10 07:46:19 +02:00
|
|
|
ty => ty,
|
|
|
|
}).to_string()
|
|
|
|
};
|
2015-09-10 08:21:27 +02:00
|
|
|
self.test_size_align(ty, &cty);
|
|
|
|
}
|
|
|
|
|
2015-09-10 08:46:19 +02:00
|
|
|
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),
|
2015-09-10 08:21:27 +02:00
|
|
|
};
|
|
|
|
self.test_size_align(ty, &cty);
|
2015-09-10 08:46:19 +02:00
|
|
|
|
|
|
|
writeln!(self.rust, r#"
|
|
|
|
#[test]
|
|
|
|
fn field_offset_size_{ty}() {{
|
|
|
|
"#, ty = ty);
|
|
|
|
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"),
|
|
|
|
};
|
|
|
|
|
|
|
|
let cname = match &name.to_string()[..] {
|
|
|
|
s if s.ends_with("_nsec") && ty == "stat" => {
|
|
|
|
s.replace("_nsec", "spec.tv_nsec")
|
|
|
|
}
|
|
|
|
s => s.to_string(),
|
|
|
|
};
|
|
|
|
|
|
|
|
writeln!(self.c, r#"
|
|
|
|
uint64_t __test_offset_{ty}_{rust_field}() {{
|
|
|
|
return offsetof({cty}, {c_field});
|
|
|
|
}}
|
|
|
|
uint64_t __test_size_{ty}_{rust_field}() {{
|
|
|
|
{cty}* foo = NULL;
|
|
|
|
return sizeof(foo->{c_field});
|
|
|
|
}}
|
|
|
|
"#, ty = ty, cty = cty, rust_field = name, c_field = cname);
|
|
|
|
writeln!(self.rust, r#"
|
|
|
|
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}");
|
|
|
|
}}
|
|
|
|
"#, ty = ty, field = name);
|
|
|
|
}
|
|
|
|
writeln!(self.rust, r#"
|
|
|
|
}}
|
|
|
|
"#);
|
2015-09-10 08:21:27 +02:00
|
|
|
}
|
2015-09-10 07:59:24 +02:00
|
|
|
|
2015-09-10 08:21:27 +02:00
|
|
|
fn test_size_align(&mut self, rust: &str, c: &str) {
|
2015-09-10 07:46:19 +02:00
|
|
|
writeln!(self.c, r#"
|
2015-09-10 08:21:27 +02:00
|
|
|
uint64_t __test_size_{ty}() {{ return sizeof({cty}); }}
|
|
|
|
uint64_t __test_align_{ty}() {{ return alignof({cty}); }}
|
|
|
|
"#, ty = rust, cty = c);
|
2015-09-10 07:46:19 +02:00
|
|
|
writeln!(self.rust, r#"
|
|
|
|
#[test]
|
2015-09-10 08:21:27 +02:00
|
|
|
fn size_align_{ty}() {{
|
2015-09-10 07:59:24 +02:00
|
|
|
extern {{
|
2015-09-10 08:21:27 +02:00
|
|
|
fn __test_size_{ty}() -> u64;
|
|
|
|
fn __test_align_{ty}() -> u64;
|
2015-09-10 07:59:24 +02:00
|
|
|
}}
|
|
|
|
unsafe {{
|
2015-09-10 08:46:19 +02:00
|
|
|
same(mem::size_of::<{ty}>() as u64,
|
|
|
|
__test_size_{ty}(), "size");
|
|
|
|
same(mem::align_of::<{ty}>() as u64,
|
|
|
|
__test_align_{ty}(), "align");
|
2015-09-10 07:59:24 +02:00
|
|
|
}}
|
2015-09-10 07:46:19 +02:00
|
|
|
}}
|
2015-09-10 08:21:27 +02:00
|
|
|
"#, ty = rust);
|
|
|
|
}
|
|
|
|
|
|
|
|
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-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
|
|
|
}
|
|
|
|
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
visit::walk_item(self, i)
|
|
|
|
}
|
|
|
|
}
|