您的位置:首页 > 移动开发 > Android开发

Android-Product-Makefiles

2015-06-11 11:16 387 查看

Android-Product-Makefiles

1 到哪儿去找某Product的Makefiles?

Android的产品配置中,我们在build的时候用chooseproduct指定了产品名,比如叫
pxa988dkb_def
,那么,Android怎么知道应该去使用这个产品的Makefiles,而不是
pxa986ff_def
的Makefiles呢?

尤其是,它怎么知道到哪儿去找
pxa988dkb_def
的Makefiles呢?

答案是,它不知道。

Android的做法是把所有产品的定义都读进来,然后就能知道我们指定的产品名是不是有定义,有的话就使用它相关的定义就可以了。

这样做好像很笨,好像做了很多无用功,读了一些可能最后用不到的productmakefiles进来。但事实上不是那样的,因为否则的话我们就必须在指定产品名的同时再自己指定该产品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
也不能很好的处理 (比如,Ainclude 了 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

《 pdf-for-kindle-touch

Emacs和它的朋友们——阅读源代码篇
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: