CMake命令
cmake_minimum_required
指定cmake的最低版本,可选项, 不指定可能会有如下警告:
CMake Warning (dev) at CMakeLists.txt:1 (project):
cmake_minimum_required() should be called prior to this top-level project()
call. Please see the cmake-commands(7) manual for usage documentation of
both commands.
This warning is for project developers. Use -Wno-dev to suppress it.
示例:
cmake_minimum_required(VERSION 3.2.0)
project
指定项目名称
示例:
project(DEMO
VERSION 1.0.0
DESCRIPTION "This is a sample project"
HOMEPAGE_URL "https://example.com"
LANGUAGES CXX
)
add_executable
指定可执行文件的名称,语法格式 add_executable(<target_name> source1 … sourceN)
示例:
add_executable(out.exe main.cpp utils.cpp)
注意:此命令前需要使用project设置好项目名称,输入只需要包含cpp即可
set
定义变量, 语法格式:
set(variable [value] [value] ...)
示例:
set(SRC_LIST add.cpp sub.cpp)
设置了SRC_LIST变量,读取变量的值,使用${SRC_LISAT} 来获取变量值
注意:set赋值,包含了拼接功能如:set(SRC_1 ${SRC_1} ${SRC_2} ${TEMP})
上述add_executeable命令可以改写成 add_executable(out.exe ${SRC_LSIT})
指定c++版本
通常编译的时候,可以通过指定编译选项来开启c++支持,如:g++ *.cpp -std=c++11 -0 app
在cmake中,可以通过set命令指定:
set(CMAKE_CXX_STANDARD 11) // 14 17 20等
指定可执行文件输出目录
set(HOME /home/robin/Linux/Sort)
set(EXECUTABLE_OUTPUT_PATH ${HOME}/bin)
注意:如果目录不存在,会自动创建,无需手动,生成的可执行文件是在执行make命令后,如果此处使用相对路径,那么对于./ 指的是makefile文件所在的目录。
aux_source_directory
搜索指定目录下的cpp文件,并将结果存储到变量中
语法格式:
aux_source_directory (<dir> <variable>)
示例:
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/src SRC_LIST)
add_executable(app ${SRC_LIST})
file
语法格式:file([GLOB|GLOB_RECURESE] variable files_type)
示例:
file(GLOB MAIN_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
file(GLOB MAIN_HEAD ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h)
file(GLOB MAIN_HEAD “${CMAKE_CURRENT_SOURCE_DIR}/include/*.h”)
include
从文件或模块加载和运行CMake代码。
include(<file|module> [OPTIONAL] [RESULT_VARIABLE <var>]
[NO_POLICY_SCOPE])
从给定的文件中加载和运行CMake代码。变量的读取和写入访问调用者的作用域(动态作用域)
如果使用OPTIONAL选项,则如果文件不存在,不会引发错误。如果给定了RESULT_VARIABLE,则将变量设置为已包含的完整文件名,如果失败,则设置为NOTFOUND。
如果指定的是模块而不是文件,则首先在CMAKE_MODULE_PATH中搜索名为.cmake的文件,然后在CMake模块目录中进行搜索。有一个例外情况:如果包含include()的文件自身位于CMake内置模块目录中,那么首先搜索CMake内置模块目录,然后搜索CMAKE_MODULE_PATH。
在CMake中
,include指令的作用是包含外部的CMake脚本文件,以便在当前的CMakeLists.txt文件中可以使用被包含文件中定义的变量、函数和命令。
include指令的语法如下:
include(<filename>)
包含外部的CMake脚本文件通常有以下几种常见的用途:
模块化的构建配置:
通过将通用的构建逻辑抽取到单独的CMake模块文件中,可以使项目的CMakeLists.txt文件更加简洁和可维护。通过使用include指令,将模块文件包含到主CMakeLists.txt中,可以复用这些构建逻辑。
引入外部功能:有时候,你可能需要引入某些外部的CMake脚本文件,例如用于构建测试、安装目标、导出项目等。通过使用include指令,将这些功能性的脚本文件包含到主CMakeLists.txt中,就可以使用这些功能了。
变量和宏定义的共享:使用include指令可以将定义在外部CMake脚本文件中的变量和宏带入到当前的CMakeLists.txt中。这样可以在不同的脚本文件之间共享变量和宏定义,提高代码的重用性和可维护性。
示例:
假设有两个CMake脚本文件,分别是helper_functions.cmake和main.cmake。
helper_functions.cmake:
定义一个辅助函数
function(hello_world)
message("Hello, World!")
endfunction()
main.cmake:
包含helper_functions.cmake文件
include(helper_functions.cmake)
调用辅助函数
hello_world()
通过在main.cmake中使用include指令,可以将helper_functions.cmake文件包含进来,并可以在主CMakeLists.txt中使用辅助函数hello_world()。这样,可以使构建逻辑更加模块化和可读性更高。
总结来说,CMake中的include指令的作用是将外部的CMake脚本文件引入到当前的CMakeLists.txt文件中,以实现模块化构建配置、引入外部功能和共享变量和宏定义等目的。
include_directories
包含头文件路径,类似Qt的include
include_directories(${PROJECT_SOURCE_DIR}/include)
注意:PROJECT_SOURCE_DIR为 cmakeLists.txt所在的根路径
add_library
创建静态库/动态库, 语法格式add_libaray(lib_name STATIC|SHARED 1.cpp 2.cpp …)
示例:
cmake_minimum_required(VERSION 3.0)
project(CALC)
include_directories(${PROJECT_SOURCE_DIR}/include)
file(GLOB SRC_LIST "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp")
设置动态库生成路径,由于动态库有执行权限,所有可以使用该宏
静态库则不行,可以使用通用的set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
add_library(calc SHARED ${SRC_LIST})
link_directories
链接库目录,语法格式link_directories(lib_path)
link_libraries
链接静态库库,语法格式link_libraries(math)
target_link_libraries
将目标target与指定动态库进行链接,确保target运行时,可以使用动态库中的函数和符号,语法格式target_link_libraries(target <PRIVATE/PUBLIC/INTERFACE> item)
target:动态库名称,可以是源文件,可以使动态库,也可以是可执行文件
<PRIVATE/PUBLIC/INTERFACE>: 动态库访问权限,默认PUBLIC
注意:如果动态库A以来动态库B,C,而动态库D依赖于A,那么A肯定也依赖于B,C
示例:
target_link_libraries(program dll_name)
target_link_libraries(“${PROJECT_NAME}” PRIVATE ${PROJECT_LIBRARY_NAMES})
PUBLIC:
在public后面的库会被Link到前面的target中,并且里面的符号也会被导出,提供给第三方使用。
PRIVATE:
在private后面的库仅被link到前面的target中,并且终结掉,第三方不能感知你调了啥库
INTERFACE:
在interface后面引入的库不会被链接到前面的target中,只会导出符号。
message
语法格式:
message([STATUS|WARNING|AUTHOR_WARNING|FATAL_ERROR|SEND_ERROR] “message to display” …)
// 输出一般日志信息
message(STATUS “source path: ${PROJECT_SOURCE_DIR}”)
//输出警告信息
// message(WARNING “source path: ${PROJECT_SOURCE_DIR}”)
// 输出错误信息
message(FATAL_ERROR “source path: ${PROJECT_SOURCE_DIR}”)
注意:FATAL_ERROR 会自动退出编译过程
常用宏
CMAKE常用预定义宏
PROJECT_SOURCE_DIR 使用 cmake 命令后紧跟的目录,一般是工程的根目录
PROJECT_BINARY_DIR 执行 cmake 命令的目录
CMAKE_CURRENT_SOURCE_DIR 当前处理的 CMakeLists.txt 所在的路径
CMAKE_CURRENT_BINARY_DIR target 编译目录
EXECUTABLE_OUTPUT_PATH 重新定义目标二进制可执行文件的存放位置
LIBRARY_OUTPUT_PATH 重新定义目标链接库文件的存放位置
PROJECT_NAME 返回通过 PROJECT 指令定义的项目名称
CMAKE_BINARY_DIR 项目实际构建路径, build 目录进行的构建,那么就是这个目录的路径
option
定义一个选项,可让用户构建是选择是否开启和关闭
语法格式:
option(<option_variable> <option_description> [initial_value])
示例:
定义一个选项,并设置初始值为 OFF
option(ENABLE_FEATURE “Enable a specific feature” OFF)
在构建过程中根据选项的值进行不同的处理
if(ENABLE_FEATURE)
message("The feature is enabled") # 选项值为 ON,执行一些特定的行为
else()
message("The feature is disabled") # 选项值为 OFF,执行一些默认行为
endif()
在构建时,用户可以使用 -D 参数指定选项的值,例如 -DENABLE_FEATURE=ON 来开启该功能。
设置程序版本-并在cpp中使用
CMakeLists中设置以下变量:
# The version number.
set (Tutorial_VERSION_MAJOR 1)
set (Tutorial_VERSION_MINOR 0)
# configure a header file to pass some of the CMake settings
# to the source code
configure_file (
"${PROJECT_SOURCE_DIR}/TutorialConfig.h.in"
"${PROJECT_BINARY_DIR}/TutorialConfig.h"
)
然后再工程目录下新建一个TutorialConfig.h.in,内容如下:
1.// the configured options and settings for Tutorial
2.#define Tutorial_VERSION_MAJOR @Tutorial_VERSION_MAJOR@
3.#define Tutorial_VERSION_MINOR @Tutorial_VERSION_MINOR@
上面的@Tutorial_VERSION_MAJOR@和@Tutorial_VERSION_MINOR@会被替换为CMake中的1和0。
然后再我们程序中,就可以使用 #include “TutorialConfig.h”
然后调用 Tutorial_VERSION_MAJOR 变量
find_file
语法格式:
find_file (<VAR> name1 [path1 path2 ...])
VAR 保存搜索结果的变量名,是一个完整的路径名
name1:要搜索的文件名。
path1 path2 …:可选参数,指定查找文件的路径。如果不指定路径,则默认在CMAKE_MODULE_PATH和CMAKE_PREFIX_PATH中查找。
find_file(MY_HEADER my_header.h)
if (MY_HEADER)
message("Found header file: ${MY_HEADER}")
else()
message(FATAL_ERROR "Could not find my_header.h")
endif()
此示例将在默认路径(CMAKE_MODULE_PATH和CMAKE_PREFIX_PATH)中查找名为my_header.h的文件。如果找到了该文件,则将其路径存储在变量MY_HEADER中。否则,将会出现致命错误。
find_path 查找包含指定文件的路径
find_path (<VAR> name1 [path1 path2 ...])
VAR用于存放结果路径
在CMake里想要引用外部库,最好是使用find_package。可惜要使用find_package,要有对应的FindXXX.cmake存在才可以,对于比较小众的库就不太好找。还可以使用find_library。
对于只需要头文件的库,最简单的,可以使用find_path。就是在CMakeLists.txt指定一个文件名,CMake会自动搜索,并且最后获取这个文件所在的文件夹,可以直接作为include dir。
例如stb库,https://github.com/nothings/stb,源代码里甚至没有CMakeLists.txt。毕竟全部都是头文件。这种想在CMake里使用,可以直接复制想要的文件到自己的项目里。根据情况也许更好的,使用find_path,由用户来提供stb的源代码的路径。
find_path(stb_INCLUDE_DIR stb_image.h DOC “Path to stb folder” REQUIRED)
target_include_directories(myapp PRIVATE ${stb_INCLUDE_DIR})
这样,用户在Configure的时候,需要指定stb_image.h的路径,find_path就会设置正确的包含路径,也就是stb_image.h所在的文件夹,到stb_INCLUDE_DIR里了,后面可以直接用target_include_directories来引用。
甚至可以直接把stb源代码目录加到CMAKE_PREFIX_PATH里,这样不需要再手动指定文件的路径,CMake可以直接搜索到。比直接使用set(stb_INCLUDE_DIR xxx)要好不少。
find_library
find_library 是 CMake 中的一个命令,用于查找并导入系统库或第三方库的路径。
使用 find_library 的一般格式如下:
find_library( name1 [path1 path2 …])
其中,参数的含义如下:
:变量名,用于存储找到的库的全路径。
name1:要查找的库的名称。
path1 path2 …:可选的路径列表,用于指定额外的搜索路径。
find_library 命令会按照一定的规则在指定的路径列表中查找库文件,并将找到的库的全路径存储在 变量中。一旦找到库文件,你就可以使用类似 ${} 的变量来引用该库的路径。
以下是一个示例用法:
cmake_minimum_required(VERSION 3.0)
project(MyProject)
#查找并加载名为 "mylibrary" 的库
find_library(MYLIBRARY_PATH mylibrary
PATHS "/usr/lib" "/usr/local/lib")
#输出库的全路径
message(STATUS "Library path: ${MYLIBRARY_PATH}")
#添加源文件
add_executable(MyExecutable main.cpp)
设置链接库
target_link_libraries(MyExecutable ${MYLIBRARY_PATH})
上述示例中,find_library 用于查找名为 “mylibrary” 的库,并将找到的库的全路径存储在 MYLIBRARY_PATH 变量中。之后,使用 ${MYLIBRARY_PATH} 来引用该库的路径。最后,通过 target_link_libraries 将库与可执行文件关联。
请注意,不同的操作系统和库可能具有不同的命名规则和默认搜索路径,所以在使用 find_library 时,需要根据具体的情况指定适当的路径或其他参数。
另外,你也可以使用 NO_DEFAULT_PATH 选项来告诉 CMake 不要在默认搜索路径中查找库文件,例如:find_library(MYLIBRARY_PATH mylibrary NO_DEFAULT_PATH)
有关更多详细信息和参数选项,请参考 CMake 的官方文档。
find_package 查找系统软件包
是 CMake 中的一个命令,用于查找和加载已安装的第三方库(包)的配置文件,并将其导入到项目中。
语法格式:
find_package(package_name [version] [EXACT] [QUIET] [MODULE] [REQUIRED] [COMPONENTS components…])
参数说明:
EXAC:表示要求查找的版本必须与指定的版本完全匹配
QUIET:在查找过程中不生成任何输出。
MODULE:强制CMake按模块搜索模式查找软件包。
REQUIRED:指定软件包是必需的,如果找不到,则会产生一个致命错误并中止构建过程。
COMPONENTS:指定软件包提供的组件,用于在使用多个组件的情况下查找软件包。
使用示例:
find_package(OpenCV 4.5.1 REQUIRED)
target_link_libraries(my_program PRIVATE ${OpenCV_LIBS})
CMkake官方预定义了很多依赖包的Module,存储在cmake_dir/cmake-2.7.0/Modules下,名字为
Find.cmake的文件都可以帮我们找到一个包,如以curl库为例:
我们可以看到已经有了FindCURL.cmake
find_package(CURL)
add_executable(curltest curltest.cc)
if(CURL_FOUND)
target_include_directories(clib PRIVATE ${CURL_INCLUDE_DIR})
target_link_libraries(curltest ${CURL_LIBRARY})
else(CURL_FOUND)
message(FATAL_ERROR ”CURL library not found”) #FATAL_ERROR 会自动退出
endif(CURL_FOUND)
每个模块都会预定义一下几个变量:
_FOUND # 模块是否找到
_INCLUDE_DIR// or _INCLUDES
_LIBRARY // or _LIBRARIES
对于大多数库,在使用cmake编译后,都会在指定文件夹生成.cmake文件,以便可以让cmake使用find_package
方便的找到他们
Find a package (usually provided by something external to the project), and load its package-specific details.
find_package find_libaray区别
find_package 和 find_library 是 CMake 中用于查找外部库的两个命令,它们之间存在以下区别:
功能和用途:
find_package:主要用于查找和导入已安装的第三方库包的配置文件。通常,这些配置文件由库的开发者提供或使用 CMake 自带的 FindXXX 模块进行查找。
find_library:用于查找特定的库文件,并返回其完整路径。它可以用于查找系统库或第三方库的位置。
查找方式:
find_package:使用预定义的规则和路径来查找库包的配置文件。CMake 会根据库名、版本等信息在预定的路径下查找,并加载配置文件中定义的变量和选项。
find_library:根据给定的库名和路径,在指定路径中查找和返回匹配的库文件的完整路径。
使用方式:
find_package:查找到库包的配置文件后,可以使用导入的变量和选项,简化库的引用和使用。通常,通过查找到的导入变量来指定包含路径和连接库的信息。
find_library:它返回找到库文件的完整路径(包括库文件的文件名和路径)。然后,可以使用返回的路径变量来直接链接该库。
库的访问:
find_package:通常用于查找和导入较高级别的库,提供了封装、抽象和更方便的使用方式。例如,它可以加载整个库的配置信息,并提供了库的版本控制、包含路径、链接库等信息。
find_library:主要用于直接查找和链接库文件,通常用于更底层的库操作。通过 target_link_libraries 将找到的库文件直接关联到可执行文件或目标库。
总的来说,find_package 更适合于导入和使用具有配置文件的第三方库包,而 find_library 主要用于查找和链接特定的库文件。选择使用哪个命令取决于你的具体需求以及库的提供方式和使用要求。
macro
宏是一种用于扩展CMake代码的方式。宏可以接受参数,并根据参数执行相应的操作,类似于编程语言中的宏。宏不会创建作用域,它们会在调用它们的位置进行代码替换。
宏的定义语法如下:
macro(<macro_name> [arg1 [arg2 ...]])
# 宏的具体实现代码
endmacro()
使用宏的语法如下:
<macro_name>([arg1 [arg2 ...]])
在调用宏时,宏的参数可以被传递给宏内部的CMake命令,用于执行相应的操作。
function(函数):
函数也是用于定义可重用代码块的机制,但函数与宏不同的是,函数会创建一个新的作用域。
函数在调用它的位置创建变量和状态,并在函数结束时将控制流返回给调用方。
函数的定义语法如下:
function(<function_name> [arg1 [arg2 ...]])
# 函数的具体实现代码
endfunction()
使用函数的语法和调用宏的语法相同。
在函数中,你可以使用 set() 命令在函数内部创建变量,并使用 return() 命令将结果返回给调用方。
funciton
OPTIONS
-DCMAKE_INSTALL_PREFIX=…/bin
安装目录
-DCMAKE_BUILD_TYPE=Release
编译输出类型
-DCMAKE_POSITION_INDEPENDENT_CODE=ON
添加fPic
示例:
mkdir build bin && cd build && cmake -DCMAKE_INSTALL_PREFIX=…/bin -DCMAKE_BUILD_TYPE=Release -DCMAKE_POSITION_INDEPENDENT_CODE=ON …