cmake 入门实践

GitHub - wzpan/cmake-demo: 《CMake入门实战》源码

CMake 入门实战 | HaHack

CMake Reference Documentation — CMake 3.27.6 Documentation

Key Concepts — Mastering CMake

CMake 入门与进阶_.cmake文件_行稳方能走远的博客-CSDN博客

A CMake-based buildsystem is organized as a set of high-level logical targets. Each target corresponds to an executable or library, or is a custom target containing custom commands. Dependencies between the targets are expressed in the buildsystem to determine the build order and the rules for regeneration in response to change.

cmake编写:cmake 编写的过程实际上是编程的过程,不过你需要编写的是 CMakeLists.txt(确保每个目录一个,注意文件名大小写.),使用的是”cmake 语言和语法”。

CMakeLists.txt 的语法比较简单,由命令注释空格组成,其中命令是不区分大小写的。符号 # 后面的内容被认为是注释。命令由命令名称、小括号和参数组成,参数之间使用空格进行间隔

1,变量使用 ${} 方式取值,但是在 IF 控制语句中是直接使用变量名
2,指令(参数 1 参数 2...) 参数使用括弧括起,参数之间使用空格或分号分开。

指令是大小写无关的参数和变量是大小写相关的

编译项目:在当前目录执行 cmake . ,得到 Makefile 后再使用 make 命令编译。

执行 cmake . (注意命令后面的点号,代表本目录)后,你会发现,系统自动生成了: 
CMakeFiles, CMakeCache.txt, cmake_install.cmake 等文件,并且生成了 Makefile. 
现在不需要理会这些文件的作用,以后你也可以不去理会。最关键的是,它自动生成了 Makefile. 

如果你需要看到 make 构建的详细过程,可以使用 make VERBOSE=1 或者 VERBOSE=1 
make 命令来进行构建。

多个目录,多个源文件

1. 在每个目录里创建 CMakeLists.txt 文件。在顶层目录的 CMakeLists.txt 里使用命令 add_subdirectory 指明本项目包含一个子目录 math,这样 math 目录下的 CMakeLists.txt 文件和源代码也会被处理 。 使用命令 target_link_libraries 指明可执行文件 main 需要连接一个名为 MathFunctions 的链接库 。

# CMake 最低版本号要求
cmake_minimum_required (VERSION 2.8)

# 项目信息
project (Demo3)

# 查找当前目录下的所有源文件,并将名称保存到 DIR_SRCS 变量
aux_source_directory(. DIR_SRCS)

# 添加 math 子目录
add_subdirectory(math)

# 指定生成目标 
add_executable(Demo main.cc)

# 添加链接库
target_link_libraries(Demo MathFunctions)

Add a subdirectory to the build.

add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL] [SYSTEM])

这个指令用于向当前工程添加存放源文件的子目录,并可以指定中间二进制目标二进制存放的位置。EXCLUDE_FROM_ALL 参数的含义是将这个目录从编译过程中排除,比如,工程的 example,可能就需要工程构建完成后,再进入 example 目录单独进行构建(当然,你也可以通过定义依赖来解决此类问题)。

add_subdirectory(src bin) 了将 src 子目录加入工程,并指定编译输出(包含编译中间结果)路径为 bin 目录。如果不进行 bin 目录的指定,那么编译结果(包括中间结果)都将存放在 build/src 目录(这个目录跟原有的 src 目录对应),指定 bin 目录后,相当于在编译时将 src 重命名为 bin,所有的中间结果和目标二进制都将存放在 bin 目录。

可以使用 CMake 的变量来指定可执行文件输出路径。例如,将可执行文件输出到 bin 目录:set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)

可以使用 CMake 的变量来指定库文件输出路径。例如,将静态库输出到 lib 目录:set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)

可以使用 CMake 的变量设置动态库输出路径(注意这个放在最后):
set_target_properties(dllname PROPERTIES RUNTIME_OUTPUT_DIRECTORY  ${CMAKE_BINARY_DIR/lib)

CMake 可以通过 set_target_properties 函数来指定一个文件的输出路径。这个函数用于设置目标属性,包括输出路径、编译选项等。

假设我们需要将一个名为 mylib.so 的共享库文件输出到 build/lib 目录下,可以使用以下代码:

add_library(mylib SHARED mylib.cpp)
# 指定mylib.so的输出路径
set_target_properties(mylib PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/lib)

类似地,如果你想要将一个可执行文件 output.exe 输出到 build/bin 目录下,可以使用以下代码:

add_executable(output main.cpp)
# 指定output.exe的输出路径
set_target_properties(output PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bin)

注意:如果你使用了多个target,你需要分别对每个target使用set_target_properties进行配置。

2. 子目录中的 CMakeLists.txt:使用命令 add_library 将 src 目录中的源文件编译为静态链接库。

# 查找当前目录下的所有源文件
# 并将名称保存到 DIR_LIB_SRCS 变量
aux_source_directory(. DIR_LIB_SRCS)

# 生成链接库
add_library (MathFunctions ${DIR_LIB_SRCS})

一 、add_library 介绍
使用该命令可以在Linux下生成(静态/动态)库so或者.a文件,Windows下就是dll与lib文件,它有两种命令格式:

1. Normal Libraries 普通库,add_library命令的格式为:

add_library(<name> [STATIC | SHARED | MODULE]
            [EXCLUDE_FROM_ALL]
            [<source>...])

生成一个名为 < name > 的library,注意以下规则:

  1. < name > 应该保证在一个项目中的唯一性
  2. 实际生成的 library文件名是基于平台的约定规则,比如 linux 下的 lib< name >.a, Windows下的 < name >.lib 等
  3. STATICSHARED,MODULE 用于指定创建的 library 类型
  4. STATIC库:生成 obj 文件后,将其链接成静态库,用于链接到其他 targets
  5. SHARED库:生成 obj 文件后,将其链接成动态库,用于运行时加载
  6. MODULE库:不能链接到其它 targets,但是可以用 dlopen 之类的方法在运行时动态加载。
  7. 如果没有明确指定要生成的 library 的类型到底是 STATIC,SHARED 还是 MODULE。则查看 BUILD_SHARED_LIBS 变量,如果值为 ON,则默认是 SHARED,否则默认 STATIC
  8. 对于 SHARED 和 MODULE 类型的库,POSITION_INDEPENDENT_CODE 属性自动置为ON。
  9. EXCLUDE_FROM_ALL:加了EXCLUDE_FROM_ALL 属性的 target 在默认编译的时候,不会被编译,如果要编译它们,需要手动编译。
  10. source: 构建库的文件

By default, the add_library() command defines a STATIC library, unless a type is specified.

2. 对象库,生成 obj 文件对象,该对象库只编译源文件,但不链接

add_library(<name> OBJECT [<source>...])

这种方式,只编译 source 列表的文件,但不将生成的目标文件打包或者链接为库,而是在其他 add_library() 或者 add_executable() 生成目标的时候,可以使用形如 $<TARGET_OBJECTS:name> 表达式将对象库作为源引入。其中,name 是对象库的名称。格式如下:

add_library(... $<TARGET_OBJECTS:name> ...)
add_executable(... $<TARGET_OBJECTS:name> ...)

The OBJECT library type defines a non-archival collection of object files resulting from compiling the given source files. The object files collection may be used as source inputs to other targets by using the syntax $<TARGET_OBJECTS:name>.

add_library(archive OBJECT archive.cpp zip.cpp lzma.cpp)

add_library(archiveExtras STATIC $<TARGET_OBJECTS:archive> extras.cpp)

add_executable(test_exe $<TARGET_OBJECTS:archive> test.cpp)

这种方式不常使用,因为静态库就是把文件直接打包,这种情况直接使用静态库就行。 

二 、target_link_libraries 的介绍
指定链接给定目标和/或其依赖项时要使用的库。命名的 <tartget> 必须是由 add_executable() 或add_library() 之类的命令创建的。一般与  link_directories 连用(添加外部库的搜索路径 )

target_link_libraries 命令的格式:

target_link_libraries(<target> ... <item>... ...)

大家习惯通过 $<TARGET_OBJECTS:name> 引用 object 文件。其实 object library 与 静态库,动态库一样,都是可以设置 PUBLIC/PRIVATE 属性的,再通过 target_link_library() 命令链接。

三、target_include_directories

该命令用于为指定的目标(target)添加包含目录(include directories)。指定编译目标时需要搜索头文件的路径,以确保编译器可以找到所需的头文件,这对于创建 CMake 项目中的库和可执行文件非常有用,因为它们可能依赖于其他模块或库的头文件。

target_include_directories(<target> [SYSTEM] [AFTER|BEFORE]
  <INTERFACE|PUBLIC|PRIVATE> [items1...]
  [<INTERFACE|PUBLIC|PRIVATE> [items2...] ...])

参数说明

  1. target:要添加包含目录的目标名称。
  2. SYSTEM:可选参数,指定被包含的目录是系统目录。这会告诉编译器将这些目录视为系统的标准头文件目录。
  3. BEFORE:可选参数,指定将包含目录添加到已有的包含目录之前,而不是默认的添加到后面。
  4. INTERFACE、PUBLIC、PRIVATE:指定包含目录的可见性级别。
  • INTERFACE:表示包含目录将应用于目标及其使用该目标的其他目标。
  • PUBLIC:表示包含目录将应用于目标本身和使用该目标的其他目标
  • PRIVATE:表示包含目录仅应用于目标本身
  1. items:指定要添加的包含目录路径,可以是目录名、绝对路径或相对路径。

三、add_library 的实例

1. 文件结构:

2. 第一种 library 格式

cmake_minimum_required (VERSION 3.12.1)
 
project (Demo)
 
# 生成对象库文件
add_library(hello hello.cpp)
 
# 添加头文件目录
target_include_directories(hello PUBLIC ${CMAKE_SOURCE_DIR}/public)
 
# 生成可执行文件
add_executable(Demo main.cpp)
 
# 链接对象库
target_link_libraries(Demo hello)

3. 第二种 object 格式


cmake_minimum_required (VERSION 3.12.1)
 
project (Demo)
 
# 生成对象库文件,不链接
add_library(hello OBJECT hello.cpp)
 
# 添加头文件目录
target_include_directories(hello PUBLIC ${CMAKE_SOURCE_DIR}/public)
 
# 添加编译选项 -Wall
target_compile_options(hello PUBLIC -Wall)
 
# 生成可执行文件
add_executable(Demo main.cpp $<TARGET_OBJECTS:hello>)
 
# 添加头文件目录
target_include_directories(Demo PUBLIC ${CMAKE_SOURCE_DIR}/public)

大家习惯通过 $<TARGET_OBJECTS:name> 引用 object 文件,尤其 object 是多文件时。

也可以直接指定链接文件:

cmake_minimum_required (VERSION 3.12.1)
 
project (Demo)
 
# 生成对象库文件,不链接
add_library(hello OBJECT hello.cpp)
 
# 添加头文件目录
target_include_directories(hello PUBLIC ${CMAKE_SOURCE_DIR}/public)
 
# 添加编译选项 -Wall
target_compile_options(hello PUBLIC -Wall)
 
# 生成可执行文件
add_executable(Demo main.cpp)
 
# 链接对象库
target_link_libraries(Demo hello)

A-------------------------

add_library(archive OBJECT archive.cpp zip.cpp lzma.cpp)

add_library(archiveExtras STATIC $<TARGET_OBJECTS:archive> extras.cpp)

add_executable(test_exe $<TARGET_OBJECTS:archive> test.cpp)

B-------------------------

add_library(archive OBJECT archive.cpp zip.cpp lzma.cpp)

add_library(archiveExtras STATIC extras.cpp)
target_link_libraries(archiveExtras PUBLIC archive)

add_executable(test_exe test.cpp)
target_link_libraries(test_exe archive)

支持 gdb
让 CMake 支持 gdb 的设置也很容易,只需要指定 Debug 模式下开启 -g 选项:

set(CMAKE_BUILD_TYPE "Debug")
set(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g -ggdb")
set(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall")

之后可以直接对生成的程序使用 gdb 来调试。

自定义编译选项

CMake 允许为项目增加编译选项,从而可以根据用户的环境和需求选择最合适的编译方案。

例如,可以将 MathFunctions 库设为一个可选的库,如果该选项为 ON ,就使用该库定义的数学函数来进行运算。否则就调用标准库中的数学函数库。

# CMake 最低版本号要求
cmake_minimum_required (VERSION 2.8)

# 项目信息
project (Demo4)

# 加入一个配置头文件,用于处理 CMake 对源码的设置
configure_file (
  "${PROJECT_SOURCE_DIR}/config.h.in"
  "${PROJECT_BINARY_DIR}/config.h"
  )

# 是否使用自己的 MathFunctions 库
option (USE_MYMATH "Use provided math implementation" ON)

# 是否加入 MathFunctions 库
if (USE_MYMATH)
  include_directories ("${PROJECT_SOURCE_DIR}/math")
  add_subdirectory (math)  
  set (EXTRA_LIBS ${EXTRA_LIBS} MathFunctions)
endif (USE_MYMATH)

# 查找当前目录下的所有源文件,并将名称保存到 DIR_SRCS 变量
aux_source_directory(. DIR_SRCS)

# 指定生成目标
add_executable(Demo ${DIR_SRCS})
target_link_libraries (Demo  ${EXTRA_LIBS})
  1.  configure_file 命令用于加入一个配置头文件 config.h ,这个文件由 CMake 从 config.h.in 生成,通过这样的机制,将可以通过预定义一些参数和变量来控制代码的生成。
  2. option 命令添加了一个 USE_MYMATH 选项,并且默认值为 ON 。
  3. 根据 USE_MYMATH 变量的值来决定是否使用我们自己编写的 MathFunctions 库。
#include <stdio.h>
#include <stdlib.h>
#include "config.h"

#ifdef USE_MYMATH
  #include "math/MathFunctions.h"
#else
  #include <math.h>
#endif

编写 config.h.in 文件
上面的程序值得注意的是引用了一个 config.h 文件,这个文件预定义了 USE_MYMATH 的值。但我们并不直接编写这个文件,为了方便从 CMakeLists.txt 中导入配置,我们编写一个 config.h.in 文件,内容如下:

#cmakedefine USE_MYMATH

这样 CMake 会自动根据 CMakeLists 配置文件中的设置自动生成 config.h 文件。

设置 USE_MYMATH  选项值为 ON 。此时 config.h 的内容为:

#define USE_MYMATH

设置 USE_MYMATH  选项值为 OFF 。此时 config.h 的内容为:

/* #undef USE_MYMATH */

cmake-commands(7)

内部构建与外部构建

内部构建(in-source build) 就是直接在源代码目录里构建。相信看到生成的临时文件比您的代码文件还要多的时候,估计这辈子你都不希望再使用内部构建。

外部构建(out-of-source build)就是让编译中间文件和源代码分离。

外部编译的过程如下: 

  1. 首先,请清除工程目录中除代码原文件和 CmakeLists.txt 之外的所有中间文件,最关键的是CMakeCache.txt。
  2. 在工程目录中建立 build 目录,当然你也可以在任何地方建立 build 目录,不一定必须在工程目录中。
  3. 进入 build 目录,运行 cmake .. (注意,.. 代表父目录,因为父目录存在我们需要的 CMakeLists.txt,如果你在其他地方建立了 build 目录,需要运行 cmake <工程的全路径>),查看一下 build 目录,就会发现了生成了编译需要的 Makefile 以及其他的中间文件. 
  4. 运行 make 构建工程,就会在当前目录(build 目录)中获得目标文件 xxxx。

进入 build 目录进行外部编译

out-of-source 外部编译,一个最大的好处是,对于原有的工程没有任何影响,所有动作全部发生在编译目录。

指定编译器
可以使用 CMake 的变量来指定编译器。例如,使用 GCC 编译器:
set(CMAKE_C_COMPILER gcc)
set(CMAKE_CXX_COMPILER g++)

指定编译选项
可以使用 CMake 的变量来指定编译选项。例如,指定编译器优化选项:
set(CMAKE_CXX_FLAGS_RELEASE \O3\)

cmake 项目

CMake的一个基本概念是项目既要有 source directory 又要有 binary directory

​source directory 是 CMakeLists.txt 文件所在的位置,项目的源文件和构建所需的所有其他文件都组织在该位置下。源目录通常使用 git、subversion 或类似工具进行版本控制。

​binary directory 是创建 build 生成的所有内容的地方。它通常也被称为 build directory。CMake通常使用术语 build directory,但在开发人员中,术语 build directory 更常用。

cmake --build . 该命令的含义是:执行当前目录下的构建系统,生成构建目标。

作用: cmake --build . 效果与 make 等价,但可以自动识别适配当前平台的生成器,无需手动指定采用 mingw32-make 还是 Nmake,常用于跨平台脚本中。

注:其用于cmake ..命令之后

cmake 项目构建过程简述:

  1. 首先,使用命令行: ‘cmake <source tree>’,比如:cmake .. ,在构建目录(外部构建方式)下生成了项目文件 project files,官方文档中又叫 build tree/binary tree,这其中就包括,比如:Makefile,还有一些其他相关文件/目录/子目录;
  2. 其次,自然是对生成好的项目 (project files) 进行编译构建,使用到 'cmake --build .'
  3. 最后,--build后面的那个‘.’,指的是生成好的 build tree 的路径。 一般来说,如果明确知道系统中使用的是哪种构建器 (build generator),比如:Unix Makefiles,完全可以直接使用 make 进行项目构建。

对于这种 --build 的形式,多用于自动化脚本之中,或者IDE环境下。

注: <source tree> 指的是源文件+顶层 CMakeLists 所在的路径,cmake .. 假设了路径在上一层。通过 cmake . / cmake .. 命令创建 Makefile 文件后,一般使用 make 命令编译文件。这里的cmake --build . 就与 make 一样的效果。

为什么不直接 make,而是使用 cmake --build 形式的命令,主要是为了跨平台,使用这种形式后,不管你是使用的什么生成器,CMake 都能正确构建,否则如果使用的是 Ninja 或者其他生成器,那 make 就不生效了。

【Cmake】.cmake 文件是什么|有什么用?

.cmake 文件 是给 cmake 执行用的:cmake  -P    /path/of/xxx.cmake
Process the given cmake file as a script written in the CMake language. No configure or generate step is performed and the cache is not modified. If variables are defined using -D, this must be done before the -P argument.


.cmake 文件的作用
cmake 可以被 include,以达到复用其中函数的作用。
就像 CMakeList.txt 的库一样,加载后可以在 CMakeList.txt 中使用它的一些函数和定义?

Cmake 官方教程(翻译)-面圈网 (mianshigee.com)

Mordern CMake 简体中文版 

Introduction · Modern CMake (modern-cmake-cn.github.io)

GitHub - Modern-CMake-CN/Modern-CMake-zh_CN: CMake 教程 Modern-CMake 的简体中文翻译,中文版 Gitbook :https://modern-cmake-cn.github.io/Modern-CMake-zh_CN/ Chinese(simplified) translation of famous cmake tutorial Modern CMake. GitHub Pages : https://modern-cmake-cn.github.io/Modern-CMake-zh_CN/

------------------------------------------------------------------------------------

Download CMake

CMake基础篇---用一个小型项目了解CMake及环境构建    来自 <学习C++:C++进阶(三)CMake基础篇---用一个小型项目了解CMake及环境构建>

我们平常在Linux上用命令编译链接文件时,可能会涉及以下几项:

add_library — CMake 3.22.0 Documentation

target_link_libraries — CMake 3.22.0 Documentation

一、add_library 介绍

add_library命令用来使用指定的源文件向工程中添加一个目标库,使用该命令可以在 Linux 下生成(静态/动态)库 so 或者 .a 文件,Windows下就是 dll 与 lib 文件,它有两种命令格式。

1.1 第一种格式 :

Normal Libraries,add_library 命令的格式为:

add_library(<name> [STATIC | SHARED | MODULE]

               [EXCLUDE_FROM_ALL]

               source1 [source2 ...])

生成一个名为 <name> 的 library,注意以下规则:

  • <name> 应该保证在一个项目中的唯一性。
  • 实际生成的 library 文件名是基于平台的约定规则,比如linux下的lib<name>.a, Windows下的<name>.lib等
  • STATIC,SHARED,MODULE 用于指定创建的 library 类型。
    • STATIC库:生成obj文件后,将其链接成静态库用于链接到其他 targets
    • SHARED库:生成obj文件后,将其链接成动态库,用于运行时加载。
    • MODULE库:不能链接到其他 targets,但是可以用 dlopen 之类的方法在运行时动态加载。
  • 如果没有明确指定要生成的 library 的类型到底是 STATIC,SHARED 还是 MODULE。则查看 BUILD_SHARED_LIBS 变量,如果值为 ON,则默认是SHARED,否则默认 STATIC。
  • 对于 SHARED 和 MODULE 类型的库,POSITION_INDEPENDENT_CODE 属性自动置为 ON。
  • EXCLUDE_FROM_ALL:表明该 target 是否从默认构建 target 中排除。加了 EXCLUDE_FROM_ALL 属性的 target 在默认编译的时候,不会被编译,如果要编译它们,需要手动编译
  • source参数可以使用 generator表达式($ <…>)。构建库的源文件可以直接指定,也可以后续使用 target_sources() 指定。

generator 表达式 :Generator expressions are evaluated during build system generation to produce information specific to each build configuration.

逻辑表达式

$<BOOL : …> : 如果…是true,则为1,否则为0.

$<AND:?[,?]… > : 如果所有?都是1,则为1,否则为0.

$<EQUAL:a,b > : 如果数字a等于数字b,则为1,否则为0.

信息表达式

$<C_COMPILER_VERSION> : C compiler的版本.

$<TARGET_FILE:tgt> : 名为tgt的主文件的全路径。

输出表达式

$<0:…> : 输出空字符串,忽略…内容。

$<1:…> : 输出…的内容。

$<JOIN:list, … > :将…内容加到list上。

1.2 第二种格式 :

生成 obj 文件对象库该对象库只编译源文件,但不链接

add_library(<name> OBJECT [<source>...])

这种形式类型固定为 OBJECT,以这种方式,只编译 source 列表的文件,但不将生成的目标文件打包或者链接为库,而是在其他 add_library() 或者add_executable() 生成目标的时候,可以使用形如 $<TARGET_OBJECTS:name> 的表达式将对象库作为源引入。其中,name 是对象库的名称。格式如下:

add_library(... $<TARGET_OBJECTS:name> ...)

add_executable(... $<TARGET_OBJECTS:name> ...)

使用方式

add_library(<objlib> OBJECT [<source>...])

add_library(... $<TARGET_OBJECTS:objlib> ...)

add_executable(... $<TARGET_OBJECTS:objlib> ...)

具体示例

add_library(test_library OBJECT a.cpp b.cpp c.cpp)

add_executable(test_app main.cpp $<TARGET_OBJECTS:test_library>)

add_library(anotherlib STATIC other.cpp $<TARGET_OBJECTS:test_library>)

这种方式不常使用,因为静态库就是把文件打包,这种情况直接使用静态库就行,了解即可。

1.3 接口库

add_library(<name> INTERFACE [IMPORTED [GLOBAL]])

生成一个接口库这类库不编译任何文件,也不在磁盘上产生库文件。它有一些属性被设置,并且能够被安装和导出。通常,使用以下命令在接口目标上填充属性。

- set_property()

- target_link_library(INTERFACE)

- target_link_options(INTERFACE)

- target_include_directions(INTERFACE)

- target_compile_options(INTERFACE)

- target_compile_definitions(INTERFACE)

- target_sources(INTERFACE)

然后像其他目标一样被用作参数给 target_link_libraries()。

1.4 导入的库

add_library(<name> <SHARED|STATIC|MODULE|UNKNOWN> IMPORTED [GLOBAL])

用来导入已经存在的库,Cmake 也不会添加任何编译规则给它。

  • 此类库的标志就是有 IMPORT 属性,导入的库的作用域为创建它的目录及更下级目录。但是如果有 GLOBE 属性,则作用域被拓展到全工程。
  •  一个 IMPORTED target 表示一个已经存在的依赖,且不可变的存在,无法修改它。
  • 导入的库的类型必须是 STATIC,SHARED,MODULE,UNKNOWN中的一种。对于 UNKNOW 类型,不需要知道类型就可使用的。从工程外部引入一个库,使用IMPORTED_LOCATION 属性确定库文件的在磁盘上的完整路径。

关于 imported library 的详细信息,是通过设置那些以 IMPORTED_INTERFACE_ 开头的属性来指定的。其中最重要的属性是:IMPORTED_LOCATION,这个属性用于指定磁盘上主库文件的位置。

举个例子:

add_library(PocoLib SHARED IMPORTED GLOBAL)

#It's important to specify the full path to the library you want to import

set_target_properties(PocoLib PROPERTIES IMPORTED_LOCATION "/usr/local/lib/Poco_1.7.2/lib/libPocoFoundation.so")

set_target_properties
set_target_properties(target1 target2 ... PROPERTIES prop1 value1 prop2 value2 ...)

1.5 别名库

add_library(<name> ALIAS <target>)

为给定 library 添加一个别名,后续可使用<name>来替代<target>

 二、target_link_libraries的介绍

该命令用于用于将目标与所需的库进行链接。它用于指定一个目标(例如可执行文件)需要依赖的其他库,以便在构建过程中正确地链接这些库。该命令有多种使用方法,但他们都符合下面的命令格式:

target_link_libraries(<target> ... <item>... ...)

target:要链接库的目标

target 必须是由 add_executable() 或 add_library() 等命令创建的,并且不能是 ALIAS 目标。对同一 target 的重复调用按照调用的顺序追加项。

链接的库必须是由项目中的add_library()创建或者作为导入库创建。如果是在项目中创建的,则会在构建系统中自动添加排序依赖项,以确保指定的库目标在被链接时是最新的。

注: 构建系统自动添加排序依赖项,指的是在构建项目时,当目标(例如可执行文件)依赖于其他目标(通常是)时,构建系统会确保按正确的顺序构建这些目标,以满足依赖关系。比如:

add_library(mylib1...)

target_link_libraries(mylib2 PRIVATE mylib1)

add_executable(myapp...)

target_link_libraries(myapp PRIVATE mylib2)

add_library(mylib2...)

在这个例子中,myapp可执行文件依赖于mylib2库,而mylib2库又依赖于mylib1库。由于存在这种依赖关系,构建系统会自动添加构建规则,确保在构建myapp可执行文件之前,先构建mylib2库,而在构建mylib2库之前,先构建mylib1库。

最常使用方式

target_link_libraries(<target>

                      <PRIVATE|PUBLIC|INTERFACE> <item>...

                     [<PRIVATE|PUBLIC|INTERFACE> <item>...]...)

在这个命令中,可以使用 PUBLIC、PRIVATE 和 INTERFACE 这三个作用域关键字来指定链接的依赖项和链接接口:

  • PUBLIC 表示将指定的库或目标链接到目标,并将其作为链接接口的一部分。这意味着当其他目标链接到当前目标时,也会自动链接这些PUBLIC作用域的依赖项。这样的依赖项将被传递给依赖于当前目标的其他目标
  • PRIVATE表示将指定的库或目标链接到目标,但不将其作为链接接口的一部分。这意味着这些 PRIVATE 作用域的依赖项只会影响当前目标,而不会传递给依赖于当前目标的其他目标
  • INTERFACE 表示将指定的库链接到链接接口,而不进行实际的链接操作。这意味着这些 INTERFACE 作用域的依赖项不会对当前目标产生影响,但会被传递给依赖于当前目标的其他目标这通常用于定义一组接口,以满足其他目标的特定要求。

不指定作用域

target_link_libraries(<target> <item>...)

根据这个命令的默认行为,库的依赖关系是传递的,即如果一个目标链接到了另一个目标,那么链接到这个目标的库也会出现在另一个目标的链接行中。

三、target_include_directories

该命令用于为指定的目标(target)添加包含目录(include directories)指定编译目标时需要搜索头文件的路径,以确保编译器可以找到所需的头文件,这对于创建 CMake 项目中的库和可执行文件非常有用,因为它们可能依赖于其他模块或库的头文件

target_include_directories(<target> [SYSTEM] [AFTER|BEFORE]

                                            <INTERFACE|PUBLIC|PRIVATE> [items1...]

                                             [<INTERFACE|PUBLIC|PRIVATE> [items2...] ...])

  • target:要添加包含目录的目标名称。
  • SYSTEM:可选参数,指定被包含的目录是系统目录。这会告诉编译器将这些目录视为系统的标准头文件目录。
  • BEFORE:可选参数,指定将包含目录添加到已有的包含目录之前,而不是默认的添加到后面。
  • INTERFACE、PUBLIC、PRIVATE:指定包含目录的可见性级别
    • INTERFACE:表示包含目录将应用于目标及其使用该目标的其他目标。
    • PUBLIC:表示包含目录将应用于目标本身和使用该目标的其他目标
    • PRIVATE:表示包含目录仅应用于目标本身
  • items:指定要添加的包含目录路径,可以是目录名绝对路径相对路径

使用示例:

使用绝对路径:target_include_directories(target_name PRIVATE /path/to/directory) 这将为指定的目标(target_name)添加一个私有的包含目录,即只有该目标及其依赖项可以访问这个目录。

使用相对路径:target_include_directories(target_name PRIVATE relative/path/to/directory) 这将使用相对于当前 CMakeLists.txt 文件所在路径的相对路径来指定包含目录。
使用多个包含路径:target_include_directories(target_name PRIVATE directory1 directory2 ... )   这种用法允许一次性指定多个包含目录。

四、include_directories

include_directories 用于指定包含头文件目录。include_directories 函数允许在 CMakeLists.txt 文件中指定项目的头文件目录,以便在编译过程中能够正确地找到和包含目录中的头文件。

include_directories([AFTER|BEFORE] [SYSTEM] dir1 [dir2 ...])

  • BEFORE | AFTER:可选参数,它们用于指定包含目录的添加顺序。如果使用AFTER修饰符,那么添加的目录将会放在已有包含目录的后面;如果使用BEFORE修饰符,那么添加的目录将会放在已有包含目录的前面。默认情况下,新的包含目录会放在已有目录的后面。
  • SYSTEM:是一个可选的修饰符,用于指定所包含的目录是系统级别的目录。当使用SYSTEM修饰符时,编译器会将这些目录视为系统级别的头文件目录,这意味着编译器不会产生关于这些目录的警告信息。

注意事项

  • include_directories 命令应该在 add_executable add_library 之前使用,以确保编译器在构建过程中能够找到所需的头文件。
  • include_directories 命令仅仅告诉编译器在哪些目录中查找头文件,并不会自动包含这些头文件。要包含头文件,需要使用 #include 预处理指令在代码中显式包含。
  • 尽量避免使用 include_directories 命令的绝对路径。推荐使用相对于 CMakeLists.txt 文件的相对路径,以增加项目的可移植性。
  • 在使用 include_directories 命令时,应确保指定的目录中包含正确的头文件。否则,编译器可能无法找到所需的头文件,导致编译错误。

推荐使用 target_include_directories 而不是 include_directories 原因如下

  • 更明确的作用域include_directories命令是全局性的,它将全局添加的目录路径应用于整个项目。这可能导致潜在的命名冲突或不必要的搜索路径。而target_include_directories命令是针对特定目标(例如可执行文件或库)的,它可以将目录路径限定在特定的目标范围内,提供了更明确的作用域控制。
  • 更好的可维护性:通过使用target_include_directories命令,可以将目录路径与特定的目标关联起来。这样,在添加、修改或删除目标时,与之相关的目录路径也会自动调整,保持代码的一致性和可维护性。而使用include_directories命令时,需要手动确保目录路径的一致性,增加了维护工作的复杂性。
  • 更好的依赖管理target_include_directories命令可以与target_link_libraries命令结合使用,以确保正确的头文件搜索路径传递给依赖的目标。这样,在构建库时,库的用户不需要手动添加依赖的头文件路径,而是通过依赖关系自动传递。这简化了项目的依赖管理。
  • 跨平台兼容性:在某些情况下,使用target_include_directories命令可以提供更好的跨平台兼容性。例如,在使用一些编译器或构建工具链时,target_include_directories命令可以更好地处理特定的平台或编译器标志,以确保正确的头文件搜索路径。

CMake系列讲解 - 总目录(由浅入深,实例讲解)  来自 <https://codetheworld.blog.csdn.net/article/details/116940648>

Classic CMake Build Procedure (TM):

~/package $ mkdir build

~/package $ cd build

~/package/build $ cmake ..

~/package/build $ make   [-j2]

Newer version of Cmake Build Procedure:

~/package $ cmake -S . -B build

~/package $ cmake --build build  [-j2]

CMake菜谱(CMake Cookbook中文版)  Introduction - 《CMake菜谱(CMake Cookbook中文版)》 - 书栈网 · BookStack

https://github.com/dev-cafe/cmake-cookbook

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值