CMake项目中神器:CMakeLists.txt

首次接触 CMake,见识了 CMakeLists.txt 的强大后,赶紧整理出来分享一下。

参考资料:《Cmake 3.6 W3Cschool参考手册》

本文讲述了一个 CMake 项目,在从单文件到多文件,单目录到多子目录的代码演化过程中,CMakeLists.txt 脚本的编写变化。

本文内容都是在 Linux 系统上操作的,如果在 VS CodeVisual Status 2019 上操作的话,也是一样的。

单文件的CMake项目

例子1

创建如下 CMake 项目

目录结构

[root@localhost cmaketest]# tree
.
├── CMakeLists.txt
└── main.cpp

0 directories, 2 files
[root@localhost cmaketest]#

创建CMake项目

如上,新建一个文件夹存放 CMake 项目,这里是 cmaketest 目录。在目录下新建两个文件:main.cppCMakeLists.txt

main.cpp 中写上如下代码(当然你也可以写其它的,心随你动,哈哈)

#include <iostream>

int Func_Add(int num1, int num2)
{
	int nSum = num1 + num2;
	return nSum;
}

int main()
{
	int numSum = Func_Add(2, 3);
	std::cout << "2 + 3 = " << numSum << std::endl;

	return 0;
}

CMakeLists.txt 中写上如下命令

cmake_minimum_required (VERSION 3.8)	

PROJECT(calcSum)

ADD_EXECUTABLE(calcSum main.cpp)

这样一个简单的 CMake 项目就新建好了。

CMakeLists.txt 中命令解释:

  • cmake_minimum_required (VERSION 3.8)
    设置项目所需的 cmake 最低版本为 cmake 3.8 版本。
  • PROJECT(calcSum)
    为整个项目设置一个名字,这里起名为 calcSum
  • ADD_EXECUTABLE(calcSum main.cpp)
    添加一个目标可执行文件 calcSum 到项目中,这个可执行文件是使用后面指定的 main.cpp 文件生成的。

构建和编译CMake项目

在项目路径下新建一个 build 目录,用来存放构建和编译过程中生成的文件。
进入 build 目录后编译验证如下:

[root@localhost cmaketest]# mkdir build
[root@localhost cmaketest]# cd build/
[root@localhost build]# cmake ..
-- The C compiler identification is GNU 4.8.5
-- The CXX compiler identification is GNU 4.8.5
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done (1.7s)
-- Generating done (0.0s)
-- Build files have been written to: /home/fenghx/myCMakePro/cmaketest/build
[root@localhost build]# make
[ 50%] Building CXX object CMakeFiles/calcSum.dir/main.cpp.o
[100%] Linking CXX executable calcSum
[100%] Built target calcSum
[root@localhost build]# ls
calcSum  CMakeCache.txt  CMakeFiles  cmake_install.cmake  Makefile
[root@localhost build]# ./calcSum 
2 + 3 = 5
[root@localhost build]# 

这就是单个文件的 CMake 项目中 CMakeLists.txt 的编写。

多个文件的CMake项目

我们开发一个项目,代码文件肯定不只有一个 main.cpp 那么简单。肯定会有很多源文件。这里先说一下有两个文件:main.cpputil.hCMake 项目。

例子2

目录结构

[root@localhost cmaketest]# tree
.
├── build
├── CMakeLists.txt
├── main.cpp
└── util.h

0 directories, 3 files
[root@localhost cmaketest]#

修改项目

将第一个例子中 main.cpp 中的 Func_Add() 函数的代码提取到 util.h 中,main.cpp 中保留剩下的。其它不变。如下

util.h 文件中的代码:

#ifndef __UTIL_H__
#define __UTIL_H__

#include <iostream>

int Func_Add(int num1, int num2)
{
	int nSum = num1 + num2;
	return nSum;
}

#endif

main.cpp 文件中的代码:

#include "util.h"

int main()
{
	int numSum = Func_Add(2, 3);
	std::cout << "2 + 3 = " << numSum << std::endl;

	return 0;
}

编译项目

现在,不用修改 CMakeLists.txt 文件,直接编译的话也是OK的:

[root@localhost cmaketest]# rm -rf build/*
[root@localhost cmaketest]# cd build/
[root@localhost build]# cmake ..
-- The C compiler identification is GNU 4.8.5
-- The CXX compiler identification is GNU 4.8.5
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done (1.4s)
-- Generating done (0.0s)
-- Build files have been written to: /home/fenghx/myCMakePro/cmaketest/build
[root@localhost build]# make
[ 50%] Building CXX object CMakeFiles/calcSum.dir/main.cpp.o
[100%] Linking CXX executable calcSum
[100%] Built target calcSum
[root@localhost build]# ls
calcSum  CMakeCache.txt  CMakeFiles  cmake_install.cmake  Makefile
[root@localhost build]# ./calcSum 
2 + 3 = 5

原因想必大家也都知道,在预处理的时候,会将 util.h 头文件中的内容整合到 main.cpp 中,这就相当于和例子1一样了。

但是我们日常开发时,很多时候都不会将函数的实现,即函数体放到头文件中,头文件中只放函数的声明,.cpp 源文件中才放函数的实现。即如下。

例子3

目录结构:

[root@localhost cmaketest]# tree
.
├── build
├── CMakeLists.txt
├── main.cpp
├── util.cpp
└── util.h

1 directory, 4 files
[root@localhost cmaketest]#

修改项目

新增一个 util.cpp 文件如下:

#include "util.h"

int Func_Add(int num1, int num2)
{
	int nSum = num1 + num2;
	return nSum;
}

头文件 util.h 中代码修改为:

#ifndef __UTIL_H__
#define __UTIL_H__
#include < iostream >

int Func_Add(int num1, int num2);

#endif

编译项目

[root@localhost cmaketest]# rm -rf build/*
[root@localhost cmaketest]# cd build/
[root@localhost build]# cmake ..
-- The C compiler identification is GNU 4.8.5
-- The CXX compiler identification is GNU 4.8.5
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done (1.5s)
-- Generating done (0.0s)
-- Build files have been written to: /home/fenghx/myCMakePro/cmaketest/build
[root@localhost build]# make
[ 50%] Building CXX object CMakeFiles/calcSum.dir/main.cpp.o
[100%] Linking CXX executable calcSum
CMakeFiles/calcSum.dir/main.cpp.o:在函数‘main’中:
main.cpp:(.text+0x13):对‘Func_Add(int, int)’未定义的引用
collect2: 错误:ld 返回 1
make[2]: *** [calcSum] 错误 1
make[1]: *** [CMakeFiles/calcSum.dir/all] 错误 2
make: *** [all] 错误 2
[root@localhost build]# 

可见,我们构建项目后,使用 make 命令编译时,会报错:main.cpp:(.text+0x13):对‘Func_Add(int, int)’未定义的引用
这是由于,我们在 CMakeLists.txt 中语句 ADD_EXECUTABLE(calcSum main.cpp) 只指定了一个依赖源文件 main.cpp, 但是现在的话,要想生成 calcSum 可执行文件,需要依赖 main.cpputil.cpp 两个源文件才可以。

修改CMakeLists.txt

cmake_minimum_required (VERSION 3.8)

PROJECT(calcSum)

ADD_EXECUTABLE(calcSum main.cpp util.cpp)

上面我们将 util.cpp 源文件也指定在依赖项中,用空格分割。

再编译项目

[root@localhost cmaketest]# cd build/
[root@localhost build]# cmake ..
-- Configuring done (0.0s)
-- Generating done (0.0s)
-- Build files have been written to: /home/fenghx/myCMakePro/cmaketest/build
[root@localhost build]# make
[ 33%] Building CXX object CMakeFiles/calcSum.dir/util.cpp.o
[ 66%] Linking CXX executable calcSum
[100%] Built target calcSum
[root@localhost build]# ./calcSum 
2 + 3 = 5
[root@localhost build]# 

发现编译成功了。ADD_EXECUTABLE() 的第一个参数是目标可执行文件的名字,后面的为依赖源文件,当编译需要依赖多个源文件时,可以将依赖的源文件加在后面,源文件和源文件之间用空格分割。

单个子目录的CMake项目

例子4

我们的项目不可能所有的文件都放在同一个主目录下,实际开发中项目都会分很多子目录,比如下面的

目录结构

[root@localhost cmaketest]# tree
.
├── build
├── CMakeLists.txt
├── main.cpp
└── src
       ├── util.cpp
       └── util.h

2 directories, 4 files
[root@localhost cmaketest]#

修改项目

在项目主目录下新建一个 src 目录,然后将文件 util.hutil.cpp 文件移动到 src 目录下。

编译项目

[root@localhost cmaketest]# rm -rf build/*
[root@localhost cmaketest]# cd build/
[root@localhost build]# cmake ..
-- The C compiler identification is GNU 4.8.5
-- The CXX compiler identification is GNU 4.8.5
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done (1.5s)
CMake Error at CMakeLists.txt:5 (ADD_EXECUTABLE):
  Cannot find source file:

    util.cpp

  Tried extensions .c .C .c++ .cc .cpp .cxx .cu .mpp .m .M .mm .ixx .cppm .h
  .hh .h++ .hm .hpp .hxx .in .txx .f .F .for .f77 .f90 .f95 .f03 .hip .ispc

CMake Error at CMakeLists.txt:5 (ADD_EXECUTABLE):
  No SOURCES given to target: calcSum

CMake Generate step failed.  Build files cannot be regenerated correctly.
[root@localhost build]# 

发现在执行 cmake 命令构建项目时就报错了:

CMake Error at CMakeLists.txt:5 (ADD_EXECUTABLE):
    Cannot find source file:

        util.cpp

无法找到依赖的源文件 util.cpp,这是由于将 util.cpp 文件移动到 src 后,并没有在 CMakeLists.txt 中修改依赖源文件的路径,所以 cmake 编译器在主目录下查找时无法找到。

修改CMakeLists.txt文件

cmake_minimum_required (VERSION 3.8)

PROJECT(calcSum)

ADD_EXECUTABLE(calcSum main.cpp src/util.cpp)

构建项目

[root@localhost cmaketest]# cd build/
[root@localhost build]# cmake ..
-- Configuring done (0.0s)
-- Generating done (0.0s)
-- Build files have been written to: /home/fenghx/myCMakePro/cmaketest/build
[root@localhost build]# 

发现可以成功建立了。接着我们执行 make 编译项目。

编译项目

[root@localhost build]# make
[ 33%] Building CXX object CMakeFiles/calcSum.dir/main.cpp.o
/home/fenghx/myCMakePro/cmaketest/main.cpp:1:18: 致命错误:util.h:没有那个文件或目录
 #include "util.h"
                  ^
编译中断。
make[2]: *** [CMakeFiles/calcSum.dir/main.cpp.o] 错误 1
make[1]: *** [CMakeFiles/calcSum.dir/all] 错误 2
make: *** [all] 错误 2
[root@localhost build]# 

这里又报错了,无法找到 util.h 头文件。可想而知和一开始找不到 util.cpp 是一样。我们试着将头文件加到依赖项中,看行不行。

修改CMakeLists.txt文件

cmake_minimum_required (VERSION 3.8)

PROJECT(calcSum)

ADD_EXECUTABLE(calcSum main.cpp src/util.cpp src/util.h)

重新编译

[root@localhost cmaketest]# cd build/
[root@localhost build]# cmake ..
-- Configuring done (0.0s)
-- Generating done (0.0s)
-- Build files have been written to: /home/fenghx/myCMakePro/cmaketest/build
[root@localhost build]# make
[ 33%] Building CXX object CMakeFiles/calcSum.dir/main.cpp.o
/home/fenghx/myCMakePro/cmaketest/main.cpp:1:18: 致命错误:util.h:没有那个文件或目录
 #include "util.h"
                  ^
编译中断。
make[2]: *** [CMakeFiles/calcSum.dir/main.cpp.o] 错误 1
make[1]: *** [CMakeFiles/calcSum.dir/all] 错误 2
make: *** [all] 错误 2
[root@localhost build]# 

发现这样还是无法找到 util.h 头文件,那应该如何才能让编译器找到这个头文件呢,无非就是告诉编译器这个头文件的路径,这就需要用到:include_directories()

  • include_directories(路径名称)
    添加一个头文件路径到项目构建中去。

修改CMakeLists.txt文件

cmake_minimum_required (VERSION 3.8)

PROJECT(calcSum)

INCLUDE_DIRECTORIES(src/)

ADD_EXECUTABLE(calcSum main.cpp src/util.cpp)

如上,我们将相对路径 src/ 添加到了项目的构件中,然后再编译项目。

编译项目

[root@localhost cmaketest]# cd build/
[root@localhost build]# cmake ..
-- Configuring done (0.0s)
-- Generating done (0.0s)
-- Build files have been written to: /home/fenghx/myCMakePro/cmaketest/build
[root@localhost build]# make
[ 33%] Building CXX object CMakeFiles/calcSum.dir/main.cpp.o
[ 66%] Building CXX object CMakeFiles/calcSum.dir/src/util.cpp.o
[100%] Linking CXX executable calcSum
[100%] Built target calcSum
[root@localhost build]# ./calcSum 
2 + 3 = 5
[root@localhost build]# 

发现此时就已经可以编译生成可执行文件了。

子目录中有很多的源文件的CMake项目

如果子目录中有很多的 .cpp 源文件,单单把这么多的源文件都写到依赖项中,就很费劲了,密密麻麻可读性也不好,而且如果要新建 .cpp 源文件的话,岂不是每次添加都得编辑一下 CMakeLists.txt 文件,太麻烦了,有没有办法解决这个事情呢?

有办法:

  • aux_source_directory(路径名称 变量名称)
    搜索指定目录下的所有源文件,并将他们保存在给定的变量中。

修改CMakeLists.txt文件

cmake_minimum_required (VERSION 3.8)

PROJECT(calcSum)

INCLUDE_DIRECTORIES(src/)

AUX_SOURCE_DIRECTORY(src/ SUB_SOURCE_FILE)

ADD_EXECUTABLE(calcSum main.cpp ${SUB_SOURCE_FILE})

如上,AUX_SOURCE_DIRECTORY() 会搜索 src/ 目录下的所有源文件,将它们保存到指定的变量 SUB_SOURCE_FILE 中。之后我们直接在 ADD_EXECUTABLE() 中使用这个变量就可以了,使用变量的方法为:${变量名}

编译项目

[root@localhost cmaketest]# rm -rf build/*
[root@localhost cmaketest]# cd build/
[root@localhost build]# cmake ..
-- The C compiler identification is GNU 4.8.5
-- The CXX compiler identification is GNU 4.8.5
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done (1.5s)
-- Generating done (0.0s)
-- Build files have been written to: /home/fenghx/myCMakePro/cmaketest/build
[root@localhost build]# make
[ 33%] Building CXX object CMakeFiles/calcSum.dir/main.cpp.o
[ 66%] Building CXX object CMakeFiles/calcSum.dir/src/util.cpp.o
[100%] Linking CXX executable calcSum
[100%] Built target calcSum
[root@localhost build]# ./calcSum 
2 + 3 = 5
[root@localhost build]#

可见,在使用 make 编译的时候确实找到 util.cpp 源文件了。这样就不用我们每次都要添加依赖的源文件了。是不是很帅。

多个子目录的CMake项目

再来想想,项目中一般不会只有一个子目录,往往有很多个子目录,如果子目录很多,那只使用 AUX_SOURCE_DIRECTORY() 的话显然要写很多行,并且变量也要定义很多,不太好,那该怎么办呢?有没有传递多个目录的方法呢?

有,使用 file() 文件操作命令。

  • file(GLOB 变量名 路径1 路径2 …)
    按照一定规则搜索给定路径列表中的所有文件,将文件名都保存在给定的变量中。

修改CMakeLists.txt文件

cmake_minimum_required (VERSION 3.8)

PROJECT(calcSum)

INCLUDE_DIRECTORIES(src/)

FILE(GLOB SOURCE_FILES 
        ${PROJECT_SOURCE_DIR}/*.cpp
        ${PROJECT_SOURCE_DIR}/src/*.cpp
)

ADD_EXECUTABLE(calcSum ${SOURCE_FILES})

这里的变量 PROJECT_SOURCE_DIRCMake 中的一个变量,代表的是当前项目的根目录。上面命令是将根目录下的所有 .cpp 文件和 src/ 目录下的所有 .cpp 文件都搜索出来,存放到变量 SOURCE_FILES 中,这样,在命令 ADD_EXECUTABLE() 中就可以直接使用变量 SOURCE_FILES ,而不需要编写依赖的源文件了。

编译项目

[root@localhost cmaketest]# rm -rf build/*
[root@localhost cmaketest]# cd build/
[root@localhost build]# cmake ..
-- The C compiler identification is GNU 4.8.5
-- The CXX compiler identification is GNU 4.8.5
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done (1.4s)
-- Generating done (0.0s)
-- Build files have been written to: /home/fenghx/myCMakePro/cmaketest/build
[root@localhost build]# make
[ 33%] Building CXX object CMakeFiles/calcSum.dir/main.cpp.o
[ 66%] Building CXX object CMakeFiles/calcSum.dir/src/util.cpp.o
[100%] Linking CXX executable calcSum
[100%] Built target calcSum
[root@localhost build]# ./calcSum 
2 + 3 = 5
[root@localhost build]# 

发现真的是可以的,完美解决不爽的问题。

需要指定编译器版本的CMake项目

修改项目

如果将 main.cpp 改为如下:

#include "util.h"

int main()
{
	auto numSum = Func_Add(2, 3);
	std::cout << "2 + 3 = " << numSum << std::endl;

	return 0;
}

我用 auto 自动识别类型定义了变量 numSum,来接受 Func_Add() 返回的值。

编译项目

[root@localhost cmaketest]# cd build/
[root@localhost build]# make
[ 33%] Building CXX object CMakeFiles/calcSum.dir/main.cpp.o
/home/fenghx/myCMakePro/cmaketest/main.cpp: 在函数‘int main()’中:
/home/fenghx/myCMakePro/cmaketest/main.cpp:5:7: 错误:‘numSum’不是一个类型名
  auto numSum = Func_Add(2, 3);
       ^
/home/fenghx/myCMakePro/cmaketest/main.cpp:6:29: 错误:‘numSum’在此作用域中尚未声明
  std::cout << "2 + 3 = " << numSum << std::endl;
                             ^
make[2]: *** [CMakeFiles/calcSum.dir/main.cpp.o] 错误 1
make[1]: *** [CMakeFiles/calcSum.dir/all] 错误 2
make: *** [all] 错误 2
[root@localhost build]#

这里就会报错,原因是 auto 关键字是 C++11 标准里面的,而在不指定 C++ 标准版本的情况下,编译器是不会执行 C++11 标准的。

那如何来设置 C++11 标准呢?有办法,如下:

  • set(CMAKE_CXX_STANDARD 11)
    设置C++标准为 C++11

修改CMakeLists.txt文件

cmake_minimum_required (VERSION 3.8)

SET(CMAKE_CXX_STANDARD 11)

PROJECT(calcSum)

INCLUDE_DIRECTORIES(src/)

FILE(GLOB SOURCE_FILES 
        ${PROJECT_SOURCE_DIR}/*.cpp
        ${PROJECT_SOURCE_DIR}/src/*.cpp
)

ADD_EXECUTABLE(calcSum ${SOURCE_FILES})

编译项目

[root@localhost build]# rm -rf ./*
[root@localhost build]# ls
[root@localhost build]# cmake ..
-- The C compiler identification is GNU 4.8.5
-- The CXX compiler identification is GNU 4.8.5
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done (1.4s)
-- Generating done (0.0s)
-- Build files have been written to: /home/fenghx/myCMakePro/cmaketest/build
[root@localhost build]# make
[ 33%] Building CXX object CMakeFiles/calcSum.dir/main.cpp.o
[ 66%] Building CXX object CMakeFiles/calcSum.dir/src/util.cpp.o
[100%] Linking CXX executable calcSum
[100%] Built target calcSum
[root@localhost build]# ./calcSum 
2 + 3 = 5
[root@localhost build]# 

如上,完美解决问题。

需要连接动态库的CMake项目

另外,如果你要链接动态库,那么就要使用 target_link_libraries()

  • target_link_libraries(目标可执行文件名 动态库名)
    指定要链接给目标可执行文件的动态库

更多的 CMakeLists.txt 编写技巧,请参考《Cmake 3.6 W3Cschool参考手册》


CMake项目中CMakeLists.txt的编写就分享到这里,感谢你的翻阅,希望能帮到你。

猜你喜欢

转载自blog.csdn.net/weixin_44131612/article/details/129417040