1. Add loading fixed address report if the fixed address map file is generated.
[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 from Eot.Eot import Eot
31
32
33 ## Pattern to extract contents in EDK DXS files
34 gDxsDependencyPattern = re.compile(r"DEPENDENCY_START(.+)DEPENDENCY_END", re.DOTALL)
35
36 ## Pattern to find total FV total size, occupied size in flash report intermediate file
37 gFvTotalSizePattern = re.compile(r"EFI_FV_TOTAL_SIZE = (0x[0-9a-fA-F]+)")
38 gFvTakenSizePattern = re.compile(r"EFI_FV_TAKEN_SIZE = (0x[0-9a-fA-F]+)")
39
40 ## Pattern to find module size and time stamp in module summary report intermediate file  
41 gModuleSizePattern = re.compile(r"MODULE_SIZE = (\d+)")
42 gTimeStampPattern  = re.compile(r"TIME_STAMP = (\d+)") 
43
44 ## Pattern to find GUID value in flash description files
45 gPcdGuidPattern = re.compile(r"PCD\((\w+)[.](\w+)\)")
46
47 ## Pattern to collect offset, GUID value pair in the flash report intermediate file 
48 gOffsetGuidPattern = re.compile(r"(0x[0-9A-Fa-f]+) ([-A-Fa-f0-9]+)")
49
50 ## Pattern to find module base address and entry point in fixed flash map file
51 gModulePattern = r"\n\w+\s*\(([^,]+),\s*BaseAddress=%(Address)s,\s*EntryPoint=%(Address)s\)\s*\(GUID=([-0-9A-Fa-f]+)[^)]*\)"
52 gMapFileItemPattern = re.compile(gModulePattern % {"Address" : "(-?0[xX][0-9A-Fa-f]+)"})
53
54 ## Pattern to find all module referenced header files in source files
55 gIncludePattern  = re.compile(r'#include\s*["<]([^">]+)[">]')
56 gIncludePattern2 = re.compile(r"#include\s+EFI_([A-Z_]+)\s*[(]\s*(\w+)\s*[)]")
57
58 ## Pattern to find the entry point for EDK module using EDKII Glue library
59 gGlueLibEntryPoint = re.compile(r"__EDKII_GLUE_MODULE_ENTRY_POINT__\s*=\s*(\w+)")
60
61 ## Tags for section start, end and separator
62 gSectionStart = ">" + "=" * 118 + "<"
63 gSectionEnd = "<" + "=" * 118 + ">" + "\n"
64 gSectionSep = "=" * 120
65
66 ## Tags for subsection start, end and separator
67 gSubSectionStart = ">" + "-" * 118 + "<"
68 gSubSectionEnd = "<" + "-" * 118 + ">"
69 gSubSectionSep = "-" * 120
70
71 ## The look up table to map PCD type to pair of report display type and DEC type
72 gPcdTypeMap = {
73   'FixedAtBuild'     : ('FIXED',  'FixedAtBuild'),
74   'PatchableInModule': ('PATCH',  'PatchableInModule'),
75   'FeatureFlag'      : ('FLAG',   'FeatureFlag'),
76   'Dynamic'          : ('DYN',    'Dynamic'),
77   'DynamicHii'       : ('DYNHII', 'Dynamic'),
78   'DynamicVpd'       : ('DYNVPD', 'Dynamic'),
79   'DynamicEx'        : ('DEX',    'Dynamic'),
80   'DynamicExHii'     : ('DEXHII', 'Dynamic'),
81   'DynamicExVpd'     : ('DEXVPD', 'Dynamic'),
82   }
83
84 ## The look up table to map module type to driver type
85 gDriverTypeMap = {
86   'SEC'               : '0x3 (SECURITY_CORE)',
87   'PEI_CORE'          : '0x4 (PEI_CORE)',
88   'PEIM'              : '0x6 (PEIM)',
89   'DXE_CORE'          : '0x5 (DXE_CORE)',
90   'DXE_DRIVER'        : '0x7 (DRIVER)',
91   'DXE_SAL_DRIVER'    : '0x7 (DRIVER)',
92   'DXE_SMM_DRIVER'    : '0x7 (DRIVER)',
93   'DXE_RUNTIME_DRIVER': '0x7 (DRIVER)',
94   'UEFI_DRIVER'       : '0x7 (DRIVER)',
95   'UEFI_APPLICATION'  : '0x9 (APPLICATION)',
96   'SMM_CORE'          : '0xD (SMM_CORE)',
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 + "\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         self.DriverType = gDriverTypeMap.get(ModuleType, "")
401         self.UefiSpecVersion = M.Module.Specification.get("UEFI_SPECIFICATION_VERSION", "")
402         self.PiSpecVersion = M.Module.Specification.get("PI_SPECIFICATION_VERSION", "")
403         self.PciDeviceId = M.Module.Defines.get("PCI_DEVICE_ID", "")
404         self.PciVendorId = M.Module.Defines.get("PCI_VENDOR_ID", "")
405         self.PciClassCode = M.Module.Defines.get("PCI_CLASS_CODE", "")
406   
407         self._BuildDir = M.BuildDir
408         self.ModulePcdSet = {}
409         self.ModuleDscOverridePcds = {}
410         if "PCD" in ReportType:
411             #
412             # Collect all module used PCD set: module INF referenced directly or indirectly.
413             # It also saves module INF default values of them in case they exist.
414             #
415             for Pcd in M.ModulePcdList + M.LibraryPcdList:
416                 self.ModulePcdSet.setdefault((Pcd.TokenCName, Pcd.TokenSpaceGuidCName, Pcd.Type), Pcd.InfDefaultValue)
417             
418             #
419             # Collect module DSC override PCD set for report
420             #
421             for (PcdTokenCName, PcdTokenSpaceGuidCName) in DscOverridePcds:
422                 Pcd = DscOverridePcds[(PcdTokenCName, PcdTokenSpaceGuidCName)]
423                 self.ModuleDscOverridePcds.setdefault((PcdTokenCName, PcdTokenSpaceGuidCName), Pcd.DefaultValue)
424         
425         self.LibraryReport = None
426         if "LIBRARY" in ReportType:
427             self.LibraryReport = LibraryReport(M)
428         
429         self.DepexReport = None
430         if "DEPEX" in ReportType:
431             self.DepexReport = DepexReport(M)
432         
433         if "BUILD_FLAGS" in ReportType:
434             self.BuildFlagsReport = BuildFlagsReport(M)
435         
436     
437     ##
438     # Generate report for module information
439     #
440     # This function generates report for separate module expression
441     # in a platform build.
442     #
443     # @param self            The object pointer
444     # @param File            The file object for report
445     # @param GlobalPcdReport The platform global PCD class object
446     # @param ReportType      The kind of report items in the final report file
447     #
448     def GenerateReport(self, File, GlobalPcdReport, GlobalPredictionReport, ReportType):
449         FileWrite(File, gSectionStart)
450         
451         FwReportFileName = os.path.join(self._BuildDir, "DEBUG", self.ModuleName + ".txt")
452         if os.path.isfile(FwReportFileName):
453             try:
454                 FileContents = open(FwReportFileName).read()
455                 Match = gModuleSizePattern.search(FileContents)
456                 if Match:
457                     self.Size = int(Match.group(1))
458                 
459                 Match = gTimeStampPattern.search(FileContents)
460                 if Match:
461                     self.BuildTimeStamp = datetime.utcfromtimestamp(int(Match.group(1)))
462             except IOError:
463                 EdkLogger.warn(None, "Fail to read report file", FwReportFileName)
464             
465         FileWrite(File, "Module Summary")
466         FileWrite(File, "Module Name:          %s" % self.ModuleName)
467         FileWrite(File, "Module INF Path:      %s" % self.ModuleInfPath)
468         FileWrite(File, "File GUID:            %s" % self.FileGuid)
469         if self.Size:
470             FileWrite(File, "Size:                 0x%X (%.2fK)" % (self.Size, self.Size / 1024.0))
471         if self.BuildTimeStamp:
472             FileWrite(File, "Build Time Stamp:     %s" % self.BuildTimeStamp)
473         if self.DriverType:
474             FileWrite(File, "Driver Type:          %s" % self.DriverType)
475         if self.UefiSpecVersion:
476             FileWrite(File, "UEFI Spec Version:    %s" % self.DriverType)
477         if self.PiSpecVersion:
478             FileWrite(File, "PI Spec Version:      %s" % self.PiSpecVersion)
479         if self.PciDeviceId:
480             FileWrite(File, "PCI Device ID:        %s" % self.PciDeviceId)
481         if self.PciVendorId:
482             FileWrite(File, "PCI Vendor ID:        %s" % self.PciVendorId)
483         if self.PciClassCode:
484             FileWrite(File, "PCI Class Code:       %s" % self.PciClassCode)
485         
486         FileWrite(File, gSectionSep)   
487         
488         if "PCD" in ReportType:
489             GlobalPcdReport.GenerateReport(File, self.ModulePcdSet, self.ModuleDscOverridePcds)
490         
491         if "LIBRARY" in ReportType:
492             self.LibraryReport.GenerateReport(File)
493         
494         if "DEPEX" in ReportType:
495             self.DepexReport.GenerateReport(File)
496         
497         if "BUILD_FLAGS" in ReportType:
498             self.BuildFlagsReport.GenerateReport(File)
499
500         if "PREDICTION" in ReportType:
501             GlobalPredictionReport.GenerateReport(File, self.FileGuid)
502     
503         FileWrite(File, gSectionEnd)
504
505 ##
506 # Reports platform and module PCD information
507 #
508 # This class reports the platform PCD section and module PCD subsection
509 # in the build report file.
510 #     
511 class PcdReport(object):
512     ##
513     # Constructor function for class PcdReport
514     #
515     # This constructor function generates PcdReport object a platform build.
516     # It collects the whole PCD database from platform DSC files, platform
517     # flash description file and package DEC files.
518     #
519     # @param self            The object pointer
520     # @param Wa              Workspace context information
521     #
522     def __init__(self, Wa):
523         self.AllPcds = {}
524         self.MaxLen = 0
525         self.FdfPcdSet = Wa.FdfProfile.PcdDict
526
527         self.DecPcdDefault = {}
528         self.ModulePcdOverride = {}
529         for Pa in Wa.AutoGenObjectList:
530             #
531             # Collect all platform referenced PCDs and grouped them by PCD token space
532             # GUID C Names
533             #
534             for Pcd in Pa.AllPcdList:
535                 PcdList = self.AllPcds.setdefault(Pcd.TokenSpaceGuidCName, {}).setdefault(Pcd.Type, [])
536                 if Pcd not in PcdList:
537                     PcdList.append(Pcd)
538                 if len(Pcd.TokenCName) > self.MaxLen:
539                     self.MaxLen = len(Pcd.TokenCName)
540             
541             for ModuleKey in Pa.Platform.Modules:
542                 #
543                 # Collect PCD DEC default value.
544                 #   
545                 Module = Pa.Platform.Modules[ModuleKey]
546                 for Package in Module.M.Module.Packages:
547                     for (TokenCName, TokenSpaceGuidCName, DecType) in Package.Pcds:
548                         DecDefaultValue = Package.Pcds[TokenCName, TokenSpaceGuidCName, DecType].DefaultValue
549                         self.DecPcdDefault.setdefault((TokenCName, TokenSpaceGuidCName, DecType), DecDefaultValue)
550                 #
551                 # Collect module override PCDs
552                 #
553                 for ModulePcd in Module.M.ModulePcdList + Module.M.LibraryPcdList:
554                     TokenCName = ModulePcd.TokenCName
555                     TokenSpaceGuid = ModulePcd.TokenSpaceGuidCName
556                     ModuleDefault = ModulePcd.DefaultValue
557                     ModulePath = os.path.basename(Module.M.MetaFile.File)
558                     self.ModulePcdOverride.setdefault((TokenCName, TokenSpaceGuid), []).append((ModuleDefault, ModulePath))
559
560         #
561         # Collect PCDs defined in DSC common section
562         #
563         self.DscPcdDefault = {}
564         for Platform in Wa.BuildDatabase.WorkspaceDb.PlatformList:
565             for (TokenCName, TokenSpaceGuidCName) in Platform.Pcds:
566                 DscDefaultValue = Platform.Pcds[(TokenCName, TokenSpaceGuidCName)].DefaultValue
567                 self.DscPcdDefault[(TokenCName, TokenSpaceGuidCName)] = DscDefaultValue
568     
569     ##
570     # Generate report for PCD information
571     #
572     # This function generates report for separate module expression
573     # in a platform build.
574     #
575     # @param self            The object pointer
576     # @param File            The file object for report
577     # @param ModulePcdSet    Set of all PCDs referenced by module or None for
578     #                        platform PCD report
579     # @param DscOverridePcds Module DSC override PCDs set
580     #
581     def GenerateReport(self, File, ModulePcdSet, DscOverridePcds):
582         if ModulePcdSet == None:
583             #
584             # For platform global PCD section
585             #
586             FileWrite(File, gSectionStart)
587             FileWrite(File, "Platform Configuration Database Report")
588             FileWrite(File, "  *P  - Platform scoped PCD override in DSC file")
589             FileWrite(File, "  *F  - Platform scoped PCD override in FDF file")
590             FileWrite(File, "  *M  - Module scoped PCD override in DSC file")
591             FileWrite(File, gSectionSep)
592         else:
593             #
594             # For module PCD sub-section
595             #
596             FileWrite(File, gSubSectionStart)
597             FileWrite(File, "PCD")
598             FileWrite(File, gSubSectionSep)
599
600         for Key in self.AllPcds:
601             #
602             # Group PCD by their token space GUID C Name
603             #
604             First = True
605             for Type in self.AllPcds[Key]:
606                 #
607                 # Group PCD by their usage type
608                 #
609                 TypeName, DecType = gPcdTypeMap.get(Type, ("", Type))
610                 for Pcd in self.AllPcds[Key][Type]:
611                     #
612                     # Get PCD default value and their override relationship
613                     #
614                     InfDefaultValue = None
615                     if ModulePcdSet != None:
616                         if (Pcd.TokenCName, Pcd.TokenSpaceGuidCName, Type) not in ModulePcdSet:
617                             continue
618                         InfDefault = ModulePcdSet[Pcd.TokenCName, Pcd.TokenSpaceGuidCName, Type]
619                         if InfDefault == "":
620                             InfDefault = None
621                     if First:
622                         if ModulePcdSet == None:
623                             FileWrite(File, "")
624                         FileWrite(File, Key)
625                         First = False
626                     DecDefaultValue = self.DecPcdDefault.get((Pcd.TokenCName, Pcd.TokenSpaceGuidCName, DecType))
627                     DscDefaultValue = self.DscPcdDefault.get((Pcd.TokenCName, Pcd.TokenSpaceGuidCName))
628                     DscModuleOverrideValue = DscOverridePcds.get((Pcd.TokenCName, Pcd.TokenSpaceGuidCName))
629                           
630                     if Pcd.DatumType in ('UINT8', 'UINT16', 'UINT32', 'UINT64'):
631                         PcdDefaultValueNumber = int(Pcd.DefaultValue.strip(), 0)
632                         if DecDefaultValue == None:
633                             DecMatch = True
634                         else:
635                             DecDefaultValueNumber = int(DecDefaultValue.strip(), 0)
636                             DecMatch = (DecDefaultValueNumber == PcdDefaultValueNumber)
637                   
638                         if InfDefaultValue == None:
639                             InfMatch = True
640                         else:
641                             InfDefaultValueNumber = int(InfDefaultValue.strip(), 0)
642                             InfMatch = (InfDefaultValueNumber == PcdDefaultValueNumber)
643                                 
644                         if DscDefaultValue == None:
645                             DscMatch = True
646                         else:
647                             DscDefaultValueNumber = int(DscDefaultValue.strip(), 0)
648                             DscMatch = (DscDefaultValueNumber == PcdDefaultValueNumber)
649                     else:
650                         if DecDefaultValue == None:
651                             DecMatch = True
652                         else:
653                             DecMatch = (DecDefaultValue == Pcd.DefaultValue)
654                   
655                         if InfDefaultValue == None:
656                             InfMatch = True
657                         else:
658                             InfMatch = (InfDefaultValue == Pcd.DefaultValue)
659                             
660                         if DscDefaultValue == None:
661                             DscMatch = True
662                         else:
663                             DscMatch = (DscDefaultValue == Pcd.DefaultValue)
664                     
665                     #
666                     # Report PCD item according to their override relationship
667                     #        
668                     if DecMatch and InfMatch:
669                         FileWrite(File, '    %-*s: %6s %10s = %-22s' % (self.MaxLen, Pcd.TokenCName, TypeName, '('+Pcd.DatumType+')', Pcd.DefaultValue))
670                     else:
671                         if DscMatch and DscModuleOverrideValue == None:
672                             if (Pcd.TokenCName, Key) in self.FdfPcdSet:
673                                 FileWrite(File, ' *F %-*s: %6s %10s = %-22s' % (self.MaxLen, Pcd.TokenCName, TypeName, '('+Pcd.DatumType+')', Pcd.DefaultValue))
674                             else:
675                                 FileWrite(File, ' *P %-*s: %6s %10s = %-22s' % (self.MaxLen, Pcd.TokenCName, TypeName, '('+Pcd.DatumType+')', Pcd.DefaultValue))
676                         else:
677                             FileWrite(File, ' *M %-*s: %6s %10s = %-22s' % (self.MaxLen, Pcd.TokenCName, TypeName, '('+Pcd.DatumType+')', Pcd.DefaultValue))
678                             if DscDefaultValue != None:
679                                 FileWrite(File, '    %*s = %s' % (self.MaxLen + 19, 'DSC DEFAULT', DscDefaultValue))
680                         
681                         if InfDefaultValue != None:
682                             FileWrite(File, '    %*s = %s' % (self.MaxLen + 19, 'INF DEFAULT', InfDefaultValue))
683                         
684                         if DecDefaultValue != None and not DecMatch:
685                             FileWrite(File, '    %*s = %s' % (self.MaxLen + 19, 'DEC DEFAULT', DecDefaultValue))
686
687                     if ModulePcdSet == None:
688                         for (ModuleDefault, ModulePath) in self.ModulePcdOverride.get((Pcd.TokenCName, Pcd.TokenSpaceGuidCName), []):
689                             if Pcd.DatumType in ('UINT8', 'UINT16', 'UINT32', 'UINT64'):
690                                 ModulePcdDefaultValueNumber = int(ModuleDefault.strip(), 0)
691                                 Match = (ModulePcdDefaultValueNumber == PcdDefaultValueNumber)
692                             else:
693                                 Match = (ModuleDefault == Pcd.DefaultValue)
694                             if Match:
695                                 continue
696                             FileWrite(File, ' *M %-*s = %s' % (self.MaxLen + 19, ModulePath, ModuleDefault))
697         
698         if ModulePcdSet == None:
699             FileWrite(File, gSectionEnd)
700         else:
701             FileWrite(File, gSubSectionEnd)     
702
703
704
705 ##
706 # Reports platform and module Prediction information
707 #
708 # This class reports the platform execution order prediction section and
709 # module load fixed address prediction subsection in the build report file.
710 #    
711 class PredictionReport(object):
712     ##
713     # Constructor function for class PredictionReport
714     #
715     # This constructor function generates PredictionReport object for the platform. 
716     #
717     # @param self:           The object pointer
718     # @param Wa              Workspace context information
719     #
720     def __init__(self, Wa):
721         self._MapFileName = os.path.join(Wa.BuildDir, Wa.Name + ".map")
722         self._MapFileParsed = False
723         self._EotToolInvoked = False
724         self._FvDir = Wa.FvDir
725         
726         self.FixedMapDict = {}
727         self.ItemList = []
728         self.MaxLen = 0
729        
730         self._SourceFileList = os.path.join(Wa.BuildDir, Wa.Name + "_SourceFileList.txt")
731         SourceList = open(self._SourceFileList, "w+")
732         
733         self._FfsEntryPoint = {}
734         
735         #
736         # Collect all platform reference source files and write to the an intermediate file
737         # for EOT tool to parse.
738         #
739         GuidMap = {}
740         for Pa in Wa.AutoGenObjectList:
741             for Module in Pa.LibraryAutoGenList + Pa.ModuleAutoGenList:
742                 #
743                 # Add module referenced source files
744                 #
745                 SourceList.write(str(Module) + "\n")
746                 IncludeList = {}
747                 for Source in Module.SourceFileList:
748                     if os.path.splitext(str(Source))[1].lower() == ".c":
749                         SourceList.write("  " + str(Source) + "\n")
750                         FindIncludeFiles(Source.Path, Module.IncludePathList, IncludeList)
751                 for IncludeFile in IncludeList.values():
752                     SourceList.write("  " + IncludeFile + "\n")
753  
754                 for Guid in Module.PpiList:
755                     GuidMap[Guid] = GuidStructureStringToGuidString(Module.PpiList[Guid])
756                 for Guid in Module.ProtocolList:
757                     GuidMap[Guid] = GuidStructureStringToGuidString(Module.ProtocolList[Guid])
758                 for Guid in Module.GuidList:
759                     GuidMap[Guid] = GuidStructureStringToGuidString(Module.GuidList[Guid])
760         
761                 if Module.Guid and not Module.IsLibrary:
762                     EntryPoint = " ".join(Module.Module.ModuleEntryPointList)
763                     if int(str(Module.AutoGenVersion), 0) >= 0x00010005:
764                         RealEntryPoint = "_ModuleEntryPoint"
765                     else:
766                         RealEntryPoint = EntryPoint
767                         if EntryPoint == "_ModuleEntryPoint":
768                             CCFlags = Module.BuildOption.get("CC", {}).get("FLAGS", "")
769                             Match = gGlueLibEntryPoint.search(CCFlags)
770                             if Match:
771                                 EntryPoint = Match.group(1)
772                                 
773                     self._FfsEntryPoint[Module.Guid.upper()] = (EntryPoint, RealEntryPoint)
774                          
775         SourceList.close()         
776         
777         #
778         # Write platform referenced GUID list as the input of EOT tool
779         # to calculate module dependency GUID 
780         #
781         self._GuidList = os.path.join(Wa.BuildDir, Wa.Name + "_GuidList.txt")
782         GuidList = open(self._GuidList, "w+")
783         for Guid in GuidMap:
784             GuidList.write("%s %s\n" % (Guid, GuidMap[Guid]))
785         GuidList.close()
786     
787         #
788         # Collect platform firmware volume list as the input of EOT.
789         #
790         self._FvList = [] 
791         for Fd in Wa.FdfProfile.FdDict:
792             for FdRegion in Wa.FdfProfile.FdDict[Fd].RegionList:
793                 if FdRegion.RegionType != "FV":
794                     continue
795                 for FvName in FdRegion.RegionDataList:
796                     if FvName in self._FvList:
797                         continue
798                     self._FvList.append(FvName)
799                     for Ffs in Wa.FdfProfile.FvDict[FvName.upper()].FfsList:
800                         for Section in Ffs.SectionList:
801                             try:
802                                 for FvSection in Section.SectionList:
803                                     if FvSection.FvName in self._FvList:
804                                         continue
805                                     self._FvList.append(FvSection.FvName)
806                             except AttributeError:
807                                 pass
808         
809
810         self._Dispatch = os.path.join(Wa.BuildDir, Wa.Name + "_Dispatch.log")
811
812     ##
813     # Parse platform fixed address map files
814     #
815     # This function parses the platform final fixed address map file to get
816     # the database of predicted fixed address for module image base, entry point
817     # etc. 
818     #
819     # @param self:           The object pointer
820     #  
821     def _ParseMapFile(self):
822         if self._MapFileParsed:
823             return
824         self._MapFileParsed = True
825         if os.path.isfile(self._MapFileName):
826             try:
827                 FileContents = open(self._MapFileName).read()
828                 for Match in gMapFileItemPattern.finditer(FileContents):
829                     AddressType = Match.group(1)
830                     BaseAddress = Match.group(2)
831                     EntryPoint = Match.group(3)
832                     Guid = Match.group(4).upper()
833                     List = self.FixedMapDict.setdefault(Guid, [])
834                     List.append((AddressType, BaseAddress, "*I"))
835                     List.append((AddressType, EntryPoint, "*E"))
836             except:
837                 EdkLogger.warn(None, "Cannot open file to read", self._MapFileName)
838     
839     ##
840     # Invokes EOT tool to get the predicted the execution order.
841     #
842     # This function invokes EOT tool to calculate the predicted dispatch order
843     #
844     # @param self:           The object pointer
845     #  
846     def _InvokeEotTool(self):
847         if self._EotToolInvoked:
848             return
849         
850         self._EotToolInvoked = True        
851         FvFileList = []
852         for FvName in self._FvList:
853             FvFile = os.path.join(self._FvDir, FvName + ".Fv")
854             if os.path.isfile(FvFile):
855                 FvFileList.append(FvFile)
856        
857         #
858         # Invoke EOT tool
859         #
860         Eot(CommandLineOption=False, SourceFileList=self._SourceFileList, GuidList=self._GuidList,
861             FvFileList=' '.join(FvFileList), Dispatch=self._Dispatch, IsInit=True)
862         
863         #
864         # Parse the output of EOT tool
865         #
866         for Line in open(self._Dispatch):
867             (Guid, Phase, FfsName, FilePath) = Line.split()
868             Symbol = self._FfsEntryPoint.get(Guid, [FfsName, ""])[0]
869             if len(Symbol) > self.MaxLen:
870                 self.MaxLen = len(Symbol)
871             self.ItemList.append((Phase, Symbol, FilePath))
872     
873     ##
874     # Generate platform execution order report
875     #
876     # This function generates the predicted module execution order.
877     #
878     # @param self            The object pointer
879     # @param File            The file object for report
880     #
881     def _GenerateExecutionOrderReport(self, File):
882         FileWrite(File, gSectionStart)
883         FileWrite(File, "Execution Order Prediction")
884         FileWrite(File, "*P PEI phase")
885         FileWrite(File, "*D DXE phase")
886         FileWrite(File, "*E Module INF entry point name")
887         FileWrite(File, "*N Module notification function name")
888         
889         FileWrite(File, "Type %-*s %s" % (self.MaxLen, "Symbol", "Module INF Path"))
890         FileWrite(File, gSectionSep)
891         for Item in self.ItemList:
892             FileWrite(File, "*%sE  %-*s %s" % (Item[0], self.MaxLen, Item[1], Item[2]))
893             
894         FileWrite(File, gSectionStart)
895     
896     ##
897     # Generate Fixed Address report.
898     #
899     # This function generate the predicted fixed address report for a module
900     # specified by Guid.
901     #
902     # @param self            The object pointer
903     # @param File            The file object for report
904     # @param Guid            The module Guid value.
905     # @param NotifyList      The list of all notify function in a module
906     #
907     def _GenerateFixedAddressReport(self, File, Guid, NotifyList):
908         FixedAddressList = self.FixedMapDict.get(Guid)
909         if not FixedAddressList:
910             return
911         
912         FileWrite(File, gSubSectionStart)
913         FileWrite(File, "Fixed Address Prediction")
914         FileWrite(File, "*I  Image Loading Address")
915         FileWrite(File, "*E  Entry Point Address")
916         FileWrite(File, "*N  Notification Function Address")
917         FileWrite(File, "*F  Flash Address")
918         FileWrite(File, "*M  Memory Address")
919         FileWrite(File, "*S  SMM RAM Address")
920         FileWrite(File, "TOM Top of Memory")
921         
922         FileWrite(File, "Type Address           Name")
923         FileWrite(File, gSubSectionSep)
924         for Item in FixedAddressList:
925             Type = Item[0]
926             Value = Item[1]
927             Symbol = Item[2]
928             if Symbol == "*I":
929                 Name = "(Image Base)"
930             elif Symbol == "*E":
931                 Name = self._FfsEntryPoint.get(Guid, ["", "_ModuleEntryPoint"])[1]
932             elif Symbol in NotifyList:
933                 Name = Symbol
934                 Symbol = "*N"
935             else:
936                 continue
937             
938             if "Flash" in Type:
939                 Symbol += "F"
940             elif "Memory" in Type:
941                 Symbol += "M"
942             else:
943                 Symbol += "S"
944             
945             if Value[0] == "-":
946                 Value = "TOM" + Value 
947
948             FileWrite(File, "%s  %-16s  %s" % (Symbol, Value, Name))
949
950     ##
951     # Generate report for the prediction part
952     #
953     # This function generate the predicted fixed address report for a module or
954     # predicted module execution order for a platform. 
955     # If the input Guid is None, then, it generates the predicted module execution order;
956     # otherwise it generated the module fixed loading address for the module specified by
957     # Guid.
958     #
959     # @param self            The object pointer
960     # @param File            The file object for report
961     # @param Guid            The module Guid value.
962     #
963     def GenerateReport(self, File, Guid):
964         self._ParseMapFile()
965         self._InvokeEotTool()
966         if Guid:
967             self._GenerateFixedAddressReport(File, Guid.upper(), [])
968         else:
969             self._GenerateExecutionOrderReport(File)
970
971 ##        
972 # Reports FD region information
973 #
974 # This class reports the FD subsection in the build report file.
975 # It collects region information of platform flash device. 
976 # If the region is a firmware volume, it lists the set of modules
977 # and its space information; otherwise, it only lists its region name,
978 # base address and size in its sub-section header.
979 # If there are nesting FVs, the nested FVs will list immediate after
980 # this FD region subsection
981 #
982 class FdRegionReport(object):
983     ##
984     # Discover all the nested FV name list.
985     #
986     # This is an internal worker function to discover the all the nested FV information
987     # in the parent firmware volume. It uses deep first search algorithm recursively to
988     # find all the FV list name and append them to the list.
989     #
990     # @param self            The object pointer
991     # @param FvName          The name of current firmware file system
992     # @param Wa              Workspace context information
993     #
994     def _DiscoverNestedFvList(self, FvName, Wa):
995         for Ffs in Wa.FdfProfile.FvDict[FvName.upper()].FfsList:
996             for Section in Ffs.SectionList:
997                 try:
998                     for FvSection in Section.SectionList:
999                         if FvSection.FvName in self.FvList:
1000                             continue
1001                         self._GuidsDb[Ffs.NameGuid.upper()] = FvSection.FvName
1002                         self.FvList.append(FvSection.FvName)
1003                         self.FvInfo[FvSection.FvName] = ("Nested FV", 0, 0)
1004                         self._DiscoverNestedFvList(FvSection.FvName, Wa)
1005                 except AttributeError:
1006                     pass
1007     
1008     ##
1009     # Constructor function for class FdRegionReport
1010     #
1011     # This constructor function generates FdRegionReport object for a specified FdRegion. 
1012     # If the FdRegion is a firmware volume, it will recursively find all its nested Firmware
1013     # volume list. This function also collects GUID map in order to dump module identification
1014     # in the final report.
1015     #
1016     # @param self:           The object pointer
1017     # @param FdRegion        The current FdRegion object
1018     # @param Wa              Workspace context information
1019     #
1020     def __init__(self, FdRegion, Wa):
1021         self.Type = FdRegion.RegionType
1022         self.BaseAddress = FdRegion.Offset
1023         self.Size = FdRegion.Size
1024         self.FvList = []
1025         self.FvInfo = {}
1026         self._GuidsDb = {}
1027         self._FvDir = Wa.FvDir
1028
1029         #
1030         # If the input FdRegion is not a firmware volume,
1031         # we are done. 
1032         #
1033         if self.Type != "FV":
1034             return
1035         
1036         #
1037         # Find all nested FVs in the FdRegion
1038         #
1039         for FvName in FdRegion.RegionDataList:
1040             if FvName in self.FvList:
1041                 continue
1042             self.FvList.append(FvName)
1043             self.FvInfo[FvName] = ("Fd Region", self.BaseAddress, self.Size)
1044             self._DiscoverNestedFvList(FvName, Wa)
1045
1046         PlatformPcds = {}
1047         for Pa in Wa.AutoGenObjectList:
1048             PackageList = []
1049             for ModuleKey in Pa.Platform.Modules:
1050                 #
1051                 # Collect PCD DEC default value.
1052                 #   
1053                 Module = Pa.Platform.Modules[ModuleKey]
1054                 for Package in Module.M.Module.Packages:
1055                     if Package not in PackageList:
1056                         PackageList.append(Package)
1057                 
1058             for Package in PackageList:
1059                 for (TokenCName, TokenSpaceGuidCName, DecType) in Package.Pcds:
1060                     DecDefaultValue = Package.Pcds[TokenCName, TokenSpaceGuidCName, DecType].DefaultValue
1061                     PlatformPcds[(TokenCName, TokenSpaceGuidCName)] = DecDefaultValue
1062         
1063         #
1064         # Collect PCDs defined in DSC common section
1065         #
1066         for Platform in Wa.BuildDatabase.WorkspaceDb.PlatformList:
1067             for (TokenCName, TokenSpaceGuidCName) in Platform.Pcds:
1068                 DscDefaultValue = Platform.Pcds[(TokenCName, TokenSpaceGuidCName)].DefaultValue
1069                 PlatformPcds[(TokenCName, TokenSpaceGuidCName)] = DscDefaultValue
1070         
1071         #
1072         # Add PEI and DXE a priori files GUIDs defined in PI specification.
1073         #
1074         self._GuidsDb["1B45CC0A-156A-428A-AF62-49864DA0E6E6"] = "PEI Apriori"
1075         self._GuidsDb["FC510EE7-FFDC-11D4-BD41-0080C73C8881"] = "DXE Apriori" 
1076         #
1077         # Add ACPI table storage file
1078         #
1079         self._GuidsDb["7E374E25-8E01-4FEE-87F2-390C23C606CD"] = "ACPI table storage"
1080         #
1081         # Collect the GUID map in the FV firmware volume
1082         #
1083         for FvName in self.FvList:
1084             for Ffs in Wa.FdfProfile.FvDict[FvName.upper()].FfsList:
1085                 try:
1086                     #
1087                     # Collect GUID, module mapping from INF file
1088                     #
1089                     InfFileName = Ffs.InfFileName
1090                     try:
1091                         FileGuid = ""
1092                         ModuleName = ""
1093                         InfPath = os.path.join(Wa.WorkspaceDir, InfFileName)
1094                         for Line in open(InfPath):
1095                             ItemList = Line.split("#")[0].split("=")
1096                             if len(ItemList) == 2:
1097                                 Key   = ItemList[0].strip().upper()
1098                                 Value = ItemList[1].strip()
1099                                 if Key == "FILE_GUID":
1100                                     FileGuid = Value.upper()
1101                                 if Key == "BASE_NAME":
1102                                     ModuleName = Value
1103                         if FileGuid:
1104                             self._GuidsDb[FileGuid] = "%s (%s)" % (ModuleName, InfPath)
1105                     except IOError:
1106                         EdkLogger.warn(None, "Cannot open file to read", InfPath)
1107                 except AttributeError:
1108                     try:
1109                         #
1110                         # collect GUID map for binary EFI file in FDF file.
1111                         #
1112                         Guid = Ffs.NameGuid.upper()
1113                         Match = gPcdGuidPattern.match(Ffs.NameGuid)
1114                         if Match:
1115                             PcdTokenspace = Match.group(1)
1116                             PcdToken = Match.group(2)
1117                             if (PcdToken, PcdTokenspace) in PlatformPcds:
1118                                 GuidValue = PlatformPcds[(PcdToken, PcdTokenspace)]
1119                                 Guid = GuidStructureByteArrayToGuidString(GuidValue).upper()
1120
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 or "ALL" in ReportType:
1363                 self.ReportType = ["PCD", "LIBRARY", "BUILD_FLAGS", "DEPEX", "FLASH", "PREDICTION"]
1364             else:
1365                 for ReportTypeItem in ReportType:
1366                     if ReportTypeItem not in self.ReportType:
1367                         self.ReportType.append(ReportTypeItem)
1368     
1369     ##
1370     # Adds platform report to the list
1371     #
1372     # This function adds a platform report to the final report list.
1373     #
1374     # @param self            The object pointer
1375     # @param Wa              Workspace context information
1376     #           
1377     def AddPlatformReport(self, Wa):
1378         if self.ReportFile:
1379             self.ReportList.append(PlatformReport(Wa, self.ReportType))
1380
1381     ##
1382     # Generates the final report.
1383     #
1384     # This function generates platform build report. It invokes GenerateReport()
1385     # method for every platform report in the list.
1386     #
1387     # @param self            The object pointer
1388     # @param BuildDuration   The total time to build the modules
1389     # 
1390     def GenerateReport(self, BuildDuration):
1391         if self.ReportFile:
1392             try:
1393                 File = open(self.ReportFile, "w+")
1394             except IOError:
1395                 EdkLogger.error(None, FILE_OPEN_FAILURE, ExtraData=self.ReportFile)
1396             try:
1397                 for Report in self.ReportList:
1398                     Report.GenerateReport(File, BuildDuration, self.ReportType) 
1399             except IOError:
1400                 EdkLogger.error(None, FILE_WRITE_FAILURE, ExtraData=self.ReportFile)
1401             File.close()
1402         
1403 # This acts like the main() function for the script, unless it is 'import'ed into another script.
1404 if __name__ == '__main__':
1405     pass
1406