以QT為例談環境搭建

_哲思發表於2022-03-13

以QT為例談環境搭建

作者:哲思

時間:2022.1.5

郵箱:1464445232@qq.com

GitHub:zhe-si (哲思) (github.com)

前言

自從實習結束,好久沒寫部落格了。作為2022年的開端,打算先談一談幾乎讓所有開發人員發愁的問題——環境搭建。而契機,是最近在615所做的一個QT專案中QT的環境搭建。

本文主要有兩個目的:

  1. 提供一種簡單、有效的QT安裝與環境配置解決方案
  2. 從通用軟體層面聊一聊對軟體環境的理解,並嘗試回答:我們配的環境,到底是什麼?

注:本文提到的“軟體”,不談軟體工程中 程式 + 資料 + 文件 的定義,而指一個完整可執行的程式。

QT安裝與環境配置

QT簡介

說起qt,很多人下意識的認為這是一個c++的介面開發庫,qt確實在介面開發中使用最廣泛,但它並不止於此。

簡單來說,Qt 是一個跨平臺C++應用程式開發框架,並提供給開發者一個便捷的圖形介面開發工具與較為高階的物件導向開發方法。

大家安裝與配置QT,只需按照以下步驟進行:

  1. 下載QT
  2. 安裝QT
  3. 配置任意一個 qt 開發環境
  4. 配置 qt 執行時環境

QT下載

  • 途徑一:官網下載,在 Try QT -> Download QT 註冊後下載,當前QT官網下載都需要註冊,在安裝中也需要登入賬號。

  • 途徑二:百度網盤下載,筆者提供的是 qt 5.12.12 離線安裝包(qt-opensource-windows-x86-5.12.12.exe)和 qt 線上安裝包(qt-unified-windows-x86-4.2.0-online.exe)

    下載連結,提取碼:lznc

    :可通過關閉網際網路的方式跳過安裝包的登入過程

  • 途徑三:清華映象下載

    映象中 qt 5.14.2 (該版本在大部分場景已經足夠高)及以下版本提供了安裝包,以上版本只有原始碼,若想以安裝包安裝,可以使用官網的線上下載器下載安裝。

QT安裝

離線安裝包

  1. 雙擊開啟安裝包,如圖

    qt-ins-0

    左邊是安裝步驟,在 welcom 步驟需要登入QT官網的賬號,此步驟可通過關閉網路後重新開啟安裝包跳過。

  2. 如果登入了,會增加一個 open source obligations 步驟讓你確認開源協議,個人使用者可忽略,勾選兩個選項。

    qt-ins-1

  3. setup 步驟直接next;在 installation floder 步驟選擇安裝位置,勾選“Associate common file types with Qt Creator”會自動關聯Qt相關的檔案格式到Qt Creator(就是預設用Qt creator開啟)

  4. Select Components 步驟選擇了安裝的具體元件,先從內容比較少的 Developer and Designer Tools 看起。

    qt-ins-2

    該部分是 Qt 的額外工具部分,不是 Qt 的庫,包括了必選的 Qt Creator,和可選的 Qt Creator debug工具、MinGW 工具鏈套件、Perl語言構建工具。預設選擇如圖。

    MinGW 是 Qt 附贈的工具套件,構建工具鏈需要和 Qt 的版本相容,若為了避免版本不相容的麻煩,推薦勾選該構建工具並用它構建Qt專案。

    Qt + 版本號目錄下的元件是 Qt 庫中的內容,如下圖:

    qt-ins-3

    在 Sources 之上的是Qt的各種版本,Qt版本對應構建工具鏈,需要與構建工具鏈版本相容(一般會向下相容,但儘量保持一致)。各種版本的 Qt 根據使用場景至少選擇一套。

    • MSVC是visual studio(以下簡稱vs)的預設構建工具,版本對應vs的版本。舉例:使用64位 vs2017的使用者需要選擇MSVC 2017 64-bit。

    • MinGW,是 GNU 針對 windows系統的最小標頭檔案與庫的集合,通過將 Linux 標準的API介面翻譯為 Windows 標準的API介面來實現跨平臺專案開發,此處筆者推薦採用 MinGW 開發(更標準,易跨平臺)。舉例:開發工具鏈選擇MinGW7.3.0-64bit,則此處選擇

    • UWP是 windows 提出的通用 windows 開發的專案,期望讓不同種類的終端上跑的windows系統提供相同的API。

    • 同時,Qt還為 Android 提供了一套開發工具庫。

    Sources是Qt的原始碼,推薦勾選;其下是Qt的補充庫,提供了web等相關元件,並不必要,但由於不大,可以勾選上。

  5. 之後就一路next就好,如果讓同意協議,就點選同意,其他全部預設,安裝成功撒花。

線上安裝包

線上安裝包是一個下載器,可以根據你的選擇自動從官方倉庫下載對應元件,版本可以自由挑選,但可能下載較慢。

安裝的基本步驟與離線一致,在選擇安裝元件的時候,線上安裝包顯示了當前所有版本的 qt,以下是選擇 qt6.1.2 時筆者推薦的選擇,如有特殊需要(如串列埠相關),可以在Additional Libraries中新增,這裡筆者在 Tools 中選擇了與 Qt6.2.1 中 QT MinGW版本一致的 MinGW工具鏈套件,如下圖:

qt-ins-4

qt-ins-5

QT開發環境配置

QT Creator配置

無需配置,在安裝時自動配好,QT Creator會自動找到所有安裝的QT 版本、構建工具鏈(此處為編譯器)、debugger、cmake。當前配置可在 工具 -> 選擇 -> Kits 檢視與修改。

qt-creator-1

MinGW 構建工具鏈直接包含了debug工具 gdb.exe,可以直接使用。

若使用 msvc 構建工具鏈,其中不包含 debug 工具,需要自行補充安裝。Windows 10 的除錯工具(WinDbg)整合在了 Windows 10 sdk中,若沒有安裝過該 sdk,可以根據官網教程安裝或者安裝一個vs;若已經安裝過 Windows 10 sdk(安裝過vs一般會預設安裝一個),可以在 控制皮膚 -> 程式和功能 找到 Windows Software Development Kit- Windows,右鍵更改,新增debug工具,如下:

qt-msvc-dbg-1

qt-msvc-dbg-2

qt-msvc-dbg-3

配置其他開發環境的 msvc debug工具,方法同上。

visual studio配置

  1. 在 vs 工具 -> 擴充與更新 -> 聯機 搜尋與安裝 Qt Visual studio Tools,之後重啟vs

    qt-vs-1

  2. 在 vs -> Qt VS Tools -> Qt Versions 配置對應版本的QT目錄,需要在 path 選擇 qt 安裝路徑下 bin -> qmake.exe,在 vs 中要配置 msvc 版本的 qt。(該介面可能在不同 vs 版本下不同,但都是新增新安裝的 qt 版本)

    qt-vs-2

  3. 配置專案的 qt 引數,在專案右鍵 -> 屬性 -> Qt Project Setting

    qt-vs-3

    主要引數有兩個,在 Qt Installation 選擇剛配置的 qt,在 Qt Modules 新增需要使用的 Qt 模組。

clion配置(CMake)

clion無自己的工程配置,而是採用了 Cmake 進行專案管理。筆者比較喜歡使用該方式,配置比較清晰,專案也比較跨平臺。

clion需要配置兩個地方,構建工具鏈與工程的 CmakeLists 檔案。

  1. 構建工具鏈

    clion沒有預設的構建工具鏈,需要自行安裝 MinGW、clang或使用 vs 的構建工具,若在環境變數配置了構建工具,可自動找到。也可以自行配置,在 檔案 -> 設定 -> 構建、執行、部署 -> 工具鏈 點選 + 進行新增。

    在最上面的為預設構建工具鏈,在專案中也可以指定其他的,但要選擇與 qt 版本相容的構建工具鏈(最好版本對應)。

    qt-clion-1

  2. CmakeLists.txt

    CmakeLists.txt 是 cmake 工程的工程檔案,描述了工程的所有配置與構建,具體語法推薦大家在官網教程中學習。

    以下給出一個 cmake 配置 qt 的 CmakeLists 配置,相信大家都一目瞭然:

    cmake_minimum_required(VERSION 3.15.5)
    project(ProjectName)
    
    set(CMAKE_CXX_STANDARD 17)
    set(CMAKE_AUTOMOC ON)
    set(CMAKE_AUTORCC ON)
    set(CMAKE_AUTOUIC ON)
    
    
    # 本地qt安裝路徑(指定了庫的查詢位置,不是執行環境,若不指定,從path讀取,若找不到,找不到Qt庫)
    set(CMAKE_PREFIX_PATH "D:/env/c++/qt/qt5_12_12/5.12.12/mingw73_64")
    
    # 從 qt 庫中找到對應的元件庫(需要根據自己需要設定),但還沒將庫連結到產物
    find_package(Qt5 COMPONENTS
            Core
            Gui
            Widgets
            SerialPort
            Xml
            REQUIRED) # 表示如果找不到某個元件,cmake載入專案時報錯
    
    # 指定標頭檔案的搜尋路徑
    include_directories(
    	# find_package core元件自動新增qt include目錄
    )
    
    # 指定引用的外部庫的搜尋路徑
    link_directories(
    	# find_package 匯入元件時,就已經找到元件的位置,無需額外指定
    )
    
    # 新增產物
    add_executable(EXEName
            main.cpp
            )
    # 連結lib到產物(將 Qt 的庫也連結進去)
    target_link_libraries(TE_TEST
            Qt5::Core
            Qt5::Gui
            Qt5::Widgets
            Qt5::SerialPort
            Qt5::Xml
            )
    

QT執行環境配置

將安裝的 qt 下的 bin 目錄配到環境變數 path 中即可。

構建工具鏈(MinGW、MSVC等)無需配置到 path 環境變數中,但如果想要在命令列使用 gcc 命令等,可以進行配置。注意不要把多個版本的構建工具鏈在 path 中進行配置。

小結

至此,QT環境配置完成,從在整合開發環境編寫程式碼並編譯、編譯過程正確連線到對應的靜態連結庫,到在 ide 中執行/除錯或直接雙擊 exe 開啟。

軟體環境理解

作為一個軟體開發者,筆者多次被配置環境所折磨,尤其是跑別人的專案或者使用框架的時候。

這次配置 QT 環境,原來的專案是在 vs 下的 QT工程,經筆者根據網上教程的小心配置,終於不再飄紅,跑過了構建,又在執行過程中找不到 libgcc_s_seh-1.dll和QtCored.dll、無法定位到程式輸入點於動態連結庫等諸多問題,如下:

qt-error-5 qt-error-3 qt-error-6 qt-error-4 qt-error qt-error-2

經過艱苦卓絕的奮戰,終於解決了一個個奇怪的問題,並搞清了問題的根源。忽然回顧了一下多次配環境的經歷與解決方法,發現原理都大同小異,在搞明白了之後,配其他環境也有種輕車熟路的感覺。所以想借此機會談一談,什麼是軟體環境?到底如何配軟體環境?

QT環境

重新回顧一下我們剛剛配的 QT 環境,我們都幹了什麼?

安裝QT

首先我們下載安裝了一個QT,安裝是什麼?

最簡單的安裝就是解壓壓縮包。安裝包就是一個壓縮包,內部可能包含了多個可執行檔案(exe)、動態連結庫(dll)等檔案,我們將它解壓縮到某個資料夾就是最簡單的安裝過程。而我們就是要正確執行其中的某個或某幾個可執行檔案。

有時,安裝中還伴隨著下載過程,相當於安裝包只包含了部分的程式檔案,在安裝中可以根據使用者選擇自動下載其他附加功能的程式檔案。

在安裝中,有時要我們選擇是否在桌面或者選單新增快捷方式,其實就是根據程式的主可執行檔案生成快捷方式並放到桌面或者選單資料夾。以下是筆者的選單資料夾:

env-1

有些時候,程式希望能在全域性找到自己,就會把自己的可執行檔案或者動態連結庫新增到 path 環境變數下,或者放到已經新增到 path 的資料夾中。可以在cmd輸入“path”檢視當前的path環境變數包含的內容:

env-2

還有些時候,安裝包希望讓作業系統感知到安裝的軟體,還會把自己的資訊寫到登錄檔中。下面是筆者電腦中QQ在登錄檔中註冊的資訊:

env-3

安裝還有根據軟體需要而產生的很多個性化需求,但無外乎都是讓安裝的軟體讀寫某些資訊或者找到某個檔案,進而可以正確執行。

對於 QT,它從安裝包與網路上的官方倉庫獲得需要的檔案後,全部放到了安裝路徑,並將自己的資訊寫到登錄檔,告訴了作業系統它的存在。但並未在環境變數進行配置,所以無法在全域性直接找到 QT。

配置QT開發環境

接下來,我們配置了 QT 的開發環境。

像開發c/c++的 dev c++、visual studio、clion、開發 java 的 eclipse、idea等,相信大家都不陌生。而他們都有一個統一的名字,叫 IDE,全名“整合開發環境”。下圖是筆者常用的 IDE :

env-ide-1

引 RedHat 對 IDE 的定義:整合開發環境(IDE)是一種用於構建應用程式的軟體,可將常用的開發人員工具合併到單個圖形使用者介面(GUI)中。

IDE 主要包括一個原始碼編輯器、本地構建版本自動化工具(也就是程式碼的編譯、封裝等)、偵錯程式,當然還可能整合了一些其他開發中的工具,如資料庫訪問工具、效能分析器等。

所以顧名思義,我們配置 QT 的開發環境,就是將 QT 的開發 sdk 整合到 ide 中(讓 IDE 能找到 QT 的 sdk),好讓 ide 可以使用 sdk 完成 QT 的配置、構建、執行/除錯工作,同時複用 ide 提供的其他工具。

在前面,我們配置了 c++ 的構建工具鏈(MinGW、MSVC),用來構建一般的 c++ 程式。

同時配置了 QT 的特有構建工具

  • vs 的 QT VS Tools 採用的是 qmake 進行專案構建描述,需要配置 qmake 的位置,而 qmake 知道如何找到和使用 QT 對應的構建工具與庫。
  • clion(cmake)不直接配置並使用 QT 的某個工具,而是採用 cmake 對構建的通用描述能力。QT 在庫中編寫 cmake 可以直接讀取的庫的描述檔案。該描述檔案描述了應該如何找到 uic、rcc、moc 等 QT 特有的構建工具並使用它們將 .ui、.rcc、QT 元物件等處理為一般 c++ 的原始碼。該描述也說明了 QT 庫的位置,所以我們才可以通過 find_package 自動找到對應的庫。

配置QT執行時環境

最後,我們配置了 QT 的執行時環境。

執行時環境是什麼?顧名思義,就是程式執行的時候它所處的環境。而我們說某個軟體的執行時環境,也就是在說該軟體對外界的依賴。

這樣就很清晰了,為什麼軟體在執行時環境不對時會報錯?因為軟體在這個執行時環境中執行,依賴(也就是使用)了環境中的其他東西,當這個東西找不到或者和我們想象中的不一樣,我們沒法使用它時,程式就執行不下去了,就會報錯。

那環境中我們的依賴是什麼呢?在大部分情況下,我們可以說是動態連結庫。動態連結庫在 Windows 下字尾名為 .dll,在 linux 下為 .so。

那 QT 程式需要什麼依賴呢?就是 QT 安裝目錄中 bin 內的 dll(Windows 下的動態連結庫)。所以我們把 QT 的 bin 目錄配到環境變數裡即可。

注:此處無需配置構建工具鏈的 bin 目錄。

軟體環境

剛才我們重新回顧了一遍 QT 的安裝與環境配置的流程與每一個流程的意義。回顧其他安裝與配置的流程,便會發現都大同小異,而其中的原理都是一致的,我們可以基於 QT 的配置由點到面的總結一下:

一個程式,從原始碼到執行,包括:編譯(compile)、連結(link)、載入(load)、執行(execute),對應的GNU工具一般為:編譯器compiler(gcc)、連結器linker(ld)、載入器loader。

一般編譯和連結統稱為編譯,期間為編譯時(compile time);而載入和執行統稱為執行,期間為執行時或執行時(runtime/execution time)。

  • 安裝

    安裝就是解壓,並幫助使用者便捷的配置執行時環境的過程。概念比較簡單,在上一節“QT 環境”的“安裝QT”小節中已基本敘述清楚。

  • 軟體環境

    • 開發環境(編譯時環境是開發環境的子集):

      • 構建工具:如編譯器、連結器、QT元物件處理器 moc、QT 使用者介面編譯器 uic、QT 資原始檔編譯器 rcc 等。這些構建工具也有自己的執行時環境(依賴其他動態庫等),共同組成了被開發的軟體的編譯時環境。

      • 除錯工具:gdb

      • 專案依賴的靜態連結庫、標頭檔案與隱式載入動態連結庫:

        專案在編譯過程中需要使用的外部庫。

        • 靜態連結庫:

          編譯時(compile time)被使用(連結的時候)。在連結靜態庫的時候,連結器會在其中找到所需要連結的函式,然後將它們拷貝到執行檔案,這種拷貝是完整的拷貝,所以在連結成功後,程式執行不需要靜態庫的參與。

        • 標頭檔案:

          函式或型別等的宣告檔案,向編譯器宣告該函式或型別等已經存在,在其他地方有宣告的具體實現,在連結的時候將二者關聯起來,如果找不到具體實現,會報錯:

          undefined reference to xxx
          

          靜態連結庫與隱式載入的動態連結庫都是宣告的實現可能存在的地方。

        • 隱式載入的動態連結庫:

          在執行時環境介紹動態連結庫時進行詳細介紹。

      • 其他:如程式碼分析器、效能分析器等,文字編輯與程式碼提示也包含在內。

    • 執行時環境 / 執行時環境:

      • 動態連結庫

        動態連結庫的查詢方式是名字查詢,也就是說,呼叫某個函式,只要動態連結庫記憶體在該名字的函式(函式簽名也一致),哪怕不是一開始的動態連結庫也可以正常呼叫。

        • 隱式載入

          編譯時和執行時被使用。在編譯時,連結器在其中找到所需要的函式(或其他物件檔案),生成地址/位置無關程式碼(Position Independent Code (PIC)),並沒有真正的實現拷貝;在執行時(runtime/execution-time),某個程式在執行中要呼叫某個動態連結庫函式的時候,作業系統首先會檢視所有正在執行的程式,看在記憶體裡是否已有此庫函式的拷貝了,如果有,則讓其共享那一個拷貝;只有沒有才連結載入。

          如果連結庫被更換,系統會自動同步記憶體。

        • 顯示載入

          在程式碼中主動呼叫系統 API 載入動態連結庫。由於指定了準確的位置,與讀取檔案類似。

      • 其他軟體

      • 資料庫

      • 檔案

      • 系統登錄檔

      • 等等,可以說凡是軟體使用到的都是其執行環境,軟體最大的軟體環境就是作業系統了。

“配環境”,就是指讓系統無二義性的找到我們想要的外部依賴。

開發環境

開發環境簡單來說就是程式猿敲程式碼的環境,從我們建立一個專案、到原始碼編輯與版本管理、再到原始碼構建、最後到程式除錯等一整個開發流程。

最基本的開發環境就是編譯器和連結器,其次是文字編輯器,再然後是偵錯程式、專案構建工具等等。

在開發中,我們可以分別使用每一個工具(可執行檔案)去處理某個開發流程;也可以使用一個 ide 整合開發環境,告訴 ide 那些工具在哪裡,這樣你就可以在 ide 中便捷的使用所有的工具了。

一般開發環境問題較少,但請注意:

  • 有些庫是與構建工具鏈的版本一一對應的
  • 有些庫分 release / debug 版本、64 / 32 位,儘量選擇場景對應的版本

執行時環境

執行時環境是出問題最多的地方,尤其是執行時環境中的動態連結庫,因為它採用執行時動態查詢連結庫的位置。

我們還沒有詳細說明“找不到”和“不一樣”的具體含義。那什麼時候我們會找不到依賴呢?這就要先說系統是如何找這個依賴的,這裡以 Windows 傳統的桌面應用程式 和 linux 為例。

  • Windows 桌面應用程式

    在 Windows 傳統的桌面應用程式中(官方說明),系統會按照以下順序搜尋目標(以下為預設的安全 DLL 搜尋模式,非安全模式就是將第5項提前插入到第二項,也就是當前目錄優先於系統的庫目錄):

    1. 從其中載入應用程式的目錄。
    2. 系統目錄。 使用 GetSystemDirectory 函式獲取此目錄的路徑。
    3. 16 位系統目錄。 沒有函式可獲取此目錄的路徑,但會進行搜尋。
    4. Windows 目錄。 使用 GetWindowsDirectory 函式獲取此目錄的路徑。
    5. 當前目錄。
    6. PATH 環境變數中列出的目錄。 請注意,這不包括應用路徑登錄檔項指定的 每個應用程式路徑 。 計算 DLL 搜尋路徑時,不會使用應用路徑金鑰。
  • linux

    1. 編譯目的碼時指定的動態庫搜尋路徑;
    2. 環境變數 LD_LIBRARY_PATH 指定的動態庫搜尋路徑;
    3. ldconfig 快取配置檔案 /etc/ld.so.conf 中指定的動態庫搜尋路徑;
    4. 系統預設動態庫搜尋路徑 /lib;
    5. 系統預設動態庫搜尋路徑 /usr/lib。

找不到動態連結庫,通常會報錯:

由於找不到 dll, 無法繼續執行程式碼。

這裡還有幾個注意點:

  • 查詢的順序的前後

    系統會按照順序根據庫的名字進行查詢,直到找到第一個符合的動態庫。也就是說當出現同名動態庫,前面的配置優先於後面的配置。

    這裡還要注意,一般有一個環境變數作為使用者自定義的動態庫搜尋路徑集合,而該環境變數的賦值是覆蓋方式,可能存在非增量式覆蓋或覆蓋了後面其他程式使用的動態連結庫。

  • 記憶體已經載入某靜態庫的問題

    如果記憶體已載入某動態庫,則後來的程式如果依賴該名字的動態庫,系統會直接使用記憶體中的同名動態庫,不會重新搜尋。

而“不一樣”指的是與預期呼叫的函式宣告不一致。這可能由於版本不一致,也可能被後新增的同名庫覆蓋導致呼叫了錯誤的動態庫等,而該錯誤下通常會報錯:

無法定位程式輸入點 xxx 於 xxx.dll 上

總結,把書本讀薄

讀到這裡,想必大家對軟體環境與環境搭建有了一定的認識,但可能上面的文字太多,還不夠清晰,讓我們一起把書讀薄。

  • 環境主要有兩種:開發環境、執行時環境
  • 開發環境下的產物就是軟體本身,而軟體想要獨立執行,就需要在合適的執行時環境下
  • 配置環境就是讓系統無二義性的找到外部的依賴,如構建工具、靜態庫、動態庫等
  • 配置開發環境重點要解決構建工具鏈與靜態連結庫的相容、靜態連結庫與構建目標(x86 / x64、release / debug)的相容
  • 配置執行時環境重點解決確定軟體的實際執行時環境、讓系統正確找到對應的動態連結庫兩個問題

後記

理解了軟體的環境與環境的搭建對一個開發人員十分重要,不但大大減輕了配環境帶來的成本,也讓我們的軟體包更清晰易用、相容性好。

包括 python 的 pip、烏班圖的 apt 包管理器也是這個道理,但它們通過更清晰的軟體包的依賴自描述讓軟體包管理變得更加明確、簡單。而很多遊戲將所有依賴的動態連結庫都打包到遊戲中也是一個經典的解決執行時依賴的方法。

最後,獻出美好的祝福:願世間再無因配環境而痛苦的程式猿!

相關文章