-
Notifications
You must be signed in to change notification settings - Fork 26
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adds CRAN compliance helpers and vignette (#320)
* add use_cran_defaults() function along with vendor_pkgs() * add articles to build ignore * lint * 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) * address lintr * resolve pkgdown CI * address CI lintr * Update R/cran-compliance.R Co-authored-by: Ilia Kosenkov <[email protected]> * Update R/cran-compliance.R Co-authored-by: Ilia Kosenkov <[email protected]> * Update R/cran-compliance.R Co-authored-by: Ilia Kosenkov <[email protected]> * Update R/cran-compliance.R Co-authored-by: Ilia Kosenkov <[email protected]> * Update vignettes/articles/cran-compliance.Rmd Co-authored-by: Ilia Kosenkov <[email protected]> * Update vignettes/articles/cran-compliance.Rmd Co-authored-by: Ilia Kosenkov <[email protected]> * Update vignettes/articles/cran-compliance.Rmd Co-authored-by: Ilia Kosenkov <[email protected]> * Update vignettes/articles/cran-compliance.Rmd Co-authored-by: Ilia Kosenkov <[email protected]> * Update vignettes/articles/cran-compliance.Rmd Co-authored-by: Ilia Kosenkov <[email protected]> * Update vignettes/articles/cran-compliance.Rmd Co-authored-by: Ilia Kosenkov <[email protected]> * Update R/cran-compliance.R Co-authored-by: Ilia Kosenkov <[email protected]> * Apply suggestions from code review Co-authored-by: Ilia Kosenkov <[email protected]> * explicitly print data frame for tests * ensure update_res only exists when cargo_lock_fp file does not * Fix how crate versions are detected * Execute `{styler}` over package * Update snapshots * Don't use `print()` in tests * Reorder asserts * Strip ANSI symbols * Ensure order * Lintr * Update snapshot * Reuse callback * update news * add OS check for Sys.chmod add test for vendor.tar.xz existing * update Makevars to better clean up vendored libraries * remove superfluous new line * update snaps * update NEWS to reference PR --------- Co-authored-by: Ilia Kosenkov <[email protected]> Co-authored-by: Ilia Kosenkov <[email protected]>
- Loading branch information
1 parent
09bfeba
commit edc5476
Showing
15 changed files
with
730 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,3 +17,4 @@ | |
^\.idea$ | ||
^inst/libgcc_mock/libgcc_eh.a$ | ||
^CRAN-SUBMISSION$ | ||
^vignettes/articles$ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,224 @@ | ||
#' 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 | ||
# ignore for Windows | ||
if (.Platform[["OS.type"]] == "unix") { | ||
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 should 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 folder will be large 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) { | ||
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 | ||
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" = "Did you run {.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 = stderr_line_callback | ||
) | ||
}) | ||
|
||
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, { | ||
vendor_res <- processx::run( | ||
"cargo", | ||
c( | ||
"vendor", | ||
"--locked", | ||
"--manifest-path", | ||
file.path(src_dir, "Cargo.toml") | ||
), | ||
stderr_line_callback = stderr_line_callback | ||
) | ||
}) | ||
|
||
if (vendor_res[["status"]] != 0) { | ||
cli::cli_abort( | ||
"{.code cargo vendor} failed", | ||
class = "rextendr_error" | ||
) | ||
} | ||
|
||
# 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) %>% | ||
dplyr::arrange(crate) # nolint: object_usage_linter | ||
|
||
# capture vendor-config.toml content | ||
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. | ||
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" | ||
) | ||
} | ||
|
||
# return packages and versions invisibly | ||
invisible(res) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
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 | ||
VENDOR_DIR = $(CURDIR)/vendor | ||
|
||
$(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`; | ||
rm -Rf $(CARGOTMP) $(VENDOR_DIR) $(LIBDIR)/build; \ | ||
|
||
C_clean: | ||
rm -Rf $(SHLIB) $(STATLIB) $(OBJECTS) $(CARGOTMP) $(VENDOR_DIR) | ||
|
||
clean: | ||
rm -Rf $(SHLIB) $(STATLIB) $(OBJECTS) $(CARGOTMP) $(VENDOR_DIR) $(TARGET_DIR) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
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 | ||
VENDOR_DIR = $(CURDIR)/vendor | ||
|
||
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) $(CARGOTMP) $(VENDOR_DIR) | ||
|
||
clean: | ||
rm -Rf $(SHLIB) $(STATLIB) $(OBJECTS) $(CARGOTMP) $(VENDOR_DIR) $(TARGET_DIR) |
Oops, something went wrong.