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