HarmonyOS开发:开发者工具链——CLI工具与自动化脚本

举报
Jack20 发表于 2026/06/28 21:18:49 2026/06/28
【摘要】 HarmonyOS开发:开发者工具链——CLI工具与自动化脚本📌 核心要点:掌握命令行工具是脱离IDE依赖、实现自动化构建的第一步,hvigorw和hdc是你最该熟悉的两个命令。 背景与动机你在DevEco Studio里点按钮构建项目、点按钮运行应用、点按钮看日志——一切都很美好。直到有一天,你要搭CI/CD流水线,发现离开IDE你什么都干不了。或者更常见的场景:你改了一行代码,IDE...

HarmonyOS开发:开发者工具链——CLI工具与自动化脚本

📌 核心要点:掌握命令行工具是脱离IDE依赖、实现自动化构建的第一步,hvigorw和hdc是你最该熟悉的两个命令。

背景与动机

你在DevEco Studio里点按钮构建项目、点按钮运行应用、点按钮看日志——一切都很美好。直到有一天,你要搭CI/CD流水线,发现离开IDE你什么都干不了。

或者更常见的场景:你改了一行代码,IDE要编译两分钟。你受不了了,想只编译改动的部分,但IDE的增量编译有时候不靠谱,全量编译又太慢。

这时候,命令行工具就派上用场了。hvigorw负责构建,hdc负责设备交互,再配合自动化脚本,你可以把整个开发流程搬到命令行上——更快、更可控、更适合自动化。

这篇文章,我把HarmonyOS开发中最常用的CLI工具和自动化脚本技巧讲清楚。

核心原理

工具链全景

HarmonyOS开发者工具链不是一两个命令,而是一整套体系:

graph TB
    A[HarmonyOS CLI工具链] --> B[构建工具]
    A --> C[设备管理]
    A --> D[包管理]
    A --> E[签名工具]
    A --> F[自定义CLI]

    B --> B1[hvigorw - 构建执行器]
    B --> B2[hvigor - 构建框架]

    C --> C1[hdc - 设备通信]
    C --> C2[hdc list targets]
    C --> C3[hdc install]

    D --> D1[ohpm - 包管理器]
    D --> D2[ohpm install]
    D --> D3[ohpm publish]

    E --> E1[hapsigner - 签名工具]
    E --> E2[证书管理]

    F --> F1[自定义hvigor任务]
    F --> F2[Shell/Python脚本]

    classDef root fill:#E91E63,stroke:#880E4F,color:#fff
    classDef cat fill:#2196F3,stroke:#1565C0,color:#fff
    classDef tool fill:#4CAF50,stroke:#2E7D32,color:#fff

    class A root
    class B,C,D,E,F cat
    class B1,B2,C1,C2,C3,D1,D2,D3,E1,E2,F1,F2 tool

hvigorw vs hvigor

这俩名字容易搞混:

  • hvigor:构建框架,类似Gradle。定义任务、管理依赖、配置构建流程。
  • hvigorw:构建执行器的包装脚本,类似gradlew。它会自动下载正确版本的hvigor,然后执行构建任务。

你平时用的就是hvigorw,不用关心hvigor的版本管理。

hdc是什么?

hdc(HarmonyOS Device Connector)是HarmonyOS的设备通信工具,对标Android的adb。通过hdc,你可以在命令行里安装应用、推送文件、查看日志、执行shell命令。

hdc命令 作用 对应adb命令
hdc list targets 列出已连接设备 adb devices
hdc install xxx.hap 安装应用 adb install xxx.apk
hdc hilog 查看日志 adb logcat
hdc file send 推送文件 adb push
hdc shell 进入设备shell adb shell

代码实战

基础用法:hvigorw常用构建命令

// hvigorw常用命令速查
// 
// === 构建相关 ===
// 
// 构建HAP(默认模块)
// hvigorw assembleHap
//
// 构建HSP动态共享库
// hvigorw assembleHsp
//
// 构建HAR静态共享库
// hvigorw assembleHar
//
// 构建APP包(用于发布)
// hvigorw assembleApp
//
// 清理构建产物
// hvigorw clean
//
// === 模块指定 ===
//
// 只构建指定模块
// hvigorw --module entry assembleHap
//
// 指定产品配置(多目标构建)
// hvigorw --product-name default assembleHap
//
// === 信息查看 ===
//
// 查看所有可用任务
// hvigorw --list
//
// 查看项目依赖树
// hvigorw --dependencies
//
// === 性能相关 ===
//
// 开启并行构建
// hvigorw --parallel assembleHap
//
// 开启构建缓存
// hvigorw --no-build-cache assembleHap  // 反向:禁用缓存
//
// 详细日志
// hvigorw --verbose assembleHap

进阶用法:hdc设备管理自动化

hdc的命令很多,但日常用的就那几个。关键是把它们串起来,形成自动化脚本。

// DeviceManager.ets - hdc设备管理自动化脚本
// 用ArkTS写的自动化工具,可以在Node.js环境运行

import { exec } from 'child_process';
import { promisify } from 'util';

const execAsync = promisify(exec);

// 设备信息接口
interface DeviceInfo {
  id: string;
  model: string;
  sdkVersion: string;
  status: 'online' | 'offline';
}

// 应用信息接口
interface AppInfo {
  bundleName: string;
  versionCode: number;
  versionName: string;
}

export class DeviceManager {
  // 列出所有已连接设备
  async listDevices(): Promise<DeviceInfo[]> {
    try {
      const { stdout } = await execAsync('hdc list targets');
      const lines = stdout.trim().split('\n').filter(l => l.length > 0);
      
      const devices: DeviceInfo[] = [];
      for (const line of lines) {
        // 解析设备信息
        const id = line.split('\t')[0].trim();
        if (id && !id.includes('[Empty]')) {
          const model = await this.getDeviceModel(id);
          const sdkVersion = await this.getDeviceSdkVersion(id);
          devices.push({
            id,
            model,
            sdkVersion,
            status: 'online',
          });
        }
      }
      return devices;
    } catch (err) {
      console.error(`获取设备列表失败: ${err}`);
      return [];
    }
  }

  // 获取设备型号
  private async getDeviceModel(deviceId: string): Promise<string> {
    try {
      const { stdout } = await execAsync(
        `hdc -t ${deviceId} shell param get const.product.model`
      );
      return stdout.trim();
    } catch {
      return 'Unknown';
    }
  }

  // 获取设备SDK版本
  private async getDeviceSdkVersion(deviceId: string): Promise<string> {
    try {
      const { stdout } = await execAsync(
        `hdc -t ${deviceId} shell param get const.ohos.apiversion`
      );
      return stdout.trim();
    } catch {
      return 'Unknown';
    }
  }

  // 安装HAP到指定设备
  async installApp(deviceId: string, hapPath: string): Promise<boolean> {
    try {
      console.info(`正在安装 ${hapPath} 到设备 ${deviceId}...`);
      const { stdout } = await execAsync(
        `hdc -t ${deviceId} install -r ${hapPath}`,
        { timeout: 60000 }  // 安装超时60秒
      );
      
      if (stdout.includes('AppMod finish') || stdout.includes('msg:install success')) {
        console.info('安装成功!');
        return true;
      }
      console.error(`安装失败: ${stdout}`);
      return false;
    } catch (err) {
      console.error(`安装异常: ${err}`);
      return false;
    }
  }

  // 启动应用
  async launchApp(deviceId: string, bundleName: string, abilityName: string): Promise<boolean> {
    try {
      const { stdout } = await execAsync(
        `hdc -t ${deviceId} shell aa start -a ${abilityName} -b ${bundleName}`
      );
      return stdout.includes('start ability successfully');
    } catch (err) {
      console.error(`启动应用失败: ${err}`);
      return false;
    }
  }

  // 获取应用日志(实时)
  async streamLogs(deviceId: string, tag?: string): Promise<void> {
    const cmd = tag 
      ? `hdc -t ${deviceId} hilog | grep "${tag}"`
      : `hdc -t ${deviceId} hilog`;
    
    console.info(`开始监听设备 ${deviceId} 的日志...`);
    const child = require('child_process').spawn('hdc', [
      '-t', deviceId, 'hilog'
    ]);

    child.stdout.on('data', (data: Buffer) => {
      const lines = data.toString().split('\n');
      for (const line of lines) {
        if (!tag || line.includes(tag)) {
          console.log(line);
        }
      }
    });

    child.stderr.on('data', (data: Buffer) => {
      console.error(`日志错误: ${data}`);
    });
  }

  // 卸载应用
  async uninstallApp(deviceId: string, bundleName: string): Promise<boolean> {
    try {
      const { stdout } = await execAsync(
        `hdc -t ${deviceId} uninstall ${bundleName}`
      );
      return stdout.includes('msg:uninstall success');
    } catch (err) {
      console.error(`卸载失败: ${err}`);
      return false;
    }
  }

  // 推送文件到设备
  async pushFile(deviceId: string, localPath: string, remotePath: string): Promise<boolean> {
    try {
      await execAsync(
        `hdc -t ${deviceId} file send "${localPath}" "${remotePath}"`
      );
      console.info(`文件推送成功: ${localPath} -> ${remotePath}`);
      return true;
    } catch (err) {
      console.error(`文件推送失败: ${err}`);
      return false;
    }
  }
}

完整示例:CI/CD自动化构建脚本

把hvigorw和hdc串起来,写一个完整的CI/CD构建部署脚本。

// cicd-pipeline.ets - CI/CD自动化流水线
import { DeviceManager } from './DeviceManager';
import { exec } from 'child_process';
import { promisify } from 'util';
import * as fs from 'fs';
import * as path from 'path';

const execAsync = promisify(exec);

// 构建配置
interface BuildConfig {
  projectDir: string;       // 项目目录
  moduleName: string;       // 模块名
  productName: string;      // 产品名
  outputDir: string;        // 输出目录
  signConfig?: {            // 签名配置
    certPath: string;
    profilePath: string;
    keystorePath: string;
    keystorePwd: string;
  };
}

// 流水线阶段结果
interface StageResult {
  stage: string;
  success: boolean;
  duration: number;  // 毫秒
  message: string;
}

export class CICDPipeline {
  private config: BuildConfig;
  private results: StageResult[] = [];
  private deviceManager: DeviceManager;

  constructor(config: BuildConfig) {
    this.config = config;
    this.deviceManager = new DeviceManager();
  }

  // 执行完整流水线
  async run(): Promise<boolean> {
    console.info('========================================');
    console.info('  HarmonyOS CI/CD Pipeline');
    console.info('========================================\n');

    // 阶段1:代码检查
    const lintResult = await this.runStage('代码检查', async () => {
      await execAsync('hvigorw --module entry lint', {
        cwd: this.config.projectDir,
      });
    });
    if (!lintResult) return false;

    // 阶段2:单元测试
    const testResult = await this.runStage('单元测试', async () => {
      await execAsync('hvigorw --module entry test@unittest', {
        cwd: this.config.projectDir,
        timeout: 300000,  // 测试超时5分钟
      });
    });
    if (!testResult) return false;

    // 阶段3:构建HAP
    const buildResult = await this.runStage('构建HAP', async () => {
      const cmd = `hvigorw --module ${this.config.moduleName} --product-name ${this.config.productName} assembleHap`;
      await execAsync(cmd, {
        cwd: this.config.projectDir,
        timeout: 600000,  // 构建超时10分钟
      });
    });
    if (!buildResult) return false;

    // 阶段4:签名
    if (this.config.signConfig) {
      const signResult = await this.runStage('应用签名', async () => {
        await this.signHap();
      });
      if (!signResult) return false;
    }

    // 阶段5:部署到设备
    const deployResult = await this.runStage('设备部署', async () => {
      await this.deployToDevice();
    });
    if (!deployResult) return false;

    // 输出报告
    this.printReport();
    return true;
  }

  // 签名HAP
  private async signHap(): Promise<void> {
    if (!this.config.signConfig) return;

    const hapPath = this.findHapFile();
    if (!hapPath) throw new Error('找不到HAP文件');

    const { certPath, profilePath, keystorePath, keystorePwd } = this.config.signConfig;
    
    await execAsync(
      `hapsigner sign-app -keyAlias "debug" -keyPwd "${keystorePwd}" ` +
      `-keystoreFile "${keystorePath}" -certFile "${certPath}" ` +
      `-profileFile "${profilePath}" -inFile "${hapPath}" -outFile "${hapPath}"`,
      { cwd: this.config.projectDir }
    );
  }

  // 部署到设备
  private async deployToDevice(): Promise<void> {
    const devices = await this.deviceManager.listDevices();
    if (devices.length === 0) {
      throw new Error('没有已连接的设备');
    }

    const hapPath = this.findHapFile();
    if (!hapPath) throw new Error('找不到HAP文件');

    // 部署到所有已连接设备
    for (const device of devices) {
      console.info(`部署到设备: ${device.model} (${device.id})`);
      const installed = await this.deviceManager.installApp(device.id, hapPath);
      if (!installed) {
        throw new Error(`设备 ${device.id} 部署失败`);
      }
    }
  }

  // 查找构建产物
  private findHapFile(): string | null {
    const buildDir = path.join(
      this.config.projectDir,
      this.config.moduleName,
      'build',
      'default',
      'outputs',
      'default'
    );
    
    if (!fs.existsSync(buildDir)) return null;
    
    const files = fs.readdirSync(buildDir).filter(f => f.endsWith('.hap'));
    return files.length > 0 ? path.join(buildDir, files[0]) : null;
  }

  // 执行单个阶段
  private async runStage(stageName: string, fn: () => Promise<void>): Promise<boolean> {
    const start = Date.now();
    console.info(`\n>>> 阶段: ${stageName}`);
    
    try {
      await fn();
      const duration = Date.now() - start;
      this.results.push({
        stage: stageName,
        success: true,
        duration,
        message: '成功',
      });
      console.info(`${stageName} 完成 (${(duration / 1000).toFixed(1)}s)`);
      return true;
    } catch (err) {
      const duration = Date.now() - start;
      this.results.push({
        stage: stageName,
        success: false,
        duration,
        message: (err as Error).message,
      });
      console.error(`${stageName} 失败: ${(err as Error).message}`);
      return false;
    }
  }

  // 输出构建报告
  private printReport(): void {
    console.info('\n========================================');
    console.info('  构建报告');
    console.info('========================================');
    
    let totalDuration = 0;
    let allSuccess = true;
    
    for (const r of this.results) {
      const icon = r.success ? '✅' : '❌';
      console.info(`${icon} ${r.stage}: ${r.message} (${(r.duration / 1000).toFixed(1)}s)`);
      totalDuration += r.duration;
      if (!r.success) allSuccess = false;
    }
    
    console.info(`\n总耗时: ${(totalDuration / 1000).toFixed(1)}s`);
    console.info(`结果: ${allSuccess ? '🎉 全部通过' : '⚠️ 存在失败阶段'}`);
  }
}

// 使用示例
async function main() {
  const pipeline = new CICDPipeline({
    projectDir: '/path/to/your/project',
    moduleName: 'entry',
    productName: 'default',
    outputDir: './dist',
    signConfig: {
      certPath: './certs/cert.cer',
      profilePath: './certs/profile.p7b',
      keystorePath: './certs/debug.p12',
      keystorePwd: 'your-password',
    },
  });

  const success = await pipeline.run();
  process.exit(success ? 0 : 1);
}

踩坑与注意事项

坑1:hdc连接不上设备

你插上设备,hdc list targets返回空列表。大概率是驱动没装或者USB调试没开。

解法

  1. 确认设备上开启了开发者模式和USB调试
  2. Windows上需要安装华为USB驱动
  3. 如果是无线连接,先用USB连接一次,再配置TCP模式:
# USB连接后切换到TCP模式
hdc tconn 192.168.1.100:5555

坑2:hvigorw构建报内存不足

大型项目编译时,hvigorw可能报OutOfMemoryError

解法:在hvigor-config.json5里增加JVM内存:

{
  "modelVersion": "5.0.0",
  "dependencies": {
  },
  "execution": {
    "daemon": true,
    "parallel": true,
    "incremental": true,
    "jvmOptions": "-Xmx4g"  // 增加到4GB
  }
}

坑3:hdc install报签名错误

你构建的HAP没签名,直接hdc install会报签名校验失败。

解法:要么用签名工具签名,要么用调试签名:

# 使用调试签名安装
hdc install -r -g your-app.hap

但注意,-g参数只在开发者模式下有效,发布版本必须正式签名。

坑4:增量编译不生效

你改了代码,但hvigorw assembleHap还是全量编译。可能是缓存被污染了。

解法:清理缓存后重新构建:

hvigorw clean
hvigorw --no-build-cache assembleHap

坑5:hdc hilog日志刷太快

设备日志每秒几百行,你根本看不过来。

解法:用过滤参数:

# 只看特定tag的日志
hdc hilog -t MyApp

# 只看ERROR级别
hdc hilog -l E

# 只看特定进程
hdc hilog -P <pid>

坑6:自定义hvigor任务不执行

你在hvigorfile.ts里定义了自定义任务,但hvigorw --list里看不到。

解法:确保任务被正确导出和注册:

// hvigorfile.ts
import { hapTasks } from '@ohos/hvigor-ohos-plugin';

export default {
  system: hapTasks,
  plugins: [{
    apply() {
      this.registerTask({
        name: 'myCustomTask',
        run: () => {
          console.log('自定义任务执行了!');
        }
      });
    }
  }]
};

HarmonyOS 6适配说明

HarmonyOS 6对CLI工具链的更新:

  1. hvigor 5.0:构建框架大版本升级,支持增量编译的粒度更细(文件级别→函数级别),构建速度提升约30%。

  2. hdc增强:新增hdc perf命令,可以在命令行直接启动性能分析,不需要IDE。

  3. ohpm workspaces:支持monorepo项目管理,类似npm的workspaces特性。一个仓库里有多个模块,一条命令全构建。

  4. 构建缓存云同步:hvigor 5.0支持将构建缓存推送到远程服务器,团队成员可以共享缓存,首次构建速度大幅提升。

总结

CLI工具不是IDE的替代品,而是IDE的补充。你日常开发用IDE没问题,但遇到CI/CD、批量操作、自动化场景,CLI才是正解。

维度 评价
学习难度 ⭐⭐⭐ 命令本身简单,组合使用需要经验
使用频率 ⭐⭐⭐⭐⭐ 每天都在用
重要程度 ⭐⭐⭐⭐ 自动化构建的基础

几个关键建议:

  • 先熟悉hdc:hdc是你和设备之间的桥梁,装应用、看日志、推文件,哪个离得了?
  • 构建脚本化:把常用的构建命令封装成脚本,一键执行。别每次都手动敲一长串命令。
  • 增量编译优先:开发阶段用增量编译,发布阶段用全量编译。别为了省事每次都全量。
  • 日志要过滤:hilog刷屏是常态,学会用-t-l-P过滤,不然你找不到有用的信息。
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。