天天看點

C++跨平台開發工具CLion——使用任意編譯器快速指南

作者:慧都科技

CLion是一款專為開發C及C++所設計的跨平台IDE,它是以IntelliJ為基礎設計的,包含了許多智能功能來提高開發人員的生産力。這種強大的IDE幫助開發人員在Linux、OS X和Windows上來開發C/C++,同時它還使用智能編輯器來提高代碼品質、自動代碼重構并且深度整合CMake編譯系統,進而提高開發人員的工作效率。

CLion官方最新版免費下載下傳試用,曆史版本下載下傳,線上文檔和幫助檔案下載下傳-慧都網

作為一款IDE,CLion提供了許多功能來幫助開發者,它可以顯示文檔彈出視窗、實時檢測錯誤、建議修複等。内部代碼分析器始終在背景運作,在您輸入時分析 C 和 C++ 代碼。 對于自動分析器來說,C 和 C++ 是具有挑戰性的語言,因為需要特定于編譯器的資料才能正确解析代碼,引擎需要知道标題搜尋路徑、預定義的宏定義和其他一些詳細資訊。

對于一組預定義的已知編譯器,CLion會使用特殊選項運作項目的編譯器,并在解析項目時收集所需資訊。 當然這隻适用于有限數量的編譯器,如GCC、Clang、MSVC、IAR及其衍生工具,如果某個項目使用特定的自定義編譯器、很少使用的編譯器或我們無權通路的專有編譯器來正确內建,則CLion無法通過其标準過程進行使用。 在這種情況下,您可以選擇使用自定義編譯器功能。

要在CLion中使用自定義編譯器,您需要一個包含必要編譯器資料的YAML檔案。 此類檔案應包含一個或多個小節,每個小節描述一個特定的編譯器或編譯器變體,每個小節應有一個描述、一個或多個比對記錄,以及多個資訊記錄。

當CLion解析項目并遇到針對項目檔案的編譯器時,CLion會逐節掃描,并檢查編譯器是否與記錄比對。 比對會使用一個或多個比對記錄來完成,可用的比對記錄包括:

  • match-compiler-exe是正規表達式,用來檢查編譯器名稱,一個與平台無關的良好示例是 "(.*/)?sdcc(.exe)?"。
  • match-source也是正規表達式,用來與要編譯的源檔案的名稱比對。 對于 C,通常為 ".*\.c",而對于 C++,通常為 ".*\.cpp"。
  • match-args是單個指令行開關、開關序列或無序開關數組。 例如,"-a" 比對實參 -a,"-b -c" 比對實參 -b -c,而 ["-b", "-c"] 比對 -b -c 或 -c -b。
  • match-language可能為 C 或 CPP,對于 CMake 項目,它會與CMake檢測到的語言比對;對于其他類型的項目,該語句會被直接忽略。

在所有比對項成功解析之後,就會選取小節資訊記錄,并跳過YAML檔案的其餘部分。 編譯器資料擷取自資訊記錄,然後直接傳遞給 CLion 的代碼分析器;也就是說,在編譯器試運作期間不收集資料。

如果這些小節都沒有為IDE提供将記錄與特定編譯器積極比對的資訊,它會嘗試以通常的方式(即通過猜測編譯器的類型并運作編譯器)來收集編譯器資料。

如何編寫自定義編譯器YAML檔案

要開始編寫自定義編譯器YAML檔案,需要确定有關您的編譯器的以下詳細資訊:

  • 您希望支援的語言,每種受支援的語言或語言變體都需要至少一個配置小節。
  • 編譯器二進制檔案名稱,可以有不止一個。 一些工具鍊具有不同的 C 和 C++ 編譯器二進制檔案、不同的記憶體模型、不同的語言變體等,這些資訊可以在工具鍊文檔中找到,并且必須反映在您的自定義編譯器 YAML 檔案中。
  • 标準包含檔案所在的目錄,要辨別這些目錄,請閱讀編譯器的文檔或直接浏覽已安裝的工具鍊檔案夾樹。
  • 預定義宏的清單,一些編譯器可以報告預定義宏的清單,而其他編譯器則不能。請參考您的編譯器文檔,以了解如何為您的編譯器擷取它們。

實用示例

我們通過為Small Device C Compiler (SDCC) 編寫一個自定義編譯器YAML檔案來實踐此過程, SDCC支援多種8位CPU架構,如STM8、Z80等。 該編譯器是開源的,是多個MCU系列中唯一的免費編譯器。 在此示例中,我們将為其Microchip PIC-16 變體(端口)編寫一個 YAML 檔案。

以下是我們需要了解的有關此編譯器的詳細資訊:

  • 僅支援 C 語言。
  • 編譯器二進制檔案名稱為 sdcc 或 sdcc.exe(在 Windows 中)。
  • -mpic16 選項對于 PIC16 架構支援是必需的,還應該使用 --use-non-free。
  • 要報告标題搜尋路徑清單,編譯器應使用 --print-search-dirs 選項運作。
  • 要報告預定義的宏,編譯器應使用 -E 和 -dM 選項和一個空白源檔案運作。

我們将從建立一個測試項目開始,此最小項目包含一個C檔案、YAML檔案本身和一個建構系統檔案,我們将使用Makefile。 如果要使用 C++,那麼還應該添加另一個使用 C++ 編寫的源檔案。

首先,建立一個項目檔案夾,并在裡面建立一個空白 Makefile。

在CLion中打開該檔案夾,然後在彈出的 Load Project(附加元件目)對話框中點選 Cancel(取消)。

在 CLion 編輯器中打開 Makefile,并将最少内容添加到該 Makefile:

clean:
all: main.c
sdcc -mpic16 --use-non-free -S main.c           

該檔案現在包含一個用于在不需要更多步驟的情況下将 main.c 編譯成程式集的指令。

建立一個 main.c 檔案,我們來建立一個經典的 “Hello, World”,并帶有額外特定于 SDCC 的附加内容,即__code 關鍵字和預定義的宏 __SDCC_pic16。

#include <stdio.h>
int main() {
printf("Hello, World!n");
return 0;
}
int __code i = 0;
#ifndef __SDCC_pic16
# error "__SDCC_pic16 macro is expected to be defined"
#endif           

接下來,我們編寫一個自定義編譯器配置存根:

  1. 建立一個名為 clion-custom-compiler-sdcc-pic16.yaml 的檔案。
  2. 将 JSON 架構“Custom Compiler Definition”配置設定給該檔案。
C++跨平台開發工具CLion——使用任意編譯器快速指南

這樣,CLion可以通過動态檔案結構驗證和輸入提示為您提供幫助。 架構名稱顯示在 IDE 狀态欄中,可以通過點選名稱進行選擇。

存根為:

compilers:
- description: SDCC for PIC-16
match-compiler-exe: "(.*/)?sdcc(.exe)?"           

目前我們隻通過名稱進行比對,它是一個比對 Linux 和 Windows 編譯器二進制檔案名稱的正規表達式,無論包含檔案夾是什麼。

下一步是在 CLion 中使用我們的自定義編譯器配置,打開 Settings/Preferences | Build, Execution, Deployment | Toolchains | Custom Compiler(設定/偏好設定 | 建構、執行、部署 | 工具鍊 | 自定義編譯器), 選中 Use custom compiler config(使用自定義編譯器配置),并選擇項目的 YAML 檔案作為配置檔案。

C++跨平台開發工具CLion——使用任意編譯器快速指南

在 Tools(工具)菜單中選擇 Makefile | Clean and Reload Makefile Project(Makefile | 清理并重新加載 Makefile 項目),如果一切正常,Build(建構)工具視窗将顯示項目已成功加載。

C++跨平台開發工具CLion——使用任意編譯器快速指南

如果項目未正确加載,則應仔細檢查 YAML 檔案中的 match-compiler-exe 語句。 記錄值是一個正規表達式,它是該過程中最脆弱的部分。

現在我們來看看CLion是否真的适配了 SDCC,我們的項目是否按預期運作。

檢查自定義編譯器是否正常運作

在編輯器中打開 main.c, 此時看到一些錯誤沒有關系。 轉到 Help | Diagnostics Tools(幫助 | 診斷工具),選擇 Show Compiler Info(顯示編譯器資訊)。 這将打開一個診斷頁面,其中包含正在編輯的檔案的編譯器資訊。 在這裡,您可以看到編譯器種類(“Custom Defined Compiler”)和檢測到的編譯器描述(“SDCC for PIC-16”)。 這意味着 CLion 感覺到了項目結構,但是代碼分析器還沒有所需的編譯器資料,是以在 main.c 中顯示了各種錯誤。

C++跨平台開發工具CLion——使用任意編譯器快速指南

收集缺少的編譯器資訊

我們的 main.c 檔案目前似乎已損壞, 找不到 stdio.h,printf 未定義,__code 修飾符錯誤,并且未定義文檔中的預定義宏。

C++跨平台開發工具CLion——使用任意編譯器快速指南

通過提供正确的編譯器資訊修正所有這些問題。

幸運的是,SDCC 可以列印标題位置和預定義的宏。 将以下各行添加到 Makefile 即可解決這個問題:

gather_info:
# List directories
sdcc -mpic16 --use-non-free --print-search-dirs
# Create a void C file
echo //void > void.c
# List predefined macros
sdcc -mpic16 --use-non-free -E -dM void.c           

接下來,我們需要建構 gather_info 目标并檢視輸出。 首先,有一個标題搜尋路徑清單:

…
includedir:
C:Program FilesSDCCbin..includepic16
C:Program FilesSDCCbin..include
C:Program FilesSDCCbin..non-freeincludepic16
C:Program FilesSDCCbin..non-freeinclude
…           

唯一的問題是這些路徑是絕對路徑,出于可移植性原因,我們将它們設為相對于編譯器位置,并将它們作為 include-dirs 數組添加到編譯器定義中。

接下來是預定義的宏,它們被列印在輸出的最底部,可以通過 defines-text 将它們添加到 YAML 檔案。

最後但同樣重要的是,我們需要通過使用 match-args: -mpic16 和 match-language: C 語句使我們的編譯器比對得更具體一些,然後作為空定義添加 SDCC 語言擴充字。 完成後,最終的 YAML 檔案将如下所示:

compilers:
- description: SDCC for PIC-16
match-compiler-exe: "(.*/)?sdcc(.exe)?"
match-args: -mpic16
match-language: C
include-dirs:
- ${compiler-exe-dir}/../include/pic16
- ${compiler-exe-dir}/../include
- ${compiler-exe-dir}/../non-free/include/pic16
- ${compiler-exe-dir}/../non-free/include
defines-text: "
#define __SDCC_USE_NON_FREE 1
#define __SDCC_PIC18F452 1
#define __18f452 1
#define __STACK_MODEL_SMALL 1
#define __SDCC_pic16 1
#define __SDCC_ALL_CALLEE_SAVES 1
#define __STDC_VERSION__ 201112L
#define __STDC_HOSTED__ 0
#define __SDCCCALL 0
#define __STDC_UTF_16__ 1
#define __SDCC_VERSION_MINOR 2
#define __STDC_ISO_10646__ 201409L
#define __SDCC_VERSION_PATCH 0
#define __SDCC_VERSION_MAJOR 4
#define __STDC_NO_VLA__ 1
#define __SDCC 4_2_0
#define __STDC_UTF_32__ 1
#define __STDC_NO_THREADS__ 1
#define __SDCC_CHAR_UNSIGNED 1
#define __STDC_NO_ATOMICS__ 1
#define __STDC__ 1
#define __SDCC_REVISION 13081
#define __STDC_NO_COMPLEX__ 1
#define __interrupt
#define __code
#define __at
"           

現在,我們可以重新附加元件目 Tools | Makefile | Reload Makefile Project(工具 | Makefile | 重新加載 Makefile 項目)并再次檢查 main.c 檔案。

C++跨平台開發工具CLion——使用任意編譯器快速指南

錯誤已經消失,并且可以導航到 stdio.h。 Show Compiler Info(顯示編譯器資訊)視窗顯示了正确的資訊,包括預定義的宏、語言功能和标題搜尋路徑。