温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

用VSCode和CMake构建跨平台的C/C++开发环境的方法

发布时间:2021-06-17 10:18:30 来源:亿速云 阅读:193 作者:chen 栏目:开发技术

本篇内容介绍了“用VSCode和CMake构建跨平台的C/C++开发环境的方法”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

目录
  • 一、前言

  • 二、开发工具

  • 三、示例项目

  • 四、使用CMake

    • 4.1、如何引入一个第三方静态库

    • 4.2、动态库的加载问题

    • 4.3、utf-8编码格式的代码通过visual studio编译报错问题

    • 4.4、可执行文件的工作目录问题

    • 4.5、如何修改Mac上动态库的加载路径

  • 五、利用VSCode的task.json执行指定命令

    • 5.1、通过Ctrl+Shift+B执行CMake编译本地工程

    • 5.2、通过Ctrl+B构建与运行可执行文件

  • 六、利用VSCode的launch.json完成调试

    一、前言

    日前在学习制作LearnOpenGL教程的实战项目Breakout游戏时,希望能将这个小游戏开发成跨平台的,支持在多个平台运行。工欲善其事必先利其器,首先需要做的自然是搭建一个舒服的跨平台C/C++开发环境,所以这篇文章主要就是记录环境搭建的整个过程,踩到的一些坑,以及对应的解决办法。

    正文开始之前,先来阐述几个问题

    为什么选择使用VSCode

    • 实在用不习惯Visual Studio(也可能是用的太少了T▽T)

    • 代码编辑方面更喜欢用轻量级的编辑器,比如Sublime或者VSCodeVSCode

    • 确实比较强大好用,插件丰富

    为什么使用CMake

    • 通用的编译构建工具,跨平台的关键,一份代码,CMake可以针对不同的系统编译构建生成不同的项目工程

    • 源代码管理,编译更加方便(如果仅仅使用VSCode搭建开发环境,则每添加一个源文件,就要改动一下编译指令)

    最终实现的开发流程是怎样的VSCode编写代码快捷键Ctrl+Shift+B,调用CMake完成本地项目生成(Mac快捷键Command+Shift+B)快捷键Ctrl+B,完成项目的编译构建与运行(Mac快捷键Command+B)快捷键F5,完成项目的调试与运行(VSCode的F5调试运行,为了能够实现调试功能额外做了许多工作,所以启动会有些慢,因此在不需要调试的时候,直接使用Ctrl+B编译运行看效果会更快些)

    二、开发工具

    CMake与VSCodeCMake的获取,可以查看CMake官网

    VSCode的获取,可以查看VSCode官网

    关于CMake与VSCode如何安装,比较简单,网上也有很多教程,这里就不详细介绍了

    VSCode插件推荐安装在VSCode的Extensions面板中搜索下面的插件名即可,记得看清作者名,不要下错啦插件

    插件名作者描述
    C/C++Microsoft提供C/C++的代码提示,跳转,调试等诸多功能,官方出品,基本是C/C++开发必备了
    CMaketwxs提供CMake语法的高亮显示以及代码段提示

    三、示例项目

    这里给出例子工程的文件目录情况,并不完整,但具有一定的代表性,不仅涉及源代码的编译,同样包含了静态库,动态库的加载,以及资源文件的读取等问题

    Breakout

    ├── 3rd                         // 第三方库

    │    ├── glfw                   // 一个静态库目录

    │    ├── irrKlang-1.6.0         // 一个动态库目录

    │    └── FindIrrKlang.cmake     // cmake文件

    ├── resources                   // 资源目录

    │    ├── textures               // 存放贴图文件

    │    └── shaders                // 存放shader文件

    ├── src                         // 源代码目录

    │    ├── game                   // 源代码子目录

    │    │    ├── game.h

    │    │    └── game.cc

    │    └── main.cc    

    └── CMakeLists.txt              // cmake文件

    四、使用CMake

    本文仅着重介绍为了完成示例项目开发,解决特定问题而使用的一些cmake语句

    cmake的所有语句都写在CMakeLists.txt中,cmake会根据该文件中的配置完成最终的编译,构建,打包,测试等一系列任务

    一个简单的CMakeLists.txt如下所示,完整的文件可以查看这里

    # cmake最低版本号要求

    cmake_minimum_required (VERSION 2.8)

    # 设置PROJECT_NAME变量

    set(PROJECT_NAME Breakout)

    # 设置工程名

    project (${PROJECT_NAME})

    # 查找当前目录下的所有源文件并存入DIR_SRCS变量

    aux_source_directory(src DIR_SRCS)

    # 添加一个可编译的目标到工程

    add_executable (${PROJECT_NAME} ${DIR_SRCS})

    如何编译一个文件夹下的所有源代码

    在开发过程中,由于架构设计或是为了便于管理与查找,源文件一般会根据不同的功能存放在不同的文件夹中,文件夹中又可能嵌入文件夹,所以需要一条语句能够获取所有的源文件进行编译,而不用每新创建一个源文件,就修改一次编译指令

    # 递归列出所有源文件

    file (GLOB_RECURSE SOURCE_FILES *.cc)

    # 添加一个可编译的目标到工程

    add_executable (${PROJECT_NAME} ${SOURCE_FILES})

    上面这条file命令会递归列出所有.cc文件,并存入SOURCE_FILES变量,然后将SOURCE_FILES表示的所有.cc文件添加到目标即可,从而解决多源文件编译问题

    4.1、如何引入一个第三方静态库

    为了不重复造轮子,开发中不可避免的要引入其他第三方库。正常情况下,这个第三方库也会是一个CMake工程(或是库的开发者直接提供已经编译好的库)

    以示例项目引入的glfw库为例

    1.添加submodule引入glfw(glfw正好是github上的一个开源项目),或是直接将第三方库的源码放到自己的目录中

    git submodule add https://github.com/glfw/glfw.git

    2.使用add_subdirectory命令,将glfw所在的文件夹添加到编译的任务列表中

    # 保证glfw dir被编译

    add_subdirectory (${GLFW_DIR})

    3.将glfw的头文件目录添加到头文件搜索路径中

    # 添加头文件搜索路径

    include_directories (${GLFW_DIR}/include)

    4.链接glfw库,target_link_libraries命令用来链接目标与库文件,第一个参数就是我们的构建目标,后面可以跟多个参数,来表示链接多个库

    # 添加链接库

    target_link_libraries (${PROJECT_NAME} glfw)

    4.2、动态库的加载问题

    以示例项目引入的irrKlang库为例,它并不是一个开源项目,不过好在它提供了已经在多个平台上编译好的库,所以我们需要根据不同的平台来设置引入不同的库文件

    1.利用find_package引入外部依赖包,它可以帮我们找到官方预定义的许多依赖包模块,当未在官方预定义的依赖中找到时,会再查找FindXXX.cmake文件,执行该文件从而找到XXX库。

    # IrrKlang

    find_package (IrrKlang REQUIRED)

    2.先新建FindIrrKlang.cmake文件,由它来负责具体的irrKlang库加载。部分语句如下所示,主要是根据当前平台的不同,设置不同的头文件路径,库路径,库所在目录等变量。用到的find_library语句可以实现直接根据库的base name(即不需要lib,so等),找到对应的库,并存入IRRKLANG_LIBRARY变量

    find_path(IRRKLANG_INCLUDE_DIR NAMES irrKlang.h PATHS "${3RD_DIR}/irrKlang-1.6.0/include")
    IF(WIN32)  # win32平台
        if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
            message(STATUS "Using MSVC")
            set (IRRKLANG_LIB_DIR "${3RD_DIR}/irrKlang-1.6.0/lib/Win32-visualStudio")
            set (IRRKLANG_BIN_DIR "${3RD_DIR}/irrKlang-1.6.0/bin/Win32-visualStudio")
            find_library(IRRKLANG_LIBRARY NAMES irrKlang PATHS ${IRRKLANG_LIB_DIR})
        elseif("${CMAKE_CXX_COMPILED_ID}" STREQUAL "GNU")
            message(STATUS "Using GCC")
            set (IRRKLANG_LIB_DIR "${3RD_DIR}/irrKlang-1.6.0/lib/Win32-gcc")
            set (IRRKLANG_BIN_DIR "${3RD_DIR}/irrKlang-1.6.0/bin/Win32-gcc")
            find_library(IRRKLANG_LIBRARY NAMES libirrKlang.a PATHS ${IRRKLANG_LIB_DIR})
        endif()
    
    elseif(APPLE)  # mac平台
        set (IRRKLANG_BIN_DIR "${3RD_DIR}/irrKlang-1.6.0/bin/macosx-gcc")
        find_library(IRRKLANG_LIBRARY NAMES libirrklang.dylib PATHS "${3RD_DIR}/irrKlang-1.6.0/bin/macosx-gcc")
    
    elseif(UNIX AND NOT APPLE)  # 等同于linux平台
        set (IRRKLANG_BIN_DIR "${3RD_DIR}/irrKlang-1.6.0/bin/inux-gcc")
        find_library(IRRKLANG_LIBRARY NAMES IrrKlang PATHS "${3RD_DIR}/irrKlang-1.6.0/bin/linux-gcc")
    
    endif()

    3.将找到的irrKlan头文件添加到头文件搜索路径中

    include_directories (${IRRKLANG_INCLUDE_DIR})

    4.链接irrKlang库

    # 添加链接库

    target_link_libraries (${PROJECT_NAME} glfw ${IRRKLANG_LIBRARY})

    4.3、utf-8编码格式的代码通过visual studio编译报错问题

    跨平台的代码,一般使用utf-8编码格式的代码,更加通用,也可以保证在MacOS或者Linux平台上的正常编译。

    但是visual studio默认编译文件的编码是utf-8 with bom,在没有中文的情况下,直接编译是没有问题的,然而当源文件含有中文时(比如中文注释),则可能会出现异常,报一些莫名其妙的语法错误。解决办法是通过CMake语句通知MSVC编译时采用utf-8编码

    # 设置MSVC编译编码

    add_compile_options("$<$<CXX_COMPILER_ID:MSVC>:/source-charset:utf-8>")

    4.4、可执行文件的工作目录问题

    当编译构建生成可执行文件后,我们希望可以直接通过命令行命令启动可执行文件来查看效果。但是由于工作目录的问题,可能会导致出现资源文件找不到,或者库加载失败问题

    对于visual studio工程,可以通过CMake语句设置其工作目录,但是这个工作目录仅在通过visual studio调试启动时才会生效,但对于直接启动可执行文件的情况仍然是没用的

    # 设置工作目录

    set_property(TARGET ${PROJECT_NAME} PROPERTY VS_DEBUGGER_WORKING_DIRECTORY 

        ${CMAKE_SOURCE_DIR}/resources

    )

    所以只能将资源文件目录放置到可执行文件所在目录下,以保证一定能加载到需要的资源,这可以通过CMake的自定义命令实现

    # 复制资源文件到工作目录

    add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD

        COMMAND ${CMAKE_COMMAND} -E copy_directory

        ${CMAKE_SOURCE_DIR}/resources

        $<TARGET_FILE_DIR:${PROJECT_NAME}>/resources

    )

    这条语句的功能是在编译结束后,将指定的资源目录复制到生成的可执行文件所在的目录。

    同样的,对于一些动态库,比如dylib,dll等也需要复制,不过注意最好在编译前就将它们复制到目标目录,使用PRE_BUILD指明命令执行的时机

    # 复制库到工作目录

    add_custom_command(TARGET ${PROJECT_NAME} PRE_BUILD

        COMMAND ${CMAKE_COMMAND} -E copy_directory

        ${IRRKLANG_BIN_DIR}

        $<TARGET_FILE_DIR:${PROJECT_NAME}>

    )

    4.5、如何修改Mac上动态库的加载路径

    在Mac上启动可执行文件时,一直遇到一个动态库无法加载的报错

    dyld: Library not loaded: /usr/local/lib/libirrklang.dylib

      Referenced from: ...

      Reason: image not found

    这是由于在编译生成动态库时,可以指定动态库的加载路径,比如我们引入的libirrklang库默认会到/usr/local/lib目录下查找dylib文件

    简单的解决方式,自然是通过install命令,将libirrklang.dylib文件安装到/usr/local/lib目录下,不过为了不“污染”其他目录,更希望可执行文件加载的是我们放置在其所在目录下的libirrklang.dylib文件。为了实现这个目标,我们添加下面的CMake语句

    if(APPLE)

        add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD

            COMMAND install_name_tool -change /usr/local/lib/libirrklang.dylib @executable_path/libirrklang.dylib ${PROJECT_NAME}

        )

    endif()

    判断如果是Mac平台,则通过add_custom_command调用install_name_tool命令,来修改应该应用程序对动态库的查找路径。其中的@executable_path就表示可执行文件所在目录

    五、利用VSCode的task.json执行指定命令

    CMake的配置文件已经基本准备完毕了,接下来就是怎样结合VSCode更方便的调用CMake的问题了

    5.1、通过Ctrl+Shift+B执行CMake编译本地工程

    Ctrl+Shift+B是VSCode调用task的默认快捷键,task定义在.vscode目录下的task.json文件中,一般使用task来完成编译等任务,VSCode任务系统提供了丰富的参数配置,我们可以利用它完成很多自定义任务

    定义一个task,执行cmake ..命令,完成本地项目的编译生成。Linux会生成Makefile,MacOS生成Makefile或Xcode工程,Windows下生成Visual Studio工程。注意这里的..表示的是上层目录,因为我们会在项目根目录下新建一个build文件夹,然后在这个文件夹内完成一系列的编译工作,这样cmake生成的中间文件都在build目录,不会“污染”开发目录(将build目录加入.gitignorej即可忽略CMake所产生的所有中间文件),在编译出问题的时候也可以直接删除buidl目录重新编译

    {
        "label": "cmake",  // task的名字
        "type": "shell",
        "command": "cmake",
        "args": [
            // "-DCMAKE_BUILD_TYPE=${input:CMAKE_BUILD_TYPE}",
            ".."
        ],
        "options": {
            "cwd": "build"  // 表示当前执行目录build文件夹
        },
        "group": "build",
        "presentation": {  // 一些控制台显示配置
            "echo": true,
            "reveal": "always",  
            "focus": false,
            "panel": "shared",
            "showReuseMessage": true,
            "clear": true
        },
        // Use the standard MS compiler pattern to detect errors, warnings and infos
        "problemMatcher": "$msCompile",
        "dependsOn":["mkbuild"]  // 依赖的任务,在本任务执行前先执行mkbuild任务
    }

    cmake ..命令执行前,先通过task执行mkdir新建build文件夹。其中通过windows参数,区分不同平台设置不同的参数

    {
        "label": "mkbuild",
        "type": "shell",
        "command": "mkdir",  // 调用的命令
        "args": [  // 命令参数
            "-p",
            "build"
        ],
        "windows":{  // windows平台使用mkdir -Force build新建文件夹
            "args": [
                "-Force",
                "build"
            ]
        },
        "group": "build",
        "presentation": {
            "echo": true,
            "reveal": "always",
            "focus": false,
            "panel": "shared",
            "showReuseMessage": true,
            "clear": true
        },
        // Use the standard MS compiler pattern to detect errors, warnings and infos
        "problemMatcher": "$msCompile",
    }

    当按下Ctrl+Shift+B时,VSCode将弹出所有可执行的task,选择执行cmake task即可。由于定义了依赖(dependsOn),在cmak task执行前,将自动先执行mkbuild task

    用VSCode和CMake构建跨平台的C/C++开发环境的方法

    5.2、通过Ctrl+B构建与运行可执行文件

    在启动可执行文件前,先通过cmake --build .构建生成可执行文件

    {
        "label": "compile",
        "type": "shell",
        "command": "cmake --build .",
        "options": {
            "cwd": "build"
        },
        "group": "build",
        "presentation": {
            // Reveal the output only if unrecognized errors occur.
            "reveal": "always",
            "clear": true
        },
        // Use the standard MS compiler pattern to detect errors, warnings and infos
        "problemMatcher": "$msCompile"
        }

    在可执行文件生成后,通过task启动可执行文件。其中的${workspaceFolderBasename}是VSCode的内置变量,表示工程名。更多的内置变量介绍可以查看这里

    {
        "label": "run",
        "type": "shell",
        "command": "./${workspaceFolderBasename}",
        "group": "build",
        "presentation": {
            "echo": true,
            "reveal": "always",
            "focus": false,
            "panel": "shared",
            "showReuseMessage": true,
            "clear": true
        },
        "options": {
            "cwd": "build" 
        },
        "windows":{
            "options": {
                "cwd": "build/Debug"  // windows visual studio项目会默认多生成Debug/Release目录
            },
        },
        // Use the standard MS compiler pattern to detect errors, warnings and infos
        "problemMatcher": "$msCompile",
        "dependsOn":["compile"]  // 在run任务执行前先执行compile任务,确保修改的代码生效
    }

    通过Ctrl+B直接调用run任务。由于编译运行这个任务经常要用到,如果依然通过Ctrl+Shift+B弹出所有任务,再进一步选择就会有些麻烦,所以定义一个快捷键(读者可以用同样的方法设置自己喜欢的快捷键)来直接运行run任务打开VSCode快捷键设置

    用VSCode和CMake构建跨平台的C/C++开发环境的方法

    在弹出的界面中输入“run build task”搜索,并修改其快捷键

    用VSCode和CMake构建跨平台的C/C++开发环境的方法

    点击右上角的翻转按钮,进入快捷键文件配置

    用VSCode和CMake构建跨平台的C/C++开发环境的方法

    在打开的文件中,为刚配置的快捷键添加参数,告诉它直接启动名字叫“run”的task

    {
        "key": "ctrl+b",
        "command": "workbench.action.tasks.runTask",
        "args" : "run"
    }

    六、利用VSCode的launch.json完成调试

    VSCode通过.vscode目录下的launch.json实现对多个平台多种语言的调试支持

    通过F5完成项目的调试与运行配置launch.json文件以支持C/C++项目的调试

    {
        // 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": "Launch Debug", //名称
                "type": "cppdbg", //调试类型,除使用msvc进行调试外,均为该类型
                "request": "launch",
                "program": "${workspaceFolder}/build/${workspaceFolderBasename}", //指定C/C++程序位置
                "args": [], //指定运行参数
                "stopAtEntry": false,
                "cwd": "${workspaceFolder}/build", //指定工作目录
                "preLaunchTask": "compile", //在调试前会先调用这个task编译构建程序
                "environment": [],
                "externalConsole": false,
                "osx": { //macOS的特定配置
                    // "miDebuggerPath": "/Applications/Xcode.app/Contents/Developer/usr/bin/lldb-mi", //修改使用的lldb-mi,一般不需要修改
                    "MIMode": "lldb" //指定使用lldb进行调试
                },
                "linux": { //linux的特定配置
                    "MIMode": "gdb", //指定使用gdb调试
                    "setupCommands": [
                        {
                            "description": "Enable pretty-printing for gdb",
                            "text": "-enable-pretty-printing",
                            "ignoreFailures": true
                        }
                    ]
                },
                "windows": { //windows的特定配置
                    "type": "cppvsdbg", //指定使用msvc进行调试
                    "program": "${workspaceFolder}/build/Debug/${workspaceFolderBasename}.exe", //指定C/C++程序位置
                }
            }
        ]
    }

    F5是VSCode调试运行的默认快捷键,不需要额外配置,启动调试后的界面如下所示

    用VSCode和CMake构建跨平台的C/C++开发环境的方法

    通过使用本地工具完成调试由于cmake在windows平台会默认生成Visual Stduio工程,所以也可以直接打开生成的解决方案,通过Visual Studio进行调试Mac平台可以使用cmake .. -GXcode命令指定生成XCode工程,然后通过XCode进行调试

    “用VSCode和CMake构建跨平台的C/C++开发环境的方法”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注亿速云网站,小编将为大家输出更多高质量的实用文章!

    向AI问一下细节

    免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

    AI