9e4507ad3b975759257b5cafef9a5cbc7e3a0cc7
[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.InfClassObject import gComponentType2ModuleType
27 from Common.BuildToolError import FILE_OPEN_FAILURE
28 from Common.BuildToolError import FILE_WRITE_FAILURE
29
30
31 ## Pattern to extract contents in EDK DXS files
32 gDxsDependencyPattern = re.compile(r"DEPENDENCY_START(.+)DEPENDENCY_END", re.DOTALL)
33
34 ## Pattern to find total FV total size, occupied size in flash report intermediate file
35 gFvTotalSizePattern = re.compile(r"EFI_FV_TOTAL_SIZE = (0x[0-9a-fA-F]+)")
36 gFvTakenSizePattern = re.compile(r"EFI_FV_TAKEN_SIZE = (0x[0-9a-fA-F]+)")
37
38 ## Pattern to find module size and time stamp in module summary report intermediate file  
39 gModuleSizePattern = re.compile(r"MODULE_SIZE = (\d+)")
40 gTimeStampPattern  = re.compile(r"TIME_STAMP = (\d+)") 
41
42 ## Pattern to find GUID value in flash description files
43 gPcdGuidPattern = re.compile(r"PCD\((\w+)[.](\w+)\)")
44
45 ## Pattern to collect offset, GUID value pair in the flash report intermediate file 
46 gOffsetGuidPattern = re.compile(r"(0x[0-9A-Fa-f]+) ([-A-Fa-f0-9]+)")
47
48 ## Tags for section start, end and separator
49 gSectionStart = ">" + "=" * 118 + "<"
50 gSectionEnd = "<" + "=" * 118 + ">" + "\n"
51 gSectionSep = "=" * 120
52
53 ## Tags for subsection start, end and separator
54 gSubSectionStart = ">" + "-" * 118 + "<"
55 gSubSectionEnd = "<" + "-" * 118 + ">"
56 gSubSectionSep = "-" * 120
57
58 ## The look up table to map PCD type to pair of report display type and DEC type
59 gPcdTypeMap = {
60   'FixedAtBuild'     : ('FIXED',  'FixedAtBuild'),
61   'PatchableInModule': ('PATCH',  'PatchableInModule'),
62   'FeatureFlag'      : ('FLAG',   'FeatureFlag'),
63   'Dynamic'          : ('DYN',    'Dynamic'),
64   'DynamicHii'       : ('DYNHII', 'Dynamic'),
65   'DynamicVpd'       : ('DYNVPD', 'Dynamic'),
66   'DynamicEx'        : ('DEX',    'Dynamic'),
67   'DynamicExHii'     : ('DEXHII', 'Dynamic'),
68   'DynamicExVpd'     : ('DEXVPD', 'Dynamic'),
69   }
70
71 ## The look up table to map module type to driver type
72 gDriverTypeMap = {
73   'SEC'               : '0x3 (SECURITY_CORE)',
74   'PEI_CORE'          : '0x4 (PEI_CORE)',
75   'PEIM'              : '0x6 (PEIM)',
76   'DXE_CORE'          : '0x5 (DXE_CORE)',
77   'DXE_DRIVER'        : '0x7 (DRIVER)',
78   'DXE_SAL_DRIVER'    : '0x7 (DRIVER)',
79   'DXE_SMM_DRIVER'    : '0x7 (DRIVER)',
80   'DXE_RUNTIME_DRIVER': '0x7 (DRIVER)',
81   'UEFI_DRIVER'       : '0x7 (DRIVER)',
82   'UEFI_APPLICATION'  : '0x9 (APPLICATION)',
83   'SMM_CORE'          : '0xD (SMM_CORE)',
84   }
85
86 ##
87 # Writes a string to the file object.
88
89 # This function writes a string to the file object and a new line is appended 
90 # afterwards. It may optionally wraps the string for better readability.   
91 #
92 # @File                      The file object to write
93 # @String                    The string to be written to the file
94 # @Wrapper                   Indicates whether to wrap the string
95 #
96 def FileWrite(File, String, Wrapper=False):
97     if Wrapper:
98         String = textwrap.fill(String, 120)
99     File.write(String + "\n")
100
101
102 ##
103 # Reports library information
104 #
105 # This class reports the module library subsection in the build report file.
106 #     
107 class LibraryReport(object):
108     ##
109     # Constructor function for class LibraryReport
110     #
111     # This constructor function generates LibraryReport object for 
112     # a module.
113     #
114     # @param self            The object pointer
115     # @param M               Module context information
116     #
117     def __init__(self, M):
118         self.LibraryList = []
119         if int(str(M.AutoGenVersion), 0) >= 0x00010005:
120             self._EdkIIModule = True
121         else:
122             self._EdkIIModule = False
123                
124         for Lib in M.DependentLibraryList:
125             LibInfPath = str(Lib)
126             LibClassList = Lib.LibraryClass[0].LibraryClass
127             LibConstructorList = Lib.ConstructorList
128             LibDesstructorList = Lib.DestructorList
129             LibDepexList = Lib.DepexExpression[M.Arch, M.ModuleType]
130             self.LibraryList.append((LibInfPath, LibClassList, LibConstructorList, LibDesstructorList, LibDepexList))
131     
132     ##
133     # Generate report for module library information
134     #
135     # This function generates report for the module library.
136     # If the module is EDKII style one, the additional library class, library
137     # constructor/destructor and dependency expression may also be reported.  
138     #
139     # @param self            The object pointer
140     # @param File            The file object for report
141     #
142     def GenerateReport(self, File):
143         FileWrite(File, gSubSectionStart)
144         FileWrite(File, "Library")
145         if len(self.LibraryList) > 0:
146             FileWrite(File, gSubSectionSep)
147             for LibraryItem in self.LibraryList:
148                 LibInfPath = LibraryItem[0]
149                 FileWrite(File, LibInfPath)
150                 
151                 #
152                 # Report library class, library constructor and destructor for
153                 # EDKII style module.
154                 #
155                 if self._EdkIIModule:
156                     LibClass = LibraryItem[1]
157                     EdkIILibInfo = ""
158                     LibConstructor = " ".join(LibraryItem[2])
159                     if LibConstructor:
160                         EdkIILibInfo += " C = " + LibConstructor
161                     LibDestructor = " ".join(LibraryItem[3])
162                     if LibDestructor:
163                         EdkIILibInfo += " D = " + LibConstructor
164                     LibDepex = " ".join(LibraryItem[3])
165                     if LibDepex:
166                         EdkIILibInfo += " Depex = " + LibDepex
167                     if EdkIILibInfo:
168                         FileWrite(File, "{%s: %s}" % (LibClass, EdkIILibInfo))
169                     else:
170                         FileWrite(File, "{%s}" % LibClass)
171         
172         FileWrite(File, gSubSectionEnd)
173
174 ##
175 # Reports dependency expression information
176 #
177 # This class reports the module dependency expression subsection in the build report file.
178 #             
179 class DepexReport(object):
180     ##
181     # Constructor function for class DepexReport
182     #
183     # This constructor function generates DepexReport object for 
184     # a module. If the module source contains the DXS file (usually EDK
185     # style module), it uses the dependency in DXS file; otherwise,
186     # it uses the dependency expression from its own INF [Depex] section
187     # and then merges with the ones from its dependent library INF.
188     #
189     # @param self            The object pointer
190     # @param M               Module context information
191     #
192     def __init__(self, M):
193         for Source in M.SourceFileList:
194             if os.path.splitext(Source.Path)[1].lower() == ".dxs":
195                 Match = gDxsDependencyPattern.search(open(Source.Path).read())
196                 if Match:
197                     self.Depex = Match.group(1).strip()
198                     self.Source = "DXS"
199                     break
200         else:
201             self.Depex = M.DepexExpressionList[M.ModuleType]
202             self.ModuleDepex = " ".join(M.Module.DepexExpression[M.Arch, M.ModuleType])
203             if not self.ModuleDepex:
204                 self.ModuleDepex = "TRUE"
205             
206             LibDepexList = []
207             for Lib in M.DependentLibraryList:
208                 LibDepex = " ".join(Lib.DepexExpression[M.Arch, M.ModuleType]).strip()
209                 if LibDepex != "":                         
210                     if " " in LibDepex:
211                         LibDepex = "(" + LibDepex + ")"
212                     LibDepexList.append(LibDepex)
213             self.LibraryDepex = " AND ".join(LibDepexList)
214             if not self.LibraryDepex:
215                 self.LibraryDepex = "(None)"
216             self.Source = "INF"
217     
218     ##
219     # Generate report for module dependency expression information
220     #
221     # This function generates report for the module dependency expression.
222     #
223     # @param self            The object pointer
224     # @param File            The file object for report
225     #
226     def GenerateReport(self, File):
227         FileWrite(File, gSubSectionStart)
228         FileWrite(File, "Dependency Expression (DEPEX) from %s" % self.Source)
229         
230         if self.Source == "INF":
231             FileWrite(File, "%s" % self.Depex, True)
232             FileWrite(File, gSubSectionSep)
233             FileWrite(File, "From Module INF:  %s" % self.ModuleDepex, True)
234             FileWrite(File, "From Library INF: %s" % self.LibraryDepex, True)
235         else:
236             FileWrite(File, "%s" % self.Depex)
237         FileWrite(File, gSubSectionEnd)
238
239 ##
240 # Reports dependency expression information
241 #
242 # This class reports the module build flags subsection in the build report file.
243 #                     
244 class BuildFlagsReport(object):
245     ##
246     # Constructor function for class BuildFlagsReport
247     #
248     # This constructor function generates BuildFlagsReport object for 
249     # a module. It reports the build tool chain tag and all relevant 
250     # build flags to build the module.
251     #
252     # @param self            The object pointer
253     # @param M               Module context information
254     #
255     def __init__(self, M):
256         BuildOptions = {}
257         #
258         # Add build flags according to source file extension so that
259         # irrelevant ones can be filtered out. 
260         #
261         for Source in M.SourceFileList:
262             Ext = os.path.splitext(Source.File)[1].lower()
263             if Ext in [".c", ".cc", ".cpp"]:
264                 BuildOptions["CC"] = 1
265             elif Ext in [".s", ".asm"]:
266                 BuildOptions["PP"] = 1
267                 BuildOptions["ASM"] = 1
268             elif Ext in [".vfr"]:
269                 BuildOptions["VFRPP"] = 1
270                 BuildOptions["VFR"] = 1
271             elif Ext in [".dxs"]:
272                 BuildOptions["APP"] = 1
273                 BuildOptions["CC"] = 1
274             elif Ext in [".asl"]:
275                 BuildOptions["ASLPP"] = 1
276                 BuildOptions["ASL"] = 1
277             elif Ext in [".aslc"]:
278                 BuildOptions["ASLCC"] = 1
279                 BuildOptions["ASLDLINK"] = 1
280                 BuildOptions["CC"] = 1
281             elif Ext in [".asm16"]:
282                 BuildOptions["ASMLINK"] = 1
283             BuildOptions["SLINK"] = 1
284             BuildOptions["DLINK"] = 1
285         
286         #
287         # Save module build flags.
288         #
289         self.ToolChainTag = M.ToolChain
290         self.BuildFlags = {}
291         for Tool in BuildOptions:
292             self.BuildFlags[Tool + "_FLAGS"] = M.BuildOption.get(Tool, {}).get("FLAGS", "")
293
294     ##
295     # Generate report for module build flags information
296     #
297     # This function generates report for the module build flags expression.
298     #
299     # @param self            The object pointer
300     # @param File            The file object for report
301     #
302     def GenerateReport(self, File):
303         FileWrite(File, gSubSectionStart)
304         FileWrite(File, "Build Flags")
305         FileWrite(File, "Tool Chain Tag: %s" % self.ToolChainTag)
306         for Tool in self.BuildFlags:
307             FileWrite(File, gSubSectionSep)
308             FileWrite(File, "%s = %s" % (Tool, self.BuildFlags[Tool]), True)
309         
310         FileWrite(File, gSubSectionEnd)
311
312
313 ##
314 # Reports individual module information
315 #
316 # This class reports the module section in the build report file. 
317 # It comprises of module summary, module PCD, library, dependency expression,
318 # build flags sections.  
319 #        
320 class ModuleReport(object):
321     ##
322     # Constructor function for class ModuleReport
323     #
324     # This constructor function generates ModuleReport object for 
325     # a separate module in a platform build.  
326     #
327     # @param self            The object pointer
328     # @param M               Module context information
329     # @param DscOverridePcds Module DSC override PCD information
330     # @param ReportType      The kind of report items in the final report file
331     #
332     def __init__(self, M, DscOverridePcds, ReportType):
333         self.ModuleName = M.Module.BaseName
334         self.ModuleInfPath = M.MetaFile.File
335         self.FileGuid = M.Guid
336         self.Size = 0
337         self.BuildTimeStamp = None
338         self.DriverType = ""
339         ModuleType = M.ModuleType
340         if not ModuleType:
341             ModuleType = gComponentType2ModuleType.get(M.ComponentType, "")
342         self.DriverType = gDriverTypeMap.get(ModuleType, "")
343         self.UefiSpecVersion = M.Module.Specification.get("UEFI_SPECIFICATION_VERSION", "")
344         self.PiSpecVersion = M.Module.Specification.get("PI_SPECIFICATION_VERSION", "")
345         self.PciDeviceId = M.Module.Defines.get("PCI_DEVICE_ID", "")
346         self.PciVendorId = M.Module.Defines.get("PCI_VENDOR_ID", "")
347         self.PciClassCode = M.Module.Defines.get("PCI_CLASS_CODE", "")
348   
349         self._BuildDir = M.BuildDir
350         self.ModulePcdSet = {}
351         self.ModuleDscOverridePcds = {}
352         if "PCD" in ReportType:
353             #
354             # Collect all module used PCD set: module INF referenced directly or indirectly.
355             # It also saves module INF default values of them in case they exist.
356             #
357             for Pcd in M.ModulePcdList + M.LibraryPcdList:
358                 self.ModulePcdSet.setdefault((Pcd.TokenCName, Pcd.TokenSpaceGuidCName, Pcd.Type), Pcd.InfDefaultValue)
359             
360             #
361             # Collect module DSC override PCD set for report
362             #
363             for (PcdTokenCName, PcdTokenSpaceGuidCName) in DscOverridePcds:
364                 Pcd = DscOverridePcds[(PcdTokenCName, PcdTokenSpaceGuidCName)]
365                 self.ModuleDscOverridePcds.setdefault((PcdTokenCName, PcdTokenSpaceGuidCName), Pcd.DefaultValue)
366         
367         self.LibraryReport = None
368         if "LIBRARY" in ReportType:
369             self.LibraryReport = LibraryReport(M)
370         
371         self.DepexReport = None
372         if "DEPEX" in ReportType:
373             self.DepexReport = DepexReport(M)
374         
375         if "BUILD_FLAGS" in ReportType:
376             self.BuildFlagsReport = BuildFlagsReport(M)
377         
378     
379     ##
380     # Generate report for module information
381     #
382     # This function generates report for separate module expression
383     # in a platform build.
384     #
385     # @param self            The object pointer
386     # @param File            The file object for report
387     # @param GlobalPcdReport The platform global PCD class object
388     # @param ReportType      The kind of report items in the final report file
389     #
390     def GenerateReport(self, File, GlobalPcdReport, ReportType):
391         FileWrite(File, gSectionStart)
392         
393         FwReportFileName = os.path.join(self._BuildDir, "DEBUG", self.ModuleName + ".txt")
394         if os.path.isfile(FwReportFileName):
395             try:
396                 FileContents = open(FwReportFileName).read()
397                 Match = gModuleSizePattern.search(FileContents)
398                 if Match:
399                     self.Size = int(Match.group(1))
400                 
401                 Match = gTimeStampPattern.search(FileContents)
402                 if Match:
403                     self.BuildTimeStamp = datetime.utcfromtimestamp(int(Match.group(1)))
404             except IOError:
405                 EdkLogger.warn(None, "Fail to read report file", FwReportFileName)
406             
407         FileWrite(File, "Module Summary")
408         FileWrite(File, "Module Name:          %s" % self.ModuleName)
409         FileWrite(File, "Module INF Path:      %s" % self.ModuleInfPath)
410         FileWrite(File, "File GUID:            %s" % self.FileGuid)
411         if self.Size:
412             FileWrite(File, "Size:                 0x%X (%.2fK)" % (self.Size, self.Size / 1024.0))
413         if self.BuildTimeStamp:
414             FileWrite(File, "Build Time Stamp:     %s" % self.BuildTimeStamp)
415         if self.DriverType:
416             FileWrite(File, "Driver Type:          %s" % self.DriverType)
417         if self.UefiSpecVersion:
418             FileWrite(File, "UEFI Spec Version:    %s" % self.DriverType)
419         if self.PiSpecVersion:
420             FileWrite(File, "PI Spec Version:      %s" % self.PiSpecVersion)
421         if self.PciDeviceId:
422             FileWrite(File, "PCI Device ID:        %s" % self.PciDeviceId)
423         if self.PciVendorId:
424             FileWrite(File, "PCI Vendor ID:        %s" % self.PciVendorId)
425         if self.PciClassCode:
426             FileWrite(File, "PCI Class Code:       %s" % self.PciClassCode)
427         
428         FileWrite(File, gSectionSep)   
429         
430         if "PCD" in ReportType:
431             GlobalPcdReport.GenerateReport(File, self.ModulePcdSet, self.ModuleDscOverridePcds)
432         
433         if "LIBRARY" in ReportType:
434             self.LibraryReport.GenerateReport(File)
435         
436         if "DEPEX" in ReportType:
437             self.DepexReport.GenerateReport(File)
438         
439         if "BUILD_FLAGS" in ReportType:
440             self.BuildFlagsReport.GenerateReport(File)
441         
442         FileWrite(File, gSectionEnd)
443
444 ##
445 # Reports platform and module PCD information
446 #
447 # This class reports the platform PCD section and module PCD subsection
448 # in the build report file.
449 #     
450 class PcdReport(object):
451     ##
452     # Constructor function for class PcdReport
453     #
454     # This constructor function generates PcdReport object a platform build.
455     # It collects the whole PCD database from platform DSC files, platform
456     # flash description file and package DEC files.
457     #
458     # @param self            The object pointer
459     # @param Wa              Workspace context information
460     #
461     def __init__(self, Wa):
462         self.AllPcds = {}
463         self.MaxLen = 0
464         self.FdfPcdSet = Wa.FdfProfile.PcdDict
465
466         self.DecPcdDefault = {}
467         self.ModulePcdOverride = {}
468         for Pa in Wa.AutoGenObjectList:
469             #
470             # Collect all platform referenced PCDs and grouped them by PCD token space
471             # GUID C Names
472             #
473             for Pcd in Pa.AllPcdList:
474                 PcdList = self.AllPcds.setdefault(Pcd.TokenSpaceGuidCName, {}).setdefault(Pcd.Type, [])
475                 if Pcd not in PcdList:
476                     PcdList.append(Pcd)
477                 if len(Pcd.TokenCName) > self.MaxLen:
478                     self.MaxLen = len(Pcd.TokenCName)
479             
480             for ModuleKey in Pa.Platform.Modules:
481                 #
482                 # Collect PCD DEC default value.
483                 #   
484                 Module = Pa.Platform.Modules[ModuleKey]
485                 for Package in Module.M.Module.Packages:
486                     for (TokenCName, TokenSpaceGuidCName, DecType) in Package.Pcds:
487                         DecDefaultValue = Package.Pcds[TokenCName, TokenSpaceGuidCName, DecType].DefaultValue
488                         self.DecPcdDefault.setdefault((TokenCName, TokenSpaceGuidCName, DecType), DecDefaultValue)
489                 #
490                 # Collect module override PCDs
491                 #
492                 for ModulePcd in Module.M.ModulePcdList + Module.M.LibraryPcdList:
493                     TokenCName = ModulePcd.TokenCName
494                     TokenSpaceGuid = ModulePcd.TokenSpaceGuidCName
495                     ModuleDefault = ModulePcd.DefaultValue
496                     ModulePath = os.path.basename(Module.M.MetaFile.File)
497                     self.ModulePcdOverride.setdefault((TokenCName, TokenSpaceGuid), []).append((ModuleDefault, ModulePath))
498
499         #
500         # Collect PCDs defined in DSC common section
501         #
502         self.DscPcdDefault = {}
503         for Platform in Wa.BuildDatabase.WorkspaceDb.PlatformList:
504             for (TokenCName, TokenSpaceGuidCName) in Platform.Pcds:
505                 DscDefaultValue = Platform.Pcds[(TokenCName, TokenSpaceGuidCName)].DefaultValue
506                 self.DscPcdDefault[(TokenCName, TokenSpaceGuidCName)] = DscDefaultValue
507     
508     ##
509     # Generate report for PCD information
510     #
511     # This function generates report for separate module expression
512     # in a platform build.
513     #
514     # @param self            The object pointer
515     # @param File            The file object for report
516     # @param ModulePcdSet    Set of all PCDs referenced by module or None for
517     #                        platform PCD report
518     # @param DscOverridePcds Module DSC override PCDs set
519     #
520     def GenerateReport(self, File, ModulePcdSet, DscOverridePcds):
521         if ModulePcdSet == None:
522             #
523             # For platform global PCD section
524             #
525             FileWrite(File, gSectionStart)
526             FileWrite(File, "Platform Configuration Database Report")
527             FileWrite(File, "  *P  - Platform scoped PCD override in DSC file")
528             FileWrite(File, "  *F  - Platform scoped PCD override in FDF file")
529             FileWrite(File, "  *M  - Module scoped PCD override in DSC file")
530             FileWrite(File, gSectionSep)
531         else:
532             #
533             # For module PCD sub-section
534             #
535             FileWrite(File, gSubSectionStart)
536             FileWrite(File, "PCD")
537             FileWrite(File, gSubSectionSep)
538
539         for Key in self.AllPcds:
540             #
541             # Group PCD by their token space GUID C Name
542             #
543             First = True
544             for Type in self.AllPcds[Key]:
545                 #
546                 # Group PCD by their usage type
547                 #
548                 TypeName, DecType = gPcdTypeMap.get(Type, ("", Type))
549                 for Pcd in self.AllPcds[Key][Type]:
550                     #
551                     # Get PCD default value and their override relationship
552                     #
553                     InfDefaultValue = None
554                     if ModulePcdSet != None:
555                         if (Pcd.TokenCName, Pcd.TokenSpaceGuidCName, Type) not in ModulePcdSet:
556                             continue
557                         InfDefault = ModulePcdSet[Pcd.TokenCName, Pcd.TokenSpaceGuidCName, Type]
558                         if InfDefault == "":
559                             InfDefault = None
560                     if First:
561                         if ModulePcdSet == None:
562                             FileWrite(File, "")
563                         FileWrite(File, Key)
564                         First = False
565                     DecDefaultValue = self.DecPcdDefault.get((Pcd.TokenCName, Pcd.TokenSpaceGuidCName, DecType))
566                     DscDefaultValue = self.DscPcdDefault.get((Pcd.TokenCName, Pcd.TokenSpaceGuidCName))
567                     DscModuleOverrideValue = DscOverridePcds.get((Pcd.TokenCName, Pcd.TokenSpaceGuidCName))
568                           
569                     if Pcd.DatumType in ('UINT8', 'UINT16', 'UINT32', 'UINT64'):
570                         PcdDefaultValueNumber = int(Pcd.DefaultValue.strip(), 0)
571                         if DecDefaultValue == None:
572                             DecMatch = True
573                         else:
574                             DecDefaultValueNumber = int(DecDefaultValue.strip(), 0)
575                             DecMatch = (DecDefaultValueNumber == PcdDefaultValueNumber)
576                   
577                         if InfDefaultValue == None:
578                             InfMatch = True
579                         else:
580                             InfDefaultValueNumber = int(InfDefaultValue.strip(), 0)
581                             InfMatch = (InfDefaultValueNumber == PcdDefaultValueNumber)
582                                 
583                         if DscDefaultValue == None:
584                             DscMatch = True
585                         else:
586                             DscDefaultValueNumber = int(DscDefaultValue.strip(), 0)
587                             DscMatch = (DscDefaultValueNumber == PcdDefaultValueNumber)
588                     else:
589                         if DecDefaultValue == None:
590                             DecMatch = True
591                         else:
592                             DecMatch = (DecDefaultValue == Pcd.DefaultValue)
593                   
594                         if InfDefaultValue == None:
595                             InfMatch = True
596                         else:
597                             InfMatch = (InfDefaultValue == Pcd.DefaultValue)
598                             
599                         if DscDefaultValue == None:
600                             DscMatch = True
601                         else:
602                             DscMatch = (DscDefaultValue == Pcd.DefaultValue)
603                     
604                     #
605                     # Report PCD item according to their override relationship
606                     #        
607                     if DecMatch and InfMatch:
608                         FileWrite(File, '    %-*s: %6s %10s = %-22s' % (self.MaxLen, Pcd.TokenCName, TypeName, '('+Pcd.DatumType+')', Pcd.DefaultValue))
609                     else:
610                         if DscMatch and DscModuleOverrideValue == None:
611                             if (Pcd.TokenCName, Key) in self.FdfPcdSet:
612                                 FileWrite(File, ' *F %-*s: %6s %10s = %-22s' % (self.MaxLen, Pcd.TokenCName, TypeName, '('+Pcd.DatumType+')', Pcd.DefaultValue))
613                             else:
614                                 FileWrite(File, ' *P %-*s: %6s %10s = %-22s' % (self.MaxLen, Pcd.TokenCName, TypeName, '('+Pcd.DatumType+')', Pcd.DefaultValue))
615                         else:
616                             FileWrite(File, ' *M %-*s: %6s %10s = %-22s' % (self.MaxLen, Pcd.TokenCName, TypeName, '('+Pcd.DatumType+')', Pcd.DefaultValue))
617                             if DscDefaultValue != None:
618                                 FileWrite(File, '    %*s = %s' % (self.MaxLen + 19, 'DSC DEFAULT', DscDefaultValue))
619                         
620                         if InfDefaultValue != None:
621                             FileWrite(File, '    %*s = %s' % (self.MaxLen + 19, 'INF DEFAULT', InfDefaultValue))
622                         
623                         if DecDefaultValue != None and not DecMatch:
624                             FileWrite(File, '    %*s = %s' % (self.MaxLen + 19, 'DEC DEFAULT', DecDefaultValue))
625
626                     if ModulePcdSet == None:
627                         for (ModuleDefault, ModulePath) in self.ModulePcdOverride.get((Pcd.TokenCName, Pcd.TokenSpaceGuidCName), []):
628                             if Pcd.DatumType in ('UINT8', 'UINT16', 'UINT32', 'UINT64'):
629                                 ModulePcdDefaultValueNumber = int(ModuleDefault.strip(), 0)
630                                 Match = (ModulePcdDefaultValueNumber == PcdDefaultValueNumber)
631                             else:
632                                 Match = (ModuleDefault == Pcd.DefaultValue)
633                             if Match:
634                                 continue
635                             FileWrite(File, ' *M %-*s = %s' % (self.MaxLen + 19, ModulePath, ModuleDefault))
636         
637         if ModulePcdSet == None:
638             FileWrite(File, gSectionEnd)
639         else:
640             FileWrite(File, gSubSectionEnd)     
641
642
643 ##
644 # Reports FD region information
645 #
646 # This class reports the FD subsection in the build report file.
647 # It collects region information of platform flash device. 
648 # If the region is a firmware volume, it lists the set of modules
649 # and its space information; otherwise, it only lists its region name,
650 # base address and size in its sub-section header.
651 # If there are nesting FVs, the nested FVs will list immediate after
652 # this FD region subsection
653 #
654 class FdRegionReport(object):
655     ##
656     # Discover all the nested FV name list.
657     #
658     # This is an internal worker function to discover the all the nested FV information
659     # in the parent firmware volume. It uses deep first search algorithm recursively to
660     # find all the FV list name and append them to the list.
661     #
662     # @param self            The object pointer
663     # @param FvName          The name of current firmware file system
664     # @param Wa              Workspace context information
665     #
666     def _DiscoverNestedFvList(self, FvName, Wa):
667         for Ffs in Wa.FdfProfile.FvDict[FvName.upper()].FfsList:
668             for Section in Ffs.SectionList:
669                 try:
670                     for FvSection in Section.SectionList:
671                         if FvSection.FvName in self.FvList:
672                             continue
673                         self._GuidsDb[Ffs.NameGuid.upper()] = FvSection.FvName
674                         self.FvList.append(FvSection.FvName)
675                         self.FvInfo[FvSection.FvName] = ("Nested FV", 0, 0)
676                         self._DiscoverNestedFvList(FvSection.FvName, Wa)
677                 except AttributeError:
678                     pass
679     
680     ##
681     # Constructor function for class FdRegionReport
682     #
683     # This constructor function generates FdRegionReport object for a specified FdRegion. 
684     # If the FdRegion is a firmware volume, it will recursively find all its nested Firmware
685     # volume list. This function also collects GUID map in order to dump module identification
686     # in the final report.
687     #
688     # @param self:           The object pointer
689     # @param FdRegion        The current FdRegion object
690     # @param Wa              Workspace context information
691     #
692     def __init__(self, FdRegion, Wa):
693         self.Type = FdRegion.RegionType
694         self.BaseAddress = FdRegion.Offset
695         self.Size = FdRegion.Size
696         self.FvList = []
697         self.FvInfo = {}
698         self._GuidsDb = {}
699         self._FvDir = Wa.FvDir
700
701         #
702         # If the input FdRegion is not a firmware volume,
703         # we are done. 
704         #
705         if self.Type != "FV":
706             return
707         
708         #
709         # Find all nested FVs in the FdRegion
710         #
711         for FvName in FdRegion.RegionDataList:
712             if FvName in self.FvList:
713                 continue
714             self.FvList.append(FvName)
715             self.FvInfo[FvName] = ("Fd Region", self.BaseAddress, self.Size)
716             self._DiscoverNestedFvList(FvName, Wa)
717
718         PlatformPcds = {}
719         for Pa in Wa.AutoGenObjectList:
720             PackageList = []
721             for ModuleKey in Pa.Platform.Modules:
722                 #
723                 # Collect PCD DEC default value.
724                 #   
725                 Module = Pa.Platform.Modules[ModuleKey]
726                 for Package in Module.M.Module.Packages:
727                     if Package not in PackageList:
728                         PackageList.append(Package)
729                 
730             for Package in PackageList:
731                 for (TokenCName, TokenSpaceGuidCName, DecType) in Package.Pcds:
732                     DecDefaultValue = Package.Pcds[TokenCName, TokenSpaceGuidCName, DecType].DefaultValue
733                     PlatformPcds[(TokenCName, TokenSpaceGuidCName)] = DecDefaultValue
734         
735         #
736         # Collect PCDs defined in DSC common section
737         #
738         for Platform in Wa.BuildDatabase.WorkspaceDb.PlatformList:
739             for (TokenCName, TokenSpaceGuidCName) in Platform.Pcds:
740                 DscDefaultValue = Platform.Pcds[(TokenCName, TokenSpaceGuidCName)].DefaultValue
741                 PlatformPcds[(TokenCName, TokenSpaceGuidCName)] = DscDefaultValue
742         
743         #
744         # Add PEI and DXE a priori files GUIDs defined in PI specification.
745         #
746         self._GuidsDb["1B45CC0A-156A-428A-AF62-49864DA0E6E6"] = "PEI Apriori"
747         self._GuidsDb["FC510EE7-FFDC-11D4-BD41-0080C73C8881"] = "DXE Apriori" 
748         #
749         # Add ACPI table storage file
750         #
751         self._GuidsDb["7E374E25-8E01-4FEE-87F2-390C23C606CD"] = "ACPI table storage"
752         #
753         # Collect the GUID map in the FV firmware volume
754         #
755         for FvName in self.FvList:
756             for Ffs in Wa.FdfProfile.FvDict[FvName.upper()].FfsList:
757                 try:
758                     #
759                     # Collect GUID, module mapping from INF file
760                     #
761                     InfFileName = Ffs.InfFileName
762                     try:
763                         FileGuid = ""
764                         ModuleName = ""
765                         InfPath = os.path.join(Wa.WorkspaceDir, InfFileName)
766                         for Line in open(InfPath):
767                             ItemList = Line.split("#")[0].split("=")
768                             if len(ItemList) == 2:
769                                 Key   = ItemList[0].strip().upper()
770                                 Value = ItemList[1].strip()
771                                 if Key == "FILE_GUID":
772                                     FileGuid = Value.upper()
773                                 if Key == "BASE_NAME":
774                                     ModuleName = Value
775                         if FileGuid:
776                             self._GuidsDb[FileGuid] = "%s (%s)" % (ModuleName, InfPath)
777                     except IOError:
778                         EdkLogger.warn(None, "Cannot open file to read", InfPath)
779                 except AttributeError:
780                     try:
781                         #
782                         # collect GUID map for binary EFI file in FDF file.
783                         #
784                         Guid = Ffs.NameGuid.upper()
785                         Match = gPcdGuidPattern.match(Ffs.NameGuid)
786                         if Match:
787                             PcdTokenspace = Match.group(1)
788                             PcdToken = Match.group(2)
789                             if (PcdToken, PcdTokenspace) in PlatformPcds:
790                                 GuidValue = PlatformPcds[(PcdToken, PcdTokenspace)]
791                                 Guid = GuidStructureByteArrayToGuidString(GuidValue).upper()
792
793                         for Section in Ffs.SectionList:
794                             try:
795                                 ModuleSectFile = os.path.join(Wa.WorkspaceDir, Section.SectFileName)
796                                 self._GuidsDb[Guid] = ModuleSectFile
797                             except AttributeError:
798                                 pass
799                     except AttributeError:
800                         pass
801         
802     
803     ##
804     # Internal worker function to generate report for the FD region
805     #
806     # This internal worker function to generate report for the FD region.
807     # It the type is firmware volume, it lists offset and module identification. 
808     #
809     # @param self            The object pointer
810     # @param File            The file object for report
811     # @param Title           The title for the FD subsection 
812     # @param BaseAddress     The base address for the FD region
813     # @param Size            The size of the FD region
814     # @param FvName          The FV name if the FD region is a firmware volume
815     #
816     def _GenerateReport(self, File, Title, Type, BaseAddress, Size=0, FvName=None):
817         FileWrite(File, gSubSectionStart)
818         FileWrite(File, Title)
819         FileWrite(File, "Type:               %s" % Type)
820         FileWrite(File, "Base Address:       0x%X" % BaseAddress)
821         
822         if self.Type == "FV":
823             FvTotalSize = 0
824             FvTakenSize = 0
825             FvFreeSize  = 0
826             FvReportFileName = os.path.join(self._FvDir, FvName + ".fv.txt")
827             try:
828                 #
829                 # Collect size info in the firmware volume.
830                 #
831                 FvReport = open(FvReportFileName).read()
832                 Match = gFvTotalSizePattern.search(FvReport)
833                 if Match:
834                     FvTotalSize = int(Match.group(1), 16)
835                 Match = gFvTakenSizePattern.search(FvReport)
836                 if Match:
837                     FvTakenSize = int(Match.group(1), 16)
838                 FvFreeSize = FvTotalSize - FvTakenSize 
839                 #
840                 # Write size information to the report file.
841                 #
842                 FileWrite(File, "Size:               0x%X (%.0fK)" % (FvTotalSize, FvTotalSize / 1024.0))
843                 FileWrite(File, "Fv Name:            %s (%.1f%% Full)" % (FvName, FvTakenSize * 100.0 / FvTotalSize))
844                 FileWrite(File, "Occupied Size:      0x%X (%.0fK)" % (FvTakenSize, FvTakenSize / 1024.0))
845                 FileWrite(File, "Free Size:          0x%X (%.0fK)" % (FvFreeSize, FvFreeSize / 1024.0))
846                 FileWrite(File, "Offset     Module")
847                 FileWrite(File, gSubSectionSep)
848                 #
849                 # Write module offset and module identification to the report file.
850                 #
851                 for Match in gOffsetGuidPattern.finditer(FvReport):
852                     Guid = Match.group(2).upper()
853                     Offset = int(Match.group(1), 16)
854                     FileWrite (File, "0x%07X %s" % (Offset, self._GuidsDb.get(Guid, Guid)))
855             except IOError:
856                 EdkLogger.warn(None, "Fail to read report file", FvReportFileName)
857         else:
858             FileWrite(File, "Size:               0x%X (%.0fK)" % (Size, Size / 1024.0))
859         FileWrite(File, gSubSectionEnd)
860
861     ##
862     # Generate report for the FD region
863     #
864     # This function generates report for the FD region. 
865     #
866     # @param self            The object pointer
867     # @param File            The file object for report
868     #
869     def GenerateReport(self, File):
870         if (len(self.FvList) > 0):
871             for FvItem in self.FvList:
872                 Info = self.FvInfo[FvItem]
873                 self._GenerateReport(File, Info[0], "FV", Info[1], Info[2], FvItem)
874         else:
875             self._GenerateReport(File, "FD Region", self.Type, self.BaseAddress, self.Size)    
876             
877 ##
878 # Reports FD information
879 #
880 # This class reports the FD section in the build report file.
881 # It collects flash device information for a platform. 
882 #
883 class FdReport(object):
884     ##
885     # Constructor function for class FdReport
886     #
887     # This constructor function generates FdReport object for a specified
888     # firmware device. 
889     #
890     # @param self            The object pointer
891     # @param Fd              The current Firmware device object
892     # @param Wa              Workspace context information
893     #
894     def __init__(self, Fd, Wa):
895         self.FdName = Fd.FdUiName
896         self.BaseAddress = Fd.BaseAddress
897         self.Size = Fd.Size
898         self.FdRegionList = [FdRegionReport(FdRegion, Wa) for FdRegion in Fd.RegionList]
899
900     ##
901     # Generate report for the firmware device.
902     #
903     # This function generates report for the firmware device. 
904     #
905     # @param self            The object pointer
906     # @param File            The file object for report
907     #
908     def GenerateReport(self, File):
909         FileWrite(File, gSectionStart)
910         FileWrite(File, "Firmware Device (FD)")
911         FileWrite(File, "FD Name:            %s" % self.FdName)
912         FileWrite(File, "Base Address:       0x%s" % self.BaseAddress)
913         FileWrite(File, "Size:               0x%X (%.0fK)" % (self.Size, self.Size / 1024.0))
914         if len(self.FdRegionList) > 0:
915             FileWrite(File, gSectionSep)
916             for FdRegionItem in self.FdRegionList:
917                 FdRegionItem.GenerateReport(File)
918         
919         FileWrite(File, gSectionEnd)
920
921     
922 ##
923 # Reports platform information
924 #
925 # This class reports the whole platform information 
926 #     
927 class PlatformReport(object):
928     ##
929     # Constructor function for class PlatformReport
930     #
931     # This constructor function generates PlatformReport object a platform build.
932     # It generates report for platform summary, flash, global PCDs and detailed
933     # module information for modules involved in platform build.
934     #
935     # @param self            The object pointer
936     # @param Wa              Workspace context information
937     #
938     def __init__(self, Wa, ReportType):
939         self._WorkspaceDir = Wa.WorkspaceDir
940         self.PlatformName = Wa.Name
941         self.PlatformDscPath = Wa.Platform
942         self.Architectures = " ".join(Wa.ArchList)
943         self.ToolChain = Wa.ToolChain
944         self.Target = Wa.BuildTarget
945         self.OutputPath = os.path.join(Wa.WorkspaceDir, Wa.OutputDir)
946         self.BuildEnvironment = platform.platform()
947         
948         self.PcdReport = None
949         if "PCD" in ReportType:
950             self.PcdReport = PcdReport(Wa)
951
952         self.FdReportList = []
953         if "FLASH" in ReportType and Wa.FdfProfile:
954             for Fd in Wa.FdfProfile.FdDict:
955                 self.FdReportList.append(FdReport(Wa.FdfProfile.FdDict[Fd], Wa))
956                 
957         self.ModuleReportList = []
958         for Pa in Wa.AutoGenObjectList:
959             for ModuleKey in Pa.Platform.Modules:
960                 for Platform in Wa.BuildDatabase.WorkspaceDb.PlatformList:
961                     if ModuleKey in Platform.Modules:
962                         DscOverridePcds = Platform.Modules[ModuleKey].Pcds
963                         break
964                 else:
965                     DscOverridePcds = {}
966                 self.ModuleReportList.append(ModuleReport(Pa.Platform.Modules[ModuleKey].M, DscOverridePcds, ReportType))
967  
968     ##
969     # Generate report for the whole platform.
970     #
971     # This function generates report for platform information.
972     # It comprises of platform summary, global PCD, flash and 
973     # module list sections.
974     #
975     # @param self            The object pointer
976     # @param File            The file object for report
977     # @param BuildDuration   The total time to build the modules
978     # @param ReportType      The kind of report items in the final report file
979     #
980     def GenerateReport(self, File, BuildDuration, ReportType):
981         FileWrite(File, "Platform Summary")
982         FileWrite(File, "Platform Name:        %s" % self.PlatformName)
983         FileWrite(File, "Platform DSC Path:    %s" % self.PlatformDscPath)
984         FileWrite(File, "Platform DSC Path:    %s" % self.Architectures)
985         FileWrite(File, "Tool Chain:           %s" % self.ToolChain)
986         FileWrite(File, "Target:               %s" % self.Target)
987         FileWrite(File, "Output Path:          %s" % self.OutputPath)
988         FileWrite(File, "Build Environment:    %s" % self.BuildEnvironment)
989         FileWrite(File, "Build Duration:       %s" % BuildDuration)
990         FileWrite(File, "Report Content:       %s" % ", ".join(ReportType))
991  
992         if "PCD" in ReportType:
993             self.PcdReport.GenerateReport(File, None, {})
994             
995         if "FLASH" in ReportType:
996             for FdReportListItem in self.FdReportList:
997                 FdReportListItem.GenerateReport(File)
998         
999         for ModuleReportItem in self.ModuleReportList:
1000             ModuleReportItem.GenerateReport(File, self.PcdReport, ReportType)
1001         
1002
1003 ## BuildReport class
1004 #
1005 #  This base class contain the routines to collect data and then
1006 #  applies certain format to the output report 
1007 #
1008 class BuildReport(object):
1009     ##
1010     # Constructor function for class BuildReport
1011     #
1012     # This constructor function generates BuildReport object a platform build.
1013     # It generates report for platform summary, flash, global PCDs and detailed
1014     # module information for modules involved in platform build.
1015     #
1016     # @param self            The object pointer
1017     # @param ReportFile      The file name to save report file
1018     # @param ReportType      The kind of report items in the final report file
1019     #
1020     def __init__(self, ReportFile, ReportType):
1021         self.ReportFile = ReportFile
1022         if ReportFile:
1023             self.ReportList = []
1024             if ReportType == None or "ALL" in ReportType:
1025                 self.ReportType = ["PCD", "LIBRARY", "BUILD_FLAGS", "DEPEX", "FLASH", "PREDICTION"]
1026             else:
1027                 for ReportTypeItem in ReportType:
1028                     if ReportTypeItem not in self.ReportType:
1029                         self.ReportType.append(ReportTypeItem)
1030     
1031     ##
1032     # Adds platform report to the list
1033     #
1034     # This function adds a platform report to the final report list.
1035     #
1036     # @param self            The object pointer
1037     # @param Wa              Workspace context information
1038     #           
1039     def AddPlatformReport(self, Wa):
1040         if self.ReportFile:
1041             self.ReportList.append(PlatformReport(Wa, self.ReportType))
1042
1043     ##
1044     # Generates the final report.
1045     #
1046     # This function generates platform build report. It invokes GenerateReport()
1047     # method for every platform report in the list.
1048     #
1049     # @param self            The object pointer
1050     # @param BuildDuration   The total time to build the modules
1051     # 
1052     def GenerateReport(self, BuildDuration):
1053         if self.ReportFile:
1054             try:
1055                 File = open(self.ReportFile, "w+")
1056             except IOError:
1057                 EdkLogger.error(None, FILE_OPEN_FAILURE, ExtraData=self.ReportFile)
1058             try:
1059                 for Report in self.ReportList:
1060                     Report.GenerateReport(File, BuildDuration, self.ReportType) 
1061             except IOError:
1062                 EdkLogger.error(None, FILE_WRITE_FAILURE, ExtraData=self.ReportFile)
1063             File.close()
1064         
1065 # This acts like the main() function for the script, unless it is 'import'ed into another script.
1066 if __name__ == '__main__':
1067     pass
1068