blob: 194d8f3625f39aad827b2ba0be207f6054e46caa [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 }
Alex Crichtonac2bd852015-09-10 17:21:20 -070044 if self.target.contains("msvc") {
45 ret.push("alignof __alignof");
46 }
Alex Crichton0df7c102015-09-10 16:35:37 -070047 return ret
48 }
49
Alex Crichton310d6232015-09-10 10:56:31 -070050 fn headers(&self) -> Vec<&'static str> {
Alex Crichtonac2bd852015-09-10 17:21:20 -070051 let mut base = Vec::new();
52
53 if self.target.contains("windows") {
54 base.push("windows.h");
55 }
56
57 base.extend(&[
Alex Crichton0df7c102015-09-10 16:35:37 -070058 "errno.h",
59 "fcntl.h",
Alex Crichton0df7c102015-09-10 16:35:37 -070060 "limits.h",
Alex Crichton310d6232015-09-10 10:56:31 -070061 "stddef.h",
62 "stdint.h",
Alex Crichton0df7c102015-09-10 16:35:37 -070063 "stdio.h",
64 "stdlib.h",
Alex Crichton310d6232015-09-10 10:56:31 -070065 "sys/stat.h",
Alex Crichton310d6232015-09-10 10:56:31 -070066 "sys/types.h",
Alex Crichton310d6232015-09-10 10:56:31 -070067 "time.h",
Alex Crichton310d6232015-09-10 10:56:31 -070068 "wchar.h",
Alex Crichtonac2bd852015-09-10 17:21:20 -070069 ]);
Alex Crichton310d6232015-09-10 10:56:31 -070070
71 if self.target.contains("apple-darwin") {
72 base.push("mach/mach_time.h");
73 }
Alex Crichtonac2bd852015-09-10 17:21:20 -070074
Alex Crichton310d6232015-09-10 10:56:31 -070075 if self.target.contains("unknown-linux") {
76 base.push("linux/if_packet.h");
77 base.push("net/ethernet.h");
78 }
79
Alex Crichtonac2bd852015-09-10 17:21:20 -070080 if self.target.contains("windows") {
81 base.push("ws2tcpip.h");
82 } else {
83 base.push("glob.h");
84 base.push("ifaddrs.h");
85 base.push("net/if.h");
86 base.push("netdb.h");
87 base.push("netinet/in.h");
88 base.push("netinet/ip.h");
89 base.push("netinet/tcp.h");
90 base.push("pthread.h");
91 base.push("signal.h");
92 base.push("stdalign.h");
93 base.push("sys/mman.h");
94 base.push("sys/resource.h");
95 base.push("sys/socket.h");
96 base.push("sys/time.h");
97 base.push("sys/un.h");
98 base.push("utime.h");
99 base.push("unistd.h");
100 }
101
Alex Crichton310d6232015-09-10 10:56:31 -0700102 return base
103 }
104
105 fn rust2c(&self, ty: &str) -> String {
106 match ty {
107 t if t.starts_with("c_") => {
108 match &ty[2..].replace("long", " long")[..] {
109 s if s.starts_with("u") => format!("unsigned {}", &s[1..]),
110 "short" => format!("short"),
111 s if s.starts_with("s") => format!("signed {}", &s[1..]),
112 s => s.to_string(),
113 }
114 }
115 "ip6_mreq" => "struct ipv6_mreq".to_string(),
116 "glob_t" => "glob_t".to_string(),
117 t if t.starts_with("pthread") => t.to_string(),
118
119 t if self.structs.contains(t) => format!("struct {}", t),
120
121 t => t.to_string(),
122 }
123 }
124
125 fn rust2cfield(&self, struct_: &str, field: &str) -> String {
126 match field {
127 s if s.ends_with("_nsec") && struct_ == "stat" => {
128 if self.target.contains("apple-darwin") {
129 s.replace("_nsec", "spec.tv_nsec")
130 } else {
131 s.replace("e_nsec", ".tv_nsec")
132 }
133 }
134 s => s.to_string(),
135 }
136 }
137
138 fn cfg_list(&self) -> Vec<(&'static str, Option<&'static str>)> {
139 let mut ret = Vec::new();
140 let (arch, target_pointer_width) = if self.target.starts_with("x86_64") {
141 ("x86_64", "64")
142 } else if self.target.starts_with("i686") {
143 ("x86", "32")
144 } else {
145 panic!("unknown arch/pointer width: {}", self.target)
146 };
147 let (os, family, env) = if self.target.contains("unknown-linux") {
148 ("linux", "unix", "gnu")
149 } else if self.target.contains("apple-darwin") {
150 ("macos", "unix", "")
Alex Crichtonac2bd852015-09-10 17:21:20 -0700151 } else if self.target.contains("windows-msvc") {
152 ("windows", "windows", "msvc")
153 } else if self.target.contains("windows-gnu") {
154 ("windows", "windows", "gnu")
Alex Crichton310d6232015-09-10 10:56:31 -0700155 } else {
156 panic!("unknown os/family width: {}", self.target)
157 };
158
159 ret.push((family, None));
160 ret.push(("target_os", Some(os)));
161 ret.push(("target_family", Some(family)));
162 ret.push(("target_arch", Some(arch)));
163 // skip endianness
164 ret.push(("target_pointer_width", Some(target_pointer_width)));
165 ret.push(("target_env", Some(env)));
166
167 return ret
168 }
Alex Crichton8e5f0cd2015-09-09 22:46:19 -0700169}
170
171fn main() {
Alex Crichton310d6232015-09-10 10:56:31 -0700172 // Prep the test generator
173 let target = t!(env::var("TARGET"));
174 let out = PathBuf::from(env::var_os("OUT_DIR").unwrap());
175 let rust_out = BufWriter::new(t!(File::create(out.join("all.rs"))));
176 let c_out = BufWriter::new(t!(File::create(out.join("all.c"))));
Alex Crichton16083062015-09-09 22:59:24 -0700177 let sess = ParseSess::new();
Alex Crichton310d6232015-09-10 10:56:31 -0700178 let mut tg = TestGenerator {
179 target: target,
180 rust: Box::new(rust_out),
181 c: Box::new(c_out),
182 sh: &sess.span_diagnostic,
183 structs: HashSet::new(),
184 };
185
186 // Parse the libc crate
Alex Crichton8e5f0cd2015-09-09 22:46:19 -0700187 let src = Path::new("../src/lib.rs");
188 let cfg = Vec::new();
189 let mut krate = parse::parse_crate_from_file(src, cfg, &sess);
Alex Crichton8e5f0cd2015-09-09 22:46:19 -0700190
Alex Crichton310d6232015-09-10 10:56:31 -0700191 // Strip the crate down to just what's configured for our target
192 for (k, v) in tg.cfg_list() {
193 let s = InternedString::new;
194 krate.config.push(match v {
195 Some(v) => attr::mk_name_value_item_str(s(k), s(v)),
196 None => attr::mk_word_item(s(k)),
197 });
198 }
Alex Crichton8e5f0cd2015-09-09 22:46:19 -0700199 let mut gated_cfgs = Vec::new();
200 let krate = syntax::config::strip_unconfigured_items(&sess.span_diagnostic,
201 krate,
202 &mut gated_cfgs);
203
Alex Crichton310d6232015-09-10 10:56:31 -0700204 // Probe the crate to find all structs (used to convert type names to names
205 // in C).
206 let mut structs = StructFinder {
207 structs: HashSet::new(),
208 };
209 visit::walk_crate(&mut structs, &krate);
210 tg.structs = structs.structs;
Alex Crichton8e5f0cd2015-09-09 22:46:19 -0700211
Alex Crichton0df7c102015-09-10 16:35:37 -0700212 // Prep the C file by emitting header stuff
213 for define in tg.defines() {
214 t!(writeln!(tg.c, "#define {}", define));
215 }
Alex Crichton310d6232015-09-10 10:56:31 -0700216 for header in tg.headers() {
217 t!(writeln!(tg.c, "#include <{}>", header));
Alex Crichton16083062015-09-09 22:59:24 -0700218 }
Alex Crichton0df7c102015-09-10 16:35:37 -0700219
220 // Walk the crate, emitting test cases for everything found
Alex Crichton310d6232015-09-10 10:56:31 -0700221 visit::walk_crate(&mut tg, &krate);
Alex Crichton16083062015-09-09 22:59:24 -0700222
Alex Crichton310d6232015-09-10 10:56:31 -0700223 // Compile our C shim to be linked into tests
Alex Crichtonac2bd852015-09-10 17:21:20 -0700224 let mut cfg = gcc::Config::new();
225 cfg.file(out.join("all.c"));
226
227 if tg.target.contains("msvc") {
228 cfg.flag("/W3").flag("/Wall").flag("/WX");
229 } else {
Alex Crichtonb01a8cc2015-09-10 17:37:22 -0700230 cfg.flag("-Wall").flag("-Wextra").flag("-Werror")
231 .arg("-Wno-unused-parameter");
Alex Crichtonac2bd852015-09-10 17:21:20 -0700232 }
233
234 drop(tg);
235 cfg.compile("liball.a");
Alex Crichton8e5f0cd2015-09-09 22:46:19 -0700236}
237
Alex Crichtona9adfbf2015-09-09 23:21:27 -0700238impl<'a> TestGenerator<'a> {
239 fn test_type(&mut self, ty: &str) {
Alex Crichton310d6232015-09-10 10:56:31 -0700240 match ty {
241 "sighandler_t" => return,
242 _ => {}
243 }
244 let c = self.rust2c(ty);
245 self.test_size_align(ty, &c);
Alex Crichtona9adfbf2015-09-09 23:21:27 -0700246 }
Alex Crichton16083062015-09-09 22:59:24 -0700247
Alex Crichton3e5155b2015-09-09 23:46:19 -0700248 fn test_struct(&mut self, ty: &str, s: &ast::StructDef) {
Alex Crichton310d6232015-09-10 10:56:31 -0700249 let cty = self.rust2c(ty);
Alex Crichtona9adfbf2015-09-09 23:21:27 -0700250 self.test_size_align(ty, &cty);
Alex Crichton3e5155b2015-09-09 23:46:19 -0700251
Alex Crichton310d6232015-09-10 10:56:31 -0700252 t!(writeln!(self.rust, r#"
Alex Crichton3e5155b2015-09-09 23:46:19 -0700253 #[test]
254 fn field_offset_size_{ty}() {{
Alex Crichton310d6232015-09-10 10:56:31 -0700255 "#, ty = ty));
Alex Crichton3e5155b2015-09-09 23:46:19 -0700256 for field in s.fields.iter() {
257 let name = match field.node.kind {
258 ast::NamedField(name, ast::Public) => name,
259 ast::NamedField(_, ast::Inherited) => continue,
260 ast::UnnamedField(..) => panic!("no tuple structs in FFI"),
261 };
262
Alex Crichton310d6232015-09-10 10:56:31 -0700263 let cfield = self.rust2cfield(ty, &name.to_string());
Alex Crichton3e5155b2015-09-09 23:46:19 -0700264
Alex Crichton310d6232015-09-10 10:56:31 -0700265 t!(writeln!(self.c, r#"
Alex Crichton671376b2015-09-10 17:39:03 -0700266 uint64_t __test_offset_{ty}_{rust_field}(void) {{
Alex Crichton3e5155b2015-09-09 23:46:19 -0700267 return offsetof({cty}, {c_field});
268 }}
Alex Crichton671376b2015-09-10 17:39:03 -0700269 uint64_t __test_size_{ty}_{rust_field}(void) {{
Alex Crichton3e5155b2015-09-09 23:46:19 -0700270 {cty}* foo = NULL;
271 return sizeof(foo->{c_field});
272 }}
Alex Crichton310d6232015-09-10 10:56:31 -0700273 "#, ty = ty, cty = cty, rust_field = name, c_field = cfield));
274 t!(writeln!(self.rust, r#"
Alex Crichton3e5155b2015-09-09 23:46:19 -0700275 extern {{
276 fn __test_offset_{ty}_{field}() -> u64;
277 fn __test_size_{ty}_{field}() -> u64;
278 }}
279 unsafe {{
280 let foo = 0 as *const {ty};
281 same(offset_of!({ty}, {field}),
282 __test_offset_{ty}_{field}(),
283 "field offset {field} of {ty}");
284 same(mem::size_of_val(&(*foo).{field}) as u64,
285 __test_size_{ty}_{field}(),
286 "field size {field} of {ty}");
287 }}
Alex Crichton310d6232015-09-10 10:56:31 -0700288 "#, ty = ty, field = name));
Alex Crichton3e5155b2015-09-09 23:46:19 -0700289 }
Alex Crichton310d6232015-09-10 10:56:31 -0700290 t!(writeln!(self.rust, r#"
Alex Crichton3e5155b2015-09-09 23:46:19 -0700291 }}
Alex Crichton310d6232015-09-10 10:56:31 -0700292 "#));
Alex Crichtona9adfbf2015-09-09 23:21:27 -0700293 }
294
295 fn test_size_align(&mut self, rust: &str, c: &str) {
Alex Crichton310d6232015-09-10 10:56:31 -0700296 t!(writeln!(self.c, r#"
Alex Crichton671376b2015-09-10 17:39:03 -0700297 uint64_t __test_size_{ty}(void) {{ return sizeof({cty}); }}
298 uint64_t __test_align_{ty}(void) {{ return alignof({cty}); }}
Alex Crichton310d6232015-09-10 10:56:31 -0700299 "#, ty = rust, cty = c));
300 t!(writeln!(self.rust, r#"
Alex Crichton8e5f0cd2015-09-09 22:46:19 -0700301 #[test]
Alex Crichtona9adfbf2015-09-09 23:21:27 -0700302 fn size_align_{ty}() {{
Alex Crichton16083062015-09-09 22:59:24 -0700303 extern {{
Alex Crichtona9adfbf2015-09-09 23:21:27 -0700304 fn __test_size_{ty}() -> u64;
305 fn __test_align_{ty}() -> u64;
Alex Crichton16083062015-09-09 22:59:24 -0700306 }}
307 unsafe {{
Alex Crichton3e5155b2015-09-09 23:46:19 -0700308 same(mem::size_of::<{ty}>() as u64,
309 __test_size_{ty}(), "size");
Alex Crichtonc8b895c2015-09-10 13:24:15 -0700310 same(align::<{ty}>() as u64,
Alex Crichton3e5155b2015-09-09 23:46:19 -0700311 __test_align_{ty}(), "align");
Alex Crichton16083062015-09-09 22:59:24 -0700312 }}
Alex Crichton8e5f0cd2015-09-09 22:46:19 -0700313 }}
Alex Crichton310d6232015-09-10 10:56:31 -0700314 "#, ty = rust));
Alex Crichtona9adfbf2015-09-09 23:21:27 -0700315 }
316
Alex Crichton0df7c102015-09-10 16:35:37 -0700317 fn test_const(&mut self, name: &str, rust_ty: &str) {
318 let cty = self.rust2c(&rust_ty.replace("*mut ", "")
319 .replace("*const ", ""));
320 let ptrs = rust_ty.matches("*").count();
321 let cty = format!("{}{}", cty,
322 iter::repeat("*").take(ptrs).collect::<String>());
323 let cast = if name == "SIG_IGN" {"(size_t)"} else {""};
324 t!(writeln!(self.c, r#"
Alex Crichtone7afdd82015-09-10 17:15:20 -0700325 int __test_const_{name}({cty} *out) {{
326 int ret = 0;
327 #if defined({name})
328 *out = {cast}({name});
329 ret = 1;
330 #endif
331 return ret;
332 }}
Alex Crichton0df7c102015-09-10 16:35:37 -0700333 "#, name = name, cast = cast, cty = cty));
334 t!(writeln!(self.rust, r#"
335 #[test]
336 fn const_{name}() {{
337 extern {{
Alex Crichtone7afdd82015-09-10 17:15:20 -0700338 fn __test_const_{name}(out: *mut {ty}) -> c_int;
Alex Crichton0df7c102015-09-10 16:35:37 -0700339 }}
340 unsafe {{
Alex Crichtone7afdd82015-09-10 17:15:20 -0700341 let mut o = mem::zeroed();
342 if __test_const_{name}(&mut o) == 0 {{
343 panic!("not defined");
344 }} else {{
345 same({name}, o, "value");
346 }}
Alex Crichton0df7c102015-09-10 16:35:37 -0700347 }}
348 }}
349 "#, ty = rust_ty, name = name));
350 }
351
Alex Crichtona9adfbf2015-09-09 23:21:27 -0700352 fn assert_no_generics(&self, _i: &ast::Item, generics: &ast::Generics) {
353 assert!(generics.lifetimes.len() == 0);
354 assert!(generics.ty_params.len() == 0);
355 assert!(generics.where_clause.predicates.len() == 0);
Alex Crichton8e5f0cd2015-09-09 22:46:19 -0700356 }
Alex Crichton0df7c102015-09-10 16:35:37 -0700357
358 fn ty2name(&self, ty: &ast::Ty) -> String {
359 match ty.node {
360 ast::TyPath(_, ref path) => {
361 path.segments.last().unwrap().identifier.to_string()
362 }
363 ast::TyPtr(ref t) => {
364 format!("*{} {}", match t.mutbl {
365 ast::MutImmutable => "const",
366 ast::MutMutable => "mut",
367 }, self.ty2name(&t.ty))
368 }
369 _ => panic!("unknown ty {:?}", ty),
370 }
371 }
Alex Crichton8e5f0cd2015-09-09 22:46:19 -0700372}
373
Alex Crichtona9adfbf2015-09-09 23:21:27 -0700374impl<'a, 'v> Visitor<'v> for TestGenerator<'a> {
Alex Crichton8e5f0cd2015-09-09 22:46:19 -0700375 fn visit_item(&mut self, i: &'v ast::Item) {
376 match i.node {
377 ast::ItemTy(_, ref generics) => {
Alex Crichtona9adfbf2015-09-09 23:21:27 -0700378 self.assert_no_generics(i, generics);
379 self.test_type(&i.ident.to_string());
380 }
381
382 ast::ItemStruct(ref s, ref generics) => {
383 self.assert_no_generics(i, generics);
384 let is_c = i.attrs.iter().any(|a| {
385 attr::find_repr_attrs(self.sh, a).iter().any(|a| {
386 *a == ReprAttr::ReprExtern
387 })
388 });
389 if !is_c {
390 panic!("{} is not marked #[repr(C)]", i.ident);
391 }
392 self.test_struct(&i.ident.to_string(), s);
Alex Crichton8e5f0cd2015-09-09 22:46:19 -0700393 }
394
Alex Crichton0df7c102015-09-10 16:35:37 -0700395 ast::ItemConst(ref ty, _) => {
396 let ty = self.ty2name(ty);
397 self.test_const(&i.ident.to_string(), &ty);
398 }
399
Alex Crichton8e5f0cd2015-09-09 22:46:19 -0700400 _ => {}
401 }
402 visit::walk_item(self, i)
403 }
404}
Alex Crichton310d6232015-09-10 10:56:31 -0700405
406impl<'v> Visitor<'v> for StructFinder {
407 fn visit_item(&mut self, i: &'v ast::Item) {
408 match i.node {
409 ast::ItemStruct(..) => {
410 self.structs.insert(i.ident.to_string());
411 }
412
413 _ => {}
414 }
415 visit::walk_item(self, i)
416 }
417}