I'm mostly writing this here for my future self.
As you know, you kind of need a complete C compiler toolchain installed in order to build Rust apps. You don't usually need to interact with it directly, but it has to be there, and cargo/rustc needs to know where it is.
The default Rust build target on Windows, x86_64-pc-windows-msvc, uses Microsoft's Visual Studio compiler toolchain.
Installing or updating Visual Studio is a powerfully hellish experience, to be avoided at all costs, and, when un-avoidable, to be forgotten as soon as practicable.
I had done that at some point on my gaming PC, but the upgraded version of the Tracy client in my game engine required some symbol that was only recently added to the OS SDK, so I needed to upgrade the native toolchain and the SDK.
Now, for as much time as I spend pissed off about the current direction of the Mac platform, at least on Mac you have the option of installing an official package called "Xcode command line tools", which gives you all of the compiler infrastructure you need to build regular Unix-world stuff without installing dozens of gigabytes of Actual Xcode IDE.
I had recently come across something called msvcup that claimed to offer a very similar experience on Windows. Better yet, it promised to let you maintain multiple local versions of the toolchains, and lock projects to the stuff they need. (Here's the developer's grandiosely titled blog post introducing it.) Could I get away with just using that, and installing a separate little toolchain off to the side of VS proper?
Well, not out of the box, but with a small amount of extra finagling, yes.
As part of the install, msvcup adds some vcvars-ARCH.bat files, to assist with setting the PATH, LIB, and INCLUDE environment variables to reference this version of the toolchain. By default, these env vars aren't ever set correctly in your shell; Visual Studio usually requires you to start a "Developer Command Prompt" via a special start menu item, which lets it finagle things properly according to its own internal configuration.
However, even after sourcing that batch file and setting the vars correctly, Rust was still accessing the wrong (outdated) versions of the C toolchain and SDK. That's because it needs three more environment variables to be set correctly: VCINSTALLDIR, WindowsSdkDir, and WindowsSdkVersion. As a batch file following the pattern of the ones msvcup made, that would look like:
@REM Remember to update the SDK version string below when you upgrade. -NF
set "VCINSTALLDIR=%~dp0VC\"
set "WindowsSdkDir=%~dp0Windows Kits\10\"
set "WindowsSdkVersion=10.0.22621.0"
...with that last version number matching whatever directory name is inside Windows Kits/10/Include/.
The reason for all this is that if you're just running a script to compile a C project, you're probably just relying on the shell resolving cl.exe and link.exe to the right executables and on having the include path correctly populated so the compiler can find headers and link to dlls; in that scenario, the original three vars are enough. But Rust's Cargo build system ends up doing a bunch of really dank and cursed logic to automatically discover Visual Studio toolchains -- which finally answers my longstanding question of why cargo build actually works fine even when you're not in a VS "Developer Command Prompt"! When processing a build.rs script, it ends up delegating to a crate called find-msvc-tools; this library has a clean early exit if you ARE running in a VS dev shell (and it can just rely on PATH/LIB/INCLUDE being correct), but if it doesn't spot those three extra env vars (which the VS dev shell sets!), then it goes into a fallback path through the registry and some COM interfaces that will pretty reliably find the VS version you didn't want to use, and it'll override PATH/LIB/INCLUDE with that.
Anyway, the upshot is:
- Download msvcup and install it somewhere in your $PATH.
- Remember that to modify your $PATH on Windows, you go to the start menu and search for "environment"; it should turn up a shortcut to the cursed deeply nested settings dialog you need.
- Make a folder somewhere to hold your toolchain stuff. I decided I only really wanted one central one, rather than trying to do something per-project, since that would require making build scripts or whatever.
- Run the install command and install at least the msvc and sdk packages.
- Make the batch script with extra env vars as depicted above, and put it in the same directory as the others.
- Rig up your shell so that it'll source
vcvars-x64.batandwhatever-yours-is-called.baton startup when you're gonna be working on Rust projects, however you want to do that.- For Powershell, it can't natively source batch files (it just wants to run them in a subshell where they can't pollute the environment the way you want em to), so you need to install Powershell Community Extensions and use
Invoke-BatchFile FILE. Also, your shell profile is located at~/Documents/PowerShell/Profile.ps1, since (if you're me) you've probably forgotten that by now and can't find it in any of the normal places where you'd expect a shell profile.
- For Powershell, it can't natively source batch files (it just wants to run them in a subshell where they can't pollute the environment the way you want em to), so you need to install Powershell Community Extensions and use
Anyway, even after getting all that sorted, it turned out that the current sdk-10.0.26100.15 package being installed by msvcup was missing a critical header file for some stupid and unknown reason, but at least that was a different compilation error, which as we all know counts as Progress. I downgraded to the prior version and everything built fine. fuck yeah.