BaseTools: Generate compile information in build report

REF:https://bugzilla.tianocore.org/show_bug.cgi?id=2850

Add "-Y REPORT_INFO" option to build command to generate compile
information as part of BuildReport.
This option generates files to be used by external tools as IDE's
to enhance functionality.
Files are created inside build folder:
<Build>/<BuildTarget>/<ToolChain>/CompileInfo

Files created:
* compile_commands.json - Compilation Database. To be used by IDE's
  to enable advance features
* cscope.files - List of files used in compilation. Used by Cscope to parse
  C code and provide browse functionality.
* module_report.json - Module data form buildReport in Json format.

Signed-off-by: Guillermo Antonio Palomino Sosa <guillermo.a.palomino.sosa@intel.com>
Reviewed-by: Bob Feng <bob.c.feng@intel.com>
This commit is contained in:
Palomino Sosa, Guillermo A 2023-02-07 11:07:23 +08:00 committed by mergify[bot]
parent 9b94ebb0c8
commit 4ad7ea9c84
2 changed files with 140 additions and 3 deletions

View File

@ -10,6 +10,8 @@
## Import Modules
#
import json
from pathlib import Path
import Common.LongFilePathOs as os
import re
import platform
@ -41,6 +43,7 @@ from Common.DataType import *
import collections
from Common.Expression import *
from GenFds.AprioriSection import DXE_APRIORI_GUID, PEI_APRIORI_GUID
from AutoGen.IncludesAutoGen import IncludesAutoGen
## Pattern to extract contents in EDK DXS files
gDxsDependencyPattern = re.compile(r"DEPENDENCY_START(.+)DEPENDENCY_END", re.DOTALL)
@ -2298,6 +2301,10 @@ class BuildReport(object):
def GenerateReport(self, BuildDuration, AutoGenTime, MakeTime, GenFdsTime):
if self.ReportFile:
try:
if "COMPILE_INFO" in self.ReportType:
self.GenerateCompileInfo()
File = []
for (Wa, MaList) in self.ReportList:
PlatformReport(Wa, MaList, self.ReportType).GenerateReport(File, BuildDuration, AutoGenTime, MakeTime, GenFdsTime, self.ReportType)
@ -2310,7 +2317,137 @@ class BuildReport(object):
EdkLogger.error("BuildReport", CODE_ERROR, "Unknown fatal error when generating build report", ExtraData=self.ReportFile, RaiseError=False)
EdkLogger.quiet("(Python %s on %s\n%s)" % (platform.python_version(), sys.platform, traceback.format_exc()))
##
# Generates compile data files to be used by external tools.
# Compile information will be generated in <Build>/<BuildTarget>/<ToolChain>/CompileInfo
# Files generated: compile_commands.json, cscope.files, modules_report.json
#
# @param self The object pointer
#
def GenerateCompileInfo(self):
try:
# Lists for the output elements
compile_commands = []
used_files = set()
module_report = []
for (Wa, MaList) in self.ReportList:
# Obtain list of all processed Workspace files
for file_path in Wa._GetMetaFiles(Wa.BuildTarget, Wa.ToolChain):
used_files.add(file_path)
for autoGen in Wa.AutoGenObjectList:
# Loop through all modules
for module in (autoGen.LibraryAutoGenList + autoGen.ModuleAutoGenList):
used_files.add(module.MetaFile.Path)
# Main elements of module report
module_report_data = {}
module_report_data["Name"] = module.Name
module_report_data["Arch"] = module.Arch
module_report_data["Path"] = module.MetaFile.Path
module_report_data["Guid"] = module.Guid
module_report_data["BuildType"] = module.BuildType
module_report_data["IsLibrary"] = module.IsLibrary
module_report_data["SourceDir"] = module.SourceDir
module_report_data["Files"] = []
# Files used by module
for data_file in module.SourceFileList:
module_report_data["Files"].append({"Name": data_file.Name, "Path": data_file.Path})
# Libraries used by module
module_report_data["Libraries"] = []
for data_library in module.LibraryAutoGenList:
module_report_data["Libraries"].append({"Path": data_library.MetaFile.Path})
# Packages used by module
module_report_data["Packages"] = []
for data_package in module.PackageList:
module_report_data["Packages"].append({"Path": data_package.MetaFile.Path, "Includes": []})
# Includes path used in package
for data_package_include in data_package.Includes:
module_report_data["Packages"][-1]["Includes"].append(data_package_include.Path)
# PPI's in module
module_report_data["PPI"] = []
for data_ppi in module.PpiList.keys():
module_report_data["PPI"].append({"Name": data_ppi, "Guid": module.PpiList[data_ppi]})
# Protocol's in module
module_report_data["Protocol"] = []
for data_protocol in module.ProtocolList.keys():
module_report_data["Protocol"].append({"Name": data_protocol, "Guid": module.ProtocolList[data_protocol]})
# PCD's in module
module_report_data["Pcd"] = []
for data_pcd in module.LibraryPcdList:
module_report_data["Pcd"].append({"Space": data_pcd.TokenSpaceGuidCName,
"Name": data_pcd.TokenCName,
"Value": data_pcd.TokenValue,
"Guid": data_pcd.TokenSpaceGuidValue,
"DatumType": data_pcd.DatumType,
"Type": data_pcd.Type,
"DefaultValue": data_pcd.DefaultValue})
# Add module to report
module_report.append(module_report_data)
# Include file dependencies to used files
includes_autogen = IncludesAutoGen(module.MakeFileDir, module)
for dep in includes_autogen.DepsCollection:
used_files.add(dep)
inc_flag = "-I" # Default include flag
if module.BuildRuleFamily == TAB_COMPILER_MSFT:
inc_flag = "/I"
for source in module.SourceFileList:
used_files.add(source.Path)
compile_command = {}
if source.Ext in [".c", ".cc", ".cpp"]:
#
# Generate compile command for each c file
#
compile_command["file"] = source.Path
compile_command["directory"] = source.Dir
build_command = module.BuildRules[source.Ext].CommandList[0]
build_command_variables = re.findall(r"\$\((.*?)\)", build_command)
for var in build_command_variables:
var_tokens = var.split("_")
var_main = var_tokens[0]
if len(var_tokens) == 1:
var_value = module.BuildOption[var_main]["PATH"]
else:
var_value = module.BuildOption[var_main][var_tokens[1]]
build_command = build_command.replace(f"$({var})", var_value)
include_files = f" {inc_flag}".join(module.IncludePathList)
build_command = build_command.replace("${src}", include_files)
build_command = build_command.replace("${dst}", module.OutputDir)
# Remove un defined macros
compile_command["command"] = re.sub(r"\$\(.*?\)", "", build_command)
compile_commands.append(compile_command)
# Create output folder if doesn't exist
compile_info_folder = Path(Wa.BuildDir).joinpath("CompileInfo")
compile_info_folder.mkdir(exist_ok=True)
# Sort and save files
compile_commands.sort(key=lambda x: x["file"])
SaveFileOnChange(compile_info_folder.joinpath(f"compile_commands.json"),json.dumps(compile_commands, indent=2), False)
SaveFileOnChange(compile_info_folder.joinpath(f"cscope.files"), "\n".join(sorted(used_files)), False)
module_report.sort(key=lambda x: x["Path"])
SaveFileOnChange(compile_info_folder.joinpath(f"module_report.json"), json.dumps(module_report, indent=2), False)
except:
EdkLogger.error("BuildReport", CODE_ERROR, "Unknown fatal error when generating build report compile information", ExtraData=self.ReportFile, RaiseError=False)
EdkLogger.quiet("(Python %s on %s\n%s)" % (platform.python_version(), sys.platform, traceback.format_exc()))
# This acts like the main() function for the script, unless it is 'import'ed into another script.
if __name__ == '__main__':
pass

View File

@ -84,8 +84,8 @@ class MyOptionParser():
Parser.add_option("-D", "--define", action="append", type="string", dest="Macros", help="Macro: \"Name [= Value]\".")
Parser.add_option("-y", "--report-file", action="store", dest="ReportFile", help="Create/overwrite the report to the specified filename.")
Parser.add_option("-Y", "--report-type", action="append", type="choice", choices=['PCD', 'LIBRARY', 'FLASH', 'DEPEX', 'BUILD_FLAGS', 'FIXED_ADDRESS', 'HASH', 'EXECUTION_ORDER'], 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]. "\
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]. "\
"To specify more than one flag, repeat this option on the command line and the default flag set is [PCD, LIBRARY, FLASH, DEPEX, HASH, BUILD_FLAGS, FIXED_ADDRESS]")
Parser.add_option("-F", "--flag", action="store", type="string", dest="Flag",
help="Specify the specific option to parse EDK UNI file. Must be one of: [-c, -s]. -c is for EDK framework UNI file, and -s is for EDK UEFI UNI file. "\