此篇文章会随时更新,最终目的为总结Cmake在大型项目中常见的用法。
前言
Cmake是跨平台构编译大型项目的工具,配合make工具和编译器我们理论上我们可以编译任何工程。具体的介绍就不多说了,不论是OpenCV还是Pytorch都是用cmake作为构建工具,当然还有很多很多工程项目使用它,这里不进行详细的介绍。
本文也不是Cmake的教程,仅仅是一些基本的cmake知识普及,另外总结一些Cmake中经常使用的设置项变量和参数。作为自己的额外笔记,从而避免频繁的查阅而浪费时间。
但是这里推荐一个不错的入门教程~:http://www.hahack.com/codes/cmake/
以及Cmake设置项查询链接(对于cmake中各种命令的查询最好的是官方):https://cmake.org/cmake/help/latest/index.html
概念和常用的命令
要记住Cmake不是一套编译工具,它里面并不包含编译器,它能做的只是减轻我们输入繁琐命令的负担。类似于我们使用的IDE,将我们的重心放在编代码而不是浪费在编译代码这一系列无用的步骤中。它仅仅是一个跨平台工具,可以帮助你利用当前平台的编译器实现大型项目配置编译工程的一个工具。
具体的介绍可以看这里:编译器gcc、clang、make、cmake辨析
Cmake使用的编译器
使用cmake的时候,cmake会在指定路径(例如linux中为/usr/bin)自动查找当前系统内存在的编译器,至于不同系统的编译器,我们主要的系统一般为Windows、Linux以及MacOS。
其中,MSVC是指微软的VS编译器,我们在安装Visual Studio的时候会自动附带上这个编译器,可以编译C/C++等语言,但是这个编译器最好还是和VS的工具搭配起来使用会更好些。
Clang是MacOS中的编译器,我们在安装Xcode的时候会附带上这个,MingGW是指是Minimalist GNU on Windows的缩写。它是一个可自由使用和自由发布的Windows特定头文件和使用GNU工具集导入库的集合,允许你在GNU/Linux和Windows平台生成本地的Windows程序而不需要第三方C运行时库。
在linux或者Mac中,使用make和cmake搭配编译器来编译我们的源文件,但是在windows中,有两个选项,一是用mingw-w64中提供的编译器和构造工具make搭配cmake来进行编译(这个时候应该使用VScode作为代码的编辑器),或者利用windows中原生的VS携带的编译器来进行编译,在使用cmake的时候会自动检测vs的编译器生成适合于VS的生成文件(这个时候应该用Visual Studio),我们经常使用的Opencv的官方编译版,只提供利用VS进行编译后的window版本,如果想要使用Mingw64则必须自己下载源代码去编译。
而在linux中我们最常见的编译器就是GCC了,这个也无需多言。
基本设置
如果我们仅仅是将源代码放到一个文件夹中,然后想编译一边,最简单的设置就是:
# 最小Cmake版本,可以灵活更改 cmake_minimum_required(VERSION 3.13) # 项目名称 project(Cars) # 设置编译的C++标准 set(CMAKE_CXX_STANDARD 11) # 将此目录内的所有源文件都扫面一遍 file(GLOB Sources *.cpp ) file(GLOB Includes *.h ) # 编译出可执行文件(一般来说都是这样) add_executable(Cars ${Sources} ${Includes})
这样设置好cmake之后,执行cmake命令就可以将一个目录内的所有文件编译一遍了。
实际中的使用
实际中我们不可能将所有的文件都放到一起,这样会显得很乱,因此我们总会将一些头文件和源文件放在不同的文件夹中,甚至多级嵌套也是有可能,这样如果我们还想要将这些文件编译的话就需要在Cmakelist中指定这些文件夹的位置。
当然我们可能也需要在工程中链接一些其他的动态链接库,然后一起编译,这些都是需要通过cmake去显式指定的。
# 当于g++选项中的-I参数的作用,也相当于环境变量中增加路径到CPLUS_INCLUDE_PATH变量的作用,也就是编译包含的目录 include_directories(${PROJECT_SOURCE_DIR}/include) # 添加需要链接的库文件路径,注意这里是全路径 LINK_LIBRARIES("/usr/local/cuda/lib64/libcublas.so.9.0") # Adds -D define flags to the compilation of source files. add_definitions("-DONNX_NAMESPACE=${ONNX_NAMESPACE}") # 新添加一个目录位置,编译这个目录中所有的内容,一般这个目录中也会包含CMakeLists文件 add_subdirectory(third_party/onnx EXCLUDE_FROM_ALL)
find_pachage
这个命令是cmake中经常使用的命令,如果我们想在cmake中使用一些其他的大型开源项目(编译好的),例如OpenCV,在我们将OpenCV编译好之后,如果我们想使用它,我们就可以在cmake中添加:
find_package(OpenCV REQUIRED) message(STATUS "OpenCV library status:") message(STATUS " version: ${OpenCV_VERSION}") message(STATUS " libraries: ${OpenCV_LIBS}") message(STATUS " include path: ${OpenCV_INCLUDE_DIRS}") add_executable(example main.cpp) target_link_libraries(example ${OpenCV_LIBS})
find_package会自动帮我们找到需要链接OpenCV的配置信息,如果提示找不到,我们则需要在CMAKE的生成命令中添加-DCMAKE_PREFIX_PATH=/path/to/opencv
。
变量设置
cmake中有很多已经定义好的默认变量,我们要知道其代表什么并且可以使用它们。这里假设我们的项目路径在/project
中,而编译后文件的存放位置为/project/build
。
- PROJECT_SOURCE_DIR : 当前项目的路径,即为
/project
- PROJECT_BINARY_DIR : 项目编译后存放的路径
/project/build
- CMAKE_CURRENT_BINARY_DIR :与PROJECT_BINARY_DIR基本相同
- CMAKE_CURRENT_SOURCE_DIR :与PROJECT_SOURCE_DIR基本相同
在cmake中也可以利用一些循环方法设置变量:
list(APPEND GPU_ARCHS 51 61 75 ) foreach(arch ${GPU_ARCHS}) set(GENCODES "${GENCODES} -gencode arch=compute_${arch},code=sm_${arch}") endforeach()
或者采用set命令来设置相关的目录信息:
# 这里设置 PROJECT_OUTPUT_DIR 为 ${PROJECT_BINARY_DIR}/${CMAKE_SYSTEM_PROCESSOR} 其中后者两个参量是cmake的默认变量 set(PROJECT_OUTPUT_DIR ${PROJECT_BINARY_DIR}/${CMAKE_SYSTEM_PROCESSOR})
额外编译设置
-fPIC
# 设置编译的参数 -fPIC set(CMAKE_POSITION_INDEPENDENT_CODE ON)
-fPIC
作用于编译阶段,告诉编译器产生与位置无关代码(Position-Independent Code),则产生的代码中,没有绝对地址,全部使用相对地址,故而代码可以被加载器加载到内存的任意位置,都可以正确的执行。这正是共享库所要求的,共享库被加载时,在内存的位置不是固定的。
configure_file
命令的本意为Copy a file to another location and modify its contents.
,这个命令一般用于使用模板代码生成实际使用的代码。
config.cmake
如果需要我们的CMakeLists有一定的自由配置,比如,我们需要开启CUDA的支持,或者关闭某个功能。如果功能项比较多的话,每次增加功能或者修改,直接在CMakeLists中写一堆代码命令会很麻烦。
在这种情况下的话,最好是另外创建一个名为config.cmake
的文件,这个文件中填写了我们的配置信息(举个例子):
# Possible values: # - ON: enable CUDA with cmake's auto search # - OFF: disbale CUDA # - /path/to/cuda: use specific path to cuda toolkit set(USE_CUDA ON) # Whether enable RPC runtime set(USE_RPC ON) # Whether build with LLVM support # Requires LLVM version >= 4.0 # # Possible values: # - ON: enable llvm with cmake's find search # - OFF: disbale llvm # - /path/to/llvm-config: enable specific LLVM when multiple llvm-dev is available. set(USE_LLVM /home/prototype/Downloads/clang+llvm-7.0.1-x86_64-linux-gnu-ubuntu-16.04/bin/llvm-config)
然后我们在CmakeList中使用这个配置文件即可:
if(EXISTS ${CMAKE_CURRENT_BINARY_DIR}/config.cmake) include(${CMAKE_CURRENT_BINARY_DIR}/config.cmake) else() if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/config.cmake) include(${CMAKE_CURRENT_SOURCE_DIR}/config.cmake) endif() endif()
参考链接
https://blog.csdn.net/a_ran/article/details/41943749
https://blog.csdn.net/arackethis/article/details/43488021
https://www.cnblogs.com/foohack/p/3877276.html
https://www.cnblogs.com/x_wukong/p/4833578.html
https://www.cnblogs.com/oxspirt/p/6847438.html
https://www.cnblogs.com/x_wukong/p/4833578.html
https://cmake.org/cmake-tutorial/
https://www.cnblogs.com/yxysuanfa/p/7383800.html
https://baijiahao.baidu.com/s?id=1594739596254836207&wfr=spider&for=pc