<commit_before>// This file is part of rgtk.
//
// rgtk is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// rgtk is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with rgtk.  If not, see <http://www.gnu.org/licenses/>.

#![feature(slicing_syntax)]

extern crate gcc;
extern crate "pkg-config" as pkg_config;

use std::io::process::Command;
use std::path::Path;
use std::os;
use gcc::Config;

// specific os x path, we need to tell to pkgconfig were are pkg config files
#[cfg(target_os="macos")]
fn configure_env() {
    let mut paths = os::getenv_as_bytes("PKG_CONFIG_PATH").map_or(Vec::new(), os::split_paths);
    paths.push(Path::new("/opt/X11/lib/pkgconfig"));
    os::setenv("PKG_CONFIG_PATH", os::join_paths(paths.as_slice()).unwrap());
}

// nothing to do here for now
#[cfg(any(target_os="win32", target_os="linux"))]
fn configure_env() {}

fn main() {
    let out_dir = os::getenv("OUT_DIR").unwrap();

    // export some stuff in the env if its needed
    configure_env();

    // try to find gtk+-3.0 library
    match pkg_config::find_library("gtk+-3.0") {
        Ok(_) => {},
        Err(e) => println!("{}", e)
    };

    // call native pkg-config, there is no way to do this with pkg-config for now
    let cmd = match Command::new("pkg-config").arg("--cflags").arg("gtk+-3.0").output() {
        Ok(r) => r,
        Err(e) => panic!("{}", e)
    };

    // make the vector of path to set to gcc::Config
    let output: String = unsafe { String::from_utf8_unchecked(cmd.output) };
    let res: Vec<&str> = output.split(|c: char| c  == ' ').collect();
    let paths: Vec<Path> = res.iter().filter_map(|s| {
        if s.len() > 1 && s.char_at(1) == 'I' { Some(Path::new(s[2..])) }
        else { None }
    }).collect();

    // build include path
    let gcc_conf = Config {
        include_directories: paths,
        definitions: vec!(),
        objects: vec!()
    };

    // build library
    gcc::compile_library("librgtk_glue.a", &gcc_conf, &["./gtk_glue/gtk_glue.c"]);

    // say to cargo where it is
    println!("cargo:rustc-flags=-L {} -l rgtk_glue:static", out_dir);
}
<commit_msg>Remove X11 pkgconfig path, and allow cross compile so find_library works<commit_after>// This file is part of rgtk.
//
// rgtk is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// rgtk is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with rgtk.  If not, see <http://www.gnu.org/licenses/>.

#![feature(slicing_syntax)]

extern crate gcc;
extern crate "pkg-config" as pkg_config;

use std::io::process::Command;
use std::path::Path;
use std::os;
use gcc::Config;

fn main() {
    let out_dir = os::getenv("OUT_DIR").unwrap();

    os::setenv("PKG_CONFIG_ALLOW_CROSS", "1");

    // try to find gtk+-3.0 library
    match pkg_config::find_library("gtk+-3.0") {
        Ok(_) => {},
        Err(e) => panic!("{}", e)
    };

    // call native pkg-config, there is no way to do this with pkg-config for now
    let cmd = match Command::new("pkg-config").arg("--cflags").arg("gtk+-3.0").output() {
        Ok(r) => r,
        Err(e) => panic!("{}", e)
    };

    // make the vector of path to set to gcc::Config
    let output: String = unsafe { String::from_utf8_unchecked(cmd.output) };
    let res: Vec<&str> = output.split(|c: char| c  == ' ').collect();
    let paths: Vec<Path> = res.iter().filter_map(|s| {
        if s.len() > 1 && s.char_at(1) == 'I' { Some(Path::new(s[2..])) }
        else { None }
    }).collect();

    // build include path
    let gcc_conf = Config {
        include_directories: paths,
        definitions: vec!(),
        objects: vec!()
    };

    // build library
    gcc::compile_library("librgtk_glue.a", &gcc_conf, &["./gtk_glue/gtk_glue.c"]);

    // say to cargo where it is
    println!("cargo:rustc-flags=-L {} -l rgtk_glue:static", out_dir);
}
<|endoftext|>


<commit_before>fn main() {
    for lib in ["HALAthena",
                "wpiutil",
                "FRC_NetworkCommunication",
                "RoboRIO_FRC_ChipObject",
                "NiFpga",
                "NiFpgaLv",
                "niriosession",
                "spi",
                "i2c",
                "visa",
                "NiRioSrv",
                "niriodevenum"]
        .iter() {
        println!("cargo:rustc-link-lib=dylib={}", lib);
    }

    println!("cargo:rustc-link-search=native=ni-libraries");
    println!("cargo:rustc-link-search=native=athena/lib");
}
<commit_msg>Use absolute paths for linking in build script (#14)<commit_after>use std::env;

fn main() {
    for lib in ["HALAthena",
                "wpiutil",
                "FRC_NetworkCommunication",
                "RoboRIO_FRC_ChipObject",
                "NiFpga",
                "NiFpgaLv",
                "niriosession",
                "spi",
                "i2c",
                "visa",
                "NiRioSrv",
                "niriodevenum"]
        .iter() {
        println!("cargo:rustc-link-lib=dylib={}", lib);
    }

    let path = env::current_dir().unwrap();

    println!("cargo:rustc-link-search=native={}/ni-libraries", path.display());
    println!("cargo:rustc-link-search=native={}/athena/lib", path.display());
}
<|endoftext|>


<commit_before>#[cfg(linux)]
mod linux {
    use std::env;
    use std::fs;
    use std::path::PathBuf;

    const LAUNCH_SCRIPT: &str = "anup.sh";

    pub fn run() {
        let profile = env::var("PROFILE").unwrap();

        let mut out_path = PathBuf::from("target");
        out_path.push(profile);
        out_path.push(LAUNCH_SCRIPT);

        fs::copy(LAUNCH_SCRIPT, out_path).unwrap();
    }
}

fn main() {
    #[cfg(linux)]
    linux::run();
}
<commit_msg>[build] Fix build script not running on Linux<commit_after>#[cfg(target_os = "linux")]
mod linux {
    use std::env;
    use std::fs;
    use std::path::PathBuf;

    const LAUNCH_SCRIPT: &str = "anup.sh";

    pub fn run() {
        let profile = env::var("PROFILE").unwrap();

        let mut out_path = PathBuf::from("target");
        out_path.push(profile);
        out_path.push(LAUNCH_SCRIPT);

        fs::copy(LAUNCH_SCRIPT, out_path).unwrap();
    }
}

fn main() {
    #[cfg(target_os = "linux")]
    linux::run();
}
<|endoftext|>


<commit_before>extern crate build;
fn main() {
    build::link("d2d1", true)
}<commit_msg>add shell32 to link dependency<commit_after>extern crate build;
fn main() {
    build::link("shell32", true);
    build::link("d2d1", true);
}<|endoftext|>


<commit_before>use std::fs::File;
use std::io::Write;
use std::path::Path;
use std::env;
use std::string::String;

/// Generate the name of the swizzle function and what it returns.
/// NOTE: This function assumes that variables are in ASCII format
fn gen_swizzle_nth<'a>(variables: &'a str, mut i: usize, upto: usize) -> Option<(String, String)> {
    debug_assert!(i > 0); // zeroth permutation is empty
    let mut swizzle_impl = String::new();
    let mut swizzle = String::new();
    let n = variables.len()+1;
    for _ in 0..upto {
        if i == 0 { break; }
        if i % n == 0 { return None; }
        let c = variables.as_bytes()[i%n - 1] as char;
        swizzle.push(c);
        swizzle_impl.push_str(&format!("self.{}, ", c));
        i = i/n;
    }
    Some((swizzle, swizzle_impl))
}

/// A function that generates swizzle functions as a string.
/// `variables`: swizzle variables (e.g. "xyz")
/// `upto`: largest output vector size (e.g. for `variables = "xy"` and `upto = 4`, `xyxy()` is a
/// valid swizzle operator.
/// NOTE: This function assumes that variables are in ASCII format
fn gen_swizzle_functions(variables: &'static str, upto: usize) -> String {
    let mut result = String::new();
    let nn = (variables.len()+1).pow(upto as u32);
    for i in 1..nn {
        if let Some((swizzle_name, swizzle_impl)) = gen_swizzle_nth(variables, i, upto) {
            let vec_type = format!("$vector_type{}", swizzle_name.len());
            result.push_str(
                &format!("        #[inline] pub fn {0}(&self) -> {2}<$S> {{ {2}::new({1}) }}\n",
                swizzle_name, swizzle_impl, vec_type));
        }
    }
    result
}

/// This script generates the macro for building swizzle operators for multidimensional
/// vectors and points. This macro is included in macros.rs
fn main() {
    // save the file to output directory
    let out_dir = env::var("OUT_DIR").unwrap();
    let swizzle_file_path = Path::new(&out_dir).join("swizzle_operator_macro.rs");

    // This is the string representing the generated macro
    let data = format!(
"/// Generate glm/glsl style swizzle operators
macro_rules! impl_swizzle_functions {{
    ($vector_type1:ident, $vector_type2:ident, $vector_type3:ident, $S:ident, x) => {{
{x3}
    }};
    ($vector_type1:ident, $vector_type2:ident, $vector_type3:ident, $S:ident, xy) => {{
{xy3}
    }};
    ($vector_type1:ident, $vector_type2:ident, $vector_type3:ident, $S:ident, xyz) => {{
{xyz3}
    }};
    ($vector_type1:ident, $vector_type2:ident, $vector_type3:ident, $vector_type4:ident, $S:ident, x) => {{
{x4}
    }};
    ($vector_type1:ident, $vector_type2:ident, $vector_type3:ident, $vector_type4:ident, $S:ident, xy) => {{
{xy4}
    }};
    ($vector_type1:ident, $vector_type2:ident, $vector_type3:ident, $vector_type4:ident, $S:ident, xyz) => {{
{xyz4}
    }};
    ($vector_type1:ident, $vector_type2:ident, $vector_type3:ident, $vector_type4:ident, $S:ident, xyzw) => {{
{xyzw4}
    }};
}}", x3=gen_swizzle_functions("x",3),
     xy3=gen_swizzle_functions("xy",3),
     xyz3=gen_swizzle_functions("xyz",3),
     x4=gen_swizzle_functions("x",4),
     xy4=gen_swizzle_functions("xy",4),
     xyz4=gen_swizzle_functions("xyz",4),
     xyzw4=gen_swizzle_functions("xyzw",4));
    let mut f = File::create(swizzle_file_path)
        .expect("Unable to create file that defines the swizzle operator macro.");
    f.write_all(data.as_bytes()).expect("Unable to write swizzle operator macro.");
}
<commit_msg>Add a doc explanation to each swizzle function.<commit_after>use std::fs::File;
use std::io::Write;
use std::path::Path;
use std::env;
use std::string::String;

/// Generate the name of the swizzle function and what it returns.
/// NOTE: This function assumes that variables are in ASCII format
fn gen_swizzle_nth<'a>(variables: &'a str, mut i: usize, upto: usize) -> Option<(String, String)> {
    debug_assert!(i > 0); // zeroth permutation is empty
    let mut swizzle_impl = String::new();
    let mut swizzle = String::new();
    let n = variables.len()+1;
    for _ in 0..upto {
        if i == 0 { break; }
        if i % n == 0 { return None; }
        let c = variables.as_bytes()[i%n - 1] as char;
        swizzle.push(c);
        swizzle_impl.push_str(&format!("self.{}, ", c));
        i = i/n;
    }
    Some((swizzle, swizzle_impl))
}

/// A function that generates swizzle functions as a string.
/// `variables`: swizzle variables (e.g. "xyz")
/// `upto`: largest output vector size (e.g. for `variables = "xy"` and `upto = 4`, `xyxy()` is a
/// valid swizzle operator.
/// NOTE: This function assumes that variables are in ASCII format
fn gen_swizzle_functions(variables: &'static str, upto: usize) -> String {
    let mut result = String::new();
    let nn = (variables.len()+1).pow(upto as u32);
    for i in 1..nn {
        if let Some((swizzle_name, swizzle_impl)) = gen_swizzle_nth(variables, i, upto) {
            let dim = format!("{}", swizzle_name.len());
            result.push_str(
                &format!("
        /// Swizzle operator that creates a new type with dimension {2} from variables `{0}`.
        #[inline] pub fn {0}(&self) -> $vector_type{2}<$S> {{ $vector_type{2}::new({1}) }}\n",
                swizzle_name, swizzle_impl, dim));
        }
    }
    result
}

/// This script generates the macro for building swizzle operators for multidimensional
/// vectors and points. This macro is included in macros.rs
fn main() {
    // save the file to output directory
    let out_dir = env::var("OUT_DIR").unwrap();
    let swizzle_file_path = Path::new(&out_dir).join("swizzle_operator_macro.rs");

    // This is the string representing the generated macro
    let data = format!(
"/// Generate glm/glsl style swizzle operators
macro_rules! impl_swizzle_functions {{
    ($vector_type1:ident, $vector_type2:ident, $vector_type3:ident, $S:ident, x) => {{
{x3}
    }};
    ($vector_type1:ident, $vector_type2:ident, $vector_type3:ident, $S:ident, xy) => {{
{xy3}
    }};
    ($vector_type1:ident, $vector_type2:ident, $vector_type3:ident, $S:ident, xyz) => {{
{xyz3}
    }};
    ($vector_type1:ident, $vector_type2:ident, $vector_type3:ident, $vector_type4:ident, $S:ident, x) => {{
{x4}
    }};
    ($vector_type1:ident, $vector_type2:ident, $vector_type3:ident, $vector_type4:ident, $S:ident, xy) => {{
{xy4}
    }};
    ($vector_type1:ident, $vector_type2:ident, $vector_type3:ident, $vector_type4:ident, $S:ident, xyz) => {{
{xyz4}
    }};
    ($vector_type1:ident, $vector_type2:ident, $vector_type3:ident, $vector_type4:ident, $S:ident, xyzw) => {{
{xyzw4}
    }};
}}", x3=gen_swizzle_functions("x",3),
     xy3=gen_swizzle_functions("xy",3),
     xyz3=gen_swizzle_functions("xyz",3),
     x4=gen_swizzle_functions("x",4),
     xy4=gen_swizzle_functions("xy",4),
     xyz4=gen_swizzle_functions("xyz",4),
     xyzw4=gen_swizzle_functions("xyzw",4));
    let mut f = File::create(swizzle_file_path)
        .expect("Unable to create file that defines the swizzle operator macro.");
    f.write_all(data.as_bytes()).expect("Unable to write swizzle operator macro.");
}
<|endoftext|>


<commit_before>pub mod options {
  use super::*;

  {{#each protocol.classes as |class| ~}}
  {{#each class.methods as |method| ~}}
  {{#unless method.ignore_args ~}}
  {{#each_argument method.arguments as |argument| ~}}
  {{#unless argument_is_value ~}}
  {{#unless argument.ignore_flags ~}}
  #[derive(Clone, Debug, Default, PartialEq)]
  pub struct {{camel class.name}}{{camel method.name}}Options {
    {{#each argument.flags as |flag| ~}}
    pub {{snake flag.name}}: Boolean,
    {{/each ~}}
  }

  {{/unless ~}}
  {{/unless ~}}
  {{/each_argument ~}}
  {{/unless ~}}
  {{/each ~}}
  {{/each ~}}
}

use options::*;

#[derive(Debug)]
#[allow(clippy::enum_variant_names)]
pub(crate) enum Reply {
  {{#each protocol.classes as |class| ~}}
  {{#each class.methods as |method| ~}}
  {{#if method.c2s ~}}
  {{#if method.synchronous ~}}
  {{camel class.name}}{{camel method.name}}Ok(WaitHandle<{{#if method.metadata.confirmation.type ~}}{{method.metadata.confirmation.type}}{{else}}(){{/if ~}}>{{#each method.metadata.state as |state| ~}}, {{state.type}}{{/each ~}}),
  {{/if ~}}
  {{/if ~}}
  {{/each ~}}
  {{/each ~}}
}

impl Channel {
  pub(crate) fn receive_method(&self, method: AMQPClass) -> Result<()> {
    match method {
      {{#each protocol.classes as |class| ~}}
      {{#each class.methods as |method| ~}}
      {{#unless method.metadata.skip ~}}
      {{#if method.s2c ~}}
      AMQPClass::{{camel class.name}}(protocol::{{snake class.name}}::AMQPMethod::{{camel method.name}}(m)) => self.receive_{{snake class.name false}}_{{snake method.name false}}(m),
      {{/if ~}}
      {{/unless ~}}
      {{/each ~}}
      {{/each ~}}
      m => {
        error!("the client should not receive this method: {:?}", m);
        Err(Error::InvalidMethod(m))
      }
    }
  }

  {{#each protocol.classes as |class| ~}}
  {{#each class.methods as |method| ~}}
  {{#unless method.metadata.skip ~}}
  {{#if method.c2s ~}}
  {{#unless method.metadata.require_wrapper ~}}{{#unless method.is_reply ~}}pub {{#if method.metadata.internal ~}}(crate) {{/if ~}}{{/unless ~}}fn {{else}}fn do_{{/unless ~}}{{snake class.name false}}_{{snake method.name false}}(&self{{#unless method.ignore_args ~}}{{#each_argument method.arguments as |argument| ~}}{{#if argument_is_value ~}}{{#unless argument.force_default ~}}, {{snake argument.name}}: {{#if (use_str_ref argument.type) ~}}&str{{else}}{{argument.type}}{{/if ~}}{{/unless ~}}{{else}}{{#unless argument.ignore_flags ~}}, options: {{camel class.name}}{{camel method.name}}Options{{/unless ~}}{{/if ~}}{{/each_argument ~}}{{/unless ~}}{{#each method.metadata.extra_args as |arg| ~}}, {{arg.name}}: {{arg.type}}{{/each ~}}) -> Confirmation<{{#if method.metadata.confirmation.type ~}}{{method.metadata.confirmation.type}}{{else}}(){{/if ~}}> {
    {{#if method.metadata.channel_init ~}}
    if !self.status.is_initializing() {
    {{else}}
    {{#if method.metadata.channel_deinit ~}}
    if !self.status.is_closing() {
    {{else}}
    if !self.status.is_connected() {
    {{/if ~}}
    {{/if ~}}
      return Confirmation::new_error(Error::NotConnected);
    }

    {{#if method.metadata.start_hook ~}}
    self.before_{{snake class.name false}}_{{snake method.name false}}({{#each method.metadata.start_hook.params as |param| ~}}{{#unless @first ~}}, {{/unless ~}}{{param}}{{/each ~}});
    {{/if ~}}

    {{#unless method.ignore_args ~}}
    {{#each_argument method.arguments as |argument| ~}}
    {{#unless argument_is_value ~}}
    {{#unless argument.ignore_flags ~}}
    let {{camel class.name}}{{camel method.name}}Options {
      {{#each argument.flags as |flag| ~}}
      {{snake flag.name}}{{#if flag.force_default ~}}: _{{/if ~}},
      {{/each ~}}
    } = options;
    {{/unless ~}}
    {{/unless ~}}
    {{/each_argument ~}}
    {{/unless ~}}

    let method = AMQPClass::{{camel class.name}}(protocol::{{snake class.name}}::AMQPMethod::{{camel method.name}} (protocol::{{snake class.name}}::{{camel method.name}} {
      {{#each_argument method.arguments as |argument| ~}}
      {{#if argument_is_value ~}}
      {{#unless argument.force_default ~}}
      {{snake argument.name}}: {{snake argument.name}}{{#if (use_str_ref argument.type) ~}}.into(){{/if ~}},
      {{/unless ~}}
      {{else}}
      {{#unless argument.ignore_flags ~}}
      {{#each argument.flags as |flag| ~}}
      {{#unless flag.force_default ~}}
      {{snake flag.name}},
      {{/unless ~}}
      {{/each ~}}
      {{/unless ~}}
      {{/if ~}}
      {{/each_argument ~}}
    }));

    {{#if method.synchronous ~}}
    let (wait, {{#if method.metadata.bypass_wait_handle ~}}_{{/if ~}}wait_handle) = Wait::new();
    {{/if ~}}
    {{#if method.metadata.carry_headers ~}}
    let send_res = self.send_method_frame_with_body(method, payload, properties);
    {{else}}
    let send_res = self.send_method_frame(method, {{#if method.synchronous ~}}Some((Reply::{{camel class.name}}{{camel method.name}}Ok({{#if method.metadata.bypass_wait_handle ~}}_{{/if ~}}wait_handle.clone(){{#each method.metadata.state as |state| ~}}, {{state.name}}{{#if state.use_str_ref ~}}.into(){{/if ~}}{{/each ~}}), Box::new({{#if method.metadata.bypass_wait_handle ~}}_{{/if ~}}wait_handle))){{else}}None{{/if ~}});
    {{/if ~}}
    if let Err(err) = send_res {
      return Confirmation::new_error(err);
    }
    {{#if method.metadata.end_hook ~}}
    let end_hook_res = self.on_{{snake class.name false}}_{{snake method.name false}}_sent({{#each method.metadata.end_hook.params as |param| ~}}{{#unless @first ~}}, {{/unless ~}}{{param}}{{/each ~}});
    if let Err(err) = end_hook_res {
      return Confirmation::new_error(err);
    }
    {{/if ~}}

    {{#if method.synchronous ~}}
    {{#if (method_has_flag method "nowait") ~}}
    if nowait {
      {{#if method.metadata.nowait_hook ~}}
      if let Err(err) = self.receive_{{snake class.name false}}_{{snake method.name false}}_ok(protocol::{{snake class.name}}::{{camel method.name}}Ok { {{#each method.metadata.nowait_hook.fields as |field| ~}}{{field}}, {{/each ~}}{{#unless method.metadata.nowait_hook.exhaustive_args ~}}..Default::default(){{/unless ~}} }) {
        return Confirmation::new_error(err);
      }
      {{/if ~}}
    }
    {{/if ~}}
    Confirmation::new(wait)
    {{else}}
    Confirmation::new(send_res.unwrap())
    {{/if ~}}
  }
  {{/if ~}}

  {{#if method.s2c ~}}
  {{#if method.is_reply ~}}
  fn receive_{{snake class.name false}}_{{snake method.name false}}(&self, {{#if method.arguments ~}}method{{else}}_{{/if ~}}: protocol::{{snake class.name}}::{{camel method.name}}) -> Result<()> {
    {{#if method.metadata.channel_init ~}}
    if !self.status.is_initializing() {
    {{else}}
    {{#if method.metadata.channel_deinit ~}}
    if !self.status.is_closing() {
    {{else}}
    if !self.status.is_connected() {
    {{/if ~}}
    {{/if ~}}
      return Err(Error::NotConnected);
    }

    match self.connection.next_expected_reply(self.id) {
      Some(Reply::{{camel class.name}}{{camel method.name}}(wait_handle{{#each method.metadata.state as |state| ~}}, {{state.name}}{{/each ~}})) => {
        {{#unless method.metadata.confirmation.type ~}}
        wait_handle.finish(());
        {{/unless ~}}
        {{#if method.arguments ~}}
        self.on_{{snake class.name false}}_{{snake method.name false}}_received(method{{#if method.metadata.confirmation.type ~}}, wait_handle{{/if ~}}{{#each method.metadata.state as |state| ~}}, {{state.name}}{{/each ~}})
        {{else}}
        {{#if method.metadata.received_hook ~}}
        self.on_{{snake class.name false}}_{{snake method.name false}}_received({{#each method.metadata.received_hook.params as |param| ~}}{{#unless @first ~}}, {{/unless ~}}{{param}}{{/each ~}})
        {{else}}
        Ok(())
        {{/if ~}}
        {{/if ~}}
      },
      _ => {
        self.set_error()?;
        Err(Error::UnexpectedReply)
      },
    }
  }
  {{else}}
  fn receive_{{snake class.name false}}_{{snake method.name false}}(&self, method: protocol::{{snake class.name}}::{{camel method.name}}) -> Result<()> {
    if !self.status.is_connected() {
      return Err(Error::NotConnected);
    }
    self.on_{{snake class.name false}}_{{snake method.name false}}_received(method)
  }
  {{/if ~}}
  {{/if ~}}
  {{/unless ~}}
  {{/each ~}}
  {{/each ~}}
}
<commit_msg>channel: mark operation as finished after post receive hooks are run<commit_after>pub mod options {
  use super::*;

  {{#each protocol.classes as |class| ~}}
  {{#each class.methods as |method| ~}}
  {{#unless method.ignore_args ~}}
  {{#each_argument method.arguments as |argument| ~}}
  {{#unless argument_is_value ~}}
  {{#unless argument.ignore_flags ~}}
  #[derive(Clone, Debug, Default, PartialEq)]
  pub struct {{camel class.name}}{{camel method.name}}Options {
    {{#each argument.flags as |flag| ~}}
    pub {{snake flag.name}}: Boolean,
    {{/each ~}}
  }

  {{/unless ~}}
  {{/unless ~}}
  {{/each_argument ~}}
  {{/unless ~}}
  {{/each ~}}
  {{/each ~}}
}

use options::*;

#[derive(Debug)]
#[allow(clippy::enum_variant_names)]
pub(crate) enum Reply {
  {{#each protocol.classes as |class| ~}}
  {{#each class.methods as |method| ~}}
  {{#if method.c2s ~}}
  {{#if method.synchronous ~}}
  {{camel class.name}}{{camel method.name}}Ok(WaitHandle<{{#if method.metadata.confirmation.type ~}}{{method.metadata.confirmation.type}}{{else}}(){{/if ~}}>{{#each method.metadata.state as |state| ~}}, {{state.type}}{{/each ~}}),
  {{/if ~}}
  {{/if ~}}
  {{/each ~}}
  {{/each ~}}
}

impl Channel {
  pub(crate) fn receive_method(&self, method: AMQPClass) -> Result<()> {
    match method {
      {{#each protocol.classes as |class| ~}}
      {{#each class.methods as |method| ~}}
      {{#unless method.metadata.skip ~}}
      {{#if method.s2c ~}}
      AMQPClass::{{camel class.name}}(protocol::{{snake class.name}}::AMQPMethod::{{camel method.name}}(m)) => self.receive_{{snake class.name false}}_{{snake method.name false}}(m),
      {{/if ~}}
      {{/unless ~}}
      {{/each ~}}
      {{/each ~}}
      m => {
        error!("the client should not receive this method: {:?}", m);
        Err(Error::InvalidMethod(m))
      }
    }
  }

  {{#each protocol.classes as |class| ~}}
  {{#each class.methods as |method| ~}}
  {{#unless method.metadata.skip ~}}
  {{#if method.c2s ~}}
  {{#unless method.metadata.require_wrapper ~}}{{#unless method.is_reply ~}}pub {{#if method.metadata.internal ~}}(crate) {{/if ~}}{{/unless ~}}fn {{else}}fn do_{{/unless ~}}{{snake class.name false}}_{{snake method.name false}}(&self{{#unless method.ignore_args ~}}{{#each_argument method.arguments as |argument| ~}}{{#if argument_is_value ~}}{{#unless argument.force_default ~}}, {{snake argument.name}}: {{#if (use_str_ref argument.type) ~}}&str{{else}}{{argument.type}}{{/if ~}}{{/unless ~}}{{else}}{{#unless argument.ignore_flags ~}}, options: {{camel class.name}}{{camel method.name}}Options{{/unless ~}}{{/if ~}}{{/each_argument ~}}{{/unless ~}}{{#each method.metadata.extra_args as |arg| ~}}, {{arg.name}}: {{arg.type}}{{/each ~}}) -> Confirmation<{{#if method.metadata.confirmation.type ~}}{{method.metadata.confirmation.type}}{{else}}(){{/if ~}}> {
    {{#if method.metadata.channel_init ~}}
    if !self.status.is_initializing() {
    {{else}}
    {{#if method.metadata.channel_deinit ~}}
    if !self.status.is_closing() {
    {{else}}
    if !self.status.is_connected() {
    {{/if ~}}
    {{/if ~}}
      return Confirmation::new_error(Error::NotConnected);
    }

    {{#if method.metadata.start_hook ~}}
    self.before_{{snake class.name false}}_{{snake method.name false}}({{#each method.metadata.start_hook.params as |param| ~}}{{#unless @first ~}}, {{/unless ~}}{{param}}{{/each ~}});
    {{/if ~}}

    {{#unless method.ignore_args ~}}
    {{#each_argument method.arguments as |argument| ~}}
    {{#unless argument_is_value ~}}
    {{#unless argument.ignore_flags ~}}
    let {{camel class.name}}{{camel method.name}}Options {
      {{#each argument.flags as |flag| ~}}
      {{snake flag.name}}{{#if flag.force_default ~}}: _{{/if ~}},
      {{/each ~}}
    } = options;
    {{/unless ~}}
    {{/unless ~}}
    {{/each_argument ~}}
    {{/unless ~}}

    let method = AMQPClass::{{camel class.name}}(protocol::{{snake class.name}}::AMQPMethod::{{camel method.name}} (protocol::{{snake class.name}}::{{camel method.name}} {
      {{#each_argument method.arguments as |argument| ~}}
      {{#if argument_is_value ~}}
      {{#unless argument.force_default ~}}
      {{snake argument.name}}: {{snake argument.name}}{{#if (use_str_ref argument.type) ~}}.into(){{/if ~}},
      {{/unless ~}}
      {{else}}
      {{#unless argument.ignore_flags ~}}
      {{#each argument.flags as |flag| ~}}
      {{#unless flag.force_default ~}}
      {{snake flag.name}},
      {{/unless ~}}
      {{/each ~}}
      {{/unless ~}}
      {{/if ~}}
      {{/each_argument ~}}
    }));

    {{#if method.synchronous ~}}
    let (wait, {{#if method.metadata.bypass_wait_handle ~}}_{{/if ~}}wait_handle) = Wait::new();
    {{/if ~}}
    {{#if method.metadata.carry_headers ~}}
    let send_res = self.send_method_frame_with_body(method, payload, properties);
    {{else}}
    let send_res = self.send_method_frame(method, {{#if method.synchronous ~}}Some((Reply::{{camel class.name}}{{camel method.name}}Ok({{#if method.metadata.bypass_wait_handle ~}}_{{/if ~}}wait_handle.clone(){{#each method.metadata.state as |state| ~}}, {{state.name}}{{#if state.use_str_ref ~}}.into(){{/if ~}}{{/each ~}}), Box::new({{#if method.metadata.bypass_wait_handle ~}}_{{/if ~}}wait_handle))){{else}}None{{/if ~}});
    {{/if ~}}
    if let Err(err) = send_res {
      return Confirmation::new_error(err);
    }
    {{#if method.metadata.end_hook ~}}
    let end_hook_res = self.on_{{snake class.name false}}_{{snake method.name false}}_sent({{#each method.metadata.end_hook.params as |param| ~}}{{#unless @first ~}}, {{/unless ~}}{{param}}{{/each ~}});
    if let Err(err) = end_hook_res {
      return Confirmation::new_error(err);
    }
    {{/if ~}}

    {{#if method.synchronous ~}}
    {{#if (method_has_flag method "nowait") ~}}
    if nowait {
      {{#if method.metadata.nowait_hook ~}}
      if let Err(err) = self.receive_{{snake class.name false}}_{{snake method.name false}}_ok(protocol::{{snake class.name}}::{{camel method.name}}Ok { {{#each method.metadata.nowait_hook.fields as |field| ~}}{{field}}, {{/each ~}}{{#unless method.metadata.nowait_hook.exhaustive_args ~}}..Default::default(){{/unless ~}} }) {
        return Confirmation::new_error(err);
      }
      {{/if ~}}
    }
    {{/if ~}}
    Confirmation::new(wait)
    {{else}}
    Confirmation::new(send_res.unwrap())
    {{/if ~}}
  }
  {{/if ~}}

  {{#if method.s2c ~}}
  {{#if method.is_reply ~}}
  fn receive_{{snake class.name false}}_{{snake method.name false}}(&self, {{#if method.arguments ~}}method{{else}}_{{/if ~}}: protocol::{{snake class.name}}::{{camel method.name}}) -> Result<()> {
    {{#if method.metadata.channel_init ~}}
    if !self.status.is_initializing() {
    {{else}}
    {{#if method.metadata.channel_deinit ~}}
    if !self.status.is_closing() {
    {{else}}
    if !self.status.is_connected() {
    {{/if ~}}
    {{/if ~}}
      return Err(Error::NotConnected);
    }

    match self.connection.next_expected_reply(self.id) {
      Some(Reply::{{camel class.name}}{{camel method.name}}(wait_handle{{#each method.metadata.state as |state| ~}}, {{state.name}}{{/each ~}})) => {
        {{#if method.arguments ~}}
        let res = self.on_{{snake class.name false}}_{{snake method.name false}}_received(method{{#if method.metadata.confirmation.type ~}}, wait_handle{{/if ~}}{{#each method.metadata.state as |state| ~}}, {{state.name}}{{/each ~}});
        {{else}}
        {{#if method.metadata.received_hook ~}}
        let res = self.on_{{snake class.name false}}_{{snake method.name false}}_received({{#each method.metadata.received_hook.params as |param| ~}}{{#unless @first ~}}, {{/unless ~}}{{param}}{{/each ~}});
        {{else}}
        let res = Ok(());
        {{/if ~}}
        {{/if ~}}
        {{#unless method.metadata.confirmation.type ~}}
        wait_handle.finish(());
        {{/unless ~}}
        res
      },
      _ => {
        self.set_error()?;
        Err(Error::UnexpectedReply)
      },
    }
  }
  {{else}}
  fn receive_{{snake class.name false}}_{{snake method.name false}}(&self, method: protocol::{{snake class.name}}::{{camel method.name}}) -> Result<()> {
    if !self.status.is_connected() {
      return Err(Error::NotConnected);
    }
    self.on_{{snake class.name false}}_{{snake method.name false}}_received(method)
  }
  {{/if ~}}
  {{/if ~}}
  {{/unless ~}}
  {{/each ~}}
  {{/each ~}}
}
<|endoftext|>


<commit_before>#![feature(test)]

extern crate hdrsample;
extern crate rand;
extern crate test;

use hdrsample::*;
use hdrsample::serialization::*;
use self::rand::distributions::range::Range;
use self::rand::distributions::IndependentSample;
use self::test::Bencher;
use std::io::Cursor;

#[bench]
fn serialize_tiny_dense(b: &mut Bencher) {
    // 256 + 3 * 128 = 640 counts
    do_serialize_bench(b, 1, 2047, 2, 640 * 3 / 2)
}

#[bench]
fn serialize_tiny_sparse(b: &mut Bencher) {
    // 256 + 3 * 128 = 640 counts
    do_serialize_bench(b, 1, 2047, 2, 640 / 10)
}

#[bench]
fn serialize_small_dense(b: &mut Bencher) {
    // 2048 counts
    do_serialize_bench(b, 1, 2047, 3, 2048 * 3 / 2)
}

#[bench]
fn serialize_small_sparse(b: &mut Bencher) {
    // 2048 counts
    do_serialize_bench(b, 1, 2047, 3, 2048 / 10)
}

#[bench]
fn serialize_medium_dense(b: &mut Bencher) {
    // 56320 counts
    do_serialize_bench(b, 1, u64::max_value(), 3, 56320 * 3 / 2)
}

#[bench]
fn serialize_medium_sparse(b: &mut Bencher) {
    // 56320 counts
    do_serialize_bench(b, 1, u64::max_value(), 3, 56320 / 10)
}

#[bench]
fn serialize_large_dense(b: &mut Bencher) {
    // 6291456 buckets
    do_serialize_bench(b, 1, u64::max_value(), 5, 6291456 * 3 / 2)
}

#[bench]
fn serialize_large_sparse(b: &mut Bencher) {
    // 6291456 buckets
    do_serialize_bench(b, 1, u64::max_value(), 5, 6291456 / 10)
}

#[bench]
fn deserialize_tiny_dense(b: &mut Bencher) {
    // 256 + 3 * 128 = 640 counts
    do_deserialize_bench(b, 1, 2047, 2, 640 * 3 / 2)
}

#[bench]
fn deserialize_tiny_sparse(b: &mut Bencher) {
    // 256 + 3 * 128 = 640 counts
    do_deserialize_bench(b, 1, 2047, 2, 640 / 10)
}

#[bench]
fn deserialize_small_dense(b: &mut Bencher) {
    // 2048 counts
    do_deserialize_bench(b, 1, 2047, 3, 2048 * 3 / 2)
}

#[bench]
fn deserialize_small_sparse(b: &mut Bencher) {
    // 2048 counts
    do_deserialize_bench(b, 1, 2047, 3, 2048 / 10)
}

#[bench]
fn deserialize_medium_dense(b: &mut Bencher) {
    // 56320 counts
    do_deserialize_bench(b, 1, u64::max_value(), 3, 56320 * 3 / 2)
}

#[bench]
fn deserialize_medium_sparse(b: &mut Bencher) {
    // 56320 counts
    do_deserialize_bench(b, 1, u64::max_value(), 3, 56320 / 10)
}

#[bench]
fn deserialize_large_dense(b: &mut Bencher) {
    // 6291456 buckets
    do_deserialize_bench(b, 1, u64::max_value(), 5, 6291456 * 3 / 2)
}

#[bench]
fn deserialize_large_sparse(b: &mut Bencher) {
    // 6291456 buckets
    do_deserialize_bench(b, 1, u64::max_value(), 5, 6291456 / 10)
}

fn do_serialize_bench(b: &mut Bencher, low: u64, high: u64, digits: u8, random_counts: usize) {
    let mut s = V2Serializer::new();
    let mut vec = Vec::with_capacity(random_counts);

    let range = Range::new(low, high);

    let mut h = Histogram::<u64>::new_with_bounds(low, high, digits).unwrap();

    let mut rng = rand::weak_rng();
    for _ in 0..random_counts {
        h.record(range.ind_sample(&mut rng)).unwrap();
    };

    b.iter(|| {
        vec.clear();

        let _ = s.serialize(&h, &mut vec).unwrap();
    });
}

fn do_deserialize_bench(b: &mut Bencher, low: u64, high: u64, digits: u8, random_counts: usize) {
    let mut s = V2Serializer::new();
    let mut vec = Vec::with_capacity(random_counts);

    let range = Range::new(low, high);

    let mut h = Histogram::<u64>::new_with_bounds(low, high, digits).unwrap();

    let mut rng = rand::weak_rng();
    for _ in 0..random_counts {
        h.record(range.ind_sample(&mut rng)).unwrap();
    };

    let _ = s.serialize(&h, &mut vec).unwrap();

    let mut d = Deserializer::new();
    b.iter(|| {
        let mut cursor = Cursor::new(&vec);
        let _: Histogram<u64> = d.deserialize(&mut cursor).unwrap();
    });
}
<commit_msg>Make serialization benchmark sparseness easier to understand<commit_after>#![feature(test)]

extern crate hdrsample;
extern crate rand;
extern crate test;

use hdrsample::*;
use hdrsample::serialization::*;
use self::rand::distributions::range::Range;
use self::rand::distributions::IndependentSample;
use self::test::Bencher;
use std::io::Cursor;

#[bench]
fn serialize_tiny_dense(b: &mut Bencher) {
    // 256 + 3 * 128 = 640 counts
    do_serialize_bench(b, 1, 2047, 2, 1.5)
}

#[bench]
fn serialize_tiny_sparse(b: &mut Bencher) {
    // 256 + 3 * 128 = 640 counts
    do_serialize_bench(b, 1, 2047, 2, 0.1)
}

#[bench]
fn serialize_small_dense(b: &mut Bencher) {
    // 2048 counts
    do_serialize_bench(b, 1, 2047, 3, 1.5)
}

#[bench]
fn serialize_small_sparse(b: &mut Bencher) {
    // 2048 counts
    do_serialize_bench(b, 1, 2047, 3, 0.1)
}

#[bench]
fn serialize_medium_dense(b: &mut Bencher) {
    // 56320 counts
    do_serialize_bench(b, 1, u64::max_value(), 3, 1.5)
}

#[bench]
fn serialize_medium_sparse(b: &mut Bencher) {
    // 56320 counts
    do_serialize_bench(b, 1, u64::max_value(), 3, 0.1)
}

#[bench]
fn serialize_large_dense(b: &mut Bencher) {
    // 6291456 buckets
    do_serialize_bench(b, 1, u64::max_value(), 5, 1.5)
}

#[bench]
fn serialize_large_sparse(b: &mut Bencher) {
    // 6291456 buckets
    do_serialize_bench(b, 1, u64::max_value(), 5, 0.1)
}

#[bench]
fn deserialize_tiny_dense(b: &mut Bencher) {
    // 256 + 3 * 128 = 640 counts
    do_deserialize_bench(b, 1, 2047, 2, 1.5)
}

#[bench]
fn deserialize_tiny_sparse(b: &mut Bencher) {
    // 256 + 3 * 128 = 640 counts
    do_deserialize_bench(b, 1, 2047, 2, 0.1)
}

#[bench]
fn deserialize_small_dense(b: &mut Bencher) {
    // 2048 counts
    do_deserialize_bench(b, 1, 2047, 3, 1.5)
}

#[bench]
fn deserialize_small_sparse(b: &mut Bencher) {
    // 2048 counts
    do_deserialize_bench(b, 1, 2047, 3, 0.1)
}

#[bench]
fn deserialize_medium_dense(b: &mut Bencher) {
    // 56320 counts
    do_deserialize_bench(b, 1, u64::max_value(), 3, 1.5)
}

#[bench]
fn deserialize_medium_sparse(b: &mut Bencher) {
    // 56320 counts
    do_deserialize_bench(b, 1, u64::max_value(), 3, 0.1)
}

#[bench]
fn deserialize_large_dense(b: &mut Bencher) {
    // 6291456 buckets
    do_deserialize_bench(b, 1, u64::max_value(), 5, 1.5)
}

#[bench]
fn deserialize_large_sparse(b: &mut Bencher) {
    // 6291456 buckets
    do_deserialize_bench(b, 1, u64::max_value(), 5, 0.1)
}

fn do_serialize_bench(b: &mut Bencher, low: u64, high: u64, digits: u8, fraction_of_counts_len: f64) {
    let mut s = V2Serializer::new();
    let mut h = Histogram::<u64>::new_with_bounds(low, high, digits).unwrap();
    let random_counts = (fraction_of_counts_len * h.len() as f64) as usize;
    let mut vec = Vec::with_capacity(random_counts);

    let range = Range::new(low, high);

    let mut rng = rand::weak_rng();
    for _ in 0..random_counts {
        h.record(range.ind_sample(&mut rng)).unwrap();
    };

    b.iter(|| {
        vec.clear();

        let _ = s.serialize(&h, &mut vec).unwrap();
    });
}

fn do_deserialize_bench(b: &mut Bencher, low: u64, high: u64, digits: u8, fraction_of_counts_len: f64) {
    let mut s = V2Serializer::new();
    let mut h = Histogram::<u64>::new_with_bounds(low, high, digits).unwrap();
    let random_counts = (fraction_of_counts_len * h.len() as f64) as usize;
    let mut vec = Vec::with_capacity(random_counts);

    let range = Range::new(low, high);

    let mut rng = rand::weak_rng();
    for _ in 0..random_counts {
        h.record(range.ind_sample(&mut rng)).unwrap();
    };

    let _ = s.serialize(&h, &mut vec).unwrap();

    let mut d = Deserializer::new();
    b.iter(|| {
        let mut cursor = Cursor::new(&vec);
        let _: Histogram<u64> = d.deserialize(&mut cursor).unwrap();
    });
}
<|endoftext|>


<commit_before>use crate::utils::{implements_trait, is_copy, multispan_sugg, snippet, span_lint, span_lint_and_then, SpanlessEq};
use rustc::hir::*;
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
use rustc::{declare_lint_pass, declare_tool_lint};
use rustc_errors::Applicability;

declare_clippy_lint! {
    /// **What it does:** Checks for equal operands to comparison, logical and
    /// bitwise, difference and division binary operators (`==`, `>`, etc., `&&`,
    /// `||`, `&`, `|`, `^`, `-` and `/`).
    ///
    /// **Why is this bad?** This is usually just a typo or a copy and paste error.
    ///
    /// **Known problems:** False negatives: We had some false positives regarding
    /// calls (notably [racer](https://github.com/phildawes/racer) had one instance
    /// of `x.pop() && x.pop()`), so we removed matching any function or method
    /// calls. We may introduce a whitelist of known pure functions in the future.
    ///
    /// **Example:**
    /// ```rust
    /// # let x = 1;
    /// if x + 1 == x + 1 {}
    /// ```
    pub EQ_OP,
    correctness,
    "equal operands on both sides of a comparison or bitwise combination (e.g., `x == x`)"
}

declare_clippy_lint! {
    /// **What it does:** Checks for arguments to `==` which have their address
    /// taken to satisfy a bound
    /// and suggests to dereference the other argument instead
    ///
    /// **Why is this bad?** It is more idiomatic to dereference the other argument.
    ///
    /// **Known problems:** None
    ///
    /// **Example:**
    /// ```ignore
    /// &x == y
    /// ```
    pub OP_REF,
    style,
    "taking a reference to satisfy the type constraints on `==`"
}

declare_lint_pass!(EqOp => [EQ_OP, OP_REF]);

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for EqOp {
    #[allow(clippy::similar_names, clippy::too_many_lines)]
    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr) {
        if let ExprKind::Binary(op, ref left, ref right) = e.kind {
            if e.span.from_expansion() {
                return;
            }
            if is_valid_operator(op) && SpanlessEq::new(cx).ignore_fn().eq_expr(left, right) {
                span_lint(
                    cx,
                    EQ_OP,
                    e.span,
                    &format!("equal expressions as operands to `{}`", op.node.as_str()),
                );
                return;
            }
            let (trait_id, requires_ref) = match op.node {
                BinOpKind::Add => (cx.tcx.lang_items().add_trait(), false),
                BinOpKind::Sub => (cx.tcx.lang_items().sub_trait(), false),
                BinOpKind::Mul => (cx.tcx.lang_items().mul_trait(), false),
                BinOpKind::Div => (cx.tcx.lang_items().div_trait(), false),
                BinOpKind::Rem => (cx.tcx.lang_items().rem_trait(), false),
                // don't lint short circuiting ops
                BinOpKind::And | BinOpKind::Or => return,
                BinOpKind::BitXor => (cx.tcx.lang_items().bitxor_trait(), false),
                BinOpKind::BitAnd => (cx.tcx.lang_items().bitand_trait(), false),
                BinOpKind::BitOr => (cx.tcx.lang_items().bitor_trait(), false),
                BinOpKind::Shl => (cx.tcx.lang_items().shl_trait(), false),
                BinOpKind::Shr => (cx.tcx.lang_items().shr_trait(), false),
                BinOpKind::Ne | BinOpKind::Eq => (cx.tcx.lang_items().eq_trait(), true),
                BinOpKind::Lt | BinOpKind::Le | BinOpKind::Ge | BinOpKind::Gt => {
                    (cx.tcx.lang_items().ord_trait(), true)
                },
            };
            if let Some(trait_id) = trait_id {
                #[allow(clippy::match_same_arms)]
                match (&left.kind, &right.kind) {
                    // do not suggest to dereference literals
                    (&ExprKind::Lit(..), _) | (_, &ExprKind::Lit(..)) => {},
                    // &foo == &bar
                    (&ExprKind::AddrOf(BorrowKind::Ref, _, ref l), &ExprKind::AddrOf(BorrowKind::Ref, _, ref r)) => {
                        let lty = cx.tables.expr_ty(l);
                        let rty = cx.tables.expr_ty(r);
                        let lcpy = is_copy(cx, lty);
                        let rcpy = is_copy(cx, rty);
                        // either operator autorefs or both args are copyable
                        if (requires_ref || (lcpy && rcpy)) && implements_trait(cx, lty, trait_id, &[rty.into()]) {
                            span_lint_and_then(
                                cx,
                                OP_REF,
                                e.span,
                                "needlessly taken reference of both operands",
                                |db| {
                                    let lsnip = snippet(cx, l.span, "...").to_string();
                                    let rsnip = snippet(cx, r.span, "...").to_string();
                                    multispan_sugg(
                                        db,
                                        "use the values directly".to_string(),
                                        vec![(left.span, lsnip), (right.span, rsnip)],
                                    );
                                },
                            )
                        } else if lcpy
                            && !rcpy
                            && implements_trait(cx, lty, trait_id, &[cx.tables.expr_ty(right).into()])
                        {
                            span_lint_and_then(cx, OP_REF, e.span, "needlessly taken reference of left operand", |db| {
                                let lsnip = snippet(cx, l.span, "...").to_string();
                                db.span_suggestion(
                                    left.span,
                                    "use the left value directly",
                                    lsnip,
                                    Applicability::MaybeIncorrect, // FIXME #2597
                                );
                            })
                        } else if !lcpy
                            && rcpy
                            && implements_trait(cx, cx.tables.expr_ty(left), trait_id, &[rty.into()])
                        {
                            span_lint_and_then(
                                cx,
                                OP_REF,
                                e.span,
                                "needlessly taken reference of right operand",
                                |db| {
                                    let rsnip = snippet(cx, r.span, "...").to_string();
                                    db.span_suggestion(
                                        right.span,
                                        "use the right value directly",
                                        rsnip,
                                        Applicability::MaybeIncorrect, // FIXME #2597
                                    );
                                },
                            )
                        }
                    },
                    // &foo == bar
                    (&ExprKind::AddrOf(BorrowKind::Ref, _, ref l), _) => {
                        let lty = cx.tables.expr_ty(l);
                        let lcpy = is_copy(cx, lty);
                        if (requires_ref || lcpy)
                            && implements_trait(cx, lty, trait_id, &[cx.tables.expr_ty(right).into()])
                        {
                            span_lint_and_then(cx, OP_REF, e.span, "needlessly taken reference of left operand", |db| {
                                let lsnip = snippet(cx, l.span, "...").to_string();
                                db.span_suggestion(
                                    left.span,
                                    "use the left value directly",
                                    lsnip,
                                    Applicability::MachineApplicable, // snippet
                                );
                            })
                        }
                    },
                    // foo == &bar
                    (_, &ExprKind::AddrOf(BorrowKind::Ref, _, ref r)) => {
                        let rty = cx.tables.expr_ty(r);
                        let rcpy = is_copy(cx, rty);
                        if (requires_ref || rcpy)
                            && implements_trait(cx, cx.tables.expr_ty(left), trait_id, &[rty.into()])
                        {
                            span_lint_and_then(cx, OP_REF, e.span, "taken reference of right operand", |db| {
                                let rsnip = snippet(cx, r.span, "...").to_string();
                                db.span_suggestion(
                                    right.span,
                                    "use the right value directly",
                                    rsnip,
                                    Applicability::MachineApplicable, // snippet
                                );
                            })
                        }
                    },
                    _ => {},
                }
            }
        }
    }
}

fn is_valid_operator(op: BinOp) -> bool {
    match op.node {
        BinOpKind::Sub
        | BinOpKind::Div
        | BinOpKind::Eq
        | BinOpKind::Lt
        | BinOpKind::Le
        | BinOpKind::Gt
        | BinOpKind::Ge
        | BinOpKind::Ne
        | BinOpKind::And
        | BinOpKind::Or
        | BinOpKind::BitXor
        | BinOpKind::BitAnd
        | BinOpKind::BitOr => true,
        _ => false,
    }
}
<commit_msg>Fire clippy::op_ref on PartialOrd but !Ord types<commit_after>use crate::utils::{implements_trait, is_copy, multispan_sugg, snippet, span_lint, span_lint_and_then, SpanlessEq};
use rustc::hir::*;
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
use rustc::{declare_lint_pass, declare_tool_lint};
use rustc_errors::Applicability;

declare_clippy_lint! {
    /// **What it does:** Checks for equal operands to comparison, logical and
    /// bitwise, difference and division binary operators (`==`, `>`, etc., `&&`,
    /// `||`, `&`, `|`, `^`, `-` and `/`).
    ///
    /// **Why is this bad?** This is usually just a typo or a copy and paste error.
    ///
    /// **Known problems:** False negatives: We had some false positives regarding
    /// calls (notably [racer](https://github.com/phildawes/racer) had one instance
    /// of `x.pop() && x.pop()`), so we removed matching any function or method
    /// calls. We may introduce a whitelist of known pure functions in the future.
    ///
    /// **Example:**
    /// ```rust
    /// # let x = 1;
    /// if x + 1 == x + 1 {}
    /// ```
    pub EQ_OP,
    correctness,
    "equal operands on both sides of a comparison or bitwise combination (e.g., `x == x`)"
}

declare_clippy_lint! {
    /// **What it does:** Checks for arguments to `==` which have their address
    /// taken to satisfy a bound
    /// and suggests to dereference the other argument instead
    ///
    /// **Why is this bad?** It is more idiomatic to dereference the other argument.
    ///
    /// **Known problems:** None
    ///
    /// **Example:**
    /// ```ignore
    /// &x == y
    /// ```
    pub OP_REF,
    style,
    "taking a reference to satisfy the type constraints on `==`"
}

declare_lint_pass!(EqOp => [EQ_OP, OP_REF]);

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for EqOp {
    #[allow(clippy::similar_names, clippy::too_many_lines)]
    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, e: &'tcx Expr) {
        if let ExprKind::Binary(op, ref left, ref right) = e.kind {
            if e.span.from_expansion() {
                return;
            }
            if is_valid_operator(op) && SpanlessEq::new(cx).ignore_fn().eq_expr(left, right) {
                span_lint(
                    cx,
                    EQ_OP,
                    e.span,
                    &format!("equal expressions as operands to `{}`", op.node.as_str()),
                );
                return;
            }
            let (trait_id, requires_ref) = match op.node {
                BinOpKind::Add => (cx.tcx.lang_items().add_trait(), false),
                BinOpKind::Sub => (cx.tcx.lang_items().sub_trait(), false),
                BinOpKind::Mul => (cx.tcx.lang_items().mul_trait(), false),
                BinOpKind::Div => (cx.tcx.lang_items().div_trait(), false),
                BinOpKind::Rem => (cx.tcx.lang_items().rem_trait(), false),
                // don't lint short circuiting ops
                BinOpKind::And | BinOpKind::Or => return,
                BinOpKind::BitXor => (cx.tcx.lang_items().bitxor_trait(), false),
                BinOpKind::BitAnd => (cx.tcx.lang_items().bitand_trait(), false),
                BinOpKind::BitOr => (cx.tcx.lang_items().bitor_trait(), false),
                BinOpKind::Shl => (cx.tcx.lang_items().shl_trait(), false),
                BinOpKind::Shr => (cx.tcx.lang_items().shr_trait(), false),
                BinOpKind::Ne | BinOpKind::Eq => (cx.tcx.lang_items().eq_trait(), true),
                BinOpKind::Lt | BinOpKind::Le | BinOpKind::Ge | BinOpKind::Gt => {
                    (cx.tcx.lang_items().partial_ord_trait(), true)
                },
            };
            if let Some(trait_id) = trait_id {
                #[allow(clippy::match_same_arms)]
                match (&left.kind, &right.kind) {
                    // do not suggest to dereference literals
                    (&ExprKind::Lit(..), _) | (_, &ExprKind::Lit(..)) => {},
                    // &foo == &bar
                    (&ExprKind::AddrOf(BorrowKind::Ref, _, ref l), &ExprKind::AddrOf(BorrowKind::Ref, _, ref r)) => {
                        let lty = cx.tables.expr_ty(l);
                        let rty = cx.tables.expr_ty(r);
                        let lcpy = is_copy(cx, lty);
                        let rcpy = is_copy(cx, rty);
                        // either operator autorefs or both args are copyable
                        if (requires_ref || (lcpy && rcpy)) && implements_trait(cx, lty, trait_id, &[rty.into()]) {
                            span_lint_and_then(
                                cx,
                                OP_REF,
                                e.span,
                                "needlessly taken reference of both operands",
                                |db| {
                                    let lsnip = snippet(cx, l.span, "...").to_string();
                                    let rsnip = snippet(cx, r.span, "...").to_string();
                                    multispan_sugg(
                                        db,
                                        "use the values directly".to_string(),
                                        vec![(left.span, lsnip), (right.span, rsnip)],
                                    );
                                },
                            )
                        } else if lcpy
                            && !rcpy
                            && implements_trait(cx, lty, trait_id, &[cx.tables.expr_ty(right).into()])
                        {
                            span_lint_and_then(cx, OP_REF, e.span, "needlessly taken reference of left operand", |db| {
                                let lsnip = snippet(cx, l.span, "...").to_string();
                                db.span_suggestion(
                                    left.span,
                                    "use the left value directly",
                                    lsnip,
                                    Applicability::MaybeIncorrect, // FIXME #2597
                                );
                            })
                        } else if !lcpy
                            && rcpy
                            && implements_trait(cx, cx.tables.expr_ty(left), trait_id, &[rty.into()])
                        {
                            span_lint_and_then(
                                cx,
                                OP_REF,
                                e.span,
                                "needlessly taken reference of right operand",
                                |db| {
                                    let rsnip = snippet(cx, r.span, "...").to_string();
                                    db.span_suggestion(
                                        right.span,
                                        "use the right value directly",
                                        rsnip,
                                        Applicability::MaybeIncorrect, // FIXME #2597
                                    );
                                },
                            )
                        }
                    },
                    // &foo == bar
                    (&ExprKind::AddrOf(BorrowKind::Ref, _, ref l), _) => {
                        let lty = cx.tables.expr_ty(l);
                        let lcpy = is_copy(cx, lty);
                        if (requires_ref || lcpy)
                            && implements_trait(cx, lty, trait_id, &[cx.tables.expr_ty(right).into()])
                        {
                            span_lint_and_then(cx, OP_REF, e.span, "needlessly taken reference of left operand", |db| {
                                let lsnip = snippet(cx, l.span, "...").to_string();
                                db.span_suggestion(
                                    left.span,
                                    "use the left value directly",
                                    lsnip,
                                    Applicability::MachineApplicable, // snippet
                                );
                            })
                        }
                    },
                    // foo == &bar
                    (_, &ExprKind::AddrOf(BorrowKind::Ref, _, ref r)) => {
                        let rty = cx.tables.expr_ty(r);
                        let rcpy = is_copy(cx, rty);
                        if (requires_ref || rcpy)
                            && implements_trait(cx, cx.tables.expr_ty(left), trait_id, &[rty.into()])
                        {
                            span_lint_and_then(cx, OP_REF, e.span, "taken reference of right operand", |db| {
                                let rsnip = snippet(cx, r.span, "...").to_string();
                                db.span_suggestion(
                                    right.span,
                                    "use the right value directly",
                                    rsnip,
                                    Applicability::MachineApplicable, // snippet
                                );
                            })
                        }
                    },
                    _ => {},
                }
            }
        }
    }
}

fn is_valid_operator(op: BinOp) -> bool {
    match op.node {
        BinOpKind::Sub
        | BinOpKind::Div
        | BinOpKind::Eq
        | BinOpKind::Lt
        | BinOpKind::Le
        | BinOpKind::Gt
        | BinOpKind::Ge
        | BinOpKind::Ne
        | BinOpKind::And
        | BinOpKind::Or
        | BinOpKind::BitXor
        | BinOpKind::BitAnd
        | BinOpKind::BitOr => true,
        _ => false,
    }
}
<|endoftext|>


<commit_before>//! Loads "sysroot" crate.
//!
//! One confusing point here is that normally sysroot is a bunch of `.rlib`s,
//! but we can't process `.rlib` and need source code instead. The source code
//! is typically installed with `rustup component add rust-src` command.

use std::{convert::TryFrom, env, ops, path::PathBuf, process::Command};

use anyhow::{format_err, Result};
use la_arena::{Arena, Idx};
use paths::{AbsPath, AbsPathBuf};

use crate::utf8_stdout;

#[derive(Default, Debug, Clone, Eq, PartialEq)]
pub struct Sysroot {
    crates: Arena<SysrootCrateData>,
}

pub(crate) type SysrootCrate = Idx<SysrootCrateData>;

#[derive(Debug, Clone, Eq, PartialEq)]
pub struct SysrootCrateData {
    pub name: String,
    pub root: AbsPathBuf,
    pub deps: Vec<SysrootCrate>,
}

impl ops::Index<SysrootCrate> for Sysroot {
    type Output = SysrootCrateData;
    fn index(&self, index: SysrootCrate) -> &SysrootCrateData {
        &self.crates[index]
    }
}

impl Sysroot {
    pub fn public_deps(&self) -> impl Iterator<Item = (&'static str, SysrootCrate)> + '_ {
        // core is added as a dependency before std in order to
        // mimic rustcs dependency order
        ["core", "alloc", "std"].iter().filter_map(move |&it| Some((it, self.by_name(it)?)))
    }

    pub fn proc_macro(&self) -> Option<SysrootCrate> {
        self.by_name("proc_macro")
    }

    pub fn crates<'a>(&'a self) -> impl Iterator<Item = SysrootCrate> + ExactSizeIterator + 'a {
        self.crates.iter().map(|(id, _data)| id)
    }

    pub fn discover(cargo_toml: &AbsPath) -> Result<Sysroot> {
        log::debug!("Discovering sysroot for {}", cargo_toml.display());
        let current_dir = cargo_toml.parent().ok_or_else(|| {
            format_err!("Failed to find the parent directory for file {:?}", cargo_toml)
        })?;
        let sysroot_dir = discover_sysroot_dir(current_dir)?;
        let sysroot_src_dir = discover_sysroot_src_dir(&sysroot_dir, current_dir)?;
        let res = Sysroot::load(&sysroot_src_dir)?;
        Ok(res)
    }

    pub fn discover_rustc(cargo_toml: &AbsPath) -> Option<AbsPathBuf> {
        log::debug!("Discovering rustc source for {}", cargo_toml.display());
        let current_dir = cargo_toml.parent().unwrap();
        discover_sysroot_dir(current_dir).ok().and_then(|sysroot_dir| get_rustc_src(&sysroot_dir))
    }

    pub fn load(sysroot_src_dir: &AbsPath) -> Result<Sysroot> {
        let mut sysroot = Sysroot { crates: Arena::default() };

        for name in SYSROOT_CRATES.trim().lines() {
            let root = [format!("{}/src/lib.rs", name), format!("lib{}/lib.rs", name)]
                .iter()
                .map(|it| sysroot_src_dir.join(it))
                .find(|it| it.exists());

            if let Some(root) = root {
                sysroot.crates.alloc(SysrootCrateData {
                    name: name.into(),
                    root,
                    deps: Vec::new(),
                });
            }
        }

        if let Some(std) = sysroot.by_name("std") {
            for dep in STD_DEPS.trim().lines() {
                if let Some(dep) = sysroot.by_name(dep) {
                    sysroot.crates[std].deps.push(dep)
                }
            }
        }

        if let Some(alloc) = sysroot.by_name("alloc") {
            if let Some(core) = sysroot.by_name("core") {
                sysroot.crates[alloc].deps.push(core);
            }
        }

        if sysroot.by_name("core").is_none() {
            let var_note = if env::var_os("RUST_SRC_PATH").is_some() {
                " (`RUST_SRC_PATH` might be incorrect, try unsetting it)"
            } else {
                ""
            };
            anyhow::bail!(
                "could not find libcore in sysroot path `{}`{}",
                sysroot_src_dir.as_ref().display(),
                var_note,
            );
        }

        Ok(sysroot)
    }

    fn by_name(&self, name: &str) -> Option<SysrootCrate> {
        let (id, _data) = self.crates.iter().find(|(_id, data)| data.name == name)?;
        Some(id)
    }
}

fn discover_sysroot_dir(current_dir: &AbsPath) -> Result<AbsPathBuf> {
    let mut rustc = Command::new(toolchain::rustc());
    rustc.current_dir(current_dir).args(&["--print", "sysroot"]);
    log::debug!("Discovering sysroot by {:?}", rustc);
    let stdout = utf8_stdout(rustc)?;
    Ok(AbsPathBuf::assert(PathBuf::from(stdout)))
}

fn discover_sysroot_src_dir(
    sysroot_path: &AbsPathBuf,
    current_dir: &AbsPath,
) -> Result<AbsPathBuf> {
    if let Ok(path) = env::var("RUST_SRC_PATH") {
        let path = AbsPathBuf::try_from(path.as_str())
            .map_err(|path| format_err!("RUST_SRC_PATH must be absolute: {}", path.display()))?;
        let core = path.join("core");
        if core.exists() {
            log::debug!("Discovered sysroot by RUST_SRC_PATH: {}", path.display());
            return Ok(path);
        }
        log::debug!("RUST_SRC_PATH is set, but is invalid (no core: {:?}), ignoring", core);
    }

    get_rust_src(&sysroot_path)
        .or_else(|| {
            let mut rustup = Command::new(toolchain::rustup());
            rustup.current_dir(current_dir).args(&["component", "add", "rust-src"]);
            utf8_stdout(rustup).ok()?;
            get_rust_src(&sysroot_path)
        })
        .ok_or_else(|| {
            format_err!(
                "\
can't load standard library from sysroot
{}
(discovered via `rustc --print sysroot`)
try installing the Rust source the same way you installed rustc",
                sysroot_path.display(),
            )
        })
}

fn get_rustc_src(sysroot_path: &AbsPath) -> Option<AbsPathBuf> {
    let rustc_src = sysroot_path.join("lib/rustlib/rustc-src/rust/compiler/rustc/Cargo.toml");
    log::debug!("Checking for rustc source code: {}", rustc_src.display());
    if rustc_src.exists() {
        Some(rustc_src)
    } else {
        None
    }
}

fn get_rust_src(sysroot_path: &AbsPath) -> Option<AbsPathBuf> {
    // Try the new path first since the old one still exists.
    let rust_src = sysroot_path.join("lib/rustlib/src/rust");
    log::debug!("Checking sysroot (looking for `library` and `src` dirs): {}", rust_src.display());
    ["library", "src"].iter().map(|it| rust_src.join(it)).find(|it| it.exists())
}

impl SysrootCrateData {
    pub fn root_dir(&self) -> &AbsPath {
        self.root.parent().unwrap()
    }
}

const SYSROOT_CRATES: &str = "
alloc
core
panic_abort
panic_unwind
proc_macro
profiler_builtins
rtstartup
std
stdarch
term
test
unwind";

const STD_DEPS: &str = "
alloc
core
panic_abort
panic_unwind
profiler_builtins
rtstartup
proc_macro
stdarch
term
test
unwind";
<commit_msg>Small file error display fix<commit_after>//! Loads "sysroot" crate.
//!
//! One confusing point here is that normally sysroot is a bunch of `.rlib`s,
//! but we can't process `.rlib` and need source code instead. The source code
//! is typically installed with `rustup component add rust-src` command.

use std::{convert::TryFrom, env, ops, path::PathBuf, process::Command};

use anyhow::{format_err, Result};
use la_arena::{Arena, Idx};
use paths::{AbsPath, AbsPathBuf};

use crate::utf8_stdout;

#[derive(Default, Debug, Clone, Eq, PartialEq)]
pub struct Sysroot {
    crates: Arena<SysrootCrateData>,
}

pub(crate) type SysrootCrate = Idx<SysrootCrateData>;

#[derive(Debug, Clone, Eq, PartialEq)]
pub struct SysrootCrateData {
    pub name: String,
    pub root: AbsPathBuf,
    pub deps: Vec<SysrootCrate>,
}

impl ops::Index<SysrootCrate> for Sysroot {
    type Output = SysrootCrateData;
    fn index(&self, index: SysrootCrate) -> &SysrootCrateData {
        &self.crates[index]
    }
}

impl Sysroot {
    pub fn public_deps(&self) -> impl Iterator<Item = (&'static str, SysrootCrate)> + '_ {
        // core is added as a dependency before std in order to
        // mimic rustcs dependency order
        ["core", "alloc", "std"].iter().filter_map(move |&it| Some((it, self.by_name(it)?)))
    }

    pub fn proc_macro(&self) -> Option<SysrootCrate> {
        self.by_name("proc_macro")
    }

    pub fn crates<'a>(&'a self) -> impl Iterator<Item = SysrootCrate> + ExactSizeIterator + 'a {
        self.crates.iter().map(|(id, _data)| id)
    }

    pub fn discover(cargo_toml: &AbsPath) -> Result<Sysroot> {
        log::debug!("Discovering sysroot for {}", cargo_toml.display());
        let current_dir = cargo_toml.parent().ok_or_else(|| {
            format_err!("Failed to find the parent directory for {}", cargo_toml.display())
        })?;
        let sysroot_dir = discover_sysroot_dir(current_dir)?;
        let sysroot_src_dir = discover_sysroot_src_dir(&sysroot_dir, current_dir)?;
        let res = Sysroot::load(&sysroot_src_dir)?;
        Ok(res)
    }

    pub fn discover_rustc(cargo_toml: &AbsPath) -> Option<AbsPathBuf> {
        log::debug!("Discovering rustc source for {}", cargo_toml.display());
        let current_dir = cargo_toml.parent().unwrap();
        discover_sysroot_dir(current_dir).ok().and_then(|sysroot_dir| get_rustc_src(&sysroot_dir))
    }

    pub fn load(sysroot_src_dir: &AbsPath) -> Result<Sysroot> {
        let mut sysroot = Sysroot { crates: Arena::default() };

        for name in SYSROOT_CRATES.trim().lines() {
            let root = [format!("{}/src/lib.rs", name), format!("lib{}/lib.rs", name)]
                .iter()
                .map(|it| sysroot_src_dir.join(it))
                .find(|it| it.exists());

            if let Some(root) = root {
                sysroot.crates.alloc(SysrootCrateData {
                    name: name.into(),
                    root,
                    deps: Vec::new(),
                });
            }
        }

        if let Some(std) = sysroot.by_name("std") {
            for dep in STD_DEPS.trim().lines() {
                if let Some(dep) = sysroot.by_name(dep) {
                    sysroot.crates[std].deps.push(dep)
                }
            }
        }

        if let Some(alloc) = sysroot.by_name("alloc") {
            if let Some(core) = sysroot.by_name("core") {
                sysroot.crates[alloc].deps.push(core);
            }
        }

        if sysroot.by_name("core").is_none() {
            let var_note = if env::var_os("RUST_SRC_PATH").is_some() {
                " (`RUST_SRC_PATH` might be incorrect, try unsetting it)"
            } else {
                ""
            };
            anyhow::bail!(
                "could not find libcore in sysroot path `{}`{}",
                sysroot_src_dir.as_ref().display(),
                var_note,
            );
        }

        Ok(sysroot)
    }

    fn by_name(&self, name: &str) -> Option<SysrootCrate> {
        let (id, _data) = self.crates.iter().find(|(_id, data)| data.name == name)?;
        Some(id)
    }
}

fn discover_sysroot_dir(current_dir: &AbsPath) -> Result<AbsPathBuf> {
    let mut rustc = Command::new(toolchain::rustc());
    rustc.current_dir(current_dir).args(&["--print", "sysroot"]);
    log::debug!("Discovering sysroot by {:?}", rustc);
    let stdout = utf8_stdout(rustc)?;
    Ok(AbsPathBuf::assert(PathBuf::from(stdout)))
}

fn discover_sysroot_src_dir(
    sysroot_path: &AbsPathBuf,
    current_dir: &AbsPath,
) -> Result<AbsPathBuf> {
    if let Ok(path) = env::var("RUST_SRC_PATH") {
        let path = AbsPathBuf::try_from(path.as_str())
            .map_err(|path| format_err!("RUST_SRC_PATH must be absolute: {}", path.display()))?;
        let core = path.join("core");
        if core.exists() {
            log::debug!("Discovered sysroot by RUST_SRC_PATH: {}", path.display());
            return Ok(path);
        }
        log::debug!("RUST_SRC_PATH is set, but is invalid (no core: {:?}), ignoring", core);
    }

    get_rust_src(&sysroot_path)
        .or_else(|| {
            let mut rustup = Command::new(toolchain::rustup());
            rustup.current_dir(current_dir).args(&["component", "add", "rust-src"]);
            utf8_stdout(rustup).ok()?;
            get_rust_src(&sysroot_path)
        })
        .ok_or_else(|| {
            format_err!(
                "\
can't load standard library from sysroot
{}
(discovered via `rustc --print sysroot`)
try installing the Rust source the same way you installed rustc",
                sysroot_path.display(),
            )
        })
}

fn get_rustc_src(sysroot_path: &AbsPath) -> Option<AbsPathBuf> {
    let rustc_src = sysroot_path.join("lib/rustlib/rustc-src/rust/compiler/rustc/Cargo.toml");
    log::debug!("Checking for rustc source code: {}", rustc_src.display());
    if rustc_src.exists() {
        Some(rustc_src)
    } else {
        None
    }
}

fn get_rust_src(sysroot_path: &AbsPath) -> Option<AbsPathBuf> {
    // Try the new path first since the old one still exists.
    let rust_src = sysroot_path.join("lib/rustlib/src/rust");
    log::debug!("Checking sysroot (looking for `library` and `src` dirs): {}", rust_src.display());
    ["library", "src"].iter().map(|it| rust_src.join(it)).find(|it| it.exists())
}

impl SysrootCrateData {
    pub fn root_dir(&self) -> &AbsPath {
        self.root.parent().unwrap()
    }
}

const SYSROOT_CRATES: &str = "
alloc
core
panic_abort
panic_unwind
proc_macro
profiler_builtins
rtstartup
std
stdarch
term
test
unwind";

const STD_DEPS: &str = "
alloc
core
panic_abort
panic_unwind
profiler_builtins
rtstartup
proc_macro
stdarch
term
test
unwind";
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

extern crate webrender_build;

use std::borrow::Cow;
use std::env;
use std::fs::{canonicalize, read_dir, File};
use std::io::prelude::*;
use std::path::{Path, PathBuf};
use std::collections::hash_map::DefaultHasher;
use std::hash::Hasher;
use webrender_build::shader::*;
use webrender_build::shader_features::{ShaderFeatureFlags, get_shader_features};

/// Compute the shader path for insertion into the include_str!() macro.
/// This makes for more compact generated code than inserting the literal
/// shader source into the generated file.
///
/// If someone is building on a network share, I'm sorry.
fn escape_include_path(path: &Path) -> String {
    let full_path = canonicalize(path).unwrap();
    let full_name = full_path.as_os_str().to_str().unwrap();
    let full_name = full_name.replace("\\\\?\\", "");
    let full_name = full_name.replace("\\", "/");

    full_name
}

fn write_unoptimized_shaders(mut glsl_files: Vec<PathBuf>, shader_file: &mut File) -> Result<(), std::io::Error> {
    writeln!(
        shader_file,
        "  pub static ref UNOPTIMIZED_SHADERS: HashMap<&'static str, SourceWithDigest> = {{"
    )?;
    writeln!(shader_file, "    let mut shaders = HashMap::new();")?;

    // Sort the file list so that the shaders.rs file is filled
    // deterministically.
    glsl_files.sort_by(|a, b| a.file_name().cmp(&b.file_name()));

    for glsl in glsl_files {
        // Compute the shader name.
        assert!(glsl.is_file());
        let shader_name = glsl.file_name().unwrap().to_str().unwrap();
        let shader_name = shader_name.replace(".glsl", "");

        // Compute a digest of the #include-expanded shader source. We store
        // this as a literal alongside the source string so that we don't need
        // to hash large strings at runtime.
        let mut hasher = DefaultHasher::new();
        let base = glsl.parent().unwrap();
        assert!(base.is_dir());
        ShaderSourceParser::new().parse(
            Cow::Owned(shader_source_from_file(&glsl)),
            &|f| Cow::Owned(shader_source_from_file(&base.join(&format!("{}.glsl", f)))),
            &mut |s| hasher.write(s.as_bytes()),
        );
        let digest: ProgramSourceDigest = hasher.into();

        writeln!(
            shader_file,
            "    shaders.insert(\"{}\", SourceWithDigest {{ source: include_str!(\"{}\"), digest: \"{}\"}});",
            shader_name,
            escape_include_path(&glsl),
            digest,
        )?;
    }
    writeln!(shader_file, "    shaders")?;
    writeln!(shader_file, "  }};")?;

    Ok(())
}

#[derive(Clone, Debug)]
struct ShaderOptimizationInput {
    shader_name: &'static str,
    config: String,
    gl_version: ShaderVersion,
}

#[derive(Debug)]
struct ShaderOptimizationOutput {
    full_shader_name: String,
    gl_version: ShaderVersion,
    vert_file_path: PathBuf,
    frag_file_path: PathBuf,
    digest: ProgramSourceDigest,
}

#[derive(Debug)]
struct ShaderOptimizationError {
    shader: ShaderOptimizationInput,
    message: String,
}

fn print_shader_source(shader_src: &str) {
    // For some reason the glsl-opt errors are offset by 1 compared
    // to the provided shader source string.
    println!("0\t|");
    for (n, line) in shader_src.split('\n').enumerate() {
        let line_number = n + 1;
        println!("{}\t|{}", line_number, line);
    }
}

fn write_optimized_shaders(shader_dir: &Path, shader_file: &mut File, out_dir: &str) -> Result<(), std::io::Error> {
    writeln!(
        shader_file,
        "  pub static ref OPTIMIZED_SHADERS: HashMap<(ShaderVersion, &'static str), OptimizedSourceWithDigest> = {{"
    )?;
    writeln!(shader_file, "    let mut shaders = HashMap::new();")?;

    // The full set of optimized shaders can be quite large, so only optimize
    // for the GL version we expect to be used on the target platform. If a different GL
    // version is used we will simply fall back to the unoptimized shaders.
    let shader_versions = match env::var("CARGO_CFG_TARGET_OS").as_ref().map(|s| &**s) {
        Ok("android") | Ok("windows") => [ShaderVersion::Gles],
        _ => [ShaderVersion::Gl],
    };

    let mut shaders = Vec::default();
    for &gl_version in &shader_versions {
        let mut flags = ShaderFeatureFlags::all();
        if gl_version != ShaderVersion::Gl {
            flags.remove(ShaderFeatureFlags::GL);
        }
        if gl_version != ShaderVersion::Gles {
            flags.remove(ShaderFeatureFlags::GLES);
            flags.remove(ShaderFeatureFlags::TEXTURE_EXTERNAL);
        }
        if !matches!(env::var("CARGO_CFG_TARGET_OS").as_ref().map(|s| &**s), Ok("android")) {
            flags.remove(ShaderFeatureFlags::TEXTURE_EXTERNAL_ESSL1);
        }
        flags.remove(ShaderFeatureFlags::DITHERING);

        for (shader_name, configs) in get_shader_features(flags) {
            for config in configs {
                shaders.push(ShaderOptimizationInput {
                    shader_name,
                    config,
                    gl_version,
                });
            }
        }
    }

    let outputs = build_parallel::compile_objects(&|shader: &ShaderOptimizationInput| {
        println!("Optimizing shader {:?}", shader);
        let target = match shader.gl_version {
            ShaderVersion::Gl => glslopt::Target::OpenGl,
            ShaderVersion::Gles => glslopt::Target::OpenGles30,
        };
        let glslopt_ctx = glslopt::Context::new(target);

        let features = shader.config.split(",").filter(|f| !f.is_empty()).collect::<Vec<_>>();

        let (vert_src, frag_src) = build_shader_strings(
            shader.gl_version,
            &features,
            shader.shader_name,
            &|f| Cow::Owned(shader_source_from_file(&shader_dir.join(&format!("{}.glsl", f)))),
        );

        let full_shader_name = if shader.config.is_empty() {
            shader.shader_name.to_string()
        } else {
            format!("{}_{}", shader.shader_name, shader.config.replace(",", "_"))
        };

        let vert = glslopt_ctx.optimize(glslopt::ShaderType::Vertex, vert_src.clone());
        if !vert.get_status() {
            print_shader_source(&vert_src);
            return Err(ShaderOptimizationError {
                shader: shader.clone(),
                message: vert.get_log().to_string(),
            });
        }
        let frag = glslopt_ctx.optimize(glslopt::ShaderType::Fragment, frag_src.clone());
        if !frag.get_status() {
            print_shader_source(&frag_src);
            return Err(ShaderOptimizationError {
                shader: shader.clone(),
                message: frag.get_log().to_string(),
            });
        }

        let vert_source = vert.get_output().unwrap();
        let frag_source = frag.get_output().unwrap();

        // Compute a digest of the optimized shader sources. We store this
        // as a literal alongside the source string so that we don't need
        // to hash large strings at runtime.
        let mut hasher = DefaultHasher::new();

        let vert_file_path = Path::new(out_dir)
            .join(format!("{}_{:?}.vert", full_shader_name, shader.gl_version));
        write_optimized_shader_file(&vert_file_path, vert_source, &shader.shader_name, &features, &mut hasher);

        let frag_file_path = vert_file_path.with_extension("frag");
        write_optimized_shader_file(&frag_file_path, frag_source, &shader.shader_name, &features, &mut hasher);

        let digest: ProgramSourceDigest = hasher.into();

        println!("Finished optimizing shader {:?}", shader);

        Ok(ShaderOptimizationOutput {
            full_shader_name,
            gl_version: shader.gl_version,
            vert_file_path,
            frag_file_path,
            digest,
        })
    }, &shaders);

    match outputs {
        Ok(mut outputs) => {
            // Sort the shader list so that the shaders.rs file is filled
            // deterministically.
            outputs.sort_by(|a, b| {
                (a.gl_version, a.full_shader_name.clone()).cmp(&(b.gl_version, b.full_shader_name.clone()))
            });

            for shader in outputs {
                writeln!(
                    shader_file,
                    "    shaders.insert(({}, \"{}\"), OptimizedSourceWithDigest {{",
                    shader.gl_version.variant_name(),
                    shader.full_shader_name,
                )?;
                writeln!(
                    shader_file,
                    "        vert_source: include_str!(\"{}\"),",
                    escape_include_path(&shader.vert_file_path),
                )?;
                writeln!(
                    shader_file,
                    "        frag_source: include_str!(\"{}\"),",
                    escape_include_path(&shader.frag_file_path),
                )?;
                writeln!(shader_file, "        digest: \"{}\",", shader.digest)?;
                writeln!(shader_file, "    }});")?;
            }
        }
        Err(err) => match err {
            build_parallel::Error::BuildError(err) => {
                panic!("Error optimizing shader {:?}: {}", err.shader, err.message)
            }
            _ => panic!("Error optimizing shaders."),
        }
    }

    writeln!(shader_file, "    shaders")?;
    writeln!(shader_file, "  }};")?;

    Ok(())
}

fn write_optimized_shader_file(
    path: &Path,
    source: &str,
    shader_name: &str,
    features: &[&str],
    hasher: &mut DefaultHasher,
) {
    let mut file = File::create(&path).unwrap();
    for (line_number, line) in source.lines().enumerate() {
        // We embed the shader name and features as a comment in the
        // source to make debugging easier.
        // The #version directive must be on the first line so we insert
        // the extra information on the next line.
        if line_number == 1 {
            let prelude = format!(
                "// {}\n// features: {:?}\n\n",
                shader_name, features
            );
            file.write_all(prelude.as_bytes()).unwrap();
            hasher.write(prelude.as_bytes());
        }
        file.write_all(line.as_bytes()).unwrap();
        file.write_all("\n".as_bytes()).unwrap();
        hasher.write(line.as_bytes());
        hasher.write("\n".as_bytes());
    }
}

fn main() -> Result<(), std::io::Error> {
    let out_dir = env::var("OUT_DIR").unwrap_or("out".to_owned());

    let shaders_file_path = Path::new(&out_dir).join("shaders.rs");
    let mut glsl_files = vec![];

    println!("cargo:rerun-if-changed=res");
    let res_dir = Path::new("res");
    for entry in read_dir(res_dir)? {
        let entry = entry?;
        let path = entry.path();

        if entry.file_name().to_str().unwrap().ends_with(".glsl") {
            println!("cargo:rerun-if-changed={}", path.display());
            glsl_files.push(path.to_owned());
        }
    }

    let mut shader_file = File::create(shaders_file_path)?;

    writeln!(shader_file, "/// AUTO GENERATED BY build.rs\n")?;
    writeln!(shader_file, "use std::collections::HashMap;\n")?;
    writeln!(shader_file, "use webrender_build::shader::ShaderVersion;\n")?;
    writeln!(shader_file, "pub struct SourceWithDigest {{")?;
    writeln!(shader_file, "    pub source: &'static str,")?;
    writeln!(shader_file, "    pub digest: &'static str,")?;
    writeln!(shader_file, "}}\n")?;
    writeln!(shader_file, "pub struct OptimizedSourceWithDigest {{")?;
    writeln!(shader_file, "    pub vert_source: &'static str,")?;
    writeln!(shader_file, "    pub frag_source: &'static str,")?;
    writeln!(shader_file, "    pub digest: &'static str,")?;
    writeln!(shader_file, "}}\n")?;
    writeln!(shader_file, "lazy_static! {{")?;

    write_unoptimized_shaders(glsl_files, &mut shader_file)?;
    writeln!(shader_file, "")?;
    write_optimized_shaders(&res_dir, &mut shader_file, &out_dir)?;
    writeln!(shader_file, "}}")?;

    Ok(())
}
<commit_msg>Auto merge of #4322 - moz-gfx:github-sync, r=auto<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

extern crate webrender_build;

use std::borrow::Cow;
use std::env;
use std::fs::{canonicalize, read_dir, File};
use std::io::prelude::*;
use std::path::{Path, PathBuf};
use std::collections::hash_map::DefaultHasher;
use std::hash::Hasher;
use webrender_build::shader::*;
use webrender_build::shader_features::{ShaderFeatureFlags, get_shader_features};

// glsopt is known to leak, but we don't particularly care.
#[no_mangle]
pub extern "C" fn __lsan_default_options() -> *const u8 {
    b"detect_leaks=0\0".as_ptr()
}

/// Compute the shader path for insertion into the include_str!() macro.
/// This makes for more compact generated code than inserting the literal
/// shader source into the generated file.
///
/// If someone is building on a network share, I'm sorry.
fn escape_include_path(path: &Path) -> String {
    let full_path = canonicalize(path).unwrap();
    let full_name = full_path.as_os_str().to_str().unwrap();
    let full_name = full_name.replace("\\\\?\\", "");
    let full_name = full_name.replace("\\", "/");

    full_name
}

fn write_unoptimized_shaders(mut glsl_files: Vec<PathBuf>, shader_file: &mut File) -> Result<(), std::io::Error> {
    writeln!(
        shader_file,
        "  pub static ref UNOPTIMIZED_SHADERS: HashMap<&'static str, SourceWithDigest> = {{"
    )?;
    writeln!(shader_file, "    let mut shaders = HashMap::new();")?;

    // Sort the file list so that the shaders.rs file is filled
    // deterministically.
    glsl_files.sort_by(|a, b| a.file_name().cmp(&b.file_name()));

    for glsl in glsl_files {
        // Compute the shader name.
        assert!(glsl.is_file());
        let shader_name = glsl.file_name().unwrap().to_str().unwrap();
        let shader_name = shader_name.replace(".glsl", "");

        // Compute a digest of the #include-expanded shader source. We store
        // this as a literal alongside the source string so that we don't need
        // to hash large strings at runtime.
        let mut hasher = DefaultHasher::new();
        let base = glsl.parent().unwrap();
        assert!(base.is_dir());
        ShaderSourceParser::new().parse(
            Cow::Owned(shader_source_from_file(&glsl)),
            &|f| Cow::Owned(shader_source_from_file(&base.join(&format!("{}.glsl", f)))),
            &mut |s| hasher.write(s.as_bytes()),
        );
        let digest: ProgramSourceDigest = hasher.into();

        writeln!(
            shader_file,
            "    shaders.insert(\"{}\", SourceWithDigest {{ source: include_str!(\"{}\"), digest: \"{}\"}});",
            shader_name,
            escape_include_path(&glsl),
            digest,
        )?;
    }
    writeln!(shader_file, "    shaders")?;
    writeln!(shader_file, "  }};")?;

    Ok(())
}

#[derive(Clone, Debug)]
struct ShaderOptimizationInput {
    shader_name: &'static str,
    config: String,
    gl_version: ShaderVersion,
}

#[derive(Debug)]
struct ShaderOptimizationOutput {
    full_shader_name: String,
    gl_version: ShaderVersion,
    vert_file_path: PathBuf,
    frag_file_path: PathBuf,
    digest: ProgramSourceDigest,
}

#[derive(Debug)]
struct ShaderOptimizationError {
    shader: ShaderOptimizationInput,
    message: String,
}

fn print_shader_source(shader_src: &str) {
    // For some reason the glsl-opt errors are offset by 1 compared
    // to the provided shader source string.
    println!("0\t|");
    for (n, line) in shader_src.split('\n').enumerate() {
        let line_number = n + 1;
        println!("{}\t|{}", line_number, line);
    }
}

fn write_optimized_shaders(shader_dir: &Path, shader_file: &mut File, out_dir: &str) -> Result<(), std::io::Error> {
    writeln!(
        shader_file,
        "  pub static ref OPTIMIZED_SHADERS: HashMap<(ShaderVersion, &'static str), OptimizedSourceWithDigest> = {{"
    )?;
    writeln!(shader_file, "    let mut shaders = HashMap::new();")?;

    // The full set of optimized shaders can be quite large, so only optimize
    // for the GL version we expect to be used on the target platform. If a different GL
    // version is used we will simply fall back to the unoptimized shaders.
    let shader_versions = match env::var("CARGO_CFG_TARGET_OS").as_ref().map(|s| &**s) {
        Ok("android") | Ok("windows") => [ShaderVersion::Gles],
        _ => [ShaderVersion::Gl],
    };

    let mut shaders = Vec::default();
    for &gl_version in &shader_versions {
        let mut flags = ShaderFeatureFlags::all();
        if gl_version != ShaderVersion::Gl {
            flags.remove(ShaderFeatureFlags::GL);
        }
        if gl_version != ShaderVersion::Gles {
            flags.remove(ShaderFeatureFlags::GLES);
            flags.remove(ShaderFeatureFlags::TEXTURE_EXTERNAL);
        }
        if !matches!(env::var("CARGO_CFG_TARGET_OS").as_ref().map(|s| &**s), Ok("android")) {
            flags.remove(ShaderFeatureFlags::TEXTURE_EXTERNAL_ESSL1);
        }
        flags.remove(ShaderFeatureFlags::DITHERING);

        for (shader_name, configs) in get_shader_features(flags) {
            for config in configs {
                shaders.push(ShaderOptimizationInput {
                    shader_name,
                    config,
                    gl_version,
                });
            }
        }
    }

    let outputs = build_parallel::compile_objects(&|shader: &ShaderOptimizationInput| {
        println!("Optimizing shader {:?}", shader);
        let target = match shader.gl_version {
            ShaderVersion::Gl => glslopt::Target::OpenGl,
            ShaderVersion::Gles => glslopt::Target::OpenGles30,
        };
        let glslopt_ctx = glslopt::Context::new(target);

        let features = shader.config.split(",").filter(|f| !f.is_empty()).collect::<Vec<_>>();

        let (vert_src, frag_src) = build_shader_strings(
            shader.gl_version,
            &features,
            shader.shader_name,
            &|f| Cow::Owned(shader_source_from_file(&shader_dir.join(&format!("{}.glsl", f)))),
        );

        let full_shader_name = if shader.config.is_empty() {
            shader.shader_name.to_string()
        } else {
            format!("{}_{}", shader.shader_name, shader.config.replace(",", "_"))
        };

        let vert = glslopt_ctx.optimize(glslopt::ShaderType::Vertex, vert_src.clone());
        if !vert.get_status() {
            print_shader_source(&vert_src);
            return Err(ShaderOptimizationError {
                shader: shader.clone(),
                message: vert.get_log().to_string(),
            });
        }
        let frag = glslopt_ctx.optimize(glslopt::ShaderType::Fragment, frag_src.clone());
        if !frag.get_status() {
            print_shader_source(&frag_src);
            return Err(ShaderOptimizationError {
                shader: shader.clone(),
                message: frag.get_log().to_string(),
            });
        }

        let vert_source = vert.get_output().unwrap();
        let frag_source = frag.get_output().unwrap();

        // Compute a digest of the optimized shader sources. We store this
        // as a literal alongside the source string so that we don't need
        // to hash large strings at runtime.
        let mut hasher = DefaultHasher::new();

        let vert_file_path = Path::new(out_dir)
            .join(format!("{}_{:?}.vert", full_shader_name, shader.gl_version));
        write_optimized_shader_file(&vert_file_path, vert_source, &shader.shader_name, &features, &mut hasher);

        let frag_file_path = vert_file_path.with_extension("frag");
        write_optimized_shader_file(&frag_file_path, frag_source, &shader.shader_name, &features, &mut hasher);

        let digest: ProgramSourceDigest = hasher.into();

        println!("Finished optimizing shader {:?}", shader);

        Ok(ShaderOptimizationOutput {
            full_shader_name,
            gl_version: shader.gl_version,
            vert_file_path,
            frag_file_path,
            digest,
        })
    }, &shaders);

    match outputs {
        Ok(mut outputs) => {
            // Sort the shader list so that the shaders.rs file is filled
            // deterministically.
            outputs.sort_by(|a, b| {
                (a.gl_version, a.full_shader_name.clone()).cmp(&(b.gl_version, b.full_shader_name.clone()))
            });

            for shader in outputs {
                writeln!(
                    shader_file,
                    "    shaders.insert(({}, \"{}\"), OptimizedSourceWithDigest {{",
                    shader.gl_version.variant_name(),
                    shader.full_shader_name,
                )?;
                writeln!(
                    shader_file,
                    "        vert_source: include_str!(\"{}\"),",
                    escape_include_path(&shader.vert_file_path),
                )?;
                writeln!(
                    shader_file,
                    "        frag_source: include_str!(\"{}\"),",
                    escape_include_path(&shader.frag_file_path),
                )?;
                writeln!(shader_file, "        digest: \"{}\",", shader.digest)?;
                writeln!(shader_file, "    }});")?;
            }
        }
        Err(err) => match err {
            build_parallel::Error::BuildError(err) => {
                panic!("Error optimizing shader {:?}: {}", err.shader, err.message)
            }
            _ => panic!("Error optimizing shaders."),
        }
    }

    writeln!(shader_file, "    shaders")?;
    writeln!(shader_file, "  }};")?;

    Ok(())
}

fn write_optimized_shader_file(
    path: &Path,
    source: &str,
    shader_name: &str,
    features: &[&str],
    hasher: &mut DefaultHasher,
) {
    let mut file = File::create(&path).unwrap();
    for (line_number, line) in source.lines().enumerate() {
        // We embed the shader name and features as a comment in the
        // source to make debugging easier.
        // The #version directive must be on the first line so we insert
        // the extra information on the next line.
        if line_number == 1 {
            let prelude = format!(
                "// {}\n// features: {:?}\n\n",
                shader_name, features
            );
            file.write_all(prelude.as_bytes()).unwrap();
            hasher.write(prelude.as_bytes());
        }
        file.write_all(line.as_bytes()).unwrap();
        file.write_all("\n".as_bytes()).unwrap();
        hasher.write(line.as_bytes());
        hasher.write("\n".as_bytes());
    }
}

fn main() -> Result<(), std::io::Error> {
    let out_dir = env::var("OUT_DIR").unwrap_or("out".to_owned());

    let shaders_file_path = Path::new(&out_dir).join("shaders.rs");
    let mut glsl_files = vec![];

    println!("cargo:rerun-if-changed=res");
    let res_dir = Path::new("res");
    for entry in read_dir(res_dir)? {
        let entry = entry?;
        let path = entry.path();

        if entry.file_name().to_str().unwrap().ends_with(".glsl") {
            println!("cargo:rerun-if-changed={}", path.display());
            glsl_files.push(path.to_owned());
        }
    }

    let mut shader_file = File::create(shaders_file_path)?;

    writeln!(shader_file, "/// AUTO GENERATED BY build.rs\n")?;
    writeln!(shader_file, "use std::collections::HashMap;\n")?;
    writeln!(shader_file, "use webrender_build::shader::ShaderVersion;\n")?;
    writeln!(shader_file, "pub struct SourceWithDigest {{")?;
    writeln!(shader_file, "    pub source: &'static str,")?;
    writeln!(shader_file, "    pub digest: &'static str,")?;
    writeln!(shader_file, "}}\n")?;
    writeln!(shader_file, "pub struct OptimizedSourceWithDigest {{")?;
    writeln!(shader_file, "    pub vert_source: &'static str,")?;
    writeln!(shader_file, "    pub frag_source: &'static str,")?;
    writeln!(shader_file, "    pub digest: &'static str,")?;
    writeln!(shader_file, "}}\n")?;
    writeln!(shader_file, "lazy_static! {{")?;

    write_unoptimized_shaders(glsl_files, &mut shader_file)?;
    writeln!(shader_file, "")?;
    write_optimized_shaders(&res_dir, &mut shader_file, &out_dir)?;
    writeln!(shader_file, "}}")?;

    Ok(())
}
<|endoftext|>