Skip to content

Commit 34cace4

Browse files
authored
Merge pull request #110 from r-lib/feature/aliases
2 parents 35555b4 + f5f7c71 commit 34cace4

File tree

13 files changed

+531
-55
lines changed

13 files changed

+531
-55
lines changed

NEWS.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33

44
* rig now prints output from `apt`, etc. like regular logging output on Linux.
55

6+
* rig now supports the alises `oldrel`, `release`, `next`, `devel` in
7+
`rig default`, `rig rm`, `rig rstudio`, etc. (#108).
8+
69
# rig 0.5.0
710

811
* rig can now open renv projects in RStudio, with the correct R version.

src/alias.rs

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
2+
use std::error::Error;
3+
#[cfg(target_os = "windows")]
4+
use std::io::Write;
5+
#[cfg(target_os = "windows")]
6+
use std::fs::File;
7+
use std::path::Path;
8+
9+
#[cfg(any(target_os = "macos", target_os = "linux"))]
10+
use std::os::unix::fs::symlink;
11+
12+
use clap::ArgMatches;
13+
#[cfg(any(target_os = "macos", target_os = "linux"))]
14+
use simple_error::*;
15+
use simplelog::*;
16+
17+
#[cfg(target_os = "macos")]
18+
use crate::macos::*;
19+
20+
#[cfg(target_os = "windows")]
21+
use crate::windows::*;
22+
23+
#[cfg(target_os = "linux")]
24+
use crate::linux::*;
25+
26+
use crate::escalate::*;
27+
28+
#[cfg(target_os = "macos")]
29+
pub fn get_alias(args: &ArgMatches) -> Option<String> {
30+
match args.value_of("str") {
31+
None => None,
32+
Some(str) => {
33+
match str {
34+
"oldrel" | "oldrel/1" => Some("oldrel".to_string()),
35+
"release" | "devel" | "next" => Some(str.to_string()),
36+
_ => None
37+
}
38+
}
39+
}
40+
}
41+
42+
#[cfg(target_os = "linux")]
43+
pub fn get_alias(args: &ArgMatches) -> Option<String> {
44+
match args.value_of("str") {
45+
None => None,
46+
Some(str) => {
47+
match str {
48+
"oldrel" | "oldrel/1" => Some("oldrel".to_string()),
49+
"release" => Some(str.to_string()),
50+
_ => None
51+
}
52+
}
53+
}
54+
}
55+
56+
#[cfg(target_os = "windows")]
57+
pub fn get_alias(args: &ArgMatches) -> Option<String> {
58+
match args.value_of("str") {
59+
None => None,
60+
Some(str) => {
61+
match str {
62+
"oldrel" | "oldrel/1" => Some("oldrel".to_string()),
63+
"release" | "next" => Some(str.to_string()),
64+
_ => None
65+
}
66+
}
67+
}
68+
}
69+
70+
#[cfg(target_os = "macos")]
71+
pub fn add_alias(ver: &str, alias: &str) -> Result<(), Box<dyn Error>> {
72+
let msg = "Adding R-".to_string() + alias + " alias";
73+
escalate(&msg)?;
74+
75+
info!("Adding R-{} alias to R {}", alias, ver);
76+
77+
let base = Path::new(R_ROOT);
78+
let target = base.join(ver).join("Resources/bin/R");
79+
let linkfile = Path::new("/usr/local/bin/").join("R-".to_string() + alias);
80+
81+
// If it exists then we check that it points to the right place
82+
// Cannot use .exists(), because it follows symlinks
83+
let meta = std::fs::symlink_metadata(&linkfile);
84+
if meta.is_ok() {
85+
match std::fs::read_link(&linkfile) {
86+
Err(_) => bail!("{} is not a symlink, aborting", linkfile.display()),
87+
Ok(xtarget) => {
88+
if xtarget == target {
89+
return Ok(())
90+
} else {
91+
debug!("{} is wrong, updating", linkfile.display());
92+
match std::fs::remove_file(&linkfile) {
93+
Err(err) => {
94+
bail!(
95+
"Failed to delete {}, cannot update alias: {}",
96+
linkfile.display(),
97+
err.to_string()
98+
);
99+
},
100+
_ => {}
101+
}
102+
}
103+
}
104+
}
105+
}
106+
107+
// If we are still here, then we need to create the link
108+
debug!("Adding {} -> {}", linkfile.display(), target.display());
109+
match symlink(&target, &linkfile) {
110+
Err(err) => bail!(
111+
"Cannot create alias {}: {}",
112+
linkfile.display(),
113+
err.to_string()
114+
),
115+
_ => {}
116+
};
117+
118+
Ok(())
119+
}
120+
121+
#[cfg(target_os = "windows")]
122+
pub fn add_alias(ver: &str, alias: &str) -> Result<(), Box<dyn Error>> {
123+
let msg = "Adding R-".to_string() + alias + " alias";
124+
escalate(&msg)?;
125+
let base = Path::new(R_ROOT);
126+
let bin = base.join("bin");
127+
128+
// should exist at this point, but make sure
129+
std::fs::create_dir_all(&bin)?;
130+
131+
let filename = "R-".to_string() + alias + ".bat";
132+
let linkfile = bin.join(&filename);
133+
134+
let cnt = "@\"C:\\Program Files\\R\\R-".to_string() + &ver + "\\bin\\R\" %*\n";
135+
let op;
136+
if linkfile.exists() {
137+
op = "Updating";
138+
let orig = std::fs::read_to_string(&linkfile)?;
139+
if orig == cnt {
140+
return Ok(());
141+
}
142+
} else {
143+
op = "Adding";
144+
};
145+
info!("{} R-{} -> {} alias", op, alias, ver);
146+
let mut file = File::create(&linkfile)?;
147+
file.write_all(cnt.as_bytes())?;
148+
149+
Ok(())
150+
}
151+
152+
#[cfg(target_os = "linux")]
153+
pub fn add_alias(ver: &str, alias: &str) -> Result<(), Box<dyn Error>> {
154+
let msg = "Adding R-".to_string() + alias + " alias";
155+
escalate(&msg)?;
156+
157+
info!("Adding R-{} alias to R {}", alias, ver);
158+
159+
let base = Path::new(R_ROOT);
160+
let target = base.join(ver).join("bin/R");
161+
let linkfile = Path::new("/usr/local/bin/").join("R-".to_string() + alias);
162+
163+
// If it exists then we check that it points to the right place
164+
// Cannot use .exists(), because it follows symlinks
165+
let meta = std::fs::symlink_metadata(&linkfile);
166+
if meta.is_ok() {
167+
match std::fs::read_link(&linkfile) {
168+
Err(_) => bail!("{} is not a symlink, aborting", linkfile.display()),
169+
Ok(xtarget) => {
170+
if xtarget == target {
171+
return Ok(())
172+
} else {
173+
debug!("{} is wrong, updating", linkfile.display());
174+
match std::fs::remove_file(&linkfile) {
175+
Err(err) => {
176+
bail!(
177+
"Failed to delete {}, cannot update alias: {}",
178+
linkfile.display(),
179+
err.to_string()
180+
);
181+
},
182+
_ => {}
183+
}
184+
}
185+
}
186+
}
187+
}
188+
189+
// If we are still here, then we need to create the link
190+
debug!("Adding {} -> {}", linkfile.display(), target.display());
191+
match symlink(&target, &linkfile) {
192+
Err(err) => bail!(
193+
"Cannot create alias {}: {}",
194+
linkfile.display(),
195+
err.to_string()
196+
),
197+
_ => {}
198+
};
199+
200+
Ok(())
201+
}

src/common.rs

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,20 @@ use crate::rversion::*;
2323
use crate::run::*;
2424
use crate::utils::*;
2525

26-
pub fn check_installed(ver: &String) -> Result<bool, Box<dyn Error>> {
27-
let inst = sc_get_list()?;
28-
if !inst.contains(&ver) {
29-
bail!("R version <b>{}</b> is not installed", &ver);
26+
pub fn check_installed(x: &String) -> Result<String, Box<dyn Error>> {
27+
let inst = sc_get_list_details()?;
28+
29+
for ver in inst {
30+
if &ver.name == x {
31+
return Ok(ver.name);
32+
}
33+
if ver.aliases.contains(x) {
34+
debug!("Alias {} is resolved to version {}", x, ver.name);
35+
return Ok(ver.name);
36+
}
3037
}
31-
Ok(true)
38+
39+
bail!("R version <b>{}</b> is not installed", &x);
3240
}
3341

3442
// -- rig default ---------------------------------------------------------
@@ -55,6 +63,7 @@ pub fn set_default_if_none(ver: String) -> Result<(), Box<dyn Error>> {
5563

5664
pub fn sc_get_list_details() -> Result<Vec<InstalledVersion>, Box<dyn Error>> {
5765
let names = sc_get_list()?;
66+
let aliases = find_aliases()?;
5867
let mut res: Vec<InstalledVersion> = vec![];
5968
let re = Regex::new("^Version:[ ]?")?;
6069

@@ -74,11 +83,18 @@ pub fn sc_get_list_details() -> Result<Vec<InstalledVersion>, Box<dyn Error>> {
7483
};
7584
let path = Path::new(R_ROOT).join(R_VERSIONDIR.replace("{}", &name));
7685
let binary = Path::new(R_ROOT).join(R_BINPATH.replace("{}", &name));
86+
let mut myaliases: Vec<String> = vec![];
87+
for a in &aliases {
88+
if a.version == name {
89+
myaliases.push(a.alias.to_owned());
90+
}
91+
}
7792
res.push(InstalledVersion {
7893
name: name.to_string(),
7994
version: version,
8095
path: path.to_str().and_then(|x| Some(x.to_string())),
81-
binary: binary.to_str().and_then(|x| Some(x.to_string()))
96+
binary: binary.to_str().and_then(|x| Some(x.to_string())),
97+
aliases: myaliases
8298
});
8399
}
84100

@@ -98,7 +114,7 @@ pub fn system_add_pak(
98114
};
99115

100116
for ver in vers {
101-
check_installed(&ver)?;
117+
let ver = check_installed(&ver)?;
102118
if update {
103119
info!("Installing pak for R {}", ver);
104120
} else {

src/config.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ fn empty_stringmap() -> HashMap<String, String> {
2323
fn rig_config_file() -> Result<PathBuf, Box<dyn Error>> {
2424
let proj_dirs = match ProjectDirs::from("com", "gaborcsardi", "rig") {
2525
Some(x) => x,
26-
None => bail!("Config file if not supported on this system"),
26+
None => bail!("Config file is not supported on this system"),
2727
};
2828
let config_dir = proj_dirs.data_dir();
2929
let config_file = config_dir.join("config.json");

src/download.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use futures::future;
22
use futures_util::StreamExt;
33
use std::error::Error;
44
use std::ffi::OsStr;
5+
#[cfg(any(target_os = "macos", target_os = "windows"))]
56
use std::ffi::OsString;
67
use std::fs::File;
78
use std::io::Write;
@@ -11,10 +12,12 @@ use std::path::Path;
1112
use clap::ArgMatches;
1213

1314
use simple_error::bail;
15+
#[cfg(any(target_os = "macos", target_os = "windows"))]
1416
use simplelog::info;
1517

1618
#[cfg(target_os = "windows")]
1719
use crate::rversion::Rversion;
20+
#[cfg(any(target_os = "macos", target_os = "windows"))]
1821
use crate::utils::*;
1922
#[cfg(target_os = "windows")]
2023
use crate::windows::*;

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use lazy_static::lazy_static;
1414
use libc;
1515
use simple_error::bail;
1616

17+
mod alias;
1718
mod args;
1819
mod common;
1920
mod config;

0 commit comments

Comments
 (0)