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 - 2012, Intel Corporation. All rights reserved.<BR>
8 # 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.
27 from datetime
import datetime
28 from StringIO
import StringIO
29 from Common
import EdkLogger
30 from Common
.Misc
import SaveFileOnChange
31 from Common
.Misc
import GuidStructureByteArrayToGuidString
32 from Common
.Misc
import GuidStructureStringToGuidString
33 from Common
.InfClassObject
import gComponentType2ModuleType
34 from Common
.BuildToolError
import FILE_WRITE_FAILURE
35 from Common
.BuildToolError
import CODE_ERROR
36 from Common
.DataType
import TAB_LINE_BREAK
37 from Common
.DataType
import TAB_DEPEX
38 from Common
.DataType
import TAB_SLASH
39 from Common
.DataType
import TAB_SPACE_SPLIT
40 from Common
.DataType
import TAB_BRG_PCD
41 from Common
.DataType
import TAB_BRG_LIBRARY
42 from Common
.DataType
import TAB_BACK_SLASH
44 ## Pattern to extract contents in EDK DXS files
45 gDxsDependencyPattern
= re
.compile(r
"DEPENDENCY_START(.+)DEPENDENCY_END", re
.DOTALL
)
47 ## Pattern to find total FV total size, occupied size in flash report intermediate file
48 gFvTotalSizePattern
= re
.compile(r
"EFI_FV_TOTAL_SIZE = (0x[0-9a-fA-F]+)")
49 gFvTakenSizePattern
= re
.compile(r
"EFI_FV_TAKEN_SIZE = (0x[0-9a-fA-F]+)")
51 ## Pattern to find module size and time stamp in module summary report intermediate file
52 gModuleSizePattern
= re
.compile(r
"MODULE_SIZE = (\d+)")
53 gTimeStampPattern
= re
.compile(r
"TIME_STAMP = (\d+)")
55 ## Pattern to find GUID value in flash description files
56 gPcdGuidPattern
= re
.compile(r
"PCD\((\w+)[.](\w+)\)")
58 ## Pattern to collect offset, GUID value pair in the flash report intermediate file
59 gOffsetGuidPattern
= re
.compile(r
"(0x[0-9A-Fa-f]+) ([-A-Fa-f0-9]+)")
61 ## Pattern to find module base address and entry point in fixed flash map file
62 gModulePattern
= r
"\n[-\w]+\s*\(([^,]+),\s*BaseAddress=%(Address)s,\s*EntryPoint=%(Address)s\)\s*\(GUID=([-0-9A-Fa-f]+)[^)]*\)"
63 gMapFileItemPattern
= re
.compile(gModulePattern
% {"Address" : "(-?0[xX][0-9A-Fa-f]+)"})
65 ## Pattern to find all module referenced header files in source files
66 gIncludePattern
= re
.compile(r
'#include\s*["<]([^">]+)[">]')
67 gIncludePattern2
= re
.compile(r
"#include\s+EFI_([A-Z_]+)\s*[(]\s*(\w+)\s*[)]")
69 ## Pattern to find the entry point for EDK module using EDKII Glue library
70 gGlueLibEntryPoint
= re
.compile(r
"__EDKII_GLUE_MODULE_ENTRY_POINT__\s*=\s*(\w+)")
72 ## Tags for MaxLength of line in report
75 ## Tags for end of line in report
78 ## Tags for section start, end and separator
79 gSectionStart
= ">" + "=" * (gLineMaxLength
-2) + "<"
80 gSectionEnd
= "<" + "=" * (gLineMaxLength
-2) + ">" + "\n"
81 gSectionSep
= "=" * gLineMaxLength
83 ## Tags for subsection start, end and separator
84 gSubSectionStart
= ">" + "-" * (gLineMaxLength
-2) + "<"
85 gSubSectionEnd
= "<" + "-" * (gLineMaxLength
-2) + ">"
86 gSubSectionSep
= "-" * gLineMaxLength
89 ## The look up table to map PCD type to pair of report display type and DEC type
91 'FixedAtBuild' : ('FIXED', 'FixedAtBuild'),
92 'PatchableInModule': ('PATCH', 'PatchableInModule'),
93 'FeatureFlag' : ('FLAG', 'FeatureFlag'),
94 'Dynamic' : ('DYN', 'Dynamic'),
95 'DynamicHii' : ('DYNHII', 'Dynamic'),
96 'DynamicVpd' : ('DYNVPD', 'Dynamic'),
97 'DynamicEx' : ('DEX', 'DynamicEx'),
98 'DynamicExHii' : ('DEXHII', 'DynamicEx'),
99 'DynamicExVpd' : ('DEXVPD', 'DynamicEx'),
102 ## The look up table to map module type to driver type
104 'SEC' : '0x3 (SECURITY_CORE)',
105 'PEI_CORE' : '0x4 (PEI_CORE)',
106 'PEIM' : '0x6 (PEIM)',
107 'DXE_CORE' : '0x5 (DXE_CORE)',
108 'DXE_DRIVER' : '0x7 (DRIVER)',
109 'DXE_SAL_DRIVER' : '0x7 (DRIVER)',
110 'DXE_SMM_DRIVER' : '0x7 (DRIVER)',
111 'DXE_RUNTIME_DRIVER': '0x7 (DRIVER)',
112 'UEFI_DRIVER' : '0x7 (DRIVER)',
113 'UEFI_APPLICATION' : '0x9 (APPLICATION)',
114 'SMM_CORE' : '0xD (SMM_CORE)',
115 'SMM_DRIVER' : '0xA (SMM)', # Extension of module type to support PI 1.1 SMM drivers
118 ## The look up table of the supported opcode in the dependency expression binaries
119 gOpCodeList
= ["BEFORE", "AFTER", "PUSH", "AND", "OR", "NOT", "TRUE", "FALSE", "END", "SOR"]
122 # Writes a string to the file object.
124 # This function writes a string to the file object and a new line is appended
125 # afterwards. It may optionally wraps the string for better readability.
127 # @File The file object to write
128 # @String The string to be written to the file
129 # @Wrapper Indicates whether to wrap the string
131 def FileWrite(File
, String
, Wrapper
=False):
133 String
= textwrap
.fill(String
, 120)
134 File
.write(String
+ gEndOfLine
)
137 # Find all the header file that the module source directly includes.
139 # This function scans source code to find all header files the module may
140 # include. This is not accurate but very effective to find all the header
141 # file the module might include with #include statement.
143 # @Source The source file name
144 # @IncludePathList The list of include path to find the source file.
145 # @IncludeFiles The dictionary of current found include files.
147 def FindIncludeFiles(Source
, IncludePathList
, IncludeFiles
):
148 FileContents
= open(Source
).read()
150 # Find header files with pattern #include "XXX.h" or #include <XXX.h>
152 for Match
in gIncludePattern
.finditer(FileContents
):
153 FileName
= Match
.group(1).strip()
154 for Dir
in [os
.path
.dirname(Source
)] + IncludePathList
:
155 FullFileName
= os
.path
.normpath(os
.path
.join(Dir
, FileName
))
156 if os
.path
.exists(FullFileName
):
157 IncludeFiles
[FullFileName
.lower().replace("\\", "/")] = FullFileName
161 # Find header files with pattern like #include EFI_PPI_CONSUMER(XXX)
163 for Match
in gIncludePattern2
.finditer(FileContents
):
165 Type
= Match
.group(1)
166 if "ARCH_PROTOCOL" in Type
:
167 FileName
= "ArchProtocol/%(Key)s/%(Key)s.h" % {"Key" : Key
}
168 elif "PROTOCOL" in Type
:
169 FileName
= "Protocol/%(Key)s/%(Key)s.h" % {"Key" : Key
}
171 FileName
= "Ppi/%(Key)s/%(Key)s.h" % {"Key" : Key
}
173 FileName
= "Guid/%(Key)s/%(Key)s.h" % {"Key" : Key
}
176 for Dir
in IncludePathList
:
177 FullFileName
= os
.path
.normpath(os
.path
.join(Dir
, FileName
))
178 if os
.path
.exists(FullFileName
):
179 IncludeFiles
[FullFileName
.lower().replace("\\", "/")] = FullFileName
182 ## Split each lines in file
184 # This method is used to split the lines in file to make the length of each line
185 # less than MaxLength.
187 # @param Content The content of file
188 # @param MaxLength The Max Length of the line
190 def FileLinesSplit(Content
=None, MaxLength
=None):
191 ContentList
= Content
.split(TAB_LINE_BREAK
)
194 for Line
in ContentList
:
195 while len(Line
.rstrip()) > MaxLength
:
196 LineSpaceIndex
= Line
.rfind(TAB_SPACE_SPLIT
, 0, MaxLength
)
197 LineSlashIndex
= Line
.rfind(TAB_SLASH
, 0, MaxLength
)
198 LineBackSlashIndex
= Line
.rfind(TAB_BACK_SLASH
, 0, MaxLength
)
199 if max(LineSpaceIndex
, LineSlashIndex
, LineBackSlashIndex
) > 0:
200 LineBreakIndex
= max(LineSpaceIndex
, LineSlashIndex
, LineBackSlashIndex
)
202 LineBreakIndex
= MaxLength
203 NewContentList
.append(Line
[:LineBreakIndex
])
204 Line
= Line
[LineBreakIndex
:]
206 NewContentList
.append(Line
)
207 for NewLine
in NewContentList
:
208 NewContent
+= NewLine
+ TAB_LINE_BREAK
210 NewContent
= NewContent
.replace(TAB_LINE_BREAK
, gEndOfLine
).replace('\r\r\n', gEndOfLine
)
216 # Parse binary dependency expression section
218 # This utility class parses the dependency expression section and translate the readable
219 # GUID name and value.
221 class DepexParser(object):
223 # Constructor function for class DepexParser
225 # This constructor function collect GUID values so that the readable
226 # GUID name can be translated.
228 # @param self The object pointer
229 # @param Wa Workspace context information
231 def __init__(self
, Wa
):
233 for Pa
in Wa
.AutoGenObjectList
:
234 for Package
in Pa
.PackageList
:
235 for Protocol
in Package
.Protocols
:
236 GuidValue
= GuidStructureStringToGuidString(Package
.Protocols
[Protocol
])
237 self
._GuidDb
[GuidValue
.upper()] = Protocol
238 for Ppi
in Package
.Ppis
:
239 GuidValue
= GuidStructureStringToGuidString(Package
.Ppis
[Ppi
])
240 self
._GuidDb
[GuidValue
.upper()] = Ppi
241 for Guid
in Package
.Guids
:
242 GuidValue
= GuidStructureStringToGuidString(Package
.Guids
[Guid
])
243 self
._GuidDb
[GuidValue
.upper()] = Guid
246 # Parse the binary dependency expression files.
248 # This function parses the binary dependency expression file and translate it
249 # to the instruction list.
251 # @param self The object pointer
252 # @param DepexFileName The file name of binary dependency expression file.
254 def ParseDepexFile(self
, DepexFileName
):
255 DepexFile
= open(DepexFileName
, "rb")
257 OpCode
= DepexFile
.read(1)
259 Statement
= gOpCodeList
[struct
.unpack("B", OpCode
)[0]]
260 if Statement
in ["BEFORE", "AFTER", "PUSH"]:
261 GuidValue
= "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X" % \
262 struct
.unpack("=LHHBBBBBBBB", DepexFile
.read(16))
263 GuidString
= self
._GuidDb
.get(GuidValue
, GuidValue
)
264 Statement
= "%s %s" % (Statement
, GuidString
)
265 DepexStatement
.append(Statement
)
266 OpCode
= DepexFile
.read(1)
268 return DepexStatement
271 # Reports library information
273 # This class reports the module library subsection in the build report file.
275 class LibraryReport(object):
277 # Constructor function for class LibraryReport
279 # This constructor function generates LibraryReport object for
282 # @param self The object pointer
283 # @param M Module context information
285 def __init__(self
, M
):
286 self
.LibraryList
= []
287 if int(str(M
.AutoGenVersion
), 0) >= 0x00010005:
288 self
._EdkIIModule
= True
290 self
._EdkIIModule
= False
292 for Lib
in M
.DependentLibraryList
:
293 LibInfPath
= str(Lib
)
294 LibClassList
= Lib
.LibraryClass
[0].LibraryClass
295 LibConstructorList
= Lib
.ConstructorList
296 LibDesstructorList
= Lib
.DestructorList
297 LibDepexList
= Lib
.DepexExpression
[M
.Arch
, M
.ModuleType
]
298 self
.LibraryList
.append((LibInfPath
, LibClassList
, LibConstructorList
, LibDesstructorList
, LibDepexList
))
301 # Generate report for module library information
303 # This function generates report for the module library.
304 # If the module is EDKII style one, the additional library class, library
305 # constructor/destructor and dependency expression may also be reported.
307 # @param self The object pointer
308 # @param File The file object for report
310 def GenerateReport(self
, File
):
311 FileWrite(File
, gSubSectionStart
)
312 FileWrite(File
, TAB_BRG_LIBRARY
)
313 if len(self
.LibraryList
) > 0:
314 FileWrite(File
, gSubSectionSep
)
315 for LibraryItem
in self
.LibraryList
:
316 LibInfPath
= LibraryItem
[0]
317 FileWrite(File
, LibInfPath
)
320 # Report library class, library constructor and destructor for
321 # EDKII style module.
323 if self
._EdkIIModule
:
324 LibClass
= LibraryItem
[1]
326 LibConstructor
= " ".join(LibraryItem
[2])
328 EdkIILibInfo
+= " C = " + LibConstructor
329 LibDestructor
= " ".join(LibraryItem
[3])
331 EdkIILibInfo
+= " D = " + LibDestructor
332 LibDepex
= " ".join(LibraryItem
[4])
334 EdkIILibInfo
+= " Depex = " + LibDepex
336 FileWrite(File
, "{%s: %s}" % (LibClass
, EdkIILibInfo
))
338 FileWrite(File
, "{%s}" % LibClass
)
340 FileWrite(File
, gSubSectionEnd
)
343 # Reports dependency expression information
345 # This class reports the module dependency expression subsection in the build report file.
347 class DepexReport(object):
349 # Constructor function for class DepexReport
351 # This constructor function generates DepexReport object for
352 # a module. If the module source contains the DXS file (usually EDK
353 # style module), it uses the dependency in DXS file; otherwise,
354 # it uses the dependency expression from its own INF [Depex] section
355 # and then merges with the ones from its dependent library INF.
357 # @param self The object pointer
358 # @param M Module context information
360 def __init__(self
, M
):
362 self
._DepexFileName
= os
.path
.join(M
.BuildDir
, "OUTPUT", M
.Module
.BaseName
+ ".depex")
363 ModuleType
= M
.ModuleType
365 ModuleType
= gComponentType2ModuleType
.get(M
.ComponentType
, "")
367 if ModuleType
in ["SEC", "PEI_CORE", "DXE_CORE", "SMM_CORE", "UEFI_APPLICATION"]:
370 for Source
in M
.SourceFileList
:
371 if os
.path
.splitext(Source
.Path
)[1].lower() == ".dxs":
372 Match
= gDxsDependencyPattern
.search(open(Source
.Path
).read())
374 self
.Depex
= Match
.group(1).strip()
378 self
.Depex
= M
.DepexExpressionList
.get(M
.ModuleType
, "")
379 self
.ModuleDepex
= " ".join(M
.Module
.DepexExpression
[M
.Arch
, M
.ModuleType
])
380 if not self
.ModuleDepex
:
381 self
.ModuleDepex
= "(None)"
384 for Lib
in M
.DependentLibraryList
:
385 LibDepex
= " ".join(Lib
.DepexExpression
[M
.Arch
, M
.ModuleType
]).strip()
387 LibDepexList
.append("(" + LibDepex
+ ")")
388 self
.LibraryDepex
= " AND ".join(LibDepexList
)
389 if not self
.LibraryDepex
:
390 self
.LibraryDepex
= "(None)"
394 # Generate report for module dependency expression information
396 # This function generates report for the module dependency expression.
398 # @param self The object pointer
399 # @param File The file object for report
400 # @param GlobalDepexParser The platform global Dependency expression parser object
402 def GenerateReport(self
, File
, GlobalDepexParser
):
404 FileWrite(File
, gSubSectionStart
)
405 FileWrite(File
, TAB_DEPEX
)
406 FileWrite(File
, gSubSectionEnd
)
408 FileWrite(File
, gSubSectionStart
)
409 if os
.path
.isfile(self
._DepexFileName
):
411 DepexStatements
= GlobalDepexParser
.ParseDepexFile(self
._DepexFileName
)
412 FileWrite(File
, "Final Dependency Expression (DEPEX) Instructions")
413 for DepexStatement
in DepexStatements
:
414 FileWrite(File
, " %s" % DepexStatement
)
415 FileWrite(File
, gSubSectionSep
)
417 EdkLogger
.warn(None, "Dependency expression file is corrupted", self
._DepexFileName
)
419 FileWrite(File
, "Dependency Expression (DEPEX) from %s" % self
.Source
)
421 if self
.Source
== "INF":
422 FileWrite(File
, "%s" % self
.Depex
, True)
423 FileWrite(File
, gSubSectionSep
)
424 FileWrite(File
, "From Module INF: %s" % self
.ModuleDepex
, True)
425 FileWrite(File
, "From Library INF: %s" % self
.LibraryDepex
, True)
427 FileWrite(File
, "%s" % self
.Depex
)
428 FileWrite(File
, gSubSectionEnd
)
431 # Reports dependency expression information
433 # This class reports the module build flags subsection in the build report file.
435 class BuildFlagsReport(object):
437 # Constructor function for class BuildFlagsReport
439 # This constructor function generates BuildFlagsReport object for
440 # a module. It reports the build tool chain tag and all relevant
441 # build flags to build the module.
443 # @param self The object pointer
444 # @param M Module context information
446 def __init__(self
, M
):
449 # Add build flags according to source file extension so that
450 # irrelevant ones can be filtered out.
452 for Source
in M
.SourceFileList
:
453 Ext
= os
.path
.splitext(Source
.File
)[1].lower()
454 if Ext
in [".c", ".cc", ".cpp"]:
455 BuildOptions
["CC"] = 1
456 elif Ext
in [".s", ".asm"]:
457 BuildOptions
["PP"] = 1
458 BuildOptions
["ASM"] = 1
459 elif Ext
in [".vfr"]:
460 BuildOptions
["VFRPP"] = 1
461 BuildOptions
["VFR"] = 1
462 elif Ext
in [".dxs"]:
463 BuildOptions
["APP"] = 1
464 BuildOptions
["CC"] = 1
465 elif Ext
in [".asl"]:
466 BuildOptions
["ASLPP"] = 1
467 BuildOptions
["ASL"] = 1
468 elif Ext
in [".aslc"]:
469 BuildOptions
["ASLCC"] = 1
470 BuildOptions
["ASLDLINK"] = 1
471 BuildOptions
["CC"] = 1
472 elif Ext
in [".asm16"]:
473 BuildOptions
["ASMLINK"] = 1
474 BuildOptions
["SLINK"] = 1
475 BuildOptions
["DLINK"] = 1
478 # Save module build flags.
480 self
.ToolChainTag
= M
.ToolChain
482 for Tool
in BuildOptions
:
483 self
.BuildFlags
[Tool
+ "_FLAGS"] = M
.BuildOption
.get(Tool
, {}).get("FLAGS", "")
486 # Generate report for module build flags information
488 # This function generates report for the module build flags expression.
490 # @param self The object pointer
491 # @param File The file object for report
493 def GenerateReport(self
, File
):
494 FileWrite(File
, gSubSectionStart
)
495 FileWrite(File
, "Build Flags")
496 FileWrite(File
, "Tool Chain Tag: %s" % self
.ToolChainTag
)
497 for Tool
in self
.BuildFlags
:
498 FileWrite(File
, gSubSectionSep
)
499 FileWrite(File
, "%s = %s" % (Tool
, self
.BuildFlags
[Tool
]), True)
501 FileWrite(File
, gSubSectionEnd
)
505 # Reports individual module information
507 # This class reports the module section in the build report file.
508 # It comprises of module summary, module PCD, library, dependency expression,
509 # build flags sections.
511 class ModuleReport(object):
513 # Constructor function for class ModuleReport
515 # This constructor function generates ModuleReport object for
516 # a separate module in a platform build.
518 # @param self The object pointer
519 # @param M Module context information
520 # @param ReportType The kind of report items in the final report file
522 def __init__(self
, M
, ReportType
):
523 self
.ModuleName
= M
.Module
.BaseName
524 self
.ModuleInfPath
= M
.MetaFile
.File
525 self
.FileGuid
= M
.Guid
527 self
.BuildTimeStamp
= None
530 ModuleType
= M
.ModuleType
532 ModuleType
= gComponentType2ModuleType
.get(M
.ComponentType
, "")
534 # If a module complies to PI 1.1, promote Module type to "SMM_DRIVER"
536 if ModuleType
== "DXE_SMM_DRIVER":
537 PiSpec
= M
.Module
.Specification
.get("PI_SPECIFICATION_VERSION", "0x00010000")
538 if int(PiSpec
, 0) >= 0x0001000A:
539 ModuleType
= "SMM_DRIVER"
540 self
.DriverType
= gDriverTypeMap
.get(ModuleType
, "0x2 (FREE_FORM)")
541 self
.UefiSpecVersion
= M
.Module
.Specification
.get("UEFI_SPECIFICATION_VERSION", "")
542 self
.PiSpecVersion
= M
.Module
.Specification
.get("PI_SPECIFICATION_VERSION", "")
543 self
.PciDeviceId
= M
.Module
.Defines
.get("PCI_DEVICE_ID", "")
544 self
.PciVendorId
= M
.Module
.Defines
.get("PCI_VENDOR_ID", "")
545 self
.PciClassCode
= M
.Module
.Defines
.get("PCI_CLASS_CODE", "")
547 self
._BuildDir
= M
.BuildDir
548 self
.ModulePcdSet
= {}
549 if "PCD" in ReportType
:
551 # Collect all module used PCD set: module INF referenced directly or indirectly.
552 # It also saves module INF default values of them in case they exist.
554 for Pcd
in M
.ModulePcdList
+ M
.LibraryPcdList
:
555 self
.ModulePcdSet
.setdefault((Pcd
.TokenCName
, Pcd
.TokenSpaceGuidCName
, Pcd
.Type
), (Pcd
.InfDefaultValue
, Pcd
.DefaultValue
))
557 self
.LibraryReport
= None
558 if "LIBRARY" in ReportType
:
559 self
.LibraryReport
= LibraryReport(M
)
561 self
.DepexReport
= None
562 if "DEPEX" in ReportType
:
563 self
.DepexReport
= DepexReport(M
)
565 if "BUILD_FLAGS" in ReportType
:
566 self
.BuildFlagsReport
= BuildFlagsReport(M
)
570 # Generate report for module information
572 # This function generates report for separate module expression
573 # in a platform build.
575 # @param self The object pointer
576 # @param File The file object for report
577 # @param GlobalPcdReport The platform global PCD report object
578 # @param GlobalPredictionReport The platform global Prediction report object
579 # @param GlobalDepexParser The platform global Dependency expression parser object
580 # @param ReportType The kind of report items in the final report file
582 def GenerateReport(self
, File
, GlobalPcdReport
, GlobalPredictionReport
, GlobalDepexParser
, ReportType
):
583 FileWrite(File
, gSectionStart
)
585 FwReportFileName
= os
.path
.join(self
._BuildDir
, "DEBUG", self
.ModuleName
+ ".txt")
586 if os
.path
.isfile(FwReportFileName
):
588 FileContents
= open(FwReportFileName
).read()
589 Match
= gModuleSizePattern
.search(FileContents
)
591 self
.Size
= int(Match
.group(1))
593 Match
= gTimeStampPattern
.search(FileContents
)
595 self
.BuildTimeStamp
= datetime
.fromtimestamp(int(Match
.group(1)))
597 EdkLogger
.warn(None, "Fail to read report file", FwReportFileName
)
599 FileWrite(File
, "Module Summary")
600 FileWrite(File
, "Module Name: %s" % self
.ModuleName
)
601 FileWrite(File
, "Module INF Path: %s" % self
.ModuleInfPath
)
602 FileWrite(File
, "File GUID: %s" % self
.FileGuid
)
604 FileWrite(File
, "Size: 0x%X (%.2fK)" % (self
.Size
, self
.Size
/ 1024.0))
605 if self
.BuildTimeStamp
:
606 FileWrite(File
, "Build Time Stamp: %s" % self
.BuildTimeStamp
)
608 FileWrite(File
, "Driver Type: %s" % self
.DriverType
)
609 if self
.UefiSpecVersion
:
610 FileWrite(File
, "UEFI Spec Version: %s" % self
.UefiSpecVersion
)
611 if self
.PiSpecVersion
:
612 FileWrite(File
, "PI Spec Version: %s" % self
.PiSpecVersion
)
614 FileWrite(File
, "PCI Device ID: %s" % self
.PciDeviceId
)
616 FileWrite(File
, "PCI Vendor ID: %s" % self
.PciVendorId
)
617 if self
.PciClassCode
:
618 FileWrite(File
, "PCI Class Code: %s" % self
.PciClassCode
)
620 FileWrite(File
, gSectionSep
)
622 if "PCD" in ReportType
:
623 GlobalPcdReport
.GenerateReport(File
, self
.ModulePcdSet
)
625 if "LIBRARY" in ReportType
:
626 self
.LibraryReport
.GenerateReport(File
)
628 if "DEPEX" in ReportType
:
629 self
.DepexReport
.GenerateReport(File
, GlobalDepexParser
)
631 if "BUILD_FLAGS" in ReportType
:
632 self
.BuildFlagsReport
.GenerateReport(File
)
634 if "FIXED_ADDRESS" in ReportType
and self
.FileGuid
:
635 GlobalPredictionReport
.GenerateReport(File
, self
.FileGuid
)
637 FileWrite(File
, gSectionEnd
)
640 # Reports platform and module PCD information
642 # This class reports the platform PCD section and module PCD subsection
643 # in the build report file.
645 class PcdReport(object):
647 # Constructor function for class PcdReport
649 # This constructor function generates PcdReport object a platform build.
650 # It collects the whole PCD database from platform DSC files, platform
651 # flash description file and package DEC files.
653 # @param self The object pointer
654 # @param Wa Workspace context information
656 def __init__(self
, Wa
):
660 self
.FdfPcdSet
= Wa
.FdfProfile
.PcdDict
664 self
.ModulePcdOverride
= {}
665 for Pa
in Wa
.AutoGenObjectList
:
667 # Collect all platform referenced PCDs and grouped them by PCD token space
670 for Pcd
in Pa
.AllPcdList
:
671 PcdList
= self
.AllPcds
.setdefault(Pcd
.TokenSpaceGuidCName
, {}).setdefault(Pcd
.Type
, [])
672 if Pcd
not in PcdList
:
674 if len(Pcd
.TokenCName
) > self
.MaxLen
:
675 self
.MaxLen
= len(Pcd
.TokenCName
)
677 for Module
in Pa
.Platform
.Modules
.values():
679 # Collect module override PCDs
681 for ModulePcd
in Module
.M
.ModulePcdList
+ Module
.M
.LibraryPcdList
:
682 TokenCName
= ModulePcd
.TokenCName
683 TokenSpaceGuid
= ModulePcd
.TokenSpaceGuidCName
684 ModuleDefault
= ModulePcd
.DefaultValue
685 ModulePath
= os
.path
.basename(Module
.M
.MetaFile
.File
)
686 self
.ModulePcdOverride
.setdefault((TokenCName
, TokenSpaceGuid
), {})[ModulePath
] = ModuleDefault
690 # Collect PCD DEC default value.
692 self
.DecPcdDefault
= {}
693 for Pa
in Wa
.AutoGenObjectList
:
694 for Package
in Pa
.PackageList
:
695 for (TokenCName
, TokenSpaceGuidCName
, DecType
) in Package
.Pcds
:
696 DecDefaultValue
= Package
.Pcds
[TokenCName
, TokenSpaceGuidCName
, DecType
].DefaultValue
697 self
.DecPcdDefault
.setdefault((TokenCName
, TokenSpaceGuidCName
, DecType
), DecDefaultValue
)
699 # Collect PCDs defined in DSC common section
701 self
.DscPcdDefault
= {}
702 for Arch
in Wa
.ArchList
:
703 Platform
= Wa
.BuildDatabase
[Wa
.MetaFile
, Arch
, Wa
.BuildTarget
, Wa
.ToolChain
]
704 for (TokenCName
, TokenSpaceGuidCName
) in Platform
.Pcds
:
705 DscDefaultValue
= Platform
.Pcds
[(TokenCName
, TokenSpaceGuidCName
)].DefaultValue
707 self
.DscPcdDefault
[(TokenCName
, TokenSpaceGuidCName
)] = DscDefaultValue
710 # Generate report for PCD information
712 # This function generates report for separate module expression
713 # in a platform build.
715 # @param self The object pointer
716 # @param File The file object for report
717 # @param ModulePcdSet Set of all PCDs referenced by module or None for
718 # platform PCD report
719 # @param DscOverridePcds Module DSC override PCDs set
721 def GenerateReport(self
, File
, ModulePcdSet
):
722 if ModulePcdSet
== None:
724 # For platform global PCD section
726 FileWrite(File
, gSectionStart
)
727 FileWrite(File
, "Platform Configuration Database Report")
728 FileWrite(File
, " *P - Platform scoped PCD override in DSC file")
729 FileWrite(File
, " *F - Platform scoped PCD override in FDF file")
730 FileWrite(File
, " *M - Module scoped PCD override")
731 FileWrite(File
, gSectionSep
)
734 # For module PCD sub-section
736 FileWrite(File
, gSubSectionStart
)
737 FileWrite(File
, TAB_BRG_PCD
)
738 FileWrite(File
, gSubSectionSep
)
740 for Key
in self
.AllPcds
:
742 # Group PCD by their token space GUID C Name
745 for Type
in self
.AllPcds
[Key
]:
747 # Group PCD by their usage type
749 TypeName
, DecType
= gPcdTypeMap
.get(Type
, ("", Type
))
750 for Pcd
in self
.AllPcds
[Key
][Type
]:
752 # Get PCD default value and their override relationship
754 DecDefaultValue
= self
.DecPcdDefault
.get((Pcd
.TokenCName
, Pcd
.TokenSpaceGuidCName
, DecType
))
755 DscDefaultValue
= self
.DscPcdDefault
.get((Pcd
.TokenCName
, Pcd
.TokenSpaceGuidCName
))
756 DscDefaultValue
= self
.FdfPcdSet
.get((Pcd
.TokenCName
, Key
), DscDefaultValue
)
757 InfDefaultValue
= None
759 PcdValue
= DecDefaultValue
761 PcdValue
= DscDefaultValue
762 if ModulePcdSet
!= None:
763 if (Pcd
.TokenCName
, Pcd
.TokenSpaceGuidCName
, Type
) not in ModulePcdSet
:
765 InfDefault
, PcdValue
= ModulePcdSet
[Pcd
.TokenCName
, Pcd
.TokenSpaceGuidCName
, Type
]
769 if ModulePcdSet
== None:
775 if Pcd
.DatumType
in ('UINT8', 'UINT16', 'UINT32', 'UINT64'):
776 PcdValueNumber
= int(PcdValue
.strip(), 0)
777 if DecDefaultValue
== None:
780 DecDefaultValueNumber
= int(DecDefaultValue
.strip(), 0)
781 DecMatch
= (DecDefaultValueNumber
== PcdValueNumber
)
783 if InfDefaultValue
== None:
786 InfDefaultValueNumber
= int(InfDefaultValue
.strip(), 0)
787 InfMatch
= (InfDefaultValueNumber
== PcdValueNumber
)
789 if DscDefaultValue
== None:
792 DscDefaultValueNumber
= int(DscDefaultValue
.strip(), 0)
793 DscMatch
= (DscDefaultValueNumber
== PcdValueNumber
)
795 if DecDefaultValue
== None:
798 DecMatch
= (DecDefaultValue
.strip() == PcdValue
.strip())
800 if InfDefaultValue
== None:
803 InfMatch
= (InfDefaultValue
.strip() == PcdValue
.strip())
805 if DscDefaultValue
== None:
808 DscMatch
= (DscDefaultValue
.strip() == PcdValue
.strip())
811 # Report PCD item according to their override relationship
813 if DecMatch
and InfMatch
:
814 FileWrite(File
, ' %-*s: %6s %10s = %-22s' % (self
.MaxLen
, Pcd
.TokenCName
, TypeName
, '('+Pcd
.DatumType
+')', PcdValue
.strip()))
817 if (Pcd
.TokenCName
, Key
) in self
.FdfPcdSet
:
818 FileWrite(File
, ' *F %-*s: %6s %10s = %-22s' % (self
.MaxLen
, Pcd
.TokenCName
, TypeName
, '('+Pcd
.DatumType
+')', PcdValue
.strip()))
820 FileWrite(File
, ' *P %-*s: %6s %10s = %-22s' % (self
.MaxLen
, Pcd
.TokenCName
, TypeName
, '('+Pcd
.DatumType
+')', PcdValue
.strip()))
822 FileWrite(File
, ' *M %-*s: %6s %10s = %-22s' % (self
.MaxLen
, Pcd
.TokenCName
, TypeName
, '('+Pcd
.DatumType
+')', PcdValue
.strip()))
824 if TypeName
in ('DYNHII', 'DEXHII', 'DYNVPD', 'DEXVPD'):
825 for SkuInfo
in Pcd
.SkuInfoList
.values():
826 if TypeName
in ('DYNHII', 'DEXHII'):
827 FileWrite(File
, '%*s: %s: %s' % (self
.MaxLen
+ 4, SkuInfo
.VariableGuid
, SkuInfo
.VariableName
, SkuInfo
.VariableOffset
))
829 FileWrite(File
, '%*s' % (self
.MaxLen
+ 4, SkuInfo
.VpdOffset
))
831 if not DscMatch
and DscDefaultValue
!= None:
832 FileWrite(File
, ' %*s = %s' % (self
.MaxLen
+ 19, 'DSC DEFAULT', DscDefaultValue
.strip()))
834 if not InfMatch
and InfDefaultValue
!= None:
835 FileWrite(File
, ' %*s = %s' % (self
.MaxLen
+ 19, 'INF DEFAULT', InfDefaultValue
.strip()))
837 if not DecMatch
and DecDefaultValue
!= None:
838 FileWrite(File
, ' %*s = %s' % (self
.MaxLen
+ 19, 'DEC DEFAULT', DecDefaultValue
.strip()))
840 if ModulePcdSet
== None:
841 ModuleOverride
= self
.ModulePcdOverride
.get((Pcd
.TokenCName
, Pcd
.TokenSpaceGuidCName
), {})
842 for ModulePath
in ModuleOverride
:
843 ModuleDefault
= ModuleOverride
[ModulePath
]
844 if Pcd
.DatumType
in ('UINT8', 'UINT16', 'UINT32', 'UINT64'):
845 ModulePcdDefaultValueNumber
= int(ModuleDefault
.strip(), 0)
846 Match
= (ModulePcdDefaultValueNumber
== PcdValueNumber
)
848 Match
= (ModuleDefault
.strip() == PcdValue
.strip())
851 FileWrite(File
, ' *M %-*s = %s' % (self
.MaxLen
+ 19, ModulePath
, ModuleDefault
.strip()))
853 if ModulePcdSet
== None:
854 FileWrite(File
, gSectionEnd
)
856 FileWrite(File
, gSubSectionEnd
)
861 # Reports platform and module Prediction information
863 # This class reports the platform execution order prediction section and
864 # module load fixed address prediction subsection in the build report file.
866 class PredictionReport(object):
868 # Constructor function for class PredictionReport
870 # This constructor function generates PredictionReport object for the platform.
872 # @param self: The object pointer
873 # @param Wa Workspace context information
875 def __init__(self
, Wa
):
876 self
._MapFileName
= os
.path
.join(Wa
.BuildDir
, Wa
.Name
+ ".map")
877 self
._MapFileParsed
= False
878 self
._EotToolInvoked
= False
879 self
._FvDir
= Wa
.FvDir
880 self
._EotDir
= Wa
.BuildDir
881 self
._FfsEntryPoint
= {}
883 self
._SourceList
= []
884 self
.FixedMapDict
= {}
889 # Collect all platform reference source files and GUID C Name
891 for Pa
in Wa
.AutoGenObjectList
:
892 for Module
in Pa
.LibraryAutoGenList
+ Pa
.ModuleAutoGenList
:
894 # BASE typed modules are EFI agnostic, so we need not scan
895 # their source code to find PPI/Protocol produce or consume
898 if Module
.ModuleType
== "BASE":
901 # Add module referenced source files
903 self
._SourceList
.append(str(Module
))
905 for Source
in Module
.SourceFileList
:
906 if os
.path
.splitext(str(Source
))[1].lower() == ".c":
907 self
._SourceList
.append(" " + str(Source
))
908 FindIncludeFiles(Source
.Path
, Module
.IncludePathList
, IncludeList
)
909 for IncludeFile
in IncludeList
.values():
910 self
._SourceList
.append(" " + IncludeFile
)
912 for Guid
in Module
.PpiList
:
913 self
._GuidMap
[Guid
] = GuidStructureStringToGuidString(Module
.PpiList
[Guid
])
914 for Guid
in Module
.ProtocolList
:
915 self
._GuidMap
[Guid
] = GuidStructureStringToGuidString(Module
.ProtocolList
[Guid
])
916 for Guid
in Module
.GuidList
:
917 self
._GuidMap
[Guid
] = GuidStructureStringToGuidString(Module
.GuidList
[Guid
])
919 if Module
.Guid
and not Module
.IsLibrary
:
920 EntryPoint
= " ".join(Module
.Module
.ModuleEntryPointList
)
921 if int(str(Module
.AutoGenVersion
), 0) >= 0x00010005:
922 RealEntryPoint
= "_ModuleEntryPoint"
924 RealEntryPoint
= EntryPoint
925 if EntryPoint
== "_ModuleEntryPoint":
926 CCFlags
= Module
.BuildOption
.get("CC", {}).get("FLAGS", "")
927 Match
= gGlueLibEntryPoint
.search(CCFlags
)
929 EntryPoint
= Match
.group(1)
931 self
._FfsEntryPoint
[Module
.Guid
.upper()] = (EntryPoint
, RealEntryPoint
)
935 # Collect platform firmware volume list as the input of EOT.
939 for Fd
in Wa
.FdfProfile
.FdDict
:
940 for FdRegion
in Wa
.FdfProfile
.FdDict
[Fd
].RegionList
:
941 if FdRegion
.RegionType
!= "FV":
943 for FvName
in FdRegion
.RegionDataList
:
944 if FvName
in self
._FvList
:
946 self
._FvList
.append(FvName
)
947 for Ffs
in Wa
.FdfProfile
.FvDict
[FvName
.upper()].FfsList
:
948 for Section
in Ffs
.SectionList
:
950 for FvSection
in Section
.SectionList
:
951 if FvSection
.FvName
in self
._FvList
:
953 self
._FvList
.append(FvSection
.FvName
)
954 except AttributeError:
959 # Parse platform fixed address map files
961 # This function parses the platform final fixed address map file to get
962 # the database of predicted fixed address for module image base, entry point
965 # @param self: The object pointer
967 def _ParseMapFile(self
):
968 if self
._MapFileParsed
:
970 self
._MapFileParsed
= True
971 if os
.path
.isfile(self
._MapFileName
):
973 FileContents
= open(self
._MapFileName
).read()
974 for Match
in gMapFileItemPattern
.finditer(FileContents
):
975 AddressType
= Match
.group(1)
976 BaseAddress
= Match
.group(2)
977 EntryPoint
= Match
.group(3)
978 Guid
= Match
.group(4).upper()
979 List
= self
.FixedMapDict
.setdefault(Guid
, [])
980 List
.append((AddressType
, BaseAddress
, "*I"))
981 List
.append((AddressType
, EntryPoint
, "*E"))
983 EdkLogger
.warn(None, "Cannot open file to read", self
._MapFileName
)
986 # Invokes EOT tool to get the predicted the execution order.
988 # This function invokes EOT tool to calculate the predicted dispatch order
990 # @param self: The object pointer
992 def _InvokeEotTool(self
):
993 if self
._EotToolInvoked
:
996 self
._EotToolInvoked
= True
998 for FvName
in self
._FvList
:
999 FvFile
= os
.path
.join(self
._FvDir
, FvName
+ ".Fv")
1000 if os
.path
.isfile(FvFile
):
1001 FvFileList
.append(FvFile
)
1003 if len(FvFileList
) == 0:
1006 # Write source file list and GUID file list to an intermediate file
1007 # as the input for EOT tool and dispatch List as the output file
1010 SourceList
= os
.path
.join(self
._EotDir
, "SourceFile.txt")
1011 GuidList
= os
.path
.join(self
._EotDir
, "GuidList.txt")
1012 DispatchList
= os
.path
.join(self
._EotDir
, "Dispatch.txt")
1014 TempFile
= open(SourceList
, "w+")
1015 for Item
in self
._SourceList
:
1016 FileWrite(TempFile
, Item
)
1018 TempFile
= open(GuidList
, "w+")
1019 for Key
in self
._GuidMap
:
1020 FileWrite(TempFile
, "%s %s" % (Key
, self
._GuidMap
[Key
]))
1024 from Eot
.Eot
import Eot
1027 # Invoke EOT tool and echo its runtime performance
1029 EotStartTime
= time
.time()
1030 Eot(CommandLineOption
=False, SourceFileList
=SourceList
, GuidList
=GuidList
,
1031 FvFileList
=' '.join(FvFileList
), Dispatch
=DispatchList
, IsInit
=True)
1032 EotEndTime
= time
.time()
1033 EotDuration
= time
.strftime("%H:%M:%S", time
.gmtime(int(round(EotEndTime
- EotStartTime
))))
1034 EdkLogger
.quiet("EOT run time: %s\n" % EotDuration
)
1037 # Parse the output of EOT tool
1039 for Line
in open(DispatchList
):
1040 if len(Line
.split()) < 4:
1042 (Guid
, Phase
, FfsName
, FilePath
) = Line
.split()
1043 Symbol
= self
._FfsEntryPoint
.get(Guid
, [FfsName
, ""])[0]
1044 if len(Symbol
) > self
.MaxLen
:
1045 self
.MaxLen
= len(Symbol
)
1046 self
.ItemList
.append((Phase
, Symbol
, FilePath
))
1048 EdkLogger
.quiet("(Python %s on %s\n%s)" % (platform
.python_version(), sys
.platform
, traceback
.format_exc()))
1049 EdkLogger
.warn(None, "Failed to generate execution order prediction report, for some error occurred in executing EOT.")
1053 # Generate platform execution order report
1055 # This function generates the predicted module execution order.
1057 # @param self The object pointer
1058 # @param File The file object for report
1060 def _GenerateExecutionOrderReport(self
, File
):
1061 self
._InvokeEotTool
()
1062 if len(self
.ItemList
) == 0:
1064 FileWrite(File
, gSectionStart
)
1065 FileWrite(File
, "Execution Order Prediction")
1066 FileWrite(File
, "*P PEI phase")
1067 FileWrite(File
, "*D DXE phase")
1068 FileWrite(File
, "*E Module INF entry point name")
1069 FileWrite(File
, "*N Module notification function name")
1071 FileWrite(File
, "Type %-*s %s" % (self
.MaxLen
, "Symbol", "Module INF Path"))
1072 FileWrite(File
, gSectionSep
)
1073 for Item
in self
.ItemList
:
1074 FileWrite(File
, "*%sE %-*s %s" % (Item
[0], self
.MaxLen
, Item
[1], Item
[2]))
1076 FileWrite(File
, gSectionStart
)
1079 # Generate Fixed Address report.
1081 # This function generate the predicted fixed address report for a module
1082 # specified by Guid.
1084 # @param self The object pointer
1085 # @param File The file object for report
1086 # @param Guid The module Guid value.
1087 # @param NotifyList The list of all notify function in a module
1089 def _GenerateFixedAddressReport(self
, File
, Guid
, NotifyList
):
1090 self
._ParseMapFile
()
1091 FixedAddressList
= self
.FixedMapDict
.get(Guid
)
1092 if not FixedAddressList
:
1095 FileWrite(File
, gSubSectionStart
)
1096 FileWrite(File
, "Fixed Address Prediction")
1097 FileWrite(File
, "*I Image Loading Address")
1098 FileWrite(File
, "*E Entry Point Address")
1099 FileWrite(File
, "*N Notification Function Address")
1100 FileWrite(File
, "*F Flash Address")
1101 FileWrite(File
, "*M Memory Address")
1102 FileWrite(File
, "*S SMM RAM Offset")
1103 FileWrite(File
, "TOM Top of Memory")
1105 FileWrite(File
, "Type Address Name")
1106 FileWrite(File
, gSubSectionSep
)
1107 for Item
in FixedAddressList
:
1112 Name
= "(Image Base)"
1113 elif Symbol
== "*E":
1114 Name
= self
._FfsEntryPoint
.get(Guid
, ["", "_ModuleEntryPoint"])[1]
1115 elif Symbol
in NotifyList
:
1123 elif "Memory" in Type
:
1129 Value
= "TOM" + Value
1131 FileWrite(File
, "%s %-16s %s" % (Symbol
, Value
, Name
))
1134 # Generate report for the prediction part
1136 # This function generate the predicted fixed address report for a module or
1137 # predicted module execution order for a platform.
1138 # If the input Guid is None, then, it generates the predicted module execution order;
1139 # otherwise it generated the module fixed loading address for the module specified by
1142 # @param self The object pointer
1143 # @param File The file object for report
1144 # @param Guid The module Guid value.
1146 def GenerateReport(self
, File
, Guid
):
1148 self
._GenerateFixedAddressReport
(File
, Guid
.upper(), [])
1150 self
._GenerateExecutionOrderReport
(File
)
1153 # Reports FD region information
1155 # This class reports the FD subsection in the build report file.
1156 # It collects region information of platform flash device.
1157 # If the region is a firmware volume, it lists the set of modules
1158 # and its space information; otherwise, it only lists its region name,
1159 # base address and size in its sub-section header.
1160 # If there are nesting FVs, the nested FVs will list immediate after
1161 # this FD region subsection
1163 class FdRegionReport(object):
1165 # Discover all the nested FV name list.
1167 # This is an internal worker function to discover the all the nested FV information
1168 # in the parent firmware volume. It uses deep first search algorithm recursively to
1169 # find all the FV list name and append them to the list.
1171 # @param self The object pointer
1172 # @param FvName The name of current firmware file system
1173 # @param Wa Workspace context information
1175 def _DiscoverNestedFvList(self
, FvName
, Wa
):
1176 for Ffs
in Wa
.FdfProfile
.FvDict
[FvName
.upper()].FfsList
:
1177 for Section
in Ffs
.SectionList
:
1179 for FvSection
in Section
.SectionList
:
1180 if FvSection
.FvName
in self
.FvList
:
1182 self
._GuidsDb
[Ffs
.NameGuid
.upper()] = FvSection
.FvName
1183 self
.FvList
.append(FvSection
.FvName
)
1184 self
.FvInfo
[FvSection
.FvName
] = ("Nested FV", 0, 0)
1185 self
._DiscoverNestedFvList
(FvSection
.FvName
, Wa
)
1186 except AttributeError:
1190 # Constructor function for class FdRegionReport
1192 # This constructor function generates FdRegionReport object for a specified FdRegion.
1193 # If the FdRegion is a firmware volume, it will recursively find all its nested Firmware
1194 # volume list. This function also collects GUID map in order to dump module identification
1195 # in the final report.
1197 # @param self: The object pointer
1198 # @param FdRegion The current FdRegion object
1199 # @param Wa Workspace context information
1201 def __init__(self
, FdRegion
, Wa
):
1202 self
.Type
= FdRegion
.RegionType
1203 self
.BaseAddress
= FdRegion
.Offset
1204 self
.Size
= FdRegion
.Size
1208 self
._FvDir
= Wa
.FvDir
1211 # If the input FdRegion is not a firmware volume,
1214 if self
.Type
!= "FV":
1218 # Find all nested FVs in the FdRegion
1220 for FvName
in FdRegion
.RegionDataList
:
1221 if FvName
in self
.FvList
:
1223 self
.FvList
.append(FvName
)
1224 self
.FvInfo
[FvName
] = ("Fd Region", self
.BaseAddress
, self
.Size
)
1225 self
._DiscoverNestedFvList
(FvName
, Wa
)
1229 # Collect PCDs declared in DEC files.
1231 for Pa
in Wa
.AutoGenObjectList
:
1232 for Package
in Pa
.PackageList
:
1233 for (TokenCName
, TokenSpaceGuidCName
, DecType
) in Package
.Pcds
:
1234 DecDefaultValue
= Package
.Pcds
[TokenCName
, TokenSpaceGuidCName
, DecType
].DefaultValue
1235 PlatformPcds
[(TokenCName
, TokenSpaceGuidCName
)] = DecDefaultValue
1237 # Collect PCDs defined in DSC common section
1239 for Platform
in Wa
.BuildDatabase
.WorkspaceDb
.PlatformList
:
1240 for (TokenCName
, TokenSpaceGuidCName
) in Platform
.Pcds
:
1241 DscDefaultValue
= Platform
.Pcds
[(TokenCName
, TokenSpaceGuidCName
)].DefaultValue
1242 PlatformPcds
[(TokenCName
, TokenSpaceGuidCName
)] = DscDefaultValue
1245 # Add PEI and DXE a priori files GUIDs defined in PI specification.
1247 self
._GuidsDb
["1B45CC0A-156A-428A-AF62-49864DA0E6E6"] = "PEI Apriori"
1248 self
._GuidsDb
["FC510EE7-FFDC-11D4-BD41-0080C73C8881"] = "DXE Apriori"
1250 # Add ACPI table storage file
1252 self
._GuidsDb
["7E374E25-8E01-4FEE-87F2-390C23C606CD"] = "ACPI table storage"
1254 for Pa
in Wa
.AutoGenObjectList
:
1255 for ModuleKey
in Pa
.Platform
.Modules
:
1256 M
= Pa
.Platform
.Modules
[ModuleKey
].M
1257 InfPath
= os
.path
.join(Wa
.WorkspaceDir
, M
.MetaFile
.File
)
1258 self
._GuidsDb
[M
.Guid
.upper()] = "%s (%s)" % (M
.Module
.BaseName
, InfPath
)
1261 # Collect the GUID map in the FV firmware volume
1263 for FvName
in self
.FvList
:
1264 for Ffs
in Wa
.FdfProfile
.FvDict
[FvName
.upper()].FfsList
:
1267 # collect GUID map for binary EFI file in FDF file.
1269 Guid
= Ffs
.NameGuid
.upper()
1270 Match
= gPcdGuidPattern
.match(Ffs
.NameGuid
)
1272 PcdTokenspace
= Match
.group(1)
1273 PcdToken
= Match
.group(2)
1274 if (PcdToken
, PcdTokenspace
) in PlatformPcds
:
1275 GuidValue
= PlatformPcds
[(PcdToken
, PcdTokenspace
)]
1276 Guid
= GuidStructureByteArrayToGuidString(GuidValue
).upper()
1277 for Section
in Ffs
.SectionList
:
1279 ModuleSectFile
= os
.path
.join(Wa
.WorkspaceDir
, Section
.SectFileName
)
1280 self
._GuidsDb
[Guid
] = ModuleSectFile
1281 except AttributeError:
1283 except AttributeError:
1288 # Internal worker function to generate report for the FD region
1290 # This internal worker function to generate report for the FD region.
1291 # It the type is firmware volume, it lists offset and module identification.
1293 # @param self The object pointer
1294 # @param File The file object for report
1295 # @param Title The title for the FD subsection
1296 # @param BaseAddress The base address for the FD region
1297 # @param Size The size of the FD region
1298 # @param FvName The FV name if the FD region is a firmware volume
1300 def _GenerateReport(self
, File
, Title
, Type
, BaseAddress
, Size
=0, FvName
=None):
1301 FileWrite(File
, gSubSectionStart
)
1302 FileWrite(File
, Title
)
1303 FileWrite(File
, "Type: %s" % Type
)
1304 FileWrite(File
, "Base Address: 0x%X" % BaseAddress
)
1306 if self
.Type
== "FV":
1310 FvReportFileName
= os
.path
.join(self
._FvDir
, FvName
+ ".Fv.txt")
1313 # Collect size info in the firmware volume.
1315 FvReport
= open(FvReportFileName
).read()
1316 Match
= gFvTotalSizePattern
.search(FvReport
)
1318 FvTotalSize
= int(Match
.group(1), 16)
1319 Match
= gFvTakenSizePattern
.search(FvReport
)
1321 FvTakenSize
= int(Match
.group(1), 16)
1322 FvFreeSize
= FvTotalSize
- FvTakenSize
1324 # Write size information to the report file.
1326 FileWrite(File
, "Size: 0x%X (%.0fK)" % (FvTotalSize
, FvTotalSize
/ 1024.0))
1327 FileWrite(File
, "Fv Name: %s (%.1f%% Full)" % (FvName
, FvTakenSize
* 100.0 / FvTotalSize
))
1328 FileWrite(File
, "Occupied Size: 0x%X (%.0fK)" % (FvTakenSize
, FvTakenSize
/ 1024.0))
1329 FileWrite(File
, "Free Size: 0x%X (%.0fK)" % (FvFreeSize
, FvFreeSize
/ 1024.0))
1330 FileWrite(File
, "Offset Module")
1331 FileWrite(File
, gSubSectionSep
)
1333 # Write module offset and module identification to the report file.
1336 for Match
in gOffsetGuidPattern
.finditer(FvReport
):
1337 Guid
= Match
.group(2).upper()
1338 OffsetInfo
[Match
.group(1)] = self
._GuidsDb
.get(Guid
, Guid
)
1339 OffsetList
= OffsetInfo
.keys()
1341 for Offset
in OffsetList
:
1342 FileWrite (File
, "%s %s" % (Offset
, OffsetInfo
[Offset
]))
1344 EdkLogger
.warn(None, "Fail to read report file", FvReportFileName
)
1346 FileWrite(File
, "Size: 0x%X (%.0fK)" % (Size
, Size
/ 1024.0))
1347 FileWrite(File
, gSubSectionEnd
)
1350 # Generate report for the FD region
1352 # This function generates report for the FD region.
1354 # @param self The object pointer
1355 # @param File The file object for report
1357 def GenerateReport(self
, File
):
1358 if (len(self
.FvList
) > 0):
1359 for FvItem
in self
.FvList
:
1360 Info
= self
.FvInfo
[FvItem
]
1361 self
._GenerateReport
(File
, Info
[0], "FV", Info
[1], Info
[2], FvItem
)
1363 self
._GenerateReport
(File
, "FD Region", self
.Type
, self
.BaseAddress
, self
.Size
)
1366 # Reports FD information
1368 # This class reports the FD section in the build report file.
1369 # It collects flash device information for a platform.
1371 class FdReport(object):
1373 # Constructor function for class FdReport
1375 # This constructor function generates FdReport object for a specified
1378 # @param self The object pointer
1379 # @param Fd The current Firmware device object
1380 # @param Wa Workspace context information
1382 def __init__(self
, Fd
, Wa
):
1383 self
.FdName
= Fd
.FdUiName
1384 self
.BaseAddress
= Fd
.BaseAddress
1386 self
.FdRegionList
= [FdRegionReport(FdRegion
, Wa
) for FdRegion
in Fd
.RegionList
]
1389 # Generate report for the firmware device.
1391 # This function generates report for the firmware device.
1393 # @param self The object pointer
1394 # @param File The file object for report
1396 def GenerateReport(self
, File
):
1397 FileWrite(File
, gSectionStart
)
1398 FileWrite(File
, "Firmware Device (FD)")
1399 FileWrite(File
, "FD Name: %s" % self
.FdName
)
1400 FileWrite(File
, "Base Address: %s" % self
.BaseAddress
)
1401 FileWrite(File
, "Size: 0x%X (%.0fK)" % (self
.Size
, self
.Size
/ 1024.0))
1402 if len(self
.FdRegionList
) > 0:
1403 FileWrite(File
, gSectionSep
)
1404 for FdRegionItem
in self
.FdRegionList
:
1405 FdRegionItem
.GenerateReport(File
)
1407 FileWrite(File
, gSectionEnd
)
1412 # Reports platform information
1414 # This class reports the whole platform information
1416 class PlatformReport(object):
1418 # Constructor function for class PlatformReport
1420 # This constructor function generates PlatformReport object a platform build.
1421 # It generates report for platform summary, flash, global PCDs and detailed
1422 # module information for modules involved in platform build.
1424 # @param self The object pointer
1425 # @param Wa Workspace context information
1426 # @param MaList The list of modules in the platform build
1428 def __init__(self
, Wa
, MaList
, ReportType
):
1429 self
._WorkspaceDir
= Wa
.WorkspaceDir
1430 self
.PlatformName
= Wa
.Name
1431 self
.PlatformDscPath
= Wa
.Platform
1432 self
.Architectures
= " ".join(Wa
.ArchList
)
1433 self
.ToolChain
= Wa
.ToolChain
1434 self
.Target
= Wa
.BuildTarget
1435 self
.OutputPath
= os
.path
.join(Wa
.WorkspaceDir
, Wa
.OutputDir
)
1436 self
.BuildEnvironment
= platform
.platform()
1438 self
.PcdReport
= None
1439 if "PCD" in ReportType
:
1440 self
.PcdReport
= PcdReport(Wa
)
1442 self
.FdReportList
= []
1443 if "FLASH" in ReportType
and Wa
.FdfProfile
and MaList
== None:
1444 for Fd
in Wa
.FdfProfile
.FdDict
:
1445 self
.FdReportList
.append(FdReport(Wa
.FdfProfile
.FdDict
[Fd
], Wa
))
1447 self
.PredictionReport
= None
1448 if "FIXED_ADDRESS" in ReportType
or "EXECUTION_ORDER" in ReportType
:
1449 self
.PredictionReport
= PredictionReport(Wa
)
1451 self
.DepexParser
= None
1452 if "DEPEX" in ReportType
:
1453 self
.DepexParser
= DepexParser(Wa
)
1455 self
.ModuleReportList
= []
1457 self
._IsModuleBuild
= True
1459 self
.ModuleReportList
.append(ModuleReport(Ma
, ReportType
))
1461 self
._IsModuleBuild
= False
1462 for Pa
in Wa
.AutoGenObjectList
:
1463 for ModuleKey
in Pa
.Platform
.Modules
:
1464 self
.ModuleReportList
.append(ModuleReport(Pa
.Platform
.Modules
[ModuleKey
].M
, ReportType
))
1469 # Generate report for the whole platform.
1471 # This function generates report for platform information.
1472 # It comprises of platform summary, global PCD, flash and
1473 # module list sections.
1475 # @param self The object pointer
1476 # @param File The file object for report
1477 # @param BuildDuration The total time to build the modules
1478 # @param ReportType The kind of report items in the final report file
1480 def GenerateReport(self
, File
, BuildDuration
, ReportType
):
1481 FileWrite(File
, "Platform Summary")
1482 FileWrite(File
, "Platform Name: %s" % self
.PlatformName
)
1483 FileWrite(File
, "Platform DSC Path: %s" % self
.PlatformDscPath
)
1484 FileWrite(File
, "Architectures: %s" % self
.Architectures
)
1485 FileWrite(File
, "Tool Chain: %s" % self
.ToolChain
)
1486 FileWrite(File
, "Target: %s" % self
.Target
)
1487 FileWrite(File
, "Output Path: %s" % self
.OutputPath
)
1488 FileWrite(File
, "Build Environment: %s" % self
.BuildEnvironment
)
1489 FileWrite(File
, "Build Duration: %s" % BuildDuration
)
1490 FileWrite(File
, "Report Content: %s" % ", ".join(ReportType
))
1492 if not self
._IsModuleBuild
:
1493 if "PCD" in ReportType
:
1494 self
.PcdReport
.GenerateReport(File
, None)
1496 if "FLASH" in ReportType
:
1497 for FdReportListItem
in self
.FdReportList
:
1498 FdReportListItem
.GenerateReport(File
)
1500 for ModuleReportItem
in self
.ModuleReportList
:
1501 ModuleReportItem
.GenerateReport(File
, self
.PcdReport
, self
.PredictionReport
, self
.DepexParser
, ReportType
)
1503 if not self
._IsModuleBuild
:
1504 if "EXECUTION_ORDER" in ReportType
:
1505 self
.PredictionReport
.GenerateReport(File
, None)
1507 ## BuildReport class
1509 # This base class contain the routines to collect data and then
1510 # applies certain format to the output report
1512 class BuildReport(object):
1514 # Constructor function for class BuildReport
1516 # This constructor function generates BuildReport object a platform build.
1517 # It generates report for platform summary, flash, global PCDs and detailed
1518 # module information for modules involved in platform build.
1520 # @param self The object pointer
1521 # @param ReportFile The file name to save report file
1522 # @param ReportType The kind of report items in the final report file
1524 def __init__(self
, ReportFile
, ReportType
):
1525 self
.ReportFile
= ReportFile
1527 self
.ReportList
= []
1528 self
.ReportType
= []
1530 for ReportTypeItem
in ReportType
:
1531 if ReportTypeItem
not in self
.ReportType
:
1532 self
.ReportType
.append(ReportTypeItem
)
1534 self
.ReportType
= ["PCD", "LIBRARY", "BUILD_FLAGS", "DEPEX", "FLASH", "FIXED_ADDRESS"]
1536 # Adds platform report to the list
1538 # This function adds a platform report to the final report list.
1540 # @param self The object pointer
1541 # @param Wa Workspace context information
1542 # @param MaList The list of modules in the platform build
1544 def AddPlatformReport(self
, Wa
, MaList
=None):
1546 self
.ReportList
.append((Wa
, MaList
))
1549 # Generates the final report.
1551 # This function generates platform build report. It invokes GenerateReport()
1552 # method for every platform report in the list.
1554 # @param self The object pointer
1555 # @param BuildDuration The total time to build the modules
1557 def GenerateReport(self
, BuildDuration
):
1561 for (Wa
, MaList
) in self
.ReportList
:
1562 PlatformReport(Wa
, MaList
, self
.ReportType
).GenerateReport(File
, BuildDuration
, self
.ReportType
)
1563 Content
= FileLinesSplit(File
.getvalue(), gLineMaxLength
)
1564 SaveFileOnChange(self
.ReportFile
, Content
, True)
1565 EdkLogger
.quiet("Build report can be found at %s" % os
.path
.abspath(self
.ReportFile
))
1567 EdkLogger
.error(None, FILE_WRITE_FAILURE
, ExtraData
=self
.ReportFile
)
1569 EdkLogger
.error("BuildReport", CODE_ERROR
, "Unknown fatal error when generating build report", ExtraData
=self
.ReportFile
, RaiseError
=False)
1570 EdkLogger
.quiet("(Python %s on %s\n%s)" % (platform
.python_version(), sys
.platform
, traceback
.format_exc()))
1573 # This acts like the main() function for the script, unless it is 'import'ed into another script.
1574 if __name__
== '__main__':