<commit_before>extern crate docopt;
extern crate flac;
extern crate rustc_serialize;

use docopt::Docopt;
use flac::{ReadStream, Stream, StreamProducer};
use flac::metadata;
use flac::metadata::VorbisComment;

use std::env;
use std::fs::File;

const USAGE: &'static str = "
Usage: metadata streaminfo [options] <input>
       metadata comments [options] <input>
       metadata --help

Options:
  --block-size       Show both the max and min block size from StreamInfo.
  --frame-size       Show both the max and min frame size from StreamInfo.
  --sample-rate      Show the sample rate from StreamInfo.
  --channels         Show the number of channels from StreamInfo.
  --bits-per-sample  Show the size in bits for each sample from StreamInfo.
  --total-samples    Show total number of samples from StreamInfo.
  --md5              Show the MD5 signature from StreamInfo.
  --vendor           Show the vendor string from VorbisComment.
  --name=NAME        Show the comments matching the `NAME` from VorbisComment.
  -h, --help         Show this message.
";

#[derive(Debug, RustcDecodable)]
struct Arguments {
  arg_input: String,
  cmd_streaminfo: bool,
  cmd_comments: bool,
  flag_block_size: bool,
  flag_frame_size: bool,
  flag_sample_rate: bool,
  flag_channels: bool,
  flag_bits_per_sample: bool,
  flag_total_samples: bool,
  flag_md5: bool,
  flag_vendor: bool,
  flag_name: Option<String>,
}

fn print_stream_info<P>(stream: &Stream<P>, args: &Arguments)
 where P: StreamProducer {
  let info     = stream.info();
  let no_flags = (args.flag_block_size      || args.flag_frame_size    ||
                  args.flag_sample_rate     || args.flag_channels      ||
                  args.flag_bits_per_sample || args.flag_total_samples ||
                  args.flag_md5) == false;

  if no_flags || args.flag_block_size {
    println!("Minimum block size: {} samples", info.min_block_size);
    println!("Maximum block size: {} samples", info.max_block_size);
  }

  if no_flags || args.flag_frame_size {
    println!("Minimum frame size: {} bytes", info.min_frame_size);
    println!("Maximum frame size: {} bytes", info.max_frame_size);
  }

  if no_flags || args.flag_sample_rate {
    println!("Sample rate: {} Hz", info.sample_rate);
  }

  if no_flags || args.flag_channels {
    println!("Number of channels: {}", info.channels);
  }

  if no_flags || args.flag_bits_per_sample {
    println!("Bits per sample: {}", info.bits_per_sample);
  }

  if no_flags || args.flag_total_samples {
    println!("Total samples: {}", info.total_samples);
  }

  if no_flags || args.flag_md5 {
    let mut md5  = String::with_capacity(32);

    for byte in &info.md5_sum {
      let hex = format!("{:02x}", byte);

      md5.push_str(&hex);
    }

    println!("MD5 sum: {}", md5);
  }
}

fn print_vorbis_comments(vorbis_comment: &VorbisComment) {
  let mut index = 1;

  println!("Vendor String: {}", vorbis_comment.vendor_string);
  println!("Number of Comments: {}", vorbis_comment.comments.len());

  for comment in &vorbis_comment.comments {
    println!("  {}: \"{}\" = {}", index, comment.0, comment.1);

    index += 1;
  }
}

fn main() {
  let args: Arguments = Docopt::new(USAGE)
    .and_then(|d| d.argv(env::args()).decode())
    .unwrap_or_else(|e| e.exit());

  let stream = Stream::<ReadStream<File>>::from_file(&args.arg_input)
                 .expect("Couldn't parse file");

  if args.cmd_streaminfo {
    print_stream_info(&stream, &args);
  }

  for meta in stream.metadata() {
    match meta.data {
      metadata::Data::VorbisComment(ref v) => {
        if args.cmd_comments {
          print_vorbis_comments(v)
        }
      }
      _                                    => continue,
    }
  }
}
<commit_msg>Implement the options for vorbis comments<commit_after>extern crate docopt;
extern crate flac;
extern crate rustc_serialize;

use docopt::Docopt;
use flac::{ReadStream, Stream, StreamProducer};
use flac::metadata;
use flac::metadata::VorbisComment;

use std::env;
use std::fs::File;

const USAGE: &'static str = "
Usage: metadata streaminfo [options] <input>
       metadata comments [options] <input>
       metadata --help

Options:
  --block-size       Show both the max and min block size from StreamInfo.
  --frame-size       Show both the max and min frame size from StreamInfo.
  --sample-rate      Show the sample rate from StreamInfo.
  --channels         Show the number of channels from StreamInfo.
  --bits-per-sample  Show the size in bits for each sample from StreamInfo.
  --total-samples    Show total number of samples from StreamInfo.
  --md5              Show the MD5 signature from StreamInfo.
  --vendor           Show the vendor string from VorbisComment.
  --name=NAME        Show the comments matching the `NAME` from VorbisComment.
  -h, --help         Show this message.
";

#[derive(Debug, RustcDecodable)]
struct Arguments {
  arg_input: String,
  cmd_streaminfo: bool,
  cmd_comments: bool,
  flag_block_size: bool,
  flag_frame_size: bool,
  flag_sample_rate: bool,
  flag_channels: bool,
  flag_bits_per_sample: bool,
  flag_total_samples: bool,
  flag_md5: bool,
  flag_vendor: bool,
  flag_name: Option<String>,
}

fn print_stream_info<P>(stream: &Stream<P>, args: &Arguments)
 where P: StreamProducer {
  let info     = stream.info();
  let no_flags = (args.flag_block_size      || args.flag_frame_size    ||
                  args.flag_sample_rate     || args.flag_channels      ||
                  args.flag_bits_per_sample || args.flag_total_samples ||
                  args.flag_md5) == false;

  if no_flags || args.flag_block_size {
    println!("Minimum block size: {} samples", info.min_block_size);
    println!("Maximum block size: {} samples", info.max_block_size);
  }

  if no_flags || args.flag_frame_size {
    println!("Minimum frame size: {} bytes", info.min_frame_size);
    println!("Maximum frame size: {} bytes", info.max_frame_size);
  }

  if no_flags || args.flag_sample_rate {
    println!("Sample rate: {} Hz", info.sample_rate);
  }

  if no_flags || args.flag_channels {
    println!("Number of channels: {}", info.channels);
  }

  if no_flags || args.flag_bits_per_sample {
    println!("Bits per sample: {}", info.bits_per_sample);
  }

  if no_flags || args.flag_total_samples {
    println!("Total samples: {}", info.total_samples);
  }

  if no_flags || args.flag_md5 {
    let mut md5  = String::with_capacity(32);

    for byte in &info.md5_sum {
      let hex = format!("{:02x}", byte);

      md5.push_str(&hex);
    }

    println!("MD5 sum: {}", md5);
  }
}

fn print_vorbis_comments(vorbis_comment: &VorbisComment, args: &Arguments) {
  let mut index = 1;
  let no_flags  = (args.flag_vendor || args.flag_name.is_some()) == false;

  if no_flags || args.flag_vendor {
    println!("Vendor String: {}", vorbis_comment.vendor_string);
  }

  if no_flags {
    println!("Number of Comments: {}", vorbis_comment.comments.len());
  }

  for comment in &vorbis_comment.comments {
    let is_name = if let Some(ref name) = args.flag_name {
      name == comment.0
    } else {
      false
    };

    if no_flags || is_name {
      println!("  {}: \"{}\" = {}", index, comment.0, comment.1);
    }

    index += 1;
  }
}

fn main() {
  let args: Arguments = Docopt::new(USAGE)
    .and_then(|d| d.argv(env::args()).decode())
    .unwrap_or_else(|e| e.exit());

  let stream = Stream::<ReadStream<File>>::from_file(&args.arg_input)
                 .expect("Couldn't parse file");

  if args.cmd_streaminfo {
    print_stream_info(&stream, &args);
  }

  for meta in stream.metadata() {
    match meta.data {
      metadata::Data::VorbisComment(ref v) => {
        if args.cmd_comments {
          print_vorbis_comments(v)
        }
      }
      _                                    => continue,
    }
  }
}
<|endoftext|>


<commit_before>use std::path::{Path, PathBuf};
use std::borrow::Cow;
use std::env;
use std::io;
use std::process::Command;
use std::fmt::{self, Display};

use itertools::Itertools;

use errors::*;
use multirust_dist::{temp, dist};
use multirust_utils::{self, utils};
use override_db::OverrideDB;
use toolchain::Toolchain;

// Note: multirust-rs jumped from 2 to 12 to leave multirust.sh room to diverge
pub const METADATA_VERSION: &'static str = "12";

#[derive(Debug)]
pub enum OverrideReason {
    Environment,
    OverrideDB(PathBuf),
}

impl Display for OverrideReason {
    fn fmt(&self, f: &mut fmt::Formatter) -> ::std::result::Result<(), fmt::Error> {
        match *self {
            OverrideReason::Environment => write!(f, "environment override by MULTIRUST_TOOLCHAIN"),
            OverrideReason::OverrideDB(ref path) => {
                write!(f, "directory override for '{}'", path.display())
            }
        }
    }
}

#[derive(Debug)]
pub struct Cfg {
    pub multirust_dir: PathBuf,
    pub version_file: PathBuf,
    pub override_db: OverrideDB,
    pub default_file: PathBuf,
    pub toolchains_dir: PathBuf,
    pub update_hash_dir: PathBuf,
    pub temp_cfg: temp::Cfg,
    pub gpg_key: Cow<'static, str>,
    pub env_override: Option<String>,
    pub dist_root_url: Cow<'static, str>,
    pub notify_handler: SharedNotifyHandler,
}

impl Cfg {
    pub fn from_env(notify_handler: SharedNotifyHandler) -> Result<Self> {
        // Get absolute home directory
        let data_dir = try!(utils::get_local_data_path());

        // Set up the multirust home directory
        let multirust_dir = env::var_os("MULTIRUST_HOME")
                                .and_then(utils::if_not_empty)
                                .map_or_else(|| data_dir.join(".multirust"), PathBuf::from);

        try!(utils::ensure_dir_exists("home", &multirust_dir, ntfy!(&notify_handler)));

        // Data locations
        let version_file = multirust_dir.join("version");
        let override_db = OverrideDB::new(multirust_dir.join("overrides"));
        let default_file = multirust_dir.join("default");
        let toolchains_dir = multirust_dir.join("toolchains");
        let update_hash_dir = multirust_dir.join("update-hashes");

        let notify_clone = notify_handler.clone();
        let temp_cfg = temp::Cfg::new(multirust_dir.join("tmp"),
                                      shared_ntfy!(move |n: temp::Notification| {
                                          notify_clone.call(Notification::Temp(n));
                                      }));

        // GPG key
        let gpg_key = if let Some(path) = env::var_os("MULTIRUST_GPG_KEY")
                                              .and_then(utils::if_not_empty) {
            Cow::Owned(try!(utils::read_file("public key", Path::new(&path))))
        } else {
            Cow::Borrowed(include_str!("rust-key.gpg.ascii"))
        };

        // Environment override
        let env_override = env::var("MULTIRUST_TOOLCHAIN")
                               .ok()
                               .and_then(utils::if_not_empty);

        let dist_root_url = env::var("MULTIRUST_DIST_ROOT")
                                .ok()
                                .and_then(utils::if_not_empty)
                                .map_or(Cow::Borrowed(dist::DEFAULT_DIST_ROOT), Cow::Owned);

        Ok(Cfg {
            multirust_dir: multirust_dir,
            version_file: version_file,
            override_db: override_db,
            default_file: default_file,
            toolchains_dir: toolchains_dir,
            update_hash_dir: update_hash_dir,
            temp_cfg: temp_cfg,
            gpg_key: gpg_key,
            notify_handler: notify_handler,
            env_override: env_override,
            dist_root_url: dist_root_url,
        })
    }

    pub fn set_default(&self, toolchain: &str) -> Result<()> {
        let work_file = try!(self.temp_cfg.new_file());

        try!(utils::write_file("temp", &work_file, toolchain));

        try!(utils::rename_file("default", &*work_file, &self.default_file));

        self.notify_handler.call(Notification::SetDefaultToolchain(toolchain));

        Ok(())
    }

    pub fn get_toolchain(&self, name: &str, create_parent: bool) -> Result<Toolchain> {
        if create_parent {
            try!(utils::ensure_dir_exists("toolchains",
                                          &self.toolchains_dir,
                                          ntfy!(&self.notify_handler)));
        }

        Ok(Toolchain::from(self, name))
    }

    pub fn verify_toolchain(&self, name: &str) -> Result<Toolchain> {
        let toolchain = try!(self.get_toolchain(name, false));
        try!(toolchain.verify());
        Ok(toolchain)
    }

    pub fn get_hash_file(&self, toolchain: &str, create_parent: bool) -> Result<PathBuf> {
        if create_parent {
            try!(utils::ensure_dir_exists("update-hash",
                                          &self.update_hash_dir,
                                          ntfy!(&self.notify_handler)));
        }

        Ok(self.update_hash_dir.join(toolchain))
    }

    pub fn which_binary(&self, path: &Path, binary: &str) -> Result<Option<PathBuf>> {

        if let Some((toolchain, _)) = try!(self.find_override_toolchain_or_default(path)) {
            Ok(Some(toolchain.binary_file(binary)))
        } else {
            Ok(None)
        }
    }

    pub fn upgrade_data(&self) -> Result<()> {
        if !utils::is_file(&self.version_file) {
            return Ok(());
        }

        let mut current_version = try!(utils::read_file("version", &self.version_file));
        let len = current_version.trim_right().len();
        current_version.truncate(len);

        if current_version == METADATA_VERSION {
            self.notify_handler
                .call(Notification::MetadataUpgradeNotNeeded(METADATA_VERSION));
            return Ok(());
        }

        self.notify_handler
            .call(Notification::UpgradingMetadata(&current_version, METADATA_VERSION));

        match &*current_version {
            "1" => {
                // This corresponds to an old version of multirust.sh.
                Err(Error::UnknownMetadataVersion(current_version))
            }
            "2" => {
                // The toolchain installation format changed. Just delete them all.
                self.notify_handler
                    .call(Notification::UpgradeRemovesToolchains);

                let dirs = try!(utils::read_dir("toolchains", &self.toolchains_dir));
                for dir in dirs {
                    let dir = try!(dir.map_err(|e| Error::UpgradeIoError(e)));
                    try!(utils::remove_dir("toolchain", &dir.path(),
                                           multirust_utils::NotifyHandler::some(&self.notify_handler)));
                }

                // Also delete the update hashes
                let files = try!(utils::read_dir("update hashes", &self.update_hash_dir));
                for file in files {
                    let file = try!(file.map_err(|e| Error::UpgradeIoError(e)));
                    try!(utils::remove_file("update hash", &file.path()));
                }

                try!(utils::write_file("version", &self.version_file, METADATA_VERSION));

                Ok(())
            }
            _ => Err(Error::UnknownMetadataVersion(current_version)),
        }
    }

    pub fn delete_data(&self) -> Result<()> {
        if utils::path_exists(&self.multirust_dir) {
            Ok(try!(utils::remove_dir("home", &self.multirust_dir, ntfy!(&self.notify_handler))))
        } else {
            Ok(())
        }
    }

    pub fn find_default(&self) -> Result<Option<Toolchain>> {
        if !utils::is_file(&self.default_file) {
            return Ok(None);
        }
        let content = try!(utils::read_file("default", &self.default_file));
        let name = content.trim_matches('\n');
        if name.is_empty() {
            return Ok(None);
        }

        let toolchain = try!(self.verify_toolchain(name).map_err(|_| Error::ToolchainNotInstalled(name.to_string())));

        Ok(Some(toolchain))
    }

    pub fn find_override(&self, path: &Path) -> Result<Option<(Toolchain, OverrideReason)>> {
        if let Some(ref name) = self.env_override {
            let toolchain = try!(self.verify_toolchain(name).map_err(|_| Error::ToolchainNotInstalled(name.to_string())));

            return Ok(Some((toolchain, OverrideReason::Environment)));
        }

        if let Some((name, reason_path)) = try!(self.override_db
                                                    .find(path, self.notify_handler.as_ref())) {
            let toolchain = try!(self.verify_toolchain(&name).map_err(|_| Error::ToolchainNotInstalled(name.to_string())));
            return Ok(Some((toolchain, OverrideReason::OverrideDB(reason_path))));
        }

        Ok(None)
    }

    pub fn find_override_toolchain_or_default
        (&self,
         path: &Path)
         -> Result<Option<(Toolchain, Option<OverrideReason>)>> {
        Ok(if let Some((toolchain, reason)) = try!(self.find_override(path)) {
            Some((toolchain, Some(reason)))
        } else {
            try!(self.find_default()).map(|toolchain| (toolchain, None))
        })
    }

    pub fn list_toolchains(&self) -> Result<Vec<String>> {
        if utils::is_directory(&self.toolchains_dir) {
            let toolchains: Vec<_> = try!(utils::read_dir("toolchains", &self.toolchains_dir))
                                         .filter_map(io::Result::ok)
                                         .filter_map(|e| e.file_name().into_string().ok())
                                         .collect();

            Ok(toolchains)
        } else {
            Ok(Vec::new())
        }
    }

    pub fn update_all_channels(&self) -> Result<Vec<(String, Result<()>)>> {
        let mut toolchains = try!(self.list_toolchains());
        toolchains.sort();

        Ok(toolchains.into_iter()
                     .merge(["beta", "nightly", "stable"].into_iter().map(|s| (*s).to_owned()))
                     .dedup()
                     .filter(|name| {
                         dist::ToolchainDesc::from_str(&name).map(|d| d.is_tracking()).ok() == Some(true)
                     })
                     .map(|name| {
                         let result = self.get_toolchain(&name, true)
                                          .and_then(|t| t.install_from_dist());
                         if let Err(ref e) = result {
                             self.notify_handler.call(Notification::NonFatalError(e));
                         }
                         (name, result)
                     })
                     .collect())
    }

    pub fn check_metadata_version(&self) -> Result<()> {
        try!(utils::assert_is_directory(&self.multirust_dir));

        if !utils::is_file(&self.version_file) {
            self.notify_handler.call(Notification::WritingMetadataVersion(METADATA_VERSION));

            try!(utils::write_file("metadata version", &self.version_file, METADATA_VERSION));

            Ok(())
        } else {
            let current_version = try!(utils::read_file("metadata version", &self.version_file));

            self.notify_handler.call(Notification::ReadMetadataVersion(&current_version));

            if &*current_version == METADATA_VERSION {
                Ok(())
            } else {
                Err(Error::NeedMetadataUpgrade)
            }
        }
    }

    pub fn toolchain_for_dir(&self, path: &Path) -> Result<(Toolchain, Option<OverrideReason>)> {
        self.find_override_toolchain_or_default(path)
            .and_then(|r| r.ok_or(Error::NoDefaultToolchain))
    }

    pub fn create_command_for_dir(&self, path: &Path, binary: &str) -> Result<Command> {
        let (toolchain, _) = try!(self.toolchain_for_dir(path));
        toolchain.create_command(binary)
    }

    pub fn doc_path_for_dir(&self, path: &Path, relative: &str) -> Result<PathBuf> {
        let (toolchain, _) = try!(self.toolchain_for_dir(path));
        toolchain.doc_path(relative)
    }

    pub fn open_docs_for_dir(&self, path: &Path, relative: &str) -> Result<()> {
        let (toolchain, _) = try!(self.toolchain_for_dir(path));
        toolchain.open_docs(relative)
    }
}
<commit_msg>Another workaround for Rust resolve bugs<commit_after>use std::path::{Path, PathBuf};
use std::borrow::Cow;
use std::env;
use std::io;
use std::process::Command;
use std::fmt::{self, Display};

use itertools::Itertools;

use errors::*;
use multirust_dist::{temp, dist};
use multirust_utils::utils;
use override_db::OverrideDB;
use toolchain::Toolchain;

// Note: multirust-rs jumped from 2 to 12 to leave multirust.sh room to diverge
pub const METADATA_VERSION: &'static str = "12";

#[derive(Debug)]
pub enum OverrideReason {
    Environment,
    OverrideDB(PathBuf),
}

impl Display for OverrideReason {
    fn fmt(&self, f: &mut fmt::Formatter) -> ::std::result::Result<(), fmt::Error> {
        match *self {
            OverrideReason::Environment => write!(f, "environment override by MULTIRUST_TOOLCHAIN"),
            OverrideReason::OverrideDB(ref path) => {
                write!(f, "directory override for '{}'", path.display())
            }
        }
    }
}

#[derive(Debug)]
pub struct Cfg {
    pub multirust_dir: PathBuf,
    pub version_file: PathBuf,
    pub override_db: OverrideDB,
    pub default_file: PathBuf,
    pub toolchains_dir: PathBuf,
    pub update_hash_dir: PathBuf,
    pub temp_cfg: temp::Cfg,
    pub gpg_key: Cow<'static, str>,
    pub env_override: Option<String>,
    pub dist_root_url: Cow<'static, str>,
    pub notify_handler: SharedNotifyHandler,
}

impl Cfg {
    pub fn from_env(notify_handler: SharedNotifyHandler) -> Result<Self> {
        // Get absolute home directory
        let data_dir = try!(utils::get_local_data_path());

        // Set up the multirust home directory
        let multirust_dir = env::var_os("MULTIRUST_HOME")
                                .and_then(utils::if_not_empty)
                                .map_or_else(|| data_dir.join(".multirust"), PathBuf::from);

        try!(utils::ensure_dir_exists("home", &multirust_dir, ntfy!(&notify_handler)));

        // Data locations
        let version_file = multirust_dir.join("version");
        let override_db = OverrideDB::new(multirust_dir.join("overrides"));
        let default_file = multirust_dir.join("default");
        let toolchains_dir = multirust_dir.join("toolchains");
        let update_hash_dir = multirust_dir.join("update-hashes");

        let notify_clone = notify_handler.clone();
        let temp_cfg = temp::Cfg::new(multirust_dir.join("tmp"),
                                      shared_ntfy!(move |n: temp::Notification| {
                                          notify_clone.call(Notification::Temp(n));
                                      }));

        // GPG key
        let gpg_key = if let Some(path) = env::var_os("MULTIRUST_GPG_KEY")
                                              .and_then(utils::if_not_empty) {
            Cow::Owned(try!(utils::read_file("public key", Path::new(&path))))
        } else {
            Cow::Borrowed(include_str!("rust-key.gpg.ascii"))
        };

        // Environment override
        let env_override = env::var("MULTIRUST_TOOLCHAIN")
                               .ok()
                               .and_then(utils::if_not_empty);

        let dist_root_url = env::var("MULTIRUST_DIST_ROOT")
                                .ok()
                                .and_then(utils::if_not_empty)
                                .map_or(Cow::Borrowed(dist::DEFAULT_DIST_ROOT), Cow::Owned);

        Ok(Cfg {
            multirust_dir: multirust_dir,
            version_file: version_file,
            override_db: override_db,
            default_file: default_file,
            toolchains_dir: toolchains_dir,
            update_hash_dir: update_hash_dir,
            temp_cfg: temp_cfg,
            gpg_key: gpg_key,
            notify_handler: notify_handler,
            env_override: env_override,
            dist_root_url: dist_root_url,
        })
    }

    pub fn set_default(&self, toolchain: &str) -> Result<()> {
        let work_file = try!(self.temp_cfg.new_file());

        try!(utils::write_file("temp", &work_file, toolchain));

        try!(utils::rename_file("default", &*work_file, &self.default_file));

        self.notify_handler.call(Notification::SetDefaultToolchain(toolchain));

        Ok(())
    }

    pub fn get_toolchain(&self, name: &str, create_parent: bool) -> Result<Toolchain> {
        if create_parent {
            try!(utils::ensure_dir_exists("toolchains",
                                          &self.toolchains_dir,
                                          ntfy!(&self.notify_handler)));
        }

        Ok(Toolchain::from(self, name))
    }

    pub fn verify_toolchain(&self, name: &str) -> Result<Toolchain> {
        let toolchain = try!(self.get_toolchain(name, false));
        try!(toolchain.verify());
        Ok(toolchain)
    }

    pub fn get_hash_file(&self, toolchain: &str, create_parent: bool) -> Result<PathBuf> {
        if create_parent {
            try!(utils::ensure_dir_exists("update-hash",
                                          &self.update_hash_dir,
                                          ntfy!(&self.notify_handler)));
        }

        Ok(self.update_hash_dir.join(toolchain))
    }

    pub fn which_binary(&self, path: &Path, binary: &str) -> Result<Option<PathBuf>> {

        if let Some((toolchain, _)) = try!(self.find_override_toolchain_or_default(path)) {
            Ok(Some(toolchain.binary_file(binary)))
        } else {
            Ok(None)
        }
    }

    pub fn upgrade_data(&self) -> Result<()> {
        if !utils::is_file(&self.version_file) {
            return Ok(());
        }

        let mut current_version = try!(utils::read_file("version", &self.version_file));
        let len = current_version.trim_right().len();
        current_version.truncate(len);

        if current_version == METADATA_VERSION {
            self.notify_handler
                .call(Notification::MetadataUpgradeNotNeeded(METADATA_VERSION));
            return Ok(());
        }

        self.notify_handler
            .call(Notification::UpgradingMetadata(&current_version, METADATA_VERSION));

        match &*current_version {
            "1" => {
                // This corresponds to an old version of multirust.sh.
                Err(Error::UnknownMetadataVersion(current_version))
            }
            "2" => {
                // The toolchain installation format changed. Just delete them all.
                self.notify_handler
                    .call(Notification::UpgradeRemovesToolchains);

                let dirs = try!(utils::read_dir("toolchains", &self.toolchains_dir));
                for dir in dirs {
                    let dir = try!(dir.map_err(|e| Error::UpgradeIoError(e)));
                    try!(utils::remove_dir("toolchain", &dir.path(),
                                           ::multirust_utils::NotifyHandler::some(&self.notify_handler)));
                }

                // Also delete the update hashes
                let files = try!(utils::read_dir("update hashes", &self.update_hash_dir));
                for file in files {
                    let file = try!(file.map_err(|e| Error::UpgradeIoError(e)));
                    try!(utils::remove_file("update hash", &file.path()));
                }

                try!(utils::write_file("version", &self.version_file, METADATA_VERSION));

                Ok(())
            }
            _ => Err(Error::UnknownMetadataVersion(current_version)),
        }
    }

    pub fn delete_data(&self) -> Result<()> {
        if utils::path_exists(&self.multirust_dir) {
            Ok(try!(utils::remove_dir("home", &self.multirust_dir, ntfy!(&self.notify_handler))))
        } else {
            Ok(())
        }
    }

    pub fn find_default(&self) -> Result<Option<Toolchain>> {
        if !utils::is_file(&self.default_file) {
            return Ok(None);
        }
        let content = try!(utils::read_file("default", &self.default_file));
        let name = content.trim_matches('\n');
        if name.is_empty() {
            return Ok(None);
        }

        let toolchain = try!(self.verify_toolchain(name).map_err(|_| Error::ToolchainNotInstalled(name.to_string())));

        Ok(Some(toolchain))
    }

    pub fn find_override(&self, path: &Path) -> Result<Option<(Toolchain, OverrideReason)>> {
        if let Some(ref name) = self.env_override {
            let toolchain = try!(self.verify_toolchain(name).map_err(|_| Error::ToolchainNotInstalled(name.to_string())));

            return Ok(Some((toolchain, OverrideReason::Environment)));
        }

        if let Some((name, reason_path)) = try!(self.override_db
                                                    .find(path, self.notify_handler.as_ref())) {
            let toolchain = try!(self.verify_toolchain(&name).map_err(|_| Error::ToolchainNotInstalled(name.to_string())));
            return Ok(Some((toolchain, OverrideReason::OverrideDB(reason_path))));
        }

        Ok(None)
    }

    pub fn find_override_toolchain_or_default
        (&self,
         path: &Path)
         -> Result<Option<(Toolchain, Option<OverrideReason>)>> {
        Ok(if let Some((toolchain, reason)) = try!(self.find_override(path)) {
            Some((toolchain, Some(reason)))
        } else {
            try!(self.find_default()).map(|toolchain| (toolchain, None))
        })
    }

    pub fn list_toolchains(&self) -> Result<Vec<String>> {
        if utils::is_directory(&self.toolchains_dir) {
            let toolchains: Vec<_> = try!(utils::read_dir("toolchains", &self.toolchains_dir))
                                         .filter_map(io::Result::ok)
                                         .filter_map(|e| e.file_name().into_string().ok())
                                         .collect();

            Ok(toolchains)
        } else {
            Ok(Vec::new())
        }
    }

    pub fn update_all_channels(&self) -> Result<Vec<(String, Result<()>)>> {
        let mut toolchains = try!(self.list_toolchains());
        toolchains.sort();

        Ok(toolchains.into_iter()
                     .merge(["beta", "nightly", "stable"].into_iter().map(|s| (*s).to_owned()))
                     .dedup()
                     .filter(|name| {
                         dist::ToolchainDesc::from_str(&name).map(|d| d.is_tracking()).ok() == Some(true)
                     })
                     .map(|name| {
                         let result = self.get_toolchain(&name, true)
                                          .and_then(|t| t.install_from_dist());
                         if let Err(ref e) = result {
                             self.notify_handler.call(Notification::NonFatalError(e));
                         }
                         (name, result)
                     })
                     .collect())
    }

    pub fn check_metadata_version(&self) -> Result<()> {
        try!(utils::assert_is_directory(&self.multirust_dir));

        if !utils::is_file(&self.version_file) {
            self.notify_handler.call(Notification::WritingMetadataVersion(METADATA_VERSION));

            try!(utils::write_file("metadata version", &self.version_file, METADATA_VERSION));

            Ok(())
        } else {
            let current_version = try!(utils::read_file("metadata version", &self.version_file));

            self.notify_handler.call(Notification::ReadMetadataVersion(&current_version));

            if &*current_version == METADATA_VERSION {
                Ok(())
            } else {
                Err(Error::NeedMetadataUpgrade)
            }
        }
    }

    pub fn toolchain_for_dir(&self, path: &Path) -> Result<(Toolchain, Option<OverrideReason>)> {
        self.find_override_toolchain_or_default(path)
            .and_then(|r| r.ok_or(Error::NoDefaultToolchain))
    }

    pub fn create_command_for_dir(&self, path: &Path, binary: &str) -> Result<Command> {
        let (toolchain, _) = try!(self.toolchain_for_dir(path));
        toolchain.create_command(binary)
    }

    pub fn doc_path_for_dir(&self, path: &Path, relative: &str) -> Result<PathBuf> {
        let (toolchain, _) = try!(self.toolchain_for_dir(path));
        toolchain.doc_path(relative)
    }

    pub fn open_docs_for_dir(&self, path: &Path, relative: &str) -> Result<()> {
        let (toolchain, _) = try!(self.toolchain_for_dir(path));
        toolchain.open_docs(relative)
    }
}
<|endoftext|>


<commit_before>#![doc(html_favicon_url = "https://www.ruma.io/favicon.ico")]
#![doc(html_logo_url = "https://www.ruma.io/images/logo.png")]
//! Crate **ruma_identifiers** contains types for [Matrix](https://matrix.org/) identifiers
//! for events, rooms, room aliases, room versions, and users.

#![warn(rust_2018_idioms, missing_debug_implementations, missing_docs)]
#![cfg_attr(docsrs, feature(doc_cfg))]

// Renamed in `Cargo.toml` so we can have a serde feature.
// Rename it back here because `serde1` is ugly.
#[cfg(feature = "serde")]
extern crate serde1 as serde;

use std::convert::TryFrom;

#[cfg(feature = "serde")]
use serde::de::{self, Deserializer, Unexpected};

#[doc(inline)]
pub use crate::{
    crypto_algorithms::{DeviceKeyAlgorithm, EventEncryptionAlgorithm, SigningKeyAlgorithm},
    device_key_id::DeviceKeyId,
    event_id::EventId,
    key_id::{DeviceSigningKeyId, KeyId, ServerSigningKeyId, SigningKeyId},
    mxc_uri::MxcUri,
    opaque_ids::{DeviceId, DeviceIdBox, KeyName, KeyNameBox},
    room_alias_id::RoomAliasId,
    room_id::RoomId,
    room_id_or_room_alias_id::RoomIdOrAliasId,
    room_version_id::RoomVersionId,
    server_name::{ServerName, ServerNameBox},
    signatures::{DeviceSignatures, EntitySignatures, ServerSignatures, Signatures},
    user_id::UserId,
};
#[doc(inline)]
pub use ruma_identifiers_validation::error::Error;

#[macro_use]
mod macros;

pub mod user_id;

mod crypto_algorithms;
mod device_key_id;
mod event_id;
mod key_id;
mod mxc_uri;
mod opaque_ids;
mod room_alias_id;
mod room_id;
mod room_id_or_room_alias_id;
mod room_version_id;
mod server_name;
mod signatures;

/// Check whether a given string is a valid server name according to [the specification][].
///
/// [the specification]: https://matrix.org/docs/spec/appendices#server-name
#[deprecated = "Use the [`ServerName`](server_name/struct.ServerName.html) type instead."]
pub fn is_valid_server_name(name: &str) -> bool {
    <&ServerName>::try_from(name).is_ok()
}

/// Generates a random identifier localpart.
#[cfg(feature = "rand")]
fn generate_localpart(length: usize) -> Box<str> {
    use rand::Rng as _;
    rand::thread_rng()
        .sample_iter(&rand::distributions::Alphanumeric)
        .map(char::from)
        .take(length)
        .collect::<String>()
        .into_boxed_str()
}

/// Deserializes any type of id using the provided TryFrom implementation.
///
/// This is a helper function to reduce the boilerplate of the Deserialize implementations.
#[cfg(feature = "serde")]
fn deserialize_id<'de, D, T>(deserializer: D, expected_str: &str) -> Result<T, D::Error>
where
    D: Deserializer<'de>,
    T: for<'a> std::convert::TryFrom<&'a str>,
{
    ruma_serde::deserialize_cow_str(deserializer).and_then(|v| {
        T::try_from(&v).map_err(|_| de::Error::invalid_value(Unexpected::Str(&v), &expected_str))
    })
}

/// Shorthand for `Box::<DeviceId>::from`.
#[macro_export]
macro_rules! device_id {
    ($s:tt) => {
        ::std::boxed::Box::<$crate::DeviceId>::from($s)
    };
}

#[doc(hidden)]
pub use ruma_identifiers_macros as _macros;

/// Compile-time checked `DeviceKeyId` construction.
#[macro_export]
macro_rules! device_key_id {
    ($s:literal) => {
        $crate::_macros::device_key_id!($crate, $s)
    };
}

/// Compile-time checked `EventId` construction.
#[macro_export]
macro_rules! event_id {
    ($s:literal) => {
        $crate::_macros::event_id!($crate, $s)
    };
}

/// Compile-time checked `RoomAliasId` construction.
#[macro_export]
macro_rules! room_alias_id {
    ($s:literal) => {
        $crate::_macros::room_alias_id!($crate, $s)
    };
}

/// Compile-time checked `RoomId` construction.
#[macro_export]
macro_rules! room_id {
    ($s:literal) => {
        $crate::_macros::room_id!($crate, $s)
    };
}

/// Compile-time checked `RoomVersionId` construction.
#[macro_export]
macro_rules! room_version_id {
    ($s:literal) => {
        $crate::_macros::room_version_id!($crate, $s)
    };
}

/// Compile-time checked `ServerSigningKeyId` construction.
#[macro_export]
#[deprecated = "use server_signing_key_id!()"]
macro_rules! server_key_id {
    ($s:literal) => {
        $crate::_macros::server_signing_key_id!($crate, $s)
    };
}

/// Compile-time checked `ServerSigningKeyId` construction.
#[macro_export]
macro_rules! server_signing_key_id {
    ($s:literal) => {
        $crate::_macros::server_signing_key_id!($crate, $s)
    };
}

/// Compile-time checked `ServerName` construction.
#[macro_export]
macro_rules! server_name {
    ($s:literal) => {
        $crate::_macros::server_name!($crate, $s)
    };
}

/// Compile-time checked `MxcUri` construction.
#[macro_export]
macro_rules! mxc_uri {
    ($s:literal) => {
        $crate::_macros::mxc_uri!($crate, $s)
    };
}

/// Compile-time checked `UserId` construction.
#[macro_export]
macro_rules! user_id {
    ($s:literal) => {
        $crate::_macros::user_id!($crate, $s)
    };
}
<commit_msg>identifiers: Fix hidden _macros module being visible in rustdoc<commit_after>#![doc(html_favicon_url = "https://www.ruma.io/favicon.ico")]
#![doc(html_logo_url = "https://www.ruma.io/images/logo.png")]
//! Crate **ruma_identifiers** contains types for [Matrix](https://matrix.org/) identifiers
//! for events, rooms, room aliases, room versions, and users.

#![warn(rust_2018_idioms, missing_debug_implementations, missing_docs)]
#![cfg_attr(docsrs, feature(doc_cfg))]

// Renamed in `Cargo.toml` so we can have a serde feature.
// Rename it back here because `serde1` is ugly.
#[cfg(feature = "serde")]
extern crate serde1 as serde;

use std::convert::TryFrom;

#[cfg(feature = "serde")]
use serde::de::{self, Deserializer, Unexpected};

#[doc(inline)]
pub use crate::{
    crypto_algorithms::{DeviceKeyAlgorithm, EventEncryptionAlgorithm, SigningKeyAlgorithm},
    device_key_id::DeviceKeyId,
    event_id::EventId,
    key_id::{DeviceSigningKeyId, KeyId, ServerSigningKeyId, SigningKeyId},
    mxc_uri::MxcUri,
    opaque_ids::{DeviceId, DeviceIdBox, KeyName, KeyNameBox},
    room_alias_id::RoomAliasId,
    room_id::RoomId,
    room_id_or_room_alias_id::RoomIdOrAliasId,
    room_version_id::RoomVersionId,
    server_name::{ServerName, ServerNameBox},
    signatures::{DeviceSignatures, EntitySignatures, ServerSignatures, Signatures},
    user_id::UserId,
};
#[doc(inline)]
pub use ruma_identifiers_validation::error::Error;

#[macro_use]
mod macros;

pub mod user_id;

mod crypto_algorithms;
mod device_key_id;
mod event_id;
mod key_id;
mod mxc_uri;
mod opaque_ids;
mod room_alias_id;
mod room_id;
mod room_id_or_room_alias_id;
mod room_version_id;
mod server_name;
mod signatures;

/// Check whether a given string is a valid server name according to [the specification][].
///
/// [the specification]: https://matrix.org/docs/spec/appendices#server-name
#[deprecated = "Use the [`ServerName`](server_name/struct.ServerName.html) type instead."]
pub fn is_valid_server_name(name: &str) -> bool {
    <&ServerName>::try_from(name).is_ok()
}

/// Generates a random identifier localpart.
#[cfg(feature = "rand")]
fn generate_localpart(length: usize) -> Box<str> {
    use rand::Rng as _;
    rand::thread_rng()
        .sample_iter(&rand::distributions::Alphanumeric)
        .map(char::from)
        .take(length)
        .collect::<String>()
        .into_boxed_str()
}

/// Deserializes any type of id using the provided TryFrom implementation.
///
/// This is a helper function to reduce the boilerplate of the Deserialize implementations.
#[cfg(feature = "serde")]
fn deserialize_id<'de, D, T>(deserializer: D, expected_str: &str) -> Result<T, D::Error>
where
    D: Deserializer<'de>,
    T: for<'a> std::convert::TryFrom<&'a str>,
{
    ruma_serde::deserialize_cow_str(deserializer).and_then(|v| {
        T::try_from(&v).map_err(|_| de::Error::invalid_value(Unexpected::Str(&v), &expected_str))
    })
}

/// Shorthand for `Box::<DeviceId>::from`.
#[macro_export]
macro_rules! device_id {
    ($s:tt) => {
        ::std::boxed::Box::<$crate::DeviceId>::from($s)
    };
}

// A plain re-export shows up in rustdoc despite doc(hidden). Use a module instead.
// Bug report: https://github.com/rust-lang/rust/issues/83939
#[doc(hidden)]
pub mod _macros {
    pub use ruma_identifiers_macros::*;
}

/// Compile-time checked `DeviceKeyId` construction.
#[macro_export]
macro_rules! device_key_id {
    ($s:literal) => {
        $crate::_macros::device_key_id!($crate, $s)
    };
}

/// Compile-time checked `EventId` construction.
#[macro_export]
macro_rules! event_id {
    ($s:literal) => {
        $crate::_macros::event_id!($crate, $s)
    };
}

/// Compile-time checked `RoomAliasId` construction.
#[macro_export]
macro_rules! room_alias_id {
    ($s:literal) => {
        $crate::_macros::room_alias_id!($crate, $s)
    };
}

/// Compile-time checked `RoomId` construction.
#[macro_export]
macro_rules! room_id {
    ($s:literal) => {
        $crate::_macros::room_id!($crate, $s)
    };
}

/// Compile-time checked `RoomVersionId` construction.
#[macro_export]
macro_rules! room_version_id {
    ($s:literal) => {
        $crate::_macros::room_version_id!($crate, $s)
    };
}

/// Compile-time checked `ServerSigningKeyId` construction.
#[macro_export]
#[deprecated = "use server_signing_key_id!()"]
macro_rules! server_key_id {
    ($s:literal) => {
        $crate::_macros::server_signing_key_id!($crate, $s)
    };
}

/// Compile-time checked `ServerSigningKeyId` construction.
#[macro_export]
macro_rules! server_signing_key_id {
    ($s:literal) => {
        $crate::_macros::server_signing_key_id!($crate, $s)
    };
}

/// Compile-time checked `ServerName` construction.
#[macro_export]
macro_rules! server_name {
    ($s:literal) => {
        $crate::_macros::server_name!($crate, $s)
    };
}

/// Compile-time checked `MxcUri` construction.
#[macro_export]
macro_rules! mxc_uri {
    ($s:literal) => {
        $crate::_macros::mxc_uri!($crate, $s)
    };
}

/// Compile-time checked `UserId` construction.
#[macro_export]
macro_rules! user_id {
    ($s:literal) => {
        $crate::_macros::user_id!($crate, $s)
    };
}
<|endoftext|>


<commit_before>//  Copyright (C) 2020 Éloïs SANCHEZ.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program 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 Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

use anyhow::Result;
use std::process::{Command, Output};
use structopt::StructOpt;

const MIN_RUST_VERTION: &str = "1.50.0";
const NODE_VERSION: &str = "10.22.1";
const NVM_VERSION: &str = "0.35.3";

#[derive(StructOpt)]
struct DuniterXTask {
    #[structopt(long)]
    skip_npm: bool,
    #[structopt(subcommand)]
    command: DuniterXTaskCommand,
}

#[derive(StructOpt)]
enum DuniterXTaskCommand {
    Build {
        #[structopt(long)]
        production: bool,
    },
    Test,
}

fn main() -> Result<()> {
    let args = DuniterXTask::from_args();

    if !version_check::is_min_version(MIN_RUST_VERTION).unwrap_or(false)
        && exec_should_success(Command::new("rustup").args(&["update", "stable"])).is_err()
    {
        eprintln!(
                "Duniter requires Rust {} or higher. If you installed the Rust toolchain via rustup, please execute the command `rustup update stable`.",
                MIN_RUST_VERTION
            );
        std::process::exit(1);
    }
    Command::new("rustc").arg("--version").status()?;
    Command::new("cargo").arg("--version").status()?;

    if !args.skip_npm {
        println!("Check node version …");
        if exec_and_get_stdout(Command::new("node").arg("-v"))
            .unwrap_or_default()
            .trim_end()
            != format!("v{}", NODE_VERSION)
        {
            println!("Install node v{} …", NODE_VERSION);
            install_and_use_node_version()?;
        } else {
            println!("Node v{} already installed ✔", NODE_VERSION);
        }
    }
    match args.command {
        DuniterXTaskCommand::Build { production } => build(args.skip_npm, production),
        DuniterXTaskCommand::Test => test(args.skip_npm),
    }
}

fn install_and_use_node_version() -> Result<()> {
    if exec_should_success(Command::new("nvm").arg("--version")).is_err() {
        println!("Duniter need nvm to build, please install it: https://raw.githubusercontent.com/nvm-sh/nvm/v{}/install.sh", NVM_VERSION);
        std::process::abort();
    }
    exec_should_success(Command::new("nvm").args(&["install", NODE_VERSION]))?;
    exec_should_success(Command::new("nvm").args(&["use", NODE_VERSION]))
}

fn exec_and_get_stdout(command: &mut Command) -> Result<String> {
    let Output {
        status,
        stdout,
        stderr,
    } = command.output()?;

    if status.success() {
        Ok(String::from_utf8(stdout)
            .unwrap_or_else(|_| "Output is not a valid utf8 string".to_owned()))
    } else {
        eprintln!(
            "{}",
            String::from_utf8(stderr)
                .unwrap_or_else(|_| "Error message is not a valid utf8 string".to_owned())
        );
        std::process::exit(1);
    }
}

fn exec_should_success(command: &mut Command) -> Result<()> {
    if !command.status()?.success() {
        std::process::exit(1);
    } else {
        Ok(())
    }
}

fn build(skip_npm: bool, production: bool) -> Result<()> {
    if !skip_npm {
        exec_should_success(Command::new("npm").args(&["add", "duniter-ui"]))?;
        exec_should_success(
            Command::new("npm")
                .env("NEON_BUILD_RELEASE", "true")
                .arg("install"),
        )?;
        if production {
            exec_should_success(Command::new("npm").args(&["prune", "--production"]))?;
        }
    }
    println!("Build duniter-launcher …");
    exec_should_success(Command::new("cargo").args(&["build", "--release"]))?;
    std::fs::copy("target/release/duniter", "bin/duniter")?;
    Ok(())
}

fn test(skip_npm: bool) -> Result<()> {
    exec_should_success(Command::new("cargo").args(&["test", "--all"]))?;
    if !skip_npm {
        exec_should_success(Command::new("npm").arg("test"))?;
    }
    Ok(())
}
<commit_msg>[build] xtask: improve nvm error message<commit_after>//  Copyright (C) 2020 Éloïs SANCHEZ.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program 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 Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

use anyhow::Result;
use std::process::{Command, Output};
use structopt::StructOpt;

const MIN_RUST_VERTION: &str = "1.50.0";
const NODE_VERSION: &str = "10.22.1";
const NVM_VERSION: &str = "0.35.3";

#[derive(StructOpt)]
struct DuniterXTask {
    #[structopt(long)]
    skip_npm: bool,
    #[structopt(subcommand)]
    command: DuniterXTaskCommand,
}

#[derive(StructOpt)]
enum DuniterXTaskCommand {
    Build {
        #[structopt(long)]
        production: bool,
    },
    Test,
}

fn main() -> Result<()> {
    let args = DuniterXTask::from_args();

    if !version_check::is_min_version(MIN_RUST_VERTION).unwrap_or(false)
        && exec_should_success(Command::new("rustup").args(&["update", "stable"])).is_err()
    {
        eprintln!(
                "Duniter requires Rust {} or higher. If you installed the Rust toolchain via rustup, please execute the command `rustup update stable`.",
                MIN_RUST_VERTION
            );
        std::process::exit(1);
    }
    Command::new("rustc").arg("--version").status()?;
    Command::new("cargo").arg("--version").status()?;

    if !args.skip_npm {
        println!("Check nvm …");
        if exec_should_success(Command::new("nvm").arg("--version")).is_err() {
            println!("Duniter requires nvm, but nvm is not found, please install it or add it to your PATH. Nvm installation link: https://raw.githubusercontent.com/nvm-sh/nvm/v{}/install.sh", NVM_VERSION);
            std::process::abort();
        }
        println!("Check node version …");
        if exec_and_get_stdout(Command::new("node").arg("-v"))
            .unwrap_or_default()
            .trim_end()
            != format!("v{}", NODE_VERSION)
        {
            println!("Install node v{} …", NODE_VERSION);
            install_and_use_node_version()?;
        } else {
            println!("Node v{} already installed ✔", NODE_VERSION);
        }
    }
    match args.command {
        DuniterXTaskCommand::Build { production } => build(args.skip_npm, production),
        DuniterXTaskCommand::Test => test(args.skip_npm),
    }
}

fn install_and_use_node_version() -> Result<()> {
    exec_should_success(Command::new("nvm").args(&["install", NODE_VERSION]))?;
    exec_should_success(Command::new("nvm").args(&["use", NODE_VERSION]))
}

fn exec_and_get_stdout(command: &mut Command) -> Result<String> {
    let Output {
        status,
        stdout,
        stderr,
    } = command.output()?;

    if status.success() {
        Ok(String::from_utf8(stdout)
            .unwrap_or_else(|_| "Output is not a valid utf8 string".to_owned()))
    } else {
        eprintln!(
            "{}",
            String::from_utf8(stderr)
                .unwrap_or_else(|_| "Error message is not a valid utf8 string".to_owned())
        );
        std::process::exit(1);
    }
}

fn exec_should_success(command: &mut Command) -> Result<()> {
    if !command.status()?.success() {
        std::process::exit(1);
    } else {
        Ok(())
    }
}

fn build(skip_npm: bool, production: bool) -> Result<()> {
    if !skip_npm {
        exec_should_success(Command::new("npm").args(&["add", "duniter-ui"]))?;
        exec_should_success(
            Command::new("npm")
                .env("NEON_BUILD_RELEASE", "true")
                .arg("install"),
        )?;
        if production {
            exec_should_success(Command::new("npm").args(&["prune", "--production"]))?;
        }
    }
    println!("Build duniter-launcher …");
    exec_should_success(Command::new("cargo").args(&["build", "--release"]))?;
    std::fs::copy("target/release/duniter", "bin/duniter")?;
    Ok(())
}

fn test(skip_npm: bool) -> Result<()> {
    exec_should_success(Command::new("cargo").args(&["test", "--all"]))?;
    if !skip_npm {
        exec_should_success(Command::new("npm").arg("test"))?;
    }
    Ok(())
}
<|endoftext|>


<commit_before>//! Rust entry point
//!
//! Boot is split into two parts
//!
//! + Early boot where we walk hardware data structures and setup initial
//!   machine state before
//! + Late boot where we are operating in the final kernel window and can
//!   setup the initial user environment
//!
//! Early boot is extremely architecture specific, late boot is mostly
//! generic.

use plat::*;
use vspace::*;
use panic::*;
use ::config::BootConfig;
use ::core::fmt::Write;
use ::core::marker::PhantomData;
use ::core::default::Default;
use super::halt::halt;
use super::vspace::*;
extern crate multiboot;

extern {
    /// Represent the start of the kernel image region. The type is not
    /// correct as there is no way to properly represent the type we want
    /// in Rust
    static kernel_image_start: u8;
    static kernel_image_end: u8;
}

/// Package of state that is passed to the early boot function
struct EarlyBootState<'h, 'l> {
    /// Referenece to the high window. Objects created from here can
    /// persist forever, and as such references to them can be returned
    /// in `PostEarlyBootState`
    high_window : &'h BootHighWindow<'h>,
    /// Reference to the low window. References to objects from here
    /// cannot be returned in `PostEarlyBootState`
    low_window : &'l BootLowWindow<'l>,
    /// The value of EAX passed from the assembly entry. This is checked
    /// to ensure we were multiboot loaded
    mbi_magic: usize,
    /// Raw physical pointer that should point to the multiboot structure
    mbi: *const usize,
}

/// Package of state that is returned as a result of early boot
struct PostEarlyBootState<'a> {
    /// An initialized platform interface
    plat: PlatInterfaceType,
    /// Currently the lifetime 'a is unused, so have some `PhantomData` to get
    /// around that
    phantom: PhantomData<&'a usize>,
}

/// Debug function to print out the contents of the multiboot information
fn display_multiboot<'a>(plat: &mut PlatInterfaceType, mbi: &'a multiboot::Multiboot<'a>) {
    write!(plat, "Multiboot information:\n").unwrap();
    if let Some(low) = mbi.lower_memory_bound() {
        write!(plat,"\t{}kb of low memory\n", low).unwrap();
    }
    if let Some(high) = mbi.upper_memory_bound() {
        write!(plat,"\t{}mb of high memory\n", high / 1024).unwrap();
    }
    if let Some(boot) = mbi.boot_device() {
        write!(plat,"\tBoot device {:?}\n", boot).unwrap();
    }
    if let Some(line) = mbi.command_line() {
        write!(plat,"\tCommand line \"{}\"\n", line).unwrap();
    }
    if let Some(modules) = mbi.modules() {
        write!(plat,"Multiboot modules:\n").unwrap();
        for m in modules {
            write!(plat,"\t{:?}\n", m).unwrap();
        }
    }
    if let Some(memory) = mbi.memory_regions() {
        write!(plat,"Memory regions:\n").unwrap();
        for m in memory {
            write!(plat,"\t{:?}\n", m).unwrap();
        }
    }
}

/// We create a wrapper struct because I don't know how else
/// to get the lifetime of the return value of callback function
/// to line up with the multiboot lifetime
struct MbiWrapper<'a> {
    mbi: Option<multiboot::Multiboot<'a>>,
    callback: &'a Fn(u64, usize) -> Option<&'a [u8]>,
}

fn make_bootconfig<'a>(mbi: &MbiWrapper<'a>) -> BootConfig<'a> {
    let mut cmdline = "";
    if let &Some(ref mbi) = &mbi.mbi {
        if let Some(cmd) = mbi.command_line() {
            cmdline = cmd;
        }
    }
    return BootConfig::new(cmdline);
}

/// Convert the kernel image start and end variables from the linker script
/// into useful values
fn get_kernel_image_region() -> (usize, usize) {
    (&kernel_image_start as *const u8 as usize, &kernel_image_end as *const u8 as usize)
}

/// Try and boot the system, potentially returning an error.
/// Since if we fail there is no way to display or do anything
/// with the error we do do not bother to have an error type
/// This is specifically the 'early' boot as it happens before
/// we switch to the final kernel address space
///
/// # Safety
///
/// Should only be called once during bootup with the correct
/// initial state
unsafe fn try_early_boot_system<'h, 'l>(init: EarlyBootState<'h, 'l>) -> Result<PostEarlyBootState<'h>, ()> {
    /* check that we are multi-booted */
    if init.mbi_magic as u64 != multiboot::SIGNATURE_RAX {
        return Err(());
    }
    /* construct a reference to the mbi to get the command line */
    let mut mbi = MbiWrapper{mbi: None, callback: &|p, s|
        Some(init.low_window.make_slice(p as usize, s))
        };
    /* Thanks to the stupidy in this function interface of requiring
     * an unsafe function we cannot pass a lambda expression. Since
     * a lambda cannot be made unsafe and we have no other way of
     * passing our state we just transmute the lambda. Unfortunately
     * this removes all type checking on our function :( */
    mbi.mbi = match multiboot::Multiboot::new(init.mbi as multiboot::PAddr, mbi.callback) {
        Some(mbi) => Some(mbi),
        None => {
                return Err(());
            }
        };
    let bootconfig = make_bootconfig(&mbi);
    /* Initial the serial output of our platform first so that
     * we can get debugging output. */
    let mut plat = get_platform(&bootconfig);
    plat.init_serial();
    write!(plat, "R4: In early setup\n").unwrap();
    let (ki_start, ki_end) = get_kernel_image_region();
    write!(plat, "Kernel imagine region {:x} {:x}\n", ki_start, ki_end).unwrap();
    if !init.high_window.range_valid(ki_start, ki_end - ki_start) {
        write!(plat, "Kernel image outside boot window!").unwrap();
        return Err(());
    }
    /* Initialize the panic function so we can see anything
     * really bad that happens */
    panic_set_plat(&mut plat);
    /* Now we can continue with the rest of init */
    if let &Some(ref mbi) = &mbi.mbi {
        display_multiboot(&mut plat, &mbi);
    }
    /* Construct early kernel allocator for memory stealing */
    /* Perform early platform specific system initialization */
    try!(plat.early_init());
    /* Do any platform device discovery */
//    try!(plat.early_device_discovery());
    /* Construct kernel window */

    Ok(PostEarlyBootState{ plat: plat, phantom: PhantomData })
}

/// Perform the rest of the system boot in the final kernel Window.
///
/// # Safety
///
/// Should only be called oncce during bootup. Assumes that the kernel
/// address space has been loaded and is currently active
unsafe fn try_boot_system() {
    /* Initialize CPU */
    /* Initialize other system state? */
    /* Perform any post cpu platform init */
    /* Load initial user thread. Part of this will have to be
     * moved earlier as the data for this is in the early boot
     * window :(. Worry about this later */
}

/// Rust entry point for the kernel. This expects two parameters, one the
/// boot info magic, and the other a raw pointer to the boot info structure.
/// Additionally it expects that both the boot kernel windows are configured
/// correctly in the active address space root
#[no_mangle]
pub extern fn boot_system(magic: usize, mbi: *const usize) -> ! {
    /* This *will* be our final kernel window, but it is not our window
     * yet. We create it here so that our temporary high kernel window,
     * which is a subset of the final kernel window, can be constructed
     * from it as having the same lifetime. This allows any references
     * created from the boot high kernel window being able to live on
     * into the final kernel window */
    let final_window = unsafe{KernelWindow::default()};
    /* this variable will hold our system state as returned by early boot */
    let mut boot;
    {
        /* Construct our system state for boot */
        let boot_high_window;
        unsafe {
            boot_high_window = final_window.subwindow(BootHighWindow::default());
        }
        /* whilst doing early boot we can also access things in low memory */
        let boot_low_window = unsafe {BootLowWindow::default()};
        let boot_state = EarlyBootState {
            high_window: &boot_high_window,
            low_window: &boot_low_window,
            mbi_magic: magic,
            mbi: mbi,
        };
        boot = match unsafe{try_early_boot_system(boot_state)} {
            Err(_) => halt(),
            Ok(b) => b,
        };
    }
    /* The 'plat' definition got moved into boot. Reset the panic location */
    panic_set_plat(&mut boot.plat);
    /* Switch to kernel address space for this cluster */
    /* Now we can perform the rest of the system boot */
    unsafe{try_boot_system();}
    unimplemented!()
}
<commit_msg>Remove commment about the multiboot library that stopped being true after we forked it<commit_after>//! Rust entry point
//!
//! Boot is split into two parts
//!
//! + Early boot where we walk hardware data structures and setup initial
//!   machine state before
//! + Late boot where we are operating in the final kernel window and can
//!   setup the initial user environment
//!
//! Early boot is extremely architecture specific, late boot is mostly
//! generic.

use plat::*;
use vspace::*;
use panic::*;
use steal_mem::*;
use ::config::BootConfig;
use ::core::fmt::Write;
use ::core::marker::PhantomData;
use ::core::default::Default;
use super::halt::halt;
use super::vspace::*;
extern crate multiboot;

extern {
    /// Represent the start of the kernel image region. The type is not
    /// correct as there is no way to properly represent the type we want
    /// in Rust
    static kernel_image_start: u8;
    static kernel_image_end: u8;
}

/// Package of state that is passed to the early boot function
struct EarlyBootState<'h, 'l> {
    /// Referenece to the high window. Objects created from here can
    /// persist forever, and as such references to them can be returned
    /// in `PostEarlyBootState`
    high_window : &'h BootHighWindow<'h>,
    /// Reference to the low window. References to objects from here
    /// cannot be returned in `PostEarlyBootState`
    low_window : &'l BootLowWindow<'l>,
    /// The value of EAX passed from the assembly entry. This is checked
    /// to ensure we were multiboot loaded
    mbi_magic: usize,
    /// Raw physical pointer that should point to the multiboot structure
    mbi: *const usize,
}

/// Package of state that is returned as a result of early boot
struct PostEarlyBootState<'a> {
    /// An initialized platform interface
    plat: PlatInterfaceType,
    /// Currently the lifetime 'a is unused, so have some `PhantomData` to get
    /// around that
    phantom: PhantomData<&'a usize>,
}

/// Debug function to print out the contents of the multiboot information
fn display_multiboot<'a>(plat: &mut PlatInterfaceType, mbi: &'a multiboot::Multiboot<'a>) {
    write!(plat, "Multiboot information:\n").unwrap();
    if let Some(low) = mbi.lower_memory_bound() {
        write!(plat,"\t{}kb of low memory\n", low).unwrap();
    }
    if let Some(high) = mbi.upper_memory_bound() {
        write!(plat,"\t{}mb of high memory\n", high / 1024).unwrap();
    }
    if let Some(boot) = mbi.boot_device() {
        write!(plat,"\tBoot device {:?}\n", boot).unwrap();
    }
    if let Some(line) = mbi.command_line() {
        write!(plat,"\tCommand line \"{}\"\n", line).unwrap();
    }
    if let Some(modules) = mbi.modules() {
        write!(plat,"Multiboot modules:\n").unwrap();
        for m in modules {
            write!(plat,"\t{:?}\n", m).unwrap();
        }
    }
    if let Some(memory) = mbi.memory_regions() {
        write!(plat,"Memory regions:\n").unwrap();
        for m in memory {
            write!(plat,"\t{:?}\n", m).unwrap();
        }
    }
}

/// We create a wrapper struct because I don't know how else
/// to get the lifetime of the return value of callback function
/// to line up with the multiboot lifetime
struct MbiWrapper<'a> {
    mbi: Option<multiboot::Multiboot<'a>>,
    callback: &'a Fn(u64, usize) -> Option<&'a [u8]>,
}

fn make_bootconfig<'a>(mbi: &MbiWrapper<'a>) -> BootConfig<'a> {
    let mut cmdline = "";
    if let &Some(ref mbi) = &mbi.mbi {
        if let Some(cmd) = mbi.command_line() {
            cmdline = cmd;
        }
    }
    return BootConfig::new(cmdline);
}

/// Convert the kernel image start and end variables from the linker script
/// into useful values
fn get_kernel_image_region() -> (usize, usize) {
    (&kernel_image_start as *const u8 as usize, &kernel_image_end as *const u8 as usize)
}

/// Try and boot the system, potentially returning an error.
/// Since if we fail there is no way to display or do anything
/// with the error we do do not bother to have an error type
/// This is specifically the 'early' boot as it happens before
/// we switch to the final kernel address space
///
/// # Safety
///
/// Should only be called once during bootup with the correct
/// initial state
unsafe fn try_early_boot_system<'h, 'l>(init: EarlyBootState<'h, 'l>) -> Result<PostEarlyBootState<'h>, ()> {
    /* check that we are multi-booted */
    if init.mbi_magic as u64 != multiboot::SIGNATURE_RAX {
        return Err(());
    }
    /* construct a reference to the mbi to get the command line */
    let mut mbi = MbiWrapper{mbi: None, callback: &|p, s|
        Some(init.low_window.make_slice(p as usize, s))
        };
    mbi.mbi = match multiboot::Multiboot::new(init.mbi as multiboot::PAddr, mbi.callback) {
        Some(mbi) => Some(mbi),
        None => {
                return Err(());
            }
        };
    let bootconfig = make_bootconfig(&mbi);
    /* Initial the serial output of our platform first so that
     * we can get debugging output. */
    let mut plat = get_platform(&bootconfig);
    plat.init_serial();
    write!(plat, "R4: In early setup\n").unwrap();
    let (ki_start, ki_end) = get_kernel_image_region();
    write!(plat, "Kernel imagine region {:x} {:x}\n", ki_start, ki_end).unwrap();
    if !init.high_window.range_valid(ki_start, ki_end - ki_start) {
        write!(plat, "Kernel image outside boot window!").unwrap();
        return Err(());
    }
    /* Initialize the panic function so we can see anything
     * really bad that happens */
    panic_set_plat(&mut plat);
    /* Now we can continue with the rest of init */
    if let &Some(ref mbi) = &mbi.mbi {
        display_multiboot(&mut plat, &mbi);
    }
    /* Construct early kernel allocator for memory stealing */
//    let early_alloc = StealMem::new(||
    /* Perform early platform specific system initialization */
    try!(plat.early_init());
    /* Do any platform device discovery */
//    try!(plat.early_device_discovery());
    /* Construct kernel window */

    Ok(PostEarlyBootState{ plat: plat, phantom: PhantomData })
}

/// Perform the rest of the system boot in the final kernel Window.
///
/// # Safety
///
/// Should only be called oncce during bootup. Assumes that the kernel
/// address space has been loaded and is currently active
unsafe fn try_boot_system() {
    /* Initialize CPU */
    /* Initialize other system state? */
    /* Perform any post cpu platform init */
    /* Load initial user thread. Part of this will have to be
     * moved earlier as the data for this is in the early boot
     * window :(. Worry about this later */
}

/// Rust entry point for the kernel. This expects two parameters, one the
/// boot info magic, and the other a raw pointer to the boot info structure.
/// Additionally it expects that both the boot kernel windows are configured
/// correctly in the active address space root
#[no_mangle]
pub extern fn boot_system(magic: usize, mbi: *const usize) -> ! {
    /* This *will* be our final kernel window, but it is not our window
     * yet. We create it here so that our temporary high kernel window,
     * which is a subset of the final kernel window, can be constructed
     * from it as having the same lifetime. This allows any references
     * created from the boot high kernel window being able to live on
     * into the final kernel window */
    let final_window = unsafe{KernelWindow::default()};
    /* this variable will hold our system state as returned by early boot */
    let mut boot;
    {
        /* Construct our system state for boot */
        let boot_high_window;
        unsafe {
            boot_high_window = final_window.subwindow(BootHighWindow::default());
        }
        /* whilst doing early boot we can also access things in low memory */
        let boot_low_window = unsafe {BootLowWindow::default()};
        let boot_state = EarlyBootState {
            high_window: &boot_high_window,
            low_window: &boot_low_window,
            mbi_magic: magic,
            mbi: mbi,
        };
        boot = match unsafe{try_early_boot_system(boot_state)} {
            Err(_) => halt(),
            Ok(b) => b,
        };
    }
    /* The 'plat' definition got moved into boot. Reset the panic location */
    panic_set_plat(&mut boot.plat);
    /* Switch to kernel address space for this cluster */
    /* Now we can perform the rest of the system boot */
    unsafe{try_boot_system();}
    unimplemented!()
}
<|endoftext|>


<commit_before>extern crate tiny;

use tiny::Tiny;

fn main() {
    Tiny::run("tiny_test".to_owned(), "tiny@tiny".to_owned(), "yada yada".to_owned());
}
<commit_msg>Fix hostname, some IRC servers doesn't accept @ in hostnames<commit_after>extern crate tiny;

use tiny::Tiny;

fn main() {
    Tiny::run("tiny_test".to_owned(), "tiny".to_owned(), "yada yada".to_owned());
}
<|endoftext|>


<commit_before>//! Types for the *m.room.pinned_events* event.

use ruma_events_macros::StateEventContent;
use ruma_identifiers::EventId;
use serde::{Deserialize, Serialize};

use crate::StateEvent;

/// Used to "pin" particular events in a room for other participants to review later.
pub type PinnedEventsEvent = StateEvent<PinnedEventsEventContent>;

/// The payload for `PinnedEventsEvent`.
#[derive(Clone, Debug, Deserialize, Serialize, StateEventContent)]
#[ruma_event(type = "m.room.pinned_events")]
pub struct PinnedEventsEventContent {
    /// An ordered list of event IDs to pin.
    pub pinned: Vec<EventId>,
}

#[cfg(test)]
mod tests {
    use std::{
        convert::TryFrom,
        time::{Duration, UNIX_EPOCH},
    };

    use ruma_common::Raw;
    use ruma_identifiers::{EventId, RoomId, ServerName, UserId};
    use serde_json::to_string;

    use super::PinnedEventsEventContent;
    use crate::{StateEvent, Unsigned};

    #[test]
    fn serialization_deserialization() {
        let mut content: PinnedEventsEventContent = PinnedEventsEventContent { pinned: Vec::new() };
        let server_name = <&ServerName>::try_from("example.com").unwrap();

        content.pinned.push(EventId::new(server_name));
        content.pinned.push(EventId::new(server_name));

        let event = StateEvent {
            content: content.clone(),
            event_id: EventId::new(server_name),
            origin_server_ts: UNIX_EPOCH + Duration::from_millis(1_432_804_485_886u64),
            prev_content: None,
            room_id: RoomId::new(server_name),
            sender: UserId::new(server_name),
            state_key: "".into(),
            unsigned: Unsigned::default(),
        };

        let serialized_event = to_string(&event).unwrap();
        let parsed_event =
            serde_json::from_str::<Raw<StateEvent<PinnedEventsEventContent>>>(&serialized_event)
                .unwrap()
                .deserialize()
                .unwrap();

        assert_eq!(parsed_event.event_id, event.event_id);
        assert_eq!(parsed_event.room_id, event.room_id);
        assert_eq!(parsed_event.sender, event.sender);
        assert_eq!(parsed_event.state_key, event.state_key);
        assert_eq!(parsed_event.origin_server_ts, event.origin_server_ts);

        assert_eq!(parsed_event.content.pinned, event.content.pinned);
        assert_eq!(parsed_event.content.pinned[0], content.pinned[0]);
        assert_eq!(parsed_event.content.pinned[1], content.pinned[1]);
    }
}
<commit_msg>Mark PinnedEventsEventContent as non_exhaustive<commit_after>//! Types for the *m.room.pinned_events* event.

use ruma_events_macros::StateEventContent;
use ruma_identifiers::EventId;
use serde::{Deserialize, Serialize};

use crate::StateEvent;

/// Used to "pin" particular events in a room for other participants to review later.
pub type PinnedEventsEvent = StateEvent<PinnedEventsEventContent>;

/// The payload for `PinnedEventsEvent`.
#[derive(Clone, Debug, Deserialize, Serialize, StateEventContent)]
#[non_exhaustive]
#[ruma_event(type = "m.room.pinned_events")]
pub struct PinnedEventsEventContent {
    /// An ordered list of event IDs to pin.
    pub pinned: Vec<EventId>,
}

impl PinnedEventsEventContent {
    /// Creates a new `PinnedEventsEventContent` with the given events.
    pub fn new(pinned: Vec<EventId>) -> Self {
        Self { pinned }
    }
}

#[cfg(test)]
mod tests {
    use std::{
        convert::TryFrom,
        time::{Duration, UNIX_EPOCH},
    };

    use ruma_common::Raw;
    use ruma_identifiers::{EventId, RoomId, ServerName, UserId};
    use serde_json::to_string;

    use super::PinnedEventsEventContent;
    use crate::{StateEvent, Unsigned};

    #[test]
    fn serialization_deserialization() {
        let mut content: PinnedEventsEventContent = PinnedEventsEventContent { pinned: Vec::new() };
        let server_name = <&ServerName>::try_from("example.com").unwrap();

        content.pinned.push(EventId::new(server_name));
        content.pinned.push(EventId::new(server_name));

        let event = StateEvent {
            content: content.clone(),
            event_id: EventId::new(server_name),
            origin_server_ts: UNIX_EPOCH + Duration::from_millis(1_432_804_485_886u64),
            prev_content: None,
            room_id: RoomId::new(server_name),
            sender: UserId::new(server_name),
            state_key: "".into(),
            unsigned: Unsigned::default(),
        };

        let serialized_event = to_string(&event).unwrap();
        let parsed_event =
            serde_json::from_str::<Raw<StateEvent<PinnedEventsEventContent>>>(&serialized_event)
                .unwrap()
                .deserialize()
                .unwrap();

        assert_eq!(parsed_event.event_id, event.event_id);
        assert_eq!(parsed_event.room_id, event.room_id);
        assert_eq!(parsed_event.sender, event.sender);
        assert_eq!(parsed_event.state_key, event.state_key);
        assert_eq!(parsed_event.origin_server_ts, event.origin_server_ts);

        assert_eq!(parsed_event.content.pinned, event.content.pinned);
        assert_eq!(parsed_event.content.pinned[0], content.pinned[0]);
        assert_eq!(parsed_event.content.pinned[1], content.pinned[1]);
    }
}
<|endoftext|>


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

//! Compilation of native dependencies like LLVM.
//!
//! Native projects like LLVM unfortunately aren't suited just yet for
//! compilation in build scripts that Cargo has. This is because thie
//! compilation takes a *very* long time but also because we don't want to
//! compile LLVM 3 times as part of a normal bootstrap (we want it cached).
//!
//! LLVM and compiler-rt are essentially just wired up to everything else to
//! ensure that they're always in place if needed.

use std::env;
use std::fs::{self, File};
use std::io::{Read, Write};
use std::path::Path;
use std::process::Command;

use build_helper::output;
use cmake;
use gcc;

use Build;
use util;
use build_helper::up_to_date;

/// Compile LLVM for `target`.
pub fn llvm(build: &Build, target: &str) {
    // If we're using a custom LLVM bail out here, but we can only use a
    // custom LLVM for the build triple.
    if let Some(config) = build.config.target_config.get(target) {
        if let Some(ref s) = config.llvm_config {
            return check_llvm_version(build, s);
        }
    }

    let rebuild_trigger = build.src.join("src/rustllvm/llvm-rebuild-trigger");
    let mut rebuild_trigger_contents = String::new();
    t!(t!(File::open(&rebuild_trigger)).read_to_string(&mut rebuild_trigger_contents));

    let out_dir = build.llvm_out(target);
    let done_stamp = out_dir.join("llvm-finished-building");
    if done_stamp.exists() {
        let mut done_contents = String::new();
        t!(t!(File::open(&done_stamp)).read_to_string(&mut done_contents));

        // If LLVM was already built previously and contents of the rebuild-trigger file
        // didn't change from the previous build, then no action is required.
        if done_contents == rebuild_trigger_contents {
            return
        }
    }
    if build.config.llvm_clean_rebuild {
        drop(fs::remove_dir_all(&out_dir));
    }

    println!("Building LLVM for {}", target);
    let _time = util::timeit();
    t!(fs::create_dir_all(&out_dir));

    // http://llvm.org/docs/CMake.html
    let mut cfg = cmake::Config::new(build.src.join("src/llvm"));
    if build.config.ninja {
        cfg.generator("Ninja");
    }

    let profile = match (build.config.llvm_optimize, build.config.llvm_release_debuginfo) {
        (false, _) => "Debug",
        (true, false) => "Release",
        (true, true) => "RelWithDebInfo",
    };

    // NOTE: remember to also update `config.toml.example` when changing the defaults!
    let llvm_targets = match build.config.llvm_targets {
        Some(ref s) => s,
        None => "X86;ARM;AArch64;Mips;PowerPC;SystemZ;JSBackend;MSP430;Sparc;NVPTX",
    };

    let assertions = if build.config.llvm_assertions {"ON"} else {"OFF"};

    cfg.target(target)
       .host(&build.config.build)
       .out_dir(&out_dir)
       .profile(profile)
       .define("LLVM_ENABLE_ASSERTIONS", assertions)
       .define("LLVM_TARGETS_TO_BUILD", llvm_targets)
       .define("LLVM_INCLUDE_EXAMPLES", "OFF")
       .define("LLVM_INCLUDE_TESTS", "OFF")
       .define("LLVM_INCLUDE_DOCS", "OFF")
       .define("LLVM_ENABLE_ZLIB", "OFF")
       .define("WITH_POLLY", "OFF")
       .define("LLVM_ENABLE_TERMINFO", "OFF")
       .define("LLVM_ENABLE_LIBEDIT", "OFF")
       .define("LLVM_PARALLEL_COMPILE_JOBS", build.jobs().to_string())
       .define("LLVM_TARGET_ARCH", target.split('-').next().unwrap())
       .define("LLVM_DEFAULT_TARGET_TRIPLE", target);

    if target.contains("msvc") {
        cfg.define("LLVM_USE_CRT_DEBUG", "MT");
        cfg.define("LLVM_USE_CRT_RELEASE", "MT");
        cfg.define("LLVM_USE_CRT_RELWITHDEBINFO", "MT");
    }

    if target.starts_with("i686") {
        cfg.define("LLVM_BUILD_32_BITS", "ON");
    }

    if let Some(num_linkers) = build.config.llvm_link_jobs {
        if num_linkers > 0 {
            cfg.define("LLVM_PARALLEL_LINK_JOBS", num_linkers.to_string());
        }
    }

    // http://llvm.org/docs/HowToCrossCompileLLVM.html
    if target != build.config.build {
        // FIXME: if the llvm root for the build triple is overridden then we
        //        should use llvm-tblgen from there, also should verify that it
        //        actually exists most of the time in normal installs of LLVM.
        let host = build.llvm_out(&build.config.build).join("bin/llvm-tblgen");
        cfg.define("CMAKE_CROSSCOMPILING", "True")
           .define("LLVM_TABLEGEN", &host);
    }

    // MSVC handles compiler business itself
    if !target.contains("msvc") {
        if let Some(ref ccache) = build.config.ccache {
           cfg.define("CMAKE_C_COMPILER", ccache)
              .define("CMAKE_C_COMPILER_ARG1", build.cc(target))
              .define("CMAKE_CXX_COMPILER", ccache)
              .define("CMAKE_CXX_COMPILER_ARG1", build.cxx(target));
        } else {
           cfg.define("CMAKE_C_COMPILER", build.cc(target))
              .define("CMAKE_CXX_COMPILER", build.cxx(target));
        }
        cfg.build_arg("-j").build_arg(build.jobs().to_string());

        cfg.define("CMAKE_C_FLAGS", build.cflags(target).join(" "));
        cfg.define("CMAKE_CXX_FLAGS", build.cflags(target).join(" "));
    }

    if env::var_os("SCCACHE_ERROR_LOG").is_some() {
        cfg.env("RUST_LOG", "sccache=info");
    }

    // FIXME: we don't actually need to build all LLVM tools and all LLVM
    //        libraries here, e.g. we just want a few components and a few
    //        tools. Figure out how to filter them down and only build the right
    //        tools and libs on all platforms.
    cfg.build();

    t!(t!(File::create(&done_stamp)).write_all(rebuild_trigger_contents.as_bytes()));
}

fn check_llvm_version(build: &Build, llvm_config: &Path) {
    if !build.config.llvm_version_check {
        return
    }

    let mut cmd = Command::new(llvm_config);
    let version = output(cmd.arg("--version"));
    if version.starts_with("3.5") || version.starts_with("3.6") ||
       version.starts_with("3.7") {
        return
    }
    panic!("\n\nbad LLVM version: {}, need >=3.5\n\n", version)
}

/// Compiles the `rust_test_helpers.c` library which we used in various
/// `run-pass` test suites for ABI testing.
pub fn test_helpers(build: &Build, target: &str) {
    let dst = build.test_helpers_out(target);
    let src = build.src.join("src/rt/rust_test_helpers.c");
    if up_to_date(&src, &dst.join("librust_test_helpers.a")) {
        return
    }

    println!("Building test helpers");
    t!(fs::create_dir_all(&dst));
    let mut cfg = gcc::Config::new();

    // We may have found various cross-compilers a little differently due to our
    // extra configuration, so inform gcc of these compilers. Note, though, that
    // on MSVC we still need gcc's detection of env vars (ugh).
    if !target.contains("msvc") {
        if let Some(ar) = build.ar(target) {
            cfg.archiver(ar);
        }
        cfg.compiler(build.cc(target));
    }

    cfg.cargo_metadata(false)
       .out_dir(&dst)
       .target(target)
       .host(&build.config.build)
       .opt_level(0)
       .debug(false)
       .file(build.src.join("src/rt/rust_test_helpers.c"))
       .compile("librust_test_helpers.a");
}
const OPENSSL_VERS: &'static str = "1.0.2k";
const OPENSSL_SHA256: &'static str =
    "6b3977c61f2aedf0f96367dcfb5c6e578cf37e7b8d913b4ecb6643c3cb88d8c0";

pub fn openssl(build: &Build, target: &str) {
    let out = match build.openssl_dir(target) {
        Some(dir) => dir,
        None => return,
    };

    let stamp = out.join(".stamp");
    let mut contents = String::new();
    drop(File::open(&stamp).and_then(|mut f| f.read_to_string(&mut contents)));
    if contents == OPENSSL_VERS {
        return
    }
    t!(fs::create_dir_all(&out));

    let name = format!("openssl-{}.tar.gz", OPENSSL_VERS);
    let tarball = out.join(&name);
    if !tarball.exists() {
        let tmp = tarball.with_extension("tmp");
        // originally from https://www.openssl.org/source/...
        let url = format!("https://s3.amazonaws.com/rust-lang-ci/rust-ci-mirror/{}",
                          name);
        let mut ok = false;
        for _ in 0..3 {
            let status = Command::new("curl")
                            .arg("-o").arg(&tmp)
                            .arg(&url)
                            .status()
                            .expect("failed to spawn curl");
            if status.success() {
                ok = true;
                break
            }
        }
        if !ok {
            panic!("failed to download openssl source")
        }
        let mut shasum = if target.contains("apple") {
            let mut cmd = Command::new("shasum");
            cmd.arg("-a").arg("256");
            cmd
        } else {
            Command::new("sha256sum")
        };
        let output = output(&mut shasum.arg(&tmp));
        let found = output.split_whitespace().next().unwrap();
        if found != OPENSSL_SHA256 {
            panic!("downloaded openssl sha256 different\n\
                    expected: {}\n\
                    found:    {}\n", OPENSSL_SHA256, found);
        }
        t!(fs::rename(&tmp, &tarball));
    }
    let obj = out.join(format!("openssl-{}", OPENSSL_VERS));
    let dst = build.openssl_install_dir(target).unwrap();
    drop(fs::remove_dir_all(&obj));
    drop(fs::remove_dir_all(&dst));
    build.run(Command::new("tar").arg("xf").arg(&tarball).current_dir(&out));

    let mut configure = Command::new(obj.join("Configure"));
    configure.arg(format!("--prefix={}", dst.display()));
    configure.arg("no-dso");
    configure.arg("no-ssl2");
    configure.arg("no-ssl3");

    let os = match target {
        "aarch64-unknown-linux-gnu" => "linux-aarch64",
        "arm-unknown-linux-gnueabi" => "linux-armv4",
        "arm-unknown-linux-gnueabihf" => "linux-armv4",
        "armv7-unknown-linux-gnueabihf" => "linux-armv4",
        "i686-apple-darwin" => "darwin-i386-cc",
        "i686-unknown-freebsd" => "BSD-x86-elf",
        "i686-unknown-linux-gnu" => "linux-elf",
        "i686-unknown-linux-musl" => "linux-elf",
        "mips-unknown-linux-gnu" => "linux-mips32",
        "mips64-unknown-linux-gnuabi64" => "linux64-mips64",
        "mips64el-unknown-linux-gnuabi64" => "linux64-mips64",
        "mipsel-unknown-linux-gnu" => "linux-mips32",
        "powerpc-unknown-linux-gnu" => "linux-ppc",
        "powerpc64-unknown-linux-gnu" => "linux-ppc64",
        "powerpc64le-unknown-linux-gnu" => "linux-ppc64le",
        "s390x-unknown-linux-gnu" => "linux64-s390x",
        "x86_64-apple-darwin" => "darwin64-x86_64-cc",
        "x86_64-unknown-freebsd" => "BSD-x86_64",
        "x86_64-unknown-linux-gnu" => "linux-x86_64",
        "x86_64-unknown-linux-musl" => "linux-x86_64",
        "x86_64-unknown-netbsd" => "BSD-x86_64",
        _ => panic!("don't know how to configure OpenSSL for {}", target),
    };
    configure.arg(os);
    configure.env("CC", build.cc(target));
    for flag in build.cflags(target) {
        configure.arg(flag);
    }
    configure.current_dir(&obj);
    println!("Configuring openssl for {}", target);
    build.run_quiet(&mut configure);
    println!("Building openssl for {}", target);
    build.run_quiet(Command::new("make").current_dir(&obj));
    println!("Installing openssl for {}", target);
    build.run_quiet(Command::new("make").arg("install").current_dir(&obj));

    let mut f = t!(File::create(&stamp));
    t!(f.write_all(OPENSSL_VERS.as_bytes()));
}
<commit_msg>Auto merge of #41563 - aidanhs:aphs-fix-spurious-osx-openssl-failure, r=alexcrichton<commit_after>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Compilation of native dependencies like LLVM.
//!
//! Native projects like LLVM unfortunately aren't suited just yet for
//! compilation in build scripts that Cargo has. This is because thie
//! compilation takes a *very* long time but also because we don't want to
//! compile LLVM 3 times as part of a normal bootstrap (we want it cached).
//!
//! LLVM and compiler-rt are essentially just wired up to everything else to
//! ensure that they're always in place if needed.

use std::env;
use std::fs::{self, File};
use std::io::{Read, Write};
use std::path::Path;
use std::process::Command;

use build_helper::output;
use cmake;
use gcc;

use Build;
use util;
use build_helper::up_to_date;

/// Compile LLVM for `target`.
pub fn llvm(build: &Build, target: &str) {
    // If we're using a custom LLVM bail out here, but we can only use a
    // custom LLVM for the build triple.
    if let Some(config) = build.config.target_config.get(target) {
        if let Some(ref s) = config.llvm_config {
            return check_llvm_version(build, s);
        }
    }

    let rebuild_trigger = build.src.join("src/rustllvm/llvm-rebuild-trigger");
    let mut rebuild_trigger_contents = String::new();
    t!(t!(File::open(&rebuild_trigger)).read_to_string(&mut rebuild_trigger_contents));

    let out_dir = build.llvm_out(target);
    let done_stamp = out_dir.join("llvm-finished-building");
    if done_stamp.exists() {
        let mut done_contents = String::new();
        t!(t!(File::open(&done_stamp)).read_to_string(&mut done_contents));

        // If LLVM was already built previously and contents of the rebuild-trigger file
        // didn't change from the previous build, then no action is required.
        if done_contents == rebuild_trigger_contents {
            return
        }
    }
    if build.config.llvm_clean_rebuild {
        drop(fs::remove_dir_all(&out_dir));
    }

    println!("Building LLVM for {}", target);
    let _time = util::timeit();
    t!(fs::create_dir_all(&out_dir));

    // http://llvm.org/docs/CMake.html
    let mut cfg = cmake::Config::new(build.src.join("src/llvm"));
    if build.config.ninja {
        cfg.generator("Ninja");
    }

    let profile = match (build.config.llvm_optimize, build.config.llvm_release_debuginfo) {
        (false, _) => "Debug",
        (true, false) => "Release",
        (true, true) => "RelWithDebInfo",
    };

    // NOTE: remember to also update `config.toml.example` when changing the defaults!
    let llvm_targets = match build.config.llvm_targets {
        Some(ref s) => s,
        None => "X86;ARM;AArch64;Mips;PowerPC;SystemZ;JSBackend;MSP430;Sparc;NVPTX",
    };

    let assertions = if build.config.llvm_assertions {"ON"} else {"OFF"};

    cfg.target(target)
       .host(&build.config.build)
       .out_dir(&out_dir)
       .profile(profile)
       .define("LLVM_ENABLE_ASSERTIONS", assertions)
       .define("LLVM_TARGETS_TO_BUILD", llvm_targets)
       .define("LLVM_INCLUDE_EXAMPLES", "OFF")
       .define("LLVM_INCLUDE_TESTS", "OFF")
       .define("LLVM_INCLUDE_DOCS", "OFF")
       .define("LLVM_ENABLE_ZLIB", "OFF")
       .define("WITH_POLLY", "OFF")
       .define("LLVM_ENABLE_TERMINFO", "OFF")
       .define("LLVM_ENABLE_LIBEDIT", "OFF")
       .define("LLVM_PARALLEL_COMPILE_JOBS", build.jobs().to_string())
       .define("LLVM_TARGET_ARCH", target.split('-').next().unwrap())
       .define("LLVM_DEFAULT_TARGET_TRIPLE", target);

    if target.contains("msvc") {
        cfg.define("LLVM_USE_CRT_DEBUG", "MT");
        cfg.define("LLVM_USE_CRT_RELEASE", "MT");
        cfg.define("LLVM_USE_CRT_RELWITHDEBINFO", "MT");
    }

    if target.starts_with("i686") {
        cfg.define("LLVM_BUILD_32_BITS", "ON");
    }

    if let Some(num_linkers) = build.config.llvm_link_jobs {
        if num_linkers > 0 {
            cfg.define("LLVM_PARALLEL_LINK_JOBS", num_linkers.to_string());
        }
    }

    // http://llvm.org/docs/HowToCrossCompileLLVM.html
    if target != build.config.build {
        // FIXME: if the llvm root for the build triple is overridden then we
        //        should use llvm-tblgen from there, also should verify that it
        //        actually exists most of the time in normal installs of LLVM.
        let host = build.llvm_out(&build.config.build).join("bin/llvm-tblgen");
        cfg.define("CMAKE_CROSSCOMPILING", "True")
           .define("LLVM_TABLEGEN", &host);
    }

    // MSVC handles compiler business itself
    if !target.contains("msvc") {
        if let Some(ref ccache) = build.config.ccache {
           cfg.define("CMAKE_C_COMPILER", ccache)
              .define("CMAKE_C_COMPILER_ARG1", build.cc(target))
              .define("CMAKE_CXX_COMPILER", ccache)
              .define("CMAKE_CXX_COMPILER_ARG1", build.cxx(target));
        } else {
           cfg.define("CMAKE_C_COMPILER", build.cc(target))
              .define("CMAKE_CXX_COMPILER", build.cxx(target));
        }
        cfg.build_arg("-j").build_arg(build.jobs().to_string());

        cfg.define("CMAKE_C_FLAGS", build.cflags(target).join(" "));
        cfg.define("CMAKE_CXX_FLAGS", build.cflags(target).join(" "));
    }

    if env::var_os("SCCACHE_ERROR_LOG").is_some() {
        cfg.env("RUST_LOG", "sccache=info");
    }

    // FIXME: we don't actually need to build all LLVM tools and all LLVM
    //        libraries here, e.g. we just want a few components and a few
    //        tools. Figure out how to filter them down and only build the right
    //        tools and libs on all platforms.
    cfg.build();

    t!(t!(File::create(&done_stamp)).write_all(rebuild_trigger_contents.as_bytes()));
}

fn check_llvm_version(build: &Build, llvm_config: &Path) {
    if !build.config.llvm_version_check {
        return
    }

    let mut cmd = Command::new(llvm_config);
    let version = output(cmd.arg("--version"));
    if version.starts_with("3.5") || version.starts_with("3.6") ||
       version.starts_with("3.7") {
        return
    }
    panic!("\n\nbad LLVM version: {}, need >=3.5\n\n", version)
}

/// Compiles the `rust_test_helpers.c` library which we used in various
/// `run-pass` test suites for ABI testing.
pub fn test_helpers(build: &Build, target: &str) {
    let dst = build.test_helpers_out(target);
    let src = build.src.join("src/rt/rust_test_helpers.c");
    if up_to_date(&src, &dst.join("librust_test_helpers.a")) {
        return
    }

    println!("Building test helpers");
    t!(fs::create_dir_all(&dst));
    let mut cfg = gcc::Config::new();

    // We may have found various cross-compilers a little differently due to our
    // extra configuration, so inform gcc of these compilers. Note, though, that
    // on MSVC we still need gcc's detection of env vars (ugh).
    if !target.contains("msvc") {
        if let Some(ar) = build.ar(target) {
            cfg.archiver(ar);
        }
        cfg.compiler(build.cc(target));
    }

    cfg.cargo_metadata(false)
       .out_dir(&dst)
       .target(target)
       .host(&build.config.build)
       .opt_level(0)
       .debug(false)
       .file(build.src.join("src/rt/rust_test_helpers.c"))
       .compile("librust_test_helpers.a");
}
const OPENSSL_VERS: &'static str = "1.0.2k";
const OPENSSL_SHA256: &'static str =
    "6b3977c61f2aedf0f96367dcfb5c6e578cf37e7b8d913b4ecb6643c3cb88d8c0";

pub fn openssl(build: &Build, target: &str) {
    let out = match build.openssl_dir(target) {
        Some(dir) => dir,
        None => return,
    };

    let stamp = out.join(".stamp");
    let mut contents = String::new();
    drop(File::open(&stamp).and_then(|mut f| f.read_to_string(&mut contents)));
    if contents == OPENSSL_VERS {
        return
    }
    t!(fs::create_dir_all(&out));

    let name = format!("openssl-{}.tar.gz", OPENSSL_VERS);
    let tarball = out.join(&name);
    if !tarball.exists() {
        let tmp = tarball.with_extension("tmp");
        // originally from https://www.openssl.org/source/...
        let url = format!("https://s3.amazonaws.com/rust-lang-ci/rust-ci-mirror/{}",
                          name);
        let mut ok = false;
        for _ in 0..3 {
            let status = Command::new("curl")
                            .arg("-o").arg(&tmp)
                            .arg(&url)
                            .status()
                            .expect("failed to spawn curl");
            if status.success() {
                ok = true;
                break
            }
        }
        if !ok {
            panic!("failed to download openssl source")
        }
        let mut shasum = if target.contains("apple") {
            let mut cmd = Command::new("shasum");
            cmd.arg("-a").arg("256");
            cmd
        } else {
            Command::new("sha256sum")
        };
        let output = output(&mut shasum.arg(&tmp));
        let found = output.split_whitespace().next().unwrap();
        if found != OPENSSL_SHA256 {
            panic!("downloaded openssl sha256 different\n\
                    expected: {}\n\
                    found:    {}\n", OPENSSL_SHA256, found);
        }
        t!(fs::rename(&tmp, &tarball));
    }
    let obj = out.join(format!("openssl-{}", OPENSSL_VERS));
    let dst = build.openssl_install_dir(target).unwrap();
    drop(fs::remove_dir_all(&obj));
    drop(fs::remove_dir_all(&dst));
    build.run(Command::new("tar").arg("xf").arg(&tarball).current_dir(&out));

    let mut configure = Command::new(obj.join("Configure"));
    configure.arg(format!("--prefix={}", dst.display()));
    configure.arg("no-dso");
    configure.arg("no-ssl2");
    configure.arg("no-ssl3");

    let os = match target {
        "aarch64-unknown-linux-gnu" => "linux-aarch64",
        "arm-unknown-linux-gnueabi" => "linux-armv4",
        "arm-unknown-linux-gnueabihf" => "linux-armv4",
        "armv7-unknown-linux-gnueabihf" => "linux-armv4",
        "i686-apple-darwin" => "darwin-i386-cc",
        "i686-unknown-freebsd" => "BSD-x86-elf",
        "i686-unknown-linux-gnu" => "linux-elf",
        "i686-unknown-linux-musl" => "linux-elf",
        "mips-unknown-linux-gnu" => "linux-mips32",
        "mips64-unknown-linux-gnuabi64" => "linux64-mips64",
        "mips64el-unknown-linux-gnuabi64" => "linux64-mips64",
        "mipsel-unknown-linux-gnu" => "linux-mips32",
        "powerpc-unknown-linux-gnu" => "linux-ppc",
        "powerpc64-unknown-linux-gnu" => "linux-ppc64",
        "powerpc64le-unknown-linux-gnu" => "linux-ppc64le",
        "s390x-unknown-linux-gnu" => "linux64-s390x",
        "x86_64-apple-darwin" => "darwin64-x86_64-cc",
        "x86_64-unknown-freebsd" => "BSD-x86_64",
        "x86_64-unknown-linux-gnu" => "linux-x86_64",
        "x86_64-unknown-linux-musl" => "linux-x86_64",
        "x86_64-unknown-netbsd" => "BSD-x86_64",
        _ => panic!("don't know how to configure OpenSSL for {}", target),
    };
    configure.arg(os);
    configure.env("CC", build.cc(target));
    for flag in build.cflags(target) {
        configure.arg(flag);
    }
    configure.current_dir(&obj);
    println!("Configuring openssl for {}", target);
    build.run_quiet(&mut configure);
    println!("Building openssl for {}", target);
    build.run_quiet(Command::new("make").arg("-j1").current_dir(&obj));
    println!("Installing openssl for {}", target);
    build.run_quiet(Command::new("make").arg("install").current_dir(&obj));

    let mut f = t!(File::create(&stamp));
    t!(f.write_all(OPENSSL_VERS.as_bytes()));
}
<|endoftext|>