use std::path::{Path, PathBuf};
use crate::build_env::Error;
use crate::sdk::Sdk;
use crate::target::Target;
const LIBCXX_SONAME: &str = "libc++.so";
#[derive(Debug, Clone, serde::Serialize)]
pub struct Toolchain {
pub root: PathBuf,
pub external: bool,
pub clang: PathBuf,
pub clangxx: PathBuf,
pub ar: PathBuf,
pub ranlib: PathBuf,
pub strip: PathBuf,
pub objcopy: PathBuf,
pub readelf: PathBuf,
pub libclang_dir: PathBuf,
}
#[derive(Debug, Clone, serde::Serialize)]
pub struct RuntimeLibrary {
pub path: PathBuf,
pub soname: String,
pub kind: RuntimeLibraryKind,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize)]
#[serde(rename_all = "snake_case")]
pub enum RuntimeLibraryKind {
CxxStdlib,
}
impl Toolchain {
pub fn resolve(sdk: &Sdk, llvm: Option<&Path>) -> Result<Self, Error> {
let root = match llvm {
Some(root) => {
let root = root.canonicalize().map_err(|source| Error::Io {
path: root.to_path_buf(),
source,
})?;
if !root.join("include").join("libcxx-ohos").is_dir() {
return Err(Error::InvalidToolchain { path: root });
}
root
}
None => sdk.llvm_root.clone(),
};
let bin = root.join("bin");
Ok(Self {
external: llvm.is_some(),
clang: tool(&bin, "clang")?,
clangxx: tool(&bin, "clang++")?,
ar: tool(&bin, "llvm-ar")?,
ranlib: tool(&bin, "llvm-ranlib")?,
strip: tool(&bin, "llvm-strip")?,
objcopy: tool(&bin, "llvm-objcopy")?,
readelf: tool(&bin, "llvm-readelf")?,
libclang_dir: libclang_dir(&root)?,
root,
})
}
pub fn runtime_libraries(&self, target: &Target) -> Result<Vec<RuntimeLibrary>, Error> {
if !self.external {
return Ok(Vec::new());
}
let path = self
.root
.join("lib")
.join(&target.lib_dir)
.join(LIBCXX_SONAME);
if !path.is_file() {
return Err(Error::MissingRuntimeLibrary { path });
}
Ok(vec![RuntimeLibrary {
path,
soname: LIBCXX_SONAME.to_owned(),
kind: RuntimeLibraryKind::CxxStdlib,
}])
}
}
fn tool(bin_dir: &Path, name: &str) -> Result<PathBuf, Error> {
let mut path = bin_dir.join(name);
if cfg!(windows) {
path.set_extension("exe");
}
if !path.is_file() {
return Err(Error::MissingTool { path });
}
Ok(path)
}
fn libclang_dir(root: &Path) -> Result<PathBuf, Error> {
let dirs: &[&str] = if cfg!(windows) {
&["lib", "bin"]
} else {
&["lib"]
};
for dir in dirs {
let dir = root.join(dir);
if contains_libclang(&dir) {
return Ok(dir);
}
}
Err(Error::MissingLibclang {
path: root.join("lib"),
})
}
fn contains_libclang(dir: &Path) -> bool {
let ext = if cfg!(windows) {
".dll"
} else if cfg!(target_os = "macos") {
".dylib"
} else {
".so"
};
let Ok(entries) = std::fs::read_dir(dir) else {
return false;
};
entries.flatten().any(|entry| {
let name = entry.file_name();
let name = name.to_string_lossy();
name.starts_with("libclang.") && name.contains(ext)
})
}