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