Android源代码定制:MK文件执行顺序|属性覆盖

前言

Android的构建系统中,有一些机制和工具可以帮助我们实现源代码的定制和优化,例如:

  • mk文件:这是一种用于描述构建规则和依赖关系的Makefile。可以在mk文件中定义一些变量和指令,来控制源代码的编译和打包过程。
  • 属性(sysprop):这是一种用于控制系统行为和配置的系统属性。我们可以在不同的地方定义和赋值属性,来影响系统的运行时状态。

在本文中,我将介绍这些机制和工具的基本概念和用法,并且通过一些实例来展示如何使用它们来实现Android源代码的定制和优化。还将介绍如何调试我们的修改是否生效,以及如何查看我们的修改的详细信息。

1. 如何确定mk文件的执行顺序

在Android的构建系统中,xxx.mk文件是一种用于描述构建规则和依赖关系的Makefile。mk文件可以通过include-include指令来包含其他mk文件,从而形成一个复杂的构建图。mk文件的执行顺序是由构建图中的依赖关系决定的,而不是由文件名或目录结构决定的。

构建系统会从根目录下的build/make/core/main.mk开始执行,然后根据不同的目标平台和产品选择相应的mk文件。例如我选择了rk3568_s作为目标项目,那么构建系统会执行以下mk文件:

  • build/make/core/main.mk
  • build/make/target/product/core.mk
  • device/rockchip/rk356x/rk3568_s/rk3568_s.mk
  • device/rockchip/rk356x/BoardConfig.mk
  • device/rockchip/common/BoardConfig.mk
  • vendor/customize/customize.mk

每个mk文件都可能包含其他mk文件,形成一个嵌套的结构。可以通过在mk文件中添加$(warning)$(info)指令来打印一些信息,从而观察它们的执行顺序。在调试过程中,在rk3568_s.mkcustomize.mk中添加了以下代码:

$(warning Enter rk3568_s > device.mk is included $(TARGET_PRODUCT))
 
ifeq ($(TARGET_PRODUCT),rk3568_s)
$(warning Enter rk3568_s)
endif
$(warning Enter customize > customize.mk is included $(TARGET_PRODUCT))
 
ifeq ($(filter $(TARGET_PRODUCT),rk3568_s_beta rk3568_s),)
# 这里是当TARGET_PRODUCT不是rk3568_s_beta或rk3568_s时执行的代码
else
# 这里是当TARGET_PRODUCT是rk3568_s_beta或rk3568_s时执行的代码
$(warning Enter $(MY_CUSTOM_PATH)/rk3568_s_beta)
endif

这样当我执行lunch rk3568_s-userdebug时,会看到以下输出:

device/rockchip/rk356x/rk3568_s/rk3568_s.mk:17: warning: Enter rk3568_s > device.mk is included rk3568_s
device/rockchip/rk356x/rk3568_s/rk3568_s.mk:20: warning: Enter rk3568_s
vendor/customize/customize.mk:3: warning: Enter customize > customize.mk is included rk3568_s
vendor/customize/customize.mk:12: warning: Enter ./vendor/customize/rk3568_s_beta

在这里插入图片描述

这说明了rk3568_s.mk先于customize.mk被执行,并且它们都被正确地识别了目标产品,除了确定顺序外,还可以通过在mk文件中添加$(warning)$(info)指令来打印 来确认有些模块 是否编译,是否进入了我们预期的编译条件内 懂我意思吧 而不是傻傻的改完就刷机验证,很多内容可以在编译期间就可以知道结果了。

2. 如何覆盖PRODUCT_PROPERTY_OVERRIDES属性

在Android中,有一些系统属性(sysprop)是用于控制系统行为和配置的。例如,ro.sf.lcd_density属性用于指定屏幕密度(dpi)。这些属性可以在不同的地方被定义和赋值,例如:

  • 在设备相关的mk文件中,使用PRODUCT_PROPERTY_OVERRIDES += <property>=<value>指令。
  • 在设备相关的Java代码中,使用SystemProperties.set(<property>, <value>)方法。
  • 在设备相关的C/C++代码中,使用property_set(<property>, <value>)函数。
  • 在命令行中,使用setprop <property> <value>命令。

当一个属性在多个地方被定义和赋值时,它们可能会产生冲突和覆盖。为了避免这种情况,Android的构建系统提供了一些规则和工具来检查和处理属性的冲突和覆盖。

属性的赋值可以分为两种类型:强制可选。强制赋值是指在mk文件中直接使用PRODUCT_PROPERTY_OVERRIDES += <property>=<value>指令的赋值,它表示这个属性必须被设置为这个值。可选赋值是指在mk文件中使用PRODUCT_PROPERTY_OVERRIDES += ?<property>=<value>指令的赋值,它表示这个属性可以被设置为这个值,但也可以被其他强制赋值覆盖。

当一个属性有多个强制赋值时,构建系统会检查它们是否有相同的值。如果有相同的值,那么构建系统会忽略可选赋值,并将这个属性设置为这个值。如果没有相同的值,那么构建系统会报错,并提示找到了重复的赋值。

其实我开始是不知道这么个问题的,因为早期调试Android源码的时候 大概记得可以在不同的MK定义PRODUCT_PROPERTY_OVERRIDES 从而形成覆盖属性, 但是根据我调试发现

假设我在Android源码device目录下 有个文件比如device.mk 定义了
PRODUCT_PROPERTY_OVERRIDES += ro.sf.lcd_density=240
然后vendor目录下又有一个自定义的custom.mk 又定义了
PRODUCT_PROPERTY_OVERRIDES += ro.sf.lcd_density=320
编译Android源码最终会编译使用240还是320?我开始理解的以为根据覆盖原则,在vendor/build.prop 应该只存在240 直接编译。。
编译报错:
error: found duplicate sysprop assignments:
ro.sf.lcd_density=240
ro.sf.lcd_density=320

资料显示:既然已经确定了vendor下的custom.mk是最后加载的,那么它的定义会覆盖之前的定义。但从Android 9 开始,构建系统不再允许同一个属性在多个地方被定义,因此会看到这个错误。

所以这就为啥 在device.mkcustom.mk中都使用了强制赋值:

PRODUCT_PROPERTY_OVERRIDES += ro.sf.lcd_density=240
PRODUCT_PROPERTY_OVERRIDES += ro.sf.lcd_density=320

执行make时,会看到以下错误:

error: found duplicate sysprop assignments:
ro.sf.lcd_density=240
ro.sf.lcd_density=320

这是因为构建系统不允许同一个属性有多个不同的强制赋值。

为了解决这个问题,有以下几种方法:

  • 修改其中一个强制赋值为可选赋值:如果想让一个强制赋值可以被另一个强制赋值覆盖,可以在属性名前加上一个问号(?),表示这是一个可选赋值。如果想让device.mk中的赋值可以被custom.mk中的赋值覆盖,可以修改device.mk中的代码为:
PRODUCT_PROPERTY_OVERRIDES += ?ro.sf.lcd_density=240

当执行make时,构建系统会忽略device.mk中的可选赋值,并将这个属性设置为custom.mk中的强制赋值(320)。

  • 修改构建工具来删除重复的强制赋值:如果不想修改mk文件中的代码,也可以修改构建系统中用于处理属性冲突和覆盖的工具。这个工具是一个py脚本,位于build/make/tools/post_process_props.py。它会读取所有定义的属性,并根据它们的类型和值来决定是否删除或保留它们。可以修改这个脚本来实现想要的逻辑。尝试了以下几种修改:

    • 去掉了重复属性的限制:注释掉了报错的代码,使得构建系统不再检查重复属性。这样当你执行make时,构建系统会保留所有定义的属性,并将它们写入到最终生成的build.prop文件中。例如:
    rk_android12.0_sdk/out/target/product/rk3568_s/vendor$ grep -rn "ro.sf.lcd_density"
    build.prop:91:ro.sf.lcd_density=240
    build.prop:156:ro.sf.lcd_density=320
    

    这样做的问题是,当系统启动时,它会读取最后一个定义的属性作为最终的值,而忽略之前的定义。这可能会导致一些不一致和混乱。

    • 去掉了重复属性的最后一个:添加了一些代码,使得构建系统在遇到重复属性时,删除最后一个定义的属性,并保留第一个定义的属性。这样,当你执行make时,构建系统会删除custom.mk中的强制赋值,并将这个属性设置为device.mk中的强制赋值(240)。例如:
rk_android12.0_sdk/out/target/product/rk3568_s/vendor$ grep -rn "ro.sf.lcd_density"

  #build.prop:92:ro.sf.lcd_density=240
  # Removed by post_process_props.py because Duplicate property, keeping the first one
  build.prop:161:#ro.sf.lcd_density=320

这样做的问题是,不想让device.mk中的赋值覆盖custom.mk中的赋值,而是反过来,但结果却是注释掉了custom.mk的240。
按照正常理解 应该是custom.mk(240) 比device.mk(320) 后加载应该删除320才对?但是py脚本加载写入是倒序的?这不符合我的预期。

  • 去掉了重复属性的第一个:继续修改,使得构建系统在遇到重复属性时,删除第一个定义的属性,并保留最后一个定义的属性。这样,当你执行make时,构建系统会删除device.mk中的强制赋值,并将这个属性设置为custom.mk中的强制赋值(320)。例如:
# Removed by post_process_props.py because Duplicate property, keeping the last one
build.prop:92:#ro.sf.lcd_density=240
# Removed by post_process_props.py because Duplicate property, keeping the first one
#build.prop:161:ro.sf.lcd_density=320

这样做可以让custom.mk中的赋值覆盖device.mk中的赋值,而不用修改原生的device.mk文件中的代码 不管系统之前属性如何写的,只需要最终在我们的客制化mk文件中重新覆盖即可,不需要整个源码的去找。但是这样做的缺点是,可能会影响其他属性的赋值顺序和结果,因为改变了构建工具的默认行为。但根据我的测试暂时没什么影响。
在这里插入图片描述

总结

在本文中,介绍了Android源代码定制的一些机制和工具,包括:

  • mk文件:介绍了mk文件的结构和语法,以及如何确定mk文件的执行顺序。
  • 属性(sysprop):介绍了属性的类型和赋值方式,以及如何覆盖属性的值。

希望这篇文章能够对你有所帮助,如果你有任何问题或建议,请在评论区留言。谢谢!

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一歲抬頭

点赞1元,收藏免费,打赏随意。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值