Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/third_party/rust/cc/src/   (Firefox Browser Version 153.0.1©)  Datei vom 27.6.2026 mit Größe 15 kB image not shown  

Quelle  command_helpers.rs

  Sprache: Rust
 

//! Miscellaneous helpers for running commands

use std::{
    borrow::Cow,
    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(cratestruct 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(crateenum 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(cratefn 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.is_empty(),
                None => false,
            },
            checked_dbg_var: Arc::new(AtomicBool::new(false)),
        }
    }

    pub(cratefn print_metadata(&self, s: &dyn Display) {
        if self.metadata {
            println!("{s}");
        }
    }

    pub(cratefn print_warning(&self, arg: &>dyn Display) {
        if self.warnings {
            println!("cargo:warning={arg}");
        }
    }

    pub(cratefn 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(cratestruct 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(cratefn 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(cratefn 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,
    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 `{cmd:?}`: {e}"),
            ));
        }
    };

    cargo_output.print_debug(&status);

    if status.success() {
        Ok(())
    } else {
        Err(Error::new(
            ErrorKind::ToolExecError,
            format!("command did not execute successfully (status code {status}): {cmd:?}"),
        ))
    }
}

/// Find the destination object path for each file in the input source files,
/// and store them in the output Object.
pub(cratefn 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();

        // Make the dirname relative (if possible) to avoid full system paths influencing the sha
        // and making the output system-dependent
        //
        // NOTE: Here we allow using std::env::var (instead of Build::getenv) because
        // CARGO_* variables always trigger a rebuild when changed
        #[allow(clippy::disallowed_methods)]
        let dirname = if let Some(root) = std::env::var_os("CARGO_MANIFEST_DIR") {
            let root = root.to_string_lossy();
            Cow::Borrowed(dirname.strip_prefix(&*root).unwrap_or(&dirname))
        } else {
            dirname
        };

        hasher.write(dirname.as_bytes());
        if let Some(extension) = file.extension() {
            hasher.write(extension.to_string_lossy().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(cratefn run(cmd: &mut Command, cargo_output: &CargoOutput) -> Result<(), Error> {
    let mut child = spawn(cmd, cargo_output)?;
    wait_on_child(cmd, &mut child, cargo_output)
}

pub(cratefn run_output(cmd: &mut Command, cargo_output: &CargoOutput) -> Result<Vec<u8>, Error> {
    // 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, &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, &mut child, cargo_output)?;

    Ok(stdout)
}

pub(cratefn spawn(cmd: &mut Command, 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 {:?}: {e}{extra}", cmd.0.get_program()),
            ))
        }
        Err(e) => Err(Error::new(
            ErrorKind::ToolExecError,
            format!("command `{:?}` failed to start: {e}", cmd.0),
        )),
    }
}

pub(cratestruct 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(cratefn command_add_output_file(cmd: &mut Command, dst: &Path,&nbsp;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(cratefn try_wait_on_child(
    cmd: &Command,
    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 did not execute successfully (status code {status}): {cmd:?}"),
                ))
            }
        }
        Ok(None) => Ok(None),
        Err(e) => {
            stderr_forwarder.forward_all();
            Err(Error::new(
                ErrorKind::ToolExecError,
                format!("failed to wait on spawned child process `{cmd:?}`: {e}"),
            ))
        }
    }
}

Messung V0.5 in Prozent
C=84 H=97 G=90

¤ Dauer der Verarbeitung: 0.7 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.