OpenConsole 构建指南:从子模块初始化到 Windows Terminal 的 MSIX 打包部署
【免费下载链接】terminalThe new Windows Terminal and the original Windows console host, all in the same place!项目地址: https://gitcode.com/GitHub_Trending/term/terminal
本文围绕 doc/building.md 展开,完整讲解 OpenConsole(Windows Terminal 与 conhost 的共用仓库)的构建全流程:子模块初始化、PowerShell 与 CMD 两种构建方式、测试脚本、NuGet 依赖的版本管理机制,以及如何从命令行构建并部署 Terminal 的.msix包、处理DEP0700部署失败。读完后可在 Windows 上独立完成该仓库的构建、测试、调试与打包部署。
一、构建前准备:子模块初始化与代码格式化工具
仓库使用 git 子模块管理部分依赖。在首次构建前,务必先还原/更新子模块:
git submodule update --init --recursive仓库根目录的 OpenConsole.slnx 既可以在 Visual Studio 中打开构建,也可以通过/tools目录下的便捷脚本从命令行构建。
如果使用 Visual Studio 开发,建议同时配置代码格式化(clang-format)。先按下面任一构建说明完成环境搭建,然后执行:
Import-Module .\tools\OpenConsole.psm1 Set-MsBuildDevEnvironment Get-Format之后在 Visual Studio 中进入 Tools > Options > Text Editor > C++ > Formatting,勾选 "Use custom clang-format.exe file",并选择仓库中/packages/clang-format.win-x86.10.0.0/tools/clang-format.exe(点击复选框下方的 "browse" 浏览)。
从源码看,PowerShell 模块 tools/OpenConsole.psm1 通过vswhere定位 Visual Studio 自带的 clang-format(见 Invoke-CodeFormat),因此格式化能力与本机安装的 VS 版本绑定;XAML 文件则由 XamlStyler 处理(配置文件见 XamlStyler.json)。
二、在 PowerShell 中构建
推荐的 PowerShell 构建流程只有三步:
Import-Module .\tools\OpenConsole.psm1 Set-MsBuildDevEnvironment Invoke-OpenConsoleBuild各步骤的作用(结合 tools/OpenConsole.psm1 源码):
Import-Module .\tools\OpenConsole.psm1:导入项目 PowerShell 模块。注意该模块头部声明了#Requires -Version 7(OpenConsole.psm1#L1),即需要 PowerShell 7 及以上版本。Set-MsBuildDevEnvironment:通过VSSetup模块查找已安装的 Visual Studio 实例(要求带Microsoft.VisualStudio.Component.VC.Tools.x86.x64组件),再调用Enter-VsDevShell进入 VS 开发者 Shell 环境,把 MSBuild 等工具注入当前 PowerShell 会话(见 Set-MsbuildDevEnvironment)。它还会根据PROCESSOR_ARCHITECTURE自动映射架构:amd64 →x64、x86 →x86、arm64 →arm64。Invoke-OpenConsoleBuild:先执行两次nuget.exe restore(分别还原 OpenConsole.slnx 和全局包清单 dep/nuget/packages.config),然后调用msbuild.exe OpenConsole.slnx,且任何额外参数都会原样传递给 msbuild(见 Invoke-OpenConsoleBuild):
# 示例:指定配置与平台 Invoke-OpenConsoleBuild /p:Configuration=Release /p:Platform=x64模块共导出了以下函数(见 Export-ModuleMember),文档中列出的核心函数包括:
| 函数 | 作用 |
|---|---|
Invoke-OpenConsoleBuild | 构建整个解决方案,可透传 msbuild 参数 |
Invoke-OpenConsoleTests | 运行测试,默认只运行单元测试 |
Start-OpenConsole | 从输出目录启动 OpenConsole.exe,默认运行 x64 |
Debug-OpenConsole | 启动 OpenConsole.exe 并附加默认调试器,默认 x64 |
Invoke-CodeFormat | 使用 clang-format 按仓库编码风格格式化所有 C++ 文件 |
Invoke-XamlFormat/Test-XamlFormat | 用 XamlStyler 格式化 / 校验 XAML 文件格式 |
其中Invoke-OpenConsoleTests的实现细节值得注意(源码):
- 测试清单来自 tools/tests.xml,按
unit/ft类型区分;不传参数时只跑type="unit"的单元测试;-AllTests跑全部,-FTOnly只跑功能测试,-Test host等可按名称精确选择单个测试组; - 单元测试直接在当前目录执行 TAEF 的
te.exe;功能测试(ft)会通过Invoke-TaefInNewWindow在新建的 OpenConsole 窗口中逐个运行; - 需要 UIA 测试或全量测试时,脚本会自动拉起
dep\WinAppDriver\WinAppDriver.exe,测试结束后再停止它——所以 UIA 测试期间鼠标会被占用,不能移动; - 测试二进制的输出位置是
bin\<Platform>\<Configuration>\,且x86平台在路径上会被映射为Win32。
tools/tests.xml 中定义的测试组包括:host、textBuffer、terminalCore、terminalApp、localTerminalApp、unitSettingsModel、unitControl、interactivityWin32、terminal、adapter、types、til(以上为 unit 类型),以及feature、uia、winconpty(ft 类型)。
三、在 CMD 中构建:razzle 与 bcz
CMD 下的标准流程是:
.\tools\razzle.cmd bczrazzle.cmd:开发者环境初始化
tools/razzle.cmd 仿照 Windows 内部构建系统的同名脚本,负责一次性搭建好命令行构建环境:
- 把
tools目录加入 PATH,使bcz、runut等脚本随处可用; - 设置
OPENCON(仓库根目录)与OPENCON_TOOLS环境变量; - 把
dep\nuget加入 PATH,并先执行nuget restore OpenConsole.slnx和nuget restore dep\nuget\packages.config,以便后续能用上 vswhere; - 定位 MSBuild:优先使用 PATH 中已有的 msbuild.exe;否则用 vswhere 查找,版本范围限定为
[17.0,19.0),即同时兼容 VS 2022(17.x)与 VS 18 预发布版(见 razzle.cmd#L58); - 根据
PROCESSOR_ARCHITECTURE设置ARCH/PLATFORM(AMD64 → x64,否则 x86/Win32),默认构建配置DEFAULT_CONFIGURATION=Debug; - 支持
.razzlerc.cmd个人环境脚本(不存在时自动创建),也接受dbg/rel/x86等参数调整默认配置。
脚本结束时会打印 "The dev environment is ready to go!"。如果找不到 MSBuild,可先在一个开发者 Shell(Developer PowerShell/Command Prompt for VS)中执行Import-Module .\tools\OpenConsole.psm1; Set-MsbuildDevEnvironment再运行 razzle。
bcz / bx / bz:构建命令族
tools/bcz.cmd 用于清理并构建解决方案,支持参数:
dbg:手动指定 Debug 配置构建;rel:手动指定 Release 配置构建;audit:手动指定 AuditMode 配置构建;no_clean:构建前不清理(更快,但可能因残留产物导致意外的构建失败);exclusive:只构建当前目录下的项目而非整个解决方案。
关键行为(bcz.cmd 源码):
- Debug 整解构建时会自动附加
/p:AppxBundle=false,跳过耗时的 appx 打包——想要.msix请用 Release 构建; - 构建命令始终带
/p:GenerateAppxPackageOnBuild=false,防止构建 wapproj 项目时触发完整的 .msix 打包; - 构建前会先
nuget.exe restore(可用环境变量_SKIP_NUGET_RESTORE=1跳过); - 构建结果决定任务栏进度提示:成功则清除进度,失败则闪烁红色状态。
tools/bx.cmd 则是"只构建当前目录项目、且不先清理"的快捷方式,其实现就是一行call bcz exclusive no_clean %*;bcz exclusive依赖 tools/bx.ps1 来解析当前目录对应的.vcxproj项目名。
测试脚本
razzle 环境下还有配套的测试脚本(见 tools/README.md):
runut.cmd—— 运行单元测试;runft.cmd—— 运行功能测试;runuia.cmd—— 运行 UIA 测试;runformat.cmd—— 用 clang-format 格式化所有 C++ 文件以匹配编码风格(提 PR 前应执行,否则 CI 会阻止合并)。
tools/runut.cmd 会把%TAEF%(razzle 中设置为packages\Microsoft.Taef.10.100.251104001\build\Binaries\%ARCH%\TE.exe)指向十余个单元测试 DLL 依次跑通,例如Conhost.Unit.Tests.dll、ConParser.Unit.Tests.dll、til.unit.tests.dll、UnitTests_TerminalCore\Terminal.Core.Unit.Tests.dll等;其中SettingsModel.Unit.Tests.dll单独使用其目录下独立的te.exe运行(对应 tests.xml 中isolatedTaef="true"的unitSettingsModel),并且任何参数都会透传给 TAEF,实现更细粒度的测试控制。
一个推荐的日常工作流(来自 tools/README.md):
bcz dbg && runut /name:*<name of test>*把<name of test>换成你正在开发的功能区域对应测试名(例如鼠标输入相关的MouseInputTest)即可只跑相关测试;不传/name参数则运行全部单元测试。
四、运行与调试
要在 Visual Studio 中调试 Windows Terminal:
- 在解决方案资源管理器中右键
CascadiaPackage,打开属性; - 在 Debug 菜单中,把 "Application process" 和 "Background task process" 都改为 "Native Only";
- 之后按F5即可构建并调试 Terminal 项目。
注意:你不能通过直接运行 WindowsTerminal.exe 来启动 Terminal。原因在于它是打包应用(packaged app),需要以 Appx 包身份注册后才能运行——这也是后文要介绍 MSIX 部署流程的原因。
五、配置类型(Configuration Types)
OpenConsole 有三种构建配置:
- Debug
- Release
- AuditMode
AuditMode 是一个实验性模式,会启用来自 CppCoreCheck 的额外静态分析。这与 bcz.cmd 中audit参数把_LAST_BUILD_CONF设置为AuditMode的实现相互印证。
六、NuGet 依赖的版本管理
全局版本统一管理
本项目的大多数 NuGet 包引用集中在单一配置中,保证所有包有唯一的"权威版本"。该版本会在构建前由构建流水线、环境初始化脚本或 Visual Studio(视情况)还原。
权威版本号定义在 dep/nuget/packages.config。例如当前清单中锁定了Microsoft.Taef 10.100.251104001、Microsoft.Windows.CppWinRT 2.0.250303.1、Microsoft.UI.Xaml 2.8.4、Microsoft.Windows.ImplementationLibrary 1.0.250325.1等原生包,以及Appium.WebDriver、Selenium.WebDriver等用于 UIA 测试的管理包。多数 NuGet 包还带有.props/.targets文件,需要由每个消费项目导入;这些导入语句集中在两个文件里:
- src/common.nugetversions.props
- src/common.nugetversions.targets
以 src/common.nugetversions.props 为例,可以看到其中按包名硬编码了带版本号的导入路径,如packages\Microsoft.Windows.CppWinRT.2.0.250303.1\...与packages\Microsoft.UI.Xaml.2.8.4\...。因此当某个全局管理的版本变化时,以上三个文件必须同步修改。
本地版本包的批量更新
某些 NuGet 包引用(如Microsoft.UI.Xaml)必须通过 Visual Studio 的 NuGet 包管理器之外的方式来更新。可以使用下面的命令片段(其中sed依赖 WSL 环境):
git grep -z -l $PackageName | xargs -0 sed -i -e 's/$OldVersionNumber/$NewVersionNumber/g'其中:
$PackageName是包名,例如Microsoft.UI.Xaml;$OldVersionNumber是当前使用的版本号,例如2.4.0-prerelease.200506002;$NewVersionNumber是要迁移到的版本号,例如2.5.0-prerelease.200812002。
示例用法:
git grep -z -l Microsoft.UI.Xaml | xargs -0 sed -i -e 's/2.4.0-prerelease.200506002/2.5.0-prerelease.200812002/g'使用本地 .nupkg 文件替代下载包
如果希望使用本地的.nupkg文件而不是下载的 NuGet 包,步骤如下:
- 打开根目录的 NuGet.Config,取消注释 "Static Package Dependencies" 那一行(当前仓库中是
<!--<add key="Static Package Dependencies" value="dep\packages" />-->,即第 6 行的注释行); - 创建文件夹
/dep/packages; - 把你的
.nupkg文件放入/dep/packages; - 如果你使用的版本与现有版本不同,还需要按上文"Updating Nuget package references"的方式更新引用。
顺带说明:NuGet.Config 还把globalPackagesFolder与repositorypath都指向仓库内的.\packages目录,并配置了TerminalDependencies私有包源——这正是构建脚本执行nuget restore时包会落到根目录packages\下的原因(例如前面 clang-format 与 TAEF 的路径)。
七、从命令行构建并部署 Terminal 包
构建 .msix
Terminal 以.msix形式捆绑,由CascadiaPackage.wapproj项目产出。在已经运行过tools\razzle.cmd的窗口中执行:
"%msbuild%" "%OPENCON%\OpenConsole.slnx" /p:Configuration=%_LAST_BUILD_CONF% /p:Platform=%ARCH% /p:AppxSymbolPackageEnabled=false /t:Terminal\CascadiaPackage /m这个过程比较耗时,且只生成msix、不会安装它。要部署该包,可以用 PowerShell:
# 如果还没有做过的话: Import-Module .\tools\OpenConsole.psm1; Set-MsBuildDevEnvironment; # Set-MsBuildDevEnvironment 用于找到 makeappx 的路径,执行时间略长。 # 如果你一直停留在 powershell 中,最好还是执行它。 Set-Location -Path src\cascadia\CascadiaPackage\AppPackages\CascadiaPackage_0.0.1.0_x64_Debug_Test; if ((Get-AppxPackage -Name 'WindowsTerminalDev*') -ne $null) { Remove-AppxPackage 'WindowsTerminalDev_0.0.1.0_x64__8wekyb3d8bbwe' }; New-Item ..\loose -Type Directory -Force; makeappx unpack /v /o /p .\CascadiaPackage_0.0.1.0_x64_Debug.msix /d ..\loose\; Add-AppxPackage -Path ..\loose\AppxManifest.xml -Register -ForceUpdateFromAnyVersion -ForceApplicationShutdown流程要点:先按名称(WindowsTerminalDev*)删除已注册的同名开发包;再用makeappx unpack把 msix 解包为松散(loose)目录;最后以松散布局的AppxManifest.xml注册安装。
也有对应的 cmd.exe 版本(本质上是调 PowerShell 执行同样的逻辑,作者注明是直接复制自.vscode\tasks.json):
@rem razzle.cmd 没有设置 WindowsSdkDir(vsdevcmd.bat 会花很多逻辑去找它),这里硬编码: powershell -Command Set-Location -Path %OPENCON%\src\cascadia\CascadiaPackage\AppPackages\CascadiaPackage_0.0.1.0_x64_Debug_Test;if ((Get-AppxPackage -Name 'WindowsTerminalDev*') -ne $null) { Remove-AppxPackage 'WindowsTerminalDev_0.0.1.0_x64__8wekyb3d8bbwe'};New-Item ..\loose -Type Directory -Force;C:\'Program Files (x86)'\'Windows Kits'\10\bin\10.0.19041.0\x64\makeappx unpack /v /o /p .\CascadiaPackage_0.0.1.0_x64_Debug.msix /d ..\Loose\;Add-AppxPackage -Path ..\loose\AppxManifest.xml -Register -ForceUpdateFromAnyVersion -ForceApplicationShutdown从 VS 构建包时会直接生成松散布局并注册松散清单,跳过 msix 环节,因此 VS 的内循环实际上比上面这套命令行方式快得多。
2022 年的改进:bx + DeployAppRecipe
以下命令可以构建 Terminal 包并直接部署:
pushd %OPENCON%\src\cascadia\CascadiaPackage bx "C:\Program Files\Microsoft Visual Studio\2022\Preview\Common7\IDE\DeployAppRecipe.exe" bin\%ARCH%\%_LAST_BUILD_CONF%\CascadiaPackage.build.appxrecipe popdbx只构建 Terminal 包,其关键作用是生成CascadiaPackage.build.appxrecipe文件;构建完成后,DeployAppRecipe.exe就能像 Visual Studio 那样以松散布局部署。需要注意的是,这种方式无法利用 Visual Studio 的 FastUpToDate 检查——cppwinrt 在确认"无需重建"之前会做大量工作,因此整个包的构建明显更慢。
八、疑难排查:DEP0700 注册失败
偶尔在 VS 中部署时会出现如下错误:
DEP0700: Registration of the app failed. [0x80073CF6] error 0x80070020: Windows cannot register the package because of an internal error or low memory.从源码仓库的经验记录看,这往往是因为OpenConsoleProxy.dll被其他 terminal 包实例锁定。在 PowerShell 中执行等效命令可以拿到更多信息:
Add-AppxPackage -register "Z:\dev\public\OpenConsole\src\cascadia\CascadiaPackage\bin\x64\Debug\AppX\AppxManifest.xml"输出中会提示类似NOTE: For additional information, look for [ActivityId] dbf551f1-83d0-0007-43e7-9cded083da01 in the Event Log or use the command line Get-AppPackageLog -ActivityID ...,照做即可:
Get-AppPackageLog -ActivityID dbf551f1-83d0-0007-43e7-9cded083da01日志会给出大量信息。典型的根因是平台无法删除打包 COM 条目,关键行形如:
AppX Deployment operation failed with error 0x0 from API Logging data because access was denied for file: C:\ProgramData\Microsoft\Windows\AppRepository\Packages\WindowsTerminalDev_0.0.1.0_x64__8wekyb3d8bbwe, user SID: S-1-5-18拿到该路径后执行:
sudo start C:\ProgramData\Microsoft\Windows\AppRepository\Packages\WindowsTerminalDev_0.0.1.0_x64__8wekyb3d8bbwe(用sudo,因为该路径受权限锁定。)进入其中的PackagedCom文件夹,用 File Locksmith(或 Process Explorer)检查OpenConsoleProxy.dll的占用情况,并且建议直接以管理员身份重新启动它——这会列出几个挂着的 terminal 进程,把它们全部结束。之后即可正常部署。
九、小结:推荐的完整构建闭环
综合本文与 tools/README.md,一个完整的开发验证闭环是:
bcz:清理并构建解决方案;opencon:启动刚构建出的 console(继承 razzle 环境变量,宏脚本可直接用);testcon(在新窗口中):跑全部测试;runformat:格式化代码。
全部通过后,你的改动就具备了提 PR 的条件。整条链路的底层支撑是:tools/OpenConsole.psm1(PowerShell 侧)、tools/razzle.cmd+bcz/bx/run*.cmd(CMD 侧)、tools/tests.xml(测试清单)与dep/nuget/packages.config+src/common.nugetversions.props/.targets(依赖版本管理),阅读这些文件可以快速定位任何构建问题的来源。
【免费下载链接】terminalThe new Windows Terminal and the original Windows console host, all in the same place!项目地址: https://gitcode.com/GitHub_Trending/term/terminal
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考