【NDK系列】ndk-build使用说明

6 篇文章 0 订阅

ndk-build是上一代android ndk开发编译工具,尽管现在官方推荐使用CMake,AS默认的工具也切换成了后者,但是仍有必要对ndk-build有一定了解,以求:

  1. 知道ndk-build如何使用;
  2. 知道如何从ndk-build项目切换到CMake

还是先回顾一下NDK开发的步骤:

  1. 在java类文件中编写native接口;
  2. 借助AS代码提示自动生成或者使用javah命令生成.h头文件;
  3. 新建c/cpp文件引入头文件并实现接口函数;
  4. 配置编译选项编译成so文件(ndk-build或CMake)提供给三方使用;

可以看到ndk-build和CMake是NDK开发编码完成后的编译工具,是整个开发环节的最后一环。下面对ndk-build工具的使用做一个简单的介绍:

Android.mk

Android.mk文件用于指导编译器如何编译程序,文件名后缀为mk表明其是一个Makefile。Android.mk用于配置每个module的C/C++源码,module可以是静态库共享库或者独立的可执行文件。 一个Android.mk文件可以有一个或多个module,modules之间也可以有依赖关系。来看一个最简单的Android.mk配置文件:

# 获取脚本当前文件路径
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
# 生成的so文件名
LOCAL_MODULE    := hello    
# 目标文件
LOCAL_SRC_FILES := hello.c  
# 指定生成库为动态链接库,即so文件
include $(BUILD_SHARED_LIBRARY)

NDK提供了宏、变量以及模块描述变量,这些宏、变量以及变量的赋值共同组成了Android.mk文件。

  • 宏:包括my-dirall-subdir-makefiles等,通过$(call )来调用,返回文本信息。

  • 变量:包括CLEAR_VARSBUILD_SHARED_LIBRARYTARGET_ARCH等,由NDK编译系统提供,并且在Android.mk文件被解析前就已经存在。Android.mk文件有可能被多次解析,因此每次解析时这些变量的值都有可能不同。

  • 模块描述变量:Module-description,包括LOCAL_PATHLOCAL_MODULELOCAL_SRC_FILES等LOCAL_前缀变量,这些变量除LOCAL_PATH外,均填写在语句include $(CLEAR_VARS)include $(BUILD_XXX)之间。

下面介绍一下常用变量:

变量说明可选项示例
LOCAL_PATH当前目录,为模块描述变量,一个Android.mk必须定义LOCAL_PATH,用于定位源文件,在本例中,使用的是编译系统提供的宏“my-dir”(“my-dir”返回最近一次包括Makefile文件路径,通常为当前Android.mk所在目录),用于返回当前目录。此变量不会被CLEAR_VARS清除,所以每个Android.mk文件只需要定义一次就可以了。调用宏-
CLEAR_VARS变量清除,由编译系统提供,作用是清除模块变量(在include $(CLEAR_VARS)和include $(BUILD_XXX)之间的LOCAL_XXX模块变量),当然LOCAL_PATH除外。由于所有的编译控制文件都是单一的GNU Make可执行上下文环境中解析,而这个上下文环境中所有的变量都是全局的,所以编译module前需要清理相应变量。-
LOCAL_MODULEmodule名称,module的唯一标识,这个名字必须是唯一的,且中间不能有空格。在默认情况下,它决定了生成的文件名,如“hello-jni”对应的动态库名称为libhello-jni.so,然而要索引它时,需要“hello-jni”即可,也可以通过变量LOCAL_MODULE_FILENAME来覆盖这个默认名称。模块名称-
LOCAL_SRC_FILES源码文件,包括C/C++源文件列表,这些源文件会被编译到一个module中,不过也不必列出头文件和包括文件,编译系统会自动为你找打所有需要的依赖关系。值得注意的是linux下路径使用顺斜杠(/)。C/C++源文件列表-
BUILD_SHARED_LIBRARY动态库编译,编译器提供的变量,表示编译成动态库,它指向一个GNU Makefile脚本,这个脚本收集从include $(CLEAR_VARS)后所有的LOCAL_XXX变量中定义的所有信息,决定编译什么以及怎么编译。还有BUILD_STATIC_LIBRARY表示编译成静态库(.a),静态库不会被拷贝到APK中。-
PREBUILT_SHARED_LIBRARY预编译,指向一个编译脚本,用来指定一个预编译动态库。使用此变量时,不像BUILD_SHARED_LIBRARYBUILD_STATIC_LIBRARY那样,LOCAL_SRC_FILES的值必须是只能有一个指向预编译动态库的路径,如foo/libfoo.so,而不是源文件。PREBUILD_STATIC_LIBRARYPREBUILD_SHARED_LIBRARY一样,只不过是用于引用静态库。include $ ( C L E A R V A R S ) < b r > L O C A L M O D U L E : = t e s t < b r > L O C A L S R C F I L E S : = l i b / (CLEAR_VARS) <br> LOCAL_MODULE := test <br> LOCAL_SRC_FILES := lib/ (CLEARVARS)<br>LOCALMODULE:=test<br>LOCALSRCFILES:=lib/(TARGET_ARCH_ABI)/libtest.so
include $(PREBUILT_SHARED_LIBRARY)
TARGET_ARCH_ABI目标ABI名称,若定义了多个ABI,则每次解析Android.mk时值都不一样,主要使用场景为根本不同的ABI定义不同的文件等。-
LOCAL_LDLIBS链接库,用于额外链接选项,所有的库都有“-l”前缀。可同时列出多个库,用空格隔开。Android NDK默认链接了多个库,不需要显式地添加到LOCAL_LDLIBS中,包括 the standard C libraries,the standard C++ libraries,real-time extensions和 pthread库。同时也提供了一些需要显式添加的库。Android-3:-llog (Android Log)
-lzZlib(Compression Library)
-ldl(Dynamic Linker Library)
Android-4:-lGLESv1_CM(OpenGL ES 1.x Library)
Android-5:-lGLESv2(OpenGL ES 2.0 Library)
Android-8:-ljnigraphics(The jnigraphics Library)
Android-9:-lEGL(The EGL graphics library)
-lOpenSLES(Open ES native audio Library)
-landroidNatice (Android API)
Android-14:-lOpenMAXAL(OpenMAX AL natice multimedia Library)
Android-18:-lGLESv3(OpenGL ES 3.0 Library)
Android-21:-lGLESv3(OpenGL ES 3.1 Library)
LOCAL_LDLIBS := -llog -ldl
LOCAL_CFLAGS编译、链接标志,在编译C/C++时,传递给编译器的标志集合。-fexceptions:由于NDK编译从R5开始才支持C++异常控制,为了通用性,异常处理默认是禁用的(-fno-exceptions),因此需要在指定module中添加LOCAL_CPPFLAGS += -fexceptions编译选项方可编译带异常处理的C++代码。也可以直接在Application.mk中配置APP_CPPFLAGS += -fexceptions。
-frtti:从NDK R5开始,NDK也开始支持C++ RTTI了,但为了通用性,所有的C++源文件被构建的时候默认是不支持RTTI的(-fno-rtti),可以通过在Android.mk中添加:LOCAL_CPPFLAGS += -frtti或者在Application.mk添加APP_CPPFLAGS += -frtti来开启RTTI。
-fvisibility=hidden:在NDK开发中,源文件的函数都有一个默认的visibility属性为public,编译生成的so文件中几乎所有的函数名、全局变量名均被导出,其实只需要导出java_com开头的jni函数即可,其他函数不需要暴露出来,在Android.mk中设置LOCAL_CFLAGS += -fvisibility=hidden,就可以隐藏不需要导出的函数,若某个函数需要导出,则添加JNIEXPORT或者__attribute__ ((visibility (“default”)))即可。
-ffunction-sections:不添加此参数时,编译文件.o中代码部分只有.text段,使用此参数,会使每个函数单独有一个段,举个栗子,函数func1()会编译成.text.func1段,虽然段多了,但对链接后代码大小并没有影响。
-fdata-sections:同上,每个data都有一个单独的段。
-Wl --gc-sections:-Wl,选项是告诉编译器,将后面选项传递给连接器,-Wl,–gc-sections的意思是使用连接器ld链接时删除不用的段。若使用LOCAL_CFLAGS += -ffunction-sections -fdata-sections,则代码和数据均被分割成不同的段,若某个函数或数据未被任何函数调用,则ld不会链接未被调用的函数,从而减小so文件体积,达到优化so的目的。
-fPIC:PIC(position independent code)用于编译位置无关代码,生成可用于共享库的位置独立代码。若不添加-fPIC,则加载.so文件的代码段时,代码段引用的数据对象需要重定位,重定位会修改代码段内容,这样就导致没使用这个.so,代码段的进程在内核中就会生成这个文件的拷贝。
-Wall: wring all 意思在编译和链接过程中显示所有警告信息。
LOCAL_CPPFLAGS += -fexceptions
LOCAL_CPPFLAGS编译、链接标志,在编译C/C++时,传递给编译器的标志集合,只支持C++。见上-
LOCAL_LDFLAGS编译、链接标志,在编译C/C++时传递给连接器一些额外的参数。见上-

更多资料:GCC Command Options 文档

Appliction.mk

Appliction.mk则是用于配置编译生成物相关属性,是轻量级的Makefile,通常位于项目的jni目录下。一个典型的示例如下:

APP_ABI := armeabi arm64-v8a x86_64 x86 armeabi-v7a
NDK_TOOLCHAIN_VERSION := clang3.5
APP_STL := stlport_static
APP_OPTIM:= debug

配置项含义表:

配置项功能可选项示例
APP_ABI目标平台ABI类型,默认选择armeabi ABI,可通过设置APP_ABI设置一个或者多个ABI。对于ABI的选择需要考虑到效率和APK大小。由于armeabi-v7a指令集兼容armeabi;市面上的x86手机为了兼容性,基本都使用libhoudini模块,兼容arm指令集;64位机型默认支持32位abi的so,因此在对大小要求比较高的情况下,可以只选择市面上设备基本兼容的armeabi ABI,如果对性能有些许要求,可以再添加x86 ABI。- armeabi
- arm64-v8a
- x86_64
- x86
- armeabi-v7a
- mips
- mips64
- all(全部)
-
NDK_TOOLCHAIN_VERSION编译器类型、版本。默认采用的是GCC编译器,对于GCC版本的选择与NDK版本有关系,如NDK R12,在64位ABI默认是GCC 4.9,32位ABI默认是GCC 4.8,当然也可以像上面例子中给出的设置一样,设置clang编译器。- gcc4.9
-clang3.5
-
APP_STL运行库类型。Android NDK 默认使用的是最小支持的C++运行库,如果你需要你的NDK程序中使用STL,则可以设置APP_STL := stlport_static,若APK中有多个SO文件用到STL,建议都使用动态方式链接STL,这样可以减小整个APK文件大小。另外需要注意的是官方提供的NDK运行库除了默认的以外都支持RTTI和异常,然而默认是禁用的,将在下面的Android.mk中说明如何开启。system(default)系统默认的C++运行库
stlport_static以静态链接方式使用的sttport版本的STL
stlport_shared以动态链接方式使用的sttport版本的STL
gnustl_static以静态链接方式使用的gnustl版本的STL
gnustl_shared以动态链接方式使用的gnustl版本的STL
gabi++_static以静态链接方式使用的gabi++
gabi++_shared以动态链接方式使用的gabi++
c++_static以静态链接方式使用的LLVM libc++
c++_shared以动态链接方式使用的LLVM libc++
APP_OPTIM编译模式。“release”模式为默认的,生成的是优化后的二进制;也可以设置为“debug”模式,“debug”模式生成的是未优化二进制,提供很多BUG信息,便于调试和分析。- release
- debug

参考资料:Application.mk官方文档

命令行

在AS项目根目录下执行ndk-build开始生成so文件。

参考资料:ndk-build 脚本

参考资料

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值