From 82bdf45442d8da6f920dd442d64f4abca55949b6 Mon Sep 17 00:00:00 2001 From: Josiah Parry Date: Sat, 23 Sep 2023 10:06:56 -0400 Subject: [PATCH 01/37] add use_cran_defaults() function along with vendor_pkgs() --- NAMESPACE | 2 + R/cran-compliance.R | 221 +++++++++++++++++++++ inst/templates/cran/Makevars | 42 ++++ inst/templates/cran/Makevars.win | 54 +++++ inst/templates/cran/configure | 21 ++ inst/templates/cran/configure.win | 15 ++ man/cran.Rd | 53 +++++ tests/testthat/_snaps/use_cran_defaults.md | 200 +++++++++++++++++++ tests/testthat/test-use_cran_defaults.R | 39 ++++ vignettes/articles/cran-compliance.Rmd | 74 +++++++ 10 files changed, 721 insertions(+) create mode 100644 R/cran-compliance.R create mode 100644 inst/templates/cran/Makevars create mode 100644 inst/templates/cran/Makevars.win create mode 100644 inst/templates/cran/configure create mode 100644 inst/templates/cran/configure.win create mode 100644 man/cran.Rd create mode 100644 tests/testthat/_snaps/use_cran_defaults.md create mode 100644 tests/testthat/test-use_cran_defaults.R create mode 100644 vignettes/articles/cran-compliance.Rmd diff --git a/NAMESPACE b/NAMESPACE index 4232e168..81fec799 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -11,7 +11,9 @@ export(rust_function) export(rust_sitrep) export(rust_source) export(to_toml) +export(use_cran_defaults) export(use_extendr) +export(vendor_pkgs) export(write_license_note) importFrom(dplyr,"%>%") importFrom(dplyr,mutate) diff --git a/R/cran-compliance.R b/R/cran-compliance.R new file mode 100644 index 00000000..994c3750 --- /dev/null +++ b/R/cran-compliance.R @@ -0,0 +1,221 @@ +#' Use CRAN compliant defaults +#' +#' Modifies an extendr package to use CRAN compliant settings. +#' +#' @details +#' +#' `use_cran_defaults()` modifies an existing package to provide CRAN complaint +#' settings and files. It creates `configure` and `configure.win` files as well as +#' modifies `Makevars` and `Makevars.win` to use required CRAN settings. +#' +#' `vendor_pkgs()` is used to package the dependencies as required by CRAN. +#' It executes `cargo vendor` on your behalf creating a `vendor/` directory and a +#' compressed `vendor.tar.xz` which will be shipped with package itself. +#' If you have modified your dependencies, you will need need to repackage +# the vendored dependencies using `vendor_pkgs()`. +#' +#' @inheritParams use_extendr +#' @returns +#' +#' - `vendor_pkgs()` returns a data.frame with two columns `crate` and `version` +#' - `use_cran_defaults()` returns `NULL` and is used solely for its side effects +#' +#' @examples +#' +#' if (interactive()) { +#' use_cran_defaults() +#' vendor_pkgs() +#' } +#' @name cran +#' @export +use_cran_defaults <- function( + path = ".", + quiet = FALSE, + overwrite = NULL, + lib_name = NULL +) { + + # if not in an interactive session and overwrite is null, set it to false + if (!rlang::is_interactive()) { + overwrite <- overwrite %||% FALSE + } + + # silence output + local_quiet_cli(quiet) + + # find package root + pkg_root <- rprojroot::find_package_root_file(path) + + # set the path for the duration of the function + withr::local_dir(pkg_root) + + if (is.null(lib_name)) { + lib_name <- as_valid_rust_name(pkg_name(path)) + } else if (length(lib_name) > 1) { + cli::cli_abort( + "{.arg lib_name} must be a character scalar", + class = "rextendr_error" + ) + } + + # add configure and configure.win templates + use_rextendr_template( + "cran/configure", + save_as = "configure", + quiet = quiet, + overwrite = overwrite, + data = list(lib_name = lib_name) + ) + + # configure needs to be made executable + Sys.chmod("configure", "0755") + + use_rextendr_template( + "cran/configure.win", + save_as = "configure.win", + quiet = quiet, + overwrite = overwrite, + data = list(lib_name = lib_name) + ) + + # use CRAN specific Makevars templates + use_rextendr_template( + "cran/Makevars", + save_as = file.path("src", "Makevars"), + quiet = quiet, + overwrite = overwrite, + data = list(lib_name = lib_name) + ) + + use_rextendr_template( + "cran/Makevars.win", + save_as = file.path("src", "Makevars.win"), + quiet = quiet, + overwrite = overwrite, + data = list(lib_name = lib_name) + ) + + # vendor directory will need to be ignored by git and R CMD build + if (!rlang::is_installed("usethis")) { + cli::cli_inform( + c( + "!" = "Add {.code ^src/rust/vendor$} to your {.file .Rbuildignore}", + "!" = "Add {.code ^src/rust/vendor$} to your {.file .gitignore}", + "i" = "Install {.pkg usethis} to have this done automatically." + ) + ) + } else { + # vendor will be big when expanded and should be ignored + usethis::use_build_ignore( + file.path("src", "rust", "vendor") + ) + + usethis::use_git_ignore( + file.path("src", "rust", "vendor") + ) + } + + invisible(NULL) +} + +#' @export +#' @name cran +vendor_pkgs <- function(path = ".", quiet = FALSE, overwrite = NULL) { + local_quiet_cli(quiet) + + # get path to rust folder + src_dir <- rprojroot::find_package_root_file(path, "src/rust") + + # if `src/rust` does not exist error + if (!dir.exists(src_dir)) { + cli::cli_abort( + c("{.path src/rust} cannot be found", "i" = "Have you used {.fn use_extendr}?"), + class = "rextendr_error" + ) + } + + # if cargo.lock does not exist, cerate it using `cargo update` + cargo_lock_fp <- file.path(src_dir, "Cargo.lock") + + if (!file.exists(cargo_lock_fp)) { + withr::with_dir(src_dir, { + update_res <- processx::run( + "cargo", + c( + "generate-lockfile", + "--manifest-path", + file.path(src_dir, "Cargo.toml") + ), + stderr_line_callback = function(x, proc) { + if (!grepl("To use vendored sources", x) && x != "") { + cli::cat_bullet(stringi::stri_trim_left(x)) + } + } + ) + }) + } + + # vendor crates + withr::with_dir(src_dir, { + vendor_res <- processx::run( + "cargo", + c( + "vendor", + "--locked", + "--manifest-path", + file.path(src_dir, "Cargo.toml") + ), + stderr_line_callback = function(x, proc) { + if (!grepl("To use vendored sources", x) && x != "") { + cli::cat_bullet(stringi::stri_trim_left(x)) + } + } + ) + }) + + if (vendor_res[["status"]] != 0) { + cli::cli_abort( + "{.code cargo vendor} was not executed successfully", + class = "rextendr_error" + ) + } + + # capture vendor-config.toml content + config_toml <- stringi::stri_split(vendor_res$stdout, coll = "\n")[[1]] + + # always write to file as cargo vendor catches things like patch.crates-io + # and provides the appropriate configuration. + brio::write_lines(config_toml, file.path(src_dir, "vendor-config.toml")) + cli::cli_alert_info("Writing {.file src/rust/vendor-config.toml}") + + # compress to vendor.tar.xz + compress_res <- withr::with_dir(src_dir, { + processx::run( + "tar", c( + "-cJ", "--no-xattrs", "-f", "vendor.tar.xz", "vendor" + ) + ) + }) + + if (compress_res[["status"]] != 0) { + cli::cli_abort( + "Folder {.path vendor} could not be compressed", + class = "rextendr_error" + ) + } + + # create a dataframe of vendored crates + vendored <- stringi::stri_split(vendor_res[["stderr"]], coll = "\n")[[1]] + trimmed <- stringi::stri_trim_left(vendored) + to_remove <- grepl("To use vendored sources", trimmed) | trimmed == "" + rows <- stringi::stri_split_fixed(trimmed[!to_remove], pattern = " ") + + res <- purrr::map_dfr(rows, function(x) { + data.frame(crate = x[2], version = x[3]) + }) + + # return packages and versions invisibly + invisible(res) +} + + diff --git a/inst/templates/cran/Makevars b/inst/templates/cran/Makevars new file mode 100644 index 00000000..df08b1ce --- /dev/null +++ b/inst/templates/cran/Makevars @@ -0,0 +1,42 @@ +TARGET_DIR = ./rust/target +LIBDIR = $(TARGET_DIR)/release +STATLIB = $(LIBDIR)/lib{{{lib_name}}}.a +PKG_LIBS = -L$(LIBDIR) -l{{{lib_name}}} + +all: C_clean + +$(SHLIB): $(STATLIB) + +CRAN_FLAGS=-j 2 --offline +CARGOTMP = $(CURDIR)/.cargo + + +$(STATLIB): + if [ -f ./rust/vendor.tar.xz ]; then \ + tar xf rust/vendor.tar.xz && \ + mkdir -p $(CARGOTMP) && \ + cp rust/vendor-config.toml $(CARGOTMP)/config.toml; \ + fi + + # In some environments, ~/.cargo/bin might not be included in PATH, so we need + # to set it here to ensure cargo can be invoked. It is appended to PATH and + # therefore is only used if cargo is absent from the user's PATH. + if [ "$(NOT_CRAN)" != "true" ]; then \ + export CARGO_HOME=$(CARGOTMP); \ + fi && \ + export PATH="$(PATH):$(HOME)/.cargo/bin" && \ + cargo build $(CRAN_FLAGS) --lib --release --manifest-path=./rust/Cargo.toml --target-dir $(TARGET_DIR) && \ + echo `cargo --version` && echo `rustc --version`; + if [ "$(NOT_CRAN)" != "true" ]; then \ + rm -Rf $(CARGOTMP) $(VENDOR_DIR) $(LIBDIR)/build; \ + fi + +C_clean: + rm -Rf $(SHLIB) $(STATLIB) $(OBJECTS) + +clean: + rm -Rf $(SHLIB) $(STATLIB) $(OBJECTS) ./rust/target + + + + diff --git a/inst/templates/cran/Makevars.win b/inst/templates/cran/Makevars.win new file mode 100644 index 00000000..f503fc9a --- /dev/null +++ b/inst/templates/cran/Makevars.win @@ -0,0 +1,54 @@ +TARGET = $(subst 64,x86_64,$(subst 32,i686,$(WIN)))-pc-windows-gnu + +TARGET_DIR = ./rust/target +LIBDIR = $(TARGET_DIR)/$(TARGET)/release +STATLIB = $(LIBDIR)/lib{{{lib_name}}}.a +PKG_LIBS = -L$(LIBDIR) -l{{{lib_name}}} -lws2_32 -ladvapi32 -luserenv -lbcrypt -lntdll + +all: C_clean + +$(SHLIB): $(STATLIB) + +CRAN_FLAGS=-j 2 --offline +CARGOTMP = $(CURDIR)/.cargo + +all: C_clean + +$(SHLIB): $(STATLIB) + +CRAN_FLAGS=-j 2 --offline +CARGOTMP = $(CURDIR)/.cargo + +$(STATLIB): + # uncompress vendored deps + if [ -f ./rust/vendor.tar.xz ]; then \ + tar xf rust/vendor.tar.xz && \ + mkdir -p $(CARGOTMP) && \ + cp rust/vendor-config.toml $(CARGOTMP)/config.toml; \ + fi + + mkdir -p $(TARGET_DIR)/libgcc_mock + # `rustc` adds `-lgcc_eh` flags to the compiler, but Rtools' GCC doesn't have + # `libgcc_eh` due to the compilation settings. So, in order to please the + # compiler, we need to add empty `libgcc_eh` to the library search paths. + # For more details, please refer to + # https://github.com/r-windows/rtools-packages/blob/2407b23f1e0925bbb20a4162c963600105236318/mingw-w64-gcc/PKGBUILD#L313-L316 + touch $(TARGET_DIR)/libgcc_mock/libgcc_eh.a + + # CARGO_LINKER is provided in Makevars.ucrt for R >= 4.2 + if [ "$(NOT_CRAN)" != "true" ]; then \ + export CARGO_HOME=$(CARGOTMP); \ + fi && \ + export CARGO_TARGET_X86_64_PC_WINDOWS_GNU_LINKER="$(CARGO_LINKER)" && \ + export LIBRARY_PATH="$${LIBRARY_PATH};$(CURDIR)/$(TARGET_DIR)/libgcc_mock"; \ + cargo build $(CRAN_FLAGS) --target=$(TARGET) --lib --release --manifest-path=./rust/Cargo.toml --target-dir $(TARGET_DIR) && \ + echo `cargo --version` && echo `rustc --version`; + if [ "$(NOT_CRAN)" != "true" ]; then \ + rm -Rf $(CARGOTMP) $(VENDOR_DIR) $(LIBDIR)/build; \ + fi + +C_clean: + rm -Rf $(SHLIB) $(STATLIB) $(OBJECTS) + +clean: + rm -Rf $(SHLIB) $(STATLIB) $(OBJECTS) $(TARGET_DIR) ./rust/vendor $(CARGO_HOME) diff --git a/inst/templates/cran/configure b/inst/templates/cran/configure new file mode 100644 index 00000000..93bc3a4c --- /dev/null +++ b/inst/templates/cran/configure @@ -0,0 +1,21 @@ +#!/usr/bin/env sh + +# https://github.com/eitsupi/prqlr/blob/main/configure +export PATH="$PATH:$HOME/.cargo/bin" + +if [ ! "$(command -v cargo)" ]; then + echo "----------------------- [RUST NOT FOUND]---------------------------" + echo "The 'cargo' command was not found on the PATH. Please install rustc" + echo "from: https://www.rust-lang.org/tools/install" + echo "" + echo "Alternatively, you may install cargo from your OS package manager:" + echo " - Debian/Ubuntu: apt-get install cargo" + echo " - Fedora/CentOS: dnf install cargo" + echo " - macOS: brew install rustc" + echo "-------------------------------------------------------------------" + echo "" + exit 1 +fi + +exit 0 + diff --git a/inst/templates/cran/configure.win b/inst/templates/cran/configure.win new file mode 100644 index 00000000..d9b66edb --- /dev/null +++ b/inst/templates/cran/configure.win @@ -0,0 +1,15 @@ +#!/bin/sh + +# https://github.com/eitsupi/prqlr/blob/main/configure.win +export PATH="$PATH:$HOME/.cargo/bin" + +if [ ! "$(command -v cargo)" ]; then + echo "----------------------- [RUST NOT FOUND]---------------------------" + echo "The 'cargo' command was not found on the PATH. Please install rustc" + echo "from: https://www.rust-lang.org/tools/install" + echo "-------------------------------------------------------------------" + echo "" + exit 1 +fi + +exit 0 diff --git a/man/cran.Rd b/man/cran.Rd new file mode 100644 index 00000000..1d773ef4 --- /dev/null +++ b/man/cran.Rd @@ -0,0 +1,53 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/cran-compliance.R +\name{cran} +\alias{cran} +\alias{use_cran_defaults} +\alias{vendor_pkgs} +\title{Use CRAN compliant defaults} +\usage{ +use_cran_defaults(path = ".", quiet = FALSE, overwrite = NULL, lib_name = NULL) + +vendor_pkgs(path = ".", quiet = FALSE, overwrite = NULL) +} +\arguments{ +\item{path}{File path to the package for which to generate wrapper code.} + +\item{quiet}{Logical indicating whether any progress messages should be +generated or not.} + +\item{overwrite}{Logical scalar or \code{NULL} indicating whether the files in the \code{path} should be overwritten. +If \code{NULL} (default), the function will ask the user whether each file should +be overwritten in an interactive session or do nothing in a non-interactive session. +If \code{FALSE} and each file already exists, the function will do nothing. +If \code{TRUE}, all files will be overwritten.} + +\item{lib_name}{String that is used as the name of the Rust library. +If \code{NULL}, sanitized R package name is used instead.} +} +\value{ +\itemize{ +\item \code{vendor_pkgs()} returns a data.frame with two columns \code{crate} and \code{version} +\item \code{use_cran_defaults()} returns \code{NULL} and is used solely for its side effects +} +} +\description{ +Modifies an extendr package to use CRAN compliant settings. +} +\details{ +\code{use_cran_defaults()} modifies an existing package to provide CRAN complaint +settings and files. It creates \code{configure} and \code{configure.win} files as well as +modifies \code{Makevars} and \code{Makevars.win} to use required CRAN settings. + +\code{vendor_pkgs()} is used to package the dependencies as required by CRAN. +It executes \verb{cargo vendor} on your behalf creating a \verb{vendor/} directory and a +compressed \code{vendor.tar.xz} which will be shipped with package itself. +If you have modified your dependencies, you will need need to repackage +} +\examples{ + +if (interactive()) { + use_cran_defaults() + vendor_pkgs() +} +} diff --git a/tests/testthat/_snaps/use_cran_defaults.md b/tests/testthat/_snaps/use_cran_defaults.md new file mode 100644 index 00000000..8bdd3618 --- /dev/null +++ b/tests/testthat/_snaps/use_cran_defaults.md @@ -0,0 +1,200 @@ +# use_cran_defaults() modifies and creates files correctly + + Code + use_extendr() + Message + i First time using rextendr. Upgrading automatically... + i Setting `Config/rextendr/version` to "0.3.1.9000" in the 'DESCRIPTION' file. + i Setting `SystemRequirements` to "Cargo (rustc package manager)" in the 'DESCRIPTION' file. + v Creating 'src/rust/src'. + v Writing 'src/entrypoint.c' + v Writing 'src/Makevars' + v Writing 'src/Makevars.win' + v Writing 'src/Makevars.ucrt' + v Writing 'src/.gitignore' + v Adding '^src/\\.cargo$' to '.Rbuildignore' + v Writing 'src/rust/Cargo.toml' + v Writing 'src/rust/src/lib.rs' + v Writing 'src/testpkg-win.def' + v Writing 'R/extendr-wrappers.R' + v Finished configuring extendr for package testpkg. + * Please run `rextendr::document()` for changes to take effect. + +--- + + Code + use_cran_defaults() + Message + v Writing 'configure' + v Writing 'configure.win' + > File 'src/Makevars' already exists. Skip writing the file. + > File 'src/Makevars.win' already exists. Skip writing the file. + v Adding '^src/rust/vendor$' to '.Rbuildignore' + v Adding 'src/rust/vendor' to '.gitignore' + +--- + + Code + cat_file("src", "Makevars") + Output + TARGET_DIR = ./rust/target + LIBDIR = $(TARGET_DIR)/release + STATLIB = $(LIBDIR)/libtestpkg.a + PKG_LIBS = -L$(LIBDIR) -ltestpkg + + all: C_clean + + $(SHLIB): $(STATLIB) + + CARGOTMP = $(CURDIR)/.cargo + + $(STATLIB): + # In some environments, ~/.cargo/bin might not be included in PATH, so we need + # to set it here to ensure cargo can be invoked. It is appended to PATH and + # therefore is only used if cargo is absent from the user's PATH. + if [ "$(NOT_CRAN)" != "true" ]; then \ + export CARGO_HOME=$(CARGOTMP); \ + fi && \ + export PATH="$(PATH):$(HOME)/.cargo/bin" && \ + cargo build --lib --release --manifest-path=./rust/Cargo.toml --target-dir $(TARGET_DIR) + if [ "$(NOT_CRAN)" != "true" ]; then \ + rm -Rf $(CARGOTMP) && \ + rm -Rf $(LIBDIR)/build; \ + fi + + C_clean: + rm -Rf $(SHLIB) $(STATLIB) $(OBJECTS) + + clean: + rm -Rf $(SHLIB) $(STATLIB) $(OBJECTS) rust/target + +--- + + Code + cat_file("src", "Makevars.win") + Output + TARGET = $(subst 64,x86_64,$(subst 32,i686,$(WIN)))-pc-windows-gnu + + TARGET_DIR = ./rust/target + LIBDIR = $(TARGET_DIR)/$(TARGET)/release + STATLIB = $(LIBDIR)/libtestpkg.a + PKG_LIBS = -L$(LIBDIR) -ltestpkg -lws2_32 -ladvapi32 -luserenv -lbcrypt -lntdll + + all: C_clean + + $(SHLIB): $(STATLIB) + + CARGOTMP = $(CURDIR)/.cargo + + $(STATLIB): + mkdir -p $(TARGET_DIR)/libgcc_mock + # `rustc` adds `-lgcc_eh` flags to the compiler, but Rtools' GCC doesn't have + # `libgcc_eh` due to the compilation settings. So, in order to please the + # compiler, we need to add empty `libgcc_eh` to the library search paths. + # + # For more details, please refer to + # https://github.com/r-windows/rtools-packages/blob/2407b23f1e0925bbb20a4162c963600105236318/mingw-w64-gcc/PKGBUILD#L313-L316 + touch $(TARGET_DIR)/libgcc_mock/libgcc_eh.a + + # CARGO_LINKER is provided in Makevars.ucrt for R >= 4.2 + if [ "$(NOT_CRAN)" != "true" ]; then \ + export CARGO_HOME=$(CARGOTMP); \ + fi && \ + export CARGO_TARGET_X86_64_PC_WINDOWS_GNU_LINKER="$(CARGO_LINKER)" && \ + export LIBRARY_PATH="$${LIBRARY_PATH};$(CURDIR)/$(TARGET_DIR)/libgcc_mock" && \ + cargo build --target=$(TARGET) --lib --release --manifest-path=./rust/Cargo.toml --target-dir $(TARGET_DIR) + if [ "$(NOT_CRAN)" != "true" ]; then \ + rm -Rf $(CARGOTMP) && \ + rm -Rf $(LIBDIR)/build; \ + fi + + C_clean: + rm -Rf $(SHLIB) $(STATLIB) $(OBJECTS) + + clean: + rm -Rf $(SHLIB) $(STATLIB) $(OBJECTS) $(TARGET_DIR) + +--- + + Code + cat_file("configure") + Output + #!/usr/bin/env sh + + # https://github.com/eitsupi/prqlr/blob/main/configure + export PATH="$PATH:$HOME/.cargo/bin" + + if [ ! "$(command -v cargo)" ]; then + echo "----------------------- [RUST NOT FOUND]---------------------------" + echo "The 'cargo' command was not found on the PATH. Please install rustc" + echo "from: https://www.rust-lang.org/tools/install" + echo "" + echo "Alternatively, you may install cargo from your OS package manager:" + echo " - Debian/Ubuntu: apt-get install cargo" + echo " - Fedora/CentOS: dnf install cargo" + echo " - macOS: brew install rustc" + echo "-------------------------------------------------------------------" + echo "" + exit 1 + fi + + exit 0 + +--- + + Code + cat_file("configure.win") + Output + #!/bin/sh + + # https://github.com/eitsupi/prqlr/blob/main/configure.win + export PATH="$PATH:$HOME/.cargo/bin" + + if [ ! "$(command -v cargo)" ]; then + echo "----------------------- [RUST NOT FOUND]---------------------------" + echo "The 'cargo' command was not found on the PATH. Please install rustc" + echo "from: https://www.rust-lang.org/tools/install" + echo "-------------------------------------------------------------------" + echo "" + exit 1 + fi + + exit 0 + +# use_cran_defaults() quiet if quiet=TRUE + + Code + use_extendr(quiet = TRUE) + use_cran_defaults(quiet = TRUE) + +# vendor_pkgs() vendors dependencies + + Code + vendor_pkgs(path) + Output + * Updating crates.io index + * Vendoring extendr-api v0.4.0 (/Users/josiahparry/.cargo/registry/src/index.crates.io-6f17d22bba15001f/extendr-api-0.4.0) to vendor/extendr-api + * Vendoring extendr-engine v0.4.0 (/Users/josiahparry/.cargo/registry/src/index.crates.io-6f17d22bba15001f/extendr-engine-0.4.0) to vendor/extendr-engine + * Vendoring extendr-macros v0.4.0 (/Users/josiahparry/.cargo/registry/src/index.crates.io-6f17d22bba15001f/extendr-macros-0.4.0) to vendor/extendr-macros + * Vendoring lazy_static v1.4.0 (/Users/josiahparry/.cargo/registry/src/index.crates.io-6f17d22bba15001f/lazy_static-1.4.0) to vendor/lazy_static + * Vendoring libR-sys v0.4.0 (/Users/josiahparry/.cargo/registry/src/index.crates.io-6f17d22bba15001f/libR-sys-0.4.0) to vendor/libR-sys + * Vendoring paste v1.0.14 (/Users/josiahparry/.cargo/registry/src/index.crates.io-6f17d22bba15001f/paste-1.0.14) to vendor/paste + * Vendoring proc-macro2 v1.0.67 (/Users/josiahparry/.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.67) to vendor/proc-macro2 + * Vendoring quote v1.0.33 (/Users/josiahparry/.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.33) to vendor/quote + * Vendoring syn v1.0.109 (/Users/josiahparry/.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-1.0.109) to vendor/syn + * Vendoring unicode-ident v1.0.12 (/Users/josiahparry/.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.12) to vendor/unicode-ident + Message + i Writing 'src/rust/vendor-config.toml' + +--- + + Code + cat_file("src", "rust", "vendor-config.toml") + Output + [source.crates-io] + replace-with = "vendored-sources" + + [source.vendored-sources] + directory = "vendor" + + diff --git a/tests/testthat/test-use_cran_defaults.R b/tests/testthat/test-use_cran_defaults.R new file mode 100644 index 00000000..ca16e0df --- /dev/null +++ b/tests/testthat/test-use_cran_defaults.R @@ -0,0 +1,39 @@ +test_that("use_cran_defaults() modifies and creates files correctly", { + skip_if_not_installed("usethis") + + path <- local_package("testpkg") + # capture setup messages + withr::local_options(usethis.quiet = FALSE) + expect_snapshot(use_extendr()) + expect_snapshot(use_cran_defaults()) + + expect_snapshot(cat_file("src", "Makevars")) + expect_snapshot(cat_file("src", "Makevars.win")) + expect_snapshot(cat_file("configure")) + expect_snapshot(cat_file("configure.win")) +}) + +test_that("use_cran_defaults() quiet if quiet=TRUE", { + skip_if_not_installed("usethis") + + path <- local_package("quiet") + expect_snapshot({ + use_extendr(quiet = TRUE) + use_cran_defaults(quiet = TRUE) + }) +}) + + +test_that("vendor_pkgs() vendors dependencies", { + skip_if_not_installed("usethis") + + path <- local_package("testpkg") + # capture setup messages + withr::local_options(usethis.quiet = FALSE) + use_extendr(path, quiet = TRUE) + use_cran_defaults(path, quiet = TRUE) + + expect_snapshot(vendor_pkgs(path)) + expect_snapshot(cat_file("src", "rust", "vendor-config.toml")) + +}) diff --git a/vignettes/articles/cran-compliance.Rmd b/vignettes/articles/cran-compliance.Rmd new file mode 100644 index 00000000..3b6ea2be --- /dev/null +++ b/vignettes/articles/cran-compliance.Rmd @@ -0,0 +1,74 @@ +--- +title: "CRAN compliant extendr packages" +author: "Josiah Parry" +--- + +```{r, include = FALSE} +knitr::opts_chunk$set( + collapse = TRUE, + comment = "#>" +) +``` + +In order Rust based packages to exist on CRAN, there are a number of +fairly stringent requirements that must be adhered to. CRAN published [Using Rust in CRAN packages](https://cran.r-project.org/web/packages/using_rust.html) in mid-2023 outlining their requirement for building and hosting Rust based packages. + +This article describes CRAN requirements as of the day of writing and illustrates how `{rextendr}` can be used to adhere to them. + +## `SystemRequirements` + +Building Rust-backed packages from source requires the system dependencies `cargo` and `rustc`. CRAN has stipulated their preferred way of tracking this is using the following line in a packages `DESCRIPTION` file. + +``` +SystemRequirements: Cargo (Rust's package manager), rustc +``` +Even though this is a free form field, having consistency can help the whole ecosystem keep track of Rust based R packages. + +## `cargo` and `rustc` availability + +In order for an R package to be built from source, `cargo` and `rustc` need to be available to the machine compiling the package. The expectation for R packages using external dependencies is to have a `configure` and `configure.win` file to check if the dependencies are available before attempting to compile the package. If the checks fail, the build process will be stopped early. + +CRAN expects that if cargo is not on the `PATH`, the user's home directory is checked at `~/.cargo/bin`. The configuration files must perform these checks. + +## `cargo build` settings + +CRAN also places restrictions on the way that cargo builds. CRAN has requested that no more than two logical CPUs be used in the build process. By default, cargo uses multiple threads to speed up the compilation process. CRAN policy allows for a maximum of two. This is set using the `-j 2` option in cargo build. + +Additionally, to minimize security risks and ensure package stability, CRAN requires that packages be built completely offline. This prevents external dependencies from being downloaded at compile time. Because of this requirement, vendored dependencies must be used. + +## Vendored dependencies + +Vendoring dependencies is the act of including the dependency itself into a packages source code. In the case of Rust, dependencies are fetched only at compile time. To enable compilation in an offline environment, dependencies must be vendored which is accomplished using the `cargo vendor` command. + +`cargo vendor` creates a local directory, called `vendor/` by default, which contains the source code for each of the recursive dependencies of the crate that is being built. For CRAN compatibility, the `vendor/` directory must be compressed using tar xz compression and included in the source of the package. + +During build time, the dependencies are uncompressed, compiled in the offline build, and discarded. This process is controlled by the `Makevars` and `Makevars.win` files. + +## Package compilation + +All of this comes together during package compilation time. During package compilation certain behaviors must be achieved: + +- cargo must be able to be called from a user's home directory +- the user's home directory must not be modified or written to +- the package must be compiled offline +- no more than two logical CPUs are used +- the versions of `cargo` and `rustc` are printed + + +## Using CRAN defaults + +rextendr provides default CRAN compliant scaffolding via the `use_cran_defaults()` function and appropriate vendoring with `vendor_pkgs()`. + +### Making a package CRAN compliant + +To create a CRAN compliant R package begin by creating a new R package. Do so by using `usethis::create_package()`. In the new R project, run `rextendr::use_extendr()` to create the minimal scaffolding necessary for a Rust powered R package. Once you have done this, you can now run `rextendr::use_cran_defaults()`. + +`use_cran_defaults()` will create the files `configure` and `configure.win` files. Additionally, it will create new `Makevars` and `Makevars.win` that print the versions of cargo and rustc as well as uses the `cargo build` argument `-j 2 --offline`. + +### Vendoring packages + +After having configured your R package to use CRAN defaults, you will need to vendor your dependencies. + +`vendor_pkgs()` runs `cargo vendor` on your behalf, compresses the `vendor/` directory, and updates the `vendor-config.toml` file accordingly. + +When you have added new dependencies, changed the version or source of the crates, you should use `vendor_pkgs()` again. Doing so ensures that the compressed `vendor.tar.xz` contains the updates too. This is very important for CI and publishing to CRAN. From acd444c89b62dd548e5e418a4b45fc209eb35a54 Mon Sep 17 00:00:00 2001 From: Josiah Parry Date: Sat, 23 Sep 2023 10:07:15 -0400 Subject: [PATCH 02/37] add articles to build ignore --- .Rbuildignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.Rbuildignore b/.Rbuildignore index 15e32d8b..49da0119 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -17,3 +17,4 @@ ^\.idea$ ^inst/libgcc_mock/libgcc_eh.a$ ^CRAN-SUBMISSION$ +^vignettes/articles$ From 9907abdf158d40deca10d8cb547649e3010f6b4b Mon Sep 17 00:00:00 2001 From: Josiah Parry Date: Sat, 23 Sep 2023 10:11:36 -0400 Subject: [PATCH 03/37] lint --- R/cran-compliance.R | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/R/cran-compliance.R b/R/cran-compliance.R index 994c3750..c46e9ccb 100644 --- a/R/cran-compliance.R +++ b/R/cran-compliance.R @@ -155,6 +155,13 @@ vendor_pkgs <- function(path = ".", quiet = FALSE, overwrite = NULL) { }) } + if (update_res[["status"]] != 0) { + cli::cli_abort( + "{.file Cargo.lock} could not be creates with {.code cargo generate-lockfile}", + class = "rextendr_error" + ) + } + # vendor crates withr::with_dir(src_dir, { vendor_res <- processx::run( @@ -217,5 +224,3 @@ vendor_pkgs <- function(path = ".", quiet = FALSE, overwrite = NULL) { # return packages and versions invisibly invisible(res) } - - From deef23fa16c6bfb4a5b0625d1237f0ee9e878b84 Mon Sep 17 00:00:00 2001 From: Josiah Parry Date: Sat, 23 Sep 2023 15:37:39 -0400 Subject: [PATCH 04/37] address testthat snaps being different based on OS and cargo path. Now has a snapshot for the invisibly returned data frame instead. This should not change (unless order is not preserved) --- tests/testthat/_snaps/use_cran_defaults.md | 26 ++++++++++------------ tests/testthat/test-use_cran_defaults.R | 5 +++-- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/tests/testthat/_snaps/use_cran_defaults.md b/tests/testthat/_snaps/use_cran_defaults.md index 8bdd3618..8349eece 100644 --- a/tests/testthat/_snaps/use_cran_defaults.md +++ b/tests/testthat/_snaps/use_cran_defaults.md @@ -170,21 +170,19 @@ # vendor_pkgs() vendors dependencies Code - vendor_pkgs(path) + x Output - * Updating crates.io index - * Vendoring extendr-api v0.4.0 (/Users/josiahparry/.cargo/registry/src/index.crates.io-6f17d22bba15001f/extendr-api-0.4.0) to vendor/extendr-api - * Vendoring extendr-engine v0.4.0 (/Users/josiahparry/.cargo/registry/src/index.crates.io-6f17d22bba15001f/extendr-engine-0.4.0) to vendor/extendr-engine - * Vendoring extendr-macros v0.4.0 (/Users/josiahparry/.cargo/registry/src/index.crates.io-6f17d22bba15001f/extendr-macros-0.4.0) to vendor/extendr-macros - * Vendoring lazy_static v1.4.0 (/Users/josiahparry/.cargo/registry/src/index.crates.io-6f17d22bba15001f/lazy_static-1.4.0) to vendor/lazy_static - * Vendoring libR-sys v0.4.0 (/Users/josiahparry/.cargo/registry/src/index.crates.io-6f17d22bba15001f/libR-sys-0.4.0) to vendor/libR-sys - * Vendoring paste v1.0.14 (/Users/josiahparry/.cargo/registry/src/index.crates.io-6f17d22bba15001f/paste-1.0.14) to vendor/paste - * Vendoring proc-macro2 v1.0.67 (/Users/josiahparry/.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.67) to vendor/proc-macro2 - * Vendoring quote v1.0.33 (/Users/josiahparry/.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.33) to vendor/quote - * Vendoring syn v1.0.109 (/Users/josiahparry/.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-1.0.109) to vendor/syn - * Vendoring unicode-ident v1.0.12 (/Users/josiahparry/.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.12) to vendor/unicode-ident - Message - i Writing 'src/rust/vendor-config.toml' + crate version + 1 extendr-api v0.4.0 + 2 extendr-engine v0.4.0 + 3 extendr-macros v0.4.0 + 4 lazy_static v1.4.0 + 5 libR-sys v0.4.0 + 6 paste v1.0.14 + 7 proc-macro2 v1.0.67 + 8 quote v1.0.33 + 9 syn v1.0.109 + 10 unicode-ident v1.0.12 --- diff --git a/tests/testthat/test-use_cran_defaults.R b/tests/testthat/test-use_cran_defaults.R index ca16e0df..b3fdada2 100644 --- a/tests/testthat/test-use_cran_defaults.R +++ b/tests/testthat/test-use_cran_defaults.R @@ -31,9 +31,10 @@ test_that("vendor_pkgs() vendors dependencies", { # capture setup messages withr::local_options(usethis.quiet = FALSE) use_extendr(path, quiet = TRUE) - use_cran_defaults(path, quiet = TRUE) + use_cran_defaults(path, quiet = TRUE, overwrite = TRUE) - expect_snapshot(vendor_pkgs(path)) + x <- vendor_pkgs(path, quiet = TRUE) + expect_snapshot(x) expect_snapshot(cat_file("src", "rust", "vendor-config.toml")) }) From f6925d4ed0a58b3831923e381cb89e1962995886 Mon Sep 17 00:00:00 2001 From: Josiah Parry Date: Sat, 23 Sep 2023 15:37:55 -0400 Subject: [PATCH 05/37] address lintr --- R/cran-compliance.R | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/R/cran-compliance.R b/R/cran-compliance.R index c46e9ccb..5d66fdbc 100644 --- a/R/cran-compliance.R +++ b/R/cran-compliance.R @@ -28,12 +28,7 @@ #' } #' @name cran #' @export -use_cran_defaults <- function( - path = ".", - quiet = FALSE, - overwrite = NULL, - lib_name = NULL -) { +use_cran_defaults <- function(path = ".", quiet = FALSE, overwrite = NULL, lib_name = NULL) { # if not in an interactive session and overwrite is null, set it to false if (!rlang::is_interactive()) { From aca8222b670ac7ebab92e14ca01ed7c4c773a12c Mon Sep 17 00:00:00 2001 From: Josiah Parry Date: Sat, 23 Sep 2023 15:38:11 -0400 Subject: [PATCH 06/37] resolve pkgdown CI --- _pkgdown.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/_pkgdown.yml b/_pkgdown.yml index 6a6cff6b..dabd49fe 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -20,6 +20,7 @@ reference: - register_extendr - write_license_note - clean + - cran - title: Various utility functions contents: From 2caf23b1d9b57de228676d146297f49e0c436c53 Mon Sep 17 00:00:00 2001 From: Josiah Parry Date: Sun, 24 Sep 2023 07:43:53 -0400 Subject: [PATCH 07/37] address CI lintr --- R/cran-compliance.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/cran-compliance.R b/R/cran-compliance.R index 5d66fdbc..73041e92 100644 --- a/R/cran-compliance.R +++ b/R/cran-compliance.R @@ -140,7 +140,7 @@ vendor_pkgs <- function(path = ".", quiet = FALSE, overwrite = NULL) { "generate-lockfile", "--manifest-path", file.path(src_dir, "Cargo.toml") - ), + ), stderr_line_callback = function(x, proc) { if (!grepl("To use vendored sources", x) && x != "") { cli::cat_bullet(stringi::stri_trim_left(x)) From e79b1798eadab1caabd76a7079cf2cbbf68cbf18 Mon Sep 17 00:00:00 2001 From: Josiah Parry Date: Sun, 24 Sep 2023 07:44:32 -0400 Subject: [PATCH 08/37] Update R/cran-compliance.R Co-authored-by: Ilia Kosenkov --- R/cran-compliance.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/cran-compliance.R b/R/cran-compliance.R index 73041e92..f0f211ec 100644 --- a/R/cran-compliance.R +++ b/R/cran-compliance.R @@ -90,7 +90,7 @@ use_cran_defaults <- function(path = ".", quiet = FALSE, overwrite = NULL, lib_n data = list(lib_name = lib_name) ) - # vendor directory will need to be ignored by git and R CMD build + # vendor directory should be ignored by git and R CMD build if (!rlang::is_installed("usethis")) { cli::cli_inform( c( From 2d79a313f101970eec760029dbbc28f9b710a8cf Mon Sep 17 00:00:00 2001 From: Josiah Parry Date: Sun, 24 Sep 2023 07:44:39 -0400 Subject: [PATCH 09/37] Update R/cran-compliance.R Co-authored-by: Ilia Kosenkov --- R/cran-compliance.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/cran-compliance.R b/R/cran-compliance.R index f0f211ec..90f81c56 100644 --- a/R/cran-compliance.R +++ b/R/cran-compliance.R @@ -100,7 +100,7 @@ use_cran_defaults <- function(path = ".", quiet = FALSE, overwrite = NULL, lib_n ) ) } else { - # vendor will be big when expanded and should be ignored + # vendor folder will be large when expanded and should be ignored usethis::use_build_ignore( file.path("src", "rust", "vendor") ) From e0e3d18e751e3a398b1036eb50ba0307adb59ec8 Mon Sep 17 00:00:00 2001 From: Josiah Parry Date: Sun, 24 Sep 2023 07:44:46 -0400 Subject: [PATCH 10/37] Update R/cran-compliance.R Co-authored-by: Ilia Kosenkov --- R/cran-compliance.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/cran-compliance.R b/R/cran-compliance.R index 90f81c56..7f9b7748 100644 --- a/R/cran-compliance.R +++ b/R/cran-compliance.R @@ -124,7 +124,7 @@ vendor_pkgs <- function(path = ".", quiet = FALSE, overwrite = NULL) { # if `src/rust` does not exist error if (!dir.exists(src_dir)) { cli::cli_abort( - c("{.path src/rust} cannot be found", "i" = "Have you used {.fn use_extendr}?"), + c("{.path src/rust} cannot be found", "i" = "Did you run {.fn use_extendr}?"), class = "rextendr_error" ) } From 2ac41ebc7efb2d8a2aa17b20a9d1a6b87bad219a Mon Sep 17 00:00:00 2001 From: Josiah Parry Date: Sun, 24 Sep 2023 07:45:00 -0400 Subject: [PATCH 11/37] Update R/cran-compliance.R Co-authored-by: Ilia Kosenkov --- R/cran-compliance.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/cran-compliance.R b/R/cran-compliance.R index 7f9b7748..071aa986 100644 --- a/R/cran-compliance.R +++ b/R/cran-compliance.R @@ -152,7 +152,7 @@ vendor_pkgs <- function(path = ".", quiet = FALSE, overwrite = NULL) { if (update_res[["status"]] != 0) { cli::cli_abort( - "{.file Cargo.lock} could not be creates with {.code cargo generate-lockfile}", + "{.file Cargo.lock} could not be created using {.code cargo generate-lockfile}", class = "rextendr_error" ) } From 38d04053a46e06af1dd6818b9025dbf257665e8f Mon Sep 17 00:00:00 2001 From: Josiah Parry Date: Sun, 24 Sep 2023 07:45:07 -0400 Subject: [PATCH 12/37] Update vignettes/articles/cran-compliance.Rmd Co-authored-by: Ilia Kosenkov --- vignettes/articles/cran-compliance.Rmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vignettes/articles/cran-compliance.Rmd b/vignettes/articles/cran-compliance.Rmd index 3b6ea2be..98952a4f 100644 --- a/vignettes/articles/cran-compliance.Rmd +++ b/vignettes/articles/cran-compliance.Rmd @@ -40,7 +40,7 @@ Additionally, to minimize security risks and ensure package stability, CRAN requ Vendoring dependencies is the act of including the dependency itself into a packages source code. In the case of Rust, dependencies are fetched only at compile time. To enable compilation in an offline environment, dependencies must be vendored which is accomplished using the `cargo vendor` command. -`cargo vendor` creates a local directory, called `vendor/` by default, which contains the source code for each of the recursive dependencies of the crate that is being built. For CRAN compatibility, the `vendor/` directory must be compressed using tar xz compression and included in the source of the package. +`cargo vendor` creates a local directory with the default name `vendor`, which contains the source code for each of the recursive dependencies of the crate that is being built. For CRAN compatibility, the `vendor` directory must be compressed using tar xz compression and included in the source of the package. During build time, the dependencies are uncompressed, compiled in the offline build, and discarded. This process is controlled by the `Makevars` and `Makevars.win` files. From d4dab5c2cba3970cfbd7c94f80a77bd1ab609a20 Mon Sep 17 00:00:00 2001 From: Josiah Parry Date: Sun, 24 Sep 2023 07:47:14 -0400 Subject: [PATCH 13/37] Update vignettes/articles/cran-compliance.Rmd Co-authored-by: Ilia Kosenkov --- vignettes/articles/cran-compliance.Rmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vignettes/articles/cran-compliance.Rmd b/vignettes/articles/cran-compliance.Rmd index 98952a4f..ac02f7e7 100644 --- a/vignettes/articles/cran-compliance.Rmd +++ b/vignettes/articles/cran-compliance.Rmd @@ -10,7 +10,7 @@ knitr::opts_chunk$set( ) ``` -In order Rust based packages to exist on CRAN, there are a number of +In order for Rust-based packages to exist on CRAN, there are a number of fairly stringent requirements that must be adhered to. CRAN published [Using Rust in CRAN packages](https://cran.r-project.org/web/packages/using_rust.html) in mid-2023 outlining their requirement for building and hosting Rust based packages. This article describes CRAN requirements as of the day of writing and illustrates how `{rextendr}` can be used to adhere to them. From b4b387e7537ea3f6a4090314444ea8d34111a84b Mon Sep 17 00:00:00 2001 From: Josiah Parry Date: Sun, 24 Sep 2023 07:47:20 -0400 Subject: [PATCH 14/37] Update vignettes/articles/cran-compliance.Rmd Co-authored-by: Ilia Kosenkov --- vignettes/articles/cran-compliance.Rmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vignettes/articles/cran-compliance.Rmd b/vignettes/articles/cran-compliance.Rmd index ac02f7e7..50a0d35e 100644 --- a/vignettes/articles/cran-compliance.Rmd +++ b/vignettes/articles/cran-compliance.Rmd @@ -42,7 +42,7 @@ Vendoring dependencies is the act of including the dependency itself into a pack `cargo vendor` creates a local directory with the default name `vendor`, which contains the source code for each of the recursive dependencies of the crate that is being built. For CRAN compatibility, the `vendor` directory must be compressed using tar xz compression and included in the source of the package. -During build time, the dependencies are uncompressed, compiled in the offline build, and discarded. This process is controlled by the `Makevars` and `Makevars.win` files. +During the build time, the dependencies are extracted, compiled, and then discarded. This process is controlled by the `Makevars` and `Makevars.win` files. ## Package compilation From 0192b84a0f730eaac331888dbd87fee701b46a52 Mon Sep 17 00:00:00 2001 From: Josiah Parry Date: Sun, 24 Sep 2023 07:47:29 -0400 Subject: [PATCH 15/37] Update vignettes/articles/cran-compliance.Rmd Co-authored-by: Ilia Kosenkov --- vignettes/articles/cran-compliance.Rmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vignettes/articles/cran-compliance.Rmd b/vignettes/articles/cran-compliance.Rmd index 50a0d35e..81a1789c 100644 --- a/vignettes/articles/cran-compliance.Rmd +++ b/vignettes/articles/cran-compliance.Rmd @@ -61,7 +61,7 @@ rextendr provides default CRAN compliant scaffolding via the `use_cran_defaults( ### Making a package CRAN compliant -To create a CRAN compliant R package begin by creating a new R package. Do so by using `usethis::create_package()`. In the new R project, run `rextendr::use_extendr()` to create the minimal scaffolding necessary for a Rust powered R package. Once you have done this, you can now run `rextendr::use_cran_defaults()`. +To create a CRAN compliant R package begin by creating a new R package. Do so by calling `usethis::create_package()`. In the new R project, run `rextendr::use_extendr()` to create the minimal scaffolding necessary for a Rust-powered R package. Once you have done this, you can now run `rextendr::use_cran_defaults()`. `use_cran_defaults()` will create the files `configure` and `configure.win` files. Additionally, it will create new `Makevars` and `Makevars.win` that print the versions of cargo and rustc as well as uses the `cargo build` argument `-j 2 --offline`. From eda78097f9fa33f7e16f6a7759bc84f28fc154d3 Mon Sep 17 00:00:00 2001 From: Josiah Parry Date: Sun, 24 Sep 2023 07:47:44 -0400 Subject: [PATCH 16/37] Update vignettes/articles/cran-compliance.Rmd Co-authored-by: Ilia Kosenkov --- vignettes/articles/cran-compliance.Rmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vignettes/articles/cran-compliance.Rmd b/vignettes/articles/cran-compliance.Rmd index 81a1789c..fee22c81 100644 --- a/vignettes/articles/cran-compliance.Rmd +++ b/vignettes/articles/cran-compliance.Rmd @@ -46,7 +46,7 @@ During the build time, the dependencies are extracted, compiled, and then discar ## Package compilation -All of this comes together during package compilation time. During package compilation certain behaviors must be achieved: +All of this comes together during package compilation time, providing all of the following requirements are met: - cargo must be able to be called from a user's home directory - the user's home directory must not be modified or written to From e54dc06551692316cb2be5c201498d417ee8998a Mon Sep 17 00:00:00 2001 From: Josiah Parry Date: Sun, 24 Sep 2023 07:47:53 -0400 Subject: [PATCH 17/37] Update vignettes/articles/cran-compliance.Rmd Co-authored-by: Ilia Kosenkov --- vignettes/articles/cran-compliance.Rmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vignettes/articles/cran-compliance.Rmd b/vignettes/articles/cran-compliance.Rmd index fee22c81..b76b3344 100644 --- a/vignettes/articles/cran-compliance.Rmd +++ b/vignettes/articles/cran-compliance.Rmd @@ -63,7 +63,7 @@ rextendr provides default CRAN compliant scaffolding via the `use_cran_defaults( To create a CRAN compliant R package begin by creating a new R package. Do so by calling `usethis::create_package()`. In the new R project, run `rextendr::use_extendr()` to create the minimal scaffolding necessary for a Rust-powered R package. Once you have done this, you can now run `rextendr::use_cran_defaults()`. -`use_cran_defaults()` will create the files `configure` and `configure.win` files. Additionally, it will create new `Makevars` and `Makevars.win` that print the versions of cargo and rustc as well as uses the `cargo build` argument `-j 2 --offline`. +`use_cran_defaults()` will create the `configure` and `configure.win` files. Additionally, it will create new `Makevars` and `Makevars.win` that print the versions of `cargo` and `rustc` as well as use the `cargo build` argument `-j 2 --offline`. ### Vendoring packages From 6f39904794bc7ee678dc3862fc7966961f9da4de Mon Sep 17 00:00:00 2001 From: Josiah Parry Date: Sun, 24 Sep 2023 08:07:37 -0400 Subject: [PATCH 18/37] Update R/cran-compliance.R Co-authored-by: Ilia Kosenkov --- R/cran-compliance.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/cran-compliance.R b/R/cran-compliance.R index 071aa986..93ccd45b 100644 --- a/R/cran-compliance.R +++ b/R/cran-compliance.R @@ -177,7 +177,7 @@ vendor_pkgs <- function(path = ".", quiet = FALSE, overwrite = NULL) { if (vendor_res[["status"]] != 0) { cli::cli_abort( - "{.code cargo vendor} was not executed successfully", + "{.code cargo vendor} failed", class = "rextendr_error" ) } From d99ba28490eb87c407841a04b741346af28aa26a Mon Sep 17 00:00:00 2001 From: Josiah Parry Date: Sun, 24 Sep 2023 09:39:49 -0400 Subject: [PATCH 19/37] Apply suggestions from code review Co-authored-by: Ilia Kosenkov --- vignettes/articles/cran-compliance.Rmd | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/vignettes/articles/cran-compliance.Rmd b/vignettes/articles/cran-compliance.Rmd index b76b3344..ba43272b 100644 --- a/vignettes/articles/cran-compliance.Rmd +++ b/vignettes/articles/cran-compliance.Rmd @@ -11,7 +11,7 @@ knitr::opts_chunk$set( ``` In order for Rust-based packages to exist on CRAN, there are a number of -fairly stringent requirements that must be adhered to. CRAN published [Using Rust in CRAN packages](https://cran.r-project.org/web/packages/using_rust.html) in mid-2023 outlining their requirement for building and hosting Rust based packages. +fairly stringent requirements that must be adhered to. CRAN published [Using Rust in CRAN packages](https://cran.r-project.org/web/packages/using_rust.html) in mid-2023, outlining their requirements for building and hosting Rust-based packages. This article describes CRAN requirements as of the day of writing and illustrates how `{rextendr}` can be used to adhere to them. @@ -22,23 +22,23 @@ Building Rust-backed packages from source requires the system dependencies `carg ``` SystemRequirements: Cargo (Rust's package manager), rustc ``` -Even though this is a free form field, having consistency can help the whole ecosystem keep track of Rust based R packages. +Even though this is a free-form field, having consistency can help the whole ecosystem keep track of Rust-based R packages. ## `cargo` and `rustc` availability -In order for an R package to be built from source, `cargo` and `rustc` need to be available to the machine compiling the package. The expectation for R packages using external dependencies is to have a `configure` and `configure.win` file to check if the dependencies are available before attempting to compile the package. If the checks fail, the build process will be stopped early. +In order for an R package to be built from source, `cargo` and `rustc` need to be available to the machine compiling the package. The expectation for R packages using external dependencies is to have a `configure` and `configure.win` files that check if the dependencies are available before attempting to compile the package. If the checks fail, the build process will be stopped prematurely. -CRAN expects that if cargo is not on the `PATH`, the user's home directory is checked at `~/.cargo/bin`. The configuration files must perform these checks. +CRAN expects that if `cargo` is not on the `PATH`, the user's home directory is checked at `~/.cargo/bin`. The configuration files must perform these checks. ## `cargo build` settings -CRAN also places restrictions on the way that cargo builds. CRAN has requested that no more than two logical CPUs be used in the build process. By default, cargo uses multiple threads to speed up the compilation process. CRAN policy allows for a maximum of two. This is set using the `-j 2` option in cargo build. +CRAN also imposes restrictions on how `cargo` builds crates. CRAN has requested that no more than two logical CPUs be used in the build process. By default, `cargo` uses multiple threads to speed up the compilation process. CRAN policy allows for a maximum of two. This is set using the `-j 2` option, which is passed to `cargo build`. Additionally, to minimize security risks and ensure package stability, CRAN requires that packages be built completely offline. This prevents external dependencies from being downloaded at compile time. Because of this requirement, vendored dependencies must be used. ## Vendored dependencies -Vendoring dependencies is the act of including the dependency itself into a packages source code. In the case of Rust, dependencies are fetched only at compile time. To enable compilation in an offline environment, dependencies must be vendored which is accomplished using the `cargo vendor` command. +Vendoring dependencies is the act of including the dependency itself in a package source code. In the case of Rust, dependencies are fetched only at compile time. To enable compilation in an offline environment, dependencies must be vendored, which is accomplished using the `cargo vendor` command. `cargo vendor` creates a local directory with the default name `vendor`, which contains the source code for each of the recursive dependencies of the crate that is being built. For CRAN compatibility, the `vendor` directory must be compressed using tar xz compression and included in the source of the package. From 071efd82389ffb4a57f1baa24141e4103a3785a7 Mon Sep 17 00:00:00 2001 From: Josiah Parry Date: Sun, 24 Sep 2023 10:08:44 -0400 Subject: [PATCH 20/37] explicitly print data frame for tests --- tests/testthat/test-use_cran_defaults.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/testthat/test-use_cran_defaults.R b/tests/testthat/test-use_cran_defaults.R index b3fdada2..725e841a 100644 --- a/tests/testthat/test-use_cran_defaults.R +++ b/tests/testthat/test-use_cran_defaults.R @@ -34,7 +34,7 @@ test_that("vendor_pkgs() vendors dependencies", { use_cran_defaults(path, quiet = TRUE, overwrite = TRUE) x <- vendor_pkgs(path, quiet = TRUE) - expect_snapshot(x) + expect_snapshot(print(x)) expect_snapshot(cat_file("src", "rust", "vendor-config.toml")) }) From 4dbd2de939d78e464e058d0b30feb595b6f0b07f Mon Sep 17 00:00:00 2001 From: Josiah Parry Date: Sun, 24 Sep 2023 10:39:07 -0400 Subject: [PATCH 21/37] ensure update_res only exists when cargo_lock_fp file does not --- R/cran-compliance.R | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/R/cran-compliance.R b/R/cran-compliance.R index 93ccd45b..b8de8f11 100644 --- a/R/cran-compliance.R +++ b/R/cran-compliance.R @@ -148,14 +148,15 @@ vendor_pkgs <- function(path = ".", quiet = FALSE, overwrite = NULL) { } ) }) + if (update_res[["status"]] != 0) { + cli::cli_abort( + "{.file Cargo.lock} could not be created using {.code cargo generate-lockfile}", + class = "rextendr_error" + ) + } } - if (update_res[["status"]] != 0) { - cli::cli_abort( - "{.file Cargo.lock} could not be created using {.code cargo generate-lockfile}", - class = "rextendr_error" - ) - } + # vendor crates withr::with_dir(src_dir, { From 552a99a2c0467b2839e63a2ddc964e977852e115 Mon Sep 17 00:00:00 2001 From: Ilia Kosenkov Date: Sun, 24 Sep 2023 19:31:13 +0300 Subject: [PATCH 22/37] Fix how crate versions are detected --- R/cran-compliance.R | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/R/cran-compliance.R b/R/cran-compliance.R index b8de8f11..05302d68 100644 --- a/R/cran-compliance.R +++ b/R/cran-compliance.R @@ -29,7 +29,6 @@ #' @name cran #' @export use_cran_defaults <- function(path = ".", quiet = FALSE, overwrite = NULL, lib_name = NULL) { - # if not in an interactive session and overwrite is null, set it to false if (!rlang::is_interactive()) { overwrite <- overwrite %||% FALSE @@ -208,14 +207,12 @@ vendor_pkgs <- function(path = ".", quiet = FALSE, overwrite = NULL) { } # create a dataframe of vendored crates - vendored <- stringi::stri_split(vendor_res[["stderr"]], coll = "\n")[[1]] - trimmed <- stringi::stri_trim_left(vendored) - to_remove <- grepl("To use vendored sources", trimmed) | trimmed == "" - rows <- stringi::stri_split_fixed(trimmed[!to_remove], pattern = " ") - - res <- purrr::map_dfr(rows, function(x) { - data.frame(crate = x[2], version = x[3]) - }) + vendored <- stringi::stri_split_lines1(vendor_res[["stderr"]]) + res <- stringi::stri_match_first_regex(vendored, "Vendoring\\s([A-z0-9_][A-z0-9_-]*?)\\s[vV](.+?)(?=\\s)") %>% + tibble::as_tibble(.name_repair = "minimal") %>% + rlang::set_names(c("source", "crate", "version")) %>% + dplyr::filter(!is.na(source)) %>% + dplyr::select(-source) # return packages and versions invisibly invisible(res) From 27f6bfda6ea2e745c39a7bdaf0164e62546b1106 Mon Sep 17 00:00:00 2001 From: Ilia Kosenkov Date: Sun, 24 Sep 2023 19:32:20 +0300 Subject: [PATCH 23/37] Execute `{styler}` over package --- R/features.R | 10 ++++++---- README.Rmd | 7 +++---- tests/testthat/test-use_cran_defaults.R | 1 - 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/R/features.R b/R/features.R index 8a32f21e..e4552398 100644 --- a/R/features.R +++ b/R/features.R @@ -6,10 +6,12 @@ validate_extendr_features <- function(features, suppress_warnings) { features <- features %||% character(0) if (!vctrs::vec_is(features, character())) { - cli::cli_abort(c( - "!" = "{.arg features} expected to be a vector of type {.cls character}, but got {.cls {class(features)}}." - ), - class = "rextendr_error") + cli::cli_abort( + c( + "!" = "{.arg features} expected to be a vector of type {.cls character}, but got {.cls {class(features)}}." + ), + class = "rextendr_error" + ) } features <- unique(features) diff --git a/README.Rmd b/README.Rmd index af2cb1e2..9effa1ae 100644 --- a/README.Rmd +++ b/README.Rmd @@ -41,7 +41,7 @@ remotes::install_cran("rextendr") You can also install `{rextendr}` from [r-universe](https://extendr.r-universe.dev/rextendr): ```{r, results = "hide"} -install.packages('rextendr', repos = c('https://extendr.r-universe.dev', 'https://cloud.r-project.org')) +install.packages("rextendr", repos = c("https://extendr.r-universe.dev", "https://cloud.r-project.org")) ``` Latest development version can be installed from GitHub: @@ -80,8 +80,8 @@ rust_function( Either::Right(x) => Either::Right(x.iter().sum()), } }", - use_dev_extendr = TRUE, # Use development version of extendr from GitHub - features = "either", # Enable support for Either crate + use_dev_extendr = TRUE, # Use development version of extendr from GitHub + features = "either", # Enable support for Either crate extendr_fn_options = list(use_try_from = TRUE) # Enable advanced type conversion ) @@ -96,7 +96,6 @@ tibble::tibble( SumRaw = purrr::flatten_dbl(Sum), ResultType = purrr::map_chr(Sum, typeof) ) - ``` The package also enables a new chunk type for knitr, `extendr`, which compiles and evaluates Rust code. For example, a code chunk such as this one: diff --git a/tests/testthat/test-use_cran_defaults.R b/tests/testthat/test-use_cran_defaults.R index 725e841a..6c29a78f 100644 --- a/tests/testthat/test-use_cran_defaults.R +++ b/tests/testthat/test-use_cran_defaults.R @@ -36,5 +36,4 @@ test_that("vendor_pkgs() vendors dependencies", { x <- vendor_pkgs(path, quiet = TRUE) expect_snapshot(print(x)) expect_snapshot(cat_file("src", "rust", "vendor-config.toml")) - }) From f60f55e4c51dfc2beba13af05ae277970d1aaa5c Mon Sep 17 00:00:00 2001 From: Ilia Kosenkov Date: Sun, 24 Sep 2023 19:35:12 +0300 Subject: [PATCH 24/37] Update snapshots --- .../testthat/_snaps/unix/helper-functions.md | 25 ------------------ tests/testthat/_snaps/use_cran_defaults.md | 26 ++++++++++--------- .../_snaps/windows/helper-functions.md | 25 ------------------ 3 files changed, 14 insertions(+), 62 deletions(-) delete mode 100644 tests/testthat/_snaps/unix/helper-functions.md delete mode 100644 tests/testthat/_snaps/windows/helper-functions.md diff --git a/tests/testthat/_snaps/unix/helper-functions.md b/tests/testthat/_snaps/unix/helper-functions.md deleted file mode 100644 index 4ca85585..00000000 --- a/tests/testthat/_snaps/unix/helper-functions.md +++ /dev/null @@ -1,25 +0,0 @@ -# `ui_throw()` called by `rust_function()` captures Rust compilation errors - - Rust code could not be compiled successfully. Aborting. - x error[E0412]: cannot find type `i33` in this scope - --> src/lib.rs:3:19 - | - 3 | fn failed_fn(_x : i33, _y : i34, _z : i35) -> f100 { false } - | ^^^ help: a builtin type with a similar name exists: `i32` - x error[E0412]: cannot find type `i34` in this scope - --> src/lib.rs:3:29 - | - 3 | fn failed_fn(_x : i33, _y : i34, _z : i35) -> f100 { false } - | ^^^ help: a builtin type with a similar name exists: `i32` - x error[E0412]: cannot find type `i35` in this scope - --> src/lib.rs:3:39 - | - 3 | fn failed_fn(_x : i33, _y : i34, _z : i35) -> f100 { false } - | ^^^ help: a builtin type with a similar name exists: `i32` - x error[E0412]: cannot find type `f100` in this scope - --> src/lib.rs:3:47 - | - 3 | fn failed_fn(_x : i33, _y : i34, _z : i35) -> f100 { false } - | ^^^^ not found in this scope - x error: aborting due to 4 previous errors - diff --git a/tests/testthat/_snaps/use_cran_defaults.md b/tests/testthat/_snaps/use_cran_defaults.md index 8349eece..8005d21b 100644 --- a/tests/testthat/_snaps/use_cran_defaults.md +++ b/tests/testthat/_snaps/use_cran_defaults.md @@ -170,19 +170,21 @@ # vendor_pkgs() vendors dependencies Code - x + print(x) Output - crate version - 1 extendr-api v0.4.0 - 2 extendr-engine v0.4.0 - 3 extendr-macros v0.4.0 - 4 lazy_static v1.4.0 - 5 libR-sys v0.4.0 - 6 paste v1.0.14 - 7 proc-macro2 v1.0.67 - 8 quote v1.0.33 - 9 syn v1.0.109 - 10 unicode-ident v1.0.12 + # A tibble: 10 x 2 + crate version + + 1 extendr-api 0.4.0 + 2 extendr-engine 0.4.0 + 3 extendr-macros 0.4.0 + 4 lazy_static 1.4.0 + 5 libR-sys 0.4.0 + 6 paste 1.0.14 + 7 proc-macro2 1.0.67 + 8 quote 1.0.33 + 9 syn 1.0.109 + 10 unicode-ident 1.0.12 --- diff --git a/tests/testthat/_snaps/windows/helper-functions.md b/tests/testthat/_snaps/windows/helper-functions.md deleted file mode 100644 index 65bd3630..00000000 --- a/tests/testthat/_snaps/windows/helper-functions.md +++ /dev/null @@ -1,25 +0,0 @@ -# `ui_throw()` called by `rust_function()` captures Rust compilation errors - - Rust code could not be compiled successfully. Aborting. - x error[E0412]: cannot find type `i33` in this scope - --> src\lib.rs:3:19 - | - 3 | fn failed_fn(_x : i33, _y : i34, _z : i35) -> f100 { false } - | ^^^ help: a builtin type with a similar name exists: `i32` - x error[E0412]: cannot find type `i34` in this scope - --> src\lib.rs:3:29 - | - 3 | fn failed_fn(_x : i33, _y : i34, _z : i35) -> f100 { false } - | ^^^ help: a builtin type with a similar name exists: `i32` - x error[E0412]: cannot find type `i35` in this scope - --> src\lib.rs:3:39 - | - 3 | fn failed_fn(_x : i33, _y : i34, _z : i35) -> f100 { false } - | ^^^ help: a builtin type with a similar name exists: `i32` - x error[E0412]: cannot find type `f100` in this scope - --> src\lib.rs:3:47 - | - 3 | fn failed_fn(_x : i33, _y : i34, _z : i35) -> f100 { false } - | ^^^^ not found in this scope - x error: aborting due to 4 previous errors - From 013854a020c226ff71877f6c97d5cbd34aef8fd6 Mon Sep 17 00:00:00 2001 From: Ilia Kosenkov Date: Sun, 24 Sep 2023 20:14:42 +0300 Subject: [PATCH 25/37] Don't use `print()` in tests --- tests/testthat/_snaps/use_cran_defaults.md | 2 +- tests/testthat/test-use_cran_defaults.R | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/testthat/_snaps/use_cran_defaults.md b/tests/testthat/_snaps/use_cran_defaults.md index 8005d21b..1410bcb0 100644 --- a/tests/testthat/_snaps/use_cran_defaults.md +++ b/tests/testthat/_snaps/use_cran_defaults.md @@ -170,7 +170,7 @@ # vendor_pkgs() vendors dependencies Code - print(x) + x Output # A tibble: 10 x 2 crate version diff --git a/tests/testthat/test-use_cran_defaults.R b/tests/testthat/test-use_cran_defaults.R index 6c29a78f..a816963a 100644 --- a/tests/testthat/test-use_cran_defaults.R +++ b/tests/testthat/test-use_cran_defaults.R @@ -34,6 +34,6 @@ test_that("vendor_pkgs() vendors dependencies", { use_cran_defaults(path, quiet = TRUE, overwrite = TRUE) x <- vendor_pkgs(path, quiet = TRUE) - expect_snapshot(print(x)) + expect_snapshot(x) expect_snapshot(cat_file("src", "rust", "vendor-config.toml")) }) From 0370e2066e750a6db2ad2c9cab030247de13aad0 Mon Sep 17 00:00:00 2001 From: Ilia Kosenkov Date: Sun, 24 Sep 2023 20:15:38 +0300 Subject: [PATCH 26/37] Reorder asserts --- tests/testthat/_snaps/use_cran_defaults.md | 26 +++++++++++----------- tests/testthat/test-use_cran_defaults.R | 4 ++-- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/tests/testthat/_snaps/use_cran_defaults.md b/tests/testthat/_snaps/use_cran_defaults.md index 1410bcb0..040fd11e 100644 --- a/tests/testthat/_snaps/use_cran_defaults.md +++ b/tests/testthat/_snaps/use_cran_defaults.md @@ -170,7 +170,19 @@ # vendor_pkgs() vendors dependencies Code - x + cat_file("src", "rust", "vendor-config.toml") + Output + [source.crates-io] + replace-with = "vendored-sources" + + [source.vendored-sources] + directory = "vendor" + + +--- + + Code + package_versions Output # A tibble: 10 x 2 crate version @@ -186,15 +198,3 @@ 9 syn 1.0.109 10 unicode-ident 1.0.12 ---- - - Code - cat_file("src", "rust", "vendor-config.toml") - Output - [source.crates-io] - replace-with = "vendored-sources" - - [source.vendored-sources] - directory = "vendor" - - diff --git a/tests/testthat/test-use_cran_defaults.R b/tests/testthat/test-use_cran_defaults.R index a816963a..f8082a40 100644 --- a/tests/testthat/test-use_cran_defaults.R +++ b/tests/testthat/test-use_cran_defaults.R @@ -33,7 +33,7 @@ test_that("vendor_pkgs() vendors dependencies", { use_extendr(path, quiet = TRUE) use_cran_defaults(path, quiet = TRUE, overwrite = TRUE) - x <- vendor_pkgs(path, quiet = TRUE) - expect_snapshot(x) + package_versions <- vendor_pkgs(path, quiet = TRUE) expect_snapshot(cat_file("src", "rust", "vendor-config.toml")) + expect_snapshot(package_versions) }) From 4f391ce9d7867e2409ddd56abe3c9ae92799eb70 Mon Sep 17 00:00:00 2001 From: Ilia Kosenkov Date: Tue, 26 Sep 2023 00:09:13 +0300 Subject: [PATCH 27/37] Strip ANSI symbols --- R/cran-compliance.R | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/R/cran-compliance.R b/R/cran-compliance.R index 05302d68..0a7db90a 100644 --- a/R/cran-compliance.R +++ b/R/cran-compliance.R @@ -147,6 +147,7 @@ vendor_pkgs <- function(path = ".", quiet = FALSE, overwrite = NULL) { } ) }) + if (update_res[["status"]] != 0) { cli::cli_abort( "{.file Cargo.lock} could not be created using {.code cargo generate-lockfile}", @@ -155,8 +156,6 @@ vendor_pkgs <- function(path = ".", quiet = FALSE, overwrite = NULL) { } } - - # vendor crates withr::with_dir(src_dir, { vendor_res <- processx::run( @@ -182,8 +181,21 @@ vendor_pkgs <- function(path = ".", quiet = FALSE, overwrite = NULL) { ) } + # create a dataframe of vendored crates + vendored <- vendor_res[["stderr"]] %>% + cli::ansi_strip() %>% + stringi::stri_split_lines1() + + res <- stringi::stri_match_first_regex(vendored, "Vendoring\\s([A-z0-9_][A-z0-9_-]*?)\\s[vV](.+?)(?=\\s)") %>% + tibble::as_tibble(.name_repair = "minimal") %>% + rlang::set_names(c("source", "crate", "version")) %>% + dplyr::filter(!is.na(source)) %>% + dplyr::select(-source) + # capture vendor-config.toml content - config_toml <- stringi::stri_split(vendor_res$stdout, coll = "\n")[[1]] + config_toml <- vendor_res[["stdout"]] %>% + cli::ansi_strip() %>% + stringi::stri_split_lines1() # always write to file as cargo vendor catches things like patch.crates-io # and provides the appropriate configuration. @@ -206,14 +218,6 @@ vendor_pkgs <- function(path = ".", quiet = FALSE, overwrite = NULL) { ) } - # create a dataframe of vendored crates - vendored <- stringi::stri_split_lines1(vendor_res[["stderr"]]) - res <- stringi::stri_match_first_regex(vendored, "Vendoring\\s([A-z0-9_][A-z0-9_-]*?)\\s[vV](.+?)(?=\\s)") %>% - tibble::as_tibble(.name_repair = "minimal") %>% - rlang::set_names(c("source", "crate", "version")) %>% - dplyr::filter(!is.na(source)) %>% - dplyr::select(-source) - # return packages and versions invisibly invisible(res) } From b9cddfe8dbe3b38a5454005e46ab76bb856420e0 Mon Sep 17 00:00:00 2001 From: Ilia Kosenkov Date: Tue, 26 Sep 2023 00:09:34 +0300 Subject: [PATCH 28/37] Ensure order --- R/cran-compliance.R | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/R/cran-compliance.R b/R/cran-compliance.R index 0a7db90a..efca4b2e 100644 --- a/R/cran-compliance.R +++ b/R/cran-compliance.R @@ -190,7 +190,8 @@ vendor_pkgs <- function(path = ".", quiet = FALSE, overwrite = NULL) { tibble::as_tibble(.name_repair = "minimal") %>% rlang::set_names(c("source", "crate", "version")) %>% dplyr::filter(!is.na(source)) %>% - dplyr::select(-source) + dplyr::select(-source) %>% + dplyr::arrange(crate) # capture vendor-config.toml content config_toml <- vendor_res[["stdout"]] %>% From 834a6ed8f8fd6cb4b343a6ac6f8d3b1d0d0ccff3 Mon Sep 17 00:00:00 2001 From: Ilia Kosenkov Date: Tue, 26 Sep 2023 00:48:32 +0300 Subject: [PATCH 29/37] Lintr --- R/cran-compliance.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/cran-compliance.R b/R/cran-compliance.R index efca4b2e..d3e3b00b 100644 --- a/R/cran-compliance.R +++ b/R/cran-compliance.R @@ -191,7 +191,7 @@ vendor_pkgs <- function(path = ".", quiet = FALSE, overwrite = NULL) { rlang::set_names(c("source", "crate", "version")) %>% dplyr::filter(!is.na(source)) %>% dplyr::select(-source) %>% - dplyr::arrange(crate) + dplyr::arrange(crate) # nolint: object_usage_linter # capture vendor-config.toml content config_toml <- vendor_res[["stdout"]] %>% From 98ae6a09a227ad81531f5cb51f2e6a78cc155160 Mon Sep 17 00:00:00 2001 From: Ilia Kosenkov Date: Tue, 26 Sep 2023 00:48:44 +0300 Subject: [PATCH 30/37] Update snapshot --- tests/testthat/_snaps/use_cran_defaults.md | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/testthat/_snaps/use_cran_defaults.md b/tests/testthat/_snaps/use_cran_defaults.md index 040fd11e..02bf3356 100644 --- a/tests/testthat/_snaps/use_cran_defaults.md +++ b/tests/testthat/_snaps/use_cran_defaults.md @@ -177,7 +177,6 @@ [source.vendored-sources] directory = "vendor" - --- From a03b5c301918b9c9b88f06e123934b8175a41ec8 Mon Sep 17 00:00:00 2001 From: Ilia Kosenkov Date: Tue, 26 Sep 2023 01:06:23 +0300 Subject: [PATCH 31/37] Reuse callback --- R/cran-compliance.R | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/R/cran-compliance.R b/R/cran-compliance.R index d3e3b00b..25cdeebd 100644 --- a/R/cran-compliance.R +++ b/R/cran-compliance.R @@ -115,6 +115,11 @@ use_cran_defaults <- function(path = ".", quiet = FALSE, overwrite = NULL, lib_n #' @export #' @name cran vendor_pkgs <- function(path = ".", quiet = FALSE, overwrite = NULL) { + stderr_line_callback <- function(x, proc) { + if (!cli::ansi_grepl("To use vendored sources", x) && cli::ansi_nzchar(x)) { + cli::cat_bullet(stringi::stri_trim_left(x)) + } + } local_quiet_cli(quiet) # get path to rust folder @@ -140,11 +145,7 @@ vendor_pkgs <- function(path = ".", quiet = FALSE, overwrite = NULL) { "--manifest-path", file.path(src_dir, "Cargo.toml") ), - stderr_line_callback = function(x, proc) { - if (!grepl("To use vendored sources", x) && x != "") { - cli::cat_bullet(stringi::stri_trim_left(x)) - } - } + stderr_line_callback = stderr_line_callback ) }) @@ -166,11 +167,7 @@ vendor_pkgs <- function(path = ".", quiet = FALSE, overwrite = NULL) { "--manifest-path", file.path(src_dir, "Cargo.toml") ), - stderr_line_callback = function(x, proc) { - if (!grepl("To use vendored sources", x) && x != "") { - cli::cat_bullet(stringi::stri_trim_left(x)) - } - } + stderr_line_callback = stderr_line_callback ) }) From b96f08431a4528a058005af1de9113b93664772d Mon Sep 17 00:00:00 2001 From: Josiah Parry Date: Tue, 26 Sep 2023 08:41:07 -0400 Subject: [PATCH 32/37] update news --- NEWS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/NEWS.md b/NEWS.md index 08850422..10298e20 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,6 @@ # rextendr (development version) +* Introduces new functions `use_cran_defaults()` and `vendor_pkgs()` to ease the publication of extendr-powered packages on CRAN. See the new article _CRAN compliant extendr packages_ on how to use these. * `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). From e3b609aa758f31f87231483436d1b2ac0c4dc6ad Mon Sep 17 00:00:00 2001 From: Josiah Parry Date: Mon, 2 Oct 2023 07:36:58 -0400 Subject: [PATCH 33/37] add OS check for Sys.chmod add test for vendor.tar.xz existing --- R/cran-compliance.R | 5 ++++- tests/testthat/test-use_cran_defaults.R | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/R/cran-compliance.R b/R/cran-compliance.R index 25cdeebd..6fef5b36 100644 --- a/R/cran-compliance.R +++ b/R/cran-compliance.R @@ -62,7 +62,10 @@ use_cran_defaults <- function(path = ".", quiet = FALSE, overwrite = NULL, lib_n ) # configure needs to be made executable - Sys.chmod("configure", "0755") + # ignore for Windows + if (.Platform[["OS.type"]] == "unix") { + Sys.chmod("configure", "0755") + } use_rextendr_template( "cran/configure.win", diff --git a/tests/testthat/test-use_cran_defaults.R b/tests/testthat/test-use_cran_defaults.R index f8082a40..83ee5ad3 100644 --- a/tests/testthat/test-use_cran_defaults.R +++ b/tests/testthat/test-use_cran_defaults.R @@ -36,4 +36,5 @@ test_that("vendor_pkgs() vendors dependencies", { package_versions <- vendor_pkgs(path, quiet = TRUE) expect_snapshot(cat_file("src", "rust", "vendor-config.toml")) expect_snapshot(package_versions) + expect_true(file.exists(file.path("src", "rust", "vendor.tar.xz"))) }) From 38a6e84f85ad0933f5e3bb9475e148abdfcf14ee Mon Sep 17 00:00:00 2001 From: Josiah Parry Date: Thu, 9 Nov 2023 11:16:48 -0500 Subject: [PATCH 34/37] update Makevars to better clean up vendored libraries --- inst/templates/cran/Makevars | 13 ++++--------- inst/templates/cran/Makevars.win | 5 +++-- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/inst/templates/cran/Makevars b/inst/templates/cran/Makevars index df08b1ce..2859824d 100644 --- a/inst/templates/cran/Makevars +++ b/inst/templates/cran/Makevars @@ -9,7 +9,7 @@ $(SHLIB): $(STATLIB) CRAN_FLAGS=-j 2 --offline CARGOTMP = $(CURDIR)/.cargo - +VENDOR_DIR = $(CURDIR)/vendor $(STATLIB): if [ -f ./rust/vendor.tar.xz ]; then \ @@ -27,16 +27,11 @@ $(STATLIB): export PATH="$(PATH):$(HOME)/.cargo/bin" && \ cargo build $(CRAN_FLAGS) --lib --release --manifest-path=./rust/Cargo.toml --target-dir $(TARGET_DIR) && \ echo `cargo --version` && echo `rustc --version`; - if [ "$(NOT_CRAN)" != "true" ]; then \ rm -Rf $(CARGOTMP) $(VENDOR_DIR) $(LIBDIR)/build; \ - fi + C_clean: - rm -Rf $(SHLIB) $(STATLIB) $(OBJECTS) + rm -Rf $(SHLIB) $(STATLIB) $(OBJECTS) $(CARGOTMP) $(VENDOR_DIR) clean: - rm -Rf $(SHLIB) $(STATLIB) $(OBJECTS) ./rust/target - - - - + rm -Rf $(SHLIB) $(STATLIB) $(OBJECTS) $(CARGOTMP) $(VENDOR_DIR) $(TARGET_DIR) diff --git a/inst/templates/cran/Makevars.win b/inst/templates/cran/Makevars.win index f503fc9a..f02ffd85 100644 --- a/inst/templates/cran/Makevars.win +++ b/inst/templates/cran/Makevars.win @@ -11,6 +11,7 @@ $(SHLIB): $(STATLIB) CRAN_FLAGS=-j 2 --offline CARGOTMP = $(CURDIR)/.cargo +VENDOR_DIR = $(CURDIR)/vendor all: C_clean @@ -48,7 +49,7 @@ $(STATLIB): fi C_clean: - rm -Rf $(SHLIB) $(STATLIB) $(OBJECTS) + rm -Rf $(SHLIB) $(STATLIB) $(OBJECTS) $(CARGOTMP) $(VENDOR_DIR) clean: - rm -Rf $(SHLIB) $(STATLIB) $(OBJECTS) $(TARGET_DIR) ./rust/vendor $(CARGO_HOME) + rm -Rf $(SHLIB) $(STATLIB) $(OBJECTS) $(CARGOTMP) $(VENDOR_DIR) $(TARGET_DIR) From 6e1c742c6d727cdc5f996a7aefd33919a3aca2fa Mon Sep 17 00:00:00 2001 From: Josiah Parry Date: Thu, 9 Nov 2023 11:17:53 -0500 Subject: [PATCH 35/37] remove superfluous new line --- inst/templates/cran/Makevars | 1 - 1 file changed, 1 deletion(-) diff --git a/inst/templates/cran/Makevars b/inst/templates/cran/Makevars index 2859824d..e2dc513e 100644 --- a/inst/templates/cran/Makevars +++ b/inst/templates/cran/Makevars @@ -29,7 +29,6 @@ $(STATLIB): echo `cargo --version` && echo `rustc --version`; rm -Rf $(CARGOTMP) $(VENDOR_DIR) $(LIBDIR)/build; \ - C_clean: rm -Rf $(SHLIB) $(STATLIB) $(OBJECTS) $(CARGOTMP) $(VENDOR_DIR) From 8401fc9d44c01fa624cc5550a6fdea66c3b2ba03 Mon Sep 17 00:00:00 2001 From: Josiah Parry Date: Thu, 21 Dec 2023 08:59:07 -0500 Subject: [PATCH 36/37] update snaps --- tests/testthat/_snaps/use_cran_defaults.md | 25 +++++++++++----------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/tests/testthat/_snaps/use_cran_defaults.md b/tests/testthat/_snaps/use_cran_defaults.md index 02bf3356..aa9b6355 100644 --- a/tests/testthat/_snaps/use_cran_defaults.md +++ b/tests/testthat/_snaps/use_cran_defaults.md @@ -183,17 +183,16 @@ Code package_versions Output - # A tibble: 10 x 2 - crate version - - 1 extendr-api 0.4.0 - 2 extendr-engine 0.4.0 - 3 extendr-macros 0.4.0 - 4 lazy_static 1.4.0 - 5 libR-sys 0.4.0 - 6 paste 1.0.14 - 7 proc-macro2 1.0.67 - 8 quote 1.0.33 - 9 syn 1.0.109 - 10 unicode-ident 1.0.12 + # A tibble: 9 x 2 + crate version + + 1 extendr-api 0.6.0 + 2 extendr-macros 0.6.0 + 3 libR-sys 0.6.0 + 4 once_cell 1.19.0 + 5 paste 1.0.14 + 6 proc-macro2 1.0.70 + 7 quote 1.0.33 + 8 syn 2.0.42 + 9 unicode-ident 1.0.12 From b202bb2db12e4ef4659c6f18f52b8a694868f93b Mon Sep 17 00:00:00 2001 From: Josiah Parry Date: Thu, 21 Dec 2023 09:19:46 -0500 Subject: [PATCH 37/37] update NEWS to reference PR --- NEWS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index 09178963..f0c904a9 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,6 +1,6 @@ # rextendr (development version) -* Introduces new functions `use_cran_defaults()` and `vendor_pkgs()` to ease the publication of extendr-powered packages on CRAN. See the new article _CRAN compliant extendr packages_ on how to use these. +* Introduces new functions `use_cran_defaults()` and `vendor_pkgs()` to ease the publication of extendr-powered packages on CRAN. See the new article _CRAN compliant extendr packages_ on how to use these (#320). * `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).