@@ -6,6 +6,7 @@ use database::{ArtifactId, Commit};
6
6
use log:: debug;
7
7
use std:: collections:: HashSet ;
8
8
use std:: fs;
9
+ use std:: fs:: File ;
9
10
use std:: path:: { Path , PathBuf } ;
10
11
use std:: process;
11
12
use std:: process:: { Command , Stdio } ;
@@ -16,6 +17,7 @@ mod execute;
16
17
mod sysroot;
17
18
18
19
use execute:: { BenchProcessor , Benchmark , BenchmarkName , ProfileProcessor , Profiler } ;
20
+ use std:: io:: Write ;
19
21
use sysroot:: Sysroot ;
20
22
21
23
#[ derive( Debug , Copy , Clone ) ]
@@ -848,6 +850,27 @@ enum Commands {
848
850
849
851
/// Installs the next commit for perf.rust-lang.org
850
852
InstallNext ,
853
+
854
+ /// Download a crate into collector/benchmarks.
855
+ Download ( DownloadCommand ) ,
856
+ }
857
+
858
+ #[ derive( Debug , clap:: Parser ) ]
859
+ struct DownloadCommand {
860
+ /// Name of the benchmark created directory
861
+ #[ clap( long, global = true ) ]
862
+ name : Option < String > ,
863
+
864
+ #[ clap( subcommand) ]
865
+ command : DownloadSubcommand ,
866
+ }
867
+
868
+ #[ derive( Debug , clap:: Parser ) ]
869
+ enum DownloadSubcommand {
870
+ /// Download a crate from a git repository.
871
+ Git { url : String } ,
872
+ /// Download a crate from crates.io.
873
+ Crate { krate : String , version : String } ,
851
874
}
852
875
853
876
fn main_result ( ) -> anyhow:: Result < i32 > {
@@ -1141,7 +1164,99 @@ fn main_result() -> anyhow::Result<i32> {
1141
1164
1142
1165
Ok ( 0 )
1143
1166
}
1167
+ Commands :: Download ( opts) => {
1168
+ let crate_path = match opts. command {
1169
+ DownloadSubcommand :: Git { url } => {
1170
+ download_from_git ( & benchmark_dir, opts. name . as_deref ( ) , & url) ?
1171
+ }
1172
+ DownloadSubcommand :: Crate { krate, version } => {
1173
+ download_from_crates_io ( & benchmark_dir, & krate, & version, opts. name . as_deref ( ) ) ?
1174
+ }
1175
+ } ;
1176
+ prepare_downloaded_benchmark ( & crate_path) ;
1177
+ Ok ( 0 )
1178
+ }
1179
+ }
1180
+ }
1181
+
1182
+ fn download_from_git (
1183
+ benchmark_dir : & Path ,
1184
+ name : Option < & str > ,
1185
+ url : & str ,
1186
+ ) -> anyhow:: Result < PathBuf > {
1187
+ let target = PathBuf :: from ( benchmark_dir) ;
1188
+ let crate_name = match name {
1189
+ Some ( name) => name,
1190
+ None => url
1191
+ . trim_end_matches ( "/" )
1192
+ . trim_end_matches ( ".git" )
1193
+ . split ( "/" )
1194
+ . last ( )
1195
+ . expect ( "Crate name could not be determined from git URL" ) ,
1196
+ } ;
1197
+ let target = target. join ( crate_name) ;
1198
+ if target. exists ( ) {
1199
+ return Err ( anyhow:: anyhow!(
1200
+ "Directory {} already exists" ,
1201
+ target. display( )
1202
+ ) ) ;
1203
+ }
1204
+
1205
+ Command :: new ( "git" )
1206
+ . arg ( "clone" )
1207
+ . arg ( url)
1208
+ . arg ( & target)
1209
+ . output ( )
1210
+ . expect ( "Git clone failed" ) ;
1211
+ Ok ( target)
1212
+ }
1213
+
1214
+ fn download_from_crates_io (
1215
+ benchmark_dir : & Path ,
1216
+ krate : & str ,
1217
+ version : & str ,
1218
+ name : Option < & str > ,
1219
+ ) -> anyhow:: Result < PathBuf > {
1220
+ let full_name = format ! ( "{krate}-{version}" ) ;
1221
+ let name = name. unwrap_or ( & full_name) ;
1222
+ let target = PathBuf :: from ( benchmark_dir) . join ( name) ;
1223
+ if target. exists ( ) {
1224
+ return Err ( anyhow:: anyhow!(
1225
+ "Directory {} already exists" ,
1226
+ target. display( )
1227
+ ) ) ;
1228
+ }
1229
+
1230
+ let url = format ! ( "https://crates.io/api/v1/crates/{krate}/{version}/download" ) ;
1231
+ let body = reqwest:: blocking:: get ( url) . expect ( "Cannot download crate" ) ;
1232
+ let data = flate2:: read:: GzDecoder :: new ( body) ;
1233
+ let mut archive = tar:: Archive :: new ( data) ;
1234
+
1235
+ let tmpdir = tempfile:: TempDir :: new ( ) . unwrap ( ) ;
1236
+ archive. unpack ( & tmpdir) ?;
1237
+ let unpacked_dir = std:: fs:: read_dir ( & tmpdir. path ( ) )
1238
+ . unwrap ( )
1239
+ . next ( )
1240
+ . unwrap ( )
1241
+ . unwrap ( )
1242
+ . path ( ) ;
1243
+ execute:: rename ( & unpacked_dir, & target) ?;
1244
+
1245
+ Ok ( target)
1246
+ }
1247
+
1248
+ fn prepare_downloaded_benchmark ( path : & Path ) {
1249
+ let manifest_path = path. join ( "Cargo.toml" ) ;
1250
+ let manifest = std:: fs:: read_to_string ( & manifest_path) . expect ( "Cannot open Cargo.toml" ) ;
1251
+ if !manifest. contains ( "[workspace]" ) {
1252
+ let mut file = File :: options ( ) . append ( true ) . open ( & manifest_path) . unwrap ( ) ;
1253
+ writeln ! ( file, "[workspace]" ) . expect ( "Cannot append workspace to Cargo.toml" ) ;
1144
1254
}
1255
+ Command :: new ( "cargo" )
1256
+ . arg ( "generate-lockfile" )
1257
+ . current_dir ( manifest_path. parent ( ) . unwrap ( ) )
1258
+ . status ( )
1259
+ . expect ( "Cannot generate lock file" ) ;
1145
1260
}
1146
1261
1147
1262
pub fn get_commit_or_fake_it ( sha : & str ) -> anyhow:: Result < Commit > {
0 commit comments