Android-Product-Makefiles
1 到哪儿去找某Product的Makefiles?
Android的产品配置中,我们在build的时候用chooseproduct指定了产品名,比如
叫 pxa988dkb_def
,那么,Android怎么知道应该去使用这个产品的Makefiles,而不
是 pxa986ff_def
的Makefiles呢?
尤其是,它怎么知道到哪儿去找 pxa988dkb_def
的Makefiles呢?
答案是,它不知道。
Android的做法是把所有产品的定义都读进来,然后就能知道我们指定的产品名是 不是有定义,有的话就使用它相关的定义就可以了。
这样做好像很笨,好像做了很多无用功,读了一些可能最后用不到的product
makefiles进来。但事实上不是那样的,因为否则的话我们就必须在指定产品名的
同时再自己指定该产品Makefile的路径,比如 pxa988dkb_def
的产品
Makefile是放在 android/device/marvell/pxa988dkb/pxa988dkb.mk,这样就会
很麻烦。而且违反了SPOT (single point of truth)的原则,也就是说,
pxa988dkb_def
被android/device/marvell/pxa988dkb.mk定义是一个truth,
但是要求我们在命令行上再重复一遍来指出这个事实,就有可能会指错啊;而它
是可以用算法算出来的,这个是不会算错的,而如果有另一个Makefile重复定义
了这个产品的错误情况,也能够被发现。
2 产品Makefile只能定义特定的 PRODUCT_XXX
变量
由于Android会把所有的产品Makefile都读进来,所以这些Makefile里定义的变量 都是经过特殊处理的,以保证产品与产品之间虽然定义了同样的变量,但是它们 之间不会产生相互冲突。
所以在产品Makefile里只可以定义这个列表中的变量(这个列表的定义在 android/build/core/product.mk中),任何其它的变量都不应该定义,否则在多 产品的环境中会产生冲突:
_product_var_list := \ PRODUCT_NAME \ PRODUCT_MODEL \ PRODUCT_LOCALES \ PRODUCT_AAPT_CONFIG \ PRODUCT_AAPT_PREF_CONFIG \ PRODUCT_PACKAGES \ PRODUCT_DEVICE \ PRODUCT_MANUFACTURER \ PRODUCT_BRAND \ PRODUCT_PROPERTY_OVERRIDES \ PRODUCT_DEFAULT_PROPERTY_OVERRIDES \ PRODUCT_CHARACTERISTICS \ PRODUCT_COPY_FILES \ PRODUCT_OTA_PUBLIC_KEYS \ PRODUCT_EXTRA_RECOVERY_KEYS \ PRODUCT_PACKAGE_OVERLAYS \ DEVICE_PACKAGE_OVERLAYS \ PRODUCT_TAGS \ PRODUCT_SDK_ADDON_NAME \ PRODUCT_SDK_ADDON_COPY_FILES \ PRODUCT_SDK_ADDON_COPY_MODULES \ PRODUCT_SDK_ADDON_DOC_MODULES \ PRODUCT_DEFAULT_WIFI_CHANNELS \ PRODUCT_DEFAULT_DEV_CERTIFICATE \ PRODUCT_RESTRICT_VENDOR_FILES \ PRODUCT_FACTORY_RAMDISK_MODULES \ PRODUCT_VENDOR_KERNEL_HEADERS \
3 产品Makefile的重用应该用inherit-product
Android产品定义里已经给出了一个generic的产品,其它公司的产品大部分都是
继承它的,也就是说generic里定义的 PRODUCT_PACKAGES
基本上其它产品也都需要
包括,解决的方法有三种:
- 把generic里的定义抄一遍
- 把generic.mk给直接include进来
- 设计一个新的机制,把generic给继承下来,也就是inherit-product
1 和 2 都不可取,1 根本就完全违反了软件重用的原则。
而 2 则很隐蔽的与软件重用背道而驰,我们知道,这些Makefiles里定义的变量
都是需要特殊处理的,以防止命名冲突,如果直接include的话,就没法做这个特
殊处理,从而容易出现冲突。简单来说,比如 A 先后 include 了 B 和 C,那么,
B 和 C 里就只能全程用 PRODUCT_xxx +=
这种形式去定义,不然的话在B里定
义了的 PRODUCT_xxx
,在C里用 PRODUCT_xxx :=
直接就丢失了。
另外,如果多重继承的话,直接用 include
也不能很好的处理 (比如,A
include 了 B 和 C,而 C 也 include 了 B的这种情况)。
所以Android采取的是3,把产品继承和变量定义重命名防冲突一起用
android/build/core/product.mk
和 android/build/core/node_fns.mk
这两个
文件实现了。
关于 2 不可取的证据是,Android在build这个project底下有一个git commit, 可以看一下:
commit 7b86bfb03ee785cb828139c94bb86817d3249667 Author: Joe Onorato <joeo@android.com> AuthorDate: Thu Jan 7 11:24:46 2010 -0800 Commit: Joe Onorato <joeo@android.com> CommitDate: Thu Jan 7 11:26:05 2010 -0800 add a warning about using include in product spec files. currently disabled because there are too many of them.
也就是说,很遗憾现在还是有很多地方在用 2 这种方式去实现,但我们不应该再 继续用这种方式了。
4 PRODUCT 处理的详细分析
4.1 找到所有产品定义的Makefile
4.1.1 先要找到所有 AndroidProducts.mk
define _find-android-products-files $(shell test -d device && find device -maxdepth 6 -name AndroidProducts.mk) \ $(shell test -d vendor && find vendor -maxdepth 6 -name AndroidProducts.mk) \ $(SRC_TARGET_DIR)/product/AndroidProducts.mk endef
4.1.2 再找到产品的Makefiles
define get-all-product-makefiles $(call get-product-makefiles,$(_find-android-products-files)) endef define get-product-makefiles $(sort \ $(foreach f,$(1), \ $(eval PRODUCT_MAKEFILES :=) \ $(eval LOCAL_DIR := $(patsubst %/,%,$(dir $(f)))) \ $(eval include $(f)) \ $(PRODUCT_MAKEFILES) \ ) \ $(eval PRODUCT_MAKEFILES :=) \ $(eval LOCAL_DIR :=) \ ) endef
4.2 把所有产品定义import进来
$(call import-products, $(get-all-product-makefiles))
4.3 对所有产品makefile调用import-nodes
import-nodes
的第2个参数就是所有的产品makefile列表
define import-products $(call import-nodes,PRODUCTS,$(1),$(_product_var_list)) endef
4.4 对每个产品makefile调用 _import-nodes-inner
,并用 move-var-list
把 PRODUCT_xxx
变量重命名
define import-nodes $(if \ $(foreach _in,$(2), \ $(eval _node_import_context := _nic.$(1).[[$(_in)]]) \ $(if $(_include_stack),$(eval $(error ASSERTION FAILED: _include_stack \ should be empty here: $(_include_stack))),) \ $(eval _include_stack := ) \ $(call _import-nodes-inner,$(_node_import_context),$(_in),$(3)) \ $(call move-var-list,$(_node_import_context).$(_in),$(1).$(_in),$(3)) \ $(eval _node_import_context :=) \ $(eval $(1) := $($(1)) $(_in)) \ $(if $(_include_stack),$(eval $(error ASSERTION FAILED: _include_stack \ should be empty here: $(_include_stack))),) \ ) \ ,) endef
4.5 调用 _import-node
同时注意如果已经import过了的话,就不重复import了(用.seen变量来标记)。
也就是解决了上面说的直接 include
的话没法解决的多重继承问题。
define _import-nodes-inner $(foreach _in,$(2), \ $(if $(wildcard $(_in)), \ $(if $($(1).$(_in).seen), \ $(eval ### "skipping already-imported $(_in)") \ , \ $(eval $(1).$(_in).seen := true) \ $(call _import-node,$(1),$(strip $(_in)),$(3)) \ ) \ , \ $(error $(1): "$(_in)" does not exist) \ ) \ ) endef
4.6 _import-node
会对所有继承下来的 makefile 去递归调用 import-nodes-inner
(后者又会调 _import-node
回来)
这里是它真正 include
继承下来的 makefile的时候了,并且 include 之前与
之后它会clear-var-list,把所有 PRODUCT_xxx
变量都清空。当然,第二次清
空之前它会调用 copy-var-list 把所有 PRODUCT_XXX
给保存下来。
所以被继承的makefile不需要担心应该是使用 :=
还是 +=
来设置那些变量,
因为有保存了嘛。这也是之前提到的直接用 include
无法解决的一个问题。
最后的 _expand-inherited-values
实在是一个 brain teaser,但是其基本的
意思就是把继承过来的子makefile里设的产品变量的值给摘到真正的产品的变量
设置上。
顺便说一句,里面有一句 $(eval $(warning...))
是我加的,在Makefile里调
试一些宏的定义时可能需要这样做,直接用 $(warning)
而不用 $(eval)
的
话可能会出错。
在Makefile里几乎所有的变量都是全局的变量,除了那些宏调用的时候的
$(1), $(2)...
等是自动局部(只读)变量,而Android通过这种做法,硬生生
地造出一堆类似于伪自动局部变量,不服不行…
define _import-node $(eval _include_stack := $(2) $$(_include_stack)) $(call clear-var-list, $(3)) $(eval LOCAL_PATH := $(patsubst %/,%,$(dir $(2)))) $(eval MAKEFILE_LIST :=) $(eval include $(2)) $(eval $(warning importing node $(2), context is $(_node_import_context))) $(eval _included := $(filter-out $(2),$(MAKEFILE_LIST))) $(eval MAKEFILE_LIST :=) $(eval LOCAL_PATH :=) $(call copy-var-list, $(1).$(2), $(3)) $(call clear-var-list, $(3)) $(eval $(1).$(2).inherited := \ $(call get-inherited-nodes,$(1).$(2),$(3))) $(call _import-nodes-inner,$(1),$($(1).$(2).inherited),$(3)) $(call _expand-inherited-values,$(1),$(2),$(3)) $(eval $(1).$(2).inherited :=) $(eval _include_stack := $(wordlist 2,9999,$$(_include_stack))) endef