Skip to main content

Installs Portable MSVC tools in user space

Project description

portablemsvc

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.

Features

  • 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 with pure-Python MSI extraction by default
  • Preserve msiexec extraction as an explicit fallback path
  • Prune unneeded files (debug symbols, telemetry, etc.) to minimize disk usage
  • Generate env.json, activate.cmd, activate.ps1, and activate.xsh for 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

Requirements

  • Windows 10+
  • Python 3.10+
  • msiexec.exe on PATH only when using PORTABLEMSVC_MSI_EXTRACTOR=msiexec or fallback

Installation

Using UV (https://github.com/astral-sh/uv) Recommended

uv tool install portablemsvc

The UV tool bin directory may need to be added to PATH.

Without installing

uvx portablemsvc --help
uvx portablemsvc search

Using pip

python -m pip install portablemsvc

Alternative: Clone and install locally

git clone https://github.com/tgbender/portablemsvc.git
cd portablemsvc
pip install .

This provides the portablemsvc console script on your PATH.

Usage

Run portablemsvc --help to view global options and subcommands.

Microsoft License Terms

PortableMSVC is not affiliated with Microsoft. It downloads MSVC and Windows SDK packages from Microsoft's official Visual Studio release channel, and those packages remain subject to Microsoft's license terms.

Before installing a toolchain, review the applicable Microsoft terms for your use case:

Passing --accept-license means you have reviewed and accept the applicable Microsoft terms for the packages PortableMSVC downloads and installs. Do not redistribute downloaded packages, cache contents, or extracted toolchains unless Microsoft's terms allow it.

Subcommands

  • search - Search available MSVC and SDK versions
  • list - List installed toolchains
  • install - Install a portable toolchain
  • register - Register toolchain into HKCU\Environment
  • unregister - Unregister toolchain from HKCU\Environment
  • install-from-lockfile - Reproducible install from a lockfile
  • get-path - Get install path for build scripts

Search Available Versions

portablemsvc search [--channel release|preview] [--no-cache] [--full]

Search for available MSVC and Windows SDK versions before installing.

Install a Portable Toolchain

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>

If --output points at an existing directory, PortableMSVC will only replace it when it is empty or already looks like a PortableMSVC install. It refuses to replace arbitrary non-empty directories.

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
PORTABLEMSVC_MSI_EXTRACTOR Select MSI extractor: auto/pymsi (default), msiexec, or fallback

Example:

set PORTABLEMSVC_CACHE=D:\cache\portablemsvc
portablemsvc install

Downloaded package payloads are cached by SHA256 so older toolchain versions can coexist for lockfile installs. Visual Studio manifests are also retained by downloaded content hash, with URL metadata pointing at the latest cached body.

List Installed Toolchains

portablemsvc list

Displays for each install:

  • ID
  • Path
  • MSVC (manifest) version
  • MSVC (internal) build folder version
  • SDK version
  • Host & Targets
  • Installed at timestamp

Register / Unregister

  • Register the toolchain into your user environment:

    portablemsvc register [--id <install_id>]
    
  • Unregister (restore original PATH):

    portablemsvc unregister [--id <install_id>]
    

Example Workflow

  1. Install the latest x64 toolchain (you will be prompted to review and accept the Microsoft license terms):

    portablemsvc install
    
  2. Register it into your environment:

    portablemsvc register
    
  3. Activate the environment (choose one):

    Command Prompt:

    activate.cmd
    

    PowerShell:

    .\activate.ps1
    

    xonsh:

    source activate.xsh
    
  4. Verify:

    where link.exe
    rustup show        # should show x86_64-pc-windows-msvc
    cargo build        # should invoke link.exe from your portable MSVC
    
  5. List all installs:

    portablemsvc list
    
  6. Switch to another install later:

    portablemsvc register --id <other_install_id>
    
  7. Cleanup:

    portablemsvc unregister
    

Build Script Integration

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
& "$MSVC_ROOT\activate.ps1"
nmake /f Makefile

The env.json file contains all environment variables needed for building:

  • CC, CXX, AR - Compiler paths
  • PATH, INCLUDE, LIB - Search paths
  • TOOL_VERSIONS - PE file versions of tools

CI/CD Usage

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 reproducible package selection
portablemsvc install-from-lockfile portablemsvc.lock --accept-license

Lockfiles are executable supply-chain input: they contain exact package URLs and hashes. Only use lockfiles from repositories and commits you trust.

GitHub 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: |
    $msvcPath = portablemsvc get-path --lockfile portablemsvc.lock
    & "$msvcPath\activate.ps1"
    cargo build --release
  shell: pwsh

Contributing

Contributions and issues are welcome!

  • Run mise run lint to check code style and types
  • Run mise run test to run the test suite
  • Run mise run test-most before changing installer behavior
  • Keep PRs focused on a single feature or bugfix
  • Add tests for all new behavior

Publishing

Releases are published from the manual GitHub Actions release workflow on the main branch. The publish job uses PyPI trusted publishing through the pypi environment, which requires reviewer approval and a 60 second wait timer before upload.

License

This project is MIT-licensed. See LICENSE for details.

Disclaimer

PortableMSVC is provided as-is. You are responsible for complying with the licenses for any Microsoft packages you download, install, use, or redistribute.

Acknowledgments

Huge thanks to @mmozeiko for foundational work on portable MSVC tooling inspiration.

Testing 🔍

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-all

Test 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

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

portablemsvc-0.3.0.tar.gz (40.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

portablemsvc-0.3.0-py3-none-any.whl (48.3 kB view details)

Uploaded Python 3

File details

Details for the file portablemsvc-0.3.0.tar.gz.

File metadata

  • Download URL: portablemsvc-0.3.0.tar.gz
  • Upload date:
  • Size: 40.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.15 {"installer":{"name":"uv","version":"0.11.15","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for portablemsvc-0.3.0.tar.gz
Algorithm Hash digest
SHA256 36faca6b0c91c6751f9e0df2675d1f9ec8766ebd074f956e031df641669f70fe
MD5 af2ca979e6f402194a14e0fc8ab477e2
BLAKE2b-256 3a78e301cc13fb0c657d0d53e149f520a7363c36ef58a5e00c13948eafb55c3b

See more details on using hashes here.

File details

Details for the file portablemsvc-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: portablemsvc-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 48.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.15 {"installer":{"name":"uv","version":"0.11.15","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for portablemsvc-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 e3b735fb763cf226ac2c405ab0e3031e4243f23b30b325e026c84013b8638bf1
MD5 1265140d80a778315bbbe14af6b782bb
BLAKE2b-256 52d2d7d4807a4c16eafdb21611697bf60cac089b55cf5f45a1c3302523da549a

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page