use std::collections::hash_map::DefaultHasher;
use std::env;
use std::hash::{Hash, Hasher};
use std::io;
use std::io::Write;
use std::path::Path;
use std::process::Command;
use rustc_version::VersionMeta;
use tempdir::TempDir;
use toml::{Table, Value};
use cargo;
use cargo::Rustflags;
use config::Config;
use errors::*;
use extensions::CommandExt;
use rustc::{Src, Sysroot, Target};
use util;
use xargo::Home;
use CompilationMode;
#[cfg(feature = "dev")]
fn profile() -> &'static str {
"debug"
}
#[cfg(not(feature = "dev"))]
fn profile() -> &'static str {
"release"
}
fn build(
cmode: &CompilationMode,
ctoml: &cargo::Toml,
home: &Home,
config: &Config,
src: &Src,
hash: u64,
verbose: bool,
) -> Result<()> {
let rustlib = home.lock_rw(cmode.triple())?;
rustlib
.remove_siblings()
.chain_err(|| format!("couldn't clear {}", rustlib.path().display()))?;
let dst = rustlib.parent().join("lib");
util::mkdir(&dst)?;
build_liballoc(cmode, &ctoml, src, &dst, config, verbose)?;
util::write(&rustlib.parent().join(".hash"), &hash.to_string())?;
Ok(())
}
fn build_crate(
crate_name: &str,
mut stoml: String,
cmode: &CompilationMode,
ctoml: &cargo::Toml,
dst: &Path,
verbose: bool,
) -> Result<()> {
let td = TempDir::new("xargo").chain_err(|| "couldn't create a temporary directory")?;
let td = td.path();
if let Some(profile) = ctoml.profile() {
stoml.push_str(&profile.to_string())
}
util::write(&td.join("Cargo.toml"), &stoml)?;
util::mkdir(&td.join("src"))?;
util::write(&td.join("src/lib.rs"), "")?;
let cargo = std::env::var("CARGO").unwrap_or("cargo".to_string());
let mut cmd = Command::new(cargo);
cmd.env_remove("CARGO_TARGET_DIR");
cmd.env_remove("RUSTFLAGS");
cmd.env("__CARGO_DEFAULT_LIB_METADATA", "XARGO");
if env::var_os("RUST_TARGET_PATH").is_none() {
if let CompilationMode::Cross(ref target) = *cmode {
if let Target::Custom { ref json, .. } = *target {
cmd.env("RUST_TARGET_PATH", json.parent().unwrap());
}
}
}
cmd.arg("rustc");
cmd.arg("-p").arg(crate_name);
match () {
#[cfg(feature = "dev")]
() => {}
#[cfg(not(feature = "dev"))]
() => {
cmd.arg("--release");
}
}
cmd.arg("--manifest-path");
cmd.arg(td.join("Cargo.toml"));
cmd.args(&["--target", cmode.orig_triple()]);
if verbose {
cmd.arg("-v");
}
cmd.arg("--");
cmd.arg("-Z");
cmd.arg("force-unstable-if-unmarked");
cmd.run(verbose)?;
util::cp_r(
&td.join("target")
.join(cmode.triple())
.join(profile())
.join("deps"),
dst,
)?;
Ok(())
}
fn build_liballoc(
cmode: &CompilationMode,
ctoml: &cargo::Toml,
src: &Src,
dst: &Path,
config: &Config,
verbose: bool,
) -> Result<()> {
const TOML: &'static str = r#"
[package]
authors = ["The Rust Project Developers"]
name = "alloc"
version = "0.0.0"
edition = "2018"
[dependencies.compiler_builtins]
version = "0.1.0"
"#;
let mut stoml = TOML.to_owned();
if config.memcpy {
stoml.push_str("features = ['mem', 'core']\n");
} else {
stoml.push_str("features = ['rustc-std-workspace-core']\n");
}
stoml.push_str("[dependencies.core]\n");
stoml.push_str(&format!(
"path = '{}'\n",
src.path().join("libcore").display()
));
stoml.push_str("[patch.crates-io.rustc-std-workspace-core]\n");
stoml.push_str(&format!(
"path = '{}'\n",
src.path().join("tools/rustc-std-workspace-core").display()
));
let path = src.path().join("liballoc/lib.rs").display().to_string();
let mut map = Table::new();
let mut lib = Table::new();
lib.insert("name".to_owned(), Value::String("alloc".to_owned()));
lib.insert("path".to_owned(), Value::String(path));
map.insert("lib".to_owned(), Value::Table(lib));
stoml.push_str(&Value::Table(map).to_string());
build_crate("alloc", stoml, cmode, ctoml, dst, verbose)
}
fn old_hash(cmode: &CompilationMode, home: &Home) -> Result<Option<u64>> {
let lock = home.lock_rw(cmode.triple())?;
let hfile = lock.parent().join(".hash");
if hfile.exists() {
Ok(util::read(&hfile)?.parse().ok())
} else {
Ok(None)
}
}
fn hash(
cmode: &CompilationMode,
rustflags: &Rustflags,
ctoml: &cargo::Toml,
meta: &VersionMeta,
config: &Config,
) -> Result<u64> {
let mut hasher = DefaultHasher::new();
rustflags.hash(&mut hasher);
cmode.hash(&mut hasher)?;
if let Some(profile) = ctoml.profile() {
profile.hash(&mut hasher);
}
if let Some(ref hash) = meta.commit_hash {
hash.hash(&mut hasher);
}
config.hash(&mut hasher);
Ok(hasher.finish())
}
pub fn update(
cmode: &CompilationMode,
home: &Home,
root: &Path,
config: &Config,
rustflags: &Rustflags,
meta: &VersionMeta,
src: &Src,
sysroot: &Sysroot,
verbose: bool,
) -> Result<()> {
let ctoml = cargo::toml(root)?;
let mut stderr = io::stderr();
let hash = hash(cmode, rustflags, &ctoml, meta, config)?;
if old_hash(cmode, home)? != Some(hash) {
build(cmode, &ctoml, home, config, src, hash, verbose)?;
}
if cmode.is_native() {
return Ok(());
}
let lock = home.lock_rw(&meta.host)?;
let hfile = lock.parent().join(".hash");
let hash = meta.commit_hash.as_ref().map(|s| &**s).unwrap_or("");
if hfile.exists() {
if util::read(&hfile)? == hash {
return Ok(());
}
}
lock.remove_siblings()
.chain_err(|| format!("couldn't clear {}", lock.path().display()))?;
let dst = lock.parent().join("lib");
util::mkdir(&dst)?;
match util::cp_r(
&sysroot
.path()
.join("lib/rustlib")
.join(&meta.host)
.join("lib"),
&dst,
) {
Ok(()) => {}
Err(e) => {
writeln!(
stderr,
"Unable to copy the directory 'lib' from sysroot: {}",
e
)
.ok();
}
};
let bin_dst = lock.parent().join("bin");
util::mkdir(&bin_dst)?;
match util::cp_r(
&sysroot
.path()
.join("lib/rustlib")
.join(&meta.host)
.join("bin"),
&bin_dst,
) {
Ok(()) => {}
Err(e) => {
writeln!(
stderr,
"Unable to copy the directory 'bin' from sysroot: {}",
e
)
.ok();
}
};
util::write(&hfile, hash)?;
Ok(())
}