#! [allow(dead_code)]
extern crate glob;
extern crate serial_test;
extern crate tempfile;
use std::collections::HashMap;
use std::env;
use std::fs;
use std::path::PathBuf;
use std::sync::Arc;
use std::sync::Mutex;
use serial_test::serial;
use tempfile::TempDir;
#[ macro_use]
#[ path = "../build/macros.rs" ]
mod macros;
#[ path = "../build/common.rs" ]
mod common;
#[ path = "../build/dynamic.rs" ]
mod dynamic;
#[ path = "../build/static.rs" ]
mod r#static ;
#[ derive(Debug, Default)]
struct RunCommandMock {
invocations: Vec<(String, String, Vec<String>)>,
responses: HashMap<Vec<String>, String>,
}
#[ derive(Debug)]
struct Env {
os: String,
pointer_width: String,
env: Option<String>,
vars: HashMap<String, (Option<String>, Option<String>)>,
cwd: PathBuf,
tmp: TempDir,
files: Vec<String>,
commands: Arc<Mutex<RunCommandMock>>,
}
impl Env {
fn new(os: &str, pointer_width: &str) -> Self {
Env {
os: os.into(),
pointer_width: pointer_width.into(),
env: None,
vars: HashMap::new(),
cwd: env::current_dir().unwrap(),
tmp: tempfile::Builder::new().prefix("clang_sys_test" ).tempdir().unwrap(),
files: vec![],
commands: Default::default(),
}
.var("CLANG_PATH" , None)
.var("LD_LIBRARY_PATH" , None)
.var("LIBCLANG_PATH" , None)
.var("LIBCLANG_STATIC_PATH" , None)
.var("LLVM_CONFIG_PATH" , None)
.var("PATH" , None)
}
fn env(mut self , env: &str) -> Self {
self .env = Some(env.into());
self
}
fn var(mut self , name: &str, value: Option<&str>) -> Self {
let previous = env::var(name).ok();
self .vars.insert(name.into(), (value.map(|v| v.into()), previous));
self
}
fn dir(mut self , path: &str) -> Self {
self .files.push(path.into());
let path = self .tmp.path().join(path);
fs::create_dir_all(path).unwrap();
self
}
fn file(mut self , path: &str, contents: &[u8]) -> Self {
self .files.push(path.into());
let path = self .tmp.path().join(path);
fs::create_dir_all(path.parent().unwrap()).unwrap();
fs::write(self .tmp.path().join(path), contents).unwrap();
self
}
fn dll(self , path: &str, pointer_width: &str) -> Self {
// PE header.
let mut contents = [0 ; 64 ];
contents[0 x3C..0 x3C + 4 ].copy_from_slice(&i32::to_le_bytes(10 ));
contents[10 ..14 ].copy_from_slice(&[b'P' , b'E' , 0 , 0 ]);
let magic = if pointer_width == "64" { 523 } else { 267 };
contents[34 ..36 ].copy_from_slice(&u16::to_le_bytes(magic));
self .file(path, &contents)
}
fn so(self , path: &str, pointer_width: &str) -> Self {
// ELF header.
let class = if pointer_width == "64" { 2 } else { 1 };
let contents = [127 , 69 , 76 , 70 , class];
self .file(path, &contents)
}
fn command(self , command: &str, args: &[&str], response: &str) -> Self {
let command = command.to_string();
let args = args.iter().map(|a| a.to_string()).collect::<Vec<_>>();
let mut key = vec![command];
key.extend(args);
self .commands.lock().unwrap().responses.insert(key, response.into());
self
}
fn enable(self ) -> Self {
env::set_var("_CLANG_SYS_TEST" , "yep" );
env::set_var("_CLANG_SYS_TEST_OS" , &self .os);
env::set_var("_CLANG_SYS_TEST_POINTER_WIDTH" , &self .pointer_width);
if let Some(env) = &self .env {
env::set_var("_CLANG_SYS_TEST_ENV" , env);
}
for (name, (value, _)) in &self .vars {
if let Some(value) = value {
env::set_var(name, value);
} else {
env::remove_var(name);
}
}
env::set_current_dir(&self .tmp).unwrap();
let commands = self .commands.clone();
let mock = &mut *common::RUN_COMMAND_MOCK.lock().unwrap();
*mock = Some(Box ::new(move |command, path, args| {
let command = command.to_string();
let path = path.to_string();
let args = args.iter().map(|a| a.to_string()).collect::<Vec<_>>();
let mut commands = commands.lock().unwrap();
commands.invocations.push((command.clone(), path, args.clone()));
let mut key = vec![command];
key.extend(args);
commands.responses.get(&key).cloned()
}));
self
}
}
impl Drop for Env {
fn drop(&mut self ) {
env::remove_var("_CLANG_SYS_TEST" );
env::remove_var("_CLANG_SYS_TEST_OS" );
env::remove_var("_CLANG_SYS_TEST_POINTER_WIDTH" );
env::remove_var("_CLANG_SYS_TEST_ENV" );
for (name, (_, previous)) in &self .vars {
if let Some(previous) = previous {
env::set_var(name, previous);
} else {
env::remove_var(name);
}
}
if let Err(error) = env::set_current_dir(&self .cwd) {
println!("Failed to reset working directory: {:?}" , error);
}
}
}
//================================================
// Dynamic
//================================================
// Linux -----------------------------------------
#[ test]
#[ serial]
fn test_linux_directory_preference() {
let _env = Env::new("linux" , "64" )
.so("usr/lib/libclang.so.1" , "64" )
.so("usr/local/lib/libclang.so.1" , "64" )
.enable();
assert_eq!(
dynamic::find(true ),
Ok(("usr/local/lib" .into(), "libclang.so.1" .into())),
);
}
#[ test]
#[ serial]
fn test_linux_version_preference() {
let _env = Env::new("linux" , "64" )
.so("usr/lib/libclang-3.so" , "64" )
.so("usr/lib/libclang-3.5.so" , "64" )
.so("usr/lib/libclang-3.5.0.so" , "64" )
.enable();
assert_eq!(
dynamic::find(true ),
Ok(("usr/lib" .into(), "libclang-3.5.0.so" .into())),
);
}
#[ test]
#[ serial]
fn test_linux_directory_and_version_preference() {
let _env = Env::new("linux" , "64" )
.so("usr/local/llvm/lib/libclang-3.so" , "64" )
.so("usr/local/lib/libclang-3.5.so" , "64" )
.so("usr/lib/libclang-3.5.0.so" , "64" )
.enable();
assert_eq!(
dynamic::find(true ),
Ok(("usr/lib" .into(), "libclang-3.5.0.so" .into())),
);
}
// Windows ---------------------------------------
#[ cfg(target_os = "windows" )]
#[ test]
#[ serial]
fn test_windows_bin_sibling() {
let _env = Env::new("windows" , "64" )
.dir("Program Files\\LLVM\\lib" )
.dll("Program Files\\LLVM\\bin\\libclang.dll" , "64" )
.enable();
assert_eq!(
dynamic::find(true ),
Ok(("Program Files\\LLVM\\bin" .into(), "libclang.dll" .into())),
);
}
#[ cfg(target_os = "windows" )]
#[ test]
#[ serial]
fn test_windows_mingw_gnu() {
let _env = Env::new("windows" , "64" )
.env("gnu" )
.dir("MSYS\\MinGW\\lib" )
.dll("MSYS\\MinGW\\bin\\clang.dll" , "64" )
.dir("Program Files\\LLVM\\lib" )
.dll("Program Files\\LLVM\\bin\\libclang.dll" , "64" )
.enable();
assert_eq!(
dynamic::find(true ),
Ok(("MSYS\\MinGW\\bin" .into(), "clang.dll" .into())),
);
}
#[ cfg(target_os = "windows" )]
#[ test]
#[ serial]
fn test_windows_mingw_msvc() {
let _env = Env::new("windows" , "64" )
.env("msvc" )
.dir("MSYS\\MinGW\\lib" )
.dll("MSYS\\MinGW\\bin\\clang.dll" , "64" )
.dir("Program Files\\LLVM\\lib" )
.dll("Program Files\\LLVM\\bin\\libclang.dll" , "64" )
.enable();
assert_eq!(
dynamic::find(true ),
Ok(("Program Files\\LLVM\\bin" .into(), "libclang.dll" .into())),
);
}
Messung V0.5 in Prozent C=89 H=98 G=93
¤ Dauer der Verarbeitung: 0.10 Sekunden
(vorverarbeitet am 2026-06-18)
¤
*© Formatika GbR, Deutschland