[①CMake]: CMake简介,第一个CMake工程样例

CMake简介

CMake是cross platform make的缩写,从名称中就可以看出它是一个跨平台的编译工具,开发者编写一种与平台无关的CMakeList.txt文件来定制整个编译流程,它会根据不同平台生成不同的Makefile(相对于直接写Makefile来说,CMake的命令语法比较简单),然后编译运行,实现了一个工程能够适配不同平台的一种编译手段。CMake支持in-source构建(二进制文件和源代码在同一个目录中)和out-of-source构建(二进制文件在别的目录里),另外CMake 也支持静态与动态库的构建。CMake官方网站是:www.cmake.org

Ubuntu 18.04 下CMake的安装方法:

sudo apt install cmake

安装完成后可以查看CMake的版本:

cmake --version

CMake工程例程

如果工程结构比较简单的话,我们只要使用一个顶层CMakeLists.txt就够了,但是如果工程结构比较复杂的话,由很多目录组成,这时候把所有构建内容写在一个CMakeLists.txt就显得有点冗长了,这里就推荐使用多个CMakeLists.txt进行构建,同样的从顶层CMakeLists.txt开始执行,通过add_subdirectory把子级的CMakeLists.txt包含进来(一般来说,可以一个文件夹包含一个CMakeLists.txt),从而完成整个工程的构建。下面是博主创建的一个简单的CMake工程结构:

这里推荐使用out-of-source构建的方法,所有的源文件都在src这个文件夹里,所有编译产生的临时文件,和最后的可执行程序都放在build文件夹里,这样的好处是方便对源文件进行版本管理,所有编译的临时文件都是不需要版本追踪的。
首先介绍下编译的流程:

mkdir build         // 没有创建build文件夹的话
cd build
cmake ..            // 使用上级 ../CMakeLists.txt文件作为起始点
make                // 开始编译

使用cmake命令之后,会检查CMakeLists.txt中的语法,配置编译环境,如果正确的话会出现下面类似的输出:

CMakeLists.txt(No.0)

接下来我们来看下最顶层CMakeLists.txt(No.0)里的内容:

cmake_minimum_required(VERSION 2.8)

# create project 
project(project_box)

# set compiler
set(CMAKE_CXX_COMPILER "x86_64-linux-gnu-g++-8")
# add compiler flags
add_compile_options(-march=native -O3)

# set binary file output path
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin)
# set lib output path
set(LIBRARY_OUTPUT_PATH    ${CMAKE_BINARY_DIR}/lib)

message(STATUS "binary  dir is" ${EXECUTABLE_OUTPUT_PATH})
message(STATUS "library dir is" ${LIBRARY_OUTPUT_PATH})

# add subdirectory
add_subdirectory(src)

CMakeLists.txt 的内容主要由CMake的命令和注释组成,使用的注意事项:

  1. CMakeLists.txt 文件名严格区分大小写
  2. CMake命令可以不区分大小写,大小写混写都行,每条语句结尾不需要加分号
  3. CMakeLists.txt里的参数严格区分大小写,参数名称只能用字母、数字、下划线、破折号
  4. 参数使用 ${} 来引用 , 参数之间使用空格分割
  5. 注释以符号 # 开头

cmake_minimum_required(VERSION 2.8):指定运行这个配置文件所需的CMake最低版本,这个命令一般要求放CMakeLists.txt文件的最开始,在其他命令执行前调用,这是因为后续CMake命令的行为表现可能跟版本有关系。不同的CMakeLists.txt要求的最低版本可以写不同,cmake_minimum_required命令暗含了对cmake_policy的调用,而cmake_policy指定了CMake版本的策略机制,主要是为了保障在不同的CMake版本间的兼容性。同样的道理我们也可以指定最高版本,例如下面的命令就指定了CMake的版本范围:

cmake_minimum_required(VERSION 3.10.0...3.12.0)

project(project_box):设置项目的名称,这个命令会自动生成一些变量,假如整个工程是由多个项目组成,那么引用其他项目的某些变量会变得容易些。比如自动生成了PROJECT_NAME 这个变量,变量值${PROJECT_NAME}为project_box。

set(CMAKE_CXX_COMPILER “”) : set()命令可以设置变量的值,CMAKE_CXX_COMPILER是CMake的环境变量,这个命令是选择CMake的编译器,因为例程是C++的项目所以改变的是CMAKE_CXX_COMPILER这个环境变量,如果是C的项目,那命令可以是下面类似的:

set(CMAKE_C_COMPILER "/usr/local/gcc/bin/gcc")

当然也可以不设置这两个环境变量,CMake会默认去系统目录/usr/bin下找编译器。因为可以灵活选择编译器,这里就体现了CMake这个跨平台的优势,我们可以让同一个代码通过使用CMake在不同平台下编译,实现的方法就是设置一个变量和选择编译器这个命令,比如在CMakeLists.txt里我们可以这么写:

# set compiler
if(NOT TARGET)
  set(TARGET "NATIVE")  
endif()

if (${TARGET} STREQUAL "NATIVE") # local cpu
  set(CMAKE_CXX_COMPILER "x86_64-linux-gnu-g++-8")
elseif (${TARGET} STREQUAL "A9") # cortex-a9
  set(CMAKE_CXX_COMPILER "arm-linux-gnueabihf-g++")
endif()

STREQUAL 用于比较字符串,检测TARGET这个变量的值。当没有设置TARGET这个变量时,CMake会使用g++8编译器,对应的就是x86平台,如果TARGET定义为A9,那么CMake就会使用arm-linux-gnueabihf-g++这个交叉编译器,对应的就是arm cortex-a9这个平台,具体使用方法如下:

mkdir build_a9         // 创建针对cortex-a9的build文件夹
cd build_a9         
cmake .. -DTARGET=A9   // 使用上级 ../CMakeLists.txt文件作为起始点, 定义TARGET变量
make                   // 开始编译

add_compile_options(-march=native -O3):这个命令会添加些编译器选项,比如这里-march=native,告诉编译器当前cpu的架构,根据指定的架构对代码进行指令的优化,-O3提供最高级的代码优化。当然这里也可以设置些预处理宏定义给源文件使用(通常用于一些编译开关之类的),比如:

#define VALUE_MACRO                        // 在源文件中

add_compile_options(-DVALUE_MACRO)         // 无参
add_compile_options(-DVALUE_MACRO=0x100)   // 有参

设置预处理宏定义也可以通过add_definitions,add_compile_definitions命令实现。

set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin) :这个命令设置编译出的二进制文件的存放路径,EXECUTABLE_OUTPUT_PATH和CMAKE_BINARY_DIR也是CMake的环境变量,CMAKE_BINARY_DIR这个变量在terminal里执行完cmake命令之后就设置好了,因为是out-of-place构建,指的是程编译发生的build目录,所以编译完成后二进制文件会放在build/bin文件夹下。如果是in-source的构建方法,那么CMAKE_BINARY_DIR指的就是工程顶层目录。

set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}/lib):LIBRARY_OUTPUT_PATH是CMake的环境变量,设置编译出的动态库以及静态库的存放路径,这里为/build/lib。

message(STATUS “binary dir is” ${EXECUTABLE_OUTPUT_PATH}):CMake提供了messge()这个消息命令,在执行make的时候会有提示消息展示。STATUS是消息的类型,我们可以通过消息的类型来控制编译是否继续运行,也可以做些简单的打印提示:

  1. STATUS = 非重要消息
  2. WARNING = CMake警告,编译会继续执行
  3. SEND_ERROR = CMake错误,编译继续执行,但是会跳过生成的步骤
  4. FATAL_ERROR = CMake错误,终止所有处理过程

add_subdirectory(src):子目录的CMakeLists.txt就是通过这个命令被包含进来,比如这个命令就是去执行src文件夹下CMakeLists.txt。

CMakeLists.txt(No.1)

CMakeLists.txt(No.1)里的内容相对简单,主要是通过add_subdirectory命令将子目录math和app中的CMakeLists.txt包含进来。

cmake_minimum_required(VERSION 2.8)

# include header file
include_directories(.)

# add subdirectory
add_subdirectory(app)
add_subdirectory(math)

include_directories(.):这个命令将指定目录添加到编译器的头文件搜索路径之下,指定的目录被解析成当前源码路径的相对路径。include_directories(.) 就表示包含当前路径的头文件,博主主要是想在main程序里引用box.hpp这个头文件,这样的话就可以在main程序里直接写:

#include "box.hpp"

CMakeLists.txt(No.2)

子目录math下的源文件将被编译成动态库,最后被二进制文件所链接。

cmake_minimum_required(VERSION 2.8)

# include header file
include_directories(.)

# add source file, store in [SRC]
aux_source_directory(. SRC)

# create dynamic library
add_library(box_math SHARED ${SRC})

aux_source_directory(. SRC):这个命令会查找指定路径下的所有源文件名,并将输出结果列表储存在指定的变量中,比较适合有多个源文件存在的情况下使用。比如这里就是查找当前目录下的所有源文件,并且储存到SRC这个变量中。

add_library(box_math SHARED ${SRC}):生成名为box_math的动态库,源文件来自SRC这个变量。SHARED为库的类型,类型为STATIC(静态库)/SHARED(动态库)/MODULE(模块库)之一。

CMakeLists.txt(No.3)

最后是app目录下的CMakeLists.txt,将会生成可执行文件,并且进行链接。

cmake_minimum_required(VERSION 2.8)

# add binary file
add_executable(box_test main.cpp) 

# add link library path
link_directories(${LIBRARY_OUTPUT_PATH})

# link library
target_link_libraries(box_test box_math boost_program_options)

add_executable(box_test main.cpp):这个命令用于使用指定的源文件生成可执行文件,比如这里可执行文件的名称是box_test,源文件是main.cpp。

link_directories(${LIBRARY_OUTPUT_PATH}):这个命令用于向链接器添加库文件的搜索路径,以便在链接时找到需要的库文件。因为需要链接本地生成的box_math的动态库,所以路径是LIBRARY_OUTPUT_PATH变量的值。

target_link_libraries(box_test box_math boost_program_options):这个命令用于向给定目标链接依赖库,比如我们向box_test链接box_math动态库,另外代码里用到了boost_program_options这个外部库,也需要进行链接。

总结

这篇博客介绍了一个简单CMake工程样例,给大家有个大致的用CMake构建的概念,CMake的命令介绍的不是很详细,博主将在接下来的博客中详细地分类介绍CMake命令。最后附上整个工程样例的下载链接cmake_box_training

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

李71~李先森

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值