Repository navigation
[MSYS2] Allow default host triple to be overridden by an environment variable #5024
Description
Problem you are trying to solve
MSYS2 on Windows provides multiple development environments which can share the same $HOME and therefore the same rustup configuration, but which require different Rust host ABIs. For example:
native Windows → x86_64-pc-windows-msvc
UCRT64 → x86_64-pc-windows-gnu
CLANG64 → x86_64-pc-windows-gnullvm
The appropriate host is therefore a property of the current development environment rather than of the user, machine, or project.
At the same time, a project may want to pin its Rust version without pinning its host ABI:
[toolchain]
channel = "1.97"Ideally, the same project would then resolve that partial toolchain specification according to the active environment:
native Windows → 1.97-x86_64-pc-windows-msvc
UCRT64 → 1.97-x86_64-pc-windows-gnu
CLANG64 → 1.97-x86_64-pc-windows-gnullvm
Rustup already supports this distinction conceptually: an unqualified toolchain such as 1.97 is completed using rustup's default host. However, the default host is persisted in rustup's configuration, so environments sharing the same rustup home cannot have different defaults.
RUSTUP_TOOLCHAIN does not solve the problem because it selects the complete toolchain and overrides rust-toolchain.toml. Using it to select the ABI would therefore also override the project's choice of Rust version.
Separate RUSTUP_HOMEs can provide separate persisted default hosts, but that makes the host choice a property of separate rustup installations. For environments such as MSYS2, the host choice is naturally a property of the currently active shell environment, while the environments otherwise share the same home directory, projects, and installed tooling.
Solution you'd like
Add an environment variable, perhaps RUSTUP_DEFAULT_HOST, which overrides rustup's configured default host when resolving a toolchain specification that does not contain an explicit host.
For example:
export RUSTUP_DEFAULT_HOST=x86_64-pc-windows-gnullvmcombined with:
[toolchain]
channel = "1.97"would resolve to:
1.97-x86_64-pc-windows-gnullvm
This would allow environments such as MSYS2 UCRT64 and CLANG64 to set their appropriate default host during environment initialization while continuing to share the same RUSTUP_HOME.
The environment variable would affect only resolution of the omitted host component. It would not select or override the channel/version as RUSTUP_TOOLCHAIN does.
Conceptually, host resolution could follow:
explicit host in toolchain specification
↓
RUSTUP_DEFAULT_HOST
↓
configured default_host_triple
↓
normal rustup host detection/defaulting
An explicitly host-qualified toolchain would therefore remain authoritative, while an unqualified toolchain could inherit its host ABI from the current execution environment.
Notes
No response
Activity
rami3l commentedon Aug 20, 2026
@pgalbraith Hello, and thanks for filing this issue!
I think the problem reported here is quite reasonable and the suggested solution might even play quite well with #4947, but I remember that there had been some concerns WRT the extra complexity introduced by adding new environment variable overrides to this project.
@rust-lang/rustup do you think this would be a nice feature to have? If not, what potential alternatives could/should we suggest? 🙏
rami3l commentedon Aug 20, 2026
@pgalbraith Also, I have an additional question since I'm not super familiar with MSYS2 to see if this is the best way forward: in other environments, we'd expect the host machine to have a single main host tuple and multiple target tuples, overridable with
CARGO_BUILD_TARGET. What is preventing you from using that in your case?pgalbraith commentedon Aug 20, 2026
@rami3l Thanks for picking up on the topic, I hope others can weigh in because I feel I'm too new to Rust, especially, to confidently drive this home. My suggestion and PR here are my best read on the situation after only a few days with Rust :-).
If you're using MSYS2 environments on windows, you can easily land in one of three relatively independent toolchain environments depending on your env config: either standard windows powershell with msvc toolchain, msys2 bash shell with gnu toolhain, or msys2 bash shell with llvm toolchain. So the 'host' is really describing your build toolchain, it's the build toolchain that varies from env to env, not the build target. My understanding varying CARGO_BUILD_TARGET is you're not varying your build toolchain, rather just the target ABI, so this doesn't solve the problem. I hope it makes sense :-).
rami3l commentedon Aug 20, 2026
@pgalbraith Thanks for your reply, but I think I may need to know your original issue to see the full picture.
What I was trying to say is that if you just want to compile a single project to different target platforms, then installing 3 different toolchains of different host tuples on the same machine makes little sense, as you should probably just install the 3 targets on a single toolchain of your choice (say MSVC) and compile to those targets, and that final part is absolutely overridable with
CARGO_BUILD_TARGET.Otherwise, the only possibility I can think of is that you're relying on certain parts of the host toolchain for your particular goal, not just the compiled artifacts from your project? What would that be?
ChrisDenton commentedon Aug 20, 2026
cc @mati865, in case you have any thoughts on switching between different msys2/cygwin environments with rust.
pgalbraith commentedon Aug 20, 2026
The smoking gun, for me, is the user guide for windows, which steers you to changing the default host, to work with MSYS2, rather than simply setting CARGO_BUILD_TARGET, which would be a much cleaner solution, if it worked.
rami3l commentedon Aug 20, 2026
@pgalbraith ... and the end of that page states exactly that you can use a different
CARGO_BUILD_TARGETto support a different target tuple:... as long as you have
rustup target added that tuple beforehand :)pgalbraith commentedon Aug 21, 2026
@rami3l Looking at this again, I agree, it's not standing up to scrutiny. My original problem has evaporated with correct target, and the best example of any remaining potential problem that I've been able to find, is in rust-libxml. For me, building this fails under msys (even with the correct target set), but I'm also not convinced this build script just isn't plain looking at the wrong env value.
I'll withdraw the PR and leave this issue to see if anyone with more rust/msys experience wants to chime in.
[-]Allow default host triple to be overridden by an environment variable[/-][+][MSYS2] Allow default host triple to be overridden by an environment variable[/+]mati865 commentedon Aug 24, 2026
I definitely can see the use case for that.
To make the situation clear how MSYS2 behaves, there are multiple sysroots, called environments (listing just the non-deprecated ones):
<path to MSYS2 install>/ucrt64)<path to MSYS2 install>/clang64)<path to MSYS2 install>/clangarm64)<path to MSYS2 install>/usr)Imagine if you had on Linux sysroots organised like that:
/glibc/musl/ucrtEach sysroot is a fully-fledged independent host toolchain, so you don't have to cross compile from glibc to musl. Instead, you just use
/musl/bin/ccwhich is MUSL native C compiler.Returning to the issue. You typically get
x86_64-pc-windows-gnuas the default Rustup host (which translates to now deprecated MSYS2/msys64environment, but/ucrt64also works). So, targetingx86_64-pc-windows-gnullvm(MSYS2/clang64environment) means you are cross-compiling fromx86_64-pc-windows-gnutox86_64-pc-windows-gnullvm.That wouldn't be an issue on Linux with RPATH, but on Windows, with its DLL Hell things are not as simple. Entering CLANG64 environment in MSYS2 puts
/clang64/binat the beginning of PATH, which depending on what you are building may break the cross-compilation fromx86_64-pc-windows-gnuthanks to mentioned DLL Hell. So, there are two options:rustup set default-host ...every time you change the environment, so you run the native toolchain/mingw64/binor/ucrt64/binbefore/clang64/binin PATH, but only when invoking CargoPersonally, I very rarely boot Windows nowadays. Although, when I do, I only use
gnullvmtoolchains. So, for me, settingrustup set default-host x86_64-pc-windows-gnullvmpermanentny checks out.ChrisDenton commentedon Aug 24, 2026
Maybe rustup should re-evaluate its gnu default.
rami3l commentedon Aug 24, 2026
@ChrisDenton Wait... would you mind clarifying?
I thought
x86_64-pc-windows-gnuwas never the default... As far as I can remember we default tomsvcand the user can override that. Am I missing something here?ChrisDenton commentedon Aug 24, 2026
It's overridden when using the
rustup-init.shinstall script.mati865 commentedon Aug 24, 2026
I think
rustup-init.exealso defaults to windows-gnu if you don't have MSVC installed. Or at least used to do that years ago.rami3l commentedon Aug 25, 2026
@ChrisDenton Oh, thanks for pointing that out! In the script I think we already have
pc-windows-gnullvmby default for Windows ARM64. The next natural question is: can/should we do the same for Windows x64?mati865 commentedon Aug 25, 2026
Because there is no AArch64
windows-gnutriple,windows-gnullvmis the only free/open source tooling for that arch.That wouldn't really help as you'd turn the problem in the other direction: you'd be cross-compiling from
x86_64-pc-windows-gnullvmtox86_64-pc-windows-gnurather than usingx86_64-pc-windows-gnunatively when using MINGW64 or UCRT64 environments in MSYS2.And keep in mind that
x86_64-pc-windows-gnuremains tier 1 platform whilex86_64-pc-windows-gnullvmis tier 2.pgalbraith commentedon Aug 30, 2026
Yes, exactly. E.g. how do I specify in a build script that "I want MSYS Python to execute X while building for target Y", vs. "I want Windows Python to execute X while building for target Y". The choice of Python varies by host, in that case.
Certainly, it's worth waiting for a case that proves it, but I think the argument is ideologically sound, and I expect the proof will come.
rami3l commentedon Aug 30, 2026
@pgalbraith Note that this feature has been available in today's rustup, but it's not documented in any way (on purpose):
rustup/src/dist/mod.rs
Lines 644 to 645 in 8d732ef
Looking back at your original issue which has triggered the whole discussion, I have the impression that some libraries, notably cross-language ones (those involve C/Python/Perl or whatnot in their build processes), are essentially hostile to cross compilation so we cannot rely on cross builds?
If so, then I am thinking about the user journey: what should we recommend regular users (i.e. not Windows experts) to use in order to get better overall compilation experience? Having to override
RUSTUP_OVERRIDE_HOST_TUPLEeach time still feels somewhat subpar.I highly suspect that this should be a problem with our host tuple detection (so far it only detects the host architecture), rather than a missing opportunity to add a new level of override (or complete its user experience):
rustup/src/dist/mod.rs
Lines 570 to 573 in 8d732ef
@ChrisDenton @mati865 How difficult would it be to add host environment detection WRT the ABI?
PS: Apart from that there is a small issue that
set_default_host_tuple()doesn't acceptnoneas a legal input, but that's a minor issue which can be resolved pretty easily.mati865 commentedon Aug 30, 2026
Why would you need to control which Python to run?
When cross-compiling, build scripts are always ran with the host toolchain. So, the first python executable found in PATH is going to be used.
Of course, to avoid path separator clashes (Cygwin vs Win32) you need a way to override rustup toolchain to match your env.
Am I reading it wrong, or does that only override the toolchain for install/update. The problem here is overriding it at the runtime, so something like
RUSTUP_TOOLCHAINbut for the host triple.Recommend using Linux for coding...
More seriously, setting
RUSTUP_OVERRIDE_HOST_TUPLEvia bash.rc/config.fish/.env is the only thing I can think of 🙃For example, MSYS2 sets
MSYSTEMthat describes which environment is currently used (of course only if you use dedicatedsource shell <msys/ucrt64/clang64/...>).RUSTUP_OVERRIDE_HOST_TUPLEcould be set the same way.I'm not sure if that is worth the trouble, but if you want, the most generic way would be to check what
cc --versionprints. So, it'd look sorta like this:cc --versionifccis present in PATH.clangand something likeTarget: x86_64-w64-windows-gnuorTarget: x86_64-w64-mingw32then it's likely windows-gnullvm.gccand something likeTarget: x86_64-w64-mingw32then it's windows-gnu.gccand something likeTarget: x86_64-pc-cygwinthen it's windows-gnu.Full outputs of
cc --verbosebelow. MSYS2 provides saidMSYSTEMenvironment variable, but it wouldn't apply anywhere else.You might be wondering why GCC, Clang and Rustc all use different strings for their target triple. There is no standardised structure for it, everybody just starts up with what seems reasonable to them, and then changes accumulate over time.
For example, GCC previously used
i686-pc-mingw32for the original MinGW (a bit dated Win32 API, 32-bit only), then when its fork - mingw-w64 was made (modern Win32 API, 32 and 64 bit), they added triples withw64in place of previouspc.Details
rami3l commentedon Aug 30, 2026
@mati865 Thanks for all the explanation! Especially regarding the target detection, that's actually discussed elsewhere previously IIRC, so definitely worth saving the details you've provided here 🙏
One point I'd love to clarify:
Since #4947 we have this (the demo is on my Mac):
So what @pgalbraith has asked is basically already implemented because rustup will try to call the
stablewith the host tuple you've specified, not the one it auto-detected.I see, so it'd be the same as what has been originally suggested here.
@pgalbraith What do you think has been going wrong with the building process of the libxml binding, does it really have to be resolved by switching the host tuple?
pgalbraith commentedon Aug 31, 2026
I can try to reason about this ideologically, but please understand I'm no longer speaking from personal experience here, I'm just reasoning from the outside of things. My own problem, which was the genesis of this issue, melted away rather quickly when I followed your advise. I am merely suspicious that there is still a thread of an underlying problem, and I'm really hoping someone more knowledgeable than I can confirm this. (i.e. please don't rely solely on my arguments at this point :-D).
For libxml, I think even that was not a good example, I suspect maybe that build script was just conflating build/target environment when it shouldn't have, and it couldn't find msys libxml because it was looking at host env and deciding "I need to find native windows libxml", when in fact it should have looked at the target env instead and located the msys lib instead (i.e. fault of the build script alone).
My current 10,000-foot view of this, goes roughly like this: there is a difference between *-gnu and *-msvc host toolchains, and even though I may have the elements of both fully installed and available, I may legitimately want the behaviour of one specific host toolchain and not the other. By my still very-limited understanding of the Rust ecosystem, I think those differences probably surface during executing of complex custom build scripts which may be making tool selections based on the host. If you agree that, in principle, yes there probably are some obscure cases of this, I'm just extending this argument a little to say, it probably makes sense to use a *-gnu (or gnullvm) host when building in an msys environment, and a *-msvc host when building outside of one, to more consistently support those edges (and the best way to do that is via the env var).
rami3l commentedon Sep 1, 2026
@pgalbraith Thanks for your clarification!
Given all the background provided here, I'd prefer keeping
RUSTUP_OVERRIDE_HOST_TUPLEas an undocumented workaround here, and keeping this issue open just in case we need to complete its UX and/or come up with more creative solutions.