contributed by <
Shiritai
>
我將改進版的腳本連同說明文件放置於 make2intelliSense repository,歡迎檢閱、使用與不吝指教 :)
以 fibdrv
這個作業專案為例,如果純粹將 reposiroty clone 好後使用 VSCode 開啟比如 fibdrv.c
,相信會看到貼心的 IntelliSense 的警告。
這是因為預設的 include path 並不包含我們需要的核心標頭檔。
由前車之鑑 1 和 2 得知 IntelliSense 需要調整 C/C++ Extension 設定檔: c_cpp_properties.json
。
準備 Linux 設定檔模板
{
"configurations": [
{
"name": "Linux",
"browse": {
"limitSymbolsToIncludedHeaders": true,
"databaseFilename": ""
},
"intelliSenseMode": "linux-gcc-x64",
"compilerPath": "/usr/bin/gcc",
"cStandard": "c99",
"cppStandard": "gnu++17"
}
],
"version": 4
}
加入 includePath
設定
...
"name": "Linux",
"includePath": [
"${workspaceFolder}/**",
"/usr/include",
"/usr/local/include",
"/usr/src/LINUX_KERNEL_HEADER/include",
"/usr/src/LINUX_KERNEL_HEADER/arch/x86/include",
"/usr/src/LINUX_KERNEL_HEADER/arch/x86/include/generated",
"/usr/lib/gcc/x86_64-linux-gnu/GCC_VERSION/include"
],
"browse": {
...
要調整兩點。
LINUX_KERNEL_HEADER
換成自己的 kernel header 路徑名。GCC_VERSION
換成自己的 GCC 版本。基本上 includePath
中前五項都是開發 Kernel Module 的常客,當然第七項編譯器本身也是。第六項本次實作會用到,不過未來開發其他模組的話可能會引入其他的標頭檔。
尋找標頭檔的方法為至 include
資料夾下尋找被 include 的標頭檔,將其所在的資料夾加入 includePath
。
又以 lab0-c
作業中探討 list_sort.c
為例,即使直接將 linux
這個 repository clone 下來並修正 include path 問題,還是會發現一些巨集或函式無法被識別,而被 IntelliSense 自動猜測為 int FUNC_NAME()
。
likely 和 unlikely 兩巨集為編譯器提供的巨集,為了提示編譯器判斷分支發生的可能性,編譯器便可以根據此提示進行最佳化,此文提供了詳細的說明。
使用時須引入 <linux/compiler.h>
,同時可以進入原始碼查看巨集的細節。發現與 __KERNEL__
以及其他數個巨集有關:
以下展示部分,前面還有另一個定義。
這些巨集定義都影響著 IntelliSense 的運作,若不告訴它我們編譯時定義的巨集,IntelliSense 就不會正常運作。
defines
是為了解決條件編譯的問題,當中加入的字串會使 IntelliSense 在 indexing 時看見需指定條件編譯後才會編譯到的程式碼。
基本上加入以下巨集定義就能解決大部分條件編譯的影響。
...
"compilerPath": "/usr/bin/gcc",
"defines": [
"__GNUC__",
"__KERNEL__",
"MODULE"
],
"cStandard": "c99",
...
由於前面設定正確的 include path,並加入 __KERNEL__
等巨集, IntelliSense 復活了 :)
Include Path 的例子
Include Path + 巨集設定的例子
正常進行編譯後,以 Linux 核心為例,可能會看到目錄看見以下:
不僅不美觀,VSCode 更會去遞迴的解析這些不必要解析的檔案,從而降低 VSCode 的速度,增加搜尋程式碼的負擔。
對此,VSCode 有很多設定可以調整對特定檔案的排除,不過最便捷的應該是以下兩種。
直接套用 .gitignore
的設定
// .vscode/settings.json
{
"explorer.excludeGitIgnore": true,
}
利用 files.exclude
決定檔案的去留
// .vscode/settings.json
"files.exclude": {
"**/*.a": true,
"**/*.cmd": true,
"**/*.d": true,
"**/*.elf": true,
...
"**/Module.symvers": true,
"**/Thumbs.db": true,
"**/client": true,
"**/dkms.conf": true,
"**/modules.order": true,
"**/out": true
}
後者若要手動添加會有點麻煩…
在開發已經打好基礎之模組的情況下,本方法依賴完好的 Makefile
和 .gitignore
便可自動生成設定檔,因為 MakeFile
是告訴我們編譯方法的第一手資料、.gitignore
則是告訴我們哪些檔案沒有顯示的必要。
直接手動追蹤 Makefile 固然可行,但若以 Linux 核心原始碼為例,如果打開其 Makefile,我們會看到
以 Linux 6.3.0 為例,兩千一百多行的 Makefile
…別開玩笑了,這不是肉眼可以追蹤的量吧。對小的專案沒問題,但我們的目標是 Linux 核心…
對此,本網站提供很暴力但可行的解決方法: 利用 make
的 --just-print
功能 (其會印出若真的運行時會執行的命令),以獲得包含 includePath
,defines
等訊息,透過 Python 腳本解析後生成方才提及之常規做法的模板。
本方法的特色有二
但由於一次粗暴的解析所有編譯時加入的巨集,故有兩缺點:
Note: 有改善的空間,比如針對不同次編譯為 VSCode 客製化編譯時的巨集定義。不過需要更查詢 VSCode 的官方文件…
針對第一個缺點,我提出了改進版的腳本。當中加入了一些例外處理與客製化設定,另根據Makefile 語法和官方文件練習寫簡單的 Makefile
。
具體請參考此腳本。
加入對排除檔案的調整
exclude
: 手動加入想排除之 pattern 的列表bypass
: 不希望排除之 pattern 的列表.vscode
避免其被忽略。
bypass = {
'.vscode'
}
replace
: 將指定偵測到的排除檔案置換成自己想要的 pattern
.*
(匹配以 .
為首之任意長度字串) 忽略 .vscode
資料夾。
replace = {
".*": ".[!v]*" # exclude files start with . but not .v
}
VSCode 採用 Unix 下經典的 glob 語法匹配檔名 pattern,但不同於
bash
,VSCode 並不支援 glob 的擴充語法,不如 regex 靈活。不過語法規則少很多,相信看維基就能理解了。
可能的改進:
gen_file_exclude.py
的 exclude
, replace
, bypass
三個參數開放在執行 make 時客製化。make clean
命令: 嘗試刪除目標資料夾下 .vscode 內的設定檔
針對 c_cpp_properties.json
,新增自動偵測以下客製化設定
gnu11
和 gnu99
,則會採用 gnu99
。c99
,這可能會使 IntelliSense 有些許不符合預期。方法為使用 regex,正好把以前不願面對的它重新學了一遍。