Skip to content

Commit

Permalink
Merge pull request #75 from r-lib/feature/faster-library
Browse files Browse the repository at this point in the history
  • Loading branch information
gaborcsardi authored May 28, 2022
2 parents dbafb5c + b25f318 commit 1de92e6
Show file tree
Hide file tree
Showing 21 changed files with 1,085 additions and 803 deletions.
595 changes: 349 additions & 246 deletions Cargo.lock

Large diffs are not rendered by default.

12 changes: 9 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,24 +25,29 @@ path = "src/main.rs"

[dependencies]
clap = "3.1.15"
directories = "4.0.1"
futures = "0.3.17"
futures-util = "0.3.14"
lazy_static = "1.3.0"
lazy_static = "1.4.0"
libc = "0.2"
log = "0.4"
nix = "0.23.0"
rand = "0.8.5"
regex = "1.5.4"
reqwest = { version = "0.11", features = ["json", "stream"] }
semver = "1.0.4"
serde_json = "1.0.69"
serde = { version = "1.0", features = ["derive"] }
serde_derive = "1.0"
serde_json = "1.0"
sha2 = "0.9.8"
shellexpand = "2.1.0"
simple-error = "0.2.3"
simplelog = { version = "^0.12.0", features = ["paris"] }
sudo = "0.6.0"
tokio = { version = "1", features = ["full"] }

[target.'cfg(target_os = "linux")'.dependencies]
nix = "0.23.0"

[target.'cfg(windows)'.dependencies]
is_elevated = "0.1.2"
remove_dir_all = "0.7.0"
Expand All @@ -51,5 +56,6 @@ winreg = "0.10"
[build-dependencies]
clap = "3.0.10"
clap_complete = "3.0.4"
lazy_static = "1.4.0"
simplelog = { version = "^0.12.0", features = ["paris"] }
static_vcruntime = "2.0"
8 changes: 8 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@

## New features

* Experimental multi-library support. See `rig library --help` for the
details.

* On macOS rig now includes a menu bar app that show the default R version,
lets you choose between R versions and libraries, and lets you start
RStudio with a specific R version, and/or a recent RStudio project.
Start the app with `open -a Rig`.

* Better messages. rig has a `-v` and `-vv` flag now, for extra debug and
trace messages.

Expand Down
1 change: 1 addition & 0 deletions README.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ Run `rig` to see all commands and examples.
```
rig add -- install a new R version
rig default -- print or set default R version
library -- Manage package libraries [alias: lib] (experimental)
rig list -- list installed R versions
rig resolve -- resolve a symbolic R version
rig rm -- remove R versions
Expand Down
62 changes: 50 additions & 12 deletions Rig.app/Rig/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,27 +26,29 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate {
style: .segmentedControl
)

func applicationDidFinishLaunching(_ aNotification: Notification) {
let statusBar = NSStatusBar.system
statusBarItem = statusBar.statusItem(withLength: NSStatusItem.variableLength)
func setStatusBarTitle() {
let def = try? rigDefault()
if def == nil {
statusBarItem.button?.title = "R"
} else {
statusBarItem.button?.title = "R " + def!
let lib = try? rigLibDefault()
let libstr = (lib == nil || lib! == "main") ? "" : " (" + lib! + ")"
statusBarItem.button?.title = "R " + def! + libstr
}
}

func applicationDidFinishLaunching(_ aNotification: Notification) {
let statusBar = NSStatusBar.system
statusBarItem = statusBar.statusItem(withLength: NSStatusItem.variableLength)
setStatusBarTitle()
statusBarItem.button?.action = #selector(self.statusBarButtonClicked(sender:))
statusBarItem.button?.sendAction(on: [.leftMouseUp, .rightMouseUp])

let fileManager = FileManager.default
let versions = "/Library/Frameworks/R.framework/Versions"
if fileManager.fileExists(atPath: versions) {
watcher = DirectoryWatcher(withPath: versions, callback: { directoryWatcher in
let def = try? rigDefault()
if def != nil {
self.statusBarItem.button?.title = "R " + def!
}
self.setStatusBarTitle()
})
}
}
Expand All @@ -61,6 +63,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate {

let def = try? rigDefault() ?? ""
let list: Array<InstalledVersion> = (try? rigList()) ?? []
let libs: Array<String> = (try? rigLibList()) ?? []

// -- rstudio menu -----------------------------------------------------------------------------------------------------

Expand Down Expand Up @@ -129,6 +132,27 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate {
menu.addItem(item)
}

// -- library version menu ----------------------------------------------------------------------------------------------

if libs.count > 1 {
menu.addItem(NSMenuItem.separator())
menu.addItem(NSMenuItem(title: "Curent library", action: nil, keyEquivalent: ""))
for lib in libs {
let item = NSMenuItem()
var label = lib
if label.last != nil && label.last! == "*" {
label.removeLast()
item.state = NSControl.StateValue.on
}
item.title = label
item.action = #selector(selectLibrary)
item.keyEquivalent = ""
menu.addItem(item)
}
}

// ----------------------------------------------------------------------------------------------------------------------

menu.addItem(NSMenuItem.separator())
menu.addItem(NSMenuItem(title: "Preferences...", action: #selector(preferencesMenuItemActionHandler), keyEquivalent: ""))
menu.addItem(NSMenuItem(title: "Quit", action: #selector(NSApplication.terminate(_:)), keyEquivalent: "q"))
Expand Down Expand Up @@ -162,17 +186,31 @@ from a shell. (It will need an admin password.)
do {
try rigSetDefault(version: ver)
// the directory watcher will update this, but nevertheless we update it as well
let newver = try? rigDefault()
if newver != nil {
statusBarItem.button?.title = "R " + newver!
}
self.setStatusBarTitle()
} catch RigError.error(let msg) {
setError(msg: "Failed to set default: \(msg)", info: info)
} catch {
setError(msg: "Failed to set default, unknown error", info: info)
}
}

@objc func selectLibrary(_ sender: NSMenuItem?) {
let lib = sender!.title
let info = """
Check if `rig lib default` works form the command line.
Consider reporting a bug at https://github.com/r-lib/rig/issues.
Thank you!
"""
do {
try rigLibSetDefault(library: lib)
self.setStatusBarTitle()
} catch RigError.error(let msg) {
setError(msg: "Failed to set default library: \(msg)", info: info)
} catch {
setError(msg: "Failed to set default library, unknown error", info: info)
}
}

@objc func startRStudio2(_ sender: NSMenuItem?) {
var msg = sender!.representedObject! as! Array<String>
var proj = msg[0] as! String
Expand Down
4 changes: 4 additions & 0 deletions Rig.app/Rig/rig.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,7 @@ int rig_list_with_versions(char *ptr, size_t size);
int rig_set_default(const char *ptr);

int rig_start_rstudio(const char *pversion, const char *pproject);

int rig_library_list(char *ptr, size_t size);

int rig_lib_set_default(const char *ptr);
61 changes: 61 additions & 0 deletions Rig.app/Rig/rigapi.swift
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,67 @@ func rigList() throws -> Array<InstalledVersion> {
return result
}

func rigLibDefault() throws -> String {
let libs = try rigLibList()
if libs.count <= 1 {
return "main"
} else {
var def = "main"
for lib in libs {
let star = lib.last
if star != nil && star! == "*" {
def = lib
def.removeLast()
break
}
}
return def
}
}

func rigLibSetDefault(library: String) throws {
var buffer = library.data(using: .utf8)!
buffer.append(0)
var err: Int32 = 0;
buffer.withUnsafeMutableBytes({(p: UnsafeMutablePointer<CChar>) -> Void in
err = rig_lib_set_default(p)
})
if err != 0 {
throw RigError.error(msg: rigLastError())
}
}

func rigLibList() throws -> Array<String> {
var buffer = Data(count: 1024)
let n = buffer.count
var err: Int32 = 0
buffer.withUnsafeMutableBytes({(p: UnsafeMutablePointer<CChar>) -> Void in
err = rig_library_list(p, n)
})

if err != 0 {
throw RigError.error(msg: rigLastError())
}

var result: Array<String> = []
var i = 0
while i < buffer.count && buffer[i] != 0 {
if buffer[i] == 0 { break }
let start = i
while i < buffer.count && buffer[i] != 0 {
i += 1;
}
let end = i
if end > start {
let v = String(data: buffer.subdata(in: start..<end), encoding: .utf8)
result.append(v!)
}
i += 1;
}

return result
}

func rigStartRStudio(version: String?, project: String?) throws {
var version2: String = version ?? ""
var project2: String = project ?? ""
Expand Down
54 changes: 50 additions & 4 deletions src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
// Arguemnt parsing
// ------------------------------------------------------------------------

// crates used here need to go in build-dependencies as well !!!

use clap::{Command, Arg, ArgMatches};
use lazy_static::lazy_static;

#[cfg(target_os = "macos")]
use simplelog::*;
Expand All @@ -16,6 +19,49 @@ std::include!("help-windows.in");
#[cfg(target_os = "linux")]
std::include!("help-linux.in");

const HELP_LIBRARY: &str = r#"
DESCRIPTION
Manage package libraries [alias: lib] (experimental)
rig supports multiple user package libraries. The usual user library is
called "main".
`rig library default` shows or sets the default library for the
current R version.
`rig library list` lists all libraries for the current R version.
`rig library add` adds a new library for the current R version.
`rig library rm` deletes a library, including all packages in it.
It is not possible to delete the current default library, and it is not
possible to delete the main library.
User libraries are implemented at the user level, no administrator or
root password is needed to add, set or delete them. If you delete an
R installation, the user package libraries and their configurations are
kept for all users on the system.
`rig library` is currently experimental, and might change in future
versions. Feedback is appreciated.
"#;

const HELP_ABOUT_PRE: &str = r#"NAME
rig - manage R installations
DESCRIPTION
rig manages your R installations, on macOS, Windows, and Linux. It can
install and set up multiple versions of R, and make sure that they work
together.
"#;

const HELP_ABOUT_POST: &str = r#"
rig is currently experimental and is a work in progress. Feedback is much
appreciated. See https://github.com/r-lib/rig for bug reports.
"#;

lazy_static! {
static ref HELP_ABOUT_REAL: String =
HELP_ABOUT_PRE.to_string() + HELP_ABOUT + HELP_ABOUT_POST;
}

pub fn rig_app() -> Command<'static> {

let _arch_x86_64: &'static str = "x86_64";
Expand Down Expand Up @@ -51,7 +97,7 @@ pub fn rig_app() -> Command<'static> {

let rig = Command::new("RIG -- The R Installation Manager")
.version("0.3.1")
.about(HELP_ABOUT)
.about(HELP_ABOUT_REAL.as_str())
.arg_required_else_help(true)
.term_width(80);

Expand Down Expand Up @@ -180,11 +226,11 @@ pub fn rig_app() -> Command<'static> {
.long_about(HELP_SYSTEM_LINKS);

let cmd_system_lib = Command::new("create-lib")
.about("Create current user's package libraries")
.about("Set up automatic user package libraries")
.long_about(HELP_SYSTEM_LIB)
.arg(
Arg::new("version")
.help("R versions to create the library for (default: all)")
.help("R versions (default: all)")
.required(false)
.multiple_occurrences(true),
);
Expand Down Expand Up @@ -363,7 +409,7 @@ pub fn rig_app() -> Command<'static> {

let cmd_library = Command::new("library")
.about("Manage package libraries [alias: lib] (experimental)")
.long_about("TODO")
.long_about(HELP_LIBRARY)
.aliases(&["lib"])
.arg_required_else_help(true)
.arg(
Expand Down
Loading

0 comments on commit 1de92e6

Please sign in to comment.