devAlice
← Windows

Windows 开发环境每周维护 — MSYS2 · winget · 工具链一键更新

一个每周运行的 PowerShell 脚本,可更新 MSYS2 pacman、winget、Scoop、npm 全局包、rustup、cargo 二进制、pipx 和 Flutter。

Windows 开发环境始终是各种依赖的拼凑——IDE 来自 winget,gcc/make/git 来自 MSYS2,CLI 工具来自 scoop,全局包来自 npm/pip/cargo。每个工具的更新命令各不相同,一旦遗忘,最终就会出现类似 ld.exe: cannot find -lcrypt32 这样莫名其妙的 Tauri 构建失败。

我认为 Windows 环境维护问题的根本不在于各个工具的质量,而在于多工具更新步骤的分散性。以前开发者依靠记忆逐一更新;如今通过集成脚本一次覆盖所有工具,因为自动化的维护流程才能防止依赖版本漂移。

本文提供8 步集成 PowerShell 脚本来预防这种情况。每周运行一次——搞定。

TL;DR

  • MSYS2 pacmanwingetScoopnpm -grustupcargo install-updatepipxflutter
  • MSYS2 是第一步:它是 Windows 上构建环境的操作系统。跳过它,原生构建就会像多米诺骨牌一样接连崩溃
  • 每步如果工具未安装则自动跳过;一步失败不影响其余步骤
  • 约 100 行 PowerShell,双击 update-system.bat 即可运行

为什么 MSYS2 是第一步

Windows 的 winget 负责用户应用(Chrome、Slack……)和最终的开发工具产物。但真正编译代码的环境在 MSYS2 里——gcc、make、autotools、openssl 头文件、libsqlite、通过 pacman 管理的 zlib。

跳过一个月,你就会看到:

  • Rust + Tauri 交叉编译因 OpenSSL 头文件版本不匹配而失败
  • Python C 扩展构建(pip install pillow 等)因 freetype 版本偏差而失败
  • 任何基于 MinGW 的构建因依赖库 ABI 不匹配而失败

这就是为什么 pacman -Syuu(双 u——含降级的同步)始终是第一步

前置条件

  • Windows 10 1709+ / 11(内置 winget)
  • 完整的 8 步需要:预先安装 MSYS2、pipx、cargo-update、Flutter
  • Scoop 可选——未安装时自动跳过

一次性准备

管理员 PowerShell:

# MSYS2(约 700MB)——构建环境的操作系统
winget install MSYS2.MSYS2
 
# pipx(Python 全局包管理器)
python -m pip install --user pipx
python -m pipx ensurepath
 
# cargo-update 扩展
cargo install cargo-update
 
# Flutter(约 1GB)
winget install Flutter.Flutter
# 如果 winget 没有:https://docs.flutter.dev/get-started/install/windows

安装 MSYS2 后,打开 MSYS2 shell 运行一次 pacman -Syuu 同步基础包。

8 个更新步骤

#步骤命令备注
1MSYS2pacman -Syuu构建环境的操作系统 — 第 1 步
2wingetwinget upgrade --all系统包
3Scoopscoop update *仅在已安装时执行
4npm 全局包npm update -g全局 npm 包
5Rust 工具链rustup updatestable channel
6Cargo 二进制cargo install-update -a需要 cargo-update
7pipxpipx upgrade-allPython 全局工具
8Flutter SDKflutter upgrade当前 channel 的最新版

集成脚本

保存以下两个文件:

update-system.ps1(逻辑主体):

<#
.SYNOPSIS
    批量系统工具更新 — Windows
.DESCRIPTION
    一次性刷新构建环境 / 包管理器 / 语言工具链。
.NOTES
    运行:update-system.bat(建议管理员权限)
    目标:Windows 10/11
#>
 
$ErrorActionPreference = "Continue"
$script:Total = 8
$script:Step  = 0
 
function Step-Header {
    param([string]$Message)
    $script:Step++
    Write-Host ""
    Write-Host ("[{0}/{1}] {2}" -f $script:Step, $script:Total, $Message) -ForegroundColor Cyan
}
 
function Has-Command {
    param([string]$Cmd)
    $null = Get-Command $Cmd -ErrorAction SilentlyContinue
    return $?
}
 
function Write-OK   { param([string]$m) Write-Host "  OK   $m" -ForegroundColor Green }
function Write-Skip { param([string]$m) Write-Host "  SKIP $m" -ForegroundColor DarkGray }
function Write-Warn { param([string]$m) Write-Host "  WARN $m" -ForegroundColor Yellow }
function Write-Fail { param([string]$m) Write-Host "  FAIL $m" -ForegroundColor Red }
 
Write-Host "━━━ System update (Windows) ━━━" -ForegroundColor Cyan
 
# 1. MSYS2(最重要——构建环境的操作系统)
Step-Header "MSYS2 (pacman -Syuu) — critical"
$msys2Candidates = @(
    "C:\msys64\usr\bin\bash.exe",
    "D:\msys64\usr\bin\bash.exe",
    "C:\tools\msys64\usr\bin\bash.exe"
)
$msys2Bash = $msys2Candidates | Where-Object { Test-Path $_ } | Select-Object -First 1
if ($msys2Bash) {
    Write-Host "  → using: $msys2Bash" -ForegroundColor DarkGray
    & $msys2Bash -lc "pacman -Syuu --noconfirm"
    if ($LASTEXITCODE -eq 0) {
        Write-OK "pacman -Syuu done (rerun once if core packages updated)"
    } else {
        Write-Fail "pacman -Syuu (after core updates, restart shell + rerun)"
    }
} else {
    Write-Skip "MSYS2 not installed (checked: C:\msys64, D:\msys64, C:\tools\msys64)"
}
 
# 2. winget
Step-Header "winget (system packages)"
if (Has-Command winget) {
    winget upgrade --all --silent --accept-package-agreements --accept-source-agreements
    if ($LASTEXITCODE -eq 0) { Write-OK "winget upgrade --all" }
    else { Write-Fail "winget upgrade --all (some packages may need admin/interactive)" }
} else {
    Write-Skip "winget not installed"
}
 
# 3. Scoop(可选)
Step-Header "Scoop (if installed)"
if (Has-Command scoop) {
    scoop update
    scoop update *
    Write-OK "scoop update / update *"
} else {
    Write-Skip "scoop not installed"
}
 
# 4. npm 全局包
Step-Header "npm globals"
if (Has-Command npm) {
    npm update -g
    if ($LASTEXITCODE -eq 0) { Write-OK "npm update -g" } else { Write-Fail "npm update -g" }
} else {
    Write-Skip "npm not installed"
}
 
# 5. Rust 工具链
Step-Header "Rust toolchain (rustup)"
if (Has-Command rustup) {
    rustup update
    if ($LASTEXITCODE -eq 0) { Write-OK "rustup update" } else { Write-Fail "rustup update" }
} else {
    Write-Skip "rustup not installed"
}
 
# 6. Cargo 二进制
Step-Header "Cargo binaries (cargo install-update -a)"
if (Has-Command cargo) {
    cargo install-update --version 2>$null | Out-Null
    if ($LASTEXITCODE -eq 0) {
        cargo install-update -a
        if ($LASTEXITCODE -eq 0) { Write-OK "cargo install-update -a" }
        else { Write-Fail "cargo install-update -a" }
    } else {
        Write-Warn "cargo-update missing (install: cargo install cargo-update)"
    }
} else {
    Write-Skip "cargo not installed"
}
 
# 7. pipx
Step-Header "pipx (Python global tools)"
if (Has-Command pipx) {
    pipx upgrade-all
    if ($LASTEXITCODE -eq 0) { Write-OK "pipx upgrade-all" } else { Write-Fail "pipx upgrade-all" }
} else {
    Write-Skip "pipx not installed"
}
 
# 8. Flutter SDK
Step-Header "Flutter SDK (flutter upgrade)"
if (Has-Command flutter) {
    flutter upgrade
    if ($LASTEXITCODE -eq 0) { Write-OK "flutter upgrade" } else { Write-Fail "flutter upgrade" }
} else {
    Write-Skip "flutter not installed"
}
 
Write-Host ""
Write-Host "━━━ Done ━━━" -ForegroundColor Cyan
Write-Host "Checks: winget upgrade  /  npm outdated -g  /  pacman -Qu (MSYS2)" -ForegroundColor DarkGray

update-system.bat(双击入口):

@echo off
title System Update
 
echo ============================================
echo   System Update (Windows)
echo   MSYS2 + winget + npm + rustup + cargo + pipx
echo ============================================
echo.
 
powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%~dp0update-system.ps1" %*
 
if %errorlevel% neq 0 (
    echo.
    echo [ERROR] Update failed. Check log above.
) else (
    echo.
    echo [DONE] System update complete.
)
 
pause

将 .ps1 文件保存为 UTF-8 with BOM。 没有 BOM,Windows PowerShell 5.1 会乱码非 ASCII 输出。在 VS Code 中,点击右下角编码 → 「Save with Encoding」→ 「UTF-8 with BOM」。

示例输出

双击 update-system.bat(接受 UAC)。首次运行约 5–10 分钟(MSYS2 + winget),后续运行 1–3 分钟。

━━━ System update (Windows) ━━━

[1/8] MSYS2 (pacman -Syuu) — critical
  → using: C:\msys64\usr\bin\bash.exe
:: Synchronizing package databases...
:: Starting core system upgrade...
...
  OK pacman -Syuu done (rerun once if core packages updated)

[2/8] winget (system packages)
  Microsoft.VisualStudioCode 1.99.2 → 1.100.0
  GitHub.cli 2.65.0 → 2.66.1
  ...
  OK winget upgrade --all

[3/8] Scoop (if installed)
  SKIP scoop not installed

[4/8] npm globals
  OK npm update -g

...

━━━ Done ━━━
Checks: winget upgrade  /  npm outdated -g  /  pacman -Qu (MSYS2)

自动化——仅设置 Task Scheduler 提醒

不建议完全自动化。winget 偶尔会弹出 EULA 对话框,MSYS2 核心更新需要重启 shell,这会破坏无人值守的运行。设置提醒,而不是自动运行

通过 PowerShell 配置 Task Scheduler:

$action = New-ScheduledTaskAction -Execute "powershell.exe" `
    -Argument '-NoProfile -Command "[System.Reflection.Assembly]::LoadWithPartialName(\"System.Windows.Forms\"); [System.Windows.Forms.MessageBox]::Show(\"Weekly update-system reminder\", \"Dev Maintenance\")"'
 
$trigger = New-ScheduledTaskTrigger -Weekly -DaysOfWeek Friday -At 10:00am
 
Register-ScheduledTask -TaskName "Dev Update Reminder" `
    -Action $action -Trigger $trigger `
    -Description "Weekly reminder to run update-system"

每周五 10:00 弹出一个消息框,由你决定何时运行。

注意事项

  • MSYS2 核心更新需要重启 shell 后再次运行:当 pacman 更新自身或核心库(libcurl、openssl)时,它会中断事务并要求重新运行。第一次完成后再运行一次 update-system.bat
  • 部分 winget 包需要交互式输入:大多数包用 --silent 可静默安装,但新 EULA 会阻断流程。请手动安装这些包。
  • Flutter 可能不在 winget 仓库中:如果 Flutter.Flutter 未列出,从 docs.flutter.dev 下载 zip 并添加到 PATH。
  • Scoop 是可选的:如果你只用 winget 或只用 scoop,另一步会干净地跳过。无冲突风险。
  • -Syuu 中的第二个 u:同步包括降级。MSYS2 有时会故意降级包;只用 -Syu 会积累冲突。

Mac 用户?

涵盖 brew + 语言工具链 + Flutter + CocoaPods 的同类指南 → Mac 开发环境每周维护

总结

  • 在 Windows 上,MSYS2 更新是优先级 #1
  • 逐一更新工具 → 你会遗忘
  • 一个集成脚本 → 随便忘,跑就行
  • 只设置提醒,不要自动执行
  • 每周一次可以防止构建中断、证书过期和 SDK 版本漂移