@@ -87,7 +87,7 @@ pub fn use_version(opt_version: Option<Version>) -> Result<()> {
8787 . next ( )
8888 . expect ( "Expected input" ) ?;
8989 match input. as_str ( ) {
90- "y" | "yes" => return install_version ( InstallTarget :: Version ( version) , false ) ,
90+ "y" | "yes" => return install_version ( InstallTarget :: Version ( version) , false , false ) ,
9191 _ => return Err ( anyhow ! ( "Installation rejected." ) ) ,
9292 } ;
9393 }
@@ -107,7 +107,7 @@ pub enum InstallTarget {
107107/// Update to the latest version
108108pub fn update ( ) -> Result < ( ) > {
109109 let latest_version = get_latest_version ( ) ?;
110- install_version ( InstallTarget :: Version ( latest_version) , false )
110+ install_version ( InstallTarget :: Version ( latest_version) , false , false )
111111}
112112
113113/// The commit sha provided can be shortened,
@@ -165,84 +165,119 @@ fn get_anchor_version_from_commit(commit: &str) -> Result<Version> {
165165}
166166
167167/// Install a version of anchor-cli
168- pub fn install_version ( install_target : InstallTarget , force : bool ) -> Result < ( ) > {
169- let mut args: Vec < String > = vec ! [
170- "install" . into( ) ,
171- "--git" . into( ) ,
172- "https://github.com/coral-xyz/anchor" . into( ) ,
173- "anchor-cli" . into( ) ,
174- "--locked" . into( ) ,
175- "--root" . into( ) ,
176- AVM_HOME . to_str( ) . unwrap( ) . into( ) ,
177- ] ;
178- let version = match install_target {
179- InstallTarget :: Version ( version) => {
180- args. extend ( [ "--tag" . into ( ) , format ! ( "v{}" , version) , "anchor-cli" . into ( ) ] ) ;
181- version
182- }
183- InstallTarget :: Commit ( commit) => {
184- args. extend ( [ "--rev" . into ( ) , commit. clone ( ) ] ) ;
185- get_anchor_version_from_commit ( & commit) ?
186- }
168+ pub fn install_version (
169+ install_target : InstallTarget ,
170+ force : bool ,
171+ from_source : bool ,
172+ ) -> Result < ( ) > {
173+ let version = match & install_target {
174+ InstallTarget :: Version ( version) => version. to_owned ( ) ,
175+ InstallTarget :: Commit ( commit) => get_anchor_version_from_commit ( commit) ?,
187176 } ;
188-
189- // If version is already installed we ignore the request.
190- let installed_versions = read_installed_versions ( ) ?;
191- if installed_versions. contains ( & version) && !force {
192- println ! ( "Version {version} is already installed" ) ;
177+ // Return early if version is already installed
178+ if !force && read_installed_versions ( ) ?. contains ( & version) {
179+ eprintln ! ( "Version `{version}` is already installed" ) ;
193180 return Ok ( ( ) ) ;
194181 }
195182
196- // If the version is older than v0.31, install using `rustc 1.79.0` to get around the problem
197- // explained in https://github.com/coral-xyz/anchor/pull/3143
198- if version < Version :: parse ( "0.31.0" ) ? {
199- const REQUIRED_VERSION : & str = "1.79.0" ;
200- let is_installed = Command :: new ( "rustup" )
201- . args ( [ "toolchain" , "list" ] )
202- . output ( )
203- . map ( |output| String :: from_utf8 ( output. stdout ) ) ??
204- . lines ( )
205- . any ( |line| line. starts_with ( REQUIRED_VERSION ) ) ;
206- if !is_installed {
207- let exit_status = Command :: new ( "rustup" )
208- . args ( [ "toolchain" , "install" , REQUIRED_VERSION ] )
209- . spawn ( ) ?
210- . wait ( ) ?;
211- if !exit_status. success ( ) {
212- return Err ( anyhow ! (
213- "Installation of `rustc {REQUIRED_VERSION}` failed. \
183+ let is_older_than_v0_31_0 = version < Version :: parse ( "0.31.0" ) ?;
184+ if from_source || is_older_than_v0_31_0 {
185+ // Build from source using `cargo install --git`
186+ let mut args: Vec < String > = vec ! [
187+ "install" . into( ) ,
188+ "anchor-cli" . into( ) ,
189+ "--git" . into( ) ,
190+ "https://github.com/coral-xyz/anchor" . into( ) ,
191+ "--locked" . into( ) ,
192+ "--root" . into( ) ,
193+ AVM_HOME . to_str( ) . unwrap( ) . into( ) ,
194+ ] ;
195+ let conditional_args = match install_target {
196+ InstallTarget :: Version ( version) => [ "--tag" . into ( ) , format ! ( "v{}" , version) ] ,
197+ InstallTarget :: Commit ( commit) => [ "--rev" . into ( ) , commit] ,
198+ } ;
199+ args. extend_from_slice ( & conditional_args) ;
200+
201+ // If the version is older than v0.31, install using `rustc 1.79.0` to get around the problem
202+ // explained in https://github.com/coral-xyz/anchor/pull/3143
203+ if is_older_than_v0_31_0 {
204+ const REQUIRED_VERSION : & str = "1.79.0" ;
205+ let is_installed = Command :: new ( "rustup" )
206+ . args ( [ "toolchain" , "list" ] )
207+ . output ( )
208+ . map ( |output| String :: from_utf8 ( output. stdout ) ) ??
209+ . lines ( )
210+ . any ( |line| line. starts_with ( REQUIRED_VERSION ) ) ;
211+ if !is_installed {
212+ let exit_status = Command :: new ( "rustup" )
213+ . args ( [ "toolchain" , "install" , REQUIRED_VERSION ] )
214+ . spawn ( ) ?
215+ . wait ( ) ?;
216+ if !exit_status. success ( ) {
217+ return Err ( anyhow ! (
218+ "Installation of `rustc {REQUIRED_VERSION}` failed. \
214219 `rustc <1.80` is required to install Anchor v{version} from source. \
215220 See https://github.com/coral-xyz/anchor/pull/3143 for more information."
216- ) ) ;
221+ ) ) ;
222+ }
217223 }
218- }
219224
220- // Prepend the toolchain to use with the `cargo install` command
221- args. insert ( 0 , format ! ( "+{REQUIRED_VERSION}" ) ) ;
222- }
225+ // Prepend the toolchain to use with the `cargo install` command
226+ args. insert ( 0 , format ! ( "+{REQUIRED_VERSION}" ) ) ;
227+ }
223228
224- let output = Command :: new ( "cargo" )
225- . args ( args)
226- . stdout ( Stdio :: inherit ( ) )
227- . stderr ( Stdio :: inherit ( ) )
228- . output ( )
229- . map_err ( |e| anyhow ! ( "Cargo install for {version} failed: {e}" ) ) ?;
230- if !output. status . success ( ) {
231- return Err ( anyhow ! (
232- "Failed to install {version}, is it a valid version?"
233- ) ) ;
234- }
229+ let output = Command :: new ( "cargo" )
230+ . args ( args)
231+ . stdout ( Stdio :: inherit ( ) )
232+ . stderr ( Stdio :: inherit ( ) )
233+ . output ( )
234+ . map_err ( |e| anyhow ! ( "`cargo install` for version ` {version}` failed: {e}" ) ) ?;
235+ if !output. status . success ( ) {
236+ return Err ( anyhow ! (
237+ "Failed to install {version}, is it a valid version?"
238+ ) ) ;
239+ }
235240
236- let bin_dir = get_bin_dir_path ( ) ;
237- let bin_name = if cfg ! ( target_os = "windows" ) {
238- "anchor.exe"
241+ let bin_dir = get_bin_dir_path ( ) ;
242+ let bin_name = if cfg ! ( target_os = "windows" ) {
243+ "anchor.exe"
244+ } else {
245+ "anchor"
246+ } ;
247+ fs:: rename ( bin_dir. join ( bin_name) , version_binary_path ( & version) ) ?;
239248 } else {
240- "anchor"
241- } ;
242- fs:: rename (
243- bin_dir. join ( bin_name) ,
244- bin_dir. join ( format ! ( "anchor-{version}" ) ) ,
245- ) ?;
249+ let output = Command :: new ( "rustc" ) . arg ( "-vV" ) . output ( ) ?;
250+ let target = core:: str:: from_utf8 ( & output. stdout ) ?
251+ . lines ( )
252+ . find ( |line| line. starts_with ( "host:" ) )
253+ . and_then ( |line| line. split ( ':' ) . last ( ) )
254+ . ok_or_else ( || anyhow ! ( "`host` not found from `rustc -vV` output" ) ) ?
255+ . trim ( ) ;
256+ let ext = if cfg ! ( target_os = "windows" ) {
257+ ".exe"
258+ } else {
259+ ""
260+ } ;
261+ let res = reqwest:: blocking:: get ( format ! (
262+ "https://github.com/coral-xyz/anchor/releases/download/v{version}/anchor-{version}-{target}{ext}"
263+ ) ) ?;
264+ if !res. status ( ) . is_success ( ) {
265+ return Err ( anyhow ! (
266+ "Failed to download the binary for version `{version}` (status code: {})" ,
267+ res. status( )
268+ ) ) ;
269+ }
270+
271+ let bin_path = version_binary_path ( & version) ;
272+ fs:: write ( & bin_path, res. bytes ( ) ?) ?;
273+
274+ // Set file to executable on UNIX
275+ #[ cfg( not( target_os = "windows" ) ) ]
276+ fs:: set_permissions (
277+ bin_path,
278+ <fs:: Permissions as std:: os:: unix:: fs:: PermissionsExt >:: from_mode ( 0o775 ) ,
279+ ) ?;
280+ }
246281
247282 // If .version file is empty or not parseable, write the newly installed version to it
248283 if current_version ( ) . is_err ( ) {
@@ -255,7 +290,7 @@ pub fn install_version(install_target: InstallTarget, force: bool) -> Result<()>
255290
256291/// Remove an installed version of anchor-cli
257292pub fn uninstall_version ( version : & Version ) -> Result < ( ) > {
258- let version_path = get_bin_dir_path ( ) . join ( format ! ( "anchor-{ version}" ) ) ;
293+ let version_path = version_binary_path ( version) ;
259294 if !version_path. exists ( ) {
260295 return Err ( anyhow ! ( "anchor-cli {} is not installed" , version) ) ;
261296 }
0 commit comments