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