Raw capture â raw/installing-vs-compilers-from-commandline.md
url: https://marler8997.github.io/blog/fixed-windows/ date_fetched: 2026-07-05 backfilled: true
Imagine youâre maintaining a native project. You use Visual Studio for building on Windows, so you do the responsible thing and list it as a dependency
Build Requirements: Install Visual Studio
If youâre lucky enough not to know this yet, I envy you. Unfortunately, at this point even Boromir knowsâŚ
What you may not realize is, youâve actually signed up to be unpaid tech support for Microsoftâs âVisual Studio Installerâ. You might notice GitHub Issues becoming less about your code and more about broken builds, specifically on Windows. You find yourself explaining to a contributor that they didnât check the âDesktop development with C++â workload, but specifically the v143 build tools and the 10.0.22621.0 SDK. No, not that one, the other one. You spend less time on your project because youâre too busy being a human-powered dependency resolver for a 50GB IDE.
Saying âInstall Visual Studioâ is like handing contributors a choose-your-own-adventure book riddled with bad endings, some of which donât let you go back. Iâve had to re-image my entire OS more than once over the years.
On Linux, the toolchain is usually just a package manager command away. On the other hand, âVisual Studioâ is thousands of components. Itâs so vast that Microsoft distributes it with a sophisticated GUI installer where you navigate a maze of checkboxes, hunting for which âWorkloadsâ or âIndividual Componentsâ contain the actual compiler. Select the wrong one and you might lose hours installing something you donât need. Miss one, like âWindows 10 SDK (10.0.17763.0)â or âSpectre-mitigated libs,â and your build fails three hours later with a cryptic error like MSB8101. And heaven help you if you need to downgrade to an older version of the build tools for a legacy project.
The Visual Studio ecosystem is built on a legacy of âall-in-oneâ monoliths. It conflates the editor, the compiler, and the SDK into a single, tangled web. When we list âVisual Studioâ as a dependency, weâre failing to distinguish between the tool we use to write code and the environment required to compile it.
The pain compounds quickly:
Even after installation, compiling a single C file from the command line requires finding the Developer Command Prompt. Under the hood, this shortcut invokes vcvarsall.bat, a fragile batch script that globally mutates your environment variables just to locate where the compiler is hiding this week.
Ultimately, you end up with build instructions that look like a legal disclaimer:
âWorks on my machine with VS 17.4.2 (Build 33027.167) and SDK 10.0.22621.0. If you have 17.5, please see Issue #412. If you are on ARM64, godspeed.â
On Windows, this has become the âcost of doing businessâ. We tell users to wait three hours for a 20GB install just so they can compile a 5MB executable. Itâs become an active deterrent to native development.
Iâm not interested in being a human debugger for someone elseâs installer. I want the MSVC toolchain to behave like a modern dependency: versioned, isolated, declarative.
I spent a few weeks building an open source tool to make things better. Itâs called msvcup. Itâs a small CLI program. On good network/hardware, it can install the toolchain/SDK in a few minutes, including everything to cross-compile to/from ARM. Each version of the toolchain/SDK gets its own isolated directory. Itâs idempotent and fast enough to invoke every time you build. Letâs try it out.
Create hello.c and build.bat:
#include <stdio.h>
int main() { printf("Hello, World\n"); }
@setlocal
@if not exist msvcup.exe (
echo msvcup.exe: installing...
curl -L -o msvcup.zip https://github.com/marler8997/msvcup/releases/download/v2026_02_07/msvcup-x86_64-windows.zip
tar xf msvcup.zip
del msvcup.zip
) else (
echo msvcup.exe: already installed
)
@if not exist msvcup.exe exit /b 1
set MSVC=msvc-14.44.17.14
set SDK=sdk-10.0.22621.7
msvcup install --lock-file msvcup.lock --manifest-update-off %MSVC% %SDK%
@if %errorlevel% neq 0 (exit /b %errorlevel%)
msvcup autoenv --target-cpu x64 --out-dir autoenv %MSVC% %SDK%
@if %errorlevel% neq 0 (exit /b %errorlevel%)
.\autoenv\cl hello.c
And weâre done.
Believe it or not, this build.bat script replaces the need to âInstall Visual Studioâ. This script should run on any Windows system since Windows 10 (assuming it has curl/tar which have been shipped since 2018). It installs the MSVC toolchain, the Windows SDK and then compiles our program.
For my fellow Windows developers, go ahead and take a moment. Visual Studio canât hurt you anymore. The build.bat above isnât just a helper script; itâs a declaration of independence from the Visual Studio Installer. Our dependencies are fully specified, making builds reproducible across machines. And when those dependencies are installed, they wonât pollute your registry or lock you into a single global version.
Also note that after the first run, the msvcup commands take milliseconds, meaning we can just leave these commands in our build script and now we have a fully self-contained script that can build our project on virtually any modern Windows machine.
msvcup is inspired by a small Python script written by MÄrtiĹĹĄ MoĹžeiko. The key insight is that Microsoft publishes JSON manifests describing every component in Visual Studio, the same manifests the official installer uses. msvcup parses these manifests, identifies just the packages needed for compilation (the compiler, linker, headers, and libraries), and downloads them directly from Microsoftâs CDN. Everything lands in versioned directories under C:\msvcup\. For details on lock files, cross-compilation, and other features, see the msvcup README.md.
The astute will also notice that our build.bat script never sources any batch files to set up the âDeveloper Environmentâ. The script contains two msvcup commands. The first installs the toolchain/SDK, and like a normal installation, it includes âvcvarsâ scripts to set up a developer environment. Instead, our build.bat leverages the msvcup autoenv command to create an âAutomatic Environmentâ. This creates a directory that contains wrapper executables to set the environment variables on your behalf before forwarding to the underlying tools. It even includes a toolchain.cmake file which will point your CMake projects to these tools, allowing you to build your CMake projects outside a special environment.
At Tuple (a pair-programming app), I integrated msvcup into our build system and CI, which allowed us to remove the requirement for the user/CI to pre-install Visual Studio. Tuple compiles hundreds of C/C++ projects including WebRTC. This enabled both x86_64 and ARM builds on the CI as well as keeping the CI and everyone on the same toolchain/SDK.
The benefits:
installand- autoenvcommands are idempotent and complete in milliseconds when thereâs no work to do.No more âit works on my machine because I have the 2019 Build Tools installed.â No more registry-diving to find where cl.exe is hiding this week. With msvcup, your environment is defined by your code, portable across machines, and ready to compile in milliseconds.
msvcup focuses on the core compilation toolchain. If you need the full Visual Studio IDE youâll still need the official installer. For most native development workflows, though, it covers what you actually need.
Letâs try this on a real project. Hereâs a script that builds raylib from scratch on a clean Windows system. In this case, weâll just use the SDK without the autoenv:
@setlocal
set TARGET_CPU=x64
@if not exist msvcup.exe (
echo msvcup.exe: installing...
curl -L -o msvcup.zip https://github.com/marler8997/msvcup/releases/download/v2026_02_07/msvcup-x86_64-windows.zip
tar xf msvcup.zip
del msvcup.zip
)
set MSVC=msvc-14.44.17.14
set SDK=sdk-10.0.22621.7
msvcup.exe install --lock-file msvcup.lock --manifest-update-off %MSVC% %SDK%
@if %errorlevel% neq 0 (exit /b %errorlevel%)
@if not exist raylib (
git clone https://github.com/raysan5/raylib -b 5.5
)
call C:\msvcup\%MSVC%\vcvars-%TARGET_CPU%.bat
call C:\msvcup\%SDK%\vcvars-%TARGET_CPU%.bat
cmd /c "cd raylib\projects\scripts && build-windows"
@if %errorlevel% neq 0 (exit /b %errorlevel%)
@echo build success: game exe at:
@echo .\raylib\projects\scripts\builds\windows-msvc\game.exe
No Visual Studio installation. No GUI. No prayer. Just a script that does exactly what it says.
P.S. Here is a page that shows how to use msvcup to build LLVM and Zig from scratch on Windows.