CMake学习
文章目录
一、CMake是一款自动生成本地化Makefile的工具
是全自动完成代码编译、链接、打包的整个过程,同时管理不同组件和第三方库。
其编译流程如下图:
- cmake就是一行行执行的,并没有跳转。
二、预处理和编译
单用g++:
- 使用预处理器展开头文件,宏替换,去掉注释:
g++ -E ??.cpp -o ??.i
- 使用编译器对文件进行编译:
g++ -S ??.i -o ??.s
- 使用汇编器对文件进行汇编(生成机器语言):
g++ -c ??.s -o ??.o
- 调用链接器对程序需要调用的库进行链接:
g++ ??.o ???.o ........ -o ?
但是实际上是在makefile中保存g++命令,并用make启用调用,而cmake就是自动化生成makefile/sln(windows)。
所以其实可以先用cmake ..
命令生成makefile,再用make
命令进行g++步骤/使用sln。
如果想要平台统一命令,就cmake ..
,再用cmake --build .
生成可执行文件等。
1.预处理:inclue,define,if,ifdef等等
- 其中include就是打开指定文件,并把文件的所有内容copy到包含的文件中。
- define就是搜索,直接替换。
- if预处理语句让我们依据特定条件包含或剔除代码,例如if 0和endif中间的代码,在处理include时,将不会被拷贝。
- 标记解释
- 解析
- 创建抽象语法树(代码的表达),将代码转换成constant data/常数资料/instruction指令/机器语言。
- 产生代码,cpu执行
2.编译
- 首先编译器将会把每一个cpp文件编译成一个中继格式obj(一行行编译成二进制,计算机只能识别二进制),所以这些cpp也被称为translation unity/编译单元(但cpp只有在不互相include的时候与编译单元相等)。
- 在c++中,文件不代表任何指定的东西,只要我告诉编译器,甚至可以实现将.h文件当作cpp来编译。
- 声明:
三、编写CMakeLists构建成项目
添加一个子目录并构建/跳转该子目录
- cmake可以去找别的cmake:添加一个子目录并构建/跳转该子目录.
类似vs中项目的概念,例如项目1可以生成可执行文件,项目2可以生成静态库;这两个项目都可以处于同一个工程中。
# cmakelists.txt中
# cmake默认为可以找到当前路径的文件(cmakelists当前所在文件夹下)
cmake_minimum_required(VERSION 3.0)
project(工程名)
add_subdirectory (source_dir [binary_dir] [EXCLUDE_FROM_ALL])
。。。
- source_dir,必选参数,该参数指定一个子目录,子目录下应该包含CMakeLists.txt文件和代码文件。
- binary_dir,可选参数,用于存放输出文件。默认的输出目录使用source_dir。
- EXCLUDE_FROM_ALL,可选参数。当指定了该参数,则子目录下的目标不会被父目录下的目标文件包含进去,父目录的CMakeLists.txt不会构建子目录的目标文件,必须在子目录下显式去构建。例外情况:当父目录的目标依赖于子目录的目标,则子目录的目标仍然会被构建出来以满足依赖关系(例如使用了target_link_libraries)。
同理:
- 可以使用相对路径法
./ 代表当前路径
…/ 代表上一级路径
#下面写法含义相同
add_subdirectory(lesson1_1)
add_subdirectory(./lesson1_1)
- 可以使用环境变量
$ENV{JAVA_HOME}
原链接
1.写:
set( ENV{PATH} /home/martink )
2.判断:
if(NOT DEFINED ENV{JAVA_HOME})
# 没有找到JAVA_HOME环境变量
message(FATAL_ERROR "not defined environment variable:JAVA_HOME")
#不能用if(ENV{JAVA_HOME})形式来判断是否定义
#但可以用if($ENV{JAVA_HOME})
3.读+$
add_subdirectory(&ENV{PATH}\\source_dir [binary_dir] [EXCLUDE_FROM_ALL])
配置环境
思路还是和在vs中一样:
1.先考虑可见性,能通过编译;
-
通用方法:
-
使用类似vs中附加包含目录的方法配置编译环境:
# 在cmakelists中
# 将指定目录添加到编译器的头文件搜索路径之下
include_directories ([AFTER|BEFORE] [SYSTEM] dir1 [dir2 ...])
//但是不推荐使用,尤其是在最上层的cmakelists中使用,可能会导致过度依赖。
推荐使用:
target_include_directories(<target> [SYSTEM] [AFTER|BEFORE]
<INTERFACE|PUBLIC|PRIVATE> [items1...]
[<INTERFACE|PUBLIC|PRIVATE> [items2...] ...])
- PRIVATE 表示只应添加到 target 。
- PUBLIC 表示应添加到 target 和任何链接到target 的对象中。
- INTERFACE 用于不应添加到 target 但应添加到任何链接到 target 的对象。
cmake中很多关于target指定的接口:
2.再考虑链接性之对编译单元的链接,cmake中可以使用add_executable来指定可执行文件所需要的main和cpp文件。
编译成可执行文件
- 有main的函数才会有可执行文件。
此时如果选用vs的组件包将会自动生成由
project(工程名)
命名的工程名.sln
,并且生成add_executable(可执行程序名 源文件名称)
指定的可执行文件可执行程序名.exe
。
1.普通可执行目标文件
这里源文件只需要加入c/cpp文件,因为copy头文件是在预处理阶段就完成了,即使不在这里也会执行。
add_executable(<可执行文件名称> <源代码文件1> <源代码文件2> ...)
- 可以通过
target_sources
继续为可执行文件添加源文件,要求是可执行目标target已经通过add_executable或add_library定义过了。
target_sources(<target>
<INTERFACE|PUBLIC|PRIVATE> [items1...]
[<INTERFACE|PUBLIC|PRIVATE> [items2...] ...])
- 例子
add_executable(target main.cpp)
target_sources(target test.cpp)
target_sources(target
PRIVATE
foo.cpp
foo_p.cpp
foo_p.h
PUBLIC
foo.h
)
- PRIVATE 表示这些源只应添加到 target 。
- PUBLIC 表示这些源应添加到 target 和任何链接到target 的对象中。
- INTERFACE 用于不应添加到 target 但应添加到任何链接到 target 的对象的源。
2.导入可执行目标文件
add_executable(<targetname> IMPORTED [GLOBAL])
- 将工程外部的可执行目标导入进来,不会有任何构建可执行目标文件的动作。
- 如果不指定GLOBAL,则可执行目标文件的范围为文件所创建的目录及其子目录;GLOBAL则将范围扩大到整个工程。
- IMPORTED选项指定后为true;在工程内构建的可执行目标文件的属性IMPORTED为false。
3.别名可执行目标文件
add_executable(<name> ALIAS <target>)
# 例子
add_executable(runtest main.cpp)
add_executable(test_name ALIAS runtest )
4.给变量设置值——set
原链接
用于给下面的变量设置值:
- 一般变量(Set Normal Variable)
set(<variable> <value>... [PARENT_SCOPE])
- value:可以有0个,1个或多个,当value值为空时,方法同unset,用于取消设置的值。
- PARENT_SCOPE(父作用域):不加则修改的是当前CMakeLists的变量的值;
加了则修改的是上一级目录中CMakeLists的变量的值,而当前CMakeLists中该变量的值不变。- function scope(方法作用域):set只会修改作用域内的
set(MY_CUSTOM_VAL 123)
message("根目录的 MY_CUSTOM_VAL = ${MY_CUSTOM_VAL}")
function(func1)
message(" function func1 修改变量之前 MY_CUSTOM_VAL = ${MY_CUSTOM_VAL}")
set(MY_CUSTOM_VAL 234)
message(" function func1 修改变量之后 MY_CUSTOM_VAL = ${MY_CUSTOM_VAL}")
endfunction(func1)
func1()
message("回到根目录")
message("根目录的 MY_CUSTOM_VAL = ${MY_CUSTOM_VAL}")
// 如果是 set(MY_CUSTOM_VAL 234 PARENT_SCOPE)
// 则修改的是上一级作用域变量的值
- 缓存变量(Set Cache Entry)
- 此Cache变量的值在第一次执行cmake …后保存在了一个名为CMakeCache.txt文件中;
- 删除CMakeCache.txt文件可以删掉此缓存变量。
- 而第一次执行cmake的代码中,如果需要修改/重新赋值cache变量需要指定FORCE
message("设置Cache变量之前 filePath = ${filePath}")
set(filePath ${PROJECT_SOURCE_DIR}/CMakeLists.txt CACHE FILEPATH " file path")
message("设置Cache变量之后 filePath = ${filePath}")
set(filePath ${PROJECT_SOURCE_DIR}/subsrc/CMakeLists.txt CACHE FILEPATH " file path")
message("设置Cache变量[不带FORCE]之后 filePath = ${filePath}")
set(filePath ${PROJECT_SOURCE_DIR}/cmake/custom.cmake CACHE FILEPATH " file path" FORCE)
message("设置Cache变量[带FORCE]之后 filePath = ${filePath}")
message("回到根目录")
message("根目录的 MY_CUSTOM_VAL = ${MY_CUSTOM_VAL}")
- 环境变量(Set Environment Variable)
# 设置环境变量
message("环境变量修改之前 TEXT_ENV_VAL = $ENV{TEXT_ENV_VAL}")
set(ENV{TEXT_ENV_VAL} /usr/local/text_c)
message("环境变量修改之后 TEXT_ENV_VAL = $ENV{TEXT_ENV_VAL}")
message("回到根目录")
message("根目录的 MY_CUSTOM_VAL = ${MY_CUSTOM_VAL}")
- 修改环境变量只在当前CMakeList.txt中有效,而没有真正改变操作系统中的环境变量的值;
- 在子目录的CMakeLists.txt中查看此环境变量的值,也是修改后的值。
四、静态库
- 工程的cmakelists:
cmake_minimum_required(VERSION 3.0)
project(工程名)
add_subdirectory(包含可生成可执行文件的文件夹名)
add_subdirectory(包含可生成静态库的文件夹名)
。。。。。。
1.生成静态库
add_library(<name> [STATIC | SHARED | MODULE]
[EXCLUDE_FROM_ALL]
[source1] [source2 ...])
- STATIC(静态库)/SHARED(动态库)/MODULE(模块库)
- 这里的STATIC和SHARED可不设置,通过全局的BUILD_SHARED_LIBS的FALSE或TRUE来指定;如果dll没有export任何信息,则不能使用SHARED,要标识为MODULE。
set_property() / get_property()参考
2.引入静态库
- 使用静态库(假设已经生成了.lib)的cmakelists:
1.考虑编译:使用声明或者包含头文件来保证可见性
可以使用include_directories或target_include_directories
,类似“附加包含目录”作用。
2.考虑链接:
link_directories([AFTER|BEFORE] directory1 [directory2 ...])
- 类似“包含库目录”作用。用于添加目录使链接器能在其查找库。
- 该命令只适用于在它被调用后创建的target,在add_executalbe之前使用。
- 这些目录将添加到当前CMakeLists.txt文件的LINK_DIRECTORIES目录属性中。
- 可能’产生过度依赖,推荐target_link_libraries。
add_library(<name> <SHARED|STATIC|MODULE|OBJECT|UNKNOWN> IMPORTED [GLOBAL])
add_library(baz STATIC IMPORTED)
set_target_properties(baz PROPERTIES
IMPORTED_LOCATION_RELEASE ${CMAKE_CURRENT_SOURCE_DIR}/libbaz.a
IMPORTED_LOCATION_DEBUG ${CMAKE_CURRENT_SOURCE_DIR}/libbazd.a)
- 这种用法直接导入已经生成的库,cmake不会给这类library添加编译规则。
IMPORTED_LOCATION_<CONFIG>
,其中的可以是DEBUG/RELEASE或其他。主要是用于标明library在硬盘上的位置。
target_link_libraries(<target> <PRIVATE|PUBLIC|INTERFACE> <item>... [<PRIVATE|PUBLIC|INTERFACE> <item>...]...)
- 用于将一个或多个库链接到特定的目标上,如可执行文件或库。这个命令非常灵活,可以用来指定目标所需的所有依赖项,包括系统库、第三方库以及你自己的其他目标。
- target:目标名称,可以是通过 add_executable 或 add_library 定义的可执行文件或库的名称。
- item1, item2, …:可以是以下类型之一:
1.库的名称(普通库),通常是系统自带的标准库,需要可见(例如使用了link_directories)。
2.变量:指向库路径或名称的变量,一般用find_package找到的库,例如 ${ARMADILLO_LIBRARIES}。
3.全路径:指定要链接的库的完整路径,一般是自定义库,例如 /usr/lib/libexample.lib或dll。
五、动态库
1.生成动态库
add_library(<name> [STATIC | SHARED | MODULE]
[EXCLUDE_FROM_ALL]
[source1] [source2 ...])
- STATIC(静态库)/SHARED(动态库)/MODULE(模块库)
- 这里的STATIC和SHARED可不设置,通过全局的BUILD_SHARED_LIBS的FALSE或TRUE来指定;如果dll没有export任何信息,则不能使用SHARED,要标识为MODULE。
- 只有dll有export任何信息,即包含关键字
_declspec(dllexport)
,在vs开发环境下才会生成与dll对应的lib文件。
2.添加预处理宏
target_compile_definitions(<target>
<INTERFACE|PUBLIC|PRIVATE> [items1...]
[<INTERFACE|PUBLIC|PRIVATE> [items2...] ...])
add_definitions(-D<DEFINE>)
//D: 要定义的宏名称。在大多数编译器中,使用 -D 参数可以定义一个宏。
//定义全局宏,但是不推荐使用,因为这可能导致意外的依赖关系和难以调试的问题。
3.引入动态库
参考引入静态库,只不过需要将生成的dll放在程序可见位置,例如.exe目录下,或者系统环境中。
六、Find_package引入外部依赖包
find_package(<PackageName> [<version>] [REQUIRED] [COMPONENTS <components>...])
例如配置OpenCV库时,该库一般会有一个“OpenCVConfig.cmake”。
我们只需要把包含“OpenCVConfig.cmake”文件的路径放到系统变量中,再在CMakeLists.txt中使用find_package(OpenCV REQUIRED)
,当执行到这句,将自动寻找并且执行该PackageName+Config.cmake
,然后将会根据相对路径,寻找到include和lib。
七、OBJECT库——减少依赖
1.静态
- 链接普通库:
- 对象库的生成:
add_library(<name> OBJECT <sources>...)
- 使用:
add_library(... $<TARGET_OBJECTS:objlib> ...)
add_executable(... $<TARGET_OBJECTS:objlib> ...)
- 例子
如果add链接了common的对象库,则add.lib提供出去里面是包含common库的内容的,能够直接用的。
以此类推:
- PUBLIC 表示应添加到 target 和任何链接到target 的对象中,
target_include_directories
同理。
此时:
2.动态
以此类推,add,sub都像common一样做,就可以最终合成calculator的动态库:
原文地址:https://blog.csdn.net/qq_42491346/article/details/142922105
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!