From 610d7ec689c7b15d90e0cb2fa33c9d8240ce496c Mon Sep 17 00:00:00 2001 From: Josiah Parry Date: Mon, 28 Oct 2024 17:40:26 -0700 Subject: [PATCH 01/12] make rustflags additive --- inst/templates/Makevars | 2 +- inst/templates/Makevars.win | 2 +- inst/templates/cran/Makevars | 2 +- inst/templates/cran/Makevars.win | 2 +- tests/testthat/_snaps/use_cran_defaults.md | 4 ++-- tests/testthat/_snaps/use_extendr.md | 6 +++--- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/inst/templates/Makevars b/inst/templates/Makevars index 6ec006bf..eba6dd7f 100644 --- a/inst/templates/Makevars +++ b/inst/templates/Makevars @@ -4,7 +4,7 @@ STATLIB = $(LIBDIR)/lib{{{lib_name}}}.a PKG_LIBS = -L$(LIBDIR) -l{{{lib_name}}} # Print linked static libraries at compile time -export RUSTFLAGS=--print=native-static-libs +export RUSTFLAGS += --print=native-static-libs all: C_clean diff --git a/inst/templates/Makevars.win b/inst/templates/Makevars.win index 4e2a4387..9bdff35c 100644 --- a/inst/templates/Makevars.win +++ b/inst/templates/Makevars.win @@ -6,7 +6,7 @@ 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 +export RUSTFLAGS += --print=native-static-libs all: C_clean diff --git a/inst/templates/cran/Makevars b/inst/templates/cran/Makevars index 0f0e40aa..3858063a 100644 --- a/inst/templates/cran/Makevars +++ b/inst/templates/cran/Makevars @@ -4,7 +4,7 @@ STATLIB = $(LIBDIR)/lib{{{lib_name}}}.a PKG_LIBS = -L$(LIBDIR) -l{{{lib_name}}} # Print linked static libraries at compile time -export RUSTFLAGS=--print=native-static-libs +export RUSTFLAGS += --print=native-static-libs all: C_clean diff --git a/inst/templates/cran/Makevars.win b/inst/templates/cran/Makevars.win index 72f7479d..599ab819 100644 --- a/inst/templates/cran/Makevars.win +++ b/inst/templates/cran/Makevars.win @@ -6,7 +6,7 @@ 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 +export RUSTFLAGS += --print=native-static-libs all: C_clean diff --git a/tests/testthat/_snaps/use_cran_defaults.md b/tests/testthat/_snaps/use_cran_defaults.md index 4c637a16..db6d29e8 100644 --- a/tests/testthat/_snaps/use_cran_defaults.md +++ b/tests/testthat/_snaps/use_cran_defaults.md @@ -44,7 +44,7 @@ PKG_LIBS = -L$(LIBDIR) -ltestpkg # Print linked static libraries at compile time - export RUSTFLAGS=--print=native-static-libs + export RUSTFLAGS += --print=native-static-libs all: C_clean @@ -85,7 +85,7 @@ PKG_LIBS = -L$(LIBDIR) -ltestpkg -lws2_32 -ladvapi32 -luserenv -lbcrypt -lntdll # Print linked static libraries at compile time - export RUSTFLAGS=--print=native-static-libs + export RUSTFLAGS += --print=native-static-libs all: C_clean diff --git a/tests/testthat/_snaps/use_extendr.md b/tests/testthat/_snaps/use_extendr.md index bb281aff..a712f430 100644 --- a/tests/testthat/_snaps/use_extendr.md +++ b/tests/testthat/_snaps/use_extendr.md @@ -52,7 +52,7 @@ PKG_LIBS = -L$(LIBDIR) -ltestpkg # Print linked static libraries at compile time - export RUSTFLAGS=--print=native-static-libs + export RUSTFLAGS += --print=native-static-libs all: C_clean @@ -93,7 +93,7 @@ PKG_LIBS = -L$(LIBDIR) -ltestpkg -lws2_32 -ladvapi32 -luserenv -lbcrypt -lntdll # Print linked static libraries at compile time - export RUSTFLAGS=--print=native-static-libs + export RUSTFLAGS += --print=native-static-libs all: C_clean @@ -276,7 +276,7 @@ PKG_LIBS = -L$(LIBDIR) -lbar # Print linked static libraries at compile time - export RUSTFLAGS=--print=native-static-libs + export RUSTFLAGS += --print=native-static-libs all: C_clean From 9080d17685f601d734808507373262ed50b8f393 Mon Sep 17 00:00:00 2001 From: Josiah Parry Date: Mon, 28 Oct 2024 17:42:11 -0700 Subject: [PATCH 02/12] make rustflags additive --- NEWS.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index 246de4ea..075c7108 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,6 +1,7 @@ # rextendr (development version) -* `Makevars` now prints linked static libraries at compile time +* `use_extendr()` now creates `tools/msrv.R`, `configure` and `configure.win`. These have been moved out of `use_cran_defaults()` +* `Makevars` now prints linked static libraries at compile time by adding `--print=native-static-libs` to `RUSTFLAGS` * `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). From 16b7d5a0fd226636ce80746e243b6d89d5556d25 Mon Sep 17 00:00:00 2001 From: Josiah Parry Date: Fri, 8 Nov 2024 13:45:28 -0800 Subject: [PATCH 03/12] rework the makevars configure approach --- R/use_extendr.R | 32 +++++++----- inst/templates/Makevars | 33 ------------ inst/templates/Makevars.in | 50 ++++++++++++++++++ .../{Makevars.win => Makevars.win.in} | 35 ++++++++----- inst/templates/cleanup | 4 ++ inst/templates/configure | 13 +++++ inst/templates/configure.win | 12 +++++ inst/templates/cran/Makevars | 39 -------------- inst/templates/cran/Makevars.win | 51 ------------------- inst/templates/cran/configure | 3 -- inst/templates/cran/configure.win | 2 - inst/templates/{cran => }/msrv.R | 0 12 files changed, 121 insertions(+), 153 deletions(-) delete mode 100644 inst/templates/Makevars create mode 100644 inst/templates/Makevars.in rename inst/templates/{Makevars.win => Makevars.win.in} (53%) create mode 100644 inst/templates/cleanup create mode 100644 inst/templates/configure create mode 100644 inst/templates/configure.win delete mode 100644 inst/templates/cran/Makevars delete mode 100644 inst/templates/cran/Makevars.win delete mode 100644 inst/templates/cran/configure delete mode 100644 inst/templates/cran/configure.win rename inst/templates/{cran => }/msrv.R (100%) diff --git a/R/use_extendr.R b/R/use_extendr.R index 7c62d9c7..a878c49c 100644 --- a/R/use_extendr.R +++ b/R/use_extendr.R @@ -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) @@ -179,7 +179,7 @@ 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 @@ -187,27 +187,35 @@ use_extendr <- function(path = ".", # 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) ) - # configure needs to be made executable - # ignore for Windows - if (.Platform[["OS.type"]] == "unix") { - Sys.chmod("configure", "0755") - } + use_rextendr_template( + "cleanup", + save_as = "cleanup", + quiet = quiet, + overwrite = overwrite, + data = list(lib_name = lib_name) + ) use_rextendr_template( - "cran/configure.win", + "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") + Sys.chmod("cleanup", "0755") + } if (!isTRUE(quiet)) { cli::cli_alert_success("Finished configuring {.pkg extendr} for package {.pkg {pkg_name}}.") diff --git a/inst/templates/Makevars b/inst/templates/Makevars deleted file mode 100644 index eba6dd7f..00000000 --- a/inst/templates/Makevars +++ /dev/null @@ -1,33 +0,0 @@ -TARGET_DIR = ./rust/target -LIBDIR = $(TARGET_DIR)/release -STATLIB = $(LIBDIR)/lib{{{lib_name}}}.a -PKG_LIBS = -L$(LIBDIR) -l{{{lib_name}}} - -# Print linked static libraries at compile time -export RUSTFLAGS += --print=native-static-libs - -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) $(TARGET_DIR) diff --git a/inst/templates/Makevars.in b/inst/templates/Makevars.in new file mode 100644 index 00000000..19d72678 --- /dev/null +++ b/inst/templates/Makevars.in @@ -0,0 +1,50 @@ +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) + +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 +# 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 + + 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) + + # 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 + +C_clean: + rm -Rf $(SHLIB) $(STATLIB) $(OBJECTS) + +clean: + rm -Rf $(SHLIB) $(STATLIB) $(OBJECTS) $(TARGET_DIR) $(VENDOR_DIR) diff --git a/inst/templates/Makevars.win b/inst/templates/Makevars.win.in similarity index 53% rename from inst/templates/Makevars.win rename to inst/templates/Makevars.win.in index 9bdff35c..ae47cd81 100644 --- a/inst/templates/Makevars.win +++ b/inst/templates/Makevars.win.in @@ -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) @@ -24,18 +21,30 @@ $(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 \ + 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 + # 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 + C_clean: rm -Rf $(SHLIB) $(STATLIB) $(OBJECTS) diff --git a/inst/templates/cleanup b/inst/templates/cleanup new file mode 100644 index 00000000..ec35c3f8 --- /dev/null +++ b/inst/templates/cleanup @@ -0,0 +1,4 @@ +# removes the Makevars file generated by configure +# this allows the Makevars file to be generated each +# time the build is called +rm -rf src/Makevars \ No newline at end of file diff --git a/inst/templates/configure b/inst/templates/configure new file mode 100644 index 00000000..3aab1212 --- /dev/null +++ b/inst/templates/configure @@ -0,0 +1,13 @@ +#!/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 + +# 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 \ No newline at end of file diff --git a/inst/templates/configure.win b/inst/templates/configure.win new file mode 100644 index 00000000..40335385 --- /dev/null +++ b/inst/templates/configure.win @@ -0,0 +1,12 @@ +#!/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 + +# 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 \ No newline at end of file diff --git a/inst/templates/cran/Makevars b/inst/templates/cran/Makevars deleted file mode 100644 index 3858063a..00000000 --- a/inst/templates/cran/Makevars +++ /dev/null @@ -1,39 +0,0 @@ -TARGET_DIR = ./rust/target -LIBDIR = $(TARGET_DIR)/release -STATLIB = $(LIBDIR)/lib{{{lib_name}}}.a -PKG_LIBS = -L$(LIBDIR) -l{{{lib_name}}} - -# Print linked static libraries at compile time -export RUSTFLAGS += --print=native-static-libs - -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) diff --git a/inst/templates/cran/Makevars.win b/inst/templates/cran/Makevars.win deleted file mode 100644 index 599ab819..00000000 --- a/inst/templates/cran/Makevars.win +++ /dev/null @@ -1,51 +0,0 @@ -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 - -# Print linked static libraries at compile time -export RUSTFLAGS += --print=native-static-libs - -all: C_clean - -$(SHLIB): $(STATLIB) - -CRAN_FLAGS=-j 2 --offline -CARGOTMP = $(CURDIR)/.cargo -VENDOR_DIR = $(CURDIR)/vendor - -$(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) diff --git a/inst/templates/cran/configure b/inst/templates/cran/configure deleted file mode 100644 index 0f4c1be0..00000000 --- a/inst/templates/cran/configure +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env sh -: "${R_HOME=`R RHOME`}" -"${R_HOME}/bin/Rscript" tools/msrv.R diff --git a/inst/templates/cran/configure.win b/inst/templates/cran/configure.win deleted file mode 100644 index f1945ac1..00000000 --- a/inst/templates/cran/configure.win +++ /dev/null @@ -1,2 +0,0 @@ -#!/usr/bin/env sh -"${R_HOME}/bin${R_ARCH_BIN}/Rscript.exe" tools/msrv.R diff --git a/inst/templates/cran/msrv.R b/inst/templates/msrv.R similarity index 100% rename from inst/templates/cran/msrv.R rename to inst/templates/msrv.R From 3daee1c7366b2574625c6020e795343854d02b32 Mon Sep 17 00:00:00 2001 From: Josiah Parry Date: Sat, 9 Nov 2024 09:08:44 -0800 Subject: [PATCH 04/12] overhaul makevars. remove use_cran_defaults() add tests for r cmd check for cran and non-cran packages --- NAMESPACE | 1 - R/cran-compliance.R | 82 +----- R/use_extendr.R | 28 +- inst/templates/Makevars.win.in | 1 + inst/templates/cleanup | 4 - inst/templates/configure | 3 + inst/templates/configure.win | 3 + man/cran.Rd | 19 +- tests/testthat/_snaps/cran-compliance.md | 29 ++ tests/testthat/_snaps/use_cran_defaults.md | 297 --------------------- tests/testthat/_snaps/use_extendr.md | 154 +++++++---- tests/testthat/test-cran-compliance.R | 30 +++ tests/testthat/test-use_cran_defaults.R | 41 --- tests/testthat/test-use_extendr.R | 32 ++- 14 files changed, 214 insertions(+), 510 deletions(-) delete mode 100644 inst/templates/cleanup create mode 100644 tests/testthat/_snaps/cran-compliance.md delete mode 100644 tests/testthat/_snaps/use_cran_defaults.md create mode 100644 tests/testthat/test-cran-compliance.R delete mode 100644 tests/testthat/test-use_cran_defaults.R diff --git a/NAMESPACE b/NAMESPACE index b04fbc0d..5b3da274 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -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) diff --git a/R/cran-compliance.R b/R/cran-compliance.R index 4ca7505f..154fa1ef 100644 --- a/R/cran-compliance.R +++ b/R/cran-compliance.R @@ -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)) { diff --git a/R/use_extendr.R b/R/use_extendr.R index a878c49c..abcfe318 100644 --- a/R/use_extendr.R +++ b/R/use_extendr.R @@ -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), @@ -194,14 +192,6 @@ use_extendr <- function(path = ".", data = list(lib_name = lib_name) ) - use_rextendr_template( - "cleanup", - save_as = "cleanup", - quiet = quiet, - overwrite = overwrite, - data = list(lib_name = lib_name) - ) - use_rextendr_template( "configure.win", save_as = "configure.win", @@ -214,9 +204,25 @@ use_extendr <- function(path = ".", # ignore for Windows if (.Platform[["OS.type"]] == "unix") { Sys.chmod("configure", "0755") - Sys.chmod("cleanup", "0755") } + # 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}}.") cli::cli_ul( diff --git a/inst/templates/Makevars.win.in b/inst/templates/Makevars.win.in index ae47cd81..b612d14a 100644 --- a/inst/templates/Makevars.win.in +++ b/inst/templates/Makevars.win.in @@ -30,6 +30,7 @@ $(STATLIB): 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)" && \ diff --git a/inst/templates/cleanup b/inst/templates/cleanup deleted file mode 100644 index ec35c3f8..00000000 --- a/inst/templates/cleanup +++ /dev/null @@ -1,4 +0,0 @@ -# removes the Makevars file generated by configure -# this allows the Makevars file to be generated each -# time the build is called -rm -rf src/Makevars \ No newline at end of file diff --git a/inst/templates/configure b/inst/templates/configure index 3aab1212..ba384901 100644 --- a/inst/templates/configure +++ b/inst/templates/configure @@ -9,5 +9,8 @@ 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 \ No newline at end of file diff --git a/inst/templates/configure.win b/inst/templates/configure.win index 40335385..957bc2fe 100644 --- a/inst/templates/configure.win +++ b/inst/templates/configure.win @@ -8,5 +8,8 @@ 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 \ No newline at end of file diff --git a/man/cran.Rd b/man/cran.Rd index 92fb04d9..8c410182 100644 --- a/man/cran.Rd +++ b/man/cran.Rd @@ -2,12 +2,9 @@ % Please edit documentation in R/cran-compliance.R \name{cran} \alias{cran} -\alias{use_cran_defaults} \alias{vendor_pkgs} -\title{Use CRAN compliant defaults} +\title{Vendor Rust dependencies} \usage{ -use_cran_defaults(path = ".", quiet = FALSE, overwrite = NULL, lib_name = NULL) - vendor_pkgs(path = ".", quiet = FALSE, overwrite = NULL) } \arguments{ @@ -21,24 +18,13 @@ If \code{NULL} (default), the function will ask the user whether each file shoul 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{tools/msrv.R}, \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. @@ -46,8 +32,7 @@ If you have modified your dependencies, you will need need to repackage } \examples{ -if (interactive()) { - use_cran_defaults() +\dontrun{ vendor_pkgs() } } diff --git a/tests/testthat/_snaps/cran-compliance.md b/tests/testthat/_snaps/cran-compliance.md new file mode 100644 index 00000000..e7dfdfa0 --- /dev/null +++ b/tests/testthat/_snaps/cran-compliance.md @@ -0,0 +1,29 @@ +# vendor_pkgs() vendors dependencies + + Code + 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: 9 x 2 + crate version + + 1 extendr-api *.*.* + 2 extendr-macros *.*.* + 3 libR-sys *.*.* + 4 once_cell *.*.* + 5 paste *.*.* + 6 proc-macro2 *.*.* + 7 quote *.*.* + 8 syn *.*.* + 9 unicode-ident *.*.* + diff --git a/tests/testthat/_snaps/use_cran_defaults.md b/tests/testthat/_snaps/use_cran_defaults.md deleted file mode 100644 index db6d29e8..00000000 --- a/tests/testthat/_snaps/use_cran_defaults.md +++ /dev/null @@ -1,297 +0,0 @@ -# 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 "*.*.*" in the 'DESCRIPTION' file. - i Setting `SystemRequirements` to "Cargo (Rust's package manager), rustc" 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 Writing 'tools/msrv.R' - v Writing 'configure' - v Writing 'configure.win' - v Finished configuring extendr for package testpkg. - * Please run `rextendr::document()` for changes to take effect. - ---- - - Code - use_cran_defaults() - Message - > 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 - - # Print linked static libraries at compile time - export RUSTFLAGS += --print=native-static-libs - - 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) $(TARGET_DIR) - ---- - - 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 - - # Print linked static libraries at compile time - export RUSTFLAGS += --print=native-static-libs - - 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 - : "${R_HOME=`R RHOME`}" - "${R_HOME}/bin/Rscript" tools/msrv.R - ---- - - Code - cat_file("configure.win") - Output - #!/usr/bin/env sh - "${R_HOME}/bin${R_ARCH_BIN}/Rscript.exe" tools/msrv.R - ---- - - Code - cat_file("tools", "msrv.R") - Output - # read the DESCRIPTION file - desc <- read.dcf("DESCRIPTION") - - if (!"SystemRequirements" %in% colnames(desc)) { - fmt <- c( - "`SystemRequirements` not found in `DESCRIPTION`.", - "Please specify `SystemRequirements: Cargo (Rust's package manager), rustc`" - ) - stop(paste(fmt, collapse = "\n")) - } - - # extract system requirements - sysreqs <- desc[, "SystemRequirements"] - - # check that cargo and rustc is found - if (!grepl("cargo", sysreqs, ignore.case = TRUE)) { - stop("You must specify `Cargo (Rust's package manager)` in your `SystemRequirements`") - } - - if (!grepl("rustc", sysreqs, ignore.case = TRUE)) { - stop("You must specify `Cargo (Rust's package manager), rustc` in your `SystemRequirements`") - } - - # split into parts - parts <- strsplit(sysreqs, ", ")[[1]] - - # identify which is the rustc - rustc_ver <- parts[grepl("rustc", parts)] - - # perform checks for the presence of rustc and cargo on the OS - no_cargo_msg <- c( - "----------------------- [CARGO NOT FOUND]--------------------------", - "The 'cargo' command was not found on the PATH. Please install Cargo", - "from: https://www.rust-lang.org/tools/install", - "", - "Alternatively, you may install Cargo from your OS package manager:", - " - Debian/Ubuntu: apt-get install cargo", - " - Fedora/CentOS: dnf install cargo", - " - macOS: brew install rustc", - "-------------------------------------------------------------------" - ) - - no_rustc_msg <- c( - "----------------------- [RUST NOT FOUND]---------------------------", - "The 'rustc' compiler was not found on the PATH. Please install", - paste(rustc_ver, "or higher from:"), - "https://www.rust-lang.org/tools/install", - "", - "Alternatively, you may install Rust from your OS package manager:", - " - Debian/Ubuntu: apt-get install rustc", - " - Fedora/CentOS: dnf install rustc", - " - macOS: brew install rustc", - "-------------------------------------------------------------------" - ) - - # Add {user}/.cargo/bin to path before checking - new_path <- paste0( - Sys.getenv("PATH"), - ":", - paste0(Sys.getenv("HOME"), "/.cargo/bin") - ) - - # set the path with the new path - Sys.setenv("PATH" = new_path) - - # check for rustc installation - rustc_version <- tryCatch( - system("rustc --version", intern = TRUE), - error = function(e) { - stop(paste(no_rustc_msg, collapse = "\n")) - } - ) - - # check for cargo installation - cargo_version <- tryCatch( - system("cargo --version", intern = TRUE), - error = function(e) { - stop(paste(no_cargo_msg, collapse = "\n")) - } - ) - - # helper function to extract versions - extract_semver <- function(ver) { - if (grepl("\\d+\\.\\d+(\\.\\d+)?", ver)) { - sub(".*?(\\d+\\.\\d+(\\.\\d+)?).*", "\\1", ver) - } else { - NA - } - } - - # get the MSRV - msrv <- extract_semver(rustc_ver) - - # extract current version - current_rust_version <- extract_semver(rustc_version) - - # perform check - if (!is.na(msrv)) { - # -1 when current version is later - # 0 when they are the same - # 1 when MSRV is newer than current - is_msrv <- utils::compareVersion(msrv, current_rust_version) - if (is_msrv == 1) { - fmt <- paste0( - "\n------------------ [UNSUPPORTED RUST VERSION]------------------\n", - "- Minimum supported Rust version is %s.\n", - "- Installed Rust version is %s.\n", - "---------------------------------------------------------------" - ) - stop(sprintf(fmt, msrv, current_rust_version)) - } - } - - # print the versions - versions_fmt <- "Using %s\nUsing %s" - message(sprintf(versions_fmt, cargo_version, rustc_version)) - -# use_cran_defaults() quiet if quiet=TRUE - - Code - use_extendr(quiet = TRUE) - use_cran_defaults(quiet = TRUE) - -# vendor_pkgs() vendors dependencies - - Code - 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: 9 x 2 - crate version - - 1 extendr-api *.*.* - 2 extendr-macros *.*.* - 3 libR-sys *.*.* - 4 once_cell *.*.* - 5 paste *.*.* - 6 proc-macro2 *.*.* - 7 quote *.*.* - 8 syn *.*.* - 9 unicode-ident *.*.* - diff --git a/tests/testthat/_snaps/use_extendr.md b/tests/testthat/_snaps/use_extendr.md index a712f430..f22750f7 100644 --- a/tests/testthat/_snaps/use_extendr.md +++ b/tests/testthat/_snaps/use_extendr.md @@ -8,11 +8,10 @@ i Setting `SystemRequirements` to "Cargo (Rust's package manager), rustc" 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.in' + v Writing 'src/Makevars.win.in' 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' @@ -20,6 +19,11 @@ v Writing 'tools/msrv.R' v Writing 'configure' v Writing 'configure.win' + v Adding "^src/\\.cargo$" to '.Rbuildignore'. + v Adding "^src/rust/vendor$" to '.Rbuildignore'. + v Adding "src/rust/vendor" to '.gitignore'. + v Adding "^src/Makevars$" to '.Rbuildignore'. + v Adding "src/Makevars" to '.gitignore'. v Finished configuring extendr for package testpkg. * Please run `rextendr::document()` for changes to take effect. @@ -44,46 +48,63 @@ --- Code - cat_file("src", "Makevars") + cat_file("src", "Makevars.in") Output TARGET_DIR = ./rust/target LIBDIR = $(TARGET_DIR)/release STATLIB = $(LIBDIR)/libtestpkg.a PKG_LIBS = -L$(LIBDIR) -ltestpkg - # Print linked static libraries at compile time - export RUSTFLAGS += --print=native-static-libs - 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 + # 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): - # 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; \ + + # 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 + 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) + + # 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 + C_clean: rm -Rf $(SHLIB) $(STATLIB) $(OBJECTS) clean: - rm -Rf $(SHLIB) $(STATLIB) $(OBJECTS) $(TARGET_DIR) + rm -Rf $(SHLIB) $(STATLIB) $(OBJECTS) $(TARGET_DIR) $(VENDOR_DIR) --- Code - cat_file("src", "Makevars.win") + cat_file("src", "Makevars.win.in") Output TARGET = $(subst 64,x86_64,$(subst 32,i686,$(WIN)))-pc-windows-gnu @@ -92,9 +113,6 @@ STATLIB = $(LIBDIR)/libtestpkg.a PKG_LIBS = -L$(LIBDIR) -ltestpkg -lws2_32 -ladvapi32 -luserenv -lbcrypt -lntdll - # Print linked static libraries at compile time - export RUSTFLAGS += --print=native-static-libs - all: C_clean $(SHLIB): $(STATLIB) @@ -111,18 +129,31 @@ # 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 \ + 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 + C_clean: rm -Rf $(SHLIB) $(STATLIB) $(OBJECTS) @@ -213,8 +244,8 @@ use_extendr() Message > File 'src/entrypoint.c' already exists. Skip writing the file. - > File 'src/Makevars' already exists. Skip writing the file. - > File 'src/Makevars.win' already exists. Skip writing the file. + > File 'src/Makevars.in' already exists. Skip writing the file. + > File 'src/Makevars.win.in' already exists. Skip writing the file. > File 'src/Makevars.ucrt' already exists. Skip writing the file. > File 'src/.gitignore' already exists. Skip writing the file. > File 'src/rust/Cargo.toml' already exists. Skip writing the file. @@ -233,8 +264,8 @@ use_extendr(crate_name = "foo", lib_name = "bar", overwrite = TRUE) Message v Writing 'src/entrypoint.c' - v Writing 'src/Makevars' - v Writing 'src/Makevars.win' + v Writing 'src/Makevars.in' + v Writing 'src/Makevars.win.in' v Writing 'src/Makevars.ucrt' v Writing 'src/.gitignore' v Writing 'src/rust/Cargo.toml' @@ -268,39 +299,56 @@ # use_rextendr_template() can overwrite existing files Code - cat_file("src", "Makevars") + cat_file("src", "Makevars.in") Output TARGET_DIR = ./rust/target LIBDIR = $(TARGET_DIR)/release STATLIB = $(LIBDIR)/libbar.a PKG_LIBS = -L$(LIBDIR) -lbar - # Print linked static libraries at compile time - export RUSTFLAGS += --print=native-static-libs - 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 + # 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): - # 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; \ + + # 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 + 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) + + # 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 + C_clean: rm -Rf $(SHLIB) $(STATLIB) $(OBJECTS) clean: - rm -Rf $(SHLIB) $(STATLIB) $(OBJECTS) $(TARGET_DIR) + rm -Rf $(SHLIB) $(STATLIB) $(OBJECTS) $(TARGET_DIR) $(VENDOR_DIR) diff --git a/tests/testthat/test-cran-compliance.R b/tests/testthat/test-cran-compliance.R new file mode 100644 index 00000000..2e913660 --- /dev/null +++ b/tests/testthat/test-cran-compliance.R @@ -0,0 +1,30 @@ +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) + + package_versions <- vendor_pkgs(path, quiet = TRUE) + expect_snapshot(cat_file("src", "rust", "vendor-config.toml")) + expect_snapshot(package_versions, transform = mask_any_version) + expect_true(file.exists(file.path("src", "rust", "vendor.tar.xz"))) +}) + + +test_that("rextendr passes NOT_CRAN=false checks", { + skip_if_not_installed("usethis") + skip_if_not_installed("rcmdcheck") + + path <- local_package("testpkg") + # write the license file to pass R CMD check + usethis::use_mit_license() + use_extendr() + document() + vendor_pkgs() + res <- rcmdcheck::rcmdcheck(env = c("NOT_CRAN"="false")) + expect_true( + rlang::is_empty(res$errors) && rlang::is_empty(res$warnings) + ) +}) diff --git a/tests/testthat/test-use_cran_defaults.R b/tests/testthat/test-use_cran_defaults.R deleted file mode 100644 index 5b3db4b7..00000000 --- a/tests/testthat/test-use_cran_defaults.R +++ /dev/null @@ -1,41 +0,0 @@ -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(), transform = mask_any_version) - 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")) - expect_snapshot(cat_file("tools", "msrv.R")) -}) - -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, overwrite = TRUE) - - package_versions <- vendor_pkgs(path, quiet = TRUE) - expect_snapshot(cat_file("src", "rust", "vendor-config.toml")) - expect_snapshot(package_versions, transform = mask_any_version) - expect_true(file.exists(file.path("src", "rust", "vendor.tar.xz"))) -}) diff --git a/tests/testthat/test-use_extendr.R b/tests/testthat/test-use_extendr.R index 68c6571d..edae4bac 100644 --- a/tests/testthat/test-use_extendr.R +++ b/tests/testthat/test-use_extendr.R @@ -18,8 +18,8 @@ test_that("use_extendr() sets up extendr files correctly", { expect_true(dir.exists(file.path("src", "rust", "src"))) expect_snapshot(cat_file("R", "extendr-wrappers.R")) - expect_snapshot(cat_file("src", "Makevars")) - expect_snapshot(cat_file("src", "Makevars.win")) + expect_snapshot(cat_file("src", "Makevars.in")) + expect_snapshot(cat_file("src", "Makevars.win.in")) expect_snapshot(cat_file("src", "Makevars.ucrt")) expect_snapshot(cat_file("src", "entrypoint.c")) expect_snapshot(cat_file("src", "testpkg-win.def")) @@ -87,23 +87,23 @@ test_that("use_rextendr_template() can overwrite existing files", { path <- local_package("testpkg.wrap") dir.create("src") - file_path <- file.path("src", "Makevars") + file_path <- file.path("src", "Makevars.in") use_rextendr_template( - "Makevars", + "Makevars.in", save_as = file_path, quiet = TRUE, data = list(lib_name = "foo") ) use_rextendr_template( - "Makevars", + "Makevars.in", save_as = file_path, quiet = TRUE, overwrite = TRUE, data = list(lib_name = "bar") ) - expect_snapshot(cat_file("src", "Makevars")) + expect_snapshot(cat_file("src", "Makevars.in")) }) # Check that {rextendr} works in packages containing dots in their names. @@ -140,7 +140,7 @@ test_that("use_extendr() handles R package name, crate name and library name sep test_that("use_extendr() does not allow invalid rust names", { skip_if_not_installed("usethis") - path <- local_package("testPackage") + path <- local_package("testPackage") expect_rextendr_error(use_extendr(crate_name = "22unsupported")) expect_rextendr_error(use_extendr(lib_name = "@unsupported")) }) @@ -184,3 +184,21 @@ test_that("`use_extendr()` works correctly when path is specified explicitly", { use_extendr(path = "testpkg") succeed() }) + + +test_that("`use_extendr()` passes R CMD check", { + skip_if_not_installed("usethis") + skip_if_not_installed("rcmdcheck") + + path <- local_package("testpkg") + # write the license file to pass R CMD check + usethis::use_mit_license() + use_extendr() + # store results + res <- rcmdcheck::rcmdcheck() + + # check the output + expect_true( + rlang::is_empty(res$errors) && rlang::is_empty(res$warnings) + ) +}) From 1e10ecca5505d13bce606ac121f78f3001330386 Mon Sep 17 00:00:00 2001 From: Josiah Parry Date: Sat, 9 Nov 2024 09:52:13 -0800 Subject: [PATCH 05/12] remove unneeded rm -rf from makevars fix indentation etc --- R/use_extendr.R | 2 + inst/templates/Makevars.in | 8 +--- inst/templates/Makevars.win.in | 62 ++++++++++++--------------- inst/templates/configure.win | 2 +- tests/testthat/test-cran-compliance.R | 24 +++++------ tests/testthat/test-use_extendr.R | 1 + 6 files changed, 45 insertions(+), 54 deletions(-) diff --git a/R/use_extendr.R b/R/use_extendr.R index abcfe318..e33be192 100644 --- a/R/use_extendr.R +++ b/R/use_extendr.R @@ -222,6 +222,8 @@ use_extendr <- function(path = ".", # is built. This is handled via the configure file usethis::use_build_ignore("src/Makevars") usethis::use_git_ignore("src/Makevars") + usethis::use_build_ignore("src/Makevars.win") + usethis::use_git_ignore("src/Makevars.win") if (!isTRUE(quiet)) { cli::cli_alert_success("Finished configuring {.pkg extendr} for package {.pkg {pkg_name}}.") diff --git a/inst/templates/Makevars.in b/inst/templates/Makevars.in index 19d72678..de61879c 100644 --- a/inst/templates/Makevars.in +++ b/inst/templates/Makevars.in @@ -35,13 +35,7 @@ $(STATLIB): RUSTFLAGS="$(RUSTFLAGS) --print=native-static-libs" cargo build @CRAN_FLAGS@ --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 + rm -Rf $(CARGOTMP); C_clean: rm -Rf $(SHLIB) $(STATLIB) $(OBJECTS) diff --git a/inst/templates/Makevars.win.in b/inst/templates/Makevars.win.in index b612d14a..fab992ff 100644 --- a/inst/templates/Makevars.win.in +++ b/inst/templates/Makevars.win.in @@ -12,42 +12,36 @@ $(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 - - # Handle NOT_CRAN case: If NOT_CRAN is false, vendor tarball should be handled - 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 - - # 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; \ + 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 + + # Handle NOT_CRAN case: If NOT_CRAN is false, vendor tarball should be handled + 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 + # 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); + C_clean: - rm -Rf $(SHLIB) $(STATLIB) $(OBJECTS) + rm -Rf $(SHLIB) $(STATLIB) $(OBJECTS) clean: - rm -Rf $(SHLIB) $(STATLIB) $(OBJECTS) $(TARGET_DIR) + rm -Rf $(SHLIB) $(STATLIB) $(OBJECTS) $(TARGET_DIR) diff --git a/inst/templates/configure.win b/inst/templates/configure.win index 957bc2fe..f3867c45 100644 --- a/inst/templates/configure.win +++ b/inst/templates/configure.win @@ -9,7 +9,7 @@ else fi # delete Makevars if it is present -[ -f src/Makevars ] && rm src/Makevars +[ -f src/Makevars ] && rm src/Makevars.win # 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 \ No newline at end of file diff --git a/tests/testthat/test-cran-compliance.R b/tests/testthat/test-cran-compliance.R index 2e913660..c250347f 100644 --- a/tests/testthat/test-cran-compliance.R +++ b/tests/testthat/test-cran-compliance.R @@ -14,17 +14,17 @@ test_that("vendor_pkgs() vendors dependencies", { test_that("rextendr passes NOT_CRAN=false checks", { - skip_if_not_installed("usethis") - skip_if_not_installed("rcmdcheck") + skip_if_not_installed("usethis") + skip_if_not_installed("rcmdcheck") - path <- local_package("testpkg") - # write the license file to pass R CMD check - usethis::use_mit_license() - use_extendr() - document() - vendor_pkgs() - res <- rcmdcheck::rcmdcheck(env = c("NOT_CRAN"="false")) - expect_true( - rlang::is_empty(res$errors) && rlang::is_empty(res$warnings) - ) + path <- local_package("testpkg") + # write the license file to pass R CMD check + usethis::use_mit_license() + use_extendr() + document() + vendor_pkgs() + res <- rcmdcheck::rcmdcheck(env = c("NOT_CRAN" = "false")) + expect_true( + rlang::is_empty(res$errors) && rlang::is_empty(res$warnings) + ) }) diff --git a/tests/testthat/test-use_extendr.R b/tests/testthat/test-use_extendr.R index edae4bac..f8e19296 100644 --- a/tests/testthat/test-use_extendr.R +++ b/tests/testthat/test-use_extendr.R @@ -194,6 +194,7 @@ test_that("`use_extendr()` passes R CMD check", { # write the license file to pass R CMD check usethis::use_mit_license() use_extendr() + document() # store results res <- rcmdcheck::rcmdcheck() From 4404db8d03692445f78dd9b4293ef7ba3cd00e4d Mon Sep 17 00:00:00 2001 From: Josiah Parry Date: Fri, 15 Nov 2024 14:06:14 -0800 Subject: [PATCH 06/12] add rcmdcheck to suggests update makevars templates to check for NOT_CRAN != true --- DESCRIPTION | 1 + inst/templates/Makevars.in | 6 +- inst/templates/Makevars.win.in | 5 +- tests/testthat/_snaps/use_extendr.md | 91 ++++++++++++---------------- 4 files changed, 45 insertions(+), 58 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 65722059..f3a90aef 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -57,6 +57,7 @@ Imports: withr Suggests: devtools, + rcmdcheck, knitr, lintr, rmarkdown, diff --git a/inst/templates/Makevars.in b/inst/templates/Makevars.in index de61879c..1f947ad7 100644 --- a/inst/templates/Makevars.in +++ b/inst/templates/Makevars.in @@ -17,12 +17,12 @@ VENDOR_DIR = $(CURDIR)/vendor # CRAN note: Cargo and Rustc versions are reported during # 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 +# When the NOT_CRAN flag is *not* set, the vendor.tar.xz, if present, +# is unzipped and used for offline compilation. $(STATLIB): # Check if NOT_CRAN is false and unzip vendor.tar.xz if so - if [ "$(NOT_CRAN)" = "false" ]; then \ + if [ "$(NOT_CRAN)" != "true" ]; then \ if [ -f ./rust/vendor.tar.xz ]; then \ tar xf rust/vendor.tar.xz && \ mkdir -p $(CARGOTMP) && \ diff --git a/inst/templates/Makevars.win.in b/inst/templates/Makevars.win.in index fab992ff..06d5a2ec 100644 --- a/inst/templates/Makevars.win.in +++ b/inst/templates/Makevars.win.in @@ -21,8 +21,9 @@ $(STATLIB): # https://github.com/r-windows/rtools-packages/blob/2407b23f1e0925bbb20a4162c963600105236318/mingw-w64-gcc/PKGBUILD#L313-L316 touch $(TARGET_DIR)/libgcc_mock/libgcc_eh.a - # Handle NOT_CRAN case: If NOT_CRAN is false, vendor tarball should be handled - if [ "$(NOT_CRAN)" = "false" ]; then \ + # When the NOT_CRAN flag is *not* set, the vendor.tar.xz, if present, + # is unzipped and used for offline compilation. + if [ "$(NOT_CRAN)" != "true" ]; then \ if [ -f ./rust/vendor.tar.xz ]; then \ tar xf rust/vendor.tar.xz && \ mkdir -p $(CARGOTMP) && \ diff --git a/tests/testthat/_snaps/use_extendr.md b/tests/testthat/_snaps/use_extendr.md index f22750f7..2fbd3645 100644 --- a/tests/testthat/_snaps/use_extendr.md +++ b/tests/testthat/_snaps/use_extendr.md @@ -24,6 +24,8 @@ v Adding "src/rust/vendor" to '.gitignore'. v Adding "^src/Makevars$" to '.Rbuildignore'. v Adding "src/Makevars" to '.gitignore'. + v Adding "^src/Makevars\\.win$" to '.Rbuildignore'. + v Adding "src/Makevars.win" to '.gitignore'. v Finished configuring extendr for package testpkg. * Please run `rextendr::document()` for changes to take effect. @@ -69,12 +71,12 @@ # CRAN note: Cargo and Rustc versions are reported during # 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 + # When the NOT_CRAN flag is *not* set, the vendor.tar.xz, if present, + # is unzipped and used for offline compilation. $(STATLIB): # Check if NOT_CRAN is false and unzip vendor.tar.xz if so - if [ "$(NOT_CRAN)" = "false" ]; then \ + if [ "$(NOT_CRAN)" != "true" ]; then \ if [ -f ./rust/vendor.tar.xz ]; then \ tar xf rust/vendor.tar.xz && \ mkdir -p $(CARGOTMP) && \ @@ -87,13 +89,7 @@ RUSTFLAGS="$(RUSTFLAGS) --print=native-static-libs" cargo build @CRAN_FLAGS@ --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 + rm -Rf $(CARGOTMP); C_clean: rm -Rf $(SHLIB) $(STATLIB) $(OBJECTS) @@ -120,45 +116,40 @@ 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 - - # Handle NOT_CRAN case: If NOT_CRAN is false, vendor tarball should be handled - 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 - - # 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) + 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 + + # When the NOT_CRAN flag is *not* set, the vendor.tar.xz, if present, + # is unzipped and used for offline compilation. + if [ "$(NOT_CRAN)" != "true" ]; 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 - # Always clean up CARGOTMP - rm -Rf $(CARGOTMP) + # 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) - if [ "$(NOT_CRAN)" != "false" ]; then \ - rm -Rf $(VENDOR_DIR); \ - rm -Rf $(TARGET_DIR); \ - rm -Rf $(LIBDIR)/build; \ - fi + # Always clean up CARGOTMP + rm -Rf $(CARGOTMP); C_clean: - rm -Rf $(SHLIB) $(STATLIB) $(OBJECTS) + rm -Rf $(SHLIB) $(STATLIB) $(OBJECTS) clean: - rm -Rf $(SHLIB) $(STATLIB) $(OBJECTS) $(TARGET_DIR) + rm -Rf $(SHLIB) $(STATLIB) $(OBJECTS) $(TARGET_DIR) --- @@ -320,12 +311,12 @@ # CRAN note: Cargo and Rustc versions are reported during # 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 + # When the NOT_CRAN flag is *not* set, the vendor.tar.xz, if present, + # is unzipped and used for offline compilation. $(STATLIB): # Check if NOT_CRAN is false and unzip vendor.tar.xz if so - if [ "$(NOT_CRAN)" = "false" ]; then \ + if [ "$(NOT_CRAN)" != "true" ]; then \ if [ -f ./rust/vendor.tar.xz ]; then \ tar xf rust/vendor.tar.xz && \ mkdir -p $(CARGOTMP) && \ @@ -338,13 +329,7 @@ RUSTFLAGS="$(RUSTFLAGS) --print=native-static-libs" cargo build @CRAN_FLAGS@ --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 + rm -Rf $(CARGOTMP); C_clean: rm -Rf $(SHLIB) $(STATLIB) $(OBJECTS) From 426400c1dcdf163f9c855d25cae8e903b101bb2b Mon Sep 17 00:00:00 2001 From: Josiah Parry Date: Fri, 15 Nov 2024 15:26:05 -0800 Subject: [PATCH 07/12] update tests --- NEWS.md | 1 + R/cran-compliance.R | 32 ++++++++++- inst/templates/configure | 2 +- inst/templates/configure.win | 2 +- man/cran.Rd | 51 +++++++++--------- man/vendor_pkgs.Rd | 37 +++++++++++++ tests/testthat/_snaps/use_extendr.md | 33 ++++-------- tests/testthat/test-cran-compliance.R | 14 ++++- tests/testthat/test-use_extendr.R | 13 ++--- vignettes/articles/cran-compliance.Rmd | 74 -------------------------- 10 files changed, 125 insertions(+), 134 deletions(-) create mode 100644 man/vendor_pkgs.Rd delete mode 100644 vignettes/articles/cran-compliance.Rmd diff --git a/NEWS.md b/NEWS.md index 075c7108..ebe8e1c2 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,6 @@ # rextendr (development version) +* `use_cran_default()` has been removed as the default package template is CRAN compatible * `use_extendr()` now creates `tools/msrv.R`, `configure` and `configure.win`. These have been moved out of `use_cran_defaults()` * `Makevars` now prints linked static libraries at compile time by adding `--print=native-static-libs` to `RUSTFLAGS` * `use_extendr()` sets the `DESCRIPTION`'s `SystemRequirements` field according to CRAN policy to `Cargo (Rust's package manager), rustc` (#329) diff --git a/R/cran-compliance.R b/R/cran-compliance.R index 154fa1ef..1af01806 100644 --- a/R/cran-compliance.R +++ b/R/cran-compliance.R @@ -16,7 +16,6 @@ #' \dontrun{ #' vendor_pkgs() #' } -#' @name cran #' @export vendor_pkgs <- function(path = ".", quiet = FALSE, overwrite = NULL) { stderr_line_callback <- function(x, proc) { @@ -123,3 +122,34 @@ vendor_pkgs <- function(path = ".", quiet = FALSE, overwrite = NULL) { # return packages and versions invisibly invisible(res) } + + +#' CRAN compliant extendr packages +#' +#' R packages developed using extendr are not immediately ready to +#' be published to CRAN. The extendr package template ensures that +#' CRAN publication is (farily) painless. +#' +#' @section CRAN requirements: +#' +#' In order to publish a Rust based package on CRAN it must meet certain +#' requirements. These are: +#' +#' - Rust dependencies are vendored +#' - The package is compiled offline +#' - the `DESCRIPTION` file's `SystemRequirements` field contains `Cargo (Rust's package manager), rustc` +#' +#' The extendr templates handle all of this _except_ vendoring dependencies. +#' This must be done prior to publication using [`vendor_pkgs()`]. +#' +#' In addition, it is important to make sure that CRAN maintainers +#' are aware that the package they are checking contains Rust code. +#' Depending on which and how many crates are used as a dependencies +#' the `vendor.tar.xz` will be larger than a few megabytes. If a +#' built package is larger than 5mbs CRAN may reject the submission. +#' +#' To prevent rejection make a note in your `cran-comments.md` file +#' (create one using [`usethis::use_cran_comments()`]) along the lines of +#' "The package tarball is 6mb because Rust dependencies are vendored within src/rust/vendor.tar.xz which is 5.9mb." +#' @name cran +NULL \ No newline at end of file diff --git a/inst/templates/configure b/inst/templates/configure index ba384901..822ea414 100644 --- a/inst/templates/configure +++ b/inst/templates/configure @@ -3,7 +3,7 @@ "${R_HOME}/bin/Rscript" tools/msrv.R # Set CRAN_FLAGS based on the NOT_CRAN value -if [ "${NOT_CRAN}" = "false" ]; then +if [ "${NOT_CRAN}" != "true" ]; then export CRAN_FLAGS="-j 2 --offline" else export CRAN_FLAGS="" diff --git a/inst/templates/configure.win b/inst/templates/configure.win index f3867c45..bb18d407 100644 --- a/inst/templates/configure.win +++ b/inst/templates/configure.win @@ -2,7 +2,7 @@ "${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 +if [ "${NOT_CRAN}" != "true" ]; then export CRAN_FLAGS="-j 2 --offline" else export CRAN_FLAGS="" diff --git a/man/cran.Rd b/man/cran.Rd index 8c410182..05a481fe 100644 --- a/man/cran.Rd +++ b/man/cran.Rd @@ -2,37 +2,34 @@ % Please edit documentation in R/cran-compliance.R \name{cran} \alias{cran} -\alias{vendor_pkgs} -\title{Vendor Rust dependencies} -\usage{ -vendor_pkgs(path = ".", quiet = FALSE, overwrite = NULL) +\title{CRAN compliant extendr packages} +\description{ +R packages developed using extendr are not immediately ready to +be published to CRAN. The extendr package template ensures that +CRAN publication is (farily) painless. } -\arguments{ -\item{path}{File path to the package for which to generate wrapper code.} +\section{CRAN requirements}{ -\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.} -} -\value{ +In order to publish a Rust based package on CRAN it must meet certain +requirements. These are: \itemize{ -\item \code{vendor_pkgs()} returns a data.frame with two columns \code{crate} and \code{version} -} -} -\description{ -\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 +\item Rust dependencies are vendored +\item The package is compiled offline +\item the \code{DESCRIPTION} file's \code{SystemRequirements} field contains \verb{Cargo (Rust's package manager), rustc} } -\examples{ -\dontrun{ - vendor_pkgs() -} +The extendr templates handle all of this \emph{except} vendoring dependencies. +This must be done prior to publication using \code{\link[=vendor_pkgs]{vendor_pkgs()}}. + +In addition, it is important to make sure that CRAN maintainers +are aware that the package they are checking contains Rust code. +Depending on which and how many crates are used as a dependencies +the \code{vendor.tar.xz} will be larger than a few megabytes. If a +built package is larger than 5mbs CRAN may reject the submission. + +To prevent rejection make a note in your \code{cran-comments.md} file +(create one using \code{\link[usethis:use_cran_comments]{usethis::use_cran_comments()}}) along the lines of +"The package tarball is 6mb because Rust dependencies are vendored within src/rust/vendor.tar.xz which is 5.9mb." } + diff --git a/man/vendor_pkgs.Rd b/man/vendor_pkgs.Rd new file mode 100644 index 00000000..98e75d57 --- /dev/null +++ b/man/vendor_pkgs.Rd @@ -0,0 +1,37 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/cran-compliance.R +\name{vendor_pkgs} +\alias{vendor_pkgs} +\title{Vendor Rust dependencies} +\usage{ +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.} +} +\value{ +\itemize{ +\item \code{vendor_pkgs()} returns a data.frame with two columns \code{crate} and \code{version} +} +} +\description{ +\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{ + +\dontrun{ + vendor_pkgs() +} +} diff --git a/tests/testthat/_snaps/use_extendr.md b/tests/testthat/_snaps/use_extendr.md index 2fbd3645..168f3319 100644 --- a/tests/testthat/_snaps/use_extendr.md +++ b/tests/testthat/_snaps/use_extendr.md @@ -7,18 +7,18 @@ i Setting `Config/rextendr/version` to "*.*.*" in the 'DESCRIPTION' file. i Setting `SystemRequirements` to "Cargo (Rust's package manager), rustc" in the 'DESCRIPTION' file. v Creating 'src/rust/src'. - v Writing 'src/entrypoint.c' - v Writing 'src/Makevars.in' - v Writing 'src/Makevars.win.in' - v Writing 'src/Makevars.ucrt' - v Writing 'src/.gitignore' - v Writing 'src/rust/Cargo.toml' - v Writing 'src/rust/src/lib.rs' - v Writing 'src/testpkg-win.def' + v Writing 'src/entrypoint.c'. + v Writing 'src/Makevars.in'. + v Writing 'src/Makevars.win.in'. + v Writing 'src/Makevars.ucrt'. + v Writing 'src/.gitignore'. + 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 Writing 'tools/msrv.R' - v Writing 'configure' - v Writing 'configure.win' + v Writing 'tools/msrv.R'. + v Writing 'configure'. + v Writing 'configure.win'. v Adding "^src/\\.cargo$" to '.Rbuildignore'. v Adding "^src/rust/vendor$" to '.Rbuildignore'. v Adding "src/rust/vendor" to '.gitignore'. @@ -234,18 +234,7 @@ Code use_extendr() Message - > File 'src/entrypoint.c' already exists. Skip writing the file. - > File 'src/Makevars.in' already exists. Skip writing the file. - > File 'src/Makevars.win.in' already exists. Skip writing the file. - > File 'src/Makevars.ucrt' already exists. Skip writing the file. - > File 'src/.gitignore' already exists. Skip writing the file. - > File 'src/rust/Cargo.toml' already exists. Skip writing the file. - > File 'src/rust/src/lib.rs' already exists. Skip writing the file. - > File 'src/testpkg.wrap-win.def' already exists. Skip writing the file. > File 'R/extendr-wrappers.R' already exists. Skip writing the file. - > File 'tools/msrv.R' already exists. Skip writing the file. - > File 'configure' already exists. Skip writing the file. - > File 'configure.win' already exists. Skip writing the file. v Finished configuring extendr for package testpkg.wrap. * Please run `rextendr::document()` for changes to take effect. diff --git a/tests/testthat/test-cran-compliance.R b/tests/testthat/test-cran-compliance.R index c250347f..ecb8a52f 100644 --- a/tests/testthat/test-cran-compliance.R +++ b/tests/testthat/test-cran-compliance.R @@ -13,7 +13,7 @@ test_that("vendor_pkgs() vendors dependencies", { }) -test_that("rextendr passes NOT_CRAN=false checks", { +test_that("rextendr passes CRAN checks", { skip_if_not_installed("usethis") skip_if_not_installed("rcmdcheck") @@ -23,7 +23,17 @@ test_that("rextendr passes NOT_CRAN=false checks", { use_extendr() document() vendor_pkgs() - res <- rcmdcheck::rcmdcheck(env = c("NOT_CRAN" = "false")) + res <- rcmdcheck::rcmdcheck(env = c("NOT_CRAN" = "")) + + # --offline flag should be set + expect_true(grepl("--offline", res$install_out)) + # -j 2 flag should be set + expect_true(grepl("-j 2", res$install_out)) + + # "Downloading" should not be present + expect_false(grepl("Downloading", res$install_out)) + + expect_true( rlang::is_empty(res$errors) && rlang::is_empty(res$warnings) ) diff --git a/tests/testthat/test-use_extendr.R b/tests/testthat/test-use_extendr.R index f8e19296..a8a42123 100644 --- a/tests/testthat/test-use_extendr.R +++ b/tests/testthat/test-use_extendr.R @@ -9,8 +9,8 @@ test_that("use_extendr() sets up extendr files correctly", { # DESCRITION file version_in_desc <- stringi::stri_trim_both(desc::desc_get("Config/rextendr/version", path)[[1]]) sysreq_in_desc <- stringi::stri_trim_both(desc::desc_get("SystemRequirements", path)[[1]]) - expect_equal(version_in_desc, as.character(packageVersion("rextendr"))) - expect_equal(sysreq_in_desc, "Cargo (Rust's package manager), rustc") + expect_identical(version_in_desc, as.character(packageVersion("rextendr"))) + expect_identical(sysreq_in_desc, "Cargo (Rust's package manager), rustc") # directory structure expect_true(dir.exists("src")) @@ -119,7 +119,7 @@ test_that("use_extendr() handles R packages with dots in the name", { use_extendr() document() devtools::load_all() - expect_equal(hello_world(), "Hello world!") + expect_identical(hello_world(), "Hello world!") }) # Specify crate name and library names explicitly @@ -133,7 +133,7 @@ test_that("use_extendr() handles R package name, crate name and library name sep use_extendr(crate_name = "crate_name", lib_name = "lib_name") document() devtools::load_all() - expect_equal(hello_world(), "Hello world!") + expect_identical(hello_world(), "Hello world!") }) # Pass unsupported values to `crate_name` and `lib_name` and expect errors. @@ -173,7 +173,7 @@ test_that("Message if the SystemRequirements field is already set.", { ) expect_true(created) - expect_equal(desc::desc_get("SystemRequirements")[[1]], sys_req) + expect_identical(desc::desc_get("SystemRequirements")[[1]], sys_req) }) test_that("`use_extendr()` works correctly when path is specified explicitly", { @@ -195,8 +195,9 @@ test_that("`use_extendr()` passes R CMD check", { usethis::use_mit_license() use_extendr() document() + # store results - res <- rcmdcheck::rcmdcheck() + res <- rcmdcheck::rcmdcheck(env = c("NOT_CRAN"="true")) # check the output expect_true( diff --git a/vignettes/articles/cran-compliance.Rmd b/vignettes/articles/cran-compliance.Rmd deleted file mode 100644 index ba43272b..00000000 --- a/vignettes/articles/cran-compliance.Rmd +++ /dev/null @@ -1,74 +0,0 @@ ---- -title: "CRAN compliant extendr packages" -author: "Josiah Parry" ---- - -```{r, include = FALSE} -knitr::opts_chunk$set( - collapse = TRUE, - comment = "#>" -) -``` - -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 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. - -## `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` 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. - -## `cargo build` settings - -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 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. - -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 - -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 -- 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 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 `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 - -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 95a2b61cba2aa1f93d80777039f5ccaba7120df9 Mon Sep 17 00:00:00 2001 From: Josiah Parry Date: Sat, 16 Nov 2024 09:17:43 -0800 Subject: [PATCH 08/12] refactor use_extendr tests --- _pkgdown.yml | 1 + tests/testthat/_snaps/use_extendr.md | 257 ++++++++++++++++++++++---- tests/testthat/test-cran-compliance.R | 1 - tests/testthat/test-use_extendr.R | 14 +- 4 files changed, 228 insertions(+), 45 deletions(-) diff --git a/_pkgdown.yml b/_pkgdown.yml index eddfcfa1..f01456f1 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -22,6 +22,7 @@ reference: - write_license_note - clean - cran + - vendor_pkgs - use_msrv - title: Various utility functions diff --git a/tests/testthat/_snaps/use_extendr.md b/tests/testthat/_snaps/use_extendr.md index 168f3319..02b3cc7a 100644 --- a/tests/testthat/_snaps/use_extendr.md +++ b/tests/testthat/_snaps/use_extendr.md @@ -1,33 +1,186 @@ # use_extendr() sets up extendr files correctly Code - use_extendr() - Message - i First time using rextendr. Upgrading automatically... - i Setting `Config/rextendr/version` to "*.*.*" in the 'DESCRIPTION' file. - i Setting `SystemRequirements` to "Cargo (Rust's package manager), rustc" in the 'DESCRIPTION' file. - v Creating 'src/rust/src'. - v Writing 'src/entrypoint.c'. - v Writing 'src/Makevars.in'. - v Writing 'src/Makevars.win.in'. - v Writing 'src/Makevars.ucrt'. - v Writing 'src/.gitignore'. - 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 Writing 'tools/msrv.R'. - v Writing 'configure'. - v Writing 'configure.win'. - v Adding "^src/\\.cargo$" to '.Rbuildignore'. - v Adding "^src/rust/vendor$" to '.Rbuildignore'. - v Adding "src/rust/vendor" to '.gitignore'. - v Adding "^src/Makevars$" to '.Rbuildignore'. - v Adding "src/Makevars" to '.gitignore'. - v Adding "^src/Makevars\\.win$" to '.Rbuildignore'. - v Adding "src/Makevars.win" to '.gitignore'. - v Finished configuring extendr for package testpkg. - * Please run `rextendr::document()` for changes to take effect. + cat_file(".gitignore") + Output + src/rust/vendor + src/Makevars + src/Makevars.win + +--- + + Code + cat_file(".Rbuildignore") + Output + ^src/\.cargo$ + ^src/rust/vendor$ + ^src/Makevars$ + ^src/Makevars\.win$ + +--- + + Code + cat_file("configure") + Output + #!/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}" != "true" ]; 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 + +--- + + Code + cat_file("configure.win") + Output + #!/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}" != "true" ]; then + export CRAN_FLAGS="-j 2 --offline" + else + export CRAN_FLAGS="" + fi + + # delete Makevars if it is present + [ -f src/Makevars ] && rm src/Makevars.win + + # 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 + +--- + + Code + cat_file("tools", "msrv.R") + Output + # read the DESCRIPTION file + desc <- read.dcf("DESCRIPTION") + + if (!"SystemRequirements" %in% colnames(desc)) { + fmt <- c( + "`SystemRequirements` not found in `DESCRIPTION`.", + "Please specify `SystemRequirements: Cargo (Rust's package manager), rustc`" + ) + stop(paste(fmt, collapse = "\n")) + } + + # extract system requirements + sysreqs <- desc[, "SystemRequirements"] + + # check that cargo and rustc is found + if (!grepl("cargo", sysreqs, ignore.case = TRUE)) { + stop("You must specify `Cargo (Rust's package manager)` in your `SystemRequirements`") + } + + if (!grepl("rustc", sysreqs, ignore.case = TRUE)) { + stop("You must specify `Cargo (Rust's package manager), rustc` in your `SystemRequirements`") + } + + # split into parts + parts <- strsplit(sysreqs, ", ")[[1]] + + # identify which is the rustc + rustc_ver <- parts[grepl("rustc", parts)] + + # perform checks for the presence of rustc and cargo on the OS + no_cargo_msg <- c( + "----------------------- [CARGO NOT FOUND]--------------------------", + "The 'cargo' command was not found on the PATH. Please install Cargo", + "from: https://www.rust-lang.org/tools/install", + "", + "Alternatively, you may install Cargo from your OS package manager:", + " - Debian/Ubuntu: apt-get install cargo", + " - Fedora/CentOS: dnf install cargo", + " - macOS: brew install rustc", + "-------------------------------------------------------------------" + ) + + no_rustc_msg <- c( + "----------------------- [RUST NOT FOUND]---------------------------", + "The 'rustc' compiler was not found on the PATH. Please install", + paste(rustc_ver, "or higher from:"), + "https://www.rust-lang.org/tools/install", + "", + "Alternatively, you may install Rust from your OS package manager:", + " - Debian/Ubuntu: apt-get install rustc", + " - Fedora/CentOS: dnf install rustc", + " - macOS: brew install rustc", + "-------------------------------------------------------------------" + ) + + # Add {user}/.cargo/bin to path before checking + new_path <- paste0( + Sys.getenv("PATH"), + ":", + paste0(Sys.getenv("HOME"), "/.cargo/bin") + ) + + # set the path with the new path + Sys.setenv("PATH" = new_path) + + # check for rustc installation + rustc_version <- tryCatch( + system("rustc --version", intern = TRUE), + error = function(e) { + stop(paste(no_rustc_msg, collapse = "\n")) + } + ) + + # check for cargo installation + cargo_version <- tryCatch( + system("cargo --version", intern = TRUE), + error = function(e) { + stop(paste(no_cargo_msg, collapse = "\n")) + } + ) + + # helper function to extract versions + extract_semver <- function(ver) { + if (grepl("\\d+\\.\\d+(\\.\\d+)?", ver)) { + sub(".*?(\\d+\\.\\d+(\\.\\d+)?).*", "\\1", ver) + } else { + NA + } + } + + # get the MSRV + msrv <- extract_semver(rustc_ver) + + # extract current version + current_rust_version <- extract_semver(rustc_version) + + # perform check + if (!is.na(msrv)) { + # -1 when current version is later + # 0 when they are the same + # 1 when MSRV is newer than current + is_msrv <- utils::compareVersion(msrv, current_rust_version) + if (is_msrv == 1) { + fmt <- paste0( + "\n------------------ [UNSUPPORTED RUST VERSION]------------------\n", + "- Minimum supported Rust version is %s.\n", + "- Installed Rust version is %s.\n", + "---------------------------------------------------------------" + ) + stop(sprintf(fmt, msrv, current_rust_version)) + } + } + + # print the versions + versions_fmt <- "Using %s\nUsing %s" + message(sprintf(versions_fmt, cargo_version, rustc_version)) --- @@ -47,6 +200,17 @@ # nolint end +--- + + Code + cat_file("src", ".gitignore") + Output + *.o + *.so + *.dll + target + .cargo + --- Code @@ -97,6 +261,20 @@ clean: rm -Rf $(SHLIB) $(STATLIB) $(OBJECTS) $(TARGET_DIR) $(VENDOR_DIR) +--- + + Code + cat_file("src", "entrypoint.c") + Output + // We need to forward routine registration from C to Rust + // to avoid the linker removing the static library. + + void R_init_testpkg_extendr(void *dll); + + void R_init_testpkg(void *dll) { + R_init_testpkg_extendr(dll); + } + --- Code @@ -162,20 +340,6 @@ include Makevars.win ---- - - Code - cat_file("src", "entrypoint.c") - Output - // We need to forward routine registration from C to Rust - // to avoid the linker removing the static library. - - void R_init_testpkg_extendr(void *dll); - - void R_init_testpkg(void *dll) { - R_init_testpkg_extendr(dll); - } - --- Code @@ -234,7 +398,18 @@ Code use_extendr() Message + > File 'src/entrypoint.c' already exists. Skip writing the file. + > File 'src/Makevars.in' already exists. Skip writing the file. + > File 'src/Makevars.win.in' already exists. Skip writing the file. + > File 'src/Makevars.ucrt' already exists. Skip writing the file. + > File 'src/.gitignore' already exists. Skip writing the file. + > File 'src/rust/Cargo.toml' already exists. Skip writing the file. + > File 'src/rust/src/lib.rs' already exists. Skip writing the file. + > File 'src/testpkg.wrap-win.def' already exists. Skip writing the file. > File 'R/extendr-wrappers.R' already exists. Skip writing the file. + > File 'tools/msrv.R' already exists. Skip writing the file. + > File 'configure' already exists. Skip writing the file. + > File 'configure.win' already exists. Skip writing the file. v Finished configuring extendr for package testpkg.wrap. * Please run `rextendr::document()` for changes to take effect. diff --git a/tests/testthat/test-cran-compliance.R b/tests/testthat/test-cran-compliance.R index ecb8a52f..4c1e505e 100644 --- a/tests/testthat/test-cran-compliance.R +++ b/tests/testthat/test-cran-compliance.R @@ -32,7 +32,6 @@ test_that("rextendr passes CRAN checks", { # "Downloading" should not be present expect_false(grepl("Downloading", res$install_out)) - expect_true( rlang::is_empty(res$errors) && rlang::is_empty(res$warnings) diff --git a/tests/testthat/test-use_extendr.R b/tests/testthat/test-use_extendr.R index a8a42123..a770b098 100644 --- a/tests/testthat/test-use_extendr.R +++ b/tests/testthat/test-use_extendr.R @@ -4,7 +4,7 @@ test_that("use_extendr() sets up extendr files correctly", { path <- local_package("testpkg") # capture setup messages withr::local_options(usethis.quiet = FALSE) - expect_snapshot(use_extendr(), transform = mask_any_version) + use_extendr() # DESCRITION file version_in_desc <- stringi::stri_trim_both(desc::desc_get("Config/rextendr/version", path)[[1]]) @@ -14,14 +14,22 @@ test_that("use_extendr() sets up extendr files correctly", { # directory structure expect_true(dir.exists("src")) + expect_true(dir.exists("tools")) expect_true(dir.exists(file.path("src", "rust"))) expect_true(dir.exists(file.path("src", "rust", "src"))) + # ensure all files generated by rextendr are present + expect_snapshot(cat_file(".gitignore")) + expect_snapshot(cat_file(".Rbuildignore")) + expect_snapshot(cat_file("configure")) + expect_snapshot(cat_file("configure.win")) + expect_snapshot(cat_file("tools", "msrv.R")) expect_snapshot(cat_file("R", "extendr-wrappers.R")) + expect_snapshot(cat_file("src", ".gitignore")) expect_snapshot(cat_file("src", "Makevars.in")) + expect_snapshot(cat_file("src", "entrypoint.c")) expect_snapshot(cat_file("src", "Makevars.win.in")) expect_snapshot(cat_file("src", "Makevars.ucrt")) - expect_snapshot(cat_file("src", "entrypoint.c")) expect_snapshot(cat_file("src", "testpkg-win.def")) expect_snapshot(cat_file("src", "rust", "Cargo.toml")) expect_snapshot(cat_file("src", "rust", "src", "lib.rs")) @@ -195,7 +203,7 @@ test_that("`use_extendr()` passes R CMD check", { usethis::use_mit_license() use_extendr() document() - + # store results res <- rcmdcheck::rcmdcheck(env = c("NOT_CRAN"="true")) From 38323f57bf0a901209f652c3e0f2f216fee5df55 Mon Sep 17 00:00:00 2001 From: Josiah Parry Date: Sat, 16 Nov 2024 13:27:22 -0800 Subject: [PATCH 09/12] there is a bug in rcmdcheck --- R/cran-compliance.R | 37 +++++++++++++-------------- inst/templates/configure.win | 2 +- tests/testthat/_snaps/use_extendr.md | 2 +- tests/testthat/test-cran-compliance.R | 9 +++++-- tests/testthat/test-use_extendr.R | 9 ++++--- 5 files changed, 33 insertions(+), 26 deletions(-) diff --git a/R/cran-compliance.R b/R/cran-compliance.R index 1af01806..57323a28 100644 --- a/R/cran-compliance.R +++ b/R/cran-compliance.R @@ -12,9 +12,8 @@ #' - `vendor_pkgs()` returns a data.frame with two columns `crate` and `version` #' #' @examples -#' #' \dontrun{ -#' vendor_pkgs() +#' vendor_pkgs() #' } #' @export vendor_pkgs <- function(path = ".", quiet = FALSE, overwrite = NULL) { @@ -125,31 +124,31 @@ vendor_pkgs <- function(path = ".", quiet = FALSE, overwrite = NULL) { #' CRAN compliant extendr packages -#' -#' R packages developed using extendr are not immediately ready to -#' be published to CRAN. The extendr package template ensures that +#' +#' R packages developed using extendr are not immediately ready to +#' be published to CRAN. The extendr package template ensures that #' CRAN publication is (farily) painless. -#' -#' @section CRAN requirements: -#' -#' In order to publish a Rust based package on CRAN it must meet certain -#' requirements. These are: +#' +#' @section CRAN requirements: +#' +#' In order to publish a Rust based package on CRAN it must meet certain +#' requirements. These are: #' #' - Rust dependencies are vendored #' - The package is compiled offline #' - the `DESCRIPTION` file's `SystemRequirements` field contains `Cargo (Rust's package manager), rustc` -#' -#' The extendr templates handle all of this _except_ vendoring dependencies. +#' +#' The extendr templates handle all of this _except_ vendoring dependencies. #' This must be done prior to publication using [`vendor_pkgs()`]. -#' +#' #' In addition, it is important to make sure that CRAN maintainers -#' are aware that the package they are checking contains Rust code. +#' are aware that the package they are checking contains Rust code. #' Depending on which and how many crates are used as a dependencies -#' the `vendor.tar.xz` will be larger than a few megabytes. If a -#' built package is larger than 5mbs CRAN may reject the submission. -#' +#' the `vendor.tar.xz` will be larger than a few megabytes. If a +#' built package is larger than 5mbs CRAN may reject the submission. +#' #' To prevent rejection make a note in your `cran-comments.md` file #' (create one using [`usethis::use_cran_comments()`]) along the lines of -#' "The package tarball is 6mb because Rust dependencies are vendored within src/rust/vendor.tar.xz which is 5.9mb." +#' "The package tarball is 6mb because Rust dependencies are vendored within src/rust/vendor.tar.xz which is 5.9mb." #' @name cran -NULL \ No newline at end of file +NULL diff --git a/inst/templates/configure.win b/inst/templates/configure.win index bb18d407..1c465d5a 100644 --- a/inst/templates/configure.win +++ b/inst/templates/configure.win @@ -12,4 +12,4 @@ fi [ -f src/Makevars ] && rm src/Makevars.win # 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 \ No newline at end of file +sed -e "s|@CRAN_FLAGS@|$CRAN_FLAGS|" src/Makevars.win.in > src/Makevars.win \ No newline at end of file diff --git a/tests/testthat/_snaps/use_extendr.md b/tests/testthat/_snaps/use_extendr.md index 02b3cc7a..3454f723 100644 --- a/tests/testthat/_snaps/use_extendr.md +++ b/tests/testthat/_snaps/use_extendr.md @@ -58,7 +58,7 @@ [ -f src/Makevars ] && rm src/Makevars.win # 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 + sed -e "s|@CRAN_FLAGS@|$CRAN_FLAGS|" src/Makevars.win.in > src/Makevars.win --- diff --git a/tests/testthat/test-cran-compliance.R b/tests/testthat/test-cran-compliance.R index 4c1e505e..1eac8c7d 100644 --- a/tests/testthat/test-cran-compliance.R +++ b/tests/testthat/test-cran-compliance.R @@ -23,14 +23,19 @@ test_that("rextendr passes CRAN checks", { use_extendr() document() vendor_pkgs() - res <- rcmdcheck::rcmdcheck(env = c("NOT_CRAN" = "")) + + res <- rcmdcheck::rcmdcheck( + env = c("NOT_CRAN" = ""), + args = "--no-manual", + libpath = rev(.libPaths()) + ) # --offline flag should be set expect_true(grepl("--offline", res$install_out)) # -j 2 flag should be set expect_true(grepl("-j 2", res$install_out)) - # "Downloading" should not be present + # "Downloading" should not be present expect_false(grepl("Downloading", res$install_out)) expect_true( diff --git a/tests/testthat/test-use_extendr.R b/tests/testthat/test-use_extendr.R index a770b098..52f1f236 100644 --- a/tests/testthat/test-use_extendr.R +++ b/tests/testthat/test-use_extendr.R @@ -148,7 +148,7 @@ test_that("use_extendr() handles R package name, crate name and library name sep test_that("use_extendr() does not allow invalid rust names", { skip_if_not_installed("usethis") - path <- local_package("testPackage") + path <- local_package("testPackage") expect_rextendr_error(use_extendr(crate_name = "22unsupported")) expect_rextendr_error(use_extendr(lib_name = "@unsupported")) }) @@ -205,9 +205,12 @@ test_that("`use_extendr()` passes R CMD check", { document() # store results - res <- rcmdcheck::rcmdcheck(env = c("NOT_CRAN"="true")) + res <- rcmdcheck::rcmdcheck( + args = "--no-manual", + libpath = rev(.libPaths()) + ) - # check the output + # check the output expect_true( rlang::is_empty(res$errors) && rlang::is_empty(res$warnings) ) From 37df69fb1cf6ff4d65f055c12fe3430f756956c4 Mon Sep 17 00:00:00 2001 From: Josiah Parry Date: Sun, 17 Nov 2024 08:59:56 -0800 Subject: [PATCH 10/12] use tabs instead of spaces for Makevars.win.in --- inst/templates/Makevars.win.in | 60 +++++++++++++++++----------------- inst/templates/configure.win | 2 +- 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/inst/templates/Makevars.win.in b/inst/templates/Makevars.win.in index 06d5a2ec..78e0bbfd 100644 --- a/inst/templates/Makevars.win.in +++ b/inst/templates/Makevars.win.in @@ -12,37 +12,37 @@ $(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 - - # When the NOT_CRAN flag is *not* set, the vendor.tar.xz, if present, - # is unzipped and used for offline compilation. - if [ "$(NOT_CRAN)" != "true" ]; 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 - - # 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); + 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 + + # When the NOT_CRAN flag is *not* set, the vendor.tar.xz, if present, + # is unzipped and used for offline compilation. + if [ "$(NOT_CRAN)" != "true" ]; 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 + + # 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 build @CRAN_FLAGS@ --target=$(TARGET) --lib --release --manifest-path=./rust/Cargo.toml --target-dir $(TARGET_DIR) + + # Always clean up CARGOTMP + rm -Rf $(CARGOTMP); C_clean: - rm -Rf $(SHLIB) $(STATLIB) $(OBJECTS) + rm -Rf $(SHLIB) $(STATLIB) $(OBJECTS) clean: - rm -Rf $(SHLIB) $(STATLIB) $(OBJECTS) $(TARGET_DIR) + rm -Rf $(SHLIB) $(STATLIB) $(OBJECTS) $(TARGET_DIR) diff --git a/inst/templates/configure.win b/inst/templates/configure.win index 1c465d5a..1156e442 100644 --- a/inst/templates/configure.win +++ b/inst/templates/configure.win @@ -9,7 +9,7 @@ else fi # delete Makevars if it is present -[ -f src/Makevars ] && rm src/Makevars.win +[ -f src/Makevars.win ] && rm src/Makevars.win # Substitute @CRAN_FLAGS@ in Makevars.in with the actual value of $CRAN_FLAGS sed -e "s|@CRAN_FLAGS@|$CRAN_FLAGS|" src/Makevars.win.in > src/Makevars.win \ No newline at end of file From 479b30fe36e4daa8dae95f2356e7f5d9e59149b9 Mon Sep 17 00:00:00 2001 From: Josiah Parry Date: Sun, 17 Nov 2024 09:12:30 -0800 Subject: [PATCH 11/12] update snapshots --- tests/testthat/_snaps/use_extendr.md | 62 ++++++++++++++-------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/tests/testthat/_snaps/use_extendr.md b/tests/testthat/_snaps/use_extendr.md index 3454f723..91d6267e 100644 --- a/tests/testthat/_snaps/use_extendr.md +++ b/tests/testthat/_snaps/use_extendr.md @@ -55,7 +55,7 @@ fi # delete Makevars if it is present - [ -f src/Makevars ] && rm src/Makevars.win + [ -f src/Makevars.win ] && rm src/Makevars.win # Substitute @CRAN_FLAGS@ in Makevars.in with the actual value of $CRAN_FLAGS sed -e "s|@CRAN_FLAGS@|$CRAN_FLAGS|" src/Makevars.win.in > src/Makevars.win @@ -294,40 +294,40 @@ 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 - - # When the NOT_CRAN flag is *not* set, the vendor.tar.xz, if present, - # is unzipped and used for offline compilation. - if [ "$(NOT_CRAN)" != "true" ]; 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 - - # 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); + 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 + + # When the NOT_CRAN flag is *not* set, the vendor.tar.xz, if present, + # is unzipped and used for offline compilation. + if [ "$(NOT_CRAN)" != "true" ]; 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 + + # 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 build @CRAN_FLAGS@ --target=$(TARGET) --lib --release --manifest-path=./rust/Cargo.toml --target-dir $(TARGET_DIR) + + # Always clean up CARGOTMP + rm -Rf $(CARGOTMP); C_clean: - rm -Rf $(SHLIB) $(STATLIB) $(OBJECTS) + rm -Rf $(SHLIB) $(STATLIB) $(OBJECTS) clean: - rm -Rf $(SHLIB) $(STATLIB) $(OBJECTS) $(TARGET_DIR) + rm -Rf $(SHLIB) $(STATLIB) $(OBJECTS) $(TARGET_DIR) --- From d2db22e0d296965b7c30686e8a4d208b0171c2fc Mon Sep 17 00:00:00 2001 From: Josiah Parry Date: Sun, 17 Nov 2024 10:22:15 -0800 Subject: [PATCH 12/12] set CRAN_FLAGS only when vendored packages are used --- inst/templates/configure | 2 +- inst/templates/configure.win | 4 ++-- tests/testthat/_snaps/use_extendr.md | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/inst/templates/configure b/inst/templates/configure index 822ea414..5c1355a1 100644 --- a/inst/templates/configure +++ b/inst/templates/configure @@ -3,7 +3,7 @@ "${R_HOME}/bin/Rscript" tools/msrv.R # Set CRAN_FLAGS based on the NOT_CRAN value -if [ "${NOT_CRAN}" != "true" ]; then +if [ "${NOT_CRAN}" != "true" ] && [ -f ./src/rust/vendor.tar.xz ]; then export CRAN_FLAGS="-j 2 --offline" else export CRAN_FLAGS="" diff --git a/inst/templates/configure.win b/inst/templates/configure.win index 1156e442..f6d1efbd 100644 --- a/inst/templates/configure.win +++ b/inst/templates/configure.win @@ -2,13 +2,13 @@ "${R_HOME}/bin${R_ARCH_BIN}/Rscript.exe" tools/msrv.R # Set CRAN_FLAGS based on the NOT_CRAN value -if [ "${NOT_CRAN}" != "true" ]; then +if [ "${NOT_CRAN}" != "true" ] && [ -f ./src/rust/vendor.tar.xz ]; then export CRAN_FLAGS="-j 2 --offline" else export CRAN_FLAGS="" fi -# delete Makevars if it is present +# delete Makevars.win if it is present [ -f src/Makevars.win ] && rm src/Makevars.win # Substitute @CRAN_FLAGS@ in Makevars.in with the actual value of $CRAN_FLAGS diff --git a/tests/testthat/_snaps/use_extendr.md b/tests/testthat/_snaps/use_extendr.md index 91d6267e..6d53afcf 100644 --- a/tests/testthat/_snaps/use_extendr.md +++ b/tests/testthat/_snaps/use_extendr.md @@ -27,7 +27,7 @@ "${R_HOME}/bin/Rscript" tools/msrv.R # Set CRAN_FLAGS based on the NOT_CRAN value - if [ "${NOT_CRAN}" != "true" ]; then + if [ "${NOT_CRAN}" != "true" ] && [ -f ./src/rust/vendor.tar.xz ]; then export CRAN_FLAGS="-j 2 --offline" else export CRAN_FLAGS="" @@ -48,13 +48,13 @@ "${R_HOME}/bin${R_ARCH_BIN}/Rscript.exe" tools/msrv.R # Set CRAN_FLAGS based on the NOT_CRAN value - if [ "${NOT_CRAN}" != "true" ]; then + if [ "${NOT_CRAN}" != "true" ] && [ -f ./src/rust/vendor.tar.xz ]; then export CRAN_FLAGS="-j 2 --offline" else export CRAN_FLAGS="" fi - # delete Makevars if it is present + # delete Makevars.win if it is present [ -f src/Makevars.win ] && rm src/Makevars.win # Substitute @CRAN_FLAGS@ in Makevars.in with the actual value of $CRAN_FLAGS