pub mod types;
#[cfg(windows)]
pub mod windows;
pub mod install;
pub use types::{CompilerType, Toolchain, ToolchainError};
use std::path::PathBuf;
pub fn detect_toolchain(preferred: Option<CompilerType>) -> Result<Toolchain, ToolchainError> {
#[cfg(windows)]
{
windows::detect_toolchain(preferred)
}
#[cfg(not(windows))]
{
detect_unix_toolchain(preferred)
}
}
#[cfg(not(windows))]
fn detect_unix_toolchain(preferred: Option<CompilerType>) -> Result<Toolchain, ToolchainError> {
use std::process::Command;
let compilers = match preferred {
Some(CompilerType::GCC) => {
vec![("g++", CompilerType::GCC), ("clang++", CompilerType::Clang)]
}
_ => vec![("clang++", CompilerType::Clang), ("g++", CompilerType::GCC)],
};
for (cmd, compiler_type) in compilers {
if let Ok(output) = Command::new("which").arg(cmd).output() {
if output.status.success() {
let path_str = String::from_utf8_lossy(&output.stdout).trim().to_string();
let cxx_path = PathBuf::from(&path_str);
let version = Command::new(cmd)
.arg("--version")
.output()
.map(|o| {
String::from_utf8_lossy(&o.stdout)
.lines()
.next()
.unwrap_or("unknown")
.to_string()
})
.unwrap_or_else(|_| "unknown".to_string());
return Ok(Toolchain::new_simple(compiler_type, cxx_path, version));
}
}
}
Err(ToolchainError::NotFound(
"No C++ compiler found. Please install clang or gcc.".to_string(),
))
}
pub fn get_or_detect_toolchain(
preferred: Option<CompilerType>,
force_detect: bool,
) -> Result<Toolchain, ToolchainError> {
let selection_path = get_user_selection_path();
if !force_detect
&& selection_path.exists()
&& let Ok(contents) = std::fs::read_to_string(&selection_path)
{
let mut selected_type: Option<CompilerType> = None;
let mut selected_path: Option<PathBuf> = None;
let mut selected_source: Option<String> = None;
for line in contents.lines() {
if line.starts_with("compiler_type") {
if line.contains("\"MSVC\"") {
selected_type = Some(CompilerType::MSVC);
} else if line.contains("\"ClangCL\"") {
selected_type = Some(CompilerType::ClangCL);
} else if line.contains("\"Clang\"") {
selected_type = Some(CompilerType::Clang);
} else if line.contains("\"GCC\"") {
selected_type = Some(CompilerType::GCC);
}
}
if line.starts_with("path") {
if let Some(start) = line.find('"')
&& let Some(end) = line.rfind('"')
&& start < end
{
selected_path = Some(PathBuf::from(&line[start + 1..end]));
}
}
if line.starts_with("source") {
if let Some(start) = line.find('"')
&& let Some(end) = line.rfind('"')
&& start < end
{
selected_source = Some(line[start + 1..end].to_string());
}
}
}
if let (Some(sel_type), Some(path)) = (&selected_type, &selected_path) {
let matches_preference = match &preferred {
None => true,
Some(pref) => pref == sel_type,
};
if matches_preference && path.exists() {
#[cfg(windows)]
{
if let Some(ref source) = selected_source
&& let Ok(toolchain) =
windows::detect_toolchain_from_source(sel_type.clone(), source)
{
return Ok(toolchain);
}
}
if sel_type == &CompilerType::GCC {
let version = std::process::Command::new(path)
.arg("--version")
.output()
.map(|o| {
String::from_utf8_lossy(&o.stdout)
.lines()
.next()
.unwrap_or("unknown")
.to_string()
})
.unwrap_or_else(|_| "unknown".to_string());
return Ok(Toolchain {
compiler_type: CompilerType::GCC,
cc_path: path.with_file_name("gcc.exe"),
cxx_path: path.clone(),
linker_path: PathBuf::new(),
version,
msvc_toolset_version: None,
windows_sdk_version: None,
vs_install_path: None,
env_vars: std::collections::HashMap::new(),
});
}
}
}
}
let cache_path = get_toolchain_cache_path();
if !force_detect
&& cache_path.exists()
&& let Ok(contents) = std::fs::read_to_string(&cache_path)
&& let Ok(cached) = toml::from_str::<Toolchain>(&contents)
{
if cached.cxx_path.exists() {
let matches_preference = match &preferred {
None => true, Some(pref) => *pref == cached.compiler_type,
};
if matches_preference {
return Ok(cached);
}
}
}
let toolchain = detect_toolchain(preferred)?;
if let Ok(toml_str) = toml::to_string_pretty(&toolchain) {
if let Some(parent) = cache_path.parent() {
let _ = std::fs::create_dir_all(parent);
}
let _ = std::fs::write(&cache_path, toml_str);
}
Ok(toolchain)
}
fn get_user_selection_path() -> PathBuf {
dirs::home_dir()
.unwrap_or_else(|| PathBuf::from("."))
.join(".cx")
.join("toolchain-selection.toml")
}
fn get_toolchain_cache_path() -> PathBuf {
dirs::home_dir()
.unwrap_or_else(|| PathBuf::from("."))
.join(".cx")
.join("toolchain.toml")
}
#[allow(dead_code)]
pub fn clear_toolchain_cache() {
let cache_path = get_toolchain_cache_path();
let _ = std::fs::remove_file(cache_path);
}