Linux C/C++ 配置开发项目

本文档是基于linux开发环境进行编写,也同样适用于window开发环境。

本文仅是介绍 vscode、xmake 的一种使用方法。 xmake本身也是支持远程编译,相比ssh更加稳定、高效。

学习源码 : https://gitcode.com/xiaofeio/stu_linux/overview

介绍

  • vscode、xmake、git 使用

  • 通过 xmake 配置项目工程(目标)依赖

  • 配置 vscode,实现快捷构建任务(习惯了vs2013的快捷方式)、构建依赖(非xmake的配置)、IntelliSense功能(代码分析、代码补全),调试程序的参数配置,附加进程调试,调试器自定义结构的Visualizers

系统环境

操作系统 CentOS 9 Stream

工作环境 vscodegccxmakegit

项目版本管理

创建git版本库

  1. 登录GitCode - 全球开发者的开源社区,开源代码托管平台

  2. 点击右侧 新建 按钮创建一个 stu_linux 项目(可自行命名)

    在这里插入图片描述

  3. 创建完成进程项目代码后点击 Clone 按钮

    在这里插入图片描述

  4. 弹出的页面中选择 SSH ,点击标记2复制url路径

    在这里插入图片描述

    克隆git仓储

    请参考《Linux C/C++ 编程环境配置》配置版本管理部分,配置 ssh 参考 第4条第5条, 克隆仓储参考 第6条,完成后如下图所示

    在这里插入图片描述

创建项目工程

不要创建 代码工程,规划不会为每个学习用例都创建一个仓储,而是都放在这一个仓储下(根据个人规划可跳过下面这几步,通过 vscodeXMake:CreateProject 命令创建工程也会失败,提示不是一个空文件夹)。

在这里插入图片描述

在这里插入图片描述

方法一

  1. 首先创建一个 eg_prjs 的用例,在终端中输入

    mkdir eg_prjs
    

    在这里插入图片描述

  2. 在vscode中选择打开 eg_prjs 文件夹

    在这里插入图片描述

    在这里插入图片描述

  3. 通过 vscode XMak:CreateProject 创建项目工程

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    需要注意的是vscode XMake:CreateProject 创建了 .vscode 文件夹

方法二

  1. 通过 xmak 命令行的方式创建 ex_prjs 共享库,进入到 ex_prjs 文件夹下,通过命令行的方式可以正常编译

    : 为了区分方法一命名为 ex_prjs
    在这里插入图片描述

    但是不能使用vscode中 XMake:Build 命令构建

    在这里插入图片描述

    提示如下没有找到 xmake.lua,这是因为 vscode xmake 插件的命令会直接作用在顶级目录上,还是需要切换vs code的工作目录

    在这里插入图片描述

  2. 在vscode中通过打开 ex_prjs 文件夹方式切换vscode的工作空间

    在这里插入图片描述

    在这里插入图片描述

    需要注意的是没有 .vscode 文件夹,这并不影响后面的配置,可以添加回来。

    相比 方法一 缺少了 compile_commands.json 文件,通过输入下列命令

    xmake project -k compile_commands
    

    会生成 compile_commands.json 文件

    在这里插入图片描述

    在根目录下创建 .vscode 文件夹,并把 compile_commands.json 文件移动(vscode中直接拖拽)到 .vscode 文件夹下就和方法一样了

    在这里插入图片描述

    .xmake 文件夹下内容随着后期使用自然会明白的,虽然和 方法一 有些不一样,但这不影响本次实验,后面的操作都是相同的。

编译调试项目

: 沿用 创建项目工程方法一

在这里插入图片描述

提交代码

在进行下列演练之前,先提交代码并上传到 远端仓储

  1. 点击导航中 Source Control,在切换 SOURCE CONTROL界面上点击提交,查看变更信息,书写日志,提交代码

在这里插入图片描述

在这里插入图片描述

​ 点击”提交“按钮弹出确认对话框,点击”Save“

在这里插入图片描述

​ 只是完成了本地的提交

  1. 同步推送到远端仓储,点击”Sync Changes“

    在这里插入图片描述

    在弹出的确认对话中点击”OK“或"OK, Don’t Show Again"

    在这里插入图片描述

    提交成功后, Source Control 界面更新控件状态

    在这里插入图片描述

    查看远端仓储,如果一直打开了远端仓储界面,按下浏览器上的 F5刷新一下

    在这里插入图片描述

    查看到提交信息

编译工程

编译前先了解下各个文件和文件内容

  1. xmake.lua,这是本节中的一个重要文件
    在这里插入图片描述

    这个文件是 xmake 的配置文件,含义参考图中的描述,创建默认是没有这些说明。 xmake是基于 lua 开发的,注释可以用 ,为了保留原样没加注释。

    重要的一句是 add_deps(“foo”) 表示 eg_prjs 目标(或称为应用程序)依赖了 foo 目标(或称为模块)。

  2. foo.h

    在这里插入图片描述

    这里重要的一句是导出 add(int a, int b) 函数

    __export int add(int a, int b);
    

    C/C++编程人员都懂的,foo.cpp 的内容不想就知道是什么了,这里就不在说明了。

  3. main.cpp

    在这里插入图片描述

    很好,foo.h没有红色的波浪线等情况,这是因为foo.h、foo.cpp、main.cpp都在一个src的文件夹下

  4. 使用 vscode xmake 插件中的命令构建编译项目工程。没有编译无法启动调试(后面会讲解如何配置满足个人习惯)

    在这里插入图片描述

    执行 XMake:Build
    命令,终端一共执行两条 xmak 命令,分别是

    xmake f -p linux a x86_64 -m debug
    

    这句用于配置平台、架构、debug模式

    xmake
    

    这句用于构建编译。

    出现了 build ok 表示构建成功,花费 1.077 秒。

运行和调试

运行

通过点击 xmake 工具栏上 运行 按钮就可以启动运行,效果如下

在这里插入图片描述

首先会执行 xmake run eg_prjs 命令启动运行, 程序运行后main.cpp中调用了foo.h中的 add()函数进行计算并打印计算结果。

既然已经知道了 xmake 插件在vscode中的执行也是基于 xmake 本身提供的命令集进行操作,那么自然也可以通过输入命令行的方式进行 运行 同样也适用 调试

在这里插入图片描述

在命令终端中直接输入 xmake run 即可运行, 与上一个比少输入了一个eg_prjs(因为这个下面下就一个可执行目标可以忽略,关于xmake的用法请参考官网说明)

调试

简单调试

首先在main.cpp中第5行按下快捷键 F9 打一个断点

在这里插入图片描述

然后点击上图的标记2运行 debug 启动调试

在这里插入图片描述

调试程序启动后,会停在断点处,并且显示标记2的 调试操作命令(可执行单步调试,进入、跳出、停止、重启),在标记3可以看到变量信息,调用堆栈。

按下 F11 进入到 foo.cppadd()函数

接下来修改一下main.cpp的函数

#include "foo.h"
#include <iostream>
#include <string>
#include <vector>

int main(int argc, char** argv) {
    std::cout << "add(1, 2) = " << add(1, 2) << std::endl;

    std::vector<std::string> stdvec = { "Hello", "vscode", "xmake", "C/C++", "!!!" };
    for (auto &&i : stdvec)
    {
        std::cout  << i << " ";
    }
    std::cout << std::endl;
    


    return 0;
}

在第7行再次按下快捷键 F9 去掉第7断点, 在main.cpp中第10行按下快捷键 F9 打一个断点,然后再启动调试(可以直接按下 F5 弹出的命令面板中选择 xmake

在这里插入图片描述

当程序停止在断点处,在右侧变量查看区,点击 stdvec 右侧展开按钮,可以看到 vector 容器内容的各个元素值。

项目工程部署

创建项目工程中,可以看到通过 xmake 命令创建的工程是一个简单框架目录部署结构,而在实际开发中会根据不同的应用场景进行更复杂的目录部署。

调整文件部署结构

接下来以foo、app两个文件夹分别作为独立模块的存储文件夹为例。将目录调整如下结构。

在这里插入图片描述

foo文件夹下的inc文件夹用于对外提供的头文件,而src文件作为foo模块内部的源码文件。

app文件夹用于存放应用程序的源文件,由于app并不对外提供头文件,因此没有部署inc、src等相应功能的文件夹。

修改 xmake 配置、编译和调试

接下来打开 xmake.lua 文件,修改内容如下

add_rules("mode.debug", "mode.release")

target("foo")
    set_kind("shared")
    add_files("src/foo/src/**.cpp")  -- ** 表示添加 src/foo/src文件夹及子文件夹夹下的所有.cpp文件

target("eg_prjs")
    set_kind("binary")
    add_deps("foo")
    add_files("src/app/*.cpp") -- * 表示添加 src/app文件夹下的所有cpp文件

修改完成后编译一下看看会是什么情况

在这里插入图片描述

结果显示 没有找到 foo.h 这个文件,并且是因为编译 foo.cpp 在第1行第10列报的错误。

因为编译是基于 xmake, 因此只需要继续修改 xmake.lua 文件即可, 更多的信息请参考 xmake教程 ,修改内容如下。

add_rules("mode.debug", "mode.release")

target("foo")
    set_kind("shared")
    add_includedirs("src/foo/inc")   -- 添加头文件的目录
    add_files("src/foo/src/**.cpp")  -- ** 表示添加 src/foo/src文件夹及子文件夹夹下的所有.cpp文件

target("eg_prjs")
    set_kind("binary")
    add_deps("foo")
    add_files("src/app/*.cpp") -- * 表示添加 src/app文件夹下的所有cpp文件

再次构建看看效果

在这里插入图片描述

还是有错误,只不过报告的错误是 main.cpp 在第1行第10列报的错误。那么继续修改 xmake.lua 文件,内容如下

add_rules("mode.debug", "mode.release")

target("foo")
    set_kind("shared")
    add_includedirs("src/foo/inc", {public = true })   -- 添加头文件的目录, public表示把头文件暴露出来提供给通过 add_deps() 添加代模块, 如下面的 add_deps(fool)
    add_files("src/foo/src/**.cpp")  -- ** 表示添加 src/foo/src文件夹及子文件夹夹下的所有.cpp文件

target("eg_prjs")
    set_kind("binary")
    add_deps("foo")
    add_files("src/app/*.cpp") -- * 表示添加 src/app文件夹下的所有cpp文件

再次构建,可以看到编译成功

在这里插入图片描述

启动调试

在这里插入图片描述

查看调试效果

在这里插入图片描述

和之前的调试效果是一样的

构建调试运行

配置构建任务

习惯了使用Vistual Studio 2013等系列的IDE,按下 Ctrl + Shift + B 进行构建程序,而vscode 会弹出命令面板, 并且根据当前打开的文件显示的命令也不同,打开的是cpp文件显示效果如下

在这里插入图片描述

如果当前文件是 .json 格式,显示如下

在这里插入图片描述

虽然可以通过设置 xmake 的快捷键是 Ctrl + Shift + B,但这样也会丢失了vscode 的Ctrl + Shift + B灵活行。可以参考vscode官网 Get Started with C++ on Linux in Visual Studio Code配置tasks.json方法。

下面来通过配置tasks.json达到使用 Ctrl + Shift + B 快捷键的方法

关闭 所有打开的文档,点击 Terminal - Configure Tasks…

在这里插入图片描述

弹出的命令面板中 点击 Create tasks.json file from template

在这里插入图片描述

点击 Others Example to run an arbitrary extemal command

在这里插入图片描述

会在 .vscode 文件夹下自动创建 tasks.json 文件,并自动打开

在这里插入图片描述

修改配置文件的内容如下然后保存

{
    // See https://go.microsoft.com/fwlink/?LinkId=733558
    // for the documentation about the tasks.json format
    "version": "2.0.0",
    "tasks": [
        {
            "label": "xmake-b",           // 显示的名称
            "type": "shell",              // 脚本任务
            "command": "xmake",           // 指定运行的命令
            "args": [                     // xmake 命令行的参数
                "-v"                      // 打印详细, 根据个人习惯是否添加. 详细说明请参考xmake官方文档说明
            ],
            "group": {
                "kind": "build",         // 添加到构建分组中, 用于在命令面板中显示 
                "isDefault": false       // 默认是 false, 设置为true后, 按下快捷键 Ctrl + Shift + B 后, 不弹出命令面板直接运行该任务
            }
            
        }
    ]
}

避免xmake 构建的缓存影响查看的效果,先在终端中运行下面的命令,清空构建缓存

xmake clean
clear

在这里插入图片描述

清理完构建的缓存后,按下 Ctrl + Shift + B 快捷键,弹出的命令面板中显示tasks的任务列表,可以看到刚刚配置的 xmake-b 任务

在这里插入图片描述

点击 xmake-b 任务,

在这里插入图片描述

可以看到执行任务的构建命令 xmake -v,然后是编译的详细信息,如果不设置 -v 参数就和前面的构建输出信息一致。关于参数请查看 xmake 教程。

有时希望在构建任务前先执行一些其他事情,类似vs2013的构建前事件。比如在构建前先自动清理后再构建,那么可以在 task.json 文件中通过添加其他任务(也就是配置多任务都在 task.json 文件中), 然后通过 dependsOn 配置依赖的其他任务。 拷贝下面代码并粘贴 task.json 文件中

{
    // See https://go.microsoft.com/fwlink/?LinkId=733558
    // for the documentation about the tasks.json format
    "version": "2.0.0",
    "tasks": [
        {
            "label": "xmake-b",           // 显示的任务名称
            "type": "shell",              // 脚本任务
            "command": "xmake",           // 指定运行的命令
            "args": [                     // xmake 命令行的参数
                "-v"                      // 打印详细, 根据个人习惯是否添加. 详细说明请参考xmake官方文档说明
            ],
            "group": {
                "kind": "build",          // 添加到构建分组中, 用于在命令面板中显示 
                "isDefault": false        // 默认是 false, 设置为true后, 按下快捷键 Ctrl + Shift + B 后, 不弹出命令面板直接运行该任务
            },
            "dependsOn":[                 // 构建前先执行其他依赖任务集. 
                "stu-echo",               // stu-echo 显示 Hello task for echo!!!
                "xmake-c"                 // 执行 xmake-c 任务清理构建缓存
            ],           
        },
        {
            "label": "xmake-c",           // 显示的任务名称
            "type": "shell",              // 脚本任务
            "command": "xmake",           // 指定运行的命令
            "args": [                     // xmake 命令行的参数
                "c"                       // 清理构建缓存
            ],
            "group": "build",             // 添加到构建分组中, 用于在命令面板中显示
        },
        {
            "label": "stu-echo",          // 显示的任务名称
            "detail": "显示输出字符",      // 详细信息
            "type": "shell",              // 脚本任务
            "command": "echo",            // 指定运行的命令
            "args": [                     // echo 命令行的参数
                "Hello task for echo!!!"  // 显示 Hello task for echo!!!
            ],
            //"group": "build",           // 去掉分组, 表示这个任务不会出现在命令面板的构建列表中
        }
    ]
}

粘贴保存后再次 按下 Ctrl + Shift + B 快捷键,在命令面板的任务列中可以看到 xmake-bxmake-c 两个任务

在这里插入图片描述

选择 xmake-b 并执行

在这里插入图片描述

这里并没有看到显示 Hello task for echo!!! 信息,原因是执行 xmake 的命令创建了一个新的输出面板,原文的翻译 终端将被任务重新使用,按任意键将其关闭。按任意键后,可以看到执行了 stu-echo 并输出 Hello task for echo!!! 信息

在这里插入图片描述

配置运行调试

虽然通过按 F5快捷键或 xmake 工具栏 上的 debug 按钮可以启动调试,但是不能解决代码改变后自动编译后再运行调试。可以通过配置运行任务增加前置任务来解决。

点击 Run - Add Configuration

在这里插入图片描述

在弹出的命令面板中选择并点击 xmake

在这里插入图片描述

.vscode 文件夹下自动创建 launch.json 文件,并自动打开

在这里插入图片描述

打开 main.cpp 并增加输出 Hello launch.json

#include "foo.h"
#include <iostream>
#include <string>
#include <vector>

int main(int argc, char** argv) {
    std::cout << "add(1, 2) = " << add(1, 2) << std::endl;

    std::vector<std::string> stdvec = { "Hello", "vscode", "xmake", "C/C++", "!!!" };
    for (auto &&i : stdvec)
    {
        std::cout  << i << " ";
    }
    std::cout << std::endl;    
    
    std::cout << "Hello launch.json" << std::endl;    // 增加输出 Hello launch.json

    return 0;
}

保存后直接 按 F5 启动调试,发现并没有输出 Hello launch.json,原因是因为没有编译

在这里插入图片描述

在使用vs2013习惯了自动编译,通过在 launch.json 文件配置 preLaunchTask 执行前置的构建任务,内容修改如下

{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "type": "xmake",                // 调试类型 xmake
            "request": "launch",            // 请求方式是运行
            "name": "xmake-d",              // 名称, 修改一个好识别的
            "target": "target name",        // 目标名
            "cwd": "${workspaceFolder}",    // 工作目录
            "stopAtEntry": false,            // 默认是true, 设为true时程序将暂停在程序入口处,相当于在main上打断点
            "preLaunchTask": "xmake-b"      // 运行前执行 xmake-b 任务
        }
    ]
}

再次按 F5 启动调试,发现并输出 Hello launch.json

配置调试程序的参数

修改 main.cpp, 增加 输出 argv 的值

#include "foo.h"
#include <iostream>
#include <string>
#include <vector>

int main(int argc, char** argv) {

    std::cout << "开始打印程序的参数!" << std::endl;
    for (int i = 0; i < argc; ++i)
    {
        std::cout << "参数" << i << " " << argv[i]  << std::endl;            
    }
    std::cout << "结束打印程序的参数!" << std::endl;


    std::cout << "add(1, 2) = " << add(1, 2) << std::endl;

    std::vector<std::string> stdvec = { "Hello", "vscode", "xmake", "C/C++", "!!!" };
    for (auto &&i : stdvec)
    {
        std::cout  << i << " ";
    }
    std::cout << std::endl;    
    
    std::cout << "Hello launch.json" << std::endl;    

    return 0;
}

修改 launch.json 文件, 增加设置 argv 值,

{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [

        {
            "type": "xmake",                // 调试类型 xmake
            "request": "launch",            // 请求方式是运行
            "name": "xmake-d",              // 名称, 修改一个好识别的
            "target": "target name",        // 目标名
            "cwd": "${workspaceFolder}",    // 工作目录
            "args": [                       // 配置调试程序的运行参数
                "ABC",
                "BCD",
                "123",
                "Hello launch param"
            ],
            "stopAtEntry": false,            // 默认是true, 设为true时程序将暂停在程序入口处,相当于在main上打断点
            "preLaunchTask": "xmake-b"      // 运行前执行 xmake-b 任务
        }
    ]
}

F5 运行程序,程序打印输出了程序参数

在这里插入图片描述

附加进程调试

调试程序还有一种经常遇到的情况,就是程序已经运行了,无法用 启动调试 的方法进行调试,在vs2013中都是通过 附加进程 方式进行调试

Linux

修改 main.cpp, 新增加 std::cin >> waitAttach; 用于程序运行起来后等待输入字符,这是好通过 vscode进行附加进程操作, 内容如下

#include "foo.h"
#include <iostream>
#include <string>
#include <vector>

int main(int argc, char** argv) {

    std::cout << "开始打印程序的参数!" << std::endl;
    for (int i = 0; i < argc; ++i)
    {
        std::cout << "参数" << i << " " << argv[i]  << std::endl;            
    }
    std::cout << "结束打印程序的参数!" << std::endl;

    std::cout << "验证Attach功能" << std::endl;
    std::cout << "第一步, 在std::cin >> waitAttach;下一句打断点" << std::endl; 
    std::cout << "第二步, 启动eg_prjs程序" << std::endl; 
    std::cout << "第三步, 在调试界面 选择 cppvsdbg attech" << std::endl; 
    std::cout << "第四步, 点击绿色执行按钮" << std::endl;
    std::cout << "第五步, 附加 eg_prjs 进程. 然后在 eg_prjs 控制台界面中输入字符回车后, 会自动停止打断点处"<< std::endl;  
    std::cout << "等待输入:" << std::endl;
    std::string waitAttach;
    std::cin >> waitAttach;
    std::cout << "输入值是:" << waitAttach  << std::endl;  


    std::cout << "add(1, 2) = " << add(1, 2) << std::endl;

    std::vector<std::string> stdvec = { "Hello", "vscode", "xmake", "C/C++", "!!!" };
    for (auto &&i : stdvec)
    {
        std::cout  << i << " ";
    }
    std::cout << std::endl;    
    
    std::cout << "Hello launch.json" << std::endl;    

    return 0;
}

修改完一定要 重新编译

打开 launch.json 文件,点击 Add Configuration… 文件

在这里插入图片描述

在弹出的窗口界面中选择 C/C++: (gdb) Attach

注:本篇文档也适用 vscode + xmake + msvc开发环境的配置,需要选择 C/C++: (Windows) Attach

在这里插入图片描述

自动把 (gdb) Attach 配置增加到 launch.json 文件中

"program": "enter program name, for example ${workspaceFolder}/a.out",

修改为

"program": "${workspaceFolder}/build/linux/x86_64/debug/eg_prjs",

launch.json 文件内容如下

{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "name": "(gdb) Attach",
            "type": "cppdbg",
            "request": "attach",
            "program": "${workspaceFolder}/build/linux/x86_64/debug/eg_prjs", // 修改为程序运行的路径
            "MIMode": "gdb",
            "setupCommands": [
                {
                    "description": "Enable pretty-printing for gdb",
                    "text": "-enable-pretty-printing",
                    "ignoreFailures": true
                },
                {
                    "description": "Set Disassembly Flavor to Intel",
                    "text": "-gdb-set disassembly-flavor intel",
                    "ignoreFailures": true
                }
            ]
        },        

        {
            "type": "xmake",                // 调试类型 xmake
            "request": "launch",            // 请求方式是运行
            "name": "xmake-d",              // 名称, 修改一个好识别的
            "target": "target name",        // 目标名
            "cwd": "${workspaceFolder}",    // 工作目录
            "args": [                       // 配置调试程序的运行参数
                "ABC",
                "BCD",
                "123",
                "Hello launch param"
            ],
            "stopAtEntry": false,            // 默认是true, 设为true时程序将暂停在程序入口处,相当于在main上打断点
            "preLaunchTask": "xmake-b"      // 运行前执行 xmake-b 任务
        }
    ]
}

在命令终端中输入

xmake run

先运行起程序

在这里插入图片描述

程序运行到等待输入操作,接下来切换到调试界面,如下图所示,启动项中选择 (gdb) Attach,然后点击旁边的绿色按钮(标记3)启动附加进程

在这里插入图片描述

在弹出命令面板的搜索框张输入 eg_prjs,查找到运行的进程

在这里插入图片描述

双击 eg_prjs 进程,发现弹出错误提示窗(请忽略进程的id,因为是多次启动, 进程的id发生变化,这并不影响本文档操作流程步骤)

在这里插入图片描述

出现这个原因是应该是没有权限,也许在截图的过程中超时导致的。可以尝试开启 linux 系统上的 xmake的远程编译环境(该操作会创建token),然后按下快捷键 Ctrl + C 终止这个服务。如果还不起作用,在window上启动cmd命令窗口,也同样启动一下 xmake 的远程编译环境,在关闭。

暂时还不明白什么原因,待学习研究中。

在这里插入图片描述

这样操作后可以附加进程调试了,附上 linux 指令

  • 查看下运行的进程

    ps aux
    
  • kill进程

    kill 进程id
    

确保当前没有 eg_prjs 进行在运行。

这次多开一个终端窗口方便查看,通过终端 分割 按钮分割成两个终端窗口

在这里插入图片描述

分割后如下图所示,并在第2个终端窗口输入 xmake run 启动 程序,并进入等待输入操作

在这里插入图片描述

切换到调试模式再次选择 (gdb) Attach,然后点击旁边的绿色按钮(标记3)启动附加进程

在这里插入图片描述

选择要附加的进程 eg_prjs

在这里插入图片描述

启动附加进程后,终端界面被自动切换了,需要把刚才 xmake run 界面显示出来,在最右侧的终端列表中点击 xmake eg_prjs

在这里插入图片描述

在下图的标记1处,输入字符串后回车

在这里插入图片描述

可以看到,附加调试停止在断点处

在这里插入图片描述

按下 F5 键,终端输出窗口打印的消息是在 24 行之后执行的。(注: 虽然输入的是 Hello Test Attach 但打印输出的是 Hello 这是C++输入缓冲区问题,有关C/C++编程请自行学习)

Window

本文也适用window、 xmake、msvc。

添加附加进程任务选择 C/C++: (Windows) Attach

在这里插入图片描述

遇到的问题是有win11可以正常查询进程,有的报错 wmic 错误,是因为win11系统中没有 wmic 程序。而vscode 1.96.2 还在使用 wmic 这个命令获取系统进程列表,可以自行安装或等待微软官网后期升级改进。报错截图如下

在这里插入图片描述

报错解释

Visual Studio Code (VS Code) 在尝试通过Windows Management Instrumentation Command-line (WMIC) 工具进行附加调试时,未能在系统上找到WMIC。WMIC是一个命令行界面,用于访问Windows系统的WMI信息。VS Code需要WMIC来启动附加进程的调试会话。

微软官网资料: WMI 命令行 (WMIC) 实用程序 - Win32 apps | Microsoft Learn
可以参考《 真是奇哉怪也,Windows11居然不支持WMIC?(附离线安装方法及安装包) 》安装WMIC

数据结构Visualizers

说明

在远端主机(linux)上用户目录下没有找到 .vscode 文件夹,这是在远端主机没有安装过 vscode,一直都是通过vscode ssh插件远程访问,vscode在远端主机上仅安装了 .vscode-server

在这里插入图片描述

进入到扩展文件夹下查看到 ms-vscode.cpptools-1.22.11-linux-x64

cd .vscode-server/extensions/

在这里插入图片描述

在这里插入图片描述

查看 debugAdapters 文件下并没有 vsdbg 文件夹。从 git hub 下载 cpptools 的cpptools-linux-x64_1.22.11.vsix和cpptools-windows-x64_1.22.11.vsix后,通过压缩软件(vsix实际是zip格式)打开 debugAdapters 文件夹对比

在这里插入图片描述

可以看到,微软没有将C/C++插件Visualizer功能支持l到inux版本的插件上,期望以后添加。

对应linux上自定义数据结构的Visualizers需要在学习其他方法(本次先停一段落)。

window上Visualizer

下面以 window 平台基于 msvc 编译环境(vs2022)为例。

系统环境

操作系统 Windows 11

工作环境 vscodevs2022xmakegit

关闭远程

关闭linux的远程链接,如下图所示,先点击 SSH: 192.168.1.82,在弹出的命令面板中点击 Close Remote Connection

在这里插入图片描述

下载代码

与linux保持一致的文件夹(除根目录)结构,创建 D:/stu 文件夹并按照前面的记录将代码克隆到 D:/stu 文件夹下

在这里插入图片描述

通过 File - Open Folder…切换到 eg_prjs 文件夹

实现举例

打开 foo/inc/fool.h 文件,在文件后面添加如下代码,其中 FXXDateTime 自定义数据类,演示在调试时如何显示类信息

/**
 * brief 日期时间. 为了避免与常用的名称区分, 定义名称为 FXXDateTime
 * 成员变量就是 年/月/日 小时:分钟:秒
 */
class __export FXXDateTime
{
public:
    FXXDateTime(int year, int month, int day, int hour, int minute, int second);
    ~FXXDateTime();

/// 头文件除了提供API外, 更重要的是让使用者通过查看头文件就知道怎么使用(谁还看文档[文档该写还是的得写], 除非必要, 大家用微信有看过微信说明吗),
/// 那么使用者最关心的就是public部分, 根本就不看private部分, protected部分根据情况才看
/// 私有的放在前面, 在阅读的时候还要把代码拉到下面, 不觉的麻烦么. 
/// 我的理念是private部分放在类的最后面, 其次是protected部分, public放在类的最前面. 因为实际开发过程中我也从不看私有(除非必要).
/// 况且类似Qt库等做法, 把私有数据完全封装到一个data的结构体中, 类中只有一个data的指针, 继承类连data的指针都看不见.
/// 这里只是演示, 定义成员变量如下
/// @cond
private:
    int m_year;
    int m_month;
    int m_day;
    
    int m_hour;
    int m_minute;
    int m_second;
/// @endcond
};

打开 foo/inc/fool.cpp 文件,在文件后面添加如下代码

FXXDateTime::FXXDateTime(int year, int month, int day, int hour, int minute, int second)
    : m_year(year)
    , m_month(month)
    , m_day(day)
    , m_hour(hour)
    , m_minute(minute)
    , m_second(second)
{}

FXXDateTime::~FXXDateTime()
{
}

app/main.cpp 文件,添加在 main() 入口函数(为了好打断点)

#include "foo.h"
#include <iostream>
#include <string>
#include <vector>

int main(int argc, char** argv) {





    // 里 main 远点是为了腾出空间好查看 fdatetime 变量的调试信息
    FXXDateTime fdatetime(2024, 12, 22, 05, 53, 29); // 添加到这里, 在下面一句打断点, 调试运行后将鼠标悬停在fdatetime变量上

    std::cout << "开始打印程序的参数!" << std::endl;  // 打断点
    for (int i = 0; i < argc; ++i)
    {
        std::cout << "参数" << i << " " << argv[i]  << std::endl;            
    }
    std::cout << "结束打印程序的参数!" << std::endl;

    std::cout << "验证Attach功能" << std::endl;
    std::cout << "第一步, 在std::cin >> waitAttach;下一句打断点" << std::endl; 
    std::cout << "第二步, 启动eg_prjs程序" << std::endl; 
    std::cout << "第三步, 在调试界面 选择 cppvsdbg attech" << std::endl; 
    std::cout << "第四步, 点击绿色执行按钮" << std::endl;
    std::cout << "第五步, 附加 eg_prjs 进程. 然后在 eg_prjs 控制台界面中输入字符回车后, 会自动停止打断点处"<< std::endl;  
    std::cout << "等待输入:" << std::endl;
    std::string waitAttach;
    std::cin >> waitAttach;
    std::cout << "输入值是:" << waitAttach  << std::endl;  


    std::cout << "add(1, 2) = " << add(1, 2) << std::endl;

    std::vector<std::string> stdvec = { "Hello", "vscode", "xmake", "C/C++", "!!!" };
    for (auto &&i : stdvec)
    {
        std::cout  << i << " ";
    }
    std::cout << std::endl;    
    
    std::cout << "Hello launch.json" << std::endl;        


    return 0;
}

在这里插入图片描述

按下 F5 启动调试看看,需要注意的调试 快捷键 是否配置正确,检测是否和下图一致。也可手动编译、调试(没有直接按下 F5 编译和调试一起执行的方便,使用vs2022遗留的习惯)。请参考 配置运行调试 确保配置正确。

在这里插入图片描述

查看一下调试运行效果,记得鼠标悬停在 fdatatime 变量上才能看到标记2的信息

在这里插入图片描述

标记3的显示效果还是不能像 argc = 5 那样正常显示,接下来要做的就是如何让fdatetime像 argc = 5 那样显示

微软官方资料 创建 C++ 对象的自定义视图 - Visual Studio (Windows) | Microsoft Learn 使用 Natvis 框架在调试器中创建 C++ 对象的自定义视图。

[C++][调试技巧] VS 中的 Debugger Visualizers 使用指南 (.natvis文件)_vs debugger-优快云博客

本着开发程序管理一切的变更都要有版本控制和记录,那么首先在 eg_prjs 工程下创建一个文件夹,用于管理其他文件。

举例创建文件夹名为 natvis,如下图所示

在这里插入图片描述

natvis 文件夹下新建一个 eg_prjs.natvis 文件,内容如下

<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">

 <Type Name="FXXDateTime">
    <DisplayString>{{ {m_year}/{m_month}/{m_day} {m_hour}:{m_minute}:{m_second} }}</DisplayString>
    <Expand>
      <Item Name="[year]">m_year</Item>
      <Item Name="[month]">m_month</Item>
      <Item Name="[day]">m_day</Item>
      <Item Name="[hour]">m_hour</Item>
      <Item Name="[minute]">m_minute</Item>
      <Item Name="[second]">m_second</Item>      
    </Expand>
  </Type>

</AutoVisualizer>

确保调试快捷键切换回 xmake-d

在这里插入图片描述

按下 快捷键 Ctrl + Shift + B,命令面板中选择设置过的 xmake-b 启动编译

在这里插入图片描述

可以看到,正确执行设置的 xmake cxmake -v 命令。从编译信息看编译失败的原因是源文件的代码页问题,UTF-8 格式的文件需要 BOM 标签msvc才能较好的支持,linux 是不需要的,可能是为了兼容微软的规则,目前linux也支持编译带有 BOM 标签的文件,因此代码的文本格式建议都修改为带有 BOM 标签的 UTF-8

下面切换一下文本的编码格式。打开源码文件, 点击右下角 UTF-8,弹出的界面选择 Save with Encoding

在这里插入图片描述

选择 UTF-8 with BOM utf8bom

在这里插入图片描述

再次快捷键 Ctrl + Shift + B,命令面板中选择设置过的 xmake-b 启动编译

在这里插入图片描述

编译成功,打开 main.cpp 文件,在第15行打断点,启动调试。

注: xmake插件和vscode 协调性需要注意一下,通过快捷键 F5 启动自定义编译、调试运行任务有时失效,需要在命令面板中 通过 >XMake:Build>XMake:ReBuild 编译后在按 快捷键 F5 就会生效。

在这里插入图片描述

看到的效果还不是预期的,原因是还没有使用 eg_prjs.natvis 文件,把 eg_prjs.natvis 文件拷贝到

C:\Users\xxx\.vscode\extensions\ms-vscode.cpptools-1.22.11-win32-x64\debugAdapters\vsdbg\bin\Visualizers

文件夹下,xxx 是实际用户名, ms-vscode.cpptools-1.22.11-win32-x64 根据当前安装的版本决定

在这里插入图片描述

再次启动调试

在这里插入图片描述

可以看到,调试信息中关于 fdatetime 变量显示的信息是符合预期。

配置intellisense

创建工程

把eg_prjs中的foo模块作为一个外部库使用。在 stu_linux 文件夹下创建一个 eg_app 文件夹,如下图所示

在这里插入图片描述

在vscode中打开 eg_app 文件夹,并通过 >XMake:CreateProject - c++ - console 创建一个C++的控制台程序,目录结构如下图所示

在这里插入图片描述

引用foo模块

引用前面的 eg_prjs 工程下已经生成了 foo 模块并使用 foo 模块的 add 函数和 FXXDatetime 类。

修改 xmake.lua 配置文件

add_rules("mode.debug", "mode.release")

target("eg_app")
    add_includedirs("../eg_prjs/src/foo/inc")                          -- 增加头文件夹的路径
    if is_plat("linux") then                                           -- 是否是linux平台
        if is_mode("debug") then                                       -- 是否是调试模式
            add_linkdirs("../eg_prjs/build/linux/x86_64/debug")        -- 增加链接库的路径
            add_rpathdirs("/home/xx/stu/stu_linux/eg_prjs/build/linux/x86_64/debug")       -- 添加程序运行时动态库的加载搜索目录. 遇坑指南, 必须是绝对路径
        else 
            add_linkdirs("../eg_prjs/build/linux/x86_64/release")
            add_rpathdirs("/home/xx/stu/stu_linux/eg_prjs/build/linux/x86_64/release")       
        end
        add_links("libfoo.so")                                         -- 引用 foo 模块链接库
    end 
    set_kind("binary")
    add_files("src/*.cpp")

修改 main.cpp 文件

#include <foo.h>
#include <iostream>

int main(int argc, char** argv) {
    
    FXXDateTime fdatetime(2024, 12, 22, 05, 53, 29);  // 添加到这里, 在下面一句打断点, 调试运行后将鼠标悬停在fdatetime变量上
    std::cout << "add(1, 2) = " << add(1, 2) << std::endl;
    
    return 0;
}

点击构建

在这里插入图片描述

可以看到构建成功,但是标记3处, #include <foo.h> 未能正确解析(有红色波浪线提示),FXXDatetime 显示颜色提示也不正确

打开控制面板,选择 C/C++: Edit Configurations(JSON) ,也可以选择 C/C++: Edit Configurations(UI) 根据个人习惯来定

在这里插入图片描述

{
    "configurations": [
        {
            "name": "Linux",
            "includePath": [
                "${workspaceFolder}/**",
                "/home/xx/stu/stu_linux/eg_prjs/src/foo/inc/**"  // 增加 foo模块的头文件路径
            ],
            "defines": [],
            "compilerPath": "/usr/bin/clang",
            "cStandard": "c17",
            "cppStandard": "c++17",
            "intelliSenseMode": "linux-clang-x64"
        }
    ],
    "version": 4
}

在打开查看 main.cpp 文件,#include <foo.h>FXXDatetime 正确解析

在这里插入图片描述

运行程序,看到调用 foo 模块的 add 函数正常运行并输出结果3

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值