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 - 2014, 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
45 ## Pattern to extract contents in EDK DXS files
46 gDxsDependencyPattern
= re
.compile(r
"DEPENDENCY_START(.+)DEPENDENCY_END", re
.DOTALL
)
48 ## Pattern to find total FV total size, occupied size in flash report intermediate file
49 gFvTotalSizePattern
= re
.compile(r
"EFI_FV_TOTAL_SIZE = (0x[0-9a-fA-F]+)")
50 gFvTakenSizePattern
= re
.compile(r
"EFI_FV_TAKEN_SIZE = (0x[0-9a-fA-F]+)")
52 ## Pattern to find module size and time stamp in module summary report intermediate file
53 gModuleSizePattern
= re
.compile(r
"MODULE_SIZE = (\d+)")
54 gTimeStampPattern
= re
.compile(r
"TIME_STAMP = (\d+)")
56 ## Pattern to find GUID value in flash description files
57 gPcdGuidPattern
= re
.compile(r
"PCD\((\w+)[.](\w+)\)")
59 ## Pattern to collect offset, GUID value pair in the flash report intermediate file
60 gOffsetGuidPattern
= re
.compile(r
"(0x[0-9A-Fa-f]+) ([-A-Fa-f0-9]+)")
62 ## Pattern to find module base address and entry point in fixed flash map file
63 gModulePattern
= r
"\n[-\w]+\s*\(([^,]+),\s*BaseAddress=%(Address)s,\s*EntryPoint=%(Address)s\)\s*\(GUID=([-0-9A-Fa-f]+)[^)]*\)"
64 gMapFileItemPattern
= re
.compile(gModulePattern
% {"Address" : "(-?0[xX][0-9A-Fa-f]+)"})
66 ## Pattern to find all module referenced header files in source files
67 gIncludePattern
= re
.compile(r
'#include\s*["<]([^">]+)[">]')
68 gIncludePattern2
= re
.compile(r
"#include\s+EFI_([A-Z_]+)\s*[(]\s*(\w+)\s*[)]")
70 ## Pattern to find the entry point for EDK module using EDKII Glue library
71 gGlueLibEntryPoint
= re
.compile(r
"__EDKII_GLUE_MODULE_ENTRY_POINT__\s*=\s*(\w+)")
73 ## Tags for MaxLength of line in report
76 ## Tags for end of line in report
79 ## Tags for section start, end and separator
80 gSectionStart
= ">" + "=" * (gLineMaxLength
-2) + "<"
81 gSectionEnd
= "<" + "=" * (gLineMaxLength
-2) + ">" + "\n"
82 gSectionSep
= "=" * gLineMaxLength
84 ## Tags for subsection start, end and separator
85 gSubSectionStart
= ">" + "-" * (gLineMaxLength
-2) + "<"
86 gSubSectionEnd
= "<" + "-" * (gLineMaxLength
-2) + ">"
87 gSubSectionSep
= "-" * gLineMaxLength
90 ## The look up table to map PCD type to pair of report display type and DEC type
92 'FixedAtBuild' : ('FIXED', 'FixedAtBuild'),
93 'PatchableInModule': ('PATCH', 'PatchableInModule'),
94 'FeatureFlag' : ('FLAG', 'FeatureFlag'),
95 'Dynamic' : ('DYN', 'Dynamic'),
96 'DynamicHii' : ('DYNHII', 'Dynamic'),
97 'DynamicVpd' : ('DYNVPD', 'Dynamic'),
98 'DynamicEx' : ('DEX', 'DynamicEx'),
99 'DynamicExHii' : ('DEXHII', 'DynamicEx'),
100 'DynamicExVpd' : ('DEXVPD', 'DynamicEx'),
103 ## The look up table to map module type to driver type
105 'SEC' : '0x3 (SECURITY_CORE)',
106 'PEI_CORE' : '0x4 (PEI_CORE)',
107 'PEIM' : '0x6 (PEIM)',
108 'DXE_CORE' : '0x5 (DXE_CORE)',
109 'DXE_DRIVER' : '0x7 (DRIVER)',
110 'DXE_SAL_DRIVER' : '0x7 (DRIVER)',
111 'DXE_SMM_DRIVER' : '0x7 (DRIVER)',
112 'DXE_RUNTIME_DRIVER': '0x7 (DRIVER)',
113 'UEFI_DRIVER' : '0x7 (DRIVER)',
114 'UEFI_APPLICATION' : '0x9 (APPLICATION)',
115 'SMM_CORE' : '0xD (SMM_CORE)',
116 'SMM_DRIVER' : '0xA (SMM)', # Extension of module type to support PI 1.1 SMM drivers
119 ## The look up table of the supported opcode in the dependency expression binaries
120 gOpCodeList
= ["BEFORE", "AFTER", "PUSH", "AND", "OR", "NOT", "TRUE", "FALSE", "END", "SOR"]
123 # Writes a string to the file object.
125 # This function writes a string to the file object and a new line is appended
126 # afterwards. It may optionally wraps the string for better readability.
128 # @File The file object to write
129 # @String The string to be written to the file
130 # @Wrapper Indicates whether to wrap the string
132 def FileWrite(File
, String
, Wrapper
=False):
134 String
= textwrap
.fill(String
, 120)
135 File
.write(String
+ gEndOfLine
)
138 # Find all the header file that the module source directly includes.
140 # This function scans source code to find all header files the module may
141 # include. This is not accurate but very effective to find all the header
142 # file the module might include with #include statement.
144 # @Source The source file name
145 # @IncludePathList The list of include path to find the source file.
146 # @IncludeFiles The dictionary of current found include files.
148 def FindIncludeFiles(Source
, IncludePathList
, IncludeFiles
):
149 FileContents
= open(Source
).read()
151 # Find header files with pattern #include "XXX.h" or #include <XXX.h>
153 for Match
in gIncludePattern
.finditer(FileContents
):
154 FileName
= Match
.group(1).strip()
155 for Dir
in [os
.path
.dirname(Source
)] + IncludePathList
:
156 FullFileName
= os
.path
.normpath(os
.path
.join(Dir
, FileName
))
157 if os
.path
.exists(FullFileName
):
158 IncludeFiles
[FullFileName
.lower().replace("\\", "/")] = FullFileName
162 # Find header files with pattern like #include EFI_PPI_CONSUMER(XXX)
164 for Match
in gIncludePattern2
.finditer(FileContents
):
166 Type
= Match
.group(1)
167 if "ARCH_PROTOCOL" in Type
:
168 FileName
= "ArchProtocol/%(Key)s/%(Key)s.h" % {"Key" : Key
}
169 elif "PROTOCOL" in Type
:
170 FileName
= "Protocol/%(Key)s/%(Key)s.h" % {"Key" : Key
}
172 FileName
= "Ppi/%(Key)s/%(Key)s.h" % {"Key" : Key
}
174 FileName
= "Guid/%(Key)s/%(Key)s.h" % {"Key" : Key
}
177 for Dir
in IncludePathList
:
178 FullFileName
= os
.path
.normpath(os
.path
.join(Dir
, FileName
))
179 if os
.path
.exists(FullFileName
):
180 IncludeFiles
[FullFileName
.lower().replace("\\", "/")] = FullFileName
183 ## Split each lines in file
185 # This method is used to split the lines in file to make the length of each line
186 # less than MaxLength.
188 # @param Content The content of file
189 # @param MaxLength The Max Length of the line
191 def FileLinesSplit(Content
=None, MaxLength
=None):
192 ContentList
= Content
.split(TAB_LINE_BREAK
)
195 for Line
in ContentList
:
196 while len(Line
.rstrip()) > MaxLength
:
197 LineSpaceIndex
= Line
.rfind(TAB_SPACE_SPLIT
, 0, MaxLength
)
198 LineSlashIndex
= Line
.rfind(TAB_SLASH
, 0, MaxLength
)
199 LineBackSlashIndex
= Line
.rfind(TAB_BACK_SLASH
, 0, MaxLength
)
200 if max(LineSpaceIndex
, LineSlashIndex
, LineBackSlashIndex
) > 0:
201 LineBreakIndex
= max(LineSpaceIndex
, LineSlashIndex
, LineBackSlashIndex
)
203 LineBreakIndex
= MaxLength
204 NewContentList
.append(Line
[:LineBreakIndex
])
205 Line
= Line
[LineBreakIndex
:]
207 NewContentList
.append(Line
)
208 for NewLine
in NewContentList
:
209 NewContent
+= NewLine
+ TAB_LINE_BREAK
211 NewContent
= NewContent
.replace(TAB_LINE_BREAK
, gEndOfLine
).replace('\r\r\n', gEndOfLine
)
217 # Parse binary dependency expression section
219 # This utility class parses the dependency expression section and translate the readable
220 # GUID name and value.
222 class DepexParser(object):
224 # Constructor function for class DepexParser
226 # This constructor function collect GUID values so that the readable
227 # GUID name can be translated.
229 # @param self The object pointer
230 # @param Wa Workspace context information
232 def __init__(self
, Wa
):
234 for Pa
in Wa
.AutoGenObjectList
:
235 for Package
in Pa
.PackageList
:
236 for Protocol
in Package
.Protocols
:
237 GuidValue
= GuidStructureStringToGuidString(Package
.Protocols
[Protocol
])
238 self
._GuidDb
[GuidValue
.upper()] = Protocol
239 for Ppi
in Package
.Ppis
:
240 GuidValue
= GuidStructureStringToGuidString(Package
.Ppis
[Ppi
])
241 self
._GuidDb
[GuidValue
.upper()] = Ppi
242 for Guid
in Package
.Guids
:
243 GuidValue
= GuidStructureStringToGuidString(Package
.Guids
[Guid
])
244 self
._GuidDb
[GuidValue
.upper()] = Guid
247 # Parse the binary dependency expression files.
249 # This function parses the binary dependency expression file and translate it
250 # to the instruction list.
252 # @param self The object pointer
253 # @param DepexFileName The file name of binary dependency expression file.
255 def ParseDepexFile(self
, DepexFileName
):
256 DepexFile
= open(DepexFileName
, "rb")
258 OpCode
= DepexFile
.read(1)
260 Statement
= gOpCodeList
[struct
.unpack("B", OpCode
)[0]]
261 if Statement
in ["BEFORE", "AFTER", "PUSH"]:
262 GuidValue
= "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X" % \
263 struct
.unpack("=LHHBBBBBBBB", DepexFile
.read(16))
264 GuidString
= self
._GuidDb
.get(GuidValue
, GuidValue
)
265 Statement
= "%s %s" % (Statement
, GuidString
)
266 DepexStatement
.append(Statement
)
267 OpCode
= DepexFile
.read(1)
269 return DepexStatement
272 # Reports library information
274 # This class reports the module library subsection in the build report file.
276 class LibraryReport(object):
278 # Constructor function for class LibraryReport
280 # This constructor function generates LibraryReport object for
283 # @param self The object pointer
284 # @param M Module context information
286 def __init__(self
, M
):
287 self
.LibraryList
= []
288 if int(str(M
.AutoGenVersion
), 0) >= 0x00010005:
289 self
._EdkIIModule
= True
291 self
._EdkIIModule
= False
293 for Lib
in M
.DependentLibraryList
:
294 LibInfPath
= str(Lib
)
295 LibClassList
= Lib
.LibraryClass
[0].LibraryClass
296 LibConstructorList
= Lib
.ConstructorList
297 LibDesstructorList
= Lib
.DestructorList
298 LibDepexList
= Lib
.DepexExpression
[M
.Arch
, M
.ModuleType
]
299 self
.LibraryList
.append((LibInfPath
, LibClassList
, LibConstructorList
, LibDesstructorList
, LibDepexList
))
302 # Generate report for module library information
304 # This function generates report for the module library.
305 # If the module is EDKII style one, the additional library class, library
306 # constructor/destructor and dependency expression may also be reported.
308 # @param self The object pointer
309 # @param File The file object for report
311 def GenerateReport(self
, File
):
312 FileWrite(File
, gSubSectionStart
)
313 FileWrite(File
, TAB_BRG_LIBRARY
)
314 if len(self
.LibraryList
) > 0:
315 FileWrite(File
, gSubSectionSep
)
316 for LibraryItem
in self
.LibraryList
:
317 LibInfPath
= LibraryItem
[0]
318 FileWrite(File
, LibInfPath
)
321 # Report library class, library constructor and destructor for
322 # EDKII style module.
324 if self
._EdkIIModule
:
325 LibClass
= LibraryItem
[1]
327 LibConstructor
= " ".join(LibraryItem
[2])
329 EdkIILibInfo
+= " C = " + LibConstructor
330 LibDestructor
= " ".join(LibraryItem
[3])
332 EdkIILibInfo
+= " D = " + LibDestructor
333 LibDepex
= " ".join(LibraryItem
[4])
335 EdkIILibInfo
+= " Depex = " + LibDepex
337 FileWrite(File
, "{%s: %s}" % (LibClass
, EdkIILibInfo
))
339 FileWrite(File
, "{%s}" % LibClass
)
341 FileWrite(File
, gSubSectionEnd
)
344 # Reports dependency expression information
346 # This class reports the module dependency expression subsection in the build report file.
348 class DepexReport(object):
350 # Constructor function for class DepexReport
352 # This constructor function generates DepexReport object for
353 # a module. If the module source contains the DXS file (usually EDK
354 # style module), it uses the dependency in DXS file; otherwise,
355 # it uses the dependency expression from its own INF [Depex] section
356 # and then merges with the ones from its dependent library INF.
358 # @param self The object pointer
359 # @param M Module context information
361 def __init__(self
, M
):
363 self
._DepexFileName
= os
.path
.join(M
.BuildDir
, "OUTPUT", M
.Module
.BaseName
+ ".depex")
364 ModuleType
= M
.ModuleType
366 ModuleType
= gComponentType2ModuleType
.get(M
.ComponentType
, "")
368 if ModuleType
in ["SEC", "PEI_CORE", "DXE_CORE", "SMM_CORE", "UEFI_APPLICATION"]:
371 for Source
in M
.SourceFileList
:
372 if os
.path
.splitext(Source
.Path
)[1].lower() == ".dxs":
373 Match
= gDxsDependencyPattern
.search(open(Source
.Path
).read())
375 self
.Depex
= Match
.group(1).strip()
379 self
.Depex
= M
.DepexExpressionList
.get(M
.ModuleType
, "")
380 self
.ModuleDepex
= " ".join(M
.Module
.DepexExpression
[M
.Arch
, M
.ModuleType
])
381 if not self
.ModuleDepex
:
382 self
.ModuleDepex
= "(None)"
385 for Lib
in M
.DependentLibraryList
:
386 LibDepex
= " ".join(Lib
.DepexExpression
[M
.Arch
, M
.ModuleType
]).strip()
388 LibDepexList
.append("(" + LibDepex
+ ")")
389 self
.LibraryDepex
= " AND ".join(LibDepexList
)
390 if not self
.LibraryDepex
:
391 self
.LibraryDepex
= "(None)"
395 # Generate report for module dependency expression information
397 # This function generates report for the module dependency expression.
399 # @param self The object pointer
400 # @param File The file object for report
401 # @param GlobalDepexParser The platform global Dependency expression parser object
403 def GenerateReport(self
, File
, GlobalDepexParser
):
405 FileWrite(File
, gSubSectionStart
)
406 FileWrite(File
, TAB_DEPEX
)
407 FileWrite(File
, gSubSectionEnd
)
409 FileWrite(File
, gSubSectionStart
)
410 if os
.path
.isfile(self
._DepexFileName
):
412 DepexStatements
= GlobalDepexParser
.ParseDepexFile(self
._DepexFileName
)
413 FileWrite(File
, "Final Dependency Expression (DEPEX) Instructions")
414 for DepexStatement
in DepexStatements
:
415 FileWrite(File
, " %s" % DepexStatement
)
416 FileWrite(File
, gSubSectionSep
)
418 EdkLogger
.warn(None, "Dependency expression file is corrupted", self
._DepexFileName
)
420 FileWrite(File
, "Dependency Expression (DEPEX) from %s" % self
.Source
)
422 if self
.Source
== "INF":
423 FileWrite(File
, "%s" % self
.Depex
, True)
424 FileWrite(File
, gSubSectionSep
)
425 FileWrite(File
, "From Module INF: %s" % self
.ModuleDepex
, True)
426 FileWrite(File
, "From Library INF: %s" % self
.LibraryDepex
, True)
428 FileWrite(File
, "%s" % self
.Depex
)
429 FileWrite(File
, gSubSectionEnd
)
432 # Reports dependency expression information
434 # This class reports the module build flags subsection in the build report file.
436 class BuildFlagsReport(object):
438 # Constructor function for class BuildFlagsReport
440 # This constructor function generates BuildFlagsReport object for
441 # a module. It reports the build tool chain tag and all relevant
442 # build flags to build the module.
444 # @param self The object pointer
445 # @param M Module context information
447 def __init__(self
, M
):
450 # Add build flags according to source file extension so that
451 # irrelevant ones can be filtered out.
453 for Source
in M
.SourceFileList
:
454 Ext
= os
.path
.splitext(Source
.File
)[1].lower()
455 if Ext
in [".c", ".cc", ".cpp"]:
456 BuildOptions
["CC"] = 1
457 elif Ext
in [".s", ".asm"]:
458 BuildOptions
["PP"] = 1
459 BuildOptions
["ASM"] = 1
460 elif Ext
in [".vfr"]:
461 BuildOptions
["VFRPP"] = 1
462 BuildOptions
["VFR"] = 1
463 elif Ext
in [".dxs"]:
464 BuildOptions
["APP"] = 1
465 BuildOptions
["CC"] = 1
466 elif Ext
in [".asl"]:
467 BuildOptions
["ASLPP"] = 1
468 BuildOptions
["ASL"] = 1
469 elif Ext
in [".aslc"]:
470 BuildOptions
["ASLCC"] = 1
471 BuildOptions
["ASLDLINK"] = 1
472 BuildOptions
["CC"] = 1
473 elif Ext
in [".asm16"]:
474 BuildOptions
["ASMLINK"] = 1
475 BuildOptions
["SLINK"] = 1
476 BuildOptions
["DLINK"] = 1
479 # Save module build flags.
481 self
.ToolChainTag
= M
.ToolChain
483 for Tool
in BuildOptions
:
484 self
.BuildFlags
[Tool
+ "_FLAGS"] = M
.BuildOption
.get(Tool
, {}).get("FLAGS", "")
487 # Generate report for module build flags information
489 # This function generates report for the module build flags expression.
491 # @param self The object pointer
492 # @param File The file object for report
494 def GenerateReport(self
, File
):
495 FileWrite(File
, gSubSectionStart
)
496 FileWrite(File
, "Build Flags")
497 FileWrite(File
, "Tool Chain Tag: %s" % self
.ToolChainTag
)
498 for Tool
in self
.BuildFlags
:
499 FileWrite(File
, gSubSectionSep
)
500 FileWrite(File
, "%s = %s" % (Tool
, self
.BuildFlags
[Tool
]), True)
502 FileWrite(File
, gSubSectionEnd
)
506 # Reports individual module information
508 # This class reports the module section in the build report file.
509 # It comprises of module summary, module PCD, library, dependency expression,
510 # build flags sections.
512 class ModuleReport(object):
514 # Constructor function for class ModuleReport
516 # This constructor function generates ModuleReport object for
517 # a separate module in a platform build.
519 # @param self The object pointer
520 # @param M Module context information
521 # @param ReportType The kind of report items in the final report file
523 def __init__(self
, M
, ReportType
):
524 self
.ModuleName
= M
.Module
.BaseName
525 self
.ModuleInfPath
= M
.MetaFile
.File
526 self
.FileGuid
= M
.Guid
528 self
.BuildTimeStamp
= None
531 ModuleType
= M
.ModuleType
533 ModuleType
= gComponentType2ModuleType
.get(M
.ComponentType
, "")
535 # If a module complies to PI 1.1, promote Module type to "SMM_DRIVER"
537 if ModuleType
== "DXE_SMM_DRIVER":
538 PiSpec
= M
.Module
.Specification
.get("PI_SPECIFICATION_VERSION", "0x00010000")
539 if int(PiSpec
, 0) >= 0x0001000A:
540 ModuleType
= "SMM_DRIVER"
541 self
.DriverType
= gDriverTypeMap
.get(ModuleType
, "0x2 (FREE_FORM)")
542 self
.UefiSpecVersion
= M
.Module
.Specification
.get("UEFI_SPECIFICATION_VERSION", "")
543 self
.PiSpecVersion
= M
.Module
.Specification
.get("PI_SPECIFICATION_VERSION", "")
544 self
.PciDeviceId
= M
.Module
.Defines
.get("PCI_DEVICE_ID", "")
545 self
.PciVendorId
= M
.Module
.Defines
.get("PCI_VENDOR_ID", "")
546 self
.PciClassCode
= M
.Module
.Defines
.get("PCI_CLASS_CODE", "")
548 self
._BuildDir
= M
.BuildDir
549 self
.ModulePcdSet
= {}
550 if "PCD" in ReportType
:
552 # Collect all module used PCD set: module INF referenced directly or indirectly.
553 # It also saves module INF default values of them in case they exist.
555 for Pcd
in M
.ModulePcdList
+ M
.LibraryPcdList
:
556 self
.ModulePcdSet
.setdefault((Pcd
.TokenCName
, Pcd
.TokenSpaceGuidCName
, Pcd
.Type
), (Pcd
.InfDefaultValue
, Pcd
.DefaultValue
))
558 self
.LibraryReport
= None
559 if "LIBRARY" in ReportType
:
560 self
.LibraryReport
= LibraryReport(M
)
562 self
.DepexReport
= None
563 if "DEPEX" in ReportType
:
564 self
.DepexReport
= DepexReport(M
)
566 if "BUILD_FLAGS" in ReportType
:
567 self
.BuildFlagsReport
= BuildFlagsReport(M
)
571 # Generate report for module information
573 # This function generates report for separate module expression
574 # in a platform build.
576 # @param self The object pointer
577 # @param File The file object for report
578 # @param GlobalPcdReport The platform global PCD report object
579 # @param GlobalPredictionReport The platform global Prediction report object
580 # @param GlobalDepexParser The platform global Dependency expression parser object
581 # @param ReportType The kind of report items in the final report file
583 def GenerateReport(self
, File
, GlobalPcdReport
, GlobalPredictionReport
, GlobalDepexParser
, ReportType
):
584 FileWrite(File
, gSectionStart
)
586 FwReportFileName
= os
.path
.join(self
._BuildDir
, "DEBUG", self
.ModuleName
+ ".txt")
587 if os
.path
.isfile(FwReportFileName
):
589 FileContents
= open(FwReportFileName
).read()
590 Match
= gModuleSizePattern
.search(FileContents
)
592 self
.Size
= int(Match
.group(1))
594 Match
= gTimeStampPattern
.search(FileContents
)
596 self
.BuildTimeStamp
= datetime
.fromtimestamp(int(Match
.group(1)))
598 EdkLogger
.warn(None, "Fail to read report file", FwReportFileName
)
600 FileWrite(File
, "Module Summary")
601 FileWrite(File
, "Module Name: %s" % self
.ModuleName
)
602 FileWrite(File
, "Module INF Path: %s" % self
.ModuleInfPath
)
603 FileWrite(File
, "File GUID: %s" % self
.FileGuid
)
605 FileWrite(File
, "Size: 0x%X (%.2fK)" % (self
.Size
, self
.Size
/ 1024.0))
606 if self
.BuildTimeStamp
:
607 FileWrite(File
, "Build Time Stamp: %s" % self
.BuildTimeStamp
)
609 FileWrite(File
, "Driver Type: %s" % self
.DriverType
)
610 if self
.UefiSpecVersion
:
611 FileWrite(File
, "UEFI Spec Version: %s" % self
.UefiSpecVersion
)
612 if self
.PiSpecVersion
:
613 FileWrite(File
, "PI Spec Version: %s" % self
.PiSpecVersion
)
615 FileWrite(File
, "PCI Device ID: %s" % self
.PciDeviceId
)
617 FileWrite(File
, "PCI Vendor ID: %s" % self
.PciVendorId
)
618 if self
.PciClassCode
:
619 FileWrite(File
, "PCI Class Code: %s" % self
.PciClassCode
)
621 FileWrite(File
, gSectionSep
)
623 if "PCD" in ReportType
:
624 GlobalPcdReport
.GenerateReport(File
, self
.ModulePcdSet
)
626 if "LIBRARY" in ReportType
:
627 self
.LibraryReport
.GenerateReport(File
)
629 if "DEPEX" in ReportType
:
630 self
.DepexReport
.GenerateReport(File
, GlobalDepexParser
)
632 if "BUILD_FLAGS" in ReportType
:
633 self
.BuildFlagsReport
.GenerateReport(File
)
635 if "FIXED_ADDRESS" in ReportType
and self
.FileGuid
:
636 GlobalPredictionReport
.GenerateReport(File
, self
.FileGuid
)
638 FileWrite(File
, gSectionEnd
)
641 # Reports platform and module PCD information
643 # This class reports the platform PCD section and module PCD subsection
644 # in the build report file.
646 class PcdReport(object):
648 # Constructor function for class PcdReport
650 # This constructor function generates PcdReport object a platform build.
651 # It collects the whole PCD database from platform DSC files, platform
652 # flash description file and package DEC files.
654 # @param self The object pointer
655 # @param Wa Workspace context information
657 def __init__(self
, Wa
):
661 self
.FdfPcdSet
= Wa
.FdfProfile
.PcdDict
665 self
.ModulePcdOverride
= {}
666 for Pa
in Wa
.AutoGenObjectList
:
668 # Collect all platform referenced PCDs and grouped them by PCD token space
671 for Pcd
in Pa
.AllPcdList
:
672 PcdList
= self
.AllPcds
.setdefault(Pcd
.TokenSpaceGuidCName
, {}).setdefault(Pcd
.Type
, [])
673 if Pcd
not in PcdList
:
675 if len(Pcd
.TokenCName
) > self
.MaxLen
:
676 self
.MaxLen
= len(Pcd
.TokenCName
)
678 for Module
in Pa
.Platform
.Modules
.values():
680 # Collect module override PCDs
682 for ModulePcd
in Module
.M
.ModulePcdList
+ Module
.M
.LibraryPcdList
:
683 TokenCName
= ModulePcd
.TokenCName
684 TokenSpaceGuid
= ModulePcd
.TokenSpaceGuidCName
685 ModuleDefault
= ModulePcd
.DefaultValue
686 ModulePath
= os
.path
.basename(Module
.M
.MetaFile
.File
)
687 self
.ModulePcdOverride
.setdefault((TokenCName
, TokenSpaceGuid
), {})[ModulePath
] = ModuleDefault
691 # Collect PCD DEC default value.
693 self
.DecPcdDefault
= {}
694 for Pa
in Wa
.AutoGenObjectList
:
695 for Package
in Pa
.PackageList
:
696 for (TokenCName
, TokenSpaceGuidCName
, DecType
) in Package
.Pcds
:
697 DecDefaultValue
= Package
.Pcds
[TokenCName
, TokenSpaceGuidCName
, DecType
].DefaultValue
698 self
.DecPcdDefault
.setdefault((TokenCName
, TokenSpaceGuidCName
, DecType
), DecDefaultValue
)
700 # Collect PCDs defined in DSC common section
702 self
.DscPcdDefault
= {}
703 for Arch
in Wa
.ArchList
:
704 Platform
= Wa
.BuildDatabase
[Wa
.MetaFile
, Arch
, Wa
.BuildTarget
, Wa
.ToolChain
]
705 for (TokenCName
, TokenSpaceGuidCName
) in Platform
.Pcds
:
706 DscDefaultValue
= Platform
.Pcds
[(TokenCName
, TokenSpaceGuidCName
)].DefaultValue
708 self
.DscPcdDefault
[(TokenCName
, TokenSpaceGuidCName
)] = DscDefaultValue
711 # Generate report for PCD information
713 # This function generates report for separate module expression
714 # in a platform build.
716 # @param self The object pointer
717 # @param File The file object for report
718 # @param ModulePcdSet Set of all PCDs referenced by module or None for
719 # platform PCD report
720 # @param DscOverridePcds Module DSC override PCDs set
722 def GenerateReport(self
, File
, ModulePcdSet
):
723 if ModulePcdSet
== None:
725 # For platform global PCD section
727 FileWrite(File
, gSectionStart
)
728 FileWrite(File
, "Platform Configuration Database Report")
729 FileWrite(File
, " *P - Platform scoped PCD override in DSC file")
730 FileWrite(File
, " *F - Platform scoped PCD override in FDF file")
731 FileWrite(File
, " *M - Module scoped PCD override")
732 FileWrite(File
, gSectionSep
)
735 # For module PCD sub-section
737 FileWrite(File
, gSubSectionStart
)
738 FileWrite(File
, TAB_BRG_PCD
)
739 FileWrite(File
, gSubSectionSep
)
741 for Key
in self
.AllPcds
:
743 # Group PCD by their token space GUID C Name
746 for Type
in self
.AllPcds
[Key
]:
748 # Group PCD by their usage type
750 TypeName
, DecType
= gPcdTypeMap
.get(Type
, ("", Type
))
751 for Pcd
in self
.AllPcds
[Key
][Type
]:
753 # Get PCD default value and their override relationship
755 DecDefaultValue
= self
.DecPcdDefault
.get((Pcd
.TokenCName
, Pcd
.TokenSpaceGuidCName
, DecType
))
756 DscDefaultValue
= self
.DscPcdDefault
.get((Pcd
.TokenCName
, Pcd
.TokenSpaceGuidCName
))
757 DscDefaultValue
= self
.FdfPcdSet
.get((Pcd
.TokenCName
, Key
), DscDefaultValue
)
758 InfDefaultValue
= None
760 PcdValue
= DecDefaultValue
762 PcdValue
= DscDefaultValue
763 if ModulePcdSet
!= None:
764 if (Pcd
.TokenCName
, Pcd
.TokenSpaceGuidCName
, Type
) not in ModulePcdSet
:
766 InfDefault
, PcdValue
= ModulePcdSet
[Pcd
.TokenCName
, Pcd
.TokenSpaceGuidCName
, Type
]
770 if ModulePcdSet
== None:
776 if Pcd
.DatumType
in ('UINT8', 'UINT16', 'UINT32', 'UINT64'):
777 PcdValueNumber
= int(PcdValue
.strip(), 0)
778 if DecDefaultValue
== None:
781 DecDefaultValueNumber
= int(DecDefaultValue
.strip(), 0)
782 DecMatch
= (DecDefaultValueNumber
== PcdValueNumber
)
784 if InfDefaultValue
== None:
787 InfDefaultValueNumber
= int(InfDefaultValue
.strip(), 0)
788 InfMatch
= (InfDefaultValueNumber
== PcdValueNumber
)
790 if DscDefaultValue
== None:
793 DscDefaultValueNumber
= int(DscDefaultValue
.strip(), 0)
794 DscMatch
= (DscDefaultValueNumber
== PcdValueNumber
)
796 if DecDefaultValue
== None:
799 DecMatch
= (DecDefaultValue
.strip() == PcdValue
.strip())
801 if InfDefaultValue
== None:
804 InfMatch
= (InfDefaultValue
.strip() == PcdValue
.strip())
806 if DscDefaultValue
== None:
809 DscMatch
= (DscDefaultValue
.strip() == PcdValue
.strip())
812 # Report PCD item according to their override relationship
814 if DecMatch
and InfMatch
:
815 FileWrite(File
, ' %-*s: %6s %10s = %-22s' % (self
.MaxLen
, Pcd
.TokenCName
, TypeName
, '('+Pcd
.DatumType
+')', PcdValue
.strip()))
818 if (Pcd
.TokenCName
, Key
) in self
.FdfPcdSet
:
819 FileWrite(File
, ' *F %-*s: %6s %10s = %-22s' % (self
.MaxLen
, Pcd
.TokenCName
, TypeName
, '('+Pcd
.DatumType
+')', PcdValue
.strip()))
821 FileWrite(File
, ' *P %-*s: %6s %10s = %-22s' % (self
.MaxLen
, Pcd
.TokenCName
, TypeName
, '('+Pcd
.DatumType
+')', PcdValue
.strip()))
823 FileWrite(File
, ' *M %-*s: %6s %10s = %-22s' % (self
.MaxLen
, Pcd
.TokenCName
, TypeName
, '('+Pcd
.DatumType
+')', PcdValue
.strip()))
825 if TypeName
in ('DYNHII', 'DEXHII', 'DYNVPD', 'DEXVPD'):
826 for SkuInfo
in Pcd
.SkuInfoList
.values():
827 if TypeName
in ('DYNHII', 'DEXHII'):
828 FileWrite(File
, '%*s: %s: %s' % (self
.MaxLen
+ 4, SkuInfo
.VariableGuid
, SkuInfo
.VariableName
, SkuInfo
.VariableOffset
))
830 FileWrite(File
, '%*s' % (self
.MaxLen
+ 4, SkuInfo
.VpdOffset
))
832 if not DscMatch
and DscDefaultValue
!= None:
833 FileWrite(File
, ' %*s = %s' % (self
.MaxLen
+ 19, 'DSC DEFAULT', DscDefaultValue
.strip()))
835 if not InfMatch
and InfDefaultValue
!= None:
836 FileWrite(File
, ' %*s = %s' % (self
.MaxLen
+ 19, 'INF DEFAULT', InfDefaultValue
.strip()))
838 if not DecMatch
and DecDefaultValue
!= None:
839 FileWrite(File
, ' %*s = %s' % (self
.MaxLen
+ 19, 'DEC DEFAULT', DecDefaultValue
.strip()))
841 if ModulePcdSet
== None:
842 ModuleOverride
= self
.ModulePcdOverride
.get((Pcd
.TokenCName
, Pcd
.TokenSpaceGuidCName
), {})
843 for ModulePath
in ModuleOverride
:
844 ModuleDefault
= ModuleOverride
[ModulePath
]
845 if Pcd
.DatumType
in ('UINT8', 'UINT16', 'UINT32', 'UINT64'):
846 ModulePcdDefaultValueNumber
= int(ModuleDefault
.strip(), 0)
847 Match
= (ModulePcdDefaultValueNumber
== PcdValueNumber
)
849 Match
= (ModuleDefault
.strip() == PcdValue
.strip())
852 FileWrite(File
, ' *M %-*s = %s' % (self
.MaxLen
+ 19, ModulePath
, ModuleDefault
.strip()))
854 if ModulePcdSet
== None:
855 FileWrite(File
, gSectionEnd
)
857 FileWrite(File
, gSubSectionEnd
)
862 # Reports platform and module Prediction information
864 # This class reports the platform execution order prediction section and
865 # module load fixed address prediction subsection in the build report file.
867 class PredictionReport(object):
869 # Constructor function for class PredictionReport
871 # This constructor function generates PredictionReport object for the platform.
873 # @param self: The object pointer
874 # @param Wa Workspace context information
876 def __init__(self
, Wa
):
877 self
._MapFileName
= os
.path
.join(Wa
.BuildDir
, Wa
.Name
+ ".map")
878 self
._MapFileParsed
= False
879 self
._EotToolInvoked
= False
880 self
._FvDir
= Wa
.FvDir
881 self
._EotDir
= Wa
.BuildDir
882 self
._FfsEntryPoint
= {}
884 self
._SourceList
= []
885 self
.FixedMapDict
= {}
890 # Collect all platform reference source files and GUID C Name
892 for Pa
in Wa
.AutoGenObjectList
:
893 for Module
in Pa
.LibraryAutoGenList
+ Pa
.ModuleAutoGenList
:
895 # BASE typed modules are EFI agnostic, so we need not scan
896 # their source code to find PPI/Protocol produce or consume
899 if Module
.ModuleType
== "BASE":
902 # Add module referenced source files
904 self
._SourceList
.append(str(Module
))
906 for Source
in Module
.SourceFileList
:
907 if os
.path
.splitext(str(Source
))[1].lower() == ".c":
908 self
._SourceList
.append(" " + str(Source
))
909 FindIncludeFiles(Source
.Path
, Module
.IncludePathList
, IncludeList
)
910 for IncludeFile
in IncludeList
.values():
911 self
._SourceList
.append(" " + IncludeFile
)
913 for Guid
in Module
.PpiList
:
914 self
._GuidMap
[Guid
] = GuidStructureStringToGuidString(Module
.PpiList
[Guid
])
915 for Guid
in Module
.ProtocolList
:
916 self
._GuidMap
[Guid
] = GuidStructureStringToGuidString(Module
.ProtocolList
[Guid
])
917 for Guid
in Module
.GuidList
:
918 self
._GuidMap
[Guid
] = GuidStructureStringToGuidString(Module
.GuidList
[Guid
])
920 if Module
.Guid
and not Module
.IsLibrary
:
921 EntryPoint
= " ".join(Module
.Module
.ModuleEntryPointList
)
922 if int(str(Module
.AutoGenVersion
), 0) >= 0x00010005:
923 RealEntryPoint
= "_ModuleEntryPoint"
925 RealEntryPoint
= EntryPoint
926 if EntryPoint
== "_ModuleEntryPoint":
927 CCFlags
= Module
.BuildOption
.get("CC", {}).get("FLAGS", "")
928 Match
= gGlueLibEntryPoint
.search(CCFlags
)
930 EntryPoint
= Match
.group(1)
932 self
._FfsEntryPoint
[Module
.Guid
.upper()] = (EntryPoint
, RealEntryPoint
)
936 # Collect platform firmware volume list as the input of EOT.
940 for Fd
in Wa
.FdfProfile
.FdDict
:
941 for FdRegion
in Wa
.FdfProfile
.FdDict
[Fd
].RegionList
:
942 if FdRegion
.RegionType
!= "FV":
944 for FvName
in FdRegion
.RegionDataList
:
945 if FvName
in self
._FvList
:
947 self
._FvList
.append(FvName
)
948 for Ffs
in Wa
.FdfProfile
.FvDict
[FvName
.upper()].FfsList
:
949 for Section
in Ffs
.SectionList
:
951 for FvSection
in Section
.SectionList
:
952 if FvSection
.FvName
in self
._FvList
:
954 self
._FvList
.append(FvSection
.FvName
)
955 except AttributeError:
960 # Parse platform fixed address map files
962 # This function parses the platform final fixed address map file to get
963 # the database of predicted fixed address for module image base, entry point
966 # @param self: The object pointer
968 def _ParseMapFile(self
):
969 if self
._MapFileParsed
:
971 self
._MapFileParsed
= True
972 if os
.path
.isfile(self
._MapFileName
):
974 FileContents
= open(self
._MapFileName
).read()
975 for Match
in gMapFileItemPattern
.finditer(FileContents
):
976 AddressType
= Match
.group(1)
977 BaseAddress
= Match
.group(2)
978 EntryPoint
= Match
.group(3)
979 Guid
= Match
.group(4).upper()
980 List
= self
.FixedMapDict
.setdefault(Guid
, [])
981 List
.append((AddressType
, BaseAddress
, "*I"))
982 List
.append((AddressType
, EntryPoint
, "*E"))
984 EdkLogger
.warn(None, "Cannot open file to read", self
._MapFileName
)
987 # Invokes EOT tool to get the predicted the execution order.
989 # This function invokes EOT tool to calculate the predicted dispatch order
991 # @param self: The object pointer
993 def _InvokeEotTool(self
):
994 if self
._EotToolInvoked
:
997 self
._EotToolInvoked
= True
999 for FvName
in self
._FvList
:
1000 FvFile
= os
.path
.join(self
._FvDir
, FvName
+ ".Fv")
1001 if os
.path
.isfile(FvFile
):
1002 FvFileList
.append(FvFile
)
1004 if len(FvFileList
) == 0:
1007 # Write source file list and GUID file list to an intermediate file
1008 # as the input for EOT tool and dispatch List as the output file
1011 SourceList
= os
.path
.join(self
._EotDir
, "SourceFile.txt")
1012 GuidList
= os
.path
.join(self
._EotDir
, "GuidList.txt")
1013 DispatchList
= os
.path
.join(self
._EotDir
, "Dispatch.txt")
1015 TempFile
= open(SourceList
, "w+")
1016 for Item
in self
._SourceList
:
1017 FileWrite(TempFile
, Item
)
1019 TempFile
= open(GuidList
, "w+")
1020 for Key
in self
._GuidMap
:
1021 FileWrite(TempFile
, "%s %s" % (Key
, self
._GuidMap
[Key
]))
1025 from Eot
.Eot
import Eot
1028 # Invoke EOT tool and echo its runtime performance
1030 EotStartTime
= time
.time()
1031 Eot(CommandLineOption
=False, SourceFileList
=SourceList
, GuidList
=GuidList
,
1032 FvFileList
=' '.join(FvFileList
), Dispatch
=DispatchList
, IsInit
=True)
1033 EotEndTime
= time
.time()
1034 EotDuration
= time
.strftime("%H:%M:%S", time
.gmtime(int(round(EotEndTime
- EotStartTime
))))
1035 EdkLogger
.quiet("EOT run time: %s\n" % EotDuration
)
1038 # Parse the output of EOT tool
1040 for Line
in open(DispatchList
):
1041 if len(Line
.split()) < 4:
1043 (Guid
, Phase
, FfsName
, FilePath
) = Line
.split()
1044 Symbol
= self
._FfsEntryPoint
.get(Guid
, [FfsName
, ""])[0]
1045 if len(Symbol
) > self
.MaxLen
:
1046 self
.MaxLen
= len(Symbol
)
1047 self
.ItemList
.append((Phase
, Symbol
, FilePath
))
1049 EdkLogger
.quiet("(Python %s on %s\n%s)" % (platform
.python_version(), sys
.platform
, traceback
.format_exc()))
1050 EdkLogger
.warn(None, "Failed to generate execution order prediction report, for some error occurred in executing EOT.")
1054 # Generate platform execution order report
1056 # This function generates the predicted module execution order.
1058 # @param self The object pointer
1059 # @param File The file object for report
1061 def _GenerateExecutionOrderReport(self
, File
):
1062 self
._InvokeEotTool
()
1063 if len(self
.ItemList
) == 0:
1065 FileWrite(File
, gSectionStart
)
1066 FileWrite(File
, "Execution Order Prediction")
1067 FileWrite(File
, "*P PEI phase")
1068 FileWrite(File
, "*D DXE phase")
1069 FileWrite(File
, "*E Module INF entry point name")
1070 FileWrite(File
, "*N Module notification function name")
1072 FileWrite(File
, "Type %-*s %s" % (self
.MaxLen
, "Symbol", "Module INF Path"))
1073 FileWrite(File
, gSectionSep
)
1074 for Item
in self
.ItemList
:
1075 FileWrite(File
, "*%sE %-*s %s" % (Item
[0], self
.MaxLen
, Item
[1], Item
[2]))
1077 FileWrite(File
, gSectionStart
)
1080 # Generate Fixed Address report.
1082 # This function generate the predicted fixed address report for a module
1083 # specified by Guid.
1085 # @param self The object pointer
1086 # @param File The file object for report
1087 # @param Guid The module Guid value.
1088 # @param NotifyList The list of all notify function in a module
1090 def _GenerateFixedAddressReport(self
, File
, Guid
, NotifyList
):
1091 self
._ParseMapFile
()
1092 FixedAddressList
= self
.FixedMapDict
.get(Guid
)
1093 if not FixedAddressList
:
1096 FileWrite(File
, gSubSectionStart
)
1097 FileWrite(File
, "Fixed Address Prediction")
1098 FileWrite(File
, "*I Image Loading Address")
1099 FileWrite(File
, "*E Entry Point Address")
1100 FileWrite(File
, "*N Notification Function Address")
1101 FileWrite(File
, "*F Flash Address")
1102 FileWrite(File
, "*M Memory Address")
1103 FileWrite(File
, "*S SMM RAM Offset")
1104 FileWrite(File
, "TOM Top of Memory")
1106 FileWrite(File
, "Type Address Name")
1107 FileWrite(File
, gSubSectionSep
)
1108 for Item
in FixedAddressList
:
1113 Name
= "(Image Base)"
1114 elif Symbol
== "*E":
1115 Name
= self
._FfsEntryPoint
.get(Guid
, ["", "_ModuleEntryPoint"])[1]
1116 elif Symbol
in NotifyList
:
1124 elif "Memory" in Type
:
1130 Value
= "TOM" + Value
1132 FileWrite(File
, "%s %-16s %s" % (Symbol
, Value
, Name
))
1135 # Generate report for the prediction part
1137 # This function generate the predicted fixed address report for a module or
1138 # predicted module execution order for a platform.
1139 # If the input Guid is None, then, it generates the predicted module execution order;
1140 # otherwise it generated the module fixed loading address for the module specified by
1143 # @param self The object pointer
1144 # @param File The file object for report
1145 # @param Guid The module Guid value.
1147 def GenerateReport(self
, File
, Guid
):
1149 self
._GenerateFixedAddressReport
(File
, Guid
.upper(), [])
1151 self
._GenerateExecutionOrderReport
(File
)
1154 # Reports FD region information
1156 # This class reports the FD subsection in the build report file.
1157 # It collects region information of platform flash device.
1158 # If the region is a firmware volume, it lists the set of modules
1159 # and its space information; otherwise, it only lists its region name,
1160 # base address and size in its sub-section header.
1161 # If there are nesting FVs, the nested FVs will list immediate after
1162 # this FD region subsection
1164 class FdRegionReport(object):
1166 # Discover all the nested FV name list.
1168 # This is an internal worker function to discover the all the nested FV information
1169 # in the parent firmware volume. It uses deep first search algorithm recursively to
1170 # find all the FV list name and append them to the list.
1172 # @param self The object pointer
1173 # @param FvName The name of current firmware file system
1174 # @param Wa Workspace context information
1176 def _DiscoverNestedFvList(self
, FvName
, Wa
):
1177 for Ffs
in Wa
.FdfProfile
.FvDict
[FvName
.upper()].FfsList
:
1178 for Section
in Ffs
.SectionList
:
1180 for FvSection
in Section
.SectionList
:
1181 if FvSection
.FvName
in self
.FvList
:
1183 self
._GuidsDb
[Ffs
.NameGuid
.upper()] = FvSection
.FvName
1184 self
.FvList
.append(FvSection
.FvName
)
1185 self
.FvInfo
[FvSection
.FvName
] = ("Nested FV", 0, 0)
1186 self
._DiscoverNestedFvList
(FvSection
.FvName
, Wa
)
1187 except AttributeError:
1191 # Constructor function for class FdRegionReport
1193 # This constructor function generates FdRegionReport object for a specified FdRegion.
1194 # If the FdRegion is a firmware volume, it will recursively find all its nested Firmware
1195 # volume list. This function also collects GUID map in order to dump module identification
1196 # in the final report.
1198 # @param self: The object pointer
1199 # @param FdRegion The current FdRegion object
1200 # @param Wa Workspace context information
1202 def __init__(self
, FdRegion
, Wa
):
1203 self
.Type
= FdRegion
.RegionType
1204 self
.BaseAddress
= FdRegion
.Offset
1205 self
.Size
= FdRegion
.Size
1209 self
._FvDir
= Wa
.FvDir
1212 # If the input FdRegion is not a firmware volume,
1215 if self
.Type
!= "FV":
1219 # Find all nested FVs in the FdRegion
1221 for FvName
in FdRegion
.RegionDataList
:
1222 if FvName
in self
.FvList
:
1224 self
.FvList
.append(FvName
)
1225 self
.FvInfo
[FvName
] = ("Fd Region", self
.BaseAddress
, self
.Size
)
1226 self
._DiscoverNestedFvList
(FvName
, Wa
)
1230 # Collect PCDs declared in DEC files.
1232 for Pa
in Wa
.AutoGenObjectList
:
1233 for Package
in Pa
.PackageList
:
1234 for (TokenCName
, TokenSpaceGuidCName
, DecType
) in Package
.Pcds
:
1235 DecDefaultValue
= Package
.Pcds
[TokenCName
, TokenSpaceGuidCName
, DecType
].DefaultValue
1236 PlatformPcds
[(TokenCName
, TokenSpaceGuidCName
)] = DecDefaultValue
1238 # Collect PCDs defined in DSC common section
1240 for Platform
in Wa
.BuildDatabase
.WorkspaceDb
.PlatformList
:
1241 for (TokenCName
, TokenSpaceGuidCName
) in Platform
.Pcds
:
1242 DscDefaultValue
= Platform
.Pcds
[(TokenCName
, TokenSpaceGuidCName
)].DefaultValue
1243 PlatformPcds
[(TokenCName
, TokenSpaceGuidCName
)] = DscDefaultValue
1246 # Add PEI and DXE a priori files GUIDs defined in PI specification.
1248 self
._GuidsDb
["1B45CC0A-156A-428A-AF62-49864DA0E6E6"] = "PEI Apriori"
1249 self
._GuidsDb
["FC510EE7-FFDC-11D4-BD41-0080C73C8881"] = "DXE Apriori"
1251 # Add ACPI table storage file
1253 self
._GuidsDb
["7E374E25-8E01-4FEE-87F2-390C23C606CD"] = "ACPI table storage"
1255 for Pa
in Wa
.AutoGenObjectList
:
1256 for ModuleKey
in Pa
.Platform
.Modules
:
1257 M
= Pa
.Platform
.Modules
[ModuleKey
].M
1258 InfPath
= os
.path
.join(Wa
.WorkspaceDir
, M
.MetaFile
.File
)
1259 self
._GuidsDb
[M
.Guid
.upper()] = "%s (%s)" % (M
.Module
.BaseName
, InfPath
)
1262 # Collect the GUID map in the FV firmware volume
1264 for FvName
in self
.FvList
:
1265 for Ffs
in Wa
.FdfProfile
.FvDict
[FvName
.upper()].FfsList
:
1268 # collect GUID map for binary EFI file in FDF file.
1270 Guid
= Ffs
.NameGuid
.upper()
1271 Match
= gPcdGuidPattern
.match(Ffs
.NameGuid
)
1273 PcdTokenspace
= Match
.group(1)
1274 PcdToken
= Match
.group(2)
1275 if (PcdToken
, PcdTokenspace
) in PlatformPcds
:
1276 GuidValue
= PlatformPcds
[(PcdToken
, PcdTokenspace
)]
1277 Guid
= GuidStructureByteArrayToGuidString(GuidValue
).upper()
1278 for Section
in Ffs
.SectionList
:
1280 ModuleSectFile
= os
.path
.join(Wa
.WorkspaceDir
, Section
.SectFileName
)
1281 self
._GuidsDb
[Guid
] = ModuleSectFile
1282 except AttributeError:
1284 except AttributeError:
1289 # Internal worker function to generate report for the FD region
1291 # This internal worker function to generate report for the FD region.
1292 # It the type is firmware volume, it lists offset and module identification.
1294 # @param self The object pointer
1295 # @param File The file object for report
1296 # @param Title The title for the FD subsection
1297 # @param BaseAddress The base address for the FD region
1298 # @param Size The size of the FD region
1299 # @param FvName The FV name if the FD region is a firmware volume
1301 def _GenerateReport(self
, File
, Title
, Type
, BaseAddress
, Size
=0, FvName
=None):
1302 FileWrite(File
, gSubSectionStart
)
1303 FileWrite(File
, Title
)
1304 FileWrite(File
, "Type: %s" % Type
)
1305 FileWrite(File
, "Base Address: 0x%X" % BaseAddress
)
1307 if self
.Type
== "FV":
1311 FvReportFileName
= os
.path
.join(self
._FvDir
, FvName
+ ".Fv.txt")
1314 # Collect size info in the firmware volume.
1316 FvReport
= open(FvReportFileName
).read()
1317 Match
= gFvTotalSizePattern
.search(FvReport
)
1319 FvTotalSize
= int(Match
.group(1), 16)
1320 Match
= gFvTakenSizePattern
.search(FvReport
)
1322 FvTakenSize
= int(Match
.group(1), 16)
1323 FvFreeSize
= FvTotalSize
- FvTakenSize
1325 # Write size information to the report file.
1327 FileWrite(File
, "Size: 0x%X (%.0fK)" % (FvTotalSize
, FvTotalSize
/ 1024.0))
1328 FileWrite(File
, "Fv Name: %s (%.1f%% Full)" % (FvName
, FvTakenSize
* 100.0 / FvTotalSize
))
1329 FileWrite(File
, "Occupied Size: 0x%X (%.0fK)" % (FvTakenSize
, FvTakenSize
/ 1024.0))
1330 FileWrite(File
, "Free Size: 0x%X (%.0fK)" % (FvFreeSize
, FvFreeSize
/ 1024.0))
1331 FileWrite(File
, "Offset Module")
1332 FileWrite(File
, gSubSectionSep
)
1334 # Write module offset and module identification to the report file.
1337 for Match
in gOffsetGuidPattern
.finditer(FvReport
):
1338 Guid
= Match
.group(2).upper()
1339 OffsetInfo
[Match
.group(1)] = self
._GuidsDb
.get(Guid
, Guid
)
1340 OffsetList
= OffsetInfo
.keys()
1342 for Offset
in OffsetList
:
1343 FileWrite (File
, "%s %s" % (Offset
, OffsetInfo
[Offset
]))
1345 EdkLogger
.warn(None, "Fail to read report file", FvReportFileName
)
1347 FileWrite(File
, "Size: 0x%X (%.0fK)" % (Size
, Size
/ 1024.0))
1348 FileWrite(File
, gSubSectionEnd
)
1351 # Generate report for the FD region
1353 # This function generates report for the FD region.
1355 # @param self The object pointer
1356 # @param File The file object for report
1358 def GenerateReport(self
, File
):
1359 if (len(self
.FvList
) > 0):
1360 for FvItem
in self
.FvList
:
1361 Info
= self
.FvInfo
[FvItem
]
1362 self
._GenerateReport
(File
, Info
[0], "FV", Info
[1], Info
[2], FvItem
)
1364 self
._GenerateReport
(File
, "FD Region", self
.Type
, self
.BaseAddress
, self
.Size
)
1367 # Reports FD information
1369 # This class reports the FD section in the build report file.
1370 # It collects flash device information for a platform.
1372 class FdReport(object):
1374 # Constructor function for class FdReport
1376 # This constructor function generates FdReport object for a specified
1379 # @param self The object pointer
1380 # @param Fd The current Firmware device object
1381 # @param Wa Workspace context information
1383 def __init__(self
, Fd
, Wa
):
1384 self
.FdName
= Fd
.FdUiName
1385 self
.BaseAddress
= Fd
.BaseAddress
1387 self
.FdRegionList
= [FdRegionReport(FdRegion
, Wa
) for FdRegion
in Fd
.RegionList
]
1390 # Generate report for the firmware device.
1392 # This function generates report for the firmware device.
1394 # @param self The object pointer
1395 # @param File The file object for report
1397 def GenerateReport(self
, File
):
1398 FileWrite(File
, gSectionStart
)
1399 FileWrite(File
, "Firmware Device (FD)")
1400 FileWrite(File
, "FD Name: %s" % self
.FdName
)
1401 FileWrite(File
, "Base Address: %s" % self
.BaseAddress
)
1402 FileWrite(File
, "Size: 0x%X (%.0fK)" % (self
.Size
, self
.Size
/ 1024.0))
1403 if len(self
.FdRegionList
) > 0:
1404 FileWrite(File
, gSectionSep
)
1405 for FdRegionItem
in self
.FdRegionList
:
1406 FdRegionItem
.GenerateReport(File
)
1408 FileWrite(File
, gSectionEnd
)
1413 # Reports platform information
1415 # This class reports the whole platform information
1417 class PlatformReport(object):
1419 # Constructor function for class PlatformReport
1421 # This constructor function generates PlatformReport object a platform build.
1422 # It generates report for platform summary, flash, global PCDs and detailed
1423 # module information for modules involved in platform build.
1425 # @param self The object pointer
1426 # @param Wa Workspace context information
1427 # @param MaList The list of modules in the platform build
1429 def __init__(self
, Wa
, MaList
, ReportType
):
1430 self
._WorkspaceDir
= Wa
.WorkspaceDir
1431 self
.PlatformName
= Wa
.Name
1432 self
.PlatformDscPath
= Wa
.Platform
1433 self
.Architectures
= " ".join(Wa
.ArchList
)
1434 self
.ToolChain
= Wa
.ToolChain
1435 self
.Target
= Wa
.BuildTarget
1436 self
.OutputPath
= os
.path
.join(Wa
.WorkspaceDir
, Wa
.OutputDir
)
1437 self
.BuildEnvironment
= platform
.platform()
1439 self
.PcdReport
= None
1440 if "PCD" in ReportType
:
1441 self
.PcdReport
= PcdReport(Wa
)
1443 self
.FdReportList
= []
1444 if "FLASH" in ReportType
and Wa
.FdfProfile
and MaList
== None:
1445 for Fd
in Wa
.FdfProfile
.FdDict
:
1446 self
.FdReportList
.append(FdReport(Wa
.FdfProfile
.FdDict
[Fd
], Wa
))
1448 self
.PredictionReport
= None
1449 if "FIXED_ADDRESS" in ReportType
or "EXECUTION_ORDER" in ReportType
:
1450 self
.PredictionReport
= PredictionReport(Wa
)
1452 self
.DepexParser
= None
1453 if "DEPEX" in ReportType
:
1454 self
.DepexParser
= DepexParser(Wa
)
1456 self
.ModuleReportList
= []
1458 self
._IsModuleBuild
= True
1460 self
.ModuleReportList
.append(ModuleReport(Ma
, ReportType
))
1462 self
._IsModuleBuild
= False
1463 for Pa
in Wa
.AutoGenObjectList
:
1464 for ModuleKey
in Pa
.Platform
.Modules
:
1465 self
.ModuleReportList
.append(ModuleReport(Pa
.Platform
.Modules
[ModuleKey
].M
, ReportType
))
1470 # Generate report for the whole platform.
1472 # This function generates report for platform information.
1473 # It comprises of platform summary, global PCD, flash and
1474 # module list sections.
1476 # @param self The object pointer
1477 # @param File The file object for report
1478 # @param BuildDuration The total time to build the modules
1479 # @param ReportType The kind of report items in the final report file
1481 def GenerateReport(self
, File
, BuildDuration
, ReportType
):
1482 FileWrite(File
, "Platform Summary")
1483 FileWrite(File
, "Platform Name: %s" % self
.PlatformName
)
1484 FileWrite(File
, "Platform DSC Path: %s" % self
.PlatformDscPath
)
1485 FileWrite(File
, "Architectures: %s" % self
.Architectures
)
1486 FileWrite(File
, "Tool Chain: %s" % self
.ToolChain
)
1487 FileWrite(File
, "Target: %s" % self
.Target
)
1488 FileWrite(File
, "Output Path: %s" % self
.OutputPath
)
1489 FileWrite(File
, "Build Environment: %s" % self
.BuildEnvironment
)
1490 FileWrite(File
, "Build Duration: %s" % BuildDuration
)
1491 FileWrite(File
, "Report Content: %s" % ", ".join(ReportType
))
1493 if not self
._IsModuleBuild
:
1494 if "PCD" in ReportType
:
1495 self
.PcdReport
.GenerateReport(File
, None)
1497 if "FLASH" in ReportType
:
1498 for FdReportListItem
in self
.FdReportList
:
1499 FdReportListItem
.GenerateReport(File
)
1501 for ModuleReportItem
in self
.ModuleReportList
:
1502 ModuleReportItem
.GenerateReport(File
, self
.PcdReport
, self
.PredictionReport
, self
.DepexParser
, ReportType
)
1504 if not self
._IsModuleBuild
:
1505 if "EXECUTION_ORDER" in ReportType
:
1506 self
.PredictionReport
.GenerateReport(File
, None)
1508 ## BuildReport class
1510 # This base class contain the routines to collect data and then
1511 # applies certain format to the output report
1513 class BuildReport(object):
1515 # Constructor function for class BuildReport
1517 # This constructor function generates BuildReport object a platform build.
1518 # It generates report for platform summary, flash, global PCDs and detailed
1519 # module information for modules involved in platform build.
1521 # @param self The object pointer
1522 # @param ReportFile The file name to save report file
1523 # @param ReportType The kind of report items in the final report file
1525 def __init__(self
, ReportFile
, ReportType
):
1526 self
.ReportFile
= ReportFile
1528 self
.ReportList
= []
1529 self
.ReportType
= []
1531 for ReportTypeItem
in ReportType
:
1532 if ReportTypeItem
not in self
.ReportType
:
1533 self
.ReportType
.append(ReportTypeItem
)
1535 self
.ReportType
= ["PCD", "LIBRARY", "BUILD_FLAGS", "DEPEX", "FLASH", "FIXED_ADDRESS"]
1537 # Adds platform report to the list
1539 # This function adds a platform report to the final report list.
1541 # @param self The object pointer
1542 # @param Wa Workspace context information
1543 # @param MaList The list of modules in the platform build
1545 def AddPlatformReport(self
, Wa
, MaList
=None):
1547 self
.ReportList
.append((Wa
, MaList
))
1550 # Generates the final report.
1552 # This function generates platform build report. It invokes GenerateReport()
1553 # method for every platform report in the list.
1555 # @param self The object pointer
1556 # @param BuildDuration The total time to build the modules
1558 def GenerateReport(self
, BuildDuration
):
1562 for (Wa
, MaList
) in self
.ReportList
:
1563 PlatformReport(Wa
, MaList
, self
.ReportType
).GenerateReport(File
, BuildDuration
, self
.ReportType
)
1564 Content
= FileLinesSplit(File
.getvalue(), gLineMaxLength
)
1565 SaveFileOnChange(self
.ReportFile
, Content
, True)
1566 EdkLogger
.quiet("Build report can be found at %s" % os
.path
.abspath(self
.ReportFile
))
1568 EdkLogger
.error(None, FILE_WRITE_FAILURE
, ExtraData
=self
.ReportFile
)
1570 EdkLogger
.error("BuildReport", CODE_ERROR
, "Unknown fatal error when generating build report", ExtraData
=self
.ReportFile
, RaiseError
=False)
1571 EdkLogger
.quiet("(Python %s on %s\n%s)" % (platform
.python_version(), sys
.platform
, traceback
.format_exc()))
1574 # This acts like the main() function for the script, unless it is 'import'ed into another script.
1575 if __name__
== '__main__':