Files
build_infra/install.ps1
T
2026-06-16 10:23:51 +08:00

499 lines
14 KiB
PowerShell
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ============================================================
# Config
# ============================================================
$ErrorActionPreference = "Stop"
$isoPath = "D:\ae\iso\feiq_zh-cn_windows_10_business_editions_version_22h2_updated_oct_2025_x64_dvd_d4e92df7.iso"
$index = 2
$baseDir = "D:\sysmbols_1405"
$mountDir = Join-Path $baseDir "mount"
$symbolDir = Join-Path $baseDir "symbols"
$logDir = Join-Path $baseDir "symbol_logs"
$symchk = "D:\ae\env\EWDK_vb_release_svc_prod1_19041_201201-2105\Program Files\Windows Kits\10\Debuggers\x64\symchk.exe"
# true = symchk 输出更详细,但更慢
# false = 输出少一些
$verboseSymchk = $true
# ============================================================
# Precomputed paths
# ============================================================
$winDir = Join-Path $mountDir "Windows"
$system32Dir = Join-Path $winDir "System32"
$driversDir = Join-Path $system32Dir "drivers"
$ntoskrnlPath = Join-Path $system32Dir "ntoskrnl.exe"
$symbolPath = "srv*$symbolDir*https://msdl.microsoft.com/download/symbols"
$coreSymbolListFile = Join-Path $logDir "core_symbol_files.txt"
$missingSymbolListFile = Join-Path $logDir "core_symbol_missing.txt"
$coreSymchkLogFile = Join-Path $logDir "core_symbols_symchk.log"
# 只下载核心符号,不递归扫描整个 drivers 目录
$coreSymbolRelativePaths = @(
# Kernel / HAL / debug transport
"Windows\System32\ntoskrnl.exe"
"Windows\System32\hal.dll"
"Windows\System32\kdcom.dll"
# Code integrity / crypto / security
"Windows\System32\ci.dll"
"Windows\System32\drivers\ksecdd.sys"
"Windows\System32\drivers\ksecpkg.sys"
"Windows\System32\drivers\cng.sys"
# Win32 kernel side
"Windows\System32\win32k.sys"
"Windows\System32\win32kbase.sys"
"Windows\System32\win32kfull.sys"
# Plug and Play / ACPI / PCI
"Windows\System32\drivers\ACPI.sys"
"Windows\System32\drivers\pci.sys"
"Windows\System32\drivers\msisadrv.sys"
"Windows\System32\drivers\vdrvroot.sys"
# Storage stack
"Windows\System32\drivers\disk.sys"
"Windows\System32\drivers\partmgr.sys"
"Windows\System32\drivers\mountmgr.sys"
"Windows\System32\drivers\volmgr.sys"
"Windows\System32\drivers\volsnap.sys"
"Windows\System32\drivers\classpnp.sys"
"Windows\System32\drivers\storport.sys"
"Windows\System32\drivers\storahci.sys"
"Windows\System32\drivers\stornvme.sys"
# File system stack
"Windows\System32\drivers\ntfs.sys"
"Windows\System32\drivers\fltmgr.sys"
"Windows\System32\drivers\fileinfo.sys"
# Network stack
"Windows\System32\drivers\ndis.sys"
"Windows\System32\drivers\NETIO.SYS"
"Windows\System32\drivers\tcpip.sys"
"Windows\System32\drivers\fwpkclnt.sys"
# Driver framework
"Windows\System32\drivers\Wdf01000.sys"
"Windows\System32\drivers\WppRecorder.sys"
# Graphics kernel
"Windows\System32\drivers\dxgkrnl.sys"
"Windows\System32\drivers\dxgmms2.sys"
)
# ============================================================
# Helpers
# ============================================================
function Ensure-Dirs {
New-Item -ItemType Directory -Force -Path $baseDir | Out-Null
New-Item -ItemType Directory -Force -Path $mountDir | Out-Null
New-Item -ItemType Directory -Force -Path $symbolDir | Out-Null
New-Item -ItemType Directory -Force -Path $logDir | Out-Null
}
function Test-WimMounted {
param(
[Parameter(Mandatory = $true)]
[string]$MountDir
)
$info = dism /Get-MountedWimInfo 2>&1
return ($info -match [regex]::Escape($MountDir))
}
function Ensure-MountDirReady {
if (Test-WimMounted -MountDir $mountDir) {
Write-Host "WIM already mounted by DISM: $mountDir"
if (-not (Test-Path -LiteralPath $ntoskrnlPath)) {
throw "DISM 认为 WIM 已挂载,但找不到 ntoskrnl.exe$ntoskrnlPath。请先执行 clean_env 或手动 dism /Unmount-Wim /Discard。"
}
return $true
}
if (Test-Path -LiteralPath $mountDir) {
$items = Get-ChildItem -LiteralPath $mountDir -Force -ErrorAction SilentlyContinue
if ($items.Count -gt 0) {
Write-Host "Mount directory is not empty but DISM says not mounted. Cleaning: $mountDir" -ForegroundColor Yellow
Get-ChildItem -LiteralPath $mountDir -Force |
Remove-Item -Recurse -Force -ErrorAction Stop
}
}
else {
New-Item -ItemType Directory -Force -Path $mountDir | Out-Null
}
return $false
}
function Get-IsoRoot {
param(
[Parameter(Mandatory = $true)]
[string]$ImagePath
)
$diskImage = Get-DiskImage -ImagePath $ImagePath -ErrorAction SilentlyContinue
if ($null -eq $diskImage -or -not $diskImage.Attached) {
Write-Host "Mounting ISO..."
Mount-DiskImage -ImagePath $ImagePath | Out-Null
Start-Sleep -Seconds 2
}
else {
Write-Host "ISO already mounted."
}
$diskImage = Get-DiskImage -ImagePath $ImagePath
$volume = $diskImage |
Get-Volume |
Where-Object { $_.DriveLetter } |
Select-Object -First 1
if ($null -eq $volume) {
throw "无法获取 ISO 盘符"
}
return "$($volume.DriveLetter):\"
}
function Show-Config {
Write-Host ""
Write-Host "========== Config =========="
Write-Host "isoPath = $isoPath"
Write-Host "index = $index"
Write-Host "baseDir = $baseDir"
Write-Host "mountDir = $mountDir"
Write-Host "symbolDir = $symbolDir"
Write-Host "logDir = $logDir"
Write-Host "symchk = $symchk"
Write-Host "symbolPath = $symbolPath"
Write-Host "ntoskrnl = $ntoskrnlPath"
Write-Host "driversDir = $driversDir"
Write-Host "============================"
Write-Host ""
}
function Assert-BaseTools {
if (-not (Test-Path -LiteralPath $isoPath)) {
throw "ISO 文件不存在:$isoPath"
}
if (-not (Test-Path -LiteralPath $symchk)) {
throw "symchk.exe 不存在:$symchk"
}
}
function Assert-ImageReady {
if (-not (Test-Path -LiteralPath $ntoskrnlPath)) {
throw "离线 Windows 镜像还没有准备好,找不到:$ntoskrnlPath。请先执行 init_env。"
}
if (-not (Test-Path -LiteralPath $driversDir)) {
throw "离线 Windows drivers 目录不存在:$driversDir。请先执行 init_env。"
}
}
function Write-CoreSymbolFileList {
Ensure-Dirs
Assert-ImageReady
$existing = New-Object System.Collections.Generic.List[string]
$missing = New-Object System.Collections.Generic.List[string]
foreach ($rel in $coreSymbolRelativePaths) {
$fullPath = Join-Path $mountDir $rel
if (Test-Path -LiteralPath $fullPath) {
$existing.Add($fullPath)
}
else {
$missing.Add($fullPath)
}
}
if ($missing.Count -gt 0) {
$missing |
Set-Content -LiteralPath $missingSymbolListFile -Encoding UTF8
Write-Host ""
Write-Host "部分核心文件不存在,已记录到:" -ForegroundColor Yellow
Write-Host $missingSymbolListFile -ForegroundColor Yellow
}
if ($existing.Count -eq 0) {
throw "核心符号文件列表为空,无法执行 symchk。"
}
$existing |
Set-Content -LiteralPath $coreSymbolListFile -Encoding ASCII
Write-Host ""
Write-Host "Core symbol files: $($existing.Count)"
Write-Host "Core symbol list : $coreSymbolListFile"
return $coreSymbolListFile
}
function Invoke-SymChk {
param(
[Parameter(Mandatory = $true)]
[string[]]$Arguments,
[Parameter(Mandatory = $true)]
[string]$LogFile
)
Write-Host ""
Write-Host "symchk arguments:" -ForegroundColor Cyan
Write-Host ($Arguments -join " ")
$oldErrorActionPreference = $ErrorActionPreference
$hasNativeCommandPreference = Test-Path Variable:\PSNativeCommandUseErrorActionPreference
$oldNativeCommandUseErrorActionPreference = $null
if ($hasNativeCommandPreference) {
$oldNativeCommandUseErrorActionPreference = $PSNativeCommandUseErrorActionPreference
}
$exitCode = $null
try {
# symchk 的详细信息可能写到 stderr。
# 这里临时放宽 ErrorActionPreference,避免正常 stderr 被 PowerShell 当成 NativeCommandError 中断。
$ErrorActionPreference = "Continue"
if ($hasNativeCommandPreference) {
$global:PSNativeCommandUseErrorActionPreference = $false
}
& $symchk @Arguments 2>&1 |
ForEach-Object {
$line = $_.ToString()
Write-Host $line
Add-Content `
-LiteralPath $LogFile `
-Value $line `
-Encoding UTF8
}
$exitCode = $LASTEXITCODE
}
finally {
$ErrorActionPreference = $oldErrorActionPreference
if ($hasNativeCommandPreference) {
$global:PSNativeCommandUseErrorActionPreference = $oldNativeCommandUseErrorActionPreference
}
}
if ($exitCode -ne 0) {
throw "symchk 执行失败,ExitCode=$exitCode,日志:$LogFile"
}
}
# ============================================================
# Main functions
# ============================================================
function init_env {
Write-Host ""
Write-Host "Initializing environment..."
Ensure-Dirs
Assert-BaseTools
Show-Config
Write-Host "[init] Getting ISO root..."
$isoRoot = Get-IsoRoot -ImagePath $isoPath
Write-Host "[init] Checking install.wim..."
$wimFile = Join-Path $isoRoot "sources\install.wim"
if (-not (Test-Path -LiteralPath $wimFile)) {
throw "install.wim 不存在:$wimFile"
}
Write-Host "ISO Root: $isoRoot"
Write-Host "WIM File: $wimFile"
$alreadyMounted = Ensure-MountDirReady
if (-not $alreadyMounted) {
Write-Host "[init] Mounting Windows image, Index $index..."
dism /Mount-Wim "/WimFile:$wimFile" "/Index:$index" "/MountDir:$mountDir" /ReadOnly
if ($LASTEXITCODE -ne 0) {
throw "DISM 挂载 install.wim 失败,ExitCode=$LASTEXITCODE"
}
Write-Host "[init] DISM mount finished."
}
else {
Write-Host "Skip mounting. WIM is already mounted: $mountDir"
}
Write-Host "[init] Checking mounted image..."
Assert-ImageReady
Write-Host "[init] Writing core symbol file list..."
Write-CoreSymbolFileList | Out-Null
Write-Host ""
Write-Host "init_env finished." -ForegroundColor Green
}
function exec {
Write-Host ""
Write-Host "Downloading core Windows symbols..."
Ensure-Dirs
Assert-BaseTools
Assert-ImageReady
Show-Config
$listFile = Write-CoreSymbolFileList
$coreFiles = Get-Content -LiteralPath $listFile |
Where-Object {
-not [string]::IsNullOrWhiteSpace($_)
}
if ($coreFiles.Count -eq 0) {
throw "核心符号文件列表为空:$listFile"
}
if (Test-Path -LiteralPath $coreSymchkLogFile) {
Remove-Item -LiteralPath $coreSymchkLogFile -Force
}
Write-Host ""
Write-Host "Core files to process: $($coreFiles.Count)"
Write-Host "Log file: $coreSymchkLogFile"
Write-Host ""
$current = 0
foreach ($file in $coreFiles) {
$current++
Write-Host "============================================================"
Write-Host "[$current/$($coreFiles.Count)] Checking symbols for:" -ForegroundColor Yellow
Write-Host $file -ForegroundColor Yellow
Write-Host "============================================================"
if (-not (Test-Path -LiteralPath $file)) {
Write-Host "File missing, skipped: $file" -ForegroundColor Red
continue
}
$symchkArgs = @(
"/if", $file
"/s", $symbolPath
"/od"
"/ob"
"/os"
"/ot"
)
if ($verboseSymchk) {
$symchkArgs += "/v"
}
Invoke-SymChk `
-Arguments $symchkArgs `
-LogFile $coreSymchkLogFile
Write-Host ""
}
Write-Host ""
Write-Host "Done." -ForegroundColor Green
Write-Host "Symbols saved to: $symbolDir"
Write-Host "Logs saved to: $logDir"
}
function clean_env {
Write-Host ""
Write-Host "Cleaning up..."
Get-Process symchk -ErrorAction SilentlyContinue |
Stop-Process -Force -ErrorAction SilentlyContinue
if (Test-WimMounted -MountDir $mountDir) {
Write-Host "Unmounting WIM: $mountDir"
dism /Unmount-Wim "/MountDir:$mountDir" /Discard
if ($LASTEXITCODE -ne 0) {
throw "DISM 卸载 WIM 失败,ExitCode=$LASTEXITCODE"
}
Write-Host "WIM unmounted: $mountDir"
}
else {
Write-Host "WIM is not mounted according to DISM: $mountDir"
}
Write-Host "Running DISM cleanup..."
dism /Cleanup-Wim | Out-Host
$diskImage = Get-DiskImage -ImagePath $isoPath -ErrorAction SilentlyContinue
if ($null -ne $diskImage -and $diskImage.Attached) {
Write-Host "Dismounting ISO..."
Dismount-DiskImage -ImagePath $isoPath | Out-Null
}
else {
Write-Host "ISO is not mounted."
}
Write-Host "Cleanup finished." -ForegroundColor Green
}
# ============================================================
# Run
# ============================================================
function Test-Admin {
$currentIdentity = [Security.Principal.WindowsIdentity]::GetCurrent()
$principal = New-Object Security.Principal.WindowsPrincipal($currentIdentity)
return $principal.IsInRole(
[Security.Principal.WindowsBuiltInRole]::Administrator
)
}
if (-not (Test-Admin)) {
Write-Host "错误:当前 PowerShell 不是管理员权限。" -ForegroundColor Red
Write-Host "请右键 PowerShell / CLion,选择“以管理员身份运行”,然后重新执行脚本。" -ForegroundColor Yellow
Read-Host "按回车退出"
exit 1
}
try {
# init_env
# exec
clean_env
}
finally {
Write-Host ""
Read-Host "执行完毕,按回车退出"
}