Android NDK学习(二):编译脚本语法Android.mk和Application.mk

时间:2021-09-22 13:11:56

一、Android.mk

Android.mk分为一下几部分:

  • LOCAL_PATH:= $(call my-dir), 返回当前文件在系统中的路径,Android.mk文件开始时必须定义该变量。
  • include $(CLEAR_VARS), 表明清楚上一次构建过程中的所有全局变量,因为在一个Makefile编译脚本中,会使用到大量的全局变量,使用这行脚本表明需要清除所有的全局变量。
  • LOCAL_SRC_FILES,要编译的C或者CPP的文件,注意这里不需要列举头文件,构建系统会自动帮助开发者依赖这些文件。
  • LOCAL_STATIC_LIBRARIES,所依赖的静态库文件
  • LOCAL_LDLIBS:=-L$(SYSROOT)/usr/lib -llog -lOPENSLES -lGLESv2 -lEGL -lz,指定编译过程所依赖的NDK提供的动态和静态库,SYSROOT变量代表的是NDK_ROOT下面的目录$NDK_ROOT/platforms/android-18/arch-arm,而在这个目录的usr/lib/目录下有很多对应的so的动态库以及.a的静态库。
  • LOCAL_CFLAGS,编译C或者cpp的编译标志,在实际编译的时候会发送给编译器。比如常用的实例是加上-DAUTO_TEST,然后在代码中就可以利用条件判断#ifdef AUTO_TEST来做一些与自动化测试相关的事情。
  • LOCAL_LDFLAGS,链接标志的可选列表,当对目标文件进行链接以生成输出文件的时候,将这些标志带给链接器。该指令与LOCAL_LDLIBS有些类似,一般情况下,该选项会用于指定第三方编译的静态库,LOCAL_LDLIBS经常用于指定系统的库(比如log,OpenGL ES, EGL等)。
  • LOCAL_MODULE,该模块的编译的目标名,用于区分各个模块,名字必须是唯一并且不包含空格的,如果编译目标是so库,那么该so库的名字就是lib项目名.so。
  • include$(BUILD_SHARED_LIBRARY),其实类似的include还有很多,都是构建系统提供的内置变量,该变量的意义就是构建动态库,其他的内置变量还包括如下几种:

  --- BUILD_STATIC_LIBRARY:构建静态库

     --- PREBUILD_STATIC_LIBRARY:对已有的静态库进行包装,使其成为一个模块。

     --- PREBUILD_SHARE_LIBRARY:对已有的动态库进行包装,使其成为一个模块。

   --- BUILD_EXECUTABLE:构建可执行文件。

构建系统提供好的这些内置变量在哪里能看大呢?他们都是在$NDK-ROOT/build/core/目录下,这里会有所有预先定义好的Makefile,开发者include一个变量,实际上就是把对应的Makefile包含到Android.mk中,包括前面提到的CLEAR_VARS,其也是该目录下面的一个Makefile。

  • include$(call all-makefiles-under, $(LOCAL_PATH)), 也是构建系统提供的变量,该命令会返回该目录下所有子目录的Android.mk列表。

LOCAL_SHARED_LIBRARIES 与 LOCAL_LDLIBS,LOCAL_LDFLAGS的区别?

  • LOCAL_LDLIBS :链接的库不产生依赖关系,一般用于不需要重新编译的库,如库不存在,则会报错找不到。且貌似只能链接那些存在于系统目录下本模块需要连接的库。如果某一个库既有动态库又有静态库,那么在默认情况下是链接的动态库而非静态库。如:LOCAL_LDLIBS += -lm –lz –lc -lcutils –lutils –llog …如果你的Android.mk文件中只有这么一行,那么将会采用动态链接。
  • LOCAL_SHARED_LIBRARIES 会生成依赖关系,当库不存在时会去编译这个库。
  • LOCAL_LDFLAGS:这个编译变量传递给链接器一个一些额外的参数,比如想传递给外面的库和库路径给ld,或者传递给ld linker的一些链接参数,-On,-EL{B}(大小端字节序),那么就要加到这个上面,如:LOCAL_LDFLAGS += -L(LOCALPATH)/lib/−lHWrecog–EBEL–On…或者直接加上绝对路径库的全名:LOCALLDFLAGS+=(LOCALPATH)/lib/−lHWrecog–EBEL–On…或者直接加上绝对路径库的全名:LOCALLDFLAGS+=(LOCAL_PATH)/lib/libHWrecog.a –EB{EL} –O{n}

注:如果是非系统的第三方库,貌似只能用LOCAL_LDFLAGS方式,LOCAL_LDLIBS方式不行。

二、Application.mk

Application.mk 分为以下几个部分:

  • APP_ABI :=xxx , 这里的xxx是指不同的平台,可以选填的有x86、mips、armeabi、armeabi-v7a,all等,值得一提的是若选择all会构建出来所有平台的so,如果不填写此项,默认构建为armeabi平台下的库。
  • APP_STL := gnustl_static, NDK构建系统提供了由Android系统给出的最小C++运行时库(/system/lib/libstdc++.so)的C++头文件。然后NDK带有另一个C++实现,开发者可以在自己的应用程序中使用或链接它,定义APP_STL可以选择他们中的一个,包括stlport_static、stlport_shared、gnustl_static.
  • APP_CPPFLAGS := -std=gnu++11 -fexceptions,指定编译过程中的flag,可以在该选项中开启 exception rtti 等特性,但为了效率,最好关闭rtti。
  • NDK_TOOLCHAIN_VERSION = 4.8,指定交叉工具编译链里面的版本号,这里指定使用4.8。
  • APP_PLATFORM:= android-9,指定创建的动态库的平台。
  • APP_OPTIM := release, 该变量是可选的,用来定义‘release’或者‘debug’,‘release’模式是默认的,并会生成高度优化的二进制代码,’debug‘模式生成的是未优化的二进制代码,但是可以检测出很多的bug,经常用于调试阶段,也相当于在ndk-build指令后面加上参数 NDK-DEBUG=1。

三、实例分析

1. 在上面我们了解了Android.mk里的基本语法规则,那么在输入ndk-build之后,,系统到底会使用哪些编译器以及打包器和链接器来编译我们的程序呢?

答:会使用$NDK_ROOT/toolChains/arm-linux-androideabi-4.8/prebuilt/darwin-x86_64/bin/ 目录下面的gcc、g++、ar、ld等工具。同样在该目录下的strip工具将会用于清除so包里面的源码,nm工具可以供开发者查看静态库下的符号列表。

2. 那么进行gcc编译的时候,头文件放在哪里呢?

答:$NDK_ROOT/platforms/android-18/arch-arm/usr/include/ 目录下会存放编译过程所依赖的头文件。

3. 链接过程中,经常使用的log或者OpenGL ES等库又将存放在哪里呢?

答:/platforms/android-18/arch-arm/usr/lib 目录下会存放链接过程中所依赖的库文件。