出处:http://www.cnblogs.com/wengzilin/p/4466708.html
在开始介绍如何使用CMake编译跨平台的静态库之前,先讲讲我在没有使用CMake之前所趟过的坑。因为很多开源的程序,比如png,都是自带编译脚本的。我们可以使用下列脚本来进行编译:
1
2
3
|
./configure --prefix=/xxx/xx --enable- static = YES
make
make install
|
相信手动在类Unix系统上面编译过开源程序的同学对上面的命令肯定非常熟悉。更悲惨的是,有些开源库是不提供configure配置文件的,只有一个Makefile或者Makefile.gcc。我的体会是,Makefile是一个很复杂的东西,没有一定的积累我们是看不懂的,更别说去修改它了。而本文的CMake可以更傻瓜更简单地达到我们的目的,你不需要理会复杂的makefile语法。Just follow me!
如果不配置编译器和一些编译、链接参数,这样的操作,最后编译出来的静态库只能在本系统上面被链接使用。比如你在mac上面运行上面的命令,编译出来的静态库就只能给mac程序链接使用。如果在Linux上面运行上述命令,则也只能给Linux上面的程序所链接使用。如果我们想要在Mac上面编译出ios和android的静态库,就必须要用到交叉编译。
要进行交叉编译,一般来说要指定目标编译平台的编译器,通常是指定一个CC环境变量,根据编译的是c库还是c++库,要分别指定C_flags和CXX_flag,当然还需要指定c/c++和系统sdk的头文件包含路径。总之,非常之繁琐。
为什么我们不使用autoconf?为什么我们不使用QMake,JAM,ANT呢?具体原因大家可以参考我在本文最后的参考链接里面的《Mastering CMake》一书的第一章。我自己使用CMake的感受就是:我原来编写bash,配置configure参数,读各个开源库的INSTALL文件(因为不同库的configure参数有差别),配置各种编译flag,头文件包含等。最后3天时间,折腾了png和jepg两个库的编译。当然,中间我还写了android和linux的编译脚本。而换用CMake以后,我2天时间编译完了Box2D,spine和Chipmunk的编译。并且配置脚本相当简单,添加新的库,基本上只是拷贝脚本,修改一两个参数即可。有了CMake,编译跨平台静态库和生成跨平台可执行程序So Easy!
编写一个静态库的CMake配置文件过程如下:(这里我以Box2D为例)
1
2
3
4
5
|
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}
)
file(GLOB_RECURSE box2d_source_files "${CMAKE_CURRENT_SOURCE_DIR}/Box2D/*.cpp" )
|
我的CMakeLists.txt和Box2D的文件结构关系如下图所示:
这里的${CMAKE_CURRENT_SOURCE_DIR}
表示CMakeLists.txt所在的目录。而GLOB_RECURSE可以递归地去搜索Box2D目录下面所有的.cpp文件来参与静态库的编译。而include_directories和file指令,显而易见,它们是用来指定静态库的头文件和实现文件。
注:指定头文件的原则是:可以多引入,但不能缺。交叉编译本质也是编译,因此基本的要求是语法没问题,如果必要的头文件缺少了自然编译会失败!所以,原则上可以把整个根目录的头文件都引入进去,不过这样虽然省事,但是会导致生成的库文件体积过大,但是会更保险一些,比如:
1
2
3
4
5
|
include_directories(
"../../../myWindows"
"../../../" #很残暴地引入了整个根目录
"../../../include_windows"
)
|
1
|
add_definitions( -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_REENTRANT -DENV_UNIX -DBREAK_HANDLER -DUNICODE -D_UNICODE)
|
如果需要判断平台,可以这么写:
1
2
3
4
|
IF(APPLE)
add_definitions(-DENV_MACOSX)
FIND_LIBRARY(COREFOUNDATION_LIBRARY CoreFoundation )
ENDIF(APPLE)
|
其中-D_FILE_OFFSET_BITS=64表示定义一个环境变量_FILE_OFFSET_BITS且值为64。添加环境变量用在什么时候呢?我们常常可以在一些开源的项目工程代码中看到这样的形式:
1
2
3
4
5
|
#ifdef _UNICODE
AString name = nameWindowToUnix2(fileName);
#else
const char * name = nameWindowToUnix(fileName);
#endif
|
以上代码中_UNICODE就是环境变量,那像这种变量该通过什么时候定义呢?一种是像上面一样通过add_definitions写我们的编译脚本CMakeLists.txt,另一种是新建一个.h文件,写在里面然后引用。两种方式完全等效,我在我的交叉编译工程中实践过。例如,上面的add_definitions可以转化为:
1
2
3
4
5
6
7
|
#define FILE_OFFSET_BITS 64
#define _LARGEFILE_SOURCE 1
#define _REENTRANT 1
#define ENV_UNIX 1
#define BREAK_HANDLER 1
#define UNICODE 1
#define _UNICODE 1
|
1
|
add_library(Box2D STATIC ${box2d_source_files})
|
这里add_library表示最终编译为一个库,static表示是静态库,如果想编译动态库,可以修改为shared. 至此,一个静态库的配置就完成了。当然,因为这个库没有包括其它外部的头文件,所以会比较简单。但这也远比自己写一个Makefile要简单N倍,请记住这句话。
以上就是编写一个CMakeLists.txt配置文件的全部必要过程,一些更复杂的配置文件可能会增加一些其他东西,不过以上部分是基本逃不掉的。只要包含以上步骤就能成功交叉编译出目标平台的库文件。下面是一个完整的CMakeLists.txt文件示例(文件名不能改):
我们有了配置完毕的CMakeLists.txt文件,但不要以为这样就万事大吉了!不知道你发现了没,上述内容并不涉及目标平台的相关信息,因此编译出来的库只能在运行该配置文件的当前系统上使用。现在需要配合接下来的操作才能最终达到目的。
编译iOS库,一般要先使用cmake指令生成xcode工程,再用xcode工程运行编译出静态库(也就是工程的product是静态库,而不是**.app)。插播一段MAC系统下cmake安装与使用方法介绍:
MAC默认是没有cmake指令的。要测试你的MAC是否已经装过cmake,可以这样做:打开Terminal,输入cmake --version,如果已经安装,则会显示具体的版本号;否则就是没安装或者没配置成功。 1、从这里http://mac.softpedia.com/get/Development/Compilers/CMake.shtml下载cmake.app,然后安装到默认位置; 2、将cmake.app与terminal相链接。打开terminal,输入以下命令:export PATH=/Applications/CMake.app/Contents/bin:$PATH 3、配置成功。这次再输入cmake就有效了。不过,以上链接只对本terminal窗口有效,一旦关闭或者其他新建的terminal同样要再做一遍!
回到iOS交叉编译上来,使用cmake命令生成xcode工程可以这么做:
1
|
cmake -GXcode .
|
通过该命令可以生成一个project.xcodeproject工程。但是,上述命令并不包含任何关于iOS的信息,因此该xcode工程只能用于MAC库的编译。不过我们可以借助ios-cmake开源项目。 下载iOS_64.cmake这个toolchain文件,然后使用下列命令来生成ios工程:
1
|
cmake -DCMAKE_TOOLCHAIN_FILE=iOS_64.cmake -DCMAKE_IOS_DEVELOPER_ROOT=/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/ -DCMAKE_IOS_SDK_ROOT=/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/ -GXcode .
|
这个过程很容易出错,出错了不要慌,根据terminal的提示大胆地更改iOS_64.cmake(记得提前备份)。我也是一步步调试过来的,以下的iOS_64.cmake是我自己更改后的,SDK是iOS8.3,Xcode6.3,如果环境跟我一样的话理论上说可以直接使用我的.cmake:
如果上面的操作都没错,就会顺利生成一个project.xcodeproject文件,打开后记得做下面几件事情:
1、设置Product->Scheme->Edit Scheme为release模式
2、其他设置如图:
设置完毕后,点击运行,就能生成.a静态库了。这时候,你可以使用下面的命令测试一下生成的静态库是否真的是iOS下的库。
打开terminal,cd到.a所在目录,假设静态库名字为libMyLib.a,输入: lipo -info libMyLib.a ,如果显示 Architectures in the fat file: lib7z_C++_938.a are: armv7 arm64 就说明操作无误了。然后,尽情享用你的静态库吧!
编译linux的静态库是非常简单的,只需要安装好cmake以后,运行以下命令即可:
1
2
|
cmake .
make
|
注意,如果是64位的系统,那么这样只能生成64位的静态库。想要编译出32位的静态库,则必须要先安装32位系统的编译工具链。
1
2
3
4
|
sudo apt-get install libx32gcc-4.8-dev
sudo apt-get install libc6-dev-i386
sudo apt-get install lib32stdc++6
sudo apt-get install g++-multilib
|
然后,只需要指定cxx_flags为-m32即可,对应的CMake的写法为:
1
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m32" )
|
最后用cmake生成makefile并make即可生成32位的静态库。
这个比较简单,直接Xcode -GXcode
,然后用xcodebuild命令即可。
编译android库我们同样可以引入一个toolchain文件,这里我是从android-cmake里面下载的。 在使用这个toolchain文件之前,我们先要使用ndk自带的make-standalone-toolchain.sh脚本来生成对应平台的toolchain.这个脚本位于你的NDK的路径下面的buil/tools目录下。
比如要生成arm平台的toolchain,我们可以使用下列命令:
1
|
sh $ANDROID_NDK/build/tools/make-standalone-toolchain.sh --platform=android-$ANDROID_API_LEVEL --install-dir=./android-toolchain --system=darwin-x86_64 --ndk-dir=/Users/guanghui/AndroidDev/android-ndk-r9d/ --toolchain=arm-linux-androideabi-4.8
|
这里的$ANDROID_NDK
为你的NDK的安装路径。这段命令可以生成arm的toolchain,最终可以编译出armeabi和armeabi-v7a静态库。 如果想生成x86的toolchain,指需要使用下列命令:
1
|
sh $ANDROID_NDK/build/tools/make-standalone-toolchain.sh --platform=android-$ANDROID_API_LEVEL --install-dir=./android-toolchain-x86 --system=darwin-x86_64 --ndk-dir=/Users/guanghui/AndroidDev/android-ndk-r9d/ --toolchain=x86-4.8
|
1
2
3
|
export PATH=$PATH:./android-toolchain/bin
export ANDROID_STANDALONE_TOOLCHAIN=./android-toolchain
cmake -DCMAKE_TOOLCHAIN_FILE=../android.toolchain.cmake -DANDROID_ABI= "armeabi" ..
|
这里直接使用cmake-gui生成对应的VS工程,然后再手动编译即可。
关于Box2D完整的跨平台编译脚本可以参考子龙山人Github
Reference:
cmake by example
mastering cmake pdf
Meta-configuration of C/C++ projects with CMake
Setting up Android standalone toolchain for CMake
cmake and the Android NDK
android-cmake
CMake for Andrioid
ios-cmake google code
以上内容除iOS部分比较多原创外,其他内容大部分转自子龙山人bolg。
##THAT IS ALL.
=======================================
原创文章,转载请注明 编程小翁@博客园,邮件[email protected],微信Jilon,欢迎各位与我在C/C++/Objective-C/机器视觉等领域展开交流!
=======================================