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
46 ## Pattern to extract contents in EDK DXS files
47 gDxsDependencyPattern
= re
.compile(r
"DEPENDENCY_START(.+)DEPENDENCY_END", re
.DOTALL
)
49 ## Pattern to find total FV total size, occupied size in flash report intermediate file
50 gFvTotalSizePattern
= re
.compile(r
"EFI_FV_TOTAL_SIZE = (0x[0-9a-fA-F]+)")
51 gFvTakenSizePattern
= re
.compile(r
"EFI_FV_TAKEN_SIZE = (0x[0-9a-fA-F]+)")
53 ## Pattern to find module size and time stamp in module summary report intermediate file
54 gModuleSizePattern
= re
.compile(r
"MODULE_SIZE = (\d+)")
55 gTimeStampPattern
= re
.compile(r
"TIME_STAMP = (\d+)")
57 ## Pattern to find GUID value in flash description files
58 gPcdGuidPattern
= re
.compile(r
"PCD\((\w+)[.](\w+)\)")
60 ## Pattern to collect offset, GUID value pair in the flash report intermediate file
61 gOffsetGuidPattern
= re
.compile(r
"(0x[0-9A-Fa-f]+) ([-A-Fa-f0-9]+)")
63 ## Pattern to find module base address and entry point in fixed flash map file
64 gModulePattern
= r
"\n[-\w]+\s*\(([^,]+),\s*BaseAddress=%(Address)s,\s*EntryPoint=%(Address)s\)\s*\(GUID=([-0-9A-Fa-f]+)[^)]*\)"
65 gMapFileItemPattern
= re
.compile(gModulePattern
% {"Address" : "(-?0[xX][0-9A-Fa-f]+)"})
67 ## Pattern to find all module referenced header files in source files
68 gIncludePattern
= re
.compile(r
'#include\s*["<]([^">]+)[">]')
69 gIncludePattern2
= re
.compile(r
"#include\s+EFI_([A-Z_]+)\s*[(]\s*(\w+)\s*[)]")
71 ## Pattern to find the entry point for EDK module using EDKII Glue library
72 gGlueLibEntryPoint
= re
.compile(r
"__EDKII_GLUE_MODULE_ENTRY_POINT__\s*=\s*(\w+)")
74 ## Tags for MaxLength of line in report
77 ## Tags for end of line in report
80 ## Tags for section start, end and separator
81 gSectionStart
= ">" + "=" * (gLineMaxLength
- 2) + "<"
82 gSectionEnd
= "<" + "=" * (gLineMaxLength
- 2) + ">" + "\n"
83 gSectionSep
= "=" * gLineMaxLength
85 ## Tags for subsection start, end and separator
86 gSubSectionStart
= ">" + "-" * (gLineMaxLength
- 2) + "<"
87 gSubSectionEnd
= "<" + "-" * (gLineMaxLength
- 2) + ">"
88 gSubSectionSep
= "-" * gLineMaxLength
91 ## The look up table to map PCD type to pair of report display type and DEC type
93 'FixedAtBuild' : ('FIXED', 'FixedAtBuild'),
94 'PatchableInModule': ('PATCH', 'PatchableInModule'),
95 'FeatureFlag' : ('FLAG', 'FeatureFlag'),
96 'Dynamic' : ('DYN', 'Dynamic'),
97 'DynamicHii' : ('DYNHII', 'Dynamic'),
98 'DynamicVpd' : ('DYNVPD', 'Dynamic'),
99 'DynamicEx' : ('DEX', 'DynamicEx'),
100 'DynamicExHii' : ('DEXHII', 'DynamicEx'),
101 'DynamicExVpd' : ('DEXVPD', 'DynamicEx'),
104 ## The look up table to map module type to driver type
106 'SEC' : '0x3 (SECURITY_CORE)',
107 'PEI_CORE' : '0x4 (PEI_CORE)',
108 'PEIM' : '0x6 (PEIM)',
109 'DXE_CORE' : '0x5 (DXE_CORE)',
110 'DXE_DRIVER' : '0x7 (DRIVER)',
111 'DXE_SAL_DRIVER' : '0x7 (DRIVER)',
112 'DXE_SMM_DRIVER' : '0x7 (DRIVER)',
113 'DXE_RUNTIME_DRIVER': '0x7 (DRIVER)',
114 'UEFI_DRIVER' : '0x7 (DRIVER)',
115 'UEFI_APPLICATION' : '0x9 (APPLICATION)',
116 'SMM_CORE' : '0xD (SMM_CORE)',
117 'SMM_DRIVER' : '0xA (SMM)', # Extension of module type to support PI 1.1 SMM drivers
120 ## The look up table of the supported opcode in the dependency expression binaries
121 gOpCodeList
= ["BEFORE", "AFTER", "PUSH", "AND", "OR", "NOT", "TRUE", "FALSE", "END", "SOR"]
124 # Writes a string to the file object.
126 # This function writes a string to the file object and a new line is appended
127 # afterwards. It may optionally wraps the string for better readability.
129 # @File The file object to write
130 # @String The string to be written to the file
131 # @Wrapper Indicates whether to wrap the string
133 def FileWrite(File
, String
, Wrapper
=False):
135 String
= textwrap
.fill(String
, 120)
136 File
.write(String
+ gEndOfLine
)
139 # Find all the header file that the module source directly includes.
141 # This function scans source code to find all header files the module may
142 # include. This is not accurate but very effective to find all the header
143 # file the module might include with #include statement.
145 # @Source The source file name
146 # @IncludePathList The list of include path to find the source file.
147 # @IncludeFiles The dictionary of current found include files.
149 def FindIncludeFiles(Source
, IncludePathList
, IncludeFiles
):
150 FileContents
= open(Source
).read()
152 # Find header files with pattern #include "XXX.h" or #include <XXX.h>
154 for Match
in gIncludePattern
.finditer(FileContents
):
155 FileName
= Match
.group(1).strip()
156 for Dir
in [os
.path
.dirname(Source
)] + IncludePathList
:
157 FullFileName
= os
.path
.normpath(os
.path
.join(Dir
, FileName
))
158 if os
.path
.exists(FullFileName
):
159 IncludeFiles
[FullFileName
.lower().replace("\\", "/")] = FullFileName
163 # Find header files with pattern like #include EFI_PPI_CONSUMER(XXX)
165 for Match
in gIncludePattern2
.finditer(FileContents
):
167 Type
= Match
.group(1)
168 if "ARCH_PROTOCOL" in Type
:
169 FileName
= "ArchProtocol/%(Key)s/%(Key)s.h" % {"Key" : Key
}
170 elif "PROTOCOL" in Type
:
171 FileName
= "Protocol/%(Key)s/%(Key)s.h" % {"Key" : Key
}
173 FileName
= "Ppi/%(Key)s/%(Key)s.h" % {"Key" : Key
}
175 FileName
= "Guid/%(Key)s/%(Key)s.h" % {"Key" : Key
}
178 for Dir
in IncludePathList
:
179 FullFileName
= os
.path
.normpath(os
.path
.join(Dir
, FileName
))
180 if os
.path
.exists(FullFileName
):
181 IncludeFiles
[FullFileName
.lower().replace("\\", "/")] = FullFileName
184 ## Split each lines in file
186 # This method is used to split the lines in file to make the length of each line
187 # less than MaxLength.
189 # @param Content The content of file
190 # @param MaxLength The Max Length of the line
192 def FileLinesSplit(Content
=None, MaxLength
=None):
193 ContentList
= Content
.split(TAB_LINE_BREAK
)
196 for Line
in ContentList
:
197 while len(Line
.rstrip()) > MaxLength
:
198 LineSpaceIndex
= Line
.rfind(TAB_SPACE_SPLIT
, 0, MaxLength
)
199 LineSlashIndex
= Line
.rfind(TAB_SLASH
, 0, MaxLength
)
200 LineBackSlashIndex
= Line
.rfind(TAB_BACK_SLASH
, 0, MaxLength
)
201 if max(LineSpaceIndex
, LineSlashIndex
, LineBackSlashIndex
) > 0:
202 LineBreakIndex
= max(LineSpaceIndex
, LineSlashIndex
, LineBackSlashIndex
)
204 LineBreakIndex
= MaxLength
205 NewContentList
.append(Line
[:LineBreakIndex
])
206 Line
= Line
[LineBreakIndex
:]
208 NewContentList
.append(Line
)
209 for NewLine
in NewContentList
:
210 NewContent
+= NewLine
+ TAB_LINE_BREAK
212 NewContent
= NewContent
.replace(TAB_LINE_BREAK
, gEndOfLine
).replace('\r\r\n', gEndOfLine
)
218 # Parse binary dependency expression section
220 # This utility class parses the dependency expression section and translate the readable
221 # GUID name and value.
223 class DepexParser(object):
225 # Constructor function for class DepexParser
227 # This constructor function collect GUID values so that the readable
228 # GUID name can be translated.
230 # @param self The object pointer
231 # @param Wa Workspace context information
233 def __init__(self
, Wa
):
235 for Pa
in Wa
.AutoGenObjectList
:
236 for Package
in Pa
.PackageList
:
237 for Protocol
in Package
.Protocols
:
238 GuidValue
= GuidStructureStringToGuidString(Package
.Protocols
[Protocol
])
239 self
._GuidDb
[GuidValue
.upper()] = Protocol
240 for Ppi
in Package
.Ppis
:
241 GuidValue
= GuidStructureStringToGuidString(Package
.Ppis
[Ppi
])
242 self
._GuidDb
[GuidValue
.upper()] = Ppi
243 for Guid
in Package
.Guids
:
244 GuidValue
= GuidStructureStringToGuidString(Package
.Guids
[Guid
])
245 self
._GuidDb
[GuidValue
.upper()] = Guid
248 # Parse the binary dependency expression files.
250 # This function parses the binary dependency expression file and translate it
251 # to the instruction list.
253 # @param self The object pointer
254 # @param DepexFileName The file name of binary dependency expression file.
256 def ParseDepexFile(self
, DepexFileName
):
257 DepexFile
= open(DepexFileName
, "rb")
259 OpCode
= DepexFile
.read(1)
261 Statement
= gOpCodeList
[struct
.unpack("B", OpCode
)[0]]
262 if Statement
in ["BEFORE", "AFTER", "PUSH"]:
263 GuidValue
= "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X" % \
264 struct
.unpack("=LHHBBBBBBBB", DepexFile
.read(16))
265 GuidString
= self
._GuidDb
.get(GuidValue
, GuidValue
)
266 Statement
= "%s %s" % (Statement
, GuidString
)
267 DepexStatement
.append(Statement
)
268 OpCode
= DepexFile
.read(1)
270 return DepexStatement
273 # Reports library information
275 # This class reports the module library subsection in the build report file.
277 class LibraryReport(object):
279 # Constructor function for class LibraryReport
281 # This constructor function generates LibraryReport object for
284 # @param self The object pointer
285 # @param M Module context information
287 def __init__(self
, M
):
288 self
.LibraryList
= []
289 if int(str(M
.AutoGenVersion
), 0) >= 0x00010005:
290 self
._EdkIIModule
= True
292 self
._EdkIIModule
= False
294 for Lib
in M
.DependentLibraryList
:
295 LibInfPath
= str(Lib
)
296 LibClassList
= Lib
.LibraryClass
[0].LibraryClass
297 LibConstructorList
= Lib
.ConstructorList
298 LibDesstructorList
= Lib
.DestructorList
299 LibDepexList
= Lib
.DepexExpression
[M
.Arch
, M
.ModuleType
]
300 self
.LibraryList
.append((LibInfPath
, LibClassList
, LibConstructorList
, LibDesstructorList
, LibDepexList
))
303 # Generate report for module library information
305 # This function generates report for the module library.
306 # If the module is EDKII style one, the additional library class, library
307 # constructor/destructor and dependency expression may also be reported.
309 # @param self The object pointer
310 # @param File The file object for report
312 def GenerateReport(self
, File
):
313 FileWrite(File
, gSubSectionStart
)
314 FileWrite(File
, TAB_BRG_LIBRARY
)
315 if len(self
.LibraryList
) > 0:
316 FileWrite(File
, gSubSectionSep
)
317 for LibraryItem
in self
.LibraryList
:
318 LibInfPath
= LibraryItem
[0]
319 FileWrite(File
, LibInfPath
)
322 # Report library class, library constructor and destructor for
323 # EDKII style module.
325 if self
._EdkIIModule
:
326 LibClass
= LibraryItem
[1]
328 LibConstructor
= " ".join(LibraryItem
[2])
330 EdkIILibInfo
+= " C = " + LibConstructor
331 LibDestructor
= " ".join(LibraryItem
[3])
333 EdkIILibInfo
+= " D = " + LibDestructor
334 LibDepex
= " ".join(LibraryItem
[4])
336 EdkIILibInfo
+= " Depex = " + LibDepex
338 FileWrite(File
, "{%s: %s}" % (LibClass
, EdkIILibInfo
))
340 FileWrite(File
, "{%s}" % LibClass
)
342 FileWrite(File
, gSubSectionEnd
)
345 # Reports dependency expression information
347 # This class reports the module dependency expression subsection in the build report file.
349 class DepexReport(object):
351 # Constructor function for class DepexReport
353 # This constructor function generates DepexReport object for
354 # a module. If the module source contains the DXS file (usually EDK
355 # style module), it uses the dependency in DXS file; otherwise,
356 # it uses the dependency expression from its own INF [Depex] section
357 # and then merges with the ones from its dependent library INF.
359 # @param self The object pointer
360 # @param M Module context information
362 def __init__(self
, M
):
364 self
._DepexFileName
= os
.path
.join(M
.BuildDir
, "OUTPUT", M
.Module
.BaseName
+ ".depex")
365 ModuleType
= M
.ModuleType
367 ModuleType
= gComponentType2ModuleType
.get(M
.ComponentType
, "")
369 if ModuleType
in ["SEC", "PEI_CORE", "DXE_CORE", "SMM_CORE", "UEFI_APPLICATION"]:
372 for Source
in M
.SourceFileList
:
373 if os
.path
.splitext(Source
.Path
)[1].lower() == ".dxs":
374 Match
= gDxsDependencyPattern
.search(open(Source
.Path
).read())
376 self
.Depex
= Match
.group(1).strip()
380 self
.Depex
= M
.DepexExpressionList
.get(M
.ModuleType
, "")
381 self
.ModuleDepex
= " ".join(M
.Module
.DepexExpression
[M
.Arch
, M
.ModuleType
])
382 if not self
.ModuleDepex
:
383 self
.ModuleDepex
= "(None)"
386 for Lib
in M
.DependentLibraryList
:
387 LibDepex
= " ".join(Lib
.DepexExpression
[M
.Arch
, M
.ModuleType
]).strip()
389 LibDepexList
.append("(" + LibDepex
+ ")")
390 self
.LibraryDepex
= " AND ".join(LibDepexList
)
391 if not self
.LibraryDepex
:
392 self
.LibraryDepex
= "(None)"
396 # Generate report for module dependency expression information
398 # This function generates report for the module dependency expression.
400 # @param self The object pointer
401 # @param File The file object for report
402 # @param GlobalDepexParser The platform global Dependency expression parser object
404 def GenerateReport(self
, File
, GlobalDepexParser
):
406 FileWrite(File
, gSubSectionStart
)
407 FileWrite(File
, TAB_DEPEX
)
408 FileWrite(File
, gSubSectionEnd
)
410 FileWrite(File
, gSubSectionStart
)
411 if os
.path
.isfile(self
._DepexFileName
):
413 DepexStatements
= GlobalDepexParser
.ParseDepexFile(self
._DepexFileName
)
414 FileWrite(File
, "Final Dependency Expression (DEPEX) Instructions")
415 for DepexStatement
in DepexStatements
:
416 FileWrite(File
, " %s" % DepexStatement
)
417 FileWrite(File
, gSubSectionSep
)
419 EdkLogger
.warn(None, "Dependency expression file is corrupted", self
._DepexFileName
)
421 FileWrite(File
, "Dependency Expression (DEPEX) from %s" % self
.Source
)
423 if self
.Source
== "INF":
424 FileWrite(File
, "%s" % self
.Depex
, True)
425 FileWrite(File
, gSubSectionSep
)
426 FileWrite(File
, "From Module INF: %s" % self
.ModuleDepex
, True)
427 FileWrite(File
, "From Library INF: %s" % self
.LibraryDepex
, True)
429 FileWrite(File
, "%s" % self
.Depex
)
430 FileWrite(File
, gSubSectionEnd
)
433 # Reports dependency expression information
435 # This class reports the module build flags subsection in the build report file.
437 class BuildFlagsReport(object):
439 # Constructor function for class BuildFlagsReport
441 # This constructor function generates BuildFlagsReport object for
442 # a module. It reports the build tool chain tag and all relevant
443 # build flags to build the module.
445 # @param self The object pointer
446 # @param M Module context information
448 def __init__(self
, M
):
451 # Add build flags according to source file extension so that
452 # irrelevant ones can be filtered out.
454 for Source
in M
.SourceFileList
:
455 Ext
= os
.path
.splitext(Source
.File
)[1].lower()
456 if Ext
in [".c", ".cc", ".cpp"]:
457 BuildOptions
["CC"] = 1
458 elif Ext
in [".s", ".asm"]:
459 BuildOptions
["PP"] = 1
460 BuildOptions
["ASM"] = 1
461 elif Ext
in [".vfr"]:
462 BuildOptions
["VFRPP"] = 1
463 BuildOptions
["VFR"] = 1
464 elif Ext
in [".dxs"]:
465 BuildOptions
["APP"] = 1
466 BuildOptions
["CC"] = 1
467 elif Ext
in [".asl"]:
468 BuildOptions
["ASLPP"] = 1
469 BuildOptions
["ASL"] = 1
470 elif Ext
in [".aslc"]:
471 BuildOptions
["ASLCC"] = 1
472 BuildOptions
["ASLDLINK"] = 1
473 BuildOptions
["CC"] = 1
474 elif Ext
in [".asm16"]:
475 BuildOptions
["ASMLINK"] = 1
476 BuildOptions
["SLINK"] = 1
477 BuildOptions
["DLINK"] = 1
480 # Save module build flags.
482 self
.ToolChainTag
= M
.ToolChain
484 for Tool
in BuildOptions
:
485 self
.BuildFlags
[Tool
+ "_FLAGS"] = M
.BuildOption
.get(Tool
, {}).get("FLAGS", "")
488 # Generate report for module build flags information
490 # This function generates report for the module build flags expression.
492 # @param self The object pointer
493 # @param File The file object for report
495 def GenerateReport(self
, File
):
496 FileWrite(File
, gSubSectionStart
)
497 FileWrite(File
, "Build Flags")
498 FileWrite(File
, "Tool Chain Tag: %s" % self
.ToolChainTag
)
499 for Tool
in self
.BuildFlags
:
500 FileWrite(File
, gSubSectionSep
)
501 FileWrite(File
, "%s = %s" % (Tool
, self
.BuildFlags
[Tool
]), True)
503 FileWrite(File
, gSubSectionEnd
)
507 # Reports individual module information
509 # This class reports the module section in the build report file.
510 # It comprises of module summary, module PCD, library, dependency expression,
511 # build flags sections.
513 class ModuleReport(object):
515 # Constructor function for class ModuleReport
517 # This constructor function generates ModuleReport object for
518 # a separate module in a platform build.
520 # @param self The object pointer
521 # @param M Module context information
522 # @param ReportType The kind of report items in the final report file
524 def __init__(self
, M
, ReportType
):
525 self
.ModuleName
= M
.Module
.BaseName
526 self
.ModuleInfPath
= M
.MetaFile
.File
527 self
.FileGuid
= M
.Guid
529 self
.BuildTimeStamp
= None
532 ModuleType
= M
.ModuleType
534 ModuleType
= gComponentType2ModuleType
.get(M
.ComponentType
, "")
536 # If a module complies to PI 1.1, promote Module type to "SMM_DRIVER"
538 if ModuleType
== "DXE_SMM_DRIVER":
539 PiSpec
= M
.Module
.Specification
.get("PI_SPECIFICATION_VERSION", "0x00010000")
540 if int(PiSpec
, 0) >= 0x0001000A:
541 ModuleType
= "SMM_DRIVER"
542 self
.DriverType
= gDriverTypeMap
.get(ModuleType
, "0x2 (FREE_FORM)")
543 self
.UefiSpecVersion
= M
.Module
.Specification
.get("UEFI_SPECIFICATION_VERSION", "")
544 self
.PiSpecVersion
= M
.Module
.Specification
.get("PI_SPECIFICATION_VERSION", "")
545 self
.PciDeviceId
= M
.Module
.Defines
.get("PCI_DEVICE_ID", "")
546 self
.PciVendorId
= M
.Module
.Defines
.get("PCI_VENDOR_ID", "")
547 self
.PciClassCode
= M
.Module
.Defines
.get("PCI_CLASS_CODE", "")
549 self
._BuildDir
= M
.BuildDir
550 self
.ModulePcdSet
= {}
551 if "PCD" in ReportType
:
553 # Collect all module used PCD set: module INF referenced directly or indirectly.
554 # It also saves module INF default values of them in case they exist.
556 for Pcd
in M
.ModulePcdList
+ M
.LibraryPcdList
:
557 self
.ModulePcdSet
.setdefault((Pcd
.TokenCName
, Pcd
.TokenSpaceGuidCName
, Pcd
.Type
), (Pcd
.InfDefaultValue
, Pcd
.DefaultValue
))
559 self
.LibraryReport
= None
560 if "LIBRARY" in ReportType
:
561 self
.LibraryReport
= LibraryReport(M
)
563 self
.DepexReport
= None
564 if "DEPEX" in ReportType
:
565 self
.DepexReport
= DepexReport(M
)
567 if "BUILD_FLAGS" in ReportType
:
568 self
.BuildFlagsReport
= BuildFlagsReport(M
)
572 # Generate report for module information
574 # This function generates report for separate module expression
575 # in a platform build.
577 # @param self The object pointer
578 # @param File The file object for report
579 # @param GlobalPcdReport The platform global PCD report object
580 # @param GlobalPredictionReport The platform global Prediction report object
581 # @param GlobalDepexParser The platform global Dependency expression parser object
582 # @param ReportType The kind of report items in the final report file
584 def GenerateReport(self
, File
, GlobalPcdReport
, GlobalPredictionReport
, GlobalDepexParser
, ReportType
):
585 FileWrite(File
, gSectionStart
)
587 FwReportFileName
= os
.path
.join(self
._BuildDir
, "DEBUG", self
.ModuleName
+ ".txt")
588 if os
.path
.isfile(FwReportFileName
):
590 FileContents
= open(FwReportFileName
).read()
591 Match
= gModuleSizePattern
.search(FileContents
)
593 self
.Size
= int(Match
.group(1))
595 Match
= gTimeStampPattern
.search(FileContents
)
597 self
.BuildTimeStamp
= datetime
.fromtimestamp(int(Match
.group(1)))
599 EdkLogger
.warn(None, "Fail to read report file", FwReportFileName
)
601 FileWrite(File
, "Module Summary")
602 FileWrite(File
, "Module Name: %s" % self
.ModuleName
)
603 FileWrite(File
, "Module INF Path: %s" % self
.ModuleInfPath
)
604 FileWrite(File
, "File GUID: %s" % self
.FileGuid
)
606 FileWrite(File
, "Size: 0x%X (%.2fK)" % (self
.Size
, self
.Size
/ 1024.0))
607 if self
.BuildTimeStamp
:
608 FileWrite(File
, "Build Time Stamp: %s" % self
.BuildTimeStamp
)
610 FileWrite(File
, "Driver Type: %s" % self
.DriverType
)
611 if self
.UefiSpecVersion
:
612 FileWrite(File
, "UEFI Spec Version: %s" % self
.UefiSpecVersion
)
613 if self
.PiSpecVersion
:
614 FileWrite(File
, "PI Spec Version: %s" % self
.PiSpecVersion
)
616 FileWrite(File
, "PCI Device ID: %s" % self
.PciDeviceId
)
618 FileWrite(File
, "PCI Vendor ID: %s" % self
.PciVendorId
)
619 if self
.PciClassCode
:
620 FileWrite(File
, "PCI Class Code: %s" % self
.PciClassCode
)
622 FileWrite(File
, gSectionSep
)
624 if "PCD" in ReportType
:
625 GlobalPcdReport
.GenerateReport(File
, self
.ModulePcdSet
)
627 if "LIBRARY" in ReportType
:
628 self
.LibraryReport
.GenerateReport(File
)
630 if "DEPEX" in ReportType
:
631 self
.DepexReport
.GenerateReport(File
, GlobalDepexParser
)
633 if "BUILD_FLAGS" in ReportType
:
634 self
.BuildFlagsReport
.GenerateReport(File
)
636 if "FIXED_ADDRESS" in ReportType
and self
.FileGuid
:
637 GlobalPredictionReport
.GenerateReport(File
, self
.FileGuid
)
639 FileWrite(File
, gSectionEnd
)
642 # Reports platform and module PCD information
644 # This class reports the platform PCD section and module PCD subsection
645 # in the build report file.
647 class PcdReport(object):
649 # Constructor function for class PcdReport
651 # This constructor function generates PcdReport object a platform build.
652 # It collects the whole PCD database from platform DSC files, platform
653 # flash description file and package DEC files.
655 # @param self The object pointer
656 # @param Wa Workspace context information
658 def __init__(self
, Wa
):
662 self
.FdfPcdSet
= Wa
.FdfProfile
.PcdDict
666 self
.ModulePcdOverride
= {}
667 for Pa
in Wa
.AutoGenObjectList
:
669 # Collect all platform referenced PCDs and grouped them by PCD token space
672 for Pcd
in Pa
.AllPcdList
:
673 PcdList
= self
.AllPcds
.setdefault(Pcd
.TokenSpaceGuidCName
, {}).setdefault(Pcd
.Type
, [])
674 if Pcd
not in PcdList
:
676 if len(Pcd
.TokenCName
) > self
.MaxLen
:
677 self
.MaxLen
= len(Pcd
.TokenCName
)
679 for Module
in Pa
.Platform
.Modules
.values():
681 # Collect module override PCDs
683 for ModulePcd
in Module
.M
.ModulePcdList
+ Module
.M
.LibraryPcdList
:
684 TokenCName
= ModulePcd
.TokenCName
685 TokenSpaceGuid
= ModulePcd
.TokenSpaceGuidCName
686 ModuleDefault
= ModulePcd
.DefaultValue
687 ModulePath
= os
.path
.basename(Module
.M
.MetaFile
.File
)
688 self
.ModulePcdOverride
.setdefault((TokenCName
, TokenSpaceGuid
), {})[ModulePath
] = ModuleDefault
692 # Collect PCD DEC default value.
694 self
.DecPcdDefault
= {}
695 for Pa
in Wa
.AutoGenObjectList
:
696 for Package
in Pa
.PackageList
:
697 for (TokenCName
, TokenSpaceGuidCName
, DecType
) in Package
.Pcds
:
698 DecDefaultValue
= Package
.Pcds
[TokenCName
, TokenSpaceGuidCName
, DecType
].DefaultValue
699 self
.DecPcdDefault
.setdefault((TokenCName
, TokenSpaceGuidCName
, DecType
), DecDefaultValue
)
701 # Collect PCDs defined in DSC common section
703 self
.DscPcdDefault
= {}
704 for Arch
in Wa
.ArchList
:
705 Platform
= Wa
.BuildDatabase
[Wa
.MetaFile
, Arch
, Wa
.BuildTarget
, Wa
.ToolChain
]
706 for (TokenCName
, TokenSpaceGuidCName
) in Platform
.Pcds
:
707 DscDefaultValue
= Platform
.Pcds
[(TokenCName
, TokenSpaceGuidCName
)].DefaultValue
709 self
.DscPcdDefault
[(TokenCName
, TokenSpaceGuidCName
)] = DscDefaultValue
712 # Generate report for PCD information
714 # This function generates report for separate module expression
715 # in a platform build.
717 # @param self The object pointer
718 # @param File The file object for report
719 # @param ModulePcdSet Set of all PCDs referenced by module or None for
720 # platform PCD report
721 # @param DscOverridePcds Module DSC override PCDs set
723 def GenerateReport(self
, File
, ModulePcdSet
):
724 if ModulePcdSet
== None:
726 # For platform global PCD section
728 FileWrite(File
, gSectionStart
)
729 FileWrite(File
, "Platform Configuration Database Report")
730 FileWrite(File
, " *P - Platform scoped PCD override in DSC file")
731 FileWrite(File
, " *F - Platform scoped PCD override in FDF file")
732 FileWrite(File
, " *M - Module scoped PCD override")
733 FileWrite(File
, gSectionSep
)
736 # For module PCD sub-section
738 FileWrite(File
, gSubSectionStart
)
739 FileWrite(File
, TAB_BRG_PCD
)
740 FileWrite(File
, gSubSectionSep
)
742 for Key
in self
.AllPcds
:
744 # Group PCD by their token space GUID C Name
747 for Type
in self
.AllPcds
[Key
]:
749 # Group PCD by their usage type
751 TypeName
, DecType
= gPcdTypeMap
.get(Type
, ("", Type
))
752 for Pcd
in self
.AllPcds
[Key
][Type
]:
754 # Get PCD default value and their override relationship
756 DecDefaultValue
= self
.DecPcdDefault
.get((Pcd
.TokenCName
, Pcd
.TokenSpaceGuidCName
, DecType
))
757 DscDefaultValue
= self
.DscPcdDefault
.get((Pcd
.TokenCName
, Pcd
.TokenSpaceGuidCName
))
758 DscDefaultValue
= self
.FdfPcdSet
.get((Pcd
.TokenCName
, Key
), DscDefaultValue
)
759 InfDefaultValue
= None
761 PcdValue
= DecDefaultValue
763 PcdValue
= DscDefaultValue
764 if ModulePcdSet
!= None:
765 if (Pcd
.TokenCName
, Pcd
.TokenSpaceGuidCName
, Type
) not in ModulePcdSet
:
767 InfDefault
, PcdValue
= ModulePcdSet
[Pcd
.TokenCName
, Pcd
.TokenSpaceGuidCName
, Type
]
771 if ModulePcdSet
== None:
777 if Pcd
.DatumType
in ('UINT8', 'UINT16', 'UINT32', 'UINT64'):
778 PcdValueNumber
= int(PcdValue
.strip(), 0)
779 if DecDefaultValue
== None:
782 DecDefaultValueNumber
= int(DecDefaultValue
.strip(), 0)
783 DecMatch
= (DecDefaultValueNumber
== PcdValueNumber
)
785 if InfDefaultValue
== None:
788 InfDefaultValueNumber
= int(InfDefaultValue
.strip(), 0)
789 InfMatch
= (InfDefaultValueNumber
== PcdValueNumber
)
791 if DscDefaultValue
== None:
794 DscDefaultValueNumber
= int(DscDefaultValue
.strip(), 0)
795 DscMatch
= (DscDefaultValueNumber
== PcdValueNumber
)
797 if DecDefaultValue
== None:
800 DecMatch
= (DecDefaultValue
.strip() == PcdValue
.strip())
802 if InfDefaultValue
== None:
805 InfMatch
= (InfDefaultValue
.strip() == PcdValue
.strip())
807 if DscDefaultValue
== None:
810 DscMatch
= (DscDefaultValue
.strip() == PcdValue
.strip())
813 # Report PCD item according to their override relationship
815 if DecMatch
and InfMatch
:
816 FileWrite(File
, ' %-*s: %6s %10s = %-22s' % (self
.MaxLen
, Pcd
.TokenCName
, TypeName
, '(' + Pcd
.DatumType
+ ')', PcdValue
.strip()))
819 if (Pcd
.TokenCName
, Key
) in self
.FdfPcdSet
:
820 FileWrite(File
, ' *F %-*s: %6s %10s = %-22s' % (self
.MaxLen
, Pcd
.TokenCName
, TypeName
, '(' + Pcd
.DatumType
+ ')', PcdValue
.strip()))
822 FileWrite(File
, ' *P %-*s: %6s %10s = %-22s' % (self
.MaxLen
, Pcd
.TokenCName
, TypeName
, '(' + Pcd
.DatumType
+ ')', PcdValue
.strip()))
824 FileWrite(File
, ' *M %-*s: %6s %10s = %-22s' % (self
.MaxLen
, Pcd
.TokenCName
, TypeName
, '(' + Pcd
.DatumType
+ ')', PcdValue
.strip()))
826 if TypeName
in ('DYNHII', 'DEXHII', 'DYNVPD', 'DEXVPD'):
827 for SkuInfo
in Pcd
.SkuInfoList
.values():
828 if TypeName
in ('DYNHII', 'DEXHII'):
829 FileWrite(File
, '%*s: %s: %s' % (self
.MaxLen
+ 4, SkuInfo
.VariableGuid
, SkuInfo
.VariableName
, SkuInfo
.VariableOffset
))
831 FileWrite(File
, '%*s' % (self
.MaxLen
+ 4, SkuInfo
.VpdOffset
))
833 if not DscMatch
and DscDefaultValue
!= None:
834 FileWrite(File
, ' %*s = %s' % (self
.MaxLen
+ 19, 'DSC DEFAULT', DscDefaultValue
.strip()))
836 if not InfMatch
and InfDefaultValue
!= None:
837 FileWrite(File
, ' %*s = %s' % (self
.MaxLen
+ 19, 'INF DEFAULT', InfDefaultValue
.strip()))
839 if not DecMatch
and DecDefaultValue
!= None:
840 FileWrite(File
, ' %*s = %s' % (self
.MaxLen
+ 19, 'DEC DEFAULT', DecDefaultValue
.strip()))
842 if ModulePcdSet
== None:
843 ModuleOverride
= self
.ModulePcdOverride
.get((Pcd
.TokenCName
, Pcd
.TokenSpaceGuidCName
), {})
844 for ModulePath
in ModuleOverride
:
845 ModuleDefault
= ModuleOverride
[ModulePath
]
846 if Pcd
.DatumType
in ('UINT8', 'UINT16', 'UINT32', 'UINT64'):
847 ModulePcdDefaultValueNumber
= int(ModuleDefault
.strip(), 0)
848 Match
= (ModulePcdDefaultValueNumber
== PcdValueNumber
)
850 Match
= (ModuleDefault
.strip() == PcdValue
.strip())
853 FileWrite(File
, ' *M %-*s = %s' % (self
.MaxLen
+ 19, ModulePath
, ModuleDefault
.strip()))
855 if ModulePcdSet
== None:
856 FileWrite(File
, gSectionEnd
)
858 FileWrite(File
, gSubSectionEnd
)
863 # Reports platform and module Prediction information
865 # This class reports the platform execution order prediction section and
866 # module load fixed address prediction subsection in the build report file.
868 class PredictionReport(object):
870 # Constructor function for class PredictionReport
872 # This constructor function generates PredictionReport object for the platform.
874 # @param self: The object pointer
875 # @param Wa Workspace context information
877 def __init__(self
, Wa
):
878 self
._MapFileName
= os
.path
.join(Wa
.BuildDir
, Wa
.Name
+ ".map")
879 self
._MapFileParsed
= False
880 self
._EotToolInvoked
= False
881 self
._FvDir
= Wa
.FvDir
882 self
._EotDir
= Wa
.BuildDir
883 self
._FfsEntryPoint
= {}
885 self
._SourceList
= []
886 self
.FixedMapDict
= {}
891 # Collect all platform reference source files and GUID C Name
893 for Pa
in Wa
.AutoGenObjectList
:
894 for Module
in Pa
.LibraryAutoGenList
+ Pa
.ModuleAutoGenList
:
896 # BASE typed modules are EFI agnostic, so we need not scan
897 # their source code to find PPI/Protocol produce or consume
900 if Module
.ModuleType
== "BASE":
903 # Add module referenced source files
905 self
._SourceList
.append(str(Module
))
907 for Source
in Module
.SourceFileList
:
908 if os
.path
.splitext(str(Source
))[1].lower() == ".c":
909 self
._SourceList
.append(" " + str(Source
))
910 FindIncludeFiles(Source
.Path
, Module
.IncludePathList
, IncludeList
)
911 for IncludeFile
in IncludeList
.values():
912 self
._SourceList
.append(" " + IncludeFile
)
914 for Guid
in Module
.PpiList
:
915 self
._GuidMap
[Guid
] = GuidStructureStringToGuidString(Module
.PpiList
[Guid
])
916 for Guid
in Module
.ProtocolList
:
917 self
._GuidMap
[Guid
] = GuidStructureStringToGuidString(Module
.ProtocolList
[Guid
])
918 for Guid
in Module
.GuidList
:
919 self
._GuidMap
[Guid
] = GuidStructureStringToGuidString(Module
.GuidList
[Guid
])
921 if Module
.Guid
and not Module
.IsLibrary
:
922 EntryPoint
= " ".join(Module
.Module
.ModuleEntryPointList
)
923 if int(str(Module
.AutoGenVersion
), 0) >= 0x00010005:
924 RealEntryPoint
= "_ModuleEntryPoint"
926 RealEntryPoint
= EntryPoint
927 if EntryPoint
== "_ModuleEntryPoint":
928 CCFlags
= Module
.BuildOption
.get("CC", {}).get("FLAGS", "")
929 Match
= gGlueLibEntryPoint
.search(CCFlags
)
931 EntryPoint
= Match
.group(1)
933 self
._FfsEntryPoint
[Module
.Guid
.upper()] = (EntryPoint
, RealEntryPoint
)
937 # Collect platform firmware volume list as the input of EOT.
941 for Fd
in Wa
.FdfProfile
.FdDict
:
942 for FdRegion
in Wa
.FdfProfile
.FdDict
[Fd
].RegionList
:
943 if FdRegion
.RegionType
!= "FV":
945 for FvName
in FdRegion
.RegionDataList
:
946 if FvName
in self
._FvList
:
948 self
._FvList
.append(FvName
)
949 for Ffs
in Wa
.FdfProfile
.FvDict
[FvName
.upper()].FfsList
:
950 for Section
in Ffs
.SectionList
:
952 for FvSection
in Section
.SectionList
:
953 if FvSection
.FvName
in self
._FvList
:
955 self
._FvList
.append(FvSection
.FvName
)
956 except AttributeError:
961 # Parse platform fixed address map files
963 # This function parses the platform final fixed address map file to get
964 # the database of predicted fixed address for module image base, entry point
967 # @param self: The object pointer
969 def _ParseMapFile(self
):
970 if self
._MapFileParsed
:
972 self
._MapFileParsed
= True
973 if os
.path
.isfile(self
._MapFileName
):
975 FileContents
= open(self
._MapFileName
).read()
976 for Match
in gMapFileItemPattern
.finditer(FileContents
):
977 AddressType
= Match
.group(1)
978 BaseAddress
= Match
.group(2)
979 EntryPoint
= Match
.group(3)
980 Guid
= Match
.group(4).upper()
981 List
= self
.FixedMapDict
.setdefault(Guid
, [])
982 List
.append((AddressType
, BaseAddress
, "*I"))
983 List
.append((AddressType
, EntryPoint
, "*E"))
985 EdkLogger
.warn(None, "Cannot open file to read", self
._MapFileName
)
988 # Invokes EOT tool to get the predicted the execution order.
990 # This function invokes EOT tool to calculate the predicted dispatch order
992 # @param self: The object pointer
994 def _InvokeEotTool(self
):
995 if self
._EotToolInvoked
:
998 self
._EotToolInvoked
= True
1000 for FvName
in self
._FvList
:
1001 FvFile
= os
.path
.join(self
._FvDir
, FvName
+ ".Fv")
1002 if os
.path
.isfile(FvFile
):
1003 FvFileList
.append(FvFile
)
1005 if len(FvFileList
) == 0:
1008 # Write source file list and GUID file list to an intermediate file
1009 # as the input for EOT tool and dispatch List as the output file
1012 SourceList
= os
.path
.join(self
._EotDir
, "SourceFile.txt")
1013 GuidList
= os
.path
.join(self
._EotDir
, "GuidList.txt")
1014 DispatchList
= os
.path
.join(self
._EotDir
, "Dispatch.txt")
1016 TempFile
= open(SourceList
, "w+")
1017 for Item
in self
._SourceList
:
1018 FileWrite(TempFile
, Item
)
1020 TempFile
= open(GuidList
, "w+")
1021 for Key
in self
._GuidMap
:
1022 FileWrite(TempFile
, "%s %s" % (Key
, self
._GuidMap
[Key
]))
1026 from Eot
.Eot
import Eot
1029 # Invoke EOT tool and echo its runtime performance
1031 EotStartTime
= time
.time()
1032 Eot(CommandLineOption
=False, SourceFileList
=SourceList
, GuidList
=GuidList
,
1033 FvFileList
=' '.join(FvFileList
), Dispatch
=DispatchList
, IsInit
=True)
1034 EotEndTime
= time
.time()
1035 EotDuration
= time
.strftime("%H:%M:%S", time
.gmtime(int(round(EotEndTime
- EotStartTime
))))
1036 EdkLogger
.quiet("EOT run time: %s\n" % EotDuration
)
1039 # Parse the output of EOT tool
1041 for Line
in open(DispatchList
):
1042 if len(Line
.split()) < 4:
1044 (Guid
, Phase
, FfsName
, FilePath
) = Line
.split()
1045 Symbol
= self
._FfsEntryPoint
.get(Guid
, [FfsName
, ""])[0]
1046 if len(Symbol
) > self
.MaxLen
:
1047 self
.MaxLen
= len(Symbol
)
1048 self
.ItemList
.append((Phase
, Symbol
, FilePath
))
1050 EdkLogger
.quiet("(Python %s on %s\n%s)" % (platform
.python_version(), sys
.platform
, traceback
.format_exc()))
1051 EdkLogger
.warn(None, "Failed to generate execution order prediction report, for some error occurred in executing EOT.")
1055 # Generate platform execution order report
1057 # This function generates the predicted module execution order.
1059 # @param self The object pointer
1060 # @param File The file object for report
1062 def _GenerateExecutionOrderReport(self
, File
):
1063 self
._InvokeEotTool
()
1064 if len(self
.ItemList
) == 0:
1066 FileWrite(File
, gSectionStart
)
1067 FileWrite(File
, "Execution Order Prediction")
1068 FileWrite(File
, "*P PEI phase")
1069 FileWrite(File
, "*D DXE phase")
1070 FileWrite(File
, "*E Module INF entry point name")
1071 FileWrite(File
, "*N Module notification function name")
1073 FileWrite(File
, "Type %-*s %s" % (self
.MaxLen
, "Symbol", "Module INF Path"))
1074 FileWrite(File
, gSectionSep
)
1075 for Item
in self
.ItemList
:
1076 FileWrite(File
, "*%sE %-*s %s" % (Item
[0], self
.MaxLen
, Item
[1], Item
[2]))
1078 FileWrite(File
, gSectionStart
)
1081 # Generate Fixed Address report.
1083 # This function generate the predicted fixed address report for a module
1084 # specified by Guid.
1086 # @param self The object pointer
1087 # @param File The file object for report
1088 # @param Guid The module Guid value.
1089 # @param NotifyList The list of all notify function in a module
1091 def _GenerateFixedAddressReport(self
, File
, Guid
, NotifyList
):
1092 self
._ParseMapFile
()
1093 FixedAddressList
= self
.FixedMapDict
.get(Guid
)
1094 if not FixedAddressList
:
1097 FileWrite(File
, gSubSectionStart
)
1098 FileWrite(File
, "Fixed Address Prediction")
1099 FileWrite(File
, "*I Image Loading Address")
1100 FileWrite(File
, "*E Entry Point Address")
1101 FileWrite(File
, "*N Notification Function Address")
1102 FileWrite(File
, "*F Flash Address")
1103 FileWrite(File
, "*M Memory Address")
1104 FileWrite(File
, "*S SMM RAM Offset")
1105 FileWrite(File
, "TOM Top of Memory")
1107 FileWrite(File
, "Type Address Name")
1108 FileWrite(File
, gSubSectionSep
)
1109 for Item
in FixedAddressList
:
1114 Name
= "(Image Base)"
1115 elif Symbol
== "*E":
1116 Name
= self
._FfsEntryPoint
.get(Guid
, ["", "_ModuleEntryPoint"])[1]
1117 elif Symbol
in NotifyList
:
1125 elif "Memory" in Type
:
1131 Value
= "TOM" + Value
1133 FileWrite(File
, "%s %-16s %s" % (Symbol
, Value
, Name
))
1136 # Generate report for the prediction part
1138 # This function generate the predicted fixed address report for a module or
1139 # predicted module execution order for a platform.
1140 # If the input Guid is None, then, it generates the predicted module execution order;
1141 # otherwise it generated the module fixed loading address for the module specified by
1144 # @param self The object pointer
1145 # @param File The file object for report
1146 # @param Guid The module Guid value.
1148 def GenerateReport(self
, File
, Guid
):
1150 self
._GenerateFixedAddressReport
(File
, Guid
.upper(), [])
1152 self
._GenerateExecutionOrderReport
(File
)
1155 # Reports FD region information
1157 # This class reports the FD subsection in the build report file.
1158 # It collects region information of platform flash device.
1159 # If the region is a firmware volume, it lists the set of modules
1160 # and its space information; otherwise, it only lists its region name,
1161 # base address and size in its sub-section header.
1162 # If there are nesting FVs, the nested FVs will list immediate after
1163 # this FD region subsection
1165 class FdRegionReport(object):
1167 # Discover all the nested FV name list.
1169 # This is an internal worker function to discover the all the nested FV information
1170 # in the parent firmware volume. It uses deep first search algorithm recursively to
1171 # find all the FV list name and append them to the list.
1173 # @param self The object pointer
1174 # @param FvName The name of current firmware file system
1175 # @param Wa Workspace context information
1177 def _DiscoverNestedFvList(self
, FvName
, Wa
):
1178 FvDictKey
=FvName
.upper()
1179 if FvDictKey
in Wa
.FdfProfile
.FvDict
:
1180 for Ffs
in Wa
.FdfProfile
.FvDict
[FvName
.upper()].FfsList
:
1181 for Section
in Ffs
.SectionList
:
1183 for FvSection
in Section
.SectionList
:
1184 if FvSection
.FvName
in self
.FvList
:
1186 self
._GuidsDb
[Ffs
.NameGuid
.upper()] = FvSection
.FvName
1187 self
.FvList
.append(FvSection
.FvName
)
1188 self
.FvInfo
[FvSection
.FvName
] = ("Nested FV", 0, 0)
1189 self
._DiscoverNestedFvList
(FvSection
.FvName
, Wa
)
1190 except AttributeError:
1194 # Constructor function for class FdRegionReport
1196 # This constructor function generates FdRegionReport object for a specified FdRegion.
1197 # If the FdRegion is a firmware volume, it will recursively find all its nested Firmware
1198 # volume list. This function also collects GUID map in order to dump module identification
1199 # in the final report.
1201 # @param self: The object pointer
1202 # @param FdRegion The current FdRegion object
1203 # @param Wa Workspace context information
1205 def __init__(self
, FdRegion
, Wa
):
1206 self
.Type
= FdRegion
.RegionType
1207 self
.BaseAddress
= FdRegion
.Offset
1208 self
.Size
= FdRegion
.Size
1212 self
._FvDir
= Wa
.FvDir
1215 # If the input FdRegion is not a firmware volume,
1218 if self
.Type
!= "FV":
1222 # Find all nested FVs in the FdRegion
1224 for FvName
in FdRegion
.RegionDataList
:
1225 if FvName
in self
.FvList
:
1227 self
.FvList
.append(FvName
)
1228 self
.FvInfo
[FvName
] = ("Fd Region", self
.BaseAddress
, self
.Size
)
1229 self
._DiscoverNestedFvList
(FvName
, Wa
)
1233 # Collect PCDs declared in DEC files.
1235 for Pa
in Wa
.AutoGenObjectList
:
1236 for Package
in Pa
.PackageList
:
1237 for (TokenCName
, TokenSpaceGuidCName
, DecType
) in Package
.Pcds
:
1238 DecDefaultValue
= Package
.Pcds
[TokenCName
, TokenSpaceGuidCName
, DecType
].DefaultValue
1239 PlatformPcds
[(TokenCName
, TokenSpaceGuidCName
)] = DecDefaultValue
1241 # Collect PCDs defined in DSC file
1243 for arch
in Wa
.ArchList
:
1244 Platform
= Wa
.BuildDatabase
[Wa
.MetaFile
, arch
]
1245 for (TokenCName
, TokenSpaceGuidCName
) in Platform
.Pcds
:
1246 DscDefaultValue
= Platform
.Pcds
[(TokenCName
, TokenSpaceGuidCName
)].DefaultValue
1247 PlatformPcds
[(TokenCName
, TokenSpaceGuidCName
)] = DscDefaultValue
1250 # Add PEI and DXE a priori files GUIDs defined in PI specification.
1252 self
._GuidsDb
["1B45CC0A-156A-428A-AF62-49864DA0E6E6"] = "PEI Apriori"
1253 self
._GuidsDb
["FC510EE7-FFDC-11D4-BD41-0080C73C8881"] = "DXE Apriori"
1255 # Add ACPI table storage file
1257 self
._GuidsDb
["7E374E25-8E01-4FEE-87F2-390C23C606CD"] = "ACPI table storage"
1259 for Pa
in Wa
.AutoGenObjectList
:
1260 for ModuleKey
in Pa
.Platform
.Modules
:
1261 M
= Pa
.Platform
.Modules
[ModuleKey
].M
1262 InfPath
= mws
.join(Wa
.WorkspaceDir
, M
.MetaFile
.File
)
1263 self
._GuidsDb
[M
.Guid
.upper()] = "%s (%s)" % (M
.Module
.BaseName
, InfPath
)
1266 # Collect the GUID map in the FV firmware volume
1268 for FvName
in self
.FvList
:
1269 FvDictKey
=FvName
.upper()
1270 if FvDictKey
in Wa
.FdfProfile
.FvDict
:
1271 for Ffs
in Wa
.FdfProfile
.FvDict
[FvName
.upper()].FfsList
:
1274 # collect GUID map for binary EFI file in FDF file.
1276 Guid
= Ffs
.NameGuid
.upper()
1277 Match
= gPcdGuidPattern
.match(Ffs
.NameGuid
)
1279 PcdTokenspace
= Match
.group(1)
1280 PcdToken
= Match
.group(2)
1281 if (PcdToken
, PcdTokenspace
) in PlatformPcds
:
1282 GuidValue
= PlatformPcds
[(PcdToken
, PcdTokenspace
)]
1283 Guid
= GuidStructureByteArrayToGuidString(GuidValue
).upper()
1284 for Section
in Ffs
.SectionList
:
1286 ModuleSectFile
= mws
.join(Wa
.WorkspaceDir
, Section
.SectFileName
)
1287 self
._GuidsDb
[Guid
] = ModuleSectFile
1288 except AttributeError:
1290 except AttributeError:
1295 # Internal worker function to generate report for the FD region
1297 # This internal worker function to generate report for the FD region.
1298 # It the type is firmware volume, it lists offset and module identification.
1300 # @param self The object pointer
1301 # @param File The file object for report
1302 # @param Title The title for the FD subsection
1303 # @param BaseAddress The base address for the FD region
1304 # @param Size The size of the FD region
1305 # @param FvName The FV name if the FD region is a firmware volume
1307 def _GenerateReport(self
, File
, Title
, Type
, BaseAddress
, Size
=0, FvName
=None):
1308 FileWrite(File
, gSubSectionStart
)
1309 FileWrite(File
, Title
)
1310 FileWrite(File
, "Type: %s" % Type
)
1311 FileWrite(File
, "Base Address: 0x%X" % BaseAddress
)
1313 if self
.Type
== "FV":
1317 FvReportFileName
= os
.path
.join(self
._FvDir
, FvName
+ ".Fv.txt")
1320 # Collect size info in the firmware volume.
1322 FvReport
= open(FvReportFileName
).read()
1323 Match
= gFvTotalSizePattern
.search(FvReport
)
1325 FvTotalSize
= int(Match
.group(1), 16)
1326 Match
= gFvTakenSizePattern
.search(FvReport
)
1328 FvTakenSize
= int(Match
.group(1), 16)
1329 FvFreeSize
= FvTotalSize
- FvTakenSize
1331 # Write size information to the report file.
1333 FileWrite(File
, "Size: 0x%X (%.0fK)" % (FvTotalSize
, FvTotalSize
/ 1024.0))
1334 FileWrite(File
, "Fv Name: %s (%.1f%% Full)" % (FvName
, FvTakenSize
* 100.0 / FvTotalSize
))
1335 FileWrite(File
, "Occupied Size: 0x%X (%.0fK)" % (FvTakenSize
, FvTakenSize
/ 1024.0))
1336 FileWrite(File
, "Free Size: 0x%X (%.0fK)" % (FvFreeSize
, FvFreeSize
/ 1024.0))
1337 FileWrite(File
, "Offset Module")
1338 FileWrite(File
, gSubSectionSep
)
1340 # Write module offset and module identification to the report file.
1343 for Match
in gOffsetGuidPattern
.finditer(FvReport
):
1344 Guid
= Match
.group(2).upper()
1345 OffsetInfo
[Match
.group(1)] = self
._GuidsDb
.get(Guid
, Guid
)
1346 OffsetList
= OffsetInfo
.keys()
1348 for Offset
in OffsetList
:
1349 FileWrite (File
, "%s %s" % (Offset
, OffsetInfo
[Offset
]))
1351 EdkLogger
.warn(None, "Fail to read report file", FvReportFileName
)
1353 FileWrite(File
, "Size: 0x%X (%.0fK)" % (Size
, Size
/ 1024.0))
1354 FileWrite(File
, gSubSectionEnd
)
1357 # Generate report for the FD region
1359 # This function generates report for the FD region.
1361 # @param self The object pointer
1362 # @param File The file object for report
1364 def GenerateReport(self
, File
):
1365 if (len(self
.FvList
) > 0):
1366 for FvItem
in self
.FvList
:
1367 Info
= self
.FvInfo
[FvItem
]
1368 self
._GenerateReport
(File
, Info
[0], "FV", Info
[1], Info
[2], FvItem
)
1370 self
._GenerateReport
(File
, "FD Region", self
.Type
, self
.BaseAddress
, self
.Size
)
1373 # Reports FD information
1375 # This class reports the FD section in the build report file.
1376 # It collects flash device information for a platform.
1378 class FdReport(object):
1380 # Constructor function for class FdReport
1382 # This constructor function generates FdReport object for a specified
1385 # @param self The object pointer
1386 # @param Fd The current Firmware device object
1387 # @param Wa Workspace context information
1389 def __init__(self
, Fd
, Wa
):
1390 self
.FdName
= Fd
.FdUiName
1391 self
.BaseAddress
= Fd
.BaseAddress
1393 self
.FdRegionList
= [FdRegionReport(FdRegion
, Wa
) for FdRegion
in Fd
.RegionList
]
1394 self
.FvPath
= os
.path
.join(Wa
.BuildDir
, "FV")
1395 self
.VpdFilePath
= os
.path
.join(self
.FvPath
, "%s.map" % Wa
.Platform
.VpdToolGuid
)
1396 self
.VPDBaseAddress
= 0
1398 self
.VPDInfoList
= []
1399 for index
, FdRegion
in enumerate(Fd
.RegionList
):
1400 if str(FdRegion
.RegionType
) is 'FILE' and Wa
.Platform
.VpdToolGuid
in str(FdRegion
.RegionDataList
):
1401 self
.VPDBaseAddress
= self
.FdRegionList
[index
].BaseAddress
1402 self
.VPDSize
= self
.FdRegionList
[index
].Size
1405 if os
.path
.isfile(self
.VpdFilePath
):
1406 fd
= open(self
.VpdFilePath
, "r")
1407 Lines
= fd
.readlines()
1410 if len(Line
) == 0 or Line
.startswith("#"):
1413 PcdName
, SkuId
, Offset
, Size
, Value
= Line
.split("#")[0].split("|")
1414 PcdName
, SkuId
, Offset
, Size
, Value
= PcdName
.strip(), SkuId
.strip(), Offset
.strip(), Size
.strip(), Value
.strip()
1415 Offset
= '0x%08X' % (int(Offset
, 16) + self
.VPDBaseAddress
)
1416 self
.VPDInfoList
.append("%s | %s | %s | %s | %s" % (PcdName
, SkuId
, Offset
, Size
, Value
))
1418 EdkLogger
.error("BuildReport", CODE_ERROR
, "Fail to parse VPD information file %s" % self
.VpdFilePath
)
1422 # Generate report for the firmware device.
1424 # This function generates report for the firmware device.
1426 # @param self The object pointer
1427 # @param File The file object for report
1429 def GenerateReport(self
, File
):
1430 FileWrite(File
, gSectionStart
)
1431 FileWrite(File
, "Firmware Device (FD)")
1432 FileWrite(File
, "FD Name: %s" % self
.FdName
)
1433 FileWrite(File
, "Base Address: %s" % self
.BaseAddress
)
1434 FileWrite(File
, "Size: 0x%X (%.0fK)" % (self
.Size
, self
.Size
/ 1024.0))
1435 if len(self
.FdRegionList
) > 0:
1436 FileWrite(File
, gSectionSep
)
1437 for FdRegionItem
in self
.FdRegionList
:
1438 FdRegionItem
.GenerateReport(File
)
1440 if len(self
.VPDInfoList
) > 0:
1441 FileWrite(File
, gSubSectionStart
)
1442 FileWrite(File
, "FD VPD Region")
1443 FileWrite(File
, "Base Address: 0x%X" % self
.VPDBaseAddress
)
1444 FileWrite(File
, "Size: 0x%X (%.0fK)" % (self
.VPDSize
, self
.VPDSize
/ 1024.0))
1445 FileWrite(File
, gSubSectionSep
)
1446 for item
in self
.VPDInfoList
:
1447 FileWrite(File
, item
)
1448 FileWrite(File
, gSubSectionEnd
)
1449 FileWrite(File
, gSectionEnd
)
1454 # Reports platform information
1456 # This class reports the whole platform information
1458 class PlatformReport(object):
1460 # Constructor function for class PlatformReport
1462 # This constructor function generates PlatformReport object a platform build.
1463 # It generates report for platform summary, flash, global PCDs and detailed
1464 # module information for modules involved in platform build.
1466 # @param self The object pointer
1467 # @param Wa Workspace context information
1468 # @param MaList The list of modules in the platform build
1470 def __init__(self
, Wa
, MaList
, ReportType
):
1471 self
._WorkspaceDir
= Wa
.WorkspaceDir
1472 self
.PlatformName
= Wa
.Name
1473 self
.PlatformDscPath
= Wa
.Platform
1474 self
.Architectures
= " ".join(Wa
.ArchList
)
1475 self
.ToolChain
= Wa
.ToolChain
1476 self
.Target
= Wa
.BuildTarget
1477 self
.OutputPath
= os
.path
.join(Wa
.WorkspaceDir
, Wa
.OutputDir
)
1478 self
.BuildEnvironment
= platform
.platform()
1480 self
.PcdReport
= None
1481 if "PCD" in ReportType
:
1482 self
.PcdReport
= PcdReport(Wa
)
1484 self
.FdReportList
= []
1485 if "FLASH" in ReportType
and Wa
.FdfProfile
and MaList
== None:
1486 for Fd
in Wa
.FdfProfile
.FdDict
:
1487 self
.FdReportList
.append(FdReport(Wa
.FdfProfile
.FdDict
[Fd
], Wa
))
1489 self
.PredictionReport
= None
1490 if "FIXED_ADDRESS" in ReportType
or "EXECUTION_ORDER" in ReportType
:
1491 self
.PredictionReport
= PredictionReport(Wa
)
1493 self
.DepexParser
= None
1494 if "DEPEX" in ReportType
:
1495 self
.DepexParser
= DepexParser(Wa
)
1497 self
.ModuleReportList
= []
1499 self
._IsModuleBuild
= True
1501 self
.ModuleReportList
.append(ModuleReport(Ma
, ReportType
))
1503 self
._IsModuleBuild
= False
1504 for Pa
in Wa
.AutoGenObjectList
:
1505 for ModuleKey
in Pa
.Platform
.Modules
:
1506 self
.ModuleReportList
.append(ModuleReport(Pa
.Platform
.Modules
[ModuleKey
].M
, ReportType
))
1511 # Generate report for the whole platform.
1513 # This function generates report for platform information.
1514 # It comprises of platform summary, global PCD, flash and
1515 # module list sections.
1517 # @param self The object pointer
1518 # @param File The file object for report
1519 # @param BuildDuration The total time to build the modules
1520 # @param ReportType The kind of report items in the final report file
1522 def GenerateReport(self
, File
, BuildDuration
, ReportType
):
1523 FileWrite(File
, "Platform Summary")
1524 FileWrite(File
, "Platform Name: %s" % self
.PlatformName
)
1525 FileWrite(File
, "Platform DSC Path: %s" % self
.PlatformDscPath
)
1526 FileWrite(File
, "Architectures: %s" % self
.Architectures
)
1527 FileWrite(File
, "Tool Chain: %s" % self
.ToolChain
)
1528 FileWrite(File
, "Target: %s" % self
.Target
)
1529 FileWrite(File
, "Output Path: %s" % self
.OutputPath
)
1530 FileWrite(File
, "Build Environment: %s" % self
.BuildEnvironment
)
1531 FileWrite(File
, "Build Duration: %s" % BuildDuration
)
1532 FileWrite(File
, "Report Content: %s" % ", ".join(ReportType
))
1534 if not self
._IsModuleBuild
:
1535 if "PCD" in ReportType
:
1536 self
.PcdReport
.GenerateReport(File
, None)
1538 if "FLASH" in ReportType
:
1539 for FdReportListItem
in self
.FdReportList
:
1540 FdReportListItem
.GenerateReport(File
)
1542 for ModuleReportItem
in self
.ModuleReportList
:
1543 ModuleReportItem
.GenerateReport(File
, self
.PcdReport
, self
.PredictionReport
, self
.DepexParser
, ReportType
)
1545 if not self
._IsModuleBuild
:
1546 if "EXECUTION_ORDER" in ReportType
:
1547 self
.PredictionReport
.GenerateReport(File
, None)
1549 ## BuildReport class
1551 # This base class contain the routines to collect data and then
1552 # applies certain format to the output report
1554 class BuildReport(object):
1556 # Constructor function for class BuildReport
1558 # This constructor function generates BuildReport object a platform build.
1559 # It generates report for platform summary, flash, global PCDs and detailed
1560 # module information for modules involved in platform build.
1562 # @param self The object pointer
1563 # @param ReportFile The file name to save report file
1564 # @param ReportType The kind of report items in the final report file
1566 def __init__(self
, ReportFile
, ReportType
):
1567 self
.ReportFile
= ReportFile
1569 self
.ReportList
= []
1570 self
.ReportType
= []
1572 for ReportTypeItem
in ReportType
:
1573 if ReportTypeItem
not in self
.ReportType
:
1574 self
.ReportType
.append(ReportTypeItem
)
1576 self
.ReportType
= ["PCD", "LIBRARY", "BUILD_FLAGS", "DEPEX", "FLASH", "FIXED_ADDRESS"]
1578 # Adds platform report to the list
1580 # This function adds a platform report to the final report list.
1582 # @param self The object pointer
1583 # @param Wa Workspace context information
1584 # @param MaList The list of modules in the platform build
1586 def AddPlatformReport(self
, Wa
, MaList
=None):
1588 self
.ReportList
.append((Wa
, MaList
))
1591 # Generates the final report.
1593 # This function generates platform build report. It invokes GenerateReport()
1594 # method for every platform report in the list.
1596 # @param self The object pointer
1597 # @param BuildDuration The total time to build the modules
1599 def GenerateReport(self
, BuildDuration
):
1603 for (Wa
, MaList
) in self
.ReportList
:
1604 PlatformReport(Wa
, MaList
, self
.ReportType
).GenerateReport(File
, BuildDuration
, self
.ReportType
)
1605 Content
= FileLinesSplit(File
.getvalue(), gLineMaxLength
)
1606 SaveFileOnChange(self
.ReportFile
, Content
, True)
1607 EdkLogger
.quiet("Build report can be found at %s" % os
.path
.abspath(self
.ReportFile
))
1609 EdkLogger
.error(None, FILE_WRITE_FAILURE
, ExtraData
=self
.ReportFile
)
1611 EdkLogger
.error("BuildReport", CODE_ERROR
, "Unknown fatal error when generating build report", ExtraData
=self
.ReportFile
, RaiseError
=False)
1612 EdkLogger
.quiet("(Python %s on %s\n%s)" % (platform
.python_version(), sys
.platform
, traceback
.format_exc()))
1615 # This acts like the main() function for the script, unless it is 'import'ed into another script.
1616 if __name__
== '__main__':