use anyhow::{bail, Context};
use cargo_util::ProcessBuilder;
use semver::{Comparator, Op, Version};
use crate::error::Error;
use crate::CIResult;
pub const LLVM_MIN_VERSION: Version = Version::new(9, 0, 0);
pub const LLVM_MAX_VERSION: Version = Version::new(15, 0, 0);
#[derive(Copy, Clone, Debug)]
pub enum LlvmUtility {
Archiver,
Config,
Clang,
NameMangling,
Optimizer,
StaticCompiler,
}
impl LlvmUtility {
fn as_str(&self) -> &str {
match self {
LlvmUtility::Archiver => "llvm-ar",
LlvmUtility::Config => "llvm-config",
LlvmUtility::Clang => "clang",
LlvmUtility::NameMangling => "llvm-nm",
LlvmUtility::Optimizer => "opt",
LlvmUtility::StaticCompiler => "llc",
}
}
pub fn process_builder(&self, toolchain: &LlvmToolchain) -> ProcessBuilder {
if toolchain.suffix {
ProcessBuilder::new(format!("{}-{}", self.as_str(), toolchain.version.major))
} else {
ProcessBuilder::new(self.as_str())
}
}
}
#[derive(Debug)]
pub struct LlvmToolchain {
pub version: Version,
suffix: bool,
}
pub fn toolchain() -> CIResult<LlvmToolchain> {
let output = ProcessBuilder::new("rustc").arg("-vV").exec_with_output()?;
let stdout = String::from_utf8(output.stdout)?;
let rustc_llvm_version = Version::parse(
stdout
.lines()
.find_map(|line| line.strip_prefix("LLVM version: "))
.context("expect `LLVM version` field")?
.trim(),
)?;
if rustc_llvm_version < LLVM_MIN_VERSION || rustc_llvm_version >= LLVM_MAX_VERSION {
bail!(Error::LLVMNotSupported(rustc_llvm_version))
}
let config = ProcessBuilder::new("llvm-config")
.arg("--version")
.exec_with_output();
let config_suffix = ProcessBuilder::new(format!("llvm-config-{}", rustc_llvm_version.major))
.arg("--version")
.exec_with_output();
let comparator = Comparator {
op: Op::Exact,
major: rustc_llvm_version.major,
minor: Some(rustc_llvm_version.minor),
patch: None,
pre: Default::default(),
};
let add_suffix = match (config, config_suffix) {
(Ok(out), Ok(out_suffix)) => {
let llvm_version = Version::parse(String::from_utf8(out.stdout)?.trim())?;
let llvm_version_suffix = Version::parse(String::from_utf8(out_suffix.stdout)?.trim())?;
if comparator.matches(&llvm_version) {
false
} else if comparator.matches(&llvm_version_suffix) {
true
} else {
bail!(Error::LLVMVersionNotMatch(rustc_llvm_version, llvm_version));
}
}
(Ok(out), Err(_)) => {
let llvm_version = Version::parse(String::from_utf8(out.stdout)?.trim())?;
if !comparator.matches(&llvm_version) {
bail!(Error::LLVMVersionNotMatch(rustc_llvm_version, llvm_version));
}
false
}
(Err(_), Ok(out_suffix)) => {
let llvm_version_suffix = Version::parse(String::from_utf8(out_suffix.stdout)?.trim())?;
if !comparator.matches(&llvm_version_suffix) {
bail!(Error::LLVMVersionNotMatch(
rustc_llvm_version,
llvm_version_suffix
));
}
true
}
(Err(_), Err(_)) => {
bail!(Error::LLVMNotInstalled);
}
};
Ok(LlvmToolchain {
version: rustc_llvm_version,
suffix: add_suffix,
})
}