Fix some wording in report
[efi/basetools/.git] / Source / Python / build / BuildReport.py
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, Intel Corporation
8 # All rights reserved. This program and the accompanying materials
9 # are licensed and made available under the terms and conditions of the BSD License
10 # which accompanies this distribution.  The full text of the license may be found at
11 # http://opensource.org/licenses/bsd-license.php
12 #
13 # THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
14 # WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
15 #
16
17 ## Import Modules
18 #
19 import os
20 import re
21 import platform
22 import textwrap
23 import traceback
24 import sys
25 from datetime import datetime
26 from Common import EdkLogger
27 from Common.Misc import GuidStructureByteArrayToGuidString
28 from Common.Misc import GuidStructureStringToGuidString
29 from Common.InfClassObject import gComponentType2ModuleType
30 from Common.BuildToolError import FILE_OPEN_FAILURE
31 from Common.BuildToolError import FILE_WRITE_FAILURE
32 from Common.BuildToolError import CODE_ERROR
33
34
35 ## Pattern to extract contents in EDK DXS files
36 gDxsDependencyPattern = re.compile(r"DEPENDENCY_START(.+)DEPENDENCY_END", re.DOTALL)
37
38 ## Pattern to find total FV total size, occupied size in flash report intermediate file
39 gFvTotalSizePattern = re.compile(r"EFI_FV_TOTAL_SIZE = (0x[0-9a-fA-F]+)")
40 gFvTakenSizePattern = re.compile(r"EFI_FV_TAKEN_SIZE = (0x[0-9a-fA-F]+)")
41
42 ## Pattern to find module size and time stamp in module summary report intermediate file
43 gModuleSizePattern = re.compile(r"MODULE_SIZE = (\d+)")
44 gTimeStampPattern  = re.compile(r"TIME_STAMP = (\d+)")
45
46 ## Pattern to find GUID value in flash description files
47 gPcdGuidPattern = re.compile(r"PCD\((\w+)[.](\w+)\)")
48
49 ## Pattern to collect offset, GUID value pair in the flash report intermediate file
50 gOffsetGuidPattern = re.compile(r"(0x[0-9A-Fa-f]+) ([-A-Fa-f0-9]+)")
51
52 ## Pattern to find module base address and entry point in fixed flash map file
53 gModulePattern = r"\n[-\w]+\s*\(([^,]+),\s*BaseAddress=%(Address)s,\s*EntryPoint=%(Address)s\)\s*\(GUID=([-0-9A-Fa-f]+)[^)]*\)"
54 gMapFileItemPattern = re.compile(gModulePattern % {"Address" : "(-?0[xX][0-9A-Fa-f]+)"})
55
56 ## Pattern to find all module referenced header files in source files
57 gIncludePattern  = re.compile(r'#include\s*["<]([^">]+)[">]')
58 gIncludePattern2 = re.compile(r"#include\s+EFI_([A-Z_]+)\s*[(]\s*(\w+)\s*[)]")
59
60 ## Pattern to find the entry point for EDK module using EDKII Glue library
61 gGlueLibEntryPoint = re.compile(r"__EDKII_GLUE_MODULE_ENTRY_POINT__\s*=\s*(\w+)")
62
63 ## Tags for section start, end and separator
64 gSectionStart = ">" + "=" * 118 + "<"
65 gSectionEnd = "<" + "=" * 118 + ">" + "\n"
66 gSectionSep = "=" * 120
67
68 ## Tags for subsection start, end and separator
69 gSubSectionStart = ">" + "-" * 118 + "<"
70 gSubSectionEnd = "<" + "-" * 118 + ">"
71 gSubSectionSep = "-" * 120
72
73 ## The look up table to map PCD type to pair of report display type and DEC type
74 gPcdTypeMap = {
75   'FixedAtBuild'     : ('FIXED',  'FixedAtBuild'),
76   'PatchableInModule': ('PATCH',  'PatchableInModule'),
77   'FeatureFlag'      : ('FLAG',   'FeatureFlag'),
78   'Dynamic'          : ('DYN',    'Dynamic'),
79   'DynamicHii'       : ('DYNHII', 'Dynamic'),
80   'DynamicVpd'       : ('DYNVPD', 'Dynamic'),
81   'DynamicEx'        : ('DEX',    'Dynamic'),
82   'DynamicExHii'     : ('DEXHII', 'Dynamic'),
83   'DynamicExVpd'     : ('DEXVPD', 'Dynamic'),
84   }
85
86 ## The look up table to map module type to driver type
87 gDriverTypeMap = {
88   'SEC'               : '0x3 (SECURITY_CORE)',
89   'PEI_CORE'          : '0x4 (PEI_CORE)',
90   'PEIM'              : '0x6 (PEIM)',
91   'DXE_CORE'          : '0x5 (DXE_CORE)',
92   'DXE_DRIVER'        : '0x7 (DRIVER)',
93   'DXE_SAL_DRIVER'    : '0x7 (DRIVER)',
94   'DXE_SMM_DRIVER'    : '0x7 (DRIVER)',
95   'DXE_RUNTIME_DRIVER': '0x7 (DRIVER)',
96   'UEFI_DRIVER'       : '0x7 (DRIVER)',
97   'UEFI_APPLICATION'  : '0x9 (APPLICATION)',
98   'SMM_CORE'          : '0xD (SMM_CORE)',
99   'SMM_DRIVER'        : '0xA (SMM)', # Extension of module type to support PI 1.1 SMM drivers
100   }
101
102 ##
103 # Writes a string to the file object.
104 #
105 # This function writes a string to the file object and a new line is appended
106 # afterwards. It may optionally wraps the string for better readability.
107 #
108 # @File                      The file object to write
109 # @String                    The string to be written to the file
110 # @Wrapper                   Indicates whether to wrap the string
111 #
112 def FileWrite(File, String, Wrapper=False):
113     if Wrapper:
114         String = textwrap.fill(String, 120)
115     File.write(String + "\r\n")
116
117 ##
118 # Find all the header file that the module source directly includes.
119 #
120 # This function scans source code to find all header files the module may
121 # include. This is not accurate but very effective to find all the header
122 # file the module might include with #include statement.
123 #
124 # @Source                    The source file name
125 # @IncludePathList           The list of include path to find the source file.
126 # @IncludeFiles              The dictionary of current found include files.
127 #
128 def FindIncludeFiles(Source, IncludePathList, IncludeFiles):
129     FileContents = open(Source).read()
130     #
131     # Find header files with pattern #include "XXX.h" or #include <XXX.h>
132     #
133     for Match in gIncludePattern.finditer(FileContents):
134         FileName = Match.group(1).strip()
135         for Dir in [os.path.dirname(Source)] + IncludePathList:
136             FullFileName = os.path.normpath(os.path.join(Dir, FileName))
137             if os.path.exists(FullFileName):
138                 IncludeFiles[FullFileName.lower().replace("\\", "/")] = FullFileName
139                 break
140
141     #
142     # Find header files with pattern like #include EFI_PPI_CONSUMER(XXX)
143     #
144     for Match in gIncludePattern2.finditer(FileContents):
145         Key = Match.group(2)
146         Type = Match.group(1)
147         if "ARCH_PROTOCOL" in Type:
148             FileName = "ArchProtocol/%(Key)s/%(Key)s.h" % {"Key" : Key}
149         elif "PROTOCOL" in Type:
150             FileName = "Protocol/%(Key)s/%(Key)s.h" % {"Key" : Key}
151         elif "PPI" in Type:
152             FileName = "Ppi/%(Key)s/%(Key)s.h" % {"Key" : Key}
153         elif "GUID" in Type:
154             FileName = "Guid/%(Key)s/%(Key)s.h" % {"Key" : Key}
155         else:
156             continue
157         for Dir in IncludePathList:
158             FullFileName = os.path.normpath(os.path.join(Dir, FileName))
159             if os.path.exists(FullFileName):
160                 IncludeFiles[FullFileName.lower().replace("\\", "/")] = FullFileName
161                 break
162
163 ##
164 # Reports library information
165 #
166 # This class reports the module library subsection in the build report file.
167 #
168 class LibraryReport(object):
169     ##
170     # Constructor function for class LibraryReport
171     #
172     # This constructor function generates LibraryReport object for
173     # a module.
174     #
175     # @param self            The object pointer
176     # @param M               Module context information
177     #
178     def __init__(self, M):
179         self.LibraryList = []
180         if int(str(M.AutoGenVersion), 0) >= 0x00010005:
181             self._EdkIIModule = True
182         else:
183             self._EdkIIModule = False
184
185         for Lib in M.DependentLibraryList:
186             LibInfPath = str(Lib)
187             LibClassList = Lib.LibraryClass[0].LibraryClass
188             LibConstructorList = Lib.ConstructorList
189             LibDesstructorList = Lib.DestructorList
190             LibDepexList = Lib.DepexExpression[M.Arch, M.ModuleType]
191             self.LibraryList.append((LibInfPath, LibClassList, LibConstructorList, LibDesstructorList, LibDepexList))
192
193     ##
194     # Generate report for module library information
195     #
196     # This function generates report for the module library.
197     # If the module is EDKII style one, the additional library class, library
198     # constructor/destructor and dependency expression may also be reported.
199     #
200     # @param self            The object pointer
201     # @param File            The file object for report
202     #
203     def GenerateReport(self, File):
204         FileWrite(File, gSubSectionStart)
205         FileWrite(File, "Library")
206         if len(self.LibraryList) > 0:
207             FileWrite(File, gSubSectionSep)
208             for LibraryItem in self.LibraryList:
209                 LibInfPath = LibraryItem[0]
210                 FileWrite(File, LibInfPath)
211
212                 #
213                 # Report library class, library constructor and destructor for
214                 # EDKII style module.
215                 #
216                 if self._EdkIIModule:
217                     LibClass = LibraryItem[1]
218                     EdkIILibInfo = ""
219                     LibConstructor = " ".join(LibraryItem[2])
220                     if LibConstructor:
221                         EdkIILibInfo += " C = " + LibConstructor
222                     LibDestructor = " ".join(LibraryItem[3])
223                     if LibDestructor:
224                         EdkIILibInfo += " D = " + LibDestructor
225                     LibDepex = " ".join(LibraryItem[4])
226                     if LibDepex:
227                         EdkIILibInfo += " Depex = " + LibDepex
228                     if EdkIILibInfo:
229                         FileWrite(File, "{%s: %s}" % (LibClass, EdkIILibInfo))
230                     else:
231                         FileWrite(File, "{%s}" % LibClass)
232
233         FileWrite(File, gSubSectionEnd)
234
235 ##
236 # Reports dependency expression information
237 #
238 # This class reports the module dependency expression subsection in the build report file.
239 #
240 class DepexReport(object):
241     ##
242     # Constructor function for class DepexReport
243     #
244     # This constructor function generates DepexReport object for
245     # a module. If the module source contains the DXS file (usually EDK
246     # style module), it uses the dependency in DXS file; otherwise,
247     # it uses the dependency expression from its own INF [Depex] section
248     # and then merges with the ones from its dependent library INF.
249     #
250     # @param self            The object pointer
251     # @param M               Module context information
252     #
253     def __init__(self, M):
254         self.Depex = ""
255         ModuleType = M.ModuleType
256         if not ModuleType:
257             ModuleType = gComponentType2ModuleType.get(M.ComponentType, "")
258
259         if ModuleType in ["SEC", "PEI_CORE", "DXE_CORE", "SMM_CORE", "UEFI_APPLICATION"]:
260             return
261       
262         for Source in M.SourceFileList:
263             if os.path.splitext(Source.Path)[1].lower() == ".dxs":
264                 Match = gDxsDependencyPattern.search(open(Source.Path).read())
265                 if Match:
266                     self.Depex = Match.group(1).strip()
267                     self.Source = "DXS"
268                     break
269         else:
270             self.Depex = M.DepexExpressionList.get(M.ModuleType, "")
271             self.ModuleDepex = " ".join(M.Module.DepexExpression[M.Arch, M.ModuleType])
272             if not self.ModuleDepex:
273                 self.ModuleDepex = "(None)"
274
275             LibDepexList = []
276             for Lib in M.DependentLibraryList:
277                 LibDepex = " ".join(Lib.DepexExpression[M.Arch, M.ModuleType]).strip()
278                 if LibDepex != "":
279                     LibDepexList.append("(" + LibDepex + ")")
280             self.LibraryDepex = " AND ".join(LibDepexList)
281             if not self.LibraryDepex:
282                 self.LibraryDepex = "(None)"
283             self.Source = "INF"
284
285     ##
286     # Generate report for module dependency expression information
287     #
288     # This function generates report for the module dependency expression.
289     #
290     # @param self            The object pointer
291     # @param File            The file object for report
292     #
293     def GenerateReport(self, File):
294         if not self.Depex:
295             return
296                 
297         FileWrite(File, gSubSectionStart)
298         FileWrite(File, "Dependency Expression (DEPEX) from %s" % self.Source)
299
300         if self.Source == "INF":
301             FileWrite(File, "%s" % self.Depex, True)
302             FileWrite(File, gSubSectionSep)
303             FileWrite(File, "From Module INF:  %s" % self.ModuleDepex, True)
304             FileWrite(File, "From Library INF: %s" % self.LibraryDepex, True)
305         else:
306             FileWrite(File, "%s" % self.Depex)
307         FileWrite(File, gSubSectionEnd)
308
309 ##
310 # Reports dependency expression information
311 #
312 # This class reports the module build flags subsection in the build report file.
313 #
314 class BuildFlagsReport(object):
315     ##
316     # Constructor function for class BuildFlagsReport
317     #
318     # This constructor function generates BuildFlagsReport object for
319     # a module. It reports the build tool chain tag and all relevant
320     # build flags to build the module.
321     #
322     # @param self            The object pointer
323     # @param M               Module context information
324     #
325     def __init__(self, M):
326         BuildOptions = {}
327         #
328         # Add build flags according to source file extension so that
329         # irrelevant ones can be filtered out.
330         #
331         for Source in M.SourceFileList:
332             Ext = os.path.splitext(Source.File)[1].lower()
333             if Ext in [".c", ".cc", ".cpp"]:
334                 BuildOptions["CC"] = 1
335             elif Ext in [".s", ".asm"]:
336                 BuildOptions["PP"] = 1
337                 BuildOptions["ASM"] = 1
338             elif Ext in [".vfr"]:
339                 BuildOptions["VFRPP"] = 1
340                 BuildOptions["VFR"] = 1
341             elif Ext in [".dxs"]:
342                 BuildOptions["APP"] = 1
343                 BuildOptions["CC"] = 1
344             elif Ext in [".asl"]:
345                 BuildOptions["ASLPP"] = 1
346                 BuildOptions["ASL"] = 1
347             elif Ext in [".aslc"]:
348                 BuildOptions["ASLCC"] = 1
349                 BuildOptions["ASLDLINK"] = 1
350                 BuildOptions["CC"] = 1
351             elif Ext in [".asm16"]:
352                 BuildOptions["ASMLINK"] = 1
353             BuildOptions["SLINK"] = 1
354             BuildOptions["DLINK"] = 1
355
356         #
357         # Save module build flags.
358         #
359         self.ToolChainTag = M.ToolChain
360         self.BuildFlags = {}
361         for Tool in BuildOptions:
362             self.BuildFlags[Tool + "_FLAGS"] = M.BuildOption.get(Tool, {}).get("FLAGS", "")
363
364     ##
365     # Generate report for module build flags information
366     #
367     # This function generates report for the module build flags expression.
368     #
369     # @param self            The object pointer
370     # @param File            The file object for report
371     #
372     def GenerateReport(self, File):
373         FileWrite(File, gSubSectionStart)
374         FileWrite(File, "Build Flags")
375         FileWrite(File, "Tool Chain Tag: %s" % self.ToolChainTag)
376         for Tool in self.BuildFlags:
377             FileWrite(File, gSubSectionSep)
378             FileWrite(File, "%s = %s" % (Tool, self.BuildFlags[Tool]), True)
379
380         FileWrite(File, gSubSectionEnd)
381
382
383 ##
384 # Reports individual module information
385 #
386 # This class reports the module section in the build report file.
387 # It comprises of module summary, module PCD, library, dependency expression,
388 # build flags sections.
389 #
390 class ModuleReport(object):
391     ##
392     # Constructor function for class ModuleReport
393     #
394     # This constructor function generates ModuleReport object for
395     # a separate module in a platform build.
396     #
397     # @param self            The object pointer
398     # @param M               Module context information
399     # @param ReportType      The kind of report items in the final report file
400     #
401     def __init__(self, M, ReportType):
402         self.ModuleName = M.Module.BaseName
403         self.ModuleInfPath = M.MetaFile.File
404         self.FileGuid = M.Guid
405         self.Size = 0
406         self.BuildTimeStamp = None
407         self.DriverType = ""
408         if not M.IsLibrary:
409             ModuleType = M.ModuleType
410             if not ModuleType:
411                 ModuleType = gComponentType2ModuleType.get(M.ComponentType, "")
412             #
413             # If a module complies to PI 1.1, promote Module type to "SMM_DRIVER"
414             #
415             if ModuleType == "DXE_SMM_DRIVER":
416                 PiSpec =  M.Module.Specification.get("PI_SPECIFICATION_VERSION", "0x00010000")
417                 if int(PiSpec, 0) >= 0x0001000A:
418                     ModuleType = "SMM_DRIVER"
419             self.DriverType = gDriverTypeMap.get(ModuleType, "0x2 (FREE_FORM)")
420         self.UefiSpecVersion = M.Module.Specification.get("UEFI_SPECIFICATION_VERSION", "")
421         self.PiSpecVersion = M.Module.Specification.get("PI_SPECIFICATION_VERSION", "")
422         self.PciDeviceId = M.Module.Defines.get("PCI_DEVICE_ID", "")
423         self.PciVendorId = M.Module.Defines.get("PCI_VENDOR_ID", "")
424         self.PciClassCode = M.Module.Defines.get("PCI_CLASS_CODE", "")
425
426         self._BuildDir = M.BuildDir
427         self.ModulePcdSet = {}
428         if "PCD" in ReportType:
429             #
430             # Collect all module used PCD set: module INF referenced directly or indirectly.
431             # It also saves module INF default values of them in case they exist.
432             #
433             for Pcd in M.ModulePcdList + M.LibraryPcdList:
434                 self.ModulePcdSet.setdefault((Pcd.TokenCName, Pcd.TokenSpaceGuidCName, Pcd.Type), (Pcd.InfDefaultValue, Pcd.DefaultValue))
435
436         self.LibraryReport = None
437         if "LIBRARY" in ReportType:
438             self.LibraryReport = LibraryReport(M)
439
440         self.DepexReport = None
441         if "DEPEX" in ReportType:
442             self.DepexReport = DepexReport(M)
443
444         if "BUILD_FLAGS" in ReportType:
445             self.BuildFlagsReport = BuildFlagsReport(M)
446
447
448     ##
449     # Generate report for module information
450     #
451     # This function generates report for separate module expression
452     # in a platform build.
453     #
454     # @param self            The object pointer
455     # @param File            The file object for report
456     # @param GlobalPcdReport The platform global PCD class object
457     # @param ReportType      The kind of report items in the final report file
458     #
459     def GenerateReport(self, File, GlobalPcdReport, GlobalPredictionReport, ReportType):
460         FileWrite(File, gSectionStart)
461
462         FwReportFileName = os.path.join(self._BuildDir, "DEBUG", self.ModuleName + ".txt")
463         if os.path.isfile(FwReportFileName):
464             try:
465                 FileContents = open(FwReportFileName).read()
466                 Match = gModuleSizePattern.search(FileContents)
467                 if Match:
468                     self.Size = int(Match.group(1))
469
470                 Match = gTimeStampPattern.search(FileContents)
471                 if Match:
472                     self.BuildTimeStamp = datetime.fromtimestamp(int(Match.group(1)))
473             except IOError:
474                 EdkLogger.warn(None, "Fail to read report file", FwReportFileName)
475
476         FileWrite(File, "Module Summary")
477         FileWrite(File, "Module Name:          %s" % self.ModuleName)
478         FileWrite(File, "Module INF Path:      %s" % self.ModuleInfPath)
479         FileWrite(File, "File GUID:            %s" % self.FileGuid)
480         if self.Size:
481             FileWrite(File, "Size:                 0x%X (%.2fK)" % (self.Size, self.Size / 1024.0))
482         if self.BuildTimeStamp:
483             FileWrite(File, "Build Time Stamp:     %s" % self.BuildTimeStamp)
484         if self.DriverType:
485             FileWrite(File, "Driver Type:          %s" % self.DriverType)
486         if self.UefiSpecVersion:
487             FileWrite(File, "UEFI Spec Version:    %s" % self.UefiSpecVersion)
488         if self.PiSpecVersion:
489             FileWrite(File, "PI Spec Version:      %s" % self.PiSpecVersion)
490         if self.PciDeviceId:
491             FileWrite(File, "PCI Device ID:        %s" % self.PciDeviceId)
492         if self.PciVendorId:
493             FileWrite(File, "PCI Vendor ID:        %s" % self.PciVendorId)
494         if self.PciClassCode:
495             FileWrite(File, "PCI Class Code:       %s" % self.PciClassCode)
496
497         FileWrite(File, gSectionSep)
498
499         if "PCD" in ReportType:
500             GlobalPcdReport.GenerateReport(File, self.ModulePcdSet)
501
502         if "LIBRARY" in ReportType:
503             self.LibraryReport.GenerateReport(File)
504
505         if "DEPEX" in ReportType:
506             self.DepexReport.GenerateReport(File)
507
508         if "BUILD_FLAGS" in ReportType:
509             self.BuildFlagsReport.GenerateReport(File)
510
511         if "FIXED_ADDRESS" in ReportType and self.FileGuid:
512             GlobalPredictionReport.GenerateReport(File, self.FileGuid)
513
514         FileWrite(File, gSectionEnd)
515
516 ##
517 # Reports platform and module PCD information
518 #
519 # This class reports the platform PCD section and module PCD subsection
520 # in the build report file.
521 #
522 class PcdReport(object):
523     ##
524     # Constructor function for class PcdReport
525     #
526     # This constructor function generates PcdReport object a platform build.
527     # It collects the whole PCD database from platform DSC files, platform
528     # flash description file and package DEC files.
529     #
530     # @param self            The object pointer
531     # @param Wa              Workspace context information
532     #
533     def __init__(self, Wa):
534         self.AllPcds = {}
535         self.MaxLen = 0
536         if Wa.FdfProfile:
537             self.FdfPcdSet = Wa.FdfProfile.PcdDict
538         else:
539             self.FdfPcdSet = {}
540
541         self.ModulePcdOverride = {}
542         for Pa in Wa.AutoGenObjectList:
543             #
544             # Collect all platform referenced PCDs and grouped them by PCD token space
545             # GUID C Names
546             #
547             for Pcd in Pa.AllPcdList:
548                 PcdList = self.AllPcds.setdefault(Pcd.TokenSpaceGuidCName, {}).setdefault(Pcd.Type, [])
549                 if Pcd not in PcdList:
550                     PcdList.append(Pcd)
551                 if len(Pcd.TokenCName) > self.MaxLen:
552                     self.MaxLen = len(Pcd.TokenCName)
553
554             for Module in Pa.Platform.Modules.values():
555                 #
556                 # Collect module override PCDs
557                 #
558                 for ModulePcd in Module.M.ModulePcdList + Module.M.LibraryPcdList:
559                     TokenCName = ModulePcd.TokenCName
560                     TokenSpaceGuid = ModulePcd.TokenSpaceGuidCName
561                     ModuleDefault = ModulePcd.DefaultValue
562                     ModulePath = os.path.basename(Module.M.MetaFile.File)
563                     self.ModulePcdOverride.setdefault((TokenCName, TokenSpaceGuid), {})[ModulePath] = ModuleDefault
564
565
566         #
567         # Collect PCD DEC default value.
568         #
569         self.DecPcdDefault = {}
570         for Package in Wa.BuildDatabase.WorkspaceDb.PackageList:
571             for (TokenCName, TokenSpaceGuidCName, DecType) in Package.Pcds:
572                 DecDefaultValue = Package.Pcds[TokenCName, TokenSpaceGuidCName, DecType].DefaultValue
573                 self.DecPcdDefault.setdefault((TokenCName, TokenSpaceGuidCName, DecType), DecDefaultValue)
574         #
575         # Collect PCDs defined in DSC common section
576         #
577         self.DscPcdDefault = {}
578         for Platform in Wa.BuildDatabase.WorkspaceDb.PlatformList:
579             for (TokenCName, TokenSpaceGuidCName) in Platform.Pcds:
580                 DscDefaultValue = Platform.Pcds[(TokenCName, TokenSpaceGuidCName)].DefaultValue
581                 self.DscPcdDefault[(TokenCName, TokenSpaceGuidCName)] = DscDefaultValue
582
583     ##
584     # Generate report for PCD information
585     #
586     # This function generates report for separate module expression
587     # in a platform build.
588     #
589     # @param self            The object pointer
590     # @param File            The file object for report
591     # @param ModulePcdSet    Set of all PCDs referenced by module or None for
592     #                        platform PCD report
593     # @param DscOverridePcds Module DSC override PCDs set
594     #
595     def GenerateReport(self, File, ModulePcdSet):
596         if ModulePcdSet == None:
597             #
598             # For platform global PCD section
599             #
600             FileWrite(File, gSectionStart)
601             FileWrite(File, "Platform Configuration Database Report")
602             FileWrite(File, "  *P  - Platform scoped PCD override in DSC file")
603             FileWrite(File, "  *F  - Platform scoped PCD override in FDF file")
604             FileWrite(File, "  *M  - Module scoped PCD override")
605             FileWrite(File, gSectionSep)
606         else:
607             #
608             # For module PCD sub-section
609             #
610             FileWrite(File, gSubSectionStart)
611             FileWrite(File, "PCD")
612             FileWrite(File, gSubSectionSep)
613
614         for Key in self.AllPcds:
615             #
616             # Group PCD by their token space GUID C Name
617             #
618             First = True
619             for Type in self.AllPcds[Key]:
620                 #
621                 # Group PCD by their usage type
622                 #
623                 TypeName, DecType = gPcdTypeMap.get(Type, ("", Type))
624                 for Pcd in self.AllPcds[Key][Type]:
625                     #
626                     # Get PCD default value and their override relationship
627                     #
628                     DecDefaultValue = self.DecPcdDefault.get((Pcd.TokenCName, Pcd.TokenSpaceGuidCName, DecType))
629                     DscDefaultValue = self.DscPcdDefault.get((Pcd.TokenCName, Pcd.TokenSpaceGuidCName))
630                     DscDefaultValue = self.FdfPcdSet.get((Pcd.TokenCName, Key), DscDefaultValue)
631                     InfDefaultValue = None
632                     
633                     PcdValue = DecDefaultValue
634                     if DscDefaultValue:
635                         PcdValue = DscDefaultValue
636                     if ModulePcdSet != None:
637                         if (Pcd.TokenCName, Pcd.TokenSpaceGuidCName, Type) not in ModulePcdSet:
638                             continue
639                         InfDefault, PcdValue = ModulePcdSet[Pcd.TokenCName, Pcd.TokenSpaceGuidCName, Type]
640                         if InfDefault == "":
641                             InfDefault = None
642                     if First:
643                         if ModulePcdSet == None:
644                             FileWrite(File, "")
645                         FileWrite(File, Key)
646                         First = False
647
648
649                     if Pcd.DatumType in ('UINT8', 'UINT16', 'UINT32', 'UINT64'):
650                         PcdValueNumber = int(PcdValue.strip(), 0)
651                         if DecDefaultValue == None:
652                             DecMatch = True
653                         else:
654                             DecDefaultValueNumber = int(DecDefaultValue.strip(), 0)
655                             DecMatch = (DecDefaultValueNumber == PcdValueNumber)
656
657                         if InfDefaultValue == None:
658                             InfMatch = True
659                         else:
660                             InfDefaultValueNumber = int(InfDefaultValue.strip(), 0)
661                             InfMatch = (InfDefaultValueNumber == PcdValueNumber)
662
663                         if DscDefaultValue == None:
664                             DscMatch = True
665                         else:
666                             DscDefaultValueNumber = int(DscDefaultValue.strip(), 0)
667                             DscMatch = (DscDefaultValueNumber == PcdValueNumber)
668                     else:
669                         if DecDefaultValue == None:
670                             DecMatch = True
671                         else:
672                             DecMatch = (DecDefaultValue.strip() == PcdValue.strip())
673
674                         if InfDefaultValue == None:
675                             InfMatch = True
676                         else:
677                             InfMatch = (InfDefaultValue.strip() == PcdValue.strip())
678
679                         if DscDefaultValue == None:
680                             DscMatch = True
681                         else:
682                             DscMatch = (DscDefaultValue.strip() == PcdValue.strip())
683
684                     #
685                     # Report PCD item according to their override relationship
686                     #
687                     if DecMatch and InfMatch:
688                         FileWrite(File, '    %-*s: %6s %10s = %-22s' % (self.MaxLen, Pcd.TokenCName, TypeName, '('+Pcd.DatumType+')', PcdValue.strip()))
689                     else:
690                         if DscMatch:
691                             if (Pcd.TokenCName, Key) in self.FdfPcdSet:
692                                 FileWrite(File, ' *F %-*s: %6s %10s = %-22s' % (self.MaxLen, Pcd.TokenCName, TypeName, '('+Pcd.DatumType+')', PcdValue.strip()))
693                             else:
694                                 FileWrite(File, ' *P %-*s: %6s %10s = %-22s' % (self.MaxLen, Pcd.TokenCName, TypeName, '('+Pcd.DatumType+')', PcdValue.strip()))
695                         else:
696                             FileWrite(File, ' *M %-*s: %6s %10s = %-22s' % (self.MaxLen, Pcd.TokenCName, TypeName, '('+Pcd.DatumType+')', PcdValue.strip()))
697                     
698                     if TypeName in ('DYNHII', 'DEXHII', 'DYNVPD', 'DEXVPD'):
699                         for SkuInfo in Pcd.SkuInfoList.values():
700                             if TypeName in ('DYNHII', 'DEXHII'):
701                                 FileWrite(File, '%*s: %s: %s' % (self.MaxLen + 4, SkuInfo.VariableGuid, SkuInfo.VariableName, SkuInfo.VariableOffset))        
702                             else:
703                                 FileWrite(File, '%*s' % (self.MaxLen + 4, SkuInfo.VpdOffset))
704                                
705                     if not DscMatch and DscDefaultValue != None:
706                         FileWrite(File, '    %*s = %s' % (self.MaxLen + 19, 'DSC DEFAULT', DscDefaultValue.strip()))
707
708                     if not InfMatch and InfDefaultValue != None:
709                         FileWrite(File, '    %*s = %s' % (self.MaxLen + 19, 'INF DEFAULT', InfDefaultValue.strip()))
710
711                     if not DecMatch and DecDefaultValue != None:
712                         FileWrite(File, '    %*s = %s' % (self.MaxLen + 19, 'DEC DEFAULT', DecDefaultValue.strip()))
713
714                     if ModulePcdSet == None:
715                         ModuleOverride = self.ModulePcdOverride.get((Pcd.TokenCName, Pcd.TokenSpaceGuidCName), {})
716                         for ModulePath in ModuleOverride:
717                             ModuleDefault = ModuleOverride[ModulePath]
718                             if Pcd.DatumType in ('UINT8', 'UINT16', 'UINT32', 'UINT64'):
719                                 ModulePcdDefaultValueNumber = int(ModuleDefault.strip(), 0)
720                                 Match = (ModulePcdDefaultValueNumber == PcdValueNumber)
721                             else:
722                                 Match = (ModuleDefault.strip() == PcdValue.strip())
723                             if Match:
724                                 continue
725                             FileWrite(File, ' *M %-*s = %s' % (self.MaxLen + 19, ModulePath, ModuleDefault.strip()))
726
727         if ModulePcdSet == None:
728             FileWrite(File, gSectionEnd)
729         else:
730             FileWrite(File, gSubSectionEnd)
731
732
733
734 ##
735 # Reports platform and module Prediction information
736 #
737 # This class reports the platform execution order prediction section and
738 # module load fixed address prediction subsection in the build report file.
739 #
740 class PredictionReport(object):
741     ##
742     # Constructor function for class PredictionReport
743     #
744     # This constructor function generates PredictionReport object for the platform.
745     #
746     # @param self:           The object pointer
747     # @param Wa              Workspace context information
748     #
749     def __init__(self, Wa):
750         self._MapFileName = os.path.join(Wa.BuildDir, Wa.Name + ".map")
751         self._MapFileParsed = False
752         self._EotToolInvoked = False
753         self._FvDir = Wa.FvDir
754         self._EotDir = Wa.BuildDir
755         self._FfsEntryPoint = {}
756         self._GuidMap = {}
757         self._SourceList = []
758         self.FixedMapDict = {}
759         self.ItemList = []
760         self.MaxLen = 0
761
762         #
763         # Collect all platform reference source files and GUID C Name
764         #
765         for Pa in Wa.AutoGenObjectList:
766             for Module in Pa.LibraryAutoGenList + Pa.ModuleAutoGenList:
767                 #
768                 # Add module referenced source files
769                 #
770                 self._SourceList.append(str(Module))
771                 IncludeList = {}
772                 for Source in Module.SourceFileList:
773                     if os.path.splitext(str(Source))[1].lower() == ".c":
774                         self._SourceList.append("  " + str(Source))
775                         FindIncludeFiles(Source.Path, Module.IncludePathList, IncludeList)
776                 for IncludeFile in IncludeList.values():
777                     self._SourceList.append("  " + IncludeFile)
778
779                 for Guid in Module.PpiList:
780                     self._GuidMap[Guid] = GuidStructureStringToGuidString(Module.PpiList[Guid])
781                 for Guid in Module.ProtocolList:
782                     self._GuidMap[Guid] = GuidStructureStringToGuidString(Module.ProtocolList[Guid])
783                 for Guid in Module.GuidList:
784                     self._GuidMap[Guid] = GuidStructureStringToGuidString(Module.GuidList[Guid])
785
786                 if Module.Guid and not Module.IsLibrary:
787                     EntryPoint = " ".join(Module.Module.ModuleEntryPointList)
788                     if int(str(Module.AutoGenVersion), 0) >= 0x00010005:
789                         RealEntryPoint = "_ModuleEntryPoint"
790                     else:
791                         RealEntryPoint = EntryPoint
792                         if EntryPoint == "_ModuleEntryPoint":
793                             CCFlags = Module.BuildOption.get("CC", {}).get("FLAGS", "")
794                             Match = gGlueLibEntryPoint.search(CCFlags)
795                             if Match:
796                                 EntryPoint = Match.group(1)
797
798                     self._FfsEntryPoint[Module.Guid.upper()] = (EntryPoint, RealEntryPoint)
799
800
801         #
802         # Collect platform firmware volume list as the input of EOT.
803         #
804         self._FvList = []
805         if Wa.FdfProfile:
806             for Fd in Wa.FdfProfile.FdDict:
807                 for FdRegion in Wa.FdfProfile.FdDict[Fd].RegionList:
808                     if FdRegion.RegionType != "FV":
809                         continue
810                     for FvName in FdRegion.RegionDataList:
811                         if FvName in self._FvList:
812                             continue
813                         self._FvList.append(FvName)
814                         for Ffs in Wa.FdfProfile.FvDict[FvName.upper()].FfsList:
815                             for Section in Ffs.SectionList:
816                                 try:
817                                     for FvSection in Section.SectionList:
818                                         if FvSection.FvName in self._FvList:
819                                             continue
820                                         self._FvList.append(FvSection.FvName)
821                                 except AttributeError:
822                                     pass
823
824
825     ##
826     # Parse platform fixed address map files
827     #
828     # This function parses the platform final fixed address map file to get
829     # the database of predicted fixed address for module image base, entry point
830     # etc.
831     #
832     # @param self:           The object pointer
833     #
834     def _ParseMapFile(self):
835         if self._MapFileParsed:
836             return
837         self._MapFileParsed = True
838         if os.path.isfile(self._MapFileName):
839             try:
840                 FileContents = open(self._MapFileName).read()
841                 for Match in gMapFileItemPattern.finditer(FileContents):
842                     AddressType = Match.group(1)
843                     BaseAddress = Match.group(2)
844                     EntryPoint = Match.group(3)
845                     Guid = Match.group(4).upper()
846                     List = self.FixedMapDict.setdefault(Guid, [])
847                     List.append((AddressType, BaseAddress, "*I"))
848                     List.append((AddressType, EntryPoint, "*E"))
849             except:
850                 EdkLogger.warn(None, "Cannot open file to read", self._MapFileName)
851
852     ##
853     # Invokes EOT tool to get the predicted the execution order.
854     #
855     # This function invokes EOT tool to calculate the predicted dispatch order
856     #
857     # @param self:           The object pointer
858     #
859     def _InvokeEotTool(self):
860         if self._EotToolInvoked:
861             return
862
863         self._EotToolInvoked = True
864         FvFileList = []
865         for FvName in self._FvList:
866             FvFile = os.path.join(self._FvDir, FvName + ".Fv")
867             if os.path.isfile(FvFile):
868                 FvFileList.append(FvFile)
869
870         if len(FvFileList) == 0:
871             return
872         #
873         # Write source file list and GUID file list to an intermediate file
874         # as the input for EOT tool and dispatch List as the output file
875         # from EOT tool.
876         #
877         SourceList = os.path.join(self._EotDir, "SourceFile.txt")
878         GuidList = os.path.join(self._EotDir, "GuidList.txt")
879         DispatchList = os.path.join(self._EotDir, "Dispatch.txt")
880
881         TempFile = open(SourceList, "w+")
882         for Item in self._SourceList:
883             FileWrite(TempFile, Item)
884         TempFile.close()
885         TempFile = open(GuidList, "w+")
886         for Key in self._GuidMap:
887             FileWrite(TempFile, "%s %s" % (Key, self._GuidMap[Key]))
888         TempFile.close()
889
890         try:
891             from Eot.Eot import Eot
892             #
893             # Invoke EOT tool
894             #
895             Eot(CommandLineOption=False, SourceFileList=SourceList, GuidList=GuidList,
896                 FvFileList=' '.join(FvFileList), Dispatch=DispatchList, IsInit=True)
897
898             #
899             # Parse the output of EOT tool
900             #
901             for Line in open(DispatchList):
902                 if len(Line.split()) < 4:
903                     continue
904                 (Guid, Phase, FfsName, FilePath) = Line.split()
905                 Symbol = self._FfsEntryPoint.get(Guid, [FfsName, ""])[0]
906                 if len(Symbol) > self.MaxLen:
907                     self.MaxLen = len(Symbol)
908                 self.ItemList.append((Phase, Symbol, FilePath))
909         except:
910             EdkLogger.quiet("(Python %s on %s\n%s)" % (platform.python_version(), sys.platform, traceback.format_exc()))
911             EdkLogger.warn(None, "Failed to generate execution order prediction report, for some error occurred in executing EOT.")
912
913
914     ##
915     # Generate platform execution order report
916     #
917     # This function generates the predicted module execution order.
918     #
919     # @param self            The object pointer
920     # @param File            The file object for report
921     #
922     def _GenerateExecutionOrderReport(self, File):
923         self._InvokeEotTool()
924         if len(self.ItemList) == 0:
925             return
926         FileWrite(File, gSectionStart)
927         FileWrite(File, "Execution Order Prediction")
928         FileWrite(File, "*P PEI phase")
929         FileWrite(File, "*D DXE phase")
930         FileWrite(File, "*E Module INF entry point name")
931         FileWrite(File, "*N Module notification function name")
932
933         FileWrite(File, "Type %-*s %s" % (self.MaxLen, "Symbol", "Module INF Path"))
934         FileWrite(File, gSectionSep)
935         for Item in self.ItemList:
936             FileWrite(File, "*%sE  %-*s %s" % (Item[0], self.MaxLen, Item[1], Item[2]))
937
938         FileWrite(File, gSectionStart)
939
940     ##
941     # Generate Fixed Address report.
942     #
943     # This function generate the predicted fixed address report for a module
944     # specified by Guid.
945     #
946     # @param self            The object pointer
947     # @param File            The file object for report
948     # @param Guid            The module Guid value.
949     # @param NotifyList      The list of all notify function in a module
950     #
951     def _GenerateFixedAddressReport(self, File, Guid, NotifyList):
952         self._ParseMapFile()
953         FixedAddressList = self.FixedMapDict.get(Guid)
954         if not FixedAddressList:
955             return
956
957         FileWrite(File, gSubSectionStart)
958         FileWrite(File, "Fixed Address Prediction")
959         FileWrite(File, "*I  Image Loading Address")
960         FileWrite(File, "*E  Entry Point Address")
961         FileWrite(File, "*N  Notification Function Address")
962         FileWrite(File, "*F  Flash Address")
963         FileWrite(File, "*M  Memory Address")
964         FileWrite(File, "*S  SMM RAM Offset")
965         FileWrite(File, "TOM Top of Memory")
966
967         FileWrite(File, "Type Address           Name")
968         FileWrite(File, gSubSectionSep)
969         for Item in FixedAddressList:
970             Type = Item[0]
971             Value = Item[1]
972             Symbol = Item[2]
973             if Symbol == "*I":
974                 Name = "(Image Base)"
975             elif Symbol == "*E":
976                 Name = self._FfsEntryPoint.get(Guid, ["", "_ModuleEntryPoint"])[1]
977             elif Symbol in NotifyList:
978                 Name = Symbol
979                 Symbol = "*N"
980             else:
981                 continue
982
983             if "Flash" in Type:
984                 Symbol += "F"
985             elif "Memory" in Type:
986                 Symbol += "M"
987             else:
988                 Symbol += "S"
989
990             if Value[0] == "-":
991                 Value = "TOM" + Value
992
993             FileWrite(File, "%s  %-16s  %s" % (Symbol, Value, Name))
994
995     ##
996     # Generate report for the prediction part
997     #
998     # This function generate the predicted fixed address report for a module or
999     # predicted module execution order for a platform.
1000     # If the input Guid is None, then, it generates the predicted module execution order;
1001     # otherwise it generated the module fixed loading address for the module specified by
1002     # Guid.
1003     #
1004     # @param self            The object pointer
1005     # @param File            The file object for report
1006     # @param Guid            The module Guid value.
1007     #
1008     def GenerateReport(self, File, Guid):
1009         if Guid:
1010             self._GenerateFixedAddressReport(File, Guid.upper(), [])
1011         else:
1012             self._GenerateExecutionOrderReport(File)
1013
1014 ##
1015 # Reports FD region information
1016 #
1017 # This class reports the FD subsection in the build report file.
1018 # It collects region information of platform flash device.
1019 # If the region is a firmware volume, it lists the set of modules
1020 # and its space information; otherwise, it only lists its region name,
1021 # base address and size in its sub-section header.
1022 # If there are nesting FVs, the nested FVs will list immediate after
1023 # this FD region subsection
1024 #
1025 class FdRegionReport(object):
1026     ##
1027     # Discover all the nested FV name list.
1028     #
1029     # This is an internal worker function to discover the all the nested FV information
1030     # in the parent firmware volume. It uses deep first search algorithm recursively to
1031     # find all the FV list name and append them to the list.
1032     #
1033     # @param self            The object pointer
1034     # @param FvName          The name of current firmware file system
1035     # @param Wa              Workspace context information
1036     #
1037     def _DiscoverNestedFvList(self, FvName, Wa):
1038         for Ffs in Wa.FdfProfile.FvDict[FvName.upper()].FfsList:
1039             for Section in Ffs.SectionList:
1040                 try:
1041                     for FvSection in Section.SectionList:
1042                         if FvSection.FvName in self.FvList:
1043                             continue
1044                         self._GuidsDb[Ffs.NameGuid.upper()] = FvSection.FvName
1045                         self.FvList.append(FvSection.FvName)
1046                         self.FvInfo[FvSection.FvName] = ("Nested FV", 0, 0)
1047                         self._DiscoverNestedFvList(FvSection.FvName, Wa)
1048                 except AttributeError:
1049                     pass
1050
1051     ##
1052     # Constructor function for class FdRegionReport
1053     #
1054     # This constructor function generates FdRegionReport object for a specified FdRegion.
1055     # If the FdRegion is a firmware volume, it will recursively find all its nested Firmware
1056     # volume list. This function also collects GUID map in order to dump module identification
1057     # in the final report.
1058     #
1059     # @param self:           The object pointer
1060     # @param FdRegion        The current FdRegion object
1061     # @param Wa              Workspace context information
1062     #
1063     def __init__(self, FdRegion, Wa):
1064         self.Type = FdRegion.RegionType
1065         self.BaseAddress = FdRegion.Offset
1066         self.Size = FdRegion.Size
1067         self.FvList = []
1068         self.FvInfo = {}
1069         self._GuidsDb = {}
1070         self._FvDir = Wa.FvDir
1071
1072         #
1073         # If the input FdRegion is not a firmware volume,
1074         # we are done.
1075         #
1076         if self.Type != "FV":
1077             return
1078
1079         #
1080         # Find all nested FVs in the FdRegion
1081         #
1082         for FvName in FdRegion.RegionDataList:
1083             if FvName in self.FvList:
1084                 continue
1085             self.FvList.append(FvName)
1086             self.FvInfo[FvName] = ("Fd Region", self.BaseAddress, self.Size)
1087             self._DiscoverNestedFvList(FvName, Wa)
1088
1089         PlatformPcds = {}
1090         
1091         #
1092         # Collect PCDs declared in DEC files.
1093         #
1094         for Package in Wa.BuildDatabase.WorkspaceDb.PackageList:
1095             for (TokenCName, TokenSpaceGuidCName, DecType) in Package.Pcds:
1096                 DecDefaultValue = Package.Pcds[TokenCName, TokenSpaceGuidCName, DecType].DefaultValue
1097                 PlatformPcds[(TokenCName, TokenSpaceGuidCName)] = DecDefaultValue
1098         #
1099         # Collect PCDs defined in DSC common section
1100         #
1101         for Platform in Wa.BuildDatabase.WorkspaceDb.PlatformList:
1102             for (TokenCName, TokenSpaceGuidCName) in Platform.Pcds:
1103                 DscDefaultValue = Platform.Pcds[(TokenCName, TokenSpaceGuidCName)].DefaultValue
1104                 PlatformPcds[(TokenCName, TokenSpaceGuidCName)] = DscDefaultValue
1105
1106         #
1107         # Add PEI and DXE a priori files GUIDs defined in PI specification.
1108         #
1109         self._GuidsDb["1B45CC0A-156A-428A-AF62-49864DA0E6E6"] = "PEI Apriori"
1110         self._GuidsDb["FC510EE7-FFDC-11D4-BD41-0080C73C8881"] = "DXE Apriori"
1111         #
1112         # Add ACPI table storage file
1113         #
1114         self._GuidsDb["7E374E25-8E01-4FEE-87F2-390C23C606CD"] = "ACPI table storage"
1115
1116         for Pa in Wa.AutoGenObjectList:
1117             for ModuleKey in Pa.Platform.Modules:
1118                 M = Pa.Platform.Modules[ModuleKey].M
1119                 InfPath = os.path.join(Wa.WorkspaceDir, M.MetaFile.File)
1120                 self._GuidsDb[M.Guid.upper()] = "%s (%s)" % (M.Module.BaseName, InfPath)
1121
1122         #
1123         # Collect the GUID map in the FV firmware volume
1124         #
1125         for FvName in self.FvList:
1126             for Ffs in Wa.FdfProfile.FvDict[FvName.upper()].FfsList:
1127                 try:
1128                     #
1129                     # collect GUID map for binary EFI file in FDF file.
1130                     #
1131                     Guid = Ffs.NameGuid.upper()
1132                     Match = gPcdGuidPattern.match(Ffs.NameGuid)
1133                     if Match:
1134                         PcdTokenspace = Match.group(1)
1135                         PcdToken = Match.group(2)
1136                         if (PcdToken, PcdTokenspace) in PlatformPcds:
1137                             GuidValue = PlatformPcds[(PcdToken, PcdTokenspace)]
1138                             Guid = GuidStructureByteArrayToGuidString(GuidValue).upper()
1139                     for Section in Ffs.SectionList:
1140                         try:
1141                             ModuleSectFile = os.path.join(Wa.WorkspaceDir, Section.SectFileName)
1142                             self._GuidsDb[Guid] = ModuleSectFile
1143                         except AttributeError:
1144                             pass
1145                 except AttributeError:
1146                     pass
1147
1148
1149     ##
1150     # Internal worker function to generate report for the FD region
1151     #
1152     # This internal worker function to generate report for the FD region.
1153     # It the type is firmware volume, it lists offset and module identification.
1154     #
1155     # @param self            The object pointer
1156     # @param File            The file object for report
1157     # @param Title           The title for the FD subsection
1158     # @param BaseAddress     The base address for the FD region
1159     # @param Size            The size of the FD region
1160     # @param FvName          The FV name if the FD region is a firmware volume
1161     #
1162     def _GenerateReport(self, File, Title, Type, BaseAddress, Size=0, FvName=None):
1163         FileWrite(File, gSubSectionStart)
1164         FileWrite(File, Title)
1165         FileWrite(File, "Type:               %s" % Type)
1166         FileWrite(File, "Base Address:       0x%X" % BaseAddress)
1167
1168         if self.Type == "FV":
1169             FvTotalSize = 0
1170             FvTakenSize = 0
1171             FvFreeSize  = 0
1172             FvReportFileName = os.path.join(self._FvDir, FvName + ".fv.txt")
1173             try:
1174                 #
1175                 # Collect size info in the firmware volume.
1176                 #
1177                 FvReport = open(FvReportFileName).read()
1178                 Match = gFvTotalSizePattern.search(FvReport)
1179                 if Match:
1180                     FvTotalSize = int(Match.group(1), 16)
1181                 Match = gFvTakenSizePattern.search(FvReport)
1182                 if Match:
1183                     FvTakenSize = int(Match.group(1), 16)
1184                 FvFreeSize = FvTotalSize - FvTakenSize
1185                 #
1186                 # Write size information to the report file.
1187                 #
1188                 FileWrite(File, "Size:               0x%X (%.0fK)" % (FvTotalSize, FvTotalSize / 1024.0))
1189                 FileWrite(File, "Fv Name:            %s (%.1f%% Full)" % (FvName, FvTakenSize * 100.0 / FvTotalSize))
1190                 FileWrite(File, "Occupied Size:      0x%X (%.0fK)" % (FvTakenSize, FvTakenSize / 1024.0))
1191                 FileWrite(File, "Free Size:          0x%X (%.0fK)" % (FvFreeSize, FvFreeSize / 1024.0))
1192                 FileWrite(File, "Offset     Module")
1193                 FileWrite(File, gSubSectionSep)
1194                 #
1195                 # Write module offset and module identification to the report file.
1196                 #
1197                 OffsetInfo = {}
1198                 for Match in gOffsetGuidPattern.finditer(FvReport):
1199                     Guid = Match.group(2).upper()
1200                     OffsetInfo[Match.group(1)] = self._GuidsDb.get(Guid, Guid)
1201                 OffsetList = OffsetInfo.keys()
1202                 OffsetList.sort()
1203                 for Offset in OffsetList:
1204                     FileWrite (File, "%s %s" % (Offset, OffsetInfo[Offset]))
1205             except IOError:
1206                 EdkLogger.warn(None, "Fail to read report file", FvReportFileName)
1207         else:
1208             FileWrite(File, "Size:               0x%X (%.0fK)" % (Size, Size / 1024.0))
1209         FileWrite(File, gSubSectionEnd)
1210
1211     ##
1212     # Generate report for the FD region
1213     #
1214     # This function generates report for the FD region.
1215     #
1216     # @param self            The object pointer
1217     # @param File            The file object for report
1218     #
1219     def GenerateReport(self, File):
1220         if (len(self.FvList) > 0):
1221             for FvItem in self.FvList:
1222                 Info = self.FvInfo[FvItem]
1223                 self._GenerateReport(File, Info[0], "FV", Info[1], Info[2], FvItem)
1224         else:
1225             self._GenerateReport(File, "FD Region", self.Type, self.BaseAddress, self.Size)
1226
1227 ##
1228 # Reports FD information
1229 #
1230 # This class reports the FD section in the build report file.
1231 # It collects flash device information for a platform.
1232 #
1233 class FdReport(object):
1234     ##
1235     # Constructor function for class FdReport
1236     #
1237     # This constructor function generates FdReport object for a specified
1238     # firmware device.
1239     #
1240     # @param self            The object pointer
1241     # @param Fd              The current Firmware device object
1242     # @param Wa              Workspace context information
1243     #
1244     def __init__(self, Fd, Wa):
1245         self.FdName = Fd.FdUiName
1246         self.BaseAddress = Fd.BaseAddress
1247         self.Size = Fd.Size
1248         self.FdRegionList = [FdRegionReport(FdRegion, Wa) for FdRegion in Fd.RegionList]
1249
1250     ##
1251     # Generate report for the firmware device.
1252     #
1253     # This function generates report for the firmware device.
1254     #
1255     # @param self            The object pointer
1256     # @param File            The file object for report
1257     #
1258     def GenerateReport(self, File):
1259         FileWrite(File, gSectionStart)
1260         FileWrite(File, "Firmware Device (FD)")
1261         FileWrite(File, "FD Name:            %s" % self.FdName)
1262         FileWrite(File, "Base Address:       %s" % self.BaseAddress)
1263         FileWrite(File, "Size:               0x%X (%.0fK)" % (self.Size, self.Size / 1024.0))
1264         if len(self.FdRegionList) > 0:
1265             FileWrite(File, gSectionSep)
1266             for FdRegionItem in self.FdRegionList:
1267                 FdRegionItem.GenerateReport(File)
1268
1269         FileWrite(File, gSectionEnd)
1270
1271
1272
1273 ##
1274 # Reports platform information
1275 #
1276 # This class reports the whole platform information
1277 #
1278 class PlatformReport(object):
1279     ##
1280     # Constructor function for class PlatformReport
1281     #
1282     # This constructor function generates PlatformReport object a platform build.
1283     # It generates report for platform summary, flash, global PCDs and detailed
1284     # module information for modules involved in platform build.
1285     #
1286     # @param self            The object pointer
1287     # @param Wa              Workspace context information
1288     # @param MaList          The list of modules in the platform build
1289     #
1290     def __init__(self, Wa, MaList, ReportType):
1291         self._WorkspaceDir = Wa.WorkspaceDir
1292         self.PlatformName = Wa.Name
1293         self.PlatformDscPath = Wa.Platform
1294         self.Architectures = " ".join(Wa.ArchList)
1295         self.ToolChain = Wa.ToolChain
1296         self.Target = Wa.BuildTarget
1297         self.OutputPath = os.path.join(Wa.WorkspaceDir, Wa.OutputDir)
1298         self.BuildEnvironment = platform.platform()
1299
1300         self.PcdReport = None
1301         if "PCD" in ReportType:
1302             self.PcdReport = PcdReport(Wa)
1303
1304         self.FdReportList = []
1305         if "FLASH" in ReportType and Wa.FdfProfile and MaList == None:
1306             for Fd in Wa.FdfProfile.FdDict:
1307                 self.FdReportList.append(FdReport(Wa.FdfProfile.FdDict[Fd], Wa))
1308
1309         self.PredictionReport = None
1310         if "FIXED_ADDRESS" in ReportType or "EXECUTION_ORDER" in ReportType:
1311             self.PredictionReport = PredictionReport(Wa)
1312
1313         self.ModuleReportList = []
1314         if MaList != None:
1315             self._IsModuleBuild = True
1316             for Ma in MaList:
1317                 self.ModuleReportList.append(ModuleReport(Ma, ReportType))
1318         else:
1319             self._IsModuleBuild = False
1320             for Pa in Wa.AutoGenObjectList:
1321                 for ModuleKey in Pa.Platform.Modules:
1322                     self.ModuleReportList.append(ModuleReport(Pa.Platform.Modules[ModuleKey].M, ReportType))
1323
1324
1325
1326     ##
1327     # Generate report for the whole platform.
1328     #
1329     # This function generates report for platform information.
1330     # It comprises of platform summary, global PCD, flash and
1331     # module list sections.
1332     #
1333     # @param self            The object pointer
1334     # @param File            The file object for report
1335     # @param BuildDuration   The total time to build the modules
1336     # @param ReportType      The kind of report items in the final report file
1337     #
1338     def GenerateReport(self, File, BuildDuration, ReportType):
1339         FileWrite(File, "Platform Summary")
1340         FileWrite(File, "Platform Name:        %s" % self.PlatformName)
1341         FileWrite(File, "Platform DSC Path:    %s" % self.PlatformDscPath)
1342         FileWrite(File, "Architectures:        %s" % self.Architectures)
1343         FileWrite(File, "Tool Chain:           %s" % self.ToolChain)
1344         FileWrite(File, "Target:               %s" % self.Target)
1345         FileWrite(File, "Output Path:          %s" % self.OutputPath)
1346         FileWrite(File, "Build Environment:    %s" % self.BuildEnvironment)
1347         FileWrite(File, "Build Duration:       %s" % BuildDuration)
1348         FileWrite(File, "Report Content:       %s" % ", ".join(ReportType))
1349
1350         if not self._IsModuleBuild:
1351             if "PCD" in ReportType:
1352                 self.PcdReport.GenerateReport(File, None)
1353     
1354             if "FLASH" in ReportType:
1355                 for FdReportListItem in self.FdReportList:
1356                     FdReportListItem.GenerateReport(File)
1357
1358         for ModuleReportItem in self.ModuleReportList:
1359             ModuleReportItem.GenerateReport(File, self.PcdReport, self.PredictionReport, ReportType)
1360
1361         if not self._IsModuleBuild:
1362             if "EXECUTION_ORDER" in ReportType:
1363                 self.PredictionReport.GenerateReport(File, None)
1364
1365 ## BuildReport class
1366 #
1367 #  This base class contain the routines to collect data and then
1368 #  applies certain format to the output report
1369 #
1370 class BuildReport(object):
1371     ##
1372     # Constructor function for class BuildReport
1373     #
1374     # This constructor function generates BuildReport object a platform build.
1375     # It generates report for platform summary, flash, global PCDs and detailed
1376     # module information for modules involved in platform build.
1377     #
1378     # @param self            The object pointer
1379     # @param ReportFile      The file name to save report file
1380     # @param ReportType      The kind of report items in the final report file
1381     #
1382     def __init__(self, ReportFile, ReportType):
1383         self.ReportFile = ReportFile
1384         if ReportFile:
1385             self.ReportList = []
1386             self.ReportType = []
1387             if ReportType: 
1388                 for ReportTypeItem in ReportType:
1389                     if ReportTypeItem not in self.ReportType:
1390                         self.ReportType.append(ReportTypeItem)
1391             else:
1392                 self.ReportType = ["PCD", "LIBRARY", "BUILD_FLAGS", "DEPEX", "FLASH", "FIXED_ADDRESS"]
1393     ##
1394     # Adds platform report to the list
1395     #
1396     # This function adds a platform report to the final report list.
1397     #
1398     # @param self            The object pointer
1399     # @param Wa              Workspace context information
1400     # @param MaList          The list of modules in the platform build
1401     #
1402     def AddPlatformReport(self, Wa, MaList=None):
1403         if self.ReportFile:
1404             self.ReportList.append((Wa, MaList))
1405
1406     ##
1407     # Generates the final report.
1408     #
1409     # This function generates platform build report. It invokes GenerateReport()
1410     # method for every platform report in the list.
1411     #
1412     # @param self            The object pointer
1413     # @param BuildDuration   The total time to build the modules
1414     #
1415     def GenerateReport(self, BuildDuration):
1416         if self.ReportFile:
1417             try:
1418                 File = open(self.ReportFile, "w+")
1419             except IOError:
1420                 EdkLogger.error(None, FILE_OPEN_FAILURE, ExtraData=self.ReportFile)
1421             try:
1422                 for (Wa, MaList) in self.ReportList:
1423                     PlatformReport(Wa, MaList, self.ReportType).GenerateReport(File, BuildDuration, self.ReportType)
1424                 EdkLogger.quiet("Report successfully saved to %s" % os.path.abspath(self.ReportFile))
1425             except IOError:
1426                 EdkLogger.error(None, FILE_WRITE_FAILURE, ExtraData=self.ReportFile)
1427             except:
1428                 EdkLogger.error("BuildReport", CODE_ERROR, "Unknown fatal error when generating build report", ExtraData=self.ReportFile, RaiseError=False)
1429                 EdkLogger.quiet("(Python %s on %s\n%s)" % (platform.python_version(), sys.platform, traceback.format_exc()))
1430             File.close()
1431             
1432 # This acts like the main() function for the script, unless it is 'import'ed into another script.
1433 if __name__ == '__main__':
1434     pass
1435