- 此文为:轻松入门cmake系列教程
- 前文为:cmake:同一目录下多个源文件
一般来说,当程序文件比较多时,我们会进行分类管理,把代码根据功能放在不同的目录下,这样方便查找。那么这种情况下如何编写CMakeLists.txt呢?
实验
实验一
我们把之前的源文件整理一下(新建2个目录test_func和test_func1),整理好后整体文件结构如下:
修改CMakeLists.txt:
cmake_minimum_required (VERSION 2.8)
project (demo)
# 向工程添加多个指定头文件的搜索路径,路径之间用空格分隔
include_directories (test_func test_func1)
#使用aux_source_directory把当前目录下的源文件存列表存放到变量SRC_LIST里
aux_source_directory (test_func SRC_LIST)
aux_source_directory (test_func1 SRC_LIST1)
add_executable (${PROJECT_NAME} main.c ${SRC_LIST} ${SRC_LIST1})
- 这里出现了一个新的命令:
include_directories
。该命令是用来向工程添加多个指定头文件的搜索路径,路径之间用空格分隔。- (方法一)因为main.c里include了testFunc.h和testFunc1.h,如果没有
include_directories
命令来指定头文件所在位置,就会无法编译。 - (方法二,不推荐)如果不适用如果没有
include_directories
命令,可以在main.c里使用include来指定路径。如下
- (方法一)因为main.c里include了testFunc.h和testFunc1.h,如果没有
#include "test_func/testFunc.h"
#include "test_func1/testFunc1.h"
- 另外,我们使用了2次
aux_source_directory
,因为源文件分布在2个目录下,所以添加2次
优化:正规一点的组织结构
正规一点来说,一般会把源文件放到src目录下,把头文件放入到include文件下,生成的对象文件放入到build目录下,最终输出的elf文件会放到bin目录下,这样整个结构更加清晰。
让我们把前面的文件再次重新组织下,
main.c内容如下:
#include <stdio.h>
#include "testFunc.h"
#include "testFunc1.h"
int main(void)
{
func(100);
func1(200);
return 0;
}
testFunc.c内容如下:
/*
** testFunc.c
*/
#include <stdio.h>
#include "testFunc.h"
void func(int data)
{
printf("data is %d\n", data);
}
testFunc1.c内容如下:
/*
** testFunc1.c
*/
#include <stdio.h>
#include "testFunc1.h"
void func1(int data)
{
printf("data is %d\n", data);
}
testFunc.h内容如下:
/*
** testFunc.h
*/
#ifndef _TEST_FUNC_H_
#define _TEST_FUNC_H_
void func(int data);
#endif
testFunc1.h内容如下:
/*
** testFunc1.h
*/
#ifndef _TEST_FUNC1_H_
#define _TEST_FUNC1_H_
void func1(int data);
#endif
实验一
操作
当前项目路径如下:
(1)最外层目录CMakeLists.txt内容如下
cmake_minimum_required (VERSION 2.8)
project (demo)
set(SOURCES
src/testFunc.c
src/testFunc1.c
src/main.c
)
add_executable(${PROJECT_NAME} ${SOURCES})
target_include_directories(${PROJECT_NAME}
PRIVATE
${PROJECT_SOURCE_DIR}/include
)
(2)构建并说明
执行下面命令:
$ cd build/
$ cmake ..
$ make
$ ./demo
理论:CMakeLists.txt说明
(1)源文件变量
- 创建包含源码文件的变量可使你更清楚地了解这些文件,并轻松将其添加到多个命令中,例如,add_executable()功能。(不推荐的写法)
set(SOURCES
src/testFunc.c
src/testFunc1.c
src/main.c
)
add_executable(${PROJECT_NAME} ${SOURCES})
- Note:另一种替代方案是使用 GLOB 命令使用通配符模式匹配查找文件
file(GLOB SOURCES "src/*.c")
add_executable(${PROJECT_NAME} ${SOURCES})
- 对于现代的CMake,不建议对源代码使用变量,通常直接在add_xxx函数中声明源代码。也就是建议写成:
add_executable(${PROJECT_NAME}
src/testFunc.c
src/testFunc1.c
src/main.c
)
(2)头文件目录
- 当你有不同的头文件目录时,可以使用
target_include_directories()
函数让编译器知道它们编译此目标时,会将这些目录添加到带有-i
标志的编译指令中,例如-i/directory/path
target_include_directories(target
PRIVATE
${PROJECT_SOURCE_DIR}/include
)
- PRIVATE标识符指定include的范围,这对库很重要,这些内容将在下一节中进行说明
(3)目录路径
- CMake 语法指定了许多变量,这些变量可用于帮助在项目或源代码树中找到有用的目录。其中一些包括:
变量 | 信息 |
---|---|
CMAKE_SOURCE_DIR | 根源目录 |
CMAKE_CURRENT_SOURCE_DIR | 如果使用子项目和目录,则为当前源目录 |
PROJECT_SOURCE_DIR | 当前 cmake 项目的源目录。 |
CMAKE_BINARY_DIR | 根二进制文件生成目录。这是运行cmake命令的目录。 |
CMAKE_CURRENT_BINARY_DIR | 你当前所处的生成目录。 |
PROJECT_BINARY_DIR | 当前项目的生成目录。 |
输出说明
在前面的示例中,当运行make命令时,输出仅显示构建的状态。要查看用于调试目的的完整输出,可以在运行make时添加VERBOSE=1标志。
VERBOSE 输出如下所示。可以看到,include目录已经被添加到了编译命令中。
$ make clean
$ make VERBOSE=1
实验二
项目结构如下
(1)最外层录下CMakeLists.txt,内容如下,
cmake_minimum_required (VERSION 2.8)
project (demo)
add_subdirectory (src)
这里出现一个新的命令add_subdirectory()
:
- 这个命令可以向当前工程添加存放源文件的子目录,并可以指定中间二进制和目标二进制的存放位置
- 这里指定src目录下存放了源文件,当执行cmake时,就会进入src目录下去找src目录下的CMakeLists.txt
- 所以在src目录下也建立一个CMakeLists.txt
(2)src/CMakeLists.txt 内容如下
#include_directories (../include)
include_directories (${PROJECT_SOURCE_DIR}/include)
aux_source_directory (. SOURCES)
add_executable (${PROJECT_NAME} ${SOURCES})
set (EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
- 命令set,是用于定义变量的。
- EXECUTABLE_OUT_PATH和PROJECT_SOURCE_DIR是CMake自带的预定义变量,其意义如下,
- EXECUTABLE_OUTPUT_PATH :目标二进制可执行文件的存放位置
- PROJECT_SOURCE_DIR:工程的根目录
所以,这里set的意思是把存放elf文件的位置设置为工程根目录下的bin目录。
运行cmake
- 先我们切到build目录下,然后输入以下命令,
cmake ..
,Makefile会在build目录下生成,然后在build目录下运行make
- 运行ok,我们再切到bin目录下,发现main已经生成,并运行测试,
测试OK!
这里解释一下为什么在build目录下运行cmake?
- 从前面几个case中可以看到,如果不这样做,cmake运行时生成的附带文件就会跟源码文件混在一起,这样会对程序的目录结构造成污染
- 而在build目录下运行cmake,生成的附带文件就只会待在build目录下,如果我们不想要这些文件了就可以直接清空build目录,非常方便。
两种结构对比:
- 对于实验一:只生成一个elf文件,推荐使用这种结构
- 对于实验二:这种写法是为了处理需要生成多个elf文件的情况,最外层的CMakeLists.txt用于掌控全局,使用add_subdirectory来添加要生成elf文件的源码目录。
总结
命令总结:
命令 | 说明 | 示例 |
---|---|---|
include_directories | 该命令是用来向工程添加多个指定头文件的搜索路径,路径之间用空格分隔 | include_directories (../include) |
add_subdirectory() | 向当前工程添加存放源文件的子目录 | add_subdirectory (src) |
set | 用于定义变量的 | set (EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin) |
CMake自带的预定义变量总结:
示例 | 说明 |
---|---|
EXECUTABLE_OUTPUT_PATH | 目标二进制可执行文件的存放位置 |
PROJECT_SOURCE_DIR | 工程的根目录 |