Visual Studio与VSCode中C++构建工具的配置手册

Vscode

准备工作

官方文档:https://code.visualstudio.com/docs/cpp/config-mingw

参考:

  1. 在vscode运行c++:https://blog.csdn.net/weixin_62411288/article/details/130796591
  2. 在vscode用makefile运行opengl:https://blog.csdn.net/weixin_43952192/article/details/122877840
  3. VSCode-Clang-MinGW-OpenGl配置教程:https://apollomao.com/VSCode-Clang-MinGW-OpenGl%E9%85%8D%E7%BD%AE%E6%95%99%E7%A8%8B/
  4. vscode中文乱码解决:https://blog.csdn.net/weixin_51723388/article/details/124171357
  1. 下载vscode Visual Studio Code - Code Editing. Redefined

  2. vscode安装c++扩展;

    vscode安装c++ extension

  3. g++的话推荐是安装 MSYS2;MSVC的话需要安装 visual studio

  4. 安装完打开MSYS2 UCRT64,安装mingw-w64-ucrt-x86_64-gccmingw-w64-x86_64-toolchain

    1
    2
    pacman -S mingw-w64-ucrt-x86_64-gcc
    pacman -S --needed base-devel mingw-w64-x86_64-toolchain
  5. D:\msys64\mingw64\bin 配置系统环境变量,重启电脑后在 cmd 上输入检查是否安装成功:

    1
    2
    3
    gcc --version
    g++ --version
    gdb --version
  6. 如果配置MSVC,需要给D:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.38.33130\bin\Hostx64\x64 配置环境变量,其中 Hostx64 表示软件编译时使用的处理器架构,即 x64 架构。第二个 x64 表示目标平台的处理器架构,即编译生成的可执行文件将在 x64 架构的处理器上运行,重启电脑后在 cmd 上输入检查是否安装成功:

    1
    cl -V

Vscode && g++

官方文档:https://code.visualstudio.com/docs/cpp/config-mingw

learnOpenGL源码为例,需要配置的有glfw、glad等库。

准备工作

需要用cmake-gui进行 ConfigureGenerate 生成 root_directory.h,随后需要 Include 到目录中。

cmake-gui生成root_directory.h

生成的root_directory.habs_path/build_vs2022/configuration/ 中。

这个代码运行的过程中会遇到 undefined reference to ‘stbi_load’ 的问题,需要在代码 #include <stb_image.h> 加入 STB_IMAGE_IMPLEMENTATION 宏的定义。

1
2
#include <stb_image.h>
#define STB_IMAGE_IMPLEMENTATION

配置编译器(c_cpp_properties.json)

在vscode中输入 ctrl+shift+p 调出命令行,输入选择 C/C++: Edit Configurations (UI)

c_cpp_properties.json

这个操作会在 .vscode 的文件夹中创建 c_cpp_properties.json 文件,其中需要设置的是 Include PathCompiler PathIntelliSense Mode

gcc-c_cpp_properties_ui

其中:

  • Compiler Path是使用的编译器,这里选择 gcc.exe
  • IntelliSense Mode 选择和编译器、运行平台对应的,这里选择 windows-gcc-x64
  • Include Path是下述 xxx 文件中的 includepath 的搜索范围:

这等价于在 c_cpp_properties.json 进行相应的编辑:

gcc-c_cpp_properties_json

配置构建任务(tasks.json)

在vscode中输入 ctrl+shift+p 调出命令行,输入选择 Tasks:Configure Default Build Task

Configure Default Build Task

再选择 C/C++: g++.exe build active file

build active file

这个操作会在 .vscode 的文件夹中创建 tasks.json 文件:

其中,一些配置说明如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
{
{
"version": "2.0.0",
"tasks": [
{
"label": "Compile", // 任务名称,与launch.json的preLaunchTask相对应
"command": "clang++",
"args": [ //编译时的参数
"${file}",
"-o", //指定输出文件名
"${fileDirname}/${fileBasenameNoExtension}.exe",
"-g", //添加gdb调试选项
"-Wall", //开启额外警告
"-static-libgcc", //静态链接libgcc
"--target=x86_64-w64-mingw", //clang编译器需要加上这条,因为它默认的target是msvc;如果用gcc或者linux要注释掉
"-std=c++17",
"-I${workspaceFolder}/include", //添加工作路径下的include
"-L${workspaceFolder}/lib", //添加工作路径下的lib
"-lglut32", //使用glut
"-lglu32", //使用glut
"-lopengl32", //使用opengl
"-lglad", //使用glad+glfw,这里可以先注释掉
"-lglfw3", //使用glad+glfw,这里可以先注释掉
"-lglfw3dll", //使用glad+glfw,这里可以先注释掉
"-lgdi32", //使用glad+glfw,这里可以先注释掉
],
"type": "shell",
"group": {
"kind": "build",
"isDefault": true //表示快捷键Ctrl+Shift+B可以运行该任务
},
"presentation": {
"echo": true,
"reveal": "always", // 执行任务时是否跳转到终端面板
"focus": false,
"panel": "shared" // 不同的文件的编译信息共享一个终端面板
},
"problemMatcher": []
}
]
}

在这个demo中,配置如下:

gcc-tasks.json

配置调试设置(launch.json)

菜单栏点击 run and debug,或者直接用 F5

run and debug

选择 C++(GDB/LLDB)

(GDB/LLDB)

这个操作会在 .vscode 的文件夹中创建 launch.json 文件:

其相关配置说明如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
{
"version": "0.2.0",
"configurations": [
{
"name": "(gdb) Launch", // 配置名称,将会在启动配置的下拉菜单中显示
"type": "cppdbg", // 配置类型,cppdbg对应cpptools提供的调试功能;可以认为此处只能是cppdbg
"request": "launch", // 请求配置类型,可以为launch(启动)或attach(附加)
"program": "${fileDirname}/${fileBasenameNoExtension}.exe", // 将要进行调试的程序的路径
"args": [], // 程序调试时传递给程序的命令行参数,一般设为空即可
"stopAtEntry": false, // 设为true时程序将暂停在程序入口处,相当于在main上打断点
"cwd": "${workspaceFolder}", // 调试程序时的工作目录,此为工作区文件夹;改成${fileDirname}可变为文件所在目录
"environment": [], // 环境变量
"externalConsole": false, // 为true时使用单独的cmd窗口,与其它IDE一致;18年10月后设为false可调用VSC内置终端
"internalConsoleOptions": "neverOpen", // 如果不设为neverOpen,调试时会跳到“调试控制台”选项卡,你应该不需要对gdb手动输命令吧?
"MIMode": "gdb", // 指定连接的调试器,可以为gdb或lldb。但我没试过lldb
"miDebuggerPath": "gdb.exe", // 调试器路径,Windows下后缀不能省略,Linux下则不要
"setupCommands": [
{ // 模板自带,好像可以更好地显示STL容器的内容,具体作用自行Google
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": false
}
],
"preLaunchTask": "Compile" // 调试会话开始前执行的任务,一般为编译程序。与tasks.json的label相对应
}
]
}

Vscode && MSVC

官方文档:https://code.visualstudio.com/docs/cpp/config-msvc

learnOpenGL源码为例,需要配置的有glfw、glad等库。

准备工作

准备工作基本同上。

需要用cmake-gui进行 ConfigureGenerate 生成 root_directory.h,随后需要 Include 到目录中。

cmake-gui生成root_directory.h

生成的root_directory.habs_path/build_vs2022/configuration/ 中。

这个代码运行的过程中会遇到 undefined reference to ‘stbi_load’ 的问题,需要在代码 #include <stb_image.h> 加入 STB_IMAGE_IMPLEMENTATION 宏的定义。

1
2
#include <stb_image.h>
#define STB_IMAGE_IMPLEMENTATION

配置编译器(c_cpp_properties.json)

在vscode中输入 ctrl+shift+p 调出命令行,输入选择 C/C++: Edit Configurations (UI)

c_cpp_properties.json

这个操作会在 .vscode 的文件夹中创建 c_cpp_properties.json 文件,其中需要设置的是 Include PathCompiler PathIntelliSense Mode

msvc-c_cpp_properties_json_ui

其中:

  • Compiler Path是使用的编译器,这里选择 cl.exe
  • IntelliSense Mode 选择和编译器、运行平台对应的,这里选择 msvc-x64(legacy)
  • Include Path是下述 xxx 文件中的 includepath 的搜索范围:

这等价于在 c_cpp_properties.json 进行相应的编辑:

msvc-c_cpp_properties.json

配置构建任务(tasks.json)

使用Develop Command Prompt启动

启动Develop Command Prompt,输入where cl可以得到cl.exe的位置:

where cl

可以看到默认是使用x86,通过命令set path=D:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.38.33130\bin\Hostx64\x64;%path%修改环境变量可以修改为使用 x64

update x86 cl.exe to x64 cl.exe

命令行修改目录到vscode工作目录:

使用code .使用vscode打开当前工作目录,如果code命令找不到,则需要配置vscode中bin目录添加到系统环境变量中。

where code

tasks.json配置如下,简单来说就是缺啥找啥,这里使用everything进行对报错缺失的文件进行快速搜索:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
"tasks": [
{
"type": "cppbuild",
"label": "C/C++: cl.exe build active file",
"command": "D:/Program Files/Microsoft Visual Studio/2022/Professional/VC/Tools/MSVC/14.38.33130/bin/Hostx64/x64/cl.exe",
"args": [
"/Zi",
"/EHsc",
"/I",
"D:/Program Files/Microsoft Visual Studio/2022/Professional/VC/Tools/MSVC/14.38.33130/include", // 附加项目录
"/I",
"C:/Program Files (x86)/Windows Kits/10/Include/10.0.22621.0/um",
"/I",
"C:/Program Files (x86)/Windows Kits/10/Include/10.0.22621.0/shared",
"/I",
"C:/Program Files (x86)/Windows Kits/10/Include/10.0.22621.0/ucrt",
"/I",
"C:/Program Files (x86)/Windows Kits/10/Include/10.0.22621.0/include",
"/I",
"${workspaceFolder}/includes/",
"/I",
"${workspaceFolder}/build_vs2022/configuration/",
"/Fe:",
"${fileDirname}\\${fileBasenameNoExtension}.exe",
"${file}", // 需要参与编译的文件
"${workspaceFolder}/src/glad.c",
"/MDd",
"/link",
"/MACHINE:x64", // 目标系统
"/LIBPATH:${workspaceFolder}/lib", // 静态链接路径
"/LIBPATH:D:/Program Files/Microsoft Visual Studio/2022/Professional/VC/Tools/MSVC/14.38.33130/lib/x64",
"/LIBPATH:C:/Program Files (x86)/Windows Kits/10/Lib/10.0.22621.0/um/x64",
"/LIBPATH:C:/Program Files (x86)/Windows Kits/10/Lib/10.0.22621.0/ucrt/x64",
"glfw3.lib", // 静态链接
"assimp.lib",
"freetype.lib",
"user32.lib",
"gdi32.lib",
"kernel32.lib",
"winspool.lib",
"shell32.lib"
],
"options": {
"cwd": "${fileDirname}"
},
"problemMatcher": [
"$msCompile"
],
"group": {
"kind": "build",
"isDefault": true
},
"detail": "Task generated by Debugger."
}
]

其参数分为编译和连接两个阶段。

其中:

  • "/Zi":生成调试信息。
  • "/EHsc":启用 C++ 异常处理。
  • "/Fe:":指定输出文件的名称和路径。
  • ${fileDirname}\\${fileBasenameNoExtension}.exe":输出文件的路径和名称,基于当前打开的文件。
  • ${file}:当前打开的文件的路径和名称。
  • ${workspaceFolder}/src/glad.c":引入 glad.c 文件。
  • "/I":指定包含文件的路径。
  • ${workspaceFolder}/includes":包含文件的路径。
  • ${workspaceFolder}/build_vs2022/configuration/":配置文件的路径。
  • "/link":链接阶段的参数。
  • "MACHINE:x64":指定目标机器架构为 64 位。
  • "LIBPATH:${workspaceFolder}/lib":指定库文件的路径。
  • "glfw3.lib":链接到 glfw3.lib 库。
  • ${workspaceFolder}/build_vs2022/Debug/STB_IMAGE.lib":链接到 STB_IMAGE.lib 库。
  • ${workspaceFolder}/build_vs2022/Debug/GLAD.lib":链接到 GLAD.lib 库。

使用vscode直接启动(还没验证)

需要加上"windows" 部分,可从官网上复制:https://code.visualstudio.com/docs/cpp/config-msvc

msvc-tasks.json

这里的:"\"D:/Program Files/Microsoft Visual Studio/2022/Professional/Common7/Tools/VsDevCmd.bat\"":这是要运行的批处理文件的路径。在这里,VsDevCmd.batVisual Studio 的开发者命令提示符批处理文件。它设置了一些环境变量和路径,以便在命令行中使用 Visual Studio 的工具和编译器。

Visual Studio

环境配置

环境设置。用一个油管up主thecherno的环境设置:http://thecherno.com/vs

在Visual Studio中,使用工具->导入导出设置

Environment setting

一直点下一步,在这个界面通过预览来找到需要加载的环境配置文件:

Import environment setting

属性管理器。使用属性管理器复用项目配置的时候,创建了 PropertySheet.prop 文件需要点击到项目的里面的PropertySheet才能打开图形窗口编辑,直接双击 PropertySheet.prop 是以文本编辑的形式打开。

propertySheet

输出路径,可以将输出目录和中间目录(主要包括 .obj.pdb 文件等)修改到 bin 下。

常规配置

附加包含目录附加库目录中尽量使用宏来设定。

Include directory

Library directory

library

生成事件可用于拷贝 .dll.exe 所在目录:

1
copy $(SolutionDir)xxxx\xxxx.dll $(SolutionDir)yyyy\yyyy.dll  /y

post event

动态链接库。在属性页->配置属性->调试->环境处进行配置:

dll

配置选项卡显示多行

visual studio选项卡显示多行

cmd输出中文乱码问题

cmd窗口汉字显示乱码

  1. win+R输入regedit进入注册表。

  2. 找到HKEY CURRENT USER\Console%SystemRoot%system:32cmd.exe如果该项下已存在CodePage项,则把值改为十进制”65001”;如果不存在,在该项下新建一个DWORD(32位值),命名为“CodePage”,值设为“65001”。

  3. 重启cmd后生效。

  4. 对于Power shell修改同样,只需在第2步修改%SystemRoot%system32 WindowsPowerShell_v1.0_powershell.exe下的项。

配置中文保存不乱码

拓展安装 Force UTF-8 2022

vcpkg安装包

遇到问题:vcpkg install error:in triplet x64-windows: Unable to find a valid Visual Studio instance Could not locate a complete Visual Studio instance,见https://github.com/microsoft/vcpkg/issues/22074:

vcpkg issue

安装命令:vcpkg install curl:x64-windows

卸载命令:vcpkg remove curl:x64-windows

vcpkg 集成到项目或开发环境中:vcpkg.exe integrate install

取消将 vcpkg 集成:vcpkg.exe integrate remove

curl编译

build-openssl.bat vc14.30 x64 debug D:\jupyter_notebook\compile_curl\openssl -VSpath D:\Program Files\Microsoft Visual Studio -perlpath D:\jupyter_notebook\vcpkg\downloads\tools\perl

看so

readelf -d

库配置

Libtorch

附加包含目录:

include path for libtorch

附加库目录:

library path for libtorch

附加依赖项,包含上述所有.lib

library for libtorc

动态依赖库:

image-20240811111642254

支持cuda命令:/INCLUDE:“?ignore_this_library_placeholder@@YAHXZ”

cuda availiable for libtorch.png

Cmake语法解释

set

  1. 设置变量

    1
    set(VARIABLE_NAME "value")

    这将创建一个名为VARIABLE_NAME的变量,并赋予它字符串值"value"

  2. 修改变量
    如果变量已经存在,你可以使用set来修改它的值:

    1
    set(VARIABLE_NAME "new_value")
  3. 使用变量
    在CMake脚本中,你可以使用${VARIABLE_NAME}来引用变量的值。

  4. 设置缓存变量
    使用CACHE选项可以将变量设置为缓存变量,这意味着它的值会在CMake的缓存中保存,并且可以在图形界面中编辑:

    1
    set(VARIABLE_NAME "value" CACHE STRING "Description of the variable")
  5. 条件设置变量
    你可以使用条件语句来根据条件设置变量的值:

    1
    2
    3
    4
    set(VARIABLE_NAME "value1" CACHE STRING "Description of the variable")
    if(CONDITION)
    set(VARIABLE_NAME "value2")
    endif()
  6. 设置多个变量
    你可以使用一个set命令来设置多个变量:

    1
    set(VARIABLE1 "value1" VARIABLE2 "value2")
  7. 使用PARENT_SCOPE
    默认情况下,set命令只在当前作用域内有效。如果你想在父作用域中设置变量,可以使用PARENT_SCOPE

    1
    2
    3
    function(some_function)
    set(VARIABLE_NAME "value" PARENT_SCOPE)
    endfunction()
  8. 使用APPEND或PREPEND
    你可以使用APPENDPREPEND选项来向现有变量的值添加内容:

    1
    2
    set(VARIABLE_NAME "initial_value")
    set(VARIABLE_NAME "${VARIABLE_NAME} additional_value" APPEND)
  9. 使用FILE PATH
    set命令可以与FILE PATH一起使用,以确保变量包含一个有效的文件路径:

    1
    set(VARIABLE_NAME "${CMAKE_SOURCE_DIR}/path/to/file" FILEPATH)
  10. 使用PATH
    set命令可以与PATH一起使用,以确保变量包含一个有效的路径列表:

    1
    set(VARIABLE_NAME "path1;path2" PATH)

option

在CMake中,如果你想为变量设置一个默认值,并且希望用户可以在CMake配置过程中修改这个值,你可以使用option命令。option命令允许用户在运行cmake时设置一个布尔选项的值,通常用于启用或禁用某些特性或组件。

以下是option命令的基本用法:

  1. 定义选项

    1
    option(OPTION_NAME "Description of the option" DEFAULT_VALUE)

    这里OPTION_NAME是选项的名称,Description of the option是选项的描述,DEFAULT_VALUE是选项的默认值,可以是ONOFF

  2. 使用选项
    一旦定义了选项,你可以在CMake脚本中使用OPTION_NAME变量,它的值将是用户设置的值,如果没有设置,则为默认值。

  3. 条件编译
    根据选项的值,你可以控制条件编译:

    1
    2
    3
    4
    5
    if(OPTION_NAME)
    # 代码块,当选项为ON时执行
    else()
    # 代码块,当选项为OFF时执行
    endif()
  4. 在命令行中设置选项
    用户可以在运行CMake时使用-D选项来设置变量的值:

    1
    cmake -DOPTION_NAME=ON ..
  5. 在CMakeLists.txt中使用选项
    你可以在CMakeLists.txt文件中使用option命令来定义选项,并在后续的命令中引用这个选项。

  6. 选项的缓存类型
    option命令会自动将选项设置为缓存变量,并设置为BOOL类型。

  7. 选项的高级用法
    你可以使用FORCE关键字来强制重新评估选项,即使它已经在缓存中:

    1
    option(OPTION_NAME "Description" DEFAULT_VALUE FORCE)

使用option命令可以让用户在构建系统外部控制项目的配置,而无需修改源代码。

include_directories

在CMake中,include_directories命令用于向项目添加头文件搜索路径。这个命令会告诉编译器在哪里查找头文件,这对于编译依赖于外部库的项目非常有用。

以下是include_directories的一些基本用法:

  1. 添加单个头文件搜索路径

    1
    include_directories(PATH_TO_INCLUDE)

    这将添加PATH_TO_INCLUDE到编译器的头文件搜索路径中。

  2. 添加多个头文件搜索路径

    1
    include_directories(PATH1 PATH2 PATH3)

    这将添加多个路径到编译器的头文件搜索路径中。

  3. 使用变量添加头文件搜索路径

    1
    2
    set(MY_INCLUDE_PATHS PATH1 PATH2)
    include_directories(${MY_INCLUDE_PATHS})
  4. 条件添加头文件搜索路径

    1
    2
    3
    if(SOME_CONDITION)
    include_directories(PATH_CONDITIONAL)
    endif()
  5. 使用系统头文件搜索路径
    使用SYSTEM关键字可以告诉CMake将这些路径视为系统目录。在这些目录中找到的库通常不会触发编译器警告:

    1
    include_directories(SYSTEM PATH_TO_SYSTEM_INCLUDE)
  6. 注意

    • 从CMake 3.4开始,推荐使用target_include_directories来替代include_directories,因为这样可以更精确地控制哪些目标应该包含这些头文件路径。
    • include_directories会影响全局编译器命令行,可能会在项目的不同部分产生冲突。使用target_include_directories可以提供更细粒度的控制。
  7. 使用target_include_directories
    你可以使用target_include_directories为特定目标添加头文件搜索路径:

    1
    2
    3
    add_executable(my_app main.cpp)
    target_include_directories(my_app PRIVATE PATH_TO_PRIVATE_INCLUDE
    PUBLIC PATH_TO_PUBLIC_INCLUDE)

    这里PRIVATE表示只有my_app可以访问PATH_TO_PRIVATE_INCLUDE,而PUBLIC表示my_app及其依赖项都可以访问PATH_TO_PUBLIC_INCLUDE

  8. 使用INTERFACEPUBLIC/PRIVATE关键字
    使用target_include_directories时,你可以使用INTERFACE关键字为依赖项添加头文件搜索路径,或者使用PUBLIC/PRIVATE关键字来控制路径的可见性:

    • PUBLIC:头文件路径对目标本身及其依赖项可见。
    • PRIVATE:头文件路径仅对目标本身可见。

使用include_directories可以方便地添加头文件搜索路径,但要注意不要过度使用,以免造成依赖关系混乱。在可能的情况下,使用target_include_directories来替代全局的头文件路径添加。

add_definitions

在CMake中,add_definitions命令用于向编译器添加编译器定义。这些定义通常用于在编译时启用或禁用特定的宏,这对于条件编译非常有用。

以下是add_definitions命令的一些基本用法:

  1. 添加单个定义

    1
    add_definitions(-DDEFINITION)

    这将向编译器添加一个预处理器宏DEFINITION

  2. 添加多个定义
    你可以一次性添加多个定义:

    1
    add_definitions(-DDEFINITION1 -DDEFINITION2)
  3. 使用变量添加定义
    你可以将定义存储在变量中,然后使用这个变量来添加定义:

    1
    2
    set(MY_DEFINITIONS -DDEFINITION1 -DDEFINITION2)
    add_definitions(${MY_DEFINITIONS})
  4. 条件添加定义
    根据条件添加定义:

    1
    2
    3
    if(MY_CONDITION)
    add_definitions(-DCONDITIONAL_DEFINITION)
    endif()
  5. 添加定义到特定目标
    从CMake 3.4开始,推荐使用target_compile_definitions代替add_definitions,因为这样可以更精确地控制哪些目标应该包含这些定义。例如:

    1
    2
    add_executable(my_app main.cpp)
    target_compile_definitions(my_app PRIVATE -DAPP_DEFINITION)
  6. 使用INTERFACEPUBLIC/PRIVATE关键字
    使用target_compile_definitions时,你可以使用INTERFACE关键字为依赖项添加定义,或者使用PUBLIC/PRIVATE关键字来控制定义的可见性:

    • PUBLIC:定义对目标本身及其依赖项可见。
    • PRIVATE:定义仅对目标本身可见。
  7. 使用add_definitions添加的宏定义
    添加的宏定义可以在源代码中使用#ifdef#ifndef#if等预处理指令进行条件编译。

  8. 注意

    • 从CMake 3.11开始,add_definitions添加的定义默认只影响C和C++源文件。如果你需要添加对其他语言的支持,可以使用-DDEFINITION LANGS CXX
    • 由于add_definitions会影响全局编译器命令行,因此可能会在项目的不同部分产生冲突。使用target_compile_definitions可以提供更细粒度的控制。

使用add_definitions可以方便地在编译时控制代码的行为,但要注意不要过度使用,以免造成依赖关系混乱。在可能的情况下,使用条件编译和target_compile_definitions来替代全局的宏定义。

cmake_minimum_required

cmake_minimum_required 是 CMake 脚本中非常重要的一个命令,用于指定项目所需的最低 CMake 版本。这个命令必须放在 CMakeLists.txt 文件的最开始位置,以确保在进一步处理之前满足所需的 CMake 版本。

以下是 cmake_minimum_required 的基本用法:

  1. 指定最低 CMake 版本

    1
    cmake_minimum_required(VERSION 3.10)

    这行命令指定了项目的最低 CMake 版本需求是 3.10。如果用户使用的 CMake 版本低于这个版本,CMake 会报错并提示用户升级。

  2. 指定政策
    从 CMake 2.8.12 开始,cmake_minimum_required 还可以指定一个政策版本,以确保项目遵循某些特定的 CMake 行为:

    1
    cmake_minimum_required(VERSION 3.10...3.12)

    这里 ...3.12 表示除了指定最低版本 3.10 外,还指定了政策版本,即项目应该遵循 CMake 3.10 到 3.12 之间的政策。

  3. 指定精确版本
    如果你希望项目严格使用某个特定版本的 CMake,可以指定一个精确的版本号:

    1
    cmake_minimum_required(VERSION 3.10 EXACT)

    使用 EXACT 关键字后,如果用户使用的 CMake 版本不是 3.10,CMake 将报错。

  4. 指定最低和最高版本
    你还可以在一定范围内指定最低和最高版本:

    1
    cmake_minimum_required(VERSION 3.10.2 UPGRADE)

    使用 UPGRADE 关键字后,如果用户使用的 CMake 版本低于 3.10.2,CMake 会报错;如果版本高于 3.10.2,CMake 会尝试升级到新的行为。

  5. 指定特性
    cmake_minimum_required 还可以用于确保 CMake 支持某些特性,例如:

    1
    cmake_minimum_required(VERSION 3.1 FATAL_ERROR)

    这里 FATAL_ERROR 表示如果用户使用的 CMake 版本低于指定版本,CMake 将终止并报错。

  6. 注意

    • 确保 cmake_minimum_requiredCMakeLists.txt 中的第一个命令。
    • 根据项目需求选择合适的 CMake 版本。新版本的 CMake 通常包含更多的特性和改进。

使用 cmake_minimum_required 可以确保项目的构建环境符合预期,避免因 CMake 版本不兼容导致的问题。

add_library

add_library 是 CMake 中用于定义库目标的命令。库可以是静态库(STATIC)、动态库(SHARED)或对象库(OBJECT),并且可以包含源文件、编译选项、依赖关系等。

以下是 add_library 命令的一些基本用法:

  1. 创建静态库

    1
    add_library(mylib STATIC source1.cpp source2.cpp)

    这将创建一个名为 mylib 的静态库,包含 source1.cppsource2.cpp 两个源文件。

  2. 创建动态库

    1
    add_library(mylib SHARED source1.cpp source2.cpp)

    这将创建一个名为 mylib 的动态库。

  3. 创建对象库

    1
    add_library(mylib OBJECT source1.cpp source2.cpp)

    对象库不生成最终的二进制文件,而是生成对象文件,这些对象文件可以被其他库或可执行文件目标重用。

  4. 添加头文件
    通常,头文件不直接添加到库中,但可以指定头文件的路径,以便其他目标包含:

    1
    2
    add_library(mylib STATIC source1.cpp)
    target_include_directories(mylib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
  5. 设置库的别名
    别名可以在其他命令中引用库:

    1
    add_library(mylib::mylib ALIAS source1.cpp source2.cpp)
  6. 设置库的输出名称
    使用 OUTPUT_NAME 选项来指定库文件的名称:

    1
    2
    add_library(mylib STATIC source1.cpp source2.cpp)
    set_target_properties(mylib PROPERTIES OUTPUT_NAME "my_library")
  7. 设置库的版本
    使用 VERSIONSOVERSION 选项来指定库的版本:

    1
    2
    add_library(mylib SHARED source1.cpp source2.cpp)
    set_target_properties(mylib PROPERTIES VERSION 1.2 SOVERSION 1)
  8. 为库添加编译选项
    使用 target_compile_options 为库添加特定的编译选项:

    1
    2
    add_library(mylib STATIC source1.cpp)
    target_compile_options(mylib PRIVATE -Wall)
  9. 为库添加定义
    使用 target_compile_definitions 为库添加预处理器定义:

    1
    2
    add_library(mylib STATIC source1.cpp)
    target_compile_definitions(mylib PRIVATE "MYLIB_DEFINE")
  10. 为库添加依赖
    使用 target_link_libraries 为库添加链接库或框架:

    1
    2
    add_library(mylib STATIC source1.cpp)
    target_link_libraries(mylib PRIVATE otherlib)
  11. 为库设置安装规则
    使用 install 命令为库设置安装规则:

    1
    2
    3
    install(TARGETS mylib
    LIBRARY DESTINATION lib
    ARCHIVE DESTINATION lib/static)

add_library 是构建库的基础命令,通过与 set_target_propertiestarget_include_directoriestarget_compile_options 等其他命令结合使用,可以定义库的各种属性和行为。

find_library

find_library 是 CMake 中的一个命令,用于在系统上搜索库文件。这个命令通常与 find_package 一起使用,作为查找第三方库的一部分。find_library 可以找到库的路径,然后你可以使用这个路径来链接库。

以下是 find_library 的基本用法:

  1. 基本搜索

    1
    2
    3
    4
    5
    6
    7
    8
    9
    find_library(LIBRARY_NAME
    NAMES libname # 库文件的名称,例如 "mylib"
    PATHS /path/to/search # 可选的搜索路径列表
    PATH_SUFFIXES bin lib # 可选的路径后缀列表
    NO_DEFAULT_PATH # 可选,不使用默认的搜索路径
    NO_CMAKE_ENVIRONMENT_PATH # 可选,不使用环境变量中的路径
    NO_CMAKE_PATH # 可选,不使用 CMake 路径变量中的路径
    NO_SYSTEM_ENVIRONMENT_PATH # 可选,不使用系统环境变量中的路径
    )
  2. 使用搜索结果
    如果 find_library 成功找到库,LIBRARY_NAME 变量将被设置为库的完整路径。你可以使用这个变量来链接库:

    1
    2
    3
    if(LIBRARY_NAME)
    target_link_libraries(my_target ${LIBRARY_NAME})
    endif()
  3. 指定库的类型
    find_library 可以搜索不同类型的库文件,例如静态库(.a)、动态库(.so、.dll)等。通过 NAMES 参数指定库的名称时,你可以指定多个名称,以覆盖不同类型的库:

    1
    find_library(LIBRARY_NAME NAMES mylib mylib_static)
  4. 使用变量
    你可以将库的名称存储在变量中,然后在 find_library 中使用这个变量:

    1
    2
    set(LIB_NAME "mylib")
    find_library(LIBRARY_NAME NAMES ${LIB_NAME})
  5. 组合使用多个 find_library
    如果你的项目需要链接多个库,可以为每个库调用一次 find_library

    1
    2
    3
    find_library(LIBRARY_NAME1 NAMES lib1)
    find_library(LIBRARY_NAME2 NAMES lib2)
    # ...
  6. 使用 pkg-config
    如果库提供了 pkg-config 文件,你可以使用 pkg_check_modules 来查找库,这通常比 find_library 更方便:

    1
    pkg_check_modules(LIB_NAME REQUIRED mylib)
  7. 注意

    • find_library 只返回第一个找到的库路径。
    • 如果库文件不在标准的库搜索路径中,你可能需要指定 PATHS 参数。
    • 使用 find_library 时,最好结合条件判断来检查库是否真正被找到,以避免在找不到库时产生错误。

find_library 是 CMake 中查找库文件位置的一个有用工具,但随着 CMake 的发展,推荐使用 find_packagepkg_check_modules 来处理库的查找和配置,因为这些命令提供了更高级的功能和更好的错误处理。

target_link_libraries 是 CMake 中的一个命令,用于为目标(可执行文件或库)添加链接库。这个命令允许你指定目标应该链接的库,包括库的名称、路径或目标名称。

以下是 target_link_libraries 的一些基本用法:

  1. 链接单个库

    1
    2
    add_executable(my_app main.cpp)
    target_link_libraries(my_app mylib)

    这里 my_app 是可执行文件目标,mylib 是要链接的库目标或库文件的路径。

  2. 链接多个库

    1
    target_link_libraries(my_app mylib1 mylib2)

    这将链接 my_appmylib1mylib2

  3. 链接库文件的路径

    1
    target_link_libraries(my_app "/path/to/libmylib.a")

    如果库文件不在标准搜索路径中,可以指定库文件的完整路径。

  4. 使用链接选项

    1
    target_link_libraries(my_app mylib INTERFACE)

    使用 INTERFACE 关键字可以指定链接库仅作为接口链接,这意味着链接库的依赖项不会传递给依赖 my_app 的其他目标。

  5. 链接系统库

    1
    target_link_libraries(my_app PRIVATE ${CMAKE_DL_LIBS})

    CMAKE_DL_LIBS 是 CMake 预定义的变量,包含了链接动态加载器所需的库。

  6. 链接条件库

    1
    2
    3
    if(MY_CONDITION)
    target_link_libraries(my_app PRIVATE mylib)
    endif()

    根据条件添加链接库。

  7. 使用 LINK_PUBLICLINK_PRIVATE
    使用 LINK_PUBLICLINK_PRIVATE 指定链接库的可见性:

    • LINK_PUBLIC:链接库对目标及其依赖项都可见。
    • LINK_PRIVATE:链接库仅对目标本身可见。
  8. 链接编译器选项

    1
    target_link_options(my_app PRIVATE -Wl,--as-needed)

    target_link_options 命令用于为目标添加链接选项。

  9. 链接库的依赖项
    当你链接一个 CMake 目标时,它的依赖项也会自动链接。例如:

    1
    2
    3
    4
    add_library(mylib STATIC source.cpp)
    target_link_libraries(mylib PRIVATE otherlib)
    add_executable(my_app main.cpp)
    target_link_libraries(my_app PRIVATE mylib)

    my_app 将链接 mylib 以及 mylib 依赖的 otherlib

  10. 注意

    • 确保库目标在调用 target_link_libraries 之前已经定义。
    • 使用 target_link_libraries 时,最好指定库的别名或目标名称,而不是直接指定库文件路径,这样可以提高构建系统的可移植性。

target_link_libraries 是构建系统中链接库的重要工具,它允许你精确控制目标的链接过程,包括链接哪些库以及这些库的链接方式。

add_executable

add_executable 是 CMake 中用于定义可执行文件目标的命令。使用这个命令,你可以指定生成一个可执行文件,并且列出构成这个可执行文件的所有源文件。

以下是 add_executable 的一些基本用法:

  1. 定义可执行文件

    1
    add_executable(my_app main.cpp other_source.cpp)

    这将创建一个名为 my_app 的可执行文件,由 main.cppother_source.cpp 源文件编译而成。

  2. 添加源文件
    你可以在 add_executable 命令中列出所有源文件,或者之后使用 target_sources 命令添加:

    1
    2
    add_executable(my_app main.cpp)
    target_sources(my_app PRIVATE other_source.cpp)
  3. 设置可执行文件的输出名称
    使用 OUTPUT_NAME 属性来指定可执行文件的输出名称:

    1
    2
    add_executable(my_app main.cpp)
    set_target_properties(my_app PROPERTIES OUTPUT_NAME "my_application")
  4. 设置可执行文件的版本
    使用 VERSIONSOVERSION 属性来指定可执行文件的版本(这些属性通常用于共享库):

    1
    set_target_properties(my_app PROPERTIES VERSION 1.2 SOVERSION 1)
  5. 为可执行文件添加编译选项
    使用 target_compile_options 命令为可执行文件添加特定的编译选项:

    1
    target_compile_options(my_app PRIVATE -Wall)
  6. 为可执行文件添加定义
    使用 target_compile_definitions 命令为可执行文件添加预处理器定义:

    1
    target_compile_definitions(my_app PRIVATE "MY_APP_DEFINE")
  7. 为可执行文件添加头文件搜索路径
    使用 target_include_directories 命令为可执行文件添加头文件搜索路径:

    1
    target_include_directories(my_app PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include)
  8. 链接库到可执行文件
    使用 target_link_libraries 命令将库链接到可执行文件:

    1
    target_link_libraries(my_app PRIVATE mylib)
  9. 添加依赖项
    如果你的可执行文件依赖于在构建过程中生成的其他目标,可以使用 add_dependencies 命令添加依赖:

    1
    add_dependencies(my_app my_dependency)
  10. 为可执行文件设置安装规则
    使用 install 命令为可执行文件设置安装规则:

    1
    install(TARGETS my_app RUNTIME DESTINATION bin)

add_executable 是构建可执行文件的基础命令,通过与 set_target_propertiestarget_compile_optionstarget_link_libraries 等其他命令结合使用,可以定义可执行文件的各种属性和行为。