Android源码中 filter-out 的使用及示例

此笔记已同步推送到微信公众号:灰灰的Rom笔记。


需求背景

上周做第三方(思必驰)降噪算法库的集成,碰到了一个问题:
思必驰给过来的文件有好一些,其中有一个audio_policy_configuration.xml,要替换vendor/etc中的同名文件;两个lib库(32bit和64bit),要编译到vendor/lib/hw/vendor/lib64/hw;其余文件编译到vendor/etc中。

这个问题不难,但为了免去后续其他项目的配置,我需要提交到公版分支;但如果直接添加,那所有基于此公版的项目都会受影响,因此必须要做成项目差异化的。

分析到这里,自然就想到宏开关,而且给过来的文件,只需要做简单的替换和新增,不需要其他操作,因此宏开关+拷贝脚本的常规组合是最简单方便的。于是,解决方法就有了:
在 vendor 新建 aispeech 的目录,放入所有思必驰的文件,编写拷贝脚本,然后在公共文件 device/mediatek/common/device.mk根据各个项目的宏开关状态,去决定是否要引入 vendor/aispeech/ 的资源。这个方法类似于引入 GMS 包,具体代码就不贴了(贴了也没用)。

本以为已经 OK 了,编译验证一顿操作猛如虎,没想到audio_policy_configuration.xml文件始终无法完成覆盖!!!

既然常规思路搞不定,那只能另辟蹊径了。上面提到的文件中,除了audio_policy_configuration.xml,其他都是新增的,都是小事;而重点是如何通过宏开关决定是否要覆盖audio_policy_configuration.xml 文件,要是能找到拷贝它的脚本,加上宏开关判断就可以了。

于是全局搜索这个 policy 文件,发现了一些引用,但根据 policy 的源路径和目标路径可以判断都是无关的 policy 文件。

PS:源码中有多个audio_policy_configuration.xml文件,它们的优先级是:
项目目录 > MTK目录 > Google目录
这里项目目录是:device/xxxx/xxxx/audio_policy_config/

居然没有拷贝脚本,文件就拷贝进去了,很不科学!难道,它不是基于文件名拷贝,而是基于目录拷贝的?

filter-out 在源码中的应用

于是全局搜了audio_policy_config/目录的引用情况,果然在:

\device\mediatek\common\device.mk

中找到匹配度最高的代码:

扫描二维码关注公众号,回复: 2699406 查看本文章
# Audio policy config
ifeq ($(strip $(USE_XML_AUDIO_POLICY_CONF)), 1)

AUDIO_POLICY_PROJECT_CONFIGS := \
  $(strip \
    $(notdir $(wildcard $(MTK_TARGET_PROJECT_FOLDER)/audio_policy_config/*.xml)\
    ) \
  )

AUDIO_POLICY_BASE_PROJECT_CONFIGS := \
  $(strip \
    $(filter-out $(AUDIO_POLICY_PROJECT_CONFIGS), \
      $(notdir $(wildcard $(MTK_PROJECT_FOLDER)/audio_policy_config/*.xml)) \
    ) \
  )
AUDIO_POLICY_PLATFORM_CONFIGS := \
  $(strip \
    $(filter-out $(AUDIO_POLICY_PROJECT_CONFIGS) $(AUDIO_POLICY_BASE_PROJECT_CONFIGS), \
      $(notdir $(wildcard device/mediatek/$(MTK_PLATFORM_DIR)/audio_policy_config/*.xml)) \
    ) \
  )
AUDIO_POLICY_COMMON_CONFIGS := \
  $(strip \
    $(filter-out $(AUDIO_POLICY_PROJECT_CONFIGS) $(AUDIO_POLICY_BASE_PROJECT_CONFIGS) $(AUDIO_POLICY_PLATFORM_CONFIGS), \
      $(notdir $(wildcard device/mediatek/common/audio_policy_config/*.xml)) \
    ) \
  )

$(foreach x,$(AUDIO_POLICY_PROJECT_CONFIGS), \
  $(eval PRODUCT_COPY_FILES += $(MTK_TARGET_PROJECT_FOLDER)/audio_policy_config/$(x):$(TARGET_COPY_OUT_VENDOR)/etc/$(x)) \
)

$(foreach x,$(AUDIO_POLICY_BASE_PROJECT_CONFIGS), \
  $(eval PRODUCT_COPY_FILES += $(MTK_PROJECT_FOLDER)/audio_policy_config/$(x):$(TARGET_COPY_OUT_VENDOR)/etc/$(x)) \
)

$(foreach x,$(AUDIO_POLICY_PLATFORM_CONFIGS), \
  $(eval PRODUCT_COPY_FILES += device/mediatek/$(MTK_PLATFORM_DIR)/audio_policy_config/$(x):$(TARGET_COPY_OUT_VENDOR)/etc/$(x)) \
)

$(foreach x,$(AUDIO_POLICY_COMMON_CONFIGS), \
  $(eval PRODUCT_COPY_FILES += device/mediatek/common/audio_policy_config/$(x):$(TARGET_COPY_OUT_VENDOR)/etc/$(x)) \
)
endif

不难看出,后面几个foreach是在进行循环拷贝,第二个参数是文件名的集合。但这个集合是怎么来的,就需要理解上面的几个filter-out了。

理解 filter-out 和 filter

filter-out
使用方法:$(filter-out pattern…,text)

Returns all whitespace-separated words in text that do not match any of the pattern words, removing the words that do match one or more. This is the exact opposite of the filter function.

一句话:返回 text 中,所有不符合 pattern 规则的结果,可以有多个 pattern ,但多个 pattern 之间要用空格连接。
换句话说就是:只返回 text 中,pattern 里面没有的内容。
借用数学里面的集合表示就是:

前面提到的 device.mk 中的栗子比较复杂,举个简单的:

objects = main1.o foo.o main2.o bar.o 
mains = main1.o main2.o 

$(filter-out $(mains),$(objects)) 

意思就是只返回 foo.o、bar.o。

filter

既然 filter-out 都说了,那就顺便提下 filter:

使用方法:$(filter pattern…,text)

Returns all whitespace-separated words in text that do match any of the pattern words, removing any words that do not match. The patterns are written using ‘%’, just like the patterns used in the patsubst function above.

一句话:返回 text 中所有符合 pattern 规则的结果,同样可以有多个 pattern ,但多个 pattern 之间要用空格连接。
换句话说就是:只返回 text 和 pattern 中共同包含的内容。
用集合表示就是:

栗子(只返回 .c 和 .s 文件):

sources = foo.c bar.c baz.s abc.h
$(filter %.c %.s,$(sources))

再分析 device.mk

我们回过头再看看上面 device.mk 中的使用样例,其中后半部分的 foreach 的作用太明显,不再做过多说明;这里只解释前半部分的集合初始化。

1、先看 AUDIO_POLICY_PROJECT_CONFIGS 部分:

AUDIO_POLICY_PROJECT_CONFIGS := \
  $(strip \
    $(notdir $(wildcard $(MTK_TARGET_PROJECT_FOLDER)/audio_policy_config/*.xml)\
    ) \
  )

显然这个是 PROJECT 的,按前文所说,具体 PROJECT 的优先级是最高的,这里放在最前面声明也印证了这一点。这段代码的意思就是将 AUDIO_POLICY_PROJECT_CONFIGS 指向device/xxxx/xxxx/audio_policy_config/目录中的所有 xml 文件

2、再看 AUDIO_POLICY_BASE_PROJECT_CONFIGS 部分:

AUDIO_POLICY_BASE_PROJECT_CONFIGS := \
  $(strip \
    $(filter-out $(AUDIO_POLICY_PROJECT_CONFIGS), \
      $(notdir $(wildcard $(MTK_PROJECT_FOLDER)/audio_policy_config/*.xml)) \
    ) \
  )

这个是 BASE_PROJECT 的,优先级较 PROJECT 低,为了保证高优先级的 PROJECT 生效,这里使用了filter-out函数将 BASE_PROJECT 中的同名文件过滤掉,最后 AUDIO_POLICY_BASE_PROJECT_CONFIGS 指向的就是 BASE_PROJECT 有,而 PROJECT 没有的所有xml文件了。(有点绕口)

后面几个类比 AUDIO_POLICY_BASE_PROJECT_CONFIGS 即可,不再赘述。
最后经过这一系列filter-out处理之后的集合模型就是这个样子:

解决需求问题

现在再看一开始的需求问题,解决方法就很清晰了。只需要在初始化 AUDIO_POLICY_PROJECT_CONFIGS 时,增加宏判断就好了嘛。

若具体项目没有开宏,则使用原始代码逻辑;若有开宏,则使用如下代码逻辑:

AISPEECH_AUDIO_POLICY_CONFIGS := \
    $(strip \
        $(notdir $(wildcard aispeech_patches/*.*)\
        ) \
    )

AUDIO_POLICY_PROJECT_CONFIGS := \
    $(strip \
        $(filter-out $(AISPEECH_AUDIO_POLICY_CONFIGS), \
            $(notdir $(wildcard $(MTK_TARGET_PROJECT_FOLDER)/audio_policy_config/*.xml)) \
        ) \
    )

这里进一步将*.xml修改为*.*了,可更方便的将思必驰的其他文件一同编译进去。

疑问

这里对 audio_policy_configuration.xml 的拷贝虽然是按目录拷贝的,但按理说,在这拷贝代码后面新增:

PRODUCT_COPY_FILES += aispeech_patches/audio_policy_configuration.xml:$(TARGET_COPY_OUT_VENDOR)/etc/audio_policy_configuration.xml

应该是能再次执行拷贝,覆盖掉刚刚按目录拷贝的文件的。
但实际情况是,即便就加在上面 foreach 之后,也不能完成覆盖。
本人不清楚是否因为多线程编译,脚本也会并发执行。若有懂的人,烦请赐教!


此笔记已同步推送到微信公众号:灰灰的Rom笔记。

猜你喜欢

转载自blog.csdn.net/ShawnXiaFei/article/details/80888095