Browse Source

avm: Make installation download binaries by default (#3445)

acheron 9 tháng trước cách đây
mục cha
commit
6ff6655526
3 tập tin đã thay đổi với 111 bổ sung71 xóa
  1. 1 0
      CHANGELOG.md
  2. 105 70
      avm/src/lib.rs
  3. 5 1
      avm/src/main.rs

+ 1 - 0
CHANGELOG.md

@@ -62,6 +62,7 @@ The minor version will be incremented upon a breaking change and the patch versi
 - cli: Add test template for [Mollusk](https://github.com/buffalojoec/mollusk) ([#3352](https://github.com/coral-xyz/anchor/pull/3352)).
 - idl: Disallow account discriminators that can conflict with the `zero` constraint ([#3365](https://github.com/coral-xyz/anchor/pull/3365)).
 - cli: Include recommended solana args by default and add new `--max-retries` option to the `deploy` command ([#3354](https://github.com/coral-xyz/anchor/pull/3354)).
+- avm: Make installation download binaries by default ([#3445](https://github.com/coral-xyz/anchor/pull/3445)).
 
 ### Fixes
 

+ 105 - 70
avm/src/lib.rs

@@ -87,7 +87,7 @@ pub fn use_version(opt_version: Option<Version>) -> Result<()> {
             .next()
             .expect("Expected input")?;
         match input.as_str() {
-            "y" | "yes" => return install_version(InstallTarget::Version(version), false),
+            "y" | "yes" => return install_version(InstallTarget::Version(version), false, false),
             _ => return Err(anyhow!("Installation rejected.")),
         };
     }
@@ -107,7 +107,7 @@ pub enum InstallTarget {
 /// Update to the latest version
 pub fn update() -> Result<()> {
     let latest_version = get_latest_version()?;
-    install_version(InstallTarget::Version(latest_version), false)
+    install_version(InstallTarget::Version(latest_version), false, false)
 }
 
 /// The commit sha provided can be shortened,
@@ -165,84 +165,119 @@ fn get_anchor_version_from_commit(commit: &str) -> Result<Version> {
 }
 
 /// Install a version of anchor-cli
-pub fn install_version(install_target: InstallTarget, force: bool) -> Result<()> {
-    let mut args: Vec<String> = vec![
-        "install".into(),
-        "--git".into(),
-        "https://github.com/coral-xyz/anchor".into(),
-        "anchor-cli".into(),
-        "--locked".into(),
-        "--root".into(),
-        AVM_HOME.to_str().unwrap().into(),
-    ];
-    let version = match install_target {
-        InstallTarget::Version(version) => {
-            args.extend(["--tag".into(), format!("v{}", version), "anchor-cli".into()]);
-            version
-        }
-        InstallTarget::Commit(commit) => {
-            args.extend(["--rev".into(), commit.clone()]);
-            get_anchor_version_from_commit(&commit)?
-        }
+pub fn install_version(
+    install_target: InstallTarget,
+    force: bool,
+    from_source: bool,
+) -> Result<()> {
+    let version = match &install_target {
+        InstallTarget::Version(version) => version.to_owned(),
+        InstallTarget::Commit(commit) => get_anchor_version_from_commit(commit)?,
     };
-
-    // If version is already installed we ignore the request.
-    let installed_versions = read_installed_versions()?;
-    if installed_versions.contains(&version) && !force {
-        println!("Version {version} is already installed");
+    // Return early if version is already installed
+    if !force && read_installed_versions()?.contains(&version) {
+        eprintln!("Version `{version}` is already installed");
         return Ok(());
     }
 
-    // If the version is older than v0.31, install using `rustc 1.79.0` to get around the problem
-    // explained in https://github.com/coral-xyz/anchor/pull/3143
-    if version < Version::parse("0.31.0")? {
-        const REQUIRED_VERSION: &str = "1.79.0";
-        let is_installed = Command::new("rustup")
-            .args(["toolchain", "list"])
-            .output()
-            .map(|output| String::from_utf8(output.stdout))??
-            .lines()
-            .any(|line| line.starts_with(REQUIRED_VERSION));
-        if !is_installed {
-            let exit_status = Command::new("rustup")
-                .args(["toolchain", "install", REQUIRED_VERSION])
-                .spawn()?
-                .wait()?;
-            if !exit_status.success() {
-                return Err(anyhow!(
-                    "Installation of `rustc {REQUIRED_VERSION}` failed. \
+    let is_older_than_v0_31_0 = version < Version::parse("0.31.0")?;
+    if from_source || is_older_than_v0_31_0 {
+        // Build from source using `cargo install --git`
+        let mut args: Vec<String> = vec![
+            "install".into(),
+            "anchor-cli".into(),
+            "--git".into(),
+            "https://github.com/coral-xyz/anchor".into(),
+            "--locked".into(),
+            "--root".into(),
+            AVM_HOME.to_str().unwrap().into(),
+        ];
+        let conditional_args = match install_target {
+            InstallTarget::Version(version) => ["--tag".into(), format!("v{}", version)],
+            InstallTarget::Commit(commit) => ["--rev".into(), commit],
+        };
+        args.extend_from_slice(&conditional_args);
+
+        // If the version is older than v0.31, install using `rustc 1.79.0` to get around the problem
+        // explained in https://github.com/coral-xyz/anchor/pull/3143
+        if is_older_than_v0_31_0 {
+            const REQUIRED_VERSION: &str = "1.79.0";
+            let is_installed = Command::new("rustup")
+                .args(["toolchain", "list"])
+                .output()
+                .map(|output| String::from_utf8(output.stdout))??
+                .lines()
+                .any(|line| line.starts_with(REQUIRED_VERSION));
+            if !is_installed {
+                let exit_status = Command::new("rustup")
+                    .args(["toolchain", "install", REQUIRED_VERSION])
+                    .spawn()?
+                    .wait()?;
+                if !exit_status.success() {
+                    return Err(anyhow!(
+                        "Installation of `rustc {REQUIRED_VERSION}` failed. \
                     `rustc <1.80` is required to install Anchor v{version} from source. \
                     See https://github.com/coral-xyz/anchor/pull/3143 for more information."
-                ));
+                    ));
+                }
             }
-        }
 
-        // Prepend the toolchain to use with the `cargo install` command
-        args.insert(0, format!("+{REQUIRED_VERSION}"));
-    }
+            // Prepend the toolchain to use with the `cargo install` command
+            args.insert(0, format!("+{REQUIRED_VERSION}"));
+        }
 
-    let output = Command::new("cargo")
-        .args(args)
-        .stdout(Stdio::inherit())
-        .stderr(Stdio::inherit())
-        .output()
-        .map_err(|e| anyhow!("Cargo install for {version} failed: {e}"))?;
-    if !output.status.success() {
-        return Err(anyhow!(
-            "Failed to install {version}, is it a valid version?"
-        ));
-    }
+        let output = Command::new("cargo")
+            .args(args)
+            .stdout(Stdio::inherit())
+            .stderr(Stdio::inherit())
+            .output()
+            .map_err(|e| anyhow!("`cargo install` for version `{version}` failed: {e}"))?;
+        if !output.status.success() {
+            return Err(anyhow!(
+                "Failed to install {version}, is it a valid version?"
+            ));
+        }
 
-    let bin_dir = get_bin_dir_path();
-    let bin_name = if cfg!(target_os = "windows") {
-        "anchor.exe"
+        let bin_dir = get_bin_dir_path();
+        let bin_name = if cfg!(target_os = "windows") {
+            "anchor.exe"
+        } else {
+            "anchor"
+        };
+        fs::rename(bin_dir.join(bin_name), version_binary_path(&version))?;
     } else {
-        "anchor"
-    };
-    fs::rename(
-        bin_dir.join(bin_name),
-        bin_dir.join(format!("anchor-{version}")),
-    )?;
+        let output = Command::new("rustc").arg("-vV").output()?;
+        let target = core::str::from_utf8(&output.stdout)?
+            .lines()
+            .find(|line| line.starts_with("host:"))
+            .and_then(|line| line.split(':').last())
+            .ok_or_else(|| anyhow!("`host` not found from `rustc -vV` output"))?
+            .trim();
+        let ext = if cfg!(target_os = "windows") {
+            ".exe"
+        } else {
+            ""
+        };
+        let res = reqwest::blocking::get(format!(
+            "https://github.com/coral-xyz/anchor/releases/download/v{version}/anchor-{version}-{target}{ext}"
+        ))?;
+        if !res.status().is_success() {
+            return Err(anyhow!(
+                "Failed to download the binary for version `{version}` (status code: {})",
+                res.status()
+            ));
+        }
+
+        let bin_path = version_binary_path(&version);
+        fs::write(&bin_path, res.bytes()?)?;
+
+        // Set file to executable on UNIX
+        #[cfg(not(target_os = "windows"))]
+        fs::set_permissions(
+            bin_path,
+            <fs::Permissions as std::os::unix::fs::PermissionsExt>::from_mode(0o775),
+        )?;
+    }
 
     // If .version file is empty or not parseable, write the newly installed version to it
     if current_version().is_err() {
@@ -255,7 +290,7 @@ pub fn install_version(install_target: InstallTarget, force: bool) -> Result<()>
 
 /// Remove an installed version of anchor-cli
 pub fn uninstall_version(version: &Version) -> Result<()> {
-    let version_path = get_bin_dir_path().join(format!("anchor-{version}"));
+    let version_path = version_binary_path(version);
     if !version_path.exists() {
         return Err(anyhow!("anchor-cli {} is not installed", version));
     }

+ 5 - 1
avm/src/main.rs

@@ -26,6 +26,9 @@ pub enum Commands {
         /// Flag to force installation even if the version
         /// is already installed
         force: bool,
+        #[clap(long)]
+        /// Build from source code rather than downloading prebuilt binaries
+        from_source: bool,
     },
     #[clap(about = "Uninstall a version of Anchor")]
     Uninstall {
@@ -77,7 +80,8 @@ pub fn entry(opts: Cli) -> Result<()> {
         Commands::Install {
             version_or_commit,
             force,
-        } => avm::install_version(version_or_commit, force),
+            from_source,
+        } => avm::install_version(version_or_commit, force, from_source),
         Commands::Uninstall { version } => avm::uninstall_version(&version),
         Commands::List {} => avm::list_versions(),
         Commands::Update {} => avm::update(),