Skip to content

Instantly share code, notes, and snippets.

@mmozeiko
Last active December 12, 2024 16:42
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 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 native desktop app development.

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

You can list available versions with py.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 first run setup_TARGET.bat - after that PATH/INCLUDE/LIB env variables will be updated 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
# NOTE: not all host & target architecture combinations are supported
DEFAULT_HOST = "x64"
ALL_HOSTS = "x64 x86 arm64".split()
DEFAULT_TARGET = "x64"
ALL_TARGETS = "x64 x86 arm arm64".split()
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, filename):
fpath = DOWNLOADS / filename
if fpath.exists():
data = fpath.read_bytes()
if hashlib.sha256(data).hexdigest() == check.lower():
print(f"\r{filename} ... 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{filename} ... {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 = lambda x: True):
return next((item for item in items if cond(item)), None)
### 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")
ap.add_argument("--target", default=DEFAULT_TARGET, help=f"Target architectures, comma separated ({','.join(ALL_TARGETS)})")
ap.add_argument("--host", default=DEFAULT_HOST, help=f"Host architecture", choices=ALL_HOSTS)
args = ap.parse_args()
host = args.host
targets = args.target.split(',')
for target in targets:
if target not in ALL_TARGETS:
exit(f"Unknown {target} target architecture!")
### 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 = [
f"microsoft.visualcpp.dia.sdk",
f"microsoft.vc.{msvc_ver}.crt.headers.base",
f"microsoft.vc.{msvc_ver}.crt.source.base",
f"microsoft.vc.{msvc_ver}.asan.headers.base",
f"microsoft.vc.{msvc_ver}.pgo.headers.base",
]
for target in targets:
msvc_packages += [
f"microsoft.vc.{msvc_ver}.tools.host{host}.target{target}.base",
f"microsoft.vc.{msvc_ver}.tools.host{host}.target{target}.res.base",
f"microsoft.vc.{msvc_ver}.crt.{target}.desktop.base",
f"microsoft.vc.{msvc_ver}.crt.{target}.store.base",
f"microsoft.vc.{msvc_ver}.premium.tools.host{host}.target{target}.base",
f"microsoft.vc.{msvc_ver}.pgo.{target}.base",
]
if target in ["x86", "x64"]:
msvc_packages += [f"microsoft.vc.{msvc_ver}.asan.{target}.base"]
redist_suffix = ".onecore.desktop" if target == "arm" else ""
redist_pkg = f"microsoft.vc.{msvc_ver}.crt.redist.{target}{redist_suffix}.base"
if redist_pkg not in packages:
redist_name = f"microsoft.visualcpp.crt.redist.{target}{redist_suffix}"
redist = first(packages[redist_name])
redist_pkg = first(redist["dependencies"], lambda dep: dep.endswith(".base")).lower()
msvc_packages += [redist_pkg]
for pkg in sorted(msvc_packages):
if pkg not in packages:
print(f"\r{pkg} ... !!! MISSING !!!")
continue
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"], 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 = [
f"Windows SDK for Windows Store Apps Tools-x86_en-us.msi",
f"Windows SDK for Windows Store Apps Headers-x86_en-us.msi",
f"Windows SDK for Windows Store Apps Headers OnecoreUap-x86_en-us.msi",
f"Windows SDK for Windows Store Apps Libs-x86_en-us.msi",
f"Universal CRT Headers Libraries and Sources-x86_en-us.msi",
]
for target in ALL_TARGETS:
sdk_packages += [
f"Windows SDK Desktop Headers {target}-x86_en-us.msi",
f"Windows SDK OnecoreUap Headers {target}-x86_en-us.msi",
]
for target in targets:
sdk_packages += [f"Windows SDK Desktop Libs {target}-x86_en-us.msi"]
with tempfile.TemporaryDirectory(dir=DOWNLOADS) as d:
dst = Path(d)
sdk_pkg = packages[sdk_pid][0]
sdk_pkg = packages[first(sdk_pkg["dependencies"]).lower()][0]
msi = []
cabs = []
# download msi files
for pkg in sorted(sdk_packages):
payload = first(sdk_pkg["payloads"], lambda p: p["fileName"] == f"Installers\\{pkg}")
if payload is None:
continue
msi.append(DOWNLOADS / pkg)
data = download_progress(payload["url"], payload["sha256"], 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)
print("Unpacking msi files...")
# run msi installers
for m in msi:
subprocess.check_call(["msiexec.exe", "/a", m, "/quiet", "/qn", f"TARGETDIR={OUTPUT.resolve()}"])
(OUTPUT / m.name).unlink()
### versions
msvcv = first((OUTPUT / "VC/Tools/MSVC").glob("*")).name
sdkv = first((OUTPUT / "Windows Kits/10/bin").glob("*")).name
# place debug CRT runtime files into MSVC bin folder (not what real Visual Studio installer does... but is reasonable)
# NOTE: these are Target architecture, not Host architecture binaries
redist = OUTPUT / "VC/Redist"
if redist.exists():
redistv = first((redist / "MSVC").glob("*")).name
src = redist / "MSVC" / redistv / "debug_nonredist"
for target in targets:
for f in (src / target).glob("**/*.dll"):
dst = OUTPUT / "VC/Tools/MSVC" / msvcv / f"bin/Host{host}" / target
f.replace(dst / f.name)
shutil.rmtree(redist)
# copy msdia140.dll file into MSVC bin folder
# NOTE: this is meant only for development - always Host architecture, even when placed into all Target architecture folders
msdia140dll = {
"x86": "msdia140.dll",
"x64": "amd64/msdia140.dll",
"arm": "arm/msdia140.dll",
"arm64": "arm64/msdia140.dll",
}
dst = OUTPUT / "VC/Tools/MSVC" / msvcv / f"bin/Host{host}"
src = OUTPUT / "DIA%20SDK/bin" / msdia140dll[host]
for target in targets:
shutil.copyfile(src, dst / target / src.name)
shutil.rmtree(OUTPUT / "DIA%20SDK")
### cleanup
shutil.rmtree(OUTPUT / "Common7", ignore_errors=True)
shutil.rmtree(OUTPUT / "VC/Tools/MSVC" / msvcv / "Auxiliary")
for target in targets:
for f in [f"store", "uwp", "enclave", "onecore"]:
shutil.rmtree(OUTPUT / "VC/Tools/MSVC" / msvcv / "lib" / target / f, ignore_errors=True)
shutil.rmtree(OUTPUT / "VC/Tools/MSVC" / msvcv / f"bin/Host{host}" / target / "onecore", ignore_errors=True)
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 not in targets:
shutil.rmtree(OUTPUT / "Windows Kits/10/Lib" / sdkv / "ucrt" / arch, ignore_errors=True)
shutil.rmtree(OUTPUT / "Windows Kits/10/Lib" / sdkv / "um" / arch, ignore_errors=True)
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, ignore_errors=True)
# executable that is collecting & sending telemetry every time cl/link runs
for target in targets:
(OUTPUT / "VC/Tools/MSVC" / msvcv / f"bin/Host{host}/{target}/vctip.exe").unlink(missing_ok=True)
### setup.bat
for target in targets:
SETUP = fr"""@echo off
set VSCMD_ARG_HOST_ARCH={host}
set VSCMD_ARG_TGT_ARCH={target}
set VCToolsVersion={msvcv}
set WindowsSDKVersion={sdkv}\
set VCToolsInstallDir=%~dp0VC\Tools\MSVC\{msvcv}\
set WindowsSdkBinPath=%~dp0Windows Kits\10\bin\
set PATH=%~dp0VC\Tools\MSVC\{msvcv}\bin\Host{host}\{target};%~dp0Windows Kits\10\bin\{sdkv}\{host};%~dp0Windows Kits\10\bin\{sdkv}\{host}\ucrt;%PATH%
set INCLUDE=%~dp0VC\Tools\MSVC\{msvcv}\include;%~dp0Windows Kits\10\Include\{sdkv}\ucrt;%~dp0Windows Kits\10\Include\{sdkv}\shared;%~dp0Windows Kits\10\Include\{sdkv}\um;%~dp0Windows Kits\10\Include\{sdkv}\winrt;%~dp0Windows Kits\10\Include\{sdkv}\cppwinrt
set LIB=%~dp0VC\Tools\MSVC\{msvcv}\lib\{target};%~dp0Windows Kits\10\Lib\{sdkv}\ucrt\{target};%~dp0Windows Kits\10\Lib\{sdkv}\um\{target}
"""
(OUTPUT / f"setup_{target}.bat").write_text(SETUP)
print(f"Total downloaded: {total_download>>20} MB")
print("Done!")
@hellishvictor
Copy link

that was already available as CLI stdin option if i recall correct

Thank you, I'll check it out !

@hellishvictor
Copy link

Pushed an update that should fix it. It also allows host/target selection in CLI arguments.

The previous script worked like a charm, but this fix shows:

Traceback (most recent call last):
  File "C:\Python\Lib\urllib\request.py", line 1344, in do_open
    h.request(req.get_method(), req.selector, req.data, headers,
  File "C:\Python\Lib\http\client.py", line 1336, in request
    self._send_request(method, url, body, headers, encode_chunked)
  File "C:\Python\Lib\http\client.py", line 1382, in _send_request
    self.endheaders(body, encode_chunked=encode_chunked)
  File "C:\Python\Lib\http\client.py", line 1331, in endheaders
    self._send_output(message_body, encode_chunked=encode_chunked)
  File "C:\Python\Lib\http\client.py", line 1091, in _send_output
    self.send(msg)
  File "C:\Python\Lib\http\client.py", line 1035, in send
    self.connect()
  File "C:\Python\Lib\http\client.py", line 1470, in connect
    super().connect()
  File "C:\Python\Lib\http\client.py", line 1001, in connect
    self.sock = self._create_connection(
                ^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Python\Lib\socket.py", line 853, in create_connection
    raise exceptions[0]
  File "C:\Python\Lib\socket.py", line 838, in create_connection
    sock.connect(sa)
TimeoutError: [WinError 10060] Se produjo un error durante el intento de conexión ya que la parte conectada no respondió adecuadamente tras un periodo de tiempo, o bien se produjo un error en la conexión establecida ya que el host conectado no ha podido responder

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\MSVC\portable-msvc.py", line 97, in <module>
    manifest = json.loads(download(URL))
                          ^^^^^^^^^^^^^
  File "C:\MSVC\portable-msvc.py", line 31, in download
    with urllib.request.urlopen(url, context=ssl_context) as res:
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Python\Lib\urllib\request.py", line 215, in urlopen
    return opener.open(url, data, timeout)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Python\Lib\urllib\request.py", line 515, in open
    response = self._open(req, data)
               ^^^^^^^^^^^^^^^^^^^^^
  File "C:\Python\Lib\urllib\request.py", line 532, in _open
    result = self._call_chain(self.handle_open, protocol, protocol +
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Python\Lib\urllib\request.py", line 492, in _call_chain
    result = func(*args)
             ^^^^^^^^^^^
  File "C:\Python\Lib\urllib\request.py", line 1392, in https_open
    return self.do_open(http.client.HTTPSConnection, req,
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Python\Lib\urllib\request.py", line 1347, in do_open
    raise URLError(err)
urllib.error.URLError: <urlopen error [WinError 10060] Se produjo un error durante el intento de conexión ya que la parte conectada no respondió adecuadamente tras un periodo de tiempo, o bien se produjo un error en la conexión establecida ya que el host conectado no ha podido responder>

I'm on Win 7 x64 SP1. And btw, thank you for the implementation of host/target selection in CLI arguments.

@mmozeiko
Copy link
Author

mmozeiko commented Jul 2, 2024

Windows error 10060 is connection timeout error. Either something is too slow on your network, ISP throttling you or timing out connections or DNS requests to some domains. Try opening URL directly in IE, if that does not work, then you need to solve that problem first. Otherwise if that does work then try increasing timeout in urlopen() call.

@DuckDictatorship
Copy link

DuckDictatorship commented Jul 18, 2024

Hello, I'm trying to install python packages using python pip similar to this comment from a long time ago. I've made it past the error referenced in that comment, however I've reached a new issue that seems to be related to a failed installation/link of msvc
C:\Users\USER\AppData\Local\Temp\pip-install-wslxrcj0\editdistpy_6a7a7599a98645b08f225c24144b17ad\editdistpy\def.hpp(10): fatal error C1083: Cannot open include file: 'cstdint': No such file or directory
Any ideas?

@DArtagnant
Copy link

Great script, thank you very much

@hellishvictor
Copy link

Hi, there's a way to download the "C++ ATL for v143 build tools (x86 & x64)" included in the VS installer with this script?

@greenozon
Copy link

greenozon commented Aug 21, 2024

Currently it does not propose...
just this:

usage: portable-msvc.py [-h] [--show-versions] [--accept-license] [--msvc-version MSVC_VERSION] [--sdk-version SDK_VERSION] [--preview] [--target TARGET]
                        [--host {x64,x86,arm64}]


python3 ./portable-msvc.py --show-versions
MSVC versions: 14.29 14.30 14.31 14.32 14.33 14.34 14.35 14.36 14.37 14.38 14.39 14.40 14.41
Windows SDK versions: 18362 19041 20348 22000 22621 26100

@hellishvictor
Copy link

Thanks for your reply, man. Definelly add the option to include some components like that one would be a great implementation.

@harshn05
Copy link

I have been redirected from here: Data-Oriented-House/PortableBuildTools#23 (Please go through it)

Using the latest MSVC version = 14.41, SDK Version = 26100

libomp140.x86_64.dll is missing in the installation.

@mmozeiko
Copy link
Author

Pushed update to fix missing debug CRT files for 14.41 version.

As for ATL or MFC - this is not a tool for that. Technically it would be possible to add those, but as I'm not using them I do not wish to maintain such code. Please use Build Tools for Visual Studio to handle more complex VS installations.

@harshn05
Copy link

harshn05 commented Aug 22, 2024

Thanks for your help, Martins. I confirm that the updated script has successfully made libomp140(d).x86_64.dll available in the installation.

I also don't use ATL or MFC, only pure standard C++, which can be compiled by gcc/mingw64 and llvm-clang.

@madebr
Copy link

madebr commented Aug 24, 2024

Thanks @mmozeiko ,

With this patch, I got it downloading and (attempting to) install (but failing) on Linux.

--- a/portable-msvc.py
+++ b/portable-msvc.py
@@ -2,6 +2,7 @@
 
 import io
 import os
+import platform
 import sys
 import stat
 import json
@@ -278,7 +279,10 @@ with tempfile.TemporaryDirectory(dir=DOWNLOADS) as d:
 
   # run msi installers
   for m in msi:
-    subprocess.check_call(["msiexec.exe", "/a", m, "/quiet", "/qn", f"TARGETDIR={OUTPUT.resolve()}"])
+    msiexec_cmd = ["msiexec.exe", "/a", m, "/quiet", "/qn", f"TARGETDIR={OUTPUT.resolve()}"]
+    if platform.system() != "Windows":
+      msiexec_cmd.insert(0, os.environ.get("WINE", "wine"))
+    subprocess.check_call(msiexec_cmd)
     (OUTPUT / m.name).unlink()
 
 

The msiexec command fails because wine does not support administrative installs.

Unpacking msi files...
002c:fixme:winediag:loader_init wine-staging 9.1 is a testing version containing experimental patches.
002c:fixme:winediag:loader_init Please mention your exact version when filing bug reports on winehq.org.
002c:err:wineboot:process_run_key Error running cmd L"C:\\windows\\system32\\winemenubuilder.exe -a -r" (126).
0120:fixme:msiexec:WinMain Administrative installs are not currently supported
0120:fixme:file:NtLockFile I/O completion on lock not implemented yet
0120:fixme:ntdll:NtQuerySystemInformation info_class SYSTEM_PERFORMANCE_INFORMATION

There's an upstream ticket for this: https://bugs.winehq.org/show_bug.cgi?id=26813

@mmozeiko
Copy link
Author

I would suggest to not do wine for this. Instead use one of 3rd party msi extraction utilities that runs natively in linux. Like cabextract, or msiextract from msitools, or some other. Not sure which one works the best, have not tried them.

@madebr
Copy link

madebr commented Aug 24, 2024

@mmozeiko
I tried msiextract and got it working with the following patch.
It creates a msvc directory that is 100% identical to one generated using msiexec on Windows.

patch
--- a/portable-msvc.py
+++ b/portable-msvc.py
@@ -278,12 +278,13 @@ with tempfile.TemporaryDirectory(dir=DOWNLOADS) as d:
 
   # run msi installers
   for m in msi:
-    subprocess.check_call(["msiexec.exe", "/a", m, "/quiet", "/qn", f"TARGETDIR={OUTPUT.resolve()}"])
-    (OUTPUT / m.name).unlink()
+    subprocess.check_call(["msiextract", m, "-C", OUTPUT.resolve()])
 
 
 ### versions
 
+os.rename(OUTPUT / "Program Files/Windows Kits", OUTPUT / "Windows Kits")
+
 msvcv = first((OUTPUT / "VC/Tools/MSVC").glob("*")).name
 sdkv = first((OUTPUT / "Windows Kits/10/bin").glob("*")).name
 

(OUTPUT / m.name).unlink() was always throwing a FileNotFoundError, so I removed it. Not sure how to handle that.

With those changes, I can build a simple "Hello World" application :)

edit: As soon as I use the Windows SDK, it fails to link
edit2: It fails with the same link errors on a Windows system, so it's nothing Linux/wine-specific
edit3: It works. I forgot to explicitly add the win32 import libraries on the command line when testing.

@realyukii
Copy link

How to install the python program in the first place? newest python version are not compatible with Windows 7 :(

@greenozon
Copy link

you can't use official python installer, unfortunately....
but, there is a golden recipe: https://github.com/adang1345/PythonWin7

I'm using those builds already for years - works like a charm! highly recommended for old-schoolers :)

@realyukii
Copy link

realyukii commented Sep 1, 2024

Thanks for the link! I'll try it :)

Copy link

ghost commented Oct 20, 2024

I get this error after the last update:

A:\BuildTools\Windows Kits\10\Include\10.0.26100.0\um\shobjidl_core.h(1911): fatal error C1083: Cannot open include file: 'ObjectArray.h': No such file or directory

@greenozon
Copy link

Is that header file phyiscally preset on your disk?
in my case (older SDK) it's over here: "c:\Program Files (x86)\Windows Kits\10\Include\10.0.20348.0\um\ObjectArray.h"

Copy link

ghost commented Oct 20, 2024

No, it's missing, i'm on x64 target btw

@greenozon
Copy link

I'm also, but using Win7

@goyalyashpal
Copy link

goyalyashpal commented Oct 20, 2024

hi @mmozeiko
i have been thinking about this for quite some time now. would u mind creating a dedicated repo for this?

there are some merge requests i want to create, and i also think that this gist clearly has surpassed the use case and audience reach of a gist, and is now much more akin to a proper repo.

thanks a lot for all the work u have put into this.

@mmozeiko
Copy link
Author

What kind of merge requests are we talking about? I consider this script pretty much finished, and I'm ok only with keeping up with bugfixes and changes for newer versions of VS. I do not really want to turn this script into full fledged VS installation manager. VS build tools exe is meant for that.

Copy link

ghost commented Oct 21, 2024

Will this script ever be compatible with vcpkg?

@goyalyashpal
Copy link

goyalyashpal commented Oct 21, 2024 via email

@realyukii
Copy link

realyukii commented Oct 22, 2024

is this script working in headless mode? I tried to install it through msys2 ssh, but somehow it stuck on unpacking msi

# python.exe portable-msvc.py
Downloading MSVC v14.41.17.11 and Windows SDK v26100
Do you accept Visual Studio license at https://go.microsoft.com/fwlink/?LinkId=2179911 [Y/N] ? Y
Microsoft.VC.14.40.17.10.CRT.Redist.X64.base.vsix ... 100%
Microsoft.VC.14.41.17.11.ASAN.Headers.base.vsix ... 100%
Microsoft.VC.14.41.17.11.ASAN.X64.base.vsix ... 100%
Microsoft.VC.14.41.17.11.CRT.Headers.base.vsix ... 100%
Microsoft.VC.14.41.17.11.CRT.Source.base.vsix ... 100%
Microsoft.VC.14.41.17.11.CRT.x64.Desktop.base.vsix ... 100%
Microsoft.VC.14.41.17.11.CRT.x64.Store.base.vsix ... 100%
Microsoft.VC.14.41.17.11.PGO.Headers.base.vsix ... 100%
Microsoft.VC.14.41.17.11.PGO.X64.base.vsix ... 100%
Microsoft.VC.14.41.17.11.Premium.Tools.HostX64.TargetX64.base.vsix ... 100%
Microsoft.VC.14.41.17.11.Tools.HostX64.TargetX64.base.vsix ... 100%
Microsoft.VC.14.41.17.11.Tools.HostX64.TargetX64.Res.base.enu.vsix ... 100%
Microsoft.VisualCpp.DIA.SDK.vsix ... 100%
Universal CRT Headers Libraries and Sources-x86_en-us.msi ... 100%
Windows SDK Desktop Headers arm64-x86_en-us.msi ... 100%
Windows SDK Desktop Headers x64-x86_en-us.msi ... 100%
Windows SDK Desktop Headers x86-x86_en-us.msi ... 100%
Windows SDK Desktop Libs x64-x86_en-us.msi ... 100%
Windows SDK OnecoreUap Headers arm64-x86_en-us.msi ... 100%
Windows SDK OnecoreUap Headers x64-x86_en-us.msi ... 100%
Windows SDK OnecoreUap Headers x86-x86_en-us.msi ... 100%
Windows SDK for Windows Store Apps Headers OnecoreUap-x86_en-us.msi ... 100%
Windows SDK for Windows Store Apps Headers-x86_en-us.msi ... 100%
Windows SDK for Windows Store Apps Libs-x86_en-us.msi ... 100%
Windows SDK for Windows Store Apps Tools-x86_en-us.msi ... 100%
16ab2ea2187acffa6435e334796c8c89.cab ... 100%
6ee7bbee8435130a869cf971694fd9e2.cab ... 100%
78fa3c824c2c48bd4a49ab5969adaaf7.cab ... 100%
7afc7b670accd8e3cc94cfffd516f5cb.cab ... 100%
96076045170fe5db6d5dcf14b6f6688e.cab ... 100%
a1e2a83aa8a71c48c742eeaff6e71928.cab ... 100%
b2f03f34ff83ec013b9e45c7cd8e8a73.cab ... 100%
beb5360d2daaa3167dea7ad16c28f996.cab ... 100%
d95da93904819b1f7e68adb98b49a9c7.cab ... 100%
eca0aa33de85194cd50ed6e0aae0156f.cab ... 100%
f9ff50431335056fb4fbac05b8268204.cab ... 100%
d1de88680a8e53fe75e01e94dc0ed767.cab ... 100%
07a57cdb41ba28cced14005f087267be.cab ... 100%
2e876dd22fa5e6785f137e3422dd50ec.cab ... 100%
4fe4c8b88812f5339018c0eef95acdb9.cab ... 100%
58314d0646d7e1a25e97c902166c3155.cab ... 100%
43fd989a0684600a8c4dda7a6373eb59.cab ... 100%
b0656253ef2f688d2982eafa2e73f621.cab ... 100%
f2e05dfd38ed343d3de77209cf3ecdae.cab ... 100%
e89e3dcbb016928c7e426238337d69eb.cab ... 100%
766c0ffd568bbb31bf7fb6793383e24a.cab ... 100%
8125ee239710f33ea485965f76fae646.cab ... 100%
c0aa6d435b0851bf34365aadabd0c20f.cab ... 100%
05047a45609f311645eebcac2739fc4c.cab ... 100%
13d68b8a7b6678a368e2d13ff4027521.cab ... 100%
463ad1b0783ebda908fd6c16a4abfe93.cab ... 100%
5a22e5cde814b041749fb271547f4dd5.cab ... 100%
e10768bb6e9d0ea730280336b697da66.cab ... 100%
f9b24c8280986c0683fbceca5326d806.cab ... 100%
2630bae9681db6a9f6722366f47d055c.cab ... 100%
26ea25236f12b23db661acf268a70cfa.cab ... 100%
2a30b5d1115d515c6ddd8cd6b5173835.cab ... 100%
4a4c678668584fc994ead5b99ccf7f03.cab ... 100%
61d57a7a82309cd161a854a6f4619e52.cab ... 100%
68de71e3e2fb9941ee5b7c77500c0508.cab ... 100%
69661e20556b3ca9456b946c2c881ddd.cab ... 100%
b82881a61b7477bd4eb5de2cd5037fe2.cab ... 100%
dcfb1aa345e349091a44e86ce1766566.cab ... 100%
e3d1b35aecfccda1b4af6fe5988ac4be.cab ... 100%
Unpacking msi files...

@mmozeiko
Copy link
Author

I do not know about msys2 ssh, but I have used it over regular Windows native ssh session - and there it works fine.

@realyukii
Copy link

in the ssh, it not show error, maybe it stuck because the program trying to spawn some window popup?

when I access it directly and not from ssh connection, it show help popup and when I close it, the program show some error:

Unpacking msi files...
Traceback (most recent call last):
  File "/home/ACER/portable-msvc.py", line 285, in <module>
    subprocess.check_call(["msiexec.exe", "/a", m, "/quiet", "/qn", f"TARGETDIR={OUTPUT.resolve()}"])
  File "/usr/lib/python3.12/subprocess.py", line 413, in check_call
    raise CalledProcessError(retcode, cmd)
subprocess.CalledProcessError: Command '['msiexec.exe', '/a', PosixPath('downloads/Universal CRT Headers Libraries and Sources-x86_en-us.msi'), '/quiet', '/qn', 'TARGETDIR=/home/ACER/msvc']' returned non-zero exit status 103.

@realyukii
Copy link

Hmmm it's weird the program show help popup knowing the /qn and /quiet option already passed to the msiexec

@realyukii
Copy link

I think it's an issue of msys, but tried on powershell:
image

and it seems not an issue of msys

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