CMakeLists基础
- 命令
-
- cmake中预定义变量
- 环境变量
- 系统信息预定义变量
- 开关选项
- cmake_minimum_required
- project()
- add_executable()
- message()
- include()
- set()
- add_library()
- target_link_libraries()
- find_package()
- configure_file()
- include_directories()
- option()
- add_definitions()
- aux_source_directory()
- install()
- write_basic_package_version_file()
- cmake
- CMakeLists命令
官方文档:
CMake Reference Documentation — CMake 3.28.0-rc1 Documentation
简介
- CMake是一个跨平台的编译(Build)工具,可以用简单的语句来描述所有平台的编译过程。
- 能够输出各种各样的makefile或者project文件,能测试编译器所支持的C++特性,类似UNIX下的automake。
- CMake 的组态档取名为 CMakeLists.txt。Cmake 并不直接建构出最终的软件,而是产生标准的建构档(如 Unix 的 Makefile 或 Windows Visual C++ 的projects/workspaces),然后再依一般的建构方式使用。这使得熟悉某个集成开发环境(IDE)的开发者可以用标准的方式建构他的软件,这种可以使用各平台的原生建构系统的能力是 CMake 和 SCons 等其他类似系统的区别之处。
- 高级编译配置工具
- 可执行文件就是exe 动态链接库就是dll 静态链接库就是.lib
- cmakes是生成跨平台工程的工具,比如可以为工程生成.sln文件在Visual Studio上打开,也可以生成.pro文件在QtCreator上打开。
- CMakeLists.txt中顺序可以改变,可以理解为 CMakeLists.txt 并不是顺序执行的。它相当于一系列的声明。假如我们有一个深度学习框架的部分工程列表,里面有超过40个互相调用的工程共同组成,一些用于生成库文件,一些用于实现逻辑功能。他们之间的调用关系复杂而严格,如果我想在这样复杂的框架下进行二次开发,显然只拥有它的源码是远远不够的,还需要清楚的明白这几十个项目之间的复杂关系,在没有原作者的帮助下进行这项工作几乎是不可能的。即使是原作者给出了相关的结构文档,对新手来说建立工程的过程依旧是漫长而艰辛的,因此CMake的作用就凸显出来了。原作者只需要生成一份CMakeLists.txt文档,框架的使用者们只需要在下载源码的同时下载作者提供的CMakeLists.txt,就可以利用CMake,在”原作者的帮助下“进行工程的搭建。
- cmakeLists.txt代码的编写过程中,宏定义变量名需要区分大小写 。
makefile
简介:或许很多Winodws的程序员都不知道这个东西,因为那些Windows的IDE都为你做了这个工作,但我觉得要作一个好的和professional的程序员,makefile还是要懂。这就好像现在有这么多的HTML的编辑器,但如果你想成为一个专业人士,你还是要了解HTML的标识的含义。特别在Unix下的软件编译,你就不能不自己写makefile了,会不会写makefile,从一个侧面说明了一个人是否具备完成大型工程的能力。因为,makefile关系到了整个工程的编译规则。一个工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中,makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作,因为makefile就像一个Shell脚本一样,其中也可以执行操作系统的命令。makefile带来的好处就是——“自动化编译”,一旦写好,只需要一个make命令,整个工程完全自动编译,极大的提高了软件开发的效率。make是一个命令工具,是一个解释makefile中指令的命令工具,一般来说,大多数的IDE都有这个命令,比如:Delphi的make,Visual C++的nmake,Linux下GNU的make。可见,makefile都成为了一种在工程方面的编译方法。
为什么使用makefile:
对于一个大型软件,其编译、维护是一个复杂而耗时的过程。它涉及到大量的文件、目录,这些文件可能是在不同的时间、由不同的人、在不同的地方分别写的,其中一些是程序,有些是数据,有些是文档,有些是衍生文件。甚至参与开发的人员也不一定清楚所有文件的细节,包括如何处理它们。此外,构成软件的文件数目可能达到成百上千,甚至成千上万个,开发过程中当修改了少量几个文件后,往往只需要重新编译、生成少数几个文件。有效地描述这些文件之间的依赖关系以及处理命令,当个别文件改动后仅执行必要的处理,而不必重复整个编译过程,可以大大提高软件开发的效率。
CMake和makefile
Cmake是用来makefile的一个工具:读入所有源文件之后,自动生成makefile。
CMakeLists命令
确定cmake最低版本需求
cmake_minimum_required(VERSION 3.0.0)
表示指定运行此配置文件所需的 CMake 的最低版本;所以看到其他的CMakeList文件没有写这个也是正常的.但是还是推荐写,是因为避免引起cmake不同版本之间构建错误的问题.
确定工程名
project(projectname [C++] [C] [Java])
你可以用这个指令定义工程名称,并可指定工程支持的语言,支持的语言列表是可以忽略的,默认情况表示支持所有语言。所以我们常见使用的时候,就直接使用project(projectname) 就够了。
这个不是必须,但是最好写一下,这一行会引入两个变量XXX_BINARY_DIR (二进制文件保存路径)和 XXX_SOURCE_DIR(源代码保存路径)
set 指令
set (VAR [VALUE] [CACHE TYPE DOCSTRING [FORCE]])
- set 指令可以用来显式的定义变量
- 显式定义使用SET指令构建自定义变量,比如:SET(HELLO_SRC main.c)就可以通过${HELLO_SRC}来引用这个自定义变量了。
实例:
set(SRC_LIST main.c)
set(SRC_LIST main.c t1.c t2.c)
变量引用方式
使用 进行变量的引用;在 I F 等语句中 , 是直接使用变量名而不通过 {}进行变量的引用;在IF等语句中,是直接使用变量名而不通过 进行变量的引用;在IF等语句中,是直接使用变量名而不通过{}取值。
添加需要的库
set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} "/usr/local/share/OpenCV")
find_package(OpenCV 3.2.0 REQUIRED)
find_package令CMake搜索所有名为Find.cmake的文件,3.2.0 REQUIRED给出需要的具体版本,以避免一台电脑安装了多个版本opencv而造成不必要的错误。通常情况下,通过设置CMAKE_PREFIX_PATH来设置CMake搜索路径,通常情况下不加也可以,但考虑到代码的可移植性,最好还是对搜索路径进行对应设置。
set()
具有统一命名的作用,set作用为将第一个字符之后的字符命名为第一个字符,下面的例子表示将add.c与sub.c用SOURCES表示,即SOURCES = [add.c, sub.c],add.c与sub.c会自动形成一个数组,赋给变量SOURCES
set(SOURCES add.c sub.c)
添加需要的头文件
include_directories(include)
include_directories(${OpenCV_INCLUDE_DIRS})
include_directories(/usr/local/cuda-8.0/include/)
如上,将头文件所在路径写在括号内即可(上例中将头文件放在了include文件夹中),而需要的一些库的头文件可以如2行变量的形式,也可以如3行直接给出库头文件所在的位置。
确定编译语言
以使用c++为例,可以用set来设定
set(CMAKE_CXX_STANDARD 11)
也可以通过add_definitions来设定
add_definitions(-std=c++11)
设定变量
ADD_DEFINITIONS(-DGPU -DCUDNN)
如darknet中代码编译需要define变量GPU,CUDNN,OPENCV等,则用该语句进行定义
添加源代码
set(SRC ${PROJECT_SOURCE_DIR}/test.cpp)
通过设定SRC变量,将源代码路径都给SRC,如果有多个,可以直接在后面继续添加:
set(SRC
${PROJECT_SOURCE_DIR}/src/detector.cpp
${PROJECT_SOURCE_DIR}/src/demo.cpp
${PROJECT_SOURCE_DIR}/test.cpp
)
编译动态库并链接库文件
link_directories(${PROJECT_SOURCE_DIR})
add_library(plate_recognition SHARED ${SRC})
target_link_libraries(plate_recognition ${OpenCV_LIBS})
target_link_libraries(plate_recognition -llianghao -lpthread -lm -lstdc++)
- add_library为生成库文件,SHARED为生成动态库,STATIC为生成静态库,前面的plate_recognition为生成的文件名,如上生成的动态库为libplate_recognition.so,最后${SRC}为源文件路径。
- target_link_libraries为链接需要的库,plate_recognition为需要进行链接的文件名,后面接需要链接的库,如第三行链接了opencv。如果需要链接其他的动态库,-l后接去除lib前缀和.so后缀的名称,以链接liblianghao.so为例,-llianghao。
生成可执行文件
link_directories(${PROJECT_SOURCE_DIR})
add_executable(Test ${SRC})
target_link_libraries(Test ${OpenCV_LIBS})
target_link_libraries(Test -llianghao -lpthread -lm -lstdc++)
- add_executable表示生成可执行文件,Test为生成的可执行文件名,后接源文件路径。
常用变量
CMAKE_BINARY_DIR
PROJECT_BINARY_DIR
< projectname >_BINARY_DIR
这三个变量指代的内容是一致的,如果是in-source编译,指得就是工程顶层目录;如果是out-of-source编译,指的是工程编译发生的目录。PROJECT_BINARY_DIR跟其它指令稍有区别,目前可以认为它们是一致的。
CMAKE_SOURCE_DIR
PROJECT_SOURCE_DIR
< projectname >_SOURCE_DIR
这三个变量指代的内容是一致的,不论采用何种编译方式,都是工程顶层目录。也就是在in-source编译时,他跟CMAKE_BINARY_DIR等变量一致。PROJECT_SOURCE_DIR跟其它指令稍有区别,目前可以认为它们是一致的。(out-of-source build与in-source build相对,指是否在CMakeLists.txt所在目录进行编译。)
CMAKE_CURRENT_SOURCE_DIR
当前处理的CMakeLists.txt所在的路径,比如上面我们提到的src子目录。
CMAKE_CURRRENT_BINARY_DIR
如果是in-source编译,它跟CMAKE_CURRENT_SOURCE_DIR一致;如果是out-of-source编译,指的是target编译目录。使用ADD_SUBDIRECTORY(src bin)可以更改这个变量的值。使用SET(EXECUTABLE_OUTPUT_PATH <新路径>)并不会对这个变量造成影响,它仅仅修改了最终目标文件存放的路径。
CMAKE_CURRENT_LIST_FILE
输出调用这个变量的CMakeLists.txt的完整路径
CMAKE_CURRENT_LIST_LINE
输出这个变量所在的行
CMAKE_MODULE_PATH
这个变量用来定义自己的cmake模块所在的路径。如果工程比较复杂,有可能会自己编写一些cmake模块,这些cmake模块是随工程发布的,为了让cmake在处理CMakeLists.txt时找到这些模块,你需要通过SET指令将cmake模块路径设置一下。比如SET(CMAKE_MODULE_PATH,${PROJECT_SOURCE_DIR}/cmake)这时候就可以通过INCLUDE指令来调用自己的模块了。
EXECUTABLE_OUTPUT_PATH
新定义最终结果的存放目录
LIBRARY_OUTPUT_PATH
新定义最终结果的存放目录
PROJECT_NAME
返回通过PROJECT指令定义的项目名称
指定include路径
#${CMAKE_CURRENT_SOURCE_DIR}表示当前文件目录,是cmake内置变量
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/add/)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/sub/)
Directory Paths
CMake语法指定了许多变量,可用于帮助您在项目或源代码树中找到有用的目录
ALL_BUILD
CMake 会自动生成 ALL_BUILD, ZERO_CHECK。
-
ZERO_CHECK
该目标会检查生成工程的 CMake 配置文件( CMakeLists.txt )是否更新。如更新,将运行 CMake 重新生成工程文件。
如果确信 CMakeLists.txt 不会被更新,或者希望手工运行 CMake 重新生成工程文件,可以在 CMakeLists.txt 配置文件中添加 set(CMAKE_SUPPRESS_REGENERATION FALSE) 命令, ZERO_CHECK 目标将不会生成。 -
ALL_BUILD
该目标会导致工程中所有项目被构建,类似 Visual Studio 的 Build All 或者 make 的 make all命令。
命令
CMakeLists.txt 的语法比较简单,由命令、注释和空格组成,其中命令是不区分大小写的。指令是大小写无关的,参数和变量是大小写相关的。但推荐全部使用大写指令。符号 # 后面的内容被认为是注释。命令由命令名称、小括号和参数组成,参数之间使用空格进行间隔。
cmake中预定义变量
PROJECT_SOURCE_DIR 工程的根目录PROJECT_BINARY_DIR 运行cmake命令的目录,通常是${PROJECT_SOURCE_DIR}/buildCMAKE_INCLUDE_PATH 环境变量,非cmake变量CMAKE_LIBRARY_PATH 环境变量CMAKE_CURRENT_SOURCE_DIR 当前处理的CMakeLists.txt所在的路径CMAKE_CURRENT_BINARY_DIR target编译目录使用ADD_SURDIRECTORY(src bin)可以更改此变量的值SET(EXECUTABLE_OUTPUT_PATH <新路径>)并不会对此变量有影响,只是改变了最终目标文件的存储路径CMAKE_CURRENT_LIST_FILE 输出调用这个变量的CMakeLists.txt的完整路径CMAKE_CURRENT_LIST_LINE 输出这个变量所在的行CMAKE_MODULE_PATH 定义自己的cmake模块所在的路径SET(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake),然后可以用INCLUDE命令来调用自己的模块EXECUTABLE_OUTPUT_PATH 重新定义目标二进制可执行文件的存放位置LIBRARY_OUTPUT_PATH 重新定义目标链接库文件的存放位置PROJECT_NAME 返回通过PROJECT指令定义的项目名称CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS 用来控制IF ELSE语句的书写方式
环境变量
- 使用 E N V N A M E 可以调用系统的环境变量,比如: M E S S A G E ( S T A T U S “ H O M E d i r :” ENV{NAME}可以调用系统的环境变量,比如:MESSAGE(STATUS “HOME dir:” ENVNAME可以调用系统的环境变量,比如:MESS