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和它的朋友们——阅读源代码篇
相关文章推荐
- Error:(32) android studio开发,报错undefined reference to `AndroidBitmap_getInfo'
- android系统如何自适应屏幕大小
- 【android】[SQlite] common commands explanations
- Android 框架练成 教你打造高效的图片加载框架
- android aidl详解
- Android-->最全获取本地IP的方法(Wifi/以太网/3G)
- android数据存储读取6:contentProvider的使用(提供自己应用的数据)
- 判断Android程序是否在前台运行的两种方法
- Android自动化测试总结
- Android中SharedPreferences的使用
- [Android] 环境配置之Android Studio开发NDK
- Android官方文档-Bound Services(绑定服务)
- Android 里面的多线程操作备忘,关于Thread,Handler, Looper
- Android官方文档-Services(服务)
- Android 从入门到精通 第七章 事件
- [Android][Activity] XML parse
- Android中ExpandableListView的使用
- android调用系统相机并调整照片大小保存,最后上传照片
- Android自定义DataTimePicker(日期选择器)
- android布局属性详解