Skip to content

Commit

Permalink
rust_sitrep(): explain what toolchains are required, guide through …
Browse files Browse the repository at this point in the history
…installation (#318)

* Cleanup after latests PR

* Highlight targets

* Update target verification

* More info on the toolchain

* Finalize notifications

* Update snapshots & tests

* Cleanup

* Add tests

* Comments

* Update news

* README

* Print rust toolchain only if at least one is found

* Update readme
  • Loading branch information
Ilia-Kosenkov authored Sep 26, 2023
1 parent bdb1b0f commit 69eba4d
Show file tree
Hide file tree
Showing 9 changed files with 361 additions and 69 deletions.
1 change: 1 addition & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# rextendr (development version)

* `rust_sitrep()` now better communicates the status of the Rust toolchain and available targets. It also guides the user through necessary installation steps to fix Rust setup (#318).
* `use_extendr()` and `document()` now set the `SystemRequirements` field of the `DESCRIPTION` file to
`Cargo (rustc package manager)` if the field is empty (#298).
* `use_extendr()` gets a new ability to overwrite existing rextendr templates (#292).
Expand Down
149 changes: 135 additions & 14 deletions R/rust_sitrep.R
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,38 @@ rust_sitrep <- function() {
msgs <- c(
msgs,
"i" = "host: {rustup_status$host}",
"i" = "toolchain: {rustup_status$toolchain}",
"i" = "target{?s}: {rustup_status$targets}"
"i" = if (length(rustup_status[["toolchains"]]) > 0) {
"toolchain{?s}: {rustup_status$toolchains}"
} else {
NULL
}
)

if (!is.null(rustup_status[["candidate_toolchains"]])) {
msgs <- c(
msgs,
"!" = "{?This/One of these} toolchain{?s} should be default: {.strong {rustup_status$candidate_toolchains}}",
"i" = "Run e.g. {.code rustup default {rustup_status$candidate_toolchains[1]}}"
)
} else if (!is.null(rustup_status[["missing_toolchain"]])) {
msgs <- c(
msgs,
"!" = "Toolchain {.strong {rustup_status$missing_toolchain}} is required to be installed and set as default",
"i" = "Run {.code rustup toolchain install {rustup_status$missing_toolchain}} to install it",
"i" = "Run {.code rustup default {rustup_status$missing_toolchain}} to make it default"
)
} else {
msgs <- c(msgs,
"i" = "target{?s}: {rustup_status$targets}"
)
if (!is.null(rustup_status[["missing_target"]])) {
msgs <- c(
msgs,
"!" = "Target {.strong {rustup_status$missing_target}} is required on this host machine",
"i" = "Run {.code rustup target add {rustup_status$missing_target}} to install it"
)
}
}
} else {
msgs <- c(
msgs,
Expand Down Expand Up @@ -90,18 +119,110 @@ rustup_toolchain_target <- function() {
stringi::stri_sub(from = 15L) %>%
vctrs::vec_slice(1L)

# > rustup show active-toolchain
# stable-x86_64-pc-windows-msvc (default)
toolchain <- try_exec_cmd("rustup", c("show", "active-toolchain")) %>%
stringi::stri_replace_last_fixed("(default)", "") %>%
stringi::stri_trim_both()
# > rustup toolchain list
# stable-x86_64-pc-windows-msvc
# nightly-x86_64-pc-windows-msvc (default)
toolchain_info <- try_exec_cmd("rustup", c("toolchain", "list")) %>%
stringi::stri_trim_both() %>%
verify_toolchains(host)

# > rustup target list --installed
# i686-pc-windows-gnu
# x86_64-pc-windows-gnu
# x86_64-pc-windows-msvc
targets <- try_exec_cmd("rustup", c("target", "list", "--installed")) %>%
stringi::stri_trim_both()
if (is.null(toolchain_info[["missing_toolchain"]]) && is.null(toolchain_info[["candidate_toolchains"]])) {
# > rustup target list --installed
# i686-pc-windows-gnu
# x86_64-pc-windows-gnu
# x86_64-pc-windows-msvc
targets_info <- try_exec_cmd("rustup", c("target", "list", "--installed")) %>%
stringi::stri_trim_both() %>%
verify_targets(host)
} else {
targets_info <- list()
}

list(host = host) %>%
append(targets_info) %>%
append(toolchain_info)
}

#' Verify that the required toolchain is available.
#'
#' If a toolchain with architecture matching host's is default, color it green.
#' If a default toolchain does not match host's architecture, color it red.
#' Color yellow all toolchains that match hots's architecutre and return then as \code{$candidate_toolchains}.
#' If not matching toolchain is found, determine the best candidate using host's architecture
#' and return it as \code{$missing_toolchain}.#'
#' @param toolchains A character vector of toolchains
#' @param host Host architecture identifier
#' @return A list with the following elements:
#' \itemize{
#' \item \code{toolchains}: A character vector of toolchains, colored
#' \itemize{
#' \item green if matching and default,
#' \item yellow if candidate,
#' \item red if matching and not default,
#' }
#' \item \code{missing_toolchain}: An identifier of the toolchain that should be available on the system,
#' \item \code{candidate_toolchains}: A character vector of toolchains that are candidates to be default.
#' }
#' @noRd
verify_toolchains <- function(toolchains, host) {
if (rlang::is_empty(toolchains)) {
return(list(toolchains = toolchains, missing_toolchain = glue("stable-{host}")))
}

default_toolchain_index <- stringi::stri_detect_fixed(toolchains, "(default)")
missing_toolchain <- NULL
candidate_toolchains <- NULL
if (isTRUE(stringi::stri_detect_fixed(toolchains[default_toolchain_index], host))) {
toolchains[default_toolchain_index] <- cli::col_green(toolchains[default_toolchain_index])
} else {
toolchains[default_toolchain_index] <- cli::col_red(toolchains[default_toolchain_index])
candidates <- stringi::stri_detect_fixed(toolchains, host)
if (any(candidates)) {
candidate_toolchains <- toolchains[candidates]
toolchains[candidates] <- cli::col_yellow(toolchains[candidates])
} else {
missing_toolchain <- glue("stable-{host}")
}
}
list(toolchains = toolchains, missing_toolchain = missing_toolchain, candidate_toolchains = candidate_toolchains)
}

list(host = host, toolchain = toolchain, targets = targets)
#' Search for targets that are matching the host.
#'
#' On machines other than Windows, the target should match the host exactly.
#' On Windows, the target is GNU.
#' If a matching target is found, color it green.
#' @param targets A character vector of targets
#' @param host Host architecture identifier
#' @return A list with the following elements:
#' \itemize{
#' \item \code{targets}: A character vector of targets with matching target colored green,
#' \item \code{missing_target}: An identifier of the target that should be available on the system.
#' }
#' @noRd
verify_targets <- function(targets, host) {
expected_target <- get_required_target(host)

target_index <- stringi::stri_cmp_eq(targets, expected_target)
targets[target_index] <- cli::col_green(targets[target_index])

if (any(target_index)) {
missing_target <- NULL
} else {
missing_target <- expected_target
}

list(targets = targets, missing_target = missing_target)
}

#' Return the expected target identifier given host identifier.
#' @param host Host architecture identifier
#' @return Required target identifier
#' @noRd
get_required_target <- function(host) {
if (.Platform[["OS.type"]] == "windows") {
stringi::stri_replace_first_regex(host, pattern = "-[a-z]+$", replacement = "-gnu")
} else {
host
}
}
43 changes: 43 additions & 0 deletions README.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,49 @@ To execute Rust code, you will also need to set up a working Rust toolchain. See

## Usage

### Sitrep

A good first step is to check the status of Rust toolchain and available targets using `rust_sitrep()`. If everything is OK, you should see something like this:

```{r eval=FALSE, echo=TRUE}
rust_sitrep()
# Rust infrastructure sitrep:
# ✔ "rustup": 1.26.0 (5af9b9484 2023-04-05)
# ✔ "cargo": 1.72.0 (103a7ff2e 2023-08-15)
# ℹ host: x86_64-pc-windows-msvc
# ℹ toolchain: stable-x86_64-pc-windows-msvc (default)
# ℹ target: x86_64-pc-windows-gnu
```

If, for instance, no toolchain is found, you will see something like this:

```{r eval=FALSE, echo=TRUE}
rust_sitrep()
# Rust infrastructure sitrep:
# ✔ "rustup": 1.26.0 (5af9b9484 2023-04-05)
# ✔ "cargo": 1.72.0 (103a7ff2e 2023-08-15)
# ℹ host: x86_64-pc-windows-msvc
# ! Toolchain stable-x86_64-pc-windows-msvc is required to be installed and set as default
# ℹ Run `rustup toolchain install stable-x86_64-pc-windows-msvc` to install it
# ℹ Run `rustup default stable-x86_64-pc-windows-msvc` to make it default
```

Finally, if you are missing the required target (on all platforms but Windows `{rextendr}` uses default target), the report will resemble the following:

```{r eval=FALSE, echo=TRUE}
rust_sitrep()
# Rust infrastructure sitrep:
# ✔ "rustup": 1.26.0 (5af9b9484 2023-04-05)
# ✔ "cargo": 1.72.0 (103a7ff2e 2023-08-15)
# ℹ host: x86_64-pc-windows-msvc
# i toolchains: nightly-x86_64-pc-windows-msvc and stable-x86_64-pc-windows-msvc (default)
# i targets: x86_64-pc-windows-msvc and i686-pc-windows-msvc
# ! Target x86_64-pc-windows-gnu is required on this host machine
# i Run `rustup target add x86_64-pc-windows-gnu` to install it
```

### Code examples

Basic use example:

```{r}
Expand Down
48 changes: 48 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,54 @@ successfully build libR-sys you’re good.

## Usage

### Sitrep

A good first step is to check the status of Rust toolchain and available
targets using `rust_sitrep()`. If everything is OK, you should see
something like this:

``` r
rust_sitrep()
# Rust infrastructure sitrep:
# ✔ "rustup": 1.26.0 (5af9b9484 2023-04-05)
# ✔ "cargo": 1.72.0 (103a7ff2e 2023-08-15)
# ℹ host: x86_64-pc-windows-msvc
# ℹ toolchain: stable-x86_64-pc-windows-msvc (default)
# ℹ target: x86_64-pc-windows-gnu
```

If, for instance, no toolchain is found, you will see something like
this:

``` r
rust_sitrep()
# Rust infrastructure sitrep:
# ✔ "rustup": 1.26.0 (5af9b9484 2023-04-05)
# ✔ "cargo": 1.72.0 (103a7ff2e 2023-08-15)
# ℹ host: x86_64-pc-windows-msvc
# ! Toolchain stable-x86_64-pc-windows-msvc is required to be installed and set as default
# ℹ Run `rustup toolchain install stable-x86_64-pc-windows-msvc` to install it
# ℹ Run `rustup default stable-x86_64-pc-windows-msvc` to make it default
```

Finally, if you are missing the required target (on all platforms but
Windows `{rextendr}` uses default target), the report will resemble the
following:

``` r
rust_sitrep()
# Rust infrastructure sitrep:
# ✔ "rustup": 1.26.0 (5af9b9484 2023-04-05)
# ✔ "cargo": 1.72.0 (103a7ff2e 2023-08-15)
# ℹ host: x86_64-pc-windows-msvc
# i toolchains: nightly-x86_64-pc-windows-msvc and stable-x86_64-pc-windows-msvc (default)
# i targets: x86_64-pc-windows-msvc and i686-pc-windows-msvc
# ! Target x86_64-pc-windows-gnu is required on this host machine
# i Run `rustup target add x86_64-pc-windows-gnu` to install it
```

### Code examples

Basic use example:

``` r
Expand Down
1 change: 0 additions & 1 deletion man/rextendr.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

58 changes: 56 additions & 2 deletions tests/testthat/_snaps/rust-sitrep.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
v "rustup": 1.0.0 (0000000 0000-00-00)
x "cargo": not found
i host: arch-pc-os-tool
i toolchain: stable-arch-pc-os-tool
i toolchain: stable-arch-pc-os-tool (default)
i target: arch-pc-os-tool
x "cargo" is required to build rextendr-powered packages
i It is recommended to use "rustup" to manage "cargo" and "rustc"
Expand All @@ -47,6 +47,60 @@
v "rustup": 1.0.0 (0000000 0000-00-00)
v "cargo": 1.0.0 (0000000 0000-00-00)
i host: arch-pc-os-tool
i toolchain: stable-arch-pc-os-tool
i toolchain: stable-arch-pc-os-tool (default)
i target: arch-pc-os-tool

# No toolchains found

Code
rust_sitrep()
Message
Rust infrastructure sitrep:
v "rustup": 1.0.0 (0000000 0000-00-00)
v "cargo": 1.0.0 (0000000 0000-00-00)
i host: arch-pc-os-tool
! Toolchain stable-arch-pc-os-tool is required to be installed and set as default
i Run `rustup toolchain install stable-arch-pc-os-tool` to install it
i Run `rustup default stable-arch-pc-os-tool` to make it default

# Wrong toolchain found

Code
rust_sitrep()
Message
Rust infrastructure sitrep:
v "rustup": 1.0.0 (0000000 0000-00-00)
v "cargo": 1.0.0 (0000000 0000-00-00)
i host: arch-pc-os-tool
i toolchain: not-a-valid-toolchain
! Toolchain stable-arch-pc-os-tool is required to be installed and set as default
i Run `rustup toolchain install stable-arch-pc-os-tool` to install it
i Run `rustup default stable-arch-pc-os-tool` to make it default

# Wrong toolchain is set as default

Code
rust_sitrep()
Message
Rust infrastructure sitrep:
v "rustup": 1.0.0 (0000000 0000-00-00)
v "cargo": 1.0.0 (0000000 0000-00-00)
i host: arch-pc-os-tool
i toolchains: not-a-valid-toolchain (default) and stable-arch-pc-os-tool
! This toolchain should be default: stable-arch-pc-os-tool
i Run e.g. `rustup default stable-arch-pc-os-tool`

# Required target is not available

Code
rust_sitrep()
Message
Rust infrastructure sitrep:
v "rustup": 1.0.0 (0000000 0000-00-00)
v "cargo": 1.0.0 (0000000 0000-00-00)
i host: arch-pc-os-tool
i toolchains: not-a-valid-toolchain and stable-arch-pc-os-tool (default)
i targets: wrong-target-1 and wrong-target-2
! Target required-target is required on this host machine
i Run `rustup target add required-target` to install it

25 changes: 0 additions & 25 deletions tests/testthat/_snaps/unix/helper-functions.md

This file was deleted.

Loading

0 comments on commit 69eba4d

Please sign in to comment.