Link the C runtime statically on Windows
smb.exe imported VCRUNTIME140.dll, so a machine without the Visual C++ Redistributable failed at load with 0xC0000135 (this is what the winget validation hit on 0.7.2). Build with +crt-static on windows-msvc and have the release workflow fail if dumpbin still lists a VC++ runtime DLL. Also add PrivacyUrl to the winget locale template. ci.yml has no Windows job, so the check runs only in release-github.yml. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Seto Elkahfi committed
Oct 19, 2026 at 23:34 UTC
fccb7ea0484246043f93190f945338c0b6e815d0
5 files changed
+50
.cargo/config.toml
new
+7
@@ -0,0 +1,7 @@
1
+# Link the MSVC C runtime statically on Windows. Without this the binary
2
+# imports VCRUNTIME140.dll and the UCRT api-ms-win-crt-*.dll set, and on a
3
+# machine without the Visual C++ Redistributable it dies at load time with
4
+# STATUS_DLL_NOT_FOUND (0xC0000135) before main runs. winget ships smb as a
5
+# portable exe, so there is no installer to bring the runtime along.
6
+[target.'cfg(all(windows, target_env = "msvc"))']
7
+rustflags = ["-C", "target-feature=+crt-static"]
.github/workflows/release-github.yml
+5
@@ -88,6 +88,11 @@ jobs:
88
shell: bash
89
run: cargo build --locked --release --target ${{ matrix.target }} --package smbcloud-cli
90
91
+ - name: Check Windows binary has no VC++ runtime dependency
92
+ if: contains(matrix.target, 'windows')
93
+ shell: pwsh
94
+ run: ./packaging/windows/check-static-crt.ps1 -Path target/${{ matrix.target }}/release/${{ env.PROJECT_NAME }}.exe
95
+
96
- name: Package binary
97
shell: bash
98
run: |
packaging/windows/check-static-crt.ps1
new
+35
@@ -0,0 +1,35 @@
1
+# Fails if a Windows binary depends on the Visual C++ runtime DLLs.
2
+#
3
+# smb is linked with +crt-static (see .cargo/config.toml) so it runs on a
4
+# clean Windows install. If RUSTFLAGS overrides that, or a native dependency
5
+# drags in the DLL runtime, the exe still builds and passes tests on a runner
6
+# that has the redistributable installed, then fails to load on users'
7
+# machines. This check catches it before release.
8
+param(
9
+ [Parameter(Mandatory = $true)]
10
+ [string]$Path
11
+)
12
+
13
+$ErrorActionPreference = 'Stop'
14
+
15
+$vswhere = Join-Path ${env:ProgramFiles(x86)} 'Microsoft Visual Studio\Installer\vswhere.exe'
16
+$dumpbin = & $vswhere -latest -products * -find 'VC\Tools\MSVC\*\bin\Hostx64\x64\dumpbin.exe' |
17
+ Select-Object -First 1
18
+if (-not $dumpbin) {
19
+ throw 'dumpbin.exe not found; is the MSVC toolset installed?'
20
+}
21
+
22
+$output = & $dumpbin /nologo /dependents $Path
23
+if ($LASTEXITCODE -ne 0) {
24
+ throw "dumpbin failed on $Path"
25
+}
26
+$output | Write-Host
27
+
28
+$runtime = $output |
29
+ Where-Object { $_ -match '^\s+(vcruntime|msvcp|concrt|ucrtbase|api-ms-win-crt-)\S*\.dll\s*$' } |
30
+ ForEach-Object { $_.Trim() }
31
+if ($runtime) {
32
+ Write-Host "::error::$Path depends on the Visual C++ runtime: $($runtime -join ', ')"
33
+ exit 1
34
+}
35
+Write-Host "$Path does not depend on the Visual C++ runtime."
packaging/winget/smbCloudXYZ.smbcloud-cli.installer.yaml.in
+2
@@ -2,6 +2,8 @@
2
PackageIdentifier: smbCloudXYZ.smbcloud-cli
3
PackageVersion: @VERSION@
4
InstallerType: portable
5
+# No VCRedist dependency: the exe links the C runtime statically
6
+# (.cargo/config.toml), and CI rejects a build that imports it.
7
# `Commands` is what winget names the shim it drops on PATH. The release
8
# assets are smb-win-<arch>.exe, but the command has to be `smb`.
9
Commands:
packaging/winget/smbCloudXYZ.smbcloud-cli.locale.en-US.yaml.in
+1
@@ -7,6 +7,7 @@ PublisherUrl: https://smbcloud.xyz/
7
PublisherSupportUrl: https://github.com/@REPO@/issues
8
PackageName: smbCloud CLI
9
PackageUrl: https://smbcloud.xyz/
10
+PrivacyUrl: https://smbcloud.xyz/privacy-policy
11
License: Apache-2.0
12
LicenseUrl: https://github.com/@REPO@/blob/main/LICENSE
13
ShortDescription: smbCloud command line interface.