Home | History | Annotate | Download | only in build
      1 ## @file

      2 # Routines for generating build report.

      3 #

      4 # This module contains the functionality to generate build report after

      5 # build all target completes successfully.

      6 #

      7 # Copyright (c) 2010 - 2017, Intel Corporation. All rights reserved.<BR>

      8 # This program and the accompanying materials

      9 # are licensed and made available under the terms and conditions of the BSD License

     10 # which accompanies this distribution.  The full text of the license may be found at

     11 # http://opensource.org/licenses/bsd-license.php

     12 #

     13 # THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

     14 # WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

     15 #

     16 
     17 ## Import Modules

     18 #

     19 import Common.LongFilePathOs as os
     20 import re
     21 import platform
     22 import textwrap
     23 import traceback
     24 import sys
     25 import time
     26 import struct
     27 import hashlib
     28 import subprocess
     29 import threading
     30 from datetime import datetime
     31 from StringIO import StringIO
     32 from Common import EdkLogger
     33 from Common.Misc import SaveFileOnChange
     34 from Common.Misc import GuidStructureByteArrayToGuidString
     35 from Common.Misc import GuidStructureStringToGuidString
     36 from Common.InfClassObject import gComponentType2ModuleType
     37 from Common.BuildToolError import FILE_WRITE_FAILURE
     38 from Common.BuildToolError import CODE_ERROR
     39 from Common.BuildToolError import COMMAND_FAILURE
     40 from Common.DataType import TAB_LINE_BREAK
     41 from Common.DataType import TAB_DEPEX
     42 from Common.DataType import TAB_SLASH
     43 from Common.DataType import TAB_SPACE_SPLIT
     44 from Common.DataType import TAB_BRG_PCD
     45 from Common.DataType import TAB_BRG_LIBRARY
     46 from Common.DataType import TAB_BACK_SLASH
     47 from Common.LongFilePathSupport import OpenLongFilePath as open
     48 from Common.MultipleWorkspace import MultipleWorkspace as mws
     49 import Common.GlobalData as GlobalData
     50 from AutoGen.AutoGen import ModuleAutoGen
     51 from Common.Misc import PathClass
     52 from Common.String import NormPath
     53 
     54 ## Pattern to extract contents in EDK DXS files

     55 gDxsDependencyPattern = re.compile(r"DEPENDENCY_START(.+)DEPENDENCY_END", re.DOTALL)
     56 
     57 ## Pattern to find total FV total size, occupied size in flash report intermediate file

     58 gFvTotalSizePattern = re.compile(r"EFI_FV_TOTAL_SIZE = (0x[0-9a-fA-F]+)")
     59 gFvTakenSizePattern = re.compile(r"EFI_FV_TAKEN_SIZE = (0x[0-9a-fA-F]+)")
     60 
     61 ## Pattern to find module size and time stamp in module summary report intermediate file

     62 gModuleSizePattern = re.compile(r"MODULE_SIZE = (\d+)")
     63 gTimeStampPattern  = re.compile(r"TIME_STAMP = (\d+)")
     64 
     65 ## Pattern to find GUID value in flash description files

     66 gPcdGuidPattern = re.compile(r"PCD\((\w+)[.](\w+)\)")
     67 
     68 ## Pattern to collect offset, GUID value pair in the flash report intermediate file

     69 gOffsetGuidPattern = re.compile(r"(0x[0-9A-Fa-f]+) ([-A-Fa-f0-9]+)")
     70 
     71 ## Pattern to find module base address and entry point in fixed flash map file

     72 gModulePattern = r"\n[-\w]+\s*\(([^,]+),\s*BaseAddress=%(Address)s,\s*EntryPoint=%(Address)s\)\s*\(GUID=([-0-9A-Fa-f]+)[^)]*\)"
     73 gMapFileItemPattern = re.compile(gModulePattern % {"Address" : "(-?0[xX][0-9A-Fa-f]+)"})
     74 
     75 ## Pattern to find all module referenced header files in source files

     76 gIncludePattern  = re.compile(r'#include\s*["<]([^">]+)[">]')
     77 gIncludePattern2 = re.compile(r"#include\s+EFI_([A-Z_]+)\s*[(]\s*(\w+)\s*[)]")
     78 
     79 ## Pattern to find the entry point for EDK module using EDKII Glue library

     80 gGlueLibEntryPoint = re.compile(r"__EDKII_GLUE_MODULE_ENTRY_POINT__\s*=\s*(\w+)")
     81 
     82 ## Tags for MaxLength of line in report

     83 gLineMaxLength = 120
     84 
     85 ## Tags for end of line in report

     86 gEndOfLine = "\r\n"
     87 
     88 ## Tags for section start, end and separator

     89 gSectionStart = ">" + "=" * (gLineMaxLength - 2) + "<"
     90 gSectionEnd = "<" + "=" * (gLineMaxLength - 2) + ">" + "\n"
     91 gSectionSep = "=" * gLineMaxLength
     92 
     93 ## Tags for subsection start, end and separator

     94 gSubSectionStart = ">" + "-" * (gLineMaxLength - 2) + "<"
     95 gSubSectionEnd = "<" + "-" * (gLineMaxLength - 2) + ">"
     96 gSubSectionSep = "-" * gLineMaxLength
     97 
     98 
     99 ## The look up table to map PCD type to pair of report display type and DEC type

    100 gPcdTypeMap = {
    101   'FixedAtBuild'     : ('FIXED',  'FixedAtBuild'),
    102   'PatchableInModule': ('PATCH',  'PatchableInModule'),
    103   'FeatureFlag'      : ('FLAG',   'FeatureFlag'),
    104   'Dynamic'          : ('DYN',    'Dynamic'),
    105   'DynamicHii'       : ('DYNHII', 'Dynamic'),
    106   'DynamicVpd'       : ('DYNVPD', 'Dynamic'),
    107   'DynamicEx'        : ('DEX',    'DynamicEx'),
    108   'DynamicExHii'     : ('DEXHII', 'DynamicEx'),
    109   'DynamicExVpd'     : ('DEXVPD', 'DynamicEx'),
    110   }
    111 
    112 ## The look up table to map module type to driver type

    113 gDriverTypeMap = {
    114   'SEC'               : '0x3 (SECURITY_CORE)',
    115   'PEI_CORE'          : '0x4 (PEI_CORE)',
    116   'PEIM'              : '0x6 (PEIM)',
    117   'DXE_CORE'          : '0x5 (DXE_CORE)',
    118   'DXE_DRIVER'        : '0x7 (DRIVER)',
    119   'DXE_SAL_DRIVER'    : '0x7 (DRIVER)',
    120   'DXE_SMM_DRIVER'    : '0x7 (DRIVER)',
    121   'DXE_RUNTIME_DRIVER': '0x7 (DRIVER)',
    122   'UEFI_DRIVER'       : '0x7 (DRIVER)',
    123   'UEFI_APPLICATION'  : '0x9 (APPLICATION)',
    124   'SMM_CORE'          : '0xD (SMM_CORE)',
    125   'SMM_DRIVER'        : '0xA (SMM)', # Extension of module type to support PI 1.1 SMM drivers

    126   }
    127 
    128 ## The look up table of the supported opcode in the dependency expression binaries

    129 gOpCodeList = ["BEFORE", "AFTER", "PUSH", "AND", "OR", "NOT", "TRUE", "FALSE", "END", "SOR"]
    130 
    131 ##

    132 # Writes a string to the file object.

    133 #

    134 # This function writes a string to the file object and a new line is appended

    135 # afterwards. It may optionally wraps the string for better readability.

    136 #

    137 # @File                      The file object to write

    138 # @String                    The string to be written to the file

    139 # @Wrapper                   Indicates whether to wrap the string

    140 #

    141 def FileWrite(File, String, Wrapper=False):
    142     if Wrapper:
    143         String = textwrap.fill(String, 120)
    144     File.write(String + gEndOfLine)
    145 
    146 ##

    147 # Find all the header file that the module source directly includes.

    148 #

    149 # This function scans source code to find all header files the module may

    150 # include. This is not accurate but very effective to find all the header

    151 # file the module might include with #include statement.

    152 #

    153 # @Source                    The source file name

    154 # @IncludePathList           The list of include path to find the source file.

    155 # @IncludeFiles              The dictionary of current found include files.

    156 #

    157 def FindIncludeFiles(Source, IncludePathList, IncludeFiles):
    158     FileContents = open(Source).read()
    159     #

    160     # Find header files with pattern #include "XXX.h" or #include <XXX.h>

    161     #

    162     for Match in gIncludePattern.finditer(FileContents):
    163         FileName = Match.group(1).strip()
    164         for Dir in [os.path.dirname(Source)] + IncludePathList:
    165             FullFileName = os.path.normpath(os.path.join(Dir, FileName))
    166             if os.path.exists(FullFileName):
    167                 IncludeFiles[FullFileName.lower().replace("\\", "/")] = FullFileName
    168                 break
    169 
    170     #

    171     # Find header files with pattern like #include EFI_PPI_CONSUMER(XXX)

    172     #

    173     for Match in gIncludePattern2.finditer(FileContents):
    174         Key = Match.group(2)
    175         Type = Match.group(1)
    176         if "ARCH_PROTOCOL" in Type:
    177             FileName = "ArchProtocol/%(Key)s/%(Key)s.h" % {"Key" : Key}
    178         elif "PROTOCOL" in Type:
    179             FileName = "Protocol/%(Key)s/%(Key)s.h" % {"Key" : Key}
    180         elif "PPI" in Type:
    181             FileName = "Ppi/%(Key)s/%(Key)s.h" % {"Key" : Key}
    182         elif "GUID" in Type:
    183             FileName = "Guid/%(Key)s/%(Key)s.h" % {"Key" : Key}
    184         else:
    185             continue
    186         for Dir in IncludePathList:
    187             FullFileName = os.path.normpath(os.path.join(Dir, FileName))
    188             if os.path.exists(FullFileName):
    189                 IncludeFiles[FullFileName.lower().replace("\\", "/")] = FullFileName
    190                 break
    191 
    192 ## Split each lines in file

    193 #

    194 #  This method is used to split the lines in file to make the length of each line 

    195 #  less than MaxLength.

    196 #

    197 #  @param      Content           The content of file

    198 #  @param      MaxLength         The Max Length of the line

    199 #

    200 def FileLinesSplit(Content=None, MaxLength=None):
    201     ContentList = Content.split(TAB_LINE_BREAK)
    202     NewContent = ''
    203     NewContentList = []
    204     for Line in ContentList:
    205         while len(Line.rstrip()) > MaxLength:
    206             LineSpaceIndex = Line.rfind(TAB_SPACE_SPLIT, 0, MaxLength)
    207             LineSlashIndex = Line.rfind(TAB_SLASH, 0, MaxLength)
    208             LineBackSlashIndex = Line.rfind(TAB_BACK_SLASH, 0, MaxLength)
    209             if max(LineSpaceIndex, LineSlashIndex, LineBackSlashIndex) > 0:
    210                 LineBreakIndex = max(LineSpaceIndex, LineSlashIndex, LineBackSlashIndex)
    211             else:
    212                 LineBreakIndex = MaxLength
    213             NewContentList.append(Line[:LineBreakIndex])
    214             Line = Line[LineBreakIndex:]
    215         if Line:
    216             NewContentList.append(Line)
    217     for NewLine in NewContentList:
    218         NewContent += NewLine + TAB_LINE_BREAK
    219     
    220     NewContent = NewContent.replace(TAB_LINE_BREAK, gEndOfLine).replace('\r\r\n', gEndOfLine)
    221     return NewContent
    222     
    223     
    224     
    225 ##

    226 # Parse binary dependency expression section

    227 #

    228 # This utility class parses the dependency expression section and translate the readable

    229 # GUID name and value.

    230 #

    231 class DepexParser(object):
    232     ##

    233     # Constructor function for class DepexParser

    234     #

    235     # This constructor function collect GUID values so that the readable

    236     # GUID name can be translated.

    237     #

    238     # @param self            The object pointer

    239     # @param Wa              Workspace context information

    240     #

    241     def __init__(self, Wa):
    242         self._GuidDb = {}
    243         for Pa in Wa.AutoGenObjectList:
    244             for Package in Pa.PackageList:
    245                 for Protocol in Package.Protocols:
    246                     GuidValue = GuidStructureStringToGuidString(Package.Protocols[Protocol])
    247                     self._GuidDb[GuidValue.upper()] = Protocol
    248                 for Ppi in Package.Ppis:
    249                     GuidValue = GuidStructureStringToGuidString(Package.Ppis[Ppi])
    250                     self._GuidDb[GuidValue.upper()] = Ppi
    251                 for Guid in Package.Guids:
    252                     GuidValue = GuidStructureStringToGuidString(Package.Guids[Guid])
    253                     self._GuidDb[GuidValue.upper()] = Guid
    254     
    255     ##

    256     # Parse the binary dependency expression files.

    257     # 

    258     # This function parses the binary dependency expression file and translate it

    259     # to the instruction list.

    260     #

    261     # @param self            The object pointer

    262     # @param DepexFileName   The file name of binary dependency expression file.

    263     #

    264     def ParseDepexFile(self, DepexFileName):
    265         DepexFile = open(DepexFileName, "rb")
    266         DepexStatement = []
    267         OpCode = DepexFile.read(1)
    268         while OpCode:
    269             Statement = gOpCodeList[struct.unpack("B", OpCode)[0]]
    270             if Statement in ["BEFORE", "AFTER", "PUSH"]:
    271                 GuidValue = "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X" % \
    272                             struct.unpack("=LHHBBBBBBBB", DepexFile.read(16))
    273                 GuidString = self._GuidDb.get(GuidValue, GuidValue)
    274                 Statement = "%s %s" % (Statement, GuidString)
    275             DepexStatement.append(Statement)
    276             OpCode = DepexFile.read(1)
    277 
    278         return DepexStatement
    279     
    280 ##

    281 # Reports library information

    282 #

    283 # This class reports the module library subsection in the build report file.

    284 #

    285 class LibraryReport(object):
    286     ##

    287     # Constructor function for class LibraryReport

    288     #

    289     # This constructor function generates LibraryReport object for

    290     # a module.

    291     #

    292     # @param self            The object pointer

    293     # @param M               Module context information

    294     #

    295     def __init__(self, M):
    296         self.LibraryList = []
    297         if int(str(M.AutoGenVersion), 0) >= 0x00010005:
    298             self._EdkIIModule = True
    299         else:
    300             self._EdkIIModule = False
    301 
    302         for Lib in M.DependentLibraryList:
    303             LibInfPath = str(Lib)
    304             LibClassList = Lib.LibraryClass[0].LibraryClass
    305             LibConstructorList = Lib.ConstructorList
    306             LibDesstructorList = Lib.DestructorList
    307             LibDepexList = Lib.DepexExpression[M.Arch, M.ModuleType]
    308             self.LibraryList.append((LibInfPath, LibClassList, LibConstructorList, LibDesstructorList, LibDepexList))
    309 
    310     ##

    311     # Generate report for module library information

    312     #

    313     # This function generates report for the module library.

    314     # If the module is EDKII style one, the additional library class, library

    315     # constructor/destructor and dependency expression may also be reported.

    316     #

    317     # @param self            The object pointer

    318     # @param File            The file object for report

    319     #

    320     def GenerateReport(self, File):
    321         FileWrite(File, gSubSectionStart)
    322         FileWrite(File, TAB_BRG_LIBRARY)
    323         if len(self.LibraryList) > 0:
    324             FileWrite(File, gSubSectionSep)
    325             for LibraryItem in self.LibraryList:
    326                 LibInfPath = LibraryItem[0]
    327                 FileWrite(File, LibInfPath)
    328 
    329                 #

    330                 # Report library class, library constructor and destructor for

    331                 # EDKII style module.

    332                 #

    333                 if self._EdkIIModule:
    334                     LibClass = LibraryItem[1]
    335                     EdkIILibInfo = ""
    336                     LibConstructor = " ".join(LibraryItem[2])
    337                     if LibConstructor:
    338                         EdkIILibInfo += " C = " + LibConstructor
    339                     LibDestructor = " ".join(LibraryItem[3])
    340                     if LibDestructor:
    341                         EdkIILibInfo += " D = " + LibDestructor
    342                     LibDepex = " ".join(LibraryItem[4])
    343                     if LibDepex:
    344                         EdkIILibInfo += " Depex = " + LibDepex
    345                     if EdkIILibInfo:
    346                         FileWrite(File, "{%s: %s}" % (LibClass, EdkIILibInfo))
    347                     else:
    348                         FileWrite(File, "{%s}" % LibClass)
    349 
    350         FileWrite(File, gSubSectionEnd)
    351 
    352 ##

    353 # Reports dependency expression information

    354 #

    355 # This class reports the module dependency expression subsection in the build report file.

    356 #

    357 class DepexReport(object):
    358     ##

    359     # Constructor function for class DepexReport

    360     #

    361     # This constructor function generates DepexReport object for

    362     # a module. If the module source contains the DXS file (usually EDK

    363     # style module), it uses the dependency in DXS file; otherwise,

    364     # it uses the dependency expression from its own INF [Depex] section

    365     # and then merges with the ones from its dependent library INF.

    366     #

    367     # @param self            The object pointer

    368     # @param M               Module context information

    369     #

    370     def __init__(self, M):
    371         self.Depex = ""
    372         self._DepexFileName = os.path.join(M.BuildDir, "OUTPUT", M.Module.BaseName + ".depex")
    373         ModuleType = M.ModuleType
    374         if not ModuleType:
    375             ModuleType = gComponentType2ModuleType.get(M.ComponentType, "")
    376 
    377         if ModuleType in ["SEC", "PEI_CORE", "DXE_CORE", "SMM_CORE", "UEFI_APPLICATION"]:
    378             return
    379       
    380         for Source in M.SourceFileList:
    381             if os.path.splitext(Source.Path)[1].lower() == ".dxs":
    382                 Match = gDxsDependencyPattern.search(open(Source.Path).read())
    383                 if Match:
    384                     self.Depex = Match.group(1).strip()
    385                     self.Source = "DXS"
    386                     break
    387         else:
    388             self.Depex = M.DepexExpressionList.get(M.ModuleType, "")
    389             self.ModuleDepex = " ".join(M.Module.DepexExpression[M.Arch, M.ModuleType])
    390             if not self.ModuleDepex:
    391                 self.ModuleDepex = "(None)"
    392 
    393             LibDepexList = []
    394             for Lib in M.DependentLibraryList:
    395                 LibDepex = " ".join(Lib.DepexExpression[M.Arch, M.ModuleType]).strip()
    396                 if LibDepex != "":
    397                     LibDepexList.append("(" + LibDepex + ")")
    398             self.LibraryDepex = " AND ".join(LibDepexList)
    399             if not self.LibraryDepex:
    400                 self.LibraryDepex = "(None)"
    401             self.Source = "INF"
    402 
    403     ##

    404     # Generate report for module dependency expression information

    405     #

    406     # This function generates report for the module dependency expression.

    407     #

    408     # @param self              The object pointer

    409     # @param File              The file object for report

    410     # @param GlobalDepexParser The platform global Dependency expression parser object

    411     #

    412     def GenerateReport(self, File, GlobalDepexParser):
    413         if not self.Depex:
    414             FileWrite(File, gSubSectionStart)
    415             FileWrite(File, TAB_DEPEX)
    416             FileWrite(File, gSubSectionEnd)
    417             return
    418         FileWrite(File, gSubSectionStart)
    419         if os.path.isfile(self._DepexFileName):
    420             try:
    421                 DepexStatements = GlobalDepexParser.ParseDepexFile(self._DepexFileName)
    422                 FileWrite(File, "Final Dependency Expression (DEPEX) Instructions")
    423                 for DepexStatement in DepexStatements:
    424                     FileWrite(File, "  %s" % DepexStatement)
    425                 FileWrite(File, gSubSectionSep)
    426             except:
    427                 EdkLogger.warn(None, "Dependency expression file is corrupted", self._DepexFileName)
    428         
    429         FileWrite(File, "Dependency Expression (DEPEX) from %s" % self.Source)
    430 
    431         if self.Source == "INF":
    432             FileWrite(File, "%s" % self.Depex, True)
    433             FileWrite(File, gSubSectionSep)
    434             FileWrite(File, "From Module INF:  %s" % self.ModuleDepex, True)
    435             FileWrite(File, "From Library INF: %s" % self.LibraryDepex, True)
    436         else:
    437             FileWrite(File, "%s" % self.Depex)
    438         FileWrite(File, gSubSectionEnd)
    439 
    440 ##

    441 # Reports dependency expression information

    442 #

    443 # This class reports the module build flags subsection in the build report file.

    444 #

    445 class BuildFlagsReport(object):
    446     ##

    447     # Constructor function for class BuildFlagsReport

    448     #

    449     # This constructor function generates BuildFlagsReport object for

    450     # a module. It reports the build tool chain tag and all relevant

    451     # build flags to build the module.

    452     #

    453     # @param self            The object pointer

    454     # @param M               Module context information

    455     #

    456     def __init__(self, M):
    457         BuildOptions = {}
    458         #

    459         # Add build flags according to source file extension so that

    460         # irrelevant ones can be filtered out.

    461         #

    462         for Source in M.SourceFileList:
    463             Ext = os.path.splitext(Source.File)[1].lower()
    464             if Ext in [".c", ".cc", ".cpp"]:
    465                 BuildOptions["CC"] = 1
    466             elif Ext in [".s", ".asm"]:
    467                 BuildOptions["PP"] = 1
    468                 BuildOptions["ASM"] = 1
    469             elif Ext in [".vfr"]:
    470                 BuildOptions["VFRPP"] = 1
    471                 BuildOptions["VFR"] = 1
    472             elif Ext in [".dxs"]:
    473                 BuildOptions["APP"] = 1
    474                 BuildOptions["CC"] = 1
    475             elif Ext in [".asl"]:
    476                 BuildOptions["ASLPP"] = 1
    477                 BuildOptions["ASL"] = 1
    478             elif Ext in [".aslc"]:
    479                 BuildOptions["ASLCC"] = 1
    480                 BuildOptions["ASLDLINK"] = 1
    481                 BuildOptions["CC"] = 1
    482             elif Ext in [".asm16"]:
    483                 BuildOptions["ASMLINK"] = 1
    484             BuildOptions["SLINK"] = 1
    485             BuildOptions["DLINK"] = 1
    486 
    487         #

    488         # Save module build flags.

    489         #

    490         self.ToolChainTag = M.ToolChain
    491         self.BuildFlags = {}
    492         for Tool in BuildOptions:
    493             self.BuildFlags[Tool + "_FLAGS"] = M.BuildOption.get(Tool, {}).get("FLAGS", "")
    494 
    495     ##

    496     # Generate report for module build flags information

    497     #

    498     # This function generates report for the module build flags expression.

    499     #

    500     # @param self            The object pointer

    501     # @param File            The file object for report

    502     #

    503     def GenerateReport(self, File):
    504         FileWrite(File, gSubSectionStart)
    505         FileWrite(File, "Build Flags")
    506         FileWrite(File, "Tool Chain Tag: %s" % self.ToolChainTag)
    507         for Tool in self.BuildFlags:
    508             FileWrite(File, gSubSectionSep)
    509             FileWrite(File, "%s = %s" % (Tool, self.BuildFlags[Tool]), True)
    510 
    511         FileWrite(File, gSubSectionEnd)
    512 
    513 
    514 ##

    515 # Reports individual module information

    516 #

    517 # This class reports the module section in the build report file.

    518 # It comprises of module summary, module PCD, library, dependency expression,

    519 # build flags sections.

    520 #

    521 class ModuleReport(object):
    522     ##

    523     # Constructor function for class ModuleReport

    524     #

    525     # This constructor function generates ModuleReport object for

    526     # a separate module in a platform build.

    527     #

    528     # @param self            The object pointer

    529     # @param M               Module context information

    530     # @param ReportType      The kind of report items in the final report file

    531     #

    532     def __init__(self, M, ReportType):
    533         self.ModuleName = M.Module.BaseName
    534         self.ModuleInfPath = M.MetaFile.File
    535         self.FileGuid = M.Guid
    536         self.Size = 0
    537         self.BuildTimeStamp = None
    538         self.Hash = 0
    539         self.DriverType = ""
    540         if not M.IsLibrary:
    541             ModuleType = M.ModuleType
    542             if not ModuleType:
    543                 ModuleType = gComponentType2ModuleType.get(M.ComponentType, "")
    544             #

    545             # If a module complies to PI 1.1, promote Module type to "SMM_DRIVER"

    546             #

    547             if ModuleType == "DXE_SMM_DRIVER":
    548                 PiSpec = M.Module.Specification.get("PI_SPECIFICATION_VERSION", "0x00010000")
    549                 if int(PiSpec, 0) >= 0x0001000A:
    550                     ModuleType = "SMM_DRIVER"
    551             self.DriverType = gDriverTypeMap.get(ModuleType, "0x2 (FREE_FORM)")
    552         self.UefiSpecVersion = M.Module.Specification.get("UEFI_SPECIFICATION_VERSION", "")
    553         self.PiSpecVersion = M.Module.Specification.get("PI_SPECIFICATION_VERSION", "")
    554         self.PciDeviceId = M.Module.Defines.get("PCI_DEVICE_ID", "")
    555         self.PciVendorId = M.Module.Defines.get("PCI_VENDOR_ID", "")
    556         self.PciClassCode = M.Module.Defines.get("PCI_CLASS_CODE", "")
    557 
    558         self._BuildDir = M.BuildDir
    559         self.ModulePcdSet = {}
    560         if "PCD" in ReportType:
    561             #

    562             # Collect all module used PCD set: module INF referenced directly or indirectly.

    563             # It also saves module INF default values of them in case they exist.

    564             #

    565             for Pcd in M.ModulePcdList + M.LibraryPcdList:
    566                 self.ModulePcdSet.setdefault((Pcd.TokenCName, Pcd.TokenSpaceGuidCName, Pcd.Type), (Pcd.InfDefaultValue, Pcd.DefaultValue))
    567 
    568         self.LibraryReport = None
    569         if "LIBRARY" in ReportType:
    570             self.LibraryReport = LibraryReport(M)
    571 
    572         self.DepexReport = None
    573         if "DEPEX" in ReportType:
    574             self.DepexReport = DepexReport(M)
    575 
    576         if "BUILD_FLAGS" in ReportType:
    577             self.BuildFlagsReport = BuildFlagsReport(M)
    578 
    579 
    580     ##

    581     # Generate report for module information

    582     #

    583     # This function generates report for separate module expression

    584     # in a platform build.

    585     #

    586     # @param self                   The object pointer

    587     # @param File                   The file object for report

    588     # @param GlobalPcdReport        The platform global PCD report object

    589     # @param GlobalPredictionReport The platform global Prediction report object

    590     # @param GlobalDepexParser      The platform global Dependency expression parser object

    591     # @param ReportType             The kind of report items in the final report file

    592     #

    593     def GenerateReport(self, File, GlobalPcdReport, GlobalPredictionReport, GlobalDepexParser, ReportType):
    594         FileWrite(File, gSectionStart)
    595 
    596         FwReportFileName = os.path.join(self._BuildDir, "DEBUG", self.ModuleName + ".txt")
    597         if os.path.isfile(FwReportFileName):
    598             try:
    599                 FileContents = open(FwReportFileName).read()
    600                 Match = gModuleSizePattern.search(FileContents)
    601                 if Match:
    602                     self.Size = int(Match.group(1))
    603 
    604                 Match = gTimeStampPattern.search(FileContents)
    605                 if Match:
    606                     self.BuildTimeStamp = datetime.fromtimestamp(int(Match.group(1)))
    607             except IOError:
    608                 EdkLogger.warn(None, "Fail to read report file", FwReportFileName)
    609 
    610         if "HASH" in ReportType:
    611             OutputDir = os.path.join(self._BuildDir, "OUTPUT")
    612             DefaultEFIfile = os.path.join(OutputDir, self.ModuleName + ".efi")
    613             if os.path.isfile(DefaultEFIfile):
    614                 Tempfile = os.path.join(OutputDir, self.ModuleName + "_hash.tmp")
    615                 # rebase the efi image since its base address may not zero

    616                 cmd = ["GenFw", "--rebase", str(0), "-o", Tempfile, DefaultEFIfile]
    617                 try:
    618                     PopenObject = subprocess.Popen(' '.join(cmd), stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
    619                 except Exception, X:
    620                     EdkLogger.error("GenFw", COMMAND_FAILURE, ExtraData="%s: %s" % (str(X), cmd[0]))
    621                 EndOfProcedure = threading.Event()
    622                 EndOfProcedure.clear()
    623                 if PopenObject.stderr:
    624                     StdErrThread = threading.Thread(target=ReadMessage, args=(PopenObject.stderr, EdkLogger.quiet, EndOfProcedure))
    625                     StdErrThread.setName("STDERR-Redirector")
    626                     StdErrThread.setDaemon(False)
    627                     StdErrThread.start()
    628                 # waiting for program exit

    629                 PopenObject.wait()
    630                 if PopenObject.stderr:
    631                     StdErrThread.join()
    632                 if PopenObject.returncode != 0:
    633                     EdkLogger.error("GenFw", COMMAND_FAILURE, "Failed to generate firmware hash image for %s" % (DefaultEFIfile))
    634                 if os.path.isfile(Tempfile):
    635                     self.Hash = hashlib.sha1()
    636                     buf = open(Tempfile, 'rb').read()
    637                     if self.Hash.update(buf):
    638                         self.Hash = self.Hash.update(buf)
    639                     self.Hash = self.Hash.hexdigest()
    640                     os.remove(Tempfile)
    641 
    642         FileWrite(File, "Module Summary")
    643         FileWrite(File, "Module Name:          %s" % self.ModuleName)
    644         FileWrite(File, "Module INF Path:      %s" % self.ModuleInfPath)
    645         FileWrite(File, "File GUID:            %s" % self.FileGuid)
    646         if self.Size:
    647             FileWrite(File, "Size:                 0x%X (%.2fK)" % (self.Size, self.Size / 1024.0))
    648         if self.Hash:
    649             FileWrite(File, "SHA1 HASH:            %s *%s" % (self.Hash, self.ModuleName + ".efi"))
    650         if self.BuildTimeStamp:
    651             FileWrite(File, "Build Time Stamp:     %s" % self.BuildTimeStamp)
    652         if self.DriverType:
    653             FileWrite(File, "Driver Type:          %s" % self.DriverType)
    654         if self.UefiSpecVersion:
    655             FileWrite(File, "UEFI Spec Version:    %s" % self.UefiSpecVersion)
    656         if self.PiSpecVersion:
    657             FileWrite(File, "PI Spec Version:      %s" % self.PiSpecVersion)
    658         if self.PciDeviceId:
    659             FileWrite(File, "PCI Device ID:        %s" % self.PciDeviceId)
    660         if self.PciVendorId:
    661             FileWrite(File, "PCI Vendor ID:        %s" % self.PciVendorId)
    662         if self.PciClassCode:
    663             FileWrite(File, "PCI Class Code:       %s" % self.PciClassCode)
    664 
    665         FileWrite(File, gSectionSep)
    666 
    667         if "PCD" in ReportType:
    668             GlobalPcdReport.GenerateReport(File, self.ModulePcdSet)
    669 
    670         if "LIBRARY" in ReportType:
    671             self.LibraryReport.GenerateReport(File)
    672 
    673         if "DEPEX" in ReportType:
    674             self.DepexReport.GenerateReport(File, GlobalDepexParser)
    675 
    676         if "BUILD_FLAGS" in ReportType:
    677             self.BuildFlagsReport.GenerateReport(File)
    678 
    679         if "FIXED_ADDRESS" in ReportType and self.FileGuid:
    680             GlobalPredictionReport.GenerateReport(File, self.FileGuid)
    681 
    682         FileWrite(File, gSectionEnd)
    683 
    684 def ReadMessage(From, To, ExitFlag):
    685     while True:
    686         # read one line a time

    687         Line = From.readline()
    688         # empty string means "end"

    689         if Line != None and Line != "":
    690             To(Line.rstrip())
    691         else:
    692             break
    693         if ExitFlag.isSet():
    694             break
    695 
    696 ##

    697 # Reports platform and module PCD information

    698 #

    699 # This class reports the platform PCD section and module PCD subsection

    700 # in the build report file.

    701 #

    702 class PcdReport(object):
    703     ##

    704     # Constructor function for class PcdReport

    705     #

    706     # This constructor function generates PcdReport object a platform build.

    707     # It collects the whole PCD database from platform DSC files, platform

    708     # flash description file and package DEC files.

    709     #

    710     # @param self            The object pointer

    711     # @param Wa              Workspace context information

    712     #

    713     def __init__(self, Wa):
    714         self.AllPcds = {}
    715         self.UnusedPcds = {}
    716         self.ConditionalPcds = {}
    717         self.MaxLen = 0
    718         if Wa.FdfProfile:
    719             self.FdfPcdSet = Wa.FdfProfile.PcdDict
    720         else:
    721             self.FdfPcdSet = {}
    722 
    723         self.ModulePcdOverride = {}
    724         for Pa in Wa.AutoGenObjectList:
    725             #

    726             # Collect all platform referenced PCDs and grouped them by PCD token space

    727             # GUID C Names

    728             #

    729             for Pcd in Pa.AllPcdList:
    730                 PcdList = self.AllPcds.setdefault(Pcd.TokenSpaceGuidCName, {}).setdefault(Pcd.Type, [])
    731                 if Pcd not in PcdList:
    732                     PcdList.append(Pcd)
    733                 if len(Pcd.TokenCName) > self.MaxLen:
    734                     self.MaxLen = len(Pcd.TokenCName)
    735             #

    736             # Collect the PCD defined in DSC/FDF file, but not used in module

    737             #

    738             UnusedPcdFullList = []
    739             for item in Pa.Platform.Pcds:
    740                 Pcd = Pa.Platform.Pcds[item]
    741                 if not Pcd.Type:
    742                     PcdTypeFlag = False
    743                     for package in Pa.PackageList:
    744                         for T in ["FixedAtBuild", "PatchableInModule", "FeatureFlag", "Dynamic", "DynamicEx"]:
    745                             if (Pcd.TokenCName, Pcd.TokenSpaceGuidCName, T) in package.Pcds:
    746                                 Pcd.Type = T
    747                                 PcdTypeFlag = True
    748                                 if not Pcd.DatumType:
    749                                     Pcd.DatumType = package.Pcds[(Pcd.TokenCName, Pcd.TokenSpaceGuidCName, T)].DatumType
    750                                 break
    751                         if PcdTypeFlag:
    752                             break
    753                 if not Pcd.DatumType:
    754                     PcdType = Pcd.Type
    755                     # Try to remove Hii and Vpd suffix

    756                     if PcdType.startswith("DynamicEx"):
    757                         PcdType = "DynamicEx"
    758                     elif PcdType.startswith("Dynamic"):
    759                         PcdType = "Dynamic"
    760                     for package in Pa.PackageList:
    761                         if (Pcd.TokenCName, Pcd.TokenSpaceGuidCName, PcdType) in package.Pcds:
    762                             Pcd.DatumType = package.Pcds[(Pcd.TokenCName, Pcd.TokenSpaceGuidCName, PcdType)].DatumType
    763                             break
    764 
    765                 PcdList = self.AllPcds.setdefault(Pcd.TokenSpaceGuidCName, {}).setdefault(Pcd.Type, [])
    766                 if Pcd not in PcdList and Pcd not in UnusedPcdFullList:
    767                     UnusedPcdFullList.append(Pcd)
    768                 if len(Pcd.TokenCName) > self.MaxLen:
    769                     self.MaxLen = len(Pcd.TokenCName)
    770 
    771             if GlobalData.gConditionalPcds:
    772                 for PcdItem in GlobalData.gConditionalPcds:
    773                     if '.' in PcdItem:
    774                         (TokenSpaceGuidCName, TokenCName) = PcdItem.split('.')
    775                         if (TokenCName, TokenSpaceGuidCName) in Pa.Platform.Pcds.keys():
    776                             Pcd = Pa.Platform.Pcds[(TokenCName, TokenSpaceGuidCName)]
    777                             PcdList = self.ConditionalPcds.setdefault(Pcd.TokenSpaceGuidCName, {}).setdefault(Pcd.Type, [])
    778                             if Pcd not in PcdList:
    779                                 PcdList.append(Pcd)
    780 
    781             UnusedPcdList = []
    782             if UnusedPcdFullList:
    783                 for Pcd in UnusedPcdFullList:
    784                     if Pcd.TokenSpaceGuidCName + '.' + Pcd.TokenCName in GlobalData.gConditionalPcds:
    785                         continue
    786                     UnusedPcdList.append(Pcd)
    787 
    788             for Pcd in UnusedPcdList:
    789                 PcdList = self.UnusedPcds.setdefault(Pcd.TokenSpaceGuidCName, {}).setdefault(Pcd.Type, [])
    790                 if Pcd not in PcdList:
    791                     PcdList.append(Pcd)
    792 
    793             for Module in Pa.Platform.Modules.values():
    794                 #

    795                 # Collect module override PCDs

    796                 #

    797                 for ModulePcd in Module.M.ModulePcdList + Module.M.LibraryPcdList:
    798                     TokenCName = ModulePcd.TokenCName
    799                     TokenSpaceGuid = ModulePcd.TokenSpaceGuidCName
    800                     ModuleDefault = ModulePcd.DefaultValue
    801                     ModulePath = os.path.basename(Module.M.MetaFile.File)
    802                     self.ModulePcdOverride.setdefault((TokenCName, TokenSpaceGuid), {})[ModulePath] = ModuleDefault
    803 
    804 
    805         #

    806         # Collect PCD DEC default value.

    807         #

    808         self.DecPcdDefault = {}
    809         for Pa in Wa.AutoGenObjectList:
    810             for Package in Pa.PackageList:
    811                 for (TokenCName, TokenSpaceGuidCName, DecType) in Package.Pcds:
    812                     DecDefaultValue = Package.Pcds[TokenCName, TokenSpaceGuidCName, DecType].DefaultValue
    813                     self.DecPcdDefault.setdefault((TokenCName, TokenSpaceGuidCName, DecType), DecDefaultValue)
    814         #

    815         # Collect PCDs defined in DSC common section

    816         #

    817         self.DscPcdDefault = {}
    818         for Arch in Wa.ArchList:
    819             Platform = Wa.BuildDatabase[Wa.MetaFile, Arch, Wa.BuildTarget, Wa.ToolChain]
    820             for (TokenCName, TokenSpaceGuidCName) in Platform.Pcds:
    821                 DscDefaultValue = Platform.Pcds[(TokenCName, TokenSpaceGuidCName)].DefaultValue
    822                 if DscDefaultValue:
    823                     self.DscPcdDefault[(TokenCName, TokenSpaceGuidCName)] = DscDefaultValue
    824 
    825     def GenerateReport(self, File, ModulePcdSet):
    826         if self.ConditionalPcds:
    827             self.GenerateReportDetail(File, ModulePcdSet, 1)
    828         if self.UnusedPcds:
    829             self.GenerateReportDetail(File, ModulePcdSet, 2)
    830         self.GenerateReportDetail(File, ModulePcdSet)
    831 
    832     ##

    833     # Generate report for PCD information

    834     #

    835     # This function generates report for separate module expression

    836     # in a platform build.

    837     #

    838     # @param self            The object pointer

    839     # @param File            The file object for report

    840     # @param ModulePcdSet    Set of all PCDs referenced by module or None for

    841     #                        platform PCD report

    842     # @param ReportySubType  0 means platform/module PCD report, 1 means Conditional

    843     #                        directives section report, 2 means Unused Pcds section report

    844     # @param DscOverridePcds Module DSC override PCDs set

    845     #

    846     def GenerateReportDetail(self, File, ModulePcdSet, ReportSubType = 0):
    847         PcdDict = self.AllPcds
    848         if ReportSubType == 1:
    849             PcdDict = self.ConditionalPcds
    850         elif ReportSubType == 2:
    851             PcdDict = self.UnusedPcds
    852 
    853         if ModulePcdSet == None:
    854             FileWrite(File, gSectionStart)
    855             if ReportSubType == 1:
    856                 FileWrite(File, "Conditional Directives used by the build system")
    857             elif ReportSubType == 2:
    858                 FileWrite(File, "PCDs not used by modules or in conditional directives")
    859             else:
    860                 FileWrite(File, "Platform Configuration Database Report")
    861 
    862             FileWrite(File, "  *B  - PCD override in the build option")
    863             FileWrite(File, "  *P  - Platform scoped PCD override in DSC file")
    864             FileWrite(File, "  *F  - Platform scoped PCD override in FDF file")
    865             if not ReportSubType:
    866                 FileWrite(File, "  *M  - Module scoped PCD override")
    867             FileWrite(File, gSectionSep)
    868         else:
    869             if not ReportSubType:
    870                 #

    871                 # For module PCD sub-section

    872                 #

    873                 FileWrite(File, gSubSectionStart)
    874                 FileWrite(File, TAB_BRG_PCD)
    875                 FileWrite(File, gSubSectionSep)
    876 
    877         for Key in PcdDict:
    878             #

    879             # Group PCD by their token space GUID C Name

    880             #

    881             First = True
    882             for Type in PcdDict[Key]:
    883                 #

    884                 # Group PCD by their usage type

    885                 #

    886                 TypeName, DecType = gPcdTypeMap.get(Type, ("", Type))
    887                 for Pcd in PcdDict[Key][Type]:
    888                     PcdTokenCName = Pcd.TokenCName
    889                     MixedPcdFlag = False
    890                     if GlobalData.MixedPcd:
    891                         for PcdKey in GlobalData.MixedPcd:
    892                             if (Pcd.TokenCName, Pcd.TokenSpaceGuidCName) in GlobalData.MixedPcd[PcdKey]:
    893                                 PcdTokenCName = PcdKey[0]
    894                                 MixedPcdFlag = True
    895                         if MixedPcdFlag and not ModulePcdSet:
    896                             continue
    897                     #

    898                     # Get PCD default value and their override relationship

    899                     #

    900                     DecDefaultValue = self.DecPcdDefault.get((Pcd.TokenCName, Pcd.TokenSpaceGuidCName, DecType))
    901                     DscDefaultValue = self.DscPcdDefault.get((Pcd.TokenCName, Pcd.TokenSpaceGuidCName))
    902                     DscDefaultValue = self.FdfPcdSet.get((Pcd.TokenCName, Key), DscDefaultValue)
    903                     InfDefaultValue = None
    904                     
    905                     PcdValue = DecDefaultValue
    906                     if DscDefaultValue:
    907                         PcdValue = DscDefaultValue
    908                     if ModulePcdSet != None:
    909                         if (Pcd.TokenCName, Pcd.TokenSpaceGuidCName, Type) not in ModulePcdSet:
    910                             continue
    911                         InfDefault, PcdValue = ModulePcdSet[Pcd.TokenCName, Pcd.TokenSpaceGuidCName, Type]
    912                         if InfDefault == "":
    913                             InfDefault = None
    914 
    915                     BuildOptionMatch = False
    916                     if GlobalData.BuildOptionPcd:
    917                         for pcd in GlobalData.BuildOptionPcd:
    918                             if (Pcd.TokenSpaceGuidCName, Pcd.TokenCName) == (pcd[0], pcd[1]):
    919                                 PcdValue = pcd[2]
    920                                 BuildOptionMatch = True
    921                                 break
    922 
    923                     if First:
    924                         if ModulePcdSet == None:
    925                             FileWrite(File, "")
    926                         FileWrite(File, Key)
    927                         First = False
    928 
    929 
    930                     if Pcd.DatumType in ('UINT8', 'UINT16', 'UINT32', 'UINT64'):
    931                         PcdValueNumber = int(PcdValue.strip(), 0)
    932                         if DecDefaultValue == None:
    933                             DecMatch = True
    934                         else:
    935                             DecDefaultValueNumber = int(DecDefaultValue.strip(), 0)
    936                             DecMatch = (DecDefaultValueNumber == PcdValueNumber)
    937 
    938                         if InfDefaultValue == None:
    939                             InfMatch = True
    940                         else:
    941                             InfDefaultValueNumber = int(InfDefaultValue.strip(), 0)
    942                             InfMatch = (InfDefaultValueNumber == PcdValueNumber)
    943 
    944                         if DscDefaultValue == None:
    945                             DscMatch = True
    946                         else:
    947                             DscDefaultValueNumber = int(DscDefaultValue.strip(), 0)
    948                             DscMatch = (DscDefaultValueNumber == PcdValueNumber)
    949                     else:
    950                         if DecDefaultValue == None:
    951                             DecMatch = True
    952                         else:
    953                             DecMatch = (DecDefaultValue.strip() == PcdValue.strip())
    954 
    955                         if InfDefaultValue == None:
    956                             InfMatch = True
    957                         else:
    958                             InfMatch = (InfDefaultValue.strip() == PcdValue.strip())
    959 
    960                         if DscDefaultValue == None:
    961                             DscMatch = True
    962                         else:
    963                             DscMatch = (DscDefaultValue.strip() == PcdValue.strip())
    964 
    965                     #

    966                     # Report PCD item according to their override relationship

    967                     #

    968                     if BuildOptionMatch:
    969                         FileWrite(File, ' *B %-*s: %6s %10s = %-22s' % (self.MaxLen, PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', PcdValue.strip()))
    970                     elif DecMatch and InfMatch:
    971                         FileWrite(File, '    %-*s: %6s %10s = %-22s' % (self.MaxLen, PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', PcdValue.strip()))
    972                     else:
    973                         if DscMatch:
    974                             if (Pcd.TokenCName, Key) in self.FdfPcdSet:
    975                                 FileWrite(File, ' *F %-*s: %6s %10s = %-22s' % (self.MaxLen, PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', PcdValue.strip()))
    976                             else:
    977                                 FileWrite(File, ' *P %-*s: %6s %10s = %-22s' % (self.MaxLen, PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', PcdValue.strip()))
    978                         else:
    979                             FileWrite(File, ' *M %-*s: %6s %10s = %-22s' % (self.MaxLen, PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', PcdValue.strip()))
    980 
    981                     if TypeName in ('DYNHII', 'DEXHII', 'DYNVPD', 'DEXVPD'):
    982                         for SkuInfo in Pcd.SkuInfoList.values():
    983                             if TypeName in ('DYNHII', 'DEXHII'):
    984                                 FileWrite(File, '%*s: %s: %s' % (self.MaxLen + 4, SkuInfo.VariableGuid, SkuInfo.VariableName, SkuInfo.VariableOffset))
    985                             else:
    986                                 FileWrite(File, '%*s' % (self.MaxLen + 4, SkuInfo.VpdOffset))
    987                                
    988                     if not DscMatch and DscDefaultValue != None:
    989                         FileWrite(File, '    %*s = %s' % (self.MaxLen + 19, 'DSC DEFAULT', DscDefaultValue.strip()))
    990 
    991                     if not InfMatch and InfDefaultValue != None:
    992                         FileWrite(File, '    %*s = %s' % (self.MaxLen + 19, 'INF DEFAULT', InfDefaultValue.strip()))
    993 
    994                     if not DecMatch and DecDefaultValue != None:
    995                         FileWrite(File, '    %*s = %s' % (self.MaxLen + 19, 'DEC DEFAULT', DecDefaultValue.strip()))
    996 
    997                     if ModulePcdSet == None:
    998                         if not BuildOptionMatch:
    999                             ModuleOverride = self.ModulePcdOverride.get((Pcd.TokenCName, Pcd.TokenSpaceGuidCName), {})
   1000                             for ModulePath in ModuleOverride:
   1001                                 ModuleDefault = ModuleOverride[ModulePath]
   1002                                 if Pcd.DatumType in ('UINT8', 'UINT16', 'UINT32', 'UINT64'):
   1003                                     ModulePcdDefaultValueNumber = int(ModuleDefault.strip(), 0)
   1004                                     Match = (ModulePcdDefaultValueNumber == PcdValueNumber)
   1005                                 else:
   1006                                     Match = (ModuleDefault.strip() == PcdValue.strip())
   1007                                 if Match:
   1008                                     continue
   1009                                 FileWrite(File, ' *M %-*s = %s' % (self.MaxLen + 19, ModulePath, ModuleDefault.strip()))
   1010 
   1011         if ModulePcdSet == None:
   1012             FileWrite(File, gSectionEnd)
   1013         else:
   1014             if not ReportSubType:
   1015                 FileWrite(File, gSubSectionEnd)
   1016 
   1017 
   1018 
   1019 ##

   1020 # Reports platform and module Prediction information

   1021 #

   1022 # This class reports the platform execution order prediction section and

   1023 # module load fixed address prediction subsection in the build report file.

   1024 #

   1025 class PredictionReport(object):
   1026     ##

   1027     # Constructor function for class PredictionReport

   1028     #

   1029     # This constructor function generates PredictionReport object for the platform.

   1030     #

   1031     # @param self:           The object pointer

   1032     # @param Wa              Workspace context information

   1033     #

   1034     def __init__(self, Wa):
   1035         self._MapFileName = os.path.join(Wa.BuildDir, Wa.Name + ".map")
   1036         self._MapFileParsed = False
   1037         self._EotToolInvoked = False
   1038         self._FvDir = Wa.FvDir
   1039         self._EotDir = Wa.BuildDir
   1040         self._FfsEntryPoint = {}
   1041         self._GuidMap = {}
   1042         self._SourceList = []
   1043         self.FixedMapDict = {}
   1044         self.ItemList = []
   1045         self.MaxLen = 0
   1046 
   1047         #

   1048         # Collect all platform reference source files and GUID C Name

   1049         #

   1050         for Pa in Wa.AutoGenObjectList:
   1051             for Module in Pa.LibraryAutoGenList + Pa.ModuleAutoGenList:
   1052                 #

   1053                 # BASE typed modules are EFI agnostic, so we need not scan

   1054                 # their source code to find PPI/Protocol produce or consume

   1055                 # information.

   1056                 #

   1057                 if Module.ModuleType == "BASE":
   1058                     continue
   1059                 #

   1060                 # Add module referenced source files

   1061                 #

   1062                 self._SourceList.append(str(Module))
   1063                 IncludeList = {}
   1064                 for Source in Module.SourceFileList:
   1065                     if os.path.splitext(str(Source))[1].lower() == ".c":
   1066                         self._SourceList.append("  " + str(Source))
   1067                         FindIncludeFiles(Source.Path, Module.IncludePathList, IncludeList)
   1068                 for IncludeFile in IncludeList.values():
   1069                     self._SourceList.append("  " + IncludeFile)
   1070 
   1071                 for Guid in Module.PpiList:
   1072                     self._GuidMap[Guid] = GuidStructureStringToGuidString(Module.PpiList[Guid])
   1073                 for Guid in Module.ProtocolList:
   1074                     self._GuidMap[Guid] = GuidStructureStringToGuidString(Module.ProtocolList[Guid])
   1075                 for Guid in Module.GuidList:
   1076                     self._GuidMap[Guid] = GuidStructureStringToGuidString(Module.GuidList[Guid])
   1077 
   1078                 if Module.Guid and not Module.IsLibrary:
   1079                     EntryPoint = " ".join(Module.Module.ModuleEntryPointList)
   1080                     if int(str(Module.AutoGenVersion), 0) >= 0x00010005:
   1081                         RealEntryPoint = "_ModuleEntryPoint"
   1082                     else:
   1083                         RealEntryPoint = EntryPoint
   1084                         if EntryPoint == "_ModuleEntryPoint":
   1085                             CCFlags = Module.BuildOption.get("CC", {}).get("FLAGS", "")
   1086                             Match = gGlueLibEntryPoint.search(CCFlags)
   1087                             if Match:
   1088                                 EntryPoint = Match.group(1)
   1089 
   1090                     self._FfsEntryPoint[Module.Guid.upper()] = (EntryPoint, RealEntryPoint)
   1091 
   1092 
   1093         #

   1094         # Collect platform firmware volume list as the input of EOT.

   1095         #

   1096         self._FvList = []
   1097         if Wa.FdfProfile:
   1098             for Fd in Wa.FdfProfile.FdDict:
   1099                 for FdRegion in Wa.FdfProfile.FdDict[Fd].RegionList:
   1100                     if FdRegion.RegionType != "FV":
   1101                         continue
   1102                     for FvName in FdRegion.RegionDataList:
   1103                         if FvName in self._FvList:
   1104                             continue
   1105                         self._FvList.append(FvName)
   1106                         for Ffs in Wa.FdfProfile.FvDict[FvName.upper()].FfsList:
   1107                             for Section in Ffs.SectionList:
   1108                                 try:
   1109                                     for FvSection in Section.SectionList:
   1110                                         if FvSection.FvName in self._FvList:
   1111                                             continue
   1112                                         self._FvList.append(FvSection.FvName)
   1113                                 except AttributeError:
   1114                                     pass
   1115 
   1116 
   1117     ##

   1118     # Parse platform fixed address map files

   1119     #

   1120     # This function parses the platform final fixed address map file to get

   1121     # the database of predicted fixed address for module image base, entry point

   1122     # etc.

   1123     #

   1124     # @param self:           The object pointer

   1125     #

   1126     def _ParseMapFile(self):
   1127         if self._MapFileParsed:
   1128             return
   1129         self._MapFileParsed = True
   1130         if os.path.isfile(self._MapFileName):
   1131             try:
   1132                 FileContents = open(self._MapFileName).read()
   1133                 for Match in gMapFileItemPattern.finditer(FileContents):
   1134                     AddressType = Match.group(1)
   1135                     BaseAddress = Match.group(2)
   1136                     EntryPoint = Match.group(3)
   1137                     Guid = Match.group(4).upper()
   1138                     List = self.FixedMapDict.setdefault(Guid, [])
   1139                     List.append((AddressType, BaseAddress, "*I"))
   1140                     List.append((AddressType, EntryPoint, "*E"))
   1141             except:
   1142                 EdkLogger.warn(None, "Cannot open file to read", self._MapFileName)
   1143 
   1144     ##

   1145     # Invokes EOT tool to get the predicted the execution order.

   1146     #

   1147     # This function invokes EOT tool to calculate the predicted dispatch order

   1148     #

   1149     # @param self:           The object pointer

   1150     #

   1151     def _InvokeEotTool(self):
   1152         if self._EotToolInvoked:
   1153             return
   1154 
   1155         self._EotToolInvoked = True
   1156         FvFileList = []
   1157         for FvName in self._FvList:
   1158             FvFile = os.path.join(self._FvDir, FvName + ".Fv")
   1159             if os.path.isfile(FvFile):
   1160                 FvFileList.append(FvFile)
   1161 
   1162         if len(FvFileList) == 0:
   1163             return
   1164         #

   1165         # Write source file list and GUID file list to an intermediate file

   1166         # as the input for EOT tool and dispatch List as the output file

   1167         # from EOT tool.

   1168         #

   1169         SourceList = os.path.join(self._EotDir, "SourceFile.txt")
   1170         GuidList = os.path.join(self._EotDir, "GuidList.txt")
   1171         DispatchList = os.path.join(self._EotDir, "Dispatch.txt")
   1172 
   1173         TempFile = open(SourceList, "w+")
   1174         for Item in self._SourceList:
   1175             FileWrite(TempFile, Item)
   1176         TempFile.close()
   1177         TempFile = open(GuidList, "w+")
   1178         for Key in self._GuidMap:
   1179             FileWrite(TempFile, "%s %s" % (Key, self._GuidMap[Key]))
   1180         TempFile.close()
   1181 
   1182         try:
   1183             from Eot.Eot import Eot
   1184 
   1185             #

   1186             # Invoke EOT tool and echo its runtime performance

   1187             #

   1188             EotStartTime = time.time()
   1189             Eot(CommandLineOption=False, SourceFileList=SourceList, GuidList=GuidList,
   1190                 FvFileList=' '.join(FvFileList), Dispatch=DispatchList, IsInit=True)
   1191             EotEndTime = time.time()
   1192             EotDuration = time.strftime("%H:%M:%S", time.gmtime(int(round(EotEndTime - EotStartTime))))
   1193             EdkLogger.quiet("EOT run time: %s\n" % EotDuration)
   1194             
   1195             #

   1196             # Parse the output of EOT tool

   1197             #

   1198             for Line in open(DispatchList):
   1199                 if len(Line.split()) < 4:
   1200                     continue
   1201                 (Guid, Phase, FfsName, FilePath) = Line.split()
   1202                 Symbol = self._FfsEntryPoint.get(Guid, [FfsName, ""])[0]
   1203                 if len(Symbol) > self.MaxLen:
   1204                     self.MaxLen = len(Symbol)
   1205                 self.ItemList.append((Phase, Symbol, FilePath))
   1206         except:
   1207             EdkLogger.quiet("(Python %s on %s\n%s)" % (platform.python_version(), sys.platform, traceback.format_exc()))
   1208             EdkLogger.warn(None, "Failed to generate execution order prediction report, for some error occurred in executing EOT.")
   1209 
   1210 
   1211     ##

   1212     # Generate platform execution order report

   1213     #

   1214     # This function generates the predicted module execution order.

   1215     #

   1216     # @param self            The object pointer

   1217     # @param File            The file object for report

   1218     #

   1219     def _GenerateExecutionOrderReport(self, File):
   1220         self._InvokeEotTool()
   1221         if len(self.ItemList) == 0:
   1222             return
   1223         FileWrite(File, gSectionStart)
   1224         FileWrite(File, "Execution Order Prediction")
   1225         FileWrite(File, "*P PEI phase")
   1226         FileWrite(File, "*D DXE phase")
   1227         FileWrite(File, "*E Module INF entry point name")
   1228         FileWrite(File, "*N Module notification function name")
   1229 
   1230         FileWrite(File, "Type %-*s %s" % (self.MaxLen, "Symbol", "Module INF Path"))
   1231         FileWrite(File, gSectionSep)
   1232         for Item in self.ItemList:
   1233             FileWrite(File, "*%sE  %-*s %s" % (Item[0], self.MaxLen, Item[1], Item[2]))
   1234 
   1235         FileWrite(File, gSectionStart)
   1236 
   1237     ##

   1238     # Generate Fixed Address report.

   1239     #

   1240     # This function generate the predicted fixed address report for a module

   1241     # specified by Guid.

   1242     #

   1243     # @param self            The object pointer

   1244     # @param File            The file object for report

   1245     # @param Guid            The module Guid value.

   1246     # @param NotifyList      The list of all notify function in a module

   1247     #

   1248     def _GenerateFixedAddressReport(self, File, Guid, NotifyList):
   1249         self._ParseMapFile()
   1250         FixedAddressList = self.FixedMapDict.get(Guid)
   1251         if not FixedAddressList:
   1252             return
   1253 
   1254         FileWrite(File, gSubSectionStart)
   1255         FileWrite(File, "Fixed Address Prediction")
   1256         FileWrite(File, "*I  Image Loading Address")
   1257         FileWrite(File, "*E  Entry Point Address")
   1258         FileWrite(File, "*N  Notification Function Address")
   1259         FileWrite(File, "*F  Flash Address")
   1260         FileWrite(File, "*M  Memory Address")
   1261         FileWrite(File, "*S  SMM RAM Offset")
   1262         FileWrite(File, "TOM Top of Memory")
   1263 
   1264         FileWrite(File, "Type Address           Name")
   1265         FileWrite(File, gSubSectionSep)
   1266         for Item in FixedAddressList:
   1267             Type = Item[0]
   1268             Value = Item[1]
   1269             Symbol = Item[2]
   1270             if Symbol == "*I":
   1271                 Name = "(Image Base)"
   1272             elif Symbol == "*E":
   1273                 Name = self._FfsEntryPoint.get(Guid, ["", "_ModuleEntryPoint"])[1]
   1274             elif Symbol in NotifyList:
   1275                 Name = Symbol
   1276                 Symbol = "*N"
   1277             else:
   1278                 continue
   1279 
   1280             if "Flash" in Type:
   1281                 Symbol += "F"
   1282             elif "Memory" in Type:
   1283                 Symbol += "M"
   1284             else:
   1285                 Symbol += "S"
   1286 
   1287             if Value[0] == "-":
   1288                 Value = "TOM" + Value
   1289 
   1290             FileWrite(File, "%s  %-16s  %s" % (Symbol, Value, Name))
   1291 
   1292     ##

   1293     # Generate report for the prediction part

   1294     #

   1295     # This function generate the predicted fixed address report for a module or

   1296     # predicted module execution order for a platform.

   1297     # If the input Guid is None, then, it generates the predicted module execution order;

   1298     # otherwise it generated the module fixed loading address for the module specified by

   1299     # Guid.

   1300     #

   1301     # @param self            The object pointer

   1302     # @param File            The file object for report

   1303     # @param Guid            The module Guid value.

   1304     #

   1305     def GenerateReport(self, File, Guid):
   1306         if Guid:
   1307             self._GenerateFixedAddressReport(File, Guid.upper(), [])
   1308         else:
   1309             self._GenerateExecutionOrderReport(File)
   1310 
   1311 ##

   1312 # Reports FD region information

   1313 #

   1314 # This class reports the FD subsection in the build report file.

   1315 # It collects region information of platform flash device.

   1316 # If the region is a firmware volume, it lists the set of modules

   1317 # and its space information; otherwise, it only lists its region name,

   1318 # base address and size in its sub-section header.

   1319 # If there are nesting FVs, the nested FVs will list immediate after

   1320 # this FD region subsection

   1321 #

   1322 class FdRegionReport(object):
   1323     ##

   1324     # Discover all the nested FV name list.

   1325     #

   1326     # This is an internal worker function to discover the all the nested FV information

   1327     # in the parent firmware volume. It uses deep first search algorithm recursively to

   1328     # find all the FV list name and append them to the list.

   1329     #

   1330     # @param self            The object pointer

   1331     # @param FvName          The name of current firmware file system

   1332     # @param Wa              Workspace context information

   1333     #

   1334     def _DiscoverNestedFvList(self, FvName, Wa):
   1335         FvDictKey=FvName.upper()
   1336         if FvDictKey in Wa.FdfProfile.FvDict:
   1337             for Ffs in Wa.FdfProfile.FvDict[FvName.upper()].FfsList:
   1338                 for Section in Ffs.SectionList:
   1339                     try:
   1340                         for FvSection in Section.SectionList:
   1341                             if FvSection.FvName in self.FvList:
   1342                                 continue
   1343                             self._GuidsDb[Ffs.NameGuid.upper()] = FvSection.FvName
   1344                             self.FvList.append(FvSection.FvName)
   1345                             self.FvInfo[FvSection.FvName] = ("Nested FV", 0, 0)
   1346                             self._DiscoverNestedFvList(FvSection.FvName, Wa)
   1347                     except AttributeError:
   1348                         pass
   1349 
   1350     ##

   1351     # Constructor function for class FdRegionReport

   1352     #

   1353     # This constructor function generates FdRegionReport object for a specified FdRegion.

   1354     # If the FdRegion is a firmware volume, it will recursively find all its nested Firmware

   1355     # volume list. This function also collects GUID map in order to dump module identification

   1356     # in the final report.

   1357     #

   1358     # @param self:           The object pointer

   1359     # @param FdRegion        The current FdRegion object

   1360     # @param Wa              Workspace context information

   1361     #

   1362     def __init__(self, FdRegion, Wa):
   1363         self.Type = FdRegion.RegionType
   1364         self.BaseAddress = FdRegion.Offset
   1365         self.Size = FdRegion.Size
   1366         self.FvList = []
   1367         self.FvInfo = {}
   1368         self._GuidsDb = {}
   1369         self._FvDir = Wa.FvDir
   1370 
   1371         #

   1372         # If the input FdRegion is not a firmware volume,

   1373         # we are done.

   1374         #

   1375         if self.Type != "FV":
   1376             return
   1377 
   1378         #

   1379         # Find all nested FVs in the FdRegion

   1380         #

   1381         for FvName in FdRegion.RegionDataList:
   1382             if FvName in self.FvList:
   1383                 continue
   1384             self.FvList.append(FvName)
   1385             self.FvInfo[FvName] = ("Fd Region", self.BaseAddress, self.Size)
   1386             self._DiscoverNestedFvList(FvName, Wa)
   1387 
   1388         PlatformPcds = {}
   1389         #

   1390         # Collect PCDs declared in DEC files.

   1391         #        

   1392         for Pa in Wa.AutoGenObjectList:
   1393             for Package in Pa.PackageList:
   1394                 for (TokenCName, TokenSpaceGuidCName, DecType) in Package.Pcds:
   1395                     DecDefaultValue = Package.Pcds[TokenCName, TokenSpaceGuidCName, DecType].DefaultValue
   1396                     PlatformPcds[(TokenCName, TokenSpaceGuidCName)] = DecDefaultValue
   1397         #

   1398         # Collect PCDs defined in DSC file

   1399         #

   1400         for arch in Wa.ArchList:
   1401             Platform = Wa.BuildDatabase[Wa.MetaFile, arch]
   1402             for (TokenCName, TokenSpaceGuidCName) in Platform.Pcds:
   1403                 DscDefaultValue = Platform.Pcds[(TokenCName, TokenSpaceGuidCName)].DefaultValue
   1404                 PlatformPcds[(TokenCName, TokenSpaceGuidCName)] = DscDefaultValue
   1405 
   1406         #

   1407         # Add PEI and DXE a priori files GUIDs defined in PI specification.

   1408         #

   1409         self._GuidsDb["1B45CC0A-156A-428A-AF62-49864DA0E6E6"] = "PEI Apriori"
   1410         self._GuidsDb["FC510EE7-FFDC-11D4-BD41-0080C73C8881"] = "DXE Apriori"
   1411         #

   1412         # Add ACPI table storage file

   1413         #

   1414         self._GuidsDb["7E374E25-8E01-4FEE-87F2-390C23C606CD"] = "ACPI table storage"
   1415 
   1416         for Pa in Wa.AutoGenObjectList:
   1417             for ModuleKey in Pa.Platform.Modules:
   1418                 M = Pa.Platform.Modules[ModuleKey].M
   1419                 InfPath = mws.join(Wa.WorkspaceDir, M.MetaFile.File)
   1420                 self._GuidsDb[M.Guid.upper()] = "%s (%s)" % (M.Module.BaseName, InfPath)
   1421 
   1422         #

   1423         # Collect the GUID map in the FV firmware volume

   1424         #

   1425         for FvName in self.FvList:
   1426             FvDictKey=FvName.upper()
   1427             if FvDictKey in Wa.FdfProfile.FvDict:
   1428                 for Ffs in Wa.FdfProfile.FvDict[FvName.upper()].FfsList:
   1429                     try:
   1430                         #

   1431                         # collect GUID map for binary EFI file in FDF file.

   1432                         #

   1433                         Guid = Ffs.NameGuid.upper()
   1434                         Match = gPcdGuidPattern.match(Ffs.NameGuid)
   1435                         if Match:
   1436                             PcdTokenspace = Match.group(1)
   1437                             PcdToken = Match.group(2)
   1438                             if (PcdToken, PcdTokenspace) in PlatformPcds:
   1439                                 GuidValue = PlatformPcds[(PcdToken, PcdTokenspace)]
   1440                                 Guid = GuidStructureByteArrayToGuidString(GuidValue).upper()
   1441                         for Section in Ffs.SectionList:
   1442                             try:
   1443                                 ModuleSectFile = mws.join(Wa.WorkspaceDir, Section.SectFileName)
   1444                                 self._GuidsDb[Guid] = ModuleSectFile
   1445                             except AttributeError:
   1446                                 pass
   1447                     except AttributeError:
   1448                         pass
   1449 
   1450 
   1451     ##

   1452     # Internal worker function to generate report for the FD region

   1453     #

   1454     # This internal worker function to generate report for the FD region.

   1455     # It the type is firmware volume, it lists offset and module identification.

   1456     #

   1457     # @param self            The object pointer

   1458     # @param File            The file object for report

   1459     # @param Title           The title for the FD subsection

   1460     # @param BaseAddress     The base address for the FD region

   1461     # @param Size            The size of the FD region

   1462     # @param FvName          The FV name if the FD region is a firmware volume

   1463     #

   1464     def _GenerateReport(self, File, Title, Type, BaseAddress, Size=0, FvName=None):
   1465         FileWrite(File, gSubSectionStart)
   1466         FileWrite(File, Title)
   1467         FileWrite(File, "Type:               %s" % Type)
   1468         FileWrite(File, "Base Address:       0x%X" % BaseAddress)
   1469 
   1470         if self.Type == "FV":
   1471             FvTotalSize = 0
   1472             FvTakenSize = 0
   1473             FvFreeSize  = 0
   1474             FvReportFileName = os.path.join(self._FvDir, FvName + ".Fv.txt")
   1475             try:
   1476                 #

   1477                 # Collect size info in the firmware volume.

   1478                 #

   1479                 FvReport = open(FvReportFileName).read()
   1480                 Match = gFvTotalSizePattern.search(FvReport)
   1481                 if Match:
   1482                     FvTotalSize = int(Match.group(1), 16)
   1483                 Match = gFvTakenSizePattern.search(FvReport)
   1484                 if Match:
   1485                     FvTakenSize = int(Match.group(1), 16)
   1486                 FvFreeSize = FvTotalSize - FvTakenSize
   1487                 #

   1488                 # Write size information to the report file.

   1489                 #

   1490                 FileWrite(File, "Size:               0x%X (%.0fK)" % (FvTotalSize, FvTotalSize / 1024.0))
   1491                 FileWrite(File, "Fv Name:            %s (%.1f%% Full)" % (FvName, FvTakenSize * 100.0 / FvTotalSize))
   1492                 FileWrite(File, "Occupied Size:      0x%X (%.0fK)" % (FvTakenSize, FvTakenSize / 1024.0))
   1493                 FileWrite(File, "Free Size:          0x%X (%.0fK)" % (FvFreeSize, FvFreeSize / 1024.0))
   1494                 FileWrite(File, "Offset     Module")
   1495                 FileWrite(File, gSubSectionSep)
   1496                 #

   1497                 # Write module offset and module identification to the report file.

   1498                 #

   1499                 OffsetInfo = {}
   1500                 for Match in gOffsetGuidPattern.finditer(FvReport):
   1501                     Guid = Match.group(2).upper()
   1502                     OffsetInfo[Match.group(1)] = self._GuidsDb.get(Guid, Guid)
   1503                 OffsetList = OffsetInfo.keys()
   1504                 OffsetList.sort()
   1505                 for Offset in OffsetList:
   1506                     FileWrite (File, "%s %s" % (Offset, OffsetInfo[Offset]))
   1507             except IOError:
   1508                 EdkLogger.warn(None, "Fail to read report file", FvReportFileName)
   1509         else:
   1510             FileWrite(File, "Size:               0x%X (%.0fK)" % (Size, Size / 1024.0))
   1511         FileWrite(File, gSubSectionEnd)
   1512 
   1513     ##

   1514     # Generate report for the FD region

   1515     #

   1516     # This function generates report for the FD region.

   1517     #

   1518     # @param self            The object pointer

   1519     # @param File            The file object for report

   1520     #

   1521     def GenerateReport(self, File):
   1522         if (len(self.FvList) > 0):
   1523             for FvItem in self.FvList:
   1524                 Info = self.FvInfo[FvItem]
   1525                 self._GenerateReport(File, Info[0], "FV", Info[1], Info[2], FvItem)
   1526         else:
   1527             self._GenerateReport(File, "FD Region", self.Type, self.BaseAddress, self.Size)
   1528 
   1529 ##

   1530 # Reports FD information

   1531 #

   1532 # This class reports the FD section in the build report file.

   1533 # It collects flash device information for a platform.

   1534 #

   1535 class FdReport(object):
   1536     ##

   1537     # Constructor function for class FdReport

   1538     #

   1539     # This constructor function generates FdReport object for a specified

   1540     # firmware device.

   1541     #

   1542     # @param self            The object pointer

   1543     # @param Fd              The current Firmware device object

   1544     # @param Wa              Workspace context information

   1545     #

   1546     def __init__(self, Fd, Wa):
   1547         self.FdName = Fd.FdUiName
   1548         self.BaseAddress = Fd.BaseAddress
   1549         self.Size = Fd.Size
   1550         self.FdRegionList = [FdRegionReport(FdRegion, Wa) for FdRegion in Fd.RegionList]
   1551         self.FvPath = os.path.join(Wa.BuildDir, "FV")
   1552         self.VpdFilePath = os.path.join(self.FvPath, "%s.map" % Wa.Platform.VpdToolGuid)
   1553         self.VPDBaseAddress = 0
   1554         self.VPDSize = 0
   1555         self.VPDInfoList = []
   1556         for index, FdRegion in enumerate(Fd.RegionList):
   1557             if str(FdRegion.RegionType) is 'FILE' and Wa.Platform.VpdToolGuid in str(FdRegion.RegionDataList):
   1558                 self.VPDBaseAddress = self.FdRegionList[index].BaseAddress
   1559                 self.VPDSize = self.FdRegionList[index].Size
   1560                 break
   1561 
   1562         if os.path.isfile(self.VpdFilePath):
   1563             fd = open(self.VpdFilePath, "r")
   1564             Lines = fd.readlines()
   1565             for Line in Lines:
   1566                 Line = Line.strip()
   1567                 if len(Line) == 0 or Line.startswith("#"):
   1568                     continue
   1569                 try:
   1570                     PcdName, SkuId, Offset, Size, Value = Line.split("#")[0].split("|")
   1571                     PcdName, SkuId, Offset, Size, Value = PcdName.strip(), SkuId.strip(), Offset.strip(), Size.strip(), Value.strip()
   1572                     if Offset.lower().startswith('0x'):
   1573                         Offset = '0x%08X' % (int(Offset, 16) + self.VPDBaseAddress)
   1574                     else:
   1575                         Offset = '0x%08X' % (int(Offset, 10) + self.VPDBaseAddress)
   1576                     self.VPDInfoList.append("%s | %s | %s | %s | %s" % (PcdName, SkuId, Offset, Size, Value))
   1577                 except:
   1578                     EdkLogger.error("BuildReport", CODE_ERROR, "Fail to parse VPD information file %s" % self.VpdFilePath)
   1579             fd.close()
   1580 
   1581     ##

   1582     # Generate report for the firmware device.

   1583     #

   1584     # This function generates report for the firmware device.

   1585     #

   1586     # @param self            The object pointer

   1587     # @param File            The file object for report

   1588     #

   1589     def GenerateReport(self, File):
   1590         FileWrite(File, gSectionStart)
   1591         FileWrite(File, "Firmware Device (FD)")
   1592         FileWrite(File, "FD Name:            %s" % self.FdName)
   1593         FileWrite(File, "Base Address:       %s" % self.BaseAddress)
   1594         FileWrite(File, "Size:               0x%X (%.0fK)" % (self.Size, self.Size / 1024.0))
   1595         if len(self.FdRegionList) > 0:
   1596             FileWrite(File, gSectionSep)
   1597             for FdRegionItem in self.FdRegionList:
   1598                 FdRegionItem.GenerateReport(File)
   1599 
   1600         if len(self.VPDInfoList) > 0:
   1601             FileWrite(File, gSubSectionStart)
   1602             FileWrite(File, "FD VPD Region")
   1603             FileWrite(File, "Base Address:       0x%X" % self.VPDBaseAddress)
   1604             FileWrite(File, "Size:               0x%X (%.0fK)" % (self.VPDSize, self.VPDSize / 1024.0))
   1605             FileWrite(File, gSubSectionSep)
   1606             for item in self.VPDInfoList:
   1607                 FileWrite(File, item)
   1608             FileWrite(File, gSubSectionEnd)
   1609         FileWrite(File, gSectionEnd)
   1610 
   1611 
   1612 
   1613 ##

   1614 # Reports platform information

   1615 #

   1616 # This class reports the whole platform information

   1617 #

   1618 class PlatformReport(object):
   1619     ##

   1620     # Constructor function for class PlatformReport

   1621     #

   1622     # This constructor function generates PlatformReport object a platform build.

   1623     # It generates report for platform summary, flash, global PCDs and detailed

   1624     # module information for modules involved in platform build.

   1625     #

   1626     # @param self            The object pointer

   1627     # @param Wa              Workspace context information

   1628     # @param MaList          The list of modules in the platform build

   1629     #

   1630     def __init__(self, Wa, MaList, ReportType):
   1631         self._WorkspaceDir = Wa.WorkspaceDir
   1632         self.PlatformName = Wa.Name
   1633         self.PlatformDscPath = Wa.Platform
   1634         self.Architectures = " ".join(Wa.ArchList)
   1635         self.ToolChain = Wa.ToolChain
   1636         self.Target = Wa.BuildTarget
   1637         self.OutputPath = os.path.join(Wa.WorkspaceDir, Wa.OutputDir)
   1638         self.BuildEnvironment = platform.platform()
   1639 
   1640         self.PcdReport = None
   1641         if "PCD" in ReportType:
   1642             self.PcdReport = PcdReport(Wa)
   1643 
   1644         self.FdReportList = []
   1645         if "FLASH" in ReportType and Wa.FdfProfile and MaList == None:
   1646             for Fd in Wa.FdfProfile.FdDict:
   1647                 self.FdReportList.append(FdReport(Wa.FdfProfile.FdDict[Fd], Wa))
   1648 
   1649         self.PredictionReport = None
   1650         if "FIXED_ADDRESS" in ReportType or "EXECUTION_ORDER" in ReportType:
   1651             self.PredictionReport = PredictionReport(Wa)
   1652 
   1653         self.DepexParser = None
   1654         if "DEPEX" in ReportType:
   1655             self.DepexParser = DepexParser(Wa)
   1656             
   1657         self.ModuleReportList = []
   1658         if MaList != None:
   1659             self._IsModuleBuild = True
   1660             for Ma in MaList:
   1661                 self.ModuleReportList.append(ModuleReport(Ma, ReportType))
   1662         else:
   1663             self._IsModuleBuild = False
   1664             for Pa in Wa.AutoGenObjectList:
   1665                 ModuleAutoGenList = []
   1666                 for ModuleKey in Pa.Platform.Modules:
   1667                     ModuleAutoGenList.append(Pa.Platform.Modules[ModuleKey].M)
   1668                 if GlobalData.gFdfParser != None:
   1669                     if Pa.Arch in GlobalData.gFdfParser.Profile.InfDict:
   1670                         INFList = GlobalData.gFdfParser.Profile.InfDict[Pa.Arch]
   1671                         for InfName in INFList:
   1672                             InfClass = PathClass(NormPath(InfName), Wa.WorkspaceDir, Pa.Arch)
   1673                             Ma = ModuleAutoGen(Wa, InfClass, Pa.BuildTarget, Pa.ToolChain, Pa.Arch, Wa.MetaFile)
   1674                             if Ma == None:
   1675                                 continue
   1676                             if Ma not in ModuleAutoGenList:
   1677                                 ModuleAutoGenList.append(Ma)
   1678                 for MGen in ModuleAutoGenList:
   1679                     self.ModuleReportList.append(ModuleReport(MGen, ReportType))
   1680 
   1681 
   1682 
   1683     ##

   1684     # Generate report for the whole platform.

   1685     #

   1686     # This function generates report for platform information.

   1687     # It comprises of platform summary, global PCD, flash and

   1688     # module list sections.

   1689     #

   1690     # @param self            The object pointer

   1691     # @param File            The file object for report

   1692     # @param BuildDuration   The total time to build the modules

   1693     # @param ReportType      The kind of report items in the final report file

   1694     #

   1695     def GenerateReport(self, File, BuildDuration, ReportType):
   1696         FileWrite(File, "Platform Summary")
   1697         FileWrite(File, "Platform Name:        %s" % self.PlatformName)
   1698         FileWrite(File, "Platform DSC Path:    %s" % self.PlatformDscPath)
   1699         FileWrite(File, "Architectures:        %s" % self.Architectures)
   1700         FileWrite(File, "Tool Chain:           %s" % self.ToolChain)
   1701         FileWrite(File, "Target:               %s" % self.Target)
   1702         FileWrite(File, "Output Path:          %s" % self.OutputPath)
   1703         FileWrite(File, "Build Environment:    %s" % self.BuildEnvironment)
   1704         FileWrite(File, "Build Duration:       %s" % BuildDuration)
   1705         FileWrite(File, "Report Content:       %s" % ", ".join(ReportType))
   1706 
   1707         if GlobalData.MixedPcd:
   1708             FileWrite(File, gSectionStart)
   1709             FileWrite(File, "The following PCDs use different access methods:")
   1710             FileWrite(File, gSectionSep)
   1711             for PcdItem in GlobalData.MixedPcd:
   1712                 FileWrite(File, "%s.%s" % (str(PcdItem[1]), str(PcdItem[0])))
   1713             FileWrite(File, gSectionEnd)
   1714 
   1715         if not self._IsModuleBuild:
   1716             if "PCD" in ReportType:
   1717                 self.PcdReport.GenerateReport(File, None)
   1718     
   1719             if "FLASH" in ReportType:
   1720                 for FdReportListItem in self.FdReportList:
   1721                     FdReportListItem.GenerateReport(File)
   1722 
   1723         for ModuleReportItem in self.ModuleReportList:
   1724             ModuleReportItem.GenerateReport(File, self.PcdReport, self.PredictionReport, self.DepexParser, ReportType)
   1725 
   1726         if not self._IsModuleBuild:
   1727             if "EXECUTION_ORDER" in ReportType:
   1728                 self.PredictionReport.GenerateReport(File, None)
   1729 
   1730 ## BuildReport class

   1731 #

   1732 #  This base class contain the routines to collect data and then

   1733 #  applies certain format to the output report

   1734 #

   1735 class BuildReport(object):
   1736     ##

   1737     # Constructor function for class BuildReport

   1738     #

   1739     # This constructor function generates BuildReport object a platform build.

   1740     # It generates report for platform summary, flash, global PCDs and detailed

   1741     # module information for modules involved in platform build.

   1742     #

   1743     # @param self            The object pointer

   1744     # @param ReportFile      The file name to save report file

   1745     # @param ReportType      The kind of report items in the final report file

   1746     #

   1747     def __init__(self, ReportFile, ReportType):
   1748         self.ReportFile = ReportFile
   1749         if ReportFile:
   1750             self.ReportList = []
   1751             self.ReportType = []
   1752             if ReportType: 
   1753                 for ReportTypeItem in ReportType:
   1754                     if ReportTypeItem not in self.ReportType:
   1755                         self.ReportType.append(ReportTypeItem)
   1756             else:
   1757                 self.ReportType = ["PCD", "LIBRARY", "BUILD_FLAGS", "DEPEX", "HASH", "FLASH", "FIXED_ADDRESS"]
   1758     ##

   1759     # Adds platform report to the list

   1760     #

   1761     # This function adds a platform report to the final report list.

   1762     #

   1763     # @param self            The object pointer

   1764     # @param Wa              Workspace context information

   1765     # @param MaList          The list of modules in the platform build

   1766     #

   1767     def AddPlatformReport(self, Wa, MaList=None):
   1768         if self.ReportFile:
   1769             self.ReportList.append((Wa, MaList))
   1770 
   1771     ##

   1772     # Generates the final report.

   1773     #

   1774     # This function generates platform build report. It invokes GenerateReport()

   1775     # method for every platform report in the list.

   1776     #

   1777     # @param self            The object pointer

   1778     # @param BuildDuration   The total time to build the modules

   1779     #

   1780     def GenerateReport(self, BuildDuration):
   1781         if self.ReportFile:
   1782             try:
   1783                 File = StringIO('')
   1784                 for (Wa, MaList) in self.ReportList:
   1785                     PlatformReport(Wa, MaList, self.ReportType).GenerateReport(File, BuildDuration, self.ReportType)
   1786                 Content = FileLinesSplit(File.getvalue(), gLineMaxLength)
   1787                 SaveFileOnChange(self.ReportFile, Content, True)
   1788                 EdkLogger.quiet("Build report can be found at %s" % os.path.abspath(self.ReportFile))
   1789             except IOError:
   1790                 EdkLogger.error(None, FILE_WRITE_FAILURE, ExtraData=self.ReportFile)
   1791             except:
   1792                 EdkLogger.error("BuildReport", CODE_ERROR, "Unknown fatal error when generating build report", ExtraData=self.ReportFile, RaiseError=False)
   1793                 EdkLogger.quiet("(Python %s on %s\n%s)" % (platform.python_version(), sys.platform, traceback.format_exc()))
   1794             File.close()
   1795             
   1796 # This acts like the main() function for the script, unless it is 'import'ed into another script.

   1797 if __name__ == '__main__':
   1798     pass
   1799 
   1800