CMake最大优势是跨平台,可以根据不同的平台生成Makefile文件,看下CMake的基础使用。
cmake(单目录单文件)
第一个最简单的cmake构建。
demo1.cpp代码:
#include <iostream>
using namespace std;
int main(int argc, char *argv[]) {
cout << "hello, cmake" << endl;
return 0;
}
编写 CMakeLists.txt 文件
CMAKE_MINIMUM_REQUIRED(VERSION 3.10)
PROJECT(demo1)
# 生成可执行程序
ADD_EXECUTABLE(demo1 demo1.cpp)
在当前目录下,执行cmake:
lorien@ubuntu20:/work2/cmake/demo1$ cmake ./
会生成一些中间文件和Makefile文件。
执行make
lorien@ubuntu20:/work2/cmake/demo1$ make
第一个cmake构建就完成了。
cmake(单目录多文件)
目录结构:
main.cpp:
#include "hello_cmake.h"
int main(int argc, char *argv[]) {
print_hello();
return 0;
}
hello_cmake.cpp:
#include <iostream>
using namespace std;
int print_hello() {
cout << "hello cmake" << endl;
return 0;
}
hello_cmake.h:
int print_hello();
看下CMakeLists.txt的写法
CMAKE_MINIMUM_REQUIRED(VERSION 3.10)
PROJECT(demo2)
# 生成可执行程序
ADD_EXECUTABLE(demo2 main.cpp hello_cmake.cpp)
通过在ADD_EXECUTABLE后面追加源文件就可以实现但目录多文件cmake构建。
如果源文件太多,在后面一直追加很是麻烦,AUX_SOURCE_DIRECTORY可以解决这个问题。
AUX_SOURCE_DIRECTORY:发现一个目录下所有源代码文件并将列表存储在一个变量中
CMAKE_MINIMUM_REQUIRED(VERSION 3.10)
PROJECT(demo2)
AUX_SOURCE_DIRECTORY(./ DIR_HELLO_SRCS)
ADD_EXECUTABLE(demo2 ${DIR_HELLO_SRCS})
cmake(多目录多文件)
文件结构如下:
源文件和上面一样,不再贴了。看一下子目录和根目录下的CMakeLists.txt
mylib目录下的CMakeLists.txt
AUX_SOURCE_DIRECTORY(./ DIR_LIB_SRCS)
# 生成库文件
ADD_LIBRARY(mylib STATIC ${DIR_LIB_SRCS})
ADD_LIBRARY:表示生成库文件。STATIC表示静态库。这样mylib下的所有源代码会被编译成一个.a文件
根目录下的CMakeLists.txt
CMAKE_MINIMUM_REQUIRED(VERSION 3.10)
PROJECT(demo3)
# 向当前工程添加存放源文件的子目录
ADD_SUBDIRECTORY(./mylib)
AUX_SOURCE_DIRECTORY(./ DIR_HELLO_SRCS)
ADD_EXECUTABLE(demo3 ${DIR_HELLO_SRCS})
# 为target链接共享库
TARGET_LINK_LIBRARIES(demo3 mylib)
解释下两个命令:
- ADD_SUBDIRECTORY:向当前工程添加存放源文件的子目录
- TARGET_LINK_LIBRARIES: 为target链接共享库,target是demo3。库是mylib,这个库由上面mylib目录下的源代码编译生成。
在根目录下执行:cmake ./
我们看到在mylib目录下生成了库文件libmylib.a,在根目录下生成了可执行文件:demo3
cmake工程标准结构
上面的例子,源文件和cmake构建的中间产物混在一起,导致源文件被污染。合理的目录结构如下:
- mylib/:存放三方库文件
- src/:项目源文件
- build/:cmake构建中间产物
mylib/CMakeLists.txt
AUX_SOURCE_DIRECTORY(./ DIR_LIB_SRCS)
# 指定库文件存储路径
SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
# 生成库文件
ADD_LIBRARY(mylib STATIC ${DIR_LIB_SRCS})
LIBRARY_OUTPUT_PATH 用于指定库文件的生成路径。
PROJECT_BINARY_DIR表示工程二进制根目录,这个目录是执行cmake命令时所处的目录,比如我们会在build下面执行cmake目录,PROJECT_BINARY_DIR就是build/
src/CMakeLists.txt
# 包含头文件搜索目录
INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/mylib)
# 指定可执行程序存储路径
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
AUX_SOURCE_DIRECTORY(./ DIR_SRCS)
# 生成可执行文件
ADD_EXECUTABLE(demo4 ${DIR_SRCS})
TARGET_LINK_LIBRARIES(demo4 mylib)
INCLUDE_DIRECTORIES:向工程添加头文件搜索路径。添加这个命令后,我们在源文件中include头文件就不用再添加目录下。src/main.cpp
// 通过INCLUDE_DIRECTORIES添加了头文件搜索路径,可以直接include文件
#include "hello_cmake.h"
int main(int argc, char *argv[]) {
print_hello();
return 0;
}
顶层目录下的CMakeLists.txt
CMAKE_MINIMUM_REQUIRED(VERSION 3.10)
PROJECT(demo4)
# 向当前工程添加存放源文件的子目录
ADD_SUBDIRECTORY(./mylib)
# 向当前工程添加存放源文件的子目录
ADD_SUBDIRECTORY(./src)
我们cd进入build目录,执行命令
cmake ..
看下文件结构:
可以看到中间文件都在build目录下面,同时生成了bin/ 和 lib/目录。避免了我们源文件被污染。接下来在build目录执行make
最终,可执行文件demo4在bin目录下,库文件libmylib.a在lib目录下。
cmake自定义编译选项
cmake实现自定义编译选项用来控制程序逻辑。先看下demo目录结构
顶层目录的CMakeLists.txt
CMAKE_MINIMUM_REQUIRED(VERSION 3.10)
PROJECT(demo5)
# 向当前工程添加存放源文件的子目录
ADD_SUBDIRECTORY(./src)
src/CMakeLists.txt
configure_file(
"${PROJECT_SOURCE_DIR}/config/config.h.in"
"${PROJECT_SOURCE_DIR}/config/config.h"
)
option(MY_LOG
"use MY_LOG"
ON)
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
AUX_SOURCE_DIRECTORY(./ DIR_SRCS)
ADD_EXECUTABLE(demo5 ${DIR_SRCS})
重点是configure_file和option
- configure_file:生成配置头文件config.h,这个头文件通过config.h.in生成。
- option:配置宏打开或者关闭
config/config.h.in
#cmakedefine MY_LOG
src/main.cpp
#include <iostream>
#include "../config/config.h"
using namespace std;
int main(int argc, char *argv[]) {
#ifdef MY_LOG
cout << "define MY_LOG" << endl;
#else
cout << "not define MY_LOG" << endl;
#endif
return 0;
}
接着我们进入build目录,执行cmake
cmake ..
一切正常的话,我们会在config目录下,发现cmake自动生成了头文件config.h
#define MY_LOG
如果要关掉宏定义,在src/CMakeLists.txt里面设置option为OFF
option(MY_LOG
"use MY_LOG"
OFF)