1use anyhow::{anyhow, Context, Result};
3use fs_extra::dir::{copy, CopyOptions};
4use std::{
5 fs,
6 path::{Path, PathBuf},
7 process::Command,
8};
9
10fn get_rustc_sysroot() -> Result<PathBuf> {
13 let rustc = Command::new("rustc")
14 .arg("--print")
15 .arg("sysroot")
16 .output()?;
17 let sysroot = PathBuf::from(
18 std::str::from_utf8(&rustc.stdout)
19 .context("Failed to convert sysroot path to utf-8")?
20 .trim(),
21 );
22 Ok(sysroot)
23}
24
25fn get_rustc_target_libdir(target: Option<&Path>) -> Result<PathBuf> {
27 let mut rustc = Command::new("rustc");
28 rustc.arg("--print").arg("target-libdir");
29 if let Some(target) = target {
30 rustc.arg("--target").arg(target);
31 }
32 let rustc = rustc.output()?;
33 let sysroot = PathBuf::from(
34 std::str::from_utf8(&rustc.stdout)
35 .context("Failed to convert sysroot path to utf-8")?
36 .trim(),
37 );
38 Ok(sysroot)
39}
40
41pub fn get_rust_src() -> Result<PathBuf> {
47 let root = get_rustc_sysroot()?;
48 let sys = root
49 .join("lib")
50 .join("rustlib")
51 .join("src")
52 .join("rust")
53 .join("library");
54 if root
55 .file_stem()
56 .and_then(|f| f.to_str())
57 .and_then(|s| s.split('-').next())
58 .context("Error parsing rust-src path")?
59 != "nightly"
60 {
61 return Err(anyhow!(
62 "A nightly Rust version is required to build the sysroot"
63 ));
64 }
65 Ok(sys)
66}
67
68#[allow(clippy::blocks_in_if_conditions)]
71pub fn copy_host_tools(local_sysroot: &Path) -> Result<()> {
72 let root = get_rustc_target_libdir(None)?;
73 let host = root
74 .parent()
75 .and_then(|f| f.file_stem())
76 .and_then(|f| f.to_str())
77 .context("Error parsing host target triple")?;
78 let local_sysroot = local_sysroot.join("lib").join("rustlib").join(&host);
79 let src = root;
80
81 let src_meta = fs::metadata(&src)
82 .with_context(|| format!("Couldn't get metadata for {}", src.display()))?;
83 let to_meta = fs::metadata(&local_sysroot)
84 .with_context(|| format!("Couldn't get metadata for {}", local_sysroot.display()));
85
86 if let Ok(to_meta) = to_meta {
88 if to_meta.modified().with_context(|| {
92 format!(
93 "Couldn't get modification time for {}",
94 local_sysroot.display()
95 )
96 })? > src_meta.modified().with_context(|| {
97 format!(
98 "Couldn't get modification time for {}",
99 local_sysroot.display()
100 )
101 })? {
102 return Ok(());
103 }
104 }
105 let mut options = CopyOptions::new();
106 options.overwrite = true;
107 let local_sysroot = local_sysroot
108 .parent()
109 .context("Error getting local sysroot parent directory")?;
110 copy(&src, &local_sysroot, &options).with_context(|| {
111 format!(
112 "Couldn't copy from `{}` to `{}`",
113 src.display(),
114 local_sysroot.display()
115 )
116 })?;
117 Ok(())
118}