blob: c62dbe0642343c66157a996d9a4f3a296d0d5f28 [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::*;
Alex Crichton0df7c102015-09-10 16:35:37 -07009use std::iter;
Alex Crichton8e5f0cd2015-09-09 22:46:19 -070010use std::path::{Path, PathBuf};
11
12use syntax::ast;
Alex Crichtona9adfbf2015-09-09 23:21:27 -070013use syntax::diagnostic::SpanHandler;
Alex Crichton8e5f0cd2015-09-09 22:46:19 -070014use syntax::parse::token::InternedString;
Alex Crichtona9adfbf2015-09-09 23:21:27 -070015use syntax::attr::{self, ReprAttr};
Alex Crichton8e5f0cd2015-09-09 22:46:19 -070016use syntax::parse::{self, ParseSess};
17use syntax::visit::{self, Visitor};
18
Alex Crichton310d6232015-09-10 10:56:31 -070019macro_rules! t {
20 ($e:expr) => (match $e {
21 Ok(e) => e,
22 Err(e) => panic!("{} failed with {}", stringify!($e), e),
23 })
24}
25
Alex Crichtona9adfbf2015-09-09 23:21:27 -070026struct TestGenerator<'a> {
Alex Crichton310d6232015-09-10 10:56:31 -070027 target: String,
Alex Crichton8e5f0cd2015-09-09 22:46:19 -070028 rust: Box<Write>,
29 c: Box<Write>,
Alex Crichtona9adfbf2015-09-09 23:21:27 -070030 sh: &'a SpanHandler,
Alex Crichton310d6232015-09-10 10:56:31 -070031 structs: HashSet<String>,
32}
33
34struct StructFinder {
35 structs: HashSet<String>,
36}
37
38impl<'a> TestGenerator<'a> {
Alex Crichton0df7c102015-09-10 16:35:37 -070039 fn defines(&self) -> Vec<&'static str> {
40 let mut ret = Vec::new();
41 if self.target.contains("unknown-linux") {
42 ret.push("_GNU_SOURCE");
43 }
44 return ret
45 }
46
Alex Crichton310d6232015-09-10 10:56:31 -070047 fn headers(&self) -> Vec<&'static str> {
48 let mut base = vec![
Alex Crichton0df7c102015-09-10 16:35:37 -070049 "errno.h",
50 "fcntl.h",
Alex Crichton310d6232015-09-10 10:56:31 -070051 "glob.h",
52 "ifaddrs.h",
Alex Crichton0df7c102015-09-10 16:35:37 -070053 "limits.h",
54 "net/if.h",
Alex Crichton310d6232015-09-10 10:56:31 -070055 "netdb.h",
56 "netinet/in.h",
57 "netinet/ip.h",
Alex Crichton0df7c102015-09-10 16:35:37 -070058 "netinet/tcp.h",
Alex Crichton310d6232015-09-10 10:56:31 -070059 "pthread.h",
60 "signal.h",
61 "stdalign.h",
62 "stddef.h",
63 "stdint.h",
Alex Crichton0df7c102015-09-10 16:35:37 -070064 "stdio.h",
65 "stdlib.h",
66 "sys/mman.h",
Alex Crichton310d6232015-09-10 10:56:31 -070067 "sys/resource.h",
68 "sys/socket.h",
69 "sys/stat.h",
70 "sys/time.h",
71 "sys/types.h",
72 "sys/un.h",
73 "time.h",
74 "unistd.h",
75 "utime.h",
76 "wchar.h",
77 ];
78
79 if self.target.contains("apple-darwin") {
80 base.push("mach/mach_time.h");
81 }
82 if self.target.contains("unknown-linux") {
83 base.push("linux/if_packet.h");
84 base.push("net/ethernet.h");
85 }
86
87 return base
88 }
89
90 fn rust2c(&self, ty: &str) -> String {
91 match ty {
92 t if t.starts_with("c_") => {
93 match &ty[2..].replace("long", " long")[..] {
94 s if s.starts_with("u") => format!("unsigned {}", &s[1..]),
95 "short" => format!("short"),
96 s if s.starts_with("s") => format!("signed {}", &s[1..]),
97 s => s.to_string(),
98 }
99 }
100 "ip6_mreq" => "struct ipv6_mreq".to_string(),
101 "glob_t" => "glob_t".to_string(),
102 t if t.starts_with("pthread") => t.to_string(),
103
104 t if self.structs.contains(t) => format!("struct {}", t),
105
106 t => t.to_string(),
107 }
108 }
109
110 fn rust2cfield(&self, struct_: &str, field: &str) -> String {
111 match field {
112 s if s.ends_with("_nsec") && struct_ == "stat" => {
113 if self.target.contains("apple-darwin") {
114 s.replace("_nsec", "spec.tv_nsec")
115 } else {
116 s.replace("e_nsec", ".tv_nsec")
117 }
118 }
119 s => s.to_string(),
120 }
121 }
122
123 fn cfg_list(&self) -> Vec<(&'static str, Option<&'static str>)> {
124 let mut ret = Vec::new();
125 let (arch, target_pointer_width) = if self.target.starts_with("x86_64") {
126 ("x86_64", "64")
127 } else if self.target.starts_with("i686") {
128 ("x86", "32")
129 } else {
130 panic!("unknown arch/pointer width: {}", self.target)
131 };
132 let (os, family, env) = if self.target.contains("unknown-linux") {
133 ("linux", "unix", "gnu")
134 } else if self.target.contains("apple-darwin") {
135 ("macos", "unix", "")
136 } else {
137 panic!("unknown os/family width: {}", self.target)
138 };
139
140 ret.push((family, None));
141 ret.push(("target_os", Some(os)));
142 ret.push(("target_family", Some(family)));
143 ret.push(("target_arch", Some(arch)));
144 // skip endianness
145 ret.push(("target_pointer_width", Some(target_pointer_width)));
146 ret.push(("target_env", Some(env)));
147
148 return ret
149 }
Alex Crichton8e5f0cd2015-09-09 22:46:19 -0700150}
151
152fn main() {
Alex Crichton310d6232015-09-10 10:56:31 -0700153 // Prep the test generator
154 let target = t!(env::var("TARGET"));
155 let out = PathBuf::from(env::var_os("OUT_DIR").unwrap());
156 let rust_out = BufWriter::new(t!(File::create(out.join("all.rs"))));
157 let c_out = BufWriter::new(t!(File::create(out.join("all.c"))));
Alex Crichton16083062015-09-09 22:59:24 -0700158 let sess = ParseSess::new();
Alex Crichton310d6232015-09-10 10:56:31 -0700159 let mut tg = TestGenerator {
160 target: target,
161 rust: Box::new(rust_out),
162 c: Box::new(c_out),
163 sh: &sess.span_diagnostic,
164 structs: HashSet::new(),
165 };
166
167 // Parse the libc crate
Alex Crichton8e5f0cd2015-09-09 22:46:19 -0700168 let src = Path::new("../src/lib.rs");
169 let cfg = Vec::new();
170 let mut krate = parse::parse_crate_from_file(src, cfg, &sess);
Alex Crichton8e5f0cd2015-09-09 22:46:19 -0700171
Alex Crichton310d6232015-09-10 10:56:31 -0700172 // Strip the crate down to just what's configured for our target
173 for (k, v) in tg.cfg_list() {
174 let s = InternedString::new;
175 krate.config.push(match v {
176 Some(v) => attr::mk_name_value_item_str(s(k), s(v)),
177 None => attr::mk_word_item(s(k)),
178 });
179 }
Alex Crichton8e5f0cd2015-09-09 22:46:19 -0700180 let mut gated_cfgs = Vec::new();
181 let krate = syntax::config::strip_unconfigured_items(&sess.span_diagnostic,
182 krate,
183 &mut gated_cfgs);
184
Alex Crichton310d6232015-09-10 10:56:31 -0700185 // Probe the crate to find all structs (used to convert type names to names
186 // in C).
187 let mut structs = StructFinder {
188 structs: HashSet::new(),
189 };
190 visit::walk_crate(&mut structs, &krate);
191 tg.structs = structs.structs;
Alex Crichton8e5f0cd2015-09-09 22:46:19 -0700192
Alex Crichton0df7c102015-09-10 16:35:37 -0700193 // Prep the C file by emitting header stuff
194 for define in tg.defines() {
195 t!(writeln!(tg.c, "#define {}", define));
196 }
Alex Crichton310d6232015-09-10 10:56:31 -0700197 for header in tg.headers() {
198 t!(writeln!(tg.c, "#include <{}>", header));
Alex Crichton16083062015-09-09 22:59:24 -0700199 }
Alex Crichton0df7c102015-09-10 16:35:37 -0700200
201 // Walk the crate, emitting test cases for everything found
Alex Crichton310d6232015-09-10 10:56:31 -0700202 visit::walk_crate(&mut tg, &krate);
203 drop(tg);
Alex Crichton16083062015-09-09 22:59:24 -0700204
Alex Crichton310d6232015-09-10 10:56:31 -0700205 // Compile our C shim to be linked into tests
Alex Crichton8e5f0cd2015-09-09 22:46:19 -0700206 gcc::Config::new()
207 .file(out.join("all.c"))
Alex Crichton0df7c102015-09-10 16:35:37 -0700208 .flag("-Wall")
209 .flag("-Wextra")
210 .flag("-Werror")
Alex Crichton8e5f0cd2015-09-09 22:46:19 -0700211 .compile("liball.a");
212}
213
Alex Crichtona9adfbf2015-09-09 23:21:27 -0700214impl<'a> TestGenerator<'a> {
215 fn test_type(&mut self, ty: &str) {
Alex Crichton310d6232015-09-10 10:56:31 -0700216 match ty {
217 "sighandler_t" => return,
218 _ => {}
219 }
220 let c = self.rust2c(ty);
221 self.test_size_align(ty, &c);
Alex Crichtona9adfbf2015-09-09 23:21:27 -0700222 }
Alex Crichton16083062015-09-09 22:59:24 -0700223
Alex Crichton3e5155b2015-09-09 23:46:19 -0700224 fn test_struct(&mut self, ty: &str, s: &ast::StructDef) {
Alex Crichton310d6232015-09-10 10:56:31 -0700225 let cty = self.rust2c(ty);
Alex Crichtona9adfbf2015-09-09 23:21:27 -0700226 self.test_size_align(ty, &cty);
Alex Crichton3e5155b2015-09-09 23:46:19 -0700227
Alex Crichton310d6232015-09-10 10:56:31 -0700228 t!(writeln!(self.rust, r#"
Alex Crichton3e5155b2015-09-09 23:46:19 -0700229 #[test]
230 fn field_offset_size_{ty}() {{
Alex Crichton310d6232015-09-10 10:56:31 -0700231 "#, ty = ty));
Alex Crichton3e5155b2015-09-09 23:46:19 -0700232 for field in s.fields.iter() {
233 let name = match field.node.kind {
234 ast::NamedField(name, ast::Public) => name,
235 ast::NamedField(_, ast::Inherited) => continue,
236 ast::UnnamedField(..) => panic!("no tuple structs in FFI"),
237 };
238
Alex Crichton310d6232015-09-10 10:56:31 -0700239 let cfield = self.rust2cfield(ty, &name.to_string());
Alex Crichton3e5155b2015-09-09 23:46:19 -0700240
Alex Crichton310d6232015-09-10 10:56:31 -0700241 t!(writeln!(self.c, r#"
Alex Crichton3e5155b2015-09-09 23:46:19 -0700242 uint64_t __test_offset_{ty}_{rust_field}() {{
243 return offsetof({cty}, {c_field});
244 }}
245 uint64_t __test_size_{ty}_{rust_field}() {{
246 {cty}* foo = NULL;
247 return sizeof(foo->{c_field});
248 }}
Alex Crichton310d6232015-09-10 10:56:31 -0700249 "#, ty = ty, cty = cty, rust_field = name, c_field = cfield));
250 t!(writeln!(self.rust, r#"
Alex Crichton3e5155b2015-09-09 23:46:19 -0700251 extern {{
252 fn __test_offset_{ty}_{field}() -> u64;
253 fn __test_size_{ty}_{field}() -> u64;
254 }}
255 unsafe {{
256 let foo = 0 as *const {ty};
257 same(offset_of!({ty}, {field}),
258 __test_offset_{ty}_{field}(),
259 "field offset {field} of {ty}");
260 same(mem::size_of_val(&(*foo).{field}) as u64,
261 __test_size_{ty}_{field}(),
262 "field size {field} of {ty}");
263 }}
Alex Crichton310d6232015-09-10 10:56:31 -0700264 "#, ty = ty, field = name));
Alex Crichton3e5155b2015-09-09 23:46:19 -0700265 }
Alex Crichton310d6232015-09-10 10:56:31 -0700266 t!(writeln!(self.rust, r#"
Alex Crichton3e5155b2015-09-09 23:46:19 -0700267 }}
Alex Crichton310d6232015-09-10 10:56:31 -0700268 "#));
Alex Crichtona9adfbf2015-09-09 23:21:27 -0700269 }
270
271 fn test_size_align(&mut self, rust: &str, c: &str) {
Alex Crichton310d6232015-09-10 10:56:31 -0700272 t!(writeln!(self.c, r#"
Alex Crichtona9adfbf2015-09-09 23:21:27 -0700273 uint64_t __test_size_{ty}() {{ return sizeof({cty}); }}
274 uint64_t __test_align_{ty}() {{ return alignof({cty}); }}
Alex Crichton310d6232015-09-10 10:56:31 -0700275 "#, ty = rust, cty = c));
276 t!(writeln!(self.rust, r#"
Alex Crichton8e5f0cd2015-09-09 22:46:19 -0700277 #[test]
Alex Crichtona9adfbf2015-09-09 23:21:27 -0700278 fn size_align_{ty}() {{
Alex Crichton16083062015-09-09 22:59:24 -0700279 extern {{
Alex Crichtona9adfbf2015-09-09 23:21:27 -0700280 fn __test_size_{ty}() -> u64;
281 fn __test_align_{ty}() -> u64;
Alex Crichton16083062015-09-09 22:59:24 -0700282 }}
283 unsafe {{
Alex Crichton3e5155b2015-09-09 23:46:19 -0700284 same(mem::size_of::<{ty}>() as u64,
285 __test_size_{ty}(), "size");
Alex Crichtonc8b895c2015-09-10 13:24:15 -0700286 same(align::<{ty}>() as u64,
Alex Crichton3e5155b2015-09-09 23:46:19 -0700287 __test_align_{ty}(), "align");
Alex Crichton16083062015-09-09 22:59:24 -0700288 }}
Alex Crichton8e5f0cd2015-09-09 22:46:19 -0700289 }}
Alex Crichton310d6232015-09-10 10:56:31 -0700290 "#, ty = rust));
Alex Crichtona9adfbf2015-09-09 23:21:27 -0700291 }
292
Alex Crichton0df7c102015-09-10 16:35:37 -0700293 fn test_const(&mut self, name: &str, rust_ty: &str) {
294 let cty = self.rust2c(&rust_ty.replace("*mut ", "")
295 .replace("*const ", ""));
296 let ptrs = rust_ty.matches("*").count();
297 let cty = format!("{}{}", cty,
298 iter::repeat("*").take(ptrs).collect::<String>());
299 let cast = if name == "SIG_IGN" {"(size_t)"} else {""};
300 t!(writeln!(self.c, r#"
Alex Crichtone7afdd82015-09-10 17:15:20 -0700301 int __test_const_{name}({cty} *out) {{
302 int ret = 0;
303 #if defined({name})
304 *out = {cast}({name});
305 ret = 1;
306 #endif
307 return ret;
308 }}
Alex Crichton0df7c102015-09-10 16:35:37 -0700309 "#, name = name, cast = cast, cty = cty));
310 t!(writeln!(self.rust, r#"
311 #[test]
312 fn const_{name}() {{
313 extern {{
Alex Crichtone7afdd82015-09-10 17:15:20 -0700314 fn __test_const_{name}(out: *mut {ty}) -> c_int;
Alex Crichton0df7c102015-09-10 16:35:37 -0700315 }}
316 unsafe {{
Alex Crichtone7afdd82015-09-10 17:15:20 -0700317 let mut o = mem::zeroed();
318 if __test_const_{name}(&mut o) == 0 {{
319 panic!("not defined");
320 }} else {{
321 same({name}, o, "value");
322 }}
Alex Crichton0df7c102015-09-10 16:35:37 -0700323 }}
324 }}
325 "#, ty = rust_ty, name = name));
326 }
327
Alex Crichtona9adfbf2015-09-09 23:21:27 -0700328 fn assert_no_generics(&self, _i: &ast::Item, generics: &ast::Generics) {
329 assert!(generics.lifetimes.len() == 0);
330 assert!(generics.ty_params.len() == 0);
331 assert!(generics.where_clause.predicates.len() == 0);
Alex Crichton8e5f0cd2015-09-09 22:46:19 -0700332 }
Alex Crichton0df7c102015-09-10 16:35:37 -0700333
334 fn ty2name(&self, ty: &ast::Ty) -> String {
335 match ty.node {
336 ast::TyPath(_, ref path) => {
337 path.segments.last().unwrap().identifier.to_string()
338 }
339 ast::TyPtr(ref t) => {
340 format!("*{} {}", match t.mutbl {
341 ast::MutImmutable => "const",
342 ast::MutMutable => "mut",
343 }, self.ty2name(&t.ty))
344 }
345 _ => panic!("unknown ty {:?}", ty),
346 }
347 }
Alex Crichton8e5f0cd2015-09-09 22:46:19 -0700348}
349
Alex Crichtona9adfbf2015-09-09 23:21:27 -0700350impl<'a, 'v> Visitor<'v> for TestGenerator<'a> {
Alex Crichton8e5f0cd2015-09-09 22:46:19 -0700351 fn visit_item(&mut self, i: &'v ast::Item) {
352 match i.node {
353 ast::ItemTy(_, ref generics) => {
Alex Crichtona9adfbf2015-09-09 23:21:27 -0700354 self.assert_no_generics(i, generics);
355 self.test_type(&i.ident.to_string());
356 }
357
358 ast::ItemStruct(ref s, ref generics) => {
359 self.assert_no_generics(i, generics);
360 let is_c = i.attrs.iter().any(|a| {
361 attr::find_repr_attrs(self.sh, a).iter().any(|a| {
362 *a == ReprAttr::ReprExtern
363 })
364 });
365 if !is_c {
366 panic!("{} is not marked #[repr(C)]", i.ident);
367 }
368 self.test_struct(&i.ident.to_string(), s);
Alex Crichton8e5f0cd2015-09-09 22:46:19 -0700369 }
370
Alex Crichton0df7c102015-09-10 16:35:37 -0700371 ast::ItemConst(ref ty, _) => {
372 let ty = self.ty2name(ty);
373 self.test_const(&i.ident.to_string(), &ty);
374 }
375
Alex Crichton8e5f0cd2015-09-09 22:46:19 -0700376 _ => {}
377 }
378 visit::walk_item(self, i)
379 }
380}
Alex Crichton310d6232015-09-10 10:56:31 -0700381
382impl<'v> Visitor<'v> for StructFinder {
383 fn visit_item(&mut self, i: &'v ast::Item) {
384 match i.node {
385 ast::ItemStruct(..) => {
386 self.structs.insert(i.ident.to_string());
387 }
388
389 _ => {}
390 }
391 visit::walk_item(self, i)
392 }
393}