创建一个CMake+GTK的项目,并配置vscode+GDB调试

创建一个CMake+GTK的项目,并配置vscode+GDB调试

概述

通过一步步创建一个基于C语言和CMake的GTK项目,来熟悉一个C语言项目的创建过程和最基本的项目结构。 之后会配置一套基于GDB和vscode的调试环境,并简单讲一下如何在这种调试环境下调试代码

编写CMakeLists.txt

指定cmake的最小版本,我本地安装的是3.26的版本,所以这边就设置为3.10了,像3.10往上的版本都不会有什么问题

scss 复制代码
cmake_minimum_required(VERSION 3.10)

配置项目名称

scss 复制代码
project(GTKCTest)

project命令中除了可以配置项目的名称,还可以配置项目的语言环境、版本、说明信息以及官方首页地址

配置C语言标准版本以及cmake执行时的调试参数

swift 复制代码
set(CMAKE_C_STANDARD 99)
set(CMAKE_C_FLAGS "-Wall -g -O0")

set函数是在配置本次运行时的环境变量 这里配置了CMAKE_C_STANDARD和CMAKE_C_FLAGS两个环境变量 CMAKE_C_STANDARD是使用C编译时,使用的C的标准版本,这里指定了这个项目所使用的C语言的标准版本为99 CMAKE_C_FLAGS是使用C编译时,使用的编译器选项, -Wall是指编译C语言代码时启用所有的警告, -g告诉C语言编译器在编译时生成调试信息, -O0是在告诉C语言编译器在编译时不要做优化处理,禁止编译器进行编译过程中的优化,是为了能在调试时看到更清晰的代码

接下来是配置包管理器和依赖包

scss 复制代码
find_package(PkgConfig REQUIRED)
pkg_check_modules(GTK3 REQUIRED gtk+-3.0)

这里我们先声明了包管理器PkgConfig是必要的依赖 然后我们用PkgConfig去检查本地开发环境时是否有安装3.0版本的gtk+依赖包

接下来配置依赖包头文件、连接库和依赖包需要的编译器选项

scss 复制代码
include_directories(${GTK3_INCLUDE_DIRS})
link_directories(${GTK3_LIBRARY_DIRS})
add_definitions(${GTK3_CFLAGS_OTHER})

这里我们配置了GTK3的头文件地址、连接库地址、和编译器选项 除了依赖包的头文件地址需要配置,我们项目中的依赖库地址也要配置 比如我们在项目中创建一个src/include路径,在这个路径下放我们所有的依赖包 就可以这样配置一下

scss 复制代码
include_directories(src/include)

接下来我们使用file方法,将src目录下的所有c文件,都添加到一个变量中 并且我们要用GLOB_RECURSE参数,来指定使用递归方式来进行文件匹配

erlang 复制代码
file(GLOB_RECURSE SOURCES "src/*.c" "src/**/*.c")

这样我们就把src下所有的c文件都写入了SOURCES变量,之后我们就可以使用SOURCES变量来编译

scss 复制代码
add_executable(GTKCTest ${SOURCES})

这里的GTKCTest是我们编译出来的可执行文件的名称

最后,我们把GTK3的库链接到编译产物身上

scss 复制代码
target_link_libraries(GTKCTest ${GTK3_LIBRARIES})

到这我们的CMakeLists.txt文件就差不多了, 然后接下来我们就可以开始写代码

编写main.c和main.h

我们在src目录下创建了main.c并在src/include目录下创建了main.h文件

首先我们先在main.h中创建头文件保护,并引入gtk

arduino 复制代码
#ifndef MAIN_H
#define MAIN_H

#include <gtk/gtk.h>

#endif

MAIN_H是我们创建的一个全局变量,他用来保护这个头文件中的声明不被多次执行,避免造成内存上的浪费

接下来我们开始编写main.c文件,在main.c文件中,我们先引入main.h 接着创建一个主函数,并在主函数中创建GTK窗口,在窗口关闭之后退出程序

scss 复制代码
#include "main.h"

static void on_window_closed(GtkWidget* widget, gpointer data)
{
    g_print("Window is closed\n");
    gtk_main_quit();
}

int main(int args, char** argv)
{
    gtk_init(&args, &argv);

    GtkWidget* main_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);

    gtk_window_set_title(GTK_WINDOW(main_window), "Hello World!");
    gtk_window_set_default_size(GTK_WINDOW(main_window), 800, 600);

    g_signal_connect(G_OBJECT(main_window), "delete_event", G_CALLBACK(on_window_closed), NULL);

    gtk_widget_show(GTK_WIDGET(main_window));

    gtk_main();
    return 0;
}

然后我们在项目目录下创建build目录,cd到build下,执行cmake..,再执行make 我们就可以在build目录下找到编译生成的GTKCTest可执行文件了,运行他就可以看到我们创建的窗口了,并且在窗口关闭之后可以看到程序进程也退出了 接下来我们可以把一些变量放在头文件中声明以及定义,然后在c文件中使用,比如像窗口的标题、尺寸以及窗口本身,我们都可以放在h文件中声明,标题和尺寸则可以直接在h文件中定义赋值。

ini 复制代码
char *MAIN_WINDOW_TITLE = "Hello World!";

int MAIN_WINDOW_WIDTH = 800;
int MAIN_WINDOW_HEIGHT = 600;

GtkWidget* MAIN_WINDOW;
scss 复制代码
MAIN_WINDOW = gtk_window_new(GTK_WINDOW_TOPLEVEL);

gtk_window_set_title(GTK_WINDOW(MAIN_WINDOW), MAIN_WINDOW_TITLE);
gtk_window_set_default_size(GTK_WINDOW(MAIN_WINDOW), MAIN_WINDOW_WIDTH, MAIN_WINDOW_HEIGHT);

g_signal_connect(G_OBJECT(MAIN_WINDOW), "delete_event", G_CALLBACK(on_window_closed), NULL);

gtk_widget_show(GTK_WIDGET(MAIN_WINDOW));

这样做的好处呢,就是我们以后如果要在其他的c文件中去写一些需要用到窗口属性,或者需要控制窗口的逻辑时,我们就可以直接把这些变量引入进来,直接去使用或者重新赋值。

配置vscode gdb调试环境

如果我们每次都从命令行中执行编译、启动命令,就感觉有点繁琐,那我们可以利用vscode的launch,来做一些自动化的配置 首先我们先在项目的根目录下创建一个.vsocde文件夹,并在文件夹下创建launch.json和tasks.json两个文件 首先我们先在tasks.json文件中配置cmake和make的任务

bash 复制代码
{
    "version": "2.0.0",
    "tasks": [
        {
            "label": "cmake",
            "options": {
                "cwd": "${workspaceFolder}/build"
            },
            "type": "shell",
            "command": "cmake .."
        },
        {
            "label": "make",
            "options": {
                "cwd": "${workspaceFolder}/build"
            },
            "type": "shell",
            "command": "make",
            "dependsOn": ["cmake"]
        }
    ]
}

这里我们用了dependsOn属性,让make执行时,先去执行cmake 接着我们来编写launch.json文件,我们先写一个RUN的launch,确保我们的task和程序都可以正常运行

bash 复制代码
{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "RUN",
            "type": "cppdbg",
            "request": "launch",
            "cwd": "${workspaceFolder}/build",
            "program": "${workspaceFolder}/build/GTKCTest",
            "preLaunchTask": "make"
        }
    ]
}

这里我们用preLaunchTask告诉vscode,执行这个launch之前,先执行make的任务,这样就可以把cmake、make、run连成一条线 接着我们在vscode的Run and Debug中找到刚才编写的Run Launch 然后运行一下 程序可以正常运行 接着我们在写一个GDB的Launch

bash 复制代码
{
    "name": "GDB",
    "type": "cppdbg",
    "request": "launch",
    "program": "${workspaceFolder}/build/GTKCTest",
    "args": [],
    "stopAtEntry": true,
    "cwd": "${workspaceFolder}/build",
    "environment": [],
    "externalConsole": false,
    "MIMode": "gdb",
    "miDebuggerPath": "/usr/bin/gdb",
    "preLaunchTask": "make"
}

这里同样指定了preLaunchTask为make 同时也是用了MIMode和miDebuggerPath,指定了gdb作为我们的调试工具 之后我们就可以使用vscode来调试我们的Gtk项目了

相关推荐
zfenggo17 分钟前
c/c++ 无法跳转定义
c语言·开发语言·c++
图灵猿20 分钟前
【Lua之·Lua与C/C++交互·Lua CAPI访问栈操作】
c语言·c++·lua
A懿轩A1 小时前
C/C++ 数据结构与算法【树和二叉树】 树和二叉树,二叉树先中后序遍历详细解析【日常学习,考研必备】带图+详细代码
c语言·数据结构·c++·学习·二叉树·
hjxxlsx1 小时前
探索 C++ 自定义函数的深度与广度
开发语言·c++
lijiachang0307182 小时前
设计模式(一):单例模式
c++·笔记·学习·程序人生·单例模式·设计模式·大学生
<但凡.2 小时前
题海拾贝:蓝桥杯 2020 省AB 乘法表
c++·算法·蓝桥杯
DogDaoDao3 小时前
leetcode 面试经典 150 题:矩阵置零
数据结构·c++·leetcode·面试·矩阵·二维数组·矩阵置零
计科土狗4 小时前
前缀和与差分
c++·算法
午言若5 小时前
MYSQL 架构
c++·mysql
羑悻的小杀马特7 小时前
【AIGC篇】畅谈游戏开发设计中AIGC所发挥的不可或缺的作用
c++·人工智能·aigc·游戏开发