Bug 1882202 - Upgrade the cc crate to 1.0.88. r=emilio,supply-chain-reviewers

Differential Revision: https://phabricator.services.mozilla.com/D202771
This commit is contained in:
Mike Hommey 2024-02-27 21:11:25 +00:00
Родитель f909cd37c4
Коммит 2f7038b9d9
30 изменённых файлов: 3659 добавлений и 2547 удалений

6
Cargo.lock сгенерированный
Просмотреть файл

@ -646,11 +646,11 @@ dependencies = [
[[package]]
name = "cc"
version = "1.0.78"
version = "1.0.88"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d"
checksum = "02f341c093d19155a6e41631ce5971aac4e9a868262212153124c15fa22d1cdc"
dependencies = [
"jobserver",
"libc",
]
[[package]]

Просмотреть файл

@ -4754,6 +4754,12 @@ user-id = 6741 # Alice Ryhl (Darksonn)
start = "2021-01-11"
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]]
criteria = "safe-to-deploy"
user-id = 6743 # Ed Page (epage)

Просмотреть файл

@ -71,6 +71,13 @@ user-id = 6741
user-login = "Darksonn"
user-name = "Alice Ryhl"
[[publisher.cc]]
version = "1.0.88"
when = "2024-02-25"
user-id = 2915
user-login = "Amanieu"
user-name = "Amanieu d'Antras"
[[publisher.cexpr]]
version = "0.6.0"
when = "2021-10-11"

2
third_party/rust/cc/.cargo-checksum.json поставляемый
Просмотреть файл

@ -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":"20e23a82fa9c03b73bee3a466a08b7388d75a7329dec60cb053f1687f5d17240","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397","README.md":"f1ddbede208a5b78333a25dac0a7598e678e9b601a7d99a791069bddaf180dfe","src/command_helpers.rs":"3ef95bdcd79a43406fdab275d8a8f45ba787876399b54df34068955ec0109e69","src/lib.rs":"23c2575733eb162a0540ebb28846f4167de700fcc6338cc242af0f7531c8ce96","src/parallel/async_executor.rs":"4ce24435fff6b6555b43fee042c16bd65d4150d0346567f246b9190d85b45983","src/parallel/job_token/mod.rs":"98bc764cf5dcef7786114ce4ee2497ffccf4b4c3a4d761a5d7e50ac4cd79f21f","src/parallel/job_token/unix.rs":"c8feb8075d27719af8a124dad065b1a22d6d657961799e9a13e053ee866814d1","src/parallel/job_token/windows.rs":"88fda34547a4bbe0076b02b1d6f13bfeb75e0e251d6c7d07911b80ed4bfeaafa","src/parallel/mod.rs":"aaffed5ad3dc0d28641533ab0d6f522bf34a059d4b1a239dc4d217cb5d58e232","src/parallel/stderr.rs":"7544f41e1ee8978311470e77892cf6670c293859b52fd53ea197e8baff432638","src/tool.rs":"bc5a63b9d553408b46a7bc1958059034e38b6ccd76d599295432ae32b8d866f7","src/windows/com.rs":"be1564756c9f3ef1398eafeed7b54ba610caba28e8f6258d28a997737ebf9535","src/windows/find_tools.rs":"61dffce9e0529ec64a023f354158c0b39cbca38ec60ae3467cd64a7965ce4777","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":"02f341c093d19155a6e41631ce5971aac4e9a868262212153124c15fa22d1cdc"}

110
third_party/rust/cc/Cargo.lock сгенерированный поставляемый
Просмотреть файл

@ -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"

20
third_party/rust/cc/Cargo.toml поставляемый
Просмотреть файл

@ -11,10 +11,15 @@
[package]
edition = "2018"
rust-version = "1.53"
name = "cc"
version = "1.0.78"
version = "1.0.88"
authors = ["Alex Crichton <alex@alexcrichton.com>"]
exclude = ["/.github"]
exclude = [
"/.github",
"tests",
"src/bin",
]
description = """
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
@ -28,12 +33,13 @@ categories = ["development-tools::build-utils"]
license = "MIT OR Apache-2.0"
repository = "https://github.com/rust-lang/cc-rs"
[dependencies.jobserver]
version = "0.1.16"
optional = true
[dev-dependencies.tempfile]
version = "3"
[features]
parallel = ["jobserver"]
parallel = ["libc"]
[target."cfg(unix)".dependencies.libc]
version = "0.2.62"
optional = true
default-features = false

210
third_party/rust/cc/README.md поставляемый
Просмотреть файл

@ -1,209 +1,13 @@
# 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)
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");
}
```
Refer to the [documentation](https://docs.rs/cc) for detailed usage instructions.
## License

48
third_party/rust/cc/src/bin/gcc-shim.rs поставляемый
Просмотреть файл

@ -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 поставляемый 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
),
))
}
}
}

2576
third_party/rust/cc/src/lib.rs поставляемый

Разница между файлами не показана из-за своего большого размера Загрузить разницу

118
third_party/rust/cc/src/parallel/async_executor.rs поставляемый 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
};
}
}

257
third_party/rust/cc/src/parallel/job_token/mod.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,257 @@
use std::{mem::MaybeUninit, sync::Once};
use crate::Error;
#[cfg(unix)]
#[path = "unix.rs"]
mod sys;
#[cfg(windows)]
#[path = "windows.rs"]
mod sys;
pub(crate) struct JobToken();
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) struct ActiveJobTokenServer(&'static JobTokenServer);
impl ActiveJobTokenServer {
pub(crate) fn new() -> Result<Self, Error> {
let jobserver = JobTokenServer::new();
#[cfg(unix)]
if let JobTokenServer::Inherited(inherited_jobserver) = &jobserver {
inherited_jobserver.enter_active()?;
}
Ok(Self(jobserver))
}
pub(crate) fn try_acquire(&self) -> Result<Option<JobToken>, Error> {
match &self.0 {
JobTokenServer::Inherited(jobserver) => jobserver.try_acquire(),
JobTokenServer::InProcess(jobserver) => Ok(jobserver.try_acquire()),
}
}
}
impl Drop for ActiveJobTokenServer {
fn drop(&mut self) {
#[cfg(unix)]
if let JobTokenServer::Inherited(inherited_jobserver) = &self.0 {
inherited_jobserver.exit_active();
}
}
}
mod inherited_jobserver {
use super::{sys, Error, JobToken};
use std::{
env::var_os,
sync::atomic::{
AtomicBool,
Ordering::{AcqRel, Acquire},
},
};
#[cfg(unix)]
use std::sync::{Mutex, MutexGuard, PoisonError};
pub(crate) 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.
global_implicit_token: AtomicBool,
inner: sys::JobServerClient,
/// number of active clients is required to know when it is safe to clear non-blocking
/// flags
#[cfg(unix)]
active_clients_cnt: Mutex<usize>,
}
impl JobServer {
pub(super) unsafe fn from_env() -> Option<Self> {
let var = var_os("CARGO_MAKEFLAGS")
.or_else(|| var_os("MAKEFLAGS"))
.or_else(|| var_os("MFLAGS"))?;
#[cfg(unix)]
let var = std::os::unix::ffi::OsStrExt::as_bytes(var.as_os_str());
#[cfg(not(unix))]
let var = var.to_str()?.as_bytes();
let makeflags = var.split(u8::is_ascii_whitespace);
// `--jobserver-auth=` is the only documented makeflags.
// `--jobserver-fds=` is actually an internal only makeflags, so we should
// always prefer `--jobserver-auth=`.
//
// Also, according to doc of makeflags, if there are multiple `--jobserver-auth=`
// the last one is used
if let Some(flag) = makeflags
.clone()
.filter_map(|s| s.strip_prefix(b"--jobserver-auth="))
.last()
{
sys::JobServerClient::open(flag)
} else {
sys::JobServerClient::open(
makeflags
.filter_map(|s| s.strip_prefix(b"--jobserver-fds="))
.last()?,
)
}
.map(|inner| Self {
inner,
global_implicit_token: AtomicBool::new(true),
#[cfg(unix)]
active_clients_cnt: Mutex::new(0),
})
}
#[cfg(unix)]
fn get_locked_active_cnt(&self) -> MutexGuard<'_, usize> {
self.active_clients_cnt
.lock()
.unwrap_or_else(PoisonError::into_inner)
}
#[cfg(unix)]
pub(super) fn enter_active(&self) -> Result<(), Error> {
let mut active_cnt = self.get_locked_active_cnt();
if *active_cnt == 0 {
self.inner.prepare_for_acquires()?;
}
*active_cnt += 1;
Ok(())
}
#[cfg(unix)]
pub(super) fn exit_active(&self) {
let mut active_cnt = self.get_locked_active_cnt();
*active_cnt -= 1;
if *active_cnt == 0 {
self.inner.done_acquires();
}
}
pub(super) fn try_acquire(&self) -> Result<Option<JobToken>, Error> {
if !self.global_implicit_token.swap(false, AcqRel) {
// Cold path, no global implicit token, obtain one
if self.inner.try_acquire()?.is_none() {
return Ok(None);
}
}
Ok(Some(JobToken()))
}
pub(super) fn release_token_raw(&self) {
// All tokens will be put back into the jobserver immediately
// and they cannot be cached, since Rust does not call `Drop::drop`
// on global variables.
if self
.global_implicit_token
.compare_exchange(false, true, AcqRel, Acquire)
.is_err()
{
// There's already a global implicit token, so this token must
// be released back into jobserver
let _ = self.inner.release();
}
}
}
}
mod inprocess_jobserver {
use super::JobToken;
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) fn try_acquire(&self) -> Option<JobToken> {
let res = self
.0
.fetch_update(AcqRel, Acquire, |tokens| tokens.checked_sub(1));
res.ok().map(|_| JobToken())
}
pub(super) fn release_token_raw(&self) {
self.0.fetch_add(1, AcqRel);
}
}
}

176
third_party/rust/cc/src/parallel/job_token/unix.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,176 @@
use std::{
ffi::OsStr,
fs::{self, File},
io::{self, Read, Write},
mem::ManuallyDrop,
os::{raw::c_int, unix::prelude::*},
path::Path,
};
use crate::parallel::stderr::{set_blocking, set_non_blocking};
pub(super) struct JobServerClient {
read: File,
write: Option<File>,
}
impl JobServerClient {
pub(super) unsafe fn open(var: &[u8]) -> Option<Self> {
if let Some(fifo) = var.strip_prefix(b"fifo:") {
Self::from_fifo(Path::new(OsStr::from_bytes(fifo)))
} else {
Self::from_pipe(OsStr::from_bytes(var).to_str()?)
}
}
/// `--jobserver-auth=fifo:PATH`
fn from_fifo(path: &Path) -> Option<Self> {
let file = fs::OpenOptions::new()
.read(true)
.write(true)
.open(path)
.ok()?;
if is_pipe(&file)? {
// File in Rust is always closed-on-exec as long as it's opened by
// `File::open` or `fs::OpenOptions::open`.
set_non_blocking(&file).ok()?;
Some(Self {
read: file,
write: None,
})
} else {
None
}
}
/// `--jobserver-auth=fd-for-R,fd-for-W`
unsafe fn from_pipe(s: &str) -> Option<Self> {
let (read, write) = s.split_once(',')?;
let read = read.parse().ok()?;
let write = write.parse().ok()?;
let read = ManuallyDrop::new(File::from_raw_fd(read));
let write = ManuallyDrop::new(File::from_raw_fd(write));
// Ok so we've got two integers that look like file descriptors, but
// for extra sanity checking let's see if they actually look like
// instances of a pipe before we return the client.
//
// If we're called from `make` *without* the leading + on our rule
// then we'll have `MAKEFLAGS` env vars but won't actually have
// access to the file descriptors.
match (
is_pipe(&read),
is_pipe(&write),
get_access_mode(&read),
get_access_mode(&write),
) {
(
Some(true),
Some(true),
Some(libc::O_RDONLY) | Some(libc::O_RDWR),
Some(libc::O_WRONLY) | Some(libc::O_RDWR),
) => {
// Optimization: Try converting it to a fifo by using /dev/fd
if let Some(jobserver) =
Self::from_fifo(Path::new(&format!("/dev/fd/{}", read.as_raw_fd())))
{
return Some(jobserver);
}
let read = read.try_clone().ok()?;
let write = write.try_clone().ok()?;
Some(Self {
read,
write: Some(write),
})
}
_ => None,
}
}
pub(super) fn prepare_for_acquires(&self) -> Result<(), crate::Error> {
if let Some(write) = self.write.as_ref() {
set_non_blocking(&self.read)?;
set_non_blocking(write)?;
}
Ok(())
}
pub(super) fn done_acquires(&self) {
if let Some(write) = self.write.as_ref() {
let _ = set_blocking(&self.read);
let _ = set_blocking(write);
}
}
/// Must call `prepare_for_acquire` before using it.
pub(super) fn try_acquire(&self) -> io::Result<Option<()>> {
let mut fds = [libc::pollfd {
fd: self.read.as_raw_fd(),
events: libc::POLLIN,
revents: 0,
}];
let ret = cvt(unsafe { libc::poll(fds.as_mut_ptr(), 1, 0) })?;
if ret == 1 {
let mut buf = [0];
match (&self.read).read(&mut buf) {
Ok(1) => Ok(Some(())),
Ok(_) => Ok(None), // 0, eof
Err(e)
if e.kind() == io::ErrorKind::Interrupted
|| e.kind() == io::ErrorKind::WouldBlock =>
{
Ok(None)
}
Err(e) => Err(e),
}
} else {
Ok(None)
}
}
pub(super) fn release(&self) -> io::Result<()> {
// For write to block, this would mean that pipe is full.
// If all every release are pair with an acquire, then this cannot
// happen.
//
// If it does happen, it is likely a bug in the program using this
// crate or some other programs that use the same jobserver have a
// bug in their code.
//
// If that turns out to not be the case we'll get an error anyway!
let mut write = self.write.as_ref().unwrap_or(&self.read);
match write.write(&[b'+'])? {
1 => Ok(()),
_ => Err(io::Error::from(io::ErrorKind::UnexpectedEof)),
}
}
}
fn cvt(t: c_int) -> io::Result<c_int> {
if t == -1 {
Err(io::Error::last_os_error())
} else {
Ok(t)
}
}
fn is_pipe(file: &File) -> Option<bool> {
Some(file.metadata().ok()?.file_type().is_fifo())
}
fn get_access_mode(file: &File) -> Option<c_int> {
let ret = unsafe { libc::fcntl(file.as_raw_fd(), libc::F_GETFL) };
if ret == -1 {
return None;
}
Some(ret & libc::O_ACCMODE)
}

68
third_party/rust/cc/src/parallel/job_token/windows.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,68 @@
use std::{ffi::CString, io, ptr, str};
use crate::windows::windows_sys::{
OpenSemaphoreA, ReleaseSemaphore, WaitForSingleObject, FALSE, HANDLE, SEMAPHORE_MODIFY_STATE,
THREAD_SYNCHRONIZE, WAIT_ABANDONED, WAIT_FAILED, WAIT_OBJECT_0, WAIT_TIMEOUT,
};
pub(super) struct JobServerClient {
sem: HANDLE,
}
unsafe impl Sync for JobServerClient {}
unsafe impl Send for JobServerClient {}
impl JobServerClient {
pub(super) unsafe fn open(var: &[u8]) -> Option<Self> {
let var = str::from_utf8(var).ok()?;
if !var.is_ascii() {
// `OpenSemaphoreA` only accepts ASCII, not utf-8.
//
// Upstream implementation jobserver and jobslot also uses the
// same function and they works without problem, so there's no
// motivation to support utf-8 here using `OpenSemaphoreW`
// which only makes the code harder to maintain by making it more
// different than upstream.
return None;
}
let name = CString::new(var).ok()?;
let sem = OpenSemaphoreA(
THREAD_SYNCHRONIZE | SEMAPHORE_MODIFY_STATE,
FALSE,
name.as_bytes().as_ptr(),
);
if sem != ptr::null_mut() {
Some(Self { sem })
} else {
None
}
}
pub(super) fn try_acquire(&self) -> io::Result<Option<()>> {
match unsafe { WaitForSingleObject(self.sem, 0) } {
WAIT_OBJECT_0 => Ok(Some(())),
WAIT_TIMEOUT => Ok(None),
WAIT_FAILED => Err(io::Error::last_os_error()),
// We believe this should be impossible for a semaphore, but still
// check the error code just in case it happens.
WAIT_ABANDONED => Err(io::Error::new(
io::ErrorKind::Other,
"Wait on jobserver semaphore returned WAIT_ABANDONED",
)),
_ => unreachable!("Unexpected return value from WaitForSingleObject"),
}
}
pub(super) fn release(&self) -> io::Result<()> {
// SAFETY: ReleaseSemaphore will write to prev_count is it is Some
// and release semaphore self.sem by 1.
let r = unsafe { ReleaseSemaphore(self.sem, 1, ptr::null_mut()) };
if r != 0 {
Ok(())
} else {
Err(io::Error::last_os_error())
}
}
}

20
third_party/rust/cc/src/parallel/mod.rs поставляемый 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);
}
}
}

100
third_party/rust/cc/src/parallel/stderr.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,100 @@
/// 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_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))
}
#[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())
}

400
third_party/rust/cc/src/tool.rs поставляемый Normal file
Просмотреть файл

@ -0,0 +1,400 @@
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,
)
}
#[cfg(windows)]
/// 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)]
use crate::winapi::CoInitializeEx;
use crate::winapi::IUnknown;
use crate::winapi::Interface;
use crate::winapi::BSTR;
use crate::winapi::COINIT_MULTITHREADED;
use crate::winapi::{SysFreeString, SysStringLen};
use crate::winapi::{HRESULT, S_FALSE, S_OK};
use std::ffi::{OsStr, OsString};
use std::mem::forget;
use std::ops::Deref;
use std::os::windows::ffi::{OsStrExt, OsStringExt};
use std::ptr::null_mut;
use std::slice::from_raw_parts;
use crate::windows::{
winapi::{IUnknown, Interface},
windows_sys::{
CoInitializeEx, SysFreeString, SysStringLen, BSTR, COINIT_MULTITHREADED, HRESULT, S_FALSE,
S_OK,
},
};
use std::{
convert::TryInto,
ffi::{OsStr, OsString},
mem::ManuallyDrop,
ops::Deref,
os::windows::ffi::{OsStrExt, OsStringExt},
ptr::{null, null_mut},
slice::from_raw_parts,
};
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 {
// S_FALSE just means COM is already initialized
return Err(err);
Err(err)
} else {
Ok(())
}
Ok(())
}
pub struct ComPtr<T>(*mut T)
@ -55,15 +59,13 @@ where
/// Extracts the raw pointer.
/// You are now responsible for releasing it yourself.
pub fn into_raw(self) -> *mut T {
let p = self.0;
forget(self);
p
ManuallyDrop::new(self).0
}
/// For internal use only.
fn as_unknown(&self) -> &IUnknown {
unsafe { &*(self.0 as *mut IUnknown) }
}
/// Performs QueryInterface fun.
/// Performs `QueryInterface` fun.
pub fn cast<U>(&self) -> Result<ComPtr<U>, i32>
where
U: Interface,

Просмотреть файл

@ -11,6 +11,8 @@
//! A helper module to probe the Windows Registry when looking for
//! windows-specific tools.
#![allow(clippy::upper_case_acronyms)]
use std::process::Command;
use crate::Tool;
@ -53,6 +55,9 @@ pub fn find_tool(target: &str, tool: &str) -> Option<Tool> {
return None;
}
// Split the target to get the arch.
let target = impl_::TargetArch(target.split_once('-')?.0);
// 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
// dedicated registry keys.
@ -71,15 +76,16 @@ pub fn find_tool(target: &str, tool: &str) -> Option<Tool> {
// environment variables like `LIB`, `INCLUDE`, and `PATH` to ensure that
// 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_14(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
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[non_exhaustive]
pub enum VsVers {
/// Visual Studio 12 (2013)
Vs12,
@ -91,13 +97,6 @@ pub enum VsVers {
Vs16,
/// Visual Studio 17 (2022)
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
@ -106,7 +105,7 @@ pub enum VsVers {
/// generator.
#[cfg(not(windows))]
pub fn find_vs_version() -> Result<VsVers, String> {
Err(format!("not windows"))
Err("not windows".to_string())
}
/// Documented above
@ -160,10 +159,14 @@ pub fn find_vs_version() -> Result<VsVers, String> {
#[cfg(windows)]
mod impl_ {
use crate::com;
use crate::registry::{RegistryKey, LOCAL_MACHINE};
use crate::setup_config::SetupConfiguration;
use crate::vs_instances::{VsInstances, VswhereInstance};
use crate::windows::com;
use crate::windows::registry::{RegistryKey, LOCAL_MACHINE};
use crate::windows::setup_config::SetupConfiguration;
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::env;
use std::ffi::OsString;
@ -174,10 +177,27 @@ mod impl_ {
use std::path::{Path, PathBuf};
use std::process::Command;
use std::str::FromStr;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Once;
use super::MSVC_FAMILY;
use crate::Tool;
#[derive(Copy, Clone)]
pub 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
}
}
struct MsvcTool {
tool: PathBuf,
libs: Vec<PathBuf>,
@ -185,10 +205,75 @@ mod impl_ {
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 {
fn new(tool: PathBuf) -> MsvcTool {
MsvcTool {
tool: tool,
tool,
libs: Vec::new(),
path: Vec::new(),
include: Vec::new(),
@ -202,7 +287,7 @@ mod impl_ {
path,
include,
} = 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, "PATH", path);
add_env(&mut tool, "INCLUDE", include);
@ -212,15 +297,14 @@ mod impl_ {
/// 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.
#[cfg(windows)]
fn is_vscmd_target(target: &str) -> Option<bool> {
fn is_vscmd_target(target: TargetArch<'_>) -> Option<bool> {
let vscmd_arch = env::var("VSCMD_ARG_TGT_ARCH").ok()?;
// Convert the Rust target arch to its VS arch equivalent.
let arch = match target.split("-").next() {
Some("x86_64") => "x64",
Some("aarch64") => "arm64",
Some("i686") | Some("i586") => "x86",
Some("thumbv7a") => "arm",
let arch = match target.into() {
"x86_64" => "x64",
"aarch64" | "arm64ec" => "arm64",
"i686" | "i586" => "x86",
"thumbv7a" => "arm",
// An unrecognized arch.
_ => return Some(false),
};
@ -228,7 +312,7 @@ mod impl_ {
}
/// Attempt to find the tool using environment variables set by vcvars.
pub fn find_msvc_environment(tool: &str, target: &str) -> Option<Tool> {
pub fn find_msvc_environment(tool: &str, target: TargetArch<'_>) -> Option<Tool> {
// Early return if the environment doesn't contain a VC install.
if env::var_os("VCINSTALLDIR").is_none() {
return None;
@ -248,16 +332,19 @@ mod impl_ {
.map(|p| p.join(tool))
.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")
}
#[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) {
instances
} else {
@ -275,7 +362,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)
.filter_map(|path| {
let path = path.join(tool);
@ -283,10 +374,10 @@ mod impl_ {
return None;
}
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()));
}
if target.contains("aarch64") {
if target == "aarch64" || target == "arm64ec" {
tool.env.push(("Platform".into(), "ARM64".into()));
}
Some(tool)
@ -294,7 +385,7 @@ mod impl_ {
.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")
}
@ -310,7 +401,7 @@ mod impl_ {
//
// 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.
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))
}
@ -323,7 +414,7 @@ mod impl_ {
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)")
.or_else(|_| env::var("ProgramFiles"))
.ok()?
@ -336,11 +427,10 @@ mod impl_ {
return None;
}
let arch = target.split('-').next().unwrap();
let tools_arch = match arch {
let tools_arch = match target.into() {
"i586" | "i686" | "x86_64" => Some("x86.x64"),
"arm" | "thumbv7a" => Some("ARM"),
"aarch64" => Some("ARM64"),
"aarch64" | "arm64ec" => Some("ARM64"),
_ => None,
};
@ -374,7 +464,7 @@ mod impl_ {
.collect()
}
pub fn find_msvc_15plus(tool: &str, target: &str) -> Option<Tool> {
pub fn find_msvc_15plus(tool: &str, target: TargetArch<'_>) -> Option<Tool> {
let iter = vs15plus_instances(target)?;
iter.into_iter()
.filter_map(|instance| {
@ -394,13 +484,13 @@ mod impl_ {
// we keep the registry method as a fallback option.
//
// [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) {
Some(instances) => instances
.into_iter()
.filter_map(|instance| instance.installation_path())
.map(|path| path.join(tool))
.find(|ref path| path.is_file()),
.find(|path| path.is_file()),
None => None,
};
@ -416,10 +506,9 @@ mod impl_ {
path.map(|path| {
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()));
}
if target.contains("aarch64") {
} else if target == "aarch64" {
tool.env.push(("Platform".into(), "ARM64".into()));
}
tool
@ -428,10 +517,10 @@ mod impl_ {
fn tool_from_vs15plus_instance(
tool: &str,
target: &str,
target: TargetArch<'_>,
instance_path: &PathBuf,
) -> 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)?;
let tool_path = bin_path.join(tool);
if !tool_path.exists() {
@ -441,6 +530,9 @@ mod impl_ {
let mut tool = MsvcTool::new(tool_path);
tool.path.push(bin_path.clone());
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.include.push(include_path);
@ -455,45 +547,97 @@ mod impl_ {
}
fn vs15plus_vc_paths(
target: &str,
instance_path: &PathBuf,
) -> Option<(PathBuf, PathBuf, PathBuf, PathBuf, PathBuf)> {
let version_path =
instance_path.join(r"VC\Auxiliary\Build\Microsoft.VCToolsVersion.default.txt");
let mut version_file = File::open(version_path).ok()?;
let mut version = String::new();
version_file.read_to_string(&mut version).ok()?;
let version = version.trim();
let host = match host_arch() {
X86 => "X86",
X86_64 => "X64",
// There is no natively hosted compiler on ARM64.
// Instead, use the x86 toolchain under emulation (there is no x64 emulation).
AARCH64 => "X86",
target: TargetArch<'_>,
instance_path: &Path,
) -> Option<(PathBuf, PathBuf, PathBuf, PathBuf, Option<PathBuf>, PathBuf)> {
let version = vs15plus_vc_read_version(instance_path)?;
let hosts = match host_arch() {
X86 => &["X86"],
X86_64 => &["X64"],
// Starting with VS 17.4, there is a natively hosted compiler on ARM64:
// https://devblogs.microsoft.com/visualstudio/arm64-visual-studio-is-officially-here/
// On older versions of VS, we use x64 if running under emulation is supported,
// otherwise use x86.
AARCH64 => {
if is_amd64_emulation_supported() {
&["ARM64", "X64", "X86"][..]
} else {
&["ARM64", "X86"]
}
}
_ => return None,
};
let target = lib_subdir(target)?;
// The directory layout here is MSVC/bin/Host$host/$target/
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
// on a given host
let bin_path = path
.join("bin")
.join(&format!("Host{}", host))
.join(&target);
let bin_path = host_path.join(target);
// But! we also need PATH to contain the target directory for the host
// architecture, because it contains dlls like mspdb140.dll compiled for
// the host architecture.
let host_dylib_path = path
.join("bin")
.join(&format!("Host{}", host))
.join(&host.to_lowercase());
let lib_path = path.join("lib").join(&target);
let host_dylib_path = host_path.join(host.to_lowercase());
let lib_path = path.join("lib").join(target);
let alt_lib_path = (target == "arm64ec").then(|| path.join("lib").join("arm64ec"));
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 sub = lib_subdir(target)?;
if atl_path.exists() {
@ -505,14 +649,14 @@ mod impl_ {
// For MSVC 14 we need to find the Universal CRT as well as either
// the Windows 10 SDK or Windows 8.1 SDK.
pub fn find_msvc_14(tool: &str, target: &str) -> Option<Tool> {
pub fn find_msvc_14(tool: &str, target: TargetArch<'_>) -> Option<Tool> {
let vcdir = get_vc_dir("14.0")?;
let mut tool = get_tool(tool, &vcdir, target)?;
add_sdks(&mut tool, target)?;
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 (ucrt, ucrt_version) = get_ucrt_dir()?;
@ -555,7 +699,7 @@ mod impl_ {
}
// For MSVC 12 we need to find the Windows 8.1 SDK.
pub fn find_msvc_12(tool: &str, target: &str) -> Option<Tool> {
pub fn find_msvc_12(tool: &str, target: TargetArch<'_>) -> Option<Tool> {
let vcdir = get_vc_dir("12.0")?;
let mut tool = get_tool(tool, &vcdir, target)?;
let sub = lib_subdir(target)?;
@ -571,7 +715,7 @@ mod impl_ {
}
// For MSVC 11 we need to find the Windows 8 SDK.
pub fn find_msvc_11(tool: &str, target: &str) -> Option<Tool> {
pub fn find_msvc_11(tool: &str, target: TargetArch<'_>) -> Option<Tool> {
let vcdir = get_vc_dir("11.0")?;
let mut tool = get_tool(tool, &vcdir, target)?;
let sub = lib_subdir(target)?;
@ -596,7 +740,7 @@ mod impl_ {
// Given a possible MSVC installation directory, we look for the linker and
// 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)
.into_iter()
.map(|(sub, host)| {
@ -605,7 +749,7 @@ mod impl_ {
path.join("bin").join(host),
)
})
.filter(|&(ref path, _)| path.is_file())
.filter(|(path, _)| path.is_file())
.map(|(path, host)| {
let mut tool = MsvcTool::new(path);
tool.path.push(host);
@ -734,9 +878,8 @@ mod impl_ {
// 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
// address space to work with and potentially being faster.
fn bin_subdir(target: &str) -> Vec<(&'static str, &'static str)> {
let arch = target.split('-').next().unwrap();
match (arch, host_arch()) {
fn bin_subdir(target: TargetArch<'_>) -> Vec<(&'static str, &'static str)> {
match (target.into(), host_arch()) {
("i586", X86) | ("i686", X86) => vec![("", "")],
("i586", X86_64) | ("i686", X86_64) => vec![("amd64_x86", "amd64"), ("", "")],
("x86_64", X86) => vec![("x86_amd64", "")],
@ -747,21 +890,19 @@ mod impl_ {
}
}
fn lib_subdir(target: &str) -> Option<&'static str> {
let arch = target.split('-').next().unwrap();
match arch {
fn lib_subdir(target: TargetArch<'_>) -> Option<&'static str> {
match target.into() {
"i586" | "i686" => Some("x86"),
"x86_64" => Some("x64"),
"arm" | "thumbv7a" => Some("arm"),
"aarch64" => Some("arm64"),
"aarch64" | "arm64ec" => Some("arm64"),
_ => None,
}
}
// MSVC's x86 libraries are not in a subfolder
fn vc_lib_subdir(target: &str) -> Option<&'static str> {
let arch = target.split('-').next().unwrap();
match arch {
fn vc_lib_subdir(target: TargetArch<'_>) -> Option<&'static str> {
match target.into() {
"i586" | "i686" => Some(""),
"x86_64" => Some("amd64"),
"arm" | "thumbv7a" => Some("arm"),
@ -813,7 +954,7 @@ mod impl_ {
for subkey in key.iter().filter_map(|k| k.ok()) {
let val = subkey
.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 {
Some(s) => s,
None => continue,
@ -831,19 +972,19 @@ mod impl_ {
pub fn has_msbuild_version(version: &str) -> bool {
match version {
"17.0" => {
find_msbuild_vs17("x86_64-pc-windows-msvc").is_some()
|| find_msbuild_vs17("i686-pc-windows-msvc").is_some()
|| find_msbuild_vs17("aarch64-pc-windows-msvc").is_some()
find_msbuild_vs17(TargetArch("x86_64")).is_some()
|| find_msbuild_vs17(TargetArch("i686")).is_some()
|| find_msbuild_vs17(TargetArch("aarch64")).is_some()
}
"16.0" => {
find_msbuild_vs16("x86_64-pc-windows-msvc").is_some()
|| find_msbuild_vs16("i686-pc-windows-msvc").is_some()
|| find_msbuild_vs16("aarch64-pc-windows-msvc").is_some()
find_msbuild_vs16(TargetArch("x86_64")).is_some()
|| find_msbuild_vs16(TargetArch("i686")).is_some()
|| find_msbuild_vs16(TargetArch("aarch64")).is_some()
}
"15.0" => {
find_msbuild_vs15("x86_64-pc-windows-msvc").is_some()
|| find_msbuild_vs15("i686-pc-windows-msvc").is_some()
|| find_msbuild_vs15("aarch64-pc-windows-msvc").is_some()
find_msbuild_vs15(TargetArch("x86_64")).is_some()
|| find_msbuild_vs15(TargetArch("i686")).is_some()
|| find_msbuild_vs15(TargetArch("aarch64")).is_some()
}
"12.0" | "14.0" => LOCAL_MACHINE
.open(&OsString::from(format!(
@ -855,18 +996,20 @@ mod impl_ {
}
}
pub fn find_devenv(target: &str) -> Option<Tool> {
find_devenv_vs15(&target)
pub fn find_devenv(target: TargetArch<'_>) -> Option<Tool> {
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)
}
// see http://stackoverflow.com/questions/328017/path-to-msbuild
pub fn find_msbuild(target: &str) -> Option<Tool> {
pub fn find_msbuild(target: TargetArch<'_>) -> Option<Tool> {
// 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);
} else if let Some(r) = find_msbuild_vs15(target) {
return Some(r);
@ -875,11 +1018,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)
}
fn find_old_msbuild(target: &str) -> Option<Tool> {
fn find_old_msbuild(target: TargetArch<'_>) -> Option<Tool> {
let key = r"SOFTWARE\Microsoft\MSBuild\ToolsVersions";
LOCAL_MACHINE
.open(key.as_ref())
@ -891,7 +1034,7 @@ mod impl_ {
let mut path = PathBuf::from(path);
path.push("MSBuild.exe");
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

20
third_party/rust/cc/src/windows/mod.rs поставляемый 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
// except according to those terms.
use std::ffi::{OsStr, OsString};
use std::io;
use std::ops::RangeFrom;
use std::os::raw;
use std::os::windows::prelude::*;
use crate::windows::windows_sys::{
RegCloseKey, RegEnumKeyExW, RegOpenKeyExW, RegQueryValueExW, ERROR_NO_MORE_ITEMS,
ERROR_SUCCESS, HKEY, HKEY_LOCAL_MACHINE, KEY_READ, KEY_WOW64_32KEY, REG_SZ,
};
use std::{
ffi::{OsStr, OsString},
io,
ops::RangeFrom,
os::windows::prelude::*,
ptr::null_mut,
};
/// Must never be `HKEY_PERFORMANCE_DATA`.
pub(crate) struct RegistryKey(Repr);
type HKEY = *mut u8;
#[allow(clippy::upper_case_acronyms)]
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);
@ -97,7 +57,7 @@ impl RegistryKey {
/// Open a sub-key of `self`.
pub fn open(&self, key: &OsStr) -> io::Result<RegistryKey> {
let key = key.encode_wide().chain(Some(0)).collect::<Vec<_>>();
let mut ret = 0 as *mut _;
let mut ret = null_mut();
let err = unsafe {
RegOpenKeyExW(
self.raw(),
@ -107,7 +67,7 @@ impl RegistryKey {
&mut ret,
)
};
if err == ERROR_SUCCESS as LONG {
if err == ERROR_SUCCESS {
Ok(RegistryKey(Repr::Owned(OwnedKey(ret))))
} else {
Err(io::Error::from_raw_os_error(err as i32))
@ -130,12 +90,12 @@ impl RegistryKey {
let err = RegQueryValueExW(
self.raw(),
name.as_ptr(),
0 as *mut _,
null_mut(),
&mut kind,
0 as *mut _,
null_mut(),
&mut len,
);
if err != ERROR_SUCCESS as LONG {
if err != ERROR_SUCCESS {
return Err(io::Error::from_raw_os_error(err as i32));
}
if kind != REG_SZ {
@ -156,8 +116,8 @@ impl RegistryKey {
let err = RegQueryValueExW(
self.raw(),
name.as_ptr(),
0 as *mut _,
0 as *mut _,
null_mut(),
null_mut(),
v.as_mut_ptr() as *mut _,
&mut len,
);
@ -165,7 +125,7 @@ impl RegistryKey {
// grew between the first and second call to `RegQueryValueExW`),
// both because it's extremely unlikely, and this is a bit more
// 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));
}
// 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 {
v.pop();
}
return Ok(OsString::from_wide(&v));
Ok(OsString::from_wide(&v))
}
}
}
@ -213,14 +173,14 @@ impl<'a> Iterator for Iter<'a> {
i,
v.as_mut_ptr(),
&mut len,
0 as *mut _,
0 as *mut _,
0 as *mut _,
0 as *mut _,
null_mut(),
null_mut(),
null_mut(),
null_mut(),
);
if ret == ERROR_NO_MORE_ITEMS as LONG {
if ret == ERROR_NO_MORE_ITEMS {
None
} else if ret != ERROR_SUCCESS as LONG {
} else if ret != ERROR_SUCCESS {
Some(Err(io::Error::from_raw_os_error(ret as i32)))
} else {
v.set_len(len as usize);

Просмотреть файл

@ -8,19 +8,19 @@
#![allow(bad_style)]
#![allow(unused)]
use crate::winapi::Interface;
use crate::winapi::BSTR;
use crate::winapi::LPCOLESTR;
use crate::winapi::LPSAFEARRAY;
use crate::winapi::S_FALSE;
use crate::winapi::{CoCreateInstance, CLSCTX_ALL};
use crate::winapi::{IUnknown, IUnknownVtbl};
use crate::winapi::{HRESULT, LCID, LPCWSTR, PULONGLONG};
use crate::winapi::{LPFILETIME, ULONG};
use std::ffi::OsString;
use std::ptr::null_mut;
use crate::windows::{
com::{BStr, ComPtr},
winapi::{
IUnknown, IUnknownVtbl, Interface, LCID, LPCOLESTR, LPCWSTR, LPFILETIME, LPSAFEARRAY,
PULONGLONG, ULONG,
},
windows_sys::{CoCreateInstance, BSTR, CLSCTX_ALL, HRESULT, S_FALSE},
};
use crate::com::{BStr, ComPtr};
use std::{
ffi::OsString,
ptr::{null, null_mut},
};
// Bindings to the Setup.Configuration stuff
pub type InstanceState = u32;
@ -212,7 +212,7 @@ impl SetupInstance {
SetupInstance(ComPtr::from_raw(obj))
}
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 bstr = unsafe { BStr::from_raw(s) };
if err < 0 {
@ -221,7 +221,7 @@ impl SetupInstance {
Ok(bstr.to_osstring())
}
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 bstr = unsafe { BStr::from_raw(s) };
if err < 0 {
@ -230,7 +230,7 @@ impl SetupInstance {
Ok(bstr.to_osstring())
}
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 bstr = unsafe { BStr::from_raw(s) };
if err < 0 {
@ -239,7 +239,7 @@ impl SetupInstance {
Ok(bstr.to_osstring())
}
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 bstr = unsafe { BStr::from_raw(s) };
if err < 0 {
@ -248,7 +248,7 @@ impl SetupInstance {
Ok(bstr.to_osstring())
}
pub fn product_path(&self) -> Result<OsString, i32> {
let mut s = null_mut();
let mut s = null();
let this = self.0.cast::<ISetupInstance2>()?;
let err = unsafe { this.GetProductPath(&mut s) };
let bstr = unsafe { BStr::from_raw(s) };

Просмотреть файл

@ -4,7 +4,7 @@ use std::convert::TryFrom;
use std::io::BufRead;
use std::path::PathBuf;
use crate::setup_config::{EnumSetupInstances, SetupInstance};
use crate::windows::setup_config::{EnumSetupInstances, SetupInstance};
pub enum VsInstance {
Com(SetupInstance),

Просмотреть файл

@ -5,26 +5,19 @@
// All files in the project carrying such notice may not be copied, modified, or distributed
// except according to those terms.
#![allow(bad_style)]
#![allow(bad_style, clippy::upper_case_acronyms)]
use std::os::raw;
pub type wchar_t = u16;
pub type UINT = raw::c_uint;
pub type LPUNKNOWN = *mut IUnknown;
pub use crate::windows::windows_sys::{FILETIME, GUID, HRESULT, SAFEARRAY};
pub type REFIID = *const IID;
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 LONG = raw::c_long;
pub type DWORD = u32;
pub type LPVOID = *mut raw::c_void;
pub type HRESULT = raw::c_long;
pub type LPFILETIME = *mut FILETIME;
pub type BSTR = *mut OLECHAR;
pub type OLECHAR = WCHAR;
pub type WCHAR = wchar_t;
pub type LPCOLESTR = *const OLECHAR;
@ -33,75 +26,10 @@ pub type LPCWSTR = *const WCHAR;
pub type PULONGLONG = *mut ULONGLONG;
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 {
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;
macro_rules! DEFINE_GUID {
@ -109,11 +37,11 @@ macro_rules! DEFINE_GUID {
$name:ident, $l:expr, $w1:expr, $w2: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 {
Data1: $l,
Data2: $w1,
Data3: $w2,
Data4: [$b1, $b2, $b3, $b4, $b5, $b6, $b7, $b8],
pub const $name: $crate::windows::winapi::GUID = $crate::windows::winapi::GUID {
data1: $l,
data2: $w1,
data3: $w2,
data4: [$b1, $b2, $b3, $b4, $b5, $b6, $b7, $b8],
};
};
}
@ -193,14 +121,14 @@ macro_rules! RIDL {
$l:expr, $w1:expr, $w2: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]
fn uuidof() -> $crate::winapi::GUID {
$crate::winapi::GUID {
Data1: $l,
Data2: $w1,
Data3: $w2,
Data4: [$b1, $b2, $b3, $b4, $b5, $b6, $b7, $b8],
fn uuidof() -> $crate::windows::winapi::GUID {
$crate::windows::winapi::GUID {
data1: $l,
data2: $w1,
data3: $w2,
data4: [$b1, $b2, $b3, $b4, $b5, $b6, $b7, $b8],
}
}
}

223
third_party/rust/cc/src/windows/windows_sys.rs поставляемый 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 поставляемый
Просмотреть файл

@ -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 поставляемый
Просмотреть файл

@ -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 поставляемый
Просмотреть файл

@ -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 поставляемый
Просмотреть файл

@ -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 поставляемый
Просмотреть файл

@ -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");
}