<commit_before>use crate::{Diagnostic, DiagnosticId, DiagnosticStyledString};
use crate::{Handler, Level, StashKey};
use rustc_lint_defs::Applicability;

use rustc_span::{MultiSpan, Span};
use std::fmt::{self, Debug};
use std::ops::{Deref, DerefMut};
use std::thread::panicking;
use tracing::debug;

/// Used for emitting structured error messages and other diagnostic information.
///
/// If there is some state in a downstream crate you would like to
/// access in the methods of `DiagnosticBuilder` here, consider
/// extending `HandlerFlags`, accessed via `self.handler.flags`.
#[must_use]
#[derive(Clone)]
pub struct DiagnosticBuilder<'a>(Box<DiagnosticBuilderInner<'a>>);

/// This is a large type, and often used as a return value, especially within
/// the frequently-used `PResult` type. In theory, return value optimization
/// (RVO) should avoid unnecessary copying. In practice, it does not (at the
/// time of writing). The split between `DiagnosticBuilder` and
/// `DiagnosticBuilderInner` exists to avoid many `memcpy` calls.
#[must_use]
#[derive(Clone)]
struct DiagnosticBuilderInner<'a> {
    handler: &'a Handler,
    diagnostic: Diagnostic,
    allow_suggestions: bool,
}

/// In general, the `DiagnosticBuilder` uses deref to allow access to
/// the fields and methods of the embedded `diagnostic` in a
/// transparent way. *However,* many of the methods are intended to
/// be used in a chained way, and hence ought to return `self`. In
/// that case, we can't just naively forward to the method on the
/// `diagnostic`, because the return type would be a `&Diagnostic`
/// instead of a `&DiagnosticBuilder<'a>`. This `forward!` macro makes
/// it easy to declare such methods on the builder.
macro_rules! forward {
    // Forward pattern for &self -> &Self
    (
        $(#[$attrs:meta])*
        pub fn $n:ident(&self, $($name:ident: $ty:ty),* $(,)?) -> &Self
    ) => {
        $(#[$attrs])*
        pub fn $n(&self, $($name: $ty),*) -> &Self {
            self.diagnostic.$n($($name),*);
            self
        }
    };

    // Forward pattern for &mut self -> &mut Self
    (
        $(#[$attrs:meta])*
        pub fn $n:ident(&mut self, $($name:ident: $ty:ty),* $(,)?) -> &mut Self
    ) => {
        $(#[$attrs])*
        pub fn $n(&mut self, $($name: $ty),*) -> &mut Self {
            self.0.diagnostic.$n($($name),*);
            self
        }
    };

    // Forward pattern for &mut self -> &mut Self, with S: Into<MultiSpan>
    // type parameter. No obvious way to make this more generic.
    (
        $(#[$attrs:meta])*
        pub fn $n:ident<S: Into<MultiSpan>>(
            &mut self,
            $($name:ident: $ty:ty),*
            $(,)?
        ) -> &mut Self
    ) => {
        $(#[$attrs])*
        pub fn $n<S: Into<MultiSpan>>(&mut self, $($name: $ty),*) -> &mut Self {
            self.0.diagnostic.$n($($name),*);
            self
        }
    };
}

impl<'a> Deref for DiagnosticBuilder<'a> {
    type Target = Diagnostic;

    fn deref(&self) -> &Diagnostic {
        &self.0.diagnostic
    }
}

impl<'a> DerefMut for DiagnosticBuilder<'a> {
    fn deref_mut(&mut self) -> &mut Diagnostic {
        &mut self.0.diagnostic
    }
}

impl<'a> DiagnosticBuilder<'a> {
    /// Emit the diagnostic.
    pub fn emit(&mut self) {
        self.0.handler.emit_diagnostic(&self);
        self.cancel();
    }

    /// Emit the diagnostic unless `delay` is true,
    /// in which case the emission will be delayed as a bug.
    ///
    /// See `emit` and `delay_as_bug` for details.
    pub fn emit_unless(&mut self, delay: bool) {
        if delay {
            self.delay_as_bug();
        } else {
            self.emit();
        }
    }

    /// Stashes diagnostic for possible later improvement in a different,
    /// later stage of the compiler. The diagnostic can be accessed with
    /// the provided `span` and `key` through `.steal_diagnostic` on `Handler`.
    ///
    /// As with `buffer`, this is unless the handler has disabled such buffering.
    pub fn stash(self, span: Span, key: StashKey) {
        if let Some((diag, handler)) = self.into_diagnostic() {
            handler.stash_diagnostic(span, key, diag);
        }
    }

    /// Converts the builder to a `Diagnostic` for later emission,
    /// unless handler has disabled such buffering.
    pub fn into_diagnostic(mut self) -> Option<(Diagnostic, &'a Handler)> {
        if self.0.handler.flags.dont_buffer_diagnostics
            || self.0.handler.flags.treat_err_as_bug.is_some()
        {
            self.emit();
            return None;
        }

        let handler = self.0.handler;

        // We must use `Level::Cancelled` for `dummy` to avoid an ICE about an
        // unused diagnostic.
        let dummy = Diagnostic::new(Level::Cancelled, "");
        let diagnostic = std::mem::replace(&mut self.0.diagnostic, dummy);

        // Logging here is useful to help track down where in logs an error was
        // actually emitted.
        debug!("buffer: diagnostic={:?}", diagnostic);

        Some((diagnostic, handler))
    }

    /// Buffers the diagnostic for later emission,
    /// unless handler has disabled such buffering.
    pub fn buffer(self, buffered_diagnostics: &mut Vec<Diagnostic>) {
        buffered_diagnostics.extend(self.into_diagnostic().map(|(diag, _)| diag));
    }

    /// Convenience function for internal use, clients should use one of the
    /// span_* methods instead.
    pub fn sub<S: Into<MultiSpan>>(
        &mut self,
        level: Level,
        message: &str,
        span: Option<S>,
    ) -> &mut Self {
        let span = span.map(|s| s.into()).unwrap_or_else(MultiSpan::new);
        self.0.diagnostic.sub(level, message, span, None);
        self
    }

    /// Delay emission of this diagnostic as a bug.
    ///
    /// This can be useful in contexts where an error indicates a bug but
    /// typically this only happens when other compilation errors have already
    /// happened. In those cases this can be used to defer emission of this
    /// diagnostic as a bug in the compiler only if no other errors have been
    /// emitted.
    ///
    /// In the meantime, though, callsites are required to deal with the "bug"
    /// locally in whichever way makes the most sense.
    pub fn delay_as_bug(&mut self) {
        self.level = Level::Bug;
        self.0.handler.delay_as_bug(self.0.diagnostic.clone());
        self.cancel();
    }

    /// Adds a span/label to be included in the resulting snippet.
    ///
    /// This is pushed onto the [`MultiSpan`] that was created when the diagnostic
    /// was first built. That means it will be shown together with the original
    /// span/label, *not* a span added by one of the `span_{note,warn,help,suggestions}` methods.
    ///
    /// This span is *not* considered a ["primary span"][`MultiSpan`]; only
    /// the `Span` supplied when creating the diagnostic is primary.
    ///
    /// [`MultiSpan`]: ../rustc_span/struct.MultiSpan.html
    pub fn span_label(&mut self, span: Span, label: impl Into<String>) -> &mut Self {
        self.0.diagnostic.span_label(span, label);
        self
    }

    /// Labels all the given spans with the provided label.
    /// See `span_label` for more information.
    pub fn span_labels(
        &mut self,
        spans: impl IntoIterator<Item = Span>,
        label: impl AsRef<str>,
    ) -> &mut Self {
        let label = label.as_ref();
        for span in spans {
            self.0.diagnostic.span_label(span, label);
        }
        self
    }

    forward!(pub fn note_expected_found(
        &mut self,
        expected_label: &dyn fmt::Display,
        expected: DiagnosticStyledString,
        found_label: &dyn fmt::Display,
        found: DiagnosticStyledString,
    ) -> &mut Self);

    forward!(pub fn note_expected_found_extra(
        &mut self,
        expected_label: &dyn fmt::Display,
        expected: DiagnosticStyledString,
        found_label: &dyn fmt::Display,
        found: DiagnosticStyledString,
        expected_extra: &dyn fmt::Display,
        found_extra: &dyn fmt::Display,
    ) -> &mut Self);

    forward!(pub fn note_unsuccessfull_coercion(
        &mut self,
        expected: DiagnosticStyledString,
        found: DiagnosticStyledString,
    ) -> &mut Self);

    forward!(pub fn note(&mut self, msg: &str) -> &mut Self);
    forward!(pub fn span_note<S: Into<MultiSpan>>(
        &mut self,
        sp: S,
        msg: &str,
    ) -> &mut Self);
    forward!(pub fn warn(&mut self, msg: &str) -> &mut Self);
    forward!(pub fn span_warn<S: Into<MultiSpan>>(&mut self, sp: S, msg: &str) -> &mut Self);
    forward!(pub fn help(&mut self, msg: &str) -> &mut Self);
    forward!(pub fn span_help<S: Into<MultiSpan>>(
        &mut self,
        sp: S,
        msg: &str,
    ) -> &mut Self);

    pub fn multipart_suggestion(
        &mut self,
        msg: &str,
        suggestion: Vec<(Span, String)>,
        applicability: Applicability,
    ) -> &mut Self {
        if !self.0.allow_suggestions {
            return self;
        }
        self.0.diagnostic.multipart_suggestion(msg, suggestion, applicability);
        self
    }

    pub fn multipart_suggestions(
        &mut self,
        msg: &str,
        suggestions: Vec<Vec<(Span, String)>>,
        applicability: Applicability,
    ) -> &mut Self {
        if !self.0.allow_suggestions {
            return self;
        }
        self.0.diagnostic.multipart_suggestions(msg, suggestions, applicability);
        self
    }

    pub fn tool_only_multipart_suggestion(
        &mut self,
        msg: &str,
        suggestion: Vec<(Span, String)>,
        applicability: Applicability,
    ) -> &mut Self {
        if !self.0.allow_suggestions {
            return self;
        }
        self.0.diagnostic.tool_only_multipart_suggestion(msg, suggestion, applicability);
        self
    }

    pub fn span_suggestion(
        &mut self,
        sp: Span,
        msg: &str,
        suggestion: String,
        applicability: Applicability,
    ) -> &mut Self {
        if !self.0.allow_suggestions {
            return self;
        }
        self.0.diagnostic.span_suggestion(sp, msg, suggestion, applicability);
        self
    }

    pub fn span_suggestions(
        &mut self,
        sp: Span,
        msg: &str,
        suggestions: impl Iterator<Item = String>,
        applicability: Applicability,
    ) -> &mut Self {
        if !self.0.allow_suggestions {
            return self;
        }
        self.0.diagnostic.span_suggestions(sp, msg, suggestions, applicability);
        self
    }

    pub fn span_suggestion_short(
        &mut self,
        sp: Span,
        msg: &str,
        suggestion: String,
        applicability: Applicability,
    ) -> &mut Self {
        if !self.0.allow_suggestions {
            return self;
        }
        self.0.diagnostic.span_suggestion_short(sp, msg, suggestion, applicability);
        self
    }

    pub fn span_suggestion_verbose(
        &mut self,
        sp: Span,
        msg: &str,
        suggestion: String,
        applicability: Applicability,
    ) -> &mut Self {
        if !self.0.allow_suggestions {
            return self;
        }
        self.0.diagnostic.span_suggestion_verbose(sp, msg, suggestion, applicability);
        self
    }

    pub fn span_suggestion_hidden(
        &mut self,
        sp: Span,
        msg: &str,
        suggestion: String,
        applicability: Applicability,
    ) -> &mut Self {
        if !self.0.allow_suggestions {
            return self;
        }
        self.0.diagnostic.span_suggestion_hidden(sp, msg, suggestion, applicability);
        self
    }

    pub fn tool_only_span_suggestion(
        &mut self,
        sp: Span,
        msg: &str,
        suggestion: String,
        applicability: Applicability,
    ) -> &mut Self {
        if !self.0.allow_suggestions {
            return self;
        }
        self.0.diagnostic.tool_only_span_suggestion(sp, msg, suggestion, applicability);
        self
    }

    forward!(pub fn set_span<S: Into<MultiSpan>>(&mut self, sp: S) -> &mut Self);
    forward!(pub fn code(&mut self, s: DiagnosticId) -> &mut Self);

    pub fn allow_suggestions(&mut self, allow: bool) -> &mut Self {
        self.0.allow_suggestions = allow;
        self
    }

    /// Convenience function for internal use, clients should use one of the
    /// struct_* methods on Handler.
    crate fn new(handler: &'a Handler, level: Level, message: &str) -> DiagnosticBuilder<'a> {
        DiagnosticBuilder::new_with_code(handler, level, None, message)
    }

    /// Convenience function for internal use, clients should use one of the
    /// struct_* methods on Handler.
    crate fn new_with_code(
        handler: &'a Handler,
        level: Level,
        code: Option<DiagnosticId>,
        message: &str,
    ) -> DiagnosticBuilder<'a> {
        let diagnostic = Diagnostic::new_with_code(level, code, message);
        DiagnosticBuilder::new_diagnostic(handler, diagnostic)
    }

    /// Creates a new `DiagnosticBuilder` with an already constructed
    /// diagnostic.
    crate fn new_diagnostic(handler: &'a Handler, diagnostic: Diagnostic) -> DiagnosticBuilder<'a> {
        debug!("Created new diagnostic");
        DiagnosticBuilder(Box::new(DiagnosticBuilderInner {
            handler,
            diagnostic,
            allow_suggestions: true,
        }))
    }
}

impl<'a> Debug for DiagnosticBuilder<'a> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.0.diagnostic.fmt(f)
    }
}

/// Destructor bomb - a `DiagnosticBuilder` must be either emitted or canceled
/// or we emit a bug.
impl<'a> Drop for DiagnosticBuilder<'a> {
    fn drop(&mut self) {
        if !panicking() && !self.cancelled() {
            let mut db = DiagnosticBuilder::new(
                self.0.handler,
                Level::Bug,
                "the following error was constructed but not emitted",
            );
            db.emit();
            self.emit();
            panic!();
        }
    }
}

#[macro_export]
macro_rules! struct_span_err {
    ($session:expr, $span:expr, $code:ident, $($message:tt)*) => ({
        $session.struct_span_err_with_code(
            $span,
            &format!($($message)*),
            $crate::error_code!($code),
        )
    })
}

#[macro_export]
macro_rules! error_code {
    ($code:ident) => {{ $crate::DiagnosticId::Error(stringify!($code).to_owned()) }};
}
<commit_msg>Rollup merge of #79088 - euclio:span-label-doc, r=estebank<commit_after>use crate::{Diagnostic, DiagnosticId, DiagnosticStyledString};
use crate::{Handler, Level, StashKey};
use rustc_lint_defs::Applicability;

use rustc_span::{MultiSpan, Span};
use std::fmt::{self, Debug};
use std::ops::{Deref, DerefMut};
use std::thread::panicking;
use tracing::debug;

/// Used for emitting structured error messages and other diagnostic information.
///
/// If there is some state in a downstream crate you would like to
/// access in the methods of `DiagnosticBuilder` here, consider
/// extending `HandlerFlags`, accessed via `self.handler.flags`.
#[must_use]
#[derive(Clone)]
pub struct DiagnosticBuilder<'a>(Box<DiagnosticBuilderInner<'a>>);

/// This is a large type, and often used as a return value, especially within
/// the frequently-used `PResult` type. In theory, return value optimization
/// (RVO) should avoid unnecessary copying. In practice, it does not (at the
/// time of writing). The split between `DiagnosticBuilder` and
/// `DiagnosticBuilderInner` exists to avoid many `memcpy` calls.
#[must_use]
#[derive(Clone)]
struct DiagnosticBuilderInner<'a> {
    handler: &'a Handler,
    diagnostic: Diagnostic,
    allow_suggestions: bool,
}

/// In general, the `DiagnosticBuilder` uses deref to allow access to
/// the fields and methods of the embedded `diagnostic` in a
/// transparent way. *However,* many of the methods are intended to
/// be used in a chained way, and hence ought to return `self`. In
/// that case, we can't just naively forward to the method on the
/// `diagnostic`, because the return type would be a `&Diagnostic`
/// instead of a `&DiagnosticBuilder<'a>`. This `forward!` macro makes
/// it easy to declare such methods on the builder.
macro_rules! forward {
    // Forward pattern for &self -> &Self
    (
        $(#[$attrs:meta])*
        pub fn $n:ident(&self, $($name:ident: $ty:ty),* $(,)?) -> &Self
    ) => {
        $(#[$attrs])*
        pub fn $n(&self, $($name: $ty),*) -> &Self {
            self.diagnostic.$n($($name),*);
            self
        }
    };

    // Forward pattern for &mut self -> &mut Self
    (
        $(#[$attrs:meta])*
        pub fn $n:ident(&mut self, $($name:ident: $ty:ty),* $(,)?) -> &mut Self
    ) => {
        $(#[$attrs])*
        pub fn $n(&mut self, $($name: $ty),*) -> &mut Self {
            self.0.diagnostic.$n($($name),*);
            self
        }
    };

    // Forward pattern for &mut self -> &mut Self, with S: Into<MultiSpan>
    // type parameter. No obvious way to make this more generic.
    (
        $(#[$attrs:meta])*
        pub fn $n:ident<S: Into<MultiSpan>>(
            &mut self,
            $($name:ident: $ty:ty),*
            $(,)?
        ) -> &mut Self
    ) => {
        $(#[$attrs])*
        pub fn $n<S: Into<MultiSpan>>(&mut self, $($name: $ty),*) -> &mut Self {
            self.0.diagnostic.$n($($name),*);
            self
        }
    };
}

impl<'a> Deref for DiagnosticBuilder<'a> {
    type Target = Diagnostic;

    fn deref(&self) -> &Diagnostic {
        &self.0.diagnostic
    }
}

impl<'a> DerefMut for DiagnosticBuilder<'a> {
    fn deref_mut(&mut self) -> &mut Diagnostic {
        &mut self.0.diagnostic
    }
}

impl<'a> DiagnosticBuilder<'a> {
    /// Emit the diagnostic.
    pub fn emit(&mut self) {
        self.0.handler.emit_diagnostic(&self);
        self.cancel();
    }

    /// Emit the diagnostic unless `delay` is true,
    /// in which case the emission will be delayed as a bug.
    ///
    /// See `emit` and `delay_as_bug` for details.
    pub fn emit_unless(&mut self, delay: bool) {
        if delay {
            self.delay_as_bug();
        } else {
            self.emit();
        }
    }

    /// Stashes diagnostic for possible later improvement in a different,
    /// later stage of the compiler. The diagnostic can be accessed with
    /// the provided `span` and `key` through `.steal_diagnostic` on `Handler`.
    ///
    /// As with `buffer`, this is unless the handler has disabled such buffering.
    pub fn stash(self, span: Span, key: StashKey) {
        if let Some((diag, handler)) = self.into_diagnostic() {
            handler.stash_diagnostic(span, key, diag);
        }
    }

    /// Converts the builder to a `Diagnostic` for later emission,
    /// unless handler has disabled such buffering.
    pub fn into_diagnostic(mut self) -> Option<(Diagnostic, &'a Handler)> {
        if self.0.handler.flags.dont_buffer_diagnostics
            || self.0.handler.flags.treat_err_as_bug.is_some()
        {
            self.emit();
            return None;
        }

        let handler = self.0.handler;

        // We must use `Level::Cancelled` for `dummy` to avoid an ICE about an
        // unused diagnostic.
        let dummy = Diagnostic::new(Level::Cancelled, "");
        let diagnostic = std::mem::replace(&mut self.0.diagnostic, dummy);

        // Logging here is useful to help track down where in logs an error was
        // actually emitted.
        debug!("buffer: diagnostic={:?}", diagnostic);

        Some((diagnostic, handler))
    }

    /// Buffers the diagnostic for later emission,
    /// unless handler has disabled such buffering.
    pub fn buffer(self, buffered_diagnostics: &mut Vec<Diagnostic>) {
        buffered_diagnostics.extend(self.into_diagnostic().map(|(diag, _)| diag));
    }

    /// Convenience function for internal use, clients should use one of the
    /// span_* methods instead.
    pub fn sub<S: Into<MultiSpan>>(
        &mut self,
        level: Level,
        message: &str,
        span: Option<S>,
    ) -> &mut Self {
        let span = span.map(|s| s.into()).unwrap_or_else(MultiSpan::new);
        self.0.diagnostic.sub(level, message, span, None);
        self
    }

    /// Delay emission of this diagnostic as a bug.
    ///
    /// This can be useful in contexts where an error indicates a bug but
    /// typically this only happens when other compilation errors have already
    /// happened. In those cases this can be used to defer emission of this
    /// diagnostic as a bug in the compiler only if no other errors have been
    /// emitted.
    ///
    /// In the meantime, though, callsites are required to deal with the "bug"
    /// locally in whichever way makes the most sense.
    pub fn delay_as_bug(&mut self) {
        self.level = Level::Bug;
        self.0.handler.delay_as_bug(self.0.diagnostic.clone());
        self.cancel();
    }

    /// Appends a labeled span to the diagnostic.
    ///
    /// Labels are used to convey additional context for the diagnostic's primary span. They will
    /// be shown together with the original diagnostic's span, *not* with spans added by
    /// `span_note`, `span_help`, etc. Therefore, if the primary span is not displayable (because
    /// the span is `DUMMY_SP` or the source code isn't found), labels will not be displayed
    /// either.
    ///
    /// Implementation-wise, the label span is pushed onto the [`MultiSpan`] that was created when
    /// the diagnostic was constructed. However, the label span is *not* considered a
    /// ["primary span"][`MultiSpan`]; only the `Span` supplied when creating the diagnostic is
    /// primary.
    pub fn span_label(&mut self, span: Span, label: impl Into<String>) -> &mut Self {
        self.0.diagnostic.span_label(span, label);
        self
    }

    /// Labels all the given spans with the provided label.
    /// See `span_label` for more information.
    pub fn span_labels(
        &mut self,
        spans: impl IntoIterator<Item = Span>,
        label: impl AsRef<str>,
    ) -> &mut Self {
        let label = label.as_ref();
        for span in spans {
            self.0.diagnostic.span_label(span, label);
        }
        self
    }

    forward!(pub fn note_expected_found(
        &mut self,
        expected_label: &dyn fmt::Display,
        expected: DiagnosticStyledString,
        found_label: &dyn fmt::Display,
        found: DiagnosticStyledString,
    ) -> &mut Self);

    forward!(pub fn note_expected_found_extra(
        &mut self,
        expected_label: &dyn fmt::Display,
        expected: DiagnosticStyledString,
        found_label: &dyn fmt::Display,
        found: DiagnosticStyledString,
        expected_extra: &dyn fmt::Display,
        found_extra: &dyn fmt::Display,
    ) -> &mut Self);

    forward!(pub fn note_unsuccessfull_coercion(
        &mut self,
        expected: DiagnosticStyledString,
        found: DiagnosticStyledString,
    ) -> &mut Self);

    forward!(pub fn note(&mut self, msg: &str) -> &mut Self);
    forward!(pub fn span_note<S: Into<MultiSpan>>(
        &mut self,
        sp: S,
        msg: &str,
    ) -> &mut Self);
    forward!(pub fn warn(&mut self, msg: &str) -> &mut Self);
    forward!(pub fn span_warn<S: Into<MultiSpan>>(&mut self, sp: S, msg: &str) -> &mut Self);
    forward!(pub fn help(&mut self, msg: &str) -> &mut Self);
    forward!(pub fn span_help<S: Into<MultiSpan>>(
        &mut self,
        sp: S,
        msg: &str,
    ) -> &mut Self);

    pub fn multipart_suggestion(
        &mut self,
        msg: &str,
        suggestion: Vec<(Span, String)>,
        applicability: Applicability,
    ) -> &mut Self {
        if !self.0.allow_suggestions {
            return self;
        }
        self.0.diagnostic.multipart_suggestion(msg, suggestion, applicability);
        self
    }

    pub fn multipart_suggestions(
        &mut self,
        msg: &str,
        suggestions: Vec<Vec<(Span, String)>>,
        applicability: Applicability,
    ) -> &mut Self {
        if !self.0.allow_suggestions {
            return self;
        }
        self.0.diagnostic.multipart_suggestions(msg, suggestions, applicability);
        self
    }

    pub fn tool_only_multipart_suggestion(
        &mut self,
        msg: &str,
        suggestion: Vec<(Span, String)>,
        applicability: Applicability,
    ) -> &mut Self {
        if !self.0.allow_suggestions {
            return self;
        }
        self.0.diagnostic.tool_only_multipart_suggestion(msg, suggestion, applicability);
        self
    }

    pub fn span_suggestion(
        &mut self,
        sp: Span,
        msg: &str,
        suggestion: String,
        applicability: Applicability,
    ) -> &mut Self {
        if !self.0.allow_suggestions {
            return self;
        }
        self.0.diagnostic.span_suggestion(sp, msg, suggestion, applicability);
        self
    }

    pub fn span_suggestions(
        &mut self,
        sp: Span,
        msg: &str,
        suggestions: impl Iterator<Item = String>,
        applicability: Applicability,
    ) -> &mut Self {
        if !self.0.allow_suggestions {
            return self;
        }
        self.0.diagnostic.span_suggestions(sp, msg, suggestions, applicability);
        self
    }

    pub fn span_suggestion_short(
        &mut self,
        sp: Span,
        msg: &str,
        suggestion: String,
        applicability: Applicability,
    ) -> &mut Self {
        if !self.0.allow_suggestions {
            return self;
        }
        self.0.diagnostic.span_suggestion_short(sp, msg, suggestion, applicability);
        self
    }

    pub fn span_suggestion_verbose(
        &mut self,
        sp: Span,
        msg: &str,
        suggestion: String,
        applicability: Applicability,
    ) -> &mut Self {
        if !self.0.allow_suggestions {
            return self;
        }
        self.0.diagnostic.span_suggestion_verbose(sp, msg, suggestion, applicability);
        self
    }

    pub fn span_suggestion_hidden(
        &mut self,
        sp: Span,
        msg: &str,
        suggestion: String,
        applicability: Applicability,
    ) -> &mut Self {
        if !self.0.allow_suggestions {
            return self;
        }
        self.0.diagnostic.span_suggestion_hidden(sp, msg, suggestion, applicability);
        self
    }

    pub fn tool_only_span_suggestion(
        &mut self,
        sp: Span,
        msg: &str,
        suggestion: String,
        applicability: Applicability,
    ) -> &mut Self {
        if !self.0.allow_suggestions {
            return self;
        }
        self.0.diagnostic.tool_only_span_suggestion(sp, msg, suggestion, applicability);
        self
    }

    forward!(pub fn set_span<S: Into<MultiSpan>>(&mut self, sp: S) -> &mut Self);
    forward!(pub fn code(&mut self, s: DiagnosticId) -> &mut Self);

    pub fn allow_suggestions(&mut self, allow: bool) -> &mut Self {
        self.0.allow_suggestions = allow;
        self
    }

    /// Convenience function for internal use, clients should use one of the
    /// struct_* methods on Handler.
    crate fn new(handler: &'a Handler, level: Level, message: &str) -> DiagnosticBuilder<'a> {
        DiagnosticBuilder::new_with_code(handler, level, None, message)
    }

    /// Convenience function for internal use, clients should use one of the
    /// struct_* methods on Handler.
    crate fn new_with_code(
        handler: &'a Handler,
        level: Level,
        code: Option<DiagnosticId>,
        message: &str,
    ) -> DiagnosticBuilder<'a> {
        let diagnostic = Diagnostic::new_with_code(level, code, message);
        DiagnosticBuilder::new_diagnostic(handler, diagnostic)
    }

    /// Creates a new `DiagnosticBuilder` with an already constructed
    /// diagnostic.
    crate fn new_diagnostic(handler: &'a Handler, diagnostic: Diagnostic) -> DiagnosticBuilder<'a> {
        debug!("Created new diagnostic");
        DiagnosticBuilder(Box::new(DiagnosticBuilderInner {
            handler,
            diagnostic,
            allow_suggestions: true,
        }))
    }
}

impl<'a> Debug for DiagnosticBuilder<'a> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.0.diagnostic.fmt(f)
    }
}

/// Destructor bomb - a `DiagnosticBuilder` must be either emitted or canceled
/// or we emit a bug.
impl<'a> Drop for DiagnosticBuilder<'a> {
    fn drop(&mut self) {
        if !panicking() && !self.cancelled() {
            let mut db = DiagnosticBuilder::new(
                self.0.handler,
                Level::Bug,
                "the following error was constructed but not emitted",
            );
            db.emit();
            self.emit();
            panic!();
        }
    }
}

#[macro_export]
macro_rules! struct_span_err {
    ($session:expr, $span:expr, $code:ident, $($message:tt)*) => ({
        $session.struct_span_err_with_code(
            $span,
            &format!($($message)*),
            $crate::error_code!($code),
        )
    })
}

#[macro_export]
macro_rules! error_code {
    ($code:ident) => {{ $crate::DiagnosticId::Error(stringify!($code).to_owned()) }};
}
<|endoftext|>


<commit_before>// Copyright 2018 Google LLC
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.

/// - The PubSub server sets up TCP listeners on 2 ports, the "subscriber" port and the "publisher"
///   port. Because both publishers and subscribers initiate their connections to the PubSub
///   server, the server requires no prior knowledge of either publishers or subscribers.
///
/// - Subscribers connect to the server on the server's "subscriber" port. Once the connection is
///   established, the server acts as the client, sending messages to the subscribers via a
///   Subscriber service.
///
/// - Publishers connect to the server on the "publisher"port, and once connected, they send
///   messages to the server to be broadcast via a Publisher service.
///
///       Subscriber                        Publisher                       PubSub Server
/// T1        |                                 |                                 |
/// T2        |-----Connect------------------------------------------------------>|
/// T3        |                                 |                                 |
/// T4        |                                 |-----Connect-------------------->|
/// T5        |                                 |                                 |
/// T6        |                                 |-----Publish-------------------->|
/// T7        |                                 |                                 |
/// T8        |<------------------------------------------------------Receive-----|
/// T9        |-----(Receive OK)------------------------------------------------->|
/// T10       |                                 |                                 |
/// T11       |                                 |<--------------(Publish OK)------|

use futures::{
    future::{self, AbortHandle},
    prelude::*,
};
use log::info;
use publisher::Publisher as _;
use std::{
    collections::HashMap,
    io,
    net::SocketAddr,
    sync::{Arc, Mutex},
};
use subscriber::Subscriber as _;
use tarpc::{
    client, context,
    serde_transport::tcp,
    server::{self, Channel},
};
use tokio::net::ToSocketAddrs;
use tokio_serde::formats::Json;

pub mod subscriber {
    #[tarpc::service]
    pub trait Subscriber {
        async fn receive(message: String);
    }
}

pub mod publisher {
    #[tarpc::service]
    pub trait Publisher {
        async fn broadcast(message: String);
    }
}

#[derive(Clone, Debug)]
struct Subscriber {
    local_addr: SocketAddr,
}

#[tarpc::server]
impl subscriber::Subscriber for Subscriber {
    async fn receive(self, _: context::Context, message: String) {
        info!("[{}] received message: {}", self.local_addr, message);
    }
}

struct SubscriberHandle(AbortHandle);

impl Drop for SubscriberHandle {
    fn drop(&mut self) {
        self.0.abort();
    }
}

impl Subscriber {
    async fn connect(publisher_addr: impl ToSocketAddrs) -> io::Result<SubscriberHandle> {
        let publisher = tcp::connect(publisher_addr, Json::default()).await?;
        let local_addr = publisher.local_addr()?;
        let (handler, abort_handle) = future::abortable(
            server::BaseChannel::with_defaults(publisher)
                .respond_with(Subscriber { local_addr }.serve())
                .execute(),
        );
        tokio::spawn(async move {
            match handler.await {
                Ok(()) | Err(future::Aborted) => info!("[{}] subscriber shutdown.", local_addr),
            }
        });
        Ok(SubscriberHandle(abort_handle))
    }
}

#[derive(Clone, Debug)]
struct Publisher {
    clients: Arc<Mutex<HashMap<SocketAddr, subscriber::SubscriberClient>>>,
}

struct PublisherAddrs {
    publisher: SocketAddr,
    subscriptions: SocketAddr,
}

impl Publisher {
    async fn start(self) -> io::Result<PublisherAddrs> {
        let mut connecting_publishers = tcp::listen("localhost:0", Json::default).await?;

        let publisher_addrs = PublisherAddrs {
            publisher: connecting_publishers.local_addr(),
            subscriptions: Self::start_subscription_manager(self.clients.clone()).await?,
        };

        info!("[{}] listening for publishers.", publisher_addrs.publisher);
        tokio::spawn(async move {
            // Because this is just an example, we know there will only be one publisher. In more
            // realistic code, this would be a loop to continually accept new publisher
            // connections.
            let publisher = connecting_publishers.next().await.unwrap().unwrap();
            info!("[{}] publisher connected.", publisher.peer_addr().unwrap());

            server::BaseChannel::with_defaults(publisher)
                .respond_with(self.serve())
                .execute()
                .await
        });

        Ok(publisher_addrs)
    }

    async fn start_subscription_manager(
        clients: Arc<Mutex<HashMap<SocketAddr, subscriber::SubscriberClient>>>,
    ) -> io::Result<SocketAddr> {
        let mut connecting_subscribers = tcp::listen("localhost:0", Json::default)
            .await?
            .filter_map(|r| future::ready(r.ok()));
        let new_subscriber_addr = connecting_subscribers.get_ref().local_addr();
        info!("[{}] listening for subscribers.", new_subscriber_addr);

        tokio::spawn(async move {
            while let Some(conn) = connecting_subscribers.next().await {
                let subscriber_addr = conn.peer_addr().unwrap();
                info!("[{}] subscriber connected.", subscriber_addr);

                let tarpc::client::NewClient {
                    client: subscriber,
                    dispatch,
                } = subscriber::SubscriberClient::new(client::Config::default(), conn);
                clients.lock().unwrap().insert(subscriber_addr, subscriber);

                let dropped_clients = clients.clone();
                tokio::spawn(async move {
                    match dispatch.await {
                        Ok(()) => info!("[{:?}] subscriber connection closed", subscriber_addr),
                        Err(e) => info!(
                            "[{:?}] subscriber connection broken: {:?}",
                            subscriber_addr, e
                        ),
                    }
                    dropped_clients.lock().unwrap().remove(&subscriber_addr);
                });
            }
        });

        Ok(new_subscriber_addr)
    }
}

#[tarpc::server]
impl publisher::Publisher for Publisher {
    async fn broadcast(self, _: context::Context, message: String) {
        info!("received message to broadcast.");
        let mut clients = self.clients.lock().unwrap().clone();
        let mut publications = Vec::new();
        for client in clients.values_mut() {
            publications.push(client.receive(context::current(), message.clone()));
        }
        // Ignore failing subscribers. In a real pubsub, you'd want to continually retry until
        // subscribers ack. Of course, a lot would be different in a real pubsub :)
        future::join_all(publications).await;
    }
}

#[tokio::main]
async fn main() -> io::Result<()> {
    env_logger::init();

    let clients = Arc::new(Mutex::new(HashMap::new()));
    let addrs = Publisher { clients }.start().await?;

    let mut publisher = publisher::PublisherClient::new(
        client::Config::default(),
        tcp::connect(addrs.publisher, Json::default()).await?,
    )
    .spawn()?;

    let _subscriber0 = Subscriber::connect(addrs.subscriptions).await?;
    publisher
        .broadcast(context::current(), "hello to one".to_string())
        .await?;

    let _subscriber1 = Subscriber::connect(addrs.subscriptions).await?;
    publisher
        .broadcast(context::current(), "hello to all".to_string())
        .await?;

    drop(_subscriber0);

    publisher
        .broadcast(context::current(), "hello to who?".to_string())
        .await?;

    info!("done.");

    Ok(())
}
<commit_msg>Add topics to PubSub example<commit_after>// Copyright 2018 Google LLC
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.

/// - The PubSub server sets up TCP listeners on 2 ports, the "subscriber" port and the "publisher"
///   port. Because both publishers and subscribers initiate their connections to the PubSub
///   server, the server requires no prior knowledge of either publishers or subscribers.
///
/// - Subscribers connect to the server on the server's "subscriber" port. Once the connection is
///   established, the server acts as the client, sending messages to the subscribers via a
///   Subscriber service.
///
/// - Publishers connect to the server on the "publisher"port, and once connected, they send
///   messages to the server to be publish via a Publisher service.
///
///       Subscriber                        Publisher                       PubSub Server
/// T1        |                                 |                                 |
/// T2        |-----Connect------------------------------------------------------>|
/// T3        |                                 |                                 |
/// T4        |                                 |-----Connect-------------------->|
/// T5        |                                 |                                 |
/// T6        |                                 |-----Publish-------------------->|
/// T7        |                                 |                                 |
/// T8        |<------------------------------------------------------Receive-----|
/// T9        |-----(Receive OK)------------------------------------------------->|
/// T10       |                                 |                                 |
/// T11       |                                 |<--------------(Publish OK)------|
use futures::{
    channel::oneshot,
    future::{self, AbortHandle},
    prelude::*,
};
use log::info;
use publisher::Publisher as _;
use std::{
    collections::HashMap,
    io,
    net::SocketAddr,
    sync::{Arc, Mutex, RwLock},
    time::Duration,
};
use subscriber::Subscriber as _;
use tarpc::{
    client, context,
    serde_transport::tcp,
    server::{self, Channel},
};
use tokio::net::ToSocketAddrs;
use tokio_serde::formats::Json;

pub mod subscriber {
    #[tarpc::service]
    pub trait Subscriber {
        async fn topics() -> Vec<String>;
        async fn receive(topic: String, message: String);
    }
}

pub mod publisher {

    #[tarpc::service]
    pub trait Publisher {
        async fn publish(topic: String, message: String);
    }
}

#[derive(Clone, Debug)]
struct Subscriber {
    local_addr: SocketAddr,
    topics: Vec<String>,
}

#[tarpc::server]
impl subscriber::Subscriber for Subscriber {
    async fn topics(self, _: context::Context) -> Vec<String> {
        self.topics.clone()
    }

    async fn receive(self, _: context::Context, topic: String, message: String) {
        info!(
            "[{}] received message on topic '{}': {}",
            self.local_addr, topic, message
        );
    }
}

struct SubscriberHandle(AbortHandle);

impl Drop for SubscriberHandle {
    fn drop(&mut self) {
        self.0.abort();
    }
}

impl Subscriber {
    async fn connect(
        publisher_addr: impl ToSocketAddrs,
        topics: Vec<String>,
    ) -> io::Result<SubscriberHandle> {
        let publisher = tcp::connect(publisher_addr, Json::default()).await?;
        let local_addr = publisher.local_addr()?;
        let (handler, abort_handle) = future::abortable(
            server::BaseChannel::with_defaults(publisher)
                .respond_with(Subscriber { local_addr, topics }.serve())
                .execute(),
        );
        tokio::spawn(async move {
            match handler.await {
                Ok(()) | Err(future::Aborted) => info!("[{}] subscriber shutdown.", local_addr),
            }
        });
        Ok(SubscriberHandle(abort_handle))
    }
}

#[derive(Debug)]
struct Subscription {
    subscriber: subscriber::SubscriberClient,
    topics: Vec<String>,
}

#[derive(Clone, Debug)]
struct Publisher {
    clients: Arc<Mutex<HashMap<SocketAddr, Subscription>>>,
    subscriptions: Arc<RwLock<HashMap<String, HashMap<SocketAddr, subscriber::SubscriberClient>>>>,
}

struct PublisherAddrs {
    publisher: SocketAddr,
    subscriptions: SocketAddr,
}

impl Publisher {
    async fn start(self) -> io::Result<PublisherAddrs> {
        let mut connecting_publishers = tcp::listen("localhost:0", Json::default).await?;

        let publisher_addrs = PublisherAddrs {
            publisher: connecting_publishers.local_addr(),
            subscriptions: self.clone().start_subscription_manager().await?,
        };

        info!("[{}] listening for publishers.", publisher_addrs.publisher);
        tokio::spawn(async move {
            // Because this is just an example, we know there will only be one publisher. In more
            // realistic code, this would be a loop to continually accept new publisher
            // connections.
            let publisher = connecting_publishers.next().await.unwrap().unwrap();
            info!("[{}] publisher connected.", publisher.peer_addr().unwrap());

            server::BaseChannel::with_defaults(publisher)
                .respond_with(self.serve())
                .execute()
                .await
        });

        Ok(publisher_addrs)
    }

    async fn start_subscription_manager(self) -> io::Result<SocketAddr> {
        let mut connecting_subscribers = tcp::listen("localhost:0", Json::default)
            .await?
            .filter_map(|r| future::ready(r.ok()));
        let new_subscriber_addr = connecting_subscribers.get_ref().local_addr();
        info!("[{}] listening for subscribers.", new_subscriber_addr);

        tokio::spawn(async move {
            while let Some(conn) = connecting_subscribers.next().await {
                let subscriber_addr = conn.peer_addr().unwrap();
                info!("[{}] subscriber connected.", subscriber_addr);

                let tarpc::client::NewClient {
                    client: mut subscriber,
                    dispatch,
                } = subscriber::SubscriberClient::new(client::Config::default(), conn);
                self.clients.lock().unwrap().insert(
                    subscriber_addr,
                    Subscription {
                        subscriber: subscriber.clone(),
                        topics: Vec::new(),
                    },
                );

                let (ready_tx, ready) = oneshot::channel();
                let me = self.clone();
                tokio::spawn(async move {
                    match dispatch.await {
                        Ok(()) => info!("[{:?}] subscriber connection closed", subscriber_addr),
                        Err(e) => info!(
                            "[{:?}] subscriber connection broken: {:?}",
                            subscriber_addr, e
                        ),
                    }
                    // Don't clean up the subscriber until initialization is done.
                    let _ = ready.await;
                    if let Some(subscription) = me.clients.lock().unwrap().remove(&subscriber_addr)
                    {
                        let mut subscriptions = me.subscriptions.write().unwrap();
                        for topic in subscription.topics {
                            let subscribers = subscriptions.get_mut(&topic).unwrap();
                            subscribers.remove(&subscriber_addr);
                            if subscribers.is_empty() {
                                subscriptions.remove(&topic);
                            }
                        }
                    }
                });

                // Populate the topics
                if let Ok(topics) = subscriber.topics(context::current()).await {
                    let mut subscriptions = self.subscriptions.write().unwrap();
                    for topic in topics {
                        subscriptions
                            .entry(topic)
                            .or_insert_with(HashMap::new)
                            .insert(subscriber_addr, subscriber.clone());
                    }
                }
                // Signal that initialization is done.
                ready_tx.send(()).unwrap();
            }
        });

        Ok(new_subscriber_addr)
    }
}

#[tarpc::server]
impl publisher::Publisher for Publisher {
    async fn publish(self, _: context::Context, topic: String, message: String) {
        info!("received message to publish.");
        let mut subscribers = match self.subscriptions.read().unwrap().get(&topic) {
            None => return,
            Some(subscriptions) => subscriptions.clone(),
        };
        tokio::spawn(async move {
            let mut publications = Vec::new();
            for client in subscribers.values_mut() {
                publications.push(client.receive(
                    context::current(),
                    topic.clone(),
                    message.clone(),
                ));
            }
            // Ignore failing subscribers. In a real pubsub, you'd want to continually retry until
            // subscribers ack. Of course, a lot would be different in a real pubsub :)
            future::join_all(publications).await;
        });
    }
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    env_logger::init();

    let clients = Arc::new(Mutex::new(HashMap::new()));
    let addrs = Publisher {
        clients,
        subscriptions: Arc::new(RwLock::new(HashMap::new())),
    }
    .start()
    .await?;

    let _subscriber0 = Subscriber::connect(
        addrs.subscriptions,
        vec!["calculus".into(), "cool shorts".into()],
    )
    .await?;

    let _subscriber1 = Subscriber::connect(
        addrs.subscriptions,
        vec!["cool shorts".into(), "history".into()],
    )
    .await?;

    let mut publisher = publisher::PublisherClient::new(
        client::Config::default(),
        tcp::connect(addrs.publisher, Json::default()).await?,
    )
    .spawn()?;

    // Wait a moment for subscribers to get set up.
    tokio::time::delay_for(Duration::from_millis(25)).await;

    publisher
        .publish(context::current(), "calculus".into(), "sqrt(2)".into())
        .await?;

    publisher
        .publish(
            context::current(),
            "cool shorts".into(),
            "hello to all".into(),
        )
        .await?;

    publisher
        .publish(context::current(), "history".into(), "napoleon".to_string())
        .await?;

    // Wait a moment for the last publication.
    tokio::time::delay_for(Duration::from_millis(25)).await;

    info!("done.");

    Ok(())
}
<|endoftext|>


<commit_before>use std::collections::{BTreeSet, HashMap, HashSet};
use std::env;
use std::ffi::OsStr;
use std::path::PathBuf;

use semver::Version;

use super::BuildContext;
use crate::core::{Edition, Package, PackageId, Target};
use crate::util::{self, join_paths, process, CargoResult, CfgExpr, Config, ProcessBuilder};

pub struct Doctest {
    /// The package being doc-tested.
    pub package: Package,
    /// The target being tested (currently always the package's lib).
    pub target: Target,
    /// Extern dependencies needed by `rustdoc`. The path is the location of
    /// the compiled lib.
    pub deps: Vec<(String, PathBuf)>,
}

/// A structure returning the result of a compilation.
pub struct Compilation<'cfg> {
    /// An array of all tests created during this compilation.
    pub tests: Vec<(Package, Target, PathBuf)>,

    /// An array of all binaries created.
    pub binaries: Vec<PathBuf>,

    /// All directories for the output of native build commands.
    ///
    /// This is currently used to drive some entries which are added to the
    /// LD_LIBRARY_PATH as appropriate.
    ///
    /// The order should be deterministic.
    pub native_dirs: BTreeSet<PathBuf>,

    /// Root output directory (for the local package's artifacts)
    pub root_output: PathBuf,

    /// Output directory for rust dependencies.
    /// May be for the host or for a specific target.
    pub deps_output: PathBuf,

    /// Output directory for the rust host dependencies.
    pub host_deps_output: PathBuf,

    /// The path to rustc's own libstd
    pub host_dylib_path: Option<PathBuf>,

    /// The path to libstd for the target
    pub target_dylib_path: Option<PathBuf>,

    /// Extra environment variables that were passed to compilations and should
    /// be passed to future invocations of programs.
    pub extra_env: HashMap<PackageId, Vec<(String, String)>>,

    /// Libraries to test with rustdoc.
    pub to_doc_test: Vec<Doctest>,

    /// Features per package enabled during this compilation.
    pub cfgs: HashMap<PackageId, HashSet<String>>,

    /// Flags to pass to rustdoc when invoked from cargo test, per package.
    pub rustdocflags: HashMap<PackageId, Vec<String>>,

    pub host: String,
    pub target: String,

    config: &'cfg Config,
    rustc_process: ProcessBuilder,

    target_runner: Option<(PathBuf, Vec<String>)>,
}

impl<'cfg> Compilation<'cfg> {
    pub fn new<'a>(bcx: &BuildContext<'a, 'cfg>) -> CargoResult<Compilation<'cfg>> {
        let mut rustc = bcx.rustc.process();

        if bcx.config.extra_verbose() {
            rustc.display_env_vars();
        }
        let srv = bcx.build_config.rustfix_diagnostic_server.borrow();
        if let Some(server) = &*srv {
            server.configure(&mut rustc);
        }
        Ok(Compilation {
            // TODO: deprecated; remove.
            native_dirs: BTreeSet::new(),
            root_output: PathBuf::from("/"),
            deps_output: PathBuf::from("/"),
            host_deps_output: PathBuf::from("/"),
            host_dylib_path: bcx.host_info.sysroot_libdir.clone(),
            target_dylib_path: bcx.target_info.sysroot_libdir.clone(),
            tests: Vec::new(),
            binaries: Vec::new(),
            extra_env: HashMap::new(),
            to_doc_test: Vec::new(),
            cfgs: HashMap::new(),
            rustdocflags: HashMap::new(),
            config: bcx.config,
            rustc_process: rustc,
            host: bcx.host_triple().to_string(),
            target: bcx.target_triple().to_string(),
            target_runner: target_runner(bcx)?,
        })
    }

    /// See `process`.
    pub fn rustc_process(&self, pkg: &Package, target: &Target) -> CargoResult<ProcessBuilder> {
        let mut p = self.fill_env(self.rustc_process.clone(), pkg, true)?;
        if target.edition() != Edition::Edition2015 {
            p.arg(format!("--edition={}", target.edition()));
        }
        Ok(p)
    }

    /// See `process`.
    pub fn rustdoc_process(&self, pkg: &Package, target: &Target) -> CargoResult<ProcessBuilder> {
        let mut p = self.fill_env(process(&*self.config.rustdoc()?), pkg, false)?;
        if target.edition() != Edition::Edition2015 {
            p.arg(format!("--edition={}", target.edition()));
        }
        Ok(p)
    }

    /// See `process`.
    pub fn host_process<T: AsRef<OsStr>>(
        &self,
        cmd: T,
        pkg: &Package,
    ) -> CargoResult<ProcessBuilder> {
        self.fill_env(process(cmd), pkg, true)
    }

    fn target_runner(&self) -> &Option<(PathBuf, Vec<String>)> {
        &self.target_runner
    }

    /// See `process`.
    pub fn target_process<T: AsRef<OsStr>>(
        &self,
        cmd: T,
        pkg: &Package,
    ) -> CargoResult<ProcessBuilder> {
        let builder = if let Some((ref runner, ref args)) = *self.target_runner() {
            let mut builder = process(runner);
            builder.args(args);
            builder.arg(cmd);
            builder
        } else {
            process(cmd)
        };
        self.fill_env(builder, pkg, false)
    }

    /// Prepares a new process with an appropriate environment to run against
    /// the artifacts produced by the build process.
    ///
    /// The package argument is also used to configure environment variables as
    /// well as the working directory of the child process.
    fn fill_env(
        &self,
        mut cmd: ProcessBuilder,
        pkg: &Package,
        is_host: bool,
    ) -> CargoResult<ProcessBuilder> {
        let mut search_path = if is_host {
            let mut search_path = vec![self.host_deps_output.clone()];
            search_path.extend(self.host_dylib_path.clone());
            search_path
        } else {
            let mut search_path =
                super::filter_dynamic_search_path(self.native_dirs.iter(), &self.root_output);
            search_path.push(self.deps_output.clone());
            search_path.push(self.root_output.clone());
            search_path.extend(self.target_dylib_path.clone());
            search_path
        };

        search_path.extend(util::dylib_path().into_iter());
        if cfg!(target_os = "macos") {
            // These are the defaults when DYLD_FALLBACK_LIBRARY_PATH isn't
            // set. Since Cargo is explicitly setting the value, make sure the
            // defaults still work.
            if let Ok(home) = env::var("HOME") {
                search_path.push(PathBuf::from(home).join("lib"));
            }
            search_path.push(PathBuf::from("/usr/local/lib"));
            search_path.push(PathBuf::from("/usr/lib"));
        }
        let search_path = join_paths(&search_path, util::dylib_path_envvar())?;

        cmd.env(util::dylib_path_envvar(), &search_path);
        if let Some(env) = self.extra_env.get(&pkg.package_id()) {
            for &(ref k, ref v) in env {
                cmd.env(k, v);
            }
        }

        let metadata = pkg.manifest().metadata();

        let cargo_exe = self.config.cargo_exe()?;
        cmd.env(crate::CARGO_ENV, cargo_exe);

        // When adding new environment variables depending on
        // crate properties which might require rebuild upon change
        // consider adding the corresponding properties to the hash
        // in BuildContext::target_metadata()
        cmd.env("CARGO_MANIFEST_DIR", pkg.root())
            .env("CARGO_PKG_VERSION_MAJOR", &pkg.version().major.to_string())
            .env("CARGO_PKG_VERSION_MINOR", &pkg.version().minor.to_string())
            .env("CARGO_PKG_VERSION_PATCH", &pkg.version().patch.to_string())
            .env(
                "CARGO_PKG_VERSION_PRE",
                &pre_version_component(pkg.version()),
            )
            .env("CARGO_PKG_VERSION", &pkg.version().to_string())
            .env("CARGO_PKG_NAME", &*pkg.name())
            .env(
                "CARGO_PKG_DESCRIPTION",
                metadata.description.as_ref().unwrap_or(&String::new()),
            )
            .env(
                "CARGO_PKG_HOMEPAGE",
                metadata.homepage.as_ref().unwrap_or(&String::new()),
            )
            .env(
                "CARGO_PKG_REPOSITORY",
                metadata.repository.as_ref().unwrap_or(&String::new()),
            )
            .env("CARGO_PKG_AUTHORS", &pkg.authors().join(":"))
            .cwd(pkg.root());
        Ok(cmd)
    }
}

fn pre_version_component(v: &Version) -> String {
    if v.pre.is_empty() {
        return String::new();
    }

    let mut ret = String::new();

    for (i, x) in v.pre.iter().enumerate() {
        if i != 0 {
            ret.push('.')
        };
        ret.push_str(&x.to_string());
    }

    ret
}

fn target_runner(bcx: &BuildContext<'_, '_>) -> CargoResult<Option<(PathBuf, Vec<String>)>> {
    let target = bcx.target_triple();

    // try target.{}.runner
    let key = format!("target.{}.runner", target);
    if let Some(v) = bcx.config.get_path_and_args(&key)? {
        return Ok(Some(v.val));
    }

    // try target.'cfg(...)'.runner
    if let Some(target_cfg) = bcx.target_info.cfg() {
        if let Some(table) = bcx.config.get_table("target")? {
            let mut matching_runner = None;

            for key in table.val.keys() {
                if CfgExpr::matches_key(key, target_cfg) {
                    let key = format!("target.{}.runner", key);
                    if let Some(runner) = bcx.config.get_path_and_args(&key)? {
                        // more than one match, error out
                        if matching_runner.is_some() {
                            failure::bail!(
                                "several matching instances of `target.'cfg(..)'.runner` \
                                 in `.cargo/config`"
                            )
                        }

                        matching_runner = Some(runner.val);
                    }
                }
            }

            return Ok(matching_runner);
        }
    }

    Ok(None)
}
<commit_msg>Use env::var_os("HOME") rather than env::var("HOME")<commit_after>use std::collections::{BTreeSet, HashMap, HashSet};
use std::env;
use std::ffi::OsStr;
use std::path::PathBuf;

use semver::Version;

use super::BuildContext;
use crate::core::{Edition, Package, PackageId, Target};
use crate::util::{self, join_paths, process, CargoResult, CfgExpr, Config, ProcessBuilder};

pub struct Doctest {
    /// The package being doc-tested.
    pub package: Package,
    /// The target being tested (currently always the package's lib).
    pub target: Target,
    /// Extern dependencies needed by `rustdoc`. The path is the location of
    /// the compiled lib.
    pub deps: Vec<(String, PathBuf)>,
}

/// A structure returning the result of a compilation.
pub struct Compilation<'cfg> {
    /// An array of all tests created during this compilation.
    pub tests: Vec<(Package, Target, PathBuf)>,

    /// An array of all binaries created.
    pub binaries: Vec<PathBuf>,

    /// All directories for the output of native build commands.
    ///
    /// This is currently used to drive some entries which are added to the
    /// LD_LIBRARY_PATH as appropriate.
    ///
    /// The order should be deterministic.
    pub native_dirs: BTreeSet<PathBuf>,

    /// Root output directory (for the local package's artifacts)
    pub root_output: PathBuf,

    /// Output directory for rust dependencies.
    /// May be for the host or for a specific target.
    pub deps_output: PathBuf,

    /// Output directory for the rust host dependencies.
    pub host_deps_output: PathBuf,

    /// The path to rustc's own libstd
    pub host_dylib_path: Option<PathBuf>,

    /// The path to libstd for the target
    pub target_dylib_path: Option<PathBuf>,

    /// Extra environment variables that were passed to compilations and should
    /// be passed to future invocations of programs.
    pub extra_env: HashMap<PackageId, Vec<(String, String)>>,

    /// Libraries to test with rustdoc.
    pub to_doc_test: Vec<Doctest>,

    /// Features per package enabled during this compilation.
    pub cfgs: HashMap<PackageId, HashSet<String>>,

    /// Flags to pass to rustdoc when invoked from cargo test, per package.
    pub rustdocflags: HashMap<PackageId, Vec<String>>,

    pub host: String,
    pub target: String,

    config: &'cfg Config,
    rustc_process: ProcessBuilder,

    target_runner: Option<(PathBuf, Vec<String>)>,
}

impl<'cfg> Compilation<'cfg> {
    pub fn new<'a>(bcx: &BuildContext<'a, 'cfg>) -> CargoResult<Compilation<'cfg>> {
        let mut rustc = bcx.rustc.process();

        if bcx.config.extra_verbose() {
            rustc.display_env_vars();
        }
        let srv = bcx.build_config.rustfix_diagnostic_server.borrow();
        if let Some(server) = &*srv {
            server.configure(&mut rustc);
        }
        Ok(Compilation {
            // TODO: deprecated; remove.
            native_dirs: BTreeSet::new(),
            root_output: PathBuf::from("/"),
            deps_output: PathBuf::from("/"),
            host_deps_output: PathBuf::from("/"),
            host_dylib_path: bcx.host_info.sysroot_libdir.clone(),
            target_dylib_path: bcx.target_info.sysroot_libdir.clone(),
            tests: Vec::new(),
            binaries: Vec::new(),
            extra_env: HashMap::new(),
            to_doc_test: Vec::new(),
            cfgs: HashMap::new(),
            rustdocflags: HashMap::new(),
            config: bcx.config,
            rustc_process: rustc,
            host: bcx.host_triple().to_string(),
            target: bcx.target_triple().to_string(),
            target_runner: target_runner(bcx)?,
        })
    }

    /// See `process`.
    pub fn rustc_process(&self, pkg: &Package, target: &Target) -> CargoResult<ProcessBuilder> {
        let mut p = self.fill_env(self.rustc_process.clone(), pkg, true)?;
        if target.edition() != Edition::Edition2015 {
            p.arg(format!("--edition={}", target.edition()));
        }
        Ok(p)
    }

    /// See `process`.
    pub fn rustdoc_process(&self, pkg: &Package, target: &Target) -> CargoResult<ProcessBuilder> {
        let mut p = self.fill_env(process(&*self.config.rustdoc()?), pkg, false)?;
        if target.edition() != Edition::Edition2015 {
            p.arg(format!("--edition={}", target.edition()));
        }
        Ok(p)
    }

    /// See `process`.
    pub fn host_process<T: AsRef<OsStr>>(
        &self,
        cmd: T,
        pkg: &Package,
    ) -> CargoResult<ProcessBuilder> {
        self.fill_env(process(cmd), pkg, true)
    }

    fn target_runner(&self) -> &Option<(PathBuf, Vec<String>)> {
        &self.target_runner
    }

    /// See `process`.
    pub fn target_process<T: AsRef<OsStr>>(
        &self,
        cmd: T,
        pkg: &Package,
    ) -> CargoResult<ProcessBuilder> {
        let builder = if let Some((ref runner, ref args)) = *self.target_runner() {
            let mut builder = process(runner);
            builder.args(args);
            builder.arg(cmd);
            builder
        } else {
            process(cmd)
        };
        self.fill_env(builder, pkg, false)
    }

    /// Prepares a new process with an appropriate environment to run against
    /// the artifacts produced by the build process.
    ///
    /// The package argument is also used to configure environment variables as
    /// well as the working directory of the child process.
    fn fill_env(
        &self,
        mut cmd: ProcessBuilder,
        pkg: &Package,
        is_host: bool,
    ) -> CargoResult<ProcessBuilder> {
        let mut search_path = if is_host {
            let mut search_path = vec![self.host_deps_output.clone()];
            search_path.extend(self.host_dylib_path.clone());
            search_path
        } else {
            let mut search_path =
                super::filter_dynamic_search_path(self.native_dirs.iter(), &self.root_output);
            search_path.push(self.deps_output.clone());
            search_path.push(self.root_output.clone());
            search_path.extend(self.target_dylib_path.clone());
            search_path
        };

        search_path.extend(util::dylib_path().into_iter());
        if cfg!(target_os = "macos") {
            // These are the defaults when DYLD_FALLBACK_LIBRARY_PATH isn't
            // set. Since Cargo is explicitly setting the value, make sure the
            // defaults still work.
            if let Some(home) = env::var_os("HOME") {
                search_path.push(PathBuf::from(home).join("lib"));
            }
            search_path.push(PathBuf::from("/usr/local/lib"));
            search_path.push(PathBuf::from("/usr/lib"));
        }
        let search_path = join_paths(&search_path, util::dylib_path_envvar())?;

        cmd.env(util::dylib_path_envvar(), &search_path);
        if let Some(env) = self.extra_env.get(&pkg.package_id()) {
            for &(ref k, ref v) in env {
                cmd.env(k, v);
            }
        }

        let metadata = pkg.manifest().metadata();

        let cargo_exe = self.config.cargo_exe()?;
        cmd.env(crate::CARGO_ENV, cargo_exe);

        // When adding new environment variables depending on
        // crate properties which might require rebuild upon change
        // consider adding the corresponding properties to the hash
        // in BuildContext::target_metadata()
        cmd.env("CARGO_MANIFEST_DIR", pkg.root())
            .env("CARGO_PKG_VERSION_MAJOR", &pkg.version().major.to_string())
            .env("CARGO_PKG_VERSION_MINOR", &pkg.version().minor.to_string())
            .env("CARGO_PKG_VERSION_PATCH", &pkg.version().patch.to_string())
            .env(
                "CARGO_PKG_VERSION_PRE",
                &pre_version_component(pkg.version()),
            )
            .env("CARGO_PKG_VERSION", &pkg.version().to_string())
            .env("CARGO_PKG_NAME", &*pkg.name())
            .env(
                "CARGO_PKG_DESCRIPTION",
                metadata.description.as_ref().unwrap_or(&String::new()),
            )
            .env(
                "CARGO_PKG_HOMEPAGE",
                metadata.homepage.as_ref().unwrap_or(&String::new()),
            )
            .env(
                "CARGO_PKG_REPOSITORY",
                metadata.repository.as_ref().unwrap_or(&String::new()),
            )
            .env("CARGO_PKG_AUTHORS", &pkg.authors().join(":"))
            .cwd(pkg.root());
        Ok(cmd)
    }
}

fn pre_version_component(v: &Version) -> String {
    if v.pre.is_empty() {
        return String::new();
    }

    let mut ret = String::new();

    for (i, x) in v.pre.iter().enumerate() {
        if i != 0 {
            ret.push('.')
        };
        ret.push_str(&x.to_string());
    }

    ret
}

fn target_runner(bcx: &BuildContext<'_, '_>) -> CargoResult<Option<(PathBuf, Vec<String>)>> {
    let target = bcx.target_triple();

    // try target.{}.runner
    let key = format!("target.{}.runner", target);
    if let Some(v) = bcx.config.get_path_and_args(&key)? {
        return Ok(Some(v.val));
    }

    // try target.'cfg(...)'.runner
    if let Some(target_cfg) = bcx.target_info.cfg() {
        if let Some(table) = bcx.config.get_table("target")? {
            let mut matching_runner = None;

            for key in table.val.keys() {
                if CfgExpr::matches_key(key, target_cfg) {
                    let key = format!("target.{}.runner", key);
                    if let Some(runner) = bcx.config.get_path_and_args(&key)? {
                        // more than one match, error out
                        if matching_runner.is_some() {
                            failure::bail!(
                                "several matching instances of `target.'cfg(..)'.runner` \
                                 in `.cargo/config`"
                            )
                        }

                        matching_runner = Some(runner.val);
                    }
                }
            }

            return Ok(matching_runner);
        }
    }

    Ok(None)
}
<|endoftext|>


<commit_before>use std::collections::HashSet;
use std::collections::hash_map::HashMap;
use std::fmt;
use std::io;
use std::mem;
use std::sync::mpsc::{channel, Sender, Receiver};

use crossbeam::{self, Scope};
use jobserver::{Acquired, HelperThread};

use core::{PackageId, Target, Profile};
use util::{Config, DependencyQueue, Fresh, Dirty, Freshness};
use util::{CargoResult, ProcessBuilder, profile, internal, CargoResultExt};
use {handle_error};

use super::{Context, Kind, Unit};
use super::job::Job;

/// A management structure of the entire dependency graph to compile.
///
/// This structure is backed by the `DependencyQueue` type and manages the
/// actual compilation step of each package. Packages enqueue units of work and
/// then later on the entire graph is processed and compiled.
pub struct JobQueue<'a> {
    queue: DependencyQueue<Key<'a>, Vec<(Job, Freshness)>>,
    tx: Sender<Message<'a>>,
    rx: Receiver<Message<'a>>,
    active: usize,
    pending: HashMap<Key<'a>, PendingBuild>,
    compiled: HashSet<&'a PackageId>,
    documented: HashSet<&'a PackageId>,
    counts: HashMap<&'a PackageId, usize>,
    is_release: bool,
}

/// A helper structure for metadata about the state of a building package.
struct PendingBuild {
    /// Number of jobs currently active
    amt: usize,
    /// Current freshness state of this package. Any dirty target within a
    /// package will cause the entire package to become dirty.
    fresh: Freshness,
}

#[derive(Clone, Copy, Eq, PartialEq, Hash)]
struct Key<'a> {
    pkg: &'a PackageId,
    target: &'a Target,
    profile: &'a Profile,
    kind: Kind,
}

pub struct JobState<'a> {
    tx: Sender<Message<'a>>,
}

enum Message<'a> {
    Run(String),
    Stdout(String),
    Stderr(String),
    Token(io::Result<Acquired>),
    Finish(Key<'a>, CargoResult<()>),
}

impl<'a> JobState<'a> {
    pub fn running(&self, cmd: &ProcessBuilder) {
        let _ = self.tx.send(Message::Run(cmd.to_string()));
    }

    pub fn stdout(&self, out: &str) {
        let _ = self.tx.send(Message::Stdout(out.to_string()));
    }

    pub fn stderr(&self, err: &str) {
        let _ = self.tx.send(Message::Stderr(err.to_string()));
    }
}

impl<'a> JobQueue<'a> {
    pub fn new<'cfg>(cx: &Context<'a, 'cfg>) -> JobQueue<'a> {
        let (tx, rx) = channel();
        JobQueue {
            queue: DependencyQueue::new(),
            tx: tx,
            rx: rx,
            active: 0,
            pending: HashMap::new(),
            compiled: HashSet::new(),
            documented: HashSet::new(),
            counts: HashMap::new(),
            is_release: cx.build_config.release,
        }
    }

    pub fn enqueue<'cfg>(&mut self,
                         cx: &Context<'a, 'cfg>,
                         unit: &Unit<'a>,
                         job: Job,
                         fresh: Freshness) -> CargoResult<()> {
        let key = Key::new(unit);
        let deps = key.dependencies(cx)?;
        self.queue.queue(Fresh, key, Vec::new(), &deps).push((job, fresh));
        *self.counts.entry(key.pkg).or_insert(0) += 1;
        Ok(())
    }

    /// Execute all jobs necessary to build the dependency graph.
    ///
    /// This function will spawn off `config.jobs()` workers to build all of the
    /// necessary dependencies, in order. Freshness is propagated as far as
    /// possible along each dependency chain.
    pub fn execute(&mut self, cx: &mut Context) -> CargoResult<()> {
        let _p = profile::start("executing the job graph");

        // We need to give a handle to the send half of our message queue to the
        // jobserver helper thrad. Unfortunately though we need the handle to be
        // `'static` as that's typically what's required when spawning a
        // thread!
        //
        // To work around this we transmute the `Sender` to a static lifetime.
        // we're only sending "longer living" messages and we should also
        // destroy all references to the channel before this function exits as
        // the destructor for the `helper` object will ensure the associated
        // thread i sno longer running.
        //
        // As a result, this `transmute` to a longer lifetime should be safe in
        // practice.
        let tx = self.tx.clone();
        let tx = unsafe {
            mem::transmute::<Sender<Message<'a>>, Sender<Message<'static>>>(tx)
        };
        let helper = cx.jobserver.clone().into_helper_thread(move |token| {
            drop(tx.send(Message::Token(token)));
        }).chain_err(|| {
            "failed to create helper thread for jobserver management"
        })?;

        crossbeam::scope(|scope| {
            self.drain_the_queue(cx, scope, &helper)
        })
    }

    fn drain_the_queue(&mut self,
                       cx: &mut Context,
                       scope: &Scope<'a>,
                       jobserver_helper: &HelperThread)
                       -> CargoResult<()> {
        use std::time::Instant;

        let mut tokens = Vec::new();
        let mut queue = Vec::new();
        trace!("queue: {:#?}", self.queue);

        // Iteratively execute the entire dependency graph. Each turn of the
        // loop starts out by scheduling as much work as possible (up to the
        // maximum number of parallel jobs we have tokens for). A local queue
        // is maintained separately from the main dependency queue as one
        // dequeue may actually dequeue quite a bit of work (e.g. 10 binaries
        // in one project).
        //
        // After a job has finished we update our internal state if it was
        // successful and otherwise wait for pending work to finish if it failed
        // and then immediately return.
        let mut error = None;
        let start_time = Instant::now();
        loop {
            // Dequeue as much work as we can, learning about everything
            // possible that can run. Note that this is also the point where we
            // start requesting job tokens. Each job after the first needs to
            // request a token.
            while let Some((fresh, key, jobs)) = self.queue.dequeue() {
                let total_fresh = jobs.iter().fold(fresh, |fresh, &(_, f)| {
                    f.combine(fresh)
                });
                self.pending.insert(key, PendingBuild {
                    amt: jobs.len(),
                    fresh: total_fresh,
                });
                for (job, f) in jobs {
                    queue.push((key, job, f.combine(fresh)));
                    if self.active + queue.len() > 0 {
                        jobserver_helper.request_token();
                    }
                }
            }

            // Now that we've learned of all possible work that we can execute
            // try to spawn it so long as we've got a jobserver token which says
            // we're able to perform some parallel work.
            while error.is_none() && self.active < tokens.len() + 1 && !queue.is_empty() {
                let (key, job, fresh) = queue.remove(0);
                self.run(key, fresh, job, cx.config, scope)?;
            }

            // If after all that we're not actually running anything then we're
            // done!
            if self.active == 0 {
                break
            }

            // And finally, before we block waiting for the next event, drop any
            // excess tokens we may have accidentally acquired. Due to how our
            // jobserver interface is architected we may acquire a token that we
            // don't actually use, and if this happens just relinquish it back
            // to the jobserver itself.
            tokens.truncate(self.active - 1);

            match self.rx.recv().unwrap() {
                Message::Run(cmd) => {
                    cx.config.shell().verbose(|c| c.status("Running", &cmd))?;
                }
                Message::Stdout(out) => {
                    if cx.config.extra_verbose() {
                        println!("{}", out);
                    }
                }
                Message::Stderr(err) => {
                    if cx.config.extra_verbose() {
                        writeln!(cx.config.shell().err(), "{}", err)?;
                    }
                }
                Message::Finish(key, result) => {
                    info!("end: {:?}", key);
                    self.active -= 1;
                    if self.active > 0 {
                        assert!(tokens.len() > 0);
                        drop(tokens.pop());
                    }
                    match result {
                        Ok(()) => self.finish(key, cx)?,
                        Err(e) => {
                            let msg = "The following warnings were emitted during compilation:";
                            self.emit_warnings(Some(msg), key, cx)?;

                            if self.active > 0 {
                                error = Some("build failed".into());
                                handle_error(e, &mut *cx.config.shell());
                                cx.config.shell().warn(
                                            "build failed, waiting for other \
                                             jobs to finish...")?;
                            }
                            else {
                                error = Some(e);
                            }
                        }
                    }
                }
                Message::Token(acquired_token) => {
                    tokens.push(acquired_token.chain_err(|| {
                        "failed to acquire jobserver token"
                    })?);
                }
            }
        }

        let build_type = if self.is_release { "release" } else { "dev" };
        let profile = cx.lib_profile();
        let mut opt_type = String::from(if profile.opt_level == "0" { "unoptimized" }
                                        else { "optimized" });
        if profile.debuginfo.is_some() {
            opt_type = opt_type + " + debuginfo";
        }
        let duration = start_time.elapsed();
        let time_elapsed = format!("{}.{1:.2} secs",
                                   duration.as_secs(),
                                   duration.subsec_nanos() / 10000000);
        if self.queue.is_empty() {
            let message = format!("{} [{}] target(s) in {}",
                                  build_type,
                                  opt_type,
                                  time_elapsed);
            cx.config.shell().status("Finished", message)?;
            Ok(())
        } else if let Some(e) = error {
            Err(e)
        } else {
            debug!("queue: {:#?}", self.queue);
            Err(internal("finished with jobs still left in the queue"))
        }
    }

    /// Executes a job in the `scope` given, pushing the spawned thread's
    /// handled onto `threads`.
    fn run(&mut self,
           key: Key<'a>,
           fresh: Freshness,
           job: Job,
           config: &Config,
           scope: &Scope<'a>) -> CargoResult<()> {
        info!("start: {:?}", key);

        self.active += 1;
        *self.counts.get_mut(key.pkg).unwrap() -= 1;

        let my_tx = self.tx.clone();
        let doit = move || {
            let res = job.run(fresh, &JobState {
                tx: my_tx.clone(),
            });
            my_tx.send(Message::Finish(key, res)).unwrap();
        };
        match fresh {
            Freshness::Fresh => doit(),
            Freshness::Dirty => { scope.spawn(doit); }
        }

        // Print out some nice progress information
        self.note_working_on(config, &key, fresh)?;

        Ok(())
    }

    fn emit_warnings(&self, msg: Option<&str>, key: Key<'a>, cx: &mut Context) -> CargoResult<()> {
        let output = cx.build_state.outputs.lock().unwrap();
        if let Some(output) = output.get(&(key.pkg.clone(), key.kind)) {
            if let Some(msg) = msg {
                if !output.warnings.is_empty() {
                    writeln!(cx.config.shell().err(), "{}\n", msg)?;
                }
            }

            for warning in output.warnings.iter() {
                cx.config.shell().warn(warning)?;
            }

            if !output.warnings.is_empty() && msg.is_some() {
                // Output an empty line.
                writeln!(cx.config.shell().err(), "")?;
            }
        }

        Ok(())
    }

    fn finish(&mut self, key: Key<'a>, cx: &mut Context) -> CargoResult<()> {
        if key.profile.run_custom_build && cx.show_warnings(key.pkg) {
            self.emit_warnings(None, key, cx)?;
        }

        let state = self.pending.get_mut(&key).unwrap();
        state.amt -= 1;
        if state.amt == 0 {
            self.queue.finish(&key, state.fresh);
        }
        Ok(())
    }

    // This isn't super trivial because we don't want to print loads and
    // loads of information to the console, but we also want to produce a
    // faithful representation of what's happening. This is somewhat nuanced
    // as a package can start compiling *very* early on because of custom
    // build commands and such.
    //
    // In general, we try to print "Compiling" for the first nontrivial task
    // run for a package, regardless of when that is. We then don't print
    // out any more information for a package after we've printed it once.
    fn note_working_on(&mut self,
                       config: &Config,
                       key: &Key<'a>,
                       fresh: Freshness) -> CargoResult<()> {
        if (self.compiled.contains(key.pkg) && !key.profile.doc) ||
            (self.documented.contains(key.pkg) && key.profile.doc) {
            return Ok(())
        }

        match fresh {
            // Any dirty stage which runs at least one command gets printed as
            // being a compiled package
            Dirty => {
                if key.profile.doc {
                    if !key.profile.test {
                        self.documented.insert(key.pkg);
                        config.shell().status("Documenting", key.pkg)?;
                    }
                } else {
                    self.compiled.insert(key.pkg);
                    config.shell().status("Compiling", key.pkg)?;
                }
            }
            Fresh if self.counts[key.pkg] == 0 => {
                self.compiled.insert(key.pkg);
                config.shell().verbose(|c| c.status("Fresh", key.pkg))?;
            }
            Fresh => {}
        }
        Ok(())
    }
}

impl<'a> Key<'a> {
    fn new(unit: &Unit<'a>) -> Key<'a> {
        Key {
            pkg: unit.pkg.package_id(),
            target: unit.target,
            profile: unit.profile,
            kind: unit.kind,
        }
    }

    fn dependencies<'cfg>(&self, cx: &Context<'a, 'cfg>)
                          -> CargoResult<Vec<Key<'a>>> {
        let unit = Unit {
            pkg: cx.get_package(self.pkg)?,
            target: self.target,
            profile: self.profile,
            kind: self.kind,
        };
        let targets = cx.dep_targets(&unit)?;
        Ok(targets.iter().filter_map(|unit| {
            // Binaries aren't actually needed to *compile* tests, just to run
            // them, so we don't include this dependency edge in the job graph.
            if self.target.is_test() && unit.target.is_bin() {
                None
            } else {
                Some(Key::new(unit))
            }
        }).collect())
    }
}

impl<'a> fmt::Debug for Key<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{} => {}/{} => {:?}", self.pkg, self.target, self.profile,
               self.kind)
    }
}
<commit_msg>Fix typo in comment<commit_after>use std::collections::HashSet;
use std::collections::hash_map::HashMap;
use std::fmt;
use std::io;
use std::mem;
use std::sync::mpsc::{channel, Sender, Receiver};

use crossbeam::{self, Scope};
use jobserver::{Acquired, HelperThread};

use core::{PackageId, Target, Profile};
use util::{Config, DependencyQueue, Fresh, Dirty, Freshness};
use util::{CargoResult, ProcessBuilder, profile, internal, CargoResultExt};
use {handle_error};

use super::{Context, Kind, Unit};
use super::job::Job;

/// A management structure of the entire dependency graph to compile.
///
/// This structure is backed by the `DependencyQueue` type and manages the
/// actual compilation step of each package. Packages enqueue units of work and
/// then later on the entire graph is processed and compiled.
pub struct JobQueue<'a> {
    queue: DependencyQueue<Key<'a>, Vec<(Job, Freshness)>>,
    tx: Sender<Message<'a>>,
    rx: Receiver<Message<'a>>,
    active: usize,
    pending: HashMap<Key<'a>, PendingBuild>,
    compiled: HashSet<&'a PackageId>,
    documented: HashSet<&'a PackageId>,
    counts: HashMap<&'a PackageId, usize>,
    is_release: bool,
}

/// A helper structure for metadata about the state of a building package.
struct PendingBuild {
    /// Number of jobs currently active
    amt: usize,
    /// Current freshness state of this package. Any dirty target within a
    /// package will cause the entire package to become dirty.
    fresh: Freshness,
}

#[derive(Clone, Copy, Eq, PartialEq, Hash)]
struct Key<'a> {
    pkg: &'a PackageId,
    target: &'a Target,
    profile: &'a Profile,
    kind: Kind,
}

pub struct JobState<'a> {
    tx: Sender<Message<'a>>,
}

enum Message<'a> {
    Run(String),
    Stdout(String),
    Stderr(String),
    Token(io::Result<Acquired>),
    Finish(Key<'a>, CargoResult<()>),
}

impl<'a> JobState<'a> {
    pub fn running(&self, cmd: &ProcessBuilder) {
        let _ = self.tx.send(Message::Run(cmd.to_string()));
    }

    pub fn stdout(&self, out: &str) {
        let _ = self.tx.send(Message::Stdout(out.to_string()));
    }

    pub fn stderr(&self, err: &str) {
        let _ = self.tx.send(Message::Stderr(err.to_string()));
    }
}

impl<'a> JobQueue<'a> {
    pub fn new<'cfg>(cx: &Context<'a, 'cfg>) -> JobQueue<'a> {
        let (tx, rx) = channel();
        JobQueue {
            queue: DependencyQueue::new(),
            tx: tx,
            rx: rx,
            active: 0,
            pending: HashMap::new(),
            compiled: HashSet::new(),
            documented: HashSet::new(),
            counts: HashMap::new(),
            is_release: cx.build_config.release,
        }
    }

    pub fn enqueue<'cfg>(&mut self,
                         cx: &Context<'a, 'cfg>,
                         unit: &Unit<'a>,
                         job: Job,
                         fresh: Freshness) -> CargoResult<()> {
        let key = Key::new(unit);
        let deps = key.dependencies(cx)?;
        self.queue.queue(Fresh, key, Vec::new(), &deps).push((job, fresh));
        *self.counts.entry(key.pkg).or_insert(0) += 1;
        Ok(())
    }

    /// Execute all jobs necessary to build the dependency graph.
    ///
    /// This function will spawn off `config.jobs()` workers to build all of the
    /// necessary dependencies, in order. Freshness is propagated as far as
    /// possible along each dependency chain.
    pub fn execute(&mut self, cx: &mut Context) -> CargoResult<()> {
        let _p = profile::start("executing the job graph");

        // We need to give a handle to the send half of our message queue to the
        // jobserver helper thread. Unfortunately though we need the handle to be
        // `'static` as that's typically what's required when spawning a
        // thread!
        //
        // To work around this we transmute the `Sender` to a static lifetime.
        // we're only sending "longer living" messages and we should also
        // destroy all references to the channel before this function exits as
        // the destructor for the `helper` object will ensure the associated
        // thread i sno longer running.
        //
        // As a result, this `transmute` to a longer lifetime should be safe in
        // practice.
        let tx = self.tx.clone();
        let tx = unsafe {
            mem::transmute::<Sender<Message<'a>>, Sender<Message<'static>>>(tx)
        };
        let helper = cx.jobserver.clone().into_helper_thread(move |token| {
            drop(tx.send(Message::Token(token)));
        }).chain_err(|| {
            "failed to create helper thread for jobserver management"
        })?;

        crossbeam::scope(|scope| {
            self.drain_the_queue(cx, scope, &helper)
        })
    }

    fn drain_the_queue(&mut self,
                       cx: &mut Context,
                       scope: &Scope<'a>,
                       jobserver_helper: &HelperThread)
                       -> CargoResult<()> {
        use std::time::Instant;

        let mut tokens = Vec::new();
        let mut queue = Vec::new();
        trace!("queue: {:#?}", self.queue);

        // Iteratively execute the entire dependency graph. Each turn of the
        // loop starts out by scheduling as much work as possible (up to the
        // maximum number of parallel jobs we have tokens for). A local queue
        // is maintained separately from the main dependency queue as one
        // dequeue may actually dequeue quite a bit of work (e.g. 10 binaries
        // in one project).
        //
        // After a job has finished we update our internal state if it was
        // successful and otherwise wait for pending work to finish if it failed
        // and then immediately return.
        let mut error = None;
        let start_time = Instant::now();
        loop {
            // Dequeue as much work as we can, learning about everything
            // possible that can run. Note that this is also the point where we
            // start requesting job tokens. Each job after the first needs to
            // request a token.
            while let Some((fresh, key, jobs)) = self.queue.dequeue() {
                let total_fresh = jobs.iter().fold(fresh, |fresh, &(_, f)| {
                    f.combine(fresh)
                });
                self.pending.insert(key, PendingBuild {
                    amt: jobs.len(),
                    fresh: total_fresh,
                });
                for (job, f) in jobs {
                    queue.push((key, job, f.combine(fresh)));
                    if self.active + queue.len() > 0 {
                        jobserver_helper.request_token();
                    }
                }
            }

            // Now that we've learned of all possible work that we can execute
            // try to spawn it so long as we've got a jobserver token which says
            // we're able to perform some parallel work.
            while error.is_none() && self.active < tokens.len() + 1 && !queue.is_empty() {
                let (key, job, fresh) = queue.remove(0);
                self.run(key, fresh, job, cx.config, scope)?;
            }

            // If after all that we're not actually running anything then we're
            // done!
            if self.active == 0 {
                break
            }

            // And finally, before we block waiting for the next event, drop any
            // excess tokens we may have accidentally acquired. Due to how our
            // jobserver interface is architected we may acquire a token that we
            // don't actually use, and if this happens just relinquish it back
            // to the jobserver itself.
            tokens.truncate(self.active - 1);

            match self.rx.recv().unwrap() {
                Message::Run(cmd) => {
                    cx.config.shell().verbose(|c| c.status("Running", &cmd))?;
                }
                Message::Stdout(out) => {
                    if cx.config.extra_verbose() {
                        println!("{}", out);
                    }
                }
                Message::Stderr(err) => {
                    if cx.config.extra_verbose() {
                        writeln!(cx.config.shell().err(), "{}", err)?;
                    }
                }
                Message::Finish(key, result) => {
                    info!("end: {:?}", key);
                    self.active -= 1;
                    if self.active > 0 {
                        assert!(tokens.len() > 0);
                        drop(tokens.pop());
                    }
                    match result {
                        Ok(()) => self.finish(key, cx)?,
                        Err(e) => {
                            let msg = "The following warnings were emitted during compilation:";
                            self.emit_warnings(Some(msg), key, cx)?;

                            if self.active > 0 {
                                error = Some("build failed".into());
                                handle_error(e, &mut *cx.config.shell());
                                cx.config.shell().warn(
                                            "build failed, waiting for other \
                                             jobs to finish...")?;
                            }
                            else {
                                error = Some(e);
                            }
                        }
                    }
                }
                Message::Token(acquired_token) => {
                    tokens.push(acquired_token.chain_err(|| {
                        "failed to acquire jobserver token"
                    })?);
                }
            }
        }

        let build_type = if self.is_release { "release" } else { "dev" };
        let profile = cx.lib_profile();
        let mut opt_type = String::from(if profile.opt_level == "0" { "unoptimized" }
                                        else { "optimized" });
        if profile.debuginfo.is_some() {
            opt_type = opt_type + " + debuginfo";
        }
        let duration = start_time.elapsed();
        let time_elapsed = format!("{}.{1:.2} secs",
                                   duration.as_secs(),
                                   duration.subsec_nanos() / 10000000);
        if self.queue.is_empty() {
            let message = format!("{} [{}] target(s) in {}",
                                  build_type,
                                  opt_type,
                                  time_elapsed);
            cx.config.shell().status("Finished", message)?;
            Ok(())
        } else if let Some(e) = error {
            Err(e)
        } else {
            debug!("queue: {:#?}", self.queue);
            Err(internal("finished with jobs still left in the queue"))
        }
    }

    /// Executes a job in the `scope` given, pushing the spawned thread's
    /// handled onto `threads`.
    fn run(&mut self,
           key: Key<'a>,
           fresh: Freshness,
           job: Job,
           config: &Config,
           scope: &Scope<'a>) -> CargoResult<()> {
        info!("start: {:?}", key);

        self.active += 1;
        *self.counts.get_mut(key.pkg).unwrap() -= 1;

        let my_tx = self.tx.clone();
        let doit = move || {
            let res = job.run(fresh, &JobState {
                tx: my_tx.clone(),
            });
            my_tx.send(Message::Finish(key, res)).unwrap();
        };
        match fresh {
            Freshness::Fresh => doit(),
            Freshness::Dirty => { scope.spawn(doit); }
        }

        // Print out some nice progress information
        self.note_working_on(config, &key, fresh)?;

        Ok(())
    }

    fn emit_warnings(&self, msg: Option<&str>, key: Key<'a>, cx: &mut Context) -> CargoResult<()> {
        let output = cx.build_state.outputs.lock().unwrap();
        if let Some(output) = output.get(&(key.pkg.clone(), key.kind)) {
            if let Some(msg) = msg {
                if !output.warnings.is_empty() {
                    writeln!(cx.config.shell().err(), "{}\n", msg)?;
                }
            }

            for warning in output.warnings.iter() {
                cx.config.shell().warn(warning)?;
            }

            if !output.warnings.is_empty() && msg.is_some() {
                // Output an empty line.
                writeln!(cx.config.shell().err(), "")?;
            }
        }

        Ok(())
    }

    fn finish(&mut self, key: Key<'a>, cx: &mut Context) -> CargoResult<()> {
        if key.profile.run_custom_build && cx.show_warnings(key.pkg) {
            self.emit_warnings(None, key, cx)?;
        }

        let state = self.pending.get_mut(&key).unwrap();
        state.amt -= 1;
        if state.amt == 0 {
            self.queue.finish(&key, state.fresh);
        }
        Ok(())
    }

    // This isn't super trivial because we don't want to print loads and
    // loads of information to the console, but we also want to produce a
    // faithful representation of what's happening. This is somewhat nuanced
    // as a package can start compiling *very* early on because of custom
    // build commands and such.
    //
    // In general, we try to print "Compiling" for the first nontrivial task
    // run for a package, regardless of when that is. We then don't print
    // out any more information for a package after we've printed it once.
    fn note_working_on(&mut self,
                       config: &Config,
                       key: &Key<'a>,
                       fresh: Freshness) -> CargoResult<()> {
        if (self.compiled.contains(key.pkg) && !key.profile.doc) ||
            (self.documented.contains(key.pkg) && key.profile.doc) {
            return Ok(())
        }

        match fresh {
            // Any dirty stage which runs at least one command gets printed as
            // being a compiled package
            Dirty => {
                if key.profile.doc {
                    if !key.profile.test {
                        self.documented.insert(key.pkg);
                        config.shell().status("Documenting", key.pkg)?;
                    }
                } else {
                    self.compiled.insert(key.pkg);
                    config.shell().status("Compiling", key.pkg)?;
                }
            }
            Fresh if self.counts[key.pkg] == 0 => {
                self.compiled.insert(key.pkg);
                config.shell().verbose(|c| c.status("Fresh", key.pkg))?;
            }
            Fresh => {}
        }
        Ok(())
    }
}

impl<'a> Key<'a> {
    fn new(unit: &Unit<'a>) -> Key<'a> {
        Key {
            pkg: unit.pkg.package_id(),
            target: unit.target,
            profile: unit.profile,
            kind: unit.kind,
        }
    }

    fn dependencies<'cfg>(&self, cx: &Context<'a, 'cfg>)
                          -> CargoResult<Vec<Key<'a>>> {
        let unit = Unit {
            pkg: cx.get_package(self.pkg)?,
            target: self.target,
            profile: self.profile,
            kind: self.kind,
        };
        let targets = cx.dep_targets(&unit)?;
        Ok(targets.iter().filter_map(|unit| {
            // Binaries aren't actually needed to *compile* tests, just to run
            // them, so we don't include this dependency edge in the job graph.
            if self.target.is_test() && unit.target.is_bin() {
                None
            } else {
                Some(Key::new(unit))
            }
        }).collect())
    }
}

impl<'a> fmt::Debug for Key<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{} => {}/{} => {:?}", self.pkg, self.target, self.profile,
               self.kind)
    }
}
<|endoftext|>