From 6fda365b88162d6bc727c3ea1c360d023c409e49 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 13 Sep 2018 09:36:03 -0700 Subject: [PATCH 01/10] Remove dependency on `cmake` I've spent too many hours of my life bending over backwards trying to satisfy native libraryies' build systems. The most recent pain is that informing a native build system of its dependencies (such as telling libgit2 where libssh2 is installed) is an absolute never-ending nightmare. The towel is now thrown in as `cmake` is jettisoned and this is now just using the `cc` crate to directly compile all the various C code. For some more info see alexcrichton/curl-rust#225 --- libgit2-sys/Cargo.toml | 1 - libgit2-sys/build.rs | 263 +++++++++++++++-------------------------- systest/Cargo.toml | 2 +- 3 files changed, 97 insertions(+), 169 deletions(-) diff --git a/libgit2-sys/Cargo.toml b/libgit2-sys/Cargo.toml index 60fc27650c..c1fb68e589 100644 --- a/libgit2-sys/Cargo.toml +++ b/libgit2-sys/Cargo.toml @@ -23,7 +23,6 @@ libz-sys = "1.0.18" [build-dependencies] pkg-config = "0.3" -cmake = "0.1.24" cc = "1.0" [target.'cfg(unix)'.dependencies] diff --git a/libgit2-sys/build.rs b/libgit2-sys/build.rs index 1141c8aca6..1563e7ad9b 100644 --- a/libgit2-sys/build.rs +++ b/libgit2-sys/build.rs @@ -1,35 +1,15 @@ -extern crate cmake; extern crate cc; extern crate pkg_config; use std::env; -use std::ffi::OsString; -use std::fs::{self, File}; -use std::io::prelude::*; +use std::fs; use std::path::{Path, PathBuf}; use std::process::Command; -macro_rules! t { - ($e:expr) => (match $e{ - Ok(e) => e, - Err(e) => panic!("{} failed with {}", stringify!($e), e), - }) -} - fn main() { let https = env::var("CARGO_FEATURE_HTTPS").is_ok(); let ssh = env::var("CARGO_FEATURE_SSH").is_ok(); let curl = env::var("CARGO_FEATURE_CURL").is_ok(); - if ssh { - register_dep("SSH2"); - } - if https { - register_dep("OPENSSL"); - } - if curl { - register_dep("CURL"); - } - let has_pkgconfig = Command::new("pkg-config").output().is_ok(); if env::var("LIBGIT2_SYS_USE_PKG_CONFIG").is_ok() { if pkg_config::find_library("libgit2").is_ok() { @@ -43,141 +23,107 @@ fn main() { } let target = env::var("TARGET").unwrap(); - let host = env::var("HOST").unwrap(); let windows = target.contains("windows"); - let msvc = target.contains("msvc"); - let mut cfg = cmake::Config::new("libgit2"); - - #[cfg(feature = "ssh_key_from_memory")] - cfg.define("GIT_SSH_MEMORY_CREDENTIALS", "1"); - - if msvc { - // libgit2 passes the /GL flag to enable whole program optimization, but - // this requires that the /LTCG flag is passed to the linker later on, - // and currently the compiler does not do that, so we disable whole - // program optimization entirely. - cfg.cflag("/GL-"); - - // Currently liblibc links to msvcrt which apparently is a dynamic CRT, - // so we need to turn this off to get it to link right. - let features = env::var("CARGO_CFG_TARGET_FEATURE") - .unwrap_or(String::new()); - if features.contains("crt-static") { - cfg.define("STATIC_CRT", "ON"); - } else { - cfg.define("STATIC_CRT", "OFF"); - } + let dst = PathBuf::from(env::var_os("OUT_DIR").unwrap()); + let include = dst.join("include"); + let mut cfg = cc::Build::new(); + + // Copy over all header files + cp_r("libgit2/include".as_ref(), &include); + + cfg.include(&include) + .include("libgit2/src") + .out_dir(dst.join("build")) + .warnings(false); + + // Include all cross-platform C files + add_c_files(&mut cfg, "libgit2/src".as_ref()); + add_c_files(&mut cfg, "libgit2/src/xdiff".as_ref()); + + // These are activated by feautres, but they're all unconditionally always + // compiled apparently and have internal #define's to make sure they're + // compiled correctly. + add_c_files(&mut cfg, "libgit2/src/transports".as_ref()); + add_c_files(&mut cfg, "libgit2/src/streams".as_ref()); + + // Always use bundled http-parser for now + cfg.include("libgit2/deps/http-parser") + .file("libgit2/deps/http-parser/http_parser.c"); + + if windows { + add_c_files(&mut cfg, "libgit2/src/win32".as_ref()); + } else { + add_c_files(&mut cfg, "libgit2/src/unix".as_ref()); } - // libgit2 uses pkg-config to discover libssh2, but this doesn't work on - // windows as libssh2 doesn't come with a libssh2.pc file in that install - // (or when pkg-config isn't found). As a result we just manually turn on - // SSH support in libgit2 (a little jankily) here... - let mut ssh_forced = false; - if ssh && (windows || !has_pkgconfig) { - if let Ok(libssh2_include) = env::var("DEP_SSH2_INCLUDE") { - ssh_forced = true; - if msvc { - cfg.cflag(format!("/I{}", libssh2_include)) - .cflag("/DGIT_SSH"); - } else { - cfg.cflag(format!("-I{}", sanitize_sh(libssh2_include.as_ref()))) - .cflag("-DGIT_SSH"); - } - } - } + let mut features = String::new(); - // When cross-compiling, we're pretty unlikely to find a `dlltool` binary - // lying around, so try to find another if it exists - if windows && !host.contains("windows") { - let c_compiler = cc::Build::new().cargo_metadata(false) - .get_compiler(); - let exe = c_compiler.path(); - let path = env::var_os("PATH").unwrap_or(OsString::new()); - let exe = env::split_paths(&path) - .map(|p| p.join(&exe)) - .find(|p| p.exists()); - if let Some(exe) = exe { - if let Some(name) = exe.file_name().and_then(|e| e.to_str()) { - let name = name.replace("gcc", "dlltool"); - let dlltool = exe.with_file_name(name); - cfg.define("DLLTOOL", &dlltool); - } - } + features.push_str("#ifndef INCLUDE_features_h\n"); + features.push_str("#define INCLUDE_features_h\n"); + features.push_str("#define GIT_THREADS 1\n"); + features.push_str("#define GIT_USE_NSEC 1\n"); + features.push_str("#define GIT_USE_STAT_MTIM 1\n"); + if env::var("CARGO_CFG_TARGET_POINTER_WIDTH").unwrap() == "32" { + features.push_str("#define GIT_ARCH_32 1\n"); + } else { + features.push_str("#define GIT_ARCH_64 1\n"); } if ssh { - cfg.register_dep("SSH2"); - } else { - cfg.define("USE_SSH", "OFF"); + if let Some(path) = env::var_os("DEP_SSH2_INCLUDE") { + cfg.include(path); + } + features.push_str("#define GIT_SSH 1\n"); + features.push_str("#define GIT_SSH_MEMORY_CREDENTIALS 1\n"); } if https { - cfg.register_dep("OPENSSL"); - } else { - cfg.define("USE_OPENSSL", "OFF"); - cfg.define("USE_HTTPS", "OFF"); + features.push_str("#define GIT_HTTPS 1\n"); + + if windows { + features.push_str("#define GIT_WINHTTP 1\n"); + features.push_str("#define GIT_SHA1_WIN32 1\n"); + cfg.file("libgit2/src/hash/hash_win32.c"); + } else if target.contains("apple") { + features.push_str("#define GIT_SECURE_TRANSPORT 1\n"); + features.push_str("#define GIT_SHA1_COMMON_CRYPTO 1\n"); + cfg.file("libgit2/src/hash/hash_common_crypto.c"); + } else { + features.push_str("#define GIT_OPENSSL 1\n"); + features.push_str("#define GIT_SHA1_OPENSSL 1\n"); + if let Some(path) = env::var_os("DEP_OPENSSL_INCLUDE") { + cfg.include(path); + } + } } if curl { - cfg.register_dep("CURL"); - } else { - cfg.define("CURL", "OFF"); + features.push_str("#define GIT_CURL 1\n"); + if let Some(path) = env::var_os("DEP_CURL_INCLUDE") { + cfg.include(path); + } } - - //Use bundled http-parser if cross compiling - if host != target { - cfg.define("USE_EXT_HTTP_PARSER", "OFF"); + if let Some(path) = env::var_os("DEP_Z_INCLUDE") { + cfg.include(path); } - let _ = fs::remove_dir_all(env::var("OUT_DIR").unwrap()); - t!(fs::create_dir_all(env::var("OUT_DIR").unwrap())); - - // Unset DESTDIR or libgit2.a ends up in it and cargo can't find it - env::remove_var("DESTDIR"); - let dst = cfg.define("BUILD_SHARED_LIBS", "OFF") - .define("BUILD_CLAR", "OFF") - .register_dep("Z") - .build(); - - // Make sure libssh2 was detected on unix systems, because it definitely - // should have been! - if ssh && !ssh_forced { - let flags = dst.join("build/src/git2/sys/features.h"); - let mut contents = String::new(); - t!(t!(File::open(flags)).read_to_string(&mut contents)); - if !contents.contains("#define GIT_SSH 1") { - panic!("libgit2 failed to find libssh2, and SSH support is required"); - } + if target.contains("apple") { + features.push_str("#define GIT_USE_ICONV 1\n"); } - // libgit2 requires the http_parser library for the HTTP transport to be - // implemented, and it will attempt to use the system http_parser if it's - // available. Detect this situation and report using the system http parser - // the same way in this situation. - // - // Note that other dependencies of libgit2 like openssl, libz, and libssh2 - // are tracked via crates instead of this. Ideally this should be a crate as - // well. - let pkgconfig_file = dst.join("lib/pkgconfig/libgit2.pc"); - if let Ok(mut f) = File::open(&pkgconfig_file) { - let mut contents = String::new(); - t!(f.read_to_string(&mut contents)); - if contents.contains("-lhttp_parser") { - println!("cargo:rustc-link-lib=http_parser"); - } - } + features.push_str("#endif\n"); + fs::write(include.join("git2/sys/features.h"), features).unwrap(); + + cfg.compile("git2"); + + println!("cargo:root={}", dst.display()); if target.contains("windows") { println!("cargo:rustc-link-lib=winhttp"); println!("cargo:rustc-link-lib=rpcrt4"); println!("cargo:rustc-link-lib=ole32"); println!("cargo:rustc-link-lib=crypt32"); - println!("cargo:rustc-link-lib=static=git2"); - println!("cargo:rustc-link-search=native={}/lib", dst.display()); return } - println!("cargo:rustc-link-lib=static=git2"); - println!("cargo:rustc-link-search=native={}", dst.join("lib").display()); if target.contains("apple") { println!("cargo:rustc-link-lib=iconv"); println!("cargo:rustc-link-lib=framework=Security"); @@ -185,46 +131,29 @@ fn main() { } } -fn register_dep(dep: &str) { - if let Some(s) = env::var_os(&format!("DEP_{}_ROOT", dep)) { - if !cfg!(target_env = "msvc") { - prepend("PKG_CONFIG_PATH", Path::new(&s).join("lib/pkgconfig")); - } - return - } - 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() { - if !cfg!(target_env = "msvc") { - prepend("PKG_CONFIG_PATH", path); - } - return +fn cp_r(from: &Path, to: &Path) { + for e in from.read_dir().unwrap() { + let e = e.unwrap(); + let from = e.path(); + let to = to.join(e.file_name()); + if e.file_type().unwrap().is_dir() { + fs::create_dir_all(&to).unwrap(); + cp_r(&from, &to); + } else { + println!("{} => {}", from.display(), to.display()); + fs::copy(&from, &to).unwrap(); } } } -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 sanitize_sh(path: &Path) -> String { - let path = path.to_str().unwrap().replace("\\", "/"); - return change_drive(&path).unwrap_or(path); - - fn change_drive(s: &str) -> Option { - let mut ch = s.chars(); - let drive = ch.next().unwrap_or('C'); - if ch.next() != Some(':') { - return None - } - if ch.next() != Some('/') { - return None +fn add_c_files(build: &mut cc::Build, path: &Path) { + for e in path.read_dir().unwrap() { + let e = e.unwrap(); + let path = e.path(); + if e.file_type().unwrap().is_dir() { + // skip dirs for now + } else if path.extension().and_then(|s| s.to_str()) == Some("c") { + build.file(&path); } - Some(format!("/{}/{}", drive, &s[drive.len_utf8() + 2..])) } } diff --git a/systest/Cargo.toml b/systest/Cargo.toml index 7ed93c9f8c..dc60edc46f 100644 --- a/systest/Cargo.toml +++ b/systest/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Alex Crichton "] build = "build.rs" [dependencies] -libgit2-sys = { path = "../libgit2-sys" } +libgit2-sys = { path = "../libgit2-sys", features = ['curl', 'https', 'ssh'] } libc = "0.2" [build-dependencies] From e66d7077e38e8faeb181cb46b26fd526526c34ac Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 13 Sep 2018 09:50:56 -0700 Subject: [PATCH 02/10] Use bundled regex unconditionally --- libgit2-sys/build.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/libgit2-sys/build.rs b/libgit2-sys/build.rs index 1563e7ad9b..5e7de64a3a 100644 --- a/libgit2-sys/build.rs +++ b/libgit2-sys/build.rs @@ -50,6 +50,13 @@ fn main() { cfg.include("libgit2/deps/http-parser") .file("libgit2/deps/http-parser/http_parser.c"); + // Always use bundled regex for now + cfg.include("libgit2/deps/regex") + .file("libgit2/deps/regex/regex.c") + .file("libgit2/deps/regex/regcomp.c") + .file("libgit2/deps/regex/regexec.c") + .file("libgit2/deps/regex/regex_internal.c"); + if windows { add_c_files(&mut cfg, "libgit2/src/win32".as_ref()); } else { From 74d2fd7b130954f83953afe2e8607babdd03bd38 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 13 Sep 2018 10:33:50 -0700 Subject: [PATCH 03/10] Fix a few more --- libgit2-sys/build.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/libgit2-sys/build.rs b/libgit2-sys/build.rs index 5e7de64a3a..d5227fcd71 100644 --- a/libgit2-sys/build.rs +++ b/libgit2-sys/build.rs @@ -52,13 +52,11 @@ fn main() { // Always use bundled regex for now cfg.include("libgit2/deps/regex") - .file("libgit2/deps/regex/regex.c") - .file("libgit2/deps/regex/regcomp.c") - .file("libgit2/deps/regex/regexec.c") - .file("libgit2/deps/regex/regex_internal.c"); + .file("libgit2/deps/regex/regex.c"); if windows { add_c_files(&mut cfg, "libgit2/src/win32".as_ref()); + cfg.define("STRSAFE_NO_DEPRECATE", None); } else { add_c_files(&mut cfg, "libgit2/src/unix".as_ref()); } From d3aee0c0057bfc2623f246983eb0f518881db41c Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 13 Sep 2018 10:48:19 -0700 Subject: [PATCH 04/10] Make some dirs --- libgit2-sys/build.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/libgit2-sys/build.rs b/libgit2-sys/build.rs index d5227fcd71..9e05fd8191 100644 --- a/libgit2-sys/build.rs +++ b/libgit2-sys/build.rs @@ -27,6 +27,7 @@ fn main() { let dst = PathBuf::from(env::var_os("OUT_DIR").unwrap()); let include = dst.join("include"); let mut cfg = cc::Build::new(); + fs::create_dir_all(&include).unwrap(); // Copy over all header files cp_r("libgit2/include".as_ref(), &include); @@ -67,7 +68,13 @@ fn main() { features.push_str("#define INCLUDE_features_h\n"); features.push_str("#define GIT_THREADS 1\n"); features.push_str("#define GIT_USE_NSEC 1\n"); - features.push_str("#define GIT_USE_STAT_MTIM 1\n"); + + if target.contains("apple") { + features.push_str("#define GIT_USE_STAT_MTIMESPEC 1\n"); + } else { + features.push_str("#define GIT_USE_STAT_MTIM 1\n"); + } + if env::var("CARGO_CFG_TARGET_POINTER_WIDTH").unwrap() == "32" { features.push_str("#define GIT_ARCH_32 1\n"); } else { From df743008c7f493e801c59a586e42f9736782e79b Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 13 Sep 2018 11:17:15 -0700 Subject: [PATCH 05/10] Remove CMake from travis --- .travis.yml | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/.travis.yml b/.travis.yml index 10e97eafb0..c7ba589fab 100644 --- a/.travis.yml +++ b/.travis.yml @@ -42,13 +42,3 @@ env: notifications: email: on_success: never - -addons: - apt: - sources: - - kalakris-cmake - packages: - - cmake - - libcurl4-openssl-dev - - libelf-dev - - libdw-dev From 035ba9d4e969c81ddcbf82fa0cbe5f7960bc2267 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 13 Sep 2018 11:23:53 -0700 Subject: [PATCH 06/10] Fix no-default-features builds --- libgit2-sys/build.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libgit2-sys/build.rs b/libgit2-sys/build.rs index 9e05fd8191..72d9e06ccb 100644 --- a/libgit2-sys/build.rs +++ b/libgit2-sys/build.rs @@ -106,7 +106,10 @@ fn main() { cfg.include(path); } } + } else { + cfg.file("libgit2/src/hash/hash_generic.c"); } + if curl { features.push_str("#define GIT_CURL 1\n"); if let Some(path) = env::var_os("DEP_CURL_INCLUDE") { From 61393cd15efc0cd3f37f5a6ee046adec872de7d6 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 13 Sep 2018 11:27:13 -0700 Subject: [PATCH 07/10] Tweak README --- README.md | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index b060602d36..83c158607f 100644 --- a/README.md +++ b/README.md @@ -20,8 +20,6 @@ library, the libgit2-sys crate will figure that and/or build that for you. ## Building git2-rs -First, you'll need to install _CMake_. Afterwards, just run: - ```sh $ git clone https://github.com/alexcrichton/git2-rs $ cd git2-rs @@ -42,14 +40,9 @@ To skip tests on a simple commit or doc-fixes, use `git commit --no-verify`. ## Building on OSX 10.10+ -Currently libssh2 requires linking against OpenSSL, and to compile libssh2 it -also needs to find the OpenSSL headers. On OSX 10.10+ the OpenSSL headers have -been removed, but if you're using Homebrew you can install them via: - -```sh -brew install openssl -``` - +If the `ssh` feature is enabled (and it is by default) then this library depends +on libssh2 which depends on OpenSSL. To get OpenSSL working follow the +[`openssl` crate's instructions](https://github.com/sfackler/rust-openssl#macos). # License From c9197f8f7e4635df5063e5381e221218ac0dab7c Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 13 Sep 2018 11:38:23 -0700 Subject: [PATCH 08/10] Hidden vis --- libgit2-sys/build.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/libgit2-sys/build.rs b/libgit2-sys/build.rs index 72d9e06ccb..19590a05cd 100644 --- a/libgit2-sys/build.rs +++ b/libgit2-sys/build.rs @@ -60,6 +60,7 @@ fn main() { cfg.define("STRSAFE_NO_DEPRECATE", None); } else { add_c_files(&mut cfg, "libgit2/src/unix".as_ref()); + cfg.flag("-fvisibility=hidden"); } let mut features = String::new(); From ce98d0326f4750a9bc71ef1b820b6ee839d7baff Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 13 Sep 2018 12:00:49 -0700 Subject: [PATCH 09/10] Bump minimum Rust --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index c7ba589fab..2855475e48 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,7 @@ git: matrix: include: - - rust: 1.21.0 + - rust: 1.26.0 - rust: stable - os: osx rust: stable From d4d474f0900a99ae30e8c1a72bcc435a5ab29bec Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 13 Sep 2018 12:10:12 -0700 Subject: [PATCH 10/10] FIx apple --- libgit2-sys/build.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/libgit2-sys/build.rs b/libgit2-sys/build.rs index 19590a05cd..467b13355d 100644 --- a/libgit2-sys/build.rs +++ b/libgit2-sys/build.rs @@ -99,7 +99,6 @@ fn main() { } else if target.contains("apple") { features.push_str("#define GIT_SECURE_TRANSPORT 1\n"); features.push_str("#define GIT_SHA1_COMMON_CRYPTO 1\n"); - cfg.file("libgit2/src/hash/hash_common_crypto.c"); } else { features.push_str("#define GIT_OPENSSL 1\n"); features.push_str("#define GIT_SHA1_OPENSSL 1\n");