更新libclamav库1.0.0版本

This commit is contained in:
2023-01-14 18:28:39 +08:00
parent b879ee0b2e
commit 45fe15f472
8531 changed files with 1222046 additions and 177272 deletions

View File

@@ -0,0 +1 @@
{"files":{"CHANGELOG.md":"e213b234e1e64fca8c4fc26cc194d3e4c7d3de1f30eaab5fe2a001b5decf461d","Cargo.toml":"6c398894882867cf488387a5adc927336ae6e31d4a413c7918655f934b617d0c","LICENSE.txt":"3ddf9be5c28fe27dad143a5dc76eea25222ad1dd68934a047064e56ed2fa40c5","README.md":"00ea4c69bb97f07cc29ee68d673666252c35b94593e3eb26b4f7a838a4ce1d08","build.rs":"fbcd7eb0494a38be9e3fda648f91e4be05cad53a4c9faaf4fb8c7be270b1305d","build/common.rs":"7a38220e50c7c208057cb0b5ac13d54152aa4f6bebaea2160b0391adaf518e68","build/dynamic.rs":"84bc5a004dd7fa72a176cf9564db58a517c09870bac9ae6f5169411e1ed7f548","build/static.rs":"15e680b3896d1a83720210de7b72cc2fd2791626721e480c89902595d876eba7","clippy.toml":"fcf54943ba571514b244cc098ce08671b4117167733e8107e799d533a12a2195","src/lib.rs":"31cbacb197fc75dc13719d162666602f1a373d910f7947bebe2c5236f5a547d4","src/link.rs":"2fd772f679752a215566460671b159c01324504e7a36b642751fa3e1eca8d42f","src/support.rs":"da7e4e08a5fce844f563943588fca5de3b72972fc61db96c5158b1246737e006","tests/header.h":"1b15a686d1c06561960045a26c25a34d840f26c8246f2f5e630f993b69c7492c","tests/lib.rs":"81a459e9f48e6d384b3fd61e7942e685f7ea39af6574bc2b382a0fbe7820ff65"},"package":"fa2e27ae6ab525c3d369ded447057bca5438d86dc3a68f6faafb8269ba82ebf3"}

View File

@@ -0,0 +1,490 @@
## [1.4.0] - 2022-09-22
### Changed
- The value of an `EntityKind` enum variant
(`EntityKind::CXCursor_TranslationUnit`) has been updated for Clang 15.0 and
later to match the
[breaking change made in `libclang`](https://github.com/llvm/llvm-project/commit/bb83f8e70bd1d56152f02307adacd718cd67e312#diff-674613a0e47f4e66cc19061e28e3296d39be2d124dceefb68237b30b8e241e7c)
### Added
- Added support for `clang` 16.0.x
- Added support for `clang` 15.0.x
- Added support for `clang` 14.0.x
## [1.3.3] - 2022-05-28
### Fixed
- Fixed `Clang::find` to check that `CLANG_PATH` is an executable file before
selecting it
## [1.3.2] - 2022-05-18
### Added
- Added support for illumos and derivatives
## [1.3.1] - 2022-02-03
### Added
- Added missing `clang_getToken` function
## [1.3.0] - 2021-10-31
### Added
- Added support for `clang` 13.0.x
- Added support for `clang` 12.0.x
- Added support for the Haiku operating system
## [1.2.2] - 2021-09-02
### Fixed
- Fixed handling of paths that contain characters that have special meaning in
glob patterns (e.g., `[` or `]`)
## [1.2.1] - 2021-08-24
### Changed
- Updated build script to check the install location used by the
[Scoop](https://scoop.sh/) command-line installer on Windows
### Fixed
- Updated build script to support environments where the `PATH` environment
variable is not set
## [1.2.0] - 2021-04-08
### Changed
- Changed `Clang::find` to prefer target-prefixed binaries when a `-target`
argument is provided (e.g., if the arguments `-target` and
`x86_64-unknown-linux-gnu` are provided, a target-prefixed Clang executable
such as `x86_64-unknown-linux-gnu-clang` will be preferred over a non-target
prefixed Clang executable)
### Fixed
- Fixed build script to split paths in environment variables (e.g.,
`LD_LIBRARY_PATH`) using the appropriate separator for the platform (previously
`:` was used as the separator but some platforms such as Windows use `;`)
## [1.1.1] - 2021-02-19
### Changed
- Bumped `libloading` version to `0.7`
## [1.1.0] - 2021-02-09
### Changed
- Added Visual Studio LLVM component directory to search paths on Windows
([#121](https://github.com/KyleMayes/clang-sys/issues/121))
### Added
- Added support for `clang` 11.0.x
## [1.0.3] - 2020-11-19
### Fixed
- Fixed `Clang::find` panicking when `llvm-config` or `xcode-build` don't output anything to `stdout`
## [1.0.2] - 2020-11-17
### Fixed
- Fixed `Clang::find` to properly search directories returned by the
`llvm-config --bindir` and `xcodebuild -find clang` commands
- Improved version selection algorithm in the case where there are multiple
instances of `libclang` with the highest version found; previously the lowest
priority instance would be selected instead of the highest priority instance
(e.g., the versions found by searching the fallback directories were preferred
over the versions found by searching the `llvm-config --prefix` directory)
## [1.0.1] - 2020-10-01
### Changed
- Improved panic error message when calling an unloaded function
## [1.0.0] - 2020-07-14
### Changed
- Bumped `libloading` version to `0.6.0`
- Updated build script to not print warnings about failures to execute
`llvm-config` and `xcode-select` unless an instance of `libclang` is not found
### Added
- Added support for `clang` 10.0.x
### Removed
- Removed `gte_clang_*` Cargo features (these were an implementation detail)
## [0.29.3] - 2020-03-31
### Added
- Added ability to determine version of runtime-linked instance of `libclang`
## [0.29.2] - 2020-03-09
### Added
- Revert unnecessary increase of minimum version of `libc` and `libloading`
## [0.29.2] - 2020-03-09
### Added
- Revert unnecessary increase of minimum version of `libc` and `libloading`
## [0.29.1] - 2020-03-06
### Added
- Added support for finding instances of `libclang` matching `libclang-*.so.*`
## [0.29.0] - 2020-02-17
### Changed
- Wrapped function pointer fields in `Option` in the `CXCursorAndRangeVisitor`
and `IndexerCallbacks` structs (to permit nullability and to avoid undefined
behavior caused by `Default` implementations for these structs which returns a
zeroed value)
### Added
- Added support for `clang` 9.0.x
- Added missing `CXCallingConv_AArch64VectorCall` variant to `CXCallingConv` enum
- Added missing `clang_CompileCommand_getNumMappedSources` function
## [0.28.1] - 2019-07-28
### Changed
- Bumped `glob` version to `0.3.0`
- Improved error message when an invocation of an executable is not successful
- Allowed `LIBCLANG_PATH` to refer to a specific `libclang` instance (e.g.,
`/usr/local/lib/libclang.so.10`)
### Fixed
- Fixed
[`libclang-cpp`](https://github.com/llvm-mirror/clang/commit/90d6722bdcbc2af52306f7e948c556ad6185ac48)
being linked instead of `libclang`
## [0.28.0] - 2019-02-17
### Changed
- Changed `llvm-config` to be first search candidate on macOS
### Added
- Added support for `clang` 8.0.x
### Removed
- Removed `assert-minimum` feature
- Removed version detection for libraries without versions embedded in the filename
## [0.27.0] - 2019-01-10
### Changed
- Added version detection for libraries without versions embedded in the filename
### Added
- Added `assert-minimum` feature (see `README.md` for details)
## [0.26.4] - 2018-12-29
### Changed
- Added shared library path to `SharedLibrary` struct
## [0.26.3] - 2018-11-14
### Changed
- Disable default features of `libc` dependency
## [0.26.2] - 2018-11-03
### Fixed
- Fixed dynamic linking on macOS
## [0.26.1] - 2018-10-10
### Fixed
- Fixed support for finding libraries in `bin` directories on Windows
## [0.26.0] - 2018-10-07
### Changed
- Added support for finding libraries with version suffixes on Linux when using runtime linking (e.g., `libclang.so.1`)
## [0.25.0] - 2018-10-06
### Changed
- Added support for versioned libraries on BSDs
## [0.24.0] - 2018-09-15
### Changed
- Reworked finding of libraries (see `README.md` for details)
### Added
- Added support for `clang` 7.0.x
## [0.23.0] - 2018-06-16
### Changed
- Changed `Clang::find` to skip dynamic libraries for an incorrect architecture on Windows
## [0.22.0] - 2018-03-11
### Added
- Added support for `clang` 6.0.x
- Bumped `libc` version to `0.2.39`
- Bumped `libloading` version to `0.5.0`
## [0.21.2] - 2018-02-17
### Changed
- Added original errors to error messages
- Added support for searching for libraries in `LD_LIBRARY_PATH` directories
## [0.21.1] - 2017-11-24
### Changed
- Improved finding of versioned libraries (e.g., `libclang-3.9.so`)
### Fixed
* Fixed compilation failures on the beta and nightly channels caused by a [compiler bug](https://github.com/KyleMayes/clang-sys/pull/69)
## [0.21.0] - 2017-10-11
### Changed
* Replaced `bitflags` usage with constants which avoids crashes on 32-bit Linux platforms
## [0.20.1] - 2017-09-16
### Fixed
- Fixed static linking
## [0.20.0] - 2017-09-14
### Added
- Added support for `clang` 5.0.x
- Added `clang` as a link target of this package
- Added dummy implementations of `is_loaded` for builds with the `static` Cargo feature enabled
## [0.19.0] - 2017-07-02
### Changed
- Bumped `bitflags` version to `0.9.1`
- Added `args` parameter to `Clang::new` function which passes arguments to the Clang executable
## [0.18.0] - 2017-05-16
### Changed
- Improved finding of versioned libraries (e.g., `libclang.so.3.9`)
## [0.17.0] - 2017-05-08
### Changed
- Changed storage type of include search paths from `Vec<PathBuf>` to `Option<Vec<PathBuf>>`
## [0.16.0] - 2017-05-02
### Changed
- Bumped `libloading` version to `0.4.0`
## [0.15.2] - 2017-04-28
### Fixed
- Fixed finding of `libclang.so.1` on Linux
## [0.15.1] - 2017-03-29
### Fixed
- Fixed static linking when libraries are in [different directories](https://github.com/KyleMayes/clang-sys/issues/50)
## [0.15.0] - 2017-03-13
### Added
- Added support for `clang` 4.0.x
### Changed
- Changed functions in the `Functions` struct to be `unsafe` (`runtime` feature only)
- Changed `Clang::find` method to ignore directories and non-executable files
- Changed `Clang::find` to skip dynamic libraries for an incorrect architecture on FreeBSD and Linux
- Bumped `bitflags` version to `0.7.0`
## [0.14.0] - 2017-01-30
### Changed
- Changed all enum types from tuple structs to raw integers to avoid
[segmentation faults](https://github.com/rust-lang/rust/issues/39394) on some platforms
## [0.13.0] - 2017-01-29
### Changed
- Changed all opaque pointers types from tuple structs to raw pointers to avoid
[segmentation faults](https://github.com/rust-lang/rust/issues/39394) on some platforms
## [0.12.0] - 2016-12-13
### Changed
- Altered the runtime linking API to allow for testing the presence of functions
## [0.11.1] - 2016-12-07
### Added
- Added support for linking to Clang on Windows from unofficial LLVM sources such as MSYS and MinGW
## [0.11.0] - 2016-10-07
### Changed
- Changed all enums from Rust enums to typed constants to avoid
[undefined behavior](https://github.com/KyleMayes/clang-sys/issues/42)
## [0.10.1] - 2016-08-21
### Changed
- Changed static linking on FreeBSD and macOS to link against `libc++` instead of `libstd++`
## [0.10.0] - 2016-08-01
### Changed
- Added `runtime` Cargo feature that links to `libclang` shared library at runtime
- Added `from_raw` method to `CXTypeLayoutError` enum
- Added implementations of `Deref` for opaque FFI structs
- Changed `Default` implementations for structs to zero out the struct
## [0.9.0] - 2016-07-21
### Added
- Added documentation bindings
## [0.8.1] - 2016-07-20
### Changed
- Added `CLANG_PATH` environment variable for providing a path to `clang` executable
- Added usage of `llvm-config` to search for `clang`
- Added usage of `xcodebuild` to search for `clang` on macOS
## [0.8.0] - 2016-07-18
### Added
- Added support for `clang` 3.9.x
### Changed
- Bumped `libc` version to `0.2.14`
### Fixed
- Fixed `LIBCLANG_PATH` usage on Windows to search both the `bin` and `lib` directories
- Fixed search path parsing on macOS
- Fixed search path parsing on Windows
- Fixed default search path ordering on macOS
## [0.7.2] - 2016-06-17
### Fixed
- Fixed finding of `clang` executables when system has executables matching `clang-*`
(e.g., `clang-format`)
## [0.7.1] - 2016-06-10
### Changed
- Bumped `libc` version to `0.2.12`
### Fixed
- Fixed finding of `clang` executables suffixed by their version (e.g., `clang-3.5`)
## [0.7.0] - 2016-05-31
### Changed
- Changed `Clang` struct `version` field type to `Option<CXVersion>`
## [0.6.0] - 2016-05-26
### Added
- Added `support` module
### Fixed
- Fixed `libclang` linking on FreeBSD
- Fixed `libclang` linking on Windows with the MSVC toolchain
- Improved `libclang` static linking
## [0.5.4] - 20160-5-19
### Changed
- Added implementations of `Default` for FFI structs
## [0.5.3] - 2016-05-17
### Changed
- Bumped `bitflags` version to `0.7.0`
## [0.5.2] - 2016-05-12
### Fixed
- Fixed `libclang` static linking
## [0.5.1] - 2016-05-10
### Fixed
- Fixed `libclang` linking on macOS
- Fixed `libclang` linking on Windows
## [0.5.0] - 2016-05-10
### Removed
- Removed `rustc_version` dependency
- Removed support for `LIBCLANG_STATIC` environment variable
### Changed
- Bumped `bitflags` version to `0.6.0`
- Bumped `libc` version to `0.2.11`
- Improved `libclang` search path
- Improved `libclang` static linking
## [0.4.2] - 2016-04-20
### Changed
- Bumped `libc` version to `0.2.10`
## [0.4.1] - 2016-04-02
### Changed
- Bumped `libc` version to `0.2.9`
- Bumped `rustc_version` version to `0.1.7`
## [0.4.0] - 2016-03-28
### Removed
- Removed support for `clang` 3.4.x
## [0.3.1] - 2016-03-21
### Added
- Added support for finding `libclang`
## [0.3.0] - 2016-03-16
### Removed
- Removed build system types and functions
### Added
- Added support for `clang` 3.4.x
### Changed
- Bumped `bitflags` version to `0.5.0`
- Bumped `libc` version to `0.2.8`
## [0.2.1] - 2016-02-13
### Changed
- Simplified internal usage of conditional compilation
- Bumped `bitflags` version to `0.4.0`
- Bumped `libc` version to `0.2.7`
- Bumped `rustc_version` version to `0.1.6`
## [0.2.0] - 2016-02-12
### Added
- Added support for `clang` 3.8.x
## [0.1.2] - 2015-12-29
### Added
- Added derivations of `Debug` for FFI structs
## [0.1.1] - 2015-12-26
### Added
- Added derivations of `PartialOrd` and `Ord` for FFI enums
## [0.1.0] - 2015-12-22
- Initial release

View File

@@ -0,0 +1,64 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies.
#
# If you are reading this file be aware that the original Cargo.toml
# will likely look very different (and much more reasonable).
# See Cargo.toml.orig for the original contents.
[package]
name = "clang-sys"
version = "1.4.0"
authors = ["Kyle Mayes <kyle@mayeses.com>"]
build = "build.rs"
links = "clang"
description = "Rust bindings for libclang."
documentation = "https://docs.rs/clang-sys"
readme = "README.md"
license = "Apache-2.0"
repository = "https://github.com/KyleMayes/clang-sys"
[package.metadata.docs.rs]
features = [
"clang_16_0",
"runtime",
]
[dependencies.glob]
version = "0.3"
[dependencies.libc]
version = "0.2.39"
default-features = false
[dependencies.libloading]
version = "0.7"
optional = true
[build-dependencies.glob]
version = "0.3"
[features]
clang_10_0 = ["clang_9_0"]
clang_11_0 = ["clang_10_0"]
clang_12_0 = ["clang_11_0"]
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_3_5 = []
clang_3_6 = ["clang_3_5"]
clang_3_7 = ["clang_3_6"]
clang_3_8 = ["clang_3_7"]
clang_3_9 = ["clang_3_8"]
clang_4_0 = ["clang_3_9"]
clang_5_0 = ["clang_4_0"]
clang_6_0 = ["clang_5_0"]
clang_7_0 = ["clang_6_0"]
clang_8_0 = ["clang_7_0"]
clang_9_0 = ["clang_8_0"]
runtime = ["libloading"]
static = []

View File

@@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.

View File

@@ -0,0 +1,89 @@
# clang-sys
[![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)
Rust bindings for `libclang`.
If you are interested in a somewhat idiomatic Rust wrapper for these bindings, see [`clang-rs`](https://github.com/KyleMayes/clang-rs).
Released under the Apache License 2.0.
## [Documentation](https://docs.rs/clang-sys)
Note that the documentation on https://docs.rs for this crate assumes usage of the `runtime` Cargo feature as well as the Cargo feature for the latest supported version of `libclang` (e.g., `clang_16_0`), neither of which are enabled by default.
Due to the usage of the `runtime` Cargo feature, this documentation will contain some additional types and functions to manage a dynamically loaded `libclang` instance at runtime.
Due to the usage of the Cargo feature for the latest supported version of `libclang`, this documentation will contain constants and functions that are not available in the oldest supported version of `libclang` (3.5). All of these types and functions have a documentation comment which specifies the minimum `libclang` version required to use the item.
## Supported Versions
To target a version of `libclang`, enable a Cargo features such as one of the following:
* `clang_3_5` - requires `libclang` 3.5 or later
* `clang_3_6` - requires `libclang` 3.6 or later
* etc...
* `clang_15_0` - requires `libclang` 15.0 or later
* `clang_16_0` - requires `libclang` 16.0 or later
If you do not enable one of these features, the API provided by `libclang` 3.5 will be available by default.
**Note:** If you are using Clang 15.0 or later, you should enable the `clang_15_0` feature or a more recent version feature. Clang 15.0 introduced [a breaking change to the `EntityKind` enum](https://github.com/llvm/llvm-project/commit/bb83f8e70bd1d56152f02307adacd718cd67e312#diff-674613a0e47f4e66cc19061e28e3296d39be2d124dceefb68237b30b8e241e7c) which resulted in a mismatch between the values returned by `libclang` and the values for `EntityKind` defined by this crate in previous versions.
## Dependencies
By default, this crate will attempt to link to `libclang` dynamically. In this case, this crate depends on the `libclang` shared library (`libclang.so` on Linux, `libclang.dylib` on macOS, `libclang.dll` on Windows). If you want to link to `libclang` statically instead, enable the `static` Cargo feature. In this case, this crate depends on the LLVM and Clang static libraries. If you don't want to link to `libclang` at compiletime but instead want to load it at runtime, enable the `runtime` Cargo feature.
These libraries can be either be installed as a part of Clang or downloaded [here](http://llvm.org/releases/download.html).
**Note:** The downloads for LLVM and Clang 3.8 and later do not include the `libclang.a` static library. This means you cannot link to any of these versions of `libclang` statically unless you build it from source.
### Versioned Dependencies
This crate supports finding versioned instances of `libclang.so` (e.g.,`libclang-3.9.so`). In the case where there are multiple instances to choose from, this crate will prefer instances with higher versions. For example, the following instances of `libclang.so` are listed in descending order of preference:
1. `libclang-4.0.so`
2. `libclang-4.so`
3. `libclang-3.9.so`
4. `libclang-3.so`
5. `libclang.so`
**Note:** On BSD distributions, versioned instances of `libclang.so` matching the pattern `libclang.so.*` (e.g., `libclang.so.7.0`) are also included.
**Note:** On Linux distributions when the `runtime` features is enabled, versioned instances of `libclang.so` matching the pattern `libclang.so.*` (e.g., `libclang.so.1`) are also included.
## Environment Variables
The following environment variables, if set, are used by this crate to find the required libraries and executables:
* `LLVM_CONFIG_PATH` **(compiletime)** - provides a full path to an `llvm-config` executable (including the executable itself [i.e., `/usr/local/bin/llvm-config-8.0`])
* `LIBCLANG_PATH` **(compiletime)** - provides a path to a directory containing a `libclang` shared library or a full path to a specific `libclang` shared library
* `LIBCLANG_STATIC_PATH` **(compiletime)** - provides a path to a directory containing LLVM and Clang static libraries
* `CLANG_PATH` **(runtime)** - provides a path to a `clang` executable
## Linking
### Dynamic
`libclang` shared libraries will be searched for in the following directories:
* the directory provided by the `LIBCLANG_PATH` environment variable
* the `bin` and `lib` directories in the directory provided by `llvm-config --libdir`
* the directories provided by `LD_LIBRARY_PATH` environment variable
* a list of likely directories for the target platform (e.g., `/usr/local/lib` on Linux)
* **macOS only:** the toolchain directory in the directory provided by `xcode-select --print-path`
On Linux, running an executable that has been dynamically linked to `libclang` may require you to add a path to `libclang.so` to the `LD_LIBRARY_PATH` environment variable. The same is true on OS X, except the `DYLD_LIBRARY_PATH` environment variable is used instead.
On Windows, running an executable that has been dynamically linked to `libclang` requires that `libclang.dll` can be found by the executable at runtime. See [here](https://msdn.microsoft.com/en-us/library/7d83bc18.aspx) for more information.
### Static
The availability of `llvm-config` is not optional for static linking. Ensure that an instance of this executable can be found on your system's path or set the `LLVM_CONFIG_PATH` environment variable. The required LLVM and Clang static libraries will be searched for in the same way as shared libraries are searched for, except the `LIBCLANG_STATIC_PATH` environment variable is used in place of the `LIBCLANG_PATH` environment variable.
### Runtime
The `clang_sys::load` function is used to load a `libclang` shared library for use in the thread in which it is called. The `clang_sys::unload` function will unload the `libclang` shared library. `clang_sys::load` searches for a `libclang` shared library in the same way one is searched for when linking to `libclang` dynamically at compiletime.

View File

@@ -0,0 +1,74 @@
// SPDX-License-Identifier: Apache-2.0
//! Finds `libclang` static or shared libraries and links to them.
//!
//! # Environment Variables
//!
//! This build script can make use of several environment variables to help it
//! find the required static or shared libraries.
//!
//! * `LLVM_CONFIG_PATH` - provides a path to an `llvm-config` executable
//! * `LIBCLANG_PATH` - provides a path to a directory containing a `libclang`
//! shared library or a path to a specific `libclang` shared library
//! * `LIBCLANG_STATIC_PATH` - provides a path to a directory containing LLVM
//! and Clang static libraries
#![allow(unused_attributes)]
extern crate glob;
use std::path::Path;
#[path = "build/common.rs"]
pub mod common;
#[path = "build/dynamic.rs"]
pub mod dynamic;
#[path = "build/static.rs"]
pub mod r#static;
/// Copies a file.
#[cfg(feature = "runtime")]
fn copy(source: &str, destination: &Path) {
use std::fs::File;
use std::io::{Read, Write};
let mut string = String::new();
File::open(source)
.unwrap()
.read_to_string(&mut string)
.unwrap();
File::create(destination)
.unwrap()
.write_all(string.as_bytes())
.unwrap();
}
/// Copies the code used to find and link to `libclang` shared libraries into
/// the build output directory so that it may be used when linking at runtime.
#[cfg(feature = "runtime")]
fn main() {
use std::env;
if cfg!(feature = "static") {
panic!("`runtime` and `static` features can't be combined");
}
let out = env::var("OUT_DIR").unwrap();
copy("build/common.rs", &Path::new(&out).join("common.rs"));
copy("build/dynamic.rs", &Path::new(&out).join("dynamic.rs"));
}
/// Finds and links to the required libraries dynamically or statically.
#[cfg(not(feature = "runtime"))]
fn main() {
if cfg!(feature = "static") {
r#static::link();
} else {
dynamic::link();
}
if let Some(output) = common::run_llvm_config(&["--includedir"]) {
let directory = Path::new(output.trim_end());
println!("cargo:include={}", directory.display());
}
}

View File

@@ -0,0 +1,319 @@
// SPDX-License-Identifier: Apache-2.0
extern crate glob;
use std::cell::RefCell;
use std::collections::HashMap;
use std::env;
use std::path::{Path, PathBuf};
use std::process::Command;
use glob::{MatchOptions, Pattern};
//================================================
// Commands
//================================================
thread_local! {
/// The errors encountered by the build script while executing commands.
static COMMAND_ERRORS: RefCell<HashMap<String, Vec<String>>> = RefCell::default();
}
/// 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,
))
});
}
/// 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,
}
impl CommandErrorPrinter {
pub fn discard(mut self) {
self.discard = true;
}
}
impl Drop for CommandErrorPrinter {
fn drop(&mut self) {
if self.discard {
return;
}
let errors = COMMAND_ERRORS.with(|e| e.borrow().clone());
if let Some(errors) = errors.get("llvm-config") {
println!(
"cargo:warning=could not execute `llvm-config` one or more \
times, if the LLVM_CONFIG_PATH environment variable is set to \
a full path to valid `llvm-config` executable it will be used \
to try to find an instance of `libclang` on your system: {}",
errors
.iter()
.map(|e| format!("\"{}\"", e))
.collect::<Vec<_>>()
.join("\n "),
)
}
if let Some(errors) = errors.get("xcode-select") {
println!(
"cargo:warning=could not execute `xcode-select` one or more \
times, if a valid instance of this executable is on your PATH \
it will be used to try to find an instance of `libclang` on \
your system: {}",
errors
.iter()
.map(|e| format!("\"{}\"", e))
.collect::<Vec<_>>()
.join("\n "),
)
}
}
}
/// 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",
];
/// `libclang` directory patterns for illumos
const DIRECTORIES_ILLUMOS: &[&str] = &[
"/opt/ooce/clang-*/lib",
"/opt/ooce/llvm-*/lib",
];
//================================================
// 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 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 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 to ensure that the search
// is limited to the specified directory.
let mut options = MatchOptions::new();
options.require_literal_separator = true;
paths
.map(|p| glob::glob_with(&p, options))
.filter_map(Result::ok)
.flatten()
.filter_map(|p| {
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.,
// `libclang-cpp.so.10`) to be matched by patterns looking for
// instances of `libclang`.
if filename.contains("-cpp.") {
return None;
}
Some((directory.to_owned(), filename.into()))
})
.collect::<Vec<_>>()
}
/// 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);
// On Windows, `libclang.dll` is usually found in the LLVM `bin` directory
// while `libclang.lib` is usually found in the LLVM `lib` directory. To
// 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") {
let sibling = directory.parent().unwrap().join("bin");
results.extend(search_directory(&sibling, filenames).into_iter());
}
results
}
/// 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 a matching file.
if let Some(parent) = path.parent() {
let filename = path.file_name().unwrap().to_str().unwrap();
let libraries = search_directories(parent, filenames);
if libraries.iter().any(|(_, f)| f == filename) {
return vec![(parent.into(), filename.into())];
}
}
// 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 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"), filenames));
found.extend(search_directories(&directory.join("lib"), filenames));
found.extend(search_directories(&directory.join("lib64"), filenames));
}
// Search the toolchain directory in the directory returned by
// `xcode-select --print-path`.
if cfg!(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");
found.extend(search_directories(&directory, filenames));
}
}
// 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, filenames));
}
}
// 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") {
DIRECTORIES_WINDOWS
} else if cfg!(target_os = "illumos") {
DIRECTORIES_ILLUMOS
} else {
&[]
};
// 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() {
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));
}
}
}
found
}

View File

@@ -0,0 +1,262 @@
// SPDX-License-Identifier: Apache-2.0
use std::env;
use std::fs::File;
use std::io::{self, Error, ErrorKind, Read, Seek, SeekFrom};
use std::path::{Path, PathBuf};
use super::common;
//================================================
// Validation
//================================================
/// Extracts the ELF class from the ELF header in a shared library.
fn parse_elf_header(path: &Path) -> io::Result<u8> {
let mut file = File::open(path)?;
let mut buffer = [0; 5];
file.read_exact(&mut buffer)?;
if buffer[..4] == [127, 69, 76, 70] {
Ok(buffer[4])
} else {
Err(Error::new(ErrorKind::InvalidData, "invalid ELF header"))
}
}
/// Extracts the magic number from the PE header in a shared library.
fn parse_pe_header(path: &Path) -> io::Result<u16> {
let mut file = File::open(path)?;
// Extract the header offset.
let mut buffer = [0; 4];
let start = SeekFrom::Start(0x3C);
file.seek(start)?;
file.read_exact(&mut buffer)?;
let offset = i32::from_le_bytes(buffer);
// Check the validity of the header.
file.seek(SeekFrom::Start(offset as u64))?;
file.read_exact(&mut buffer)?;
if buffer != [80, 69, 0, 0] {
return Err(Error::new(ErrorKind::InvalidData, "invalid PE header"));
}
// Extract the magic number.
let mut buffer = [0; 2];
file.seek(SeekFrom::Current(20))?;
file.read_exact(&mut buffer)?;
Ok(u16::from_le_bytes(buffer))
}
/// 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")) {
let class = parse_elf_header(path).map_err(|e| e.to_string())?;
if cfg!(target_pointer_width = "32") && class != 1 {
return Err("invalid ELF class (64-bit)".into());
}
if cfg!(target_pointer_width = "64") && class != 2 {
return Err("invalid ELF class (32-bit)".into());
}
Ok(())
} else if cfg!(target_os = "windows") {
let magic = parse_pe_header(path).map_err(|e| e.to_string())?;
if cfg!(target_pointer_width = "32") && magic != 267 {
return Err("invalid DLL (64-bit)".into());
}
if cfg!(target_pointer_width = "64") && magic != 523 {
return Err("invalid DLL (32-bit)".into());
}
Ok(())
} else {
Ok(())
}
}
//================================================
// Searching
//================================================
/// Extracts the version components in a `libclang` shared library filename.
fn parse_version(filename: &str) -> Vec<u32> {
let version = if let Some(version) = filename.strip_prefix("libclang.so.") {
version
} else if filename.starts_with("libclang-") {
&filename[9..filename.len() - 3]
} else {
return vec![];
};
version.split('.').map(|s| s.parse().unwrap_or(0)).collect()
}
/// Finds `libclang` shared libraries and returns the paths to, filenames of,
/// and versions of those shared libraries.
fn search_libclang_directories(runtime: bool) -> Result<Vec<(PathBuf, String, Vec<u32>)>, String> {
let mut files = vec![format!(
"{}clang{}",
env::consts::DLL_PREFIX,
env::consts::DLL_SUFFIX
)];
if cfg!(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());
// Some Linux distributions don't create a `libclang.so` symlink and
// don't have versioned files as described above, so we need to look for
// suffix versioned files (e.g., `libclang.so.1`). However, `ld` cannot
// link to these files, so this will only be included when linking at
// runtime.
if runtime {
files.push("libclang.so.*".into());
files.push("libclang-*.so.*".into());
}
}
if cfg!(any(
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") {
// 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());
}
// Find and validate `libclang` shared libraries and collect the versions.
let mut valid = vec![];
let mut invalid = vec![];
for (directory, filename) in common::search_libclang_directories(&files, "LIBCLANG_PATH") {
let path = directory.join(&filename);
match validate_library(&path) {
Ok(()) => {
let version = parse_version(&filename);
valid.push((directory, filename, version))
}
Err(message) => invalid.push(format!("({}: {})", path.display(), message)),
}
}
if !valid.is_empty() {
return Ok(valid);
}
let message = format!(
"couldn't find any valid shared libraries matching: [{}], set the \
`LIBCLANG_PATH` environment variable to a path where one of these files \
can be found (invalid: [{}])",
files
.iter()
.map(|f| format!("'{}'", f))
.collect::<Vec<_>>()
.join(", "),
invalid.join(", "),
);
Err(message)
}
/// Finds the "best" `libclang` shared library and returns the directory and
/// filename of that library.
pub fn find(runtime: bool) -> Result<(PathBuf, String), String> {
search_libclang_directories(runtime)?
.iter()
// We want to find the `libclang` shared library with the highest
// version number, hence `max_by_key` below.
//
// However, in the case where there are multiple such `libclang` shared
// libraries, we want to use the order in which they appeared in the
// list returned by `search_libclang_directories` as a tiebreaker since
// that function returns `libclang` shared libraries in descending order
// of preference by how they were found.
//
// `max_by_key`, perhaps surprisingly, returns the *last* item with the
// maximum key rather than the first which results in the opposite of
// the tiebreaking behavior we want. This is easily fixed by reversing
// the list first.
.rev()
.max_by_key(|f| &f.2)
.cloned()
.map(|(path, filename, _)| (path, filename))
.ok_or_else(|| "unreachable".into())
}
//================================================
// Linking
//================================================
/// Finds and links to a `libclang` shared library.
#[cfg(not(feature = "runtime"))]
pub fn link() {
let cep = common::CommandErrorPrinter::default();
use std::fs;
let (directory, filename) = find(false).unwrap();
println!("cargo:rustc-link-search={}", directory.display());
if cfg!(all(target_os = "windows", target_env = "msvc")) {
// Find the `libclang` stub static library required for the MSVC
// toolchain.
let lib = if !directory.ends_with("bin") {
directory
} else {
directory.parent().unwrap().join("lib")
};
if lib.join("libclang.lib").exists() {
println!("cargo:rustc-link-search={}", lib.display());
} else if lib.join("libclang.dll.a").exists() {
// MSYS and MinGW use `libclang.dll.a` instead of `libclang.lib`.
// It is linkable with the MSVC linker, but Rust doesn't recognize
// the `.a` suffix, so we need to copy it with a different name.
//
// FIXME: Maybe we can just hardlink or symlink it?
let out = env::var("OUT_DIR").unwrap();
fs::copy(
lib.join("libclang.dll.a"),
Path::new(&out).join("libclang.lib"),
)
.unwrap();
println!("cargo:rustc-link-search=native={}", out);
} else {
panic!(
"using '{}', so 'libclang.lib' or 'libclang.dll.a' must be \
available in {}",
filename,
lib.display(),
);
}
println!("cargo:rustc-link-lib=dylib=libclang");
} else {
let name = filename.trim_start_matches("lib");
// Strip extensions and trailing version numbers (e.g., the `.so.7.0` in
// `libclang.so.7.0`).
let name = match name.find(".dylib").or_else(|| name.find(".so")) {
Some(index) => &name[0..index],
None => name,
};
println!("cargo:rustc-link-lib=dylib={}", name);
}
cep.discard();
}

View File

@@ -0,0 +1,140 @@
// SPDX-License-Identifier: Apache-2.0
extern crate glob;
use std::path::{Path, PathBuf};
use glob::Pattern;
use common;
//================================================
// Searching
//================================================
/// Clang static libraries required to link to `libclang` 3.5 and later.
const CLANG_LIBRARIES: &[&str] = &[
"clang",
"clangAST",
"clangAnalysis",
"clangBasic",
"clangDriver",
"clangEdit",
"clangFrontend",
"clangIndex",
"clangLex",
"clangParse",
"clangRewrite",
"clangSema",
"clangSerialization",
];
/// Gets the name of an LLVM or Clang static library from a path.
fn get_library_name(path: &Path) -> Option<String> {
path.file_stem().map(|p| {
let string = p.to_string_lossy();
if let Some(name) = string.strip_prefix("lib") {
name.to_owned()
} else {
string.to_string()
}
})
}
/// Gets the LLVM static libraries required to link to `libclang`.
fn get_llvm_libraries() -> Vec<String> {
common::run_llvm_config(&["--libs"])
.unwrap()
.split_whitespace()
.filter_map(|p| {
// Depending on the version of `llvm-config` in use, listed
// libraries may be in one of two forms, a full path to the library
// or simply prefixed with `-l`.
if let Some(path) = p.strip_prefix("-l") {
Some(path.into())
} else {
get_library_name(Path::new(p))
}
})
.collect()
}
/// Gets the Clang static libraries required to link to `libclang`.
fn get_clang_libraries<P: AsRef<Path>>(directory: P) -> Vec<String> {
// Escape the directory in case it contains characters that have special
// meaning in glob patterns (e.g., `[` or `]`).
let directory = Pattern::escape(directory.as_ref().to_str().unwrap());
let directory = Path::new(&directory);
let pattern = directory.join("libclang*.a").to_str().unwrap().to_owned();
if let Ok(libraries) = glob::glob(&pattern) {
libraries
.filter_map(|l| l.ok().and_then(|l| get_library_name(&l)))
.collect()
} else {
CLANG_LIBRARIES.iter().map(|l| (*l).to_string()).collect()
}
}
/// 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") {
"libclang.lib"
} else {
"libclang.a"
};
let files = common::search_libclang_directories(&[name.into()], "LIBCLANG_STATIC_PATH");
if let Some((directory, _)) = files.into_iter().next() {
directory
} else {
panic!("could not find any static libraries");
}
}
//================================================
// Linking
//================================================
/// Finds and links to `libclang` static libraries.
pub fn link() {
let cep = common::CommandErrorPrinter::default();
let directory = find();
// Specify required Clang static libraries.
println!("cargo:rustc-link-search=native={}", directory.display());
for library in get_clang_libraries(directory) {
println!("cargo:rustc-link-lib=static={}", library);
}
// Determine the shared mode used by LLVM.
let mode = common::run_llvm_config(&["--shared-mode"]).map(|m| m.trim().to_owned());
let prefix = if mode.map_or(false, |m| m == "static") {
"static="
} else {
""
};
// Specify required LLVM static libraries.
println!(
"cargo:rustc-link-search=native={}",
common::run_llvm_config(&["--libdir"]).unwrap().trim_end()
);
for library in get_llvm_libraries() {
println!("cargo:rustc-link-lib={}{}", prefix, library);
}
// Specify required system libraries.
// MSVC doesn't need this, as it tracks dependencies inside `.lib` files.
if cfg!(target_os = "freebsd") {
println!("cargo:rustc-flags=-l ffi -l ncursesw -l c++ -l z");
} else if cfg!(any(target_os = "haiku", target_os = "linux")) {
println!("cargo:rustc-flags=-l ffi -l ncursesw -l stdc++ -l z");
} else if cfg!(target_os = "macos") {
println!("cargo:rustc-flags=-l ffi -l ncurses -l c++ -l z");
}
cep.discard();
}

View File

@@ -0,0 +1 @@
doc-valid-idents = ["FreeBSD"]

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,271 @@
// SPDX-License-Identifier: Apache-2.0
//================================================
// Macros
//================================================
#[cfg(feature = "runtime")]
macro_rules! link {
(
@LOAD:
$(#[doc=$doc:expr])*
#[cfg($cfg:meta)]
fn $name:ident($($pname:ident: $pty:ty), *) $(-> $ret:ty)*
) => (
$(#[doc=$doc])*
#[cfg($cfg)]
pub fn $name(library: &mut super::SharedLibrary) {
let symbol = unsafe { library.library.get(stringify!($name).as_bytes()) }.ok();
library.functions.$name = match symbol {
Some(s) => *s,
None => None,
};
}
#[cfg(not($cfg))]
pub fn $name(_: &mut super::SharedLibrary) {}
);
(
@LOAD:
fn $name:ident($($pname:ident: $pty:ty), *) $(-> $ret:ty)*
) => (
link!(@LOAD: #[cfg(feature = "runtime")] fn $name($($pname: $pty), *) $(-> $ret)*);
);
(
$(
$(#[doc=$doc:expr] #[cfg($cfg:meta)])*
pub fn $name:ident($($pname:ident: $pty:ty), *) $(-> $ret:ty)*;
)+
) => (
use std::cell::{RefCell};
use std::sync::{Arc};
use std::path::{Path, PathBuf};
/// The (minimum) version of a `libclang` shared library.
#[allow(missing_docs)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Version {
V3_5 = 35,
V3_6 = 36,
V3_7 = 37,
V3_8 = 38,
V3_9 = 39,
V4_0 = 40,
V5_0 = 50,
V6_0 = 60,
V7_0 = 70,
V8_0 = 80,
V9_0 = 90,
}
/// The set of functions loaded dynamically.
#[derive(Debug, Default)]
pub struct Functions {
$(
$(#[doc=$doc] #[cfg($cfg)])*
pub $name: Option<unsafe extern fn($($pname: $pty), *) $(-> $ret)*>,
)+
}
/// A dynamically loaded instance of the `libclang` library.
#[derive(Debug)]
pub struct SharedLibrary {
library: libloading::Library,
path: PathBuf,
pub functions: Functions,
}
impl SharedLibrary {
fn new(library: libloading::Library, path: PathBuf) -> Self {
Self { library, path, functions: Functions::default() }
}
/// Returns the path to this `libclang` shared library.
pub fn path(&self) -> &Path {
&self.path
}
/// Returns the (minimum) version of this `libclang` shared library.
///
/// If this returns `None`, it indicates that the version is too old
/// to be supported by this crate (i.e., `3.4` or earlier). If the
/// version of this shared library is more recent than that fully
/// supported by this crate, the most recent fully supported version
/// will be returned.
pub fn version(&self) -> Option<Version> {
macro_rules! check {
($fn:expr, $version:ident) => {
if self.library.get::<unsafe extern fn()>($fn).is_ok() {
return Some(Version::$version);
}
};
}
unsafe {
check!(b"clang_Cursor_isAnonymousRecordDecl", V9_0);
check!(b"clang_Cursor_getObjCPropertyGetterName", V8_0);
check!(b"clang_File_tryGetRealPathName", V7_0);
check!(b"clang_CXIndex_setInvocationEmissionPathOption", V6_0);
check!(b"clang_Cursor_isExternalSymbol", V5_0);
check!(b"clang_EvalResult_getAsLongLong", V4_0);
check!(b"clang_CXXConstructor_isConvertingConstructor", V3_9);
check!(b"clang_CXXField_isMutable", V3_8);
check!(b"clang_Cursor_getOffsetOfField", V3_7);
check!(b"clang_Cursor_getStorageClass", V3_6);
check!(b"clang_Type_getNumTemplateArguments", V3_5);
}
None
}
}
thread_local!(static LIBRARY: RefCell<Option<Arc<SharedLibrary>>> = RefCell::new(None));
/// Returns whether a `libclang` shared library is loaded on this thread.
pub fn is_loaded() -> bool {
LIBRARY.with(|l| l.borrow().is_some())
}
fn with_library<T, F>(f: F) -> Option<T> where F: FnOnce(&SharedLibrary) -> T {
LIBRARY.with(|l| {
match l.borrow().as_ref() {
Some(library) => Some(f(&library)),
_ => None,
}
})
}
$(
#[cfg_attr(feature="cargo-clippy", allow(clippy::missing_safety_doc))]
#[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"))
}).expect("a `libclang` shared library is not loaded on this thread");
f($($pname), *)
}
$(#[doc=$doc] #[cfg($cfg)])*
pub mod $name {
pub fn is_loaded() -> bool {
super::with_library(|l| l.functions.$name.is_some()).unwrap_or(false)
}
}
)+
mod load {
$(link!(@LOAD: $(#[cfg($cfg)])* fn $name($($pname: $pty), *) $(-> $ret)*);)+
}
/// Loads a `libclang` shared library and returns the library instance.
///
/// This function does not attempt to load any functions from the shared library. The caller
/// is responsible for loading the functions they require.
///
/// # Failures
///
/// * a `libclang` shared library could not be found
/// * the `libclang` shared library could not be opened
pub fn load_manually() -> Result<SharedLibrary, String> {
mod build {
pub mod common { include!(concat!(env!("OUT_DIR"), "/common.rs")); }
pub mod dynamic { include!(concat!(env!("OUT_DIR"), "/dynamic.rs")); }
}
let (directory, filename) = build::dynamic::find(true)?;
let path = directory.join(filename);
unsafe {
let library = libloading::Library::new(&path).map_err(|e| {
format!(
"the `libclang` shared library at {} could not be opened: {}",
path.display(),
e,
)
});
let mut library = SharedLibrary::new(library?, path);
$(load::$name(&mut library);)+
Ok(library)
}
}
/// Loads a `libclang` shared library for use in the current thread.
///
/// This functions attempts to load all the functions in the shared library. Whether a
/// function has been loaded can be tested by calling the `is_loaded` function on the
/// module with the same name as the function (e.g., `clang_createIndex::is_loaded()` for
/// the `clang_createIndex` function).
///
/// # Failures
///
/// * a `libclang` shared library could not be found
/// * the `libclang` shared library could not be opened
#[allow(dead_code)]
pub fn load() -> Result<(), String> {
let library = Arc::new(load_manually()?);
LIBRARY.with(|l| *l.borrow_mut() = Some(library));
Ok(())
}
/// Unloads the `libclang` shared library in use in the current thread.
///
/// # Failures
///
/// * a `libclang` shared library is not in use in the current thread
pub fn unload() -> Result<(), String> {
let library = set_library(None);
if library.is_some() {
Ok(())
} else {
Err("a `libclang` shared library is not in use in the current thread".into())
}
}
/// Returns the library instance stored in TLS.
///
/// This functions allows for sharing library instances between threads.
pub fn get_library() -> Option<Arc<SharedLibrary>> {
LIBRARY.with(|l| l.borrow_mut().clone())
}
/// Sets the library instance stored in TLS and returns the previous library.
///
/// This functions allows for sharing library instances between threads.
pub fn set_library(library: Option<Arc<SharedLibrary>>) -> Option<Arc<SharedLibrary>> {
LIBRARY.with(|l| mem::replace(&mut *l.borrow_mut(), library))
}
)
}
#[cfg(not(feature = "runtime"))]
macro_rules! link {
(
$(
$(#[doc=$doc:expr] #[cfg($cfg:meta)])*
pub fn $name:ident($($pname:ident: $pty:ty), *) $(-> $ret:ty)*;
)+
) => (
extern {
$(
$(#[doc=$doc] #[cfg($cfg)])*
pub fn $name($($pname: $pty), *) $(-> $ret)*;
)+
}
$(
$(#[doc=$doc] #[cfg($cfg)])*
pub mod $name {
pub fn is_loaded() -> bool { true }
}
)+
)
}

View File

@@ -0,0 +1,236 @@
// SPDX-License-Identifier: Apache-2.0
//! Provides helper functionality.
use std::path::{Path, PathBuf};
use std::process::Command;
use std::{env, io};
use glob::{self, Pattern};
use libc::c_int;
use super::CXVersion;
//================================================
// Structs
//================================================
/// A `clang` executable.
#[derive(Clone, Debug)]
pub struct Clang {
/// The path to this `clang` executable.
pub path: PathBuf,
/// The version of this `clang` executable if it could be parsed.
pub version: Option<CXVersion>,
/// The directories searched by this `clang` executable for C headers if
/// they could be parsed.
pub c_search_paths: Option<Vec<PathBuf>>,
/// The directories searched by this `clang` executable for C++ headers if
/// they could be parsed.
pub cpp_search_paths: Option<Vec<PathBuf>>,
}
impl Clang {
fn new(path: impl AsRef<Path>, args: &[String]) -> Self {
Self {
path: path.as_ref().into(),
version: parse_version(path.as_ref()),
c_search_paths: parse_search_paths(path.as_ref(), "c", args),
cpp_search_paths: parse_search_paths(path.as_ref(), "c++", args),
}
}
/// Returns a `clang` executable if one can be found.
///
/// If the `CLANG_PATH` environment variable is set, that is the instance of
/// `clang` used. Otherwise, a series of directories are searched. First, if
/// a path is supplied, that is the first directory searched. Then, the
/// directory returned by `llvm-config --bindir` is searched. On macOS
/// systems, `xcodebuild -find clang` will next be queried. Last, the
/// directories in the system's `PATH` are searched.
///
/// ## Cross-compilation
///
/// If target arguments are provided (e.g., `-target` followed by a target
/// like `x86_64-unknown-linux-gnu`) then this method will prefer a
/// target-prefixed instance of `clang` (e.g.,
/// `x86_64-unknown-linux-gnu-clang` for the above example).
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) {
return Some(Clang::new(p, args));
}
}
// Determine the cross-compilation target, if any.
let mut target = None;
for i in 0..args.len() {
if args[i] == "-target" && i + 1 < args.len() {
target = Some(&args[i + 1]);
}
}
// Collect the paths to search for a `clang` executable in.
let mut paths = vec![];
if let Some(path) = path {
paths.push(path.into());
}
if let Ok(path) = run_llvm_config(&["--bindir"]) {
if let Some(line) = path.lines().next() {
paths.push(line.into());
}
}
if cfg!(target_os = "macos") {
if let Ok((path, _)) = run("xcodebuild", &["-find", "clang"]) {
if let Some(line) = path.lines().next() {
paths.push(line.into());
}
}
}
if let Ok(path) = env::var("PATH") {
paths.extend(env::split_paths(&path));
}
// First, look for a target-prefixed `clang` executable.
if let Some(target) = target {
let default = format!("{}-clang{}", target, env::consts::EXE_SUFFIX);
let versioned = format!("{}-clang-[0-9]*{}", target, env::consts::EXE_SUFFIX);
let patterns = &[&default[..], &versioned[..]];
for path in &paths {
if let Some(path) = find(path, patterns) {
return Some(Clang::new(path, args));
}
}
}
// Otherwise, look for any other `clang` executable.
let default = format!("clang{}", env::consts::EXE_SUFFIX);
let versioned = format!("clang-[0-9]*{}", env::consts::EXE_SUFFIX);
let patterns = &[&default[..], &versioned[..]];
for path in paths {
if let Some(path) = find(&path, patterns) {
return Some(Clang::new(path, args));
}
}
None
}
}
//================================================
// Functions
//================================================
/// Returns the first match to the supplied glob patterns in the supplied
/// directory if there are any matches.
fn find(directory: &Path, patterns: &[&str]) -> Option<PathBuf> {
// Escape the directory in case it contains characters that have special
// meaning in glob patterns (e.g., `[` or `]`).
let directory = if let Some(directory) = directory.to_str() {
Path::new(&Pattern::escape(directory)).to_owned()
} else {
return None;
};
for pattern in patterns {
let pattern = directory.join(pattern).to_string_lossy().into_owned();
if let Some(path) = glob::glob(&pattern).ok()?.filter_map(|p| p.ok()).next() {
if path.is_file() && is_executable(&path).unwrap_or(false) {
return Some(path);
}
}
}
None
}
#[cfg(unix)]
fn is_executable(path: &Path) -> io::Result<bool> {
use std::ffi::CString;
use std::os::unix::ffi::OsStrExt;
let path = CString::new(path.as_os_str().as_bytes())?;
unsafe { Ok(libc::access(path.as_ptr(), libc::X_OK) == 0) }
}
#[cfg(not(unix))]
fn is_executable(_: &Path) -> io::Result<bool> {
Ok(true)
}
/// Attempts to run an executable, returning the `stdout` and `stderr` output if
/// successful.
fn run(executable: &str, arguments: &[&str]) -> Result<(String, String), String> {
Command::new(executable)
.args(arguments)
.output()
.map(|o| {
let stdout = String::from_utf8_lossy(&o.stdout).into_owned();
let stderr = String::from_utf8_lossy(&o.stderr).into_owned();
(stdout, stderr)
})
.map_err(|e| format!("could not run executable `{}`: {}", executable, e))
}
/// 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()
}
/// Runs `llvm-config`, returning the `stdout` output if successful.
fn run_llvm_config(arguments: &[&str]) -> Result<String, String> {
let config = env::var("LLVM_CONFIG_PATH").unwrap_or_else(|_| "llvm-config".to_string());
run(&config, arguments).map(|(o, _)| o)
}
/// Parses a version number if possible, ignoring trailing non-digit characters.
fn parse_version_number(number: &str) -> Option<c_int> {
number
.chars()
.take_while(|c| c.is_digit(10))
.collect::<String>()
.parse()
.ok()
}
/// Parses the version from the output of a `clang` executable if possible.
fn parse_version(path: &Path) -> Option<CXVersion> {
let output = run_clang(path, &["--version"]).0;
let start = output.find("version ")? + 8;
let mut numbers = output[start..].split_whitespace().next()?.split('.');
let major = numbers.next().and_then(parse_version_number)?;
let minor = numbers.next().and_then(parse_version_number)?;
let subminor = numbers.next().and_then(parse_version_number).unwrap_or(0);
Some(CXVersion {
Major: major,
Minor: minor,
Subminor: subminor,
})
}
/// Parses the search paths from the output of a `clang` executable if possible.
fn parse_search_paths(path: &Path, language: &str, args: &[String]) -> Option<Vec<PathBuf>> {
let mut clang_args = vec!["-E", "-x", language, "-", "-v"];
clang_args.extend(args.iter().map(|s| &**s));
let output = run_clang(path, &clang_args).1;
let start = output.find("#include <...> search starts here:")? + 34;
let end = output.find("End of search list.")?;
let paths = output[start..end].replace("(framework directory)", "");
Some(
paths
.lines()
.filter(|l| !l.is_empty())
.map(|l| Path::new(l.trim()).into())
.collect(),
)
}

View File

@@ -0,0 +1,6 @@
#ifndef HEADER_H_
#define HEADER_H_
int add(int a, int b);
#endif

View File

@@ -0,0 +1,55 @@
extern crate clang_sys;
extern crate libc;
use std::ptr;
use clang_sys::*;
use libc::c_char;
fn parse() {
unsafe {
let index = clang_createIndex(0, 0);
assert!(!index.is_null());
let tu = clang_parseTranslationUnit(
index,
"tests/header.h\0".as_ptr() as *const c_char,
ptr::null_mut(),
0,
ptr::null_mut(),
0,
0,
);
assert!(!tu.is_null());
}
}
#[cfg(feature = "runtime")]
#[test]
fn test() {
load().unwrap();
let library = get_library().unwrap();
println!("{:?} ({:?})", library.version(), library.path());
parse();
unload().unwrap();
}
#[cfg(not(feature = "runtime"))]
#[test]
fn test() {
parse();
}
#[test]
fn test_support() {
let clang = support::Clang::find(None, &[]).unwrap();
println!("{:?}", clang);
}
#[test]
fn test_support_target() {
let args = &["-target".into(), "x86_64-unknown-linux-gnu".into()];
let clang = support::Clang::find(None, args).unwrap();
println!("{:?}", clang);
}