一 CMake介绍
CMake是一个跨平台的安装(编译)工具,可以用简单的语句来描述所有平台的安装(编译过程)。他能够输出各种各样的makefile或者project文件。 CMake 的组态档取名为 CmakeLists.txt。Cmake 并不直接建构出最终的软件,而是产生标准的建构档(如 Unix 的 Makefile 或 Windows Visual C++ 的 projects/workspaces),然后再依一般的建构方式使用。即将头文件、源文件以及库文件等组建成一个工程框架,并设置好需要的配置信息。
CMake 可以编译源代码、制作程式库、产生适配器(wrapper)、还可以用任意的顺序建构执行档。CMake 支持 in-place 建构(二进档和源代码在同一个目录树中)和 out-of-place 建构(二进档在别的目录里),因此可以很容易从同一个源代码目录树中建构出多个二进档。CMake 也支持静态与动态程式库的建构。
二 CMakeLists文件编写
1 基本结构
(1)依赖CMakeLists.txt文件,项目主目标一个,主目录中可指定包含的子目录;
(2)在项目CMakeLists.txt中使用project指定项目名称,add_subdirectory添加子目录;
(3)子目录CMakeLists.txt将从父目录CMakeLists.txt继承设置(TBD,待检验);
2 语法规则
(1)# 注释;
(2)变量:使用set命令显式定义及赋值,在非if语句中,使用${}引用,if中直接使用变量名引用;后续的set命令会清理变量原来的值;
(3)command (args ...) #命令不分大小写,参数使用空格分隔,使用双引号引起参数中空格;
(4)set(var a;b;c) <=> set(var a b c) #定义变量var并赋值为a;b;c这样一个string list;
(5)Add_executable(${var}) <=> Add_executable(a b c) #变量使用${xxx}引用;
(6)条件语句:
if(var) #var 非empty 0 N No OFF FALSE... #非运算使用NOT
…
else()/elseif() … endif(var)
(7)循环语句
Set(VAR a b c)
Foreach(f ${VAR}) …Endforeach(f)
(8)循环语句
WHILE() … ENDWHILE()
3 常用内部变量
CMAKE_BINARY_DIR, PROJECT_BINARY_DIR, _BINARY_DIR:这三个变量内容一致,如果是内部编译,就指的是工程的顶级目录,如果是外部编译,指的就是工程编译发生的目录;
CMAKE_SOURCE_DIR, PROJECT_SOURCE_DIR, _SOURCE_DIR:这三个变量内容一致,都指的是工程的顶级目录;
CMAKE_CURRENT_BINARY_DIR:外部编译时,指的是target目录,内部编译时,指的是顶级目录;
CMAKE_CURRENT_SOURCE_DIR:CMakeList.txt所在的目录;
CMAKE_CURRENT_LIST_DIR:CMakeList.txt的完整路径;
CMAKE_CURRENT_LIST_LINE:当前所在的行;
EXECUTABLE_OUTPUT_PATH:生成的可执行文件的存放路径;
LIBRARY_OUTPUT_PATH:生成的库文件的存放路径;
CMAKE_C_COMPILER:指定C编译器;
CMAKE_C_FLAGS:编译C文件时的选项,如-g;也可以通过add_definitions添加编译选项;
CMAKE_BUILD_TYPE:build 类型(Debug, Release, ...),默认Debug;
BUILD_SHARED_LIBS:如果不进行设置,使用ADD_LIBRARY且没有指定库类型,默认编译生成的库是静态库;
CMAKE_INCLUDE_PATH:配合 FIND_FILE() 以及 FIND_PATH() 使用。如果头文件没有存放在常规路径/usr/include, /usr/local/include等),则可以通过这些变量就行弥补。如果不使用 FIND_FILE 和 FIND_PATH的话,CMAKE_INCLUDE_PATH,没有任何作用;
CMAKE_LIBRARY_PATH:配合 FIND_LIBRARY() 使用。否则没有任何作用;
CMAKE_MODULE_PATH:cmake 为上百个软件包提供了查找器(finder):FindXXXX.cmake,当使用非cmake自带的finder时,需要指定finder的路径,这就是CMAKE_MODULE_PATH,通过SET(CMAKE_MODULE_PATH dir)设置,配合 FIND_PACKAGE()使用;
CMAKE_INSTALL_PREFIX:控制make install是文件会安装到什么地方。默认定义是/usr/local 或 %PROGRAMFILES%;
4 常用命令
CMAKE_MINMUM_REQUIRED(VERSION 2.8) :设定cmake最低版本要求
MESSAGE( status|fatal_error, “message”):两个参数,即消息类型和消息内容,输出信息,方便进行调试
>> message(STATUS "FoundHello: ${HELLO_LIBRARY}") #输出找到的Hello所在目录
SET:定义变量,并赋予其值
>>set(SRC_LIST main.c hello.h) #将main.cpp和hello.h存入变量SRC_LIST
PROJECT:指定项目名称,生成的VC项目的名称;
>>project(hello) #使用hello作为项目名称
ADD_EXECUTABLE:编译可执行程序,指定编译,好像也可以添加.o文件;
>> add_executable (helloDemo demo.cxx demo_b.cxx) #将cxx编译成 可执行文件
ADD_LIBRARY:生成一个库文件;
>> add_library(Hello SHARED hello.cxx) #将hello.cxx编译成静态库 如libHello.a,默认参数是SHARED,即编译成静态库;若要编译成动态库, 则改参数为STATIC
ADD_CUSTOM_TARGET:自定义目标,生成一个自定义文件类型;
TARGET_LINK_LIBRARIES:添加链接库
>> target_link_libraries(demo Hello) #将可执行文件demo与链接库 Hello链接;
INCLUDE_DIRECTORIES:指定头文件的搜索路径,一般用于添加库函数的头文件所在目录;
>> include_directories (${HELLO_SOURCE_DIR}/Hello) #增加Hello为 include目录
LINK_DIRECTORIES:动态链接库或静态链接库的搜索路径,一般用于添加库函数的*.lib或*.dll文件所在目录;
>> link_directories (${HELLO_BINARY_DIR}/Hello) #增加Hello为 link目录
ADD_SUBDIRECTORY:包含子目录,一般用于上一级文件夹中的CMakeLists.txt中指定子目录,并进入执行内部的CMakeLists.txt,在目标文件中生成子文件夹;2个参数的话就是 源→目标 文件夹生成对应;
>> add_subdirectory (Hello) #增加子文件夹src
>> add_subdirectory(src bin) #在cmake目标文件夹中与源文件夹对应 src→bin文件夹
FILE(GLOB var *.h *.hpp *.cpp):搜索当前CMakeLists所在目录中匹配文件,并存在变量var中;注意GLOB这个参数,它不支持子目录。如果你想让它支持子目录,用GLOB_RECURSE。
>>file(GLOB_RECURSE SOURCE "${CMAKE_SOURCE_DIR}/main/*.cpp") #告诉CMake,源文件在哪里
AUX_SOURCE_DIRECTORY ( “sourcedir” variable):收集目录中的文件名并赋值给变量;
SET_TARGET_PROPERTIES( target1 target2 ...PROPERTIES prop1 value1 prop2 value2 ... ):设置目标文件的属性,比如OUTPUT_NAME, VERSION等;
>>set_target_properties(libhello PROPERTIES OUTPUT_NAME"hello") # 将库libhello生成的库文件名称设置为hello,即hello.lib或hello.dll
ADD_DEFINITIONS:添加编译预处理需要的宏定义参数;
>> add_definitions(-DDEBUG) #将在gcc命令行添加DEBUG宏定义
>> add_definitions( “-Wall -ansi –pedantic –g”)
ADD_DEPENDENCIES:定义target依赖的其他target,确保在编译本target之前,其他的target已经被构建
>>add_dependencies(target-name depend-target1 depend-target2 ...)
INSTALL:参数多杂不便解释,以一例如下示之,安装头文件和库文件到相应的目录
>>install(TARGETS libhello ARCHIVE DESTINATION lib)
>>install(TARGETS hello_static ARCHIVE DESTINATION lib)
>>install(FILES hello.h DESTINATION include)
INCLUED: CMake会定义许多的模块来查找通常会用到的包,比如OpenGL或Java。 这些模块为你节省了很多的时间来编写这些查找包。这些模块可以像这样加到你的CMakeList文件中,如下:
>>INCLUDE(${CMAKE_ROOT}/Modules/FindTCL.cmake) #CMAKE_ROOT 总是 定义在CMake中,用于指向CMake安装的路径
FIND_PACKAGE:该命令将会在module路径下查找 Find<name>.cmake。首先它搜索 ${CMAKE_MODULE_PATH}中的所有路径,然后搜索 <CMAKE_ROOT>/share/
cmake-x.y/Modules; 如果这个文件未找到,它将会查找 <Name>Config.cmake 或 <lower-case-name>-config.cmake 文件。这两个文件是库文件安装时自己安装的,将自己的路径硬编码到其中。 前者称为module模式,后者称为 config 模式 每个模块一般都会提供一下几个变量 <name>_FOUND、<name>_INCLUDE_DIR 或 <name>_INCLUDES、<name>_LIBRARY 或 <name>_LIBRARIES 或 <name>_LIBS <name>_DEFINITIONS;
>>find_package(CURL 4.5.0 REQUIRED)
include_directories(${CURL_INCLUDE_DIR})
target_link_libraries(demo ${CURL_LIBRARY}) #搜索QT库,并设定为必须属性,根据搜到的路径,为目标工程添加其头文件和静态库的路径
FIND_LIBRARY:用于寻找在一些指定目录下的特定的Tcl库文件。一般外部库的link方式可以通过两种方法来做,一种是显示添加路径,采用link_directories(), 一种是通过find_library()去查找对应的库的绝对路径。后一种方法是更好的,因为它可以减少不少潜在的冲突。 一般find_library会根据一些默认规则来搜索文件,如果找到,将会set传入的第一个变量参数、否则,对应的参数不被定义,并且有一个xxx-NOTFOUND被定义;可以通过这种方式来调试库搜索是否成功。
>>find_library(TCL_LIBRARY NAMES tcl tc184 tc183 tc 182 tc 180
PATHS /usr/lib /usr/local/lib)
if(TCL_LIBRARY)
target_add_library (Hello TCL_LIBRARY)
endif(TCL_LIBRARY)
三 经典实例
1 测试一:组织自身内部文件
(1)文件目录组织如下:
Test
-build
-src
-main.c
-lib
-function.h
-function.c
(2)将lib文件夹编译成静态函数库,给src中函数调用
(3)需要写三个CMakeLists文件,依此放入Test、src以及lib文件夹的根目录,内容如下:
Test-CMakeLists
project(HELLO)
cmake_minimum_required(VERSION 2.8)
add_subdirectory(lib)
add_subdirectory(src)
src-CMakeLists
include_directories(${PROJECT_SOURCE_DIR}/libfunction)
link_directories(${CMAKE_INSTALL_PREFIX}/lib)
aux_source_directory(. APP_SRC)
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
add_executable(demo ${APP_SRC})
target_link_libraries(demo libfunction)
install(TARGETS demo RUNTIME DESTINATION bin) #安装程序到 ${CMAKE_INSTALL_PREFIX}/bin目录
set_property(TARGET hello PROPERTY INSTALL_RPATH_USE_LINK_PATH TRUE) #设定安装的可执行文件所需的库文件路径,#如果没有该项设置, 会出错:cannot open shared object file: No such file or directory
lib-CMakeLists
set(LIB_SRC function.cpp)
add_definitions("-DLIBHELLO_BUILD")
set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
add_library(libfunction SHARED ${LIB_SRC})
add_library(function_static STATIC ${LIB_SRC})
install(TARGETS libfunction ARCHIVE DESTINATION lib)
install(TARGETS function_static ARCHIVE DESTINATION lib)
install(FILES function.h DESTINATION include)
set_target_properties(libfunction PROPERTIES OUTPUT_NAME "function")
set_target_properties(function_static PROPERTIES OUTPUT_NAME "function")
2 测试二:添加第三方库,并自动配置好路径
(1)文件目录组织如下:
Test
-build
-main.c
(2)将Test中的文件组建工程,并配置好第三方库OpenCV
(3)CMakeLists内容如下:
PROJECT(LSTranform)
# this command finds OpenCV libraries and sets all required variables
FIND_PACKAGE(OpenCV REQUIRED)
INCLUDE_DIRECTORIES(${OPENCV_INCLUDE_DIR})
set(SRC_LIST main.cpp LSTransformEstimator.h LSTransformEstimator.cpp)
add_executable(${PROJECT_NAME} ${SRC_LIST})
TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${OpenCV_LIBS})
PS:更多学习资源
http://www.cmake.org/cmake/help/v3.0/manual/cmake-buildsystem.7.html#manual:cmake-buildsystem(7)
http://jiyeqian.is-programmer.com/2011/7/4/cmake_tutorial.27813.html
http://blog.youkuaiyun.com/panweiguozhou/article/details/6829085