Skip to content

使用VSCode开发CMake项目

Altair Wei edited this page Dec 5, 2021 · 8 revisions

本文以 WizNotePlus 项目为例,讲解如何配置 Visual Studio Code 以便开发基于 CMake 构建系统的 C++ 项目。

1 语法提示与自动补全

1.1 需要安装的扩展

  • C/C++ :提供语法提示、调试以及代码浏览等功能。
  • C/C++ Clang Command Adapter :提供自动补全、语法诊断等功能。

1.2 配置 C/C++ 扩展

打开命令面板,输入 C/C++: Edit Configurations (JSON) 以生成 ./.vscode/c_cpp_properties.json 文件,然后修改配置。

Linux:

{
    "configurations": [
        {
            "name": "Linux",
            "includePath": [
                "${workspaceFolder}/**",
                "~/usr/Qt/5.14.2/gcc_64/**"
            ],
            "defines": [],
            "compilerPath": "/usr/bin/gcc",
            "cStandard": "c11",
            "cppStandard": "c++11",
            "intelliSenseMode": "gcc-x64",
            "browse": {
                "path": [
                    "${workspaceFolder}"
                ],
                "limitSymbolsToIncludedHeaders": true,
                "databaseFilename": ""
            }
        }
    ],
    "version": 4
}

MacOS:

{
    "configurations": [
        {
            "name": "MacOS",
            "includePath": [
                "${workspaceFolder}/**",
                "~/Qt/5.15.2/clang_64/**"
            ],
            "defines": [],
            "macFrameworkPath": [
                "/Library/Developer/CommandLineTools/SDKs/MacOSX10.15.sdk/System/Library/Frameworks"
            ],
            "compilerPath": "/usr/bin/clang",
            "cStandard": "c11",
            "cppStandard": "c++11",
            "intelliSenseMode": "macos-clang-x64"
        }
    ],
    "version": 4
}

注意 includePath 很重要,需要指向 Qt 库。另外,不同编译工具链下某些设置需要修改,比如使用 VC 编译器需要将 intelliSenseMode 指定为 msvc-x64 ,编译器地址设置可以删除,因为我们使用 CMake Tool 来自动检测工具链。这个设置文件主要用来提供语法检测。

1.3 配置 C/C++ Clang Command Adapter 扩展

注意:clang 版本可以根据你的需求自由选择。

首先安装 clang-6.0 :

sudo apt install clang-6.0

并在 ./.vscode/settings.json 中写入以下设置:

{
    "clang.executable": "/usr/bin/clang-6.0"
}

2 项目配置与构建

2.1 需要安装的扩展

  • CMake :提供 CMake 语法高亮和提示功能。
  • CMake Tools :提供 CMake 构建流程支持,也能为其他插件提供对语法诊断的支持。

2.2 配置 CMake Tools 扩展

Settings

CMake 的最佳实践是 out-of-source 构建,所以指定cmake.buildDirectory 为源码外的位置。

./.vscode/settings.json 中写入以下设置:

{
    "cmake.buildDirectory": "${workspaceRoot}/../build-WizNotePlus-VSCode",
    "cmake.configureSettings": {
        "CMAKE_PREFIX_PATH": "/home/altairwei/usr/Qt/5.14.2/gcc_64"
    }
}

这里的 CMAKE_PREFIX_PATH 主要作为默认设置,我们可以在 cmake-variants.json 中定义几套适合不同 Qt 版本的设置。

Kits

这一步不是必需的,你也可以使用插件自动检测的 Kits

./.vscode/cmake-kits.json 中写入 Kit :

[
    {
      "name": "GCC x64",
      "compilers": {
        "C": "/usr/bin/gcc",
        "CXX": "/usr/bin/g++"
      }
    }
]

保存后在底部状态栏选择该 Kit 。

Variants

因为 CMake Tools 在 Configure 时不会定义 CMAKE_BUILD_TYPE 变量,导致 Configure 过程出现潜在的问题,所以我们可以通过 Variants 的功能来显示定义它。

./.vscode/cmake-variants.json 中写入 Variants :

{
    "buildType": {
        "default": "Release",
        "description": "WizNotePlus Option",
        "choices": {
            "Debug": {
                "short": "Debug",
                "long": "Build with debugging information",
                "buildType": "Debug",
                "settings": {
                    "CMAKE_BUILD_TYPE": "Debug"
                }
            },
            "Release": {
                "short": "Release",
                "long": "Optimize the resulting binaries",
                "buildType": "Release",
                "settings": {
                    "CMAKE_BUILD_TYPE": "Release"
                }
            }
        }
    }
}

另外,我们也可以设置不同 Qt 库,比如:

{
    "buildType": {
        "default": "Release",
        "description": "WizNotePlus Option",
        "choices": {
            "Debug-Qt5.9": {
                "short": "Debug-Qt5.9",
                "long": "Build with debugging information",
                "buildType": "Debug",
                "settings": {
                    "CMAKE_BUILD_TYPE": "Debug",
                    "BUILD_TESTING": "ON",
                    "CMAKE_PREFIX_PATH": "/home/altairwei/usr/Qt/5.9.9/gcc_64"
                }
            },
            "Debug-Qt5.12": {
                "short": "Debug-Qt5.12",
                "long": "Build with debugging information",
                "buildType": "Debug",
                "settings": {
                    "CMAKE_BUILD_TYPE": "Debug",
                    "BUILD_TESTING": "ON",
                    "CMAKE_PREFIX_PATH": "/home/altairwei/usr/Qt/5.12.8/gcc_64"
                }
            },
            "Debug-Qt5.14": {
                "short": "Debug-Qt5.14",
                "long": "Build with debugging information",
                "buildType": "Debug",
                "settings": {
                    "CMAKE_BUILD_TYPE": "Debug",
                    "BUILD_TESTING": "ON",
                    "CMAKE_PREFIX_PATH": "/home/altairwei/usr/Qt/5.14.2/gcc_64"
                }
            },
            "Debug-Qt5.15": {
                "short": "Debug-Qt5.15",
                "long": "Build with debugging information",
                "buildType": "Debug",
                "settings": {
                    "CMAKE_BUILD_TYPE": "Debug",
                    "BUILD_TESTING": "ON",
                    "CMAKE_PREFIX_PATH": "/home/altairwei/usr/Qt/5.15.0/gcc_64"
                }
            },
            "Release": {
                "short": "Release",
                "long": "Optimize the resulting binaries",
                "buildType": "Release",
                "settings": {
                    "CMAKE_BUILD_TYPE": "Release"
                }
            }
        }
    }
}

4 调试应用

4.1 Linux 相关配置

注意:Qt 5.12 以后版本依赖于 OpenSSL v1.1

WizNotePlus 会用到 libQt5Network.so 模块,而该模块是以 OpenSSL v1.0 版本构建的。Ubuntu 18.04 平台系统自带 OpenSSL v1.1,因而运行程序时会出现网络错误的问题。WizNotePlus 项目通过 Conan 引入了 OpenSSL v1.0.2p 用以解决 libQt5Network.so 的依赖问题。

通过 readelf -d libQt5Network.so 可知该动态库的 RUNPATH 是 $ORIGIN ,看起来我们直接将 OpenSSL v1.0 相关动态库复制到 Qt5 库位置即可解决问题,但这毕竟比较麻烦。

我们注意到 Linux 下动态链接器的搜索顺序如下:

  • RPATH - a list of directories which is linked into the executable, supported on most UNIX systems. It is ignored if RUNPATH is present.
  • LD_LIBRARY_PATH - an environment variable which holds a list of directories
  • RUNPATH - same as RPATH, but searched after LD_LIBRARY_PATH, supported only on most recent UNIX systems, e.g. on most current Linux systems
  • /etc/ld.so.conf - configuration file for ld.so which lists additional library directories
  • builtin directories - basically /lib and /usr/lib

这意味着我们直接利用环境变量 LD_LIBRARY_PATH 可以达到随意设置 OpenSSL v1.0 动态库放置位置的目的。我们可以通过 VSCode C/C++ 扩展的 launch.json 来设置每次调试时的环境变量。

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "(gdb) Launch",
            "type": "cppdbg",
            "request": "launch",
            // Resolved by CMake Tools:
            "program": "${command:cmake.launchTargetPath}",
            "args": [],
            "stopAtEntry": false,
            "cwd": "${workspaceFolder}",
            "environment": [
                {
                    "name": "LD_LIBRARY_PATH",
                    "value": "${config:cmake.buildDirectory}/lib:${config:cmake.buildDirectory}/bin"
                }
            ],
            "externalConsole": true,
            "MIMode": "gdb",
            "setupCommands": [
                {
                    "description": "Enable pretty-printing for gdb",
                    "text": "-enable-pretty-printing",
                    "ignoreFailures": true
                }
            ]
        }
    ]
}

4.2 MacOS 相关配置

在 MacOS 上调试应用程序,可能出现找不到 dylib 的问题,因此我们需要使用 DYLD_LIBRARY_PATH 变量指向构建目录中包含缺失 dylib 的文件夹。相关 launch.json 配置:

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "(lldb) Launch",
            "type": "cppdbg",
            "request": "launch",
            // Resolved by CMake Tools:
            "program": "${command:cmake.launchTargetPath}",
            "args": [],
            "stopAtEntry": false,
            "cwd": "${workspaceFolder}",
            "environment": [
                {
                    // add the directory where our target was built to the PATHs
                    // it gets resolved by CMake Tools:
                    "name": "PATH",
                    "value": "${env:PATH}:${command:cmake.getLaunchTargetDirectory}"
                },
                {
                    "name": "DYLD_LIBRARY_PATH",
                    "value": "${config:cmake.buildDirectory}/lib:${config:cmake.buildDirectory}/bin"
                }
            ],
            "externalConsole": false,
            "MIMode": "lldb"
        }
    ]
}

5 添加打包任务

注意:wiznoteplus-build 镜像可能更新不及时,如有必要请根据 .ci/Dockerfile 自己构建镜像。

使用 VSCode C/C++ 的任务功能,在 ./.vscode 下新建 task.json

{
    // See https://go.microsoft.com/fwlink/?LinkId=733558
    // for the documentation about the tasks.json format
    "version": "2.0.0",
    "tasks": [
        {
            "label": "Build",
            "group": "build",
            "type": "shell",
            "dependsOrder": "sequence",
            "dependsOn": [
                "Mkdir"
            ],
            "options": {
                "env": {
                    "QT_INSTALL_PREFIX": "${config:qt.qtdir}"
                }
            },
            "args": [
                "${workspaceFolder}",
                "${workspaceFolder}/_build"
            ],
            "windows": {
                "command": ".\\.ci\\build.ps1",
                "options": {
                    "env": {
                        "CONAN_CMAKE_GENERATOR": "NMake Makefiles JOM"
                    }
                }
            },
            "osx": {
                "command": ".ci/build.sh"
            },
            "presentation": {
                "reveal": "always",
                "focus": true,
                "panel":"shared"
            },
            "problemMatcher": []
        },
        {
            "label": "Clean & Build",
            "group": "build",
            "type": "shell",
            "dependsOrder": "sequence",
            "dependsOn": [
                "Clean",
                "Build"
            ]
        },
        {
            "label": "Mkdir",
            "type": "shell",
            "windows": {
                "command": "mkdir -Force _build"
            },
            "osx": {
                "command": "mkdir -p _build"
            }
        },
        {
            "label": "Clean",
            "type": "shell",
            "windows": {
                "command": "rm -Force -Recurse _build"
            },
            "osx": {
                "command": "rm -rf _build"
            }
        },
        {
            "label": "Update Translation",
            "type": "shell",
            "command": "python ${workspaceFolder}/script/update_translations.py",
            "options": {
                "env": {
                    "PATH": "${config:qt.qtdir}/bin;${env:PATH}"
                }
            }
        },
        {
            "label": "Open Linguist",
            "type": "shell",
            "command": "linguist ${workspaceFolder}/i18n/wiznote_zh_CN.ts ${workspaceFolder}/i18n/wiznote_zh_TW.ts",
            "options": {
                "env": {
                    "PATH": "${config:qt.qtdir}/bin;${env:PATH}"
                }
            }
        },
        {
            "label": "Translate",
            "type": "shell",
            "problemMatcher": [],
            "dependsOrder": "sequence",
            "dependsOn": [
                "Update Translation",
                "Open Linguist"
            ]
        },
    ]
}