@@ -94,10 +94,17 @@ impl DevProcess for DevChild {
9494 }
9595}
9696
97+ #[ derive( Debug ) ]
98+ struct Target {
99+ name : String ,
100+ installed : bool ,
101+ }
102+
97103pub struct Rust {
98104 app_settings : RustAppSettings ,
99105 config_features : Vec < String > ,
100106 product_name : Option < String > ,
107+ available_targets : Option < Vec < Target > > ,
101108}
102109
103110impl Interface for Rust {
@@ -109,14 +116,15 @@ impl Interface for Rust {
109116 app_settings : RustAppSettings :: new ( config) ?,
110117 config_features : config. build . features . clone ( ) . unwrap_or_default ( ) ,
111118 product_name : config. package . product_name . clone ( ) ,
119+ available_targets : None ,
112120 } )
113121 }
114122
115123 fn app_settings ( & self ) -> & Self :: AppSettings {
116124 & self . app_settings
117125 }
118126
119- fn build ( & self , options : Options ) -> crate :: Result < ( ) > {
127+ fn build ( & mut self , options : Options ) -> crate :: Result < ( ) > {
120128 let bin_path = self . app_settings . app_binary_path ( & options) ?;
121129 let out_dir = bin_path. parent ( ) . unwrap ( ) ;
122130
@@ -165,7 +173,7 @@ impl Interface for Rust {
165173 }
166174
167175 fn dev < F : FnOnce ( ExitStatus , ExitReason ) + Send + ' static > (
168- & self ,
176+ & mut self ,
169177 options : Options ,
170178 manifest : & Manifest ,
171179 on_exit : F ,
@@ -174,6 +182,12 @@ impl Interface for Rust {
174182 let product_name = self . product_name . clone ( ) ;
175183
176184 let runner = options. runner . unwrap_or_else ( || "cargo" . into ( ) ) ;
185+
186+ if let Some ( target) = & options. target {
187+ self . fetch_available_targets ( ) ;
188+ self . validate_target ( target) ?;
189+ }
190+
177191 let mut build_cmd = Command :: new ( & runner) ;
178192 build_cmd
179193 . env (
@@ -345,9 +359,52 @@ impl Interface for Rust {
345359}
346360
347361impl Rust {
348- fn build_app ( & self , options : Options ) -> crate :: Result < ( ) > {
362+ fn fetch_available_targets ( & mut self ) {
363+ if let Ok ( output) = Command :: new ( "rustup" ) . args ( [ "target" , "list" ] ) . output ( ) {
364+ let stdout = String :: from_utf8_lossy ( & output. stdout ) . into_owned ( ) ;
365+ self . available_targets . replace (
366+ stdout
367+ . split ( '\n' )
368+ . map ( |t| {
369+ let mut s = t. split ( ' ' ) ;
370+ let name = s. next ( ) . unwrap ( ) . to_string ( ) ;
371+ let installed = s. next ( ) . map ( |v| v == "(installed)" ) . unwrap_or_default ( ) ;
372+ Target { name, installed }
373+ } )
374+ . filter ( |t| !t. name . is_empty ( ) )
375+ . collect ( ) ,
376+ ) ;
377+ }
378+ }
379+
380+ fn validate_target ( & self , target : & str ) -> crate :: Result < ( ) > {
381+ if let Some ( available_targets) = & self . available_targets {
382+ if let Some ( target) = available_targets. iter ( ) . find ( |t| t. name == target) {
383+ if !target. installed {
384+ anyhow:: bail!(
385+ "Target {target} is not installed (installed targets: {installed}). Please run `rustup target add {target}`." ,
386+ target = target. name,
387+ installed = available_targets. iter( ) . filter( |t| t. installed) . map( |t| t. name. as_str( ) ) . collect:: <Vec <& str >>( ) . join( ", " )
388+ ) ;
389+ }
390+ }
391+ if !available_targets. iter ( ) . any ( |t| t. name == target) {
392+ anyhow:: bail!( "Target {target} does not exist. Please run `rustup target list` to see the available targets." , target = target) ;
393+ }
394+ }
395+ Ok ( ( ) )
396+ }
397+
398+ fn build_app ( & mut self , options : Options ) -> crate :: Result < ( ) > {
349399 let runner = options. runner . unwrap_or_else ( || "cargo" . into ( ) ) ;
350400
401+ if let Some ( target) = & options. target {
402+ if self . available_targets . is_none ( ) {
403+ self . fetch_available_targets ( ) ;
404+ }
405+ self . validate_target ( target) ?;
406+ }
407+
351408 let mut args = Vec :: new ( ) ;
352409 if !options. args . is_empty ( ) {
353410 args. extend ( options. args ) ;
0 commit comments