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 - 2018, Intel Corporation. All rights reserved.<BR>
8# SPDX-License-Identifier: BSD-2-Clause-Patent
9#
10
11## Import Modules
12#
13import Common.LongFilePathOs as os
14import re
15import platform
16import textwrap
17import traceback
18import sys
19import time
20import struct
21import hashlib
22import subprocess
23import threading
24from datetime import datetime
25from io import BytesIO
26from Common import EdkLogger
27from Common.Misc import SaveFileOnChange
28from Common.Misc import GuidStructureByteArrayToGuidString
29from Common.Misc import GuidStructureStringToGuidString
30from Common.BuildToolError import FILE_WRITE_FAILURE
31from Common.BuildToolError import CODE_ERROR
32from Common.BuildToolError import COMMAND_FAILURE
33from Common.BuildToolError import FORMAT_INVALID
34from Common.LongFilePathSupport import OpenLongFilePath as open
35from Common.MultipleWorkspace import MultipleWorkspace as mws
36import Common.GlobalData as GlobalData
37from AutoGen.AutoGen import ModuleAutoGen
38from Common.Misc import PathClass
39from Common.StringUtils import NormPath
40from Common.DataType import *
41import collections
42from Common.Expression import *
43from GenFds.AprioriSection import DXE_APRIORI_GUID, PEI_APRIORI_GUID
44
45## Pattern to extract contents in EDK DXS files
46gDxsDependencyPattern = re.compile(r"DEPENDENCY_START(.+)DEPENDENCY_END", re.DOTALL)
47
48## Pattern to find total FV total size, occupied size in flash report intermediate file
49gFvTotalSizePattern = re.compile(r"EFI_FV_TOTAL_SIZE = (0x[0-9a-fA-F]+)")
50gFvTakenSizePattern = re.compile(r"EFI_FV_TAKEN_SIZE = (0x[0-9a-fA-F]+)")
51
52## Pattern to find module size and time stamp in module summary report intermediate file
53gModuleSizePattern = re.compile(r"MODULE_SIZE = (\d+)")
54gTimeStampPattern  = re.compile(r"TIME_STAMP = (\d+)")
55
56## Pattern to find GUID value in flash description files
57gPcdGuidPattern = re.compile(r"PCD\((\w+)[.](\w+)\)")
58
59## Pattern to collect offset, GUID value pair in the flash report intermediate file
60gOffsetGuidPattern = re.compile(r"(0x[0-9A-Fa-f]+) ([-A-Fa-f0-9]+)")
61
62## Pattern to find module base address and entry point in fixed flash map file
63gModulePattern = r"\n[-\w]+\s*\(([^,]+),\s*BaseAddress=%(Address)s,\s*EntryPoint=%(Address)s\)\s*\(GUID=([-0-9A-Fa-f]+)[^)]*\)"
64gMapFileItemPattern = re.compile(gModulePattern % {"Address" : "(-?0[xX][0-9A-Fa-f]+)"})
65
66## Pattern to find all module referenced header files in source files
67gIncludePattern  = re.compile(r'#include\s*["<]([^">]+)[">]')
68gIncludePattern2 = re.compile(r"#include\s+EFI_([A-Z_]+)\s*[(]\s*(\w+)\s*[)]")
69
70## Pattern to find the entry point for EDK module using EDKII Glue library
71gGlueLibEntryPoint = re.compile(r"__EDKII_GLUE_MODULE_ENTRY_POINT__\s*=\s*(\w+)")
72
73## Tags for MaxLength of line in report
74gLineMaxLength = 120
75
76## Tags for end of line in report
77gEndOfLine = "\r\n"
78
79## Tags for section start, end and separator
80gSectionStart = ">" + "=" * (gLineMaxLength - 2) + "<"
81gSectionEnd = "<" + "=" * (gLineMaxLength - 2) + ">" + "\n"
82gSectionSep = "=" * gLineMaxLength
83
84## Tags for subsection start, end and separator
85gSubSectionStart = ">" + "-" * (gLineMaxLength - 2) + "<"
86gSubSectionEnd = "<" + "-" * (gLineMaxLength - 2) + ">"
87gSubSectionSep = "-" * gLineMaxLength
88
89
90## The look up table to map PCD type to pair of report display type and DEC type
91gPcdTypeMap = {
92  TAB_PCDS_FIXED_AT_BUILD     : ('FIXED',  TAB_PCDS_FIXED_AT_BUILD),
93  TAB_PCDS_PATCHABLE_IN_MODULE: ('PATCH',  TAB_PCDS_PATCHABLE_IN_MODULE),
94  TAB_PCDS_FEATURE_FLAG       : ('FLAG',   TAB_PCDS_FEATURE_FLAG),
95  TAB_PCDS_DYNAMIC            : ('DYN',    TAB_PCDS_DYNAMIC),
96  TAB_PCDS_DYNAMIC_HII        : ('DYNHII', TAB_PCDS_DYNAMIC),
97  TAB_PCDS_DYNAMIC_VPD        : ('DYNVPD', TAB_PCDS_DYNAMIC),
98  TAB_PCDS_DYNAMIC_EX         : ('DEX',    TAB_PCDS_DYNAMIC_EX),
99  TAB_PCDS_DYNAMIC_EX_HII     : ('DEXHII', TAB_PCDS_DYNAMIC_EX),
100  TAB_PCDS_DYNAMIC_EX_VPD     : ('DEXVPD', TAB_PCDS_DYNAMIC_EX),
101  }
102
103## The look up table to map module type to driver type
104gDriverTypeMap = {
105  SUP_MODULE_SEC               : '0x3 (SECURITY_CORE)',
106  SUP_MODULE_PEI_CORE          : '0x4 (PEI_CORE)',
107  SUP_MODULE_PEIM              : '0x6 (PEIM)',
108  SUP_MODULE_DXE_CORE          : '0x5 (DXE_CORE)',
109  SUP_MODULE_DXE_DRIVER        : '0x7 (DRIVER)',
110  SUP_MODULE_DXE_SAL_DRIVER    : '0x7 (DRIVER)',
111  SUP_MODULE_DXE_SMM_DRIVER    : '0x7 (DRIVER)',
112  SUP_MODULE_DXE_RUNTIME_DRIVER: '0x7 (DRIVER)',
113  SUP_MODULE_UEFI_DRIVER       : '0x7 (DRIVER)',
114  SUP_MODULE_UEFI_APPLICATION  : '0x9 (APPLICATION)',
115  SUP_MODULE_SMM_CORE          : '0xD (SMM_CORE)',
116  'SMM_DRIVER'        : '0xA (SMM)', # Extension of module type to support PI 1.1 SMM drivers
117  SUP_MODULE_MM_STANDALONE     : '0xE (MM_STANDALONE)',
118  SUP_MODULE_MM_CORE_STANDALONE : '0xF (MM_CORE_STANDALONE)'
119  }
120
121## The look up table of the supported opcode in the dependency expression binaries
122gOpCodeList = ["BEFORE", "AFTER", "PUSH", "AND", "OR", "NOT", "TRUE", "FALSE", "END", "SOR"]
123
124## Save VPD Pcd
125VPDPcdList = []
126
127##
128# Writes a string to the file object.
129#
130# This function writes a string to the file object and a new line is appended
131# afterwards. It may optionally wraps the string for better readability.
132#
133# @File                      The file object to write
134# @String                    The string to be written to the file
135# @Wrapper                   Indicates whether to wrap the string
136#
137def FileWrite(File, String, Wrapper=False):
138    if Wrapper:
139        String = textwrap.fill(String, 120)
140    File.append(String + gEndOfLine)
141
142def ByteArrayForamt(Value):
143    IsByteArray = False
144    SplitNum = 16
145    ArrayList = []
146    if Value.startswith('{') and Value.endswith('}') and not Value.startswith("{CODE("):
147        Value = Value[1:-1]
148        ValueList = Value.split(',')
149        if len(ValueList) >= SplitNum:
150            IsByteArray = True
151    if IsByteArray:
152        if ValueList:
153            Len = len(ValueList)/SplitNum
154            for i, element in enumerate(ValueList):
155                ValueList[i] = '0x%02X' % int(element.strip(), 16)
156        if Len:
157            Id = 0
158            while (Id <= Len):
159                End = min(SplitNum*(Id+1), len(ValueList))
160                Str = ','.join(ValueList[SplitNum*Id : End])
161                if End == len(ValueList):
162                    Str += '}'
163                    ArrayList.append(Str)
164                    break
165                else:
166                    Str += ','
167                    ArrayList.append(Str)
168                Id += 1
169        else:
170            ArrayList = [Value + '}']
171    return IsByteArray, ArrayList
172
173##
174# Find all the header file that the module source directly includes.
175#
176# This function scans source code to find all header files the module may
177# include. This is not accurate but very effective to find all the header
178# file the module might include with #include statement.
179#
180# @Source                    The source file name
181# @IncludePathList           The list of include path to find the source file.
182# @IncludeFiles              The dictionary of current found include files.
183#
184def FindIncludeFiles(Source, IncludePathList, IncludeFiles):
185    FileContents = open(Source).read()
186    #
187    # Find header files with pattern #include "XXX.h" or #include <XXX.h>
188    #
189    for Match in gIncludePattern.finditer(FileContents):
190        FileName = Match.group(1).strip()
191        for Dir in [os.path.dirname(Source)] + IncludePathList:
192            FullFileName = os.path.normpath(os.path.join(Dir, FileName))
193            if os.path.exists(FullFileName):
194                IncludeFiles[FullFileName.lower().replace("\\", "/")] = FullFileName
195                break
196
197    #
198    # Find header files with pattern like #include EFI_PPI_CONSUMER(XXX)
199    #
200    for Match in gIncludePattern2.finditer(FileContents):
201        Key = Match.group(2)
202        Type = Match.group(1)
203        if "ARCH_PROTOCOL" in Type:
204            FileName = "ArchProtocol/%(Key)s/%(Key)s.h" % {"Key" : Key}
205        elif "PROTOCOL" in Type:
206            FileName = "Protocol/%(Key)s/%(Key)s.h" % {"Key" : Key}
207        elif "PPI" in Type:
208            FileName = "Ppi/%(Key)s/%(Key)s.h" % {"Key" : Key}
209        elif TAB_GUID in Type:
210            FileName = "Guid/%(Key)s/%(Key)s.h" % {"Key" : Key}
211        else:
212            continue
213        for Dir in IncludePathList:
214            FullFileName = os.path.normpath(os.path.join(Dir, FileName))
215            if os.path.exists(FullFileName):
216                IncludeFiles[FullFileName.lower().replace("\\", "/")] = FullFileName
217                break
218
219## Split each lines in file
220#
221#  This method is used to split the lines in file to make the length of each line
222#  less than MaxLength.
223#
224#  @param      Content           The content of file
225#  @param      MaxLength         The Max Length of the line
226#
227def FileLinesSplit(Content=None, MaxLength=None):
228    ContentList = Content.split(TAB_LINE_BREAK)
229    NewContent = ''
230    NewContentList = []
231    for Line in ContentList:
232        while len(Line.rstrip()) > MaxLength:
233            LineSpaceIndex = Line.rfind(TAB_SPACE_SPLIT, 0, MaxLength)
234            LineSlashIndex = Line.rfind(TAB_SLASH, 0, MaxLength)
235            LineBackSlashIndex = Line.rfind(TAB_BACK_SLASH, 0, MaxLength)
236            if max(LineSpaceIndex, LineSlashIndex, LineBackSlashIndex) > 0:
237                LineBreakIndex = max(LineSpaceIndex, LineSlashIndex, LineBackSlashIndex)
238            else:
239                LineBreakIndex = MaxLength
240            NewContentList.append(Line[:LineBreakIndex])
241            Line = Line[LineBreakIndex:]
242        if Line:
243            NewContentList.append(Line)
244    for NewLine in NewContentList:
245        NewContent += NewLine + TAB_LINE_BREAK
246
247    NewContent = NewContent.replace(gEndOfLine, TAB_LINE_BREAK).replace('\r\r\n', gEndOfLine)
248    return NewContent
249
250
251
252##
253# Parse binary dependency expression section
254#
255# This utility class parses the dependency expression section and translate the readable
256# GUID name and value.
257#
258class DepexParser(object):
259    ##
260    # Constructor function for class DepexParser
261    #
262    # This constructor function collect GUID values so that the readable
263    # GUID name can be translated.
264    #
265    # @param self            The object pointer
266    # @param Wa              Workspace context information
267    #
268    def __init__(self, Wa):
269        self._GuidDb = {}
270        for Pa in Wa.AutoGenObjectList:
271            for Package in Pa.PackageList:
272                for Protocol in Package.Protocols:
273                    GuidValue = GuidStructureStringToGuidString(Package.Protocols[Protocol])
274                    self._GuidDb[GuidValue.upper()] = Protocol
275                for Ppi in Package.Ppis:
276                    GuidValue = GuidStructureStringToGuidString(Package.Ppis[Ppi])
277                    self._GuidDb[GuidValue.upper()] = Ppi
278                for Guid in Package.Guids:
279                    GuidValue = GuidStructureStringToGuidString(Package.Guids[Guid])
280                    self._GuidDb[GuidValue.upper()] = Guid
281            for Ma in Pa.ModuleAutoGenList:
282                for Pcd in Ma.FixedVoidTypePcds:
283                    PcdValue = Ma.FixedVoidTypePcds[Pcd]
284                    if len(PcdValue.split(',')) == 16:
285                        GuidValue = GuidStructureByteArrayToGuidString(PcdValue)
286                        self._GuidDb[GuidValue.upper()] = Pcd
287    ##
288    # Parse the binary dependency expression files.
289    #
290    # This function parses the binary dependency expression file and translate it
291    # to the instruction list.
292    #
293    # @param self            The object pointer
294    # @param DepexFileName   The file name of binary dependency expression file.
295    #
296    def ParseDepexFile(self, DepexFileName):
297        DepexFile = open(DepexFileName, "rb")
298        DepexStatement = []
299        OpCode = DepexFile.read(1)
300        while OpCode:
301            Statement = gOpCodeList[struct.unpack("B", OpCode)[0]]
302            if Statement in ["BEFORE", "AFTER", "PUSH"]:
303                GuidValue = "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X" % \
304                            struct.unpack(PACK_PATTERN_GUID, DepexFile.read(16))
305                GuidString = self._GuidDb.get(GuidValue, GuidValue)
306                Statement = "%s %s" % (Statement, GuidString)
307            DepexStatement.append(Statement)
308            OpCode = DepexFile.read(1)
309
310        return DepexStatement
311
312##
313# Reports library information
314#
315# This class reports the module library subsection in the build report file.
316#
317class LibraryReport(object):
318    ##
319    # Constructor function for class LibraryReport
320    #
321    # This constructor function generates LibraryReport object for
322    # a module.
323    #
324    # @param self            The object pointer
325    # @param M               Module context information
326    #
327    def __init__(self, M):
328        self.LibraryList = []
329
330        for Lib in M.DependentLibraryList:
331            LibInfPath = str(Lib)
332            LibClassList = Lib.LibraryClass[0].LibraryClass
333            LibConstructorList = Lib.ConstructorList
334            LibDesstructorList = Lib.DestructorList
335            LibDepexList = Lib.DepexExpression[M.Arch, M.ModuleType]
336            for LibAutoGen in M.LibraryAutoGenList:
337                if LibInfPath == LibAutoGen.MetaFile.Path:
338                    LibTime = LibAutoGen.BuildTime
339                    break
340            self.LibraryList.append((LibInfPath, LibClassList, LibConstructorList, LibDesstructorList, LibDepexList, LibTime))
341
342    ##
343    # Generate report for module library information
344    #
345    # This function generates report for the module library.
346    # If the module is EDKII style one, the additional library class, library
347    # constructor/destructor and dependency expression may also be reported.
348    #
349    # @param self            The object pointer
350    # @param File            The file object for report
351    #
352    def GenerateReport(self, File):
353        if len(self.LibraryList) > 0:
354            FileWrite(File, gSubSectionStart)
355            FileWrite(File, TAB_BRG_LIBRARY)
356            FileWrite(File, gSubSectionSep)
357            for LibraryItem in self.LibraryList:
358                LibInfPath = LibraryItem[0]
359                FileWrite(File, LibInfPath)
360
361                LibClass = LibraryItem[1]
362                EdkIILibInfo = ""
363                LibConstructor = " ".join(LibraryItem[2])
364                if LibConstructor:
365                    EdkIILibInfo += " C = " + LibConstructor
366                LibDestructor = " ".join(LibraryItem[3])
367                if LibDestructor:
368                    EdkIILibInfo += " D = " + LibDestructor
369                LibDepex = " ".join(LibraryItem[4])
370                if LibDepex:
371                    EdkIILibInfo += " Depex = " + LibDepex
372                if LibraryItem[5]:
373                    EdkIILibInfo += " Time = " + LibraryItem[5]
374                if EdkIILibInfo:
375                    FileWrite(File, "{%s: %s}" % (LibClass, EdkIILibInfo))
376                else:
377                    FileWrite(File, "{%s}" % LibClass)
378
379            FileWrite(File, gSubSectionEnd)
380
381##
382# Reports dependency expression information
383#
384# This class reports the module dependency expression subsection in the build report file.
385#
386class DepexReport(object):
387    ##
388    # Constructor function for class DepexReport
389    #
390    # This constructor function generates DepexReport object for
391    # a module. If the module source contains the DXS file (usually EDK
392    # style module), it uses the dependency in DXS file; otherwise,
393    # it uses the dependency expression from its own INF [Depex] section
394    # and then merges with the ones from its dependent library INF.
395    #
396    # @param self            The object pointer
397    # @param M               Module context information
398    #
399    def __init__(self, M):
400        self.Depex = ""
401        self._DepexFileName = os.path.join(M.BuildDir, "OUTPUT", M.Module.BaseName + ".depex")
402        ModuleType = M.ModuleType
403        if not ModuleType:
404            ModuleType = COMPONENT_TO_MODULE_MAP_DICT.get(M.ComponentType, "")
405
406        if ModuleType in [SUP_MODULE_SEC, SUP_MODULE_PEI_CORE, SUP_MODULE_DXE_CORE, SUP_MODULE_SMM_CORE, SUP_MODULE_MM_CORE_STANDALONE, SUP_MODULE_UEFI_APPLICATION]:
407            return
408
409        for Source in M.SourceFileList:
410            if os.path.splitext(Source.Path)[1].lower() == ".dxs":
411                Match = gDxsDependencyPattern.search(open(Source.Path).read())
412                if Match:
413                    self.Depex = Match.group(1).strip()
414                    self.Source = "DXS"
415                    break
416        else:
417            self.Depex = M.DepexExpressionDict.get(M.ModuleType, "")
418            self.ModuleDepex = " ".join(M.Module.DepexExpression[M.Arch, M.ModuleType])
419            if not self.ModuleDepex:
420                self.ModuleDepex = "(None)"
421
422            LibDepexList = []
423            for Lib in M.DependentLibraryList:
424                LibDepex = " ".join(Lib.DepexExpression[M.Arch, M.ModuleType]).strip()
425                if LibDepex != "":
426                    LibDepexList.append("(" + LibDepex + ")")
427            self.LibraryDepex = " AND ".join(LibDepexList)
428            if not self.LibraryDepex:
429                self.LibraryDepex = "(None)"
430            self.Source = "INF"
431
432    ##
433    # Generate report for module dependency expression information
434    #
435    # This function generates report for the module dependency expression.
436    #
437    # @param self              The object pointer
438    # @param File              The file object for report
439    # @param GlobalDepexParser The platform global Dependency expression parser object
440    #
441    def GenerateReport(self, File, GlobalDepexParser):
442        if not self.Depex:
443            return
444        FileWrite(File, gSubSectionStart)
445        if os.path.isfile(self._DepexFileName):
446            try:
447                DepexStatements = GlobalDepexParser.ParseDepexFile(self._DepexFileName)
448                FileWrite(File, "Final Dependency Expression (DEPEX) Instructions")
449                for DepexStatement in DepexStatements:
450                    FileWrite(File, "  %s" % DepexStatement)
451                FileWrite(File, gSubSectionSep)
452            except:
453                EdkLogger.warn(None, "Dependency expression file is corrupted", self._DepexFileName)
454
455        FileWrite(File, "Dependency Expression (DEPEX) from %s" % self.Source)
456
457        if self.Source == "INF":
458            FileWrite(File, self.Depex, True)
459            FileWrite(File, gSubSectionSep)
460            FileWrite(File, "From Module INF:  %s" % self.ModuleDepex, True)
461            FileWrite(File, "From Library INF: %s" % self.LibraryDepex, True)
462        else:
463            FileWrite(File, self.Depex)
464        FileWrite(File, gSubSectionEnd)
465
466##
467# Reports dependency expression information
468#
469# This class reports the module build flags subsection in the build report file.
470#
471class BuildFlagsReport(object):
472    ##
473    # Constructor function for class BuildFlagsReport
474    #
475    # This constructor function generates BuildFlagsReport object for
476    # a module. It reports the build tool chain tag and all relevant
477    # build flags to build the module.
478    #
479    # @param self            The object pointer
480    # @param M               Module context information
481    #
482    def __init__(self, M):
483        BuildOptions = {}
484        #
485        # Add build flags according to source file extension so that
486        # irrelevant ones can be filtered out.
487        #
488        for Source in M.SourceFileList:
489            Ext = os.path.splitext(Source.File)[1].lower()
490            if Ext in [".c", ".cc", ".cpp"]:
491                BuildOptions["CC"] = 1
492            elif Ext in [".s", ".asm"]:
493                BuildOptions["PP"] = 1
494                BuildOptions["ASM"] = 1
495            elif Ext in [".vfr"]:
496                BuildOptions["VFRPP"] = 1
497                BuildOptions["VFR"] = 1
498            elif Ext in [".dxs"]:
499                BuildOptions["APP"] = 1
500                BuildOptions["CC"] = 1
501            elif Ext in [".asl"]:
502                BuildOptions["ASLPP"] = 1
503                BuildOptions["ASL"] = 1
504            elif Ext in [".aslc"]:
505                BuildOptions["ASLCC"] = 1
506                BuildOptions["ASLDLINK"] = 1
507                BuildOptions["CC"] = 1
508            elif Ext in [".asm16"]:
509                BuildOptions["ASMLINK"] = 1
510            BuildOptions["SLINK"] = 1
511            BuildOptions["DLINK"] = 1
512
513        #
514        # Save module build flags.
515        #
516        self.ToolChainTag = M.ToolChain
517        self.BuildFlags = {}
518        for Tool in BuildOptions:
519            self.BuildFlags[Tool + "_FLAGS"] = M.BuildOption.get(Tool, {}).get("FLAGS", "")
520
521    ##
522    # Generate report for module build flags information
523    #
524    # This function generates report for the module build flags expression.
525    #
526    # @param self            The object pointer
527    # @param File            The file object for report
528    #
529    def GenerateReport(self, File):
530        FileWrite(File, gSubSectionStart)
531        FileWrite(File, "Build Flags")
532        FileWrite(File, "Tool Chain Tag: %s" % self.ToolChainTag)
533        for Tool in self.BuildFlags:
534            FileWrite(File, gSubSectionSep)
535            FileWrite(File, "%s = %s" % (Tool, self.BuildFlags[Tool]), True)
536
537        FileWrite(File, gSubSectionEnd)
538
539
540##
541# Reports individual module information
542#
543# This class reports the module section in the build report file.
544# It comprises of module summary, module PCD, library, dependency expression,
545# build flags sections.
546#
547class ModuleReport(object):
548    ##
549    # Constructor function for class ModuleReport
550    #
551    # This constructor function generates ModuleReport object for
552    # a separate module in a platform build.
553    #
554    # @param self            The object pointer
555    # @param M               Module context information
556    # @param ReportType      The kind of report items in the final report file
557    #
558    def __init__(self, M, ReportType):
559        self.ModuleName = M.Module.BaseName
560        self.ModuleInfPath = M.MetaFile.File
561        self.FileGuid = M.Guid
562        self.Size = 0
563        self.BuildTimeStamp = None
564        self.Hash = 0
565        self.DriverType = ""
566        if not M.IsLibrary:
567            ModuleType = M.ModuleType
568            if not ModuleType:
569                ModuleType = COMPONENT_TO_MODULE_MAP_DICT.get(M.ComponentType, "")
570            #
571            # If a module complies to PI 1.1, promote Module type to "SMM_DRIVER"
572            #
573            if ModuleType == SUP_MODULE_DXE_SMM_DRIVER:
574                PiSpec = M.Module.Specification.get("PI_SPECIFICATION_VERSION", "0x00010000")
575                if int(PiSpec, 0) >= 0x0001000A:
576                    ModuleType = "SMM_DRIVER"
577            self.DriverType = gDriverTypeMap.get(ModuleType, "0x2 (FREE_FORM)")
578        self.UefiSpecVersion = M.Module.Specification.get("UEFI_SPECIFICATION_VERSION", "")
579        self.PiSpecVersion = M.Module.Specification.get("PI_SPECIFICATION_VERSION", "")
580        self.PciDeviceId = M.Module.Defines.get("PCI_DEVICE_ID", "")
581        self.PciVendorId = M.Module.Defines.get("PCI_VENDOR_ID", "")
582        self.PciClassCode = M.Module.Defines.get("PCI_CLASS_CODE", "")
583        self.BuildTime = M.BuildTime
584
585        self._BuildDir = M.BuildDir
586        self.ModulePcdSet = {}
587        if "PCD" in ReportType:
588            #
589            # Collect all module used PCD set: module INF referenced directly or indirectly.
590            # It also saves module INF default values of them in case they exist.
591            #
592            for Pcd in M.ModulePcdList + M.LibraryPcdList:
593                self.ModulePcdSet.setdefault((Pcd.TokenCName, Pcd.TokenSpaceGuidCName, Pcd.Type), (Pcd.InfDefaultValue, Pcd.DefaultValue))
594
595        self.LibraryReport = None
596        if "LIBRARY" in ReportType:
597            self.LibraryReport = LibraryReport(M)
598
599        self.DepexReport = None
600        if "DEPEX" in ReportType:
601            self.DepexReport = DepexReport(M)
602
603        if "BUILD_FLAGS" in ReportType:
604            self.BuildFlagsReport = BuildFlagsReport(M)
605
606
607    ##
608    # Generate report for module information
609    #
610    # This function generates report for separate module expression
611    # in a platform build.
612    #
613    # @param self                   The object pointer
614    # @param File                   The file object for report
615    # @param GlobalPcdReport        The platform global PCD report object
616    # @param GlobalPredictionReport The platform global Prediction report object
617    # @param GlobalDepexParser      The platform global Dependency expression parser object
618    # @param ReportType             The kind of report items in the final report file
619    #
620    def GenerateReport(self, File, GlobalPcdReport, GlobalPredictionReport, GlobalDepexParser, ReportType):
621        FileWrite(File, gSectionStart)
622
623        FwReportFileName = os.path.join(self._BuildDir, "OUTPUT", self.ModuleName + ".txt")
624        if os.path.isfile(FwReportFileName):
625            try:
626                FileContents = open(FwReportFileName).read()
627                Match = gModuleSizePattern.search(FileContents)
628                if Match:
629                    self.Size = int(Match.group(1))
630
631                Match = gTimeStampPattern.search(FileContents)
632                if Match:
633                    self.BuildTimeStamp = datetime.utcfromtimestamp(int(Match.group(1)))
634            except IOError:
635                EdkLogger.warn(None, "Fail to read report file", FwReportFileName)
636
637        if "HASH" in ReportType:
638            OutputDir = os.path.join(self._BuildDir, "OUTPUT")
639            DefaultEFIfile = os.path.join(OutputDir, self.ModuleName + ".efi")
640            if os.path.isfile(DefaultEFIfile):
641                Tempfile = os.path.join(OutputDir, self.ModuleName + "_hash.tmp")
642                # rebase the efi image since its base address may not zero
643                cmd = ["GenFw", "--rebase", str(0), "-o", Tempfile, DefaultEFIfile]
644                try:
645                    PopenObject = subprocess.Popen(' '.join(cmd), stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
646                except Exception as X:
647                    EdkLogger.error("GenFw", COMMAND_FAILURE, ExtraData="%s: %s" % (str(X), cmd[0]))
648                EndOfProcedure = threading.Event()
649                EndOfProcedure.clear()
650                if PopenObject.stderr:
651                    StdErrThread = threading.Thread(target=ReadMessage, args=(PopenObject.stderr, EdkLogger.quiet, EndOfProcedure))
652                    StdErrThread.setName("STDERR-Redirector")
653                    StdErrThread.setDaemon(False)
654                    StdErrThread.start()
655                # waiting for program exit
656                PopenObject.wait()
657                if PopenObject.stderr:
658                    StdErrThread.join()
659                if PopenObject.returncode != 0:
660                    EdkLogger.error("GenFw", COMMAND_FAILURE, "Failed to generate firmware hash image for %s" % (DefaultEFIfile))
661                if os.path.isfile(Tempfile):
662                    self.Hash = hashlib.sha1()
663                    buf = open(Tempfile, 'rb').read()
664                    if self.Hash.update(buf):
665                        self.Hash = self.Hash.update(buf)
666                    self.Hash = self.Hash.hexdigest()
667                    os.remove(Tempfile)
668
669        FileWrite(File, "Module Summary")
670        FileWrite(File, "Module Name:          %s" % self.ModuleName)
671        FileWrite(File, "Module INF Path:      %s" % self.ModuleInfPath)
672        FileWrite(File, "File GUID:            %s" % self.FileGuid)
673        if self.Size:
674            FileWrite(File, "Size:                 0x%X (%.2fK)" % (self.Size, self.Size / 1024.0))
675        if self.Hash:
676            FileWrite(File, "SHA1 HASH:            %s *%s" % (self.Hash, self.ModuleName + ".efi"))
677        if self.BuildTimeStamp:
678            FileWrite(File, "Build Time Stamp:     %s" % self.BuildTimeStamp)
679        if self.BuildTime:
680            FileWrite(File, "Module Build Time:    %s" % self.BuildTime)
681        if self.DriverType:
682            FileWrite(File, "Driver Type:          %s" % self.DriverType)
683        if self.UefiSpecVersion:
684            FileWrite(File, "UEFI Spec Version:    %s" % self.UefiSpecVersion)
685        if self.PiSpecVersion:
686            FileWrite(File, "PI Spec Version:      %s" % self.PiSpecVersion)
687        if self.PciDeviceId:
688            FileWrite(File, "PCI Device ID:        %s" % self.PciDeviceId)
689        if self.PciVendorId:
690            FileWrite(File, "PCI Vendor ID:        %s" % self.PciVendorId)
691        if self.PciClassCode:
692            FileWrite(File, "PCI Class Code:       %s" % self.PciClassCode)
693
694        FileWrite(File, gSectionSep)
695
696        if "PCD" in ReportType:
697            GlobalPcdReport.GenerateReport(File, self.ModulePcdSet)
698
699        if "LIBRARY" in ReportType:
700            self.LibraryReport.GenerateReport(File)
701
702        if "DEPEX" in ReportType:
703            self.DepexReport.GenerateReport(File, GlobalDepexParser)
704
705        if "BUILD_FLAGS" in ReportType:
706            self.BuildFlagsReport.GenerateReport(File)
707
708        if "FIXED_ADDRESS" in ReportType and self.FileGuid:
709            GlobalPredictionReport.GenerateReport(File, self.FileGuid)
710
711        FileWrite(File, gSectionEnd)
712
713def ReadMessage(From, To, ExitFlag):
714    while True:
715        # read one line a time
716        Line = From.readline()
717        # empty string means "end"
718        if Line is not None and Line != b"":
719            To(Line.rstrip().decode(encoding='utf-8', errors='ignore'))
720        else:
721            break
722        if ExitFlag.isSet():
723            break
724
725##
726# Reports platform and module PCD information
727#
728# This class reports the platform PCD section and module PCD subsection
729# in the build report file.
730#
731class PcdReport(object):
732    ##
733    # Constructor function for class PcdReport
734    #
735    # This constructor function generates PcdReport object a platform build.
736    # It collects the whole PCD database from platform DSC files, platform
737    # flash description file and package DEC files.
738    #
739    # @param self            The object pointer
740    # @param Wa              Workspace context information
741    #
742    def __init__(self, Wa):
743        self.AllPcds = {}
744        self.UnusedPcds = {}
745        self.ConditionalPcds = {}
746        self.MaxLen = 0
747        self.Arch = None
748        if Wa.FdfProfile:
749            self.FdfPcdSet = Wa.FdfProfile.PcdDict
750        else:
751            self.FdfPcdSet = {}
752
753        self.DefaultStoreSingle = True
754        self.SkuSingle = True
755        if GlobalData.gDefaultStores and len(GlobalData.gDefaultStores) > 1:
756            self.DefaultStoreSingle = False
757        if GlobalData.gSkuids and len(GlobalData.gSkuids) > 1:
758            self.SkuSingle = False
759
760        self.ModulePcdOverride = {}
761        for Pa in Wa.AutoGenObjectList:
762            self.Arch = Pa.Arch
763            #
764            # Collect all platform referenced PCDs and grouped them by PCD token space
765            # GUID C Names
766            #
767            for Pcd in Pa.AllPcdList:
768                PcdList = self.AllPcds.setdefault(Pcd.TokenSpaceGuidCName, {}).setdefault(Pcd.Type, [])
769                if Pcd not in PcdList:
770                    PcdList.append(Pcd)
771                if len(Pcd.TokenCName) > self.MaxLen:
772                    self.MaxLen = len(Pcd.TokenCName)
773            #
774            # Collect the PCD defined in DSC/FDF file, but not used in module
775            #
776            UnusedPcdFullList = []
777            StructPcdDict = GlobalData.gStructurePcd.get(self.Arch, collections.OrderedDict())
778            for Name, Guid in StructPcdDict:
779                if (Name, Guid) not in Pa.Platform.Pcds:
780                    Pcd = StructPcdDict[(Name, Guid)]
781                    PcdList = self.AllPcds.setdefault(Guid, {}).setdefault(Pcd.Type, [])
782                    if Pcd not in PcdList and Pcd not in UnusedPcdFullList:
783                        UnusedPcdFullList.append(Pcd)
784            for item in Pa.Platform.Pcds:
785                Pcd = Pa.Platform.Pcds[item]
786                if not Pcd.Type:
787                    # check the Pcd in FDF file, whether it is used in module first
788                    for T in PCD_TYPE_LIST:
789                        PcdList = self.AllPcds.setdefault(Pcd.TokenSpaceGuidCName, {}).setdefault(T, [])
790                        if Pcd in PcdList:
791                            Pcd.Type = T
792                            break
793                if not Pcd.Type:
794                    PcdTypeFlag = False
795                    for package in Pa.PackageList:
796                        for T in PCD_TYPE_LIST:
797                            if (Pcd.TokenCName, Pcd.TokenSpaceGuidCName, T) in package.Pcds:
798                                Pcd.Type = T
799                                PcdTypeFlag = True
800                                if not Pcd.DatumType:
801                                    Pcd.DatumType = package.Pcds[(Pcd.TokenCName, Pcd.TokenSpaceGuidCName, T)].DatumType
802                                break
803                        if PcdTypeFlag:
804                            break
805                if not Pcd.DatumType:
806                    PcdType = Pcd.Type
807                    # Try to remove Hii and Vpd suffix
808                    if PcdType.startswith(TAB_PCDS_DYNAMIC_EX):
809                        PcdType = TAB_PCDS_DYNAMIC_EX
810                    elif PcdType.startswith(TAB_PCDS_DYNAMIC):
811                        PcdType = TAB_PCDS_DYNAMIC
812                    for package in Pa.PackageList:
813                        if (Pcd.TokenCName, Pcd.TokenSpaceGuidCName, PcdType) in package.Pcds:
814                            Pcd.DatumType = package.Pcds[(Pcd.TokenCName, Pcd.TokenSpaceGuidCName, PcdType)].DatumType
815                            break
816
817                PcdList = self.AllPcds.setdefault(Pcd.TokenSpaceGuidCName, {}).setdefault(Pcd.Type, [])
818                UnusedPcdList = self.UnusedPcds.setdefault(Pcd.TokenSpaceGuidCName, {}).setdefault(Pcd.Type, [])
819                if Pcd in UnusedPcdList:
820                    UnusedPcdList.remove(Pcd)
821                if Pcd not in PcdList and Pcd not in UnusedPcdFullList:
822                    UnusedPcdFullList.append(Pcd)
823                if len(Pcd.TokenCName) > self.MaxLen:
824                    self.MaxLen = len(Pcd.TokenCName)
825
826            if GlobalData.gConditionalPcds:
827                for PcdItem in GlobalData.gConditionalPcds:
828                    if '.' in PcdItem:
829                        (TokenSpaceGuidCName, TokenCName) = PcdItem.split('.')
830                        if (TokenCName, TokenSpaceGuidCName) in Pa.Platform.Pcds:
831                            Pcd = Pa.Platform.Pcds[(TokenCName, TokenSpaceGuidCName)]
832                            PcdList = self.ConditionalPcds.setdefault(Pcd.TokenSpaceGuidCName, {}).setdefault(Pcd.Type, [])
833                            if Pcd not in PcdList:
834                                PcdList.append(Pcd)
835
836            UnusedPcdList = []
837            if UnusedPcdFullList:
838                for Pcd in UnusedPcdFullList:
839                    if Pcd.TokenSpaceGuidCName + '.' + Pcd.TokenCName in GlobalData.gConditionalPcds:
840                        continue
841                    UnusedPcdList.append(Pcd)
842
843            for Pcd in UnusedPcdList:
844                PcdList = self.UnusedPcds.setdefault(Pcd.TokenSpaceGuidCName, {}).setdefault(Pcd.Type, [])
845                if Pcd not in PcdList:
846                    PcdList.append(Pcd)
847
848            for Module in Pa.Platform.Modules.values():
849                #
850                # Collect module override PCDs
851                #
852                for ModulePcd in Module.M.ModulePcdList + Module.M.LibraryPcdList:
853                    TokenCName = ModulePcd.TokenCName
854                    TokenSpaceGuid = ModulePcd.TokenSpaceGuidCName
855                    ModuleDefault = ModulePcd.DefaultValue
856                    ModulePath = os.path.basename(Module.M.MetaFile.File)
857                    self.ModulePcdOverride.setdefault((TokenCName, TokenSpaceGuid), {})[ModulePath] = ModuleDefault
858
859
860        #
861        # Collect PCD DEC default value.
862        #
863        self.DecPcdDefault = {}
864        self._GuidDict = {}
865        for Pa in Wa.AutoGenObjectList:
866            for Package in Pa.PackageList:
867                Guids = Package.Guids
868                self._GuidDict.update(Guids)
869                for (TokenCName, TokenSpaceGuidCName, DecType) in Package.Pcds:
870                    DecDefaultValue = Package.Pcds[TokenCName, TokenSpaceGuidCName, DecType].DefaultValue
871                    self.DecPcdDefault.setdefault((TokenCName, TokenSpaceGuidCName, DecType), DecDefaultValue)
872        #
873        # Collect PCDs defined in DSC common section
874        #
875        self.DscPcdDefault = {}
876        for Pa in Wa.AutoGenObjectList:
877            for (TokenCName, TokenSpaceGuidCName) in Pa.Platform.Pcds:
878                DscDefaultValue = Pa.Platform.Pcds[(TokenCName, TokenSpaceGuidCName)].DscDefaultValue
879                if DscDefaultValue:
880                    self.DscPcdDefault[(TokenCName, TokenSpaceGuidCName)] = DscDefaultValue
881
882    def GenerateReport(self, File, ModulePcdSet):
883        if not ModulePcdSet:
884            if self.ConditionalPcds:
885                self.GenerateReportDetail(File, ModulePcdSet, 1)
886            if self.UnusedPcds:
887                IsEmpty = True
888                for Token in self.UnusedPcds:
889                    TokenDict = self.UnusedPcds[Token]
890                    for Type in TokenDict:
891                        if TokenDict[Type]:
892                            IsEmpty = False
893                            break
894                    if not IsEmpty:
895                        break
896                if not IsEmpty:
897                    self.GenerateReportDetail(File, ModulePcdSet, 2)
898        self.GenerateReportDetail(File, ModulePcdSet)
899
900    ##
901    # Generate report for PCD information
902    #
903    # This function generates report for separate module expression
904    # in a platform build.
905    #
906    # @param self            The object pointer
907    # @param File            The file object for report
908    # @param ModulePcdSet    Set of all PCDs referenced by module or None for
909    #                        platform PCD report
910    # @param ReportySubType  0 means platform/module PCD report, 1 means Conditional
911    #                        directives section report, 2 means Unused Pcds section report
912    # @param DscOverridePcds Module DSC override PCDs set
913    #
914    def GenerateReportDetail(self, File, ModulePcdSet, ReportSubType = 0):
915        PcdDict = self.AllPcds
916        if ReportSubType == 1:
917            PcdDict = self.ConditionalPcds
918        elif ReportSubType == 2:
919            PcdDict = self.UnusedPcds
920
921        if not ModulePcdSet:
922            FileWrite(File, gSectionStart)
923            if ReportSubType == 1:
924                FileWrite(File, "Conditional Directives used by the build system")
925            elif ReportSubType == 2:
926                FileWrite(File, "PCDs not used by modules or in conditional directives")
927            else:
928                FileWrite(File, "Platform Configuration Database Report")
929
930            FileWrite(File, "  *B  - PCD override in the build option")
931            FileWrite(File, "  *P  - Platform scoped PCD override in DSC file")
932            FileWrite(File, "  *F  - Platform scoped PCD override in FDF file")
933            if not ReportSubType:
934                FileWrite(File, "  *M  - Module scoped PCD override")
935            FileWrite(File, gSectionSep)
936        else:
937            if not ReportSubType and ModulePcdSet:
938                #
939                # For module PCD sub-section
940                #
941                FileWrite(File, gSubSectionStart)
942                FileWrite(File, TAB_BRG_PCD)
943                FileWrite(File, gSubSectionSep)
944        AllPcdDict = {}
945        for Key in PcdDict:
946            AllPcdDict[Key] = {}
947            for Type in PcdDict[Key]:
948                for Pcd in PcdDict[Key][Type]:
949                    AllPcdDict[Key][(Pcd.TokenCName, Type)] = Pcd
950        for Key in sorted(AllPcdDict):
951            #
952            # Group PCD by their token space GUID C Name
953            #
954            First = True
955            for PcdTokenCName, Type in sorted(AllPcdDict[Key]):
956                #
957                # Group PCD by their usage type
958                #
959                Pcd = AllPcdDict[Key][(PcdTokenCName, Type)]
960                TypeName, DecType = gPcdTypeMap.get(Type, ("", Type))
961                MixedPcdFlag = False
962                if GlobalData.MixedPcd:
963                    for PcdKey in GlobalData.MixedPcd:
964                        if (Pcd.TokenCName, Pcd.TokenSpaceGuidCName) in GlobalData.MixedPcd[PcdKey]:
965                            PcdTokenCName = PcdKey[0]
966                            MixedPcdFlag = True
967                    if MixedPcdFlag and not ModulePcdSet:
968                        continue
969                #
970                # Get PCD default value and their override relationship
971                #
972                DecDefaultValue = self.DecPcdDefault.get((Pcd.TokenCName, Pcd.TokenSpaceGuidCName, DecType))
973                DscDefaultValue = self.DscPcdDefault.get((Pcd.TokenCName, Pcd.TokenSpaceGuidCName))
974                DscDefaultValBak = DscDefaultValue
975                Field = ''
976                for (CName, Guid, Field) in self.FdfPcdSet:
977                    if CName == PcdTokenCName and Guid == Key:
978                        DscDefaultValue = self.FdfPcdSet[(CName, Guid, Field)]
979                        break
980                if DscDefaultValue != DscDefaultValBak:
981                    try:
982                        DscDefaultValue = ValueExpressionEx(DscDefaultValue, Pcd.DatumType, self._GuidDict)(True)
983                    except BadExpression as DscDefaultValue:
984                        EdkLogger.error('BuildReport', FORMAT_INVALID, "PCD Value: %s, Type: %s" %(DscDefaultValue, Pcd.DatumType))
985
986                InfDefaultValue = None
987
988                PcdValue = DecDefaultValue
989                if DscDefaultValue:
990                    PcdValue = DscDefaultValue
991                #The DefaultValue of StructurePcd already be the latest, no need to update.
992                if not self.IsStructurePcd(Pcd.TokenCName, Pcd.TokenSpaceGuidCName):
993                    Pcd.DefaultValue = PcdValue
994                if ModulePcdSet is not None:
995                    if (Pcd.TokenCName, Pcd.TokenSpaceGuidCName, Type) not in ModulePcdSet:
996                        continue
997                    InfDefaultValue, PcdValue = ModulePcdSet[Pcd.TokenCName, Pcd.TokenSpaceGuidCName, Type]
998                    #The DefaultValue of StructurePcd already be the latest, no need to update.
999                    if not self.IsStructurePcd(Pcd.TokenCName, Pcd.TokenSpaceGuidCName):
1000                        Pcd.DefaultValue = PcdValue
1001                    if InfDefaultValue:
1002                        try:
1003                            InfDefaultValue = ValueExpressionEx(InfDefaultValue, Pcd.DatumType, self._GuidDict)(True)
1004                        except BadExpression as InfDefaultValue:
1005                            EdkLogger.error('BuildReport', FORMAT_INVALID, "PCD Value: %s, Type: %s" % (InfDefaultValue, Pcd.DatumType))
1006                    if InfDefaultValue == "":
1007                        InfDefaultValue = None
1008
1009                BuildOptionMatch = False
1010                if GlobalData.BuildOptionPcd:
1011                    for pcd in GlobalData.BuildOptionPcd:
1012                        if (Pcd.TokenSpaceGuidCName, Pcd.TokenCName) == (pcd[0], pcd[1]):
1013                            if pcd[2]:
1014                                continue
1015                            PcdValue = pcd[3]
1016                            #The DefaultValue of StructurePcd already be the latest, no need to update.
1017                            if not self.IsStructurePcd(Pcd.TokenCName, Pcd.TokenSpaceGuidCName):
1018                                Pcd.DefaultValue = PcdValue
1019                            BuildOptionMatch = True
1020                            break
1021
1022                if First:
1023                    if ModulePcdSet is None:
1024                        FileWrite(File, "")
1025                    FileWrite(File, Key)
1026                    First = False
1027
1028
1029                if Pcd.DatumType in TAB_PCD_NUMERIC_TYPES:
1030                    if PcdValue.startswith('0') and not PcdValue.lower().startswith('0x') and \
1031                            len(PcdValue) > 1 and PcdValue.lstrip('0'):
1032                        PcdValue = PcdValue.lstrip('0')
1033                    PcdValueNumber = int(PcdValue.strip(), 0)
1034                    if DecDefaultValue is None:
1035                        DecMatch = True
1036                    else:
1037                        if DecDefaultValue.startswith('0') and not DecDefaultValue.lower().startswith('0x') and \
1038                                len(DecDefaultValue) > 1 and DecDefaultValue.lstrip('0'):
1039                            DecDefaultValue = DecDefaultValue.lstrip('0')
1040                        DecDefaultValueNumber = int(DecDefaultValue.strip(), 0)
1041                        DecMatch = (DecDefaultValueNumber == PcdValueNumber)
1042
1043                    if InfDefaultValue is None:
1044                        InfMatch = True
1045                    else:
1046                        if InfDefaultValue.startswith('0') and not InfDefaultValue.lower().startswith('0x') and \
1047                                len(InfDefaultValue) > 1 and InfDefaultValue.lstrip('0'):
1048                            InfDefaultValue = InfDefaultValue.lstrip('0')
1049                        InfDefaultValueNumber = int(InfDefaultValue.strip(), 0)
1050                        InfMatch = (InfDefaultValueNumber == PcdValueNumber)
1051
1052                    if DscDefaultValue is None:
1053                        DscMatch = True
1054                    else:
1055                        if DscDefaultValue.startswith('0') and not DscDefaultValue.lower().startswith('0x') and \
1056                                len(DscDefaultValue) > 1 and DscDefaultValue.lstrip('0'):
1057                            DscDefaultValue = DscDefaultValue.lstrip('0')
1058                        DscDefaultValueNumber = int(DscDefaultValue.strip(), 0)
1059                        DscMatch = (DscDefaultValueNumber == PcdValueNumber)
1060                else:
1061                    if DecDefaultValue is None:
1062                        DecMatch = True
1063                    else:
1064                        DecMatch = (DecDefaultValue.strip() == PcdValue.strip())
1065
1066                    if InfDefaultValue is None:
1067                        InfMatch = True
1068                    else:
1069                        InfMatch = (InfDefaultValue.strip() == PcdValue.strip())
1070
1071                    if DscDefaultValue is None:
1072                        DscMatch = True
1073                    else:
1074                        DscMatch = (DscDefaultValue.strip() == PcdValue.strip())
1075
1076                IsStructure = False
1077                if self.IsStructurePcd(Pcd.TokenCName, Pcd.TokenSpaceGuidCName):
1078                    IsStructure = True
1079                    if TypeName in ('DYNVPD', 'DEXVPD'):
1080                        SkuInfoList = Pcd.SkuInfoList
1081                    Pcd = GlobalData.gStructurePcd[self.Arch][(Pcd.TokenCName, Pcd.TokenSpaceGuidCName)]
1082                    Pcd.DatumType = Pcd.StructName
1083                    if TypeName in ('DYNVPD', 'DEXVPD'):
1084                        Pcd.SkuInfoList = SkuInfoList
1085                    if Pcd.PcdValueFromComm or Pcd.PcdFieldValueFromComm:
1086                        BuildOptionMatch = True
1087                        DecMatch = False
1088                    elif Pcd.PcdValueFromFdf or Pcd.PcdFieldValueFromFdf:
1089                        DscDefaultValue = True
1090                        DscMatch = True
1091                        DecMatch = False
1092                    elif Pcd.SkuOverrideValues:
1093                        DscOverride = False
1094                        if Pcd.DefaultFromDSC:
1095                            DscOverride = True
1096                        else:
1097                            DictLen = 0
1098                            for item in Pcd.SkuOverrideValues:
1099                                DictLen += len(Pcd.SkuOverrideValues[item])
1100                            if not DictLen:
1101                                DscOverride = False
1102                            else:
1103                                if not Pcd.SkuInfoList:
1104                                    OverrideValues = Pcd.SkuOverrideValues
1105                                    if OverrideValues:
1106                                        for Data in OverrideValues.values():
1107                                            Struct = list(Data.values())
1108                                            if Struct:
1109                                                DscOverride = self.ParseStruct(Struct[0])
1110                                                break
1111                                else:
1112                                    SkuList = sorted(Pcd.SkuInfoList.keys())
1113                                    for Sku in SkuList:
1114                                        SkuInfo = Pcd.SkuInfoList[Sku]
1115                                        if SkuInfo.DefaultStoreDict:
1116                                            DefaultStoreList = sorted(SkuInfo.DefaultStoreDict.keys())
1117                                            for DefaultStore in DefaultStoreList:
1118                                                OverrideValues = Pcd.SkuOverrideValues[Sku]
1119                                                DscOverride = self.ParseStruct(OverrideValues[DefaultStore])
1120                                                if DscOverride:
1121                                                    break
1122                                        if DscOverride:
1123                                            break
1124                        if DscOverride:
1125                            DscDefaultValue = True
1126                            DscMatch = True
1127                            DecMatch = False
1128                        else:
1129                            DecMatch = True
1130                    else:
1131                        DscDefaultValue = True
1132                        DscMatch = True
1133                        DecMatch = False
1134
1135                #
1136                # Report PCD item according to their override relationship
1137                #
1138                if Pcd.DatumType == 'BOOLEAN':
1139                    if DscDefaultValue:
1140                        DscDefaultValue = str(int(DscDefaultValue, 0))
1141                    if DecDefaultValue:
1142                        DecDefaultValue = str(int(DecDefaultValue, 0))
1143                    if InfDefaultValue:
1144                        InfDefaultValue = str(int(InfDefaultValue, 0))
1145                    if Pcd.DefaultValue:
1146                        Pcd.DefaultValue = str(int(Pcd.DefaultValue, 0))
1147                if DecMatch:
1148                    self.PrintPcdValue(File, Pcd, PcdTokenCName, TypeName, IsStructure, DscMatch, DscDefaultValBak, InfMatch, InfDefaultValue, DecMatch, DecDefaultValue, '  ')
1149                elif InfDefaultValue and InfMatch:
1150                    self.PrintPcdValue(File, Pcd, PcdTokenCName, TypeName, IsStructure, DscMatch, DscDefaultValBak, InfMatch, InfDefaultValue, DecMatch, DecDefaultValue, '*M')
1151                elif BuildOptionMatch:
1152                    self.PrintPcdValue(File, Pcd, PcdTokenCName, TypeName, IsStructure, DscMatch, DscDefaultValBak, InfMatch, InfDefaultValue, DecMatch, DecDefaultValue, '*B')
1153                else:
1154                    if DscDefaultValue and DscMatch:
1155                        if (Pcd.TokenCName, Key, Field) in self.FdfPcdSet:
1156                            self.PrintPcdValue(File, Pcd, PcdTokenCName, TypeName, IsStructure, DscMatch, DscDefaultValBak, InfMatch, InfDefaultValue, DecMatch, DecDefaultValue, '*F')
1157                        else:
1158                            self.PrintPcdValue(File, Pcd, PcdTokenCName, TypeName, IsStructure, DscMatch, DscDefaultValBak, InfMatch, InfDefaultValue, DecMatch, DecDefaultValue, '*P')
1159                    else:
1160                        self.PrintPcdValue(File, Pcd, PcdTokenCName, TypeName, IsStructure, DscMatch, DscDefaultValBak, InfMatch, InfDefaultValue, DecMatch, DecDefaultValue, '*M')
1161
1162                if ModulePcdSet is None:
1163                    if IsStructure:
1164                        continue
1165                    if not TypeName in ('PATCH', 'FLAG', 'FIXED'):
1166                        continue
1167                    if not BuildOptionMatch:
1168                        ModuleOverride = self.ModulePcdOverride.get((Pcd.TokenCName, Pcd.TokenSpaceGuidCName), {})
1169                        for ModulePath in ModuleOverride:
1170                            ModuleDefault = ModuleOverride[ModulePath]
1171                            if Pcd.DatumType in TAB_PCD_NUMERIC_TYPES:
1172                                if ModuleDefault.startswith('0') and not ModuleDefault.lower().startswith('0x') and \
1173                                        len(ModuleDefault) > 1 and ModuleDefault.lstrip('0'):
1174                                    ModuleDefault = ModuleDefault.lstrip('0')
1175                                ModulePcdDefaultValueNumber = int(ModuleDefault.strip(), 0)
1176                                Match = (ModulePcdDefaultValueNumber == PcdValueNumber)
1177                                if Pcd.DatumType == 'BOOLEAN':
1178                                    ModuleDefault = str(ModulePcdDefaultValueNumber)
1179                            else:
1180                                Match = (ModuleDefault.strip() == PcdValue.strip())
1181                            if Match:
1182                                continue
1183                            IsByteArray, ArrayList = ByteArrayForamt(ModuleDefault.strip())
1184                            if IsByteArray:
1185                                FileWrite(File, ' *M     %-*s = %s' % (self.MaxLen + 15, ModulePath, '{'))
1186                                for Array in ArrayList:
1187                                    FileWrite(File, Array)
1188                            else:
1189                                Value =  ModuleDefault.strip()
1190                                if Pcd.DatumType in TAB_PCD_CLEAN_NUMERIC_TYPES:
1191                                    if Value.startswith(('0x', '0X')):
1192                                        Value = '{} ({:d})'.format(Value, int(Value, 0))
1193                                    else:
1194                                        Value = "0x{:X} ({})".format(int(Value, 0), Value)
1195                                FileWrite(File, ' *M     %-*s = %s' % (self.MaxLen + 15, ModulePath, Value))
1196
1197        if ModulePcdSet is None:
1198            FileWrite(File, gSectionEnd)
1199        else:
1200            if not ReportSubType and ModulePcdSet:
1201                FileWrite(File, gSubSectionEnd)
1202
1203    def ParseStruct(self, struct):
1204        HasDscOverride = False
1205        if struct:
1206            for _, Values in list(struct.items()):
1207                for Key, value in Values.items():
1208                    if value[1] and value[1].endswith('.dsc'):
1209                        HasDscOverride = True
1210                        break
1211                if HasDscOverride == True:
1212                    break
1213        return HasDscOverride
1214
1215    def PrintPcdDefault(self, File, Pcd, IsStructure, DscMatch, DscDefaultValue, InfMatch, InfDefaultValue, DecMatch, DecDefaultValue):
1216        if not DscMatch and DscDefaultValue is not None:
1217            Value = DscDefaultValue.strip()
1218            IsByteArray, ArrayList = ByteArrayForamt(Value)
1219            if IsByteArray:
1220                FileWrite(File, '    %*s = %s' % (self.MaxLen + 19, 'DSC DEFAULT', "{"))
1221                for Array in ArrayList:
1222                    FileWrite(File, Array)
1223            else:
1224                if Pcd.DatumType in TAB_PCD_CLEAN_NUMERIC_TYPES:
1225                    if Value.startswith(('0x', '0X')):
1226                        Value = '{} ({:d})'.format(Value, int(Value, 0))
1227                    else:
1228                        Value = "0x{:X} ({})".format(int(Value, 0), Value)
1229                FileWrite(File, '    %*s = %s' % (self.MaxLen + 19, 'DSC DEFAULT', Value))
1230        if not InfMatch and InfDefaultValue is not None:
1231            Value = InfDefaultValue.strip()
1232            IsByteArray, ArrayList = ByteArrayForamt(Value)
1233            if IsByteArray:
1234                FileWrite(File, '    %*s = %s' % (self.MaxLen + 19, 'INF DEFAULT', "{"))
1235                for Array in ArrayList:
1236                    FileWrite(File, Array)
1237            else:
1238                if Pcd.DatumType in TAB_PCD_CLEAN_NUMERIC_TYPES:
1239                    if Value.startswith(('0x', '0X')):
1240                        Value = '{} ({:d})'.format(Value, int(Value, 0))
1241                    else:
1242                        Value = "0x{:X} ({})".format(int(Value, 0), Value)
1243                FileWrite(File, '    %*s = %s' % (self.MaxLen + 19, 'INF DEFAULT', Value))
1244
1245        if not DecMatch and DecDefaultValue is not None:
1246            Value = DecDefaultValue.strip()
1247            IsByteArray, ArrayList = ByteArrayForamt(Value)
1248            if IsByteArray:
1249                FileWrite(File, '    %*s = %s' % (self.MaxLen + 19, 'DEC DEFAULT', "{"))
1250                for Array in ArrayList:
1251                    FileWrite(File, Array)
1252            else:
1253                if Pcd.DatumType in TAB_PCD_CLEAN_NUMERIC_TYPES:
1254                    if Value.startswith(('0x', '0X')):
1255                        Value = '{} ({:d})'.format(Value, int(Value, 0))
1256                    else:
1257                        Value = "0x{:X} ({})".format(int(Value, 0), Value)
1258                FileWrite(File, '    %*s = %s' % (self.MaxLen + 19, 'DEC DEFAULT', Value))
1259            if IsStructure:
1260                for filedvalues in Pcd.DefaultValues.values():
1261                    self.PrintStructureInfo(File, filedvalues)
1262        if DecMatch and IsStructure:
1263            for filedvalues in Pcd.DefaultValues.values():
1264                self.PrintStructureInfo(File, filedvalues)
1265
1266    def PrintPcdValue(self, File, Pcd, PcdTokenCName, TypeName, IsStructure, DscMatch, DscDefaultValue, InfMatch, InfDefaultValue, DecMatch, DecDefaultValue, Flag = '  '):
1267        if not Pcd.SkuInfoList:
1268            Value = Pcd.DefaultValue
1269            IsByteArray, ArrayList = ByteArrayForamt(Value)
1270            if IsByteArray:
1271                FileWrite(File, ' %-*s   : %6s %10s = %s' % (self.MaxLen, Flag + ' ' + PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', '{'))
1272                for Array in ArrayList:
1273                    FileWrite(File, Array)
1274            else:
1275                if Pcd.DatumType in TAB_PCD_CLEAN_NUMERIC_TYPES:
1276                    if Value.startswith('0') and not Value.lower().startswith('0x') and len(Value) > 1 and Value.lstrip('0'):
1277                        Value = Value.lstrip('0')
1278                    if Value.startswith(('0x', '0X')):
1279                        Value = '{} ({:d})'.format(Value, int(Value, 0))
1280                    else:
1281                        Value = "0x{:X} ({})".format(int(Value, 0), Value)
1282                FileWrite(File, ' %-*s   : %6s %10s = %s' % (self.MaxLen, Flag + ' ' + PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', Value))
1283            if IsStructure:
1284                FiledOverrideFlag = False
1285                if (Pcd.TokenCName,Pcd.TokenSpaceGuidCName) in GlobalData.gPcdSkuOverrides:
1286                    OverrideValues = GlobalData.gPcdSkuOverrides[(Pcd.TokenCName,Pcd.TokenSpaceGuidCName)]
1287                else:
1288                    OverrideValues = Pcd.SkuOverrideValues
1289                if OverrideValues:
1290                    for Data in OverrideValues.values():
1291                        Struct = list(Data.values())
1292                        if Struct:
1293                            OverrideFieldStruct = self.OverrideFieldValue(Pcd, Struct[0])
1294                            self.PrintStructureInfo(File, OverrideFieldStruct)
1295                            FiledOverrideFlag = True
1296                            break
1297                if not FiledOverrideFlag and (Pcd.PcdFieldValueFromComm or Pcd.PcdFieldValueFromFdf):
1298                    OverrideFieldStruct = self.OverrideFieldValue(Pcd, {})
1299                    self.PrintStructureInfo(File, OverrideFieldStruct)
1300            self.PrintPcdDefault(File, Pcd, IsStructure, DscMatch, DscDefaultValue, InfMatch, InfDefaultValue, DecMatch, DecDefaultValue)
1301        else:
1302            FirstPrint = True
1303            SkuList = sorted(Pcd.SkuInfoList.keys())
1304            for Sku in SkuList:
1305                SkuInfo = Pcd.SkuInfoList[Sku]
1306                SkuIdName = SkuInfo.SkuIdName
1307                if TypeName in ('DYNHII', 'DEXHII'):
1308                    if SkuInfo.DefaultStoreDict:
1309                        DefaultStoreList = sorted(SkuInfo.DefaultStoreDict.keys())
1310                        for DefaultStore in DefaultStoreList:
1311                            Value = SkuInfo.DefaultStoreDict[DefaultStore]
1312                            IsByteArray, ArrayList = ByteArrayForamt(Value)
1313                            if Pcd.DatumType == 'BOOLEAN':
1314                                Value = str(int(Value, 0))
1315                            if FirstPrint:
1316                                FirstPrint = False
1317                                if IsByteArray:
1318                                    if self.DefaultStoreSingle and self.SkuSingle:
1319                                        FileWrite(File, ' %-*s   : %6s %10s = %s' % (self.MaxLen, Flag + ' ' + PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', '{'))
1320                                    elif self.DefaultStoreSingle and not self.SkuSingle:
1321                                        FileWrite(File, ' %-*s   : %6s %10s %10s = %s' % (self.MaxLen, Flag + ' ' + PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', '(' + SkuIdName + ')', '{'))
1322                                    elif not self.DefaultStoreSingle and self.SkuSingle:
1323                                        FileWrite(File, ' %-*s   : %6s %10s %10s = %s' % (self.MaxLen, Flag + ' ' + PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', '(' + DefaultStore + ')', '{'))
1324                                    else:
1325                                        FileWrite(File, ' %-*s   : %6s %10s %10s %10s = %s' % (self.MaxLen, Flag + ' ' + PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', '(' + SkuIdName + ')', '(' + DefaultStore + ')', '{'))
1326                                    for Array in ArrayList:
1327                                        FileWrite(File, Array)
1328                                else:
1329                                    if Pcd.DatumType in TAB_PCD_CLEAN_NUMERIC_TYPES:
1330                                        if Value.startswith(('0x', '0X')):
1331                                            Value = '{} ({:d})'.format(Value, int(Value, 0))
1332                                        else:
1333                                            Value = "0x{:X} ({})".format(int(Value, 0), Value)
1334                                    if self.DefaultStoreSingle and self.SkuSingle:
1335                                        FileWrite(File, ' %-*s   : %6s %10s = %s' % (self.MaxLen, Flag + ' ' + PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', Value))
1336                                    elif self.DefaultStoreSingle and not self.SkuSingle:
1337                                        FileWrite(File, ' %-*s   : %6s %10s %10s = %s' % (self.MaxLen, Flag + ' ' + PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', '(' + SkuIdName + ')', Value))
1338                                    elif not self.DefaultStoreSingle and self.SkuSingle:
1339                                        FileWrite(File, ' %-*s   : %6s %10s %10s = %s' % (self.MaxLen, Flag + ' ' + PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', '(' + DefaultStore + ')', Value))
1340                                    else:
1341                                        FileWrite(File, ' %-*s   : %6s %10s %10s %10s = %s' % (self.MaxLen, Flag + ' ' + PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', '(' + SkuIdName + ')', '(' + DefaultStore + ')', Value))
1342                            else:
1343                                if IsByteArray:
1344                                    if self.DefaultStoreSingle and self.SkuSingle:
1345                                        FileWrite(File, ' %-*s   : %6s %10s = %s' % (self.MaxLen, ' ', TypeName, '(' + Pcd.DatumType + ')', '{'))
1346                                    elif self.DefaultStoreSingle and not self.SkuSingle:
1347                                        FileWrite(File, ' %-*s   : %6s %10s %10s = %s' % (self.MaxLen, ' ', TypeName, '(' + Pcd.DatumType + ')', '(' + SkuIdName + ')', '{'))
1348                                    elif not self.DefaultStoreSingle and self.SkuSingle:
1349                                        FileWrite(File, ' %-*s   : %6s %10s %10s = %s' % (self.MaxLen, ' ', TypeName, '(' + Pcd.DatumType + ')', '(' + DefaultStore + ')', '{'))
1350                                    else:
1351                                        FileWrite(File, ' %-*s   : %6s %10s %10s %10s = %s' % (self.MaxLen, ' ', TypeName, '(' + Pcd.DatumType + ')', '(' + SkuIdName + ')', '(' + DefaultStore + ')', '{'))
1352                                    for Array in ArrayList:
1353                                        FileWrite(File, Array)
1354                                else:
1355                                    if Pcd.DatumType in TAB_PCD_CLEAN_NUMERIC_TYPES:
1356                                        if Value.startswith(('0x', '0X')):
1357                                            Value = '{} ({:d})'.format(Value, int(Value, 0))
1358                                        else:
1359                                            Value = "0x{:X} ({})".format(int(Value, 0), Value)
1360                                    if self.DefaultStoreSingle and self.SkuSingle:
1361                                        FileWrite(File, ' %-*s   : %6s %10s = %s' % (self.MaxLen, ' ', TypeName, '(' + Pcd.DatumType + ')',  Value))
1362                                    elif self.DefaultStoreSingle and not self.SkuSingle:
1363                                        FileWrite(File, ' %-*s   : %6s %10s %10s = %s' % (self.MaxLen, ' ', TypeName, '(' + Pcd.DatumType + ')', '(' + SkuIdName + ')', Value))
1364                                    elif not self.DefaultStoreSingle and self.SkuSingle:
1365                                        FileWrite(File, ' %-*s   : %6s %10s %10s = %s' % (self.MaxLen, ' ', TypeName, '(' + Pcd.DatumType + ')', '(' + DefaultStore + ')', Value))
1366                                    else:
1367                                        FileWrite(File, ' %-*s   : %6s %10s %10s %10s = %s' % (self.MaxLen, ' ', TypeName, '(' + Pcd.DatumType + ')', '(' + SkuIdName + ')', '(' + DefaultStore + ')', Value))
1368                            FileWrite(File, '%*s: %s: %s' % (self.MaxLen + 4, SkuInfo.VariableGuid, SkuInfo.VariableName, SkuInfo.VariableOffset))
1369                            if IsStructure:
1370                                OverrideValues = Pcd.SkuOverrideValues[Sku]
1371                                OverrideFieldStruct = self.OverrideFieldValue(Pcd, OverrideValues[DefaultStore])
1372                                self.PrintStructureInfo(File, OverrideFieldStruct)
1373                            self.PrintPcdDefault(File, Pcd, IsStructure, DscMatch, DscDefaultValue, InfMatch, InfDefaultValue, DecMatch, DecDefaultValue)
1374                else:
1375                    Value = SkuInfo.DefaultValue
1376                    IsByteArray, ArrayList = ByteArrayForamt(Value)
1377                    if Pcd.DatumType == 'BOOLEAN':
1378                        Value = str(int(Value, 0))
1379                    if FirstPrint:
1380                        FirstPrint = False
1381                        if IsByteArray:
1382                            if self.SkuSingle:
1383                                FileWrite(File, ' %-*s   : %6s %10s = %s' % (self.MaxLen, Flag + ' ' + PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', "{"))
1384                            else:
1385                                FileWrite(File, ' %-*s   : %6s %10s %10s = %s' % (self.MaxLen, Flag + ' ' + PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', '(' + SkuIdName + ')', "{"))
1386                            for Array in ArrayList:
1387                                FileWrite(File, Array)
1388                        else:
1389                            if Pcd.DatumType in TAB_PCD_CLEAN_NUMERIC_TYPES:
1390                                if Value.startswith(('0x', '0X')):
1391                                    Value = '{} ({:d})'.format(Value, int(Value, 0))
1392                                else:
1393                                    Value = "0x{:X} ({})".format(int(Value, 0), Value)
1394                            if self.SkuSingle:
1395                                FileWrite(File, ' %-*s   : %6s %10s = %s' % (self.MaxLen, Flag + ' ' + PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', Value))
1396                            else:
1397                                FileWrite(File, ' %-*s   : %6s %10s %10s = %s' % (self.MaxLen, Flag + ' ' + PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', '(' + SkuIdName + ')', Value))
1398                    else:
1399                        if IsByteArray:
1400                            if self.SkuSingle:
1401                                FileWrite(File, ' %-*s   : %6s %10s = %s' % (self.MaxLen, ' ', TypeName, '(' + Pcd.DatumType + ')', "{"))
1402                            else:
1403                                FileWrite(File, ' %-*s   : %6s %10s %10s = %s' % (self.MaxLen, ' ', TypeName, '(' + Pcd.DatumType + ')', '(' + SkuIdName + ')', "{"))
1404                            for Array in ArrayList:
1405                                FileWrite(File, Array)
1406                        else:
1407                            if Pcd.DatumType in TAB_PCD_CLEAN_NUMERIC_TYPES:
1408                                if Value.startswith(('0x', '0X')):
1409                                    Value = '{} ({:d})'.format(Value, int(Value, 0))
1410                                else:
1411                                    Value = "0x{:X} ({})".format(int(Value, 0), Value)
1412                            if self.SkuSingle:
1413                                FileWrite(File, ' %-*s   : %6s %10s = %s' % (self.MaxLen, ' ', TypeName, '(' + Pcd.DatumType + ')', Value))
1414                            else:
1415                                FileWrite(File, ' %-*s   : %6s %10s %10s = %s' % (self.MaxLen, ' ', TypeName, '(' + Pcd.DatumType + ')', '(' + SkuIdName + ')', Value))
1416                    if TypeName in ('DYNVPD', 'DEXVPD'):
1417                        FileWrite(File, '%*s' % (self.MaxLen + 4, SkuInfo.VpdOffset))
1418                        VPDPcdItem = (Pcd.TokenSpaceGuidCName + '.' + PcdTokenCName, SkuIdName, SkuInfo.VpdOffset, Pcd.MaxDatumSize, SkuInfo.DefaultValue)
1419                        if VPDPcdItem not in VPDPcdList:
1420                            VPDPcdList.append(VPDPcdItem)
1421                    if IsStructure:
1422                        FiledOverrideFlag = False
1423                        OverrideValues = Pcd.SkuOverrideValues[Sku]
1424                        if OverrideValues:
1425                            Keys = list(OverrideValues.keys())
1426                            OverrideFieldStruct = self.OverrideFieldValue(Pcd, OverrideValues[Keys[0]])
1427                            self.PrintStructureInfo(File, OverrideFieldStruct)
1428                            FiledOverrideFlag = True
1429                        if not FiledOverrideFlag and (Pcd.PcdFieldValueFromComm or Pcd.PcdFieldValueFromFdf):
1430                            OverrideFieldStruct = self.OverrideFieldValue(Pcd, {})
1431                            self.PrintStructureInfo(File, OverrideFieldStruct)
1432                    self.PrintPcdDefault(File, Pcd, IsStructure, DscMatch, DscDefaultValue, InfMatch, InfDefaultValue, DecMatch, DecDefaultValue)
1433
1434    def OverrideFieldValue(self, Pcd, OverrideStruct):
1435        OverrideFieldStruct = collections.OrderedDict()
1436        if OverrideStruct:
1437            for _, Values in OverrideStruct.items():
1438                for Key,value in Values.items():
1439                    if value[1] and value[1].endswith('.dsc'):
1440                        OverrideFieldStruct[Key] = value
1441        if Pcd.PcdFieldValueFromFdf:
1442            for Key, Values in Pcd.PcdFieldValueFromFdf.items():
1443                if Key in OverrideFieldStruct and Values[0] == OverrideFieldStruct[Key][0]:
1444                    continue
1445                OverrideFieldStruct[Key] = Values
1446        if Pcd.PcdFieldValueFromComm:
1447            for Key, Values in Pcd.PcdFieldValueFromComm.items():
1448                if Key in OverrideFieldStruct and Values[0] == OverrideFieldStruct[Key][0]:
1449                    continue
1450                OverrideFieldStruct[Key] = Values
1451        return OverrideFieldStruct
1452
1453    def PrintStructureInfo(self, File, Struct):
1454        for Key, Value in sorted(Struct.items(), key=lambda x: x[0]):
1455            if Value[1] and 'build command options' in Value[1]:
1456                FileWrite(File, '    *B  %-*s = %s' % (self.MaxLen + 4, '.' + Key, Value[0]))
1457            elif Value[1] and Value[1].endswith('.fdf'):
1458                FileWrite(File, '    *F  %-*s = %s' % (self.MaxLen + 4, '.' + Key, Value[0]))
1459            else:
1460                FileWrite(File, '        %-*s = %s' % (self.MaxLen + 4, '.' + Key, Value[0]))
1461
1462    def StrtoHex(self, value):
1463        try:
1464            value = hex(int(value))
1465            return value
1466        except:
1467            if value.startswith("L\"") and value.endswith("\""):
1468                valuelist = []
1469                for ch in value[2:-1]:
1470                    valuelist.append(hex(ord(ch)))
1471                    valuelist.append('0x00')
1472                return valuelist
1473            elif value.startswith("\"") and value.endswith("\""):
1474                return hex(ord(value[1:-1]))
1475            elif value.startswith("{") and value.endswith("}"):
1476                valuelist = []
1477                if ',' not in value:
1478                    return value[1:-1]
1479                for ch in value[1:-1].split(','):
1480                    ch = ch.strip()
1481                    if ch.startswith('0x') or ch.startswith('0X'):
1482                        valuelist.append(ch)
1483                        continue
1484                    try:
1485                        valuelist.append(hex(int(ch.strip())))
1486                    except:
1487                        pass
1488                return valuelist
1489            else:
1490                return value
1491
1492    def IsStructurePcd(self, PcdToken, PcdTokenSpaceGuid):
1493        if GlobalData.gStructurePcd and (self.Arch in GlobalData.gStructurePcd) and ((PcdToken, PcdTokenSpaceGuid) in GlobalData.gStructurePcd[self.Arch]):
1494            return True
1495        else:
1496            return False
1497
1498##
1499# Reports platform and module Prediction information
1500#
1501# This class reports the platform execution order prediction section and
1502# module load fixed address prediction subsection in the build report file.
1503#
1504class PredictionReport(object):
1505    ##
1506    # Constructor function for class PredictionReport
1507    #
1508    # This constructor function generates PredictionReport object for the platform.
1509    #
1510    # @param self:           The object pointer
1511    # @param Wa              Workspace context information
1512    #
1513    def __init__(self, Wa):
1514        self._MapFileName = os.path.join(Wa.BuildDir, Wa.Name + ".map")
1515        self._MapFileParsed = False
1516        self._EotToolInvoked = False
1517        self._FvDir = Wa.FvDir
1518        self._EotDir = Wa.BuildDir
1519        self._FfsEntryPoint = {}
1520        self._GuidMap = {}
1521        self._SourceList = []
1522        self.FixedMapDict = {}
1523        self.ItemList = []
1524        self.MaxLen = 0
1525
1526        #
1527        # Collect all platform reference source files and GUID C Name
1528        #
1529        for Pa in Wa.AutoGenObjectList:
1530            for Module in Pa.LibraryAutoGenList + Pa.ModuleAutoGenList:
1531                #
1532                # BASE typed modules are EFI agnostic, so we need not scan
1533                # their source code to find PPI/Protocol produce or consume
1534                # information.
1535                #
1536                if Module.ModuleType == SUP_MODULE_BASE:
1537                    continue
1538                #
1539                # Add module referenced source files
1540                #
1541                self._SourceList.append(str(Module))
1542                IncludeList = {}
1543                for Source in Module.SourceFileList:
1544                    if os.path.splitext(str(Source))[1].lower() == ".c":
1545                        self._SourceList.append("  " + str(Source))
1546                        FindIncludeFiles(Source.Path, Module.IncludePathList, IncludeList)
1547                for IncludeFile in IncludeList.values():
1548                    self._SourceList.append("  " + IncludeFile)
1549
1550                for Guid in Module.PpiList:
1551                    self._GuidMap[Guid] = GuidStructureStringToGuidString(Module.PpiList[Guid])
1552                for Guid in Module.ProtocolList:
1553                    self._GuidMap[Guid] = GuidStructureStringToGuidString(Module.ProtocolList[Guid])
1554                for Guid in Module.GuidList:
1555                    self._GuidMap[Guid] = GuidStructureStringToGuidString(Module.GuidList[Guid])
1556
1557                if Module.Guid and not Module.IsLibrary:
1558                    EntryPoint = " ".join(Module.Module.ModuleEntryPointList)
1559
1560                    RealEntryPoint = "_ModuleEntryPoint"
1561
1562                    self._FfsEntryPoint[Module.Guid.upper()] = (EntryPoint, RealEntryPoint)
1563
1564
1565        #
1566        # Collect platform firmware volume list as the input of EOT.
1567        #
1568        self._FvList = []
1569        if Wa.FdfProfile:
1570            for Fd in Wa.FdfProfile.FdDict:
1571                for FdRegion in Wa.FdfProfile.FdDict[Fd].RegionList:
1572                    if FdRegion.RegionType != BINARY_FILE_TYPE_FV:
1573                        continue
1574                    for FvName in FdRegion.RegionDataList:
1575                        if FvName in self._FvList:
1576                            continue
1577                        self._FvList.append(FvName)
1578                        for Ffs in Wa.FdfProfile.FvDict[FvName.upper()].FfsList:
1579                            for Section in Ffs.SectionList:
1580                                try:
1581                                    for FvSection in Section.SectionList:
1582                                        if FvSection.FvName in self._FvList:
1583                                            continue
1584                                        self._FvList.append(FvSection.FvName)
1585                                except AttributeError:
1586                                    pass
1587
1588
1589    ##
1590    # Parse platform fixed address map files
1591    #
1592    # This function parses the platform final fixed address map file to get
1593    # the database of predicted fixed address for module image base, entry point
1594    # etc.
1595    #
1596    # @param self:           The object pointer
1597    #
1598    def _ParseMapFile(self):
1599        if self._MapFileParsed:
1600            return
1601        self._MapFileParsed = True
1602        if os.path.isfile(self._MapFileName):
1603            try:
1604                FileContents = open(self._MapFileName).read()
1605                for Match in gMapFileItemPattern.finditer(FileContents):
1606                    AddressType = Match.group(1)
1607                    BaseAddress = Match.group(2)
1608                    EntryPoint = Match.group(3)
1609                    Guid = Match.group(4).upper()
1610                    List = self.FixedMapDict.setdefault(Guid, [])
1611                    List.append((AddressType, BaseAddress, "*I"))
1612                    List.append((AddressType, EntryPoint, "*E"))
1613            except:
1614                EdkLogger.warn(None, "Cannot open file to read", self._MapFileName)
1615
1616    ##
1617    # Invokes EOT tool to get the predicted the execution order.
1618    #
1619    # This function invokes EOT tool to calculate the predicted dispatch order
1620    #
1621    # @param self:           The object pointer
1622    #
1623    def _InvokeEotTool(self):
1624        if self._EotToolInvoked:
1625            return
1626
1627        self._EotToolInvoked = True
1628        FvFileList = []
1629        for FvName in self._FvList:
1630            FvFile = os.path.join(self._FvDir, FvName + ".Fv")
1631            if os.path.isfile(FvFile):
1632                FvFileList.append(FvFile)
1633
1634        if len(FvFileList) == 0:
1635            return
1636        #
1637        # Write source file list and GUID file list to an intermediate file
1638        # as the input for EOT tool and dispatch List as the output file
1639        # from EOT tool.
1640        #
1641        SourceList = os.path.join(self._EotDir, "SourceFile.txt")
1642        GuidList = os.path.join(self._EotDir, "GuidList.txt")
1643        DispatchList = os.path.join(self._EotDir, "Dispatch.txt")
1644
1645        TempFile = []
1646        for Item in self._SourceList:
1647            FileWrite(TempFile, Item)
1648        SaveFileOnChange(SourceList, "".join(TempFile), False)
1649        TempFile = []
1650        for Key in self._GuidMap:
1651            FileWrite(TempFile, "%s %s" % (Key, self._GuidMap[Key]))
1652        SaveFileOnChange(GuidList, "".join(TempFile), False)
1653
1654        try:
1655            from Eot.EotMain import Eot
1656
1657            #
1658            # Invoke EOT tool and echo its runtime performance
1659            #
1660            EotStartTime = time.time()
1661            Eot(CommandLineOption=False, SourceFileList=SourceList, GuidList=GuidList,
1662                FvFileList=' '.join(FvFileList), Dispatch=DispatchList, IsInit=True)
1663            EotEndTime = time.time()
1664            EotDuration = time.strftime("%H:%M:%S", time.gmtime(int(round(EotEndTime - EotStartTime))))
1665            EdkLogger.quiet("EOT run time: %s\n" % EotDuration)
1666
1667            #
1668            # Parse the output of EOT tool
1669            #
1670            for Line in open(DispatchList):
1671                if len(Line.split()) < 4:
1672                    continue
1673                (Guid, Phase, FfsName, FilePath) = Line.split()
1674                Symbol = self._FfsEntryPoint.get(Guid, [FfsName, ""])[0]
1675                if len(Symbol) > self.MaxLen:
1676                    self.MaxLen = len(Symbol)
1677                self.ItemList.append((Phase, Symbol, FilePath))
1678        except:
1679            EdkLogger.quiet("(Python %s on %s\n%s)" % (platform.python_version(), sys.platform, traceback.format_exc()))
1680            EdkLogger.warn(None, "Failed to generate execution order prediction report, for some error occurred in executing EOT.")
1681
1682
1683    ##
1684    # Generate platform execution order report
1685    #
1686    # This function generates the predicted module execution order.
1687    #
1688    # @param self            The object pointer
1689    # @param File            The file object for report
1690    #
1691    def _GenerateExecutionOrderReport(self, File):
1692        self._InvokeEotTool()
1693        if len(self.ItemList) == 0:
1694            return
1695        FileWrite(File, gSectionStart)
1696        FileWrite(File, "Execution Order Prediction")
1697        FileWrite(File, "*P PEI phase")
1698        FileWrite(File, "*D DXE phase")
1699        FileWrite(File, "*E Module INF entry point name")
1700        FileWrite(File, "*N Module notification function name")
1701
1702        FileWrite(File, "Type %-*s %s" % (self.MaxLen, "Symbol", "Module INF Path"))
1703        FileWrite(File, gSectionSep)
1704        for Item in self.ItemList:
1705            FileWrite(File, "*%sE  %-*s %s" % (Item[0], self.MaxLen, Item[1], Item[2]))
1706
1707        FileWrite(File, gSectionStart)
1708
1709    ##
1710    # Generate Fixed Address report.
1711    #
1712    # This function generate the predicted fixed address report for a module
1713    # specified by Guid.
1714    #
1715    # @param self            The object pointer
1716    # @param File            The file object for report
1717    # @param Guid            The module Guid value.
1718    # @param NotifyList      The list of all notify function in a module
1719    #
1720    def _GenerateFixedAddressReport(self, File, Guid, NotifyList):
1721        self._ParseMapFile()
1722        FixedAddressList = self.FixedMapDict.get(Guid)
1723        if not FixedAddressList:
1724            return
1725
1726        FileWrite(File, gSubSectionStart)
1727        FileWrite(File, "Fixed Address Prediction")
1728        FileWrite(File, "*I  Image Loading Address")
1729        FileWrite(File, "*E  Entry Point Address")
1730        FileWrite(File, "*N  Notification Function Address")
1731        FileWrite(File, "*F  Flash Address")
1732        FileWrite(File, "*M  Memory Address")
1733        FileWrite(File, "*S  SMM RAM Offset")
1734        FileWrite(File, "TOM Top of Memory")
1735
1736        FileWrite(File, "Type Address           Name")
1737        FileWrite(File, gSubSectionSep)
1738        for Item in FixedAddressList:
1739            Type = Item[0]
1740            Value = Item[1]
1741            Symbol = Item[2]
1742            if Symbol == "*I":
1743                Name = "(Image Base)"
1744            elif Symbol == "*E":
1745                Name = self._FfsEntryPoint.get(Guid, ["", "_ModuleEntryPoint"])[1]
1746            elif Symbol in NotifyList:
1747                Name = Symbol
1748                Symbol = "*N"
1749            else:
1750                continue
1751
1752            if "Flash" in Type:
1753                Symbol += "F"
1754            elif "Memory" in Type:
1755                Symbol += "M"
1756            else:
1757                Symbol += "S"
1758
1759            if Value[0] == "-":
1760                Value = "TOM" + Value
1761
1762            FileWrite(File, "%s  %-16s  %s" % (Symbol, Value, Name))
1763
1764    ##
1765    # Generate report for the prediction part
1766    #
1767    # This function generate the predicted fixed address report for a module or
1768    # predicted module execution order for a platform.
1769    # If the input Guid is None, then, it generates the predicted module execution order;
1770    # otherwise it generated the module fixed loading address for the module specified by
1771    # Guid.
1772    #
1773    # @param self            The object pointer
1774    # @param File            The file object for report
1775    # @param Guid            The module Guid value.
1776    #
1777    def GenerateReport(self, File, Guid):
1778        if Guid:
1779            self._GenerateFixedAddressReport(File, Guid.upper(), [])
1780        else:
1781            self._GenerateExecutionOrderReport(File)
1782
1783##
1784# Reports FD region information
1785#
1786# This class reports the FD subsection in the build report file.
1787# It collects region information of platform flash device.
1788# If the region is a firmware volume, it lists the set of modules
1789# and its space information; otherwise, it only lists its region name,
1790# base address and size in its sub-section header.
1791# If there are nesting FVs, the nested FVs will list immediate after
1792# this FD region subsection
1793#
1794class FdRegionReport(object):
1795    ##
1796    # Discover all the nested FV name list.
1797    #
1798    # This is an internal worker function to discover the all the nested FV information
1799    # in the parent firmware volume. It uses deep first search algorithm recursively to
1800    # find all the FV list name and append them to the list.
1801    #
1802    # @param self            The object pointer
1803    # @param FvName          The name of current firmware file system
1804    # @param Wa              Workspace context information
1805    #
1806    def _DiscoverNestedFvList(self, FvName, Wa):
1807        FvDictKey=FvName.upper()
1808        if FvDictKey in Wa.FdfProfile.FvDict:
1809            for Ffs in Wa.FdfProfile.FvDict[FvName.upper()].FfsList:
1810                for Section in Ffs.SectionList:
1811                    try:
1812                        for FvSection in Section.SectionList:
1813                            if FvSection.FvName in self.FvList:
1814                                continue
1815                            self._GuidsDb[Ffs.NameGuid.upper()] = FvSection.FvName
1816                            self.FvList.append(FvSection.FvName)
1817                            self.FvInfo[FvSection.FvName] = ("Nested FV", 0, 0)
1818                            self._DiscoverNestedFvList(FvSection.FvName, Wa)
1819                    except AttributeError:
1820                        pass
1821
1822    ##
1823    # Constructor function for class FdRegionReport
1824    #
1825    # This constructor function generates FdRegionReport object for a specified FdRegion.
1826    # If the FdRegion is a firmware volume, it will recursively find all its nested Firmware
1827    # volume list. This function also collects GUID map in order to dump module identification
1828    # in the final report.
1829    #
1830    # @param self:           The object pointer
1831    # @param FdRegion        The current FdRegion object
1832    # @param Wa              Workspace context information
1833    #
1834    def __init__(self, FdRegion, Wa):
1835        self.Type = FdRegion.RegionType
1836        self.BaseAddress = FdRegion.Offset
1837        self.Size = FdRegion.Size
1838        self.FvList = []
1839        self.FvInfo = {}
1840        self._GuidsDb = {}
1841        self._FvDir = Wa.FvDir
1842        self._WorkspaceDir = Wa.WorkspaceDir
1843
1844        #
1845        # If the input FdRegion is not a firmware volume,
1846        # we are done.
1847        #
1848        if self.Type != BINARY_FILE_TYPE_FV:
1849            return
1850
1851        #
1852        # Find all nested FVs in the FdRegion
1853        #
1854        for FvName in FdRegion.RegionDataList:
1855            if FvName in self.FvList:
1856                continue
1857            self.FvList.append(FvName)
1858            self.FvInfo[FvName] = ("Fd Region", self.BaseAddress, self.Size)
1859            self._DiscoverNestedFvList(FvName, Wa)
1860
1861        PlatformPcds = {}
1862        #
1863        # Collect PCDs declared in DEC files.
1864        #
1865        for Pa in Wa.AutoGenObjectList:
1866            for Package in Pa.PackageList:
1867                for (TokenCName, TokenSpaceGuidCName, DecType) in Package.Pcds:
1868                    DecDefaultValue = Package.Pcds[TokenCName, TokenSpaceGuidCName, DecType].DefaultValue
1869                    PlatformPcds[(TokenCName, TokenSpaceGuidCName)] = DecDefaultValue
1870        #
1871        # Collect PCDs defined in DSC file
1872        #
1873        for Pa in Wa.AutoGenObjectList:
1874            for (TokenCName, TokenSpaceGuidCName) in Pa.Platform.Pcds:
1875                DscDefaultValue = Pa.Platform.Pcds[(TokenCName, TokenSpaceGuidCName)].DefaultValue
1876                PlatformPcds[(TokenCName, TokenSpaceGuidCName)] = DscDefaultValue
1877
1878        #
1879        # Add PEI and DXE a priori files GUIDs defined in PI specification.
1880        #
1881        self._GuidsDb[PEI_APRIORI_GUID] = "PEI Apriori"
1882        self._GuidsDb[DXE_APRIORI_GUID] = "DXE Apriori"
1883        #
1884        # Add ACPI table storage file
1885        #
1886        self._GuidsDb["7E374E25-8E01-4FEE-87F2-390C23C606CD"] = "ACPI table storage"
1887
1888        for Pa in Wa.AutoGenObjectList:
1889            for ModuleKey in Pa.Platform.Modules:
1890                M = Pa.Platform.Modules[ModuleKey].M
1891                InfPath = mws.join(Wa.WorkspaceDir, M.MetaFile.File)
1892                self._GuidsDb[M.Guid.upper()] = "%s (%s)" % (M.Module.BaseName, InfPath)
1893
1894        #
1895        # Collect the GUID map in the FV firmware volume
1896        #
1897        for FvName in self.FvList:
1898            FvDictKey=FvName.upper()
1899            if FvDictKey in Wa.FdfProfile.FvDict:
1900                for Ffs in Wa.FdfProfile.FvDict[FvName.upper()].FfsList:
1901                    try:
1902                        #
1903                        # collect GUID map for binary EFI file in FDF file.
1904                        #
1905                        Guid = Ffs.NameGuid.upper()
1906                        Match = gPcdGuidPattern.match(Ffs.NameGuid)
1907                        if Match:
1908                            PcdTokenspace = Match.group(1)
1909                            PcdToken = Match.group(2)
1910                            if (PcdToken, PcdTokenspace) in PlatformPcds:
1911                                GuidValue = PlatformPcds[(PcdToken, PcdTokenspace)]
1912                                Guid = GuidStructureByteArrayToGuidString(GuidValue).upper()
1913                        for Section in Ffs.SectionList:
1914                            try:
1915                                ModuleSectFile = mws.join(Wa.WorkspaceDir, Section.SectFileName)
1916                                self._GuidsDb[Guid] = ModuleSectFile
1917                            except AttributeError:
1918                                pass
1919                    except AttributeError:
1920                        pass
1921
1922
1923    ##
1924    # Internal worker function to generate report for the FD region
1925    #
1926    # This internal worker function to generate report for the FD region.
1927    # It the type is firmware volume, it lists offset and module identification.
1928    #
1929    # @param self            The object pointer
1930    # @param File            The file object for report
1931    # @param Title           The title for the FD subsection
1932    # @param BaseAddress     The base address for the FD region
1933    # @param Size            The size of the FD region
1934    # @param FvName          The FV name if the FD region is a firmware volume
1935    #
1936    def _GenerateReport(self, File, Title, Type, BaseAddress, Size=0, FvName=None):
1937        FileWrite(File, gSubSectionStart)
1938        FileWrite(File, Title)
1939        FileWrite(File, "Type:               %s" % Type)
1940        FileWrite(File, "Base Address:       0x%X" % BaseAddress)
1941
1942        if self.Type == BINARY_FILE_TYPE_FV:
1943            FvTotalSize = 0
1944            FvTakenSize = 0
1945            FvFreeSize  = 0
1946            if FvName.upper().endswith('.FV'):
1947                FileExt = FvName + ".txt"
1948            else:
1949                FileExt = FvName + ".Fv.txt"
1950
1951            if not os.path.isfile(FileExt):
1952                FvReportFileName = mws.join(self._WorkspaceDir, FileExt)
1953                if not os.path.isfile(FvReportFileName):
1954                    FvReportFileName = os.path.join(self._FvDir, FileExt)
1955            try:
1956                #
1957                # Collect size info in the firmware volume.
1958                #
1959                FvReport = open(FvReportFileName).read()
1960                Match = gFvTotalSizePattern.search(FvReport)
1961                if Match:
1962                    FvTotalSize = int(Match.group(1), 16)
1963                Match = gFvTakenSizePattern.search(FvReport)
1964                if Match:
1965                    FvTakenSize = int(Match.group(1), 16)
1966                FvFreeSize = FvTotalSize - FvTakenSize
1967                #
1968                # Write size information to the report file.
1969                #
1970                FileWrite(File, "Size:               0x%X (%.0fK)" % (FvTotalSize, FvTotalSize / 1024.0))
1971                FileWrite(File, "Fv Name:            %s (%.1f%% Full)" % (FvName, FvTakenSize * 100.0 / FvTotalSize))
1972                FileWrite(File, "Occupied Size:      0x%X (%.0fK)" % (FvTakenSize, FvTakenSize / 1024.0))
1973                FileWrite(File, "Free Size:          0x%X (%.0fK)" % (FvFreeSize, FvFreeSize / 1024.0))
1974                FileWrite(File, "Offset     Module")
1975                FileWrite(File, gSubSectionSep)
1976                #
1977                # Write module offset and module identification to the report file.
1978                #
1979                OffsetInfo = {}
1980                for Match in gOffsetGuidPattern.finditer(FvReport):
1981                    Guid = Match.group(2).upper()
1982                    OffsetInfo[Match.group(1)] = self._GuidsDb.get(Guid, Guid)
1983                OffsetList = sorted(OffsetInfo.keys())
1984                for Offset in OffsetList:
1985                    FileWrite (File, "%s %s" % (Offset, OffsetInfo[Offset]))
1986            except IOError:
1987                EdkLogger.warn(None, "Fail to read report file", FvReportFileName)
1988        else:
1989            FileWrite(File, "Size:               0x%X (%.0fK)" % (Size, Size / 1024.0))
1990        FileWrite(File, gSubSectionEnd)
1991
1992    ##
1993    # Generate report for the FD region
1994    #
1995    # This function generates report for the FD region.
1996    #
1997    # @param self            The object pointer
1998    # @param File            The file object for report
1999    #
2000    def GenerateReport(self, File):
2001        if (len(self.FvList) > 0):
2002            for FvItem in self.FvList:
2003                Info = self.FvInfo[FvItem]
2004                self._GenerateReport(File, Info[0], TAB_FV_DIRECTORY, Info[1], Info[2], FvItem)
2005        else:
2006            self._GenerateReport(File, "FD Region", self.Type, self.BaseAddress, self.Size)
2007
2008##
2009# Reports FD information
2010#
2011# This class reports the FD section in the build report file.
2012# It collects flash device information for a platform.
2013#
2014class FdReport(object):
2015    ##
2016    # Constructor function for class FdReport
2017    #
2018    # This constructor function generates FdReport object for a specified
2019    # firmware device.
2020    #
2021    # @param self            The object pointer
2022    # @param Fd              The current Firmware device object
2023    # @param Wa              Workspace context information
2024    #
2025    def __init__(self, Fd, Wa):
2026        self.FdName = Fd.FdUiName
2027        self.BaseAddress = Fd.BaseAddress
2028        self.Size = Fd.Size
2029        self.FdRegionList = [FdRegionReport(FdRegion, Wa) for FdRegion in Fd.RegionList]
2030        self.FvPath = os.path.join(Wa.BuildDir, TAB_FV_DIRECTORY)
2031        self.VPDBaseAddress = 0
2032        self.VPDSize = 0
2033        for index, FdRegion in enumerate(Fd.RegionList):
2034            if str(FdRegion.RegionType) is 'FILE' and Wa.Platform.VpdToolGuid in str(FdRegion.RegionDataList):
2035                self.VPDBaseAddress = self.FdRegionList[index].BaseAddress
2036                self.VPDSize = self.FdRegionList[index].Size
2037                break
2038
2039    ##
2040    # Generate report for the firmware device.
2041    #
2042    # This function generates report for the firmware device.
2043    #
2044    # @param self            The object pointer
2045    # @param File            The file object for report
2046    #
2047    def GenerateReport(self, File):
2048        FileWrite(File, gSectionStart)
2049        FileWrite(File, "Firmware Device (FD)")
2050        FileWrite(File, "FD Name:            %s" % self.FdName)
2051        FileWrite(File, "Base Address:       %s" % self.BaseAddress)
2052        FileWrite(File, "Size:               0x%X (%.0fK)" % (self.Size, self.Size / 1024.0))
2053        if len(self.FdRegionList) > 0:
2054            FileWrite(File, gSectionSep)
2055            for FdRegionItem in self.FdRegionList:
2056                FdRegionItem.GenerateReport(File)
2057
2058        if VPDPcdList:
2059            VPDPcdList.sort(key=lambda x: int(x[2], 0))
2060            FileWrite(File, gSubSectionStart)
2061            FileWrite(File, "FD VPD Region")
2062            FileWrite(File, "Base Address:       0x%X" % self.VPDBaseAddress)
2063            FileWrite(File, "Size:               0x%X (%.0fK)" % (self.VPDSize, self.VPDSize / 1024.0))
2064            FileWrite(File, gSubSectionSep)
2065            for item in VPDPcdList:
2066                # Add BaseAddress for offset
2067                Offset = '0x%08X' % (int(item[2], 16) + self.VPDBaseAddress)
2068                IsByteArray, ArrayList = ByteArrayForamt(item[-1])
2069                Skuinfo = item[1]
2070                if len(GlobalData.gSkuids) == 1 :
2071                    Skuinfo = GlobalData.gSkuids[0]
2072                if IsByteArray:
2073                    FileWrite(File, "%s | %s | %s | %s | %s" % (item[0], Skuinfo, Offset, item[3], '{'))
2074                    for Array in ArrayList:
2075                        FileWrite(File, Array)
2076                else:
2077                    FileWrite(File, "%s | %s | %s | %s | %s" % (item[0], Skuinfo, Offset, item[3], item[-1]))
2078            FileWrite(File, gSubSectionEnd)
2079        FileWrite(File, gSectionEnd)
2080
2081
2082
2083##
2084# Reports platform information
2085#
2086# This class reports the whole platform information
2087#
2088class PlatformReport(object):
2089    ##
2090    # Constructor function for class PlatformReport
2091    #
2092    # This constructor function generates PlatformReport object a platform build.
2093    # It generates report for platform summary, flash, global PCDs and detailed
2094    # module information for modules involved in platform build.
2095    #
2096    # @param self            The object pointer
2097    # @param Wa              Workspace context information
2098    # @param MaList          The list of modules in the platform build
2099    #
2100    def __init__(self, Wa, MaList, ReportType):
2101        self._WorkspaceDir = Wa.WorkspaceDir
2102        self.PlatformName = Wa.Name
2103        self.PlatformDscPath = Wa.Platform
2104        self.Architectures = " ".join(Wa.ArchList)
2105        self.ToolChain = Wa.ToolChain
2106        self.Target = Wa.BuildTarget
2107        self.OutputPath = os.path.join(Wa.WorkspaceDir, Wa.OutputDir)
2108        self.BuildEnvironment = platform.platform()
2109
2110        self.PcdReport = None
2111        if "PCD" in ReportType:
2112            self.PcdReport = PcdReport(Wa)
2113
2114        self.FdReportList = []
2115        if "FLASH" in ReportType and Wa.FdfProfile and MaList is None:
2116            for Fd in Wa.FdfProfile.FdDict:
2117                self.FdReportList.append(FdReport(Wa.FdfProfile.FdDict[Fd], Wa))
2118
2119        self.PredictionReport = None
2120        if "FIXED_ADDRESS" in ReportType or "EXECUTION_ORDER" in ReportType:
2121            self.PredictionReport = PredictionReport(Wa)
2122
2123        self.DepexParser = None
2124        if "DEPEX" in ReportType:
2125            self.DepexParser = DepexParser(Wa)
2126
2127        self.ModuleReportList = []
2128        if MaList is not None:
2129            self._IsModuleBuild = True
2130            for Ma in MaList:
2131                self.ModuleReportList.append(ModuleReport(Ma, ReportType))
2132        else:
2133            self._IsModuleBuild = False
2134            for Pa in Wa.AutoGenObjectList:
2135                ModuleAutoGenList = []
2136                for ModuleKey in Pa.Platform.Modules:
2137                    ModuleAutoGenList.append(Pa.Platform.Modules[ModuleKey].M)
2138                if GlobalData.gFdfParser is not None:
2139                    if Pa.Arch in GlobalData.gFdfParser.Profile.InfDict:
2140                        INFList = GlobalData.gFdfParser.Profile.InfDict[Pa.Arch]
2141                        for InfName in INFList:
2142                            InfClass = PathClass(NormPath(InfName), Wa.WorkspaceDir, Pa.Arch)
2143                            Ma = ModuleAutoGen(Wa, InfClass, Pa.BuildTarget, Pa.ToolChain, Pa.Arch, Wa.MetaFile)
2144                            if Ma is None:
2145                                continue
2146                            if Ma not in ModuleAutoGenList:
2147                                ModuleAutoGenList.append(Ma)
2148                for MGen in ModuleAutoGenList:
2149                    self.ModuleReportList.append(ModuleReport(MGen, ReportType))
2150
2151
2152
2153    ##
2154    # Generate report for the whole platform.
2155    #
2156    # This function generates report for platform information.
2157    # It comprises of platform summary, global PCD, flash and
2158    # module list sections.
2159    #
2160    # @param self            The object pointer
2161    # @param File            The file object for report
2162    # @param BuildDuration   The total time to build the modules
2163    # @param AutoGenTime     The total time of AutoGen Phase
2164    # @param MakeTime        The total time of Make Phase
2165    # @param GenFdsTime      The total time of GenFds Phase
2166    # @param ReportType      The kind of report items in the final report file
2167    #
2168    def GenerateReport(self, File, BuildDuration, AutoGenTime, MakeTime, GenFdsTime, ReportType):
2169        FileWrite(File, "Platform Summary")
2170        FileWrite(File, "Platform Name:        %s" % self.PlatformName)
2171        FileWrite(File, "Platform DSC Path:    %s" % self.PlatformDscPath)
2172        FileWrite(File, "Architectures:        %s" % self.Architectures)
2173        FileWrite(File, "Tool Chain:           %s" % self.ToolChain)
2174        FileWrite(File, "Target:               %s" % self.Target)
2175        if GlobalData.gSkuids:
2176            FileWrite(File, "SKUID:                %s" % " ".join(GlobalData.gSkuids))
2177        if GlobalData.gDefaultStores:
2178            FileWrite(File, "DefaultStore:         %s" % " ".join(GlobalData.gDefaultStores))
2179        FileWrite(File, "Output Path:          %s" % self.OutputPath)
2180        FileWrite(File, "Build Environment:    %s" % self.BuildEnvironment)
2181        FileWrite(File, "Build Duration:       %s" % BuildDuration)
2182        if AutoGenTime:
2183            FileWrite(File, "AutoGen Duration:     %s" % AutoGenTime)
2184        if MakeTime:
2185            FileWrite(File, "Make Duration:        %s" % MakeTime)
2186        if GenFdsTime:
2187            FileWrite(File, "GenFds Duration:      %s" % GenFdsTime)
2188        FileWrite(File, "Report Content:       %s" % ", ".join(ReportType))
2189
2190        if GlobalData.MixedPcd:
2191            FileWrite(File, gSectionStart)
2192            FileWrite(File, "The following PCDs use different access methods:")
2193            FileWrite(File, gSectionSep)
2194            for PcdItem in GlobalData.MixedPcd:
2195                FileWrite(File, "%s.%s" % (str(PcdItem[1]), str(PcdItem[0])))
2196            FileWrite(File, gSectionEnd)
2197
2198        if not self._IsModuleBuild:
2199            if "PCD" in ReportType:
2200                self.PcdReport.GenerateReport(File, None)
2201
2202            if "FLASH" in ReportType:
2203                for FdReportListItem in self.FdReportList:
2204                    FdReportListItem.GenerateReport(File)
2205
2206        for ModuleReportItem in self.ModuleReportList:
2207            ModuleReportItem.GenerateReport(File, self.PcdReport, self.PredictionReport, self.DepexParser, ReportType)
2208
2209        if not self._IsModuleBuild:
2210            if "EXECUTION_ORDER" in ReportType:
2211                self.PredictionReport.GenerateReport(File, None)
2212
2213## BuildReport class
2214#
2215#  This base class contain the routines to collect data and then
2216#  applies certain format to the output report
2217#
2218class BuildReport(object):
2219    ##
2220    # Constructor function for class BuildReport
2221    #
2222    # This constructor function generates BuildReport object a platform build.
2223    # It generates report for platform summary, flash, global PCDs and detailed
2224    # module information for modules involved in platform build.
2225    #
2226    # @param self            The object pointer
2227    # @param ReportFile      The file name to save report file
2228    # @param ReportType      The kind of report items in the final report file
2229    #
2230    def __init__(self, ReportFile, ReportType):
2231        self.ReportFile = ReportFile
2232        if ReportFile:
2233            self.ReportList = []
2234            self.ReportType = []
2235            if ReportType:
2236                for ReportTypeItem in ReportType:
2237                    if ReportTypeItem not in self.ReportType:
2238                        self.ReportType.append(ReportTypeItem)
2239            else:
2240                self.ReportType = ["PCD", "LIBRARY", "BUILD_FLAGS", "DEPEX", "HASH", "FLASH", "FIXED_ADDRESS"]
2241    ##
2242    # Adds platform report to the list
2243    #
2244    # This function adds a platform report to the final report list.
2245    #
2246    # @param self            The object pointer
2247    # @param Wa              Workspace context information
2248    # @param MaList          The list of modules in the platform build
2249    #
2250    def AddPlatformReport(self, Wa, MaList=None):
2251        if self.ReportFile:
2252            self.ReportList.append((Wa, MaList))
2253
2254    ##
2255    # Generates the final report.
2256    #
2257    # This function generates platform build report. It invokes GenerateReport()
2258    # method for every platform report in the list.
2259    #
2260    # @param self            The object pointer
2261    # @param BuildDuration   The total time to build the modules
2262    # @param AutoGenTime     The total time of AutoGen phase
2263    # @param MakeTime        The total time of Make phase
2264    # @param GenFdsTime      The total time of GenFds phase
2265    #
2266    def GenerateReport(self, BuildDuration, AutoGenTime, MakeTime, GenFdsTime):
2267        if self.ReportFile:
2268            try:
2269                File = []
2270                for (Wa, MaList) in self.ReportList:
2271                    PlatformReport(Wa, MaList, self.ReportType).GenerateReport(File, BuildDuration, AutoGenTime, MakeTime, GenFdsTime, self.ReportType)
2272                Content = FileLinesSplit(''.join(File), gLineMaxLength)
2273                SaveFileOnChange(self.ReportFile, Content, False)
2274                EdkLogger.quiet("Build report can be found at %s" % os.path.abspath(self.ReportFile))
2275            except IOError:
2276                EdkLogger.error(None, FILE_WRITE_FAILURE, ExtraData=self.ReportFile)
2277            except:
2278                EdkLogger.error("BuildReport", CODE_ERROR, "Unknown fatal error when generating build report", ExtraData=self.ReportFile, RaiseError=False)
2279                EdkLogger.quiet("(Python %s on %s\n%s)" % (platform.python_version(), sys.platform, traceback.format_exc()))
2280
2281# This acts like the main() function for the script, unless it is 'import'ed into another script.
2282if __name__ == '__main__':
2283    pass
2284
2285