]> git.proxmox.com Git - rustc.git/blame - vendor/libgit2-sys/build.rs
New upstream version 1.74.1+dfsg1
[rustc.git] / vendor / libgit2-sys / build.rs
CommitLineData
0a29b90c
FG
1use std::env;
2use std::fs;
3use std::io;
4use std::path::{Path, PathBuf};
5use std::process::Command;
6
781aab86
FG
7/// Tries to use system libgit2 and emits necessary build script instructions.
8fn try_system_libgit2() -> Result<pkg_config::Library, pkg_config::Error> {
9 let mut cfg = pkg_config::Config::new();
10 match cfg.range_version("1.7.1".."1.8.0").probe("libgit2") {
11 Ok(lib) => {
12 for include in &lib.include_paths {
13 println!("cargo:root={}", include.display());
14 }
15 Ok(lib)
16 }
17 Err(e) => {
18 println!("cargo:warning=failed to probe system libgit2: {e}");
19 Err(e)
20 }
21 }
22}
23
0a29b90c
FG
24fn main() {
25 let https = env::var("CARGO_FEATURE_HTTPS").is_ok();
26 let ssh = env::var("CARGO_FEATURE_SSH").is_ok();
27 let vendored = env::var("CARGO_FEATURE_VENDORED").is_ok();
28 let zlib_ng_compat = env::var("CARGO_FEATURE_ZLIB_NG_COMPAT").is_ok();
29
781aab86
FG
30 // Specify `LIBGIT2_NO_VENDOR` to force to use system libgit2.
31 // Due to the additive nature of Cargo features, if some crate in the
32 // dependency graph activates `vendored` feature, there is no way to revert
33 // it back. This env var serves as a workaround for this purpose.
34 println!("cargo:rerun-if-env-changed=LIBGIT2_NO_VENDOR");
35 let forced_no_vendor = env::var_os("LIBGIT2_NO_VENDOR").map_or(false, |s| s != "0");
36
37 if forced_no_vendor {
38 if try_system_libgit2().is_err() {
39 panic!(
40 "\
41The environment variable `LIBGIT2_NO_VENDOR` has been set but no compatible system libgit2 could be found.
42The build is now aborting. To disable, unset the variable or use `LIBGIT2_NO_VENDOR=0`.
43",
44 );
45 }
46
47 // We've reached here, implying we're using system libgit2.
48 return;
49 }
50
0a29b90c
FG
51 // To use zlib-ng in zlib-compat mode, we have to build libgit2 ourselves.
52 let try_to_use_system_libgit2 = !vendored && !zlib_ng_compat;
781aab86
FG
53 if try_to_use_system_libgit2 && try_system_libgit2().is_ok() {
54 // using system libgit2 has worked
55 return;
0a29b90c
FG
56 }
57
58 println!("cargo:rustc-cfg=libgit2_vendored");
59
60 if !Path::new("libgit2/src").exists() {
61 let _ = Command::new("git")
62 .args(&["submodule", "update", "--init", "libgit2"])
63 .status();
64 }
65
66 let target = env::var("TARGET").unwrap();
67 let windows = target.contains("windows");
68 let dst = PathBuf::from(env::var_os("OUT_DIR").unwrap());
69 let include = dst.join("include");
70 let mut cfg = cc::Build::new();
71 fs::create_dir_all(&include).unwrap();
72
73 // Copy over all header files
74 cp_r("libgit2/include", &include);
75
76 cfg.include(&include)
77 .include("libgit2/src/libgit2")
78 .include("libgit2/src/util")
79 .out_dir(dst.join("build"))
80 .warnings(false);
81
82 // Include all cross-platform C files
83 add_c_files(&mut cfg, "libgit2/src/libgit2");
84 add_c_files(&mut cfg, "libgit2/src/util");
0a29b90c
FG
85
86 // These are activated by features, but they're all unconditionally always
87 // compiled apparently and have internal #define's to make sure they're
88 // compiled correctly.
89 add_c_files(&mut cfg, "libgit2/src/libgit2/transports");
90 add_c_files(&mut cfg, "libgit2/src/libgit2/streams");
91
92 // Always use bundled http-parser for now
93 cfg.include("libgit2/deps/http-parser")
94 .file("libgit2/deps/http-parser/http_parser.c");
95
781aab86
FG
96 // external/system xdiff is not yet supported
97 cfg.include("libgit2/deps/xdiff");
98 add_c_files(&mut cfg, "libgit2/deps/xdiff");
99
0a29b90c
FG
100 // Use the included PCRE regex backend.
101 //
102 // Ideally these defines would be specific to the pcre files (or placed in
103 // a config.h), but since libgit2 already has a config.h used for other
104 // reasons, just define on the command-line for everything. Perhaps there
105 // is some way with cc to have different instructions per-file?
106 cfg.define("GIT_REGEX_BUILTIN", "1")
107 .include("libgit2/deps/pcre")
108 .define("HAVE_STDINT_H", Some("1"))
109 .define("HAVE_MEMMOVE", Some("1"))
110 .define("NO_RECURSE", Some("1"))
111 .define("NEWLINE", Some("10"))
112 .define("POSIX_MALLOC_THRESHOLD", Some("10"))
113 .define("LINK_SIZE", Some("2"))
114 .define("PARENS_NEST_LIMIT", Some("250"))
115 .define("MATCH_LIMIT", Some("10000000"))
116 .define("MATCH_LIMIT_RECURSION", Some("MATCH_LIMIT"))
117 .define("MAX_NAME_SIZE", Some("32"))
118 .define("MAX_NAME_COUNT", Some("10000"));
119 // "no symbols" warning on pcre_string_utils.c is because it is only used
120 // when when COMPILE_PCRE8 is not defined, which is the default.
121 add_c_files(&mut cfg, "libgit2/deps/pcre");
122
123 cfg.file("libgit2/src/util/allocators/failalloc.c");
124 cfg.file("libgit2/src/util/allocators/stdalloc.c");
125
126 if windows {
127 add_c_files(&mut cfg, "libgit2/src/util/win32");
128 cfg.define("STRSAFE_NO_DEPRECATE", None);
129 cfg.define("WIN32", None);
130 cfg.define("_WIN32_WINNT", Some("0x0600"));
131
132 // libgit2's build system claims that forks like mingw-w64 of MinGW
133 // still want this define to use C99 stdio functions automatically.
134 // Apparently libgit2 breaks at runtime if this isn't here? Who knows!
135 if target.contains("gnu") {
136 cfg.define("__USE_MINGW_ANSI_STDIO", "1");
137 }
138 } else {
139 add_c_files(&mut cfg, "libgit2/src/util/unix");
140 cfg.flag("-fvisibility=hidden");
141 }
142 if target.contains("solaris") || target.contains("illumos") {
143 cfg.define("_POSIX_C_SOURCE", "200112L");
144 cfg.define("__EXTENSIONS__", None);
145 }
146
147 let mut features = String::new();
148
149 features.push_str("#ifndef INCLUDE_features_h\n");
150 features.push_str("#define INCLUDE_features_h\n");
151 features.push_str("#define GIT_THREADS 1\n");
152 features.push_str("#define GIT_TRACE 1\n");
153
154 if !target.contains("android") {
155 features.push_str("#define GIT_USE_NSEC 1\n");
156 }
157
781aab86
FG
158 if windows {
159 features.push_str("#define GIT_IO_WSAPOLL 1\n");
160 } else {
161 // Should we fallback to `select` as more systems have that?
162 features.push_str("#define GIT_IO_POLL 1\n");
163 features.push_str("#define GIT_IO_SELECT 1\n");
164 }
165
0a29b90c
FG
166 if target.contains("apple") {
167 features.push_str("#define GIT_USE_STAT_MTIMESPEC 1\n");
168 } else {
169 features.push_str("#define GIT_USE_STAT_MTIM 1\n");
170 }
171
172 if env::var("CARGO_CFG_TARGET_POINTER_WIDTH").unwrap() == "32" {
173 features.push_str("#define GIT_ARCH_32 1\n");
174 } else {
175 features.push_str("#define GIT_ARCH_64 1\n");
176 }
177
178 if ssh {
179 if let Some(path) = env::var_os("DEP_SSH2_INCLUDE") {
180 cfg.include(path);
181 }
182 features.push_str("#define GIT_SSH 1\n");
183 features.push_str("#define GIT_SSH_MEMORY_CREDENTIALS 1\n");
184 }
185 if https {
186 features.push_str("#define GIT_HTTPS 1\n");
187
188 if windows {
189 features.push_str("#define GIT_WINHTTP 1\n");
190 } else if target.contains("apple") {
191 features.push_str("#define GIT_SECURE_TRANSPORT 1\n");
192 } else {
193 features.push_str("#define GIT_OPENSSL 1\n");
194 if let Some(path) = env::var_os("DEP_OPENSSL_INCLUDE") {
195 cfg.include(path);
196 }
197 }
198 }
199
200 // Use the CollisionDetection SHA1 implementation.
201 features.push_str("#define GIT_SHA1_COLLISIONDETECT 1\n");
202 cfg.define("SHA1DC_NO_STANDARD_INCLUDES", "1");
203 cfg.define("SHA1DC_CUSTOM_INCLUDE_SHA1_C", "\"common.h\"");
204 cfg.define("SHA1DC_CUSTOM_INCLUDE_UBC_CHECK_C", "\"common.h\"");
205 cfg.file("libgit2/src/util/hash/collisiondetect.c");
206 cfg.file("libgit2/src/util/hash/sha1dc/sha1.c");
207 cfg.file("libgit2/src/util/hash/sha1dc/ubc_check.c");
208
209 if https {
210 if windows {
211 features.push_str("#define GIT_SHA256_WIN32 1\n");
212 cfg.file("libgit2/src/util/hash/win32.c");
213 } else if target.contains("apple") {
214 features.push_str("#define GIT_SHA256_COMMON_CRYPTO 1\n");
215 cfg.file("libgit2/src/util/hash/common_crypto.c");
216 } else {
217 features.push_str("#define GIT_SHA256_OPENSSL 1\n");
218 cfg.file("libgit2/src/util/hash/openssl.c");
219 }
220 } else {
221 features.push_str("#define GIT_SHA256_BUILTIN 1\n");
222 cfg.file("libgit2/src/util/hash/builtin.c");
223 cfg.file("libgit2/src/util/hash/rfc6234/sha224-256.c");
224 }
225
226 if let Some(path) = env::var_os("DEP_Z_INCLUDE") {
227 cfg.include(path);
228 }
229
230 if target.contains("apple") {
231 features.push_str("#define GIT_USE_ICONV 1\n");
232 }
233
234 features.push_str("#endif\n");
235 fs::write(include.join("git2_features.h"), features).unwrap();
236
237 cfg.compile("git2");
238
239 println!("cargo:root={}", dst.display());
240
241 if target.contains("windows") {
242 println!("cargo:rustc-link-lib=winhttp");
243 println!("cargo:rustc-link-lib=rpcrt4");
244 println!("cargo:rustc-link-lib=ole32");
245 println!("cargo:rustc-link-lib=crypt32");
781aab86 246 println!("cargo:rustc-link-lib=secur32");
0a29b90c
FG
247 }
248
249 if target.contains("apple") {
250 println!("cargo:rustc-link-lib=iconv");
251 println!("cargo:rustc-link-lib=framework=Security");
252 println!("cargo:rustc-link-lib=framework=CoreFoundation");
253 }
254
255 println!("cargo:rerun-if-changed=libgit2/include");
256 println!("cargo:rerun-if-changed=libgit2/src");
257 println!("cargo:rerun-if-changed=libgit2/deps");
258}
259
260fn cp_r(from: impl AsRef<Path>, to: impl AsRef<Path>) {
261 for e in from.as_ref().read_dir().unwrap() {
262 let e = e.unwrap();
263 let from = e.path();
264 let to = to.as_ref().join(e.file_name());
265 if e.file_type().unwrap().is_dir() {
266 fs::create_dir_all(&to).unwrap();
267 cp_r(&from, &to);
268 } else {
269 println!("{} => {}", from.display(), to.display());
270 fs::copy(&from, &to).unwrap();
271 }
272 }
273}
274
275fn add_c_files(build: &mut cc::Build, path: impl AsRef<Path>) {
276 let path = path.as_ref();
277 if !path.exists() {
278 panic!("Path {} does not exist", path.display());
279 }
280 // sort the C files to ensure a deterministic build for reproducible builds
281 let dir = path.read_dir().unwrap();
282 let mut paths = dir.collect::<io::Result<Vec<_>>>().unwrap();
283 paths.sort_by_key(|e| e.path());
284
285 for e in paths {
286 let path = e.path();
287 if e.file_type().unwrap().is_dir() {
288 // skip dirs for now
289 } else if path.extension().and_then(|s| s.to_str()) == Some("c") {
290 build.file(&path);
291 }
292 }
293}