diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-06-15 21:43:36 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-06-15 21:43:36 +0000 |
commit | 791dccd2f33bb084a5d7a170ed88d1977497fcda (patch) | |
tree | d5e2f7181bd9c53349fc833a43987aacbb1a721f | |
parent | 3c5fa164b47774e31e4597d2d39d0b9e9183c24d (diff) | |
parent | 6cf6b955d4f3c9f8c38107d846decb0bf4f7a1ac (diff) | |
download | clang-sys-android12-mainline-tzdata3-release.tar.gz |
Snap for 8730993 from 6cf6b955d4f3c9f8c38107d846decb0bf4f7a1ac to mainline-tzdata3-releaseaml_tz3_314012070aml_tz3_314012050aml_tz3_314012010aml_tz3_313110000aml_tz3_312511020aml_tz3_312511010aml_tz3_312410020aml_tz3_312410010android12-mainline-tzdata3-releaseaml_tz3_314012010
Change-Id: Ibad18ad422141ca1d4b04ad7f679a3edd453fd91
-rw-r--r-- | .cargo_vcs_info.json | 7 | ||||
-rw-r--r-- | .github/workflows/ci.yml | 15 | ||||
-rw-r--r-- | .github/workflows/ssh.yml | 40 | ||||
-rw-r--r-- | Android.bp | 10 | ||||
-rw-r--r-- | CHANGELOG.md | 42 | ||||
-rw-r--r-- | Cargo.toml | 15 | ||||
-rw-r--r-- | Cargo.toml.orig | 6 | ||||
-rw-r--r-- | METADATA | 10 | ||||
-rw-r--r-- | README.md | 101 | ||||
-rw-r--r-- | TEST_MAPPING | 15 | ||||
-rw-r--r-- | build.rs | 29 | ||||
-rw-r--r-- | build/common.rs | 288 | ||||
-rw-r--r-- | build/dynamic.rs | 70 | ||||
-rw-r--r-- | build/static.rs | 90 | ||||
-rw-r--r-- | cargo2android.json | 5 | ||||
-rw-r--r-- | out/common.rs | 288 | ||||
-rw-r--r-- | out/dynamic.rs | 70 | ||||
-rw-r--r-- | src/lib.rs | 114 | ||||
-rw-r--r-- | src/link.rs | 14 | ||||
-rw-r--r-- | src/support.rs | 95 | ||||
-rw-r--r-- | tests/lib.rs | 7 |
21 files changed, 589 insertions, 742 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json index ac25bc0..b4474d7 100644 --- a/.cargo_vcs_info.json +++ b/.cargo_vcs_info.json @@ -1,6 +1,5 @@ { "git": { - "sha1": "8090d6853624a04b859e8ab2e467bae88a8d4ef6" - }, - "path_in_vcs": "" -}
\ No newline at end of file + "sha1": "fae8e5e15f060bc1fb11c26eac168eaf8a50632b" + } +} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5bd0a2a..993f360 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,17 +15,24 @@ jobs: strategy: matrix: os: [macos-latest, ubuntu-latest, windows-latest] - clang: [["13.0", "clang_13_0"]] + clang: [["11.0", "clang_11_0"]] rust: ["1.40.0"] steps: - name: Checkout Repository uses: actions/checkout@v2 # LLVM and Clang + - name: Cache LLVM and Clang + id: cache-llvm + uses: actions/cache@v2 + with: + path: ${{ runner.temp }}/llvm-${{ matrix.clang[0] }} + key: ${{ matrix.os }}-llvm-${{ matrix.clang[0] }} - name: Install LLVM and Clang uses: KyleMayes/install-llvm-action@v1 with: version: ${{ matrix.clang[0] }} directory: ${{ runner.temp }}/llvm-${{ matrix.clang[0] }} + cached: ${{ steps.cache-llvm.outputs.cache-hit }} # Rust - name: Install Rust uses: actions-rs/toolchain@v1 @@ -34,11 +41,17 @@ jobs: # Test - name: Cargo Test (Dynamic) uses: actions-rs/cargo@v1 + env: + LIBCLANG_PATH: ${{ runner.temp }}/llvm-${{ matrix.clang[0] }}/lib + LLVM_CONFIG_PATH: ${{ runner.temp }}/llvm-${{ matrix.clang[0] }}/bin/llvm-config with: command: test args: --verbose --features ${{ matrix.clang[1] }} -- --nocapture - name: Cargo Test (Runtime) uses: actions-rs/cargo@v1 + env: + LIBCLANG_PATH: ${{ runner.temp }}/llvm-${{ matrix.clang[0] }}/lib + LLVM_CONFIG_PATH: ${{ runner.temp }}/llvm-${{ matrix.clang[0] }}/bin/llvm-config with: command: test args: --verbose --features "${{ matrix.clang[1] }} runtime" -- --nocapture diff --git a/.github/workflows/ssh.yml b/.github/workflows/ssh.yml deleted file mode 100644 index 5dfc251..0000000 --- a/.github/workflows/ssh.yml +++ /dev/null @@ -1,40 +0,0 @@ -name: SSH - -on: - workflow_dispatch: - inputs: - os: - description: "Operating System" - required: true - default: "ubuntu-latest" - -jobs: - ssh: - name: SSH - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [macos-latest, ubuntu-latest, windows-latest] - clang: [["13.0", "clang_13_0"]] - rust: ["1.40.0"] - steps: - - name: Checkout Repository - uses: actions/checkout@v2 - if: github.event.inputs.os == matrix.os - # LLVM and Clang - - name: Install LLVM and Clang - uses: KyleMayes/install-llvm-action@v1 - if: github.event.inputs.os == matrix.os - with: - version: ${{ matrix.clang[0] }} - directory: ${{ runner.temp }}/llvm-${{ matrix.clang[0] }} - # Rust - - name: Install Rust - uses: actions-rs/toolchain@v1 - if: github.event.inputs.os == matrix.os - with: - toolchain: ${{ matrix.rust }} - # SSH - - name: Enable SSH - uses: mxschmitt/action-tmate@v3 - if: github.event.inputs.os == matrix.os @@ -1,4 +1,4 @@ -// This file is generated by cargo2android.py --config cargo2android.json. +// This file is generated by cargo2android.py --run --features=runtime,clang_10_0 --dependencies --copy-out. // Do not modify this file as changes will be overridden on upgrade. package { @@ -31,8 +31,6 @@ genrule { rust_library_host { name: "libclang_sys", crate_name: "clang_sys", - cargo_env_compat: true, - cargo_pkg_version: "1.3.1", srcs: [ "src/lib.rs", ":copy_clang-sys_build_out", @@ -60,3 +58,9 @@ rust_library_host { "liblibloading", ], } + +// dependent_library ["feature_list"] +// cfg-if-1.0.0 +// glob-0.3.0 +// libc-0.2.86 +// libloading-0.7.0 diff --git a/CHANGELOG.md b/CHANGELOG.md index 681cde4..a4940fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,45 +1,3 @@ -## [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 @@ -3,15 +3,16 @@ # 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. +# 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. +# If you believe there's an error in this file please file an +# issue against the rust-lang/cargo repository. If you're +# editing this file be aware that the upstream Cargo.toml +# will likely look very different (and much more reasonable) [package] name = "clang-sys" -version = "1.3.1" +version = "1.1.1" authors = ["Kyle Mayes <kyle@mayeses.com>"] build = "build.rs" links = "clang" @@ -21,7 +22,7 @@ readme = "README.md" license = "Apache-2.0" repository = "https://github.com/KyleMayes/clang-sys" [package.metadata.docs.rs] -features = ["clang_13_0", "runtime"] +features = ["clang_10_0", "runtime"] [dependencies.glob] version = "0.3" @@ -38,8 +39,6 @@ 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_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 a78a106..f7abf3c 100644 --- a/Cargo.toml.orig +++ b/Cargo.toml.orig @@ -3,7 +3,7 @@ name = "clang-sys" authors = ["Kyle Mayes <kyle@mayeses.com>"] -version = "1.3.1" +version = "1.1.1" readme = "README.md" license = "Apache-2.0" @@ -31,8 +31,6 @@ clang_8_0 = ["clang_7_0"] clang_9_0 = ["clang_8_0"] clang_10_0 = ["clang_9_0"] clang_11_0 = ["clang_10_0"] -clang_12_0 = ["clang_11_0"] -clang_13_0 = ["clang_12_0"] runtime = ["libloading"] static = [] @@ -49,4 +47,4 @@ glob = "0.3" [package.metadata.docs.rs] -features = ["clang_13_0", "runtime"] +features = ["clang_10_0", "runtime"] @@ -7,13 +7,13 @@ third_party { } url { type: ARCHIVE - value: "https://static.crates.io/crates/clang-sys/clang-sys-1.3.1.crate" + value: "https://static.crates.io/crates/clang-sys/clang-sys-1.1.1.crate" } - version: "1.3.1" + version: "1.1.1" license_type: NOTICE last_upgrade_date { - year: 2022 - month: 3 - day: 1 + year: 2021 + month: 2 + day: 22 } } @@ -2,46 +2,76 @@ [![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://github.com/KyleMayes/clang-sys/workflows/CI/badge.svg?branch=master)](https://github.com/KyleMayes/clang-sys/actions?query=workflow%3ACI) 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). +If you are interested in a Rust wrapper for these bindings, see +[clang-rs](https://github.com/KyleMayes/clang-rs). + +Supported on the stable, beta, and nightly Rust channels.<br/> +Minimum supported Rust version: **1.40.0** Released under the Apache License 2.0. -## [Documentation](https://docs.rs/clang-sys) +## Documentation -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_13_0`), neither of which are enabled by default. +There are two versions of the documentation, one for the API exposed when +linking dynamically or statically and one for the API exposed when linking at +runtime (see the +[Dependencies](https://github.com/KyleMayes/clang-sys#dependencies) section +of the README for more information on the linking options). -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. +The only difference between the APIs exposed is that when linking at runtime a +few additional types and functions are exposed to manage the loaded `libclang` +shared library. -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. +* Runtime - [Documentation](https://kylemayes.github.io/clang-sys/runtime/clang_sys) +* Dynamic / Static - [Documentation](https://kylemayes.github.io/clang-sys/default/clang_sys) ## Supported Versions -To target a version of `libclang`, enable a Cargo features such as one of the following: +To target a version of `libclang`, enable one of the following Cargo features: * `clang_3_5` - requires `libclang` 3.5 or later * `clang_3_6` - requires `libclang` 3.6 or later -* etc... -* `clang_12_0` - requires `libclang` 12.0 or later -* `clang_13_0` - requires `libclang` 13.0 or later - -If you do not enable one of these features, the API provided by `libclang` 3.5 will be available by default. +* `clang_3_7` - requires `libclang` 3.7 or later +* `clang_3_8` - requires `libclang` 3.8 or later +* `clang_3_9` - requires `libclang` 3.9 or later +* `clang_4_0` - requires `libclang` 4.0 or later +* `clang_5_0` - requires `libclang` 5.0 or later +* `clang_6_0` - requires `libclang` 6.0 or later +* `clang_7_0` - requires `libclang` 7.0 or later +* `clang_8_0` - requires `libclang` 8.0 or later +* `clang_9_0` - requires `libclang` 9.0 or later +* `clang_10_0` - requires `libclang` 10.0 or later +* `clang_11_0` - requires `libclang` 11.0 or later + +If you do not enable one of these features, the API provided by `libclang` 3.5 will be available by +default. ## 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. +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). +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. +**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: +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` @@ -49,17 +79,23 @@ This crate supports finding versioned instances of `libclang.so` (e.g.,`libclang 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 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. +**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: +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 +* `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 @@ -74,14 +110,25 @@ The following environment variables, if set, are used by this crate to find the * 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 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. +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. +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. +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. diff --git a/TEST_MAPPING b/TEST_MAPPING index e4ec3b3..7de080a 100644 --- a/TEST_MAPPING +++ b/TEST_MAPPING @@ -1,24 +1,11 @@ // Generated by update_crate_tests.py for tests that depend on this crate. { - "imports": [ - { - "path": "external/rust/crates/libsqlite3-sys" - } - ], "presubmit": [ { - "name": "keystore2_test" + "name": "libsqlite3-sys_device_test_src_lib" }, { - "name": "legacykeystore_test" - } - ], - "presubmit-rust": [ - { "name": "keystore2_test" - }, - { - "name": "legacykeystore_test" } ] } @@ -1,11 +1,23 @@ -// SPDX-License-Identifier: Apache-2.0 +// Copyright 2016 Kyle Mayes +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. -//! Finds `libclang` static or shared libraries and links to them. +//! Finds `libclang` static or dynamic 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. +//! find the required static or dynamic libraries. //! //! * `LLVM_CONFIG_PATH` - provides a path to an `llvm-config` executable //! * `LIBCLANG_PATH` - provides a path to a directory containing a `libclang` @@ -24,9 +36,9 @@ pub mod common; #[path = "build/dynamic.rs"] pub mod dynamic; #[path = "build/static.rs"] -pub mod r#static; +pub mod static_; -/// Copies a file. +/// Copy the file from the supplied source to the supplied destination. #[cfg(feature = "runtime")] fn copy(source: &str, destination: &Path) { use std::fs::File; @@ -43,8 +55,7 @@ fn copy(source: &str, destination: &Path) { .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. +/// Generates the finding and linking code so that it may be used at runtime. #[cfg(feature = "runtime")] fn main() { use std::env; @@ -58,11 +69,11 @@ fn main() { copy("build/dynamic.rs", &Path::new(&out).join("dynamic.rs")); } -/// Finds and links to the required libraries dynamically or statically. +/// Finds and links to the required libraries. #[cfg(not(feature = "runtime"))] fn main() { if cfg!(feature = "static") { - r#static::link(); + static_::link(); } else { dynamic::link(); } diff --git a/build/common.rs b/build/common.rs index bc720ca..265a0cf 100644 --- a/build/common.rs +++ b/build/common.rs @@ -1,4 +1,16 @@ -// SPDX-License-Identifier: Apache-2.0 +// Copyright 2018 Kyle Mayes +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. extern crate glob; @@ -8,43 +20,87 @@ use std::env; use std::path::{Path, PathBuf}; use std::process::Command; -use glob::{MatchOptions, Pattern}; +use glob::MatchOptions; -//================================================ -// Commands -//================================================ +/// `libclang` directory patterns for FreeBSD and Linux. +const DIRECTORIES_LINUX: &[&str] = &[ + "/usr/lib*", + "/usr/lib*/*", + "/usr/lib*/*/*", + "/usr/local/lib*", + "/usr/local/lib*/*", + "/usr/local/lib*/*/*", + "/usr/local/llvm*/lib*", +]; + +/// `libclang` directory patterns for macOS. +const DIRECTORIES_MACOS: &[&str] = &[ + "/usr/local/opt/llvm*/lib", + "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib", + "/Library/Developer/CommandLineTools/usr/lib", + "/usr/local/opt/llvm*/lib/llvm*/lib", +]; + +/// `libclang` directory patterns for Windows. +const DIRECTORIES_WINDOWS: &[&str] = &[ + "C:\\LLVM\\lib", + "C:\\Program Files*\\LLVM\\lib", + "C:\\MSYS*\\MinGW*\\lib", + // LLVM + Clang can be installed as a component of Visual Studio. + // https://github.com/KyleMayes/clang-sys/issues/121 + "C:\\Program Files*\\Microsoft Visual Studio\\*\\BuildTools\\VC\\Tools\\Llvm\\**\\bin", +]; thread_local! { - /// The errors encountered by the build script while executing commands. + /// The errors encountered when attempting to execute console 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, - )) - }); +/// Executes the supplied console command, returning the `stdout` output if the +/// command was successfully executed. +fn run_command(name: &str, command: &str, arguments: &[&str]) -> Option<String> { + macro_rules! error { + ($error:expr) => {{ + COMMAND_ERRORS.with(|e| e.borrow_mut() + .entry(name.into()) + .or_insert_with(Vec::new) + .push(format!( + "couldn't execute `{} {}` ({})", + command, + arguments.join(" "), + $error, + ))); + }}; + } + + let output = match Command::new(command).args(arguments).output() { + Ok(output) => output, + Err(error) => { + error!(format!("error: {}", error)); + return None; + } + }; + + if !output.status.success() { + error!(format!("exit code: {}", output.status)); + return None; + } + + Some(String::from_utf8_lossy(&output.stdout).into_owned()) +} + +/// Executes `llvm-config`, returning the `stdout` output if the command was +/// successfully executed. +pub fn run_llvm_config(arguments: &[&str]) -> Option<String> { + let path = env::var("LLVM_CONFIG_PATH").unwrap_or_else(|_| "llvm-config".into()); + run_command("llvm-config", &path, arguments) } -/// A struct that prints 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. +/// A struct that prints errors encountered when attempting to execute console +/// commands on drop if not discarded. #[derive(Default)] pub struct CommandErrorPrinter { - discard: bool, + discard: bool } impl CommandErrorPrinter { @@ -67,11 +123,7 @@ impl Drop for CommandErrorPrinter { 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 "), + errors.iter().map(|e| format!("\"{}\"", e)).collect::<Vec<_>>().join("\n "), ) } @@ -81,127 +133,34 @@ impl Drop for CommandErrorPrinter { 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 "), + 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", -]; - -//================================================ -// 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. +/// Returns the paths to and the filenames of the files matching the supplied +/// filename patterns in the supplied directory. 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. + // Join the directory to the filename patterns to obtain the path patterns. let paths = filenames .iter() - .map(|f| directory.join(f).to_str().unwrap().to_owned()); + .filter_map(|f| directory.join(f).to_str().map(ToOwned::to_owned)); - // Prevent wildcards from matching path separators to ensure that the search - // is limited to the specified directory. + // Prevent wildcards from matching path separators. let mut options = MatchOptions::new(); options.require_literal_separator = true; paths - .map(|p| glob::glob_with(&p, options)) - .filter_map(Result::ok) - .flatten() + .flat_map(|p| { + if let Ok(paths) = glob::glob_with(&p, options) { + paths.filter_map(Result::ok).collect() + } else { + vec![] + } + }) .filter_map(|p| { - let path = p.ok()?; - let filename = path.file_name()?.to_str().unwrap(); + let filename = p.file_name().and_then(|f| f.to_str())?; // The `libclang_shared` library has been renamed to `libclang-cpp` // in Clang 10. This can cause instances of this library (e.g., @@ -216,9 +175,9 @@ fn search_directory(directory: &Path, filenames: &[String]) -> Vec<(PathBuf, Str .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. +/// Returns the paths to and the filenames of the files matching the supplied +/// filename patterns in the supplied directory, checking any relevant sibling +/// directories. fn search_directories(directory: &Path, filenames: &[String]) -> Vec<(PathBuf, String)> { let mut results = search_directory(directory, filenames); @@ -235,57 +194,54 @@ fn search_directories(directory: &Path, filenames: &[String]) -> Vec<(PathBuf, S 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. +/// Returns the paths to and the filenames of the `libclang` static or dynamic +/// libraries matching the supplied filename patterns. +pub fn search_libclang_directories(files: &[String], variable: &str) -> Vec<(PathBuf, String)> { + // Use the path provided by the relevant environment variable. if let Ok(path) = env::var(variable).map(|d| Path::new(&d).to_path_buf()) { - // Check if the path is a matching file. + // Check if the path is referring to a matching file already. if let Some(parent) = path.parent() { let filename = path.file_name().unwrap().to_str().unwrap(); - let libraries = search_directories(parent, filenames); + let libraries = search_directories(parent, files); 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); + return search_directories(&path, files); } let mut found = vec![]; - // Search the `bin` and `lib` directories in the directory returned by + // Search the `bin` and `lib` directories in directory provided 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)); + found.extend(search_directories(&directory.join("bin"), files)); + found.extend(search_directories(&directory.join("lib"), files)); + found.extend(search_directories(&directory.join("lib64"), files)); } - // Search the toolchain directory in the directory returned by + // Search the toolchain directory in the directory provided by // `xcode-select --print-path`. if cfg!(target_os = "macos") { - if let Some(output) = run_xcode_select(&["--print-path"]) { + if let Some(output) = run_command("xcode-select", "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)); + found.extend(search_directories(&directory, files)); } } - // Search the directories in the `LD_LIBRARY_PATH` environment variable. + // Search the directories provided by 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)); + for directory in path.split(':').map(Path::new) { + found.extend(search_directories(&directory, files)); } } // Determine the `libclang` directory patterns. - let directories = if cfg!(target_os = "haiku") { - DIRECTORIES_HAIKU - } else if cfg!(any(target_os = "linux", target_os = "freebsd")) { + let directories = if cfg!(any(target_os = "freebsd", target_os = "linux")) { DIRECTORIES_LINUX } else if cfg!(target_os = "macos") { DIRECTORIES_MACOS @@ -302,7 +258,7 @@ pub fn search_libclang_directories(filenames: &[String], variable: &str) -> Vec< 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.extend(search_directories(&directory, files)); } } } diff --git a/build/dynamic.rs b/build/dynamic.rs index 39247c8..c15973c 100644 --- a/build/dynamic.rs +++ b/build/dynamic.rs @@ -1,4 +1,16 @@ -// SPDX-License-Identifier: Apache-2.0 +// Copyright 2018 Kyle Mayes +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. use std::env; use std::fs::File; @@ -7,11 +19,7 @@ use std::path::{Path, PathBuf}; use super::common; -//================================================ -// Validation -//================================================ - -/// Extracts the ELF class from the ELF header in a shared library. +/// Returns the ELF class from the ELF header in the supplied file. fn parse_elf_header(path: &Path) -> io::Result<u8> { let mut file = File::open(path)?; let mut buffer = [0; 5]; @@ -23,34 +31,34 @@ fn parse_elf_header(path: &Path) -> io::Result<u8> { } } -/// Extracts the magic number from the PE header in a shared library. +/// Returns the magic number from the PE header in the supplied file. fn parse_pe_header(path: &Path) -> io::Result<u16> { let mut file = File::open(path)?; - // Extract the header offset. + // Determine 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. + // Determine 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. + // Find 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")) { +/// Validates the header for the supplied `libclang` shared library. +fn validate_header(path: &Path) -> Result<(), String> { + if cfg!(any(target_os = "freebsd", target_os = "linux")) { let class = parse_elf_header(path).map_err(|e| e.to_string())?; if cfg!(target_pointer_width = "32") && class != 1 { @@ -79,14 +87,11 @@ fn validate_library(path: &Path) -> Result<(), String> { } } -//================================================ -// Searching -//================================================ - -/// Extracts the version components in a `libclang` shared library filename. +/// Returns the components of the version in the supplied `libclang` shared +// library filename. fn parse_version(filename: &str) -> Vec<u32> { - let version = if let Some(version) = filename.strip_prefix("libclang.so.") { - version + let version = if filename.starts_with("libclang.so.") { + &filename[12..] } else if filename.starts_with("libclang-") { &filename[9..filename.len() - 3] } else { @@ -96,8 +101,8 @@ fn parse_version(filename: &str) -> Vec<u32> { 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. +/// Returns the paths to, the filenames, and the versions of the `libclang` +// shared libraries. fn search_libclang_directories(runtime: bool) -> Result<Vec<(PathBuf, String, Vec<u32>)>, String> { let mut files = vec![format!( "{}clang{}", @@ -122,10 +127,9 @@ 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", + target_os = "freebsd", + target_os = "netbsd" )) { // Some BSD distributions don't create a `libclang.so` symlink either, // but use a different naming scheme for versioned files (e.g., @@ -139,12 +143,12 @@ fn search_libclang_directories(runtime: bool) -> Result<Vec<(PathBuf, String, Ve files.push("libclang.dll".into()); } - // Find and validate `libclang` shared libraries and collect the versions. + // Validate the `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) { + match validate_header(&path) { Ok(()) => { let version = parse_version(&filename); valid.push((directory, filename, version)) @@ -172,8 +176,8 @@ fn search_libclang_directories(runtime: bool) -> Result<Vec<(PathBuf, String, Ve Err(message) } -/// Finds the "best" `libclang` shared library and returns the directory and -/// filename of that library. +/// Returns the directory and filename of the "best" available `libclang` shared +/// library. pub fn find(runtime: bool) -> Result<(PathBuf, String), String> { search_libclang_directories(runtime)? .iter() @@ -197,11 +201,7 @@ pub fn find(runtime: bool) -> Result<(PathBuf, String), String> { .ok_or_else(|| "unreachable".into()) } -//================================================ -// Linking -//================================================ - -/// Finds and links to a `libclang` shared library. +/// Find and link to `libclang` dynamically. #[cfg(not(feature = "runtime"))] pub fn link() { let cep = common::CommandErrorPrinter::default(); @@ -252,7 +252,7 @@ pub fn link() { // `libclang.so.7.0`). let name = match name.find(".dylib").or_else(|| name.find(".so")) { Some(index) => &name[0..index], - None => name, + None => &name, }; println!("cargo:rustc-link-lib=dylib={}", name); diff --git a/build/static.rs b/build/static.rs index 6af914f..83a8185 100644 --- a/build/static.rs +++ b/build/static.rs @@ -1,47 +1,36 @@ -// SPDX-License-Identifier: Apache-2.0 +// Copyright 2018 Kyle Mayes +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. 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. +/// Returns the name of an LLVM or Clang library from a path to such a library. 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() + if string.starts_with("lib") { + string[3..].to_owned() } else { string.to_string() } }) } -/// Gets the LLVM static libraries required to link to `libclang`. +/// Returns the LLVM libraries required to link to `libclang` statically. fn get_llvm_libraries() -> Vec<String> { common::run_llvm_config(&["--libs"]) .unwrap() @@ -50,8 +39,8 @@ fn get_llvm_libraries() -> Vec<String> { // 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()) + if p.starts_with("-l") { + Some(p[2..].into()) } else { get_library_name(Path::new(p)) } @@ -59,14 +48,30 @@ fn get_llvm_libraries() -> Vec<String> { .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); +/// Clang libraries required to link to `libclang` 3.5 and later statically. +const CLANG_LIBRARIES: &[&str] = &[ + "clang", + "clangAST", + "clangAnalysis", + "clangBasic", + "clangDriver", + "clangEdit", + "clangFrontend", + "clangIndex", + "clangLex", + "clangParse", + "clangRewrite", + "clangSema", + "clangSerialization", +]; - let pattern = directory.join("libclang*.a").to_str().unwrap().to_owned(); +/// Returns the Clang libraries required to link to `libclang` statically. +fn get_clang_libraries<P: AsRef<Path>>(directory: P) -> Vec<String> { + let pattern = directory + .as_ref() + .join("libclang*.a") + .to_string_lossy() + .to_string(); if let Ok(libraries) = glob::glob(&pattern) { libraries .filter_map(|l| l.ok().and_then(|l| get_library_name(&l))) @@ -76,8 +81,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. +/// Returns a directory containing `libclang` static libraries. fn find() -> PathBuf { let name = if cfg!(target_os = "windows") { "libclang.lib" @@ -93,11 +97,7 @@ fn find() -> PathBuf { } } -//================================================ -// Linking -//================================================ - -/// Finds and links to `libclang` static libraries. +/// Find and link to `libclang` statically. pub fn link() { let cep = common::CommandErrorPrinter::default(); @@ -130,7 +130,7 @@ pub fn link() { // 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")) { + } else if cfg!(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"); diff --git a/cargo2android.json b/cargo2android.json deleted file mode 100644 index a0f1a8e..0000000 --- a/cargo2android.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "copy-out": true, - "features": "runtime,clang_10_0", - "run": true -}
\ No newline at end of file diff --git a/out/common.rs b/out/common.rs index bc720ca..265a0cf 100644 --- a/out/common.rs +++ b/out/common.rs @@ -1,4 +1,16 @@ -// SPDX-License-Identifier: Apache-2.0 +// Copyright 2018 Kyle Mayes +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. extern crate glob; @@ -8,43 +20,87 @@ use std::env; use std::path::{Path, PathBuf}; use std::process::Command; -use glob::{MatchOptions, Pattern}; +use glob::MatchOptions; -//================================================ -// Commands -//================================================ +/// `libclang` directory patterns for FreeBSD and Linux. +const DIRECTORIES_LINUX: &[&str] = &[ + "/usr/lib*", + "/usr/lib*/*", + "/usr/lib*/*/*", + "/usr/local/lib*", + "/usr/local/lib*/*", + "/usr/local/lib*/*/*", + "/usr/local/llvm*/lib*", +]; + +/// `libclang` directory patterns for macOS. +const DIRECTORIES_MACOS: &[&str] = &[ + "/usr/local/opt/llvm*/lib", + "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib", + "/Library/Developer/CommandLineTools/usr/lib", + "/usr/local/opt/llvm*/lib/llvm*/lib", +]; + +/// `libclang` directory patterns for Windows. +const DIRECTORIES_WINDOWS: &[&str] = &[ + "C:\\LLVM\\lib", + "C:\\Program Files*\\LLVM\\lib", + "C:\\MSYS*\\MinGW*\\lib", + // LLVM + Clang can be installed as a component of Visual Studio. + // https://github.com/KyleMayes/clang-sys/issues/121 + "C:\\Program Files*\\Microsoft Visual Studio\\*\\BuildTools\\VC\\Tools\\Llvm\\**\\bin", +]; thread_local! { - /// The errors encountered by the build script while executing commands. + /// The errors encountered when attempting to execute console 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, - )) - }); +/// Executes the supplied console command, returning the `stdout` output if the +/// command was successfully executed. +fn run_command(name: &str, command: &str, arguments: &[&str]) -> Option<String> { + macro_rules! error { + ($error:expr) => {{ + COMMAND_ERRORS.with(|e| e.borrow_mut() + .entry(name.into()) + .or_insert_with(Vec::new) + .push(format!( + "couldn't execute `{} {}` ({})", + command, + arguments.join(" "), + $error, + ))); + }}; + } + + let output = match Command::new(command).args(arguments).output() { + Ok(output) => output, + Err(error) => { + error!(format!("error: {}", error)); + return None; + } + }; + + if !output.status.success() { + error!(format!("exit code: {}", output.status)); + return None; + } + + Some(String::from_utf8_lossy(&output.stdout).into_owned()) +} + +/// Executes `llvm-config`, returning the `stdout` output if the command was +/// successfully executed. +pub fn run_llvm_config(arguments: &[&str]) -> Option<String> { + let path = env::var("LLVM_CONFIG_PATH").unwrap_or_else(|_| "llvm-config".into()); + run_command("llvm-config", &path, arguments) } -/// A struct that prints 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. +/// A struct that prints errors encountered when attempting to execute console +/// commands on drop if not discarded. #[derive(Default)] pub struct CommandErrorPrinter { - discard: bool, + discard: bool } impl CommandErrorPrinter { @@ -67,11 +123,7 @@ impl Drop for CommandErrorPrinter { 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 "), + errors.iter().map(|e| format!("\"{}\"", e)).collect::<Vec<_>>().join("\n "), ) } @@ -81,127 +133,34 @@ impl Drop for CommandErrorPrinter { 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 "), + 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", -]; - -//================================================ -// 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. +/// Returns the paths to and the filenames of the files matching the supplied +/// filename patterns in the supplied directory. 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. + // Join the directory to the filename patterns to obtain the path patterns. let paths = filenames .iter() - .map(|f| directory.join(f).to_str().unwrap().to_owned()); + .filter_map(|f| directory.join(f).to_str().map(ToOwned::to_owned)); - // Prevent wildcards from matching path separators to ensure that the search - // is limited to the specified directory. + // Prevent wildcards from matching path separators. let mut options = MatchOptions::new(); options.require_literal_separator = true; paths - .map(|p| glob::glob_with(&p, options)) - .filter_map(Result::ok) - .flatten() + .flat_map(|p| { + if let Ok(paths) = glob::glob_with(&p, options) { + paths.filter_map(Result::ok).collect() + } else { + vec![] + } + }) .filter_map(|p| { - let path = p.ok()?; - let filename = path.file_name()?.to_str().unwrap(); + let filename = p.file_name().and_then(|f| f.to_str())?; // The `libclang_shared` library has been renamed to `libclang-cpp` // in Clang 10. This can cause instances of this library (e.g., @@ -216,9 +175,9 @@ fn search_directory(directory: &Path, filenames: &[String]) -> Vec<(PathBuf, Str .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. +/// Returns the paths to and the filenames of the files matching the supplied +/// filename patterns in the supplied directory, checking any relevant sibling +/// directories. fn search_directories(directory: &Path, filenames: &[String]) -> Vec<(PathBuf, String)> { let mut results = search_directory(directory, filenames); @@ -235,57 +194,54 @@ fn search_directories(directory: &Path, filenames: &[String]) -> Vec<(PathBuf, S 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. +/// Returns the paths to and the filenames of the `libclang` static or dynamic +/// libraries matching the supplied filename patterns. +pub fn search_libclang_directories(files: &[String], variable: &str) -> Vec<(PathBuf, String)> { + // Use the path provided by the relevant environment variable. if let Ok(path) = env::var(variable).map(|d| Path::new(&d).to_path_buf()) { - // Check if the path is a matching file. + // Check if the path is referring to a matching file already. if let Some(parent) = path.parent() { let filename = path.file_name().unwrap().to_str().unwrap(); - let libraries = search_directories(parent, filenames); + let libraries = search_directories(parent, files); 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); + return search_directories(&path, files); } let mut found = vec![]; - // Search the `bin` and `lib` directories in the directory returned by + // Search the `bin` and `lib` directories in directory provided 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)); + found.extend(search_directories(&directory.join("bin"), files)); + found.extend(search_directories(&directory.join("lib"), files)); + found.extend(search_directories(&directory.join("lib64"), files)); } - // Search the toolchain directory in the directory returned by + // Search the toolchain directory in the directory provided by // `xcode-select --print-path`. if cfg!(target_os = "macos") { - if let Some(output) = run_xcode_select(&["--print-path"]) { + if let Some(output) = run_command("xcode-select", "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)); + found.extend(search_directories(&directory, files)); } } - // Search the directories in the `LD_LIBRARY_PATH` environment variable. + // Search the directories provided by 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)); + for directory in path.split(':').map(Path::new) { + found.extend(search_directories(&directory, files)); } } // Determine the `libclang` directory patterns. - let directories = if cfg!(target_os = "haiku") { - DIRECTORIES_HAIKU - } else if cfg!(any(target_os = "linux", target_os = "freebsd")) { + let directories = if cfg!(any(target_os = "freebsd", target_os = "linux")) { DIRECTORIES_LINUX } else if cfg!(target_os = "macos") { DIRECTORIES_MACOS @@ -302,7 +258,7 @@ pub fn search_libclang_directories(filenames: &[String], variable: &str) -> Vec< 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.extend(search_directories(&directory, files)); } } } diff --git a/out/dynamic.rs b/out/dynamic.rs index 39247c8..c15973c 100644 --- a/out/dynamic.rs +++ b/out/dynamic.rs @@ -1,4 +1,16 @@ -// SPDX-License-Identifier: Apache-2.0 +// Copyright 2018 Kyle Mayes +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. use std::env; use std::fs::File; @@ -7,11 +19,7 @@ use std::path::{Path, PathBuf}; use super::common; -//================================================ -// Validation -//================================================ - -/// Extracts the ELF class from the ELF header in a shared library. +/// Returns the ELF class from the ELF header in the supplied file. fn parse_elf_header(path: &Path) -> io::Result<u8> { let mut file = File::open(path)?; let mut buffer = [0; 5]; @@ -23,34 +31,34 @@ fn parse_elf_header(path: &Path) -> io::Result<u8> { } } -/// Extracts the magic number from the PE header in a shared library. +/// Returns the magic number from the PE header in the supplied file. fn parse_pe_header(path: &Path) -> io::Result<u16> { let mut file = File::open(path)?; - // Extract the header offset. + // Determine 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. + // Determine 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. + // Find 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")) { +/// Validates the header for the supplied `libclang` shared library. +fn validate_header(path: &Path) -> Result<(), String> { + if cfg!(any(target_os = "freebsd", target_os = "linux")) { let class = parse_elf_header(path).map_err(|e| e.to_string())?; if cfg!(target_pointer_width = "32") && class != 1 { @@ -79,14 +87,11 @@ fn validate_library(path: &Path) -> Result<(), String> { } } -//================================================ -// Searching -//================================================ - -/// Extracts the version components in a `libclang` shared library filename. +/// Returns the components of the version in the supplied `libclang` shared +// library filename. fn parse_version(filename: &str) -> Vec<u32> { - let version = if let Some(version) = filename.strip_prefix("libclang.so.") { - version + let version = if filename.starts_with("libclang.so.") { + &filename[12..] } else if filename.starts_with("libclang-") { &filename[9..filename.len() - 3] } else { @@ -96,8 +101,8 @@ fn parse_version(filename: &str) -> Vec<u32> { 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. +/// Returns the paths to, the filenames, and the versions of the `libclang` +// shared libraries. fn search_libclang_directories(runtime: bool) -> Result<Vec<(PathBuf, String, Vec<u32>)>, String> { let mut files = vec![format!( "{}clang{}", @@ -122,10 +127,9 @@ 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", + target_os = "freebsd", + target_os = "netbsd" )) { // Some BSD distributions don't create a `libclang.so` symlink either, // but use a different naming scheme for versioned files (e.g., @@ -139,12 +143,12 @@ fn search_libclang_directories(runtime: bool) -> Result<Vec<(PathBuf, String, Ve files.push("libclang.dll".into()); } - // Find and validate `libclang` shared libraries and collect the versions. + // Validate the `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) { + match validate_header(&path) { Ok(()) => { let version = parse_version(&filename); valid.push((directory, filename, version)) @@ -172,8 +176,8 @@ fn search_libclang_directories(runtime: bool) -> Result<Vec<(PathBuf, String, Ve Err(message) } -/// Finds the "best" `libclang` shared library and returns the directory and -/// filename of that library. +/// Returns the directory and filename of the "best" available `libclang` shared +/// library. pub fn find(runtime: bool) -> Result<(PathBuf, String), String> { search_libclang_directories(runtime)? .iter() @@ -197,11 +201,7 @@ pub fn find(runtime: bool) -> Result<(PathBuf, String), String> { .ok_or_else(|| "unreachable".into()) } -//================================================ -// Linking -//================================================ - -/// Finds and links to a `libclang` shared library. +/// Find and link to `libclang` dynamically. #[cfg(not(feature = "runtime"))] pub fn link() { let cep = common::CommandErrorPrinter::default(); @@ -252,7 +252,7 @@ pub fn link() { // `libclang.so.7.0`). let name = match name.find(".dylib").or_else(|| name.find(".so")) { Some(index) => &name[0..index], - None => name, + None => &name, }; println!("cargo:rustc-link-lib=dylib={}", name); @@ -1,23 +1,33 @@ -// SPDX-License-Identifier: Apache-2.0 +// Copyright 2016 Kyle Mayes +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. //! Rust bindings for `libclang`. //! -//! ## [Documentation](https://docs.rs/clang-sys) +//! ## Documentation //! -//! 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_11_0`), neither of which are -//! enabled by default. +//! There are two versions of the documentation, one for the API exposed when +//! linking dynamically or statically and one for the API exposed when linking +//! at runtime (see the +//! [Dependencies](https://github.com/KyleMayes/clang-sys#dependencies) section +//! of the README for more information on the linking options). //! -//! 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. +//! The only difference between the APIs exposed is that when linking at runtime +//! a few additional types and functions are exposed to manage the loaded +//! `libclang` shared library. //! -//! 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. +//! * Runtime - [Documentation](https://kylemayes.github.io/clang-sys/runtime/clang_sys) +//! * Dynamic / Static - [Documentation](https://kylemayes.github.io/clang-sys/default/clang_sys) #![allow(non_camel_case_types, non_snake_case, non_upper_case_globals)] #![cfg_attr(feature = "cargo-clippy", allow(clippy::unreadable_literal))] @@ -125,8 +135,6 @@ cenum! { const CXCallingConv_AArch64VectorCall = 16, const CXCallingConv_Invalid = 100, const CXCallingConv_Unexposed = 200, - /// Only produced by `libclang` 13.0 and later. - const CXCallingConv_SwiftAsync = 17, } } @@ -315,12 +323,6 @@ cenum! { const CXCursor_ObjCAvailabilityCheckExpr = 148, /// Only produced by `libclang` 7.0 and later. const CXCursor_FixedPointLiteral = 149, - /// Only produced by `libclang` 12.0 and later. - const CXCursor_OMPArrayShapingExpr = 150, - /// Only produced by `libclang` 12.0 and later. - const CXCursor_OMPIteratorExpr = 151, - /// Only produced by `libclang` 12.0 and later. - const CXCursor_CXXAddrspaceCastExpr = 152, const CXCursor_UnexposedStmt = 200, const CXCursor_LabelStmt = 201, const CXCursor_CompoundStmt = 202, @@ -450,18 +452,6 @@ cenum! { const CXCursor_OMPDepobjDirective = 286, /// Only produced by `libclang` 11.0 and later. const CXCursor_OMPScanDirective = 287, - /// Only produced by `libclang` 13.0 and later. - const CXCursor_OMPTileDirective = 288, - /// Only produced by `libclang` 13.0 and later. - const CXCursor_OMPCanonicalLoop = 289, - /// Only produced by `libclang` 13.0 and later. - const CXCursor_OMPInteropDirective = 290, - /// Only produced by `libclang` 13.0 and later. - const CXCursor_OMPDispatchDirective = 291, - /// Only produced by `libclang` 13.0 and later. - const CXCursor_OMPMaskedDirective = 292, - /// Only produced by `libclang` 13.0 and later. - const CXCursor_OMPUnrollDirective = 293, const CXCursor_TranslationUnit = 300, const CXCursor_UnexposedAttr = 400, const CXCursor_IBActionAttr = 401, @@ -1041,8 +1031,6 @@ cenum! { const CXTypeNullability_Nullable = 1, const CXTypeNullability_Unspecified = 2, const CXTypeNullability_Invalid = 3, - /// Only produced by `libclang` 12.0 and later. - const CXTypeNullability_NullableResult = 4, } } @@ -1774,6 +1762,12 @@ link! { /// Only available on `libclang` 3.7 and later. #[cfg(feature = "clang_3_7")] pub fn clang_Cursor_getOffsetOfField(cursor: CXCursor) -> c_longlong; + /// Only available on `libclang` 9.0 and later. + #[cfg(feature = "clang_9_0")] + pub fn clang_Cursor_isAnonymousRecordDecl(cursor: CXCursor) -> c_uint; + /// Only available on `libclang` 9.0 and later. + #[cfg(feature = "clang_9_0")] + pub fn clang_Cursor_isInlineNamespace(cursor: CXCursor) -> c_uint; pub fn clang_Cursor_getRawCommentText(cursor: CXCursor) -> CXString; pub fn clang_Cursor_getReceiverType(cursor: CXCursor) -> CXType; pub fn clang_Cursor_getSpellingNameRange(cursor: CXCursor, index: c_uint, reserved: c_uint) -> CXSourceRange; @@ -1793,24 +1787,12 @@ link! { #[cfg(feature = "clang_3_6")] pub fn clang_Cursor_getTemplateArgumentValue(cursor: CXCursor, index: c_uint) -> c_longlong; pub fn clang_Cursor_getTranslationUnit(cursor: CXCursor) -> CXTranslationUnit; - /// Only available on `libclang` 12.0 and later. - #[cfg(feature = "clang_12_0")] - pub fn clang_Cursor_getVarDeclInitializer(cursor: CXCursor) -> CXCursor; /// Only available on `libclang` 3.9 and later. #[cfg(feature = "clang_3_9")] pub fn clang_Cursor_hasAttrs(cursor: CXCursor) -> c_uint; - /// Only available on `libclang` 12.0 and later. - #[cfg(feature = "clang_12_0")] - pub fn clang_Cursor_hasVarDeclGlobalStorage(cursor: CXCursor) -> c_uint; - /// Only available on `libclang` 12.0 and later. - #[cfg(feature = "clang_12_0")] - pub fn clang_Cursor_hasVarDeclExternalStorage(cursor: CXCursor) -> c_uint; /// Only available on `libclang` 3.7 and later. #[cfg(feature = "clang_3_7")] pub fn clang_Cursor_isAnonymous(cursor: CXCursor) -> c_uint; - /// Only available on `libclang` 9.0 and later. - #[cfg(feature = "clang_9_0")] - pub fn clang_Cursor_isAnonymousRecordDecl(cursor: CXCursor) -> c_uint; pub fn clang_Cursor_isBitField(cursor: CXCursor) -> c_uint; pub fn clang_Cursor_isDynamicCall(cursor: CXCursor) -> c_int; /// Only available on `libclang` 5.0 and later. @@ -1819,9 +1801,6 @@ link! { /// Only available on `libclang` 3.9 and later. #[cfg(feature = "clang_3_9")] pub fn clang_Cursor_isFunctionInlined(cursor: CXCursor) -> c_uint; - /// Only available on `libclang` 9.0 and later. - #[cfg(feature = "clang_9_0")] - pub fn clang_Cursor_isInlineNamespace(cursor: CXCursor) -> c_uint; /// Only available on `libclang` 3.9 and later. #[cfg(feature = "clang_3_9")] pub fn clang_Cursor_isMacroBuiltin(cursor: CXCursor) -> c_uint; @@ -1897,35 +1876,35 @@ link! { pub fn clang_Type_getAlignOf(type_: CXType) -> c_longlong; pub fn clang_Type_getCXXRefQualifier(type_: CXType) -> CXRefQualifierKind; pub fn clang_Type_getClassType(type_: CXType) -> CXType; - /// Only available on `libclang` 8.0 and later. - #[cfg(feature = "clang_8_0")] - pub fn clang_Type_getModifiedType(type_: CXType) -> CXType; /// Only available on `libclang` 3.9 and later. #[cfg(feature = "clang_3_9")] pub fn clang_Type_getNamedType(type_: CXType) -> CXType; + pub fn clang_Type_getNumTemplateArguments(type_: CXType) -> c_int; /// Only available on `libclang` 8.0 and later. #[cfg(feature = "clang_8_0")] - pub fn clang_Type_getNullability(type_: CXType) -> CXTypeNullabilityKind; + pub fn clang_Type_getObjCObjectBaseType(type_: CXType) -> CXType; /// Only available on `libclang` 8.0 and later. #[cfg(feature = "clang_8_0")] pub fn clang_Type_getNumObjCProtocolRefs(type_: CXType) -> c_uint; /// Only available on `libclang` 8.0 and later. #[cfg(feature = "clang_8_0")] + pub fn clang_Type_getObjCProtocolDecl(type_: CXType, index: c_uint) -> CXCursor; + /// Only available on `libclang` 8.0 and later. + #[cfg(feature = "clang_8_0")] pub fn clang_Type_getNumObjCTypeArgs(type_: CXType) -> c_uint; - pub fn clang_Type_getNumTemplateArguments(type_: CXType) -> c_int; + /// Only available on `libclang` 8.0 and later. + #[cfg(feature = "clang_8_0")] + pub fn clang_Type_getObjCTypeArg(type_: CXType, index: c_uint) -> CXType; /// Only available on `libclang` 3.9 and later. #[cfg(feature = "clang_3_9")] pub fn clang_Type_getObjCEncoding(type_: CXType) -> CXString; + pub fn clang_Type_getOffsetOf(type_: CXType, field: *const c_char) -> c_longlong; /// Only available on `libclang` 8.0 and later. #[cfg(feature = "clang_8_0")] - pub fn clang_Type_getObjCObjectBaseType(type_: CXType) -> CXType; - /// Only available on `libclang` 8.0 and later. - #[cfg(feature = "clang_8_0")] - pub fn clang_Type_getObjCProtocolDecl(type_: CXType, index: c_uint) -> CXCursor; + pub fn clang_Type_getModifiedType(type_: CXType) -> CXType; /// Only available on `libclang` 8.0 and later. #[cfg(feature = "clang_8_0")] - pub fn clang_Type_getObjCTypeArg(type_: CXType, index: c_uint) -> CXType; - pub fn clang_Type_getOffsetOf(type_: CXType, field: *const c_char) -> c_longlong; + pub fn clang_Type_getNullability(type_: CXType) -> CXTypeNullabilityKind; pub fn clang_Type_getSizeOf(type_: CXType) -> c_longlong; pub fn clang_Type_getTemplateArgumentAsType(type_: CXType, index: c_uint) -> CXType; /// Only available on `libclang` 11.0 and later. @@ -2118,17 +2097,16 @@ link! { pub fn clang_getSpecializedCursorTemplate(cursor: CXCursor) -> CXCursor; pub fn clang_getSpellingLocation(location: CXSourceLocation, file: *mut CXFile, line: *mut c_uint, column: *mut c_uint, offset: *mut c_uint); pub fn clang_getTUResourceUsageName(kind: CXTUResourceUsageKind) -> *const c_char; + /// Only available on `libclang` 5.0 and later. + #[cfg(feature = "clang_5_0")] + pub fn clang_getTranslationUnitTargetInfo(tu: CXTranslationUnit) -> CXTargetInfo; pub fn clang_getTemplateCursorKind(cursor: CXCursor) -> CXCursorKind; - pub fn clang_getToken(tu: CXTranslationUnit, location: CXSourceLocation) -> *mut CXToken; pub fn clang_getTokenExtent(tu: CXTranslationUnit, token: CXToken) -> CXSourceRange; pub fn clang_getTokenKind(token: CXToken) -> CXTokenKind; pub fn clang_getTokenLocation(tu: CXTranslationUnit, token: CXToken) -> CXSourceLocation; pub fn clang_getTokenSpelling(tu: CXTranslationUnit, token: CXToken) -> CXString; pub fn clang_getTranslationUnitCursor(tu: CXTranslationUnit) -> CXCursor; pub fn clang_getTranslationUnitSpelling(tu: CXTranslationUnit) -> CXString; - /// Only available on `libclang` 5.0 and later. - #[cfg(feature = "clang_5_0")] - pub fn clang_getTranslationUnitTargetInfo(tu: CXTranslationUnit) -> CXTargetInfo; pub fn clang_getTypeDeclaration(type_: CXType) -> CXCursor; pub fn clang_getTypeKindSpelling(type_: CXTypeKind) -> CXString; pub fn clang_getTypeSpelling(type_: CXType) -> CXString; @@ -2207,10 +2185,10 @@ link! { pub fn clang_Cursor_getParsedComment(C: CXCursor) -> CXComment; pub fn clang_FullComment_getAsHTML(comment: CXComment) -> CXString; pub fn clang_FullComment_getAsXML(comment: CXComment) -> CXString; + pub fn clang_HTMLStartTagComment_isSelfClosing(comment: CXComment) -> c_uint; pub fn clang_HTMLStartTag_getAttrName(comment: CXComment, index: c_uint) -> CXString; pub fn clang_HTMLStartTag_getAttrValue(comment: CXComment, index: c_uint) -> CXString; pub fn clang_HTMLStartTag_getNumAttrs(comment: CXComment) -> c_uint; - pub fn clang_HTMLStartTagComment_isSelfClosing(comment: CXComment) -> c_uint; pub fn clang_HTMLTagComment_getAsString(comment: CXComment) -> CXString; pub fn clang_HTMLTagComment_getTagName(comment: CXComment) -> CXString; pub fn clang_InlineCommandComment_getArgText(comment: CXComment, index: c_uint) -> CXString; @@ -2223,11 +2201,11 @@ link! { pub fn clang_ParamCommandComment_getParamName(comment: CXComment) -> CXString; pub fn clang_ParamCommandComment_isDirectionExplicit(comment: CXComment) -> c_uint; pub fn clang_ParamCommandComment_isParamIndexValid(comment: CXComment) -> c_uint; - pub fn clang_TextComment_getText(comment: CXComment) -> CXString; pub fn clang_TParamCommandComment_getDepth(comment: CXComment) -> c_uint; pub fn clang_TParamCommandComment_getIndex(comment: CXComment, depth: c_uint) -> c_uint; pub fn clang_TParamCommandComment_getParamName(comment: CXComment) -> CXString; pub fn clang_TParamCommandComment_isParamPositionValid(comment: CXComment) -> c_uint; + pub fn clang_TextComment_getText(comment: CXComment) -> CXString; pub fn clang_VerbatimBlockLineComment_getText(comment: CXComment) -> CXString; pub fn clang_VerbatimLineComment_getText(comment: CXComment) -> CXString; } diff --git a/src/link.rs b/src/link.rs index c3b0830..64a3528 100644 --- a/src/link.rs +++ b/src/link.rs @@ -1,4 +1,16 @@ -// SPDX-License-Identifier: Apache-2.0 +// Copyright 2016 Kyle Mayes +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. //================================================ // Macros diff --git a/src/support.rs b/src/support.rs index ff38d39..8422f59 100644 --- a/src/support.rs +++ b/src/support.rs @@ -1,4 +1,16 @@ -// SPDX-License-Identifier: Apache-2.0 +// Copyright 2016 Kyle Mayes +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. //! Provides helper functionality. @@ -6,13 +18,26 @@ use std::path::{Path, PathBuf}; use std::process::Command; use std::{env, io}; -use glob::{self, Pattern}; +use glob; use libc::c_int; use super::CXVersion; //================================================ +// Macros +//================================================ + +macro_rules! try_opt { + ($option:expr) => {{ + match $option { + Some(some) => some, + None => return None, + } + }}; +} + +//================================================ // Structs //================================================ @@ -49,41 +74,20 @@ impl Clang { /// 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") { return Some(Clang::new(path, 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() { @@ -91,25 +95,7 @@ impl Clang { } } } - - 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. + paths.extend(env::split_paths(&env::var("PATH").unwrap())); let default = format!("clang{}", env::consts::EXE_SUFFIX); let versioned = format!("clang-[0-9]*{}", env::consts::EXE_SUFFIX); @@ -131,17 +117,12 @@ impl Clang { /// 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 let Some(path) = try_opt!(glob::glob(&pattern).ok()) + .filter_map(|p| p.ok()) + .next() + { if path.is_file() && is_executable(&path).unwrap_or(false) { return Some(path); } @@ -203,10 +184,10 @@ fn parse_version_number(number: &str) -> Option<c_int> { /// 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 start = try_opt!(output.find("version ")) + 8; + let mut numbers = try_opt!(output[start..].split_whitespace().next()).split('.'); + let major = try_opt!(numbers.next().and_then(parse_version_number)); + let minor = try_opt!(numbers.next().and_then(parse_version_number)); let subminor = numbers.next().and_then(parse_version_number).unwrap_or(0); Some(CXVersion { Major: major, @@ -220,8 +201,8 @@ fn parse_search_paths(path: &Path, language: &str, args: &[String]) -> Option<Ve 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 start = try_opt!(output.find("#include <...> search starts here:")) + 34; + let end = try_opt!(output.find("End of search list.")); let paths = output[start..end].replace("(framework directory)", ""); Some( paths diff --git a/tests/lib.rs b/tests/lib.rs index b50055a..100a6c6 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -46,10 +46,3 @@ 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); -} |