【EDK2】在UDK2018中實現兼容Vscode中的Edk2Code插件
原理
新版 EDK2 把“生成編譯信息(compile_commands.json 等)”做在 BaseTools/Source/Python/build/BuildReport.py 里的 BuildReport 類里,并通過 -Y COMPILE_INFO -y BuildReport.log 開關觸發。
官方 issue / 文檔和擴展插件都在用這個開關(不是 REPORT_INFO)來生成 Build/…/CompileInfo/compile_commands.json 等文件。
其中-Y COMPILE_INFO -y BuildReport.log編譯選項是近期才加上的,不能兼容老版本的UDK2018,很多現有的項目是基于老版EDK2,因此,需要改動做一些兼容。
代碼修改
改動一:BuildReport.py
文件:
BaseTools/Source/Python/build/BuildReport.py
1)在文件頂部 import 區補充(如已有相同 import 可略過):
import json
from Common.Misc import SaveFileOnChange
from Common.DataType import TAB_COMPILER_MSFT
2)在 class BuildReport(): 內添加方法:
def GenerateCompileInfo(self):
"""
生成供 IDE/clangd/vscode 使用的編譯數據庫,以及輔助文件。
輸出目錄:<Build>/<BuildTarget>/<ToolChain>/CompileInfo/
輸出文件:compile_commands.json, cscope.files, module_report.json
"""
try:
compile_commands = []
used_files = set()
module_report = []
# self.ReportList 由現有 BuildReport 邏輯維護(與原有報表一致)
for (Wa, MaList) in self.ReportList:
# 工作區已處理文件(盡可能多地記錄)
try:
for fp in Wa._GetMetaFiles(Wa.BuildTarget, Wa.ToolChain):
used_files.add(fp)
except Exception:
pass
for autogen in Wa.AutoGenObjectList:
# 遍歷模塊與庫(與新版 EDK2 的思路一致)
for module in (autogen.LibraryAutoGenList + autogen.ModuleAutoGenList):
used_files.add(module.MetaFile.Path)
# —— 可選:模塊摘要(用于 module_report.json)——
md = {
"Name": module.Name,
"Arch": module.Arch,
"Path": module.MetaFile.Path,
"Guid": getattr(module, "Guid", ""),
"BuildType": getattr(module, "BuildType", ""),
"IsLibrary": getattr(module, "IsLibrary", False),
"SourceDir": getattr(module, "SourceDir", ""),
"Files": [],
"Libraries": [],
"Packages": [],
"PPI": [],
"Protocol": [],
"Pcd": []
}
for sf in module.SourceFileList:
md["Files"].append({"Name": sf.Name, "Path": sf.Path})
for libag in getattr(module, "LibraryAutoGenList", []):
md["Libraries"].append({"Path": libag.MetaFile.Path})
for pkg in getattr(module, "PackageList", []):
entry = {"Path": pkg.MetaFile.Path, "Includes": []}
for inc in getattr(pkg, "Includes", []):
entry["Includes"].append(inc.Path)
md["Packages"].append(entry)
for k in getattr(module, "PpiList", {}).keys():
md["PPI"].append({"Name": k, "Guid": module.PpiList[k]})
for k in getattr(module, "ProtocolList", {}).keys():
md["Protocol"].append({"Name": k, "Guid": module.ProtocolList[k]})
for pcd in getattr(module, "LibraryPcdList", []):
md["Pcd"].append({
"Space": getattr(pcd, "TokenSpaceGuidCName", ""),
"Name": getattr(pcd, "TokenCName", ""),
"Value": getattr(pcd, "TokenValue", ""),
"Guid": getattr(pcd, "TokenSpaceGuidValue", ""),
"DatumType": getattr(pcd, "DatumType", ""),
"Type": getattr(pcd, "Type", ""),
"DefaultValue": getattr(pcd, "DefaultValue", "")
})
module_report.append(md)
# 生成 compile_commands 項(僅 C/C++ 源)
inc_flag = "/I" if module.BuildRuleFamily == TAB_COMPILER_MSFT else "-I"
for src in module.SourceFileList:
used_files.add(src.Path)
if src.Ext not in [".c", ".cc", ".cpp", ".cxx"]:
continue
# 基于 BuildRules 獲取單條編譯命令模板
try:
rule_cmd = module.BuildRules[src.Ext].CommandList[0]
except Exception:
# 回退:無法解析就跳過該文件
continue
# 展開 $(VAR) 變量(與新版實現思路一致,盡量保守)
def _expand_var(m):
token = m.group(1)
parts = token.split("_")
try:
if len(parts) == 1:
return module.BuildOption[parts[0]]["PATH"]
else:
return module.BuildOption[parts[0]][parts[1]]
except Exception:
return ""
build_cmd = re.sub(r"\$\((.*?)\)", _expand_var, rule_cmd)
# 處理常見占位:${src}(包含路徑列表),${dst}(輸出目錄)
try:
incs = getattr(module, "IncludePathList", [])
# 構造 “/Ipath1 /Ipath2 …”
inc_blob = " ".join([(inc_flag + "\"" + inc + "\"") if " " in inc else (inc_flag + inc) for inc in incs])
build_cmd = build_cmd.replace("${src}", inc_blob)
build_cmd = build_cmd.replace("${dst}", getattr(module, "OutputDir", ""))
except Exception:
pass
# 清理未展開殘留形如 $(XXX) 的片段
build_cmd = re.sub(r"\$\((?:.*?)\)", "", build_cmd).strip()
# compilation database 條目
entry = {
"file": src.Path, # 保持與 EDK2 新版一致:絕對路徑
"directory": src.Dir, # 編譯時工作目錄
"command": build_cmd # MSVC 風格 cl.exe 命令
}
compile_commands.append(entry)
# 輸出目錄:Build/.../CompileInfo
compile_info_dir = os.path.join(Wa.BuildDir, "CompileInfo")
if not os.path.isdir(compile_info_dir):
try:
os.makedirs(compile_info_dir)
except Exception:
pass
# 排序并寫出
compile_commands.sort(key=lambda x: x["file"])
SaveFileOnChange(os.path.join(compile_info_dir, "compile_commands.json"),
json.dumps(compile_commands, indent=2), False)
SaveFileOnChange(os.path.join(compile_info_dir, "cscope.files"),
"\n".join(sorted(used_files)), False)
module_report.sort(key=lambda x: x["Path"])
SaveFileOnChange(os.path.join(compile_info_dir, "module_report.json"),
json.dumps(module_report, indent=2), False)
except Exception:
from Common import EdkLogger
import traceback, platform, sys
EdkLogger.error("BuildReport", 0, "Unknown fatal error when generating compile information",
ExtraData=getattr(self, "ReportFile", None), RaiseError=False)
EdkLogger.quiet("(Python %s on %s\n%s)" % (platform.python_version(), sys.platform, traceback.format_exc()))
3)在生成報表的入口處掛鉤調用(GenerateReport 里追加判斷):
找到 def GenerateReport(self, BuildDuration, AutoGenTime, MakeTime, GenFdsTime):在它寫日志/報表的 try 塊開頭,加上:

4)在下圖所示加入一行:

改動二:build.py(給 -Y 添加 COMPILE_INFO 選項)
文件:
BaseTools/Source/Python/build/build.py
找到命令行解析對 -Y/--report-type 的定義,把允許值里加入 COMPILE_INFO。
Parser.add_option("-Y", "--report-type", action="append", type="choice", choices=['PCD', 'LIBRARY', 'FLASH', 'DEPEX', 'BUILD_FLAGS', 'FIXED_ADDRESS', 'HASH', 'EXECUTION_ORDER', 'COMPILE_INFO'], dest="ReportType", default=[],
help="Flags that control the type of build report to generate. Must be one of: [PCD, LIBRARY, FLASH, DEPEX, BUILD_FLAGS, FIXED_ADDRESS, HASH, EXECUTION_ORDER, COMPILE_INFO]. "\
改動三:Common/Datatype.py
文件:
BaseTools/Source/Python/Common/Datatype.py
在最后一行加上
TAB_COMPILER_MSFT = 'MSFT'
使用與驗證
使用python編譯
edksetup.bat執行后,build -h查看-Y選項下是否有COMPILE_INFO如果有,說明build使用的是python腳本。- 正常編譯,但增加(注意要使用Python2.7編譯UDK2018):
build -p <YourDsc> -a IA32 -t VS2015x86 -b DEBUG -Y COMPILE_INFO -y BuildReport.log^a359b1 - 在你的 build 產物目錄(例如
Build/NT32IA32/DEBUG_VS2015x86/CompileInfo/)應出現:
compile_commands.jsoncscope.filesmodule_report.json
VS Code(MS 的 C/C++ 插件)設置 C/C++: Compile Commands 指向上面的 compile_commands.json。我推薦使用clangd,因為生成的這個json文件很大,C/C++插件總是索引很慢,知道怎么解決的大佬請留言,感謝!
安裝了Edk2Code插件后,ctrl + shift + p 輸入EDK2: rebuild index database,選擇你編譯后的./EDK2/Build目錄,讓 Edk2Code正確索引database。官方與社區都推薦用這個流程。
如果發現build -h的-Y沒有COMPILE_INFO,則證明使用的是build.exe編譯,可以將EDK2/BaseTools/Bin/Win32/build.exe重命名為1build.exe,這樣EDK2會自動使用python編譯。
使用build.exe編譯
如果沒有環境,無法使用python編譯,則麻煩一些,需要將build.py 編譯為build.exe。
- python注意版本是2.7.14,另外還需要安裝
cx_Freeze-4.2.3.win-amd64-py2.7.msi,這個版本的cx_Freeze不好找,如果需要可以聯系我。 - 備份一個
EDK2/Basetools整個目錄 - 在
EDK2/Basetools/目錄下,進入cmd,運行nmake /f Makefile clean,再運行nmake /f Makefile - 將編譯好的
build.exe以及Genfds.exe替換原來的,這樣就可以使用build.exe - 按照從這開始的操作步驟,一步步也可以生成
Build/.../CompileInfo整個文件夾。

浙公網安備 33010602011771號