Skip to content

Commit 6f76df4

Browse files
fix(avm): expose anchor on PATH via $CARGO_HOME/bin symlink (#3835)
1 parent 018d5e8 commit 6f76df4

File tree

1 file changed

+76
-0
lines changed

1 file changed

+76
-0
lines changed

avm/src/lib.rs

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,15 @@ pub fn version_binary_path(version: &Version) -> PathBuf {
4444
get_bin_dir_path().join(format!("anchor-{version}"))
4545
}
4646

47+
/// Path to the cargo binary directory, defaults to `~/.cargo/bin` if `CARGO_HOME`
48+
#[cfg(not(test))] // this prevents tests from running this function so we don't change the developer environment during tests
49+
fn cargo_bin_dir() -> Option<PathBuf> {
50+
if let Ok(cargo_home) = std::env::var("CARGO_HOME") {
51+
return Some(PathBuf::from(cargo_home).join("bin"));
52+
}
53+
dirs::home_dir().map(|home| home.join(".cargo").join("bin"))
54+
}
55+
4756
/// Ensure the users home directory is setup with the paths required by AVM.
4857
pub fn ensure_paths() {
4958
let home_dir = AVM_HOME.to_path_buf();
@@ -94,6 +103,73 @@ pub fn ensure_paths() {
94103
}
95104
}
96105

106+
// Try to make `anchor` available on PATH by placing it into $CARGO_HOME/bin (or ~/.cargo/bin).
107+
#[cfg(not(test))]
108+
{
109+
if let Some(cargo_bin) = cargo_bin_dir() {
110+
if cargo_bin.exists() {
111+
let anchor_in_cargo = cargo_bin.join(if cfg!(target_os = "windows") {
112+
"anchor.exe"
113+
} else {
114+
"anchor"
115+
});
116+
if !anchor_in_cargo.exists() {
117+
let target = avm_in_bin.clone(); // ~/.avm/bin/avm
118+
119+
let mut linked = false;
120+
#[cfg(unix)]
121+
{
122+
if let Err(e) = std::os::unix::fs::symlink(&target, &anchor_in_cargo) {
123+
eprintln!(
124+
"Failed to create cargo-bin symlink: {e}. Falling back to copy."
125+
);
126+
} else {
127+
linked = true;
128+
}
129+
}
130+
#[cfg(windows)]
131+
{
132+
use std::os::windows::fs::symlink_file;
133+
if let Err(e) = symlink_file(&target, &anchor_in_cargo) {
134+
eprintln!(
135+
"Failed to create cargo-bin symlink: {e}. Falling back to copy."
136+
);
137+
} else {
138+
linked = true;
139+
}
140+
}
141+
142+
if !linked {
143+
if let Err(e) = fs::copy(&target, &anchor_in_cargo) {
144+
eprintln!(
145+
"Failed to place `anchor` in {}: {}.\nAdd {} to your PATH or create a symlink manually.",
146+
cargo_bin.display(),
147+
e,
148+
bin_dir.display()
149+
);
150+
} else {
151+
// Ensure executable bit on UNIX when copying.
152+
#[cfg(unix)]
153+
{
154+
use std::os::unix::fs::PermissionsExt;
155+
if let Err(e) = fs::set_permissions(
156+
&anchor_in_cargo,
157+
fs::Permissions::from_mode(0o775),
158+
) {
159+
eprintln!(
160+
"Failed to set executable permissions on {}: {}",
161+
anchor_in_cargo.display(),
162+
e
163+
);
164+
}
165+
}
166+
}
167+
}
168+
}
169+
}
170+
}
171+
}
172+
97173
if !current_version_file_path().exists() {
98174
fs::File::create(current_version_file_path()).expect("Could not create .version file");
99175
}

0 commit comments

Comments
 (0)