@@ -848,6 +848,31 @@ enum Commands {
848
848
849
849
/// Installs the next commit for perf.rust-lang.org
850
850
InstallNext ,
851
+
852
+ /// Download a crate into collector/benchmarks.
853
+ Download ( DownloadCommand ) ,
854
+ }
855
+
856
+ #[ derive( Debug , clap:: Parser ) ]
857
+ struct DownloadCommand {
858
+ /// Name of the benchmark created directory
859
+ #[ clap( long, global = true ) ]
860
+ name : Option < String > ,
861
+
862
+ /// Overwrite the benchmark directory if it already exists.
863
+ #[ clap( long, global = true ) ]
864
+ overwrite : bool ,
865
+
866
+ #[ clap( subcommand) ]
867
+ command : DownloadSubcommand ,
868
+ }
869
+
870
+ #[ derive( Debug , clap:: Parser ) ]
871
+ enum DownloadSubcommand {
872
+ /// Download a crate from a git repository.
873
+ Git { url : String } ,
874
+ /// Download a crate from crates.io.
875
+ Crate { krate : String , version : String } ,
851
876
}
852
877
853
878
fn main_result ( ) -> anyhow:: Result < i32 > {
@@ -1141,7 +1166,100 @@ fn main_result() -> anyhow::Result<i32> {
1141
1166
1142
1167
Ok ( 0 )
1143
1168
}
1169
+ Commands :: Download ( cmd) => {
1170
+ let target_dir = get_downloaded_crate_target ( & benchmark_dir, & cmd) ;
1171
+ check_target_dir ( & target_dir, cmd. overwrite ) ?;
1172
+
1173
+ match cmd. command {
1174
+ DownloadSubcommand :: Git { url } => download_from_git ( & target_dir, & url) ?,
1175
+ DownloadSubcommand :: Crate { krate, version } => {
1176
+ download_from_crates_io ( & target_dir, & krate, & version) ?
1177
+ }
1178
+ } ;
1179
+ println ! ( "Benchmark stored at {}" , target_dir. display( ) ) ;
1180
+ Ok ( 0 )
1181
+ }
1182
+ }
1183
+ }
1184
+
1185
+ fn check_target_dir ( target_dir : & Path , overwrite : bool ) -> anyhow:: Result < ( ) > {
1186
+ if target_dir. exists ( ) {
1187
+ if overwrite {
1188
+ std:: fs:: remove_dir_all ( & target_dir) . expect ( & format ! (
1189
+ "Cannot remove previous directory at {}" ,
1190
+ target_dir. display( )
1191
+ ) ) ;
1192
+ } else {
1193
+ return Err ( anyhow:: anyhow!(
1194
+ "Directory {} already exists" ,
1195
+ target_dir. display( )
1196
+ ) ) ;
1197
+ }
1144
1198
}
1199
+ Ok ( ( ) )
1200
+ }
1201
+
1202
+ fn get_downloaded_crate_target ( benchmark_dir : & Path , cmd : & DownloadCommand ) -> PathBuf {
1203
+ let name = cmd. name . clone ( ) . unwrap_or_else ( || match & cmd. command {
1204
+ DownloadSubcommand :: Git { url } => url
1205
+ . trim_end_matches ( "/" )
1206
+ . trim_end_matches ( ".git" )
1207
+ . split ( "/" )
1208
+ . last ( )
1209
+ . expect ( "Crate name could not be determined from git URL" )
1210
+ . to_string ( ) ,
1211
+ DownloadSubcommand :: Crate { krate, version } => format ! ( "{krate}-{version}" ) ,
1212
+ } ) ;
1213
+ PathBuf :: from ( benchmark_dir) . join ( name)
1214
+ }
1215
+
1216
+ fn download_from_git ( target : & Path , url : & str ) -> anyhow:: Result < ( ) > {
1217
+ let tmpdir = tempfile:: TempDir :: new ( ) . unwrap ( ) ;
1218
+ Command :: new ( "git" )
1219
+ . arg ( "clone" )
1220
+ . arg ( url)
1221
+ . arg ( tmpdir. path ( ) )
1222
+ . status ( )
1223
+ . expect ( "Git clone failed" ) ;
1224
+ generate_lockfile ( tmpdir. path ( ) ) ;
1225
+ execute:: rename ( & tmpdir, & target) ?;
1226
+ Ok ( ( ) )
1227
+ }
1228
+
1229
+ fn download_from_crates_io ( target_dir : & Path , krate : & str , version : & str ) -> anyhow:: Result < ( ) > {
1230
+ let url = format ! ( "https://crates.io/api/v1/crates/{krate}/{version}/download" ) ;
1231
+ let response = reqwest:: blocking:: get ( url)
1232
+ . expect ( "Cannot download crate" )
1233
+ . error_for_status ( ) ?;
1234
+
1235
+ let data = flate2:: read:: GzDecoder :: new ( response) ;
1236
+ let mut archive = tar:: Archive :: new ( data) ;
1237
+
1238
+ let tmpdir = tempfile:: TempDir :: new ( ) . unwrap ( ) ;
1239
+ archive. unpack ( & tmpdir) ?;
1240
+ let unpacked_dir = std:: fs:: read_dir ( & tmpdir. path ( ) )
1241
+ . unwrap ( )
1242
+ . next ( )
1243
+ . unwrap ( )
1244
+ . unwrap ( )
1245
+ . path ( ) ;
1246
+ generate_lockfile ( & unpacked_dir) ;
1247
+ execute:: rename ( & unpacked_dir, & target_dir) ?;
1248
+
1249
+ Ok ( ( ) )
1250
+ }
1251
+
1252
+ fn generate_lockfile ( directory : & Path ) {
1253
+ let manifest_path = directory. join ( "Cargo.toml" ) ;
1254
+
1255
+ // Cargo metadata should do nothing if there is already a lockfile present.
1256
+ // Otherwise it will generate a lockfile.
1257
+ Command :: new ( "cargo" )
1258
+ . arg ( "metadata" )
1259
+ . current_dir ( manifest_path. parent ( ) . unwrap ( ) )
1260
+ . stdout ( std:: process:: Stdio :: null ( ) )
1261
+ . status ( )
1262
+ . expect ( "Cannot generate lock file" ) ;
1145
1263
}
1146
1264
1147
1265
pub fn get_commit_or_fake_it ( sha : & str ) -> anyhow:: Result < Commit > {
0 commit comments