Android 源码编译 及 mk文件解读

Android 源码编译

参考:探索Android FrameWork底层开发视频_哔哩哔哩_bilibili

基础

1.源代码编译 分为2个部分:
(1)boot/Kernel
(2)Android

2.linux

配置环境

Android 源码编译 及 mk文件解读_第1张图片

配置java环境

3.Android 源码编译过程

Android 源码编译 及 mk文件解读_第2张图片

Android 源码编译 及 mk文件解读_第3张图片

Android 源码编译 及 mk文件解读_第4张图片

Android 源码编译 及 mk文件解读_第5张图片

动态静态库编译

Android 源码编译 及 mk文件解读_第6张图片

Android 源码编译 及 mk文件解读_第7张图片

Android 源码编译 及 mk文件解读_第8张图片

Android 源码编译 及 mk文件解读_第9张图片

Android 源码编译 及 mk文件解读_第10张图片

Android 源码编译 及 mk文件解读_第11张图片

Android 源码编译 及 mk文件解读_第12张图片

Android 源码编译 及 mk文件解读_第13张图片

基本语法

"#" 注释
"$()" 取值 引用变量值
“:=” 赋值
call 调用宏
\ 换行 引用多个文件 以 “\” 隔开
include 关键字 编译系统 引用宏????
+=  追加

mk语句大全

参考文献

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、LOCAL_MODULE_CLASS

指定模块的类型,可不用定义。

# 编译 apk 文件
LOCAL_MODULE_CLASS := APPS

# 编译 jar 包
LOCAL_MODULE_CLASS := JAVA_LIBRAYIES

# 定义动态库文件
LOCAL_MODULE_CLASS := SHARED_LIBRAYIES

# 编译可执行文件
LOCAL_MODULE_CLASS := EXECUTABLES

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

#-------------------------------------

1、Android.mk 文件解读

基本语法 参考 C/C++

Android.mk 文件用来告诉NDk编译系统,应该如何编译源码。确切的说该文件是一个小型的MakeFile,该文件会被NDK编译文件解析多次,所以要注意不能过多使用环境变量,以免第一次解析时产生的变量影响后面的解析。Android.mk 把源码组织成不同的模块,每个模块可以是一个静态库也可以是一个动态库。动态库才会被拷贝到安装包中,静态库只能用于编译生成动态库。

同一个Android.mk文件可以定义多个模块,不同的模块可以共用同一个源文件。

注意,NDK所用的Android.mk文件的语法与Android操作系统的开放源码中的Android.mk 的语法非常接近,但是两个编译系统中对Android.mk的使用方法不同,这是为了方便应用程序开发人员复用以前的代码。

2、简单例子

在详细讨论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 目录下有更加丰富的例子,里面都有详细的注释!!!!!!

3、变量名限制

以下变量 可以直接使用,也可以自己定义但是不能使用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)

4、NDK预定义变量

以下是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.so 生成的库为动态库

BUILD_STATIC_LIBRARY

与BUILD_SHARED_LIBRARY 对应 ,生成的库为静态库。静态库不会被拷贝到安装包中,往往用来编译其他动态库在,

用法:include$(BUILD_STATIC_LIBRARY) 默认文件名 lib.a

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 指向一个动态库,并且最后被打包到安装包中的库是这个动态库

注意:(静态库在程序编译时会被连接到目标代码中,程序运行时将不再需要该静态库。编译后程序文件大,但是加载快,个理性好。动态库在编译时并不会被连接到代码中,而是在程序运行时才被载入,因此在程序运行时还需动态库存在。多个应用程序可以使用同一个动态库,启动多个应用程序的时候,只需要将动态库加载到内存一次即可)

5、NDK 预定义的宏函数

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 不会传递给编译器。

你可能感兴趣的:(Android,android)