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