package visual c++ directly in windows installer

This commit is contained in:
Julian Chang 2026-08-07 22:07:26 +07:00
parent d54283d99c
commit e2064a5784
No known key found for this signature in database
5 changed files with 141 additions and 12 deletions

View file

@ -132,6 +132,27 @@ jobs:
rm -f "$out/OneClient.icns"
test -f "$out/Assets.car" || { echo "::error::actool produced no Assets.car"; exit 1; }
- name: (Windows) Bundle VC++ redistributable into the installer
if: matrix.platform == 'windows-latest'
shell: bash
run: python3 distribution/windows/bundle-vcredist.py
- name: (Windows) Verify bundled redistributable is Microsoft-signed
if: matrix.platform == 'windows-latest'
shell: pwsh
run: |
$sig = Get-AuthenticodeSignature 'target/vcredist/vc_redist.x64.exe'
Write-Host "Authenticode status: $($sig.Status)"
Write-Host "Signer: $($sig.SignerCertificate.Subject)"
if ($sig.Status -ne 'Valid') {
Write-Error "vc_redist.x64.exe signature is $($sig.Status), refusing to bundle it"
exit 1
}
if ($sig.SignerCertificate.Subject -notmatch 'O=Microsoft Corporation') {
Write-Error "vc_redist.x64.exe is not signed by Microsoft: $($sig.SignerCertificate.Subject)"
exit 1
}
- name: Package (unsigned)
shell: bash
env:

View file

@ -345,6 +345,27 @@ jobs:
rm -f "$out/OneClient.icns"
test -f "$out/Assets.car" || { echo "::error::actool produced no Assets.car"; exit 1; }
- name: (Windows) Bundle VC++ redistributable into the installer
if: matrix.platform == 'windows-latest'
shell: bash
run: python3 distribution/windows/bundle-vcredist.py
- name: (Windows) Verify bundled redistributable is Microsoft-signed
if: matrix.platform == 'windows-latest'
shell: pwsh
run: |
$sig = Get-AuthenticodeSignature 'target/vcredist/vc_redist.x64.exe'
Write-Host "Authenticode status: $($sig.Status)"
Write-Host "Signer: $($sig.SignerCertificate.Subject)"
if ($sig.Status -ne 'Valid') {
Write-Error "vc_redist.x64.exe signature is $($sig.Status), refusing to bundle it"
exit 1
}
if ($sig.SignerCertificate.Subject -notmatch 'O=Microsoft Corporation') {
Write-Error "vc_redist.x64.exe is not signed by Microsoft: $($sig.SignerCertificate.Subject)"
exit 1
}
- name: Package
shell: bash
env:

View file

@ -0,0 +1,81 @@
#!/usr/bin/env python3
import argparse
import hashlib
import re
import sys
import urllib.request
from pathlib import Path
REDIST_URL = "https://aka.ms/vs/17/release/vc_redist.x64.exe"
BEGIN = "; >>> VC_REDIST_INSTALL >>>"
END = "; <<< VC_REDIST_INSTALL <<<"
REPO_ROOT = Path(__file__).resolve().parents[2]
DEFAULT_CARGO_TOML = REPO_ROOT / "packages" / "oneclient_app" / "Cargo.toml"
DEFAULT_REDIST = REPO_ROOT / "target" / "vcredist" / "vc_redist.x64.exe"
BLOCK = """ {begin}
; Generated by distribution/windows/bundle-vcredist.py -- do not edit by hand.
DetailPrint "Installing Microsoft Visual C++ Redistributable (x64)..."
InitPluginsDir
File "/oname=$PLUGINSDIR\\\\vc_redist.x64.exe" "{redist}"
ExecWait '"$PLUGINSDIR\\\\vc_redist.x64.exe" /quiet /norestart' $R3
${{If}} $R3 == 3010
SetRebootFlag true
${{EndIf}}
{end}"""
REGION = re.compile(
r"^[ \t]*" + re.escape(BEGIN) + r".*?^[ \t]*" + re.escape(END) + r"[ \t]*$",
re.DOTALL | re.MULTILINE,
)
def download(dest: Path) -> None:
dest.parent.mkdir(parents=True, exist_ok=True)
print(f"downloading {REDIST_URL} -> {dest}")
with urllib.request.urlopen(REDIST_URL) as resp, dest.open("wb") as out:
while chunk := resp.read(1 << 20):
out.write(chunk)
data = dest.read_bytes()
if data[:2] != b"MZ":
sys.exit(f"error: {dest} is not a PE image (no MZ header)")
if len(data) < 10 * 1024 * 1024:
sys.exit(f"error: {dest} is only {len(data)} bytes, expected >10MB")
print(f" {len(data)} bytes, sha256={hashlib.sha256(data).hexdigest()}")
def patch(cargo_toml: Path, redist: Path) -> None:
text = cargo_toml.read_text(encoding="utf-8")
if not REGION.search(text):
sys.exit(f"error: {BEGIN} .. {END} region not found in {cargo_toml}")
block = BLOCK.format(begin=BEGIN, end=END, redist=str(redist).replace("\\", "\\\\"))
cargo_toml.write_text(REGION.sub(lambda _: block, text, count=1), encoding="utf-8")
print(f"patched {cargo_toml} to embed {redist}")
def main() -> None:
ap = argparse.ArgumentParser(description=__doc__)
ap.add_argument("--cargo-toml", type=Path, default=DEFAULT_CARGO_TOML)
ap.add_argument("--redist", type=Path, default=DEFAULT_REDIST)
ap.add_argument(
"--skip-download",
action="store_true",
help="reuse an already-downloaded --redist instead of fetching it",
)
args = ap.parse_args()
redist = args.redist.resolve()
if args.skip_download:
if not redist.is_file():
sys.exit(f"error: --skip-download given but {redist} does not exist")
else:
download(redist)
patch(args.cargo_toml.resolve(), redist)
if __name__ == "__main__":
main()

View file

@ -63,7 +63,7 @@ winresource = "=0.1.31"
product-name = "OneClient"
identifier = "org.polyfrost.OneClient"
category = "Game"
publisher = "Polyfrost"
publisher = "Polyfrost Inc."
copyright = "© 2026 Polyfrost Inc."
homepage = "https://polyfrost.org/projects/oneclient"
long-description = "Next-generation open source Minecraft launcher"
@ -115,17 +115,12 @@ Section "-VCRedist"
Goto vcredist_done
${EndIf}
DetailPrint "Installing Microsoft Visual C++ Redistributable (x64)..."
nsis_tauri_utils::Download "https://aka.ms/vs/17/release/vc_redist.x64.exe" "$PLUGINSDIR\\vc_redist.x64.exe"
Pop $R2
${If} $R2 == 0
ExecWait '"$PLUGINSDIR\\vc_redist.x64.exe" /quiet /norestart' $R3
${If} $R3 == 3010
SetRebootFlag true
${EndIf}
${ElseIf} $PassiveMode != 1
MessageBox MB_OK|MB_ICONEXCLAMATION "Could not download the Microsoft Visual C++ Redistributable. OneClient may fail to start until you install it manually from https://aka.ms/vs/17/release/vc_redist.x64.exe"
; >>> VC_REDIST_INSTALL >>>
${If} $PassiveMode != 1
MessageBox MB_OK|MB_ICONEXCLAMATION "OneClient needs the Microsoft Visual C++ Redistributable (x64) 14.40 or newer. Install it from https://aka.ms/vs/17/release/vc_redist.x64.exe, then run this installer again."
${EndIf}
; <<< VC_REDIST_INSTALL <<<
vcredist_done:
SectionEnd
"""

View file

@ -1,9 +1,20 @@
fn main() {
// Embeds the app icon into the Windows executable.
// Embeds the app icon and VERSIONINFO into the Windows executable.
#[cfg(windows)]
{
let mut res = winresource::WindowsResource::new();
res.set_icon("icons/icon.ico");
let version = env!("CARGO_PKG_VERSION");
res.set("CompanyName", "Polyfrost Inc.");
res.set("ProductName", "OneClient");
res.set("FileDescription", "OneClient");
res.set("LegalCopyright", "© 2026 Polyfrost Inc.");
res.set("OriginalFilename", "oneclient_app.exe");
res.set("InternalName", "oneclient_app");
res.set("FileVersion", version);
res.set("ProductVersion", version);
res.compile()
.expect("failed to embed Windows icon resource");
}