Skip to content

Simplify the lookup process for windows toolchains#1211

Open
andrjohns wants to merge 10 commits into
masterfrom
rtools-simplify
Open

Simplify the lookup process for windows toolchains#1211
andrjohns wants to merge 10 commits into
masterfrom
rtools-simplify

Conversation

@andrjohns

Copy link
Copy Markdown
Collaborator

Submission Checklist

  • Run unit tests
  • Declare copyright holder and agree to license (see below)

Summary

The current process for the toolchain on Windows is a little brittle, and requires hard-coding a new conditional on each RTools release. Starting from R4.2 the R_TOOLS_SOFT config variable was introduced to provide the path to the toolchain directory for the current installation - also allowing alternate packagers to specify a different default, which we can query directly.

The current PR updates to toolchain resolution to first check for a valid toolchain in the R_TOOLS_SOFT directory. If one is not found, it falls back to searching the PATH (via Sys.which()) as R automatically prepends the toolchain directories to the PATH on startup.

For R4.0 (before R_TOOLS_SOFT existed or the toolchain was automatically added to the PATH), it uses the original lookup of the RTOOLS40_HOME environment variable

Copyright and Licensing

Please list the copyright holder for the work you are submitting
(this will be you or your assignee, such as a university or company):
Andrew Johnson

By submitting this pull request, the copyright holder is agreeing to
license the submitted work under the following licenses:

@jgabry jgabry left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool thanks for doing this! I just saw the PR and happened to have a sec so I took a quick glance (I'll wait to look more thoroughly until you're ready) and I noticed one thing that has bitten me before so I made a suggested change

Comment thread R/install.R Outdated
# R 4.0 did not include the toolchain in the PATH by default
# or set the R_TOOLS_SOFT or LOCAL_SOFT config variables, so
# we use the RTOOLS40_HOME environment variable instead
if (R.version$major == 4 && R.version$minor < 2) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (R.version$major == 4 && R.version$minor < 2) {
if (current_r_version() < "4.2.0") {

Not a problem anytime soon, but because R.version$major actually returns a character we can get weird things like "10.0" < 2 evaluates to TRUE, so R 4.10 would incorrectly use Rtools40. current_r_version() returns getRversion() and has class numeric_version, which avoids this.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah that is classic R. Thanks for catching!

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we could just require R >= 4.2? Or do you think there are still enough older R versions in the wild that we should continue supporting it?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I think there might still be a bunch of older systems out there - where I work in gov we only left 4.2 a few months ago 😮‍💨

Pretty happy to only do the bare minimum for it though!

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah good point. I would guess some companies still use old R versions too, so we should keep supporting it

@codecov-commenter

codecov-commenter commented Jul 22, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 57.89474% with 8 lines in your changes missing coverage. Please review.
✅ Project coverage is 91.70%. Comparing base (05d5fa5) to head (c319008).

Files with missing lines Patch % Lines
R/install.R 57.89% 8 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1211      +/-   ##
==========================================
+ Coverage   90.96%   91.70%   +0.74%     
==========================================
  Files          15       15              
  Lines        6252     6184      -68     
==========================================
- Hits         5687     5671      -16     
+ Misses        565      513      -52     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

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

@andrjohns

Copy link
Copy Markdown
Collaborator Author

@jgabry this one's good to look at! CI is all green for standard RTools and I've also verified locally that we now support the MSYS-packaged R too

@jgabry

jgabry commented Jul 23, 2026

Copy link
Copy Markdown
Member

Cool, thanks @andrjohns! I'll take a look

@jgabry jgabry left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is great, thanks for doing this. I did a little research to try to understand R_TOOLS_SOFT and I tested a couple things. I have a couple of questions/concerns, but not sure how big a deal they are (see review comments).

Comment thread R/install.R
r_arch <- ifelse(Sys.getenv("R_ARCH") == "/i386", "mingw32", "mingw64")
rtools_soft <- file.path(rtools40_home, r_arch)
} else {
rtools_soft <- repair_path(tools::Rcmd(c("config", "R_TOOLS_SOFT"), stdout = TRUE))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will start an R CMD config subprocess every time toolchain_PATH_env_var() is called, right? Since that gets called in a ton of places in the package (every model fit, every CSV read, cpp_opts, etc.), should we compute it once and cache it somewhere (maybe an environment internal to the package)? I'm assuming it can't change within a single session.

That said, maybe it's not worth bothering to do this it if it's pretty fast. Could it add up enough to be noticeable? I defer to your judgement on this, since I'm not a windows users.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, what happens if R_TOOLS_SOFT isn't in Makeconf? Maybe that's not possible with CRAN R >= 4.2 but maybe it's possible for something like conda R?

I tried testing it on my Mac, which kind of simulates a windows distribution that doesn't have it, and R CMD config has a nonzero exit status and system2 throws a "running command ... had status 1" warning on every call to toolchain_PATH_env_var().

What if we do something like this:

out <- suppressWarnings(
  tools::Rcmd(c("config", "R_TOOLS_SOFT"), stdout = TRUE)
)
if (is.null(attr(out, "status")) && length(out) == 1) {
  rtools_soft <- repair_path(out) 
} else {
  rtools_soft <- ""
}

Comment thread R/install.R
Comment on lines -630 to -634
# If RTools is installed in a path with spaces or brackets
# we error as this path is not valid
if (grepl("\\(|)| ", rtools_path)) {
stop(
"\n", rtools_version, " is installed in a path with spaces or brackets, which is not supported.",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we keep something like this to make sure we error early when rtools is in a directory with spaces? Seems somewhat common

)
})
})

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're deleting hundreds of lines of tests, which is great, but is there anything we should be adding to the tests for the new logic? Makes me slightly nervous to not have anything. Or do you think our other existing tests cover everything we need?

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