VS Code Note 02

用VSCode開發C++

在現代軟體開發中,跨平台開發的重要性日益增長,尤其在 C++ 領域,由於其高效能和靈活性,使其成為許多應用程式的首選語言。在這樣的背景下,Visual Studio Code憑藉其輕量、跨平台、功能強大的特點,成為越來越多開發者的首選開發環境。VSCode不僅支援多種語言,還能夠透過擴充套件實現如 IDE 般的開發體驗,並提供與 Git 整合、遠端開發等功能,使開發者能更高效地管理跨平台專案。

在實際開發中,根據專案需求,開發者可能需要編譯出對應不同作業系統的動態連結庫 (DLL)。對於Windows系統,可以透過MinGW (Minimalist GNU for Windows) 來編譯DLL,而對於Linux系統,則可以利用WSL結合GCC來生成適用於Linux的動態庫。這樣的架構使得開發者能夠在同一個環境中同時開發並測試針對不同平台的程式碼,從而提高了工作效率並減少了環境切換所帶來的麻煩。

安裝編譯器

安裝方式可以按照VSCode的文檔說明來安裝,文檔上在Windows上有分別介紹使用MinGWWSLMicrosoft Visual C++ (MSVC) 這三種方式來安裝編譯器。

在安裝之前要記得在VSCode擴充套件中安裝C/C++擴充功能的套件:

MinGW

可以按照文檔上所推薦的方式,透過MSYS2來安裝MinGW,安裝好MSYS2後打開終端執行下面這段

pacman -S --needed base-devel mingw-w64-ucrt-x86_64-toolchain

按下Enter接受 toolchain 群組中的預設套件數量:

直接安裝就可以了:

也可以透過MinGW Installation Manager Setup Tool或是MinGW的網站來安裝MinGW,安裝後將C:\msys64\ucrt64\bin加入環境變數中:

加入環境變數後,可以在terminal上執行以下指令確定可以執行:

gcc --version
g++ --version
gdb --version

在VSCode中點選右上角執行C/C++檔案後選擇用g++來建置及偵錯:

WSL

在Windows的WSL安裝完Ubuntu後,開啟WSL的Bash Shell更新一下Ubuntu再安裝GNU編譯器工具和GDB偵錯程式:

sudo apt-get update && sudo apt-get dist-upgrade

sudo apt-get install build-essential gdb

可以尋找g++和gdb來驗證安裝是否成功。如果whereis命令未傳回檔案名稱,請嘗試再次執行更新命令:

whereis g++
whereis gdb

安裝WSL的延伸模組:

安裝完後可以從VSCode的左下角連線到WSL裡面:

在Ubuntu的terminal中執行以下指令,確定可以執行:

whereis g++
whereis gdb

在VSCode中點選右上角執行C/C++檔案後選擇一樣用g++來建置及偵錯:

MSVC

如果選擇不安裝完整Visual Studio IDE的情況下,可以在Visual Studio最下面下載Build Tools for Visual Studio 2022:

勾選使用 C++ 進行桌面開發的選項,並選取安裝:

打開Developer Command Prompt,執行cl確定可以執行,之後如果要使用cl來編譯執行,要記得使用這個terminal輸入code來啟動VSCode才能編譯執行:

在VSCode中點選右上角執行C/C++檔案後選擇一樣cl.exe來建置及偵錯:

如果不在Developer Command Prompt中來啟動VSCode的話,需要在tasks.json的task前新增以下的段落:

{
  "version": "2.0.0",
  "windows": {
    "options": {
      "shell": {
        "executable": "cmd.exe",
        "args": [
          "/C",
          // The path to VsDevCmd.bat depends on the version of Visual Studio you have installed.
          "\"C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/Common7/Tools/VsDevCmd.bat\"",
          "&&"
        ]
      }
    }
  },
  "tasks": [
    ...
  ]
}

VsDevCmd.bat的路徑可能會有所不同,可以透過dir "\VsDevCmd*" /s來找到VsDevCmd.bat的的位置。

tasks.json

tasks.json主要用來定義執行各種任務的配置,這些任務通常是指編譯、清理、打包、執行自動化測試等操作。VSCode透過這個檔案來告訴編譯器或其他工具該如何運行,例如使用哪個編譯器、輸入和輸出檔案的位置、編譯選項等。

在第一次執行程式時,C++擴充功能會建立一個tasks.json檔案,可以在專案的.vscode資料夾中找到它,tasks.json會儲存建置組態,新tasks.json檔案應該類似於以下的JSON:

{
    "tasks": [
        {
            "type": "cppbuild",
            "label": "C/C++: g++.exe 建置使用中檔案",
            "command": "C:\\msys64\\ucrt64\\bin\\g++.exe",
            "args": [
                "-fdiagnostics-color=always",
                "-g",
                "${file}",
                "-o",
                "${fileDirname}\\${fileBasenameNoExtension}.exe"
            ],
            "options": {
                "cwd": "${fileDirname}"
            },
            "problemMatcher": [
                "$gcc"
            ],
            "group": {
                "kind": "build",
                "isDefault": true
            },
            "detail": "偵錯工具產生的工作。"
        }
    ],
    "version": "2.0.0"
}

這是VSCode中tasks.json文件的一個設定範例,它主要負責配置編譯C/C++程式的任務。

  • "tasks": 這個鍵是任務列表,裡面可以定義多個任務 (tasks),每個任務都表示一個具體的操作,例如編譯程式碼。
  • "version": "2.0.0": 這表示tasks.json文件的版本,VSCode使用這個版本來理解文件格式。

"tasks"中的每個key:

  • "type": "cppbuild": 定義了任務的類型,cppbuild是用來標示這是一個 C++編譯任務,VSCode會針對這種類型的任務提供相應的支援,例如 C++ 擴充套件會監控這種編譯過程。
  • "label": "C/C++: g++.exe 建置使用中檔案": 任務的標籤,用來描述這個任務的用途可以隨意命名,會在VSCode的任務列表中顯示出來,這個標籤表明這個任務是使用g++.exe編譯當前開啟的C++檔案。
  • "command": "C:\\msys64\\ucrt64\\bin\\g++.exe": 這是實際執行的命令,這裡指的是MSYS2的g++編譯器,它位於指定的路徑下(C:\\msys64\\ucrt64\\bin\\g++.exe),這個命令會被用來編譯 C++ 檔案。
  • "args": 定義了執行編譯器時要傳遞的參數。每個參數會在執行命令時依次加在後面:
    • "-fdiagnostics-color=always": 用來啟用編譯器輸出的彩色診斷信息,方便識別錯誤和警告。
    • "-g": 用來告訴編譯器生成包含調試資訊的可執行檔案,這樣可以在調試階段更容易追蹤程式行為。
    • "${file}": 代表當前打開的檔案,VSCode會自動將這個變數替換為實際文件的路徑,並將該文件作為編譯的輸入。
    • "${fileDirname}\\${fileBasenameNoExtension}.exe": 這個參數指定了輸出的可執行文件的名稱。"${fileDirname}"是當前檔案所在的目錄,"${fileBasenameNoExtension}"是不帶副檔名的文件名稱,最後編譯生成的可執行檔案是.exe
  • "options": 定義了執行編譯命令的選項,這裡的cwd (current working directory) 設置為${fileDirname},即當前打開檔案的目錄。這確保編譯命令在該檔案所在的目錄中執行。
  • "problemMatcher": 定義了問題匹配器,用來識別編譯器輸出的錯誤和警告,"$gcc"是一個內建的問題匹配器,適用於GCC編譯器 (包括 g++)。它會解析編譯器的輸出並顯示在VSCode問題面板中,讓開發者能方便地查看錯誤位置。
  • "group": 這個鍵定義了任務分組的屬性:
    • "kind": "build": 這表示這個任務屬於「編譯」類別,VSCode 會將其分類到編譯任務。
    • "isDefault": true: 這表示這個任務是默認的編譯任務,當使用快捷鍵 (如 Ctrl+Shift+B) 啟動編譯時,VSCode會執行這個任務。
  • "detail": "偵錯工具產生的工作。": 這是任務的詳細描述,用來幫助開發者了解任務的用途。

如果建置的時候需要使用多個cpp檔案來建置,可以修改tasks.json檔案的args,將"${file}"改為"${workspaceFolder}/*.cpp",將建置當前資料夾所有的cpp檔案;原本"${fileDirname}\\${fileBasenameNoExtension}.exe"是會根據當前開始的檔案生成一個與該檔案名稱相同的exe檔,改為"${workspaceFolder}\\myProgram.exe"代表將所有cpp檔都將編譯並鏈接成一個名為myProgram.exe的檔案,這適合專案中有多個源文件,但最終只生成一個可執行檔案的情況。

launch.json

launch.json用於設定VSCode調試C++程式的配置,在編輯C++程式後需要啟動調試器時,VSCode會根據這個文件中的配置來執行調試過程。過程包括指定要調試的可執行文件、設定調試器 (這裡使用GDB)、在調試過程中如何處理命令列參數、工作目錄、環境變數等、設定調試前是否需要執行編譯任務。

launch.json的範例:

{
    "configurations": [
        {
            "name": "C/C++: g++.exe 建置及偵錯使用中的檔案",
            "type": "cppdbg",
            "request": "launch",
            "program": "${fileDirname}\\${fileBasenameNoExtension}.exe",
            "args": [],
            "stopAtEntry": false,
            "cwd": "${fileDirname}",
            "environment": [],
            "externalConsole": false,
            "MIMode": "gdb",
            "miDebuggerPath": "C:\\msys64\\ucrt64\\bin\\gdb.exe",
            "setupCommands": [
                {
                    "description": "啟用 gdb 的美化顯示",
                    "text": "-enable-pretty-printing",
                    "ignoreFailures": true
                },
                {
                    "description": "將反組譯碼變體設為 Intel",
                    "text": "-gdb-set disassembly-flavor intel",
                    "ignoreFailures": true
                }
            ],
            "preLaunchTask": "C/C++: g++.exe 建置使用中檔案"
        }
    ],
    "version": "2.0.0"
}

這是一個啟動調試過程配置文件,"configurations"內各個參數的定義如下:

  • "name": 這是調試配置的名稱,用來標識這個調試任務,可以隨意命名,會在VSCode調試面板中顯示出來,這個名稱表明該配置是用來編譯並調試當前打開的 C++ 檔案。
  • "type": "cppdbg": 定義了調試的類型,cppdbg是用來表示這是一個 C++ 調試任務,VSCode 會使用這個類型來啟動對應的 C++ 調試功能,通常與 GDB 或 LLDB 調試器結合使用。
  • "request": "launch": 定義調試請求的類型,launch表示會啟動一個新的可執行文件並進行調試,如果是attach,則表示會附加到已經運行的程序上進行調試。
  • "program": "${fileDirname}\${fileBasenameNoExtension}.exe": 指定要調試的可執行文件的路徑,${fileDirname} 代表當前檔案所在的目錄,${fileBasenameNoExtension} 是當前打開檔案不帶副檔名的文件名稱,這樣可以生成 .exe 檔案作為調試目標。
  • "args": []: 定義了在調試過程中傳遞給可執行文件的命令列參數,如果需要在啟動時傳遞特定參數,可以在此設定。
  • "stopAtEntry": false: 定義是否在程序進入入口點時 (通常是 main 函數) 自動停止調試。如果設為 true,程序會在入口點處暫停,方便開發者檢查初始狀態。設為 false 表示程序將會直接執行,直到遇到斷點或結束。
  • "cwd": "${fileDirname}": 指定調試過程中執行程序的當前工作目錄,${fileDirname} 代表當前打開檔案所在的目錄,確保程序在這個目錄中執行。
  • "environment": []: 定義了調試過程中要設置的環境變數,當前是空列表,表示不會設置任何額外的環境變數。如果需要為程序運行設置特定的環境變數,可以在此添加。
  • "MIMode": "gdb": 指定使用哪種調試協議。gdb表示使用GNU Debugger (GDB) 來進行調試。如果使用的是LLDB調試器,這裡可以設為lldb
  • "miDebuggerPath": "C:\msys64\ucrt64\bin\gdb.exe": 定義了GDB調試器的路徑。這裡指定了MSYS2安裝的GDB調試器所在的路徑,讓VSCode知道在哪裡找到調試器進行調試。
  • "setupCommands": 定義了一些在調試過程中執行的初始命令,這些命令通常用來配置調試器的行為。
    • { "description": "啟用 gdb 的美化顯示", "text": "-enable-pretty-printing", "ignoreFailures": true }:
      這個命令用來啟用GDB的美化顯示功能 (pretty printing),這樣可以更好地格式化顯示STL容器等複雜的數據結構,使其更易於閱讀和調試。
    • { "description": "將反組譯碼變體設為 Intel", "text": "-gdb-set disassembly-flavor intel", "ignoreFailures": true }:
      這個命令設置GDB使用Intel語法進行反組譯顯示,這對於習慣Intel指令集的開發者來說會更容易閱讀。
  • "preLaunchTask": "C/C++: g++.exe 建置使用中檔案": 指定在啟動調試之前需要執行的任務。在此情況下,"C/C++: g++.exe 建置使用中檔案" 是在tasks.json中定義的編譯任務,這確保在每次啟動調試之前會重新編譯最新版本的程式碼。

CMake

Windows可以去CMake的網站找到對應作業系統的版本下載安裝,Ubuntu可以透過sudo apt-get install cmake來安裝,如果是Windows系統可以在安裝過程中點選Add CMake to the PATH environment variable這樣可以在terminal上直接使用CMake:

安裝完成後,在terminal上輸入cmake --version確認是否安裝成功。

可以透過VSCode的命令面板 (Ctrl+Shift+P) 執行CMake: Quick Start:

輸入專案名稱:

選擇C++作為專案語言:

選擇建立可執行檔,如果是想建立程式庫則選擇上方那個:

CPack 是一個打包工具,用來生成軟體的安裝包 (installers) 或發行包 (distribution packages);CTest 是一個測試工具,用來運行和管理專案中的單元測試、整合測試等,幫助驗證專案的正確性。根據需求選擇:

接下來是要選擇預設的編譯器:

選擇從編譯器建立:

這裡選擇要使用的編譯器即可:

直接按Enter會使用預設的名稱:

全都設定好後會自動產生CMakePresets.json檔案,CMakePresets.json是一個用來簡化CMake配置、構建和測試過程的文件,允許在專案中定義多個不同的預設配置,並能夠輕鬆切換這些配置,統一在不同平台上的配置過程,並提高配置和構建的效率:

{
    "version": 8,
    "configurePresets": [
        {
            "name": "Visual Studio Build Tools 2022 Release - amd64",
            "displayName": "Visual Studio Build Tools 2022 Release - amd64",
            "description": "使用 Visual Studio 17 2022 的編譯器(x64 架構)",
            "generator": "Visual Studio 17 2022",
            "toolset": "host=x64",
            "architecture": "x64",
            "binaryDir": "${sourceDir}/out/build/${presetName}",
            "cacheVariables": {
                "CMAKE_INSTALL_PREFIX": "${sourceDir}/out/install/${presetName}",
                "CMAKE_C_COMPILER": "cl.exe",
                "CMAKE_CXX_COMPILER": "cl.exe"
            }
        }
    ],
    "buildPresets": [
        {
            "name": "Visual Studio Build Tools 2022 Release - amd64-debug",
            "displayName": "Visual Studio Build Tools 2022 Release - amd64 - Debug",
            "configurePreset": "Visual Studio Build Tools 2022 Release - amd64",
            "configuration": "Debug"
        }
    ]
}

接下來使用CMake: Configure來配置專案,生成構建系統文件,這是編譯之前的準備步驟:

接著用CMake: Build編譯專案,將源代碼編譯成可執行文件或庫,是最終生成目標的步驟:

結語

在這篇文章中,介紹了如何使用VSCode來進行C++開發,並詳細講解了在編譯C++專案時可能會用到的各種設定,如tasks.jsonlaunch.json。同時也初步探討了CMake的作用,展示了如何利用它來簡化跨平台的編譯與配置過程,透過這些工具和設定,VSCode成為一個靈活、高效的C++開發環境,幫助開發者更輕鬆地管理和構建複雜的專案。