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

Adds CRAN compliance helpers and vignette #320

Merged
merged 41 commits into from
Dec 29, 2023
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
82bdf45
add use_cran_defaults() function along with vendor_pkgs()
JosiahParry Sep 23, 2023
acd444c
add articles to build ignore
JosiahParry Sep 23, 2023
9907abd
lint
JosiahParry Sep 23, 2023
deef23f
address testthat snaps being different based on OS and cargo path. No…
JosiahParry Sep 23, 2023
f6925d4
address lintr
JosiahParry Sep 23, 2023
aca8222
resolve pkgdown CI
JosiahParry Sep 23, 2023
2caf23b
address CI lintr
JosiahParry Sep 24, 2023
e79b179
Update R/cran-compliance.R
JosiahParry Sep 24, 2023
2d79a31
Update R/cran-compliance.R
JosiahParry Sep 24, 2023
e0e3d18
Update R/cran-compliance.R
JosiahParry Sep 24, 2023
2ac41eb
Update R/cran-compliance.R
JosiahParry Sep 24, 2023
38d0405
Update vignettes/articles/cran-compliance.Rmd
JosiahParry Sep 24, 2023
d4dab5c
Update vignettes/articles/cran-compliance.Rmd
JosiahParry Sep 24, 2023
b4b387e
Update vignettes/articles/cran-compliance.Rmd
JosiahParry Sep 24, 2023
0192b84
Update vignettes/articles/cran-compliance.Rmd
JosiahParry Sep 24, 2023
eda7809
Update vignettes/articles/cran-compliance.Rmd
JosiahParry Sep 24, 2023
e54dc06
Update vignettes/articles/cran-compliance.Rmd
JosiahParry Sep 24, 2023
6f39904
Update R/cran-compliance.R
JosiahParry Sep 24, 2023
d99ba28
Apply suggestions from code review
JosiahParry Sep 24, 2023
071efd8
explicitly print data frame for tests
JosiahParry Sep 24, 2023
4dbd2de
ensure update_res only exists when cargo_lock_fp file does not
JosiahParry Sep 24, 2023
552a99a
Fix how crate versions are detected
Ilia-Kosenkov Sep 24, 2023
27f6bfd
Execute `{styler}` over package
Ilia-Kosenkov Sep 24, 2023
f60f55e
Update snapshots
Ilia-Kosenkov Sep 24, 2023
5327019
Merge pull request #1 from Ilia-Kosenkov/hotfix/cran-fixed
JosiahParry Sep 24, 2023
013854a
Don't use `print()` in tests
Ilia-Kosenkov Sep 24, 2023
0370e20
Reorder asserts
Ilia-Kosenkov Sep 24, 2023
4f391ce
Strip ANSI symbols
Ilia-Kosenkov Sep 25, 2023
b9cddfe
Ensure order
Ilia-Kosenkov Sep 25, 2023
834a6ed
Lintr
Ilia-Kosenkov Sep 25, 2023
98ae6a0
Update snapshot
Ilia-Kosenkov Sep 25, 2023
a03b5c3
Reuse callback
Ilia-Kosenkov Sep 25, 2023
842b1b3
Merge pull request #2 from Ilia-Kosenkov/hotfix/cran-fixed
JosiahParry Sep 26, 2023
b96f084
update news
JosiahParry Sep 26, 2023
e3b609a
add OS check for Sys.chmod add test for vendor.tar.xz existing
JosiahParry Oct 2, 2023
515dbb3
Merge branch 'main' into cran
JosiahParry Oct 2, 2023
38a6e84
update Makevars to better clean up vendored libraries
JosiahParry Nov 9, 2023
6e1c742
remove superfluous new line
JosiahParry Nov 9, 2023
eb30891
Merge branch 'extendr:main' into cran
JosiahParry Dec 21, 2023
8401fc9
update snaps
JosiahParry Dec 21, 2023
b202bb2
update NEWS to reference PR
JosiahParry Dec 21, 2023
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 .Rbuildignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@
^\.idea$
^inst/libgcc_mock/libgcc_eh.a$
^CRAN-SUBMISSION$
^vignettes/articles$
2 changes: 2 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
221 changes: 221 additions & 0 deletions R/cran-compliance.R
Original file line number Diff line number Diff line change
@@ -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")
JosiahParry marked this conversation as resolved.
Show resolved Hide resolved

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) {
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 = function(x, proc) {
if (!grepl("To use vendored sources", x) && x != "") {
cli::cat_bullet(stringi::stri_trim_left(x))
}
}
)
})
}

if (update_res[["status"]] != 0) {
JosiahParry marked this conversation as resolved.
Show resolved Hide resolved
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 = 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} failed",
class = "rextendr_error"
)
}

# capture vendor-config.toml content
config_toml <- stringi::stri_split(vendor_res$stdout, coll = "\n")[[1]]
JosiahParry marked this conversation as resolved.
Show resolved Hide resolved

# 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(
Ilia-Kosenkov marked this conversation as resolved.
Show resolved Hide resolved
"-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]]
JosiahParry marked this conversation as resolved.
Show resolved Hide resolved
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)
}
1 change: 1 addition & 0 deletions _pkgdown.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ reference:
- register_extendr
- write_license_note
- clean
- cran

- title: Various utility functions
contents:
Expand Down
42 changes: 42 additions & 0 deletions inst/templates/cran/Makevars
Original file line number Diff line number Diff line change
@@ -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




JosiahParry marked this conversation as resolved.
Show resolved Hide resolved
54 changes: 54 additions & 0 deletions inst/templates/cran/Makevars.win
Original file line number Diff line number Diff line change
@@ -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)
21 changes: 21 additions & 0 deletions inst/templates/cran/configure
Original file line number Diff line number Diff line change
@@ -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

15 changes: 15 additions & 0 deletions inst/templates/cran/configure.win
Original file line number Diff line number Diff line change
@@ -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
Loading