当前位置:首页 > 嵌入式培训 > 嵌入式学习 > 讲师博文 > Android.mk分析

Android.mk分析 时间:2018-09-26      来源:未知

在Android的源码中每个目录下几乎都有一个Android.mk的文件,这个文件就是用来管理当前目录或子目录 下的文件进行编译的。我们打开 Android.mk文件,我们会发现它并没有太多的Makefile语法,更多的是一些大写的宏,例 如:LOCAL_PATH,LOCAL_MODULE等。此时我们就会在想,"Android源码下的文件是如何编译的呢?",实际上 Android源码已经有比较完善的编译系统,它是以模块化的思想设计编译系统的,Android源码的每个目录几 乎都可以当独编译,我们可以给予这个编译系统,编写自己的Android.mk文件,将我们需要的文件进行编 译。

Android的编译系统相关的文件存放在Android源码目录下的build/core目录下,这里不介绍Android源码的 编译系统,如果想了解Android编译系统的读者可以自己在网上查阅相关的资料。好了下面我们就来看看如何 编写自己的Android.mk吧!

一、简单的Android.mk

从编译一个史上第一个程序开始吧,编写编译hello.c的Android.mk。

hello.c文件中的内容我想你是会写的,这里就不贴出来了,下面我们来编译一下这个模块。

设置好了编译环境,下面我们就来编译我们的模块吧!我的模块放在Android源码目录下的external/test/test1目 录下。

嗯,我们很轻松的就完成了在Android源码目录下编译自己的模块,下面我们来详细分析一下每个变量的具体 含义。

(1) LOCAL_PATH := $(call my-dir)

$(call my-dir)这种写法是Makefile中调用一个自定义函数的写法,也就是获取my-dir这个自定义函数的结果。下 面我们来看看my-dir这个

函数具体是如何实现的?

我们重点关注红颜标注的地方,MAKEFILE_LIST是make解释器内部定义的变量,它的含义就是获取它所寻找 到的Makefile文件的绝对路径列表。当我们在源码目录的顶层目录进行"mmm"进行编译的时候,Android的编 译系统就会使用make工具找到相关的Makefile文件。我们的模块里面的Makefile就是后一个Makefile文件 了。

LOCAL_MODULE_MAKEFILE := $$(lastword $$(MAKEFILE_LIST)) 获取后一个Makefile文件绝对 路径

$(dir $(LOCAL_MODULE_MAKEFILE)) 获取目录路径,不包含文件 例如:$(dir /home/linux/Makefile) ->

/home/linux/

$(patsubst %/, %, /home/linux/) -> /home/linux

好了,我们总结一下 $(call my-dr)的终极含义:获取当前模块的路径

(2) include $(CLEAR_VARS)

CLEAR_VARS这个变量在Android源码树下的build/core/config.mk文件中定义:

include类似于C语言中的头文件包含,嗯,它的含义就是在我们的Android.mk文件中包含编译系统目录下的 clear_vars.mk这个文件中的内容。clear_vars.mk中就是将一些编译的时候需要用到的一些变量清空。但是我可 以肯定它一定不会把LOCAL_PATH这个变量清空,想想为什么?

(3)LOCAL_MODULE

用来指定当前模块的名称

(4)LOCAL_SRC_FILES

用来指定当前模块需要参与编译的文件

(5)include $(BUILD_EXECUTABLE)

BUILD_EXECUTABLE这个变量在Android源码树下的build/core/config.mk文件中定义:

executable.mk文件中定义了如何编译设备上的可执行文件。

二、编译模块下的多个文件

上面的Android.mk中我们只编译了一个文件,如果有多个文件需要编译该如何做呢?在Android的编译系统 中,我们有两种方法让多个文件

可以参与编译。

(1)将需要编译的文件名都指定在LOCAL_SRC_FILES变量 例如:在我们的test1目录下还有两个文件 add.c 和 sub.c ,这两个文件中的内容如下:

我们的Android.mk的内容如下:

LOCAL_MODULE_PATH = $(LOCAL_PATH)/bin

表示将编译好的模块存放在模块所在目录的bin子目录下

(2)调用Android编译系统的函数,获取当前模块下所有需要编译的文件

我们以获取C语言为例,来看看函数的具体实现:

(1)all-c-files-under

(2)all-subdir-c-files

嗯,对比一下两者的区别:

(1)all-c-files-under 比较灵活,可以指定模块下一个指定的子目录下搜索所有的c语言文件 (2)all-subdir-c-files 是获取模块下所有的子目录下的C语言文件 注意:在这里函数中,寻找Makefile文件的时候只会递归一级子目录

好了,我们来看看我们修改后的Android.mk文件吧!

我们把所有的C语言文件存放在了src子目录下,所以这里指定的是在src子目录下搜索。

我们在hello.c中调用了add和sub函数,没有声明,所以编译的时候报了警告,下面我们自己定义一个hello.h的头

文件,在这个头文件中 我们声明这两个函数。

问题:如何在Android.mk文件中指定自己的头文件搜索路径?

三、编译静态库和动态库

前面我们通过Android.mk将我们模块中的代码编译成了ELF格式的可执行文件,下面我们来看看如何将自己的 模块编译成库。

(1)BUILD_SHARED_LIBRARY

将模块编译成动态库 , 例如:libadd_sub.so

(2)BUILD_STATIC_LIBRARY

将模块编译成静态库 , 例如:libadd_sub.a

四、链接库

1、链接Android系统中自带的库

ALOGE是Android系统中用来输出log信息的函数,它在liblog.so中,所以我们在编译我们的代码时候,要告诉 编译系统,需要去连接liblog.so这个动态库。

我们的Android.mk写成如下形式:

(1)LOCAL_SHARED_LIBRARIES

告诉编译系统需要链接的Android系统提供的动态库

(2)LOCAL_STATIC_LIBRARIES

告诉编译系统需要链接的Android系统提供的静态库

2、链接第三方库 我们将add.c和sub.c编译成libadd_sub.so,然后我们在test.c中调用add和sub这两个函数,此时Android.mk应该写 成如下形式:

LOCAL_LDFLAGS

指定链接参数, -L 指定需要连接的库所在的路径, -l指定库的名字

五、预置编译

所谓的预置编译指的是将一个编译好的可执行文件或APK以及他们依赖的库、jar包拷贝到Android系统源 码相关的存放路径下,这样我们在对Android 系统就行打包生成system.img镜像时,这些东西就会被打包进 去。哦,还有就是当我们引入第三方库或

问:为了不自己手动把这些东西拷贝到Android源码对应的目录下,然后在打包呀? 答:麻烦,Android版本总是在升级,目录结构也总是在发生变化,使用预置编译,所以的事情都由Android系 统自带的编译系统去做,这样就何乐而不为呢?

下面我们以预置libadd_sub.so文件到Android系统中为例,来讲解预置编译的使用方法:

编译效果如下:

解释如下:

(1)LOCAL_MODULE

这里的含义和以前的含义不一样,它表示文件(xx.apk/jar/so)预置到系统中之后的名字。例如:libadd_sub.so预 置之后的名字为libaddsub.so

(2)LOCAL_SRC_FILES

需要预置到系统中的文件

(3)LOCAL_MODULE_TAGS

这个变量可以赋值为user 、eng、tests、optional

user 指该模块只在user版本下才编译 eng 指该模块只在eng版本下才编译 tests 指该模块只在tests版本下才编译 optional 指该模块在所有版本下都编译

(4)LOCAL_MODULE_CLASS

这个变量用来指定文件类型,它可以赋的值有

APPS apk文件

SHARED_LIBRARIES 动态库文件

JAVA_LIBRARIES dex归档文件 EXECUTABLES ELF格式文件

ETC 其他格式文件

(5)BUILD_PREBUILT 和 BUILD_MULTI_PREBUILT

不同点:

<1>BUILD_PREBUILT 只能针对一个文件, BUILD_MUTI_PREBUILT针对多个文件

<2>BUILD_PREBUILT 在预置的时候可以通过LOCAL_MODULE_PATH将文件拷贝到自己指定的路径下,而

BUILD_MULTI_PREBUILT只能将文件拷贝到Android系统指定的路径下。

六、引入第三方jar包

很多时候我们在做APP开发的时候都会用到第三方的jar包,那如何在Android系统中引入第三方jar包,编译

java代码生成apk文件呢?

我们来看看Android源码中packages/apps/Calculator目录下Android.mk的写法。

Calculator app应用程序使用到了第三方的arity-2.1.2.jar文件,而arity-2.1.2.jar文件在Calculator当前目录下,为了 方便引用arity-2.1.2.jar文件这里先通过预置编译将arity-2.1.2.jar拷贝到Android 系统的相应目录下,这样开始 编译Calculator应用程序的时候才可以找到它依赖的jar包。

我们看看它的编译流程:

make: Entering directory `/home/a/workdir/androidL'

target R.java/Manifest.java: Calculator (out/target/common/obj/APPS/Calculator_intermediates/src/R.stamp) target Prebuilt: Calculator (out/target/common/obj/JAVA_LIBRARIES/libarity_intermediates/classes.jar) target Prebuilt: Calculator (out/target/common/obj/JAVA_LIBRARIES/libarity_intermediates/javalib.jar) target Java: Calculator (out/target/common/obj/APPS/Calculator_intermediates/classes)

Copying: out/target/common/obj/APPS/Calculator_intermediates/classes-jarjar.jar

Copying: out/target/common/obj/APPS/Calculator_intermediates/emma_out/lib/classes-jarjar.jar Copying: out/target/common/obj/APPS/Calculator_intermediates/classes.jar

Proguard: out/target/common/obj/APPS/Calculator_intermediates/proguard.classes.jar ProGuard, version 4.10

Reading program jar [/home/a/workdir/androidL/out/target/common/obj/APPS/Calculator_intermediates/classes.jar] Reading library jar [/home/a/workdir/androidL/out/target/common/obj/JAVA_LIBRARIES/android_stubs_current_intermediates/classes.ja r]

Preparing output jar [/home/a/workdir/androidL/out/target/common/obj/APPS/Calculator_intermediates/proguard.classes.jar] Copying resources from program jar [/home/a/workdir/androidL/out/target/common/obj/APPS/Calculator_intermediates/classes.jar]

target Dex: Calculator

Copying: out/target/common/obj/APPS/Calculator_intermediates/classes.dex

target Package: Calculator (out/target/product/fspad-733/obj/APPS/Calculator_intermediates/package.apk) Notice file: packages/apps/Calculator/NOTICE -- out/target/product/fspad- 733/obj/NOTICE_FILES/src//system/app/Calculator/Calculator.apk.txt

Install: out/target/product/fspad-733/system/app/Calculator/Calculator.apk target Prebuilt: libarity (out/target/product/fspad- 733/obj/JAVA_LIBRARIES/libarity_intermediates/javalib.jar)

make: Leaving directory `/home/a/workdir/androidL'

好了,下面我们来看看这个Android.mk中我们前面没有用过的变量。

(1)LOCAL_STATIC_JAVA_LIBRARIES

指定当前模块依赖的jar包

(2)LOCAL_SDK_VERSION

指定当前SDK的版本为Android源码中的SDK版本

(3)LOCAL_PACKAGE_NAME

指定生成的apk文件的名字

(4)LOCAL_CERTIFICATE

指定apk文件的签名。可以指定的值有:testkey、media、platform、shared这四种,可以在源码 build/target/product/security里面看到对应的秘钥,其中shared.pk8代表私钥,shared.x509.perm代表公钥,一定 是成对出现的。

其中testkey是作为android编译的时候默认的签名key,如果系统中的apk的Android.mk中没有设置 LOCAL_CERTIFICATE的值,就默认使用testkey。而如果设置成LOCAL_CERTIFICATE :=platform就代表使用 platform来签名,这样的话这个apk就拥有了和system相同的签名,因为系统级别的签名也是使用platform来签 名的。

(5)BUILD_PACKAGE

将模块编译成APK文件

(6)LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES

指定prebuilt jar库的规则,格式 别名 : jar文件路径。注意:别名一定要与 LOCAL_STATIC_JAVA_LIBRARIES里所取的别名一致,且不含jar;jar文件路径一定要是真实的存放第 三方jar包的路径。编译用BUILD_MULTI_PREBUILT。

上一篇:Android init进程之如何进入java世界

下一篇:moc文件分析

热点文章推荐
华清学员就业榜单
高薪学员经验分享
热点新闻推荐
前台专线:010-82525158 企业培训洽谈专线:010-82525379 院校合作洽谈专线:010-82525379 Copyright © 2004-2022 北京华清远见科技集团有限公司 版权所有 ,京ICP备16055225号-5京公海网安备11010802025203号

回到顶部