Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor cargo-processx functions #397

Merged
merged 22 commits into from
Nov 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,4 @@ Roxygen: list(markdown = TRUE)
RoxygenNote: 7.3.2
SystemRequirements: Rust 'cargo'; the crate 'libR-sys' must compile
without error
Config/rextendr/version: 0.3.1.9001
91 changes: 42 additions & 49 deletions R/clean.R
Original file line number Diff line number Diff line change
Expand Up @@ -4,76 +4,69 @@
#' invokes `cargo clean` to reset cargo target directory
#' (found by default at `pkg_root/src/rust/target/`).
#' Useful when Rust code should be recompiled from scratch.
#' @param path \[ string \] Path to the package root.
#'
#' @param path character scalar, path to R package root.
#' @param echo logical scalar, should cargo command and outputs be printed to
#' console (default is `TRUE`)
#'
#' @return character vector with names of all deleted files (invisibly).
#'
#' @export
clean <- function(path = ".") {
root <- rprojroot::find_package_root_file(path = path)

rust_folder <- normalizePath(
file.path(root, "src", "rust"),
winslash = "/",
mustWork = FALSE
)
#'
#' @examples
#' \dontrun{
#' clean()
#' }
clean <- function(path = ".", echo = TRUE) {
check_string(path, class = "rextendr_error")
check_bool(echo, class = "rextendr_error")

toml_path <- normalizePath(
file.path(rust_folder, "Cargo.toml"),
winslash = "/",
mustWork = FALSE
)
manifest_path <- find_extendr_manifest(path = path)

# Note: This should be adjusted if `TARGET_DIR` changes in `Makevars`
target_dir <- normalizePath( # nolint: object_usage_linter
file.path(rust_folder, "target"),
winslash = "/",
mustWork = FALSE
target_dir <- rprojroot::find_package_root_file(
"src", "rust", "target",
path = path
)

if (!file.exists(toml_path)) {
cli::cli_abort(c(
"Unable to clean binaries.",
"!" = "{.file Cargo.toml} not found in {.path {rust_folder}}.",
if (!dir.exists(target_dir)) {
cli::cli_abort(
c(
"Could not clean binaries.",
"Target directory not found at {.path target_dir}."
),
call = rlang::caller_call(),
class = "rextendr_error"
))
)
}

cargo_envvars <- get_cargo_envvars()

args <- c(
"clean",
glue("--manifest-path={toml_path}"),
glue("--target-dir={target_dir}"),
glue::glue("--manifest-path={manifest_path}"),
glue::glue("--target-dir={target_dir}"),
if (tty_has_colors()) {
"--color=always"
} else {
"--color=never"
},
"--quiet"
JosiahParry marked this conversation as resolved.
Show resolved Hide resolved
}
)
exec_result <- processx::run(
command = "cargo",
args = args,
echo_cmd = FALSE,
windows_verbatim_args = FALSE,
stderr = "|",
stdout = "|",
error_on_status = FALSE,
env = cargo_envvars

run_cargo(
args,
wd = find_extendr_crate(path = path),
echo = echo
)

if (!isTRUE(exec_result$status == 0)) {
JosiahParry marked this conversation as resolved.
Show resolved Hide resolved
if (!tty_has_colors()) {
err_msg <- cli::ansi_strip(exec_result$stderr)
} else {
err_msg <- exec_result$stderr
}
root <- rprojroot::find_package_root_file(path = path)

if (!dir.exists(root)) {
cli::cli_abort(
c(
"Unable to execute {.code cargo clean}.",
"x" = paste(err_msg, collapse = "\n")
),
call = caller_env(),
"Could not clean binaries.",
"R package directory not found at {.path root}.",
call = rlang::caller_call(),

Check warning on line 66 in R/clean.R

View check run for this annotation

Codecov / codecov/patch

R/clean.R#L64-L66

Added lines #L64 - L66 were not covered by tests
class = "rextendr_error"
)
}

pkgbuild::clean_dll(path = root)
}
8 changes: 0 additions & 8 deletions R/find_extendr.R
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,8 @@
#'
#' @return character scalar, path to Rust crate
#'
#' @examples
#' \dontrun{
#' find_extendr_crate()
#' @keywords internal
#' @noRd
#' }
find_extendr_crate <- function(
path = ".",
error_call = rlang::caller_call()) {
Expand Down Expand Up @@ -41,12 +37,8 @@ find_extendr_crate <- function(
#'
#' @return character scalar, path to Cargo manifest
#'
#' @examples
#' \dontrun{
#' find_extendr_manifest()
#' @keywords internal
#' @noRd
#' }
find_extendr_manifest <- function(
path = ".",
error_call = rlang::caller_call()) {
Expand Down
177 changes: 110 additions & 67 deletions R/license_note.R
Original file line number Diff line number Diff line change
@@ -1,97 +1,140 @@
#' Generate LICENSE.note file.
#'
#' LICENSE.note generated by this function contains information about Rust crate dependencies.
#' To use this function, the [cargo-license](https://crates.io/crates/cargo-license) command must be installed.
#' @param force Logical indicating whether to regenerate LICENSE.note if LICENSE.note already exists.
#' @inheritParams register_extendr
JosiahParry marked this conversation as resolved.
Show resolved Hide resolved
#' @return No return value, called for side effects.
#' LICENSE.note generated by this function contains information about all
#' recursive dependencies in Rust crate.
#'
#' @param path character scalar, the R package directory
JosiahParry marked this conversation as resolved.
Show resolved Hide resolved
#' @param quiet logical scalar, whether to signal successful writing of
#' LICENSE.note (default is `FALSE`)
JosiahParry marked this conversation as resolved.
Show resolved Hide resolved
#' @param force logical scalar, whether to regenerate LICENSE.note if
#' LICENSE.note already exists (default is `TRUE`)
#'
#' @return text printed to LICENSE.note (invisibly).
#'
#' @export
write_license_note <- function(path = ".", quiet = FALSE, force = TRUE) {
if (!cargo_command_available(c("license", "--help"))) {
#'
#' @examples
#' \dontrun{
#' write_license_note()
#' }
write_license_note <- function(
path = ".",
quiet = FALSE,
force = TRUE) {
check_string(path, class = "rextendr_error")
check_bool(quiet, class = "rextendr_error")
check_bool(force, class = "rextendr_error")

Check warning on line 26 in R/license_note.R

View check run for this annotation

Codecov / codecov/patch

R/license_note.R#L24-L26

Added lines #L24 - L26 were not covered by tests

outfile <- rprojroot::find_package_root_file(
"LICENSE.note",
path = path
)

Check warning on line 31 in R/license_note.R

View check run for this annotation

Codecov / codecov/patch

R/license_note.R#L28-L31

Added lines #L28 - L31 were not covered by tests

args <- c(
"metadata",
"--format-version=1"
)

Check warning on line 36 in R/license_note.R

View check run for this annotation

Codecov / codecov/patch

R/license_note.R#L33-L36

Added lines #L33 - L36 were not covered by tests

metadata <- run_cargo(
args,
wd = find_extendr_crate(path = path),
echo = FALSE,
parse_json = TRUE
)

Check warning on line 43 in R/license_note.R

View check run for this annotation

Codecov / codecov/patch

R/license_note.R#L38-L43

Added lines #L38 - L43 were not covered by tests

packages <- metadata[["packages"]]

Check warning on line 45 in R/license_note.R

View check run for this annotation

Codecov / codecov/patch

R/license_note.R#L45

Added line #L45 was not covered by tests

# did we actually get the recursive dependency metadata we need?
required_variables <- c("name", "repository", "authors", "license", "id")

Check warning on line 48 in R/license_note.R

View check run for this annotation

Codecov / codecov/patch

R/license_note.R#L48

Added line #L48 was not covered by tests

packages_exist <- is.data.frame(packages) &&
!is.null(packages) &&
nrow(packages) > 0 &&
all(required_variables %in% names(packages))

Check warning on line 53 in R/license_note.R

View check run for this annotation

Codecov / codecov/patch

R/license_note.R#L50-L53

Added lines #L50 - L53 were not covered by tests

if (!packages_exist) {

Check warning on line 55 in R/license_note.R

View check run for this annotation

Codecov / codecov/patch

R/license_note.R#L55

Added line #L55 was not covered by tests
cli::cli_abort(
c(
"The {.code cargo license} command is required to run the {.fun write_license_note} function.",
"*" = "Please install cargo-license ({.url https://crates.io/crates/cargo-license}) first.",
i = "Run {.code cargo install cargo-license} from your terminal."
),
"Unable to write LICENSE.note.",
"Metadata for recursive dependencies not found.",
call = rlang::caller_call(),

Check warning on line 59 in R/license_note.R

View check run for this annotation

Codecov / codecov/patch

R/license_note.R#L57-L59

Added lines #L57 - L59 were not covered by tests
class = "rextendr_error"
)
}

manifest_file <- rprojroot::find_package_root_file("src", "rust", "Cargo.toml", path = path)
outfile <- rprojroot::find_package_root_file("LICENSE.note", path = path)
# exclude current package from LICENSE.note
current_package <- metadata[["resolve"]][["root"]]

Check warning on line 65 in R/license_note.R

View check run for this annotation

Codecov / codecov/patch

R/license_note.R#L65

Added line #L65 was not covered by tests
JosiahParry marked this conversation as resolved.
Show resolved Hide resolved

current_package_exists <- length(current_package) == 1 &&
is.character(current_package) &&
!is.null(current_package)

Check warning on line 69 in R/license_note.R

View check run for this annotation

Codecov / codecov/patch

R/license_note.R#L67-L69

Added lines #L67 - L69 were not covered by tests

if (!isTRUE(force) && file.exists(outfile)) {
if (!current_package_exists) {

Check warning on line 71 in R/license_note.R

View check run for this annotation

Codecov / codecov/patch

R/license_note.R#L71

Added line #L71 was not covered by tests
cli::cli_abort(
c(
"LICENSE.note already exists.",
"If you want to regenerate LICENSE.note, set `force = TRUE` to {.fun write_license_note}."
),
"Unable to write LICENSE.note.",
"Failed to identify current Rust crate.",
call = rlang::caller_call(),

Check warning on line 75 in R/license_note.R

View check run for this annotation

Codecov / codecov/patch

R/license_note.R#L73-L75

Added lines #L73 - L75 were not covered by tests
class = "rextendr_error"
)
}

list_license <- processx::run(
"cargo",
c(
"license",
"--authors",
"--json",
"--avoid-build-deps",
"--avoid-dev-deps",
"--manifest-path", manifest_file
)
)$stdout %>%
jsonlite::parse_json()

package_names <- processx::run(
"cargo",
c(
"metadata",
"--no-deps",
"--format-version", "1",
"--manifest-path", manifest_file
)
)$stdout %>%
jsonlite::parse_json() %>%
purrr::pluck("packages") %>%
purrr::map_chr("name")

.prep_authors <- function(authors, package) {
ifelse(!is.null(authors), authors, paste0(package, " authors")) %>%
stringi::stri_replace_all_regex(r"(\ <.+?>)", "") %>%
stringi::stri_replace_all_regex(r"(\|)", ", ")
}
packages <- packages[packages[["id"]] != current_package, ]

Check warning on line 80 in R/license_note.R

View check run for this annotation

Codecov / codecov/patch

R/license_note.R#L80

Added line #L80 was not covered by tests

# replace missing values
packages[["respository"]] <- replace_na(
packages[["repository"]],
"unknown"
)

Check warning on line 86 in R/license_note.R

View check run for this annotation

Codecov / codecov/patch

R/license_note.R#L83-L86

Added lines #L83 - L86 were not covered by tests

packages[["licenses"]] <- replace_na(
packages[["repository"]],
"not provided"
)

Check warning on line 91 in R/license_note.R

View check run for this annotation

Codecov / codecov/patch

R/license_note.R#L88-L91

Added lines #L88 - L91 were not covered by tests

# remove email addresses and special characters and combine all authors
# of a crate into a single character scalar
packages[["authors"]] <- unlist(Map(
prep_authors,
packages[["authors"]],
packages[["name"]]
))

Check warning on line 99 in R/license_note.R

View check run for this annotation

Codecov / codecov/patch

R/license_note.R#L95-L99

Added lines #L95 - L99 were not covered by tests
JosiahParry marked this conversation as resolved.
Show resolved Hide resolved

separator <- "-------------------------------------------------------------"

note_header <- paste0(
"The binary compiled from the source code of this package contains the following Rust crates:\n",
"The binary compiled from the source code of this package ",
"contains the following Rust crates:\n",

Check warning on line 105 in R/license_note.R

View check run for this annotation

Codecov / codecov/patch

R/license_note.R#L104-L105

Added lines #L104 - L105 were not covered by tests
"\n",
"\n",
separator
)

note_body <- list_license %>%
purrr::discard(function(x) x$name %in% package_names) %>%
purrr::map_chr(
function(x) {
paste0(
"\n",
"Name: ", x$name, "\n",
"Repository: ", x$repository, "\n",
"Authors: ", .prep_authors(x$authors, x$name), "\n",
"License: ", x$license, "\n",
"\n",
separator
)
}
)
note_body <- paste0(
"\n",
"Name: ", packages[["name"]], "\n",
"Repository: ", packages[["repository"]], "\n",
"Authors: ", packages[["authors"]], "\n",
"License: ", packages[["license"]], "\n",
"\n",
separator
)

Check warning on line 119 in R/license_note.R

View check run for this annotation

Codecov / codecov/patch

R/license_note.R#L111-L119

Added lines #L111 - L119 were not covered by tests

write_file(
text = c(note_header, note_body),
path = outfile,
search_root_from = path,
quiet = quiet
quiet = quiet,
overwrite = force
)

Check warning on line 127 in R/license_note.R

View check run for this annotation

Codecov / codecov/patch

R/license_note.R#L125-L127

Added lines #L125 - L127 were not covered by tests
}

prep_authors <- function(authors, package) {
authors <- ifelse(
is.na(authors),
paste0(package, " authors"),
authors

Check warning on line 134 in R/license_note.R

View check run for this annotation

Codecov / codecov/patch

R/license_note.R#L131-L134

Added lines #L131 - L134 were not covered by tests
)

authors <- stringi::stri_replace_all_regex(authors, r"(\ <.+?>)", "")

Check warning on line 137 in R/license_note.R

View check run for this annotation

Codecov / codecov/patch

R/license_note.R#L137

Added line #L137 was not covered by tests

paste0(authors, collapse = ", ")

Check warning on line 139 in R/license_note.R

View check run for this annotation

Codecov / codecov/patch

R/license_note.R#L139

Added line #L139 was not covered by tests
}
Loading
Loading