@think3r
2019-01-18 22:02:05
JAVA
代码的 JAR 包
;JAVA
代码的 APK 应用程序
;C/C++
代码的 二进制可执行文件 ELF(BUILD_EXECUTABLE)
;C/C++
代码的库文件:
*.so (BUILD_SHARED_LIBRARY)
*.a (BULID_STATIC_LIBRARY)
Android.mk 是一个向 Android NDK 构建系统描述 NDK 项目的 GUN Makefile 片段。它是每一个 NDK 项目的必备组件。构建系统希望它出现在 jni 子目录中。下面是 hello-jni 项目中 Android.mk 文件的内容。
Android.mk 文件首先必须要指定 LOCAL_PATH 变量,用于查找源文件。由于一般情况下 Android.mk 和需要编译的源文件在同一目录下,所以定义成如下形式:
LOCAL_PATH := $(call my-dir)
将 LOCAL_PATH 变量定义成本文件所在目录路径;parent-makefile
返回调用树中父 Makefile 路径。即包含当前Makefile的Makefile 路径Android.mk 中可以定义多个编译模块,每个编译模块都是
include $(CLEAR_VARS)
开始,
-CLEAR_VARS:指向一个编译脚本
CLEAR_VARS
由编译系统提供,指定让 GNU MAKEFILE 为你清除除 LOCAL_PATH 以外的所有 LOCAL_XXX 变量,清楚它们可以避免冲突.include $(BUILD_XXX)
结束
BUILD_EXECUTABLE
生成 C/C++ 可执行文件;LOCAL_C_INCLUDES
: 可选变量,表示头文件的搜索路径
LOCAL_CFLAGS
LOCAL_CFLAGS-D
, 增加全局宏定义。LOCAL_CFLAGS := -DUSE_JSC
相当于在所有源文件中增加一个#define USE_JSCLOCAL_CFLAGS := -DUSE_COPY_BIT=1
相当于在所有源文件中增加一个#define USE_COPY_BIT 1LOCAL_MODULE
变量用来给这些模块设定一个唯一的名称(编译生成的目标名称):
LOCAL_MODULE := hello-jni
LOCAL_SRC_FILES
变量定义用来建立和组装这个模块的源文件列表,
LOCAL_SRC_FILES := hello-jni.c
LOCAL_LDLIBS
: 编译模块时要使用的附加的链接器选项。这对于使用 -l
前缀传递指定库的名字是有用的。
LOCAL_LDLIBS := -lz
表示告诉链接器生成的模块要在加载时刻链接到 /system/lib/libz.so
LOCAL_SHARED_LIBRARIES
表示模块在运行时要依赖的共享库(动态库),在链接时就需要,以便在生成文件时嵌入其相应的信息。
LOCAL_STATIC_LIBRARIES
表示该模块需要使用哪些静态库,以便在编译时进行链接。
LOCAL_WHOLE_STATIC_LIBRARIES
会加载整个静态库.
LOCAL_JNI_SHARED_LIBRARIES
定义了要包含的 so 库文件的名字,如果程序没有采用 jni,不需要;
LOCAL_JNI_SHARED_LIBRARIES := libxxx
这样在编译的时候,NDK 自动会把这个 libxxx 打包进 apk; 放在 your-apk/lib/
目录下对于 android 而言,android 使用的是 GNU 的 make,因此它的 makefile 格式也是 GNU 的 makefile 格式。现在网络上关于 makefile 最好的文档就是陈皓的《跟我一起写makefile》,这份文档对 makefile 进行了详细的介绍,因此推荐大家先看这份文档
android make 系统总共分为四层
在一个 makefile 文件中,可以一次性的 make 出多个目标!
常用的 mk 分类:
Android Build 系统用来编译 Android 系统,Android SDK 以及相关文档。该系统主要由 Make 文件,Shell 脚本以及 Python 脚本组成,其中最主要的是 Make 文件。众所周知,Android 是一个开源的操作系统。Android 的源码中包含了大量的开源项目以及许多的模块。不同产商的不同设备对于 Android 系统的定制都是不一样的。如何将这些项目和模块的编译统一管理起来,如何能够在不同的操作系统上进行编译,如何在编译时能够支持面向不同的硬件设备,不同的编译类型,且还要提供面向各个产商的定制扩展,是非常有难度的。但 Android Build 系统很好的解决了这些问题,这里面有很多值得我们开发人员学习的地方。
Build 系统中最主要的处理逻辑都在 Make 文件中,而其他的脚本文件只是起到一些辅助作用,由于篇幅所限,本文只探讨 Make 文件中的内容。
整个 Build 系统中的 Make 文件可以分为三类:
/build/core
(本文所提到的所有路径都是以 Android 源码树作为背景的,/
指的是源码树的根目录,与文件系统无关)目录下。/device/sony/it26
目录下的文件共同构成了对于 Sony LT26 型号手机的定义。Android.mk
,该文件中定义了如何编译当前模块。Build 系统会在整个源码树中扫描名称为 Android.mk
的文件并根据其中的内容执行模块的编译。Android 系统的编译环境目前只支持 Ubuntu 以及 Mac OS 两种操作系统。关于编译环境的构建方法请参见以下路径:http://source.android.com/source/initializing.html
获取到安卓源码后, 在其目录下执行如下的三条命令即可:
source build/envsetup.sh
lunch full-eng
make -j8
source build/envsetup.sh
引入了 build/envsetup.sh
脚本。该脚本的作用是初始化编译环境,并引入一些辅助的 Shell 函数,这其中就包括第二步使用 lunch 函数。
croot
切换到源码树的根目录;m
在源码树的根目录下执行 make
;mm
build 当前目录下的模块;mmm
bulid 指定目录下的模块;lunch full-eng
是调用 lunch 函数,并指定参数为 full-eng
。lunch 函数的参数用来指定此次编译的目标设备以及编译类型。在这里,这两个值分别是 “full”和“eng”。
make -j8
才真正开始执行编译。make 的参数 -j
指定了同时编译的 Job 数量,这是个整数,该值通常是编译主机 CPU 支持的并发线程总数的 1 倍或 2 倍(例如:在一个 4 核,每个核支持两个线程的 CPU 上,可以使用 make -j8 或 make -j16)。在调用 make 命令时,如果没有指定任何目标,则将使用 默认 的名称为 droid
目标,该目标会编译出完整的 Android 系统镜像。/out/host/
:该目录下包含了 针对主机的 Android 开发工具的产物 。即 SDK 中的各种工具,例如:emulator,adb,aapt 等。/out/target/common/
:该目录下包含了针对设备的共通的编译产物,主要是 Java 应用代码和 Java 库。/out/target/product/<product_name>/
:包含了针对特定设备的编译结果以及平台相关的 C/C++
库和二进制文件。其中,<product_name>是具体目标设备的名称。/out/dist/
:包含了为多种分发而准备的包,通过 make disttarget
将文件拷贝到该目录,默认的编译目标不会产生该目录。/out/target/product/<product_name>/
目录下。
system.img
:包含了 Android OS 的系统文件,库,可执行文件以及预置的应用程序,将被挂载为根分区。
ramdisk.img
:在启动时将被 Linux 内核挂载为只读分区,它包含了 /init 文件和一些配置文件。它用来挂载其他系统镜像并启动 init 进程。
userdata.img
:将被挂载为 /data,包含了应用程序相关的数据以及和用户相关的数据。
整个 Build 系统的入口文件是源码树根目录下名称为“Makefile”的文件,当在源代码根目录上调用 make 命令时,make 命令首先将读取该文件。Makefile 文件的内容只有一行:“include build/core/main.mk”。该行代码的作用很明显:包含 build/core/main.mk 文件。在 main.mk 文件中又会包含其他的文件,其他文件中又会包含更多的文件,这样就引入了整个 Build 系统。
Android 源码中包含了许多的模块,模块的类型有很多种,例如:Java 库,C/C++ 库,APK 应用,以及可执行文件等。 并且,Java 或者 C/C++ 库还可以分为静态的或者动态的,库或可执行文件既可能是针对设备(本文的 “设备” 指的是 Android 系统将被安装的设备,例如某个型号的手机或平板)的也可能是针对主机(本文的“主机”指的是开发 Android 系统的机器,例如装有 Ubuntu 操作系统的 PC 机或装有 MacOS 的 iMac 或 Macbook)的。不同类型的模块的编译步骤和方法是不一样,为了能够一致且方便的执行各种类型模块的编译,在 config.mk
中定义了许多的常量,这其中的每个常量描述了一种类型模块的编译方式,这些常量有:
这些常量的值都是另外一个 Make 文件的路径,详细的编译方式都是在对应的 Make 文件中定义的。这些常量和 Make 文件的是一一对应的,对应规则也很简单:常量的名称是 Make 文件的文件名除去后缀全部改为大写然后加上“BUILD_”作为前缀。例如常量 BUILD_HOST_PREBUILT 的值对应的文件就是 host_prebuilt.mk。
在源码树中,一个模块的所有文件通常都位于同一个文件夹中。为了将当前模块添加到整个 Build 系统中,每个模块都需要一个专门的 Make 文件,该文件的名称为“Android.mk”。Build 系统会扫描名称为“Android.mk”的文件,并根据该文件中内容编译出相应的产物。
需要注意的是:在 Android Build 系统中,编译是以模块(而不是文件)作为单位的,每个模块都有一个唯一的名称,一个模块的依赖对象只能是另外一个模块,而不能是其他类型的对象。对于已经编译好的二进制库,如果要用来被当作是依赖对象,那么应当将这些已经编译好的库作为单独的模块。对于这些已经编译好的库使用 BUILD_PREBUILT 或 BUILD_MULTI_PREBUILT。例如:当编译某个 Java 库需要依赖一些 Jar 包时,并不能直接指定 Jar 包的路径作为依赖,而必须首先将这些 Jar 包定义为一个模块,然后在编译 Java 库的时候通过模块的名称来依赖这些 Jar 包。
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。