cmake的一些小经验
来源:互联网 发布:淘宝内部优惠卷怎么做 编辑:程序博客网 时间:2024/06/11 20:06
利用cmake来搭建开发环境
对于经常在终端下写程序的non-windows程序员,Makefile绝对是最常用的工具,小到一个文件的简单的测试程序,大到数百个文件的商业软件,只需要有shell,一个make命令就可得到可运行的程序,Makefile绝对功不可没;可惜世界中不是那么太平,不但各个Posix系统的API千差万别,硬件平台各异,就连Makefile本身也有多个不兼容的格式,譬如GNU Makefile 拿到Solaris平台上就没法make下去,除非你有gmake,但gmake对并行编译的支持就没有solaris自带的dmake要好了。GNU autotools提供了一个不错的选择,可以做到组织工具链来生成所需的Makefile,但缺陷是学习起来比较麻烦,而且模版文件写起来比较费劲。老实说我跟Makefile打了近3年的交道,几乎没有自己写过automake脚本,相反的工作倒是干了不少,譬如分析生成的Makefile运行过程,然后模拟自己手写Makefile;得到一个轻爽的定制环境。
除了autotools,其实也有不少其他的工具,譬如apache的ant,基于Python的scons;ant在java界是鼎鼎大名了,可惜对c++的支持确实让我感觉很不习惯;scons号称可以嵌入Python代码,用起来也算简单,但是想实现复杂的功能就很头疼了,而且运行速度让人挠头。
cmake则弥补了上述几个工具的诸多缺陷:
1> 易于学习,文档易懂,只需牢记以下两个命令即可:
cmake --help-command-list
cmake --help-command xxx
cmake --help-variable-list
cmake --help-variable yyy
3> 生成的Makefile文件简洁易懂
4> 编译器选项可自己在ccmake中编辑,利于交叉编译
5> 支持集成ctest/cpack,前者可以方便的做单元测试,后者则可以打包生成tgz/rpm
6> 支持多个生成器,可以生成eclipse/codeblocks/gmake/unix make文件,甚至可以生成VC各个版本的dsw/sln.
7> 内嵌语言,可以自己写函数、宏等
对于经常写小测试程序的人来说,在test目录下加上个CMakeLists.txt,里边加上几行简单的语句就可以方便的以后重复使用了。对于这种情况,手工写的Makefile碰到依赖检测这种麻烦的事情往往力不从心,automake又太小题大作,而cmake则恰到好处了。
对于大型程序,cmake可以自己定制生成的中间文件和目标文件路径,有效避免了automake带来的每个目录下生成一大堆文件的弊端,也不需要手工写Makefile。
最有用的是可以生成多个知名IDE的工程文件,包括Windows下的vc6-vc9.
cmake的一些小经验
初用CMake或者对其了解不太深的人,可能经常会被路径包含、库搜索路径、链接路径、RPath这些问题所绊倒,因为这些东西在手工执行gcc或者编写makefile的时候是很轻而易举的任务。
其实我当初也有不少疑惑,不过通过较长时间的实践和阅读manual,总算有了个相对很清晰的认识。
- 如何使用其manual
cmake的帮助组织的还是很有规律的,了解了其规律,找自己想要的东西就会很简单,所以个人觉得这一点可能是最重要的。其help系统大概是这么几类:
- command
这个是实用过程中最长用到的,相当于一般脚步语言中的基本语法,包括定义变量,foreach,string,if,builtin command都在这里。
可以用如下这些命令获取帮助:
cmake --help-commands这个命令将给出所有cmake内置的命令的详细帮助,一般不知道自己要找什么或者想随机翻翻得时候,可以用这个。
我一般更常用的方法是将其重定向到less里边,然后在编辑器里边搜索关键字。
另外也可以用如下的办法层层缩小搜索范围:
cmake --help-command-listcmake --help-command-list | grep find
skyscribe@skyscribe:~/program/ltesim/bld$ cmake --help-command-list | grep findfind_filefind_libraryfind_packagefind_pathfind_program
cmake --help-command find_library
cmake version 2.6-patch 4------------------------------------------------------------------------------SingleItem
find_library Find a library.
find_library(<VAR> name1 [path1 path2 ...])
This is the short-hand signature for the command that is sufficient in many cases. It is the same as find_library(<VAR> name1 [PATHS path1 path2 ...])
find_library( <VAR> name | NAMES name1 [name2 ...] [HINTS path1 [path2 ... ENV var]] [PATHS path1 [path2 ... ENV var]] [PATH_SUFFIXES suffix1 [suffix2 ...]] [DOC "cache documentation string"] [NO_DEFAULT_PATH] [NO_CMAKE_ENVIRONMENT_PATH] [NO_CMAKE_PATH] [NO_SYSTEM_ENVIRONMENT_PATH] [NO_CMAKE_SYSTEM_PATH] [CMAKE_FIND_ROOT_PATH_BOTH | ONLY_CMAKE_FIND_ROOT_PATH | NO_CMAKE_FIND_ROOT_PATH] )
- variable
和command的帮助比较类似,只不过这里可以查找cmake自己定义了那些变量你可以直接使用,譬如OSName,是否是Windows,Unix等。
我最常用的一个例子:
cmake --help-variable-list | grep CMAKE | grep HOSTCMAKE_HOST_APPLECMAKE_HOST_SYSTEMCMAKE_HOST_SYSTEM_NAMECMAKE_HOST_SYSTEM_PROCESSORCMAKE_HOST_SYSTEM_VERSIONCMAKE_HOST_UNIXCMAKE_HOST_WIN32这里查找所有CMake自己定义的builtin变量;一般和系统平台相关。
如果希望将所有生成的可执行文件、库放在同一的目录下,可以如此做:
这里的target_dir是一个实现设置好的绝对路径。(CMake里边绝对路径比相对路径更少出问题,如果可能尽量用绝对路径)
# Targets directoryset(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${target_dir}/lib)set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${target_dir}/lib)set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${target_dir}/bin)
- property
Property一般很少需要直接改动,除非你想修改一些默认的行为,譬如修改生成的动态库文件的soname等。
譬如需要在同一个目录下既生成动态库,也生成静态库,那么默认的情况下,cmake根据你提供的target名字自动生成类似的libtarget.so, libtarget.a,但是同一个project只能同时有一个,因为target必须唯一。
这时候,就可以通过修改taget对应的文件名,从而达到既生成动态库也产生静态库的目的。
譬如:
cmake --help-property-list | grep NAMEGENERATOR_FILE_NAMEIMPORTED_SONAMEIMPORTED_SONAME_<CONFIG>INSTALL_NAME_DIROUTPUT_NAMEVS_SCC_PROJECTNAMEskyscribe@skyscribe:~$ cmake --help-property OUTPUT_NAMEcmake version 2.6-patch 4------------------------------------------------------------------------------SingleItemOUTPUT_NAMESets the real name of a target when it is built.Sets the real name of a target when it is built and can be used tohelp create two targets of the same name even though CMake requiresunique logical target names. There is also a <CONFIG>_OUTPUT_NAMEthat can set the output name on a per-configuration basis.
- module
用于查找常用的模块,譬如boost,bzip2, python等。通过简单的include命令包含预定义的模块,就可以得到一些模块执行后定义好的变量,非常方便。
譬如常用的boost库,可以通过如下方式:
# Find boost 1.40INCLUDE(FindBoost)find_package(Boost 1.40.0 COMPONENTS thread unit_test_framework)if(NOT Boost_FOUND)
message(STATUS "BOOST not found, test will not succeed!")
endif()一般开头部分的解释都相当有用,可满足80%需求:cmake --help-module FindBoost | head -40cmake version 2.6-patch 4------------------------------------------------------------------------------SingleItemFindBoostTry to find Boost include dirs and librariesUsage of this module as follows:== Using Header-Only libraries from within Boost: ==find_package( Boost 1.36.0 )if(Boost_FOUND)
include_directories(${Boost_INCLUDE_DIRS})add_executable(foo foo.cc)endif()== Using actual libraries from within Boost: ==set(Boost_USE_STATIC_LIBS ON)set(Boost_USE_MULTITHREADED ON)find_package( Boost 1.36.0 COMPONENTS date_time filesystem system ... )
if(Boost_FOUND)
include_directories(${Boost_INCLUDE_DIRS})add_executable(foo foo.cc)target_link_libraries(foo ${Boost_LIBRARIES})endif()The components list needs to contain actual names of boost libraries
- 如何根据其生成的中间文件查看一些关键信息
CMake相比较于autotools的一个优势就在于其生成的中间文件组织的很有序,并且清晰易懂,不像autotools会生成天书一样的庞然大物(10000+的不鲜见)。
一般CMake对应的Makefile都是有层级结构的,并且会根据你的CMakeLists.txt间的相对结构在binary directory里边生成相应的目录结构。
譬如对于某一个target,一般binary tree下可以找到一个文件夹: CMakeFiles/<targentName>.dir/,比如:
skyscribe@skyscribe:~/program/ltesim/bld/dev/simcluster/CMakeFiles/SIMCLUSTER.dir$ ls -ltotal 84-rw-r--r-- 1 skyscribe skyscribe 52533 2009-12-12 12:20 build.make-rw-r--r-- 1 skyscribe skyscribe 1190 2009-12-12 12:20 cmake_clean.cmake-rw-r--r-- 1 skyscribe skyscribe 4519 2009-12-12 12:20 DependInfo.cmake-rw-r--r-- 1 skyscribe skyscribe 94 2009-12-12 12:20 depend.make-rw-r--r-- 1 skyscribe skyscribe 573 2009-12-12 12:20 flags.make-rw-r--r-- 1 skyscribe skyscribe 1310 2009-12-12 12:20 link.txt-rw-r--r-- 1 skyscribe skyscribe 406 2009-12-12 12:20 progress.makedrwxr-xr-x 2 skyscribe skyscribe 4096 2009-12-12 12:20 src这里,每一个文件都是个很短小的文本文件,内容相当清晰明了。build.make一般包含中间生成文件的依赖规则,DependInfo.cmake一般包含源代码文件自身的依赖规则。比较重要的是flags.make和link.txt,前者一般包含了类似于GCC的-I的相关信息,如搜索路径,宏定义等;后者则包含了最终生成target时候的linkage信息,库搜索路径等。这些信息在出现问题的时候是个很好的辅助调试手段。
- 文件查找、路径相关
- include
一般常用的是:
include_directories()用于添加头文件的包含搜索路径cmake --help-command include_directoriescmake version 2.6-patch 4------------------------------------------------------------------------------SingleIteminclude_directoriesAdd include directories to the build.include_directories([AFTER|BEFORE] [SYSTEM] dir1 dir2 ...)Add the given directories to those searched by the compiler for
include files. By default the directories are appended onto the
current list of directories. This default behavior can be changed by
setting CMAKE_include_directories_BEFORE to ON. By using BEFORE orAFTER you can select between appending and prepending, independentfrom the default. If the SYSTEM option is given the compiler will be
told that the directories are meant as system include directories on
some platforms.link_directories()用于添加查找库文件的搜索路径cmake --help-command link_directoriescmake version 2.6-patch 4------------------------------------------------------------------------------SingleItemlink_directoriesSpecify directories in which the linker will look for libraries.
link_directories(directory1 directory2 ...)Specify the paths in which the linker should search for libraries.
The command will apply only to targets created after it is called.For historical reasons, relative paths given to this command arepassed to the linker unchanged (unlike many CMake commands whichinterpret them relative to the current source directory).
- library search
一般外部库的link方式可以通过两种方法来做,一种是显示添加路径,采用link_directories(), 一种是通过find_library()去查找对应的库的绝对路径。
后一种方法是更好的,因为它可以减少不少潜在的冲突。
一般find_library会根据一些默认规则来搜索文件,如果找到,将会set传入的第一个变量参数、否则,对应的参数不被定义,并且有一个xxx-NOTFOUND被定义;可以通过这种方式来调试库搜索是否成功。
对于库文件的名字而言,动态库搜索的时候会自动搜索libxxx.so (xxx.dll),静态库则是libxxx.a(xxx.lib),对于动态库和静态库混用的情况,可能会出现一些混乱,需要格外小心;一般尽量做匹配连接。
- rpath
所谓的rpath是和动态库的加载运行相关的。我一般采用如下的方式取代默认添加的rpath:
# RPATH and library search settingSET(CMAKE_SKIP_BUILD_RPATH FALSE)SET(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE)SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/nesim/lib")
SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
- cmake的一些小经验
- cmake的一些小经验
- cmake的一些小经验
- 一些小的经验积累
- 一些小的网站经验
- 面试的一些小经验
- Android的一些小经验
- 下午试题解题的一些小经验
- hibernate 关联查询的一些小经验
- 手机图片上传的一些小经验
- 关于GRIDVIEW的一些小经验
- 学习小波的一些经验
- 分享调试SI4432的一些小经验
- Spark1.0.0 的一些小经验
- 日本生活的一些小经验
- 今天写后台的一些小经验
- 关于安装GenyMotion的一些小经验
- 关于achartengine的使用一些小经验
- apache 编译错误 configure: error: APR not found
- Android 注解和反射原理和实现学习(下)
- js contains方法
- xterm run command
- CF#309(Div2)C(组合数的数学题)
- cmake的一些小经验
- js中cookie的使用详细分析
- 查看安卓应用包名
- richedit插入图片错误
- ssh学习路线
- 正则表达式
- 当Fragment通过ViewPager嵌套Fragment时第二进入不显示的问题
- <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/
- git创建版本库