use crate::util::errors::CargoResult;
use crate::util::interning::InternedString;
use crate::util::{GlobalContext, StableHasher, try_canonicalize};
use crate::workspace::Target;
use anyhow::Context as _;
use anyhow::bail;
use cargo_util::ProcessBuilder;
use serde::Serialize;
use std::collections::BTreeSet;
use std::fs;
use std::hash::{Hash, Hasher};
use std::path::Path;
#[derive(PartialEq, Eq, Hash, Debug, Clone, Copy, PartialOrd, Ord)]
pub enum CompileKind {
Host,
Target(CompileTarget),
}
pub enum CompileKindFallback {
BuildConfig,
JustHost,
}
impl CompileKind {
pub fn is_host(&self) -> bool {
matches!(self, CompileKind::Host)
}
pub fn for_target(self, target: &Target) -> CompileKind {
match self {
CompileKind::Host => CompileKind::Host,
CompileKind::Target(_) if target.for_host() => CompileKind::Host,
CompileKind::Target(n) => CompileKind::Target(n),
}
}
pub fn from_requested_targets(
gctx: &GlobalContext,
targets: &[String],
) -> CargoResult<Vec<CompileKind>> {
CompileKind::from_requested_targets_with_fallback(
gctx,
targets,
CompileKindFallback::BuildConfig,
)
}
pub fn from_requested_targets_with_fallback(
gctx: &GlobalContext,
targets: &[String],
fallback: CompileKindFallback,
) -> CargoResult<Vec<CompileKind>> {
let dedup = |targets: &[String]| {
let deduplicated_targets = targets
.iter()
.map(|value| {
if value.as_str() == "host-tuple" {
let host_triple = env!("RUST_HOST_TARGET");
Ok(CompileKind::Target(CompileTarget::new(
host_triple,
gctx.cli_unstable().json_target_spec,
)?))
} else {
Ok(CompileKind::Target(CompileTarget::new(
value.as_str(),
gctx.cli_unstable().json_target_spec,
)?))
}
})
.collect::<CargoResult<BTreeSet<_>>>()?
.into_iter()
.collect();
Ok(deduplicated_targets)
};
if !targets.is_empty() {
return dedup(targets);
}
let kinds = match (fallback, &gctx.build_config()?.target) {
(_, None) | (CompileKindFallback::JustHost, _) => Ok(vec![CompileKind::Host]),
(CompileKindFallback::BuildConfig, Some(build_target_config)) => {
dedup(&build_target_config.values(gctx.cwd())?)
}
};
kinds
}
pub fn fingerprint_hash(&self) -> u64 {
match self {
CompileKind::Host => 0,
CompileKind::Target(target) => target.fingerprint_hash(),
}
}
pub fn add_target_arg(&self, builder: &mut ProcessBuilder) {
if let CompileKind::Target(target) = self {
builder.arg("--target").arg(target.rustc_target());
if matches!(target, CompileTarget::Json { .. }) {
builder.arg("-Zunstable-options");
}
}
}
}
impl serde::ser::Serialize for CompileKind {
fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
where
S: serde::ser::Serializer,
{
match self {
CompileKind::Host => None::<&str>.serialize(s),
CompileKind::Target(t) => Some(t.rustc_target()).serialize(s),
}
}
}
#[derive(PartialEq, Eq, Hash, Debug, Clone, Copy, PartialOrd, Ord, Serialize)]
pub enum CompileTarget {
Tuple(InternedString),
Json {
short: InternedString,
path: InternedString,
},
}
impl CompileTarget {
pub fn new(name: &str, unstable_json: bool) -> CargoResult<CompileTarget> {
let name = name.trim();
if name.is_empty() {
bail!("target was empty");
}
if !name.ends_with(".json") {
return Ok(CompileTarget::Tuple(name.into()));
}
if !unstable_json {
bail!(
"`.json` target specs require -Zjson-target-spec to be added to the cargo invocation"
);
}
let p = try_canonicalize(Path::new(name))
.with_context(|| format!("target path `{name}` is not a valid file"))?;
let path = p
.to_str()
.ok_or_else(|| anyhow::format_err!("target path `{name}` is not valid unicode"))?
.into();
let short = p.file_stem().unwrap().to_str().unwrap().into();
Ok(CompileTarget::Json { short, path })
}
pub fn rustc_target(&self) -> InternedString {
match self {
CompileTarget::Tuple(name) => *name,
CompileTarget::Json { path, .. } => *path,
}
}
pub fn short_name(&self) -> &str {
match self {
CompileTarget::Tuple(name) => name,
CompileTarget::Json { short, .. } => short,
}
}
pub fn fingerprint_hash(&self) -> u64 {
let mut hasher = StableHasher::new();
match self {
CompileTarget::Tuple(name) => name.hash(&mut hasher),
CompileTarget::Json { path, .. } => {
match fs::read_to_string(path) {
Ok(contents) => contents.hash(&mut hasher),
Err(_) => path.hash(&mut hasher),
}
}
}
Hasher::finish(&hasher)
}
}