上篇提到的都是使用现有的ndk-build建立共享库。如何建立可执行的C/C++文件?将jni/Android.mk内的
include $(BUILD_SHARED_LIBRARY) 改成include $(BUILD_EXECUTABLE) 即可。那么,想“自由使用”工具链,比如 像 直接使用gcc/g++那样,尤其是想往Android上面移植 C/C++程序或者库时,如何才能跳出ndk-build的限制?
I.提取Android的gcc工具链
如果你想偷懒的话,你很幸运,有现成的 ,附件的perl脚本agcc(csdn搞不出附件,只好链接新帖子 ) ,即可以完成工具链的抽取和封装,可以像使用 gcc 那样使用它,它最先来自
plausible.org/andy/agcc
不过这个是面对整个Android开发包的(包括Android/SDK/NDK的所有文件),而且没有C++/STL支持,我对它进行了修改,使它与上篇给出的ndk-r4配合,完全支持c/c++。
如果你想自己动手,请这样做:
1.分别对
1)生成可执行文件
2)静态库
3) 动态库
4)多个源文件 的不同类型的工程使用
#ndk-build -B V=1
命令,会让ndk-build “揭露出”整个工具链的不同使用过程 ,比如上篇中的helloworld工程:
显示:
Compile thumb : helloworld <= /opt/android/android-ndk-r4c/samples/helloworld/jni/helloworld.c
/opt/android/android-ndk-r4c/build/prebuilt/linux-x86/arm-eabi-4.4.0/bin/arm-eabi-gcc -I/opt/android/android-ndk-r4c/build/platforms/android-8/arch-arm/usr/include -fpic -mthumb-interwork -ffunction-sections -funwind-tables -fstack-protector -fno-short-enums -D__ARM_ARCH_5__ -D__ARM_ARCH_5T__ -D__ARM_ARCH_5E__ -D__ARM_ARCH_5TE__ -Wno-psabi -march=armv5te -mtune=xscale -msoft-float -mthumb -Os -fomit-frame-pointer -fno-strict-aliasing -finline-limit=64 -I/opt/android/android-ndk-r4c/samples/helloworld/jni -DANDROID -Wa,--noexecstack -O2 -DNDEBUG -g -c -MMD -MP -MF /opt/android/android-ndk-r4c/samples/helloworld/bin/ndk/local/armeabi/objs/helloworld/helloworld.o.d /opt/android/android-ndk-r4c/samples/helloworld/jni/helloworld.c -o /opt/android/android-ndk-r4c/samples/helloworld/bin/ndk/local/armeabi/objs/helloworld/helloworld.o
SharedLibrary : libhelloworld.so
/opt/android/android-ndk-r4c/build/prebuilt/linux-x86/arm-eabi-4.4.0/bin/arm-eabi-gcc -nostdlib -Wl,-soname,libhelloworld.so -Wl,-shared,-Bsymbolic /opt/android/android-ndk-r4c/samples/helloworld/bin/ndk/local/armeabi/objs/helloworld/helloworld.o -Wl,--whole-archive -Wl,--no-whole-archive /opt/android/android-ndk-r4c/build/prebuilt/linux-x86/arm-eabi-4.4.0/bin/../lib/gcc/arm-eabi/4.4.0/../../../../arm-eabi/lib/libstdc++.a /opt/android/android-ndk-r4c/build/prebuilt/linux-x86/arm-eabi-4.4.0/bin/../lib/gcc/arm-eabi/4.4.0/../../../../arm-eabi/lib/libsupc++.a /opt/android/android-ndk-r4c/build/prebuilt/linux-x86/arm-eabi-4.4.0/bin/../lib/gcc/arm-eabi/4.4.0/libgcc.a /opt/android/android-ndk-r4c/build/platforms/android-8/arch-arm/usr/lib/libc.so /opt/android/android-ndk-r4c/build/platforms/android-8/arch-arm/usr/lib/libm.so -Wl,--no-undefined -Wl,-z,noexecstack -Wl,-rpath-link=/opt/android/android-ndk-r4c/build/platforms/android-8/arch-arm/usr/lib /opt/android/android-ndk-r4c/build/prebuilt/linux-x86/arm-eabi-4.4.0/bin/../lib/gcc/arm-eabi/4.4.0/../../../../arm-eabi/lib/libstdc++.a /opt/android/android-ndk-r4c/build/prebuilt/linux-x86/arm-eabi-4.4.0/bin/../lib/gcc/arm-eabi/4.4.0/../../../../arm-eabi/lib/libsupc++.a /opt/android/android-ndk-r4c/build/prebuilt/linux-x86/arm-eabi-4.4.0/bin/../lib/gcc/arm-eabi/4.4.0/libgcc.a -o /opt/android/android-ndk-r4c/samples/helloworld/bin/ndk/local/armeabi/libhelloworld.so
......
从这些命令记录分别提取出
1)工具的名称和位置
例如gcc/g++工具是 arm-eabi-gcc/g++,位置是 /opt/android/android-ndk-r4c/build/prebuilt/linux-x86/arm-eabi-4.4.0/bin/。。
2)工具相关选项和参数
交叉编译时,最重要的就是编译工具的相关参数设置,以及从环境中提取的文件(头文件夹,链接文件,库文件..)。下面的从上面提取的相关参数就不用我解释了吧?(如果不熟悉这些的话,真的不应该直接从Android上手学交叉编译,道理你们懂的。。)
CC = arm-eabi-gcc
CFLAGS/CXXFLAGS ="-fpic -mthumb-interwork -ffunction-sections -funwind-tables -fstack-protector -fno-short-enums -Wno-psabi -march=armv5te -mtune=xscale -msoft-float -mthumb -Os -fomit-frame-pointer -fno-strict-aliasing -finline-limit=64"
CPPFLAGS ="-I/opt/android/android-ndk-r4c/build/platforms/android-8/arch-arm/usr/include -fpic -mthumb-interwork -ffunction-sections -funwind-tables -fstack-protector -fno-short-enums -D__ARM_ARCH_5__ -D__ARM_ARCH_5T__ -D__ARM_ARCH_5E__ -D__ARM_ARCH_5TE__ -I/opt/android/android-ndk-r4c/samples/helloworld/jni -DANDROID "
CXX =arm-eabi-g++
LDFLAGS = "-nostdlib -Wl,-soname,libhelloworld.so -Wl,-shared,-Bsymbolic -Wl,--whole-archive -Wl,--no-whole-archive /opt/android/android-ndk-r4c/build/prebuilt/linux-x86/arm-eabi-4.4.0/bin/../lib/gcc/arm-eabi/4.4.0/../../../../arm-eabi/lib/libstdc++.a /opt/android/android-ndk-r4c/build/prebuilt/linux-x86/arm-eabi-4.4.0/bin/../lib/gcc/arm-eabi/4.4.0/../../../../arm-eabi/lib/libsupc++.a /opt/android/android-ndk-r4c/build/prebuilt/linux-x86/arm-eabi-4.4.0/bin/../lib/gcc/arm-eabi/4.4.0/libgcc.a /opt/android/android-ndk-r4c/build/platforms/android-8/arch-arm/usr/lib/libc.so /opt/android/android-ndk-r4c/build/platforms/android-8/arch-arm/usr/lib/libm.so -Wl,--no-undefined -Wl,-z,noexecstack -Wl,-rpath-link=/opt/android/android-ndk-r4c/build/platforms/android-8/arch-arm/usr/lib /opt/android/android-ndk-r4c/build/prebuilt/linux-x86/arm-eabi-4.4.0/bin/../lib/gcc/arm-eabi/4.4.0/../../../../arm-eabi/lib/libstdc++.a /opt/android/android-ndk-r4c/build/prebuilt/linux-x86/arm-eabi-4.4.0/bin/../lib/gcc/arm-eabi/4.4.0/../../../../arm-eabi/lib/libsupc++.a /opt/android/android-ndk-r4c/build/prebuilt/linux-x86/arm-eabi-4.4.0/bin/../lib/gcc/arm-eabi/4.4.0/libgcc.a"
3)工具的使用过程
主要是面对不同的源文件(.c/.cpp/.o/.h...),以及不同的目标(生成什么?.o/.exe/.s...),工具的使用过程和参数都是不同的,比如上例:
.c-->.o
$CC $CPPFLAGS $CFLAGS helloworld.c -c -g -o helloworld.o
仅仅 预处理-->编译,未链接
然后
.o-->.so
$CC $LDFLAGS helloworld.o -o libhelloworld.so
完成链接
4)大家可以查看其他几类情况,使用的工具和参数各不相同。
II.直接使用工具链
提取出了工具链,弄清其不同参数和工作流程后,就可以自己指明使用了。首先将工具加入$PATH,然后,例如编译如下的两个全面测试C++功能的.cpp文件,要求ahoo.cpp -->libahoo.so(动态库);a.cpp+libahoo.so --->a(程序):
ahoo.h
#include
ahoo.cpp
#include"ahoo.h" int hana; A::A(){printf("ahoooooooooo coming!/n");} A::~A(){printf("ahoooooooooo dying!/n");} void ahoo(char** a) { hana = 100; printf("sizeof hana:%d/n",hana); *a=(char*) malloc(256); memset(*a,0,256); std::string s = "Hello from JNI !"; try { if (std::getenv("NON_EXISTENT_ENVIRONMENT_VARIABLE") == NULL) throw std::runtime_error("libahoo.so:--exception.runtime_error.string:/t--琉球の海風にいる !"); } catch (std::exception &ex) { s = ex.what(); s = s+"/nlibahoo.so:--exception.catch.string:/t--You are Foooooooooyoooooooed!/n"; } strcpy(*a,s.c_str()); }
a.cpp
#include "ahoo.h" #include"pthread.h" B::B(){printf("bagaaaaaaaaa coming!/n");} B::~B(){printf("bagaaaaaaaaa dying!/n");} template
命令就是:(都是先编译,再链接,注意参数的区别)
1)ahoo.cpp--->ahoo.o a.cpp-->a.o
arm-eabi-g++ ahoo.cpp -I/opt/android/android-ndk-r4c/build/platforms/android-8/arch-arm/usr/include -D__ARM_ARCH_5__ -D__ARM_ARCH_5T__ -D__ARM_ARCH_5E__ -D__ARM_ARCH_5TE__ -DANDROID -DNDEBUG -mthumb-interwork -march=armv5te -mtune=xscale -msoft-float -mthumb -fpic -fPIC -ffunction-sections -funwind-tables -fstack-protector -fno-short-enums -fomit-frame-pointer -fno-strict-aliasing -finline-limit=64 -Wno-psabi -Wa,--noexecstack -Os -O2 -c -o ahoo.o
arm-eabi-g++ a.cpp -I/opt/android/android-ndk-r4c/build/platforms/android-8/arch-arm/usr/include -D__ARM_ARCH_5__ -D__ARM_ARCH_5T__ -D__ARM_ARCH_5E__ -D__ARM_ARCH_5TE__ -DANDROID -DNDEBUG -mthumb-interwork -march=armv5te -mtune=xscale -msoft-float -mthumb -fpic -fPIC -ffunction-sections -funwind-tables -fstack-protector -fno-short-enums -fomit-frame-pointer -fno-strict-aliasing -finline-limit=64 -Wno-psabi -Wa,--noexecstack -Os -O2 -c -o a.o
2) ahoo.o -->libahoo.so
arm-eabi-gcc ahoo.o -nostdlib -Wl,-shared,-Bsymbolic -Wl,--whole-archive -Wl,--no-whole-archive -Wl,--no-undefined -Wl,-z,noexecstack -Wl,-rpath-link=/opt/android/android-ndk-r4c/build/platforms/android-8/arch-arm/usr/lib -L/opt/android/android-ndk-r4c/build/platforms/android-8/arch-arm/usr/lib -lc -lm /opt/android/android-ndk-r4c/build/prebuilt/linux-x86/arm-eabi-4.4.0/arm-eabi/lib/libstdc++.a /opt/android/android-ndk-r4c/build/prebuilt/linux-x86/arm-eabi-4.4.0/arm-eabi/lib/libsupc++.a /opt/android/android-ndk-r4c/build/prebuilt/linux-x86/arm-eabi-4.4.0/lib/gcc/arm-eabi/4.4.0/libgcc.a -o libahoo.so
3) a.o -->a
arm-eabi-gcc a.o -nostdlib -Bdynamic -Wl,-dynamic-linker,/system/bin/linker -Wl,--gc-sections -Wl,-z,nocopyreloc -Wl,--no-undefined -Wl,-z,noexecstack -Wl,-rpath-link=/opt/android/android-ndk-r4c/build/platforms/android-8/arch-arm/usr/lib -L/opt/android/android-ndk-r4c/build/platforms/android-8/arch-arm/usr/lib -lc -lm /opt/android/android-ndk-r4c/build/prebuilt/linux-x86/arm-eabi-4.4.0/arm-eabi/lib/libstdc++.a /opt/android/android-ndk-r4c/build/prebuilt/linux-x86/arm-eabi-4.4.0/arm-eabi/lib/libsupc++.a /opt/android/android-ndk-r4c/build/prebuilt/linux-x86/arm-eabi-4.4.0/lib/gcc/arm-eabi/4.4.0/libgcc.a /opt/android/android-ndk-r4c/build/platforms/android-8/arch-arm/usr/lib/crtend_android.o /opt/android/android-ndk-r4c/build/platforms/android-8/arch-arm/usr/lib/crtbegin_dynamic.o -o a -lahoo -L./
你是什么感觉呢,不觉得太长太麻烦了吗?何苦呢?何必呢?所以我才将所有的内容都封装在agcc 内的啊,你难道不想也这样做吗?(尤其是对程序或者库进行 ./configure 的时候!),上面的例子,直接用我的agcc 的话(先将包含arm-eabi-*的路径加入$PATH )
#agcc ahoo.cpp -shared -o libahoo.so
Compile Thumb++ :ahoo.cpp ===> ahoo.o
...
Link ARM shared lib : ahoo.o ====> libahoo.so
...
#agcc a.cpp -o a -lahoo -L./
Compile Thumb++ :a.cpp ===> a.o
....
Link ARM executable : a.o ====> a
...
# file a
a: ELF 32-bit LSB executable, ARM, version 1 (SYSV), dynamically linked (uses shared libs), not stripped
#file libahoo.so
libahoo.so: ELF 32-bit LSB shared object, ARM, version 1 (SYSV), dynamically linked, not stripped
#arm-eabi-readelf -d a
Dynamic section at offset 0x132cc contains 23 entries:
Tag Type Name/Value
0x00000001 (NEEDED) Shared library: [libc.so]
0x00000001 (NEEDED) Shared library: [libm.so]
0x00000001 (NEEDED) Shared library: [libahoo.so]
0x00000020 (PREINIT_ARRAY) 0x1c2a4
0x00000021 (PREINIT_ARRAYSZ) 0x8
.................
搞定!现在你也可以从自己的NDK出发提取组合自己的工具链了吧!