前言
Android中C运用 讲解(一)中主要表述了如何建一个JNI DEMO,什么是JNI,什么是NDK。接下来我要为大家总结Android.mk的编译姿势和cmaklist的编译姿势,这篇文章先为大家带来android.mk编译姿势讲解。
感谢:https://blog.csdn.net/ruglcc/article/details/7814546/(如作者觉得侵权,请及时联系本人)
感谢:《GNU Make手册》。
感谢:https://blog.csdn.net/gjq_1988/article/details/12573721(如作者觉得侵权,请及时联系本人)
背景
- 一般来说,无论是C、C++、还是pas,首先要把源文件编译成中间代码文件,在Windows下也就是 .obj 文件,UNIX下是 .o 文件,即 Object File,这个动作叫做编译(compile)。然后再把大量的Object File合成执行文件,这个动作叫作链接(link)。
- 编译时,编译器需要的是语法的正确,函数与变量的声明的正确。对于后者,通常是你需要告诉编译器头文件的所在位置(头文件中应该只是声明,而定义应该放在C/C++文件中),只要所有的语法正确,编译器就可以编译出中间目标文件。一般来说,每个源文件都应该对应于一个中间目标文件(O文件或是OBJ文件)。
- 链接时,主要是链接函数和全局变量,所以,我们可以使用这些中间目标文件(O文件或是OBJ文件)来链接我们的应用程序。链接器并不管函数所在的源文件,只管函数的中间目标文件(Object File),在大多数时候,由于源文件太多,编译生成的中间目标文件太多,而在链接时需要明显地指出中间目标文件名,这对于编译很不方便,所以,我们要给中间目标文件打个包,在Windows下这种包叫“库文件”(Library File),也就是 .lib 文件,在UNIX下,是Archive File,也就是 .a 文件。
- 总结一下,源文件首先会生成中间目标文件,再由中间目标文件生成执行文件。在编译时,编译器只检测程序语法,和函数、变量是否被声明。如果函数未被声明,编译器会给出一个警告,但可以生成Object File。而在链接程序时,链接器会在所有的Object File中找寻函数的实现,如果找不到,那到就会报链接错误码(Linker Error),在VC下,这种错误一般是:Link 2001错误,意思说是说,链接器未能找到函数的实现。你需要指定函数的ObjectFile.
Android编译C、C++ 需要:1️⃣、Android.mk 2️⃣、Application.mk 3️⃣、ndk-build 。
1️⃣、Android.mk 本质上是android编译环境下的一种特殊的“makefile”文件, 它是经过了android编译系统处理的。
那么什么是Makefile让我们来看看怎么说:
Makefile 介绍
百科的诠释:一个工程中的源文件不计其数,其按类型、功能、模块分别放在若干个目录中,makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作,因为 makefile就像一个Shell脚本一样,其中也可以执行操作系统的命令。
百科里面说的并不是很精髓,我把《GNU Make手册》中一句话粘贴出来:You need a file called a makefile to tell make
what to do. Most often, the makefile tells make
how to compile and link a program.
简单的说:就是我们需要一个文件来告诉make如何去编译、链接一个程序,这个文件的名字是makefile
Android.mk和makefile的联系
makefile的描述已经说过了,android.mk描述的是android编译环境下的一种特殊的“makefile”。那首先我们来阐释一下android编译环境:是android顶层目录下的build目录里面的一系列编译控制文件,其实就是一系列makefile文件和 .mk 文件,这些文件才是编译android系统完整的makefile文件. 每个模块里的android.mk只不过是被包含进android编译系统的一小部分而已。经过android编译系统的一大堆处理,android.mk的格式就变得非常简单,且与普通的makefile文件书写格式不一样了,但这有利于为Android增加一个新的Component。
但归根结底,android.mk文件最终还是要被android编译系统层层解析,转化为make命令能够读懂的格式,从而调用gcc编译器进行编译。
Android.mk只不过是被android编译系统包含的一种文件,需要在android编译系统的支持下解析。本质上android模块在编译时最终还是使用make命令解析一大堆makefile 和.mk文件(有些文件以.mk为后缀,应该也是按照makefile文件的规则来解析)
Android.mk详解
Android.mk
文件位于项目 jni/
目录的子目录中,用于向构建系统描述源文件和共享库。 它实际上是构建系统解析一次或多次的微小 GNU makefile 片段。 Android.mk
文件用于定义 Application.mk
、构建系统和环境变量所未定义的项目范围设置。 它还可替换特定模块的项目范围设置。
Android.mk
的语法用于将源文件分组为模块。 模块是静态库、共享库或独立可执行文件。 可在每个 Android.mk
文件中定义一个或多个模块,也可在多个模块中使用同一个源文件。 构建系统只会将共享库放入应用软件包。 此外,静态库可生成共享库。
基础知识
标识C/C++源文件。在该项目中,my-dir 是一个由编译系统提供的宏函数,用于返回Android.mk所在目录的路径。
LOCAL_PATH := $(call my-dir)
CLEAR_VARS编译系统预定义的一个变量,它指向一个特殊的Makefile,这个Makefile负责清除 LOCAL_xxx 的变量
include $(CLEAR_VARS)
用来指定模块编译时的其余连接器标志。例如:这里表示它依赖于系统提供的 liblog.so
LOCAL_LDLIBS :=-llog
LOCAL_MODULE 用来给每个模块定义一个名字,不同模块的名字不能相同,不能有空格。这里的名字会传给NDK编译系统,然后加上lib前缀和.so后缀 (例如,变成libhello-jni.so)。注意,如果你在LOCAL_MODULE定义中自己加上了lib前缀,则ndk在处理的时候就不会再加上lib前缀了(为了兼容Android系统的一些源码)。
LOCAL_MODULE := test
定义C++文件路径
MY_CPP_LIST := $(wildcard $(LOCAL_PATH)/src/*.cpp)
定义C文件路径
MY_CPP_LIST += $(wildcard $(LOCAL_PATH)/src/*.c)
在LOCAL_SRC_FILES 变量里面列举出对应于同一个模块的、要编译的那些文件,这里不要把头文件加进来,编译系统可以自动检测头文件依赖关系。默认情况下,C++源码文件的扩展名应该是cpp
LOCAL_SRC_FILES := $(MY_CPP_LIST)
指定应该链接到当前模块的静态库(可指定多个)。当前模块是动态库时,该选项才有意义。
LOCAL_STATIC_LIBRARIES := cpufeatures
用来指定模块编译时的其余连接器标志
# LOCAL_LDLIBS += -L$(SYSROOT)/usr/lib -llog
# NDK_DEBUG = 1
这个 BUILD_SHARED_LIBRARY也是预定义的变量,也是指向一个Makefile,负责将你在 LOCAL_XXX 等变量中定义信息收集起来,确定要编译的文件,如何编译。如果要编译的是静态库而不是动态库,则可以用 BUILD_STATIC_LIBRARY。
include $(BUILD_SHARED_LIBRARY)
import-module该函数用于按模块名查找另一个模块的Android.mk文件,并包含进来。
$(call import-module,android/cpufeatures)
更全更详细Android 官方提供的文档:https://developer.android.com/ndk/guides/android_mk?hl=zh-cn
接下来我们说下Application.mk:
Application.mk详解
官方描述:Application.mk 文件实际上是定义要编译的多个变量的微小 GNU Makefile 片段。 它通常位于 $PROJECT/jni/ 下,其中 PROJECT 指向应用的项目目录。 另一种方式是将其放在顶级 NDK/apps/目录的子目录下。
APP_PROJECT_PATH 这个变量存储应用程序的项目根目录的绝对路径。
APP_OPTIM 配置release和debug
APP_CFLAGS 这个变量存储一组构建系统的C编译器标志传递给编译器编译任何C或c++源代码的任何模块,可以修改应用需要的构建模块而不用修改Android.mk文件
APP_CPPFLAGS 和 APP_CFLAGS类似
APP_LDFLAGS A set of linker flags that the build system passes when linking the application,只对 shared libraries 和 executables有效
APP_BUILD_SCRIPT 指定Android.mk文件
APP_ABI 指定abi
APP_PLATFORM 指定android api版本
APP_STL 链接其他的c++支持
NDK_TOOLCHAIN_VERSION gcc编译版本
APP_PIE
官方地址:https://developer.android.com/ndk/guides/application_mk?hl=zh-cn
Androidmk更多的是侧重于定义源文件、依赖库之间的关系、模块名称等等。
Application.mk更侧重于指定基于什么环境(C++11库等)、关联哪些库编译的编译参数。
ndk-build
ndk-build 文件是 Android NDK r4 中引入的一个 shell 脚本。其用途是调用正确的 NDK 构建脚本。
内部构建
运行 ndk-build 脚本相当于运行以下命令:
$GNUMAKE -f /build/core/build-local.mk
$GNUMAKE 指向 GNU Make 3.81 或更新版本,
从命令行调用
ndk-build
文件位于 NDK 安装目录的顶层。若要从命令行运行该文件,请在应用项目目录中或其子目录中调用它。例如:
在此示例中,
指向项目的根目录,
是您安装 NDK 的目录。
选项
ndk-build 的所有参数将直接传递到运行 NDK 构建脚本的底层 GNU make
。 将 ndk-build
和表单 ndk-build
中的选项结合使用。 例如:
提供的选项如下:
移除以前生成的任意二进制文件。 clean
移除以前生成的任意二进制文件。
V=1
启动构建,并显示构建命令。
-B
强制执行完全的重新构建。
-B V=1
强制执行完全的重新构建,并显示构建命令。
NDK_LOG=1
显示内部 NDK 日志消息(用于调试 NDK 本身)。
NDK_DEBUG=1
强制执行可调试版构建(请参阅表 1)。
NDK_DEBUG=0
强制执行发布版构建(请参阅表 1)。
NDK_HOST_32BIT=1
始终使用 32 位模式下的工具链(请参阅 64 位和 32 位工具链)。
NDK_APPLICATION_MK=
使用 NDK_APPLICATION_MK 变量指向的特定 Application.mk 文件构建。
-C
构建位于
要求:
一般情况下,您需要安装 GNU Make 3.81 或更新版本才能使用 ndk-build 或 NDK。构建脚本将检测不兼容的 Make 工具,并生成错误消息。
如果您已安装 GNU Make 3.81,但默认 make 命令不能启动它,则在您的环境中定义 GNUMAKE 以便在启动 ndk-build 之前指向它。 例如:
$ export GNUMAKE=/usr/local/bin/gmake
$ ndk-build
您可以在 $NDK/prebuilt/
$ export NDK_HOST_AWK=
$ export NDK_HOST_ECHO=
$ export NDK_HOST_CMP=