CMake你该知道的事!

CMake你该知道的事

CMake是一个开源的跨平台工具系列,用于构建、测试和打包软件。CMake用于使用简单的平台和编译器独立配置文件控制软件编译过程,并生成可在您选择的编译器环境中使用的本机makefile和工作区。CMake工具套件是由Kitware创建的,以满足对ITK和VTK等开源项目强大的跨平台构建环境的需求。CMake是Kitware的软件开发商业支持的开源平台集合的一部分。


工作需要去仔细研究了一下CMake并谈谈理解。


CMake基本使用

简单示例

示例:求一个数的平方根,编写一个头文件cal_sqrt.h和一个.cc文件main.cc。

//目录结构
// └── demo
//     ├── build
//     ├── cal_sqrt.h
//     ├── CMakeLists.txt
//     └── main.cc// cal_sqrt.h
#ifndef __CAL_SQRT_H__
#define __CAL_SQRT_H__#include <cmath>double cal_sqrt(double value);#endif// main.cc
#include "cal_sqrt.h"
#include <iostream>int main() {
 double value = 81.0;
 double res = 0.0;
​
 res = cal_sqrt(value);
 std::cout<<value<<" sqrt result is: "<<res<<std::endl;return 0;
}double cal_sqrt(double value) {
 return sqrt(value);
}

CMakeList.txt内容:

cmake_minimum_required(VERSION 2.8)   #1  指定 cmake 的最小版本
project(cal_sqrt)                     #2  设置项目名
​
include_directories(                  #3  包含头文件目录
   ${PROJECT_SOURCE_DIR}/include
  )
set(SRC                               #4  添加可执行源文件
   ${PROJECT_SOURCE_DIR}/main.cc
  )
add_executable(cal_sqrt ${SRC})

使用set添加需要的源文件,将所有源文件包含到SRC中,再使用add_executable(XXX ${SRC})生成可执行文件。

注意:使用add_executable(XXX ${SRC})生成可执行文件时,SRC中包含的所有源文件中只能有一个main()函数,否则在cmake在执行add_executable(XXX ${SRC})会识别到多个main()函数,导致生成可执行文件失败。SRC中包含多个main()函数时,可使用如下方法生成多个可执行文件。

set(SRC
       ${PROJECT_SOURCE_DIR}/main1.cc
       ${PROJECT_SOURCE_DIR}/main2.cc
      )
​
foreach (src ${SRC})
  get_filename_component(name ${src} NAME_WE)
  add_executable(${name} ${src})
endforeach (src ${SRC})

二、cmake常用命令

以上用到的都是cmake中比较常用的命令,cmake还有许多其他常用命令。

aux_source_directory

aux_source_directory,查找源文件并保存到相应的变量中。

# 搜索dir目录下所有的源文件并将其存储在变量VAR中。
aux_source_directory(dir VAR)

add_library

1.生成库文件
该命令的主要作用就是将指定的源文件生成库文件,然后添加到工程中去。该命令常用的语法如下。

add_library( [STATIC | SHARED | MODULE]
[EXCLUDE_FROM_ALL]
[source1] [source2] […])

  • 生成一个名为的库文件。
  • 指定STATIC, SHARED, MODULE参数来指定要创建的库的类型, STATIC对应的静态库(.a),SHARED对应共享动态库(.so),MODULE库是一种不会被链接到其它目标中的插件。
  • [EXCLUDE_FROM_ALL], 如果指定了这一属性,对应的一些属性会在目标被创建时被设置(指明此目录和子目录中所有的目标,是否应当从默认构建中排除, 子目录的IDE工程文件/Makefile将从顶级IDE工程文件/Makefile中排除)
  • source1 source2 …用来指定源文件

2.导入已有的库

add_library( [STATIC | SHARED | MODULE | UNKNOWN] IMPORTED)

导入了一个已存在的库文件,导入库一般配合set_target_properties使用,这个命令用来指定导入库的路径,比如:

add_library(test SHARED IMPORTED)
set_target_properties( test 								# 指定目标库名称
                      PROPERTIES IMPORTED_LOCATION 			# 指明要设置的参数
                      libs/src/${ANDROID_ABI}/libtest.so 	# 设定导入库的路径
                      )

link_directories

link_directories,指定链接库搜索目录。

link_directories(
   ${CMAKE_CURRENT_SOURCE_DIR}/libs
  )

其中,CMAKE_CURRENT_SOURCE_DIR指当前处理的 CMakeLists.txt 所在的路径。

target_link_libraries

target_link_libraries,将目标文件与库文件进行链接。该指令的语法如下.

# 将lib1, lib2,...链接到<name>target_link_libraries(<name> [lib1] [lib2] [...])

上述为add_executable()生成的可执行文件,lib1,lib2,…为add_library生成或导入的库。

set

set,设置cmake变量。
例子:

# 设置可执行文件的输出路径(EXCUTABLE_OUTPUT_PATH是全局变量)
set(EXECUTABLE_OUTPUT_PATH [output_path])
​
# 设置库文件的输出路径(LIBRARY_OUTPUT_PATH是全局变量)
set(LIBRARY_OUTPUT_PATH [output_path])
​
# 设置C++编译参数(CMAKE_CXX_FLAGS是全局变量)
set(CMAKE_CXX_FLAGS "-Wall std=c++11")
​
# 设置源文件集合(SOURCE_FILES是本地变量即自定义变量)
set(SOURCE_FILES main.cc test.cc ...)

add_subdirectory

如果当前目录下还有子目录时可以使用add_subdirectory,子目录中也需要包含有CMakeLists.txt。
例子:

# sub_dir指定包含CMakeLists.txt和源码文件的子目录位置
# binary_dir是输出路径, 一般可以不指定
add_subdirecroty(sub_dir [binary_dir])

set_property

set_property,在给定的作用域内设置一个命名的属性。

set_property(<GLOBAL |
          DIRECTORY [dir] |
          TARGET [target ...] |
          SOURCE [src1 ...] |
          TEST [test1 ...] |
          CACHE [entry1 ...]>
            [APPEND] [APPEND_STRING]
            PROPERTY <name> [value ...])

在某个域中对零个或多个对象设置一个属性。第一个参数决定该属性设置所在的域。它必须为下面中的其中之一。

  • GLOBAL域是唯一的,并且不接特殊的任何名字。
  • DIRECTORY域默认为当前目录,但也可以用全路径或相对路径指定其他的目录(前提是该目录已经被cmake处理)。
  • TARGET域可命名零或多个已经存在的目标。
  • SOURCE域可命名零或多个源文件。注意:源文件属性只对在相同目录下的目标是可见的(CMakeLists.txt)。
  • TEST域可命名零或多个已存在的测试。
  • CACHE域必须命名零或多个已存在条目的cache.

必选项PROPERTY后面紧跟着要设置的属性的名字。其他的参数用于构建以分号隔开的列表形式的属性值。如果指定了APPEND选项,则指定的列表将会追加到任何已存在的属性值当中。如果指定了APPEND_STRING选项,则会将值作为字符串追加到任何已存在的属性值。

message

message,用于打印信息

message(${PROJECT_SOURCE_DIR})
message("build with debug mode")
message(STATUS "this is status message" )
message(WARNING "this is warnning message")
message(FATAL_ERROR "this build has many error") # FATAL_ERROR 会导致编译失败

add_definitions

add_definitions,为当前路径以及子目录的源文件加入由-D引入的define flag

# 添加FOO宏定义,在源文件中使用#if defined(FOO),可以针对定义了FOO宏的情况做额外的处理
add_definitions(-DFOO ...)# add_definitions一般可以与option结合使用,控制代码的开启和关闭
# 在cmake时可以添加参数控制宏的开启和关闭
# 开启: cmake -DUSE_MACRO=on ..
# 关闭: cmake -DUSE_MACRO=off ..
option(USE_MACRO "Build the project using macro" OFF)
if(USE_MACRO)
add_definitions("-DUSE_MACRO")
endif(USE_MACRO)

file

file,文件操作命令。

# 将message写入filename文件中,会覆盖文件原有内容
file(WRITE filename "message")
# 将message写入filename文件中,会追加在文件末尾
file(APPEND filename "message")
# 从filename文件中读取内容并存储到var变量中,如果指定了numBytes和offset,
# 则从offset处开始最多读numBytes个字节,另外如果指定了HEX参数,则内容会以十六进制形式存储在var变量中
file(READ filename var [LIMIT numBytes] [OFFSET offset] [HEX])
# 重命名文件
file(RENAME <oldname> <newname>)
# 删除文件, 等于rm命令
file(REMOVE [file1 ...])
# 递归的执行删除文件命令, 等于rm -r
file(REMOVE_RECURSE [file1 ...])
# 根据指定的url下载文件
# timeout超时时间; 下载的状态会保存到status中; 下载日志会被保存到log; sum指定所下载文件预期的MD5
# 值,如果指定会自动进行比对,如果不一致,则返回一个错误; SHOW_PROGRESS,进度信息会以状态信息的形式被
# 打印出来
file(DOWNLOAD url file [TIMEOUT timeout] [STATUS status] [LOG log] [EXPECTED_MD5 sum] [SHOW_PROGRESS])
# 创建目录
file(MAKE_DIRECTORY [dir1 dir2 ...])
# 会把path转换为以unix的/开头的cmake风格路径,保存在result中
file(TO_CMAKE_PATH path result)
# 它会把cmake风格的路径转换为本地路径风格:windows下用"\",而unix下用"/"
file(TO_NATIVE_PATH path result)

三、常用变量

预定义变量

PROJECT_SOURCE_DIR:工程的根目录

PROJECT_BINARY_DIR:运行 cmake 命令的目录,通常是 ${PROJECT_SOURCE_DIR}/build

PROJECT_NAME:返回通过 project 命令定义的项目名称

CMAKE_CURRENT_SOURCE_DIR:当前处理的 CMakeLists.txt 所在的路径

CMAKE_CURRENT_BINARY_DIR:target 编译目录

CMAKE_CURRENT_LIST_DIR:CMakeLists.txt 的完整路径

CMAKE_CURRENT_LIST_LINE:当前所在的行

CMAKE_MODULE_PATH:定义自己的 cmake 模块所在的路径,SET(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake),然后可以用INCLUDE命令来调用自己的模块

EXECUTABLE_OUTPUT_PATH:重新定义目标二进制可执行文件的存放位置

LIBRARY_OUTPUT_PATH:重新定义目标链接库文件的存放位置

系统信息

­CMAKE_MAJOR_VERSION:cmake 主版本号,比如 3.4.1 中的 3

­CMAKE_MINOR_VERSION:cmake 次版本号,比如 3.4.1 中的 4

­CMAKE_PATCH_VERSION:cmake 补丁等级,比如 3.4.1 中的 1

­CMAKE_SYSTEM:系统名称,比如 Linux-­2.6.22

­CMAKE_SYSTEM_NAME:不包含版本的系统名,比如 Linux

­CMAKE_SYSTEM_VERSION:系统版本,比如 2.6.22

­CMAKE_SYSTEM_PROCESSOR:处理器名称,比如 x86_64,aarch64

­UNIX:在所有的类 UNIX 平台下该值为 TRUE,包括 OS X 和 cygwin

­WIN32:在所有的 win32 平台下该值为 TRUE,包括 cygwin

设置编译选项

BUILD_SHARED_LIBS:这个开关用来控制默认的库编译方式,如果不进行设置,使用 add_library 又没有指定库类型的情况下,默认编译生成的库都是静态库。如果 set(BUILD_SHARED_LIBS ON) 后,默认生成的为动态库

CMAKE_C_FLAGS:设置 C 编译选项

CMAKE_CXX_FLAGS:设置 C++ 编译选项

四、构建方式

使用cmake命令进行构建,构建分为内部构建(in-source)和外部构建(out-of-source)

内部构建

在源文件目录下构建,执行cmake .,生产的Makefile文件、以及一些cmake缓存文件与源文件同目录。

外部构建

内部构建生成的cmake的中间文件与源代码文件混杂在一起,并且cmake没有提供清理这些中间文件的命令。
所以cmake推荐使用外部构建,步骤如下。

  • 在CMakeLists.txt的同级目录下,新建一个build文件夹
  • 进入build文件夹,执行cmake …命令,这样所有的中间文件以及Makefile都在build目录下了
  • 在buil目录下执行make就可以得到可执行文件

复杂项目示例

项目结构

├── demo
│   ├── build
│   ├── CMakeLists.txt
│   ├── main.cc
│   └── test
│       ├── CMakeLists.txt
│       ├── test.cc
│       └── test.h

项目内容

使用自定义编译选项,在编译时,如果设置-DUSE_SQRT=off,则计算9的平方,如果设置-DUSE_SQRT=on,则计算9的平方根。默认-DUSE_SQRT=on。

//test.h
#ifndef __TEST_H__
#define __TEST_H__#include <cmath>double calculation(double value);#endif//test.cc
#include "test.h"double calculation(double value) {
#if defined(USE_SQRT)
	return sqrt(value);
#else
	return pow(value, 2);
#endif
}//main.cc
#include "./test/test.h"
#include <iostream>int main() {
	double value = 9.0;
	double res = 0.0;

	res = calculation(value);#if defined(USE_SQRT)
	std::cout<<value<<" sqrt result is: "<<res<<std::endl;
#else
	std::cout<<value<<" pow(2) result is: "<<res<<std::endl;
#endif
	return 0;
}

在CMakeLists.txt中添加自定义编译选项。

# ./CMakeLists.txt
cmake_minimum_required(VERSION 2.8)project(demo)
​
# 设置USE_SQRT编译选项
option(USE_SQRT "Build the project with sqrt" ON)
# 如果编译选项-DUSE_SQRT=ON,则添加宏定义USE_SQRT
if(USE_SQRT)
add_definitions("-DUSE_SQRT")
endif(USE_SQRT)include_directories(
   ${PROJECT_SOURCE_DIR}/test
  )
# 添加子目录
add_subdirectory(test)
# 设置链接库
set(EXTRA_LIBS ${EXTRA_LIBS} test)set(MAIN_SRC
   ${PROJECT_SOURCE_DIR}/main.cc
)
# 生成可执行文件
add_executable(main ${MAIN_SRC})
# 将可执行文件mian与库文件进行链接
target_link_libraries (main ${EXTRA_LIBS})
​
# ./test/CMakeLists.txt
# 生成动态库
add_library(test SHARED ${CMAKE_CURRENT_SOURCE_DIR}/test.cc)

总结

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值