Set the FdfPcdSet to be empty if the platform DSC does not has assosicated FDF file.
[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         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         
729         self.FixedMapDict = {}
730         self.ItemList = []
731         self.MaxLen = 0
732        
733         self._SourceFileList = os.path.join(Wa.BuildDir, Wa.Name + "_SourceFileList.txt")
734         SourceList = open(self._SourceFileList, "w+")
735         
736         self._FfsEntryPoint = {}
737         
738         #
739         # Collect all platform reference source files and write to the an intermediate file
740         # for EOT tool to parse.
741         #
742         GuidMap = {}
743         for Pa in Wa.AutoGenObjectList:
744             for Module in Pa.LibraryAutoGenList + Pa.ModuleAutoGenList:
745                 #
746                 # Add module referenced source files
747                 #
748                 SourceList.write(str(Module) + "\n")
749                 IncludeList = {}
750                 for Source in Module.SourceFileList:
751                     if os.path.splitext(str(Source))[1].lower() == ".c":
752                         SourceList.write("  " + str(Source) + "\n")
753                         FindIncludeFiles(Source.Path, Module.IncludePathList, IncludeList)
754                 for IncludeFile in IncludeList.values():
755                     SourceList.write("  " + IncludeFile + "\n")
756  
757                 for Guid in Module.PpiList:
758                     GuidMap[Guid] = GuidStructureStringToGuidString(Module.PpiList[Guid])
759                 for Guid in Module.ProtocolList:
760                     GuidMap[Guid] = GuidStructureStringToGuidString(Module.ProtocolList[Guid])
761                 for Guid in Module.GuidList:
762                     GuidMap[Guid] = GuidStructureStringToGuidString(Module.GuidList[Guid])
763         
764                 if Module.Guid and not Module.IsLibrary:
765                     EntryPoint = " ".join(Module.Module.ModuleEntryPointList)
766                     if int(str(Module.AutoGenVersion), 0) >= 0x00010005:
767                         RealEntryPoint = "_ModuleEntryPoint"
768                     else:
769                         RealEntryPoint = EntryPoint
770                         if EntryPoint == "_ModuleEntryPoint":
771                             CCFlags = Module.BuildOption.get("CC", {}).get("FLAGS", "")
772                             Match = gGlueLibEntryPoint.search(CCFlags)
773                             if Match:
774                                 EntryPoint = Match.group(1)
775                                 
776                     self._FfsEntryPoint[Module.Guid.upper()] = (EntryPoint, RealEntryPoint)
777                          
778         SourceList.close()         
779         
780         #
781         # Write platform referenced GUID list as the input of EOT tool
782         # to calculate module dependency GUID 
783         #
784         self._GuidList = os.path.join(Wa.BuildDir, Wa.Name + "_GuidList.txt")
785         GuidList = open(self._GuidList, "w+")
786         for Guid in GuidMap:
787             GuidList.write("%s %s\n" % (Guid, GuidMap[Guid]))
788         GuidList.close()
789     
790         #
791         # Collect platform firmware volume list as the input of EOT.
792         #
793         self._FvList = [] 
794         for Fd in Wa.FdfProfile.FdDict:
795             for FdRegion in Wa.FdfProfile.FdDict[Fd].RegionList:
796                 if FdRegion.RegionType != "FV":
797                     continue
798                 for FvName in FdRegion.RegionDataList:
799                     if FvName in self._FvList:
800                         continue
801                     self._FvList.append(FvName)
802                     for Ffs in Wa.FdfProfile.FvDict[FvName.upper()].FfsList:
803                         for Section in Ffs.SectionList:
804                             try:
805                                 for FvSection in Section.SectionList:
806                                     if FvSection.FvName in self._FvList:
807                                         continue
808                                     self._FvList.append(FvSection.FvName)
809                             except AttributeError:
810                                 pass
811         
812
813         self._Dispatch = os.path.join(Wa.BuildDir, Wa.Name + "_Dispatch.log")
814
815     ##
816     # Parse platform fixed address map files
817     #
818     # This function parses the platform final fixed address map file to get
819     # the database of predicted fixed address for module image base, entry point
820     # etc. 
821     #
822     # @param self:           The object pointer
823     #  
824     def _ParseMapFile(self):
825         if self._MapFileParsed:
826             return
827         self._MapFileParsed = True
828         if os.path.isfile(self._MapFileName):
829             try:
830                 FileContents = open(self._MapFileName).read()
831                 for Match in gMapFileItemPattern.finditer(FileContents):
832                     AddressType = Match.group(1)
833                     BaseAddress = Match.group(2)
834                     EntryPoint = Match.group(3)
835                     Guid = Match.group(4).upper()
836                     List = self.FixedMapDict.setdefault(Guid, [])
837                     List.append((AddressType, BaseAddress, "*I"))
838                     List.append((AddressType, EntryPoint, "*E"))
839             except:
840                 EdkLogger.warn(None, "Cannot open file to read", self._MapFileName)
841     
842     ##
843     # Invokes EOT tool to get the predicted the execution order.
844     #
845     # This function invokes EOT tool to calculate the predicted dispatch order
846     #
847     # @param self:           The object pointer
848     #  
849     def _InvokeEotTool(self):
850         if self._EotToolInvoked:
851             return
852         
853         self._EotToolInvoked = True        
854         FvFileList = []
855         for FvName in self._FvList:
856             FvFile = os.path.join(self._FvDir, FvName + ".Fv")
857             if os.path.isfile(FvFile):
858                 FvFileList.append(FvFile)
859        
860         #
861         # Invoke EOT tool
862         #
863         Eot(CommandLineOption=False, SourceFileList=self._SourceFileList, GuidList=self._GuidList,
864             FvFileList=' '.join(FvFileList), Dispatch=self._Dispatch, IsInit=True)
865         
866         #
867         # Parse the output of EOT tool
868         #
869         for Line in open(self._Dispatch):
870             (Guid, Phase, FfsName, FilePath) = Line.split()
871             Symbol = self._FfsEntryPoint.get(Guid, [FfsName, ""])[0]
872             if len(Symbol) > self.MaxLen:
873                 self.MaxLen = len(Symbol)
874             self.ItemList.append((Phase, Symbol, FilePath))
875     
876     ##
877     # Generate platform execution order report
878     #
879     # This function generates the predicted module execution order.
880     #
881     # @param self            The object pointer
882     # @param File            The file object for report
883     #
884     def _GenerateExecutionOrderReport(self, File):
885         FileWrite(File, gSectionStart)
886         FileWrite(File, "Execution Order Prediction")
887         FileWrite(File, "*P PEI phase")
888         FileWrite(File, "*D DXE phase")
889         FileWrite(File, "*E Module INF entry point name")
890         FileWrite(File, "*N Module notification function name")
891         
892         FileWrite(File, "Type %-*s %s" % (self.MaxLen, "Symbol", "Module INF Path"))
893         FileWrite(File, gSectionSep)
894         for Item in self.ItemList:
895             FileWrite(File, "*%sE  %-*s %s" % (Item[0], self.MaxLen, Item[1], Item[2]))
896             
897         FileWrite(File, gSectionStart)
898     
899     ##
900     # Generate Fixed Address report.
901     #
902     # This function generate the predicted fixed address report for a module
903     # specified by Guid.
904     #
905     # @param self            The object pointer
906     # @param File            The file object for report
907     # @param Guid            The module Guid value.
908     # @param NotifyList      The list of all notify function in a module
909     #
910     def _GenerateFixedAddressReport(self, File, Guid, NotifyList):
911         FixedAddressList = self.FixedMapDict.get(Guid)
912         if not FixedAddressList:
913             return
914         
915         FileWrite(File, gSubSectionStart)
916         FileWrite(File, "Fixed Address Prediction")
917         FileWrite(File, "*I  Image Loading Address")
918         FileWrite(File, "*E  Entry Point Address")
919         FileWrite(File, "*N  Notification Function Address")
920         FileWrite(File, "*F  Flash Address")
921         FileWrite(File, "*M  Memory Address")
922         FileWrite(File, "*S  SMM RAM Address")
923         FileWrite(File, "TOM Top of Memory")
924         
925         FileWrite(File, "Type Address           Name")
926         FileWrite(File, gSubSectionSep)
927         for Item in FixedAddressList:
928             Type = Item[0]
929             Value = Item[1]
930             Symbol = Item[2]
931             if Symbol == "*I":
932                 Name = "(Image Base)"
933             elif Symbol == "*E":
934                 Name = self._FfsEntryPoint.get(Guid, ["", "_ModuleEntryPoint"])[1]
935             elif Symbol in NotifyList:
936                 Name = Symbol
937                 Symbol = "*N"
938             else:
939                 continue
940             
941             if "Flash" in Type:
942                 Symbol += "F"
943             elif "Memory" in Type:
944                 Symbol += "M"
945             else:
946                 Symbol += "S"
947             
948             if Value[0] == "-":
949                 Value = "TOM" + Value 
950
951             FileWrite(File, "%s  %-16s  %s" % (Symbol, Value, Name))
952
953     ##
954     # Generate report for the prediction part
955     #
956     # This function generate the predicted fixed address report for a module or
957     # predicted module execution order for a platform. 
958     # If the input Guid is None, then, it generates the predicted module execution order;
959     # otherwise it generated the module fixed loading address for the module specified by
960     # Guid.
961     #
962     # @param self            The object pointer
963     # @param File            The file object for report
964     # @param Guid            The module Guid value.
965     #
966     def GenerateReport(self, File, Guid):
967         self._ParseMapFile()
968         self._InvokeEotTool()
969         if Guid:
970             self._GenerateFixedAddressReport(File, Guid.upper(), [])
971         else:
972             self._GenerateExecutionOrderReport(File)
973
974 ##        
975 # Reports FD region information
976 #
977 # This class reports the FD subsection in the build report file.
978 # It collects region information of platform flash device. 
979 # If the region is a firmware volume, it lists the set of modules
980 # and its space information; otherwise, it only lists its region name,
981 # base address and size in its sub-section header.
982 # If there are nesting FVs, the nested FVs will list immediate after
983 # this FD region subsection
984 #
985 class FdRegionReport(object):
986     ##
987     # Discover all the nested FV name list.
988     #
989     # This is an internal worker function to discover the all the nested FV information
990     # in the parent firmware volume. It uses deep first search algorithm recursively to
991     # find all the FV list name and append them to the list.
992     #
993     # @param self            The object pointer
994     # @param FvName          The name of current firmware file system
995     # @param Wa              Workspace context information
996     #
997     def _DiscoverNestedFvList(self, FvName, Wa):
998         for Ffs in Wa.FdfProfile.FvDict[FvName.upper()].FfsList:
999             for Section in Ffs.SectionList:
1000                 try:
1001                     for FvSection in Section.SectionList:
1002                         if FvSection.FvName in self.FvList:
1003                             continue
1004                         self._GuidsDb[Ffs.NameGuid.upper()] = FvSection.FvName
1005                         self.FvList.append(FvSection.FvName)
1006                         self.FvInfo[FvSection.FvName] = ("Nested FV", 0, 0)
1007                         self._DiscoverNestedFvList(FvSection.FvName, Wa)
1008                 except AttributeError:
1009                     pass
1010     
1011     ##
1012     # Constructor function for class FdRegionReport
1013     #
1014     # This constructor function generates FdRegionReport object for a specified FdRegion. 
1015     # If the FdRegion is a firmware volume, it will recursively find all its nested Firmware
1016     # volume list. This function also collects GUID map in order to dump module identification
1017     # in the final report.
1018     #
1019     # @param self:           The object pointer
1020     # @param FdRegion        The current FdRegion object
1021     # @param Wa              Workspace context information
1022     #
1023     def __init__(self, FdRegion, Wa):
1024         self.Type = FdRegion.RegionType
1025         self.BaseAddress = FdRegion.Offset
1026         self.Size = FdRegion.Size
1027         self.FvList = []
1028         self.FvInfo = {}
1029         self._GuidsDb = {}
1030         self._FvDir = Wa.FvDir
1031
1032         #
1033         # If the input FdRegion is not a firmware volume,
1034         # we are done. 
1035         #
1036         if self.Type != "FV":
1037             return
1038         
1039         #
1040         # Find all nested FVs in the FdRegion
1041         #
1042         for FvName in FdRegion.RegionDataList:
1043             if FvName in self.FvList:
1044                 continue
1045             self.FvList.append(FvName)
1046             self.FvInfo[FvName] = ("Fd Region", self.BaseAddress, self.Size)
1047             self._DiscoverNestedFvList(FvName, Wa)
1048
1049         PlatformPcds = {}
1050         for Pa in Wa.AutoGenObjectList:
1051             PackageList = []
1052             for ModuleKey in Pa.Platform.Modules:
1053                 #
1054                 # Collect PCD DEC default value.
1055                 #   
1056                 Module = Pa.Platform.Modules[ModuleKey]
1057                 for Package in Module.M.Module.Packages:
1058                     if Package not in PackageList:
1059                         PackageList.append(Package)
1060                 
1061             for Package in PackageList:
1062                 for (TokenCName, TokenSpaceGuidCName, DecType) in Package.Pcds:
1063                     DecDefaultValue = Package.Pcds[TokenCName, TokenSpaceGuidCName, DecType].DefaultValue
1064                     PlatformPcds[(TokenCName, TokenSpaceGuidCName)] = DecDefaultValue
1065         
1066         #
1067         # Collect PCDs defined in DSC common section
1068         #
1069         for Platform in Wa.BuildDatabase.WorkspaceDb.PlatformList:
1070             for (TokenCName, TokenSpaceGuidCName) in Platform.Pcds:
1071                 DscDefaultValue = Platform.Pcds[(TokenCName, TokenSpaceGuidCName)].DefaultValue
1072                 PlatformPcds[(TokenCName, TokenSpaceGuidCName)] = DscDefaultValue
1073         
1074         #
1075         # Add PEI and DXE a priori files GUIDs defined in PI specification.
1076         #
1077         self._GuidsDb["1B45CC0A-156A-428A-AF62-49864DA0E6E6"] = "PEI Apriori"
1078         self._GuidsDb["FC510EE7-FFDC-11D4-BD41-0080C73C8881"] = "DXE Apriori" 
1079         #
1080         # Add ACPI table storage file
1081         #
1082         self._GuidsDb["7E374E25-8E01-4FEE-87F2-390C23C606CD"] = "ACPI table storage"
1083         #
1084         # Collect the GUID map in the FV firmware volume
1085         #
1086         for FvName in self.FvList:
1087             for Ffs in Wa.FdfProfile.FvDict[FvName.upper()].FfsList:
1088                 try:
1089                     #
1090                     # Collect GUID, module mapping from INF file
1091                     #
1092                     InfFileName = Ffs.InfFileName
1093                     try:
1094                         FileGuid = ""
1095                         ModuleName = ""
1096                         InfPath = os.path.join(Wa.WorkspaceDir, InfFileName)
1097                         for Line in open(InfPath):
1098                             ItemList = Line.split("#")[0].split("=")
1099                             if len(ItemList) == 2:
1100                                 Key   = ItemList[0].strip().upper()
1101                                 Value = ItemList[1].strip()
1102                                 if Key == "FILE_GUID":
1103                                     FileGuid = Value.upper()
1104                                 if Key == "BASE_NAME":
1105                                     ModuleName = Value
1106                         if FileGuid:
1107                             self._GuidsDb[FileGuid] = "%s (%s)" % (ModuleName, InfPath)
1108                     except IOError:
1109                         EdkLogger.warn(None, "Cannot open file to read", InfPath)
1110                 except AttributeError:
1111                     try:
1112                         #
1113                         # collect GUID map for binary EFI file in FDF file.
1114                         #
1115                         Guid = Ffs.NameGuid.upper()
1116                         Match = gPcdGuidPattern.match(Ffs.NameGuid)
1117                         if Match:
1118                             PcdTokenspace = Match.group(1)
1119                             PcdToken = Match.group(2)
1120                             if (PcdToken, PcdTokenspace) in PlatformPcds:
1121                                 GuidValue = PlatformPcds[(PcdToken, PcdTokenspace)]
1122                                 Guid = GuidStructureByteArrayToGuidString(GuidValue).upper()
1123
1124                         for Section in Ffs.SectionList:
1125                             try:
1126                                 ModuleSectFile = os.path.join(Wa.WorkspaceDir, Section.SectFileName)
1127                                 self._GuidsDb[Guid] = ModuleSectFile
1128                             except AttributeError:
1129                                 pass
1130                     except AttributeError:
1131                         pass
1132         
1133     
1134     ##
1135     # Internal worker function to generate report for the FD region
1136     #
1137     # This internal worker function to generate report for the FD region.
1138     # It the type is firmware volume, it lists offset and module identification. 
1139     #
1140     # @param self            The object pointer
1141     # @param File            The file object for report
1142     # @param Title           The title for the FD subsection 
1143     # @param BaseAddress     The base address for the FD region
1144     # @param Size            The size of the FD region
1145     # @param FvName          The FV name if the FD region is a firmware volume
1146     #
1147     def _GenerateReport(self, File, Title, Type, BaseAddress, Size=0, FvName=None):
1148         FileWrite(File, gSubSectionStart)
1149         FileWrite(File, Title)
1150         FileWrite(File, "Type:               %s" % Type)
1151         FileWrite(File, "Base Address:       0x%X" % BaseAddress)
1152         
1153         if self.Type == "FV":
1154             FvTotalSize = 0
1155             FvTakenSize = 0
1156             FvFreeSize  = 0
1157             FvReportFileName = os.path.join(self._FvDir, FvName + ".fv.txt")
1158             try:
1159                 #
1160                 # Collect size info in the firmware volume.
1161                 #
1162                 FvReport = open(FvReportFileName).read()
1163                 Match = gFvTotalSizePattern.search(FvReport)
1164                 if Match:
1165                     FvTotalSize = int(Match.group(1), 16)
1166                 Match = gFvTakenSizePattern.search(FvReport)
1167                 if Match:
1168                     FvTakenSize = int(Match.group(1), 16)
1169                 FvFreeSize = FvTotalSize - FvTakenSize 
1170                 #
1171                 # Write size information to the report file.
1172                 #
1173                 FileWrite(File, "Size:               0x%X (%.0fK)" % (FvTotalSize, FvTotalSize / 1024.0))
1174                 FileWrite(File, "Fv Name:            %s (%.1f%% Full)" % (FvName, FvTakenSize * 100.0 / FvTotalSize))
1175                 FileWrite(File, "Occupied Size:      0x%X (%.0fK)" % (FvTakenSize, FvTakenSize / 1024.0))
1176                 FileWrite(File, "Free Size:          0x%X (%.0fK)" % (FvFreeSize, FvFreeSize / 1024.0))
1177                 FileWrite(File, "Offset     Module")
1178                 FileWrite(File, gSubSectionSep)
1179                 #
1180                 # Write module offset and module identification to the report file.
1181                 #
1182                 for Match in gOffsetGuidPattern.finditer(FvReport):
1183                     Guid = Match.group(2).upper()
1184                     Offset = int(Match.group(1), 16)
1185                     FileWrite (File, "0x%07X %s" % (Offset, self._GuidsDb.get(Guid, Guid)))
1186             except IOError:
1187                 EdkLogger.warn(None, "Fail to read report file", FvReportFileName)
1188         else:
1189             FileWrite(File, "Size:               0x%X (%.0fK)" % (Size, Size / 1024.0))
1190         FileWrite(File, gSubSectionEnd)
1191
1192     ##
1193     # Generate report for the FD region
1194     #
1195     # This function generates report for the FD region. 
1196     #
1197     # @param self            The object pointer
1198     # @param File            The file object for report
1199     #
1200     def GenerateReport(self, File):
1201         if (len(self.FvList) > 0):
1202             for FvItem in self.FvList:
1203                 Info = self.FvInfo[FvItem]
1204                 self._GenerateReport(File, Info[0], "FV", Info[1], Info[2], FvItem)
1205         else:
1206             self._GenerateReport(File, "FD Region", self.Type, self.BaseAddress, self.Size)    
1207             
1208 ##
1209 # Reports FD information
1210 #
1211 # This class reports the FD section in the build report file.
1212 # It collects flash device information for a platform. 
1213 #
1214 class FdReport(object):
1215     ##
1216     # Constructor function for class FdReport
1217     #
1218     # This constructor function generates FdReport object for a specified
1219     # firmware device. 
1220     #
1221     # @param self            The object pointer
1222     # @param Fd              The current Firmware device object
1223     # @param Wa              Workspace context information
1224     #
1225     def __init__(self, Fd, Wa):
1226         self.FdName = Fd.FdUiName
1227         self.BaseAddress = Fd.BaseAddress
1228         self.Size = Fd.Size
1229         self.FdRegionList = [FdRegionReport(FdRegion, Wa) for FdRegion in Fd.RegionList]
1230
1231     ##
1232     # Generate report for the firmware device.
1233     #
1234     # This function generates report for the firmware device. 
1235     #
1236     # @param self            The object pointer
1237     # @param File            The file object for report
1238     #
1239     def GenerateReport(self, File):
1240         FileWrite(File, gSectionStart)
1241         FileWrite(File, "Firmware Device (FD)")
1242         FileWrite(File, "FD Name:            %s" % self.FdName)
1243         FileWrite(File, "Base Address:       0x%s" % self.BaseAddress)
1244         FileWrite(File, "Size:               0x%X (%.0fK)" % (self.Size, self.Size / 1024.0))
1245         if len(self.FdRegionList) > 0:
1246             FileWrite(File, gSectionSep)
1247             for FdRegionItem in self.FdRegionList:
1248                 FdRegionItem.GenerateReport(File)
1249         
1250         FileWrite(File, gSectionEnd)
1251
1252
1253     
1254 ##
1255 # Reports platform information
1256 #
1257 # This class reports the whole platform information 
1258 #     
1259 class PlatformReport(object):
1260     ##
1261     # Constructor function for class PlatformReport
1262     #
1263     # This constructor function generates PlatformReport object a platform build.
1264     # It generates report for platform summary, flash, global PCDs and detailed
1265     # module information for modules involved in platform build.
1266     #
1267     # @param self            The object pointer
1268     # @param Wa              Workspace context information
1269     #
1270     def __init__(self, Wa, ReportType):
1271         self._WorkspaceDir = Wa.WorkspaceDir
1272         self.PlatformName = Wa.Name
1273         self.PlatformDscPath = Wa.Platform
1274         self.Architectures = " ".join(Wa.ArchList)
1275         self.ToolChain = Wa.ToolChain
1276         self.Target = Wa.BuildTarget
1277         self.OutputPath = os.path.join(Wa.WorkspaceDir, Wa.OutputDir)
1278         self.BuildEnvironment = platform.platform()
1279         
1280         self.PcdReport = None
1281         if "PCD" in ReportType:
1282             self.PcdReport = PcdReport(Wa)
1283
1284         self.FdReportList = []
1285         if "FLASH" in ReportType and Wa.FdfProfile:
1286             for Fd in Wa.FdfProfile.FdDict:
1287                 self.FdReportList.append(FdReport(Wa.FdfProfile.FdDict[Fd], Wa))
1288         
1289         self.PredictionReport = None        
1290         if "PREDICTION" in ReportType:
1291             self.PredictionReport = PredictionReport(Wa)
1292         
1293         self.ModuleReportList = []
1294         for Pa in Wa.AutoGenObjectList:
1295             for ModuleKey in Pa.Platform.Modules:
1296                 for Platform in Wa.BuildDatabase.WorkspaceDb.PlatformList:
1297                     if ModuleKey in Platform.Modules:
1298                         DscOverridePcds = Platform.Modules[ModuleKey].Pcds
1299                         break
1300                 else:
1301                     DscOverridePcds = {}
1302                 self.ModuleReportList.append(ModuleReport(Pa.Platform.Modules[ModuleKey].M, DscOverridePcds, ReportType))
1303  
1304
1305         
1306     ##
1307     # Generate report for the whole platform.
1308     #
1309     # This function generates report for platform information.
1310     # It comprises of platform summary, global PCD, flash and 
1311     # module list sections.
1312     #
1313     # @param self            The object pointer
1314     # @param File            The file object for report
1315     # @param BuildDuration   The total time to build the modules
1316     # @param ReportType      The kind of report items in the final report file
1317     #
1318     def GenerateReport(self, File, BuildDuration, ReportType):
1319         FileWrite(File, "Platform Summary")
1320         FileWrite(File, "Platform Name:        %s" % self.PlatformName)
1321         FileWrite(File, "Platform DSC Path:    %s" % self.PlatformDscPath)
1322         FileWrite(File, "Platform DSC Path:    %s" % self.Architectures)
1323         FileWrite(File, "Tool Chain:           %s" % self.ToolChain)
1324         FileWrite(File, "Target:               %s" % self.Target)
1325         FileWrite(File, "Output Path:          %s" % self.OutputPath)
1326         FileWrite(File, "Build Environment:    %s" % self.BuildEnvironment)
1327         FileWrite(File, "Build Duration:       %s" % BuildDuration)
1328         FileWrite(File, "Report Content:       %s" % ", ".join(ReportType))
1329  
1330         if "PCD" in ReportType:
1331             self.PcdReport.GenerateReport(File, None, {})
1332             
1333         if "FLASH" in ReportType:
1334             for FdReportListItem in self.FdReportList:
1335                 FdReportListItem.GenerateReport(File)
1336         
1337         for ModuleReportItem in self.ModuleReportList:
1338             ModuleReportItem.GenerateReport(File, self.PcdReport, self.PredictionReport, ReportType)
1339         
1340         if "PREDICTION" in ReportType:
1341             self.PredictionReport.GenerateReport(File, None)
1342
1343 ## BuildReport class
1344 #
1345 #  This base class contain the routines to collect data and then
1346 #  applies certain format to the output report 
1347 #
1348 class BuildReport(object):
1349     ##
1350     # Constructor function for class BuildReport
1351     #
1352     # This constructor function generates BuildReport object a platform build.
1353     # It generates report for platform summary, flash, global PCDs and detailed
1354     # module information for modules involved in platform build.
1355     #
1356     # @param self            The object pointer
1357     # @param ReportFile      The file name to save report file
1358     # @param ReportType      The kind of report items in the final report file
1359     #
1360     def __init__(self, ReportFile, ReportType):
1361         self.ReportFile = ReportFile
1362         if ReportFile:
1363             self.ReportList = []
1364             self.ReportType = []
1365             if ReportType == None or "ALL" in ReportType:
1366                 self.ReportType = ["PCD", "LIBRARY", "BUILD_FLAGS", "DEPEX", "FLASH", "PREDICTION"]
1367             else:
1368                 for ReportTypeItem in ReportType:
1369                     if ReportTypeItem not in self.ReportType:
1370                         self.ReportType.append(ReportTypeItem)
1371     
1372     ##
1373     # Adds platform report to the list
1374     #
1375     # This function adds a platform report to the final report list.
1376     #
1377     # @param self            The object pointer
1378     # @param Wa              Workspace context information
1379     #           
1380     def AddPlatformReport(self, Wa):
1381         if self.ReportFile:
1382             self.ReportList.append(PlatformReport(Wa, self.ReportType))
1383
1384     ##
1385     # Generates the final report.
1386     #
1387     # This function generates platform build report. It invokes GenerateReport()
1388     # method for every platform report in the list.
1389     #
1390     # @param self            The object pointer
1391     # @param BuildDuration   The total time to build the modules
1392     # 
1393     def GenerateReport(self, BuildDuration):
1394         if self.ReportFile:
1395             try:
1396                 File = open(self.ReportFile, "w+")
1397             except IOError:
1398                 EdkLogger.error(None, FILE_OPEN_FAILURE, ExtraData=self.ReportFile)
1399             try:
1400                 for Report in self.ReportList:
1401                     Report.GenerateReport(File, BuildDuration, self.ReportType) 
1402             except IOError:
1403                 EdkLogger.error(None, FILE_WRITE_FAILURE, ExtraData=self.ReportFile)
1404             File.close()
1405         
1406 # This acts like the main() function for the script, unless it is 'import'ed into another script.
1407 if __name__ == '__main__':
1408     pass
1409