理论上,任意一个C++程序都可以用g++来编译。
但当程序规模越来越大时,一个工程可能有许多个文件夹和源文件,这时输入的编译命令将越来越长。通常,一个小型C++项目可能含有十几个类,各类间还存在着复杂的依赖关系。其中一部分要编译成可执行文件,另一部分编译成库文件。如果仅靠g++命令,则需要输入大量的编译指令,整个编译过程会变得异常烦琐。因此,对于C++项目,使用一些工程管理工具会更加高效。在历史上,工程师们曾使用makefile进行自动编译,但下面要谈的cmake比它更加方便。并且cmake在工程上广泛使用
cmake允许开发者编写一种与平台无关(跨平台)的CMakeLists.txt 文件来制定整个工程的编译流程,cmake 工具会解析CMakeLists.txt 文件语法规则,再根据当前的编译平台,生成本地化的Makefile 和工程文件
简而言之,makefile是一个定义源文件编译过程的文件,而CMakeLists.txt是一个可以生成makefile的文件,它们之间的关系如下图所示:
sudo apt-get install cmake
如下,我们写了一个简单的cpp文件
#include
using namespace std;
int main()
{
cout<<"hello slam!"<
现在我们想编译该文件以生成一个可执行文件,如果使用g++进行编译是这样的:
g++ helloSLAM.cpp -o helloSLAM
如果我们使用cmake,则我们首先需要编写CMakeLists.txt文件,内容如下:
#声明cmake的最低版本
cmake_minimum_required(VERSION 2.8)
#声明一个cmake工程
project(hello)
#添加一个可执行文件,类似于执行g++ helloSLAM.cpp -o helloSLAM
add_executable(helloSLAM helloSLAM.cpp )
文件中内容的各行作用如注释所示
当前路径下各文件如下所示:
保存该文件后,执行如下命令
cmake ./
cmake 后面携带的路径指定了CMakeLists.txt 文件的所在路径,执行结果如下所示
可以看到,执行完cmake 之后,除了源文件main.c 和CMakeLists.txt 之外,生成了很多其它的文件或文件夹,包括:CMakeCache.txt、CmakeFiles、cmake_install.cmake、Makefile
接下来,我们之间执行make编译命令即可生成可执行文件
执行可执行文件,执行结果如下
为了便于管理,我们可以把cmake生成的文件单独存放在一个文件夹内, 这时我们只需创建一个build文件夹
之后进入build文件夹,执行
cmake ../
可以看到,此时cmake生成的文件就全部在build文件中了
如果我们的源代码中使用了库文件,该如何编写CMakeLists.txt文件呢
库文件libHelloSLAM.h
#ifndef LIBHELLOSLAM_H
#define LIBHELLOSLAM_H
void printHello();
#endif
libHelloSLAM.cpp
#include
using namespace std;
void printHello()
{
cout<<"hello SLAM!"<
helloSLAM.cpp
#include
using namespace std;
#include"libHelloSLAM.h"
int main()
{
// cout<<"hello slam!"<
则CMakeLists.txt文件内容如下:
#声明cmake的最低版本
cmake_minimum_required(VERSION 2.8)
#声明一个cmake工程
project(hello)
#添加一个静态库文件
add_library(helloLib libHelloSLAM.cpp)
#添加一个可执行文件,类似于执行g++ helloSLAM.cpp -o helloSLAM
add_executable(helloSLAM helloSLAM.cpp )
#将库文件内容链接到可执行程序上
target_link_libraries(helloSLAM helloLib)
此时文件结构如下:
之后再次进入build文件夹执行cmake ../命令,文件夹结构没有什么变化
但是执行完make编译命令后,再次查看build文件夹,会发现多了一个libhelloLib.a的文件,该文件就是我们的静态库文件
在Linux中,库文件分成静态库和共享库两种。静态库以.a作为后缀名,共享库以.so结尾。所有库都是一些函数打包后的集合,差别在于静态库每次被调用都会生成一个副本,而共享库则只有一个副本,更省空间。
如果想生成共享库而不是静态库,只需使用以下语句即可。
add_library(helloSharedLib SHARED libHelloSLAM.cpp)
此时CMakeLists.txt文件内容为:
#声明cmake的最低版本
cmake_minimum_required(VERSION 2.8)
#声明一个cmake工程
project(hello)
#添加一个静态库文件
#add_library(helloLib libHelloSLAM.cpp)
#添加一个动态库文件
add_library(helloSharedLib SHARED libHelloSLAM.cpp)
#添加一个可执行文件,类似于执行g++ helloSLAM.cpp -o helloSLAM
add_executable(helloSLAM helloSLAM.cpp )
#将库文件内容链接到可执行程序上
target_link_libraries(helloSLAM helloSharedLib)
再次执行cmake ..命令和make编译命令后,build文件内容如下:
文件结构如下:
.
├── build
├── CMakeLists.txt
├── libHello
│ ├── CMakeLists.txt
│ ├── libHelloSLAM.cpp
│ └── libHelloSLAM.h
└── src
├── CMakeLists.txt
└── helloSLAM.cpp
#声明cmake的最低版本
cmake_minimum_required(VERSION 2.8)
#声明一个cmake工程
project(hello)
#添加子文件
add_subdirectory(libHello)
add_subdirectory(src)
其中add_subdirectory 命令告诉cmake 去子目录中寻找新的CMakeLists.txt 文件并解析它
add_library(libSharedHello SHARED libHelloSLAM.cpp)
libHello文件夹内存放的是库文件,因此该 CMakeLists.txt文件表示生成一个共享库文件
include_directories(../libHello)
add_executable(helloSLAM helloSLAM.cpp)
target_link_libraries(helloSLAM libSharedHello)
该文件夹内存放的是主程序,其中include_directories 命令用来指明头文件所在的路径
或者如下:
include_directories(${PROJECT_SOURCE_DIR}/libHello)
add_executable(helloSLAM helloSLAM.cpp)
target_link_libraries(helloSLAM libSharedHello)
这种方式使用到了PROJECT_SOURCE_DIR 变量,该变量指向了一个路径,从命名上可知,该变量表示工程源码的目录。
和前面一样,进入到build 目录下进行构建、编译,最终会得到可执行文件helloSLAM(build/src/helloSLAM)和库文件libSharedHello.so(build/libHello/libSharedHello.so)
如果我想让可执行文件单独放置在bin目录下,而库文件单独放置在lib目录下,如下所示
├──build
├──lib
│ └──libSharedHello.so
└──bin
└──helloSLAM
可以通过两个变量来实现,将src 目录下的CMakeList.txt 文件进行修改,如下所示:
include_directories(${PROJECT_SOURCE_DIR}/libHello)
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
add_executable(helloSLAM helloSLAM.cpp)
target_link_libraries(helloSLAM libSharedHello)
然后再对libhello 目录下的CMakeList.txt 文件进行修改,如下所示:
set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
add_library(libSharedHello SHARED libHelloSLAM.cpp)
EXECUTABLE_OUTPUT_PATH 变量控制可执行文件的输出路径,
LIBRARY_OUTPUT_PATH 变量控制库文件的输出路径。
参考:
【精选】CMake 入门与进阶_.cmake文件-CSDN博客