f83285e0d439ac71fb0eed5233ca8d376d7b7b34
[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 "FIXED_ADDRESS" in ReportType and self.FileGuid:
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.strip() == PcdValue.strip())
671
672                         if InfDefaultValue == None:
673                             InfMatch = True
674                         else:
675                             InfMatch = (InfDefaultValue.strip() == PcdValue.strip())
676
677                         if DscDefaultValue == None:
678                             DscMatch = True
679                         else:
680                             DscMatch = (DscDefaultValue.strip() == PcdValue.strip())
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.strip()))
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.strip()))
691                             else:
692                                 FileWrite(File, ' *P %-*s: %6s %10s = %-22s' % (self.MaxLen, Pcd.TokenCName, TypeName, '('+Pcd.DatumType+')', PcdValue.strip()))
693                         else:
694                             FileWrite(File, ' *M %-*s: %6s %10s = %-22s' % (self.MaxLen, Pcd.TokenCName, TypeName, '('+Pcd.DatumType+')', PcdValue.strip()))
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.strip()))
705
706                     if not InfMatch and InfDefaultValue != None:
707                         FileWrite(File, '    %*s = %s' % (self.MaxLen + 19, 'INF DEFAULT', InfDefaultValue.strip()))
708
709                     if not DecMatch and DecDefaultValue != None:
710                         FileWrite(File, '    %*s = %s' % (self.MaxLen + 19, 'DEC DEFAULT', DecDefaultValue.strip()))
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.strip() == PcdValue.strip())
721                             if Match:
722                                 continue
723                             FileWrite(File, ' *M %-*s = %s' % (self.MaxLen + 19, ModulePath, ModuleDefault.strip()))
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                 if len(Line.split()) < 4:
901                     continue
902                 (Guid, Phase, FfsName, FilePath) = Line.split()
903                 Symbol = self._FfsEntryPoint.get(Guid, [FfsName, ""])[0]
904                 if len(Symbol) > self.MaxLen:
905                     self.MaxLen = len(Symbol)
906                 self.ItemList.append((Phase, Symbol, FilePath))
907         except:
908             EdkLogger.quiet("(Python %s on %s\n%s)" % (platform.python_version(), sys.platform, traceback.format_exc()))
909             EdkLogger.warn(None, "Failed to generate execution order prediction report, for some error occurred in executing EOT.")
910
911
912     ##
913     # Generate platform execution order report
914     #
915     # This function generates the predicted module execution order.
916     #
917     # @param self            The object pointer
918     # @param File            The file object for report
919     #
920     def _GenerateExecutionOrderReport(self, File):
921         self._InvokeEotTool()
922         if len(self.ItemList) == 0:
923             return
924         FileWrite(File, gSectionStart)
925         FileWrite(File, "Execution Order Prediction")
926         FileWrite(File, "*P PEI phase")
927         FileWrite(File, "*D DXE phase")
928         FileWrite(File, "*E Module INF entry point name")
929         FileWrite(File, "*N Module notification function name")
930
931         FileWrite(File, "Type %-*s %s" % (self.MaxLen, "Symbol", "Module INF Path"))
932         FileWrite(File, gSectionSep)
933         for Item in self.ItemList:
934             FileWrite(File, "*%sE  %-*s %s" % (Item[0], self.MaxLen, Item[1], Item[2]))
935
936         FileWrite(File, gSectionStart)
937
938     ##
939     # Generate Fixed Address report.
940     #
941     # This function generate the predicted fixed address report for a module
942     # specified by Guid.
943     #
944     # @param self            The object pointer
945     # @param File            The file object for report
946     # @param Guid            The module Guid value.
947     # @param NotifyList      The list of all notify function in a module
948     #
949     def _GenerateFixedAddressReport(self, File, Guid, NotifyList):
950         self._ParseMapFile()
951         FixedAddressList = self.FixedMapDict.get(Guid)
952         if not FixedAddressList:
953             return
954
955         FileWrite(File, gSubSectionStart)
956         FileWrite(File, "Fixed Address Prediction")
957         FileWrite(File, "*I  Image Loading Address")
958         FileWrite(File, "*E  Entry Point Address")
959         FileWrite(File, "*N  Notification Function Address")
960         FileWrite(File, "*F  Flash Address")
961         FileWrite(File, "*M  Memory Address")
962         FileWrite(File, "*S  SMM RAM Offset")
963         FileWrite(File, "TOM Top of Memory")
964
965         FileWrite(File, "Type Address           Name")
966         FileWrite(File, gSubSectionSep)
967         for Item in FixedAddressList:
968             Type = Item[0]
969             Value = Item[1]
970             Symbol = Item[2]
971             if Symbol == "*I":
972                 Name = "(Image Base)"
973             elif Symbol == "*E":
974                 Name = self._FfsEntryPoint.get(Guid, ["", "_ModuleEntryPoint"])[1]
975             elif Symbol in NotifyList:
976                 Name = Symbol
977                 Symbol = "*N"
978             else:
979                 continue
980
981             if "Flash" in Type:
982                 Symbol += "F"
983             elif "Memory" in Type:
984                 Symbol += "M"
985             else:
986                 Symbol += "S"
987
988             if Value[0] == "-":
989                 Value = "TOM" + Value
990
991             FileWrite(File, "%s  %-16s  %s" % (Symbol, Value, Name))
992
993     ##
994     # Generate report for the prediction part
995     #
996     # This function generate the predicted fixed address report for a module or
997     # predicted module execution order for a platform.
998     # If the input Guid is None, then, it generates the predicted module execution order;
999     # otherwise it generated the module fixed loading address for the module specified by
1000     # Guid.
1001     #
1002     # @param self            The object pointer
1003     # @param File            The file object for report
1004     # @param Guid            The module Guid value.
1005     #
1006     def GenerateReport(self, File, Guid):
1007         if Guid:
1008             self._GenerateFixedAddressReport(File, Guid.upper(), [])
1009         else:
1010             self._GenerateExecutionOrderReport(File)
1011
1012 ##
1013 # Reports FD region information
1014 #
1015 # This class reports the FD subsection in the build report file.
1016 # It collects region information of platform flash device.
1017 # If the region is a firmware volume, it lists the set of modules
1018 # and its space information; otherwise, it only lists its region name,
1019 # base address and size in its sub-section header.
1020 # If there are nesting FVs, the nested FVs will list immediate after
1021 # this FD region subsection
1022 #
1023 class FdRegionReport(object):
1024     ##
1025     # Discover all the nested FV name list.
1026     #
1027     # This is an internal worker function to discover the all the nested FV information
1028     # in the parent firmware volume. It uses deep first search algorithm recursively to
1029     # find all the FV list name and append them to the list.
1030     #
1031     # @param self            The object pointer
1032     # @param FvName          The name of current firmware file system
1033     # @param Wa              Workspace context information
1034     #
1035     def _DiscoverNestedFvList(self, FvName, Wa):
1036         for Ffs in Wa.FdfProfile.FvDict[FvName.upper()].FfsList:
1037             for Section in Ffs.SectionList:
1038                 try:
1039                     for FvSection in Section.SectionList:
1040                         if FvSection.FvName in self.FvList:
1041                             continue
1042                         self._GuidsDb[Ffs.NameGuid.upper()] = FvSection.FvName
1043                         self.FvList.append(FvSection.FvName)
1044                         self.FvInfo[FvSection.FvName] = ("Nested FV", 0, 0)
1045                         self._DiscoverNestedFvList(FvSection.FvName, Wa)
1046                 except AttributeError:
1047                     pass
1048
1049     ##
1050     # Constructor function for class FdRegionReport
1051     #
1052     # This constructor function generates FdRegionReport object for a specified FdRegion.
1053     # If the FdRegion is a firmware volume, it will recursively find all its nested Firmware
1054     # volume list. This function also collects GUID map in order to dump module identification
1055     # in the final report.
1056     #
1057     # @param self:           The object pointer
1058     # @param FdRegion        The current FdRegion object
1059     # @param Wa              Workspace context information
1060     #
1061     def __init__(self, FdRegion, Wa):
1062         self.Type = FdRegion.RegionType
1063         self.BaseAddress = FdRegion.Offset
1064         self.Size = FdRegion.Size
1065         self.FvList = []
1066         self.FvInfo = {}
1067         self._GuidsDb = {}
1068         self._FvDir = Wa.FvDir
1069
1070         #
1071         # If the input FdRegion is not a firmware volume,
1072         # we are done.
1073         #
1074         if self.Type != "FV":
1075             return
1076
1077         #
1078         # Find all nested FVs in the FdRegion
1079         #
1080         for FvName in FdRegion.RegionDataList:
1081             if FvName in self.FvList:
1082                 continue
1083             self.FvList.append(FvName)
1084             self.FvInfo[FvName] = ("Fd Region", self.BaseAddress, self.Size)
1085             self._DiscoverNestedFvList(FvName, Wa)
1086
1087         PlatformPcds = {}
1088         
1089         #
1090         # Collect PCDs declared in DEC files.
1091         #
1092         for Package in Wa.BuildDatabase.WorkspaceDb.PackageList:
1093             for (TokenCName, TokenSpaceGuidCName, DecType) in Package.Pcds:
1094                 DecDefaultValue = Package.Pcds[TokenCName, TokenSpaceGuidCName, DecType].DefaultValue
1095                 PlatformPcds[(TokenCName, TokenSpaceGuidCName)] = DecDefaultValue
1096         #
1097         # Collect PCDs defined in DSC common section
1098         #
1099         for Platform in Wa.BuildDatabase.WorkspaceDb.PlatformList:
1100             for (TokenCName, TokenSpaceGuidCName) in Platform.Pcds:
1101                 DscDefaultValue = Platform.Pcds[(TokenCName, TokenSpaceGuidCName)].DefaultValue
1102                 PlatformPcds[(TokenCName, TokenSpaceGuidCName)] = DscDefaultValue
1103
1104         #
1105         # Add PEI and DXE a priori files GUIDs defined in PI specification.
1106         #
1107         self._GuidsDb["1B45CC0A-156A-428A-AF62-49864DA0E6E6"] = "PEI Apriori"
1108         self._GuidsDb["FC510EE7-FFDC-11D4-BD41-0080C73C8881"] = "DXE Apriori"
1109         #
1110         # Add ACPI table storage file
1111         #
1112         self._GuidsDb["7E374E25-8E01-4FEE-87F2-390C23C606CD"] = "ACPI table storage"
1113
1114         for Pa in Wa.AutoGenObjectList:
1115             for ModuleKey in Pa.Platform.Modules:
1116                 M = Pa.Platform.Modules[ModuleKey].M
1117                 InfPath = os.path.join(Wa.WorkspaceDir, M.MetaFile.File)
1118                 self._GuidsDb[M.Guid.upper()] = "%s (%s)" % (M.Module.BaseName, InfPath)
1119
1120         #
1121         # Collect the GUID map in the FV firmware volume
1122         #
1123         for FvName in self.FvList:
1124             for Ffs in Wa.FdfProfile.FvDict[FvName.upper()].FfsList:
1125                 try:
1126                     #
1127                     # collect GUID map for binary EFI file in FDF file.
1128                     #
1129                     Guid = Ffs.NameGuid.upper()
1130                     Match = gPcdGuidPattern.match(Ffs.NameGuid)
1131                     if Match:
1132                         PcdTokenspace = Match.group(1)
1133                         PcdToken = Match.group(2)
1134                         if (PcdToken, PcdTokenspace) in PlatformPcds:
1135                             GuidValue = PlatformPcds[(PcdToken, PcdTokenspace)]
1136                             Guid = GuidStructureByteArrayToGuidString(GuidValue).upper()
1137                     for Section in Ffs.SectionList:
1138                         try:
1139                             ModuleSectFile = os.path.join(Wa.WorkspaceDir, Section.SectFileName)
1140                             self._GuidsDb[Guid] = ModuleSectFile
1141                         except AttributeError:
1142                             pass
1143                 except AttributeError:
1144                     pass
1145
1146
1147     ##
1148     # Internal worker function to generate report for the FD region
1149     #
1150     # This internal worker function to generate report for the FD region.
1151     # It the type is firmware volume, it lists offset and module identification.
1152     #
1153     # @param self            The object pointer
1154     # @param File            The file object for report
1155     # @param Title           The title for the FD subsection
1156     # @param BaseAddress     The base address for the FD region
1157     # @param Size            The size of the FD region
1158     # @param FvName          The FV name if the FD region is a firmware volume
1159     #
1160     def _GenerateReport(self, File, Title, Type, BaseAddress, Size=0, FvName=None):
1161         FileWrite(File, gSubSectionStart)
1162         FileWrite(File, Title)
1163         FileWrite(File, "Type:               %s" % Type)
1164         FileWrite(File, "Base Address:       0x%X" % BaseAddress)
1165
1166         if self.Type == "FV":
1167             FvTotalSize = 0
1168             FvTakenSize = 0
1169             FvFreeSize  = 0
1170             FvReportFileName = os.path.join(self._FvDir, FvName + ".fv.txt")
1171             try:
1172                 #
1173                 # Collect size info in the firmware volume.
1174                 #
1175                 FvReport = open(FvReportFileName).read()
1176                 Match = gFvTotalSizePattern.search(FvReport)
1177                 if Match:
1178                     FvTotalSize = int(Match.group(1), 16)
1179                 Match = gFvTakenSizePattern.search(FvReport)
1180                 if Match:
1181                     FvTakenSize = int(Match.group(1), 16)
1182                 FvFreeSize = FvTotalSize - FvTakenSize
1183                 #
1184                 # Write size information to the report file.
1185                 #
1186                 FileWrite(File, "Size:               0x%X (%.0fK)" % (FvTotalSize, FvTotalSize / 1024.0))
1187                 FileWrite(File, "Fv Name:            %s (%.1f%% Full)" % (FvName, FvTakenSize * 100.0 / FvTotalSize))
1188                 FileWrite(File, "Occupied Size:      0x%X (%.0fK)" % (FvTakenSize, FvTakenSize / 1024.0))
1189                 FileWrite(File, "Free Size:          0x%X (%.0fK)" % (FvFreeSize, FvFreeSize / 1024.0))
1190                 FileWrite(File, "Offset     Module")
1191                 FileWrite(File, gSubSectionSep)
1192                 #
1193                 # Write module offset and module identification to the report file.
1194                 #
1195                 OffsetInfo = {}
1196                 for Match in gOffsetGuidPattern.finditer(FvReport):
1197                     Guid = Match.group(2).upper()
1198                     OffsetInfo[Match.group(1)] = self._GuidsDb.get(Guid, Guid)
1199                 OffsetList = OffsetInfo.keys()
1200                 OffsetList.sort()
1201                 for Offset in OffsetList:
1202                     FileWrite (File, "%s %s" % (Offset, OffsetInfo[Offset]))
1203             except IOError:
1204                 EdkLogger.warn(None, "Fail to read report file", FvReportFileName)
1205         else:
1206             FileWrite(File, "Size:               0x%X (%.0fK)" % (Size, Size / 1024.0))
1207         FileWrite(File, gSubSectionEnd)
1208
1209     ##
1210     # Generate report for the FD region
1211     #
1212     # This function generates report for the FD region.
1213     #
1214     # @param self            The object pointer
1215     # @param File            The file object for report
1216     #
1217     def GenerateReport(self, File):
1218         if (len(self.FvList) > 0):
1219             for FvItem in self.FvList:
1220                 Info = self.FvInfo[FvItem]
1221                 self._GenerateReport(File, Info[0], "FV", Info[1], Info[2], FvItem)
1222         else:
1223             self._GenerateReport(File, "FD Region", self.Type, self.BaseAddress, self.Size)
1224
1225 ##
1226 # Reports FD information
1227 #
1228 # This class reports the FD section in the build report file.
1229 # It collects flash device information for a platform.
1230 #
1231 class FdReport(object):
1232     ##
1233     # Constructor function for class FdReport
1234     #
1235     # This constructor function generates FdReport object for a specified
1236     # firmware device.
1237     #
1238     # @param self            The object pointer
1239     # @param Fd              The current Firmware device object
1240     # @param Wa              Workspace context information
1241     #
1242     def __init__(self, Fd, Wa):
1243         self.FdName = Fd.FdUiName
1244         self.BaseAddress = Fd.BaseAddress
1245         self.Size = Fd.Size
1246         self.FdRegionList = [FdRegionReport(FdRegion, Wa) for FdRegion in Fd.RegionList]
1247
1248     ##
1249     # Generate report for the firmware device.
1250     #
1251     # This function generates report for the firmware device.
1252     #
1253     # @param self            The object pointer
1254     # @param File            The file object for report
1255     #
1256     def GenerateReport(self, File):
1257         FileWrite(File, gSectionStart)
1258         FileWrite(File, "Firmware Device (FD)")
1259         FileWrite(File, "FD Name:            %s" % self.FdName)
1260         FileWrite(File, "Base Address:       %s" % self.BaseAddress)
1261         FileWrite(File, "Size:               0x%X (%.0fK)" % (self.Size, self.Size / 1024.0))
1262         if len(self.FdRegionList) > 0:
1263             FileWrite(File, gSectionSep)
1264             for FdRegionItem in self.FdRegionList:
1265                 FdRegionItem.GenerateReport(File)
1266
1267         FileWrite(File, gSectionEnd)
1268
1269
1270
1271 ##
1272 # Reports platform information
1273 #
1274 # This class reports the whole platform information
1275 #
1276 class PlatformReport(object):
1277     ##
1278     # Constructor function for class PlatformReport
1279     #
1280     # This constructor function generates PlatformReport object a platform build.
1281     # It generates report for platform summary, flash, global PCDs and detailed
1282     # module information for modules involved in platform build.
1283     #
1284     # @param self            The object pointer
1285     # @param Wa              Workspace context information
1286     # @param MaList          The list of modules in the platform build
1287     #
1288     def __init__(self, Wa, MaList, ReportType):
1289         self._WorkspaceDir = Wa.WorkspaceDir
1290         self.PlatformName = Wa.Name
1291         self.PlatformDscPath = Wa.Platform
1292         self.Architectures = " ".join(Wa.ArchList)
1293         self.ToolChain = Wa.ToolChain
1294         self.Target = Wa.BuildTarget
1295         self.OutputPath = os.path.join(Wa.WorkspaceDir, Wa.OutputDir)
1296         self.BuildEnvironment = platform.platform()
1297
1298         self.PcdReport = None
1299         if "PCD" in ReportType:
1300             self.PcdReport = PcdReport(Wa)
1301
1302         self.FdReportList = []
1303         if "FLASH" in ReportType and Wa.FdfProfile and MaList == None:
1304             for Fd in Wa.FdfProfile.FdDict:
1305                 self.FdReportList.append(FdReport(Wa.FdfProfile.FdDict[Fd], Wa))
1306
1307         self.PredictionReport = None
1308         if "FIXED_ADDRESS" in ReportType or "EXECUTION_ORDER" in ReportType:
1309             self.PredictionReport = PredictionReport(Wa)
1310
1311         self.ModuleReportList = []
1312         if MaList != None:
1313             for Ma in MaList:
1314                 self.ModuleReportList.append(ModuleReport(Ma, ReportType))
1315         else:
1316             for Pa in Wa.AutoGenObjectList:
1317                 for ModuleKey in Pa.Platform.Modules:
1318                     self.ModuleReportList.append(ModuleReport(Pa.Platform.Modules[ModuleKey].M, ReportType))
1319
1320
1321
1322     ##
1323     # Generate report for the whole platform.
1324     #
1325     # This function generates report for platform information.
1326     # It comprises of platform summary, global PCD, flash and
1327     # module list sections.
1328     #
1329     # @param self            The object pointer
1330     # @param File            The file object for report
1331     # @param BuildDuration   The total time to build the modules
1332     # @param ReportType      The kind of report items in the final report file
1333     #
1334     def GenerateReport(self, File, BuildDuration, ReportType):
1335         FileWrite(File, "Platform Summary")
1336         FileWrite(File, "Platform Name:        %s" % self.PlatformName)
1337         FileWrite(File, "Platform DSC Path:    %s" % self.PlatformDscPath)
1338         FileWrite(File, "Architectures:        %s" % self.Architectures)
1339         FileWrite(File, "Tool Chain:           %s" % self.ToolChain)
1340         FileWrite(File, "Target:               %s" % self.Target)
1341         FileWrite(File, "Output Path:          %s" % self.OutputPath)
1342         FileWrite(File, "Build Environment:    %s" % self.BuildEnvironment)
1343         FileWrite(File, "Build Duration:       %s" % BuildDuration)
1344         FileWrite(File, "Report Content:       %s" % ", ".join(ReportType))
1345
1346         if "PCD" in ReportType:
1347             self.PcdReport.GenerateReport(File, None)
1348
1349         if "FLASH" in ReportType:
1350             for FdReportListItem in self.FdReportList:
1351                 FdReportListItem.GenerateReport(File)
1352
1353         for ModuleReportItem in self.ModuleReportList:
1354             ModuleReportItem.GenerateReport(File, self.PcdReport, self.PredictionReport, ReportType)
1355
1356         if "EXECUTION_ORDER" in ReportType:
1357             self.PredictionReport.GenerateReport(File, None)
1358
1359 ## BuildReport class
1360 #
1361 #  This base class contain the routines to collect data and then
1362 #  applies certain format to the output report
1363 #
1364 class BuildReport(object):
1365     ##
1366     # Constructor function for class BuildReport
1367     #
1368     # This constructor function generates BuildReport object a platform build.
1369     # It generates report for platform summary, flash, global PCDs and detailed
1370     # module information for modules involved in platform build.
1371     #
1372     # @param self            The object pointer
1373     # @param ReportFile      The file name to save report file
1374     # @param ReportType      The kind of report items in the final report file
1375     #
1376     def __init__(self, ReportFile, ReportType):
1377         self.ReportFile = ReportFile
1378         if ReportFile:
1379             self.ReportList = []
1380             self.ReportType = []
1381             if ReportType: 
1382                 for ReportTypeItem in ReportType:
1383                     if ReportTypeItem not in self.ReportType:
1384                         self.ReportType.append(ReportTypeItem)
1385             else:
1386                 self.ReportType = ["PCD", "LIBRARY", "BUILD_FLAGS", "DEPEX", "FLASH", "FIXED_ADDRESS"]
1387     ##
1388     # Adds platform report to the list
1389     #
1390     # This function adds a platform report to the final report list.
1391     #
1392     # @param self            The object pointer
1393     # @param Wa              Workspace context information
1394     # @param MaList          The list of modules in the platform build
1395     #
1396     def AddPlatformReport(self, Wa, MaList=None):
1397         if self.ReportFile:
1398             self.ReportList.append((Wa, MaList))
1399
1400     ##
1401     # Generates the final report.
1402     #
1403     # This function generates platform build report. It invokes GenerateReport()
1404     # method for every platform report in the list.
1405     #
1406     # @param self            The object pointer
1407     # @param BuildDuration   The total time to build the modules
1408     #
1409     def GenerateReport(self, BuildDuration):
1410         if self.ReportFile:
1411             try:
1412                 File = open(self.ReportFile, "w+")
1413             except IOError:
1414                 EdkLogger.error(None, FILE_OPEN_FAILURE, ExtraData=self.ReportFile)
1415             try:
1416                 for (Wa, MaList) in self.ReportList:
1417                     PlatformReport(Wa, MaList, self.ReportType).GenerateReport(File, BuildDuration, self.ReportType)
1418                 EdkLogger.quiet("Report successfully saved to %s" % os.path.abspath(self.ReportFile))
1419             except IOError:
1420                 EdkLogger.error(None, FILE_WRITE_FAILURE, ExtraData=self.ReportFile)
1421             except:
1422                 EdkLogger.error("BuildReport", CODE_ERROR, "Unknown fatal error when generating build report", ExtraData=self.ReportFile, RaiseError=False)
1423                 EdkLogger.quiet("(Python %s on %s\n%s)" % (platform.python_version(), sys.platform, traceback.format_exc()))
1424             File.close()
1425
1426 # This acts like the main() function for the script, unless it is 'import'ed into another script.
1427 if __name__ == '__main__':
1428     pass
1429