use ego_tree::Tree;
use phf::phf_map;
use crate::{
parse::{self, resolve_package, Error},
platforms::SystemInfo,
Features, Module,
};
use std::{
env, fs,
path::{Path, PathBuf},
process::Command,
string::FromUtf8Error,
};
use super::compat_pkg::ManualPackage;
pub const SYSROOT_CRATES: phf::Map<&str, (&str, &[&str])> = phf_map! {
"alloc" => ("alloc", &["core"]),
"backtrace" => ("backtrace", &[]),
"core" => ("core", &[]),
"panic_abort" => ("panic_abort", &[]),
"panic_unwind" => ("panic_unwind", &[]),
"proc_macro" => ("proc_macro", &["std"]),
"profiler_builtins" => ("profiler_builtins", &[]),
"std" => ("std", &["alloc", "panic_unwind", "panic_abort", "core", "profiler_builtins", "unwind", "std_detect", "test"]),
"std_detect" => ("stdarch/crates/std_detect", &[]),
"test" => ("test", &[]),
"unwind" => ("unwind", &[]),
};
pub(crate) fn add_sysroot_crate(
tree: &mut Tree<Module>,
features: &Features,
platform: &SystemInfo,
crat: &str,
) -> parse::Result<()> {
if let Some((path, deps)) = SYSROOT_CRATES.get(crat) {
for toplevel in tree.root().children() {
if toplevel.value().ident == crat {
return Ok(());
}
}
let sysroot = get_sysroot()?;
let mut path = sysroot.join(path);
path.push("src");
path.push("lib.rs");
resolve_package(
features,
platform,
tree,
ManualPackage::new_rl(crat, &path),
&mut false,
)?;
for dep in *deps {
add_sysroot_crate(tree, features, platform, dep)?;
}
Ok(())
} else {
Err(Error::InvalidSysrootCrate(crat.to_string()))
}
}
fn get_sysroot() -> parse::Result<PathBuf> {
let sysroot_dir = discover_sysroot_dir()?;
let sysroot_src_dir = discover_sysroot_src_dir_or_add_component(&sysroot_dir)?;
Ok(sysroot_src_dir)
}
fn discover_sysroot_dir() -> parse::Result<PathBuf> {
let stdout = Command::new("rustc")
.args(["--print", "sysroot"])
.output()?
.stdout;
Ok(stdout_to_path(stdout)?)
}
fn discover_sysroot_src_dir_or_add_component(sysroot_path: &Path) -> parse::Result<PathBuf> {
discover_sysroot_src_dir(sysroot_path)
.or_else(|| {
Command::new("rustup")
.args(["component", "add", "rust-src"])
.output()
.ok();
get_rust_src(sysroot_path)
})
.ok_or(Error::Sysroot)
}
fn discover_sysroot_src_dir(sysroot_path: &Path) -> Option<PathBuf> {
if let Ok(path) = env::var("RUST_SRC_PATH") {
let path = PathBuf::from(path.as_str());
let core = path.join("core");
if fs::metadata(core).is_ok() {
return Some(path);
}
}
get_rust_src(sysroot_path)
}
fn get_rust_src(sysroot_path: &Path) -> Option<PathBuf> {
let rust_src = sysroot_path.join("lib/rustlib/src/rust/library");
if fs::metadata(&rust_src).is_ok() {
Some(rust_src)
} else {
None
}
}
fn stdout_to_path(mut stdout: Vec<u8>) -> Result<PathBuf, FromUtf8Error> {
if stdout.last() == Some(&b'\n') {
stdout.pop();
}
let path = String::from_utf8(stdout)?;
Ok(PathBuf::from(path))
}