aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.cargo_vcs_info.json2
-rw-r--r--.github/workflows/ci.yml7
-rw-r--r--.github/workflows/ssh.yml2
-rw-r--r--.gitignore1
-rw-r--r--Android.bp3
-rw-r--r--CHANGELOG.md37
-rw-r--r--Cargo.toml16
-rw-r--r--Cargo.toml.orig13
-rw-r--r--METADATA25
-rw-r--r--README.md4
-rw-r--r--build.rs5
-rw-r--r--build/common.rs110
-rw-r--r--build/dynamic.rs23
-rw-r--r--build/macros.rs38
-rw-r--r--build/static.rs4
-rw-r--r--out/common.rs110
-rw-r--r--out/dynamic.rs23
-rw-r--r--out/macros.rs38
-rw-r--r--src/lib.rs164
-rw-r--r--src/link.rs66
-rw-r--r--src/support.rs6
-rw-r--r--tests/build.rs281
22 files changed, 834 insertions, 144 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
index d04b5e7..26dff97 100644
--- a/.cargo_vcs_info.json
+++ b/.cargo_vcs_info.json
@@ -1,6 +1,6 @@
{
"git": {
- "sha1": "1b9b8a71748aebd26180f1f16728ef111afce5ad"
+ "sha1": "b1854129fa7ae4b6d521feed5eca3eb6816c9342"
},
"path_in_vcs": ""
} \ No newline at end of file
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 646f4c2..bbcc176 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -16,7 +16,7 @@ jobs:
matrix:
os: [macos-latest, ubuntu-latest, windows-latest]
clang: [["14.0", "clang_14_0"]]
- rust: ["1.40.0"]
+ rust: ["1.51.0"]
steps:
- name: Checkout Repository
uses: actions/checkout@v2
@@ -42,3 +42,8 @@ jobs:
with:
command: test
args: --verbose --features "${{ matrix.clang[1] }} runtime" -- --nocapture
+ - name: Cargo Run (bindgen-test)
+ uses: actions-rs/cargo@v1
+ with:
+ command: run
+ args: --manifest-path bindgen-test/Cargo.toml
diff --git a/.github/workflows/ssh.yml b/.github/workflows/ssh.yml
index 5dfc251..db22ef4 100644
--- a/.github/workflows/ssh.yml
+++ b/.github/workflows/ssh.yml
@@ -16,7 +16,7 @@ jobs:
matrix:
os: [macos-latest, ubuntu-latest, windows-latest]
clang: [["13.0", "clang_13_0"]]
- rust: ["1.40.0"]
+ rust: ["1.51.0"]
steps:
- name: Checkout Repository
uses: actions/checkout@v2
diff --git a/.gitignore b/.gitignore
index 09b05f5..654b48a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
.docs/
+bindgen-test/target/
target/
Cargo.lock
diff --git a/Android.bp b/Android.bp
index def44c3..4f3b6ae 100644
--- a/Android.bp
+++ b/Android.bp
@@ -27,6 +27,7 @@ genrule {
out: [
"common.rs",
"dynamic.rs",
+ "macros.rs",
],
}
@@ -34,7 +35,7 @@ rust_library_host {
name: "libclang_sys",
crate_name: "clang_sys",
cargo_env_compat: true,
- cargo_pkg_version: "1.4.0",
+ cargo_pkg_version: "1.7.0",
srcs: [
"src/lib.rs",
":copy_clang-sys_build_out",
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d697220..d08b4bc 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,40 @@
+## [1.7.0] - 2023-12-31
+
+### Added
+- Added support for `clang` 17.0.x
+
+## [1.6.1] - 2023-03-29
+
+### Fixed
+- Improved error message when calling a `libclang` function that is not supported by the loaded `libclang` instance (https://github.com/rust-lang/rust-bindgen/issues/2446)
+
+## [1.6.0] - 2023-02-18
+
+### Changed
+- MinGW directories are not searched for `libclang` instances on Windows when
+compiling for an MSVC target
+- Bumped minimum supported Rust version (MSRV) to 1.51.0
+- Changed Windows search directory preferences (`libclang` instances from
+Visual Studio installs are now the lowest priority rather than the second
+highest)
+
+## ~~[1.5.1] - 2023-02-05~~ (YANKED)
+
+### Changed
+- MinGW directories are not searched for `libclang` instances on Windows when
+compiling for an MSVC target
+
+## ~~[1.5.0] - 2023-02-05~~ (YANKED)
+
+### Changed
+- Bumped minimum supported Rust version (MSRV) to 1.51.0
+- Changed Windows search directory preferences (`libclang` instances from
+Visual Studio installs are now the lowest priority rather than the second
+highest)
+
+### Added
+- Added additional support for `clang` 16.0.x
+
## [1.4.0] - 2022-09-22
### Changed
diff --git a/Cargo.toml b/Cargo.toml
index a0d9cbb..49e15ce 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -11,7 +11,7 @@
[package]
name = "clang-sys"
-version = "1.4.0"
+version = "1.7.0"
authors = ["Kyle Mayes <kyle@mayeses.com>"]
build = "build.rs"
links = "clang"
@@ -23,7 +23,7 @@ repository = "https://github.com/KyleMayes/clang-sys"
[package.metadata.docs.rs]
features = [
- "clang_16_0",
+ "clang_17_0",
"runtime",
]
@@ -35,9 +35,18 @@ version = "0.2.39"
default-features = false
[dependencies.libloading]
-version = "0.7"
+version = "0.8"
optional = true
+[dev-dependencies.glob]
+version = "0.3"
+
+[dev-dependencies.serial_test]
+version = "1"
+
+[dev-dependencies.tempfile]
+version = "3"
+
[build-dependencies.glob]
version = "0.3"
@@ -49,6 +58,7 @@ clang_13_0 = ["clang_12_0"]
clang_14_0 = ["clang_13_0"]
clang_15_0 = ["clang_14_0"]
clang_16_0 = ["clang_15_0"]
+clang_17_0 = ["clang_16_0"]
clang_3_5 = []
clang_3_6 = ["clang_3_5"]
clang_3_7 = ["clang_3_6"]
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
index 1041981..c70b380 100644
--- a/Cargo.toml.orig
+++ b/Cargo.toml.orig
@@ -3,7 +3,7 @@
name = "clang-sys"
authors = ["Kyle Mayes <kyle@mayeses.com>"]
-version = "1.4.0"
+version = "1.7.0"
readme = "README.md"
license = "Apache-2.0"
@@ -36,6 +36,7 @@ clang_13_0 = ["clang_12_0"]
clang_14_0 = ["clang_13_0"]
clang_15_0 = ["clang_14_0"]
clang_16_0 = ["clang_15_0"]
+clang_17_0 = ["clang_16_0"]
runtime = ["libloading"]
static = []
@@ -44,12 +45,18 @@ static = []
glob = "0.3"
libc = { version = "0.2.39", default-features = false }
-libloading = { version = "0.7", optional = true }
+libloading = { version = "0.8", optional = true }
[build-dependencies]
glob = "0.3"
+[dev-dependencies]
+
+glob = "0.3"
+serial_test = "1"
+tempfile = "3"
+
[package.metadata.docs.rs]
-features = ["clang_16_0", "runtime"]
+features = ["clang_17_0", "runtime"]
diff --git a/METADATA b/METADATA
index ac49596..839736a 100644
--- a/METADATA
+++ b/METADATA
@@ -1,23 +1,20 @@
# This project was upgraded with external_updater.
-# Usage: tools/external_updater/updater.sh update rust/crates/clang-sys
-# For more info, check https://cs.android.com/android/platform/superproject/+/master:tools/external_updater/README.md
+# Usage: tools/external_updater/updater.sh update external/rust/crates/clang-sys
+# For more info, check https://cs.android.com/android/platform/superproject/+/main:tools/external_updater/README.md
name: "clang-sys"
description: "Rust bindings for libclang."
third_party {
- url {
- type: HOMEPAGE
- value: "https://crates.io/crates/clang-sys"
- }
- url {
- type: ARCHIVE
- value: "https://static.crates.io/crates/clang-sys/clang-sys-1.4.0.crate"
- }
- version: "1.4.0"
license_type: NOTICE
last_upgrade_date {
- year: 2022
- month: 12
- day: 8
+ year: 2024
+ month: 5
+ day: 10
+ }
+ homepage: "https://crates.io/crates/clang-sys"
+ identifier {
+ type: "Archive"
+ value: "https://static.crates.io/crates/clang-sys/clang-sys-1.7.0.crate"
+ version: "1.7.0"
}
}
diff --git a/README.md b/README.md
index ed5f52e..40252db 100644
--- a/README.md
+++ b/README.md
@@ -2,8 +2,8 @@
[![Crate](https://img.shields.io/crates/v/clang-sys.svg)](https://crates.io/crates/clang-sys)
[![Documentation](https://docs.rs/clang-sys/badge.svg)](https://docs.rs/clang-sys)
-[![CI](https://img.shields.io/github/workflow/status/KyleMayes/clang-sys/CI/master)](https://github.com/KyleMayes/vulkanalia/actions?query=workflow%3ACI)
-![MSRV](https://img.shields.io/badge/MSRV-1.40.0-blue)
+[![CI](https://img.shields.io/github/actions/workflow/status/KyleMayes/clang-sys/ci.yml?branch=master)](https://github.com/KyleMayes/clang-sys/actions?query=workflow%3ACI)
+![MSRV](https://img.shields.io/badge/MSRV-1.51.0-blue)
Rust bindings for `libclang`.
diff --git a/build.rs b/build.rs
index bfaaff6..6b15126 100644
--- a/build.rs
+++ b/build.rs
@@ -19,6 +19,10 @@ extern crate glob;
use std::path::Path;
+#[macro_use]
+#[path = "build/macros.rs"]
+pub mod macros;
+
#[path = "build/common.rs"]
pub mod common;
#[path = "build/dynamic.rs"]
@@ -54,6 +58,7 @@ fn main() {
}
let out = env::var("OUT_DIR").unwrap();
+ copy("build/macros.rs", &Path::new(&out).join("macros.rs"));
copy("build/common.rs", &Path::new(&out).join("common.rs"));
copy("build/dynamic.rs", &Path::new(&out).join("dynamic.rs"));
}
diff --git a/build/common.rs b/build/common.rs
index 735d5da..3005a8a 100644
--- a/build/common.rs
+++ b/build/common.rs
@@ -91,9 +91,19 @@ impl Drop for CommandErrorPrinter {
}
}
+#[cfg(test)]
+pub static RUN_COMMAND_MOCK: std::sync::Mutex<
+ Option<Box<dyn Fn(&str, &str, &[&str]) -> Option<String> + Send + Sync + 'static>>,
+> = std::sync::Mutex::new(None);
+
/// 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> {
+ #[cfg(test)]
+ if let Some(command) = &*RUN_COMMAND_MOCK.lock().unwrap() {
+ return command(name, path, arguments);
+ }
+
let output = match Command::new(path).args(arguments).output() {
Ok(output) => output,
Err(error) => {
@@ -128,54 +138,64 @@ pub fn run_xcode_select(arguments: &[&str]) -> Option<String> {
//================================================
// Search Directories
//================================================
+// These search directories are listed in order of
+// preference, so if multiple `libclang` instances
+// are found when searching matching directories,
+// the `libclang` instances from earlier
+// directories will be preferred (though version
+// takes precedence over location).
+//================================================
/// `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",
+ "/boot/home/config/non-packaged/lib",
+ "/boot/system/non-packaged/develop/lib",
+ "/boot/system/non-packaged/lib",
+ "/boot/system/develop/lib",
+ "/boot/system/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*",
+ "/usr/local/lib*/*/*",
+ "/usr/local/lib*/*",
+ "/usr/local/lib*",
+ "/usr/lib*/*/*",
+ "/usr/lib*/*",
+ "/usr/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",
+ "/Library/Developer/CommandLineTools/usr/lib",
+ "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib",
+ "/usr/local/opt/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",
+///
+/// The boolean indicates whether the directory pattern should be used when
+/// compiling for an MSVC target environment.
+const DIRECTORIES_WINDOWS: &[(&str, bool)] = &[
// LLVM + Clang can be installed using Scoop (https://scoop.sh).
- // Other Windows package managers install LLVM + Clang to previously listed
+ // Other Windows package managers install LLVM + Clang to other listed
// system-wide directories.
- "C:\\Users\\*\\scoop\\apps\\llvm\\current\\bin",
+ ("C:\\Users\\*\\scoop\\apps\\llvm\\current\\lib", true),
+ ("C:\\MSYS*\\MinGW*\\lib", false),
+ ("C:\\Program Files*\\LLVM\\lib", true),
+ ("C:\\LLVM\\lib", true),
+ // 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\\**\\lib", true),
];
/// `libclang` directory patterns for illumos
const DIRECTORIES_ILLUMOS: &[&str] = &[
- "/opt/ooce/clang-*/lib",
"/opt/ooce/llvm-*/lib",
+ "/opt/ooce/clang-*/lib",
];
//================================================
@@ -233,7 +253,7 @@ fn search_directories(directory: &Path, filenames: &[String]) -> Vec<(PathBuf, S
// keep things consistent with other platforms, only LLVM `lib` directories
// are included in the backup search directory globs so we need to search
// the LLVM `bin` directory here.
- if cfg!(target_os = "windows") && directory.ends_with("lib") {
+ if target_os!("windows") && directory.ends_with("lib") {
let sibling = directory.parent().unwrap().join("bin");
results.extend(search_directory(&sibling, filenames).into_iter());
}
@@ -273,7 +293,7 @@ pub fn search_libclang_directories(filenames: &[String], variable: &str) -> Vec<
// Search the toolchain directory in the directory returned by
// `xcode-select --print-path`.
- if cfg!(target_os = "macos") {
+ if target_os!("macos") {
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");
@@ -289,25 +309,41 @@ pub fn search_libclang_directories(filenames: &[String], variable: &str) -> Vec<
}
// Determine the `libclang` directory patterns.
- 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
- } else if cfg!(target_os = "windows") {
+ let directories: Vec<&str> = if target_os!("haiku") {
+ DIRECTORIES_HAIKU.into()
+ } else if target_os!("linux") || target_os!("freebsd") {
+ DIRECTORIES_LINUX.into()
+ } else if target_os!("macos") {
+ DIRECTORIES_MACOS.into()
+ } else if target_os!("windows") {
+ let msvc = target_env!("msvc");
DIRECTORIES_WINDOWS
- } else if cfg!(target_os = "illumos") {
- DIRECTORIES_ILLUMOS
+ .iter()
+ .filter(|d| d.1 || !msvc)
+ .map(|d| d.0)
+ .collect()
+ } else if target_os!("illumos") {
+ DIRECTORIES_ILLUMOS.into()
+ } else {
+ vec![]
+ };
+
+ // We use temporary directories when testing the build script so we'll
+ // remove the prefixes that make the directories absolute.
+ let directories = if test!() {
+ directories
+ .iter()
+ .map(|d| d.strip_prefix('/').or_else(|| d.strip_prefix("C:\\")).unwrap_or(d))
+ .collect::<Vec<_>>()
} else {
- &[]
+ directories
};
// Search the directories provided by the `libclang` directory patterns.
let mut options = MatchOptions::new();
options.case_sensitive = false;
options.require_literal_separator = true;
- for directory in directories.iter().rev() {
+ for directory in directories.iter() {
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, filenames));
diff --git a/build/dynamic.rs b/build/dynamic.rs
index 39247c8..25e1c18 100644
--- a/build/dynamic.rs
+++ b/build/dynamic.rs
@@ -50,26 +50,26 @@ fn parse_pe_header(path: &Path) -> io::Result<u16> {
/// Checks that a `libclang` shared library matches the target platform.
fn validate_library(path: &Path) -> Result<(), String> {
- if cfg!(any(target_os = "linux", target_os = "freebsd")) {
+ if target_os!("linux") || target_os!("freebsd") {
let class = parse_elf_header(path).map_err(|e| e.to_string())?;
- if cfg!(target_pointer_width = "32") && class != 1 {
+ if target_pointer_width!("32") && class != 1 {
return Err("invalid ELF class (64-bit)".into());
}
- if cfg!(target_pointer_width = "64") && class != 2 {
+ if target_pointer_width!("64") && class != 2 {
return Err("invalid ELF class (32-bit)".into());
}
Ok(())
- } else if cfg!(target_os = "windows") {
+ } else if target_os!("windows") {
let magic = parse_pe_header(path).map_err(|e| e.to_string())?;
- if cfg!(target_pointer_width = "32") && magic != 267 {
+ if target_pointer_width!("32") && magic != 267 {
return Err("invalid DLL (64-bit)".into());
}
- if cfg!(target_pointer_width = "64") && magic != 523 {
+ if target_pointer_width!("64") && magic != 523 {
return Err("invalid DLL (32-bit)".into());
}
@@ -105,7 +105,7 @@ fn search_libclang_directories(runtime: bool) -> Result<Vec<(PathBuf, String, Ve
env::consts::DLL_SUFFIX
)];
- if cfg!(target_os = "linux") {
+ if target_os!("linux") {
// Some Linux distributions don't create a `libclang.so` symlink, so we
// need to look for versioned files (e.g., `libclang-3.9.so`).
files.push("libclang-*.so".into());
@@ -121,19 +121,14 @@ fn search_libclang_directories(runtime: bool) -> Result<Vec<(PathBuf, String, Ve
}
}
- if cfg!(any(
- target_os = "freebsd",
- target_os = "haiku",
- target_os = "netbsd",
- target_os = "openbsd",
- )) {
+ if target_os!("freebsd") || target_os!("haiku") || target_os!("netbsd") || target_os!("openbsd") {
// Some BSD distributions don't create a `libclang.so` symlink either,
// but use a different naming scheme for versioned files (e.g.,
// `libclang.so.7.0`).
files.push("libclang.so.*".into());
}
- if cfg!(target_os = "windows") {
+ if target_os!("windows") {
// The official LLVM build uses `libclang.dll` on Windows instead of
// `clang.dll`. However, unofficial builds such as MinGW use `clang.dll`.
files.push("libclang.dll".into());
diff --git a/build/macros.rs b/build/macros.rs
new file mode 100644
index 0000000..811c7c3
--- /dev/null
+++ b/build/macros.rs
@@ -0,0 +1,38 @@
+// SPDX-License-Identifier: Apache-2.0
+
+macro_rules! test {
+ () => (cfg!(test) && ::std::env::var("_CLANG_SYS_TEST").is_ok());
+}
+
+macro_rules! target_os {
+ ($os:expr) => {
+ if cfg!(test) && ::std::env::var("_CLANG_SYS_TEST").is_ok() {
+ let var = ::std::env::var("_CLANG_SYS_TEST_OS");
+ var.map_or(false, |v| v == $os)
+ } else {
+ cfg!(target_os = $os)
+ }
+ };
+}
+
+macro_rules! target_pointer_width {
+ ($pointer_width:expr) => {
+ if cfg!(test) && ::std::env::var("_CLANG_SYS_TEST").is_ok() {
+ let var = ::std::env::var("_CLANG_SYS_TEST_POINTER_WIDTH");
+ var.map_or(false, |v| v == $pointer_width)
+ } else {
+ cfg!(target_pointer_width = $pointer_width)
+ }
+ };
+}
+
+macro_rules! target_env {
+ ($env:expr) => {
+ if cfg!(test) && ::std::env::var("_CLANG_SYS_TEST").is_ok() {
+ let var = ::std::env::var("_CLANG_SYS_TEST_ENV");
+ var.map_or(false, |v| v == $env)
+ } else {
+ cfg!(target_env = $env)
+ }
+ };
+}
diff --git a/build/static.rs b/build/static.rs
index 6af914f..1123189 100644
--- a/build/static.rs
+++ b/build/static.rs
@@ -6,7 +6,7 @@ use std::path::{Path, PathBuf};
use glob::Pattern;
-use common;
+use super::common;
//================================================
// Searching
@@ -79,7 +79,7 @@ fn get_clang_libraries<P: AsRef<Path>>(directory: P) -> Vec<String> {
/// Finds a directory containing LLVM and Clang static libraries and returns the
/// path to that directory.
fn find() -> PathBuf {
- let name = if cfg!(target_os = "windows") {
+ let name = if target_os!("windows") {
"libclang.lib"
} else {
"libclang.a"
diff --git a/out/common.rs b/out/common.rs
index 735d5da..3005a8a 100644
--- a/out/common.rs
+++ b/out/common.rs
@@ -91,9 +91,19 @@ impl Drop for CommandErrorPrinter {
}
}
+#[cfg(test)]
+pub static RUN_COMMAND_MOCK: std::sync::Mutex<
+ Option<Box<dyn Fn(&str, &str, &[&str]) -> Option<String> + Send + Sync + 'static>>,
+> = std::sync::Mutex::new(None);
+
/// 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> {
+ #[cfg(test)]
+ if let Some(command) = &*RUN_COMMAND_MOCK.lock().unwrap() {
+ return command(name, path, arguments);
+ }
+
let output = match Command::new(path).args(arguments).output() {
Ok(output) => output,
Err(error) => {
@@ -128,54 +138,64 @@ pub fn run_xcode_select(arguments: &[&str]) -> Option<String> {
//================================================
// Search Directories
//================================================
+// These search directories are listed in order of
+// preference, so if multiple `libclang` instances
+// are found when searching matching directories,
+// the `libclang` instances from earlier
+// directories will be preferred (though version
+// takes precedence over location).
+//================================================
/// `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",
+ "/boot/home/config/non-packaged/lib",
+ "/boot/system/non-packaged/develop/lib",
+ "/boot/system/non-packaged/lib",
+ "/boot/system/develop/lib",
+ "/boot/system/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*",
+ "/usr/local/lib*/*/*",
+ "/usr/local/lib*/*",
+ "/usr/local/lib*",
+ "/usr/lib*/*/*",
+ "/usr/lib*/*",
+ "/usr/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",
+ "/Library/Developer/CommandLineTools/usr/lib",
+ "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib",
+ "/usr/local/opt/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",
+///
+/// The boolean indicates whether the directory pattern should be used when
+/// compiling for an MSVC target environment.
+const DIRECTORIES_WINDOWS: &[(&str, bool)] = &[
// LLVM + Clang can be installed using Scoop (https://scoop.sh).
- // Other Windows package managers install LLVM + Clang to previously listed
+ // Other Windows package managers install LLVM + Clang to other listed
// system-wide directories.
- "C:\\Users\\*\\scoop\\apps\\llvm\\current\\bin",
+ ("C:\\Users\\*\\scoop\\apps\\llvm\\current\\lib", true),
+ ("C:\\MSYS*\\MinGW*\\lib", false),
+ ("C:\\Program Files*\\LLVM\\lib", true),
+ ("C:\\LLVM\\lib", true),
+ // 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\\**\\lib", true),
];
/// `libclang` directory patterns for illumos
const DIRECTORIES_ILLUMOS: &[&str] = &[
- "/opt/ooce/clang-*/lib",
"/opt/ooce/llvm-*/lib",
+ "/opt/ooce/clang-*/lib",
];
//================================================
@@ -233,7 +253,7 @@ fn search_directories(directory: &Path, filenames: &[String]) -> Vec<(PathBuf, S
// keep things consistent with other platforms, only LLVM `lib` directories
// are included in the backup search directory globs so we need to search
// the LLVM `bin` directory here.
- if cfg!(target_os = "windows") && directory.ends_with("lib") {
+ if target_os!("windows") && directory.ends_with("lib") {
let sibling = directory.parent().unwrap().join("bin");
results.extend(search_directory(&sibling, filenames).into_iter());
}
@@ -273,7 +293,7 @@ pub fn search_libclang_directories(filenames: &[String], variable: &str) -> Vec<
// Search the toolchain directory in the directory returned by
// `xcode-select --print-path`.
- if cfg!(target_os = "macos") {
+ if target_os!("macos") {
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");
@@ -289,25 +309,41 @@ pub fn search_libclang_directories(filenames: &[String], variable: &str) -> Vec<
}
// Determine the `libclang` directory patterns.
- 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
- } else if cfg!(target_os = "windows") {
+ let directories: Vec<&str> = if target_os!("haiku") {
+ DIRECTORIES_HAIKU.into()
+ } else if target_os!("linux") || target_os!("freebsd") {
+ DIRECTORIES_LINUX.into()
+ } else if target_os!("macos") {
+ DIRECTORIES_MACOS.into()
+ } else if target_os!("windows") {
+ let msvc = target_env!("msvc");
DIRECTORIES_WINDOWS
- } else if cfg!(target_os = "illumos") {
- DIRECTORIES_ILLUMOS
+ .iter()
+ .filter(|d| d.1 || !msvc)
+ .map(|d| d.0)
+ .collect()
+ } else if target_os!("illumos") {
+ DIRECTORIES_ILLUMOS.into()
+ } else {
+ vec![]
+ };
+
+ // We use temporary directories when testing the build script so we'll
+ // remove the prefixes that make the directories absolute.
+ let directories = if test!() {
+ directories
+ .iter()
+ .map(|d| d.strip_prefix('/').or_else(|| d.strip_prefix("C:\\")).unwrap_or(d))
+ .collect::<Vec<_>>()
} else {
- &[]
+ directories
};
// Search the directories provided by the `libclang` directory patterns.
let mut options = MatchOptions::new();
options.case_sensitive = false;
options.require_literal_separator = true;
- for directory in directories.iter().rev() {
+ for directory in directories.iter() {
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, filenames));
diff --git a/out/dynamic.rs b/out/dynamic.rs
index 39247c8..25e1c18 100644
--- a/out/dynamic.rs
+++ b/out/dynamic.rs
@@ -50,26 +50,26 @@ fn parse_pe_header(path: &Path) -> io::Result<u16> {
/// Checks that a `libclang` shared library matches the target platform.
fn validate_library(path: &Path) -> Result<(), String> {
- if cfg!(any(target_os = "linux", target_os = "freebsd")) {
+ if target_os!("linux") || target_os!("freebsd") {
let class = parse_elf_header(path).map_err(|e| e.to_string())?;
- if cfg!(target_pointer_width = "32") && class != 1 {
+ if target_pointer_width!("32") && class != 1 {
return Err("invalid ELF class (64-bit)".into());
}
- if cfg!(target_pointer_width = "64") && class != 2 {
+ if target_pointer_width!("64") && class != 2 {
return Err("invalid ELF class (32-bit)".into());
}
Ok(())
- } else if cfg!(target_os = "windows") {
+ } else if target_os!("windows") {
let magic = parse_pe_header(path).map_err(|e| e.to_string())?;
- if cfg!(target_pointer_width = "32") && magic != 267 {
+ if target_pointer_width!("32") && magic != 267 {
return Err("invalid DLL (64-bit)".into());
}
- if cfg!(target_pointer_width = "64") && magic != 523 {
+ if target_pointer_width!("64") && magic != 523 {
return Err("invalid DLL (32-bit)".into());
}
@@ -105,7 +105,7 @@ fn search_libclang_directories(runtime: bool) -> Result<Vec<(PathBuf, String, Ve
env::consts::DLL_SUFFIX
)];
- if cfg!(target_os = "linux") {
+ if target_os!("linux") {
// Some Linux distributions don't create a `libclang.so` symlink, so we
// need to look for versioned files (e.g., `libclang-3.9.so`).
files.push("libclang-*.so".into());
@@ -121,19 +121,14 @@ fn search_libclang_directories(runtime: bool) -> Result<Vec<(PathBuf, String, Ve
}
}
- if cfg!(any(
- target_os = "freebsd",
- target_os = "haiku",
- target_os = "netbsd",
- target_os = "openbsd",
- )) {
+ if target_os!("freebsd") || target_os!("haiku") || target_os!("netbsd") || target_os!("openbsd") {
// Some BSD distributions don't create a `libclang.so` symlink either,
// but use a different naming scheme for versioned files (e.g.,
// `libclang.so.7.0`).
files.push("libclang.so.*".into());
}
- if cfg!(target_os = "windows") {
+ if target_os!("windows") {
// The official LLVM build uses `libclang.dll` on Windows instead of
// `clang.dll`. However, unofficial builds such as MinGW use `clang.dll`.
files.push("libclang.dll".into());
diff --git a/out/macros.rs b/out/macros.rs
new file mode 100644
index 0000000..811c7c3
--- /dev/null
+++ b/out/macros.rs
@@ -0,0 +1,38 @@
+// SPDX-License-Identifier: Apache-2.0
+
+macro_rules! test {
+ () => (cfg!(test) && ::std::env::var("_CLANG_SYS_TEST").is_ok());
+}
+
+macro_rules! target_os {
+ ($os:expr) => {
+ if cfg!(test) && ::std::env::var("_CLANG_SYS_TEST").is_ok() {
+ let var = ::std::env::var("_CLANG_SYS_TEST_OS");
+ var.map_or(false, |v| v == $os)
+ } else {
+ cfg!(target_os = $os)
+ }
+ };
+}
+
+macro_rules! target_pointer_width {
+ ($pointer_width:expr) => {
+ if cfg!(test) && ::std::env::var("_CLANG_SYS_TEST").is_ok() {
+ let var = ::std::env::var("_CLANG_SYS_TEST_POINTER_WIDTH");
+ var.map_or(false, |v| v == $pointer_width)
+ } else {
+ cfg!(target_pointer_width = $pointer_width)
+ }
+ };
+}
+
+macro_rules! target_env {
+ ($env:expr) => {
+ if cfg!(test) && ::std::env::var("_CLANG_SYS_TEST").is_ok() {
+ let var = ::std::env::var("_CLANG_SYS_TEST_ENV");
+ var.map_or(false, |v| v == $env)
+ } else {
+ cfg!(target_env = $env)
+ }
+ };
+}
diff --git a/src/lib.rs b/src/lib.rs
index c101ce6..31e2e96 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -48,6 +48,20 @@ pub type CXInclusionVisitor = extern "C" fn(CXFile, *mut CXSourceLocation, c_uin
/// Defines a C enum as a series of constants.
macro_rules! cenum {
+ (#[repr($ty:ty)] $(#[$meta:meta])* enum $name:ident {
+ $($(#[$vmeta:meta])* const $variant:ident = $value:expr), +,
+ }) => (
+ pub type $name = $ty;
+
+ $($(#[$vmeta])* pub const $variant: $name = $value;)+
+ );
+ (#[repr($ty:ty)] $(#[$meta:meta])* enum $name:ident {
+ $($(#[$vmeta:meta])* const $variant:ident = $value:expr); +;
+ }) => (
+ pub type $name = $ty;
+
+ $($(#[$vmeta])* pub const $variant: $name = $value;)+
+ );
($(#[$meta:meta])* enum $name:ident {
$($(#[$vmeta:meta])* const $variant:ident = $value:expr), +,
}) => (
@@ -98,6 +112,47 @@ cenum! {
}
cenum! {
+ /// Only available on `libclang` 17.0 and later.
+ #[cfg(feature = "clang_17_0")]
+ enum CXBinaryOperatorKind {
+ const CXBinaryOperator_Invalid = 0,
+ const CXBinaryOperator_PtrMemD = 1,
+ const CXBinaryOperator_PtrMemI = 2,
+ const CXBinaryOperator_Mul = 3,
+ const CXBinaryOperator_Div = 4,
+ const CXBinaryOperator_Rem = 5,
+ const CXBinaryOperator_Add = 6,
+ const CXBinaryOperator_Sub = 7,
+ const CXBinaryOperator_Shl = 8,
+ const CXBinaryOperator_Shr = 9,
+ const CXBinaryOperator_Cmp = 10,
+ const CXBinaryOperator_LT = 11,
+ const CXBinaryOperator_GT = 12,
+ const CXBinaryOperator_LE = 13,
+ const CXBinaryOperator_GE = 14,
+ const CXBinaryOperator_EQ = 15,
+ const CXBinaryOperator_NE = 16,
+ const CXBinaryOperator_And = 17,
+ const CXBinaryOperator_Xor = 18,
+ const CXBinaryOperator_Or = 19,
+ const CXBinaryOperator_LAnd = 20,
+ const CXBinaryOperator_LOr = 21,
+ const CXBinaryOperator_Assign = 22,
+ const CXBinaryOperator_MulAssign = 23,
+ const CXBinaryOperator_DivAssign = 24,
+ const CXBinaryOperator_RemAssign = 25,
+ const CXBinaryOperator_AddAssign = 26,
+ const CXBinaryOperator_SubAssign = 27,
+ const CXBinaryOperator_ShlAssign = 28,
+ const CXBinaryOperator_ShrAssign = 29,
+ const CXBinaryOperator_AndAssign = 30,
+ const CXBinaryOperator_XorAssign = 31,
+ const CXBinaryOperator_OrAssign = 32,
+ const CXBinaryOperator_Comma = 33,
+ }
+}
+
+cenum! {
enum CXCallingConv {
const CXCallingConv_Default = 0,
const CXCallingConv_C = 1,
@@ -141,6 +196,17 @@ cenum! {
}
cenum! {
+ #[repr(c_uchar)]
+ /// Only available on `libclang` 17.0 and later.
+ #[cfg(feature = "clang_17_0")]
+ enum CXChoice {
+ const CXChoice_Default = 0,
+ const CXChoice_Enabled = 1,
+ const CXChoice_Disabled = 2,
+ }
+}
+
+cenum! {
enum CXCommentInlineCommandRenderKind {
const CXCommentInlineCommandRenderKind_Normal = 0,
const CXCommentInlineCommandRenderKind_Bold = 1,
@@ -327,6 +393,8 @@ cenum! {
const CXCursor_ConceptSpecializationExpr = 153,
/// Only produced by `libclang` 15.0 and later.
const CXCursor_RequiresExpr = 154,
+ /// Only produced by `libclang` 16.0 and later.
+ const CXCursor_CXXParenListInitExpr = 155,
const CXCursor_UnexposedStmt = 200,
const CXCursor_LabelStmt = 201,
const CXCursor_CompoundStmt = 202,
@@ -490,6 +558,8 @@ cenum! {
const CXCursor_OMPParallelMaskedTaskLoopDirective = 303,
/// Only produced by `libclang` 15.0 and later.
const CXCursor_OMPParallelMaskedTaskLoopSimdDirective = 304,
+ /// Only produced by `libclang` 16.0 and later.
+ const CXCursor_OMPErrorDirective = 305,
#[cfg(not(feature="clang_15_0"))]
const CXCursor_TranslationUnit = 300,
#[cfg(feature="clang_15_0")]
@@ -1045,7 +1115,7 @@ cenum! {
/// Only produced by `libclang` 11.0 and later.
const CXType_Atomic = 177,
/// Only produced by `libclang` 15.0 and later.
- const CXType_BTFTagAttributed = 178,
+ const CXType_BTFTagAttributed = 178,
}
}
@@ -1086,6 +1156,28 @@ cenum! {
}
cenum! {
+ /// Only available on `libclang` 17.0 and later.
+ #[cfg(feature = "clang_17_0")]
+ enum CXUnaryOperatorKind {
+ const CXUnaryOperator_Invalid = 0,
+ const CXUnaryOperator_PostInc = 1,
+ const CXUnaryOperator_PostDec = 2,
+ const CXUnaryOperator_PreInc = 3,
+ const CXUnaryOperator_PreDec = 4,
+ const CXUnaryOperator_AddrOf = 5,
+ const CXUnaryOperator_Deref = 6,
+ const CXUnaryOperator_Plus = 7,
+ const CXUnaryOperator_Minus = 8,
+ const CXUnaryOperator_Not = 9,
+ const CXUnaryOperator_LNot = 10,
+ const CXUnaryOperator_Real = 11,
+ const CXUnaryOperator_Imag = 12,
+ const CXUnaryOperator_Extension = 13,
+ const CXUnaryOperator_Coawait = 14,
+ }
+}
+
+cenum! {
enum CXVisitorResult {
const CXVisit_Break = 0,
const CXVisit_Continue = 1,
@@ -1197,6 +1289,28 @@ cenum! {
}
}
+/// Only available on `libclang` 17.0 and later.
+#[cfg(feature = "clang_17_0")]
+#[cfg(not(target_os = "windows"))]
+pub type CXIndexOptions_Flags = c_ushort;
+
+/// Only available on `libclang` 17.0 and later.
+#[cfg(feature = "clang_17_0")]
+#[cfg(target_os = "windows")]
+pub type CXIndexOptions_Flags = c_uint;
+
+/// Only available on `libclang` 17.0 and later.
+#[cfg(feature = "clang_17_0")]
+pub const CXIndexOptions_ExcludeDeclarationsFromPCH: CXIndexOptions_Flags = 1;
+
+/// Only available on `libclang` 17.0 and later.
+#[cfg(feature = "clang_17_0")]
+pub const CXIndexOptions_DisplayDiagnostics: CXIndexOptions_Flags = 2;
+
+/// Only available on `libclang` 17.0 and later.
+#[cfg(feature = "clang_17_0")]
+pub const CXIndexOptions_StorePreamblesInMemory: CXIndexOptions_Flags = 4;
+
cenum! {
enum CXNameRefFlags {
const CXNameRange_WantQualifier = 1;
@@ -1590,6 +1704,21 @@ pub struct CXIdxObjCProtocolRefListInfo {
default!(CXIdxObjCProtocolRefListInfo);
+#[cfg(feature = "clang_17_0")]
+#[derive(Copy, Clone, Debug)]
+#[repr(C)]
+pub struct CXIndexOptions {
+ pub Size: c_uint,
+ pub ThreadBackgroundPriorityForIndexing: CXChoice,
+ pub ThreadBackgroundPriorityForEditing: CXChoice,
+ pub flags: CXIndexOptions_Flags,
+ pub PreambleStoragePath: *const c_char,
+ pub InvocationEmissionPath: *const c_char,
+}
+
+#[cfg(feature = "clang_17_0")]
+default!(CXIndexOptions);
+
#[derive(Copy, Clone, Debug)]
#[repr(C)]
pub struct CXPlatformAvailability {
@@ -1752,12 +1881,24 @@ link! {
#[cfg(feature = "clang_3_8")]
pub fn clang_CXXField_isMutable(cursor: CXCursor) -> c_uint;
pub fn clang_CXXMethod_isConst(cursor: CXCursor) -> c_uint;
+ /// Only available on `libclang` 16.0 and later.
+ #[cfg(feature = "clang_16_0")]
+ pub fn clang_CXXMethod_isCopyAssignmentOperator(cursor: CXCursor) -> c_uint;
/// Only available on `libclang` 3.9 and later.
#[cfg(feature = "clang_3_9")]
pub fn clang_CXXMethod_isDefaulted(cursor: CXCursor) -> c_uint;
+ /// Only available on `libclang` 16.0 and later.
+ #[cfg(feature = "clang_16_0")]
+ pub fn clang_CXXMethod_isDeleted(cursor: CXCursor) -> c_uint;
+ /// Only available on `libclang` 16.0 and later.
+ #[cfg(feature = "clang_16_0")]
+ pub fn clang_CXXMethod_isMoveAssignmentOperator(cursor: CXCursor) -> c_uint;
pub fn clang_CXXMethod_isPureVirtual(cursor: CXCursor) -> c_uint;
pub fn clang_CXXMethod_isStatic(cursor: CXCursor) -> c_uint;
pub fn clang_CXXMethod_isVirtual(cursor: CXCursor) -> c_uint;
+ /// Only available on `libclang` 17.0 and later.
+ #[cfg(feature = "clang_17_0")]
+ pub fn clang_CXXMethod_isExplicit(cursor: CXCursor) -> c_uint;
/// Only available on `libclang` 6.0 and later.
#[cfg(feature = "clang_6_0")]
pub fn clang_CXXRecord_isAbstract(cursor: CXCursor) -> c_uint;
@@ -1992,6 +2133,9 @@ link! {
pub fn clang_constructUSR_ObjCProtocol(protocol: *const c_char) -> CXString;
pub fn clang_createCXCursorSet() -> CXCursorSet;
pub fn clang_createIndex(exclude: c_int, display: c_int) -> CXIndex;
+ /// Only available on `libclang` 17.0 and later.
+ #[cfg(feature = "clang_17_0")]
+ pub fn clang_createIndexWithOptions(options: CXIndexOptions) -> CXIndex;
pub fn clang_createTranslationUnit(index: CXIndex, file: *const c_char) -> CXTranslationUnit;
pub fn clang_createTranslationUnit2(index: CXIndex, file: *const c_char, tu: *mut CXTranslationUnit) -> CXErrorCode;
pub fn clang_createTranslationUnitFromSourceFile(index: CXIndex, file: *const c_char, n_arguments: c_int, arguments: *const *const c_char, n_unsaved: c_uint, unsaved: *mut CXUnsavedFile) -> CXTranslationUnit;
@@ -2036,6 +2180,9 @@ link! {
pub fn clang_getArgType(type_: CXType, index: c_uint) -> CXType;
pub fn clang_getArrayElementType(type_: CXType) -> CXType;
pub fn clang_getArraySize(type_: CXType) -> c_longlong;
+ /// Only available on `libclang` 17.0 and later.
+ #[cfg(feature = "clang_17_0")]
+ pub fn clang_getBinaryOperatorKindSpelling(kind: CXBinaryOperatorKind) -> CXString;
pub fn clang_getCString(string: CXString) -> *const c_char;
pub fn clang_getCXTUResourceUsage(tu: CXTranslationUnit) -> CXTUResourceUsage;
pub fn clang_getCXXAccessSpecifier(cursor: CXCursor) -> CX_CXXAccessSpecifier;
@@ -2060,6 +2207,9 @@ link! {
pub fn clang_getCompletionPriority(string: CXCompletionString) -> c_uint;
pub fn clang_getCursor(tu: CXTranslationUnit, location: CXSourceLocation) -> CXCursor;
pub fn clang_getCursorAvailability(cursor: CXCursor) -> CXAvailabilityKind;
+ /// Only available on `libclang` 17.0 and later.
+ #[cfg(feature = "clang_17_0")]
+ pub fn clang_getCursorBinaryOperatorKind(cursor: CXCursor) -> CXBinaryOperatorKind;
pub fn clang_getCursorCompletionString(cursor: CXCursor) -> CXCompletionString;
pub fn clang_getCursorDefinition(cursor: CXCursor) -> CXCursor;
pub fn clang_getCursorDisplayName(cursor: CXCursor) -> CXString;
@@ -2089,6 +2239,9 @@ link! {
#[cfg(feature = "clang_6_0")]
pub fn clang_getCursorTLSKind(cursor: CXCursor) -> CXTLSKind;
pub fn clang_getCursorType(cursor: CXCursor) -> CXType;
+ /// Only available on `libclang` 17.0 and later.
+ #[cfg(feature = "clang_17_0")]
+ pub fn clang_getCursorUnaryOperatorKind(cursor: CXCursor) -> CXUnaryOperatorKind;
pub fn clang_getCursorUSR(cursor: CXCursor) -> CXString;
/// Only available on `libclang` 3.8 and later.
#[cfg(feature = "clang_3_8")]
@@ -2134,6 +2287,9 @@ link! {
pub fn clang_getLocation(tu: CXTranslationUnit, file: CXFile, line: c_uint, column: c_uint) -> CXSourceLocation;
pub fn clang_getLocationForOffset(tu: CXTranslationUnit, file: CXFile, offset: c_uint) -> CXSourceLocation;
pub fn clang_getModuleForFile(tu: CXTranslationUnit, file: CXFile) -> CXModule;
+ /// Only available on `libclang` 16.0 and later.
+ #[cfg(feature = "clang_16_0")]
+ pub fn clang_getNonReferenceType(type_: CXType) -> CXType;
pub fn clang_getNullCursor() -> CXCursor;
pub fn clang_getNullLocation() -> CXSourceLocation;
pub fn clang_getNullRange() -> CXSourceRange;
@@ -2168,12 +2324,12 @@ link! {
/// Only available on `libclang` 5.0 and later.
#[cfg(feature = "clang_5_0")]
pub fn clang_getTranslationUnitTargetInfo(tu: CXTranslationUnit) -> CXTargetInfo;
+ /// Only available on `libclang` 17.0 and later.
+ #[cfg(feature = "clang_17_0")]
+ pub fn clang_getUnaryOperatorKindSpelling(kind: CXUnaryOperatorKind) -> CXString;
/// Only available on `libclang` 16.0 and later.
#[cfg(feature = "clang_16_0")]
pub fn clang_getUnqualifiedType(type_: CXType) -> CXType;
- /// Only available on `libclang` 16.0 and later.
- #[cfg(feature = "clang_16_0")]
- pub fn clang_getNonReferenceType(type_: CXType) -> CXType;
pub fn clang_getTypeDeclaration(type_: CXType) -> CXCursor;
pub fn clang_getTypeKindSpelling(type_: CXTypeKind) -> CXString;
pub fn clang_getTypeSpelling(type_: CXType) -> CXString;
diff --git a/src/link.rs b/src/link.rs
index c3b0830..79b3d49 100644
--- a/src/link.rs
+++ b/src/link.rs
@@ -40,6 +40,7 @@ macro_rules! link {
)+
) => (
use std::cell::{RefCell};
+ use std::fmt;
use std::sync::{Arc};
use std::path::{Path, PathBuf};
@@ -58,6 +59,33 @@ macro_rules! link {
V7_0 = 70,
V8_0 = 80,
V9_0 = 90,
+ V11_0 = 110,
+ V12_0 = 120,
+ V16_0 = 160,
+ V17_0 = 170,
+ }
+
+ impl fmt::Display for Version {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use Version::*;
+ match self {
+ V3_5 => write!(f, "3.5.x"),
+ V3_6 => write!(f, "3.6.x"),
+ V3_7 => write!(f, "3.7.x"),
+ V3_8 => write!(f, "3.8.x"),
+ V3_9 => write!(f, "3.9.x"),
+ V4_0 => write!(f, "4.0.x"),
+ V5_0 => write!(f, "5.0.x"),
+ V6_0 => write!(f, "6.0.x"),
+ V7_0 => write!(f, "7.0.x"),
+ V8_0 => write!(f, "8.0.x"),
+ V9_0 => write!(f, "9.0.x - 10.0.x"),
+ V11_0 => write!(f, "11.0.x"),
+ V12_0 => write!(f, "12.0.x - 15.0.x"),
+ V16_0 => write!(f, "16.0.x"),
+ V17_0 => write!(f, "17.0.x or later"),
+ }
+ }
}
/// The set of functions loaded dynamically.
@@ -104,6 +132,10 @@ macro_rules! link {
}
unsafe {
+ check!(b"clang_CXXMethod_isExplicit", V17_0);
+ check!(b"clang_CXXMethod_isCopyAssignmentOperator", V16_0);
+ check!(b"clang_Cursor_getVarDeclInitializer", V12_0);
+ check!(b"clang_Type_getValueType", V11_0);
check!(b"clang_Cursor_isAnonymousRecordDecl", V9_0);
check!(b"clang_Cursor_getObjCPropertyGetterName", V8_0);
check!(b"clang_File_tryGetRealPathName", V7_0);
@@ -142,13 +174,31 @@ macro_rules! link {
#[cfg_attr(feature="cargo-clippy", allow(clippy::too_many_arguments))]
$(#[doc=$doc] #[cfg($cfg)])*
pub unsafe fn $name($($pname: $pty), *) $(-> $ret)* {
- let f = with_library(|l| {
- l.functions.$name.expect(concat!(
- "`libclang` function not loaded: `",
- stringify!($name),
- "`. This crate requires that `libclang` 3.9 or later be installed on your ",
- "system. For more information on how to accomplish this, see here: ",
- "https://rust-lang.github.io/rust-bindgen/requirements.html#installing-clang-39"))
+ let f = with_library(|library| {
+ if let Some(function) = library.functions.$name {
+ function
+ } else {
+ panic!(
+ r#"
+A `libclang` function was called that is not supported by the loaded `libclang` instance.
+
+ called function = `{0}`
+ loaded `libclang` instance = {1}
+
+This crate only supports `libclang` 3.5 and later.
+The minimum `libclang` requirement for this particular function can be found here:
+https://docs.rs/clang-sys/latest/clang_sys/{0}/index.html
+
+Instructions for installing `libclang` can be found here:
+https://rust-lang.github.io/rust-bindgen/requirements.html
+"#,
+ stringify!($name),
+ library
+ .version()
+ .map(|v| format!("{}", v))
+ .unwrap_or_else(|| "unsupported version".into()),
+ );
+ }
}).expect("a `libclang` shared library is not loaded on this thread");
f($($pname), *)
}
@@ -175,7 +225,9 @@ macro_rules! link {
/// * a `libclang` shared library could not be found
/// * the `libclang` shared library could not be opened
pub fn load_manually() -> Result<SharedLibrary, String> {
+ #[allow(dead_code)]
mod build {
+ include!(concat!(env!("OUT_DIR"), "/macros.rs"));
pub mod common { include!(concat!(env!("OUT_DIR"), "/common.rs")); }
pub mod dynamic { include!(concat!(env!("OUT_DIR"), "/dynamic.rs")); }
}
diff --git a/src/support.rs b/src/support.rs
index 2b27cc0..20005ba 100644
--- a/src/support.rs
+++ b/src/support.rs
@@ -59,7 +59,7 @@ impl Clang {
pub fn find(path: Option<&Path>, args: &[String]) -> Option<Clang> {
if let Ok(path) = env::var("CLANG_PATH") {
let p = Path::new(&path);
- if p.is_file() && is_executable(&p).unwrap_or(false) {
+ if p.is_file() && is_executable(p).unwrap_or(false) {
return Some(Clang::new(p, args));
}
}
@@ -184,7 +184,7 @@ fn run(executable: &str, arguments: &[&str]) -> Result<(String, String), String>
/// Runs `clang`, returning the `stdout` and `stderr` output.
fn run_clang(path: &Path, arguments: &[&str]) -> (String, String) {
- run(&path.to_string_lossy().into_owned(), arguments).unwrap()
+ run(&path.to_string_lossy(), arguments).unwrap()
}
/// Runs `llvm-config`, returning the `stdout` output if successful.
@@ -197,7 +197,7 @@ fn run_llvm_config(arguments: &[&str]) -> Result<String, String> {
fn parse_version_number(number: &str) -> Option<c_int> {
number
.chars()
- .take_while(|c| c.is_digit(10))
+ .take_while(|c| c.is_ascii_digit())
.collect::<String>()
.parse()
.ok()
diff --git a/tests/build.rs b/tests/build.rs
new file mode 100644
index 0000000..669c561
--- /dev/null
+++ b/tests/build.rs
@@ -0,0 +1,281 @@
+#![allow(dead_code)]
+
+extern crate glob;
+extern crate serial_test;
+extern crate tempfile;
+
+use std::collections::HashMap;
+use std::env;
+use std::fs;
+use std::path::PathBuf;
+use std::sync::Arc;
+use std::sync::Mutex;
+
+use serial_test::serial;
+use tempfile::TempDir;
+
+#[macro_use]
+#[path = "../build/macros.rs"]
+mod macros;
+
+#[path = "../build/common.rs"]
+mod common;
+#[path = "../build/dynamic.rs"]
+mod dynamic;
+#[path = "../build/static.rs"]
+mod r#static;
+
+#[derive(Debug, Default)]
+struct RunCommandMock {
+ invocations: Vec<(String, String, Vec<String>)>,
+ responses: HashMap<Vec<String>, String>,
+}
+
+#[derive(Debug)]
+struct Env {
+ os: String,
+ pointer_width: String,
+ env: Option<String>,
+ vars: HashMap<String, (Option<String>, Option<String>)>,
+ cwd: PathBuf,
+ tmp: TempDir,
+ files: Vec<String>,
+ commands: Arc<Mutex<RunCommandMock>>,
+}
+
+impl Env {
+ fn new(os: &str, pointer_width: &str) -> Self {
+ Env {
+ os: os.into(),
+ pointer_width: pointer_width.into(),
+ env: None,
+ vars: HashMap::new(),
+ cwd: env::current_dir().unwrap(),
+ tmp: tempfile::Builder::new().prefix("clang_sys_test").tempdir().unwrap(),
+ files: vec![],
+ commands: Default::default(),
+ }
+ .var("CLANG_PATH", None)
+ .var("LD_LIBRARY_PATH", None)
+ .var("LIBCLANG_PATH", None)
+ .var("LIBCLANG_STATIC_PATH", None)
+ .var("LLVM_CONFIG_PATH", None)
+ .var("PATH", None)
+ }
+
+ fn env(mut self, env: &str) -> Self {
+ self.env = Some(env.into());
+ self
+ }
+
+ fn var(mut self, name: &str, value: Option<&str>) -> Self {
+ let previous = env::var(name).ok();
+ self.vars.insert(name.into(), (value.map(|v| v.into()), previous));
+ self
+ }
+
+ fn dir(mut self, path: &str) -> Self {
+ self.files.push(path.into());
+ let path = self.tmp.path().join(path);
+ fs::create_dir_all(path).unwrap();
+ self
+ }
+
+ fn file(mut self, path: &str, contents: &[u8]) -> Self {
+ self.files.push(path.into());
+ let path = self.tmp.path().join(path);
+ fs::create_dir_all(path.parent().unwrap()).unwrap();
+ fs::write(self.tmp.path().join(path), contents).unwrap();
+ self
+ }
+
+ fn dll(self, path: &str, pointer_width: &str) -> Self {
+ // PE header.
+ let mut contents = [0; 64];
+ contents[0x3C..0x3C + 4].copy_from_slice(&i32::to_le_bytes(10));
+ contents[10..14].copy_from_slice(&[b'P', b'E', 0, 0]);
+ let magic = if pointer_width == "64" { 523 } else { 267 };
+ contents[34..36].copy_from_slice(&u16::to_le_bytes(magic));
+
+ self.file(path, &contents)
+ }
+
+ fn so(self, path: &str, pointer_width: &str) -> Self {
+ // ELF header.
+ let class = if pointer_width == "64" { 2 } else { 1 };
+ let contents = [127, 69, 76, 70, class];
+
+ self.file(path, &contents)
+ }
+
+ fn command(self, command: &str, args: &[&str], response: &str) -> Self {
+ let command = command.to_string();
+ let args = args.iter().map(|a| a.to_string()).collect::<Vec<_>>();
+
+ let mut key = vec![command];
+ key.extend(args);
+ self.commands.lock().unwrap().responses.insert(key, response.into());
+
+ self
+ }
+
+ fn enable(self) -> Self {
+ env::set_var("_CLANG_SYS_TEST", "yep");
+ env::set_var("_CLANG_SYS_TEST_OS", &self.os);
+ env::set_var("_CLANG_SYS_TEST_POINTER_WIDTH", &self.pointer_width);
+ if let Some(env) = &self.env {
+ env::set_var("_CLANG_SYS_TEST_ENV", env);
+ }
+
+ for (name, (value, _)) in &self.vars {
+ if let Some(value) = value {
+ env::set_var(name, value);
+ } else {
+ env::remove_var(name);
+ }
+ }
+
+ env::set_current_dir(&self.tmp).unwrap();
+
+ let commands = self.commands.clone();
+ let mock = &mut *common::RUN_COMMAND_MOCK.lock().unwrap();
+ *mock = Some(Box::new(move |command, path, args| {
+ let command = command.to_string();
+ let path = path.to_string();
+ let args = args.iter().map(|a| a.to_string()).collect::<Vec<_>>();
+
+ let mut commands = commands.lock().unwrap();
+ commands.invocations.push((command.clone(), path, args.clone()));
+
+ let mut key = vec![command];
+ key.extend(args);
+ commands.responses.get(&key).cloned()
+ }));
+
+ self
+ }
+}
+
+impl Drop for Env {
+ fn drop(&mut self) {
+ env::remove_var("_CLANG_SYS_TEST");
+ env::remove_var("_CLANG_SYS_TEST_OS");
+ env::remove_var("_CLANG_SYS_TEST_POINTER_WIDTH");
+ env::remove_var("_CLANG_SYS_TEST_ENV");
+
+ for (name, (_, previous)) in &self.vars {
+ if let Some(previous) = previous {
+ env::set_var(name, previous);
+ } else {
+ env::remove_var(name);
+ }
+ }
+
+ if let Err(error) = env::set_current_dir(&self.cwd) {
+ println!("Failed to reset working directory: {:?}", error);
+ }
+ }
+}
+
+//================================================
+// Dynamic
+//================================================
+
+// Linux -----------------------------------------
+
+#[test]
+#[serial]
+fn test_linux_directory_preference() {
+ let _env = Env::new("linux", "64")
+ .so("usr/lib/libclang.so.1", "64")
+ .so("usr/local/lib/libclang.so.1", "64")
+ .enable();
+
+ assert_eq!(
+ dynamic::find(true),
+ Ok(("usr/local/lib".into(), "libclang.so.1".into())),
+ );
+}
+
+#[test]
+#[serial]
+fn test_linux_version_preference() {
+ let _env = Env::new("linux", "64")
+ .so("usr/lib/libclang-3.so", "64")
+ .so("usr/lib/libclang-3.5.so", "64")
+ .so("usr/lib/libclang-3.5.0.so", "64")
+ .enable();
+
+ assert_eq!(
+ dynamic::find(true),
+ Ok(("usr/lib".into(), "libclang-3.5.0.so".into())),
+ );
+}
+
+#[test]
+#[serial]
+fn test_linux_directory_and_version_preference() {
+ let _env = Env::new("linux", "64")
+ .so("usr/local/llvm/lib/libclang-3.so", "64")
+ .so("usr/local/lib/libclang-3.5.so", "64")
+ .so("usr/lib/libclang-3.5.0.so", "64")
+ .enable();
+
+ assert_eq!(
+ dynamic::find(true),
+ Ok(("usr/lib".into(), "libclang-3.5.0.so".into())),
+ );
+}
+
+// Windows ---------------------------------------
+
+#[cfg(target_os = "windows")]
+#[test]
+#[serial]
+fn test_windows_bin_sibling() {
+ let _env = Env::new("windows", "64")
+ .dir("Program Files\\LLVM\\lib")
+ .dll("Program Files\\LLVM\\bin\\libclang.dll", "64")
+ .enable();
+
+ assert_eq!(
+ dynamic::find(true),
+ Ok(("Program Files\\LLVM\\bin".into(), "libclang.dll".into())),
+ );
+}
+
+#[cfg(target_os = "windows")]
+#[test]
+#[serial]
+fn test_windows_mingw_gnu() {
+ let _env = Env::new("windows", "64")
+ .env("gnu")
+ .dir("MSYS\\MinGW\\lib")
+ .dll("MSYS\\MinGW\\bin\\clang.dll", "64")
+ .dir("Program Files\\LLVM\\lib")
+ .dll("Program Files\\LLVM\\bin\\libclang.dll", "64")
+ .enable();
+
+ assert_eq!(
+ dynamic::find(true),
+ Ok(("MSYS\\MinGW\\bin".into(), "clang.dll".into())),
+ );
+}
+
+#[cfg(target_os = "windows")]
+#[test]
+#[serial]
+fn test_windows_mingw_msvc() {
+ let _env = Env::new("windows", "64")
+ .env("msvc")
+ .dir("MSYS\\MinGW\\lib")
+ .dll("MSYS\\MinGW\\bin\\clang.dll", "64")
+ .dir("Program Files\\LLVM\\lib")
+ .dll("Program Files\\LLVM\\bin\\libclang.dll", "64")
+ .enable();
+
+ assert_eq!(
+ dynamic::find(true),
+ Ok(("Program Files\\LLVM\\bin".into(), "libclang.dll".into())),
+ );
+}