Repository: extendr/rextendr
Pull request #419: Address R CMD check `_abort` WARNING
Author: login: JosiahParry
Created: 2025-03-25T00:47:51Z | Merged: 2025-03-31T16:46:28Z

R 4.5 a new check was introduces that identifies undesirable C function calls

https://github.com/r-devel/r-svn/blob/5695e158124051562158767089a55de80e7da452/src/library/tools/R/check.R#L3953-L3971

This has affected 24 packages on CRAN a good number of which use extendr. 

Inspiration was taken from https://github.com/r-rust/hellorust/blob/9a3ab01fd7ce6b3e5ee24d5af5e8ca3b28abd71f/src/Makevars#L7

closes https://github.com/extendr/rextendr/issues/418
sneaks in a resolution and closes https://github.com/extendr/rextendr/issues/375




Diff:
diff --git a/.github/workflows/test_pkg_gen.yaml b/.github/workflows/test_pkg_gen.yaml
index 91e9a5c2..1f28c5e9 100644
--- a/.github/workflows/test_pkg_gen.yaml
+++ b/.github/workflows/test_pkg_gen.yaml
@@ -120,6 +120,7 @@ jobs:
 
           # check if compile = TRUE forces compile, and accordingly the wrapper generation
           rextendr::register_extendr(compile = TRUE)
+
           stopifnot(library_mtime_before < file.info(library_path)$mtime)
           stopifnot(wrappers_mtime_before < file.info(wrappers_path)$mtime)
         shell: Rscript {0}
diff --git a/NEWS.md b/NEWS.md
index a8c294cc..9ad82f3f 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -1,5 +1,6 @@
 # rextendr (development version)
 
+* Addresses new CRAN check in R 4.5+ adding warning for `_abort` usage
 * Removes `Makevars.ucrt` as R versions < 4.1 are not supported by extendr <https://github.com/extendr/rextendr/pull/414> 
 * `purrr` has been replaced with [`R/standalone-purrr.R`](https://github.com/r-lib/rlang/blob/main/R/standalone-purrr.R) removing `purrr` from `Imports` <https://github.com/extendr/rextendr/pull/408>
 * `document()` will no longer try to save all open files using rstudioapi <https://github.com/extendr/rextendr/issues/404> <https://github.com/extendr/rextendr/issues/407>
diff --git a/R/use_extendr.R b/R/use_extendr.R
index 499db4ba..46a5588e 100644
--- a/R/use_extendr.R
+++ b/R/use_extendr.R
@@ -21,12 +21,14 @@
 #' @return A logical value (invisible) indicating whether any package files were
 #' generated or not.
 #' @export
-use_extendr <- function(path = ".",
-                        crate_name = NULL,
-                        lib_name = NULL,
-                        quiet = FALSE,
-                        overwrite = NULL,
-                        edition = c("2021", "2018")) {
+use_extendr <- function(
+  path = ".",
+  crate_name = NULL,
+  lib_name = NULL,
+  quiet = FALSE,
+  overwrite = NULL,
+  edition = c("2021", "2018")
+) {
   # https://github.com/r-lib/cli/issues/434
 
   local_quiet_cli(quiet)
@@ -80,7 +82,6 @@ use_extendr <- function(path = ".",
   src_dir <- rprojroot::find_package_root_file("src")
   r_dir <- rprojroot::find_package_root_file("R")
 
-
   if (!dir.exists(r_dir)) {
     dir.create(r_dir)
     cli::cli_alert_success("Creating {.file {pretty_rel_path(r_dir, path)}}.")
@@ -89,7 +90,9 @@ use_extendr <- function(path = ".",
   rust_src_dir <- file.path(src_dir, "rust", "src")
   if (!dir.exists(rust_src_dir)) {
     dir.create(rust_src_dir, recursive = TRUE)
-    cli::cli_alert_success("Creating {.file {pretty_rel_path(rust_src_dir, path)}}.")
+    cli::cli_alert_success(
+      "Creating {.file {pretty_rel_path(rust_src_dir, path)}}."
+    )
   }
 
   use_rextendr_template(
@@ -125,7 +128,13 @@ use_extendr <- function(path = ".",
 
   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),
+    package = list(
+      name = crate_name,
+      publish = FALSE,
+      version = "0.1.0",
+      edition = edition,
+      `rust-version` = "1.65"
+    ),
     lib = list(`crate-type` = array("staticlib", 1), name = lib_name),
     dependencies = list(`extendr-api` = "*")
   )
@@ -175,6 +184,14 @@ use_extendr <- function(path = ".",
     overwrite = overwrite
   )
 
+  # add config.R template
+  use_rextendr_template(
+    "config.R",
+    save_as = file.path("tools", "config.R"),
+    quiet = quiet,
+    overwrite = overwrite
+  )
+
   # add configure and configure.win templates
   use_rextendr_template(
     "configure",
@@ -198,6 +215,9 @@ use_extendr <- function(path = ".",
     Sys.chmod("configure", "0755")
   }
 
+  # Set the minimum version of R to 4.2 which is 64 bit
+  usethis::use_package("R", "Depends", "4.2")
+
   # the temporary cargo directory must be ignored
   usethis::use_build_ignore("src/.cargo")
 
@@ -210,6 +230,11 @@ use_extendr <- function(path = ".",
     file.path("src", "rust", "vendor")
   )
 
+  # ensure that the target directory is ignored
+  usethis::use_build_ignore(
+    file.path("src", "rust", "target")
+  )
+
   # 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")
@@ -218,7 +243,9 @@ use_extendr <- function(path = ".",
   usethis::use_git_ignore("src/Makevars.win")
 
   if (!isTRUE(quiet)) {
-    cli::cli_alert_success("Finished configuring {.pkg extendr} for package {.pkg {pkg_name}}.")
+    cli::cli_alert_success(
+      "Finished configuring {.pkg extendr} for package {.pkg {pkg_name}}."
+    )
     cli::cli_ul(
       c(
         "Please run {.fun rextendr::document} for changes to take effect."
@@ -230,7 +257,10 @@ use_extendr <- function(path = ".",
 }
 
 try_get_normalized_path <- function(path_fn) {
-  tryCatch(normalizePath(path_fn(), winslash = "/", mustWork = FALSE), error = function(e) NA)
+  tryCatch(
+    normalizePath(path_fn(), winslash = "/", mustWork = FALSE),
+    error = function(e) NA
+  )
 }
 
 try_get_proj_path <- function() {
@@ -238,7 +268,9 @@ try_get_proj_path <- function() {
 }
 
 try_get_root_path <- function(path) {
-  try_get_normalized_path(function() rprojroot::find_package_root_file(path = path))
+  try_get_normalized_path(
+    function() rprojroot::find_package_root_file(path = path)
+  )
 }
 
 #' Checks if provided name is a valid Rust name (identifier)
@@ -298,15 +330,19 @@ throw_if_invalid_rust_name <- function(name, call = caller_env()) {
 #' be overwritten in an interactive session or do nothing in a non-interactive session.
 #' Otherwise, the file will be overwritten.
 #' @noRd
-use_rextendr_template <- function(template,
-                                  save_as = template,
-                                  data = list(),
-                                  quiet = FALSE,
-                                  overwrite = NULL) {
+use_rextendr_template <- function(
+  template,
+  save_as = template,
+  data = list(),
+  quiet = FALSE,
+  overwrite = NULL
+) {
   local_quiet_cli(quiet)
 
   if (isFALSE(overwrite) && file.exists(save_as)) {
-    cli::cli_alert("File {.path {save_as}} already exists. Skip writing the file.")
+    cli::cli_alert(
+      "File {.path {save_as}} already exists. Skip writing the file."
+    )
     return(invisible(NULL))
   }
 
@@ -334,7 +370,8 @@ use_rextendr_template <- function(template,
   template_content <- glue::glue_data(
     template_content,
     .x = data,
-    .open = "{{{", .close = "}}}",
+    .open = "{{{",
+    .close = "}}}",
     .trim = FALSE
   )
 
diff --git a/inst/templates/Makevars.in b/inst/templates/Makevars.in
index 1f947ad7..72590681 100644
--- a/inst/templates/Makevars.in
+++ b/inst/templates/Makevars.in
@@ -1,9 +1,11 @@
 TARGET_DIR = ./rust/target
-LIBDIR = $(TARGET_DIR)/release
+LIBDIR = $(TARGET_DIR)/@LIBDIR@
 STATLIB = $(LIBDIR)/lib{{{lib_name}}}.a
 PKG_LIBS = -L$(LIBDIR) -l{{{lib_name}}}
 
-all: C_clean
+all: $(SHLIB) rust_clean
+
+.PHONY: $(STATLIB)
 
 $(SHLIB): $(STATLIB)
 
@@ -32,13 +34,13 @@ $(STATLIB):
 
 	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)
+	RUSTFLAGS="$(RUSTFLAGS) --print=native-static-libs" cargo build @CRAN_FLAGS@ --lib @PROFILE@ --manifest-path=./rust/Cargo.toml --target-dir $(TARGET_DIR)
 
 	# Always clean up CARGOTMP
 	rm -Rf $(CARGOTMP);
 
-C_clean:
-	rm -Rf $(SHLIB) $(STATLIB) $(OBJECTS)
+rust_clean:
+	rm -Rf $(CARGOTMP) $(VENDOR_DIR) @CLEAN_TARGET@
 
 clean:
-	rm -Rf $(SHLIB) $(STATLIB) $(OBJECTS) $(TARGET_DIR) $(VENDOR_DIR)
+	rm -Rf $(SHLIB) $(STATLIB) $(OBJECTS) $(TARGET_DIR)
\ No newline at end of file
diff --git a/inst/templates/Makevars.win.in b/inst/templates/Makevars.win.in
index 6a2269a4..58c4b3be 100644
--- a/inst/templates/Makevars.win.in
+++ b/inst/templates/Makevars.win.in
@@ -1,28 +1,23 @@
 TARGET = $(subst 64,x86_64,$(subst 32,i686,$(WIN)))-pc-windows-gnu
 
 TARGET_DIR = ./rust/target
-LIBDIR = $(TARGET_DIR)/$(TARGET)/release
+LIBDIR = $(TARGET_DIR)/$(TARGET)/@LIBDIR@
 STATLIB = $(LIBDIR)/lib{{{lib_name}}}.a
 PKG_LIBS = -L$(LIBDIR) -l{{{lib_name}}} -lws2_32 -ladvapi32 -luserenv -lbcrypt -lntdll
 
-all: C_clean
+all: $(SHLIB) rust_clean
+
+.PHONY: $(STATLIB)
 
 $(SHLIB): $(STATLIB)
 
 CARGOTMP = $(CURDIR)/.cargo
+VENDOR_DIR = vendor
 
 $(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 && \
@@ -34,14 +29,14 @@ $(STATLIB):
 	# 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)
+	export LIBRARY_PATH="$(LIBRARY_PATH);$(CURDIR)/$(TARGET_DIR)/libgcc_mock" && \
+	RUSTFLAGS="$(RUSTFLAGS) --print=native-static-libs" cargo build @CRAN_FLAGS@ --target=$(TARGET) --lib @PROFILE@ --manifest-path=rust/Cargo.toml --target-dir=$(TARGET_DIR)
 
 	# Always clean up CARGOTMP
 	rm -Rf $(CARGOTMP);
 
-C_clean:
-	rm -Rf $(SHLIB) $(STATLIB) $(OBJECTS)
+rust_clean:
+	rm -Rf $(CARGOTMP) $(VENDOR_DIR) @CLEAN_TARGET@
 
 clean:
-	rm -Rf $(SHLIB) $(STATLIB) $(OBJECTS) $(TARGET_DIR)
+	rm -Rf $(SHLIB) $(STATLIB) $(OBJECTS) $(TARGET_DIR)
\ No newline at end of file
diff --git a/inst/templates/config.R b/inst/templates/config.R
new file mode 100644
index 00000000..13a59044
--- /dev/null
+++ b/inst/templates/config.R
@@ -0,0 +1,77 @@
+# check the packages MSRV first
+source("tools/msrv.R")
+
+# check DEBUG and NOT_CRAN environment variables
+env_debug <- Sys.getenv("DEBUG")
+env_not_cran <- Sys.getenv("NOT_CRAN")
+
+# check if the vendored zip file exists
+vendor_exists <- file.exists("src/rust/vendor.tar.xz")
+
+is_not_cran <- env_not_cran != ""
+is_debug <- env_debug != ""
+
+if (is_debug) {
+  # if we have DEBUG then we set not cran to true
+  # CRAN is always release build
+  is_not_cran <- TRUE
+  message("Creating DEBUG build.")
+}
+
+if (!is_not_cran) {
+  message("Building for CRAN.")
+}
+
+# we set cran flags only if NOT_CRAN is empty and if
+# the vendored crates are present.
+.cran_flags <- ifelse(
+  !is_not_cran && vendor_exists,
+  "-j 2 --offline",
+  ""
+)
+
+# when DEBUG env var is present we use `--debug` build
+.profile <- ifelse(is_debug, "", "--release")
+.clean_targets <- ifelse(is_debug, "", "$(TARGET_DIR)")
+
+# when we are using a debug build we need to use target/debug instead of target/release
+.libdir <- ifelse(is_debug, "debug", "release")
+
+# read in the Makevars.in file
+is_windows <- .Platform[["OS.type"]] == "windows"
+
+# if windows we replace in the Makevars.win.in
+mv_fp <- ifelse(
+  is_windows,
+  "src/Makevars.win.in",
+  "src/Makevars.in"
+)
+
+# set the output file
+mv_ofp <- ifelse(
+  is_windows,
+  "src/Makevars.win",
+  "src/Makevars"
+)
+
+# delete the existing Makevars{.win}
+if (file.exists(mv_ofp)) {
+  message("Cleaning previous `", mv_ofp, "`.")
+  invisible(file.remove(mv_ofp))
+}
+
+# read as a single string
+mv_txt <- readLines(mv_fp)
+
+# replace placeholder values
+new_txt <- gsub("@CRAN_FLAGS@", .cran_flags, mv_txt) |>
+  gsub("@PROFILE@", .profile, x = _) |>
+  gsub("@CLEAN_TARGET@", .clean_targets, x = _) |>
+  gsub("@LIBDIR@", .libdir, x = _)
+
+message("Writing `", mv_ofp, "`.")
+con <- file(mv_ofp, open = "wb")
+writeLines(new_txt, con, sep = "\n")
+close(con)
+
+message("`tools/config.R` has finished.")
diff --git a/inst/templates/configure b/inst/templates/configure
index 5c1355a1..c608b11a 100644
--- a/inst/templates/configure
+++ b/inst/templates/configure
@@ -1,16 +1,3 @@
 #!/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" ] && [ -f ./src/rust/vendor.tar.xz ]; 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
\ No newline at end of file
+"${R_HOME}/bin/Rscript" tools/config.R 
diff --git a/inst/templates/configure.win b/inst/templates/configure.win
index f6d1efbd..5caecba0 100644
--- a/inst/templates/configure.win
+++ b/inst/templates/configure.win
@@ -1,15 +1,2 @@
 #!/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" ] && [ -f ./src/rust/vendor.tar.xz ]; then
-  export CRAN_FLAGS="-j 2 --offline"
-else
-  export CRAN_FLAGS=""
-fi
-
-# 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
-sed -e "s|@CRAN_FLAGS@|$CRAN_FLAGS|" src/Makevars.win.in > src/Makevars.win
\ No newline at end of file
+"${R_HOME}/bin${R_ARCH_BIN}/Rscript.exe" tools/config.R
\ No newline at end of file
diff --git a/tests/testthat/_snaps/use_extendr.md b/tests/testthat/_snaps/use_extendr.md
index 12969ea5..15300066 100644
--- a/tests/testthat/_snaps/use_extendr.md
+++ b/tests/testthat/_snaps/use_extendr.md
@@ -14,6 +14,7 @@
     Output
       ^src/\.cargo$
       ^src/rust/vendor$
+      ^src/rust/target$
       ^src/Makevars$
       ^src/Makevars\.win$
 
@@ -24,20 +25,7 @@
     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" ] && [ -f ./src/rust/vendor.tar.xz ]; 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
+      "${R_HOME}/bin/Rscript" tools/config.R
 
 ---
 
@@ -45,20 +33,7 @@
       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" ] && [ -f ./src/rust/vendor.tar.xz ]; then
-        export CRAN_FLAGS="-j 2 --offline"
-      else
-        export CRAN_FLAGS=""
-      fi
-      
-      # 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
-      sed -e "s|@CRAN_FLAGS@|$CRAN_FLAGS|" src/Makevars.win.in > src/Makevars.win
+      "${R_HOME}/bin${R_ARCH_BIN}/Rscript.exe" tools/config.R
 
 ---
 
@@ -217,11 +192,13 @@
       cat_file("src", "Makevars.in")
     Output
       TARGET_DIR = ./rust/target
-      LIBDIR = $(TARGET_DIR)/release
+      LIBDIR = $(TARGET_DIR)/@LIBDIR@
       STATLIB = $(LIBDIR)/libtestpkg.a
       PKG_LIBS = -L$(LIBDIR) -ltestpkg
       
-      all: C_clean
+      all: $(SHLIB) rust_clean
+      
+      .PHONY: $(STATLIB)
       
       $(SHLIB): $(STATLIB)
       
@@ -250,16 +227,16 @@
       
       	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)
+      	RUSTFLAGS="$(RUSTFLAGS) --print=native-static-libs" cargo build @CRAN_FLAGS@ --lib @PROFILE@ --manifest-path=./rust/Cargo.toml --target-dir $(TARGET_DIR)
       
       	# Always clean up CARGOTMP
       	rm -Rf $(CARGOTMP);
       
-      C_clean:
-      	rm -Rf $(SHLIB) $(STATLIB) $(OBJECTS)
+      rust_clean:
+      	rm -Rf $(CARGOTMP) $(VENDOR_DIR) @CLEAN_TARGET@
       
       clean:
-      	rm -Rf $(SHLIB) $(STATLIB) $(OBJECTS) $(TARGET_DIR) $(VENDOR_DIR)
+      	rm -Rf $(SHLIB) $(STATLIB) $(OBJECTS) $(TARGET_DIR)
 
 ---
 
@@ -283,28 +260,23 @@
       TARGET = $(subst 64,x86_64,$(subst 32,i686,$(WIN)))-pc-windows-gnu
       
       TARGET_DIR = ./rust/target
-      LIBDIR = $(TARGET_DIR)/$(TARGET)/release
+      LIBDIR = $(TARGET_DIR)/$(TARGET)/@LIBDIR@
       STATLIB = $(LIBDIR)/libtestpkg.a
       PKG_LIBS = -L$(LIBDIR) -ltestpkg -lws2_32 -ladvapi32 -luserenv -lbcrypt -lntdll
       
-      all: C_clean
+      all: $(SHLIB) rust_clean
+      
+      .PHONY: $(STATLIB)
       
       $(SHLIB): $(STATLIB)
       
       CARGOTMP = $(CURDIR)/.cargo
+      VENDOR_DIR = vendor
       
       $(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 && \
@@ -316,14 +288,14 @@
       	# 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)
+      	export LIBRARY_PATH="$(LIBRARY_PATH);$(CURDIR)/$(TARGET_DIR)/libgcc_mock" && \
+      	RUSTFLAGS="$(RUSTFLAGS) --print=native-static-libs" cargo build @CRAN_FLAGS@ --target=$(TARGET) --lib @PROFILE@ --manifest-path=rust/Cargo.toml --target-dir=$(TARGET_DIR)
       
       	# Always clean up CARGOTMP
       	rm -Rf $(CARGOTMP);
       
-      C_clean:
-      	rm -Rf $(SHLIB) $(STATLIB) $(OBJECTS)
+      rust_clean:
+      	rm -Rf $(CARGOTMP) $(VENDOR_DIR) @CLEAN_TARGET@
       
       clean:
       	rm -Rf $(SHLIB) $(STATLIB) $(OBJECTS) $(TARGET_DIR)
@@ -346,6 +318,7 @@
       publish = false
       version = '0.1.0'
       edition = '2021'
+      rust-version = '1.65'
       
       [lib]
       crate-type = [ 'staticlib' ]
@@ -395,6 +368,7 @@
       > 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 'tools/config.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.
@@ -414,6 +388,7 @@
       v Writing 'src/testpkg-win.def'
       > File 'R/extendr-wrappers.R' already exists. Skip writing the file.
       v Writing 'tools/msrv.R'
+      v Writing 'tools/config.R'
       v Writing 'configure'
       v Writing 'configure.win'
       v Finished configuring extendr for package testpkg.
@@ -429,6 +404,7 @@
       publish = false
       version = '0.1.0'
       edition = '2021'
+      rust-version = '1.65'
       
       [lib]
       crate-type = [ 'staticlib' ]
@@ -443,11 +419,13 @@
       cat_file("src", "Makevars.in")
     Output
       TARGET_DIR = ./rust/target
-      LIBDIR = $(TARGET_DIR)/release
+      LIBDIR = $(TARGET_DIR)/@LIBDIR@
       STATLIB = $(LIBDIR)/libbar.a
       PKG_LIBS = -L$(LIBDIR) -lbar
       
-      all: C_clean
+      all: $(SHLIB) rust_clean
+      
+      .PHONY: $(STATLIB)
       
       $(SHLIB): $(STATLIB)
       
@@ -476,14 +454,14 @@
       
       	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)
+      	RUSTFLAGS="$(RUSTFLAGS) --print=native-static-libs" cargo build @CRAN_FLAGS@ --lib @PROFILE@ --manifest-path=./rust/Cargo.toml --target-dir $(TARGET_DIR)
       
       	# Always clean up CARGOTMP
       	rm -Rf $(CARGOTMP);
       
-      C_clean:
-      	rm -Rf $(SHLIB) $(STATLIB) $(OBJECTS)
+      rust_clean:
+      	rm -Rf $(CARGOTMP) $(VENDOR_DIR) @CLEAN_TARGET@
       
       clean:
-      	rm -Rf $(SHLIB) $(STATLIB) $(OBJECTS) $(TARGET_DIR) $(VENDOR_DIR)
+      	rm -Rf $(SHLIB) $(STATLIB) $(OBJECTS) $(TARGET_DIR)
 
diff --git a/tests/testthat/test-cran-compliance.R b/tests/testthat/test-cran-compliance.R
index 11e1293e..61b5db28 100644
--- a/tests/testthat/test-cran-compliance.R
+++ b/tests/testthat/test-cran-compliance.R
@@ -4,6 +4,7 @@ test_that("vendor_pkgs() vendors dependencies", {
   path <- local_package("testpkg")
 
   expect_error(vendor_pkgs(path))
+
   # capture setup messages
   withr::local_options(usethis.quiet = FALSE)
   use_extendr(path, quiet = TRUE)


Reviews:
user: login: Ilia-Kosenkov
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-27T22:03:24Z
user: login: JosiahParry
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-27T22:06:57Z
user: login: Ilia-Kosenkov
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-27T22:14:52Z
user: login: Ilia-Kosenkov
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-28T20:40:20Z
user: login: JosiahParry
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-28T21:04:38Z
user: login: JosiahParry
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-28T21:06:37Z
user: login: JosiahParry
body: Commenting on major changes / additions for context
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-28T21:11:38Z
user: login: Ilia-Kosenkov
body: I trust in you and green checkmarks
state: APPROVED
author_association: MEMBER
submitted_at: 2025-03-31T10:03:31Z

Review comments:
diff_hunk: @@ -0,0 +1,77 @@
+# check the packages MSRV first
+source("tools/msrv.R")
+
+# check DEBUG and NOT_CRAN environment variables
+env_debug <- Sys.getenv("DEBUG")
+env_not_cran <- Sys.getenv("NOT_CRAN")
+
+# check if the vendored zip file exists
+vendor_exists <- file.exists("src/rust/vendor.tar.xz")
+
+is_not_cran <- env_not_cran != ""
+is_debug <- env_debug != ""
+
+if (is_debug) {
+  # if we have DEBUG then we set not cran to true
+  # CRAN is always release build
+  is_not_cran <- TRUE
+  message("Creating DEBUG build.")
+}
+
+if (!is_not_cran) {
+  message("Building for CRAN.")
+}
+
+# we set cran flags only if NOT_CRAN is empty and if
+# the vendored crates are present.
+.cran_flags <- ifelse(
path: inst/templates/config.R
user: login: Ilia-Kosenkov
body: What's the point of dot-prefixed variables here?
created_at: 2025-03-27T22:03:24Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,77 @@
+# check the packages MSRV first
+source("tools/msrv.R")
+
+# check DEBUG and NOT_CRAN environment variables
+env_debug <- Sys.getenv("DEBUG")
+env_not_cran <- Sys.getenv("NOT_CRAN")
+
+# check if the vendored zip file exists
+vendor_exists <- file.exists("src/rust/vendor.tar.xz")
+
+is_not_cran <- env_not_cran != ""
+is_debug <- env_debug != ""
+
+if (is_debug) {
+  # if we have DEBUG then we set not cran to true
+  # CRAN is always release build
+  is_not_cran <- TRUE
+  message("Creating DEBUG build.")
+}
+
+if (!is_not_cran) {
+  message("Building for CRAN.")
+}
+
+# we set cran flags only if NOT_CRAN is empty and if
+# the vendored crates are present.
+.cran_flags <- ifelse(
path: inst/templates/config.R
user: login: JosiahParry
body: Just a visual indicator for myself to know that these are the variables that are going into the Makevars files. I can remove the prefix if it gives you the ick 😷 
created_at: 2025-03-27T22:06:56Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,77 @@
+# check the packages MSRV first
+source("tools/msrv.R")
+
+# check DEBUG and NOT_CRAN environment variables
+env_debug <- Sys.getenv("DEBUG")
+env_not_cran <- Sys.getenv("NOT_CRAN")
+
+# check if the vendored zip file exists
+vendor_exists <- file.exists("src/rust/vendor.tar.xz")
+
+is_not_cran <- env_not_cran != ""
+is_debug <- env_debug != ""
+
+if (is_debug) {
+  # if we have DEBUG then we set not cran to true
+  # CRAN is always release build
+  is_not_cran <- TRUE
+  message("Creating DEBUG build.")
+}
+
+if (!is_not_cran) {
+  message("Building for CRAN.")
+}
+
+# we set cran flags only if NOT_CRAN is empty and if
+# the vendored crates are present.
+.cran_flags <- ifelse(
path: inst/templates/config.R
user: login: Ilia-Kosenkov
body: Not really, just curious. `dot` prefixes I kinda in the 'wild west' of R, can't recall any styling rules regulating their usage.
created_at: 2025-03-27T22:14:51Z
author_association: MEMBER
diff_hunk: @@ -34,14 +29,14 @@ $(STATLIB):
 	# 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)
+	export LIBRARY_PATH="$(LIBRARY_PATH);$(CURDIR)/$(TARGET_DIR)/libgcc_mock" && \
+	RUSTFLAGS="$(RUSTFLAGS) --print=native-static-libs" cargo build @CRAN_FLAGS@ --target=$(TARGET) --lib @PROFILE@ --manifest-path=rust/Cargo.toml --target-dir=$(TARGET_DIR)
 
 	# Always clean up CARGOTMP
 	rm -Rf $(CARGOTMP);
 
-C_clean:
-	rm -Rf $(SHLIB) $(STATLIB) $(OBJECTS)
+rust_clean:
path: inst/templates/Makevars.win.in
user: login: Ilia-Kosenkov
body: Can you briefly explain how these new clean targets work?
created_at: 2025-03-28T20:40:19Z
author_association: MEMBER
diff_hunk: @@ -34,14 +29,14 @@ $(STATLIB):
 	# 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)
+	export LIBRARY_PATH="$(LIBRARY_PATH);$(CURDIR)/$(TARGET_DIR)/libgcc_mock" && \
+	RUSTFLAGS="$(RUSTFLAGS) --print=native-static-libs" cargo build @CRAN_FLAGS@ --target=$(TARGET) --lib @PROFILE@ --manifest-path=rust/Cargo.toml --target-dir=$(TARGET_DIR)
 
 	# Always clean up CARGOTMP
 	rm -Rf $(CARGOTMP);
 
-C_clean:
-	rm -Rf $(SHLIB) $(STATLIB) $(OBJECTS)
+rust_clean:
path: inst/templates/Makevars.win.in
user: login: JosiahParry
body: I can do my best but I'm still not _entirely_ sure about how Make works. 

But my understanding of the issue is roughly that the targets directory contains some stuff and symbols that trigger the warning. (`_abort` in this case). The targets directory would get cleaned up at the end of the installation process traditionally but the new check happens before the target dir was being cleaned up.

The new build process is (IIUC) the shared lib `$(SHLIB)` is build. Afterwards, the `rust_clean` target is run cleaning up the targets and vendor directory (eliminating the new WARNING). 

The new `.PHONY` is present to ensure that the shared library is always built again—the introduction of the `rust_clean` target caused a failing check in the package generation CI because I think the `$(STATLIB)` wasn't always being regenerated properly. This now ensures it always is after the intermediate cleaning step. 


created_at: 2025-03-28T21:04:38Z
author_association: CONTRIBUTOR
diff_hunk: @@ -34,14 +29,14 @@ $(STATLIB):
 	# 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)
+	export LIBRARY_PATH="$(LIBRARY_PATH);$(CURDIR)/$(TARGET_DIR)/libgcc_mock" && \
+	RUSTFLAGS="$(RUSTFLAGS) --print=native-static-libs" cargo build @CRAN_FLAGS@ --target=$(TARGET) --lib @PROFILE@ --manifest-path=rust/Cargo.toml --target-dir=$(TARGET_DIR)
 
 	# Always clean up CARGOTMP
 	rm -Rf $(CARGOTMP);
 
-C_clean:
-	rm -Rf $(SHLIB) $(STATLIB) $(OBJECTS)
+rust_clean:
path: inst/templates/Makevars.win.in
user: login: JosiahParry
body: It is also worth mentioning that `@CLEAN_TARGET@` is included in the templates so that the target directory isn't always cleaned. When `DEBUG` is present we do not clean target (e.g. using `devtools::load_all()`). Otherwise we do clean it. This lets us keep the target directory for incremental compilation making the DX a bit nicer
created_at: 2025-03-28T21:06:37Z
author_association: CONTRIBUTOR
diff_hunk: @@ -125,7 +128,13 @@ use_extendr <- function(path = ".",
 
   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),
+    package = list(
+      name = crate_name,
+      publish = FALSE,
+      version = "0.1.0",
+      edition = edition,
+      `rust-version` = "1.65"
path: R/use_extendr.R
user: login: JosiahParry
body: This explicitly specifies the msrv for extendr to ensure that a version 4 cargo.lock file isn't generated.
created_at: 2025-03-28T21:08:04Z
author_association: CONTRIBUTOR
diff_hunk: @@ -175,6 +184,14 @@ use_extendr <- function(path = ".",
     overwrite = overwrite
   )
 
+  # add config.R template
+  use_rextendr_template(
+    "config.R",
+    save_as = file.path("tools", "config.R"),
+    quiet = quiet,
+    overwrite = overwrite
+  )
path: R/use_extendr.R
user: login: JosiahParry
body: the `config.R` is used instead of a `configure.win` and `configure` file. Making it easier to maintain (plus its R which is just nicer to use imo).
created_at: 2025-03-28T21:08:33Z
author_association: CONTRIBUTOR
diff_hunk: @@ -198,6 +215,9 @@ use_extendr <- function(path = ".",
     Sys.chmod("configure", "0755")
   }
 
+  # Set the minimum version of R to 4.2 which is 64 bit
+  usethis::use_package("R", "Depends", "4.2")
path: R/use_extendr.R
user: login: JosiahParry
body: Set the minimum supported version of R in extendr explicitly. 
created_at: 2025-03-28T21:08:47Z
author_association: CONTRIBUTOR
diff_hunk: @@ -210,6 +230,11 @@ use_extendr <- function(path = ".",
     file.path("src", "rust", "vendor")
   )
 
+  # ensure that the target directory is ignored
+  usethis::use_build_ignore(
+    file.path("src", "rust", "target")
+  )
path: R/use_extendr.R
user: login: JosiahParry
body: ensure no accidental incorporation of target files into a bundle. Has no effect other than preventing it from being included in the final tarball
created_at: 2025-03-28T21:09:16Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,9 +1,11 @@
 TARGET_DIR = ./rust/target
-LIBDIR = $(TARGET_DIR)/release
+LIBDIR = $(TARGET_DIR)/@LIBDIR@
path: inst/templates/Makevars.in
user: login: JosiahParry
body: this is used to permit debug / release builds based on the `DEBUG` env var
created_at: 2025-03-28T21:09:36Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,9 +1,11 @@
 TARGET_DIR = ./rust/target
-LIBDIR = $(TARGET_DIR)/release
+LIBDIR = $(TARGET_DIR)/@LIBDIR@
 STATLIB = $(LIBDIR)/lib{{{lib_name}}}.a
 PKG_LIBS = -L$(LIBDIR) -l{{{lib_name}}}
 
-all: C_clean
+all: $(SHLIB) rust_clean
path: inst/templates/Makevars.in
user: login: JosiahParry
body: used to clean up target and vendor directory which is the main resolution for the new WARNING
created_at: 2025-03-28T21:10:01Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,9 +1,11 @@
 TARGET_DIR = ./rust/target
-LIBDIR = $(TARGET_DIR)/release
+LIBDIR = $(TARGET_DIR)/@LIBDIR@
 STATLIB = $(LIBDIR)/lib{{{lib_name}}}.a
 PKG_LIBS = -L$(LIBDIR) -l{{{lib_name}}}
 
-all: C_clean
+all: $(SHLIB) rust_clean
+
+.PHONY: $(STATLIB)
path: inst/templates/Makevars.in
user: login: JosiahParry
body: ensures that the .so / .dll is always updated when being compiled e.g. `register_extendr(compile = TRUE)`
created_at: 2025-03-28T21:10:26Z
author_association: CONTRIBUTOR
diff_hunk: @@ -32,13 +34,13 @@ $(STATLIB):
 
 	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)
+	RUSTFLAGS="$(RUSTFLAGS) --print=native-static-libs" cargo build @CRAN_FLAGS@ --lib @PROFILE@ --manifest-path=./rust/Cargo.toml --target-dir $(TARGET_DIR)
 
 	# Always clean up CARGOTMP
 	rm -Rf $(CARGOTMP);
path: inst/templates/Makevars.in
user: login: JosiahParry
body: `@PROFILE@` is used to match the `DEBUG` env var
created_at: 2025-03-28T21:10:39Z
author_association: CONTRIBUTOR
diff_hunk: @@ -32,13 +34,13 @@ $(STATLIB):
 
 	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)
+	RUSTFLAGS="$(RUSTFLAGS) --print=native-static-libs" cargo build @CRAN_FLAGS@ --lib @PROFILE@ --manifest-path=./rust/Cargo.toml --target-dir $(TARGET_DIR)
 
 	# Always clean up CARGOTMP
 	rm -Rf $(CARGOTMP);
 
-C_clean:
-	rm -Rf $(SHLIB) $(STATLIB) $(OBJECTS)
+rust_clean:
+	rm -Rf $(CARGOTMP) $(VENDOR_DIR) @CLEAN_TARGET@
path: inst/templates/Makevars.in
user: login: JosiahParry
body: `@CLEAN_TARGET@` is based on the `DEBUG` env var so that we do not clean the target directory in a debug build and permit incremental compilation.
created_at: 2025-03-28T21:11:04Z
author_association: CONTRIBUTOR

PR comments:
user: login: JosiahParry
created_at: 2025-03-31T14:52:01Z
body: That means a lot. I'll try publishing an update using this version of the package before pressing merge 
author_association: CONTRIBUTOR
user: login: JosiahParry
created_at: 2025-03-31T16:42:49Z
body: b64 is on its way to cran with this version of rextendr and extendr from pull https://github.com/extendr/extendr/pull/888/
author_association: CONTRIBUTOR
user: login: Ilia-Kosenkov
created_at: 2025-03-31T16:45:36Z
body: Feel free to merge this when you deem necessary 
author_association: MEMBER
user: login: codecov[bot]
created_at: 2025-03-31T16:47:21Z
body: ## [Codecov](https://app.codecov.io/gh/extendr/rextendr/pull/419?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=extendr) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 83.16%. Comparing base [(`88ec630`)](https://app.codecov.io/gh/extendr/rextendr/commit/88ec63044b7ffd3f8b2ab7a5853375bf6661679f?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=extendr) to head [(`68ebbfc`)](https://app.codecov.io/gh/extendr/rextendr/commit/68ebbfc0a4b6a4714ea3f19826f2aa8f40b24cce?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=extendr).
> Report is 1 commits behind head on main.

<details><summary>Additional details and impacted files</summary>


[![Impacted file tree graph](https://app.codecov.io/gh/extendr/rextendr/pull/419/graphs/tree.svg?width=650&height=150&src=pr&token=5H6ID0LAO7&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=extendr)](https://app.codecov.io/gh/extendr/rextendr/pull/419?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=extendr)

| [Files with missing lines](https://app.codecov.io/gh/extendr/rextendr/pull/419?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=extendr) | Coverage Δ | |
|---|---|---|
| [R/use\_extendr.R](https://app.codecov.io/gh/extendr/rextendr/pull/419?src=pr&el=tree&filepath=R%2Fuse_extendr.R&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=extendr#diff-Ui91c2VfZXh0ZW5kci5S) | `99.08% <100.00%> (+0.13%)` | :arrow_up: |

</details>
<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
</details>
author_association: NONE