@@ -4,9 +4,10 @@ use std::env;
44use std:: ffi:: OsString ;
55use std:: fs:: { self , File } ;
66use std:: io:: prelude:: * ;
7- use std:: io:: SeekFrom ;
7+ use std:: io:: { self , SeekFrom } ;
88use std:: path:: { Path , PathBuf } ;
99use std:: sync:: Arc ;
10+ use std:: sync:: atomic:: { AtomicBool , ATOMIC_BOOL_INIT , Ordering } ;
1011
1112use semver:: Version ;
1213use tempdir:: TempDir ;
@@ -55,36 +56,75 @@ impl Drop for Transaction {
5556}
5657
5758pub fn install ( root : Option < & str > ,
59+ krates : Vec < & str > ,
60+ source_id : & SourceId ,
61+ vers : Option < & str > ,
62+ opts : & ops:: CompileOptions ,
63+ force : bool ) -> CargoResult < ( ) > {
64+ let root = resolve_root ( root, opts. config ) ?;
65+ let map = SourceConfigMap :: new ( opts. config ) ?;
66+
67+ if krates. is_empty ( ) {
68+ install_one ( root, map, None , source_id, vers, opts, force)
69+ } else {
70+ let mut success = vec ! [ ] ;
71+ let mut errors = vec ! [ ] ;
72+ for krate in krates {
73+ let root = root. clone ( ) ;
74+ let map = map. clone ( ) ;
75+ match install_one ( root, map, Some ( krate) , source_id, vers, opts, force) {
76+ Ok ( ( ) ) => success. push ( krate) ,
77+ Err ( e) => errors. push ( format ! ( "{}: {}" , krate, e) )
78+ }
79+ }
80+
81+ writeln ! ( io:: stderr( ) ,
82+ "\n \n SUMMARY\n \n Successfully installed: {}\n \n Errors:\n \t {}" ,
83+ success. join( ", " ) ,
84+ errors. join( "\n \t " ) ) ?;
85+
86+ Ok ( ( ) )
87+ }
88+ }
89+
90+ fn install_one ( root : Filesystem ,
91+ map : SourceConfigMap ,
5892 krate : Option < & str > ,
5993 source_id : & SourceId ,
6094 vers : Option < & str > ,
6195 opts : & ops:: CompileOptions ,
6296 force : bool ) -> CargoResult < ( ) > {
97+
98+ static ALREADY_UPDATED : AtomicBool = ATOMIC_BOOL_INIT ;
99+ let needs_update = !ALREADY_UPDATED . load ( Ordering :: SeqCst ) ;
100+
63101 let config = opts. config ;
64- let root = resolve_root ( root, config) ?;
65- let map = SourceConfigMap :: new ( config) ?;
102+
66103 let ( pkg, source) = if source_id. is_git ( ) {
67104 select_pkg ( GitSource :: new ( source_id, config) ,
68- krate, vers, config, & mut |git| git. read_packages ( ) ) ?
105+ krate, vers, config, needs_update,
106+ & mut |git| git. read_packages ( ) ) ?
69107 } else if source_id. is_path ( ) {
70- let path = source_id. url ( ) . to_file_path ( ) . ok ( )
71- . expect ( "path sources must have a valid path" ) ;
108+ let path = source_id. url ( ) . to_file_path ( )
109+ . map_err ( | ( ) | CargoError :: from ( "path sources must have a valid path" ) ) ? ;
72110 let mut src = PathSource :: new ( & path, source_id, config) ;
73111 src. update ( ) . chain_err ( || {
74112 format ! ( "`{}` is not a crate root; specify a crate to \
75113 install from crates.io, or use --path or --git to \
76114 specify an alternate source", path. display( ) )
77115 } ) ?;
78116 select_pkg ( PathSource :: new ( & path, source_id, config) ,
79- krate, vers, config, & mut |path| path. read_packages ( ) ) ?
117+ krate, vers, config, needs_update,
118+ & mut |path| path. read_packages ( ) ) ?
80119 } else {
81120 select_pkg ( map. load ( source_id) ?,
82- krate, vers, config,
121+ krate, vers, config, needs_update ,
83122 & mut |_| Err ( "must specify a crate to install from \
84123 crates.io, or use --path or --git to \
85124 specify alternate source". into ( ) ) ) ?
86125 } ;
87126
127+ ALREADY_UPDATED . store ( true , Ordering :: SeqCst ) ;
88128
89129 let mut td_opt = None ;
90130 let overidden_target_dir = if source_id. is_path ( ) {
@@ -267,11 +307,15 @@ fn select_pkg<'a, T>(mut source: T,
267307 name : Option < & str > ,
268308 vers : Option < & str > ,
269309 config : & Config ,
310+ needs_update : bool ,
270311 list_all : & mut FnMut ( & mut T ) -> CargoResult < Vec < Package > > )
271312 -> CargoResult < ( Package , Box < Source + ' a > ) >
272313 where T : Source + ' a
273314{
274- source. update ( ) ?;
315+ if needs_update {
316+ source. update ( ) ?;
317+ }
318+
275319 match name {
276320 Some ( name) => {
277321 let vers = match vers {
0 commit comments