这里咱们向Android中添加本身的模块,只涉及到.so/*.a/elf source的编译,以及如何将prebuilt file添加进工程。对于APK以及jar的source暂时没有仔细研究,要添加进去的话能够参照/pacakge和/framework 里面的Android.mk ~~
首先是可能用获得的基础知识,必须了解基础Makefile的语法,而后下面是Andriod用来编译相应模块使用的核心makefile,固然若是要速成的话也能够不看这些东西,直接按照后面的例子添加就能够了:
一、prebuilt
/build/core/base_rules.mk
/build/core/prebuilt.mk
/build/core/multi_prebuilt.mk
二、.so/
/build/core/base_rules.mk
/build/core/shared_library.mk
/build/core/dynamic_library.mk
/build/core/binary.mk
三、.a
/build/core/base_rules.mk
/build/core/static_library.mk
/build/core/binary.mk
编写可执行文件基本上和.so是差很少的,如今分为两类来仔细讲一下,一类是prebuilt files的编译,另外就是.so/.a/elf的编译。
在全部这许makefile中最重要的是base_rules.mk,它是对module进行处理的核心过程,下面先看看这个文件的内容:
每一个模块在编译的时候都会产生一个编译目录和一个安装目录,编译目录就是这个模块编译之后生成的目标文件,安装目录就表明着这个模块是否会编译进文件系统,就是是否编译进IMG啦~~在base_rules.mk提供了两个变量来定义你要输出的目录,仔细弄懂对你了解编译后的生成目录是颇有帮助的~~
built_module_path := $(intermediates)
LOCAL_BUILT_MODULE := $(built_module_path)/$(LOCAL_BUILT_MODULE_STEM)
LOCAL_INSTALLED_MODULE := $(LOCAL_MODULE_PATH)/$(LOCAL_MODULE_SUBDIR)$(LOCAL_INSTALLED_MODULE_STEM)
built_module_path是编译生成的中间文件所在的目录,LOCAL_BUILT_MODULE_STEM就是你要生成的编译目标啦,若是本地模块指定了LOCAL_MODULE_STEM的话,它的值就是$(LOCAL_MODULE_STEM)$(LOCAL_MODULE_SUFFIX),若是没有指定了的话就是$(LOCAL_MODULE)$(LOCAL_MODULE_SUFFIX)。由此能够看到LOCAL_MODULE的定义是颇有讲究的,好比什么*.so,通常*就用来做模块名。LOCAL_MODULE_SUFFIX在编译不一样的模块时,GOOGLE内置会给你加上相应的值,若是你不了解的话仍是尽可能本身来指定,否则可能编译出来的东西被篡改了文件名哦噢~~
将编译目录的文件拷贝到安装目录就是咱们的LOCAL_INSTALLED_MODULE了,是否会安装就要看你定义的 LOCAL_MODULE_TAGS了,固然你也能够经过修改LOCAL_MODULE_PATH来自定义安装。LOCAL_MODULE_PATH是有个颇有用的变量,首先咱们看看当咱们在本地模块没有指定这个值的时候,它的值其实是:LOCAL_MODULE_PATH := $($(my_prefix)OUT$(use_data)_$(LOCAL_MODULE_CLASS)),若是你的模块定义了TAGS := TESTS则user_data的值是DATA,这样的模块会被安装在data/目录下,那么经过替换咱们就知道这个LOCAL_MODULE_PATH := TARGET_OUT_$(LOCAL_MODULE_CLASS)。这个LOCAL_MODULE_CLASS在特定的类型编译会被google赋值成固定内容,可是在prebuilt的编译中它是由你本身来赋值的,它的值就会用来定义生成的目录,好比LOCAL_MODULE_CLASS := ETC的时候,则就会被安装在/system/etc目录。那么咱们就知道如何来定义prebuilt模块里面的CLASS了,google还提供了一个 LOCAL_MODULE_SUBDIR可让你来定义子目录,可是要记得的是在每一个模块的最后要将这个值清空,由于默认CLEAR_VARS是不会清空这个值的。
固然前面说的是LOCAL_MODULE_PATH的默认值,咱们能够经过给它赋值来强制指定安装的目录,好比说要安装在system/etc /permissions目录,则能够强制指定它的值为$(TARGET_OUT_ETC)/permissions,这样模块就会被强制安装在这个目录了,给LOCAL_MODULE_PATH赋值的状况主要应用在prebuilt模块的编译上,其余的应该尽可能采用其默认值。
下面咱们就具体看看我写的一个如何编译本身的.so *.a elf的例子,具体能使用到的变量,和要注意的地方我都写出来了:
这里要说明的是这个prelink,prelinke只有在编译.so的时候才会有的选项,主要是经过预连接的方式来加快程序启动和执行的速度,若是你的本地模块prelink是真的话,那你要在build/core/prelink-linux-arm.map中定义你的库须要使用的空间,空间不够的话会报错(具体使用没仔细研究过)
下面再看看如何来将咱们本身的prebuilt files编译进工程,先让咱们和这个相关的最重要的两个makefile:
multi_prebuilt.mk只用来加入须要加入的各类库,仔细注意它里面的那个对加入文件进行处理的函数,尤为是 LOCAL_MODULE,LOCAL_MODULE_SUFFIX ,LOCAL_SRC_FILES这3个变量是如何获得的,使用multi_prebuilt编译进工程的文件都会自动打上USER的tag。并且它最后仍是会掉用prebuilt.mk来执行真正的编译操做。
prebuilt.mk实际上能够编译任何文件到咱们的工程中,下面是我写的一个例子,能够自动将目录下相应格式的文件编译进工程:
此外添加prebuilt.mk也有一个粗暴的方式,能够经过下面的规则简单的将全部prebuilt files添加到你工程中去,必须一个一个添加:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
# your prebuilt file name
LOCAL_MODULE := example.so
LOCAL_MODULE_TAGS := user eng
# your prebuilt file (must be relative directory )
LOCAL_SRC_FILES := lib/example.so
# the path your prebuilt file will be installed $(TARGET_OUT) is the system directory
LOCAL_MODULE_PATH := $(TARGET_OUT)/lib
include (BUILD_PREBUILT)
在最后说一下Android对模块惟一性检测的规则,在base_rules.mk里面经过module_id里来检测这个模块是否已经存在,咱们看看这个值是如何定义的:
module_id := MODULE.$(if \
$(LOCAL_IS_HOST_MODULE),HOST,TARGET).$(LOCAL_MODULE_CLASS).$(LOCAL_MODULE)
因此它是经过LOCAL_MODULE_CLASS和LOCAL_MODULE这两个变量来检测模块的惟一性,所以当你定义同同样的CLASS和MODULE的时候,Android就会报错,提示模块必须是惟一的。所以若是你将LOCAL_MODULE_CALSS进行修改,使用LOCAL_MODULE_PATH来指定安装目录的时候就会逃过Android对模块惟一性的检测,可是这样致使的问题就是同一个目标会有多个规则来实现它,并且每一个规则都是相同的命令。这样致使的结果就是MAKE会将全部的依赖放在一块儿,而后使用命令来将其生成目标,这样是很是危险的,会形成库的覆盖,并且不少时候你不知道它就究竟使用的是哪一个依赖来最终生成目标,只能用md5sum来检查。
所以当你使用prebuilt来覆盖系统原有的文件的时候应该特别当心,若是你确认你的文件在覆盖系统原有文件之后系统能正常运行的话,好的方法是仍是打开Android对module_id的检测,同时把原来生成这个文件的Makefile进行修改,让其不编译出这个文件~
最后补充一个Android如何来存放模块的编译中间文件: 一、若是你的LOCAL_MODULE_CLASS包含COMMON_MODULE_CLASS := JAVA_LIBRARIES NOTICE_FILES,则你编译出的中间文件会放在: $(TARGET__OUT_COMMON_INTERMEDIATES)/$(LOCAL_MODULE_CLASS)/$(LOCAL_MODULE)_intermediates/ TARGET_OUT_COMMON_INTERMEDIATES := out/target/common/obj 二、若是你的LOCAL_MODULE_CLASS不包含COMMON_MODULE_CLASS,则你编译出的中间文件会放在: $(TARGET__OUT_INTERMEDIATES)/$(LOCAL_MODULE_CLASS)/$(LOCAL_MODULE)_intermediates/ TARGET_OUT_INTERMEDIATES := out/target/product/msm7627_ffa/obj