Skip to content

Conversation

Yunuuuu
Copy link

@Yunuuuu Yunuuuu commented Sep 17, 2025

This PR introduces two new configure arguments, --with-features and --with-profile, which allow enabling features or profiles during installation.

It also respects the environment variables {pkg_name}_FEATURES and {pkg_name}_PROFILE.

@Yunuuuu
Copy link
Author

Yunuuuu commented Sep 17, 2025

fix #474

@Yunuuuu
Copy link
Author

Yunuuuu commented Sep 18, 2025

In addition, this PR disables the use of use_vscode() in tests, as it breaks snapshots in GitHub Actions checks.

Copy link

codecov bot commented Sep 18, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 81.28%. Comparing base (2084b8e) to head (7ebc13f).

Additional details and impacted files

Impacted file tree graph

Files with missing lines Coverage Δ
R/use_extendr.R 98.70% <100.00%> (+0.01%) ⬆️

... and 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@JosiahParry
Copy link
Contributor

Thank you so much for the PR!

I've not had a chance to give this a full review but my initial reaction is that I am highly skeptical of adding command line args to the tools/config.R file which gets called during the configure step. Moreover these args are hard coded to positions 1 and 2 to handle profile and features.

Do you have a reproducible example of what this problem solves and why you would want to specify different features arguments in the build step of the R package? Are you going to create multiple different binaries of the same R package with different functionalities?

@Ilia-Kosenkov
Copy link
Member

I agree, looks a bit strange. Also test fix is a bit unclean: you should not modify production code to satisfy tests rather you should modify tests to run correctly.

@Yunuuuu
Copy link
Author

Yunuuuu commented Sep 19, 2025

Thanks for the comment. I need the features flag to control the usage of test and benchmark functionality.

If configuration arguments are not accpeted, at the very least there should be a features-related environment variable like profile (Currently, it seems rextendr doesn’t provide profile-specific environment variables, though I recall that savvy supports this)` so users can explicitly select which features to enable.

For example, the following function should only be available when the bench feature is enabled (This is particularly important in cases where we want to run benchmarks from the R side by passing R objects into Rust functions):

#[extendr]
#[cfg(feature = "bench")]
fn pprof_kractor_koutput(
    pprof_file: &str,
) -> std::result::Result<(), String> {
    let guard = pprof::ProfilerGuardBuilder::default()
        .frequency(2000)
        .build()
        .map_err(|e| format!("cannot create profile guard {:?}", e))?;
    // ...; actual code
    if let Ok(report) = guard.report().build() {
        let file = std::fs::File::create(pprof_file).map_err(|e| {
            format!("Failed to create file {}: {}", pprof_file, e)
        })?;
        let mut options = pprof::flamegraph::Options::default();
        options.image_width = Some(2500);
        report
            .flamegraph_with_options(file, &mut options)
            .map_err(|e| {
                format!("Failed to write flamegraph to {}: {}", pprof_file, e)
            })?;
    };
    out
}

Regarding the test issue: it cannot be fixed within the unit test file. The reason is that use_extendr() checks is_vscode() || is_positron() to decide whether use_vscode() should be applied. If tests are executed from VS Code, a failure will always occur because the snapshot is generated without VS Code support.

@Yunuuuu
Copy link
Author

Yunuuuu commented Sep 19, 2025

I want to clarify that this does not depend on the first or second argument, but specifically on the --with-features and --with-profile flags passed through install.packages(configure.args = "--with-features=..."). In the current implementation (without this PR), configure.args is completely ignored.

@JosiahParry
Copy link
Contributor

configure.args is new to me as is configure.vars.

I think @Ilia-Kosenkov's point is one that I agree with strongly. The R package should be the same whenever built. Typically benchmarks are called directly from the functions that they're testing. Otherwise the benchmarks are not actually comparable.

Am I reading your code correctly that your key need for this is for a flamegraph? This would be for profiling rather than benchmarks or testing.
https://github.com/WangLabCSU/mire/blob/566070a04b4776049e03e79e1b6d516a98b0c38f/src/rust/src/kractor/mod.rs#L9

An alternative approach to this would be to use use something like the below which is some pseudo-code that would use a oncelock and an env var to perform the profiling if a given environment variable is provided

let guard = OnceLock::new();
let prof = std::env::var("MIRE_PROF")
if let Some(prof_path) =  prof {
     guard.get_or_init(|| pprof::ProfilerGuardBuilder::default()
        .frequency(2000)
        .build()
        .with_context(|| format!("cannot create profile guard"))
        .map_err(|e| format!("{:?}", e))?);
}


let res = (); // snip some code sets the result 

if let Some(_) = prof {
  if let Ok(report) = guard.report().build() {
        let file = std::fs::File::create(pprof_file)...// flame graph stuff here 
  }
}

res

I think perhaps a more flexible solution may be to have an @CARGO_ARGS@ placeholder that can be added at the end of the cargo build call or a CARGO_ENV option that can be added before the cargo build step

savvy provides a savvy-cli which allows you to write rust tests https://yutannihilation.github.io/savvy/guide/test.html. But I think (and this is my perspective not everyone else's) many of us would suggest testing / benching in R rather than rust so that you have apples to apples comparisons. But to my knowledge you can't necessarily inject command line args as needed.

@Yunuuuu
Copy link
Author

Yunuuuu commented Sep 19, 2025

Yes, it’s something like this: https://github.com/WangLabCSU/mire/blob/566070a04b4776049e03e79e1b6d516a98b0c38f/src/rust/src/kractor/mod.rs#L9.

Thanks for the recommendations. Yes, I want to profile the rust code, and initially I wanted to do it from R, but I couldn’t find a way to profile the Rust code directly through R. As a workaround, I need to add a pprof dependency solely for profiling purposes—it won’t be used by the end user and therefore shouldn’t be included in the final package.

It would be even better if CARGO_ARGS could be set to handle this. Even better would be if we could pass configure.args directly to cargo, so that R build arguments and Cargo build arguments are properly aligned.

I remember savvy can set PROFILE/FEATURES-related environment variables, https://github.com/yutannihilation/savvy/blob/3304965c0086406fd2b85a7326ce60d406445b32/R-package/configure#L36 and
https://github.com/yutannihilation/savvy/blob/3304965c0086406fd2b85a7326ce60d406445b32/R-package/configure#L46

@Yunuuuu
Copy link
Author

Yunuuuu commented Sep 19, 2025

configure.args is also described in the R Extensions manual as a way to control the build process, which can be applied when using R CMD INSTALL:
https://rstudio.github.io/r-manuals/r-exts/Creating-R-packages.html#configure-example

image

@Ilia-Kosenkov
Copy link
Member

Writing R Extensions is truly a gift that keeps on giving. While the idea as a whole makes more sense now, two things:

  • We will have to think a bit internally about the best way of incorporating extra args, if at all. You are welcome to join us on our Discord server
  • I am not convinced that you are solving the correct problem here. If you need to benchmark rust code, benchmark rust code, R is not required there. The only reason to benchmark R + Rust is the conversion between R-Rust types happenning in the middleware. Everything else should be possible either purely in Rust or purely in R

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants