use std::env;
use std::error;
use std::fmt;
use std::fs::OpenOptions;
use std::io;
use std::io::Write;
use std::path::{Path, PathBuf};
static LIBUV_VERSION: &str = "1.48.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") || cfg!(feature = "skip-pkg-config") {
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;
}
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")
.flag("/wd4100") .flag("/wd4127") .flag("/wd4201") .flag("/wd4206") .flag("/wd4210") .flag("/wd4232") .flag("/wd4456") .flag("/wd4457") .flag("/wd4459") .flag("/wd4706") .flag("/wd4996") .flag("/utf-8"); } else if apple || clang || gnu {
build
.flag("-fvisibility=hidden")
.flag("--std=gnu89")
.flag("-Wall")
.flag("-Wextra")
.flag("-Wstrict-prototypes")
.flag("-Wno-unused-parameter");
}
if gnu {
build.flag("-fno-strict-aliasing");
}
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("strtok.c"))
.file(src_path.join("thread-common.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) {
println!("cargo:rustc-link-lib=shell32");
println!("cargo:rustc-link-lib=psapi");
println!("cargo:rustc-link-lib=user32");
println!("cargo:rustc-link-lib=advapi32");
println!("cargo:rustc-link-lib=iphlpapi");
println!("cargo:rustc-link-lib=userenv");
println!("cargo:rustc-link-lib=ws2_32");
println!("cargo:rustc-link-lib=dbghelp");
println!("cargo:rustc-link-lib=ole32");
let win_path = src_path.join("win");
build
.define("_WIN32_WINNT", "0x0602")
.define("WIN32_LEAN_AND_MEAN", None)
.define("_CRT_DECLARE_NONSTDC_NAMES", "0")
.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
.define("_GNU_SOURCE", None)
.file(unix_path.join("linux.c"))
.file(unix_path.join("procfs-exepath.c"))
.file(unix_path.join("random-getentropy.c"))
.file(unix_path.join("random-getrandom.c"))
.file(unix_path.join("random-sysctl-linux.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"));
}
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.c"))
.file(unix_path.join("procfs-exepath.c"))
.file(unix_path.join("random-getrandom.c"))
.file(unix_path.join("random-sysctl-linux.c"));
println!("cargo:rustc-link-lib=dl");
println!("cargo:rustc-link-lib=rt");
}
if netbsd {
build.file(unix_path.join("netbsd.c"));
println!("cargo:rustc-link-lib=kvm");
}
if openbsd {
build.file(unix_path.join("openbsd.c"));
}
if solaris {
build
.define("__EXTENSIONS__", None)
.define("_XOPEN_SOURCE", "500")
.define("_REENTRANT", None)
.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()))
.allowlist_type("uv_.+")
.allowlist_function("uv_.+")
.allowlist_var("(?i)uv_.+")
.allowlist_var("AF_.+")
.allowlist_var("AI_.+")
.allowlist_var("IPPROTO_.+")
.allowlist_var("NI_.+")
.allowlist_var("SIG.+")
.allowlist_var("SOCK_.+")
.allowlist_type("__socket_type.*") .allowlist_type("IPPROTO") .generate()
.map_err(|_| Error::BindgenError)?;
let output = bindings.to_string();
let output = output
.replace("__socket_type_", "")
.replace("IPPROTO_IPPROTO_", "IPPROTO_");
let filename = PathBuf::from(env::var("OUT_DIR").unwrap()).join("bindings.rs");
let mut file = OpenOptions::new()
.write(true)
.truncate(true)
.create(true)
.open(filename.clone())
.map_err(|e| Error::PathError(filename.to_string_lossy().into(), e))?;
file.write(output.as_bytes())
.map_err(|e| Error::PathError(filename.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());
}