1## @file
2# Routines for generating build report.
3#
4# This module contains the functionality to generate build report after
5# build all target completes successfully.
6#
7# Copyright (c) 2010 - 2017, Intel Corporation. All rights reserved.<BR>
8# This program and the accompanying materials
9# are licensed and made available under the terms and conditions of the BSD License
10# which accompanies this distribution.  The full text of the license may be found at
11# http://opensource.org/licenses/bsd-license.php
12#
13# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
14# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
15#
16
17## Import Modules
18#
19import Common.LongFilePathOs as os
20import re
21import platform
22import textwrap
23import traceback
24import sys
25import time
26import struct
27import hashlib
28import subprocess
29import threading
30from datetime import datetime
31from StringIO import StringIO
32from Common import EdkLogger
33from Common.Misc import SaveFileOnChange
34from Common.Misc import GuidStructureByteArrayToGuidString
35from Common.Misc import GuidStructureStringToGuidString
36from Common.InfClassObject import gComponentType2ModuleType
37from Common.BuildToolError import FILE_WRITE_FAILURE
38from Common.BuildToolError import CODE_ERROR
39from Common.BuildToolError import COMMAND_FAILURE
40from Common.DataType import TAB_LINE_BREAK
41from Common.DataType import TAB_DEPEX
42from Common.DataType import TAB_SLASH
43from Common.DataType import TAB_SPACE_SPLIT
44from Common.DataType import TAB_BRG_PCD
45from Common.DataType import TAB_BRG_LIBRARY
46from Common.DataType import TAB_BACK_SLASH
47from Common.LongFilePathSupport import OpenLongFilePath as open
48from Common.MultipleWorkspace import MultipleWorkspace as mws
49import Common.GlobalData as GlobalData
50from AutoGen.AutoGen import ModuleAutoGen
51from Common.Misc import PathClass
52from Common.String import NormPath
53
54## Pattern to extract contents in EDK DXS files
55gDxsDependencyPattern = re.compile(r"DEPENDENCY_START(.+)DEPENDENCY_END", re.DOTALL)
56
57## Pattern to find total FV total size, occupied size in flash report intermediate file
58gFvTotalSizePattern = re.compile(r"EFI_FV_TOTAL_SIZE = (0x[0-9a-fA-F]+)")
59gFvTakenSizePattern = re.compile(r"EFI_FV_TAKEN_SIZE = (0x[0-9a-fA-F]+)")
60
61## Pattern to find module size and time stamp in module summary report intermediate file
62gModuleSizePattern = re.compile(r"MODULE_SIZE = (\d+)")
63gTimeStampPattern  = re.compile(r"TIME_STAMP = (\d+)")
64
65## Pattern to find GUID value in flash description files
66gPcdGuidPattern = re.compile(r"PCD\((\w+)[.](\w+)\)")
67
68## Pattern to collect offset, GUID value pair in the flash report intermediate file
69gOffsetGuidPattern = re.compile(r"(0x[0-9A-Fa-f]+) ([-A-Fa-f0-9]+)")
70
71## Pattern to find module base address and entry point in fixed flash map file
72gModulePattern = r"\n[-\w]+\s*\(([^,]+),\s*BaseAddress=%(Address)s,\s*EntryPoint=%(Address)s\)\s*\(GUID=([-0-9A-Fa-f]+)[^)]*\)"
73gMapFileItemPattern = re.compile(gModulePattern % {"Address" : "(-?0[xX][0-9A-Fa-f]+)"})
74
75## Pattern to find all module referenced header files in source files
76gIncludePattern  = re.compile(r'#include\s*["<]([^">]+)[">]')
77gIncludePattern2 = re.compile(r"#include\s+EFI_([A-Z_]+)\s*[(]\s*(\w+)\s*[)]")
78
79## Pattern to find the entry point for EDK module using EDKII Glue library
80gGlueLibEntryPoint = re.compile(r"__EDKII_GLUE_MODULE_ENTRY_POINT__\s*=\s*(\w+)")
81
82## Tags for MaxLength of line in report
83gLineMaxLength = 120
84
85## Tags for end of line in report
86gEndOfLine = "\r\n"
87
88## Tags for section start, end and separator
89gSectionStart = ">" + "=" * (gLineMaxLength - 2) + "<"
90gSectionEnd = "<" + "=" * (gLineMaxLength - 2) + ">" + "\n"
91gSectionSep = "=" * gLineMaxLength
92
93## Tags for subsection start, end and separator
94gSubSectionStart = ">" + "-" * (gLineMaxLength - 2) + "<"
95gSubSectionEnd = "<" + "-" * (gLineMaxLength - 2) + ">"
96gSubSectionSep = "-" * gLineMaxLength
97
98
99## The look up table to map PCD type to pair of report display type and DEC type
100gPcdTypeMap = {
101  'FixedAtBuild'     : ('FIXED',  'FixedAtBuild'),
102  'PatchableInModule': ('PATCH',  'PatchableInModule'),
103  'FeatureFlag'      : ('FLAG',   'FeatureFlag'),
104  'Dynamic'          : ('DYN',    'Dynamic'),
105  'DynamicHii'       : ('DYNHII', 'Dynamic'),
106  'DynamicVpd'       : ('DYNVPD', 'Dynamic'),
107  'DynamicEx'        : ('DEX',    'DynamicEx'),
108  'DynamicExHii'     : ('DEXHII', 'DynamicEx'),
109  'DynamicExVpd'     : ('DEXVPD', 'DynamicEx'),
110  }
111
112## The look up table to map module type to driver type
113gDriverTypeMap = {
114  'SEC'               : '0x3 (SECURITY_CORE)',
115  'PEI_CORE'          : '0x4 (PEI_CORE)',
116  'PEIM'              : '0x6 (PEIM)',
117  'DXE_CORE'          : '0x5 (DXE_CORE)',
118  'DXE_DRIVER'        : '0x7 (DRIVER)',
119  'DXE_SAL_DRIVER'    : '0x7 (DRIVER)',
120  'DXE_SMM_DRIVER'    : '0x7 (DRIVER)',
121  'DXE_RUNTIME_DRIVER': '0x7 (DRIVER)',
122  'UEFI_DRIVER'       : '0x7 (DRIVER)',
123  'UEFI_APPLICATION'  : '0x9 (APPLICATION)',
124  'SMM_CORE'          : '0xD (SMM_CORE)',
125  'SMM_DRIVER'        : '0xA (SMM)', # Extension of module type to support PI 1.1 SMM drivers
126  }
127
128## The look up table of the supported opcode in the dependency expression binaries
129gOpCodeList = ["BEFORE", "AFTER", "PUSH", "AND", "OR", "NOT", "TRUE", "FALSE", "END", "SOR"]
130
131##
132# Writes a string to the file object.
133#
134# This function writes a string to the file object and a new line is appended
135# afterwards. It may optionally wraps the string for better readability.
136#
137# @File                      The file object to write
138# @String                    The string to be written to the file
139# @Wrapper                   Indicates whether to wrap the string
140#
141def FileWrite(File, String, Wrapper=False):
142    if Wrapper:
143        String = textwrap.fill(String, 120)
144    File.write(String + gEndOfLine)
145
146##
147# Find all the header file that the module source directly includes.
148#
149# This function scans source code to find all header files the module may
150# include. This is not accurate but very effective to find all the header
151# file the module might include with #include statement.
152#
153# @Source                    The source file name
154# @IncludePathList           The list of include path to find the source file.
155# @IncludeFiles              The dictionary of current found include files.
156#
157def FindIncludeFiles(Source, IncludePathList, IncludeFiles):
158    FileContents = open(Source).read()
159    #
160    # Find header files with pattern #include "XXX.h" or #include <XXX.h>
161    #
162    for Match in gIncludePattern.finditer(FileContents):
163        FileName = Match.group(1).strip()
164        for Dir in [os.path.dirname(Source)] + IncludePathList:
165            FullFileName = os.path.normpath(os.path.join(Dir, FileName))
166            if os.path.exists(FullFileName):
167                IncludeFiles[FullFileName.lower().replace("\\", "/")] = FullFileName
168                break
169
170    #
171    # Find header files with pattern like #include EFI_PPI_CONSUMER(XXX)
172    #
173    for Match in gIncludePattern2.finditer(FileContents):
174        Key = Match.group(2)
175        Type = Match.group(1)
176        if "ARCH_PROTOCOL" in Type:
177            FileName = "ArchProtocol/%(Key)s/%(Key)s.h" % {"Key" : Key}
178        elif "PROTOCOL" in Type:
179            FileName = "Protocol/%(Key)s/%(Key)s.h" % {"Key" : Key}
180        elif "PPI" in Type:
181            FileName = "Ppi/%(Key)s/%(Key)s.h" % {"Key" : Key}
182        elif "GUID" in Type:
183            FileName = "Guid/%(Key)s/%(Key)s.h" % {"Key" : Key}
184        else:
185            continue
186        for Dir in IncludePathList:
187            FullFileName = os.path.normpath(os.path.join(Dir, FileName))
188            if os.path.exists(FullFileName):
189                IncludeFiles[FullFileName.lower().replace("\\", "/")] = FullFileName
190                break
191
192## Split each lines in file
193#
194#  This method is used to split the lines in file to make the length of each line
195#  less than MaxLength.
196#
197#  @param      Content           The content of file
198#  @param      MaxLength         The Max Length of the line
199#
200def FileLinesSplit(Content=None, MaxLength=None):
201    ContentList = Content.split(TAB_LINE_BREAK)
202    NewContent = ''
203    NewContentList = []
204    for Line in ContentList:
205        while len(Line.rstrip()) > MaxLength:
206            LineSpaceIndex = Line.rfind(TAB_SPACE_SPLIT, 0, MaxLength)
207            LineSlashIndex = Line.rfind(TAB_SLASH, 0, MaxLength)
208            LineBackSlashIndex = Line.rfind(TAB_BACK_SLASH, 0, MaxLength)
209            if max(LineSpaceIndex, LineSlashIndex, LineBackSlashIndex) > 0:
210                LineBreakIndex = max(LineSpaceIndex, LineSlashIndex, LineBackSlashIndex)
211            else:
212                LineBreakIndex = MaxLength
213            NewContentList.append(Line[:LineBreakIndex])
214            Line = Line[LineBreakIndex:]
215        if Line:
216            NewContentList.append(Line)
217    for NewLine in NewContentList:
218        NewContent += NewLine + TAB_LINE_BREAK
219
220    NewContent = NewContent.replace(TAB_LINE_BREAK, gEndOfLine).replace('\r\r\n', gEndOfLine)
221    return NewContent
222
223
224
225##
226# Parse binary dependency expression section
227#
228# This utility class parses the dependency expression section and translate the readable
229# GUID name and value.
230#
231class DepexParser(object):
232    ##
233    # Constructor function for class DepexParser
234    #
235    # This constructor function collect GUID values so that the readable
236    # GUID name can be translated.
237    #
238    # @param self            The object pointer
239    # @param Wa              Workspace context information
240    #
241    def __init__(self, Wa):
242        self._GuidDb = {}
243        for Pa in Wa.AutoGenObjectList:
244            for Package in Pa.PackageList:
245                for Protocol in Package.Protocols:
246                    GuidValue = GuidStructureStringToGuidString(Package.Protocols[Protocol])
247                    self._GuidDb[GuidValue.upper()] = Protocol
248                for Ppi in Package.Ppis:
249                    GuidValue = GuidStructureStringToGuidString(Package.Ppis[Ppi])
250                    self._GuidDb[GuidValue.upper()] = Ppi
251                for Guid in Package.Guids:
252                    GuidValue = GuidStructureStringToGuidString(Package.Guids[Guid])
253                    self._GuidDb[GuidValue.upper()] = Guid
254
255    ##
256    # Parse the binary dependency expression files.
257    #
258    # This function parses the binary dependency expression file and translate it
259    # to the instruction list.
260    #
261    # @param self            The object pointer
262    # @param DepexFileName   The file name of binary dependency expression file.
263    #
264    def ParseDepexFile(self, DepexFileName):
265        DepexFile = open(DepexFileName, "rb")
266        DepexStatement = []
267        OpCode = DepexFile.read(1)
268        while OpCode:
269            Statement = gOpCodeList[struct.unpack("B", OpCode)[0]]
270            if Statement in ["BEFORE", "AFTER", "PUSH"]:
271                GuidValue = "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X" % \
272                            struct.unpack("=LHHBBBBBBBB", DepexFile.read(16))
273                GuidString = self._GuidDb.get(GuidValue, GuidValue)
274                Statement = "%s %s" % (Statement, GuidString)
275            DepexStatement.append(Statement)
276            OpCode = DepexFile.read(1)
277
278        return DepexStatement
279
280##
281# Reports library information
282#
283# This class reports the module library subsection in the build report file.
284#
285class LibraryReport(object):
286    ##
287    # Constructor function for class LibraryReport
288    #
289    # This constructor function generates LibraryReport object for
290    # a module.
291    #
292    # @param self            The object pointer
293    # @param M               Module context information
294    #
295    def __init__(self, M):
296        self.LibraryList = []
297        if int(str(M.AutoGenVersion), 0) >= 0x00010005:
298            self._EdkIIModule = True
299        else:
300            self._EdkIIModule = False
301
302        for Lib in M.DependentLibraryList:
303            LibInfPath = str(Lib)
304            LibClassList = Lib.LibraryClass[0].LibraryClass
305            LibConstructorList = Lib.ConstructorList
306            LibDesstructorList = Lib.DestructorList
307            LibDepexList = Lib.DepexExpression[M.Arch, M.ModuleType]
308            self.LibraryList.append((LibInfPath, LibClassList, LibConstructorList, LibDesstructorList, LibDepexList))
309
310    ##
311    # Generate report for module library information
312    #
313    # This function generates report for the module library.
314    # If the module is EDKII style one, the additional library class, library
315    # constructor/destructor and dependency expression may also be reported.
316    #
317    # @param self            The object pointer
318    # @param File            The file object for report
319    #
320    def GenerateReport(self, File):
321        FileWrite(File, gSubSectionStart)
322        FileWrite(File, TAB_BRG_LIBRARY)
323        if len(self.LibraryList) > 0:
324            FileWrite(File, gSubSectionSep)
325            for LibraryItem in self.LibraryList:
326                LibInfPath = LibraryItem[0]
327                FileWrite(File, LibInfPath)
328
329                #
330                # Report library class, library constructor and destructor for
331                # EDKII style module.
332                #
333                if self._EdkIIModule:
334                    LibClass = LibraryItem[1]
335                    EdkIILibInfo = ""
336                    LibConstructor = " ".join(LibraryItem[2])
337                    if LibConstructor:
338                        EdkIILibInfo += " C = " + LibConstructor
339                    LibDestructor = " ".join(LibraryItem[3])
340                    if LibDestructor:
341                        EdkIILibInfo += " D = " + LibDestructor
342                    LibDepex = " ".join(LibraryItem[4])
343                    if LibDepex:
344                        EdkIILibInfo += " Depex = " + LibDepex
345                    if EdkIILibInfo:
346                        FileWrite(File, "{%s: %s}" % (LibClass, EdkIILibInfo))
347                    else:
348                        FileWrite(File, "{%s}" % LibClass)
349
350        FileWrite(File, gSubSectionEnd)
351
352##
353# Reports dependency expression information
354#
355# This class reports the module dependency expression subsection in the build report file.
356#
357class DepexReport(object):
358    ##
359    # Constructor function for class DepexReport
360    #
361    # This constructor function generates DepexReport object for
362    # a module. If the module source contains the DXS file (usually EDK
363    # style module), it uses the dependency in DXS file; otherwise,
364    # it uses the dependency expression from its own INF [Depex] section
365    # and then merges with the ones from its dependent library INF.
366    #
367    # @param self            The object pointer
368    # @param M               Module context information
369    #
370    def __init__(self, M):
371        self.Depex = ""
372        self._DepexFileName = os.path.join(M.BuildDir, "OUTPUT", M.Module.BaseName + ".depex")
373        ModuleType = M.ModuleType
374        if not ModuleType:
375            ModuleType = gComponentType2ModuleType.get(M.ComponentType, "")
376
377        if ModuleType in ["SEC", "PEI_CORE", "DXE_CORE", "SMM_CORE", "UEFI_APPLICATION"]:
378            return
379
380        for Source in M.SourceFileList:
381            if os.path.splitext(Source.Path)[1].lower() == ".dxs":
382                Match = gDxsDependencyPattern.search(open(Source.Path).read())
383                if Match:
384                    self.Depex = Match.group(1).strip()
385                    self.Source = "DXS"
386                    break
387        else:
388            self.Depex = M.DepexExpressionList.get(M.ModuleType, "")
389            self.ModuleDepex = " ".join(M.Module.DepexExpression[M.Arch, M.ModuleType])
390            if not self.ModuleDepex:
391                self.ModuleDepex = "(None)"
392
393            LibDepexList = []
394            for Lib in M.DependentLibraryList:
395                LibDepex = " ".join(Lib.DepexExpression[M.Arch, M.ModuleType]).strip()
396                if LibDepex != "":
397                    LibDepexList.append("(" + LibDepex + ")")
398            self.LibraryDepex = " AND ".join(LibDepexList)
399            if not self.LibraryDepex:
400                self.LibraryDepex = "(None)"
401            self.Source = "INF"
402
403    ##
404    # Generate report for module dependency expression information
405    #
406    # This function generates report for the module dependency expression.
407    #
408    # @param self              The object pointer
409    # @param File              The file object for report
410    # @param GlobalDepexParser The platform global Dependency expression parser object
411    #
412    def GenerateReport(self, File, GlobalDepexParser):
413        if not self.Depex:
414            FileWrite(File, gSubSectionStart)
415            FileWrite(File, TAB_DEPEX)
416            FileWrite(File, gSubSectionEnd)
417            return
418        FileWrite(File, gSubSectionStart)
419        if os.path.isfile(self._DepexFileName):
420            try:
421                DepexStatements = GlobalDepexParser.ParseDepexFile(self._DepexFileName)
422                FileWrite(File, "Final Dependency Expression (DEPEX) Instructions")
423                for DepexStatement in DepexStatements:
424                    FileWrite(File, "  %s" % DepexStatement)
425                FileWrite(File, gSubSectionSep)
426            except:
427                EdkLogger.warn(None, "Dependency expression file is corrupted", self._DepexFileName)
428
429        FileWrite(File, "Dependency Expression (DEPEX) from %s" % self.Source)
430
431        if self.Source == "INF":
432            FileWrite(File, "%s" % self.Depex, True)
433            FileWrite(File, gSubSectionSep)
434            FileWrite(File, "From Module INF:  %s" % self.ModuleDepex, True)
435            FileWrite(File, "From Library INF: %s" % self.LibraryDepex, True)
436        else:
437            FileWrite(File, "%s" % self.Depex)
438        FileWrite(File, gSubSectionEnd)
439
440##
441# Reports dependency expression information
442#
443# This class reports the module build flags subsection in the build report file.
444#
445class BuildFlagsReport(object):
446    ##
447    # Constructor function for class BuildFlagsReport
448    #
449    # This constructor function generates BuildFlagsReport object for
450    # a module. It reports the build tool chain tag and all relevant
451    # build flags to build the module.
452    #
453    # @param self            The object pointer
454    # @param M               Module context information
455    #
456    def __init__(self, M):
457        BuildOptions = {}
458        #
459        # Add build flags according to source file extension so that
460        # irrelevant ones can be filtered out.
461        #
462        for Source in M.SourceFileList:
463            Ext = os.path.splitext(Source.File)[1].lower()
464            if Ext in [".c", ".cc", ".cpp"]:
465                BuildOptions["CC"] = 1
466            elif Ext in [".s", ".asm"]:
467                BuildOptions["PP"] = 1
468                BuildOptions["ASM"] = 1
469            elif Ext in [".vfr"]:
470                BuildOptions["VFRPP"] = 1
471                BuildOptions["VFR"] = 1
472            elif Ext in [".dxs"]:
473                BuildOptions["APP"] = 1
474                BuildOptions["CC"] = 1
475            elif Ext in [".asl"]:
476                BuildOptions["ASLPP"] = 1
477                BuildOptions["ASL"] = 1
478            elif Ext in [".aslc"]:
479                BuildOptions["ASLCC"] = 1
480                BuildOptions["ASLDLINK"] = 1
481                BuildOptions["CC"] = 1
482            elif Ext in [".asm16"]:
483                BuildOptions["ASMLINK"] = 1
484            BuildOptions["SLINK"] = 1
485            BuildOptions["DLINK"] = 1
486
487        #
488        # Save module build flags.
489        #
490        self.ToolChainTag = M.ToolChain
491        self.BuildFlags = {}
492        for Tool in BuildOptions:
493            self.BuildFlags[Tool + "_FLAGS"] = M.BuildOption.get(Tool, {}).get("FLAGS", "")
494
495    ##
496    # Generate report for module build flags information
497    #
498    # This function generates report for the module build flags expression.
499    #
500    # @param self            The object pointer
501    # @param File            The file object for report
502    #
503    def GenerateReport(self, File):
504        FileWrite(File, gSubSectionStart)
505        FileWrite(File, "Build Flags")
506        FileWrite(File, "Tool Chain Tag: %s" % self.ToolChainTag)
507        for Tool in self.BuildFlags:
508            FileWrite(File, gSubSectionSep)
509            FileWrite(File, "%s = %s" % (Tool, self.BuildFlags[Tool]), True)
510
511        FileWrite(File, gSubSectionEnd)
512
513
514##
515# Reports individual module information
516#
517# This class reports the module section in the build report file.
518# It comprises of module summary, module PCD, library, dependency expression,
519# build flags sections.
520#
521class ModuleReport(object):
522    ##
523    # Constructor function for class ModuleReport
524    #
525    # This constructor function generates ModuleReport object for
526    # a separate module in a platform build.
527    #
528    # @param self            The object pointer
529    # @param M               Module context information
530    # @param ReportType      The kind of report items in the final report file
531    #
532    def __init__(self, M, ReportType):
533        self.ModuleName = M.Module.BaseName
534        self.ModuleInfPath = M.MetaFile.File
535        self.FileGuid = M.Guid
536        self.Size = 0
537        self.BuildTimeStamp = None
538        self.Hash = 0
539        self.DriverType = ""
540        if not M.IsLibrary:
541            ModuleType = M.ModuleType
542            if not ModuleType:
543                ModuleType = gComponentType2ModuleType.get(M.ComponentType, "")
544            #
545            # If a module complies to PI 1.1, promote Module type to "SMM_DRIVER"
546            #
547            if ModuleType == "DXE_SMM_DRIVER":
548                PiSpec = M.Module.Specification.get("PI_SPECIFICATION_VERSION", "0x00010000")
549                if int(PiSpec, 0) >= 0x0001000A:
550                    ModuleType = "SMM_DRIVER"
551            self.DriverType = gDriverTypeMap.get(ModuleType, "0x2 (FREE_FORM)")
552        self.UefiSpecVersion = M.Module.Specification.get("UEFI_SPECIFICATION_VERSION", "")
553        self.PiSpecVersion = M.Module.Specification.get("PI_SPECIFICATION_VERSION", "")
554        self.PciDeviceId = M.Module.Defines.get("PCI_DEVICE_ID", "")
555        self.PciVendorId = M.Module.Defines.get("PCI_VENDOR_ID", "")
556        self.PciClassCode = M.Module.Defines.get("PCI_CLASS_CODE", "")
557
558        self._BuildDir = M.BuildDir
559        self.ModulePcdSet = {}
560        if "PCD" in ReportType:
561            #
562            # Collect all module used PCD set: module INF referenced directly or indirectly.
563            # It also saves module INF default values of them in case they exist.
564            #
565            for Pcd in M.ModulePcdList + M.LibraryPcdList:
566                self.ModulePcdSet.setdefault((Pcd.TokenCName, Pcd.TokenSpaceGuidCName, Pcd.Type), (Pcd.InfDefaultValue, Pcd.DefaultValue))
567
568        self.LibraryReport = None
569        if "LIBRARY" in ReportType:
570            self.LibraryReport = LibraryReport(M)
571
572        self.DepexReport = None
573        if "DEPEX" in ReportType:
574            self.DepexReport = DepexReport(M)
575
576        if "BUILD_FLAGS" in ReportType:
577            self.BuildFlagsReport = BuildFlagsReport(M)
578
579
580    ##
581    # Generate report for module information
582    #
583    # This function generates report for separate module expression
584    # in a platform build.
585    #
586    # @param self                   The object pointer
587    # @param File                   The file object for report
588    # @param GlobalPcdReport        The platform global PCD report object
589    # @param GlobalPredictionReport The platform global Prediction report object
590    # @param GlobalDepexParser      The platform global Dependency expression parser object
591    # @param ReportType             The kind of report items in the final report file
592    #
593    def GenerateReport(self, File, GlobalPcdReport, GlobalPredictionReport, GlobalDepexParser, ReportType):
594        FileWrite(File, gSectionStart)
595
596        FwReportFileName = os.path.join(self._BuildDir, "DEBUG", self.ModuleName + ".txt")
597        if os.path.isfile(FwReportFileName):
598            try:
599                FileContents = open(FwReportFileName).read()
600                Match = gModuleSizePattern.search(FileContents)
601                if Match:
602                    self.Size = int(Match.group(1))
603
604                Match = gTimeStampPattern.search(FileContents)
605                if Match:
606                    self.BuildTimeStamp = datetime.fromtimestamp(int(Match.group(1)))
607            except IOError:
608                EdkLogger.warn(None, "Fail to read report file", FwReportFileName)
609
610        if "HASH" in ReportType:
611            OutputDir = os.path.join(self._BuildDir, "OUTPUT")
612            DefaultEFIfile = os.path.join(OutputDir, self.ModuleName + ".efi")
613            if os.path.isfile(DefaultEFIfile):
614                Tempfile = os.path.join(OutputDir, self.ModuleName + "_hash.tmp")
615                # rebase the efi image since its base address may not zero
616                cmd = ["GenFw", "--rebase", str(0), "-o", Tempfile, DefaultEFIfile]
617                try:
618                    PopenObject = subprocess.Popen(' '.join(cmd), stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
619                except Exception, X:
620                    EdkLogger.error("GenFw", COMMAND_FAILURE, ExtraData="%s: %s" % (str(X), cmd[0]))
621                EndOfProcedure = threading.Event()
622                EndOfProcedure.clear()
623                if PopenObject.stderr:
624                    StdErrThread = threading.Thread(target=ReadMessage, args=(PopenObject.stderr, EdkLogger.quiet, EndOfProcedure))
625                    StdErrThread.setName("STDERR-Redirector")
626                    StdErrThread.setDaemon(False)
627                    StdErrThread.start()
628                # waiting for program exit
629                PopenObject.wait()
630                if PopenObject.stderr:
631                    StdErrThread.join()
632                if PopenObject.returncode != 0:
633                    EdkLogger.error("GenFw", COMMAND_FAILURE, "Failed to generate firmware hash image for %s" % (DefaultEFIfile))
634                if os.path.isfile(Tempfile):
635                    self.Hash = hashlib.sha1()
636                    buf = open(Tempfile, 'rb').read()
637                    if self.Hash.update(buf):
638                        self.Hash = self.Hash.update(buf)
639                    self.Hash = self.Hash.hexdigest()
640                    os.remove(Tempfile)
641
642        FileWrite(File, "Module Summary")
643        FileWrite(File, "Module Name:          %s" % self.ModuleName)
644        FileWrite(File, "Module INF Path:      %s" % self.ModuleInfPath)
645        FileWrite(File, "File GUID:            %s" % self.FileGuid)
646        if self.Size:
647            FileWrite(File, "Size:                 0x%X (%.2fK)" % (self.Size, self.Size / 1024.0))
648        if self.Hash:
649            FileWrite(File, "SHA1 HASH:            %s *%s" % (self.Hash, self.ModuleName + ".efi"))
650        if self.BuildTimeStamp:
651            FileWrite(File, "Build Time Stamp:     %s" % self.BuildTimeStamp)
652        if self.DriverType:
653            FileWrite(File, "Driver Type:          %s" % self.DriverType)
654        if self.UefiSpecVersion:
655            FileWrite(File, "UEFI Spec Version:    %s" % self.UefiSpecVersion)
656        if self.PiSpecVersion:
657            FileWrite(File, "PI Spec Version:      %s" % self.PiSpecVersion)
658        if self.PciDeviceId:
659            FileWrite(File, "PCI Device ID:        %s" % self.PciDeviceId)
660        if self.PciVendorId:
661            FileWrite(File, "PCI Vendor ID:        %s" % self.PciVendorId)
662        if self.PciClassCode:
663            FileWrite(File, "PCI Class Code:       %s" % self.PciClassCode)
664
665        FileWrite(File, gSectionSep)
666
667        if "PCD" in ReportType:
668            GlobalPcdReport.GenerateReport(File, self.ModulePcdSet)
669
670        if "LIBRARY" in ReportType:
671            self.LibraryReport.GenerateReport(File)
672
673        if "DEPEX" in ReportType:
674            self.DepexReport.GenerateReport(File, GlobalDepexParser)
675
676        if "BUILD_FLAGS" in ReportType:
677            self.BuildFlagsReport.GenerateReport(File)
678
679        if "FIXED_ADDRESS" in ReportType and self.FileGuid:
680            GlobalPredictionReport.GenerateReport(File, self.FileGuid)
681
682        FileWrite(File, gSectionEnd)
683
684def ReadMessage(From, To, ExitFlag):
685    while True:
686        # read one line a time
687        Line = From.readline()
688        # empty string means "end"
689        if Line != None and Line != "":
690            To(Line.rstrip())
691        else:
692            break
693        if ExitFlag.isSet():
694            break
695
696##
697# Reports platform and module PCD information
698#
699# This class reports the platform PCD section and module PCD subsection
700# in the build report file.
701#
702class PcdReport(object):
703    ##
704    # Constructor function for class PcdReport
705    #
706    # This constructor function generates PcdReport object a platform build.
707    # It collects the whole PCD database from platform DSC files, platform
708    # flash description file and package DEC files.
709    #
710    # @param self            The object pointer
711    # @param Wa              Workspace context information
712    #
713    def __init__(self, Wa):
714        self.AllPcds = {}
715        self.UnusedPcds = {}
716        self.ConditionalPcds = {}
717        self.MaxLen = 0
718        if Wa.FdfProfile:
719            self.FdfPcdSet = Wa.FdfProfile.PcdDict
720        else:
721            self.FdfPcdSet = {}
722
723        self.ModulePcdOverride = {}
724        for Pa in Wa.AutoGenObjectList:
725            #
726            # Collect all platform referenced PCDs and grouped them by PCD token space
727            # GUID C Names
728            #
729            for Pcd in Pa.AllPcdList:
730                PcdList = self.AllPcds.setdefault(Pcd.TokenSpaceGuidCName, {}).setdefault(Pcd.Type, [])
731                if Pcd not in PcdList:
732                    PcdList.append(Pcd)
733                if len(Pcd.TokenCName) > self.MaxLen:
734                    self.MaxLen = len(Pcd.TokenCName)
735            #
736            # Collect the PCD defined in DSC/FDF file, but not used in module
737            #
738            UnusedPcdFullList = []
739            for item in Pa.Platform.Pcds:
740                Pcd = Pa.Platform.Pcds[item]
741                if not Pcd.Type:
742                    PcdTypeFlag = False
743                    for package in Pa.PackageList:
744                        for T in ["FixedAtBuild", "PatchableInModule", "FeatureFlag", "Dynamic", "DynamicEx"]:
745                            if (Pcd.TokenCName, Pcd.TokenSpaceGuidCName, T) in package.Pcds:
746                                Pcd.Type = T
747                                PcdTypeFlag = True
748                                if not Pcd.DatumType:
749                                    Pcd.DatumType = package.Pcds[(Pcd.TokenCName, Pcd.TokenSpaceGuidCName, T)].DatumType
750                                break
751                        if PcdTypeFlag:
752                            break
753                if not Pcd.DatumType:
754                    PcdType = Pcd.Type
755                    # Try to remove Hii and Vpd suffix
756                    if PcdType.startswith("DynamicEx"):
757                        PcdType = "DynamicEx"
758                    elif PcdType.startswith("Dynamic"):
759                        PcdType = "Dynamic"
760                    for package in Pa.PackageList:
761                        if (Pcd.TokenCName, Pcd.TokenSpaceGuidCName, PcdType) in package.Pcds:
762                            Pcd.DatumType = package.Pcds[(Pcd.TokenCName, Pcd.TokenSpaceGuidCName, PcdType)].DatumType
763                            break
764
765                PcdList = self.AllPcds.setdefault(Pcd.TokenSpaceGuidCName, {}).setdefault(Pcd.Type, [])
766                if Pcd not in PcdList and Pcd not in UnusedPcdFullList:
767                    UnusedPcdFullList.append(Pcd)
768                if len(Pcd.TokenCName) > self.MaxLen:
769                    self.MaxLen = len(Pcd.TokenCName)
770
771            if GlobalData.gConditionalPcds:
772                for PcdItem in GlobalData.gConditionalPcds:
773                    if '.' in PcdItem:
774                        (TokenSpaceGuidCName, TokenCName) = PcdItem.split('.')
775                        if (TokenCName, TokenSpaceGuidCName) in Pa.Platform.Pcds.keys():
776                            Pcd = Pa.Platform.Pcds[(TokenCName, TokenSpaceGuidCName)]
777                            PcdList = self.ConditionalPcds.setdefault(Pcd.TokenSpaceGuidCName, {}).setdefault(Pcd.Type, [])
778                            if Pcd not in PcdList:
779                                PcdList.append(Pcd)
780
781            UnusedPcdList = []
782            if UnusedPcdFullList:
783                for Pcd in UnusedPcdFullList:
784                    if Pcd.TokenSpaceGuidCName + '.' + Pcd.TokenCName in GlobalData.gConditionalPcds:
785                        continue
786                    UnusedPcdList.append(Pcd)
787
788            for Pcd in UnusedPcdList:
789                PcdList = self.UnusedPcds.setdefault(Pcd.TokenSpaceGuidCName, {}).setdefault(Pcd.Type, [])
790                if Pcd not in PcdList:
791                    PcdList.append(Pcd)
792
793            for Module in Pa.Platform.Modules.values():
794                #
795                # Collect module override PCDs
796                #
797                for ModulePcd in Module.M.ModulePcdList + Module.M.LibraryPcdList:
798                    TokenCName = ModulePcd.TokenCName
799                    TokenSpaceGuid = ModulePcd.TokenSpaceGuidCName
800                    ModuleDefault = ModulePcd.DefaultValue
801                    ModulePath = os.path.basename(Module.M.MetaFile.File)
802                    self.ModulePcdOverride.setdefault((TokenCName, TokenSpaceGuid), {})[ModulePath] = ModuleDefault
803
804
805        #
806        # Collect PCD DEC default value.
807        #
808        self.DecPcdDefault = {}
809        for Pa in Wa.AutoGenObjectList:
810            for Package in Pa.PackageList:
811                for (TokenCName, TokenSpaceGuidCName, DecType) in Package.Pcds:
812                    DecDefaultValue = Package.Pcds[TokenCName, TokenSpaceGuidCName, DecType].DefaultValue
813                    self.DecPcdDefault.setdefault((TokenCName, TokenSpaceGuidCName, DecType), DecDefaultValue)
814        #
815        # Collect PCDs defined in DSC common section
816        #
817        self.DscPcdDefault = {}
818        for Arch in Wa.ArchList:
819            Platform = Wa.BuildDatabase[Wa.MetaFile, Arch, Wa.BuildTarget, Wa.ToolChain]
820            for (TokenCName, TokenSpaceGuidCName) in Platform.Pcds:
821                DscDefaultValue = Platform.Pcds[(TokenCName, TokenSpaceGuidCName)].DefaultValue
822                if DscDefaultValue:
823                    self.DscPcdDefault[(TokenCName, TokenSpaceGuidCName)] = DscDefaultValue
824
825    def GenerateReport(self, File, ModulePcdSet):
826        if self.ConditionalPcds:
827            self.GenerateReportDetail(File, ModulePcdSet, 1)
828        if self.UnusedPcds:
829            self.GenerateReportDetail(File, ModulePcdSet, 2)
830        self.GenerateReportDetail(File, ModulePcdSet)
831
832    ##
833    # Generate report for PCD information
834    #
835    # This function generates report for separate module expression
836    # in a platform build.
837    #
838    # @param self            The object pointer
839    # @param File            The file object for report
840    # @param ModulePcdSet    Set of all PCDs referenced by module or None for
841    #                        platform PCD report
842    # @param ReportySubType  0 means platform/module PCD report, 1 means Conditional
843    #                        directives section report, 2 means Unused Pcds section report
844    # @param DscOverridePcds Module DSC override PCDs set
845    #
846    def GenerateReportDetail(self, File, ModulePcdSet, ReportSubType = 0):
847        PcdDict = self.AllPcds
848        if ReportSubType == 1:
849            PcdDict = self.ConditionalPcds
850        elif ReportSubType == 2:
851            PcdDict = self.UnusedPcds
852
853        if ModulePcdSet == None:
854            FileWrite(File, gSectionStart)
855            if ReportSubType == 1:
856                FileWrite(File, "Conditional Directives used by the build system")
857            elif ReportSubType == 2:
858                FileWrite(File, "PCDs not used by modules or in conditional directives")
859            else:
860                FileWrite(File, "Platform Configuration Database Report")
861
862            FileWrite(File, "  *B  - PCD override in the build option")
863            FileWrite(File, "  *P  - Platform scoped PCD override in DSC file")
864            FileWrite(File, "  *F  - Platform scoped PCD override in FDF file")
865            if not ReportSubType:
866                FileWrite(File, "  *M  - Module scoped PCD override")
867            FileWrite(File, gSectionSep)
868        else:
869            if not ReportSubType:
870                #
871                # For module PCD sub-section
872                #
873                FileWrite(File, gSubSectionStart)
874                FileWrite(File, TAB_BRG_PCD)
875                FileWrite(File, gSubSectionSep)
876
877        for Key in PcdDict:
878            #
879            # Group PCD by their token space GUID C Name
880            #
881            First = True
882            for Type in PcdDict[Key]:
883                #
884                # Group PCD by their usage type
885                #
886                TypeName, DecType = gPcdTypeMap.get(Type, ("", Type))
887                for Pcd in PcdDict[Key][Type]:
888                    PcdTokenCName = Pcd.TokenCName
889                    MixedPcdFlag = False
890                    if GlobalData.MixedPcd:
891                        for PcdKey in GlobalData.MixedPcd:
892                            if (Pcd.TokenCName, Pcd.TokenSpaceGuidCName) in GlobalData.MixedPcd[PcdKey]:
893                                PcdTokenCName = PcdKey[0]
894                                MixedPcdFlag = True
895                        if MixedPcdFlag and not ModulePcdSet:
896                            continue
897                    #
898                    # Get PCD default value and their override relationship
899                    #
900                    DecDefaultValue = self.DecPcdDefault.get((Pcd.TokenCName, Pcd.TokenSpaceGuidCName, DecType))
901                    DscDefaultValue = self.DscPcdDefault.get((Pcd.TokenCName, Pcd.TokenSpaceGuidCName))
902                    DscDefaultValue = self.FdfPcdSet.get((Pcd.TokenCName, Key), DscDefaultValue)
903                    InfDefaultValue = None
904
905                    PcdValue = DecDefaultValue
906                    if DscDefaultValue:
907                        PcdValue = DscDefaultValue
908                    if ModulePcdSet != None:
909                        if (Pcd.TokenCName, Pcd.TokenSpaceGuidCName, Type) not in ModulePcdSet:
910                            continue
911                        InfDefault, PcdValue = ModulePcdSet[Pcd.TokenCName, Pcd.TokenSpaceGuidCName, Type]
912                        if InfDefault == "":
913                            InfDefault = None
914
915                    BuildOptionMatch = False
916                    if GlobalData.BuildOptionPcd:
917                        for pcd in GlobalData.BuildOptionPcd:
918                            if (Pcd.TokenSpaceGuidCName, Pcd.TokenCName) == (pcd[0], pcd[1]):
919                                PcdValue = pcd[2]
920                                BuildOptionMatch = True
921                                break
922
923                    if First:
924                        if ModulePcdSet == None:
925                            FileWrite(File, "")
926                        FileWrite(File, Key)
927                        First = False
928
929
930                    if Pcd.DatumType in ('UINT8', 'UINT16', 'UINT32', 'UINT64'):
931                        PcdValueNumber = int(PcdValue.strip(), 0)
932                        if DecDefaultValue == None:
933                            DecMatch = True
934                        else:
935                            DecDefaultValueNumber = int(DecDefaultValue.strip(), 0)
936                            DecMatch = (DecDefaultValueNumber == PcdValueNumber)
937
938                        if InfDefaultValue == None:
939                            InfMatch = True
940                        else:
941                            InfDefaultValueNumber = int(InfDefaultValue.strip(), 0)
942                            InfMatch = (InfDefaultValueNumber == PcdValueNumber)
943
944                        if DscDefaultValue == None:
945                            DscMatch = True
946                        else:
947                            DscDefaultValueNumber = int(DscDefaultValue.strip(), 0)
948                            DscMatch = (DscDefaultValueNumber == PcdValueNumber)
949                    else:
950                        if DecDefaultValue == None:
951                            DecMatch = True
952                        else:
953                            DecMatch = (DecDefaultValue.strip() == PcdValue.strip())
954
955                        if InfDefaultValue == None:
956                            InfMatch = True
957                        else:
958                            InfMatch = (InfDefaultValue.strip() == PcdValue.strip())
959
960                        if DscDefaultValue == None:
961                            DscMatch = True
962                        else:
963                            DscMatch = (DscDefaultValue.strip() == PcdValue.strip())
964
965                    #
966                    # Report PCD item according to their override relationship
967                    #
968                    if BuildOptionMatch:
969                        FileWrite(File, ' *B %-*s: %6s %10s = %-22s' % (self.MaxLen, PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', PcdValue.strip()))
970                    elif DecMatch and InfMatch:
971                        FileWrite(File, '    %-*s: %6s %10s = %-22s' % (self.MaxLen, PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', PcdValue.strip()))
972                    else:
973                        if DscMatch:
974                            if (Pcd.TokenCName, Key) in self.FdfPcdSet:
975                                FileWrite(File, ' *F %-*s: %6s %10s = %-22s' % (self.MaxLen, PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', PcdValue.strip()))
976                            else:
977                                FileWrite(File, ' *P %-*s: %6s %10s = %-22s' % (self.MaxLen, PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', PcdValue.strip()))
978                        else:
979                            FileWrite(File, ' *M %-*s: %6s %10s = %-22s' % (self.MaxLen, PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', PcdValue.strip()))
980
981                    if TypeName in ('DYNHII', 'DEXHII', 'DYNVPD', 'DEXVPD'):
982                        for SkuInfo in Pcd.SkuInfoList.values():
983                            if TypeName in ('DYNHII', 'DEXHII'):
984                                FileWrite(File, '%*s: %s: %s' % (self.MaxLen + 4, SkuInfo.VariableGuid, SkuInfo.VariableName, SkuInfo.VariableOffset))
985                            else:
986                                FileWrite(File, '%*s' % (self.MaxLen + 4, SkuInfo.VpdOffset))
987
988                    if not DscMatch and DscDefaultValue != None:
989                        FileWrite(File, '    %*s = %s' % (self.MaxLen + 19, 'DSC DEFAULT', DscDefaultValue.strip()))
990
991                    if not InfMatch and InfDefaultValue != None:
992                        FileWrite(File, '    %*s = %s' % (self.MaxLen + 19, 'INF DEFAULT', InfDefaultValue.strip()))
993
994                    if not DecMatch and DecDefaultValue != None:
995                        FileWrite(File, '    %*s = %s' % (self.MaxLen + 19, 'DEC DEFAULT', DecDefaultValue.strip()))
996
997                    if ModulePcdSet == None:
998                        if not BuildOptionMatch:
999                            ModuleOverride = self.ModulePcdOverride.get((Pcd.TokenCName, Pcd.TokenSpaceGuidCName), {})
1000                            for ModulePath in ModuleOverride:
1001                                ModuleDefault = ModuleOverride[ModulePath]
1002                                if Pcd.DatumType in ('UINT8', 'UINT16', 'UINT32', 'UINT64'):
1003                                    ModulePcdDefaultValueNumber = int(ModuleDefault.strip(), 0)
1004                                    Match = (ModulePcdDefaultValueNumber == PcdValueNumber)
1005                                else:
1006                                    Match = (ModuleDefault.strip() == PcdValue.strip())
1007                                if Match:
1008                                    continue
1009                                FileWrite(File, ' *M %-*s = %s' % (self.MaxLen + 19, ModulePath, ModuleDefault.strip()))
1010
1011        if ModulePcdSet == None:
1012            FileWrite(File, gSectionEnd)
1013        else:
1014            if not ReportSubType:
1015                FileWrite(File, gSubSectionEnd)
1016
1017
1018
1019##
1020# Reports platform and module Prediction information
1021#
1022# This class reports the platform execution order prediction section and
1023# module load fixed address prediction subsection in the build report file.
1024#
1025class PredictionReport(object):
1026    ##
1027    # Constructor function for class PredictionReport
1028    #
1029    # This constructor function generates PredictionReport object for the platform.
1030    #
1031    # @param self:           The object pointer
1032    # @param Wa              Workspace context information
1033    #
1034    def __init__(self, Wa):
1035        self._MapFileName = os.path.join(Wa.BuildDir, Wa.Name + ".map")
1036        self._MapFileParsed = False
1037        self._EotToolInvoked = False
1038        self._FvDir = Wa.FvDir
1039        self._EotDir = Wa.BuildDir
1040        self._FfsEntryPoint = {}
1041        self._GuidMap = {}
1042        self._SourceList = []
1043        self.FixedMapDict = {}
1044        self.ItemList = []
1045        self.MaxLen = 0
1046
1047        #
1048        # Collect all platform reference source files and GUID C Name
1049        #
1050        for Pa in Wa.AutoGenObjectList:
1051            for Module in Pa.LibraryAutoGenList + Pa.ModuleAutoGenList:
1052                #
1053                # BASE typed modules are EFI agnostic, so we need not scan
1054                # their source code to find PPI/Protocol produce or consume
1055                # information.
1056                #
1057                if Module.ModuleType == "BASE":
1058                    continue
1059                #
1060                # Add module referenced source files
1061                #
1062                self._SourceList.append(str(Module))
1063                IncludeList = {}
1064                for Source in Module.SourceFileList:
1065                    if os.path.splitext(str(Source))[1].lower() == ".c":
1066                        self._SourceList.append("  " + str(Source))
1067                        FindIncludeFiles(Source.Path, Module.IncludePathList, IncludeList)
1068                for IncludeFile in IncludeList.values():
1069                    self._SourceList.append("  " + IncludeFile)
1070
1071                for Guid in Module.PpiList:
1072                    self._GuidMap[Guid] = GuidStructureStringToGuidString(Module.PpiList[Guid])
1073                for Guid in Module.ProtocolList:
1074                    self._GuidMap[Guid] = GuidStructureStringToGuidString(Module.ProtocolList[Guid])
1075                for Guid in Module.GuidList:
1076                    self._GuidMap[Guid] = GuidStructureStringToGuidString(Module.GuidList[Guid])
1077
1078                if Module.Guid and not Module.IsLibrary:
1079                    EntryPoint = " ".join(Module.Module.ModuleEntryPointList)
1080                    if int(str(Module.AutoGenVersion), 0) >= 0x00010005:
1081                        RealEntryPoint = "_ModuleEntryPoint"
1082                    else:
1083                        RealEntryPoint = EntryPoint
1084                        if EntryPoint == "_ModuleEntryPoint":
1085                            CCFlags = Module.BuildOption.get("CC", {}).get("FLAGS", "")
1086                            Match = gGlueLibEntryPoint.search(CCFlags)
1087                            if Match:
1088                                EntryPoint = Match.group(1)
1089
1090                    self._FfsEntryPoint[Module.Guid.upper()] = (EntryPoint, RealEntryPoint)
1091
1092
1093        #
1094        # Collect platform firmware volume list as the input of EOT.
1095        #
1096        self._FvList = []
1097        if Wa.FdfProfile:
1098            for Fd in Wa.FdfProfile.FdDict:
1099                for FdRegion in Wa.FdfProfile.FdDict[Fd].RegionList:
1100                    if FdRegion.RegionType != "FV":
1101                        continue
1102                    for FvName in FdRegion.RegionDataList:
1103                        if FvName in self._FvList:
1104                            continue
1105                        self._FvList.append(FvName)
1106                        for Ffs in Wa.FdfProfile.FvDict[FvName.upper()].FfsList:
1107                            for Section in Ffs.SectionList:
1108                                try:
1109                                    for FvSection in Section.SectionList:
1110                                        if FvSection.FvName in self._FvList:
1111                                            continue
1112                                        self._FvList.append(FvSection.FvName)
1113                                except AttributeError:
1114                                    pass
1115
1116
1117    ##
1118    # Parse platform fixed address map files
1119    #
1120    # This function parses the platform final fixed address map file to get
1121    # the database of predicted fixed address for module image base, entry point
1122    # etc.
1123    #
1124    # @param self:           The object pointer
1125    #
1126    def _ParseMapFile(self):
1127        if self._MapFileParsed:
1128            return
1129        self._MapFileParsed = True
1130        if os.path.isfile(self._MapFileName):
1131            try:
1132                FileContents = open(self._MapFileName).read()
1133                for Match in gMapFileItemPattern.finditer(FileContents):
1134                    AddressType = Match.group(1)
1135                    BaseAddress = Match.group(2)
1136                    EntryPoint = Match.group(3)
1137                    Guid = Match.group(4).upper()
1138                    List = self.FixedMapDict.setdefault(Guid, [])
1139                    List.append((AddressType, BaseAddress, "*I"))
1140                    List.append((AddressType, EntryPoint, "*E"))
1141            except:
1142                EdkLogger.warn(None, "Cannot open file to read", self._MapFileName)
1143
1144    ##
1145    # Invokes EOT tool to get the predicted the execution order.
1146    #
1147    # This function invokes EOT tool to calculate the predicted dispatch order
1148    #
1149    # @param self:           The object pointer
1150    #
1151    def _InvokeEotTool(self):
1152        if self._EotToolInvoked:
1153            return
1154
1155        self._EotToolInvoked = True
1156        FvFileList = []
1157        for FvName in self._FvList:
1158            FvFile = os.path.join(self._FvDir, FvName + ".Fv")
1159            if os.path.isfile(FvFile):
1160                FvFileList.append(FvFile)
1161
1162        if len(FvFileList) == 0:
1163            return
1164        #
1165        # Write source file list and GUID file list to an intermediate file
1166        # as the input for EOT tool and dispatch List as the output file
1167        # from EOT tool.
1168        #
1169        SourceList = os.path.join(self._EotDir, "SourceFile.txt")
1170        GuidList = os.path.join(self._EotDir, "GuidList.txt")
1171        DispatchList = os.path.join(self._EotDir, "Dispatch.txt")
1172
1173        TempFile = open(SourceList, "w+")
1174        for Item in self._SourceList:
1175            FileWrite(TempFile, Item)
1176        TempFile.close()
1177        TempFile = open(GuidList, "w+")
1178        for Key in self._GuidMap:
1179            FileWrite(TempFile, "%s %s" % (Key, self._GuidMap[Key]))
1180        TempFile.close()
1181
1182        try:
1183            from Eot.Eot import Eot
1184
1185            #
1186            # Invoke EOT tool and echo its runtime performance
1187            #
1188            EotStartTime = time.time()
1189            Eot(CommandLineOption=False, SourceFileList=SourceList, GuidList=GuidList,
1190                FvFileList=' '.join(FvFileList), Dispatch=DispatchList, IsInit=True)
1191            EotEndTime = time.time()
1192            EotDuration = time.strftime("%H:%M:%S", time.gmtime(int(round(EotEndTime - EotStartTime))))
1193            EdkLogger.quiet("EOT run time: %s\n" % EotDuration)
1194
1195            #
1196            # Parse the output of EOT tool
1197            #
1198            for Line in open(DispatchList):
1199                if len(Line.split()) < 4:
1200                    continue
1201                (Guid, Phase, FfsName, FilePath) = Line.split()
1202                Symbol = self._FfsEntryPoint.get(Guid, [FfsName, ""])[0]
1203                if len(Symbol) > self.MaxLen:
1204                    self.MaxLen = len(Symbol)
1205                self.ItemList.append((Phase, Symbol, FilePath))
1206        except:
1207            EdkLogger.quiet("(Python %s on %s\n%s)" % (platform.python_version(), sys.platform, traceback.format_exc()))
1208            EdkLogger.warn(None, "Failed to generate execution order prediction report, for some error occurred in executing EOT.")
1209
1210
1211    ##
1212    # Generate platform execution order report
1213    #
1214    # This function generates the predicted module execution order.
1215    #
1216    # @param self            The object pointer
1217    # @param File            The file object for report
1218    #
1219    def _GenerateExecutionOrderReport(self, File):
1220        self._InvokeEotTool()
1221        if len(self.ItemList) == 0:
1222            return
1223        FileWrite(File, gSectionStart)
1224        FileWrite(File, "Execution Order Prediction")
1225        FileWrite(File, "*P PEI phase")
1226        FileWrite(File, "*D DXE phase")
1227        FileWrite(File, "*E Module INF entry point name")
1228        FileWrite(File, "*N Module notification function name")
1229
1230        FileWrite(File, "Type %-*s %s" % (self.MaxLen, "Symbol", "Module INF Path"))
1231        FileWrite(File, gSectionSep)
1232        for Item in self.ItemList:
1233            FileWrite(File, "*%sE  %-*s %s" % (Item[0], self.MaxLen, Item[1], Item[2]))
1234
1235        FileWrite(File, gSectionStart)
1236
1237    ##
1238    # Generate Fixed Address report.
1239    #
1240    # This function generate the predicted fixed address report for a module
1241    # specified by Guid.
1242    #
1243    # @param self            The object pointer
1244    # @param File            The file object for report
1245    # @param Guid            The module Guid value.
1246    # @param NotifyList      The list of all notify function in a module
1247    #
1248    def _GenerateFixedAddressReport(self, File, Guid, NotifyList):
1249        self._ParseMapFile()
1250        FixedAddressList = self.FixedMapDict.get(Guid)
1251        if not FixedAddressList:
1252            return
1253
1254        FileWrite(File, gSubSectionStart)
1255        FileWrite(File, "Fixed Address Prediction")
1256        FileWrite(File, "*I  Image Loading Address")
1257        FileWrite(File, "*E  Entry Point Address")
1258        FileWrite(File, "*N  Notification Function Address")
1259        FileWrite(File, "*F  Flash Address")
1260        FileWrite(File, "*M  Memory Address")
1261        FileWrite(File, "*S  SMM RAM Offset")
1262        FileWrite(File, "TOM Top of Memory")
1263
1264        FileWrite(File, "Type Address           Name")
1265        FileWrite(File, gSubSectionSep)
1266        for Item in FixedAddressList:
1267            Type = Item[0]
1268            Value = Item[1]
1269            Symbol = Item[2]
1270            if Symbol == "*I":
1271                Name = "(Image Base)"
1272            elif Symbol == "*E":
1273                Name = self._FfsEntryPoint.get(Guid, ["", "_ModuleEntryPoint"])[1]
1274            elif Symbol in NotifyList:
1275                Name = Symbol
1276                Symbol = "*N"
1277            else:
1278                continue
1279
1280            if "Flash" in Type:
1281                Symbol += "F"
1282            elif "Memory" in Type:
1283                Symbol += "M"
1284            else:
1285                Symbol += "S"
1286
1287            if Value[0] == "-":
1288                Value = "TOM" + Value
1289
1290            FileWrite(File, "%s  %-16s  %s" % (Symbol, Value, Name))
1291
1292    ##
1293    # Generate report for the prediction part
1294    #
1295    # This function generate the predicted fixed address report for a module or
1296    # predicted module execution order for a platform.
1297    # If the input Guid is None, then, it generates the predicted module execution order;
1298    # otherwise it generated the module fixed loading address for the module specified by
1299    # Guid.
1300    #
1301    # @param self            The object pointer
1302    # @param File            The file object for report
1303    # @param Guid            The module Guid value.
1304    #
1305    def GenerateReport(self, File, Guid):
1306        if Guid:
1307            self._GenerateFixedAddressReport(File, Guid.upper(), [])
1308        else:
1309            self._GenerateExecutionOrderReport(File)
1310
1311##
1312# Reports FD region information
1313#
1314# This class reports the FD subsection in the build report file.
1315# It collects region information of platform flash device.
1316# If the region is a firmware volume, it lists the set of modules
1317# and its space information; otherwise, it only lists its region name,
1318# base address and size in its sub-section header.
1319# If there are nesting FVs, the nested FVs will list immediate after
1320# this FD region subsection
1321#
1322class FdRegionReport(object):
1323    ##
1324    # Discover all the nested FV name list.
1325    #
1326    # This is an internal worker function to discover the all the nested FV information
1327    # in the parent firmware volume. It uses deep first search algorithm recursively to
1328    # find all the FV list name and append them to the list.
1329    #
1330    # @param self            The object pointer
1331    # @param FvName          The name of current firmware file system
1332    # @param Wa              Workspace context information
1333    #
1334    def _DiscoverNestedFvList(self, FvName, Wa):
1335        FvDictKey=FvName.upper()
1336        if FvDictKey in Wa.FdfProfile.FvDict:
1337            for Ffs in Wa.FdfProfile.FvDict[FvName.upper()].FfsList:
1338                for Section in Ffs.SectionList:
1339                    try:
1340                        for FvSection in Section.SectionList:
1341                            if FvSection.FvName in self.FvList:
1342                                continue
1343                            self._GuidsDb[Ffs.NameGuid.upper()] = FvSection.FvName
1344                            self.FvList.append(FvSection.FvName)
1345                            self.FvInfo[FvSection.FvName] = ("Nested FV", 0, 0)
1346                            self._DiscoverNestedFvList(FvSection.FvName, Wa)
1347                    except AttributeError:
1348                        pass
1349
1350    ##
1351    # Constructor function for class FdRegionReport
1352    #
1353    # This constructor function generates FdRegionReport object for a specified FdRegion.
1354    # If the FdRegion is a firmware volume, it will recursively find all its nested Firmware
1355    # volume list. This function also collects GUID map in order to dump module identification
1356    # in the final report.
1357    #
1358    # @param self:           The object pointer
1359    # @param FdRegion        The current FdRegion object
1360    # @param Wa              Workspace context information
1361    #
1362    def __init__(self, FdRegion, Wa):
1363        self.Type = FdRegion.RegionType
1364        self.BaseAddress = FdRegion.Offset
1365        self.Size = FdRegion.Size
1366        self.FvList = []
1367        self.FvInfo = {}
1368        self._GuidsDb = {}
1369        self._FvDir = Wa.FvDir
1370
1371        #
1372        # If the input FdRegion is not a firmware volume,
1373        # we are done.
1374        #
1375        if self.Type != "FV":
1376            return
1377
1378        #
1379        # Find all nested FVs in the FdRegion
1380        #
1381        for FvName in FdRegion.RegionDataList:
1382            if FvName in self.FvList:
1383                continue
1384            self.FvList.append(FvName)
1385            self.FvInfo[FvName] = ("Fd Region", self.BaseAddress, self.Size)
1386            self._DiscoverNestedFvList(FvName, Wa)
1387
1388        PlatformPcds = {}
1389        #
1390        # Collect PCDs declared in DEC files.
1391        #
1392        for Pa in Wa.AutoGenObjectList:
1393            for Package in Pa.PackageList:
1394                for (TokenCName, TokenSpaceGuidCName, DecType) in Package.Pcds:
1395                    DecDefaultValue = Package.Pcds[TokenCName, TokenSpaceGuidCName, DecType].DefaultValue
1396                    PlatformPcds[(TokenCName, TokenSpaceGuidCName)] = DecDefaultValue
1397        #
1398        # Collect PCDs defined in DSC file
1399        #
1400        for arch in Wa.ArchList:
1401            Platform = Wa.BuildDatabase[Wa.MetaFile, arch]
1402            for (TokenCName, TokenSpaceGuidCName) in Platform.Pcds:
1403                DscDefaultValue = Platform.Pcds[(TokenCName, TokenSpaceGuidCName)].DefaultValue
1404                PlatformPcds[(TokenCName, TokenSpaceGuidCName)] = DscDefaultValue
1405
1406        #
1407        # Add PEI and DXE a priori files GUIDs defined in PI specification.
1408        #
1409        self._GuidsDb["1B45CC0A-156A-428A-AF62-49864DA0E6E6"] = "PEI Apriori"
1410        self._GuidsDb["FC510EE7-FFDC-11D4-BD41-0080C73C8881"] = "DXE Apriori"
1411        #
1412        # Add ACPI table storage file
1413        #
1414        self._GuidsDb["7E374E25-8E01-4FEE-87F2-390C23C606CD"] = "ACPI table storage"
1415
1416        for Pa in Wa.AutoGenObjectList:
1417            for ModuleKey in Pa.Platform.Modules:
1418                M = Pa.Platform.Modules[ModuleKey].M
1419                InfPath = mws.join(Wa.WorkspaceDir, M.MetaFile.File)
1420                self._GuidsDb[M.Guid.upper()] = "%s (%s)" % (M.Module.BaseName, InfPath)
1421
1422        #
1423        # Collect the GUID map in the FV firmware volume
1424        #
1425        for FvName in self.FvList:
1426            FvDictKey=FvName.upper()
1427            if FvDictKey in Wa.FdfProfile.FvDict:
1428                for Ffs in Wa.FdfProfile.FvDict[FvName.upper()].FfsList:
1429                    try:
1430                        #
1431                        # collect GUID map for binary EFI file in FDF file.
1432                        #
1433                        Guid = Ffs.NameGuid.upper()
1434                        Match = gPcdGuidPattern.match(Ffs.NameGuid)
1435                        if Match:
1436                            PcdTokenspace = Match.group(1)
1437                            PcdToken = Match.group(2)
1438                            if (PcdToken, PcdTokenspace) in PlatformPcds:
1439                                GuidValue = PlatformPcds[(PcdToken, PcdTokenspace)]
1440                                Guid = GuidStructureByteArrayToGuidString(GuidValue).upper()
1441                        for Section in Ffs.SectionList:
1442                            try:
1443                                ModuleSectFile = mws.join(Wa.WorkspaceDir, Section.SectFileName)
1444                                self._GuidsDb[Guid] = ModuleSectFile
1445                            except AttributeError:
1446                                pass
1447                    except AttributeError:
1448                        pass
1449
1450
1451    ##
1452    # Internal worker function to generate report for the FD region
1453    #
1454    # This internal worker function to generate report for the FD region.
1455    # It the type is firmware volume, it lists offset and module identification.
1456    #
1457    # @param self            The object pointer
1458    # @param File            The file object for report
1459    # @param Title           The title for the FD subsection
1460    # @param BaseAddress     The base address for the FD region
1461    # @param Size            The size of the FD region
1462    # @param FvName          The FV name if the FD region is a firmware volume
1463    #
1464    def _GenerateReport(self, File, Title, Type, BaseAddress, Size=0, FvName=None):
1465        FileWrite(File, gSubSectionStart)
1466        FileWrite(File, Title)
1467        FileWrite(File, "Type:               %s" % Type)
1468        FileWrite(File, "Base Address:       0x%X" % BaseAddress)
1469
1470        if self.Type == "FV":
1471            FvTotalSize = 0
1472            FvTakenSize = 0
1473            FvFreeSize  = 0
1474            FvReportFileName = os.path.join(self._FvDir, FvName + ".Fv.txt")
1475            try:
1476                #
1477                # Collect size info in the firmware volume.
1478                #
1479                FvReport = open(FvReportFileName).read()
1480                Match = gFvTotalSizePattern.search(FvReport)
1481                if Match:
1482                    FvTotalSize = int(Match.group(1), 16)
1483                Match = gFvTakenSizePattern.search(FvReport)
1484                if Match:
1485                    FvTakenSize = int(Match.group(1), 16)
1486                FvFreeSize = FvTotalSize - FvTakenSize
1487                #
1488                # Write size information to the report file.
1489                #
1490                FileWrite(File, "Size:               0x%X (%.0fK)" % (FvTotalSize, FvTotalSize / 1024.0))
1491                FileWrite(File, "Fv Name:            %s (%.1f%% Full)" % (FvName, FvTakenSize * 100.0 / FvTotalSize))
1492                FileWrite(File, "Occupied Size:      0x%X (%.0fK)" % (FvTakenSize, FvTakenSize / 1024.0))
1493                FileWrite(File, "Free Size:          0x%X (%.0fK)" % (FvFreeSize, FvFreeSize / 1024.0))
1494                FileWrite(File, "Offset     Module")
1495                FileWrite(File, gSubSectionSep)
1496                #
1497                # Write module offset and module identification to the report file.
1498                #
1499                OffsetInfo = {}
1500                for Match in gOffsetGuidPattern.finditer(FvReport):
1501                    Guid = Match.group(2).upper()
1502                    OffsetInfo[Match.group(1)] = self._GuidsDb.get(Guid, Guid)
1503                OffsetList = OffsetInfo.keys()
1504                OffsetList.sort()
1505                for Offset in OffsetList:
1506                    FileWrite (File, "%s %s" % (Offset, OffsetInfo[Offset]))
1507            except IOError:
1508                EdkLogger.warn(None, "Fail to read report file", FvReportFileName)
1509        else:
1510            FileWrite(File, "Size:               0x%X (%.0fK)" % (Size, Size / 1024.0))
1511        FileWrite(File, gSubSectionEnd)
1512
1513    ##
1514    # Generate report for the FD region
1515    #
1516    # This function generates report for the FD region.
1517    #
1518    # @param self            The object pointer
1519    # @param File            The file object for report
1520    #
1521    def GenerateReport(self, File):
1522        if (len(self.FvList) > 0):
1523            for FvItem in self.FvList:
1524                Info = self.FvInfo[FvItem]
1525                self._GenerateReport(File, Info[0], "FV", Info[1], Info[2], FvItem)
1526        else:
1527            self._GenerateReport(File, "FD Region", self.Type, self.BaseAddress, self.Size)
1528
1529##
1530# Reports FD information
1531#
1532# This class reports the FD section in the build report file.
1533# It collects flash device information for a platform.
1534#
1535class FdReport(object):
1536    ##
1537    # Constructor function for class FdReport
1538    #
1539    # This constructor function generates FdReport object for a specified
1540    # firmware device.
1541    #
1542    # @param self            The object pointer
1543    # @param Fd              The current Firmware device object
1544    # @param Wa              Workspace context information
1545    #
1546    def __init__(self, Fd, Wa):
1547        self.FdName = Fd.FdUiName
1548        self.BaseAddress = Fd.BaseAddress
1549        self.Size = Fd.Size
1550        self.FdRegionList = [FdRegionReport(FdRegion, Wa) for FdRegion in Fd.RegionList]
1551        self.FvPath = os.path.join(Wa.BuildDir, "FV")
1552        self.VpdFilePath = os.path.join(self.FvPath, "%s.map" % Wa.Platform.VpdToolGuid)
1553        self.VPDBaseAddress = 0
1554        self.VPDSize = 0
1555        self.VPDInfoList = []
1556        for index, FdRegion in enumerate(Fd.RegionList):
1557            if str(FdRegion.RegionType) is 'FILE' and Wa.Platform.VpdToolGuid in str(FdRegion.RegionDataList):
1558                self.VPDBaseAddress = self.FdRegionList[index].BaseAddress
1559                self.VPDSize = self.FdRegionList[index].Size
1560                break
1561
1562        if os.path.isfile(self.VpdFilePath):
1563            fd = open(self.VpdFilePath, "r")
1564            Lines = fd.readlines()
1565            for Line in Lines:
1566                Line = Line.strip()
1567                if len(Line) == 0 or Line.startswith("#"):
1568                    continue
1569                try:
1570                    PcdName, SkuId, Offset, Size, Value = Line.split("#")[0].split("|")
1571                    PcdName, SkuId, Offset, Size, Value = PcdName.strip(), SkuId.strip(), Offset.strip(), Size.strip(), Value.strip()
1572                    if Offset.lower().startswith('0x'):
1573                        Offset = '0x%08X' % (int(Offset, 16) + self.VPDBaseAddress)
1574                    else:
1575                        Offset = '0x%08X' % (int(Offset, 10) + self.VPDBaseAddress)
1576                    self.VPDInfoList.append("%s | %s | %s | %s | %s" % (PcdName, SkuId, Offset, Size, Value))
1577                except:
1578                    EdkLogger.error("BuildReport", CODE_ERROR, "Fail to parse VPD information file %s" % self.VpdFilePath)
1579            fd.close()
1580
1581    ##
1582    # Generate report for the firmware device.
1583    #
1584    # This function generates report for the firmware device.
1585    #
1586    # @param self            The object pointer
1587    # @param File            The file object for report
1588    #
1589    def GenerateReport(self, File):
1590        FileWrite(File, gSectionStart)
1591        FileWrite(File, "Firmware Device (FD)")
1592        FileWrite(File, "FD Name:            %s" % self.FdName)
1593        FileWrite(File, "Base Address:       %s" % self.BaseAddress)
1594        FileWrite(File, "Size:               0x%X (%.0fK)" % (self.Size, self.Size / 1024.0))
1595        if len(self.FdRegionList) > 0:
1596            FileWrite(File, gSectionSep)
1597            for FdRegionItem in self.FdRegionList:
1598                FdRegionItem.GenerateReport(File)
1599
1600        if len(self.VPDInfoList) > 0:
1601            FileWrite(File, gSubSectionStart)
1602            FileWrite(File, "FD VPD Region")
1603            FileWrite(File, "Base Address:       0x%X" % self.VPDBaseAddress)
1604            FileWrite(File, "Size:               0x%X (%.0fK)" % (self.VPDSize, self.VPDSize / 1024.0))
1605            FileWrite(File, gSubSectionSep)
1606            for item in self.VPDInfoList:
1607                FileWrite(File, item)
1608            FileWrite(File, gSubSectionEnd)
1609        FileWrite(File, gSectionEnd)
1610
1611
1612
1613##
1614# Reports platform information
1615#
1616# This class reports the whole platform information
1617#
1618class PlatformReport(object):
1619    ##
1620    # Constructor function for class PlatformReport
1621    #
1622    # This constructor function generates PlatformReport object a platform build.
1623    # It generates report for platform summary, flash, global PCDs and detailed
1624    # module information for modules involved in platform build.
1625    #
1626    # @param self            The object pointer
1627    # @param Wa              Workspace context information
1628    # @param MaList          The list of modules in the platform build
1629    #
1630    def __init__(self, Wa, MaList, ReportType):
1631        self._WorkspaceDir = Wa.WorkspaceDir
1632        self.PlatformName = Wa.Name
1633        self.PlatformDscPath = Wa.Platform
1634        self.Architectures = " ".join(Wa.ArchList)
1635        self.ToolChain = Wa.ToolChain
1636        self.Target = Wa.BuildTarget
1637        self.OutputPath = os.path.join(Wa.WorkspaceDir, Wa.OutputDir)
1638        self.BuildEnvironment = platform.platform()
1639
1640        self.PcdReport = None
1641        if "PCD" in ReportType:
1642            self.PcdReport = PcdReport(Wa)
1643
1644        self.FdReportList = []
1645        if "FLASH" in ReportType and Wa.FdfProfile and MaList == None:
1646            for Fd in Wa.FdfProfile.FdDict:
1647                self.FdReportList.append(FdReport(Wa.FdfProfile.FdDict[Fd], Wa))
1648
1649        self.PredictionReport = None
1650        if "FIXED_ADDRESS" in ReportType or "EXECUTION_ORDER" in ReportType:
1651            self.PredictionReport = PredictionReport(Wa)
1652
1653        self.DepexParser = None
1654        if "DEPEX" in ReportType:
1655            self.DepexParser = DepexParser(Wa)
1656
1657        self.ModuleReportList = []
1658        if MaList != None:
1659            self._IsModuleBuild = True
1660            for Ma in MaList:
1661                self.ModuleReportList.append(ModuleReport(Ma, ReportType))
1662        else:
1663            self._IsModuleBuild = False
1664            for Pa in Wa.AutoGenObjectList:
1665                ModuleAutoGenList = []
1666                for ModuleKey in Pa.Platform.Modules:
1667                    ModuleAutoGenList.append(Pa.Platform.Modules[ModuleKey].M)
1668                if GlobalData.gFdfParser != None:
1669                    if Pa.Arch in GlobalData.gFdfParser.Profile.InfDict:
1670                        INFList = GlobalData.gFdfParser.Profile.InfDict[Pa.Arch]
1671                        for InfName in INFList:
1672                            InfClass = PathClass(NormPath(InfName), Wa.WorkspaceDir, Pa.Arch)
1673                            Ma = ModuleAutoGen(Wa, InfClass, Pa.BuildTarget, Pa.ToolChain, Pa.Arch, Wa.MetaFile)
1674                            if Ma == None:
1675                                continue
1676                            if Ma not in ModuleAutoGenList:
1677                                ModuleAutoGenList.append(Ma)
1678                for MGen in ModuleAutoGenList:
1679                    self.ModuleReportList.append(ModuleReport(MGen, ReportType))
1680
1681
1682
1683    ##
1684    # Generate report for the whole platform.
1685    #
1686    # This function generates report for platform information.
1687    # It comprises of platform summary, global PCD, flash and
1688    # module list sections.
1689    #
1690    # @param self            The object pointer
1691    # @param File            The file object for report
1692    # @param BuildDuration   The total time to build the modules
1693    # @param ReportType      The kind of report items in the final report file
1694    #
1695    def GenerateReport(self, File, BuildDuration, ReportType):
1696        FileWrite(File, "Platform Summary")
1697        FileWrite(File, "Platform Name:        %s" % self.PlatformName)
1698        FileWrite(File, "Platform DSC Path:    %s" % self.PlatformDscPath)
1699        FileWrite(File, "Architectures:        %s" % self.Architectures)
1700        FileWrite(File, "Tool Chain:           %s" % self.ToolChain)
1701        FileWrite(File, "Target:               %s" % self.Target)
1702        FileWrite(File, "Output Path:          %s" % self.OutputPath)
1703        FileWrite(File, "Build Environment:    %s" % self.BuildEnvironment)
1704        FileWrite(File, "Build Duration:       %s" % BuildDuration)
1705        FileWrite(File, "Report Content:       %s" % ", ".join(ReportType))
1706
1707        if GlobalData.MixedPcd:
1708            FileWrite(File, gSectionStart)
1709            FileWrite(File, "The following PCDs use different access methods:")
1710            FileWrite(File, gSectionSep)
1711            for PcdItem in GlobalData.MixedPcd:
1712                FileWrite(File, "%s.%s" % (str(PcdItem[1]), str(PcdItem[0])))
1713            FileWrite(File, gSectionEnd)
1714
1715        if not self._IsModuleBuild:
1716            if "PCD" in ReportType:
1717                self.PcdReport.GenerateReport(File, None)
1718
1719            if "FLASH" in ReportType:
1720                for FdReportListItem in self.FdReportList:
1721                    FdReportListItem.GenerateReport(File)
1722
1723        for ModuleReportItem in self.ModuleReportList:
1724            ModuleReportItem.GenerateReport(File, self.PcdReport, self.PredictionReport, self.DepexParser, ReportType)
1725
1726        if not self._IsModuleBuild:
1727            if "EXECUTION_ORDER" in ReportType:
1728                self.PredictionReport.GenerateReport(File, None)
1729
1730## BuildReport class
1731#
1732#  This base class contain the routines to collect data and then
1733#  applies certain format to the output report
1734#
1735class BuildReport(object):
1736    ##
1737    # Constructor function for class BuildReport
1738    #
1739    # This constructor function generates BuildReport object a platform build.
1740    # It generates report for platform summary, flash, global PCDs and detailed
1741    # module information for modules involved in platform build.
1742    #
1743    # @param self            The object pointer
1744    # @param ReportFile      The file name to save report file
1745    # @param ReportType      The kind of report items in the final report file
1746    #
1747    def __init__(self, ReportFile, ReportType):
1748        self.ReportFile = ReportFile
1749        if ReportFile:
1750            self.ReportList = []
1751            self.ReportType = []
1752            if ReportType:
1753                for ReportTypeItem in ReportType:
1754                    if ReportTypeItem not in self.ReportType:
1755                        self.ReportType.append(ReportTypeItem)
1756            else:
1757                self.ReportType = ["PCD", "LIBRARY", "BUILD_FLAGS", "DEPEX", "HASH", "FLASH", "FIXED_ADDRESS"]
1758    ##
1759    # Adds platform report to the list
1760    #
1761    # This function adds a platform report to the final report list.
1762    #
1763    # @param self            The object pointer
1764    # @param Wa              Workspace context information
1765    # @param MaList          The list of modules in the platform build
1766    #
1767    def AddPlatformReport(self, Wa, MaList=None):
1768        if self.ReportFile:
1769            self.ReportList.append((Wa, MaList))
1770
1771    ##
1772    # Generates the final report.
1773    #
1774    # This function generates platform build report. It invokes GenerateReport()
1775    # method for every platform report in the list.
1776    #
1777    # @param self            The object pointer
1778    # @param BuildDuration   The total time to build the modules
1779    #
1780    def GenerateReport(self, BuildDuration):
1781        if self.ReportFile:
1782            try:
1783                File = StringIO('')
1784                for (Wa, MaList) in self.ReportList:
1785                    PlatformReport(Wa, MaList, self.ReportType).GenerateReport(File, BuildDuration, self.ReportType)
1786                Content = FileLinesSplit(File.getvalue(), gLineMaxLength)
1787                SaveFileOnChange(self.ReportFile, Content, True)
1788                EdkLogger.quiet("Build report can be found at %s" % os.path.abspath(self.ReportFile))
1789            except IOError:
1790                EdkLogger.error(None, FILE_WRITE_FAILURE, ExtraData=self.ReportFile)
1791            except:
1792                EdkLogger.error("BuildReport", CODE_ERROR, "Unknown fatal error when generating build report", ExtraData=self.ReportFile, RaiseError=False)
1793                EdkLogger.quiet("(Python %s on %s\n%s)" % (platform.python_version(), sys.platform, traceback.format_exc()))
1794            File.close()
1795
1796# This acts like the main() function for the script, unless it is 'import'ed into another script.
1797if __name__ == '__main__':
1798    pass
1799
1800