需要在Mac上编译出Linux服务器上可用的动态库,使用crosstool-ng生成交叉编译工具链
大小写敏感的磁盘
创建
| 1 | hdiutil create -volname "crosstool-ng" -type SPARSE -fs 'Case-sensitive Journaled HFS+' -size 20g crosstool-ng.dmg | 
挂载
| 1 | hdiutil attach crosstool-ng.dmg.sparseimage -mountpoint /Volumes/crosstool-ng | 
卸载(使用完后进行卸载)
| 1 | hdiutil detach /Volumes/crosstool-ng | 
安装依赖
| 1 | brew install autoconf binutils gawk gmp gnu-sed help2man mpfr openssl pcre readline wget xz | 
如果后续报什么错,提示没有什么包继续安装对应包即可。
编译crosstool-ng
crosstool-ng的文档地址在 http://crosstool-ng.github.io/docs/
github地址在 https://github.com/crosstool-ng/crosstool-ng
下载源码
| 1 | wget http://crosstool-ng.org/download/crosstool-ng/crosstool-ng-1.23.0.tar.bz2 | 
解压
| 1 | tar -jxvf crosstool-ng-1.23.0.tar.bz2 | 
进入源码目录
| 1 | cd crosstool-ng-1.23.0 | 
执行bootstrap脚本(可选,如果该文件存在的话需要执行,不存在该文件则跳过此步骤)
| 1 | ./bootstrap | 
执行configure
| 1 | ./configure | 
执行编译
| 1 | make -j4 | 
执行安装
| 1 | make install | 
之后就可以直接使用ct-ng命令了
配置交叉编译工具链
创建工作目录并进入
| 1 | mkdir workdir | 
拷贝配置文件。这里基于源码目录crosstool-ng-1.23.0/sample/x86_64-unknown-linux-gnu下的config进行自定义
| 1 | cp path/to/crosstool-ng-1.23.0/sample/x86_64-unknown-linux-gnu/crosstool.config .config | 
配置配置文件,需要将一些目录指向我们最开始创建的大小写敏感的磁盘
| 1 | ct-ng menuconfig | 
选择Paths and misc options,找到Paths,下面有三个路径需要修改
修改Local tarballs dictory指向大小写敏感的磁盘,如
| 1 | /Volumes/crosstool-ng/src | 
修改Working dictory指向大小写敏感的磁盘,如
| 1 | /Volumes/crosstool-ng/.build | 
修改x-tools指向大小写敏感的磁盘,如
| 1 | CT_PREFIX:-/Volumes/crosstool-ng/x-tools}/${CT_HOST:+HOST-${CT_HOST}/}${CT_TARGET} | 
因为需要用到从历史构建断点继续构建的功能,将Paths and misc options->crosstool-NG behavior选项下的Debug crosstool-NG勾选,然后继续勾选Save intermediate steps
修改完成后保存然后退出
修改binutils版本
因为我们拷贝的config使用的是2.28的binutils,而gnu的ftp上不存在该版本对应xy后缀的文件,所以需要修改版本为2.28.1,而2.28.1版本是存在xy后缀的文件的
还是一样进入menuconfig,找到Binary utilities->binutils version,修改版本号为2.28.1,但是发现这里没有2.28.1的选项,因此需要修改源码,然后重新执行编译安装步骤
打开源码crosstool-ng-1.23.0/config/binutils/binutils.in文件,找到如下代码
| 1 | config BINUTILS_V_2_28 | 
在前面加入
| 1 | config BINUTILS_V_2_28_1 | 
然后往下找到如下代码
| 1 | # CT_INSERT_VERSION_STRING_BELOW | 
在前面加入下面的代码
| 1 | default "2.28.1" if BINUTILS_V_2_28_1 | 
然后重新执行./configure、make -j4、make install等命令重新安装,然后再次进入menuconfig,会发现可以选择2.29了
生成交叉编译工具链
在workdir下执行命令构建
| 1 | ct-ng build | 
然后会下载一些zip文件到/Volumes/crosstool-ng/.build/tarballs下,文件有
| 1 | binutils-2.28.1.tar.xz | 
如果你觉得下载慢,可以手动下载这些文件,将其放入/Volumes/crosstool-ng/.build/tarballs,注意名字保持和上面一样。
之后会执行解压,构建,安装等操作,大约需要2-3小时后编译完成。
因为开启了debug相关的功能,如果中断了构建,可以随时从中断的地方继续构建,只需要找到对应的step即可,step可以从控制台的输出log中找到。
假设log中出现了如下字样,需要从该步骤恢复
| 1 | Saving state to restart at step 'companion_tools_for_build'... | 
只需要执行下面的代码即可
| 1 | ct-ng companion_tools_for_build+ | 
或者
| 1 | ct-ng build RESTART=companion_tools_for_build | 
编写cmake toolchain交叉编译工具链文件
关于cmake toolchain的编写,见cmake文档 cross-compiling
创建linux-x86_64.toolchain.cmake文件,加入如下内容
| 1 | set(CMAKE_SYSTEM_NAME Linux) | 
值得注意的是CMAKE_AR和CMAKE_RANLIB后面加了CACHE FILEPATH,这是必须的,不然可能会出错。
交叉编译
通过CMAKE_TOOLCHAIN_FILE参数指定toolchain文件
| 1 | cmake -DCMAKE_TOOLCHAIN_FILE=../linux-x86_64.toolchain.cmake .. | 
然后执行make,执行make过程中如果发生错误,可以设置VERVOSE=1将一些信息输出,便于调试
| 1 | make VERVOSE=1 | 
测试
将编译出来的动态库拷到linux上去测试,看是否可以正常使用,如果出现了类似 libstdc++.so.6: version `CXXABI_ARM_1.3.8’ not found 的错误,是因为生成的交叉编译工具链和服务器环境的gcc版本不一致导致的,要么修改交叉编译工具链的gcc版本和服务器上的gcc版本一样,要么修改服务器上的gcc版本和交叉编译工具链的gcc版本一样,无论哪种方式都可以。
关于jni
因为用到了jni,需要用到相关的头文件,而crosstool-ng其实是可以配置java的,但是测试下来发现宿主机Mac需要用到gcj,找了一大圈发现gcj老早已经从gcc中被移除了,如果需要用,则需要自己编译,实在麻烦。所以干脆简单点,直接拷贝头文件就可以解决问题,有两种解决方法
- 到服务器的JAVA_HOME目录,将include文件拷贝下来直接用
- 下载linux的jdk压缩包,直接提取出来用
编译的时候指定jni的头文件目录即可,这里将include文件夹中的内容放在/Volumes/crosstool-ng/java-linux下的include目录
关于gradle
gradle里用cmake编译c/c++代码,需要自己写gradle脚本,这里提供一个自己写的脚本
| 1 | apply plugin: JNIPlugin | 
当然打包jar的时候,还需要将动态库打到jar包里
| 1 | apply plugin: 'java' | 
cmake中需要指定安装目录
| 1 | if(NOT DEFINED CMAKE_INSTALL_PREFIX) | 
cmake 中install动态库
| 1 | install(TARGETS demodynamic LIBRARY DESTINATION lib) | 
关于java加载
参考tensorflow的动态库加载方式,代码如下
| 1 | final class NativeLibrary { | 
