<commit_before>use std::collections::HashMap;
use std::env;
use std::fs::{self, File};
use std::io::prelude::*;
use std::iter::repeat;
use std::path::{Path, PathBuf};

use curl::http;
use git2;
use registry::{Registry, NewCrate, NewCrateDependency};
use term::color::BLACK;

use core::source::Source;
use core::{Package, SourceId};
use core::dependency::Kind;
use core::manifest::ManifestMetadata;
use ops;
use sources::{PathSource, RegistrySource};
use util::config;
use util::{CargoResult, human, ChainError, ToUrl};
use util::config::{Config, ConfigValue, Location};
use util::important_paths::find_root_manifest_for_cwd;

pub struct RegistryConfig {
    pub index: Option<String>,
    pub token: Option<String>,
}

pub fn publish(manifest_path: &Path,
               config: &Config,
               token: Option<String>,
               index: Option<String>,
               verify: bool) -> CargoResult<()> {
    let mut src = try!(PathSource::for_path(manifest_path.parent().unwrap(),
                                            config));
    try!(src.update());
    let pkg = try!(src.root_package());

    let (mut registry, reg_id) = try!(registry(config, token, index));
    try!(verify_dependencies(&pkg, &reg_id));

    // Prepare a tarball, with a non-surpressable warning if metadata
    // is missing since this is being put online.
    let tarball = try!(ops::package(manifest_path, config, verify,
                                    false, true)).unwrap();

    // Upload said tarball to the specified destination
    try!(config.shell().status("Uploading", pkg.package_id().to_string()));
    try!(transmit(&pkg, &tarball, &mut registry));

    Ok(())
}

fn verify_dependencies(pkg: &Package, registry_src: &SourceId)
                       -> CargoResult<()> {
    for dep in pkg.dependencies().iter() {
        if dep.source_id().is_path() {
            if dep.specified_req().is_none() {
                return Err(human(format!("all path dependencies must have \
                                          a version specified when \
                                          publishing.\n\
                                          dependency `{}` does not specify \
                                          a version", dep.name())))
            }
        } else if dep.source_id() != registry_src {
            return Err(human(format!("all dependencies must come from the \
                                      same source.\ndependency `{}` comes \
                                      from {} instead", dep.name(),
                                     dep.source_id())))
        }
    }
    Ok(())
}

fn transmit(pkg: &Package, tarball: &Path, registry: &mut Registry)
            -> CargoResult<()> {
    let deps = pkg.dependencies().iter().map(|dep| {
        NewCrateDependency {
            optional: dep.is_optional(),
            default_features: dep.uses_default_features(),
            name: dep.name().to_string(),
            features: dep.features().to_vec(),
            version_req: dep.version_req().to_string(),
            target: dep.only_for_platform().map(|s| s.to_string()),
            kind: match dep.kind() {
                Kind::Normal => "normal",
                Kind::Build => "build",
                Kind::Development => "dev",
            }.to_string(),
        }
    }).collect::<Vec<NewCrateDependency>>();
    let manifest = pkg.manifest();
    let ManifestMetadata {
        ref authors, ref description, ref homepage, ref documentation,
        ref keywords, ref readme, ref repository, ref license, ref license_file,
    } = *manifest.metadata();
    let readme = match *readme {
        Some(ref readme) => {
            let path = pkg.root().join(readme);
            let mut contents = String::new();
            try!(File::open(&path).and_then(|mut f| {
                f.read_to_string(&mut contents)
            }).chain_error(|| {
                human("failed to read the specified README")
            }));
            Some(contents)
        }
        None => None,
    };
    match *license_file {
        Some(ref file) => {
            if fs::metadata(&pkg.root().join(file)).is_err() {
                return Err(human(format!("the license file `{}` does not exist",
                                         file)))
            }
        }
        None => {}
    }
    registry.publish(&NewCrate {
        name: pkg.name().to_string(),
        vers: pkg.version().to_string(),
        deps: deps,
        features: pkg.summary().features().clone(),
        authors: authors.clone(),
        description: description.clone(),
        homepage: homepage.clone(),
        documentation: documentation.clone(),
        keywords: keywords.clone(),
        readme: readme,
        repository: repository.clone(),
        license: license.clone(),
        license_file: license_file.clone(),
    }, tarball).map_err(|e| {
        human(e.to_string())
    })
}

pub fn registry_configuration(config: &Config) -> CargoResult<RegistryConfig> {
    let index = try!(config.get_string("registry.index")).map(|p| p.0);
    let token = try!(config.get_string("registry.token")).map(|p| p.0);
    Ok(RegistryConfig { index: index, token: token })
}

pub fn registry(config: &Config,
                token: Option<String>,
                index: Option<String>) -> CargoResult<(Registry, SourceId)> {
    // Parse all configuration options
    let RegistryConfig {
        token: token_config,
        index: index_config,
    } = try!(registry_configuration(config));
    let token = token.or(token_config);
    let index = index.or(index_config).unwrap_or(RegistrySource::default_url());
    let index = try!(index.to_url().map_err(human));
    let sid = SourceId::for_registry(&index);
    let api_host = {
        let mut src = RegistrySource::new(&sid, config);
        try!(src.update().chain_error(|| {
            human(format!("Failed to update registry {}", index))
        }));
        (try!(src.config())).api
    };
    let handle = try!(http_handle(config));
    Ok((Registry::new_handle(api_host, token, handle), sid))
}

/// Create a new HTTP handle with appropriate global configuration for cargo.
pub fn http_handle(config: &Config) -> CargoResult<http::Handle> {
    // The timeout option for libcurl by default times out the entire transfer,
    // but we probably don't want this. Instead we only set timeouts for the
    // connect phase as well as a "low speed" timeout so if we don't receive
    // many bytes in a large-ish period of time then we time out.
    let handle = http::handle().timeout(0)
                               .connect_timeout(30_000 /* milliseconds */)
                               .low_speed_limit(10 /* bytes per second */)
                               .low_speed_timeout(30 /* seconds */);
    let handle = match try!(http_proxy(config)) {
        Some(proxy) => handle.proxy(proxy),
        None => handle,
    };
    let handle = match try!(http_timeout(config)) {
        Some(timeout) => handle.connect_timeout(timeout as usize)
                               .low_speed_timeout((timeout as usize) / 1000),
        None => handle,
    };
    Ok(handle)
}

/// Find an explicit HTTP proxy if one is available.
///
/// Favor cargo's `http.proxy`, then git's `http.proxy`. Proxies specified
/// via environment variables are picked up by libcurl.
fn http_proxy(config: &Config) -> CargoResult<Option<String>> {
    match try!(config.get_string("http.proxy")) {
        Some((s, _)) => return Ok(Some(s)),
        None => {}
    }
    match git2::Config::open_default() {
        Ok(cfg) => {
            match cfg.get_str("http.proxy") {
                Ok(s) => return Ok(Some(s.to_string())),
                Err(..) => {}
            }
        }
        Err(..) => {}
    }
    Ok(None)
}

/// Determine if an http proxy exists.
///
/// Checks the following for existence, in order:
///
/// * cargo's `http.proxy`
/// * git's `http.proxy`
/// * http_proxy env var
/// * HTTP_PROXY env var
/// * https_proxy env var
/// * HTTPS_PROXY env var
pub fn http_proxy_exists(config: &Config) -> CargoResult<bool> {
    if try!(http_proxy(config)).is_some() {
        Ok(true)
    } else {
        Ok(["http_proxy", "HTTP_PROXY",
           "https_proxy", "HTTPS_PROXY"].iter().any(|v| env::var(v).is_ok()))
    }
}

pub fn http_timeout(config: &Config) -> CargoResult<Option<i64>> {
    match try!(config.get_i64("http.timeout")) {
        Some((s, _)) => return Ok(Some(s)),
        None => {}
    }
    Ok(env::var("HTTP_TIMEOUT").ok().and_then(|s| s.parse().ok()))
}

pub fn registry_login(config: &Config, token: String) -> CargoResult<()> {
    let RegistryConfig { index, token: _ } = try!(registry_configuration(config));
    let mut map = HashMap::new();
    let p = config.cwd().to_path_buf();
    match index {
        Some(index) => {
            map.insert("index".to_string(), ConfigValue::String(index, p.clone()));
        }
        None => {}
    }
    map.insert("token".to_string(), ConfigValue::String(token, p));

    config::set_config(config, Location::Global, "registry",
                       ConfigValue::Table(map, PathBuf::from(".")))
}

pub struct OwnersOptions {
    pub krate: Option<String>,
    pub token: Option<String>,
    pub index: Option<String>,
    pub to_add: Option<Vec<String>>,
    pub to_remove: Option<Vec<String>>,
    pub list: bool,
}

pub fn modify_owners(config: &Config, opts: &OwnersOptions) -> CargoResult<()> {
    let name = match opts.krate {
        Some(ref name) => name.clone(),
        None => {
            let manifest_path = try!(find_root_manifest_for_cwd(None));
            let mut src = try!(PathSource::for_path(manifest_path.parent().unwrap(),
                                                    config));
            try!(src.update());
            let pkg = try!(src.root_package());
            pkg.name().to_string()
        }
    };

    let (mut registry, _) = try!(registry(config, opts.token.clone(),
                                          opts.index.clone()));

    match opts.to_add {
        Some(ref v) => {
            let v = v.iter().map(|s| &s[..]).collect::<Vec<_>>();
            try!(config.shell().status("Owner", format!("adding `{:#?}` to `{}`",
                                                        v, name)));
            try!(registry.add_owners(&name, &v).map_err(|e| {
                human(format!("failed to add owners: {}", e))
            }));
        }
        None => {}
    }

    match opts.to_remove {
        Some(ref v) => {
            let v = v.iter().map(|s| &s[..]).collect::<Vec<_>>();
            try!(config.shell().status("Owner", format!("removing `{:?}` from `{}`",
                                                        v, name)));
            try!(registry.remove_owners(&name, &v).map_err(|e| {
                human(format!("failed to add owners: {}", e))
            }));
        }
        None => {}
    }

    if opts.list {
        let owners = try!(registry.list_owners(&name).map_err(|e| {
            human(format!("failed to list owners: {}", e))
        }));
        for owner in owners.iter() {
            print!("{}", owner.login);
            match (owner.name.as_ref(), owner.email.as_ref()) {
                (Some(name), Some(email)) => println!(" ({} <{}>)", name, email),
                (Some(s), None) |
                (None, Some(s)) => println!(" ({})", s),
                (None, None) => println!(""),
            }
        }
    }

    Ok(())
}

pub fn yank(config: &Config,
            krate: Option<String>,
            version: Option<String>,
            token: Option<String>,
            index: Option<String>,
            undo: bool) -> CargoResult<()> {
    let name = match krate {
        Some(name) => name,
        None => {
            let manifest_path = try!(find_root_manifest_for_cwd(None));
            let mut src = try!(PathSource::for_path(manifest_path.parent().unwrap(),
                                                    config));
            try!(src.update());
            let pkg = try!(src.root_package());
            pkg.name().to_string()
        }
    };
    let version = match version {
        Some(v) => v,
        None => return Err(human("a version must be specified to yank"))
    };

    let (mut registry, _) = try!(registry(config, token, index));

    if undo {
        try!(config.shell().status("Unyank", format!("{}:{}", name, version)));
        try!(registry.unyank(&name, &version).map_err(|e| {
            human(format!("failed to undo a yank: {}", e))
        }));
    } else {
        try!(config.shell().status("Yank", format!("{}:{}", name, version)));
        try!(registry.yank(&name, &version).map_err(|e| {
            human(format!("failed to yank: {}", e))
        }));
    }

    Ok(())
}

pub fn search(query: &str, config: &Config, index: Option<String>) -> CargoResult<()> {
    fn truncate_with_ellipsis(s: &str, max_length: usize) -> String {
        if s.len() < max_length {
            s.to_string()
        } else {
            format!("{}…", &s[..max_length - 1])
        }
    }

    let (mut registry, _) = try!(registry(config, None, index));
    let crates = try!(registry.search(query).map_err(|e| {
        human(format!("failed to retrieve search results from the registry: {}", e))
    }));

    let list_items = crates.iter()
        .map(|krate| (
            format!("{} ({})", krate.name, krate.max_version),
            krate.description.as_ref().map(|desc|
                truncate_with_ellipsis(&desc.replace("\n", " "), 128))
        ))
        .collect::<Vec<_>>();
    let description_margin = list_items.iter()
        .map(|&(ref left, _)| left.len() + 4)
        .max()
        .unwrap_or(0);

    for (name, description) in list_items.into_iter() {
        let line = match description {
            Some(desc) => {
                let space = repeat(' ').take(description_margin - name.len())
                                       .collect::<String>();
                name.to_string() + &space + &desc
            }
            None => name
        };
        try!(config.shell().say(line, BLACK));
    }

    Ok(())
}
<commit_msg>Auto merge of #1808 - Gankro:msg, r=alexcrichton<commit_after>use std::collections::HashMap;
use std::env;
use std::fs::{self, File};
use std::io::prelude::*;
use std::iter::repeat;
use std::path::{Path, PathBuf};

use curl::http;
use git2;
use registry::{Registry, NewCrate, NewCrateDependency};
use term::color::BLACK;

use core::source::Source;
use core::{Package, SourceId};
use core::dependency::Kind;
use core::manifest::ManifestMetadata;
use ops;
use sources::{PathSource, RegistrySource};
use util::config;
use util::{CargoResult, human, ChainError, ToUrl};
use util::config::{Config, ConfigValue, Location};
use util::important_paths::find_root_manifest_for_cwd;

pub struct RegistryConfig {
    pub index: Option<String>,
    pub token: Option<String>,
}

pub fn publish(manifest_path: &Path,
               config: &Config,
               token: Option<String>,
               index: Option<String>,
               verify: bool) -> CargoResult<()> {
    let mut src = try!(PathSource::for_path(manifest_path.parent().unwrap(),
                                            config));
    try!(src.update());
    let pkg = try!(src.root_package());

    let (mut registry, reg_id) = try!(registry(config, token, index));
    try!(verify_dependencies(&pkg, &reg_id));

    // Prepare a tarball, with a non-surpressable warning if metadata
    // is missing since this is being put online.
    let tarball = try!(ops::package(manifest_path, config, verify,
                                    false, true)).unwrap();

    // Upload said tarball to the specified destination
    try!(config.shell().status("Uploading", pkg.package_id().to_string()));
    try!(transmit(&pkg, &tarball, &mut registry));

    Ok(())
}

fn verify_dependencies(pkg: &Package, registry_src: &SourceId)
                       -> CargoResult<()> {
    for dep in pkg.dependencies().iter() {
        if dep.source_id().is_path() {
            if dep.specified_req().is_none() {
                return Err(human(format!("all path dependencies must have \
                                          a version specified when \
                                          publishing.\n\
                                          dependency `{}` does not specify \
                                          a version", dep.name())))
            }
        } else if dep.source_id() != registry_src {
            return Err(human(format!("all dependencies must come from the \
                                      same source.\ndependency `{}` comes \
                                      from {} instead", dep.name(),
                                     dep.source_id())))
        }
    }
    Ok(())
}

fn transmit(pkg: &Package, tarball: &Path, registry: &mut Registry)
            -> CargoResult<()> {
    let deps = pkg.dependencies().iter().map(|dep| {
        NewCrateDependency {
            optional: dep.is_optional(),
            default_features: dep.uses_default_features(),
            name: dep.name().to_string(),
            features: dep.features().to_vec(),
            version_req: dep.version_req().to_string(),
            target: dep.only_for_platform().map(|s| s.to_string()),
            kind: match dep.kind() {
                Kind::Normal => "normal",
                Kind::Build => "build",
                Kind::Development => "dev",
            }.to_string(),
        }
    }).collect::<Vec<NewCrateDependency>>();
    let manifest = pkg.manifest();
    let ManifestMetadata {
        ref authors, ref description, ref homepage, ref documentation,
        ref keywords, ref readme, ref repository, ref license, ref license_file,
    } = *manifest.metadata();
    let readme = match *readme {
        Some(ref readme) => {
            let path = pkg.root().join(readme);
            let mut contents = String::new();
            try!(File::open(&path).and_then(|mut f| {
                f.read_to_string(&mut contents)
            }).chain_error(|| {
                human("failed to read the specified README")
            }));
            Some(contents)
        }
        None => None,
    };
    match *license_file {
        Some(ref file) => {
            if fs::metadata(&pkg.root().join(file)).is_err() {
                return Err(human(format!("the license file `{}` does not exist",
                                         file)))
            }
        }
        None => {}
    }
    registry.publish(&NewCrate {
        name: pkg.name().to_string(),
        vers: pkg.version().to_string(),
        deps: deps,
        features: pkg.summary().features().clone(),
        authors: authors.clone(),
        description: description.clone(),
        homepage: homepage.clone(),
        documentation: documentation.clone(),
        keywords: keywords.clone(),
        readme: readme,
        repository: repository.clone(),
        license: license.clone(),
        license_file: license_file.clone(),
    }, tarball).map_err(|e| {
        human(e.to_string())
    })
}

pub fn registry_configuration(config: &Config) -> CargoResult<RegistryConfig> {
    let index = try!(config.get_string("registry.index")).map(|p| p.0);
    let token = try!(config.get_string("registry.token")).map(|p| p.0);
    Ok(RegistryConfig { index: index, token: token })
}

pub fn registry(config: &Config,
                token: Option<String>,
                index: Option<String>) -> CargoResult<(Registry, SourceId)> {
    // Parse all configuration options
    let RegistryConfig {
        token: token_config,
        index: index_config,
    } = try!(registry_configuration(config));
    let token = token.or(token_config);
    let index = index.or(index_config).unwrap_or(RegistrySource::default_url());
    let index = try!(index.to_url().map_err(human));
    let sid = SourceId::for_registry(&index);
    let api_host = {
        let mut src = RegistrySource::new(&sid, config);
        try!(src.update().chain_error(|| {
            human(format!("Failed to update registry {}", index))
        }));
        (try!(src.config())).api
    };
    let handle = try!(http_handle(config));
    Ok((Registry::new_handle(api_host, token, handle), sid))
}

/// Create a new HTTP handle with appropriate global configuration for cargo.
pub fn http_handle(config: &Config) -> CargoResult<http::Handle> {
    // The timeout option for libcurl by default times out the entire transfer,
    // but we probably don't want this. Instead we only set timeouts for the
    // connect phase as well as a "low speed" timeout so if we don't receive
    // many bytes in a large-ish period of time then we time out.
    let handle = http::handle().timeout(0)
                               .connect_timeout(30_000 /* milliseconds */)
                               .low_speed_limit(10 /* bytes per second */)
                               .low_speed_timeout(30 /* seconds */);
    let handle = match try!(http_proxy(config)) {
        Some(proxy) => handle.proxy(proxy),
        None => handle,
    };
    let handle = match try!(http_timeout(config)) {
        Some(timeout) => handle.connect_timeout(timeout as usize)
                               .low_speed_timeout((timeout as usize) / 1000),
        None => handle,
    };
    Ok(handle)
}

/// Find an explicit HTTP proxy if one is available.
///
/// Favor cargo's `http.proxy`, then git's `http.proxy`. Proxies specified
/// via environment variables are picked up by libcurl.
fn http_proxy(config: &Config) -> CargoResult<Option<String>> {
    match try!(config.get_string("http.proxy")) {
        Some((s, _)) => return Ok(Some(s)),
        None => {}
    }
    match git2::Config::open_default() {
        Ok(cfg) => {
            match cfg.get_str("http.proxy") {
                Ok(s) => return Ok(Some(s.to_string())),
                Err(..) => {}
            }
        }
        Err(..) => {}
    }
    Ok(None)
}

/// Determine if an http proxy exists.
///
/// Checks the following for existence, in order:
///
/// * cargo's `http.proxy`
/// * git's `http.proxy`
/// * http_proxy env var
/// * HTTP_PROXY env var
/// * https_proxy env var
/// * HTTPS_PROXY env var
pub fn http_proxy_exists(config: &Config) -> CargoResult<bool> {
    if try!(http_proxy(config)).is_some() {
        Ok(true)
    } else {
        Ok(["http_proxy", "HTTP_PROXY",
           "https_proxy", "HTTPS_PROXY"].iter().any(|v| env::var(v).is_ok()))
    }
}

pub fn http_timeout(config: &Config) -> CargoResult<Option<i64>> {
    match try!(config.get_i64("http.timeout")) {
        Some((s, _)) => return Ok(Some(s)),
        None => {}
    }
    Ok(env::var("HTTP_TIMEOUT").ok().and_then(|s| s.parse().ok()))
}

pub fn registry_login(config: &Config, token: String) -> CargoResult<()> {
    let RegistryConfig { index, token: _ } = try!(registry_configuration(config));
    let mut map = HashMap::new();
    let p = config.cwd().to_path_buf();
    match index {
        Some(index) => {
            map.insert("index".to_string(), ConfigValue::String(index, p.clone()));
        }
        None => {}
    }
    map.insert("token".to_string(), ConfigValue::String(token, p));

    config::set_config(config, Location::Global, "registry",
                       ConfigValue::Table(map, PathBuf::from(".")))
}

pub struct OwnersOptions {
    pub krate: Option<String>,
    pub token: Option<String>,
    pub index: Option<String>,
    pub to_add: Option<Vec<String>>,
    pub to_remove: Option<Vec<String>>,
    pub list: bool,
}

pub fn modify_owners(config: &Config, opts: &OwnersOptions) -> CargoResult<()> {
    let name = match opts.krate {
        Some(ref name) => name.clone(),
        None => {
            let manifest_path = try!(find_root_manifest_for_cwd(None));
            let mut src = try!(PathSource::for_path(manifest_path.parent().unwrap(),
                                                    config));
            try!(src.update());
            let pkg = try!(src.root_package());
            pkg.name().to_string()
        }
    };

    let (mut registry, _) = try!(registry(config, opts.token.clone(),
                                          opts.index.clone()));

    match opts.to_add {
        Some(ref v) => {
            let v = v.iter().map(|s| &s[..]).collect::<Vec<_>>();
            try!(config.shell().status("Owner", format!("adding `{:#?}` to `{}`",
                                                        v, name)));
            try!(registry.add_owners(&name, &v).map_err(|e| {
                human(format!("failed to add owners: {}", e))
            }));
        }
        None => {}
    }

    match opts.to_remove {
        Some(ref v) => {
            let v = v.iter().map(|s| &s[..]).collect::<Vec<_>>();
            try!(config.shell().status("Owner", format!("removing `{:?}` from `{}`",
                                                        v, name)));
            try!(registry.remove_owners(&name, &v).map_err(|e| {
                human(format!("failed to remove owners: {}", e))
            }));
        }
        None => {}
    }

    if opts.list {
        let owners = try!(registry.list_owners(&name).map_err(|e| {
            human(format!("failed to list owners: {}", e))
        }));
        for owner in owners.iter() {
            print!("{}", owner.login);
            match (owner.name.as_ref(), owner.email.as_ref()) {
                (Some(name), Some(email)) => println!(" ({} <{}>)", name, email),
                (Some(s), None) |
                (None, Some(s)) => println!(" ({})", s),
                (None, None) => println!(""),
            }
        }
    }

    Ok(())
}

pub fn yank(config: &Config,
            krate: Option<String>,
            version: Option<String>,
            token: Option<String>,
            index: Option<String>,
            undo: bool) -> CargoResult<()> {
    let name = match krate {
        Some(name) => name,
        None => {
            let manifest_path = try!(find_root_manifest_for_cwd(None));
            let mut src = try!(PathSource::for_path(manifest_path.parent().unwrap(),
                                                    config));
            try!(src.update());
            let pkg = try!(src.root_package());
            pkg.name().to_string()
        }
    };
    let version = match version {
        Some(v) => v,
        None => return Err(human("a version must be specified to yank"))
    };

    let (mut registry, _) = try!(registry(config, token, index));

    if undo {
        try!(config.shell().status("Unyank", format!("{}:{}", name, version)));
        try!(registry.unyank(&name, &version).map_err(|e| {
            human(format!("failed to undo a yank: {}", e))
        }));
    } else {
        try!(config.shell().status("Yank", format!("{}:{}", name, version)));
        try!(registry.yank(&name, &version).map_err(|e| {
            human(format!("failed to yank: {}", e))
        }));
    }

    Ok(())
}

pub fn search(query: &str, config: &Config, index: Option<String>) -> CargoResult<()> {
    fn truncate_with_ellipsis(s: &str, max_length: usize) -> String {
        if s.len() < max_length {
            s.to_string()
        } else {
            format!("{}…", &s[..max_length - 1])
        }
    }

    let (mut registry, _) = try!(registry(config, None, index));
    let crates = try!(registry.search(query).map_err(|e| {
        human(format!("failed to retrieve search results from the registry: {}", e))
    }));

    let list_items = crates.iter()
        .map(|krate| (
            format!("{} ({})", krate.name, krate.max_version),
            krate.description.as_ref().map(|desc|
                truncate_with_ellipsis(&desc.replace("\n", " "), 128))
        ))
        .collect::<Vec<_>>();
    let description_margin = list_items.iter()
        .map(|&(ref left, _)| left.len() + 4)
        .max()
        .unwrap_or(0);

    for (name, description) in list_items.into_iter() {
        let line = match description {
            Some(desc) => {
                let space = repeat(' ').take(description_margin - name.len())
                                       .collect::<String>();
                name.to_string() + &space + &desc
            }
            None => name
        };
        try!(config.shell().say(line, BLACK));
    }

    Ok(())
}
<|endoftext|>


<commit_before>extern crate rand;
extern crate timely;
extern crate graph_map;
extern crate differential_dataflow;

use std::time::Instant;
use std::hash::Hash;
use std::mem;

use rand::{Rng, SeedableRng, StdRng};

use timely::dataflow::*;

use differential_dataflow::input::Input;
use differential_dataflow::Collection;
use differential_dataflow::operators::*;
use differential_dataflow::operators::arrange::{ArrangeByKey, ArrangeBySelf};
use differential_dataflow::lattice::Lattice;

use graph_map::GraphMMap;

type Node = u32;
type Edge = (Node, Node);

fn main() {

    // snag a filename to use for the input graph.
    let filename = std::env::args().nth(1).unwrap();
    let program = std::env::args().nth(2).unwrap();

    timely::execute_from_args(std::env::args().skip(1), move |worker| {

        let peers = worker.peers();
        let index = worker.index();

        // // What you might do if you used GraphMMap:
        let graph = GraphMMap::new(&filename);

        let (mut input, mut query, probe) = worker.dataflow::<u64,_,_>(|scope| {

            let (input, graph) = scope.new_collection();
            let (rootz, query) = scope.new_collection();

            let probe = match program.as_str() {
                "reach" => _reach(&graph, &query).probe(),
                "cc"    => _connected_components(&graph).probe(),
                "bfs"   => _bfs(&graph, &query).probe(),
                "pymk"  => _pymk(&graph, &query, 10).probe(),
                _       => panic!("must specify one of 'reach', 'cc', 'bfs'.")
            };

            (input, rootz, probe)
        });

        let timer = Instant::now();

        // start loading up the graph
        for node in 0..graph.nodes() {
            if node % peers == index {
                for &edge in graph.edges(node) {
                    input.insert((node as u32, edge));
                }
            }
        }

        // run until graph is loaded
        input.advance_to(1); input.flush();
        query.advance_to(1); query.flush();

        input.close();

        worker.step_while(|| probe.less_than(query.time()));

        if index == 0 {
            println!("loaded: {:?}", timer.elapsed());
        }

        // conduct latencies.capacity() measurements.
        let mut latencies = Vec::with_capacity(11);

        let seed: &[_] = &[1, 2, 3, 4];
        let mut rng: StdRng = SeedableRng::from_seed(seed);    // rng for edge additions

        for _count in 0..latencies.capacity() {
            let timer = Instant::now();
            if index == 0 {
                query.insert((rng.gen_range(0, graph.nodes() as u32)));
            }
            let next = query.epoch() + 1;
            // input.advance_to(next); input.flush();
            query.advance_to(next); query.flush();
            while probe.less_than(query.time()) { worker.step(); }
            latencies.push(timer.elapsed());
        }

        if index == 0 {
            for lat in &latencies {
                println!("latency: {:?}", lat);
            }
        }

    }).unwrap();
}

// returns pairs (root, friend-of-friend) for the top-k friends of friends by count.
fn _pymk<G: Scope>(edges: &Collection<G, Edge>, query: &Collection<G, Node>, k: usize) -> Collection<G, (Node,Node)>
where G::Timestamp: Lattice+Ord {

    // symmetrize the graph
    let edges = edges.map_in_place(|x: &mut (u32, u32)| ::std::mem::swap(&mut x.0, &mut x.1))
                     .concat(&edges);

    // "arrange" edges, because we'll want to use it twice the same way.
    let edges = edges.arrange_by_key();
    let query = query.arrange_by_self();

    // restrict attention to edges from query nodes
    edges.join_core(&query, |k,v,_| Some((v.clone(), k.clone())))
         .arrange_by_key()
         .join_core(&edges, |_,x,y| Some((x.clone(), y.clone())))
         // the next thing is the "topk" worker. sorry!
         .group(move |_,s,t| {
             t.extend(s.iter().map(|&(x,y)| (*x,y)));   // propose all inputs as outputs
             t.sort_by(|x,y| (-x.1).cmp(&(-y.1)));      // sort by negative count (large numbers first)
             t.truncate(k)                              // keep at most k of these
         })
 }

// returns pairs (n, s) indicating node n can be reached from a root in s steps.
fn _reach<G: Scope>(edges: &Collection<G, Edge>, query: &Collection<G, Node>) -> Collection<G, Node>
where G::Timestamp: Lattice+Ord {

    // initialize query as reaching themselves at distance 0
    // repeatedly update minimal distances each node can be reached from each root
    query.iterate(|inner| {

        let edges = edges.enter(&inner.scope());
        let nodes = query.enter(&inner.scope());

        // edges from active sources activate their destinations
        edges.semijoin(&inner)
             .map(|(_,d)| d)
             .concat(&nodes)
             .distinct()
    })
}

// returns pairs (node, label) indicating the connected component containing each node
fn _connected_components<G: Scope>(edges: &Collection<G, Edge>) -> Collection<G, (Node, Node)>
where G::Timestamp: Lattice+Hash+Ord {

    // each edge (x,y) means that we need at least a label for the min of x and y.
    let nodes = edges.map_in_place(|pair| {
                        let min = std::cmp::min(pair.0, pair.1);
                        *pair = (min, min);
                     })
                     .consolidate();

    // each edge should exist in both directions.
    let edges = edges.map_in_place(|x| mem::swap(&mut x.0, &mut x.1))
                     .concat(&edges);

    // don't actually use these labels, just grab the type
    nodes.filter(|_| false)
         .iterate(|inner| {
             let edges = edges.enter(&inner.scope());
             let nodes = nodes.enter_at(&inner.scope(), |r| 256 * (64 - r.0.leading_zeros() as u64));

             inner.join_map(&edges, |_k,l,d| (*d,*l))
                  .concat(&nodes)
                  .group(|_, s, t| { t.push((*s[0].0, 1)); } )
         })
}

// returns pairs (n, s) indicating node n can be reached from a root in s steps.
fn _bfs<G: Scope>(edges: &Collection<G, Edge>, query: &Collection<G, Node>) -> Collection<G, (Node, u32)>
where G::Timestamp: Lattice+Ord {

    // initialize query as reaching themselves at distance 0
    let nodes = query.map(|x| (x, 0));

    // repeatedly update minimal distances each node can be reached from each root
    nodes.iterate(|inner| {

        let edges = edges.enter(&inner.scope());
        let nodes = nodes.enter(&inner.scope());

        inner.join_map(&edges, |_k,l,d| (*d, l+1))
             .concat(&nodes)
             .group(|_, s, t| t.push((*s[0].0, 1)))
     })
}<commit_msg>updated for measurements<commit_after>extern crate rand;
extern crate timely;
extern crate graph_map;
extern crate differential_dataflow;

use std::time::Instant;
use std::hash::Hash;
use std::mem;

use rand::{Rng, SeedableRng, StdRng};

use timely::dataflow::*;

use differential_dataflow::input::Input;
use differential_dataflow::Collection;
use differential_dataflow::operators::*;
use differential_dataflow::operators::arrange::{ArrangeByKey, ArrangeBySelf};
use differential_dataflow::lattice::Lattice;

use graph_map::GraphMMap;

use differential_dataflow::trace::implementations::ord::OrdValSpine as DefaultValTrace;
use differential_dataflow::operators::arrange::TraceAgent;
use differential_dataflow::operators::arrange::Arranged;

type Arrange<G: Scope, K, V, R> = Arranged<G, K, V, R, TraceAgent<K, V, G::Timestamp, R, DefaultValTrace<K, V, G::Timestamp, R>>>;

type Node = u32;
// type Edge = (Node, Node);

fn main() {

    // snag a filename to use for the input graph.
    let filename = std::env::args().nth(1).unwrap();
    let program = std::env::args().nth(2).unwrap();

    timely::execute_from_args(std::env::args().skip(1), move |worker| {

        let peers = worker.peers();
        let index = worker.index();

        let (mut input, probe) = worker.dataflow::<(),_,_>(|scope| {

            let (input, graph) = scope.new_collection();
            // let (rootz, query) = scope.new_collection();

            // each edge should exist in both directions.
            let graph = graph.arrange_by_key();

            let probe = match program.as_str() {
                "tc"    => _tc(&graph).probe(),
                "sg"    => _sg(&graph).probe(),
                // "reach" => _reach(&graph, &query).probe(),
                // "cc"    => _connected_components(&graph).probe(),
                // "bfs"   => _bfs(&graph, &query).probe(),
                // "pymk"  => _pymk(&graph, &query, 10).probe(),
                _       => panic!("must specify one of 'tc', 'sg', reach', 'cc', 'bfs', 'pymk'.")
            };

            (input, probe)
        });

        let timer = Instant::now();

        let mut nodes = 0;
        if filename.ends_with(".txt") {

            use std::io::{BufReader, BufRead};
            use std::fs::File;

            let file = BufReader::new(File::open(filename.clone()).unwrap());
            for (count, readline) in file.lines().enumerate() {
                let line = readline.ok().expect("read error");
                if count % peers == index && !line.starts_with('#') {
                    let mut elts = line[..].split_whitespace();
                    let src: u32 = elts.next().unwrap().parse().ok().expect("malformed src");
                    let dst: u32 = elts.next().unwrap().parse().ok().expect("malformed dst");
                    if nodes < src { nodes = src; }
                    if nodes < dst { nodes = dst; }
                    input.insert((src, dst));
                }
            }
        }
        else {
            // What you might do if you used GraphMMap:
            let graph = GraphMMap::new(&filename);

            // start loading up the graph
            for node in 0..graph.nodes() {
                if node % peers == index {
                    for &edge in graph.edges(node) {
                        input.insert((node as u32, edge));
                    }
                }
            }
            nodes = graph.nodes() as u32;
        }

        println!("{:?}\tData ingested", timer.elapsed());

        // // run until graph is loaded
        // input.advance_to(1); input.flush();
        // query.advance_to(1); query.flush();

        // input.close();

        // worker.step_while(|| probe.less_than(query.time()));

        // if index == 0 {
        //     println!("{:?}\tData indexed", timer.elapsed());
        // }

        // // conduct latencies.capacity() measurements.
        // let mut latencies = Vec::with_capacity(11);

        // let seed: &[_] = &[1, 2, 3, 4];
        // let mut rng: StdRng = SeedableRng::from_seed(seed);    // rng for edge additions

        // for _count in 0..latencies.capacity() {
        //     let timer = Instant::now();
        //     if index == 0 {
        //         query.insert((rng.gen_range(0, nodes)));
        //     }
        //     let next = query.epoch() + 1;
        //     // input.advance_to(next); input.flush();
        //     query.advance_to(next); query.flush();
        //     while probe.less_than(query.time()) { worker.step(); }
        //     latencies.push(timer.elapsed());
        // }

        // if index == 0 {
        //     for lat in &latencies {
        //         println!("latency: {:?}", lat);
        //     }
        // }

    }).unwrap();
}

// // returns pairs (root, friend-of-friend) for the top-k friends of friends by count.
// fn _pymk<G: Scope>(edges: &Arrange<G, Node, Node, isize>, query: &Collection<G, Node>, k: usize) -> Collection<G, (Node,Node)>
// where G::Timestamp: Lattice+Ord {

//     let query = query.arrange_by_self();

//     // restrict attention to edges from query nodes
//     query.join_core(&edges, |k,_,v| Some((v.clone(), k.clone())))
//          .join_core(&edges, |_,x,y| Some((x.clone(), y.clone())))
//          // the next thing is the "topk" worker. sorry!
//          .group(move |_,s,t| {
//              t.extend(s.iter().map(|&(x,y)| (*x,y)));   // propose all inputs as outputs
//              t.sort_by(|x,y| (-x.1).cmp(&(-y.1)));      // sort by negative count (large numbers first)
//              t.truncate(k)                              // keep at most k of these
//          })
//  }

// // returns pairs (n, s) indicating node n can be reached from a root in s steps.
// fn _reach<G: Scope>(edges: &Arrange<G, Node, Node, isize>, query: &Collection<G, Node>) -> Collection<G, Node>
// where G::Timestamp: Lattice+Ord {

//     // initialize query as reaching themselves at distance 0
//     // repeatedly update minimal distances each node can be reached from each root
//     query.iterate(|inner| {

//         let edges = edges.enter(&inner.scope());
//         let nodes = query.enter(&inner.scope());

//         // edges from active sources activate their destinations
//         edges.semijoin(&inner)
//              .map(|(_,d)| d)
//              .concat(&nodes)
//              .distinct()
//     })
// }

// // returns pairs (node, label) indicating the connected component containing each node
// fn _connected_components<G: Scope>(edges: &Arrange<G, Node, Node, isize>) -> Collection<G, (Node, Node)>
// where G::Timestamp: Lattice+Hash+Ord {

//     // each edge (x,y) means that we need at least a label for the min of x and y.
//     let nodes = edges.as_collection(|&k,&v| (k,v))
//                      .map_in_place(|pair| {
//                         let min = std::cmp::min(pair.0, pair.1);
//                         *pair = (min, min);
//                      })
//                      .consolidate();

//     let edges = edges.as_collection(|&k,&v| (k,v));
//     let edges = edges.map(|(x,y)| (y,x))
//                      .concat(&edges)
//                      .arrange_by_key();

//     // don't actually use these labels, just grab the type
//     nodes.filter(|_| false)
//          .iterate(|inner| {
//              let edges = edges.enter(&inner.scope());
//              let nodes = nodes.enter_at(&inner.scope(), |r| 256 * (64 - r.0.leading_zeros() as u64));

//              edges.join_map(&inner, |_k,d,l| (*d,*l))
//                   .concat(&nodes)
//                   .group(|_, s, t| { t.push((*s[0].0, 1)); } )
//          })
// }

// // returns pairs (n, s) indicating node n can be reached from a root in s steps.
// fn _bfs<G: Scope>(edges: &Arrange<G, Node, Node, isize>, query: &Collection<G, Node>) -> Collection<G, (Node, u32)>
// where G::Timestamp: Lattice+Ord {

//     // initialize query as reaching themselves at distance 0
//     let nodes = query.map(|x| (x, 0));

//     // repeatedly update minimal distances each node can be reached from each root
//     nodes.iterate(|inner| {

//         let edges = edges.enter(&inner.scope());
//         let nodes = nodes.enter(&inner.scope());

//         edges.join_map(&inner, |_k,d,l| (*d, l+1))
//              .concat(&nodes)
//              .group(|_, s, t| t.push((*s[0].0, 1)))
//      })
// }

// returns pairs (n, s) indicating node n can be reached from a root in s steps.
fn _tc<G: Scope>(edges: &Arrange<G, Node, Node, isize>) -> Collection<G, (Node, Node)>
where G::Timestamp: Lattice+Ord {

    use timely::dataflow::operators::Inspect;
    use timely::dataflow::operators::Accumulate;

    // repeatedly update minimal distances each node can be reached from each root
    edges
        .as_collection(|&k,&v| (k,v))
        .iterate(|inner| {

            inner.inner.count().inspect_batch(|t,xs| println!("{:?}\t{:?}", t, xs));

            let edges = edges.enter(&inner.scope());

            inner
                .map(|(x,y)| (y,x))
                .join_core(&edges, |_y,&x,&z| Some((x, z)))
                .concat(&edges.as_collection(|&k,&v| (k,v)))
                .distinct()
        }
    )
}

// returns pairs (n, s) indicating node n can be reached from a root in s steps.
fn _sg<G: Scope>(edges: &Arrange<G, Node, Node, isize>) -> Collection<G, (Node, Node)>
where G::Timestamp: Lattice+Ord {

    let peers = edges.join_core(&edges, |_, &x,&y| Some((x,y)));

    // repeatedly update minimal distances each node can be reached from each root
    peers
        .iterate(|inner| {

            let edges = edges.enter(&inner.scope());
            let peers = peers.enter(&inner.scope());

            inner
                .join_core(&edges, |_,&x,&z| Some((x, z)))
                .join_core(&edges, |_,&x,&z| Some((x, z)))
                .concat(&peers)
                .distinct()
        }
    )
}<|endoftext|>


<commit_before><commit_msg>An assertion had off-by-one error, causing valid code to fail.<commit_after><|endoftext|>


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

use std::collections::BTreeMap;
use std::mem;

use bindgen::config::Config;
use bindgen::declarationtyperesolver::DeclarationTypeResolver;
use bindgen::dependencies::Dependencies;
use bindgen::ir::{
    AnnotationSet, Cfg, Constant, Enum, OpaqueItem, Path, Static, Struct, Type, Typedef, Union,
};
use bindgen::library::Library;
use bindgen::monomorph::Monomorphs;

/// An item is any type of rust item besides a function
pub trait Item {
    fn path(&self) -> &Path;
    fn name(&self) -> &str {
        self.path().name()
    }
    fn export_name(&self) -> &str {
        self.name()
    }
    fn cfg(&self) -> &Option<Cfg>;
    fn annotations(&self) -> &AnnotationSet;
    fn annotations_mut(&mut self) -> &mut AnnotationSet;

    fn container(&self) -> ItemContainer;

    fn collect_declaration_types(&self, _resolver: &mut DeclarationTypeResolver) {
        unimplemented!()
    }
    fn resolve_declaration_types(&mut self, _resolver: &DeclarationTypeResolver) {
        unimplemented!()
    }
    fn rename_for_config(&mut self, _config: &Config) {}
    fn add_dependencies(&self, _library: &Library, _out: &mut Dependencies) {}
    fn instantiate_monomorph(&self, _generics: &[Type], _library: &Library, _out: &mut Monomorphs) {
        unreachable!("Cannot instantiate {} as a generic.", self.name())
    }
}

#[derive(Debug, Clone)]
pub enum ItemContainer {
    Constant(Constant),
    Static(Static),
    OpaqueItem(OpaqueItem),
    Struct(Struct),
    Union(Union),
    Enum(Enum),
    Typedef(Typedef),
}

impl ItemContainer {
    pub fn deref(&self) -> &Item {
        match self {
            &ItemContainer::Constant(ref x) => x,
            &ItemContainer::Static(ref x) => x,
            &ItemContainer::OpaqueItem(ref x) => x,
            &ItemContainer::Struct(ref x) => x,
            &ItemContainer::Union(ref x) => x,
            &ItemContainer::Enum(ref x) => x,
            &ItemContainer::Typedef(ref x) => x,
        }
    }
}

#[derive(Debug, Clone)]
pub enum ItemValue<T: Item> {
    Cfg(Vec<T>),
    Single(T),
}

#[derive(Debug, Clone)]
pub struct ItemMap<T: Item> {
    data: BTreeMap<Path, ItemValue<T>>,
}

impl<T: Item + Clone> ItemMap<T> {
    pub fn new() -> ItemMap<T> {
        ItemMap {
            data: BTreeMap::new(),
        }
    }

    pub fn rebuild(&mut self) {
        let old = mem::replace(self, ItemMap::new());
        old.for_all_items(|x| {
            self.try_insert(x.clone());
        });
    }

    pub fn try_insert(&mut self, item: T) -> bool {
        match (item.cfg().is_some(), self.data.get_mut(item.path())) {
            (true, Some(&mut ItemValue::Cfg(ref mut items))) => {
                items.push(item);
                return true;
            }
            (false, Some(&mut ItemValue::Cfg(_))) => {
                return false;
            }
            (true, Some(&mut ItemValue::Single(_))) => {
                return false;
            }
            (false, Some(&mut ItemValue::Single(_))) => {
                return false;
            }
            _ => {}
        }

        let path = item.path().clone();
        if item.cfg().is_some() {
            self.data.insert(path, ItemValue::Cfg(vec![item]));
        } else {
            self.data.insert(path, ItemValue::Single(item));
        }

        true
    }

    pub fn extend_with(&mut self, other: &ItemMap<T>) {
        other.for_all_items(|x| {
            self.try_insert(x.clone());
        });
    }

    pub fn to_vec(&self) -> Vec<T> {
        let mut result = Vec::with_capacity(self.data.len());
        for container in self.data.values() {
            match container {
                &ItemValue::Cfg(ref items) => result.extend_from_slice(items),
                &ItemValue::Single(ref item) => {
                    result.push(item.clone());
                }
            }
        }
        result
    }

    pub fn get_items(&self, path: &Path) -> Option<Vec<ItemContainer>> {
        match self.data.get(path) {
            Some(&ItemValue::Cfg(ref items)) => Some(items.iter().map(|x| x.container()).collect()),
            Some(&ItemValue::Single(ref item)) => Some(vec![item.container()]),
            None => None,
        }
    }

    pub fn filter<F>(&mut self, callback: F)
    where
        F: Fn(&T) -> bool,
    {
        let data = mem::replace(&mut self.data, BTreeMap::new());

        for (name, container) in data {
            match container {
                ItemValue::Cfg(items) => {
                    let mut new_items = Vec::new();
                    for item in items {
                        if !callback(&item) {
                            new_items.push(item);
                        }
                    }
                    if new_items.len() > 0 {
                        self.data.insert(name, ItemValue::Cfg(new_items));
                    }
                }
                ItemValue::Single(item) => {
                    if !callback(&item) {
                        self.data.insert(name, ItemValue::Single(item));
                    }
                }
            }
        }
    }

    pub fn for_all_items<F>(&self, mut callback: F)
    where
        F: FnMut(&T),
    {
        for container in self.data.values() {
            match container {
                &ItemValue::Cfg(ref items) => {
                    for item in items {
                        callback(item);
                    }
                }
                &ItemValue::Single(ref item) => callback(item),
            }
        }
    }

    pub fn for_all_items_mut<F>(&mut self, mut callback: F)
    where
        F: FnMut(&mut T),
    {
        for container in self.data.values_mut() {
            match container {
                &mut ItemValue::Cfg(ref mut items) => {
                    for item in items {
                        callback(item);
                    }
                }
                &mut ItemValue::Single(ref mut item) => callback(item),
            }
        }
    }

    #[allow(dead_code)]
    pub fn for_items<F>(&self, path: &Path, mut callback: F)
    where
        F: FnMut(&T),
    {
        match self.data.get(path) {
            Some(&ItemValue::Cfg(ref items)) => {
                for item in items {
                    callback(item);
                }
            }
            Some(&ItemValue::Single(ref item)) => {
                callback(item);
            }
            None => {}
        }
    }

    pub fn for_items_mut<F>(&mut self, path: &Path, mut callback: F)
    where
        F: FnMut(&mut T),
    {
        match self.data.get_mut(path) {
            Some(&mut ItemValue::Cfg(ref mut items)) => {
                for item in items {
                    callback(item);
                }
            }
            Some(&mut ItemValue::Single(ref mut item)) => {
                callback(item);
            }
            None => {}
        }
    }
}
<commit_msg>ir: Cleanup get_items.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use std::collections::BTreeMap;
use std::mem;

use bindgen::config::Config;
use bindgen::declarationtyperesolver::DeclarationTypeResolver;
use bindgen::dependencies::Dependencies;
use bindgen::ir::{
    AnnotationSet, Cfg, Constant, Enum, OpaqueItem, Path, Static, Struct, Type, Typedef, Union,
};
use bindgen::library::Library;
use bindgen::monomorph::Monomorphs;

/// An item is any type of rust item besides a function
pub trait Item {
    fn path(&self) -> &Path;
    fn name(&self) -> &str {
        self.path().name()
    }
    fn export_name(&self) -> &str {
        self.name()
    }
    fn cfg(&self) -> &Option<Cfg>;
    fn annotations(&self) -> &AnnotationSet;
    fn annotations_mut(&mut self) -> &mut AnnotationSet;

    fn container(&self) -> ItemContainer;

    fn collect_declaration_types(&self, _resolver: &mut DeclarationTypeResolver) {
        unimplemented!()
    }
    fn resolve_declaration_types(&mut self, _resolver: &DeclarationTypeResolver) {
        unimplemented!()
    }
    fn rename_for_config(&mut self, _config: &Config) {}
    fn add_dependencies(&self, _library: &Library, _out: &mut Dependencies) {}
    fn instantiate_monomorph(&self, _generics: &[Type], _library: &Library, _out: &mut Monomorphs) {
        unreachable!("Cannot instantiate {} as a generic.", self.name())
    }
}

#[derive(Debug, Clone)]
pub enum ItemContainer {
    Constant(Constant),
    Static(Static),
    OpaqueItem(OpaqueItem),
    Struct(Struct),
    Union(Union),
    Enum(Enum),
    Typedef(Typedef),
}

impl ItemContainer {
    pub fn deref(&self) -> &Item {
        match self {
            &ItemContainer::Constant(ref x) => x,
            &ItemContainer::Static(ref x) => x,
            &ItemContainer::OpaqueItem(ref x) => x,
            &ItemContainer::Struct(ref x) => x,
            &ItemContainer::Union(ref x) => x,
            &ItemContainer::Enum(ref x) => x,
            &ItemContainer::Typedef(ref x) => x,
        }
    }
}

#[derive(Debug, Clone)]
pub enum ItemValue<T: Item> {
    Cfg(Vec<T>),
    Single(T),
}

#[derive(Debug, Clone)]
pub struct ItemMap<T: Item> {
    data: BTreeMap<Path, ItemValue<T>>,
}

impl<T: Item + Clone> ItemMap<T> {
    pub fn new() -> ItemMap<T> {
        ItemMap {
            data: BTreeMap::new(),
        }
    }

    pub fn rebuild(&mut self) {
        let old = mem::replace(self, ItemMap::new());
        old.for_all_items(|x| {
            self.try_insert(x.clone());
        });
    }

    pub fn try_insert(&mut self, item: T) -> bool {
        match (item.cfg().is_some(), self.data.get_mut(item.path())) {
            (true, Some(&mut ItemValue::Cfg(ref mut items))) => {
                items.push(item);
                return true;
            }
            (false, Some(&mut ItemValue::Cfg(_))) => {
                return false;
            }
            (true, Some(&mut ItemValue::Single(_))) => {
                return false;
            }
            (false, Some(&mut ItemValue::Single(_))) => {
                return false;
            }
            _ => {}
        }

        let path = item.path().clone();
        if item.cfg().is_some() {
            self.data.insert(path, ItemValue::Cfg(vec![item]));
        } else {
            self.data.insert(path, ItemValue::Single(item));
        }

        true
    }

    pub fn extend_with(&mut self, other: &ItemMap<T>) {
        other.for_all_items(|x| {
            self.try_insert(x.clone());
        });
    }

    pub fn to_vec(&self) -> Vec<T> {
        let mut result = Vec::with_capacity(self.data.len());
        for container in self.data.values() {
            match container {
                &ItemValue::Cfg(ref items) => result.extend_from_slice(items),
                &ItemValue::Single(ref item) => {
                    result.push(item.clone());
                }
            }
        }
        result
    }

    pub fn get_items(&self, path: &Path) -> Option<Vec<ItemContainer>> {
        Some(match *self.data.get(path)? {
            ItemValue::Cfg(ref items) => items.iter().map(|x| x.container()).collect(),
            ItemValue::Single(ref item) => vec![item.container()],
        })
    }

    pub fn filter<F>(&mut self, callback: F)
    where
        F: Fn(&T) -> bool,
    {
        let data = mem::replace(&mut self.data, BTreeMap::new());

        for (name, container) in data {
            match container {
                ItemValue::Cfg(items) => {
                    let mut new_items = Vec::new();
                    for item in items {
                        if !callback(&item) {
                            new_items.push(item);
                        }
                    }
                    if new_items.len() > 0 {
                        self.data.insert(name, ItemValue::Cfg(new_items));
                    }
                }
                ItemValue::Single(item) => {
                    if !callback(&item) {
                        self.data.insert(name, ItemValue::Single(item));
                    }
                }
            }
        }
    }

    pub fn for_all_items<F>(&self, mut callback: F)
    where
        F: FnMut(&T),
    {
        for container in self.data.values() {
            match container {
                &ItemValue::Cfg(ref items) => {
                    for item in items {
                        callback(item);
                    }
                }
                &ItemValue::Single(ref item) => callback(item),
            }
        }
    }

    pub fn for_all_items_mut<F>(&mut self, mut callback: F)
    where
        F: FnMut(&mut T),
    {
        for container in self.data.values_mut() {
            match container {
                &mut ItemValue::Cfg(ref mut items) => {
                    for item in items {
                        callback(item);
                    }
                }
                &mut ItemValue::Single(ref mut item) => callback(item),
            }
        }
    }

    #[allow(dead_code)]
    pub fn for_items<F>(&self, path: &Path, mut callback: F)
    where
        F: FnMut(&T),
    {
        match self.data.get(path) {
            Some(&ItemValue::Cfg(ref items)) => {
                for item in items {
                    callback(item);
                }
            }
            Some(&ItemValue::Single(ref item)) => {
                callback(item);
            }
            None => {}
        }
    }

    pub fn for_items_mut<F>(&mut self, path: &Path, mut callback: F)
    where
        F: FnMut(&mut T),
    {
        match self.data.get_mut(path) {
            Some(&mut ItemValue::Cfg(ref mut items)) => {
                for item in items {
                    callback(item);
                }
            }
            Some(&mut ItemValue::Single(ref mut item)) => {
                callback(item);
            }
            None => {}
        }
    }
}
<|endoftext|>


<commit_before><commit_msg>Do not include `to_socket_addr` for UDP feature<commit_after><|endoftext|>


<commit_before><commit_msg>Rect can hold!<commit_after><|endoftext|>


<commit_before>// Copyright 2016 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.

//! Implementation of `make clean` in rustbuild.
//!
//! Responsible for cleaning out a build directory of all old and stale
//! artifacts to prepare for a fresh build. Currently doesn't remove the
//! `build/cache` directory (download cache) or the `build/$target/llvm`
//! directory as we want that cached between builds.

use std::fs;
use std::io::{self, ErrorKind};
use std::path::Path;

use Build;

pub fn clean(build: &Build) {
    rm_rf(build, "tmp".as_ref());
    rm_rf(build, &build.out.join("tmp"));

    for host in build.config.host.iter() {
        let entries = match build.out.join(host).read_dir() {
            Ok(iter) => iter,
            Err(_) => continue,
        };

        for entry in entries {
            let entry = t!(entry);
            if entry.file_name().to_str() == Some("llvm") {
                continue
            }
            let path = t!(entry.path().canonicalize());
            rm_rf(build, &path);
        }
    }
}

fn rm_rf(build: &Build, path: &Path) {
    if !path.exists() {
        return
    }
    if path.is_file() {
        return do_op(path, "remove file", |p| fs::remove_file(p));
    }

    for file in t!(fs::read_dir(path)) {
        let file = t!(file).path();

        if file.is_dir() {
            rm_rf(build, &file);
        } else {
            // On windows we can't remove a readonly file, and git will
            // often clone files as readonly. As a result, we have some
            // special logic to remove readonly files on windows.
            do_op(&file, "remove file", |p| fs::remove_file(p));
        }
    }
    do_op(path, "remove dir", |p| fs::remove_dir(p));
}

fn do_op<F>(path: &Path, desc: &str, mut f: F)
    where F: FnMut(&Path) -> io::Result<()>
{
    match f(path) {
        Ok(()) => {}
        Err(ref e) if cfg!(windows) &&
                      e.kind() == ErrorKind::PermissionDenied => {
            let mut p = t!(path.metadata()).permissions();
            p.set_readonly(false);
            t!(fs::set_permissions(path, p));
            f(path).unwrap_or_else(|e| {
                panic!("failed to {} {}: {}", desc, path.display(), e);
            })
        }
        Err(e) => {
            panic!("failed to {} {}: {}", desc, path.display(), e);
        }
    }
}
<commit_msg>Rollup merge of #39622 - alexcrichton:clean-dist, r=brson<commit_after>// Copyright 2016 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.

//! Implementation of `make clean` in rustbuild.
//!
//! Responsible for cleaning out a build directory of all old and stale
//! artifacts to prepare for a fresh build. Currently doesn't remove the
//! `build/cache` directory (download cache) or the `build/$target/llvm`
//! directory as we want that cached between builds.

use std::fs;
use std::io::{self, ErrorKind};
use std::path::Path;

use Build;

pub fn clean(build: &Build) {
    rm_rf(build, "tmp".as_ref());
    rm_rf(build, &build.out.join("tmp"));
    rm_rf(build, &build.out.join("dist"));

    for host in build.config.host.iter() {
        let entries = match build.out.join(host).read_dir() {
            Ok(iter) => iter,
            Err(_) => continue,
        };

        for entry in entries {
            let entry = t!(entry);
            if entry.file_name().to_str() == Some("llvm") {
                continue
            }
            let path = t!(entry.path().canonicalize());
            rm_rf(build, &path);
        }
    }
}

fn rm_rf(build: &Build, path: &Path) {
    if !path.exists() {
        return
    }
    if path.is_file() {
        return do_op(path, "remove file", |p| fs::remove_file(p));
    }

    for file in t!(fs::read_dir(path)) {
        let file = t!(file).path();

        if file.is_dir() {
            rm_rf(build, &file);
        } else {
            // On windows we can't remove a readonly file, and git will
            // often clone files as readonly. As a result, we have some
            // special logic to remove readonly files on windows.
            do_op(&file, "remove file", |p| fs::remove_file(p));
        }
    }
    do_op(path, "remove dir", |p| fs::remove_dir(p));
}

fn do_op<F>(path: &Path, desc: &str, mut f: F)
    where F: FnMut(&Path) -> io::Result<()>
{
    match f(path) {
        Ok(()) => {}
        Err(ref e) if cfg!(windows) &&
                      e.kind() == ErrorKind::PermissionDenied => {
            let mut p = t!(path.metadata()).permissions();
            p.set_readonly(false);
            t!(fs::set_permissions(path, p));
            f(path).unwrap_or_else(|e| {
                panic!("failed to {} {}: {}", desc, path.display(), e);
            })
        }
        Err(e) => {
            panic!("failed to {} {}: {}", desc, path.display(), e);
        }
    }
}
<|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.

//! Command-line interface of the rustbuild build system.
//!
//! This module implements the command-line parsing of the build system which
//! has various flags to configure how it's run.

use std::env;
use std::fs;
use std::path::PathBuf;
use std::process;

use getopts::Options;

use Build;
use config::Config;
use metadata;
use step;

/// Deserialized version of all flags for this compile.
pub struct Flags {
    pub verbose: usize, // verbosity level: 0 == not verbose, 1 == verbose, 2 == very verbose
    pub on_fail: Option<String>,
    pub stage: Option<u32>,
    pub keep_stage: Option<u32>,
    pub build: String,
    pub host: Vec<String>,
    pub target: Vec<String>,
    pub config: Option<PathBuf>,
    pub src: Option<PathBuf>,
    pub jobs: Option<u32>,
    pub cmd: Subcommand,
    pub incremental: bool,
}

impl Flags {
    pub fn verbose(&self) -> bool {
        self.verbose > 0
    }

    pub fn very_verbose(&self) -> bool {
        self.verbose > 1
    }
}

pub enum Subcommand {
    Build {
        paths: Vec<PathBuf>,
    },
    Doc {
        paths: Vec<PathBuf>,
    },
    Test {
        paths: Vec<PathBuf>,
        test_args: Vec<String>,
    },
    Bench {
        paths: Vec<PathBuf>,
        test_args: Vec<String>,
    },
    Clean,
    Dist {
        paths: Vec<PathBuf>,
        install: bool,
    },
}

impl Flags {
    pub fn parse(args: &[String]) -> Flags {
        let mut extra_help = String::new();
        let mut subcommand_help = format!("\
Usage: x.py <subcommand> [options] [<paths>...]

Subcommands:
    build       Compile either the compiler or libraries
    test        Build and run some test suites
    bench       Build and run some benchmarks
    doc         Build documentation
    clean       Clean out build directories
    dist        Build and/or install distribution artifacts

To learn more about a subcommand, run `./x.py <subcommand> -h`");

        let mut opts = Options::new();
        // Options common to all subcommands
        opts.optflagmulti("v", "verbose", "use verbose output (-vv for very verbose)");
        opts.optflag("i", "incremental", "use incremental compilation");
        opts.optopt("", "config", "TOML configuration file for build", "FILE");
        opts.optopt("", "build", "build target of the stage0 compiler", "BUILD");
        opts.optmulti("", "host", "host targets to build", "HOST");
        opts.optmulti("", "target", "target targets to build", "TARGET");
        opts.optopt("", "on-fail", "command to run on failure", "CMD");
        opts.optopt("", "stage", "stage to build", "N");
        opts.optopt("", "keep-stage", "stage to keep without recompiling", "N");
        opts.optopt("", "src", "path to the root of the rust checkout", "DIR");
        opts.optopt("j", "jobs", "number of jobs to run in parallel", "JOBS");
        opts.optflag("h", "help", "print this help message");

        // fn usage()
        let usage = |exit_code: i32, opts: &Options, subcommand_help: &str, extra_help: &str| -> ! {
            println!("{}", opts.usage(subcommand_help));
            if !extra_help.is_empty() {
                println!("{}", extra_help);
            }
            process::exit(exit_code);
        };

        // We can't use getopt to parse the options until we have completed specifying which
        // options are valid, but under the current implementation, some options are conditional on
        // the subcommand. Therefore we must manually identify the subcommand first, so that we can
        // complete the definition of the options.  Then we can use the getopt::Matches object from
        // there on out.
        let mut possible_subcommands = args.iter().collect::<Vec<_>>();
        possible_subcommands.retain(|&s| !s.starts_with('-'));
        let subcommand = match possible_subcommands.first() {
            Some(s) => s,
            None => {
                // No subcommand -- show the general usage and subcommand help
                println!("{}\n", subcommand_help);
                process::exit(0);
            }
        };

        // Some subcommands have specific arguments help text
        match subcommand.as_str() {
            "build" => {
                subcommand_help.push_str("\n
Arguments:
    This subcommand accepts a number of paths to directories to the crates
    and/or artifacts to compile. For example:

        ./x.py build src/libcore
        ./x.py build src/libcore src/libproc_macro
        ./x.py build src/libstd --stage 1

    If no arguments are passed then the complete artifacts for that stage are
    also compiled.

        ./x.py build
        ./x.py build --stage 1

    For a quick build with a usable compile, you can pass:

        ./x.py build --stage 1 src/libtest");
            }
            "test" => {
                opts.optmulti("", "test-args", "extra arguments", "ARGS");
                subcommand_help.push_str("\n
Arguments:
    This subcommand accepts a number of paths to directories to tests that
    should be compiled and run. For example:

        ./x.py test src/test/run-pass
        ./x.py test src/libstd --test-args hash_map
        ./x.py test src/libstd --stage 0

    If no arguments are passed then the complete artifacts for that stage are
    compiled and tested.

        ./x.py test
        ./x.py test --stage 1");
            }
            "bench" => {
                opts.optmulti("", "test-args", "extra arguments", "ARGS");
            }
            "doc" => {
                subcommand_help.push_str("\n
Arguments:
    This subcommand accepts a number of paths to directories of documentation
    to build. For example:

        ./x.py doc src/doc/book
        ./x.py doc src/doc/nomicon
        ./x.py doc src/doc/book src/libstd

    If no arguments are passed then everything is documented:

        ./x.py doc
        ./x.py doc --stage 1");
            }
            "dist" => {
                opts.optflag("", "install", "run installer as well");
            }
            _ => { }
        };

        // Done specifying what options are possible, so do the getopts parsing
        let matches = opts.parse(&args[..]).unwrap_or_else(|e| {
            // Invalid argument/option format
            println!("\n{}\n", e);
            usage(1, &opts, &subcommand_help, &extra_help);
        });
        // Get any optional paths which occur after the subcommand
        let cwd = t!(env::current_dir());
        let paths = matches.free[1..].iter().map(|p| cwd.join(p)).collect::<Vec<_>>();

        // All subcommands can have an optional "Available paths" section
        if matches.opt_present("verbose") {
            let flags = Flags::parse(&["build".to_string()]);
            let mut config = Config::default();
            config.build = flags.build.clone();
            let mut build = Build::new(flags, config);
            metadata::build(&mut build);
            let maybe_rules_help = step::build_rules(&build).get_help(subcommand);
            if maybe_rules_help.is_some() {
                extra_help.push_str(maybe_rules_help.unwrap().as_str());
            }
        } else {
            extra_help.push_str(format!("Run `./x.py {} -h -v` to see a list of available paths.",
                     subcommand).as_str());
        }

        // User passed in -h/--help?
        if matches.opt_present("help") {
            usage(0, &opts, &subcommand_help, &extra_help);
        }

        let cmd = match subcommand.as_str() {
            "build" => {
                Subcommand::Build { paths: paths }
            }
            "test" => {
                Subcommand::Test {
                    paths: paths,
                    test_args: matches.opt_strs("test-args"),
                }
            }
            "bench" => {
                Subcommand::Bench {
                    paths: paths,
                    test_args: matches.opt_strs("test-args"),
                }
            }
            "doc" => {
                Subcommand::Doc { paths: paths }
            }
            "clean" => {
                if matches.free.len() > 0 {
                    println!("\nclean takes no arguments\n");
                    usage(1, &opts, &subcommand_help, &extra_help);
                }
                Subcommand::Clean
            }
            "dist" => {
                Subcommand::Dist {
                    paths: paths,
                    install: matches.opt_present("install"),
                }
            }
            _ => {
                usage(1, &opts, &subcommand_help, &extra_help);
            }
        };

        let cfg_file = matches.opt_str("config").map(PathBuf::from).or_else(|| {
            if fs::metadata("config.toml").is_ok() {
                Some(PathBuf::from("config.toml"))
            } else {
                None
            }
        });

        let mut stage = matches.opt_str("stage").map(|j| j.parse().unwrap());

        if matches.opt_present("incremental") {
            if stage.is_none() {
                stage = Some(1);
            }
        }

        Flags {
            verbose: matches.opt_count("verbose"),
            stage: stage,
            on_fail: matches.opt_str("on-fail"),
            keep_stage: matches.opt_str("keep-stage").map(|j| j.parse().unwrap()),
            build: matches.opt_str("build").unwrap_or_else(|| {
                env::var("BUILD").unwrap()
            }),
            host: split(matches.opt_strs("host")),
            target: split(matches.opt_strs("target")),
            config: cfg_file,
            src: matches.opt_str("src").map(PathBuf::from),
            jobs: matches.opt_str("jobs").map(|j| j.parse().unwrap()),
            cmd: cmd,
            incremental: matches.opt_present("incremental"),
        }
    }
}

impl Subcommand {
    pub fn test_args(&self) -> Vec<&str> {
        match *self {
            Subcommand::Test { ref test_args, .. } |
            Subcommand::Bench { ref test_args, .. } => {
                test_args.iter().flat_map(|s| s.split_whitespace()).collect()
            }
            _ => Vec::new(),
        }
    }
}

fn split(s: Vec<String>) -> Vec<String> {
    s.iter().flat_map(|s| s.split(',')).map(|s| s.to_string()).collect()
}
<commit_msg>Fix breaking the 'clean' subcommand caused replacing a single-letter variable with the same value in two contexts where it was used differently.  That's why you don't use "m" as a variable for hundreds of lines in an outer function, and re-use it in closures several times in the same function.  Sheesh.<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.

//! Command-line interface of the rustbuild build system.
//!
//! This module implements the command-line parsing of the build system which
//! has various flags to configure how it's run.

use std::env;
use std::fs;
use std::path::PathBuf;
use std::process;

use getopts::Options;

use Build;
use config::Config;
use metadata;
use step;

/// Deserialized version of all flags for this compile.
pub struct Flags {
    pub verbose: usize, // verbosity level: 0 == not verbose, 1 == verbose, 2 == very verbose
    pub on_fail: Option<String>,
    pub stage: Option<u32>,
    pub keep_stage: Option<u32>,
    pub build: String,
    pub host: Vec<String>,
    pub target: Vec<String>,
    pub config: Option<PathBuf>,
    pub src: Option<PathBuf>,
    pub jobs: Option<u32>,
    pub cmd: Subcommand,
    pub incremental: bool,
}

impl Flags {
    pub fn verbose(&self) -> bool {
        self.verbose > 0
    }

    pub fn very_verbose(&self) -> bool {
        self.verbose > 1
    }
}

pub enum Subcommand {
    Build {
        paths: Vec<PathBuf>,
    },
    Doc {
        paths: Vec<PathBuf>,
    },
    Test {
        paths: Vec<PathBuf>,
        test_args: Vec<String>,
    },
    Bench {
        paths: Vec<PathBuf>,
        test_args: Vec<String>,
    },
    Clean,
    Dist {
        paths: Vec<PathBuf>,
        install: bool,
    },
}

impl Flags {
    pub fn parse(args: &[String]) -> Flags {
        let mut extra_help = String::new();
        let mut subcommand_help = format!("\
Usage: x.py <subcommand> [options] [<paths>...]

Subcommands:
    build       Compile either the compiler or libraries
    test        Build and run some test suites
    bench       Build and run some benchmarks
    doc         Build documentation
    clean       Clean out build directories
    dist        Build and/or install distribution artifacts

To learn more about a subcommand, run `./x.py <subcommand> -h`");

        let mut opts = Options::new();
        // Options common to all subcommands
        opts.optflagmulti("v", "verbose", "use verbose output (-vv for very verbose)");
        opts.optflag("i", "incremental", "use incremental compilation");
        opts.optopt("", "config", "TOML configuration file for build", "FILE");
        opts.optopt("", "build", "build target of the stage0 compiler", "BUILD");
        opts.optmulti("", "host", "host targets to build", "HOST");
        opts.optmulti("", "target", "target targets to build", "TARGET");
        opts.optopt("", "on-fail", "command to run on failure", "CMD");
        opts.optopt("", "stage", "stage to build", "N");
        opts.optopt("", "keep-stage", "stage to keep without recompiling", "N");
        opts.optopt("", "src", "path to the root of the rust checkout", "DIR");
        opts.optopt("j", "jobs", "number of jobs to run in parallel", "JOBS");
        opts.optflag("h", "help", "print this help message");

        // fn usage()
        let usage = |exit_code: i32, opts: &Options, subcommand_help: &str, extra_help: &str| -> ! {
            println!("{}", opts.usage(subcommand_help));
            if !extra_help.is_empty() {
                println!("{}", extra_help);
            }
            process::exit(exit_code);
        };

        // We can't use getopt to parse the options until we have completed specifying which
        // options are valid, but under the current implementation, some options are conditional on
        // the subcommand. Therefore we must manually identify the subcommand first, so that we can
        // complete the definition of the options.  Then we can use the getopt::Matches object from
        // there on out.
        let mut possible_subcommands = args.iter().collect::<Vec<_>>();
        possible_subcommands.retain(|&s| !s.starts_with('-'));
        let subcommand = match possible_subcommands.first() {
            Some(s) => s,
            None => {
                // No subcommand -- show the general usage and subcommand help
                println!("{}\n", subcommand_help);
                process::exit(0);
            }
        };

        // Some subcommands have specific arguments help text
        match subcommand.as_str() {
            "build" => {
                subcommand_help.push_str("\n
Arguments:
    This subcommand accepts a number of paths to directories to the crates
    and/or artifacts to compile. For example:

        ./x.py build src/libcore
        ./x.py build src/libcore src/libproc_macro
        ./x.py build src/libstd --stage 1

    If no arguments are passed then the complete artifacts for that stage are
    also compiled.

        ./x.py build
        ./x.py build --stage 1

    For a quick build with a usable compile, you can pass:

        ./x.py build --stage 1 src/libtest");
            }
            "test" => {
                opts.optmulti("", "test-args", "extra arguments", "ARGS");
                subcommand_help.push_str("\n
Arguments:
    This subcommand accepts a number of paths to directories to tests that
    should be compiled and run. For example:

        ./x.py test src/test/run-pass
        ./x.py test src/libstd --test-args hash_map
        ./x.py test src/libstd --stage 0

    If no arguments are passed then the complete artifacts for that stage are
    compiled and tested.

        ./x.py test
        ./x.py test --stage 1");
            }
            "bench" => {
                opts.optmulti("", "test-args", "extra arguments", "ARGS");
            }
            "doc" => {
                subcommand_help.push_str("\n
Arguments:
    This subcommand accepts a number of paths to directories of documentation
    to build. For example:

        ./x.py doc src/doc/book
        ./x.py doc src/doc/nomicon
        ./x.py doc src/doc/book src/libstd

    If no arguments are passed then everything is documented:

        ./x.py doc
        ./x.py doc --stage 1");
            }
            "dist" => {
                opts.optflag("", "install", "run installer as well");
            }
            _ => { }
        };

        // Done specifying what options are possible, so do the getopts parsing
        let matches = opts.parse(&args[..]).unwrap_or_else(|e| {
            // Invalid argument/option format
            println!("\n{}\n", e);
            usage(1, &opts, &subcommand_help, &extra_help);
        });
        // Get any optional paths which occur after the subcommand
        let cwd = t!(env::current_dir());
        let paths = matches.free[1..].iter().map(|p| cwd.join(p)).collect::<Vec<_>>();

        // All subcommands can have an optional "Available paths" section
        if matches.opt_present("verbose") {
            let flags = Flags::parse(&["build".to_string()]);
            let mut config = Config::default();
            config.build = flags.build.clone();
            let mut build = Build::new(flags, config);
            metadata::build(&mut build);
            let maybe_rules_help = step::build_rules(&build).get_help(subcommand);
            if maybe_rules_help.is_some() {
                extra_help.push_str(maybe_rules_help.unwrap().as_str());
            }
        } else {
            extra_help.push_str(format!("Run `./x.py {} -h -v` to see a list of available paths.",
                     subcommand).as_str());
        }

        // User passed in -h/--help?
        if matches.opt_present("help") {
            usage(0, &opts, &subcommand_help, &extra_help);
        }

        let cmd = match subcommand.as_str() {
            "build" => {
                Subcommand::Build { paths: paths }
            }
            "test" => {
                Subcommand::Test {
                    paths: paths,
                    test_args: matches.opt_strs("test-args"),
                }
            }
            "bench" => {
                Subcommand::Bench {
                    paths: paths,
                    test_args: matches.opt_strs("test-args"),
                }
            }
            "doc" => {
                Subcommand::Doc { paths: paths }
            }
            "clean" => {
                if paths.len() > 0 {
                    println!("\nclean takes no arguments\n");
                    usage(1, &opts, &subcommand_help, &extra_help);
                }
                Subcommand::Clean
            }
            "dist" => {
                Subcommand::Dist {
                    paths: paths,
                    install: matches.opt_present("install"),
                }
            }
            _ => {
                usage(1, &opts, &subcommand_help, &extra_help);
            }
        };

        let cfg_file = matches.opt_str("config").map(PathBuf::from).or_else(|| {
            if fs::metadata("config.toml").is_ok() {
                Some(PathBuf::from("config.toml"))
            } else {
                None
            }
        });

        let mut stage = matches.opt_str("stage").map(|j| j.parse().unwrap());

        if matches.opt_present("incremental") {
            if stage.is_none() {
                stage = Some(1);
            }
        }

        Flags {
            verbose: matches.opt_count("verbose"),
            stage: stage,
            on_fail: matches.opt_str("on-fail"),
            keep_stage: matches.opt_str("keep-stage").map(|j| j.parse().unwrap()),
            build: matches.opt_str("build").unwrap_or_else(|| {
                env::var("BUILD").unwrap()
            }),
            host: split(matches.opt_strs("host")),
            target: split(matches.opt_strs("target")),
            config: cfg_file,
            src: matches.opt_str("src").map(PathBuf::from),
            jobs: matches.opt_str("jobs").map(|j| j.parse().unwrap()),
            cmd: cmd,
            incremental: matches.opt_present("incremental"),
        }
    }
}

impl Subcommand {
    pub fn test_args(&self) -> Vec<&str> {
        match *self {
            Subcommand::Test { ref test_args, .. } |
            Subcommand::Bench { ref test_args, .. } => {
                test_args.iter().flat_map(|s| s.split_whitespace()).collect()
            }
            _ => Vec::new(),
        }
    }
}

fn split(s: Vec<String>) -> Vec<String> {
    s.iter().flat_map(|s| s.split(',')).map(|s| s.to_string()).collect()
}
<|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.

//! Shim which is passed to Cargo as "rustc" when running the bootstrap.
//!
//! This shim will take care of some various tasks that our build process
//! requires that Cargo can't quite do through normal configuration:
//!
//! 1. When compiling build scripts and build dependencies, we need a guaranteed
//!    full standard library available. The only compiler which actually has
//!    this is the snapshot, so we detect this situation and always compile with
//!    the snapshot compiler.
//! 2. We pass a bunch of `--cfg` and other flags based on what we're compiling
//!    (and this slightly differs based on a whether we're using a snapshot or
//!    not), so we do that all here.
//!
//! This may one day be replaced by RUSTFLAGS, but the dynamic nature of
//! switching compilers for the bootstrap and for build scripts will probably
//! never get replaced.

extern crate bootstrap;

use std::env;
use std::ffi::OsString;
use std::path::PathBuf;
use std::process::Command;

fn main() {
    let args = env::args_os().skip(1).collect::<Vec<_>>();
    // Detect whether or not we're a build script depending on whether --target
    // is passed (a bit janky...)
    let is_build_script = args.iter()
                              .position(|i| i.to_str() == Some("--target"))
                              .is_none();

    // Build scripts always use the snapshot compiler which is guaranteed to be
    // able to produce an executable, whereas intermediate compilers may not
    // have the standard library built yet and may not be able to produce an
    // executable. Otherwise we just use the standard compiler we're
    // bootstrapping with.
    let rustc = if is_build_script {
        env::var_os("RUSTC_SNAPSHOT").unwrap()
    } else {
        env::var_os("RUSTC_REAL").unwrap()
    };

    let mut cmd = Command::new(rustc);
    cmd.args(&args)
       .arg("--cfg").arg(format!("stage{}", env::var("RUSTC_STAGE").unwrap()));

    if is_build_script {
        // Build scripts are always built with the snapshot compiler, so we need
        // to be sure to set up the right path information for the OS dynamic
        // linker to find the libraries in question.
        if let Some(p) = env::var_os("RUSTC_SNAPSHOT_LIBDIR") {
            let mut path = bootstrap::dylib_path();
            path.insert(0, PathBuf::from(p));
            cmd.env(bootstrap::dylib_path_var(), env::join_paths(path).unwrap());
        }
    } else {
        cmd.arg("--sysroot").arg(env::var_os("RUSTC_SYSROOT").unwrap());

        // When we build Rust dylibs they're all intended for intermediate
        // usage, so make sure we pass the -Cprefer-dynamic flag instead of
        // linking all deps statically into the dylib.
        cmd.arg("-Cprefer-dynamic");

        if let Some(s) = env::var_os("MUSL_ROOT") {
            let mut root = OsString::from("native=");
            root.push(&s);
            root.push("/lib");
            cmd.arg("-L").arg(&root);
        }
        if let Ok(s) = env::var("RUSTC_FLAGS") {
            cmd.args(&s.split(" ").filter(|s| !s.is_empty()).collect::<Vec<_>>());
        }
    }

    // Set various options from config.toml to configure how we're building
    // code.
    if env::var("RUSTC_DEBUGINFO") == Ok("true".to_string()) {
        cmd.arg("-g");
    }
    if env::var("RUSTC_RPATH") == Ok("true".to_string()) {
        cmd.arg("-Crpath");
    }
    let debug_assertions = match env::var("RUSTC_DEBUG_ASSERTIONS") {
        Ok(s) => if s == "true" {"y"} else {"n"},
        Err(..) => "n",
    };
    cmd.arg("-C").arg(format!("debug-assertions={}", debug_assertions));
    if let Ok(s) = env::var("RUSTC_CODEGEN_UNITS") {
        cmd.arg("-C").arg(format!("codegen-units={}", s));
    }

    // Actually run the compiler!
    std::process::exit(match cmd.status() {
        Ok(s) => s.code().unwrap_or(1),
        Err(e) => panic!("\n\nfailed to run {:?}: {}\n\n", cmd, e),
    })
}
<commit_msg>Auto merge of #32903 - alexcrichton:fix-rpath, r=brson<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.

//! Shim which is passed to Cargo as "rustc" when running the bootstrap.
//!
//! This shim will take care of some various tasks that our build process
//! requires that Cargo can't quite do through normal configuration:
//!
//! 1. When compiling build scripts and build dependencies, we need a guaranteed
//!    full standard library available. The only compiler which actually has
//!    this is the snapshot, so we detect this situation and always compile with
//!    the snapshot compiler.
//! 2. We pass a bunch of `--cfg` and other flags based on what we're compiling
//!    (and this slightly differs based on a whether we're using a snapshot or
//!    not), so we do that all here.
//!
//! This may one day be replaced by RUSTFLAGS, but the dynamic nature of
//! switching compilers for the bootstrap and for build scripts will probably
//! never get replaced.

extern crate bootstrap;

use std::env;
use std::ffi::OsString;
use std::path::PathBuf;
use std::process::Command;

fn main() {
    let args = env::args_os().skip(1).collect::<Vec<_>>();
    // Detect whether or not we're a build script depending on whether --target
    // is passed (a bit janky...)
    let target = args.windows(2).find(|w| &*w[0] == "--target")
                                .and_then(|w| w[1].to_str());

    // Build scripts always use the snapshot compiler which is guaranteed to be
    // able to produce an executable, whereas intermediate compilers may not
    // have the standard library built yet and may not be able to produce an
    // executable. Otherwise we just use the standard compiler we're
    // bootstrapping with.
    let rustc = if target.is_none() {
        env::var_os("RUSTC_SNAPSHOT").unwrap()
    } else {
        env::var_os("RUSTC_REAL").unwrap()
    };

    let mut cmd = Command::new(rustc);
    cmd.args(&args)
       .arg("--cfg").arg(format!("stage{}", env::var("RUSTC_STAGE").unwrap()));

    if target.is_none() {
        // Build scripts are always built with the snapshot compiler, so we need
        // to be sure to set up the right path information for the OS dynamic
        // linker to find the libraries in question.
        if let Some(p) = env::var_os("RUSTC_SNAPSHOT_LIBDIR") {
            let mut path = bootstrap::dylib_path();
            path.insert(0, PathBuf::from(p));
            cmd.env(bootstrap::dylib_path_var(), env::join_paths(path).unwrap());
        }
    } else {
        cmd.arg("--sysroot").arg(env::var_os("RUSTC_SYSROOT").unwrap());

        // When we build Rust dylibs they're all intended for intermediate
        // usage, so make sure we pass the -Cprefer-dynamic flag instead of
        // linking all deps statically into the dylib.
        cmd.arg("-Cprefer-dynamic");

        if let Some(s) = env::var_os("MUSL_ROOT") {
            let mut root = OsString::from("native=");
            root.push(&s);
            root.push("/lib");
            cmd.arg("-L").arg(&root);
        }
        if let Ok(s) = env::var("RUSTC_FLAGS") {
            cmd.args(&s.split(" ").filter(|s| !s.is_empty()).collect::<Vec<_>>());
        }
    }

    // Set various options from config.toml to configure how we're building
    // code.
    if let Some(target) = target {
        if env::var("RUSTC_DEBUGINFO") == Ok("true".to_string()) {
            cmd.arg("-g");
        }
        let debug_assertions = match env::var("RUSTC_DEBUG_ASSERTIONS") {
            Ok(s) => if s == "true" {"y"} else {"n"},
            Err(..) => "n",
        };
        cmd.arg("-C").arg(format!("debug-assertions={}", debug_assertions));
        if let Ok(s) = env::var("RUSTC_CODEGEN_UNITS") {
            cmd.arg("-C").arg(format!("codegen-units={}", s));
        }

        // Dealing with rpath here is a little special, so let's go into some
        // detail. First off, `-rpath` is a linker option on Unix platforms
        // which adds to the runtime dynamic loader path when looking for
        // dynamic libraries. We use this by default on Unix platforms to ensure
        // that our nightlies behave the same on Windows, that is they work out
        // of the box. This can be disabled, of course, but basically that's why
        // we're gated on RUSTC_RPATH here.
        //
        // Ok, so the astute might be wondering "why isn't `-C rpath` used
        // here?" and that is indeed a good question to task. This codegen
        // option is the compiler's current interface to generating an rpath.
        // Unfortunately it doesn't quite suffice for us. The flag currently
        // takes no value as an argument, so the compiler calculates what it
        // should pass to the linker as `-rpath`. This unfortunately is based on
        // the **compile time** directory structure which when building with
        // Cargo will be very different than the runtime directory structure.
        //
        // All that's a really long winded way of saying that if we use
        // `-Crpath` then the executables generated have the wrong rpath of
        // something like `$ORIGIN/deps` when in fact the way we distribute
        // rustc requires the rpath to be `$ORIGIN/../lib`.
        //
        // So, all in all, to set up the correct rpath we pass the linker
        // argument manually via `-C link-args=-Wl,-rpath,...`. Plus isn't it
        // fun to pass a flag to a tool to pass a flag to pass a flag to a tool
        // to change a flag in a binary?
        if env::var("RUSTC_RPATH") == Ok("true".to_string()) {
            let rpath = if target.contains("apple") {
                Some("-Wl,-rpath,@loader_path/../lib")
            } else if !target.contains("windows") {
                Some("-Wl,-rpath,$ORIGIN/../lib")
            } else {
                None
            };
            if let Some(rpath) = rpath {
                cmd.arg("-C").arg(format!("link-args={}", rpath));
            }
        }
    }

    // Actually run the compiler!
    std::process::exit(match cmd.status() {
        Ok(s) => s.code().unwrap_or(1),
        Err(e) => panic!("\n\nfailed to run {:?}: {}\n\n", cmd, e),
    })
}
<|endoftext|>