diff --git a/.gitmodules b/.gitmodules index 7196785ddd..b69d1a5865 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "curl-sys/curl"] path = curl-sys/curl - url = https://github.com/alexcrichton/curl + url = https://github.com/curl/curl diff --git a/curl-sys/build.rs b/curl-sys/build.rs index e9f1894f23..687889f368 100644 --- a/curl-sys/build.rs +++ b/curl-sys/build.rs @@ -3,27 +3,13 @@ extern crate pkg_config; extern crate vcpkg; extern crate cc; -#[allow(unused_imports, deprecated)] -use std::ascii::AsciiExt; use std::env; -use std::ffi::OsString; use std::fs; -use std::path::{PathBuf, Path, Component, Prefix}; +use std::path::{PathBuf, Path}; use std::process::Command; -use std::io::ErrorKind; - -macro_rules! t { - ($e:expr) => (match $e { - Ok(t) => t, - Err(e) => panic!("{} return the error {}", stringify!($e), e), - }) -} fn main() { let target = env::var("TARGET").unwrap(); - let host = env::var("HOST").unwrap(); - let src = env::current_dir().unwrap(); - let dst = PathBuf::from(env::var_os("OUT_DIR").unwrap()); let windows = target.contains("windows"); // OSX and Haiku ships libcurl by default, so we just use that version @@ -32,27 +18,15 @@ fn main() { return println!("cargo:rustc-flags=-l curl"); } - // Illumos/Solaris requires explicit linking with libnsl - if target.contains("solaris") { - println!("cargo:rustc-flags=-l nsl"); - } - // Next, fall back and try to use pkg-config if its available. - if !target.contains("windows") { - match pkg_config::find_library("libcurl") { - Ok(lib) => { - for path in lib.include_paths.iter() { - println!("cargo:include={}", path.display()); - } - return - } - Err(e) => println!("Couldn't find libcurl from \ - pkgconfig ({:?}), compiling it from source...", e), + if windows { + if try_vcpkg() { + return + } + } else { + if try_pkg_config() { + return } - } - - if try_vcpkg() { - return; } if !Path::new("curl/.git").exists() { @@ -60,304 +34,201 @@ fn main() { .status(); } - println!("cargo:rustc-link-search=native={}/lib", dst.display()); - println!("cargo:rustc-link-lib=static=curl"); + let dst = PathBuf::from(env::var_os("OUT_DIR").unwrap()); + let include = dst.join("include"); + let build = dst.join("build"); println!("cargo:root={}", dst.display()); - println!("cargo:include={}/include", dst.display()); - if windows { - println!("cargo:rustc-link-lib=ws2_32"); - println!("cargo:rustc-link-lib=crypt32"); - } - - // MSVC builds are just totally different - if target.contains("msvc") { - return build_msvc(&target); - } - - let openssl_root = register_dep("OPENSSL"); - let zlib_root = register_dep("Z"); - let nghttp2_root = register_dep("NGHTTP2"); - - let cfg = cc::Build::new(); - let compiler = cfg.get_compiler(); - - let _ = fs::create_dir(&dst.join("build")); - - let mut cmd = Command::new("sh"); - let mut cflags = OsString::new(); - for arg in compiler.args() { - cflags.push(arg); - cflags.push(" "); + println!("cargo:include={}", include.display()); + fs::create_dir_all(include.join("curl")).unwrap(); + fs::copy("curl/include/curl/curl.h", include.join("curl/curl.h")).unwrap(); + fs::copy("curl/include/curl/curlver.h", include.join("curl/curlver.h")).unwrap(); + fs::copy("curl/include/curl/easy.h", include.join("curl/easy.h")).unwrap(); + fs::copy("curl/include/curl/mprintf.h", include.join("curl/mprintf.h")).unwrap(); + fs::copy("curl/include/curl/multi.h", include.join("curl/multi.h")).unwrap(); + fs::copy("curl/include/curl/stdcheaders.h", include.join("curl/stdcheaders.h")).unwrap(); + fs::copy("curl/include/curl/system.h", include.join("curl/system.h")).unwrap(); + fs::copy("curl/include/curl/typecheck-gcc.h", include.join("curl/typecheck-gcc.h")).unwrap(); + let mut cfg = cc::Build::new(); + cfg.out_dir(&build) + .include("curl/lib") + .include("curl/include") + .define("BUILDING_LIBCURL", None) + .define("CURL_DISABLE_CRYPTO_AUTH", None) + .define("CURL_DISABLE_DICT", None) + .define("CURL_DISABLE_FTP", None) + .define("CURL_DISABLE_GOPHER", None) + .define("CURL_DISABLE_IMAP", None) + .define("CURL_DISABLE_LDAP", None) + .define("CURL_DISABLE_LDAPS", None) + .define("CURL_DISABLE_NTLM", None) + .define("CURL_DISABLE_POP3", None) + .define("CURL_DISABLE_RTSP", None) + .define("CURL_DISABLE_SMB", None) + .define("CURL_DISABLE_SMTP", None) + .define("CURL_DISABLE_TELNET", None) + .define("CURL_DISABLE_TFTP", None) + .define("CURL_STATICLIB", None) + .define("ENABLE_IPV6", None) + .define("HAVE_ASSERT_H", None) + .define("OS", "\"unknown\"") // TODO + .define("HAVE_ZLIB_H", None) + .define("HAVE_LIBZ", None) + + .file("curl/lib/asyn-thread.c") + .file("curl/lib/base64.c") + .file("curl/lib/conncache.c") + .file("curl/lib/connect.c") + .file("curl/lib/content_encoding.c") + .file("curl/lib/cookie.c") + .file("curl/lib/curl_addrinfo.c") + .file("curl/lib/curl_ctype.c") + .file("curl/lib/curl_memrchr.c") + .file("curl/lib/curl_range.c") + .file("curl/lib/curl_threads.c") + .file("curl/lib/dotdot.c") + .file("curl/lib/easy.c") + .file("curl/lib/escape.c") + .file("curl/lib/file.c") + .file("curl/lib/fileinfo.c") + .file("curl/lib/formdata.c") + .file("curl/lib/getenv.c") + .file("curl/lib/getinfo.c") + .file("curl/lib/hash.c") + .file("curl/lib/hostasyn.c") + .file("curl/lib/hostcheck.c") + .file("curl/lib/hostip.c") + .file("curl/lib/hostip6.c") + .file("curl/lib/http.c") + .file("curl/lib/http2.c") + .file("curl/lib/http_chunks.c") + .file("curl/lib/http_proxy.c") + .file("curl/lib/if2ip.c") + .file("curl/lib/inet_ntop.c") + .file("curl/lib/inet_pton.c") + .file("curl/lib/llist.c") + .file("curl/lib/mime.c") + .file("curl/lib/mprintf.c") + .file("curl/lib/multi.c") + .file("curl/lib/netrc.c") + .file("curl/lib/nonblock.c") + .file("curl/lib/parsedate.c") + .file("curl/lib/pipeline.c") + .file("curl/lib/progress.c") + .file("curl/lib/rand.c") + .file("curl/lib/select.c") + .file("curl/lib/sendf.c") + .file("curl/lib/setopt.c") + .file("curl/lib/share.c") + .file("curl/lib/slist.c") + .file("curl/lib/socks.c") + .file("curl/lib/speedcheck.c") + .file("curl/lib/splay.c") + .file("curl/lib/strcase.c") + .file("curl/lib/strdup.c") + .file("curl/lib/strerror.c") + .file("curl/lib/strtok.c") + .file("curl/lib/strtoofft.c") + .file("curl/lib/timeval.c") + .file("curl/lib/transfer.c") + .file("curl/lib/url.c") + .file("curl/lib/version.c") + .file("curl/lib/vtls/vtls.c") + .file("curl/lib/warnless.c") + .file("curl/lib/wildcard.c") + + .define("HAVE_GETADDRINFO", None) + + .warnings(false); + + if cfg!(feature = "http2") { + cfg.define("USE_NGHTTP2", None) + .define("NGHTTP2_STATICLIB", None); + + if let Some(path) = env::var_os("DEP_NGHTTP2_ROOT") { + let path = PathBuf::from(path); + cfg.include(path.join("include")); + } } - // Can't run ./configure directly on msys2 b/c we're handing in - // Windows-style paths (those starting with C:\), but it chokes on those. - // For that reason we build up a shell script with paths converted to - // posix versions hopefully... - // - // Also apparently the buildbots choke unless we manually set LD, who knows - // why?! - cmd.env("CC", compiler.path()) - .env("CFLAGS", cflags) - .env("LD", &which("ld").unwrap()) - .env("VERBOSE", "1") - .current_dir(&dst.join("build")) - .arg(msys_compatible(&src.join("curl/configure"))); - - // For now this build script doesn't support paths with spaces in them. This - // is arguably a but in curl's configure script, but we could also try to - // paper over it by using a tmp directory which *doesn't* have spaces in it. - // As of now though that's not implemented so just give a nicer error for - // the time being. - let wants_space_error = windows && - (dst.to_str().map(|s| s.contains(" ")).unwrap_or(false) || - src.to_str().map(|s| s.contains(" ")).unwrap_or(false)); - if wants_space_error { - panic!("\n\nunfortunately ./configure of libcurl is known to \ - fail if there's a space in the path to the current \ - directory\n\n\ - there's a space in either\n {}\n {}\nand this will cause the \ - build to fail\n\n\ - the MSVC build should work with a directory that has \ - spaces in it, and it would also work to move this to a \ - different directory without spaces\n\n", - src.display(), dst.display()) + if let Some(path) = env::var_os("DEP_Z_INCLUDE") { + cfg.include(path); } if windows { - cmd.arg("--with-winssl"); + cfg.define("USE_THREADS_WIN32", None) + .define("HAVE_IOCTLSOCKET_FIONBIO", None) + .define("USE_WINSOCK", None) + .define("USE_WINDOWS_SSPI", None) + .define("USE_SCHANNEL", None) + .file("curl/lib/x509asn1.c") + .file("curl/lib/curl_sspi.c") + .file("curl/lib/socks_sspi.c") + .file("curl/lib/system_win32.c") + .file("curl/lib/vtls/schannel.c") + .file("curl/lib/vtls/schannel_verify.c"); } else { - cmd.arg("--without-ca-bundle"); - cmd.arg("--without-ca-path"); - } - if let Some(root) = openssl_root { - cmd.arg(format!("--with-ssl={}", msys_compatible(&root))); - } - if let Some(root) = zlib_root { - cmd.arg(format!("--with-zlib={}", msys_compatible(&root))); - } - cmd.arg("--enable-static=yes"); - cmd.arg("--enable-shared=no"); - match &env::var("PROFILE").unwrap()[..] { - "bench" | "release" => { - cmd.arg("--enable-optimize"); + cfg.define("RECV_TYPE_ARG1", "int") + .define("HAVE_PTHREAD_H", None) + .define("HAVE_ARPA_INET_H", None) + .define("HAVE_ERRNO_H", None) + .define("HAVE_FCNTL_H", None) + .define("HAVE_NETDB_H", None) + .define("HAVE_NETINET_IN_H", None) + .define("HAVE_POLL_FINE", None) + .define("HAVE_POLL_H", None) + .define("HAVE_FCNTL_O_NONBLOCK", None) + .define("HAVE_SYS_SELECT_H", None) + .define("HAVE_SYS_STAT_H", None) + .define("HAVE_UNISTD_H", None) + .define("HAVE_RECV", None) + .define("HAVE_SELECT", None) + .define("HAVE_SEND", None) + .define("HAVE_SOCKET", None) + .define("HAVE_STERRROR_R", None) + .define("HAVE_STRUCT_TIMEVAL", None) + .define("USE_THREADS_POSIX", None) + .define("USE_OPENSSL", None) + .define("RECV_TYPE_ARG2", "void*") + .define("RECV_TYPE_ARG3", "size_t") + .define("RECV_TYPE_ARG4", "int") + .define("RECV_TYPE_RETV", "ssize_t") + .define("SEND_QUAL_ARG2", "const") + .define("SEND_TYPE_ARG1", "int") + .define("SEND_TYPE_ARG2", "void*") + .define("SEND_TYPE_ARG3", "size_t") + .define("SEND_TYPE_ARG4", "int") + .define("SEND_TYPE_RETV", "ssize_t") + .define("SIZEOF_CURL_OFF_T", "8") + .define("SIZEOF_INT", "4") + .define("SIZEOF_SHORT", "2") + .file("curl/lib/vtls/openssl.c"); + + let width = env::var("CARGO_CFG_TARGET_POINTER_WIDTH") + .unwrap() + .parse::() + .unwrap(); + cfg.define("SIZEOF_SSIZE_T", Some(&(width / 8).to_string()[..])); + cfg.define("SIZEOF_SIZE_T", Some(&(width / 8).to_string()[..])); + cfg.define("SIZEOF_LONG", Some(&(width / 8).to_string()[..])); + + if let Some(path) = env::var_os("DEP_OPENSSL_INCLUDE") { + cfg.include(path); } - _ => { - cmd.arg("--enable-debug"); - cmd.arg("--disable-optimize"); - } - } - cmd.arg(format!("--prefix={}", msys_compatible(&dst))); - - if target != host && - (!target.contains("windows") || !host.contains("windows")) { - // NOTE GNU terminology - // BUILD = machine where we are (cross) compiling curl - // HOST = machine where the compiled curl will be used - // TARGET = only relevant when compiling compilers - if target.contains("windows") { - // curl's configure can't parse `-windows-` triples when used - // as `--host`s. In those cases we use this combination of - // `host` and `target` that appears to do the right thing. - cmd.arg(format!("--host={}", host)); - cmd.arg(format!("--target={}", target)); - } else { - cmd.arg(format!("--build={}", host)); - cmd.arg(format!("--host={}", target)); - } - } - if let Some(root) = nghttp2_root { - cmd.arg(format!("--with-nghttp2={}", msys_compatible(&root))); - } else { - cmd.arg("--without-nghttp2"); + cfg.flag("-fvisibility=hidden"); } - cmd.arg("--without-librtmp"); - cmd.arg("--without-libidn2"); - cmd.arg("--without-libssh2"); - cmd.arg("--without-libpsl"); - cmd.arg("--disable-ldap"); - cmd.arg("--disable-ldaps"); - cmd.arg("--disable-ftp"); - cmd.arg("--disable-rtsp"); - cmd.arg("--disable-dict"); - cmd.arg("--disable-telnet"); - cmd.arg("--disable-tftp"); - cmd.arg("--disable-pop3"); - cmd.arg("--disable-imap"); - cmd.arg("--disable-smtp"); - cmd.arg("--disable-gopher"); - cmd.arg("--disable-manual"); - cmd.arg("--disable-smb"); - cmd.arg("--disable-sspi"); - cmd.arg("--disable-manual"); - cmd.arg("--disable-unix-sockets"); - cmd.arg("--disable-versioned-symbols"); - cmd.arg("--enable-hidden-symbols"); - cmd.arg("--disable-libcurl-option"); - - run(&mut cmd, "sh"); - run(make() - .arg(&format!("-j{}", env::var("NUM_JOBS").unwrap())) - .current_dir(&dst.join("build")), "make"); - run(make() - .arg("install") - .current_dir(&dst.join("build")), "make"); -} + cfg.compile("curl"); -fn run(cmd: &mut Command, program: &str) { - println!("running: {:?}", cmd); - let status = match cmd.status() { - Ok(status) => status, - Err(ref e) if e.kind() == ErrorKind::NotFound => { - fail(&format!("failed to execute command: {}\nis `{}` not installed?", - e, program)); - } - Err(e) => fail(&format!("failed to execute command: {}", e)), - }; - if !status.success() { - fail(&format!("command did not execute successfully, got: {}", status)); - } -} - -fn fail(s: &str) -> ! { - panic!("\n{}\n\nbuild script failed, must exit now", s) -} - -fn make() -> Command { - let cmd = if cfg!(target_os = "freebsd") {"gmake"} else {"make"}; - let mut cmd = Command::new(cmd); - // We're using the MSYS make which doesn't work with the mingw32-make-style - // MAKEFLAGS, so remove that from the env if present. - if cfg!(windows) { - cmd.env_remove("MAKEFLAGS").env_remove("MFLAGS"); - } - return cmd -} - -fn which(cmd: &str) -> Option { - let cmd = format!("{}{}", cmd, env::consts::EXE_SUFFIX); - let paths = env::var_os("PATH").unwrap(); - env::split_paths(&paths).map(|p| p.join(&cmd)).find(|p| { - fs::metadata(p).is_ok() - }) -} - -fn msys_compatible(path: &Path) -> String { - let mut path_string = path.to_str().unwrap().to_string(); - if !cfg!(windows) { - return path_string; - } - - // Replace e.g. C:\ with /c/ - if let Component::Prefix(prefix_component) = path.components().next().unwrap() { - if let Prefix::Disk(disk) = prefix_component.kind() { - let from = format!("{}:\\", disk as char); - let to = format!("/{}/", (disk as char).to_ascii_lowercase()); - path_string = path_string.replace(&from, &to); - } - } - path_string.replace("\\", "/") -} - -fn register_dep(dep: &str) -> Option { - if let Some(s) = env::var_os(&format!("DEP_{}_ROOT", dep)) { - prepend("PKG_CONFIG_PATH", Path::new(&s).join("lib/pkgconfig")); - return Some(s.into()) - } - if let Some(s) = env::var_os(&format!("DEP_{}_INCLUDE", dep)) { - let root = Path::new(&s).parent().unwrap(); - env::set_var(&format!("DEP_{}_ROOT", dep), root); - let path = root.join("lib/pkgconfig"); - if path.exists() { - prepend("PKG_CONFIG_PATH", path); - return Some(root.to_path_buf()) - } - } - - return None; - - fn prepend(var: &str, val: PathBuf) { - let prefix = env::var(var).unwrap_or(String::new()); - let mut v = vec![val]; - v.extend(env::split_paths(&prefix)); - env::set_var(var, &env::join_paths(v).unwrap()); - } -} - -fn build_msvc(target: &str) { - let cmd = cc::windows_registry::find(target, "nmake.exe"); - let mut cmd = cmd.unwrap_or(Command::new("nmake.exe")); - let src = env::current_dir().unwrap(); - let dst = PathBuf::from(env::var_os("OUT_DIR").unwrap()); - let machine = if target.starts_with("x86_64") { - "x64" - } else if target.starts_with("i686") { - "x86" - } else { - panic!("unknown msvc target: {}", target); - }; - - t!(fs::create_dir_all(dst.join("include/curl"))); - t!(fs::create_dir_all(dst.join("lib"))); - - drop(fs::remove_dir_all(&dst.join("build"))); - cp_r(&src.join("curl"), &dst.join("build")); - cmd.current_dir(dst.join("build/winbuild")); - cmd.arg("/f").arg("Makefile.vc") - .arg("MODE=static") - .arg("ENABLE_IDN=yes") - .arg("DEBUG=no") - .arg("GEN_PDB=no") - .arg("ENABLE_WINSSL=yes") - .arg("ENABLE_SSPI=yes") - .arg(format!("MACHINE={}", machine)); - - // These env vars are intended for `make` usually, not nmake, so remove them - // unconditionally - cmd.env_remove("MAKEFLAGS") - .env_remove("MFLAGS"); - - // While in theory clang-cl can be used it doesn't work because we can't - // configure CFLAGS which means cross-compilation to 32-bit doesn't work. - // Just require MSVC cl.exe here. - cmd.env_remove("CC"); - - let features = env::var("CARGO_CFG_TARGET_FEATURE") - .unwrap_or(String::new()); - if features.contains("crt-static") { - cmd.arg("RTLIBCFG=static"); - } - - if let Some(inc) = env::var_os("DEP_Z_ROOT") { - let inc = PathBuf::from(inc); - let mut s = OsString::from("WITH_DEVEL="); - s.push(&inc); - cmd.arg("WITH_ZLIB=static").arg(s); - - // the build system for curl expects this library to be called - // zlib_a.lib, so make sure it's named correctly (where libz-sys just - // produces zlib.lib) - let _ = fs::remove_file(&inc.join("lib/zlib_a.lib")); - t!(fs::copy(inc.join("lib/zlib.lib"), inc.join("lib/zlib_a.lib"))); + if windows { + println!("cargo:rustc-link-lib=ws2_32"); + println!("cargo:rustc-link-lib=crypt32"); } - run(&mut cmd, "nmake"); - - let name = format!("libcurl-vc-{}-release-static-zlib-static-\ - ipv6-sspi-winssl", machine); - let libs = dst.join("build/builds").join(name); - t!(fs::copy(libs.join("lib/libcurl_a.lib"), dst.join("lib/curl.lib"))); - for f in t!(fs::read_dir(libs.join("include/curl"))) { - let path = t!(f).path(); - let dst = dst.join("include/curl").join(path.file_name().unwrap()); - t!(fs::copy(path, dst)); + // Illumos/Solaris requires explicit linking with libnsl + if target.contains("solaris") { + println!("cargo:rustc-link-lib=nsl"); } - t!(fs::remove_dir_all(dst.join("build/builds"))); - println!("cargo:rustc-link-lib=wldap32"); - println!("cargo:rustc-link-lib=advapi32"); - println!("cargo:rustc-link-lib=normaliz"); } #[cfg(not(target_env = "msvc"))] @@ -412,15 +283,50 @@ fn try_vcpkg() -> bool { false } -fn cp_r(src: &Path, dst: &Path) { - t!(fs::create_dir(dst)); - for e in t!(src.read_dir()).map(|e| t!(e)) { - let src = e.path(); - let dst = dst.join(e.file_name()); - if t!(e.file_type()).is_dir() { - cp_r(&src, &dst); - } else { - t!(fs::copy(&src, &dst)); +fn try_pkg_config() -> bool { + let mut cfg = pkg_config::Config::new(); + cfg.cargo_metadata(false); + let lib = match cfg.probe("libcurl") { + Ok(lib) => lib, + Err(e) => { + println!("Couldn't find libcurl from pkgconfig ({:?}), \ + compiling it from source...", e); + return false } + }; + + // Not all system builds of libcurl have http2 features enabled, so if we've + // got a http2-requested build then we may fall back to a build from source. + if cfg!(feature = "http2") { + let output = Command::new("curl-config") + .arg("--features") + .output(); + let output = match output { + Ok(out) => out, + Err(e) => { + println!("failed to run curl-config ({}), building from source", e); + return false + } + }; + if !output.status.success() { + println!("curl-config failed: {}", output.status); + return false + } + let stdout = String::from_utf8_lossy(&output.stdout); + if !stdout.contains("HTTP2") { + println!("failed to find http-2 feature enabled in pkg-config-found \ + libcurl, building from source"); + return false + } + } + + // Re-find the library to print cargo's metadata, then print some extra + // metadata as well. + cfg.cargo_metadata(true) + .probe("libcurl") + .unwrap(); + for path in lib.include_paths.iter() { + println!("cargo:include={}", path.display()); } + return true } diff --git a/curl-sys/curl b/curl-sys/curl index c88fd53b5b..432eb5f5c2 160000 --- a/curl-sys/curl +++ b/curl-sys/curl @@ -1 +1 @@ -Subproject commit c88fd53b5bb7fe8351ef602f1b2ec76559816f0c +Subproject commit 432eb5f5c254ee8383b2522ce597c9219877923e