2 # Routines for generating build report.
4 # This module contains the functionality to generate build report after
5 # build all target completes successfully.
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
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.
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
31 ## Pattern to extract contents in EDK DXS files
32 gDxsDependencyPattern = re.compile(r"DEPENDENCY_START(.+)DEPENDENCY_END", re.DOTALL)
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]+)")
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+)")
42 ## Pattern to find GUID value in flash description files
43 gPcdGuidPattern = re.compile(r"PCD\((\w+)[.](\w+)\)")
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]+)")
48 ## Tags for section start, end and separator
49 gSectionStart = ">" + "=" * 118 + "<"
50 gSectionEnd = "<" + "=" * 118 + ">" + "\n"
51 gSectionSep = "=" * 120
53 ## Tags for subsection start, end and separator
54 gSubSectionStart = ">" + "-" * 118 + "<"
55 gSubSectionEnd = "<" + "-" * 118 + ">"
56 gSubSectionSep = "-" * 120
58 ## The look up table to map PCD type to pair of report display type and DEC type
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'),
71 ## The look up table to map module type to driver type
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)',
87 # Writes a string to the file object.
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.
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
96 def FileWrite(File, String, Wrapper=False):
98 String = textwrap.fill(String, 120)
99 File.write(String + "\n")
103 # Reports library information
105 # This class reports the module library subsection in the build report file.
107 class LibraryReport(object):
109 # Constructor function for class LibraryReport
111 # This constructor function generates LibraryReport object for
114 # @param self The object pointer
115 # @param M Module context information
117 def __init__(self, M):
118 self.LibraryList = []
119 if int(str(M.AutoGenVersion), 0) >= 0x00010005:
120 self._EdkIIModule = True
122 self._EdkIIModule = False
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))
133 # Generate report for module library information
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.
139 # @param self The object pointer
140 # @param File The file object for report
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)
152 # Report library class, library constructor and destructor for
153 # EDKII style module.
155 if self._EdkIIModule:
156 LibClass = LibraryItem[1]
158 LibConstructor = " ".join(LibraryItem[2])
160 EdkIILibInfo += " C = " + LibConstructor
161 LibDestructor = " ".join(LibraryItem[3])
163 EdkIILibInfo += " D = " + LibConstructor
164 LibDepex = " ".join(LibraryItem[3])
166 EdkIILibInfo += " Depex = " + LibDepex
168 FileWrite(File, "{%s: %s}" % (LibClass, EdkIILibInfo))
170 FileWrite(File, "{%s}" % LibClass)
172 FileWrite(File, gSubSectionEnd)
175 # Reports dependency expression information
177 # This class reports the module dependency expression subsection in the build report file.
179 class DepexReport(object):
181 # Constructor function for class DepexReport
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.
189 # @param self The object pointer
190 # @param M Module context information
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())
197 self.Depex = Match.group(1).strip()
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"
207 for Lib in M.DependentLibraryList:
208 LibDepex = " ".join(Lib.DepexExpression[M.Arch, M.ModuleType]).strip()
211 LibDepex = "(" + LibDepex + ")"
212 LibDepexList.append(LibDepex)
213 self.LibraryDepex = " AND ".join(LibDepexList)
214 if not self.LibraryDepex:
215 self.LibraryDepex = "(None)"
219 # Generate report for module dependency expression information
221 # This function generates report for the module dependency expression.
223 # @param self The object pointer
224 # @param File The file object for report
226 def GenerateReport(self, File):
227 FileWrite(File, gSubSectionStart)
228 FileWrite(File, "Dependency Expression (DEPEX) from %s" % self.Source)
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)
236 FileWrite(File, "%s" % self.Depex)
237 FileWrite(File, gSubSectionEnd)
240 # Reports dependency expression information
242 # This class reports the module build flags subsection in the build report file.
244 class BuildFlagsReport(object):
246 # Constructor function for class BuildFlagsReport
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.
252 # @param self The object pointer
253 # @param M Module context information
255 def __init__(self, M):
258 # Add build flags according to source file extension so that
259 # irrelevant ones can be filtered out.
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
287 # Save module build flags.
289 self.ToolChainTag = M.ToolChain
291 for Tool in BuildOptions:
292 self.BuildFlags[Tool + "_FLAGS"] = M.BuildOption.get(Tool, {}).get("FLAGS", "")
295 # Generate report for module build flags information
297 # This function generates report for the module build flags expression.
299 # @param self The object pointer
300 # @param File The file object for report
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)
310 FileWrite(File, gSubSectionEnd)
314 # Reports individual module information
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.
320 class ModuleReport(object):
322 # Constructor function for class ModuleReport
324 # This constructor function generates ModuleReport object for
325 # a separate module in a platform build.
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
332 def __init__(self, M, DscOverridePcds, ReportType):
333 self.ModuleName = M.Module.BaseName
334 self.ModuleInfPath = M.MetaFile.File
335 self.FileGuid = M.Guid
337 self.BuildTimeStamp = None
339 ModuleType = M.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", "")
349 self._BuildDir = M.BuildDir
350 self.ModulePcdSet = {}
351 self.ModuleDscOverridePcds = {}
352 if "PCD" in ReportType:
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.
357 for Pcd in M.ModulePcdList + M.LibraryPcdList:
358 self.ModulePcdSet.setdefault((Pcd.TokenCName, Pcd.TokenSpaceGuidCName, Pcd.Type), Pcd.InfDefaultValue)
361 # Collect module DSC override PCD set for report
363 for (PcdTokenCName, PcdTokenSpaceGuidCName) in DscOverridePcds:
364 Pcd = DscOverridePcds[(PcdTokenCName, PcdTokenSpaceGuidCName)]
365 self.ModuleDscOverridePcds.setdefault((PcdTokenCName, PcdTokenSpaceGuidCName), Pcd.DefaultValue)
367 self.LibraryReport = None
368 if "LIBRARY" in ReportType:
369 self.LibraryReport = LibraryReport(M)
371 self.DepexReport = None
372 if "DEPEX" in ReportType:
373 self.DepexReport = DepexReport(M)
375 if "BUILD_FLAGS" in ReportType:
376 self.BuildFlagsReport = BuildFlagsReport(M)
380 # Generate report for module information
382 # This function generates report for separate module expression
383 # in a platform build.
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
390 def GenerateReport(self, File, GlobalPcdReport, ReportType):
391 FileWrite(File, gSectionStart)
393 FwReportFileName = os.path.join(self._BuildDir, "DEBUG", self.ModuleName + ".txt")
394 if os.path.isfile(FwReportFileName):
396 FileContents = open(FwReportFileName).read()
397 Match = gModuleSizePattern.search(FileContents)
399 self.Size = int(Match.group(1))
401 Match = gTimeStampPattern.search(FileContents)
403 self.BuildTimeStamp = datetime.utcfromtimestamp(int(Match.group(1)))
405 EdkLogger.warn(None, "Fail to read report file", FwReportFileName)
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)
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)
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)
422 FileWrite(File, "PCI Device ID: %s" % self.PciDeviceId)
424 FileWrite(File, "PCI Vendor ID: %s" % self.PciVendorId)
425 if self.PciClassCode:
426 FileWrite(File, "PCI Class Code: %s" % self.PciClassCode)
428 FileWrite(File, gSectionSep)
430 if "PCD" in ReportType:
431 GlobalPcdReport.GenerateReport(File, self.ModulePcdSet, self.ModuleDscOverridePcds)
433 if "LIBRARY" in ReportType:
434 self.LibraryReport.GenerateReport(File)
436 if "DEPEX" in ReportType:
437 self.DepexReport.GenerateReport(File)
439 if "BUILD_FLAGS" in ReportType:
440 self.BuildFlagsReport.GenerateReport(File)
442 FileWrite(File, gSectionEnd)
445 # Reports platform and module PCD information
447 # This class reports the platform PCD section and module PCD subsection
448 # in the build report file.
450 class PcdReport(object):
452 # Constructor function for class PcdReport
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.
458 # @param self The object pointer
459 # @param Wa Workspace context information
461 def __init__(self, Wa):
464 self.FdfPcdSet = Wa.FdfProfile.PcdDict
466 self.DecPcdDefault = {}
467 self.ModulePcdOverride = {}
468 for Pa in Wa.AutoGenObjectList:
470 # Collect all platform referenced PCDs and grouped them by PCD token space
473 for Pcd in Pa.AllPcdList:
474 PcdList = self.AllPcds.setdefault(Pcd.TokenSpaceGuidCName, {}).setdefault(Pcd.Type, [])
475 if Pcd not in PcdList:
477 if len(Pcd.TokenCName) > self.MaxLen:
478 self.MaxLen = len(Pcd.TokenCName)
480 for ModuleKey in Pa.Platform.Modules:
482 # Collect PCD DEC default value.
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)
490 # Collect module override PCDs
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))
500 # Collect PCDs defined in DSC common section
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
509 # Generate report for PCD information
511 # This function generates report for separate module expression
512 # in a platform build.
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
520 def GenerateReport(self, File, ModulePcdSet, DscOverridePcds):
521 if ModulePcdSet == None:
523 # For platform global PCD section
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)
533 # For module PCD sub-section
535 FileWrite(File, gSubSectionStart)
536 FileWrite(File, "PCD")
537 FileWrite(File, gSubSectionSep)
539 for Key in self.AllPcds:
541 # Group PCD by their token space GUID C Name
544 for Type in self.AllPcds[Key]:
546 # Group PCD by their usage type
548 TypeName, DecType = gPcdTypeMap.get(Type, ("", Type))
549 for Pcd in self.AllPcds[Key][Type]:
551 # Get PCD default value and their override relationship
553 InfDefaultValue = None
554 if ModulePcdSet != None:
555 if (Pcd.TokenCName, Pcd.TokenSpaceGuidCName, Type) not in ModulePcdSet:
557 InfDefault = ModulePcdSet[Pcd.TokenCName, Pcd.TokenSpaceGuidCName, Type]
561 if ModulePcdSet == None:
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))
569 if Pcd.DatumType in ('UINT8', 'UINT16', 'UINT32', 'UINT64'):
570 PcdDefaultValueNumber = int(Pcd.DefaultValue.strip(), 0)
571 if DecDefaultValue == None:
574 DecDefaultValueNumber = int(DecDefaultValue.strip(), 0)
575 DecMatch = (DecDefaultValueNumber == PcdDefaultValueNumber)
577 if InfDefaultValue == None:
580 InfDefaultValueNumber = int(InfDefaultValue.strip(), 0)
581 InfMatch = (InfDefaultValueNumber == PcdDefaultValueNumber)
583 if DscDefaultValue == None:
586 DscDefaultValueNumber = int(DscDefaultValue.strip(), 0)
587 DscMatch = (DscDefaultValueNumber == PcdDefaultValueNumber)
589 if DecDefaultValue == None:
592 DecMatch = (DecDefaultValue == Pcd.DefaultValue)
594 if InfDefaultValue == None:
597 InfMatch = (InfDefaultValue == Pcd.DefaultValue)
599 if DscDefaultValue == None:
602 DscMatch = (DscDefaultValue == Pcd.DefaultValue)
605 # Report PCD item according to their override relationship
607 if DecMatch and InfMatch:
608 FileWrite(File, ' %-*s: %6s %10s = %-22s' % (self.MaxLen, Pcd.TokenCName, TypeName, '('+Pcd.DatumType+')', Pcd.DefaultValue))
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))
614 FileWrite(File, ' *P %-*s: %6s %10s = %-22s' % (self.MaxLen, Pcd.TokenCName, TypeName, '('+Pcd.DatumType+')', Pcd.DefaultValue))
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))
620 if InfDefaultValue != None:
621 FileWrite(File, ' %*s = %s' % (self.MaxLen + 19, 'INF DEFAULT', InfDefaultValue))
623 if DecDefaultValue != None and not DecMatch:
624 FileWrite(File, ' %*s = %s' % (self.MaxLen + 19, 'DEC DEFAULT', DecDefaultValue))
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)
632 Match = (ModuleDefault == Pcd.DefaultValue)
635 FileWrite(File, ' *M %-*s = %s' % (self.MaxLen + 19, ModulePath, ModuleDefault))
637 if ModulePcdSet == None:
638 FileWrite(File, gSectionEnd)
640 FileWrite(File, gSubSectionEnd)
644 # Reports FD region information
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
654 class FdRegionReport(object):
656 # Discover all the nested FV name list.
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.
662 # @param self The object pointer
663 # @param FvName The name of current firmware file system
664 # @param Wa Workspace context information
666 def _DiscoverNestedFvList(self, FvName, Wa):
667 for Ffs in Wa.FdfProfile.FvDict[FvName.upper()].FfsList:
668 for Section in Ffs.SectionList:
670 for FvSection in Section.SectionList:
671 if FvSection.FvName in self.FvList:
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:
681 # Constructor function for class FdRegionReport
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.
688 # @param self: The object pointer
689 # @param FdRegion The current FdRegion object
690 # @param Wa Workspace context information
692 def __init__(self, FdRegion, Wa):
693 self.Type = FdRegion.RegionType
694 self.BaseAddress = FdRegion.Offset
695 self.Size = FdRegion.Size
699 self._FvDir = Wa.FvDir
702 # If the input FdRegion is not a firmware volume,
705 if self.Type != "FV":
709 # Find all nested FVs in the FdRegion
711 for FvName in FdRegion.RegionDataList:
712 if FvName in self.FvList:
714 self.FvList.append(FvName)
715 self.FvInfo[FvName] = ("Fd Region", self.BaseAddress, self.Size)
716 self._DiscoverNestedFvList(FvName, Wa)
719 for Pa in Wa.AutoGenObjectList:
721 for ModuleKey in Pa.Platform.Modules:
723 # Collect PCD DEC default value.
725 Module = Pa.Platform.Modules[ModuleKey]
726 for Package in Module.M.Module.Packages:
727 if Package not in PackageList:
728 PackageList.append(Package)
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
736 # Collect PCDs defined in DSC common section
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
744 # Add PEI and DXE a priori files GUIDs defined in PI specification.
746 self._GuidsDb["1B45CC0A-156A-428A-AF62-49864DA0E6E6"] = "PEI Apriori"
747 self._GuidsDb["FC510EE7-FFDC-11D4-BD41-0080C73C8881"] = "DXE Apriori"
749 # Add ACPI table storage file
751 self._GuidsDb["7E374E25-8E01-4FEE-87F2-390C23C606CD"] = "ACPI table storage"
753 # Collect the GUID map in the FV firmware volume
755 for FvName in self.FvList:
756 for Ffs in Wa.FdfProfile.FvDict[FvName.upper()].FfsList:
759 # Collect GUID, module mapping from INF file
761 InfFileName = Ffs.InfFileName
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":
776 self._GuidsDb[FileGuid] = "%s (%s)" % (ModuleName, InfPath)
778 EdkLogger.warn(None, "Cannot open file to read", InfPath)
779 except AttributeError:
782 # collect GUID map for binary EFI file in FDF file.
784 Guid = Ffs.NameGuid.upper()
785 Match = gPcdGuidPattern.match(Ffs.NameGuid)
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()
793 for Section in Ffs.SectionList:
795 ModuleSectFile = os.path.join(Wa.WorkspaceDir, Section.SectFileName)
796 self._GuidsDb[Guid] = ModuleSectFile
797 except AttributeError:
799 except AttributeError:
804 # Internal worker function to generate report for the FD region
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.
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
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)
822 if self.Type == "FV":
826 FvReportFileName = os.path.join(self._FvDir, FvName + ".fv.txt")
829 # Collect size info in the firmware volume.
831 FvReport = open(FvReportFileName).read()
832 Match = gFvTotalSizePattern.search(FvReport)
834 FvTotalSize = int(Match.group(1), 16)
835 Match = gFvTakenSizePattern.search(FvReport)
837 FvTakenSize = int(Match.group(1), 16)
838 FvFreeSize = FvTotalSize - FvTakenSize
840 # Write size information to the report file.
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)
849 # Write module offset and module identification to the report file.
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)))
856 EdkLogger.warn(None, "Fail to read report file", FvReportFileName)
858 FileWrite(File, "Size: 0x%X (%.0fK)" % (Size, Size / 1024.0))
859 FileWrite(File, gSubSectionEnd)
862 # Generate report for the FD region
864 # This function generates report for the FD region.
866 # @param self The object pointer
867 # @param File The file object for report
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)
875 self._GenerateReport(File, "FD Region", self.Type, self.BaseAddress, self.Size)
878 # Reports FD information
880 # This class reports the FD section in the build report file.
881 # It collects flash device information for a platform.
883 class FdReport(object):
885 # Constructor function for class FdReport
887 # This constructor function generates FdReport object for a specified
890 # @param self The object pointer
891 # @param Fd The current Firmware device object
892 # @param Wa Workspace context information
894 def __init__(self, Fd, Wa):
895 self.FdName = Fd.FdUiName
896 self.BaseAddress = Fd.BaseAddress
898 self.FdRegionList = [FdRegionReport(FdRegion, Wa) for FdRegion in Fd.RegionList]
901 # Generate report for the firmware device.
903 # This function generates report for the firmware device.
905 # @param self The object pointer
906 # @param File The file object for report
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)
919 FileWrite(File, gSectionEnd)
923 # Reports platform information
925 # This class reports the whole platform information
927 class PlatformReport(object):
929 # Constructor function for class PlatformReport
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.
935 # @param self The object pointer
936 # @param Wa Workspace context information
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()
948 self.PcdReport = None
949 if "PCD" in ReportType:
950 self.PcdReport = PcdReport(Wa)
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))
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
966 self.ModuleReportList.append(ModuleReport(Pa.Platform.Modules[ModuleKey].M, DscOverridePcds, ReportType))
969 # Generate report for the whole platform.
971 # This function generates report for platform information.
972 # It comprises of platform summary, global PCD, flash and
973 # module list sections.
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
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))
992 if "PCD" in ReportType:
993 self.PcdReport.GenerateReport(File, None, {})
995 if "FLASH" in ReportType:
996 for FdReportListItem in self.FdReportList:
997 FdReportListItem.GenerateReport(File)
999 for ModuleReportItem in self.ModuleReportList:
1000 ModuleReportItem.GenerateReport(File, self.PcdReport, ReportType)
1003 ## BuildReport class
1005 # This base class contain the routines to collect data and then
1006 # applies certain format to the output report
1008 class BuildReport(object):
1010 # Constructor function for class BuildReport
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.
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
1020 def __init__(self, ReportFile, ReportType):
1021 self.ReportFile = ReportFile
1023 self.ReportList = []
1024 if ReportType == None or "ALL" in ReportType:
1025 self.ReportType = ["PCD", "LIBRARY", "BUILD_FLAGS", "DEPEX", "FLASH", "PREDICTION"]
1027 for ReportTypeItem in ReportType:
1028 if ReportTypeItem not in self.ReportType:
1029 self.ReportType.append(ReportTypeItem)
1032 # Adds platform report to the list
1034 # This function adds a platform report to the final report list.
1036 # @param self The object pointer
1037 # @param Wa Workspace context information
1039 def AddPlatformReport(self, Wa):
1041 self.ReportList.append(PlatformReport(Wa, self.ReportType))
1044 # Generates the final report.
1046 # This function generates platform build report. It invokes GenerateReport()
1047 # method for every platform report in the list.
1049 # @param self The object pointer
1050 # @param BuildDuration The total time to build the modules
1052 def GenerateReport(self, BuildDuration):
1055 File = open(self.ReportFile, "w+")
1057 EdkLogger.error(None, FILE_OPEN_FAILURE, ExtraData=self.ReportFile)
1059 for Report in self.ReportList:
1060 Report.GenerateReport(File, BuildDuration, self.ReportType)
1062 EdkLogger.error(None, FILE_WRITE_FAILURE, ExtraData=self.ReportFile)
1065 # This acts like the main() function for the script, unless it is 'import'ed into another script.
1066 if __name__ == '__main__':