作者: Sam (甄峰) sam_code@hotmail.com
0. 基础介绍:
Android推出NDK,是为了可以编译JNI和Native程序。它提供了一系列脚本:ndk-build, ndk-stack, ndk-dbg, ndk-which. 用来帮助用户编译,调试程序。
有了这些脚本,尤其是ndk-build, 它和Android.mk, Application.mk结合,可以快速的产生需要的库或可执行程序。但他们也隐藏了很多信息。例如:编译器用的是哪个,使用什么编译选项,什么链接选项等。头文件使用哪些,库文件使用哪些。无从得知。
如果我们不想利用ndk-build系列脚本,而是想直接使用编译器,添加相关编译选项,理论上完全可以编译出Native库和可执行程序。
但这样做,有什么意义呢?Sam工作中会遇到这样的需求:有些第三方库,采用Makefile或CMake编译。若要使用NDK脚本编译,需要重新了解编译思路,哪些源码生成那些库,哪些可执行程序依赖哪些库,然后根据这个写Android.mk. 这样做比较麻烦,尤其对比较庞大复杂的第三方库来说,就不是一个好办法。则可以直接使用NDK的编译器,添加必要的编译选项,修改Makefile或CMakefiles.txt,则可以简单的生成库了。
1. 寻找编译器:
首先要知道,不同版本的NDK, 所用到的编译器并不相同. Sam手头使用的是NDK-R9。就以它为例分析。
/opt/android-ndk-r9/toolchains/arm-linux-androideabi-4.6/prebuilt/linux-x86_64/bin/ 目录下,就是交叉编译器。
2. 编译选项的添加:
2.1: 头文件系列选项:
-MMD -MP -MF
说明:
-M : 生成文件依赖关系. 显示文件依赖什么头文件,头文件又依赖什么头文件. 仅仅显示依赖关系,不会真编译
-MM: 显示文件依赖关系,但不显示依赖的系统头文件。
-MMD:同-MM。 但存储在.d文件中
例子:
gcc -M main.cpp -I../Include
main.o: main.cpp /usr/include/stdc-predef.h /usr/include/stdio.h
\
/usr/include/features.h /usr/include/sys/cdefs.h
\
/usr/include/bits/wordsize.h
/usr/include/gnu/stubs.h \
/usr/include/gnu/stubs-64.h \
/usr/lib/gcc/x86_64-redhat-linux/4.8.3/include/stddef.h
\
/usr/include/bits/types.h
/usr/include/bits/typesizes.h \
/usr/include/libio.h /usr/include/_G_config.h
/usr/include/wchar.h \
/usr/lib/gcc/x86_64-redhat-linux/4.8.3/include/stdarg.h
\
/usr/include/bits/stdio_lim.h
/usr/include/bits/sys_errlist.h \
/usr/include/unistd.h
/usr/include/bits/posix_opt.h \
/usr/include/bits/environments.h
/usr/include/bits/confname.h \
/usr/include/getopt.h
../Include/Input_Event.h
gcc -M main.cpp -I../Include
main.o: main.cpp ../Include/Input_Event.h
gcc -MMD main.cpp -I../Include -c
注意,-MMD与 -M -MM 不同,它不会阻止编译。它会真正的编译下去。
-MP:
生成的依赖文件里面,依赖规则中的所有.h依赖项都会在该文件中生成一个伪目标,其不依赖任何其他依赖项。该伪规则将避免删除了对应的头文件而没有更新”Makefile”去匹配新的依赖关系而导致make出错的情况出现。
-MF:指定依赖文件。
-MF /home/sam/work/Jeenon/Source/Input_Event_2_Tony/obj/local/armeabi-v7a/objs/Input_Event/Input_Event.o.d
感觉以上选项,是否加入意义不大,但为和NDK保持一致,还是加上, 但 -MF不准备加入了:
CXXFLAGS := -MMD -MP
2.2: fpic:
CXXFLAGS += -fpic
2.3:setion:
CXXFLAGS += -ffunction-sections
将每一个function建立一个新的Sections.
2.4: 为stack信息跟踪作准备:
CXXFLAGS += -funwind-tables -fstack-protector -no-canonical-prefixes
-funwind-tables:为GDB Stack dump做准备。
-fstack-protector :选项能够检测出 stack smashing 问题,并在测到问题时通过 abort 方式触发 core dump
2.5:指令集和浮点设置:
CXXFLAGS += -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16
2.6: 优化:
CXXFLAGS += -O2
2.7: define:
CXXFLAGS += -DNDEBUG -Dlinux -D_GLIBCXX_USE_WCHAR_T
2.8: 异常,RTTI,signed-char
CXXFLAGS += -fexceptions -frtti -fsigned-char
2.9: 头文件路径:
CXXFLAGS += -I/opt/android-ndk-r9/sources/cxx-stl/gnu-libstdc++/4.6/include
CXXFLAGS += -I/opt/android-ndk-r9/sources/cxx-stl/gnu-libstdc++/4.6/libs/armeabi-v7a/include
CXXFLAGS += -I/opt/android-ndk-r9/sources/cxx-stl/gnu-libstdc++/4.6/include/backward
CXXFLAGS += -I./
CXXFLAGS += -I/opt/android-ndk-r9/platforms/android-8/arch-arm/usr/include
2.10: 几个优化:
CXXFLAGS += -fomit-frame-pointer -fstrict-aliasing -funswitch-loops -finline-limit=300
2.11: PIE问题:
CXXFLAGS += -pie -fPIE
3. 链接选项的添加:
LFLAGS += -shared -fpic
指定了-sysroot就是为编译时指定了逻辑目录。编译过程中需要引用的库,头文件,如果要到/usr/include目录下去找的情况下,则会在前面加上逻辑目录。
LFLAGS +=
--sysroot=/opt/android-ndk-r9/platforms/android-8/arch-arm
LFLAGS += -lgcc -no-canonical-prefixes -march=armv7-a
如果so里有未定义符号,这编译不通过
LFLAGS += -Wl,--fix-cortex-a8
-Wl,--no-undefined
LFLAGS += -lc -lm
LFLAGS +=
-L/opt/android-ndk-r9/platforms/android-8/arch-arm/usr/lib
LFLAGS +=
/opt/android-ndk-r9/sources/cxx-stl/gnu-libstdc++/4.6/libs/armeabi-v7a/libsupc++.a
LFLAGS +=
/opt/android-ndk-r9/sources/cxx-stl/gnu-libstdc++/4.6/libs/armeabi-v7a/libgnustl_shared.so
这个结论还不对,下一步继续研究.
注:Android编译动态库时,会加入:
-Wl,-soname,libInput_Event.so