OpenHarmony 编译系统:架构、隔离与实战
基于
build/、build/templates/cxx/cxx.gni、build/hb/services/preloader.py、build/ohos.gni源码
目录
术语
| 缩写 | 说明 |
|---|---|
| GN | Generate Ninja,Google 元构建系统 |
| Ninja | 轻量构建执行引擎 |
| hb | HarmonyOS Build,顶层 Python CLI |
| declare_args | GN 中声明可覆盖变量的语法 |
| inner_kits | 部件向外部暴露的接口白名单 |
| preloader | 前置处理:解析产品配置 |
| forward_variables_from | GN 模板中将调用者变量传递到子目标的语法 |
| public_deps | 传递性依赖(依赖方的消费者自动获得该依赖) |
1. 架构设计
1.1 为什么是 GN + Ninja 而不是 Make 或 Bazel
OHOS 的构建框架来自 Google Chromium 项目的 GN + Ninja 组合。
| 选项 | 为什么不用 |
|---|---|
| GNU Make | 隐式规则混乱,增量编译慢(扫描全目录),并行不可靠。一个 include 错了就全体重编 |
| CMake | 交叉编译配置繁琐,需要写 toolchain file,且 CMake 本身生成 Makefile,绕不开 Make 的缺点 |
| Soong (Blueprint) | Android 专用,深度绑定 Android build environment。OHOS 不能直接用 |
| Bazel | 功能最全但部署最重——需要独立 daemon、远程缓存、严格的 hermetic build。OHOS 的嵌入式场景没那么复杂 |
GN 的哲学很纯粹:不编译,只生成。 它把 BUILD.gn 解析成 build.ninja 后就不管了,Ninja 只负责执行编译命令。这种分离让每层都能独立优化。
1.2 三层引擎的分工
hb (Python CLI) ← 产品配置、参数解析、依赖预校验
↓
GN (元构建) ← BUILD.gn → build.ninja
↓
Ninja (执行引擎) ← 编译、安装、镜像制作
hb(build/hb/main.py)做配置解析和编排,不参与编译决策。核心代码在 preloader.py——解析 JSON 配置、检测依赖环、聚合 features(preloader.py:87-112)。
GN 做模板展开。每个 ohos_executable 被展开为多个内部目标(cxx.gni:38)。主要目标包括:
__check—— 编译时校验 external_deps 是否在目标部件的 inner_kits 中__collect—— 收集模块元数据(供 install 使用)__link—— 真正的shared_library/executable编译规则- 辅助目标:
__notice(开源协议收集)、_info(模块信息)
另外还有 ohos_source_set 模板——它不产生 .so 或 .a 文件,只是把源码组织为编译单元,供其他目标引用。适合用作内部模块分组。
Ninja 只做一件事:调度执行编译命令。它不知道 C++ 是什么,只运行 build.ninja 中的命令行。
1.3 四层配置链
① subsystem_config.json → 子系统↔路径映射
② config.json → 产品选型 + 部件列表 + 特性开关
③ bundle.json → 部件定义 + inner_kits + features + deps.components
④ BUILD.gn → 模块编译规则 + deps + external_deps
Preloader 逐层向下解析,输出 out/preloader/{product}/ 下的中间文件。GN 通过 exec_script 加载这些文件——JSON(Preloader 产出)是 Python 和 GN 之间的协议格式。
1.4 Features 机制
Features 让产品配置直接影响代码编译,却不需要产品团队写一行 GN:
bundle.json → { "features": { "enable_ble": true } } ← 部件定义其可选特性
↓ 产品可覆盖
config.json → { "features": ["enable_ble=true"] } ← 产品选择开/关
↓ Preloader 聚合
build_gnargs.prop → "enable_ble=true" ← 关键值格式
↓ GN declare_args() 接收
BUILD.gn 中 if (defined(enable_ble)) { sources += [ "ble.c" ] }
这里用 defined() 而不是 if (enable_ble)。defined() 在 GN 中检查"是否被赋值过"(包括被命令行或产品配置设置),而 if (enable_ble) 只检查布尔值是否为真。前者区分"产品显式选了"和"用了默认值"。
2. GN 语法速查与常见编译单元
2.1 GN 基础语法
OHOS 的 BUILD.gn 使用 GN 语法。以下是阅读源码时最常见的 6 个语法结构:
① import():导入 .gni 头文件
import("//build/ohos.gni") # 导入 OHOS 模板(所有 BUILD.gn 几乎都以此开头)
import("//build/config/ohos/config.gni") # 导入 OHOS 平台配置
② declare_args():声明可覆盖变量
declare_args() {
enable_feature_x = false # 默认值,可被产品配置或命令行覆盖
my_string = "default"
}
declare_args() 可以多次调用。后调用的覆盖先调用的同名变量。
③ if/else:条件编译
if (is_standard_system) {
sources += [ "tcp_channel.c" ]
} else if (is_small_system) {
sources += [ "br_channel.c" ]
}
if (defined(enable_ble)) { # ← 注意:用 defined() 检查是否被设置过
sources += [ "ble_manager.c" ]
}
④ 标签引用://path:target
# // 开头表示源码根目录
# : 后面是目标名
deps = [
"//foundation/communication/dsoftbus/core/common:softbus_common",
":local_sub_target", # 当前 BUILD.gn 中的目标
]
⑤ forward_variables_from():模板中的变量透传
template("ohos_my_template") {
forward_variables_from(invoker, "*", [ "part_name", "install_images" ])
# 将调用者传入的所有变量转发给子目标(排除 part_name 和 install_images)
}
⑥ configs / public_configs:编译器标志管理
config("my_cflags") {
cflags = [ "-Wall", "-O2" ]
defines = [ "MY_DEFINE" ]
}
ohos_shared_library("foo") {
configs += [ ":my_cflags" ] # 仅本目标生效
public_configs = [ ":my_api" ] # 依赖本目标的目标也会继承
}
2.2 常用编译单元(模板)
以下为 OHOS 源码中出现频率最高的编译模板(基于 foundation/ + applications/ 下 BUILD.gn 的统计):
| 模板 | 出现次数 | 用途 | 是否安装 |
|---|---|---|---|
ohos_shared_library |
1618 | 动态库(.so),核心编译产物 | 默认安装到 system/lib64/ |
ohos_prebuilt_etc |
695 | 预置配置文件(JSON/XML/conf),直接复制到镜像 | 安装 |
ohos_source_set |
391 | 编译单元(只编译不链接,不产生产物) | 不安装 |
ohos_static_library |
296 | 静态库(.a),链接到动态库或可执行文件 | 不直接安装 |
ohos_executable |
170 | 可执行文件(bin) | 默认不安装 |
ohos_hap |
80 | 系统应用包(HAP) | 安装到 system/app/ |
ohos_fuzztest |
— | Fuzz 测试用例(测试专用) | 不安装 |
① ohos_shared_library(最常用)
ohos_shared_library("softbus") {
sources = [ "bus_center.c", "discovery.c" ]
include_dirs = [ "include", "//foundation/communication/dsoftbus/interfaces" ]
deps = [ ":common" ]
external_deps = [ "hilog:libhilog" ]
part_name = "dsoftbus"
relative_install_dir = "module/dsoftbus" # 安装到 system/lib64/module/dsoftbus/
}
② ohos_prebuilt_etc(配置文件)
ohos_prebuilt_etc("launcher_config") {
source = "launcher_config.json" # 源文件路径
module_install_dir = "etc/launcher" # 安装到 system/etc/launcher/
part_name = "launcher"
}
全系统有 695 个这样的配置描述文件——从 SELinux 策略到 init 启动脚本,都在用这个模板部署。
③ ohos_source_set(编译分组)
ohos_source_set("common_utils") {
sources = [ "string_util.c", "buffer_util.c" ]
deps = [ "//utils/common:utils" ]
part_name = "dsoftbus"
}
ohos_source_set 不产生产物,只把源码组织为编译单元。它会被 ohos_shared_library 或 ohos_executable 通过 deps 引用,引用的源码会被编译到最终产物中。
④ ohos_executable(可执行文件)
ohos_executable("init") {
sources = [ "init.cpp" ]
deps = [ "//base/startup/init:init_utils" ]
part_name = "init"
install_enable = true # ← 重要:默认不安装,需要显式声明
install_images = [ "system" ]
relative_install_dir = "bin"
}
2.3 构建系统目录结构
build/ # 编译构建子系统根目录
├── hb/ # hb 工具(Python 源码)
│ ├── main.py # CLI 入口
│ ├── services/preloader.py # Preloader:配置解析 + 依赖校验 + features 聚合
│ └── modules/ # 命令模块(build/set/clean/env)
├── config/ # GN 配置
│ ├── BUILDCONFIG.gn # GN 全局配置(1195 行,核心文件)
│ ├── ohos/config.gni # OHOS 平台配置(CPU/ABI/工具链路径)
│ ├── c++/ # C++ 编译选项
│ ├── compiler/ # 编译器选项(优化等级、sanitizer)
│ └── clang/ # Clang 工具链配置
├── templates/ # GN 模板定义
│ └── cxx/cxx.gni # ohos_executable / ohos_shared_library 模板(2186 行)
├── ohos/ # OHOS 专属构建逻辑
│ ├── app/app.gni # ohos_hap 模板(855 行)
│ ├── images/build_image.py # 镜像制作入口
│ ├── packages/modules_install.py # install 阶段脚本
│ └── sdk/ # SDK 编译配置
├── ohos.gni # 模板汇总入口(import 了所有模板)
├── ohos_var.gni # GN 全局变量声明(declare_args,536 行)
└── common.gni # 通用 default 变量
2.4 标签命名惯例
| 模式 | 示例 | 说明 |
|---|---|---|
//subsystem:target |
//utils/common:utils |
子系统根目录下的目标 |
//subsystem/part:target |
//foundation/communication/dsoftbus:softbus |
部件根目标 |
:target |
:common_utils |
当前 BUILD.gn 内的目标 |
//third_party/lib:a |
//third_party/zlib:zlib |
三方库 |
part_name:output |
dsoftbus:softbus |
external_deps 格式 |
3. 编译维度与选项
OHOS 的编译配置分布在 5 个维度:
① 系统类型(mini / small / standard)
影响:工具链、内核选择、模块粒度
设置方式:config.json 的 type 字段
② 构建变体(user / userdebug / eng)
影响:调试符号、日志等级、root 权限
设置方式:--build-variant 参数
③ 产品配置(config.json)
影响:部件列表、CPU 架构、特性开关
设置方式:vendor/{厂商}/{产品}/config.json
④ 部件特性(bundle.json features)
影响:单个部件的条件编译
设置方式:部件 bundle.json 定义默认值,config.json 覆盖
↑③和④的关系:部件声明默认值 / 产品决定最终值
⑤ GN 参数(--gn-args → gn gen --args=)
影响:任意 declare_args()
优先级:命令行直接传参 > hb --gn-args > Preloader 产出 > GNI 默认值
注意第⑤项的优先级链:gn gen --args="key=val"(在 hb 之外直接调用 gn)的优先级高于 hb build --gn-args "key=val",因为后者是通过 hb 间接传递给 gn 的。
4. 不同级别举例
4.1 系统类型
系统类型通过 config.json 的 type 字段设定,转化为 GN 的三个互斥变量:
# BUILDCONFIG.gn:257-265
if (is_mini_system) { os_level = "mini" }
if (is_small_system) { os_level = "small" }
if (is_standard_system) { os_level = "standard" }
同一个模块可以用 if (is_standard_system) 条件编译不同实现:
# 以 dsoftbus 为例
ohos_shared_library("softbus") {
sources = [ "bus_center.c", "discovery.c" ]
if (is_standard_system) {
sources += [
"transmission/tcp_channel.c", # 标准系统用 TCP
"transmission/udp_negotiation.c",
]
} else {
sources += [
"transmission/br_channel.c", # 小型系统用蓝牙
]
}
}
典型配置:
// 手机产品
{ "product_name": "ohos", "type": "standard", "target_cpu": "arm64" }
// 手表产品
{ "product_name": "watch", "type": "small", "target_cpu": "arm" }
// 传感器
{ "product_name": "sensor", "type": "mini", "target_cpu": "arm" }
4.2 构建变体
OHOS 只有 2 个有效构建变体(buildargs.json:456-458),与 Android 的 user/userdebug/eng 完全不同:
"build_variant": { "argDefault": "root", "arg_attribute": { "optional": ["user", "root"] } }
它在 GN 层面的默认值是 root(build/common.gni:15),作用于编译和打包两个阶段:
编译阶段的影响(GN 条件编译):
- Rust 标准库:
root变体额外编译 Rustlibtest.dylib.so,用于单元测试(build/rust/BUILD.gn:18) - seccomp 策略:
root变体使用宽松的 seccomp 过滤规则,允许更多系统调用,方便调试(build/.../seccomp_policy_fixer.gni:135)
# build/rust/BUILD.gn:18
if (build_variant == "root") {
deps += [ ":libtest.dylib.so" ] # root 变体才编译测试库
}
镜像打包阶段的影响:
build_variant 被传递到 build_image.py(通过 images/BUILD.gn:331),决定是否生成 eng_system 分区:
# build/ohos/images/build_image.py:92-97
def _prepare_eng_ststem(eng_system_path: str, build_variant: str):
if build_variant == "user":
return # user 变体跳过 eng_system 分区
# root 变体:复制 debug init、selinux 宽松 policy、调试工具
eng_system 是一个独立分区,包含调试工具、宽松的 SELinux 策略、非 release 的 init 参数。user 变体直接跳过它。
| 变体 | 编译 Rust libtest | seccomp 策略 | eng_system 分区 | 适用场景 |
|---|---|---|---|---|
root (默认) |
编译 | 宽松 | 包含 | 开发板、调试 |
user |
不编译 | 严格 | 跳过 | 量产固件 |
# 默认 root
hb build --product-name ohos
# 发布版本
hb build --product-name ohos --build-variant user
4.3 产品配置——以 dsoftbus 为例
dsoftbus 部件在 bundle.json 中定义可选特性:
{
"component": {
"name": "dsoftbus",
"features": {
"enable_ble": true,
"enable_wifi_direct": false,
"enable_tcp": true
}
}
}
手机产品打开全部,手表产品只开蓝牙:
// 手机 config.json
{ "component": "dsoftbus", "features": [
"enable_ble=true",
"enable_wifi_direct=true",
"enable_tcp=true"
]}
// 手表 config.json
{ "component": "dsoftbus", "features": [
"enable_ble=true",
"enable_wifi_direct=false",
"enable_tcp=false"
]}
4.4 GN 参数命令行覆盖
# 覆盖硬件加速开关
hb build --gn-args "enable_gpu=true enable_mesa3d=true"
# 查看所有 GN 变量的当前值
gn args out/ohos --list
# 只做 GN 解析(调试用)
hb build --build-only-gn
4.5 编译目标范围
# 只编一个模块(秒级)
hb build -T //foundation/communication/dsoftbus:softbus
# 只编一个部件(分钟级)
hb build -T dsoftbus
# 全量编译(小时级)
hb build
系统类型、构建变体、产品配置、GN 参数四者叠加作用——同一个 dsoftbus 模块,在 standard+userdebug 下编译 TCP 支持 + 调试符号,在 small+user 下只编译蓝牙支持 + 全优化。
5. 不同部件之间的依赖
5.1 两种依赖形式
┌──────────────────────────────────┐
│ 部件 A │
│ ┌────────┐ ┌────────┐ │
│ │ mod_A1 ├──┤ mod_A2 │ │
│ └───┬────┘ └────────┘ │
│ │ deps │
│ ├──────────────────┐ │
│ ▼ │ │
│ ┌────────┐ │ │
│ │ mod_A3 │ │ │
│ └────────┘ │ │
└─────────────────────────┼────────┘
│ external_deps
▼
┌──────────────────────────────────┐
│ 部件 B │
│ ┌──────────────┐ │
│ │ libhilog │ ← inner_kits │
│ └──────────────┘ │
│ ┌──────────────────┐ │
│ │ hilog_internal.c │ ← 不可见 │
│ └──────────────────┘ │
└──────────────────────────────────┘
| deps | external_deps | public_deps | |
|---|---|---|---|
| 访问范围 | 同部件任意模块 | 跨部件(限 inner_kits) | 同部件/跨部件 |
| 传递性 | 不传递 | 不传递 | 传递:如果 A public_dep B,A 的消费者自动获得 B |
| 举例 | "//common:utils" |
"hilog:libhilog" |
"//common:api_headers" |
public_deps 的传递性在 cxx.gni:87-94 中被捕获:
if (defined(invoker.public_deps)) {
module_deps += invoker.public_deps # 也加入校验
}
如果 A 依赖 B,B 依赖 C,且 B 的依赖声明为 public_deps,则 A 也能直接引用 C 的头文件。这用于"接口库"场景——A 只需要知道 B,不需要知道 B 内部使用了 C。
5.2 inner_kits——编译时接口契约
以 bundle_framework 为例:
{
"component": {
"name": "bundle_framework",
"build": {
"inner_kits": [
{ "name": "//foundation/bundlemanager/...:libappexecfwk" }
],
"sub_component": [
"//foundation/bundlemanager/bundle_framework:target",
"//foundation/bundlemanager/...:libappexecfwk_icon"
]
}
}
}
launcher 只能引用 libappexecfwk(在 inner_kits 中),不能引用 libappexecfwk_icon(不在白名单中)。
两个校验阶段(以下报错为示例格式,信息与实际编译输出类似):
Preloader(运行在 GN 前):
[OHOS ERROR] part "launcher" uses external_dep
"bundle_framework:libappexecfwk_icon",
but this module is NOT declared in bundle_framework's inner_kits.
Available inner_kits from bundle_framework:
- bundle_framework:libappexecfwk
GN check_target(展开 BUILD.gn 时):
ERROR at //launcher/BUILD.gn:12
Target "launcher_service" depends on
"bundle_framework:libappexecfwk_icon"
which is not in inner_kits of bundle_framework.
See //foundation/bundlemanager/.../bundle.json
Preloader 报错在 GN 之前,更快发现。GN 报错精确到文件行号。
5.3 产品遗漏依赖的情况
如果 config.json 选了部件 A 但没有选 B(A 依赖 B),Preloader 输出:
[OHOS ERROR] component "launcher" depends on "bundle_framework",
but "bundle_framework" is NOT included in product "ohos".
Please check: vendor/xxx/ohos/config.json
依赖从 bundle.json 的 deps.components 推导(部件级),而非从 BUILD.gn 的 external_deps 推导(模块级)。前者在产品配置层面校验,后者在编译层面校验。
6. 产品(系统应用)与镜像编译是否分离
6.1 独立编译与全量编译的区别
全量编译(不分离):
hb build --product-name ohos
# 全部部件 → install → system.img / vendor.img
独立编译(分离):
# 仅编译 launcher 部件
hb build -T Launcher --product-name ohos
# 产物:out/ohos/indep/Launcher.hap(含 native so,但不进镜像)
独立编译的产物不在 packages/phone/ 下,不打包到镜像中。
半分离(只更新镜像,不重新编译):
ninja -C out/ohos install # 重新收集产物
ninja -C out/ohos build_image # 只打包镜像
6.2 系统应用的 HAP 打包
以 Launcher 为例,ohos_hap 模板(build/ohos/app/app.gni:580)的处理阶段:
| 阶段 | 操作 | 产物 |
|---|---|---|
| 1 | compile_js_assets |
ets → .abc 字节码 |
| 2 | compile_resources |
资源压缩 |
| 3 | 打包 ZIP | Launcher.hap |
| 4 | install | system/app/com.ohos.launcher/ |
| 5 | build_image | system.img 对应目录 |
ohos_hap 规则实例:
ohos_hap("Launcher") {
hap_profile = "./src/main/module.json"
js_assets = [ "./src/main/ets/" ]
resources = [ "./src/main/resources/" ]
part_name = "launcher"
module_install_dir = "app/com.ohos.launcher"
deps = [ ":Launcher_binary" ]
}
7. OHOS 编译架构的优缺点
优点
① 编译隔离是编译时强制执行的。 其他系统的模块隔离靠 code review 和团队口头约定。OHOS 用 external_deps / inner_kits 白名单机制,在编译阶段就阻止跨部件引用未公开接口。在多团队协作时——bundle_framework 团队改内部实现,launcher 团队不需要改任何代码。
② 产品配置改 JSON,不需要碰 GN。 产品团队只需要在 config.json 中列部件名、开特性。不涉及 GN 语法。编译系统团队和产品团队之间有清晰的契约边界。
③ 模板展开可追踪。 每个 ohos_executable 展开为可预期的内部目标集合。可以通过 gn gen --ide=json 导出完整的展开结构。
④ 编译与打包分离。 独立编译只产生 .so,全量编译才生成镜像。修改一个 ArkTS 文件不需要重新打包镜像——hdc push 即可验证。
缺点及缓解
① declare_args 的覆盖顺序不直观。 GN 约定"后定义优先",但开发者容易在 ohos_var.gni 中改默认值却没生效(被 Preloader 产出覆盖)。
缓解: 改 GNI 不生效时,先查
out/preloader/{product}/build_gnargs.prop确认变量来源。在 config.json 的 features 中改,或用--gn-args直接覆盖。
② Preloader 的 features 聚合用了 dict.update()。 不同部件同名 feature,后遍历者覆盖前者——隐式命名空间冲突。
缓解: feature 命名加部件前缀,如
dsoftbus_enable_ble而非enable_ble。
③ GN 的 exec_script 是同步的且每次 gn gen 都执行。 但 GN 对相同脚本+相同参数有缓存机制,不会对同一脚本重复执行。对于大产品(几十个部件),exec_script 的执行总时间仍然会累加。
缓解: 该成本是 GN 架构决定的,无法避免。在合理规模下(< 100 个部件)影响可接受。
④ 双层校验(Preloader + GN check_target)有代码重复。 依赖校验在 Python 层和 GN 层各做一次。
缓解: 两者报错时机不同(Preloader 更快,GN 更精确),设计上互补而非完全冗余。出现不一致时优先信任 GN 的报错(因为它有完整标签信息)。
8. 编译注意事项
8.1 ohos_var.gni 的改动无效
最常见的问题。原因在 GN 的 declare_args 覆盖顺序:
# ohos_var.gni(较早加载)
declare_args() { build_ark = true }
# BUILDCONFIG.gn(更晚加载,通过 exec_script 加载了 Preloader 产出)
# → build_ark = false(覆盖了 GNI 的默认值)
解决: 查 out/preloader/{product}/build_gnargs.prop → 有该变量则在 config.json 的 features 中改 → 或用 --gn-args 命令行覆盖。
8.2 同名的 feature 定义冲突
# preloader.py:92
for vals in self._all_parts.items():
all_features.update(vals.get('features', {})) # 后覆盖前
解决: feature 命名加部件前缀,如 dsoftbus_enable_ble。
8.3 ohos_executable 默认不安装
ohos_executable 缺省 install_enable = false(cxx.gni 中定义):
ohos_executable("test_tool") {
install_enable = true # 显式声明
install_images = [ "system" ]
relative_install_dir = "bin"
}
动态库(ohos_shared_library)的行为不同——默认安装到 system/lib/。
8.4 独立编译产物不参与镜像(§5.1 已详述)
hb build -T 的产物在 out/{product}/indep/ 下,不在镜像中。推送开发板:
hdc shell mount -o rw,remount /
hdc push out/ohos/indep/Launcher.hap /system/app/com.ohos.launcher/
8.5 镜像重打包顺序
ninja -C out/ohos install # 先确保产物最新
ninja -C out/ohos build_image # 再打包(否则 packages/ 里是旧文件)
8.6 GN exec_script 调试
gn gen 卡住或用奇怪的错误时:
hb build --build-only-gn --verbose
# 或直接调用 gn
gn gen out/ohos --script-executable=python3 --verbose
9. 总结
9.1 核心链路
config.json → Preloader (聚合 features + 校验依赖)
→ build_gnargs.prop
→ GN exec_script → declare_args
→ BUILD.gn → ohos_executable (展开 check/collect/link)
→ build.ninja
→ Ninja (编译 + install + build_image)
→ system.img
9.2 关键设计
| 概念 | 目的 |
|---|---|
| 四层配置链 | 每层只解决自己问题域:子系统路径、产品选型、部件定义、模块规则 |
| deps / external_deps / public_deps | 同部件自由引用 / 跨部件白名单 / 传递性依赖 |
Features → defined() |
产品配置直接驱动条件编译,不碰 GN 语法 |
| Preloader + GN 双层校验 | 越快发现问题越好:Python 层毫秒级检出,GN 层精确到行号 |
| 独立编译 + image 分离 | 开发调试不断开镜像打包,hdc push 秒级验证 |
| declare_args 后定义优先 | 命令行 > Preloader 产出 > GNI 默认值 |
9.3 注意点速查
| 场景 | 问题 | 解决 |
|---|---|---|
| 改了 GNI 但编译没变 | declare_args 被 Preloader 覆盖 | 查 build_gnargs.prop,改 config.json 或用 --gn-args |
| feature 不生效 | 另一部件同名 feature 覆盖 | 加部件名前缀,如 dsoftbus_enable_ble |
| 可执行文件没进镜像 | ohos_executable 默认不安装 |
加 install_enable = true |
| 独立编译产物重启消失 | dm-verity 恢复 | hdc shell mount -o rw,remount / |
| 镜像没更新 | 改了 HAP 但没重新 install | 先 ninja install 再 ninja build_image |
gn gen 卡住 |
exec_script 异常 |
--verbose 查看执行路径 |

浙公网安备 33010602011771号