<commit_before>// Copyright Istio Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::identity;
use std::net::{IpAddr, Ipv6Addr, SocketAddr};
use std::path::PathBuf;
use std::time::Duration;
use tokio::time;

#[derive(Clone, Debug)]
pub struct Config {
    pub tls: bool,

    pub window_size: u32,
    pub connection_window_size: u32,
    pub frame_size: u32,

    pub socks5_addr: SocketAddr,
    pub admin_addr: SocketAddr,
    pub inbound_addr: SocketAddr,
    pub inbound_plaintext_addr: SocketAddr,
    pub outbound_addr: SocketAddr,

    /// The name of the node this ztunnel is running as.
    pub local_node: Option<String>,

    /// XDS address to use. If unset, XDS will not be used.
    pub xds_address: Option<String>,
    /// Filepath to a local xds file for workloads, as YAML.
    pub local_xds_path: Option<String>,
    /// If true, on-demand XDS will be used
    pub xds_on_demand: bool,

    pub auth: identity::AuthSource,

    pub termination_grace_period: time::Duration,

    /// Specify the number of worker threads the Tokio Runtime will use.
    pub num_worker_threads: usize,
}

const DEFAULT_WOKRER_THREADS: usize = 2;

impl Default for Config {
    fn default() -> Config {
        // TODO: copy JWT auth logic from CA client and use TLS here (port 15012)
        let xds_address = Some(if std::env::var("KUBERNETES_SERVICE_HOST").is_ok() {
            "http://istiod.istio-system:15010".to_string()
        } else {
            "http://localhost:15010".to_string()
        });
        Config {
            tls: std::env::var("TLS").unwrap_or_else(|_| "".into()) != "off",
            window_size: 4 * 1024 * 1024,
            connection_window_size: 4 * 1024 * 1024,
            frame_size: 1024 * 1024,

            termination_grace_period: Duration::from_secs(5),

            admin_addr: SocketAddr::new(IpAddr::V6(Ipv6Addr::UNSPECIFIED), 15021),
            socks5_addr: SocketAddr::new(IpAddr::V6(Ipv6Addr::UNSPECIFIED), 15080),
            inbound_addr: SocketAddr::new(IpAddr::V6(Ipv6Addr::UNSPECIFIED), 15008),
            inbound_plaintext_addr: SocketAddr::new(IpAddr::V6(Ipv6Addr::UNSPECIFIED), 15006),
            outbound_addr: SocketAddr::new(IpAddr::V6(Ipv6Addr::UNSPECIFIED), 15001),

            local_node: Some(std::env::var("NODE_NAME").unwrap_or_else(|_| "".into()))
                .filter(|s| !s.is_empty()),

            xds_address,
            local_xds_path: Some(std::env::var("LOCAL_XDS_PATH").unwrap_or_else(|_| "".into()))
                .filter(|s| !s.is_empty()),
            xds_on_demand: std::env::var("XDS_ON_DEMAND").unwrap_or_else(|_| "".into()) == "on",

            auth: identity::AuthSource::Token(PathBuf::from(
                r"./var/run/secrets/tokens/istio-token",
            )),

            num_worker_threads: std::env::var("ZTUNNEL_WOKRER_THREADS")
                .ok()
                .map(|v| {
                    v.parse::<usize>()
                        .ok()
                        .filter(|n| *n > 0)
                        .unwrap_or(DEFAULT_WOKRER_THREADS)
                })
                .unwrap_or(DEFAULT_WOKRER_THREADS),
        }
    }
}
<commit_msg>Simplify processing of environment variables. (#180)<commit_after>// Copyright Istio Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::identity;
use std::net::{IpAddr, Ipv6Addr, SocketAddr};
use std::path::PathBuf;
use std::time::Duration;
use tokio::time;

#[derive(Clone, Debug)]
pub struct Config {
    pub tls: bool,

    pub window_size: u32,
    pub connection_window_size: u32,
    pub frame_size: u32,

    pub socks5_addr: SocketAddr,
    pub admin_addr: SocketAddr,
    pub inbound_addr: SocketAddr,
    pub inbound_plaintext_addr: SocketAddr,
    pub outbound_addr: SocketAddr,

    /// The name of the node this ztunnel is running as.
    pub local_node: Option<String>,

    /// XDS address to use. If unset, XDS will not be used.
    pub xds_address: Option<String>,
    /// Filepath to a local xds file for workloads, as YAML.
    pub local_xds_path: Option<String>,
    /// If true, on-demand XDS will be used
    pub xds_on_demand: bool,

    pub auth: identity::AuthSource,

    pub termination_grace_period: time::Duration,

    /// Specify the number of worker threads the Tokio Runtime will use.
    pub num_worker_threads: usize,
}

const DEFAULT_WOKRER_THREADS: usize = 2;

impl Default for Config {
    fn default() -> Config {
        // TODO: copy JWT auth logic from CA client and use TLS here (port 15012)
        let xds_address = Some(if std::env::var("KUBERNETES_SERVICE_HOST").is_ok() {
            "http://istiod.istio-system:15010".to_string()
        } else {
            "http://localhost:15010".to_string()
        });
        Config {
            tls: std::env::var("TLS").ok().as_deref() != Some("off"),

            window_size: 4 * 1024 * 1024,
            connection_window_size: 4 * 1024 * 1024,
            frame_size: 1024 * 1024,

            termination_grace_period: Duration::from_secs(5),

            admin_addr: SocketAddr::new(IpAddr::V6(Ipv6Addr::UNSPECIFIED), 15021),
            socks5_addr: SocketAddr::new(IpAddr::V6(Ipv6Addr::UNSPECIFIED), 15080),
            inbound_addr: SocketAddr::new(IpAddr::V6(Ipv6Addr::UNSPECIFIED), 15008),
            inbound_plaintext_addr: SocketAddr::new(IpAddr::V6(Ipv6Addr::UNSPECIFIED), 15006),
            outbound_addr: SocketAddr::new(IpAddr::V6(Ipv6Addr::UNSPECIFIED), 15001),

            local_node: std::env::var("NODE_NAME").ok(),

            xds_address,
            local_xds_path: std::env::var("LOCAL_XDS_PATH").ok(),
            xds_on_demand: std::env::var("XDS_ON_DEMAND").ok().as_deref() == Some("on"),

            auth: identity::AuthSource::Token(PathBuf::from(
                r"./var/run/secrets/tokens/istio-token",
            )),

            num_worker_threads: std::env::var("ZTUNNEL_WOKRER_THREADS")
                .ok()
                .map(|v| {
                    v.parse::<usize>()
                        .ok()
                        .filter(|n| *n > 0)
                        .unwrap_or(DEFAULT_WOKRER_THREADS)
                })
                .unwrap_or(DEFAULT_WOKRER_THREADS),
        }
    }
}
<|endoftext|>


<commit_before>use std::any::Any;
use std::fmt;

#[cfg(feature = "bytes")]
use crate::bytes::Bytes;
#[cfg(feature = "bytes")]
use crate::chars::Chars;

use crate::reflect::runtime_types::RuntimeType;
use crate::reflect::runtime_types::RuntimeTypeBool;
#[cfg(feature = "bytes")]
use crate::reflect::runtime_types::RuntimeTypeCarllercheBytes;
#[cfg(feature = "bytes")]
use crate::reflect::runtime_types::RuntimeTypeCarllercheChars;
use crate::reflect::runtime_types::RuntimeTypeF32;
use crate::reflect::runtime_types::RuntimeTypeF64;
use crate::reflect::runtime_types::RuntimeTypeI32;
use crate::reflect::runtime_types::RuntimeTypeI64;
use crate::reflect::runtime_types::RuntimeTypeString;
use crate::reflect::runtime_types::RuntimeTypeU32;
use crate::reflect::runtime_types::RuntimeTypeU64;
use crate::reflect::runtime_types::RuntimeTypeVecU8;
use crate::reflect::value::value_box::ReflectValueBox;
use crate::reflect::value::value_ref::ReflectValueMut;
use crate::reflect::value::value_ref::ReflectValueRef;
use crate::reflect::RuntimeTypeBox;

pub(crate) mod hashable;
pub(crate) mod value_box;
pub(crate) mod value_ref;

/// Type implemented by all protobuf singular types
/// (primitives, string, messages, enums).
///
/// Used for dynamic casting in reflection.
pub trait ProtobufValue: Any + 'static + Send + Sync {}

/// Sized version of [`ProtobufValue`].
pub trait ProtobufValueSized:
    Any + Clone + Default + fmt::Debug + Send + Sync + Sized + 'static
{
    /// Actual implementation of type properties.
    type RuntimeType: RuntimeType<Value = Self>;

    // TODO: inline the rest

    /// Dynamic version of the type.
    fn runtime_type_box() -> RuntimeTypeBox {
        Self::RuntimeType::runtime_type_box()
    }

    /// Pointer to a dynamic reference.
    fn as_ref(value: &Self) -> ReflectValueRef {
        Self::RuntimeType::as_ref(value)
    }

    /// Mutable pointer to a dynamic mutable reference.
    fn as_mut(value: &mut Self) -> ReflectValueMut {
        Self::RuntimeType::as_mut(value)
    }

    /// Construct a value from given reflective value.
    ///
    /// # Panics
    ///
    /// If reflective value is of incompatible type.
    fn from_value_box(value_box: ReflectValueBox) -> Result<Self, ReflectValueBox> {
        Self::RuntimeType::from_value_box(value_box)
    }

    /// Write the value.
    fn set_from_value_box(target: &mut Self, value_box: ReflectValueBox) {
        Self::RuntimeType::set_from_value_box(target, value_box)
    }

    /// Default value for this type.
    fn default_value_ref() -> ReflectValueRef<'static> {
        Self::RuntimeType::default_value_ref()
    }

    /// Convert a value into a ref value if possible.
    ///
    /// # Panics
    ///
    /// For message and enum.
    fn into_static_value_ref(value: Self) -> ReflectValueRef<'static> {
        Self::RuntimeType::into_static_value_ref(value)
    }

    /// Value is non-default?
    fn is_non_zero(value: &Self) -> bool {
        Self::RuntimeType::is_non_zero(value)
    }
}

impl ProtobufValue for u32 {}

impl ProtobufValueSized for u32 {
    type RuntimeType = RuntimeTypeU32;
}

impl ProtobufValue for u64 {}
impl ProtobufValueSized for u64 {
    type RuntimeType = RuntimeTypeU64;
}

impl ProtobufValue for i32 {}
impl ProtobufValueSized for i32 {
    type RuntimeType = RuntimeTypeI32;
}

impl ProtobufValue for i64 {}
impl ProtobufValueSized for i64 {
    type RuntimeType = RuntimeTypeI64;
}

impl ProtobufValue for f32 {}
impl ProtobufValueSized for f32 {
    type RuntimeType = RuntimeTypeF32;
}

impl ProtobufValue for f64 {}
impl ProtobufValueSized for f64 {
    type RuntimeType = RuntimeTypeF64;
}

impl ProtobufValue for bool {}
impl ProtobufValueSized for bool {
    type RuntimeType = RuntimeTypeBool;
}

impl ProtobufValue for String {}
impl ProtobufValueSized for String {
    type RuntimeType = RuntimeTypeString;
}

impl ProtobufValue for Vec<u8> {}
impl ProtobufValueSized for Vec<u8> {
    type RuntimeType = RuntimeTypeVecU8;
}

#[cfg(feature = "bytes")]
impl ProtobufValue for Bytes {}
#[cfg(feature = "bytes")]
impl ProtobufValueSized for Bytes {
    type RuntimeType = RuntimeTypeCarllercheBytes;
}

#[cfg(feature = "bytes")]
impl ProtobufValue for Chars {}
#[cfg(feature = "bytes")]
impl ProtobufValueSized for Chars {
    type RuntimeType = RuntimeTypeCarllercheChars;
}

// conflicting implementations, so generated code is used instead
/*
impl<E : ProtobufEnum> ProtobufValue for E {
}

impl<M : Message> ProtobufValue for M {
}
*/

#[cfg(not(feature = "bytes"))]
type VecU8OrBytes = Vec<u8>;
#[cfg(feature = "bytes")]
type VecU8OrBytes = Vec<u8>;
#[cfg(not(feature = "bytes"))]
type StringOrChars = String;
#[cfg(feature = "bytes")]
type StringOrChars = Chars;
<commit_msg>Unimplement ProtobufValue for builtin types<commit_after>use std::any::Any;
use std::fmt;

#[cfg(feature = "bytes")]
use crate::bytes::Bytes;
#[cfg(feature = "bytes")]
use crate::chars::Chars;

use crate::reflect::runtime_types::RuntimeType;
use crate::reflect::runtime_types::RuntimeTypeBool;
#[cfg(feature = "bytes")]
use crate::reflect::runtime_types::RuntimeTypeCarllercheBytes;
#[cfg(feature = "bytes")]
use crate::reflect::runtime_types::RuntimeTypeCarllercheChars;
use crate::reflect::runtime_types::RuntimeTypeF32;
use crate::reflect::runtime_types::RuntimeTypeF64;
use crate::reflect::runtime_types::RuntimeTypeI32;
use crate::reflect::runtime_types::RuntimeTypeI64;
use crate::reflect::runtime_types::RuntimeTypeString;
use crate::reflect::runtime_types::RuntimeTypeU32;
use crate::reflect::runtime_types::RuntimeTypeU64;
use crate::reflect::runtime_types::RuntimeTypeVecU8;
use crate::reflect::value::value_box::ReflectValueBox;
use crate::reflect::value::value_ref::ReflectValueMut;
use crate::reflect::value::value_ref::ReflectValueRef;
use crate::reflect::RuntimeTypeBox;

pub(crate) mod hashable;
pub(crate) mod value_box;
pub(crate) mod value_ref;

/// Type implemented by all protobuf singular types
/// (primitives, string, messages, enums).
///
/// Used for dynamic casting in reflection.
pub trait ProtobufValue: Any + 'static + Send + Sync {}

/// Sized version of [`ProtobufValue`].
pub trait ProtobufValueSized:
    Any + Clone + Default + fmt::Debug + Send + Sync + Sized + 'static
{
    /// Actual implementation of type properties.
    type RuntimeType: RuntimeType<Value = Self>;

    // TODO: inline the rest

    /// Dynamic version of the type.
    fn runtime_type_box() -> RuntimeTypeBox {
        Self::RuntimeType::runtime_type_box()
    }

    /// Pointer to a dynamic reference.
    fn as_ref(value: &Self) -> ReflectValueRef {
        Self::RuntimeType::as_ref(value)
    }

    /// Mutable pointer to a dynamic mutable reference.
    fn as_mut(value: &mut Self) -> ReflectValueMut {
        Self::RuntimeType::as_mut(value)
    }

    /// Construct a value from given reflective value.
    ///
    /// # Panics
    ///
    /// If reflective value is of incompatible type.
    fn from_value_box(value_box: ReflectValueBox) -> Result<Self, ReflectValueBox> {
        Self::RuntimeType::from_value_box(value_box)
    }

    /// Write the value.
    fn set_from_value_box(target: &mut Self, value_box: ReflectValueBox) {
        Self::RuntimeType::set_from_value_box(target, value_box)
    }

    /// Default value for this type.
    fn default_value_ref() -> ReflectValueRef<'static> {
        Self::RuntimeType::default_value_ref()
    }

    /// Convert a value into a ref value if possible.
    ///
    /// # Panics
    ///
    /// For message and enum.
    fn into_static_value_ref(value: Self) -> ReflectValueRef<'static> {
        Self::RuntimeType::into_static_value_ref(value)
    }

    /// Value is non-default?
    fn is_non_zero(value: &Self) -> bool {
        Self::RuntimeType::is_non_zero(value)
    }
}

impl ProtobufValueSized for u32 {
    type RuntimeType = RuntimeTypeU32;
}

impl ProtobufValueSized for u64 {
    type RuntimeType = RuntimeTypeU64;
}

impl ProtobufValueSized for i32 {
    type RuntimeType = RuntimeTypeI32;
}

impl ProtobufValueSized for i64 {
    type RuntimeType = RuntimeTypeI64;
}

impl ProtobufValueSized for f32 {
    type RuntimeType = RuntimeTypeF32;
}

impl ProtobufValueSized for f64 {
    type RuntimeType = RuntimeTypeF64;
}

impl ProtobufValueSized for bool {
    type RuntimeType = RuntimeTypeBool;
}

impl ProtobufValueSized for String {
    type RuntimeType = RuntimeTypeString;
}

impl ProtobufValueSized for Vec<u8> {
    type RuntimeType = RuntimeTypeVecU8;
}

#[cfg(feature = "bytes")]
impl ProtobufValueSized for Bytes {
    type RuntimeType = RuntimeTypeCarllercheBytes;
}

#[cfg(feature = "bytes")]
impl ProtobufValueSized for Chars {
    type RuntimeType = RuntimeTypeCarllercheChars;
}

// conflicting implementations, so generated code is used instead
/*
impl<E : ProtobufEnum> ProtobufValue for E {
}

impl<M : Message> ProtobufValue for M {
}
*/

#[cfg(not(feature = "bytes"))]
type VecU8OrBytes = Vec<u8>;
#[cfg(feature = "bytes")]
type VecU8OrBytes = Vec<u8>;
#[cfg(not(feature = "bytes"))]
type StringOrChars = String;
#[cfg(feature = "bytes")]
type StringOrChars = Chars;
<|endoftext|>


<commit_before>use std::fmt;
use std::fmt::Write;

use protobuf_support::text_format::quote_bytes_to;

use crate::message_dyn::MessageDyn;
use crate::reflect::MessageRef;
use crate::reflect::ReflectFieldRef;
use crate::reflect::ReflectValueRef;

fn print_str_to(s: &str, buf: &mut String) {
    // TODO: keep printable Unicode
    quote_bytes_to(s.as_bytes(), buf);
}

fn do_indent(buf: &mut String, pretty: bool, indent: usize) {
    if pretty && indent > 0 {
        for _ in 0..indent {
            buf.push_str("  ");
        }
    }
}

trait FieldName: fmt::Display {}
impl<'a> FieldName for &'a str {}
impl FieldName for u32 {}

fn print_start_field<F: FieldName>(
    buf: &mut String,
    pretty: bool,
    indent: usize,
    first: &mut bool,
    field_name: F,
) {
    if !*first && !pretty {
        buf.push_str(" ");
    }
    do_indent(buf, pretty, indent);
    *first = false;
    write!(buf, "{}", field_name).unwrap();
}

fn print_end_field(buf: &mut String, pretty: bool) {
    if pretty {
        buf.push_str("\n");
    }
}

fn print_field<F: FieldName>(
    buf: &mut String,
    pretty: bool,
    indent: usize,
    first: &mut bool,
    field_name: F,
    value: ReflectValueRef,
) {
    print_start_field(buf, pretty, indent, first, field_name);

    match value {
        ReflectValueRef::Message(m) => {
            buf.push_str(" {");
            if pretty {
                buf.push_str("\n");
            }
            print_to_internal(&m, buf, pretty, indent + 1);
            do_indent(buf, pretty, indent);
            buf.push_str("}");
        }
        ReflectValueRef::Enum(d, v) => {
            buf.push_str(": ");
            match d.value_by_number(v) {
                Some(e) => buf.push_str(e.name()),
                None => write!(buf, ": {}", v).unwrap(),
            }
        }
        ReflectValueRef::String(s) => {
            buf.push_str(": ");
            print_str_to(s, buf);
        }
        ReflectValueRef::Bytes(b) => {
            buf.push_str(": ");
            quote_bytes_to(b, buf);
        }
        ReflectValueRef::I32(v) => {
            write!(buf, ": {}", v).unwrap();
        }
        ReflectValueRef::I64(v) => {
            write!(buf, ": {}", v).unwrap();
        }
        ReflectValueRef::U32(v) => {
            write!(buf, ": {}", v).unwrap();
        }
        ReflectValueRef::U64(v) => {
            write!(buf, ": {}", v).unwrap();
        }
        ReflectValueRef::Bool(v) => {
            write!(buf, ": {}", v).unwrap();
        }
        ReflectValueRef::F32(v) => {
            write!(buf, ": {}", v).unwrap();
        }
        ReflectValueRef::F64(v) => {
            write!(buf, ": {}", v).unwrap();
        }
    }

    print_end_field(buf, pretty);
}

fn print_to_internal(m: &MessageRef, buf: &mut String, pretty: bool, indent: usize) {
    let d = m.descriptor_dyn();
    let mut first = true;
    for f in d.fields() {
        match f.get_reflect(&**m) {
            ReflectFieldRef::Map(map) => {
                for (k, v) in &map {
                    print_start_field(buf, pretty, indent, &mut first, f.name());
                    buf.push_str(" {");
                    if pretty {
                        buf.push_str("\n");
                    }

                    let mut entry_first = true;

                    print_field(buf, pretty, indent + 1, &mut entry_first, "key", k);
                    print_field(buf, pretty, indent + 1, &mut entry_first, "value", v);
                    do_indent(buf, pretty, indent);
                    buf.push_str("}");
                    print_end_field(buf, pretty);
                }
            }
            ReflectFieldRef::Repeated(repeated) => {
                // TODO: do not print zeros for v3
                for v in repeated {
                    print_field(buf, pretty, indent, &mut first, f.name(), v);
                }
            }
            ReflectFieldRef::Optional(optional) => {
                if let Some(v) = optional {
                    print_field(buf, pretty, indent, &mut first, f.name(), v);
                }
            }
        }
    }

    let unknown_fields = m.unknown_fields_dyn();
    let mut numbers: Vec<u32> = m.unknown_fields_dyn().iter().map(|(n, _)| n).collect();
    // Sort for stable output
    numbers.sort();
    for &n in &numbers {
        for v in unknown_fields.get(n).unwrap() {
            // TODO: try decode nested message for length-delimited
            print_field(buf, pretty, indent, &mut first, n, v.to_reflect_value_ref());
        }
    }
}

/// Text-format
pub fn print_to(m: &dyn MessageDyn, buf: &mut String) {
    print_to_internal(&MessageRef::from(m), buf, false, 0)
}

fn print_to_string_internal(m: &dyn MessageDyn, pretty: bool) -> String {
    let mut r = String::new();
    print_to_internal(&MessageRef::from(m), &mut r, pretty, 0);
    r.to_string()
}

/// Text-format
pub fn print_to_string(m: &dyn MessageDyn) -> String {
    print_to_string_internal(m, false)
}

/// Text-format
pub fn print_to_string_pretty(m: &dyn MessageDyn) -> String {
    print_to_string_internal(m, true)
}

/// Text-format to `fmt::Formatter`.
pub fn fmt(m: &dyn MessageDyn, f: &mut fmt::Formatter) -> fmt::Result {
    let pretty = f.alternate();
    f.write_str(&print_to_string_internal(m, pretty))
}
<commit_msg>Outdated todo<commit_after>use std::fmt;
use std::fmt::Write;

use protobuf_support::text_format::quote_bytes_to;

use crate::message_dyn::MessageDyn;
use crate::reflect::MessageRef;
use crate::reflect::ReflectFieldRef;
use crate::reflect::ReflectValueRef;

fn print_str_to(s: &str, buf: &mut String) {
    // TODO: keep printable Unicode
    quote_bytes_to(s.as_bytes(), buf);
}

fn do_indent(buf: &mut String, pretty: bool, indent: usize) {
    if pretty && indent > 0 {
        for _ in 0..indent {
            buf.push_str("  ");
        }
    }
}

trait FieldName: fmt::Display {}
impl<'a> FieldName for &'a str {}
impl FieldName for u32 {}

fn print_start_field<F: FieldName>(
    buf: &mut String,
    pretty: bool,
    indent: usize,
    first: &mut bool,
    field_name: F,
) {
    if !*first && !pretty {
        buf.push_str(" ");
    }
    do_indent(buf, pretty, indent);
    *first = false;
    write!(buf, "{}", field_name).unwrap();
}

fn print_end_field(buf: &mut String, pretty: bool) {
    if pretty {
        buf.push_str("\n");
    }
}

fn print_field<F: FieldName>(
    buf: &mut String,
    pretty: bool,
    indent: usize,
    first: &mut bool,
    field_name: F,
    value: ReflectValueRef,
) {
    print_start_field(buf, pretty, indent, first, field_name);

    match value {
        ReflectValueRef::Message(m) => {
            buf.push_str(" {");
            if pretty {
                buf.push_str("\n");
            }
            print_to_internal(&m, buf, pretty, indent + 1);
            do_indent(buf, pretty, indent);
            buf.push_str("}");
        }
        ReflectValueRef::Enum(d, v) => {
            buf.push_str(": ");
            match d.value_by_number(v) {
                Some(e) => buf.push_str(e.name()),
                None => write!(buf, ": {}", v).unwrap(),
            }
        }
        ReflectValueRef::String(s) => {
            buf.push_str(": ");
            print_str_to(s, buf);
        }
        ReflectValueRef::Bytes(b) => {
            buf.push_str(": ");
            quote_bytes_to(b, buf);
        }
        ReflectValueRef::I32(v) => {
            write!(buf, ": {}", v).unwrap();
        }
        ReflectValueRef::I64(v) => {
            write!(buf, ": {}", v).unwrap();
        }
        ReflectValueRef::U32(v) => {
            write!(buf, ": {}", v).unwrap();
        }
        ReflectValueRef::U64(v) => {
            write!(buf, ": {}", v).unwrap();
        }
        ReflectValueRef::Bool(v) => {
            write!(buf, ": {}", v).unwrap();
        }
        ReflectValueRef::F32(v) => {
            write!(buf, ": {}", v).unwrap();
        }
        ReflectValueRef::F64(v) => {
            write!(buf, ": {}", v).unwrap();
        }
    }

    print_end_field(buf, pretty);
}

fn print_to_internal(m: &MessageRef, buf: &mut String, pretty: bool, indent: usize) {
    let d = m.descriptor_dyn();
    let mut first = true;
    for f in d.fields() {
        match f.get_reflect(&**m) {
            ReflectFieldRef::Map(map) => {
                for (k, v) in &map {
                    print_start_field(buf, pretty, indent, &mut first, f.name());
                    buf.push_str(" {");
                    if pretty {
                        buf.push_str("\n");
                    }

                    let mut entry_first = true;

                    print_field(buf, pretty, indent + 1, &mut entry_first, "key", k);
                    print_field(buf, pretty, indent + 1, &mut entry_first, "value", v);
                    do_indent(buf, pretty, indent);
                    buf.push_str("}");
                    print_end_field(buf, pretty);
                }
            }
            ReflectFieldRef::Repeated(repeated) => {
                for v in repeated {
                    print_field(buf, pretty, indent, &mut first, f.name(), v);
                }
            }
            ReflectFieldRef::Optional(optional) => {
                if let Some(v) = optional {
                    print_field(buf, pretty, indent, &mut first, f.name(), v);
                }
            }
        }
    }

    let unknown_fields = m.unknown_fields_dyn();
    let mut numbers: Vec<u32> = m.unknown_fields_dyn().iter().map(|(n, _)| n).collect();
    // Sort for stable output
    numbers.sort();
    for &n in &numbers {
        for v in unknown_fields.get(n).unwrap() {
            // TODO: try decode nested message for length-delimited
            print_field(buf, pretty, indent, &mut first, n, v.to_reflect_value_ref());
        }
    }
}

/// Text-format
pub fn print_to(m: &dyn MessageDyn, buf: &mut String) {
    print_to_internal(&MessageRef::from(m), buf, false, 0)
}

fn print_to_string_internal(m: &dyn MessageDyn, pretty: bool) -> String {
    let mut r = String::new();
    print_to_internal(&MessageRef::from(m), &mut r, pretty, 0);
    r.to_string()
}

/// Text-format
pub fn print_to_string(m: &dyn MessageDyn) -> String {
    print_to_string_internal(m, false)
}

/// Text-format
pub fn print_to_string_pretty(m: &dyn MessageDyn) -> String {
    print_to_string_internal(m, true)
}

/// Text-format to `fmt::Formatter`.
pub fn fmt(m: &dyn MessageDyn, f: &mut fmt::Formatter) -> fmt::Result {
    let pretty = f.alternate();
    f.write_str(&print_to_string_internal(m, pretty))
}
<|endoftext|>


<commit_before>// FIXME: Use bindgen

extern mod cocoa;

use cocoa::cg::CGFontRef;

#[nolink]
pub extern mod bindgen {
    fn cairo_quartz_font_face_create_for_cgfont(font: CGFontRef) -> *cairo::cairo_font_face_t;
}
<commit_msg>Update references to CoreGraphics bindings.<commit_after>// FIXME: Use bindgen

extern mod core_graphics;

use core_graphics::font::CGFontRef;

#[nolink]
pub extern mod bindgen {
    fn cairo_quartz_font_face_create_for_cgfont(font: CGFontRef) -> *cairo::cairo_font_face_t;
}
<|endoftext|>


<commit_before>use std::any::Any;
use std::ffi::CString;
use std::os::raw::{c_char, c_int};

use rustc::middle::cstore::EncodedMetadata;
use rustc::mir::mono::{Linkage as RLinkage, Visibility};
use rustc::session::config::{DebugInfo, OutputType};
use rustc_codegen_ssa::back::linker::LinkerInfo;
use rustc_codegen_ssa::CrateInfo;

use crate::prelude::*;

use crate::backend::{Emit, WriteDebugInfo};

pub fn codegen_crate(
    tcx: TyCtxt<'_>,
    metadata: EncodedMetadata,
    need_metadata_module: bool,
) -> Box<dyn Any> {
    tcx.sess.abort_if_errors();

    if std::env::var("SHOULD_RUN").is_ok()
        && tcx.sess.crate_types.get().contains(&CrateType::Executable)
    {
        #[cfg(not(target_arch = "wasm32"))]
        let _: ! = run_jit(tcx);

        #[cfg(target_arch = "wasm32")]
        panic!("jit not supported on wasm");
    }

    run_aot(tcx, metadata, need_metadata_module)
}

#[cfg(not(target_arch = "wasm32"))]
fn run_jit(tcx: TyCtxt<'_>) -> ! {
    use cranelift_simplejit::{SimpleJITBackend, SimpleJITBuilder};

    let imported_symbols = load_imported_symbols_for_jit(tcx);

    let mut jit_builder = SimpleJITBuilder::with_isa(
        crate::build_isa(tcx.sess, false),
        cranelift_module::default_libcall_names(),
    );
    jit_builder.symbols(imported_symbols);
    let mut jit_module: Module<SimpleJITBackend> = Module::new(jit_builder);
    assert_eq!(pointer_ty(tcx), jit_module.target_config().pointer_type());

    let sig = Signature {
        params: vec![
            AbiParam::new(jit_module.target_config().pointer_type()),
            AbiParam::new(jit_module.target_config().pointer_type()),
        ],
        returns: vec![AbiParam::new(
            jit_module.target_config().pointer_type(), /*isize*/
        )],
        call_conv: CallConv::triple_default(&crate::target_triple(tcx.sess)),
    };
    let main_func_id = jit_module
        .declare_function("main", Linkage::Import, &sig)
        .unwrap();

    codegen_cgus(tcx, &mut jit_module, &mut None);
    crate::allocator::codegen(tcx, &mut jit_module);
    jit_module.finalize_definitions();

    tcx.sess.abort_if_errors();

    let finalized_main: *const u8 = jit_module.get_finalized_function(main_func_id);

    println!("Rustc codegen cranelift will JIT run the executable, because the SHOULD_RUN env var is set");

    let f: extern "C" fn(c_int, *const *const c_char) -> c_int =
        unsafe { ::std::mem::transmute(finalized_main) };

    let args = ::std::env::var("JIT_ARGS").unwrap_or_else(|_| String::new());
    let args = args
        .split(" ")
        .chain(Some(&*tcx.crate_name(LOCAL_CRATE).as_str().to_string()))
        .map(|arg| CString::new(arg).unwrap())
        .collect::<Vec<_>>();
    let argv = args.iter().map(|arg| arg.as_ptr()).collect::<Vec<_>>();
    // TODO: Rust doesn't care, but POSIX argv has a NULL sentinel at the end

    let ret = f(args.len() as c_int, argv.as_ptr());

    jit_module.finish();
    std::process::exit(ret);
}

fn load_imported_symbols_for_jit(tcx: TyCtxt<'_>) -> Vec<(String, *const u8)> {
    use rustc::middle::dependency_format::Linkage;

    let mut dylib_paths = Vec::new();

    let crate_info = CrateInfo::new(tcx);
    let formats = tcx.dependency_formats(LOCAL_CRATE);
    let data = &formats
        .iter()
        .find(|(crate_type, _data)| *crate_type == CrateType::Executable)
        .unwrap()
        .1;
    for &(cnum, _) in &crate_info.used_crates_dynamic {
        let src = &crate_info.used_crate_source[&cnum];
        match data[cnum.as_usize() - 1] {
            Linkage::NotLinked | Linkage::IncludedFromDylib => {}
            Linkage::Static => {
                let name = tcx.crate_name(cnum);
                let mut err = tcx
                    .sess
                    .struct_err(&format!("Can't load static lib {}", name.as_str()));
                err.note("rustc_codegen_cranelift can only load dylibs in JIT mode.");
                err.emit();
            }
            Linkage::Dynamic => {
                dylib_paths.push(src.dylib.as_ref().unwrap().0.clone());
            }
        }
    }

    let mut imported_symbols = Vec::new();
    for path in dylib_paths {
        use object::Object;
        let lib = libloading::Library::new(&path).unwrap();
        let obj = std::fs::read(path).unwrap();
        let obj = object::File::parse(&obj).unwrap();
        imported_symbols.extend(obj.dynamic_symbols().filter_map(|(_idx, symbol)| {
            let name = symbol.name().unwrap().to_string();
            if name.is_empty() || !symbol.is_global() || symbol.is_undefined() {
                return None;
            }
            let symbol: libloading::Symbol<*const u8> =
                unsafe { lib.get(name.as_bytes()) }.unwrap();
            Some((name, *symbol))
        }));
        std::mem::forget(lib)
    }

    tcx.sess.abort_if_errors();

    imported_symbols
}

fn run_aot(
    tcx: TyCtxt<'_>,
    metadata: EncodedMetadata,
    need_metadata_module: bool,
) -> Box<CodegenResults> {
    let new_module = |name: String| {
        let module = crate::backend::make_module(tcx.sess, name);
        assert_eq!(pointer_ty(tcx), module.target_config().pointer_type());
        module
    };

    fn emit_module<B: Backend>(
        tcx: TyCtxt<'_>,
        name: String,
        kind: ModuleKind,
        mut module: Module<B>,
        debug: Option<DebugContext>,
    ) -> CompiledModule
        where B::Product: Emit + WriteDebugInfo,
    {
            module.finalize_definitions();
            let mut product = module.finish();

            if let Some(mut debug) = debug {
                debug.emit(&mut product);
            }

            let tmp_file = tcx
                .output_filenames(LOCAL_CRATE)
                .temp_path(OutputType::Object, Some(&name));
            let obj = product.emit();
            std::fs::write(&tmp_file, obj).unwrap();
            CompiledModule {
                name,
                kind,
                object: Some(tmp_file),
                bytecode: None,
                bytecode_compressed: None,
            }
        };

    let mut module = new_module("some_file".to_string());

    let mut debug = if tcx.sess.opts.debuginfo != DebugInfo::None {
        let debug = DebugContext::new(
            tcx,
            module.target_config().pointer_type().bytes() as u8,
        );
        Some(debug)
    } else {
        None
    };

    codegen_cgus(tcx, &mut module, &mut debug);

    tcx.sess.abort_if_errors();

    let mut allocator_module = new_module("allocator_shim".to_string());
    let created_alloc_shim = crate::allocator::codegen(tcx, &mut allocator_module);

    rustc_incremental::assert_dep_graph(tcx);
    rustc_incremental::save_dep_graph(tcx);
    rustc_incremental::finalize_session_directory(tcx.sess, tcx.crate_hash(LOCAL_CRATE));

    let metadata_module = if need_metadata_module {
        let _timer = tcx.prof.generic_activity("codegen crate metadata");
        let (metadata_cgu_name, tmp_file) = tcx.sess.time("write compressed metadata", || {
            use rustc::mir::mono::CodegenUnitNameBuilder;

            let cgu_name_builder = &mut CodegenUnitNameBuilder::new(tcx);
            let metadata_cgu_name = cgu_name_builder
                .build_cgu_name(LOCAL_CRATE, &["crate"], Some("metadata"))
                .as_str()
                .to_string();

            let tmp_file = tcx
                .output_filenames(LOCAL_CRATE)
                .temp_path(OutputType::Metadata, Some(&metadata_cgu_name));

            let obj = crate::backend::with_object(tcx.sess, &metadata_cgu_name, |object| {
                crate::metadata::write_metadata(tcx, object);
            });

            std::fs::write(&tmp_file, obj).unwrap();

            (metadata_cgu_name, tmp_file)
        });

        Some(CompiledModule {
            name: metadata_cgu_name,
            kind: ModuleKind::Metadata,
            object: Some(tmp_file),
            bytecode: None,
            bytecode_compressed: None,
        })
    } else {
        None
    };

    Box::new(CodegenResults {
        crate_name: tcx.crate_name(LOCAL_CRATE),
        modules: vec![emit_module(
            tcx,
            "some_file".to_string(),
            ModuleKind::Regular,
            module,
            debug,
        )],
        allocator_module: if created_alloc_shim {
            Some(emit_module(
                tcx,
                "allocator_shim".to_string(),
                ModuleKind::Allocator,
                allocator_module,
                None,
            ))
        } else {
            None
        },
        metadata_module,
        crate_hash: tcx.crate_hash(LOCAL_CRATE),
        metadata,
        windows_subsystem: None, // Windows is not yet supported
        linker_info: LinkerInfo::new(tcx),
        crate_info: CrateInfo::new(tcx),
    })
}

fn codegen_cgus<'tcx>(
    tcx: TyCtxt<'tcx>,
    module: &mut Module<impl Backend + 'static>,
    debug: &mut Option<DebugContext<'tcx>>,
) {
    let (_, cgus) = tcx.collect_and_partition_mono_items(LOCAL_CRATE);
    let mono_items = cgus
        .iter()
        .map(|cgu| cgu.items_in_deterministic_order(tcx).into_iter())
        .flatten()
        .collect::<FxHashMap<_, (_, _)>>();

    codegen_mono_items(tcx, module, debug.as_mut(), mono_items);

    crate::main_shim::maybe_create_entry_wrapper(tcx, module);
}

fn codegen_mono_items<'tcx>(
    tcx: TyCtxt<'tcx>,
    module: &mut Module<impl Backend + 'static>,
    debug_context: Option<&mut DebugContext<'tcx>>,
    mono_items: FxHashMap<MonoItem<'tcx>, (RLinkage, Visibility)>,
) {
    let mut cx = CodegenCx::new(tcx, module, debug_context);

    time(tcx.sess, "codegen mono items", move || {
        tcx.sess.time("predefine functions", || {
            for (&mono_item, &(linkage, visibility)) in &mono_items {
                match mono_item {
                    MonoItem::Fn(instance) => {
                        let (name, sig) =
                            get_function_name_and_sig(tcx, cx.module.isa().triple(), instance, false);
                        let linkage = crate::linkage::get_clif_linkage(mono_item, linkage, visibility);
                        cx.module.declare_function(&name, linkage, &sig).unwrap();
                    }
                    MonoItem::Static(_) | MonoItem::GlobalAsm(_) => {}
                }
            }
        });

        for (mono_item, (linkage, visibility)) in mono_items {
            crate::unimpl::try_unimpl(tcx, || {
                let linkage = crate::linkage::get_clif_linkage(mono_item, linkage, visibility);
                trans_mono_item(&mut cx, mono_item, linkage);
            });
        }

        tcx.sess.time("finalize CodegenCx", || cx.finalize());
    });
}

fn trans_mono_item<'clif, 'tcx, B: Backend + 'static>(
    cx: &mut crate::CodegenCx<'clif, 'tcx, B>,
    mono_item: MonoItem<'tcx>,
    linkage: Linkage,
) {
    let tcx = cx.tcx;
    match mono_item {
        MonoItem::Fn(inst) => {
            let _inst_guard =
                PrintOnPanic(|| format!("{:?} {}", inst, tcx.symbol_name(inst).name.as_str()));
            debug_assert!(!inst.substs.needs_infer());
            let _mir_guard = PrintOnPanic(|| {
                match inst.def {
                    InstanceDef::Item(_)
                    | InstanceDef::DropGlue(_, _)
                    | InstanceDef::Virtual(_, _) => {
                        let mut mir = ::std::io::Cursor::new(Vec::new());
                        crate::rustc_mir::util::write_mir_pretty(
                            tcx,
                            Some(inst.def_id()),
                            &mut mir,
                        )
                        .unwrap();
                        String::from_utf8(mir.into_inner()).unwrap()
                    }
                    _ => {
                        // FIXME fix write_mir_pretty for these instances
                        format!("{:#?}", tcx.instance_mir(inst.def))
                    }
                }
            });

            cx.tcx.sess.time("codegen fn", || crate::base::trans_fn(cx, inst, linkage));
        }
        MonoItem::Static(def_id) => {
            crate::constant::codegen_static(&mut cx.constants_cx, def_id);
        }
        MonoItem::GlobalAsm(node_id) => tcx
            .sess
            .fatal(&format!("Unimplemented global asm mono item {:?}", node_id)),
    }
}

fn time<R>(sess: &Session, name: &str, f: impl FnOnce() -> R) -> R {
    println!("[{}] start", name);
    let before = std::time::Instant::now();
    let res = sess.time(name, f);
    let after = std::time::Instant::now();
    println!("[{}] end time: {:?}", name, after - before);
    res
}
<commit_msg>Fix build<commit_after>use std::any::Any;
use std::ffi::CString;
use std::os::raw::{c_char, c_int};

use rustc::middle::cstore::EncodedMetadata;
use rustc::mir::mono::{Linkage as RLinkage, Visibility};
use rustc::session::config::{DebugInfo, OutputType};
use rustc_codegen_ssa::back::linker::LinkerInfo;
use rustc_codegen_ssa::CrateInfo;

use crate::prelude::*;

use crate::backend::{Emit, WriteDebugInfo};

pub fn codegen_crate(
    tcx: TyCtxt<'_>,
    metadata: EncodedMetadata,
    need_metadata_module: bool,
) -> Box<dyn Any> {
    tcx.sess.abort_if_errors();

    if std::env::var("SHOULD_RUN").is_ok()
        && tcx.sess.crate_types.get().contains(&CrateType::Executable)
    {
        #[cfg(not(target_arch = "wasm32"))]
        let _: ! = run_jit(tcx);

        #[cfg(target_arch = "wasm32")]
        panic!("jit not supported on wasm");
    }

    run_aot(tcx, metadata, need_metadata_module)
}

#[cfg(not(target_arch = "wasm32"))]
fn run_jit(tcx: TyCtxt<'_>) -> ! {
    use cranelift_simplejit::{SimpleJITBackend, SimpleJITBuilder};

    let imported_symbols = load_imported_symbols_for_jit(tcx);

    let mut jit_builder = SimpleJITBuilder::with_isa(
        crate::build_isa(tcx.sess, false),
        cranelift_module::default_libcall_names(),
    );
    jit_builder.symbols(imported_symbols);
    let mut jit_module: Module<SimpleJITBackend> = Module::new(jit_builder);
    assert_eq!(pointer_ty(tcx), jit_module.target_config().pointer_type());

    let sig = Signature {
        params: vec![
            AbiParam::new(jit_module.target_config().pointer_type()),
            AbiParam::new(jit_module.target_config().pointer_type()),
        ],
        returns: vec![AbiParam::new(
            jit_module.target_config().pointer_type(), /*isize*/
        )],
        call_conv: CallConv::triple_default(&crate::target_triple(tcx.sess)),
    };
    let main_func_id = jit_module
        .declare_function("main", Linkage::Import, &sig)
        .unwrap();

    codegen_cgus(tcx, &mut jit_module, &mut None);
    crate::allocator::codegen(tcx, &mut jit_module);
    jit_module.finalize_definitions();

    tcx.sess.abort_if_errors();

    let finalized_main: *const u8 = jit_module.get_finalized_function(main_func_id);

    println!("Rustc codegen cranelift will JIT run the executable, because the SHOULD_RUN env var is set");

    let f: extern "C" fn(c_int, *const *const c_char) -> c_int =
        unsafe { ::std::mem::transmute(finalized_main) };

    let args = ::std::env::var("JIT_ARGS").unwrap_or_else(|_| String::new());
    let args = args
        .split(" ")
        .chain(Some(&*tcx.crate_name(LOCAL_CRATE).as_str().to_string()))
        .map(|arg| CString::new(arg).unwrap())
        .collect::<Vec<_>>();
    let argv = args.iter().map(|arg| arg.as_ptr()).collect::<Vec<_>>();
    // TODO: Rust doesn't care, but POSIX argv has a NULL sentinel at the end

    let ret = f(args.len() as c_int, argv.as_ptr());

    jit_module.finish();
    std::process::exit(ret);
}

fn load_imported_symbols_for_jit(tcx: TyCtxt<'_>) -> Vec<(String, *const u8)> {
    use rustc::middle::dependency_format::Linkage;

    let mut dylib_paths = Vec::new();

    let crate_info = CrateInfo::new(tcx);
    let formats = tcx.dependency_formats(LOCAL_CRATE);
    let data = &formats
        .iter()
        .find(|(crate_type, _data)| *crate_type == CrateType::Executable)
        .unwrap()
        .1;
    for &(cnum, _) in &crate_info.used_crates_dynamic {
        let src = &crate_info.used_crate_source[&cnum];
        match data[cnum.as_usize() - 1] {
            Linkage::NotLinked | Linkage::IncludedFromDylib => {}
            Linkage::Static => {
                let name = tcx.crate_name(cnum);
                let mut err = tcx
                    .sess
                    .struct_err(&format!("Can't load static lib {}", name.as_str()));
                err.note("rustc_codegen_cranelift can only load dylibs in JIT mode.");
                err.emit();
            }
            Linkage::Dynamic => {
                dylib_paths.push(src.dylib.as_ref().unwrap().0.clone());
            }
        }
    }

    let mut imported_symbols = Vec::new();
    for path in dylib_paths {
        use object::Object;
        let lib = libloading::Library::new(&path).unwrap();
        let obj = std::fs::read(path).unwrap();
        let obj = object::File::parse(&obj).unwrap();
        imported_symbols.extend(obj.dynamic_symbols().filter_map(|(_idx, symbol)| {
            let name = symbol.name().unwrap().to_string();
            if name.is_empty() || !symbol.is_global() || symbol.is_undefined() {
                return None;
            }
            let symbol: libloading::Symbol<*const u8> =
                unsafe { lib.get(name.as_bytes()) }.unwrap();
            Some((name, *symbol))
        }));
        std::mem::forget(lib)
    }

    tcx.sess.abort_if_errors();

    imported_symbols
}

fn run_aot(
    tcx: TyCtxt<'_>,
    metadata: EncodedMetadata,
    need_metadata_module: bool,
) -> Box<CodegenResults> {
    let new_module = |name: String| {
        let module = crate::backend::make_module(tcx.sess, name);
        assert_eq!(pointer_ty(tcx), module.target_config().pointer_type());
        module
    };

    fn emit_module<B: Backend>(
        tcx: TyCtxt<'_>,
        name: String,
        kind: ModuleKind,
        mut module: Module<B>,
        debug: Option<DebugContext>,
    ) -> CompiledModule
        where B::Product: Emit + WriteDebugInfo,
    {
            module.finalize_definitions();
            let mut product = module.finish();

            if let Some(mut debug) = debug {
                debug.emit(&mut product);
            }

            let tmp_file = tcx
                .output_filenames(LOCAL_CRATE)
                .temp_path(OutputType::Object, Some(&name));
            let obj = product.emit();
            std::fs::write(&tmp_file, obj).unwrap();
            CompiledModule {
                name,
                kind,
                object: Some(tmp_file),
                bytecode: None,
                bytecode_compressed: None,
            }
        };

    let mut module = new_module("some_file".to_string());

    let mut debug = if tcx.sess.opts.debuginfo != DebugInfo::None {
        let debug = DebugContext::new(
            tcx,
            module.target_config().pointer_type().bytes() as u8,
        );
        Some(debug)
    } else {
        None
    };

    codegen_cgus(tcx, &mut module, &mut debug);

    tcx.sess.abort_if_errors();

    let mut allocator_module = new_module("allocator_shim".to_string());
    let created_alloc_shim = crate::allocator::codegen(tcx, &mut allocator_module);

    rustc_incremental::assert_dep_graph(tcx);
    rustc_incremental::save_dep_graph(tcx);
    rustc_incremental::finalize_session_directory(tcx.sess, tcx.crate_hash(LOCAL_CRATE));

    let metadata_module = if need_metadata_module {
        let _timer = tcx.prof.generic_activity("codegen crate metadata");
        let (metadata_cgu_name, tmp_file) = tcx.sess.time("write compressed metadata", || {
            use rustc::mir::mono::CodegenUnitNameBuilder;

            let cgu_name_builder = &mut CodegenUnitNameBuilder::new(tcx);
            let metadata_cgu_name = cgu_name_builder
                .build_cgu_name(LOCAL_CRATE, &["crate"], Some("metadata"))
                .as_str()
                .to_string();

            let tmp_file = tcx
                .output_filenames(LOCAL_CRATE)
                .temp_path(OutputType::Metadata, Some(&metadata_cgu_name));

            let obj = crate::backend::with_object(tcx.sess, &metadata_cgu_name, |object| {
                crate::metadata::write_metadata(tcx, object);
            });

            std::fs::write(&tmp_file, obj).unwrap();

            (metadata_cgu_name, tmp_file)
        });

        Some(CompiledModule {
            name: metadata_cgu_name,
            kind: ModuleKind::Metadata,
            object: Some(tmp_file),
            bytecode: None,
            bytecode_compressed: None,
        })
    } else {
        None
    };

    Box::new(CodegenResults {
        crate_name: tcx.crate_name(LOCAL_CRATE),
        modules: vec![emit_module(
            tcx,
            "some_file".to_string(),
            ModuleKind::Regular,
            module,
            debug,
        )],
        allocator_module: if created_alloc_shim {
            Some(emit_module(
                tcx,
                "allocator_shim".to_string(),
                ModuleKind::Allocator,
                allocator_module,
                None,
            ))
        } else {
            None
        },
        metadata_module,
        crate_hash: tcx.crate_hash(LOCAL_CRATE),
        metadata,
        windows_subsystem: None, // Windows is not yet supported
        linker_info: LinkerInfo::new(tcx),
        crate_info: CrateInfo::new(tcx),
    })
}

fn codegen_cgus<'tcx>(
    tcx: TyCtxt<'tcx>,
    module: &mut Module<impl Backend + 'static>,
    debug: &mut Option<DebugContext<'tcx>>,
) {
    let (_, cgus) = tcx.collect_and_partition_mono_items(LOCAL_CRATE);
    let mono_items = cgus
        .iter()
        .map(|cgu| cgu.items_in_deterministic_order(tcx).into_iter())
        .flatten()
        .collect::<FxHashMap<_, (_, _)>>();

    codegen_mono_items(tcx, module, debug.as_mut(), mono_items);

    crate::main_shim::maybe_create_entry_wrapper(tcx, module);
}

fn codegen_mono_items<'tcx>(
    tcx: TyCtxt<'tcx>,
    module: &mut Module<impl Backend + 'static>,
    debug_context: Option<&mut DebugContext<'tcx>>,
    mono_items: FxHashMap<MonoItem<'tcx>, (RLinkage, Visibility)>,
) {
    let mut cx = CodegenCx::new(tcx, module, debug_context);

    time(tcx.sess, "codegen mono items", move || {
        tcx.sess.time("predefine functions", || {
            for (&mono_item, &(linkage, visibility)) in &mono_items {
                match mono_item {
                    MonoItem::Fn(instance) => {
                        let (name, sig) =
                            get_function_name_and_sig(tcx, cx.module.isa().triple(), instance, false);
                        let linkage = crate::linkage::get_clif_linkage(mono_item, linkage, visibility);
                        cx.module.declare_function(&name, linkage, &sig).unwrap();
                    }
                    MonoItem::Static(_) | MonoItem::GlobalAsm(_) => {}
                }
            }
        });

        for (mono_item, (linkage, visibility)) in mono_items {
            crate::unimpl::try_unimpl(tcx, || {
                let linkage = crate::linkage::get_clif_linkage(mono_item, linkage, visibility);
                trans_mono_item(&mut cx, mono_item, linkage);
            });
        }

        tcx.sess.time("finalize CodegenCx", || cx.finalize());
    });
}

fn trans_mono_item<'clif, 'tcx, B: Backend + 'static>(
    cx: &mut crate::CodegenCx<'clif, 'tcx, B>,
    mono_item: MonoItem<'tcx>,
    linkage: Linkage,
) {
    let tcx = cx.tcx;
    match mono_item {
        MonoItem::Fn(inst) => {
            let _inst_guard =
                PrintOnPanic(|| format!("{:?} {}", inst, tcx.symbol_name(inst).name.as_str()));
            debug_assert!(!inst.substs.needs_infer());
            let _mir_guard = PrintOnPanic(|| {
                match inst.def {
                    InstanceDef::Item(_)
                    | InstanceDef::DropGlue(_, _)
                    | InstanceDef::Virtual(_, _) => {
                        let mut mir = ::std::io::Cursor::new(Vec::new());
                        crate::rustc_mir::util::write_mir_pretty(
                            tcx,
                            Some(inst.def_id()),
                            &mut mir,
                        )
                        .unwrap();
                        String::from_utf8(mir.into_inner()).unwrap()
                    }
                    _ => {
                        // FIXME fix write_mir_pretty for these instances
                        format!("{:#?}", tcx.instance_mir(inst.def))
                    }
                }
            });

            cx.tcx.sess.time("codegen fn", || crate::base::trans_fn(cx, inst, linkage));
        }
        MonoItem::Static(def_id) => {
            crate::constant::codegen_static(&mut cx.constants_cx, def_id);
        }
        MonoItem::GlobalAsm(node_id) => tcx
            .sess
            .fatal(&format!("Unimplemented global asm mono item {:?}", node_id)),
    }
}

fn time<R>(sess: &Session, name: &'static str, f: impl FnOnce() -> R) -> R {
    println!("[{}] start", name);
    let before = std::time::Instant::now();
    let res = sess.time(name, f);
    let after = std::time::Instant::now();
    println!("[{}] end time: {:?}", name, after - before);
    res
}
<|endoftext|>


<commit_before>#![feature(rustc_private)]
#![feature(once_cell)]
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
// warn on lints, that are included in `rust-lang/rust`s bootstrap
#![warn(rust_2018_idioms, unused_lifetimes)]
// warn on rustc internal lints
#![deny(rustc::internal)]

// FIXME: switch to something more ergonomic here, once available.
// (Currently there is no way to opt into sysroot crates without `extern crate`.)
extern crate rustc_driver;
extern crate rustc_errors;
extern crate rustc_interface;

use rustc_interface::interface;
use rustc_tools_util::VersionInfo;

use std::borrow::Cow;
use std::env;
use std::lazy::SyncLazy;
use std::ops::Deref;
use std::panic;
use std::path::{Path, PathBuf};
use std::process::{exit, Command};

/// If a command-line option matches `find_arg`, then apply the predicate `pred` on its value. If
/// true, then return it. The parameter is assumed to be either `--arg=value` or `--arg value`.
fn arg_value<'a, T: Deref<Target = str>>(
    args: &'a [T],
    find_arg: &str,
    pred: impl Fn(&str) -> bool,
) -> Option<&'a str> {
    let mut args = args.iter().map(Deref::deref);
    while let Some(arg) = args.next() {
        let mut arg = arg.splitn(2, '=');
        if arg.next() != Some(find_arg) {
            continue;
        }

        match arg.next().or_else(|| args.next()) {
            Some(v) if pred(v) => return Some(v),
            _ => {},
        }
    }
    None
}

#[test]
fn test_arg_value() {
    let args = &["--bar=bar", "--foobar", "123", "--foo"];

    assert_eq!(arg_value(&[] as &[&str], "--foobar", |_| true), None);
    assert_eq!(arg_value(args, "--bar", |_| false), None);
    assert_eq!(arg_value(args, "--bar", |_| true), Some("bar"));
    assert_eq!(arg_value(args, "--bar", |p| p == "bar"), Some("bar"));
    assert_eq!(arg_value(args, "--bar", |p| p == "foo"), None);
    assert_eq!(arg_value(args, "--foobar", |p| p == "foo"), None);
    assert_eq!(arg_value(args, "--foobar", |p| p == "123"), Some("123"));
    assert_eq!(arg_value(args, "--foo", |_| true), None);
}

struct DefaultCallbacks;
impl rustc_driver::Callbacks for DefaultCallbacks {}

struct ClippyCallbacks;
impl rustc_driver::Callbacks for ClippyCallbacks {
    fn config(&mut self, config: &mut interface::Config) {
        let previous = config.register_lints.take();
        config.register_lints = Some(Box::new(move |sess, mut lint_store| {
            // technically we're ~guaranteed that this is none but might as well call anything that
            // is there already. Certainly it can't hurt.
            if let Some(previous) = &previous {
                (previous)(sess, lint_store);
            }

            let conf = clippy_lints::read_conf(&[], &sess);
            clippy_lints::register_plugins(&mut lint_store, &sess, &conf);
            clippy_lints::register_pre_expansion_lints(&mut lint_store);
            clippy_lints::register_renamed(&mut lint_store);
        }));

        // FIXME: #4825; This is required, because Clippy lints that are based on MIR have to be
        // run on the unoptimized MIR. On the other hand this results in some false negatives. If
        // MIR passes can be enabled / disabled separately, we should figure out, what passes to
        // use for Clippy.
        config.opts.debugging_opts.mir_opt_level = 0;
    }
}

fn display_help() {
    println!(
        "\
Checks a package to catch common mistakes and improve your Rust code.

Usage:
    cargo clippy [options] [--] [<opts>...]

Common options:
    -h, --help               Print this message
        --rustc              Pass all args to rustc
    -V, --version            Print version info and exit

Other options are the same as `cargo check`.

To allow or deny a lint from the command line you can use `cargo clippy --`
with:

    -W --warn OPT       Set lint warnings
    -A --allow OPT      Set lint allowed
    -D --deny OPT       Set lint denied
    -F --forbid OPT     Set lint forbidden

You can use tool lints to allow or deny lints from your code, eg.:

    #[allow(clippy::needless_lifetimes)]
"
    );
}

const BUG_REPORT_URL: &str = "https://github.com/rust-lang/rust-clippy/issues/new";

static ICE_HOOK: SyncLazy<Box<dyn Fn(&panic::PanicInfo<'_>) + Sync + Send + 'static>> = SyncLazy::new(|| {
    let hook = panic::take_hook();
    panic::set_hook(Box::new(|info| report_clippy_ice(info, BUG_REPORT_URL)));
    hook
});

fn report_clippy_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) {
    // Invoke our ICE handler, which prints the actual panic message and optionally a backtrace
    (*ICE_HOOK)(info);

    // Separate the output with an empty line
    eprintln!();

    let emitter = Box::new(rustc_errors::emitter::EmitterWriter::stderr(
        rustc_errors::ColorConfig::Auto,
        None,
        false,
        false,
        None,
        false,
    ));
    let handler = rustc_errors::Handler::with_emitter(true, None, emitter);

    // a .span_bug or .bug call has already printed what
    // it wants to print.
    if !info.payload().is::<rustc_errors::ExplicitBug>() {
        let d = rustc_errors::Diagnostic::new(rustc_errors::Level::Bug, "unexpected panic");
        handler.emit_diagnostic(&d);
    }

    let version_info = rustc_tools_util::get_version_info!();

    let xs: Vec<Cow<'static, str>> = vec![
        "the compiler unexpectedly panicked. this is a bug.".into(),
        format!("we would appreciate a bug report: {}", bug_report_url).into(),
        format!("Clippy version: {}", version_info).into(),
    ];

    for note in &xs {
        handler.note_without_error(&note);
    }

    // If backtraces are enabled, also print the query stack
    let backtrace = env::var_os("RUST_BACKTRACE").map_or(false, |x| &x != "0");

    let num_frames = if backtrace { None } else { Some(2) };

    interface::try_print_query_stack(&handler, num_frames);
}

fn toolchain_path(home: Option<String>, toolchain: Option<String>) -> Option<PathBuf> {
    home.and_then(|home| {
        toolchain.map(|toolchain| {
            let mut path = PathBuf::from(home);
            path.push("toolchains");
            path.push(toolchain);
            path
        })
    })
}

#[allow(clippy::too_many_lines)]
pub fn main() {
    rustc_driver::init_rustc_env_logger();
    SyncLazy::force(&ICE_HOOK);
    exit(rustc_driver::catch_with_exit_code(move || {
        let mut orig_args: Vec<String> = env::args().collect();

        // Get the sysroot, looking from most specific to this invocation to the least:
        // - command line
        // - runtime environment
        //    - SYSROOT
        //    - RUSTUP_HOME, MULTIRUST_HOME, RUSTUP_TOOLCHAIN, MULTIRUST_TOOLCHAIN
        // - sysroot from rustc in the path
        // - compile-time environment
        //    - SYSROOT
        //    - RUSTUP_HOME, MULTIRUST_HOME, RUSTUP_TOOLCHAIN, MULTIRUST_TOOLCHAIN
        let sys_root_arg = arg_value(&orig_args, "--sysroot", |_| true);
        let have_sys_root_arg = sys_root_arg.is_some();
        let sys_root = sys_root_arg
            .map(PathBuf::from)
            .or_else(|| std::env::var("SYSROOT").ok().map(PathBuf::from))
            .or_else(|| {
                let home = std::env::var("RUSTUP_HOME")
                    .or_else(|_| std::env::var("MULTIRUST_HOME"))
                    .ok();
                let toolchain = std::env::var("RUSTUP_TOOLCHAIN")
                    .or_else(|_| std::env::var("MULTIRUST_TOOLCHAIN"))
                    .ok();
                toolchain_path(home, toolchain)
            })
            .or_else(|| {
                Command::new("rustc")
                    .arg("--print")
                    .arg("sysroot")
                    .output()
                    .ok()
                    .and_then(|out| String::from_utf8(out.stdout).ok())
                    .map(|s| PathBuf::from(s.trim()))
            })
            .or_else(|| option_env!("SYSROOT").map(PathBuf::from))
            .or_else(|| {
                let home = option_env!("RUSTUP_HOME")
                    .or(option_env!("MULTIRUST_HOME"))
                    .map(ToString::to_string);
                let toolchain = option_env!("RUSTUP_TOOLCHAIN")
                    .or(option_env!("MULTIRUST_TOOLCHAIN"))
                    .map(ToString::to_string);
                toolchain_path(home, toolchain)
            })
            .map(|pb| pb.to_string_lossy().to_string())
            .expect("need to specify SYSROOT env var during clippy compilation, or use rustup or multirust");

        // make "clippy-driver --rustc" work like a subcommand that passes further args to "rustc"
        // for example `clippy-driver --rustc --version` will print the rustc version that clippy-driver
        // uses
        if let Some(pos) = orig_args.iter().position(|arg| arg == "--rustc") {
            orig_args.remove(pos);
            orig_args[0] = "rustc".to_string();

            // if we call "rustc", we need to pass --sysroot here as well
            let mut args: Vec<String> = orig_args.clone();
            if !have_sys_root_arg {
                args.extend(vec!["--sysroot".into(), sys_root]);
            };

            return rustc_driver::RunCompiler::new(&args, &mut DefaultCallbacks).run();
        }

        if orig_args.iter().any(|a| a == "--version" || a == "-V") {
            let version_info = rustc_tools_util::get_version_info!();
            println!("{}", version_info);
            exit(0);
        }

        // Setting RUSTC_WRAPPER causes Cargo to pass 'rustc' as the first argument.
        // We're invoking the compiler programmatically, so we ignore this/
        let wrapper_mode = orig_args.get(1).map(Path::new).and_then(Path::file_stem) == Some("rustc".as_ref());

        if wrapper_mode {
            // we still want to be able to invoke it normally though
            orig_args.remove(1);
        }

        if !wrapper_mode && (orig_args.iter().any(|a| a == "--help" || a == "-h") || orig_args.len() == 1) {
            display_help();
            exit(0);
        }

        // this conditional check for the --sysroot flag is there so users can call
        // `clippy_driver` directly
        // without having to pass --sysroot or anything
        let mut args: Vec<String> = orig_args.clone();
        if !have_sys_root_arg {
            args.extend(vec!["--sysroot".into(), sys_root]);
        };

        let mut no_deps = false;
        let clippy_args = env::var("CLIPPY_ARGS")
            .unwrap_or_default()
            .split("__CLIPPY_HACKERY__")
            .filter_map(|s| match s {
                "" => None,
                "--no-deps" => {
                    no_deps = true;
                    None
                },
                _ => Some(s.to_string()),
            })
            .chain(vec!["--cfg".into(), r#"feature="cargo-clippy""#.into()])
            .collect::<Vec<String>>();

        // We enable Clippy if one of the following conditions is met
        // - IF Clippy is run on its test suite OR
        // - IF Clippy is run on the main crate, not on deps (`!cap_lints_allow`) THEN
        //    - IF `--no-deps` is not set (`!no_deps`) OR
        //    - IF `--no-deps` is set and Clippy is run on the specified primary package
        let clippy_tests_set = env::var("__CLIPPY_INTERNAL_TESTS").map_or(false, |val| val == "true");
        let cap_lints_allow = arg_value(&orig_args, "--cap-lints", |val| val == "allow").is_some();
        let in_primary_package = env::var("CARGO_PRIMARY_PACKAGE").is_ok();

        let clippy_enabled = clippy_tests_set || (!cap_lints_allow && (!no_deps || in_primary_package));
        if clippy_enabled {
            args.extend(clippy_args);
        }

        let mut clippy = ClippyCallbacks;
        let mut default = DefaultCallbacks;
        let callbacks: &mut (dyn rustc_driver::Callbacks + Send) =
            if clippy_enabled { &mut clippy } else { &mut default };

        rustc_driver::RunCompiler::new(&args, callbacks).run()
    }))
}
<commit_msg>Rollup merge of #82736 - spastorino:mir-opt-level-perf-changes, r=oli-obk<commit_after>#![feature(rustc_private)]
#![feature(once_cell)]
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
// warn on lints, that are included in `rust-lang/rust`s bootstrap
#![warn(rust_2018_idioms, unused_lifetimes)]
// warn on rustc internal lints
#![deny(rustc::internal)]

// FIXME: switch to something more ergonomic here, once available.
// (Currently there is no way to opt into sysroot crates without `extern crate`.)
extern crate rustc_driver;
extern crate rustc_errors;
extern crate rustc_interface;

use rustc_interface::interface;
use rustc_tools_util::VersionInfo;

use std::borrow::Cow;
use std::env;
use std::lazy::SyncLazy;
use std::ops::Deref;
use std::panic;
use std::path::{Path, PathBuf};
use std::process::{exit, Command};

/// If a command-line option matches `find_arg`, then apply the predicate `pred` on its value. If
/// true, then return it. The parameter is assumed to be either `--arg=value` or `--arg value`.
fn arg_value<'a, T: Deref<Target = str>>(
    args: &'a [T],
    find_arg: &str,
    pred: impl Fn(&str) -> bool,
) -> Option<&'a str> {
    let mut args = args.iter().map(Deref::deref);
    while let Some(arg) = args.next() {
        let mut arg = arg.splitn(2, '=');
        if arg.next() != Some(find_arg) {
            continue;
        }

        match arg.next().or_else(|| args.next()) {
            Some(v) if pred(v) => return Some(v),
            _ => {},
        }
    }
    None
}

#[test]
fn test_arg_value() {
    let args = &["--bar=bar", "--foobar", "123", "--foo"];

    assert_eq!(arg_value(&[] as &[&str], "--foobar", |_| true), None);
    assert_eq!(arg_value(args, "--bar", |_| false), None);
    assert_eq!(arg_value(args, "--bar", |_| true), Some("bar"));
    assert_eq!(arg_value(args, "--bar", |p| p == "bar"), Some("bar"));
    assert_eq!(arg_value(args, "--bar", |p| p == "foo"), None);
    assert_eq!(arg_value(args, "--foobar", |p| p == "foo"), None);
    assert_eq!(arg_value(args, "--foobar", |p| p == "123"), Some("123"));
    assert_eq!(arg_value(args, "--foo", |_| true), None);
}

struct DefaultCallbacks;
impl rustc_driver::Callbacks for DefaultCallbacks {}

struct ClippyCallbacks;
impl rustc_driver::Callbacks for ClippyCallbacks {
    fn config(&mut self, config: &mut interface::Config) {
        let previous = config.register_lints.take();
        config.register_lints = Some(Box::new(move |sess, mut lint_store| {
            // technically we're ~guaranteed that this is none but might as well call anything that
            // is there already. Certainly it can't hurt.
            if let Some(previous) = &previous {
                (previous)(sess, lint_store);
            }

            let conf = clippy_lints::read_conf(&[], &sess);
            clippy_lints::register_plugins(&mut lint_store, &sess, &conf);
            clippy_lints::register_pre_expansion_lints(&mut lint_store);
            clippy_lints::register_renamed(&mut lint_store);
        }));

        // FIXME: #4825; This is required, because Clippy lints that are based on MIR have to be
        // run on the unoptimized MIR. On the other hand this results in some false negatives. If
        // MIR passes can be enabled / disabled separately, we should figure out, what passes to
        // use for Clippy.
        config.opts.debugging_opts.mir_opt_level = Some(0);
    }
}

fn display_help() {
    println!(
        "\
Checks a package to catch common mistakes and improve your Rust code.

Usage:
    cargo clippy [options] [--] [<opts>...]

Common options:
    -h, --help               Print this message
        --rustc              Pass all args to rustc
    -V, --version            Print version info and exit

Other options are the same as `cargo check`.

To allow or deny a lint from the command line you can use `cargo clippy --`
with:

    -W --warn OPT       Set lint warnings
    -A --allow OPT      Set lint allowed
    -D --deny OPT       Set lint denied
    -F --forbid OPT     Set lint forbidden

You can use tool lints to allow or deny lints from your code, eg.:

    #[allow(clippy::needless_lifetimes)]
"
    );
}

const BUG_REPORT_URL: &str = "https://github.com/rust-lang/rust-clippy/issues/new";

static ICE_HOOK: SyncLazy<Box<dyn Fn(&panic::PanicInfo<'_>) + Sync + Send + 'static>> = SyncLazy::new(|| {
    let hook = panic::take_hook();
    panic::set_hook(Box::new(|info| report_clippy_ice(info, BUG_REPORT_URL)));
    hook
});

fn report_clippy_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) {
    // Invoke our ICE handler, which prints the actual panic message and optionally a backtrace
    (*ICE_HOOK)(info);

    // Separate the output with an empty line
    eprintln!();

    let emitter = Box::new(rustc_errors::emitter::EmitterWriter::stderr(
        rustc_errors::ColorConfig::Auto,
        None,
        false,
        false,
        None,
        false,
    ));
    let handler = rustc_errors::Handler::with_emitter(true, None, emitter);

    // a .span_bug or .bug call has already printed what
    // it wants to print.
    if !info.payload().is::<rustc_errors::ExplicitBug>() {
        let d = rustc_errors::Diagnostic::new(rustc_errors::Level::Bug, "unexpected panic");
        handler.emit_diagnostic(&d);
    }

    let version_info = rustc_tools_util::get_version_info!();

    let xs: Vec<Cow<'static, str>> = vec![
        "the compiler unexpectedly panicked. this is a bug.".into(),
        format!("we would appreciate a bug report: {}", bug_report_url).into(),
        format!("Clippy version: {}", version_info).into(),
    ];

    for note in &xs {
        handler.note_without_error(&note);
    }

    // If backtraces are enabled, also print the query stack
    let backtrace = env::var_os("RUST_BACKTRACE").map_or(false, |x| &x != "0");

    let num_frames = if backtrace { None } else { Some(2) };

    interface::try_print_query_stack(&handler, num_frames);
}

fn toolchain_path(home: Option<String>, toolchain: Option<String>) -> Option<PathBuf> {
    home.and_then(|home| {
        toolchain.map(|toolchain| {
            let mut path = PathBuf::from(home);
            path.push("toolchains");
            path.push(toolchain);
            path
        })
    })
}

#[allow(clippy::too_many_lines)]
pub fn main() {
    rustc_driver::init_rustc_env_logger();
    SyncLazy::force(&ICE_HOOK);
    exit(rustc_driver::catch_with_exit_code(move || {
        let mut orig_args: Vec<String> = env::args().collect();

        // Get the sysroot, looking from most specific to this invocation to the least:
        // - command line
        // - runtime environment
        //    - SYSROOT
        //    - RUSTUP_HOME, MULTIRUST_HOME, RUSTUP_TOOLCHAIN, MULTIRUST_TOOLCHAIN
        // - sysroot from rustc in the path
        // - compile-time environment
        //    - SYSROOT
        //    - RUSTUP_HOME, MULTIRUST_HOME, RUSTUP_TOOLCHAIN, MULTIRUST_TOOLCHAIN
        let sys_root_arg = arg_value(&orig_args, "--sysroot", |_| true);
        let have_sys_root_arg = sys_root_arg.is_some();
        let sys_root = sys_root_arg
            .map(PathBuf::from)
            .or_else(|| std::env::var("SYSROOT").ok().map(PathBuf::from))
            .or_else(|| {
                let home = std::env::var("RUSTUP_HOME")
                    .or_else(|_| std::env::var("MULTIRUST_HOME"))
                    .ok();
                let toolchain = std::env::var("RUSTUP_TOOLCHAIN")
                    .or_else(|_| std::env::var("MULTIRUST_TOOLCHAIN"))
                    .ok();
                toolchain_path(home, toolchain)
            })
            .or_else(|| {
                Command::new("rustc")
                    .arg("--print")
                    .arg("sysroot")
                    .output()
                    .ok()
                    .and_then(|out| String::from_utf8(out.stdout).ok())
                    .map(|s| PathBuf::from(s.trim()))
            })
            .or_else(|| option_env!("SYSROOT").map(PathBuf::from))
            .or_else(|| {
                let home = option_env!("RUSTUP_HOME")
                    .or(option_env!("MULTIRUST_HOME"))
                    .map(ToString::to_string);
                let toolchain = option_env!("RUSTUP_TOOLCHAIN")
                    .or(option_env!("MULTIRUST_TOOLCHAIN"))
                    .map(ToString::to_string);
                toolchain_path(home, toolchain)
            })
            .map(|pb| pb.to_string_lossy().to_string())
            .expect("need to specify SYSROOT env var during clippy compilation, or use rustup or multirust");

        // make "clippy-driver --rustc" work like a subcommand that passes further args to "rustc"
        // for example `clippy-driver --rustc --version` will print the rustc version that clippy-driver
        // uses
        if let Some(pos) = orig_args.iter().position(|arg| arg == "--rustc") {
            orig_args.remove(pos);
            orig_args[0] = "rustc".to_string();

            // if we call "rustc", we need to pass --sysroot here as well
            let mut args: Vec<String> = orig_args.clone();
            if !have_sys_root_arg {
                args.extend(vec!["--sysroot".into(), sys_root]);
            };

            return rustc_driver::RunCompiler::new(&args, &mut DefaultCallbacks).run();
        }

        if orig_args.iter().any(|a| a == "--version" || a == "-V") {
            let version_info = rustc_tools_util::get_version_info!();
            println!("{}", version_info);
            exit(0);
        }

        // Setting RUSTC_WRAPPER causes Cargo to pass 'rustc' as the first argument.
        // We're invoking the compiler programmatically, so we ignore this/
        let wrapper_mode = orig_args.get(1).map(Path::new).and_then(Path::file_stem) == Some("rustc".as_ref());

        if wrapper_mode {
            // we still want to be able to invoke it normally though
            orig_args.remove(1);
        }

        if !wrapper_mode && (orig_args.iter().any(|a| a == "--help" || a == "-h") || orig_args.len() == 1) {
            display_help();
            exit(0);
        }

        // this conditional check for the --sysroot flag is there so users can call
        // `clippy_driver` directly
        // without having to pass --sysroot or anything
        let mut args: Vec<String> = orig_args.clone();
        if !have_sys_root_arg {
            args.extend(vec!["--sysroot".into(), sys_root]);
        };

        let mut no_deps = false;
        let clippy_args = env::var("CLIPPY_ARGS")
            .unwrap_or_default()
            .split("__CLIPPY_HACKERY__")
            .filter_map(|s| match s {
                "" => None,
                "--no-deps" => {
                    no_deps = true;
                    None
                },
                _ => Some(s.to_string()),
            })
            .chain(vec!["--cfg".into(), r#"feature="cargo-clippy""#.into()])
            .collect::<Vec<String>>();

        // We enable Clippy if one of the following conditions is met
        // - IF Clippy is run on its test suite OR
        // - IF Clippy is run on the main crate, not on deps (`!cap_lints_allow`) THEN
        //    - IF `--no-deps` is not set (`!no_deps`) OR
        //    - IF `--no-deps` is set and Clippy is run on the specified primary package
        let clippy_tests_set = env::var("__CLIPPY_INTERNAL_TESTS").map_or(false, |val| val == "true");
        let cap_lints_allow = arg_value(&orig_args, "--cap-lints", |val| val == "allow").is_some();
        let in_primary_package = env::var("CARGO_PRIMARY_PACKAGE").is_ok();

        let clippy_enabled = clippy_tests_set || (!cap_lints_allow && (!no_deps || in_primary_package));
        if clippy_enabled {
            args.extend(clippy_args);
        }

        let mut clippy = ClippyCallbacks;
        let mut default = DefaultCallbacks;
        let callbacks: &mut (dyn rustc_driver::Callbacks + Send) =
            if clippy_enabled { &mut clippy } else { &mut default };

        rustc_driver::RunCompiler::new(&args, callbacks).run()
    }))
}
<|endoftext|>