☰
Cocos Creator 到 OpenHarmony 自动化构建全流程实践
2026/10/21 5:46:47 网站建设 项目流程

1. 为什么非要搞自动化构建

先说说我这边的情况。手上有几个 Cocos Creator 项目要适配 OpenHarmony 生态,早期真机调试阶段还好说,手动走一遍构建流程勉强能忍。可一旦进入版本迭代阶段,痛点就全暴露了:每天要出好几个测试包,每出一包就得在编辑器里手动点构建、等引擎打包、拷贝产物、开 DevEco Studio 导一遍工程、再点一次构建出 HAP,运气好一套流程下来十几分钟,运气不好中间哪一步忘记配置或者路径搞错,返工半小时起步,整个人都是麻的。

更难受的是人肉流程的不可复现性。今天这个成员在本地构建出的包和明天那个成员构建出的包,很可能因为本地环境差异、引擎版本不一致、资源没提交干净而对不上号。测试同学报一个 bug,你说“我本地是好的”,这种话在版本交付语境下已经没有说服力了。再加上团队里有人习惯性改构建选项不同步,最后产出的包到底用的是哪套参数,根本说不清楚。

自动化构建要解决的从来不只是“省时间”这么简单,三个核心诉求值得提前拎清楚。

第一是可重复性。同样的代码、同样的参数,在任何一台干净机器上跑出来的包应该一致,至少逻辑行为一致。第二是可追溯性。每个包能关联到代码版本、构建时间、构建机、构建参数,出问题能往回查。第三是并行与解放人力。构建任务挂在流水线上,人不用守着,构建期间去做别的事,出包后自动通知。

明白了这三点,再去看整个链路的设计就会清晰很多。从 Cocos Creator 到 OpenHarmony 的自动化构建,其实可以拆成两个大阶段:引擎侧构建和系统侧打包。引擎侧构建负责把游戏资源和代码编译成 OpenHarmony 原生工程;系统侧打包负责把原生工程用 OpenHarmony 的工具链编译成 HAP 应用包。这两个阶段的技术栈完全不同,但自动化流程要把它们无缝串起来。

开头先把结论摆在这:这套流程做下来,最直观的变化是出包从“半天手工活”变成“一条流水线命令”,而且谁去操作结果都一样。接下来我把每个环节的设计思路和执行细节完整拆开讲,目录约定、参数选择、脚本怎么写、坑在哪,都盘一遍。

2. 整体方案选型与流程设计

2.1 工具链选型:不用过度迷信 CI 平台

聊到自动化构建,很多人第一反应就是上 Jenkins、GitLab CI 或者云构建平台。我的经验是:先想清楚产物有多大、构建机放在哪、触发频率多高,再选工具,别上来就搭一套重的。

Cocos Creator 构建引擎 + 生成 OpenHarmony 工程的这个过程,实际上是非常吃 CPU 和内存的,单次构建时间可能是 5-15 分钟,产物从几百 MB 到 1GB 都有可能。这种体量放在共享型 CI 上不是不行,但排队时间、网络上传下载时间都会变成新的瓶颈。如果团队项目还处在一天好几个内部测试包的阶段,我更建议先在一台固定构建机上把自动化跑通,用本地脚本加任务调度来解决,后面真有扩大规模再上完善的 CI 服务。

我这里选了一条“轻量但完整”的路:一台常驻构建机 + 定时触发 + 脚本化流水线,后续如果要用 Jenkins 等平台,其实也只是把脚本挂到平台上去,核心逻辑完全不需要重写。这样既避免了前期过度搭建,也为后面演进留了口子。

工具链具体涉及三块:

  • 引擎命令行构建:Cocos Creator 从 3.x 开始支持命令行构建,通过指定项目路径和构建参数直接出包,不需要打开编辑器界面。
  • 系统工具链构建:OpenHarmony 工程使用 hvigor 构建,实际上是通过工程自带的hvigorw包装脚本调用的,和 Gradle 的 wrapper 机制类似。
  • 任务编排与产物管理:用 shell 脚本和 Python 脚本把上述两个步骤串起来,加上版本号管理、产物归档、通知逻辑。

2.2 目录约定与环境约定

自动化构建的第一个基础工作,是把目录和环境约定下来。没有约定的情况下,脚本写得再漂亮也会在执行时四处碰壁。

我这里采用的是经典的三级工作目录结构:

/workspace/ engine/ # Cocos Creator 安装目录 projects/ # 存放各个游戏项目源码 project-x/ # 某个项目的独立工作目录 build-tools/ # 自动化脚本集合 artifacts/ # 构建产物统一归档目录

项目源码、构建脚本、产物归档分离,这样有几个直接的好处:脚本里的路径稳定,不会因为项目位置变动而失效;工程和产物分开,避免把构建中间产物误提交到代码仓库;不同项目共享同一套脚本逻辑,只是传参数不一样。

环境方面,最容易踩的坑是 Cocos Creator 需要图形环境的依赖,在纯命令行模式下其实是通过模拟窗口来执行的,某些 Linux 服务器如果没有装对应的显示依赖库会直接崩溃。建议在构建机上保留一个最小图形环境或者虚拟显示方案,具体我在后面问题排查章节会展开。

2.3 流程全景:从代码到 HAP 的四个环节

整个自动化链路可以概括为四个环节:代码获取 → 引擎构建 → 系统打包 → 制品归档。

代码获取环节解决“构建哪份代码”的问题,最简单的方式就是在构建机上直接从代码仓库的指定分支拉取最新代码,或者拉取指定标签。有人会问“代码本身不是已经在构建机上了吗”,其实不是,完全脱离代码库的本地构建没有版本追溯意义,所以自动化流程里必须接一个拉取动作。

引擎构建环节是核心,引擎根据项目配置输出一个完整的 OpenHarmony 原生工程目录。这个过程包含资源导入、脚本编译、纹理压缩、音频处理、引擎库裁剪等步骤。输出目录结构看起来就像一个标准的 OpenHarmony 工程,后面可以直接被 DevEco Studio 或者 hvigor 工具识别。

系统打包环节把上一步生成的原生工程编译成最终的 HAP 包,这里要处理签名、权限、资源打包等逻辑。需要注意的是,如果游戏里有原生插件或者需要修改系统侧的一些配置,这个环节通常还要加一层工程定制。

制品归档环节把 HAP 包、构建日志、构建参数、版本信息统一存放,并打上标签。这一步看着简单,却是整个链路可追溯性的保障。

3. Cocos Creator 命令行构建的细节

3.1 命令行参数与构建配置

Cocos Creator 3.x 的命令行构建入口是安装目录下的可执行文件。在 macOS 上是CocosCreator.app/Contents/MacOS/CocosCreator,Windows 上就是CocosCreator.exe。配合--project和--build两个参数就能触发无界面构建。

一个典型的最小命令长这样:

CocosCreator \ --project /workspace/projects/project-x \ --build "platform=openharmony;debug=true;md5Cache=false"

重点说下--build参数,它接受的是一串分号分隔的键值对。实际项目里我基本不会只传这几个参数,因为大量构建选项是写在项目里的settings/builder.json中的。命令行参数的作用更像是在这套默认配置之上做覆盖。

完整的构建参数通常会包含这么几类:

参数类别典型参数作用与说明
目标平台platform=openharmony指定构建出来的原生工程平台类型
构建模式debug=true/false是否 debug 模式,影响日志输出和优化层级
资源相关md5Cache=true/false是否开启资源的 MD5 缓存策略
包名配置packageName=com.example.demo覆盖包名,多包场景非常有用
版本配置buildVersion=1.0.1覆盖构建版本号
启动场景startScene=xxx覆盖启动场景配置
输出目录outputPath=xxx覆盖默认输出目录

这里有一个经验:构建机和本地开发机的引擎版本必须锁死。Cocos Creator 不同小版本生成的 OpenHarmony 工程结构可能会有差异,从我遇到的情况看,3.6 和 3.7 之间就存在原生工程模板的调整。所以我会在构建机上固定一个明确的小版本号,并把这个信息写进环境配置文件里;升级引擎版本是一件走流程的事,不能谁想升就升。

3.2 构建模板与自定义构建流程

Cocos Creator 构建系统支持扩展,这玩好了非常舒服。项目根目录下放一个builder扩展目录,注册到扩展机制里,就可以在构建流程的各个生命周期节点插入自定义逻辑。

我这里用到的核心场景有两个。

第一个是在构建完成后自动修正工程配置。引擎默认生成的 OpenHarmony 工程在某些场景下不会完全符合你们项目团队定制的规范,比如模块名要统一、某些系统权限要打开、依赖的原生库位置要做调整。单纯靠构建参数改不了这些,就需要在构建完成节点的回调里动手改文件。

第二个是生成版本信息文件。游戏启动后往往需要展示当前版本号和构建号,这个信息在构建前是不存在的。我在构建完成后会把构建时间、代码版本、构建机信息写到一个 JSON 文件里,拷贝进工程资源目录,游戏运行时直接读取。

示例代码大概是这样的结构:

// builder 扩展中的版本信息生成模块 export function onAfterBuild(options: BuildOptions) { const versionInfo = { codeVersion: process.env.CODE_VERSION || 'unknown', buildTime: new Date().toISOString(), buildMachine: process.env.BUILD_MACHINE || 'unknown', buildNumber: process.env.BUILD_NUMBER || '0', }; const outputPath = options.outputPath; // 写入到构建输出目录的特定位置 writeVersionFile(outputPath, versionInfo); // 在生成的原生工程中追加自定义配置 patchNativeProject(outputPath); }

搞自定义构建需要一点额外成本,但我建议自动化流程稳定跑起来之前就把它纳入,因为很多信息如果不通过构建流程自动注入,后续每次手工补都很痛苦。

3.3 构建产物结构与校验点

OpenHarmony 平台构建完成后,输出目录里有什么,心里要有数。默认情况下,输出目录下会有:

  • build/或类似命名的原生工程目录,内部像标准 OpenHarmony 工程一样包含AppScope、entry、hvigorfile.js等。
  • 资源目录,存放最终的 assets 相关文件,实际是经过了引擎处理的。
  • 一些配置文件,如project.json、构建日志。

产物校验是自动化流程里不能省的一步,不然上面环节跑到下面环节才发现问题,排查成本高得多。我习惯在引擎构建完成后做三个快速检查:

  1. 检查原生工程目录是否存在,且关键文件(如oh-package.json5、hvigorfile.js)是否齐全。
  2. 检查资源目录大小是否在合理范围,如果资源异常小,可能是资源导出环节失败或资源被误清理。
  3. 检查版本信息文件是否生成成功,读取内容确认版本号和时间正确。

这三个检查在脚本里就是几条判断语句,但能拦下大部分低级问题。

4. OpenHarmony 工程集成与打包

4.1 理解引擎生成的 OpenHarmony 工程

Cocos Creator 构建输出的 OpenHarmony 工程,本质上是引擎做了一层“适配壳”。这层壳包含了系统侧启动入口、原生生命周期管理、引擎运行库的加载逻辑,以及一个内嵌的渲染容器。游戏逻辑和渲染都在引擎的 C++ 层跑,OpenHarmony 侧主要提供窗口创建、输入事件、生命周期回调等基础能力。

工程结构上值得注意两个文件:oh-package.json5负责模块依赖,entry目录里的src/main是应用代码主目录。自动化打包的多数配置修改都集中在这几个地方。

比如模块依赖有问题,依赖了机器本地不存在的包,脚本在打包时就会失败。我见过不少团队是开着 DevEco Studio 能构建成功,但在命令行环境构建失败,一查就是 SDK 依赖路径或者包管理仓库地址没配对。

4.2 hvigor 打包与签名配置

OpenHarmony 自身提供了一套构建工具叫 hvigor,可以理解为对标 Gradle 的构建系统。工程目录下有hvigorw这个 wrapper 脚本,用它可以执行构建打包操作,不需要额外全局安装工具。

核心命令如下:

cd /workspace/artifacts/project-x/build/xxx ./hvigorw assembleHap \ --mode module \ -p product=default \ -p module=entry@default \ --no-daemon

这里assembleHap是打包 HAP 的核心任务。和 Gradle 一样,hvigor 也支持增量构建和缓存,但自动化场景下我反而会关掉守护进程模式,避免构建机上有残留进程持有文件锁,导致下一个构建任务异常。

签名是打包阶段绕不开的坎。OpenHarmony 应用签名的自动化处理,需要在构建机上准备一套签名配置,包含证书文件、Profile 文件等。在自动化流水线里,我会维护一个固定的签名配置文件,并保证只有构建机可以访问这些敏感文件。

调试包和发布包用两套签名,这个边界一定要划清楚。自动化脚本里不要把签名配置写死在命令里,建议通过环境变量引用,方便在不同构建场景之间切换。

4.3 应用图标、名称与权限的自动替换

引擎构建出来的工程,默认使用的图标和应用名往往就是项目模板里的占位内容。自动化流程中根据不同环境打不同品牌的包,是再常见不过的需求了。

应用图标路径一般位于entry/src/main/resources/base/media/,应用名称在string.json里,模块配置在module.json5里。脚本干的事本质上就是:在打包前把这些文件替换成目标环境对应的内容。

替换操作有个原则:先备份后替换,打包完恢复。否则下一次引擎重新构建生成新的工程目录时,前一次的替换痕迹全会丢失。这个“每走一遍流程都是全新起点”的特性,恰好是自动化构建最舒服的地方——只要脚本是幂等的,重复跑多次结果一致。

权限配置也要注意,module.json5里声明的权限直接决定应用在系统上能使用哪些能力。如果游戏要用到网络、蓝牙、位置等能力,需要确保这些权限在构建流程里被正确加入。用脚本在构建后自动注入权限列表,比靠人工记得在 IDE 里勾选要可靠得多。

5. 自动化流水线脚本落地

5.1 一个可用的流水线脚本结构

流水线脚本是整个方案的中枢,负责把引擎构建和系统打包串起来,同时处理版本号、产物归档、异常处理。我建议主体用 Bash 写调用流程,部分复杂逻辑用 Python 处理,不要拘泥于“用语言好不好看”,重点是逻辑清楚、依赖少。

一个实际在用的脚本结构大概是这样的:

#!/bin/bash # 构建脚本入口:build_openHarmony.sh set -euo pipefail # 0. 读取环境变量与参数 PROJECT_NAME="${1:?需要传项目名}" BUILD_MODE="${BUILD_MODE:-debug}" CODE_VERSION="${CODE_VERSION:-unknown}" BUILD_NUMBER="${BUILD_NUMBER:-$(date +%Y%m%d%H%M)}" # 1. 代码获取 pull_code "$PROJECT_NAME" "$CODE_VERSION" # 2. 引擎构建 cocos_build "$PROJECT_NAME" "$BUILD_MODE" # 3. 产物自检 verify_native_project "$PROJECT_NAME" # 4. 系统打包 hvigor_build "$PROJECT_NAME" # 5. 产物归档 archive_artifacts "$PROJECT_NAME" "$CODE_VERSION" "$BUILD_NUMBER"

set -euo pipefail这行很关键。-e表示任何一条命令失败就立即退出,-u表示使用未定义变量时报错,-o pipefail让管道中任何一个环节失败都算整体失败。没有这几行,脚本很容易出现“上面失败下面还在跑”的诡异状态。

在包一层,下面是一个简化版的引擎构建函数示例,方便理解参数传递逻辑:

cocos_build() { local project="$1" local mode="$2" local project_path="/workspace/projects/$project" local build_args="platform=openharmony;debug=$mode;md5Cache=true" echo ">>> [1/3] 开始引擎构建: $project ($mode)" /workspace/engine/CocosCreator \ --project "$project_path" \ --build "$build_args" \ > "/tmp/build-engine-$project.log" 2>&1 echo ">>> [1/3] 引擎构建完成" }

日志重定向务必做,不然构建日志和脚本输出混在一起,后期排查会非常痛苦。每条命令的日志单独存一个文件,按构建时间和项目名命名。

5.2 版本号管理:别让版本成为混乱之源

版本号管理是整个流水线里最容易被低估的环节。自动化构建跑起来之后,一天出好几个包,如果版本号没有明确的生成规则,测试同学把几个包摆在一起都不知道装的是哪个版本。

我采用的方案是三段式版本号 + 构建序列号:版本号.构建序号。版本号来自代码仓库的标签或者业务方指定的版本,构建序号是流水线自己累加的。比如1.2.0.202506121530,一眼就能看出主版本是 1.2.0,构建时间是 2025 年 6 月 12 日 15:30。

版本号的传递链路要打通:代码仓库标签 → 流水线环境变量 → 引擎构建参数 → 生成文件写入 → HAP 包内信息。每一环都把同一个版本号透传下去,不要在中间环节重新生成一个号。这样从最终安装包反查代码版本,整条链是闭合的。

5.3 制品归档与通知闭环

构建完成后的产物归档,格式上我建议固定成:

artifacts/ project-x/ 1.2.0.202506121530/ app-release.hap build-engine.log build-hvigor.log version-info.json

目录名用版本号,内部放 HAP 包和日志,附带一个 JSON 描述这个包的所有构建信息。做到这一步,整个链路的可追溯性就落地了。

通知环节不要搞太复杂。构建成功把产物路径和包发到工作群里,构建失败把错误日志末尾的关键几行贴出来,让团队知道失败在哪一步。整条自动化流程跑通后,最常见的失败场景基本集中在代码拉取失败、签名过期、SDK不匹配三类,通知里直接把错误类型带上,运维成本很低。

6. 常见问题与排查经验实录

6.1 失败场景速查表

自动化流程跑得久了,一些问题会反复出现,这里把典型的几类整理成表,给后面做自动化构建的团队一个参照:

现象可能原因处理方式
引擎命令行启动即崩溃构建机缺少图形环境依赖安装最小图形依赖,或使用虚拟显示方式启动
构建产物没有生成原生工程平台参数错误或项目配置中未添加 OpenHarmony 平台检查platform=openharmony参数和项目构建面板配置
hvigor 提示找不到 SDK 或依赖环境变量未配置正确的 OpenHarmony SDK 路径检查HOS_SDK_HOME等环境变量,锁定统一版本
打包时签名校验失败证书或 Profile 过期证书管理加入日历提醒,自动化流程中增加证书有效期预检
HAP 包体异常大或异常小资源压缩配置差异或资源导出不完整检查压缩纹理参数和资源目录完整性
增量构建产物异常上一次构建的缓存影响了本次关键打包环节始终用全新输出目录或清理缓存
中文路径导致工具找不到文件构建机系统编码或路径兼容问题所有工程和产出目录一律使用纯英文小写路径

6.2 我踩过的几个印象深刻的坑

第一个坑是引擎版本漂移。团队里有人为了试新功能在本地升级了引擎版本,在本地构建没问题,但构建机还是旧版本,两者生成的 OpenHarmony 工程结构有细微差异。第一次没留意,结果测试包在部分设备上出现启动白屏,排查了很久才发现是引擎产物差异导致原生工程配置对不上。后来我在项目级配置文件里锁死引擎版本,并且把版本号塞进构建产物文件名,任何一次版本变动都会在产物层面暴露出来。

第二个坑是hvigor 守护进程抢占文件锁。某次构建机上一共有三个构建任务排队,前一个任务的 hvigor 守护进程没有退出,后面任务启动时拿到的是被锁的文件,直接报错。加上--no-daemon参数之后,这类问题彻底消失。自动化场景下,别开守护进程,别用默认缓存,虽然损失了一点增量构建速度,但换来的是重复执行的确定性。

第三个坑是关于资源 MD5 策略的。某项目开了资源 MD5 缓存后,每次构建都会生成大量带哈希名的资源文件,导致包体持续膨胀,而且 HAP 包内的资源路径与工程内的引用路径偶尔不一致。后来定位到是引擎配置与游戏代码里动态加载资源的逻辑没有匹配,统一策略之后恢复正常。这类问题排查起来非常隐蔽,建议自动化流程跑通后把资源策略固定下来,不轻易改动。

6.3 调试与日志分析思路

自动化构建出问题,第一件事不是看业务代码,而是定位是在引擎构建阶段挂的,还是在系统打包阶段挂的。分阶段定位是最高效的思路。

引擎构建阶段失败,先看命令行输出的尾部日志,绝大多数错误会有明确的关键词,比如 “ERROR”“FAILED”“Exception”。更复杂的情况需要看引擎的日志目录,里面会有更完整的堆栈信息。构建机上保留构建日志历史目录,按时间归档,这样出问题时能翻出历史日志做对比,看是什么变更引入了问题。

系统打包阶段失败,重点看 hvigor 输出的任务执行顺序,通常失败前最后成功完成的任务就是问题所在。比如在执行assembleHap之前会执行资源编译、校验等任务,失败任务前面的那个任务往往是排查起点。

日志分析不追求一次看懂所有内容,抓住几个关键动作:错误码、失败任务名、相关文件路径、上下文提示,这四个信息足够定位绝大多数问题。真遇到看不懂的日志,把这一段截出来去工程文档或者搜索,比对着整页日志瞎猜有效得多。

写在最后,说几句掏心窝的话

自动化构建这套东西做完之后,我最大的感触是:它带来的价值不是“快”,而是“稳”和“可查”。以前手工出包,功能明明没变,这个包就是比那个包多出一些莫名的问题,谁也解释不清。现在每一个包都对应着一套明确的参数、一段清晰的日志、一个可回溯的版本,团队里沟通成本极大降低。

如果你们团队正打算做这件事,我的建议是不要一上来就追求“完全体”。先把 Cocos Creator 命令行构建跑通,再手动把产物拖进打包工具出一遍 HAP,确认人工串联没问题,再写脚本把中间环节替代掉。每一步都要保证“自动化跑出来的结果和手工操作结果一致”,差异点要当场弄清楚,不要带着疑问往下走。链路短了,试错成本低,发现问题也快。

另外,构建机的硬件配置别太抠。Cocos Creator 构建吃 CPU 单核性能,内存不够时整个构建机都会卡死。实测下来,一台配置中上、能同时跑两个构建任务的构建机,比一堆低配机器轮流排队要省心得多。构建资源在自动化流程里就是生产力,该投入的地方别省。

最后,自动化构建不是一锤子买卖。随着引擎版本迭代、OpenHarmony 工具链更新、项目业务增长,脚本一定会需要持续维护。把脚本当作项目的一部分来管理,提交到代码仓库,走 CR 流程,出了问题能 revert。把它当成基建来对待,它会回报你远超预期的价值。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询