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