【原+转】用CMake代替makefile进行跨平台交叉编译
2015-04-29 18:10
513 查看
在开始介绍如何使用CMake编译跨平台的静态库之前,先讲讲我在没有使用CMake之前所趟过的坑。因为很多开源的程序,比如png,都是自带编译脚本的。我们可以使用下列脚本来进行编译:
相信手动在类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的头文件包含路径。总之,非常之繁琐。
我的CMakeLists.txt和Box2D的文件结构关系如下图所示:
我的iOS_64.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 就说明操作无误了。然后,尽情享用你的静态库吧!
注意,如果是64位的系统,那么这样只能生成64位的静态库。想要编译出32位的静态库,则必须要先安装32位系统的编译工具链。
然后,只需要指定cxx_flags为-m32即可,对应的CMake的写法为:
最后用cmake生成makefile并make即可生成32位的静态库。
比如要生成arm平台的toolchain,我们可以使用下列命令:
这里的
关于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.
=======================================
原创文章,转载请注明 编程小翁@博客园,邮件zilin_weng@163.com,微信Jilon,欢迎各位与我在C/C++/Objective-C/机器视觉等领域展开交流!
=======================================
./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的头文件包含路径。总之,非常之繁琐。
为什么要使用CMake
为什么我们不使用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!编写CMakeLists.txt
编写一个静态库的CMake配置文件过程如下:(这里我以Box2D为例)1、指定头文件和源文件
include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ) file(GLOB_RECURSE box2d_source_files "${CMAKE_CURRENT_SOURCE_DIR}/Box2D/*.cpp")
我的CMakeLists.txt和Box2D的文件结构关系如下图所示:
# This file is based off of the Platform/Darwin.cmake and Platform/UnixPaths.cmake # files which are included with CMake 2.8.4 # It has been altered for iOS development # Options: # # IOS_PLATFORM = OS (default) or SIMULATOR # This decides if SDKS will be selected from the iPhoneOS.platform or iPhoneSimulator.platform folders # OS - the default, used to build for iPhone and iPad physical devices, which have an arm arch. # SIMULATOR - used to build for the Simulator platforms, which have an x86 arch. # # CMAKE_IOS_DEVELOPER_ROOT = automatic(default) or /path/to/platform/Developer folder # By default this location is automatcially chosen based on the IOS_PLATFORM value above. # If set manually, it will override the default location and force the user of a particular Developer Platform # # CMAKE_IOS_SDK_ROOT = automatic(default) or /path/to/platform/Developer/SDKs/SDK folder # By default this location is automatcially chosen based on the CMAKE_IOS_DEVELOPER_ROOT value. # In this case it will always be the most up-to-date SDK found in the CMAKE_IOS_DEVELOPER_ROOT path. # If set manually, this will force the use of a specific SDK version # Standard settings set (CMAKE_SYSTEM_NAME Darwin) set (CMAKE_SYSTEM_VERSION 1 ) set (UNIX True) set (APPLE True) set (IOS True) # Force the compilers to gcc for iOS include (CMakeForceCompiler) #CMAKE_FORCE_C_COMPILER (gcc gcc) #CMAKE_FORCE_CXX_COMPILER (g++ g++) CMAKE_FORCE_C_COMPILER ("/usr/bin/gcc" gcc) CMAKE_FORCE_CXX_COMPILER ("/usr/bin/g++" g++) # Skip the platform compiler checks for cross compiling set (CMAKE_CXX_COMPILER_WORKS TRUE) set (CMAKE_C_COMPILER_WORKS TRUE) # All iOS/Darwin specific settings - some may be redundant set (CMAKE_SHARED_LIBRARY_PREFIX "lib") set (CMAKE_SHARED_LIBRARY_SUFFIX ".dylib") set (CMAKE_SHARED_MODULE_PREFIX "lib") set (CMAKE_SHARED_MODULE_SUFFIX ".so") set (CMAKE_MODULE_EXISTS 1) set (CMAKE_DL_LIBS "") set (CMAKE_C_OSX_COMPATIBILITY_VERSION_FLAG "-compatibility_version ") set (CMAKE_C_OSX_CURRENT_VERSION_FLAG "-current_version ") set (CMAKE_CXX_OSX_COMPATIBILITY_VERSION_FLAG "${CMAKE_C_OSX_COMPATIBILITY_VERSION_FLAG}") set (CMAKE_CXX_OSX_CURRENT_VERSION_FLAG "${CMAKE_C_OSX_CURRENT_VERSION_FLAG}") # Hidden visibilty is required for cxx on iOS set (CMAKE_C_FLAGS "") set (CMAKE_CXX_FLAGS "-headerpad_max_install_names -fvisibility=hidden -fvisibility-inlines-hidden") set (CMAKE_C_LINK_FLAGS "-Wl,-search_paths_first ${CMAKE_C_LINK_FLAGS}") set (CMAKE_CXX_LINK_FLAGS "-Wl,-search_paths_first ${CMAKE_CXX_LINK_FLAGS}") set (CMAKE_PLATFORM_HAS_INSTALLNAME 1) set (CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS "-dynamiclib -headerpad_max_install_names") set (CMAKE_SHARED_MODULE_CREATE_C_FLAGS "-bundle -headerpad_max_install_names") set (CMAKE_SHARED_MODULE_LOADER_C_FLAG "-Wl,-bundle_loader,") set (CMAKE_SHARED_MODULE_LOADER_CXX_FLAG "-Wl,-bundle_loader,") set (CMAKE_FIND_LIBRARY_SUFFIXES ".dylib" ".so" ".a") # hack: if a new cmake (which uses CMAKE_INSTALL_NAME_TOOL) runs on an old build tree # (where install_name_tool was hardcoded) and where CMAKE_INSTALL_NAME_TOOL isn't in the cache # and still cmake didn't fail in CMakeFindBinUtils.cmake (because it isn't rerun) # hardcode CMAKE_INSTALL_NAME_TOOL here to install_name_tool, so it behaves as it did before, Alex if (NOT DEFINED CMAKE_INSTALL_NAME_TOOL) find_program(CMAKE_INSTALL_NAME_TOOL install_name_tool) endif (NOT DEFINED CMAKE_INSTALL_NAME_TOOL) # Setup iOS platform if (NOT DEFINED IOS_PLATFORM) set (IOS_PLATFORM "OS") endif (NOT DEFINED IOS_PLATFORM) set (IOS_PLATFORM ${IOS_PLATFORM} CACHE STRING "Type of iOS Platform") # Check the platform selection and setup for developer root if (${IOS_PLATFORM} STREQUAL "OS") set (IOS_PLATFORM_LOCATION "iPhoneOS.platform") elseif (${IOS_PLATFORM} STREQUAL "SIMULATOR") set (IOS_PLATFORM_LOCATION "iPhoneSimulator.platform") else (${IOS_PLATFORM} STREQUAL "OS") message (FATAL_ERROR "Unsupported IOS_PLATFORM value selected. Please choose OS or SIMULATOR") endif (${IOS_PLATFORM} STREQUAL "OS") # Setup iOS developer location if (NOT DEFINED CMAKE_IOS_DEVELOPER_ROOT) set (CMAKE_IOS_DEVELOPER_ROOT "/Developer/Platforms/${IOS_PLATFORM_LOCATION}/Developer") endif (NOT DEFINED CMAKE_IOS_DEVELOPER_ROOT) set (CMAKE_IOS_DEVELOPER_ROOT ${CMAKE_IOS_DEVELOPER_ROOT} CACHE PATH "Location of iOS Platform") # Find and use the most recent iOS sdk if (NOT DEFINED CMAKE_IOS_SDK_ROOT) file (GLOB _CMAKE_IOS_SDKS "${CMAKE_IOS_DEVELOPER_ROOT}/SDKs/*") if (_CMAKE_IOS_SDKS) list (SORT _CMAKE_IOS_SDKS) list (REVERSE _CMAKE_IOS_SDKS) list (GET _CMAKE_IOS_SDKS 0 CMAKE_IOS_SDK_ROOT) else (_CMAKE_IOS_SDKS) message (FATAL_ERROR "No iOS SDK's found in default seach path ${CMAKE_IOS_DEVELOPER_ROOT}. Manually set CMAKE_IOS_SDK_ROOT or install the iOS SDK.") endif (_CMAKE_IOS_SDKS) message (STATUS "Toolchain using default iOS SDK: ${CMAKE_IOS_SDK_ROOT}") endif (NOT DEFINED CMAKE_IOS_SDK_ROOT) set (CMAKE_IOS_SDK_ROOT ${CMAKE_IOS_SDK_ROOT} CACHE PATH "Location of the selected iOS SDK") # Set the sysroot default to the most recent SDK set (CMAKE_OSX_SYSROOT ${CMAKE_IOS_SDK_ROOT} CACHE PATH "Sysroot used for iOS support") # set the architecture for iOS - using ARCHS_STANDARD_32_BIT sets armv6,armv7 and appears to be XCode's standard. # The other value that works is ARCHS_UNIVERSAL_IPHONE_OS but that sets armv7 only set (CMAKE_OSX_ARCHITECTURES "$(ARCHS_STANDARD_64_BIT)" CACHE string "Build architecture for iOS") # Set the find root to the iOS developer roots and to user defined paths set (CMAKE_FIND_ROOT_PATH ${CMAKE_IOS_DEVELOPER_ROOT} ${CMAKE_IOS_SDK_ROOT} ${CMAKE_PREFIX_PATH} CACHE string "iOS find search path root") # default to searching for frameworks first set (CMAKE_FIND_FRAMEWORK FIRST) # set up the default search directories for frameworks set (CMAKE_SYSTEM_FRAMEWORK_PATH ${CMAKE_IOS_SDK_ROOT}/System/Library/Frameworks ${CMAKE_IOS_SDK_ROOT}/System/Library/PrivateFrameworks ${CMAKE_IOS_SDK_ROOT}/Developer/Library/Frameworks ) # only search the iOS sdks, not the remainder of the host filesystem set (CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY) set (CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set (CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
我的iOS_64.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静态库(含64位和32位)
编译linux的静态库是非常简单的,只需要安装好cmake以后,运行以下命令即可:cmake . make
注意,如果是64位的系统,那么这样只能生成64位的静态库。想要编译出32位的静态库,则必须要先安装32位系统的编译工具链。
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的写法为:
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m32")
最后用cmake生成makefile并make即可生成32位的静态库。
编译mac静态库
这个比较简单,直接Xcode -GXcode,然后用xcodebuild命令即可。
编译Andoird静态库
编译android库我们同样可以引入一个toolchain文件,这里我是从android-cmake里面下载的。 在使用这个toolchain文件之前,我们先要使用ndk自带的make-standalone-toolchain.sh脚本来生成对应平台的toolchain.这个脚本位于你的NDK的路径下面的buil/tools目录下。比如要生成arm平台的toolchain,我们可以使用下列命令:
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,指需要使用下列命令:
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
export PATH=$PATH:./android-toolchain/bin export ANDROID_STANDALONE_TOOLCHAIN=./android-toolchain cmake -DCMAKE_TOOLCHAIN_FILE=../android.toolchain.cmake -DANDROID_ABI="armeabi" ..
编译Win32,wp8和winrt静态库
这里直接使用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.
=======================================
原创文章,转载请注明 编程小翁@博客园,邮件zilin_weng@163.com,微信Jilon,欢迎各位与我在C/C++/Objective-C/机器视觉等领域展开交流!
=======================================
相关文章推荐
- 用CMake代替makefile进行跨平台交叉编译
- 用CMake代替makefile进行跨平台交叉编译
- 用CMake代替makefile进行跨平台交叉编译
- 用CMake代替makefile进行跨平台交叉编译
- 【原+转】用CMake代替makefile进行跨平台交叉编译
- 在Linux平台上使用Cmake进行交叉编译替代嵌入式平台所使用的Makefile
- 编写一个shell命令脚本来交叉编译.c文件,代替Makefile
- 使用 cmake 进行交叉编译
- linux下使用CmakeLists.txt生成makefile文件进行编译
- cmake 采用toolchain.cmake 文件进行arm-linux跨平台编译
- Unbuntu16.04上用CMake图形界面交叉编译树莓派的OpenCV3.0
- 嵌入式开发之makefile---交叉编译静态库和动态库的生成和调用
- 多个源文件进行编译,即 makefile 的编写
- openwrt交叉编译利用 automake 生成makefile
- Makefile中用宏定义进行条件编译
- Makefile中用宏定义进行条件编译
- 使用clang进行交叉编译
- CMake与跨平台编译
- googletest进行单元测试(使用cmake编译)
- Ubuntu14:cmake生成Makefile编译caffe过程(OpenBLAS/CPU only)