CMake構建學習筆記16-使用VS進行CMake專案的開發

charlee44發表於2024-09-14

目錄
  • 1. 概論
  • 2. 詳論
    • 2.1 建立工程
    • 2.2 載入工程
    • 2.3 配置檔案
    • 2.4 工程配置
    • 2.5 除錯執行
  • 3. 專案案例
  • 4. 總結

1. 概論

在之前的系列博文中,我們學習瞭如何構建第三方的依賴庫,也學習瞭如何去組建自己的CMake專案,尤其是學習了CMake的核心配置檔案CMakeLists.txt如何編寫。長期以來,CMakeLists.txt這個檔案都是C/C++專案額外編寫的,然後使用CMake指令或者GUI工具配置成Windows下的MSVC工程,或者Linux下的Makefile檔案。這樣做雖然對比之前需要不同的平臺下要使用不同的工程有了長足的進步,但是還可以再進一步,那就是直接在IDE中使用CMake工程進行開發,這樣無疑對C/C++程式開發的效率有質的提升。

從Visual Studio 2017開始,Microsoft Visual Studio(簡稱VS)就開始支援CMake工程的匯入。所謂CMake工程,指的就是不再需要建立傳統的MSVC專案,例如.sln或者.vcxproj工程檔案,而是直接使用CMakeLists.txt作為工程配置檔案來進行載入,進行進行構建和開發的工作。不僅是VS,目前其他IDE比如Visual Studio Code、Qt Creator、IntelliJ IDEA、 CLion都能直接支援CMake工程的匯入。但是,作為初學者,筆者還是建議從Microsoft Visual Studio入手進行CMake專案的開發,畢竟號稱宇宙第一的IDE不是白叫的。以筆者的觀點來看,Microsoft Visual Studio的確實有點重,編輯器也不是最美觀的,UI操作也不一定是最人性化的,但是其提供的除錯功能卻是最優秀且不可或缺的,尤其適合商業中的生產力環境。這裡筆者就以Visual Studio 2019 為例,詳細講解一下如何進行CMake專案的開發,以提升我們的C/C++程式開發效率。

2. 詳論

2.1 建立工程

啟動Visual Studio 2019,彈出的啟動頁面,如下圖1所示:

圖1:Visual Studio 2019啟動頁面

點選右下方“建立新專案”按鈕,進入“建立新專案”頁面,如下圖2所示:

圖2:建立新專案頁面

選擇“CMake專案”的模板,如果沒有看到可以搜尋一下模板。點選“下一步”按鈕,進入“配置新專案”頁面,如下圖3所示:

圖3:配置新專案頁面

在“配置新專案”頁面填入專案名稱和位置,點選“建立”按鈕,就進入了Visual Studio 2019的主要工作頁面,如下圖4所示:

圖4:主要工作頁面

2.2 載入工程

關閉Visual Studio 2019,模擬一下直接載入現有CMake工程的情況。再次啟動Visual Studio 2019,一般在圖1所示的啟動頁面中可以看到上次載入過的歷史記錄,點選就可以再次進行載入了。但是如何沒有歷史記錄,就點選“繼續但無需程式碼”按鈕,直接進入主頁面。在選單欄中依次選擇檔案->開啟->CMake按鈕,如下圖5所示:

圖5:載入CMake工程入口

此時會彈出“開啟CMake專案”對話方塊,選中專案中的CMakeList.txt檔案,CMake專案就是透過這個核心配置檔案來開啟的,如下圖6所示:

圖6:選擇CMakeList.txt檔案

記住一定要透過這種方式開啟CMakeList.txt檔案才會開啟CMake專案,如果直接將CMakeList.txt檔案拖入到Visual Studio 2019主頁面中只會文字形式顯示CMakeList.txt。

2.3 配置檔案

接下來再正式進行開發之前,我們需要先搞定一個配置檔案CMakePresets.json。CMakeList.txt具有非常多的配置項,或者需要傳入的外部引數,需要使用一個配置檔案來進行管理。不過麻煩就麻煩在這裡,CMakePresets.json是CMake 3.20引入的,是個相對較新的功能,Visual Studio 2019並沒有一開始就對接這個配置檔案,而是使用自己設計的CMakeSettings.json檔案作為CMake構建專案的配置。目前,這兩種配置檔案Visual Studio 2019都支援,但是更推薦使用CMakePresets.json,因為更加標準化,符合CMake的規範,可以被多種IDE和構建工具識別和支援。

具體來說,如果程式主頁面,尤其是主頁面的工具欄與下圖7有所不同:

圖7:CMake專案的工具欄

那麼可以在選單欄依次選擇工具->選項->CMake->常規,勾選“首次使用CMake預設值進行配置、構建和測試”的單選框,如下圖8所示:

圖8:設定CMake預設配置

點選工具欄的配置下拉選單,選擇“管理配置”按鈕,如下圖9所示:

圖9:管理配置

此時Visual Studio 2019就會自動建立CMakeSettings.json配置檔案,如下圖10所示:

圖10:CMakeSettings.json配置檔案

從這個檔案可以看到預設的windows-default配置其實是Debug模式,我們可以將其增加一個RelWithDebInfo模式,也就是Release帶除錯資訊模式,CMakePresets.json具體的內容為:

{
  "version": 2,
  "configurePresets": [
    {
      "name": "linux-default",
      "displayName": "Linux Debug",
      "description": "面向適用於 Linux 的 Windows 子系統(WSL)或遠端 Linux 系統。",
      "generator": "Ninja",
      "binaryDir": "${sourceDir}/out/build/${presetName}",
      "cacheVariables": {
        "CMAKE_BUILD_TYPE": "Debug",
        "CMAKE_INSTALL_PREFIX": "${sourceDir}/out/install/${presetName}"
      },
      "vendor": {
        "microsoft.com/VisualStudioSettings/CMake/1.0": { "hostOS": [ "Linux" ] },
        "microsoft.com/VisualStudioRemoteSettings/CMake/1.0": { "sourceDir": "$env{HOME}/.vs/$ms{projectDirName}" }
      }
    },
    {
      "name": "windows-default",
      "displayName": "Windows x64 Debug",
      "description": "面向具有 Visual Studio 開發環境的 Windows。",
      "generator": "Ninja",
      "binaryDir": "${sourceDir}/out/build/${presetName}",
      "architecture": {
        "value": "x64",
        "strategy": "external"
      },
      "cacheVariables": {
        "CMAKE_BUILD_TYPE": "Debug",
        "CMAKE_INSTALL_PREFIX": "${sourceDir}/out/install/${presetName}"
      },
      "vendor": { "microsoft.com/VisualStudioSettings/CMake/1.0": { "hostOS": [ "Windows" ] } }
    },
    {
      "name": "RelWithDebInfo",
      "displayName": "Windows x64 RelWithDebInfo Shared Library",
      "description": "面向具有 Visual Studio 開發環境的 Windows。",
      "generator": "Ninja",
      "binaryDir": "${sourceDir}/out/build/${presetName}",
      "architecture": {
        "value": "x64",
        "strategy": "external"
      },
      "cacheVariables": {
        "CMAKE_BUILD_TYPE": "RelWithDebInfo",
        "CMAKE_INSTALL_PREFIX": "${sourceDir}/out/install/${presetName}"
      },
      "vendor": { "microsoft.com/VisualStudioSettings/CMake/1.0": { "hostOS": [ "Windows" ] } }
    }
  ]
}

新增這段配置並Ctrl+S儲存之後,工具欄的配置下拉選單就會多了RelWithDebInfo這個選項,將其選中,如下圖11所示:

圖11:選中配置

注意,有的時候因為兩種配置方式的衝突問題,會導致一些異常現象,比如工具欄的配置選單不太一樣。如果遇到這種情況可以推出Visual Studio 2019,清理工程的中間生成檔案,再重新載入工程試試。

2.4 工程配置

再接下來的步驟不要急著去編寫原始碼檔案,要先完成CMakeLists.txt的編寫。新建工程的CMakeLists.txt會有一段預設的內容,如下所示:

# CMakeList.txt: ZipTest 的 CMake 專案,在此處包括原始碼並定義
# 專案特定的邏輯。
#
cmake_minimum_required (VERSION 3.8)

project ("ZipTest")

# 將原始碼新增到此專案的可執行檔案。
add_executable (ZipTest "ZipTest.cpp" "ZipTest.h")

# TODO: 如有需要,請新增測試並安裝目標。

如果我們學習過上一篇博文的話就會理解這段配置程式碼檔案,推薦讀者複習一下。這裡要說的關鍵是,在修改CMakeLists.txt檔案之後,需要Ctrl+S儲存一下,Visual Studio 2019就會自動進行工程配置,可以在輸出視窗看到一些輸出資訊:

1> 已為配置“RelWithDebInfo”啟動 CMake 生成。
1> 環境設定:
1>     CommandPromptType=Native
1>     DevEnvDir=C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\
1>     ExtensionSdkDir=C:\Program Files (x86)\Microsoft SDKs\Windows Kits\10\ExtensionSDKs
1>     Framework40Version=v4.0
1>     FrameworkDir=C:\windows\Microsoft.NET\Framework64\
1>     FrameworkDIR64=C:\windows\Microsoft.NET\Framework64
1>     FrameworkVersion=v4.0.30319
1>     FrameworkVersion64=v4.0.30319
1>     HTMLHelpDir=C:\Program Files (x86)\HTML Help Workshop
1>     IFCPATH=C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Tools\MSVC\14.29.30133\ifc\x64
1>     IGCCSVC_DB=AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAAggkwNDDGDkq0HFOUjEsXQAQAAAACAAAAAAAQZgAAAAEAACAAAADxjk35GiqLjZDHeYjx5dq8wxmbU7aEbBW9J68TO/bzIwAAAAAOgAAAAAIAACAAAADdM3gyHGrxXOwEEyHmxfe9ocZnP6CM0OTQGZYVZKgQWmAAAAC8xGVDuFoU062/gozauvaMPUmsT8FAuEXoLnI9lTwHVT6XWpjF7lVBoYB+vxo1dgIUAtW0nl1wZSUg9KRxmYpIicPPLm7B+twKXEdbaDMIu55E10uazKjjvoHY/4KYu+tAAAAAoK95FWIYAE3f+YLjfb3S77+ZMJXFw69cRlxyTYekzkyfOFdUcCY94ahV+XHEgao2y8e/zT+q2zHU0SqXho0LDQ==
1>     INCLUDE=C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Tools\MSVC\14.29.30133\ATLMFC\include;C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Tools\MSVC\14.29.30133\include;C:\Program Files (x86)\Windows Kits\NETFXSDK\4.8\include\um;C:\Program Files (x86)\Windows Kits\10\include\10.0.22000.0\ucrt;C:\Program Files (x86)\Windows Kits\10\include\10.0.22000.0\shared;C:\Program Files (x86)\Windows Kits\10\include\10.0.22000.0\um;C:\Program Files (x86)\Windows Kits\10\include\10.0.22000.0\winrt;C:\Program Files (x86)\Windows Kits\10\include\10.0.22000.0\cppwinrt
1>     LIB=C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Tools\MSVC\14.29.30133\ATLMFC\lib\x64;C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Tools\MSVC\14.29.30133\lib\x64;C:\Program Files (x86)\Windows Kits\NETFXSDK\4.8\lib\um\x64;C:\Program Files (x86)\Windows Kits\10\lib\10.0.22000.0\ucrt\x64;C:\Program Files (x86)\Windows Kits\10\lib\10.0.22000.0\um\x64
1>     LIBPATH=C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Tools\MSVC\14.29.30133\ATLMFC\lib\x64;C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Tools\MSVC\14.29.30133\lib\x64;C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Tools\MSVC\14.29.30133\lib\x86\store\references;C:\Program Files (x86)\Windows Kits\10\UnionMetadata\10.0.22000.0;C:\Program Files (x86)\Windows Kits\10\References\10.0.22000.0;C:\windows\Microsoft.NET\Framework64\v4.0.30319
1>     NETFXSDKDir=C:\Program Files (x86)\Windows Kits\NETFXSDK\4.8\
1>     Path=C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\\Extensions\Microsoft\IntelliCode\CLI;C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Tools\MSVC\14.29.30133\bin\HostX64\x64;C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\VC\VCPackages;C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\CommonExtensions\Microsoft\TestWindow;C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\CommonExtensions\Microsoft\TeamFoundation\Team Explorer;C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\bin\Roslyn;C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Team Tools\Performance Tools\x64;C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Team Tools\Performance Tools;C:\Program Files (x86)\Microsoft Visual Studio\Shared\Common\VSPerfCollectionTools\vs2019\\x64;C:\Program Files (x86)\Microsoft Visual Studio\Shared\Common\VSPerfCollectionTools\vs2019\;C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8 Tools\x64\;C:\Program Files (x86)\HTML Help Workshop;C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\Tools\devinit;C:\Program Files (x86)\Windows Kits\10\bin\10.0.22000.0\x64;C:\Program Files (x86)\Windows Kits\10\bin\x64;C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\\MSBuild\Current\Bin;C:\windows\Microsoft.NET\Framework64\v4.0.30319;C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\;C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\Tools\;C:\Program Files (x86)\Common Files\Oracle\Java\javapath;C:\windows\system32;C:\windows;C:\windows\System32\Wbem;C:\windows\System32\WindowsPowerShell\v1.0\;C:\windows\System32\OpenSSH\;C:\Program Files (x86)\NVIDIA Corporation\PhysX\Common;C:\Program Files\NVIDIA Corporation\NVIDIA NvDLISR;C:\Users\Administrator\AppData\Local\Microsoft\WindowsApps;C:\Program Files\HP\OMEN-Broadcast\Common;C:\Program Files\CMake\bin;C:\Program Files\Microsoft SQL Server\130\Tools\Binn\;C:\Program Files\TortoiseGit\bin;C:\Work\3rdparty\bin;C:\SoftWare\Qt\Qt5.12.5\5.12.5\msvc2017_64\bin;C:\File\MyGitHub\GISBasic\3rdParty\bin;C:\Program Files\dotnet\;C:\Work\eGova3rdParty\protobuf\bin;C:\Program Files\Java\jdk1.8.0_271\bin;C:\Program Files\KTX-Software\bin;C:\SoftWare\sonar-scanner-msbuild;C:\Program Files\Cppcheck;C:\SoftWare\sonar-scanner-msbuild\sonar-scanner-4.7.0.2747\bin;C:\SoftWare\apache-ant-1.10.8\bin;C:\Program Files (x86)\pcsuite\;C:\SoftWare\apache-maven-3.6.2\bin;C:\Program Files\7-Zip;C:\Program Files\Git\cmd;C:\SoftWare\nvm;C:\Program Files\nodejs;C:\SoftWare\Python\Python311\Scripts\;C:\SoftWare\Python\Python311\;C:\Users\Charlee\AppData\Local\Microsoft\WindowsApps;C:\Program Files\Microsoft VS Code\bin;C:\Users\Charlee\AppData\Local\GitHubDesktop\bin;C:\Users\Charlee\AppData\Roaming\npm;C:\SoftWare\nvm;C:\Program Files\nodejs;C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\CommonExtensions\Microsoft\CMake\CMake\bin;C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\CommonExtensions\Microsoft\CMake\Ninja;C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\VC\Linux\bin\ConnectionManagerExe
1>     PROMPT=$P$G
1>     UCRTVersion=10.0.22000.0
1>     UniversalCRTSdkDir=C:\Program Files (x86)\Windows Kits\10\
1>     VCIDEInstallDir=C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\VC\
1>     VCINSTALLDIR=C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\
1>     VCToolsInstallDir=C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Tools\MSVC\14.29.30133\
1>     VCToolsRedistDir=C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Redist\MSVC\14.29.30133\
1>     VCToolsVersion=14.29.30133
1>     VS160COMNTOOLS=C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\Tools\
1>     VSCMD_ARG_app_plat=Desktop
1>     VSCMD_ARG_HOST_ARCH=x64
1>     VSCMD_ARG_no_logo=1
1>     VSCMD_ARG_TGT_ARCH=x64
1>     VSCMD_DEBUG=5 
1>     VSCMD_VER=16.11.29
1>     VSINSTALLDIR=C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\
1>     WindowsLibPath=C:\Program Files (x86)\Windows Kits\10\UnionMetadata\10.0.22000.0;C:\Program Files (x86)\Windows Kits\10\References\10.0.22000.0
1>     WindowsSdkBinPath=C:\Program Files (x86)\Windows Kits\10\bin\
1>     WindowsSdkDir=C:\Program Files (x86)\Windows Kits\10\
1>     WindowsSDKLibVersion=10.0.22000.0\
1>     WindowsSdkVerBinPath=C:\Program Files (x86)\Windows Kits\10\bin\10.0.22000.0\
1>     WindowsSDKVersion=10.0.22000.0\
1>     WindowsSDK_ExecutablePath_x64=C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8 Tools\x64\
1>     WindowsSDK_ExecutablePath_x86=C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8 Tools\
1>     __devinit_path=C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\Tools\devinit\devinit.exe
1>     __DOTNET_ADD_64BIT=1
1>     __DOTNET_PREFERRED_BITNESS=64
1>     __VSCMD_PREINIT_PATH=C:\Program Files (x86)\Common Files\Oracle\Java\javapath;C:\windows\system32;C:\windows;C:\windows\System32\Wbem;C:\windows\System32\WindowsPowerShell\v1.0\;C:\windows\System32\OpenSSH\;C:\Program Files (x86)\NVIDIA Corporation\PhysX\Common;C:\Program Files\NVIDIA Corporation\NVIDIA NvDLISR;C:\Users\Administrator\AppData\Local\Microsoft\WindowsApps;C:\Program Files\HP\OMEN-Broadcast\Common;C:\Program Files\CMake\bin;C:\Program Files\Microsoft SQL Server\130\Tools\Binn\;C:\Program Files\TortoiseGit\bin;C:\Work\3rdparty\bin;C:\SoftWare\Qt\Qt5.12.5\5.12.5\msvc2017_64\bin;C:\File\MyGitHub\GISBasic\3rdParty\bin;C:\Program Files\dotnet\;C:\Work\eGova3rdParty\protobuf\bin;C:\Program Files\Java\jdk1.8.0_271\bin;C:\Program Files\KTX-Software\bin;C:\SoftWare\sonar-scanner-msbuild;C:\Program Files\Cppcheck;C:\SoftWare\sonar-scanner-msbuild\sonar-scanner-4.7.0.2747\bin;C:\SoftWare\apache-ant-1.10.8\bin;C:\Program Files (x86)\pcsuite\;C:\SoftWare\apache-maven-3.6.2\bin;C:\Program Files\7-Zip;C:\Program Files\Git\cmd;C:\SoftWare\nvm;C:\Program Files\nodejs;C:\SoftWare\Python\Python311\Scripts\;C:\SoftWare\Python\Python311\;C:\Users\Charlee\AppData\Local\Microsoft\WindowsApps;C:\Program Files\Microsoft VS Code\bin;C:\Users\Charlee\AppData\Local\GitHubDesktop\bin;C:\Users\Charlee\AppData\Roaming\npm;C:\SoftWare\nvm;C:\Program Files\nodejs
1>     __VSCMD_script_err_count=0
1>     HOMEPATH=\Users\Charlee
1>     DriverData=C:\Windows\System32\Drivers\DriverData
1>     COMPUTERNAME=LAPTOP-K38HMG48
1>     CommonProgramFiles(x86)=C:\Program Files (x86)\Common Files
1>     POSTGIS_GDAL_ENABLED_DRIVERS=ENABLE_ALL
1>     ProgramW6432=C:\Program Files
1>     OneDrive=C:\Users\Charlee\OneDrive
1>     __PSLockDownPolicy=0
1>     RegionCode=APJ
1>     DEVECOSTUDIO_VM_OPTIONS=C:\SoftWare\ideaI-windows\2023\vmoptions\devecostudio.vmoptions
1>     VisualStudioEdition=Microsoft Visual Studio Enterprise 2019
1>     WEBIDE_VM_OPTIONS=C:\SoftWare\ideaI-windows\2023\vmoptions\webide.vmoptions
1>     ServiceHubLogSessionKey=3AFDCD75
1>     PROCESSOR_REVISION=b701
1>     PROCESSOR_IDENTIFIER=Intel64 Family 6 Model 183 Stepping 1, GenuineIntel
1>     PATHEXT=.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC
1>     PkgDefApplicationConfigFile=C:\Users\Charlee\AppData\Local\Microsoft\VisualStudio\16.0_36d14652\devenv.exe.config
1>     JETBRAINS_CLIENT_VM_OPTIONS=C:\SoftWare\ideaI-windows\2023\vmoptions\jetbrains_client.vmoptions
1>     PROJ_LIB=C:\Program Files\PostgreSQL\16\share\contrib\postgis-3.4\proj
1>     CURL_CA_BUNDLE=C:\Program Files\PostgreSQL\16\ssl\certs\ca-bundle.crt
1>     TMP=C:\Users\Charlee\AppData\Local\Temp
1>     DATAGRIP_VM_OPTIONS=C:\SoftWare\ideaI-windows\2023\vmoptions\datagrip.vmoptions
1>     TEMP=C:\Users\Charlee\AppData\Local\Temp
1>     LOCALAPPDATA=C:\Users\Charlee\AppData\Local
1>     PUBLIC=C:\Users\Public
1>     eGova3rdParty=C:\Work\3rdparty
1>     GDAL_DATA=C:\Program Files\PostgreSQL\16\gdal-data
1>     PSModulePath=C:\Program Files\WindowsPowerShell\Modules;C:\windows\system32\WindowsPowerShell\v1.0\Modules
1>     ProgramData=C:\ProgramData
1>     JAVA_HOME=C:\Program Files\Java\jdk1.8.0_271
1>     USERDOMAIN=LAPTOP-K38HMG48
1>     platformcode=M7
1>     PROCESSOR_LEVEL=6
1>     NUMBER_OF_PROCESSORS=32
1>     PHPSTORM_VM_OPTIONS=C:\SoftWare\ideaI-windows\2023\vmoptions\phpstorm.vmoptions
1>     STUDIO_VM_OPTIONS=C:\SoftWare\ideaI-windows\2023\vmoptions\studio.vmoptions
1>     VSAPPIDDIR=C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\
1>     ProgramFiles(x86)=C:\Program Files (x86)
1>     FPS_BROWSER_USER_PROFILE_STRING=Default
1>     CommonProgramFiles=C:\Program Files (x86)\Common Files
1>     VS140COMNTOOLS=C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\Tools\
1>     USERDOMAIN_ROAMINGPROFILE=LAPTOP-K38HMG48
1>     VisualStudioDir=C:\Users\Charlee\Documents\Visual Studio 2019
1>     IGCCSVC_DB=AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAAggkwNDDGDkq0HFOUjEsXQAQAAAACAAAAAAAQZgAAAAEAACAAAADxjk35GiqLjZDHeYjx5dq8wxmbU7aEbBW9J68TO/bzIwAAAAAOgAAAAAIAACAAAADdM3gyHGrxXOwEEyHmxfe9ocZnP6CM0OTQGZYVZKgQWmAAAAC8xGVDuFoU062/gozauvaMPUmsT8FAuEXoLnI9lTwHVT6XWpjF7lVBoYB+vxo1dgIUAtW0nl1wZSUg9KRxmYpIicPPLm7B+twKXEdbaDMIu55E10uazKjjvoHY/4KYu+tAAAAAoK95FWIYAE3f+YLjfb3S77+ZMJXFw69cRlxyTYekzkyfOFdUcCY94ahV+XHEgao2y8e/zT+q2zHU0SqXho0LDQ==
1>     GISBasic=C:\File\MyGitHub\GISBasic\3rdParty
1>     VSLS_SESSION_KEEPALIVE_INTERVAL=0
1>     MAVEN_HOME=C:\SoftWare\apache-maven-3.6.2
1>     ProgramFiles=C:\Program Files (x86)
1>     RUBYMINE_VM_OPTIONS=C:\SoftWare\ideaI-windows\2023\vmoptions\rubymine.vmoptions
1>     APPCODE_VM_OPTIONS=C:\SoftWare\ideaI-windows\2023\vmoptions\appcode.vmoptions
1>     FPS_BROWSER_APP_PROFILE_STRING=Internet Explorer
1>     VSSKUEDITION=Enterprise
1>     OnlineServices=Online Services
1>     ThreadedWaitDialogDpiContext=-4
1>     IDEA_VM_OPTIONS=C:\SoftWare\ideaI-windows\2023\vmoptions\idea.vmoptions
1>     WEBSTORM_VM_OPTIONS=C:\SoftWare\ideaI-windows\2023\vmoptions\webstorm.vmoptions
1>     GOLAND_VM_OPTIONS=C:\SoftWare\ideaI-windows\2023\vmoptions\goland.vmoptions
1>     NVM_SYMLINK=C:\Program Files\nodejs
1>     NVM_HOME=C:\SoftWare\nvm
1>     SESSIONNAME=Console
1>     VisualStudioVersion=16.0
1>     SystemRoot=C:\windows
1>     CommonProgramW6432=C:\Program Files\Common Files
1>     ZES_ENABLE_SYSMAN=1
1>     LOGONSERVER=\\LAPTOP-K38HMG48
1>     VSAPPIDNAME=devenv.exe
1>     USERPROFILE=C:\Users\Charlee
1>     MSBuildLoadMicrosoftTargetsReadOnly=true
1>     QtMsBuild=C:\Users\Charlee\AppData\Local\QtMsBuild
1>     POSTGIS_ENABLE_OUTDB_RASTERS=1
1>     VSLANG=2052
1>     RIDER_VM_OPTIONS=C:\SoftWare\ideaI-windows\2023\vmoptions\rider.vmoptions
1>     APPDATA=C:\Users\Charlee\AppData\Roaming
1>     HOMEDRIVE=C:
1>     DATASPELL_VM_OPTIONS=C:\SoftWare\ideaI-windows\2023\vmoptions\dataspell.vmoptions
1>     GATEWAY_VM_OPTIONS=C:\SoftWare\ideaI-windows\2023\vmoptions\gateway.vmoptions
1>     CLION_VM_OPTIONS=C:\SoftWare\ideaI-windows\2023\vmoptions\clion.vmoptions
1>     JETBRAINSCLIENT_VM_OPTIONS=C:\SoftWare\ideaI-windows\2023\vmoptions\jetbrainsclient.vmoptions
1>     USERNAME=Charlee
1>     PROCESSOR_ARCHITEW6432=AMD64
1>     EFC_20336=1
1>     PROCESSOR_ARCHITECTURE=x86
1>     OS=Windows_NT
1>     ComSpec=C:\windows\system32\cmd.exe
1>     PYCHARM_VM_OPTIONS=C:\SoftWare\ideaI-windows\2023\vmoptions\pycharm.vmoptions
1>     SystemDrive=C:
1>     windir=C:\windows
1>     ALLUSERSPROFILE=C:\ProgramData
1> 命令列: "C:\windows\system32\cmd.exe" /c "%SYSTEMROOT%\System32\chcp.com 65001 >NUL && "C:\PROGRAM FILES (X86)\MICROSOFT VISUAL STUDIO\2019\ENTERPRISE\COMMON7\IDE\COMMONEXTENSIONS\MICROSOFT\CMAKE\CMake\bin\cmake.exe"  -G "Ninja"  -DCMAKE_BUILD_TYPE:STRING="RelWithDebInfo" -DCMAKE_INSTALL_PREFIX:STRING="C:/Work/ZipTest/out/install/RelWithDebInfo"  -DCMAKE_MAKE_PROGRAM="C:\PROGRAM FILES (X86)\MICROSOFT VISUAL STUDIO\2019\ENTERPRISE\COMMON7\IDE\COMMONEXTENSIONS\MICROSOFT\CMAKE\Ninja\ninja.exe" "C:\Work\ZipTest" 2>&1"
1> 工作目錄: C:/Work/ZipTest/out/build/RelWithDebInfo
1> [CMake] -- Configuring done
1> [CMake] -- Generating done
1> [CMake] -- Build files have been written to: C:/Work/ZipTest/out/build/RelWithDebInfo
1> 已提取 CMake 變數。
1> 已提取原始檔和標頭。
1> 已提取程式碼模型。
1> 已提取工具鏈配置。
1> 已提取包含路徑。
1> CMake 生成完畢。

這個配置工程的步驟一定不能少,且要保證看到“CMake生成完畢”的提示。如果生成中斷的話,可以在輸出日誌中看到出錯的地方並進行修改。本質上來說,CMakeLists.txt只是個文字檔案而已,要透過這一步將構建的環境準備好,生成一些快取檔案和中間檔案,從而便於構建工具鏈識別進行下一步作業。

2.5 除錯執行

在保證CMake配置工程完畢之後,就可以進行除錯執行了。這一步的功能就無縫對接MSVC專案了,例如編輯程式碼,F7生成,F5除錯,Ctrl+F5執行,F9斷點,F10逐過程除錯以及F11逐語句除錯。不過有一點要注意,就是要選擇啟動項,不然可能無法執行專案。具體可以在工具欄的選擇啟動項下拉選單中,如下圖12所示:

圖12:選擇啟動項

我們當然要選中ZipTest.exe這個目標,不過一定要注意,只有當CMake生成完畢以後才會出現這個選項。如果沒有這個選項,那就說明之前的CMake生成沒有成功。

另外一個很實用的功能是,在CMake生成成功以後,可以切換到CMake目標專案。具體透過“解決資源管理器檢視”的工具欄上的“在解決方案和可用檢視之間切換”按鈕進入,如下圖13所示:

圖13:切換檢視

這個檢視看起來有點像MSVC工程了,比資料夾檢視簡潔介多了。更重要的是由這個檢視的右鍵選單功能更實用一點,比如“設為啟動項”按鈕也可以實現上面的選擇啟動項功能。另外還有“新增”功能,與MSVC專案的“新增”功能類似,可以新建原始碼檔案加入到CMake工程中。不過這個功能是透過修改CMakeList.txt檔案來實現的,讀者可以自己試用一下。

圖14:CMake目標檢視

其實筆者感覺這個CMake目標檢視是想像MSVC工程一樣,整合更多的常用GUI操作的功能,使得開發程式設計的效率更高。不過目前這些還只是半成品,比如這個“新增”功能是可以實現原始碼檔案的新增了,但是對應修改CMakeList.txt的內容不一定是我們想要的,關於這一點讀者可以試用一段時間之後再領會。目前很多常用的IDE功能還是需要我們自己編輯CMakeList.txt檔案來實現,儘管如此,已經可以幫助我們提升很大一部分開發效率了。

3. 專案案例

預設的Hello CMake案例還是太簡單了,我們還是將上一篇的呼叫libzip壓縮檔案和資料夾的案例用上。專案目錄如下:

ZipTest
│   main.cpp
│   CMakeLists.txt    
|   CMakePresets.json

main.cpp的內容如下:

#include <zip.h>

#include <filesystem>
#include <fstream>
#include <iostream>

using namespace std;

void CompressFile2Zip(std::filesystem::path unZipFilePath,
                      const char* relativeName, zip_t* zipArchive) {
  std::ifstream file(unZipFilePath, std::ios::binary);
  file.seekg(0, std::ios::end);
  size_t bufferSize = file.tellg();
  char* bufferData = (char*)malloc(bufferSize);

  file.seekg(0, std::ios::beg);
  file.read(bufferData, bufferSize);

  //第四個引數如果非0,會自動託管申請的資源,直到zip_close之前自動銷燬。
  zip_source_t* source =
      zip_source_buffer(zipArchive, bufferData, bufferSize, 1);

  if (source) {
    if (zip_file_add(zipArchive, relativeName, source, ZIP_FL_OVERWRITE) < 0) {
      std::cerr << "Failed to add file " << unZipFilePath
                << " to zip: " << zip_strerror(zipArchive) << std::endl;
      zip_source_free(source);
    }
  } else {
    std::cerr << "Failed to create zip source for " << unZipFilePath << ": "
              << zip_strerror(zipArchive) << std::endl;
  }
}

void CompressFile(std::filesystem::path unZipFilePath,
                  std::filesystem::path zipFilePath) {
  int errorCode = 0;
  zip_t* zipArchive = zip_open(zipFilePath.generic_u8string().c_str(),
                               ZIP_CREATE | ZIP_TRUNCATE, &errorCode);
  if (zipArchive) {
    CompressFile2Zip(unZipFilePath, unZipFilePath.filename().string().c_str(),
                     zipArchive);

    errorCode = zip_close(zipArchive);
    if (errorCode != 0) {
      zip_error_t zipError;
      zip_error_init_with_code(&zipError, errorCode);
      std::cerr << zip_error_strerror(&zipError) << std::endl;
      zip_error_fini(&zipError);
    }
  } else {
    zip_error_t zipError;
    zip_error_init_with_code(&zipError, errorCode);
    std::cerr << "Failed to open output file " << zipFilePath << ": "
              << zip_error_strerror(&zipError) << std::endl;
    zip_error_fini(&zipError);
  }
}

void CompressDirectory2Zip(std::filesystem::path rootDirectoryPath,
                           std::filesystem::path directoryPath,
                           zip_t* zipArchive) {
  if (rootDirectoryPath != directoryPath) {
    if (zip_dir_add(zipArchive,
                    std::filesystem::relative(directoryPath, rootDirectoryPath)
                        .generic_u8string()
                        .c_str(),
                    ZIP_FL_ENC_UTF_8) < 0) {
      std::cerr << "Failed to add directory " << directoryPath
                << " to zip: " << zip_strerror(zipArchive) << std::endl;
    }
  }

  for (const auto& entry : std::filesystem::directory_iterator(directoryPath)) {
    if (entry.is_regular_file()) {
      CompressFile2Zip(
          entry.path().generic_u8string(),
          std::filesystem::relative(entry.path(), rootDirectoryPath)
              .generic_u8string()
              .c_str(),
          zipArchive);
    } else if (entry.is_directory()) {
      CompressDirectory2Zip(rootDirectoryPath, entry.path().generic_u8string(),
                            zipArchive);
    }
  }
}

void CompressDirectory(std::filesystem::path directoryPath,
                       std::filesystem::path zipFilePath) {
  int errorCode = 0;
  zip_t* zipArchive = zip_open(zipFilePath.generic_u8string().c_str(),
                               ZIP_CREATE | ZIP_TRUNCATE, &errorCode);
  if (zipArchive) {
    CompressDirectory2Zip(directoryPath, directoryPath, zipArchive);

    errorCode = zip_close(zipArchive);
    if (errorCode != 0) {
      zip_error_t zipError;
      zip_error_init_with_code(&zipError, errorCode);
      std::cerr << zip_error_strerror(&zipError) << std::endl;
      zip_error_fini(&zipError);
    }
  } else {
    zip_error_t zipError;
    zip_error_init_with_code(&zipError, errorCode);
    std::cerr << "Failed to open output file " << zipFilePath << ": "
              << zip_error_strerror(&zipError) << std::endl;
    zip_error_fini(&zipError);
  }
}

int main() {
  //壓縮檔案
  // CompressFile("C:/Data/Builder/Demo/view.tmp",
  // "C:/Data/Builder/Demo/view.zip");

  //壓縮資料夾
  CompressDirectory("C:/Data/Builder/Demo", "C:/Data/Builder/Demo.zip");

  return 0;
}

CMakeLists.txt的內容如下:

# 輸出cmake版本提示
message(STATUS "The CMAKE_VERSION is ${CMAKE_VERSION}.")

# cmake的最低版本要求
cmake_minimum_required (VERSION 3.9)

# 工程名稱、版本、語言
project (ZipTest VERSION 0.1 LANGUAGES CXX)

# cpp17支援
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# 查詢依賴庫
find_package(libzip REQUIRED)

# 將原始碼新增到此專案的可執行檔案。
add_executable (${PROJECT_NAME} "main.cpp")

# 連結依賴庫
target_link_libraries(${PROJECT_NAME} PRIVATE libzip::zip)

CMakePresets.json的內容如下:

{
  "version": 2,
  "configurePresets": [
    {
      "name": "linux-default",
      "displayName": "Linux Debug",
      "description": "面向適用於 Linux 的 Windows 子系統(WSL)或遠端 Linux 系統。",
      "generator": "Ninja",
      "binaryDir": "${sourceDir}/out/build/${presetName}",
      "cacheVariables": {
        "CMAKE_BUILD_TYPE": "Debug",
        "CMAKE_INSTALL_PREFIX": "${sourceDir}/out/install/${presetName}"
      },
      "vendor": {
        "microsoft.com/VisualStudioSettings/CMake/1.0": { "hostOS": [ "Linux" ] },
        "microsoft.com/VisualStudioRemoteSettings/CMake/1.0": { "sourceDir": "$env{HOME}/.vs/$ms{projectDirName}" }
      }
    },
    {
      "name": "windows-default",
      "displayName": "Windows x64 Debug",
      "description": "面向具有 Visual Studio 開發環境的 Windows。",
      "generator": "Ninja",
      "binaryDir": "${sourceDir}/out/build/${presetName}",
      "architecture": {
        "value": "x64",
        "strategy": "external"
      },
      "cacheVariables": {
        "CMAKE_BUILD_TYPE": "Debug",
        "CMAKE_INSTALL_PREFIX": "${sourceDir}/out/install/${presetName}"
      },
      "vendor": { "microsoft.com/VisualStudioSettings/CMake/1.0": { "hostOS": [ "Windows" ] } }
    },
    {
      "name": "RelWithDebInfo",
      "displayName": "Windows x64 RelWithDebInfo Shared Library",
      "description": "面向具有 Visual Studio 開發環境的 Windows。",
      "generator": "Ninja",
      "binaryDir": "${sourceDir}/out/build/${presetName}",
      "architecture": {
        "value": "x64",
        "strategy": "external"
      },
      "cacheVariables": {
        "CMAKE_BUILD_TYPE": "RelWithDebInfo",
        "CMAKE_INSTALL_PREFIX": "${sourceDir}/out/install/${presetName}"
      },
      "vendor": { "microsoft.com/VisualStudioSettings/CMake/1.0": { "hostOS": [ "Windows" ] } }
    }
  ]
}

請務必注意,我們這裡使用的是CMake比較推薦和比較新的目標連結機制來引入libzip庫,關於這一點請務必複習上一篇博文的內容。這裡要說的是如果find_package(libzip REQUIRED)失敗,那麼可能需要指定依賴庫的安裝目錄,具體是在CMakePresets.json檔案中的RelWithDebInfo配置中增加CMAKE_PREFIX_PATH,筆者這裡使用的GISBasic環境變數指向的目錄。至於libzip如何構建安裝?可以參考本系列之前的博文。

{
    "name": "RelWithDebInfo",
    "displayName": "Windows x64 RelWithDebInfo Shared Library",
    "description": "面向具有 Visual Studio 開發環境的 Windows。",
    "generator": "Ninja",
    "binaryDir": "${sourceDir}/out/build/${presetName}",
    "architecture": {
        "value": "x64",
        "strategy": "external"
    },
    "cacheVariables": {
        "CMAKE_BUILD_TYPE": "RelWithDebInfo",
        "CMAKE_PREFIX_PATH": "$env{GISBasic}",
        "CMAKE_INSTALL_PREFIX": "${sourceDir}/out/install/${presetName}"
    },
    "vendor": {
        "microsoft.com/VisualStudioSettings/CMake/1.0": {
            "hostOS": [
                "Windows"
            ]
        }
    }
}

4. 總結

好了,使用Visual Studio 2019進行CMake專案的開發的步驟和注意事項就是以上內容了。其實筆者也很想使用Visual Studio 2022甚至更新的版本來進行CMake專案的開發,不過受限於工作的環境沒有進行升級。如果有試用的讀者歡迎進行留言,看看與Visual Studio 2019對比有哪些區別或者提升。

相關文章