.NET MAUI CLI(maui 命令)设计指南:统一 Android/iOS 环境配置、设备发现与应用检查
【免费下载链接】maui.NET MAUI is the .NET Multi-platform App UI, a framework for building native device applications spanning mobile, tablet, and desktop.项目地址: https://gitcode.com/GitHub_Trending/ma/maui
maui是 .NET MAUI 仓库设计文档(docs/design/cli.md)所规划的官方命令行工具,目标是为跨 Android、iOS、macOS、Windows、Mac Catalyst 的 MAUI 开发提供统一的设备管理与环境配置接口。本文以其设计文档为主体,结合仓库 CI 流水线中的实际使用方式(eng/pipelines/common/provision.yml),完整介绍安装调用、全部子命令、设备发现策略、IDE 集成架构与未来规划,让读者既能直接上手使用,也能理解其底层设计取舍。
背景与动机:为什么要为 MAUI 单独做一个 CLI
.NET MAUI 横跨多个平台,而各平台的原生工具链各自为政。文档以截图为例指出平台差异之痛:
- 在 iOS 上截屏需要
xcrun simctl io booted screenshot; - 在 Android 上则需要
adb exec-out screencap。
日志访问、可视树检查、设备管理同样存在各自的平台专属实现。这种碎片化对两类用户尤其不友好:
- 人类开发者:需要在记忆多套平台命令之间来回切换;
- AI Agent:源于仓库作者此前用 WPF 进行的 "vibe coding" 实验(
vibe-wpf),实验证明:只要给 AI Agent 合适的工具,它就能高效开发应用;而 MAUI 跨平台命令的碎片化,恰恰是 Agent 自动化开发的最大障碍。
mauiCLI 的定位,就是用一个统一的接口覆盖这些平台专属操作,同时保持与现有dotnet工作流(dotnet run、dotnet watch)的兼容。
设计原则
文档为 CLI 确立了五条设计原则:
- 委托给原生工具链——
maui自身不重新实现底层能力,而是封装sdkmanager、adb、xcrun simctl等现成工具; - 复用共享库——Android 侧复用
dotnet/android-tools(Xamarin.Android.Tools.AndroidSdk)做 SDK/JDK 发现,并把 JDK 安装、SDK 引导、许可接受等新能力回馈给该库; - 机器优先输出——每个命令都支持
--json结构化输出; - 无状态——每个命令"读状态、执行、退出",不维护会话;
- 补充
dotnet run——沿用dotnet run对 .NET MAUI 定义的设备标识符与框架选项,二者互补而非竞争。
五大目标
- 环境设置:用单一工具管理 Android SDK/JDK、Xcode 运行时、模拟器与仿真器;
- 截图捕获:让 AI Agent 能对运行中的 .NET MAUI 应用截图,用于验证视觉变更;
- 日志访问:统一访问各平台设备日志(logcat、Console 等);
- 可视树检查:让 Agent 能检查运行时可视树的结构与属性(.NET MAUI 可视树);
- 开发者体验:无缝融入现有
dotnetCLI 工作流,与dotnet run、dotnet watch等协同。
安装与调用方式
CLI 支持三种调用形态:
# 1. 直接调用(安装后) maui screenshot -o screenshot.png # 2. 通过 .NET CLI dotnet maui screenshot -o screenshot.png # 3. 内联安装并调用(无需预先安装) dotnet tool exec -y Microsoft.Maui.Cli screenshot -o screenshot.png安装方式则分全局与本地两种:
# 作为全局工具 dotnet tool install --global Microsoft.Maui.Cli # 作为本地工具(推荐用于项目) dotnet tool install Microsoft.Maui.Cli # 还原本地工具 dotnet tool restore工具安装后以maui命令出现在 PATH 上,本文档中所有命令均使用maui形式。
关于自动安装的规划:.NET workload 规范中的
tools-packs特性支持从 workload 自动安装工具,但该特性目前尚未实现。一旦可用,Microsoft.Maui.Cli有望在mauiworkload 安装时自动装上,省去手动安装步骤;在那之前,dotnet tool install仍是标准途径。
全局选项
所有命令都支持以下全局选项:
| 标志 | 说明 |
|---|---|
--json | 结构化 JSON 输出 |
--verbose | 详细日志 |
--interactive | 控制交互提示(默认:终端中为true;CI 环境或输出被重定向时为false) |
--dry-run | 预演动作,不真正执行 |
--platform <p> | 按平台过滤:android、ios、maccatalyst、windows |
交互检测机制与dotnetCLI 保持一致:自动识别 CI 环境变量(TF_BUILD、GITHUB_ACTIONS、CI等),并检测Console.IsOutputRedirected,据此决定是否弹出交互提示。这意味着在 CI 中不加参数也能安全地非交互执行。
环境设置命令
Android
| 命令 | 说明 |
|---|---|
maui android install | 安装 JDK + SDK + 推荐包 |
maui android install --accept-licenses | 非交互安装 |
maui android install --packages <list> | 安装指定包 |
maui android jdk check | 检查 JDK 状态 |
maui android jdk install | 安装 OpenJDK 21 |
maui android jdk list | 列出已安装的 JDK |
maui android sdk list | 列出已安装的包 |
maui android sdk list --available | 显示可安装的包 |
maui android sdk install <packages> | 安装指定包 |
maui android sdk accept-licenses | 接受所有许可 |
maui android sdk uninstall <package> | 卸载指定包 |
maui android emulator list | 列出仿真器 |
maui android emulator create <name> | 创建仿真器(自动探测系统镜像) |
maui android emulator start <name> | 启动仿真器 |
maui android emulator stop <name> | 停止仿真器 |
maui android emulator delete <name> | 删除仿真器 |
安装路径与默认值由dotnet/android-tools处理。
Apple(仅 macOS)
| 命令 | 说明 |
|---|---|
maui apple install [--accept-license] [--runtime <version>] | 可选接受 Xcode 许可并安装模拟器运行时;未来可能提示用户安装 Xcode |
maui apple check | 检查 Xcode、运行时与环境状态 |
maui apple xcode check | 检查 Xcode 安装与许可 |
maui apple xcode list | 列出 Xcode 安装 |
maui apple xcode select <path> | 切换当前 Xcode |
maui apple xcode accept-license | 接受 Xcode 许可 |
maui apple simulator list | 列出模拟器 |
maui apple simulator create <name> <type> <runtime> | 创建模拟器 |
maui apple simulator start <id> | 启动模拟器 |
maui apple simulator stop <id> | 停止模拟器 |
maui apple simulator delete <id> | 删除模拟器 |
maui apple runtime check | 检查运行时状态 |
maui apple runtime list | 列出已安装的运行时 |
maui apple runtime list --all | 列出所有运行时(已安装 + 可下载) |
maui apple runtime install <version> | 安装 iOS 运行时 |
许可标志命名差异:Android 用
accept-licenses(复数),因为sdkmanager要求接受多个 SDK 组件的许可;Apple 用accept-license(单数),因为xcodebuild -license accept接受的是统一的 Xcode 许可协议。
底层实现参考
Android 侧委托给dotnet/android-tools(Xamarin.Android.Tools.AndroidSdk):
| 功能 | 实现 |
|---|---|
| SDK 发现、引导与许可接受 | SdkManager |
| JDK 发现与安装 | JdkInstaller |
| ADB 设备管理 | AdbRunner |
| AVD / 仿真器管理 | AvdManagerRunner、EmulatorRunner |
Apple 侧直接封装原生工具链:
| 功能 | 原生工具 |
|---|---|
| 模拟器管理 | xcrun simctl(list、create、boot、shutdown、delete) |
| 运行时管理 | xcrun simctl runtime(list、add) |
| Xcode 管理 | xcode-select、xcodebuild -license |
| 设备检测 | xcrun devicectl list devices(真机)、xcrun simctl list(模拟器) |
Apple 操作通过 AppleDev.Tools 封装simctl与devicectl。
退出码约定
所有命令使用统一的退出码:
| 代码 | 含义 |
|---|---|
| 0 | 成功 |
| 1 | 一般错误 |
| 2 | 环境/配置错误 |
| 3 | 权限被拒绝(需要提权) |
| 4 | 网络错误(下载失败) |
| 5 | 资源未找到 |
仓库中的实际落地:CI 已经用起来了
这份设计并非纸上谈兵——.NET MAUI 仓库自己的 CI 流水线已经实际使用了mauiCLI。在 eng/pipelines/common/provision.yml 中:
- 环境准备阶段通过
dotnet build -t:ProvisionJdk ...、-t:ProvisionAndroidSdkCommonPackages ...等 MSBuild target 调用 CLI 安装 JDK 与 SDK,任务显示名直接标注为 "(maui cli)"; - 随后用
dotnet maui android sdk check --ci --json做非交互检查,并解析其 JSON 输出:读取status字段判断 SDK 是否可用,读取details.path得到首选 SDK 路径,进而设置ANDROID_SDK_ROOT与ANDROID_HOME两个环境变量。
这恰好印证了设计文档的两点:--json机器优先输出真的被 CI 消费;--ci之类隐式关闭交互的标志让流水线可以安全地非交互执行。同时说明 Android 侧还存在文档命令表之外的maui android sdk check检查命令(返回含status、details.path的 JSON 结构)。
设备发现:maui device list
maui device list用一条命令列出所有平台的已连接设备、运行中的仿真器和可用模拟器:
maui device list [--platform <p>] [--json]选项:
--platform <PLATFORM>:按平台过滤(android、ios、maccatalyst);省略则列出所有平台;--json:结构化 JSON 输出,供机器消费。
人类可读输出:
ID Description Type Platform Status emulator-5554 Pixel 7 - API 35 Emulator android Online 0A041FDD400327 Pixel 7 Pro Device android Online 94E71AE5-8040-4DB2-8A9C-6CD24EF4E7DE iPhone 16 - iOS 26.0 Simulator ios Shutdown FBF5DCE8-EE2B-4215-8118-3A2190DE1AD7 iPhone 14 - iOS 26.0 Simulator ios Booted AF40CC64-2CDB-5F16-9651-86BCDF380881 My iPhone 15 Device ios PairedJSON 输出(--json):
{ "devices": [ { "id": "emulator-5554", "description": "Pixel 7 - API 35", "type": "Emulator", "platform": "android", "status": "Online" }, { "id": "FBF5DCE8-EE2B-4215-8118-3A2190DE1AD7", "description": "iPhone 14 - iOS 26.0", "type": "Simulator", "platform": "ios", "status": "Booted" } ] }关键兼容性设计:输出中的id字段与dotnet run --device <id>接受的标识符完全一致,因此maui device list的结果可以直接管道给运行命令使用。
设备枚举的两种途径
途径 A:通过dotnet run --list-devices(基于项目)
.NET SDK(≥ .NET 11)提供dotnet run --list-devices,它会调用各平台 workload 定义的ComputeAvailableDevicesMSBuild target:
- Android:调用
adb devices,返回序列号、描述、类型(Device/Emulator)、状态、型号; - Apple:调用
simctl list与devicectl list,返回 UDID、描述、类型(Device/Simulator)、OS 版本、RuntimeIdentifier。
该途径必须存在项目文件——MSBuild 需要评估.csproj才能定位正确的 workload targets;同时它是按框架工作的:先选一个目标框架,再获取该平台下的设备。
途径 B:直接调用原生工具(无需项目)
mauiCLI 直接调用adb devices、xcrun simctl list devices、xcrun devicectl list devices,不评估任何 MSBuild 项目,一次调用即可拿到统一、跨平台的设备列表。
对比
| 维度 | 途径 A(MSBuild) | 途径 B(原生 CLI) |
|---|---|---|
| 需要项目 | 是——需要.csproj | 否 |
| 跨平台 | 每次调用一个平台(按 TFM) | 一次调用覆盖所有平台 |
| 元数据 | 丰富(RuntimeIdentifier、workload 专属字段) | 标准(id、description、type、status) |
| 速度 | 较慢(MSBuild 评估 + restore) | 快(<2s,直接进程调用) |
| ID 兼容性 | dotnet run --device的权威来源 | 相同的原生 ID——兼容 |
| 需要 workload | 是(必须安装平台 workload) | 否(只需原生工具adb、simctl) |
| 可扩展性 | workload 自动添加新设备类型 | 每平台需单独添加支持 |
没有项目时的真实场景
以下工作流在项目创建前或项目上下文之外就需要设备枚举:
- AI Agent 引导——Agent 启动 "vibe coding" 会话时,要在脚手架项目之前先发现可用目标,此时还没有
.csproj,无法调用dotnet run --list-devices; - IDE 启动——VS Code 打开的工作区尚未加载 MAUI 项目,扩展需要填充设备选择器向用户展示可用设备,无项目查询是唯一选择;
- 环境验证——开发者在任意目录运行
maui device list回答"能不能看到我的手机",这是诊断步骤而非构建步骤; - CI 流水线设置——CI 脚本在调用
dotnet run之前检查预期仿真器/模拟器是否在运行,检查不应依赖具体项目文件; - 多项目解决方案——解决方案同时含 Android 与 iOS 项目,开发者想要一个统一的设备列表,而不是逐项目跑
--list-devices; - 跨平台总览——
dotnet run --list-devices一次只显示一个 TFM 的设备,而同时切 Android/iOS 的开发者想一眼看全。
推荐方案
maui device list以途径 B(直接调用原生工具)为主实现,理由:
- 随处可用——不需要项目、workload targets,也没有 MSBuild 评估开销;
- 设备标识符与
ComputeAvailableDevices产出的原生 ID 相同,与dotnet run --device完全兼容; mauiCLI 本来就要为其他命令(环境设置、仿真器管理)封装这些原生工具,设备列表是自然延伸。
而当项目可用且需要框架级设备过滤时,dotnet run --list-devices仍是正确工具——它提供更丰富的元数据(RuntimeIdentifier)且受益于 workload 专属逻辑。两者互补:
maui device list → "这台机器上存在哪些设备?" dotnet run --list-devices → "哪些设备能运行这个项目?"各平台枚举实现:
| 平台 | 原生工具 | 枚举内容 |
|---|---|---|
| Android | adb devices -l | 真机与运行中的仿真器 |
| iOS(模拟器) | xcrun simctl list devices --json | 所有模拟器(已启动 + 已关机) |
| iOS(真机) | xcrun devicectl list devices | 已连接的真机 |
| Mac Catalyst | (宿主机器) | Mac 本身 |
应用检查命令(规划中)
应用检查命令计划在后续版本发布;首个版本聚焦环境设置与设备管理。
设备选择选项
应用检查命令将沿用dotnet run对 .NET MAUI 约定的设备选择与框架选项:
-f|--framework <FRAMEWORK> Target framework (e.g., net10.0-android, net10.0-ios) -d|--device <DEVICE_ID> Target device identifier (from --list-devices) --list-devices List available devices/emulators/simulators -p|--project <PATH> Path to the .NET MAUI project (default: current directory) -h|--help Show help information --version Show version information交互提示行为:
- 未指定
-f|--framework时:- 若存在项目文件且含多个目标框架,CLI 提示选择一个;
- 若无项目文件,CLI 从已知 .NET MAUI 目标框架中提示(如
net10.0-android、net10.0-ios、net10.0-maccatalyst、net10.0-windows)。
- 已选框架但未指定
-d|--device时:- CLI 提示从该平台可用的设备/仿真器/模拟器中选择;
- 设备列表来自与
dotnet run相同的ComputeAvailableDevicesMSBuild target。
注意:-d|--device使用的设备标识符与dotnet run --list-devices返回的一致。
screenshot命令
捕获正在运行的 .NET MAUI 应用的截图。
用法:
maui screenshot [options]选项:
-o|--output <PATH>:输出文件路径(默认:screenshot_{timestamp}.png);-w|--wait <SECONDS>:捕获前等待秒数(默认:0)。
平台实现:
首个版本覆盖 Android 与 iOS/Mac Catalyst,Windows 与 macOS 支持按规划随后跟进。
- Android:使用
adb exec-out screencap -p; - iOS/Mac Catalyst:模拟器用
xcrun simctl io booted screenshot <file>;真机捕获走 Xcode 工具链(未来); - Windows(规划):用 Windows 屏幕捕获 API 捕获活动应用窗口或全屏;
- macOS(规划):用 macOS 屏幕捕获 API 或命令行工具捕获活动应用窗口或全屏。
未来命令清单
maui screenshot—— 捕获运行中应用的截图;maui logs—— 流式输出设备日志;maui tree—— 检查可视树。
与dotnet run和dotnet watch的集成
CLI 被设计为与现有 .NET 工作流无缝协同:
常规工作流示例
# 终端 1:带热重载运行应用 dotnet watch run # 终端 2:检查应用 maui screenshot --output iteration1.png maui logs --follow --filter "MyApp" # 未来 maui tree --json # 未来AI Agent 工作流
# 0. 发现可用设备(无需项目) maui device list --json # 1. 修改代码 # ... agent modifies MainPage.xaml ... # 2. 等待热重载完成 sleep 2 # 3. 捕获截图 maui screenshot -o current.png # 4. 分析可视树(未来) maui tree --json # 5. 检查日志错误(未来) maui logs --level error # 6. Agent 分析输出并决定下一步这个闭环——改代码 → 热重载 → 截图 → 分析可视树 → 查日志——正是为 AI Agent 的"感知-行动"循环设计的,也是"vibe coding"在 MAUI 上得以成立的基础设施。
平台专属考量
Android
- 设备检测:
adb devices; - 截图:
adb exec-out screencap -p或 UI Automator; - 日志:带包过滤的
adb logcat。
iOS / Mac Catalyst
- 设备检测:
xcrun simctl list devices(模拟器)、xcrun devicectl list devices(真机)——经 AppleDev.Tools 封装; - 截图:
xcrun simctl io booted screenshot <file>(模拟器),iOS 真机(未来); - 日志:
xcrun simctl spawn booted log stream或 Console.app(模拟器)、mlaunch --logdev(真机)。
安全与隐私
CLI 仅面向开发与调试场景,设计上有三重约束:
- 仅调试构建:功能应在
Release构建中通过 trimmer 特性标志或#if DEBUG条件禁用; - 复用现有基础设施:优先复用调试器、Hot Reload 等既有传输机制,不创建新的通信通道;
- 不暴露给生产环境:除使用操作系统标准能力(如截图、日志)外,CLI 不应能作用于生产应用。
IDE 集成
mauiCLI 及其底层库被设计为 IDE 扩展的共享后端,消除各工具间重复的环境检测与设置逻辑。
架构
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐ │ VS Code ext │ │ Visual Studio │ │ AI Agent │ │ (vscode-maui) │ │ extension │ │ (Copilot, etc.) │ └────────┬─────────┘ └────────┬──────────┘ └────────┬─────────┘ │ │ │ spawns CLI references NuGet spawns CLI │ library directly │ │ │ │ ▼ ▼ ▼ ┌──────────────┐ ┌────────────────────┐ ┌──────────────┐ │ maui CLI │ │ android-tools │ │ maui CLI │ │ (process) │ │ (in-process) │ │ (--json) │ └──────┬───────┘ └────────┬───────────┘ └──────┬───────┘ │ │ │ └─────────┬───────────┴───────────────────────┘ │ spawns native tools ┌───────────┼───────────┐ ▼ ▼ ▼ ┌───────────┐ ┌──────────┐ ┌──────────┐ │ adb │ │ xcrun │ │ Windows │ │ sdkmanager│ │ simctl │ │ SDK │ └───────────┘ └──────────┘ └──────────┘集成模式
| 消费方 | 集成方式 | 理由 |
|---|---|---|
| Visual Studio扩展 | 直接引用android-toolsNuGet 包(进程内) | .NET 扩展——无序列化开销,直接 API 访问 |
VS Code(vscode-maui) | 派生mauiCLI 进程,解析--jsonstdout | TypeScript 扩展——CLI 是自然的进程边界 |
| AI Agent / CI | 以--json调用mauiCLI | 基于进程、语言无关 |
| 终端(人类) | 直接调用mauiCLI | 默认人类可读输出,需要时用--json |
Visual Studio 直接消费dotnet/android-tools中的Xamarin.Android.Tools.AndroidSdkNuGet 包——与 CLI 内部用的是同一个库,既避免了进程开销,又给 VS 扩展完整的 API 访问权;非 .NET 消费方(VS Code、AI Agent、CI)则以 CLI 为规范接口。
IDE 如何使用
| 工作流 | CLI 命令 | IDE 行为 |
|---|---|---|
| 工作区打开 | maui apple check --json、maui android jdk check --json | 在状态栏 / 问题面板显示环境状态 |
| 环境修复 | maui android install --json | 显示进度条,流式处理type: "progress"消息 |
| 设备选择器 | maui device list --json | 填充设备下拉框 / 选择 UI |
| 仿真器启动 | maui android emulator start <name> --json | 显示通知,完成后更新设备列表 |
收益
- 行为一致——VS、VS Code、CLI 通过共享库使用同一套检测与设置逻辑;
- 单一维护点——
android-tools的缺陷修复自动传播到所有消费方; - AI 就绪——Agent 消费与 VS Code 相同的
--json输出; - 集成灵活——.NET 消费方进程内集成,其余走 CLI。
当前状态
| 集成 | 状态 |
|---|---|
VS Code 扩展(vscode-maui) | ✅ 进行中 |
| Visual Studio 扩展 | 规划中(vNext) |
| GitHub Copilot / AI Agent | ✅ 通过--json输出支持 |
未来目标
MCP Server 的取舍
CLI 设计过程中曾考虑提供 MCP(Model Context Protocol)服务器,但最终结论是:先做 CLI。AI Agent 可以通过copilot-instructions.md中的示例直接使用 CLI 命令,无需自定义 MCP 服务器。若 CLI 完成后确实证明 MCP 服务器有价值,届时再以薄封装形式把 CLI 操作暴露为 MCP 协议——Visual Studio 与 VS Code 扩展都提供分发 MCP 服务器的选项,大概率会通过 .NET MAUI 工具链实现。
更多子命令
环境设置命令(Android SDK/JDK、Xcode、仿真器、模拟器)灵感来自既有社区项目:
- .NET MAUI "Check" / "Doctor" 类工具(如
dotnet-maui-check、maui-cli); - Android SDK 管理工具(如
AndroidSdk.Tools)。
规划中的命令:maui logs(查看控制台输出)、maui tree(显示可视树)、maui screenshot(捕获截图)。
决策:环境设置与设备列表先发布,应用检查命令在后续版本跟进。
总结
mauiCLI 的设计体现了清晰的取舍:委托原生工具链保证能力与生态兼容,--json机器优先输出让 AI Agent 与 CI 成为一等公民,复用共享库让 Visual Studio、VS Code 与 CLI 保持行为一致且单一维护。它不以替代dotnet run为目标,而是补齐后者在"无项目场景"下的设备发现与"运行中应用"的检查能力。对本文档与设计细节感兴趣的读者,可进一步阅读仓库中的 docs/design/cli.md 原文,并在 eng/pipelines/common/provision.yml 中看到它在真实 CI 流水线中的落地用法。
【免费下载链接】maui.NET MAUI is the .NET Multi-platform App UI, a framework for building native device applications spanning mobile, tablet, and desktop.项目地址: https://gitcode.com/GitHub_Trending/ma/maui
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考