CMake

摘要: 在Linux系统中, 没有VS这样强力的IDE帮助构建项目, 而自己写makefile文件又是一件繁琐烦心之事, 所以往往需要借助一些工具帮忙构建项目, 在早期使用automake系列工具, 生成makefile, 运行./comnfigure && make && make install来安装程序也几近成为标准, 但其使用过程较为复杂, 不如cmake来得方便, 所以在没必要的情况下, 使用cmake来构建项目当然是首选, 所以这篇文章主要讲述了cmake的使用方法, 以及CMakeLists.txt文件的编写语法.

安装CMake

1
2
3
4
5
6
7
wget https://cmake.org/files/v3.3/cmake-3.3.2.tar.gz # 下载cmake
tar xzvf cmake-3.3.2.tar.gz # 解压
./bootstrap # 运行引导程序
make && make install # 编译安装

cmake --version # 查看安装版本
> cmake version 3.10.3

CMake使用

CMake的使用非常简单, 只有两步操作:

1
2
3
# 一般来说为项目新建一个build目录为构建目录, 所以进入该目录后执行以下两句命令
cmake .. # ..告知cmake作用于上层目录, CMakeLists.txt文件存在其中
make # 编译

关键: CMakeLists.txt

CMake的安装的使用都非常简单, 所以关键之处落在了编写CMakeLists.txt头上, 对于一个项目的构建, 编写好CMakeLists.txt文件成了重中之重. 下面结合例子, 编写CMakeLists.txt, 由简入繁, 由浅至深.

首先约定好整个项目的结构:

build build.sh CMakeLists.txt include src log

– build用于存放CMake构建项目时产出的信息
– build.sh是引导脚本
– CMakeLists.txt是顶层CMakeLists.txt
– include为.h文件目录
– src为.cpp文件目录
– log目录用于存放日志信息

Hello CMake, 通过CMakeLists.txt构建简单程序

1
2
3
4
5
6
7
8
9
# 在cmake中, 一条语句由命令+变量组成, 形如command(${VAR})
# 其中命令由command()组成, command大小写不敏感, 变量由${}包含VAR组成, VAR大小写敏感

--------CMakeLists.txt--------
cmake_minimum_required(VERSION 3.10) # 必要信息, 要求的cmake最小版本

project(HelloCMake) # 必要信息, 项目名

add_subdirectory(src) # 包含子目录, 要求该目录下有子CMakeLists.txt
1
2
3
4
5
6
7
--------src/Main.cpp--------
#include <iostream>

int main(){
std::cout<< "Hello CMake!"<< std::endl;
return 0;
}
1
2
3
4
5
6
# set()命令用来定义一个变量, 其参数第一个为变量名, 后面的全是变量的值

--------src/CMakeLists.txt--------
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}) # EXECUTABLE_OUTPUT_PATH和PROJECT_SOURCE_DIR都是预定义变量, 此处修改可执行文件的输出目录为PROJECT_SOURCE_DIR, 即为根目录

add_executable(HelloCMake Main.cpp) # 生成可执行文件HelloCMake, 所需要包含的源码为main.cpp
1
2
3
4
5
6
--------build.sh--------
#!/bin/bash

cd ./build
cmake ..
make

以上文件准备就绪后, 工作完成, 只需要简单的运行build.sh脚本, 就能够得到一个可执行文件啦!

1
2
3
4
5
6
chmod a+x build.sh # 给脚本可执行权限
./build.sh # 运行脚本

# 在根目录下得到HelloCMake
./HelloCMake
> Hello CMake! # bingo!

管理更多.h, .cpp文件

例如, 我们需要创建Common.h, Common.cpp作为预编译头(暂定), 用来include一些标准库及其他三方库.

1
2
3
4
5
6
7
8
9
--------include/Common.h--------
#pragma once

// cpp
#include <iostream>


--------src/Common.cpp--------
#include "Common.h"

此时需要对CMakeLists.txt的改动如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
--------src/CMakeLists.txt--------
# 之前文件内容不再显示, 再合适的地方添加一下语句, 按理说在任何地方添加都一样
...
include_directories(${PROJECT_SOURCE_DIR}/include) # include_directories添加所需要的include目录

# 下面我们为.cpp文件专门设置一个SOURCES变量, 用来保存所用到的所有.cpp文件
set(SOURCES
Common.cpp
Main.cpp
)

add_executable(HelloCMake ${SOURCES}) # 生成可执行文件只需要简单的使用上述变量即可
...

使用静态库

如何使用静态库, 这里以glog日志库为例, 其头文件存在于/usr/local/include, 库文件存在于/usr/local/lib.(关于添加glog库详情百度)

1
2
3
4
5
6
7
8
9
10
11
12
# 在顶层CMakeLists.txt添加库信息

--------CMakeLists.txt--------
...
include_directories(/usr/local/include) # 添加glog库头文件所在目录
link_directories(/usr/local/lib) # 添加glog库所在目录
...

--------src/CMakeLists.txt--------
...
target_link_libraries(HelloCMake libglog.so) # 链接静态库 libglog.so 到HelloCMake
...
1
2
3
4
5
6
7
8
9
10
11
12
13
--------include/Common.h--------
// 添加使用库对应的头文件glog
#include <glog/logging.h>

--------src/Main.cpp--------
#include "Common.h"

int main(int argc, char** argv){
google::InitGoogleLogging(argv[0]);
google::SetLogDestination(google::GLOG_INFO, "./log/test.log");
LOG(INFO) << "Hello CMake!";
return 0;
}

库添加完成了, 程序也写好了, 运行build.sh脚本, 测试程序

1
2
./build.sh # 运行脚本
./HelloCMake # 在log目录中将看到日志信息, bingo