<commit_before>/*
 * Copyright (c) 2017-2019 Boucher, Antoni <bouanto@zoho.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

/// Connect events to sending a message.
///
/// ## Rules
/// 1. Send `$msg` to `$other_component` when the GTK+ `$event` is emitted on `$widget`.
///
/// 2. Optionally send `$msg.0` when the GTK+ `$event` is emitted on `$widget`.
/// Return `$msg.1` in the GTK+ callback.
/// This variant gives more control to the caller since it expects a `$msg` returning `(Option<MSG>,
/// ReturnValue)` where the `ReturnValue` is the value to return in the GTK+ callback.
/// Option<MSG> can be None if no message needs to be emitted.
///
/// 3. Send `$msg` when the GTK+ `$event` is emitted on `$widget`.
///
/// 4. Send `$msg` to `$widget` when the `$message` is received on `$stream`.
#[macro_export]
macro_rules! connect {
    // Connect to a GTK+ widget event, sending a message to another widget.
    ($widget:expr, $event:ident($($args:pat),*), $other_component:expr, $msg:expr) => {
        connect_stream!($widget, $event($($args),*), $other_component.stream(), $msg);
    };

    // Connect to a GTK+ widget event.
    // This variant gives more control to the caller since it expects a `$msg` returning (Option<MSG>,
    // ReturnValue) where the ReturnValue is the value to return in the GTK+ callback.
    // Option<MSG> can be None if no message needs to be emitted.
    ($relm:expr, $widget:expr, $event:ident($($args:pat),*), return $msg:expr) => {{
        connect_stream!(return $relm.stream(), $widget, $event($($args),*), $msg);
    }};

    // Connect to a GTK+ widget event.
    ($relm:expr, $widget:expr, $event:ident($($args:pat),*), $msg:expr) => {{
        let stream = $relm.stream().clone();
        let _ = $widget.$event(move |$($args),*| {
            let msg: Option<_> = $crate::IntoOption::into_option($msg);
            if let Some(msg) = msg {
                stream.emit(msg);
            }
        });
    }};

    // Connect to a message reception.
    // TODO: create another macro rule accepting multiple patterns.
    ($src_component:ident @ $message:pat, $dst_component:expr, $msg:expr) => {
        let stream = $src_component.stream().clone();
        connect_stream!(stream@$message, $dst_component.stream(), $msg);
    };
}

/// Connect events to sending a message.
/// Similar to `connect!` but wants a stream instead of a component.
///
/// ## Rules
/// 1. Send `$msg` to `$other_stream` when the GTK+ `$event` is emitted on `$widget`.
///
/// 2. Send `$msg` to `$widget` when the `$message` is received on `$stream`.
#[macro_export]
macro_rules! connect_stream {
    // Connect to a GTK+ widget event.
    // This variant gives more control to the caller since it expects a `$msg` returning (Option<MSG>,
    // ReturnValue) where the ReturnValue is the value to return in the GTK+ callback.
    // Option<MSG> can be None if no message needs to be emitted.
    (return $stream:expr, $widget:expr, $event:ident($($args:pat),*), $msg:expr) => {{
        let stream = $stream.clone();
        let _ = $widget.$event(move |$($args),*| {
            let (msg, return_value) = $crate::IntoPair::into_pair($msg);
            let msg: Option<_> = $crate::IntoOption::into_option(msg);
            if let Some(msg) = msg {
                stream.emit(msg);
            }
            return_value
        });
    }};

    // Connect to a GTK+ widget event, sending a message to another widget.
    ($widget:expr, $event:ident($($args:pat),*), $other_stream:expr, $msg:expr) => {
        let stream = $other_stream.clone();
        let _ = $widget.$event(move |$($args),*| {
            let msg: Option<_> = $crate::IntoOption::into_option($msg);
            if let Some(msg) = msg {
                stream.emit(msg);
            }
        });
    };

    // Connect to a message reception.
    // TODO: create another macro rule accepting multiple patterns.
    ($src_stream:ident @ $message:pat, $dst_stream:expr, $msg:expr) => {
        let stream = $dst_stream.clone();
        $src_stream.observe(move |msg| {
            #[allow(unreachable_patterns)]
            match msg {
                &$message =>  {
                    let msg: Option<_> = $crate::IntoOption::into_option($msg);
                    if let Some(msg) = msg {
                        stream.emit(msg);
                    }
                },
                _ => (),
            }
        });
    };
}

/// Connect an asynchronous method call to send a message.
/// The variants with `$fail_msg` will send this message when there's an error.
/// Those without this argument will ignore the error.
#[macro_export]
macro_rules! connect_async {
    ($object:expr, $async_method:ident, $relm:expr, $msg:expr) => {
        connect_async!($object, $async_method(), $relm, $msg)
    };
    ($object:expr, $async_method:ident ( $($args:expr),* ), $relm:expr, $msg:expr) => {{
        // TODO: remove any use of Fragile when gio callbacks stop requiring Send.
        let stream = ::relm::vendor::fragile::Fragile::new($relm.stream().clone());
        $object.$async_method($($args,)* None, move |result| {
            if let Ok(result) = result {
                stream.into_inner().emit($msg(result));
            }
        });
    }};
    ($object:expr, $async_method:ident, $relm:expr, $msg:expr, $fail_msg:expr) => {
        connect_async!($object, $async_method(), $relm, $msg, $fail_msg)
    };
    ($object:expr, $async_method:ident ( $($args:expr),* ), $relm:expr, $msg:expr, $fail_msg:expr) => {{
        let event_stream = ::relm::vendor::fragile::Fragile::new($relm.stream().clone());
        let fail_event_stream = ::relm::vendor::fragile::Fragile::new($relm.stream().clone());
        $object.$async_method($($args,)* None, move |result| {
            match result.into_inner() {
                Ok(value) => event_stream.into_inner().emit($msg(value)),
                Err(error) => fail_event_stream.into_inner().emit($fail_msg(error)),
            }
        });
    }};
}

/// Connect an asynchronous function call to send a message.
/// The variants with `$fail_msg` will send this message when there's an error.
/// Those without this argument will ignore the error.
#[macro_export]
macro_rules! connect_async_func {
    ($class:ident :: $async_function:ident, $relm:expr, $msg:expr) => {
        connect_async_func!($class::$async_func(), $relm, $msg)
    };
    ($class:ident :: $async_func:ident ( $($args:expr),* ), $relm:expr, $msg:expr) => {{
        let stream = ::relm::vendor::fragile::Fragile::new($relm.stream().clone());
        $class::$async_func($($args,)* None, move |result| {
            if let Ok(result) = result {
                stream.into_inner().emit($msg(result));
            }
        });
    }};
    ($class:ident :: $async_func:ident, $relm:expr, $msg:expr, $fail_msg:expr) => {
        connect_async_func!($class::$async_func(), $relm, $msg, $fail_msg)
    };
    ($class:ident :: $async_func:ident ( $($args:expr),* ), $relm:expr, $msg:expr, $fail_msg:expr) => {{
        let event_stream = ::relm::vendor::fragile::Fragile::new($relm.stream().clone());
        let fail_event_stream = ::relm::vendor::fragile::Fragile::new($relm.stream().clone());
        $class::$async_func($($args,)* None, move |result| {
            match result {
                Ok(value) => event_stream.into_inner().emit($msg(value)),
                Err(error) => fail_event_stream.into_inner().emit($fail_msg(error)),
            }
        });

    }};
}

/// Like `connect_async!`, but also return a `Cancellable` to control the asynchronous request.
#[macro_export]
macro_rules! connect_async_full {
    ($object:expr, $async_method:ident, $relm:expr, $msg:expr) => {
        connect_async_full!($object, $async_method(), $relm, $msg)
    };
    ($object:expr, $async_method:ident ( $($args:expr),* ), $relm:expr, $msg:expr) => {{
        let cancellable = ::gio::Cancellable::new();
        let stream = ::relm::vendor::fragile::Fragile::new($relm.stream().clone());
        $object.$async_method($($args,)* Some(&cancellable), move |result| {
            if let Ok(result) = result {
                stream.into_inner().emit($msg(result));
            }
        });
        cancellable
    }};
    ($object:expr, $async_method:ident, $relm:expr, $msg:expr, $fail_msg:expr) => {
        connect_async_full!($object, $async_method(), $relm, $msg, $fail_msg)
    };
    ($object:expr, $async_method:ident ( $($args:expr),* ), $relm:expr, $msg:expr, $fail_msg:expr) => {{
        let cancellable = ::gio::Cancellable::new();
        let event_stream = ::relm::vendor::fragile::Fragile::new($relm.stream().clone());
        let fail_event_stream = ::relm::vendor::fragile::Fragile::new($relm.stream().clone());
        $object.$async_method($($args,)* Some(&cancellable), move |result| {
            match result {
                Ok(value) => event_stream.into_inner().emit($msg(value)),
                Err(error) => fail_event_stream.into_inner().emit($fail_msg(error)),
            }
        });

        cancellable
    }};
}

/// Like `connect_async_func!`, but also return a `Cancellable` to control the asynchronous request.
#[macro_export]
macro_rules! connect_async_func_full {
    ($class:ident :: $async_function:ident, $relm:expr, $msg:expr) => {
        connect_async!($async_func(), $relm, $msg)
    };
    ($class:ident :: $async_func:ident ( $($args:expr),* ), $relm:expr, $msg:expr) => {{
        let cancellable = ::gio::Cancellable::new();
        let stream = ::relm::vendor::fragile::Fragile::new($relm.stream().clone());
        $class::$async_func($($args,)* Some(&cancellable), move |result| {
            if let Ok(result) = result {
                stream.into_inner().emit($msg(result));
            }
        });
        cancellable
    }};
    ($class:ident :: $async_func:ident, $relm:expr, $msg:expr, $fail_msg:expr) => {
        connect_async!($async_func(), $relm, $msg, $fail_msg)
    };
    ($class:ident :: $async_func:ident ( $($args:expr),* ), $relm:expr, $msg:expr, $fail_msg:expr) => {{
        let cancellable = ::gio::Cancellable::new();
        let event_stream = ::relm::vendor::fragile::Fragile::new($relm.stream().clone());
        let fail_event_stream = ::relm::vendor::fragile::Fragile::new($relm.stream().clone());
        $class::$async_func($($args,)* Some(&cancellable), move |result| {
            match result {
                Ok(value) => event_stream.into_inner().emit($msg(value)),
                Err(error) => fail_event_stream.into_inner().emit($fail_msg(error)),
            }
        });

        cancellable
    }};
}
<commit_msg>Fix typo in async macro<commit_after>/*
 * Copyright (c) 2017-2019 Boucher, Antoni <bouanto@zoho.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

/// Connect events to sending a message.
///
/// ## Rules
/// 1. Send `$msg` to `$other_component` when the GTK+ `$event` is emitted on `$widget`.
///
/// 2. Optionally send `$msg.0` when the GTK+ `$event` is emitted on `$widget`.
/// Return `$msg.1` in the GTK+ callback.
/// This variant gives more control to the caller since it expects a `$msg` returning `(Option<MSG>,
/// ReturnValue)` where the `ReturnValue` is the value to return in the GTK+ callback.
/// Option<MSG> can be None if no message needs to be emitted.
///
/// 3. Send `$msg` when the GTK+ `$event` is emitted on `$widget`.
///
/// 4. Send `$msg` to `$widget` when the `$message` is received on `$stream`.
#[macro_export]
macro_rules! connect {
    // Connect to a GTK+ widget event, sending a message to another widget.
    ($widget:expr, $event:ident($($args:pat),*), $other_component:expr, $msg:expr) => {
        connect_stream!($widget, $event($($args),*), $other_component.stream(), $msg);
    };

    // Connect to a GTK+ widget event.
    // This variant gives more control to the caller since it expects a `$msg` returning (Option<MSG>,
    // ReturnValue) where the ReturnValue is the value to return in the GTK+ callback.
    // Option<MSG> can be None if no message needs to be emitted.
    ($relm:expr, $widget:expr, $event:ident($($args:pat),*), return $msg:expr) => {{
        connect_stream!(return $relm.stream(), $widget, $event($($args),*), $msg);
    }};

    // Connect to a GTK+ widget event.
    ($relm:expr, $widget:expr, $event:ident($($args:pat),*), $msg:expr) => {{
        let stream = $relm.stream().clone();
        let _ = $widget.$event(move |$($args),*| {
            let msg: Option<_> = $crate::IntoOption::into_option($msg);
            if let Some(msg) = msg {
                stream.emit(msg);
            }
        });
    }};

    // Connect to a message reception.
    // TODO: create another macro rule accepting multiple patterns.
    ($src_component:ident @ $message:pat, $dst_component:expr, $msg:expr) => {
        let stream = $src_component.stream().clone();
        connect_stream!(stream@$message, $dst_component.stream(), $msg);
    };
}

/// Connect events to sending a message.
/// Similar to `connect!` but wants a stream instead of a component.
///
/// ## Rules
/// 1. Send `$msg` to `$other_stream` when the GTK+ `$event` is emitted on `$widget`.
///
/// 2. Send `$msg` to `$widget` when the `$message` is received on `$stream`.
#[macro_export]
macro_rules! connect_stream {
    // Connect to a GTK+ widget event.
    // This variant gives more control to the caller since it expects a `$msg` returning (Option<MSG>,
    // ReturnValue) where the ReturnValue is the value to return in the GTK+ callback.
    // Option<MSG> can be None if no message needs to be emitted.
    (return $stream:expr, $widget:expr, $event:ident($($args:pat),*), $msg:expr) => {{
        let stream = $stream.clone();
        let _ = $widget.$event(move |$($args),*| {
            let (msg, return_value) = $crate::IntoPair::into_pair($msg);
            let msg: Option<_> = $crate::IntoOption::into_option(msg);
            if let Some(msg) = msg {
                stream.emit(msg);
            }
            return_value
        });
    }};

    // Connect to a GTK+ widget event, sending a message to another widget.
    ($widget:expr, $event:ident($($args:pat),*), $other_stream:expr, $msg:expr) => {
        let stream = $other_stream.clone();
        let _ = $widget.$event(move |$($args),*| {
            let msg: Option<_> = $crate::IntoOption::into_option($msg);
            if let Some(msg) = msg {
                stream.emit(msg);
            }
        });
    };

    // Connect to a message reception.
    // TODO: create another macro rule accepting multiple patterns.
    ($src_stream:ident @ $message:pat, $dst_stream:expr, $msg:expr) => {
        let stream = $dst_stream.clone();
        $src_stream.observe(move |msg| {
            #[allow(unreachable_patterns)]
            match msg {
                &$message =>  {
                    let msg: Option<_> = $crate::IntoOption::into_option($msg);
                    if let Some(msg) = msg {
                        stream.emit(msg);
                    }
                },
                _ => (),
            }
        });
    };
}

/// Connect an asynchronous method call to send a message.
/// The variants with `$fail_msg` will send this message when there's an error.
/// Those without this argument will ignore the error.
#[macro_export]
macro_rules! connect_async {
    ($object:expr, $async_method:ident, $relm:expr, $msg:expr) => {
        connect_async!($object, $async_method(), $relm, $msg)
    };
    ($object:expr, $async_method:ident ( $($args:expr),* ), $relm:expr, $msg:expr) => {{
        // TODO: remove any use of Fragile when gio callbacks stop requiring Send.
        let stream = ::relm::vendor::fragile::Fragile::new($relm.stream().clone());
        $object.$async_method($($args,)* None, move |result| {
            if let Ok(result) = result {
                stream.into_inner().emit($msg(result));
            }
        });
    }};
    ($object:expr, $async_method:ident, $relm:expr, $msg:expr, $fail_msg:expr) => {
        connect_async!($object, $async_method(), $relm, $msg, $fail_msg)
    };
    ($object:expr, $async_method:ident ( $($args:expr),* ), $relm:expr, $msg:expr, $fail_msg:expr) => {{
        let event_stream = ::relm::vendor::fragile::Fragile::new($relm.stream().clone());
        let fail_event_stream = ::relm::vendor::fragile::Fragile::new($relm.stream().clone());
        $object.$async_method($($args,)* None, move |result| {
            match result {
                Ok(value) => event_stream.into_inner().emit($msg(value)),
                Err(error) => fail_event_stream.into_inner().emit($fail_msg(error)),
            }
        });
    }};
}

/// Connect an asynchronous function call to send a message.
/// The variants with `$fail_msg` will send this message when there's an error.
/// Those without this argument will ignore the error.
#[macro_export]
macro_rules! connect_async_func {
    ($class:ident :: $async_function:ident, $relm:expr, $msg:expr) => {
        connect_async_func!($class::$async_func(), $relm, $msg)
    };
    ($class:ident :: $async_func:ident ( $($args:expr),* ), $relm:expr, $msg:expr) => {{
        let stream = ::relm::vendor::fragile::Fragile::new($relm.stream().clone());
        $class::$async_func($($args,)* None, move |result| {
            if let Ok(result) = result {
                stream.into_inner().emit($msg(result));
            }
        });
    }};
    ($class:ident :: $async_func:ident, $relm:expr, $msg:expr, $fail_msg:expr) => {
        connect_async_func!($class::$async_func(), $relm, $msg, $fail_msg)
    };
    ($class:ident :: $async_func:ident ( $($args:expr),* ), $relm:expr, $msg:expr, $fail_msg:expr) => {{
        let event_stream = ::relm::vendor::fragile::Fragile::new($relm.stream().clone());
        let fail_event_stream = ::relm::vendor::fragile::Fragile::new($relm.stream().clone());
        $class::$async_func($($args,)* None, move |result| {
            match result {
                Ok(value) => event_stream.into_inner().emit($msg(value)),
                Err(error) => fail_event_stream.into_inner().emit($fail_msg(error)),
            }
        });

    }};
}

/// Like `connect_async!`, but also return a `Cancellable` to control the asynchronous request.
#[macro_export]
macro_rules! connect_async_full {
    ($object:expr, $async_method:ident, $relm:expr, $msg:expr) => {
        connect_async_full!($object, $async_method(), $relm, $msg)
    };
    ($object:expr, $async_method:ident ( $($args:expr),* ), $relm:expr, $msg:expr) => {{
        let cancellable = ::gio::Cancellable::new();
        let stream = ::relm::vendor::fragile::Fragile::new($relm.stream().clone());
        $object.$async_method($($args,)* Some(&cancellable), move |result| {
            if let Ok(result) = result {
                stream.into_inner().emit($msg(result));
            }
        });
        cancellable
    }};
    ($object:expr, $async_method:ident, $relm:expr, $msg:expr, $fail_msg:expr) => {
        connect_async_full!($object, $async_method(), $relm, $msg, $fail_msg)
    };
    ($object:expr, $async_method:ident ( $($args:expr),* ), $relm:expr, $msg:expr, $fail_msg:expr) => {{
        let cancellable = ::gio::Cancellable::new();
        let event_stream = ::relm::vendor::fragile::Fragile::new($relm.stream().clone());
        let fail_event_stream = ::relm::vendor::fragile::Fragile::new($relm.stream().clone());
        $object.$async_method($($args,)* Some(&cancellable), move |result| {
            match result {
                Ok(value) => event_stream.into_inner().emit($msg(value)),
                Err(error) => fail_event_stream.into_inner().emit($fail_msg(error)),
            }
        });

        cancellable
    }};
}

/// Like `connect_async_func!`, but also return a `Cancellable` to control the asynchronous request.
#[macro_export]
macro_rules! connect_async_func_full {
    ($class:ident :: $async_function:ident, $relm:expr, $msg:expr) => {
        connect_async!($async_func(), $relm, $msg)
    };
    ($class:ident :: $async_func:ident ( $($args:expr),* ), $relm:expr, $msg:expr) => {{
        let cancellable = ::gio::Cancellable::new();
        let stream = ::relm::vendor::fragile::Fragile::new($relm.stream().clone());
        $class::$async_func($($args,)* Some(&cancellable), move |result| {
            if let Ok(result) = result {
                stream.into_inner().emit($msg(result));
            }
        });
        cancellable
    }};
    ($class:ident :: $async_func:ident, $relm:expr, $msg:expr, $fail_msg:expr) => {
        connect_async!($async_func(), $relm, $msg, $fail_msg)
    };
    ($class:ident :: $async_func:ident ( $($args:expr),* ), $relm:expr, $msg:expr, $fail_msg:expr) => {{
        let cancellable = ::gio::Cancellable::new();
        let event_stream = ::relm::vendor::fragile::Fragile::new($relm.stream().clone());
        let fail_event_stream = ::relm::vendor::fragile::Fragile::new($relm.stream().clone());
        $class::$async_func($($args,)* Some(&cancellable), move |result| {
            match result {
                Ok(value) => event_stream.into_inner().emit($msg(value)),
                Err(error) => fail_event_stream.into_inner().emit($fail_msg(error)),
            }
        });

        cancellable
    }};
}
<|endoftext|>


<commit_before>use rustc_data_structures::fx::FxHashSet;
use std::env;
use std::path::{Path, PathBuf};
use std::fs;

use rustc::hir::def_id::CrateNum;
use rustc::middle::cstore::LibSource;

pub struct RPathConfig<'a> {
    pub used_crates: &'a [(CrateNum, LibSource)],
    pub out_filename: PathBuf,
    pub is_like_osx: bool,
    pub has_rpath: bool,
    pub linker_is_gnu: bool,
    pub get_install_prefix_lib_path: &'a mut dyn FnMut() -> PathBuf,
}

pub fn get_rpath_flags(config: &mut RPathConfig) -> Vec<String> {
    // No rpath on windows
    if !config.has_rpath {
        return Vec::new();
    }

    debug!("preparing the RPATH!");

    let libs = config.used_crates.clone();
    let libs = libs.iter().filter_map(|&(_, ref l)| l.option()).collect::<Vec<_>>();
    let rpaths = get_rpaths(config, &libs);
    let mut flags = rpaths_to_flags(&rpaths);

    // Use DT_RUNPATH instead of DT_RPATH if available
    if config.linker_is_gnu {
        flags.push("-Wl,--enable-new-dtags".to_owned());
    }

    flags
}

fn rpaths_to_flags(rpaths: &[String]) -> Vec<String> {
    let mut ret = Vec::with_capacity(rpaths.len()); // the minimum needed capacity

    for rpath in rpaths {
        if rpath.contains(',') {
            ret.push("-Wl,-rpath".into());
            ret.push("-Xlinker".into());
            ret.push(rpath.clone());
        } else {
            ret.push(format!("-Wl,-rpath,{}", &(*rpath)));
        }
    }

    ret
}

fn get_rpaths(config: &mut RPathConfig, libs: &[PathBuf]) -> Vec<String> {
    debug!("output: {:?}", config.out_filename.display());
    debug!("libs:");
    for libpath in libs {
        debug!("    {:?}", libpath.display());
    }

    // Use relative paths to the libraries. Binaries can be moved
    // as long as they maintain the relative relationship to the
    // crates they depend on.
    let rel_rpaths = get_rpaths_relative_to_output(config, libs);

    // And a final backup rpath to the global library location.
    let fallback_rpaths = vec![get_install_prefix_rpath(config)];

    fn log_rpaths(desc: &str, rpaths: &[String]) {
        debug!("{} rpaths:", desc);
        for rpath in rpaths {
            debug!("    {}", *rpath);
        }
    }

    log_rpaths("relative", &rel_rpaths);
    log_rpaths("fallback", &fallback_rpaths);

    let mut rpaths = rel_rpaths;
    rpaths.extend_from_slice(&fallback_rpaths);

    // Remove duplicates
    let rpaths = minimize_rpaths(&rpaths);

    rpaths
}

fn get_rpaths_relative_to_output(config: &mut RPathConfig,
                                 libs: &[PathBuf]) -> Vec<String> {
    libs.iter().map(|a| get_rpath_relative_to_output(config, a)).collect()
}

fn get_rpath_relative_to_output(config: &mut RPathConfig, lib: &Path) -> String {
    // Mac doesn't appear to support $ORIGIN
    let prefix = if config.is_like_osx {
        "@loader_path"
    } else {
        "$ORIGIN"
    };

    let cwd = env::current_dir().unwrap();
    let mut lib = fs::canonicalize(&cwd.join(lib)).unwrap_or_else(|_| cwd.join(lib));
    lib.pop();
    let mut output = cwd.join(&config.out_filename);
    output.pop();
    let output = fs::canonicalize(&output).unwrap_or(output);
    let relative = path_relative_from(&lib, &output).unwrap_or_else(||
        panic!("couldn't create relative path from {:?} to {:?}", output, lib));
    // FIXME (#9639): This needs to handle non-utf8 paths
    format!("{}/{}", prefix, relative.to_str().expect("non-utf8 component in path"))
}

// This routine is adapted from the *old* Path's `path_relative_from`
// function, which works differently from the new `relative_from` function.
// In particular, this handles the case on unix where both paths are
// absolute but with only the root as the common directory.
fn path_relative_from(path: &Path, base: &Path) -> Option<PathBuf> {
    use std::path::Component;

    if path.is_absolute() != base.is_absolute() {
        if path.is_absolute() {
            Some(PathBuf::from(path))
        } else {
            None
        }
    } else {
        let mut ita = path.components();
        let mut itb = base.components();
        let mut comps: Vec<Component> = vec![];
        loop {
            match (ita.next(), itb.next()) {
                (None, None) => break,
                (Some(a), None) => {
                    comps.push(a);
                    comps.extend(ita.by_ref());
                    break;
                }
                (None, _) => comps.push(Component::ParentDir),
                (Some(a), Some(b)) if comps.is_empty() && a == b => (),
                (Some(a), Some(b)) if b == Component::CurDir => comps.push(a),
                (Some(_), Some(b)) if b == Component::ParentDir => return None,
                (Some(a), Some(_)) => {
                    comps.push(Component::ParentDir);
                    comps.extend(itb.map(|_| Component::ParentDir));
                    comps.push(a);
                    comps.extend(ita.by_ref());
                    break;
                }
            }
        }
        Some(comps.iter().map(|c| c.as_os_str()).collect())
    }
}

fn get_install_prefix_rpath(config: &mut RPathConfig) -> String {
    let path = (config.get_install_prefix_lib_path)();
    let path = env::current_dir().unwrap().join(&path);
    // FIXME (#9639): This needs to handle non-utf8 paths
    path.to_str().expect("non-utf8 component in rpath").to_owned()
}

fn minimize_rpaths(rpaths: &[String]) -> Vec<String> {
    let mut set = FxHashSet::default();
    let mut minimized = Vec::new();
    for rpath in rpaths {
        if set.insert(rpath) {
            minimized.push(rpath.clone());
        }
    }
    minimized
}

#[cfg(all(unix, test))]
mod tests {
    use super::{RPathConfig};
    use super::{minimize_rpaths, rpaths_to_flags, get_rpath_relative_to_output};
    use std::path::{Path, PathBuf};

    #[test]
    fn test_rpaths_to_flags() {
        let flags = rpaths_to_flags(&[
            "path1".to_string(),
            "path2".to_string()
        ]);
        assert_eq!(flags,
                   ["-Wl,-rpath,path1",
                    "-Wl,-rpath,path2"]);
    }

    #[test]
    fn test_minimize1() {
        let res = minimize_rpaths(&[
            "rpath1".to_string(),
            "rpath2".to_string(),
            "rpath1".to_string()
        ]);
        assert!(res == [
            "rpath1",
            "rpath2",
        ]);
    }

    #[test]
    fn test_minimize2() {
        let res = minimize_rpaths(&[
            "1a".to_string(),
            "2".to_string(),
            "2".to_string(),
            "1a".to_string(),
            "4a".to_string(),
            "1a".to_string(),
            "2".to_string(),
            "3".to_string(),
            "4a".to_string(),
            "3".to_string()
        ]);
        assert!(res == [
            "1a",
            "2",
            "4a",
            "3",
        ]);
    }

    #[test]
    fn test_rpath_relative() {
        if cfg!(target_os = "macos") {
            let config = &mut RPathConfig {
                used_crates: Vec::new(),
                has_rpath: true,
                is_like_osx: true,
                linker_is_gnu: false,
                out_filename: PathBuf::from("bin/rustc"),
                get_install_prefix_lib_path: &mut || panic!(),
            };
            let res = get_rpath_relative_to_output(config,
                                                   Path::new("lib/libstd.so"));
            assert_eq!(res, "@loader_path/../lib");
        } else {
            let config = &mut RPathConfig {
                used_crates: Vec::new(),
                out_filename: PathBuf::from("bin/rustc"),
                get_install_prefix_lib_path: &mut || panic!(),
                has_rpath: true,
                is_like_osx: false,
                linker_is_gnu: true,
            };
            let res = get_rpath_relative_to_output(config,
                                                   Path::new("lib/libstd.so"));
            assert_eq!(res, "$ORIGIN/../lib");
        }
    }

    #[test]
    fn test_xlinker() {
        let args = rpaths_to_flags(&[
            "a/normal/path".to_string(),
            "a,comma,path".to_string()
        ]);

        assert_eq!(args, vec![
            "-Wl,-rpath,a/normal/path".to_string(),
            "-Wl,-rpath".to_string(),
            "-Xlinker".to_string(),
            "a,comma,path".to_string()
        ]);
    }
}
<commit_msg>rpath computation: explain why we pop()<commit_after>use rustc_data_structures::fx::FxHashSet;
use std::env;
use std::path::{Path, PathBuf};
use std::fs;

use rustc::hir::def_id::CrateNum;
use rustc::middle::cstore::LibSource;

pub struct RPathConfig<'a> {
    pub used_crates: &'a [(CrateNum, LibSource)],
    pub out_filename: PathBuf,
    pub is_like_osx: bool,
    pub has_rpath: bool,
    pub linker_is_gnu: bool,
    pub get_install_prefix_lib_path: &'a mut dyn FnMut() -> PathBuf,
}

pub fn get_rpath_flags(config: &mut RPathConfig) -> Vec<String> {
    // No rpath on windows
    if !config.has_rpath {
        return Vec::new();
    }

    debug!("preparing the RPATH!");

    let libs = config.used_crates.clone();
    let libs = libs.iter().filter_map(|&(_, ref l)| l.option()).collect::<Vec<_>>();
    let rpaths = get_rpaths(config, &libs);
    let mut flags = rpaths_to_flags(&rpaths);

    // Use DT_RUNPATH instead of DT_RPATH if available
    if config.linker_is_gnu {
        flags.push("-Wl,--enable-new-dtags".to_owned());
    }

    flags
}

fn rpaths_to_flags(rpaths: &[String]) -> Vec<String> {
    let mut ret = Vec::with_capacity(rpaths.len()); // the minimum needed capacity

    for rpath in rpaths {
        if rpath.contains(',') {
            ret.push("-Wl,-rpath".into());
            ret.push("-Xlinker".into());
            ret.push(rpath.clone());
        } else {
            ret.push(format!("-Wl,-rpath,{}", &(*rpath)));
        }
    }

    ret
}

fn get_rpaths(config: &mut RPathConfig, libs: &[PathBuf]) -> Vec<String> {
    debug!("output: {:?}", config.out_filename.display());
    debug!("libs:");
    for libpath in libs {
        debug!("    {:?}", libpath.display());
    }

    // Use relative paths to the libraries. Binaries can be moved
    // as long as they maintain the relative relationship to the
    // crates they depend on.
    let rel_rpaths = get_rpaths_relative_to_output(config, libs);

    // And a final backup rpath to the global library location.
    let fallback_rpaths = vec![get_install_prefix_rpath(config)];

    fn log_rpaths(desc: &str, rpaths: &[String]) {
        debug!("{} rpaths:", desc);
        for rpath in rpaths {
            debug!("    {}", *rpath);
        }
    }

    log_rpaths("relative", &rel_rpaths);
    log_rpaths("fallback", &fallback_rpaths);

    let mut rpaths = rel_rpaths;
    rpaths.extend_from_slice(&fallback_rpaths);

    // Remove duplicates
    let rpaths = minimize_rpaths(&rpaths);

    rpaths
}

fn get_rpaths_relative_to_output(config: &mut RPathConfig,
                                 libs: &[PathBuf]) -> Vec<String> {
    libs.iter().map(|a| get_rpath_relative_to_output(config, a)).collect()
}

fn get_rpath_relative_to_output(config: &mut RPathConfig, lib: &Path) -> String {
    // Mac doesn't appear to support $ORIGIN
    let prefix = if config.is_like_osx {
        "@loader_path"
    } else {
        "$ORIGIN"
    };

    let cwd = env::current_dir().unwrap();
    let mut lib = fs::canonicalize(&cwd.join(lib)).unwrap_or_else(|_| cwd.join(lib));
    lib.pop(); // strip filename
    let mut output = cwd.join(&config.out_filename);
    output.pop(); // strip filename
    let output = fs::canonicalize(&output).unwrap_or(output);
    let relative = path_relative_from(&lib, &output).unwrap_or_else(||
        panic!("couldn't create relative path from {:?} to {:?}", output, lib));
    // FIXME (#9639): This needs to handle non-utf8 paths
    format!("{}/{}", prefix, relative.to_str().expect("non-utf8 component in path"))
}

// This routine is adapted from the *old* Path's `path_relative_from`
// function, which works differently from the new `relative_from` function.
// In particular, this handles the case on unix where both paths are
// absolute but with only the root as the common directory.
fn path_relative_from(path: &Path, base: &Path) -> Option<PathBuf> {
    use std::path::Component;

    if path.is_absolute() != base.is_absolute() {
        if path.is_absolute() {
            Some(PathBuf::from(path))
        } else {
            None
        }
    } else {
        let mut ita = path.components();
        let mut itb = base.components();
        let mut comps: Vec<Component> = vec![];
        loop {
            match (ita.next(), itb.next()) {
                (None, None) => break,
                (Some(a), None) => {
                    comps.push(a);
                    comps.extend(ita.by_ref());
                    break;
                }
                (None, _) => comps.push(Component::ParentDir),
                (Some(a), Some(b)) if comps.is_empty() && a == b => (),
                (Some(a), Some(b)) if b == Component::CurDir => comps.push(a),
                (Some(_), Some(b)) if b == Component::ParentDir => return None,
                (Some(a), Some(_)) => {
                    comps.push(Component::ParentDir);
                    comps.extend(itb.map(|_| Component::ParentDir));
                    comps.push(a);
                    comps.extend(ita.by_ref());
                    break;
                }
            }
        }
        Some(comps.iter().map(|c| c.as_os_str()).collect())
    }
}

fn get_install_prefix_rpath(config: &mut RPathConfig) -> String {
    let path = (config.get_install_prefix_lib_path)();
    let path = env::current_dir().unwrap().join(&path);
    // FIXME (#9639): This needs to handle non-utf8 paths
    path.to_str().expect("non-utf8 component in rpath").to_owned()
}

fn minimize_rpaths(rpaths: &[String]) -> Vec<String> {
    let mut set = FxHashSet::default();
    let mut minimized = Vec::new();
    for rpath in rpaths {
        if set.insert(rpath) {
            minimized.push(rpath.clone());
        }
    }
    minimized
}

#[cfg(all(unix, test))]
mod tests {
    use super::{RPathConfig};
    use super::{minimize_rpaths, rpaths_to_flags, get_rpath_relative_to_output};
    use std::path::{Path, PathBuf};

    #[test]
    fn test_rpaths_to_flags() {
        let flags = rpaths_to_flags(&[
            "path1".to_string(),
            "path2".to_string()
        ]);
        assert_eq!(flags,
                   ["-Wl,-rpath,path1",
                    "-Wl,-rpath,path2"]);
    }

    #[test]
    fn test_minimize1() {
        let res = minimize_rpaths(&[
            "rpath1".to_string(),
            "rpath2".to_string(),
            "rpath1".to_string()
        ]);
        assert!(res == [
            "rpath1",
            "rpath2",
        ]);
    }

    #[test]
    fn test_minimize2() {
        let res = minimize_rpaths(&[
            "1a".to_string(),
            "2".to_string(),
            "2".to_string(),
            "1a".to_string(),
            "4a".to_string(),
            "1a".to_string(),
            "2".to_string(),
            "3".to_string(),
            "4a".to_string(),
            "3".to_string()
        ]);
        assert!(res == [
            "1a",
            "2",
            "4a",
            "3",
        ]);
    }

    #[test]
    fn test_rpath_relative() {
        if cfg!(target_os = "macos") {
            let config = &mut RPathConfig {
                used_crates: Vec::new(),
                has_rpath: true,
                is_like_osx: true,
                linker_is_gnu: false,
                out_filename: PathBuf::from("bin/rustc"),
                get_install_prefix_lib_path: &mut || panic!(),
            };
            let res = get_rpath_relative_to_output(config,
                                                   Path::new("lib/libstd.so"));
            assert_eq!(res, "@loader_path/../lib");
        } else {
            let config = &mut RPathConfig {
                used_crates: Vec::new(),
                out_filename: PathBuf::from("bin/rustc"),
                get_install_prefix_lib_path: &mut || panic!(),
                has_rpath: true,
                is_like_osx: false,
                linker_is_gnu: true,
            };
            let res = get_rpath_relative_to_output(config,
                                                   Path::new("lib/libstd.so"));
            assert_eq!(res, "$ORIGIN/../lib");
        }
    }

    #[test]
    fn test_xlinker() {
        let args = rpaths_to_flags(&[
            "a/normal/path".to_string(),
            "a,comma,path".to_string()
        ]);

        assert_eq!(args, vec![
            "-Wl,-rpath,a/normal/path".to_string(),
            "-Wl,-rpath".to_string(),
            "-Xlinker".to_string(),
            "a,comma,path".to_string()
        ]);
    }
}
<|endoftext|>


<commit_before>// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use llvm::ValueRef;
use rustc::middle::ty::Ty;
use rustc_mir::repr as mir;
use trans::base;
use trans::build;
use trans::common::Block;
use trans::datum;

use super::{MirContext, TempRef};

#[derive(Copy, Clone)]
pub struct OperandRef<'tcx> {
    // This will be "indirect" if `appropriate_rvalue_mode` returns
    // ByRef, and otherwise ByValue.
    pub llval: ValueRef,

    // The type of value being returned.
    pub ty: Ty<'tcx>
}

impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
    pub fn trans_operand(&mut self,
                         bcx: Block<'bcx, 'tcx>,
                         operand: &mir::Operand<'tcx>)
                         -> OperandRef<'tcx>
    {
        debug!("trans_operand(operand={:?})", operand);

        match *operand {
            mir::Operand::Consume(ref lvalue) => {
                // watch out for temporaries that do not have an
                // alloca; they are handled somewhat differently
                if let &mir::Lvalue::Temp(index) = lvalue {
                    match self.temps[index as usize] {
                        TempRef::Operand(Some(o)) => {
                            return o;
                        }
                        TempRef::Operand(None) => {
                            bcx.tcx().sess.bug(
                                &format!("use of {:?} before def", lvalue));
                        }
                        TempRef::Lvalue(..) => {
                            // use path below
                        }
                    }
                }

                // for most lvalues, to consume them we just load them
                // out from their home
                let tr_lvalue = self.trans_lvalue(bcx, lvalue);
                let ty = tr_lvalue.ty.to_ty(bcx.tcx());
                debug!("trans_operand: tr_lvalue={} @ {:?}",
                       bcx.val_to_string(tr_lvalue.llval),
                       ty);
                let llval = match datum::appropriate_rvalue_mode(bcx.ccx(), ty) {
                    datum::ByValue => build::Load(bcx, tr_lvalue.llval),
                    datum::ByRef => tr_lvalue.llval,
                };
                OperandRef {
                    llval: llval,
                    ty: ty
                }
            }

            mir::Operand::Constant(ref constant) => {
                let llval = self.trans_constant(bcx, constant);
                let ty = bcx.monomorphize(&constant.ty);
                OperandRef {
                    llval: llval,
                    ty: ty,
                }
            }
        }
    }

    pub fn trans_operand_into(&mut self,
                              bcx: Block<'bcx, 'tcx>,
                              lldest: ValueRef,
                              operand: &mir::Operand<'tcx>)
    {
        debug!("trans_operand_into(lldest={}, operand={:?})",
               bcx.val_to_string(lldest),
               operand);

        match *operand {
            mir::Operand::Consume(ref lvalue) => {
                let tr_lvalue = self.trans_lvalue(bcx, lvalue);
                let lvalue_ty = tr_lvalue.ty.to_ty(bcx.tcx());
                debug!("trans_operand_into: tr_lvalue={} @ {:?}",
                       bcx.val_to_string(tr_lvalue.llval),
                       lvalue_ty);
                base::memcpy_ty(bcx, lldest, tr_lvalue.llval, lvalue_ty);
            }

            mir::Operand::Constant(..) => {
                unimplemented!()
            }
        }
    }
}
<commit_msg>[MIR-trans] Fix handling of non-alloca temps in trans_operand_into()<commit_after>// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use llvm::ValueRef;
use rustc::middle::ty::Ty;
use rustc_mir::repr as mir;
use trans::base;
use trans::build;
use trans::common::Block;
use trans::datum;

use super::{MirContext, TempRef};

#[derive(Copy, Clone)]
pub struct OperandRef<'tcx> {
    // This will be "indirect" if `appropriate_rvalue_mode` returns
    // ByRef, and otherwise ByValue.
    pub llval: ValueRef,

    // The type of value being returned.
    pub ty: Ty<'tcx>
}

impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
    pub fn trans_operand(&mut self,
                         bcx: Block<'bcx, 'tcx>,
                         operand: &mir::Operand<'tcx>)
                         -> OperandRef<'tcx>
    {
        debug!("trans_operand(operand={:?})", operand);

        match *operand {
            mir::Operand::Consume(ref lvalue) => {
                // watch out for temporaries that do not have an
                // alloca; they are handled somewhat differently
                if let &mir::Lvalue::Temp(index) = lvalue {
                    match self.temps[index as usize] {
                        TempRef::Operand(Some(o)) => {
                            return o;
                        }
                        TempRef::Operand(None) => {
                            bcx.tcx().sess.bug(
                                &format!("use of {:?} before def", lvalue));
                        }
                        TempRef::Lvalue(..) => {
                            // use path below
                        }
                    }
                }

                // for most lvalues, to consume them we just load them
                // out from their home
                let tr_lvalue = self.trans_lvalue(bcx, lvalue);
                let ty = tr_lvalue.ty.to_ty(bcx.tcx());
                debug!("trans_operand: tr_lvalue={} @ {:?}",
                       bcx.val_to_string(tr_lvalue.llval),
                       ty);
                let llval = match datum::appropriate_rvalue_mode(bcx.ccx(), ty) {
                    datum::ByValue => build::Load(bcx, tr_lvalue.llval),
                    datum::ByRef => tr_lvalue.llval,
                };
                OperandRef {
                    llval: llval,
                    ty: ty
                }
            }

            mir::Operand::Constant(ref constant) => {
                let llval = self.trans_constant(bcx, constant);
                let ty = bcx.monomorphize(&constant.ty);
                OperandRef {
                    llval: llval,
                    ty: ty,
                }
            }
        }
    }

    pub fn trans_operand_into(&mut self,
                              bcx: Block<'bcx, 'tcx>,
                              lldest: ValueRef,
                              operand: &mir::Operand<'tcx>)
    {
        debug!("trans_operand_into(lldest={}, operand={:?})",
               bcx.val_to_string(lldest),
               operand);

        let o = self.trans_operand(bcx, operand);
        match datum::appropriate_rvalue_mode(bcx.ccx(), o.ty) {
            datum::ByValue => base::store_ty(bcx, o.llval, lldest, o.ty),
            datum::ByRef => base::memcpy_ty(bcx, lldest, o.llval, o.ty),
        };
    }
}
<|endoftext|>


<commit_before>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Orphan checker: every impl either implements a trait defined in this
//! crate or pertains to a type defined in this crate.

use middle::traits;
use middle::ty;
use syntax::ast::{Item, ItemImpl};
use syntax::ast;
use syntax::ast_util;
use syntax::codemap::Span;
use syntax::visit;

pub fn check(tcx: &ty::ctxt) {
    let mut orphan = OrphanChecker { tcx: tcx };
    visit::walk_crate(&mut orphan, tcx.map.krate());
}

struct OrphanChecker<'cx, 'tcx:'cx> {
    tcx: &'cx ty::ctxt<'tcx>
}

impl<'cx, 'tcx> OrphanChecker<'cx, 'tcx> {
    fn check_def_id(&self, item: &ast::Item, def_id: ast::DefId) {
        if def_id.krate != ast::LOCAL_CRATE {
            span_err!(self.tcx.sess, item.span, E0116,
                      "cannot define inherent `impl` for a type outside of the \
                       crate where the type is defined; define and implement \
                       a trait or new type instead");
        }
    }

    fn check_primitive_impl(&self,
                            impl_def_id: ast::DefId,
                            lang_def_id: Option<ast::DefId>,
                            lang: &str,
                            ty: &str,
                            span: Span) {
        match lang_def_id {
            Some(lang_def_id) if lang_def_id == impl_def_id => { /* OK */ },
            _ => {
                span_err!(self.tcx.sess, span, E0390,
                          "only a single inherent implementation marked with `#[lang = \"{}\"]` \
                           is allowed for the `{}` primitive", lang, ty);
            }
        }
    }

    /// Checks exactly one impl for orphan rules and other such
    /// restrictions.  In this fn, it can happen that multiple errors
    /// apply to a specific impl, so just return after reporting one
    /// to prevent inundating the user with a bunch of similar error
    /// reports.
    fn check_item(&self, item: &ast::Item) {
        let def_id = ast_util::local_def(item.id);
        match item.node {
            ast::ItemImpl(_, _, _, None, _, _) => {
                // For inherent impls, self type must be a nominal type
                // defined in this crate.
                debug!("coherence2::orphan check: inherent impl {}",
                       self.tcx.map.node_to_string(item.id));
                let self_ty = self.tcx.lookup_item_type(def_id).ty;
                match self_ty.sty {
                    ty::TyEnum(def, _) |
                    ty::TyStruct(def, _) => {
                        self.check_def_id(item, def.did);
                    }
                    ty::TyTrait(ref data) => {
                        self.check_def_id(item, data.principal_def_id());
                    }
                    ty::TyBox(..) => {
                        match self.tcx.lang_items.require_owned_box() {
                            Ok(trait_id) => self.check_def_id(item, trait_id),
                            Err(msg) => self.tcx.sess.span_fatal(item.span, &msg),
                        }
                    }
                    ty::TyChar => {
                        self.check_primitive_impl(def_id,
                                                  self.tcx.lang_items.char_impl(),
                                                  "char",
                                                  "char",
                                                  item.span);
                    }
                    ty::TyStr => {
                        self.check_primitive_impl(def_id,
                                                  self.tcx.lang_items.str_impl(),
                                                  "str",
                                                  "str",
                                                  item.span);
                    }
                    ty::TySlice(_) => {
                        self.check_primitive_impl(def_id,
                                                  self.tcx.lang_items.slice_impl(),
                                                  "slice",
                                                  "[T]",
                                                  item.span);
                    }
                    ty::TyRawPtr(ty::TypeAndMut { ty: _, mutbl: ast::MutImmutable }) => {
                        self.check_primitive_impl(def_id,
                                                  self.tcx.lang_items.const_ptr_impl(),
                                                  "const_ptr",
                                                  "*const T",
                                                  item.span);
                    }
                    ty::TyRawPtr(ty::TypeAndMut { ty: _, mutbl: ast::MutMutable }) => {
                        self.check_primitive_impl(def_id,
                                                  self.tcx.lang_items.mut_ptr_impl(),
                                                  "mut_ptr",
                                                  "*mut T",
                                                  item.span);
                    }
                    ty::TyInt(ast::TyI8) => {
                        self.check_primitive_impl(def_id,
                                                  self.tcx.lang_items.i8_impl(),
                                                  "i8",
                                                  "i8",
                                                  item.span);
                    }
                    ty::TyInt(ast::TyI16) => {
                        self.check_primitive_impl(def_id,
                                                  self.tcx.lang_items.i16_impl(),
                                                  "i16",
                                                  "i16",
                                                  item.span);
                    }
                    ty::TyInt(ast::TyI32) => {
                        self.check_primitive_impl(def_id,
                                                  self.tcx.lang_items.i32_impl(),
                                                  "i32",
                                                  "i32",
                                                  item.span);
                    }
                    ty::TyInt(ast::TyI64) => {
                        self.check_primitive_impl(def_id,
                                                  self.tcx.lang_items.i64_impl(),
                                                  "i64",
                                                  "i64",
                                                  item.span);
                    }
                    ty::TyInt(ast::TyIs) => {
                        self.check_primitive_impl(def_id,
                                                  self.tcx.lang_items.isize_impl(),
                                                  "isize",
                                                  "isize",
                                                  item.span);
                    }
                    ty::TyUint(ast::TyU8) => {
                        self.check_primitive_impl(def_id,
                                                  self.tcx.lang_items.u8_impl(),
                                                  "u8",
                                                  "u8",
                                                  item.span);
                    }
                    ty::TyUint(ast::TyU16) => {
                        self.check_primitive_impl(def_id,
                                                  self.tcx.lang_items.u16_impl(),
                                                  "u16",
                                                  "u16",
                                                  item.span);
                    }
                    ty::TyUint(ast::TyU32) => {
                        self.check_primitive_impl(def_id,
                                                  self.tcx.lang_items.u32_impl(),
                                                  "u32",
                                                  "u32",
                                                  item.span);
                    }
                    ty::TyUint(ast::TyU64) => {
                        self.check_primitive_impl(def_id,
                                                  self.tcx.lang_items.u64_impl(),
                                                  "u64",
                                                  "u64",
                                                  item.span);
                    }
                    ty::TyUint(ast::TyUs) => {
                        self.check_primitive_impl(def_id,
                                                  self.tcx.lang_items.usize_impl(),
                                                  "usize",
                                                  "usize",
                                                  item.span);
                    }
                    ty::TyFloat(ast::TyF32) => {
                        self.check_primitive_impl(def_id,
                                                  self.tcx.lang_items.f32_impl(),
                                                  "f32",
                                                  "f32",
                                                  item.span);
                    }
                    ty::TyFloat(ast::TyF64) => {
                        self.check_primitive_impl(def_id,
                                                  self.tcx.lang_items.f64_impl(),
                                                  "f64",
                                                  "f64",
                                                  item.span);
                    }
                    _ => {
                        span_err!(self.tcx.sess, item.span, E0118,
                                  "no base type found for inherent implementation; \
                                   implement a trait or new type instead");
                        return;
                    }
                }
            }
            ast::ItemImpl(_, _, _, Some(_), _, _) => {
                // "Trait" impl
                debug!("coherence2::orphan check: trait impl {}",
                       self.tcx.map.node_to_string(item.id));
                let trait_ref = self.tcx.impl_trait_ref(def_id).unwrap();
                let trait_def_id = trait_ref.def_id;
                match traits::orphan_check(self.tcx, def_id) {
                    Ok(()) => { }
                    Err(traits::OrphanCheckErr::NoLocalInputType) => {
                        span_err!(
                            self.tcx.sess, item.span, E0117,
                            "the impl does not reference any \
                             types defined in this crate; \
                             only traits defined in the current crate can be \
                             implemented for arbitrary types");
                        return;
                    }
                    Err(traits::OrphanCheckErr::UncoveredTy(param_ty)) => {
                        span_err!(self.tcx.sess, item.span, E0210,
                                "type parameter `{}` must be used as the type parameter for \
                                 some local type (e.g. `MyStruct<T>`); only traits defined in \
                                 the current crate can be implemented for a type parameter",
                                param_ty);
                        return;
                    }
                }

                // In addition to the above rules, we restrict impls of defaulted traits
                // so that they can only be implemented on structs/enums. To see why this
                // restriction exists, consider the following example (#22978). Imagine
                // that crate A defines a defaulted trait `Foo` and a fn that operates
                // on pairs of types:
                //
                // ```
                // // Crate A
                // trait Foo { }
                // impl Foo for .. { }
                // fn two_foos<A:Foo,B:Foo>(..) {
                //     one_foo::<(A,B)>(..)
                // }
                // fn one_foo<T:Foo>(..) { .. }
                // ```
                //
                // This type-checks fine; in particular the fn
                // `two_foos` is able to conclude that `(A,B):Foo`
                // because `A:Foo` and `B:Foo`.
                //
                // Now imagine that crate B comes along and does the following:
                //
                // ```
                // struct A { }
                // struct B { }
                // impl Foo for A { }
                // impl Foo for B { }
                // impl !Send for (A, B) { }
                // ```
                //
                // This final impl is legal according to the orpan
                // rules, but it invalidates the reasoning from
                // `two_foos` above.
                debug!("trait_ref={:?} trait_def_id={:?} trait_has_default_impl={}",
                       trait_ref,
                       trait_def_id,
                       self.tcx.trait_has_default_impl(trait_def_id));
                if
                    self.tcx.trait_has_default_impl(trait_def_id) &&
                    trait_def_id.krate != ast::LOCAL_CRATE
                {
                    let self_ty = trait_ref.self_ty();
                    let opt_self_def_id = match self_ty.sty {
                        ty::TyStruct(self_def, _) | ty::TyEnum(self_def, _) =>
                            Some(self_def.did),
                        ty::TyBox(..) =>
                            self.tcx.lang_items.owned_box(),
                        _ =>
                            None
                    };

                    let msg = match opt_self_def_id {
                        // We only want to permit structs/enums, but not *all* structs/enums.
                        // They must be local to the current crate, so that people
                        // can't do `unsafe impl Send for Rc<SomethingLocal>` or
                        // `impl !Send for Box<SomethingLocalAndSend>`.
                        Some(self_def_id) => {
                            if self_def_id.krate == ast::LOCAL_CRATE {
                                None
                            } else {
                                Some(format!(
                                    "cross-crate traits with a default impl, like `{}`, \
                                     can only be implemented for a struct/enum type \
                                     defined in the current crate",
                                    self.tcx.item_path_str(trait_def_id)))
                            }
                        }
                        _ => {
                            Some(format!(
                                "cross-crate traits with a default impl, like `{}`, \
                                 can only be implemented for a struct/enum type, \
                                 not `{}`",
                                self.tcx.item_path_str(trait_def_id),
                                self_ty))
                        }
                    };

                    if let Some(msg) = msg {
                        span_err!(self.tcx.sess, item.span, E0321, "{}", msg);
                        return;
                    }
                }

                // Disallow *all* explicit impls of `Sized` and `Unsize` for now.
                if Some(trait_def_id) == self.tcx.lang_items.sized_trait() {
                    span_err!(self.tcx.sess, item.span, E0322,
                              "explicit impls for the `Sized` trait are not permitted");
                    return;
                }
                if Some(trait_def_id) == self.tcx.lang_items.unsize_trait() {
                    span_err!(self.tcx.sess, item.span, E0328,
                              "explicit impls for the `Unsize` trait are not permitted");
                    return;
                }
            }
            ast::ItemDefaultImpl(..) => {
                // "Trait" impl
                debug!("coherence2::orphan check: default trait impl {}",
                       self.tcx.map.node_to_string(item.id));
                let trait_ref = self.tcx.impl_trait_ref(def_id).unwrap();
                if trait_ref.def_id.krate != ast::LOCAL_CRATE {
                    span_err!(self.tcx.sess, item.span, E0318,
                              "cannot create default implementations for traits outside the \
                               crate they're defined in; define a new trait instead");
                    return;
                }
            }
            _ => {
                // Not an impl
            }
        }
    }
}

impl<'cx, 'tcx,'v> visit::Visitor<'v> for OrphanChecker<'cx, 'tcx> {
    fn visit_item(&mut self, item: &ast::Item) {
        self.check_item(item);
        visit::walk_item(self, item);
    }
}
<commit_msg>add help for E0390<commit_after>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Orphan checker: every impl either implements a trait defined in this
//! crate or pertains to a type defined in this crate.

use middle::traits;
use middle::ty;
use syntax::ast::{Item, ItemImpl};
use syntax::ast;
use syntax::ast_util;
use syntax::codemap::Span;
use syntax::visit;

pub fn check(tcx: &ty::ctxt) {
    let mut orphan = OrphanChecker { tcx: tcx };
    visit::walk_crate(&mut orphan, tcx.map.krate());
}

struct OrphanChecker<'cx, 'tcx:'cx> {
    tcx: &'cx ty::ctxt<'tcx>
}

impl<'cx, 'tcx> OrphanChecker<'cx, 'tcx> {
    fn check_def_id(&self, item: &ast::Item, def_id: ast::DefId) {
        if def_id.krate != ast::LOCAL_CRATE {
            span_err!(self.tcx.sess, item.span, E0116,
                      "cannot define inherent `impl` for a type outside of the \
                       crate where the type is defined; define and implement \
                       a trait or new type instead");
        }
    }

    fn check_primitive_impl(&self,
                            impl_def_id: ast::DefId,
                            lang_def_id: Option<ast::DefId>,
                            lang: &str,
                            ty: &str,
                            span: Span) {
        match lang_def_id {
            Some(lang_def_id) if lang_def_id == impl_def_id => { /* OK */ },
            _ => {
                span_err!(self.tcx.sess, span, E0390,
                          "only a single inherent implementation marked with `#[lang = \"{}\"]` \
                           is allowed for the `{}` primitive", lang, ty);
                span_help!(self.tcx.sess, span,
                           "consider using a trait to implement these methods");
            }
        }
    }

    /// Checks exactly one impl for orphan rules and other such
    /// restrictions.  In this fn, it can happen that multiple errors
    /// apply to a specific impl, so just return after reporting one
    /// to prevent inundating the user with a bunch of similar error
    /// reports.
    fn check_item(&self, item: &ast::Item) {
        let def_id = ast_util::local_def(item.id);
        match item.node {
            ast::ItemImpl(_, _, _, None, _, _) => {
                // For inherent impls, self type must be a nominal type
                // defined in this crate.
                debug!("coherence2::orphan check: inherent impl {}",
                       self.tcx.map.node_to_string(item.id));
                let self_ty = self.tcx.lookup_item_type(def_id).ty;
                match self_ty.sty {
                    ty::TyEnum(def, _) |
                    ty::TyStruct(def, _) => {
                        self.check_def_id(item, def.did);
                    }
                    ty::TyTrait(ref data) => {
                        self.check_def_id(item, data.principal_def_id());
                    }
                    ty::TyBox(..) => {
                        match self.tcx.lang_items.require_owned_box() {
                            Ok(trait_id) => self.check_def_id(item, trait_id),
                            Err(msg) => self.tcx.sess.span_fatal(item.span, &msg),
                        }
                    }
                    ty::TyChar => {
                        self.check_primitive_impl(def_id,
                                                  self.tcx.lang_items.char_impl(),
                                                  "char",
                                                  "char",
                                                  item.span);
                    }
                    ty::TyStr => {
                        self.check_primitive_impl(def_id,
                                                  self.tcx.lang_items.str_impl(),
                                                  "str",
                                                  "str",
                                                  item.span);
                    }
                    ty::TySlice(_) => {
                        self.check_primitive_impl(def_id,
                                                  self.tcx.lang_items.slice_impl(),
                                                  "slice",
                                                  "[T]",
                                                  item.span);
                    }
                    ty::TyRawPtr(ty::TypeAndMut { ty: _, mutbl: ast::MutImmutable }) => {
                        self.check_primitive_impl(def_id,
                                                  self.tcx.lang_items.const_ptr_impl(),
                                                  "const_ptr",
                                                  "*const T",
                                                  item.span);
                    }
                    ty::TyRawPtr(ty::TypeAndMut { ty: _, mutbl: ast::MutMutable }) => {
                        self.check_primitive_impl(def_id,
                                                  self.tcx.lang_items.mut_ptr_impl(),
                                                  "mut_ptr",
                                                  "*mut T",
                                                  item.span);
                    }
                    ty::TyInt(ast::TyI8) => {
                        self.check_primitive_impl(def_id,
                                                  self.tcx.lang_items.i8_impl(),
                                                  "i8",
                                                  "i8",
                                                  item.span);
                    }
                    ty::TyInt(ast::TyI16) => {
                        self.check_primitive_impl(def_id,
                                                  self.tcx.lang_items.i16_impl(),
                                                  "i16",
                                                  "i16",
                                                  item.span);
                    }
                    ty::TyInt(ast::TyI32) => {
                        self.check_primitive_impl(def_id,
                                                  self.tcx.lang_items.i32_impl(),
                                                  "i32",
                                                  "i32",
                                                  item.span);
                    }
                    ty::TyInt(ast::TyI64) => {
                        self.check_primitive_impl(def_id,
                                                  self.tcx.lang_items.i64_impl(),
                                                  "i64",
                                                  "i64",
                                                  item.span);
                    }
                    ty::TyInt(ast::TyIs) => {
                        self.check_primitive_impl(def_id,
                                                  self.tcx.lang_items.isize_impl(),
                                                  "isize",
                                                  "isize",
                                                  item.span);
                    }
                    ty::TyUint(ast::TyU8) => {
                        self.check_primitive_impl(def_id,
                                                  self.tcx.lang_items.u8_impl(),
                                                  "u8",
                                                  "u8",
                                                  item.span);
                    }
                    ty::TyUint(ast::TyU16) => {
                        self.check_primitive_impl(def_id,
                                                  self.tcx.lang_items.u16_impl(),
                                                  "u16",
                                                  "u16",
                                                  item.span);
                    }
                    ty::TyUint(ast::TyU32) => {
                        self.check_primitive_impl(def_id,
                                                  self.tcx.lang_items.u32_impl(),
                                                  "u32",
                                                  "u32",
                                                  item.span);
                    }
                    ty::TyUint(ast::TyU64) => {
                        self.check_primitive_impl(def_id,
                                                  self.tcx.lang_items.u64_impl(),
                                                  "u64",
                                                  "u64",
                                                  item.span);
                    }
                    ty::TyUint(ast::TyUs) => {
                        self.check_primitive_impl(def_id,
                                                  self.tcx.lang_items.usize_impl(),
                                                  "usize",
                                                  "usize",
                                                  item.span);
                    }
                    ty::TyFloat(ast::TyF32) => {
                        self.check_primitive_impl(def_id,
                                                  self.tcx.lang_items.f32_impl(),
                                                  "f32",
                                                  "f32",
                                                  item.span);
                    }
                    ty::TyFloat(ast::TyF64) => {
                        self.check_primitive_impl(def_id,
                                                  self.tcx.lang_items.f64_impl(),
                                                  "f64",
                                                  "f64",
                                                  item.span);
                    }
                    _ => {
                        span_err!(self.tcx.sess, item.span, E0118,
                                  "no base type found for inherent implementation; \
                                   implement a trait or new type instead");
                        return;
                    }
                }
            }
            ast::ItemImpl(_, _, _, Some(_), _, _) => {
                // "Trait" impl
                debug!("coherence2::orphan check: trait impl {}",
                       self.tcx.map.node_to_string(item.id));
                let trait_ref = self.tcx.impl_trait_ref(def_id).unwrap();
                let trait_def_id = trait_ref.def_id;
                match traits::orphan_check(self.tcx, def_id) {
                    Ok(()) => { }
                    Err(traits::OrphanCheckErr::NoLocalInputType) => {
                        span_err!(
                            self.tcx.sess, item.span, E0117,
                            "the impl does not reference any \
                             types defined in this crate; \
                             only traits defined in the current crate can be \
                             implemented for arbitrary types");
                        return;
                    }
                    Err(traits::OrphanCheckErr::UncoveredTy(param_ty)) => {
                        span_err!(self.tcx.sess, item.span, E0210,
                                "type parameter `{}` must be used as the type parameter for \
                                 some local type (e.g. `MyStruct<T>`); only traits defined in \
                                 the current crate can be implemented for a type parameter",
                                param_ty);
                        return;
                    }
                }

                // In addition to the above rules, we restrict impls of defaulted traits
                // so that they can only be implemented on structs/enums. To see why this
                // restriction exists, consider the following example (#22978). Imagine
                // that crate A defines a defaulted trait `Foo` and a fn that operates
                // on pairs of types:
                //
                // ```
                // // Crate A
                // trait Foo { }
                // impl Foo for .. { }
                // fn two_foos<A:Foo,B:Foo>(..) {
                //     one_foo::<(A,B)>(..)
                // }
                // fn one_foo<T:Foo>(..) { .. }
                // ```
                //
                // This type-checks fine; in particular the fn
                // `two_foos` is able to conclude that `(A,B):Foo`
                // because `A:Foo` and `B:Foo`.
                //
                // Now imagine that crate B comes along and does the following:
                //
                // ```
                // struct A { }
                // struct B { }
                // impl Foo for A { }
                // impl Foo for B { }
                // impl !Send for (A, B) { }
                // ```
                //
                // This final impl is legal according to the orpan
                // rules, but it invalidates the reasoning from
                // `two_foos` above.
                debug!("trait_ref={:?} trait_def_id={:?} trait_has_default_impl={}",
                       trait_ref,
                       trait_def_id,
                       self.tcx.trait_has_default_impl(trait_def_id));
                if
                    self.tcx.trait_has_default_impl(trait_def_id) &&
                    trait_def_id.krate != ast::LOCAL_CRATE
                {
                    let self_ty = trait_ref.self_ty();
                    let opt_self_def_id = match self_ty.sty {
                        ty::TyStruct(self_def, _) | ty::TyEnum(self_def, _) =>
                            Some(self_def.did),
                        ty::TyBox(..) =>
                            self.tcx.lang_items.owned_box(),
                        _ =>
                            None
                    };

                    let msg = match opt_self_def_id {
                        // We only want to permit structs/enums, but not *all* structs/enums.
                        // They must be local to the current crate, so that people
                        // can't do `unsafe impl Send for Rc<SomethingLocal>` or
                        // `impl !Send for Box<SomethingLocalAndSend>`.
                        Some(self_def_id) => {
                            if self_def_id.krate == ast::LOCAL_CRATE {
                                None
                            } else {
                                Some(format!(
                                    "cross-crate traits with a default impl, like `{}`, \
                                     can only be implemented for a struct/enum type \
                                     defined in the current crate",
                                    self.tcx.item_path_str(trait_def_id)))
                            }
                        }
                        _ => {
                            Some(format!(
                                "cross-crate traits with a default impl, like `{}`, \
                                 can only be implemented for a struct/enum type, \
                                 not `{}`",
                                self.tcx.item_path_str(trait_def_id),
                                self_ty))
                        }
                    };

                    if let Some(msg) = msg {
                        span_err!(self.tcx.sess, item.span, E0321, "{}", msg);
                        return;
                    }
                }

                // Disallow *all* explicit impls of `Sized` and `Unsize` for now.
                if Some(trait_def_id) == self.tcx.lang_items.sized_trait() {
                    span_err!(self.tcx.sess, item.span, E0322,
                              "explicit impls for the `Sized` trait are not permitted");
                    return;
                }
                if Some(trait_def_id) == self.tcx.lang_items.unsize_trait() {
                    span_err!(self.tcx.sess, item.span, E0328,
                              "explicit impls for the `Unsize` trait are not permitted");
                    return;
                }
            }
            ast::ItemDefaultImpl(..) => {
                // "Trait" impl
                debug!("coherence2::orphan check: default trait impl {}",
                       self.tcx.map.node_to_string(item.id));
                let trait_ref = self.tcx.impl_trait_ref(def_id).unwrap();
                if trait_ref.def_id.krate != ast::LOCAL_CRATE {
                    span_err!(self.tcx.sess, item.span, E0318,
                              "cannot create default implementations for traits outside the \
                               crate they're defined in; define a new trait instead");
                    return;
                }
            }
            _ => {
                // Not an impl
            }
        }
    }
}

impl<'cx, 'tcx,'v> visit::Visitor<'v> for OrphanChecker<'cx, 'tcx> {
    fn visit_item(&mut self, item: &ast::Item) {
        self.check_item(item);
        visit::walk_item(self, item);
    }
}
<|endoftext|>


<commit_before>
use temp;
use errors::*;
use utils;

use std::path::Path;
use std::fmt;
use std::env;

use regex::Regex;
use hyper;

pub const DEFAULT_DIST_ROOT: &'static str = "https://static.rust-lang.org/dist";
pub const UPDATE_HASH_LEN: usize = 20;

pub struct ToolchainDesc {
	pub arch: Option<String>,
	pub os: Option<String>,
	pub env: Option<String>,
	pub channel: String,
	pub date: Option<String>,
}

impl ToolchainDesc {
	pub fn from_str(name: &str) -> Option<Self> {
		let archs = ["i686", "x86_64"];
		let oses = ["pc-windows", "unknown-linux", "apple-darwin"];
		let envs = ["gnu", "msvc"];
		let channels = ["nightly", "beta", "stable"];

		let pattern = format!(
			r"^(?:({})-)?(?:({})-)?(?:({})-)?({})(?:-(\d{{4}}-\d{{2}}-\d{{2}}))?$",
			archs.join("|"), oses.join("|"), envs.join("|"), channels.join("|")
			);

		let re = Regex::new(&pattern).unwrap();
		re.captures(name).map(|c| {
			fn fn_map(s: &str) -> Option<String> {
				if s == "" {
					None
				} else {
					Some(s.to_owned())
				}
			}

			ToolchainDesc {
				arch: c.at(1).and_then(fn_map),
				os: c.at(2).and_then(fn_map),
				env: c.at(3).and_then(fn_map),
				channel: c.at(4).unwrap().to_owned(),
				date: c.at(5).and_then(fn_map),
			}
		})
	}

	pub fn manifest_url(&self, dist_root: &str) -> String {
		match self.date {
			None =>
				format!("{}/channel-rust-{}", dist_root, self.channel),
			Some(ref date) =>
				format!("{}/{}/channel-rust-{}", dist_root, date, self.channel),
		}
	}

	pub fn package_dir(&self, dist_root: &str) -> String {
		match self.date {
			None =>
				format!("{}", dist_root),
			Some(ref date) =>
				format!("{}/{}", dist_root, date),
		}
	}

	pub fn target_triple(&self) -> Option<String> {
		let (host_arch, host_os, host_env) = get_host_triple();
		let arch = self.arch.as_ref().map(|s| &**s).unwrap_or(host_arch);
		let os = self.os.as_ref().map(|s| &**s).or(host_os);
		let env = self.env.as_ref().map(|s| &**s).or(host_env);

		os.map(|os| {
			if let Some(ref env) = env {
				format!("{}-{}-{}", arch, os, env)
			} else {
				format!("{}-{}", arch, os)
			}
		})
	}

	pub fn download_manifest<'a>(&self, cfg: DownloadCfg<'a>) -> Result<Manifest<'a>> {
		let url = self.manifest_url(cfg.dist_root);
		let package_dir = self.package_dir(cfg.dist_root);

		let manifest = try!(download_and_check(&url, None, "", cfg)).unwrap().0;

		Ok(Manifest(manifest, package_dir))
	}

	pub fn full_spec(&self) -> String {
		let triple = self.target_triple().unwrap_or_else(|| "<invalid target triple>".to_owned());
		if let Some(ref date) = self.date {
			format!("{}-{}-{}", triple, &self.channel, date)
		} else {
			format!("{}-{} (tracking)", triple, &self.channel)
		}
	}
}

pub struct Manifest<'a>(temp::File<'a>, String);

impl<'a> Manifest<'a> {
	pub fn package_url(&self, package: &str, target_triple: &str, ext: &str) -> Result<Option<String>> {
		let suffix = target_triple.to_owned() + ext;
		utils::match_file("manifest", &self.0, |line| {
			if line.starts_with(package) && line.ends_with(&suffix) {
				Some(format!("{}/{}", &self.1, line))
			} else {
				None
			}
		})
	}
}

impl fmt::Display for ToolchainDesc {
	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
		if let Some(ref arch) = self.arch {
			try!(write!(f, "{}-", arch));
		}
		if let Some(ref os) = self.os {
			try!(write!(f, "{}-", os));
		}
		if let Some(ref env) = self.env {
			try!(write!(f, "{}-", env));
		}

		try!(write!(f, "{}", &self.channel));

		if let Some(ref date) = self.date {
			try!(write!(f, "-{}", date));
		}

		Ok(())
	}
}
fn parse_url(url: &str) -> Result<hyper::Url> {
	hyper::Url::parse(url).map_err(|_| Error::InvalidUrl)
}

pub fn download_and_check<'a>(url: &str, update_hash: Option<&Path>, ext: &str, cfg: DownloadCfg<'a>) -> Result<Option<(temp::File<'a>, String)>> {
	let hash = try!(download_hash(url, cfg));
	let partial_hash: String = hash.chars().take(UPDATE_HASH_LEN).collect();

	if let Some(hash_file) = update_hash {
		if utils::is_file(hash_file) {
			if let Ok(contents) = utils::read_file("update hash", hash_file) {
				if contents == partial_hash {
					// Skip download, update hash matches
					cfg.notify_handler.call(Notification::UpdateHashMatches(&partial_hash));
					return Ok(None);
				}
			} else {
				cfg.notify_handler.call(Notification::CantReadUpdateHash(hash_file));
			}
		} else {
			cfg.notify_handler.call(Notification::NoUpdateHash(hash_file));
		}
	}

	let url = try!(parse_url(url));
	let file = try!(cfg.temp_cfg.new_file_with_ext(ext));
	try!(utils::download_file(url, &file, cfg.notify_handler));
	// TODO: Actually download and check the checksum and signature of the file
	Ok(Some((file, partial_hash)))
}

#[derive(Copy, Clone)]
pub struct DownloadCfg<'a> {
	pub dist_root: &'a str,
	pub temp_cfg: &'a temp::Cfg,
	pub notify_handler: &'a NotifyHandler,
}

pub fn download_dist<'a>(toolchain: &str, update_hash: Option<&Path>, cfg: DownloadCfg<'a>) -> Result<Option<(temp::File<'a>, String)>> {
	let desc = try!(ToolchainDesc::from_str(toolchain)
		.ok_or(Error::InvalidToolchainName));

	let target_triple = try!(desc.target_triple().ok_or_else(|| Error::UnsupportedHost(desc.full_spec())));
	let ext = get_installer_ext();

	let manifest = try!(desc.download_manifest(cfg));

	let maybe_url = try!(manifest.package_url("rust", &target_triple, ext));

	let url = try!(maybe_url.ok_or_else(|| Error::UnsupportedHost(desc.full_spec())));

	download_and_check(&url, update_hash, ext, cfg)
}

pub fn get_host_triple() -> (&'static str, Option<&'static str>, Option<&'static str>) {
	let arch = env::consts::ARCH;

	let os = match env::consts::OS {
		"windows" => Some("pc-windows"),
		"linux" => Some("unknown-linux"),
		"macos" => Some("apple-darwin"),
		_ => None,
	};

	let env = match () {
		() if cfg!(target_env = "gnu") => Some("gnu"),
		() if cfg!(target_env = "msvc") => Some("msvc"),
		_ => None,
	};

	(arch, os, env)
}

pub fn get_installer_ext() -> &'static str {
	if cfg!(windows) {
		return ".msi"
	}
	".tar.gz"
}

pub fn download_hash(url: &str, cfg: DownloadCfg) -> Result<String> {
	let hash_url = try!(parse_url(&(url.to_owned() + ".sha256")));
	let hash_file = try!(cfg.temp_cfg.new_file());

	try!(utils::download_file(hash_url, &hash_file, cfg.notify_handler));

	utils::read_file("hash", &hash_file)
}
<commit_msg>Fix 32-bit host triple detection<commit_after>
use temp;
use errors::*;
use utils;

use std::path::Path;
use std::fmt;
use std::env;

use regex::Regex;
use hyper;

pub const DEFAULT_DIST_ROOT: &'static str = "https://static.rust-lang.org/dist";
pub const UPDATE_HASH_LEN: usize = 20;

pub struct ToolchainDesc {
	pub arch: Option<String>,
	pub os: Option<String>,
	pub env: Option<String>,
	pub channel: String,
	pub date: Option<String>,
}

impl ToolchainDesc {
	pub fn from_str(name: &str) -> Option<Self> {
		let archs = ["i686", "x86_64"];
		let oses = ["pc-windows", "unknown-linux", "apple-darwin"];
		let envs = ["gnu", "msvc"];
		let channels = ["nightly", "beta", "stable"];

		let pattern = format!(
			r"^(?:({})-)?(?:({})-)?(?:({})-)?({})(?:-(\d{{4}}-\d{{2}}-\d{{2}}))?$",
			archs.join("|"), oses.join("|"), envs.join("|"), channels.join("|")
			);

		let re = Regex::new(&pattern).unwrap();
		re.captures(name).map(|c| {
			fn fn_map(s: &str) -> Option<String> {
				if s == "" {
					None
				} else {
					Some(s.to_owned())
				}
			}

			ToolchainDesc {
				arch: c.at(1).and_then(fn_map),
				os: c.at(2).and_then(fn_map),
				env: c.at(3).and_then(fn_map),
				channel: c.at(4).unwrap().to_owned(),
				date: c.at(5).and_then(fn_map),
			}
		})
	}

	pub fn manifest_url(&self, dist_root: &str) -> String {
		match self.date {
			None =>
				format!("{}/channel-rust-{}", dist_root, self.channel),
			Some(ref date) =>
				format!("{}/{}/channel-rust-{}", dist_root, date, self.channel),
		}
	}

	pub fn package_dir(&self, dist_root: &str) -> String {
		match self.date {
			None =>
				format!("{}", dist_root),
			Some(ref date) =>
				format!("{}/{}", dist_root, date),
		}
	}

	pub fn target_triple(&self) -> Option<String> {
		let (host_arch, host_os, host_env) = get_host_triple();
		let arch = self.arch.as_ref().map(|s| &**s).unwrap_or(host_arch);
		let os = self.os.as_ref().map(|s| &**s).or(host_os);
		let env = self.env.as_ref().map(|s| &**s).or(host_env);

		os.map(|os| {
			if let Some(ref env) = env {
				format!("{}-{}-{}", arch, os, env)
			} else {
				format!("{}-{}", arch, os)
			}
		})
	}

	pub fn download_manifest<'a>(&self, cfg: DownloadCfg<'a>) -> Result<Manifest<'a>> {
		let url = self.manifest_url(cfg.dist_root);
		let package_dir = self.package_dir(cfg.dist_root);

		let manifest = try!(download_and_check(&url, None, "", cfg)).unwrap().0;

		Ok(Manifest(manifest, package_dir))
	}

	pub fn full_spec(&self) -> String {
		let triple = self.target_triple().unwrap_or_else(|| "<invalid target triple>".to_owned());
		if let Some(ref date) = self.date {
			format!("{}-{}-{}", triple, &self.channel, date)
		} else {
			format!("{}-{} (tracking)", triple, &self.channel)
		}
	}
}

pub struct Manifest<'a>(temp::File<'a>, String);

impl<'a> Manifest<'a> {
	pub fn package_url(&self, package: &str, target_triple: &str, ext: &str) -> Result<Option<String>> {
		let suffix = target_triple.to_owned() + ext;
		utils::match_file("manifest", &self.0, |line| {
			if line.starts_with(package) && line.ends_with(&suffix) {
				Some(format!("{}/{}", &self.1, line))
			} else {
				None
			}
		})
	}
}

impl fmt::Display for ToolchainDesc {
	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
		if let Some(ref arch) = self.arch {
			try!(write!(f, "{}-", arch));
		}
		if let Some(ref os) = self.os {
			try!(write!(f, "{}-", os));
		}
		if let Some(ref env) = self.env {
			try!(write!(f, "{}-", env));
		}

		try!(write!(f, "{}", &self.channel));

		if let Some(ref date) = self.date {
			try!(write!(f, "-{}", date));
		}

		Ok(())
	}
}
fn parse_url(url: &str) -> Result<hyper::Url> {
	hyper::Url::parse(url).map_err(|_| Error::InvalidUrl)
}

pub fn download_and_check<'a>(url: &str, update_hash: Option<&Path>, ext: &str, cfg: DownloadCfg<'a>) -> Result<Option<(temp::File<'a>, String)>> {
	let hash = try!(download_hash(url, cfg));
	let partial_hash: String = hash.chars().take(UPDATE_HASH_LEN).collect();

	if let Some(hash_file) = update_hash {
		if utils::is_file(hash_file) {
			if let Ok(contents) = utils::read_file("update hash", hash_file) {
				if contents == partial_hash {
					// Skip download, update hash matches
					cfg.notify_handler.call(Notification::UpdateHashMatches(&partial_hash));
					return Ok(None);
				}
			} else {
				cfg.notify_handler.call(Notification::CantReadUpdateHash(hash_file));
			}
		} else {
			cfg.notify_handler.call(Notification::NoUpdateHash(hash_file));
		}
	}

	let url = try!(parse_url(url));
	let file = try!(cfg.temp_cfg.new_file_with_ext(ext));
	try!(utils::download_file(url, &file, cfg.notify_handler));
	// TODO: Actually download and check the checksum and signature of the file
	Ok(Some((file, partial_hash)))
}

#[derive(Copy, Clone)]
pub struct DownloadCfg<'a> {
	pub dist_root: &'a str,
	pub temp_cfg: &'a temp::Cfg,
	pub notify_handler: &'a NotifyHandler,
}

pub fn download_dist<'a>(toolchain: &str, update_hash: Option<&Path>, cfg: DownloadCfg<'a>) -> Result<Option<(temp::File<'a>, String)>> {
	let desc = try!(ToolchainDesc::from_str(toolchain)
		.ok_or(Error::InvalidToolchainName));

	let target_triple = try!(desc.target_triple().ok_or_else(|| Error::UnsupportedHost(desc.full_spec())));
	let ext = get_installer_ext();

	let manifest = try!(desc.download_manifest(cfg));

	let maybe_url = try!(manifest.package_url("rust", &target_triple, ext));

	let url = try!(maybe_url.ok_or_else(|| Error::UnsupportedHost(desc.full_spec())));

	download_and_check(&url, update_hash, ext, cfg)
}

pub fn get_host_triple() -> (&'static str, Option<&'static str>, Option<&'static str>) {
	let arch = match env::consts::ARCH {
		"x86" => "i686", // Why, rust... WHY?
		other => other,
	};

	let os = match env::consts::OS {
		"windows" => Some("pc-windows"),
		"linux" => Some("unknown-linux"),
		"macos" => Some("apple-darwin"),
		_ => None,
	};

	let env = match () {
		() if cfg!(target_env = "gnu") => Some("gnu"),
		() if cfg!(target_env = "msvc") => Some("msvc"),
		_ => None,
	};

	(arch, os, env)
}

pub fn get_installer_ext() -> &'static str {
	if cfg!(windows) {
		return ".msi"
	}
	".tar.gz"
}

pub fn download_hash(url: &str, cfg: DownloadCfg) -> Result<String> {
	let hash_url = try!(parse_url(&(url.to_owned() + ".sha256")));
	let hash_file = try!(cfg.temp_cfg.new_file());

	try!(utils::download_file(hash_url, &hash_file, cfg.notify_handler));

	utils::read_file("hash", &hash_file)
}
<|endoftext|>