1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
|
// SPDX-License-Identifier: Apache-2.0
use std::path::{Path, PathBuf};
use glob::Pattern;
use super::common;
//================================================
// Searching
//================================================
/// Clang static libraries required to link to `libclang` 3.5 and later.
const CLANG_LIBRARIES: &[&str] = &[
"clang",
"clangAST",
"clangAnalysis",
"clangBasic",
"clangDriver",
"clangEdit",
"clangFrontend",
"clangIndex",
"clangLex",
"clangParse",
"clangRewrite",
"clangSema",
"clangSerialization",
];
/// Gets the name of an LLVM or Clang static library from a path.
fn get_library_name(path: &Path) -> Option<String> {
path.file_stem().map(|p| {
let string = p.to_string_lossy();
if let Some(name) = string.strip_prefix("lib") {
name.to_owned()
} else {
string.to_string()
}
})
}
/// Gets the LLVM static libraries required to link to `libclang`.
fn get_llvm_libraries() -> Vec<String> {
common::run_llvm_config(&["--libs"])
.unwrap()
.split_whitespace()
.filter_map(|p| {
// Depending on the version of `llvm-config` in use, listed
// libraries may be in one of two forms, a full path to the library
// or simply prefixed with `-l`.
if let Some(path) = p.strip_prefix("-l") {
Some(path.into())
} else {
get_library_name(Path::new(p))
}
})
.collect()
}
/// Gets the Clang static libraries required to link to `libclang`.
fn get_clang_libraries<P: AsRef<Path>>(directory: P) -> Vec<String> {
// Escape the directory in case it contains characters that have special
// meaning in glob patterns (e.g., `[` or `]`).
let directory = Pattern::escape(directory.as_ref().to_str().unwrap());
let directory = Path::new(&directory);
let pattern = directory.join("libclang*.a").to_str().unwrap().to_owned();
if let Ok(libraries) = glob::glob(&pattern) {
libraries
.filter_map(|l| l.ok().and_then(|l| get_library_name(&l)))
.collect()
} else {
CLANG_LIBRARIES.iter().map(|l| (*l).to_string()).collect()
}
}
/// Finds a directory containing LLVM and Clang static libraries and returns the
/// path to that directory.
fn find() -> PathBuf {
let name = if target_os!("windows") {
"libclang.lib"
} else {
"libclang.a"
};
let files = common::search_libclang_directories(&[name.into()], "LIBCLANG_STATIC_PATH");
if let Some((directory, _)) = files.into_iter().next() {
directory
} else {
panic!(
"could not find the required `{name}` static library, see the \
README for more information on how to link to `libclang` statically: \
https://github.com/KyleMayes/clang-sys?tab=readme-ov-file#static"
);
}
}
//================================================
// Linking
//================================================
/// Finds and links to `libclang` static libraries.
pub fn link() {
let cep = common::CommandErrorPrinter::default();
let directory = find();
// Specify required Clang static libraries.
println!("cargo:rustc-link-search=native={}", directory.display());
for library in get_clang_libraries(directory) {
println!("cargo:rustc-link-lib=static={}", library);
}
// Determine the shared mode used by LLVM.
let mode = common::run_llvm_config(&["--shared-mode"]).map(|m| m.trim().to_owned());
let prefix = if mode.map_or(false, |m| m == "static") {
"static="
} else {
""
};
// Specify required LLVM static libraries.
println!(
"cargo:rustc-link-search=native={}",
common::run_llvm_config(&["--libdir"]).unwrap().trim_end()
);
for library in get_llvm_libraries() {
println!("cargo:rustc-link-lib={}{}", prefix, library);
}
// Specify required system libraries.
// MSVC doesn't need this, as it tracks dependencies inside `.lib` files.
if cfg!(target_os = "freebsd") {
println!("cargo:rustc-flags=-l ffi -l ncursesw -l c++ -l z");
} else if cfg!(any(target_os = "haiku", target_os = "linux")) {
if cfg!(feature = "libcpp") {
println!("cargo:rustc-flags=-l c++");
} else {
println!("cargo:rustc-flags=-l ffi -l ncursesw -l stdc++ -l z");
}
} else if cfg!(target_os = "macos") {
println!("cargo:rustc-flags=-l ffi -l ncurses -l c++ -l z");
}
cep.discard();
}
|