11//! xtask/src/image.rs
22//! Guest Image management commands for the Axvisor build configuration tool
33//! (https://github.com/arceos-hypervisor/xtask).
4- //!
5- //! This module provides functionality to list, download, and extract
4+ //!
5+ //! This module provides functionality to list, download, and remove
66//! pre-built guest images for various supported boards and architectures. The images
7- //! are downloaded from a specified URL base and verified using SHA-256 checksums. The extracted
8- //! images are placed in a specified output directory.
7+ //! are downloaded from a specified URL base and verified using SHA-256 checksums. The downloaded
8+ //! images are automatically extracted to a specified output directory. Images can also be removed
9+ //! from the temporary directory.
910//! ! Usage examples:
1011//!! ```
1112//! // List available images
1213//! xtask image ls
13- //! // Download a specific image
14+ //! // Download a specific image and automatically extract it
1415//! xtask image download evm3588_arceos --output-dir ./images
15- //! // Extract a downloaded image
16- //! xtask image extract ./images/evm3588_arceos.tar.gz --output-dir ./extracted
16+ //! // Download a specific image without extracting
17+ //! xtask image download evm3588_arceos --output-dir ./images --no-extract
18+ //! // Remove a specific image from temp directory
19+ //! xtask image rm evm3588_arceos
1720//! ```
1821
1922use anyhow:: { Result , anyhow} ;
2023use clap:: { Parser , Subcommand } ;
2124use std:: path:: { Path } ;
2225use std:: process:: Command ;
2326use std:: fs;
27+ use std:: env;
2428
2529/// Base URL for downloading images
2630const IMAGE_URL_BASE : & str = "https://github.com/arceos-hypervisor/axvisor-guest/releases/download/v0.0.16/" ;
@@ -37,17 +41,17 @@ pub struct ImageArgs {
3741pub enum ImageCommands {
3842 /// List all available image
3943 Ls ,
40- /// Download the specified image
44+ /// Download the specified image and automatically extract it
4145 Download {
4246 image_name : String ,
4347 #[ arg( short, long) ]
4448 output_dir : Option < String > ,
49+ #[ arg( short, long, help = "Automatically extract after download (default: true)" ) ]
50+ extract : Option < bool > ,
4551 } ,
46- /// Extract the specified image file
47- Extract {
48- input_file : String ,
49- #[ arg( short, long) ]
50- output_dir : Option < String > ,
52+ /// Remove the specified image from temp directory
53+ Rm {
54+ image_name : String ,
5155 } ,
5256}
5357
@@ -202,6 +206,30 @@ impl Image {
202206 }
203207}
204208
209+ /// Verify the SHA256 checksum of a file
210+ /// # Arguments
211+ /// * `file_path` - The path to the file to verify
212+ /// * `expected_sha256` - The expected SHA256 checksum as a hex string
213+ /// # Returns
214+ /// * `Result<bool>` - Result indicating whether the checksum matches
215+ /// # Errors
216+ /// * `anyhow::Error` - If any error occurs during the verification process
217+ fn image_verify_sha256 ( file_path : & Path , expected_sha256 : & str ) -> Result < bool > {
218+ let output = Command :: new ( "sha256sum" )
219+ . arg ( file_path)
220+ . output ( ) ?;
221+
222+ if !output. status . success ( ) {
223+ return Err ( anyhow ! ( "Failed to calculate SHA256" ) ) ;
224+ }
225+
226+ let stdout = String :: from_utf8 ( output. stdout ) ?;
227+ let actual_sha256 = stdout. split_whitespace ( ) . next ( )
228+ . ok_or_else ( || anyhow ! ( "Unable to parse SHA256 output" ) ) ?;
229+
230+ Ok ( actual_sha256 == expected_sha256)
231+ }
232+
205233/// List all available images
206234/// # Returns
207235/// * `Result<()>` - Result indicating success or failure
@@ -236,20 +264,23 @@ fn image_list() -> Result<()> {
236264 // Return Ok to indicate successful execution
237265}
238266
239- /// Download the specified image
267+ /// Download the specified image and optionally extract it
240268/// # Arguments
241269/// * `image_name` - The name of the image to download
242- /// * `output_dir` - Optional output directory to save the downloaded image
270+ /// * `output_dir` - Optional output directory to save the downloaded image
271+ /// * `extract` - Whether to automatically extract the image after download (default: true)
243272/// # Returns
244273/// * `Result<()>` - Result indicating success or failure
245274/// # Errors
246- /// * `anyhow::Error` - If any error occurs during the download process
275+ /// * `anyhow::Error` - If any error occurs during the download or extraction process
247276/// # Examples
248277/// ```
249- /// // Download the evm3588_arceos image to the ./images directory
278+ /// // Download the evm3588_arceos image to the ./images directory and automatically extract it
250279/// xtask image download evm3588_arceos --output-dir ./images
280+ /// // Download the evm3588_arceos image to the ./images directory without extracting
281+ /// xtask image download evm3588_arceos --output-dir ./images --no-extract
251282/// ```
252- fn image_download ( image_name : & str , output_dir : Option < String > ) -> Result < ( ) > {
283+ fn image_download ( image_name : & str , output_dir : Option < String > , extract : bool ) -> Result < ( ) > {
253284 let image = Image :: find_by_name ( image_name)
254285 . ok_or_else ( || anyhow ! ( "Image not found: {}. Use 'xtask image ls' to view available images" , image_name) ) ?;
255286
@@ -267,9 +298,9 @@ fn image_download(image_name: &str, output_dir: Option<String>) -> Result<()> {
267298 }
268299 }
269300 None => {
270- // If not specified, use tmp directory under current working directory
271- let current_dir = std :: env:: current_dir ( ) ? ;
272- current_dir . join ( "tmp " ) . join ( format ! ( "{}.tar.gz" , image_name) )
301+ // If not specified, use system temporary directory
302+ let temp_dir = env:: temp_dir ( ) ;
303+ temp_dir . join ( "axvisor " ) . join ( format ! ( "{}.tar.gz" , image_name) )
273304 }
274305 } ;
275306
@@ -335,99 +366,83 @@ fn image_download(image_name: &str, output_dir: Option<String>) -> Result<()> {
335366 }
336367 }
337368
338- Ok ( ( ) )
339- }
340-
341- /// Verify the SHA256 checksum of a file
342- /// # Arguments
343- /// * `file_path` - The path to the file to verify
344- /// * `expected_sha256` - The expected SHA256 checksum as a hex string
345- /// # Returns
346- /// * `Result<bool>` - Result indicating whether the checksum matches
347- /// # Errors
348- /// * `anyhow::Error` - If any error occurs during the verification process
349- fn image_verify_sha256 ( file_path : & Path , expected_sha256 : & str ) -> Result < bool > {
350- let output = Command :: new ( "sha256sum" )
351- . arg ( file_path)
352- . output ( ) ?;
353-
354- if !output. status . success ( ) {
355- return Err ( anyhow ! ( "Failed to calculate SHA256" ) ) ;
369+ // If extract flag is true, extract the downloaded file
370+ if extract {
371+ println ! ( "Extracting downloaded file..." ) ;
372+
373+ // Determine extraction output directory
374+ let extract_dir = output_path. parent ( )
375+ . ok_or_else ( || anyhow ! ( "Unable to determine parent directory of downloaded file" ) ) ?
376+ . join ( image_name) ;
377+
378+ // Ensure extraction directory exists
379+ fs:: create_dir_all ( & extract_dir) ?;
380+
381+ println ! ( "Extracting to: {}" , extract_dir. display( ) ) ;
382+
383+ // Use tar command to extract file
384+ let mut child = Command :: new ( "tar" )
385+ . arg ( "-xzf" )
386+ . arg ( & output_path)
387+ . arg ( "-C" )
388+ . arg ( & extract_dir)
389+ . spawn ( ) ?;
390+
391+ let status = child. wait ( ) ?;
392+ if !status. success ( ) {
393+ return Err ( anyhow ! ( "Extraction failed, tar exit code: {}" , status) ) ;
394+ }
395+
396+ println ! ( "Extraction completed successfully" ) ;
397+ println ! ( "Image extracted to: {}" , extract_dir. display( ) ) ;
356398 }
357399
358- let stdout = String :: from_utf8 ( output. stdout ) ?;
359- let actual_sha256 = stdout. split_whitespace ( ) . next ( )
360- . ok_or_else ( || anyhow ! ( "Unable to parse SHA256 output" ) ) ?;
361-
362- Ok ( actual_sha256 == expected_sha256)
400+ Ok ( ( ) )
363401}
364402
365- /// Extract the specified image file
403+ /// Remove the specified image from temp directory
366404/// # Arguments
367- /// * `input_file` - The path to the input image file
368- /// * `output_dir` - Optional output directory to extract the image to
405+ /// * `image_name` - The name of the image to remove
369406/// # Returns
370407/// * `Result<()>` - Result indicating success or failure
371408/// # Errors
372- /// * `anyhow::Error` - If any error occurs during the extraction process
409+ /// * `anyhow::Error` - If any error occurs during the removal process
373410/// # Examples
374411/// ```
375- /// // Extract the image file ./images/evm3588_arceos.tar.gz to ./extracted
376- /// xtask image extract ./images/ evm3588_arceos.tar.gz --output-dir ./extracted
412+ /// // Remove the evm3588_arceos image from temp directory
413+ /// xtask image rm evm3588_arceos
377414/// ```
378- fn image_extract ( input_file : & str , output_dir : Option < String > ) -> Result < ( ) > {
379- let input_path = {
380- let path = Path :: new ( input_file) ;
381- if path. is_absolute ( ) {
382- path. to_path_buf ( )
383- } else {
384- let current_dir = std:: env:: current_dir ( ) ?;
385- current_dir. join ( path)
386- }
387- } ;
388-
389- let output_path = match output_dir {
390- Some ( dir) => {
391- let path = Path :: new ( & dir) ;
392- if path. is_absolute ( ) {
393- path. to_path_buf ( )
394- } else {
395- let current_dir = std:: env:: current_dir ( ) ?;
396- current_dir. join ( path)
397- }
398- }
399- None => {
400- input_path. parent ( )
401- . ok_or_else ( || anyhow ! ( "Unable to determine parent directory of input file" ) ) ?
402- . to_path_buf ( )
403- }
404- } ;
415+ fn image_remove ( image_name : & str ) -> Result < ( ) > {
416+ // Check if the image name is valid by looking it up
417+ let _image = Image :: find_by_name ( image_name)
418+ . ok_or_else ( || anyhow ! ( "Image not found: {}. Use 'xtask image ls' to view available images" , image_name) ) ?;
405419
406- // Check if input file exists
407- if !input_path. exists ( ) {
408- return Err ( anyhow ! ( "Input file does not exist: {}" , input_path. display( ) ) ) ;
409- }
420+ let temp_dir = env:: temp_dir ( ) . join ( "axvisor" ) ;
421+ let tar_file = temp_dir. join ( format ! ( "{}.tar.gz" , image_name) ) ;
422+ let extract_dir = temp_dir. join ( image_name) ;
410423
411- // Ensure output directory exists
412- fs:: create_dir_all ( & output_path) ?;
424+ let mut removed = false ;
413425
414- println ! ( "Extracting file: {}" , input_path. display( ) ) ;
415- println ! ( "Target directory: {}" , output_path. display( ) ) ;
426+ // Remove the tar file if it exists
427+ if tar_file. exists ( ) {
428+ println ! ( "Removing tar file: {}" , tar_file. display( ) ) ;
429+ fs:: remove_file ( & tar_file) ?;
430+ removed = true ;
431+ }
416432
417- // Use tar command to extract file
418- let mut child = Command :: new ( "tar" )
419- . arg ( "-xzf" )
420- . arg ( & input_path)
421- . arg ( "-C" )
422- . arg ( & output_path)
423- . spawn ( ) ?;
433+ // Remove the extracted directory if it exists
434+ if extract_dir. exists ( ) {
435+ println ! ( "Removing extracted directory: {}" , extract_dir. display( ) ) ;
436+ fs:: remove_dir_all ( & extract_dir) ?;
437+ removed = true ;
438+ }
424439
425- let status = child. wait ( ) ?;
426- if !status. success ( ) {
427- return Err ( anyhow ! ( "Extraction failed, tar exit code: {}" , status) ) ;
440+ if !removed {
441+ println ! ( "No files found for image: {}" , image_name) ;
442+ } else {
443+ println ! ( "Successfully removed image: {}" , image_name) ;
428444 }
429445
430- println ! ( "Extraction completed" ) ;
431446 Ok ( ( ) )
432447}
433448
@@ -443,18 +458,19 @@ fn image_extract(input_file: &str, output_dir: Option<String>) -> Result<()> {
443458/// // Run image management commands
444459/// xtask image ls
445460/// xtask image download evm3588_arceos --output-dir ./images
446- /// xtask image extract ./images/evm3588_arceos.tar.gz --output-dir ./extracted
461+ /// xtask image download evm3588_arceos --output-dir ./images --no-extract
462+ /// xtask image rm evm3588_arceos
447463/// ```
448464pub fn run_image ( args : ImageArgs ) -> Result < ( ) > {
449465 match args. command {
450466 ImageCommands :: Ls => {
451467 image_list ( ) ?;
452468 }
453- ImageCommands :: Download { image_name, output_dir } => {
454- image_download ( & image_name, output_dir) ?;
469+ ImageCommands :: Download { image_name, output_dir, extract } => {
470+ image_download ( & image_name, output_dir, extract . unwrap_or ( true ) ) ?;
455471 }
456- ImageCommands :: Extract { input_file , output_dir } => {
457- image_extract ( & input_file , output_dir ) ?;
472+ ImageCommands :: Rm { image_name } => {
473+ image_remove ( & image_name ) ?;
458474 }
459475 }
460476
0 commit comments