X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=BaseTools%2FSource%2FPython%2Fbuild%2FBuildReport.py;h=327d5a53479cf0369316ff3d46042c2cb75759d9;hb=1be2ed90a20618d71ddf34b8a07d038da0b36854;hp=329352204dc4267a2965ccd116095de99fd797ef;hpb=79714906ae765f161969dfddc34adee857be97d6;p=mirror_edk2.git diff --git a/BaseTools/Source/Python/build/BuildReport.py b/BaseTools/Source/Python/build/BuildReport.py index 329352204d..327d5a5347 100644 --- a/BaseTools/Source/Python/build/BuildReport.py +++ b/BaseTools/Source/Python/build/BuildReport.py @@ -4,8 +4,8 @@ # This module contains the functionality to generate build report after # build all target completes successfully. # -# Copyright (c) 2010, Intel Corporation -# All rights reserved. This program and the accompanying materials +# Copyright (c) 2010 - 2014, Intel Corporation. All rights reserved.
+# This program and the accompanying materials # are licensed and made available under the terms and conditions of the BSD License # which accompanies this distribution. The full text of the license may be found at # http://opensource.org/licenses/bsd-license.php @@ -16,21 +16,31 @@ ## Import Modules # -import os +import Common.LongFilePathOs as os import re import platform import textwrap import traceback import sys +import time +import struct from datetime import datetime +from StringIO import StringIO from Common import EdkLogger +from Common.Misc import SaveFileOnChange from Common.Misc import GuidStructureByteArrayToGuidString from Common.Misc import GuidStructureStringToGuidString from Common.InfClassObject import gComponentType2ModuleType -from Common.BuildToolError import FILE_OPEN_FAILURE from Common.BuildToolError import FILE_WRITE_FAILURE from Common.BuildToolError import CODE_ERROR - +from Common.DataType import TAB_LINE_BREAK +from Common.DataType import TAB_DEPEX +from Common.DataType import TAB_SLASH +from Common.DataType import TAB_SPACE_SPLIT +from Common.DataType import TAB_BRG_PCD +from Common.DataType import TAB_BRG_LIBRARY +from Common.DataType import TAB_BACK_SLASH +from Common.LongFilePathSupport import OpenLongFilePath as open ## Pattern to extract contents in EDK DXS files gDxsDependencyPattern = re.compile(r"DEPENDENCY_START(.+)DEPENDENCY_END", re.DOTALL) @@ -60,15 +70,22 @@ gIncludePattern2 = re.compile(r"#include\s+EFI_([A-Z_]+)\s*[(]\s*(\w+)\s*[)]") ## Pattern to find the entry point for EDK module using EDKII Glue library gGlueLibEntryPoint = re.compile(r"__EDKII_GLUE_MODULE_ENTRY_POINT__\s*=\s*(\w+)") +## Tags for MaxLength of line in report +gLineMaxLength = 120 + +## Tags for end of line in report +gEndOfLine = "\r\n" + ## Tags for section start, end and separator -gSectionStart = ">" + "=" * 118 + "<" -gSectionEnd = "<" + "=" * 118 + ">" + "\n" -gSectionSep = "=" * 120 +gSectionStart = ">" + "=" * (gLineMaxLength-2) + "<" +gSectionEnd = "<" + "=" * (gLineMaxLength-2) + ">" + "\n" +gSectionSep = "=" * gLineMaxLength ## Tags for subsection start, end and separator -gSubSectionStart = ">" + "-" * 118 + "<" -gSubSectionEnd = "<" + "-" * 118 + ">" -gSubSectionSep = "-" * 120 +gSubSectionStart = ">" + "-" * (gLineMaxLength-2) + "<" +gSubSectionEnd = "<" + "-" * (gLineMaxLength-2) + ">" +gSubSectionSep = "-" * gLineMaxLength + ## The look up table to map PCD type to pair of report display type and DEC type gPcdTypeMap = { @@ -78,9 +95,9 @@ gPcdTypeMap = { 'Dynamic' : ('DYN', 'Dynamic'), 'DynamicHii' : ('DYNHII', 'Dynamic'), 'DynamicVpd' : ('DYNVPD', 'Dynamic'), - 'DynamicEx' : ('DEX', 'Dynamic'), - 'DynamicExHii' : ('DEXHII', 'Dynamic'), - 'DynamicExVpd' : ('DEXVPD', 'Dynamic'), + 'DynamicEx' : ('DEX', 'DynamicEx'), + 'DynamicExHii' : ('DEXHII', 'DynamicEx'), + 'DynamicExVpd' : ('DEXVPD', 'DynamicEx'), } ## The look up table to map module type to driver type @@ -99,6 +116,9 @@ gDriverTypeMap = { 'SMM_DRIVER' : '0xA (SMM)', # Extension of module type to support PI 1.1 SMM drivers } +## The look up table of the supported opcode in the dependency expression binaries +gOpCodeList = ["BEFORE", "AFTER", "PUSH", "AND", "OR", "NOT", "TRUE", "FALSE", "END", "SOR"] + ## # Writes a string to the file object. # @@ -112,7 +132,7 @@ gDriverTypeMap = { def FileWrite(File, String, Wrapper=False): if Wrapper: String = textwrap.fill(String, 120) - File.write(String + "\r\n") + File.write(String + gEndOfLine) ## # Find all the header file that the module source directly includes. @@ -160,6 +180,94 @@ def FindIncludeFiles(Source, IncludePathList, IncludeFiles): IncludeFiles[FullFileName.lower().replace("\\", "/")] = FullFileName break +## Split each lines in file +# +# This method is used to split the lines in file to make the length of each line +# less than MaxLength. +# +# @param Content The content of file +# @param MaxLength The Max Length of the line +# +def FileLinesSplit(Content=None, MaxLength=None): + ContentList = Content.split(TAB_LINE_BREAK) + NewContent = '' + NewContentList = [] + for Line in ContentList: + while len(Line.rstrip()) > MaxLength: + LineSpaceIndex = Line.rfind(TAB_SPACE_SPLIT, 0, MaxLength) + LineSlashIndex = Line.rfind(TAB_SLASH, 0, MaxLength) + LineBackSlashIndex = Line.rfind(TAB_BACK_SLASH, 0, MaxLength) + if max(LineSpaceIndex, LineSlashIndex, LineBackSlashIndex) > 0: + LineBreakIndex = max(LineSpaceIndex, LineSlashIndex, LineBackSlashIndex) + else: + LineBreakIndex = MaxLength + NewContentList.append(Line[:LineBreakIndex]) + Line = Line[LineBreakIndex:] + if Line: + NewContentList.append(Line) + for NewLine in NewContentList: + NewContent += NewLine + TAB_LINE_BREAK + + NewContent = NewContent.replace(TAB_LINE_BREAK, gEndOfLine).replace('\r\r\n', gEndOfLine) + return NewContent + + + +## +# Parse binary dependency expression section +# +# This utility class parses the dependency expression section and translate the readable +# GUID name and value. +# +class DepexParser(object): + ## + # Constructor function for class DepexParser + # + # This constructor function collect GUID values so that the readable + # GUID name can be translated. + # + # @param self The object pointer + # @param Wa Workspace context information + # + def __init__(self, Wa): + self._GuidDb = {} + for Pa in Wa.AutoGenObjectList: + for Package in Pa.PackageList: + for Protocol in Package.Protocols: + GuidValue = GuidStructureStringToGuidString(Package.Protocols[Protocol]) + self._GuidDb[GuidValue.upper()] = Protocol + for Ppi in Package.Ppis: + GuidValue = GuidStructureStringToGuidString(Package.Ppis[Ppi]) + self._GuidDb[GuidValue.upper()] = Ppi + for Guid in Package.Guids: + GuidValue = GuidStructureStringToGuidString(Package.Guids[Guid]) + self._GuidDb[GuidValue.upper()] = Guid + + ## + # Parse the binary dependency expression files. + # + # This function parses the binary dependency expression file and translate it + # to the instruction list. + # + # @param self The object pointer + # @param DepexFileName The file name of binary dependency expression file. + # + def ParseDepexFile(self, DepexFileName): + DepexFile = open(DepexFileName, "rb") + DepexStatement = [] + OpCode = DepexFile.read(1) + while OpCode: + Statement = gOpCodeList[struct.unpack("B", OpCode)[0]] + if Statement in ["BEFORE", "AFTER", "PUSH"]: + GuidValue = "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X" % \ + struct.unpack("=LHHBBBBBBBB", DepexFile.read(16)) + GuidString = self._GuidDb.get(GuidValue, GuidValue) + Statement = "%s %s" % (Statement, GuidString) + DepexStatement.append(Statement) + OpCode = DepexFile.read(1) + + return DepexStatement + ## # Reports library information # @@ -202,7 +310,7 @@ class LibraryReport(object): # def GenerateReport(self, File): FileWrite(File, gSubSectionStart) - FileWrite(File, "Library") + FileWrite(File, TAB_BRG_LIBRARY) if len(self.LibraryList) > 0: FileWrite(File, gSubSectionSep) for LibraryItem in self.LibraryList: @@ -252,6 +360,7 @@ class DepexReport(object): # def __init__(self, M): self.Depex = "" + self._DepexFileName = os.path.join(M.BuildDir, "OUTPUT", M.Module.BaseName + ".depex") ModuleType = M.ModuleType if not ModuleType: ModuleType = gComponentType2ModuleType.get(M.ComponentType, "") @@ -287,14 +396,27 @@ class DepexReport(object): # # This function generates report for the module dependency expression. # - # @param self The object pointer - # @param File The file object for report + # @param self The object pointer + # @param File The file object for report + # @param GlobalDepexParser The platform global Dependency expression parser object # - def GenerateReport(self, File): + def GenerateReport(self, File, GlobalDepexParser): if not self.Depex: + FileWrite(File, gSubSectionStart) + FileWrite(File, TAB_DEPEX) + FileWrite(File, gSubSectionEnd) return - FileWrite(File, gSubSectionStart) + if os.path.isfile(self._DepexFileName): + try: + DepexStatements = GlobalDepexParser.ParseDepexFile(self._DepexFileName) + FileWrite(File, "Final Dependency Expression (DEPEX) Instructions") + for DepexStatement in DepexStatements: + FileWrite(File, " %s" % DepexStatement) + FileWrite(File, gSubSectionSep) + except: + EdkLogger.warn(None, "Dependency expression file is corrupted", self._DepexFileName) + FileWrite(File, "Dependency Expression (DEPEX) from %s" % self.Source) if self.Source == "INF": @@ -451,12 +573,14 @@ class ModuleReport(object): # This function generates report for separate module expression # in a platform build. # - # @param self The object pointer - # @param File The file object for report - # @param GlobalPcdReport The platform global PCD class object - # @param ReportType The kind of report items in the final report file + # @param self The object pointer + # @param File The file object for report + # @param GlobalPcdReport The platform global PCD report object + # @param GlobalPredictionReport The platform global Prediction report object + # @param GlobalDepexParser The platform global Dependency expression parser object + # @param ReportType The kind of report items in the final report file # - def GenerateReport(self, File, GlobalPcdReport, GlobalPredictionReport, ReportType): + def GenerateReport(self, File, GlobalPcdReport, GlobalPredictionReport, GlobalDepexParser, ReportType): FileWrite(File, gSectionStart) FwReportFileName = os.path.join(self._BuildDir, "DEBUG", self.ModuleName + ".txt") @@ -503,7 +627,7 @@ class ModuleReport(object): self.LibraryReport.GenerateReport(File) if "DEPEX" in ReportType: - self.DepexReport.GenerateReport(File) + self.DepexReport.GenerateReport(File, GlobalDepexParser) if "BUILD_FLAGS" in ReportType: self.BuildFlagsReport.GenerateReport(File) @@ -567,18 +691,21 @@ class PcdReport(object): # Collect PCD DEC default value. # self.DecPcdDefault = {} - for Package in Wa.BuildDatabase.WorkspaceDb.PackageList: - for (TokenCName, TokenSpaceGuidCName, DecType) in Package.Pcds: - DecDefaultValue = Package.Pcds[TokenCName, TokenSpaceGuidCName, DecType].DefaultValue - self.DecPcdDefault.setdefault((TokenCName, TokenSpaceGuidCName, DecType), DecDefaultValue) + for Pa in Wa.AutoGenObjectList: + for Package in Pa.PackageList: + for (TokenCName, TokenSpaceGuidCName, DecType) in Package.Pcds: + DecDefaultValue = Package.Pcds[TokenCName, TokenSpaceGuidCName, DecType].DefaultValue + self.DecPcdDefault.setdefault((TokenCName, TokenSpaceGuidCName, DecType), DecDefaultValue) # # Collect PCDs defined in DSC common section # self.DscPcdDefault = {} - for Platform in Wa.BuildDatabase.WorkspaceDb.PlatformList: + for Arch in Wa.ArchList: + Platform = Wa.BuildDatabase[Wa.MetaFile, Arch, Wa.BuildTarget, Wa.ToolChain] for (TokenCName, TokenSpaceGuidCName) in Platform.Pcds: DscDefaultValue = Platform.Pcds[(TokenCName, TokenSpaceGuidCName)].DefaultValue - self.DscPcdDefault[(TokenCName, TokenSpaceGuidCName)] = DscDefaultValue + if DscDefaultValue: + self.DscPcdDefault[(TokenCName, TokenSpaceGuidCName)] = DscDefaultValue ## # Generate report for PCD information @@ -608,7 +735,7 @@ class PcdReport(object): # For module PCD sub-section # FileWrite(File, gSubSectionStart) - FileWrite(File, "PCD") + FileWrite(File, TAB_BRG_PCD) FileWrite(File, gSubSectionSep) for Key in self.AllPcds: @@ -765,6 +892,13 @@ class PredictionReport(object): for Pa in Wa.AutoGenObjectList: for Module in Pa.LibraryAutoGenList + Pa.ModuleAutoGenList: # + # BASE typed modules are EFI agnostic, so we need not scan + # their source code to find PPI/Protocol produce or consume + # information. + # + if Module.ModuleType == "BASE": + continue + # # Add module referenced source files # self._SourceList.append(str(Module)) @@ -889,12 +1023,17 @@ class PredictionReport(object): try: from Eot.Eot import Eot + # - # Invoke EOT tool + # Invoke EOT tool and echo its runtime performance # + EotStartTime = time.time() Eot(CommandLineOption=False, SourceFileList=SourceList, GuidList=GuidList, FvFileList=' '.join(FvFileList), Dispatch=DispatchList, IsInit=True) - + EotEndTime = time.time() + EotDuration = time.strftime("%H:%M:%S", time.gmtime(int(round(EotEndTime - EotStartTime)))) + EdkLogger.quiet("EOT run time: %s\n" % EotDuration) + # # Parse the output of EOT tool # @@ -1087,14 +1226,14 @@ class FdRegionReport(object): self._DiscoverNestedFvList(FvName, Wa) PlatformPcds = {} - # # Collect PCDs declared in DEC files. - # - for Package in Wa.BuildDatabase.WorkspaceDb.PackageList: - for (TokenCName, TokenSpaceGuidCName, DecType) in Package.Pcds: - DecDefaultValue = Package.Pcds[TokenCName, TokenSpaceGuidCName, DecType].DefaultValue - PlatformPcds[(TokenCName, TokenSpaceGuidCName)] = DecDefaultValue + # + for Pa in Wa.AutoGenObjectList: + for Package in Pa.PackageList: + for (TokenCName, TokenSpaceGuidCName, DecType) in Package.Pcds: + DecDefaultValue = Package.Pcds[TokenCName, TokenSpaceGuidCName, DecType].DefaultValue + PlatformPcds[(TokenCName, TokenSpaceGuidCName)] = DecDefaultValue # # Collect PCDs defined in DSC common section # @@ -1169,7 +1308,7 @@ class FdRegionReport(object): FvTotalSize = 0 FvTakenSize = 0 FvFreeSize = 0 - FvReportFileName = os.path.join(self._FvDir, FvName + ".fv.txt") + FvReportFileName = os.path.join(self._FvDir, FvName + ".Fv.txt") try: # # Collect size info in the firmware volume. @@ -1310,6 +1449,10 @@ class PlatformReport(object): if "FIXED_ADDRESS" in ReportType or "EXECUTION_ORDER" in ReportType: self.PredictionReport = PredictionReport(Wa) + self.DepexParser = None + if "DEPEX" in ReportType: + self.DepexParser = DepexParser(Wa) + self.ModuleReportList = [] if MaList != None: self._IsModuleBuild = True @@ -1356,7 +1499,7 @@ class PlatformReport(object): FdReportListItem.GenerateReport(File) for ModuleReportItem in self.ModuleReportList: - ModuleReportItem.GenerateReport(File, self.PcdReport, self.PredictionReport, ReportType) + ModuleReportItem.GenerateReport(File, self.PcdReport, self.PredictionReport, self.DepexParser, ReportType) if not self._IsModuleBuild: if "EXECUTION_ORDER" in ReportType: @@ -1415,13 +1558,12 @@ class BuildReport(object): def GenerateReport(self, BuildDuration): if self.ReportFile: try: - File = open(self.ReportFile, "w+") - except IOError: - EdkLogger.error(None, FILE_OPEN_FAILURE, ExtraData=self.ReportFile) - try: + File = StringIO('') for (Wa, MaList) in self.ReportList: PlatformReport(Wa, MaList, self.ReportType).GenerateReport(File, BuildDuration, self.ReportType) - EdkLogger.quiet("Report successfully saved to %s" % os.path.abspath(self.ReportFile)) + Content = FileLinesSplit(File.getvalue(), gLineMaxLength) + SaveFileOnChange(self.ReportFile, Content, True) + EdkLogger.quiet("Build report can be found at %s" % os.path.abspath(self.ReportFile)) except IOError: EdkLogger.error(None, FILE_WRITE_FAILURE, ExtraData=self.ReportFile) except: