use serde::{Deserialize, Serialize};
use super::identity::{
ArchiverIdentity, ArchiverKind, CompilerIdentity, CompilerKind, CompilerVersion,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum CapabilitySource {
Version,
AssumedDefault,
Unsupported,
}
impl CapabilitySource {
pub fn as_key(self) -> &'static str {
match self {
CapabilitySource::Version => "version",
CapabilitySource::AssumedDefault => "assumed-default",
CapabilitySource::Unsupported => "unsupported",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct Capability {
pub supported: bool,
pub source: CapabilitySource,
}
impl Capability {
pub fn supported_from(source: CapabilitySource) -> Self {
Self {
supported: true,
source,
}
}
pub fn unsupported_from(source: CapabilitySource) -> Self {
Self {
supported: false,
source,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct CompilerCapabilities {
pub gcc_style_flags: Capability,
pub msvc_style_flags: Capability,
pub depfile_mmd_mf: Capability,
pub std_flag: Capability,
pub cxx_standard_17: Capability,
pub c_standard_11: Capability,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ArchiverCapabilities {
pub ar_crs: Capability,
pub static_library_output: Capability,
}
fn msvc_versioned_capability(
version: Option<&CompilerVersion>,
min_major: u32,
min_minor: u32,
) -> Capability {
match version.map(|v| (v.major, v.minor.unwrap_or(0))) {
Some((major, minor)) if major > min_major || (major == min_major && minor >= min_minor) => {
Capability::supported_from(CapabilitySource::Version)
}
Some(_) => Capability::unsupported_from(CapabilitySource::Version),
None => Capability::supported_from(CapabilitySource::AssumedDefault),
}
}
pub fn derive_cxx_capabilities(identity: &CompilerIdentity) -> CompilerCapabilities {
let gcc_style = if identity.kind.supports_gcc_style_command_line() {
Capability::supported_from(CapabilitySource::Version)
} else if identity.kind.speaks_msvc_dialect() {
Capability::unsupported_from(CapabilitySource::Unsupported)
} else {
Capability::unsupported_from(CapabilitySource::AssumedDefault)
};
let msvc_style = if identity.kind.speaks_msvc_dialect() {
Capability::supported_from(CapabilitySource::Version)
} else {
Capability::unsupported_from(CapabilitySource::AssumedDefault)
};
let depfile_mmd_mf = if identity.kind.supports_gcc_style_command_line() {
Capability::supported_from(CapabilitySource::Version)
} else if identity.kind.speaks_msvc_dialect() {
Capability::unsupported_from(CapabilitySource::Unsupported)
} else {
Capability::unsupported_from(CapabilitySource::AssumedDefault)
};
let std_flag = if identity.kind.supports_gcc_style_command_line() {
Capability::supported_from(CapabilitySource::Version)
} else if identity.kind.speaks_msvc_dialect() {
Capability::unsupported_from(CapabilitySource::Unsupported)
} else {
Capability::unsupported_from(CapabilitySource::AssumedDefault)
};
let cxx_standard_17 = match identity.kind {
CompilerKind::Clang | CompilerKind::AppleClang | CompilerKind::ClangCl => {
Capability::supported_from(CapabilitySource::Version)
}
CompilerKind::Gcc => match identity.version.as_ref().map(|v| v.major) {
Some(m) if m >= 5 => Capability::supported_from(CapabilitySource::Version),
Some(_) => Capability::unsupported_from(CapabilitySource::Version),
None => Capability::supported_from(CapabilitySource::AssumedDefault),
},
CompilerKind::Msvc => msvc_versioned_capability(identity.version.as_ref(), 19, 11),
CompilerKind::Unknown => Capability::unsupported_from(CapabilitySource::AssumedDefault),
};
let c_standard_11 = match identity.kind {
CompilerKind::Clang
| CompilerKind::AppleClang
| CompilerKind::ClangCl
| CompilerKind::Gcc => Capability::supported_from(CapabilitySource::Version),
CompilerKind::Msvc => msvc_versioned_capability(identity.version.as_ref(), 19, 28),
CompilerKind::Unknown => Capability::unsupported_from(CapabilitySource::AssumedDefault),
};
CompilerCapabilities {
gcc_style_flags: gcc_style,
msvc_style_flags: msvc_style,
depfile_mmd_mf,
std_flag,
cxx_standard_17,
c_standard_11,
}
}
pub fn derive_ar_capabilities(identity: &ArchiverIdentity) -> ArchiverCapabilities {
let ar_crs = if identity.kind.supports_ar_crs() {
Capability::supported_from(CapabilitySource::Version)
} else if identity.kind == ArchiverKind::Lib {
Capability::unsupported_from(CapabilitySource::Unsupported)
} else {
Capability::unsupported_from(CapabilitySource::AssumedDefault)
};
let static_library_output = if identity.kind.produces_static_library() {
Capability::supported_from(CapabilitySource::Version)
} else {
Capability::unsupported_from(CapabilitySource::AssumedDefault)
};
ArchiverCapabilities {
ar_crs,
static_library_output,
}
}