参考:探索Android FrameWork底层开发视频_哔哩哔哩_bilibili
1.源代码编译 分为2个部分:
(1)boot/Kernel
(2)Android
2.linux
配置环境
配置java环境
3.Android 源码编译过程
动态静态库编译
"#" | 注释 |
"$()" | 取值 引用变量值 |
“:=” | 赋值 |
call | 调用宏 |
\ | 换行 引用多个文件 以 “\” 隔开 |
include | 关键字 编译系统 引用宏???? |
+= | 追加 |
参考文献
Android build系统中常用“LOCAL_” 变量_行者无疆-超越_新浪博客
常用 宏 | |
all-makefiles-under | 宏定义路径 build/core/definitions.mk 一个位于当前'my-dir'路径的子目录中的所有Android.mk的列表 加载当前目录下的所有 mk 文件 |
my-dir | 返回当前 Android.mk 文件所在的路径 |
all-java-files-under | 宏的定义路径 build/core/definitions.mk 返回 LOCAL_PATH 路径下的所有java源文件列表 |
特殊变量 | |
LOCAL_PATH | 返回当前mk文件路径 |
LOCAL_PACKAGE_NAME | 指定应用(APP)名称 |
LOCAL_MODULE | 指定除app 以外的模块名称 |
CLEAR_VARS | 指向 clear_vars.mk 清除 LOCAL_PATH 以外 以LOCAL 开头的变量 |
LOCAL_MODULE_TAGS = user/eng/tests/optional | 定义模块标签,build系统会根据标签 决定安装哪些模块 user:指该模块只在user版本下才编译 eng:指该模块只在eng版本下才编译 tests:指该模块只在tests版本下才编译 optional:指该模块在所有版本下都编译 |
LOCAL_JAVA_LIBRARIES | 指定模块依赖的java 共享库 |
LOCAL_SRC_FILES | 指定源文件列表 |
BUILD_PACKAGE | 指向 package.mk |
LOCAL_MODULE_CLASS | 6、 指定模块的类型,可不用定义。 |
COMMON_PACKAGE_SUFFIX:=.zip |
包名后缀 |
COMMON_JAVA_PACKAGE_SUFFIX:=.jar | 包名后缀 |
COMMON_ANDROID_PACKAGE_SUFFIX:=.apk | 包名后缀 |
LOCAL_REQUIRED_MODULES | 依赖的模块 |
#------------------------------------
android源码目录下的build/envsetup.sh文件,描述编译的命令
m,mm,mmm
- m: Makes from the top of the tree.
- mm: Builds all of the modules in the current directory, but not their dependencies.
- mmm: Builds all of the modules in the supplied directories, but not their dependencies. To limit the modules being built use the syntax: mmm dir/:target1,target2.
- mma: Builds all of the modules in the current directory, and their dependencies.
- mmma: Builds all of the modules in the supplied directories, and their dependencies.
===============================================
m:编译所有模块
mm:编译 当前目录下的所有模块 不包含依赖
mmm:编译指定目录下的所有模块 不包含依赖
mma:编译当前目录下的所有模块 包含依赖
mmma:编译指定目录下的所有模块 包含依赖
使用方法:
1,. build/envsetup.sh
2,mmm + 指定模块路径
或者
1,. build/envsetup.sh
2,cd + 指定路径
3,mmm
#-------------------------------------
基本语法 参考 C/C++
Android.mk 文件用来告诉NDk编译系统,应该如何编译源码。确切的说该文件是一个小型的MakeFile,该文件会被NDK编译文件解析多次,所以要注意不能过多使用环境变量,以免第一次解析时产生的变量影响后面的解析。Android.mk 把源码组织成不同的模块,每个模块可以是一个静态库也可以是一个动态库。动态库才会被拷贝到安装包中,静态库只能用于编译生成动态库。
同一个Android.mk文件可以定义多个模块,不同的模块可以共用同一个源文件。
注意,NDK所用的Android.mk文件的语法与Android操作系统的开放源码中的Android.mk 的语法非常接近,但是两个编译系统中对Android.mk的使用方法不同,这是为了方便应用程序开发人员复用以前的代码。
在详细讨论Android.mk文件的语法之前,先看一个简单的“Hello JNI”的例子。文件位于apps/hello-jni/project。其中的src子目录存放Android工程的JAVA源码,jni子目录存放c/c++ 源码文件,即jni/hello-jni.c(实现了一个返回字符串给虚拟机的函数)。jni/Android.mk 文件是这个模块的编译脚本,内容如下:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := hello-jni
LOCAL_SRC_FILES := hello-jni.c
include $(BUILD_SHARED_LIBRARY)
以上内容解释如下:
LOCAL_PATH := $(call my-dir)
每个Android.mk文件的开头都必须定义LOCAL_PATH 变量,这个变量被用来寻找C/C++源文件,该例中 my-dir是一个由编译系统提供的宏函数,用于返回Android.mk所在目录的路径。
include $(CLEAR_VARS)
CLEAR_VARS 是编译系统预定义的一个变量,指向特殊的Makefile,这个Makefile负责清除LOCAL_XXX的变量(例如 LOCAL_MODULE ,LOCAL_SRC_FILES, LOCAL_STATIC_LIBRARIES)但不会清除 LOCAL_PATH,之所以要清除这些变量是因为所有的编译控制文件是在一趟make 执行过程中完成的,而所有的变量都是全局的,会对其他的Android.mk 文件产生影响。
LOCAL_MODULE := hello-jni
LOCAL_MODULE 用来给每个模块定义一个名字,不同模块的名字不能相同,不能有空格,这里的名字会传给NDK编译系统,然后加上lib前缀和.so 后缀(例如 libhello-jni.so)注意如果在LOCAL_MODULE定义中,自己加上了lib前缀,则ndk在处理时候就不会再加上lib前缀了(为了兼容Android 系统的源码)。
LOCAL_SRC_FILES := hello-jni.c
在LOCAL_SRC_FILES 变量中列举出对应于同一个模块的、要编译的哪些文件,这里不要把头文件加载进来,编译系统可以自动检测头文件的依赖关系。默认情况下,C++源文件的扩展名应该是cpp,如果想修改将LOCAL_CPP_EXTENSION 修改为你想要的扩展名,注意句点 。例如:LOCAL_CPP_EXTENSION := .cXX
include $(BUILD_SHARED_LIBRARY) //确定编译的库是动态库还是静态库。
这个 BUILD_SHARED_LIVRARY 也是预定义的变量,也是指向一个Makefile,负责将在LOCAL_XXX等变量中定义信息收集起来,确定要编译的文件 如何编译。如果要编译的是静态库而不是动态库,则可以使用 BUILD_STATIC_LIBRARY。
在NDK 安装目录的samples 目录下有更加丰富的例子,里面都有详细的注释!!!!!!
以下变量 可以直接使用,也可以自己定义但是不能使用NDK 所保留的变量名:
以LOCAL_开头的名字 (例如 LOCAL_MODULE)
以PRIVATE_,NDK_, APP_ 开头的名字(供NDK 内部使用)
小写字母的变量名也不能使用(供NDK 内部使用,例如 my-dir)
例如:可以随便使用 MY_ 开头的变量名
MY_SOURCES:= foo.c
ifneq($(MY_CONFIG_BAR),)
MY_SOUCES += bar.c
endif
LOCAL_SRC_FILES += $(MY_SOURCES)
以下是NDk 提前定义好的变量 有时NDK会同时解析同一个 Android.mk文件多次,每次解析时这些变量的值可能不同
CLEAR_VARS
指向一个特殊的Makefile,负责清理LOCAL_XXX (除LOCAL_PATH外)的所有变量,一般在定义新的模块之前使用这个变量,用法:include $(CLEAR_VARS)
BUILD_SHARED_LIBRARY
该变量实际指向一个MakeFile 用来把所有名为LOCAL_XXX 变量中的信息收集起来然后确定如何把提供的源码编译成目标模块,
用法:include$(BUILD_SHARED_LIBRARY),编译生成的文件名lib
BUILD_STATIC_LIBRARY
与BUILD_SHARED_LIBRARY 对应 ,生成的库为静态库。静态库不会被拷贝到安装包中,往往用来编译其他动态库在,
用法:include$(BUILD_STATIC_LIBRARY) 默认文件名 lib
PREBUILT_SHARED_LIBRARY
该变量指向一个已经编译好的共享库。与BUILD_SHARED_LIBRARY 和 BUILD_STATIC_LIBRARY 不同 此时相应的LOCAL_SRC_FILES 不在指定源文件,而是指向这个预编译的共享库(如 foo/libfoo.so).可以再其他模块中使用LOCAL_PREBUILTS 变量来引用这个预编译模块。
PREBUILT_STATIC_LIBRARY
与PREBUILT_SHARED_LIBRARY相同,只不过这里是静态库。
预编译目的是为了在Android.mk文件中生成一个模块名字
include $(PREBUILT_SHARED_LIBRARY)
include $(PREBUILT_STATIC_LIBRARY)
主要用来包含第三方库,为第三方库在Android.mk中创建模块名字,被其他库使用。使用这种编译时,LOCAL_SOURCE_FILES 指向一个动态库,并且最后被打包到安装包中的库是这个动态库
注意:(静态库在程序编译时会被连接到目标代码中,程序运行时将不再需要该静态库。编译后程序文件大,但是加载快,个理性好。动态库在编译时并不会被连接到代码中,而是在程序运行时才被载入,因此在程序运行时还需动态库存在。多个应用程序可以使用同一个动态库,启动多个应用程序的时候,只需要将动态库加载到内存一次即可)
NDK 预定义的“函数”宏,用法是,$(call
my-dir
返回上一个被包含(include)的Makefile的路径,典型情况是,Android.mk 文件所在的路径。这个函数对LOCAL_PATH 特别有用,例如:
LOCAL_PATH := $(call-my-dir)
注意:由于make的工作原理,该函数返回的的确是上一个被包含的Makefile的路径(也即是返回的结果可能不是Android.mk文件所在的目录)。所以,包含了另外有一个文件之后就不要再使用my-dir了。
例如:
LOCAL_PATCH := $(call my-dir)
该模块其他声明
include $(LOCAL_PATCH)/foo/Android.mk
LOCAL_PATH := $(call my-dir)
另一个模块的声明
上述两个模块的声明存在的问题就是第二次调用 my-dir的时候,得到的是 $PATH/foo 而不是 $PATH,因为此前有一个include语句。
因此,最好在所有的include语句之前 定义好LOCAL_PATH:
LOCAL_PATH := $(call -my)
... declare one module
LOCAL_PATH := $(call my-dir)
... declare another module
# extra includes at the end of the Android.mk
include $(LOCAL_PATH)/foo/Android.mk
如果觉得这样做不方便,可以把第一次调用的结果保存到变量中,例如:
MY_LOCAL_PATH := $(call my-dir)
LOCAL_PATH := $(MY_LOCAL_PATH)
... declare one module
include $(LOCAL_PATH)/foo/Android.mk
LOCAL_PATH := $(MY_LOCAL_PATH)
... declare another module
all-subdir-makefiles
返回当前的my-dir目录下的所有子目录的Android.mk文件的列表。例如,文件组织如下:
sources/foo/Android.mk
sources/foo/lib1/Android.mk
sources/foo/lib2/Android.mk
如果 sources/foo/Android.mk 包含如下行:
include $(call all-subdir-makefiles)
那么,该文件将自动把 sources/foo/lib1/Android.mk 和 sources/foo/lib2/Android.mk 包含进来。当源码被组织成很多层次时可以利用该函数,默认情况下,NDK只会寻找 sources/*/Android.mk。
import-module
该函数用于按模块名查找另一个模块的Android.mk文件,并包含进来。用法如下:
$(call import-module,
上面将在 NDK_MODULE_PATH变量所指定的目录列表中寻找名为
可以参考 Import Module。
6、模块描述变量
下面这些变量用于对模块进行描述,这些变量应该在 include $(CLEAR_VARS) 和 include $(BUILD_XXXX) 之间定义好。
LOCAL_PATH (必须)
这个变量表示当前文件(一般是Android.mk)所在的路径,该变量很重要,必须定义(在Android.mk文件的开头处定义)。常见写法如下:
LOCAL_PATH := $(call my-dir)
该变量不会被 include $(CLEAR_VARS) 清空,所以不论Android.mk定义了几个模块,一个Android.mk只需要在开头定义一次即可。
LOCAL_MODULE (必须)
该变量定义当前模块的名字,名字必须唯一,不能有空格。这个变量必须在 include $(BUILD_XXX) 之前定义好。默认情况下,这里的名字会用来得到输出文件的名字。例如模块名为foo,则得到的输出文件为libfoo.so。但是,如果你要在其他模块的Android.mk文件或Application.mk中引用这个模块,应该用foo这个模块名,而不要用libfoo.so这个文件名。
LOCAL_MODULE_FILENAME (可选)
该变量可以用来重定义输出文件的名字。默认情况下,foo模块得到的静态库的名字为 libfoo.a,动态库的名字为libfoo.so(UNIX规范)。当定义了LOCAL_MODULE_FILENAME之后,输出文件名就是这个变量指定的名字,例如:
LOCAL_MODULE := foo-version-1
LOCAL_MODULE_FILENAME := libfoo
注意: LOCAL_MODULE_FILENAME不支持文件路径(所以不能有斜杠),不要写扩展名(文件路径和文件扩展名是由编译工具自动加上的)
LOCAL_SRC_FILES (必须)
该变量用来指定该模块对应的源文件,只把需要传给编译器的源文件名加进 LOCAL_SRC_FILES,编译系统会自动处理头文件依赖。这里的文件名都是以 LOCAL_PATH 作为当前目录的(即相对于LOCAL_PATH目录),例如:
LOCAL_SRC_FILES := foo.c toto/bar.c
注意:必须使用Unix风格的斜杠,Windows风格的斜杠不能正确处理。
LOCAL_CPP_EXTENSION (可选)
用来定义C++代码文件的扩展名。必须以句点开头(即 “.”),默认值是“.cpp”,可以修改,例如:
LOCAL_CPP_EXTENSION := .cxx
从 NDK r7 这个版本开始,该变量可以支持多个扩展名了,例如:
LOCAL_CPP_EXTENSION := .cxx .cpp .cc
LOCAL_CPP_FEATURES (可选)
该变量用来指定C++代码所依赖的特殊C++特性。例如,如果要告诉编译器你的C++代码使用了RTTI(RunTime Type Information):
LOCAL_CPP_FEATURES := rtti
如果要指定你的C++代码使用了C++异常,则:
LOCAL_CPP_FEATURES := exceptions
该变量可以同时指定多个特性。例如: LOCAL_CPP_FEATURES := rtti features
这个变量的作用就是在编译模块的时候,开启相应的编译器/链接器标志。对于预编译的文件,该变量表明该预编译的库依赖了这些特性,从而确保最后的链接工作正确进行。与该变量等价的做法是在LOCAL_CPPFLAGS中写上 -frtti -fexceptions 等标志选项。但是,推荐用这里的方法。
LOCAL_C_INCLUDES(非必须,但很重要)
一个路径的列表,是NDK根目录的相对路径(LOCAL_SRC_FILES中的文件相对于LOCAL_PATH)。当编译C/C++、汇编文件时,这些路径将被追加到头文件搜索路径列表中。例如:
LOCAL_C_INCLUDES := sources/foo
或者, LOCAL_C_INCLUDES := $(LOCAL_PATH)/../foo
这里的搜索路径会放在LOCAL_CFLAGS/LOCAL_CPPFALGS等标志的前面。 当使用ndk-gdb的时候,LOCAL_C_INCLUDES中的路径也会被用到。
LOCAL_LDLIBS(非必须,重要)
用来指定模块编译时的其余连接器标志。例如:
LOCAL_LDLIBS := -lz
告诉链接器在加载该共享库的时候必须链接 /system/lib/libz.so 这个共享库。
LOCAL_STATIC_LIBRARIES(非必须,但很重要)
指定应该链接到当前模块的静态库(可指定多个)。当前模块是动态库时,该选项才有意义。
LOCAL_SHARED_LIBRARIES(非必须,但很重要)
表示模块要链接的动态链接库,指定的是运行时该模块所依赖共享库(可指定多个)。这些信息是链接阶段必须的。
LOCAL_WHOLE_STATIC_LIBRARIES
它是LOCAL_STATIC_LIBRARIES的变体,用来表示它对应的模块对于linker来说应该是一个“whole archive”(见GNU linker 文档,关于 --whole-archive的资料)。当静态库之间有循环依赖时,会用到这个选项。注意,当编译动态库时,这个选项会强行把所有的对象文件组装到一起;不过,在编译可执行文件的时候情况不是这样的。
LOCAL_EXPORT_CFLAGS
这个变量定义一些C/C++编译器flags。这些flags(标志)会被追加到使用了这个模块(利用LOCAL_STATIC_LIBRARIES和LOCAL_SHARED_LIBRARIES)的模块的LOCAL_CFLAGS 定义中去。
假如foo模块的声明如下:
include $(CLEAR_VARS)
LOCAL_MODULE := foo
LOCAL_SRC_FILES := foo/foo.c
LOCAL_EXPORT_CFLAGS := -DFOO=1
include $(BUILD_STATIC_LIBRARY)
bar模块依赖foo模块,声明如下:
include $(CLEAR_VARS)
LOCAL_MODULE := bar
LOCAL_SRC_FILES := bar.c
LOCAL_CFLAGS := -DBAR=2
LOCAL_STATIC_LIBRARIES := foo
include $(BUILD_SHARED_LIBRARY)
因此在编译bar模块的时候,它的编译器标志就是 “-DFOO=1 -DBAR=2”。 导出的flags 加上本模块的 LOCAL_CFLAGS,成为最后传给编译器的flags。这样修改起来就很容易。这种依赖关系是可传递的,例如,如果zoo依赖bar,bar依赖foo,那么zoo就会同时有bar和foo的导出flags。
注意,编译模块自身时,不会使用它所导出的flags。例如在编译上面的foo模块时, -DFOO=1 不会传递给编译器。