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