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
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 for Ffs
in Wa
.FdfProfile
.FvDict
[FvName
.upper()].FfsList
:
1179 for Section
in Ffs
.SectionList
:
1181 for FvSection
in Section
.SectionList
:
1182 if FvSection
.FvName
in self
.FvList
:
1184 self
._GuidsDb
[Ffs
.NameGuid
.upper()] = FvSection
.FvName
1185 self
.FvList
.append(FvSection
.FvName
)
1186 self
.FvInfo
[FvSection
.FvName
] = ("Nested FV", 0, 0)
1187 self
._DiscoverNestedFvList
(FvSection
.FvName
, Wa
)
1188 except AttributeError:
1192 # Constructor function for class FdRegionReport
1194 # This constructor function generates FdRegionReport object for a specified FdRegion.
1195 # If the FdRegion is a firmware volume, it will recursively find all its nested Firmware
1196 # volume list. This function also collects GUID map in order to dump module identification
1197 # in the final report.
1199 # @param self: The object pointer
1200 # @param FdRegion The current FdRegion object
1201 # @param Wa Workspace context information
1203 def __init__(self
, FdRegion
, Wa
):
1204 self
.Type
= FdRegion
.RegionType
1205 self
.BaseAddress
= FdRegion
.Offset
1206 self
.Size
= FdRegion
.Size
1210 self
._FvDir
= Wa
.FvDir
1213 # If the input FdRegion is not a firmware volume,
1216 if self
.Type
!= "FV":
1220 # Find all nested FVs in the FdRegion
1222 for FvName
in FdRegion
.RegionDataList
:
1223 if FvName
in self
.FvList
:
1225 self
.FvList
.append(FvName
)
1226 self
.FvInfo
[FvName
] = ("Fd Region", self
.BaseAddress
, self
.Size
)
1227 self
._DiscoverNestedFvList
(FvName
, Wa
)
1231 # Collect PCDs declared in DEC files.
1233 for Pa
in Wa
.AutoGenObjectList
:
1234 for Package
in Pa
.PackageList
:
1235 for (TokenCName
, TokenSpaceGuidCName
, DecType
) in Package
.Pcds
:
1236 DecDefaultValue
= Package
.Pcds
[TokenCName
, TokenSpaceGuidCName
, DecType
].DefaultValue
1237 PlatformPcds
[(TokenCName
, TokenSpaceGuidCName
)] = DecDefaultValue
1239 # Collect PCDs defined in DSC file
1241 for arch
in Wa
.ArchList
:
1242 Platform
= Wa
.BuildDatabase
[Wa
.MetaFile
, arch
]
1243 for (TokenCName
, TokenSpaceGuidCName
) in Platform
.Pcds
:
1244 DscDefaultValue
= Platform
.Pcds
[(TokenCName
, TokenSpaceGuidCName
)].DefaultValue
1245 PlatformPcds
[(TokenCName
, TokenSpaceGuidCName
)] = DscDefaultValue
1248 # Add PEI and DXE a priori files GUIDs defined in PI specification.
1250 self
._GuidsDb
["1B45CC0A-156A-428A-AF62-49864DA0E6E6"] = "PEI Apriori"
1251 self
._GuidsDb
["FC510EE7-FFDC-11D4-BD41-0080C73C8881"] = "DXE Apriori"
1253 # Add ACPI table storage file
1255 self
._GuidsDb
["7E374E25-8E01-4FEE-87F2-390C23C606CD"] = "ACPI table storage"
1257 for Pa
in Wa
.AutoGenObjectList
:
1258 for ModuleKey
in Pa
.Platform
.Modules
:
1259 M
= Pa
.Platform
.Modules
[ModuleKey
].M
1260 InfPath
= mws
.join(Wa
.WorkspaceDir
, M
.MetaFile
.File
)
1261 self
._GuidsDb
[M
.Guid
.upper()] = "%s (%s)" % (M
.Module
.BaseName
, InfPath
)
1264 # Collect the GUID map in the FV firmware volume
1266 for FvName
in self
.FvList
:
1267 for Ffs
in Wa
.FdfProfile
.FvDict
[FvName
.upper()].FfsList
:
1270 # collect GUID map for binary EFI file in FDF file.
1272 Guid
= Ffs
.NameGuid
.upper()
1273 Match
= gPcdGuidPattern
.match(Ffs
.NameGuid
)
1275 PcdTokenspace
= Match
.group(1)
1276 PcdToken
= Match
.group(2)
1277 if (PcdToken
, PcdTokenspace
) in PlatformPcds
:
1278 GuidValue
= PlatformPcds
[(PcdToken
, PcdTokenspace
)]
1279 Guid
= GuidStructureByteArrayToGuidString(GuidValue
).upper()
1280 for Section
in Ffs
.SectionList
:
1282 ModuleSectFile
= mws
.join(Wa
.WorkspaceDir
, Section
.SectFileName
)
1283 self
._GuidsDb
[Guid
] = ModuleSectFile
1284 except AttributeError:
1286 except AttributeError:
1291 # Internal worker function to generate report for the FD region
1293 # This internal worker function to generate report for the FD region.
1294 # It the type is firmware volume, it lists offset and module identification.
1296 # @param self The object pointer
1297 # @param File The file object for report
1298 # @param Title The title for the FD subsection
1299 # @param BaseAddress The base address for the FD region
1300 # @param Size The size of the FD region
1301 # @param FvName The FV name if the FD region is a firmware volume
1303 def _GenerateReport(self
, File
, Title
, Type
, BaseAddress
, Size
=0, FvName
=None):
1304 FileWrite(File
, gSubSectionStart
)
1305 FileWrite(File
, Title
)
1306 FileWrite(File
, "Type: %s" % Type
)
1307 FileWrite(File
, "Base Address: 0x%X" % BaseAddress
)
1309 if self
.Type
== "FV":
1313 FvReportFileName
= os
.path
.join(self
._FvDir
, FvName
+ ".Fv.txt")
1316 # Collect size info in the firmware volume.
1318 FvReport
= open(FvReportFileName
).read()
1319 Match
= gFvTotalSizePattern
.search(FvReport
)
1321 FvTotalSize
= int(Match
.group(1), 16)
1322 Match
= gFvTakenSizePattern
.search(FvReport
)
1324 FvTakenSize
= int(Match
.group(1), 16)
1325 FvFreeSize
= FvTotalSize
- FvTakenSize
1327 # Write size information to the report file.
1329 FileWrite(File
, "Size: 0x%X (%.0fK)" % (FvTotalSize
, FvTotalSize
/ 1024.0))
1330 FileWrite(File
, "Fv Name: %s (%.1f%% Full)" % (FvName
, FvTakenSize
* 100.0 / FvTotalSize
))
1331 FileWrite(File
, "Occupied Size: 0x%X (%.0fK)" % (FvTakenSize
, FvTakenSize
/ 1024.0))
1332 FileWrite(File
, "Free Size: 0x%X (%.0fK)" % (FvFreeSize
, FvFreeSize
/ 1024.0))
1333 FileWrite(File
, "Offset Module")
1334 FileWrite(File
, gSubSectionSep
)
1336 # Write module offset and module identification to the report file.
1339 for Match
in gOffsetGuidPattern
.finditer(FvReport
):
1340 Guid
= Match
.group(2).upper()
1341 OffsetInfo
[Match
.group(1)] = self
._GuidsDb
.get(Guid
, Guid
)
1342 OffsetList
= OffsetInfo
.keys()
1344 for Offset
in OffsetList
:
1345 FileWrite (File
, "%s %s" % (Offset
, OffsetInfo
[Offset
]))
1347 EdkLogger
.warn(None, "Fail to read report file", FvReportFileName
)
1349 FileWrite(File
, "Size: 0x%X (%.0fK)" % (Size
, Size
/ 1024.0))
1350 FileWrite(File
, gSubSectionEnd
)
1353 # Generate report for the FD region
1355 # This function generates report for the FD region.
1357 # @param self The object pointer
1358 # @param File The file object for report
1360 def GenerateReport(self
, File
):
1361 if (len(self
.FvList
) > 0):
1362 for FvItem
in self
.FvList
:
1363 Info
= self
.FvInfo
[FvItem
]
1364 self
._GenerateReport
(File
, Info
[0], "FV", Info
[1], Info
[2], FvItem
)
1366 self
._GenerateReport
(File
, "FD Region", self
.Type
, self
.BaseAddress
, self
.Size
)
1369 # Reports FD information
1371 # This class reports the FD section in the build report file.
1372 # It collects flash device information for a platform.
1374 class FdReport(object):
1376 # Constructor function for class FdReport
1378 # This constructor function generates FdReport object for a specified
1381 # @param self The object pointer
1382 # @param Fd The current Firmware device object
1383 # @param Wa Workspace context information
1385 def __init__(self
, Fd
, Wa
):
1386 self
.FdName
= Fd
.FdUiName
1387 self
.BaseAddress
= Fd
.BaseAddress
1389 self
.FdRegionList
= [FdRegionReport(FdRegion
, Wa
) for FdRegion
in Fd
.RegionList
]
1390 self
.FvPath
= os
.path
.join(Wa
.BuildDir
, "FV")
1391 self
.VpdFilePath
= os
.path
.join(self
.FvPath
, "%s.map" % Wa
.Platform
.VpdToolGuid
)
1392 self
.VPDBaseAddress
= 0
1394 self
.VPDInfoList
= []
1395 for index
, FdRegion
in enumerate(Fd
.RegionList
):
1396 if str(FdRegion
.RegionType
) is 'FILE' and Wa
.Platform
.VpdToolGuid
in str(FdRegion
.RegionDataList
):
1397 self
.VPDBaseAddress
= self
.FdRegionList
[index
].BaseAddress
1398 self
.VPDSize
= self
.FdRegionList
[index
].Size
1401 if os
.path
.isfile(self
.VpdFilePath
):
1402 fd
= open(self
.VpdFilePath
, "r")
1403 Lines
= fd
.readlines()
1406 if len(Line
) == 0 or Line
.startswith("#"):
1409 PcdName
, SkuId
, Offset
, Size
, Value
= Line
.split("#")[0].split("|")
1410 PcdName
, SkuId
, Offset
, Size
, Value
= PcdName
.strip(), SkuId
.strip(), Offset
.strip(), Size
.strip(), Value
.strip()
1411 Offset
= '0x%08X' % (int(Offset
, 16) + self
.VPDBaseAddress
)
1412 self
.VPDInfoList
.append("%s | %s | %s | %s | %s" % (PcdName
, SkuId
, Offset
, Size
, Value
))
1414 EdkLogger
.error("BuildReport", CODE_ERROR
, "Fail to parse VPD information file %s" % self
.VpdFilePath
)
1418 # Generate report for the firmware device.
1420 # This function generates report for the firmware device.
1422 # @param self The object pointer
1423 # @param File The file object for report
1425 def GenerateReport(self
, File
):
1426 FileWrite(File
, gSectionStart
)
1427 FileWrite(File
, "Firmware Device (FD)")
1428 FileWrite(File
, "FD Name: %s" % self
.FdName
)
1429 FileWrite(File
, "Base Address: %s" % self
.BaseAddress
)
1430 FileWrite(File
, "Size: 0x%X (%.0fK)" % (self
.Size
, self
.Size
/ 1024.0))
1431 if len(self
.FdRegionList
) > 0:
1432 FileWrite(File
, gSectionSep
)
1433 for FdRegionItem
in self
.FdRegionList
:
1434 FdRegionItem
.GenerateReport(File
)
1436 if len(self
.VPDInfoList
) > 0:
1437 FileWrite(File
, gSubSectionStart
)
1438 FileWrite(File
, "FD VPD Region")
1439 FileWrite(File
, "Base Address: 0x%X" % self
.VPDBaseAddress
)
1440 FileWrite(File
, "Size: 0x%X (%.0fK)" % (self
.VPDSize
, self
.VPDSize
/ 1024.0))
1441 FileWrite(File
, gSubSectionSep
)
1442 for item
in self
.VPDInfoList
:
1443 FileWrite(File
, item
)
1444 FileWrite(File
, gSubSectionEnd
)
1445 FileWrite(File
, gSectionEnd
)
1450 # Reports platform information
1452 # This class reports the whole platform information
1454 class PlatformReport(object):
1456 # Constructor function for class PlatformReport
1458 # This constructor function generates PlatformReport object a platform build.
1459 # It generates report for platform summary, flash, global PCDs and detailed
1460 # module information for modules involved in platform build.
1462 # @param self The object pointer
1463 # @param Wa Workspace context information
1464 # @param MaList The list of modules in the platform build
1466 def __init__(self
, Wa
, MaList
, ReportType
):
1467 self
._WorkspaceDir
= Wa
.WorkspaceDir
1468 self
.PlatformName
= Wa
.Name
1469 self
.PlatformDscPath
= Wa
.Platform
1470 self
.Architectures
= " ".join(Wa
.ArchList
)
1471 self
.ToolChain
= Wa
.ToolChain
1472 self
.Target
= Wa
.BuildTarget
1473 self
.OutputPath
= os
.path
.join(Wa
.WorkspaceDir
, Wa
.OutputDir
)
1474 self
.BuildEnvironment
= platform
.platform()
1476 self
.PcdReport
= None
1477 if "PCD" in ReportType
:
1478 self
.PcdReport
= PcdReport(Wa
)
1480 self
.FdReportList
= []
1481 if "FLASH" in ReportType
and Wa
.FdfProfile
and MaList
== None:
1482 for Fd
in Wa
.FdfProfile
.FdDict
:
1483 self
.FdReportList
.append(FdReport(Wa
.FdfProfile
.FdDict
[Fd
], Wa
))
1485 self
.PredictionReport
= None
1486 if "FIXED_ADDRESS" in ReportType
or "EXECUTION_ORDER" in ReportType
:
1487 self
.PredictionReport
= PredictionReport(Wa
)
1489 self
.DepexParser
= None
1490 if "DEPEX" in ReportType
:
1491 self
.DepexParser
= DepexParser(Wa
)
1493 self
.ModuleReportList
= []
1495 self
._IsModuleBuild
= True
1497 self
.ModuleReportList
.append(ModuleReport(Ma
, ReportType
))
1499 self
._IsModuleBuild
= False
1500 for Pa
in Wa
.AutoGenObjectList
:
1501 for ModuleKey
in Pa
.Platform
.Modules
:
1502 self
.ModuleReportList
.append(ModuleReport(Pa
.Platform
.Modules
[ModuleKey
].M
, ReportType
))
1507 # Generate report for the whole platform.
1509 # This function generates report for platform information.
1510 # It comprises of platform summary, global PCD, flash and
1511 # module list sections.
1513 # @param self The object pointer
1514 # @param File The file object for report
1515 # @param BuildDuration The total time to build the modules
1516 # @param ReportType The kind of report items in the final report file
1518 def GenerateReport(self
, File
, BuildDuration
, ReportType
):
1519 FileWrite(File
, "Platform Summary")
1520 FileWrite(File
, "Platform Name: %s" % self
.PlatformName
)
1521 FileWrite(File
, "Platform DSC Path: %s" % self
.PlatformDscPath
)
1522 FileWrite(File
, "Architectures: %s" % self
.Architectures
)
1523 FileWrite(File
, "Tool Chain: %s" % self
.ToolChain
)
1524 FileWrite(File
, "Target: %s" % self
.Target
)
1525 FileWrite(File
, "Output Path: %s" % self
.OutputPath
)
1526 FileWrite(File
, "Build Environment: %s" % self
.BuildEnvironment
)
1527 FileWrite(File
, "Build Duration: %s" % BuildDuration
)
1528 FileWrite(File
, "Report Content: %s" % ", ".join(ReportType
))
1530 if not self
._IsModuleBuild
:
1531 if "PCD" in ReportType
:
1532 self
.PcdReport
.GenerateReport(File
, None)
1534 if "FLASH" in ReportType
:
1535 for FdReportListItem
in self
.FdReportList
:
1536 FdReportListItem
.GenerateReport(File
)
1538 for ModuleReportItem
in self
.ModuleReportList
:
1539 ModuleReportItem
.GenerateReport(File
, self
.PcdReport
, self
.PredictionReport
, self
.DepexParser
, ReportType
)
1541 if not self
._IsModuleBuild
:
1542 if "EXECUTION_ORDER" in ReportType
:
1543 self
.PredictionReport
.GenerateReport(File
, None)
1545 ## BuildReport class
1547 # This base class contain the routines to collect data and then
1548 # applies certain format to the output report
1550 class BuildReport(object):
1552 # Constructor function for class BuildReport
1554 # This constructor function generates BuildReport object a platform build.
1555 # It generates report for platform summary, flash, global PCDs and detailed
1556 # module information for modules involved in platform build.
1558 # @param self The object pointer
1559 # @param ReportFile The file name to save report file
1560 # @param ReportType The kind of report items in the final report file
1562 def __init__(self
, ReportFile
, ReportType
):
1563 self
.ReportFile
= ReportFile
1565 self
.ReportList
= []
1566 self
.ReportType
= []
1568 for ReportTypeItem
in ReportType
:
1569 if ReportTypeItem
not in self
.ReportType
:
1570 self
.ReportType
.append(ReportTypeItem
)
1572 self
.ReportType
= ["PCD", "LIBRARY", "BUILD_FLAGS", "DEPEX", "FLASH", "FIXED_ADDRESS"]
1574 # Adds platform report to the list
1576 # This function adds a platform report to the final report list.
1578 # @param self The object pointer
1579 # @param Wa Workspace context information
1580 # @param MaList The list of modules in the platform build
1582 def AddPlatformReport(self
, Wa
, MaList
=None):
1584 self
.ReportList
.append((Wa
, MaList
))
1587 # Generates the final report.
1589 # This function generates platform build report. It invokes GenerateReport()
1590 # method for every platform report in the list.
1592 # @param self The object pointer
1593 # @param BuildDuration The total time to build the modules
1595 def GenerateReport(self
, BuildDuration
):
1599 for (Wa
, MaList
) in self
.ReportList
:
1600 PlatformReport(Wa
, MaList
, self
.ReportType
).GenerateReport(File
, BuildDuration
, self
.ReportType
)
1601 Content
= FileLinesSplit(File
.getvalue(), gLineMaxLength
)
1602 SaveFileOnChange(self
.ReportFile
, Content
, True)
1603 EdkLogger
.quiet("Build report can be found at %s" % os
.path
.abspath(self
.ReportFile
))
1605 EdkLogger
.error(None, FILE_WRITE_FAILURE
, ExtraData
=self
.ReportFile
)
1607 EdkLogger
.error("BuildReport", CODE_ERROR
, "Unknown fatal error when generating build report", ExtraData
=self
.ReportFile
, RaiseError
=False)
1608 EdkLogger
.quiet("(Python %s on %s\n%s)" % (platform
.python_version(), sys
.platform
, traceback
.format_exc()))
1611 # This acts like the main() function for the script, unless it is 'import'ed into another script.
1612 if __name__
== '__main__':