<commit_before>//! Support for ANSI terminal colors via the colored crate.
//!
//! To enable support for colors, add the `"colored"` feature in your
//! `Cargo.toml`:
//!
//! ```toml
//! [dependencies]
//! fern = { version = "0.5", features = ["colored"] }
//! ```
//!
//! ---
//!
//! Colors are mainly supported via coloring the log level itself, but it's
//! also possible to color each entire log line based off of the log level.
//!
//! First, here's an example which colors the log levels themselves ("INFO" /
//! "WARN" text will have color, but won't color the rest of the line).
//! [`ColoredLevelConfig`] lets us configure the colors per-level, but also has
//! sane defaults.
//!
//! ```
//! use fern::colors::{Color, ColoredLevelConfig};
//!
//! let mut colors = ColoredLevelConfig::new()
//!     // use builder methods
//!     .info(Color::Green);
//! // or access raw fields
//! colors.warn = Color::Magenta;
//! ```
//!
//! It can then be used within any regular fern formatting closure:
//!
//! ```
//! # let colors = fern::colors::ColoredLevelConfig::new();
//! fern::Dispatch::new()
//!     // ...
//!     .format(move |out, message, record| {
//!         out.finish(format_args!(
//!             "[{}] {}",
//!             // just use 'colors.color(..)' instead of the level
//!             // itself to insert ANSI colors.
//!             colors.color(record.level()),
//!             message,
//!         ))
//!     })
//!     # .into_log();
//! ```
//!
//! ---
//!
//! Coloring levels is nice, but the alternative is good to. For an example
//! application coloring each entire log line with the right color, see
//! [examples/pretty-colored.rs][ex].
//!
//! [`ColoredLevelConfig`]: struct.ColoredLevelConfig.html
//! [ex]: https://github.com/daboross/fern/blob/master/examples/pretty-colored.rs
use std::fmt;

pub use colored::Color;
use log::Level;

/// Extension crate allowing the use of `.colored` on Levels.
trait ColoredLogLevel {
    /// Colors this log level with the given color.
    fn colored(&self, color: Color) -> WithFgColor<Level>;
}

/// Opaque structure which represents some text data and a color to display it
/// with.
///
/// This implements [`fmt::Display`] to displaying the inner text (usually a
/// log level) with ANSI color markers before to set the color and after to
/// reset the color.
///
/// `WithFgColor` instances can be created and displayed without any allocation.
// this is necessary in order to avoid using colored::ColorString, which has a
// Display implementation involving many allocations, and would involve two
// more string allocations even to create it.
//
// [`fmt::Display`]: https://doc.rust-lang.org/std/fmt/trait.Display.html
pub struct WithFgColor<T>
where
    T: fmt::Display,
{
    text: T,
    color: Color,
}

impl<T> fmt::Display for WithFgColor<T>
where
    T: fmt::Display,
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "\x1B[{}m", self.color.to_fg_str())?;
        fmt::Display::fmt(&self.text, f)?;
        write!(f, "\x1B[0m")?;
        Ok(())
    }
}

/// Configuration specifying colors a log level can be colored as.
///
/// Example usage setting custom 'info' and 'debug' colors:
///
/// ```
/// use fern::colors::{Color, ColoredLevelConfig};
///
/// let colors = ColoredLevelConfig::new()
///     .info(Color::Green)
///     .debug(Color::Magenta);
///
/// fern::Dispatch::new()
///     .format(move |out, message, record| {
///         out.finish(format_args!(
///             "[{}] {}",
///             colors.color(record.level()),
///             message
///         ))
///     })
///     .chain(std::io::stdout())
/// # /*
///     .apply()?;
/// # */
/// #   .into_log();
/// ```
#[derive(Copy, Clone)]
#[must_use = "builder methods take config by value and thus must be reassigned to variable"]
pub struct ColoredLevelConfig {
    /// The color to color logs with the [`Error`] level.
    ///
    /// [`Error`]: https://docs.rs/log/0.4/log/enum.Level.html#variant.Error
    pub error: Color,
    /// The color to color logs with the [`Warn`] level.
    ///
    /// [`Warn`]: https://docs.rs/log/0.4/log/enum.Level.html#variant.Warn
    pub warn: Color,
    /// The color to color logs with the [`Info`] level.
    ///
    /// [`Info`]: https://docs.rs/log/0.4/log/enum.Level.html#variant.Info
    pub info: Color,
    /// The color to color logs with the [`Debug`] level.
    ///
    /// [`Debug`]: https://docs.rs/log/0.4/log/enum.Level.html#variant.Debug
    pub debug: Color,
    /// The color to color logs with the [`Trace`] level.
    ///
    /// [`Trace`]: https://docs.rs/log/0.4/log/enum.Level.html#variant.Trace
    pub trace: Color,
}

impl ColoredLevelConfig {
    /// Creates a new ColoredLevelConfig with the default colors.
    ///
    /// This matches the behavior of [`ColoredLevelConfig::default`].
    ///
    /// [`ColoredLevelConfig::default`]: #method.default
    #[inline]
    pub fn new() -> Self {
        #[cfg(windows)]
        {
            let _ = colored::control::set_virtual_terminal(true);
        }
        Self::default()
    }

    /// Overrides the [`Error`] level color with the given color.
    ///
    /// The default color is [`Color::Red`].
    ///
    /// [`Error`]: https://docs.rs/log/0.4/log/enum.Level.html#variant.Error
    /// [`Color::Red`]: https://docs.rs/colored/1/colored/enum.Color.html#variant.Red
    pub fn error(mut self, error: Color) -> Self {
        self.error = error;
        self
    }

    /// Overrides the [`Warn`] level color with the given color.
    ///
    /// The default color is [`Color::Yellow`].
    ///
    /// [`Warn`]: https://docs.rs/log/0.4/log/enum.Level.html#variant.Warn
    /// [`Color::Yellow`]: https://docs.rs/colored/1/colored/enum.Color.html#variant.Yellow
    pub fn warn(mut self, warn: Color) -> Self {
        self.warn = warn;
        self
    }

    /// Overrides the [`Info`] level color with the given color.
    ///
    /// The default color is [`Color::White`].
    ///
    /// [`Info`]: https://docs.rs/log/0.4/log/enum.Level.html#variant.Info
    /// [`Color::White`]: https://docs.rs/colored/1/colored/enum.Color.html#variant.White
    pub fn info(mut self, info: Color) -> Self {
        self.info = info;
        self
    }

    /// Overrides the [`Debug`] level color with the given color.
    ///
    /// The default color is [`Color::White`].
    ///
    /// [`Debug`]: https://docs.rs/log/0.4/log/enum.Level.html#variant.Debug
    /// [`Color::White`]: https://docs.rs/colored/1/colored/enum.Color.html#variant.White
    pub fn debug(mut self, debug: Color) -> Self {
        self.debug = debug;
        self
    }

    /// Overrides the [`Trace`] level color with the given color.
    ///
    /// The default color is [`Color::White`].
    ///
    /// [`Trace`]: https://docs.rs/log/0.4/log/enum.Level.html#variant.Trace
    /// [`Color::White`]: https://docs.rs/colored/1/colored/enum.Color.html#variant.White
    pub fn trace(mut self, trace: Color) -> Self {
        self.trace = trace;
        self
    }

    /// Colors the given log level with the color in this configuration
    /// corresponding to it's level.
    ///
    /// The structure returned is opaque, but will print the Level surrounded
    /// by ANSI color codes when displayed. This will work correctly for
    /// UNIX terminals, but due to a lack of support from the [`colored`]
    /// crate, this will not function in Windows.
    ///
    /// [`colored`]: https://github.com/mackwic/colored
    pub fn color(&self, level: Level) -> WithFgColor<Level> {
        level.colored(self.get_color(&level))
    }

    /// Retrieves the color that a log level should be colored as.
    pub fn get_color(&self, level: &Level) -> Color {
        match *level {
            Level::Error => self.error,
            Level::Warn => self.warn,
            Level::Info => self.info,
            Level::Debug => self.debug,
            Level::Trace => self.trace,
        }
    }
}

impl Default for ColoredLevelConfig {
    /// Retrieves the default configuration. This has:
    ///
    /// - [`Error`] as [`Color::Red`]
    /// - [`Warn`] as [`Color::Yellow`]
    /// - [`Info`] as [`Color::White`]
    /// - [`Debug`] as [`Color::White`]
    /// - [`Trace`] as [`Color::White`]
    ///
    /// [`Error`]: https://docs.rs/log/0.4/log/enum.Level.html#variant.Error
    /// [`Warn`]: https://docs.rs/log/0.4/log/enum.Level.html#variant.Warn
    /// [`Info`]: https://docs.rs/log/0.4/log/enum.Level.html#variant.Info
    /// [`Debug`]: https://docs.rs/log/0.4/log/enum.Level.html#variant.Debug
    /// [`Trace`]: https://docs.rs/log/0.4/log/enum.Level.html#variant.Trace
    /// [`Color::White`]: https://docs.rs/colored/1/colored/enum.Color.html#variant.White
    /// [`Color::Yellow`]: https://docs.rs/colored/1/colored/enum.Color.html#variant.Yellow
    /// [`Color::Red`]: https://docs.rs/colored/1/colored/enum.Color.html#variant.Red
    fn default() -> Self {
        ColoredLevelConfig {
            error: Color::Red,
            warn: Color::Yellow,
            debug: Color::White,
            info: Color::White,
            trace: Color::White,
        }
    }
}

impl ColoredLogLevel for Level {
    fn colored(&self, color: Color) -> WithFgColor<Level> {
        WithFgColor {
            text: *self,
            color: color,
        }
    }
}

#[cfg(test)]
mod test {
    use colored::{Color::*, Colorize};

    use super::WithFgColor;

    #[test]
    fn fg_color_matches_colored_behavior() {
        #[cfg(windows)]
        {
            let _ = colored::control::set_virtual_terminal(true);
        }
        for &color in &[
            Black,
            Red,
            Green,
            Yellow,
            Blue,
            Magenta,
            Cyan,
            White,
            BrightBlack,
            BrightRed,
            BrightGreen,
            BrightYellow,
            BrightBlue,
            BrightMagenta,
            BrightCyan,
            BrightWhite,
        ] {
            assert_eq!(
                format!("{}", "test".color(color)),
                format!(
                    "{}",
                    WithFgColor {
                        text: "test",
                        color: color,
                    }
                )
            );
        }
    }

    #[test]
    fn fg_color_respects_formatting_flags() {
        #[cfg(windows)]
        {
            let _ = colored::control::set_virtual_terminal(true);
        }
        let s = format!(
            "{:^8}",
            WithFgColor {
                text: "test",
                color: Yellow,
            }
        );
        assert!(s.contains("  test  "));
        assert!(!s.contains("   test  "));
        assert!(!s.contains("  test   "));
    }
}
<commit_msg>Disable certain color tests on Windows.<commit_after>//! Support for ANSI terminal colors via the colored crate.
//!
//! To enable support for colors, add the `"colored"` feature in your
//! `Cargo.toml`:
//!
//! ```toml
//! [dependencies]
//! fern = { version = "0.5", features = ["colored"] }
//! ```
//!
//! ---
//!
//! Colors are mainly supported via coloring the log level itself, but it's
//! also possible to color each entire log line based off of the log level.
//!
//! First, here's an example which colors the log levels themselves ("INFO" /
//! "WARN" text will have color, but won't color the rest of the line).
//! [`ColoredLevelConfig`] lets us configure the colors per-level, but also has
//! sane defaults.
//!
//! ```
//! use fern::colors::{Color, ColoredLevelConfig};
//!
//! let mut colors = ColoredLevelConfig::new()
//!     // use builder methods
//!     .info(Color::Green);
//! // or access raw fields
//! colors.warn = Color::Magenta;
//! ```
//!
//! It can then be used within any regular fern formatting closure:
//!
//! ```
//! # let colors = fern::colors::ColoredLevelConfig::new();
//! fern::Dispatch::new()
//!     // ...
//!     .format(move |out, message, record| {
//!         out.finish(format_args!(
//!             "[{}] {}",
//!             // just use 'colors.color(..)' instead of the level
//!             // itself to insert ANSI colors.
//!             colors.color(record.level()),
//!             message,
//!         ))
//!     })
//!     # .into_log();
//! ```
//!
//! ---
//!
//! Coloring levels is nice, but the alternative is good to. For an example
//! application coloring each entire log line with the right color, see
//! [examples/pretty-colored.rs][ex].
//!
//! [`ColoredLevelConfig`]: struct.ColoredLevelConfig.html
//! [ex]: https://github.com/daboross/fern/blob/master/examples/pretty-colored.rs
use std::fmt;

pub use colored::Color;
use log::Level;

/// Extension crate allowing the use of `.colored` on Levels.
trait ColoredLogLevel {
    /// Colors this log level with the given color.
    fn colored(&self, color: Color) -> WithFgColor<Level>;
}

/// Opaque structure which represents some text data and a color to display it
/// with.
///
/// This implements [`fmt::Display`] to displaying the inner text (usually a
/// log level) with ANSI color markers before to set the color and after to
/// reset the color.
///
/// `WithFgColor` instances can be created and displayed without any allocation.
// this is necessary in order to avoid using colored::ColorString, which has a
// Display implementation involving many allocations, and would involve two
// more string allocations even to create it.
//
// [`fmt::Display`]: https://doc.rust-lang.org/std/fmt/trait.Display.html
pub struct WithFgColor<T>
where
    T: fmt::Display,
{
    text: T,
    color: Color,
}

impl<T> fmt::Display for WithFgColor<T>
where
    T: fmt::Display,
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "\x1B[{}m", self.color.to_fg_str())?;
        fmt::Display::fmt(&self.text, f)?;
        write!(f, "\x1B[0m")?;
        Ok(())
    }
}

/// Configuration specifying colors a log level can be colored as.
///
/// Example usage setting custom 'info' and 'debug' colors:
///
/// ```
/// use fern::colors::{Color, ColoredLevelConfig};
///
/// let colors = ColoredLevelConfig::new()
///     .info(Color::Green)
///     .debug(Color::Magenta);
///
/// fern::Dispatch::new()
///     .format(move |out, message, record| {
///         out.finish(format_args!(
///             "[{}] {}",
///             colors.color(record.level()),
///             message
///         ))
///     })
///     .chain(std::io::stdout())
/// # /*
///     .apply()?;
/// # */
/// #   .into_log();
/// ```
#[derive(Copy, Clone)]
#[must_use = "builder methods take config by value and thus must be reassigned to variable"]
pub struct ColoredLevelConfig {
    /// The color to color logs with the [`Error`] level.
    ///
    /// [`Error`]: https://docs.rs/log/0.4/log/enum.Level.html#variant.Error
    pub error: Color,
    /// The color to color logs with the [`Warn`] level.
    ///
    /// [`Warn`]: https://docs.rs/log/0.4/log/enum.Level.html#variant.Warn
    pub warn: Color,
    /// The color to color logs with the [`Info`] level.
    ///
    /// [`Info`]: https://docs.rs/log/0.4/log/enum.Level.html#variant.Info
    pub info: Color,
    /// The color to color logs with the [`Debug`] level.
    ///
    /// [`Debug`]: https://docs.rs/log/0.4/log/enum.Level.html#variant.Debug
    pub debug: Color,
    /// The color to color logs with the [`Trace`] level.
    ///
    /// [`Trace`]: https://docs.rs/log/0.4/log/enum.Level.html#variant.Trace
    pub trace: Color,
}

impl ColoredLevelConfig {
    /// Creates a new ColoredLevelConfig with the default colors.
    ///
    /// This matches the behavior of [`ColoredLevelConfig::default`].
    ///
    /// [`ColoredLevelConfig::default`]: #method.default
    #[inline]
    pub fn new() -> Self {
        #[cfg(windows)]
        {
            let _ = colored::control::set_virtual_terminal(true);
        }
        Self::default()
    }

    /// Overrides the [`Error`] level color with the given color.
    ///
    /// The default color is [`Color::Red`].
    ///
    /// [`Error`]: https://docs.rs/log/0.4/log/enum.Level.html#variant.Error
    /// [`Color::Red`]: https://docs.rs/colored/1/colored/enum.Color.html#variant.Red
    pub fn error(mut self, error: Color) -> Self {
        self.error = error;
        self
    }

    /// Overrides the [`Warn`] level color with the given color.
    ///
    /// The default color is [`Color::Yellow`].
    ///
    /// [`Warn`]: https://docs.rs/log/0.4/log/enum.Level.html#variant.Warn
    /// [`Color::Yellow`]: https://docs.rs/colored/1/colored/enum.Color.html#variant.Yellow
    pub fn warn(mut self, warn: Color) -> Self {
        self.warn = warn;
        self
    }

    /// Overrides the [`Info`] level color with the given color.
    ///
    /// The default color is [`Color::White`].
    ///
    /// [`Info`]: https://docs.rs/log/0.4/log/enum.Level.html#variant.Info
    /// [`Color::White`]: https://docs.rs/colored/1/colored/enum.Color.html#variant.White
    pub fn info(mut self, info: Color) -> Self {
        self.info = info;
        self
    }

    /// Overrides the [`Debug`] level color with the given color.
    ///
    /// The default color is [`Color::White`].
    ///
    /// [`Debug`]: https://docs.rs/log/0.4/log/enum.Level.html#variant.Debug
    /// [`Color::White`]: https://docs.rs/colored/1/colored/enum.Color.html#variant.White
    pub fn debug(mut self, debug: Color) -> Self {
        self.debug = debug;
        self
    }

    /// Overrides the [`Trace`] level color with the given color.
    ///
    /// The default color is [`Color::White`].
    ///
    /// [`Trace`]: https://docs.rs/log/0.4/log/enum.Level.html#variant.Trace
    /// [`Color::White`]: https://docs.rs/colored/1/colored/enum.Color.html#variant.White
    pub fn trace(mut self, trace: Color) -> Self {
        self.trace = trace;
        self
    }

    /// Colors the given log level with the color in this configuration
    /// corresponding to it's level.
    ///
    /// The structure returned is opaque, but will print the Level surrounded
    /// by ANSI color codes when displayed. This will work correctly for
    /// UNIX terminals, but due to a lack of support from the [`colored`]
    /// crate, this will not function in Windows.
    ///
    /// [`colored`]: https://github.com/mackwic/colored
    pub fn color(&self, level: Level) -> WithFgColor<Level> {
        level.colored(self.get_color(&level))
    }

    /// Retrieves the color that a log level should be colored as.
    pub fn get_color(&self, level: &Level) -> Color {
        match *level {
            Level::Error => self.error,
            Level::Warn => self.warn,
            Level::Info => self.info,
            Level::Debug => self.debug,
            Level::Trace => self.trace,
        }
    }
}

impl Default for ColoredLevelConfig {
    /// Retrieves the default configuration. This has:
    ///
    /// - [`Error`] as [`Color::Red`]
    /// - [`Warn`] as [`Color::Yellow`]
    /// - [`Info`] as [`Color::White`]
    /// - [`Debug`] as [`Color::White`]
    /// - [`Trace`] as [`Color::White`]
    ///
    /// [`Error`]: https://docs.rs/log/0.4/log/enum.Level.html#variant.Error
    /// [`Warn`]: https://docs.rs/log/0.4/log/enum.Level.html#variant.Warn
    /// [`Info`]: https://docs.rs/log/0.4/log/enum.Level.html#variant.Info
    /// [`Debug`]: https://docs.rs/log/0.4/log/enum.Level.html#variant.Debug
    /// [`Trace`]: https://docs.rs/log/0.4/log/enum.Level.html#variant.Trace
    /// [`Color::White`]: https://docs.rs/colored/1/colored/enum.Color.html#variant.White
    /// [`Color::Yellow`]: https://docs.rs/colored/1/colored/enum.Color.html#variant.Yellow
    /// [`Color::Red`]: https://docs.rs/colored/1/colored/enum.Color.html#variant.Red
    fn default() -> Self {
        ColoredLevelConfig {
            error: Color::Red,
            warn: Color::Yellow,
            debug: Color::White,
            info: Color::White,
            trace: Color::White,
        }
    }
}

impl ColoredLogLevel for Level {
    fn colored(&self, color: Color) -> WithFgColor<Level> {
        WithFgColor {
            text: *self,
            color: color,
        }
    }
}

#[cfg(test)]
mod test {
    use colored::{Color::*, Colorize};

    use super::WithFgColor;

    #[test]
    #[cfg(not(windows))]
    fn fg_color_matches_colored_behavior() {
        for &color in &[
            Black,
            Red,
            Green,
            Yellow,
            Blue,
            Magenta,
            Cyan,
            White,
            BrightBlack,
            BrightRed,
            BrightGreen,
            BrightYellow,
            BrightBlue,
            BrightMagenta,
            BrightCyan,
            BrightWhite,
        ] {
            assert_eq!(
                format!("{}", "test".color(color)),
                format!(
                    "{}",
                    WithFgColor {
                        text: "test",
                        color: color,
                    }
                )
            );
        }
    }

    #[test]
    #[cfg(not(windows))]
    fn fg_color_respects_formatting_flags() {
        let s = format!(
            "{:^8}",
            WithFgColor {
                text: "test",
                color: Yellow,
            }
        );
        assert!(s.contains("  test  "));
        assert!(!s.contains("   test  "));
        assert!(!s.contains("  test   "));
    }
}
<|endoftext|>


<commit_before>use std::io::{Result, Write};
use std::path::Path;

use env::Env;
use file_saver::save_to_file;
use regex::Regex;

pub fn generate(env: &Env) {
    println!(
        "generating sys build script for {}",
        env.config.library_name
    );

    let path = env.config.target_path.join("build.rs");

    println!("Generating file {:?}", path);
    save_to_file(
        &path,
        env.config.make_backup,
        |w| generate_build_script(w, env),
    );
}

fn generate_build_script(w: &mut Write, env: &Env) -> Result<()> {
    try!(write!(
        w,
        "{}",
        r##"extern crate pkg_config;

use pkg_config::{Config, Error};
use std::env;
use std::io::prelude::*;
use std::io;
use std::process;

fn main() {
    if let Err(s) = find() {
        let _ = writeln!(io::stderr(), "{}", s);
        process::exit(1);
    }
}

fn find() -> Result<(), Error> {
"##
    ));

    let ns = env.namespaces.main();
    let regex = Regex::new(r"^lib(.+)\.(so.*|dylib)$").expect("Regex failed");
    let shared_libs: Vec<_> = ns.shared_libs
        .iter()
        .map(|s| {
            let lib_path = Path::new(s);
            let lib_file_name = lib_path
                .file_name()
                .expect("A 'shared-library' in the GIR file has an invalid form")
                .to_str()
                .expect("Failed to convert OsStr to str");
            regex.replace(lib_file_name, "\"$1\"")
        })
        .collect();

    try!(writeln!(
        w,
        "\tlet package_name = \"{}\";",
        ns.package_name
            .as_ref()
            .expect("Package name doesn't exist")
    ));
    try!(writeln!(
        w,
        "\tlet shared_libs = [{}];",
        shared_libs.join(", ")
    ));
    try!(write!(w, "\tlet version = "));
    let versions = ns.versions
        .iter()
        .filter(|v| **v >= env.config.min_cfg_version)
        .skip(1)
        .collect::<Vec<_>>();
    for v in versions.iter().rev() {
        try!(write!(
            w,
            "if cfg!({}) {{\n\t\t\"{}\"\n\t}} else ",
            v.to_cfg(),
            v
        ));
    }
    try!(writeln!(
        w,
        "{{\n\t\t\"{}\"\n\t}};",
        env.config.min_cfg_version
    ));

    writeln!(
        w,
        "{}",
        r##"
    if let Ok(lib_dir) = env::var("GTK_LIB_DIR") {
        for lib_ in shared_libs.iter() {
            println!("cargo:rustc-link-lib=dylib={}", lib_);
        }
        println!("cargo:rustc-link-search=native={}", lib_dir);
        return Ok(())
    }

    let target = env::var("TARGET").expect("TARGET environment variable doesn't exist");
    let hardcode_shared_libs = target.contains("windows");

    let mut config = Config::new();
    config.atleast_version(version);
    if hardcode_shared_libs {
        config.cargo_metadata(false);
    }
    match config.probe(package_name) {
        Ok(library) => {
            if hardcode_shared_libs {
                for lib_ in shared_libs.iter() {
                    println!("cargo:rustc-link-lib=dylib={}", lib_);
                }
                for path in library.link_paths.iter() {
                    println!("cargo:rustc-link-search=native={}",
                             path.to_str().expect("library path doesn't exist"));
                }
            }
            Ok(())
        }
        Err(Error::EnvNoPkgConfig(_)) | Err(Error::Command { .. }) => {
            for lib_ in shared_libs.iter() {
                println!("cargo:rustc-link-lib=dylib={}", lib_);
            }
            Ok(())
        }
        Err(err) => Err(err),
    }
}
"##
    )
}
<commit_msg>codegen/build.rs: disable print_system_libs in calls to pkg-config<commit_after>use std::io::{Result, Write};
use std::path::Path;

use env::Env;
use file_saver::save_to_file;
use regex::Regex;

pub fn generate(env: &Env) {
    println!(
        "generating sys build script for {}",
        env.config.library_name
    );

    let path = env.config.target_path.join("build.rs");

    println!("Generating file {:?}", path);
    save_to_file(
        &path,
        env.config.make_backup,
        |w| generate_build_script(w, env),
    );
}

fn generate_build_script(w: &mut Write, env: &Env) -> Result<()> {
    try!(write!(
        w,
        "{}",
        r##"extern crate pkg_config;

use pkg_config::{Config, Error};
use std::env;
use std::io::prelude::*;
use std::io;
use std::process;

fn main() {
    if let Err(s) = find() {
        let _ = writeln!(io::stderr(), "{}", s);
        process::exit(1);
    }
}

fn find() -> Result<(), Error> {
"##
    ));

    let ns = env.namespaces.main();
    let regex = Regex::new(r"^lib(.+)\.(so.*|dylib)$").expect("Regex failed");
    let shared_libs: Vec<_> = ns.shared_libs
        .iter()
        .map(|s| {
            let lib_path = Path::new(s);
            let lib_file_name = lib_path
                .file_name()
                .expect("A 'shared-library' in the GIR file has an invalid form")
                .to_str()
                .expect("Failed to convert OsStr to str");
            regex.replace(lib_file_name, "\"$1\"")
        })
        .collect();

    try!(writeln!(
        w,
        "\tlet package_name = \"{}\";",
        ns.package_name
            .as_ref()
            .expect("Package name doesn't exist")
    ));
    try!(writeln!(
        w,
        "\tlet shared_libs = [{}];",
        shared_libs.join(", ")
    ));
    try!(write!(w, "\tlet version = "));
    let versions = ns.versions
        .iter()
        .filter(|v| **v >= env.config.min_cfg_version)
        .skip(1)
        .collect::<Vec<_>>();
    for v in versions.iter().rev() {
        try!(write!(
            w,
            "if cfg!({}) {{\n\t\t\"{}\"\n\t}} else ",
            v.to_cfg(),
            v
        ));
    }
    try!(writeln!(
        w,
        "{{\n\t\t\"{}\"\n\t}};",
        env.config.min_cfg_version
    ));

    writeln!(
        w,
        "{}",
        r##"
    if let Ok(lib_dir) = env::var("GTK_LIB_DIR") {
        for lib_ in shared_libs.iter() {
            println!("cargo:rustc-link-lib=dylib={}", lib_);
        }
        println!("cargo:rustc-link-search=native={}", lib_dir);
        return Ok(())
    }

    let target = env::var("TARGET").expect("TARGET environment variable doesn't exist");
    let hardcode_shared_libs = target.contains("windows");

    let mut config = Config::new();
    config.atleast_version(version);
    config.print_system_libs(false);
    if hardcode_shared_libs {
        config.cargo_metadata(false);
    }
    match config.probe(package_name) {
        Ok(library) => {
            if hardcode_shared_libs {
                for lib_ in shared_libs.iter() {
                    println!("cargo:rustc-link-lib=dylib={}", lib_);
                }
                for path in library.link_paths.iter() {
                    println!("cargo:rustc-link-search=native={}",
                             path.to_str().expect("library path doesn't exist"));
                }
            }
            Ok(())
        }
        Err(Error::EnvNoPkgConfig(_)) | Err(Error::Command { .. }) => {
            for lib_ in shared_libs.iter() {
                println!("cargo:rustc-link-lib=dylib={}", lib_);
            }
            Ok(())
        }
        Err(err) => Err(err),
    }
}
"##
    )
}
<|endoftext|>


<commit_before>use std::io::prelude::*;
use std::io;
use std::path::Path;

use analysis::types::IsIncomplete;
use env::Env;
use file_saver::save_to_file;
use library::{Type, MAIN_NAMESPACE};
use nameutil::crate_name;
use codegen::general;

pub fn generate(env :&Env) {
    let tests = env.config.target_path.join("tests");
    let abi_c = tests.join("abi.c");
    let abi_rs = tests.join("abi.rs");

    let ns = env.library.namespace(MAIN_NAMESPACE);
    let ctypes = ns.types
        .iter()
        .filter_map(|t| t.as_ref())
        .filter(|t| !t.is_incomplete(&env.library))
        .filter_map(|t| match *t {
            Type::Alias(_) |
            Type::Class(_) |
            Type::Record(_) |
            Type::Union(_) |
            Type::Enumeration(_) |
            Type::Bitfield(_) |
            Type::Interface(_)
            => {
                let full_name = format!("{}.{}", &ns.name, t.get_name());
                if !env.type_status_sys(&full_name).ignored() {
                    t.get_glib_name()
                } else {
                    None
                }
            },
            _ => None,
        })
        .filter(|s| !is_name_made_up(s))
        .collect::<Vec<_>>();

    if ctypes.is_empty() {
        return;
    }

    save_to_file(&abi_c, env.config.make_backup, |w| {
        generate_abi_c(env, &abi_c, w)
    });
    save_to_file(&abi_rs, env.config.make_backup, |w| {
        generate_abi_rs(env, &abi_rs, w, &ctypes)
    });
}

/// Checks if type name is unlikely to correspond to a real C type name.
fn is_name_made_up(name: &str) -> bool {
    // Unnamed types are assigned name during parsing, those names contain an underscore.
    name.contains('_')
}

fn generate_abi_c(env: &Env, path: &Path, w: &mut Write) -> io::Result<()> {
    info!("Generating file {:?}", path);
    general::start_comments(w, &env.config)?;
    writeln!(w, "")?;
    writeln!(w, "/* For %z support in printf when using MinGW. */")?;
    writeln!(w, "#define _POSIX_C_SOURCE 200809L")?;
    writeln!(w, "#include <stdalign.h>")?;
    writeln!(w, "#include <stdio.h>")?;

    let ns = env.library.namespace(MAIN_NAMESPACE);
    for include in ns.c_includes.iter() {
        writeln!(w, "#include <{}>", include)?;
    }

    writeln!(w, "{}", r##"
int main() {
  printf("%zu\n%zu\n", sizeof(ABI_TEST_TYPE), alignof(ABI_TEST_TYPE));
}"##)

}

fn generate_abi_rs(env: &Env, path: &Path, w: &mut Write, ctypes: &[&str]) -> io::Result<()> {
    info!("Generating file {:?}", path);
    general::start_comments(w, &env.config)?;
    writeln!(w, "")?;
    let name = format!("{}_sys", crate_name(&env.config.library_name));
    writeln!(w, "extern crate {};", &name)?;
    writeln!(w, "extern crate shell_words;")?;
    writeln!(w, "{}", r##"
use std::collections::BTreeMap;
use std::env;
use std::error::Error;
use std::mem::{align_of, size_of};
use std::process::Command;
use std::str;"##)?;
    writeln!(w, "use {}::*;", &name)?;
    writeln!(w, "{}", r##"

#[derive(Clone, Debug)]
struct Compiler {
    pub args: Vec<String>,
}

impl Compiler {
    pub fn new() -> Result<Compiler, Box<Error>> {
        let mut args = get_var("CC", "cc")?;
        args.extend(get_var("CFLAGS", "")?);
        args.extend(get_var("CPPFLAGS", "")?);
        Ok(Compiler { args })
    }

    pub fn define<'a, V: Into<Option<&'a str>>>(&mut self, var: &str, val: V) {
        let arg = match val.into() {
            None => format!("-D{}", var),
            Some(val) => format!("-D{}={}", var, val),
        };
        self.args.push(arg);
    }

    pub fn to_command(&self) -> Command {
        let mut cmd = Command::new(&self.args[0]);
        cmd.args(&self.args[1..]);
        cmd
    }
}

fn get_var(name: &str, default: &str) -> Result<Vec<String>, Box<Error>> {
    match env::var(name) {
        Ok(value) => Ok(shell_words::split(&value)?),
        Err(env::VarError::NotPresent) => Ok(shell_words::split(default)?),
        Err(err) => Err(format!("{} {}", name, err).into()),
    }
}

fn pkg_config_cflags(package: &str) -> Result<Vec<String>, Box<Error>> {
    let mut cmd = Command::new("pkg-config");
    cmd.arg("--cflags");
    cmd.arg(package);
    let out = cmd.output()?;
    if !out.status.success() {
        return Err(format!("command {:?} returned {}",
                           &cmd, out.status).into());
    }
    let stdout = str::from_utf8(&out.stdout)?;
    Ok(shell_words::split(stdout)?)
}

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
struct ABI {
    size: usize,
    alignment: usize,
}

impl ABI {
    pub fn from_type<T: Sized>() -> ABI {
        ABI {
            size: size_of::<T>(),
            alignment: align_of::<T>(),
        }
    }
}

#[test]
fn test_cross_validate_abi_with_c() {
    // Configure compiler instance."##)?;

    let ns = env.library.namespace(MAIN_NAMESPACE);
    let package_name = ns.package_name.as_ref()
        .expect("Missing package name");

    writeln!(w, "\tlet package_name = \"{}\";", package_name)?;
    writeln!(w, "{}", r##"    let mut cc = Compiler::new()
        .expect("compiler from environment");
    let cflags = pkg_config_cflags(package_name)
        .expect("cflags from pkg-config");
    cc.args.extend(cflags);
    cc.args.push("-Wno-deprecated-declarations".to_owned());

    // Sanity check that compilation works.
    assert_eq!(ABI {size: 1, alignment: 1},
               get_c_abi(&cc, "char").expect("C ABI for char"));

    let rust = get_rust_abi();
    let mut failed = false;
    for (name, rust_abi) in &rust {
        match get_c_abi(&cc, name) {
            Err(e) => {
                failed = true;
                eprintln!("{}", e);
                continue;
            },
            Ok(ref c_abi) => {
                if rust_abi != c_abi {
                    failed = true;
                    eprintln!("ABI mismatch for {}\nRust: {:?}\nC:    {:?}",
                              name, rust_abi, c_abi);
                }
            }
        };
    }
    assert!(!failed);
}

fn get_c_abi(cc: &Compiler, name: &str) -> Result<ABI, Box<Error>> {
    let mut cc = cc.clone();
    cc.define("ABI_TEST_TYPE", name);
    cc.args.push("tests/abi.c".to_owned());
    cc.args.push("-oabi".to_owned());

    let mut cc_cmd = cc.to_command();
    let status = cc_cmd.spawn()?.wait()?;
    if !status.success() {
        return Err(format!("compilation command {:?} failed, {}",
                           &cc_cmd, status).into());
    }

    let mut abi_cmd = Command::new("./abi");
    let output = abi_cmd.output()?;
    if !output.status.success() {
        return Err(format!("command {:?} failed, {}",
                           &abi_cmd, output.status).into());
    }

    let stdout = str::from_utf8(&output.stdout)?;
    let mut words = stdout.split_whitespace();
    let size = words.next().unwrap().parse().unwrap();
    let alignment = words.next().unwrap().parse().unwrap();
    Ok(ABI {size, alignment})
}

fn get_rust_abi() -> BTreeMap<String, ABI> {
    let mut abi = BTreeMap::new();"##)?;

    for ctype in ctypes {
        writeln!(w, r##"    abi.insert("{ctype}".to_owned(), ABI::from_type::<{ctype}>());"##,
                 ctype=ctype)?;
    }

    writeln!(w, "{}", r##"    abi
}

"##)

}

<commit_msg>Use cfg attributes when generating ABI tests.<commit_after>use std::io::prelude::*;
use std::io;
use std::path::Path;

use analysis::types::IsIncomplete;
use env::Env;
use file_saver::save_to_file;
use library::{Type, MAIN_NAMESPACE};
use nameutil::crate_name;
use codegen::general;

struct CType {
    /// Name of type, as used in C.
    name: String,
    /// Expression describing when type is available (when defined only conditionally).
    cfg_condition: Option<String>,
}

pub fn generate(env :&Env) {
    let tests = env.config.target_path.join("tests");
    let abi_c = tests.join("abi.c");
    let abi_rs = tests.join("abi.rs");

    let ns = env.library.namespace(MAIN_NAMESPACE);
    let ctypes = ns.types
        .iter()
        .filter_map(|t| t.as_ref())
        .filter(|t| !t.is_incomplete(&env.library))
        .filter_map(|t| match *t {
            Type::Alias(_) |
            Type::Class(_) |
            Type::Record(_) |
            Type::Union(_) |
            Type::Enumeration(_) |
            Type::Bitfield(_) |
            Type::Interface(_)
            => {
                let full_name = format!("{}.{}", &ns.name, t.get_name());
                if env.type_status_sys(&full_name).ignored() {
                    return None;
                }
                let name = match t.get_glib_name() {
                    None => return None,
                    Some(name) => name,
                };
                if is_name_made_up(name) {
                    return None;
                }
                let cfg_condition = env.config.objects.get(&full_name).and_then(|obj| {
                    obj.cfg_condition.clone()
                });
                Some(CType {
                    name: name.to_owned(),
                    cfg_condition,
                })
            },
            _ => None,
        })
        .collect::<Vec<_>>();

    if ctypes.is_empty() {
        return;
    }

    save_to_file(&abi_c, env.config.make_backup, |w| {
        generate_abi_c(env, &abi_c, w)
    });
    save_to_file(&abi_rs, env.config.make_backup, |w| {
        generate_abi_rs(env, &abi_rs, w, &ctypes)
    });
}

/// Checks if type name is unlikely to correspond to a real C type name.
fn is_name_made_up(name: &str) -> bool {
    // Unnamed types are assigned name during parsing, those names contain an underscore.
    name.contains('_')
}

fn generate_abi_c(env: &Env, path: &Path, w: &mut Write) -> io::Result<()> {
    info!("Generating file {:?}", path);
    general::start_comments(w, &env.config)?;
    writeln!(w, "")?;
    writeln!(w, "/* For %z support in printf when using MinGW. */")?;
    writeln!(w, "#define _POSIX_C_SOURCE 200809L")?;
    writeln!(w, "#include <stdalign.h>")?;
    writeln!(w, "#include <stdio.h>")?;

    let ns = env.library.namespace(MAIN_NAMESPACE);
    for include in ns.c_includes.iter() {
        writeln!(w, "#include <{}>", include)?;
    }

    writeln!(w, "{}", r##"
int main() {
  printf("%zu\n%zu\n", sizeof(ABI_TEST_TYPE), alignof(ABI_TEST_TYPE));
}"##)

}

fn generate_abi_rs(env: &Env, path: &Path, w: &mut Write, ctypes: &[CType]) -> io::Result<()> {
    info!("Generating file {:?}", path);
    general::start_comments(w, &env.config)?;
    writeln!(w, "")?;
    let name = format!("{}_sys", crate_name(&env.config.library_name));
    writeln!(w, "extern crate {};", &name)?;
    writeln!(w, "extern crate shell_words;")?;
    writeln!(w, "{}", r##"
use std::collections::BTreeMap;
use std::env;
use std::error::Error;
use std::mem::{align_of, size_of};
use std::process::Command;
use std::str;"##)?;
    writeln!(w, "use {}::*;", &name)?;
    writeln!(w, "{}", r##"

#[derive(Clone, Debug)]
struct Compiler {
    pub args: Vec<String>,
}

impl Compiler {
    pub fn new() -> Result<Compiler, Box<Error>> {
        let mut args = get_var("CC", "cc")?;
        args.extend(get_var("CFLAGS", "")?);
        args.extend(get_var("CPPFLAGS", "")?);
        Ok(Compiler { args })
    }

    pub fn define<'a, V: Into<Option<&'a str>>>(&mut self, var: &str, val: V) {
        let arg = match val.into() {
            None => format!("-D{}", var),
            Some(val) => format!("-D{}={}", var, val),
        };
        self.args.push(arg);
    }

    pub fn to_command(&self) -> Command {
        let mut cmd = Command::new(&self.args[0]);
        cmd.args(&self.args[1..]);
        cmd
    }
}

fn get_var(name: &str, default: &str) -> Result<Vec<String>, Box<Error>> {
    match env::var(name) {
        Ok(value) => Ok(shell_words::split(&value)?),
        Err(env::VarError::NotPresent) => Ok(shell_words::split(default)?),
        Err(err) => Err(format!("{} {}", name, err).into()),
    }
}

fn pkg_config_cflags(package: &str) -> Result<Vec<String>, Box<Error>> {
    let mut cmd = Command::new("pkg-config");
    cmd.arg("--cflags");
    cmd.arg(package);
    let out = cmd.output()?;
    if !out.status.success() {
        return Err(format!("command {:?} returned {}",
                           &cmd, out.status).into());
    }
    let stdout = str::from_utf8(&out.stdout)?;
    Ok(shell_words::split(stdout)?)
}

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
struct ABI {
    size: usize,
    alignment: usize,
}

impl ABI {
    pub fn from_type<T: Sized>() -> ABI {
        ABI {
            size: size_of::<T>(),
            alignment: align_of::<T>(),
        }
    }
}

#[test]
fn test_cross_validate_abi_with_c() {
    // Configure compiler instance."##)?;

    let ns = env.library.namespace(MAIN_NAMESPACE);
    let package_name = ns.package_name.as_ref()
        .expect("Missing package name");

    writeln!(w, "\tlet package_name = \"{}\";", package_name)?;
    writeln!(w, "{}", r##"    let mut cc = Compiler::new()
        .expect("compiler from environment");
    let cflags = pkg_config_cflags(package_name)
        .expect("cflags from pkg-config");
    cc.args.extend(cflags);
    cc.args.push("-Wno-deprecated-declarations".to_owned());

    // Sanity check that compilation works.
    assert_eq!(ABI {size: 1, alignment: 1},
               get_c_abi(&cc, "char").expect("C ABI for char"));

    let rust = get_rust_abi();
    let mut failed = false;
    for (name, rust_abi) in &rust {
        match get_c_abi(&cc, name) {
            Err(e) => {
                failed = true;
                eprintln!("{}", e);
                continue;
            },
            Ok(ref c_abi) => {
                if rust_abi != c_abi {
                    failed = true;
                    eprintln!("ABI mismatch for {}\nRust: {:?}\nC:    {:?}",
                              name, rust_abi, c_abi);
                }
            }
        };
    }
    assert!(!failed);
}

fn get_c_abi(cc: &Compiler, name: &str) -> Result<ABI, Box<Error>> {
    let mut cc = cc.clone();
    cc.define("ABI_TEST_TYPE", name);
    cc.args.push("tests/abi.c".to_owned());
    cc.args.push("-oabi".to_owned());

    let mut cc_cmd = cc.to_command();
    let status = cc_cmd.spawn()?.wait()?;
    if !status.success() {
        return Err(format!("compilation command {:?} failed, {}",
                           &cc_cmd, status).into());
    }

    let mut abi_cmd = Command::new("./abi");
    let output = abi_cmd.output()?;
    if !output.status.success() {
        return Err(format!("command {:?} failed, {}",
                           &abi_cmd, output.status).into());
    }

    let stdout = str::from_utf8(&output.stdout)?;
    let mut words = stdout.split_whitespace();
    let size = words.next().unwrap().parse().unwrap();
    let alignment = words.next().unwrap().parse().unwrap();
    Ok(ABI {size, alignment})
}

fn get_rust_abi() -> BTreeMap<String, ABI> {
    let mut abi = BTreeMap::new();"##)?;

    for ctype in ctypes {
        general::cfg_condition(w, &ctype.cfg_condition, false, 1)?;
        writeln!(w, r##"    abi.insert("{ctype}".to_owned(), ABI::from_type::<{ctype}>());"##,
                 ctype=ctype.name)?;
    }

    writeln!(w, "{}", r##"    abi
}

"##)

}

<|endoftext|>


<commit_before>// -*- rust -*-

fn main(vec[str] args) -> () {
  log "This is the rust 'self-hosted' compiler.";
  log "The one written in rust.";
  log "It does nothing yet, it's a placeholder.";
  log "You want rustboot, the compiler next door.";
}
<commit_msg>Add some (non-functional) input-readingn to rustc.rs.<commit_after>// -*- rust -*-

fn main(vec[str] args) -> () {

  log "This is the rust 'self-hosted' compiler.";
  log "The one written in rust.";
  log "It does nothing yet, it's a placeholder.";
  log "You want rustboot, the compiler next door.";

  auto i = 0;
  for (str filename in args) {
    if (i > 0) {
      auto br = std._io.new_buf_reader(filename);
      log "opened file: " + filename;
      for (u8 b in br.read()) {
        log b;
      }
    }
    i += 1;
  }

}
<|endoftext|>


<commit_before>//
//  SOS: the Stupid Operating System
//  by Hawk Weisman (hi@hawkweisman.me)
//
//  Copyright (c) 2015 Hawk Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
//! Code for interacting with the x86_64 CPU.
//!
//! Currently this module contains a quick implementation of CPU port
//! input and output, and little else.
use ::{io,util};

pub struct Port(u16);

impl Port {
    /// Read a byte (8 bits) from this port
    pub unsafe fn in8(&self) -> u8 {
        let result: u8;
        asm!(  "in al, dx"
            :  "={al}"(result)
            :  "{dx}"(self.0)
            :: "intel"
             , "volatile" );
        result
    }
    /// Read a word (16 bits) from this port
    pub unsafe fn in16(&self) -> u16 {
        let result: u16;
        asm!(  "in ax, dx"
            :  "={ax}"(result)
            :  "{dx}"(self.0)
            :: "intel"
             , "volatile" );
        result
    }

    /// Read a long word (32 bits) from this port
    pub unsafe fn in32(&self) -> u32 {
        let result: u32;
        asm!(  "in eax, dx"
            :  "={eax}"(result)
            :  "{dx}"(self.0)
            :: "intel"
             , "volatile" );
        result
    }

    pub unsafe fn out8(&self, value: u8) {
         asm!(  "out dx, al"
             :: "{dx}"(self.0)
              , "{al}"(value)
             :: "intel"
              , "volatile" );
    }

    pub unsafe fn out16(&self, value: u16) {
         asm!(  "out dx, ax"
             :: "{dx}"(self.0)
              , "{ax}"(value)
             :: "intel"
              , "volatile" );
    }

    pub unsafe fn out32(&self, value: u32) {
         asm!(  "out dx, eax"
             :: "{dx}"(self.0)
              , "{eax}"(value)
             :: "intel"
              , "volatile" );
    }
}

impl io::Read for Port {
    type Error = util::Void;

    fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
        unsafe {
            Ok(match &mut *buf {
                []                  => 0 // no bytes were read
              , [ref mut head, _..] => { *head = self.in8(); 1 }
            })
        }
    }

    fn read_all(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
        let mut read_bytes = 0;
        for idx in buf.iter_mut() {
            unsafe { *idx = self.in8();
                     read_bytes += 1; }
        }
        Ok(read_bytes)
    }

}
<commit_msg>[cpu.rs] Add documentation<commit_after>//
//  SOS: the Stupid Operating System
//  by Hawk Weisman (hi@hawkweisman.me)
//
//  Copyright (c) 2015 Hawk Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
//! Code for interacting with the x86_64 CPU.
//!
//! Currently this module contains a quick implementation of CPU port
//! input and output, and little else.
use ::{io,util};

pub struct Port(u16);

impl Port {
    /// Read a byte (8 bits) from this port
    pub unsafe fn in8(&self) -> u8 {
        let result: u8;
        asm!(  "in al, dx"
            :  "={al}"(result)
            :  "{dx}"(self.0)
            :: "intel"
             , "volatile" );
        result
    }
    /// Read a word (16 bits) from this port
    pub unsafe fn in16(&self) -> u16 {
        let result: u16;
        asm!(  "in ax, dx"
            :  "={ax}"(result)
            :  "{dx}"(self.0)
            :: "intel"
             , "volatile" );
        result
    }

    /// Read a long word (32 bits) from this port
    pub unsafe fn in32(&self) -> u32 {
        let result: u32;
        asm!(  "in eax, dx"
            :  "={eax}"(result)
            :  "{dx}"(self.0)
            :: "intel"
             , "volatile" );
        result
    }

    pub unsafe fn out8(&self, value: u8) {
         asm!(  "out dx, al"
             :: "{dx}"(self.0)
              , "{al}"(value)
             :: "intel"
              , "volatile" );
    }

    pub unsafe fn out16(&self, value: u16) {
         asm!(  "out dx, ax"
             :: "{dx}"(self.0)
              , "{ax}"(value)
             :: "intel"
              , "volatile" );
    }

    pub unsafe fn out32(&self, value: u32) {
         asm!(  "out dx, eax"
             :: "{dx}"(self.0)
              , "{eax}"(value)
             :: "intel"
              , "volatile" );
    }
}

impl io::Read for Port {
    type Error = util::Void;

    /// Reads a single byte into the given buffer
    fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
        unsafe {
            Ok(match &mut *buf {
                // if the length of the buffer is 0, then obviously
                // no bytes were read
                []                  => 0
                // otherwise, read one byte into the head of the buffer
              , [ref mut head, _..] => { *head = self.in8(); 1 }
            })
        }
    }

    /// Reads a new byte into each position in the buffer.
    fn read_all(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
        let mut read_bytes = 0;
        for idx in buf.iter_mut() {
            // for each index in the buffer, read another byte from the port
            unsafe { *idx = self.in8(); }
            // and increment the number of bytes read (this should be faster
            // than calling `buf.len()` later; as we only need 1 loop)
            read_bytes += 1;
        }
        Ok(read_bytes)
    }

}
<|endoftext|>


<commit_before>use std::path::PathBuf;
use std::str::FromStr;

use anyhow::{format_err, Result};
use clap::{Arg, ArgMatches, Command};
use glob::{glob_with, MatchOptions};
use log::{debug, warn};
use sha1_smol::Digest;

use crate::api::{Api, NewRelease};
use crate::config::Config;
use crate::utils::file_search::ReleaseFileSearch;
use crate::utils::file_upload::UploadContext;
use crate::utils::fs::path_as_url;
use crate::utils::sourcemaps::SourceMapProcessor;

pub fn make_command(command: Command) -> Command {
    command
        .about("Upload sourcemaps for a release.")
        // Backward compatibility with `releases files <VERSION>` commands.
        .arg(Arg::new("version").long("version").hide(true))
        .arg(
            Arg::new("paths")
                .value_name("PATHS")
                .required_unless_present_any(&["bundle", "bundle_sourcemap"])
                .multiple_occurrences(true)
                .help("The files to upload."),
        )
        .arg(
            Arg::new("url_prefix")
                .short('u')
                .long("url-prefix")
                .value_name("PREFIX")
                .help("The URL prefix to prepend to all filenames."),
        )
        .arg(
            Arg::new("url_suffix")
                .long("url-suffix")
                .value_name("SUFFIX")
                .help("The URL suffix to append to all filenames."),
        )
        .arg(
            Arg::new("dist")
                .long("dist")
                .short('d')
                .value_name("DISTRIBUTION")
                .help("Optional distribution identifier for the sourcemaps."),
        )
        .arg(
            Arg::new("validate")
                .long("validate")
                .help("Enable basic sourcemap validation."),
        )
        .arg(
            Arg::new("decompress")
                .long("decompress")
                .help("Enable files gzip decompression prior to upload."),
        )
        .arg(
            Arg::new("wait")
                .long("wait")
                .help("Wait for the server to fully process uploaded files."),
        )
        .arg(
            Arg::new("no_sourcemap_reference")
                .long("no-sourcemap-reference")
                .help(
                    "Disable emitting of automatic sourcemap references.{n}\
                    By default the tool will store a 'Sourcemap' header with \
                    minified files so that sourcemaps are located automatically \
                    if the tool can detect a link. If this causes issues it can \
                    be disabled.",
                ),
        )
        .arg(Arg::new("no_rewrite").long("no-rewrite").help(
            "Disables rewriting of matching sourcemaps. By default the tool \
                will rewrite sources, so that indexed maps are flattened and missing \
                sources are inlined if possible.{n}This fundamentally \
                changes the upload process to be based on sourcemaps \
                and minified files exclusively and comes in handy for \
                setups like react-native that generate sourcemaps that \
                would otherwise not work for sentry.",
        ))
        .arg(
            Arg::new("strip_prefix")
                .long("strip-prefix")
                .value_name("PREFIX")
                .multiple_occurrences(true)
                .help(
                    "Strips the given prefix from all sources references inside the upload \
                    sourcemaps (paths used within the sourcemap content, to map minified code \
                    to it's original source). Only sources that start with the given prefix \
                    will be stripped.{n}This will not modify the uploaded sources paths. \
                    To do that, point the upload or upload-sourcemaps command \
                    to a more precise directory instead.",
                )
                .conflicts_with("no_rewrite"),
        )
        .arg(
            Arg::new("strip_common_prefix")
                .long("strip-common-prefix")
                .help(
                    "Similar to --strip-prefix but strips the most common \
                    prefix on all sources references.",
                )
                .conflicts_with("no_rewrite"),
        )
        .arg(
            Arg::new("ignore")
                .long("ignore")
                .short('i')
                .value_name("IGNORE")
                .multiple_occurrences(true)
                .help("Ignores all files and folders matching the given glob"),
        )
        .arg(
            Arg::new("ignore_file")
                .long("ignore-file")
                .short('I')
                .value_name("IGNORE_FILE")
                .help(
                    "Ignore all files and folders specified in the given \
                    ignore file, e.g. .gitignore.",
                ),
        )
        .arg(
            Arg::new("bundle")
                .long("bundle")
                .value_name("BUNDLE")
                .conflicts_with("paths")
                .requires_all(&["bundle_sourcemap"])
                .help("Path to the application bundle (indexed, file, or regular)"),
        )
        .arg(
            Arg::new("bundle_sourcemap")
                .long("bundle-sourcemap")
                .value_name("BUNDLE_SOURCEMAP")
                .conflicts_with("paths")
                .requires_all(&["bundle"])
                .help("Path to the bundle sourcemap"),
        )
        .arg(
            Arg::new("extensions")
                .long("ext")
                .short('x')
                .value_name("EXT")
                .multiple_occurrences(true)
                .help(
                    "Set the file extensions that are considered for upload. \
                    This overrides the default extensions. To add an extension, all default \
                    extensions must be repeated. Specify once per extension.{n}\
                    Defaults to: `--ext=js --ext=map --ext=jsbundle --ext=bundle`",
                ),
        )
        // Legacy flag that has no effect, left hidden for backward compatibility
        .arg(Arg::new("rewrite").long("rewrite").hide(true))
        // Legacy flag that has no effect, left hidden for backward compatibility
        .arg(Arg::new("verbose").long("verbose").short('v').hide(true))
}

fn get_prefixes_from_args(matches: &ArgMatches) -> Vec<&str> {
    let mut prefixes: Vec<&str> = match matches.values_of("strip_prefix") {
        Some(paths) => paths.collect(),
        None => vec![],
    };
    if matches.is_present("strip_common_prefix") {
        prefixes.push("~");
    }
    prefixes
}

fn process_sources_from_bundle(
    matches: &ArgMatches,
    processor: &mut SourceMapProcessor,
) -> Result<()> {
    let url_suffix = matches.value_of("url_suffix").unwrap_or("");
    let mut url_prefix = matches.value_of("url_prefix").unwrap_or("~");
    // remove a single slash from the end.  so ~/ becomes ~ and app:/// becomes app://
    if url_prefix.ends_with('/') {
        url_prefix = &url_prefix[..url_prefix.len() - 1];
    }

    let bundle_path = PathBuf::from(matches.value_of("bundle").unwrap());
    let bundle_url = format!(
        "{}/{}{}",
        url_prefix,
        bundle_path.file_name().unwrap().to_string_lossy(),
        url_suffix
    );

    let sourcemap_path = PathBuf::from(matches.value_of("bundle_sourcemap").unwrap());
    let sourcemap_url = format!(
        "{}/{}{}",
        url_prefix,
        sourcemap_path.file_name().unwrap().to_string_lossy(),
        url_suffix
    );

    debug!("Bundle path: {}", bundle_path.display());
    debug!("Sourcemap path: {}", sourcemap_path.display());

    processor.add(
        &bundle_url,
        ReleaseFileSearch::collect_file(bundle_path.clone())?,
    )?;
    processor.add(
        &sourcemap_url,
        ReleaseFileSearch::collect_file(sourcemap_path)?,
    )?;

    if let Ok(ram_bundle) = sourcemap::ram_bundle::RamBundle::parse_unbundle_from_path(&bundle_path)
    {
        debug!("File RAM bundle found, extracting its contents...");
        // For file ("unbundle") RAM bundles we need to explicitly unpack it, otherwise we cannot detect it
        // reliably inside "processor.rewrite()"
        processor.unpack_ram_bundle(&ram_bundle, &bundle_url)?;
    } else if sourcemap::ram_bundle::RamBundle::parse_indexed_from_path(&bundle_path).is_ok() {
        debug!("Indexed RAM bundle found");
    } else {
        warn!("Regular bundle found");
    }

    let mut prefixes = get_prefixes_from_args(matches);
    if !prefixes.contains(&"~") {
        prefixes.push("~");
    }
    debug!("Prefixes: {:?}", prefixes);

    processor.rewrite(&prefixes)?;
    processor.add_sourcemap_references()?;

    Ok(())
}

fn process_sources_from_paths(
    matches: &ArgMatches,
    processor: &mut SourceMapProcessor,
) -> Result<()> {
    let paths = matches.values_of("paths").unwrap();
    let ignore_file = matches.value_of("ignore_file").unwrap_or("");
    let extensions = matches
        .values_of("extensions")
        .map(|extensions| extensions.map(|ext| ext.trim_start_matches('.')).collect())
        .unwrap_or_else(|| vec!["js", "map", "jsbundle", "bundle"]);
    let ignores = matches
        .values_of("ignore")
        .map(|ignores| ignores.map(|i| format!("!{}", i)).collect())
        .unwrap_or_else(Vec::new);

    let opts = MatchOptions::new();
    let collected_paths = paths.flat_map(|path| glob_with(path, opts).unwrap().flatten());

    for path in collected_paths {
        // if we start walking over something that is an actual file then
        // the directory iterator yields that path and terminates.  We
        // handle that case here specifically to figure out what the path is
        // we should strip off.
        let path = path.as_path();
        let (base_path, check_ignore) = if path.is_file() {
            (path.parent().unwrap(), false)
        } else {
            (path, true)
        };

        let mut search = ReleaseFileSearch::new(path.to_path_buf());
        search.decompress(matches.is_present("decompress"));

        if check_ignore {
            search
                .ignore_file(ignore_file)
                .ignores(ignores.clone())
                .extensions(extensions.clone());
        }

        let sources = search.collect_files()?;

        let url_suffix = matches.value_of("url_suffix").unwrap_or("");
        let mut url_prefix = matches.value_of("url_prefix").unwrap_or("~");
        // remove a single slash from the end.  so ~/ becomes ~ and app:/// becomes app://
        if url_prefix.ends_with('/') {
            url_prefix = &url_prefix[..url_prefix.len() - 1];
        }

        for source in sources {
            let local_path = source.path.strip_prefix(base_path).unwrap();
            let url = format!("{}/{}{}", url_prefix, path_as_url(local_path), url_suffix);
            processor.add(&url, source)?;
        }
    }

    if !matches.is_present("no_rewrite") {
        let prefixes = get_prefixes_from_args(matches);
        processor.rewrite(&prefixes)?;
    }

    if !matches.is_present("no_sourcemap_reference") {
        processor.add_sourcemap_references()?;
    }

    if matches.is_present("validate") {
        processor.validate_all()?;
    }

    Ok(())
}

pub fn execute(matches: &ArgMatches) -> Result<()> {
    let config = Config::current();
    let version = config.get_release_with_legacy_fallback(matches)?;
    let (org, project) = config.get_org_and_project(matches)?;
    let api = Api::current();
    let mut processor = SourceMapProcessor::new();

    for artifact in api.list_release_files(&org, Some(&project), &version)? {
        let checksum = Digest::from_str(&artifact.sha1)
            .map_err(|_| format_err!("Invalid artifact checksum"))?;

        processor.add_already_uploaded_source(checksum);
    }

    if matches.is_present("bundle") && matches.is_present("bundle_sourcemap") {
        process_sources_from_bundle(matches, &mut processor)?;
    } else {
        process_sources_from_paths(matches, &mut processor)?;
    }

    // make sure the release exists
    let release = api.new_release(
        &org,
        &NewRelease {
            version,
            projects: config.get_projects(matches)?,
            ..Default::default()
        },
    )?;

    processor.upload(&UploadContext {
        org: &org,
        project: Some(&project),
        release: &release.version,
        dist: matches.value_of("dist"),
        wait: matches.is_present("wait"),
    })?;

    Ok(())
}
<commit_msg>fix: Make sure release exists before querying for artifacts (#1284)<commit_after>use std::path::PathBuf;
use std::str::FromStr;

use anyhow::{format_err, Result};
use clap::{Arg, ArgMatches, Command};
use glob::{glob_with, MatchOptions};
use log::{debug, warn};
use sha1_smol::Digest;

use crate::api::{Api, NewRelease};
use crate::config::Config;
use crate::utils::file_search::ReleaseFileSearch;
use crate::utils::file_upload::UploadContext;
use crate::utils::fs::path_as_url;
use crate::utils::sourcemaps::SourceMapProcessor;

pub fn make_command(command: Command) -> Command {
    command
        .about("Upload sourcemaps for a release.")
        // Backward compatibility with `releases files <VERSION>` commands.
        .arg(Arg::new("version").long("version").hide(true))
        .arg(
            Arg::new("paths")
                .value_name("PATHS")
                .required_unless_present_any(&["bundle", "bundle_sourcemap"])
                .multiple_occurrences(true)
                .help("The files to upload."),
        )
        .arg(
            Arg::new("url_prefix")
                .short('u')
                .long("url-prefix")
                .value_name("PREFIX")
                .help("The URL prefix to prepend to all filenames."),
        )
        .arg(
            Arg::new("url_suffix")
                .long("url-suffix")
                .value_name("SUFFIX")
                .help("The URL suffix to append to all filenames."),
        )
        .arg(
            Arg::new("dist")
                .long("dist")
                .short('d')
                .value_name("DISTRIBUTION")
                .help("Optional distribution identifier for the sourcemaps."),
        )
        .arg(
            Arg::new("validate")
                .long("validate")
                .help("Enable basic sourcemap validation."),
        )
        .arg(
            Arg::new("decompress")
                .long("decompress")
                .help("Enable files gzip decompression prior to upload."),
        )
        .arg(
            Arg::new("wait")
                .long("wait")
                .help("Wait for the server to fully process uploaded files."),
        )
        .arg(
            Arg::new("no_sourcemap_reference")
                .long("no-sourcemap-reference")
                .help(
                    "Disable emitting of automatic sourcemap references.{n}\
                    By default the tool will store a 'Sourcemap' header with \
                    minified files so that sourcemaps are located automatically \
                    if the tool can detect a link. If this causes issues it can \
                    be disabled.",
                ),
        )
        .arg(Arg::new("no_rewrite").long("no-rewrite").help(
            "Disables rewriting of matching sourcemaps. By default the tool \
                will rewrite sources, so that indexed maps are flattened and missing \
                sources are inlined if possible.{n}This fundamentally \
                changes the upload process to be based on sourcemaps \
                and minified files exclusively and comes in handy for \
                setups like react-native that generate sourcemaps that \
                would otherwise not work for sentry.",
        ))
        .arg(
            Arg::new("strip_prefix")
                .long("strip-prefix")
                .value_name("PREFIX")
                .multiple_occurrences(true)
                .help(
                    "Strips the given prefix from all sources references inside the upload \
                    sourcemaps (paths used within the sourcemap content, to map minified code \
                    to it's original source). Only sources that start with the given prefix \
                    will be stripped.{n}This will not modify the uploaded sources paths. \
                    To do that, point the upload or upload-sourcemaps command \
                    to a more precise directory instead.",
                )
                .conflicts_with("no_rewrite"),
        )
        .arg(
            Arg::new("strip_common_prefix")
                .long("strip-common-prefix")
                .help(
                    "Similar to --strip-prefix but strips the most common \
                    prefix on all sources references.",
                )
                .conflicts_with("no_rewrite"),
        )
        .arg(
            Arg::new("ignore")
                .long("ignore")
                .short('i')
                .value_name("IGNORE")
                .multiple_occurrences(true)
                .help("Ignores all files and folders matching the given glob"),
        )
        .arg(
            Arg::new("ignore_file")
                .long("ignore-file")
                .short('I')
                .value_name("IGNORE_FILE")
                .help(
                    "Ignore all files and folders specified in the given \
                    ignore file, e.g. .gitignore.",
                ),
        )
        .arg(
            Arg::new("bundle")
                .long("bundle")
                .value_name("BUNDLE")
                .conflicts_with("paths")
                .requires_all(&["bundle_sourcemap"])
                .help("Path to the application bundle (indexed, file, or regular)"),
        )
        .arg(
            Arg::new("bundle_sourcemap")
                .long("bundle-sourcemap")
                .value_name("BUNDLE_SOURCEMAP")
                .conflicts_with("paths")
                .requires_all(&["bundle"])
                .help("Path to the bundle sourcemap"),
        )
        .arg(
            Arg::new("extensions")
                .long("ext")
                .short('x')
                .value_name("EXT")
                .multiple_occurrences(true)
                .help(
                    "Set the file extensions that are considered for upload. \
                    This overrides the default extensions. To add an extension, all default \
                    extensions must be repeated. Specify once per extension.{n}\
                    Defaults to: `--ext=js --ext=map --ext=jsbundle --ext=bundle`",
                ),
        )
        // Legacy flag that has no effect, left hidden for backward compatibility
        .arg(Arg::new("rewrite").long("rewrite").hide(true))
        // Legacy flag that has no effect, left hidden for backward compatibility
        .arg(Arg::new("verbose").long("verbose").short('v').hide(true))
}

fn get_prefixes_from_args(matches: &ArgMatches) -> Vec<&str> {
    let mut prefixes: Vec<&str> = match matches.values_of("strip_prefix") {
        Some(paths) => paths.collect(),
        None => vec![],
    };
    if matches.is_present("strip_common_prefix") {
        prefixes.push("~");
    }
    prefixes
}

fn process_sources_from_bundle(
    matches: &ArgMatches,
    processor: &mut SourceMapProcessor,
) -> Result<()> {
    let url_suffix = matches.value_of("url_suffix").unwrap_or("");
    let mut url_prefix = matches.value_of("url_prefix").unwrap_or("~");
    // remove a single slash from the end.  so ~/ becomes ~ and app:/// becomes app://
    if url_prefix.ends_with('/') {
        url_prefix = &url_prefix[..url_prefix.len() - 1];
    }

    let bundle_path = PathBuf::from(matches.value_of("bundle").unwrap());
    let bundle_url = format!(
        "{}/{}{}",
        url_prefix,
        bundle_path.file_name().unwrap().to_string_lossy(),
        url_suffix
    );

    let sourcemap_path = PathBuf::from(matches.value_of("bundle_sourcemap").unwrap());
    let sourcemap_url = format!(
        "{}/{}{}",
        url_prefix,
        sourcemap_path.file_name().unwrap().to_string_lossy(),
        url_suffix
    );

    debug!("Bundle path: {}", bundle_path.display());
    debug!("Sourcemap path: {}", sourcemap_path.display());

    processor.add(
        &bundle_url,
        ReleaseFileSearch::collect_file(bundle_path.clone())?,
    )?;
    processor.add(
        &sourcemap_url,
        ReleaseFileSearch::collect_file(sourcemap_path)?,
    )?;

    if let Ok(ram_bundle) = sourcemap::ram_bundle::RamBundle::parse_unbundle_from_path(&bundle_path)
    {
        debug!("File RAM bundle found, extracting its contents...");
        // For file ("unbundle") RAM bundles we need to explicitly unpack it, otherwise we cannot detect it
        // reliably inside "processor.rewrite()"
        processor.unpack_ram_bundle(&ram_bundle, &bundle_url)?;
    } else if sourcemap::ram_bundle::RamBundle::parse_indexed_from_path(&bundle_path).is_ok() {
        debug!("Indexed RAM bundle found");
    } else {
        warn!("Regular bundle found");
    }

    let mut prefixes = get_prefixes_from_args(matches);
    if !prefixes.contains(&"~") {
        prefixes.push("~");
    }
    debug!("Prefixes: {:?}", prefixes);

    processor.rewrite(&prefixes)?;
    processor.add_sourcemap_references()?;

    Ok(())
}

fn process_sources_from_paths(
    matches: &ArgMatches,
    processor: &mut SourceMapProcessor,
) -> Result<()> {
    let paths = matches.values_of("paths").unwrap();
    let ignore_file = matches.value_of("ignore_file").unwrap_or("");
    let extensions = matches
        .values_of("extensions")
        .map(|extensions| extensions.map(|ext| ext.trim_start_matches('.')).collect())
        .unwrap_or_else(|| vec!["js", "map", "jsbundle", "bundle"]);
    let ignores = matches
        .values_of("ignore")
        .map(|ignores| ignores.map(|i| format!("!{}", i)).collect())
        .unwrap_or_else(Vec::new);

    let opts = MatchOptions::new();
    let collected_paths = paths.flat_map(|path| glob_with(path, opts).unwrap().flatten());

    for path in collected_paths {
        // if we start walking over something that is an actual file then
        // the directory iterator yields that path and terminates.  We
        // handle that case here specifically to figure out what the path is
        // we should strip off.
        let path = path.as_path();
        let (base_path, check_ignore) = if path.is_file() {
            (path.parent().unwrap(), false)
        } else {
            (path, true)
        };

        let mut search = ReleaseFileSearch::new(path.to_path_buf());
        search.decompress(matches.is_present("decompress"));

        if check_ignore {
            search
                .ignore_file(ignore_file)
                .ignores(ignores.clone())
                .extensions(extensions.clone());
        }

        let sources = search.collect_files()?;

        let url_suffix = matches.value_of("url_suffix").unwrap_or("");
        let mut url_prefix = matches.value_of("url_prefix").unwrap_or("~");
        // remove a single slash from the end.  so ~/ becomes ~ and app:/// becomes app://
        if url_prefix.ends_with('/') {
            url_prefix = &url_prefix[..url_prefix.len() - 1];
        }

        for source in sources {
            let local_path = source.path.strip_prefix(base_path).unwrap();
            let url = format!("{}/{}{}", url_prefix, path_as_url(local_path), url_suffix);
            processor.add(&url, source)?;
        }
    }

    if !matches.is_present("no_rewrite") {
        let prefixes = get_prefixes_from_args(matches);
        processor.rewrite(&prefixes)?;
    }

    if !matches.is_present("no_sourcemap_reference") {
        processor.add_sourcemap_references()?;
    }

    if matches.is_present("validate") {
        processor.validate_all()?;
    }

    Ok(())
}

pub fn execute(matches: &ArgMatches) -> Result<()> {
    let config = Config::current();
    let version = config.get_release_with_legacy_fallback(matches)?;
    let (org, project) = config.get_org_and_project(matches)?;
    let api = Api::current();
    let mut processor = SourceMapProcessor::new();

    if matches.is_present("bundle") && matches.is_present("bundle_sourcemap") {
        process_sources_from_bundle(matches, &mut processor)?;
    } else {
        process_sources_from_paths(matches, &mut processor)?;
    }

    // make sure the release exists
    let release = api.new_release(
        &org,
        &NewRelease {
            version,
            projects: config.get_projects(matches)?,
            ..Default::default()
        },
    )?;

    for artifact in api.list_release_files(&org, Some(&project), &release.version)? {
        let checksum = Digest::from_str(&artifact.sha1)
            .map_err(|_| format_err!("Invalid artifact checksum"))?;

        processor.add_already_uploaded_source(checksum);
    }

    processor.upload(&UploadContext {
        org: &org,
        project: Some(&project),
        release: &release.version,
        dist: matches.value_of("dist"),
        wait: matches.is_present("wait"),
    })?;

    Ok(())
}
<|endoftext|>


<commit_before>extern crate nom;

pub mod database;
pub mod stream;
pub mod row;
pub mod server;
pub mod schema;
pub mod value;
pub mod row_builder;
pub mod row_reader;
pub mod parser;

mod tests;

use self::database::Database;
<commit_msg>added DB page struct time to write rows out<commit_after>extern crate nom;

pub mod database;
pub mod stream;
pub mod row;
pub mod server;
pub mod schema;
pub mod value;
pub mod row_builder;
pub mod row_reader;
pub mod parser;
pub mod storage;

mod tests;

use self::database::Database;
<|endoftext|>


<commit_before>//! A copy of the `Qualif` trait in `qualify_consts.rs` that is suitable for the new validator.

use rustc::mir::*;
use rustc::ty::{self, Ty};
use rustc::hir::def_id::DefId;
use syntax_pos::DUMMY_SP;

use super::{ConstKind, Item as ConstCx};

pub fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> ConstQualifs {
    ConstQualifs {
        has_mut_interior: HasMutInterior::in_any_value_of_ty(cx, ty),
        needs_drop: NeedsDrop::in_any_value_of_ty(cx, ty),
    }
}

/// A "qualif"(-ication) is a way to look for something "bad" in the MIR that would disqualify some
/// code for promotion or prevent it from evaluating at compile time. So `return true` means
/// "I found something bad, no reason to go on searching". `false` is only returned if we
/// definitely cannot find anything bad anywhere.
///
/// The default implementations proceed structurally.
pub trait Qualif {
    /// The name of the file used to debug the dataflow analysis that computes this qualif.
    const ANALYSIS_NAME: &'static str;

    /// Whether this `Qualif` is cleared when a local is moved from.
    const IS_CLEARED_ON_MOVE: bool = false;

    fn in_qualifs(qualifs: &ConstQualifs) -> bool;

    /// Return the qualification that is (conservatively) correct for any value
    /// of the type.
    fn in_any_value_of_ty(_cx: &ConstCx<'_, 'tcx>, _ty: Ty<'tcx>) -> bool;

    fn in_static(_cx: &ConstCx<'_, 'tcx>, _def_id: DefId) -> bool {
        // FIXME(eddyb) should we do anything here for value properties?
        false
    }

    fn in_projection_structurally(
        cx: &ConstCx<'_, 'tcx>,
        per_local: &impl Fn(Local) -> bool,
        place: PlaceRef<'_, 'tcx>,
    ) -> bool {
        if let [proj_base @ .., elem] = place.projection {
            let base_qualif = Self::in_place(cx, per_local, PlaceRef {
                base: place.base,
                projection: proj_base,
            });
            let qualif = base_qualif && Self::in_any_value_of_ty(
                cx,
                Place::ty_from(place.base, proj_base, cx.body, cx.tcx)
                    .projection_ty(cx.tcx, elem)
                    .ty,
            );
            match elem {
                ProjectionElem::Deref |
                ProjectionElem::Subslice { .. } |
                ProjectionElem::Field(..) |
                ProjectionElem::ConstantIndex { .. } |
                ProjectionElem::Downcast(..) => qualif,

                ProjectionElem::Index(local) => qualif || per_local(*local),
            }
        } else {
            bug!("This should be called if projection is not empty");
        }
    }

    fn in_projection(
        cx: &ConstCx<'_, 'tcx>,
        per_local: &impl Fn(Local) -> bool,
        place: PlaceRef<'_, 'tcx>,
    ) -> bool {
        Self::in_projection_structurally(cx, per_local, place)
    }

    fn in_place(
        cx: &ConstCx<'_, 'tcx>,
        per_local: &impl Fn(Local) -> bool,
        place: PlaceRef<'_, 'tcx>,
    ) -> bool {
        match place {
            PlaceRef {
                base: PlaceBase::Local(local),
                projection: [],
            } => per_local(*local),
            PlaceRef {
                base: PlaceBase::Static(_),
                projection: [],
            } => bug!("qualifying already promoted MIR"),
            PlaceRef {
                base: _,
                projection: [.., _],
            } => Self::in_projection(cx, per_local, place),
        }
    }

    fn in_operand(
        cx: &ConstCx<'_, 'tcx>,
        per_local: &impl Fn(Local) -> bool,
        operand: &Operand<'tcx>,
    ) -> bool {
        match *operand {
            Operand::Copy(ref place) |
            Operand::Move(ref place) => Self::in_place(cx, per_local, place.as_ref()),

            Operand::Constant(ref constant) => {
                if let Some(static_) = constant.check_static_ptr(cx.tcx) {
                    Self::in_static(cx, static_)
                } else if let ty::ConstKind::Unevaluated(def_id, _) = constant.literal.val {
                    // Don't peek inside trait associated constants.
                    if cx.tcx.trait_of_item(def_id).is_some() {
                        Self::in_any_value_of_ty(cx, constant.literal.ty)
                    } else {
                        let qualifs = cx.tcx.at(constant.span).mir_const_qualif(def_id);
                        let qualif = Self::in_qualifs(&qualifs);

                        // Just in case the type is more specific than
                        // the definition, e.g., impl associated const
                        // with type parameters, take it into account.
                        qualif && Self::in_any_value_of_ty(cx, constant.literal.ty)
                    }
                } else {
                    false
                }
            }
        }
    }

    fn in_rvalue_structurally(
        cx: &ConstCx<'_, 'tcx>,
        per_local: &impl Fn(Local) -> bool,
        rvalue: &Rvalue<'tcx>,
    ) -> bool {
        match *rvalue {
            Rvalue::NullaryOp(..) => false,

            Rvalue::Discriminant(ref place) |
            Rvalue::Len(ref place) => Self::in_place(cx, per_local, place.as_ref()),

            Rvalue::Use(ref operand) |
            Rvalue::Repeat(ref operand, _) |
            Rvalue::UnaryOp(_, ref operand) |
            Rvalue::Cast(_, ref operand, _) => Self::in_operand(cx, per_local, operand),

            Rvalue::BinaryOp(_, ref lhs, ref rhs) |
            Rvalue::CheckedBinaryOp(_, ref lhs, ref rhs) => {
                Self::in_operand(cx, per_local, lhs) || Self::in_operand(cx, per_local, rhs)
            }

            Rvalue::Ref(_, _, ref place) => {
                // Special-case reborrows to be more like a copy of the reference.
                if let &[ref proj_base @ .., elem] = place.projection.as_ref() {
                    if ProjectionElem::Deref == elem {
                        let base_ty = Place::ty_from(&place.base, proj_base, cx.body, cx.tcx).ty;
                        if let ty::Ref(..) = base_ty.kind {
                            return Self::in_place(cx, per_local, PlaceRef {
                                base: &place.base,
                                projection: proj_base,
                            });
                        }
                    }
                }

                Self::in_place(cx, per_local, place.as_ref())
            }

            Rvalue::Aggregate(_, ref operands) => {
                operands.iter().any(|o| Self::in_operand(cx, per_local, o))
            }
        }
    }

    fn in_rvalue(
        cx: &ConstCx<'_, 'tcx>,
        per_local: &impl Fn(Local) -> bool,
        rvalue: &Rvalue<'tcx>,
    ) -> bool {
        Self::in_rvalue_structurally(cx, per_local, rvalue)
    }

    fn in_call(
        cx: &ConstCx<'_, 'tcx>,
        _per_local: &impl Fn(Local) -> bool,
        _callee: &Operand<'tcx>,
        _args: &[Operand<'tcx>],
        return_ty: Ty<'tcx>,
    ) -> bool {
        // Be conservative about the returned value of a const fn.
        Self::in_any_value_of_ty(cx, return_ty)
    }
}

/// Constant containing interior mutability (`UnsafeCell<T>`).
/// This must be ruled out to make sure that evaluating the constant at compile-time
/// and at *any point* during the run-time would produce the same result. In particular,
/// promotion of temporaries must not change program behavior; if the promoted could be
/// written to, that would be a problem.
pub struct HasMutInterior;

impl Qualif for HasMutInterior {
    const ANALYSIS_NAME: &'static str = "flow_has_mut_interior";

    fn in_qualifs(qualifs: &ConstQualifs) -> bool {
        qualifs.has_mut_interior
    }

    fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool {
        !ty.is_freeze(cx.tcx, cx.param_env, DUMMY_SP)
    }

    fn in_rvalue(
        cx: &ConstCx<'_, 'tcx>,
        per_local: &impl Fn(Local) -> bool,
        rvalue: &Rvalue<'tcx>,
    ) -> bool {
        match *rvalue {
            // Returning `true` for `Rvalue::Ref` indicates the borrow isn't
            // allowed in constants (and the `Checker` will error), and/or it
            // won't be promoted, due to `&mut ...` or interior mutability.
            Rvalue::Ref(_, kind, ref place) => {
                let ty = place.ty(cx.body, cx.tcx).ty;

                if let BorrowKind::Mut { .. } = kind {
                    // In theory, any zero-sized value could be borrowed
                    // mutably without consequences.
                    match ty.kind {
                        // Inside a `static mut`, &mut [...] is also allowed.
                        | ty::Array(..)
                        | ty::Slice(_)
                        if cx.const_kind == Some(ConstKind::StaticMut)
                        => {},

                        // FIXME(eddyb): We only return false for `&mut []` outside a const
                        // context which seems unnecessary given that this is merely a ZST.
                        | ty::Array(_, len)
                        if len.try_eval_usize(cx.tcx, cx.param_env) == Some(0)
                            && cx.const_kind == None
                        => {},

                        _ => return true,
                    }
                }
            }

            Rvalue::Aggregate(ref kind, _) => {
                if let AggregateKind::Adt(def, ..) = **kind {
                    if Some(def.did) == cx.tcx.lang_items().unsafe_cell_type() {
                        let ty = rvalue.ty(cx.body, cx.tcx);
                        assert_eq!(Self::in_any_value_of_ty(cx, ty), true);
                        return true;
                    }
                }
            }

            _ => {}
        }

        Self::in_rvalue_structurally(cx, per_local, rvalue)
    }
}

/// Constant containing an ADT that implements `Drop`.
/// This must be ruled out (a) because we cannot run `Drop` during compile-time
/// as that might not be a `const fn`, and (b) because implicit promotion would
/// remove side-effects that occur as part of dropping that value.
pub struct NeedsDrop;

impl Qualif for NeedsDrop {
    const ANALYSIS_NAME: &'static str = "flow_needs_drop";
    const IS_CLEARED_ON_MOVE: bool = true;

    fn in_qualifs(qualifs: &ConstQualifs) -> bool {
        qualifs.needs_drop
    }

    fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool {
        ty.needs_drop(cx.tcx, cx.param_env)
    }

    fn in_rvalue(
        cx: &ConstCx<'_, 'tcx>,
        per_local: &impl Fn(Local) -> bool,
        rvalue: &Rvalue<'tcx>,
    ) -> bool {
        if let Rvalue::Aggregate(ref kind, _) = *rvalue {
            if let AggregateKind::Adt(def, ..) = **kind {
                if def.has_dtor(cx.tcx) {
                    return true;
                }
            }
        }

        Self::in_rvalue_structurally(cx, per_local, rvalue)
    }
}
<commit_msg>Use type-based qualification for statics<commit_after>//! A copy of the `Qualif` trait in `qualify_consts.rs` that is suitable for the new validator.

use rustc::mir::*;
use rustc::ty::{self, Ty};
use rustc::hir::def_id::DefId;
use syntax_pos::DUMMY_SP;

use super::{ConstKind, Item as ConstCx};

pub fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> ConstQualifs {
    ConstQualifs {
        has_mut_interior: HasMutInterior::in_any_value_of_ty(cx, ty),
        needs_drop: NeedsDrop::in_any_value_of_ty(cx, ty),
    }
}

/// A "qualif"(-ication) is a way to look for something "bad" in the MIR that would disqualify some
/// code for promotion or prevent it from evaluating at compile time. So `return true` means
/// "I found something bad, no reason to go on searching". `false` is only returned if we
/// definitely cannot find anything bad anywhere.
///
/// The default implementations proceed structurally.
pub trait Qualif {
    /// The name of the file used to debug the dataflow analysis that computes this qualif.
    const ANALYSIS_NAME: &'static str;

    /// Whether this `Qualif` is cleared when a local is moved from.
    const IS_CLEARED_ON_MOVE: bool = false;

    fn in_qualifs(qualifs: &ConstQualifs) -> bool;

    /// Return the qualification that is (conservatively) correct for any value
    /// of the type.
    fn in_any_value_of_ty(_cx: &ConstCx<'_, 'tcx>, _ty: Ty<'tcx>) -> bool;

    fn in_static(cx: &ConstCx<'_, 'tcx>, def_id: DefId) -> bool {
        // `mir_const_qualif` does return the qualifs in the final value of a `static`, so we could
        // use value-based qualification here, but we shouldn't do this without a good reason.
        Self::in_any_value_of_ty(cx, cx.tcx.type_of(def_id))
    }

    fn in_projection_structurally(
        cx: &ConstCx<'_, 'tcx>,
        per_local: &impl Fn(Local) -> bool,
        place: PlaceRef<'_, 'tcx>,
    ) -> bool {
        if let [proj_base @ .., elem] = place.projection {
            let base_qualif = Self::in_place(cx, per_local, PlaceRef {
                base: place.base,
                projection: proj_base,
            });
            let qualif = base_qualif && Self::in_any_value_of_ty(
                cx,
                Place::ty_from(place.base, proj_base, cx.body, cx.tcx)
                    .projection_ty(cx.tcx, elem)
                    .ty,
            );
            match elem {
                ProjectionElem::Deref |
                ProjectionElem::Subslice { .. } |
                ProjectionElem::Field(..) |
                ProjectionElem::ConstantIndex { .. } |
                ProjectionElem::Downcast(..) => qualif,

                ProjectionElem::Index(local) => qualif || per_local(*local),
            }
        } else {
            bug!("This should be called if projection is not empty");
        }
    }

    fn in_projection(
        cx: &ConstCx<'_, 'tcx>,
        per_local: &impl Fn(Local) -> bool,
        place: PlaceRef<'_, 'tcx>,
    ) -> bool {
        Self::in_projection_structurally(cx, per_local, place)
    }

    fn in_place(
        cx: &ConstCx<'_, 'tcx>,
        per_local: &impl Fn(Local) -> bool,
        place: PlaceRef<'_, 'tcx>,
    ) -> bool {
        match place {
            PlaceRef {
                base: PlaceBase::Local(local),
                projection: [],
            } => per_local(*local),
            PlaceRef {
                base: PlaceBase::Static(_),
                projection: [],
            } => bug!("qualifying already promoted MIR"),
            PlaceRef {
                base: _,
                projection: [.., _],
            } => Self::in_projection(cx, per_local, place),
        }
    }

    fn in_operand(
        cx: &ConstCx<'_, 'tcx>,
        per_local: &impl Fn(Local) -> bool,
        operand: &Operand<'tcx>,
    ) -> bool {
        match *operand {
            Operand::Copy(ref place) |
            Operand::Move(ref place) => Self::in_place(cx, per_local, place.as_ref()),

            Operand::Constant(ref constant) => {
                if let Some(static_) = constant.check_static_ptr(cx.tcx) {
                    Self::in_static(cx, static_)
                } else if let ty::ConstKind::Unevaluated(def_id, _) = constant.literal.val {
                    // Don't peek inside trait associated constants.
                    if cx.tcx.trait_of_item(def_id).is_some() {
                        Self::in_any_value_of_ty(cx, constant.literal.ty)
                    } else {
                        let qualifs = cx.tcx.at(constant.span).mir_const_qualif(def_id);
                        let qualif = Self::in_qualifs(&qualifs);

                        // Just in case the type is more specific than
                        // the definition, e.g., impl associated const
                        // with type parameters, take it into account.
                        qualif && Self::in_any_value_of_ty(cx, constant.literal.ty)
                    }
                } else {
                    false
                }
            }
        }
    }

    fn in_rvalue_structurally(
        cx: &ConstCx<'_, 'tcx>,
        per_local: &impl Fn(Local) -> bool,
        rvalue: &Rvalue<'tcx>,
    ) -> bool {
        match *rvalue {
            Rvalue::NullaryOp(..) => false,

            Rvalue::Discriminant(ref place) |
            Rvalue::Len(ref place) => Self::in_place(cx, per_local, place.as_ref()),

            Rvalue::Use(ref operand) |
            Rvalue::Repeat(ref operand, _) |
            Rvalue::UnaryOp(_, ref operand) |
            Rvalue::Cast(_, ref operand, _) => Self::in_operand(cx, per_local, operand),

            Rvalue::BinaryOp(_, ref lhs, ref rhs) |
            Rvalue::CheckedBinaryOp(_, ref lhs, ref rhs) => {
                Self::in_operand(cx, per_local, lhs) || Self::in_operand(cx, per_local, rhs)
            }

            Rvalue::Ref(_, _, ref place) => {
                // Special-case reborrows to be more like a copy of the reference.
                if let &[ref proj_base @ .., elem] = place.projection.as_ref() {
                    if ProjectionElem::Deref == elem {
                        let base_ty = Place::ty_from(&place.base, proj_base, cx.body, cx.tcx).ty;
                        if let ty::Ref(..) = base_ty.kind {
                            return Self::in_place(cx, per_local, PlaceRef {
                                base: &place.base,
                                projection: proj_base,
                            });
                        }
                    }
                }

                Self::in_place(cx, per_local, place.as_ref())
            }

            Rvalue::Aggregate(_, ref operands) => {
                operands.iter().any(|o| Self::in_operand(cx, per_local, o))
            }
        }
    }

    fn in_rvalue(
        cx: &ConstCx<'_, 'tcx>,
        per_local: &impl Fn(Local) -> bool,
        rvalue: &Rvalue<'tcx>,
    ) -> bool {
        Self::in_rvalue_structurally(cx, per_local, rvalue)
    }

    fn in_call(
        cx: &ConstCx<'_, 'tcx>,
        _per_local: &impl Fn(Local) -> bool,
        _callee: &Operand<'tcx>,
        _args: &[Operand<'tcx>],
        return_ty: Ty<'tcx>,
    ) -> bool {
        // Be conservative about the returned value of a const fn.
        Self::in_any_value_of_ty(cx, return_ty)
    }
}

/// Constant containing interior mutability (`UnsafeCell<T>`).
/// This must be ruled out to make sure that evaluating the constant at compile-time
/// and at *any point* during the run-time would produce the same result. In particular,
/// promotion of temporaries must not change program behavior; if the promoted could be
/// written to, that would be a problem.
pub struct HasMutInterior;

impl Qualif for HasMutInterior {
    const ANALYSIS_NAME: &'static str = "flow_has_mut_interior";

    fn in_qualifs(qualifs: &ConstQualifs) -> bool {
        qualifs.has_mut_interior
    }

    fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool {
        !ty.is_freeze(cx.tcx, cx.param_env, DUMMY_SP)
    }

    fn in_rvalue(
        cx: &ConstCx<'_, 'tcx>,
        per_local: &impl Fn(Local) -> bool,
        rvalue: &Rvalue<'tcx>,
    ) -> bool {
        match *rvalue {
            // Returning `true` for `Rvalue::Ref` indicates the borrow isn't
            // allowed in constants (and the `Checker` will error), and/or it
            // won't be promoted, due to `&mut ...` or interior mutability.
            Rvalue::Ref(_, kind, ref place) => {
                let ty = place.ty(cx.body, cx.tcx).ty;

                if let BorrowKind::Mut { .. } = kind {
                    // In theory, any zero-sized value could be borrowed
                    // mutably without consequences.
                    match ty.kind {
                        // Inside a `static mut`, &mut [...] is also allowed.
                        | ty::Array(..)
                        | ty::Slice(_)
                        if cx.const_kind == Some(ConstKind::StaticMut)
                        => {},

                        // FIXME(eddyb): We only return false for `&mut []` outside a const
                        // context which seems unnecessary given that this is merely a ZST.
                        | ty::Array(_, len)
                        if len.try_eval_usize(cx.tcx, cx.param_env) == Some(0)
                            && cx.const_kind == None
                        => {},

                        _ => return true,
                    }
                }
            }

            Rvalue::Aggregate(ref kind, _) => {
                if let AggregateKind::Adt(def, ..) = **kind {
                    if Some(def.did) == cx.tcx.lang_items().unsafe_cell_type() {
                        let ty = rvalue.ty(cx.body, cx.tcx);
                        assert_eq!(Self::in_any_value_of_ty(cx, ty), true);
                        return true;
                    }
                }
            }

            _ => {}
        }

        Self::in_rvalue_structurally(cx, per_local, rvalue)
    }
}

/// Constant containing an ADT that implements `Drop`.
/// This must be ruled out (a) because we cannot run `Drop` during compile-time
/// as that might not be a `const fn`, and (b) because implicit promotion would
/// remove side-effects that occur as part of dropping that value.
pub struct NeedsDrop;

impl Qualif for NeedsDrop {
    const ANALYSIS_NAME: &'static str = "flow_needs_drop";
    const IS_CLEARED_ON_MOVE: bool = true;

    fn in_qualifs(qualifs: &ConstQualifs) -> bool {
        qualifs.needs_drop
    }

    fn in_any_value_of_ty(cx: &ConstCx<'_, 'tcx>, ty: Ty<'tcx>) -> bool {
        ty.needs_drop(cx.tcx, cx.param_env)
    }

    fn in_rvalue(
        cx: &ConstCx<'_, 'tcx>,
        per_local: &impl Fn(Local) -> bool,
        rvalue: &Rvalue<'tcx>,
    ) -> bool {
        if let Rvalue::Aggregate(ref kind, _) = *rvalue {
            if let AggregateKind::Adt(def, ..) = **kind {
                if def.has_dtor(cx.tcx) {
                    return true;
                }
            }
        }

        Self::in_rvalue_structurally(cx, per_local, rvalue)
    }
}
<|endoftext|>