CMake极速入门

引言

还在手写晦涩难懂的Makefile文件吗?现如今,主流的c++项目都采取CMake作为项目构建工具,CMake可以跨平台运行,而且语法相对Makefile而言直观很多,是时候将Makefile扫进垃圾堆了。

Hello, World!

首先先以单个源文件项目为讲解,新建一个main.cpp文件:

#include<iostream>

using namespace std;

int main(void){
    cout<<"Hello, World!"<<endl;
}

那么如何使用CMake编译这个文件呢?CMake所有的语句都写在一个名为CMakeLists.txt的文件下,新建这个文件:

#设置cmake运行时最低版本
cmake_minimum_required(VERSION 3.0.0)

#设置项目名
project(hello)

#添加可执行文件
add_executable(${PROJECT_NAME} main.cpp)

cmake_minimum_required函数的作用是限定运行的cmake版本,因为cmake脚本语言的sdk可能会有变化,老版本运行不起来。project函数就不解释了,见闻知意。add_executable函数是起到编译作用的,第一个参数是编译后的可执行程序名称(这里PROJECT_NAME是一个变量,当我们用project函数设定项目名时,会自动赋值给它,而${}是CMake中调用变量的语法),之后的参数是编译可执行文件需要的源文件依赖。
终端输入cmake .


可以看到,cmake正在检查系统环境,检查c,c++编译器路径,命令执行完毕后,你会发现当前目录下多了很多东西:

其中最关键的是Makefile文件,也就是说CMake自动将CMakeLists.txt中的语句转化为Makefile文件了,执行make命令:

可以看到,成功运行了,相比于手写Makefile文件,优雅许多。

目录简洁化

输入ls,查看项目当前文件:


可以看到,cmake生成了很多中间产物,这就把目录搞脏了,有一个约定俗成的处理方法是,新建一个build目录:

在build目录下执行cmake ..make

可以看到,这样项目目录就干净多了,编译文件和项目文件互不干涉。但是编译后的可执行文件也混在build目录中了,如果我想把可执行文件放在一个单独的目录里,该怎么做呢?

设置可执行文件输出目录

我通常喜欢把可执行文件放到项目目录下的bin文件夹,新建一个bin目录,并在CMakeLists.txt文件中添加:

set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)

通过设置EXECUTABLE_OUTPUT_PATH变量即可实现。

引入库文件,头文件

实际项目中不可能就一个源文件,我们还会将代码分类,组织成不同的头文件和库文件,那么CMake怎么引入呢?
假设我们现在想给Hello World程序加一个加法功能。在项目目录下,新建一个src文件夹,用于存放库文件;新建一个inc文件夹,用于存放头文件。
编写inc/add.h:

#pragma once

int add(int a,int b);

编写src/add.cpp:

#include"add.h"

int add(int a,int b){
    return a+b;
}

当前项目结构变为:


再修改CMakeLists.txt文件:

#设置cmake运行时最低版本
cmake_minimum_required(VERSION 3.0.0)

#设置项目名
project(hello)

#添加头文件目录
include_directories(${PROJECT_SOURCE_DIR}/inc)

#查找所有库文件
file(GLOB SRC_FILES ${PROJECT_SOURCE_DIR}/src/*.cpp)

#添加可执行文件
add_executable(${PROJECT_NAME} main.cpp ${SRC_FILES})

#设置可执行文件生成目录
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)

可以看到,我们添加了include_directories函数和file函数,其中include_directories函数顾名思义,就是用来指定头文件目录的。参数PROJECT_SOURCE_DIR则表示项目根目录。而file函数是用来读取文件系统的,GLOB参数代表匹配指定模式的文件(即*.cpp文件)。
然后我们再修改一下main.cpp文件,调用一下add函数:

#include<iostream>
#include"add.h"

using namespace std;

int main(void){
    cout<<"Hello, World!"<<endl;
    cout<<"5+10="<<add(5,10)<<endl;
}

编译,运行,成功!

将代码封装成库

实际开发中,为了缩短编译时间,便于代码移植等原因,通常会将代码封装成静/动态库。

静态库

静态库的封装

在CMakeLists.txt文件中加入:

add_library(add STATIC ${SRC_FILES})

其中add_library函数第一个参数是库的名字(注意不能和可执行文件名重复了)
编译:

设置静态库生成目录

可以从上图看到,libadd.a直接编译到build目录下了,不整洁。项目根目录下新建lib文件夹,修改CMakeLists.txt文件,加入:

set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)

重新编译,可以看到:

链接静态库

CMakeLIsts.txt文件中,加入:

#包含静态库路径
link_directories(${PROJECT_SOURCE_DIR}/lib)
#链接静态库
link_libraries(add)

编译,运行,可以看到成功链接了libadd.a:

动态库

将上文静态库的CMakeLists.txt文件中的add_library(add STATIC ${SRC_FILES})改为add_library(add SHARED ${SRC_FILES})即可。
运行效果:

一些有用的设置

  • 设置C++标准(假设设为C++11):set(CMAKE_CXX_STANDARD 11)
  • 设置编译模式:SET(CMAKE_BUILD_TYPE "Debug")SET(CMAKE_BUILD_TYPE "Release")
  • 添加编译参数,比如我们想添加更多警告: add_compile_options(-Wall -Wextra -Wpedantic)

模板

直接编译二进制文件

#设置cmake运行时最低版本
cmake_minimum_required(VERSION 3.0.0)

#设置项目名
project(hello)

#设置c++标准
set(CMAKE_CXX_STANDARD 11)

SET(CMAKE_BUILD_TYPE "Release")
add_compile_options(-Wall -Wextra -Wpedantic)

#添加头文件目录
include_directories(${PROJECT_SOURCE_DIR}/inc)

#查找所有库文件
file(GLOB SRC_FILES ${PROJECT_SOURCE_DIR}/src/*.cpp)

#添加可执行文件
add_executable(${PROJECT_NAME} main.cpp ${SRC_FILES})

#设置可执行文件生成目录
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)

静态库

#设置cmake运行时最低版本
cmake_minimum_required(VERSION 3.0.0)

#设置项目名
project(hello)

#设置c++标准
set(CMAKE_CXX_STANDARD 11)

SET(CMAKE_BUILD_TYPE "Release")
add_compile_options(-Wall -Wextra -Wpedantic)

#添加头文件目录
include_directories(${PROJECT_SOURCE_DIR}/inc)

#查找所有库文件
file(GLOB SRC_FILES ${PROJECT_SOURCE_DIR}/src/*.cpp)

#添加可执行文件
add_executable(${PROJECT_NAME} main.cpp ${SRC_FILES})

#设置可执行文件生成目录
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)

#设置静态库
add_library(add STATIC ${SRC_FILES})

#设置库的生成目录
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)

#包含静态库路径
link_directories(${PROJECT_SOURCE_DIR}/lib)
#链接库
link_libraries(add)

动态库

#设置cmake运行时最低版本
cmake_minimum_required(VERSION 3.0.0)

#设置项目名
project(hello)

#设置c++标准
set(CMAKE_CXX_STANDARD 11)

SET(CMAKE_BUILD_TYPE "Release")
add_compile_options(-Wall -Wextra -Wpedantic)

#添加头文件目录
include_directories(${PROJECT_SOURCE_DIR}/inc)

#查找所有库文件
file(GLOB SRC_FILES ${PROJECT_SOURCE_DIR}/src/*.cpp)

#添加可执行文件
add_executable(${PROJECT_NAME} main.cpp ${SRC_FILES})

#设置可执行文件生成目录
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)


#设置动态库
add_library(add SHARED ${SRC_FILES})

#设置库的生成目录
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)

#包含库路径
link_directories(${PROJECT_SOURCE_DIR}/lib)
#链接库
link_libraries(add)

热门相关:异界之极品奶爸   永恒王权   超级英雄   万妖帝主   驭房我不止有问心术