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