如何移植library到Android
原文 http://bloodysofiya.blog.163.com/blog/static/11656234320103231352327/ 一般来说,当你拿到一包library的source co
a . 这时候你应该了解你要移植的库 , 是怎么编译的了吧?没有 ? Goto 1 : 继续往下阅读
b . 一般来说, source co
< 1 >. 只有一个 Makefile , 或者是有 configure 文件的。
这种的,最好是你阅读 makefile 文件 , 把这个 makefile 转换为 Android . mk 文件 , 使用 ndk build system 去编译。
< 2 >. 另一种的是大型的源代码系统。这种的通常都会有自己的 build system . 比如 android .
碰到这种大型的,可能源代码太多了,一时半会根本不能写出一个 Android . mk 文件。可以酱紫,换成你要的目标平台的 cross toolchain .
举个通常的 android 例子:
CC = $( Compile_Path )/ arm - eabi - gcc
STRIP = $( Compile_Path )/ arm - eabi - strip
AR = $( Compile_Path )/ arm - eabi - ar
LINK =&( Compile_Path )/ arm - eabi - g ++
再配合上一些编译参数:比如 , Compile_Flags = - fPIC , march = armv5t mno - thumb - interwork 之类的。
c . 试试看能不能编译出来。
能是最好的,不能,又要分几种情况了
< 1 >. 一般来说, source co
< 2 >. Compile 时,报语法错误,比如 result = obj -> func ()-> func2() ,会说类似“ XXX can ' t find func2 ”
你可以该成 : temp = obj -> func(); result = temp -> func2(); 这主要是编译器方面的差异。
< 3 >. Compile 时,报 "can't find xxx.h" or "can't find xxx.cpp xxx.c" 之类,请正确包含该文件;
比如你可以写上: INCLUDE_PATH += - I /../......./ include . 或者放到编译下的 include 路径。比如 ndk 目录下的 build / platforms / android - 3 / arch - arm / usr / include 下面 . 不过这不是好方法就是了 .
< 4 >. Link 时 , 报 "can't find reference XXX" ,基本是忘记添加库文件了。你可以写上 : Library_Path += - L /.../.../ lib 或者放到 ndk 下面的 build / platforms / android - 3 / arch - arm / usr / lib 下面。
< 5 >. 当你该加的 library 都加了,依然出现 "can't find reference XXX" , 那多半就是你用的库不是标准的,这里面没有 XXX 函数的实现 .
比如 android 的 bionic libc 就没有 ftime 函数(应该没记错吧?!)那你只好自己去添上实现文件了。
< 6 >. 又报 link error 了?通常来说,使用 android build system 的人,不应该会遇到这类问题。只有直接使用 toolchain 才有可能会出现这类问题,这类 link error 比较棘手。
解决的方法有:
( I) 库之间有依赖 , 正确的链接库的顺序;比如 libz . a 依赖 liby . a , 那么链接顺序是: ld liby . a libz . a
( II) 库版本不对,请仔细读 documentation . 这里要注意 version 的区别 , debug & release 的区别 , 是不是同一个编译器编译的库( ABI 会不一致) , static & shared 的区别
( III) C 和 C ++ 库之间的调用 . 要记得使用 extern "C" {}, 和# ifdef __PLUSPLUS
( IV) 我碰到这样一个问题,就是作者使用了 #define OWNFUNC(Function) NEWNAME##Function字符拼接宏,把所有的函数名都转换了,dumpobj时候当然找不到函数签名啦.
( V) 当你 link 成功了,运行的时候挂掉了 . 也可能是 link 的时候没有成功哦 . 举个列子来说明:
当你写了一个 android 的 c 版本的 helloworld , 编译链接过了,但是运行的时候告诉你 : "can't find _start symbol" , 这就是说 android OS 把执行权交个 c 的 runtime library 的时候 , 找不到入口 , 而这个入口就是 _start . 解决的办法是 : ld crtBegin_dynamic . o 以及 crtEnd_android . o
当然还有很多的问题不是上面说的这些,比如编译参数不对, - fPIC 和 - fpic , 应该编译 32 bit 的 arm 指令,却用了 16 bit thumb 指令 ... 等等。
< 7 > 大部分的问题,你都解决了。剩下各种平时都没见过的问题了?去论坛上问吧。我就挺喜欢到国外的论坛上去问的。
< 8 >. 考虑使用 ndk build system 方式来编译。
但不管怎么说,我推荐使用ndkbuildsystem方式。我曾经到 google ndk group 上去问过问题,得到过 David Turner 的回答:
You should not be trying to use the toolchain directly like this .... 省略 .... Really , you should use the NDK build system instead . 3. 修改逻辑,添加逻辑适应自己的需要 .
先看看androidndk的目录layout大概长的怎么样.
root
|------apps,所有你要编译的C和c++co
|------build
||----platforms下面目前为止放了3个subfold,里面分别放了android需要的include,library(提供的library有限)
||------android3代表android1.5
||------android4代表android1.6
||------android5代表android2.0
||----prebuilt该目录下存放toolchain的可执行档案,以及include&library.对于arm-eabi-4.4.0了解不多.
||------arm-eabi-4.2.1
||------------bin所有的toolchain的excutefile
||------------arm-eabi
||------include
||------lib
||----tools这个目录下存放了很多有用的工具,包括下载,buildtoolchain
|------docs必须仔细读的文档
|------out生成的所有临时文件
先从docs开始谈,这个也是非常重要的一步。
1.纠正Android-mk.txt中写的ndk只能编译出staticlibrary和sharedlibrary,实际上他还提供了命令编译可执行程序.
a.include$(BUILD_SHARED_LIBRARY)
b.include$(BUILD_STATIC_LIBRARY)
c.include$(BUILD_EXECUTABLE)
理论上能编译出so文件,当然能编译出execute文件.只是ndk是否提供这个接口。
2.在apps目录下,新建一个目录,再建立project目录,这一级目录下建立Application.mk,再建立一个subfold“jni”,jni里面放的都是c和c++源代码,还有Android.mk.Android.mk就必须仔细写了.
常用的有LOCAL_PATH:=$(callmy-dir)//访问当前path下的所有子目录,
<1>.LOCAL_DEBUG:=no|yes
<2>.LOCAL_OS:=linux//目标平台
<3>.LOCAL_ARCH:=arm//目标平台cpu
<4>.LOCAL_ARM_MODE:=arm//CPU指令集
<5>.COMMON_DEFINES+=ISP=isp_arm9-D_ARM_//这里通常会把要移植的library中的各种flag写在这里.(从待移植中的Configure或者Makefile中拷贝过来)
<6>.ifeq($(DEBUG),no)
COMMON_FLAGS+=-s-O2-fno-strict-aliasing-DNDEBUG-fno-rtti//好像都采用O2优化,fno-rtti 告诉C++不要runtime 中的一些support.
<7>.COMMON_COMPILE_FLAGS+=-fPIC+=-msoft-float+=march=armv5t
相对应的有了COMMON_XXX就有了LOCAL_XXX,
我们可以定义相关的
<1>.LOCAL_MODULE:=libxxx//定义要编译的moule
<2>.LOCAL_CFLAGS:=-I$(LOCAL_PATH)/xxx/include以及局部FLAG(从Configure或者Makefile中拷贝过来)//定义search .h fold
<3>.LOCAL_CXXFILES:=//和上面是相同的,只是这个是C++的,上面的是C
<4>.LOCAL_SRC_FILES:=XXX.cXXX.cppXXX.hpp//定义源代码
<5>.include$(BUILD_STATIC_LIBRARY)定义该模块是静态的
要编译到android上运行的excutable,还会依赖
<1>.LOCAL_LDLIBS:=-Llibrary_path-lxx//定义库的路径,以及要的library, 这里其实用的都是动态库.
在android中,有一个参数是:-dynamic-linker,/system/bin/linker,采用的不是标准的linux下的ld.so.6(好像是这个数字,具体的不清楚了),而是/system/bin/linker来链接.这也是为什么我们用其他编译器(比如codesourcery)编译出来的东西不能在android上直接跑的原因,而一定要加上-static参数,也就是说用codesourcery编译出来的东西必须不依赖android的调度,而是靠编译时把所有的库全部加入.所以这时候写出来的helloworld都可能会有1~2Mb.提到了liner,就不得不提到androidbionic,这个Cruntimelibrary设计并不是功能特别强大,并且有些gnuglic中的函数没有实现,这是移植时会碰到的问题.而且,这个Cruntimelibrary也并没有采用crt0.o,crt1.o,crti.ocrtn.o,crtbegin.ocrtend.o,而是采用了android自己的crtBegin_dynamic.o,crtBegin_static.o和crtEnd_android.o。crt1.o是crt0.o的后续演进版本,crt1.o中会非常重要的.init段和.fini段以及_start函数的入口..init段和.fini段实际上是靠crti.o以及crtn.o来实现的.init段是main函数之前的初始化工作代码,比如全局变量的构造.fini段则负责main函数之后的清理工作.crti.ocrtn.o是负责C的初始化,而C++则必须依赖crtbegin.o和crtend.o来帮助实现.
So,在标准的linux平台下,link的顺序是:ldcrt1.ocrti.o[user_objects][system_libraries]crtn.o
而在android下,link的顺序是:arm-eabi-g++crtBegin_dynamic.o[user_objects][system_libraries]crtEnd_android.o
所以这就是从另一个方面说明为什么不适合codesourcery之类编译来开发android底层东西的原因了,这里我不包括BSP之类.
<2>.LOCAL_STATIC_LIBRARIES:=//excute file 依赖的库
<3>.include$(BUILD_EXCUTEABLE)
在谈谈platform目录下的东西,这里面要说的是可能include和library中包含的头文件和library文件不太够,我们可以使用busyboxforandroid把我们要的资料都给pull出来.或者编译android源代码,需要的都东西都会有.
prebuilt目录下,有我们编译器的可执行文件,但是很不幸androidtoolchain不支持STL.目前支持STL有两种方法:
1.使用STLPort(据说这种方式不是太好)
2.重新编译使androidtoolchain支持STL.
更多相关文章
- window 下 利用gradle编译volley源代码
- Android工程使用SVG图片
- E/ServiceManager( 54): add_service('led',0x35) uid=10028 - P
- android预览word(WPS预览)
- 【从源码看Android】01从Looper说起
- android 控件: xml 设置 Button 按下背景
- android编译分析之10—config.mk
- Android(安卓)Studio 生成的目录,对应应用Logo的尺寸___ AS 与 Ec
- Tools属性Tools Attributes