]>
Commit | Line | Data |
---|---|---|
0a29b90c FG |
1 | use std::env; |
2 | use std::fs; | |
3 | use std::io; | |
4 | use std::path::{Path, PathBuf}; | |
5 | use std::process::Command; | |
6 | ||
781aab86 FG |
7 | /// Tries to use system libgit2 and emits necessary build script instructions. |
8 | fn 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 |
24 | fn 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 | "\ | |
41 | The environment variable `LIBGIT2_NO_VENDOR` has been set but no compatible system libgit2 could be found. | |
42 | The 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 | ||
260 | fn 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 | ||
275 | fn 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 | } |