Flutter for OpenHarmony:cli_util 告别手写 print,用专业级日志系统构建你的 Dart 命令行工具 深度解析与鸿蒙适配指南
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net
前言
随着 Flutter 和 Dart 生态的爆发,越来越多的开发者开始使用 Dart 编写命令行工具(CLI)。从官方的 flutter 工具链,到社区的 melos、very_good_cli,Dart 因其 AOT 编译出的独立二进制文件(无需安装运行时)和极快的启动速度,已成为编写跨平台 CLI 的首选语言。
但在开发 CLI 时,我们经常面临一些底层痛点:
- SDK 哪里找? 如何准确找到当前运行环境的 Dart SDK 路径?(用于调用
dart format或dart pub)。 - 日志怎么打? 简单的
print显得太业余,如何实现像 Flutter 那样带有进度条、颜色高亮、且支持--verbose的标准日志系统? - 跨平台兼容? Windows 的
\和 macOS/Linux/OpenHarmony 的/路径分隔符差异。
cli_util 是 Dart 官方维护的基础设施库。它虽然低调,却是 pub、analyzer 等官方工具的基石。掌握它,你就能构建出专业级的 Dart 命令行应用。
一、核心原理与机制解析
1.1 SDK 探测机制 (SDK Discovery)
cli_util 的核心功能之一是 getSdkPath()。它的探测逻辑非常健壮,确保在各种奇葩环境下都能找到 SDK:
- Platform.executable: 首选检查当前运行的 Dart 可执行文件路径。
- Symlink Resolution: 如果路径是符号链接(如
/usr/local/bin/dart),它会递归解析直到找到真实的物理路径。 - Environment Variables: 检查
DART_SDK环境变量。 - PATH Scanning: 扫描系统 PATH 变量。
这种机制保证了无论用户是通过 Homebrew、APT、FVM 还是手动解压安装的 Dart,你的工具都能精准定位。
1.2 标准化日志系统 (Logger System)
cli_util 提供的 Logger 类是对 stdout/stderr 的高级封装。
- StandardLogger: 面向普通用户的模式。支持 ANSI 颜色,支持动态进度条(Spinner)。如果检测到非 TTY 环境(如 CI 流水线),它会自动降级为纯文本输出,避免打印出一堆乱码(
[2K)。 - VerboseLogger: 面向调试的模式。显示所有
trace级别的日志,且通常不显示动态进度条(直接打印每一步)。
运行命令
请求 SDK
探测
探测
找到路径
写日志
是终端?
非终端?
标准输出
开发者
Dart CLI 工具
cli_util
Platform.executable
DART_SDK 环境变量
/usr/lib/dart-sdk
Logger系统
彩色+进度条
纯文本
控制台
二、核心 API 详解
2.1 SDK 路径查找
import'package:cli_util/cli_util.dart';import'dart:io';voidmain(){// 1. 获取 SDK 根目录// 返回如: /Users/user/fvm/versions/3.0.0/bin/cache/dart-sdkString sdkPath =getSdkPath();// 2. 获取具体工具String dartBin ='$sdkPath/bin/dart';String pubBin ='$sdkPath/bin/pub';// 旧版 SDK 可能有单独的 pubprint('Using Dart from: $dartBin');// 实战:调用 SDK 内置的 format 命令Process.run(dartBin,['format','.']).then((result){print(result.stdout);});}
2.2 专业级日志输出
import'package:cli_util/cli_logging.dart';voidmain(){// 智能工厂构造:根据 verbose 标志自动选择 Logger 实现 bool isVerbose = args.contains('-v');Logger logger = isVerbose ?Logger.verbose():Logger.standard();// 1. 普通输出 logger.stdout('Starting build...');// 2. 带颜色的输出 (Ansi)// ansi 属性会自动根据终端能力开启或关闭String greenCheck = logger.ansi.emphasized('✓'); logger.stdout('$greenCheck Configuration loaded.');// 3. 动态进度条 (Indeterminate Progress)Progress progress = logger.progress('Compiling native sources');try{// 模拟耗时操作awaitFuture.delayed(Duration(seconds:2));// 任务完成 progress.finish(showTiming:true, message:'Completed');// 输出: Compiling native sources... (2.0s) Completed}catch(e){ logger.stderr('Build failed: $e');exit(1);}}
三、OpenHarmony 平台适配实战
在鸿蒙开发中,我们经常需要编写脚本来辅助开发(如自动签名 HAP、自动推送到设备)。
3.1 查找鸿蒙开发工具链 (HDC)
虽然 cli_util 主要找 Dart SDK,但我们可以借鉴其思路来查找鸿蒙的 hdc 工具。
import'dart:io';import'package:path/path.dart'as p;String?findOhosHdc(){// 1. 尝试从环境变量获取 (HDC_HOME / OHOS_SDK_HOME)var envHome =Platform.environment['HDC_HOME'];if(envHome !=null)return p.join(envHome,'hdc');// 2. 尝试从 PATH 查找var pathVar =Platform.environment['PATH']??'';var separator =Platform.isWindows ?';':':';for(var path in pathVar.split(separator)){var hdcPath = p.join(path,Platform.isWindows ?'hdc.exe':'hdc');if(File(hdcPath).existsSync())return hdcPath;}returnnull;}3.2 实战:构建 ohos_doctor 环境诊断工具
类似于 flutter doctor,我们编写一个工具来检查开发者的环境是否满足鸿蒙开发要求。
import'package:cli_util/cli_logging.dart';import'package:cli_util/cli_util.dart';import'dart:io';voidmain()async{final logger =Logger.standard();final ansi = logger.ansi; logger.stdout(ansi.emphasized('🚀 OpenHarmony Environment Doctor')); logger.stdout('');// 1. 检查 Dart SDKawait_checkItem(logger,'Dart SDK',()async{final sdk =getSdkPath();if(sdk.isEmpty)throw'Not found';return sdk;});// 2. 检查 HDC 工具await_checkItem(logger,'HDC Tool',()async{final hdc =findOhosHdc();// 使用上面的辅助函数if(hdc ==null)throw'Not found (Please set HDC_HOME)';// 验证版本final result =awaitProcess.run(hdc,['-v']);return result.stdout.toString().trim();});// 3. 检查已连接的鸿蒙设备await_checkItem(logger,'Connected Devices',()async{final hdc =findOhosHdc();if(hdc ==null)throw'Skipped';final result =awaitProcess.run(hdc,['list','targets']);final output = result.stdout.toString().trim();if(output.isEmpty || output.contains('Empty'))throw'No devices found';return'${output.split('\n').length} device(s) connected';});}Future<void>_checkItem(Logger logger,String title,Future<String>Function() check)async{final progress = logger.progress('Checking $title');try{final result =awaitcheck(); progress.finish(message: result);// 成功时显示结果}catch(e){// 失败时显式打印红叉 logger.stdout('${logger.ansi.red}✗ $title: $e${logger.ansi.none}');// 注意:progress.finish 只能打勾,若要打叉通常需手动控制输出或扩展 Logger}}
四、高级进阶:Trace 模式与 CI 集成
在 CI/CD 环境(如 GitLab Runner, Jenkins, OpenHarmony CI)中,终端通常是不可交互的(Non-Interactive)。
Logger.standard() 会自动检测 stdout.hasTerminal。
- 本地终端:输出
Checking Dart SDK... \(旋转动画),完成后变为Checking Dart SDK... (0.1s) - CI 环境:直接输出
Checking Dart SDK...,完成后输出Checking Dart SDK... (0.1s) OK。不会有退格符\b导致的日志混乱。
最佳实践:
始终建议在工具中支持 --verbose 参数,并在出错时打印 stackTrace。
try{// ... 业务逻辑 ...}catch(e, st){if(logger.isVerbose){ logger.stderr(st.toString());}exit(1);}五、总结
cli_util 虽小,却是 Dart 命令行生态的定海神针。它解决了最繁琐的 环境一致性 和 交互体验 问题。
对于 OpenHarmony 开发者,利用 cli_util 可以快速构建出跨平台(Windows 开发机/Mac 开发机)统一的自动化脚本,无论是用于编译 Hap 包,还是进行自动化测试,它都能提供稳健的支持。
回顾核心价值:
- getSdkPath(): 无论环境多乱,总能找到 Dart。
- Logger: 自动适配 CI/CD 与本地终端,开发体验满分。
- Ansi: 轻松实现红绿高亮,无需手动拼接
\u001b[31m。