diff options
Diffstat (limited to 'out/common.rs')
-rw-r--r-- | out/common.rs | 272 |
1 files changed, 149 insertions, 123 deletions
diff --git a/out/common.rs b/out/common.rs index 56480df..bc720ca 100644 --- a/out/common.rs +++ b/out/common.rs @@ -1,16 +1,4 @@ -// Copyright 2018 Kyle Mayes -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// SPDX-License-Identifier: Apache-2.0 extern crate glob; @@ -22,87 +10,38 @@ use std::process::Command; use glob::{MatchOptions, Pattern}; -/// `libclang` directory patterns for FreeBSD and Linux. -const DIRECTORIES_LINUX: &[&str] = &[ - "/usr/lib*", - "/usr/lib*/*", - "/usr/lib*/*/*", - "/usr/local/lib*", - "/usr/local/lib*/*", - "/usr/local/lib*/*/*", - "/usr/local/llvm*/lib*", -]; - -/// `libclang` directory patterns for macOS. -const DIRECTORIES_MACOS: &[&str] = &[ - "/usr/local/opt/llvm*/lib", - "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib", - "/Library/Developer/CommandLineTools/usr/lib", - "/usr/local/opt/llvm*/lib/llvm*/lib", -]; - -/// `libclang` directory patterns for Windows. -const DIRECTORIES_WINDOWS: &[&str] = &[ - "C:\\LLVM\\lib", - "C:\\Program Files*\\LLVM\\lib", - "C:\\MSYS*\\MinGW*\\lib", - // LLVM + Clang can be installed as a component of Visual Studio. - // https://github.com/KyleMayes/clang-sys/issues/121 - "C:\\Program Files*\\Microsoft Visual Studio\\*\\BuildTools\\VC\\Tools\\Llvm\\**\\bin", - // Scoop user installation https://scoop.sh/. - // Chocolatey, WinGet and other installers use to the system wide dir listed above - "C:\\Users\\*\\scoop\\apps\\llvm\\current\\bin", -]; +//================================================ +// Commands +//================================================ thread_local! { - /// The errors encountered when attempting to execute console commands. + /// The errors encountered by the build script while executing commands. static COMMAND_ERRORS: RefCell<HashMap<String, Vec<String>>> = RefCell::default(); } -/// Executes the supplied console command, returning the `stdout` output if the -/// command was successfully executed. -fn run_command(name: &str, command: &str, arguments: &[&str]) -> Option<String> { - macro_rules! error { - ($error:expr) => {{ - COMMAND_ERRORS.with(|e| { - e.borrow_mut() - .entry(name.into()) - .or_insert_with(Vec::new) - .push(format!( - "couldn't execute `{} {}` ({})", - command, - arguments.join(" "), - $error, - )) - }); - }}; - } - - let output = match Command::new(command).args(arguments).output() { - Ok(output) => output, - Err(error) => { - error!(format!("error: {}", error)); - return None; - } - }; - - if !output.status.success() { - error!(format!("exit code: {}", output.status)); - return None; - } - - Some(String::from_utf8_lossy(&output.stdout).into_owned()) +/// Adds an error encountered by the build script while executing a command. +fn add_command_error(name: &str, path: &str, arguments: &[&str], message: String) { + COMMAND_ERRORS.with(|e| { + e.borrow_mut() + .entry(name.into()) + .or_insert_with(Vec::new) + .push(format!( + "couldn't execute `{} {}` (path={}) ({})", + name, + arguments.join(" "), + path, + message, + )) + }); } -/// Executes `llvm-config`, returning the `stdout` output if the command was -/// successfully executed. -pub fn run_llvm_config(arguments: &[&str]) -> Option<String> { - let path = env::var("LLVM_CONFIG_PATH").unwrap_or_else(|_| "llvm-config".into()); - run_command("llvm-config", &path, arguments) -} - -/// A struct that prints errors encountered when attempting to execute console -/// commands on drop if not discarded. +/// A struct that prints the errors encountered by the build script while +/// executing commands when dropped (unless explictly discarded). +/// +/// This is handy because we only want to print these errors when the build +/// script fails to link to an instance of `libclang`. For example, if +/// `llvm-config` couldn't be executed but an instance of `libclang` was found +/// anyway we don't want to pollute the build output with irrelevant errors. #[derive(Default)] pub struct CommandErrorPrinter { discard: bool, @@ -152,33 +91,117 @@ impl Drop for CommandErrorPrinter { } } -/// Returns the paths to and the filenames of the files matching the supplied -/// filename patterns in the supplied directory. +/// Executes a command and returns the `stdout` output if the command was +/// successfully executed (errors are added to `COMMAND_ERRORS`). +fn run_command(name: &str, path: &str, arguments: &[&str]) -> Option<String> { + let output = match Command::new(path).args(arguments).output() { + Ok(output) => output, + Err(error) => { + let message = format!("error: {}", error); + add_command_error(name, path, arguments, message); + return None; + } + }; + + if output.status.success() { + Some(String::from_utf8_lossy(&output.stdout).into_owned()) + } else { + let message = format!("exit code: {}", output.status); + add_command_error(name, path, arguments, message); + None + } +} + +/// Executes the `llvm-config` command and returns the `stdout` output if the +/// command was successfully executed (errors are added to `COMMAND_ERRORS`). +pub fn run_llvm_config(arguments: &[&str]) -> Option<String> { + let path = env::var("LLVM_CONFIG_PATH").unwrap_or_else(|_| "llvm-config".into()); + run_command("llvm-config", &path, arguments) +} + +/// Executes the `xcode-select` command and returns the `stdout` output if the +/// command was successfully executed (errors are added to `COMMAND_ERRORS`). +pub fn run_xcode_select(arguments: &[&str]) -> Option<String> { + run_command("xcode-select", "xcode-select", arguments) +} + +//================================================ +// Search Directories +//================================================ + +/// `libclang` directory patterns for Haiku. +const DIRECTORIES_HAIKU: &[&str] = &[ + "/boot/system/lib", + "/boot/system/develop/lib", + "/boot/system/non-packaged/lib", + "/boot/system/non-packaged/develop/lib", + "/boot/home/config/non-packaged/lib", + "/boot/home/config/non-packaged/develop/lib", +]; + +/// `libclang` directory patterns for Linux (and FreeBSD). +const DIRECTORIES_LINUX: &[&str] = &[ + "/usr/lib*", + "/usr/lib*/*", + "/usr/lib*/*/*", + "/usr/local/lib*", + "/usr/local/lib*/*", + "/usr/local/lib*/*/*", + "/usr/local/llvm*/lib*", +]; + +/// `libclang` directory patterns for macOS. +const DIRECTORIES_MACOS: &[&str] = &[ + "/usr/local/opt/llvm*/lib", + "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib", + "/Library/Developer/CommandLineTools/usr/lib", + "/usr/local/opt/llvm*/lib/llvm*/lib", +]; + +/// `libclang` directory patterns for Windows. +const DIRECTORIES_WINDOWS: &[&str] = &[ + "C:\\LLVM\\lib", + "C:\\Program Files*\\LLVM\\lib", + "C:\\MSYS*\\MinGW*\\lib", + // LLVM + Clang can be installed as a component of Visual Studio. + // https://github.com/KyleMayes/clang-sys/issues/121 + "C:\\Program Files*\\Microsoft Visual Studio\\*\\BuildTools\\VC\\Tools\\Llvm\\**\\bin", + // LLVM + Clang can be installed using Scoop (https://scoop.sh). + // Other Windows package managers install LLVM + Clang to previously listed + // system-wide directories. + "C:\\Users\\*\\scoop\\apps\\llvm\\current\\bin", +]; + +//================================================ +// Searching +//================================================ + +/// Finds the files in a directory that match one or more filename glob patterns +/// and returns the paths to and filenames of those files. fn search_directory(directory: &Path, filenames: &[String]) -> Vec<(PathBuf, String)> { - // Escape the directory in case it contains characters that have special - // meaning in glob patterns (e.g., `[` or `]`). + // Escape the specified directory in case it contains characters that have + // special meaning in glob patterns (e.g., `[` or `]`). let directory = Pattern::escape(directory.to_str().unwrap()); let directory = Path::new(&directory); - // Join the directory to the filename patterns to obtain the path patterns. + // Join the escaped directory to the filename glob patterns to obtain + // complete glob patterns for the files being searched for. let paths = filenames .iter() .map(|f| directory.join(f).to_str().unwrap().to_owned()); - // Prevent wildcards from matching path separators. + // Prevent wildcards from matching path separators to ensure that the search + // is limited to the specified directory. let mut options = MatchOptions::new(); options.require_literal_separator = true; paths - .flat_map(|p| { - if let Ok(paths) = glob::glob_with(&p, options) { - paths.filter_map(Result::ok).collect() - } else { - vec![] - } - }) + .map(|p| glob::glob_with(&p, options)) + .filter_map(Result::ok) + .flatten() .filter_map(|p| { - let filename = p.file_name()?.to_str().unwrap(); + let path = p.ok()?; + let filename = path.file_name()?.to_str().unwrap(); // The `libclang_shared` library has been renamed to `libclang-cpp` // in Clang 10. This can cause instances of this library (e.g., @@ -193,9 +216,9 @@ fn search_directory(directory: &Path, filenames: &[String]) -> Vec<(PathBuf, Str .collect::<Vec<_>>() } -/// Returns the paths to and the filenames of the files matching the supplied -/// filename patterns in the supplied directory, checking any relevant sibling -/// directories. +/// Finds the files in a directory (and any relevant sibling directories) that +/// match one or more filename glob patterns and returns the paths to and +/// filenames of those files. fn search_directories(directory: &Path, filenames: &[String]) -> Vec<(PathBuf, String)> { let mut results = search_directory(directory, filenames); @@ -212,54 +235,57 @@ fn search_directories(directory: &Path, filenames: &[String]) -> Vec<(PathBuf, S results } -/// Returns the paths to and the filenames of the `libclang` static or dynamic -/// libraries matching the supplied filename patterns. -pub fn search_libclang_directories(files: &[String], variable: &str) -> Vec<(PathBuf, String)> { - // Use the path provided by the relevant environment variable. +/// Finds the `libclang` static or dynamic libraries matching one or more +/// filename glob patterns and returns the paths to and filenames of those files. +pub fn search_libclang_directories(filenames: &[String], variable: &str) -> Vec<(PathBuf, String)> { + // Search only the path indicated by the relevant environment variable + // (e.g., `LIBCLANG_PATH`) if it is set. if let Ok(path) = env::var(variable).map(|d| Path::new(&d).to_path_buf()) { - // Check if the path is referring to a matching file already. + // Check if the path is a matching file. if let Some(parent) = path.parent() { let filename = path.file_name().unwrap().to_str().unwrap(); - let libraries = search_directories(parent, files); + let libraries = search_directories(parent, filenames); if libraries.iter().any(|(_, f)| f == filename) { return vec![(parent.into(), filename.into())]; } } - return search_directories(&path, files); + // Check if the path is directory containing a matching file. + return search_directories(&path, filenames); } let mut found = vec![]; - // Search the `bin` and `lib` directories in directory provided by + // Search the `bin` and `lib` directories in the directory returned by // `llvm-config --prefix`. if let Some(output) = run_llvm_config(&["--prefix"]) { let directory = Path::new(output.lines().next().unwrap()).to_path_buf(); - found.extend(search_directories(&directory.join("bin"), files)); - found.extend(search_directories(&directory.join("lib"), files)); - found.extend(search_directories(&directory.join("lib64"), files)); + found.extend(search_directories(&directory.join("bin"), filenames)); + found.extend(search_directories(&directory.join("lib"), filenames)); + found.extend(search_directories(&directory.join("lib64"), filenames)); } - // Search the toolchain directory in the directory provided by + // Search the toolchain directory in the directory returned by // `xcode-select --print-path`. if cfg!(target_os = "macos") { - if let Some(output) = run_command("xcode-select", "xcode-select", &["--print-path"]) { + if let Some(output) = run_xcode_select(&["--print-path"]) { let directory = Path::new(output.lines().next().unwrap()).to_path_buf(); let directory = directory.join("Toolchains/XcodeDefault.xctoolchain/usr/lib"); - found.extend(search_directories(&directory, files)); + found.extend(search_directories(&directory, filenames)); } } - // Search the directories provided by the `LD_LIBRARY_PATH` environment - // variable. + // Search the directories in the `LD_LIBRARY_PATH` environment variable. if let Ok(path) = env::var("LD_LIBRARY_PATH") { for directory in env::split_paths(&path) { - found.extend(search_directories(&directory, files)); + found.extend(search_directories(&directory, filenames)); } } // Determine the `libclang` directory patterns. - let directories = if cfg!(any(target_os = "freebsd", target_os = "linux")) { + let directories = if cfg!(target_os = "haiku") { + DIRECTORIES_HAIKU + } else if cfg!(any(target_os = "linux", target_os = "freebsd")) { DIRECTORIES_LINUX } else if cfg!(target_os = "macos") { DIRECTORIES_MACOS @@ -276,7 +302,7 @@ pub fn search_libclang_directories(files: &[String], variable: &str) -> Vec<(Pat for directory in directories.iter().rev() { if let Ok(directories) = glob::glob_with(directory, options) { for directory in directories.filter_map(Result::ok).filter(|p| p.is_dir()) { - found.extend(search_directories(&directory, files)); + found.extend(search_directories(&directory, filenames)); } } } |