blob: 62d8b07f5e9706171ce081a5572aa9c1ee9c49b8 [file] [log] [blame]
Alex Crichton8e5f0cd2015-09-09 22:46:19 -07001extern crate gcc;
2extern crate syntex_syntax as syntax;
3
Alex Crichton310d6232015-09-10 10:56:31 -07004use std::collections::HashSet;
Alex Crichton8e5f0cd2015-09-09 22:46:19 -07005use std::env;
6use std::fs::File;
7use std::io::BufWriter;
8use std::io::prelude::*;
9use std::path::{Path, PathBuf};
10
11use syntax::ast;
Alex Crichtona9adfbf2015-09-09 23:21:27 -070012use syntax::diagnostic::SpanHandler;
Alex Crichton8e5f0cd2015-09-09 22:46:19 -070013use syntax::parse::token::InternedString;
Alex Crichtona9adfbf2015-09-09 23:21:27 -070014use syntax::attr::{self, ReprAttr};
Alex Crichton8e5f0cd2015-09-09 22:46:19 -070015use syntax::parse::{self, ParseSess};
16use syntax::visit::{self, Visitor};
17
Alex Crichton310d6232015-09-10 10:56:31 -070018macro_rules! t {
19 ($e:expr) => (match $e {
20 Ok(e) => e,
21 Err(e) => panic!("{} failed with {}", stringify!($e), e),
22 })
23}
24
Alex Crichtona9adfbf2015-09-09 23:21:27 -070025struct TestGenerator<'a> {
Alex Crichton310d6232015-09-10 10:56:31 -070026 target: String,
Alex Crichton8e5f0cd2015-09-09 22:46:19 -070027 rust: Box<Write>,
28 c: Box<Write>,
Alex Crichtona9adfbf2015-09-09 23:21:27 -070029 sh: &'a SpanHandler,
Alex Crichton310d6232015-09-10 10:56:31 -070030 structs: HashSet<String>,
31}
32
33struct StructFinder {
34 structs: HashSet<String>,
35}
36
37impl<'a> TestGenerator<'a> {
38 fn headers(&self) -> Vec<&'static str> {
39 let mut base = vec![
40 "glob.h",
41 "ifaddrs.h",
42 "netdb.h",
43 "netinet/in.h",
44 "netinet/ip.h",
45 "pthread.h",
46 "signal.h",
47 "stdalign.h",
48 "stddef.h",
49 "stdint.h",
50 "sys/resource.h",
51 "sys/socket.h",
52 "sys/stat.h",
53 "sys/time.h",
54 "sys/types.h",
55 "sys/un.h",
56 "time.h",
57 "unistd.h",
58 "utime.h",
59 "wchar.h",
60 ];
61
62 if self.target.contains("apple-darwin") {
63 base.push("mach/mach_time.h");
64 }
65 if self.target.contains("unknown-linux") {
66 base.push("linux/if_packet.h");
67 base.push("net/ethernet.h");
68 }
69
70 return base
71 }
72
73 fn rust2c(&self, ty: &str) -> String {
74 match ty {
75 t if t.starts_with("c_") => {
76 match &ty[2..].replace("long", " long")[..] {
77 s if s.starts_with("u") => format!("unsigned {}", &s[1..]),
78 "short" => format!("short"),
79 s if s.starts_with("s") => format!("signed {}", &s[1..]),
80 s => s.to_string(),
81 }
82 }
83 "ip6_mreq" => "struct ipv6_mreq".to_string(),
84 "glob_t" => "glob_t".to_string(),
85 t if t.starts_with("pthread") => t.to_string(),
86
87 t if self.structs.contains(t) => format!("struct {}", t),
88
89 t => t.to_string(),
90 }
91 }
92
93 fn rust2cfield(&self, struct_: &str, field: &str) -> String {
94 match field {
95 s if s.ends_with("_nsec") && struct_ == "stat" => {
96 if self.target.contains("apple-darwin") {
97 s.replace("_nsec", "spec.tv_nsec")
98 } else {
99 s.replace("e_nsec", ".tv_nsec")
100 }
101 }
102 s => s.to_string(),
103 }
104 }
105
106 fn cfg_list(&self) -> Vec<(&'static str, Option<&'static str>)> {
107 let mut ret = Vec::new();
108 let (arch, target_pointer_width) = if self.target.starts_with("x86_64") {
109 ("x86_64", "64")
110 } else if self.target.starts_with("i686") {
111 ("x86", "32")
112 } else {
113 panic!("unknown arch/pointer width: {}", self.target)
114 };
115 let (os, family, env) = if self.target.contains("unknown-linux") {
116 ("linux", "unix", "gnu")
117 } else if self.target.contains("apple-darwin") {
118 ("macos", "unix", "")
119 } else {
120 panic!("unknown os/family width: {}", self.target)
121 };
122
123 ret.push((family, None));
124 ret.push(("target_os", Some(os)));
125 ret.push(("target_family", Some(family)));
126 ret.push(("target_arch", Some(arch)));
127 // skip endianness
128 ret.push(("target_pointer_width", Some(target_pointer_width)));
129 ret.push(("target_env", Some(env)));
130
131 return ret
132 }
Alex Crichton8e5f0cd2015-09-09 22:46:19 -0700133}
134
135fn main() {
Alex Crichton310d6232015-09-10 10:56:31 -0700136 // Prep the test generator
137 let target = t!(env::var("TARGET"));
138 let out = PathBuf::from(env::var_os("OUT_DIR").unwrap());
139 let rust_out = BufWriter::new(t!(File::create(out.join("all.rs"))));
140 let c_out = BufWriter::new(t!(File::create(out.join("all.c"))));
Alex Crichton16083062015-09-09 22:59:24 -0700141 let sess = ParseSess::new();
Alex Crichton310d6232015-09-10 10:56:31 -0700142 let mut tg = TestGenerator {
143 target: target,
144 rust: Box::new(rust_out),
145 c: Box::new(c_out),
146 sh: &sess.span_diagnostic,
147 structs: HashSet::new(),
148 };
149
150 // Parse the libc crate
Alex Crichton8e5f0cd2015-09-09 22:46:19 -0700151 let src = Path::new("../src/lib.rs");
152 let cfg = Vec::new();
153 let mut krate = parse::parse_crate_from_file(src, cfg, &sess);
Alex Crichton8e5f0cd2015-09-09 22:46:19 -0700154
Alex Crichton310d6232015-09-10 10:56:31 -0700155 // Strip the crate down to just what's configured for our target
156 for (k, v) in tg.cfg_list() {
157 let s = InternedString::new;
158 krate.config.push(match v {
159 Some(v) => attr::mk_name_value_item_str(s(k), s(v)),
160 None => attr::mk_word_item(s(k)),
161 });
162 }
Alex Crichton8e5f0cd2015-09-09 22:46:19 -0700163 let mut gated_cfgs = Vec::new();
164 let krate = syntax::config::strip_unconfigured_items(&sess.span_diagnostic,
165 krate,
166 &mut gated_cfgs);
167
Alex Crichton310d6232015-09-10 10:56:31 -0700168 // Probe the crate to find all structs (used to convert type names to names
169 // in C).
170 let mut structs = StructFinder {
171 structs: HashSet::new(),
172 };
173 visit::walk_crate(&mut structs, &krate);
174 tg.structs = structs.structs;
Alex Crichton8e5f0cd2015-09-09 22:46:19 -0700175
Alex Crichton310d6232015-09-10 10:56:31 -0700176 // Walk the crate, emitting test cases for everything found
177 for header in tg.headers() {
178 t!(writeln!(tg.c, "#include <{}>", header));
Alex Crichton16083062015-09-09 22:59:24 -0700179 }
Alex Crichton310d6232015-09-10 10:56:31 -0700180 visit::walk_crate(&mut tg, &krate);
181 drop(tg);
Alex Crichton16083062015-09-09 22:59:24 -0700182
Alex Crichton310d6232015-09-10 10:56:31 -0700183 // Compile our C shim to be linked into tests
Alex Crichton8e5f0cd2015-09-09 22:46:19 -0700184 gcc::Config::new()
185 .file(out.join("all.c"))
186 .compile("liball.a");
187}
188
Alex Crichtona9adfbf2015-09-09 23:21:27 -0700189impl<'a> TestGenerator<'a> {
190 fn test_type(&mut self, ty: &str) {
Alex Crichton310d6232015-09-10 10:56:31 -0700191 match ty {
192 "sighandler_t" => return,
193 _ => {}
194 }
195 let c = self.rust2c(ty);
196 self.test_size_align(ty, &c);
Alex Crichtona9adfbf2015-09-09 23:21:27 -0700197 }
Alex Crichton16083062015-09-09 22:59:24 -0700198
Alex Crichton3e5155b2015-09-09 23:46:19 -0700199 fn test_struct(&mut self, ty: &str, s: &ast::StructDef) {
Alex Crichton310d6232015-09-10 10:56:31 -0700200 let cty = self.rust2c(ty);
Alex Crichtona9adfbf2015-09-09 23:21:27 -0700201 self.test_size_align(ty, &cty);
Alex Crichton3e5155b2015-09-09 23:46:19 -0700202
Alex Crichton310d6232015-09-10 10:56:31 -0700203 t!(writeln!(self.rust, r#"
Alex Crichton3e5155b2015-09-09 23:46:19 -0700204 #[test]
205 fn field_offset_size_{ty}() {{
Alex Crichton310d6232015-09-10 10:56:31 -0700206 "#, ty = ty));
Alex Crichton3e5155b2015-09-09 23:46:19 -0700207 for field in s.fields.iter() {
208 let name = match field.node.kind {
209 ast::NamedField(name, ast::Public) => name,
210 ast::NamedField(_, ast::Inherited) => continue,
211 ast::UnnamedField(..) => panic!("no tuple structs in FFI"),
212 };
213
Alex Crichton310d6232015-09-10 10:56:31 -0700214 let cfield = self.rust2cfield(ty, &name.to_string());
Alex Crichton3e5155b2015-09-09 23:46:19 -0700215
Alex Crichton310d6232015-09-10 10:56:31 -0700216 t!(writeln!(self.c, r#"
Alex Crichton3e5155b2015-09-09 23:46:19 -0700217 uint64_t __test_offset_{ty}_{rust_field}() {{
218 return offsetof({cty}, {c_field});
219 }}
220 uint64_t __test_size_{ty}_{rust_field}() {{
221 {cty}* foo = NULL;
222 return sizeof(foo->{c_field});
223 }}
Alex Crichton310d6232015-09-10 10:56:31 -0700224 "#, ty = ty, cty = cty, rust_field = name, c_field = cfield));
225 t!(writeln!(self.rust, r#"
Alex Crichton3e5155b2015-09-09 23:46:19 -0700226 extern {{
227 fn __test_offset_{ty}_{field}() -> u64;
228 fn __test_size_{ty}_{field}() -> u64;
229 }}
230 unsafe {{
231 let foo = 0 as *const {ty};
232 same(offset_of!({ty}, {field}),
233 __test_offset_{ty}_{field}(),
234 "field offset {field} of {ty}");
235 same(mem::size_of_val(&(*foo).{field}) as u64,
236 __test_size_{ty}_{field}(),
237 "field size {field} of {ty}");
238 }}
Alex Crichton310d6232015-09-10 10:56:31 -0700239 "#, ty = ty, field = name));
Alex Crichton3e5155b2015-09-09 23:46:19 -0700240 }
Alex Crichton310d6232015-09-10 10:56:31 -0700241 t!(writeln!(self.rust, r#"
Alex Crichton3e5155b2015-09-09 23:46:19 -0700242 }}
Alex Crichton310d6232015-09-10 10:56:31 -0700243 "#));
Alex Crichtona9adfbf2015-09-09 23:21:27 -0700244 }
245
246 fn test_size_align(&mut self, rust: &str, c: &str) {
Alex Crichton310d6232015-09-10 10:56:31 -0700247 t!(writeln!(self.c, r#"
Alex Crichtona9adfbf2015-09-09 23:21:27 -0700248 uint64_t __test_size_{ty}() {{ return sizeof({cty}); }}
249 uint64_t __test_align_{ty}() {{ return alignof({cty}); }}
Alex Crichton310d6232015-09-10 10:56:31 -0700250 "#, ty = rust, cty = c));
251 t!(writeln!(self.rust, r#"
Alex Crichton8e5f0cd2015-09-09 22:46:19 -0700252 #[test]
Alex Crichtona9adfbf2015-09-09 23:21:27 -0700253 fn size_align_{ty}() {{
Alex Crichton16083062015-09-09 22:59:24 -0700254 extern {{
Alex Crichtona9adfbf2015-09-09 23:21:27 -0700255 fn __test_size_{ty}() -> u64;
256 fn __test_align_{ty}() -> u64;
Alex Crichton16083062015-09-09 22:59:24 -0700257 }}
258 unsafe {{
Alex Crichton3e5155b2015-09-09 23:46:19 -0700259 same(mem::size_of::<{ty}>() as u64,
260 __test_size_{ty}(), "size");
Alex Crichtonc8b895c2015-09-10 13:24:15 -0700261 same(align::<{ty}>() as u64,
Alex Crichton3e5155b2015-09-09 23:46:19 -0700262 __test_align_{ty}(), "align");
Alex Crichton16083062015-09-09 22:59:24 -0700263 }}
Alex Crichton8e5f0cd2015-09-09 22:46:19 -0700264 }}
Alex Crichton310d6232015-09-10 10:56:31 -0700265 "#, ty = rust));
Alex Crichtona9adfbf2015-09-09 23:21:27 -0700266 }
267
268 fn assert_no_generics(&self, _i: &ast::Item, generics: &ast::Generics) {
269 assert!(generics.lifetimes.len() == 0);
270 assert!(generics.ty_params.len() == 0);
271 assert!(generics.where_clause.predicates.len() == 0);
Alex Crichton8e5f0cd2015-09-09 22:46:19 -0700272 }
273}
274
Alex Crichtona9adfbf2015-09-09 23:21:27 -0700275impl<'a, 'v> Visitor<'v> for TestGenerator<'a> {
Alex Crichton8e5f0cd2015-09-09 22:46:19 -0700276 fn visit_item(&mut self, i: &'v ast::Item) {
277 match i.node {
278 ast::ItemTy(_, ref generics) => {
Alex Crichtona9adfbf2015-09-09 23:21:27 -0700279 self.assert_no_generics(i, generics);
280 self.test_type(&i.ident.to_string());
281 }
282
283 ast::ItemStruct(ref s, ref generics) => {
284 self.assert_no_generics(i, generics);
285 let is_c = i.attrs.iter().any(|a| {
286 attr::find_repr_attrs(self.sh, a).iter().any(|a| {
287 *a == ReprAttr::ReprExtern
288 })
289 });
290 if !is_c {
291 panic!("{} is not marked #[repr(C)]", i.ident);
292 }
293 self.test_struct(&i.ident.to_string(), s);
Alex Crichton8e5f0cd2015-09-09 22:46:19 -0700294 }
295
296 _ => {}
297 }
298 visit::walk_item(self, i)
299 }
300}
Alex Crichton310d6232015-09-10 10:56:31 -0700301
302impl<'v> Visitor<'v> for StructFinder {
303 fn visit_item(&mut self, i: &'v ast::Item) {
304 match i.node {
305 ast::ItemStruct(..) => {
306 self.structs.insert(i.ident.to_string());
307 }
308
309 _ => {}
310 }
311 visit::walk_item(self, i)
312 }
313}