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 - 2016, 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.
19 import Common
.LongFilePathOs
as os
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
43 from Common
.LongFilePathSupport
import OpenLongFilePath
as open
44 from Common
.MultipleWorkspace
import MultipleWorkspace
as mws
45 import Common
.GlobalData
as GlobalData
47 ## Pattern to extract contents in EDK DXS files
48 gDxsDependencyPattern
= re
.compile(r
"DEPENDENCY_START(.+)DEPENDENCY_END", re
.DOTALL
)
50 ## Pattern to find total FV total size, occupied size in flash report intermediate file
51 gFvTotalSizePattern
= re
.compile(r
"EFI_FV_TOTAL_SIZE = (0x[0-9a-fA-F]+)")
52 gFvTakenSizePattern
= re
.compile(r
"EFI_FV_TAKEN_SIZE = (0x[0-9a-fA-F]+)")
54 ## Pattern to find module size and time stamp in module summary report intermediate file
55 gModuleSizePattern
= re
.compile(r
"MODULE_SIZE = (\d+)")
56 gTimeStampPattern
= re
.compile(r
"TIME_STAMP = (\d+)")
58 ## Pattern to find GUID value in flash description files
59 gPcdGuidPattern
= re
.compile(r
"PCD\((\w+)[.](\w+)\)")
61 ## Pattern to collect offset, GUID value pair in the flash report intermediate file
62 gOffsetGuidPattern
= re
.compile(r
"(0x[0-9A-Fa-f]+) ([-A-Fa-f0-9]+)")
64 ## Pattern to find module base address and entry point in fixed flash map file
65 gModulePattern
= r
"\n[-\w]+\s*\(([^,]+),\s*BaseAddress=%(Address)s,\s*EntryPoint=%(Address)s\)\s*\(GUID=([-0-9A-Fa-f]+)[^)]*\)"
66 gMapFileItemPattern
= re
.compile(gModulePattern
% {"Address" : "(-?0[xX][0-9A-Fa-f]+)"})
68 ## Pattern to find all module referenced header files in source files
69 gIncludePattern
= re
.compile(r
'#include\s*["<]([^">]+)[">]')
70 gIncludePattern2
= re
.compile(r
"#include\s+EFI_([A-Z_]+)\s*[(]\s*(\w+)\s*[)]")
72 ## Pattern to find the entry point for EDK module using EDKII Glue library
73 gGlueLibEntryPoint
= re
.compile(r
"__EDKII_GLUE_MODULE_ENTRY_POINT__\s*=\s*(\w+)")
75 ## Tags for MaxLength of line in report
78 ## Tags for end of line in report
81 ## Tags for section start, end and separator
82 gSectionStart
= ">" + "=" * (gLineMaxLength
- 2) + "<"
83 gSectionEnd
= "<" + "=" * (gLineMaxLength
- 2) + ">" + "\n"
84 gSectionSep
= "=" * gLineMaxLength
86 ## Tags for subsection start, end and separator
87 gSubSectionStart
= ">" + "-" * (gLineMaxLength
- 2) + "<"
88 gSubSectionEnd
= "<" + "-" * (gLineMaxLength
- 2) + ">"
89 gSubSectionSep
= "-" * gLineMaxLength
92 ## The look up table to map PCD type to pair of report display type and DEC type
94 'FixedAtBuild' : ('FIXED', 'FixedAtBuild'),
95 'PatchableInModule': ('PATCH', 'PatchableInModule'),
96 'FeatureFlag' : ('FLAG', 'FeatureFlag'),
97 'Dynamic' : ('DYN', 'Dynamic'),
98 'DynamicHii' : ('DYNHII', 'Dynamic'),
99 'DynamicVpd' : ('DYNVPD', 'Dynamic'),
100 'DynamicEx' : ('DEX', 'DynamicEx'),
101 'DynamicExHii' : ('DEXHII', 'DynamicEx'),
102 'DynamicExVpd' : ('DEXVPD', 'DynamicEx'),
105 ## The look up table to map module type to driver type
107 'SEC' : '0x3 (SECURITY_CORE)',
108 'PEI_CORE' : '0x4 (PEI_CORE)',
109 'PEIM' : '0x6 (PEIM)',
110 'DXE_CORE' : '0x5 (DXE_CORE)',
111 'DXE_DRIVER' : '0x7 (DRIVER)',
112 'DXE_SAL_DRIVER' : '0x7 (DRIVER)',
113 'DXE_SMM_DRIVER' : '0x7 (DRIVER)',
114 'DXE_RUNTIME_DRIVER': '0x7 (DRIVER)',
115 'UEFI_DRIVER' : '0x7 (DRIVER)',
116 'UEFI_APPLICATION' : '0x9 (APPLICATION)',
117 'SMM_CORE' : '0xD (SMM_CORE)',
118 'SMM_DRIVER' : '0xA (SMM)', # Extension of module type to support PI 1.1 SMM drivers
121 ## The look up table of the supported opcode in the dependency expression binaries
122 gOpCodeList
= ["BEFORE", "AFTER", "PUSH", "AND", "OR", "NOT", "TRUE", "FALSE", "END", "SOR"]
125 # Writes a string to the file object.
127 # This function writes a string to the file object and a new line is appended
128 # afterwards. It may optionally wraps the string for better readability.
130 # @File The file object to write
131 # @String The string to be written to the file
132 # @Wrapper Indicates whether to wrap the string
134 def FileWrite(File
, String
, Wrapper
=False):
136 String
= textwrap
.fill(String
, 120)
137 File
.write(String
+ gEndOfLine
)
140 # Find all the header file that the module source directly includes.
142 # This function scans source code to find all header files the module may
143 # include. This is not accurate but very effective to find all the header
144 # file the module might include with #include statement.
146 # @Source The source file name
147 # @IncludePathList The list of include path to find the source file.
148 # @IncludeFiles The dictionary of current found include files.
150 def FindIncludeFiles(Source
, IncludePathList
, IncludeFiles
):
151 FileContents
= open(Source
).read()
153 # Find header files with pattern #include "XXX.h" or #include <XXX.h>
155 for Match
in gIncludePattern
.finditer(FileContents
):
156 FileName
= Match
.group(1).strip()
157 for Dir
in [os
.path
.dirname(Source
)] + IncludePathList
:
158 FullFileName
= os
.path
.normpath(os
.path
.join(Dir
, FileName
))
159 if os
.path
.exists(FullFileName
):
160 IncludeFiles
[FullFileName
.lower().replace("\\", "/")] = FullFileName
164 # Find header files with pattern like #include EFI_PPI_CONSUMER(XXX)
166 for Match
in gIncludePattern2
.finditer(FileContents
):
168 Type
= Match
.group(1)
169 if "ARCH_PROTOCOL" in Type
:
170 FileName
= "ArchProtocol/%(Key)s/%(Key)s.h" % {"Key" : Key
}
171 elif "PROTOCOL" in Type
:
172 FileName
= "Protocol/%(Key)s/%(Key)s.h" % {"Key" : Key
}
174 FileName
= "Ppi/%(Key)s/%(Key)s.h" % {"Key" : Key
}
176 FileName
= "Guid/%(Key)s/%(Key)s.h" % {"Key" : Key
}
179 for Dir
in IncludePathList
:
180 FullFileName
= os
.path
.normpath(os
.path
.join(Dir
, FileName
))
181 if os
.path
.exists(FullFileName
):
182 IncludeFiles
[FullFileName
.lower().replace("\\", "/")] = FullFileName
185 ## Split each lines in file
187 # This method is used to split the lines in file to make the length of each line
188 # less than MaxLength.
190 # @param Content The content of file
191 # @param MaxLength The Max Length of the line
193 def FileLinesSplit(Content
=None, MaxLength
=None):
194 ContentList
= Content
.split(TAB_LINE_BREAK
)
197 for Line
in ContentList
:
198 while len(Line
.rstrip()) > MaxLength
:
199 LineSpaceIndex
= Line
.rfind(TAB_SPACE_SPLIT
, 0, MaxLength
)
200 LineSlashIndex
= Line
.rfind(TAB_SLASH
, 0, MaxLength
)
201 LineBackSlashIndex
= Line
.rfind(TAB_BACK_SLASH
, 0, MaxLength
)
202 if max(LineSpaceIndex
, LineSlashIndex
, LineBackSlashIndex
) > 0:
203 LineBreakIndex
= max(LineSpaceIndex
, LineSlashIndex
, LineBackSlashIndex
)
205 LineBreakIndex
= MaxLength
206 NewContentList
.append(Line
[:LineBreakIndex
])
207 Line
= Line
[LineBreakIndex
:]
209 NewContentList
.append(Line
)
210 for NewLine
in NewContentList
:
211 NewContent
+= NewLine
+ TAB_LINE_BREAK
213 NewContent
= NewContent
.replace(TAB_LINE_BREAK
, gEndOfLine
).replace('\r\r\n', gEndOfLine
)
219 # Parse binary dependency expression section
221 # This utility class parses the dependency expression section and translate the readable
222 # GUID name and value.
224 class DepexParser(object):
226 # Constructor function for class DepexParser
228 # This constructor function collect GUID values so that the readable
229 # GUID name can be translated.
231 # @param self The object pointer
232 # @param Wa Workspace context information
234 def __init__(self
, Wa
):
236 for Pa
in Wa
.AutoGenObjectList
:
237 for Package
in Pa
.PackageList
:
238 for Protocol
in Package
.Protocols
:
239 GuidValue
= GuidStructureStringToGuidString(Package
.Protocols
[Protocol
])
240 self
._GuidDb
[GuidValue
.upper()] = Protocol
241 for Ppi
in Package
.Ppis
:
242 GuidValue
= GuidStructureStringToGuidString(Package
.Ppis
[Ppi
])
243 self
._GuidDb
[GuidValue
.upper()] = Ppi
244 for Guid
in Package
.Guids
:
245 GuidValue
= GuidStructureStringToGuidString(Package
.Guids
[Guid
])
246 self
._GuidDb
[GuidValue
.upper()] = Guid
249 # Parse the binary dependency expression files.
251 # This function parses the binary dependency expression file and translate it
252 # to the instruction list.
254 # @param self The object pointer
255 # @param DepexFileName The file name of binary dependency expression file.
257 def ParseDepexFile(self
, DepexFileName
):
258 DepexFile
= open(DepexFileName
, "rb")
260 OpCode
= DepexFile
.read(1)
262 Statement
= gOpCodeList
[struct
.unpack("B", OpCode
)[0]]
263 if Statement
in ["BEFORE", "AFTER", "PUSH"]:
264 GuidValue
= "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X" % \
265 struct
.unpack("=LHHBBBBBBBB", DepexFile
.read(16))
266 GuidString
= self
._GuidDb
.get(GuidValue
, GuidValue
)
267 Statement
= "%s %s" % (Statement
, GuidString
)
268 DepexStatement
.append(Statement
)
269 OpCode
= DepexFile
.read(1)
271 return DepexStatement
274 # Reports library information
276 # This class reports the module library subsection in the build report file.
278 class LibraryReport(object):
280 # Constructor function for class LibraryReport
282 # This constructor function generates LibraryReport object for
285 # @param self The object pointer
286 # @param M Module context information
288 def __init__(self
, M
):
289 self
.LibraryList
= []
290 if int(str(M
.AutoGenVersion
), 0) >= 0x00010005:
291 self
._EdkIIModule
= True
293 self
._EdkIIModule
= False
295 for Lib
in M
.DependentLibraryList
:
296 LibInfPath
= str(Lib
)
297 LibClassList
= Lib
.LibraryClass
[0].LibraryClass
298 LibConstructorList
= Lib
.ConstructorList
299 LibDesstructorList
= Lib
.DestructorList
300 LibDepexList
= Lib
.DepexExpression
[M
.Arch
, M
.ModuleType
]
301 self
.LibraryList
.append((LibInfPath
, LibClassList
, LibConstructorList
, LibDesstructorList
, LibDepexList
))
304 # Generate report for module library information
306 # This function generates report for the module library.
307 # If the module is EDKII style one, the additional library class, library
308 # constructor/destructor and dependency expression may also be reported.
310 # @param self The object pointer
311 # @param File The file object for report
313 def GenerateReport(self
, File
):
314 FileWrite(File
, gSubSectionStart
)
315 FileWrite(File
, TAB_BRG_LIBRARY
)
316 if len(self
.LibraryList
) > 0:
317 FileWrite(File
, gSubSectionSep
)
318 for LibraryItem
in self
.LibraryList
:
319 LibInfPath
= LibraryItem
[0]
320 FileWrite(File
, LibInfPath
)
323 # Report library class, library constructor and destructor for
324 # EDKII style module.
326 if self
._EdkIIModule
:
327 LibClass
= LibraryItem
[1]
329 LibConstructor
= " ".join(LibraryItem
[2])
331 EdkIILibInfo
+= " C = " + LibConstructor
332 LibDestructor
= " ".join(LibraryItem
[3])
334 EdkIILibInfo
+= " D = " + LibDestructor
335 LibDepex
= " ".join(LibraryItem
[4])
337 EdkIILibInfo
+= " Depex = " + LibDepex
339 FileWrite(File
, "{%s: %s}" % (LibClass
, EdkIILibInfo
))
341 FileWrite(File
, "{%s}" % LibClass
)
343 FileWrite(File
, gSubSectionEnd
)
346 # Reports dependency expression information
348 # This class reports the module dependency expression subsection in the build report file.
350 class DepexReport(object):
352 # Constructor function for class DepexReport
354 # This constructor function generates DepexReport object for
355 # a module. If the module source contains the DXS file (usually EDK
356 # style module), it uses the dependency in DXS file; otherwise,
357 # it uses the dependency expression from its own INF [Depex] section
358 # and then merges with the ones from its dependent library INF.
360 # @param self The object pointer
361 # @param M Module context information
363 def __init__(self
, M
):
365 self
._DepexFileName
= os
.path
.join(M
.BuildDir
, "OUTPUT", M
.Module
.BaseName
+ ".depex")
366 ModuleType
= M
.ModuleType
368 ModuleType
= gComponentType2ModuleType
.get(M
.ComponentType
, "")
370 if ModuleType
in ["SEC", "PEI_CORE", "DXE_CORE", "SMM_CORE", "UEFI_APPLICATION"]:
373 for Source
in M
.SourceFileList
:
374 if os
.path
.splitext(Source
.Path
)[1].lower() == ".dxs":
375 Match
= gDxsDependencyPattern
.search(open(Source
.Path
).read())
377 self
.Depex
= Match
.group(1).strip()
381 self
.Depex
= M
.DepexExpressionList
.get(M
.ModuleType
, "")
382 self
.ModuleDepex
= " ".join(M
.Module
.DepexExpression
[M
.Arch
, M
.ModuleType
])
383 if not self
.ModuleDepex
:
384 self
.ModuleDepex
= "(None)"
387 for Lib
in M
.DependentLibraryList
:
388 LibDepex
= " ".join(Lib
.DepexExpression
[M
.Arch
, M
.ModuleType
]).strip()
390 LibDepexList
.append("(" + LibDepex
+ ")")
391 self
.LibraryDepex
= " AND ".join(LibDepexList
)
392 if not self
.LibraryDepex
:
393 self
.LibraryDepex
= "(None)"
397 # Generate report for module dependency expression information
399 # This function generates report for the module dependency expression.
401 # @param self The object pointer
402 # @param File The file object for report
403 # @param GlobalDepexParser The platform global Dependency expression parser object
405 def GenerateReport(self
, File
, GlobalDepexParser
):
407 FileWrite(File
, gSubSectionStart
)
408 FileWrite(File
, TAB_DEPEX
)
409 FileWrite(File
, gSubSectionEnd
)
411 FileWrite(File
, gSubSectionStart
)
412 if os
.path
.isfile(self
._DepexFileName
):
414 DepexStatements
= GlobalDepexParser
.ParseDepexFile(self
._DepexFileName
)
415 FileWrite(File
, "Final Dependency Expression (DEPEX) Instructions")
416 for DepexStatement
in DepexStatements
:
417 FileWrite(File
, " %s" % DepexStatement
)
418 FileWrite(File
, gSubSectionSep
)
420 EdkLogger
.warn(None, "Dependency expression file is corrupted", self
._DepexFileName
)
422 FileWrite(File
, "Dependency Expression (DEPEX) from %s" % self
.Source
)
424 if self
.Source
== "INF":
425 FileWrite(File
, "%s" % self
.Depex
, True)
426 FileWrite(File
, gSubSectionSep
)
427 FileWrite(File
, "From Module INF: %s" % self
.ModuleDepex
, True)
428 FileWrite(File
, "From Library INF: %s" % self
.LibraryDepex
, True)
430 FileWrite(File
, "%s" % self
.Depex
)
431 FileWrite(File
, gSubSectionEnd
)
434 # Reports dependency expression information
436 # This class reports the module build flags subsection in the build report file.
438 class BuildFlagsReport(object):
440 # Constructor function for class BuildFlagsReport
442 # This constructor function generates BuildFlagsReport object for
443 # a module. It reports the build tool chain tag and all relevant
444 # build flags to build the module.
446 # @param self The object pointer
447 # @param M Module context information
449 def __init__(self
, M
):
452 # Add build flags according to source file extension so that
453 # irrelevant ones can be filtered out.
455 for Source
in M
.SourceFileList
:
456 Ext
= os
.path
.splitext(Source
.File
)[1].lower()
457 if Ext
in [".c", ".cc", ".cpp"]:
458 BuildOptions
["CC"] = 1
459 elif Ext
in [".s", ".asm"]:
460 BuildOptions
["PP"] = 1
461 BuildOptions
["ASM"] = 1
462 elif Ext
in [".vfr"]:
463 BuildOptions
["VFRPP"] = 1
464 BuildOptions
["VFR"] = 1
465 elif Ext
in [".dxs"]:
466 BuildOptions
["APP"] = 1
467 BuildOptions
["CC"] = 1
468 elif Ext
in [".asl"]:
469 BuildOptions
["ASLPP"] = 1
470 BuildOptions
["ASL"] = 1
471 elif Ext
in [".aslc"]:
472 BuildOptions
["ASLCC"] = 1
473 BuildOptions
["ASLDLINK"] = 1
474 BuildOptions
["CC"] = 1
475 elif Ext
in [".asm16"]:
476 BuildOptions
["ASMLINK"] = 1
477 BuildOptions
["SLINK"] = 1
478 BuildOptions
["DLINK"] = 1
481 # Save module build flags.
483 self
.ToolChainTag
= M
.ToolChain
485 for Tool
in BuildOptions
:
486 self
.BuildFlags
[Tool
+ "_FLAGS"] = M
.BuildOption
.get(Tool
, {}).get("FLAGS", "")
489 # Generate report for module build flags information
491 # This function generates report for the module build flags expression.
493 # @param self The object pointer
494 # @param File The file object for report
496 def GenerateReport(self
, File
):
497 FileWrite(File
, gSubSectionStart
)
498 FileWrite(File
, "Build Flags")
499 FileWrite(File
, "Tool Chain Tag: %s" % self
.ToolChainTag
)
500 for Tool
in self
.BuildFlags
:
501 FileWrite(File
, gSubSectionSep
)
502 FileWrite(File
, "%s = %s" % (Tool
, self
.BuildFlags
[Tool
]), True)
504 FileWrite(File
, gSubSectionEnd
)
508 # Reports individual module information
510 # This class reports the module section in the build report file.
511 # It comprises of module summary, module PCD, library, dependency expression,
512 # build flags sections.
514 class ModuleReport(object):
516 # Constructor function for class ModuleReport
518 # This constructor function generates ModuleReport object for
519 # a separate module in a platform build.
521 # @param self The object pointer
522 # @param M Module context information
523 # @param ReportType The kind of report items in the final report file
525 def __init__(self
, M
, ReportType
):
526 self
.ModuleName
= M
.Module
.BaseName
527 self
.ModuleInfPath
= M
.MetaFile
.File
528 self
.FileGuid
= M
.Guid
530 self
.BuildTimeStamp
= None
533 ModuleType
= M
.ModuleType
535 ModuleType
= gComponentType2ModuleType
.get(M
.ComponentType
, "")
537 # If a module complies to PI 1.1, promote Module type to "SMM_DRIVER"
539 if ModuleType
== "DXE_SMM_DRIVER":
540 PiSpec
= M
.Module
.Specification
.get("PI_SPECIFICATION_VERSION", "0x00010000")
541 if int(PiSpec
, 0) >= 0x0001000A:
542 ModuleType
= "SMM_DRIVER"
543 self
.DriverType
= gDriverTypeMap
.get(ModuleType
, "0x2 (FREE_FORM)")
544 self
.UefiSpecVersion
= M
.Module
.Specification
.get("UEFI_SPECIFICATION_VERSION", "")
545 self
.PiSpecVersion
= M
.Module
.Specification
.get("PI_SPECIFICATION_VERSION", "")
546 self
.PciDeviceId
= M
.Module
.Defines
.get("PCI_DEVICE_ID", "")
547 self
.PciVendorId
= M
.Module
.Defines
.get("PCI_VENDOR_ID", "")
548 self
.PciClassCode
= M
.Module
.Defines
.get("PCI_CLASS_CODE", "")
550 self
._BuildDir
= M
.BuildDir
551 self
.ModulePcdSet
= {}
552 if "PCD" in ReportType
:
554 # Collect all module used PCD set: module INF referenced directly or indirectly.
555 # It also saves module INF default values of them in case they exist.
557 for Pcd
in M
.ModulePcdList
+ M
.LibraryPcdList
:
558 self
.ModulePcdSet
.setdefault((Pcd
.TokenCName
, Pcd
.TokenSpaceGuidCName
, Pcd
.Type
), (Pcd
.InfDefaultValue
, Pcd
.DefaultValue
))
560 self
.LibraryReport
= None
561 if "LIBRARY" in ReportType
:
562 self
.LibraryReport
= LibraryReport(M
)
564 self
.DepexReport
= None
565 if "DEPEX" in ReportType
:
566 self
.DepexReport
= DepexReport(M
)
568 if "BUILD_FLAGS" in ReportType
:
569 self
.BuildFlagsReport
= BuildFlagsReport(M
)
573 # Generate report for module information
575 # This function generates report for separate module expression
576 # in a platform build.
578 # @param self The object pointer
579 # @param File The file object for report
580 # @param GlobalPcdReport The platform global PCD report object
581 # @param GlobalPredictionReport The platform global Prediction report object
582 # @param GlobalDepexParser The platform global Dependency expression parser object
583 # @param ReportType The kind of report items in the final report file
585 def GenerateReport(self
, File
, GlobalPcdReport
, GlobalPredictionReport
, GlobalDepexParser
, ReportType
):
586 FileWrite(File
, gSectionStart
)
588 FwReportFileName
= os
.path
.join(self
._BuildDir
, "DEBUG", self
.ModuleName
+ ".txt")
589 if os
.path
.isfile(FwReportFileName
):
591 FileContents
= open(FwReportFileName
).read()
592 Match
= gModuleSizePattern
.search(FileContents
)
594 self
.Size
= int(Match
.group(1))
596 Match
= gTimeStampPattern
.search(FileContents
)
598 self
.BuildTimeStamp
= datetime
.fromtimestamp(int(Match
.group(1)))
600 EdkLogger
.warn(None, "Fail to read report file", FwReportFileName
)
602 FileWrite(File
, "Module Summary")
603 FileWrite(File
, "Module Name: %s" % self
.ModuleName
)
604 FileWrite(File
, "Module INF Path: %s" % self
.ModuleInfPath
)
605 FileWrite(File
, "File GUID: %s" % self
.FileGuid
)
607 FileWrite(File
, "Size: 0x%X (%.2fK)" % (self
.Size
, self
.Size
/ 1024.0))
608 if self
.BuildTimeStamp
:
609 FileWrite(File
, "Build Time Stamp: %s" % self
.BuildTimeStamp
)
611 FileWrite(File
, "Driver Type: %s" % self
.DriverType
)
612 if self
.UefiSpecVersion
:
613 FileWrite(File
, "UEFI Spec Version: %s" % self
.UefiSpecVersion
)
614 if self
.PiSpecVersion
:
615 FileWrite(File
, "PI Spec Version: %s" % self
.PiSpecVersion
)
617 FileWrite(File
, "PCI Device ID: %s" % self
.PciDeviceId
)
619 FileWrite(File
, "PCI Vendor ID: %s" % self
.PciVendorId
)
620 if self
.PciClassCode
:
621 FileWrite(File
, "PCI Class Code: %s" % self
.PciClassCode
)
623 FileWrite(File
, gSectionSep
)
625 if "PCD" in ReportType
:
626 GlobalPcdReport
.GenerateReport(File
, self
.ModulePcdSet
)
628 if "LIBRARY" in ReportType
:
629 self
.LibraryReport
.GenerateReport(File
)
631 if "DEPEX" in ReportType
:
632 self
.DepexReport
.GenerateReport(File
, GlobalDepexParser
)
634 if "BUILD_FLAGS" in ReportType
:
635 self
.BuildFlagsReport
.GenerateReport(File
)
637 if "FIXED_ADDRESS" in ReportType
and self
.FileGuid
:
638 GlobalPredictionReport
.GenerateReport(File
, self
.FileGuid
)
640 FileWrite(File
, gSectionEnd
)
643 # Reports platform and module PCD information
645 # This class reports the platform PCD section and module PCD subsection
646 # in the build report file.
648 class PcdReport(object):
650 # Constructor function for class PcdReport
652 # This constructor function generates PcdReport object a platform build.
653 # It collects the whole PCD database from platform DSC files, platform
654 # flash description file and package DEC files.
656 # @param self The object pointer
657 # @param Wa Workspace context information
659 def __init__(self
, Wa
):
663 self
.FdfPcdSet
= Wa
.FdfProfile
.PcdDict
667 self
.ModulePcdOverride
= {}
668 for Pa
in Wa
.AutoGenObjectList
:
670 # Collect all platform referenced PCDs and grouped them by PCD token space
673 for Pcd
in Pa
.AllPcdList
:
674 PcdList
= self
.AllPcds
.setdefault(Pcd
.TokenSpaceGuidCName
, {}).setdefault(Pcd
.Type
, [])
675 if Pcd
not in PcdList
:
677 if len(Pcd
.TokenCName
) > self
.MaxLen
:
678 self
.MaxLen
= len(Pcd
.TokenCName
)
680 for Module
in Pa
.Platform
.Modules
.values():
682 # Collect module override PCDs
684 for ModulePcd
in Module
.M
.ModulePcdList
+ Module
.M
.LibraryPcdList
:
685 TokenCName
= ModulePcd
.TokenCName
686 TokenSpaceGuid
= ModulePcd
.TokenSpaceGuidCName
687 ModuleDefault
= ModulePcd
.DefaultValue
688 ModulePath
= os
.path
.basename(Module
.M
.MetaFile
.File
)
689 self
.ModulePcdOverride
.setdefault((TokenCName
, TokenSpaceGuid
), {})[ModulePath
] = ModuleDefault
693 # Collect PCD DEC default value.
695 self
.DecPcdDefault
= {}
696 for Pa
in Wa
.AutoGenObjectList
:
697 for Package
in Pa
.PackageList
:
698 for (TokenCName
, TokenSpaceGuidCName
, DecType
) in Package
.Pcds
:
699 DecDefaultValue
= Package
.Pcds
[TokenCName
, TokenSpaceGuidCName
, DecType
].DefaultValue
700 self
.DecPcdDefault
.setdefault((TokenCName
, TokenSpaceGuidCName
, DecType
), DecDefaultValue
)
702 # Collect PCDs defined in DSC common section
704 self
.DscPcdDefault
= {}
705 for Arch
in Wa
.ArchList
:
706 Platform
= Wa
.BuildDatabase
[Wa
.MetaFile
, Arch
, Wa
.BuildTarget
, Wa
.ToolChain
]
707 for (TokenCName
, TokenSpaceGuidCName
) in Platform
.Pcds
:
708 DscDefaultValue
= Platform
.Pcds
[(TokenCName
, TokenSpaceGuidCName
)].DefaultValue
710 self
.DscPcdDefault
[(TokenCName
, TokenSpaceGuidCName
)] = DscDefaultValue
713 # Generate report for PCD information
715 # This function generates report for separate module expression
716 # in a platform build.
718 # @param self The object pointer
719 # @param File The file object for report
720 # @param ModulePcdSet Set of all PCDs referenced by module or None for
721 # platform PCD report
722 # @param DscOverridePcds Module DSC override PCDs set
724 def GenerateReport(self
, File
, ModulePcdSet
):
725 if ModulePcdSet
== None:
727 # For platform global PCD section
729 FileWrite(File
, gSectionStart
)
730 FileWrite(File
, "Platform Configuration Database Report")
731 FileWrite(File
, " *B - PCD override in the build option")
732 FileWrite(File
, " *P - Platform scoped PCD override in DSC file")
733 FileWrite(File
, " *F - Platform scoped PCD override in FDF file")
734 FileWrite(File
, " *M - Module scoped PCD override")
735 FileWrite(File
, gSectionSep
)
738 # For module PCD sub-section
740 FileWrite(File
, gSubSectionStart
)
741 FileWrite(File
, TAB_BRG_PCD
)
742 FileWrite(File
, gSubSectionSep
)
744 for Key
in self
.AllPcds
:
746 # Group PCD by their token space GUID C Name
749 for Type
in self
.AllPcds
[Key
]:
751 # Group PCD by their usage type
753 TypeName
, DecType
= gPcdTypeMap
.get(Type
, ("", Type
))
754 for Pcd
in self
.AllPcds
[Key
][Type
]:
756 # Get PCD default value and their override relationship
758 DecDefaultValue
= self
.DecPcdDefault
.get((Pcd
.TokenCName
, Pcd
.TokenSpaceGuidCName
, DecType
))
759 DscDefaultValue
= self
.DscPcdDefault
.get((Pcd
.TokenCName
, Pcd
.TokenSpaceGuidCName
))
760 DscDefaultValue
= self
.FdfPcdSet
.get((Pcd
.TokenCName
, Key
), DscDefaultValue
)
761 InfDefaultValue
= None
763 PcdValue
= DecDefaultValue
765 PcdValue
= DscDefaultValue
766 if ModulePcdSet
!= None:
767 if (Pcd
.TokenCName
, Pcd
.TokenSpaceGuidCName
, Type
) not in ModulePcdSet
:
769 InfDefault
, PcdValue
= ModulePcdSet
[Pcd
.TokenCName
, Pcd
.TokenSpaceGuidCName
, Type
]
773 BuildOptionMatch
= False
774 if GlobalData
.BuildOptionPcd
:
775 for pcd
in GlobalData
.BuildOptionPcd
:
776 if (Pcd
.TokenSpaceGuidCName
, Pcd
.TokenCName
) == (pcd
[0], pcd
[1]):
778 BuildOptionMatch
= True
782 if ModulePcdSet
== None:
788 if Pcd
.DatumType
in ('UINT8', 'UINT16', 'UINT32', 'UINT64'):
789 PcdValueNumber
= int(PcdValue
.strip(), 0)
790 if DecDefaultValue
== None:
793 DecDefaultValueNumber
= int(DecDefaultValue
.strip(), 0)
794 DecMatch
= (DecDefaultValueNumber
== PcdValueNumber
)
796 if InfDefaultValue
== None:
799 InfDefaultValueNumber
= int(InfDefaultValue
.strip(), 0)
800 InfMatch
= (InfDefaultValueNumber
== PcdValueNumber
)
802 if DscDefaultValue
== None:
805 DscDefaultValueNumber
= int(DscDefaultValue
.strip(), 0)
806 DscMatch
= (DscDefaultValueNumber
== PcdValueNumber
)
808 if DecDefaultValue
== None:
811 DecMatch
= (DecDefaultValue
.strip() == PcdValue
.strip())
813 if InfDefaultValue
== None:
816 InfMatch
= (InfDefaultValue
.strip() == PcdValue
.strip())
818 if DscDefaultValue
== None:
821 DscMatch
= (DscDefaultValue
.strip() == PcdValue
.strip())
824 # Report PCD item according to their override relationship
827 FileWrite(File
, ' *B %-*s: %6s %10s = %-22s' % (self
.MaxLen
, Pcd
.TokenCName
, TypeName
, '(' + Pcd
.DatumType
+ ')', PcdValue
.strip()))
828 elif DecMatch
and InfMatch
:
829 FileWrite(File
, ' %-*s: %6s %10s = %-22s' % (self
.MaxLen
, Pcd
.TokenCName
, TypeName
, '(' + Pcd
.DatumType
+ ')', PcdValue
.strip()))
832 if (Pcd
.TokenCName
, Key
) in self
.FdfPcdSet
:
833 FileWrite(File
, ' *F %-*s: %6s %10s = %-22s' % (self
.MaxLen
, Pcd
.TokenCName
, TypeName
, '(' + Pcd
.DatumType
+ ')', PcdValue
.strip()))
835 FileWrite(File
, ' *P %-*s: %6s %10s = %-22s' % (self
.MaxLen
, Pcd
.TokenCName
, TypeName
, '(' + Pcd
.DatumType
+ ')', PcdValue
.strip()))
837 FileWrite(File
, ' *M %-*s: %6s %10s = %-22s' % (self
.MaxLen
, Pcd
.TokenCName
, TypeName
, '(' + Pcd
.DatumType
+ ')', PcdValue
.strip()))
839 if TypeName
in ('DYNHII', 'DEXHII', 'DYNVPD', 'DEXVPD'):
840 for SkuInfo
in Pcd
.SkuInfoList
.values():
841 if TypeName
in ('DYNHII', 'DEXHII'):
842 FileWrite(File
, '%*s: %s: %s' % (self
.MaxLen
+ 4, SkuInfo
.VariableGuid
, SkuInfo
.VariableName
, SkuInfo
.VariableOffset
))
844 FileWrite(File
, '%*s' % (self
.MaxLen
+ 4, SkuInfo
.VpdOffset
))
846 if not DscMatch
and DscDefaultValue
!= None:
847 FileWrite(File
, ' %*s = %s' % (self
.MaxLen
+ 19, 'DSC DEFAULT', DscDefaultValue
.strip()))
849 if not InfMatch
and InfDefaultValue
!= None:
850 FileWrite(File
, ' %*s = %s' % (self
.MaxLen
+ 19, 'INF DEFAULT', InfDefaultValue
.strip()))
852 if not DecMatch
and DecDefaultValue
!= None:
853 FileWrite(File
, ' %*s = %s' % (self
.MaxLen
+ 19, 'DEC DEFAULT', DecDefaultValue
.strip()))
855 if ModulePcdSet
== None:
856 if not BuildOptionMatch
:
857 ModuleOverride
= self
.ModulePcdOverride
.get((Pcd
.TokenCName
, Pcd
.TokenSpaceGuidCName
), {})
858 for ModulePath
in ModuleOverride
:
859 ModuleDefault
= ModuleOverride
[ModulePath
]
860 if Pcd
.DatumType
in ('UINT8', 'UINT16', 'UINT32', 'UINT64'):
861 ModulePcdDefaultValueNumber
= int(ModuleDefault
.strip(), 0)
862 Match
= (ModulePcdDefaultValueNumber
== PcdValueNumber
)
864 Match
= (ModuleDefault
.strip() == PcdValue
.strip())
867 FileWrite(File
, ' *M %-*s = %s' % (self
.MaxLen
+ 19, ModulePath
, ModuleDefault
.strip()))
869 if ModulePcdSet
== None:
870 FileWrite(File
, gSectionEnd
)
872 FileWrite(File
, gSubSectionEnd
)
877 # Reports platform and module Prediction information
879 # This class reports the platform execution order prediction section and
880 # module load fixed address prediction subsection in the build report file.
882 class PredictionReport(object):
884 # Constructor function for class PredictionReport
886 # This constructor function generates PredictionReport object for the platform.
888 # @param self: The object pointer
889 # @param Wa Workspace context information
891 def __init__(self
, Wa
):
892 self
._MapFileName
= os
.path
.join(Wa
.BuildDir
, Wa
.Name
+ ".map")
893 self
._MapFileParsed
= False
894 self
._EotToolInvoked
= False
895 self
._FvDir
= Wa
.FvDir
896 self
._EotDir
= Wa
.BuildDir
897 self
._FfsEntryPoint
= {}
899 self
._SourceList
= []
900 self
.FixedMapDict
= {}
905 # Collect all platform reference source files and GUID C Name
907 for Pa
in Wa
.AutoGenObjectList
:
908 for Module
in Pa
.LibraryAutoGenList
+ Pa
.ModuleAutoGenList
:
910 # BASE typed modules are EFI agnostic, so we need not scan
911 # their source code to find PPI/Protocol produce or consume
914 if Module
.ModuleType
== "BASE":
917 # Add module referenced source files
919 self
._SourceList
.append(str(Module
))
921 for Source
in Module
.SourceFileList
:
922 if os
.path
.splitext(str(Source
))[1].lower() == ".c":
923 self
._SourceList
.append(" " + str(Source
))
924 FindIncludeFiles(Source
.Path
, Module
.IncludePathList
, IncludeList
)
925 for IncludeFile
in IncludeList
.values():
926 self
._SourceList
.append(" " + IncludeFile
)
928 for Guid
in Module
.PpiList
:
929 self
._GuidMap
[Guid
] = GuidStructureStringToGuidString(Module
.PpiList
[Guid
])
930 for Guid
in Module
.ProtocolList
:
931 self
._GuidMap
[Guid
] = GuidStructureStringToGuidString(Module
.ProtocolList
[Guid
])
932 for Guid
in Module
.GuidList
:
933 self
._GuidMap
[Guid
] = GuidStructureStringToGuidString(Module
.GuidList
[Guid
])
935 if Module
.Guid
and not Module
.IsLibrary
:
936 EntryPoint
= " ".join(Module
.Module
.ModuleEntryPointList
)
937 if int(str(Module
.AutoGenVersion
), 0) >= 0x00010005:
938 RealEntryPoint
= "_ModuleEntryPoint"
940 RealEntryPoint
= EntryPoint
941 if EntryPoint
== "_ModuleEntryPoint":
942 CCFlags
= Module
.BuildOption
.get("CC", {}).get("FLAGS", "")
943 Match
= gGlueLibEntryPoint
.search(CCFlags
)
945 EntryPoint
= Match
.group(1)
947 self
._FfsEntryPoint
[Module
.Guid
.upper()] = (EntryPoint
, RealEntryPoint
)
951 # Collect platform firmware volume list as the input of EOT.
955 for Fd
in Wa
.FdfProfile
.FdDict
:
956 for FdRegion
in Wa
.FdfProfile
.FdDict
[Fd
].RegionList
:
957 if FdRegion
.RegionType
!= "FV":
959 for FvName
in FdRegion
.RegionDataList
:
960 if FvName
in self
._FvList
:
962 self
._FvList
.append(FvName
)
963 for Ffs
in Wa
.FdfProfile
.FvDict
[FvName
.upper()].FfsList
:
964 for Section
in Ffs
.SectionList
:
966 for FvSection
in Section
.SectionList
:
967 if FvSection
.FvName
in self
._FvList
:
969 self
._FvList
.append(FvSection
.FvName
)
970 except AttributeError:
975 # Parse platform fixed address map files
977 # This function parses the platform final fixed address map file to get
978 # the database of predicted fixed address for module image base, entry point
981 # @param self: The object pointer
983 def _ParseMapFile(self
):
984 if self
._MapFileParsed
:
986 self
._MapFileParsed
= True
987 if os
.path
.isfile(self
._MapFileName
):
989 FileContents
= open(self
._MapFileName
).read()
990 for Match
in gMapFileItemPattern
.finditer(FileContents
):
991 AddressType
= Match
.group(1)
992 BaseAddress
= Match
.group(2)
993 EntryPoint
= Match
.group(3)
994 Guid
= Match
.group(4).upper()
995 List
= self
.FixedMapDict
.setdefault(Guid
, [])
996 List
.append((AddressType
, BaseAddress
, "*I"))
997 List
.append((AddressType
, EntryPoint
, "*E"))
999 EdkLogger
.warn(None, "Cannot open file to read", self
._MapFileName
)
1002 # Invokes EOT tool to get the predicted the execution order.
1004 # This function invokes EOT tool to calculate the predicted dispatch order
1006 # @param self: The object pointer
1008 def _InvokeEotTool(self
):
1009 if self
._EotToolInvoked
:
1012 self
._EotToolInvoked
= True
1014 for FvName
in self
._FvList
:
1015 FvFile
= os
.path
.join(self
._FvDir
, FvName
+ ".Fv")
1016 if os
.path
.isfile(FvFile
):
1017 FvFileList
.append(FvFile
)
1019 if len(FvFileList
) == 0:
1022 # Write source file list and GUID file list to an intermediate file
1023 # as the input for EOT tool and dispatch List as the output file
1026 SourceList
= os
.path
.join(self
._EotDir
, "SourceFile.txt")
1027 GuidList
= os
.path
.join(self
._EotDir
, "GuidList.txt")
1028 DispatchList
= os
.path
.join(self
._EotDir
, "Dispatch.txt")
1030 TempFile
= open(SourceList
, "w+")
1031 for Item
in self
._SourceList
:
1032 FileWrite(TempFile
, Item
)
1034 TempFile
= open(GuidList
, "w+")
1035 for Key
in self
._GuidMap
:
1036 FileWrite(TempFile
, "%s %s" % (Key
, self
._GuidMap
[Key
]))
1040 from Eot
.Eot
import Eot
1043 # Invoke EOT tool and echo its runtime performance
1045 EotStartTime
= time
.time()
1046 Eot(CommandLineOption
=False, SourceFileList
=SourceList
, GuidList
=GuidList
,
1047 FvFileList
=' '.join(FvFileList
), Dispatch
=DispatchList
, IsInit
=True)
1048 EotEndTime
= time
.time()
1049 EotDuration
= time
.strftime("%H:%M:%S", time
.gmtime(int(round(EotEndTime
- EotStartTime
))))
1050 EdkLogger
.quiet("EOT run time: %s\n" % EotDuration
)
1053 # Parse the output of EOT tool
1055 for Line
in open(DispatchList
):
1056 if len(Line
.split()) < 4:
1058 (Guid
, Phase
, FfsName
, FilePath
) = Line
.split()
1059 Symbol
= self
._FfsEntryPoint
.get(Guid
, [FfsName
, ""])[0]
1060 if len(Symbol
) > self
.MaxLen
:
1061 self
.MaxLen
= len(Symbol
)
1062 self
.ItemList
.append((Phase
, Symbol
, FilePath
))
1064 EdkLogger
.quiet("(Python %s on %s\n%s)" % (platform
.python_version(), sys
.platform
, traceback
.format_exc()))
1065 EdkLogger
.warn(None, "Failed to generate execution order prediction report, for some error occurred in executing EOT.")
1069 # Generate platform execution order report
1071 # This function generates the predicted module execution order.
1073 # @param self The object pointer
1074 # @param File The file object for report
1076 def _GenerateExecutionOrderReport(self
, File
):
1077 self
._InvokeEotTool
()
1078 if len(self
.ItemList
) == 0:
1080 FileWrite(File
, gSectionStart
)
1081 FileWrite(File
, "Execution Order Prediction")
1082 FileWrite(File
, "*P PEI phase")
1083 FileWrite(File
, "*D DXE phase")
1084 FileWrite(File
, "*E Module INF entry point name")
1085 FileWrite(File
, "*N Module notification function name")
1087 FileWrite(File
, "Type %-*s %s" % (self
.MaxLen
, "Symbol", "Module INF Path"))
1088 FileWrite(File
, gSectionSep
)
1089 for Item
in self
.ItemList
:
1090 FileWrite(File
, "*%sE %-*s %s" % (Item
[0], self
.MaxLen
, Item
[1], Item
[2]))
1092 FileWrite(File
, gSectionStart
)
1095 # Generate Fixed Address report.
1097 # This function generate the predicted fixed address report for a module
1098 # specified by Guid.
1100 # @param self The object pointer
1101 # @param File The file object for report
1102 # @param Guid The module Guid value.
1103 # @param NotifyList The list of all notify function in a module
1105 def _GenerateFixedAddressReport(self
, File
, Guid
, NotifyList
):
1106 self
._ParseMapFile
()
1107 FixedAddressList
= self
.FixedMapDict
.get(Guid
)
1108 if not FixedAddressList
:
1111 FileWrite(File
, gSubSectionStart
)
1112 FileWrite(File
, "Fixed Address Prediction")
1113 FileWrite(File
, "*I Image Loading Address")
1114 FileWrite(File
, "*E Entry Point Address")
1115 FileWrite(File
, "*N Notification Function Address")
1116 FileWrite(File
, "*F Flash Address")
1117 FileWrite(File
, "*M Memory Address")
1118 FileWrite(File
, "*S SMM RAM Offset")
1119 FileWrite(File
, "TOM Top of Memory")
1121 FileWrite(File
, "Type Address Name")
1122 FileWrite(File
, gSubSectionSep
)
1123 for Item
in FixedAddressList
:
1128 Name
= "(Image Base)"
1129 elif Symbol
== "*E":
1130 Name
= self
._FfsEntryPoint
.get(Guid
, ["", "_ModuleEntryPoint"])[1]
1131 elif Symbol
in NotifyList
:
1139 elif "Memory" in Type
:
1145 Value
= "TOM" + Value
1147 FileWrite(File
, "%s %-16s %s" % (Symbol
, Value
, Name
))
1150 # Generate report for the prediction part
1152 # This function generate the predicted fixed address report for a module or
1153 # predicted module execution order for a platform.
1154 # If the input Guid is None, then, it generates the predicted module execution order;
1155 # otherwise it generated the module fixed loading address for the module specified by
1158 # @param self The object pointer
1159 # @param File The file object for report
1160 # @param Guid The module Guid value.
1162 def GenerateReport(self
, File
, Guid
):
1164 self
._GenerateFixedAddressReport
(File
, Guid
.upper(), [])
1166 self
._GenerateExecutionOrderReport
(File
)
1169 # Reports FD region information
1171 # This class reports the FD subsection in the build report file.
1172 # It collects region information of platform flash device.
1173 # If the region is a firmware volume, it lists the set of modules
1174 # and its space information; otherwise, it only lists its region name,
1175 # base address and size in its sub-section header.
1176 # If there are nesting FVs, the nested FVs will list immediate after
1177 # this FD region subsection
1179 class FdRegionReport(object):
1181 # Discover all the nested FV name list.
1183 # This is an internal worker function to discover the all the nested FV information
1184 # in the parent firmware volume. It uses deep first search algorithm recursively to
1185 # find all the FV list name and append them to the list.
1187 # @param self The object pointer
1188 # @param FvName The name of current firmware file system
1189 # @param Wa Workspace context information
1191 def _DiscoverNestedFvList(self
, FvName
, Wa
):
1192 FvDictKey
=FvName
.upper()
1193 if FvDictKey
in Wa
.FdfProfile
.FvDict
:
1194 for Ffs
in Wa
.FdfProfile
.FvDict
[FvName
.upper()].FfsList
:
1195 for Section
in Ffs
.SectionList
:
1197 for FvSection
in Section
.SectionList
:
1198 if FvSection
.FvName
in self
.FvList
:
1200 self
._GuidsDb
[Ffs
.NameGuid
.upper()] = FvSection
.FvName
1201 self
.FvList
.append(FvSection
.FvName
)
1202 self
.FvInfo
[FvSection
.FvName
] = ("Nested FV", 0, 0)
1203 self
._DiscoverNestedFvList
(FvSection
.FvName
, Wa
)
1204 except AttributeError:
1208 # Constructor function for class FdRegionReport
1210 # This constructor function generates FdRegionReport object for a specified FdRegion.
1211 # If the FdRegion is a firmware volume, it will recursively find all its nested Firmware
1212 # volume list. This function also collects GUID map in order to dump module identification
1213 # in the final report.
1215 # @param self: The object pointer
1216 # @param FdRegion The current FdRegion object
1217 # @param Wa Workspace context information
1219 def __init__(self
, FdRegion
, Wa
):
1220 self
.Type
= FdRegion
.RegionType
1221 self
.BaseAddress
= FdRegion
.Offset
1222 self
.Size
= FdRegion
.Size
1226 self
._FvDir
= Wa
.FvDir
1229 # If the input FdRegion is not a firmware volume,
1232 if self
.Type
!= "FV":
1236 # Find all nested FVs in the FdRegion
1238 for FvName
in FdRegion
.RegionDataList
:
1239 if FvName
in self
.FvList
:
1241 self
.FvList
.append(FvName
)
1242 self
.FvInfo
[FvName
] = ("Fd Region", self
.BaseAddress
, self
.Size
)
1243 self
._DiscoverNestedFvList
(FvName
, Wa
)
1247 # Collect PCDs declared in DEC files.
1249 for Pa
in Wa
.AutoGenObjectList
:
1250 for Package
in Pa
.PackageList
:
1251 for (TokenCName
, TokenSpaceGuidCName
, DecType
) in Package
.Pcds
:
1252 DecDefaultValue
= Package
.Pcds
[TokenCName
, TokenSpaceGuidCName
, DecType
].DefaultValue
1253 PlatformPcds
[(TokenCName
, TokenSpaceGuidCName
)] = DecDefaultValue
1255 # Collect PCDs defined in DSC file
1257 for arch
in Wa
.ArchList
:
1258 Platform
= Wa
.BuildDatabase
[Wa
.MetaFile
, arch
]
1259 for (TokenCName
, TokenSpaceGuidCName
) in Platform
.Pcds
:
1260 DscDefaultValue
= Platform
.Pcds
[(TokenCName
, TokenSpaceGuidCName
)].DefaultValue
1261 PlatformPcds
[(TokenCName
, TokenSpaceGuidCName
)] = DscDefaultValue
1264 # Add PEI and DXE a priori files GUIDs defined in PI specification.
1266 self
._GuidsDb
["1B45CC0A-156A-428A-AF62-49864DA0E6E6"] = "PEI Apriori"
1267 self
._GuidsDb
["FC510EE7-FFDC-11D4-BD41-0080C73C8881"] = "DXE Apriori"
1269 # Add ACPI table storage file
1271 self
._GuidsDb
["7E374E25-8E01-4FEE-87F2-390C23C606CD"] = "ACPI table storage"
1273 for Pa
in Wa
.AutoGenObjectList
:
1274 for ModuleKey
in Pa
.Platform
.Modules
:
1275 M
= Pa
.Platform
.Modules
[ModuleKey
].M
1276 InfPath
= mws
.join(Wa
.WorkspaceDir
, M
.MetaFile
.File
)
1277 self
._GuidsDb
[M
.Guid
.upper()] = "%s (%s)" % (M
.Module
.BaseName
, InfPath
)
1280 # Collect the GUID map in the FV firmware volume
1282 for FvName
in self
.FvList
:
1283 FvDictKey
=FvName
.upper()
1284 if FvDictKey
in Wa
.FdfProfile
.FvDict
:
1285 for Ffs
in Wa
.FdfProfile
.FvDict
[FvName
.upper()].FfsList
:
1288 # collect GUID map for binary EFI file in FDF file.
1290 Guid
= Ffs
.NameGuid
.upper()
1291 Match
= gPcdGuidPattern
.match(Ffs
.NameGuid
)
1293 PcdTokenspace
= Match
.group(1)
1294 PcdToken
= Match
.group(2)
1295 if (PcdToken
, PcdTokenspace
) in PlatformPcds
:
1296 GuidValue
= PlatformPcds
[(PcdToken
, PcdTokenspace
)]
1297 Guid
= GuidStructureByteArrayToGuidString(GuidValue
).upper()
1298 for Section
in Ffs
.SectionList
:
1300 ModuleSectFile
= mws
.join(Wa
.WorkspaceDir
, Section
.SectFileName
)
1301 self
._GuidsDb
[Guid
] = ModuleSectFile
1302 except AttributeError:
1304 except AttributeError:
1309 # Internal worker function to generate report for the FD region
1311 # This internal worker function to generate report for the FD region.
1312 # It the type is firmware volume, it lists offset and module identification.
1314 # @param self The object pointer
1315 # @param File The file object for report
1316 # @param Title The title for the FD subsection
1317 # @param BaseAddress The base address for the FD region
1318 # @param Size The size of the FD region
1319 # @param FvName The FV name if the FD region is a firmware volume
1321 def _GenerateReport(self
, File
, Title
, Type
, BaseAddress
, Size
=0, FvName
=None):
1322 FileWrite(File
, gSubSectionStart
)
1323 FileWrite(File
, Title
)
1324 FileWrite(File
, "Type: %s" % Type
)
1325 FileWrite(File
, "Base Address: 0x%X" % BaseAddress
)
1327 if self
.Type
== "FV":
1331 FvReportFileName
= os
.path
.join(self
._FvDir
, FvName
+ ".Fv.txt")
1334 # Collect size info in the firmware volume.
1336 FvReport
= open(FvReportFileName
).read()
1337 Match
= gFvTotalSizePattern
.search(FvReport
)
1339 FvTotalSize
= int(Match
.group(1), 16)
1340 Match
= gFvTakenSizePattern
.search(FvReport
)
1342 FvTakenSize
= int(Match
.group(1), 16)
1343 FvFreeSize
= FvTotalSize
- FvTakenSize
1345 # Write size information to the report file.
1347 FileWrite(File
, "Size: 0x%X (%.0fK)" % (FvTotalSize
, FvTotalSize
/ 1024.0))
1348 FileWrite(File
, "Fv Name: %s (%.1f%% Full)" % (FvName
, FvTakenSize
* 100.0 / FvTotalSize
))
1349 FileWrite(File
, "Occupied Size: 0x%X (%.0fK)" % (FvTakenSize
, FvTakenSize
/ 1024.0))
1350 FileWrite(File
, "Free Size: 0x%X (%.0fK)" % (FvFreeSize
, FvFreeSize
/ 1024.0))
1351 FileWrite(File
, "Offset Module")
1352 FileWrite(File
, gSubSectionSep
)
1354 # Write module offset and module identification to the report file.
1357 for Match
in gOffsetGuidPattern
.finditer(FvReport
):
1358 Guid
= Match
.group(2).upper()
1359 OffsetInfo
[Match
.group(1)] = self
._GuidsDb
.get(Guid
, Guid
)
1360 OffsetList
= OffsetInfo
.keys()
1362 for Offset
in OffsetList
:
1363 FileWrite (File
, "%s %s" % (Offset
, OffsetInfo
[Offset
]))
1365 EdkLogger
.warn(None, "Fail to read report file", FvReportFileName
)
1367 FileWrite(File
, "Size: 0x%X (%.0fK)" % (Size
, Size
/ 1024.0))
1368 FileWrite(File
, gSubSectionEnd
)
1371 # Generate report for the FD region
1373 # This function generates report for the FD region.
1375 # @param self The object pointer
1376 # @param File The file object for report
1378 def GenerateReport(self
, File
):
1379 if (len(self
.FvList
) > 0):
1380 for FvItem
in self
.FvList
:
1381 Info
= self
.FvInfo
[FvItem
]
1382 self
._GenerateReport
(File
, Info
[0], "FV", Info
[1], Info
[2], FvItem
)
1384 self
._GenerateReport
(File
, "FD Region", self
.Type
, self
.BaseAddress
, self
.Size
)
1387 # Reports FD information
1389 # This class reports the FD section in the build report file.
1390 # It collects flash device information for a platform.
1392 class FdReport(object):
1394 # Constructor function for class FdReport
1396 # This constructor function generates FdReport object for a specified
1399 # @param self The object pointer
1400 # @param Fd The current Firmware device object
1401 # @param Wa Workspace context information
1403 def __init__(self
, Fd
, Wa
):
1404 self
.FdName
= Fd
.FdUiName
1405 self
.BaseAddress
= Fd
.BaseAddress
1407 self
.FdRegionList
= [FdRegionReport(FdRegion
, Wa
) for FdRegion
in Fd
.RegionList
]
1408 self
.FvPath
= os
.path
.join(Wa
.BuildDir
, "FV")
1409 self
.VpdFilePath
= os
.path
.join(self
.FvPath
, "%s.map" % Wa
.Platform
.VpdToolGuid
)
1410 self
.VPDBaseAddress
= 0
1412 self
.VPDInfoList
= []
1413 for index
, FdRegion
in enumerate(Fd
.RegionList
):
1414 if str(FdRegion
.RegionType
) is 'FILE' and Wa
.Platform
.VpdToolGuid
in str(FdRegion
.RegionDataList
):
1415 self
.VPDBaseAddress
= self
.FdRegionList
[index
].BaseAddress
1416 self
.VPDSize
= self
.FdRegionList
[index
].Size
1419 if os
.path
.isfile(self
.VpdFilePath
):
1420 fd
= open(self
.VpdFilePath
, "r")
1421 Lines
= fd
.readlines()
1424 if len(Line
) == 0 or Line
.startswith("#"):
1427 PcdName
, SkuId
, Offset
, Size
, Value
= Line
.split("#")[0].split("|")
1428 PcdName
, SkuId
, Offset
, Size
, Value
= PcdName
.strip(), SkuId
.strip(), Offset
.strip(), Size
.strip(), Value
.strip()
1429 Offset
= '0x%08X' % (int(Offset
, 16) + self
.VPDBaseAddress
)
1430 self
.VPDInfoList
.append("%s | %s | %s | %s | %s" % (PcdName
, SkuId
, Offset
, Size
, Value
))
1432 EdkLogger
.error("BuildReport", CODE_ERROR
, "Fail to parse VPD information file %s" % self
.VpdFilePath
)
1436 # Generate report for the firmware device.
1438 # This function generates report for the firmware device.
1440 # @param self The object pointer
1441 # @param File The file object for report
1443 def GenerateReport(self
, File
):
1444 FileWrite(File
, gSectionStart
)
1445 FileWrite(File
, "Firmware Device (FD)")
1446 FileWrite(File
, "FD Name: %s" % self
.FdName
)
1447 FileWrite(File
, "Base Address: %s" % self
.BaseAddress
)
1448 FileWrite(File
, "Size: 0x%X (%.0fK)" % (self
.Size
, self
.Size
/ 1024.0))
1449 if len(self
.FdRegionList
) > 0:
1450 FileWrite(File
, gSectionSep
)
1451 for FdRegionItem
in self
.FdRegionList
:
1452 FdRegionItem
.GenerateReport(File
)
1454 if len(self
.VPDInfoList
) > 0:
1455 FileWrite(File
, gSubSectionStart
)
1456 FileWrite(File
, "FD VPD Region")
1457 FileWrite(File
, "Base Address: 0x%X" % self
.VPDBaseAddress
)
1458 FileWrite(File
, "Size: 0x%X (%.0fK)" % (self
.VPDSize
, self
.VPDSize
/ 1024.0))
1459 FileWrite(File
, gSubSectionSep
)
1460 for item
in self
.VPDInfoList
:
1461 FileWrite(File
, item
)
1462 FileWrite(File
, gSubSectionEnd
)
1463 FileWrite(File
, gSectionEnd
)
1468 # Reports platform information
1470 # This class reports the whole platform information
1472 class PlatformReport(object):
1474 # Constructor function for class PlatformReport
1476 # This constructor function generates PlatformReport object a platform build.
1477 # It generates report for platform summary, flash, global PCDs and detailed
1478 # module information for modules involved in platform build.
1480 # @param self The object pointer
1481 # @param Wa Workspace context information
1482 # @param MaList The list of modules in the platform build
1484 def __init__(self
, Wa
, MaList
, ReportType
):
1485 self
._WorkspaceDir
= Wa
.WorkspaceDir
1486 self
.PlatformName
= Wa
.Name
1487 self
.PlatformDscPath
= Wa
.Platform
1488 self
.Architectures
= " ".join(Wa
.ArchList
)
1489 self
.ToolChain
= Wa
.ToolChain
1490 self
.Target
= Wa
.BuildTarget
1491 self
.OutputPath
= os
.path
.join(Wa
.WorkspaceDir
, Wa
.OutputDir
)
1492 self
.BuildEnvironment
= platform
.platform()
1494 self
.PcdReport
= None
1495 if "PCD" in ReportType
:
1496 self
.PcdReport
= PcdReport(Wa
)
1498 self
.FdReportList
= []
1499 if "FLASH" in ReportType
and Wa
.FdfProfile
and MaList
== None:
1500 for Fd
in Wa
.FdfProfile
.FdDict
:
1501 self
.FdReportList
.append(FdReport(Wa
.FdfProfile
.FdDict
[Fd
], Wa
))
1503 self
.PredictionReport
= None
1504 if "FIXED_ADDRESS" in ReportType
or "EXECUTION_ORDER" in ReportType
:
1505 self
.PredictionReport
= PredictionReport(Wa
)
1507 self
.DepexParser
= None
1508 if "DEPEX" in ReportType
:
1509 self
.DepexParser
= DepexParser(Wa
)
1511 self
.ModuleReportList
= []
1513 self
._IsModuleBuild
= True
1515 self
.ModuleReportList
.append(ModuleReport(Ma
, ReportType
))
1517 self
._IsModuleBuild
= False
1518 for Pa
in Wa
.AutoGenObjectList
:
1519 for ModuleKey
in Pa
.Platform
.Modules
:
1520 self
.ModuleReportList
.append(ModuleReport(Pa
.Platform
.Modules
[ModuleKey
].M
, ReportType
))
1525 # Generate report for the whole platform.
1527 # This function generates report for platform information.
1528 # It comprises of platform summary, global PCD, flash and
1529 # module list sections.
1531 # @param self The object pointer
1532 # @param File The file object for report
1533 # @param BuildDuration The total time to build the modules
1534 # @param ReportType The kind of report items in the final report file
1536 def GenerateReport(self
, File
, BuildDuration
, ReportType
):
1537 FileWrite(File
, "Platform Summary")
1538 FileWrite(File
, "Platform Name: %s" % self
.PlatformName
)
1539 FileWrite(File
, "Platform DSC Path: %s" % self
.PlatformDscPath
)
1540 FileWrite(File
, "Architectures: %s" % self
.Architectures
)
1541 FileWrite(File
, "Tool Chain: %s" % self
.ToolChain
)
1542 FileWrite(File
, "Target: %s" % self
.Target
)
1543 FileWrite(File
, "Output Path: %s" % self
.OutputPath
)
1544 FileWrite(File
, "Build Environment: %s" % self
.BuildEnvironment
)
1545 FileWrite(File
, "Build Duration: %s" % BuildDuration
)
1546 FileWrite(File
, "Report Content: %s" % ", ".join(ReportType
))
1548 if not self
._IsModuleBuild
:
1549 if "PCD" in ReportType
:
1550 self
.PcdReport
.GenerateReport(File
, None)
1552 if "FLASH" in ReportType
:
1553 for FdReportListItem
in self
.FdReportList
:
1554 FdReportListItem
.GenerateReport(File
)
1556 for ModuleReportItem
in self
.ModuleReportList
:
1557 ModuleReportItem
.GenerateReport(File
, self
.PcdReport
, self
.PredictionReport
, self
.DepexParser
, ReportType
)
1559 if not self
._IsModuleBuild
:
1560 if "EXECUTION_ORDER" in ReportType
:
1561 self
.PredictionReport
.GenerateReport(File
, None)
1563 ## BuildReport class
1565 # This base class contain the routines to collect data and then
1566 # applies certain format to the output report
1568 class BuildReport(object):
1570 # Constructor function for class BuildReport
1572 # This constructor function generates BuildReport object a platform build.
1573 # It generates report for platform summary, flash, global PCDs and detailed
1574 # module information for modules involved in platform build.
1576 # @param self The object pointer
1577 # @param ReportFile The file name to save report file
1578 # @param ReportType The kind of report items in the final report file
1580 def __init__(self
, ReportFile
, ReportType
):
1581 self
.ReportFile
= ReportFile
1583 self
.ReportList
= []
1584 self
.ReportType
= []
1586 for ReportTypeItem
in ReportType
:
1587 if ReportTypeItem
not in self
.ReportType
:
1588 self
.ReportType
.append(ReportTypeItem
)
1590 self
.ReportType
= ["PCD", "LIBRARY", "BUILD_FLAGS", "DEPEX", "FLASH", "FIXED_ADDRESS"]
1592 # Adds platform report to the list
1594 # This function adds a platform report to the final report list.
1596 # @param self The object pointer
1597 # @param Wa Workspace context information
1598 # @param MaList The list of modules in the platform build
1600 def AddPlatformReport(self
, Wa
, MaList
=None):
1602 self
.ReportList
.append((Wa
, MaList
))
1605 # Generates the final report.
1607 # This function generates platform build report. It invokes GenerateReport()
1608 # method for every platform report in the list.
1610 # @param self The object pointer
1611 # @param BuildDuration The total time to build the modules
1613 def GenerateReport(self
, BuildDuration
):
1617 for (Wa
, MaList
) in self
.ReportList
:
1618 PlatformReport(Wa
, MaList
, self
.ReportType
).GenerateReport(File
, BuildDuration
, self
.ReportType
)
1619 Content
= FileLinesSplit(File
.getvalue(), gLineMaxLength
)
1620 SaveFileOnChange(self
.ReportFile
, Content
, True)
1621 EdkLogger
.quiet("Build report can be found at %s" % os
.path
.abspath(self
.ReportFile
))
1623 EdkLogger
.error(None, FILE_WRITE_FAILURE
, ExtraData
=self
.ReportFile
)
1625 EdkLogger
.error("BuildReport", CODE_ERROR
, "Unknown fatal error when generating build report", ExtraData
=self
.ReportFile
, RaiseError
=False)
1626 EdkLogger
.quiet("(Python %s on %s\n%s)" % (platform
.python_version(), sys
.platform
, traceback
.format_exc()))
1629 # This acts like the main() function for the script, unless it is 'import'ed into another script.
1630 if __name__
== '__main__':