前言
折腾了一整天,终于对cmake有了一个初步的了解,掌握了其基本用法和CMakeLists.txt文件的简单编写方法。现在总结一下,主要记录一下有无子文件夹的区别和自己以后编写源文件的目录规范。注意这篇文章的出发点不在于使用复杂的命令来编写CMakeLists.txt,而是比较几种不同情况下其编写的区别和养成良好的规范。如果是想学会更多的命令使用,那么可以参考其他的资料。
cmake入门可以参考这篇文档,写的很全面,从中学到了很多。
cmake的安装
1、首先查看自己的系统有没有安装cmake,使用命令cmake --version
2、如果没有安装,
- 使用命令
sudo apt install cmake
安装 - 使用命令
sudo apt install cmake-qt-gui
安装gui界面,之后可以利用图形化界面进行操作,参考博文的第三栏的使用教程
cmake的编译流程
在 linux 平台下使用 CMake 生成 Makefile 并编译的流程如下:
1、编写 CMake 配置文件 CMakeLists.txt 。
2、执行命令 cmake PATH 或者 ccmake PATH 生成 Makefile 1 1ccmake 和 cmake 的区别在于前者提供了一个交互式的界面。 。其中, PATH 是 CMakeLists.txt 所在的目录。
3、使用 make 命令进行编译。
其中最重要的任务就是 CMakeLists.txt 文件的编写。下面分几种不同情况讨论CMakeLists.txt 编写的不同。
CMakeLists.txt 编写示例
写在前面
- 在你源文件的根目录下创建一个build文件夹,用来放置编译过程产生的中间文件和可执行文件,这个过程叫做OUT OF SOURCE,之所以这么做的原因是因为cmake和make的过程中会产生很多的文件,如果不单独放置的话就会和源文件混杂在一起,这不是我们想要的。
- 顶层的 CMakeLists.txt 文件必须放在该工程的根目录下
- 此篇示例基于以下三个文件,不同之处就在于放置的位置不同
- main.c中头文件必须路径明确:#include “lib/hello.h”(其他文件的路径也得明确,除非在统一目录下)
main.c内容如下
#include "lib/hello.h"
int main()
{
hello("World");
return 0;
}
hello.h内容如下
#ifndef __HELLO_
#define __HELLO_
void hello(const char* name);
#endif
hello.c内容如下
#include <stdio.h>
#include "hello.h"
void hello(const char * name)
{
printf ("Hello %s!/n", name);
}
Demo1
main函数放在根目录下,库函数放在/lib子目录下(每个子目录下都有CMakeLists.txt 文件)
根目录下文件如下图:
/lib目录下文件如下图:
顶层CMakeLists.txt
# CMake 最低版本号要求
cmake_minimum_required (VERSION 2.8)
#项目信息
project(HELLO)
# 查找当前目录下的所有源文件
# 并将名称保存到 DIR_SRCS 变量
aux_source_directory(. DIR_SRCS)
# 添加子目录
add_subdirectory(lib)
# 指定生成目标
add_executable(Demo ${DIR_SRCS})
# 添加链接库
target_link_libraries(Demo libhello)
/lib中的CMakeLists.txt
# 查找当前目录下的所有源文件
# 并将名称保存到 DIR_LIB_SRCS 变量
aux_source_directory(. DIR_LIB_SRCS)
#添加库文件
add_library(libhello ${DIR_LIB_SRCS})
编译后/build文件夹下内容
编译后build/lib文件夹下内容
注:有没有什么发现?是不是发现/build里面文件目录的架构和你根目录下的架构是一样的。这是由于你每个子目录下都有一个CMakeLists.txt 文件,一层一层向下发展,所以/build里面的架构和你原先的架构是一样的。
还有就是,你的可执行文件在哪里?是不是和你main.c文件的位置是对应的。你的库文件在哪里?是不是和你的/lib是对应的。发现了吧,它们是一一对应的,很重要!!!
Demo2
main函数放在/src子目录下,库函数放在/lib子目录下(每个子目录下都有CMakeLists.txt 文件)
根目录下文件如下图:
/src子目录下文件如下图:
/lib子目录下文件如下图:
顶层CMakeLists.txt
# CMake 最低版本号要求
CMAKE_MINIMUM_REQUIRED (VERSION 2.8)
# 项目信息
PROJECT(hello)
# 增加静态库子目录
ADD_SUBDIRECTORY(src)
ADD_SUBDIRECTORY(lib)
/src中的CMakeLists.txt
# 查找目录下所有源文件
AUX_SOURCE_DIRECTORY(. DIR_SRCS)
# 指定生成目标
ADD_EXECUTABLE(hello ${DIR_SRCS})
#指定连接库
TARGET_LINK_LIBRARIES(hello libhello)
/lib中的CMakeLists.txt
# 查找目录下所有源文件
AUX_SOURCE_DIRECTORY(. DIR_LIB_SRCS)
#增加库函数
ADD_LIBRARY(libhello ${DIR_LIB_SRCS})
编译后/build文件夹下内容
编译后/build/src文件夹下内容
编译后/build/lib文件夹下内容
注:有没有什么发现?是不是发现/build里面文件目录的架构和你根目录下的架构是一样的。这是由于你每个子目录下都有一个CMakeLists.txt 文件,一层一层向下发展,所以/build里面的架构和你原先的架构是一样的。
还有就是,你的可执行文件在哪里?是不是和你main.c文件的位置是对应的。你的库文件在哪里?是不是和你的/lib是对应的。发现了吧,它们是一一对应的,很重要!!!
Demo3
main函数放在/src子目录下,库函数放在/src子目录下的/lib子目录下(每个子目录下都CMakeLists.txt 文件)
根目录下文件如下图:
/src子目录下文件如下图:
src/lib子目录下文件如下图:
顶层CMakeLists.txt
# CMake 最低版本号要求
CMAKE_MINIMUM_REQUIRED (VERSION 2.8)
# 项目信息
PROJECT(hello)
# 增加静态库子目录
ADD_SUBDIRECTORY(src)
/src中的CMakeLists.txt
# 查找目录下所有源文件
AUX_SOURCE_DIRECTORY(. DIR_SRCS)
# 增加静态库子目录
ADD_SUBDIRECTORY(lib)
# 指定生成目标
ADD_EXECUTABLE(hello ${DIR_SRCS})
#指定连接库
TARGET_LINK_LIBRARIES(hello libhello)
/lib中的CMakeLists.txt
# 查找目录下所有源文件
AUX_SOURCE_DIRECTORY(. DIR_LIB_SRCS)
#增加库文件
ADD_LIBRARY(libhello ${DIR_LIB_SRCS})
编译后/build文件夹下内容
编译后/build/src文件夹下内容
编译后/build/lib文件夹下内容
注:有没有什么发现?是不是发现/build里面文件目录的架构和你根目录下的架构是一样的。这是由于你每个子目录下都有一个CMakeLists.txt 文件,一层一层向下发展,所以/build里面的架构和你原先的架构是一样的。
还有就是,你的可执行文件在哪里?是不是和你main.c文件的位置是对应的。你的库文件在哪里?是不是和你的/lib是对应的。发现了吧,它们是一一对应的,很重要!!!
Demo4
main函数放在/src子目录下,库函数放在/src子目录下的/lib子目录下(只有一个顶层CMakeLists.txt 文件,子目录下不包含CMakeLists.txt 文件)
根目录下文件如下图:
/src子目录下文件如下图:
/lib子目录下文件如下图:
顶层CMakeLists.txt
# CMake 最低版本号要求
CMAKE_MINIMUM_REQUIRED (VERSION 2.8)
# 查找目录下所有源文件
AUX_SOURCE_DIRECTORY(src DIR_SRCS)
AUX_SOURCE_DIRECTORY(src/lib DIR_LIB_SRCS)
# 项目信息
PROJECT(hello)
# 指定生成目标
ADD_EXECUTABLE(QuadrupedRobotCtrlProg ${DIR_SRCS} ${DIR_LIB_SRCS})
#指定连接库
#TARGET_LINK_LIBRARIES(QuadrupedRobotCtrlProg m)
编译后/build文件夹下内容
注:有没有什么发现?是不是发现/build里面文件目录的架构和你根目录下的架构不一样啦。这是由于你子目录下不包含CMakeLists.txt 文件了,所以/build里面的架构和你原先的架构不一样啦。
还有什么发现,这个示例和Demo3除了CMakeLists.txt 文件只在顶层有之外,其它地方完全一样。
你会发现你的可执行文件在/build目录下,这样在你编译完成要执行的时候就不用再切换目录到你的可执行文件下啦,因为我们就在这里。
这就是下面编写源文件目录规范的来源啦。我们来看看
源文件目录架构规范
注:这只是个人的规范,希望自己尽量这么来架构,因为有几点方便之处:
1、只需要编写一个CMakeLists.txt 文件
2、可执行文件直接放在/build下,可直接执行而不用切换目录
既然只是个人的规范,那肯定就不会适用于所有人,大家参考一下就可以啦。由于自己现在编写项目的经验有限,可能也只适用于现在的自己,之后有更新再来补充。大家有任何建议也可以一起讨论。
- 根目录下放置/build、/src和CMakeLists.txt
- main.c文件放在/src顶层
- 其他库文件和功能文件放在/src中的子目录下
大致来说就是下面这样的:
根目录下架构:
/src子目录下架构:(当然除了main.c以外还可以放置其他一些必要的文件)
/app子目录下架构:
/dev子目录下架构:
/usr_lib子目录下架构:(可以看到下面可以继续放置子文件夹)
/usr_lib/CMAT子目录下架构:(其他子文件夹类似)
顶层CMakeLists.txt 文件:
# CMake 最低版本号要求
CMAKE_MINIMUM_REQUIRED (VERSION 2.8)
# 查找目录下所有源文件
AUX_SOURCE_DIRECTORY(src DIR_SRCS)
AUX_SOURCE_DIRECTORY(src/usr_lib/CMAT DIR_CMAT_SRCS)
AUX_SOURCE_DIRECTORY(src/usr_lib/RC DIR_RC_SRCS)
AUX_SOURCE_DIRECTORY(src/usr_lib/TP DIR_TP_SRCS)
AUX_SOURCE_DIRECTORY(src/usr_lib/FILTER DIR_FILTER_SRCS)
AUX_SOURCE_DIRECTORY(src/usr_lib/QUEUE DIR_QUEUE_SRCS)
AUX_SOURCE_DIRECTORY(src/usr_lib/PID DIR_PID_SRCS)
AUX_SOURCE_DIRECTORY(src/dev DIR_DEV_SRCS)
AUX_SOURCE_DIRECTORY(src/app DIR_APP_SRCS)
# 项目信息
PROJECT(QuadrupedRobotCtrlProg)
# 指定生成目标
ADD_EXECUTABLE(QuadrupedRobotCtrlProg ${DIR_SRCS} ${DIR_CMAT_SRCS} ${DIR_PID_SRCS} ${DIR_RC_SRCS} ${DIR_TP_SRCS} ${DIR_DEV_SRCS} ${DIR_FILTER_SRCS} ${DIR_QUEUE_SRCS} ${DIR_APP_SRCS})
#指定连接库
TARGET_LINK_LIBRARIES(QuadrupedRobotCtrlProg m) #里面的参数m表示math lib
/build下的文件长这样:
是不是清晰明了,没有子文件夹,可执行文件就在这里的顶层,可以直接编译完成后执行而不用更改当前目录
备注
aux_source_directory 查找在某个路径下的所有源文件。
aux_source_directory(< dir > < variable >)
搜集所有在指定路径下的源文件的文件名,将输出结果列表储存在指定的变量中。该命令主要用在那些使用显式模板实例化的工程上,这样可以避免手工罗列所有的实例。
使用该命令来避免为一个库或可执行目标写源文件的清单,是非常具有吸引力的。但是如果该命令貌似可以发挥作用,那么CMake就不需要生成一个感知新的源文件何时被加进来的构建系统了(也就是说,新文件的加入,并不会导致CMakeLists.txt过时,从而不能引起CMake重新运行。——译注)。正常情况下,生成的构建系统能够感知它何时需要重新运行CMake,因为需要修改CMakeLists.txt来引入一个新的源文件。当源文件仅仅是加到了该路径下,但是没有修改这个CMakeLists.txt文件,使用者只能手动重新运行CMake来产生一个包含这个新文件的构建系统