找回密码
 立即注册
首页 业界区 业界 【EDK2】在UDK2018中实现兼容Vscode中的Edk2Code插件 ...

【EDK2】在UDK2018中实现兼容Vscode中的Edk2Code插件

郁兰娜 2025-9-24 12:21:32
原理

新版 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 可略过):
  1. import json
  2. from Common.Misc import SaveFileOnChange
  3. from Common.DataType import TAB_COMPILER_MSFT
复制代码
2)在 class BuildReport(): 内添加方法:
  1.    def GenerateCompileInfo(self):
  2.        """
  3.        生成供 IDE/clangd/vscode 使用的编译数据库,以及辅助文件。
  4.        输出目录:<Build>/<BuildTarget>/<ToolChain>/CompileInfo/
  5.        输出文件:compile_commands.json, cscope.files, module_report.json
  6.        """
  7.        try:
  8.            compile_commands = []
  9.            used_files = set()
  10.            module_report = []
  11.            # self.ReportList 由现有 BuildReport 逻辑维护(与原有报表一致)
  12.            for (Wa, MaList) in self.ReportList:
  13.                # 工作区已处理文件(尽可能多地记录)
  14.                try:
  15.                    for fp in Wa._GetMetaFiles(Wa.BuildTarget, Wa.ToolChain):
  16.                        used_files.add(fp)
  17.                except Exception:
  18.                    pass
  19.                for autogen in Wa.AutoGenObjectList:
  20.                    # 遍历模块与库(与新版 EDK2 的思路一致)
  21.                    for module in (autogen.LibraryAutoGenList + autogen.ModuleAutoGenList):
  22.                        used_files.add(module.MetaFile.Path)
  23.                        # —— 可选:模块摘要(用于 module_report.json)——
  24.                        md = {
  25.                            "Name": module.Name,
  26.                            "Arch": module.Arch,
  27.                            "Path": module.MetaFile.Path,
  28.                            "Guid": getattr(module, "Guid", ""),
  29.                            "BuildType": getattr(module, "BuildType", ""),
  30.                            "IsLibrary": getattr(module, "IsLibrary", False),
  31.                            "SourceDir": getattr(module, "SourceDir", ""),
  32.                            "Files": [],
  33.                            "Libraries": [],
  34.                            "Packages": [],
  35.                            "PPI": [],
  36.                            "Protocol": [],
  37.                            "Pcd": []
  38.                        }
  39.                        for sf in module.SourceFileList:
  40.                            md["Files"].append({"Name": sf.Name, "Path": sf.Path})
  41.                        for libag in getattr(module, "LibraryAutoGenList", []):
  42.                            md["Libraries"].append({"Path": libag.MetaFile.Path})
  43.                        for pkg in getattr(module, "PackageList", []):
  44.                            entry = {"Path": pkg.MetaFile.Path, "Includes": []}
  45.                            for inc in getattr(pkg, "Includes", []):
  46.                                entry["Includes"].append(inc.Path)
  47.                            md["Packages"].append(entry)
  48.                        for k in getattr(module, "PpiList", {}).keys():
  49.                            md["PPI"].append({"Name": k, "Guid": module.PpiList[k]})
  50.                        for k in getattr(module, "ProtocolList", {}).keys():
  51.                            md["Protocol"].append({"Name": k, "Guid": module.ProtocolList[k]})
  52.                        for pcd in getattr(module, "LibraryPcdList", []):
  53.                            md["Pcd"].append({
  54.                                "Space": getattr(pcd, "TokenSpaceGuidCName", ""),
  55.                                "Name": getattr(pcd, "TokenCName", ""),
  56.                                "Value": getattr(pcd, "TokenValue", ""),
  57.                                "Guid": getattr(pcd, "TokenSpaceGuidValue", ""),
  58.                                "DatumType": getattr(pcd, "DatumType", ""),
  59.                                "Type": getattr(pcd, "Type", ""),
  60.                                "DefaultValue": getattr(pcd, "DefaultValue", "")
  61.                            })
  62.                        module_report.append(md)
  63.                        # 生成 compile_commands 项(仅 C/C++ 源)
  64.                        inc_flag = "/I" if module.BuildRuleFamily == TAB_COMPILER_MSFT else "-I"
  65.                        for src in module.SourceFileList:
  66.                            used_files.add(src.Path)
  67.                            if src.Ext not in [".c", ".cc", ".cpp", ".cxx"]:
  68.                                continue
  69.                            # 基于 BuildRules 获取单条编译命令模板
  70.                            try:
  71.                                rule_cmd = module.BuildRules[src.Ext].CommandList[0]
  72.                            except Exception:
  73.                                # 回退:无法解析就跳过该文件
  74.                                continue
  75.                            # 展开 $(VAR) 变量(与新版实现思路一致,尽量保守)
  76.                            def _expand_var(m):
  77.                                token = m.group(1)
  78.                                parts = token.split("_")
  79.                                try:
  80.                                    if len(parts) == 1:
  81.                                        return module.BuildOption[parts[0]]["PATH"]
  82.                                    else:
  83.                                        return module.BuildOption[parts[0]][parts[1]]
  84.                                except Exception:
  85.                                    return ""
  86.                            build_cmd = re.sub(r"\$\((.*?)\)", _expand_var, rule_cmd)
  87.                            # 处理常见占位:${src}(包含路径列表),${dst}(输出目录)
  88.                            try:
  89.                                incs = getattr(module, "IncludePathList", [])
  90.                                # 构造 “/Ipath1 /Ipath2 …”
  91.                                inc_blob = " ".join([(inc_flag + """ + inc + """) if " " in inc else (inc_flag + inc) for inc in incs])
  92.                                 build_cmd = build_cmd.replace("${src}", inc_blob)
  93.                                 build_cmd = build_cmd.replace("${dst}", getattr(module, "OutputDir", ""))
  94.                             except Exception:
  95.                                 pass
  96.                             # 清理未展开残留形如 $(XXX) 的片段
  97.                             build_cmd = re.sub(r"\$\((?:.*?)\)", "", build_cmd).strip()
  98.                             # compilation database 条目
  99.                             entry = {
  100.                                 "file": src.Path,                 # 保持与 EDK2 新版一致:绝对路径
  101.                                 "directory": src.Dir,             # 编译时工作目录
  102.                                 "command": build_cmd              # MSVC 风格 cl.exe 命令
  103.                             }
  104.                             compile_commands.append(entry)
  105.             # 输出目录:Build/.../CompileInfo
  106.             compile_info_dir = os.path.join(Wa.BuildDir, "CompileInfo")
  107.             if not os.path.isdir(compile_info_dir):
  108.                 try:
  109.                     os.makedirs(compile_info_dir)
  110.                 except Exception:
  111.                     pass
  112.             # 排序并写出
  113.             compile_commands.sort(key=lambda x: x["file"])
  114.             SaveFileOnChange(os.path.join(compile_info_dir, "compile_commands.json"),
  115.                              json.dumps(compile_commands, indent=2), False)
  116.             SaveFileOnChange(os.path.join(compile_info_dir, "cscope.files"),
  117.                              "\n".join(sorted(used_files)), False)
  118.             module_report.sort(key=lambda x: x["Path"])
  119.             SaveFileOnChange(os.path.join(compile_info_dir, "module_report.json"),
  120.                              json.dumps(module_report, indent=2), False)
  121.         except Exception:
  122.             from Common import EdkLogger
  123.             import traceback, platform, sys
  124.             EdkLogger.error("BuildReport", 0, "Unknown fatal error when generating compile information",
  125.                             ExtraData=getattr(self, "ReportFile", None), RaiseError=False)
  126.             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 块开头,加上:
1.png

4)在下图所示加入一行:
2.png

改动二:build.py(给 -Y 添加 COMPILE_INFO 选项)

文件:BaseTools/Source/Python/build/build.py
找到命令行解析对 -Y/--report-type 的定义,把允许值里加入 COMPILE_INFO。
  1.     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=[],
  2.         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
在最后一行加上
  1. TAB_COMPILER_MSFT = 'MSFT'
复制代码
使用与验证

使用python编译


  • edksetup.bat执行后,build -h查看-Y  选项下是否有COMPILE_INFO如果有,说明build使用的是python脚本。
  • 正常编译,但增加(注意要使用Python2.7编译UDK2018):
    build -p  -a IA32 -t VS2015x86 -b DEBUG -Y COMPILE_INFO -y BuildReport.log ^a359b1
  • 在你的 build 产物目录(例如 Build/NT32IA32/DEBUG_VS2015x86/CompileInfo/)应出现:


  • compile_commands.json
  • cscope.files
  • module_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整个文件夹。
点个赞再走!!!!!


来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册