Fix UEFI_SPECIFICATION_VERSION display defect.
[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[M.ModuleType]
260             self.ModuleDepex = " ".join(M.Module.DepexExpression[M.Arch, M.ModuleType])
261             if not self.ModuleDepex:
262                 self.ModuleDepex = "TRUE"
263
264             LibDepexList = []
265             for Lib in M.DependentLibraryList:
266                 LibDepex = " ".join(Lib.DepexExpression[M.Arch, M.ModuleType]).strip()
267                 if LibDepex != "":
268                     if " " in LibDepex:
269                         LibDepex = "(" + LibDepex + ")"
270                     LibDepexList.append(LibDepex)
271             self.LibraryDepex = " AND ".join(LibDepexList)
272             if not self.LibraryDepex:
273                 self.LibraryDepex = "(None)"
274             self.Source = "INF"
275
276     ##
277     # Generate report for module dependency expression information
278     #
279     # This function generates report for the module dependency expression.
280     #
281     # @param self            The object pointer
282     # @param File            The file object for report
283     #
284     def GenerateReport(self, File):
285         FileWrite(File, gSubSectionStart)
286         FileWrite(File, "Dependency Expression (DEPEX) from %s" % self.Source)
287
288         if self.Source == "INF":
289             FileWrite(File, "%s" % self.Depex, True)
290             FileWrite(File, gSubSectionSep)
291             FileWrite(File, "From Module INF:  %s" % self.ModuleDepex, True)
292             FileWrite(File, "From Library INF: %s" % self.LibraryDepex, True)
293         else:
294             FileWrite(File, "%s" % self.Depex)
295         FileWrite(File, gSubSectionEnd)
296
297 ##
298 # Reports dependency expression information
299 #
300 # This class reports the module build flags subsection in the build report file.
301 #
302 class BuildFlagsReport(object):
303     ##
304     # Constructor function for class BuildFlagsReport
305     #
306     # This constructor function generates BuildFlagsReport object for
307     # a module. It reports the build tool chain tag and all relevant
308     # build flags to build the module.
309     #
310     # @param self            The object pointer
311     # @param M               Module context information
312     #
313     def __init__(self, M):
314         BuildOptions = {}
315         #
316         # Add build flags according to source file extension so that
317         # irrelevant ones can be filtered out.
318         #
319         for Source in M.SourceFileList:
320             Ext = os.path.splitext(Source.File)[1].lower()
321             if Ext in [".c", ".cc", ".cpp"]:
322                 BuildOptions["CC"] = 1
323             elif Ext in [".s", ".asm"]:
324                 BuildOptions["PP"] = 1
325                 BuildOptions["ASM"] = 1
326             elif Ext in [".vfr"]:
327                 BuildOptions["VFRPP"] = 1
328                 BuildOptions["VFR"] = 1
329             elif Ext in [".dxs"]:
330                 BuildOptions["APP"] = 1
331                 BuildOptions["CC"] = 1
332             elif Ext in [".asl"]:
333                 BuildOptions["ASLPP"] = 1
334                 BuildOptions["ASL"] = 1
335             elif Ext in [".aslc"]:
336                 BuildOptions["ASLCC"] = 1
337                 BuildOptions["ASLDLINK"] = 1
338                 BuildOptions["CC"] = 1
339             elif Ext in [".asm16"]:
340                 BuildOptions["ASMLINK"] = 1
341             BuildOptions["SLINK"] = 1
342             BuildOptions["DLINK"] = 1
343
344         #
345         # Save module build flags.
346         #
347         self.ToolChainTag = M.ToolChain
348         self.BuildFlags = {}
349         for Tool in BuildOptions:
350             self.BuildFlags[Tool + "_FLAGS"] = M.BuildOption.get(Tool, {}).get("FLAGS", "")
351
352     ##
353     # Generate report for module build flags information
354     #
355     # This function generates report for the module build flags expression.
356     #
357     # @param self            The object pointer
358     # @param File            The file object for report
359     #
360     def GenerateReport(self, File):
361         FileWrite(File, gSubSectionStart)
362         FileWrite(File, "Build Flags")
363         FileWrite(File, "Tool Chain Tag: %s" % self.ToolChainTag)
364         for Tool in self.BuildFlags:
365             FileWrite(File, gSubSectionSep)
366             FileWrite(File, "%s = %s" % (Tool, self.BuildFlags[Tool]), True)
367
368         FileWrite(File, gSubSectionEnd)
369
370
371 ##
372 # Reports individual module information
373 #
374 # This class reports the module section in the build report file.
375 # It comprises of module summary, module PCD, library, dependency expression,
376 # build flags sections.
377 #
378 class ModuleReport(object):
379     ##
380     # Constructor function for class ModuleReport
381     #
382     # This constructor function generates ModuleReport object for
383     # a separate module in a platform build.
384     #
385     # @param self            The object pointer
386     # @param M               Module context information
387     # @param DscOverridePcds Module DSC override PCD information
388     # @param ReportType      The kind of report items in the final report file
389     #
390     def __init__(self, M, DscOverridePcds, ReportType):
391         self.ModuleName = M.Module.BaseName
392         self.ModuleInfPath = M.MetaFile.File
393         self.FileGuid = M.Guid
394         self.Size = 0
395         self.BuildTimeStamp = None
396         self.DriverType = ""
397         ModuleType = M.ModuleType
398         if not ModuleType:
399             ModuleType = gComponentType2ModuleType.get(M.ComponentType, "")
400         #
401         # If a module complies to PI 1.1, promote Module type to "SMM_DRIVER"
402         #
403         if ModuleType == "DXE_SMM_DRIVER":
404             PiSpec =  M.Module.Specification.get("PI_SPECIFICATION_VERSION", "0x00010000")
405             if int(PiSpec, 0) >= 0x0001000A:
406                 ModuleType = "SMM_DRIVER"
407         self.DriverType = gDriverTypeMap.get(ModuleType, "")
408         self.UefiSpecVersion = M.Module.Specification.get("UEFI_SPECIFICATION_VERSION", "")
409         self.PiSpecVersion = M.Module.Specification.get("PI_SPECIFICATION_VERSION", "")
410         self.PciDeviceId = M.Module.Defines.get("PCI_DEVICE_ID", "")
411         self.PciVendorId = M.Module.Defines.get("PCI_VENDOR_ID", "")
412         self.PciClassCode = M.Module.Defines.get("PCI_CLASS_CODE", "")
413
414         self._BuildDir = M.BuildDir
415         self.ModulePcdSet = {}
416         self.ModuleDscOverridePcds = {}
417         if "PCD" in ReportType:
418             #
419             # Collect all module used PCD set: module INF referenced directly or indirectly.
420             # It also saves module INF default values of them in case they exist.
421             #
422             for Pcd in M.ModulePcdList + M.LibraryPcdList:
423                 self.ModulePcdSet.setdefault((Pcd.TokenCName, Pcd.TokenSpaceGuidCName, Pcd.Type), Pcd.InfDefaultValue)
424
425             #
426             # Collect module DSC override PCD set for report
427             #
428             for (PcdTokenCName, PcdTokenSpaceGuidCName) in DscOverridePcds:
429                 Pcd = DscOverridePcds[(PcdTokenCName, PcdTokenSpaceGuidCName)]
430                 self.ModuleDscOverridePcds.setdefault((PcdTokenCName, PcdTokenSpaceGuidCName), Pcd.DefaultValue)
431
432         self.LibraryReport = None
433         if "LIBRARY" in ReportType:
434             self.LibraryReport = LibraryReport(M)
435
436         self.DepexReport = None
437         if "DEPEX" in ReportType:
438             self.DepexReport = DepexReport(M)
439
440         if "BUILD_FLAGS" in ReportType:
441             self.BuildFlagsReport = BuildFlagsReport(M)
442
443
444     ##
445     # Generate report for module information
446     #
447     # This function generates report for separate module expression
448     # in a platform build.
449     #
450     # @param self            The object pointer
451     # @param File            The file object for report
452     # @param GlobalPcdReport The platform global PCD class object
453     # @param ReportType      The kind of report items in the final report file
454     #
455     def GenerateReport(self, File, GlobalPcdReport, GlobalPredictionReport, ReportType):
456         FileWrite(File, gSectionStart)
457
458         FwReportFileName = os.path.join(self._BuildDir, "DEBUG", self.ModuleName + ".txt")
459         if os.path.isfile(FwReportFileName):
460             try:
461                 FileContents = open(FwReportFileName).read()
462                 Match = gModuleSizePattern.search(FileContents)
463                 if Match:
464                     self.Size = int(Match.group(1))
465
466                 Match = gTimeStampPattern.search(FileContents)
467                 if Match:
468                     self.BuildTimeStamp = datetime.fromtimestamp(int(Match.group(1)))
469             except IOError:
470                 EdkLogger.warn(None, "Fail to read report file", FwReportFileName)
471
472         FileWrite(File, "Module Summary")
473         FileWrite(File, "Module Name:          %s" % self.ModuleName)
474         FileWrite(File, "Module INF Path:      %s" % self.ModuleInfPath)
475         FileWrite(File, "File GUID:            %s" % self.FileGuid)
476         if self.Size:
477             FileWrite(File, "Size:                 0x%X (%.2fK)" % (self.Size, self.Size / 1024.0))
478         if self.BuildTimeStamp:
479             FileWrite(File, "Build Time Stamp:     %s" % self.BuildTimeStamp)
480         if self.DriverType:
481             FileWrite(File, "Driver Type:          %s" % self.DriverType)
482         if self.UefiSpecVersion:
483             FileWrite(File, "UEFI Spec Version:    %s" % self.UefiSpecVersion)
484         if self.PiSpecVersion:
485             FileWrite(File, "PI Spec Version:      %s" % self.PiSpecVersion)
486         if self.PciDeviceId:
487             FileWrite(File, "PCI Device ID:        %s" % self.PciDeviceId)
488         if self.PciVendorId:
489             FileWrite(File, "PCI Vendor ID:        %s" % self.PciVendorId)
490         if self.PciClassCode:
491             FileWrite(File, "PCI Class Code:       %s" % self.PciClassCode)
492
493         FileWrite(File, gSectionSep)
494
495         if "PCD" in ReportType:
496             GlobalPcdReport.GenerateReport(File, self.ModulePcdSet, self.ModuleDscOverridePcds)
497
498         if "LIBRARY" in ReportType:
499             self.LibraryReport.GenerateReport(File)
500
501         if "DEPEX" in ReportType:
502             self.DepexReport.GenerateReport(File)
503
504         if "BUILD_FLAGS" in ReportType:
505             self.BuildFlagsReport.GenerateReport(File)
506
507         if "PREDICTION" in ReportType:
508             GlobalPredictionReport.GenerateReport(File, self.FileGuid)
509
510         FileWrite(File, gSectionEnd)
511
512 ##
513 # Reports platform and module PCD information
514 #
515 # This class reports the platform PCD section and module PCD subsection
516 # in the build report file.
517 #
518 class PcdReport(object):
519     ##
520     # Constructor function for class PcdReport
521     #
522     # This constructor function generates PcdReport object a platform build.
523     # It collects the whole PCD database from platform DSC files, platform
524     # flash description file and package DEC files.
525     #
526     # @param self            The object pointer
527     # @param Wa              Workspace context information
528     #
529     def __init__(self, Wa):
530         self.AllPcds = {}
531         self.MaxLen = 0
532         if Wa.FdfProfile:
533             self.FdfPcdSet = Wa.FdfProfile.PcdDict
534         else:
535             self.FdfPcdSet = {}
536
537         self.DecPcdDefault = {}
538         self.ModulePcdOverride = {}
539         for Pa in Wa.AutoGenObjectList:
540             #
541             # Collect all platform referenced PCDs and grouped them by PCD token space
542             # GUID C Names
543             #
544             for Pcd in Pa.AllPcdList:
545                 PcdList = self.AllPcds.setdefault(Pcd.TokenSpaceGuidCName, {}).setdefault(Pcd.Type, [])
546                 if Pcd not in PcdList:
547                     PcdList.append(Pcd)
548                 if len(Pcd.TokenCName) > self.MaxLen:
549                     self.MaxLen = len(Pcd.TokenCName)
550
551             for ModuleKey in Pa.Platform.Modules:
552                 #
553                 # Collect PCD DEC default value.
554                 #
555                 Module = Pa.Platform.Modules[ModuleKey]
556                 for Package in Module.M.Module.Packages:
557                     for (TokenCName, TokenSpaceGuidCName, DecType) in Package.Pcds:
558                         DecDefaultValue = Package.Pcds[TokenCName, TokenSpaceGuidCName, DecType].DefaultValue
559                         self.DecPcdDefault.setdefault((TokenCName, TokenSpaceGuidCName, DecType), DecDefaultValue)
560                 #
561                 # Collect module override PCDs
562                 #
563                 for ModulePcd in Module.M.ModulePcdList + Module.M.LibraryPcdList:
564                     TokenCName = ModulePcd.TokenCName
565                     TokenSpaceGuid = ModulePcd.TokenSpaceGuidCName
566                     ModuleDefault = ModulePcd.DefaultValue
567                     ModulePath = os.path.basename(Module.M.MetaFile.File)
568                     self.ModulePcdOverride.setdefault((TokenCName, TokenSpaceGuid), []).append((ModuleDefault, ModulePath))
569
570         #
571         # Collect PCDs defined in DSC common section
572         #
573         self.DscPcdDefault = {}
574         for Platform in Wa.BuildDatabase.WorkspaceDb.PlatformList:
575             for (TokenCName, TokenSpaceGuidCName) in Platform.Pcds:
576                 DscDefaultValue = Platform.Pcds[(TokenCName, TokenSpaceGuidCName)].DefaultValue
577                 self.DscPcdDefault[(TokenCName, TokenSpaceGuidCName)] = DscDefaultValue
578
579     ##
580     # Generate report for PCD information
581     #
582     # This function generates report for separate module expression
583     # in a platform build.
584     #
585     # @param self            The object pointer
586     # @param File            The file object for report
587     # @param ModulePcdSet    Set of all PCDs referenced by module or None for
588     #                        platform PCD report
589     # @param DscOverridePcds Module DSC override PCDs set
590     #
591     def GenerateReport(self, File, ModulePcdSet, DscOverridePcds):
592         if ModulePcdSet == None:
593             #
594             # For platform global PCD section
595             #
596             FileWrite(File, gSectionStart)
597             FileWrite(File, "Platform Configuration Database Report")
598             FileWrite(File, "  *P  - Platform scoped PCD override in DSC file")
599             FileWrite(File, "  *F  - Platform scoped PCD override in FDF file")
600             FileWrite(File, "  *M  - Module scoped PCD override in DSC file")
601             FileWrite(File, gSectionSep)
602         else:
603             #
604             # For module PCD sub-section
605             #
606             FileWrite(File, gSubSectionStart)
607             FileWrite(File, "PCD")
608             FileWrite(File, gSubSectionSep)
609
610         for Key in self.AllPcds:
611             #
612             # Group PCD by their token space GUID C Name
613             #
614             First = True
615             for Type in self.AllPcds[Key]:
616                 #
617                 # Group PCD by their usage type
618                 #
619                 TypeName, DecType = gPcdTypeMap.get(Type, ("", Type))
620                 for Pcd in self.AllPcds[Key][Type]:
621                     #
622                     # Get PCD default value and their override relationship
623                     #
624                     InfDefaultValue = None
625                     if ModulePcdSet != None:
626                         if (Pcd.TokenCName, Pcd.TokenSpaceGuidCName, Type) not in ModulePcdSet:
627                             continue
628                         InfDefault = ModulePcdSet[Pcd.TokenCName, Pcd.TokenSpaceGuidCName, Type]
629                         if InfDefault == "":
630                             InfDefault = None
631                     if First:
632                         if ModulePcdSet == None:
633                             FileWrite(File, "")
634                         FileWrite(File, Key)
635                         First = False
636                     DecDefaultValue = self.DecPcdDefault.get((Pcd.TokenCName, Pcd.TokenSpaceGuidCName, DecType))
637                     DscDefaultValue = self.DscPcdDefault.get((Pcd.TokenCName, Pcd.TokenSpaceGuidCName))
638                     DscModuleOverrideValue = DscOverridePcds.get((Pcd.TokenCName, Pcd.TokenSpaceGuidCName))
639
640                     if Pcd.DatumType in ('UINT8', 'UINT16', 'UINT32', 'UINT64'):
641                         PcdDefaultValueNumber = int(Pcd.DefaultValue.strip(), 0)
642                         if DecDefaultValue == None:
643                             DecMatch = True
644                         else:
645                             DecDefaultValueNumber = int(DecDefaultValue.strip(), 0)
646                             DecMatch = (DecDefaultValueNumber == PcdDefaultValueNumber)
647
648                         if InfDefaultValue == None:
649                             InfMatch = True
650                         else:
651                             InfDefaultValueNumber = int(InfDefaultValue.strip(), 0)
652                             InfMatch = (InfDefaultValueNumber == PcdDefaultValueNumber)
653
654                         if DscDefaultValue == None:
655                             DscMatch = True
656                         else:
657                             DscDefaultValueNumber = int(DscDefaultValue.strip(), 0)
658                             DscMatch = (DscDefaultValueNumber == PcdDefaultValueNumber)
659                     else:
660                         if DecDefaultValue == None:
661                             DecMatch = True
662                         else:
663                             DecMatch = (DecDefaultValue == Pcd.DefaultValue)
664
665                         if InfDefaultValue == None:
666                             InfMatch = True
667                         else:
668                             InfMatch = (InfDefaultValue == Pcd.DefaultValue)
669
670                         if DscDefaultValue == None:
671                             DscMatch = True
672                         else:
673                             DscMatch = (DscDefaultValue == Pcd.DefaultValue)
674
675                     #
676                     # Report PCD item according to their override relationship
677                     #
678                     if DecMatch and InfMatch:
679                         FileWrite(File, '    %-*s: %6s %10s = %-22s' % (self.MaxLen, Pcd.TokenCName, TypeName, '('+Pcd.DatumType+')', Pcd.DefaultValue))
680                     else:
681                         if DscMatch and DscModuleOverrideValue == None:
682                             if (Pcd.TokenCName, Key) in self.FdfPcdSet:
683                                 FileWrite(File, ' *F %-*s: %6s %10s = %-22s' % (self.MaxLen, Pcd.TokenCName, TypeName, '('+Pcd.DatumType+')', Pcd.DefaultValue))
684                             else:
685                                 FileWrite(File, ' *P %-*s: %6s %10s = %-22s' % (self.MaxLen, Pcd.TokenCName, TypeName, '('+Pcd.DatumType+')', Pcd.DefaultValue))
686                         else:
687                             FileWrite(File, ' *M %-*s: %6s %10s = %-22s' % (self.MaxLen, Pcd.TokenCName, TypeName, '('+Pcd.DatumType+')', Pcd.DefaultValue))
688                             if DscDefaultValue != None:
689                                 FileWrite(File, '    %*s = %s' % (self.MaxLen + 19, 'DSC DEFAULT', DscDefaultValue))
690
691                         if InfDefaultValue != None:
692                             FileWrite(File, '    %*s = %s' % (self.MaxLen + 19, 'INF DEFAULT', InfDefaultValue))
693
694                         if DecDefaultValue != None and not DecMatch:
695                             FileWrite(File, '    %*s = %s' % (self.MaxLen + 19, 'DEC DEFAULT', DecDefaultValue))
696
697                     if ModulePcdSet == None:
698                         for (ModuleDefault, ModulePath) in self.ModulePcdOverride.get((Pcd.TokenCName, Pcd.TokenSpaceGuidCName), []):
699                             if Pcd.DatumType in ('UINT8', 'UINT16', 'UINT32', 'UINT64'):
700                                 ModulePcdDefaultValueNumber = int(ModuleDefault.strip(), 0)
701                                 Match = (ModulePcdDefaultValueNumber == PcdDefaultValueNumber)
702                             else:
703                                 Match = (ModuleDefault == Pcd.DefaultValue)
704                             if Match:
705                                 continue
706                             FileWrite(File, ' *M %-*s = %s' % (self.MaxLen + 19, ModulePath, ModuleDefault))
707
708         if ModulePcdSet == None:
709             FileWrite(File, gSectionEnd)
710         else:
711             FileWrite(File, gSubSectionEnd)
712
713
714
715 ##
716 # Reports platform and module Prediction information
717 #
718 # This class reports the platform execution order prediction section and
719 # module load fixed address prediction subsection in the build report file.
720 #
721 class PredictionReport(object):
722     ##
723     # Constructor function for class PredictionReport
724     #
725     # This constructor function generates PredictionReport object for the platform.
726     #
727     # @param self:           The object pointer
728     # @param Wa              Workspace context information
729     #
730     def __init__(self, Wa):
731         self._MapFileName = os.path.join(Wa.BuildDir, Wa.Name + ".map")
732         self._MapFileParsed = False
733         self._EotToolInvoked = False
734         self._FvDir = Wa.FvDir
735         self._EotDir = Wa.BuildDir
736         self._FfsEntryPoint = {}
737         self._GuidMap = {}
738         self._SourceList = []
739         self.FixedMapDict = {}
740         self.ItemList = []
741         self.MaxLen = 0
742
743         #
744         # Collect all platform reference source files and GUID C Name
745         #
746         for Pa in Wa.AutoGenObjectList:
747             for Module in Pa.LibraryAutoGenList + Pa.ModuleAutoGenList:
748                 #
749                 # Add module referenced source files
750                 #
751                 self._SourceList.append(str(Module))
752                 IncludeList = {}
753                 for Source in Module.SourceFileList:
754                     if os.path.splitext(str(Source))[1].lower() == ".c":
755                         self._SourceList.append("  " + str(Source))
756                         FindIncludeFiles(Source.Path, Module.IncludePathList, IncludeList)
757                 for IncludeFile in IncludeList.values():
758                     self._SourceList.append("  " + IncludeFile)
759
760                 for Guid in Module.PpiList:
761                     self._GuidMap[Guid] = GuidStructureStringToGuidString(Module.PpiList[Guid])
762                 for Guid in Module.ProtocolList:
763                     self._GuidMap[Guid] = GuidStructureStringToGuidString(Module.ProtocolList[Guid])
764                 for Guid in Module.GuidList:
765                     self._GuidMap[Guid] = GuidStructureStringToGuidString(Module.GuidList[Guid])
766
767                 if Module.Guid and not Module.IsLibrary:
768                     EntryPoint = " ".join(Module.Module.ModuleEntryPointList)
769                     if int(str(Module.AutoGenVersion), 0) >= 0x00010005:
770                         RealEntryPoint = "_ModuleEntryPoint"
771                     else:
772                         RealEntryPoint = EntryPoint
773                         if EntryPoint == "_ModuleEntryPoint":
774                             CCFlags = Module.BuildOption.get("CC", {}).get("FLAGS", "")
775                             Match = gGlueLibEntryPoint.search(CCFlags)
776                             if Match:
777                                 EntryPoint = Match.group(1)
778
779                     self._FfsEntryPoint[Module.Guid.upper()] = (EntryPoint, RealEntryPoint)
780
781
782         #
783         # Collect platform firmware volume list as the input of EOT.
784         #
785         self._FvList = []
786         if Wa.FdfProfile:
787             for Fd in Wa.FdfProfile.FdDict:
788                 for FdRegion in Wa.FdfProfile.FdDict[Fd].RegionList:
789                     if FdRegion.RegionType != "FV":
790                         continue
791                     for FvName in FdRegion.RegionDataList:
792                         if FvName in self._FvList:
793                             continue
794                         self._FvList.append(FvName)
795                         for Ffs in Wa.FdfProfile.FvDict[FvName.upper()].FfsList:
796                             for Section in Ffs.SectionList:
797                                 try:
798                                     for FvSection in Section.SectionList:
799                                         if FvSection.FvName in self._FvList:
800                                             continue
801                                         self._FvList.append(FvSection.FvName)
802                                 except AttributeError:
803                                     pass
804
805
806     ##
807     # Parse platform fixed address map files
808     #
809     # This function parses the platform final fixed address map file to get
810     # the database of predicted fixed address for module image base, entry point
811     # etc.
812     #
813     # @param self:           The object pointer
814     #
815     def _ParseMapFile(self):
816         if self._MapFileParsed:
817             return
818         self._MapFileParsed = True
819         if os.path.isfile(self._MapFileName):
820             try:
821                 FileContents = open(self._MapFileName).read()
822                 for Match in gMapFileItemPattern.finditer(FileContents):
823                     AddressType = Match.group(1)
824                     BaseAddress = Match.group(2)
825                     EntryPoint = Match.group(3)
826                     Guid = Match.group(4).upper()
827                     List = self.FixedMapDict.setdefault(Guid, [])
828                     List.append((AddressType, BaseAddress, "*I"))
829                     List.append((AddressType, EntryPoint, "*E"))
830             except:
831                 EdkLogger.warn(None, "Cannot open file to read", self._MapFileName)
832
833     ##
834     # Invokes EOT tool to get the predicted the execution order.
835     #
836     # This function invokes EOT tool to calculate the predicted dispatch order
837     #
838     # @param self:           The object pointer
839     #
840     def _InvokeEotTool(self):
841         if self._EotToolInvoked:
842             return
843
844         self._EotToolInvoked = True
845         FvFileList = []
846         for FvName in self._FvList:
847             FvFile = os.path.join(self._FvDir, FvName + ".Fv")
848             if os.path.isfile(FvFile):
849                 FvFileList.append(FvFile)
850
851         if len(FvFileList) == 0:
852             return
853         #
854         # Write source file list and GUID file list to an intermediate file
855         # as the input for EOT tool and dispatch List as the output file
856         # from EOT tool.
857         #
858         SourceList = os.path.join(self._EotDir, "SourceFile.txt")
859         GuidList = os.path.join(self._EotDir, "GuidList.txt")
860         DispatchList = os.path.join(self._EotDir, "Dispatch.txt")
861
862         TempFile = open(SourceList, "w+")
863         for Item in self._SourceList:
864             FileWrite(TempFile, Item)
865         TempFile.close()
866         TempFile = open(GuidList, "w+")
867         for Key in self._GuidMap:
868             FileWrite(TempFile, "%s %s" % (Key, self._GuidMap[Key]))
869         TempFile.close()
870
871         try:
872             from Eot.Eot import Eot
873             #
874             # Invoke EOT tool
875             #
876             Eot(CommandLineOption=False, SourceFileList=SourceList, GuidList=GuidList,
877                 FvFileList=' '.join(FvFileList), Dispatch=DispatchList, IsInit=True)
878
879             #
880             # Parse the output of EOT tool
881             #
882             for Line in open(DispatchList):
883                 (Guid, Phase, FfsName, FilePath) = Line.split()
884                 Symbol = self._FfsEntryPoint.get(Guid, [FfsName, ""])[0]
885                 if len(Symbol) > self.MaxLen:
886                     self.MaxLen = len(Symbol)
887                 self.ItemList.append((Phase, Symbol, FilePath))
888         except:
889             EdkLogger.warn(None, "Failed to generate execution order prediction report, for some error occurred in executing EOT.")
890
891
892     ##
893     # Generate platform execution order report
894     #
895     # This function generates the predicted module execution order.
896     #
897     # @param self            The object pointer
898     # @param File            The file object for report
899     #
900     def _GenerateExecutionOrderReport(self, File):
901         if len(self.ItemList) == 0:
902             return
903         FileWrite(File, gSectionStart)
904         FileWrite(File, "Execution Order Prediction")
905         FileWrite(File, "*P PEI phase")
906         FileWrite(File, "*D DXE phase")
907         FileWrite(File, "*E Module INF entry point name")
908         FileWrite(File, "*N Module notification function name")
909
910         FileWrite(File, "Type %-*s %s" % (self.MaxLen, "Symbol", "Module INF Path"))
911         FileWrite(File, gSectionSep)
912         for Item in self.ItemList:
913             FileWrite(File, "*%sE  %-*s %s" % (Item[0], self.MaxLen, Item[1], Item[2]))
914
915         FileWrite(File, gSectionStart)
916
917     ##
918     # Generate Fixed Address report.
919     #
920     # This function generate the predicted fixed address report for a module
921     # specified by Guid.
922     #
923     # @param self            The object pointer
924     # @param File            The file object for report
925     # @param Guid            The module Guid value.
926     # @param NotifyList      The list of all notify function in a module
927     #
928     def _GenerateFixedAddressReport(self, File, Guid, NotifyList):
929         FixedAddressList = self.FixedMapDict.get(Guid)
930         if not FixedAddressList:
931             return
932
933         FileWrite(File, gSubSectionStart)
934         FileWrite(File, "Fixed Address Prediction")
935         FileWrite(File, "*I  Image Loading Address")
936         FileWrite(File, "*E  Entry Point Address")
937         FileWrite(File, "*N  Notification Function Address")
938         FileWrite(File, "*F  Flash Address")
939         FileWrite(File, "*M  Memory Address")
940         FileWrite(File, "*S  SMM RAM Address")
941         FileWrite(File, "TOM Top of Memory")
942
943         FileWrite(File, "Type Address           Name")
944         FileWrite(File, gSubSectionSep)
945         for Item in FixedAddressList:
946             Type = Item[0]
947             Value = Item[1]
948             Symbol = Item[2]
949             if Symbol == "*I":
950                 Name = "(Image Base)"
951             elif Symbol == "*E":
952                 Name = self._FfsEntryPoint.get(Guid, ["", "_ModuleEntryPoint"])[1]
953             elif Symbol in NotifyList:
954                 Name = Symbol
955                 Symbol = "*N"
956             else:
957                 continue
958
959             if "Flash" in Type:
960                 Symbol += "F"
961             elif "Memory" in Type:
962                 Symbol += "M"
963             else:
964                 Symbol += "S"
965
966             if Value[0] == "-":
967                 Value = "TOM" + Value
968
969             FileWrite(File, "%s  %-16s  %s" % (Symbol, Value, Name))
970
971     ##
972     # Generate report for the prediction part
973     #
974     # This function generate the predicted fixed address report for a module or
975     # predicted module execution order for a platform.
976     # If the input Guid is None, then, it generates the predicted module execution order;
977     # otherwise it generated the module fixed loading address for the module specified by
978     # Guid.
979     #
980     # @param self            The object pointer
981     # @param File            The file object for report
982     # @param Guid            The module Guid value.
983     #
984     def GenerateReport(self, File, Guid):
985         self._ParseMapFile()
986         self._InvokeEotTool()
987         if Guid:
988             self._GenerateFixedAddressReport(File, Guid.upper(), [])
989         else:
990             self._GenerateExecutionOrderReport(File)
991
992 ##
993 # Reports FD region information
994 #
995 # This class reports the FD subsection in the build report file.
996 # It collects region information of platform flash device.
997 # If the region is a firmware volume, it lists the set of modules
998 # and its space information; otherwise, it only lists its region name,
999 # base address and size in its sub-section header.
1000 # If there are nesting FVs, the nested FVs will list immediate after
1001 # this FD region subsection
1002 #
1003 class FdRegionReport(object):
1004     ##
1005     # Discover all the nested FV name list.
1006     #
1007     # This is an internal worker function to discover the all the nested FV information
1008     # in the parent firmware volume. It uses deep first search algorithm recursively to
1009     # find all the FV list name and append them to the list.
1010     #
1011     # @param self            The object pointer
1012     # @param FvName          The name of current firmware file system
1013     # @param Wa              Workspace context information
1014     #
1015     def _DiscoverNestedFvList(self, FvName, Wa):
1016         for Ffs in Wa.FdfProfile.FvDict[FvName.upper()].FfsList:
1017             for Section in Ffs.SectionList:
1018                 try:
1019                     for FvSection in Section.SectionList:
1020                         if FvSection.FvName in self.FvList:
1021                             continue
1022                         self._GuidsDb[Ffs.NameGuid.upper()] = FvSection.FvName
1023                         self.FvList.append(FvSection.FvName)
1024                         self.FvInfo[FvSection.FvName] = ("Nested FV", 0, 0)
1025                         self._DiscoverNestedFvList(FvSection.FvName, Wa)
1026                 except AttributeError:
1027                     pass
1028
1029     ##
1030     # Constructor function for class FdRegionReport
1031     #
1032     # This constructor function generates FdRegionReport object for a specified FdRegion.
1033     # If the FdRegion is a firmware volume, it will recursively find all its nested Firmware
1034     # volume list. This function also collects GUID map in order to dump module identification
1035     # in the final report.
1036     #
1037     # @param self:           The object pointer
1038     # @param FdRegion        The current FdRegion object
1039     # @param Wa              Workspace context information
1040     #
1041     def __init__(self, FdRegion, Wa):
1042         self.Type = FdRegion.RegionType
1043         self.BaseAddress = FdRegion.Offset
1044         self.Size = FdRegion.Size
1045         self.FvList = []
1046         self.FvInfo = {}
1047         self._GuidsDb = {}
1048         self._FvDir = Wa.FvDir
1049
1050         #
1051         # If the input FdRegion is not a firmware volume,
1052         # we are done.
1053         #
1054         if self.Type != "FV":
1055             return
1056
1057         #
1058         # Find all nested FVs in the FdRegion
1059         #
1060         for FvName in FdRegion.RegionDataList:
1061             if FvName in self.FvList:
1062                 continue
1063             self.FvList.append(FvName)
1064             self.FvInfo[FvName] = ("Fd Region", self.BaseAddress, self.Size)
1065             self._DiscoverNestedFvList(FvName, Wa)
1066
1067         PlatformPcds = {}
1068         for Pa in Wa.AutoGenObjectList:
1069             PackageList = []
1070             for ModuleKey in Pa.Platform.Modules:
1071                 #
1072                 # Collect PCD DEC default value.
1073                 #
1074                 Module = Pa.Platform.Modules[ModuleKey]
1075                 for Package in Module.M.Module.Packages:
1076                     if Package not in PackageList:
1077                         PackageList.append(Package)
1078
1079             for Package in PackageList:
1080                 for (TokenCName, TokenSpaceGuidCName, DecType) in Package.Pcds:
1081                     DecDefaultValue = Package.Pcds[TokenCName, TokenSpaceGuidCName, DecType].DefaultValue
1082                     PlatformPcds[(TokenCName, TokenSpaceGuidCName)] = DecDefaultValue
1083
1084         #
1085         # Collect PCDs defined in DSC common section
1086         #
1087         for Platform in Wa.BuildDatabase.WorkspaceDb.PlatformList:
1088             for (TokenCName, TokenSpaceGuidCName) in Platform.Pcds:
1089                 DscDefaultValue = Platform.Pcds[(TokenCName, TokenSpaceGuidCName)].DefaultValue
1090                 PlatformPcds[(TokenCName, TokenSpaceGuidCName)] = DscDefaultValue
1091
1092         #
1093         # Add PEI and DXE a priori files GUIDs defined in PI specification.
1094         #
1095         self._GuidsDb["1B45CC0A-156A-428A-AF62-49864DA0E6E6"] = "PEI Apriori"
1096         self._GuidsDb["FC510EE7-FFDC-11D4-BD41-0080C73C8881"] = "DXE Apriori"
1097         #
1098         # Add ACPI table storage file
1099         #
1100         self._GuidsDb["7E374E25-8E01-4FEE-87F2-390C23C606CD"] = "ACPI table storage"
1101
1102         for Pa in Wa.AutoGenObjectList:
1103             for ModuleKey in Pa.Platform.Modules:
1104                 M = Pa.Platform.Modules[ModuleKey].M
1105                 InfPath = os.path.join(Wa.WorkspaceDir, M.MetaFile.File)
1106                 self._GuidsDb[M.Guid.upper()] = "%s (%s)" % (M.Module.BaseName, InfPath)
1107
1108         #
1109         # Collect the GUID map in the FV firmware volume
1110         #
1111         for FvName in self.FvList:
1112             for Ffs in Wa.FdfProfile.FvDict[FvName.upper()].FfsList:
1113                 try:
1114                     #
1115                     # collect GUID map for binary EFI file in FDF file.
1116                     #
1117                     Guid = Ffs.NameGuid.upper()
1118                     Match = gPcdGuidPattern.match(Ffs.NameGuid)
1119                     if Match:
1120                         PcdTokenspace = Match.group(1)
1121                         PcdToken = Match.group(2)
1122                         if (PcdToken, PcdTokenspace) in PlatformPcds:
1123                             GuidValue = PlatformPcds[(PcdToken, PcdTokenspace)]
1124                             Guid = GuidStructureByteArrayToGuidString(GuidValue).upper()
1125                     for Section in Ffs.SectionList:
1126                         try:
1127                             ModuleSectFile = os.path.join(Wa.WorkspaceDir, Section.SectFileName)
1128                             self._GuidsDb[Guid] = ModuleSectFile
1129                         except AttributeError:
1130                             pass
1131                 except AttributeError:
1132                     pass
1133
1134
1135     ##
1136     # Internal worker function to generate report for the FD region
1137     #
1138     # This internal worker function to generate report for the FD region.
1139     # It the type is firmware volume, it lists offset and module identification.
1140     #
1141     # @param self            The object pointer
1142     # @param File            The file object for report
1143     # @param Title           The title for the FD subsection
1144     # @param BaseAddress     The base address for the FD region
1145     # @param Size            The size of the FD region
1146     # @param FvName          The FV name if the FD region is a firmware volume
1147     #
1148     def _GenerateReport(self, File, Title, Type, BaseAddress, Size=0, FvName=None):
1149         FileWrite(File, gSubSectionStart)
1150         FileWrite(File, Title)
1151         FileWrite(File, "Type:               %s" % Type)
1152         FileWrite(File, "Base Address:       0x%X" % BaseAddress)
1153
1154         if self.Type == "FV":
1155             FvTotalSize = 0
1156             FvTakenSize = 0
1157             FvFreeSize  = 0
1158             FvReportFileName = os.path.join(self._FvDir, FvName + ".fv.txt")
1159             try:
1160                 #
1161                 # Collect size info in the firmware volume.
1162                 #
1163                 FvReport = open(FvReportFileName).read()
1164                 Match = gFvTotalSizePattern.search(FvReport)
1165                 if Match:
1166                     FvTotalSize = int(Match.group(1), 16)
1167                 Match = gFvTakenSizePattern.search(FvReport)
1168                 if Match:
1169                     FvTakenSize = int(Match.group(1), 16)
1170                 FvFreeSize = FvTotalSize - FvTakenSize
1171                 #
1172                 # Write size information to the report file.
1173                 #
1174                 FileWrite(File, "Size:               0x%X (%.0fK)" % (FvTotalSize, FvTotalSize / 1024.0))
1175                 FileWrite(File, "Fv Name:            %s (%.1f%% Full)" % (FvName, FvTakenSize * 100.0 / FvTotalSize))
1176                 FileWrite(File, "Occupied Size:      0x%X (%.0fK)" % (FvTakenSize, FvTakenSize / 1024.0))
1177                 FileWrite(File, "Free Size:          0x%X (%.0fK)" % (FvFreeSize, FvFreeSize / 1024.0))
1178                 FileWrite(File, "Offset     Module")
1179                 FileWrite(File, gSubSectionSep)
1180                 #
1181                 # Write module offset and module identification to the report file.
1182                 #
1183                 for Match in gOffsetGuidPattern.finditer(FvReport):
1184                     Guid = Match.group(2).upper()
1185                     Offset = int(Match.group(1), 16)
1186                     FileWrite (File, "0x%07X %s" % (Offset, self._GuidsDb.get(Guid, Guid)))
1187             except IOError:
1188                 EdkLogger.warn(None, "Fail to read report file", FvReportFileName)
1189         else:
1190             FileWrite(File, "Size:               0x%X (%.0fK)" % (Size, Size / 1024.0))
1191         FileWrite(File, gSubSectionEnd)
1192
1193     ##
1194     # Generate report for the FD region
1195     #
1196     # This function generates report for the FD region.
1197     #
1198     # @param self            The object pointer
1199     # @param File            The file object for report
1200     #
1201     def GenerateReport(self, File):
1202         if (len(self.FvList) > 0):
1203             for FvItem in self.FvList:
1204                 Info = self.FvInfo[FvItem]
1205                 self._GenerateReport(File, Info[0], "FV", Info[1], Info[2], FvItem)
1206         else:
1207             self._GenerateReport(File, "FD Region", self.Type, self.BaseAddress, self.Size)
1208
1209 ##
1210 # Reports FD information
1211 #
1212 # This class reports the FD section in the build report file.
1213 # It collects flash device information for a platform.
1214 #
1215 class FdReport(object):
1216     ##
1217     # Constructor function for class FdReport
1218     #
1219     # This constructor function generates FdReport object for a specified
1220     # firmware device.
1221     #
1222     # @param self            The object pointer
1223     # @param Fd              The current Firmware device object
1224     # @param Wa              Workspace context information
1225     #
1226     def __init__(self, Fd, Wa):
1227         self.FdName = Fd.FdUiName
1228         self.BaseAddress = Fd.BaseAddress
1229         self.Size = Fd.Size
1230         self.FdRegionList = [FdRegionReport(FdRegion, Wa) for FdRegion in Fd.RegionList]
1231
1232     ##
1233     # Generate report for the firmware device.
1234     #
1235     # This function generates report for the firmware device.
1236     #
1237     # @param self            The object pointer
1238     # @param File            The file object for report
1239     #
1240     def GenerateReport(self, File):
1241         FileWrite(File, gSectionStart)
1242         FileWrite(File, "Firmware Device (FD)")
1243         FileWrite(File, "FD Name:            %s" % self.FdName)
1244         FileWrite(File, "Base Address:       %s" % self.BaseAddress)
1245         FileWrite(File, "Size:               0x%X (%.0fK)" % (self.Size, self.Size / 1024.0))
1246         if len(self.FdRegionList) > 0:
1247             FileWrite(File, gSectionSep)
1248             for FdRegionItem in self.FdRegionList:
1249                 FdRegionItem.GenerateReport(File)
1250
1251         FileWrite(File, gSectionEnd)
1252
1253
1254
1255 ##
1256 # Reports platform information
1257 #
1258 # This class reports the whole platform information
1259 #
1260 class PlatformReport(object):
1261     ##
1262     # Constructor function for class PlatformReport
1263     #
1264     # This constructor function generates PlatformReport object a platform build.
1265     # It generates report for platform summary, flash, global PCDs and detailed
1266     # module information for modules involved in platform build.
1267     #
1268     # @param self            The object pointer
1269     # @param Wa              Workspace context information
1270     #
1271     def __init__(self, Wa, ReportType):
1272         self._WorkspaceDir = Wa.WorkspaceDir
1273         self.PlatformName = Wa.Name
1274         self.PlatformDscPath = Wa.Platform
1275         self.Architectures = " ".join(Wa.ArchList)
1276         self.ToolChain = Wa.ToolChain
1277         self.Target = Wa.BuildTarget
1278         self.OutputPath = os.path.join(Wa.WorkspaceDir, Wa.OutputDir)
1279         self.BuildEnvironment = platform.platform()
1280
1281         self.PcdReport = None
1282         if "PCD" in ReportType:
1283             self.PcdReport = PcdReport(Wa)
1284
1285         self.FdReportList = []
1286         if "FLASH" in ReportType and Wa.FdfProfile:
1287             for Fd in Wa.FdfProfile.FdDict:
1288                 self.FdReportList.append(FdReport(Wa.FdfProfile.FdDict[Fd], Wa))
1289
1290         self.PredictionReport = None
1291         if "PREDICTION" in ReportType:
1292             self.PredictionReport = PredictionReport(Wa)
1293
1294         self.ModuleReportList = []
1295         for Pa in Wa.AutoGenObjectList:
1296             for ModuleKey in Pa.Platform.Modules:
1297                 for Platform in Wa.BuildDatabase.WorkspaceDb.PlatformList:
1298                     if ModuleKey in Platform.Modules:
1299                         DscOverridePcds = Platform.Modules[ModuleKey].Pcds
1300                         break
1301                 else:
1302                     DscOverridePcds = {}
1303                 self.ModuleReportList.append(ModuleReport(Pa.Platform.Modules[ModuleKey].M, DscOverridePcds, ReportType))
1304
1305
1306
1307     ##
1308     # Generate report for the whole platform.
1309     #
1310     # This function generates report for platform information.
1311     # It comprises of platform summary, global PCD, flash and
1312     # module list sections.
1313     #
1314     # @param self            The object pointer
1315     # @param File            The file object for report
1316     # @param BuildDuration   The total time to build the modules
1317     # @param ReportType      The kind of report items in the final report file
1318     #
1319     def GenerateReport(self, File, BuildDuration, ReportType):
1320         FileWrite(File, "Platform Summary")
1321         FileWrite(File, "Platform Name:        %s" % self.PlatformName)
1322         FileWrite(File, "Platform DSC Path:    %s" % self.PlatformDscPath)
1323         FileWrite(File, "Platform DSC Path:    %s" % self.Architectures)
1324         FileWrite(File, "Tool Chain:           %s" % self.ToolChain)
1325         FileWrite(File, "Target:               %s" % self.Target)
1326         FileWrite(File, "Output Path:          %s" % self.OutputPath)
1327         FileWrite(File, "Build Environment:    %s" % self.BuildEnvironment)
1328         FileWrite(File, "Build Duration:       %s" % BuildDuration)
1329         FileWrite(File, "Report Content:       %s" % ", ".join(ReportType))
1330
1331         if "PCD" in ReportType:
1332             self.PcdReport.GenerateReport(File, None, {})
1333
1334         if "FLASH" in ReportType:
1335             for FdReportListItem in self.FdReportList:
1336                 FdReportListItem.GenerateReport(File)
1337
1338         for ModuleReportItem in self.ModuleReportList:
1339             ModuleReportItem.GenerateReport(File, self.PcdReport, self.PredictionReport, ReportType)
1340
1341         if "PREDICTION" in ReportType:
1342             self.PredictionReport.GenerateReport(File, None)
1343
1344 ## BuildReport class
1345 #
1346 #  This base class contain the routines to collect data and then
1347 #  applies certain format to the output report
1348 #
1349 class BuildReport(object):
1350     ##
1351     # Constructor function for class BuildReport
1352     #
1353     # This constructor function generates BuildReport object a platform build.
1354     # It generates report for platform summary, flash, global PCDs and detailed
1355     # module information for modules involved in platform build.
1356     #
1357     # @param self            The object pointer
1358     # @param ReportFile      The file name to save report file
1359     # @param ReportType      The kind of report items in the final report file
1360     #
1361     def __init__(self, ReportFile, ReportType):
1362         self.ReportFile = ReportFile
1363         if ReportFile:
1364             self.ReportList = []
1365             self.ReportType = []
1366             if ReportType == None:
1367                 self.ReportType = ["PCD", "LIBRARY", "BUILD_FLAGS", "DEPEX", "FLASH"]
1368             elif "ALL" in ReportType:
1369                 self.ReportType = ["PCD", "LIBRARY", "BUILD_FLAGS", "DEPEX", "FLASH", "PREDICTION"]
1370             else:
1371                 for ReportTypeItem in ReportType:
1372                     if ReportTypeItem not in self.ReportType:
1373                         self.ReportType.append(ReportTypeItem)
1374
1375     ##
1376     # Adds platform report to the list
1377     #
1378     # This function adds a platform report to the final report list.
1379     #
1380     # @param self            The object pointer
1381     # @param Wa              Workspace context information
1382     #
1383     def AddPlatformReport(self, Wa):
1384         if self.ReportFile:
1385             self.ReportList.append(PlatformReport(Wa, self.ReportType))
1386
1387     ##
1388     # Generates the final report.
1389     #
1390     # This function generates platform build report. It invokes GenerateReport()
1391     # method for every platform report in the list.
1392     #
1393     # @param self            The object pointer
1394     # @param BuildDuration   The total time to build the modules
1395     #
1396     def GenerateReport(self, BuildDuration):
1397         if self.ReportFile:
1398             try:
1399                 File = open(self.ReportFile, "w+")
1400             except IOError:
1401                 EdkLogger.error(None, FILE_OPEN_FAILURE, ExtraData=self.ReportFile)
1402             try:
1403                 for Report in self.ReportList:
1404                     Report.GenerateReport(File, BuildDuration, self.ReportType)
1405             except IOError:
1406                 EdkLogger.error(None, FILE_WRITE_FAILURE, ExtraData=self.ReportFile)
1407             File.close()
1408
1409 # This acts like the main() function for the script, unless it is 'import'ed into another script.
1410 if __name__ == '__main__':
1411     pass
1412