diff --git a/libc-test/Cargo.lock b/libc-test/Cargo.lock index 23d47b0a50e2cb8535792add692e42040b6e1e4c..1726c5370150b2099a6ad699a9327c8830a00bf3 100644 --- a/libc-test/Cargo.lock +++ b/libc-test/Cargo.lock @@ -2,7 +2,7 @@ name = "libc-test" version = "0.1.0" dependencies = [ - "gcc 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)", + "gcc 0.3.13 (git+https://github.com/alexcrichton/gcc-rs)", "libc 0.1.10", "syntex_syntax 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -24,7 +24,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "gcc" version = "0.3.13" -source = "registry+https://github.com/rust-lang/crates.io-index" +source = "git+https://github.com/alexcrichton/gcc-rs#e429c775dcbb03eb049bcb7f7215cf8d9ee3b837" dependencies = [ "advapi32-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/libc-test/Cargo.toml b/libc-test/Cargo.toml index e918ec21c125afba9f8bf0950b62387481316108..bab5699f5be3925618739699278b7f023466466d 100644 --- a/libc-test/Cargo.toml +++ b/libc-test/Cargo.toml @@ -9,7 +9,7 @@ libc = { path = ".." } [build-dependencies] syntex_syntax = "0.13.0" -gcc = "0.3" +gcc = { git = "https://github.com/alexcrichton/gcc-rs" } [lib] name = "libc_test" diff --git a/libc-test/build.rs b/libc-test/build.rs index d9aadc11f5d663d27981df6ff3e14076b40ef4d9..9b4527ec1658924b6c30b05b06aedf37e3a6f91d 100644 --- a/libc-test/build.rs +++ b/libc-test/build.rs @@ -8,10 +8,13 @@ use std::io::BufWriter; use std::io::prelude::*; use std::path::{Path, PathBuf}; +use syntax::abi::Abi; use syntax::ast; -use syntax::diagnostic::SpanHandler; -use syntax::parse::token::InternedString; use syntax::attr::{self, ReprAttr}; +use syntax::diagnostic::SpanHandler; +use syntax::ext::base::SyntaxExtension; +use syntax::ext::expand; +use syntax::parse::token::{intern, InternedString}; use syntax::parse::{self, ParseSess}; use syntax::visit::{self, Visitor}; @@ -28,6 +31,7 @@ struct TestGenerator<'a> { c: Box<Write>, sh: &'a SpanHandler, structs: HashSet<String>, + abi: Abi, } struct StructFinder { @@ -37,12 +41,21 @@ struct StructFinder { impl<'a> TestGenerator<'a> { fn defines(&self) -> Vec<&'static str> { let mut ret = Vec::new(); + + // Pull in extra goodies on linux if self.target.contains("unknown-linux") { ret.push("_GNU_SOURCE"); } + + // MSVC doesn't have stdalign.h so get alignof ourselves if self.target.contains("msvc") { ret.push("alignof __alignof"); } + + // Pull in extra goodies on mingw + if self.target.contains("windows") { + ret.push("_WIN32_WINNT 0x8000"); + } return ret } @@ -82,6 +95,11 @@ impl<'a> TestGenerator<'a> { base.push("windows.h"); base.push("process.h"); base.push("ws2ipdef.h"); + + if self.target.contains("gnu") { + base.push("stdalign.h"); + base.push("ws2tcpip.h"); + } } else { base.push("ctype.h"); base.push("dirent.h"); @@ -123,13 +141,19 @@ impl<'a> TestGenerator<'a> { s => s.to_string(), } } + // Perhaps this should be renamed in libc... "ip6_mreq" => "struct ipv6_mreq".to_string(), + + // Just pass all these through, no need for a "struct" prefix "glob_t" | "FILE" | "DIR" | "fpos_t" => ty.to_string(), t if t.starts_with("pthread") => t.to_string(), + // Windows uppercase structs don't have `struct` in front, there's a + // few special cases for windows, and then otherwise put `struct` in + // front of everything. t if self.structs.contains(t) => { if windows && ty.chars().next().unwrap().is_uppercase() { t.to_string() @@ -142,14 +166,18 @@ impl<'a> TestGenerator<'a> { } } + // Fixup a few types on windows that don't actually exist. "time64_t" if windows => "__time64_t".to_string(), "ssize_t" if windows => "SSIZE_T".to_string(), + t => t.to_string(), } } fn rust2cfield(&self, struct_: &str, field: &str) -> String { match field { + // Our stat *_nsec fields normally don't actually exist but are part + // of a timeval struct s if s.ends_with("_nsec") && struct_ == "stat" => { if self.target.contains("apple-darwin") { s.replace("_nsec", "spec.tv_nsec") @@ -207,12 +235,21 @@ fn main() { c: Box::new(c_out), sh: &sess.span_diagnostic, structs: HashSet::new(), + abi: Abi::C, }; // 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); + let krate = parse::parse_crate_from_file(src, cfg, &sess); + + // expand macros + let ecfg = expand::ExpansionConfig::default("libc".to_string()); + let exts = vec![ + (intern("macro_rules"), SyntaxExtension::MacroRulesTT), + ]; + let mut krate = expand::expand_crate(&sess, ecfg, Vec::new(), + exts, &mut Vec::new(), krate); // Strip the crate down to just what's configured for our target for (k, v) in tg.cfg_list() { @@ -359,8 +396,22 @@ impl<'a> TestGenerator<'a> { } fn test_const(&mut self, name: &str, rust_ty: &str) { + let mingw = self.target.contains("windows-gnu"); + + // Apparently these don't exist in mingw headers? + match name { + "MEM_RESET_UNDO" | + "FILE_ATTRIBUTE_NO_SCRUB_DATA" | + "FILE_ATTRIBUTE_INTEGRITY_STREAM" | + "ERROR_NOTHING_TO_TERMINATE" if mingw => return, + _ => {} + } + let cty = self.rust_ty_to_c_ty(rust_ty); + + // SIG_IGN has weird types on platforms, just worry about it as a size_t let cast = if name == "SIG_IGN" {"(size_t)"} else {""}; + t!(writeln!(self.c, r#" int __test_const_{name}({cty} *outptr) {{ *outptr = {cast}({name}); @@ -387,7 +438,7 @@ impl<'a> TestGenerator<'a> { fn test_extern_fn(&mut self, name: &str, cname: &str, args: &[String], ret: &str, - variadic: bool) { + variadic: bool, abi: Abi) { match name { // manually verified "execv" | @@ -408,11 +459,20 @@ impl<'a> TestGenerator<'a> { .connect(", ") + if variadic {", ..."} else {""} }; let cret = self.rust_ty_to_c_ty(ret); + let abi = match abi { + Abi::C => "", + Abi::Stdcall => "__stdcall ", + Abi::System if self.target.contains("i686-pc-windows") => { + "__stdcall " + } + Abi::System => "", + a => panic!("unknown ABI: {}", a), + }; t!(writeln!(self.c, r#" - {ret} (*__test_fn_{name}(void))({args}) {{ + {ret} ({abi}*__test_fn_{name}(void))({args}) {{ return {cname}; }} - "#, name = name, cname = cname, args = args, ret = cret)); + "#, name = name, cname = cname, args = args, ret = cret, abi = abi)); t!(writeln!(self.rust, r#" #[test] #[cfg_attr(windows, ignore)] // FIXME -- dllimport weirdness? @@ -473,6 +533,7 @@ impl<'a> TestGenerator<'a> { impl<'a, 'v> Visitor<'v> for TestGenerator<'a> { fn visit_item(&mut self, i: &'v ast::Item) { + let prev_abi = self.abi; match i.node { ast::ItemTy(_, ref generics) => { self.assert_no_generics(i.ident, generics); @@ -497,9 +558,14 @@ impl<'a, 'v> Visitor<'v> for TestGenerator<'a> { self.test_const(&i.ident.to_string(), &ty); } + ast::ItemForeignMod(ref fm) => { + self.abi = fm.abi; + } + _ => {} } - visit::walk_item(self, i) + visit::walk_item(self, i); + self.abi = prev_abi; } fn visit_foreign_item(&mut self, i: &'v ast::ForeignItem) { @@ -514,8 +580,9 @@ impl<'a, 'v> Visitor<'v> for TestGenerator<'a> { } _ => i.ident.to_string(), }; + let abi = self.abi; self.test_extern_fn(&i.ident.to_string(), &cname, &args, &ret, - variadic); + variadic, abi); } ast::ForeignItemStatic(_, _) => { } diff --git a/src/lib.rs b/src/lib.rs index 9c986d6a821548d86d36161bc1476247c83d21a4..fc2e6c2a2340e4d49c3fa3854d89acd4489da651 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -67,6 +67,9 @@ #![allow(bad_style, raw_pointer_derive)] +#[macro_use] +mod macros; + // Explicit export lists for the intersection (provided here) mean that // you can write more-platform-agnostic code if you stick to just these // symbols. @@ -1829,10 +1832,13 @@ pub mod types { pub type clock_t = i32; - #[cfg(target_arch = "x86")] - pub type time_t = i32; - #[cfg(target_arch = "x86_64")] - pub type time_t = i64; + cfg_if! { + if #[cfg(all(target_arch = "x86", target_env = "gnu"))] { + pub type time_t = i32; + } else { + pub type time_t = i64; + } + } pub type wchar_t = u16; } @@ -2508,8 +2514,6 @@ pub mod consts { pub const BUFSIZ : c_uint = 512; pub const FOPEN_MAX : c_uint = 20; pub const FILENAME_MAX : c_uint = 260; - pub const L_tmpnam : c_uint = 260; - pub const TMP_MAX : c_uint = 0x7fff_ffff; pub const WSAEINTR: c_int = 10004; pub const WSAEBADF: c_int = 10009; @@ -2563,6 +2567,16 @@ pub mod consts { pub const WSAEINVALIDPROCTABLE: c_int = 10104; pub const WSAEINVALIDPROVIDER: c_int = 10105; pub const WSAEPROVIDERFAILEDINIT: c_int = 10106; + + cfg_if! { + if #[cfg(all(target_env = "gnu"))] { + pub const L_tmpnam : c_uint = 14; + pub const TMP_MAX : c_uint = 0x7fff; + } else { + pub const L_tmpnam : c_uint = 260; + pub const TMP_MAX : c_uint = 0x7fff_ffff; + } + } } pub mod c99 { } @@ -6141,9 +6155,8 @@ pub mod funcs { #[cfg(windows)] pub mod bsd43 { - use types::common::c95::{c_void}; use types::os::common::bsd44::{sockaddr, SOCKET}; - use types::os::arch::c95::c_int; + use types::os::arch::c95::{c_int, c_char}; extern "system" { pub fn socket(domain: c_int, ty: c_int, protocol: c_int) -> SOCKET; @@ -6159,17 +6172,17 @@ pub mod funcs { pub fn getsockname(socket: SOCKET, address: *mut sockaddr, address_len: *mut c_int) -> c_int; pub fn setsockopt(socket: SOCKET, level: c_int, name: c_int, - value: *const c_void, + value: *const c_char, option_len: c_int) -> c_int; pub fn closesocket(socket: SOCKET) -> c_int; - pub fn recv(socket: SOCKET, buf: *mut c_void, len: c_int, + pub fn recv(socket: SOCKET, buf: *mut c_char, len: c_int, flags: c_int) -> c_int; - pub fn send(socket: SOCKET, buf: *const c_void, len: c_int, + pub fn send(socket: SOCKET, buf: *const c_char, len: c_int, flags: c_int) -> c_int; - pub fn recvfrom(socket: SOCKET, buf: *mut c_void, len: c_int, + pub fn recvfrom(socket: SOCKET, buf: *mut c_char, len: c_int, flags: c_int, addr: *mut sockaddr, addrlen: *mut c_int) -> c_int; - pub fn sendto(socket: SOCKET, buf: *const c_void, len: c_int, + pub fn sendto(socket: SOCKET, buf: *const c_char, len: c_int, flags: c_int, addr: *const sockaddr, addrlen: c_int) -> c_int; pub fn shutdown(socket: SOCKET, how: c_int) -> c_int; @@ -6414,7 +6427,7 @@ pub mod funcs { lpNumberOfBytesRead: LPDWORD, lpOverlapped: LPOVERLAPPED) -> BOOL; pub fn WriteFile(hFile: HANDLE, - lpBuffer: LPVOID, + lpBuffer: LPCVOID, nNumberOfBytesToWrite: DWORD, lpNumberOfBytesWritten: LPDWORD, lpOverlapped: LPOVERLAPPED) -> BOOL; diff --git a/src/macros.rs b/src/macros.rs new file mode 100644 index 0000000000000000000000000000000000000000..3b096a53f10155df81d0ed7c1e0bd35ca1b8e037 --- /dev/null +++ b/src/macros.rs @@ -0,0 +1,75 @@ +//! A macro for defining #[cfg] if-else statements. +//! +//! The macro provided by this crate, `cfg_if`, is similar to the `if/elif` C +//! preprocessor macro by allowing definition of a cascade of `#[cfg]` cases, +//! emitting the implementation which matches first. +//! +//! This allows you to conveniently provide a long list #[cfg]'d blocks of code +//! without having to rewrite each clause multiple times. + +macro_rules! cfg_if { + ($( + if #[cfg($($meta:meta),*)] { $($it:item)* } + ) else * else { + $($it2:item)* + }) => { + __cfg_if_items! { + () ; + $( ( ($($meta),*) ($($it)*) ), )* + ( () ($($it2)*) ), + } + } +} + +#[doc(hidden)] +macro_rules! __cfg_if_items { + (($($not:meta,)*) ; ) => {}; + (($($not:meta,)*) ; ( ($($m:meta),*) ($($it:item)*) ), $($rest:tt)*) => { + __cfg_if_apply! { cfg(all($($m,)* not(any($($not),*)))), $($it)* } + __cfg_if_items! { ($($not,)* $($m,)*) ; $($rest)* } + } +} + +#[doc(hidden)] +macro_rules! __cfg_if_apply { + ($m:meta, $($it:item)*) => { + $(#[$m] $it)* + } +} + +#[cfg(test)] +mod tests { + cfg_if! { + if #[cfg(test)] { + use std::option::Option as Option2; + fn works1() -> Option2<u32> { Some(1) } + } else { + fn works1() -> Option<u32> { None } + } + } + + cfg_if! { + if #[cfg(foo)] { + fn works2() -> bool { false } + } else if #[cfg(test)] { + fn works2() -> bool { true } + } else { + fn works2() -> bool { false } + } + } + + cfg_if! { + if #[cfg(foo)] { + fn works3() -> bool { false } + } else { + fn works3() -> bool { true } + } + } + + #[test] + fn it_works() { + assert!(works1().is_some()); + assert!(works2()); + assert!(works3()); + } +}