16cdb10c1Sopenharmony_ci// SPDX-License-Identifier: Apache-2.0
26cdb10c1Sopenharmony_ci
36cdb10c1Sopenharmony_ciextern crate glob;
46cdb10c1Sopenharmony_ci
56cdb10c1Sopenharmony_ciuse std::path::{Path, PathBuf};
66cdb10c1Sopenharmony_ci
76cdb10c1Sopenharmony_ciuse glob::Pattern;
86cdb10c1Sopenharmony_ci
96cdb10c1Sopenharmony_ciuse common;
106cdb10c1Sopenharmony_ci
116cdb10c1Sopenharmony_ci//================================================
126cdb10c1Sopenharmony_ci// Searching
136cdb10c1Sopenharmony_ci//================================================
146cdb10c1Sopenharmony_ci
156cdb10c1Sopenharmony_ci/// Clang static libraries required to link to `libclang` 3.5 and later.
166cdb10c1Sopenharmony_ciconst CLANG_LIBRARIES: &[&str] = &[
176cdb10c1Sopenharmony_ci    "clang",
186cdb10c1Sopenharmony_ci    "clangAST",
196cdb10c1Sopenharmony_ci    "clangAnalysis",
206cdb10c1Sopenharmony_ci    "clangBasic",
216cdb10c1Sopenharmony_ci    "clangDriver",
226cdb10c1Sopenharmony_ci    "clangEdit",
236cdb10c1Sopenharmony_ci    "clangFrontend",
246cdb10c1Sopenharmony_ci    "clangIndex",
256cdb10c1Sopenharmony_ci    "clangLex",
266cdb10c1Sopenharmony_ci    "clangParse",
276cdb10c1Sopenharmony_ci    "clangRewrite",
286cdb10c1Sopenharmony_ci    "clangSema",
296cdb10c1Sopenharmony_ci    "clangSerialization",
306cdb10c1Sopenharmony_ci];
316cdb10c1Sopenharmony_ci
326cdb10c1Sopenharmony_ci/// Gets the name of an LLVM or Clang static library from a path.
336cdb10c1Sopenharmony_cifn get_library_name(path: &Path) -> Option<String> {
346cdb10c1Sopenharmony_ci    path.file_stem().map(|p| {
356cdb10c1Sopenharmony_ci        let string = p.to_string_lossy();
366cdb10c1Sopenharmony_ci        if let Some(name) = string.strip_prefix("lib") {
376cdb10c1Sopenharmony_ci            name.to_owned()
386cdb10c1Sopenharmony_ci        } else {
396cdb10c1Sopenharmony_ci            string.to_string()
406cdb10c1Sopenharmony_ci        }
416cdb10c1Sopenharmony_ci    })
426cdb10c1Sopenharmony_ci}
436cdb10c1Sopenharmony_ci
446cdb10c1Sopenharmony_ci/// Gets the LLVM static libraries required to link to `libclang`.
456cdb10c1Sopenharmony_cifn get_llvm_libraries() -> Vec<String> {
466cdb10c1Sopenharmony_ci    common::run_llvm_config(&["--libs"])
476cdb10c1Sopenharmony_ci        .unwrap()
486cdb10c1Sopenharmony_ci        .split_whitespace()
496cdb10c1Sopenharmony_ci        .filter_map(|p| {
506cdb10c1Sopenharmony_ci            // Depending on the version of `llvm-config` in use, listed
516cdb10c1Sopenharmony_ci            // libraries may be in one of two forms, a full path to the library
526cdb10c1Sopenharmony_ci            // or simply prefixed with `-l`.
536cdb10c1Sopenharmony_ci            if let Some(path) = p.strip_prefix("-l") {
546cdb10c1Sopenharmony_ci                Some(path.into())
556cdb10c1Sopenharmony_ci            } else {
566cdb10c1Sopenharmony_ci                get_library_name(Path::new(p))
576cdb10c1Sopenharmony_ci            }
586cdb10c1Sopenharmony_ci        })
596cdb10c1Sopenharmony_ci        .collect()
606cdb10c1Sopenharmony_ci}
616cdb10c1Sopenharmony_ci
626cdb10c1Sopenharmony_ci/// Gets the Clang static libraries required to link to `libclang`.
636cdb10c1Sopenharmony_cifn get_clang_libraries<P: AsRef<Path>>(directory: P) -> Vec<String> {
646cdb10c1Sopenharmony_ci    // Escape the directory in case it contains characters that have special
656cdb10c1Sopenharmony_ci    // meaning in glob patterns (e.g., `[` or `]`).
666cdb10c1Sopenharmony_ci    let directory = Pattern::escape(directory.as_ref().to_str().unwrap());
676cdb10c1Sopenharmony_ci    let directory = Path::new(&directory);
686cdb10c1Sopenharmony_ci
696cdb10c1Sopenharmony_ci    let pattern = directory.join("libclang*.a").to_str().unwrap().to_owned();
706cdb10c1Sopenharmony_ci    if let Ok(libraries) = glob::glob(&pattern) {
716cdb10c1Sopenharmony_ci        libraries
726cdb10c1Sopenharmony_ci            .filter_map(|l| l.ok().and_then(|l| get_library_name(&l)))
736cdb10c1Sopenharmony_ci            .collect()
746cdb10c1Sopenharmony_ci    } else {
756cdb10c1Sopenharmony_ci        CLANG_LIBRARIES.iter().map(|l| (*l).to_string()).collect()
766cdb10c1Sopenharmony_ci    }
776cdb10c1Sopenharmony_ci}
786cdb10c1Sopenharmony_ci
796cdb10c1Sopenharmony_ci/// Finds a directory containing LLVM and Clang static libraries and returns the
806cdb10c1Sopenharmony_ci/// path to that directory.
816cdb10c1Sopenharmony_cifn find() -> PathBuf {
826cdb10c1Sopenharmony_ci    let name = if cfg!(target_os = "windows") {
836cdb10c1Sopenharmony_ci        "libclang.lib"
846cdb10c1Sopenharmony_ci    } else {
856cdb10c1Sopenharmony_ci        "libclang.a"
866cdb10c1Sopenharmony_ci    };
876cdb10c1Sopenharmony_ci
886cdb10c1Sopenharmony_ci    let files = common::search_libclang_directories(&[name.into()], "LIBCLANG_STATIC_PATH");
896cdb10c1Sopenharmony_ci    if let Some((directory, _)) = files.into_iter().next() {
906cdb10c1Sopenharmony_ci        directory
916cdb10c1Sopenharmony_ci    } else {
926cdb10c1Sopenharmony_ci        panic!("could not find any static libraries");
936cdb10c1Sopenharmony_ci    }
946cdb10c1Sopenharmony_ci}
956cdb10c1Sopenharmony_ci
966cdb10c1Sopenharmony_ci//================================================
976cdb10c1Sopenharmony_ci// Linking
986cdb10c1Sopenharmony_ci//================================================
996cdb10c1Sopenharmony_ci
1006cdb10c1Sopenharmony_ci/// Finds and links to `libclang` static libraries.
1016cdb10c1Sopenharmony_cipub fn link() {
1026cdb10c1Sopenharmony_ci    let cep = common::CommandErrorPrinter::default();
1036cdb10c1Sopenharmony_ci
1046cdb10c1Sopenharmony_ci    let directory = find();
1056cdb10c1Sopenharmony_ci
1066cdb10c1Sopenharmony_ci    // Specify required Clang static libraries.
1076cdb10c1Sopenharmony_ci    println!("cargo:rustc-link-search=native={}", directory.display());
1086cdb10c1Sopenharmony_ci    for library in get_clang_libraries(directory) {
1096cdb10c1Sopenharmony_ci        println!("cargo:rustc-link-lib=static={}", library);
1106cdb10c1Sopenharmony_ci    }
1116cdb10c1Sopenharmony_ci
1126cdb10c1Sopenharmony_ci    // Determine the shared mode used by LLVM.
1136cdb10c1Sopenharmony_ci    let mode = common::run_llvm_config(&["--shared-mode"]).map(|m| m.trim().to_owned());
1146cdb10c1Sopenharmony_ci    let prefix = if mode.map_or(false, |m| m == "static") {
1156cdb10c1Sopenharmony_ci        "static="
1166cdb10c1Sopenharmony_ci    } else {
1176cdb10c1Sopenharmony_ci        ""
1186cdb10c1Sopenharmony_ci    };
1196cdb10c1Sopenharmony_ci
1206cdb10c1Sopenharmony_ci    // Specify required LLVM static libraries.
1216cdb10c1Sopenharmony_ci    println!(
1226cdb10c1Sopenharmony_ci        "cargo:rustc-link-search=native={}",
1236cdb10c1Sopenharmony_ci        common::run_llvm_config(&["--libdir"]).unwrap().trim_end()
1246cdb10c1Sopenharmony_ci    );
1256cdb10c1Sopenharmony_ci    for library in get_llvm_libraries() {
1266cdb10c1Sopenharmony_ci        println!("cargo:rustc-link-lib={}{}", prefix, library);
1276cdb10c1Sopenharmony_ci    }
1286cdb10c1Sopenharmony_ci
1296cdb10c1Sopenharmony_ci    // Specify required system libraries.
1306cdb10c1Sopenharmony_ci    // MSVC doesn't need this, as it tracks dependencies inside `.lib` files.
1316cdb10c1Sopenharmony_ci    if cfg!(target_os = "freebsd") {
1326cdb10c1Sopenharmony_ci        println!("cargo:rustc-flags=-l ffi -l ncursesw -l c++ -l z");
1336cdb10c1Sopenharmony_ci    } else if cfg!(any(target_os = "haiku", target_os = "linux")) {
1346cdb10c1Sopenharmony_ci        println!("cargo:rustc-flags=-l ffi -l ncursesw -l stdc++ -l z");
1356cdb10c1Sopenharmony_ci    } else if cfg!(target_os = "macos") {
1366cdb10c1Sopenharmony_ci        println!("cargo:rustc-flags=-l ffi -l ncurses -l c++ -l z");
1376cdb10c1Sopenharmony_ci    }
1386cdb10c1Sopenharmony_ci
1396cdb10c1Sopenharmony_ci    cep.discard();
1406cdb10c1Sopenharmony_ci}
141