diff --git a/BaseTools/Source/Python/build/BuildReport.py b/BaseTools/Source/Python/build/BuildReport.py index 468772930c..33b43d471f 100644 --- a/BaseTools/Source/Python/build/BuildReport.py +++ b/BaseTools/Source/Python/build/BuildReport.py @@ -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 ///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 - diff --git a/BaseTools/Source/Python/build/buildoptions.py b/BaseTools/Source/Python/build/buildoptions.py index 8334604b46..5ec561f7ec 100644 --- a/BaseTools/Source/Python/build/buildoptions.py +++ b/BaseTools/Source/Python/build/buildoptions.py @@ -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. "\