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

简介: 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):介绍了属性的类型和赋值方式,以及如何覆盖属性的值。

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

相关文章
|
7天前
|
Android开发
【苹果安卓通用】xlsx 和 vCard 文件转换器,txt转vCard文件格式,CSV转 vCard格式,如何批量号码导入手机通讯录,一篇文章说全
本文介绍了如何快速将批量号码导入手机通讯录,适用于企业客户管理、营销团队、活动组织、团队协作和新员工入职等场景。步骤包括:1) 下载软件,提供腾讯云盘和百度网盘链接;2) 打开软件,复制粘贴号码并进行加载预览和制作文件;3) 将制作好的文件通过QQ或微信发送至手机,然后按苹果、安卓或鸿蒙系统的指示导入。整个过程简便快捷,可在1分钟内完成。
|
18天前
|
Android开发
Android studio 2021.3.1不生成iml文件
Android studio 2021.3.1不生成iml文件
33 5
|
18天前
|
缓存 安全 Java
Android中的persistent属性
Android中的persistent属性
14 2
|
18天前
|
Java 开发工具 Android开发
Android mk 集成app
Android mk 集成app
22 5
|
18天前
|
Android开发
Android android:exported="true" 属性
Android android:exported="true" 属性
16 0
|
18天前
|
Android开发
Android stdio 无法新建或打开AIDL文件(解决方法)
Android stdio 无法新建或打开AIDL文件(解决方法)
55 0
|
18天前
|
Android开发
android string.xml文件中的整型和string型代替
android string.xml文件中的整型和string型代替
15 0
|
18天前
|
Java Android开发
Android开发--Intent-filter属性详解
Android开发--Intent-filter属性详解
13 0
|
18天前
|
Shell Android开发
android mk中打印信息
android mk中打印信息
16 0
|
Android开发 C++ 编译器
Android.mk 用法介绍
一个Android.mk file用来向编译系统描述你的源代码。具体来说:该文件是GNU Makefile的一小部分,会被编译系统解析一次或多次。你可以在每一个Android.mk file中定义一个或多个模块,你也可以在几个模块中使用同一个源代码文件。
1154 0