一 、基本原理理解
在GitHub下载了C++工程,通过先CMAKE再在vscode中打开的方法,实现部署编译调试。Cmake的功能其实就是将cpp文件和lib文件连接起来,哪个cpp文件需要哪个lib文件,相当于自动配置了c++工程中每个工程的属性,如下图所示,可以看到工程的各项属性,如链接库、包含目录等,还可在makelist文件中以分别配置debug的属性和release的属性。
二、CMake将opencv加入到cpp工程中
c++工程与python的一个不同就是扩展包安装的没有python那么简单快速,一个pip install就可以把一些开源依赖包装上并使用了。当前有个工程需要依赖opencv的函数,#include <opencv2/opencv.hpp> // 用于矩阵和图像处理
,如何通过cmake将opencv集成到cpp工程中呢。
- 首先要安装opencv,官网下载其编译好的exe文件,执行之后是一个extracting框,会把文件提取到指定的文件夹中。
- 然后需要把其中的D:/opencv/build/文件夹放到系统环境变量的path变量下,因为OpenCVConfig.cmake和OpenCVConfig-version.cmake这两个文件在这个文件夹里,CMakeLists.txt文件中的
find_package( OpenCV REQUIRED )
之所以能找到opencv的package,就是因为cmake通过找到以上两个cmake文件,可以知道opencv这个包的信息了。 - 写你的cpp文件,官方文档中也有例程可以做测试用,OpenCV: Using OpenCV with gcc and CMake,然后就是要写CMakeLists.txt文件:
cmake_minimum_required(VERSION 2.8)
project( DisplayImage )
find_package( OpenCV REQUIRED )
include_directories( ${OpenCV_INCLUDE_DIRS} )
add_executable( DisplayImage DisplayImage.cpp ) #displayimage.cpp是对应的cpp文件名
target_link_libraries( DisplayImage ${OpenCV_LIBS} )
- 这样就可以cmake生成工程了。
从这个应用可以看出,其他的CPP库应该也是一样的套路,只要库有对应的cmake文件,并且让cmake找到这个cmake文件,就可以用find_package()方法来找到并应用这个包了。
三、 CMake将nlohmann_json库加入工程中
与opencv不同,这个库没有cmake文件,直接将其源代码整体文件夹放入本工程文件夹中,名称是nlohmann_json,然后再cmakelist.txt文件中,加入add_subdirectory(nlohmann_json )
和target_link_libraries( DisplayImage PRIVATE nlohmann_json::nlohmann_json)
,这样工程中就能用json功能了。在调试过程中发现,nlohmann_json文件夹中必须有cmakelist.txt文件,不然会报错。
四、Boost库加入到工程中
这个库的源码文件好找,但是exe安装文件不好找,后来找到后安装到C盘,找到其中的cmake文件所在的文件夹,说明可以用find_package()来设置这个工程的依赖。直接用find_package (Boost REQUIRED )
报错,找不到这个包,然后看cmake的手册,看这个find_package()从什么位置去找包,发现有一个位置变量可以定义搜索位置,就是set(CMAKE_FIND_PACKAGE_REDIRECTS_DIR "C:/local/boost_1_86_0/lib64-msvc-14.3/cmake")
,于是设置搜索位置后,find_package()就不报错了。
四、add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../pybind11)
这句报错,改成add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../pybind11 ${CMAKE_CURRENT_BINARY_DIR}/pybind11-build)
就可以了,这是为什么?
简单来说,答案是:因为 ../pybind11 文件夹不在您当前项目(pybindTest)的文件夹内部,CMake 需要您明确告诉它,应该把为 pybind11 生成的中间文件放在哪里。
详细解释
我们可以把 add_subdirectory 的行为分为两种情况:
- 情况一:添加“树内”(In-Tree)的子目录
假设您的项目结构是这样的,pybind11 在 pybindTest 内部:
/pybindTest/
├── libs/
│ └── pybind11/ <-- pybind11 在项目内部
├── CMakeLists.txt
└── ...
在这种情况下,您可以在 CMakeLists.txt 中使用单参数版本:
add_subdirectory(libs/pybind11) # 这样写完全没问题
CMake 会很智能地在您的 build 目录里也创建一个对应的 libs/pybind11 文件夹,用来存放为这个子项目生成的所有文件。一切都井井有条,没有歧义。 - 情况二:添加“树外”(Out-of-Tree)的源目录
这是您当前的情况。您的项目结构是这样的,pybind11 在 pybindTest 外部,是一个同级的“邻居”:
/some_parent_folder/
├── pybind11/ <-- pybind11 在项目外部
└── pybindTest/
├── CMakeLists.txt
└── ...
现在,当您在 pybindTest 的 CMakeLists.txt 中写下:
add_subdirectory(../pybind11) # 这会报错
CMake 就遇到了一个难题:“我应该把为 ../pybind11 这个外部项目生成的构建文件(比如 .obj, .lib 等)放在哪里呢?”
放在 ../pybind11 的源码目录里? 这是一种“源码内构建”,会污染 pybind11 的源码,是非常不好的实践。CMake 会极力避免这种情况。
放在 pybindTest/build 目录里? 直接放进去可能会和我主项目 pybindTest 自己的构建文件混在一起,甚至覆盖同名文件,这会造成混乱。
为了解决这个“模棱两可”的局面,CMake 的设计者决定不替您做决定,而是直接报错,强制要求您必须明确地提供第二个参数来指定一个专门存放 pybind11 构建文件的目录。