1 // Copyright © 2017–2021 Trevor Spiteri
2
3 // Copying and distribution of this file, with or without
4 // modification, are permitted in any medium without royalty provided
5 // the copyright notice and this notice are preserved. This file is
6 // offered as-is, without any warranty.
7
8 // Notes:
9 //
10 // 1. Configure GMP with --enable-fat so that built file is portable.
11 //
12 // 2. Configure GMP, MPFR and MPC with: --disable-shared --with-pic
13 //
14 // 3. Add symlinks to work around relative path issues in MPFR and MPC.
15 // In MPFR: ln -s ../gmp-build
16 // In MPC: ln -s ../mpfr-src ../mpfr-build ../gmp-build .
17 //
18 // 4. Use relative paths for configure otherwise msys/mingw might be
19 // confused with drives and such.
20
21 #[cfg(unix)]
22 use std::os::unix::fs as unix_fs;
23 #[cfg(windows)]
24 use std::os::windows::fs as windows_fs;
25 use std::{
26 cmp::Ordering,
27 env,
28 ffi::{OsStr, OsString},
29 fs::{self, File},
30 io::{BufRead, BufReader, BufWriter, Result as IoResult, Write},
31 path::{Path, PathBuf},
32 process::{Command, Stdio},
33 str,
34 };
35
36 const GMP_DIR: &str = "gmp-6.2.1-c";
37 const MPFR_DIR: &str = "mpfr-4.1.0-p13-c";
38 const MPC_DIR: &str = "mpc-1.2.1-c";
39 const GMP_VER: (i32, i32, i32) = (6, 2, 1);
40 const MPFR_VER: (i32, i32, i32) = (4, 1, 0);
41 const MPC_VER: (i32, i32, i32) = (1, 2, 1);
42
43 #[derive(Clone, Copy, PartialEq)]
44 enum Target {
45 Mingw,
46 Msvc,
47 Other,
48 }
49
50 struct Environment {
51 rustc: OsString,
52 c_compiler: OsString,
53 target: Target,
54 cross_target: Option<String>,
55 c_no_tests: bool,
56 src_dir: PathBuf,
57 out_dir: PathBuf,
58 lib_dir: PathBuf,
59 include_dir: PathBuf,
60 build_dir: PathBuf,
61 cache_dir: Option<PathBuf>,
62 jobs: OsString,
63 version_prefix: String,
64 version_patch: Option<u64>,
65 use_system_libs: bool,
66 workaround_47048: Workaround47048,
67 }
68
69 #[derive(Clone, Copy, PartialEq)]
70 enum Workaround47048 {
71 Yes,
72 No,
73 }
74
main()75 fn main() {
76 let rustc = cargo_env("RUSTC");
77 let c_compiler = env::var_os("CC").unwrap_or_else(|| "gcc".into());
78
79 let host = cargo_env("HOST")
80 .into_string()
81 .expect("env var HOST having sensible characters");
82 let raw_target = cargo_env("TARGET")
83 .into_string()
84 .expect("env var TARGET having sensible characters");
85 let force_cross = there_is_env("CARGO_FEATURE_FORCE_CROSS");
86 if !force_cross && !compilation_target_allowed(&host, &raw_target) {
87 panic!(
88 "Cross compilation from {} to {} not supported! \
89 Use the `force-cross` feature to cross compile anyway.",
90 host, raw_target
91 );
92 }
93
94 let target = if raw_target.contains("-windows-msvc") {
95 Target::Msvc
96 } else if raw_target.contains("-windows-gnu") {
97 Target::Mingw
98 } else {
99 Target::Other
100 };
101 let cross_target = if host == raw_target {
102 None
103 } else {
104 Some(raw_target)
105 };
106
107 let c_no_tests = there_is_env("CARGO_FEATURE_C_NO_TESTS");
108
109 let src_dir = PathBuf::from(cargo_env("CARGO_MANIFEST_DIR"));
110 let out_dir = PathBuf::from(cargo_env("OUT_DIR"));
111
112 let (version_prefix, version_patch) = get_version();
113
114 println!("cargo:rerun-if-env-changed=GMP_MPFR_SYS_CACHE");
115 let cache_dir = match env::var_os("GMP_MPFR_SYS_CACHE") {
116 Some(ref c) if c.is_empty() || c == "_" => None,
117 Some(c) => Some(PathBuf::from(c)),
118 None => system_cache_dir().map(|c| c.join("gmp-mpfr-sys")),
119 };
120 let cache_target = cross_target.as_ref().unwrap_or(&host);
121 let cache_dir = cache_dir.map(|cache| cache.join(&version_prefix).join(cache_target));
122
123 let use_system_libs = there_is_env("CARGO_FEATURE_USE_SYSTEM_LIBS");
124 if use_system_libs {
125 match target {
126 Target::Msvc => panic!("the use-system-libs feature is not supported on this target"),
127 Target::Mingw => mingw_pkg_config_libdir_or_panic(),
128 _ => {}
129 }
130 }
131 let mut env = Environment {
132 rustc,
133 c_compiler,
134 target,
135 cross_target,
136 c_no_tests,
137 src_dir,
138 out_dir: out_dir.clone(),
139 lib_dir: out_dir.join("lib"),
140 include_dir: out_dir.join("include"),
141 build_dir: out_dir.join("build"),
142 cache_dir,
143 jobs: cargo_env("NUM_JOBS"),
144 version_prefix,
145 version_patch,
146 use_system_libs,
147 workaround_47048: Workaround47048::No,
148 };
149 env.check_feature(
150 "extended_key_value_attributes",
151 TRY_EXTENDED_KEY_VALUE_ATTRIBUTES,
152 None,
153 );
154 env.check_feature("unsafe_in_unsafe", TRY_UNSAFE_IN_UNSAFE, None);
155
156 // make sure we have target directories
157 create_dir_or_panic(&env.lib_dir);
158 create_dir_or_panic(&env.include_dir);
159
160 env.workaround_47048 = check_for_bug_47048(&env);
161
162 if env.use_system_libs {
163 check_system_libs(&env);
164 } else {
165 compile_libs(&env);
166 }
167 }
168
check_system_libs(env: &Environment)169 fn check_system_libs(env: &Environment) {
170 let build_dir_existed = env.build_dir.exists();
171 let try_dir = env.build_dir.join("system_libs");
172 remove_dir_or_panic(&try_dir);
173 create_dir_or_panic(&try_dir);
174 println!("$ cd {:?}", try_dir);
175 let mut cmd;
176
177 println!("$ #Check for system GMP");
178 create_file_or_panic(&try_dir.join("system_gmp.c"), SYSTEM_GMP_C);
179
180 cmd = Command::new(&env.c_compiler);
181 cmd.current_dir(&try_dir)
182 .args(&["-fPIC", "-L/usr/local/lib", "-I/usr/local/include", "system_gmp.c", "-lgmp", "-o", "system_gmp.exe"]);
183 execute(cmd);
184
185 cmd = Command::new(try_dir.join("system_gmp.exe"));
186 cmd.current_dir(&try_dir);
187 execute(cmd);
188 process_gmp_header(
189 env,
190 &try_dir.join("system_gmp.out"),
191 Some(&env.out_dir.join("gmp_h.rs")),
192 )
193 .unwrap_or_else(|e| panic!("{}", e));
194
195 let feature_mpfr = there_is_env("CARGO_FEATURE_MPFR");
196 let feature_mpc = there_is_env("CARGO_FEATURE_MPC");
197
198 if feature_mpfr {
199 println!("$ #Check for system MPFR");
200 create_file_or_panic(&try_dir.join("system_mpfr.c"), SYSTEM_MPFR_C);
201
202 cmd = Command::new(&env.c_compiler);
203 cmd.current_dir(&try_dir).args(&[
204 "-fPIC",
205 "-L/usr/local/lib", "-I/usr/local/include",
206 "system_mpfr.c",
207 "-lmpfr",
208 "-lgmp",
209 "-o",
210 "system_mpfr.exe",
211 ]);
212 execute(cmd);
213
214 cmd = Command::new(try_dir.join("system_mpfr.exe"));
215 cmd.current_dir(&try_dir);
216 execute(cmd);
217 process_mpfr_header(
218 env,
219 &try_dir.join("system_mpfr.out"),
220 Some(&env.out_dir.join("mpfr_h.rs")),
221 )
222 .unwrap_or_else(|e| panic!("{}", e));
223 }
224
225 if feature_mpc {
226 println!("$ #Check for system MPC");
227 create_file_or_panic(&try_dir.join("system_mpc.c"), SYSTEM_MPC_C);
228
229 cmd = Command::new(&env.c_compiler);
230 cmd.current_dir(&try_dir).args(&[
231 "-fPIC",
232 "-L/usr/local/lib", "-I/usr/local/include",
233 "system_mpc.c",
234 "-lmpc",
235 "-lgmp",
236 "-o",
237 "system_mpc.exe",
238 ]);
239 execute(cmd);
240
241 cmd = Command::new(try_dir.join("system_mpc.exe"));
242 cmd.current_dir(&try_dir);
243 execute(cmd);
244 process_mpc_header(
245 env,
246 &try_dir.join("system_mpc.out"),
247 Some(&env.out_dir.join("mpc_h.rs")),
248 )
249 .unwrap_or_else(|e| panic!("{}", e));
250 }
251
252 if !there_is_env("CARGO_FEATURE_CNODELETE") {
253 if build_dir_existed {
254 let _ = remove_dir(&try_dir);
255 } else {
256 remove_dir_or_panic(&env.build_dir);
257 }
258 }
259
260 write_link_info(env, feature_mpfr, feature_mpc);
261 }
262
compile_libs(env: &Environment)263 fn compile_libs(env: &Environment) {
264 let gmp_ah = (env.lib_dir.join("libgmp.a"), env.include_dir.join("gmp.h"));
265 let mpc_ah = if there_is_env("CARGO_FEATURE_MPC") {
266 Some((env.lib_dir.join("libmpc.a"), env.include_dir.join("mpc.h")))
267 } else {
268 None
269 };
270 let mpfr_ah = if mpc_ah.is_some() || there_is_env("CARGO_FEATURE_MPFR") {
271 Some((
272 env.lib_dir.join("libmpfr.a"),
273 env.include_dir.join("mpfr.h"),
274 ))
275 } else {
276 None
277 };
278
279 let NeedCompile {
280 gmp: compile_gmp,
281 mpfr: compile_mpfr,
282 mpc: compile_mpc,
283 } = need_compile(env, &gmp_ah, &mpfr_ah, &mpc_ah);
284 if compile_gmp {
285 check_for_msvc(env);
286 remove_dir_or_panic(&env.build_dir);
287 create_dir_or_panic(&env.build_dir);
288 link_dir(&env.src_dir.join(GMP_DIR), &env.build_dir.join("gmp-src"));
289 let (ref a, ref h) = gmp_ah;
290 build_gmp(env, a, h);
291 }
292 if compile_mpfr {
293 link_dir(&env.src_dir.join(MPFR_DIR), &env.build_dir.join("mpfr-src"));
294 let (ref a, ref h) = *mpfr_ah.as_ref().unwrap();
295 build_mpfr(env, a, h);
296 }
297 if compile_mpc {
298 link_dir(&env.src_dir.join(MPC_DIR), &env.build_dir.join("mpc-src"));
299 let (ref a, ref h) = *mpc_ah.as_ref().unwrap();
300 build_mpc(env, a, h);
301 }
302 if compile_gmp {
303 if !there_is_env("CARGO_FEATURE_CNODELETE") {
304 remove_dir_or_panic(&env.build_dir);
305 }
306 if save_cache(env, &gmp_ah, &mpfr_ah, &mpc_ah) {
307 clear_cache_redundancies(env, mpfr_ah.is_some(), mpc_ah.is_some());
308 }
309 }
310 process_gmp_header(env, &gmp_ah.1, Some(&env.out_dir.join("gmp_h.rs")))
311 .unwrap_or_else(|e| panic!("{}", e));
312 if let Some(ref mpfr_ah) = mpfr_ah {
313 process_mpfr_header(env, &mpfr_ah.1, Some(&env.out_dir.join("mpfr_h.rs")))
314 .unwrap_or_else(|e| panic!("{}", e));
315 }
316 if let Some(ref mpc_ah) = mpc_ah {
317 process_mpc_header(env, &mpc_ah.1, Some(&env.out_dir.join("mpc_h.rs")))
318 .unwrap_or_else(|e| panic!("{}", e));
319 }
320 write_link_info(env, mpfr_ah.is_some(), mpc_ah.is_some());
321 }
322
get_version() -> (String, Option<u64>)323 fn get_version() -> (String, Option<u64>) {
324 let version = cargo_env("CARGO_PKG_VERSION")
325 .into_string()
326 .unwrap_or_else(|e| panic!("version not in utf-8: {:?}", e));
327 let last_dot = version
328 .rfind('.')
329 .unwrap_or_else(|| panic!("version has no dots: {}", version));
330 if last_dot == 0 {
331 panic!("version starts with dot: {}", version);
332 }
333 match version[last_dot + 1..].parse::<u64>() {
334 Ok(patch) => {
335 let mut v = version;
336 v.truncate(last_dot);
337 (v, Some(patch))
338 }
339 Err(_) => (version, None),
340 }
341 }
342
343 struct NeedCompile {
344 gmp: bool,
345 mpfr: bool,
346 mpc: bool,
347 }
348
need_compile( env: &Environment, gmp_ah: &(PathBuf, PathBuf), mpfr_ah: &Option<(PathBuf, PathBuf)>, mpc_ah: &Option<(PathBuf, PathBuf)>, ) -> NeedCompile349 fn need_compile(
350 env: &Environment,
351 gmp_ah: &(PathBuf, PathBuf),
352 mpfr_ah: &Option<(PathBuf, PathBuf)>,
353 mpc_ah: &Option<(PathBuf, PathBuf)>,
354 ) -> NeedCompile {
355 let gmp_fine = gmp_ah.0.is_file() && gmp_ah.1.is_file();
356 let mpfr_fine = match *mpfr_ah {
357 Some((ref a, ref h)) => a.is_file() && h.is_file(),
358 None => true,
359 };
360 let mpc_fine = match *mpc_ah {
361 Some((ref a, ref h)) => a.is_file() && h.is_file(),
362 None => true,
363 };
364 if gmp_fine && mpfr_fine && mpc_fine {
365 if should_save_cache(env, mpfr_ah.is_some(), mpc_ah.is_some())
366 && save_cache(env, gmp_ah, mpfr_ah, mpc_ah)
367 {
368 clear_cache_redundancies(env, mpfr_ah.is_some(), mpc_ah.is_some());
369 }
370 return NeedCompile {
371 gmp: false,
372 mpfr: false,
373 mpc: false,
374 };
375 } else if load_cache(env, gmp_ah, mpfr_ah, mpc_ah) {
376 // if loading cache works, we're done
377 return NeedCompile {
378 gmp: false,
379 mpfr: false,
380 mpc: false,
381 };
382 }
383 let need_mpc = !mpc_fine;
384 let need_mpfr = need_mpc || !mpfr_fine;
385 let need_gmp = need_mpfr || !gmp_fine;
386 NeedCompile {
387 gmp: need_gmp,
388 mpfr: need_mpfr,
389 mpc: need_mpc,
390 }
391 }
392
save_cache( env: &Environment, gmp_ah: &(PathBuf, PathBuf), mpfr_ah: &Option<(PathBuf, PathBuf)>, mpc_ah: &Option<(PathBuf, PathBuf)>, ) -> bool393 fn save_cache(
394 env: &Environment,
395 gmp_ah: &(PathBuf, PathBuf),
396 mpfr_ah: &Option<(PathBuf, PathBuf)>,
397 mpc_ah: &Option<(PathBuf, PathBuf)>,
398 ) -> bool {
399 let cache_dir = match env.cache_dir {
400 Some(ref s) => s,
401 None => return false,
402 };
403 let version_dir = match env.version_patch {
404 None => cache_dir.join(&env.version_prefix),
405 Some(patch) => cache_dir.join(format!("{}.{}", env.version_prefix, patch)),
406 };
407 let mut ok = create_dir(&version_dir).is_ok();
408 let dir = if env.c_no_tests {
409 let no_tests_dir = version_dir.join("c-no-tests");
410 ok = ok && create_dir(&no_tests_dir).is_ok();
411 no_tests_dir
412 } else {
413 version_dir
414 };
415 let (ref a, ref h) = *gmp_ah;
416 ok = ok && copy_file(a, &dir.join("libgmp.a")).is_ok();
417 ok = ok && copy_file(h, &dir.join("gmp.h")).is_ok();
418 if let Some((ref a, ref h)) = *mpfr_ah {
419 ok = ok && copy_file(a, &dir.join("libmpfr.a")).is_ok();
420 ok = ok && copy_file(h, &dir.join("mpfr.h")).is_ok();
421 }
422 if let Some((ref a, ref h)) = *mpc_ah {
423 ok = ok && copy_file(a, &dir.join("libmpc.a")).is_ok();
424 ok = ok && copy_file(h, &dir.join("mpc.h")).is_ok();
425 }
426 ok
427 }
428
clear_cache_redundancies(env: &Environment, mpfr: bool, mpc: bool)429 fn clear_cache_redundancies(env: &Environment, mpfr: bool, mpc: bool) {
430 let cache_dir = match env.cache_dir {
431 Some(ref s) => s,
432 None => return,
433 };
434 let cache_dirs = cache_directories(env, cache_dir)
435 .into_iter()
436 .rev()
437 .filter(|x| match env.version_patch {
438 None => x.1.is_none(),
439 Some(patch) => x.1.map(|p| p <= patch).unwrap_or(false),
440 });
441 for (version_dir, version_patch) in cache_dirs {
442 let no_tests_dir = version_dir.join("c-no-tests");
443
444 // do not clear newly saved cache
445 if version_patch == env.version_patch {
446 // but if we tested and c-no-tests directory doesn't have more libs, remove it
447 if !env.c_no_tests
448 && (mpc || !no_tests_dir.join("libmpc.a").is_file())
449 && (mpfr || !no_tests_dir.join("libmpfr.a").is_file())
450 {
451 let _ = remove_dir(&no_tests_dir);
452 }
453
454 continue;
455 }
456
457 // Do not clear cache with more libraries than newly saved cache.
458
459 // First check c-no-tests subdirectory for more libs.
460 if (!mpc && no_tests_dir.join("libmpc.a").is_file())
461 || (!mpfr && no_tests_dir.join("libmpfr.a").is_file())
462 {
463 continue;
464 }
465 // Remove c-no-tests subdirectory as it does not have more libs.
466 let _ = remove_dir(&no_tests_dir);
467
468 let delete_version_dir_condition = if env.c_no_tests {
469 // We did not test, so version_dir must not contain any libs at all.
470 !version_dir.join("libgmp.a").is_file()
471 } else {
472 // We did test, so delete if it does not contain more libs.
473 (mpc || !version_dir.join("libmpc.a").is_file())
474 && (mpfr || !version_dir.join("libmpfr.a").is_file())
475 };
476 if delete_version_dir_condition {
477 let _ = remove_dir(&version_dir);
478 }
479 }
480 }
481
cache_directories(env: &Environment, base: &Path) -> Vec<(PathBuf, Option<u64>)>482 fn cache_directories(env: &Environment, base: &Path) -> Vec<(PathBuf, Option<u64>)> {
483 let dir = match fs::read_dir(base) {
484 Ok(dir) => dir,
485 Err(_) => return Vec::new(),
486 };
487 let mut vec = Vec::new();
488 for entry in dir {
489 let path = match entry {
490 Ok(e) => e.path(),
491 Err(_) => continue,
492 };
493 if !path.is_dir() {
494 continue;
495 }
496 let patch = {
497 let file_name = match path.file_name() {
498 Some(name) => name,
499 None => continue,
500 };
501 let path_str = match file_name.to_str() {
502 Some(p) => p,
503 None => continue,
504 };
505 if path_str == env.version_prefix {
506 None
507 } else if !path_str.starts_with(&env.version_prefix)
508 || !path_str[env.version_prefix.len()..].starts_with('.')
509 {
510 continue;
511 } else {
512 match path_str[env.version_prefix.len() + 1..].parse::<u64>() {
513 Ok(patch) => Some(patch),
514 Err(_) => continue,
515 }
516 }
517 };
518 vec.push((path, patch));
519 }
520 vec.sort_by_key(|k| k.1);
521 vec
522 }
523
load_cache( env: &Environment, gmp_ah: &(PathBuf, PathBuf), mpfr_ah: &Option<(PathBuf, PathBuf)>, mpc_ah: &Option<(PathBuf, PathBuf)>, ) -> bool524 fn load_cache(
525 env: &Environment,
526 gmp_ah: &(PathBuf, PathBuf),
527 mpfr_ah: &Option<(PathBuf, PathBuf)>,
528 mpc_ah: &Option<(PathBuf, PathBuf)>,
529 ) -> bool {
530 let cache_dir = match env.cache_dir {
531 Some(ref s) => s,
532 None => return false,
533 };
534 let env_version_patch = env.version_patch;
535 let cache_dirs = cache_directories(env, cache_dir)
536 .into_iter()
537 .rev()
538 .filter(|x| match env_version_patch {
539 None => x.1.is_none(),
540 Some(patch) => x.1.map(|p| p >= patch).unwrap_or(false),
541 })
542 .collect::<Vec<_>>();
543 let suffixes: &[Option<&str>] = if env.c_no_tests {
544 &[None, Some("c-no-tests")]
545 } else {
546 // we need tests, so do not try to load from c-no-tests
547 &[None]
548 };
549 for suffix in suffixes {
550 for (version_dir, _) in &cache_dirs {
551 let joined;
552 let dir = if let Some(suffix) = suffix {
553 joined = version_dir.join(suffix);
554 &joined
555 } else {
556 version_dir
557 };
558 let mut ok = true;
559 if let Some((ref a, ref h)) = *mpc_ah {
560 ok = ok && copy_file(&dir.join("libmpc.a"), a).is_ok();
561 let header = dir.join("mpc.h");
562 ok = ok && process_mpc_header(env, &header, None).is_ok();
563 ok = ok && copy_file(&header, h).is_ok();
564 }
565 if let Some((ref a, ref h)) = *mpfr_ah {
566 ok = ok && copy_file(&dir.join("libmpfr.a"), a).is_ok();
567 let header = dir.join("mpfr.h");
568 ok = ok && process_mpfr_header(env, &header, None).is_ok();
569 ok = ok && copy_file(&header, h).is_ok();
570 }
571 let (ref a, ref h) = *gmp_ah;
572 ok = ok && copy_file(&dir.join("libgmp.a"), a).is_ok();
573 let header = dir.join("gmp.h");
574 ok = ok && process_gmp_header(env, &header, None).is_ok();
575 ok = ok && copy_file(&header, h).is_ok();
576
577 if ok {
578 return true;
579 }
580 }
581 }
582 false
583 }
584
should_save_cache(env: &Environment, mpfr: bool, mpc: bool) -> bool585 fn should_save_cache(env: &Environment, mpfr: bool, mpc: bool) -> bool {
586 let cache_dir = match env.cache_dir {
587 Some(ref s) => s,
588 None => return false,
589 };
590 let cache_dirs = cache_directories(env, cache_dir)
591 .into_iter()
592 .rev()
593 .filter(|x| match env.version_patch {
594 None => x.1.is_none(),
595 Some(patch) => x.1.map(|p| p >= patch).unwrap_or(false),
596 })
597 .collect::<Vec<_>>();
598 let suffixes: &[Option<&str>] = if env.c_no_tests {
599 &[None, Some("c-no-tests")]
600 } else {
601 // we need tests, so do not try to load from c-no-tests
602 &[None]
603 };
604 for suffix in suffixes {
605 for (version_dir, _) in &cache_dirs {
606 let joined;
607 let dir = if let Some(suffix) = suffix {
608 joined = version_dir.join(suffix);
609 &joined
610 } else {
611 version_dir
612 };
613 let mut ok = true;
614 if mpc {
615 ok = ok && dir.join("libmpc.a").is_file();
616 ok = ok && dir.join("mpc.h").is_file();
617 }
618 if mpfr {
619 ok = ok && dir.join("libmpfr.a").is_file();
620 ok = ok && dir.join("mpfr.h").is_file();
621 }
622 ok = ok && dir.join("libgmp.a").is_file();
623 ok = ok && dir.join("gmp.h").is_file();
624 if ok {
625 return false;
626 }
627 }
628 }
629 true
630 }
631
build_gmp(env: &Environment, lib: &Path, header: &Path)632 fn build_gmp(env: &Environment, lib: &Path, header: &Path) {
633 let build_dir = env.build_dir.join("gmp-build");
634 create_dir_or_panic(&build_dir);
635 println!("$ cd {:?}", build_dir);
636 let mut conf = String::from("../gmp-src/configure --enable-fat --disable-shared --with-pic");
637 if let Some(cross_target) = env.cross_target.as_ref() {
638 conf.push_str(" --host ");
639 conf.push_str(cross_target);
640 }
641 configure(&build_dir, &OsString::from(conf));
642 make_and_check(env, &build_dir);
643 let build_lib = build_dir.join(".libs").join("libgmp.a");
644 copy_file_or_panic(&build_lib, lib);
645 let build_header = build_dir.join("gmp.h");
646 copy_file_or_panic(&build_header, header);
647 }
648
compatible_version( env: &Environment, major: i32, minor: i32, patchlevel: i32, expected: (i32, i32, i32), ) -> bool649 fn compatible_version(
650 env: &Environment,
651 major: i32,
652 minor: i32,
653 patchlevel: i32,
654 expected: (i32, i32, i32),
655 ) -> bool {
656 (major == expected.0 && minor >= expected.1)
657 && (minor > expected.1 || patchlevel >= expected.2 || env.use_system_libs)
658 }
659
process_gmp_header( env: &Environment, header: &Path, out_file: Option<&Path>, ) -> Result<(), String>660 fn process_gmp_header(
661 env: &Environment,
662 header: &Path,
663 out_file: Option<&Path>,
664 ) -> Result<(), String> {
665 let mut major = None;
666 let mut minor = None;
667 let mut patchlevel = None;
668 let mut limb_bits = None;
669 let mut nail_bits = None;
670 let mut long_long_limb = None;
671 let mut cc = None;
672 let mut cflags = None;
673 let mut reader = open(header);
674 let mut buf = String::new();
675 while read_line(&mut reader, &mut buf, header) > 0 {
676 let s = "#define __GNU_MP_VERSION ";
677 if let Some(start) = buf.find(s) {
678 major = buf[(start + s.len())..].trim().parse::<i32>().ok();
679 }
680 let s = "#define __GNU_MP_VERSION_MINOR ";
681 if let Some(start) = buf.find(s) {
682 minor = buf[(start + s.len())..].trim().parse::<i32>().ok();
683 }
684 let s = "#define __GNU_MP_VERSION_PATCHLEVEL ";
685 if let Some(start) = buf.find(s) {
686 patchlevel = buf[(start + s.len())..].trim().parse::<i32>().ok();
687 }
688 if buf.contains("#undef _LONG_LONG_LIMB") {
689 long_long_limb = Some(false);
690 }
691 if buf.contains("#define _LONG_LONG_LIMB 1") {
692 long_long_limb = Some(true);
693 }
694 let s = "#define GMP_LIMB_BITS ";
695 if let Some(start) = buf.find(s) {
696 limb_bits = buf[(start + s.len())..].trim().parse::<i32>().ok();
697 }
698 let s = "#define GMP_NAIL_BITS ";
699 if let Some(start) = buf.find(s) {
700 nail_bits = buf[(start + s.len())..].trim().parse::<i32>().ok();
701 }
702 let s = "#define __GMP_CC ";
703 if let Some(start) = buf.find(s) {
704 cc = Some(
705 buf[(start + s.len())..]
706 .trim()
707 .trim_matches('"')
708 .to_string(),
709 );
710 }
711 let s = "#define __GMP_CFLAGS ";
712 if let Some(start) = buf.find(s) {
713 cflags = Some(
714 buf[(start + s.len())..]
715 .trim()
716 .trim_matches('"')
717 .to_string(),
718 );
719 }
720 buf.clear();
721 }
722 drop(reader);
723
724 let major = major.expect("Cannot determine __GNU_MP_VERSION");
725 let minor = minor.expect("Cannot determine __GNU_MP_VERSION_MINOR");
726 let patchlevel = patchlevel.expect("Cannot determine __GNU_MP_VERSION_PATCHLEVEL");
727 if !compatible_version(env, major, minor, patchlevel, GMP_VER) {
728 return Err(format!(
729 "This version of gmp-mpfr-sys supports GMP {}.{}.{}, but {}.{}.{} was found",
730 GMP_VER.0, GMP_VER.1, GMP_VER.2, major, minor, patchlevel
731 ));
732 }
733
734 let limb_bits = limb_bits.expect("Cannot determine GMP_LIMB_BITS");
735 println!("cargo:limb_bits={}", limb_bits);
736
737 let nail_bits = nail_bits.expect("Cannot determine GMP_NAIL_BITS");
738 if nail_bits > 0 {
739 println!("cargo:rustc-cfg=nails");
740 }
741
742 let long_long_limb = long_long_limb.expect("Cannot determine _LONG_LONG_LIMB");
743 let long_long_limb = if long_long_limb {
744 println!("cargo:rustc-cfg=long_long_limb");
745 "libc::c_ulonglong"
746 } else {
747 "c_ulong"
748 };
749 let cc = cc.expect("Cannot determine __GMP_CC");
750 let cflags = cflags.expect("Cannot determine __GMP_CFLAGS");
751
752 let content = format!(
753 concat!(
754 "const GMP_VERSION: c_int = {};\n",
755 "const GMP_VERSION_MINOR: c_int = {};\n",
756 "const GMP_VERSION_PATCHLEVEL: c_int = {};\n",
757 "const GMP_LIMB_BITS: c_int = {};\n",
758 "const GMP_NAIL_BITS: c_int = {};\n",
759 "type GMP_LIMB_T = {};\n",
760 "const GMP_CC: *const c_char = b\"{}\\0\".as_ptr() as _;\n",
761 "const GMP_CFLAGS: *const c_char = b\"{}\\0\".as_ptr() as _;\n"
762 ),
763 major, minor, patchlevel, limb_bits, nail_bits, long_long_limb, cc, cflags
764 );
765 if let Some(out_file) = out_file {
766 let mut rs = create(out_file);
767 write_flush(&mut rs, &content, out_file);
768 }
769 Ok(())
770 }
771
process_mpfr_header( env: &Environment, header: &Path, out_file: Option<&Path>, ) -> Result<(), String>772 fn process_mpfr_header(
773 env: &Environment,
774 header: &Path,
775 out_file: Option<&Path>,
776 ) -> Result<(), String> {
777 let mut major = None;
778 let mut minor = None;
779 let mut patchlevel = None;
780 let mut version = None;
781 let mut reader = open(header);
782 let mut buf = String::new();
783 while read_line(&mut reader, &mut buf, header) > 0 {
784 let s = "#define MPFR_VERSION_MAJOR ";
785 if let Some(start) = buf.find(s) {
786 major = buf[(start + s.len())..].trim().parse::<i32>().ok();
787 }
788 let s = "#define MPFR_VERSION_MINOR ";
789 if let Some(start) = buf.find(s) {
790 minor = buf[(start + s.len())..].trim().parse::<i32>().ok();
791 }
792 let s = "#define MPFR_VERSION_PATCHLEVEL ";
793 if let Some(start) = buf.find(s) {
794 patchlevel = buf[(start + s.len())..].trim().parse::<i32>().ok();
795 }
796 let s = "#define MPFR_VERSION_STRING ";
797 if let Some(start) = buf.find(s) {
798 version = Some(
799 buf[(start + s.len())..]
800 .trim()
801 .trim_matches('"')
802 .to_string(),
803 );
804 }
805 buf.clear();
806 }
807 drop(reader);
808
809 let major = major.expect("Cannot determine MPFR_VERSION_MAJOR");
810 let minor = minor.expect("Cannot determine MPFR_VERSION_MINOR");
811 let patchlevel = patchlevel.expect("Cannot determine MPFR_VERSION_PATCHLEVEL");
812 if !compatible_version(env, major, minor, patchlevel, MPFR_VER) {
813 return Err(format!(
814 "This version of gmp-mpfr-sys supports MPFR {}.{}.{}, but {}.{}.{} was found",
815 MPFR_VER.0, MPFR_VER.1, MPFR_VER.2, major, minor, patchlevel
816 ));
817 }
818
819 let version = version.expect("Cannot determine MPFR_VERSION_STRING");
820
821 let content = format!(
822 concat!(
823 "const MPFR_VERSION_MAJOR: c_int = {};\n",
824 "const MPFR_VERSION_MINOR: c_int = {};\n",
825 "const MPFR_VERSION_PATCHLEVEL: c_int = {};\n",
826 "const MPFR_VERSION_STRING: *const c_char = b\"{}\\0\".as_ptr() as _;\n"
827 ),
828 major, minor, patchlevel, version
829 );
830 if let Some(out_file) = out_file {
831 let mut rs = create(out_file);
832 write_flush(&mut rs, &content, out_file);
833 }
834 Ok(())
835 }
836
process_mpc_header( env: &Environment, header: &Path, out_file: Option<&Path>, ) -> Result<(), String>837 fn process_mpc_header(
838 env: &Environment,
839 header: &Path,
840 out_file: Option<&Path>,
841 ) -> Result<(), String> {
842 let mut major = None;
843 let mut minor = None;
844 let mut patchlevel = None;
845 let mut version = None;
846 let mut reader = open(header);
847 let mut buf = String::new();
848 while read_line(&mut reader, &mut buf, header) > 0 {
849 let s = "#define MPC_VERSION_MAJOR ";
850 if let Some(start) = buf.find(s) {
851 major = buf[(start + s.len())..].trim().parse::<i32>().ok();
852 }
853 let s = "#define MPC_VERSION_MINOR ";
854 if let Some(start) = buf.find(s) {
855 minor = buf[(start + s.len())..].trim().parse::<i32>().ok();
856 }
857 let s = "#define MPC_VERSION_PATCHLEVEL ";
858 if let Some(start) = buf.find(s) {
859 patchlevel = buf[(start + s.len())..].trim().parse::<i32>().ok();
860 }
861 let s = "#define MPC_VERSION_STRING ";
862 if let Some(start) = buf.find(s) {
863 version = Some(
864 buf[(start + s.len())..]
865 .trim()
866 .trim_matches('"')
867 .to_string(),
868 );
869 }
870 buf.clear();
871 }
872 drop(reader);
873
874 let major = major.expect("Cannot determine MPC_VERSION_MAJOR");
875 let minor = minor.expect("Cannot determine MPC_VERSION_MINOR");
876 let patchlevel = patchlevel.expect("Cannot determine MPC_VERSION_PATCHLEVEL");
877 if !compatible_version(env, major, minor, patchlevel, MPC_VER) {
878 return Err(format!(
879 "This version of gmp-mpfr-sys supports MPC {}.{}.{}, but {}.{}.{} was found",
880 MPC_VER.0, MPC_VER.1, MPC_VER.2, major, minor, patchlevel
881 ));
882 }
883
884 let version = version.expect("Cannot determine MPC_VERSION_STRING");
885
886 let content = format!(
887 concat!(
888 "const MPC_VERSION_MAJOR: c_int = {};\n",
889 "const MPC_VERSION_MINOR: c_int = {};\n",
890 "const MPC_VERSION_PATCHLEVEL: c_int = {};\n",
891 "const MPC_VERSION_STRING: *const c_char = b\"{}\\0\".as_ptr() as _;\n"
892 ),
893 major, minor, patchlevel, version
894 );
895 if let Some(out_file) = out_file {
896 let mut rs = create(out_file);
897 write_flush(&mut rs, &content, out_file);
898 }
899 Ok(())
900 }
901
build_mpfr(env: &Environment, lib: &Path, header: &Path)902 fn build_mpfr(env: &Environment, lib: &Path, header: &Path) {
903 let build_dir = env.build_dir.join("mpfr-build");
904 create_dir_or_panic(&build_dir);
905 println!("$ cd {:?}", build_dir);
906 link_dir(
907 &env.build_dir.join("gmp-build"),
908 &build_dir.join("gmp-build"),
909 );
910 let mut conf = String::from(
911 "../mpfr-src/configure --enable-thread-safe --disable-shared \
912 --with-gmp-build=../gmp-build --with-pic",
913 );
914 if let Some(cross_target) = env.cross_target.as_ref() {
915 conf.push_str(" --host ");
916 conf.push_str(cross_target);
917 }
918 configure(&build_dir, &OsString::from(conf));
919 make_and_check(env, &build_dir);
920 let build_lib = build_dir.join("src").join(".libs").join("libmpfr.a");
921 copy_file_or_panic(&build_lib, lib);
922 let src_header = env.build_dir.join("mpfr-src").join("src").join("mpfr.h");
923 copy_file_or_panic(&src_header, header);
924 }
925
build_mpc(env: &Environment, lib: &Path, header: &Path)926 fn build_mpc(env: &Environment, lib: &Path, header: &Path) {
927 let build_dir = env.build_dir.join("mpc-build");
928 create_dir_or_panic(&build_dir);
929 println!("$ cd {:?}", build_dir);
930 // steal link from mpfr-build to save some copying under MinGW,
931 // where a symlink is a just a copy (unless in developer mode).
932 mv("../mpfr-build/gmp-build", &build_dir);
933 link_dir(&env.build_dir.join("mpfr-src"), &build_dir.join("mpfr-src"));
934 link_dir(
935 &env.build_dir.join("mpfr-build"),
936 &build_dir.join("mpfr-build"),
937 );
938 let mut conf = String::from(
939 "../mpc-src/configure --disable-shared \
940 --with-mpfr-include=../mpfr-src/src \
941 --with-mpfr-lib=../mpfr-build/src/.libs \
942 --with-gmp-include=../gmp-build \
943 --with-gmp-lib=../gmp-build/.libs --with-pic",
944 );
945 if let Some(cross_target) = env.cross_target.as_ref() {
946 conf.push_str(" --host ");
947 conf.push_str(cross_target);
948 }
949 configure(&build_dir, &OsString::from(conf));
950 make_and_check(env, &build_dir);
951 let build_lib = build_dir.join("src").join(".libs").join("libmpc.a");
952 copy_file_or_panic(&build_lib, lib);
953 let src_header = env.build_dir.join("mpc-src").join("src").join("mpc.h");
954 copy_file_or_panic(&src_header, header);
955 }
956
write_link_info(env: &Environment, feature_mpfr: bool, feature_mpc: bool)957 fn write_link_info(env: &Environment, feature_mpfr: bool, feature_mpc: bool) {
958 let out_str = env.out_dir.to_str().unwrap_or_else(|| {
959 panic!(
960 "Path contains unsupported characters, can only make {}",
961 env.out_dir.display()
962 )
963 });
964 let lib_str = env.lib_dir.to_str().unwrap_or_else(|| {
965 panic!(
966 "Path contains unsupported characters, can only make {}",
967 env.lib_dir.display()
968 )
969 });
970 let include_str = env.include_dir.to_str().unwrap_or_else(|| {
971 panic!(
972 "Path contains unsupported characters, can only make {}",
973 env.include_dir.display()
974 )
975 });
976 println!("cargo:out_dir={}", out_str);
977 println!("cargo:lib_dir={}", lib_str);
978 println!("cargo:include_dir={}", include_str);
979 println!("cargo:rustc-link-search=native={}/lib", "/usr/local");
980 let maybe_static = if env.use_system_libs { "" } else { "static=" };
981 if feature_mpc {
982 println!("cargo:rustc-link-lib={}mpc", maybe_static);
983 }
984 if feature_mpfr {
985 println!("cargo:rustc-link-lib={}mpfr", maybe_static);
986 }
987 println!("cargo:rustc-link-lib={}gmp", maybe_static);
988 if env.target == Target::Mingw && env.workaround_47048 == Workaround47048::Yes {
989 println!("cargo:rustc-link-lib=static=workaround_47048");
990 }
991 }
992
993 impl Environment {
994 #[allow(dead_code)]
check_feature(&self, name: &str, contents: &str, nightly_features: Option<&str>)995 fn check_feature(&self, name: &str, contents: &str, nightly_features: Option<&str>) {
996 let try_dir = self.out_dir.join(format!("try_{}", name));
997 let filename = format!("try_{}.rs", name);
998 create_dir_or_panic(&try_dir);
999 println!("$ cd {:?}", try_dir);
1000
1001 enum Iteration {
1002 Stable,
1003 Unstable,
1004 }
1005 for i in &[Iteration::Stable, Iteration::Unstable] {
1006 let s;
1007 let file_contents = match *i {
1008 Iteration::Stable => contents,
1009 Iteration::Unstable => match nightly_features {
1010 Some(features) => {
1011 s = format!("#![feature({})]\n{}", features, contents);
1012 &s
1013 }
1014 None => continue,
1015 },
1016 };
1017 create_file_or_panic(&try_dir.join(&filename), file_contents);
1018 let mut cmd = Command::new(&self.rustc);
1019 cmd.current_dir(&try_dir)
1020 .stdout(Stdio::null())
1021 .stderr(Stdio::null())
1022 .args(&[&*filename, "--emit=dep-info,metadata"]);
1023 println!("$ {:?} >& /dev/null", cmd);
1024 let status = cmd
1025 .status()
1026 .unwrap_or_else(|_| panic!("Unable to execute: {:?}", cmd));
1027 if status.success() {
1028 println!("cargo:rustc-cfg={}", name);
1029 if let Iteration::Unstable = *i {
1030 println!("cargo:rustc-cfg=nightly_{}", name);
1031 }
1032 break;
1033 }
1034 }
1035
1036 remove_dir_or_panic(&try_dir);
1037 }
1038 }
1039
cargo_env(name: &str) -> OsString1040 fn cargo_env(name: &str) -> OsString {
1041 env::var_os(name)
1042 .unwrap_or_else(|| panic!("environment variable not found: {}, please use cargo", name))
1043 }
1044
there_is_env(name: &str) -> bool1045 fn there_is_env(name: &str) -> bool {
1046 env::var_os(name).is_some()
1047 }
1048
check_for_msvc(env: &Environment)1049 fn check_for_msvc(env: &Environment) {
1050 if env.target == Target::Msvc {
1051 panic!("Windows MSVC target is not supported (linking would fail)");
1052 }
1053 }
1054
rustc_later_eq(major: i32, minor: i32) -> bool1055 fn rustc_later_eq(major: i32, minor: i32) -> bool {
1056 let rustc = cargo_env("RUSTC");
1057 let output = Command::new(rustc)
1058 .arg("--version")
1059 .output()
1060 .expect("unable to run rustc --version");
1061 let version = String::from_utf8(output.stdout).expect("unrecognized rustc version");
1062 if !version.starts_with("rustc ") {
1063 panic!("unrecognized rustc version: {}", version);
1064 }
1065 let remain = &version[6..];
1066 let dot = remain.find('.').expect("unrecognized rustc version");
1067 let ver_major = remain[0..dot]
1068 .parse::<i32>()
1069 .expect("unrecognized rustc version");
1070 match ver_major.cmp(&major) {
1071 Ordering::Less => false,
1072 Ordering::Greater => true,
1073 Ordering::Equal => {
1074 let remain = &remain[dot + 1..];
1075 let dot = remain.find('.').expect("unrecognized rustc version");
1076 let ver_minor = remain[0..dot]
1077 .parse::<i32>()
1078 .expect("unrecognized rustc version");
1079 ver_minor >= minor
1080 }
1081 }
1082 }
1083
check_for_bug_47048(env: &Environment) -> Workaround470481084 fn check_for_bug_47048(env: &Environment) -> Workaround47048 {
1085 if env.target != Target::Mingw || rustc_later_eq(1, 43) {
1086 return Workaround47048::No;
1087 }
1088 let try_dir = env.build_dir.join("try_47048");
1089 remove_dir_or_panic(&try_dir);
1090 create_dir_or_panic(&try_dir);
1091 println!("$ cd {:?}", try_dir);
1092 println!("$ #Check for bug 47048");
1093 create_file_or_panic(&try_dir.join("say_hi.c"), BUG_47048_SAY_HI_C);
1094 create_file_or_panic(&try_dir.join("c_main.c"), BUG_47048_C_MAIN_C);
1095 create_file_or_panic(&try_dir.join("r_main.rs"), BUG_47048_R_MAIN_RS);
1096 create_file_or_panic(&try_dir.join("workaround.c"), BUG_47048_WORKAROUND_C);
1097 let mut cmd;
1098
1099 cmd = Command::new(&env.c_compiler);
1100 cmd.current_dir(&try_dir).args(&["-fPIC", "-c", "say_hi.c"]);
1101 execute(cmd);
1102
1103 cmd = Command::new("ar");
1104 cmd.current_dir(&try_dir)
1105 .args(&["cr", "libsay_hi.a", "say_hi.o"]);
1106 execute(cmd);
1107
1108 cmd = Command::new(&env.c_compiler);
1109 cmd.current_dir(&try_dir)
1110 .args(&["c_main.c", "-L.", "-lsay_hi", "-o", "c_main.exe"]);
1111 execute(cmd);
1112
1113 // try simple rustc command that should work, so that failure
1114 // really is the bug being checked for
1115 cmd = Command::new(&env.rustc);
1116 cmd.arg("--version");
1117 execute(cmd);
1118
1119 cmd = Command::new(&env.rustc);
1120 cmd.current_dir(&try_dir)
1121 .args(&["r_main.rs", "-L.", "-lsay_hi", "-o", "r_main.exe"])
1122 .stdout(Stdio::null())
1123 .stderr(Stdio::null());
1124 println!(
1125 "$ {:?} >& /dev/null && echo Bug 47048 not found || echo Working around bug 47048",
1126 cmd
1127 );
1128 let status = cmd
1129 .status()
1130 .unwrap_or_else(|_| panic!("Unable to execute: {:?}", cmd));
1131 let need_workaround = if status.success() {
1132 println!("Bug 47048 not found");
1133 Workaround47048::No
1134 } else {
1135 println!("Working around bug 47048");
1136
1137 cmd = Command::new(&env.c_compiler);
1138 cmd.current_dir(&try_dir)
1139 .args(&["-fPIC", "-O2", "-c", "workaround.c"]);
1140 execute(cmd);
1141
1142 cmd = Command::new("ar");
1143 cmd.current_dir(&try_dir)
1144 .args(&["cr", "libworkaround_47048.a", "workaround.o"]);
1145 execute(cmd);
1146
1147 cmd = Command::new(&env.rustc);
1148 cmd.current_dir(&try_dir).args(&[
1149 "r_main.rs",
1150 "-L.",
1151 "-lsay_hi",
1152 "-lworkaround_47048",
1153 "-o",
1154 "r_main.exe",
1155 ]);
1156 execute(cmd);
1157
1158 let src = try_dir.join("libworkaround_47048.a");
1159 let dst = env.lib_dir.join("libworkaround_47048.a");
1160 copy_file_or_panic(&src, &dst);
1161
1162 Workaround47048::Yes
1163 };
1164 remove_dir_or_panic(&try_dir);
1165 need_workaround
1166 }
1167
mingw_pkg_config_libdir_or_panic()1168 fn mingw_pkg_config_libdir_or_panic() {
1169 let mut cmd = Command::new("pkg-config");
1170 cmd.args(&["--libs-only-L", "gmp"]);
1171 let output = execute_stdout(cmd);
1172 if output.len() < 2 || &output[0..2] != b"-L" {
1173 panic!("expected pkg-config output to begin with \"-L\"");
1174 }
1175 let libdir = str::from_utf8(&output[2..]).expect("output from pkg-config not utf-8");
1176 println!("cargo:rustc-link-search=native={}", libdir);
1177 }
1178
remove_dir(dir: &Path) -> IoResult<()>1179 fn remove_dir(dir: &Path) -> IoResult<()> {
1180 if !dir.exists() {
1181 return Ok(());
1182 }
1183 assert!(dir.is_dir(), "Not a directory: {:?}", dir);
1184 println!("$ rm -r {:?}", dir);
1185 fs::remove_dir_all(dir)
1186 }
1187
remove_dir_or_panic(dir: &Path)1188 fn remove_dir_or_panic(dir: &Path) {
1189 remove_dir(dir).unwrap_or_else(|_| panic!("Unable to remove directory: {:?}", dir));
1190 }
1191
create_dir(dir: &Path) -> IoResult<()>1192 fn create_dir(dir: &Path) -> IoResult<()> {
1193 println!("$ mkdir -p {:?}", dir);
1194 fs::create_dir_all(dir)
1195 }
1196
create_dir_or_panic(dir: &Path)1197 fn create_dir_or_panic(dir: &Path) {
1198 create_dir(dir).unwrap_or_else(|_| panic!("Unable to create directory: {:?}", dir));
1199 }
1200
create_file_or_panic(filename: &Path, contents: &str)1201 fn create_file_or_panic(filename: &Path, contents: &str) {
1202 println!("$ printf '%s' {:?}... > {:?}", &contents[0..10], filename);
1203 let mut file =
1204 File::create(filename).unwrap_or_else(|_| panic!("Unable to create file: {:?}", filename));
1205 file.write_all(contents.as_bytes())
1206 .unwrap_or_else(|_| panic!("Unable to write to file: {:?}", filename));
1207 }
1208
copy_file(src: &Path, dst: &Path) -> IoResult<u64>1209 fn copy_file(src: &Path, dst: &Path) -> IoResult<u64> {
1210 println!("$ cp {:?} {:?}", src, dst);
1211 fs::copy(src, dst)
1212 }
1213
copy_file_or_panic(src: &Path, dst: &Path)1214 fn copy_file_or_panic(src: &Path, dst: &Path) {
1215 copy_file(src, dst).unwrap_or_else(|_| {
1216 panic!("Unable to copy {:?} -> {:?}", src, dst);
1217 });
1218 }
1219
configure(build_dir: &Path, conf_line: &OsStr)1220 fn configure(build_dir: &Path, conf_line: &OsStr) {
1221 let mut conf = Command::new("sh");
1222 conf.current_dir(&build_dir).arg("-c").arg(conf_line);
1223 execute(conf);
1224 }
1225
make_and_check(env: &Environment, build_dir: &Path)1226 fn make_and_check(env: &Environment, build_dir: &Path) {
1227 let mut make = Command::new("make");
1228 make.current_dir(build_dir).arg("-j").arg(&env.jobs);
1229 execute(make);
1230 if !env.c_no_tests && env.cross_target.is_none() {
1231 let mut make_check = Command::new("make");
1232 make_check
1233 .current_dir(build_dir)
1234 .arg("-j")
1235 .arg(&env.jobs)
1236 .arg("check");
1237 execute(make_check);
1238 }
1239 }
1240
1241 #[cfg(unix)]
link_dir(src: &Path, dst: &Path)1242 fn link_dir(src: &Path, dst: &Path) {
1243 println!("$ ln -s {:?} {:?}", src, dst);
1244 unix_fs::symlink(src, dst).unwrap_or_else(|_| {
1245 panic!("Unable to symlink {:?} -> {:?}", src, dst);
1246 });
1247 }
1248
1249 #[cfg(windows)]
link_dir(src: &Path, dst: &Path)1250 fn link_dir(src: &Path, dst: &Path) {
1251 println!("$ ln -s {:?} {:?}", src, dst);
1252 if windows_fs::symlink_dir(src, dst).is_ok() {
1253 return;
1254 }
1255 println!("symlink_dir: failed to create symbolic link, copying instead");
1256 let mut c = Command::new("cp");
1257 c.arg("-R").arg(src).arg(dst);
1258 execute(c);
1259 }
1260
mv(src: &str, dst_dir: &Path)1261 fn mv(src: &str, dst_dir: &Path) {
1262 let mut c = Command::new("mv");
1263 c.arg(src).arg(".").current_dir(dst_dir);
1264 execute(c);
1265 }
1266
execute(mut command: Command)1267 fn execute(mut command: Command) {
1268 println!("$ {:?}", command);
1269 let status = command
1270 .status()
1271 .unwrap_or_else(|_| panic!("Unable to execute: {:?}", command));
1272 if !status.success() {
1273 if let Some(code) = status.code() {
1274 panic!("Program failed with code {}: {:?}", code, command);
1275 } else {
1276 panic!("Program failed: {:?}", command);
1277 }
1278 }
1279 }
1280
execute_stdout(mut command: Command) -> Vec<u8>1281 fn execute_stdout(mut command: Command) -> Vec<u8> {
1282 println!("$ {:?}", command);
1283 let output = command
1284 .output()
1285 .unwrap_or_else(|_| panic!("Unable to execute: {:?}", command));
1286 if !output.status.success() {
1287 if let Some(code) = output.status.code() {
1288 panic!("Program failed with code {}: {:?}", code, command);
1289 } else {
1290 panic!("Program failed: {:?}", command);
1291 }
1292 }
1293 output.stdout
1294 }
1295
open(name: &Path) -> BufReader<File>1296 fn open(name: &Path) -> BufReader<File> {
1297 let file = File::open(name).unwrap_or_else(|_| panic!("Cannot open file: {:?}", name));
1298 BufReader::new(file)
1299 }
1300
create(name: &Path) -> BufWriter<File>1301 fn create(name: &Path) -> BufWriter<File> {
1302 let file = File::create(name).unwrap_or_else(|_| panic!("Cannot create file: {:?}", name));
1303 BufWriter::new(file)
1304 }
1305
read_line(reader: &mut BufReader<File>, buf: &mut String, name: &Path) -> usize1306 fn read_line(reader: &mut BufReader<File>, buf: &mut String, name: &Path) -> usize {
1307 reader
1308 .read_line(buf)
1309 .unwrap_or_else(|_| panic!("Cannot read from: {:?}", name))
1310 }
1311
write_flush(writer: &mut BufWriter<File>, buf: &str, name: &Path)1312 fn write_flush(writer: &mut BufWriter<File>, buf: &str, name: &Path) {
1313 writer
1314 .write_all(buf.as_bytes())
1315 .unwrap_or_else(|_| panic!("Cannot write to: {:?}", name));
1316 writer
1317 .flush()
1318 .unwrap_or_else(|_| panic!("Cannot write to: {:?}", name));
1319 }
1320
system_cache_dir() -> Option<PathBuf>1321 fn system_cache_dir() -> Option<PathBuf> {
1322 #[cfg(target_os = "windows")]
1323 {
1324 use core::{mem::MaybeUninit, ptr, slice};
1325 use std::os::windows::ffi::OsStringExt;
1326 use winapi::{
1327 shared::winerror::S_OK,
1328 um::{combaseapi, knownfolders::FOLDERID_LocalAppData, shlobj, winbase},
1329 };
1330 let id = &FOLDERID_LocalAppData;
1331 let flags = 0;
1332 let access = ptr::null_mut();
1333 let mut path = MaybeUninit::uninit();
1334 unsafe {
1335 if shlobj::SHGetKnownFolderPath(id, flags, access, path.as_mut_ptr()) == S_OK {
1336 let path = path.assume_init();
1337 let slice = slice::from_raw_parts(path, winbase::lstrlenW(path) as usize);
1338 let string = OsString::from_wide(slice);
1339 combaseapi::CoTaskMemFree(path as _);
1340 Some(string.into())
1341 } else {
1342 None
1343 }
1344 }
1345 }
1346 #[cfg(any(target_os = "macos", target_os = "ios"))]
1347 {
1348 env::var_os("HOME")
1349 .filter(|x| !x.is_empty())
1350 .map(|x| AsRef::<Path>::as_ref(&x).join("Library").join("Caches"))
1351 }
1352 #[cfg(not(any(target_os = "windows", target_os = "macos", target_os = "ios")))]
1353 {
1354 env::var_os("XDG_CACHE_HOME")
1355 .filter(|x| !x.is_empty())
1356 .map(PathBuf::from)
1357 .or_else(|| {
1358 env::var_os("HOME")
1359 .filter(|x| !x.is_empty())
1360 .map(|x| AsRef::<Path>::as_ref(&x).join(".cache"))
1361 })
1362 }
1363 }
1364
compilation_target_allowed(host: &str, target: &str) -> bool1365 fn compilation_target_allowed(host: &str, target: &str) -> bool {
1366 if host == target {
1367 return true;
1368 }
1369
1370 // Allow cross-compilation from x86_64 to i686, as it is a simple
1371 // -m32 switch in GMP compilation; unless MinGW is in use, where
1372 // cross compilation from 64-bit to 32-bit has issues.
1373 let (machine_x86_64, machine_i686) = ("x86_64", "i686");
1374 if host.starts_with(machine_x86_64)
1375 && target.starts_with(machine_i686)
1376 && host[machine_x86_64.len()..] == target[machine_i686.len()..]
1377 && !target.contains("-windows-gnu")
1378 {
1379 return true;
1380 }
1381
1382 false
1383 }
1384
1385 const BUG_47048_SAY_HI_C: &str = r#"/* say_hi.c */
1386 #include <stdio.h>
1387 void say_hi(void) {
1388 fprintf(stdout, "hi!\n");
1389 }
1390 "#;
1391
1392 const BUG_47048_C_MAIN_C: &str = r#"/* c_main.c */
1393 void say_hi(void);
1394 int main(void) {
1395 say_hi();
1396 return 0;
1397 }
1398 "#;
1399
1400 const BUG_47048_R_MAIN_RS: &str = r#"// r_main.rs
1401 extern "C" {
1402 fn say_hi();
1403 }
1404 fn main() {
1405 unsafe {
1406 say_hi();
1407 }
1408 }
1409 "#;
1410
1411 const BUG_47048_WORKAROUND_C: &str = r#"/* workaround.c */
1412 #define _CRTBLD
1413 #include <stdio.h>
1414
1415 FILE *__cdecl __acrt_iob_func(unsigned index)
1416 {
1417 return &(__iob_func()[index]);
1418 }
1419
1420 typedef FILE *__cdecl (*_f__acrt_iob_func)(unsigned index);
1421 _f__acrt_iob_func __MINGW_IMP_SYMBOL(__acrt_iob_func) = __acrt_iob_func;
1422 "#;
1423
1424 // prints part of the header
1425 const SYSTEM_GMP_C: &str = r##"/* system_gmp.c */
1426 #include <gmp.h>
1427 #include <stdio.h>
1428
1429 #define STRINGIFY(x) #x
1430 #define DEFINE_STR(x) ("#define " #x " " STRINGIFY(x) "\n")
1431
1432 int main(void) {
1433 FILE *f = fopen("system_gmp.out", "w");
1434
1435 #ifdef _LONG_LONG_LIMB
1436 fputs(DEFINE_STR(_LONG_LONG_LIMB), f);
1437 #else
1438 fputs("#undef _LONG_LONG_LIMB\n", f);
1439 #endif
1440
1441 fputs(DEFINE_STR(__GNU_MP_VERSION), f);
1442 fputs(DEFINE_STR(__GNU_MP_VERSION_MINOR), f);
1443 fputs(DEFINE_STR(__GNU_MP_VERSION_PATCHLEVEL), f);
1444 fputs(DEFINE_STR(GMP_LIMB_BITS), f);
1445 fputs(DEFINE_STR(GMP_NAIL_BITS), f);
1446 fputs(DEFINE_STR(__GMP_CC), f);
1447 fputs(DEFINE_STR(__GMP_CFLAGS), f);
1448
1449 fclose(f);
1450
1451 return 0;
1452 }
1453 "##;
1454
1455 // prints part of the header
1456 const SYSTEM_MPFR_C: &str = r##"/* system_mpfr.c */
1457 #include <mpfr.h>
1458 #include <stdio.h>
1459
1460 #define STRINGIFY(x) #x
1461 #define DEFINE_STR(x) ("#define " #x " " STRINGIFY(x) "\n")
1462
1463 int main(void) {
1464 FILE *f = fopen("system_mpfr.out", "w");
1465
1466 fputs(DEFINE_STR(MPFR_VERSION_MAJOR), f);
1467 fputs(DEFINE_STR(MPFR_VERSION_MINOR), f);
1468 fputs(DEFINE_STR(MPFR_VERSION_PATCHLEVEL), f);
1469 fputs(DEFINE_STR(MPFR_VERSION_STRING), f);
1470
1471 fclose(f);
1472
1473 return 0;
1474 }
1475 "##;
1476
1477 // prints part of the header
1478 const SYSTEM_MPC_C: &str = r##"/* system_mpc.c */
1479 #include <mpc.h>
1480 #include <stdio.h>
1481
1482 #define STRINGIFY(x) #x
1483 #define DEFINE_STR(x) ("#define " #x " " STRINGIFY(x) "\n")
1484
1485 int main(void) {
1486 FILE *f = fopen("system_mpc.out", "w");
1487
1488 fputs(DEFINE_STR(MPC_VERSION_MAJOR), f);
1489 fputs(DEFINE_STR(MPC_VERSION_MINOR), f);
1490 fputs(DEFINE_STR(MPC_VERSION_PATCHLEVEL), f);
1491 fputs(DEFINE_STR(MPC_VERSION_STRING), f);
1492
1493 fclose(f);
1494
1495 return 0;
1496 }
1497 "##;
1498
1499 const TRY_EXTENDED_KEY_VALUE_ATTRIBUTES: &str = r#"// try_extended_key_value_attributes.rs
1500 #[doc = include_str!("try_extended_key_value_attributes.rs")]
1501 pub struct S;
1502 fn main() {}
1503 "#;
1504
1505 const TRY_UNSAFE_IN_UNSAFE: &str = r#"// try_unsafe_in_unsafe.rs
1506 #![deny(unknown_lints)]
1507 #![warn(unsafe_op_in_unsafe_fn)]
1508 fn main() {}
1509 "#;
1510