Thread the shell through more of the system

This commit is contained in:
Yehuda Katz 2014-06-21 22:22:56 -07:00
parent 687035657d
commit 19bea0ad0c
18 changed files with 270 additions and 179 deletions

View File

@ -14,6 +14,7 @@ extern crate serialize;
use std::os; use std::os;
use cargo::{execute_main_without_stdin}; use cargo::{execute_main_without_stdin};
use cargo::ops; use cargo::ops;
use cargo::core::MultiShell;
use cargo::util::{CliResult, CliError}; use cargo::util::{CliResult, CliError};
use cargo::util::important_paths::find_project; use cargo::util::important_paths::find_project;
@ -28,7 +29,7 @@ fn main() {
execute_main_without_stdin(execute); execute_main_without_stdin(execute);
} }
fn execute(options: Options) -> CliResult<Option<()>> { fn execute(options: Options, shell: &mut MultiShell) -> CliResult<Option<()>> {
debug!("executing; cmd=cargo-compile; args={}", os::args()); debug!("executing; cmd=cargo-compile; args={}", os::args());
let root = match options.manifest_path { let root = match options.manifest_path {
@ -42,7 +43,7 @@ fn execute(options: Options) -> CliResult<Option<()>> {
})) }))
}; };
ops::compile(&root).map(|_| None).map_err(|err| { ops::compile(&root, shell).map(|_| None).map_err(|err| {
CliError::from_boxed(err, 101) CliError::from_boxed(err, 101)
}) })
} }

View File

@ -9,6 +9,7 @@ extern crate url;
extern crate hammer; extern crate hammer;
use cargo::{execute_main_without_stdin}; use cargo::{execute_main_without_stdin};
use cargo::core::MultiShell;
use cargo::core::source::{Source,SourceId}; use cargo::core::source::{Source,SourceId};
use cargo::sources::git::{GitSource}; use cargo::sources::git::{GitSource};
use cargo::util::{Config, CliResult, CliError, Require, human}; use cargo::util::{Config, CliResult, CliError, Require, human};
@ -26,7 +27,7 @@ fn main() {
execute_main_without_stdin(execute); execute_main_without_stdin(execute);
} }
fn execute(options: Options) -> CliResult<Option<()>> { fn execute(options: Options, shell: &mut MultiShell) -> CliResult<Option<()>> {
let Options { url, reference, .. } = options; let Options { url, reference, .. } = options;
let url: Url = try!(from_str(url.as_slice()) let url: Url = try!(from_str(url.as_slice())
@ -36,8 +37,8 @@ fn execute(options: Options) -> CliResult<Option<()>> {
let source_id = SourceId::for_git(&url, reference.as_slice()); let source_id = SourceId::for_git(&url, reference.as_slice());
let config = try!(Config::new().map_err(|e| CliError::from_boxed(e, 1))); let mut config = try!(Config::new(shell).map_err(|e| CliError::from_boxed(e, 1)));
let mut source = GitSource::new(&source_id, &config); let mut source = GitSource::new(&source_id, &mut config);
try!(source.update().map_err(|e| { try!(source.update().map_err(|e| {
CliError::new(format!("Couldn't update {}: {}", source, e), 1) CliError::new(format!("Couldn't update {}: {}", source, e), 1)

View File

@ -8,7 +8,7 @@ extern crate serialize;
extern crate hammer; extern crate hammer;
use cargo::{execute_main_without_stdin}; use cargo::{execute_main_without_stdin};
use cargo::core::{Package, Source, SourceId}; use cargo::core::{MultiShell, Package, Source, SourceId};
use cargo::util::{CliResult, CliError}; use cargo::util::{CliResult, CliError};
use cargo::sources::{PathSource}; use cargo::sources::{PathSource};
@ -23,7 +23,7 @@ fn main() {
execute_main_without_stdin(execute); execute_main_without_stdin(execute);
} }
fn execute(options: Options) -> CliResult<Option<Package>> { fn execute(options: Options, _: &mut MultiShell) -> CliResult<Option<Package>> {
let path = Path::new(options.manifest_path.as_slice()); let path = Path::new(options.manifest_path.as_slice());
let source_id = SourceId::for_path(&path); let source_id = SourceId::for_path(&path);
let mut source = PathSource::new(&source_id); let mut source = PathSource::new(&source_id);

View File

@ -11,7 +11,8 @@ use hammer::{FlagConfig,FlagConfiguration};
use std::os; use std::os;
use std::io::process::{Command,InheritFd,ExitStatus,ExitSignal}; use std::io::process::{Command,InheritFd,ExitStatus,ExitSignal};
use serialize::Encodable; use serialize::Encodable;
use cargo::{GlobalFlags, NoFlags, execute_main_without_stdin, handle_error}; use cargo::{GlobalFlags, NoFlags, execute_main_without_stdin, handle_error, shell};
use cargo::core::MultiShell;
use cargo::util::important_paths::find_project; use cargo::util::important_paths::find_project;
use cargo::util::{CliError, CliResult, Require, config, human}; use cargo::util::{CliError, CliResult, Require, config, human};
@ -34,7 +35,7 @@ fn execute() {
let (cmd, args) = match process(os::args()) { let (cmd, args) = match process(os::args()) {
Ok((cmd, args)) => (cmd, args), Ok((cmd, args)) => (cmd, args),
Err(err) => return handle_error(err, false) Err(err) => return handle_error(err, &mut shell(), false)
}; };
match cmd.as_slice() { match cmd.as_slice() {
@ -68,9 +69,9 @@ fn execute() {
match command { match command {
Ok(ExitStatus(0)) => (), Ok(ExitStatus(0)) => (),
Ok(ExitStatus(i)) | Ok(ExitSignal(i)) => { Ok(ExitStatus(i)) | Ok(ExitSignal(i)) => {
handle_error(CliError::new("", i as uint), false) handle_error(CliError::new("", i as uint), &mut shell(), false)
} }
Err(_) => handle_error(CliError::new("No such subcommand", 127), false) Err(_) => handle_error(CliError::new("No such subcommand", 127), &mut shell(), false)
} }
} }
} }
@ -104,7 +105,7 @@ impl FlagConfig for ConfigForKeyFlags {
} }
} }
fn config_for_key(args: ConfigForKeyFlags) -> CliResult<Option<ConfigOut>> { fn config_for_key(args: ConfigForKeyFlags, _: &mut MultiShell) -> CliResult<Option<ConfigOut>> {
let value = try!(config::get_config(os::getcwd(), let value = try!(config::get_config(os::getcwd(),
args.key.as_slice()).map_err(|_| { args.key.as_slice()).map_err(|_| {
CliError::new("Couldn't load configuration", 1) CliError::new("Couldn't load configuration", 1)
@ -132,7 +133,7 @@ impl FlagConfig for ConfigListFlags {
} }
} }
fn config_list(args: ConfigListFlags) -> CliResult<Option<ConfigOut>> { fn config_list(args: ConfigListFlags, _: &mut MultiShell) -> CliResult<Option<ConfigOut>> {
let configs = try!(config::all_configs(os::getcwd()).map_err(|_| let configs = try!(config::all_configs(os::getcwd()).map_err(|_|
CliError::new("Couldn't load configuration", 1))); CliError::new("Couldn't load configuration", 1)));
@ -146,7 +147,7 @@ fn config_list(args: ConfigListFlags) -> CliResult<Option<ConfigOut>> {
} }
} }
fn locate_project(_: NoFlags) -> CliResult<Option<ProjectLocation>> { fn locate_project(_: NoFlags, _: &mut MultiShell) -> CliResult<Option<ProjectLocation>> {
let root = try!(find_project(os::getcwd(), "Cargo.toml").map_err(|e| { let root = try!(find_project(os::getcwd(), "Cargo.toml").map_err(|e| {
CliError::from_boxed(e, 1) CliError::from_boxed(e, 1)
})); }));

View File

@ -32,6 +32,7 @@ pub use self::summary::{
pub use self::shell::{ pub use self::shell::{
Shell, Shell,
MultiShell,
ShellConfig ShellConfig
}; };

View File

@ -118,7 +118,7 @@ impl Package {
ret ret
} }
pub fn get_fingerprint(&self, config: &Config) -> CargoResult<String> { pub fn get_fingerprint(&self, config: &mut Config) -> CargoResult<String> {
let mut sources = self.get_source_ids(); let mut sources = self.get_source_ids();
// Sort the sources just to make sure we have a consistent fingerprint. // Sort the sources just to make sure we have a consistent fingerprint.
sources.sort_by(|a, b| { sources.sort_by(|a, b| {

View File

@ -1,5 +1,5 @@
use std::vec::Vec; use std::vec::Vec;
use core::{Source, SourceId, Summary, Dependency, PackageId, Package}; use core::{MultiShell, Source, SourceId, Summary, Dependency, PackageId, Package};
use util::{CargoResult, ChainError, Config, human}; use util::{CargoResult, ChainError, Config, human};
pub trait Registry { pub trait Registry {
@ -15,17 +15,20 @@ impl Registry for Vec<Summary> {
} }
} }
pub struct PackageRegistry { pub struct PackageRegistry<'a> {
sources: Vec<Box<Source>>, sources: Vec<Box<Source>>,
overrides: Vec<Summary>, overrides: Vec<Summary>,
summaries: Vec<Summary>, summaries: Vec<Summary>,
searched: Vec<SourceId> searched: Vec<SourceId>,
shell: &'a mut MultiShell
} }
impl PackageRegistry { impl<'a> PackageRegistry<'a> {
pub fn new(source_ids: Vec<SourceId>, pub fn new<'a>(source_ids: Vec<SourceId>,
override_ids: Vec<SourceId>) -> CargoResult<PackageRegistry> { override_ids: Vec<SourceId>,
let mut reg = PackageRegistry::empty(); shell: &'a mut MultiShell) -> CargoResult<PackageRegistry<'a>> {
let mut reg = PackageRegistry::empty(shell);
for id in source_ids.iter() { for id in source_ids.iter() {
try!(reg.load(id, false)); try!(reg.load(id, false));
@ -38,12 +41,13 @@ impl PackageRegistry {
Ok(reg) Ok(reg)
} }
fn empty() -> PackageRegistry { fn empty<'a>(shell: &'a mut MultiShell) -> PackageRegistry<'a> {
PackageRegistry { PackageRegistry {
sources: vec!(), sources: vec!(),
overrides: vec!(), overrides: vec!(),
summaries: vec!(), summaries: vec!(),
searched: vec!() searched: vec!(),
shell: shell
} }
} }
@ -75,11 +79,10 @@ impl PackageRegistry {
Ok(()) Ok(())
} }
fn load(&mut self, namespace: &SourceId, fn load(&mut self, namespace: &SourceId, override: bool) -> CargoResult<()> {
override: bool) -> CargoResult<()> {
(|| { (|| {
let mut source = namespace.load(&try!(Config::new())); let mut source = namespace.load(&mut try!(Config::new(self.shell)));
let dst = if override {&mut self.overrides} else {&mut self.summaries}; let dst = if override {&mut self.overrides} else {&mut self.summaries};
// Ensure the source has fetched all necessary remote data. // Ensure the source has fetched all necessary remote data.
@ -103,7 +106,7 @@ impl PackageRegistry {
} }
} }
impl Registry for PackageRegistry { impl<'a> Registry for PackageRegistry<'a> {
fn query(&mut self, dep: &Dependency) -> CargoResult<Vec<Summary>> { fn query(&mut self, dep: &Dependency) -> CargoResult<Vec<Summary>> {
let overrides = try!(self.overrides.query(dep)); // this can never fail in practice let overrides = try!(self.overrides.query(dep)); // this can never fail in practice

View File

@ -1,8 +1,9 @@
use term; use term;
use term::{Terminal,color}; use term::{Terminal,color};
use term::color::{Color, BLACK}; use term::color::{Color, BLACK, RED, GREEN};
use term::attr::Attr; use term::attr::{Attr, Bold};
use std::io::{IoResult, stderr}; use std::io::{IoResult, stderr};
use std::fmt::Show;
pub struct ShellConfig { pub struct ShellConfig {
pub color: bool, pub color: bool,
@ -20,6 +21,39 @@ pub struct Shell {
config: ShellConfig config: ShellConfig
} }
pub struct MultiShell {
out: Shell,
err: Shell
}
impl MultiShell {
pub fn new(out: Shell, err: Shell) -> MultiShell {
MultiShell { out: out, err: err }
}
pub fn out<'a>(&'a mut self) -> &'a mut Shell {
&mut self.out
}
pub fn err<'a>(&'a mut self) -> &'a mut Shell {
&mut self.err
}
pub fn say<T: ToStr>(&mut self, message: T, color: Color) -> IoResult<()> {
self.out().say(message, color)
}
pub fn status<T: Show, U: Show>(&mut self, status: T, message: U) -> IoResult<()> {
self.out().say_status(status, message, GREEN)
}
pub fn error<T: ToStr>(&mut self, message: T) -> IoResult<()> {
self.err().say(message, RED)
}
}
impl Shell { impl Shell {
pub fn create(out: Box<Writer>, config: ShellConfig) -> Shell { pub fn create(out: Box<Writer>, config: ShellConfig) -> Shell {
if config.tty && config.color { if config.tty && config.color {
@ -52,6 +86,17 @@ impl Shell {
try!(self.flush()); try!(self.flush());
Ok(()) Ok(())
} }
pub fn say_status<T: Show, U: Show>(&mut self, status: T, message: U, color: Color) -> IoResult<()> {
try!(self.reset());
if color != BLACK { try!(self.fg(color)); }
if self.supports_attr(Bold) { try!(self.attr(Bold)); }
try!(self.write_str(format!("{:>12}", status).as_slice()));
try!(self.reset());
try!(self.write_line(format!(" {}", message).as_slice()));
try!(self.flush());
Ok(())
}
} }
impl Terminal<Box<Writer>> for Shell { impl Terminal<Box<Writer>> for Shell {

View File

@ -115,7 +115,7 @@ impl SourceId {
} }
} }
pub fn load(&self, config: &Config) -> Box<Source> { pub fn load(&self, config: &mut Config) -> Box<Source> {
match self.kind { match self.kind {
GitKind(..) => { GitKind(..) => {
box GitSource::new(self, config) as Box<Source> box GitSource::new(self, config) as Box<Source>

View File

@ -21,12 +21,12 @@ extern crate hamcrest;
use serialize::{Decoder, Encoder, Decodable, Encodable, json}; use serialize::{Decoder, Encoder, Decodable, Encodable, json};
use std::io; use std::io;
use std::io::stderr; use std::io::{stdout, stderr};
use std::io::stdio::stderr_raw; use std::io::stdio::{stdout_raw, stderr_raw};
use hammer::{FlagDecoder, FlagConfig, UsageDecoder, HammerError}; use hammer::{FlagDecoder, FlagConfig, UsageDecoder, HammerError};
use core::{Shell, ShellConfig}; use core::{Shell, MultiShell, ShellConfig};
use term::color::{RED, BLACK}; use term::color::{BLACK};
pub use util::{CargoError, CliError, CliResult, human}; pub use util::{CargoError, CliError, CliResult, human};
@ -99,47 +99,55 @@ pub fn execute_main<'a,
T: RepresentsFlags, T: RepresentsFlags,
U: RepresentsJSON, U: RepresentsJSON,
V: Encodable<json::Encoder<'a>, io::IoError>>( V: Encodable<json::Encoder<'a>, io::IoError>>(
exec: fn(T, U) -> CliResult<Option<V>>) exec: fn(T, U, &mut MultiShell) -> CliResult<Option<V>>)
{ {
fn call<'a, fn call<'a,
T: RepresentsFlags, T: RepresentsFlags,
U: RepresentsJSON, U: RepresentsJSON,
V: Encodable<json::Encoder<'a>, io::IoError>>( V: Encodable<json::Encoder<'a>, io::IoError>>(
exec: fn(T, U) -> CliResult<Option<V>>, exec: fn(T, U, &mut MultiShell) -> CliResult<Option<V>>,
shell: &mut MultiShell,
args: &[String]) args: &[String])
-> CliResult<Option<V>> -> CliResult<Option<V>>
{ {
let flags = try!(flags_from_args::<T>(args)); let flags = try!(flags_from_args::<T>(args));
let json = try!(json_from_stdin::<U>()); let json = try!(json_from_stdin::<U>());
exec(flags, json) exec(flags, json, shell)
} }
match global_flags() { process::<T, V>(|rest, shell| call(exec, shell, rest));
Err(e) => handle_error(e, true),
Ok(val) => process_executed(call(exec, val.rest.as_slice()), val)
}
} }
pub fn execute_main_without_stdin<'a, pub fn execute_main_without_stdin<'a,
T: RepresentsFlags, T: RepresentsFlags,
V: Encodable<json::Encoder<'a>, io::IoError>>( V: Encodable<json::Encoder<'a>, io::IoError>>(
exec: fn(T) -> CliResult<Option<V>>) exec: fn(T, &mut MultiShell) -> CliResult<Option<V>>)
{ {
fn call<'a, fn call<'a,
T: RepresentsFlags, T: RepresentsFlags,
V: Encodable<json::Encoder<'a>, io::IoError>>( V: Encodable<json::Encoder<'a>, io::IoError>>(
exec: fn(T) -> CliResult<Option<V>>, exec: fn(T, &mut MultiShell) -> CliResult<Option<V>>,
shell: &mut MultiShell,
args: &[String]) args: &[String])
-> CliResult<Option<V>> -> CliResult<Option<V>>
{ {
let flags = try!(flags_from_args::<T>(args)); let flags = try!(flags_from_args::<T>(args));
exec(flags, shell)
exec(flags)
} }
process::<T, V>(|rest, shell| call(exec, shell, rest));
}
fn process<'a,
T: RepresentsFlags,
V: Encodable<json::Encoder<'a>, io::IoError>>(
callback: |&[String], &mut MultiShell| -> CliResult<Option<V>>) {
let mut shell = shell();
match global_flags() { match global_flags() {
Err(e) => handle_error(e, true), Err(e) => handle_error(e, &mut shell, true),
Ok(val) => { Ok(val) => {
if val.help { if val.help {
let (desc, options) = hammer::usage::<T>(true); let (desc, options) = hammer::usage::<T>(true);
@ -153,7 +161,7 @@ pub fn execute_main_without_stdin<'a,
let (_, options) = hammer::usage::<GlobalFlags>(false); let (_, options) = hammer::usage::<GlobalFlags>(false);
print!("{}", options); print!("{}", options);
} else { } else {
process_executed(call(exec, val.rest.as_slice()), val) process_executed(callback(val.rest.as_slice(), &mut shell), val, &mut shell)
} }
} }
} }
@ -162,10 +170,10 @@ pub fn execute_main_without_stdin<'a,
pub fn process_executed<'a, pub fn process_executed<'a,
T: Encodable<json::Encoder<'a>, io::IoError>>( T: Encodable<json::Encoder<'a>, io::IoError>>(
result: CliResult<Option<T>>, result: CliResult<Option<T>>,
flags: GlobalFlags) flags: GlobalFlags, shell: &mut MultiShell)
{ {
match result { match result {
Err(e) => handle_error(e, flags.verbose), Err(e) => handle_error(e, shell, flags.verbose),
Ok(encodable) => { Ok(encodable) => {
encodable.map(|encodable| { encodable.map(|encodable| {
let encoded = json::Encoder::str_encode(&encodable); let encoded = json::Encoder::str_encode(&encodable);
@ -175,38 +183,47 @@ pub fn process_executed<'a,
} }
} }
pub fn handle_error(err: CliError, verbose: bool) { pub fn shell() -> MultiShell {
log!(4, "handle_error; err={}", err);
let CliError { error, exit_code, unknown, .. } = err;
let tty = stderr_raw().isatty(); let tty = stderr_raw().isatty();
let stderr = box stderr() as Box<Writer>; let stderr = box stderr() as Box<Writer>;
let config = ShellConfig { color: true, verbose: false, tty: tty }; let config = ShellConfig { color: true, verbose: false, tty: tty };
let mut shell = Shell::create(stderr, config); let err = Shell::create(stderr, config);
let tty = stdout_raw().isatty();
let stdout = box stdout() as Box<Writer>;
let config = ShellConfig { color: true, verbose: false, tty: tty };
let out = Shell::create(stdout, config);
MultiShell::new(out, err)
}
pub fn handle_error(err: CliError, shell: &mut MultiShell, verbose: bool) {
log!(4, "handle_error; err={}", err);
let CliError { error, exit_code, unknown, .. } = err;
if unknown { if unknown {
let _ = shell.say("An unknown error occurred", RED); let _ = shell.error("An unknown error occurred");
} else { } else {
let _ = shell.say(error.to_str(), RED); let _ = shell.error(error.to_str());
} }
if unknown && !verbose { if unknown && !verbose {
let _ = shell.say("\nTo learn more, run the command again with --verbose.", BLACK); let _ = shell.err().say("\nTo learn more, run the command again with --verbose.", BLACK);
} }
if verbose { if verbose {
handle_cause(error, &mut shell); handle_cause(error, shell);
} }
std::os::set_exit_status(exit_code as int); std::os::set_exit_status(exit_code as int);
} }
fn handle_cause(err: &CargoError, shell: &mut Shell) { fn handle_cause(err: &CargoError, shell: &mut MultiShell) {
let _ = shell.say("\nCaused by:", BLACK); let _ = shell.err().say("\nCaused by:", BLACK);
let _ = shell.say(format!(" {}", err.description()), BLACK); let _ = shell.err().say(format!(" {}", err.description()), BLACK);
err.cause().map(|e| handle_cause(e, shell)); err.cause().map(|e| handle_cause(e, shell));
} }

View File

@ -24,13 +24,13 @@
use std::os; use std::os;
use util::config::{ConfigValue}; use util::config::{ConfigValue};
use core::{Source,SourceId,PackageSet,resolver}; use core::{MultiShell, Source, SourceId, PackageSet, resolver};
use core::registry::PackageRegistry; use core::registry::PackageRegistry;
use ops; use ops;
use sources::{PathSource}; use sources::{PathSource};
use util::{CargoResult, Wrap, config, internal, human}; use util::{CargoResult, Wrap, config, internal, human};
pub fn compile(manifest_path: &Path) -> CargoResult<()> { pub fn compile(manifest_path: &Path, shell: &mut MultiShell) -> CargoResult<()> {
log!(4, "compile; manifest-path={}", manifest_path.display()); log!(4, "compile; manifest-path={}", manifest_path.display());
let id = SourceId::for_path(&manifest_path.dir_path()); let id = SourceId::for_path(&manifest_path.dir_path());
@ -45,19 +45,21 @@ pub fn compile(manifest_path: &Path) -> CargoResult<()> {
let override_ids = try!(source_ids_from_config()); let override_ids = try!(source_ids_from_config());
let source_ids = package.get_source_ids(); let source_ids = package.get_source_ids();
let mut registry = try!(PackageRegistry::new(source_ids, override_ids)); let packages = {
let resolved = try!(resolver::resolve(package.get_dependencies(), let mut registry = try!(PackageRegistry::new(source_ids, override_ids, shell));
&mut registry).wrap({ let resolved = try!(resolver::resolve(package.get_dependencies(),
human("unable to resolve dependencies") &mut registry).wrap({
})); human("unable to resolve dependencies")
}));
let packages = try!(registry.get(resolved.as_slice()).wrap({ try!(registry.get(resolved.as_slice()).wrap({
human("unable to get packages from source") human("unable to get packages from source")
})); }))
};
debug!("packages={}", packages); debug!("packages={}", packages);
try!(ops::compile_packages(&package, &PackageSet::new(packages.as_slice()))); try!(ops::compile_packages(&package, &PackageSet::new(packages.as_slice()), shell));
Ok(()) Ok(())
} }

View File

@ -3,7 +3,7 @@ use std::io;
use std::io::{File, IoError}; use std::io::{File, IoError};
use std::str; use std::str;
use core::{Package, PackageSet, Target}; use core::{MultiShell, Package, PackageSet, Target};
use util; use util;
use util::{CargoResult, ChainError, ProcessBuilder, internal, human, CargoError}; use util::{CargoResult, ChainError, ProcessBuilder, internal, human, CargoError};
use util::{Config}; use util::{Config};
@ -16,13 +16,13 @@ struct Context<'a> {
primary: bool, primary: bool,
rustc_version: &'a str, rustc_version: &'a str,
compiled_anything: bool, compiled_anything: bool,
config: &'a Config, config: &'a mut Config<'a>
} }
pub fn compile_packages(pkg: &Package, deps: &PackageSet) -> CargoResult<()> { pub fn compile_packages(pkg: &Package, deps: &PackageSet, shell: &mut MultiShell) -> CargoResult<()> {
debug!("compile_packages; pkg={}; deps={}", pkg, deps); debug!("compile_packages; pkg={}; deps={}", pkg, deps);
let config = try!(Config::new()); let mut config = try!(Config::new(shell));
let target_dir = pkg.get_absolute_target_dir(); let target_dir = pkg.get_absolute_target_dir();
let deps_target_dir = target_dir.join("deps"); let deps_target_dir = target_dir.join("deps");
@ -46,7 +46,7 @@ pub fn compile_packages(pkg: &Package, deps: &PackageSet) -> CargoResult<()> {
primary: false, primary: false,
rustc_version: rustc_version.as_slice(), rustc_version: rustc_version.as_slice(),
compiled_anything: false, compiled_anything: false,
config: &config, config: &mut config
}; };
// Traverse the dependencies in topological order // Traverse the dependencies in topological order
@ -78,14 +78,14 @@ fn compile_pkg(pkg: &Package, cx: &mut Context) -> CargoResult<()> {
pkg.get_name())); pkg.get_name()));
let (is_fresh, fingerprint) = try!(is_fresh(pkg, &fingerprint_loc, cx)); let (is_fresh, fingerprint) = try!(is_fresh(pkg, &fingerprint_loc, cx));
if !cx.compiled_anything && is_fresh { if !cx.compiled_anything && is_fresh {
println!("Skipping fresh {}", pkg); try!(cx.config.shell().status("Fresh", pkg));
return Ok(()) return Ok(())
} }
// Alright, so this package is not fresh and we need to compile it. Start // Alright, so this package is not fresh and we need to compile it. Start
// off by printing a nice helpful message and then run the custom build // off by printing a nice helpful message and then run the custom build
// command if one is present. // command if one is present.
println!("Compiling {}", pkg); try!(cx.config.shell().status("Compiling", pkg));
match pkg.get_manifest().get_build() { match pkg.get_manifest().get_build() {
Some(cmd) => try!(compile_custom(pkg, cmd, cx)), Some(cmd) => try!(compile_custom(pkg, cmd, cx)),
@ -110,7 +110,7 @@ fn compile_pkg(pkg: &Package, cx: &mut Context) -> CargoResult<()> {
} }
fn is_fresh(dep: &Package, loc: &Path, fn is_fresh(dep: &Package, loc: &Path,
cx: &Context) -> CargoResult<(bool, String)> { cx: &mut Context) -> CargoResult<(bool, String)> {
let new_fingerprint = format!("{}{}", cx.rustc_version, let new_fingerprint = format!("{}{}", cx.rustc_version,
try!(dep.get_fingerprint(cx.config))); try!(dep.get_fingerprint(cx.config)));
let mut file = match File::open(loc) { let mut file = match File::open(loc) {

View File

@ -15,16 +15,17 @@ use sources::git::utils::{GitReference,GitRemote,Master,Other};
/* TODO: Refactor GitSource to delegate to a PathSource /* TODO: Refactor GitSource to delegate to a PathSource
*/ */
pub struct GitSource { pub struct GitSource<'a, 'b> {
id: SourceId, id: SourceId,
remote: GitRemote, remote: GitRemote,
reference: GitReference, reference: GitReference,
db_path: Path, db_path: Path,
checkout_path: Path checkout_path: Path,
config: &'a mut Config<'b>
} }
impl GitSource { impl<'a, 'b> GitSource<'a, 'b> {
pub fn new(source_id: &SourceId, config: &Config) -> GitSource { pub fn new<'a, 'b>(source_id: &SourceId, config: &'a mut Config<'b>) -> GitSource<'a, 'b> {
assert!(source_id.is_git(), "id is not git, id={}", source_id); assert!(source_id.is_git(), "id is not git, id={}", source_id);
let reference = match source_id.kind { let reference = match source_id.kind {
@ -46,7 +47,8 @@ impl GitSource {
remote: remote, remote: remote,
reference: GitReference::for_str(reference.as_slice()), reference: GitReference::for_str(reference.as_slice()),
db_path: db_path, db_path: db_path,
checkout_path: checkout_path checkout_path: checkout_path,
config: config
} }
} }
@ -79,7 +81,7 @@ fn to_hex(num: u64) -> String {
writer.get_ref().to_hex() writer.get_ref().to_hex()
} }
impl Show for GitSource { impl<'a, 'b> Show for GitSource<'a, 'b> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
try!(write!(f, "git repo at {}", self.remote.get_url())); try!(write!(f, "git repo at {}", self.remote.get_url()));
@ -90,9 +92,11 @@ impl Show for GitSource {
} }
} }
impl Source for GitSource { impl<'a, 'b> Source for GitSource<'a, 'b> {
fn update(&mut self) -> CargoResult<()> { fn update(&mut self) -> CargoResult<()> {
println!("Updating git repository `{}`", self.remote.get_url()); try!(self.config.shell().status("Updating",
format!("git repository `{}`", self.remote.get_url())));
log!(5, "updating git source `{}`", self.remote); log!(5, "updating git source `{}`", self.remote);
let repo = try!(self.remote.checkout(&self.db_path)); let repo = try!(self.remote.checkout(&self.db_path));
try!(repo.copy_to(self.reference.as_slice(), &self.checkout_path)); try!(repo.copy_to(self.reference.as_slice(), &self.checkout_path));

View File

@ -2,19 +2,22 @@ use std::{io,fmt,os};
use std::collections::HashMap; use std::collections::HashMap;
use serialize::{Encodable,Encoder}; use serialize::{Encodable,Encoder};
use toml; use toml;
use core::MultiShell;
use util::{CargoResult, CargoError, ChainError, Require, internal, human}; use util::{CargoResult, CargoError, ChainError, Require, internal, human};
pub struct Config { pub struct Config<'a> {
home_path: Path home_path: Path,
shell: &'a mut MultiShell
} }
impl Config { impl<'a> Config<'a> {
pub fn new() -> CargoResult<Config> { pub fn new<'a>(shell: &'a mut MultiShell) -> CargoResult<Config<'a>> {
Ok(Config { Ok(Config {
home_path: try!(os::homedir().require(|| { home_path: try!(os::homedir().require(|| {
human("Cargo couldn't find your home directory. \ human("Cargo couldn't find your home directory. \
This probably means that $HOME was not set.") This probably means that $HOME was not set.")
})) })),
shell: shell
}) })
} }
@ -25,6 +28,10 @@ impl Config {
pub fn git_checkout_path(&self) -> Path { pub fn git_checkout_path(&self) -> Path {
self.home_path.join(".cargo").join("git").join("checkouts") self.home_path.join(".cargo").join("git").join("checkouts")
} }
pub fn shell<'a>(&'a mut self) -> &'a mut MultiShell {
&mut *self.shell
}
} }
#[deriving(Eq,PartialEq,Clone,Encodable,Decodable)] #[deriving(Eq,PartialEq,Clone,Encodable,Decodable)]

View File

@ -9,7 +9,6 @@ use std::str;
use std::vec::Vec; use std::vec::Vec;
use std::fmt::Show; use std::fmt::Show;
use ham = hamcrest; use ham = hamcrest;
use cargo::core::shell;
use cargo::util::{process,ProcessBuilder}; use cargo::util::{process,ProcessBuilder};
use cargo::util::ProcessError; use cargo::util::ProcessError;
@ -305,8 +304,6 @@ impl<'a> ham::Matcher<&'a [u8]> for ShellWrites {
fn matches(&self, actual: &[u8]) fn matches(&self, actual: &[u8])
-> ham::MatchResult -> ham::MatchResult
{ {
use term::Terminal;
println!("{}", actual); println!("{}", actual);
let actual = std::str::from_utf8_lossy(actual); let actual = std::str::from_utf8_lossy(actual);
let actual = actual.to_str(); let actual = actual.to_str();

View File

@ -9,6 +9,8 @@ use cargo::util::{process,realpath};
fn setup() { fn setup() {
} }
static COMPILING: &'static str = " Compiling";
fn basic_bin_manifest(name: &str) -> String { fn basic_bin_manifest(name: &str) -> String {
format!(r#" format!(r#"
[project] [project]
@ -143,9 +145,10 @@ test!(cargo_compile_with_warnings_in_a_dep_package {
assert_that(p.cargo_process("cargo-compile"), assert_that(p.cargo_process("cargo-compile"),
execs() execs()
.with_stdout(format!("Compiling bar v0.5.0 (file:{})\n\ .with_stdout(format!("{} bar v0.5.0 (file:{})\n\
Compiling foo v0.5.0 (file:{})\n", {} foo v0.5.0 (file:{})\n",
bar.display(), main.display())) COMPILING, bar.display(),
COMPILING, main.display()))
.with_stderr("")); .with_stderr(""));
assert_that(&p.root().join("target/foo"), existing_file()); assert_that(&p.root().join("target/foo"), existing_file());
@ -343,7 +346,7 @@ test!(custom_build {
"#); "#);
assert_that(p.cargo_process("cargo-compile"), assert_that(p.cargo_process("cargo-compile"),
execs().with_status(0) execs().with_status(0)
.with_stdout(format!("Compiling foo v0.5.0 (file:{})\n", .with_stdout(format!(" Compiling foo v0.5.0 (file:{})\n",
p.root().display())) p.root().display()))
.with_stderr("")); .with_stderr(""));
}) })

View File

@ -5,6 +5,10 @@ use hamcrest::{assert_that,existing_file};
use cargo; use cargo;
use cargo::util::{ProcessError, process}; use cargo::util::{ProcessError, process};
static COMPILING: &'static str = " Compiling";
static FRESH: &'static str = " Fresh";
static UPDATING: &'static str = " Updating";
fn setup() { fn setup() {
} }
@ -83,11 +87,12 @@ test!(cargo_compile_simple_git_dep {
assert_that(project.cargo_process("cargo-compile"), assert_that(project.cargo_process("cargo-compile"),
execs() execs()
.with_stdout(format!("Updating git repository `file:{}`\n\ .with_stdout(format!("{} git repository `file:{}`\n\
Compiling dep1 v0.5.0 (file:{})\n\ {} dep1 v0.5.0 (file:{})\n\
Compiling foo v0.5.0 (file:{})\n", {} foo v0.5.0 (file:{})\n",
git_root.display(), git_root.display(), UPDATING, git_root.display(),
root.display())) COMPILING, git_root.display(),
COMPILING, root.display()))
.with_stderr("")); .with_stderr(""));
assert_that(&project.root().join("target/foo"), existing_file()); assert_that(&project.root().join("target/foo"), existing_file());
@ -211,31 +216,31 @@ test!(recompilation {
// First time around we should compile both foo and bar // First time around we should compile both foo and bar
assert_that(p.cargo_process("cargo-compile"), assert_that(p.cargo_process("cargo-compile"),
execs().with_stdout(format!("Updating git repository `file:{}`\n\ execs().with_stdout(format!("{} git repository `file:{}`\n\
Compiling bar v0.5.0 (file:{})\n\ {} bar v0.5.0 (file:{})\n\
Compiling foo v0.5.0 (file:{})\n", {} foo v0.5.0 (file:{})\n",
git_project.root().display(), UPDATING, git_project.root().display(),
git_project.root().display(), COMPILING, git_project.root().display(),
p.root().display()))); COMPILING, p.root().display())));
// Don't recompile the second time // Don't recompile the second time
assert_that(p.process("cargo-compile"), assert_that(p.process("cargo-compile"),
execs().with_stdout(format!("Updating git repository `file:{}`\n\ execs().with_stdout(format!("{} git repository `file:{}`\n\
Skipping fresh bar v0.5.0 (file:{})\n\ {} bar v0.5.0 (file:{})\n\
Skipping fresh foo v0.5.0 (file:{})\n", {} foo v0.5.0 (file:{})\n",
git_project.root().display(), UPDATING, git_project.root().display(),
git_project.root().display(), FRESH, git_project.root().display(),
p.root().display()))); FRESH, p.root().display())));
// Modify a file manually, shouldn't trigger a recompile // Modify a file manually, shouldn't trigger a recompile
File::create(&git_project.root().join("src/bar.rs")).write_str(r#" File::create(&git_project.root().join("src/bar.rs")).write_str(r#"
pub fn bar() { println!("hello!"); } pub fn bar() { println!("hello!"); }
"#).assert(); "#).assert();
assert_that(p.process("cargo-compile"), assert_that(p.process("cargo-compile"),
execs().with_stdout(format!("Updating git repository `file:{}`\n\ execs().with_stdout(format!("{} git repository `file:{}`\n\
Skipping fresh bar v0.5.0 (file:{})\n\ {} bar v0.5.0 (file:{})\n\
Skipping fresh foo v0.5.0 (file:{})\n", {} foo v0.5.0 (file:{})\n",
git_project.root().display(), UPDATING, git_project.root().display(),
git_project.root().display(), FRESH, git_project.root().display(),
p.root().display()))); FRESH, p.root().display())));
// Commit the changes and make sure we trigger a recompile // Commit the changes and make sure we trigger a recompile
File::create(&git_project.root().join("src/bar.rs")).write_str(r#" File::create(&git_project.root().join("src/bar.rs")).write_str(r#"
pub fn bar() { println!("hello!"); } pub fn bar() { println!("hello!"); }
@ -244,10 +249,10 @@ test!(recompilation {
git_project.process("git").args(["commit", "-m", "test"]).exec_with_output() git_project.process("git").args(["commit", "-m", "test"]).exec_with_output()
.assert(); .assert();
assert_that(p.process("cargo-compile"), assert_that(p.process("cargo-compile"),
execs().with_stdout(format!("Updating git repository `file:{}`\n\ execs().with_stdout(format!("{} git repository `file:{}`\n\
Compiling bar v0.5.0 (file:{})\n\ {} bar v0.5.0 (file:{})\n\
Compiling foo v0.5.0 (file:{})\n", {} foo v0.5.0 (file:{})\n",
git_project.root().display(), UPDATING, git_project.root().display(),
git_project.root().display(), COMPILING, git_project.root().display(),
p.root().display()))); COMPILING, p.root().display())));
}) })

View File

@ -9,6 +9,9 @@ use cargo::util::{process};
fn setup() { fn setup() {
} }
static COMPILING: &'static str = " Compiling";
static FRESH: &'static str = " Fresh";
test!(cargo_compile_with_nested_deps_shorthand { test!(cargo_compile_with_nested_deps_shorthand {
let p = project("foo") let p = project("foo")
.file("Cargo.toml", r#" .file("Cargo.toml", r#"
@ -115,23 +118,23 @@ test!(no_rebuild_dependency {
"#); "#);
// First time around we should compile both foo and bar // First time around we should compile both foo and bar
assert_that(p.cargo_process("cargo-compile"), assert_that(p.cargo_process("cargo-compile"),
execs().with_stdout(format!("Compiling bar v0.5.0 (file:{})\n\ execs().with_stdout(format!("{} bar v0.5.0 (file:{})\n\
Compiling foo v0.5.0 (file:{})\n", {} foo v0.5.0 (file:{})\n",
bar.display(), COMPILING, bar.display(),
p.root().display()))); COMPILING, p.root().display())));
// This time we shouldn't compile bar // This time we shouldn't compile bar
assert_that(p.process("cargo-compile"), assert_that(p.process("cargo-compile"),
execs().with_stdout(format!("Skipping fresh bar v0.5.0 (file:{})\n\ execs().with_stdout(format!("{} bar v0.5.0 (file:{})\n\
Skipping fresh foo v0.5.0 (file:{})\n", {} foo v0.5.0 (file:{})\n",
bar.display(), FRESH, bar.display(),
p.root().display()))); FRESH, p.root().display())));
p.build(); // rebuild the files (rewriting them in the process) p.build(); // rebuild the files (rewriting them in the process)
assert_that(p.process("cargo-compile"), assert_that(p.process("cargo-compile"),
execs().with_stdout(format!("Compiling bar v0.5.0 (file:{})\n\ execs().with_stdout(format!("{} bar v0.5.0 (file:{})\n\
Compiling foo v0.5.0 (file:{})\n", {} foo v0.5.0 (file:{})\n",
bar.display(), COMPILING, bar.display(),
p.root().display()))); COMPILING, p.root().display())));
}) })
test!(deep_dependencies_trigger_rebuild { test!(deep_dependencies_trigger_rebuild {
@ -183,19 +186,19 @@ test!(deep_dependencies_trigger_rebuild {
pub fn baz() {} pub fn baz() {}
"#); "#);
assert_that(p.cargo_process("cargo-compile"), assert_that(p.cargo_process("cargo-compile"),
execs().with_stdout(format!("Compiling baz v0.5.0 (file:{})\n\ execs().with_stdout(format!("{} baz v0.5.0 (file:{})\n\
Compiling bar v0.5.0 (file:{})\n\ {} bar v0.5.0 (file:{})\n\
Compiling foo v0.5.0 (file:{})\n", {} foo v0.5.0 (file:{})\n",
baz.display(), COMPILING, baz.display(),
bar.display(), COMPILING, bar.display(),
p.root().display()))); COMPILING, p.root().display())));
assert_that(p.process("cargo-compile"), assert_that(p.process("cargo-compile"),
execs().with_stdout(format!("Skipping fresh baz v0.5.0 (file:{})\n\ execs().with_stdout(format!("{} baz v0.5.0 (file:{})\n\
Skipping fresh bar v0.5.0 (file:{})\n\ {} bar v0.5.0 (file:{})\n\
Skipping fresh foo v0.5.0 (file:{})\n", {} foo v0.5.0 (file:{})\n",
baz.display(), FRESH, baz.display(),
bar.display(), FRESH, bar.display(),
p.root().display()))); FRESH, p.root().display())));
// Make sure an update to baz triggers a rebuild of bar // Make sure an update to baz triggers a rebuild of bar
// //
@ -206,12 +209,12 @@ test!(deep_dependencies_trigger_rebuild {
pub fn baz() { println!("hello!"); } pub fn baz() { println!("hello!"); }
"#).assert(); "#).assert();
assert_that(p.process("cargo-compile"), assert_that(p.process("cargo-compile"),
execs().with_stdout(format!("Compiling baz v0.5.0 (file:{})\n\ execs().with_stdout(format!("{} baz v0.5.0 (file:{})\n\
Compiling bar v0.5.0 (file:{})\n\ {} bar v0.5.0 (file:{})\n\
Compiling foo v0.5.0 (file:{})\n", {} foo v0.5.0 (file:{})\n",
baz.display(), COMPILING, baz.display(),
bar.display(), COMPILING, bar.display(),
p.root().display()))); COMPILING, p.root().display())));
// Make sure an update to bar doesn't trigger baz // Make sure an update to bar doesn't trigger baz
File::create(&p.root().join("bar/src/bar.rs")).write_str(r#" File::create(&p.root().join("bar/src/bar.rs")).write_str(r#"
@ -219,12 +222,13 @@ test!(deep_dependencies_trigger_rebuild {
pub fn bar() { println!("hello!"); baz::baz(); } pub fn bar() { println!("hello!"); baz::baz(); }
"#).assert(); "#).assert();
assert_that(p.process("cargo-compile"), assert_that(p.process("cargo-compile"),
execs().with_stdout(format!("Skipping fresh baz v0.5.0 (file:{})\n\ execs().with_stdout(format!("{} baz v0.5.0 (file:{})\n\
Compiling bar v0.5.0 (file:{})\n\ {} bar v0.5.0 (file:{})\n\
Compiling foo v0.5.0 (file:{})\n", {} foo v0.5.0 (file:{})\n",
FRESH,
baz.display(), baz.display(),
bar.display(), COMPILING, bar.display(),
p.root().display()))); COMPILING, p.root().display())));
}) })
test!(no_rebuild_two_deps { test!(no_rebuild_two_deps {
@ -276,17 +280,17 @@ test!(no_rebuild_two_deps {
pub fn baz() {} pub fn baz() {}
"#); "#);
assert_that(p.cargo_process("cargo-compile"), assert_that(p.cargo_process("cargo-compile"),
execs().with_stdout(format!("Compiling baz v0.5.0 (file:{})\n\ execs().with_stdout(format!("{} baz v0.5.0 (file:{})\n\
Compiling bar v0.5.0 (file:{})\n\ {} bar v0.5.0 (file:{})\n\
Compiling foo v0.5.0 (file:{})\n", {} foo v0.5.0 (file:{})\n",
baz.display(), COMPILING, baz.display(),
bar.display(), COMPILING, bar.display(),
p.root().display()))); COMPILING, p.root().display())));
assert_that(p.process("cargo-compile"), assert_that(p.process("cargo-compile"),
execs().with_stdout(format!("Skipping fresh baz v0.5.0 (file:{})\n\ execs().with_stdout(format!("{} baz v0.5.0 (file:{})\n\
Skipping fresh bar v0.5.0 (file:{})\n\ {} bar v0.5.0 (file:{})\n\
Skipping fresh foo v0.5.0 (file:{})\n", {} foo v0.5.0 (file:{})\n",
baz.display(), FRESH, baz.display(),
bar.display(), FRESH, bar.display(),
p.root().display()))); FRESH, p.root().display())));
}) })