4、AOSP中的设备树文件结构:BoardConfig.mk、device.mk、AndroidProducts.mk的作用与配置

好,咱们今天聊聊设备树里最核心的三个文件。很多新手刚接触AOSP时,看到device目录下一堆.mk文件就头大。其实说白了,这三个文件各司其职,搞懂了它们,设备树配置就掌握了一半。

4.1 BoardConfig.mk —— 硬件的“身份证”

这个文件描述的是硬件本身的能力。我习惯把它理解为“这块板子长什么样”。

它主要定义的是芯片架构、内存大小、分区布局、Bootloader参数等。这些信息跟具体的产品型号无关,只跟硬件平台有关。

# BoardConfig.mk 示例
TARGET_ARCH := arm64
TARGET_ARCH_VARIANT := armv8-a
TARGET_CPU_VARIANT := generic
TARGET_CPU_ABI := arm64-v8a

TARGET_NO_BOOTLOADER := true
TARGET_NO_KERNEL := false

BOARD_KERNEL_CMDLINE := console=ttyS0,115200 androidboot.hardware=mydevice
BOARD_KERNEL_BASE := 0x80000000
BOARD_KERNEL_PAGESIZE := 2048

BOARD_BOOTIMAGE_PARTITION_SIZE := 33554432
BOARD_SYSTEMIMAGE_PARTITION_SIZE := 1073741824
BOARD_USERDATAIMAGE_PARTITION_SIZE := 2147483648

这里有个坑,我踩过好几次。就是BOARD_KERNEL_CMDLINE里的参数,一定要跟你的内核实际支持的参数匹配。我曾经因为多写了一个不支持的参数,导致内核启动时直接panic,查了两天才发现是cmdline的问题。

注意:BoardConfig.mk里定义的变量,大部分是全局生效的。如果你在多个设备间共用同一个BoardConfig,一定要小心变量覆盖的问题。我建议每个设备都单独维护一份,别偷懒。

4.2 device.mk —— 产品的“配置单”

这个文件描述的是产品层面的东西。比如要编译哪些模块、预装哪些应用、系统属性的默认值等。

你想想看,同一个硬件平台,可能出好几个型号。比如标准版和Pro版,它们的BoardConfig.mk可以一样,但device.mk肯定不同。

# device.mk 示例
$(call inherit-product, $(SRC_TARGET_DIR)/product/core_64_bit.mk)
$(call inherit-product, $(SRC_TARGET_DIR)/product/full_base_telephony.mk)

PRODUCT_NAME := mydevice
PRODUCT_DEVICE := mydevice
PRODUCT_BRAND := MyBrand
PRODUCT_MODEL := MyDevice Pro
PRODUCT_MANUFACTURER := MyCompany

# 预装应用
PRODUCT_PACKAGES += \
    MyLauncher \
    MyCamera \
    MySettings

# 系统属性
PRODUCT_PROPERTY_OVERRIDES += \
    ro.sf.lcd_density=480 \
    ro.product.display.resolution=1080x1920

# 权限配置
PRODUCT_COPY_FILES += \
    frameworks/native/data/etc/android.hardware.camera.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.camera.xml

我个人习惯把PRODUCT_PACKAGES按功能模块分组,比如系统应用、第三方应用、测试工具等。这样后期维护时,一眼就能看出哪些模块是干什么的。

小技巧:如果你想让某个模块只在eng版本中编译,可以用PRODUCT_PACKAGES_ENG。同理还有PRODUCT_PACKAGES_DEBUGPRODUCT_PACKAGES_USER。这个我在做调试版本时经常用,省去了手动修改mk文件的麻烦。

4.3 AndroidProducts.mk —— 产品列表的“目录”

这个文件最简单,但也很容易被忽略。它的作用就是告诉编译系统:我这个设备目录下有哪些产品配置。

# AndroidProducts.mk 示例
PRODUCT_MAKEFILES := \
    $(LOCAL_DIR)/device.mk

# 如果有多个产品变体
# PRODUCT_MAKEFILES := \
#     $(LOCAL_DIR)/device.mk \
#     $(LOCAL_DIR)/device_lite.mk

嗯,这里要注意。如果你在AndroidProducts.mk里漏掉了某个产品,那你在执行lunch命令时就看不到它。我记得有一次帮同事排查问题,他明明写了device.mk,但lunch列表里就是没有。最后发现是AndroidProducts.mk里忘记引用了。

4.4 三个文件的关系

说白了,这三个文件的关系是这样的:

文件 作用域 定义内容 典型变量
BoardConfig.mk 硬件平台 CPU架构、分区大小、内核参数 TARGET_ARCH, BOARD_KERNEL_*
device.mk 产品型号 预装模块、系统属性、权限 PRODUCT_PACKAGES, PRODUCT_PROPERTY_*
AndroidProducts.mk 产品列表 注册可编译的产品 PRODUCT_MAKEFILES

它们的加载顺序也很有意思。编译系统会先读AndroidProducts.mk,拿到所有可用的产品列表。然后根据你lunch选择的产品,加载对应的device.mk。而BoardConfig.mk则是在device.mk中被inherit-product引入的。

核心要点:
  • BoardConfig.mk —— 硬件说了算,跟产品无关
  • device.mk —— 产品说了算,决定软件配置
  • AndroidProducts.mk —— 目录索引,告诉系统有哪些产品可选

4.5 实战中的配置建议

基于我这些年做AOSP移植的经验,给你几个建议:

  1. BoardConfig.mk尽量保持纯净 —— 只放跟硬件强相关的配置。别把产品相关的变量塞进去,否则后面维护起来会很痛苦。
  2. 善用inherit-product —— 如果你的多个产品有共同配置,可以抽到一个公共的mk文件里,然后用inherit-product引入。这样改一处,所有产品都生效。
  3. 命名规范要统一 —— 我见过有人把BoardConfig.mk写成boardconfig.mk,结果编译时死活找不到。AOSP对文件名大小写是敏感的,别在这种细节上翻车。
  4. 版本管理要跟上 —— 每次修改这三个文件,都建议在commit message里写清楚改了啥、为什么改。不然三个月后回头看,你自己都忘了当初为啥要加那个变量。

好了,这三个文件的内容就讲到这里。下一章咱们聊聊设备树里更进阶的内容——如何调试和排查配置问题。到时候我会分享一些我实际踩过的坑,保证让你少走弯路。