cargo_sysroot_2/
util.rs

1//! Utility.
2use anyhow::{Context, Result};
3use fs_extra::dir::{copy, CopyOptions};
4use std::{
5    fs,
6    path::{Path, PathBuf},
7    process::Command,
8};
9
10/// Get the configured rustc sysroot.
11/// This is the HOST sysroot.
12fn 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
25/// Get the configured rustc sysroot lib dir for `target`.
26fn 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
41/// Get the `rust-src` component of the current toolchain.
42///
43/// Errors if current toolchain isn't a nightly.
44///
45/// See <https://rust-lang.github.io/rustup/faq.html#can-rustup-download-the-rust-source-code>
46pub 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    Ok(sys)
55}
56
57/// Host tools such as rust-lld need to be in the sysroot to link correctly.
58/// Copies entire host target, so stuff like tests work.
59#[allow(clippy::blocks_in_if_conditions)]
60pub fn copy_host_tools(local_sysroot: &Path) -> Result<()> {
61    let root = get_rustc_target_libdir(None)?;
62    let host = root
63        .parent()
64        .and_then(|f| f.file_stem())
65        .and_then(|f| f.to_str())
66        .context("Error parsing host target triple")?;
67    let local_sysroot = local_sysroot.join("lib").join("rustlib").join(&host);
68    let src = root;
69
70    let src_meta = fs::metadata(&src)
71        .with_context(|| format!("Couldn't get metadata for {}", src.display()))?;
72    let to_meta = fs::metadata(&local_sysroot)
73        .with_context(|| format!("Couldn't get metadata for {}", local_sysroot.display()));
74
75    // If our host tools bin dir doesn't exist it always needs updating.
76    if let Ok(to_meta) = to_meta {
77        // If our sysroot is older than the installed component we need to update
78        // A newer rust-src should always have a newer modified time.
79        // Whereas we should always have a newer modified time if we're up to date.
80        if to_meta.modified().with_context(|| {
81            format!(
82                "Couldn't get modification time for {}",
83                local_sysroot.display()
84            )
85        })? > src_meta.modified().with_context(|| {
86            format!(
87                "Couldn't get modification time for {}",
88                local_sysroot.display()
89            )
90        })? {
91            return Ok(());
92        }
93    }
94    let mut options = CopyOptions::new();
95    options.overwrite = true;
96    let local_sysroot = local_sysroot
97        .parent()
98        .context("Error getting local sysroot parent directory")?;
99    copy(&src, &local_sysroot, &options).with_context(|| {
100        format!(
101            "Couldn't copy from `{}` to `{}`",
102            src.display(),
103            local_sysroot.display()
104        )
105    })?;
106    Ok(())
107}