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
.Rbuildignoreinst/templates/*/Rbuildignore
bootstrap.Rinst/templates/*/bootstrap.R
build.rsinst/templates/*/build.rs
cleanup, cleanup.win, cleanup.ucrtinst/templates/*/cleanup*
configure.acinst/templates/*/configure.ac
configure.win, configure.ucrtinst/templates/*/configure.*
src/stub.cinst/templates/*/stub.c
src/Makevars.ininst/templates/*/Makevars.in
src/rust/build.rsinst/templates/*/build.rs
src/rust/Cargo.tomlinst/templates/*/Cargo.toml
src/rust/cargo-config.toml.ininst/templates/*/cargo-config.toml.in
src/rust/lib.rsinst/templates/*/lib.rs (with {{package_rs}} substitution)
R/rpkg-package.Rinst/templates/*/package.R
inst/include/mx_abi.hinst/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 metadata
  • R/*.R - R function implementations
  • src/rust/*.rs - Example Rust code (except lib.rs template)
  • tests/ - Test files
  • man/ - Generated documentation

πŸ”—Implementation Details

πŸ”—templates-check Recipe

The check recipe:

  1. Copies rpkg files to a temp directory
  2. Applies patches/templates.patch to reverse approved differences
  3. Compares with actual templates
  4. Fails if any unexpected differences found

πŸ”—templates-approve Recipe

The approve recipe:

  1. Copies rpkg files to a temp directory
  2. Compares with actual templates
  3. Generates a new patches/templates.patch with current delta
  4. 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

  1. βœ… Make change in rpkg/
  2. βœ… Test thoroughly (just devtools-test)
  3. βœ… Apply to templates
  4. βœ… Approve delta
  5. ❌ 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:

  1. The example package (rpkg/) is the master source
  2. Templates are derived copies with minimal additions
  3. patches/templates.patch tracks approved differences
  4. Always test in the example package before updating templates
  5. Run just templates-check before committing
  6. Use just templates-approve to update the approved delta

Commands:

just templates-check    # Verify templates match rpkg (with approved delta)
just templates-approve  # Approve current delta as new baseline