use std::collections::BTreeSet;
use thiserror::Error;
use super::capabilities::{
ArchiverCapabilities, CompilerCapabilities, c_standard_capability, cxx_standard_capability,
standard_support_detail,
};
use super::identity::{ArchiverIdentity, ArchiverKind, CompilerIdentity, CompilerKind};
use crate::language_standard::{CStandard, CxxStandard};
#[derive(Debug, Error, Clone, PartialEq, Eq)]
pub enum ToolDetectionError {
#[error("selected C++ compiler `{spec}` cannot be matched to a supported C++ backend")]
UnsupportedCxxBackend { spec: String },
#[error(
"selected C++ compiler `{spec}` could not be identified and the current backend requires GCC-style flags"
)]
UnknownCxxRequiresGccStyle { spec: String },
#[error(
"selected C++ compiler `{spec}` ({kind}) does not support the requested C++ standard `{standard}`: {detail}"
)]
CxxLacksStandard {
spec: String,
kind: CompilerKind,
standard: CxxStandard,
detail: String,
},
#[error(
"selected C++ compiler `{spec}` ({kind}) does not support the depfile flags required by the Ninja backend"
)]
CxxLacksDepfile { spec: String, kind: CompilerKind },
#[error("selected C compiler `{spec}` cannot be matched to a supported C backend")]
UnsupportedCBackend { spec: String },
#[error(
"selected C compiler `{spec}` could not be identified and the current backend requires GCC-style flags"
)]
UnknownCRequiresGccStyle { spec: String },
#[error(
"selected C compiler `{spec}` ({kind}) does not support the depfile flags required by the Ninja backend"
)]
CLacksDepfile { spec: String, kind: CompilerKind },
#[error(
"selected C compiler `{spec}` ({kind}) does not support the requested C standard `{standard}`: {detail}"
)]
CLacksStandard {
spec: String,
kind: CompilerKind,
standard: CStandard,
detail: String,
},
#[error("selected archiver `{spec}` is not supported by the static-library backend")]
UnsupportedArchiver { spec: String },
#[error(
"selected archiver `{spec}` could not be identified and the current backend requires `ar crs`-compatible behavior"
)]
UnknownArchiverRequiresArCompatible { spec: String },
}
pub fn validate_cxx_for_backend(
spec_display: &str,
identity: &CompilerIdentity,
capabilities: &CompilerCapabilities,
) -> Result<(), ToolDetectionError> {
if identity.kind.speaks_msvc_dialect() {
if !capabilities.msvc_style_flags.supported {
return Err(ToolDetectionError::UnsupportedCxxBackend {
spec: spec_display.to_owned(),
});
}
return Ok(());
}
if !capabilities.gcc_style_flags.supported {
if identity.kind == CompilerKind::Unknown {
return Err(ToolDetectionError::UnknownCxxRequiresGccStyle {
spec: spec_display.to_owned(),
});
}
return Err(ToolDetectionError::UnsupportedCxxBackend {
spec: spec_display.to_owned(),
});
}
if !capabilities.depfile_mmd_mf.supported {
return Err(ToolDetectionError::CxxLacksDepfile {
spec: spec_display.to_owned(),
kind: identity.kind,
});
}
Ok(())
}
pub fn validate_cxx_standards(
spec_display: &str,
identity: &CompilerIdentity,
requested: &BTreeSet<CxxStandard>,
) -> Result<(), ToolDetectionError> {
for &standard in requested {
let capability = cxx_standard_capability(identity, standard);
if !capability.supported {
return Err(ToolDetectionError::CxxLacksStandard {
spec: spec_display.to_owned(),
kind: identity.kind,
standard,
detail: standard_support_detail(capability, identity.kind),
});
}
}
Ok(())
}
pub fn validate_c_standards(
spec_display: &str,
identity: &CompilerIdentity,
requested: &BTreeSet<CStandard>,
) -> Result<(), ToolDetectionError> {
for &standard in requested {
let capability = c_standard_capability(identity, standard);
if !capability.supported {
return Err(ToolDetectionError::CLacksStandard {
spec: spec_display.to_owned(),
kind: identity.kind,
standard,
detail: standard_support_detail(capability, identity.kind),
});
}
}
Ok(())
}
pub fn validate_cc_for_backend(
spec_display: &str,
identity: &CompilerIdentity,
capabilities: &CompilerCapabilities,
) -> Result<(), ToolDetectionError> {
if identity.kind.speaks_msvc_dialect() {
if !capabilities.msvc_style_flags.supported {
return Err(ToolDetectionError::UnsupportedCBackend {
spec: spec_display.to_owned(),
});
}
return Ok(());
}
if !capabilities.gcc_style_flags.supported {
if identity.kind == CompilerKind::Unknown {
return Err(ToolDetectionError::UnknownCRequiresGccStyle {
spec: spec_display.to_owned(),
});
}
return Err(ToolDetectionError::UnsupportedCBackend {
spec: spec_display.to_owned(),
});
}
if !capabilities.depfile_mmd_mf.supported {
return Err(ToolDetectionError::CLacksDepfile {
spec: spec_display.to_owned(),
kind: identity.kind,
});
}
Ok(())
}
pub fn validate_ar_for_backend(
spec_display: &str,
identity: &ArchiverIdentity,
capabilities: &ArchiverCapabilities,
) -> Result<(), ToolDetectionError> {
if identity.kind == ArchiverKind::Lib {
return Ok(());
}
if !capabilities.ar_crs.supported {
if identity.kind == ArchiverKind::Unknown {
return Err(ToolDetectionError::UnknownArchiverRequiresArCompatible {
spec: spec_display.to_owned(),
});
}
return Err(ToolDetectionError::UnsupportedArchiver {
spec: spec_display.to_owned(),
});
}
Ok(())
}