chore: add vendor dependencies for kauma build

This commit is contained in:
0xalivecow 2024-10-23 10:20:38 +02:00
parent 7c94e5d8fb
commit 067ef6141c
No known key found for this signature in database
1758 changed files with 398473 additions and 0 deletions

491
vendor/cc/src/command_helpers.rs vendored Normal file
View file

@ -0,0 +1,491 @@
//! 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,
pub(crate) output: OutputKind,
checked_dbg_var: Arc<AtomicBool>,
}
/// Different strategies for handling compiler output (to stdout)
#[derive(Clone, Debug)]
pub(crate) enum OutputKind {
/// Forward the output to this process' stdout ([`Stdio::inherit()`])
Forward,
/// Discard the output ([`Stdio::null()`])
Discard,
/// Capture the result (`[Stdio::piped()`])
Capture,
}
impl CargoOutput {
pub(crate) fn new() -> Self {
#[allow(clippy::disallowed_methods)]
Self {
metadata: true,
warnings: true,
output: OutputKind::Forward,
debug: match std::env::var_os("CC_ENABLE_DEBUG_OUTPUT") {
Some(v) => v != "0" && v != "false" && v != "",
None => false,
},
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()
}
}
fn stdio_for_output(&self) -> Stdio {
match self.output {
OutputKind::Capture => Stdio::piped(),
OutputKind::Forward => Stdio::inherit(),
OutputKind::Discard => 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,
/// number of bytes buffered in inner
bytes_buffered: usize,
}
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))),
bytes_buffered: 0,
#[cfg(feature = "parallel")]
is_non_blocking: false,
#[cfg(feature = "parallel")]
bytes_available_failed: false,
}
}
fn forward_available(&mut self) -> bool {
if let Some((stderr, buffer)) = self.inner.as_mut() {
loop {
// 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) => break 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;
break 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
}
#[cfg(target_family = "wasm")]
Err(_) => panic!("bytes_available should always succeed on wasm"),
Ok(bytes_available) => MIN_BUFFER_CAPACITY.max(bytes_available),
}
} else {
MIN_BUFFER_CAPACITY
};
if self.bytes_buffered + to_reserve > buffer.len() {
buffer.resize(self.bytes_buffered + to_reserve, 0);
}
match stderr.read(&mut buffer[self.bytes_buffered..]) {
Err(err) if err.kind() == std::io::ErrorKind::WouldBlock => {
// No data currently, yield back.
break false;
}
Err(err) if err.kind() == std::io::ErrorKind::Interrupted => {
// Interrupted, try again.
continue;
}
Ok(bytes_read) if bytes_read != 0 => {
self.bytes_buffered += bytes_read;
let mut consumed = 0;
for line in buffer[..self.bytes_buffered].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);
}
}
if consumed > 0 && consumed < self.bytes_buffered {
// Remove the consumed bytes from buffer
buffer.copy_within(consumed.., 0);
}
self.bytes_buffered -= consumed;
}
res => {
// End of stream: flush remaining data and bail.
if self.bytes_buffered > 0 {
write_warning(&buffer[..self.bytes_buffered]);
}
if let Err(err) = res {
write_warning(
format!("Failed to read from child stderr: {err}").as_bytes(),
);
}
self.inner.take();
break true;
}
}
}
} 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: &Path,
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.display(),
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.display(),
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: impl AsRef<Path>,
cargo_output: &CargoOutput,
) -> Result<(), Error> {
let program = program.as_ref();
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: impl AsRef<Path>,
cargo_output: &CargoOutput,
) -> Result<Vec<u8>, Error> {
let program = program.as_ref();
// We specifically need the output to be captured, so override default
let mut captured_cargo_output = cargo_output.clone();
captured_cargo_output.output = OutputKind::Capture;
let mut child = spawn(cmd, program, &captured_cargo_output)?;
let mut stdout = vec![];
child
.stdout
.take()
.unwrap()
.read_to_end(&mut stdout)
.unwrap();
// Don't care about this output, use the normal settings
wait_on_child(cmd, program, &mut child, cargo_output)?;
Ok(stdout)
}
pub(crate) fn spawn(
cmd: &mut Command,
program: &Path,
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())
.stdout(cargo_output.stdio_for_output())
.spawn();
match child {
Ok(child) => Ok(child),
Err(ref e) if e.kind() == io::ErrorKind::NotFound => {
let extra = if cfg!(windows) {
" (see https://docs.rs/cc/latest/cc/#compile-time-requirements \
for help)"
} else {
""
};
Err(Error::new(
ErrorKind::ToolNotFound,
format!(
"Failed to find tool. Is `{}` installed?{}",
program.display(),
extra
),
))
}
Err(e) => Err(Error::new(
ErrorKind::ToolExecError,
format!(
"Command {:?} with args {} failed to start: {:?}",
cmd.0,
program.display(),
e
),
)),
}
}
pub(crate) struct CmdAddOutputFileArgs {
pub(crate) cuda: bool,
pub(crate) is_assembler_msvc: bool,
pub(crate) msvc: bool,
pub(crate) clang: bool,
pub(crate) gnu: bool,
pub(crate) is_asm: bool,
pub(crate) is_arm: bool,
}
pub(crate) fn command_add_output_file(cmd: &mut Command, dst: &Path, args: CmdAddOutputFileArgs) {
if args.is_assembler_msvc
|| !(!args.msvc || args.clang || args.gnu || args.cuda || (args.is_asm && args.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: &Path,
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.display(),
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.display(),
e
),
))
}
}
}

15
vendor/cc/src/detect_compiler_family.c vendored Normal file
View file

@ -0,0 +1,15 @@
#ifdef __clang__
#pragma message "clang"
#endif
#ifdef __GNUC__
#pragma message "gcc"
#endif
#ifdef __EMSCRIPTEN__
#pragma message "emscripten"
#endif
#ifdef __VXWORKS__
#pragma message "VxWorks"
#endif

4348
vendor/cc/src/lib.rs vendored Normal file

File diff suppressed because it is too large Load diff

118
vendor/cc/src/parallel/async_executor.rs vendored Normal file
View 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
};
}
}

273
vendor/cc/src/parallel/job_token.rs vendored Normal file
View file

@ -0,0 +1,273 @@
use std::marker::PhantomData;
use crate::Error;
use super::once_lock::OnceLock;
pub(crate) struct JobToken(PhantomData<()>);
impl JobToken {
fn new() -> Self {
Self(PhantomData)
}
}
impl Drop for JobToken {
fn drop(&mut self) {
match JobTokenServer::new() {
JobTokenServer::Inherited(jobserver) => jobserver.release_token_raw(),
JobTokenServer::InProcess(jobserver) => jobserver.release_token_raw(),
}
}
}
enum JobTokenServer {
Inherited(inherited_jobserver::JobServer),
InProcess(inprocess_jobserver::JobServer),
}
impl JobTokenServer {
/// This function returns a static reference to the jobserver because
/// - creating a jobserver from env is a bit fd-unsafe (e.g. the fd might
/// be closed by other jobserver users in the process) and better do it
/// at the start of the program.
/// - in case a jobserver cannot be created from env (e.g. it's not
/// present), we will create a global in-process only jobserver
/// that has to be static so that it will be shared by all cc
/// compilation.
fn new() -> &'static Self {
// TODO: Replace with a OnceLock once MSRV is 1.70
static JOBSERVER: OnceLock<JobTokenServer> = OnceLock::new();
JOBSERVER.get_or_init(|| {
unsafe { inherited_jobserver::JobServer::from_env() }
.map(Self::Inherited)
.unwrap_or_else(|| Self::InProcess(inprocess_jobserver::JobServer::new()))
})
}
}
pub(crate) enum ActiveJobTokenServer {
Inherited(inherited_jobserver::ActiveJobServer<'static>),
InProcess(&'static inprocess_jobserver::JobServer),
}
impl ActiveJobTokenServer {
pub(crate) fn new() -> Self {
match JobTokenServer::new() {
JobTokenServer::Inherited(inherited_jobserver) => {
Self::Inherited(inherited_jobserver.enter_active())
}
JobTokenServer::InProcess(inprocess_jobserver) => Self::InProcess(inprocess_jobserver),
}
}
pub(crate) async fn acquire(&mut self) -> Result<JobToken, Error> {
match self {
Self::Inherited(jobserver) => jobserver.acquire().await,
Self::InProcess(jobserver) => Ok(jobserver.acquire().await),
}
}
}
mod inherited_jobserver {
use super::JobToken;
use crate::{parallel::async_executor::YieldOnce, Error, ErrorKind};
use std::{
io, mem,
sync::{mpsc, Mutex, MutexGuard, PoisonError},
};
pub(super) struct JobServer {
/// Implicit token for this process which is obtained and will be
/// released in parent. Since JobTokens only give back what they got,
/// there should be at most one global implicit token in the wild.
///
/// Since Rust does not execute any `Drop` for global variables,
/// we can't just put it back to jobserver and then re-acquire it at
/// the end of the process.
///
/// Use `Mutex` to avoid race between acquire and release.
/// If an `AtomicBool` is used, then it's possible for:
/// - `release_token_raw`: Tries to set `global_implicit_token` to true, but it is already
/// set to `true`, continue to release it to jobserver
/// - `acquire` takes the global implicit token, set `global_implicit_token` to false
/// - `release_token_raw` now writes the token back into the jobserver, while
/// `global_implicit_token` is `false`
///
/// If the program exits here, then cc effectively increases parallelism by one, which is
/// incorrect, hence we use a `Mutex` here.
global_implicit_token: Mutex<bool>,
inner: jobserver::Client,
}
impl JobServer {
pub(super) unsafe fn from_env() -> Option<Self> {
jobserver::Client::from_env().map(|inner| Self {
inner,
global_implicit_token: Mutex::new(true),
})
}
fn get_global_implicit_token(&self) -> MutexGuard<'_, bool> {
self.global_implicit_token
.lock()
.unwrap_or_else(PoisonError::into_inner)
}
/// All tokens except for the global implicit token will be put back into the jobserver
/// immediately and they cannot be cached, since Rust does not call `Drop::drop` on
/// global variables.
pub(super) fn release_token_raw(&self) {
let mut global_implicit_token = self.get_global_implicit_token();
if *global_implicit_token {
// There's already a global implicit token, so this token must
// be released back into jobserver.
//
// `release_raw` should not block
let _ = self.inner.release_raw();
} else {
*global_implicit_token = true;
}
}
pub(super) fn enter_active(&self) -> ActiveJobServer<'_> {
ActiveJobServer {
jobserver: self,
helper_thread: None,
}
}
}
struct HelperThread {
inner: jobserver::HelperThread,
/// When rx is dropped, all the token stored within it will be dropped.
rx: mpsc::Receiver<io::Result<jobserver::Acquired>>,
}
impl HelperThread {
fn new(jobserver: &JobServer) -> Result<Self, Error> {
let (tx, rx) = mpsc::channel();
Ok(Self {
rx,
inner: jobserver.inner.clone().into_helper_thread(move |res| {
let _ = tx.send(res);
})?,
})
}
}
pub(crate) struct ActiveJobServer<'a> {
jobserver: &'a JobServer,
helper_thread: Option<HelperThread>,
}
impl<'a> ActiveJobServer<'a> {
pub(super) async fn acquire(&mut self) -> Result<JobToken, Error> {
let mut has_requested_token = false;
loop {
// Fast path
if mem::replace(&mut *self.jobserver.get_global_implicit_token(), false) {
break Ok(JobToken::new());
}
match self.jobserver.inner.try_acquire() {
Ok(Some(acquired)) => {
acquired.drop_without_releasing();
break Ok(JobToken::new());
}
Ok(None) => YieldOnce::default().await,
Err(err) if err.kind() == io::ErrorKind::Unsupported => {
// Fallback to creating a help thread with blocking acquire
let helper_thread = if let Some(thread) = self.helper_thread.as_ref() {
thread
} else {
self.helper_thread
.insert(HelperThread::new(self.jobserver)?)
};
match helper_thread.rx.try_recv() {
Ok(res) => {
let acquired = res?;
acquired.drop_without_releasing();
break Ok(JobToken::new());
}
Err(mpsc::TryRecvError::Disconnected) => break Err(Error::new(
ErrorKind::JobserverHelpThreadError,
"jobserver help thread has returned before ActiveJobServer is dropped",
)),
Err(mpsc::TryRecvError::Empty) => {
if !has_requested_token {
helper_thread.inner.request_token();
has_requested_token = true;
}
YieldOnce::default().await
}
}
}
Err(err) => break Err(err.into()),
}
}
}
}
}
mod inprocess_jobserver {
use super::JobToken;
use crate::parallel::async_executor::YieldOnce;
use std::{
env::var,
sync::atomic::{
AtomicU32,
Ordering::{AcqRel, Acquire},
},
};
pub(crate) struct JobServer(AtomicU32);
impl JobServer {
pub(super) fn new() -> Self {
// Use `NUM_JOBS` if set (it's configured by Cargo) and otherwise
// just fall back to a semi-reasonable number.
//
// Note that we could use `num_cpus` here but it's an extra
// dependency that will almost never be used, so
// it's generally not too worth it.
let mut parallelism = 4;
// TODO: Use std::thread::available_parallelism as an upper bound
// when MSRV is bumped.
if let Ok(amt) = var("NUM_JOBS") {
if let Ok(amt) = amt.parse() {
parallelism = amt;
}
}
Self(AtomicU32::new(parallelism))
}
pub(super) async fn acquire(&self) -> JobToken {
loop {
let res = self
.0
.fetch_update(AcqRel, Acquire, |tokens| tokens.checked_sub(1));
if res.is_ok() {
break JobToken::new();
}
YieldOnce::default().await
}
}
pub(super) fn release_token_raw(&self) {
self.0.fetch_add(1, AcqRel);
}
}
}

4
vendor/cc/src/parallel/mod.rs vendored Normal file
View file

@ -0,0 +1,4 @@
pub(crate) mod async_executor;
pub(crate) mod job_token;
pub(crate) mod once_lock;
pub(crate) mod stderr;

47
vendor/cc/src/parallel/once_lock.rs vendored Normal file
View file

@ -0,0 +1,47 @@
use std::{
cell::UnsafeCell,
marker::PhantomData,
mem::MaybeUninit,
panic::{RefUnwindSafe, UnwindSafe},
sync::Once,
};
pub(crate) struct OnceLock<T> {
once: Once,
value: UnsafeCell<MaybeUninit<T>>,
_marker: PhantomData<T>,
}
impl<T> OnceLock<T> {
pub(crate) const fn new() -> Self {
Self {
once: Once::new(),
value: UnsafeCell::new(MaybeUninit::uninit()),
_marker: PhantomData,
}
}
pub(crate) fn get_or_init(&self, f: impl FnOnce() -> T) -> &T {
self.once.call_once(|| {
unsafe { &mut *self.value.get() }.write(f());
});
unsafe { (&*self.value.get()).assume_init_ref() }
}
}
unsafe impl<T: Sync + Send> Sync for OnceLock<T> {}
unsafe impl<T: Send> Send for OnceLock<T> {}
impl<T: RefUnwindSafe + UnwindSafe> RefUnwindSafe for OnceLock<T> {}
impl<T: UnwindSafe> UnwindSafe for OnceLock<T> {}
impl<T> Drop for OnceLock<T> {
#[inline]
fn drop(&mut self) {
if self.once.is_completed() {
// SAFETY: The cell is initialized and being dropped, so it can't
// be accessed again.
unsafe { self.value.get_mut().assume_init_drop() };
}
}
}

91
vendor/cc/src/parallel/stderr.rs vendored Normal file
View file

@ -0,0 +1,91 @@
#![cfg_attr(target_family = "wasm", allow(unused))]
/// Helpers functions for [ChildStderr].
use std::{convert::TryInto, process::ChildStderr};
use crate::{Error, ErrorKind};
#[cfg(all(not(unix), not(windows), not(target_family = "wasm")))]
compile_error!("Only unix and windows support non-blocking pipes! For other OSes, disable the parallel feature.");
#[cfg(unix)]
fn get_flags(fd: std::os::unix::io::RawFd) -> Result<i32, Error> {
let flags = unsafe { libc::fcntl(fd, libc::F_GETFL, 0) };
if flags == -1 {
Err(Error::new(
ErrorKind::IOError,
format!(
"Failed to get flags for pipe {}: {}",
fd,
std::io::Error::last_os_error()
),
))
} else {
Ok(flags)
}
}
#[cfg(unix)]
fn set_flags(fd: std::os::unix::io::RawFd, flags: std::os::raw::c_int) -> Result<(), Error> {
if unsafe { libc::fcntl(fd, libc::F_SETFL, flags) } == -1 {
Err(Error::new(
ErrorKind::IOError,
format!(
"Failed to set flags for pipe {}: {}",
fd,
std::io::Error::last_os_error()
),
))
} else {
Ok(())
}
}
#[cfg(unix)]
pub fn set_non_blocking(pipe: &impl std::os::unix::io::AsRawFd) -> Result<(), Error> {
// On Unix, switch the pipe to non-blocking mode.
// On Windows, we have a different way to be non-blocking.
let fd = pipe.as_raw_fd();
let flags = get_flags(fd)?;
set_flags(fd, flags | libc::O_NONBLOCK)
}
pub fn bytes_available(stderr: &mut ChildStderr) -> Result<usize, Error> {
let mut bytes_available = 0;
#[cfg(windows)]
{
use crate::windows::windows_sys::PeekNamedPipe;
use std::os::windows::io::AsRawHandle;
use std::ptr::null_mut;
if unsafe {
PeekNamedPipe(
stderr.as_raw_handle(),
null_mut(),
0,
null_mut(),
&mut bytes_available,
null_mut(),
)
} == 0
{
return Err(Error::new(
ErrorKind::IOError,
format!(
"PeekNamedPipe failed with {}",
std::io::Error::last_os_error()
),
));
}
}
#[cfg(unix)]
{
use std::os::unix::io::AsRawFd;
if unsafe { libc::ioctl(stderr.as_raw_fd(), libc::FIONREAD, &mut bytes_available) } != 0 {
return Err(Error::new(
ErrorKind::IOError,
format!("ioctl failed with {}", std::io::Error::last_os_error()),
));
}
}
Ok(bytes_available.try_into().unwrap())
}

29
vendor/cc/src/target_info.rs vendored Normal file
View file

@ -0,0 +1,29 @@
//! This file is generated code. Please edit the generator
//! in dev-tools/gen-target-info if you need to make changes.
pub const RISCV_ARCH_MAPPING: &[(&str, &str)] = &[
("riscv32e", "riscv32"),
("riscv32em", "riscv32"),
("riscv32emc", "riscv32"),
("riscv32gc", "riscv32"),
("riscv32i", "riscv32"),
("riscv32im", "riscv32"),
("riscv32ima", "riscv32"),
("riscv32imac", "riscv32"),
("riscv32imafc", "riscv32"),
("riscv32imc", "riscv32"),
("riscv64gc", "riscv64"),
("riscv64imac", "riscv64"),
];
pub const WINDOWS_TRIPLE_MAPPING: &[(&str, &str)] = &[
("aarch64-pc-windows-gnullvm", "aarch64-pc-windows-gnu"),
("aarch64-uwp-windows-msvc", "aarch64-pc-windows-msvc"),
("i686-pc-windows-gnullvm", "i686-pc-windows-gnu"),
("i686-uwp-windows-gnu", "i686-pc-windows-gnu"),
("i686-uwp-windows-msvc", "i686-pc-windows-msvc"),
("i686-win7-windows-msvc", "i686-pc-windows-msvc"),
("thumbv7a-uwp-windows-msvc", "thumbv7a-pc-windows-msvc"),
("x86_64-pc-windows-gnullvm", "x86_64-pc-windows-gnu"),
("x86_64-uwp-windows-gnu", "x86_64-pc-windows-gnu"),
("x86_64-uwp-windows-msvc", "x86_64-pc-windows-msvc"),
];

86
vendor/cc/src/tempfile.rs vendored Normal file
View file

@ -0,0 +1,86 @@
#![cfg_attr(target_family = "wasm", allow(unused))]
use std::{
collections::hash_map::RandomState,
fs::{remove_file, File, OpenOptions},
hash::{BuildHasher, Hasher},
io, os,
path::{Path, PathBuf},
};
#[cfg(not(any(unix, target_family = "wasm", windows)))]
compile_error!("Your system is not supported since cc cannot create named tempfile");
fn rand() -> u64 {
RandomState::new().build_hasher().finish()
}
fn tmpname(suffix: &str) -> String {
format!("{}{}", rand(), suffix)
}
fn create_named(path: &Path) -> io::Result<File> {
let mut open_options = OpenOptions::new();
open_options.read(true).write(true).create_new(true);
#[cfg(all(unix, not(target_os = "wasi")))]
<OpenOptions as os::unix::fs::OpenOptionsExt>::mode(&mut open_options, 0o600);
#[cfg(windows)]
<OpenOptions as os::windows::fs::OpenOptionsExt>::custom_flags(
&mut open_options,
crate::windows::windows_sys::FILE_ATTRIBUTE_TEMPORARY,
);
open_options.open(path)
}
pub(super) struct NamedTempfile {
path: PathBuf,
file: Option<File>,
}
impl NamedTempfile {
pub(super) fn new(base: &Path, suffix: &str) -> io::Result<Self> {
for _ in 0..10 {
let path = base.join(tmpname(suffix));
match create_named(&path) {
Ok(file) => {
return Ok(Self {
file: Some(file),
path,
})
}
Err(e) if e.kind() == io::ErrorKind::AlreadyExists => continue,
Err(e) => return Err(e),
};
}
Err(io::Error::new(
io::ErrorKind::AlreadyExists,
format!(
"too many temporary files exist in base `{}` with suffix `{}`",
base.display(),
suffix
),
))
}
pub(super) fn path(&self) -> &Path {
&self.path
}
pub(super) fn take_file(&mut self) -> Option<File> {
self.file.take()
}
}
impl Drop for NamedTempfile {
fn drop(&mut self) {
// On Windows you have to close all handle to it before
// removing the file.
self.file.take();
let _ = remove_file(&self.path);
}
}

476
vendor/cc/src/tool.rs vendored Normal file
View file

@ -0,0 +1,476 @@
use std::{
borrow::Cow,
collections::HashMap,
env,
ffi::{OsStr, OsString},
io::Write,
path::{Path, PathBuf},
process::Command,
sync::RwLock,
};
use crate::{
command_helpers::{run_output, CargoOutput},
run,
tempfile::NamedTempfile,
Error, ErrorKind, OutputKind,
};
/// 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: &RwLock<HashMap<Box<Path>, ToolFamily>>,
cargo_output: &CargoOutput,
out_dir: Option<&Path>,
) -> Self {
Self::with_features(
path,
None,
false,
cached_compiler_family,
cargo_output,
out_dir,
)
}
pub(crate) fn with_clang_driver(
path: PathBuf,
clang_driver: Option<&str>,
cached_compiler_family: &RwLock<HashMap<Box<Path>, ToolFamily>>,
cargo_output: &CargoOutput,
out_dir: Option<&Path>,
) -> Self {
Self::with_features(
path,
clang_driver,
false,
cached_compiler_family,
cargo_output,
out_dir,
)
}
/// 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: &RwLock<HashMap<Box<Path>, ToolFamily>>,
cargo_output: &CargoOutput,
out_dir: Option<&Path>,
) -> Self {
fn is_zig_cc(path: &Path, cargo_output: &CargoOutput) -> bool {
run_output(
Command::new(path).arg("--version"),
path,
// tool detection issues should always be shown as warnings
cargo_output,
)
.map(|o| String::from_utf8_lossy(&o).contains("ziglang"))
.unwrap_or_default()
}
fn detect_family_inner(
path: &Path,
cargo_output: &CargoOutput,
out_dir: Option<&Path>,
) -> Result<ToolFamily, Error> {
let out_dir = out_dir
.map(Cow::Borrowed)
.unwrap_or_else(|| Cow::Owned(env::temp_dir()));
// Ensure all the parent directories exist otherwise temp file creation
// will fail
std::fs::create_dir_all(&out_dir).map_err(|err| Error {
kind: ErrorKind::IOError,
message: format!("failed to create OUT_DIR '{}': {}", out_dir.display(), err)
.into(),
})?;
let mut tmp =
NamedTempfile::new(&out_dir, "detect_compiler_family.c").map_err(|err| Error {
kind: ErrorKind::IOError,
message: format!(
"failed to create detect_compiler_family.c temp file in '{}': {}",
out_dir.display(),
err
)
.into(),
})?;
let mut tmp_file = tmp.take_file().unwrap();
tmp_file.write_all(include_bytes!("detect_compiler_family.c"))?;
// Close the file handle *now*, otherwise the compiler may fail to open it on Windows
// (#1082). The file stays on disk and its path remains valid until `tmp` is dropped.
tmp_file.flush()?;
tmp_file.sync_data()?;
drop(tmp_file);
let stdout = run_output(
Command::new(path).arg("-E").arg(tmp.path()),
path,
// When expanding the file, the compiler prints a lot of information to stderr
// that it is not an error, but related to expanding itself.
//
// cc would have to disable warning here to prevent generation of too many warnings.
&{
let mut cargo_output = cargo_output.clone();
cargo_output.warnings = cargo_output.debug;
cargo_output
},
)?;
let stdout = String::from_utf8_lossy(&stdout);
cargo_output.print_debug(&stdout);
// https://gitlab.kitware.com/cmake/cmake/-/blob/69a2eeb9dff5b60f2f1e5b425002a0fd45b7cadb/Modules/CMakeDetermineCompilerId.cmake#L267-271
let accepts_cl_style_flags = run(Command::new(path).arg("-?"), path, &{
// the errors are not errors!
let mut cargo_output = cargo_output.clone();
cargo_output.warnings = cargo_output.debug;
cargo_output.output = OutputKind::Discard;
cargo_output
})
.is_ok();
let clang = stdout.contains(r#""clang""#);
let gcc = stdout.contains(r#""gcc""#);
let emscripten = stdout.contains(r#""emscripten""#);
let vxworks = stdout.contains(r#""VxWorks""#);
match (clang, accepts_cl_style_flags, gcc, emscripten, vxworks) {
(clang_cl, true, _, false, false) => Ok(ToolFamily::Msvc { clang_cl }),
(true, _, _, _, false) | (_, _, _, true, false) => Ok(ToolFamily::Clang {
zig_cc: is_zig_cc(path, cargo_output),
}),
(false, false, true, _, false) | (_, _, _, _, true) => Ok(ToolFamily::Gnu),
(false, false, false, false, false) => {
cargo_output.print_warning(&"Compiler family detection failed since it does not define `__clang__`, `__GNUC__`, `__EMSCRIPTEN__` or `__VXWORKS__`, also does not accept cl style flag `-?`, fallback to treating it as GNU");
Err(Error::new(
ErrorKind::ToolFamilyMacroNotFound,
"Expects macro `__clang__`, `__GNUC__` or `__EMSCRIPTEN__`, `__VXWORKS__` or accepts cl style flag `-?`, but found none",
))
}
}
}
let detect_family = |path: &Path| -> Result<ToolFamily, Error> {
if let Some(family) = cached_compiler_family.read().unwrap().get(path) {
return Ok(*family);
}
let family = detect_family_inner(path, cargo_output, out_dir)?;
cached_compiler_family
.write()
.unwrap()
.insert(path.into(), family);
Ok(family)
};
let family = detect_family(&path).unwrap_or_else(|e| {
cargo_output.print_warning(&format_args!(
"Compiler family detection failed due to error: {}",
e
));
match path.file_name().map(OsStr::to_string_lossy) {
Some(fname) if fname.contains("clang-cl") => ToolFamily::Msvc { clang_cl: true },
Some(fname) if fname.ends_with("cl") || fname == "cl.exe" => {
ToolFamily::Msvc { clang_cl: false }
}
Some(fname) if fname.contains("clang") => match clang_driver {
Some("cl") => ToolFamily::Msvc { clang_cl: true },
_ => ToolFamily::Clang {
zig_cc: is_zig_cc(&path, cargo_output),
},
},
Some(fname) if fname.contains("zig") => ToolFamily::Clang { zig_cc: true },
_ => ToolFamily::Gnu,
}
});
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()) && 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) {
eprintln!("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 {
matches!(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 {
matches!(self.family, ToolFamily::Msvc { .. })
}
}
/// 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 { zig_cc: bool },
/// 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 {
matches!(*self, ToolFamily::Clang { .. })
}
}

45
vendor/cc/src/utilities.rs vendored Normal file
View file

@ -0,0 +1,45 @@
use std::{
ffi::OsStr,
fmt::{self, Write},
path::Path,
};
pub(super) struct JoinOsStrs<'a, T> {
pub(super) slice: &'a [T],
pub(super) delimiter: char,
}
impl<T> fmt::Display for JoinOsStrs<'_, T>
where
T: AsRef<OsStr>,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let len = self.slice.len();
for (index, os_str) in self.slice.iter().enumerate() {
// TODO: Use OsStr::display once it is stablised,
// Path and OsStr has the same `Display` impl
write!(f, "{}", Path::new(os_str).display())?;
if index + 1 < len {
f.write_char(self.delimiter)?;
}
}
Ok(())
}
}
pub(super) struct OptionOsStrDisplay<T>(pub(super) Option<T>);
impl<T> fmt::Display for OptionOsStrDisplay<T>
where
T: AsRef<OsStr>,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// TODO: Use OsStr::display once it is stablised
// Path and OsStr has the same `Display` impl
if let Some(os_str) = self.0.as_ref() {
write!(f, "Some({})", Path::new(os_str).display())
} else {
f.write_str("None")
}
}
}

110
vendor/cc/src/windows/com.rs vendored Normal file
View file

@ -0,0 +1,110 @@
// Copyright © 2017 winapi-rs developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
// All files in the project carrying such notice may not be copied, modified, or distributed
// except according to those terms.
use crate::windows::{
winapi::{IUnknown, Interface},
windows_sys::{
CoInitializeEx, SysFreeString, SysStringLen, BSTR, COINIT_MULTITHREADED, HRESULT, S_FALSE,
S_OK,
},
};
use std::{
convert::TryInto,
ffi::OsString,
ops::Deref,
os::windows::ffi::OsStringExt,
ptr::{null, null_mut},
slice::from_raw_parts,
};
pub fn initialize() -> Result<(), HRESULT> {
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
Err(err)
} else {
Ok(())
}
}
pub struct ComPtr<T>(*mut T)
where
T: Interface;
impl<T> ComPtr<T>
where
T: Interface,
{
/// Creates a `ComPtr` to wrap a raw pointer.
/// It takes ownership over the pointer which means it does __not__ call `AddRef`.
/// `T` __must__ be a COM interface that inherits from `IUnknown`.
pub unsafe fn from_raw(ptr: *mut T) -> ComPtr<T> {
assert!(!ptr.is_null());
ComPtr(ptr)
}
/// For internal use only.
fn as_unknown(&self) -> &IUnknown {
unsafe { &*(self.0 as *mut IUnknown) }
}
/// Performs `QueryInterface` fun.
pub fn cast<U>(&self) -> Result<ComPtr<U>, i32>
where
U: Interface,
{
let mut obj = null_mut();
let err = unsafe { self.as_unknown().QueryInterface(&U::uuidof(), &mut obj) };
if err < 0 {
return Err(err);
}
Ok(unsafe { ComPtr::from_raw(obj as *mut U) })
}
}
impl<T> Deref for ComPtr<T>
where
T: Interface,
{
type Target = T;
fn deref(&self) -> &T {
unsafe { &*self.0 }
}
}
impl<T> Clone for ComPtr<T>
where
T: Interface,
{
fn clone(&self) -> Self {
unsafe {
self.as_unknown().AddRef();
ComPtr::from_raw(self.0)
}
}
}
impl<T> Drop for ComPtr<T>
where
T: Interface,
{
fn drop(&mut self) {
unsafe {
self.as_unknown().Release();
}
}
}
pub struct BStr(BSTR);
impl BStr {
pub unsafe fn from_raw(s: BSTR) -> BStr {
BStr(s)
}
pub fn to_osstring(&self) -> OsString {
let len = unsafe { SysStringLen(self.0) };
let slice = unsafe { from_raw_parts(self.0, len as usize) };
OsStringExt::from_wide(slice)
}
}
impl Drop for BStr {
fn drop(&mut self) {
unsafe { SysFreeString(self.0) };
}
}

1191
vendor/cc/src/windows/find_tools.rs vendored Normal file

File diff suppressed because it is too large Load diff

22
vendor/cc/src/windows/mod.rs vendored Normal file
View file

@ -0,0 +1,22 @@
//! 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 windows_targets;
#[cfg(windows)]
mod registry;
#[cfg(windows)]
#[macro_use]
mod winapi;
#[cfg(windows)]
mod com;
#[cfg(windows)]
mod setup_config;
#[cfg(windows)]
mod vs_instances;

191
vendor/cc/src/windows/registry.rs vendored Normal file
View file

@ -0,0 +1,191 @@
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
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);
#[allow(clippy::upper_case_acronyms)]
type DWORD = u32;
struct OwnedKey(HKEY);
/// Note: must not encode `HKEY_PERFORMANCE_DATA` or one of its subkeys.
enum Repr {
/// `HKEY_LOCAL_MACHINE`.
LocalMachine,
/// A subkey of `HKEY_LOCAL_MACHINE`.
Owned(OwnedKey),
}
pub struct Iter<'a> {
idx: RangeFrom<DWORD>,
key: &'a RegistryKey,
}
unsafe impl Sync for Repr {}
unsafe impl Send for Repr {}
pub(crate) const LOCAL_MACHINE: RegistryKey = RegistryKey(Repr::LocalMachine);
impl RegistryKey {
fn raw(&self) -> HKEY {
match self.0 {
Repr::LocalMachine => HKEY_LOCAL_MACHINE,
Repr::Owned(ref val) => val.0,
}
}
/// 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 = null_mut();
let err = unsafe {
RegOpenKeyExW(
self.raw(),
key.as_ptr(),
0,
KEY_READ | KEY_WOW64_32KEY,
&mut ret,
)
};
if err == ERROR_SUCCESS {
Ok(RegistryKey(Repr::Owned(OwnedKey(ret))))
} else {
Err(io::Error::from_raw_os_error(err as i32))
}
}
pub fn iter(&self) -> Iter {
Iter {
idx: 0..,
key: self,
}
}
pub fn query_str(&self, name: &str) -> io::Result<OsString> {
let name: &OsStr = name.as_ref();
let name = name.encode_wide().chain(Some(0)).collect::<Vec<_>>();
let mut len = 0;
let mut kind = 0;
unsafe {
let err = RegQueryValueExW(
self.raw(),
name.as_ptr(),
null_mut(),
&mut kind,
null_mut(),
&mut len,
);
if err != ERROR_SUCCESS {
return Err(io::Error::from_raw_os_error(err as i32));
}
if kind != REG_SZ {
return Err(io::Error::new(
io::ErrorKind::Other,
"registry key wasn't a string",
));
}
// The length here is the length in bytes, but we're using wide
// characters so we need to be sure to halve it for the length
// passed in.
assert!(len % 2 == 0, "impossible wide string size: {} bytes", len);
let vlen = len as usize / 2;
// Defensively initialized, see comment about
// `HKEY_PERFORMANCE_DATA` below.
let mut v = vec![0u16; vlen];
let err = RegQueryValueExW(
self.raw(),
name.as_ptr(),
null_mut(),
null_mut(),
v.as_mut_ptr() as *mut _,
&mut len,
);
// We don't check for `ERROR_MORE_DATA` (which would if the value
// 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 {
return Err(io::Error::from_raw_os_error(err as i32));
}
// The length is allowed to change, but should still be even, as
// well as smaller.
assert!(len % 2 == 0, "impossible wide string size: {} bytes", len);
// If the length grew but returned a success code, it *probably*
// indicates we're `HKEY_PERFORMANCE_DATA` or a subkey(?). We
// consider this UB, since those keys write "undefined" or
// "unpredictable" values to len, and need to use a completely
// different loop structure. This should be impossible (and enforce
// it in the API to the best of our ability), but to mitigate the
// damage we do some smoke-checks on the len, and ensure `v` has
// been fully initialized (rather than trusting the result of
// `RegQueryValueExW`).
let actual_len = len as usize / 2;
assert!(actual_len <= v.len());
v.truncate(actual_len);
// Some registry keys may have a terminating nul character, but
// we're not interested in that, so chop it off if it's there.
if !v.is_empty() && v[v.len() - 1] == 0 {
v.pop();
}
Ok(OsString::from_wide(&v))
}
}
}
impl Drop for OwnedKey {
fn drop(&mut self) {
unsafe {
RegCloseKey(self.0);
}
}
}
impl<'a> Iterator for Iter<'a> {
type Item = io::Result<OsString>;
fn next(&mut self) -> Option<io::Result<OsString>> {
self.idx.next().and_then(|i| unsafe {
let mut v = Vec::with_capacity(256);
let mut len = v.capacity() as DWORD;
let ret = RegEnumKeyExW(
self.key.raw(),
i,
v.as_mut_ptr(),
&mut len,
null_mut(),
null_mut(),
null_mut(),
null_mut(),
);
if ret == ERROR_NO_MORE_ITEMS {
None
} else if ret != ERROR_SUCCESS {
Some(Err(io::Error::from_raw_os_error(ret as i32)))
} else {
v.set_len(len as usize);
Some(Ok(OsString::from_wide(&v)))
}
})
}
}

283
vendor/cc/src/windows/setup_config.rs vendored Normal file
View file

@ -0,0 +1,283 @@
// Copyright © 2017 winapi-rs developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
// All files in the project carrying such notice may not be copied, modified, or distributed
// except according to those terms.
#![allow(bad_style)]
#![allow(unused)]
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 std::{
ffi::OsString,
ptr::{null, null_mut},
};
// Bindings to the Setup.Configuration stuff
pub type InstanceState = u32;
pub const eNone: InstanceState = 0;
pub const eLocal: InstanceState = 1;
pub const eRegistered: InstanceState = 2;
pub const eNoRebootRequired: InstanceState = 4;
pub const eComplete: InstanceState = -1i32 as u32;
RIDL! {#[uuid(0xb41463c3, 0x8866, 0x43b5, 0xbc, 0x33, 0x2b, 0x06, 0x76, 0xf7, 0xf4, 0x2e)]
interface ISetupInstance(ISetupInstanceVtbl): IUnknown(IUnknownVtbl) {
fn GetInstanceId(
pbstrInstanceId: *mut BSTR,
) -> HRESULT,
fn GetInstallDate(
pInstallDate: LPFILETIME,
) -> HRESULT,
fn GetInstallationName(
pbstrInstallationName: *mut BSTR,
) -> HRESULT,
fn GetInstallationPath(
pbstrInstallationPath: *mut BSTR,
) -> HRESULT,
fn GetInstallationVersion(
pbstrInstallationVersion: *mut BSTR,
) -> HRESULT,
fn GetDisplayName(
lcid: LCID,
pbstrDisplayName: *mut BSTR,
) -> HRESULT,
fn GetDescription(
lcid: LCID,
pbstrDescription: *mut BSTR,
) -> HRESULT,
fn ResolvePath(
pwszRelativePath: LPCOLESTR,
pbstrAbsolutePath: *mut BSTR,
) -> HRESULT,
}}
RIDL! {#[uuid(0x89143c9a, 0x05af, 0x49b0, 0xb7, 0x17, 0x72, 0xe2, 0x18, 0xa2, 0x18, 0x5c)]
interface ISetupInstance2(ISetupInstance2Vtbl): ISetupInstance(ISetupInstanceVtbl) {
fn GetState(
pState: *mut InstanceState,
) -> HRESULT,
fn GetPackages(
ppsaPackages: *mut LPSAFEARRAY,
) -> HRESULT,
fn GetProduct(
ppPackage: *mut *mut ISetupPackageReference,
) -> HRESULT,
fn GetProductPath(
pbstrProductPath: *mut BSTR,
) -> HRESULT,
}}
RIDL! {#[uuid(0x6380bcff, 0x41d3, 0x4b2e, 0x8b, 0x2e, 0xbf, 0x8a, 0x68, 0x10, 0xc8, 0x48)]
interface IEnumSetupInstances(IEnumSetupInstancesVtbl): IUnknown(IUnknownVtbl) {
fn Next(
celt: ULONG,
rgelt: *mut *mut ISetupInstance,
pceltFetched: *mut ULONG,
) -> HRESULT,
fn Skip(
celt: ULONG,
) -> HRESULT,
fn Reset() -> HRESULT,
fn Clone(
ppenum: *mut *mut IEnumSetupInstances,
) -> HRESULT,
}}
RIDL! {#[uuid(0x42843719, 0xdb4c, 0x46c2, 0x8e, 0x7c, 0x64, 0xf1, 0x81, 0x6e, 0xfd, 0x5b)]
interface ISetupConfiguration(ISetupConfigurationVtbl): IUnknown(IUnknownVtbl) {
fn EnumInstances(
ppEnumInstances: *mut *mut IEnumSetupInstances,
) -> HRESULT,
fn GetInstanceForCurrentProcess(
ppInstance: *mut *mut ISetupInstance,
) -> HRESULT,
fn GetInstanceForPath(
wzPath: LPCWSTR,
ppInstance: *mut *mut ISetupInstance,
) -> HRESULT,
}}
RIDL! {#[uuid(0x26aab78c, 0x4a60, 0x49d6, 0xaf, 0x3b, 0x3c, 0x35, 0xbc, 0x93, 0x36, 0x5d)]
interface ISetupConfiguration2(ISetupConfiguration2Vtbl):
ISetupConfiguration(ISetupConfigurationVtbl) {
fn EnumAllInstances(
ppEnumInstances: *mut *mut IEnumSetupInstances,
) -> HRESULT,
}}
RIDL! {#[uuid(0xda8d8a16, 0xb2b6, 0x4487, 0xa2, 0xf1, 0x59, 0x4c, 0xcc, 0xcd, 0x6b, 0xf5)]
interface ISetupPackageReference(ISetupPackageReferenceVtbl): IUnknown(IUnknownVtbl) {
fn GetId(
pbstrId: *mut BSTR,
) -> HRESULT,
fn GetVersion(
pbstrVersion: *mut BSTR,
) -> HRESULT,
fn GetChip(
pbstrChip: *mut BSTR,
) -> HRESULT,
fn GetLanguage(
pbstrLanguage: *mut BSTR,
) -> HRESULT,
fn GetBranch(
pbstrBranch: *mut BSTR,
) -> HRESULT,
fn GetType(
pbstrType: *mut BSTR,
) -> HRESULT,
fn GetUniqueId(
pbstrUniqueId: *mut BSTR,
) -> HRESULT,
}}
RIDL! {#[uuid(0x42b21b78, 0x6192, 0x463e, 0x87, 0xbf, 0xd5, 0x77, 0x83, 0x8f, 0x1d, 0x5c)]
interface ISetupHelper(ISetupHelperVtbl): IUnknown(IUnknownVtbl) {
fn ParseVersion(
pwszVersion: LPCOLESTR,
pullVersion: PULONGLONG,
) -> HRESULT,
fn ParseVersionRange(
pwszVersionRange: LPCOLESTR,
pullMinVersion: PULONGLONG,
pullMaxVersion: PULONGLONG,
) -> HRESULT,
}}
DEFINE_GUID! {CLSID_SetupConfiguration,
0x177f0c4a, 0x1cd3, 0x4de7, 0xa3, 0x2c, 0x71, 0xdb, 0xbb, 0x9f, 0xa3, 0x6d}
// Safe wrapper around the COM interfaces
pub struct SetupConfiguration(ComPtr<ISetupConfiguration>);
impl SetupConfiguration {
pub fn new() -> Result<SetupConfiguration, i32> {
let mut obj = null_mut();
let err = unsafe {
CoCreateInstance(
&CLSID_SetupConfiguration,
null_mut(),
CLSCTX_ALL,
&ISetupConfiguration::uuidof(),
&mut obj,
)
};
if err < 0 {
return Err(err);
}
let obj = unsafe { ComPtr::from_raw(obj as *mut ISetupConfiguration) };
Ok(SetupConfiguration(obj))
}
pub fn get_instance_for_current_process(&self) -> Result<SetupInstance, i32> {
let mut obj = null_mut();
let err = unsafe { self.0.GetInstanceForCurrentProcess(&mut obj) };
if err < 0 {
return Err(err);
}
Ok(unsafe { SetupInstance::from_raw(obj) })
}
pub fn enum_instances(&self) -> Result<EnumSetupInstances, i32> {
let mut obj = null_mut();
let err = unsafe { self.0.EnumInstances(&mut obj) };
if err < 0 {
return Err(err);
}
Ok(unsafe { EnumSetupInstances::from_raw(obj) })
}
pub fn enum_all_instances(&self) -> Result<EnumSetupInstances, i32> {
let mut obj = null_mut();
let this = self.0.cast::<ISetupConfiguration2>()?;
let err = unsafe { this.EnumAllInstances(&mut obj) };
if err < 0 {
return Err(err);
}
Ok(unsafe { EnumSetupInstances::from_raw(obj) })
}
}
pub struct SetupInstance(ComPtr<ISetupInstance>);
impl SetupInstance {
pub unsafe fn from_raw(obj: *mut ISetupInstance) -> SetupInstance {
SetupInstance(ComPtr::from_raw(obj))
}
pub fn instance_id(&self) -> Result<OsString, i32> {
let mut s = null();
let err = unsafe { self.0.GetInstanceId(&mut s) };
let bstr = unsafe { BStr::from_raw(s) };
if err < 0 {
return Err(err);
}
Ok(bstr.to_osstring())
}
pub fn installation_name(&self) -> Result<OsString, i32> {
let mut s = null();
let err = unsafe { self.0.GetInstallationName(&mut s) };
let bstr = unsafe { BStr::from_raw(s) };
if err < 0 {
return Err(err);
}
Ok(bstr.to_osstring())
}
pub fn installation_path(&self) -> Result<OsString, i32> {
let mut s = null();
let err = unsafe { self.0.GetInstallationPath(&mut s) };
let bstr = unsafe { BStr::from_raw(s) };
if err < 0 {
return Err(err);
}
Ok(bstr.to_osstring())
}
pub fn installation_version(&self) -> Result<OsString, i32> {
let mut s = null();
let err = unsafe { self.0.GetInstallationVersion(&mut s) };
let bstr = unsafe { BStr::from_raw(s) };
if err < 0 {
return Err(err);
}
Ok(bstr.to_osstring())
}
pub fn product_path(&self) -> Result<OsString, i32> {
let mut s = null();
let this = self.0.cast::<ISetupInstance2>()?;
let err = unsafe { this.GetProductPath(&mut s) };
let bstr = unsafe { BStr::from_raw(s) };
if err < 0 {
return Err(err);
}
Ok(bstr.to_osstring())
}
}
pub struct EnumSetupInstances(ComPtr<IEnumSetupInstances>);
impl EnumSetupInstances {
pub unsafe fn from_raw(obj: *mut IEnumSetupInstances) -> EnumSetupInstances {
EnumSetupInstances(ComPtr::from_raw(obj))
}
}
impl Iterator for EnumSetupInstances {
type Item = Result<SetupInstance, i32>;
fn next(&mut self) -> Option<Result<SetupInstance, i32>> {
let mut obj = null_mut();
let err = unsafe { self.0.Next(1, &mut obj, null_mut()) };
if err < 0 {
return Some(Err(err));
}
if err == S_FALSE {
return None;
}
Some(Ok(unsafe { SetupInstance::from_raw(obj) }))
}
}

199
vendor/cc/src/windows/vs_instances.rs vendored Normal file
View file

@ -0,0 +1,199 @@
use std::borrow::Cow;
use std::collections::HashMap;
use std::convert::TryFrom;
use std::io::BufRead;
use std::path::PathBuf;
use crate::windows::setup_config::{EnumSetupInstances, SetupInstance};
pub enum VsInstance {
Com(SetupInstance),
Vswhere(VswhereInstance),
}
impl VsInstance {
pub fn installation_name(&self) -> Option<Cow<str>> {
match self {
VsInstance::Com(s) => s
.installation_name()
.ok()
.and_then(|s| s.into_string().ok())
.map(Cow::from),
VsInstance::Vswhere(v) => v.map.get("installationName").map(Cow::from),
}
}
pub fn installation_path(&self) -> Option<PathBuf> {
match self {
VsInstance::Com(s) => s.installation_path().ok().map(PathBuf::from),
VsInstance::Vswhere(v) => v.map.get("installationPath").map(PathBuf::from),
}
}
pub fn installation_version(&self) -> Option<Cow<str>> {
match self {
VsInstance::Com(s) => s
.installation_version()
.ok()
.and_then(|s| s.into_string().ok())
.map(Cow::from),
VsInstance::Vswhere(v) => v.map.get("installationVersion").map(Cow::from),
}
}
}
pub enum VsInstances {
ComBased(EnumSetupInstances),
VswhereBased(VswhereInstance),
}
impl IntoIterator for VsInstances {
type Item = VsInstance;
#[allow(bare_trait_objects)]
type IntoIter = Box<Iterator<Item = Self::Item>>;
fn into_iter(self) -> Self::IntoIter {
match self {
VsInstances::ComBased(e) => {
Box::new(e.into_iter().filter_map(Result::ok).map(VsInstance::Com))
}
VsInstances::VswhereBased(v) => Box::new(std::iter::once(VsInstance::Vswhere(v))),
}
}
}
#[derive(Debug)]
pub struct VswhereInstance {
map: HashMap<String, String>,
}
impl TryFrom<&Vec<u8>> for VswhereInstance {
type Error = &'static str;
fn try_from(output: &Vec<u8>) -> Result<Self, Self::Error> {
let map: HashMap<_, _> = output
.lines()
.map_while(Result::ok)
.filter_map(|s| {
let mut splitn = s.splitn(2, ": ");
Some((splitn.next()?.to_owned(), splitn.next()?.to_owned()))
})
.collect();
if !map.contains_key("installationName")
|| !map.contains_key("installationPath")
|| !map.contains_key("installationVersion")
{
return Err("required properties not found");
}
Ok(Self { map })
}
}
#[cfg(test)]
mod tests_ {
use std::borrow::Cow;
use std::convert::TryFrom;
use std::path::PathBuf;
#[test]
fn it_parses_vswhere_output_correctly() {
let output = br"instanceId: 58104422
installDate: 21/02/2021 21:50:33
installationName: VisualStudio/16.9.2+31112.23
installationPath: C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools
installationVersion: 16.9.31112.23
productId: Microsoft.VisualStudio.Product.BuildTools
productPath: C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\Common7\Tools\LaunchDevCmd.bat
state: 4294967295
isComplete: 1
isLaunchable: 1
isPrerelease: 0
isRebootRequired: 0
displayName: Visual Studio Build Tools 2019
description: The Visual Studio Build Tools allows you to build native and managed MSBuild-based applications without requiring the Visual Studio IDE. There are options to install the Visual C++ compilers and libraries, MFC, ATL, and C++/CLI support.
channelId: VisualStudio.16.Release
channelUri: https://aka.ms/vs/16/release/channel
enginePath: C:\Program Files (x86)\Microsoft Visual Studio\Installer\resources\app\ServiceHub\Services\Microsoft.VisualStudio.Setup.Service
releaseNotes: https://docs.microsoft.com/en-us/visualstudio/releases/2019/release-notes-v16.9#16.9.2
thirdPartyNotices: https://go.microsoft.com/fwlink/?LinkId=660909
updateDate: 2021-03-17T21:16:46.5963702Z
catalog_buildBranch: d16.9
catalog_buildVersion: 16.9.31112.23
catalog_id: VisualStudio/16.9.2+31112.23
catalog_localBuild: build-lab
catalog_manifestName: VisualStudio
catalog_manifestType: installer
catalog_productDisplayVersion: 16.9.2
catalog_productLine: Dev16
catalog_productLineVersion: 2019
catalog_productMilestone: RTW
catalog_productMilestoneIsPreRelease: False
catalog_productName: Visual Studio
catalog_productPatchVersion: 2
catalog_productPreReleaseMilestoneSuffix: 1.0
catalog_productSemanticVersion: 16.9.2+31112.23
catalog_requiredEngineVersion: 2.9.3365.38425
properties_campaignId: 156063665.1613940062
properties_channelManifestId: VisualStudio.16.Release/16.9.2+31112.23
properties_nickname:
properties_setupEngineFilePath: C:\Program Files (x86)\Microsoft Visual Studio\Installer\vs_installershell.exe
"
.to_vec();
let vswhere_instance = super::VswhereInstance::try_from(&output);
assert!(vswhere_instance.is_ok());
let vs_instance = super::VsInstance::Vswhere(vswhere_instance.unwrap());
assert_eq!(
vs_instance.installation_name(),
Some(Cow::from("VisualStudio/16.9.2+31112.23"))
);
assert_eq!(
vs_instance.installation_path(),
Some(PathBuf::from(
r"C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools"
))
);
assert_eq!(
vs_instance.installation_version(),
Some(Cow::from("16.9.31112.23"))
);
}
#[test]
fn it_returns_an_error_for_empty_output() {
let output = b"".to_vec();
let vswhere_instance = super::VswhereInstance::try_from(&output);
assert!(vswhere_instance.is_err());
}
#[test]
fn it_returns_an_error_for_output_consisting_of_empty_lines() {
let output = br"
"
.to_vec();
let vswhere_instance = super::VswhereInstance::try_from(&output);
assert!(vswhere_instance.is_err());
}
#[test]
fn it_returns_an_error_for_output_without_required_properties() {
let output = br"instanceId: 58104422
installDate: 21/02/2021 21:50:33
productId: Microsoft.VisualStudio.Product.BuildTools
productPath: C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\Common7\Tools\LaunchDevCmd.bat
"
.to_vec();
let vswhere_instance = super::VswhereInstance::try_from(&output);
assert!(vswhere_instance.is_err());
}
}

146
vendor/cc/src/windows/winapi.rs vendored Normal file
View file

@ -0,0 +1,146 @@
// Copyright © 2015-2017 winapi-rs developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
// All files in the project carrying such notice may not be copied, modified, or distributed
// except according to those terms.
#![allow(bad_style, clippy::upper_case_acronyms)]
use std::os::raw;
pub type wchar_t = u16;
pub use crate::windows::windows_sys::{FILETIME, GUID, HRESULT, SAFEARRAY};
pub type REFIID = *const IID;
pub type IID = GUID;
pub type ULONG = raw::c_ulong;
pub type DWORD = u32;
pub type LPFILETIME = *mut FILETIME;
pub type OLECHAR = WCHAR;
pub type WCHAR = wchar_t;
pub type LPCOLESTR = *const OLECHAR;
pub type LCID = DWORD;
pub type LPCWSTR = *const WCHAR;
pub type PULONGLONG = *mut ULONGLONG;
pub type ULONGLONG = u64;
pub trait Interface {
fn uuidof() -> GUID;
}
pub type LPSAFEARRAY = *mut SAFEARRAY;
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::windows::winapi::GUID = $crate::windows::winapi::GUID {
data1: $l,
data2: $w1,
data3: $w2,
data4: [$b1, $b2, $b3, $b4, $b5, $b6, $b7, $b8],
};
};
}
macro_rules! RIDL {
(#[uuid($($uuid:expr),+)]
interface $interface:ident ($vtbl:ident) {$(
fn $method:ident($($p:ident : $t:ty,)*) -> $rtr:ty,
)+}) => (
#[repr(C)]
pub struct $vtbl {
$(pub $method: unsafe extern "system" fn(
This: *mut $interface,
$($p: $t),*
) -> $rtr,)+
}
#[repr(C)]
pub struct $interface {
pub lpVtbl: *const $vtbl,
}
RIDL!{@impl $interface {$(fn $method($($p: $t,)*) -> $rtr,)+}}
RIDL!{@uuid $interface $($uuid),+}
);
(#[uuid($($uuid:expr),+)]
interface $interface:ident ($vtbl:ident) : $pinterface:ident ($pvtbl:ident) {
}) => (
#[repr(C)]
pub struct $vtbl {
pub parent: $pvtbl,
}
#[repr(C)]
pub struct $interface {
pub lpVtbl: *const $vtbl,
}
RIDL!{@deref $interface $pinterface}
RIDL!{@uuid $interface $($uuid),+}
);
(#[uuid($($uuid:expr),+)]
interface $interface:ident ($vtbl:ident) : $pinterface:ident ($pvtbl:ident) {$(
fn $method:ident($($p:ident : $t:ty,)*) -> $rtr:ty,
)+}) => (
#[repr(C)]
pub struct $vtbl {
pub parent: $pvtbl,
$(pub $method: unsafe extern "system" fn(
This: *mut $interface,
$($p: $t,)*
) -> $rtr,)+
}
#[repr(C)]
pub struct $interface {
pub lpVtbl: *const $vtbl,
}
RIDL!{@impl $interface {$(fn $method($($p: $t,)*) -> $rtr,)+}}
RIDL!{@deref $interface $pinterface}
RIDL!{@uuid $interface $($uuid),+}
);
(@deref $interface:ident $pinterface:ident) => (
impl ::std::ops::Deref for $interface {
type Target = $pinterface;
#[inline]
fn deref(&self) -> &$pinterface {
unsafe { &*(self as *const $interface as *const $pinterface) }
}
}
);
(@impl $interface:ident {$(
fn $method:ident($($p:ident : $t:ty,)*) -> $rtr:ty,
)+}) => (
impl $interface {
$(#[inline] pub unsafe fn $method(&self, $($p: $t,)*) -> $rtr {
((*self.lpVtbl).$method)(self as *const _ as *mut _, $($p,)*)
})+
}
);
(@uuid $interface:ident
$l:expr, $w1:expr, $w2:expr,
$b1:expr, $b2:expr, $b3:expr, $b4:expr, $b5:expr, $b6:expr, $b7:expr, $b8:expr
) => (
impl $crate::windows::winapi::Interface for $interface {
#[inline]
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],
}
}
}
);
}
RIDL! {#[uuid(0x00000000, 0x0000, 0x0000, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46)]
interface IUnknown(IUnknownVtbl) {
fn QueryInterface(
riid: REFIID,
ppvObject: *mut *mut raw::c_void,
) -> HRESULT,
fn AddRef() -> ULONG,
fn Release() -> ULONG,
}}

121
vendor/cc/src/windows/windows_sys.rs vendored Normal file
View file

@ -0,0 +1,121 @@
// 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.58.0
#![allow(
non_snake_case,
non_upper_case_globals,
non_camel_case_types,
dead_code,
clippy::all
)]
windows_targets::link!("advapi32.dll" "system" fn RegCloseKey(hkey : HKEY) -> WIN32_ERROR);
windows_targets::link!("advapi32.dll" "system" fn RegEnumKeyExW(hkey : HKEY, dwindex : u32, lpname : PWSTR, lpcchname : *mut u32, lpreserved : *const u32, lpclass : PWSTR, lpcchclass : *mut u32, lpftlastwritetime : *mut FILETIME) -> WIN32_ERROR);
windows_targets::link!("advapi32.dll" "system" fn RegOpenKeyExW(hkey : HKEY, lpsubkey : PCWSTR, uloptions : u32, samdesired : REG_SAM_FLAGS, phkresult : *mut HKEY) -> WIN32_ERROR);
windows_targets::link!("advapi32.dll" "system" fn RegQueryValueExW(hkey : HKEY, lpvaluename : PCWSTR, lpreserved : *const u32, lptype : *mut REG_VALUE_TYPE, lpdata : *mut u8, lpcbdata : *mut u32) -> WIN32_ERROR);
windows_targets::link!("kernel32.dll" "system" fn FreeLibrary(hlibmodule : HMODULE) -> BOOL);
windows_targets::link!("kernel32.dll" "system" fn GetMachineTypeAttributes(machine : u16, machinetypeattributes : *mut MACHINE_ATTRIBUTES) -> HRESULT);
windows_targets::link!("kernel32.dll" "system" fn GetProcAddress(hmodule : HMODULE, lpprocname : PCSTR) -> FARPROC);
windows_targets::link!("kernel32.dll" "system" fn LoadLibraryA(lplibfilename : PCSTR) -> HMODULE);
windows_targets::link!("kernel32.dll" "system" fn OpenSemaphoreA(dwdesiredaccess : u32, binherithandle : BOOL, lpname : PCSTR) -> HANDLE);
windows_targets::link!("kernel32.dll" "system" fn PeekNamedPipe(hnamedpipe : HANDLE, lpbuffer : *mut core::ffi::c_void, nbuffersize : u32, lpbytesread : *mut u32, lptotalbytesavail : *mut u32, lpbytesleftthismessage : *mut u32) -> BOOL);
windows_targets::link!("kernel32.dll" "system" fn ReleaseSemaphore(hsemaphore : HANDLE, lreleasecount : i32, lppreviouscount : *mut i32) -> BOOL);
windows_targets::link!("kernel32.dll" "system" fn WaitForSingleObject(hhandle : HANDLE, dwmilliseconds : u32) -> WAIT_EVENT);
windows_targets::link!("ole32.dll" "system" fn CoCreateInstance(rclsid : *const GUID, punkouter : * mut core::ffi::c_void, dwclscontext : CLSCTX, riid : *const GUID, ppv : *mut *mut core::ffi::c_void) -> HRESULT);
windows_targets::link!("ole32.dll" "system" fn CoInitializeEx(pvreserved : *const core::ffi::c_void, dwcoinit : u32) -> HRESULT);
windows_targets::link!("oleaut32.dll" "system" fn SysFreeString(bstrstring : BSTR));
windows_targets::link!("oleaut32.dll" "system" 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 = Option<unsafe extern "system" fn() -> isize>;
#[repr(C)]
#[derive(Clone, Copy)]
pub struct FILETIME {
pub dwLowDateTime: u32,
pub dwHighDateTime: u32,
}
pub const FILE_ATTRIBUTE_TEMPORARY: FILE_FLAGS_AND_ATTRIBUTES = 256u32;
pub type FILE_FLAGS_AND_ATTRIBUTES = u32;
#[repr(C)]
#[derive(Clone, Copy)]
pub struct GUID {
pub data1: u32,
pub data2: u16,
pub data3: u16,
pub data4: [u8; 8],
}
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)]
#[derive(Clone, Copy)]
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],
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct SAFEARRAYBOUND {
pub cElements: u32,
pub lLbound: i32,
}
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;
#[link(name = "advapi32")]
#[link(name = "ole32")]
#[link(name = "oleaut32")]
extern "C" {}
use super::windows_targets;

View file

@ -0,0 +1,19 @@
//! Provides the `link!` macro used by the generated windows bindings.
//!
//! This is a simple wrapper around an `extern` block with a `#[link]` attribute.
//! It's very roughly equivalent to the windows-targets crate.
macro_rules! link_macro {
($library:literal $abi:literal $($link_name:literal)? $(#[$doc:meta])? fn $($function:tt)*) => (
// Note: the windows-targets crate uses a pre-built Windows.lib import library which we don't
// have in this repo. So instead we always link kernel32.lib and add the rest of the import
// libraries below by using an empty extern block. This works because extern blocks are not
// connected to the library given in the #[link] attribute.
#[link(name = "kernel32")]
extern $abi {
$(#[link_name=$link_name])?
pub fn $($function)*;
}
)
}
pub(crate) use link_macro as link;