use std::env;
use std::error;
use std::fmt;
use std::io;
use std::path::{Path, PathBuf};
static LIBUV_VERSION: &str = "1.36.0";
#[derive(Debug)]
enum Error {
BindgenError,
PathError(String, io::Error),
}
impl error::Error for Error {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match self {
Error::PathError(_, err) => Some(err),
_ => None,
}
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::BindgenError => write!(f, "Failed to generate ffi bindings"),
Error::PathError(dir, source) => write!(f, "Path error for `{}`: {}", dir, source),
}
}
}
type Result<T> = std::result::Result<T, Error>;
fn build_pkgconfig_max_version() -> String {
let dotidx = LIBUV_VERSION.find('.').unwrap();
let dotidx2 = LIBUV_VERSION[(dotidx + 1)..].find('.').unwrap() + dotidx + 1;
let next_minor_version = LIBUV_VERSION[(dotidx + 1)..dotidx2].parse::<usize>().unwrap() + 1;
format!("{}.{}.0", &LIBUV_VERSION[..dotidx], next_minor_version)
}
fn try_pkgconfig() -> Option<Option<PathBuf>> {
if env::var("TARGET") != env::var("HOST") {
return None;
}
let max_version = build_pkgconfig_max_version();
let pkgconfig_result = pkg_config::Config::new()
.range_version(&LIBUV_VERSION[..]..max_version.as_ref())
.env_metadata(true)
.probe("libuv");
if let Ok(libuv) = pkgconfig_result {
println!("Resolving libuv with pkg-config");
for include_path in libuv.include_paths {
let header_path = include_path.join("uv.h");
if header_path.exists() {
return Some(Some(include_path));
}
}
return Some(None);
}
return None
}
#[cfg(windows)]
fn windows_version_specific_settings(build: &mut cc::Build) {
match nt_version::get() {
(6, 0, _) => {
println!("cargo:rustc-link-lib=advapi32");
println!("cargo:rustc-link-lib=psapi");
println!("cargo:rustc-link-lib=shell32");
println!("cargo:rustc-link-lib=user32");
build.define("_WIN32_WINNT", "0x0600")
},
(6, 1, _) => build.define("_WIN32_WINNT", "0x0601"), (6, 2, _) => build.define("_WIN32_WINNT", "0x0602"), (6, 3, _) => build.define("_WIN32_WINNT", "0x0603"), (10, _, _) => build.define("_WIN32_WINNT", "0x0A00"), _ => panic!("This version of Windows is unsupported by libuv.")
};
}
#[cfg(not(windows))]
fn windows_version_specific_settings(_build: &mut cc::Build) {}
fn build<P: AsRef<Path>>(source_path: &P) -> Result<()> {
let src_path = source_path.as_ref().join("src");
let unix_path = src_path.join("unix");
let target = env::var("TARGET").unwrap();
let android = target.ends_with("-android") || target.ends_with("-androideabi");
let apple = target.contains("-apple-");
let dragonfly = target.ends_with("-dragonfly");
let freebsd = target.ends_with("-freebsd");
let linux = target.contains("-linux-");
let netbsd = target.ends_with("-netbsd");
let openbsd = target.ends_with("-openbsd");
let solaris = target.ends_with("-solaris");
let mut build = cc::Build::new();
let compiler = build.get_compiler();
let clang = compiler.is_like_clang();
let gnu = compiler.is_like_gnu();
let msvc = compiler.is_like_msvc();
build
.include(source_path.as_ref().join("include"))
.include(&src_path);
if msvc {
build.flag("/W4");
} else if apple || clang || gnu {
build
.flag("-fvisibility=hidden")
.flag("--std=gnu89")
.flag("-Wall")
.flag("-Wextra")
.flag("-Wstrict-prototypes")
.flag("-Wno-unused-parameter");
}
build
.file(src_path.join("fs-poll.c"))
.file(src_path.join("idna.c"))
.file(src_path.join("inet.c"))
.file(src_path.join("random.c"))
.file(src_path.join("strscpy.c"))
.file(src_path.join("threadpool.c"))
.file(src_path.join("timer.c"))
.file(src_path.join("uv-common.c"))
.file(src_path.join("uv-data-getter-setters.c"))
.file(src_path.join("version.c"));
if cfg!(windows) {
windows_version_specific_settings(&mut build);
println!("cargo:rustc-link-lib=iphlpapi");
println!("cargo:rustc-link-lib=user32");
println!("cargo:rustc-link-lib=userenv");
println!("cargo:rustc-link-lib=ws2_32");
let win_path = src_path.join("win");
build
.define("WIN32_LEAN_AND_MEAN", None)
.file(win_path.join("async.c"))
.file(win_path.join("core.c"))
.file(win_path.join("detect-wakeup.c"))
.file(win_path.join("dl.c"))
.file(win_path.join("error.c"))
.file(win_path.join("fs.c"))
.file(win_path.join("fs-event.c"))
.file(win_path.join("getaddrinfo.c"))
.file(win_path.join("getnameinfo.c"))
.file(win_path.join("handle.c"))
.file(win_path.join("loop-watcher.c"))
.file(win_path.join("pipe.c"))
.file(win_path.join("thread.c"))
.file(win_path.join("poll.c"))
.file(win_path.join("process.c"))
.file(win_path.join("process-stdio.c"))
.file(win_path.join("signal.c"))
.file(win_path.join("snprintf.c"))
.file(win_path.join("stream.c"))
.file(win_path.join("tcp.c"))
.file(win_path.join("tty.c"))
.file(win_path.join("udp.c"))
.file(win_path.join("util.c"))
.file(win_path.join("winapi.c"))
.file(win_path.join("winsock.c"));
} else {
if !android {
println!("cargo:rustc-link-lib=pthread");
}
build
.define("_FILE_OFFSET_BITS", "64")
.define("_LARGEFILE_SOURCE", None)
.file(unix_path.join("async.c"))
.file(unix_path.join("core.c"))
.file(unix_path.join("dl.c"))
.file(unix_path.join("fs.c"))
.file(unix_path.join("getaddrinfo.c"))
.file(unix_path.join("getnameinfo.c"))
.file(unix_path.join("loop-watcher.c"))
.file(unix_path.join("loop.c"))
.file(unix_path.join("pipe.c"))
.file(unix_path.join("poll.c"))
.file(unix_path.join("process.c"))
.file(unix_path.join("random-devurandom.c"))
.file(unix_path.join("signal.c"))
.file(unix_path.join("stream.c"))
.file(unix_path.join("tcp.c"))
.file(unix_path.join("thread.c"))
.file(unix_path.join("tty.c"))
.file(unix_path.join("udp.c"));
}
if android {
println!("cargo:rustc-link-lib=dl");
build
.file(unix_path.join("android-ifaddrs.c"))
.file(unix_path.join("linux-core.c"))
.file(unix_path.join("linux-inotify.c"))
.file(unix_path.join("linux-syscalls.c"))
.file(unix_path.join("procfs-exepath.c"))
.file(unix_path.join("pthread-fixes.c"))
.file(unix_path.join("random-getentropy.c"))
.file(unix_path.join("random-getrandom.c"))
.file(unix_path.join("random-sysctl-linux.c"))
.file(unix_path.join("sysinfo-loadavg.c"));
}
if apple || android || linux {
build.file(unix_path.join("proctitle.c"));
}
if dragonfly || freebsd {
build.file(unix_path.join("freebsd.c"));
}
if dragonfly || freebsd || netbsd || openbsd {
build
.file(unix_path.join("posix-hrtime.c"))
.file(unix_path.join("bsd-proctitle.c"));
println!("cargo:rustc-link-lib=kvm");
}
if apple || dragonfly || freebsd || netbsd || openbsd {
build
.file(unix_path.join("bsd-ifaddrs.c"))
.file(unix_path.join("kqueue.c"));
}
if freebsd {
build.file(unix_path.join("random-getrandom.c"));
}
if apple || openbsd {
build.file(unix_path.join("random-getentropy.c"));
}
if apple {
build
.define("_DARWIN_UNLIMITED_SELECT", "1")
.define("_DARWIN_USE_64_BIT_INODE", "1")
.file(unix_path.join("darwin-proctitle.c"))
.file(unix_path.join("darwin.c"))
.file(unix_path.join("fsevents.c"));
}
if linux {
build
.define("_GNU_SOURCE", None)
.define("_POSIX_C_SOURCE", "200112")
.file(unix_path.join("linux-core.c"))
.file(unix_path.join("linux-inotify.c"))
.file(unix_path.join("linux-syscalls.c"))
.file(unix_path.join("procfs-exepath.c"))
.file(unix_path.join("random-getrandom.c"))
.file(unix_path.join("random-sysctl-linux.c"))
.file(unix_path.join("sysinfo-loadavg.c"));
println!("cargo:rustc-link-lib=dl");
println!("cargo:rustc-link-lib=rt");
}
if netbsd {
build.file(unix_path.join("netbsd.c"));
}
if openbsd {
build.file(unix_path.join("openbsd.c"));
}
if solaris {
build
.define("__EXTENSIONS__", None)
.define("_XOPEN_SOURCE", "500")
.file(unix_path.join("no-proctitle.c"))
.file(unix_path.join("sunos.c"));
println!("cargo:rustc-link-lib=kstat");
println!("cargo:rustc-link-lib=nsl");
println!("cargo:rustc-link-lib=sendfile");
println!("cargo:rustc-link-lib=socket");
}
build.compile("uv");
Ok(())
}
fn generate_bindings<P: AsRef<Path>>(include_path: &P) -> Result<()> {
println!("Generating bindings for libuv...");
let include_path = include_path.as_ref();
let header_path = include_path.join("uv.h");
let bindings = bindgen::Builder::default()
.header(header_path.to_string_lossy())
.clang_arg(format!("-I{}", include_path.display()))
.whitelist_type("uv_.+")
.whitelist_function("uv_.+")
.whitelist_var("(?i)uv_.+")
.whitelist_var("AF_.+")
.whitelist_var("AI_.+")
.whitelist_var("IPPROTO_.+")
.whitelist_var("NI_.+")
.whitelist_var("SIG.+")
.whitelist_var("SOCK_.+")
.generate()
.map_err(|_| Error::BindgenError)?;
let outdir = PathBuf::from(env::var("OUT_DIR").unwrap()).join("bindings.rs");
bindings
.write_to_file(&outdir)
.map_err(|e| Error::PathError(outdir.to_string_lossy().into(), e))?;
Ok(())
}
fn main() {
let source_path = PathBuf::from("libuv");
let mut include_path = source_path.join("include");
if let Some(maybe_include) = try_pkgconfig() {
if let Some(incl) = maybe_include {
include_path = incl;
}
} else {
build(&source_path).unwrap();
}
generate_bindings(&include_path).unwrap();
println!("cargo:include={}", include_path.to_string_lossy());
}