Reference page
Template System
The minirextendr package provides scaffolding templates for creating new R packages with Rust backends. This document explains how the template system works and how to keep templates in sync with the reference implementation.
The minirextendr package provides scaffolding templates for creating new R packages with Rust backends. This document explains how the template system works and how to keep templates in sync with the reference implementation.
πOverview
πTemplate Sources
Templates are stored in minirextendr/inst/templates/ and come in two flavors:
rpkg/- Standalone R package template (recommended for most users)monorepo/- Workspace-based template with separate Rust crate
πMaster Source: example package (rpkg/)
Important: The templates are derived from the example package (rpkg/), not the other way around.
- The example package (
rpkg/) is the master source where changes should be tested first - Templates are copies of the example package files with additional logic for standalone use
- Changes flow:
rpkg/β test β apply to templates β approve delta
πTemplate Sync System
πApproved Delta
Templates are not exact copies of the example package (rpkg/) - they have legitimate differences for standalone projects:
- Conditional monorepo detection - Check if miniextendr-api exists before using path overrides
- Standalone vendoring - Run cargo vendor for transitive deps when not in monorepo
- Extra flexibility - Handle cases where rpkg assumptions donβt hold
The approved differences are tracked in patches/templates.patch.
πJustfile Commands
# Check if templates match rpkg (with approved delta)
just templates-check
# Approve current delta as the new baseline
just templates-approveπWorkflow for Template Changes
π1. Making Changes to Templates
Option A: Change applies to rpkg too (most common)
# 1. Make change in rpkg/ first (the master source)
vim rpkg/configure.ac
# 2. Test the change in rpkg
just configure
just devtools-test
# 3. Apply the same change to templates
vim minirextendr/inst/templates/rpkg/configure.ac
vim minirextendr/inst/templates/monorepo/rpkg/configure.ac
# 4. Approve the updated delta
just templates-approve
# 5. Verify sync
just templates-check # Should pass
Option B: Template-only change (rare)
If the change only makes sense for templates (e.g., adding fallback logic for missing monorepo):
# 1. Make change in templates only
vim minirextendr/inst/templates/rpkg/configure.ac
# 2. Approve the new delta
just templates-approve
# 3. Verify
just templates-checkπ2. When templates-check Fails
If just templates-check fails, it means templates have drifted from the example package (rpkg/):
# See what changed
just templates-check # Shows diff output
# Options:
# A) The drift is intentional (you made changes) - approve it
just templates-approve
# B) The drift is accidental - revert template changes
git restore minirextendr/inst/templates/π3. Common Scenarios
Scenario: Updated configure.ac in the example package
# Edit and test in rpkg
vim rpkg/configure.ac
cd rpkg && autoconf && ./configure
# Copy changes to templates
vim minirextendr/inst/templates/rpkg/configure.ac
vim minirextendr/inst/templates/monorepo/rpkg/configure.ac
# Approve
just templates-approve
Scenario: Updated .Rbuildignore patterns
# Edit master
vim rpkg/.Rbuildignore
# Copy to templates
vim minirextendr/inst/templates/rpkg/Rbuildignore
vim minirextendr/inst/templates/monorepo/rpkg/Rbuildignore
# Approve
just templates-approve
Scenario: Updated bootstrap.R
# Edit and test
vim rpkg/bootstrap.R
R CMD INSTALL rpkg # Tests bootstrap
# Copy to templates
vim minirextendr/inst/templates/rpkg/bootstrap.R
vim minirextendr/inst/templates/monorepo/rpkg/bootstrap.R
# Approve
just templates-approveπTemplate Files
πFiles That Need Sync
The following files in rpkg/ have corresponding template versions:
| rpkg/ | Template Location |
|---|---|
.Rbuildignore | inst/templates/*/Rbuildignore |
bootstrap.R | inst/templates/*/bootstrap.R |
build.rs | inst/templates/*/build.rs |
cleanup, cleanup.win, cleanup.ucrt | inst/templates/*/cleanup* |
configure.ac | inst/templates/*/configure.ac |
configure.win, configure.ucrt | inst/templates/*/configure.* |
src/stub.c | inst/templates/*/stub.c |
src/Makevars.in | inst/templates/*/Makevars.in |
src/rust/build.rs | inst/templates/*/build.rs |
src/rust/Cargo.toml | inst/templates/*/Cargo.toml |
src/rust/cargo-config.toml.in | inst/templates/*/cargo-config.toml.in |
src/rust/lib.rs | inst/templates/*/lib.rs (with {{package_rs}} substitution) |
R/rpkg-package.R | inst/templates/*/package.R |
inst/include/mx_abi.h | inst/templates/*/inst_include/mx_abi.h |
πFiles That Donβt Need Sync
These rpkg/ files are specific to the example package and donβt have template equivalents:
DESCRIPTION- Package metadataR/*.R- R function implementationssrc/rust/*.rs- Example Rust code (except lib.rs template)tests/- Test filesman/- Generated documentation
πImplementation Details
πtemplates-check Recipe
The check recipe:
- Copies rpkg files to a temp directory
- Applies
patches/templates.patchto reverse approved differences - Compares with actual templates
- Fails if any unexpected differences found
πtemplates-approve Recipe
The approve recipe:
- Copies rpkg files to a temp directory
- Compares with actual templates
- Generates a new
patches/templates.patchwith current delta - This becomes the new approved baseline
πPatch File
patches/templates.patch stores the approved differences between the example package and templates as a unified diff. This allows:
- Intentional differences to be tracked and reviewed
- Unexpected drift to be caught by
templates-check - Clear documentation of why templates differ from rpkg
πBest Practices
πAlways Test in the example package (rpkg/) first
- β Make change in rpkg/
- β Test thoroughly (just devtools-test)
- β Apply to templates
- β Approve delta
- β Donβt change templates without testing in rpkg first
πKeep Templates Simple
Templates should be as close to rpkg as possible. Only add template-specific logic when absolutely necessary for standalone projects.
πDocument Intentional Differences
When adding template-specific logic, add comments explaining why it differs from rpkg:
dnl Standalone scaffolded project - no monorepo available
if test -d "$VENDOR_OUT" && test -n "`ls -A \"$VENDOR_OUT\" 2>/dev/null`"; then
# This logic is template-specific - rpkg always has monorepo
echo "configure: running cargo vendor (standalone project)"
fiπRun templates-check Before Committing
Always verify templates are in sync before committing:
# Your workflow
git add rpkg/configure.ac
git add minirextendr/inst/templates/
git add patches/templates.patch
# Verify
just templates-check # Must pass before commitπTroubleshooting
πtemplates-check fails after modifying the example package
Expected behavior. You modified the master source, so templates are now out of sync.
Solution:
# Apply changes to templates, then approve
just templates-approveπtemplates-check fails but I didnβt change anything
Possible causes:
- Someone committed example package changes without updating templates
- The patch file is out of sync
Solution:
# Review the diff to understand what changed
just templates-check # Shows differences
# If changes should be in templates, update them
# If rpkg is wrong, revert it
# Then approve
just templates-approveπI modified templates but not the example package
Generally wrong approach. Templates derive from the example package (rpkg/).
Solution:
# 1. Apply the change to rpkg first
vim rpkg/configure.ac
# 2. Test in rpkg
just devtools-test
# 3. Update templates
vim minirextendr/inst/templates/rpkg/configure.ac
# 4. Approve
just templates-approveπExamples
πExample: Add New .Rbuildignore Pattern
# 1. Add to master
echo "^new-pattern$" >> rpkg/.Rbuildignore
# 2. Test
R CMD build rpkg
R CMD check rpkg_*.tar.gz
# 3. Add to templates
echo "^new-pattern$" >> minirextendr/inst/templates/rpkg/Rbuildignore
echo "^new-pattern$" >> minirextendr/inst/templates/monorepo/rpkg/Rbuildignore
# 4. Approve and verify
just templates-approve
just templates-checkπExample: Fix configure.ac Bug
# 1. Fix in rpkg
vim rpkg/configure.ac
cd rpkg && autoconf # Regenerate configure
# 2. Test the fix
just configure
just devtools-test
# 3. Apply to templates
vim minirextendr/inst/templates/rpkg/configure.ac
vim minirextendr/inst/templates/monorepo/rpkg/configure.ac
# 4. Approve
just templates-approve
# 5. Verify everything
just templates-check
just minirextendr-test # Template tests include scaffoldingπExample: Update bootstrap.R Logic
# 1. Edit master
vim rpkg/bootstrap.R
# 2. Test by triggering bootstrap
rm rpkg/src/Makevars # Force bootstrap to run
R CMD INSTALL rpkg
# 3. Copy to templates
cp rpkg/bootstrap.R minirextendr/inst/templates/rpkg/bootstrap.R
cp rpkg/bootstrap.R minirextendr/inst/templates/monorepo/rpkg/bootstrap.R
# 4. Approve
just templates-approveπCI Integration
The template sync check runs in CI via minirextendr tests:
test_that("templates patch is in sync with rpkg sources", {
# Runs `just templates-check` from repo root
# Fails if templates have drifted from rpkg
})
This ensures:
- Templates donβt drift over time
- Changes to rpkg are reflected in templates
- Patch file stays up to date
πSummary
Key Points:
- The example package (
rpkg/) is the master source - Templates are derived copies with minimal additions
patches/templates.patchtracks approved differences- Always test in the example package before updating templates
- Run
just templates-checkbefore committing - Use
just templates-approveto update the approved delta
Commands:
just templates-check # Verify templates match rpkg (with approved delta)
just templates-approve # Approve current delta as new baseline