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

Make rextendr packages CRAN compatible by default #394

Merged
merged 13 commits into from
Nov 17, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ export(rust_function)
export(rust_sitrep)
export(rust_source)
export(to_toml)
export(use_cran_defaults)
export(use_crate)
export(use_extendr)
export(use_msrv)
Expand Down
3 changes: 2 additions & 1 deletion NEWS.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# rextendr (development version)

* `Makevars` now prints linked static libraries at compile time <https://github.com/extendr/rextendr/pull/393>
* `use_extendr()` now creates `tools/msrv.R`, `configure` and `configure.win`. These have been moved out of `use_cran_defaults()` <https://github.com/extendr/rextendr/pull/393>
* `Makevars` now prints linked static libraries at compile time by adding `--print=native-static-libs` to `RUSTFLAGS` <https://github.com/extendr/rextendr/pull/393>
* `use_extendr()` sets the `DESCRIPTION`'s `SystemRequirements` field according to CRAN policy to `Cargo (Rust's package manager), rustc` (#329)
* 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).
Expand Down
82 changes: 3 additions & 79 deletions R/cran-compliance.R
Original file line number Diff line number Diff line change
@@ -1,99 +1,23 @@
#' 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 `tools/msrv.R`, `configure` and `configure.win` files as well as
#' modifies `Makevars` and `Makevars.win` to use required CRAN settings.
#' Vendor Rust dependencies
#'
#' `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()`.
# 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()
#' \dontrun{
#' 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"
)
}

# 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)) {
Expand Down
42 changes: 28 additions & 14 deletions R/use_extendr.R
Original file line number Diff line number Diff line change
Expand Up @@ -101,16 +101,16 @@ use_extendr <- function(path = ".",
)

use_rextendr_template(
"Makevars",
save_as = file.path("src", "Makevars"),
"Makevars.in",
save_as = file.path("src", "Makevars.in"),
quiet = quiet,
overwrite = overwrite,
data = list(lib_name = lib_name)
)

use_rextendr_template(
"Makevars.win",
save_as = file.path("src", "Makevars.win"),
"Makevars.win.in",
save_as = file.path("src", "Makevars.win.in"),
quiet = quiet,
overwrite = overwrite,
data = list(lib_name = lib_name)
Expand All @@ -131,8 +131,6 @@ use_extendr <- function(path = ".",
overwrite = overwrite
)

usethis::use_build_ignore("src/.cargo")

edition <- match.arg(edition, several.ok = FALSE)
cargo_toml_content <- to_toml(
package = list(name = crate_name, publish = FALSE, version = "0.1.0", edition = edition),
Expand Down Expand Up @@ -179,35 +177,51 @@ use_extendr <- function(path = ".",

# add msrv.R template
use_rextendr_template(
"cran/msrv.R",
"msrv.R",
save_as = file.path("tools", "msrv.R"),
quiet = quiet,
overwrite = overwrite
)

# add configure and configure.win templates
use_rextendr_template(
"cran/configure",
"configure",
save_as = "configure",
quiet = quiet,
overwrite = overwrite,
data = list(lib_name = lib_name)
)

use_rextendr_template(
"configure.win",
save_as = "configure.win",
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)
# the temporary cargo directory must be ignored
usethis::use_build_ignore("src/.cargo")

# ensure that the vendor directory is ignored
usethis::use_build_ignore(
file.path("src", "rust", "vendor")
)

usethis::use_git_ignore(
file.path("src", "rust", "vendor")
)

# the src/Makevars should be created each time the package
# is built. This is handled via the configure file
usethis::use_build_ignore("src/Makevars")
usethis::use_git_ignore("src/Makevars")

if (!isTRUE(quiet)) {
cli::cli_alert_success("Finished configuring {.pkg extendr} for package {.pkg {pkg_name}}.")
Expand Down
33 changes: 0 additions & 33 deletions inst/templates/Makevars

This file was deleted.

50 changes: 50 additions & 0 deletions inst/templates/Makevars.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
TARGET_DIR = ./rust/target
LIBDIR = $(TARGET_DIR)/release
STATLIB = $(LIBDIR)/lib{{{lib_name}}}.a
JosiahParry marked this conversation as resolved.
Show resolved Hide resolved
PKG_LIBS = -L$(LIBDIR) -l{{{lib_name}}}

all: C_clean

$(SHLIB): $(STATLIB)

CARGOTMP = $(CURDIR)/.cargo
VENDOR_DIR = $(CURDIR)/vendor


# RUSTFLAGS appends --print=native-static-libs to ensure that
# the correct linkers are used. Use this for debugging if need.
#
# CRAN note: Cargo and Rustc versions are reported during
JosiahParry marked this conversation as resolved.
Show resolved Hide resolved
# configure via tools/msrv.R.
#
# When on CRAN, the vendor.tar.xz file is unzipped and used
# for offline compilation. It is ignored when NOT_CRAN != false
$(STATLIB):

# Check if NOT_CRAN is false and unzip vendor.tar.xz if so
if [ "$(NOT_CRAN)" = "false" ]; then \
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; \
fi
JosiahParry marked this conversation as resolved.
Show resolved Hide resolved

export CARGO_HOME=$(CARGOTMP) && \
export PATH="$(PATH):$(HOME)/.cargo/bin" && \
RUSTFLAGS="$(RUSTFLAGS) --print=native-static-libs" cargo build @CRAN_FLAGS@ --lib --release --manifest-path=./rust/Cargo.toml --target-dir $(TARGET_DIR)
JosiahParry marked this conversation as resolved.
Show resolved Hide resolved

# Always clean up CARGOTMP
rm -Rf $(CARGOTMP)

if [ "$(NOT_CRAN)" != "false" ]; then \
rm -Rf $(VENDOR_DIR); \
rm -Rf $(TARGET_DIR); \
rm -Rf $(LIBDIR)/build; \
fi
JosiahParry marked this conversation as resolved.
Show resolved Hide resolved

C_clean:
rm -Rf $(SHLIB) $(STATLIB) $(OBJECTS)

clean:
rm -Rf $(SHLIB) $(STATLIB) $(OBJECTS) $(TARGET_DIR) $(VENDOR_DIR)
36 changes: 23 additions & 13 deletions inst/templates/Makevars.win → inst/templates/Makevars.win.in
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@ LIBDIR = $(TARGET_DIR)/$(TARGET)/release
STATLIB = $(LIBDIR)/lib{{{lib_name}}}.a
PKG_LIBS = -L$(LIBDIR) -l{{{lib_name}}} -lws2_32 -ladvapi32 -luserenv -lbcrypt -lntdll

# Print linked static libraries at compile time
export RUSTFLAGS=--print=native-static-libs

all: C_clean

$(SHLIB): $(STATLIB)
Expand All @@ -24,18 +21,31 @@ $(STATLIB):
# 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; \
# Handle NOT_CRAN case: If NOT_CRAN is false, vendor tarball should be handled
if [ "$(NOT_CRAN)" = "false" ]; then \
JosiahParry marked this conversation as resolved.
Show resolved Hide resolved
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; \
fi

# CARGO_LINKER is provided in Makevars.ucrt for R >= 4.2
# Build the project using Cargo with additional flags
export CARGO_HOME=$(CARGOTMP) && \
export CARGO_TARGET_X86_64_PC_WINDOWS_GNU_LINKER="$(CARGO_LINKER)" && \
export LIBRARY_PATH="$${LIBRARY_PATH};$(CURDIR)/$(TARGET_DIR)/libgcc_mock" && \
RUSTFLAGS="$(RUSTFLAGS) --print=native-static-libs" cargo @CRAN_FLAGS@ build --target=$(TARGET) --lib --release --manifest-path=./rust/Cargo.toml --target-dir $(TARGET_DIR)

# Always clean up CARGOTMP
rm -Rf $(CARGOTMP)

if [ "$(NOT_CRAN)" != "false" ]; then \
rm -Rf $(VENDOR_DIR); \
rm -Rf $(TARGET_DIR); \
rm -Rf $(LIBDIR)/build; \
fi
JosiahParry marked this conversation as resolved.
Show resolved Hide resolved

C_clean:
rm -Rf $(SHLIB) $(STATLIB) $(OBJECTS)

Expand Down
16 changes: 16 additions & 0 deletions inst/templates/configure
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/usr/bin/env sh
: "${R_HOME=`R RHOME`}"
"${R_HOME}/bin/Rscript" tools/msrv.R

# Set CRAN_FLAGS based on the NOT_CRAN value
if [ "${NOT_CRAN}" = "false" ]; then
export CRAN_FLAGS="-j 2 --offline"
else
export CRAN_FLAGS=""
fi

# delete Makevars if it is present
[ -f src/Makevars ] && rm src/Makevars

# Substitute @CRAN_FLAGS@ in Makevars.in with the actual value of $CRAN_FLAGS
sed -e "s|@CRAN_FLAGS@|$CRAN_FLAGS|" src/Makevars.in > src/Makevars
JosiahParry marked this conversation as resolved.
Show resolved Hide resolved
15 changes: 15 additions & 0 deletions inst/templates/configure.win
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#!/usr/bin/env sh
"${R_HOME}/bin${R_ARCH_BIN}/Rscript.exe" tools/msrv.R

# Set CRAN_FLAGS based on the NOT_CRAN value
if [ "${NOT_CRAN}" = "false" ]; then
export CRAN_FLAGS="-j 2 --offline"
else
export CRAN_FLAGS=""
fi

# delete Makevars if it is present
[ -f src/Makevars ] && rm src/Makevars

# Substitute @CRAN_FLAGS@ in Makevars.in with the actual value of $CRAN_FLAGS
sed -e "s|@CRAN_FLAGS@|$CRAN_FLAGS|" src/Makevars.in > src/Makevars
Loading
Loading