mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-23 21:01:08 +00:00
Bug 1882202 - Upgrade the cc crate to 1.0.89. r=emilio,supply-chain-reviewers
Differential Revision: https://phabricator.services.mozilla.com/D202771
This commit is contained in:
parent
99133f08ff
commit
b0cd1791f4
5
Cargo.lock
generated
5
Cargo.lock
generated
@ -646,11 +646,12 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.0.78"
|
version = "1.0.89"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d"
|
checksum = "a0ba8f7aaa012f30d5b2861462f6708eccd49c3c39863fe083a308035f63d723"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"jobserver",
|
"jobserver",
|
||||||
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -4754,6 +4754,12 @@ user-id = 6741 # Alice Ryhl (Darksonn)
|
|||||||
start = "2021-01-11"
|
start = "2021-01-11"
|
||||||
end = "2024-05-05"
|
end = "2024-05-05"
|
||||||
|
|
||||||
|
[[trusted.cc]]
|
||||||
|
criteria = "safe-to-deploy"
|
||||||
|
user-id = 2915 # Amanieu d'Antras (Amanieu)
|
||||||
|
start = "2024-02-20"
|
||||||
|
end = "2025-02-26"
|
||||||
|
|
||||||
[[trusted.clap]]
|
[[trusted.clap]]
|
||||||
criteria = "safe-to-deploy"
|
criteria = "safe-to-deploy"
|
||||||
user-id = 6743 # Ed Page (epage)
|
user-id = 6743 # Ed Page (epage)
|
||||||
|
@ -71,6 +71,13 @@ user-id = 6741
|
|||||||
user-login = "Darksonn"
|
user-login = "Darksonn"
|
||||||
user-name = "Alice Ryhl"
|
user-name = "Alice Ryhl"
|
||||||
|
|
||||||
|
[[publisher.cc]]
|
||||||
|
version = "1.0.89"
|
||||||
|
when = "2024-03-04"
|
||||||
|
user-id = 2915
|
||||||
|
user-login = "Amanieu"
|
||||||
|
user-name = "Amanieu d'Antras"
|
||||||
|
|
||||||
[[publisher.cexpr]]
|
[[publisher.cexpr]]
|
||||||
version = "0.6.0"
|
version = "0.6.0"
|
||||||
when = "2021-10-11"
|
when = "2021-10-11"
|
||||||
|
2
third_party/rust/cc/.cargo-checksum.json
vendored
2
third_party/rust/cc/.cargo-checksum.json
vendored
@ -1 +1 @@
|
|||||||
{"files":{"Cargo.lock":"23c26d62ba5114f5ac6e7ffa3ea233cea77e5cb7f98d9f056f40fe2c49971f67","Cargo.toml":"fd4b39488866b6717476fadc460ff91c89511628080769516eec452c0def8bc7","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397","README.md":"58af5106352aafa62175a90f8a5f25fa114028bf909220dc0735d79745999ec1","src/bin/gcc-shim.rs":"b77907875029494b6288841c3aed2e4939ed40708c7f597fca5c9e2570490ca6","src/com.rs":"29d0dee08a656ab1a4cc3e5fe24542e0fab5c1373cbc9b05059f7572cf9b8313","src/lib.rs":"e0cc228db97675d6a0d86b219a20e9e48925a1ccbfd9e9fd038ccf6ef129957e","src/registry.rs":"98ae2b71781acc49297e5544fa0cf059f735636f8f1338edef8dbf7232443945","src/setup_config.rs":"72deaf1927c0b713fd5c2b2d5b8f0ea3a303a00fda1579427895cac26a94122d","src/vs_instances.rs":"2d3f8278a803b0e7052f4eeb1979b29f963dd0143f4458e2cb5f33c4e5f0963b","src/winapi.rs":"e128e95b2d39ae7a02f54a7e25d33c488c14759b9f1a50a449e10545856950c3","src/windows_registry.rs":"c0340379c1f540cf96f45bbd4cf8fc28db555826f30ac937b75b87e4377b716b","tests/cc_env.rs":"e02b3b0824ad039b47e4462c5ef6dbe6c824c28e7953af94a0f28f7b5158042e","tests/cflags.rs":"57f06eb5ce1557e5b4a032d0c4673e18fbe6f8d26c1deb153126e368b96b41b3","tests/cxxflags.rs":"c2c6c6d8a0d7146616fa1caed26876ee7bc9fcfffd525eb4743593cade5f3371","tests/support/mod.rs":"a3c8d116973bb16066bf6ec4de5143183f97de7aad085d85f8118a2eaac3e1e0","tests/test.rs":"61fb35ae6dd5cf506ada000bdd82c92e9f8eac9cc053b63e83d3f897436fbf8f"},"package":"a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d"}
|
{"files":{"Cargo.toml":"1288f536f4ddf6bcdc664a91a070aad2ebd7c6edc32ce24e8d6bc04c2cd64d49","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397","README.md":"f1ddbede208a5b78333a25dac0a7598e678e9b601a7d99a791069bddaf180dfe","src/command_helpers.rs":"3ef95bdcd79a43406fdab275d8a8f45ba787876399b54df34068955ec0109e69","src/lib.rs":"91efa8f9242266752658edd66ee607ce30635f4c30710508a99eb62e7b3c54da","src/parallel/async_executor.rs":"4ce24435fff6b6555b43fee042c16bd65d4150d0346567f246b9190d85b45983","src/parallel/job_token.rs":"0676c3177b5be9d7ede483bf4bd45c5ca0f5511073e4d1c9f181a0bc83db05dc","src/parallel/mod.rs":"aaffed5ad3dc0d28641533ab0d6f522bf34a059d4b1a239dc4d217cb5d58e232","src/parallel/stderr.rs":"a2d18ba3f2e04deb9047ece9ab7ca5452d9a76b515afbe20a76307e31597f34b","src/tool.rs":"172cfcbecd7c6a363ea841a48a10a75b0a01e83b83c0691107c601598b68dedf","src/windows/com.rs":"be1564756c9f3ef1398eafeed7b54ba610caba28e8f6258d28a997737ebf9535","src/windows/find_tools.rs":"9234fe7ab27b0259c6fa9fb47826e7d1a3d1d2c7c4042ef7153ab90ccb9a3412","src/windows/mod.rs":"42f1ad7fee35a17686b003e6aa520d3d1940d47d2f531d626e9ae0c48ba49005","src/windows/registry.rs":"c521b72c825e8095843e73482ffa810ed066ad8bb9f86e6db0c5c143c171aba1","src/windows/setup_config.rs":"754439cbab492afd44c9755abcbec1a41c9b2c358131cee2df13c0e996dbbec8","src/windows/vs_instances.rs":"76e3cee74b5fd38ddaf533bba11fe401667c50dda5f9d064099840893eaa7587","src/windows/winapi.rs":"250d51c1826d1a2329e9889dd9f058cfce253dbf2a678b076147c6cdb5db046c","src/windows/windows_sys.rs":"f6b90b87f23e446284bde86749b53858c0d37b8a43515ed8d0e90b1ac8cf7771"},"package":"a0ba8f7aaa012f30d5b2861462f6708eccd49c3c39863fe083a308035f63d723"}
|
110
third_party/rust/cc/Cargo.lock
generated
vendored
110
third_party/rust/cc/Cargo.lock
generated
vendored
@ -1,110 +0,0 @@
|
|||||||
# This file is automatically @generated by Cargo.
|
|
||||||
# It is not intended for manual editing.
|
|
||||||
version = 3
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bitflags"
|
|
||||||
version = "1.3.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cc"
|
|
||||||
version = "1.0.78"
|
|
||||||
dependencies = [
|
|
||||||
"jobserver",
|
|
||||||
"tempfile",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cfg-if"
|
|
||||||
version = "1.0.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "fastrand"
|
|
||||||
version = "1.8.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499"
|
|
||||||
dependencies = [
|
|
||||||
"instant",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "instant"
|
|
||||||
version = "0.1.12"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "jobserver"
|
|
||||||
version = "0.1.25"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "068b1ee6743e4d11fb9c6a1e6064b3693a1b600e7f5f5988047d98b3dc9fb90b"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "libc"
|
|
||||||
version = "0.2.138"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "redox_syscall"
|
|
||||||
version = "0.2.16"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "remove_dir_all"
|
|
||||||
version = "0.5.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
|
|
||||||
dependencies = [
|
|
||||||
"winapi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tempfile"
|
|
||||||
version = "3.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"fastrand",
|
|
||||||
"libc",
|
|
||||||
"redox_syscall",
|
|
||||||
"remove_dir_all",
|
|
||||||
"winapi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "winapi"
|
|
||||||
version = "0.3.9"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
|
||||||
dependencies = [
|
|
||||||
"winapi-i686-pc-windows-gnu",
|
|
||||||
"winapi-x86_64-pc-windows-gnu",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "winapi-i686-pc-windows-gnu"
|
|
||||||
version = "0.4.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "winapi-x86_64-pc-windows-gnu"
|
|
||||||
version = "0.4.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
|
22
third_party/rust/cc/Cargo.toml
vendored
22
third_party/rust/cc/Cargo.toml
vendored
@ -11,10 +11,15 @@
|
|||||||
|
|
||||||
[package]
|
[package]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
rust-version = "1.53"
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.0.78"
|
version = "1.0.89"
|
||||||
authors = ["Alex Crichton <alex@alexcrichton.com>"]
|
authors = ["Alex Crichton <alex@alexcrichton.com>"]
|
||||||
exclude = ["/.github"]
|
exclude = [
|
||||||
|
"/.github",
|
||||||
|
"tests",
|
||||||
|
"src/bin",
|
||||||
|
]
|
||||||
description = """
|
description = """
|
||||||
A build-time dependency for Cargo build scripts to assist in invoking the native
|
A build-time dependency for Cargo build scripts to assist in invoking the native
|
||||||
C compiler to compile native C code into a static archive to be linked into Rust
|
C compiler to compile native C code into a static archive to be linked into Rust
|
||||||
@ -29,11 +34,20 @@ license = "MIT OR Apache-2.0"
|
|||||||
repository = "https://github.com/rust-lang/cc-rs"
|
repository = "https://github.com/rust-lang/cc-rs"
|
||||||
|
|
||||||
[dependencies.jobserver]
|
[dependencies.jobserver]
|
||||||
version = "0.1.16"
|
version = "0.1.20"
|
||||||
optional = true
|
optional = true
|
||||||
|
default-features = false
|
||||||
|
|
||||||
[dev-dependencies.tempfile]
|
[dev-dependencies.tempfile]
|
||||||
version = "3"
|
version = "3"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
parallel = ["jobserver"]
|
parallel = [
|
||||||
|
"libc",
|
||||||
|
"jobserver",
|
||||||
|
]
|
||||||
|
|
||||||
|
[target."cfg(unix)".dependencies.libc]
|
||||||
|
version = "0.2.62"
|
||||||
|
optional = true
|
||||||
|
default-features = false
|
||||||
|
210
third_party/rust/cc/README.md
vendored
210
third_party/rust/cc/README.md
vendored
@ -1,209 +1,13 @@
|
|||||||
# cc-rs
|
# cc-rs
|
||||||
|
|
||||||
A library to compile C/C++/assembly into a Rust library/application.
|
A library for [Cargo build scripts](https://doc.rust-lang.org/cargo/reference/build-scripts.html)
|
||||||
|
to compile a set of C/C++/assembly/CUDA files into a static archive for Cargo
|
||||||
|
to link into the crate being built. This crate does not compile code itself;
|
||||||
|
it calls out to the default compiler for the platform. This crate will
|
||||||
|
automatically detect situations such as cross compilation and
|
||||||
|
various environment variables and will build code appropriately.
|
||||||
|
|
||||||
[Documentation](https://docs.rs/cc)
|
Refer to the [documentation](https://docs.rs/cc) for detailed usage instructions.
|
||||||
|
|
||||||
A simple library meant to be used as a build dependency with Cargo packages in
|
|
||||||
order to build a set of C/C++ files into a static archive. This crate calls out
|
|
||||||
to the most relevant compiler for a platform, for example using `cl` on MSVC.
|
|
||||||
|
|
||||||
## Using cc-rs
|
|
||||||
|
|
||||||
First, you'll want to both add a build script for your crate (`build.rs`) and
|
|
||||||
also add this crate to your `Cargo.toml` via:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
[build-dependencies]
|
|
||||||
cc = "1.0"
|
|
||||||
```
|
|
||||||
|
|
||||||
Next up, you'll want to write a build script like so:
|
|
||||||
|
|
||||||
```rust,no_run
|
|
||||||
// build.rs
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
cc::Build::new()
|
|
||||||
.file("foo.c")
|
|
||||||
.file("bar.c")
|
|
||||||
.compile("foo");
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
And that's it! Running `cargo build` should take care of the rest and your Rust
|
|
||||||
application will now have the C files `foo.c` and `bar.c` compiled into a file
|
|
||||||
named `libfoo.a`. If the C files contain
|
|
||||||
|
|
||||||
```c
|
|
||||||
void foo_function(void) { ... }
|
|
||||||
```
|
|
||||||
|
|
||||||
and
|
|
||||||
|
|
||||||
```c
|
|
||||||
int32_t bar_function(int32_t x) { ... }
|
|
||||||
```
|
|
||||||
|
|
||||||
you can call them from Rust by declaring them in
|
|
||||||
your Rust code like so:
|
|
||||||
|
|
||||||
```rust,no_run
|
|
||||||
extern {
|
|
||||||
fn foo_function();
|
|
||||||
fn bar_function(x: i32) -> i32;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn call() {
|
|
||||||
unsafe {
|
|
||||||
foo_function();
|
|
||||||
bar_function(42);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
See [the Rustonomicon](https://doc.rust-lang.org/nomicon/ffi.html) for more details.
|
|
||||||
|
|
||||||
## External configuration via environment variables
|
|
||||||
|
|
||||||
To control the programs and flags used for building, the builder can set a
|
|
||||||
number of different environment variables.
|
|
||||||
|
|
||||||
* `CFLAGS` - a series of space separated flags passed to compilers. Note that
|
|
||||||
individual flags cannot currently contain spaces, so doing
|
|
||||||
something like: `-L=foo\ bar` is not possible.
|
|
||||||
* `CC` - the actual C compiler used. Note that this is used as an exact
|
|
||||||
executable name, so (for example) no extra flags can be passed inside
|
|
||||||
this variable, and the builder must ensure that there aren't any
|
|
||||||
trailing spaces. This compiler must understand the `-c` flag. For
|
|
||||||
certain `TARGET`s, it also is assumed to know about other flags (most
|
|
||||||
common is `-fPIC`).
|
|
||||||
* `AR` - the `ar` (archiver) executable to use to build the static library.
|
|
||||||
* `CRATE_CC_NO_DEFAULTS` - the default compiler flags may cause conflicts in some cross compiling scenarios. Setting this variable will disable the generation of default compiler flags.
|
|
||||||
* `CXX...` - see [C++ Support](#c-support).
|
|
||||||
|
|
||||||
Each of these variables can also be supplied with certain prefixes and suffixes,
|
|
||||||
in the following prioritized order:
|
|
||||||
|
|
||||||
1. `<var>_<target>` - for example, `CC_x86_64-unknown-linux-gnu`
|
|
||||||
2. `<var>_<target_with_underscores>` - for example, `CC_x86_64_unknown_linux_gnu`
|
|
||||||
3. `<build-kind>_<var>` - for example, `HOST_CC` or `TARGET_CFLAGS`
|
|
||||||
4. `<var>` - a plain `CC`, `AR` as above.
|
|
||||||
|
|
||||||
If none of these variables exist, cc-rs uses built-in defaults
|
|
||||||
|
|
||||||
In addition to the above optional environment variables, `cc-rs` has some
|
|
||||||
functions with hard requirements on some variables supplied by [cargo's
|
|
||||||
build-script driver][cargo] that it has the `TARGET`, `OUT_DIR`, `OPT_LEVEL`,
|
|
||||||
and `HOST` variables.
|
|
||||||
|
|
||||||
[cargo]: https://doc.rust-lang.org/cargo/reference/build-scripts.html#inputs-to-the-build-script
|
|
||||||
|
|
||||||
## Optional features
|
|
||||||
|
|
||||||
### Parallel
|
|
||||||
|
|
||||||
Currently cc-rs supports parallel compilation (think `make -jN`) but this
|
|
||||||
feature is turned off by default. To enable cc-rs to compile C/C++ in parallel,
|
|
||||||
you can change your dependency to:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
[build-dependencies]
|
|
||||||
cc = { version = "1.0", features = ["parallel"] }
|
|
||||||
```
|
|
||||||
|
|
||||||
By default cc-rs will limit parallelism to `$NUM_JOBS`, or if not present it
|
|
||||||
will limit it to the number of cpus on the machine. If you are using cargo,
|
|
||||||
use `-jN` option of `build`, `test` and `run` commands as `$NUM_JOBS`
|
|
||||||
is supplied by cargo.
|
|
||||||
|
|
||||||
## Compile-time Requirements
|
|
||||||
|
|
||||||
To work properly this crate needs access to a C compiler when the build script
|
|
||||||
is being run. This crate does not ship a C compiler with it. The compiler
|
|
||||||
required varies per platform, but there are three broad categories:
|
|
||||||
|
|
||||||
* Unix platforms require `cc` to be the C compiler. This can be found by
|
|
||||||
installing cc/clang on Linux distributions and Xcode on macOS, for example.
|
|
||||||
* Windows platforms targeting MSVC (e.g. your target triple ends in `-msvc`)
|
|
||||||
require `cl.exe` to be available and in `PATH`. This is typically found in
|
|
||||||
standard Visual Studio installations and the `PATH` can be set up by running
|
|
||||||
the appropriate developer tools shell.
|
|
||||||
* Windows platforms targeting MinGW (e.g. your target triple ends in `-gnu`)
|
|
||||||
require `cc` to be available in `PATH`. We recommend the
|
|
||||||
[MinGW-w64](https://www.mingw-w64.org/) distribution, which is using the
|
|
||||||
[Win-builds](http://win-builds.org/) installation system.
|
|
||||||
You may also acquire it via
|
|
||||||
[MSYS2](https://www.msys2.org/), as explained [here][msys2-help]. Make sure
|
|
||||||
to install the appropriate architecture corresponding to your installation of
|
|
||||||
rustc. GCC from older [MinGW](http://www.mingw.org/) project is compatible
|
|
||||||
only with 32-bit rust compiler.
|
|
||||||
|
|
||||||
[msys2-help]: https://github.com/rust-lang/rust#building-on-windows
|
|
||||||
|
|
||||||
## C++ support
|
|
||||||
|
|
||||||
`cc-rs` supports C++ libraries compilation by using the `cpp` method on
|
|
||||||
`Build`:
|
|
||||||
|
|
||||||
```rust,no_run
|
|
||||||
fn main() {
|
|
||||||
cc::Build::new()
|
|
||||||
.cpp(true) // Switch to C++ library compilation.
|
|
||||||
.file("foo.cpp")
|
|
||||||
.compile("libfoo.a");
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
For C++ libraries, the `CXX` and `CXXFLAGS` environment variables are used instead of `CC` and `CFLAGS`.
|
|
||||||
|
|
||||||
The C++ standard library may be linked to the crate target. By default it's `libc++` for macOS, FreeBSD, and OpenBSD, `libc++_shared` for Android, nothing for MSVC, and `libstdc++` for anything else. It can be changed in one of two ways:
|
|
||||||
|
|
||||||
1. by using the `cpp_link_stdlib` method on `Build`:
|
|
||||||
```rust,no-run
|
|
||||||
fn main() {
|
|
||||||
cc::Build::new()
|
|
||||||
.cpp(true)
|
|
||||||
.file("foo.cpp")
|
|
||||||
.cpp_link_stdlib("stdc++") // use libstdc++
|
|
||||||
.compile("libfoo.a");
|
|
||||||
}
|
|
||||||
```
|
|
||||||
2. by setting the `CXXSTDLIB` environment variable.
|
|
||||||
|
|
||||||
In particular, for Android you may want to [use `c++_static` if you have at most one shared library](https://developer.android.com/ndk/guides/cpp-support).
|
|
||||||
|
|
||||||
Remember that C++ does name mangling so `extern "C"` might be required to enable Rust linker to find your functions.
|
|
||||||
|
|
||||||
## CUDA C++ support
|
|
||||||
|
|
||||||
`cc-rs` also supports compiling CUDA C++ libraries by using the `cuda` method
|
|
||||||
on `Build` (currently for GNU/Clang toolchains only):
|
|
||||||
|
|
||||||
```rust,no_run
|
|
||||||
fn main() {
|
|
||||||
cc::Build::new()
|
|
||||||
// Switch to CUDA C++ library compilation using NVCC.
|
|
||||||
.cuda(true)
|
|
||||||
.cudart("static")
|
|
||||||
// Generate code for Maxwell (GTX 970, 980, 980 Ti, Titan X).
|
|
||||||
.flag("-gencode").flag("arch=compute_52,code=sm_52")
|
|
||||||
// Generate code for Maxwell (Jetson TX1).
|
|
||||||
.flag("-gencode").flag("arch=compute_53,code=sm_53")
|
|
||||||
// Generate code for Pascal (GTX 1070, 1080, 1080 Ti, Titan Xp).
|
|
||||||
.flag("-gencode").flag("arch=compute_61,code=sm_61")
|
|
||||||
// Generate code for Pascal (Tesla P100).
|
|
||||||
.flag("-gencode").flag("arch=compute_60,code=sm_60")
|
|
||||||
// Generate code for Pascal (Jetson TX2).
|
|
||||||
.flag("-gencode").flag("arch=compute_62,code=sm_62")
|
|
||||||
.file("bar.cu")
|
|
||||||
.compile("libbar.a");
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
48
third_party/rust/cc/src/bin/gcc-shim.rs
vendored
48
third_party/rust/cc/src/bin/gcc-shim.rs
vendored
@ -1,48 +0,0 @@
|
|||||||
#![cfg_attr(test, allow(dead_code))]
|
|
||||||
|
|
||||||
use std::env;
|
|
||||||
use std::fs::File;
|
|
||||||
use std::io::prelude::*;
|
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let mut args = env::args();
|
|
||||||
let program = args.next().expect("Unexpected empty args");
|
|
||||||
|
|
||||||
let out_dir = PathBuf::from(
|
|
||||||
env::var_os("GCCTEST_OUT_DIR").expect(&format!("{}: GCCTEST_OUT_DIR not found", program)),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Find the first nonexistent candidate file to which the program's args can be written.
|
|
||||||
for i in 0.. {
|
|
||||||
let candidate = &out_dir.join(format!("out{}", i));
|
|
||||||
|
|
||||||
// If the file exists, commands have already run. Try again.
|
|
||||||
if candidate.exists() {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a file and record the args passed to the command.
|
|
||||||
let mut f = File::create(candidate).expect(&format!(
|
|
||||||
"{}: can't create candidate: {}",
|
|
||||||
program,
|
|
||||||
candidate.to_string_lossy()
|
|
||||||
));
|
|
||||||
for arg in args {
|
|
||||||
writeln!(f, "{}", arg).expect(&format!(
|
|
||||||
"{}: can't write to candidate: {}",
|
|
||||||
program,
|
|
||||||
candidate.to_string_lossy()
|
|
||||||
));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a file used by some tests.
|
|
||||||
let path = &out_dir.join("libfoo.a");
|
|
||||||
File::create(path).expect(&format!(
|
|
||||||
"{}: can't create libfoo.a: {}",
|
|
||||||
program,
|
|
||||||
path.to_string_lossy()
|
|
||||||
));
|
|
||||||
}
|
|
433
third_party/rust/cc/src/command_helpers.rs
vendored
Normal file
433
third_party/rust/cc/src/command_helpers.rs
vendored
Normal file
@ -0,0 +1,433 @@
|
|||||||
|
//! Miscellaneous helpers for running commands
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
collections::hash_map,
|
||||||
|
ffi::OsString,
|
||||||
|
fmt::Display,
|
||||||
|
fs,
|
||||||
|
hash::Hasher,
|
||||||
|
io::{self, Read, Write},
|
||||||
|
path::Path,
|
||||||
|
process::{Child, ChildStderr, Command, Stdio},
|
||||||
|
sync::{
|
||||||
|
atomic::{AtomicBool, Ordering},
|
||||||
|
Arc,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{Error, ErrorKind, Object};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub(crate) struct CargoOutput {
|
||||||
|
pub(crate) metadata: bool,
|
||||||
|
pub(crate) warnings: bool,
|
||||||
|
pub(crate) debug: bool,
|
||||||
|
checked_dbg_var: Arc<AtomicBool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CargoOutput {
|
||||||
|
pub(crate) fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
metadata: true,
|
||||||
|
warnings: true,
|
||||||
|
debug: std::env::var_os("CC_ENABLE_DEBUG_OUTPUT").is_some(),
|
||||||
|
checked_dbg_var: Arc::new(AtomicBool::new(false)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn print_metadata(&self, s: &dyn Display) {
|
||||||
|
if self.metadata {
|
||||||
|
println!("{}", s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn print_warning(&self, arg: &dyn Display) {
|
||||||
|
if self.warnings {
|
||||||
|
println!("cargo:warning={}", arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn print_debug(&self, arg: &dyn Display) {
|
||||||
|
if self.metadata && !self.checked_dbg_var.load(Ordering::Relaxed) {
|
||||||
|
self.checked_dbg_var.store(true, Ordering::Relaxed);
|
||||||
|
println!("cargo:rerun-if-env-changed=CC_ENABLE_DEBUG_OUTPUT");
|
||||||
|
}
|
||||||
|
if self.debug {
|
||||||
|
println!("{}", arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stdio_for_warnings(&self) -> Stdio {
|
||||||
|
if self.warnings {
|
||||||
|
Stdio::piped()
|
||||||
|
} else {
|
||||||
|
Stdio::null()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct StderrForwarder {
|
||||||
|
inner: Option<(ChildStderr, Vec<u8>)>,
|
||||||
|
#[cfg(feature = "parallel")]
|
||||||
|
is_non_blocking: bool,
|
||||||
|
#[cfg(feature = "parallel")]
|
||||||
|
bytes_available_failed: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
const MIN_BUFFER_CAPACITY: usize = 100;
|
||||||
|
|
||||||
|
impl StderrForwarder {
|
||||||
|
pub(crate) fn new(child: &mut Child) -> Self {
|
||||||
|
Self {
|
||||||
|
inner: child
|
||||||
|
.stderr
|
||||||
|
.take()
|
||||||
|
.map(|stderr| (stderr, Vec::with_capacity(MIN_BUFFER_CAPACITY))),
|
||||||
|
#[cfg(feature = "parallel")]
|
||||||
|
is_non_blocking: false,
|
||||||
|
#[cfg(feature = "parallel")]
|
||||||
|
bytes_available_failed: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::uninit_vec)]
|
||||||
|
fn forward_available(&mut self) -> bool {
|
||||||
|
if let Some((stderr, buffer)) = self.inner.as_mut() {
|
||||||
|
loop {
|
||||||
|
let old_data_end = buffer.len();
|
||||||
|
|
||||||
|
// For non-blocking we check to see if there is data available, so we should try to
|
||||||
|
// read at least that much. For blocking, always read at least the minimum amount.
|
||||||
|
#[cfg(not(feature = "parallel"))]
|
||||||
|
let to_reserve = MIN_BUFFER_CAPACITY;
|
||||||
|
#[cfg(feature = "parallel")]
|
||||||
|
let to_reserve = if self.is_non_blocking && !self.bytes_available_failed {
|
||||||
|
match crate::parallel::stderr::bytes_available(stderr) {
|
||||||
|
#[cfg(windows)]
|
||||||
|
Ok(0) => return false,
|
||||||
|
#[cfg(unix)]
|
||||||
|
Ok(0) => {
|
||||||
|
// On Unix, depending on the implementation, we may sometimes get 0 in a
|
||||||
|
// loop (either there is data available or the pipe is broken), so
|
||||||
|
// continue with the non-blocking read anyway.
|
||||||
|
MIN_BUFFER_CAPACITY
|
||||||
|
}
|
||||||
|
#[cfg(windows)]
|
||||||
|
Err(_) => {
|
||||||
|
// On Windows, if we get an error then the pipe is broken, so flush
|
||||||
|
// the buffer and bail.
|
||||||
|
if !buffer.is_empty() {
|
||||||
|
write_warning(&buffer[..]);
|
||||||
|
}
|
||||||
|
self.inner = None;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#[cfg(unix)]
|
||||||
|
Err(_) => {
|
||||||
|
// On Unix, depending on the implementation, we may get spurious
|
||||||
|
// errors so make a note not to use bytes_available again and try
|
||||||
|
// the non-blocking read anyway.
|
||||||
|
self.bytes_available_failed = true;
|
||||||
|
MIN_BUFFER_CAPACITY
|
||||||
|
}
|
||||||
|
Ok(bytes_available) => MIN_BUFFER_CAPACITY.max(bytes_available),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
MIN_BUFFER_CAPACITY
|
||||||
|
};
|
||||||
|
buffer.reserve(to_reserve);
|
||||||
|
|
||||||
|
// SAFETY: 1) the length is set to the capacity, so we are never using memory beyond
|
||||||
|
// the underlying buffer and 2) we always call `truncate` below to set the len back
|
||||||
|
// to the initialized data.
|
||||||
|
unsafe {
|
||||||
|
buffer.set_len(buffer.capacity());
|
||||||
|
}
|
||||||
|
match stderr.read(&mut buffer[old_data_end..]) {
|
||||||
|
Err(err) if err.kind() == std::io::ErrorKind::WouldBlock => {
|
||||||
|
// No data currently, yield back.
|
||||||
|
buffer.truncate(old_data_end);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Err(err) if err.kind() == std::io::ErrorKind::Interrupted => {
|
||||||
|
// Interrupted, try again.
|
||||||
|
buffer.truncate(old_data_end);
|
||||||
|
}
|
||||||
|
Ok(0) | Err(_) => {
|
||||||
|
// End of stream: flush remaining data and bail.
|
||||||
|
if old_data_end > 0 {
|
||||||
|
write_warning(&buffer[..old_data_end]);
|
||||||
|
}
|
||||||
|
self.inner = None;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
Ok(bytes_read) => {
|
||||||
|
buffer.truncate(old_data_end + bytes_read);
|
||||||
|
let mut consumed = 0;
|
||||||
|
for line in buffer.split_inclusive(|&b| b == b'\n') {
|
||||||
|
// Only forward complete lines, leave the rest in the buffer.
|
||||||
|
if let Some((b'\n', line)) = line.split_last() {
|
||||||
|
consumed += line.len() + 1;
|
||||||
|
write_warning(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buffer.drain(..consumed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "parallel")]
|
||||||
|
pub(crate) fn set_non_blocking(&mut self) -> Result<(), Error> {
|
||||||
|
assert!(!self.is_non_blocking);
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
if let Some((stderr, _)) = self.inner.as_ref() {
|
||||||
|
crate::parallel::stderr::set_non_blocking(stderr)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.is_non_blocking = true;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "parallel")]
|
||||||
|
fn forward_all(&mut self) {
|
||||||
|
while !self.forward_available() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "parallel"))]
|
||||||
|
fn forward_all(&mut self) {
|
||||||
|
let forward_result = self.forward_available();
|
||||||
|
assert!(forward_result, "Should have consumed all data");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_warning(line: &[u8]) {
|
||||||
|
let stdout = io::stdout();
|
||||||
|
let mut stdout = stdout.lock();
|
||||||
|
stdout.write_all(b"cargo:warning=").unwrap();
|
||||||
|
stdout.write_all(line).unwrap();
|
||||||
|
stdout.write_all(b"\n").unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wait_on_child(
|
||||||
|
cmd: &Command,
|
||||||
|
program: &str,
|
||||||
|
child: &mut Child,
|
||||||
|
cargo_output: &CargoOutput,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
StderrForwarder::new(child).forward_all();
|
||||||
|
|
||||||
|
let status = match child.wait() {
|
||||||
|
Ok(s) => s,
|
||||||
|
Err(e) => {
|
||||||
|
return Err(Error::new(
|
||||||
|
ErrorKind::ToolExecError,
|
||||||
|
format!(
|
||||||
|
"Failed to wait on spawned child process, command {:?} with args {:?}: {}.",
|
||||||
|
cmd, program, e
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
cargo_output.print_debug(&status);
|
||||||
|
|
||||||
|
if status.success() {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(Error::new(
|
||||||
|
ErrorKind::ToolExecError,
|
||||||
|
format!(
|
||||||
|
"Command {:?} with args {:?} did not execute successfully (status code {}).",
|
||||||
|
cmd, program, status
|
||||||
|
),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Find the destination object path for each file in the input source files,
|
||||||
|
/// and store them in the output Object.
|
||||||
|
pub(crate) fn objects_from_files(files: &[Arc<Path>], dst: &Path) -> Result<Vec<Object>, Error> {
|
||||||
|
let mut objects = Vec::with_capacity(files.len());
|
||||||
|
for file in files {
|
||||||
|
let basename = file
|
||||||
|
.file_name()
|
||||||
|
.ok_or_else(|| {
|
||||||
|
Error::new(
|
||||||
|
ErrorKind::InvalidArgument,
|
||||||
|
"No file_name for object file path!",
|
||||||
|
)
|
||||||
|
})?
|
||||||
|
.to_string_lossy();
|
||||||
|
let dirname = file
|
||||||
|
.parent()
|
||||||
|
.ok_or_else(|| {
|
||||||
|
Error::new(
|
||||||
|
ErrorKind::InvalidArgument,
|
||||||
|
"No parent for object file path!",
|
||||||
|
)
|
||||||
|
})?
|
||||||
|
.to_string_lossy();
|
||||||
|
|
||||||
|
// Hash the dirname. This should prevent conflicts if we have multiple
|
||||||
|
// object files with the same filename in different subfolders.
|
||||||
|
let mut hasher = hash_map::DefaultHasher::new();
|
||||||
|
hasher.write(dirname.to_string().as_bytes());
|
||||||
|
let obj = dst
|
||||||
|
.join(format!("{:016x}-{}", hasher.finish(), basename))
|
||||||
|
.with_extension("o");
|
||||||
|
|
||||||
|
match obj.parent() {
|
||||||
|
Some(s) => fs::create_dir_all(s)?,
|
||||||
|
None => {
|
||||||
|
return Err(Error::new(
|
||||||
|
ErrorKind::InvalidArgument,
|
||||||
|
"dst is an invalid path with no parent",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
objects.push(Object::new(file.to_path_buf(), obj));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(objects)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn run(
|
||||||
|
cmd: &mut Command,
|
||||||
|
program: &str,
|
||||||
|
cargo_output: &CargoOutput,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let mut child = spawn(cmd, program, cargo_output)?;
|
||||||
|
wait_on_child(cmd, program, &mut child, cargo_output)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn run_output(
|
||||||
|
cmd: &mut Command,
|
||||||
|
program: &str,
|
||||||
|
cargo_output: &CargoOutput,
|
||||||
|
) -> Result<Vec<u8>, Error> {
|
||||||
|
cmd.stdout(Stdio::piped());
|
||||||
|
|
||||||
|
let mut child = spawn(cmd, program, cargo_output)?;
|
||||||
|
|
||||||
|
let mut stdout = vec![];
|
||||||
|
child
|
||||||
|
.stdout
|
||||||
|
.take()
|
||||||
|
.unwrap()
|
||||||
|
.read_to_end(&mut stdout)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
wait_on_child(cmd, program, &mut child, cargo_output)?;
|
||||||
|
|
||||||
|
Ok(stdout)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn spawn(
|
||||||
|
cmd: &mut Command,
|
||||||
|
program: &str,
|
||||||
|
cargo_output: &CargoOutput,
|
||||||
|
) -> Result<Child, Error> {
|
||||||
|
struct ResetStderr<'cmd>(&'cmd mut Command);
|
||||||
|
|
||||||
|
impl Drop for ResetStderr<'_> {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
// Reset stderr to default to release pipe_writer so that print thread will
|
||||||
|
// not block forever.
|
||||||
|
self.0.stderr(Stdio::inherit());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cargo_output.print_debug(&format_args!("running: {:?}", cmd));
|
||||||
|
|
||||||
|
let cmd = ResetStderr(cmd);
|
||||||
|
let child = cmd.0.stderr(cargo_output.stdio_for_warnings()).spawn();
|
||||||
|
match child {
|
||||||
|
Ok(child) => Ok(child),
|
||||||
|
Err(ref e) if e.kind() == io::ErrorKind::NotFound => {
|
||||||
|
let extra = if cfg!(windows) {
|
||||||
|
" (see https://github.com/rust-lang/cc-rs#compile-time-requirements \
|
||||||
|
for help)"
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
};
|
||||||
|
Err(Error::new(
|
||||||
|
ErrorKind::ToolNotFound,
|
||||||
|
format!("Failed to find tool. Is `{}` installed?{}", program, extra),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
Err(e) => Err(Error::new(
|
||||||
|
ErrorKind::ToolExecError,
|
||||||
|
format!(
|
||||||
|
"Command {:?} with args {:?} failed to start: {:?}",
|
||||||
|
cmd.0, program, e
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn command_add_output_file(
|
||||||
|
cmd: &mut Command,
|
||||||
|
dst: &Path,
|
||||||
|
cuda: bool,
|
||||||
|
msvc: bool,
|
||||||
|
clang: bool,
|
||||||
|
gnu: bool,
|
||||||
|
is_asm: bool,
|
||||||
|
is_arm: bool,
|
||||||
|
) {
|
||||||
|
if msvc && !clang && !gnu && !cuda && !(is_asm && is_arm) {
|
||||||
|
let mut s = OsString::from("-Fo");
|
||||||
|
s.push(dst);
|
||||||
|
cmd.arg(s);
|
||||||
|
} else {
|
||||||
|
cmd.arg("-o").arg(dst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "parallel")]
|
||||||
|
pub(crate) fn try_wait_on_child(
|
||||||
|
cmd: &Command,
|
||||||
|
program: &str,
|
||||||
|
child: &mut Child,
|
||||||
|
stdout: &mut dyn io::Write,
|
||||||
|
stderr_forwarder: &mut StderrForwarder,
|
||||||
|
) -> Result<Option<()>, Error> {
|
||||||
|
stderr_forwarder.forward_available();
|
||||||
|
|
||||||
|
match child.try_wait() {
|
||||||
|
Ok(Some(status)) => {
|
||||||
|
stderr_forwarder.forward_all();
|
||||||
|
|
||||||
|
let _ = writeln!(stdout, "{}", status);
|
||||||
|
|
||||||
|
if status.success() {
|
||||||
|
Ok(Some(()))
|
||||||
|
} else {
|
||||||
|
Err(Error::new(
|
||||||
|
ErrorKind::ToolExecError,
|
||||||
|
format!(
|
||||||
|
"Command {:?} with args {:?} did not execute successfully (status code {}).",
|
||||||
|
cmd, program, status
|
||||||
|
),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(None) => Ok(None),
|
||||||
|
Err(e) => {
|
||||||
|
stderr_forwarder.forward_all();
|
||||||
|
Err(Error::new(
|
||||||
|
ErrorKind::ToolExecError,
|
||||||
|
format!(
|
||||||
|
"Failed to wait on spawned child process, command {:?} with args {:?}: {}.",
|
||||||
|
cmd, program, e
|
||||||
|
),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
2690
third_party/rust/cc/src/lib.rs
vendored
2690
third_party/rust/cc/src/lib.rs
vendored
File diff suppressed because it is too large
Load Diff
118
third_party/rust/cc/src/parallel/async_executor.rs
vendored
Normal file
118
third_party/rust/cc/src/parallel/async_executor.rs
vendored
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
use std::{
|
||||||
|
cell::Cell,
|
||||||
|
future::Future,
|
||||||
|
pin::Pin,
|
||||||
|
ptr,
|
||||||
|
task::{Context, Poll, RawWaker, RawWakerVTable, Waker},
|
||||||
|
thread,
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::Error;
|
||||||
|
|
||||||
|
const NOOP_WAKER_VTABLE: RawWakerVTable = RawWakerVTable::new(
|
||||||
|
// Cloning just returns a new no-op raw waker
|
||||||
|
|_| NOOP_RAW_WAKER,
|
||||||
|
// `wake` does nothing
|
||||||
|
|_| {},
|
||||||
|
// `wake_by_ref` does nothing
|
||||||
|
|_| {},
|
||||||
|
// Dropping does nothing as we don't allocate anything
|
||||||
|
|_| {},
|
||||||
|
);
|
||||||
|
const NOOP_RAW_WAKER: RawWaker = RawWaker::new(ptr::null(), &NOOP_WAKER_VTABLE);
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub(crate) struct YieldOnce(bool);
|
||||||
|
|
||||||
|
impl Future for YieldOnce {
|
||||||
|
type Output = ();
|
||||||
|
|
||||||
|
fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<()> {
|
||||||
|
let flag = &mut std::pin::Pin::into_inner(self).0;
|
||||||
|
if !*flag {
|
||||||
|
*flag = true;
|
||||||
|
Poll::Pending
|
||||||
|
} else {
|
||||||
|
Poll::Ready(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Execute the futures and return when they are all done.
|
||||||
|
///
|
||||||
|
/// Here we use our own homebrew async executor since cc is used in the build
|
||||||
|
/// script of many popular projects, pulling in additional dependencies would
|
||||||
|
/// significantly slow down its compilation.
|
||||||
|
pub(crate) fn block_on<Fut1, Fut2>(
|
||||||
|
mut fut1: Fut1,
|
||||||
|
mut fut2: Fut2,
|
||||||
|
has_made_progress: &Cell<bool>,
|
||||||
|
) -> Result<(), Error>
|
||||||
|
where
|
||||||
|
Fut1: Future<Output = Result<(), Error>>,
|
||||||
|
Fut2: Future<Output = Result<(), Error>>,
|
||||||
|
{
|
||||||
|
// Shadows the future so that it can never be moved and is guaranteed
|
||||||
|
// to be pinned.
|
||||||
|
//
|
||||||
|
// The same trick used in `pin!` macro.
|
||||||
|
//
|
||||||
|
// TODO: Once MSRV is bumped to 1.68, replace this with `std::pin::pin!`
|
||||||
|
let mut fut1 = Some(unsafe { Pin::new_unchecked(&mut fut1) });
|
||||||
|
let mut fut2 = Some(unsafe { Pin::new_unchecked(&mut fut2) });
|
||||||
|
|
||||||
|
// TODO: Once `Waker::noop` stablised and our MSRV is bumped to the version
|
||||||
|
// which it is stablised, replace this with `Waker::noop`.
|
||||||
|
let waker = unsafe { Waker::from_raw(NOOP_RAW_WAKER) };
|
||||||
|
let mut context = Context::from_waker(&waker);
|
||||||
|
|
||||||
|
let mut backoff_cnt = 0;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
has_made_progress.set(false);
|
||||||
|
|
||||||
|
if let Some(fut) = fut2.as_mut() {
|
||||||
|
if let Poll::Ready(res) = fut.as_mut().poll(&mut context) {
|
||||||
|
fut2 = None;
|
||||||
|
res?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(fut) = fut1.as_mut() {
|
||||||
|
if let Poll::Ready(res) = fut.as_mut().poll(&mut context) {
|
||||||
|
fut1 = None;
|
||||||
|
res?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if fut1.is_none() && fut2.is_none() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
if !has_made_progress.get() {
|
||||||
|
if backoff_cnt > 3 {
|
||||||
|
// We have yielded at least three times without making'
|
||||||
|
// any progress, so we will sleep for a while.
|
||||||
|
let duration = Duration::from_millis(100 * (backoff_cnt - 3).min(10));
|
||||||
|
thread::sleep(duration);
|
||||||
|
} else {
|
||||||
|
// Given that we spawned a lot of compilation tasks, it is unlikely
|
||||||
|
// that OS cannot find other ready task to execute.
|
||||||
|
//
|
||||||
|
// If all of them are done, then we will yield them and spawn more,
|
||||||
|
// or simply return.
|
||||||
|
//
|
||||||
|
// Thus this will not be turned into a busy-wait loop and it will not
|
||||||
|
// waste CPU resource.
|
||||||
|
thread::yield_now();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
backoff_cnt = if has_made_progress.get() {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
backoff_cnt + 1
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
255
third_party/rust/cc/src/parallel/job_token.rs
vendored
Normal file
255
third_party/rust/cc/src/parallel/job_token.rs
vendored
Normal file
@ -0,0 +1,255 @@
|
|||||||
|
use std::{marker::PhantomData, mem::MaybeUninit, sync::Once};
|
||||||
|
|
||||||
|
use crate::Error;
|
||||||
|
|
||||||
|
pub(crate) struct JobToken(PhantomData<()>);
|
||||||
|
|
||||||
|
impl JobToken {
|
||||||
|
fn new() -> Self {
|
||||||
|
Self(PhantomData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for JobToken {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
match JobTokenServer::new() {
|
||||||
|
JobTokenServer::Inherited(jobserver) => jobserver.release_token_raw(),
|
||||||
|
JobTokenServer::InProcess(jobserver) => jobserver.release_token_raw(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum JobTokenServer {
|
||||||
|
Inherited(inherited_jobserver::JobServer),
|
||||||
|
InProcess(inprocess_jobserver::JobServer),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl JobTokenServer {
|
||||||
|
/// This function returns a static reference to the jobserver because
|
||||||
|
/// - creating a jobserver from env is a bit fd-unsafe (e.g. the fd might
|
||||||
|
/// be closed by other jobserver users in the process) and better do it
|
||||||
|
/// at the start of the program.
|
||||||
|
/// - in case a jobserver cannot be created from env (e.g. it's not
|
||||||
|
/// present), we will create a global in-process only jobserver
|
||||||
|
/// that has to be static so that it will be shared by all cc
|
||||||
|
/// compilation.
|
||||||
|
fn new() -> &'static Self {
|
||||||
|
static INIT: Once = Once::new();
|
||||||
|
static mut JOBSERVER: MaybeUninit<JobTokenServer> = MaybeUninit::uninit();
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
INIT.call_once(|| {
|
||||||
|
let server = inherited_jobserver::JobServer::from_env()
|
||||||
|
.map(Self::Inherited)
|
||||||
|
.unwrap_or_else(|| Self::InProcess(inprocess_jobserver::JobServer::new()));
|
||||||
|
JOBSERVER = MaybeUninit::new(server);
|
||||||
|
});
|
||||||
|
// TODO: Poor man's assume_init_ref, as that'd require a MSRV of 1.55.
|
||||||
|
&*JOBSERVER.as_ptr()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) enum ActiveJobTokenServer {
|
||||||
|
Inherited(inherited_jobserver::ActiveJobServer<'static>),
|
||||||
|
InProcess(&'static inprocess_jobserver::JobServer),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ActiveJobTokenServer {
|
||||||
|
pub(crate) fn new() -> Result<Self, Error> {
|
||||||
|
match JobTokenServer::new() {
|
||||||
|
JobTokenServer::Inherited(inherited_jobserver) => {
|
||||||
|
inherited_jobserver.enter_active().map(Self::Inherited)
|
||||||
|
}
|
||||||
|
JobTokenServer::InProcess(inprocess_jobserver) => {
|
||||||
|
Ok(Self::InProcess(inprocess_jobserver))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn acquire(&self) -> Result<JobToken, Error> {
|
||||||
|
match &self {
|
||||||
|
Self::Inherited(jobserver) => jobserver.acquire().await,
|
||||||
|
Self::InProcess(jobserver) => Ok(jobserver.acquire().await),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod inherited_jobserver {
|
||||||
|
use super::JobToken;
|
||||||
|
|
||||||
|
use crate::{parallel::async_executor::YieldOnce, Error, ErrorKind};
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
io, mem,
|
||||||
|
sync::{mpsc, Mutex, MutexGuard, PoisonError},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub(super) struct JobServer {
|
||||||
|
/// Implicit token for this process which is obtained and will be
|
||||||
|
/// released in parent. Since JobTokens only give back what they got,
|
||||||
|
/// there should be at most one global implicit token in the wild.
|
||||||
|
///
|
||||||
|
/// Since Rust does not execute any `Drop` for global variables,
|
||||||
|
/// we can't just put it back to jobserver and then re-acquire it at
|
||||||
|
/// the end of the process.
|
||||||
|
///
|
||||||
|
/// Use `Mutex` to avoid race between acquire and release.
|
||||||
|
/// If an `AtomicBool` is used, then it's possible for:
|
||||||
|
/// - `release_token_raw`: Tries to set `global_implicit_token` to true, but it is already
|
||||||
|
/// set to `true`, continue to release it to jobserver
|
||||||
|
/// - `acquire` takes the global implicit token, set `global_implicit_token` to false
|
||||||
|
/// - `release_token_raw` now writes the token back into the jobserver, while
|
||||||
|
/// `global_implicit_token` is `false`
|
||||||
|
///
|
||||||
|
/// If the program exits here, then cc effectively increases parallelism by one, which is
|
||||||
|
/// incorrect, hence we use a `Mutex` here.
|
||||||
|
global_implicit_token: Mutex<bool>,
|
||||||
|
inner: jobserver::Client,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl JobServer {
|
||||||
|
pub(super) unsafe fn from_env() -> Option<Self> {
|
||||||
|
jobserver::Client::from_env().map(|inner| Self {
|
||||||
|
inner,
|
||||||
|
global_implicit_token: Mutex::new(true),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_global_implicit_token(&self) -> MutexGuard<'_, bool> {
|
||||||
|
self.global_implicit_token
|
||||||
|
.lock()
|
||||||
|
.unwrap_or_else(PoisonError::into_inner)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// All tokens except for the global implicit token will be put back into the jobserver
|
||||||
|
/// immediately and they cannot be cached, since Rust does not call `Drop::drop` on
|
||||||
|
/// global variables.
|
||||||
|
pub(super) fn release_token_raw(&self) {
|
||||||
|
let mut global_implicit_token = self.get_global_implicit_token();
|
||||||
|
|
||||||
|
if *global_implicit_token {
|
||||||
|
// There's already a global implicit token, so this token must
|
||||||
|
// be released back into jobserver.
|
||||||
|
//
|
||||||
|
// `release_raw` should not block
|
||||||
|
let _ = self.inner.release_raw();
|
||||||
|
} else {
|
||||||
|
*global_implicit_token = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn enter_active(&self) -> Result<ActiveJobServer<'_>, Error> {
|
||||||
|
ActiveJobServer::new(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct ActiveJobServer<'a> {
|
||||||
|
jobserver: &'a JobServer,
|
||||||
|
helper_thread: jobserver::HelperThread,
|
||||||
|
/// When rx is dropped, all the token stored within it will be dropped.
|
||||||
|
rx: mpsc::Receiver<io::Result<jobserver::Acquired>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ActiveJobServer<'a> {
|
||||||
|
fn new(jobserver: &'a JobServer) -> Result<Self, Error> {
|
||||||
|
let (tx, rx) = mpsc::channel();
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
rx,
|
||||||
|
helper_thread: jobserver.inner.clone().into_helper_thread(move |res| {
|
||||||
|
let _ = tx.send(res);
|
||||||
|
})?,
|
||||||
|
jobserver,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) async fn acquire(&self) -> Result<JobToken, Error> {
|
||||||
|
let mut has_requested_token = false;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
// Fast path
|
||||||
|
if mem::replace(&mut *self.jobserver.get_global_implicit_token(), false) {
|
||||||
|
break Ok(JobToken::new());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cold path, no global implicit token, obtain one
|
||||||
|
match self.rx.try_recv() {
|
||||||
|
Ok(res) => {
|
||||||
|
let acquired = res?;
|
||||||
|
acquired.drop_without_releasing();
|
||||||
|
break Ok(JobToken::new());
|
||||||
|
}
|
||||||
|
Err(mpsc::TryRecvError::Disconnected) => {
|
||||||
|
break Err(Error::new(
|
||||||
|
ErrorKind::JobserverHelpThreadError,
|
||||||
|
"jobserver help thread has returned before ActiveJobServer is dropped",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
Err(mpsc::TryRecvError::Empty) => {
|
||||||
|
if !has_requested_token {
|
||||||
|
self.helper_thread.request_token();
|
||||||
|
has_requested_token = true;
|
||||||
|
}
|
||||||
|
YieldOnce::default().await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod inprocess_jobserver {
|
||||||
|
use super::JobToken;
|
||||||
|
|
||||||
|
use crate::parallel::async_executor::YieldOnce;
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
env::var,
|
||||||
|
sync::atomic::{
|
||||||
|
AtomicU32,
|
||||||
|
Ordering::{AcqRel, Acquire},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub(crate) struct JobServer(AtomicU32);
|
||||||
|
|
||||||
|
impl JobServer {
|
||||||
|
pub(super) fn new() -> Self {
|
||||||
|
// Use `NUM_JOBS` if set (it's configured by Cargo) and otherwise
|
||||||
|
// just fall back to a semi-reasonable number.
|
||||||
|
//
|
||||||
|
// Note that we could use `num_cpus` here but it's an extra
|
||||||
|
// dependency that will almost never be used, so
|
||||||
|
// it's generally not too worth it.
|
||||||
|
let mut parallelism = 4;
|
||||||
|
// TODO: Use std::thread::available_parallelism as an upper bound
|
||||||
|
// when MSRV is bumped.
|
||||||
|
if let Ok(amt) = var("NUM_JOBS") {
|
||||||
|
if let Ok(amt) = amt.parse() {
|
||||||
|
parallelism = amt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Self(AtomicU32::new(parallelism))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) async fn acquire(&self) -> JobToken {
|
||||||
|
loop {
|
||||||
|
let res = self
|
||||||
|
.0
|
||||||
|
.fetch_update(AcqRel, Acquire, |tokens| tokens.checked_sub(1));
|
||||||
|
|
||||||
|
if res.is_ok() {
|
||||||
|
break JobToken::new();
|
||||||
|
}
|
||||||
|
|
||||||
|
YieldOnce::default().await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn release_token_raw(&self) {
|
||||||
|
self.0.fetch_add(1, AcqRel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
20
third_party/rust/cc/src/parallel/mod.rs
vendored
Normal file
20
third_party/rust/cc/src/parallel/mod.rs
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
pub(crate) mod async_executor;
|
||||||
|
pub(crate) mod job_token;
|
||||||
|
pub(crate) mod stderr;
|
||||||
|
|
||||||
|
/// Remove all element in `vec` which `f(element)` returns `false`.
|
||||||
|
///
|
||||||
|
/// TODO: Remove this once the MSRV is bumped to v1.61
|
||||||
|
pub(crate) fn retain_unordered_mut<T, F>(vec: &mut Vec<T>, mut f: F)
|
||||||
|
where
|
||||||
|
F: FnMut(&mut T) -> bool,
|
||||||
|
{
|
||||||
|
let mut i = 0;
|
||||||
|
while i < vec.len() {
|
||||||
|
if f(&mut vec[i]) {
|
||||||
|
i += 1;
|
||||||
|
} else {
|
||||||
|
vec.swap_remove(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
90
third_party/rust/cc/src/parallel/stderr.rs
vendored
Normal file
90
third_party/rust/cc/src/parallel/stderr.rs
vendored
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
/// Helpers functions for [ChildStderr].
|
||||||
|
use std::{convert::TryInto, process::ChildStderr};
|
||||||
|
|
||||||
|
use crate::{Error, ErrorKind};
|
||||||
|
|
||||||
|
#[cfg(all(not(unix), not(windows)))]
|
||||||
|
compile_error!("Only unix and windows support non-blocking pipes! For other OSes, disable the parallel feature.");
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
fn get_flags(fd: std::os::unix::io::RawFd) -> Result<i32, Error> {
|
||||||
|
let flags = unsafe { libc::fcntl(fd, libc::F_GETFL, 0) };
|
||||||
|
if flags == -1 {
|
||||||
|
Err(Error::new(
|
||||||
|
ErrorKind::IOError,
|
||||||
|
format!(
|
||||||
|
"Failed to get flags for pipe {}: {}",
|
||||||
|
fd,
|
||||||
|
std::io::Error::last_os_error()
|
||||||
|
),
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
Ok(flags)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
fn set_flags(fd: std::os::unix::io::RawFd, flags: std::os::raw::c_int) -> Result<(), Error> {
|
||||||
|
if unsafe { libc::fcntl(fd, libc::F_SETFL, flags) } == -1 {
|
||||||
|
Err(Error::new(
|
||||||
|
ErrorKind::IOError,
|
||||||
|
format!(
|
||||||
|
"Failed to set flags for pipe {}: {}",
|
||||||
|
fd,
|
||||||
|
std::io::Error::last_os_error()
|
||||||
|
),
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
pub fn set_non_blocking(pipe: &impl std::os::unix::io::AsRawFd) -> Result<(), Error> {
|
||||||
|
// On Unix, switch the pipe to non-blocking mode.
|
||||||
|
// On Windows, we have a different way to be non-blocking.
|
||||||
|
let fd = pipe.as_raw_fd();
|
||||||
|
|
||||||
|
let flags = get_flags(fd)?;
|
||||||
|
set_flags(fd, flags | libc::O_NONBLOCK)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bytes_available(stderr: &mut ChildStderr) -> Result<usize, Error> {
|
||||||
|
let mut bytes_available = 0;
|
||||||
|
#[cfg(windows)]
|
||||||
|
{
|
||||||
|
use crate::windows::windows_sys::PeekNamedPipe;
|
||||||
|
use std::os::windows::io::AsRawHandle;
|
||||||
|
use std::ptr::null_mut;
|
||||||
|
if unsafe {
|
||||||
|
PeekNamedPipe(
|
||||||
|
stderr.as_raw_handle(),
|
||||||
|
null_mut(),
|
||||||
|
0,
|
||||||
|
null_mut(),
|
||||||
|
&mut bytes_available,
|
||||||
|
null_mut(),
|
||||||
|
)
|
||||||
|
} == 0
|
||||||
|
{
|
||||||
|
return Err(Error::new(
|
||||||
|
ErrorKind::IOError,
|
||||||
|
format!(
|
||||||
|
"PeekNamedPipe failed with {}",
|
||||||
|
std::io::Error::last_os_error()
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(unix)]
|
||||||
|
{
|
||||||
|
use std::os::unix::io::AsRawFd;
|
||||||
|
if unsafe { libc::ioctl(stderr.as_raw_fd(), libc::FIONREAD, &mut bytes_available) } != 0 {
|
||||||
|
return Err(Error::new(
|
||||||
|
ErrorKind::IOError,
|
||||||
|
format!("ioctl failed with {}", std::io::Error::last_os_error()),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(bytes_available.try_into().unwrap())
|
||||||
|
}
|
399
third_party/rust/cc/src/tool.rs
vendored
Normal file
399
third_party/rust/cc/src/tool.rs
vendored
Normal file
@ -0,0 +1,399 @@
|
|||||||
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
|
ffi::OsString,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
process::Command,
|
||||||
|
sync::Mutex,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::command_helpers::{run_output, CargoOutput};
|
||||||
|
|
||||||
|
/// Configuration used to represent an invocation of a C compiler.
|
||||||
|
///
|
||||||
|
/// This can be used to figure out what compiler is in use, what the arguments
|
||||||
|
/// to it are, and what the environment variables look like for the compiler.
|
||||||
|
/// This can be used to further configure other build systems (e.g. forward
|
||||||
|
/// along CC and/or CFLAGS) or the `to_command` method can be used to run the
|
||||||
|
/// compiler itself.
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
pub struct Tool {
|
||||||
|
pub(crate) path: PathBuf,
|
||||||
|
pub(crate) cc_wrapper_path: Option<PathBuf>,
|
||||||
|
pub(crate) cc_wrapper_args: Vec<OsString>,
|
||||||
|
pub(crate) args: Vec<OsString>,
|
||||||
|
pub(crate) env: Vec<(OsString, OsString)>,
|
||||||
|
pub(crate) family: ToolFamily,
|
||||||
|
pub(crate) cuda: bool,
|
||||||
|
pub(crate) removed_args: Vec<OsString>,
|
||||||
|
pub(crate) has_internal_target_arg: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Tool {
|
||||||
|
pub(crate) fn new(
|
||||||
|
path: PathBuf,
|
||||||
|
cached_compiler_family: &Mutex<HashMap<Box<Path>, ToolFamily>>,
|
||||||
|
cargo_output: &CargoOutput,
|
||||||
|
) -> Self {
|
||||||
|
Self::with_features(path, None, false, cached_compiler_family, cargo_output)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn with_clang_driver(
|
||||||
|
path: PathBuf,
|
||||||
|
clang_driver: Option<&str>,
|
||||||
|
cached_compiler_family: &Mutex<HashMap<Box<Path>, ToolFamily>>,
|
||||||
|
cargo_output: &CargoOutput,
|
||||||
|
) -> Self {
|
||||||
|
Self::with_features(
|
||||||
|
path,
|
||||||
|
clang_driver,
|
||||||
|
false,
|
||||||
|
cached_compiler_family,
|
||||||
|
cargo_output,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Explicitly set the `ToolFamily`, skipping name-based detection.
|
||||||
|
pub(crate) fn with_family(path: PathBuf, family: ToolFamily) -> Self {
|
||||||
|
Self {
|
||||||
|
path,
|
||||||
|
cc_wrapper_path: None,
|
||||||
|
cc_wrapper_args: Vec::new(),
|
||||||
|
args: Vec::new(),
|
||||||
|
env: Vec::new(),
|
||||||
|
family,
|
||||||
|
cuda: false,
|
||||||
|
removed_args: Vec::new(),
|
||||||
|
has_internal_target_arg: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn with_features(
|
||||||
|
path: PathBuf,
|
||||||
|
clang_driver: Option<&str>,
|
||||||
|
cuda: bool,
|
||||||
|
cached_compiler_family: &Mutex<HashMap<Box<Path>, ToolFamily>>,
|
||||||
|
cargo_output: &CargoOutput,
|
||||||
|
) -> Self {
|
||||||
|
fn detect_family_inner(path: &Path, cargo_output: &CargoOutput) -> ToolFamily {
|
||||||
|
let mut cmd = Command::new(path);
|
||||||
|
cmd.arg("--version");
|
||||||
|
|
||||||
|
let stdout = match run_output(
|
||||||
|
&mut cmd,
|
||||||
|
&path.to_string_lossy(),
|
||||||
|
// tool detection issues should always be shown as warnings
|
||||||
|
cargo_output,
|
||||||
|
)
|
||||||
|
.ok()
|
||||||
|
.and_then(|o| String::from_utf8(o).ok())
|
||||||
|
{
|
||||||
|
Some(s) => s,
|
||||||
|
None => {
|
||||||
|
// --version failed. fallback to gnu
|
||||||
|
cargo_output.print_warning(&format_args!("Failed to run: {:?}", cmd));
|
||||||
|
return ToolFamily::Gnu;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if stdout.contains("clang") {
|
||||||
|
ToolFamily::Clang
|
||||||
|
} else if stdout.contains("GCC") {
|
||||||
|
ToolFamily::Gnu
|
||||||
|
} else {
|
||||||
|
// --version doesn't include clang for GCC
|
||||||
|
cargo_output.print_warning(&format_args!(
|
||||||
|
"Compiler version doesn't include clang or GCC: {:?}",
|
||||||
|
cmd
|
||||||
|
));
|
||||||
|
ToolFamily::Gnu
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let detect_family = |path: &Path| -> ToolFamily {
|
||||||
|
if let Some(family) = cached_compiler_family.lock().unwrap().get(path) {
|
||||||
|
return *family;
|
||||||
|
}
|
||||||
|
|
||||||
|
let family = detect_family_inner(path, cargo_output);
|
||||||
|
cached_compiler_family
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.insert(path.into(), family);
|
||||||
|
family
|
||||||
|
};
|
||||||
|
|
||||||
|
// Try to detect family of the tool from its name, falling back to Gnu.
|
||||||
|
let family = if let Some(fname) = path.file_name().and_then(|p| p.to_str()) {
|
||||||
|
if fname.contains("clang-cl") {
|
||||||
|
ToolFamily::Msvc { clang_cl: true }
|
||||||
|
} else if fname.ends_with("cl") || fname == "cl.exe" {
|
||||||
|
ToolFamily::Msvc { clang_cl: false }
|
||||||
|
} else if fname.contains("clang") {
|
||||||
|
match clang_driver {
|
||||||
|
Some("cl") => ToolFamily::Msvc { clang_cl: true },
|
||||||
|
_ => ToolFamily::Clang,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
detect_family(&path)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
detect_family(&path)
|
||||||
|
};
|
||||||
|
|
||||||
|
Tool {
|
||||||
|
path,
|
||||||
|
cc_wrapper_path: None,
|
||||||
|
cc_wrapper_args: Vec::new(),
|
||||||
|
args: Vec::new(),
|
||||||
|
env: Vec::new(),
|
||||||
|
family,
|
||||||
|
cuda,
|
||||||
|
removed_args: Vec::new(),
|
||||||
|
has_internal_target_arg: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add an argument to be stripped from the final command arguments.
|
||||||
|
pub(crate) fn remove_arg(&mut self, flag: OsString) {
|
||||||
|
self.removed_args.push(flag);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Push an "exotic" flag to the end of the compiler's arguments list.
|
||||||
|
///
|
||||||
|
/// Nvidia compiler accepts only the most common compiler flags like `-D`,
|
||||||
|
/// `-I`, `-c`, etc. Options meant specifically for the underlying
|
||||||
|
/// host C++ compiler have to be prefixed with `-Xcompiler`.
|
||||||
|
/// [Another possible future application for this function is passing
|
||||||
|
/// clang-specific flags to clang-cl, which otherwise accepts only
|
||||||
|
/// MSVC-specific options.]
|
||||||
|
pub(crate) fn push_cc_arg(&mut self, flag: OsString) {
|
||||||
|
if self.cuda {
|
||||||
|
self.args.push("-Xcompiler".into());
|
||||||
|
}
|
||||||
|
self.args.push(flag);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks if an argument or flag has already been specified or conflicts.
|
||||||
|
///
|
||||||
|
/// Currently only checks optimization flags.
|
||||||
|
pub(crate) fn is_duplicate_opt_arg(&self, flag: &OsString) -> bool {
|
||||||
|
let flag = flag.to_str().unwrap();
|
||||||
|
let mut chars = flag.chars();
|
||||||
|
|
||||||
|
// Only duplicate check compiler flags
|
||||||
|
if self.is_like_msvc() {
|
||||||
|
if chars.next() != Some('/') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if self.is_like_gnu() || self.is_like_clang() {
|
||||||
|
if chars.next() != Some('-') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for existing optimization flags (-O, /O)
|
||||||
|
if chars.next() == Some('O') {
|
||||||
|
return self
|
||||||
|
.args()
|
||||||
|
.iter()
|
||||||
|
.any(|a| a.to_str().unwrap_or("").chars().nth(1) == Some('O'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO Check for existing -m..., -m...=..., /arch:... flags
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Don't push optimization arg if it conflicts with existing args.
|
||||||
|
pub(crate) fn push_opt_unless_duplicate(&mut self, flag: OsString) {
|
||||||
|
if self.is_duplicate_opt_arg(&flag) {
|
||||||
|
println!("Info: Ignoring duplicate arg {:?}", &flag);
|
||||||
|
} else {
|
||||||
|
self.push_cc_arg(flag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts this compiler into a `Command` that's ready to be run.
|
||||||
|
///
|
||||||
|
/// This is useful for when the compiler needs to be executed and the
|
||||||
|
/// command returned will already have the initial arguments and environment
|
||||||
|
/// variables configured.
|
||||||
|
pub fn to_command(&self) -> Command {
|
||||||
|
let mut cmd = match self.cc_wrapper_path {
|
||||||
|
Some(ref cc_wrapper_path) => {
|
||||||
|
let mut cmd = Command::new(cc_wrapper_path);
|
||||||
|
cmd.arg(&self.path);
|
||||||
|
cmd
|
||||||
|
}
|
||||||
|
None => Command::new(&self.path),
|
||||||
|
};
|
||||||
|
cmd.args(&self.cc_wrapper_args);
|
||||||
|
|
||||||
|
let value = self
|
||||||
|
.args
|
||||||
|
.iter()
|
||||||
|
.filter(|a| !self.removed_args.contains(a))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
cmd.args(&value);
|
||||||
|
|
||||||
|
for (k, v) in self.env.iter() {
|
||||||
|
cmd.env(k, v);
|
||||||
|
}
|
||||||
|
cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the path for this compiler.
|
||||||
|
///
|
||||||
|
/// Note that this may not be a path to a file on the filesystem, e.g. "cc",
|
||||||
|
/// but rather something which will be resolved when a process is spawned.
|
||||||
|
pub fn path(&self) -> &Path {
|
||||||
|
&self.path
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the default set of arguments to the compiler needed to produce
|
||||||
|
/// executables for the target this compiler generates.
|
||||||
|
pub fn args(&self) -> &[OsString] {
|
||||||
|
&self.args
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the set of environment variables needed for this compiler to
|
||||||
|
/// operate.
|
||||||
|
///
|
||||||
|
/// This is typically only used for MSVC compilers currently.
|
||||||
|
pub fn env(&self) -> &[(OsString, OsString)] {
|
||||||
|
&self.env
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the compiler command in format of CC environment variable.
|
||||||
|
/// Or empty string if CC env was not present
|
||||||
|
///
|
||||||
|
/// This is typically used by configure script
|
||||||
|
pub fn cc_env(&self) -> OsString {
|
||||||
|
match self.cc_wrapper_path {
|
||||||
|
Some(ref cc_wrapper_path) => {
|
||||||
|
let mut cc_env = cc_wrapper_path.as_os_str().to_owned();
|
||||||
|
cc_env.push(" ");
|
||||||
|
cc_env.push(self.path.to_path_buf().into_os_string());
|
||||||
|
for arg in self.cc_wrapper_args.iter() {
|
||||||
|
cc_env.push(" ");
|
||||||
|
cc_env.push(arg);
|
||||||
|
}
|
||||||
|
cc_env
|
||||||
|
}
|
||||||
|
None => OsString::from(""),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the compiler flags in format of CFLAGS environment variable.
|
||||||
|
/// Important here - this will not be CFLAGS from env, its internal gcc's flags to use as CFLAGS
|
||||||
|
/// This is typically used by configure script
|
||||||
|
pub fn cflags_env(&self) -> OsString {
|
||||||
|
let mut flags = OsString::new();
|
||||||
|
for (i, arg) in self.args.iter().enumerate() {
|
||||||
|
if i > 0 {
|
||||||
|
flags.push(" ");
|
||||||
|
}
|
||||||
|
flags.push(arg);
|
||||||
|
}
|
||||||
|
flags
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether the tool is GNU Compiler Collection-like.
|
||||||
|
pub fn is_like_gnu(&self) -> bool {
|
||||||
|
self.family == ToolFamily::Gnu
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether the tool is Clang-like.
|
||||||
|
pub fn is_like_clang(&self) -> bool {
|
||||||
|
self.family == ToolFamily::Clang
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether the tool is AppleClang under .xctoolchain
|
||||||
|
#[cfg(target_vendor = "apple")]
|
||||||
|
pub(crate) fn is_xctoolchain_clang(&self) -> bool {
|
||||||
|
let path = self.path.to_string_lossy();
|
||||||
|
path.contains(".xctoolchain/")
|
||||||
|
}
|
||||||
|
#[cfg(not(target_vendor = "apple"))]
|
||||||
|
pub(crate) fn is_xctoolchain_clang(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether the tool is MSVC-like.
|
||||||
|
pub fn is_like_msvc(&self) -> bool {
|
||||||
|
match self.family {
|
||||||
|
ToolFamily::Msvc { .. } => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents the family of tools this tool belongs to.
|
||||||
|
///
|
||||||
|
/// Each family of tools differs in how and what arguments they accept.
|
||||||
|
///
|
||||||
|
/// Detection of a family is done on best-effort basis and may not accurately reflect the tool.
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||||
|
pub enum ToolFamily {
|
||||||
|
/// Tool is GNU Compiler Collection-like.
|
||||||
|
Gnu,
|
||||||
|
/// Tool is Clang-like. It differs from the GCC in a sense that it accepts superset of flags
|
||||||
|
/// and its cross-compilation approach is different.
|
||||||
|
Clang,
|
||||||
|
/// Tool is the MSVC cl.exe.
|
||||||
|
Msvc { clang_cl: bool },
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToolFamily {
|
||||||
|
/// What the flag to request debug info for this family of tools look like
|
||||||
|
pub(crate) fn add_debug_flags(&self, cmd: &mut Tool, dwarf_version: Option<u32>) {
|
||||||
|
match *self {
|
||||||
|
ToolFamily::Msvc { .. } => {
|
||||||
|
cmd.push_cc_arg("-Z7".into());
|
||||||
|
}
|
||||||
|
ToolFamily::Gnu | ToolFamily::Clang => {
|
||||||
|
cmd.push_cc_arg(
|
||||||
|
dwarf_version
|
||||||
|
.map_or_else(|| "-g".into(), |v| format!("-gdwarf-{}", v))
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// What the flag to force frame pointers.
|
||||||
|
pub(crate) fn add_force_frame_pointer(&self, cmd: &mut Tool) {
|
||||||
|
match *self {
|
||||||
|
ToolFamily::Gnu | ToolFamily::Clang => {
|
||||||
|
cmd.push_cc_arg("-fno-omit-frame-pointer".into());
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// What the flags to enable all warnings
|
||||||
|
pub(crate) fn warnings_flags(&self) -> &'static str {
|
||||||
|
match *self {
|
||||||
|
ToolFamily::Msvc { .. } => "-W4",
|
||||||
|
ToolFamily::Gnu | ToolFamily::Clang => "-Wall",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// What the flags to enable extra warnings
|
||||||
|
pub(crate) fn extra_warnings_flags(&self) -> Option<&'static str> {
|
||||||
|
match *self {
|
||||||
|
ToolFamily::Msvc { .. } => None,
|
||||||
|
ToolFamily::Gnu | ToolFamily::Clang => Some("-Wextra"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// What the flag to turn warning into errors
|
||||||
|
pub(crate) fn warnings_to_errors_flag(&self) -> &'static str {
|
||||||
|
match *self {
|
||||||
|
ToolFamily::Msvc { .. } => "-WX",
|
||||||
|
ToolFamily::Gnu | ToolFamily::Clang => "-Werror",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn verbose_stderr(&self) -> bool {
|
||||||
|
*self == ToolFamily::Clang
|
||||||
|
}
|
||||||
|
}
|
@ -7,27 +7,31 @@
|
|||||||
|
|
||||||
#![allow(unused)]
|
#![allow(unused)]
|
||||||
|
|
||||||
use crate::winapi::CoInitializeEx;
|
use crate::windows::{
|
||||||
use crate::winapi::IUnknown;
|
winapi::{IUnknown, Interface},
|
||||||
use crate::winapi::Interface;
|
windows_sys::{
|
||||||
use crate::winapi::BSTR;
|
CoInitializeEx, SysFreeString, SysStringLen, BSTR, COINIT_MULTITHREADED, HRESULT, S_FALSE,
|
||||||
use crate::winapi::COINIT_MULTITHREADED;
|
S_OK,
|
||||||
use crate::winapi::{SysFreeString, SysStringLen};
|
},
|
||||||
use crate::winapi::{HRESULT, S_FALSE, S_OK};
|
};
|
||||||
use std::ffi::{OsStr, OsString};
|
use std::{
|
||||||
use std::mem::forget;
|
convert::TryInto,
|
||||||
use std::ops::Deref;
|
ffi::{OsStr, OsString},
|
||||||
use std::os::windows::ffi::{OsStrExt, OsStringExt};
|
mem::ManuallyDrop,
|
||||||
use std::ptr::null_mut;
|
ops::Deref,
|
||||||
use std::slice::from_raw_parts;
|
os::windows::ffi::{OsStrExt, OsStringExt},
|
||||||
|
ptr::{null, null_mut},
|
||||||
|
slice::from_raw_parts,
|
||||||
|
};
|
||||||
|
|
||||||
pub fn initialize() -> Result<(), HRESULT> {
|
pub fn initialize() -> Result<(), HRESULT> {
|
||||||
let err = unsafe { CoInitializeEx(null_mut(), COINIT_MULTITHREADED) };
|
let err = unsafe { CoInitializeEx(null(), COINIT_MULTITHREADED.try_into().unwrap()) };
|
||||||
if err != S_OK && err != S_FALSE {
|
if err != S_OK && err != S_FALSE {
|
||||||
// S_FALSE just means COM is already initialized
|
// S_FALSE just means COM is already initialized
|
||||||
return Err(err);
|
Err(err)
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ComPtr<T>(*mut T)
|
pub struct ComPtr<T>(*mut T)
|
||||||
@ -55,15 +59,13 @@ where
|
|||||||
/// Extracts the raw pointer.
|
/// Extracts the raw pointer.
|
||||||
/// You are now responsible for releasing it yourself.
|
/// You are now responsible for releasing it yourself.
|
||||||
pub fn into_raw(self) -> *mut T {
|
pub fn into_raw(self) -> *mut T {
|
||||||
let p = self.0;
|
ManuallyDrop::new(self).0
|
||||||
forget(self);
|
|
||||||
p
|
|
||||||
}
|
}
|
||||||
/// For internal use only.
|
/// For internal use only.
|
||||||
fn as_unknown(&self) -> &IUnknown {
|
fn as_unknown(&self) -> &IUnknown {
|
||||||
unsafe { &*(self.0 as *mut IUnknown) }
|
unsafe { &*(self.0 as *mut IUnknown) }
|
||||||
}
|
}
|
||||||
/// Performs QueryInterface fun.
|
/// Performs `QueryInterface` fun.
|
||||||
pub fn cast<U>(&self) -> Result<ComPtr<U>, i32>
|
pub fn cast<U>(&self) -> Result<ComPtr<U>, i32>
|
||||||
where
|
where
|
||||||
U: Interface,
|
U: Interface,
|
@ -8,18 +8,34 @@
|
|||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
//! A helper module to probe the Windows Registry when looking for
|
//! A helper module to looking for windows-specific tools:
|
||||||
//! windows-specific tools.
|
//! 1. On Windows host, probe the Windows Registry if needed;
|
||||||
|
//! 2. On non-Windows host, check specified environment variables.
|
||||||
|
|
||||||
|
#![allow(clippy::upper_case_acronyms)]
|
||||||
|
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
|
||||||
use crate::Tool;
|
use crate::Tool;
|
||||||
#[cfg(windows)]
|
|
||||||
use crate::ToolFamily;
|
use crate::ToolFamily;
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
const MSVC_FAMILY: ToolFamily = ToolFamily::Msvc { clang_cl: false };
|
const MSVC_FAMILY: ToolFamily = ToolFamily::Msvc { clang_cl: false };
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
struct TargetArch<'a>(pub &'a str);
|
||||||
|
|
||||||
|
impl PartialEq<&str> for TargetArch<'_> {
|
||||||
|
fn eq(&self, other: &&str) -> bool {
|
||||||
|
self.0 == *other
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<TargetArch<'a>> for &'a str {
|
||||||
|
fn from(target: TargetArch<'a>) -> Self {
|
||||||
|
target.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Attempts to find a tool within an MSVC installation using the Windows
|
/// Attempts to find a tool within an MSVC installation using the Windows
|
||||||
/// registry as a point to search from.
|
/// registry as a point to search from.
|
||||||
///
|
///
|
||||||
@ -39,13 +55,6 @@ pub fn find(target: &str, tool: &str) -> Option<Command> {
|
|||||||
/// Similar to the `find` function above, this function will attempt the same
|
/// Similar to the `find` function above, this function will attempt the same
|
||||||
/// operation (finding a MSVC tool in a local install) but instead returns a
|
/// operation (finding a MSVC tool in a local install) but instead returns a
|
||||||
/// `Tool` which may be introspected.
|
/// `Tool` which may be introspected.
|
||||||
#[cfg(not(windows))]
|
|
||||||
pub fn find_tool(_target: &str, _tool: &str) -> Option<Tool> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Documented above.
|
|
||||||
#[cfg(windows)]
|
|
||||||
pub fn find_tool(target: &str, tool: &str) -> Option<Tool> {
|
pub fn find_tool(target: &str, tool: &str) -> Option<Tool> {
|
||||||
// This logic is all tailored for MSVC, if we're not that then bail out
|
// This logic is all tailored for MSVC, if we're not that then bail out
|
||||||
// early.
|
// early.
|
||||||
@ -53,13 +62,17 @@ pub fn find_tool(target: &str, tool: &str) -> Option<Tool> {
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Split the target to get the arch.
|
||||||
|
let target = TargetArch(target.split_once('-')?.0);
|
||||||
|
|
||||||
// Looks like msbuild isn't located in the same location as other tools like
|
// Looks like msbuild isn't located in the same location as other tools like
|
||||||
// cl.exe and lib.exe. To handle this we probe for it manually with
|
// cl.exe and lib.exe.
|
||||||
// dedicated registry keys.
|
|
||||||
if tool.contains("msbuild") {
|
if tool.contains("msbuild") {
|
||||||
return impl_::find_msbuild(target);
|
return impl_::find_msbuild(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Looks like devenv isn't located in the same location as other tools like
|
||||||
|
// cl.exe and lib.exe.
|
||||||
if tool.contains("devenv") {
|
if tool.contains("devenv") {
|
||||||
return impl_::find_devenv(target);
|
return impl_::find_devenv(target);
|
||||||
}
|
}
|
||||||
@ -71,15 +84,16 @@ pub fn find_tool(target: &str, tool: &str) -> Option<Tool> {
|
|||||||
// environment variables like `LIB`, `INCLUDE`, and `PATH` to ensure that
|
// environment variables like `LIB`, `INCLUDE`, and `PATH` to ensure that
|
||||||
// the tool is actually usable.
|
// the tool is actually usable.
|
||||||
|
|
||||||
return impl_::find_msvc_environment(tool, target)
|
impl_::find_msvc_environment(tool, target)
|
||||||
.or_else(|| impl_::find_msvc_15plus(tool, target))
|
.or_else(|| impl_::find_msvc_15plus(tool, target))
|
||||||
.or_else(|| impl_::find_msvc_14(tool, target))
|
.or_else(|| impl_::find_msvc_14(tool, target))
|
||||||
.or_else(|| impl_::find_msvc_12(tool, target))
|
.or_else(|| impl_::find_msvc_12(tool, target))
|
||||||
.or_else(|| impl_::find_msvc_11(tool, target));
|
.or_else(|| impl_::find_msvc_11(tool, target))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A version of Visual Studio
|
/// A version of Visual Studio
|
||||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||||
|
#[non_exhaustive]
|
||||||
pub enum VsVers {
|
pub enum VsVers {
|
||||||
/// Visual Studio 12 (2013)
|
/// Visual Studio 12 (2013)
|
||||||
Vs12,
|
Vs12,
|
||||||
@ -91,30 +105,14 @@ pub enum VsVers {
|
|||||||
Vs16,
|
Vs16,
|
||||||
/// Visual Studio 17 (2022)
|
/// Visual Studio 17 (2022)
|
||||||
Vs17,
|
Vs17,
|
||||||
|
|
||||||
/// Hidden variant that should not be matched on. Callers that want to
|
|
||||||
/// handle an enumeration of `VsVers` instances should always have a default
|
|
||||||
/// case meaning that it's a VS version they don't understand.
|
|
||||||
#[doc(hidden)]
|
|
||||||
#[allow(bad_style)]
|
|
||||||
__Nonexhaustive_do_not_match_this_or_your_code_will_break,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Find the most recent installed version of Visual Studio
|
/// Find the most recent installed version of Visual Studio
|
||||||
///
|
///
|
||||||
/// This is used by the cmake crate to figure out the correct
|
/// This is used by the cmake crate to figure out the correct
|
||||||
/// generator.
|
/// generator.
|
||||||
#[cfg(not(windows))]
|
|
||||||
pub fn find_vs_version() -> Result<VsVers, String> {
|
pub fn find_vs_version() -> Result<VsVers, String> {
|
||||||
Err(format!("not windows"))
|
match std::env::var("VisualStudioVersion") {
|
||||||
}
|
|
||||||
|
|
||||||
/// Documented above
|
|
||||||
#[cfg(windows)]
|
|
||||||
pub fn find_vs_version() -> Result<VsVers, String> {
|
|
||||||
use std::env;
|
|
||||||
|
|
||||||
match env::var("VisualStudioVersion") {
|
|
||||||
Ok(version) => match &version[..] {
|
Ok(version) => match &version[..] {
|
||||||
"17.0" => Ok(VsVers::Vs17),
|
"17.0" => Ok(VsVers::Vs17),
|
||||||
"16.0" => Ok(VsVers::Vs16),
|
"16.0" => Ok(VsVers::Vs16),
|
||||||
@ -158,12 +156,17 @@ pub fn find_vs_version() -> Result<VsVers, String> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Windows Implementation.
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
mod impl_ {
|
mod impl_ {
|
||||||
use crate::com;
|
use crate::windows::com;
|
||||||
use crate::registry::{RegistryKey, LOCAL_MACHINE};
|
use crate::windows::registry::{RegistryKey, LOCAL_MACHINE};
|
||||||
use crate::setup_config::SetupConfiguration;
|
use crate::windows::setup_config::SetupConfiguration;
|
||||||
use crate::vs_instances::{VsInstances, VswhereInstance};
|
use crate::windows::vs_instances::{VsInstances, VswhereInstance};
|
||||||
|
use crate::windows::windows_sys::{
|
||||||
|
FreeLibrary, GetMachineTypeAttributes, GetProcAddress, LoadLibraryA, UserEnabled, HMODULE,
|
||||||
|
IMAGE_FILE_MACHINE_AMD64, MACHINE_ATTRIBUTES, S_OK,
|
||||||
|
};
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::ffi::OsString;
|
use std::ffi::OsString;
|
||||||
@ -174,8 +177,10 @@ mod impl_ {
|
|||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
use std::sync::Once;
|
||||||
|
|
||||||
use super::MSVC_FAMILY;
|
use super::{TargetArch, MSVC_FAMILY};
|
||||||
use crate::Tool;
|
use crate::Tool;
|
||||||
|
|
||||||
struct MsvcTool {
|
struct MsvcTool {
|
||||||
@ -185,10 +190,75 @@ mod impl_ {
|
|||||||
include: Vec<PathBuf>,
|
include: Vec<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct LibraryHandle(HMODULE);
|
||||||
|
|
||||||
|
impl LibraryHandle {
|
||||||
|
fn new(name: &[u8]) -> Option<Self> {
|
||||||
|
let handle = unsafe { LoadLibraryA(name.as_ptr() as _) };
|
||||||
|
(!handle.is_null()).then(|| Self(handle))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a function pointer to a function in the library.
|
||||||
|
/// SAFETY: The caller must ensure that the function signature matches the actual function.
|
||||||
|
/// The easiest way to do this is to add an entry to windows_sys_no_link.list and use the
|
||||||
|
/// generated function for `func_signature`.
|
||||||
|
unsafe fn get_proc_address<F>(&self, name: &[u8]) -> Option<F> {
|
||||||
|
let symbol = unsafe { GetProcAddress(self.0, name.as_ptr() as _) };
|
||||||
|
symbol.map(|symbol| unsafe { mem::transmute_copy(&symbol) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for LibraryHandle {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe { FreeLibrary(self.0) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetMachineTypeAttributesFuncType =
|
||||||
|
unsafe extern "system" fn(u16, *mut MACHINE_ATTRIBUTES) -> i32;
|
||||||
|
const _: () = {
|
||||||
|
// Ensure that our hand-written signature matches the actual function signature.
|
||||||
|
// We can't use `GetMachineTypeAttributes` outside of a const scope otherwise we'll end up statically linking to
|
||||||
|
// it, which will fail to load on older versions of Windows.
|
||||||
|
let _: GetMachineTypeAttributesFuncType = GetMachineTypeAttributes;
|
||||||
|
};
|
||||||
|
|
||||||
|
fn is_amd64_emulation_supported_inner() -> Option<bool> {
|
||||||
|
// GetMachineTypeAttributes is only available on Win11 22000+, so dynamically load it.
|
||||||
|
let kernel32 = LibraryHandle::new(b"kernel32.dll\0")?;
|
||||||
|
// SAFETY: GetMachineTypeAttributesFuncType is checked to match the real function signature.
|
||||||
|
let get_machine_type_attributes = unsafe {
|
||||||
|
kernel32
|
||||||
|
.get_proc_address::<GetMachineTypeAttributesFuncType>(b"GetMachineTypeAttributes\0")
|
||||||
|
}?;
|
||||||
|
let mut attributes = Default::default();
|
||||||
|
if unsafe { get_machine_type_attributes(IMAGE_FILE_MACHINE_AMD64, &mut attributes) } == S_OK
|
||||||
|
{
|
||||||
|
Some((attributes & UserEnabled) != 0)
|
||||||
|
} else {
|
||||||
|
Some(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_amd64_emulation_supported() -> bool {
|
||||||
|
// TODO: Replace with a OnceLock once MSRV is 1.70.
|
||||||
|
static LOAD_VALUE: Once = Once::new();
|
||||||
|
static IS_SUPPORTED: AtomicBool = AtomicBool::new(false);
|
||||||
|
|
||||||
|
// Using Relaxed ordering since the Once is providing synchronization.
|
||||||
|
LOAD_VALUE.call_once(|| {
|
||||||
|
IS_SUPPORTED.store(
|
||||||
|
is_amd64_emulation_supported_inner().unwrap_or(false),
|
||||||
|
Ordering::Relaxed,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
IS_SUPPORTED.load(Ordering::Relaxed)
|
||||||
|
}
|
||||||
|
|
||||||
impl MsvcTool {
|
impl MsvcTool {
|
||||||
fn new(tool: PathBuf) -> MsvcTool {
|
fn new(tool: PathBuf) -> MsvcTool {
|
||||||
MsvcTool {
|
MsvcTool {
|
||||||
tool: tool,
|
tool,
|
||||||
libs: Vec::new(),
|
libs: Vec::new(),
|
||||||
path: Vec::new(),
|
path: Vec::new(),
|
||||||
include: Vec::new(),
|
include: Vec::new(),
|
||||||
@ -202,7 +272,7 @@ mod impl_ {
|
|||||||
path,
|
path,
|
||||||
include,
|
include,
|
||||||
} = self;
|
} = self;
|
||||||
let mut tool = Tool::with_family(tool.into(), MSVC_FAMILY);
|
let mut tool = Tool::with_family(tool, MSVC_FAMILY);
|
||||||
add_env(&mut tool, "LIB", libs);
|
add_env(&mut tool, "LIB", libs);
|
||||||
add_env(&mut tool, "PATH", path);
|
add_env(&mut tool, "PATH", path);
|
||||||
add_env(&mut tool, "INCLUDE", include);
|
add_env(&mut tool, "INCLUDE", include);
|
||||||
@ -212,15 +282,14 @@ mod impl_ {
|
|||||||
|
|
||||||
/// Checks to see if the `VSCMD_ARG_TGT_ARCH` environment variable matches the
|
/// Checks to see if the `VSCMD_ARG_TGT_ARCH` environment variable matches the
|
||||||
/// given target's arch. Returns `None` if the variable does not exist.
|
/// given target's arch. Returns `None` if the variable does not exist.
|
||||||
#[cfg(windows)]
|
fn is_vscmd_target(target: TargetArch<'_>) -> Option<bool> {
|
||||||
fn is_vscmd_target(target: &str) -> Option<bool> {
|
|
||||||
let vscmd_arch = env::var("VSCMD_ARG_TGT_ARCH").ok()?;
|
let vscmd_arch = env::var("VSCMD_ARG_TGT_ARCH").ok()?;
|
||||||
// Convert the Rust target arch to its VS arch equivalent.
|
// Convert the Rust target arch to its VS arch equivalent.
|
||||||
let arch = match target.split("-").next() {
|
let arch = match target.into() {
|
||||||
Some("x86_64") => "x64",
|
"x86_64" => "x64",
|
||||||
Some("aarch64") => "arm64",
|
"aarch64" | "arm64ec" => "arm64",
|
||||||
Some("i686") | Some("i586") => "x86",
|
"i686" | "i586" => "x86",
|
||||||
Some("thumbv7a") => "arm",
|
"thumbv7a" => "arm",
|
||||||
// An unrecognized arch.
|
// An unrecognized arch.
|
||||||
_ => return Some(false),
|
_ => return Some(false),
|
||||||
};
|
};
|
||||||
@ -228,7 +297,7 @@ mod impl_ {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Attempt to find the tool using environment variables set by vcvars.
|
/// Attempt to find the tool using environment variables set by vcvars.
|
||||||
pub fn find_msvc_environment(tool: &str, target: &str) -> Option<Tool> {
|
pub(super) fn find_msvc_environment(tool: &str, target: TargetArch<'_>) -> Option<Tool> {
|
||||||
// Early return if the environment doesn't contain a VC install.
|
// Early return if the environment doesn't contain a VC install.
|
||||||
if env::var_os("VCINSTALLDIR").is_none() {
|
if env::var_os("VCINSTALLDIR").is_none() {
|
||||||
return None;
|
return None;
|
||||||
@ -248,16 +317,19 @@ mod impl_ {
|
|||||||
.map(|p| p.join(tool))
|
.map(|p| p.join(tool))
|
||||||
.find(|p| p.exists())
|
.find(|p| p.exists())
|
||||||
})
|
})
|
||||||
.map(|path| Tool::with_family(path.into(), MSVC_FAMILY))
|
.map(|path| Tool::with_family(path, MSVC_FAMILY))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_msbuild_vs17(target: &str) -> Option<Tool> {
|
fn find_msbuild_vs17(target: TargetArch<'_>) -> Option<Tool> {
|
||||||
find_tool_in_vs16plus_path(r"MSBuild\Current\Bin\MSBuild.exe", target, "17")
|
find_tool_in_vs16plus_path(r"MSBuild\Current\Bin\MSBuild.exe", target, "17")
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(bare_trait_objects)]
|
#[allow(bare_trait_objects)]
|
||||||
fn vs16plus_instances(target: &str, version: &'static str) -> Box<Iterator<Item = PathBuf>> {
|
fn vs16plus_instances(
|
||||||
|
target: TargetArch<'_>,
|
||||||
|
version: &'static str,
|
||||||
|
) -> Box<Iterator<Item = PathBuf>> {
|
||||||
let instances = if let Some(instances) = vs15plus_instances(target) {
|
let instances = if let Some(instances) = vs15plus_instances(target) {
|
||||||
instances
|
instances
|
||||||
} else {
|
} else {
|
||||||
@ -275,7 +347,11 @@ mod impl_ {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_tool_in_vs16plus_path(tool: &str, target: &str, version: &'static str) -> Option<Tool> {
|
fn find_tool_in_vs16plus_path(
|
||||||
|
tool: &str,
|
||||||
|
target: TargetArch<'_>,
|
||||||
|
version: &'static str,
|
||||||
|
) -> Option<Tool> {
|
||||||
vs16plus_instances(target, version)
|
vs16plus_instances(target, version)
|
||||||
.filter_map(|path| {
|
.filter_map(|path| {
|
||||||
let path = path.join(tool);
|
let path = path.join(tool);
|
||||||
@ -283,10 +359,10 @@ mod impl_ {
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let mut tool = Tool::with_family(path, MSVC_FAMILY);
|
let mut tool = Tool::with_family(path, MSVC_FAMILY);
|
||||||
if target.contains("x86_64") {
|
if target == "x86_64" {
|
||||||
tool.env.push(("Platform".into(), "X64".into()));
|
tool.env.push(("Platform".into(), "X64".into()));
|
||||||
}
|
}
|
||||||
if target.contains("aarch64") {
|
if target == "aarch64" || target == "arm64ec" {
|
||||||
tool.env.push(("Platform".into(), "ARM64".into()));
|
tool.env.push(("Platform".into(), "ARM64".into()));
|
||||||
}
|
}
|
||||||
Some(tool)
|
Some(tool)
|
||||||
@ -294,7 +370,7 @@ mod impl_ {
|
|||||||
.next()
|
.next()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_msbuild_vs16(target: &str) -> Option<Tool> {
|
fn find_msbuild_vs16(target: TargetArch<'_>) -> Option<Tool> {
|
||||||
find_tool_in_vs16plus_path(r"MSBuild\Current\Bin\MSBuild.exe", target, "16")
|
find_tool_in_vs16plus_path(r"MSBuild\Current\Bin\MSBuild.exe", target, "16")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -310,7 +386,7 @@ mod impl_ {
|
|||||||
//
|
//
|
||||||
// However, on ARM64 this method doesn't work because VS Installer fails to register COM component on ARM64.
|
// However, on ARM64 this method doesn't work because VS Installer fails to register COM component on ARM64.
|
||||||
// Hence, as the last resort we try to use vswhere.exe to list available instances.
|
// Hence, as the last resort we try to use vswhere.exe to list available instances.
|
||||||
fn vs15plus_instances(target: &str) -> Option<VsInstances> {
|
fn vs15plus_instances(target: TargetArch<'_>) -> Option<VsInstances> {
|
||||||
vs15plus_instances_using_com().or_else(|| vs15plus_instances_using_vswhere(target))
|
vs15plus_instances_using_com().or_else(|| vs15plus_instances_using_vswhere(target))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -323,7 +399,7 @@ mod impl_ {
|
|||||||
Some(VsInstances::ComBased(enum_setup_instances))
|
Some(VsInstances::ComBased(enum_setup_instances))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn vs15plus_instances_using_vswhere(target: &str) -> Option<VsInstances> {
|
fn vs15plus_instances_using_vswhere(target: TargetArch<'_>) -> Option<VsInstances> {
|
||||||
let program_files_path: PathBuf = env::var("ProgramFiles(x86)")
|
let program_files_path: PathBuf = env::var("ProgramFiles(x86)")
|
||||||
.or_else(|_| env::var("ProgramFiles"))
|
.or_else(|_| env::var("ProgramFiles"))
|
||||||
.ok()?
|
.ok()?
|
||||||
@ -336,11 +412,10 @@ mod impl_ {
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let arch = target.split('-').next().unwrap();
|
let tools_arch = match target.into() {
|
||||||
let tools_arch = match arch {
|
|
||||||
"i586" | "i686" | "x86_64" => Some("x86.x64"),
|
"i586" | "i686" | "x86_64" => Some("x86.x64"),
|
||||||
"arm" | "thumbv7a" => Some("ARM"),
|
"arm" | "thumbv7a" => Some("ARM"),
|
||||||
"aarch64" => Some("ARM64"),
|
"aarch64" | "arm64ec" => Some("ARM64"),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -374,7 +449,7 @@ mod impl_ {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_msvc_15plus(tool: &str, target: &str) -> Option<Tool> {
|
pub(super) fn find_msvc_15plus(tool: &str, target: TargetArch<'_>) -> Option<Tool> {
|
||||||
let iter = vs15plus_instances(target)?;
|
let iter = vs15plus_instances(target)?;
|
||||||
iter.into_iter()
|
iter.into_iter()
|
||||||
.filter_map(|instance| {
|
.filter_map(|instance| {
|
||||||
@ -394,13 +469,13 @@ mod impl_ {
|
|||||||
// we keep the registry method as a fallback option.
|
// we keep the registry method as a fallback option.
|
||||||
//
|
//
|
||||||
// [more reliable]: https://github.com/rust-lang/cc-rs/pull/331
|
// [more reliable]: https://github.com/rust-lang/cc-rs/pull/331
|
||||||
fn find_tool_in_vs15_path(tool: &str, target: &str) -> Option<Tool> {
|
fn find_tool_in_vs15_path(tool: &str, target: TargetArch<'_>) -> Option<Tool> {
|
||||||
let mut path = match vs15plus_instances(target) {
|
let mut path = match vs15plus_instances(target) {
|
||||||
Some(instances) => instances
|
Some(instances) => instances
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|instance| instance.installation_path())
|
.filter_map(|instance| instance.installation_path())
|
||||||
.map(|path| path.join(tool))
|
.map(|path| path.join(tool))
|
||||||
.find(|ref path| path.is_file()),
|
.find(|path| path.is_file()),
|
||||||
None => None,
|
None => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -416,10 +491,9 @@ mod impl_ {
|
|||||||
|
|
||||||
path.map(|path| {
|
path.map(|path| {
|
||||||
let mut tool = Tool::with_family(path, MSVC_FAMILY);
|
let mut tool = Tool::with_family(path, MSVC_FAMILY);
|
||||||
if target.contains("x86_64") {
|
if target == "x86_64" {
|
||||||
tool.env.push(("Platform".into(), "X64".into()));
|
tool.env.push(("Platform".into(), "X64".into()));
|
||||||
}
|
} else if target == "aarch64" {
|
||||||
if target.contains("aarch64") {
|
|
||||||
tool.env.push(("Platform".into(), "ARM64".into()));
|
tool.env.push(("Platform".into(), "ARM64".into()));
|
||||||
}
|
}
|
||||||
tool
|
tool
|
||||||
@ -428,10 +502,10 @@ mod impl_ {
|
|||||||
|
|
||||||
fn tool_from_vs15plus_instance(
|
fn tool_from_vs15plus_instance(
|
||||||
tool: &str,
|
tool: &str,
|
||||||
target: &str,
|
target: TargetArch<'_>,
|
||||||
instance_path: &PathBuf,
|
instance_path: &PathBuf,
|
||||||
) -> Option<Tool> {
|
) -> Option<Tool> {
|
||||||
let (root_path, bin_path, host_dylib_path, lib_path, include_path) =
|
let (root_path, bin_path, host_dylib_path, lib_path, alt_lib_path, include_path) =
|
||||||
vs15plus_vc_paths(target, instance_path)?;
|
vs15plus_vc_paths(target, instance_path)?;
|
||||||
let tool_path = bin_path.join(tool);
|
let tool_path = bin_path.join(tool);
|
||||||
if !tool_path.exists() {
|
if !tool_path.exists() {
|
||||||
@ -441,6 +515,9 @@ mod impl_ {
|
|||||||
let mut tool = MsvcTool::new(tool_path);
|
let mut tool = MsvcTool::new(tool_path);
|
||||||
tool.path.push(bin_path.clone());
|
tool.path.push(bin_path.clone());
|
||||||
tool.path.push(host_dylib_path);
|
tool.path.push(host_dylib_path);
|
||||||
|
if let Some(alt_lib_path) = alt_lib_path {
|
||||||
|
tool.libs.push(alt_lib_path);
|
||||||
|
}
|
||||||
tool.libs.push(lib_path);
|
tool.libs.push(lib_path);
|
||||||
tool.include.push(include_path);
|
tool.include.push(include_path);
|
||||||
|
|
||||||
@ -455,45 +532,97 @@ mod impl_ {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn vs15plus_vc_paths(
|
fn vs15plus_vc_paths(
|
||||||
target: &str,
|
target: TargetArch<'_>,
|
||||||
instance_path: &PathBuf,
|
instance_path: &Path,
|
||||||
) -> Option<(PathBuf, PathBuf, PathBuf, PathBuf, PathBuf)> {
|
) -> Option<(PathBuf, PathBuf, PathBuf, PathBuf, Option<PathBuf>, PathBuf)> {
|
||||||
let version_path =
|
let version = vs15plus_vc_read_version(instance_path)?;
|
||||||
instance_path.join(r"VC\Auxiliary\Build\Microsoft.VCToolsVersion.default.txt");
|
|
||||||
let mut version_file = File::open(version_path).ok()?;
|
let hosts = match host_arch() {
|
||||||
let mut version = String::new();
|
X86 => &["X86"],
|
||||||
version_file.read_to_string(&mut version).ok()?;
|
X86_64 => &["X64"],
|
||||||
let version = version.trim();
|
// Starting with VS 17.4, there is a natively hosted compiler on ARM64:
|
||||||
let host = match host_arch() {
|
// https://devblogs.microsoft.com/visualstudio/arm64-visual-studio-is-officially-here/
|
||||||
X86 => "X86",
|
// On older versions of VS, we use x64 if running under emulation is supported,
|
||||||
X86_64 => "X64",
|
// otherwise use x86.
|
||||||
// There is no natively hosted compiler on ARM64.
|
AARCH64 => {
|
||||||
// Instead, use the x86 toolchain under emulation (there is no x64 emulation).
|
if is_amd64_emulation_supported() {
|
||||||
AARCH64 => "X86",
|
&["ARM64", "X64", "X86"][..]
|
||||||
|
} else {
|
||||||
|
&["ARM64", "X86"]
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => return None,
|
_ => return None,
|
||||||
};
|
};
|
||||||
let target = lib_subdir(target)?;
|
let target = lib_subdir(target)?;
|
||||||
// The directory layout here is MSVC/bin/Host$host/$target/
|
// The directory layout here is MSVC/bin/Host$host/$target/
|
||||||
let path = instance_path.join(r"VC\Tools\MSVC").join(version);
|
let path = instance_path.join(r"VC\Tools\MSVC").join(version);
|
||||||
|
// We use the first available host architecture that can build for the target
|
||||||
|
let (host_path, host) = hosts.iter().find_map(|&x| {
|
||||||
|
let candidate = path.join("bin").join(format!("Host{}", x));
|
||||||
|
if candidate.join(target).exists() {
|
||||||
|
Some((candidate, x))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})?;
|
||||||
// This is the path to the toolchain for a particular target, running
|
// This is the path to the toolchain for a particular target, running
|
||||||
// on a given host
|
// on a given host
|
||||||
let bin_path = path
|
let bin_path = host_path.join(target);
|
||||||
.join("bin")
|
|
||||||
.join(&format!("Host{}", host))
|
|
||||||
.join(&target);
|
|
||||||
// But! we also need PATH to contain the target directory for the host
|
// But! we also need PATH to contain the target directory for the host
|
||||||
// architecture, because it contains dlls like mspdb140.dll compiled for
|
// architecture, because it contains dlls like mspdb140.dll compiled for
|
||||||
// the host architecture.
|
// the host architecture.
|
||||||
let host_dylib_path = path
|
let host_dylib_path = host_path.join(host.to_lowercase());
|
||||||
.join("bin")
|
let lib_path = path.join("lib").join(target);
|
||||||
.join(&format!("Host{}", host))
|
let alt_lib_path = (target == "arm64ec").then(|| path.join("lib").join("arm64ec"));
|
||||||
.join(&host.to_lowercase());
|
|
||||||
let lib_path = path.join("lib").join(&target);
|
|
||||||
let include_path = path.join("include");
|
let include_path = path.join("include");
|
||||||
Some((path, bin_path, host_dylib_path, lib_path, include_path))
|
Some((
|
||||||
|
path,
|
||||||
|
bin_path,
|
||||||
|
host_dylib_path,
|
||||||
|
lib_path,
|
||||||
|
alt_lib_path,
|
||||||
|
include_path,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn atl_paths(target: &str, path: &Path) -> Option<(PathBuf, PathBuf)> {
|
fn vs15plus_vc_read_version(dir: &Path) -> Option<String> {
|
||||||
|
// Try to open the default version file.
|
||||||
|
let mut version_path: PathBuf =
|
||||||
|
dir.join(r"VC\Auxiliary\Build\Microsoft.VCToolsVersion.default.txt");
|
||||||
|
let mut version_file = if let Ok(f) = File::open(&version_path) {
|
||||||
|
f
|
||||||
|
} else {
|
||||||
|
// If the default doesn't exist, search for other version files.
|
||||||
|
// These are in the form Microsoft.VCToolsVersion.v143.default.txt
|
||||||
|
// where `143` is any three decimal digit version number.
|
||||||
|
// This sorts versions by lexical order and selects the highest version.
|
||||||
|
let mut version_file = String::new();
|
||||||
|
version_path.pop();
|
||||||
|
for file in version_path.read_dir().ok()? {
|
||||||
|
let name = file.ok()?.file_name();
|
||||||
|
let name = name.to_str()?;
|
||||||
|
if name.starts_with("Microsoft.VCToolsVersion.v")
|
||||||
|
&& name.ends_with(".default.txt")
|
||||||
|
&& name > &version_file
|
||||||
|
{
|
||||||
|
version_file.replace_range(.., name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if version_file.is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
version_path.push(version_file);
|
||||||
|
File::open(version_path).ok()?
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get the version string from the file we found.
|
||||||
|
let mut version = String::new();
|
||||||
|
version_file.read_to_string(&mut version).ok()?;
|
||||||
|
version.truncate(version.trim_end().len());
|
||||||
|
Some(version)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn atl_paths(target: TargetArch<'_>, path: &Path) -> Option<(PathBuf, PathBuf)> {
|
||||||
let atl_path = path.join("atlmfc");
|
let atl_path = path.join("atlmfc");
|
||||||
let sub = lib_subdir(target)?;
|
let sub = lib_subdir(target)?;
|
||||||
if atl_path.exists() {
|
if atl_path.exists() {
|
||||||
@ -505,14 +634,14 @@ mod impl_ {
|
|||||||
|
|
||||||
// For MSVC 14 we need to find the Universal CRT as well as either
|
// For MSVC 14 we need to find the Universal CRT as well as either
|
||||||
// the Windows 10 SDK or Windows 8.1 SDK.
|
// the Windows 10 SDK or Windows 8.1 SDK.
|
||||||
pub fn find_msvc_14(tool: &str, target: &str) -> Option<Tool> {
|
pub(super) fn find_msvc_14(tool: &str, target: TargetArch<'_>) -> Option<Tool> {
|
||||||
let vcdir = get_vc_dir("14.0")?;
|
let vcdir = get_vc_dir("14.0")?;
|
||||||
let mut tool = get_tool(tool, &vcdir, target)?;
|
let mut tool = get_tool(tool, &vcdir, target)?;
|
||||||
add_sdks(&mut tool, target)?;
|
add_sdks(&mut tool, target)?;
|
||||||
Some(tool.into_tool())
|
Some(tool.into_tool())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_sdks(tool: &mut MsvcTool, target: &str) -> Option<()> {
|
fn add_sdks(tool: &mut MsvcTool, target: TargetArch<'_>) -> Option<()> {
|
||||||
let sub = lib_subdir(target)?;
|
let sub = lib_subdir(target)?;
|
||||||
let (ucrt, ucrt_version) = get_ucrt_dir()?;
|
let (ucrt, ucrt_version) = get_ucrt_dir()?;
|
||||||
|
|
||||||
@ -555,7 +684,7 @@ mod impl_ {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// For MSVC 12 we need to find the Windows 8.1 SDK.
|
// For MSVC 12 we need to find the Windows 8.1 SDK.
|
||||||
pub fn find_msvc_12(tool: &str, target: &str) -> Option<Tool> {
|
pub(super) fn find_msvc_12(tool: &str, target: TargetArch<'_>) -> Option<Tool> {
|
||||||
let vcdir = get_vc_dir("12.0")?;
|
let vcdir = get_vc_dir("12.0")?;
|
||||||
let mut tool = get_tool(tool, &vcdir, target)?;
|
let mut tool = get_tool(tool, &vcdir, target)?;
|
||||||
let sub = lib_subdir(target)?;
|
let sub = lib_subdir(target)?;
|
||||||
@ -571,7 +700,7 @@ mod impl_ {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// For MSVC 11 we need to find the Windows 8 SDK.
|
// For MSVC 11 we need to find the Windows 8 SDK.
|
||||||
pub fn find_msvc_11(tool: &str, target: &str) -> Option<Tool> {
|
pub(super) fn find_msvc_11(tool: &str, target: TargetArch<'_>) -> Option<Tool> {
|
||||||
let vcdir = get_vc_dir("11.0")?;
|
let vcdir = get_vc_dir("11.0")?;
|
||||||
let mut tool = get_tool(tool, &vcdir, target)?;
|
let mut tool = get_tool(tool, &vcdir, target)?;
|
||||||
let sub = lib_subdir(target)?;
|
let sub = lib_subdir(target)?;
|
||||||
@ -596,7 +725,7 @@ mod impl_ {
|
|||||||
|
|
||||||
// Given a possible MSVC installation directory, we look for the linker and
|
// Given a possible MSVC installation directory, we look for the linker and
|
||||||
// then add the MSVC library path.
|
// then add the MSVC library path.
|
||||||
fn get_tool(tool: &str, path: &Path, target: &str) -> Option<MsvcTool> {
|
fn get_tool(tool: &str, path: &Path, target: TargetArch<'_>) -> Option<MsvcTool> {
|
||||||
bin_subdir(target)
|
bin_subdir(target)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(sub, host)| {
|
.map(|(sub, host)| {
|
||||||
@ -605,7 +734,7 @@ mod impl_ {
|
|||||||
path.join("bin").join(host),
|
path.join("bin").join(host),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.filter(|&(ref path, _)| path.is_file())
|
.filter(|(path, _)| path.is_file())
|
||||||
.map(|(path, host)| {
|
.map(|(path, host)| {
|
||||||
let mut tool = MsvcTool::new(path);
|
let mut tool = MsvcTool::new(path);
|
||||||
tool.path.push(host);
|
tool.path.push(host);
|
||||||
@ -734,9 +863,8 @@ mod impl_ {
|
|||||||
// linkers that can target the architecture we desire. The 64-bit host
|
// linkers that can target the architecture we desire. The 64-bit host
|
||||||
// linker is preferred, and hence first, due to 64-bit allowing it more
|
// linker is preferred, and hence first, due to 64-bit allowing it more
|
||||||
// address space to work with and potentially being faster.
|
// address space to work with and potentially being faster.
|
||||||
fn bin_subdir(target: &str) -> Vec<(&'static str, &'static str)> {
|
fn bin_subdir(target: TargetArch<'_>) -> Vec<(&'static str, &'static str)> {
|
||||||
let arch = target.split('-').next().unwrap();
|
match (target.into(), host_arch()) {
|
||||||
match (arch, host_arch()) {
|
|
||||||
("i586", X86) | ("i686", X86) => vec![("", "")],
|
("i586", X86) | ("i686", X86) => vec![("", "")],
|
||||||
("i586", X86_64) | ("i686", X86_64) => vec![("amd64_x86", "amd64"), ("", "")],
|
("i586", X86_64) | ("i686", X86_64) => vec![("amd64_x86", "amd64"), ("", "")],
|
||||||
("x86_64", X86) => vec![("x86_amd64", "")],
|
("x86_64", X86) => vec![("x86_amd64", "")],
|
||||||
@ -747,21 +875,19 @@ mod impl_ {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lib_subdir(target: &str) -> Option<&'static str> {
|
fn lib_subdir(target: TargetArch<'_>) -> Option<&'static str> {
|
||||||
let arch = target.split('-').next().unwrap();
|
match target.into() {
|
||||||
match arch {
|
|
||||||
"i586" | "i686" => Some("x86"),
|
"i586" | "i686" => Some("x86"),
|
||||||
"x86_64" => Some("x64"),
|
"x86_64" => Some("x64"),
|
||||||
"arm" | "thumbv7a" => Some("arm"),
|
"arm" | "thumbv7a" => Some("arm"),
|
||||||
"aarch64" => Some("arm64"),
|
"aarch64" | "arm64ec" => Some("arm64"),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MSVC's x86 libraries are not in a subfolder
|
// MSVC's x86 libraries are not in a subfolder
|
||||||
fn vc_lib_subdir(target: &str) -> Option<&'static str> {
|
fn vc_lib_subdir(target: TargetArch<'_>) -> Option<&'static str> {
|
||||||
let arch = target.split('-').next().unwrap();
|
match target.into() {
|
||||||
match arch {
|
|
||||||
"i586" | "i686" => Some(""),
|
"i586" | "i686" => Some(""),
|
||||||
"x86_64" => Some("amd64"),
|
"x86_64" => Some("amd64"),
|
||||||
"arm" | "thumbv7a" => Some("arm"),
|
"arm" | "thumbv7a" => Some("arm"),
|
||||||
@ -813,7 +939,7 @@ mod impl_ {
|
|||||||
for subkey in key.iter().filter_map(|k| k.ok()) {
|
for subkey in key.iter().filter_map(|k| k.ok()) {
|
||||||
let val = subkey
|
let val = subkey
|
||||||
.to_str()
|
.to_str()
|
||||||
.and_then(|s| s.trim_left_matches("v").replace(".", "").parse().ok());
|
.and_then(|s| s.trim_left_matches("v").replace('.', "").parse().ok());
|
||||||
let val = match val {
|
let val = match val {
|
||||||
Some(s) => s,
|
Some(s) => s,
|
||||||
None => continue,
|
None => continue,
|
||||||
@ -828,22 +954,22 @@ mod impl_ {
|
|||||||
max_key
|
max_key
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn has_msbuild_version(version: &str) -> bool {
|
pub(super) fn has_msbuild_version(version: &str) -> bool {
|
||||||
match version {
|
match version {
|
||||||
"17.0" => {
|
"17.0" => {
|
||||||
find_msbuild_vs17("x86_64-pc-windows-msvc").is_some()
|
find_msbuild_vs17(TargetArch("x86_64")).is_some()
|
||||||
|| find_msbuild_vs17("i686-pc-windows-msvc").is_some()
|
|| find_msbuild_vs17(TargetArch("i686")).is_some()
|
||||||
|| find_msbuild_vs17("aarch64-pc-windows-msvc").is_some()
|
|| find_msbuild_vs17(TargetArch("aarch64")).is_some()
|
||||||
}
|
}
|
||||||
"16.0" => {
|
"16.0" => {
|
||||||
find_msbuild_vs16("x86_64-pc-windows-msvc").is_some()
|
find_msbuild_vs16(TargetArch("x86_64")).is_some()
|
||||||
|| find_msbuild_vs16("i686-pc-windows-msvc").is_some()
|
|| find_msbuild_vs16(TargetArch("i686")).is_some()
|
||||||
|| find_msbuild_vs16("aarch64-pc-windows-msvc").is_some()
|
|| find_msbuild_vs16(TargetArch("aarch64")).is_some()
|
||||||
}
|
}
|
||||||
"15.0" => {
|
"15.0" => {
|
||||||
find_msbuild_vs15("x86_64-pc-windows-msvc").is_some()
|
find_msbuild_vs15(TargetArch("x86_64")).is_some()
|
||||||
|| find_msbuild_vs15("i686-pc-windows-msvc").is_some()
|
|| find_msbuild_vs15(TargetArch("i686")).is_some()
|
||||||
|| find_msbuild_vs15("aarch64-pc-windows-msvc").is_some()
|
|| find_msbuild_vs15(TargetArch("aarch64")).is_some()
|
||||||
}
|
}
|
||||||
"12.0" | "14.0" => LOCAL_MACHINE
|
"12.0" | "14.0" => LOCAL_MACHINE
|
||||||
.open(&OsString::from(format!(
|
.open(&OsString::from(format!(
|
||||||
@ -855,18 +981,20 @@ mod impl_ {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_devenv(target: &str) -> Option<Tool> {
|
pub(super) fn find_devenv(target: TargetArch<'_>) -> Option<Tool> {
|
||||||
find_devenv_vs15(&target)
|
find_devenv_vs15(target)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_devenv_vs15(target: &str) -> Option<Tool> {
|
fn find_devenv_vs15(target: TargetArch<'_>) -> Option<Tool> {
|
||||||
find_tool_in_vs15_path(r"Common7\IDE\devenv.exe", target)
|
find_tool_in_vs15_path(r"Common7\IDE\devenv.exe", target)
|
||||||
}
|
}
|
||||||
|
|
||||||
// see http://stackoverflow.com/questions/328017/path-to-msbuild
|
// see http://stackoverflow.com/questions/328017/path-to-msbuild
|
||||||
pub fn find_msbuild(target: &str) -> Option<Tool> {
|
pub(super) fn find_msbuild(target: TargetArch<'_>) -> Option<Tool> {
|
||||||
// VS 15 (2017) changed how to locate msbuild
|
// VS 15 (2017) changed how to locate msbuild
|
||||||
if let Some(r) = find_msbuild_vs16(target) {
|
if let Some(r) = find_msbuild_vs17(target) {
|
||||||
|
Some(r)
|
||||||
|
} else if let Some(r) = find_msbuild_vs16(target) {
|
||||||
return Some(r);
|
return Some(r);
|
||||||
} else if let Some(r) = find_msbuild_vs15(target) {
|
} else if let Some(r) = find_msbuild_vs15(target) {
|
||||||
return Some(r);
|
return Some(r);
|
||||||
@ -875,11 +1003,11 @@ mod impl_ {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_msbuild_vs15(target: &str) -> Option<Tool> {
|
fn find_msbuild_vs15(target: TargetArch<'_>) -> Option<Tool> {
|
||||||
find_tool_in_vs15_path(r"MSBuild\15.0\Bin\MSBuild.exe", target)
|
find_tool_in_vs15_path(r"MSBuild\15.0\Bin\MSBuild.exe", target)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_old_msbuild(target: &str) -> Option<Tool> {
|
fn find_old_msbuild(target: TargetArch<'_>) -> Option<Tool> {
|
||||||
let key = r"SOFTWARE\Microsoft\MSBuild\ToolsVersions";
|
let key = r"SOFTWARE\Microsoft\MSBuild\ToolsVersions";
|
||||||
LOCAL_MACHINE
|
LOCAL_MACHINE
|
||||||
.open(key.as_ref())
|
.open(key.as_ref())
|
||||||
@ -891,10 +1019,82 @@ mod impl_ {
|
|||||||
let mut path = PathBuf::from(path);
|
let mut path = PathBuf::from(path);
|
||||||
path.push("MSBuild.exe");
|
path.push("MSBuild.exe");
|
||||||
let mut tool = Tool::with_family(path, MSVC_FAMILY);
|
let mut tool = Tool::with_family(path, MSVC_FAMILY);
|
||||||
if target.contains("x86_64") {
|
if target == "x86_64" {
|
||||||
tool.env.push(("Platform".into(), "X64".into()));
|
tool.env.push(("Platform".into(), "X64".into()));
|
||||||
}
|
}
|
||||||
tool
|
tool
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Non-Windows Implementation.
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
mod impl_ {
|
||||||
|
use std::{env, ffi::OsString};
|
||||||
|
|
||||||
|
use super::{TargetArch, MSVC_FAMILY};
|
||||||
|
use crate::Tool;
|
||||||
|
|
||||||
|
/// Finding msbuild.exe tool under unix system is not currently supported.
|
||||||
|
/// Maybe can check it using an environment variable looks like `MSBUILD_BIN`.
|
||||||
|
pub(super) fn find_msbuild(_target: TargetArch<'_>) -> Option<Tool> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finding devenv.exe tool under unix system is not currently supported.
|
||||||
|
// Maybe can check it using an environment variable looks like `DEVENV_BIN`.
|
||||||
|
pub(super) fn find_devenv(_target: TargetArch<'_>) -> Option<Tool> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Attempt to find the tool using environment variables set by vcvars.
|
||||||
|
pub(super) fn find_msvc_environment(tool: &str, _target: TargetArch<'_>) -> Option<Tool> {
|
||||||
|
// Early return if the environment doesn't contain a VC install.
|
||||||
|
let vc_install_dir = env::var_os("VCINSTALLDIR")?;
|
||||||
|
let vs_install_dir = env::var_os("VSINSTALLDIR")?;
|
||||||
|
|
||||||
|
let get_tool = |install_dir: OsString| {
|
||||||
|
env::split_paths(&install_dir)
|
||||||
|
.map(|p| p.join(tool))
|
||||||
|
.find(|p| p.exists())
|
||||||
|
.map(|path| Tool::with_family(path.into(), MSVC_FAMILY))
|
||||||
|
};
|
||||||
|
|
||||||
|
// Take the path of tool for the vc install directory.
|
||||||
|
get_tool(vc_install_dir)
|
||||||
|
// Take the path of tool for the vs install directory.
|
||||||
|
.or_else(|| get_tool(vs_install_dir))
|
||||||
|
// Take the path of tool for the current path environment.
|
||||||
|
.or_else(|| env::var_os("PATH").and_then(|path| get_tool(path)))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn find_msvc_15plus(_tool: &str, _target: TargetArch<'_>) -> Option<Tool> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
// For MSVC 14 we need to find the Universal CRT as well as either
|
||||||
|
// the Windows 10 SDK or Windows 8.1 SDK.
|
||||||
|
pub(super) fn find_msvc_14(_tool: &str, _target: TargetArch<'_>) -> Option<Tool> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
// For MSVC 12 we need to find the Windows 8.1 SDK.
|
||||||
|
pub(super) fn find_msvc_12(_tool: &str, _target: TargetArch<'_>) -> Option<Tool> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
// For MSVC 11 we need to find the Windows 8 SDK.
|
||||||
|
pub(super) fn find_msvc_11(_tool: &str, _target: TargetArch<'_>) -> Option<Tool> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn has_msbuild_version(version: &str) -> bool {
|
||||||
|
match version {
|
||||||
|
"17.0" => false,
|
||||||
|
"16.0" => false,
|
||||||
|
"15.0" => false,
|
||||||
|
"12.0" | "14.0" => false,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
20
third_party/rust/cc/src/windows/mod.rs
vendored
Normal file
20
third_party/rust/cc/src/windows/mod.rs
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
//! These modules are all glue to support reading the MSVC version from
|
||||||
|
//! the registry and from COM interfaces.
|
||||||
|
|
||||||
|
// This is used in the crate's public API, so don't use #[cfg(windows)]
|
||||||
|
pub mod find_tools;
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
pub(crate) mod windows_sys;
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
mod registry;
|
||||||
|
#[cfg(windows)]
|
||||||
|
#[macro_use]
|
||||||
|
mod winapi;
|
||||||
|
#[cfg(windows)]
|
||||||
|
mod com;
|
||||||
|
#[cfg(windows)]
|
||||||
|
mod setup_config;
|
||||||
|
#[cfg(windows)]
|
||||||
|
mod vs_instances;
|
@ -8,63 +8,23 @@
|
|||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
use std::ffi::{OsStr, OsString};
|
use crate::windows::windows_sys::{
|
||||||
use std::io;
|
RegCloseKey, RegEnumKeyExW, RegOpenKeyExW, RegQueryValueExW, ERROR_NO_MORE_ITEMS,
|
||||||
use std::ops::RangeFrom;
|
ERROR_SUCCESS, HKEY, HKEY_LOCAL_MACHINE, KEY_READ, KEY_WOW64_32KEY, REG_SZ,
|
||||||
use std::os::raw;
|
};
|
||||||
use std::os::windows::prelude::*;
|
use std::{
|
||||||
|
ffi::{OsStr, OsString},
|
||||||
|
io,
|
||||||
|
ops::RangeFrom,
|
||||||
|
os::windows::prelude::*,
|
||||||
|
ptr::null_mut,
|
||||||
|
};
|
||||||
|
|
||||||
/// Must never be `HKEY_PERFORMANCE_DATA`.
|
/// Must never be `HKEY_PERFORMANCE_DATA`.
|
||||||
pub(crate) struct RegistryKey(Repr);
|
pub(crate) struct RegistryKey(Repr);
|
||||||
|
|
||||||
type HKEY = *mut u8;
|
#[allow(clippy::upper_case_acronyms)]
|
||||||
type DWORD = u32;
|
type DWORD = u32;
|
||||||
type LPDWORD = *mut DWORD;
|
|
||||||
type LPCWSTR = *const u16;
|
|
||||||
type LPWSTR = *mut u16;
|
|
||||||
type LONG = raw::c_long;
|
|
||||||
type PHKEY = *mut HKEY;
|
|
||||||
type PFILETIME = *mut u8;
|
|
||||||
type LPBYTE = *mut u8;
|
|
||||||
type REGSAM = u32;
|
|
||||||
|
|
||||||
const ERROR_SUCCESS: DWORD = 0;
|
|
||||||
const ERROR_NO_MORE_ITEMS: DWORD = 259;
|
|
||||||
// Sign-extend into 64 bits if needed.
|
|
||||||
const HKEY_LOCAL_MACHINE: HKEY = 0x80000002u32 as i32 as isize as HKEY;
|
|
||||||
const REG_SZ: DWORD = 1;
|
|
||||||
const KEY_READ: DWORD = 0x20019;
|
|
||||||
const KEY_WOW64_32KEY: DWORD = 0x200;
|
|
||||||
|
|
||||||
#[link(name = "advapi32")]
|
|
||||||
extern "system" {
|
|
||||||
fn RegOpenKeyExW(
|
|
||||||
key: HKEY,
|
|
||||||
lpSubKey: LPCWSTR,
|
|
||||||
ulOptions: DWORD,
|
|
||||||
samDesired: REGSAM,
|
|
||||||
phkResult: PHKEY,
|
|
||||||
) -> LONG;
|
|
||||||
fn RegEnumKeyExW(
|
|
||||||
key: HKEY,
|
|
||||||
dwIndex: DWORD,
|
|
||||||
lpName: LPWSTR,
|
|
||||||
lpcName: LPDWORD,
|
|
||||||
lpReserved: LPDWORD,
|
|
||||||
lpClass: LPWSTR,
|
|
||||||
lpcClass: LPDWORD,
|
|
||||||
lpftLastWriteTime: PFILETIME,
|
|
||||||
) -> LONG;
|
|
||||||
fn RegQueryValueExW(
|
|
||||||
hKey: HKEY,
|
|
||||||
lpValueName: LPCWSTR,
|
|
||||||
lpReserved: LPDWORD,
|
|
||||||
lpType: LPDWORD,
|
|
||||||
lpData: LPBYTE,
|
|
||||||
lpcbData: LPDWORD,
|
|
||||||
) -> LONG;
|
|
||||||
fn RegCloseKey(hKey: HKEY) -> LONG;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct OwnedKey(HKEY);
|
struct OwnedKey(HKEY);
|
||||||
|
|
||||||
@ -97,7 +57,7 @@ impl RegistryKey {
|
|||||||
/// Open a sub-key of `self`.
|
/// Open a sub-key of `self`.
|
||||||
pub fn open(&self, key: &OsStr) -> io::Result<RegistryKey> {
|
pub fn open(&self, key: &OsStr) -> io::Result<RegistryKey> {
|
||||||
let key = key.encode_wide().chain(Some(0)).collect::<Vec<_>>();
|
let key = key.encode_wide().chain(Some(0)).collect::<Vec<_>>();
|
||||||
let mut ret = 0 as *mut _;
|
let mut ret = null_mut();
|
||||||
let err = unsafe {
|
let err = unsafe {
|
||||||
RegOpenKeyExW(
|
RegOpenKeyExW(
|
||||||
self.raw(),
|
self.raw(),
|
||||||
@ -107,7 +67,7 @@ impl RegistryKey {
|
|||||||
&mut ret,
|
&mut ret,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
if err == ERROR_SUCCESS as LONG {
|
if err == ERROR_SUCCESS {
|
||||||
Ok(RegistryKey(Repr::Owned(OwnedKey(ret))))
|
Ok(RegistryKey(Repr::Owned(OwnedKey(ret))))
|
||||||
} else {
|
} else {
|
||||||
Err(io::Error::from_raw_os_error(err as i32))
|
Err(io::Error::from_raw_os_error(err as i32))
|
||||||
@ -130,12 +90,12 @@ impl RegistryKey {
|
|||||||
let err = RegQueryValueExW(
|
let err = RegQueryValueExW(
|
||||||
self.raw(),
|
self.raw(),
|
||||||
name.as_ptr(),
|
name.as_ptr(),
|
||||||
0 as *mut _,
|
null_mut(),
|
||||||
&mut kind,
|
&mut kind,
|
||||||
0 as *mut _,
|
null_mut(),
|
||||||
&mut len,
|
&mut len,
|
||||||
);
|
);
|
||||||
if err != ERROR_SUCCESS as LONG {
|
if err != ERROR_SUCCESS {
|
||||||
return Err(io::Error::from_raw_os_error(err as i32));
|
return Err(io::Error::from_raw_os_error(err as i32));
|
||||||
}
|
}
|
||||||
if kind != REG_SZ {
|
if kind != REG_SZ {
|
||||||
@ -156,8 +116,8 @@ impl RegistryKey {
|
|||||||
let err = RegQueryValueExW(
|
let err = RegQueryValueExW(
|
||||||
self.raw(),
|
self.raw(),
|
||||||
name.as_ptr(),
|
name.as_ptr(),
|
||||||
0 as *mut _,
|
null_mut(),
|
||||||
0 as *mut _,
|
null_mut(),
|
||||||
v.as_mut_ptr() as *mut _,
|
v.as_mut_ptr() as *mut _,
|
||||||
&mut len,
|
&mut len,
|
||||||
);
|
);
|
||||||
@ -165,7 +125,7 @@ impl RegistryKey {
|
|||||||
// grew between the first and second call to `RegQueryValueExW`),
|
// grew between the first and second call to `RegQueryValueExW`),
|
||||||
// both because it's extremely unlikely, and this is a bit more
|
// both because it's extremely unlikely, and this is a bit more
|
||||||
// defensive more defensive against weird types of registry keys.
|
// defensive more defensive against weird types of registry keys.
|
||||||
if err != ERROR_SUCCESS as LONG {
|
if err != ERROR_SUCCESS {
|
||||||
return Err(io::Error::from_raw_os_error(err as i32));
|
return Err(io::Error::from_raw_os_error(err as i32));
|
||||||
}
|
}
|
||||||
// The length is allowed to change, but should still be even, as
|
// The length is allowed to change, but should still be even, as
|
||||||
@ -188,7 +148,7 @@ impl RegistryKey {
|
|||||||
if !v.is_empty() && v[v.len() - 1] == 0 {
|
if !v.is_empty() && v[v.len() - 1] == 0 {
|
||||||
v.pop();
|
v.pop();
|
||||||
}
|
}
|
||||||
return Ok(OsString::from_wide(&v));
|
Ok(OsString::from_wide(&v))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -213,14 +173,14 @@ impl<'a> Iterator for Iter<'a> {
|
|||||||
i,
|
i,
|
||||||
v.as_mut_ptr(),
|
v.as_mut_ptr(),
|
||||||
&mut len,
|
&mut len,
|
||||||
0 as *mut _,
|
null_mut(),
|
||||||
0 as *mut _,
|
null_mut(),
|
||||||
0 as *mut _,
|
null_mut(),
|
||||||
0 as *mut _,
|
null_mut(),
|
||||||
);
|
);
|
||||||
if ret == ERROR_NO_MORE_ITEMS as LONG {
|
if ret == ERROR_NO_MORE_ITEMS {
|
||||||
None
|
None
|
||||||
} else if ret != ERROR_SUCCESS as LONG {
|
} else if ret != ERROR_SUCCESS {
|
||||||
Some(Err(io::Error::from_raw_os_error(ret as i32)))
|
Some(Err(io::Error::from_raw_os_error(ret as i32)))
|
||||||
} else {
|
} else {
|
||||||
v.set_len(len as usize);
|
v.set_len(len as usize);
|
@ -8,19 +8,19 @@
|
|||||||
#![allow(bad_style)]
|
#![allow(bad_style)]
|
||||||
#![allow(unused)]
|
#![allow(unused)]
|
||||||
|
|
||||||
use crate::winapi::Interface;
|
use crate::windows::{
|
||||||
use crate::winapi::BSTR;
|
com::{BStr, ComPtr},
|
||||||
use crate::winapi::LPCOLESTR;
|
winapi::{
|
||||||
use crate::winapi::LPSAFEARRAY;
|
IUnknown, IUnknownVtbl, Interface, LCID, LPCOLESTR, LPCWSTR, LPFILETIME, LPSAFEARRAY,
|
||||||
use crate::winapi::S_FALSE;
|
PULONGLONG, ULONG,
|
||||||
use crate::winapi::{CoCreateInstance, CLSCTX_ALL};
|
},
|
||||||
use crate::winapi::{IUnknown, IUnknownVtbl};
|
windows_sys::{CoCreateInstance, BSTR, CLSCTX_ALL, HRESULT, S_FALSE},
|
||||||
use crate::winapi::{HRESULT, LCID, LPCWSTR, PULONGLONG};
|
};
|
||||||
use crate::winapi::{LPFILETIME, ULONG};
|
|
||||||
use std::ffi::OsString;
|
|
||||||
use std::ptr::null_mut;
|
|
||||||
|
|
||||||
use crate::com::{BStr, ComPtr};
|
use std::{
|
||||||
|
ffi::OsString,
|
||||||
|
ptr::{null, null_mut},
|
||||||
|
};
|
||||||
|
|
||||||
// Bindings to the Setup.Configuration stuff
|
// Bindings to the Setup.Configuration stuff
|
||||||
pub type InstanceState = u32;
|
pub type InstanceState = u32;
|
||||||
@ -212,7 +212,7 @@ impl SetupInstance {
|
|||||||
SetupInstance(ComPtr::from_raw(obj))
|
SetupInstance(ComPtr::from_raw(obj))
|
||||||
}
|
}
|
||||||
pub fn instance_id(&self) -> Result<OsString, i32> {
|
pub fn instance_id(&self) -> Result<OsString, i32> {
|
||||||
let mut s = null_mut();
|
let mut s = null();
|
||||||
let err = unsafe { self.0.GetInstanceId(&mut s) };
|
let err = unsafe { self.0.GetInstanceId(&mut s) };
|
||||||
let bstr = unsafe { BStr::from_raw(s) };
|
let bstr = unsafe { BStr::from_raw(s) };
|
||||||
if err < 0 {
|
if err < 0 {
|
||||||
@ -221,7 +221,7 @@ impl SetupInstance {
|
|||||||
Ok(bstr.to_osstring())
|
Ok(bstr.to_osstring())
|
||||||
}
|
}
|
||||||
pub fn installation_name(&self) -> Result<OsString, i32> {
|
pub fn installation_name(&self) -> Result<OsString, i32> {
|
||||||
let mut s = null_mut();
|
let mut s = null();
|
||||||
let err = unsafe { self.0.GetInstallationName(&mut s) };
|
let err = unsafe { self.0.GetInstallationName(&mut s) };
|
||||||
let bstr = unsafe { BStr::from_raw(s) };
|
let bstr = unsafe { BStr::from_raw(s) };
|
||||||
if err < 0 {
|
if err < 0 {
|
||||||
@ -230,7 +230,7 @@ impl SetupInstance {
|
|||||||
Ok(bstr.to_osstring())
|
Ok(bstr.to_osstring())
|
||||||
}
|
}
|
||||||
pub fn installation_path(&self) -> Result<OsString, i32> {
|
pub fn installation_path(&self) -> Result<OsString, i32> {
|
||||||
let mut s = null_mut();
|
let mut s = null();
|
||||||
let err = unsafe { self.0.GetInstallationPath(&mut s) };
|
let err = unsafe { self.0.GetInstallationPath(&mut s) };
|
||||||
let bstr = unsafe { BStr::from_raw(s) };
|
let bstr = unsafe { BStr::from_raw(s) };
|
||||||
if err < 0 {
|
if err < 0 {
|
||||||
@ -239,7 +239,7 @@ impl SetupInstance {
|
|||||||
Ok(bstr.to_osstring())
|
Ok(bstr.to_osstring())
|
||||||
}
|
}
|
||||||
pub fn installation_version(&self) -> Result<OsString, i32> {
|
pub fn installation_version(&self) -> Result<OsString, i32> {
|
||||||
let mut s = null_mut();
|
let mut s = null();
|
||||||
let err = unsafe { self.0.GetInstallationVersion(&mut s) };
|
let err = unsafe { self.0.GetInstallationVersion(&mut s) };
|
||||||
let bstr = unsafe { BStr::from_raw(s) };
|
let bstr = unsafe { BStr::from_raw(s) };
|
||||||
if err < 0 {
|
if err < 0 {
|
||||||
@ -248,7 +248,7 @@ impl SetupInstance {
|
|||||||
Ok(bstr.to_osstring())
|
Ok(bstr.to_osstring())
|
||||||
}
|
}
|
||||||
pub fn product_path(&self) -> Result<OsString, i32> {
|
pub fn product_path(&self) -> Result<OsString, i32> {
|
||||||
let mut s = null_mut();
|
let mut s = null();
|
||||||
let this = self.0.cast::<ISetupInstance2>()?;
|
let this = self.0.cast::<ISetupInstance2>()?;
|
||||||
let err = unsafe { this.GetProductPath(&mut s) };
|
let err = unsafe { this.GetProductPath(&mut s) };
|
||||||
let bstr = unsafe { BStr::from_raw(s) };
|
let bstr = unsafe { BStr::from_raw(s) };
|
@ -4,7 +4,7 @@ use std::convert::TryFrom;
|
|||||||
use std::io::BufRead;
|
use std::io::BufRead;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use crate::setup_config::{EnumSetupInstances, SetupInstance};
|
use crate::windows::setup_config::{EnumSetupInstances, SetupInstance};
|
||||||
|
|
||||||
pub enum VsInstance {
|
pub enum VsInstance {
|
||||||
Com(SetupInstance),
|
Com(SetupInstance),
|
@ -5,26 +5,19 @@
|
|||||||
// All files in the project carrying such notice may not be copied, modified, or distributed
|
// All files in the project carrying such notice may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
#![allow(bad_style)]
|
#![allow(bad_style, clippy::upper_case_acronyms)]
|
||||||
|
|
||||||
use std::os::raw;
|
use std::os::raw;
|
||||||
|
|
||||||
pub type wchar_t = u16;
|
pub type wchar_t = u16;
|
||||||
|
|
||||||
pub type UINT = raw::c_uint;
|
pub use crate::windows::windows_sys::{FILETIME, GUID, HRESULT, SAFEARRAY};
|
||||||
pub type LPUNKNOWN = *mut IUnknown;
|
|
||||||
pub type REFIID = *const IID;
|
pub type REFIID = *const IID;
|
||||||
pub type IID = GUID;
|
pub type IID = GUID;
|
||||||
pub type REFCLSID = *const IID;
|
|
||||||
pub type PVOID = *mut raw::c_void;
|
|
||||||
pub type USHORT = raw::c_ushort;
|
|
||||||
pub type ULONG = raw::c_ulong;
|
pub type ULONG = raw::c_ulong;
|
||||||
pub type LONG = raw::c_long;
|
|
||||||
pub type DWORD = u32;
|
pub type DWORD = u32;
|
||||||
pub type LPVOID = *mut raw::c_void;
|
|
||||||
pub type HRESULT = raw::c_long;
|
|
||||||
pub type LPFILETIME = *mut FILETIME;
|
pub type LPFILETIME = *mut FILETIME;
|
||||||
pub type BSTR = *mut OLECHAR;
|
|
||||||
pub type OLECHAR = WCHAR;
|
pub type OLECHAR = WCHAR;
|
||||||
pub type WCHAR = wchar_t;
|
pub type WCHAR = wchar_t;
|
||||||
pub type LPCOLESTR = *const OLECHAR;
|
pub type LPCOLESTR = *const OLECHAR;
|
||||||
@ -33,75 +26,10 @@ pub type LPCWSTR = *const WCHAR;
|
|||||||
pub type PULONGLONG = *mut ULONGLONG;
|
pub type PULONGLONG = *mut ULONGLONG;
|
||||||
pub type ULONGLONG = u64;
|
pub type ULONGLONG = u64;
|
||||||
|
|
||||||
pub const S_OK: HRESULT = 0;
|
|
||||||
pub const S_FALSE: HRESULT = 1;
|
|
||||||
pub const COINIT_MULTITHREADED: u32 = 0x0;
|
|
||||||
|
|
||||||
pub type CLSCTX = u32;
|
|
||||||
|
|
||||||
pub const CLSCTX_INPROC_SERVER: CLSCTX = 0x1;
|
|
||||||
pub const CLSCTX_INPROC_HANDLER: CLSCTX = 0x2;
|
|
||||||
pub const CLSCTX_LOCAL_SERVER: CLSCTX = 0x4;
|
|
||||||
pub const CLSCTX_REMOTE_SERVER: CLSCTX = 0x10;
|
|
||||||
|
|
||||||
pub const CLSCTX_ALL: CLSCTX =
|
|
||||||
CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER | CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER;
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub struct GUID {
|
|
||||||
pub Data1: raw::c_ulong,
|
|
||||||
pub Data2: raw::c_ushort,
|
|
||||||
pub Data3: raw::c_ushort,
|
|
||||||
pub Data4: [raw::c_uchar; 8],
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub struct FILETIME {
|
|
||||||
pub dwLowDateTime: DWORD,
|
|
||||||
pub dwHighDateTime: DWORD,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait Interface {
|
pub trait Interface {
|
||||||
fn uuidof() -> GUID;
|
fn uuidof() -> GUID;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[link(name = "ole32")]
|
|
||||||
#[link(name = "oleaut32")]
|
|
||||||
extern "C" {}
|
|
||||||
|
|
||||||
extern "system" {
|
|
||||||
pub fn CoInitializeEx(pvReserved: LPVOID, dwCoInit: DWORD) -> HRESULT;
|
|
||||||
pub fn CoCreateInstance(
|
|
||||||
rclsid: REFCLSID,
|
|
||||||
pUnkOuter: LPUNKNOWN,
|
|
||||||
dwClsContext: DWORD,
|
|
||||||
riid: REFIID,
|
|
||||||
ppv: *mut LPVOID,
|
|
||||||
) -> HRESULT;
|
|
||||||
pub fn SysFreeString(bstrString: BSTR);
|
|
||||||
pub fn SysStringLen(pbstr: BSTR) -> UINT;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub struct SAFEARRAYBOUND {
|
|
||||||
pub cElements: ULONG,
|
|
||||||
pub lLbound: LONG,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub struct SAFEARRAY {
|
|
||||||
pub cDims: USHORT,
|
|
||||||
pub fFeatures: USHORT,
|
|
||||||
pub cbElements: ULONG,
|
|
||||||
pub cLocks: ULONG,
|
|
||||||
pub pvData: PVOID,
|
|
||||||
pub rgsabound: [SAFEARRAYBOUND; 1],
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type LPSAFEARRAY = *mut SAFEARRAY;
|
pub type LPSAFEARRAY = *mut SAFEARRAY;
|
||||||
|
|
||||||
macro_rules! DEFINE_GUID {
|
macro_rules! DEFINE_GUID {
|
||||||
@ -109,11 +37,11 @@ macro_rules! DEFINE_GUID {
|
|||||||
$name:ident, $l:expr, $w1:expr, $w2:expr,
|
$name:ident, $l:expr, $w1:expr, $w2:expr,
|
||||||
$b1:expr, $b2:expr, $b3:expr, $b4:expr, $b5:expr, $b6:expr, $b7:expr, $b8:expr
|
$b1:expr, $b2:expr, $b3:expr, $b4:expr, $b5:expr, $b6:expr, $b7:expr, $b8:expr
|
||||||
) => {
|
) => {
|
||||||
pub const $name: $crate::winapi::GUID = $crate::winapi::GUID {
|
pub const $name: $crate::windows::winapi::GUID = $crate::windows::winapi::GUID {
|
||||||
Data1: $l,
|
data1: $l,
|
||||||
Data2: $w1,
|
data2: $w1,
|
||||||
Data3: $w2,
|
data3: $w2,
|
||||||
Data4: [$b1, $b2, $b3, $b4, $b5, $b6, $b7, $b8],
|
data4: [$b1, $b2, $b3, $b4, $b5, $b6, $b7, $b8],
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -193,14 +121,14 @@ macro_rules! RIDL {
|
|||||||
$l:expr, $w1:expr, $w2:expr,
|
$l:expr, $w1:expr, $w2:expr,
|
||||||
$b1:expr, $b2:expr, $b3:expr, $b4:expr, $b5:expr, $b6:expr, $b7:expr, $b8:expr
|
$b1:expr, $b2:expr, $b3:expr, $b4:expr, $b5:expr, $b6:expr, $b7:expr, $b8:expr
|
||||||
) => (
|
) => (
|
||||||
impl $crate::winapi::Interface for $interface {
|
impl $crate::windows::winapi::Interface for $interface {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn uuidof() -> $crate::winapi::GUID {
|
fn uuidof() -> $crate::windows::winapi::GUID {
|
||||||
$crate::winapi::GUID {
|
$crate::windows::winapi::GUID {
|
||||||
Data1: $l,
|
data1: $l,
|
||||||
Data2: $w1,
|
data2: $w1,
|
||||||
Data3: $w2,
|
data3: $w2,
|
||||||
Data4: [$b1, $b2, $b3, $b4, $b5, $b6, $b7, $b8],
|
data4: [$b1, $b2, $b3, $b4, $b5, $b6, $b7, $b8],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
223
third_party/rust/cc/src/windows/windows_sys.rs
vendored
Normal file
223
third_party/rust/cc/src/windows/windows_sys.rs
vendored
Normal file
@ -0,0 +1,223 @@
|
|||||||
|
// This file is autogenerated.
|
||||||
|
//
|
||||||
|
// To add bindings, edit windows_sys.lst then run:
|
||||||
|
//
|
||||||
|
// ```
|
||||||
|
// cd generate-windows-sys/
|
||||||
|
// cargo run
|
||||||
|
// ```
|
||||||
|
// Bindings generated by `windows-bindgen` 0.53.0
|
||||||
|
|
||||||
|
#![allow(
|
||||||
|
non_snake_case,
|
||||||
|
non_upper_case_globals,
|
||||||
|
non_camel_case_types,
|
||||||
|
dead_code,
|
||||||
|
clippy::all
|
||||||
|
)]
|
||||||
|
#[link(name = "advapi32")]
|
||||||
|
extern "system" {
|
||||||
|
pub fn RegCloseKey(hkey: HKEY) -> WIN32_ERROR;
|
||||||
|
}
|
||||||
|
#[link(name = "advapi32")]
|
||||||
|
extern "system" {
|
||||||
|
pub fn RegEnumKeyExW(
|
||||||
|
hkey: HKEY,
|
||||||
|
dwindex: u32,
|
||||||
|
lpname: PWSTR,
|
||||||
|
lpcchname: *mut u32,
|
||||||
|
lpreserved: *const u32,
|
||||||
|
lpclass: PWSTR,
|
||||||
|
lpcchclass: *mut u32,
|
||||||
|
lpftlastwritetime: *mut FILETIME,
|
||||||
|
) -> WIN32_ERROR;
|
||||||
|
}
|
||||||
|
#[link(name = "advapi32")]
|
||||||
|
extern "system" {
|
||||||
|
pub fn RegOpenKeyExW(
|
||||||
|
hkey: HKEY,
|
||||||
|
lpsubkey: PCWSTR,
|
||||||
|
uloptions: u32,
|
||||||
|
samdesired: REG_SAM_FLAGS,
|
||||||
|
phkresult: *mut HKEY,
|
||||||
|
) -> WIN32_ERROR;
|
||||||
|
}
|
||||||
|
#[link(name = "advapi32")]
|
||||||
|
extern "system" {
|
||||||
|
pub fn RegQueryValueExW(
|
||||||
|
hkey: HKEY,
|
||||||
|
lpvaluename: PCWSTR,
|
||||||
|
lpreserved: *const u32,
|
||||||
|
lptype: *mut REG_VALUE_TYPE,
|
||||||
|
lpdata: *mut u8,
|
||||||
|
lpcbdata: *mut u32,
|
||||||
|
) -> WIN32_ERROR;
|
||||||
|
}
|
||||||
|
#[link(name = "kernel32")]
|
||||||
|
extern "system" {
|
||||||
|
pub fn FreeLibrary(hlibmodule: HMODULE) -> BOOL;
|
||||||
|
}
|
||||||
|
#[link(name = "kernel32")]
|
||||||
|
extern "system" {
|
||||||
|
pub fn GetMachineTypeAttributes(
|
||||||
|
machine: u16,
|
||||||
|
machinetypeattributes: *mut MACHINE_ATTRIBUTES,
|
||||||
|
) -> HRESULT;
|
||||||
|
}
|
||||||
|
#[link(name = "kernel32")]
|
||||||
|
extern "system" {
|
||||||
|
pub fn GetProcAddress(hmodule: HMODULE, lpprocname: PCSTR) -> FARPROC;
|
||||||
|
}
|
||||||
|
#[link(name = "kernel32")]
|
||||||
|
extern "system" {
|
||||||
|
pub fn LoadLibraryA(lplibfilename: PCSTR) -> HMODULE;
|
||||||
|
}
|
||||||
|
#[link(name = "kernel32")]
|
||||||
|
extern "system" {
|
||||||
|
pub fn OpenSemaphoreA(dwdesiredaccess: u32, binherithandle: BOOL, lpname: PCSTR) -> HANDLE;
|
||||||
|
}
|
||||||
|
#[link(name = "kernel32")]
|
||||||
|
extern "system" {
|
||||||
|
pub fn PeekNamedPipe(
|
||||||
|
hnamedpipe: HANDLE,
|
||||||
|
lpbuffer: *mut ::core::ffi::c_void,
|
||||||
|
nbuffersize: u32,
|
||||||
|
lpbytesread: *mut u32,
|
||||||
|
lptotalbytesavail: *mut u32,
|
||||||
|
lpbytesleftthismessage: *mut u32,
|
||||||
|
) -> BOOL;
|
||||||
|
}
|
||||||
|
#[link(name = "kernel32")]
|
||||||
|
extern "system" {
|
||||||
|
pub fn ReleaseSemaphore(
|
||||||
|
hsemaphore: HANDLE,
|
||||||
|
lreleasecount: i32,
|
||||||
|
lppreviouscount: *mut i32,
|
||||||
|
) -> BOOL;
|
||||||
|
}
|
||||||
|
#[link(name = "kernel32")]
|
||||||
|
extern "system" {
|
||||||
|
pub fn WaitForSingleObject(hhandle: HANDLE, dwmilliseconds: u32) -> WAIT_EVENT;
|
||||||
|
}
|
||||||
|
#[link(name = "ole32")]
|
||||||
|
extern "system" {
|
||||||
|
pub fn CoCreateInstance(
|
||||||
|
rclsid: *const GUID,
|
||||||
|
punkouter: *mut ::core::ffi::c_void,
|
||||||
|
dwclscontext: CLSCTX,
|
||||||
|
riid: *const GUID,
|
||||||
|
ppv: *mut *mut ::core::ffi::c_void,
|
||||||
|
) -> HRESULT;
|
||||||
|
}
|
||||||
|
#[link(name = "ole32")]
|
||||||
|
extern "system" {
|
||||||
|
pub fn CoInitializeEx(pvreserved: *const ::core::ffi::c_void, dwcoinit: u32) -> HRESULT;
|
||||||
|
}
|
||||||
|
#[link(name = "oleaut32")]
|
||||||
|
extern "system" {
|
||||||
|
pub fn SysFreeString(bstrstring: BSTR);
|
||||||
|
}
|
||||||
|
#[link(name = "oleaut32")]
|
||||||
|
extern "system" {
|
||||||
|
pub fn SysStringLen(pbstr: BSTR) -> u32;
|
||||||
|
}
|
||||||
|
pub type ADVANCED_FEATURE_FLAGS = u16;
|
||||||
|
pub type BOOL = i32;
|
||||||
|
pub type BSTR = *const u16;
|
||||||
|
pub type CLSCTX = u32;
|
||||||
|
pub const CLSCTX_ALL: CLSCTX = 23u32;
|
||||||
|
pub type COINIT = i32;
|
||||||
|
pub const COINIT_MULTITHREADED: COINIT = 0i32;
|
||||||
|
pub const ERROR_NO_MORE_ITEMS: WIN32_ERROR = 259u32;
|
||||||
|
pub const ERROR_SUCCESS: WIN32_ERROR = 0u32;
|
||||||
|
pub const FALSE: BOOL = 0i32;
|
||||||
|
pub type FARPROC = ::core::option::Option<unsafe extern "system" fn() -> isize>;
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct FILETIME {
|
||||||
|
pub dwLowDateTime: u32,
|
||||||
|
pub dwHighDateTime: u32,
|
||||||
|
}
|
||||||
|
impl ::core::marker::Copy for FILETIME {}
|
||||||
|
impl ::core::clone::Clone for FILETIME {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
*self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct GUID {
|
||||||
|
pub data1: u32,
|
||||||
|
pub data2: u16,
|
||||||
|
pub data3: u16,
|
||||||
|
pub data4: [u8; 8],
|
||||||
|
}
|
||||||
|
impl ::core::marker::Copy for GUID {}
|
||||||
|
impl ::core::clone::Clone for GUID {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
*self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl GUID {
|
||||||
|
pub const fn from_u128(uuid: u128) -> Self {
|
||||||
|
Self {
|
||||||
|
data1: (uuid >> 96) as u32,
|
||||||
|
data2: (uuid >> 80 & 0xffff) as u16,
|
||||||
|
data3: (uuid >> 64 & 0xffff) as u16,
|
||||||
|
data4: (uuid as u64).to_be_bytes(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub type HANDLE = *mut ::core::ffi::c_void;
|
||||||
|
pub type HKEY = *mut ::core::ffi::c_void;
|
||||||
|
pub const HKEY_LOCAL_MACHINE: HKEY = -2147483646i32 as _;
|
||||||
|
pub type HMODULE = *mut ::core::ffi::c_void;
|
||||||
|
pub type HRESULT = i32;
|
||||||
|
pub type IMAGE_FILE_MACHINE = u16;
|
||||||
|
pub const IMAGE_FILE_MACHINE_AMD64: IMAGE_FILE_MACHINE = 34404u16;
|
||||||
|
pub const KEY_READ: REG_SAM_FLAGS = 131097u32;
|
||||||
|
pub const KEY_WOW64_32KEY: REG_SAM_FLAGS = 512u32;
|
||||||
|
pub type MACHINE_ATTRIBUTES = i32;
|
||||||
|
pub type PCSTR = *const u8;
|
||||||
|
pub type PCWSTR = *const u16;
|
||||||
|
pub type PWSTR = *mut u16;
|
||||||
|
pub type REG_SAM_FLAGS = u32;
|
||||||
|
pub const REG_SZ: REG_VALUE_TYPE = 1u32;
|
||||||
|
pub type REG_VALUE_TYPE = u32;
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct SAFEARRAY {
|
||||||
|
pub cDims: u16,
|
||||||
|
pub fFeatures: ADVANCED_FEATURE_FLAGS,
|
||||||
|
pub cbElements: u32,
|
||||||
|
pub cLocks: u32,
|
||||||
|
pub pvData: *mut ::core::ffi::c_void,
|
||||||
|
pub rgsabound: [SAFEARRAYBOUND; 1],
|
||||||
|
}
|
||||||
|
impl ::core::marker::Copy for SAFEARRAY {}
|
||||||
|
impl ::core::clone::Clone for SAFEARRAY {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
*self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct SAFEARRAYBOUND {
|
||||||
|
pub cElements: u32,
|
||||||
|
pub lLbound: i32,
|
||||||
|
}
|
||||||
|
impl ::core::marker::Copy for SAFEARRAYBOUND {}
|
||||||
|
impl ::core::clone::Clone for SAFEARRAYBOUND {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
*self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub const SEMAPHORE_MODIFY_STATE: SYNCHRONIZATION_ACCESS_RIGHTS = 2u32;
|
||||||
|
pub type SYNCHRONIZATION_ACCESS_RIGHTS = u32;
|
||||||
|
pub const S_FALSE: HRESULT = 0x1_u32 as _;
|
||||||
|
pub const S_OK: HRESULT = 0x0_u32 as _;
|
||||||
|
pub type THREAD_ACCESS_RIGHTS = u32;
|
||||||
|
pub const THREAD_SYNCHRONIZE: THREAD_ACCESS_RIGHTS = 1048576u32;
|
||||||
|
pub const UserEnabled: MACHINE_ATTRIBUTES = 1i32;
|
||||||
|
pub const WAIT_ABANDONED: WAIT_EVENT = 128u32;
|
||||||
|
pub type WAIT_EVENT = u32;
|
||||||
|
pub const WAIT_FAILED: WAIT_EVENT = 4294967295u32;
|
||||||
|
pub const WAIT_OBJECT_0: WAIT_EVENT = 0u32;
|
||||||
|
pub const WAIT_TIMEOUT: WAIT_EVENT = 258u32;
|
||||||
|
pub type WIN32_ERROR = u32;
|
118
third_party/rust/cc/tests/cc_env.rs
vendored
118
third_party/rust/cc/tests/cc_env.rs
vendored
@ -1,118 +0,0 @@
|
|||||||
use std::env;
|
|
||||||
use std::ffi::OsString;
|
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
mod support;
|
|
||||||
use crate::support::Test;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn main() {
|
|
||||||
ccache();
|
|
||||||
distcc();
|
|
||||||
ccache_spaces();
|
|
||||||
ccache_env_flags();
|
|
||||||
leading_spaces();
|
|
||||||
extra_flags();
|
|
||||||
path_to_ccache();
|
|
||||||
more_spaces();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ccache() {
|
|
||||||
let test = Test::gnu();
|
|
||||||
|
|
||||||
env::set_var("CC", "ccache cc");
|
|
||||||
let compiler = test.gcc().file("foo.c").get_compiler();
|
|
||||||
|
|
||||||
assert_eq!(compiler.path(), Path::new("cc"));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ccache_spaces() {
|
|
||||||
let test = Test::gnu();
|
|
||||||
test.shim("ccache");
|
|
||||||
|
|
||||||
env::set_var("CC", "ccache cc");
|
|
||||||
let compiler = test.gcc().file("foo.c").get_compiler();
|
|
||||||
assert_eq!(compiler.path(), Path::new("cc"));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn distcc() {
|
|
||||||
let test = Test::gnu();
|
|
||||||
test.shim("distcc");
|
|
||||||
|
|
||||||
env::set_var("CC", "distcc cc");
|
|
||||||
let compiler = test.gcc().file("foo.c").get_compiler();
|
|
||||||
assert_eq!(compiler.path(), Path::new("cc"));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ccache_env_flags() {
|
|
||||||
let test = Test::gnu();
|
|
||||||
test.shim("ccache");
|
|
||||||
|
|
||||||
env::set_var("CC", "ccache lol-this-is-not-a-compiler");
|
|
||||||
let compiler = test.gcc().file("foo.c").get_compiler();
|
|
||||||
assert_eq!(compiler.path(), Path::new("lol-this-is-not-a-compiler"));
|
|
||||||
assert_eq!(
|
|
||||||
compiler.cc_env(),
|
|
||||||
OsString::from("ccache lol-this-is-not-a-compiler")
|
|
||||||
);
|
|
||||||
assert!(
|
|
||||||
compiler
|
|
||||||
.cflags_env()
|
|
||||||
.into_string()
|
|
||||||
.unwrap()
|
|
||||||
.contains("ccache")
|
|
||||||
== false
|
|
||||||
);
|
|
||||||
assert!(
|
|
||||||
compiler
|
|
||||||
.cflags_env()
|
|
||||||
.into_string()
|
|
||||||
.unwrap()
|
|
||||||
.contains(" lol-this-is-not-a-compiler")
|
|
||||||
== false
|
|
||||||
);
|
|
||||||
|
|
||||||
env::set_var("CC", "");
|
|
||||||
}
|
|
||||||
|
|
||||||
fn leading_spaces() {
|
|
||||||
let test = Test::gnu();
|
|
||||||
test.shim("ccache");
|
|
||||||
|
|
||||||
env::set_var("CC", " test ");
|
|
||||||
let compiler = test.gcc().file("foo.c").get_compiler();
|
|
||||||
assert_eq!(compiler.path(), Path::new("test"));
|
|
||||||
|
|
||||||
env::set_var("CC", "");
|
|
||||||
}
|
|
||||||
|
|
||||||
fn extra_flags() {
|
|
||||||
let test = Test::gnu();
|
|
||||||
test.shim("ccache");
|
|
||||||
|
|
||||||
env::set_var("CC", "ccache cc -m32");
|
|
||||||
let compiler = test.gcc().file("foo.c").get_compiler();
|
|
||||||
assert_eq!(compiler.path(), Path::new("cc"));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn path_to_ccache() {
|
|
||||||
let test = Test::gnu();
|
|
||||||
test.shim("ccache");
|
|
||||||
|
|
||||||
env::set_var("CC", "/path/to/ccache.exe cc -m32");
|
|
||||||
let compiler = test.gcc().file("foo.c").get_compiler();
|
|
||||||
assert_eq!(compiler.path(), Path::new("cc"));
|
|
||||||
assert_eq!(
|
|
||||||
compiler.cc_env(),
|
|
||||||
OsString::from("/path/to/ccache.exe cc -m32"),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn more_spaces() {
|
|
||||||
let test = Test::gnu();
|
|
||||||
test.shim("ccache");
|
|
||||||
|
|
||||||
env::set_var("CC", "cc -m32");
|
|
||||||
let compiler = test.gcc().file("foo.c").get_compiler();
|
|
||||||
assert_eq!(compiler.path(), Path::new("cc"));
|
|
||||||
}
|
|
15
third_party/rust/cc/tests/cflags.rs
vendored
15
third_party/rust/cc/tests/cflags.rs
vendored
@ -1,15 +0,0 @@
|
|||||||
mod support;
|
|
||||||
|
|
||||||
use crate::support::Test;
|
|
||||||
use std::env;
|
|
||||||
|
|
||||||
/// This test is in its own module because it modifies the environment and would affect other tests
|
|
||||||
/// when run in parallel with them.
|
|
||||||
#[test]
|
|
||||||
fn gnu_no_warnings_if_cflags() {
|
|
||||||
env::set_var("CFLAGS", "-arbitrary");
|
|
||||||
let test = Test::gnu();
|
|
||||||
test.gcc().file("foo.c").compile("foo");
|
|
||||||
|
|
||||||
test.cmd(0).must_not_have("-Wall").must_not_have("-Wextra");
|
|
||||||
}
|
|
15
third_party/rust/cc/tests/cxxflags.rs
vendored
15
third_party/rust/cc/tests/cxxflags.rs
vendored
@ -1,15 +0,0 @@
|
|||||||
mod support;
|
|
||||||
|
|
||||||
use crate::support::Test;
|
|
||||||
use std::env;
|
|
||||||
|
|
||||||
/// This test is in its own module because it modifies the environment and would affect other tests
|
|
||||||
/// when run in parallel with them.
|
|
||||||
#[test]
|
|
||||||
fn gnu_no_warnings_if_cxxflags() {
|
|
||||||
env::set_var("CXXFLAGS", "-arbitrary");
|
|
||||||
let test = Test::gnu();
|
|
||||||
test.gcc().file("foo.cpp").cpp(true).compile("foo");
|
|
||||||
|
|
||||||
test.cmd(0).must_not_have("-Wall").must_not_have("-Wextra");
|
|
||||||
}
|
|
172
third_party/rust/cc/tests/support/mod.rs
vendored
172
third_party/rust/cc/tests/support/mod.rs
vendored
@ -1,172 +0,0 @@
|
|||||||
#![allow(dead_code)]
|
|
||||||
|
|
||||||
use std::env;
|
|
||||||
use std::ffi::{OsStr, OsString};
|
|
||||||
use std::fs::{self, File};
|
|
||||||
use std::io;
|
|
||||||
use std::io::prelude::*;
|
|
||||||
use std::path::{Path, PathBuf};
|
|
||||||
|
|
||||||
use cc;
|
|
||||||
use tempfile::{Builder, TempDir};
|
|
||||||
|
|
||||||
pub struct Test {
|
|
||||||
pub td: TempDir,
|
|
||||||
pub gcc: PathBuf,
|
|
||||||
pub msvc: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Execution {
|
|
||||||
args: Vec<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Test {
|
|
||||||
pub fn new() -> Test {
|
|
||||||
// This is ugly: `sccache` needs to introspect the compiler it is
|
|
||||||
// executing, as it adjusts its behavior depending on the
|
|
||||||
// language/compiler. This crate's test driver uses mock compilers that
|
|
||||||
// are obviously not supported by sccache, so the tests fail if
|
|
||||||
// RUSTC_WRAPPER is set. rust doesn't build test dependencies with
|
|
||||||
// the `test` feature enabled, so we can't conditionally disable the
|
|
||||||
// usage of `sccache` if running in a test environment, at least not
|
|
||||||
// without setting an environment variable here and testing for it
|
|
||||||
// there. Explicitly deasserting RUSTC_WRAPPER here seems to be the
|
|
||||||
// lesser of the two evils.
|
|
||||||
env::remove_var("RUSTC_WRAPPER");
|
|
||||||
|
|
||||||
let mut gcc = PathBuf::from(env::current_exe().unwrap());
|
|
||||||
gcc.pop();
|
|
||||||
if gcc.ends_with("deps") {
|
|
||||||
gcc.pop();
|
|
||||||
}
|
|
||||||
let td = Builder::new().prefix("gcc-test").tempdir_in(&gcc).unwrap();
|
|
||||||
gcc.push(format!("gcc-shim{}", env::consts::EXE_SUFFIX));
|
|
||||||
Test {
|
|
||||||
td: td,
|
|
||||||
gcc: gcc,
|
|
||||||
msvc: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn gnu() -> Test {
|
|
||||||
let t = Test::new();
|
|
||||||
t.shim("cc").shim("c++").shim("ar");
|
|
||||||
t
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn msvc() -> Test {
|
|
||||||
let mut t = Test::new();
|
|
||||||
t.shim("cl").shim("lib.exe");
|
|
||||||
t.msvc = true;
|
|
||||||
t
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn shim(&self, name: &str) -> &Test {
|
|
||||||
let name = if name.ends_with(env::consts::EXE_SUFFIX) {
|
|
||||||
name.to_string()
|
|
||||||
} else {
|
|
||||||
format!("{}{}", name, env::consts::EXE_SUFFIX)
|
|
||||||
};
|
|
||||||
link_or_copy(&self.gcc, self.td.path().join(name)).unwrap();
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn gcc(&self) -> cc::Build {
|
|
||||||
let mut cfg = cc::Build::new();
|
|
||||||
let target = if self.msvc {
|
|
||||||
"x86_64-pc-windows-msvc"
|
|
||||||
} else {
|
|
||||||
"x86_64-unknown-linux-gnu"
|
|
||||||
};
|
|
||||||
|
|
||||||
cfg.target(target)
|
|
||||||
.host(target)
|
|
||||||
.opt_level(2)
|
|
||||||
.debug(false)
|
|
||||||
.out_dir(self.td.path())
|
|
||||||
.__set_env("PATH", self.path())
|
|
||||||
.__set_env("GCCTEST_OUT_DIR", self.td.path());
|
|
||||||
if self.msvc {
|
|
||||||
cfg.compiler(self.td.path().join("cl"));
|
|
||||||
cfg.archiver(self.td.path().join("lib.exe"));
|
|
||||||
}
|
|
||||||
cfg
|
|
||||||
}
|
|
||||||
|
|
||||||
fn path(&self) -> OsString {
|
|
||||||
let mut path = env::split_paths(&env::var_os("PATH").unwrap()).collect::<Vec<_>>();
|
|
||||||
path.insert(0, self.td.path().to_owned());
|
|
||||||
env::join_paths(path).unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn cmd(&self, i: u32) -> Execution {
|
|
||||||
let mut s = String::new();
|
|
||||||
File::open(self.td.path().join(format!("out{}", i)))
|
|
||||||
.unwrap()
|
|
||||||
.read_to_string(&mut s)
|
|
||||||
.unwrap();
|
|
||||||
Execution {
|
|
||||||
args: s.lines().map(|s| s.to_string()).collect(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Execution {
|
|
||||||
pub fn must_have<P: AsRef<OsStr>>(&self, p: P) -> &Execution {
|
|
||||||
if !self.has(p.as_ref()) {
|
|
||||||
panic!("didn't find {:?} in {:?}", p.as_ref(), self.args);
|
|
||||||
} else {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn must_not_have<P: AsRef<OsStr>>(&self, p: P) -> &Execution {
|
|
||||||
if self.has(p.as_ref()) {
|
|
||||||
panic!("found {:?}", p.as_ref());
|
|
||||||
} else {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn has(&self, p: &OsStr) -> bool {
|
|
||||||
self.args.iter().any(|arg| OsStr::new(arg) == p)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn must_have_in_order(&self, before: &str, after: &str) -> &Execution {
|
|
||||||
let before_position = self
|
|
||||||
.args
|
|
||||||
.iter()
|
|
||||||
.rposition(|x| OsStr::new(x) == OsStr::new(before));
|
|
||||||
let after_position = self
|
|
||||||
.args
|
|
||||||
.iter()
|
|
||||||
.rposition(|x| OsStr::new(x) == OsStr::new(after));
|
|
||||||
match (before_position, after_position) {
|
|
||||||
(Some(b), Some(a)) if b < a => {}
|
|
||||||
(b, a) => panic!(
|
|
||||||
"{:?} (last position: {:?}) did not appear before {:?} (last position: {:?})",
|
|
||||||
before, b, after, a
|
|
||||||
),
|
|
||||||
};
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Hard link an executable or copy it if that fails.
|
|
||||||
///
|
|
||||||
/// We first try to hard link an executable to save space. If that fails (as on Windows with
|
|
||||||
/// different mount points, issue #60), we copy.
|
|
||||||
#[cfg(not(target_os = "macos"))]
|
|
||||||
fn link_or_copy<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<()> {
|
|
||||||
let from = from.as_ref();
|
|
||||||
let to = to.as_ref();
|
|
||||||
fs::hard_link(from, to).or_else(|_| fs::copy(from, to).map(|_| ()))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Copy an executable.
|
|
||||||
///
|
|
||||||
/// On macOS, hard linking the executable leads to strange failures (issue #419), so we just copy.
|
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
fn link_or_copy<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<()> {
|
|
||||||
fs::copy(from, to).map(|_| ())
|
|
||||||
}
|
|
461
third_party/rust/cc/tests/test.rs
vendored
461
third_party/rust/cc/tests/test.rs
vendored
@ -1,461 +0,0 @@
|
|||||||
use crate::support::Test;
|
|
||||||
|
|
||||||
mod support;
|
|
||||||
|
|
||||||
// Some tests check that a flag is *not* present. These tests might fail if the flag is set in the
|
|
||||||
// CFLAGS or CXXFLAGS environment variables. This function clears the CFLAGS and CXXFLAGS
|
|
||||||
// variables to make sure that the tests can run correctly.
|
|
||||||
fn reset_env() {
|
|
||||||
std::env::set_var("CFLAGS", "");
|
|
||||||
std::env::set_var("CXXFLAGS", "");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn gnu_smoke() {
|
|
||||||
reset_env();
|
|
||||||
|
|
||||||
let test = Test::gnu();
|
|
||||||
test.gcc().file("foo.c").compile("foo");
|
|
||||||
|
|
||||||
test.cmd(0)
|
|
||||||
.must_have("-O2")
|
|
||||||
.must_have("foo.c")
|
|
||||||
.must_not_have("-gdwarf-4")
|
|
||||||
.must_have("-c")
|
|
||||||
.must_have("-ffunction-sections")
|
|
||||||
.must_have("-fdata-sections");
|
|
||||||
test.cmd(1).must_have(test.td.path().join("foo.o"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn gnu_opt_level_1() {
|
|
||||||
reset_env();
|
|
||||||
|
|
||||||
let test = Test::gnu();
|
|
||||||
test.gcc().opt_level(1).file("foo.c").compile("foo");
|
|
||||||
|
|
||||||
test.cmd(0).must_have("-O1").must_not_have("-O2");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn gnu_opt_level_s() {
|
|
||||||
reset_env();
|
|
||||||
|
|
||||||
let test = Test::gnu();
|
|
||||||
test.gcc().opt_level_str("s").file("foo.c").compile("foo");
|
|
||||||
|
|
||||||
test.cmd(0)
|
|
||||||
.must_have("-Os")
|
|
||||||
.must_not_have("-O1")
|
|
||||||
.must_not_have("-O2")
|
|
||||||
.must_not_have("-O3")
|
|
||||||
.must_not_have("-Oz");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn gnu_debug() {
|
|
||||||
let test = Test::gnu();
|
|
||||||
test.gcc().debug(true).file("foo.c").compile("foo");
|
|
||||||
test.cmd(0).must_have("-gdwarf-4");
|
|
||||||
|
|
||||||
let test = Test::gnu();
|
|
||||||
test.gcc()
|
|
||||||
.target("x86_64-apple-darwin")
|
|
||||||
.debug(true)
|
|
||||||
.file("foo.c")
|
|
||||||
.compile("foo");
|
|
||||||
test.cmd(0).must_have("-gdwarf-2");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn gnu_debug_fp_auto() {
|
|
||||||
let test = Test::gnu();
|
|
||||||
test.gcc().debug(true).file("foo.c").compile("foo");
|
|
||||||
test.cmd(0).must_have("-gdwarf-4");
|
|
||||||
test.cmd(0).must_have("-fno-omit-frame-pointer");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn gnu_debug_fp() {
|
|
||||||
let test = Test::gnu();
|
|
||||||
test.gcc().debug(true).file("foo.c").compile("foo");
|
|
||||||
test.cmd(0).must_have("-gdwarf-4");
|
|
||||||
test.cmd(0).must_have("-fno-omit-frame-pointer");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn gnu_debug_nofp() {
|
|
||||||
reset_env();
|
|
||||||
|
|
||||||
let test = Test::gnu();
|
|
||||||
test.gcc()
|
|
||||||
.debug(true)
|
|
||||||
.force_frame_pointer(false)
|
|
||||||
.file("foo.c")
|
|
||||||
.compile("foo");
|
|
||||||
test.cmd(0).must_have("-gdwarf-4");
|
|
||||||
test.cmd(0).must_not_have("-fno-omit-frame-pointer");
|
|
||||||
|
|
||||||
let test = Test::gnu();
|
|
||||||
test.gcc()
|
|
||||||
.force_frame_pointer(false)
|
|
||||||
.debug(true)
|
|
||||||
.file("foo.c")
|
|
||||||
.compile("foo");
|
|
||||||
test.cmd(0).must_have("-gdwarf-4");
|
|
||||||
test.cmd(0).must_not_have("-fno-omit-frame-pointer");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn gnu_warnings_into_errors() {
|
|
||||||
let test = Test::gnu();
|
|
||||||
test.gcc()
|
|
||||||
.warnings_into_errors(true)
|
|
||||||
.file("foo.c")
|
|
||||||
.compile("foo");
|
|
||||||
|
|
||||||
test.cmd(0).must_have("-Werror");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn gnu_warnings() {
|
|
||||||
let test = Test::gnu();
|
|
||||||
test.gcc()
|
|
||||||
.warnings(true)
|
|
||||||
.flag("-Wno-missing-field-initializers")
|
|
||||||
.file("foo.c")
|
|
||||||
.compile("foo");
|
|
||||||
|
|
||||||
test.cmd(0).must_have("-Wall").must_have("-Wextra");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn gnu_extra_warnings0() {
|
|
||||||
reset_env();
|
|
||||||
|
|
||||||
let test = Test::gnu();
|
|
||||||
test.gcc()
|
|
||||||
.warnings(true)
|
|
||||||
.extra_warnings(false)
|
|
||||||
.flag("-Wno-missing-field-initializers")
|
|
||||||
.file("foo.c")
|
|
||||||
.compile("foo");
|
|
||||||
|
|
||||||
test.cmd(0).must_have("-Wall").must_not_have("-Wextra");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn gnu_extra_warnings1() {
|
|
||||||
reset_env();
|
|
||||||
|
|
||||||
let test = Test::gnu();
|
|
||||||
test.gcc()
|
|
||||||
.warnings(false)
|
|
||||||
.extra_warnings(true)
|
|
||||||
.flag("-Wno-missing-field-initializers")
|
|
||||||
.file("foo.c")
|
|
||||||
.compile("foo");
|
|
||||||
|
|
||||||
test.cmd(0).must_not_have("-Wall").must_have("-Wextra");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn gnu_warnings_overridable() {
|
|
||||||
reset_env();
|
|
||||||
|
|
||||||
let test = Test::gnu();
|
|
||||||
test.gcc()
|
|
||||||
.warnings(true)
|
|
||||||
.flag("-Wno-missing-field-initializers")
|
|
||||||
.file("foo.c")
|
|
||||||
.compile("foo");
|
|
||||||
|
|
||||||
test.cmd(0)
|
|
||||||
.must_have_in_order("-Wall", "-Wno-missing-field-initializers");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn gnu_x86_64() {
|
|
||||||
for vendor in &["unknown-linux-gnu", "apple-darwin"] {
|
|
||||||
let target = format!("x86_64-{}", vendor);
|
|
||||||
let test = Test::gnu();
|
|
||||||
test.gcc()
|
|
||||||
.target(&target)
|
|
||||||
.host(&target)
|
|
||||||
.file("foo.c")
|
|
||||||
.compile("foo");
|
|
||||||
|
|
||||||
test.cmd(0).must_have("-fPIC").must_have("-m64");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn gnu_x86_64_no_pic() {
|
|
||||||
reset_env();
|
|
||||||
|
|
||||||
for vendor in &["unknown-linux-gnu", "apple-darwin"] {
|
|
||||||
let target = format!("x86_64-{}", vendor);
|
|
||||||
let test = Test::gnu();
|
|
||||||
test.gcc()
|
|
||||||
.pic(false)
|
|
||||||
.target(&target)
|
|
||||||
.host(&target)
|
|
||||||
.file("foo.c")
|
|
||||||
.compile("foo");
|
|
||||||
|
|
||||||
test.cmd(0).must_not_have("-fPIC");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn gnu_i686() {
|
|
||||||
for vendor in &["unknown-linux-gnu", "apple-darwin"] {
|
|
||||||
let target = format!("i686-{}", vendor);
|
|
||||||
let test = Test::gnu();
|
|
||||||
test.gcc()
|
|
||||||
.target(&target)
|
|
||||||
.host(&target)
|
|
||||||
.file("foo.c")
|
|
||||||
.compile("foo");
|
|
||||||
|
|
||||||
test.cmd(0).must_have("-m32");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn gnu_i686_pic() {
|
|
||||||
for vendor in &["unknown-linux-gnu", "apple-darwin"] {
|
|
||||||
let target = format!("i686-{}", vendor);
|
|
||||||
let test = Test::gnu();
|
|
||||||
test.gcc()
|
|
||||||
.pic(true)
|
|
||||||
.target(&target)
|
|
||||||
.host(&target)
|
|
||||||
.file("foo.c")
|
|
||||||
.compile("foo");
|
|
||||||
|
|
||||||
test.cmd(0).must_have("-fPIC");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn gnu_x86_64_no_plt() {
|
|
||||||
let target = "x86_64-unknown-linux-gnu";
|
|
||||||
let test = Test::gnu();
|
|
||||||
test.gcc()
|
|
||||||
.pic(true)
|
|
||||||
.use_plt(false)
|
|
||||||
.target(&target)
|
|
||||||
.host(&target)
|
|
||||||
.file("foo.c")
|
|
||||||
.compile("foo");
|
|
||||||
test.cmd(0).must_have("-fno-plt");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn gnu_set_stdlib() {
|
|
||||||
reset_env();
|
|
||||||
|
|
||||||
let test = Test::gnu();
|
|
||||||
test.gcc()
|
|
||||||
.cpp_set_stdlib(Some("foo"))
|
|
||||||
.file("foo.c")
|
|
||||||
.compile("foo");
|
|
||||||
|
|
||||||
test.cmd(0).must_not_have("-stdlib=foo");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn gnu_include() {
|
|
||||||
let test = Test::gnu();
|
|
||||||
test.gcc().include("foo/bar").file("foo.c").compile("foo");
|
|
||||||
|
|
||||||
test.cmd(0).must_have("-I").must_have("foo/bar");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn gnu_define() {
|
|
||||||
let test = Test::gnu();
|
|
||||||
test.gcc()
|
|
||||||
.define("FOO", "bar")
|
|
||||||
.define("BAR", None)
|
|
||||||
.file("foo.c")
|
|
||||||
.compile("foo");
|
|
||||||
|
|
||||||
test.cmd(0).must_have("-DFOO=bar").must_have("-DBAR");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn gnu_compile_assembly() {
|
|
||||||
let test = Test::gnu();
|
|
||||||
test.gcc().file("foo.S").compile("foo");
|
|
||||||
test.cmd(0).must_have("foo.S");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn gnu_shared() {
|
|
||||||
reset_env();
|
|
||||||
|
|
||||||
let test = Test::gnu();
|
|
||||||
test.gcc()
|
|
||||||
.file("foo.c")
|
|
||||||
.shared_flag(true)
|
|
||||||
.static_flag(false)
|
|
||||||
.compile("foo");
|
|
||||||
|
|
||||||
test.cmd(0).must_have("-shared").must_not_have("-static");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn gnu_flag_if_supported() {
|
|
||||||
reset_env();
|
|
||||||
|
|
||||||
if cfg!(windows) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let test = Test::gnu();
|
|
||||||
test.gcc()
|
|
||||||
.file("foo.c")
|
|
||||||
.flag("-v")
|
|
||||||
.flag_if_supported("-Wall")
|
|
||||||
.flag_if_supported("-Wflag-does-not-exist")
|
|
||||||
.flag_if_supported("-std=c++11")
|
|
||||||
.compile("foo");
|
|
||||||
|
|
||||||
test.cmd(0)
|
|
||||||
.must_have("-v")
|
|
||||||
.must_have("-Wall")
|
|
||||||
.must_not_have("-Wflag-does-not-exist")
|
|
||||||
.must_not_have("-std=c++11");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn gnu_flag_if_supported_cpp() {
|
|
||||||
if cfg!(windows) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let test = Test::gnu();
|
|
||||||
test.gcc()
|
|
||||||
.cpp(true)
|
|
||||||
.file("foo.cpp")
|
|
||||||
.flag_if_supported("-std=c++11")
|
|
||||||
.compile("foo");
|
|
||||||
|
|
||||||
test.cmd(0).must_have("-std=c++11");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn gnu_static() {
|
|
||||||
reset_env();
|
|
||||||
|
|
||||||
let test = Test::gnu();
|
|
||||||
test.gcc()
|
|
||||||
.file("foo.c")
|
|
||||||
.shared_flag(false)
|
|
||||||
.static_flag(true)
|
|
||||||
.compile("foo");
|
|
||||||
|
|
||||||
test.cmd(0).must_have("-static").must_not_have("-shared");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn gnu_no_dash_dash() {
|
|
||||||
let test = Test::gnu();
|
|
||||||
test.gcc().file("foo.c").compile("foo");
|
|
||||||
|
|
||||||
test.cmd(0).must_not_have("--");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn msvc_smoke() {
|
|
||||||
reset_env();
|
|
||||||
|
|
||||||
let test = Test::msvc();
|
|
||||||
test.gcc().file("foo.c").compile("foo");
|
|
||||||
|
|
||||||
test.cmd(0)
|
|
||||||
.must_have("-O2")
|
|
||||||
.must_have("foo.c")
|
|
||||||
.must_not_have("-Z7")
|
|
||||||
.must_have("-c")
|
|
||||||
.must_have("-MD");
|
|
||||||
test.cmd(1).must_have(test.td.path().join("foo.o"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn msvc_opt_level_0() {
|
|
||||||
reset_env();
|
|
||||||
|
|
||||||
let test = Test::msvc();
|
|
||||||
test.gcc().opt_level(0).file("foo.c").compile("foo");
|
|
||||||
|
|
||||||
test.cmd(0).must_not_have("-O2");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn msvc_debug() {
|
|
||||||
let test = Test::msvc();
|
|
||||||
test.gcc().debug(true).file("foo.c").compile("foo");
|
|
||||||
test.cmd(0).must_have("-Z7");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn msvc_include() {
|
|
||||||
let test = Test::msvc();
|
|
||||||
test.gcc().include("foo/bar").file("foo.c").compile("foo");
|
|
||||||
|
|
||||||
test.cmd(0).must_have("-I").must_have("foo/bar");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn msvc_define() {
|
|
||||||
let test = Test::msvc();
|
|
||||||
test.gcc()
|
|
||||||
.define("FOO", "bar")
|
|
||||||
.define("BAR", None)
|
|
||||||
.file("foo.c")
|
|
||||||
.compile("foo");
|
|
||||||
|
|
||||||
test.cmd(0).must_have("-DFOO=bar").must_have("-DBAR");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn msvc_static_crt() {
|
|
||||||
let test = Test::msvc();
|
|
||||||
test.gcc().static_crt(true).file("foo.c").compile("foo");
|
|
||||||
|
|
||||||
test.cmd(0).must_have("-MT");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn msvc_no_static_crt() {
|
|
||||||
let test = Test::msvc();
|
|
||||||
test.gcc().static_crt(false).file("foo.c").compile("foo");
|
|
||||||
|
|
||||||
test.cmd(0).must_have("-MD");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn msvc_no_dash_dash() {
|
|
||||||
let test = Test::msvc();
|
|
||||||
test.gcc().file("foo.c").compile("foo");
|
|
||||||
|
|
||||||
test.cmd(0).must_not_have("--");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Disable this test with the parallel feature because the execution
|
|
||||||
// order is not deterministic.
|
|
||||||
#[cfg(not(feature = "parallel"))]
|
|
||||||
#[test]
|
|
||||||
fn asm_flags() {
|
|
||||||
let test = Test::gnu();
|
|
||||||
test.gcc()
|
|
||||||
.file("foo.c")
|
|
||||||
.file("x86_64.asm")
|
|
||||||
.file("x86_64.S")
|
|
||||||
.asm_flag("--abc")
|
|
||||||
.compile("foo");
|
|
||||||
test.cmd(0).must_not_have("--abc");
|
|
||||||
test.cmd(1).must_have("--abc");
|
|
||||||
test.cmd(2).must_have("--abc");
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user