CMake是一个跨平台的构建工具,可以用简单的语句来描述所有平台的安装(编译过程)。本文将从基础到进阶,详细介绍CMake的使用方法。
一、基础编译
1. 项目与文件结构
项目结构
1 2 3 4 5 6
| . ├── build ├── CMakeLists.txt ├── main.c ├── testFunc.c └── testFunc.h
|
main.c
1 2 3 4 5 6 7 8
| #include <stdio.h> #include "testFunc.h"
int main(void) { func(100); return 0; }
|
testFunc.c
1 2 3 4 5 6 7
| #include <stdio.h> #include "testFunc.h"
void func(int data) { printf("data is %d\n", data); }
|
testFunc.h
1 2 3 4 5 6
| #ifndef _TEST_FUNC_H_ #define _TEST_FUNC_H_
void func(int data);
#endif
|
CMakeLists.txt
1 2 3 4 5 6
| cmake_minimum_required (VERSION 2.8)
project (demo)
add_executable(main main.c testFunc.c)
|
2. 编译与运行
1 2 3
| cd build && cmake ../ make -j8 ./main
|
3. 思考与改进
源文件管理问题:如果在同一目录下有多个源文件,需要在add_executable
里把所有源文件都添加进去,这不够优雅。
- 解决方案:使用
aux_source_directory(dir var)
把指定目录下所有的源文件存储在一个变量中
- 参数说明:第一个参数
dir
是指定目录,第二个参数var
是用于存放源文件列表的变量名
头文件路径问题:当程序文件较多时,我们会进行分类管理,把代码根据功能放在不同的目录下。这时头文件定位需要使用完整路径,如:
1 2
| #include "xxxxxx/testFunc.h" #include "xxxxxx/testFunc1.h"
|
- 解决方案:使用
include_directories
方法添加多个指定头文件的搜索路径,路径之间用空格分隔
源代码组织问题:源代码随意放置不够优雅
- 解决方案:统一放入src文件夹后使用
add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
设置源代码目录
- 参数说明:
source_dir
:指定包含CMakeLists.txt和代码文件所在的目录
binary_dir
:二进制代码目录,执行后的输出文件将会存放在此处
EXCLUDE_FROM_ALL
:可选标记,表示新增加的子目录将会排除在ALL目录之外
二、进阶编译
1. 项目与文件结构
项目结构
1 2 3 4 5 6 7 8 9 10 11 12
| . ├── bin ├── build ├── CMakeLists.txt ├── include1 | └── testFunc1.h ├── include2 | └── testFunc2.h └── src ├── main.c ├── testFunc1.c └── testFunc2.c
|
main.c
1 2 3 4 5 6 7 8
| #include <stdio.h> #include "testFunc.h"
int main(void) { func(100); return 0; }
|
testFunc.c
1 2 3 4 5 6 7
| #include <stdio.h> #include "testFunc.h"
void func(int data) { printf("data is %d\n", data); }
|
testFunc.h
1 2 3 4 5 6
| #ifndef _TEST_FUNC_H_ #define _TEST_FUNC_H_
void func(int data);
#endif
|
CMakeLists.txt
1 2 3 4
| cmake_minimum_required (VERSION 2.8) project (demo)
add_subdirectory (src)
|
这里指定src目录下存放了源文件,当执行cmake时,就会进入src目录下去找src目录下的CMakeLists.txt,所以在src目录下也建立一个CMakeLists.txt,内容如下:
src/CMakeLists.txt
1 2 3 4 5 6 7 8
| aux_source_directory (. SRC_LIST)
include_directories (../include1 ../include2)
add_executable (main ${SRC_LIST})
set (EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
|
2. 编译与运行
1 2 3 4 5
| cd build cmake .. make -j8 cd ../bin ./main
|
这样Makefile会在build目录下生成,二进制程序会在bin目录下生成。
3. 思考与改进
可以只使用一个CMakeLists.txt,把最外层的CMakeLists.txt内容改成如下:
1 2 3 4 5 6
| cmake_minimum_required (VERSION 2.8) project (demo) set (EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin) aux_source_directory (src SRC_LIST) include_directories (include1 include2) add_executable (main ${SRC_LIST})
|
此时还需要把src目录下的CMakeLists.txt删除即可。
三、生成库文件
1. 项目结构
1 2 3 4 5 6 7 8 9 10 11 12 13
| . ├── bin ├── build ├── CMakeLists.txt ├── include1 | └── testFunc1.h ├── include2 | └── testFunc2.h ├── lib └── src ├── main.c ├── testFunc1.c └── testFunc2.c
|
CMakeLists.txt
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| cmake_minimum_required (VERSION 3.5) project (demo) set (SRC_LIST1 ${PROJECT_SOURCE_DIR}/src/testFunc1.c) set (SRC_LIST2 ${PROJECT_SOURCE_DIR}/src/testFunc2.c) include_directories (../include1 ../include2)
add_library (testFunc_shared1 SHARED ${SRC_LIST1}) add_library (testFunc_shared2 SHARED ${SRC_LIST2}) add_library (testFunc_static1 STATIC ${SRC_LIST1}) add_library (testFunc_static2 STATIC ${SRC_LIST2})
set_target_properties (testFunc_shared1 PROPERTIES OUTPUT_NAME "testFunc1") set_target_properties (testFunc_shared2 PROPERTIES OUTPUT_NAME "testFunc2") set_target_properties (testFunc_static1 PROPERTIES OUTPUT_NAME "testFunc1") set_target_properties (testFunc_static2 PROPERTIES OUTPUT_NAME "testFunc2")
set (LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
|
2. 运行查看编译好的库文件
1 2 3 4 5
| cd build/ cmake .. make cd ../lib/ ls
|
最后将会在lib文件夹下生成libtestFunc1.a
、libtestFunc1.so
、libtestFunc2.a
和libtestFunc2.so
。
四、链接库文件
1. 项目结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| . ├── bin ├── build ├── CMakeLists.txt ├── include1 | └── testFunc1.h ├── include2 | └── testFunc2.h ├── lib | ├── libtestFunc1.a | ├── libtestFunc1.so | ├── libtestFunc2.a | └── libtestFunc2.so └── src ├── main.c ├── testFunc1.c └── testFunc2.c
|
main.c
1 2 3 4 5 6 7
| #include <stdio.h> #include "testFunc.h" int main(void) { func(100); return 0; }
|
CMakeLists.txt
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| cmake_minimum_required (VERSION 3.5) project (demo) set (EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin) set (SRC_LIST ${PROJECT_SOURCE_DIR}/src/main.c) include_directories (${PROJECT_SOURCE_DIR}/testFunc/inc)
find_library(TESTFUNC_LIB testFunc HINTS ${PROJECT_SOURCE_DIR}/testFunc/lib) add_executable (main ${SRC_LIST})
target_link_libraries (main ${TESTFUNC_LIB})
|
2. 编译与运行
1 2 3 4 5
| cd build/ cmake .. make cd ../bin/ ./main
|
总结
CMake是一个强大的跨平台构建工具,通过本文的学习,我们掌握了:
- 基础的CMake项目构建方法
- 如何组织和管理多文件项目
- 如何生成和使用静态/动态库
- 如何链接和使用外部库
通过合理使用CMake,我们可以大大简化C/C++项目的构建过程,提高开发效率。