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