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.ModuleAutoGen 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*Type=\w+\)\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.ModuleArch = M.Arch
562        self.FileGuid = M.Guid
563        self.Size = 0
564        self.BuildTimeStamp = None
565        self.Hash = 0
566        self.DriverType = ""
567        if not M.IsLibrary:
568            ModuleType = M.ModuleType
569            if not ModuleType:
570                ModuleType = COMPONENT_TO_MODULE_MAP_DICT.get(M.ComponentType, "")
571            #
572            # If a module complies to PI 1.1, promote Module type to "SMM_DRIVER"
573            #
574            if ModuleType == SUP_MODULE_DXE_SMM_DRIVER:
575                PiSpec = M.Module.Specification.get("PI_SPECIFICATION_VERSION", "0x00010000")
576                if int(PiSpec, 0) >= 0x0001000A:
577                    ModuleType = "SMM_DRIVER"
578            self.DriverType = gDriverTypeMap.get(ModuleType, "0x2 (FREE_FORM)")
579        self.UefiSpecVersion = M.Module.Specification.get("UEFI_SPECIFICATION_VERSION", "")
580        self.PiSpecVersion = M.Module.Specification.get("PI_SPECIFICATION_VERSION", "")
581        self.PciDeviceId = M.Module.Defines.get("PCI_DEVICE_ID", "")
582        self.PciVendorId = M.Module.Defines.get("PCI_VENDOR_ID", "")
583        self.PciClassCode = M.Module.Defines.get("PCI_CLASS_CODE", "")
584        self.BuildTime = M.BuildTime
585
586        self._BuildDir = M.BuildDir
587        self.ModulePcdSet = {}
588        if "PCD" in ReportType:
589            #
590            # Collect all module used PCD set: module INF referenced directly or indirectly.
591            # It also saves module INF default values of them in case they exist.
592            #
593            for Pcd in M.ModulePcdList + M.LibraryPcdList:
594                self.ModulePcdSet.setdefault((Pcd.TokenCName, Pcd.TokenSpaceGuidCName, Pcd.Type), (Pcd.InfDefaultValue, Pcd.DefaultValue))
595
596        self.LibraryReport = None
597        if "LIBRARY" in ReportType:
598            self.LibraryReport = LibraryReport(M)
599
600        self.DepexReport = None
601        if "DEPEX" in ReportType:
602            self.DepexReport = DepexReport(M)
603
604        if "BUILD_FLAGS" in ReportType:
605            self.BuildFlagsReport = BuildFlagsReport(M)
606
607
608    ##
609    # Generate report for module information
610    #
611    # This function generates report for separate module expression
612    # in a platform build.
613    #
614    # @param self                   The object pointer
615    # @param File                   The file object for report
616    # @param GlobalPcdReport        The platform global PCD report object
617    # @param GlobalPredictionReport The platform global Prediction report object
618    # @param GlobalDepexParser      The platform global Dependency expression parser object
619    # @param ReportType             The kind of report items in the final report file
620    #
621    def GenerateReport(self, File, GlobalPcdReport, GlobalPredictionReport, GlobalDepexParser, ReportType):
622        FileWrite(File, gSectionStart)
623
624        FwReportFileName = os.path.join(self._BuildDir, "OUTPUT", self.ModuleName + ".txt")
625        if os.path.isfile(FwReportFileName):
626            try:
627                FileContents = open(FwReportFileName).read()
628                Match = gModuleSizePattern.search(FileContents)
629                if Match:
630                    self.Size = int(Match.group(1))
631
632                Match = gTimeStampPattern.search(FileContents)
633                if Match:
634                    self.BuildTimeStamp = datetime.utcfromtimestamp(int(Match.group(1)))
635            except IOError:
636                EdkLogger.warn(None, "Fail to read report file", FwReportFileName)
637
638        if "HASH" in ReportType:
639            OutputDir = os.path.join(self._BuildDir, "OUTPUT")
640            DefaultEFIfile = os.path.join(OutputDir, self.ModuleName + ".efi")
641            if os.path.isfile(DefaultEFIfile):
642                Tempfile = os.path.join(OutputDir, self.ModuleName + "_hash.tmp")
643                # rebase the efi image since its base address may not zero
644                cmd = ["GenFw", "--rebase", str(0), "-o", Tempfile, DefaultEFIfile]
645                try:
646                    PopenObject = subprocess.Popen(' '.join(cmd), stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
647                except Exception as X:
648                    EdkLogger.error("GenFw", COMMAND_FAILURE, ExtraData="%s: %s" % (str(X), cmd[0]))
649                EndOfProcedure = threading.Event()
650                EndOfProcedure.clear()
651                if PopenObject.stderr:
652                    StdErrThread = threading.Thread(target=ReadMessage, args=(PopenObject.stderr, EdkLogger.quiet, EndOfProcedure))
653                    StdErrThread.setName("STDERR-Redirector")
654                    StdErrThread.setDaemon(False)
655                    StdErrThread.start()
656                # waiting for program exit
657                PopenObject.wait()
658                if PopenObject.stderr:
659                    StdErrThread.join()
660                if PopenObject.returncode != 0:
661                    EdkLogger.error("GenFw", COMMAND_FAILURE, "Failed to generate firmware hash image for %s" % (DefaultEFIfile))
662                if os.path.isfile(Tempfile):
663                    self.Hash = hashlib.sha1()
664                    buf = open(Tempfile, 'rb').read()
665                    if self.Hash.update(buf):
666                        self.Hash = self.Hash.update(buf)
667                    self.Hash = self.Hash.hexdigest()
668                    os.remove(Tempfile)
669
670        FileWrite(File, "Module Summary")
671        FileWrite(File, "Module Name:          %s" % self.ModuleName)
672        FileWrite(File, "Module Arch:          %s" % self.ModuleArch)
673        FileWrite(File, "Module INF Path:      %s" % self.ModuleInfPath)
674        FileWrite(File, "File GUID:            %s" % self.FileGuid)
675        if self.Size:
676            FileWrite(File, "Size:                 0x%X (%.2fK)" % (self.Size, self.Size / 1024.0))
677        if self.Hash:
678            FileWrite(File, "SHA1 HASH:            %s *%s" % (self.Hash, self.ModuleName + ".efi"))
679        if self.BuildTimeStamp:
680            FileWrite(File, "Build Time Stamp:     %s" % self.BuildTimeStamp)
681        if self.BuildTime:
682            FileWrite(File, "Module Build Time:    %s" % self.BuildTime)
683        if self.DriverType:
684            FileWrite(File, "Driver Type:          %s" % self.DriverType)
685        if self.UefiSpecVersion:
686            FileWrite(File, "UEFI Spec Version:    %s" % self.UefiSpecVersion)
687        if self.PiSpecVersion:
688            FileWrite(File, "PI Spec Version:      %s" % self.PiSpecVersion)
689        if self.PciDeviceId:
690            FileWrite(File, "PCI Device ID:        %s" % self.PciDeviceId)
691        if self.PciVendorId:
692            FileWrite(File, "PCI Vendor ID:        %s" % self.PciVendorId)
693        if self.PciClassCode:
694            FileWrite(File, "PCI Class Code:       %s" % self.PciClassCode)
695
696        FileWrite(File, gSectionSep)
697
698        if "PCD" in ReportType:
699            GlobalPcdReport.GenerateReport(File, self.ModulePcdSet,self.FileGuid)
700
701        if "LIBRARY" in ReportType:
702            self.LibraryReport.GenerateReport(File)
703
704        if "DEPEX" in ReportType:
705            self.DepexReport.GenerateReport(File, GlobalDepexParser)
706
707        if "BUILD_FLAGS" in ReportType:
708            self.BuildFlagsReport.GenerateReport(File)
709
710        if "FIXED_ADDRESS" in ReportType and self.FileGuid:
711            GlobalPredictionReport.GenerateReport(File, self.FileGuid)
712
713        FileWrite(File, gSectionEnd)
714
715def ReadMessage(From, To, ExitFlag):
716    while True:
717        # read one line a time
718        Line = From.readline()
719        # empty string means "end"
720        if Line is not None and Line != b"":
721            To(Line.rstrip().decode(encoding='utf-8', errors='ignore'))
722        else:
723            break
724        if ExitFlag.isSet():
725            break
726
727##
728# Reports platform and module PCD information
729#
730# This class reports the platform PCD section and module PCD subsection
731# in the build report file.
732#
733class PcdReport(object):
734    ##
735    # Constructor function for class PcdReport
736    #
737    # This constructor function generates PcdReport object a platform build.
738    # It collects the whole PCD database from platform DSC files, platform
739    # flash description file and package DEC files.
740    #
741    # @param self            The object pointer
742    # @param Wa              Workspace context information
743    #
744    def __init__(self, Wa):
745        self.AllPcds = {}
746        self.UnusedPcds = {}
747        self.ConditionalPcds = {}
748        self.MaxLen = 0
749        self.Arch = None
750        if Wa.FdfProfile:
751            self.FdfPcdSet = Wa.FdfProfile.PcdDict
752        else:
753            self.FdfPcdSet = {}
754
755        self.DefaultStoreSingle = True
756        self.SkuSingle = True
757        if GlobalData.gDefaultStores and len(GlobalData.gDefaultStores) > 1:
758            self.DefaultStoreSingle = False
759        if GlobalData.gSkuids and len(GlobalData.gSkuids) > 1:
760            self.SkuSingle = False
761
762        self.ModulePcdOverride = {}
763        for Pa in Wa.AutoGenObjectList:
764            self.Arch = Pa.Arch
765            #
766            # Collect all platform referenced PCDs and grouped them by PCD token space
767            # GUID C Names
768            #
769            for Pcd in Pa.AllPcdList:
770                PcdList = self.AllPcds.setdefault(Pcd.TokenSpaceGuidCName, {}).setdefault(Pcd.Type, [])
771                if Pcd not in PcdList:
772                    PcdList.append(Pcd)
773                if len(Pcd.TokenCName) > self.MaxLen:
774                    self.MaxLen = len(Pcd.TokenCName)
775            #
776            # Collect the PCD defined in DSC/FDF file, but not used in module
777            #
778            UnusedPcdFullList = []
779            StructPcdDict = GlobalData.gStructurePcd.get(self.Arch, collections.OrderedDict())
780            for Name, Guid in StructPcdDict:
781                if (Name, Guid) not in Pa.Platform.Pcds:
782                    Pcd = StructPcdDict[(Name, Guid)]
783                    PcdList = self.AllPcds.setdefault(Guid, {}).setdefault(Pcd.Type, [])
784                    if Pcd not in PcdList and Pcd not in UnusedPcdFullList:
785                        UnusedPcdFullList.append(Pcd)
786            for item in Pa.Platform.Pcds:
787                Pcd = Pa.Platform.Pcds[item]
788                if not Pcd.Type:
789                    # check the Pcd in FDF file, whether it is used in module first
790                    for T in PCD_TYPE_LIST:
791                        PcdList = self.AllPcds.setdefault(Pcd.TokenSpaceGuidCName, {}).setdefault(T, [])
792                        if Pcd in PcdList:
793                            Pcd.Type = T
794                            break
795                if not Pcd.Type:
796                    PcdTypeFlag = False
797                    for package in Pa.PackageList:
798                        for T in PCD_TYPE_LIST:
799                            if (Pcd.TokenCName, Pcd.TokenSpaceGuidCName, T) in package.Pcds:
800                                Pcd.Type = T
801                                PcdTypeFlag = True
802                                if not Pcd.DatumType:
803                                    Pcd.DatumType = package.Pcds[(Pcd.TokenCName, Pcd.TokenSpaceGuidCName, T)].DatumType
804                                break
805                        if PcdTypeFlag:
806                            break
807                if not Pcd.DatumType:
808                    PcdType = Pcd.Type
809                    # Try to remove Hii and Vpd suffix
810                    if PcdType.startswith(TAB_PCDS_DYNAMIC_EX):
811                        PcdType = TAB_PCDS_DYNAMIC_EX
812                    elif PcdType.startswith(TAB_PCDS_DYNAMIC):
813                        PcdType = TAB_PCDS_DYNAMIC
814                    for package in Pa.PackageList:
815                        if (Pcd.TokenCName, Pcd.TokenSpaceGuidCName, PcdType) in package.Pcds:
816                            Pcd.DatumType = package.Pcds[(Pcd.TokenCName, Pcd.TokenSpaceGuidCName, PcdType)].DatumType
817                            break
818
819                PcdList = self.AllPcds.setdefault(Pcd.TokenSpaceGuidCName, {}).setdefault(Pcd.Type, [])
820                UnusedPcdList = self.UnusedPcds.setdefault(Pcd.TokenSpaceGuidCName, {}).setdefault(Pcd.Type, [])
821                if Pcd in UnusedPcdList:
822                    UnusedPcdList.remove(Pcd)
823                if Pcd not in PcdList and Pcd not in UnusedPcdFullList:
824                    UnusedPcdFullList.append(Pcd)
825                if len(Pcd.TokenCName) > self.MaxLen:
826                    self.MaxLen = len(Pcd.TokenCName)
827
828            if GlobalData.gConditionalPcds:
829                for PcdItem in GlobalData.gConditionalPcds:
830                    if '.' in PcdItem:
831                        (TokenSpaceGuidCName, TokenCName) = PcdItem.split('.')
832                        if (TokenCName, TokenSpaceGuidCName) in Pa.Platform.Pcds:
833                            Pcd = Pa.Platform.Pcds[(TokenCName, TokenSpaceGuidCName)]
834                            PcdList = self.ConditionalPcds.setdefault(Pcd.TokenSpaceGuidCName, {}).setdefault(Pcd.Type, [])
835                            if Pcd not in PcdList:
836                                PcdList.append(Pcd)
837
838            UnusedPcdList = []
839            if UnusedPcdFullList:
840                for Pcd in UnusedPcdFullList:
841                    if Pcd.TokenSpaceGuidCName + '.' + Pcd.TokenCName in GlobalData.gConditionalPcds:
842                        continue
843                    UnusedPcdList.append(Pcd)
844
845            for Pcd in UnusedPcdList:
846                PcdList = self.UnusedPcds.setdefault(Pcd.TokenSpaceGuidCName, {}).setdefault(Pcd.Type, [])
847                if Pcd not in PcdList:
848                    PcdList.append(Pcd)
849
850            for Module in Pa.Platform.Modules.values():
851                #
852                # Collect module override PCDs
853                #
854                for ModulePcd in Module.M.ModulePcdList + Module.M.LibraryPcdList:
855                    TokenCName = ModulePcd.TokenCName
856                    TokenSpaceGuid = ModulePcd.TokenSpaceGuidCName
857                    ModuleDefault = ModulePcd.DefaultValue
858                    ModulePath = os.path.basename(Module.M.MetaFile.File)
859                    self.ModulePcdOverride.setdefault((TokenCName, TokenSpaceGuid), {})[ModulePath] = ModuleDefault
860
861
862        #
863        # Collect PCD DEC default value.
864        #
865        self.DecPcdDefault = {}
866        self._GuidDict = {}
867        for Pa in Wa.AutoGenObjectList:
868            for Package in Pa.PackageList:
869                Guids = Package.Guids
870                self._GuidDict.update(Guids)
871                for (TokenCName, TokenSpaceGuidCName, DecType) in Package.Pcds:
872                    DecDefaultValue = Package.Pcds[TokenCName, TokenSpaceGuidCName, DecType].DefaultValue
873                    self.DecPcdDefault.setdefault((TokenCName, TokenSpaceGuidCName, DecType), DecDefaultValue)
874        #
875        # Collect PCDs defined in DSC common section
876        #
877        self.DscPcdDefault = {}
878        for Pa in Wa.AutoGenObjectList:
879            for (TokenCName, TokenSpaceGuidCName) in Pa.Platform.Pcds:
880                DscDefaultValue = Pa.Platform.Pcds[(TokenCName, TokenSpaceGuidCName)].DscDefaultValue
881                if DscDefaultValue:
882                    self.DscPcdDefault[(TokenCName, TokenSpaceGuidCName)] = DscDefaultValue
883
884    def GenerateReport(self, File, ModulePcdSet,ModuleGuid=None):
885        if not ModulePcdSet:
886            if self.ConditionalPcds:
887                self.GenerateReportDetail(File, ModulePcdSet, 1)
888            if self.UnusedPcds:
889                IsEmpty = True
890                for Token in self.UnusedPcds:
891                    TokenDict = self.UnusedPcds[Token]
892                    for Type in TokenDict:
893                        if TokenDict[Type]:
894                            IsEmpty = False
895                            break
896                    if not IsEmpty:
897                        break
898                if not IsEmpty:
899                    self.GenerateReportDetail(File, ModulePcdSet, 2)
900        self.GenerateReportDetail(File, ModulePcdSet,ModuleGuid = ModuleGuid)
901
902    ##
903    # Generate report for PCD information
904    #
905    # This function generates report for separate module expression
906    # in a platform build.
907    #
908    # @param self            The object pointer
909    # @param File            The file object for report
910    # @param ModulePcdSet    Set of all PCDs referenced by module or None for
911    #                        platform PCD report
912    # @param ReportySubType  0 means platform/module PCD report, 1 means Conditional
913    #                        directives section report, 2 means Unused Pcds section report
914    # @param DscOverridePcds Module DSC override PCDs set
915    #
916    def GenerateReportDetail(self, File, ModulePcdSet, ReportSubType = 0,ModuleGuid=None):
917        PcdDict = self.AllPcds
918        if ReportSubType == 1:
919            PcdDict = self.ConditionalPcds
920        elif ReportSubType == 2:
921            PcdDict = self.UnusedPcds
922
923        if not ModulePcdSet:
924            FileWrite(File, gSectionStart)
925            if ReportSubType == 1:
926                FileWrite(File, "Conditional Directives used by the build system")
927            elif ReportSubType == 2:
928                FileWrite(File, "PCDs not used by modules or in conditional directives")
929            else:
930                FileWrite(File, "Platform Configuration Database Report")
931
932            FileWrite(File, "  *B  - PCD override in the build option")
933            FileWrite(File, "  *P  - Platform scoped PCD override in DSC file")
934            FileWrite(File, "  *F  - Platform scoped PCD override in FDF file")
935            if not ReportSubType:
936                FileWrite(File, "  *M  - Module scoped PCD override")
937            FileWrite(File, gSectionSep)
938        else:
939            if not ReportSubType and ModulePcdSet:
940                #
941                # For module PCD sub-section
942                #
943                FileWrite(File, gSubSectionStart)
944                FileWrite(File, TAB_BRG_PCD)
945                FileWrite(File, gSubSectionSep)
946        AllPcdDict = {}
947        for Key in PcdDict:
948            AllPcdDict[Key] = {}
949            for Type in PcdDict[Key]:
950                for Pcd in PcdDict[Key][Type]:
951                    AllPcdDict[Key][(Pcd.TokenCName, Type)] = Pcd
952        for Key in sorted(AllPcdDict):
953            #
954            # Group PCD by their token space GUID C Name
955            #
956            First = True
957            for PcdTokenCName, Type in sorted(AllPcdDict[Key]):
958                #
959                # Group PCD by their usage type
960                #
961                Pcd = AllPcdDict[Key][(PcdTokenCName, Type)]
962                TypeName, DecType = gPcdTypeMap.get(Type, ("", Type))
963                MixedPcdFlag = False
964                if GlobalData.MixedPcd:
965                    for PcdKey in GlobalData.MixedPcd:
966                        if (Pcd.TokenCName, Pcd.TokenSpaceGuidCName) in GlobalData.MixedPcd[PcdKey]:
967                            PcdTokenCName = PcdKey[0]
968                            MixedPcdFlag = True
969                    if MixedPcdFlag and not ModulePcdSet:
970                        continue
971                #
972                # Get PCD default value and their override relationship
973                #
974                DecDefaultValue = self.DecPcdDefault.get((Pcd.TokenCName, Pcd.TokenSpaceGuidCName, DecType))
975                DscDefaultValue = self.DscPcdDefault.get((Pcd.TokenCName, Pcd.TokenSpaceGuidCName))
976                DscDefaultValBak = DscDefaultValue
977                Field = ''
978                for (CName, Guid, Field) in self.FdfPcdSet:
979                    if CName == PcdTokenCName and Guid == Key:
980                        DscDefaultValue = self.FdfPcdSet[(CName, Guid, Field)]
981                        break
982                if DscDefaultValue != DscDefaultValBak:
983                    try:
984                        DscDefaultValue = ValueExpressionEx(DscDefaultValue, Pcd.DatumType, self._GuidDict)(True)
985                    except BadExpression as DscDefaultValue:
986                        EdkLogger.error('BuildReport', FORMAT_INVALID, "PCD Value: %s, Type: %s" %(DscDefaultValue, Pcd.DatumType))
987
988                InfDefaultValue = None
989
990                PcdValue = DecDefaultValue
991                if DscDefaultValue:
992                    PcdValue = DscDefaultValue
993                #The DefaultValue of StructurePcd already be the latest, no need to update.
994                if not self.IsStructurePcd(Pcd.TokenCName, Pcd.TokenSpaceGuidCName):
995                    Pcd.DefaultValue = PcdValue
996                PcdComponentValue = None
997                if ModulePcdSet is not None:
998                    if (Pcd.TokenCName, Pcd.TokenSpaceGuidCName, Type) not in ModulePcdSet:
999                        continue
1000                    InfDefaultValue, PcdComponentValue = ModulePcdSet[Pcd.TokenCName, Pcd.TokenSpaceGuidCName, Type]
1001                    PcdValue = PcdComponentValue
1002                    #The DefaultValue of StructurePcd already be the latest, no need to update.
1003                    if not self.IsStructurePcd(Pcd.TokenCName, Pcd.TokenSpaceGuidCName):
1004                        Pcd.DefaultValue = PcdValue
1005                    if InfDefaultValue:
1006                        try:
1007                            InfDefaultValue = ValueExpressionEx(InfDefaultValue, Pcd.DatumType, self._GuidDict)(True)
1008                        except BadExpression as InfDefaultValue:
1009                            EdkLogger.error('BuildReport', FORMAT_INVALID, "PCD Value: %s, Type: %s" % (InfDefaultValue, Pcd.DatumType))
1010                    if InfDefaultValue == "":
1011                        InfDefaultValue = None
1012
1013                BuildOptionMatch = False
1014                if GlobalData.BuildOptionPcd:
1015                    for pcd in GlobalData.BuildOptionPcd:
1016                        if (Pcd.TokenSpaceGuidCName, Pcd.TokenCName) == (pcd[0], pcd[1]):
1017                            if pcd[2]:
1018                                continue
1019                            PcdValue = pcd[3]
1020                            #The DefaultValue of StructurePcd already be the latest, no need to update.
1021                            if not self.IsStructurePcd(Pcd.TokenCName, Pcd.TokenSpaceGuidCName):
1022                                Pcd.DefaultValue = PcdValue
1023                            BuildOptionMatch = True
1024                            break
1025
1026                if First:
1027                    if ModulePcdSet is None:
1028                        FileWrite(File, "")
1029                    FileWrite(File, Key)
1030                    First = False
1031
1032
1033                if Pcd.DatumType in TAB_PCD_NUMERIC_TYPES:
1034                    if PcdValue.startswith('0') and not PcdValue.lower().startswith('0x') and \
1035                            len(PcdValue) > 1 and PcdValue.lstrip('0'):
1036                        PcdValue = PcdValue.lstrip('0')
1037                    PcdValueNumber = int(PcdValue.strip(), 0)
1038                    if DecDefaultValue is None:
1039                        DecMatch = True
1040                    else:
1041                        if DecDefaultValue.startswith('0') and not DecDefaultValue.lower().startswith('0x') and \
1042                                len(DecDefaultValue) > 1 and DecDefaultValue.lstrip('0'):
1043                            DecDefaultValue = DecDefaultValue.lstrip('0')
1044                        DecDefaultValueNumber = int(DecDefaultValue.strip(), 0)
1045                        DecMatch = (DecDefaultValueNumber == PcdValueNumber)
1046
1047                    if InfDefaultValue is None:
1048                        InfMatch = True
1049                    else:
1050                        if InfDefaultValue.startswith('0') and not InfDefaultValue.lower().startswith('0x') and \
1051                                len(InfDefaultValue) > 1 and InfDefaultValue.lstrip('0'):
1052                            InfDefaultValue = InfDefaultValue.lstrip('0')
1053                        InfDefaultValueNumber = int(InfDefaultValue.strip(), 0)
1054                        InfMatch = (InfDefaultValueNumber == PcdValueNumber)
1055
1056                    if DscDefaultValue is None:
1057                        DscMatch = True
1058                    else:
1059                        if DscDefaultValue.startswith('0') and not DscDefaultValue.lower().startswith('0x') and \
1060                                len(DscDefaultValue) > 1 and DscDefaultValue.lstrip('0'):
1061                            DscDefaultValue = DscDefaultValue.lstrip('0')
1062                        DscDefaultValueNumber = int(DscDefaultValue.strip(), 0)
1063                        DscMatch = (DscDefaultValueNumber == PcdValueNumber)
1064                else:
1065                    if DecDefaultValue is None:
1066                        DecMatch = True
1067                    else:
1068                        DecMatch = (DecDefaultValue.strip() == PcdValue.strip())
1069
1070                    if InfDefaultValue is None:
1071                        InfMatch = True
1072                    else:
1073                        InfMatch = (InfDefaultValue.strip() == PcdValue.strip())
1074
1075                    if DscDefaultValue is None:
1076                        DscMatch = True
1077                    else:
1078                        DscMatch = (DscDefaultValue.strip() == PcdValue.strip())
1079
1080                IsStructure = False
1081                if self.IsStructurePcd(Pcd.TokenCName, Pcd.TokenSpaceGuidCName):
1082                    IsStructure = True
1083                    if TypeName in ('DYNVPD', 'DEXVPD'):
1084                        SkuInfoList = Pcd.SkuInfoList
1085                    Pcd = GlobalData.gStructurePcd[self.Arch][(Pcd.TokenCName, Pcd.TokenSpaceGuidCName)]
1086                    if ModulePcdSet and ModulePcdSet.get((Pcd.TokenCName, Pcd.TokenSpaceGuidCName, Type)):
1087                        InfDefaultValue, PcdComponentValue = ModulePcdSet[Pcd.TokenCName, Pcd.TokenSpaceGuidCName, Type]
1088                        DscDefaultValBak = Pcd.DefaultValue
1089                        Pcd.DefaultValue = PcdComponentValue
1090
1091                    Pcd.DatumType = Pcd.StructName
1092                    if TypeName in ('DYNVPD', 'DEXVPD'):
1093                        Pcd.SkuInfoList = SkuInfoList
1094                    if Pcd.PcdValueFromComm or Pcd.PcdFieldValueFromComm:
1095                        BuildOptionMatch = True
1096                        DecMatch = False
1097                    elif Pcd.PcdValueFromFdf or Pcd.PcdFieldValueFromFdf:
1098                        DscDefaultValue = True
1099                        DscMatch = True
1100                        DecMatch = False
1101                    else:
1102                        if Pcd.Type in PCD_DYNAMIC_TYPE_SET | PCD_DYNAMIC_EX_TYPE_SET:
1103                            DscOverride = False
1104                            if Pcd.DefaultFromDSC:
1105                                DscOverride = True
1106                            else:
1107                                DictLen = 0
1108                                for item in Pcd.SkuOverrideValues:
1109                                    DictLen += len(Pcd.SkuOverrideValues[item])
1110                                if not DictLen:
1111                                    DscOverride = False
1112                                else:
1113                                    if not Pcd.SkuInfoList:
1114                                        OverrideValues = Pcd.SkuOverrideValues
1115                                        if OverrideValues:
1116                                            for Data in OverrideValues.values():
1117                                                Struct = list(Data.values())
1118                                                if Struct:
1119                                                    DscOverride = self.ParseStruct(Struct[0])
1120                                                    break
1121                                    else:
1122                                        SkuList = sorted(Pcd.SkuInfoList.keys())
1123                                        for Sku in SkuList:
1124                                            SkuInfo = Pcd.SkuInfoList[Sku]
1125                                            if SkuInfo.DefaultStoreDict:
1126                                                DefaultStoreList = sorted(SkuInfo.DefaultStoreDict.keys())
1127                                                for DefaultStore in DefaultStoreList:
1128                                                    OverrideValues = Pcd.SkuOverrideValues.get(Sku)
1129                                                    if OverrideValues:
1130                                                        DscOverride = self.ParseStruct(OverrideValues[DefaultStore])
1131                                                        if DscOverride:
1132                                                            break
1133                                            if DscOverride:
1134                                                break
1135                            if DscOverride:
1136                                DscDefaultValue = True
1137                                DscMatch = True
1138                                DecMatch = False
1139                            else:
1140                                DecMatch = True
1141                        else:
1142                            if Pcd.DscRawValue or (ModuleGuid and ModuleGuid.replace("-","S") in Pcd.PcdValueFromComponents):
1143                                DscDefaultValue = True
1144                                DscMatch = True
1145                                DecMatch = False
1146                            else:
1147                                DscDefaultValue = False
1148                                DecMatch = True
1149
1150                #
1151                # Report PCD item according to their override relationship
1152                #
1153                if Pcd.DatumType == 'BOOLEAN':
1154                    if DscDefaultValue:
1155                        DscDefaultValue = str(int(DscDefaultValue, 0))
1156                    if DecDefaultValue:
1157                        DecDefaultValue = str(int(DecDefaultValue, 0))
1158                    if InfDefaultValue:
1159                        InfDefaultValue = str(int(InfDefaultValue, 0))
1160                    if Pcd.DefaultValue:
1161                        Pcd.DefaultValue = str(int(Pcd.DefaultValue, 0))
1162                if DecMatch:
1163                    self.PrintPcdValue(File, Pcd, PcdTokenCName, TypeName, IsStructure, DscMatch, DscDefaultValBak, InfMatch, InfDefaultValue, DecMatch, DecDefaultValue, '  ')
1164                elif InfDefaultValue and InfMatch:
1165                    self.PrintPcdValue(File, Pcd, PcdTokenCName, TypeName, IsStructure, DscMatch, DscDefaultValBak, InfMatch, InfDefaultValue, DecMatch, DecDefaultValue, '*M')
1166                elif BuildOptionMatch:
1167                    self.PrintPcdValue(File, Pcd, PcdTokenCName, TypeName, IsStructure, DscMatch, DscDefaultValBak, InfMatch, InfDefaultValue, DecMatch, DecDefaultValue, '*B')
1168                else:
1169                    if PcdComponentValue:
1170                        self.PrintPcdValue(File, Pcd, PcdTokenCName, TypeName, IsStructure, DscMatch, DscDefaultValBak, InfMatch, PcdComponentValue, DecMatch, DecDefaultValue, '*M', ModuleGuid)
1171                    elif DscDefaultValue and DscMatch:
1172                        if (Pcd.TokenCName, Key, Field) in self.FdfPcdSet:
1173                            self.PrintPcdValue(File, Pcd, PcdTokenCName, TypeName, IsStructure, DscMatch, DscDefaultValBak, InfMatch, InfDefaultValue, DecMatch, DecDefaultValue, '*F')
1174                        else:
1175                            self.PrintPcdValue(File, Pcd, PcdTokenCName, TypeName, IsStructure, DscMatch, DscDefaultValBak, InfMatch, InfDefaultValue, DecMatch, DecDefaultValue, '*P')
1176
1177
1178                if ModulePcdSet is None:
1179                    if IsStructure:
1180                        continue
1181                    if not TypeName in ('PATCH', 'FLAG', 'FIXED'):
1182                        continue
1183                    if not BuildOptionMatch:
1184                        ModuleOverride = self.ModulePcdOverride.get((Pcd.TokenCName, Pcd.TokenSpaceGuidCName), {})
1185                        for ModulePath in ModuleOverride:
1186                            ModuleDefault = ModuleOverride[ModulePath]
1187                            if Pcd.DatumType in TAB_PCD_NUMERIC_TYPES:
1188                                if ModuleDefault.startswith('0') and not ModuleDefault.lower().startswith('0x') and \
1189                                        len(ModuleDefault) > 1 and ModuleDefault.lstrip('0'):
1190                                    ModuleDefault = ModuleDefault.lstrip('0')
1191                                ModulePcdDefaultValueNumber = int(ModuleDefault.strip(), 0)
1192                                Match = (ModulePcdDefaultValueNumber == PcdValueNumber)
1193                                if Pcd.DatumType == 'BOOLEAN':
1194                                    ModuleDefault = str(ModulePcdDefaultValueNumber)
1195                            else:
1196                                Match = (ModuleDefault.strip() == PcdValue.strip())
1197                            if Match:
1198                                continue
1199                            IsByteArray, ArrayList = ByteArrayForamt(ModuleDefault.strip())
1200                            if IsByteArray:
1201                                FileWrite(File, ' *M     %-*s = %s' % (self.MaxLen + 15, ModulePath, '{'))
1202                                for Array in ArrayList:
1203                                    FileWrite(File, Array)
1204                            else:
1205                                Value =  ModuleDefault.strip()
1206                                if Pcd.DatumType in TAB_PCD_CLEAN_NUMERIC_TYPES:
1207                                    if Value.startswith(('0x', '0X')):
1208                                        Value = '{} ({:d})'.format(Value, int(Value, 0))
1209                                    else:
1210                                        Value = "0x{:X} ({})".format(int(Value, 0), Value)
1211                                FileWrite(File, ' *M     %-*s = %s' % (self.MaxLen + 15, ModulePath, Value))
1212
1213        if ModulePcdSet is None:
1214            FileWrite(File, gSectionEnd)
1215        else:
1216            if not ReportSubType and ModulePcdSet:
1217                FileWrite(File, gSubSectionEnd)
1218
1219    def ParseStruct(self, struct):
1220        HasDscOverride = False
1221        if struct:
1222            for _, Values in list(struct.items()):
1223                for Key, value in Values.items():
1224                    if value[1] and value[1].endswith('.dsc'):
1225                        HasDscOverride = True
1226                        break
1227                if HasDscOverride == True:
1228                    break
1229        return HasDscOverride
1230
1231    def PrintPcdDefault(self, File, Pcd, IsStructure, DscMatch, DscDefaultValue, InfMatch, InfDefaultValue, DecMatch, DecDefaultValue):
1232        if not DscMatch and DscDefaultValue is not None:
1233            Value = DscDefaultValue.strip()
1234            IsByteArray, ArrayList = ByteArrayForamt(Value)
1235            if IsByteArray:
1236                FileWrite(File, '    %*s = %s' % (self.MaxLen + 19, 'DSC DEFAULT', "{"))
1237                for Array in ArrayList:
1238                    FileWrite(File, Array)
1239            else:
1240                if Pcd.DatumType in TAB_PCD_CLEAN_NUMERIC_TYPES:
1241                    if Value.startswith(('0x', '0X')):
1242                        Value = '{} ({:d})'.format(Value, int(Value, 0))
1243                    else:
1244                        Value = "0x{:X} ({})".format(int(Value, 0), Value)
1245                FileWrite(File, '    %*s = %s' % (self.MaxLen + 19, 'DSC DEFAULT', Value))
1246        if not InfMatch and InfDefaultValue is not None:
1247            Value = InfDefaultValue.strip()
1248            IsByteArray, ArrayList = ByteArrayForamt(Value)
1249            if IsByteArray:
1250                FileWrite(File, '    %*s = %s' % (self.MaxLen + 19, 'INF DEFAULT', "{"))
1251                for Array in ArrayList:
1252                    FileWrite(File, Array)
1253            else:
1254                if Pcd.DatumType in TAB_PCD_CLEAN_NUMERIC_TYPES:
1255                    if Value.startswith(('0x', '0X')):
1256                        Value = '{} ({:d})'.format(Value, int(Value, 0))
1257                    else:
1258                        Value = "0x{:X} ({})".format(int(Value, 0), Value)
1259                FileWrite(File, '    %*s = %s' % (self.MaxLen + 19, 'INF DEFAULT', Value))
1260
1261        if not DecMatch and DecDefaultValue is not None:
1262            Value = DecDefaultValue.strip()
1263            IsByteArray, ArrayList = ByteArrayForamt(Value)
1264            if IsByteArray:
1265                FileWrite(File, '    %*s = %s' % (self.MaxLen + 19, 'DEC DEFAULT', "{"))
1266                for Array in ArrayList:
1267                    FileWrite(File, Array)
1268            else:
1269                if Pcd.DatumType in TAB_PCD_CLEAN_NUMERIC_TYPES:
1270                    if Value.startswith(('0x', '0X')):
1271                        Value = '{} ({:d})'.format(Value, int(Value, 0))
1272                    else:
1273                        Value = "0x{:X} ({})".format(int(Value, 0), Value)
1274                FileWrite(File, '    %*s = %s' % (self.MaxLen + 19, 'DEC DEFAULT', Value))
1275            if IsStructure:
1276                for filedvalues in Pcd.DefaultValues.values():
1277                    self.PrintStructureInfo(File, filedvalues)
1278        if DecMatch and IsStructure:
1279            for filedvalues in Pcd.DefaultValues.values():
1280                self.PrintStructureInfo(File, filedvalues)
1281
1282    def PrintPcdValue(self, File, Pcd, PcdTokenCName, TypeName, IsStructure, DscMatch, DscDefaultValue, InfMatch, InfDefaultValue, DecMatch, DecDefaultValue, Flag = '  ',ModuleGuid=None):
1283        if not Pcd.SkuInfoList:
1284            Value = Pcd.DefaultValue
1285            IsByteArray, ArrayList = ByteArrayForamt(Value)
1286            if IsByteArray:
1287                FileWrite(File, ' %-*s   : %6s %10s = %s' % (self.MaxLen, Flag + ' ' + PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', '{'))
1288                for Array in ArrayList:
1289                    FileWrite(File, Array)
1290            else:
1291                if Pcd.DatumType in TAB_PCD_CLEAN_NUMERIC_TYPES:
1292                    if Value.startswith('0') and not Value.lower().startswith('0x') and len(Value) > 1 and Value.lstrip('0'):
1293                        Value = Value.lstrip('0')
1294                    if Value.startswith(('0x', '0X')):
1295                        Value = '{} ({:d})'.format(Value, int(Value, 0))
1296                    else:
1297                        Value = "0x{:X} ({})".format(int(Value, 0), Value)
1298                FileWrite(File, ' %-*s   : %6s %10s = %s' % (self.MaxLen, Flag + ' ' + PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', Value))
1299            if IsStructure:
1300                FiledOverrideFlag = False
1301                if (Pcd.TokenCName,Pcd.TokenSpaceGuidCName) in GlobalData.gPcdSkuOverrides:
1302                    OverrideValues = GlobalData.gPcdSkuOverrides[(Pcd.TokenCName,Pcd.TokenSpaceGuidCName)]
1303                else:
1304                    OverrideValues = Pcd.SkuOverrideValues
1305                FieldOverrideValues = None
1306                if OverrideValues:
1307                    for Data in OverrideValues.values():
1308                        Struct = list(Data.values())
1309                        if Struct:
1310                            FieldOverrideValues = Struct[0]
1311                            FiledOverrideFlag = True
1312                            break
1313                if Pcd.PcdFiledValueFromDscComponent and ModuleGuid and ModuleGuid.replace("-","S") in Pcd.PcdFiledValueFromDscComponent:
1314                    FieldOverrideValues = Pcd.PcdFiledValueFromDscComponent[ModuleGuid.replace("-","S")]
1315                if FieldOverrideValues:
1316                    OverrideFieldStruct = self.OverrideFieldValue(Pcd, FieldOverrideValues)
1317                    self.PrintStructureInfo(File, OverrideFieldStruct)
1318
1319                if not FiledOverrideFlag and (Pcd.PcdFieldValueFromComm or Pcd.PcdFieldValueFromFdf):
1320                    OverrideFieldStruct = self.OverrideFieldValue(Pcd, {})
1321                    self.PrintStructureInfo(File, OverrideFieldStruct)
1322            self.PrintPcdDefault(File, Pcd, IsStructure, DscMatch, DscDefaultValue, InfMatch, InfDefaultValue, DecMatch, DecDefaultValue)
1323        else:
1324            FirstPrint = True
1325            SkuList = sorted(Pcd.SkuInfoList.keys())
1326            for Sku in SkuList:
1327                SkuInfo = Pcd.SkuInfoList[Sku]
1328                SkuIdName = SkuInfo.SkuIdName
1329                if TypeName in ('DYNHII', 'DEXHII'):
1330                    if SkuInfo.DefaultStoreDict:
1331                        DefaultStoreList = sorted(SkuInfo.DefaultStoreDict.keys())
1332                        for DefaultStore in DefaultStoreList:
1333                            Value = SkuInfo.DefaultStoreDict[DefaultStore]
1334                            IsByteArray, ArrayList = ByteArrayForamt(Value)
1335                            if Pcd.DatumType == 'BOOLEAN':
1336                                Value = str(int(Value, 0))
1337                            if FirstPrint:
1338                                FirstPrint = False
1339                                if IsByteArray:
1340                                    if self.DefaultStoreSingle and self.SkuSingle:
1341                                        FileWrite(File, ' %-*s   : %6s %10s = %s' % (self.MaxLen, Flag + ' ' + PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', '{'))
1342                                    elif self.DefaultStoreSingle and not self.SkuSingle:
1343                                        FileWrite(File, ' %-*s   : %6s %10s %10s = %s' % (self.MaxLen, Flag + ' ' + PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', '(' + SkuIdName + ')', '{'))
1344                                    elif not self.DefaultStoreSingle and self.SkuSingle:
1345                                        FileWrite(File, ' %-*s   : %6s %10s %10s = %s' % (self.MaxLen, Flag + ' ' + PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', '(' + DefaultStore + ')', '{'))
1346                                    else:
1347                                        FileWrite(File, ' %-*s   : %6s %10s %10s %10s = %s' % (self.MaxLen, Flag + ' ' + PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', '(' + SkuIdName + ')', '(' + DefaultStore + ')', '{'))
1348                                    for Array in ArrayList:
1349                                        FileWrite(File, Array)
1350                                else:
1351                                    if Pcd.DatumType in TAB_PCD_CLEAN_NUMERIC_TYPES:
1352                                        if Value.startswith(('0x', '0X')):
1353                                            Value = '{} ({:d})'.format(Value, int(Value, 0))
1354                                        else:
1355                                            Value = "0x{:X} ({})".format(int(Value, 0), Value)
1356                                    if self.DefaultStoreSingle and self.SkuSingle:
1357                                        FileWrite(File, ' %-*s   : %6s %10s = %s' % (self.MaxLen, Flag + ' ' + PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', Value))
1358                                    elif self.DefaultStoreSingle and not self.SkuSingle:
1359                                        FileWrite(File, ' %-*s   : %6s %10s %10s = %s' % (self.MaxLen, Flag + ' ' + PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', '(' + SkuIdName + ')', Value))
1360                                    elif not self.DefaultStoreSingle and self.SkuSingle:
1361                                        FileWrite(File, ' %-*s   : %6s %10s %10s = %s' % (self.MaxLen, Flag + ' ' + PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', '(' + DefaultStore + ')', Value))
1362                                    else:
1363                                        FileWrite(File, ' %-*s   : %6s %10s %10s %10s = %s' % (self.MaxLen, Flag + ' ' + PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', '(' + SkuIdName + ')', '(' + DefaultStore + ')', Value))
1364                            else:
1365                                if IsByteArray:
1366                                    if self.DefaultStoreSingle and self.SkuSingle:
1367                                        FileWrite(File, ' %-*s   : %6s %10s = %s' % (self.MaxLen, ' ', TypeName, '(' + Pcd.DatumType + ')', '{'))
1368                                    elif self.DefaultStoreSingle and not self.SkuSingle:
1369                                        FileWrite(File, ' %-*s   : %6s %10s %10s = %s' % (self.MaxLen, ' ', TypeName, '(' + Pcd.DatumType + ')', '(' + SkuIdName + ')', '{'))
1370                                    elif not self.DefaultStoreSingle and self.SkuSingle:
1371                                        FileWrite(File, ' %-*s   : %6s %10s %10s = %s' % (self.MaxLen, ' ', TypeName, '(' + Pcd.DatumType + ')', '(' + DefaultStore + ')', '{'))
1372                                    else:
1373                                        FileWrite(File, ' %-*s   : %6s %10s %10s %10s = %s' % (self.MaxLen, ' ', TypeName, '(' + Pcd.DatumType + ')', '(' + SkuIdName + ')', '(' + DefaultStore + ')', '{'))
1374                                    for Array in ArrayList:
1375                                        FileWrite(File, Array)
1376                                else:
1377                                    if Pcd.DatumType in TAB_PCD_CLEAN_NUMERIC_TYPES:
1378                                        if Value.startswith(('0x', '0X')):
1379                                            Value = '{} ({:d})'.format(Value, int(Value, 0))
1380                                        else:
1381                                            Value = "0x{:X} ({})".format(int(Value, 0), Value)
1382                                    if self.DefaultStoreSingle and self.SkuSingle:
1383                                        FileWrite(File, ' %-*s   : %6s %10s = %s' % (self.MaxLen, ' ', TypeName, '(' + Pcd.DatumType + ')',  Value))
1384                                    elif self.DefaultStoreSingle and not self.SkuSingle:
1385                                        FileWrite(File, ' %-*s   : %6s %10s %10s = %s' % (self.MaxLen, ' ', TypeName, '(' + Pcd.DatumType + ')', '(' + SkuIdName + ')', Value))
1386                                    elif not self.DefaultStoreSingle and self.SkuSingle:
1387                                        FileWrite(File, ' %-*s   : %6s %10s %10s = %s' % (self.MaxLen, ' ', TypeName, '(' + Pcd.DatumType + ')', '(' + DefaultStore + ')', Value))
1388                                    else:
1389                                        FileWrite(File, ' %-*s   : %6s %10s %10s %10s = %s' % (self.MaxLen, ' ', TypeName, '(' + Pcd.DatumType + ')', '(' + SkuIdName + ')', '(' + DefaultStore + ')', Value))
1390                            FileWrite(File, '%*s: %s: %s' % (self.MaxLen + 4, SkuInfo.VariableGuid, SkuInfo.VariableName, SkuInfo.VariableOffset))
1391                            if IsStructure:
1392                                OverrideValues = Pcd.SkuOverrideValues.get(Sku)
1393                                if OverrideValues:
1394                                    OverrideFieldStruct = self.OverrideFieldValue(Pcd, OverrideValues[DefaultStore])
1395                                    self.PrintStructureInfo(File, OverrideFieldStruct)
1396                            self.PrintPcdDefault(File, Pcd, IsStructure, DscMatch, DscDefaultValue, InfMatch, InfDefaultValue, DecMatch, DecDefaultValue)
1397                else:
1398                    Value = SkuInfo.DefaultValue
1399                    IsByteArray, ArrayList = ByteArrayForamt(Value)
1400                    if Pcd.DatumType == 'BOOLEAN':
1401                        Value = str(int(Value, 0))
1402                    if FirstPrint:
1403                        FirstPrint = False
1404                        if IsByteArray:
1405                            if self.SkuSingle:
1406                                FileWrite(File, ' %-*s   : %6s %10s = %s' % (self.MaxLen, Flag + ' ' + PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', "{"))
1407                            else:
1408                                FileWrite(File, ' %-*s   : %6s %10s %10s = %s' % (self.MaxLen, Flag + ' ' + PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', '(' + SkuIdName + ')', "{"))
1409                            for Array in ArrayList:
1410                                FileWrite(File, Array)
1411                        else:
1412                            if Pcd.DatumType in TAB_PCD_CLEAN_NUMERIC_TYPES:
1413                                if Value.startswith(('0x', '0X')):
1414                                    Value = '{} ({:d})'.format(Value, int(Value, 0))
1415                                else:
1416                                    Value = "0x{:X} ({})".format(int(Value, 0), Value)
1417                            if self.SkuSingle:
1418                                FileWrite(File, ' %-*s   : %6s %10s = %s' % (self.MaxLen, Flag + ' ' + PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', Value))
1419                            else:
1420                                FileWrite(File, ' %-*s   : %6s %10s %10s = %s' % (self.MaxLen, Flag + ' ' + PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', '(' + SkuIdName + ')', Value))
1421                    else:
1422                        if IsByteArray:
1423                            if self.SkuSingle:
1424                                FileWrite(File, ' %-*s   : %6s %10s = %s' % (self.MaxLen, ' ', TypeName, '(' + Pcd.DatumType + ')', "{"))
1425                            else:
1426                                FileWrite(File, ' %-*s   : %6s %10s %10s = %s' % (self.MaxLen, ' ', TypeName, '(' + Pcd.DatumType + ')', '(' + SkuIdName + ')', "{"))
1427                            for Array in ArrayList:
1428                                FileWrite(File, Array)
1429                        else:
1430                            if Pcd.DatumType in TAB_PCD_CLEAN_NUMERIC_TYPES:
1431                                if Value.startswith(('0x', '0X')):
1432                                    Value = '{} ({:d})'.format(Value, int(Value, 0))
1433                                else:
1434                                    Value = "0x{:X} ({})".format(int(Value, 0), Value)
1435                            if self.SkuSingle:
1436                                FileWrite(File, ' %-*s   : %6s %10s = %s' % (self.MaxLen, ' ', TypeName, '(' + Pcd.DatumType + ')', Value))
1437                            else:
1438                                FileWrite(File, ' %-*s   : %6s %10s %10s = %s' % (self.MaxLen, ' ', TypeName, '(' + Pcd.DatumType + ')', '(' + SkuIdName + ')', Value))
1439                    if TypeName in ('DYNVPD', 'DEXVPD'):
1440                        FileWrite(File, '%*s' % (self.MaxLen + 4, SkuInfo.VpdOffset))
1441                        VPDPcdItem = (Pcd.TokenSpaceGuidCName + '.' + PcdTokenCName, SkuIdName, SkuInfo.VpdOffset, Pcd.MaxDatumSize, SkuInfo.DefaultValue)
1442                        if VPDPcdItem not in VPDPcdList:
1443                            PcdGuidList = self.UnusedPcds.get(Pcd.TokenSpaceGuidCName)
1444                            if PcdGuidList:
1445                                PcdList = PcdGuidList.get(Pcd.Type)
1446                                if not PcdList:
1447                                    VPDPcdList.append(VPDPcdItem)
1448                                for VpdPcd in PcdList:
1449                                    if PcdTokenCName == VpdPcd.TokenCName:
1450                                        break
1451                                else:
1452                                    VPDPcdList.append(VPDPcdItem)
1453                    if IsStructure:
1454                        FiledOverrideFlag = False
1455                        OverrideValues = Pcd.SkuOverrideValues.get(Sku)
1456                        if OverrideValues:
1457                            Keys = list(OverrideValues.keys())
1458                            OverrideFieldStruct = self.OverrideFieldValue(Pcd, OverrideValues[Keys[0]])
1459                            self.PrintStructureInfo(File, OverrideFieldStruct)
1460                            FiledOverrideFlag = True
1461                        if not FiledOverrideFlag and (Pcd.PcdFieldValueFromComm or Pcd.PcdFieldValueFromFdf):
1462                            OverrideFieldStruct = self.OverrideFieldValue(Pcd, {})
1463                            self.PrintStructureInfo(File, OverrideFieldStruct)
1464                    self.PrintPcdDefault(File, Pcd, IsStructure, DscMatch, DscDefaultValue, InfMatch, InfDefaultValue, DecMatch, DecDefaultValue)
1465
1466    def OverrideFieldValue(self, Pcd, OverrideStruct):
1467        OverrideFieldStruct = collections.OrderedDict()
1468        if OverrideStruct:
1469            for _, Values in OverrideStruct.items():
1470                for Key,value in Values.items():
1471                    if value[1] and value[1].endswith('.dsc'):
1472                        OverrideFieldStruct[Key] = value
1473        if Pcd.PcdFieldValueFromFdf:
1474            for Key, Values in Pcd.PcdFieldValueFromFdf.items():
1475                if Key in OverrideFieldStruct and Values[0] == OverrideFieldStruct[Key][0]:
1476                    continue
1477                OverrideFieldStruct[Key] = Values
1478        if Pcd.PcdFieldValueFromComm:
1479            for Key, Values in Pcd.PcdFieldValueFromComm.items():
1480                if Key in OverrideFieldStruct and Values[0] == OverrideFieldStruct[Key][0]:
1481                    continue
1482                OverrideFieldStruct[Key] = Values
1483        return OverrideFieldStruct
1484
1485    def PrintStructureInfo(self, File, Struct):
1486        for Key, Value in sorted(Struct.items(), key=lambda x: x[0]):
1487            if Value[1] and 'build command options' in Value[1]:
1488                FileWrite(File, '    *B  %-*s = %s' % (self.MaxLen + 4, '.' + Key, Value[0]))
1489            elif Value[1] and Value[1].endswith('.fdf'):
1490                FileWrite(File, '    *F  %-*s = %s' % (self.MaxLen + 4, '.' + Key, Value[0]))
1491            else:
1492                FileWrite(File, '        %-*s = %s' % (self.MaxLen + 4, '.' + Key, Value[0]))
1493
1494    def StrtoHex(self, value):
1495        try:
1496            value = hex(int(value))
1497            return value
1498        except:
1499            if value.startswith("L\"") and value.endswith("\""):
1500                valuelist = []
1501                for ch in value[2:-1]:
1502                    valuelist.append(hex(ord(ch)))
1503                    valuelist.append('0x00')
1504                return valuelist
1505            elif value.startswith("\"") and value.endswith("\""):
1506                return hex(ord(value[1:-1]))
1507            elif value.startswith("{") and value.endswith("}"):
1508                valuelist = []
1509                if ',' not in value:
1510                    return value[1:-1]
1511                for ch in value[1:-1].split(','):
1512                    ch = ch.strip()
1513                    if ch.startswith('0x') or ch.startswith('0X'):
1514                        valuelist.append(ch)
1515                        continue
1516                    try:
1517                        valuelist.append(hex(int(ch.strip())))
1518                    except:
1519                        pass
1520                return valuelist
1521            else:
1522                return value
1523
1524    def IsStructurePcd(self, PcdToken, PcdTokenSpaceGuid):
1525        if GlobalData.gStructurePcd and (self.Arch in GlobalData.gStructurePcd) and ((PcdToken, PcdTokenSpaceGuid) in GlobalData.gStructurePcd[self.Arch]):
1526            return True
1527        else:
1528            return False
1529
1530##
1531# Reports platform and module Prediction information
1532#
1533# This class reports the platform execution order prediction section and
1534# module load fixed address prediction subsection in the build report file.
1535#
1536class PredictionReport(object):
1537    ##
1538    # Constructor function for class PredictionReport
1539    #
1540    # This constructor function generates PredictionReport object for the platform.
1541    #
1542    # @param self:           The object pointer
1543    # @param Wa              Workspace context information
1544    #
1545    def __init__(self, Wa):
1546        self._MapFileName = os.path.join(Wa.BuildDir, Wa.Name + ".map")
1547        self._MapFileParsed = False
1548        self._EotToolInvoked = False
1549        self._FvDir = Wa.FvDir
1550        self._EotDir = Wa.BuildDir
1551        self._FfsEntryPoint = {}
1552        self._GuidMap = {}
1553        self._SourceList = []
1554        self.FixedMapDict = {}
1555        self.ItemList = []
1556        self.MaxLen = 0
1557
1558        #
1559        # Collect all platform reference source files and GUID C Name
1560        #
1561        for Pa in Wa.AutoGenObjectList:
1562            for Module in Pa.LibraryAutoGenList + Pa.ModuleAutoGenList:
1563                #
1564                # BASE typed modules are EFI agnostic, so we need not scan
1565                # their source code to find PPI/Protocol produce or consume
1566                # information.
1567                #
1568                if Module.ModuleType == SUP_MODULE_BASE:
1569                    continue
1570                #
1571                # Add module referenced source files
1572                #
1573                self._SourceList.append(str(Module))
1574                IncludeList = {}
1575                for Source in Module.SourceFileList:
1576                    if os.path.splitext(str(Source))[1].lower() == ".c":
1577                        self._SourceList.append("  " + str(Source))
1578                        FindIncludeFiles(Source.Path, Module.IncludePathList, IncludeList)
1579                for IncludeFile in IncludeList.values():
1580                    self._SourceList.append("  " + IncludeFile)
1581
1582                for Guid in Module.PpiList:
1583                    self._GuidMap[Guid] = GuidStructureStringToGuidString(Module.PpiList[Guid])
1584                for Guid in Module.ProtocolList:
1585                    self._GuidMap[Guid] = GuidStructureStringToGuidString(Module.ProtocolList[Guid])
1586                for Guid in Module.GuidList:
1587                    self._GuidMap[Guid] = GuidStructureStringToGuidString(Module.GuidList[Guid])
1588
1589                if Module.Guid and not Module.IsLibrary:
1590                    EntryPoint = " ".join(Module.Module.ModuleEntryPointList)
1591
1592                    RealEntryPoint = "_ModuleEntryPoint"
1593
1594                    self._FfsEntryPoint[Module.Guid.upper()] = (EntryPoint, RealEntryPoint)
1595
1596
1597        #
1598        # Collect platform firmware volume list as the input of EOT.
1599        #
1600        self._FvList = []
1601        if Wa.FdfProfile:
1602            for Fd in Wa.FdfProfile.FdDict:
1603                for FdRegion in Wa.FdfProfile.FdDict[Fd].RegionList:
1604                    if FdRegion.RegionType != BINARY_FILE_TYPE_FV:
1605                        continue
1606                    for FvName in FdRegion.RegionDataList:
1607                        if FvName in self._FvList:
1608                            continue
1609                        self._FvList.append(FvName)
1610                        for Ffs in Wa.FdfProfile.FvDict[FvName.upper()].FfsList:
1611                            for Section in Ffs.SectionList:
1612                                try:
1613                                    for FvSection in Section.SectionList:
1614                                        if FvSection.FvName in self._FvList:
1615                                            continue
1616                                        self._FvList.append(FvSection.FvName)
1617                                except AttributeError:
1618                                    pass
1619
1620
1621    ##
1622    # Parse platform fixed address map files
1623    #
1624    # This function parses the platform final fixed address map file to get
1625    # the database of predicted fixed address for module image base, entry point
1626    # etc.
1627    #
1628    # @param self:           The object pointer
1629    #
1630    def _ParseMapFile(self):
1631        if self._MapFileParsed:
1632            return
1633        self._MapFileParsed = True
1634        if os.path.isfile(self._MapFileName):
1635            try:
1636                FileContents = open(self._MapFileName).read()
1637                for Match in gMapFileItemPattern.finditer(FileContents):
1638                    AddressType = Match.group(1)
1639                    BaseAddress = Match.group(2)
1640                    EntryPoint = Match.group(3)
1641                    Guid = Match.group(4).upper()
1642                    List = self.FixedMapDict.setdefault(Guid, [])
1643                    List.append((AddressType, BaseAddress, "*I"))
1644                    List.append((AddressType, EntryPoint, "*E"))
1645            except:
1646                EdkLogger.warn(None, "Cannot open file to read", self._MapFileName)
1647
1648    ##
1649    # Invokes EOT tool to get the predicted the execution order.
1650    #
1651    # This function invokes EOT tool to calculate the predicted dispatch order
1652    #
1653    # @param self:           The object pointer
1654    #
1655    def _InvokeEotTool(self):
1656        if self._EotToolInvoked:
1657            return
1658
1659        self._EotToolInvoked = True
1660        FvFileList = []
1661        for FvName in self._FvList:
1662            FvFile = os.path.join(self._FvDir, FvName + ".Fv")
1663            if os.path.isfile(FvFile):
1664                FvFileList.append(FvFile)
1665
1666        if len(FvFileList) == 0:
1667            return
1668        #
1669        # Write source file list and GUID file list to an intermediate file
1670        # as the input for EOT tool and dispatch List as the output file
1671        # from EOT tool.
1672        #
1673        SourceList = os.path.join(self._EotDir, "SourceFile.txt")
1674        GuidList = os.path.join(self._EotDir, "GuidList.txt")
1675        DispatchList = os.path.join(self._EotDir, "Dispatch.txt")
1676
1677        TempFile = []
1678        for Item in self._SourceList:
1679            FileWrite(TempFile, Item)
1680        SaveFileOnChange(SourceList, "".join(TempFile), False)
1681        TempFile = []
1682        for Key in self._GuidMap:
1683            FileWrite(TempFile, "%s %s" % (Key, self._GuidMap[Key]))
1684        SaveFileOnChange(GuidList, "".join(TempFile), False)
1685
1686        try:
1687            from Eot.EotMain import Eot
1688
1689            #
1690            # Invoke EOT tool and echo its runtime performance
1691            #
1692            EotStartTime = time.time()
1693            Eot(CommandLineOption=False, SourceFileList=SourceList, GuidList=GuidList,
1694                FvFileList=' '.join(FvFileList), Dispatch=DispatchList, IsInit=True)
1695            EotEndTime = time.time()
1696            EotDuration = time.strftime("%H:%M:%S", time.gmtime(int(round(EotEndTime - EotStartTime))))
1697            EdkLogger.quiet("EOT run time: %s\n" % EotDuration)
1698
1699            #
1700            # Parse the output of EOT tool
1701            #
1702            for Line in open(DispatchList):
1703                if len(Line.split()) < 4:
1704                    continue
1705                (Guid, Phase, FfsName, FilePath) = Line.split()
1706                Symbol = self._FfsEntryPoint.get(Guid, [FfsName, ""])[0]
1707                if len(Symbol) > self.MaxLen:
1708                    self.MaxLen = len(Symbol)
1709                self.ItemList.append((Phase, Symbol, FilePath))
1710        except:
1711            EdkLogger.quiet("(Python %s on %s\n%s)" % (platform.python_version(), sys.platform, traceback.format_exc()))
1712            EdkLogger.warn(None, "Failed to generate execution order prediction report, for some error occurred in executing EOT.")
1713
1714
1715    ##
1716    # Generate platform execution order report
1717    #
1718    # This function generates the predicted module execution order.
1719    #
1720    # @param self            The object pointer
1721    # @param File            The file object for report
1722    #
1723    def _GenerateExecutionOrderReport(self, File):
1724        self._InvokeEotTool()
1725        if len(self.ItemList) == 0:
1726            return
1727        FileWrite(File, gSectionStart)
1728        FileWrite(File, "Execution Order Prediction")
1729        FileWrite(File, "*P PEI phase")
1730        FileWrite(File, "*D DXE phase")
1731        FileWrite(File, "*E Module INF entry point name")
1732        FileWrite(File, "*N Module notification function name")
1733
1734        FileWrite(File, "Type %-*s %s" % (self.MaxLen, "Symbol", "Module INF Path"))
1735        FileWrite(File, gSectionSep)
1736        for Item in self.ItemList:
1737            FileWrite(File, "*%sE  %-*s %s" % (Item[0], self.MaxLen, Item[1], Item[2]))
1738
1739        FileWrite(File, gSectionStart)
1740
1741    ##
1742    # Generate Fixed Address report.
1743    #
1744    # This function generate the predicted fixed address report for a module
1745    # specified by Guid.
1746    #
1747    # @param self            The object pointer
1748    # @param File            The file object for report
1749    # @param Guid            The module Guid value.
1750    # @param NotifyList      The list of all notify function in a module
1751    #
1752    def _GenerateFixedAddressReport(self, File, Guid, NotifyList):
1753        self._ParseMapFile()
1754        FixedAddressList = self.FixedMapDict.get(Guid)
1755        if not FixedAddressList:
1756            return
1757
1758        FileWrite(File, gSubSectionStart)
1759        FileWrite(File, "Fixed Address Prediction")
1760        FileWrite(File, "*I  Image Loading Address")
1761        FileWrite(File, "*E  Entry Point Address")
1762        FileWrite(File, "*N  Notification Function Address")
1763        FileWrite(File, "*F  Flash Address")
1764        FileWrite(File, "*M  Memory Address")
1765        FileWrite(File, "*S  SMM RAM Offset")
1766        FileWrite(File, "TOM Top of Memory")
1767
1768        FileWrite(File, "Type Address           Name")
1769        FileWrite(File, gSubSectionSep)
1770        for Item in FixedAddressList:
1771            Type = Item[0]
1772            Value = Item[1]
1773            Symbol = Item[2]
1774            if Symbol == "*I":
1775                Name = "(Image Base)"
1776            elif Symbol == "*E":
1777                Name = self._FfsEntryPoint.get(Guid, ["", "_ModuleEntryPoint"])[1]
1778            elif Symbol in NotifyList:
1779                Name = Symbol
1780                Symbol = "*N"
1781            else:
1782                continue
1783
1784            if "Flash" in Type:
1785                Symbol += "F"
1786            elif "Memory" in Type:
1787                Symbol += "M"
1788            else:
1789                Symbol += "S"
1790
1791            if Value[0] == "-":
1792                Value = "TOM" + Value
1793
1794            FileWrite(File, "%s  %-16s  %s" % (Symbol, Value, Name))
1795
1796    ##
1797    # Generate report for the prediction part
1798    #
1799    # This function generate the predicted fixed address report for a module or
1800    # predicted module execution order for a platform.
1801    # If the input Guid is None, then, it generates the predicted module execution order;
1802    # otherwise it generated the module fixed loading address for the module specified by
1803    # Guid.
1804    #
1805    # @param self            The object pointer
1806    # @param File            The file object for report
1807    # @param Guid            The module Guid value.
1808    #
1809    def GenerateReport(self, File, Guid):
1810        if Guid:
1811            self._GenerateFixedAddressReport(File, Guid.upper(), [])
1812        else:
1813            self._GenerateExecutionOrderReport(File)
1814
1815##
1816# Reports FD region information
1817#
1818# This class reports the FD subsection in the build report file.
1819# It collects region information of platform flash device.
1820# If the region is a firmware volume, it lists the set of modules
1821# and its space information; otherwise, it only lists its region name,
1822# base address and size in its sub-section header.
1823# If there are nesting FVs, the nested FVs will list immediate after
1824# this FD region subsection
1825#
1826class FdRegionReport(object):
1827    ##
1828    # Discover all the nested FV name list.
1829    #
1830    # This is an internal worker function to discover the all the nested FV information
1831    # in the parent firmware volume. It uses deep first search algorithm recursively to
1832    # find all the FV list name and append them to the list.
1833    #
1834    # @param self            The object pointer
1835    # @param FvName          The name of current firmware file system
1836    # @param Wa              Workspace context information
1837    #
1838    def _DiscoverNestedFvList(self, FvName, Wa):
1839        FvDictKey=FvName.upper()
1840        if FvDictKey in Wa.FdfProfile.FvDict:
1841            for Ffs in Wa.FdfProfile.FvDict[FvName.upper()].FfsList:
1842                for Section in Ffs.SectionList:
1843                    try:
1844                        for FvSection in Section.SectionList:
1845                            if FvSection.FvName in self.FvList:
1846                                continue
1847                            self._GuidsDb[Ffs.NameGuid.upper()] = FvSection.FvName
1848                            self.FvList.append(FvSection.FvName)
1849                            self.FvInfo[FvSection.FvName] = ("Nested FV", 0, 0)
1850                            self._DiscoverNestedFvList(FvSection.FvName, Wa)
1851                    except AttributeError:
1852                        pass
1853
1854    ##
1855    # Constructor function for class FdRegionReport
1856    #
1857    # This constructor function generates FdRegionReport object for a specified FdRegion.
1858    # If the FdRegion is a firmware volume, it will recursively find all its nested Firmware
1859    # volume list. This function also collects GUID map in order to dump module identification
1860    # in the final report.
1861    #
1862    # @param self:           The object pointer
1863    # @param FdRegion        The current FdRegion object
1864    # @param Wa              Workspace context information
1865    #
1866    def __init__(self, FdRegion, Wa):
1867        self.Type = FdRegion.RegionType
1868        self.BaseAddress = FdRegion.Offset
1869        self.Size = FdRegion.Size
1870        self.FvList = []
1871        self.FvInfo = {}
1872        self._GuidsDb = {}
1873        self._FvDir = Wa.FvDir
1874        self._WorkspaceDir = Wa.WorkspaceDir
1875
1876        #
1877        # If the input FdRegion is not a firmware volume,
1878        # we are done.
1879        #
1880        if self.Type != BINARY_FILE_TYPE_FV:
1881            return
1882
1883        #
1884        # Find all nested FVs in the FdRegion
1885        #
1886        for FvName in FdRegion.RegionDataList:
1887            if FvName in self.FvList:
1888                continue
1889            self.FvList.append(FvName)
1890            self.FvInfo[FvName] = ("Fd Region", self.BaseAddress, self.Size)
1891            self._DiscoverNestedFvList(FvName, Wa)
1892
1893        PlatformPcds = {}
1894        #
1895        # Collect PCDs declared in DEC files.
1896        #
1897        for Pa in Wa.AutoGenObjectList:
1898            for Package in Pa.PackageList:
1899                for (TokenCName, TokenSpaceGuidCName, DecType) in Package.Pcds:
1900                    DecDefaultValue = Package.Pcds[TokenCName, TokenSpaceGuidCName, DecType].DefaultValue
1901                    PlatformPcds[(TokenCName, TokenSpaceGuidCName)] = DecDefaultValue
1902        #
1903        # Collect PCDs defined in DSC file
1904        #
1905        for Pa in Wa.AutoGenObjectList:
1906            for (TokenCName, TokenSpaceGuidCName) in Pa.Platform.Pcds:
1907                DscDefaultValue = Pa.Platform.Pcds[(TokenCName, TokenSpaceGuidCName)].DefaultValue
1908                PlatformPcds[(TokenCName, TokenSpaceGuidCName)] = DscDefaultValue
1909
1910        #
1911        # Add PEI and DXE a priori files GUIDs defined in PI specification.
1912        #
1913        self._GuidsDb[PEI_APRIORI_GUID] = "PEI Apriori"
1914        self._GuidsDb[DXE_APRIORI_GUID] = "DXE Apriori"
1915        #
1916        # Add ACPI table storage file
1917        #
1918        self._GuidsDb["7E374E25-8E01-4FEE-87F2-390C23C606CD"] = "ACPI table storage"
1919
1920        for Pa in Wa.AutoGenObjectList:
1921            for ModuleKey in Pa.Platform.Modules:
1922                M = Pa.Platform.Modules[ModuleKey].M
1923                InfPath = mws.join(Wa.WorkspaceDir, M.MetaFile.File)
1924                self._GuidsDb[M.Guid.upper()] = "%s (%s)" % (M.Module.BaseName, InfPath)
1925
1926        #
1927        # Collect the GUID map in the FV firmware volume
1928        #
1929        for FvName in self.FvList:
1930            FvDictKey=FvName.upper()
1931            if FvDictKey in Wa.FdfProfile.FvDict:
1932                for Ffs in Wa.FdfProfile.FvDict[FvName.upper()].FfsList:
1933                    try:
1934                        #
1935                        # collect GUID map for binary EFI file in FDF file.
1936                        #
1937                        Guid = Ffs.NameGuid.upper()
1938                        Match = gPcdGuidPattern.match(Ffs.NameGuid)
1939                        if Match:
1940                            PcdTokenspace = Match.group(1)
1941                            PcdToken = Match.group(2)
1942                            if (PcdToken, PcdTokenspace) in PlatformPcds:
1943                                GuidValue = PlatformPcds[(PcdToken, PcdTokenspace)]
1944                                Guid = GuidStructureByteArrayToGuidString(GuidValue).upper()
1945                        for Section in Ffs.SectionList:
1946                            try:
1947                                ModuleSectFile = mws.join(Wa.WorkspaceDir, Section.SectFileName)
1948                                self._GuidsDb[Guid] = ModuleSectFile
1949                            except AttributeError:
1950                                pass
1951                    except AttributeError:
1952                        pass
1953
1954
1955    ##
1956    # Internal worker function to generate report for the FD region
1957    #
1958    # This internal worker function to generate report for the FD region.
1959    # It the type is firmware volume, it lists offset and module identification.
1960    #
1961    # @param self            The object pointer
1962    # @param File            The file object for report
1963    # @param Title           The title for the FD subsection
1964    # @param BaseAddress     The base address for the FD region
1965    # @param Size            The size of the FD region
1966    # @param FvName          The FV name if the FD region is a firmware volume
1967    #
1968    def _GenerateReport(self, File, Title, Type, BaseAddress, Size=0, FvName=None):
1969        FileWrite(File, gSubSectionStart)
1970        FileWrite(File, Title)
1971        FileWrite(File, "Type:               %s" % Type)
1972        FileWrite(File, "Base Address:       0x%X" % BaseAddress)
1973
1974        if self.Type == BINARY_FILE_TYPE_FV:
1975            FvTotalSize = 0
1976            FvTakenSize = 0
1977            FvFreeSize  = 0
1978            if FvName.upper().endswith('.FV'):
1979                FileExt = FvName + ".txt"
1980            else:
1981                FileExt = FvName + ".Fv.txt"
1982
1983            if not os.path.isfile(FileExt):
1984                FvReportFileName = mws.join(self._WorkspaceDir, FileExt)
1985                if not os.path.isfile(FvReportFileName):
1986                    FvReportFileName = os.path.join(self._FvDir, FileExt)
1987            try:
1988                #
1989                # Collect size info in the firmware volume.
1990                #
1991                FvReport = open(FvReportFileName).read()
1992                Match = gFvTotalSizePattern.search(FvReport)
1993                if Match:
1994                    FvTotalSize = int(Match.group(1), 16)
1995                Match = gFvTakenSizePattern.search(FvReport)
1996                if Match:
1997                    FvTakenSize = int(Match.group(1), 16)
1998                FvFreeSize = FvTotalSize - FvTakenSize
1999                #
2000                # Write size information to the report file.
2001                #
2002                FileWrite(File, "Size:               0x%X (%.0fK)" % (FvTotalSize, FvTotalSize / 1024.0))
2003                FileWrite(File, "Fv Name:            %s (%.1f%% Full)" % (FvName, FvTakenSize * 100.0 / FvTotalSize))
2004                FileWrite(File, "Occupied Size:      0x%X (%.0fK)" % (FvTakenSize, FvTakenSize / 1024.0))
2005                FileWrite(File, "Free Size:          0x%X (%.0fK)" % (FvFreeSize, FvFreeSize / 1024.0))
2006                FileWrite(File, "Offset     Module")
2007                FileWrite(File, gSubSectionSep)
2008                #
2009                # Write module offset and module identification to the report file.
2010                #
2011                OffsetInfo = {}
2012                for Match in gOffsetGuidPattern.finditer(FvReport):
2013                    Guid = Match.group(2).upper()
2014                    OffsetInfo[Match.group(1)] = self._GuidsDb.get(Guid, Guid)
2015                OffsetList = sorted(OffsetInfo.keys())
2016                for Offset in OffsetList:
2017                    FileWrite (File, "%s %s" % (Offset, OffsetInfo[Offset]))
2018            except IOError:
2019                EdkLogger.warn(None, "Fail to read report file", FvReportFileName)
2020        else:
2021            FileWrite(File, "Size:               0x%X (%.0fK)" % (Size, Size / 1024.0))
2022        FileWrite(File, gSubSectionEnd)
2023
2024    ##
2025    # Generate report for the FD region
2026    #
2027    # This function generates report for the FD region.
2028    #
2029    # @param self            The object pointer
2030    # @param File            The file object for report
2031    #
2032    def GenerateReport(self, File):
2033        if (len(self.FvList) > 0):
2034            for FvItem in self.FvList:
2035                Info = self.FvInfo[FvItem]
2036                self._GenerateReport(File, Info[0], TAB_FV_DIRECTORY, Info[1], Info[2], FvItem)
2037        else:
2038            self._GenerateReport(File, "FD Region", self.Type, self.BaseAddress, self.Size)
2039
2040##
2041# Reports FD information
2042#
2043# This class reports the FD section in the build report file.
2044# It collects flash device information for a platform.
2045#
2046class FdReport(object):
2047    ##
2048    # Constructor function for class FdReport
2049    #
2050    # This constructor function generates FdReport object for a specified
2051    # firmware device.
2052    #
2053    # @param self            The object pointer
2054    # @param Fd              The current Firmware device object
2055    # @param Wa              Workspace context information
2056    #
2057    def __init__(self, Fd, Wa):
2058        self.FdName = Fd.FdUiName
2059        self.BaseAddress = Fd.BaseAddress
2060        self.Size = Fd.Size
2061        self.FdRegionList = [FdRegionReport(FdRegion, Wa) for FdRegion in Fd.RegionList]
2062        self.FvPath = os.path.join(Wa.BuildDir, TAB_FV_DIRECTORY)
2063        self.VPDBaseAddress = 0
2064        self.VPDSize = 0
2065        for index, FdRegion in enumerate(Fd.RegionList):
2066            if str(FdRegion.RegionType) == 'FILE' and Wa.Platform.VpdToolGuid in str(FdRegion.RegionDataList):
2067                self.VPDBaseAddress = self.FdRegionList[index].BaseAddress
2068                self.VPDSize = self.FdRegionList[index].Size
2069                break
2070
2071    ##
2072    # Generate report for the firmware device.
2073    #
2074    # This function generates report for the firmware device.
2075    #
2076    # @param self            The object pointer
2077    # @param File            The file object for report
2078    #
2079    def GenerateReport(self, File):
2080        FileWrite(File, gSectionStart)
2081        FileWrite(File, "Firmware Device (FD)")
2082        FileWrite(File, "FD Name:            %s" % self.FdName)
2083        FileWrite(File, "Base Address:       %s" % self.BaseAddress)
2084        FileWrite(File, "Size:               0x%X (%.0fK)" % (self.Size, self.Size / 1024.0))
2085        if len(self.FdRegionList) > 0:
2086            FileWrite(File, gSectionSep)
2087            for FdRegionItem in self.FdRegionList:
2088                FdRegionItem.GenerateReport(File)
2089
2090        if VPDPcdList:
2091            VPDPcdList.sort(key=lambda x: int(x[2], 0))
2092            FileWrite(File, gSubSectionStart)
2093            FileWrite(File, "FD VPD Region")
2094            FileWrite(File, "Base Address:       0x%X" % self.VPDBaseAddress)
2095            FileWrite(File, "Size:               0x%X (%.0fK)" % (self.VPDSize, self.VPDSize / 1024.0))
2096            FileWrite(File, gSubSectionSep)
2097            for item in VPDPcdList:
2098                # Add BaseAddress for offset
2099                Offset = '0x%08X' % (int(item[2], 16) + self.VPDBaseAddress)
2100                IsByteArray, ArrayList = ByteArrayForamt(item[-1])
2101                Skuinfo = item[1]
2102                if len(GlobalData.gSkuids) == 1 :
2103                    Skuinfo = GlobalData.gSkuids[0]
2104                if IsByteArray:
2105                    FileWrite(File, "%s | %s | %s | %s | %s" % (item[0], Skuinfo, Offset, item[3], '{'))
2106                    for Array in ArrayList:
2107                        FileWrite(File, Array)
2108                else:
2109                    FileWrite(File, "%s | %s | %s | %s | %s" % (item[0], Skuinfo, Offset, item[3], item[-1]))
2110            FileWrite(File, gSubSectionEnd)
2111        FileWrite(File, gSectionEnd)
2112
2113
2114
2115##
2116# Reports platform information
2117#
2118# This class reports the whole platform information
2119#
2120class PlatformReport(object):
2121    ##
2122    # Constructor function for class PlatformReport
2123    #
2124    # This constructor function generates PlatformReport object a platform build.
2125    # It generates report for platform summary, flash, global PCDs and detailed
2126    # module information for modules involved in platform build.
2127    #
2128    # @param self            The object pointer
2129    # @param Wa              Workspace context information
2130    # @param MaList          The list of modules in the platform build
2131    #
2132    def __init__(self, Wa, MaList, ReportType):
2133        self._WorkspaceDir = Wa.WorkspaceDir
2134        self.PlatformName = Wa.Name
2135        self.PlatformDscPath = Wa.Platform
2136        self.Architectures = " ".join(Wa.ArchList)
2137        self.ToolChain = Wa.ToolChain
2138        self.Target = Wa.BuildTarget
2139        self.OutputPath = os.path.join(Wa.WorkspaceDir, Wa.OutputDir)
2140        self.BuildEnvironment = platform.platform()
2141
2142        self.PcdReport = None
2143        if "PCD" in ReportType:
2144            self.PcdReport = PcdReport(Wa)
2145
2146        self.FdReportList = []
2147        if "FLASH" in ReportType and Wa.FdfProfile and MaList is None:
2148            for Fd in Wa.FdfProfile.FdDict:
2149                self.FdReportList.append(FdReport(Wa.FdfProfile.FdDict[Fd], Wa))
2150
2151        self.PredictionReport = None
2152        if "FIXED_ADDRESS" in ReportType or "EXECUTION_ORDER" in ReportType:
2153            self.PredictionReport = PredictionReport(Wa)
2154
2155        self.DepexParser = None
2156        if "DEPEX" in ReportType:
2157            self.DepexParser = DepexParser(Wa)
2158
2159        self.ModuleReportList = []
2160        if MaList is not None:
2161            self._IsModuleBuild = True
2162            for Ma in MaList:
2163                self.ModuleReportList.append(ModuleReport(Ma, ReportType))
2164        else:
2165            self._IsModuleBuild = False
2166            for Pa in Wa.AutoGenObjectList:
2167                ModuleAutoGenList = []
2168                for ModuleKey in Pa.Platform.Modules:
2169                    ModuleAutoGenList.append(Pa.Platform.Modules[ModuleKey].M)
2170                if GlobalData.gFdfParser is not None:
2171                    if Pa.Arch in GlobalData.gFdfParser.Profile.InfDict:
2172                        INFList = GlobalData.gFdfParser.Profile.InfDict[Pa.Arch]
2173                        for InfName in INFList:
2174                            InfClass = PathClass(NormPath(InfName), Wa.WorkspaceDir, Pa.Arch)
2175                            Ma = ModuleAutoGen(Wa, InfClass, Pa.BuildTarget, Pa.ToolChain, Pa.Arch, Wa.MetaFile, Pa.DataPipe)
2176                            if Ma is None:
2177                                continue
2178                            if Ma not in ModuleAutoGenList:
2179                                ModuleAutoGenList.append(Ma)
2180                for MGen in ModuleAutoGenList:
2181                    self.ModuleReportList.append(ModuleReport(MGen, ReportType))
2182
2183
2184
2185    ##
2186    # Generate report for the whole platform.
2187    #
2188    # This function generates report for platform information.
2189    # It comprises of platform summary, global PCD, flash and
2190    # module list sections.
2191    #
2192    # @param self            The object pointer
2193    # @param File            The file object for report
2194    # @param BuildDuration   The total time to build the modules
2195    # @param AutoGenTime     The total time of AutoGen Phase
2196    # @param MakeTime        The total time of Make Phase
2197    # @param GenFdsTime      The total time of GenFds Phase
2198    # @param ReportType      The kind of report items in the final report file
2199    #
2200    def GenerateReport(self, File, BuildDuration, AutoGenTime, MakeTime, GenFdsTime, ReportType):
2201        FileWrite(File, "Platform Summary")
2202        FileWrite(File, "Platform Name:        %s" % self.PlatformName)
2203        FileWrite(File, "Platform DSC Path:    %s" % self.PlatformDscPath)
2204        FileWrite(File, "Architectures:        %s" % self.Architectures)
2205        FileWrite(File, "Tool Chain:           %s" % self.ToolChain)
2206        FileWrite(File, "Target:               %s" % self.Target)
2207        if GlobalData.gSkuids:
2208            FileWrite(File, "SKUID:                %s" % " ".join(GlobalData.gSkuids))
2209        if GlobalData.gDefaultStores:
2210            FileWrite(File, "DefaultStore:         %s" % " ".join(GlobalData.gDefaultStores))
2211        FileWrite(File, "Output Path:          %s" % self.OutputPath)
2212        FileWrite(File, "Build Environment:    %s" % self.BuildEnvironment)
2213        FileWrite(File, "Build Duration:       %s" % BuildDuration)
2214        if AutoGenTime:
2215            FileWrite(File, "AutoGen Duration:     %s" % AutoGenTime)
2216        if MakeTime:
2217            FileWrite(File, "Make Duration:        %s" % MakeTime)
2218        if GenFdsTime:
2219            FileWrite(File, "GenFds Duration:      %s" % GenFdsTime)
2220        FileWrite(File, "Report Content:       %s" % ", ".join(ReportType))
2221
2222        if GlobalData.MixedPcd:
2223            FileWrite(File, gSectionStart)
2224            FileWrite(File, "The following PCDs use different access methods:")
2225            FileWrite(File, gSectionSep)
2226            for PcdItem in GlobalData.MixedPcd:
2227                FileWrite(File, "%s.%s" % (str(PcdItem[1]), str(PcdItem[0])))
2228            FileWrite(File, gSectionEnd)
2229
2230        if not self._IsModuleBuild:
2231            if "PCD" in ReportType:
2232                self.PcdReport.GenerateReport(File, None)
2233
2234            if "FLASH" in ReportType:
2235                for FdReportListItem in self.FdReportList:
2236                    FdReportListItem.GenerateReport(File)
2237
2238        for ModuleReportItem in self.ModuleReportList:
2239            ModuleReportItem.GenerateReport(File, self.PcdReport, self.PredictionReport, self.DepexParser, ReportType)
2240
2241        if not self._IsModuleBuild:
2242            if "EXECUTION_ORDER" in ReportType:
2243                self.PredictionReport.GenerateReport(File, None)
2244
2245## BuildReport class
2246#
2247#  This base class contain the routines to collect data and then
2248#  applies certain format to the output report
2249#
2250class BuildReport(object):
2251    ##
2252    # Constructor function for class BuildReport
2253    #
2254    # This constructor function generates BuildReport object a platform build.
2255    # It generates report for platform summary, flash, global PCDs and detailed
2256    # module information for modules involved in platform build.
2257    #
2258    # @param self            The object pointer
2259    # @param ReportFile      The file name to save report file
2260    # @param ReportType      The kind of report items in the final report file
2261    #
2262    def __init__(self, ReportFile, ReportType):
2263        self.ReportFile = ReportFile
2264        if ReportFile:
2265            self.ReportList = []
2266            self.ReportType = []
2267            if ReportType:
2268                for ReportTypeItem in ReportType:
2269                    if ReportTypeItem not in self.ReportType:
2270                        self.ReportType.append(ReportTypeItem)
2271            else:
2272                self.ReportType = ["PCD", "LIBRARY", "BUILD_FLAGS", "DEPEX", "HASH", "FLASH", "FIXED_ADDRESS"]
2273    ##
2274    # Adds platform report to the list
2275    #
2276    # This function adds a platform report to the final report list.
2277    #
2278    # @param self            The object pointer
2279    # @param Wa              Workspace context information
2280    # @param MaList          The list of modules in the platform build
2281    #
2282    def AddPlatformReport(self, Wa, MaList=None):
2283        if self.ReportFile:
2284            self.ReportList.append((Wa, MaList))
2285
2286    ##
2287    # Generates the final report.
2288    #
2289    # This function generates platform build report. It invokes GenerateReport()
2290    # method for every platform report in the list.
2291    #
2292    # @param self            The object pointer
2293    # @param BuildDuration   The total time to build the modules
2294    # @param AutoGenTime     The total time of AutoGen phase
2295    # @param MakeTime        The total time of Make phase
2296    # @param GenFdsTime      The total time of GenFds phase
2297    #
2298    def GenerateReport(self, BuildDuration, AutoGenTime, MakeTime, GenFdsTime):
2299        if self.ReportFile:
2300            try:
2301                File = []
2302                for (Wa, MaList) in self.ReportList:
2303                    PlatformReport(Wa, MaList, self.ReportType).GenerateReport(File, BuildDuration, AutoGenTime, MakeTime, GenFdsTime, self.ReportType)
2304                Content = FileLinesSplit(''.join(File), gLineMaxLength)
2305                SaveFileOnChange(self.ReportFile, Content, False)
2306                EdkLogger.quiet("Build report can be found at %s" % os.path.abspath(self.ReportFile))
2307            except IOError:
2308                EdkLogger.error(None, FILE_WRITE_FAILURE, ExtraData=self.ReportFile)
2309            except:
2310                EdkLogger.error("BuildReport", CODE_ERROR, "Unknown fatal error when generating build report", ExtraData=self.ReportFile, RaiseError=False)
2311                EdkLogger.quiet("(Python %s on %s\n%s)" % (platform.python_version(), sys.platform, traceback.format_exc()))
2312
2313# This acts like the main() function for the script, unless it is 'import'ed into another script.
2314if __name__ == '__main__':
2315    pass
2316
2317