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 基本上其它产品也都需要 包括,解决的方法有三种:

  1. 把generic里的定义抄一遍
  2. 把generic.mk给直接include进来
  3. 设计一个新的机制,把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.mkandroid/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-listPRODUCT_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