Skip to content

Commit

Permalink
cli: Add ability to override toolchain from Anchor.toml (#2649)
Browse files Browse the repository at this point in the history
  • Loading branch information
acheroncrypto authored Oct 14, 2023
1 parent 5900c93 commit 4f996d0
Show file tree
Hide file tree
Showing 4 changed files with 166 additions and 31 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ The minor version will be incremented upon a breaking change and the patch versi
- cli: Add `test.upgradeable`, `test.genesis.upgradeable` setting in anchor.toml to support testing upgradeable programs ([#2641](https://github.com/coral-xyz/anchor/pull/2642)).
- cli, client, lang, spl: Update Solana toolchain and dependencies to `1.17.0`, `1.16` remains supported ([#2645](https://github.com/coral-xyz/anchor/pull/2645)).
- spl: Add support for memo program ([#2661](https://github.com/coral-xyz/anchor/pull/2661)).
- cli: Add `toolchain` property in `Anchor.toml` to override Anchor and Solana versions ([#2649](https://github.com/coral-xyz/anchor/pull/2649)).

### Fixes

Expand All @@ -55,6 +56,7 @@ The minor version will be incremented upon a breaking change and the patch versi
- ts: Remove `base64-js` dependency ([#2635](https://github.com/coral-xyz/anchor/pull/2635)).
- syn: `IdlTypeDefinitionTy` enum has a new variant `Alias` ([#2637](https://github.com/coral-xyz/anchor/pull/2637)).
- cli, client, lang, spl: Solana `1.14` is no longer supported, minimum required Solana version is `1.16.0` ([#2645](https://github.com/coral-xyz/anchor/pull/2645)).
- cli: `anchor_version` and `solana_version` property in `Anchor.toml` that was being used in verifiable builds are moved inside `toolchain`. They are now being used for all commands in the workspace, not just verifiable builds ([#2649](https://github.com/coral-xyz/anchor/pull/2649)).

## [0.28.0] - 2023-06-09

Expand Down
27 changes: 15 additions & 12 deletions cli/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -349,8 +349,7 @@ impl<T> std::ops::DerefMut for WithPath<T> {

#[derive(Debug, Default)]
pub struct Config {
pub anchor_version: Option<String>,
pub solana_version: Option<String>,
pub toolchain: ToolchainConfig,
pub features: FeaturesConfig,
pub registry: RegistryConfig,
pub provider: ProviderConfig,
Expand All @@ -364,6 +363,12 @@ pub struct Config {
pub test_config: Option<TestConfig>,
}

#[derive(Default, Clone, Debug, Serialize, Deserialize)]
pub struct ToolchainConfig {
pub anchor_version: Option<String>,
pub solana_version: Option<String>,
}

#[derive(Default, Clone, Debug, Serialize, Deserialize)]
pub struct FeaturesConfig {
#[serde(default)]
Expand Down Expand Up @@ -444,11 +449,12 @@ impl Config {
}

pub fn docker(&self) -> String {
let ver = self
let version = self
.toolchain
.anchor_version
.clone()
.unwrap_or_else(|| crate::DOCKER_BUILDER_VERSION.to_string());
format!("backpackapp/build:v{ver}")
.as_deref()
.unwrap_or(crate::DOCKER_BUILDER_VERSION);
format!("backpackapp/build:v{version}")
}

pub fn discover(cfg_override: &ConfigOverride) -> Result<Option<WithPath<Config>>> {
Expand Down Expand Up @@ -507,8 +513,7 @@ impl Config {

#[derive(Debug, Serialize, Deserialize)]
struct _Config {
anchor_version: Option<String>,
solana_version: Option<String>,
toolchain: Option<ToolchainConfig>,
features: Option<FeaturesConfig>,
programs: Option<BTreeMap<String, BTreeMap<String, serde_json::Value>>>,
registry: Option<RegistryConfig>,
Expand Down Expand Up @@ -583,8 +588,7 @@ impl ToString for Config {
}
};
let cfg = _Config {
anchor_version: self.anchor_version.clone(),
solana_version: self.solana_version.clone(),
toolchain: Some(self.toolchain.clone()),
features: Some(self.features.clone()),
registry: Some(self.registry.clone()),
provider: Provider {
Expand Down Expand Up @@ -612,8 +616,7 @@ impl FromStr for Config {
let cfg: _Config = toml::from_str(s)
.map_err(|e| anyhow::format_err!("Unable to deserialize config: {}", e.to_string()))?;
Ok(Config {
anchor_version: cfg.anchor_version,
solana_version: cfg.solana_version,
toolchain: cfg.toolchain.unwrap_or_default(),
features: cfg.features.unwrap_or_default(),
registry: cfg.registry.unwrap_or_default(),
provider: ProviderConfig {
Expand Down
148 changes: 134 additions & 14 deletions cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ use anchor_syn::idl::types::{
};
use anyhow::{anyhow, Context, Result};
use clap::Parser;
use config::ToolchainConfig;
use dirs::home_dir;
use flate2::read::GzDecoder;
use flate2::read::ZlibDecoder;
use flate2::write::{GzEncoder, ZlibEncoder};
Expand Down Expand Up @@ -463,6 +465,126 @@ pub enum ClusterCommand {
}

pub fn entry(opts: Opts) -> Result<()> {
let toolchain_config = override_toolchain(&opts.cfg_override)?;
let result = process_command(opts);
restore_toolchain(&toolchain_config)?;

result
}

/// Override the toolchain from `Anchor.toml`.
///
/// Returns the previous versions to restore back to.
fn override_toolchain(cfg_override: &ConfigOverride) -> Result<ToolchainConfig> {
let mut previous_versions = ToolchainConfig::default();

let cfg = Config::discover(cfg_override)?;
if let Some(cfg) = cfg {
fn get_current_version(cmd_name: &str) -> Result<String> {
let output: std::process::Output = std::process::Command::new(cmd_name)
.arg("--version")
.output()?;
let output_version = std::str::from_utf8(&output.stdout)?;
let version = Regex::new(r"(\d+\.\d+\.\S+)")
.unwrap()
.captures_iter(output_version)
.next()
.unwrap()
.get(0)
.unwrap()
.as_str()
.to_string();

Ok(version)
}

if let Some(solana_version) = &cfg.toolchain.solana_version {
let current_version = get_current_version("solana")?;
if solana_version != &current_version {
// We are overriding with `solana-install` command instead of using the binaries
// from `~/.local/share/solana/install/releases` because we use multiple Solana
// binaries in various commands.
let exit_status = std::process::Command::new("solana-install")
.arg("init")
.arg(solana_version)
.stderr(Stdio::null())
.stdout(Stdio::null())
.spawn()?
.wait()?;

if !exit_status.success() {
println!(
"Failed to override `solana` version to {solana_version}, \
using {current_version} instead"
);
} else {
previous_versions.solana_version = Some(current_version);
}
}
}

// Anchor version override should be handled last
if let Some(anchor_version) = &cfg.toolchain.anchor_version {
let current_version = VERSION;
if anchor_version != current_version {
let binary_path = home_dir()
.unwrap()
.join(".avm")
.join("bin")
.join(format!("anchor-{anchor_version}"));

if !binary_path.exists() {
println!(
"`anchor` {anchor_version} is not installed with `avm`. Installing...\n"
);

let exit_status = std::process::Command::new("avm")
.arg("install")
.arg(anchor_version)
.spawn()?
.wait()?;

if !exit_status.success() {
println!(
"Failed to install `anchor` {anchor_version}, \
using {current_version} instead"
);

return Ok(previous_versions);
}
}

let exit_code = std::process::Command::new(binary_path)
.args(std::env::args_os().skip(1))
.spawn()?
.wait()?
.code()
.unwrap_or(1);
restore_toolchain(&previous_versions)?;
std::process::exit(exit_code);
}
}
}

Ok(previous_versions)
}

/// Restore toolchain to how it was before the command was run.
fn restore_toolchain(toolchain_config: &ToolchainConfig) -> Result<()> {
if let Some(solana_version) = &toolchain_config.solana_version {
std::process::Command::new("solana-install")
.arg("init")
.arg(solana_version)
.stderr(Stdio::null())
.stdout(Stdio::null())
.spawn()?
.wait()?;
}

Ok(())
}

fn process_command(opts: Opts) -> Result<()> {
match opts.command {
Command::Init {
name,
Expand Down Expand Up @@ -1000,7 +1122,7 @@ pub fn build(
let cfg = Config::discover(cfg_override)?.expect("Not in workspace.");
let build_config = BuildConfig {
verifiable,
solana_version: solana_version.or_else(|| cfg.solana_version.clone()),
solana_version: solana_version.or_else(|| cfg.toolchain.solana_version.clone()),
docker_image: docker_image.unwrap_or_else(|| cfg.docker()),
bootstrap,
};
Expand Down Expand Up @@ -1668,16 +1790,16 @@ fn verify(
if !skip_build {
build(
cfg_override,
None, // idl
None, // idl ts
true, // verifiable
true, // skip lint
None, // program name
solana_version.or_else(|| cfg.solana_version.clone()), // solana version
docker_image, // docker image
bootstrap, // bootstrap docker image
None, // stdout
None, // stderr
None, // idl
None, // idl ts
true, // verifiable
true, // skip lint
None, // program name
solana_version.or_else(|| cfg.toolchain.solana_version.clone()), // solana version
docker_image, // docker image
bootstrap, // bootstrap docker image
None, // stdout
None, // stderr
env_vars,
cargo_args,
false,
Expand Down Expand Up @@ -2647,9 +2769,7 @@ fn deserialize_idl_defined_type_to_json(
.iter()
.chain(idl.accounts.iter())
.find(|defined_type| defined_type.name == defined_type_name)
.ok_or_else(|| {
anyhow::anyhow!("Struct/Enum named {} not found in IDL.", defined_type_name)
})?
.ok_or_else(|| anyhow!("Type `{}` not found in IDL.", defined_type_name))?
.ty;

let mut deserialized_fields = Map::new();
Expand Down
20 changes: 15 additions & 5 deletions docs/src/pages/docs/manifest.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,11 @@ types = "app/src/idl/"
#### members

Sets the paths --relative to the `Anchor.toml`--
to all programs in the local
workspace, i.e., the path to the `Cargo.toml` manifest associated with each
program that can be compiled by the `anchor` CLI. For programs using the
standard Anchor workflow, this can be omitted. For programs not written in Anchor
but still want to publish, this should be added.
to all programs in the local
workspace, i.e., the path to the `Cargo.toml` manifest associated with each
program that can be compiled by the `anchor` CLI. For programs using the
standard Anchor workflow, this can be omitted. For programs not written in Anchor
but still want to publish, this should be added.

Example:

Expand Down Expand Up @@ -193,3 +193,13 @@ filename = "some_account.json"
address = "Ev8WSPQsGb4wfjybqff5eZNcS3n6HaMsBkMk9suAiuM"
filename = "some_other_account.json"
```

## toolchain

Override toolchain data in the workspace similar to [`rust-toolchain.toml`](https://rust-lang.github.io/rustup/overrides.html#the-toolchain-file).

```toml
[toolchain]
anchor_version = "0.28.0" # `anchor-cli` version to use(requires `avm`)
solana_version = "1.16.0" # Solana version to use(applies to all Solana tools)
```

1 comment on commit 4f996d0

@vercel
Copy link

@vercel vercel bot commented on 4f996d0 Oct 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

anchor-docs – ./

anchor-docs-200ms.vercel.app
anchor-docs-git-master-200ms.vercel.app
anchor-lang.com
www.anchor-lang.com

Please sign in to comment.