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)