CMake 基础学习-认识 CMake
什么是 CMake
CMake 是一个跨平台、可扩展的开源脚本系统,它以独立于编译器的方式在操作系统中管理编译、工程文件的生成过程。
在 Linux 中 CMake 生成 Makefile(若是其他平台,则生成对应的编译管理文件) 文件,然后由 Makefile 文件管理 C、C++ 文件的编译组织。
与普通编程语言一样,CMake 也有承载用户定义的文件裁体:CmakeList.txt 文件。通常每个目录需要一个 CMakeLists.txt。
如何安装
# For Ubuntu$ sudo apt-get install cmake# For Redhat$ yum install cmake# For Mac OS X with Macports$ sudo port install cmake
基础语法常识
指令(类似其他编程语言的函数)
CMake 命令类似于 C++/Java 方法或函数,它们将参数作为列表并相应地执行某些任务。
CMake 命令不区分大小写。有内置命令,可以从 cmake 文档中找到:https://cmake.org/cmake/help/latest/manual/cmake-commands.7.html
一些常用的命令
message
:打印给定的消息(类似print\printf)cmake_minimum_required
:设置要使用的最低 CMAKE 版本add_executable
:添加具有给定名称的可执行目标add_library
:添加要从列出的源文件构建的库目标add_subdirectory
:添加要参与构建的子目录
还有一些命令使开发人员能够写出条件语句、循环、迭代列表、赋值:
if, endif
elif, endif
while, endwhile
foreach, endforeach
list
return
set_property
CMake 中缩进不是强制性的,但在编写 CMake 脚本时建议使用。不同于 C/C++ 语言,CMake 不使用分号 ;
来标识语句的结尾。
所有条件语句都应以其相应的结束命令(endif
,endwhile
,endforeach
*等)结尾。
一些开源软件框架有自己的 CMake 风格,如 KDE:
- https://community.kde.org/Policies/CMake_Coding_Style
CMake 参数与变量
参数和变量是区分大小写的。
如:
set(files a.txt b.txt c.txt)
set 是赋值指令、files 是变量、a.txt 是参数。上面的语句类似 C 语句中的:
files = a.txt, b.txt, c.txt;
一些特别长的参数,如特别长的文件名,以双引号将处理不方便的文件名包含起来,如:
set(files “a1111.txt")
在变量的定义中,您只能使用字母、数字、字符、下划线、破折号。
可以在以下 URL 中找到有关 CMake 变量的更多详细信息
- https://cmake.org/cmake/help/v3.0/manual/cmake-language.7.html#variables
- https://cmake.org/cmake/help/v3.0/manual/cmake 变量.7.html#手册:cmake-variables(7)
一些变量是系统根据根文件目录预定义的:
CMAKE_BINARY_DIR
:构建库和二进制输出文件目录的完整路径,默认情况下定义为构建树的顶层。CMAKE_HOME_DIRECTORY
:源树顶部的路径CMAKE_SOURCE_DIR
:源代码的完整路径。CMAKE_INCLUDE_PATH
:用于查找文件的路径
可以使用 ${<variable_name>}
访问变量值。如下是赋值、打印语句的简单示例:
set(CMAKE_CXX_STANDARD 14)
message("CXX Standard: ${CMAKE_CXX_STANDARD}")
注意:在 if 语句中可以直接使用变量名,不使用 ${<variable_name>}
:
if(files)message("defined files!!!!!!!!!!")
else()message("NOT defined files!!!!!!!!!")
endif()if(open STREQUAL "ON")message("open = ON")
else()message("open = OFF")
endif()
CMake 环境变量
CMake 预定义了一些变量,它们是预定于的字符,有特殊含义。
环境变量用于为常规生成过程配置编译器标志、链接器标志和测试配置。
可以从以下 URL 中看到环境变量的详细列表:
- https://cmake.org/cmake/help/latest/manual/cmake-env-variables.7.html
下面是一个示例,当您想在编译过程中启用所有警告时,可以通过上述 set 指令,对环境变量-Wall命令。
CMake 列表与注释
注释通过 #
符号来标识。
CMake 中的所有值都存储为字符串,但在某些上下文中可以将多个字符串视为列表,每个字符串之间可以通过空格分隔。
表示为字符串的元素列表,最终会通过“;”符合来连接。
# sets files to "a.txt;b.txt;c.txt"
set(files a.txt b.txt c.txt)
使用 list(APPEND…) 命令追加列表:
list(APPEND requires "app_update" "esp_partition")
为了访问列表的值,您可以使用CMake 的 for 命令,如下所示,可以依次取出 files 中的每个元素存入 file:
foreach(file ${files})message("Filename: ${file}")
endforeach()
在 CMake 中使用宏定义
宏可定义可以帮助工程师有条件地构建代码,以根据正在运行的系统配置丢弃或包含某些方法。
您可以使用add_definitions命令在CMake中定义宏,在宏名称前使用-D标志。
让我们定义名为CMAKEMACROSAMPLE
的宏,并将其打印在代码中。
# Darwin is Apple's system name
if(${CMAKE_SYSTEM_NAME} MATCHES Darwin)add_definitions(-DCMAKEMACROSAMPLE="Apple MacOS")
elseif(${CMAKE_SYSTEM_NAME} MATCHES Windows)add_definitions(-DCMAKEMACROSAMPLE="Windows PC")
elseif(${CMAKE_SYSTEM_NAME} MATCHES ubuntu)add_definitions(-DCMAKEMACROSAMPLE="linux ubuntu")
endif()
下面是带有打印宏的.cpp 源文件:
#include <iostream>
#ifndef CMAKEMACROSAMPLE#define CMAKEMACROSAMPLE "NO SYSTEM NAME"
#endif
auto sum(int a, int b){return a + b;
}
int main() {std::cout<<"Hello CMake!"<<std::endl;std::cout<<CMAKEMACROSAMPLE<<std::endl;std::cout<<"Sum of 3 + 4 :"<<sum(3, 4)<<std::endl;return 0;
}
一些学习建议:
1)实践第一。如果工作中不是经常用到 cmake 就没有必要详细的学下去了,遇到问题查询一下就可以了。
2)大型项目下,如果你不是负责系统级别的工作,你可以考虑学习些 cmake 的基本语法,入门即可。小型项目其实不太会用 cmake,直接使用 Makefile 多一些。
3)在C/C++ 领域的大型项目中,如果你负责大型项目的维护工作,可以考虑研究下 cmake 的更多用法。学习 QT 开放的同学你可能向使用 qmake 更多一些。
使用 CMake 进行构建的流行的开源项目:
- OpenCV: https://github.com/opencv/opencv
- Caffe2:: https://github.com/caffe2/caffe2
- Caffe2::https://github.com/mysql/mysql-server
可阅维基百科以获取更多的开源项目 https://en.wikipedia.org/wiki/CMake#Applications_that_use_CMake
示例与解析
获取示例代码:samples1
简单 cmake 示例的解析:
# Specify the minimum version for CMake
cmake_minimum_required(VERSION 2.8)# Project's name
project(HELLO)# Set the output folder where your program will be created
set(CMAKE_BINARY_DIR ${CMAKE_SOURCE_DIR}/bin)
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR})
set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR})# The following folder will be included
include_directories("${PROJECT_SOURCE_DIR}")
其中有一些 CMake 的全局变量
-
CMAKE_BINARY_DIR:如果您在源代码中构建,则与
CMAKE_SOURCE_DIR
相同,否则这是构建树的顶级目录。 -
CMAKE_SOURCE_DIR: 这是启动 cmake 的目录,即顶级源目录。
-
EXECUTABLE_OUTPUT_PATH: 设置此变量以指定 CMake 应放置所有可执行文件的公共位置(而不是
CMAKE_CURRENT_BINARY_DIR
) -
LIBRARY_OUTPUT_PATH: 设置此变量以指定 CMake 应放置所有库的公共位置(而不是
CMAKE_CURRENT_BINARY_DIR
) -
PROJECT_NAME: 命令设置的项目名称
PROJECT()
。更完整的项目名称如下,后面的几个选项都可以缺省:project(myAppVERSION 1.0DESCRIPTION "A brief CMake experiment"LANGUAGES CXX)
-
PROJECT_SOURCE_DIR: 包含项目源目录根目录的完整路径,即 CMakeLists.txt 包含 PROJECT() 命令的最近目录.
现在要编译示例的 test.cpp,你需要在 cmake 中添加:
# hello 是要生成的可执行文件(目标),后面可以跟多个源文件、头文件。
add_executable(hello ${PROJECT_SOURCE_DIR}/test.cpp)
然后在 c1 目录查看并编译源文件:
$ ls
test.cpp CMakeLists.txt
$ cmake -H. -Bbuild // 在 build 目录创建 cmake 构建文件的临时文件,该命令会自动生成一个 bin 目录(H indicates source directory,B indicates build directory
$ ls
bin build CMakeLists.txt test.cpp
$ cmake --build build -- -j3 // 生成可执行文件./bin/hello,编译时的代码优化级别为 j3
$ ./bin/hello // 运行可执行文件./bin/hello
Hello World
$ cd build // 切换到 build 目录
$ make clean // 清理工程
注意:工程名HELLO,和可执行程序的名字 hello,他们是不同的。
注意:本示例演示的是 cmake 的外部构建流程,其实内部构建生成的临时文件很多,导致大部分情况下使用起来有些麻烦,因此这里仅介绍外部构建,内部构建的方式不过多叙述。