@@ -74,6 +74,9 @@ struct Args {
7474 #[ arg( short, long, global = true , action) ]
7575 #[ cfg( feature = "update-informer" ) ]
7676 skip_update_check : bool ,
77+
78+ #[ arg( short, long) ]
79+ toolchain_name : Option < String > ,
7780}
7881
7982/// Check crates.io for a new version of the application
@@ -98,9 +101,7 @@ fn setup_args_interactive(args: &mut Args) -> Result<()> {
98101
99102 if args. chip . is_none ( ) {
100103 let chip_variants = Chip :: iter ( ) . collect :: < Vec < _ > > ( ) ;
101-
102104 let chip = Select :: new ( "Select your target chip:" , chip_variants) . prompt ( ) ?;
103-
104105 args. chip = Some ( chip) ;
105106 }
106107
@@ -112,9 +113,47 @@ fn setup_args_interactive(args: &mut Args) -> Result<()> {
112113 args. name = Some ( project_name) ;
113114 }
114115
116+ if args. toolchain_name . is_none ( ) {
117+ let chip = args
118+ . chip
119+ . expect ( "chip must be set by now in setup_args_interactive" ) ;
120+ let target = chip. target ( ) . to_string ( ) ;
121+
122+ let toolchains = toolchains_for_target ( & target) ?;
123+
124+ if toolchains. is_empty ( ) {
125+ log:: warn!(
126+ "No rustup toolchains with target `{}` found. \
127+ Using default toolchain name `esp`. \
128+ Install an appropriate toolchain or pass --toolchain-name explicitly.",
129+ target
130+ ) ;
131+ args. toolchain_name = Some ( "esp" . to_string ( ) ) ;
132+ } else if toolchains. len ( ) == 1 {
133+ let only = toolchains[ 0 ] . clone ( ) ;
134+ println ! (
135+ "Using toolchain `{}` for target `{}`" ,
136+ only, target
137+ ) ;
138+ args. toolchain_name = Some ( only) ;
139+ } else {
140+ let selected = Select :: new (
141+ & format ! (
142+ "Multiple Rust toolchains with target `{}` found.\n Select the toolchain to use:" ,
143+ target
144+ ) ,
145+ toolchains,
146+ )
147+ . prompt ( ) ?;
148+
149+ args. toolchain_name = Some ( selected) ;
150+ }
151+ }
152+
115153 Ok ( ( ) )
116154}
117155
156+
118157fn main ( ) -> Result < ( ) > {
119158 Builder :: from_env ( Env :: default ( ) . default_filter_or ( log:: LevelFilter :: Info . as_str ( ) ) )
120159 . format_target ( false )
@@ -131,14 +170,16 @@ fn main() -> Result<()> {
131170 }
132171
133172 // Run the interactive TUI only if chip or name is missing
134- if args. chip . is_none ( ) || args. name . is_none ( ) {
173+ if args. chip . is_none ( ) || args. name . is_none ( ) || args . toolchain_name . is_none ( ) {
135174 setup_args_interactive ( & mut args) ?;
136175 }
137176
138177 let chip = args. chip . unwrap ( ) ;
139178
140179 let name = args. name . clone ( ) . unwrap ( ) ;
141180
181+ let toolchain_name = args. toolchain_name . clone ( ) . unwrap ( ) ;
182+
142183 let path = & args
143184 . output_path
144185 . clone ( )
@@ -250,6 +291,7 @@ fn main() -> Result<()> {
250291 ) ,
251292 ( "esp-hal-version" . to_string( ) , esp_hal_version) ,
252293 ( "max-dram2-uninit" . to_string( ) , format!( "{max_dram2}" ) ) ,
294+ ( "toolchain-name" . to_string( ) , toolchain_name) ,
253295 ] ;
254296
255297 variables. push ( ( "rust_target" . to_string ( ) , chip. target ( ) . to_string ( ) ) ) ;
@@ -651,6 +693,84 @@ fn should_initialize_git_repo(mut path: &Path) -> bool {
651693 true
652694}
653695
696+ fn toolchains_for_target ( target : & str ) -> Result < Vec < String > > {
697+ let output = Command :: new ( "rustup" ) . args ( [ "toolchain" , "list" ] ) . output ( ) ;
698+
699+ let output = match output {
700+ Ok ( o) => o,
701+ Err ( err) => {
702+ log:: warn!( "Failed to run `rustup toolchain list`: {err}" ) ;
703+ return Ok ( Vec :: new ( ) ) ;
704+ }
705+ } ;
706+
707+ if !output. status . success ( ) {
708+ log:: warn!(
709+ "`rustup toolchain list` failed with status: {:?}" ,
710+ output. status. code( )
711+ ) ;
712+ return Ok ( Vec :: new ( ) ) ;
713+ }
714+
715+ let stdout = String :: from_utf8_lossy ( & output. stdout ) ;
716+
717+ let mut applicable = Vec :: new ( ) ;
718+
719+ for line in stdout. lines ( ) {
720+ // Line there are like: "stable-aarch64-apple-darwin (active, default)"
721+ let Some ( name) = line. split_whitespace ( ) . next ( ) else {
722+ continue ;
723+ } ;
724+
725+ let toolchain = name. to_string ( ) ;
726+
727+ // For Xtensa targets, hide "stable-*", "beta-*", "nightly-*"
728+ if target. starts_with ( "xtensa-" )
729+ && ( toolchain. starts_with ( "stable-" )
730+ || toolchain. starts_with ( "beta-" )
731+ || toolchain. starts_with ( "nightly-" ) )
732+ {
733+ continue ;
734+ }
735+
736+ // Use rustc with an explicit +toolchain spec, which works for custom
737+ // toolchains like `esp` and `esp-alt`.
738+ let spec = format ! ( "+{}" , toolchain) ;
739+
740+ let targets_output = Command :: new ( "rustc" )
741+ . arg ( & spec)
742+ . args ( [ "--print" , "target-list" ] )
743+ . output ( ) ;
744+
745+ let targets_output = match targets_output {
746+ Ok ( o) => o,
747+ Err ( err) => {
748+ log:: warn!( "Failed to run `rustc {spec} --print target-list`: {err}" ) ;
749+ continue ;
750+ }
751+ } ;
752+
753+ if !targets_output. status . success ( ) {
754+ log:: warn!(
755+ "`rustc {spec} --print target-list` failed with status: {:?}" ,
756+ targets_output. status. code( )
757+ ) ;
758+ continue ;
759+ }
760+
761+ let targets_stdout = String :: from_utf8_lossy ( & targets_output. stdout ) ;
762+
763+ let has_target = targets_stdout. lines ( ) . any ( |t| t. trim ( ) == target) ;
764+
765+ if has_target {
766+ applicable. push ( toolchain) ;
767+ }
768+ }
769+
770+ Ok ( applicable)
771+ }
772+
773+
654774#[ cfg( test) ]
655775mod test {
656776 use super :: * ;
0 commit comments