一 基本配置
1.1 单文件项目
首先使用CMake创建一个最简单的项目
项目结构如下:
├── build
├── CMakeLists.txt
└── main.cpp
其中main.cpp为源文件,build是用来构建项目的文件夹
cmake_minimum_required(VERSION 3.0) #设置cmake的最低版本
project(CMAKEEXAMPLE) #设置项目名称
add_executable(app main.cpp) #生成一个可执行程序 指定需要用到的源文件
项目的编译可以使用如下命令:
cd build #进入到build文件夹,目的是在该文件夹中执行后续操作,后续操作产生的文件就会放在该文件夹下,项目结构更加清晰
cmake .. #因为此时我们位于build/文件夹下,所是cmake参数使用.. 表示使用上一层的CMakeists.txt文件执行cmake命令
make #cmake命令执行完毕后会生成makefile等文件,我们还需要再执行以下make命令才能完成项目的编译
1.2 多文件项目
通常情况下一个稍微完善点的项目会使用到许多头文件和源文件,项目结构如下:
├── build
├── CMakeLists.txt
├── include
│ ├── util1.h
│ └── util2.h
├── main.cpp
└── src
├── util1.cpp
└── util2.cpp
include和src文件夹中分别存放头文件和源文件,假设这些文件在main.cpp中被使用。
在g++中我们需要使用 -I 指定头文件搜索路径,使用CMake构建项目也需要类似的操作:
include_directories(./include) #指定头文件搜索路径
由于使用了新增的源文件,所需还需要进行如下的修改,文件路径可以使用绝对路径也可以使用相对路径。
add_executable(app main.cpp ./src/util1.cpp /src/util2.cpp)
项目的编译命令和1.1是一样的。
1.3 生成静态/动态库
有时候需要将项目编译为库,而不是可执行文件,假设有如下项目
├── build
├── CMakeLists.txt
├── include
│ └── util1.h
└── src
└── util1.cpp
将其编译为静态库需要进行如下设置:
include_directories(./include) #同样需要指定头文件
add_library(util1 ./src/util1.cpp) #指定生成的库名以及需要用到的源文件
add_library
的语法为:
add_library(libname [SHARED|STATIC|MODULE] [EXCLUDE_FROM_ALL] source1 source2...)
如果不指定参数,默认会使用 STATIC 也就是打包为静态库。
进入build文件夹执行编译命令后可以在该文件夹下找到一个名为 libutil1.a 的文件即为生成的静态库文件,文件名是按照“lib+你指定的文件名+.a”格式自动生成的。
1.4 使用静态/动态库
使用方式很简单,只需要指定好库文件路径以及对应头文件路径即可。如果想使用 1.3 中生成的静态库,我们需要通过如下方式指定 util1.h 和 libutil1.a 的路径(为了方便,我们可以将这两个文件放在一个特定的路径下,这里我把头文件放在了了/usr/include/中,库文件放在了/usr/lib/中)
项目结构如下:
├── build
├── CMakeLists.txt
└── main.cpp
其中main.cpp中使用到了libutil1.a库,在我们的项目的 CMakeLists.txt 中添加如下设置:
include_directories(/usr/include)
link_directories(/usr/lib) #指定库文件搜索路径
add_executable(app main.cpp) #生成可执行文件
target_link_libraries(app util1) #由于可执行文件中使用了库,需要将库链接到可执行文件
1.5 多目录管理
在编写大型项目时可能需要将整个项目划分为多个子项目,方便管理与组织,下面个出一个样例结构:
├── build
├── CMakeLists.txt
├── Alg
│ ├── CMakeLists.txt
│ ├── include
│ │ └── mymath.h
│ └── src
│ └── mymath.cpp
├── View
│ ├── CMakeLists.txt
| ├── include
| │ └── myprint.h
| └── src
| └── myprint.cpp
└── App
├── CMakeLists.txt
└── main.cpp
Alg为计算模块、View为可视化模块、App为主模块
App中调用Alg完成计算,然后调用View进行可视化
/Alg/CMakeLists.txt内容如下:
cmake_minimum_required(VERSION 3.0)
project(ALG)
include_directories(./include)
add_library(alg ./src/mymath.cpp)
/View/CMakeLists.txt内容如下:
cmake_minimum_required(VERSION 3.0)
project(VIEW)
include_directories(./include)
add_library(view ./src/myprint.cpp)
/App/CMakeLists.txt内容如下:
cmake_minimum_required(VERSION 3.0)
project(APP)
include_directories(../Alg/include ../View/include)
link_directories(../build)
add_executable(APP main.cpp)
target_link_libraries(APP alg view)
最后在根目录下的CMakeLists.txt下添加如下内容:
cmake_minimum_required(VERSION 3.0)
project(MUL_PROJECT)
#添加子目录,会自动执行子目录下CMakeLists.txt中的内容
add_subdirectory(Alg)
add_subdirectory(View)
add_subdirectory(App)
二 更多配置
2.1 自定义编译选项
有时我们需要根据配置项设置软件版本或者控制代码的运行(根据编译选项改变程序流程),camke提供了configure_file命令可以实现这种功能。
configure_file 可以根据配置选项替换模板文件中的内容从而生成一个头文件,在源码中使用该头文件完成控制。语法为:
configure_file(<input> <output>
[COPYONLY] [ESCAPE_QUOTES] [@ONLY]
[NEWLINE_STYLE] [UNIX|DOS|WIN32|LF|CRLF])
本例项目结构如下:
├── build
├── CMakeLists.txt
├── main.cpp
└── myconfig.h.in
我们创建了一个模板文件myconfig.h.in ,内容如下:
#cmakedefine APP_VERSION_MAJOR @APP_VERSION_MAJOR@
#cmakedefine APP_VERSION_MINOR @APP_VERSION_MINOR@
#cmakedefine APP_AUTHOR "@APP_AUTHOR@"
这三句替换后就是三个宏定义。
其中 #cmakedefine 将会被替换为 #define,当然直接在这里写 #define 也是可以的。
而具体的值需要使用@@获取。
下面我们需要在CMakeLists.txt中配置对应的值,configure_file才知道要替换成什么,在CMakeLists.txt中添加如下内容
cmake_minimum_required(VERSION 3.0)
project(APP)
set(APP_VERSION_MAJOR 1) #设置值
set(APP_VERSION_MINOR 1) #设置值
set(APP_AUTHOR 诗如沿海) #设置值
configure_file(
"myconfig.h.in"
"${PROJECT_BINARY_DIR}/myconfig.h"
)
include_directories(${PROJECT_BINARY_DIR})
add_executable(main main.cpp)
在main中按照如下方式使用即可:
#include <iostream>
#include "myconfig.h"
using namespace std;
int main()
{
cout << "VERSION_MAJOR: " << APP_VERSION_MAJOR << endl;
cout << "VERSION_MINOR: " << APP_VERSION_MINOR << endl;
cout << "APP_AUTHOR: " << APP_AUTHOR << endl;
return 0;
}
在CMakeLists.txt中除了使用set设置值,还可以使用option,他更适合用来控制编译流程。
其语法为:
option(<variable> "<help_text>" [value])
value定义选项的默认状态,一般是OFF或ON。
三 常用变量
CMake提供了一些常用的变量,我们可以对其进行设置或者使用他们
-
CMAKE_C_FLAGS: gcc编译选项
-
CMAKE_CXX_FLAGS: g++编译选项
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") #注意需要在g++编译选项后追加参数,不然会覆盖之前的全部内容
-
CMAKE_BUILD_TYPE: 编译类型,可以选择Debug Release
set(CMAKE_BUILD_TYPE Debug)
-
编译发生的目录:
- CMAKE_BINARY_DIR:
- PROJECT_BINARY_DIR:
- _BINARY_DIR:
-
工程顶层目录:
- CMAKE_SOURCE_DIR:
- PROJECT_SOURCE_DIR:
- _SOURCE_DIR: