c1645415a7364c735d9e62ad56e5da7244e4062d
[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 + "\r\n")
113
114 ##
115 # Find all the header file that the module source directly includes.
116 #
117 # This function scans source code to find all header files the module may
118 # include. This is not accurate but very effective to find all the header
119 # file the module might include with #include statement. 
120 #
121 # @Source                    The source file name
122 # @IncludePathList           The list of include path to find the source file.
123 # @IncludeFiles              The dictionary of current found include files.
124 #
125 def FindIncludeFiles(Source, IncludePathList, IncludeFiles):
126     FileContents = open(Source).read()
127     #
128     # Find header files with pattern #include "XXX.h" or #include <XXX.h>
129     #
130     for Match in gIncludePattern.finditer(FileContents):
131         FileName = Match.group(1).strip()
132         for Dir in [os.path.dirname(Source)] + IncludePathList:
133             FullFileName = os.path.normpath(os.path.join(Dir, FileName))
134             if os.path.exists(FullFileName):
135                 IncludeFiles[FullFileName.lower().replace("\\", "/")] = FullFileName
136                 break
137     
138     #
139     # Find header files with pattern like #include EFI_PPI_CONSUMER(XXX)
140     #
141     for Match in gIncludePattern2.finditer(FileContents):
142         Key = Match.group(2)
143         Type = Match.group(1)
144         if "ARCH_PROTOCOL" in Type:
145             FileName = "ArchProtocol/%(Key)s/%(Key)s.h" % {"Key" : Key}
146         elif "PROTOCOL" in Type:
147             FileName = "Protocol/%(Key)s/%(Key)s.h" % {"Key" : Key}
148         elif "PPI" in Type:
149             FileName = "Ppi/%(Key)s/%(Key)s.h" % {"Key" : Key}
150         elif "GUID" in Type:
151             FileName = "Guid/%(Key)s/%(Key)s.h" % {"Key" : Key}
152         else:
153             continue
154         for Dir in IncludePathList:
155             FullFileName = os.path.normpath(os.path.join(Dir, FileName))
156             if os.path.exists(FullFileName):
157                 IncludeFiles[FullFileName.lower().replace("\\", "/")] = FullFileName
158                 break
159             
160 ##
161 # Reports library information
162 #
163 # This class reports the module library subsection in the build report file.
164 #     
165 class LibraryReport(object):
166     ##
167     # Constructor function for class LibraryReport
168     #
169     # This constructor function generates LibraryReport object for 
170     # a module.
171     #
172     # @param self            The object pointer
173     # @param M               Module context information
174     #
175     def __init__(self, M):
176         self.LibraryList = []
177         if int(str(M.AutoGenVersion), 0) >= 0x00010005:
178             self._EdkIIModule = True
179         else:
180             self._EdkIIModule = False
181                
182         for Lib in M.DependentLibraryList:
183             LibInfPath = str(Lib)
184             LibClassList = Lib.LibraryClass[0].LibraryClass
185             LibConstructorList = Lib.ConstructorList
186             LibDesstructorList = Lib.DestructorList
187             LibDepexList = Lib.DepexExpression[M.Arch, M.ModuleType]
188             self.LibraryList.append((LibInfPath, LibClassList, LibConstructorList, LibDesstructorList, LibDepexList))
189     
190     ##
191     # Generate report for module library information
192     #
193     # This function generates report for the module library.
194     # If the module is EDKII style one, the additional library class, library
195     # constructor/destructor and dependency expression may also be reported.  
196     #
197     # @param self            The object pointer
198     # @param File            The file object for report
199     #
200     def GenerateReport(self, File):
201         FileWrite(File, gSubSectionStart)
202         FileWrite(File, "Library")
203         if len(self.LibraryList) > 0:
204             FileWrite(File, gSubSectionSep)
205             for LibraryItem in self.LibraryList:
206                 LibInfPath = LibraryItem[0]
207                 FileWrite(File, LibInfPath)
208                 
209                 #
210                 # Report library class, library constructor and destructor for
211                 # EDKII style module.
212                 #
213                 if self._EdkIIModule:
214                     LibClass = LibraryItem[1]
215                     EdkIILibInfo = ""
216                     LibConstructor = " ".join(LibraryItem[2])
217                     if LibConstructor:
218                         EdkIILibInfo += " C = " + LibConstructor
219                     LibDestructor = " ".join(LibraryItem[3])
220                     if LibDestructor:
221                         EdkIILibInfo += " D = " + LibConstructor
222                     LibDepex = " ".join(LibraryItem[3])
223                     if LibDepex:
224                         EdkIILibInfo += " Depex = " + LibDepex
225                     if EdkIILibInfo:
226                         FileWrite(File, "{%s: %s}" % (LibClass, EdkIILibInfo))
227                     else:
228                         FileWrite(File, "{%s}" % LibClass)
229         
230         FileWrite(File, gSubSectionEnd)
231
232 ##
233 # Reports dependency expression information
234 #
235 # This class reports the module dependency expression subsection in the build report file.
236 #             
237 class DepexReport(object):
238     ##
239     # Constructor function for class DepexReport
240     #
241     # This constructor function generates DepexReport object for 
242     # a module. If the module source contains the DXS file (usually EDK
243     # style module), it uses the dependency in DXS file; otherwise,
244     # it uses the dependency expression from its own INF [Depex] section
245     # and then merges with the ones from its dependent library INF.
246     #
247     # @param self            The object pointer
248     # @param M               Module context information
249     #
250     def __init__(self, M):
251         for Source in M.SourceFileList:
252             if os.path.splitext(Source.Path)[1].lower() == ".dxs":
253                 Match = gDxsDependencyPattern.search(open(Source.Path).read())
254                 if Match:
255                     self.Depex = Match.group(1).strip()
256                     self.Source = "DXS"
257                     break
258         else:
259             self.Depex = M.DepexExpressionList[M.ModuleType]
260             self.ModuleDepex = " ".join(M.Module.DepexExpression[M.Arch, M.ModuleType])
261             if not self.ModuleDepex:
262                 self.ModuleDepex = "TRUE"
263             
264             LibDepexList = []
265             for Lib in M.DependentLibraryList:
266                 LibDepex = " ".join(Lib.DepexExpression[M.Arch, M.ModuleType]).strip()
267                 if LibDepex != "":                         
268                     if " " in LibDepex:
269                         LibDepex = "(" + LibDepex + ")"
270                     LibDepexList.append(LibDepex)
271             self.LibraryDepex = " AND ".join(LibDepexList)
272             if not self.LibraryDepex:
273                 self.LibraryDepex = "(None)"
274             self.Source = "INF"
275     
276     ##
277     # Generate report for module dependency expression information
278     #
279     # This function generates report for the module dependency expression.
280     #
281     # @param self            The object pointer
282     # @param File            The file object for report
283     #
284     def GenerateReport(self, File):
285         FileWrite(File, gSubSectionStart)
286         FileWrite(File, "Dependency Expression (DEPEX) from %s" % self.Source)
287         
288         if self.Source == "INF":
289             FileWrite(File, "%s" % self.Depex, True)
290             FileWrite(File, gSubSectionSep)
291             FileWrite(File, "From Module INF:  %s" % self.ModuleDepex, True)
292             FileWrite(File, "From Library INF: %s" % self.LibraryDepex, True)
293         else:
294             FileWrite(File, "%s" % self.Depex)
295         FileWrite(File, gSubSectionEnd)
296
297 ##
298 # Reports dependency expression information
299 #
300 # This class reports the module build flags subsection in the build report file.
301 #                     
302 class BuildFlagsReport(object):
303     ##
304     # Constructor function for class BuildFlagsReport
305     #
306     # This constructor function generates BuildFlagsReport object for 
307     # a module. It reports the build tool chain tag and all relevant 
308     # build flags to build the module.
309     #
310     # @param self            The object pointer
311     # @param M               Module context information
312     #
313     def __init__(self, M):
314         BuildOptions = {}
315         #
316         # Add build flags according to source file extension so that
317         # irrelevant ones can be filtered out. 
318         #
319         for Source in M.SourceFileList:
320             Ext = os.path.splitext(Source.File)[1].lower()
321             if Ext in [".c", ".cc", ".cpp"]:
322                 BuildOptions["CC"] = 1
323             elif Ext in [".s", ".asm"]:
324                 BuildOptions["PP"] = 1
325                 BuildOptions["ASM"] = 1
326             elif Ext in [".vfr"]:
327                 BuildOptions["VFRPP"] = 1
328                 BuildOptions["VFR"] = 1
329             elif Ext in [".dxs"]:
330                 BuildOptions["APP"] = 1
331                 BuildOptions["CC"] = 1
332             elif Ext in [".asl"]:
333                 BuildOptions["ASLPP"] = 1
334                 BuildOptions["ASL"] = 1
335             elif Ext in [".aslc"]:
336                 BuildOptions["ASLCC"] = 1
337                 BuildOptions["ASLDLINK"] = 1
338                 BuildOptions["CC"] = 1
339             elif Ext in [".asm16"]:
340                 BuildOptions["ASMLINK"] = 1
341             BuildOptions["SLINK"] = 1
342             BuildOptions["DLINK"] = 1
343         
344         #
345         # Save module build flags.
346         #
347         self.ToolChainTag = M.ToolChain
348         self.BuildFlags = {}
349         for Tool in BuildOptions:
350             self.BuildFlags[Tool + "_FLAGS"] = M.BuildOption.get(Tool, {}).get("FLAGS", "")
351
352     ##
353     # Generate report for module build flags information
354     #
355     # This function generates report for the module build flags expression.
356     #
357     # @param self            The object pointer
358     # @param File            The file object for report
359     #
360     def GenerateReport(self, File):
361         FileWrite(File, gSubSectionStart)
362         FileWrite(File, "Build Flags")
363         FileWrite(File, "Tool Chain Tag: %s" % self.ToolChainTag)
364         for Tool in self.BuildFlags:
365             FileWrite(File, gSubSectionSep)
366             FileWrite(File, "%s = %s" % (Tool, self.BuildFlags[Tool]), True)
367         
368         FileWrite(File, gSubSectionEnd)
369
370
371 ##
372 # Reports individual module information
373 #
374 # This class reports the module section in the build report file. 
375 # It comprises of module summary, module PCD, library, dependency expression,
376 # build flags sections.  
377 #        
378 class ModuleReport(object):
379     ##
380     # Constructor function for class ModuleReport
381     #
382     # This constructor function generates ModuleReport object for 
383     # a separate module in a platform build.  
384     #
385     # @param self            The object pointer
386     # @param M               Module context information
387     # @param DscOverridePcds Module DSC override PCD information
388     # @param ReportType      The kind of report items in the final report file
389     #
390     def __init__(self, M, DscOverridePcds, ReportType):
391         self.ModuleName = M.Module.BaseName
392         self.ModuleInfPath = M.MetaFile.File
393         self.FileGuid = M.Guid
394         self.Size = 0
395         self.BuildTimeStamp = None
396         self.DriverType = ""
397         ModuleType = M.ModuleType
398         if not ModuleType:
399             ModuleType = gComponentType2ModuleType.get(M.ComponentType, "")
400         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         if Wa.FdfProfile:
526             self.FdfPcdSet = Wa.FdfProfile.PcdDict
527         else:
528             self.FdfPcdSet = {}
529
530         self.DecPcdDefault = {}
531         self.ModulePcdOverride = {}
532         for Pa in Wa.AutoGenObjectList:
533             #
534             # Collect all platform referenced PCDs and grouped them by PCD token space
535             # GUID C Names
536             #
537             for Pcd in Pa.AllPcdList:
538                 PcdList = self.AllPcds.setdefault(Pcd.TokenSpaceGuidCName, {}).setdefault(Pcd.Type, [])
539                 if Pcd not in PcdList:
540                     PcdList.append(Pcd)
541                 if len(Pcd.TokenCName) > self.MaxLen:
542                     self.MaxLen = len(Pcd.TokenCName)
543             
544             for ModuleKey in Pa.Platform.Modules:
545                 #
546                 # Collect PCD DEC default value.
547                 #   
548                 Module = Pa.Platform.Modules[ModuleKey]
549                 for Package in Module.M.Module.Packages:
550                     for (TokenCName, TokenSpaceGuidCName, DecType) in Package.Pcds:
551                         DecDefaultValue = Package.Pcds[TokenCName, TokenSpaceGuidCName, DecType].DefaultValue
552                         self.DecPcdDefault.setdefault((TokenCName, TokenSpaceGuidCName, DecType), DecDefaultValue)
553                 #
554                 # Collect module override PCDs
555                 #
556                 for ModulePcd in Module.M.ModulePcdList + Module.M.LibraryPcdList:
557                     TokenCName = ModulePcd.TokenCName
558                     TokenSpaceGuid = ModulePcd.TokenSpaceGuidCName
559                     ModuleDefault = ModulePcd.DefaultValue
560                     ModulePath = os.path.basename(Module.M.MetaFile.File)
561                     self.ModulePcdOverride.setdefault((TokenCName, TokenSpaceGuid), []).append((ModuleDefault, ModulePath))
562
563         #
564         # Collect PCDs defined in DSC common section
565         #
566         self.DscPcdDefault = {}
567         for Platform in Wa.BuildDatabase.WorkspaceDb.PlatformList:
568             for (TokenCName, TokenSpaceGuidCName) in Platform.Pcds:
569                 DscDefaultValue = Platform.Pcds[(TokenCName, TokenSpaceGuidCName)].DefaultValue
570                 self.DscPcdDefault[(TokenCName, TokenSpaceGuidCName)] = DscDefaultValue
571     
572     ##
573     # Generate report for PCD information
574     #
575     # This function generates report for separate module expression
576     # in a platform build.
577     #
578     # @param self            The object pointer
579     # @param File            The file object for report
580     # @param ModulePcdSet    Set of all PCDs referenced by module or None for
581     #                        platform PCD report
582     # @param DscOverridePcds Module DSC override PCDs set
583     #
584     def GenerateReport(self, File, ModulePcdSet, DscOverridePcds):
585         if ModulePcdSet == None:
586             #
587             # For platform global PCD section
588             #
589             FileWrite(File, gSectionStart)
590             FileWrite(File, "Platform Configuration Database Report")
591             FileWrite(File, "  *P  - Platform scoped PCD override in DSC file")
592             FileWrite(File, "  *F  - Platform scoped PCD override in FDF file")
593             FileWrite(File, "  *M  - Module scoped PCD override in DSC file")
594             FileWrite(File, gSectionSep)
595         else:
596             #
597             # For module PCD sub-section
598             #
599             FileWrite(File, gSubSectionStart)
600             FileWrite(File, "PCD")
601             FileWrite(File, gSubSectionSep)
602
603         for Key in self.AllPcds:
604             #
605             # Group PCD by their token space GUID C Name
606             #
607             First = True
608             for Type in self.AllPcds[Key]:
609                 #
610                 # Group PCD by their usage type
611                 #
612                 TypeName, DecType = gPcdTypeMap.get(Type, ("", Type))
613                 for Pcd in self.AllPcds[Key][Type]:
614                     #
615                     # Get PCD default value and their override relationship
616                     #
617                     InfDefaultValue = None
618                     if ModulePcdSet != None:
619                         if (Pcd.TokenCName, Pcd.TokenSpaceGuidCName, Type) not in ModulePcdSet:
620                             continue
621                         InfDefault = ModulePcdSet[Pcd.TokenCName, Pcd.TokenSpaceGuidCName, Type]
622                         if InfDefault == "":
623                             InfDefault = None
624                     if First:
625                         if ModulePcdSet == None:
626                             FileWrite(File, "")
627                         FileWrite(File, Key)
628                         First = False
629                     DecDefaultValue = self.DecPcdDefault.get((Pcd.TokenCName, Pcd.TokenSpaceGuidCName, DecType))
630                     DscDefaultValue = self.DscPcdDefault.get((Pcd.TokenCName, Pcd.TokenSpaceGuidCName))
631                     DscModuleOverrideValue = DscOverridePcds.get((Pcd.TokenCName, Pcd.TokenSpaceGuidCName))
632                           
633                     if Pcd.DatumType in ('UINT8', 'UINT16', 'UINT32', 'UINT64'):
634                         PcdDefaultValueNumber = int(Pcd.DefaultValue.strip(), 0)
635                         if DecDefaultValue == None:
636                             DecMatch = True
637                         else:
638                             DecDefaultValueNumber = int(DecDefaultValue.strip(), 0)
639                             DecMatch = (DecDefaultValueNumber == PcdDefaultValueNumber)
640                   
641                         if InfDefaultValue == None:
642                             InfMatch = True
643                         else:
644                             InfDefaultValueNumber = int(InfDefaultValue.strip(), 0)
645                             InfMatch = (InfDefaultValueNumber == PcdDefaultValueNumber)
646                                 
647                         if DscDefaultValue == None:
648                             DscMatch = True
649                         else:
650                             DscDefaultValueNumber = int(DscDefaultValue.strip(), 0)
651                             DscMatch = (DscDefaultValueNumber == PcdDefaultValueNumber)
652                     else:
653                         if DecDefaultValue == None:
654                             DecMatch = True
655                         else:
656                             DecMatch = (DecDefaultValue == Pcd.DefaultValue)
657                   
658                         if InfDefaultValue == None:
659                             InfMatch = True
660                         else:
661                             InfMatch = (InfDefaultValue == Pcd.DefaultValue)
662                             
663                         if DscDefaultValue == None:
664                             DscMatch = True
665                         else:
666                             DscMatch = (DscDefaultValue == Pcd.DefaultValue)
667                     
668                     #
669                     # Report PCD item according to their override relationship
670                     #        
671                     if DecMatch and InfMatch:
672                         FileWrite(File, '    %-*s: %6s %10s = %-22s' % (self.MaxLen, Pcd.TokenCName, TypeName, '('+Pcd.DatumType+')', Pcd.DefaultValue))
673                     else:
674                         if DscMatch and DscModuleOverrideValue == None:
675                             if (Pcd.TokenCName, Key) in self.FdfPcdSet:
676                                 FileWrite(File, ' *F %-*s: %6s %10s = %-22s' % (self.MaxLen, Pcd.TokenCName, TypeName, '('+Pcd.DatumType+')', Pcd.DefaultValue))
677                             else:
678                                 FileWrite(File, ' *P %-*s: %6s %10s = %-22s' % (self.MaxLen, Pcd.TokenCName, TypeName, '('+Pcd.DatumType+')', Pcd.DefaultValue))
679                         else:
680                             FileWrite(File, ' *M %-*s: %6s %10s = %-22s' % (self.MaxLen, Pcd.TokenCName, TypeName, '('+Pcd.DatumType+')', Pcd.DefaultValue))
681                             if DscDefaultValue != None:
682                                 FileWrite(File, '    %*s = %s' % (self.MaxLen + 19, 'DSC DEFAULT', DscDefaultValue))
683                         
684                         if InfDefaultValue != None:
685                             FileWrite(File, '    %*s = %s' % (self.MaxLen + 19, 'INF DEFAULT', InfDefaultValue))
686                         
687                         if DecDefaultValue != None and not DecMatch:
688                             FileWrite(File, '    %*s = %s' % (self.MaxLen + 19, 'DEC DEFAULT', DecDefaultValue))
689
690                     if ModulePcdSet == None:
691                         for (ModuleDefault, ModulePath) in self.ModulePcdOverride.get((Pcd.TokenCName, Pcd.TokenSpaceGuidCName), []):
692                             if Pcd.DatumType in ('UINT8', 'UINT16', 'UINT32', 'UINT64'):
693                                 ModulePcdDefaultValueNumber = int(ModuleDefault.strip(), 0)
694                                 Match = (ModulePcdDefaultValueNumber == PcdDefaultValueNumber)
695                             else:
696                                 Match = (ModuleDefault == Pcd.DefaultValue)
697                             if Match:
698                                 continue
699                             FileWrite(File, ' *M %-*s = %s' % (self.MaxLen + 19, ModulePath, ModuleDefault))
700         
701         if ModulePcdSet == None:
702             FileWrite(File, gSectionEnd)
703         else:
704             FileWrite(File, gSubSectionEnd)     
705
706
707
708 ##
709 # Reports platform and module Prediction information
710 #
711 # This class reports the platform execution order prediction section and
712 # module load fixed address prediction subsection in the build report file.
713 #    
714 class PredictionReport(object):
715     ##
716     # Constructor function for class PredictionReport
717     #
718     # This constructor function generates PredictionReport object for the platform. 
719     #
720     # @param self:           The object pointer
721     # @param Wa              Workspace context information
722     #
723     def __init__(self, Wa):
724         self._MapFileName = os.path.join(Wa.BuildDir, Wa.Name + ".map")
725         self._MapFileParsed = False
726         self._EotToolInvoked = False
727         self._FvDir = Wa.FvDir
728         self._EotDir = Wa.BuildDir
729         self._FfsEntryPoint = {}
730         self._GuidMap = {}
731         self._SourceList = []
732         self.FixedMapDict = {}
733         self.ItemList = []
734         self.MaxLen = 0
735        
736         #
737         # Collect all platform reference source files and GUID C Name
738         #
739         for Pa in Wa.AutoGenObjectList:
740             for Module in Pa.LibraryAutoGenList + Pa.ModuleAutoGenList:
741                 #
742                 # Add module referenced source files
743                 #
744                 self._SourceList.append(str(Module))
745                 IncludeList = {}
746                 for Source in Module.SourceFileList:
747                     if os.path.splitext(str(Source))[1].lower() == ".c":
748                         self._SourceList.append("  " + str(Source))
749                         FindIncludeFiles(Source.Path, Module.IncludePathList, IncludeList)
750                 for IncludeFile in IncludeList.values():
751                     self._SourceList.append("  " + IncludeFile)
752  
753                 for Guid in Module.PpiList:
754                     self._GuidMap[Guid] = GuidStructureStringToGuidString(Module.PpiList[Guid])
755                 for Guid in Module.ProtocolList:
756                     self._GuidMap[Guid] = GuidStructureStringToGuidString(Module.ProtocolList[Guid])
757                 for Guid in Module.GuidList:
758                     self._GuidMap[Guid] = GuidStructureStringToGuidString(Module.GuidList[Guid])
759         
760                 if Module.Guid and not Module.IsLibrary:
761                     EntryPoint = " ".join(Module.Module.ModuleEntryPointList)
762                     if int(str(Module.AutoGenVersion), 0) >= 0x00010005:
763                         RealEntryPoint = "_ModuleEntryPoint"
764                     else:
765                         RealEntryPoint = EntryPoint
766                         if EntryPoint == "_ModuleEntryPoint":
767                             CCFlags = Module.BuildOption.get("CC", {}).get("FLAGS", "")
768                             Match = gGlueLibEntryPoint.search(CCFlags)
769                             if Match:
770                                 EntryPoint = Match.group(1)
771                                 
772                     self._FfsEntryPoint[Module.Guid.upper()] = (EntryPoint, RealEntryPoint)
773                          
774   
775         #
776         # Collect platform firmware volume list as the input of EOT.
777         #
778         self._FvList = []
779         if Wa.FdfProfile: 
780             for Fd in Wa.FdfProfile.FdDict:
781                 for FdRegion in Wa.FdfProfile.FdDict[Fd].RegionList:
782                     if FdRegion.RegionType != "FV":
783                         continue
784                     for FvName in FdRegion.RegionDataList:
785                         if FvName in self._FvList:
786                             continue
787                         self._FvList.append(FvName)
788                         for Ffs in Wa.FdfProfile.FvDict[FvName.upper()].FfsList:
789                             for Section in Ffs.SectionList:
790                                 try:
791                                     for FvSection in Section.SectionList:
792                                         if FvSection.FvName in self._FvList:
793                                             continue
794                                         self._FvList.append(FvSection.FvName)
795                                 except AttributeError:
796                                     pass
797         
798
799     ##
800     # Parse platform fixed address map files
801     #
802     # This function parses the platform final fixed address map file to get
803     # the database of predicted fixed address for module image base, entry point
804     # etc. 
805     #
806     # @param self:           The object pointer
807     #  
808     def _ParseMapFile(self):
809         if self._MapFileParsed:
810             return
811         self._MapFileParsed = True
812         if os.path.isfile(self._MapFileName):
813             try:
814                 FileContents = open(self._MapFileName).read()
815                 for Match in gMapFileItemPattern.finditer(FileContents):
816                     AddressType = Match.group(1)
817                     BaseAddress = Match.group(2)
818                     EntryPoint = Match.group(3)
819                     Guid = Match.group(4).upper()
820                     List = self.FixedMapDict.setdefault(Guid, [])
821                     List.append((AddressType, BaseAddress, "*I"))
822                     List.append((AddressType, EntryPoint, "*E"))
823             except:
824                 EdkLogger.warn(None, "Cannot open file to read", self._MapFileName)
825     
826     ##
827     # Invokes EOT tool to get the predicted the execution order.
828     #
829     # This function invokes EOT tool to calculate the predicted dispatch order
830     #
831     # @param self:           The object pointer
832     #  
833     def _InvokeEotTool(self):
834         if self._EotToolInvoked:
835             return
836         
837         self._EotToolInvoked = True        
838         FvFileList = []
839         for FvName in self._FvList:
840             FvFile = os.path.join(self._FvDir, FvName + ".Fv")
841             if os.path.isfile(FvFile):
842                 FvFileList.append(FvFile)
843        
844         #
845         # Write source file list and GUID file list to an intermediate file
846         # as the input for EOT tool and dispatch List as the output file
847         # from EOT tool.
848         #
849         SourceList = os.path.join(self._EotDir, "SourceFile.txt")
850         GuidList = os.path.join(self._EotDir, "GuidList.txt")
851         DispatchList = os.path.join(self._EotDir, "Dispatch.txt")
852         
853         TempFile = open(SourceList, "w+")
854         for Item in self._SourceList:
855             FileWrite(TempFile, Item)
856         TempFile.close()
857         TempFile = open(GuidList, "w+")
858         for Key in self._GuidMap:
859             FileWrite(TempFile, "%s %s" % (Key, self._GuidMap[Key]))
860         TempFile.close()
861         
862         try:
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