Skip to content

Instantly share code, notes, and snippets.

@mmozeiko
Last active May 2, 2024 09:51
Show Gist options
  • Save mmozeiko/7f3162ec2988e81e56d5c4e22cde9977 to your computer and use it in GitHub Desktop.
Save mmozeiko/7f3162ec2988e81e56d5c4e22cde9977 to your computer and use it in GitHub Desktop.
Download MSVC compiler/linker & Windows SDK without installing full Visual Studio

This downloads standalone 64-bit MSVC compiler, linker & other tools, also headers/libraries from Windows SDK into portable folder, without installing Visual Studio. Has bare minimum components - no UWP/Store/WindowsRT stuff, just files & tools for 64-bit native desktop app development.

Run python.exe portable-msvc.py and it will download output into msvc folder. By default it will download latest available MSVC & Windows SDK - currently v14.32.17.2 and v10.0.22621.0.

You can list available versions with python.exe portable-msvc.py --show-versions and then pass versions you want with --msvc-version and --sdk-version arguments.

To use cl.exe/link.exe from output folder, first run setup.bat - after that PATH/INCLUDE/LIB env variables will be setup to use all the tools as usual. You can also use clang-cl.exe with these includes & libraries.

To use clang-cl.exe without running setup.bat, pass extra /winsysroot msvc argument (msvc is folder name where output is stored).

#!/usr/bin/env python3
import io
import os
import sys
import stat
import json
import shutil
import hashlib
import zipfile
import tempfile
import argparse
import subprocess
import urllib.error
import urllib.request
from pathlib import Path
OUTPUT = Path("msvc") # output folder
DOWNLOADS = Path("downloads") # temporary download files
# other architectures may work or may not - not really tested
HOST = "x64" # or x86
TARGET = "x64" # or x86, arm, arm64
MANIFEST_URL = "https://aka.ms/vs/17/release/channel"
MANIFEST_PREVIEW_URL = "https://aka.ms/vs/17/pre/channel"
ssl_context = None
def download(url):
with urllib.request.urlopen(url, context=ssl_context) as res:
return res.read()
total_download = 0
def download_progress(url, check, name, filename):
fpath = DOWNLOADS / filename
if fpath.exists():
data = fpath.read_bytes()
if hashlib.sha256(data).hexdigest() == check.lower():
print(f"\r{name} ... OK")
return data
global total_download
with fpath.open("wb") as f:
data = io.BytesIO()
with urllib.request.urlopen(url, context=ssl_context) as res:
total = int(res.headers["Content-Length"])
size = 0
while True:
block = res.read(1<<20)
if not block:
break
f.write(block)
data.write(block)
size += len(block)
perc = size * 100 // total
print(f"\r{name} ... {perc}%", end="")
print()
data = data.getvalue()
digest = hashlib.sha256(data).hexdigest()
if check.lower() != digest:
exit(f"Hash mismatch for f{pkg}")
total_download += len(data)
return data
# super crappy msi format parser just to find required .cab files
def get_msi_cabs(msi):
index = 0
while True:
index = msi.find(b".cab", index+4)
if index < 0:
return
yield msi[index-32:index+4].decode("ascii")
def first(items, cond):
return next(item for item in items if cond(item))
### parse command-line arguments
ap = argparse.ArgumentParser()
ap.add_argument("--show-versions", action="store_true", help="Show available MSVC and Windows SDK versions")
ap.add_argument("--accept-license", action="store_true", help="Automatically accept license")
ap.add_argument("--msvc-version", help="Get specific MSVC version")
ap.add_argument("--sdk-version", help="Get specific Windows SDK version")
ap.add_argument("--preview", action="store_true", help="Use preview channel for Preview versions")
args = ap.parse_args()
### get main manifest
URL = MANIFEST_PREVIEW_URL if args.preview else MANIFEST_URL
try:
manifest = json.loads(download(URL))
except urllib.error.URLError as err:
import ssl
if isinstance(err.args[0], ssl.SSLCertVerificationError):
# for more info about Python & issues with Windows certificates see https://stackoverflow.com/a/52074591
print("ERROR: ssl certificate verification error")
try:
import certifi
except ModuleNotFoundError:
print("ERROR: please install 'certifi' package to use Mozilla certificates")
print("ERROR: or update your Windows certs, see instructions here: https://woshub.com/updating-trusted-root-certificates-in-windows-10/#h2_3")
exit()
print("NOTE: retrying with certifi certificates")
ssl_context = ssl.create_default_context(cafile=certifi.where())
manifest = json.loads(download(URL))
else:
raise
### download VS manifest
ITEM_NAME = "Microsoft.VisualStudio.Manifests.VisualStudioPreview" if args.preview else "Microsoft.VisualStudio.Manifests.VisualStudio"
vs = first(manifest["channelItems"], lambda x: x["id"] == ITEM_NAME)
payload = vs["payloads"][0]["url"]
vsmanifest = json.loads(download(payload))
### find MSVC & WinSDK versions
packages = {}
for p in vsmanifest["packages"]:
packages.setdefault(p["id"].lower(), []).append(p)
msvc = {}
sdk = {}
for pid,p in packages.items():
if pid.startswith("Microsoft.VisualStudio.Component.VC.".lower()) and pid.endswith(".x86.x64".lower()):
pver = ".".join(pid.split(".")[4:6])
if pver[0].isnumeric():
msvc[pver] = pid
elif pid.startswith("Microsoft.VisualStudio.Component.Windows10SDK.".lower()) or \
pid.startswith("Microsoft.VisualStudio.Component.Windows11SDK.".lower()):
pver = pid.split(".")[-1]
if pver.isnumeric():
sdk[pver] = pid
if args.show_versions:
print("MSVC versions:", " ".join(sorted(msvc.keys())))
print("Windows SDK versions:", " ".join(sorted(sdk.keys())))
exit(0)
msvc_ver = args.msvc_version or max(sorted(msvc.keys()))
sdk_ver = args.sdk_version or max(sorted(sdk.keys()))
if msvc_ver in msvc:
msvc_pid = msvc[msvc_ver]
msvc_ver = ".".join(msvc_pid.split(".")[4:-2])
else:
exit(f"Unknown MSVC version: f{args.msvc_version}")
if sdk_ver in sdk:
sdk_pid = sdk[sdk_ver]
else:
exit(f"Unknown Windows SDK version: f{args.sdk_version}")
print(f"Downloading MSVC v{msvc_ver} and Windows SDK v{sdk_ver}")
### agree to license
tools = first(manifest["channelItems"], lambda x: x["id"] == "Microsoft.VisualStudio.Product.BuildTools")
resource = first(tools["localizedResources"], lambda x: x["language"] == "en-us")
license = resource["license"]
if not args.accept_license:
accept = input(f"Do you accept Visual Studio license at {license} [Y/N] ? ")
if not accept or accept[0].lower() != "y":
exit(0)
OUTPUT.mkdir(exist_ok=True)
DOWNLOADS.mkdir(exist_ok=True)
### download MSVC
msvc_packages = [
# MSVC binaries
f"microsoft.vc.{msvc_ver}.tools.host{HOST}.target{TARGET}.base",
f"microsoft.vc.{msvc_ver}.tools.host{HOST}.target{TARGET}.res.base",
# MSVC headers
f"microsoft.vc.{msvc_ver}.crt.headers.base",
# MSVC libs
f"microsoft.vc.{msvc_ver}.crt.{TARGET}.desktop.base",
f"microsoft.vc.{msvc_ver}.crt.{TARGET}.store.base",
# MSVC runtime source
f"microsoft.vc.{msvc_ver}.crt.source.base",
# ASAN
f"microsoft.vc.{msvc_ver}.asan.headers.base",
f"microsoft.vc.{msvc_ver}.asan.{TARGET}.base",
# MSVC redist
#f"microsoft.vc.{msvc_ver}.crt.redist.x64.base",
]
for pkg in msvc_packages:
p = first(packages[pkg], lambda p: p.get("language") in (None, "en-US"))
for payload in p["payloads"]:
filename = payload["fileName"]
download_progress(payload["url"], payload["sha256"], pkg, filename)
with zipfile.ZipFile(DOWNLOADS / filename) as z:
for name in z.namelist():
if name.startswith("Contents/"):
out = OUTPUT / Path(name).relative_to("Contents")
out.parent.mkdir(parents=True, exist_ok=True)
out.write_bytes(z.read(name))
### download Windows SDK
sdk_packages = [
# Windows SDK tools (like rc.exe & mt.exe)
f"Windows SDK for Windows Store Apps Tools-x86_en-us.msi",
# Windows SDK headers
f"Windows SDK for Windows Store Apps Headers-x86_en-us.msi",
f"Windows SDK Desktop Headers x86-x86_en-us.msi",
# Windows SDK libs
f"Windows SDK for Windows Store Apps Libs-x86_en-us.msi",
f"Windows SDK Desktop Libs {TARGET}-x86_en-us.msi",
# CRT headers & libs
f"Universal CRT Headers Libraries and Sources-x86_en-us.msi",
# CRT redist
#"Universal CRT Redistributable-x86_en-us.msi",
]
with tempfile.TemporaryDirectory() as d:
dst = Path(d)
sdk_pkg = packages[sdk_pid][0]
sdk_pkg = packages[first(sdk_pkg["dependencies"], lambda x: True).lower()][0]
msi = []
cabs = []
# download msi files
for pkg in sdk_packages:
payload = first(sdk_pkg["payloads"], lambda p: p["fileName"] == f"Installers\\{pkg}")
msi.append(DOWNLOADS / pkg)
data = download_progress(payload["url"], payload["sha256"], pkg, pkg)
cabs += list(get_msi_cabs(data))
# download .cab files
for pkg in cabs:
payload = first(sdk_pkg["payloads"], lambda p: p["fileName"] == f"Installers\\{pkg}")
download_progress(payload["url"], payload["sha256"], pkg, pkg)
print("Unpacking msi files...")
# run msi installers
for m in msi:
subprocess.check_call(["msiexec.exe", "/a", m, "/quiet", "/qn", f"TARGETDIR={OUTPUT.resolve()}"])
### versions
msvcv = list((OUTPUT / "VC/Tools/MSVC").glob("*"))[0].name
sdkv = list((OUTPUT / "Windows Kits/10/bin").glob("*"))[0].name
# place debug CRT runtime files into MSVC folder (not what real Visual Studio installer does... but is reasonable)
dst = OUTPUT / "VC/Tools/MSVC" / msvcv / f"bin/Host{HOST}/{TARGET}"
DOWNLOAD_FOLDER = Path("crtd")
(DOWNLOADS / DOWNLOAD_FOLDER).mkdir(exist_ok=True)
pkg = "microsoft.visualcpp.runtimedebug.14"
dbg = first(packages[pkg], lambda p: p["chip"] == HOST)
for payload in dbg["payloads"]:
name = payload["fileName"]
download_progress(payload["url"], payload["sha256"], name, DOWNLOAD_FOLDER / name)
msi = DOWNLOADS / DOWNLOAD_FOLDER / first(dbg["payloads"], lambda p: p["fileName"].endswith(".msi"))["fileName"]
with tempfile.TemporaryDirectory() as d2:
subprocess.check_call(["msiexec.exe", "/a", str(msi), "/quiet", "/qn", f"TARGETDIR={d2}"])
for f in first(Path(d2).glob("System*"), lambda x: True).iterdir():
f.replace(dst / f.name)
# download DIA SDK and put msdia140.dll file into MSVC folder
DOWNLOAD_FOLDER = Path("dia")
(DOWNLOADS / DOWNLOAD_FOLDER).mkdir(exist_ok=True)
pkg = "microsoft.visualc.140.dia.sdk.msi"
dia = packages[pkg][0]
for payload in dia["payloads"]:
name = payload["fileName"]
download_progress(payload["url"], payload["sha256"], name, DOWNLOAD_FOLDER / name)
msi = DOWNLOADS / DOWNLOAD_FOLDER / first(dia["payloads"], lambda p: p["fileName"].endswith(".msi"))["fileName"]
with tempfile.TemporaryDirectory(dir=DOWNLOADS) as d2:
subprocess.check_call(["msiexec.exe", "/a", str(msi), "/quiet", "/qn", f"TARGETDIR={d2}"])
if HOST == "x86": msdia = "msdia140.dll"
elif HOST == "x64": msdia = "amd64/msdia140.dll"
else: exit("unknown")
# remove read-only attribute
target = dst / "msdia140.dll"
target.chmod(stat.S_IWRITE)
src = Path(d2) / "Program Files/Microsoft Visual Studio 14.0/DIA SDK/bin" / msdia
src.replace(target)
### cleanup
shutil.rmtree(OUTPUT / "Common7", ignore_errors=True)
for f in ["Auxiliary", f"lib/{TARGET}/store", f"lib/{TARGET}/uwp"]:
shutil.rmtree(OUTPUT / "VC/Tools/MSVC" / msvcv / f)
for f in OUTPUT.glob("*.msi"):
f.unlink()
for f in ["Catalogs", "DesignTime", f"bin/{sdkv}/chpe", f"Lib/{sdkv}/ucrt_enclave"]:
shutil.rmtree(OUTPUT / "Windows Kits/10" / f, ignore_errors=True)
for arch in ["x86", "x64", "arm", "arm64"]:
if arch != TARGET:
shutil.rmtree(OUTPUT / "Windows Kits/10/Lib" / sdkv / "ucrt" / arch)
shutil.rmtree(OUTPUT / "Windows Kits/10/Lib" / sdkv / "um" / arch)
if arch != HOST:
shutil.rmtree(OUTPUT / "VC/Tools/MSVC" / msvcv / f"bin/Host{arch}", ignore_errors=True)
shutil.rmtree(OUTPUT / "Windows Kits/10/bin" / sdkv / arch)
# executable that is collecting & sending telemetry every time cl/link runs
(OUTPUT / "VC/Tools/MSVC" / msvcv / f"bin/Host{HOST}/{TARGET}/vctip.exe").unlink(missing_ok=True)
### setup.bat
SETUP = f"""@echo off
set ROOT=%~dp0
set MSVC_VERSION={msvcv}
set MSVC_HOST=Host{HOST}
set MSVC_ARCH={TARGET}
set SDK_VERSION={sdkv}
set SDK_ARCH={TARGET}
set MSVC_ROOT=%ROOT%VC\\Tools\\MSVC\\%MSVC_VERSION%
set SDK_INCLUDE=%ROOT%Windows Kits\\10\\Include\\%SDK_VERSION%
set SDK_LIBS=%ROOT%Windows Kits\\10\\Lib\\%SDK_VERSION%
set VCToolsInstallDir=%MSVC_ROOT%\\
set PATH=%MSVC_ROOT%\\bin\\%MSVC_HOST%\\%MSVC_ARCH%;%ROOT%Windows Kits\\10\\bin\\%SDK_VERSION%\\%SDK_ARCH%;%ROOT%Windows Kits\\10\\bin\\%SDK_VERSION%\\%SDK_ARCH%\\ucrt;%PATH%
set INCLUDE=%MSVC_ROOT%\\include;%SDK_INCLUDE%\\ucrt;%SDK_INCLUDE%\\shared;%SDK_INCLUDE%\\um;%SDK_INCLUDE%\\winrt;%SDK_INCLUDE%\\cppwinrt
set LIB=%MSVC_ROOT%\\lib\\%MSVC_ARCH%;%SDK_LIBS%\\ucrt\\%SDK_ARCH%;%SDK_LIBS%\\um\\%SDK_ARCH%
"""
(OUTPUT / "setup.bat").write_text(SETUP)
print(f"Total downloaded: {total_download>>20} MB")
print("Done!")
@poonamj03
Copy link

Hi @florianingerl How did you solved the issue? Installing in C: Directory also give the same error.

@lolo120916
Copy link

lolo120916 commented Apr 24, 2024

Hi all. I would like to use the portable version of MSVC with VSCode and the associated Microsoft plugins (C/C++ and CMake-tools). Unfortunately, as expected, these tools rely, not on the simple env variables used, but on the official over-complicated mechanism for detecting the installation of MSVC (.bat of the MSVC console and also probably vswhere.exe and registry keys).
Does anyone know how to do the equivalent setup manually? I've tried many things by picking up technical elements from cmake, ninja, etc... but I haven't found any functional solution. If anyone knows how to do it, it would be great. Thanks

@mmozeiko
Copy link
Author

mmozeiko commented Apr 24, 2024

I don't know VScode, but cmake will find all the compiler executable from PATH - so as long as you run setup.bat it will just work. If you don't want to run setup.bat, you can manually specify cl.exe, link, lib.exe, etc.. locations with cmake variables.

I believe you'll need at least these:

  • CMAKE_C_COMPILER=path/to/cl.exe
  • CMAKE_CXX_COMPILER=path/to/cl.exe
  • CMAKE_C_COMPILER_LINKER=path/to/link.exe
  • CMAKE_CXX_COMPILER_LINKER=path/to/link.exe
  • CMAKE_AR_COMPILER=path/to/lib.exe
  • CMAKE_MT_COMPILER=path/to/mt.exe

Instead of manually specifying these variables every time, you can create cmake toolchain file and the pass it with -DCMAKE_TOOLCHAIN_FILE argument, see the docs here: https://cmake.org/cmake/help/latest/manual/cmake-toolchains.7.html

But again - easiest way is just to run setup.bat and then cmake -G Ninja path/to/project will just work. No need for vswhere or registry keys.

@sohale
Copy link

sohale commented Apr 25, 2024

Any Linux equivalent available? It tries to run msiexec.exe.
I get

...
f9ff50431335056fb4fbac05b8268204.cab ... 100%
Unpacking msi files...
Traceback (most recent call last):
  File "/home/ephemssss/novorender/ifc2brep-0/scripts/external-tools/portable-msvc.py", line 248, in <module>
    subprocess.check_call(["msiexec.exe", "/a", m, "/quiet", "/qn", f"TARGETDIR={OUTPUT.resolve()}"])
  File "/usr/lib/python3.10/subprocess.py", line 364, in check_call
    retcode = call(*popenargs, **kwargs)
  File "/usr/lib/python3.10/subprocess.py", line 345, in call
    with Popen(*popenargs, **kwargs) as p:
  File "/usr/lib/python3.10/subprocess.py", line 971, in __init__
    self._execute_child(args, executable, preexec_fn, close_fds,
  File "/usr/lib/python3.10/subprocess.py", line 1863, in _execute_child
    raise child_exception_type(errno_num, err_msg, err_filename)
FileNotFoundError: [Errno 2] No such file or directory: 'msiexec.exe'

@mmozeiko
Copy link
Author

It seems there exists msiextract tool, I have not tried using it myself: https://manpages.ubuntu.com/manpages/focal/man1/msiextract.1.html

@AndhikaWB
Copy link

.. but on the official over-complicated mechanism for detecting the installation of MSVC (.bat of the MSVC console and also probably vswhere.exe and registry keys). Does anyone know how to do the equivalent setup manually?

@lolo120916 For vswhere detection see my finding above, it's possible but kinda complicated. At least it works with Flutter last time I tried.

I used procmon to detect how the registry/vswhere works, but probably still missed some things.

@lolo120916
Copy link

lolo120916 commented Apr 26, 2024

@mmozeiko and @AndhikaWB
Thank you both. What I did was restart from scratch with VS Code. I did the simplest thing possible, as suggested by mmozeiko (by the way, huge thanks for his script and for taking the time to respond to messages), I launched VS Code with the environment variables set by setup.bat. And then, in VS Code, I simply added "cmake.generator": "Ninja" to the project's settings.json (and nothing more, especially). I then generated the CMakeLists.txt using the `"CMake: Quick Start" command. With the "CMake Tools" extension, I was then able to build and debug the project step by step.
Phew. Ninja handles everything on its own: it's wonderful.
Above all, don't try to do anything more complicated as suggested in the docs of the C/C++ and CMake Tools extensions: without MSVC console and without vswhere.exe able to detect things automagically, it just creates chaos ;-).

@goyalyashpal
Copy link

goyalyashpal commented Apr 27, 2024

hi! i am sorry, but why is this script needed? like it's 345 lines - what problem it addresses?
it seems it is for setting up the MSVC minimally and portably. as the upstream doesn't provide it standalone without bloat.

is that the case?

also, what steps do i need to take after running this script to be able to build cpp-dependent libraries?

To use cl.exe/link.exe from output folder, first run setup.bat - after that PATH/INCLUDE/LIB env variables will be setup to use all the tools as usual. You can also use clang-cl.exe with these includes & libraries.

i am sorry for blabbering, i am just thinking out loud as this msvc stuff has been bothering me for well over an year now.


also, there is lots of wisdom here in comments, ...
it would have been good if it was a repo itself, so that all those things could have a better place like discussions or wiki etc.

it's kinda hard following it above where different things meshing with each other

@goyalyashpal
Copy link

goyalyashpal commented Apr 27, 2024

update2: after merging 3 way with @ CrendKing's fork (comment), i now shows error 1619

Unpacking msi files...
microsoft.visualcpp.runtimedebug.14/vc_RuntimeDebug.msi ... 100%
microsoft.visualcpp.runtimedebug.14/cab1.cab ... 100%
Traceback (most recent call last):
  File "...\portable-msvc\portable-msvc_merged.py", line 272, in <module>
    subprocess.check_call(["msiexec.exe", "/a", f"\"{msi}\"", "/quiet", "/qn", f"TARGETDIR=\"{d2}\""])
  File "...\Python\Python312\Lib\subprocess.py", line 413, in check_call
    raise CalledProcessError(retcode, cmd)
subprocess.CalledProcessError: Command '['msiexec.exe', '/a', '"...portable-msvc\\tmpz69rwo5s\\vc_RuntimeDebug.msi"', '/quiet', '/qn', 'TARGETDIR="...portable-msvc\\tmpx6j7fbvb"']' returned non-zero exit status 1619.

update1: searching duckduckgo.com, i found this table listing error codes & their description at msi/error-codes.
and the one i am seeing, non-zero exit status 1639 is an "invalid command line argument"

Error code Value Description
ERROR_INSTALL_PACKAGE_OPEN_FAILED 1619 This installation package couldn't be opened. Verify that the package exists and is accessible, or contact the application vendor to verify that this is a valid Windows Installer package.
ERROR_INVALID_COMMAND_LINE 1639 Invalid command line argument. Consult the Windows Installer SDK for detailed command-line help.
ERROR_INSTALL_FAILURE 1603 A fatal error occurred during installation.
list of msiexec options, i.e. output of msiexec /?

Windows ® Installer. V 5.0.19041.3636 

msiexec /Option <Required Parameter> [Optional Parameter]

Install Options
	</package | /i> <Product.msi>
		Installs or configures a product
	/a <Product.msi>
		Administrative install - Installs a product on the network
	/j<u|m> <Product.msi> [/t <Transform List>] [/g <Language ID>]
		Advertises a product - m to all users, u to current user
	</uninstall | /x> <Product.msi | ProductCode>
		Uninstalls the product
Display Options
	/quiet
		Quiet mode, no user interaction
	/passive
		Unattended mode - progress bar only
	/q[n|b|r|f]
		Sets user interface level
		n - No UI
		b - Basic UI
		r - Reduced UI
		f - Full UI (default)
	/help
		Help information
Restart Options
	/norestart
		Do not restart after the installation is complete
	/promptrestart
		Prompts the user for restart if necessary
	/forcerestart
		Always restart the computer after installation
Logging Options
	/l[i|w|e|a|r|u|c|m|o|p|v|x|+|!|*] <LogFile>
		i - Status messages
		w - Nonfatal warnings
		e - All error messages
		a - Start up of actions
		r - Action-specific records
		u - User requests
		c - Initial UI parameters
		m - Out-of-memory or fatal exit information
		o - Out-of-disk-space messages
		p - Terminal properties
		v - Verbose output
		x - Extra debugging information
		+ - Append to existing log file
		! - Flush each line to the log
		* - Log all information, except for v and x options
	/log <LogFile>
		Equivalent of /l* <LogFile>
Update Options
	/update <Update1.msp>[;Update2.msp]
		Applies update(s)
	/uninstall <PatchCodeGuid>[;Update2.msp] /package <Product.msi | ProductCode>
		Remove update(s) for a product
Repair Options
	/f[p|e|c|m|s|o|d|a|u|v] <Product.msi | ProductCode>
		Repairs a product
		p - only if file is missing
		o - if file is missing or an older version is installed (default)
		e - if file is missing or an equal or older version is installed
		d - if file is missing or a different version is installed
		c - if file is missing or checksum does not match the calculated value
		a - forces all files to be reinstalled
		u - all required user-specific registry entries (default)
		m - all required computer-specific registry entries (default)
		s - all existing shortcuts (default)
		v - runs from source and recaches local package
Setting Public Properties
	[PROPERTY=PropertyValue]

Consult the Windows ® Installer SDK for additional documentation on the
command line syntax.

Copyright © Microsoft Corporation. All rights reserved.
Portions of this software are based in part on the work of the Independent JPEG Group.


i got this following error after getting a popup window titled Windows Installer & showing output similar to msiexec /?, it seems to be similar to these:


Traceback (most recent call last):
  File "...\portable-msvc\portable-msvc.py", line 273, in <module>
    subprocess.check_call(["msiexec.exe", "/a", str(msi), "/quiet", "/qn", f"TARGETDIR={d2}"])
  File "...\Python\Python312\Lib\subprocess.py", line 413, in check_call
    raise CalledProcessError(retcode, cmd)
subprocess.CalledProcessError: Command '[
        'msiexec.exe', 
        '/a', 
        '$LOCALAPPDATA\\Temp\\tmp0y593jh9\\vc_RuntimeDebug.msi', 
        '/quiet', 
        '/qn', 
        'TARGETDIR=$LOCALAPPDATA\\Temp\\tmprg5jtiha'
]' returned non-zero exit status 1639.

@goyalyashpal
Copy link

goyalyashpal commented Apr 27, 2024

i wish there was a way to do retry from the "Unpacking msi files..." part, it feels stupid to download all those .base, .msi, .cab etc files over and over and over and over again.

@mmozeiko
Copy link
Author

Updated script to leave downloaded files in downloads folder. If msiexec fails then you can try running its command manually without /quiet /qn flags to see if it produces more detailed error message.

@goyalyashpal
Copy link

thanks a lot. super awesome refactoring.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment