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