PortableMSVC is a command-line utility for downloading, extracting, and managing a fully portable Microsoft C/C++ toolchain (MSVC + Windows SDK) on Windows—without requiring a full Visual Studio install.
- Fetch the latest (or specified) MSVC toolset and Windows SDK from the official Visual Studio release channel
- Download and extract ZIPs, VSIX, MSI, and embedded CABs via
msiexecwithout UI - Prune unneeded files (debug symbols, telemetry, etc.) to minimize disk usage
- Generate
env.json,activate.cmd,activate.ps1, andactivate.xshfor easy environment setup - Support for xonsh, PowerShell, and Command Prompt activation
- Register/unregister toolchains in HKCU\Environment, with automatic PATH backup
- Maintain multiple portable installs side-by-side via a simple JSON database
- Plumbum-based CLI with subcommands for listing, installing, and managing toolchains
- Windows 10+
- Python 3.12+
- May work on lower versions but not tested
msiexec.exeon PATH (standard on Windows)- Required Python packages (install via
pip install .orpip install -r requirements.txt):plumbum(CLI framework)winregenv(registry manipulation)filelock(atomic file/registry locking)
Using UV (https://github.com/astral-sh/uv) Recommended
uv tool install git+https://github.com/tgbender/portablemsvc@mainThe UV tool bin directory may need to be added to path
git clone https://github.com/your-org/portablemsvc.git
cd portablemsvc
pip install .This provides the portablemsvc console script on your PATH.
Run portablemsvc --help to view global options and subcommands.
search- Search available MSVC and SDK versionslist- List installed toolchainsinstall- Install a portable toolchainregister- Register toolchain into HKCU\Environmentunregister- Unregister toolchain from HKCU\Environmentinstall-from-lockfile- Reproducible install from a lockfileget-path- Get install path for build scripts
portablemsvc search [--channel release|preview] [--no-cache] [--full]Search for available MSVC and Windows SDK versions before installing.
portablemsvc install
[--host x64|x86|arm|arm64]
[--target x64|x86|arm|arm64|all]
[--msvc-version <major.minor>]
[--sdk-version <build>]
[--channel release|preview]
[--accept-license]
[--no-cache]
[--output <custom_dir>]By default, installs the latest x64 MSVC + SDK under:
%LOCALAPPDATA%\portable\msvc\msvc-<full_version>_sdk-<build>
Environment Variable Overrides:
| Variable | Purpose |
|---|---|
PORTABLEMSVC_CACHE |
Override download cache directory |
PORTABLEMSVC_DATA |
Override install directory |
PORTABLEMSVC_CONFIG |
Override config directory |
PORTABLEMSVC_TEMP |
Override temp directory |
Example:
set PORTABLEMSVC_CACHE=D:\cache\portablemsvc
portablemsvc installportablemsvc listDisplays for each install:
- ID
- Path
- MSVC (manifest) version
- MSVC (internal) build folder version
- SDK version
- Host & Targets
- Installed at timestamp
-
Register the toolchain into your user environment:
portablemsvc register [--id <install_id>]
-
Unregister (restore original PATH):
portablemsvc unregister [--id <install_id>]
-
Install the latest x64 toolchain
(you will be prompted to review and accept the Microsoft license terms):portablemsvc install
-
Register it into your environment:
portablemsvc register
-
Activate the environment (choose one):
Command Prompt:
activate.cmd
PowerShell:
.\activate.ps1
xonsh:
source activate.xsh
-
Verify:
where link.exe rustup show # should show x86_64-pc-windows-msvc cargo build # should invoke link.exe from your portable MSVC -
List all installs:
portablemsvc list
-
Switch to another install later:
portablemsvc register --id <other_install_id>
-
Cleanup:
portablemsvc unregister
The get-path command outputs the installation root for use in build scripts:
# Get path for latest install
MSVC_ROOT=$(portablemsvc get-path)
# Get path by lockfile (matches MSVC/SDK versions)
MSVC_ROOT=$(portablemsvc get-path --lockfile ./portablemsvc.lock)
# Get path by install ID
MSVC_ROOT=$(portablemsvc get-path --id <install_id>)
# Use in build
source "$MSVC_ROOT/activate.cmd"
nmake /f MakefileThe env.json file contains all environment variables needed for building:
CC,CXX,AR- Compiler pathsPATH,INCLUDE,LIB- Search pathsTOOL_VERSIONS- PE file versions of tools
For reproducible builds in CI, use a lockfile:
# Install and generate lockfile (commit this to your repo)
portablemsvc install --accept-license --output .\msvc
# In CI, install from the lockfile for bit-for-bit reproducibility
portablemsvc install-from-lockfile portablemsvc.lock --accept-licenseGitHub Actions Example:
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- uses: astral-sh/setup-uv@v5
- run: uv tool install portablemsvc
- run: portablemsvc install-from-lockfile portablemsvc.lock --accept-license
env:
PORTABLEMSVC_CACHE: D:\cache\portablemsvc
# Get the path and use it
- run: |
MSVC_PATH=$(portablemsvc get-path --lockfile portablemsvc.lock)
"$MSVC_PATH/activate.cmd" && cargo build --release
shell: bashContributions and issues are welcome!
- Run
mise run lintto check code style and types - Run
mise run testto run the test suite - Keep PRs focused on a single feature or bugfix
- Add tests for all new behavior
This project is MIT-licensed. See LICENSE for details.
The MSVC and Windows SDK toolchains downloaded by PortableMSVC remain subject to Microsoft's license terms and conditions.
By using this tool to fetch, install, or manage those toolchains, you agree to comply with all applicable Microsoft licensing requirements.
Huge thanks to @mmozeiko for foundational work on portable MSVC tooling inspiration.
Run the test suite:
# Fast tests (no downloads)
mise run test
# Most tests including slow_installs but not integration (~20GB downloads)
mise run test-most
# Full tests including integration (~20GB downloads)
mise run test-allTest coverage includes:
- C/C++ compilation with Windows SDK headers
- Static library creation with
lib.exe - Tool version capture (PE file metadata)
- Environment variable verification
- Lockfile-based reproducible installs
- Verify that
portablemsvc install --target=x64,arm64,arm- all specified host/target tool directories appear under
VC/Tools/MSVC/.../bin - correct compiler/linker executables (
cl.exe,link.exe) are present per target
- all specified host/target tool directories appear under
- Add integration tests covering the
--target=allshorthand - Ensure generated
env.jsonand activation scripts include all targets - Confirm redistribution DLLs (
msdia140.dll, debug CRTs) deploy properly per target - Exercise cross‐compilation scenarios (e.g. Host=x64 → Target=arm)
- Automate CI runs on Windows 10 and 11 to catch platform‐specific issues