1
0
mirror of https://gitlab.com/kelteseth/ScreenPlay.git synced 2024-11-22 02:32:29 +01:00

Fix macOS universal signing

This commit is contained in:
Elias Steurer 2022-08-26 15:45:49 +02:00
parent ed59c18f80
commit 73078edee6
10 changed files with 213 additions and 144 deletions

View File

@ -105,6 +105,7 @@ export PATH="~/.local/bin:$PATH"
```
## MacOSX
1. Install XCode 13+ , open and restart your device.
1. Install [brew](https://brew.sh) that is needed by some third party vcpkg packages. Do not forget to add brew to your path as outlined at the on of the installation!
- `brew install pkg-config git llvm cmake ninja`
1. [Download and install Qt binary installer from qt.io](https://www.qt.io/download-qt-installer)

View File

@ -5,21 +5,64 @@
2. Create a app password for distribution outside of the app store
- Go to https://appleid.apple.com/account/manage
- Call it ScreenPlay and save the password!
3. Next we need a developer certificate:
- Go to https://developer.apple.com/account/resources/certificates/list
- Click the blue plus sign
- Select `Developer ID Application`
- _This certificate is used to code sign your app for distribution outside of the Mac App Store._
## Adding an existing certificate
1. Go to https://developer.apple.com/account/resources/certificates/download
1. Download `Developer ID Application`
2. Add certificate `System`
## Add a new device
Based on: https://developer.apple.com/forums/thread/699268
1. You run Keychain Access and choose Certificate Assistant > Request a Certificate from a Certificate Authority.
1. You run through the workflow as described in Developer [Account Help > Create certificates > Create a certificate signing](https://help.apple.com/developer-account/#/devbfa00fef7) request. This does two things:
- It generates a public / private key pair in your keychain. To see these, run Keychain Access and select “login” on the left and Keys at the top. Look for keys whose names match the Common Name you entered in step 2.
- It prompts you to save a .certSigningRequest file (CSR). This contains a copy of the public key.
1. You upload the CSR file to the [developer web site](https://developer.apple.com/account/resources/certificates/list). Select `Developer ID Application` and upload your new `CertificateSigningRequest.certSigningRequest`.
1. The developer web site issues you a certificate. In human terms this certificate says “Apple certifies that the subject of this certificate holds the private key that matches the public key embedded in this certificate.”
> Note The developer web site sets the subject information in the certificate based on your developer account. It ignores the subject information in the CSR. So, you can enter any information you want in step 2. This is a good way to distinguish between different keys in your keychain. For example, you might set the Common Name field in step 2 to include a unique identifier that allows you to easily identify the public / private key pair generated in step 3.
5. Download the certificate and add it to your keychain.
## Testing
There should be at least one valid identity:
```
security find-identity -p codesigning -v
```
should print:
```
1) xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx "Developer ID Application: Elias Steurer (V887LHYKRH)"
1 valid identities found
```
## Sign the app locally with codesign
We need to sign every single file in the .app file. For this we use the name from the installed cert. This can be copied from the `Keychain Access.app`.
`codesign --deep -f -s "Developer ID Application: Elias Steurer (V887LHYKRH)" --options "runtime" "ScreenPlay.app/"`
```
codesign --deep -f -s "Developer ID Application: Elias Steurer (V887LHYKRH)" --options "runtime" "ScreenPlay.app/"
```
## Add App Store Connect API private key:
- [Create Private Key](https://appstoreconnect.apple.com/access/users)
- Save private key as a file, KEY ID, Issuer ID. We need them next:
```
xcrun notarytool store-credentials
```
1. Profile name:
- Profile name: tachiom
2. Path to App Store Connect API private key:
- `/Users/eliassteurer/Documents/AuthKey_xxxxxxx.p8`
3. App Store Connect API Key ID:
- KEY ID at: https://appstoreconnect.apple.com/access/api
4. App Store Connect API Issuer ID:
- USER ID at: https://appstoreconnect.apple.com/access/api
## Get an App-Specific Password
https://stackoverflow.com/questions/56890749/macos-notarize-in-script
```
security add-generic-password -a "kelteseth@gmail.com" -w "xxxx-xxx-xxx-xxx" -s "Developer ID Application: Elias Steurer (V887LHYKRH)"
```
## Upload to apple for notization
We use [xcnotary](https://github.com/akeru-inc/xcnotary) tools for fast automatic upload. Install it via brew:
@ -32,3 +75,4 @@ Then run it with the
- `-k` command is here the keychain name that contains your password from the app password step above!
`xcnotary notarize ScreenPlay.app -d yourDeveloperAccountEmail@example.com -k ScreenPlay`

View File

@ -5,7 +5,7 @@
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<string>ScreenPlay</string>
<key>CFBundleIconFile</key>
<string></string>
<key>CFBundleIdentifier</key>
@ -17,9 +17,9 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleVersion</key>
<string>0.14</string>
<string>0.15</string>
<key>CFBundleShortVersionString</key>
<string>0.14</string>
<string>0.15</string>
<key>CFBundleDisplayName</key>
<string>ScreenPlay</string>
</dict>

View File

@ -5,7 +5,7 @@
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<string>ScreenPlayWallpaper</string>
<key>CFBundleIconFile</key>
<string></string>
<key>CFBundleIdentifier</key>
@ -13,15 +13,15 @@
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>${PRODUCT_NAME}</string>
<string>ScreenPlayWallpaper</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleDisplayName</key>
<string>ScreenPlayWallpaper</string>
<key>CFBundleVersion</key>
<string>0.14</string>
<string>0.15</string>
<key>CFBundleShortVersionString</key>
<string>0.14</string>
<string>0.15</string>
<key>NSUIElement</key>
<string>1</string>
<key>LSUIElement</key>

View File

@ -5,7 +5,7 @@
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<string>ScreenPlayWidget</string>
<key>CFBundleIconFile</key>
<string></string>
<key>CFBundleIdentifier</key>
@ -13,15 +13,15 @@
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>${PRODUCT_NAME}</string>
<string>ScreenPlayWidget</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleDisplayName</key>
<string>ScreenPlayWidget</string>
<key>CFBundleVersion</key>
<string>0.14</string>
<string>0.15</string>
<key>CFBundleShortVersionString</key>
<string>0.14</string>
<string>0.15</string>
<key>NSUIElement</key>
<string>1</string>
<key>LSUIElement</key>

View File

@ -1,7 +1,6 @@
#!/usr/bin/python3
import platform
import os
import subprocess
import platform
import shutil
import argparse
@ -10,41 +9,7 @@ import zipfile
from typing import Tuple
from shutil import copytree
from pathlib import Path
from datetime import datetime
from util import sha256, cd_repo_root_path, zipdir, run
# Based on https://gist.github.com/l2m2/0d3146c53c767841c6ba8c4edbeb4c2c
def get_vs_env_dict():
vcvars: str # We support 2019 or 2022
# Hardcoded VS path
# check if vcvars64.bat is available.
msvc_2019_path = "C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Community\\VC\\Auxiliary\\Build\\vcvars64.bat"
msvc_2022_path = "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Auxiliary\\Build\\vcvars64.bat"
if Path(msvc_2019_path).exists():
vcvars = msvc_2019_path
# Prefer newer MSVC and override if exists
if Path(msvc_2022_path).exists():
vcvars = msvc_2022_path
if not vcvars:
raise RuntimeError(
"No Visual Studio installation found, only 2019 and 2022 are supported.")
print(f"\n\nLoading MSVC env variables via {vcvars}\n\n")
cmd = [vcvars, '&&', 'set']
popen = subprocess.Popen(
cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout, stderr = popen.communicate()
if popen.wait() != 0:
raise ValueError(stderr.decode("mbcs"))
output = stdout.decode("mbcs").split("\r\n")
return dict((e[0].upper(), e[1]) for e in [p.rstrip().split("=", 1) for p in output] if len(e) == 2)
from util import sha256, cd_repo_root_path, zipdir, run, get_vs_env_dict
def clean_build_dir(build_dir):
if isinstance(build_dir, str):
@ -74,6 +39,7 @@ class BuildConfig:
root_path: str
cmake_osx_architectures: str
cmake_target_triplet: str
package: bool
package_command: str
executable_file_ending: str
qt_path: str
@ -89,7 +55,6 @@ class BuildConfig:
executable_file_ending: str
build_folder: str
bin_dir: str
sign_build: bool
screenplay_version: str
# CMake variables need str: "ON" or "OFF"
build_steam: str
@ -134,10 +99,6 @@ def execute(
if build_config.create_installer == "ON":
build_installer(build_config, build_result)
# Mac needs signed builds for user to run the app
if platform.system() == "Darwin" and build_config.sign_build:
sign(build_config)
# Create a zip file for scoop & chocolatey
if platform.system() == "Windows":
build_result = zip(build_config, build_result)
@ -201,7 +162,7 @@ def setup(build_config: BuildConfig, build_result: BuildResult) -> Tuple[BuildCo
build_config.qt_bin_path = build_config.aqt_path.joinpath(
f"{build_config.qt_version}/macos") if build_config.use_aqt else Path(f"~/Qt/{build_config.qt_version}/macos")
# NO f string we fill it later!
build_config.package_command = "{prefix_path}/bin/macdeployqt {app}.app -qmldir=../../{app}/qml -executable={app}.app/Contents/MacOS/{app}"
build_config.package_command = "{prefix_path}/bin/macdeployqt {app}.app -qmldir=../../{app}/qml -executable={app}.app/Contents/MacOS/{app} -appstore-compliant"
build_config.aqt_install_qt_packages = f"mac desktop {build_config.qt_version} clang_64 -m all"
build_config.aqt_install_tool_packages = "mac desktop tools_ifw"
@ -379,52 +340,6 @@ def build_installer(build_config: BuildConfig, build_result: BuildResult):
print("Running cpack at: ", os.getcwd())
run("cpack", cwd=build_config.build_folder)
def sign(build_config: BuildConfig):
run("codesign --deep -f -s \"Developer ID Application: Elias Steurer (V887LHYKRH)\" --timestamp --options \"runtime\" -f --entitlements \"../../ScreenPlay/entitlements.plist\" --deep \"ScreenPlay.app/\"", cwd=build_config.bin_dir)
run("codesign --deep -f -s \"Developer ID Application: Elias Steurer (V887LHYKRH)\" --timestamp --options \"runtime\" -f --deep \"ScreenPlayWallpaper.app/\"", cwd=build_config.bin_dir)
run("codesign --deep -f -s \"Developer ID Application: Elias Steurer (V887LHYKRH)\" --timestamp --options \"runtime\" -f --deep \"ScreenPlayWidget.app/\"", cwd=build_config.bin_dir)
run("codesign --verify --verbose=4 \"ScreenPlay.app/\"", cwd=build_config.bin_dir)
run("codesign --verify --verbose=4 \"ScreenPlayWallpaper.app/\"",
cwd=build_config.bin_dir)
run("codesign --verify --verbose=4 \"ScreenPlayWidget.app/\"",
cwd=build_config.bin_dir)
# TODO: Replace with https://github.com/akeru-inc/xcnotary/issues/22#issuecomment-1179170957
run("xcnotary notarize ScreenPlay.app -d kelteseth@gmail.com -k ScreenPlay",
cwd=build_config.bin_dir),
run("xcnotary notarize ScreenPlayWallpaper.app -d kelteseth@gmail.com -k ScreenPlay",
cwd=build_config.bin_dir),
run("xcnotary notarize ScreenPlayWidget.app -d kelteseth@gmail.com -k ScreenPlay",
cwd=build_config.bin_dir)
run("spctl --assess --verbose \"ScreenPlay.app/\"", cwd=build_config.bin_dir)
run("spctl --assess --verbose \"ScreenPlayWallpaper.app/\"",
cwd=build_config.bin_dir)
run("spctl --assess --verbose \"ScreenPlayWidget.app/\"",
cwd=build_config.bin_dir)
# We also need to sign the installer in osx:
if build_config.create_installer == "ON":
run("codesign --deep -f -s \"Developer ID Application: Elias Steurer (V887LHYKRH)\" --timestamp --options \"runtime\" -f --deep \"ScreenPlay-Installer.dmg/ScreenPlay-Installer.app/Contents/MacOS/ScreenPlay-Installer\"", cwd=build_config.build_folder)
run("codesign --verify --verbose=4 \"ScreenPlay-Installer.dmg/ScreenPlay-Installer.app/Contents/MacOS/ScreenPlay-Installer\"",
cwd=build_config.build_folder)
run("xcnotary notarize ScreenPlay-Installer.dmg/ScreenPlay-Installer.app -d kelteseth@gmail.com -k ScreenPlay",
cwd=build_config.build_folder)
run("spctl --assess --verbose \"ScreenPlay-Installer.dmg/ScreenPlay-Installer.app/\"",
cwd=build_config.build_folder)
run("codesign --deep -f -s \"Developer ID Application: Elias Steurer (V887LHYKRH)\" --timestamp --options \"runtime\" -f --deep \"ScreenPlay-Installer.dmg/\"", cwd=build_config.build_folder)
run("codesign --verify --verbose=4 \"ScreenPlay-Installer.dmg/\"",
cwd=build_config.build_folder)
run("xcnotary notarize ScreenPlay-Installer.dmg -d kelteseth@gmail.com -k ScreenPlay",
cwd=build_config.build_folder)
run("spctl --assess --verbose \"ScreenPlay-Installer.dmg/\"",
cwd=build_config.build_folder)
def zip(build_config: BuildConfig, build_result: BuildResult) -> BuildResult:
zipName = f"ScreenPlay-{build_config.screenplay_version}-{build_config.cmake_target_triplet}-{build_config.build_type}.zip"
build_result.build_zip = Path(build_result.build).joinpath(zipName)
@ -465,8 +380,6 @@ if __name__ == "__main__":
help="Build type. This is either debug or release.")
parser.add_argument('-use-aqt', action="store_true", dest="use_aqt",
help="Absolute qt path. If not set the default path is used\Windows: C:\Qt\nLinux & macOS:~/Qt/.")
parser.add_argument('-sign', action="store_true", dest="sign_build",
help="Enable if you want to sign the apps. This is macOS only for now.")
parser.add_argument('-steam', action="store_true", dest="build_steam",
help="Enable if you want to build the Steam workshop plugin.")
parser.add_argument('-tests', action="store_true", dest="build_tests",
@ -516,10 +429,6 @@ if __name__ == "__main__":
if args.create_installer:
create_installer = "ON"
sign_build = False
if args.sign_build:
sign_build = True
if args.use_aqt:
use_aqt = True
@ -531,7 +440,6 @@ if __name__ == "__main__":
build_config.build_deploy = build_deploy
build_config.create_installer = create_installer
build_config.build_type = build_type
build_config.sign_build = args.sign_build
build_config.use_aqt = use_aqt
build_config.screenplay_version = screenplay_version
build_config.build_architecture = args.build_architecture

View File

@ -1,4 +1,7 @@
import steam_publish
import shutil
import sys
import macos_sign
import argparse
import os
import build
@ -31,15 +34,25 @@ if __name__ == "__main__":
build_config.build_deploy = "ON"
build_config.create_installer = "ON"
build_config.build_type = "release"
build_config.sign_build = True
build_config.use_aqt = False
if platform.system() == "Darwin":
# We do not yet support a standalone osx installer
build_config.create_installer = "OFF"
# OSX builds needs to build for x86 and arm
# and also be signed!
# We need to manually package here at the end after
# we run
build_config.package = True
# Remove old build-universal-osx-release dir that does not automatically
# deleted because it is not build directly but generated from x64 and arm64
universal_build_dir = Path(os.path.join(root_path, "build-universal-osx-release"))
if universal_build_dir.exists():
print(f"Remove previous build folder: {universal_build_dir}")
# ignore_errors removes also not empty folders...
shutil.rmtree(universal_build_dir, ignore_errors=True)
build_config.build_architecture = "arm64"
build_result = build.execute(build_config)
@ -52,6 +65,10 @@ if __name__ == "__main__":
# Create universal (fat) binary
run_lipo()
check_fat_binary()
build_config.bin_dir = os.path.join(build_config.root_path,'build-universal-osx-release/bin/')
print(f"Change binary dir to: {build_config.bin_dir}")
macos_sign.sign(build_config=build_config)
else:
build_config.build_architecture = "x64"
build_result = build.execute(build_config)
@ -74,9 +91,9 @@ if __name__ == "__main__":
# Make sure to reset to tools path
#os.chdir(tools_path)
#steam_publish.publish(
# steam_username="tachiom",
# steam_password=args.steam_password,
# set_live_branch_name="internal"
#)
os.chdir(tools_path)
steam_publish.publish(
steam_username="tachiom",
steam_password=args.steam_password,
set_live_branch_name="internal"
)

View File

@ -1,16 +1,11 @@
#!/usr/bin/python3
import platform
from distutils.dir_util import mkpath
import os
import subprocess
import shutil
import argparse
import time
import zipfile
from shutil import copytree
import pathlib
from pathlib import Path
from concurrent.futures import ThreadPoolExecutor
from datetime import datetime
from util import run, run_and_capture_output
from util import run, run_and_capture_output, cd_repo_root_path
import warnings
def listfiles(path):
files = []
@ -24,18 +19,19 @@ def listfiles(path):
file = path + os.path.join(dir, fname)
if(os.path.isfile(file)):
files.append(file)
if os.path.islink(file):
print(f"Warning: file {file} is a symlink!")
print("Symlink target: ", os.readlink(file))
return files
def run_lipo():
# Make sure the script is always started from the same folder
root_path = Path.cwd()
if root_path.name == "Tools":
root_path = root_path.parent
print(f"Change root directory to: {root_path}")
os.chdir(root_path)
# Merges x64 and arm64 build into universal
def run_lipo() :
root_path = cd_repo_root_path()
shutil.copytree(str(Path.joinpath(root_path, "build-arm64-osx-release/bin/")) ,
str(Path.joinpath(root_path, "build-universal-osx-release/bin/")) )
# Looks like it is ok the contain symlinks otherwise we get these errors for qml plugins:
# bundle format is ambiguous (could be app or framework)
# https://bugreports.qt.io/browse/QTBUG-101338
run("cp -a build-arm64-osx-release build-universal-osx-release",root_path)
apps = ["ScreenPlay","ScreenPlayWallpaper", "ScreenPlayWidget"]
for app in apps:

72
Tools/macos_sign.py Normal file
View File

@ -0,0 +1,72 @@
#!/usr/bin/python3
import platform
import os
from build import BuildConfig
from util import run
def sign(build_config: BuildConfig):
print("Run codedesign")
run("codesign --deep -f -s \"Developer ID Application: Elias Steurer (V887LHYKRH)\" --timestamp --options \"runtime\" -f --entitlements \"../../ScreenPlay/entitlements.plist\" --deep \"ScreenPlay.app/\"",
cwd=build_config.bin_dir)
run("codesign --deep -f -s \"Developer ID Application: Elias Steurer (V887LHYKRH)\" --timestamp --options \"runtime\" -f --deep \"ScreenPlayWallpaper.app/\"",
cwd=build_config.bin_dir)
run("codesign --deep -f -s \"Developer ID Application: Elias Steurer (V887LHYKRH)\" --timestamp --options \"runtime\" -f --deep \"ScreenPlayWidget.app/\"",
cwd=build_config.bin_dir)
print("Run codedesign verify")
run("codesign --verify --verbose=4 \"ScreenPlay.app/\"",
cwd=build_config.bin_dir)
run("codesign --verify --verbose=4 \"ScreenPlayWallpaper.app/\"",
cwd=build_config.bin_dir)
run("codesign --verify --verbose=4 \"ScreenPlayWidget.app/\"",
cwd=build_config.bin_dir)
# TODO: Replace with https://github.com/akeru-inc/xcnotary/issues/22#issuecomment-1179170957
# ditto -c -k --keepParent "ScreenPlay.app" "ScreenPlay.app.zip"
# Note the profile is the one name of the first step of (App Store Connect API) in the macOSSigning.md
# xcrun notarytool submit "ScreenPlay.app.zip" --keychain-profile "ScreenPlay" --wait
# xcrun stapler staple "ScreenPlay.app"
print("Packing .apps for upload")
run("ditto -c -k --keepParent 'ScreenPlay.app' 'ScreenPlay.app.zip'", cwd=build_config.bin_dir)
run("ditto -c -k --keepParent 'ScreenPlayWallpaper.app' 'ScreenPlayWallpaper.app.zip'", cwd=build_config.bin_dir)
run("ditto -c -k --keepParent 'ScreenPlayWidget.app' 'ScreenPlayWidget.app.zip'", cwd=build_config.bin_dir)
print("Run xcnotary submit")
run("xcrun notarytool submit ScreenPlay.app.zip --keychain-profile 'ScreenPlay' --wait", cwd=build_config.bin_dir)
run("xcrun notarytool submit ScreenPlayWallpaper.app.zip --keychain-profile 'ScreenPlay' --wait", cwd=build_config.bin_dir)
run("xcrun notarytool submit ScreenPlayWidget.app.zip --keychain-profile 'ScreenPlay' --wait", cwd=build_config.bin_dir)
print("Run stapler staple")
run("xcrun stapler staple ScreenPlay.app", cwd=build_config.bin_dir)
run("xcrun stapler staple ScreenPlayWallpaper.app", cwd=build_config.bin_dir)
run("xcrun stapler staple ScreenPlayWidget.app", cwd=build_config.bin_dir)
print("Run spctl assess")
run("spctl --assess --verbose \"ScreenPlay.app/\"", cwd=build_config.bin_dir)
run("spctl --assess --verbose \"ScreenPlayWallpaper.app/\"", cwd=build_config.bin_dir)
run("spctl --assess --verbose \"ScreenPlayWidget.app/\"", cwd=build_config.bin_dir)
print("Remove *.app.zip files.")
run("rm ScreenPlay.app.zip", cwd=build_config.bin_dir)
run("rm ScreenPlayWallpaper.app.zip", cwd=build_config.bin_dir)
run("rm ScreenPlayWidget.app.zip", cwd=build_config.bin_dir)
# We also need to sign the installer in osx:
if build_config.create_installer == "ON":
run("codesign --deep -f -s \"Developer ID Application: Elias Steurer (V887LHYKRH)\" --timestamp --options \"runtime\" -f --deep \"ScreenPlay-Installer.dmg/ScreenPlay-Installer.app/Contents/MacOS/ScreenPlay-Installer\"", cwd=build_config.build_folder)
run("codesign --verify --verbose=4 \"ScreenPlay-Installer.dmg/ScreenPlay-Installer.app/Contents/MacOS/ScreenPlay-Installer\"",
cwd=build_config.build_folder)
run("xcnotary notarize ScreenPlay-Installer.dmg/ScreenPlay-Installer.app -d kelteseth@gmail.com -k ScreenPlay",
cwd=build_config.build_folder)
run("spctl --assess --verbose \"ScreenPlay-Installer.dmg/ScreenPlay-Installer.app/\"",
cwd=build_config.build_folder)
run("codesign --deep -f -s \"Developer ID Application: Elias Steurer (V887LHYKRH)\" --timestamp --options \"runtime\" -f --deep \"ScreenPlay-Installer.dmg/\"", cwd=build_config.build_folder)
run("codesign --verify --verbose=4 \"ScreenPlay-Installer.dmg/\"",
cwd=build_config.build_folder)
run("xcnotary notarize ScreenPlay-Installer.dmg -d kelteseth@gmail.com -k ScreenPlay",
cwd=build_config.build_folder)
run("spctl --assess --verbose \"ScreenPlay-Installer.dmg/\"",
cwd=build_config.build_folder)

View File

@ -5,7 +5,6 @@ from os import chdir
from concurrent.futures import ThreadPoolExecutor
import os
import subprocess
import zipfile
def run(cmd, cwd=Path.cwd()):
result = subprocess.run(cmd, shell=True, cwd=cwd)
@ -52,4 +51,36 @@ def run_io_tasks_in_parallel(tasks):
with ThreadPoolExecutor() as executor:
running_tasks = [executor.submit(task) for task in tasks]
for running_task in running_tasks:
running_task.result()
running_task.result()
# Based on https://gist.github.com/l2m2/0d3146c53c767841c6ba8c4edbeb4c2c
def get_vs_env_dict():
vcvars: str # We support 2019 or 2022
# Hardcoded VS path
# check if vcvars64.bat is available.
msvc_2019_path = "C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Community\\VC\\Auxiliary\\Build\\vcvars64.bat"
msvc_2022_path = "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\Auxiliary\\Build\\vcvars64.bat"
if Path(msvc_2019_path).exists():
vcvars = msvc_2019_path
# Prefer newer MSVC and override if exists
if Path(msvc_2022_path).exists():
vcvars = msvc_2022_path
if not vcvars:
raise RuntimeError(
"No Visual Studio installation found, only 2019 and 2022 are supported.")
print(f"\n\nLoading MSVC env variables via {vcvars}\n\n")
cmd = [vcvars, '&&', 'set']
popen = subprocess.Popen(
cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout, stderr = popen.communicate()
if popen.wait() != 0:
raise ValueError(stderr.decode("mbcs"))
output = stdout.decode("mbcs").split("\r\n")
return dict((e[0].upper(), e[1]) for e in [p.rstrip().split("=", 1) for p in output] if len(e) == 2)