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 - 2017, 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
30 from datetime
import datetime
31 from StringIO
import StringIO
32 from Common
import EdkLogger
33 from Common
.Misc
import SaveFileOnChange
34 from Common
.Misc
import GuidStructureByteArrayToGuidString
35 from Common
.Misc
import GuidStructureStringToGuidString
36 from Common
.InfClassObject
import gComponentType2ModuleType
37 from Common
.BuildToolError
import FILE_WRITE_FAILURE
38 from Common
.BuildToolError
import CODE_ERROR
39 from Common
.BuildToolError
import COMMAND_FAILURE
40 from Common
.DataType
import TAB_LINE_BREAK
41 from Common
.DataType
import TAB_DEPEX
42 from Common
.DataType
import TAB_SLASH
43 from Common
.DataType
import TAB_SPACE_SPLIT
44 from Common
.DataType
import TAB_BRG_PCD
45 from Common
.DataType
import TAB_BRG_LIBRARY
46 from Common
.DataType
import TAB_BACK_SLASH
47 from Common
.LongFilePathSupport
import OpenLongFilePath
as open
48 from Common
.MultipleWorkspace
import MultipleWorkspace
as mws
49 import Common
.GlobalData
as GlobalData
50 from AutoGen
.AutoGen
import ModuleAutoGen
51 from Common
.Misc
import PathClass
52 from Common
.String
import NormPath
54 ## Pattern to extract contents in EDK DXS files
55 gDxsDependencyPattern
= re
.compile(r
"DEPENDENCY_START(.+)DEPENDENCY_END", re
.DOTALL
)
57 ## Pattern to find total FV total size, occupied size in flash report intermediate file
58 gFvTotalSizePattern
= re
.compile(r
"EFI_FV_TOTAL_SIZE = (0x[0-9a-fA-F]+)")
59 gFvTakenSizePattern
= re
.compile(r
"EFI_FV_TAKEN_SIZE = (0x[0-9a-fA-F]+)")
61 ## Pattern to find module size and time stamp in module summary report intermediate file
62 gModuleSizePattern
= re
.compile(r
"MODULE_SIZE = (\d+)")
63 gTimeStampPattern
= re
.compile(r
"TIME_STAMP = (\d+)")
65 ## Pattern to find GUID value in flash description files
66 gPcdGuidPattern
= re
.compile(r
"PCD\((\w+)[.](\w+)\)")
68 ## Pattern to collect offset, GUID value pair in the flash report intermediate file
69 gOffsetGuidPattern
= re
.compile(r
"(0x[0-9A-Fa-f]+) ([-A-Fa-f0-9]+)")
71 ## Pattern to find module base address and entry point in fixed flash map file
72 gModulePattern
= r
"\n[-\w]+\s*\(([^,]+),\s*BaseAddress=%(Address)s,\s*EntryPoint=%(Address)s\)\s*\(GUID=([-0-9A-Fa-f]+)[^)]*\)"
73 gMapFileItemPattern
= re
.compile(gModulePattern
% {"Address" : "(-?0[xX][0-9A-Fa-f]+)"})
75 ## Pattern to find all module referenced header files in source files
76 gIncludePattern
= re
.compile(r
'#include\s*["<]([^">]+)[">]')
77 gIncludePattern2
= re
.compile(r
"#include\s+EFI_([A-Z_]+)\s*[(]\s*(\w+)\s*[)]")
79 ## Pattern to find the entry point for EDK module using EDKII Glue library
80 gGlueLibEntryPoint
= re
.compile(r
"__EDKII_GLUE_MODULE_ENTRY_POINT__\s*=\s*(\w+)")
82 ## Tags for MaxLength of line in report
85 ## Tags for end of line in report
88 ## Tags for section start, end and separator
89 gSectionStart
= ">" + "=" * (gLineMaxLength
- 2) + "<"
90 gSectionEnd
= "<" + "=" * (gLineMaxLength
- 2) + ">" + "\n"
91 gSectionSep
= "=" * gLineMaxLength
93 ## Tags for subsection start, end and separator
94 gSubSectionStart
= ">" + "-" * (gLineMaxLength
- 2) + "<"
95 gSubSectionEnd
= "<" + "-" * (gLineMaxLength
- 2) + ">"
96 gSubSectionSep
= "-" * gLineMaxLength
99 ## The look up table to map PCD type to pair of report display type and DEC type
101 'FixedAtBuild' : ('FIXED', 'FixedAtBuild'),
102 'PatchableInModule': ('PATCH', 'PatchableInModule'),
103 'FeatureFlag' : ('FLAG', 'FeatureFlag'),
104 'Dynamic' : ('DYN', 'Dynamic'),
105 'DynamicHii' : ('DYNHII', 'Dynamic'),
106 'DynamicVpd' : ('DYNVPD', 'Dynamic'),
107 'DynamicEx' : ('DEX', 'DynamicEx'),
108 'DynamicExHii' : ('DEXHII', 'DynamicEx'),
109 'DynamicExVpd' : ('DEXVPD', 'DynamicEx'),
112 ## The look up table to map module type to driver type
114 'SEC' : '0x3 (SECURITY_CORE)',
115 'PEI_CORE' : '0x4 (PEI_CORE)',
116 'PEIM' : '0x6 (PEIM)',
117 'DXE_CORE' : '0x5 (DXE_CORE)',
118 'DXE_DRIVER' : '0x7 (DRIVER)',
119 'DXE_SAL_DRIVER' : '0x7 (DRIVER)',
120 'DXE_SMM_DRIVER' : '0x7 (DRIVER)',
121 'DXE_RUNTIME_DRIVER': '0x7 (DRIVER)',
122 'UEFI_DRIVER' : '0x7 (DRIVER)',
123 'UEFI_APPLICATION' : '0x9 (APPLICATION)',
124 'SMM_CORE' : '0xD (SMM_CORE)',
125 'SMM_DRIVER' : '0xA (SMM)', # Extension of module type to support PI 1.1 SMM drivers
126 'MM_STANDALONE' : '0xE (MM_STANDALONE)',
127 'MM_CORE_STANDALONE' : '0xF (MM_CORE_STANDALONE)'
130 ## The look up table of the supported opcode in the dependency expression binaries
131 gOpCodeList
= ["BEFORE", "AFTER", "PUSH", "AND", "OR", "NOT", "TRUE", "FALSE", "END", "SOR"]
134 # Writes a string to the file object.
136 # This function writes a string to the file object and a new line is appended
137 # afterwards. It may optionally wraps the string for better readability.
139 # @File The file object to write
140 # @String The string to be written to the file
141 # @Wrapper Indicates whether to wrap the string
143 def FileWrite(File
, String
, Wrapper
=False):
145 String
= textwrap
.fill(String
, 120)
146 File
.write(String
+ gEndOfLine
)
149 # Find all the header file that the module source directly includes.
151 # This function scans source code to find all header files the module may
152 # include. This is not accurate but very effective to find all the header
153 # file the module might include with #include statement.
155 # @Source The source file name
156 # @IncludePathList The list of include path to find the source file.
157 # @IncludeFiles The dictionary of current found include files.
159 def FindIncludeFiles(Source
, IncludePathList
, IncludeFiles
):
160 FileContents
= open(Source
).read()
162 # Find header files with pattern #include "XXX.h" or #include <XXX.h>
164 for Match
in gIncludePattern
.finditer(FileContents
):
165 FileName
= Match
.group(1).strip()
166 for Dir
in [os
.path
.dirname(Source
)] + IncludePathList
:
167 FullFileName
= os
.path
.normpath(os
.path
.join(Dir
, FileName
))
168 if os
.path
.exists(FullFileName
):
169 IncludeFiles
[FullFileName
.lower().replace("\\", "/")] = FullFileName
173 # Find header files with pattern like #include EFI_PPI_CONSUMER(XXX)
175 for Match
in gIncludePattern2
.finditer(FileContents
):
177 Type
= Match
.group(1)
178 if "ARCH_PROTOCOL" in Type
:
179 FileName
= "ArchProtocol/%(Key)s/%(Key)s.h" % {"Key" : Key
}
180 elif "PROTOCOL" in Type
:
181 FileName
= "Protocol/%(Key)s/%(Key)s.h" % {"Key" : Key
}
183 FileName
= "Ppi/%(Key)s/%(Key)s.h" % {"Key" : Key
}
185 FileName
= "Guid/%(Key)s/%(Key)s.h" % {"Key" : Key
}
188 for Dir
in IncludePathList
:
189 FullFileName
= os
.path
.normpath(os
.path
.join(Dir
, FileName
))
190 if os
.path
.exists(FullFileName
):
191 IncludeFiles
[FullFileName
.lower().replace("\\", "/")] = FullFileName
194 ## Split each lines in file
196 # This method is used to split the lines in file to make the length of each line
197 # less than MaxLength.
199 # @param Content The content of file
200 # @param MaxLength The Max Length of the line
202 def FileLinesSplit(Content
=None, MaxLength
=None):
203 ContentList
= Content
.split(TAB_LINE_BREAK
)
206 for Line
in ContentList
:
207 while len(Line
.rstrip()) > MaxLength
:
208 LineSpaceIndex
= Line
.rfind(TAB_SPACE_SPLIT
, 0, MaxLength
)
209 LineSlashIndex
= Line
.rfind(TAB_SLASH
, 0, MaxLength
)
210 LineBackSlashIndex
= Line
.rfind(TAB_BACK_SLASH
, 0, MaxLength
)
211 if max(LineSpaceIndex
, LineSlashIndex
, LineBackSlashIndex
) > 0:
212 LineBreakIndex
= max(LineSpaceIndex
, LineSlashIndex
, LineBackSlashIndex
)
214 LineBreakIndex
= MaxLength
215 NewContentList
.append(Line
[:LineBreakIndex
])
216 Line
= Line
[LineBreakIndex
:]
218 NewContentList
.append(Line
)
219 for NewLine
in NewContentList
:
220 NewContent
+= NewLine
+ TAB_LINE_BREAK
222 NewContent
= NewContent
.replace(TAB_LINE_BREAK
, gEndOfLine
).replace('\r\r\n', gEndOfLine
)
228 # Parse binary dependency expression section
230 # This utility class parses the dependency expression section and translate the readable
231 # GUID name and value.
233 class DepexParser(object):
235 # Constructor function for class DepexParser
237 # This constructor function collect GUID values so that the readable
238 # GUID name can be translated.
240 # @param self The object pointer
241 # @param Wa Workspace context information
243 def __init__(self
, Wa
):
245 for Pa
in Wa
.AutoGenObjectList
:
246 for Package
in Pa
.PackageList
:
247 for Protocol
in Package
.Protocols
:
248 GuidValue
= GuidStructureStringToGuidString(Package
.Protocols
[Protocol
])
249 self
._GuidDb
[GuidValue
.upper()] = Protocol
250 for Ppi
in Package
.Ppis
:
251 GuidValue
= GuidStructureStringToGuidString(Package
.Ppis
[Ppi
])
252 self
._GuidDb
[GuidValue
.upper()] = Ppi
253 for Guid
in Package
.Guids
:
254 GuidValue
= GuidStructureStringToGuidString(Package
.Guids
[Guid
])
255 self
._GuidDb
[GuidValue
.upper()] = Guid
258 # Parse the binary dependency expression files.
260 # This function parses the binary dependency expression file and translate it
261 # to the instruction list.
263 # @param self The object pointer
264 # @param DepexFileName The file name of binary dependency expression file.
266 def ParseDepexFile(self
, DepexFileName
):
267 DepexFile
= open(DepexFileName
, "rb")
269 OpCode
= DepexFile
.read(1)
271 Statement
= gOpCodeList
[struct
.unpack("B", OpCode
)[0]]
272 if Statement
in ["BEFORE", "AFTER", "PUSH"]:
273 GuidValue
= "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X" % \
274 struct
.unpack("=LHHBBBBBBBB", DepexFile
.read(16))
275 GuidString
= self
._GuidDb
.get(GuidValue
, GuidValue
)
276 Statement
= "%s %s" % (Statement
, GuidString
)
277 DepexStatement
.append(Statement
)
278 OpCode
= DepexFile
.read(1)
280 return DepexStatement
283 # Reports library information
285 # This class reports the module library subsection in the build report file.
287 class LibraryReport(object):
289 # Constructor function for class LibraryReport
291 # This constructor function generates LibraryReport object for
294 # @param self The object pointer
295 # @param M Module context information
297 def __init__(self
, M
):
298 self
.LibraryList
= []
299 if int(str(M
.AutoGenVersion
), 0) >= 0x00010005:
300 self
._EdkIIModule
= True
302 self
._EdkIIModule
= False
304 for Lib
in M
.DependentLibraryList
:
305 LibInfPath
= str(Lib
)
306 LibClassList
= Lib
.LibraryClass
[0].LibraryClass
307 LibConstructorList
= Lib
.ConstructorList
308 LibDesstructorList
= Lib
.DestructorList
309 LibDepexList
= Lib
.DepexExpression
[M
.Arch
, M
.ModuleType
]
310 self
.LibraryList
.append((LibInfPath
, LibClassList
, LibConstructorList
, LibDesstructorList
, LibDepexList
))
313 # Generate report for module library information
315 # This function generates report for the module library.
316 # If the module is EDKII style one, the additional library class, library
317 # constructor/destructor and dependency expression may also be reported.
319 # @param self The object pointer
320 # @param File The file object for report
322 def GenerateReport(self
, File
):
323 if len(self
.LibraryList
) > 0:
324 FileWrite(File
, gSubSectionStart
)
325 FileWrite(File
, TAB_BRG_LIBRARY
)
326 FileWrite(File
, gSubSectionSep
)
327 for LibraryItem
in self
.LibraryList
:
328 LibInfPath
= LibraryItem
[0]
329 FileWrite(File
, LibInfPath
)
332 # Report library class, library constructor and destructor for
333 # EDKII style module.
335 if self
._EdkIIModule
:
336 LibClass
= LibraryItem
[1]
338 LibConstructor
= " ".join(LibraryItem
[2])
340 EdkIILibInfo
+= " C = " + LibConstructor
341 LibDestructor
= " ".join(LibraryItem
[3])
343 EdkIILibInfo
+= " D = " + LibDestructor
344 LibDepex
= " ".join(LibraryItem
[4])
346 EdkIILibInfo
+= " Depex = " + LibDepex
348 FileWrite(File
, "{%s: %s}" % (LibClass
, EdkIILibInfo
))
350 FileWrite(File
, "{%s}" % LibClass
)
352 FileWrite(File
, gSubSectionEnd
)
355 # Reports dependency expression information
357 # This class reports the module dependency expression subsection in the build report file.
359 class DepexReport(object):
361 # Constructor function for class DepexReport
363 # This constructor function generates DepexReport object for
364 # a module. If the module source contains the DXS file (usually EDK
365 # style module), it uses the dependency in DXS file; otherwise,
366 # it uses the dependency expression from its own INF [Depex] section
367 # and then merges with the ones from its dependent library INF.
369 # @param self The object pointer
370 # @param M Module context information
372 def __init__(self
, M
):
374 self
._DepexFileName
= os
.path
.join(M
.BuildDir
, "OUTPUT", M
.Module
.BaseName
+ ".depex")
375 ModuleType
= M
.ModuleType
377 ModuleType
= gComponentType2ModuleType
.get(M
.ComponentType
, "")
379 if ModuleType
in ["SEC", "PEI_CORE", "DXE_CORE", "SMM_CORE", "MM_CORE_STANDALONE", "UEFI_APPLICATION"]:
382 for Source
in M
.SourceFileList
:
383 if os
.path
.splitext(Source
.Path
)[1].lower() == ".dxs":
384 Match
= gDxsDependencyPattern
.search(open(Source
.Path
).read())
386 self
.Depex
= Match
.group(1).strip()
390 self
.Depex
= M
.DepexExpressionList
.get(M
.ModuleType
, "")
391 self
.ModuleDepex
= " ".join(M
.Module
.DepexExpression
[M
.Arch
, M
.ModuleType
])
392 if not self
.ModuleDepex
:
393 self
.ModuleDepex
= "(None)"
396 for Lib
in M
.DependentLibraryList
:
397 LibDepex
= " ".join(Lib
.DepexExpression
[M
.Arch
, M
.ModuleType
]).strip()
399 LibDepexList
.append("(" + LibDepex
+ ")")
400 self
.LibraryDepex
= " AND ".join(LibDepexList
)
401 if not self
.LibraryDepex
:
402 self
.LibraryDepex
= "(None)"
406 # Generate report for module dependency expression information
408 # This function generates report for the module dependency expression.
410 # @param self The object pointer
411 # @param File The file object for report
412 # @param GlobalDepexParser The platform global Dependency expression parser object
414 def GenerateReport(self
, File
, GlobalDepexParser
):
417 FileWrite(File
, gSubSectionStart
)
418 if os
.path
.isfile(self
._DepexFileName
):
420 DepexStatements
= GlobalDepexParser
.ParseDepexFile(self
._DepexFileName
)
421 FileWrite(File
, "Final Dependency Expression (DEPEX) Instructions")
422 for DepexStatement
in DepexStatements
:
423 FileWrite(File
, " %s" % DepexStatement
)
424 FileWrite(File
, gSubSectionSep
)
426 EdkLogger
.warn(None, "Dependency expression file is corrupted", self
._DepexFileName
)
428 FileWrite(File
, "Dependency Expression (DEPEX) from %s" % self
.Source
)
430 if self
.Source
== "INF":
431 FileWrite(File
, "%s" % self
.Depex
, True)
432 FileWrite(File
, gSubSectionSep
)
433 FileWrite(File
, "From Module INF: %s" % self
.ModuleDepex
, True)
434 FileWrite(File
, "From Library INF: %s" % self
.LibraryDepex
, True)
436 FileWrite(File
, "%s" % self
.Depex
)
437 FileWrite(File
, gSubSectionEnd
)
440 # Reports dependency expression information
442 # This class reports the module build flags subsection in the build report file.
444 class BuildFlagsReport(object):
446 # Constructor function for class BuildFlagsReport
448 # This constructor function generates BuildFlagsReport object for
449 # a module. It reports the build tool chain tag and all relevant
450 # build flags to build the module.
452 # @param self The object pointer
453 # @param M Module context information
455 def __init__(self
, M
):
458 # Add build flags according to source file extension so that
459 # irrelevant ones can be filtered out.
461 for Source
in M
.SourceFileList
:
462 Ext
= os
.path
.splitext(Source
.File
)[1].lower()
463 if Ext
in [".c", ".cc", ".cpp"]:
464 BuildOptions
["CC"] = 1
465 elif Ext
in [".s", ".asm"]:
466 BuildOptions
["PP"] = 1
467 BuildOptions
["ASM"] = 1
468 elif Ext
in [".vfr"]:
469 BuildOptions
["VFRPP"] = 1
470 BuildOptions
["VFR"] = 1
471 elif Ext
in [".dxs"]:
472 BuildOptions
["APP"] = 1
473 BuildOptions
["CC"] = 1
474 elif Ext
in [".asl"]:
475 BuildOptions
["ASLPP"] = 1
476 BuildOptions
["ASL"] = 1
477 elif Ext
in [".aslc"]:
478 BuildOptions
["ASLCC"] = 1
479 BuildOptions
["ASLDLINK"] = 1
480 BuildOptions
["CC"] = 1
481 elif Ext
in [".asm16"]:
482 BuildOptions
["ASMLINK"] = 1
483 BuildOptions
["SLINK"] = 1
484 BuildOptions
["DLINK"] = 1
487 # Save module build flags.
489 self
.ToolChainTag
= M
.ToolChain
491 for Tool
in BuildOptions
:
492 self
.BuildFlags
[Tool
+ "_FLAGS"] = M
.BuildOption
.get(Tool
, {}).get("FLAGS", "")
495 # Generate report for module build flags information
497 # This function generates report for the module build flags expression.
499 # @param self The object pointer
500 # @param File The file object for report
502 def GenerateReport(self
, File
):
503 FileWrite(File
, gSubSectionStart
)
504 FileWrite(File
, "Build Flags")
505 FileWrite(File
, "Tool Chain Tag: %s" % self
.ToolChainTag
)
506 for Tool
in self
.BuildFlags
:
507 FileWrite(File
, gSubSectionSep
)
508 FileWrite(File
, "%s = %s" % (Tool
, self
.BuildFlags
[Tool
]), True)
510 FileWrite(File
, gSubSectionEnd
)
514 # Reports individual module information
516 # This class reports the module section in the build report file.
517 # It comprises of module summary, module PCD, library, dependency expression,
518 # build flags sections.
520 class ModuleReport(object):
522 # Constructor function for class ModuleReport
524 # This constructor function generates ModuleReport object for
525 # a separate module in a platform build.
527 # @param self The object pointer
528 # @param M Module context information
529 # @param ReportType The kind of report items in the final report file
531 def __init__(self
, M
, ReportType
):
532 self
.ModuleName
= M
.Module
.BaseName
533 self
.ModuleInfPath
= M
.MetaFile
.File
534 self
.FileGuid
= M
.Guid
536 self
.BuildTimeStamp
= None
540 ModuleType
= M
.ModuleType
542 ModuleType
= gComponentType2ModuleType
.get(M
.ComponentType
, "")
544 # If a module complies to PI 1.1, promote Module type to "SMM_DRIVER"
546 if ModuleType
== "DXE_SMM_DRIVER":
547 PiSpec
= M
.Module
.Specification
.get("PI_SPECIFICATION_VERSION", "0x00010000")
548 if int(PiSpec
, 0) >= 0x0001000A:
549 ModuleType
= "SMM_DRIVER"
550 self
.DriverType
= gDriverTypeMap
.get(ModuleType
, "0x2 (FREE_FORM)")
551 self
.UefiSpecVersion
= M
.Module
.Specification
.get("UEFI_SPECIFICATION_VERSION", "")
552 self
.PiSpecVersion
= M
.Module
.Specification
.get("PI_SPECIFICATION_VERSION", "")
553 self
.PciDeviceId
= M
.Module
.Defines
.get("PCI_DEVICE_ID", "")
554 self
.PciVendorId
= M
.Module
.Defines
.get("PCI_VENDOR_ID", "")
555 self
.PciClassCode
= M
.Module
.Defines
.get("PCI_CLASS_CODE", "")
557 self
._BuildDir
= M
.BuildDir
558 self
.ModulePcdSet
= {}
559 if "PCD" in ReportType
:
561 # Collect all module used PCD set: module INF referenced directly or indirectly.
562 # It also saves module INF default values of them in case they exist.
564 for Pcd
in M
.ModulePcdList
+ M
.LibraryPcdList
:
565 self
.ModulePcdSet
.setdefault((Pcd
.TokenCName
, Pcd
.TokenSpaceGuidCName
, Pcd
.Type
), (Pcd
.InfDefaultValue
, Pcd
.DefaultValue
))
567 self
.LibraryReport
= None
568 if "LIBRARY" in ReportType
:
569 self
.LibraryReport
= LibraryReport(M
)
571 self
.DepexReport
= None
572 if "DEPEX" in ReportType
:
573 self
.DepexReport
= DepexReport(M
)
575 if "BUILD_FLAGS" in ReportType
:
576 self
.BuildFlagsReport
= BuildFlagsReport(M
)
580 # Generate report for module information
582 # This function generates report for separate module expression
583 # in a platform build.
585 # @param self The object pointer
586 # @param File The file object for report
587 # @param GlobalPcdReport The platform global PCD report object
588 # @param GlobalPredictionReport The platform global Prediction report object
589 # @param GlobalDepexParser The platform global Dependency expression parser object
590 # @param ReportType The kind of report items in the final report file
592 def GenerateReport(self
, File
, GlobalPcdReport
, GlobalPredictionReport
, GlobalDepexParser
, ReportType
):
593 FileWrite(File
, gSectionStart
)
595 FwReportFileName
= os
.path
.join(self
._BuildDir
, "DEBUG", self
.ModuleName
+ ".txt")
596 if os
.path
.isfile(FwReportFileName
):
598 FileContents
= open(FwReportFileName
).read()
599 Match
= gModuleSizePattern
.search(FileContents
)
601 self
.Size
= int(Match
.group(1))
603 Match
= gTimeStampPattern
.search(FileContents
)
605 self
.BuildTimeStamp
= datetime
.fromtimestamp(int(Match
.group(1)))
607 EdkLogger
.warn(None, "Fail to read report file", FwReportFileName
)
609 if "HASH" in ReportType
:
610 OutputDir
= os
.path
.join(self
._BuildDir
, "OUTPUT")
611 DefaultEFIfile
= os
.path
.join(OutputDir
, self
.ModuleName
+ ".efi")
612 if os
.path
.isfile(DefaultEFIfile
):
613 Tempfile
= os
.path
.join(OutputDir
, self
.ModuleName
+ "_hash.tmp")
614 # rebase the efi image since its base address may not zero
615 cmd
= ["GenFw", "--rebase", str(0), "-o", Tempfile
, DefaultEFIfile
]
617 PopenObject
= subprocess
.Popen(' '.join(cmd
), stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
, shell
=True)
619 EdkLogger
.error("GenFw", COMMAND_FAILURE
, ExtraData
="%s: %s" % (str(X
), cmd
[0]))
620 EndOfProcedure
= threading
.Event()
621 EndOfProcedure
.clear()
622 if PopenObject
.stderr
:
623 StdErrThread
= threading
.Thread(target
=ReadMessage
, args
=(PopenObject
.stderr
, EdkLogger
.quiet
, EndOfProcedure
))
624 StdErrThread
.setName("STDERR-Redirector")
625 StdErrThread
.setDaemon(False)
627 # waiting for program exit
629 if PopenObject
.stderr
:
631 if PopenObject
.returncode
!= 0:
632 EdkLogger
.error("GenFw", COMMAND_FAILURE
, "Failed to generate firmware hash image for %s" % (DefaultEFIfile
))
633 if os
.path
.isfile(Tempfile
):
634 self
.Hash
= hashlib
.sha1()
635 buf
= open(Tempfile
, 'rb').read()
636 if self
.Hash
.update(buf
):
637 self
.Hash
= self
.Hash
.update(buf
)
638 self
.Hash
= self
.Hash
.hexdigest()
641 FileWrite(File
, "Module Summary")
642 FileWrite(File
, "Module Name: %s" % self
.ModuleName
)
643 FileWrite(File
, "Module INF Path: %s" % self
.ModuleInfPath
)
644 FileWrite(File
, "File GUID: %s" % self
.FileGuid
)
646 FileWrite(File
, "Size: 0x%X (%.2fK)" % (self
.Size
, self
.Size
/ 1024.0))
648 FileWrite(File
, "SHA1 HASH: %s *%s" % (self
.Hash
, self
.ModuleName
+ ".efi"))
649 if self
.BuildTimeStamp
:
650 FileWrite(File
, "Build Time Stamp: %s" % self
.BuildTimeStamp
)
652 FileWrite(File
, "Driver Type: %s" % self
.DriverType
)
653 if self
.UefiSpecVersion
:
654 FileWrite(File
, "UEFI Spec Version: %s" % self
.UefiSpecVersion
)
655 if self
.PiSpecVersion
:
656 FileWrite(File
, "PI Spec Version: %s" % self
.PiSpecVersion
)
658 FileWrite(File
, "PCI Device ID: %s" % self
.PciDeviceId
)
660 FileWrite(File
, "PCI Vendor ID: %s" % self
.PciVendorId
)
661 if self
.PciClassCode
:
662 FileWrite(File
, "PCI Class Code: %s" % self
.PciClassCode
)
664 FileWrite(File
, gSectionSep
)
666 if "PCD" in ReportType
:
667 GlobalPcdReport
.GenerateReport(File
, self
.ModulePcdSet
)
669 if "LIBRARY" in ReportType
:
670 self
.LibraryReport
.GenerateReport(File
)
672 if "DEPEX" in ReportType
:
673 self
.DepexReport
.GenerateReport(File
, GlobalDepexParser
)
675 if "BUILD_FLAGS" in ReportType
:
676 self
.BuildFlagsReport
.GenerateReport(File
)
678 if "FIXED_ADDRESS" in ReportType
and self
.FileGuid
:
679 GlobalPredictionReport
.GenerateReport(File
, self
.FileGuid
)
681 FileWrite(File
, gSectionEnd
)
683 def ReadMessage(From
, To
, ExitFlag
):
685 # read one line a time
686 Line
= From
.readline()
687 # empty string means "end"
688 if Line
!= None and Line
!= "":
696 # Reports platform and module PCD information
698 # This class reports the platform PCD section and module PCD subsection
699 # in the build report file.
701 class PcdReport(object):
703 # Constructor function for class PcdReport
705 # This constructor function generates PcdReport object a platform build.
706 # It collects the whole PCD database from platform DSC files, platform
707 # flash description file and package DEC files.
709 # @param self The object pointer
710 # @param Wa Workspace context information
712 def __init__(self
, Wa
):
715 self
.ConditionalPcds
= {}
718 self
.FdfPcdSet
= Wa
.FdfProfile
.PcdDict
722 self
.ModulePcdOverride
= {}
723 for Pa
in Wa
.AutoGenObjectList
:
725 # Collect all platform referenced PCDs and grouped them by PCD token space
728 for Pcd
in Pa
.AllPcdList
:
729 PcdList
= self
.AllPcds
.setdefault(Pcd
.TokenSpaceGuidCName
, {}).setdefault(Pcd
.Type
, [])
730 if Pcd
not in PcdList
:
732 if len(Pcd
.TokenCName
) > self
.MaxLen
:
733 self
.MaxLen
= len(Pcd
.TokenCName
)
735 # Collect the PCD defined in DSC/FDF file, but not used in module
737 UnusedPcdFullList
= []
738 for item
in Pa
.Platform
.Pcds
:
739 Pcd
= Pa
.Platform
.Pcds
[item
]
742 for package
in Pa
.PackageList
:
743 for T
in ["FixedAtBuild", "PatchableInModule", "FeatureFlag", "Dynamic", "DynamicEx"]:
744 if (Pcd
.TokenCName
, Pcd
.TokenSpaceGuidCName
, T
) in package
.Pcds
:
747 if not Pcd
.DatumType
:
748 Pcd
.DatumType
= package
.Pcds
[(Pcd
.TokenCName
, Pcd
.TokenSpaceGuidCName
, T
)].DatumType
752 if not Pcd
.DatumType
:
754 # Try to remove Hii and Vpd suffix
755 if PcdType
.startswith("DynamicEx"):
756 PcdType
= "DynamicEx"
757 elif PcdType
.startswith("Dynamic"):
759 for package
in Pa
.PackageList
:
760 if (Pcd
.TokenCName
, Pcd
.TokenSpaceGuidCName
, PcdType
) in package
.Pcds
:
761 Pcd
.DatumType
= package
.Pcds
[(Pcd
.TokenCName
, Pcd
.TokenSpaceGuidCName
, PcdType
)].DatumType
764 PcdList
= self
.AllPcds
.setdefault(Pcd
.TokenSpaceGuidCName
, {}).setdefault(Pcd
.Type
, [])
765 if Pcd
not in PcdList
and Pcd
not in UnusedPcdFullList
:
766 UnusedPcdFullList
.append(Pcd
)
767 if len(Pcd
.TokenCName
) > self
.MaxLen
:
768 self
.MaxLen
= len(Pcd
.TokenCName
)
770 if GlobalData
.gConditionalPcds
:
771 for PcdItem
in GlobalData
.gConditionalPcds
:
773 (TokenSpaceGuidCName
, TokenCName
) = PcdItem
.split('.')
774 if (TokenCName
, TokenSpaceGuidCName
) in Pa
.Platform
.Pcds
.keys():
775 Pcd
= Pa
.Platform
.Pcds
[(TokenCName
, TokenSpaceGuidCName
)]
776 PcdList
= self
.ConditionalPcds
.setdefault(Pcd
.TokenSpaceGuidCName
, {}).setdefault(Pcd
.Type
, [])
777 if Pcd
not in PcdList
:
781 if UnusedPcdFullList
:
782 for Pcd
in UnusedPcdFullList
:
783 if Pcd
.TokenSpaceGuidCName
+ '.' + Pcd
.TokenCName
in GlobalData
.gConditionalPcds
:
785 UnusedPcdList
.append(Pcd
)
787 for Pcd
in UnusedPcdList
:
788 PcdList
= self
.UnusedPcds
.setdefault(Pcd
.TokenSpaceGuidCName
, {}).setdefault(Pcd
.Type
, [])
789 if Pcd
not in PcdList
:
792 for Module
in Pa
.Platform
.Modules
.values():
794 # Collect module override PCDs
796 for ModulePcd
in Module
.M
.ModulePcdList
+ Module
.M
.LibraryPcdList
:
797 TokenCName
= ModulePcd
.TokenCName
798 TokenSpaceGuid
= ModulePcd
.TokenSpaceGuidCName
799 ModuleDefault
= ModulePcd
.DefaultValue
800 ModulePath
= os
.path
.basename(Module
.M
.MetaFile
.File
)
801 self
.ModulePcdOverride
.setdefault((TokenCName
, TokenSpaceGuid
), {})[ModulePath
] = ModuleDefault
805 # Collect PCD DEC default value.
807 self
.DecPcdDefault
= {}
808 for Pa
in Wa
.AutoGenObjectList
:
809 for Package
in Pa
.PackageList
:
810 for (TokenCName
, TokenSpaceGuidCName
, DecType
) in Package
.Pcds
:
811 DecDefaultValue
= Package
.Pcds
[TokenCName
, TokenSpaceGuidCName
, DecType
].DefaultValue
812 self
.DecPcdDefault
.setdefault((TokenCName
, TokenSpaceGuidCName
, DecType
), DecDefaultValue
)
814 # Collect PCDs defined in DSC common section
816 self
.DscPcdDefault
= {}
817 for Arch
in Wa
.ArchList
:
818 Platform
= Wa
.BuildDatabase
[Wa
.MetaFile
, Arch
, Wa
.BuildTarget
, Wa
.ToolChain
]
819 for (TokenCName
, TokenSpaceGuidCName
) in Platform
.Pcds
:
820 DscDefaultValue
= Platform
.Pcds
[(TokenCName
, TokenSpaceGuidCName
)].DefaultValue
822 self
.DscPcdDefault
[(TokenCName
, TokenSpaceGuidCName
)] = DscDefaultValue
824 def GenerateReport(self
, File
, ModulePcdSet
):
825 if self
.ConditionalPcds
:
826 self
.GenerateReportDetail(File
, ModulePcdSet
, 1)
828 self
.GenerateReportDetail(File
, ModulePcdSet
, 2)
829 self
.GenerateReportDetail(File
, ModulePcdSet
)
832 # Generate report for PCD information
834 # This function generates report for separate module expression
835 # in a platform build.
837 # @param self The object pointer
838 # @param File The file object for report
839 # @param ModulePcdSet Set of all PCDs referenced by module or None for
840 # platform PCD report
841 # @param ReportySubType 0 means platform/module PCD report, 1 means Conditional
842 # directives section report, 2 means Unused Pcds section report
843 # @param DscOverridePcds Module DSC override PCDs set
845 def GenerateReportDetail(self
, File
, ModulePcdSet
, ReportSubType
= 0):
846 PcdDict
= self
.AllPcds
847 if ReportSubType
== 1:
848 PcdDict
= self
.ConditionalPcds
849 elif ReportSubType
== 2:
850 PcdDict
= self
.UnusedPcds
852 if ModulePcdSet
== None:
853 FileWrite(File
, gSectionStart
)
854 if ReportSubType
== 1:
855 FileWrite(File
, "Conditional Directives used by the build system")
856 elif ReportSubType
== 2:
857 FileWrite(File
, "PCDs not used by modules or in conditional directives")
859 FileWrite(File
, "Platform Configuration Database Report")
861 FileWrite(File
, " *B - PCD override in the build option")
862 FileWrite(File
, " *P - Platform scoped PCD override in DSC file")
863 FileWrite(File
, " *F - Platform scoped PCD override in FDF file")
864 if not ReportSubType
:
865 FileWrite(File
, " *M - Module scoped PCD override")
866 FileWrite(File
, gSectionSep
)
868 if not ReportSubType
and ModulePcdSet
:
870 # For module PCD sub-section
872 FileWrite(File
, gSubSectionStart
)
873 FileWrite(File
, TAB_BRG_PCD
)
874 FileWrite(File
, gSubSectionSep
)
878 # Group PCD by their token space GUID C Name
881 for Type
in PcdDict
[Key
]:
883 # Group PCD by their usage type
885 TypeName
, DecType
= gPcdTypeMap
.get(Type
, ("", Type
))
886 for Pcd
in PcdDict
[Key
][Type
]:
887 PcdTokenCName
= Pcd
.TokenCName
889 if GlobalData
.MixedPcd
:
890 for PcdKey
in GlobalData
.MixedPcd
:
891 if (Pcd
.TokenCName
, Pcd
.TokenSpaceGuidCName
) in GlobalData
.MixedPcd
[PcdKey
]:
892 PcdTokenCName
= PcdKey
[0]
894 if MixedPcdFlag
and not ModulePcdSet
:
897 # Get PCD default value and their override relationship
899 DecDefaultValue
= self
.DecPcdDefault
.get((Pcd
.TokenCName
, Pcd
.TokenSpaceGuidCName
, DecType
))
900 DscDefaultValue
= self
.DscPcdDefault
.get((Pcd
.TokenCName
, Pcd
.TokenSpaceGuidCName
))
901 DscDefaultValue
= self
.FdfPcdSet
.get((Pcd
.TokenCName
, Key
), DscDefaultValue
)
902 InfDefaultValue
= None
904 PcdValue
= DecDefaultValue
906 PcdValue
= DscDefaultValue
907 if ModulePcdSet
!= None:
908 if (Pcd
.TokenCName
, Pcd
.TokenSpaceGuidCName
, Type
) not in ModulePcdSet
:
910 InfDefault
, PcdValue
= ModulePcdSet
[Pcd
.TokenCName
, Pcd
.TokenSpaceGuidCName
, Type
]
914 BuildOptionMatch
= False
915 if GlobalData
.BuildOptionPcd
:
916 for pcd
in GlobalData
.BuildOptionPcd
:
917 if (Pcd
.TokenSpaceGuidCName
, Pcd
.TokenCName
) == (pcd
[0], pcd
[1]):
919 BuildOptionMatch
= True
923 if ModulePcdSet
== None:
929 if Pcd
.DatumType
in ('UINT8', 'UINT16', 'UINT32', 'UINT64'):
930 PcdValueNumber
= int(PcdValue
.strip(), 0)
931 if DecDefaultValue
== None:
934 DecDefaultValueNumber
= int(DecDefaultValue
.strip(), 0)
935 DecMatch
= (DecDefaultValueNumber
== PcdValueNumber
)
937 if InfDefaultValue
== None:
940 InfDefaultValueNumber
= int(InfDefaultValue
.strip(), 0)
941 InfMatch
= (InfDefaultValueNumber
== PcdValueNumber
)
943 if DscDefaultValue
== None:
946 DscDefaultValueNumber
= int(DscDefaultValue
.strip(), 0)
947 DscMatch
= (DscDefaultValueNumber
== PcdValueNumber
)
949 if DecDefaultValue
== None:
952 DecMatch
= (DecDefaultValue
.strip() == PcdValue
.strip())
954 if InfDefaultValue
== None:
957 InfMatch
= (InfDefaultValue
.strip() == PcdValue
.strip())
959 if DscDefaultValue
== None:
962 DscMatch
= (DscDefaultValue
.strip() == PcdValue
.strip())
965 # Report PCD item according to their override relationship
968 FileWrite(File
, ' *B %-*s: %6s %10s = %-22s' % (self
.MaxLen
, PcdTokenCName
, TypeName
, '(' + Pcd
.DatumType
+ ')', PcdValue
.strip()))
969 elif DecMatch
and InfMatch
:
970 FileWrite(File
, ' %-*s: %6s %10s = %-22s' % (self
.MaxLen
, PcdTokenCName
, TypeName
, '(' + Pcd
.DatumType
+ ')', PcdValue
.strip()))
973 if (Pcd
.TokenCName
, Key
) in self
.FdfPcdSet
:
974 FileWrite(File
, ' *F %-*s: %6s %10s = %-22s' % (self
.MaxLen
, PcdTokenCName
, TypeName
, '(' + Pcd
.DatumType
+ ')', PcdValue
.strip()))
976 FileWrite(File
, ' *P %-*s: %6s %10s = %-22s' % (self
.MaxLen
, PcdTokenCName
, TypeName
, '(' + Pcd
.DatumType
+ ')', PcdValue
.strip()))
978 FileWrite(File
, ' *M %-*s: %6s %10s = %-22s' % (self
.MaxLen
, PcdTokenCName
, TypeName
, '(' + Pcd
.DatumType
+ ')', PcdValue
.strip()))
980 if TypeName
in ('DYNHII', 'DEXHII', 'DYNVPD', 'DEXVPD'):
981 for SkuInfo
in Pcd
.SkuInfoList
.values():
982 if TypeName
in ('DYNHII', 'DEXHII'):
983 FileWrite(File
, '%*s: %s: %s' % (self
.MaxLen
+ 4, SkuInfo
.VariableGuid
, SkuInfo
.VariableName
, SkuInfo
.VariableOffset
))
985 FileWrite(File
, '%*s' % (self
.MaxLen
+ 4, SkuInfo
.VpdOffset
))
987 if not DscMatch
and DscDefaultValue
!= None:
988 FileWrite(File
, ' %*s = %s' % (self
.MaxLen
+ 19, 'DSC DEFAULT', DscDefaultValue
.strip()))
990 if not InfMatch
and InfDefaultValue
!= None:
991 FileWrite(File
, ' %*s = %s' % (self
.MaxLen
+ 19, 'INF DEFAULT', InfDefaultValue
.strip()))
993 if not DecMatch
and DecDefaultValue
!= None:
994 FileWrite(File
, ' %*s = %s' % (self
.MaxLen
+ 19, 'DEC DEFAULT', DecDefaultValue
.strip()))
996 if ModulePcdSet
== None:
997 if not BuildOptionMatch
:
998 ModuleOverride
= self
.ModulePcdOverride
.get((Pcd
.TokenCName
, Pcd
.TokenSpaceGuidCName
), {})
999 for ModulePath
in ModuleOverride
:
1000 ModuleDefault
= ModuleOverride
[ModulePath
]
1001 if Pcd
.DatumType
in ('UINT8', 'UINT16', 'UINT32', 'UINT64'):
1002 ModulePcdDefaultValueNumber
= int(ModuleDefault
.strip(), 0)
1003 Match
= (ModulePcdDefaultValueNumber
== PcdValueNumber
)
1005 Match
= (ModuleDefault
.strip() == PcdValue
.strip())
1008 FileWrite(File
, ' *M %-*s = %s' % (self
.MaxLen
+ 19, ModulePath
, ModuleDefault
.strip()))
1010 if ModulePcdSet
== None:
1011 FileWrite(File
, gSectionEnd
)
1013 if not ReportSubType
and ModulePcdSet
:
1014 FileWrite(File
, gSubSectionEnd
)
1019 # Reports platform and module Prediction information
1021 # This class reports the platform execution order prediction section and
1022 # module load fixed address prediction subsection in the build report file.
1024 class PredictionReport(object):
1026 # Constructor function for class PredictionReport
1028 # This constructor function generates PredictionReport object for the platform.
1030 # @param self: The object pointer
1031 # @param Wa Workspace context information
1033 def __init__(self
, Wa
):
1034 self
._MapFileName
= os
.path
.join(Wa
.BuildDir
, Wa
.Name
+ ".map")
1035 self
._MapFileParsed
= False
1036 self
._EotToolInvoked
= False
1037 self
._FvDir
= Wa
.FvDir
1038 self
._EotDir
= Wa
.BuildDir
1039 self
._FfsEntryPoint
= {}
1041 self
._SourceList
= []
1042 self
.FixedMapDict
= {}
1047 # Collect all platform reference source files and GUID C Name
1049 for Pa
in Wa
.AutoGenObjectList
:
1050 for Module
in Pa
.LibraryAutoGenList
+ Pa
.ModuleAutoGenList
:
1052 # BASE typed modules are EFI agnostic, so we need not scan
1053 # their source code to find PPI/Protocol produce or consume
1056 if Module
.ModuleType
== "BASE":
1059 # Add module referenced source files
1061 self
._SourceList
.append(str(Module
))
1063 for Source
in Module
.SourceFileList
:
1064 if os
.path
.splitext(str(Source
))[1].lower() == ".c":
1065 self
._SourceList
.append(" " + str(Source
))
1066 FindIncludeFiles(Source
.Path
, Module
.IncludePathList
, IncludeList
)
1067 for IncludeFile
in IncludeList
.values():
1068 self
._SourceList
.append(" " + IncludeFile
)
1070 for Guid
in Module
.PpiList
:
1071 self
._GuidMap
[Guid
] = GuidStructureStringToGuidString(Module
.PpiList
[Guid
])
1072 for Guid
in Module
.ProtocolList
:
1073 self
._GuidMap
[Guid
] = GuidStructureStringToGuidString(Module
.ProtocolList
[Guid
])
1074 for Guid
in Module
.GuidList
:
1075 self
._GuidMap
[Guid
] = GuidStructureStringToGuidString(Module
.GuidList
[Guid
])
1077 if Module
.Guid
and not Module
.IsLibrary
:
1078 EntryPoint
= " ".join(Module
.Module
.ModuleEntryPointList
)
1079 if int(str(Module
.AutoGenVersion
), 0) >= 0x00010005:
1080 RealEntryPoint
= "_ModuleEntryPoint"
1082 RealEntryPoint
= EntryPoint
1083 if EntryPoint
== "_ModuleEntryPoint":
1084 CCFlags
= Module
.BuildOption
.get("CC", {}).get("FLAGS", "")
1085 Match
= gGlueLibEntryPoint
.search(CCFlags
)
1087 EntryPoint
= Match
.group(1)
1089 self
._FfsEntryPoint
[Module
.Guid
.upper()] = (EntryPoint
, RealEntryPoint
)
1093 # Collect platform firmware volume list as the input of EOT.
1097 for Fd
in Wa
.FdfProfile
.FdDict
:
1098 for FdRegion
in Wa
.FdfProfile
.FdDict
[Fd
].RegionList
:
1099 if FdRegion
.RegionType
!= "FV":
1101 for FvName
in FdRegion
.RegionDataList
:
1102 if FvName
in self
._FvList
:
1104 self
._FvList
.append(FvName
)
1105 for Ffs
in Wa
.FdfProfile
.FvDict
[FvName
.upper()].FfsList
:
1106 for Section
in Ffs
.SectionList
:
1108 for FvSection
in Section
.SectionList
:
1109 if FvSection
.FvName
in self
._FvList
:
1111 self
._FvList
.append(FvSection
.FvName
)
1112 except AttributeError:
1117 # Parse platform fixed address map files
1119 # This function parses the platform final fixed address map file to get
1120 # the database of predicted fixed address for module image base, entry point
1123 # @param self: The object pointer
1125 def _ParseMapFile(self
):
1126 if self
._MapFileParsed
:
1128 self
._MapFileParsed
= True
1129 if os
.path
.isfile(self
._MapFileName
):
1131 FileContents
= open(self
._MapFileName
).read()
1132 for Match
in gMapFileItemPattern
.finditer(FileContents
):
1133 AddressType
= Match
.group(1)
1134 BaseAddress
= Match
.group(2)
1135 EntryPoint
= Match
.group(3)
1136 Guid
= Match
.group(4).upper()
1137 List
= self
.FixedMapDict
.setdefault(Guid
, [])
1138 List
.append((AddressType
, BaseAddress
, "*I"))
1139 List
.append((AddressType
, EntryPoint
, "*E"))
1141 EdkLogger
.warn(None, "Cannot open file to read", self
._MapFileName
)
1144 # Invokes EOT tool to get the predicted the execution order.
1146 # This function invokes EOT tool to calculate the predicted dispatch order
1148 # @param self: The object pointer
1150 def _InvokeEotTool(self
):
1151 if self
._EotToolInvoked
:
1154 self
._EotToolInvoked
= True
1156 for FvName
in self
._FvList
:
1157 FvFile
= os
.path
.join(self
._FvDir
, FvName
+ ".Fv")
1158 if os
.path
.isfile(FvFile
):
1159 FvFileList
.append(FvFile
)
1161 if len(FvFileList
) == 0:
1164 # Write source file list and GUID file list to an intermediate file
1165 # as the input for EOT tool and dispatch List as the output file
1168 SourceList
= os
.path
.join(self
._EotDir
, "SourceFile.txt")
1169 GuidList
= os
.path
.join(self
._EotDir
, "GuidList.txt")
1170 DispatchList
= os
.path
.join(self
._EotDir
, "Dispatch.txt")
1172 TempFile
= open(SourceList
, "w+")
1173 for Item
in self
._SourceList
:
1174 FileWrite(TempFile
, Item
)
1176 TempFile
= open(GuidList
, "w+")
1177 for Key
in self
._GuidMap
:
1178 FileWrite(TempFile
, "%s %s" % (Key
, self
._GuidMap
[Key
]))
1182 from Eot
.Eot
import Eot
1185 # Invoke EOT tool and echo its runtime performance
1187 EotStartTime
= time
.time()
1188 Eot(CommandLineOption
=False, SourceFileList
=SourceList
, GuidList
=GuidList
,
1189 FvFileList
=' '.join(FvFileList
), Dispatch
=DispatchList
, IsInit
=True)
1190 EotEndTime
= time
.time()
1191 EotDuration
= time
.strftime("%H:%M:%S", time
.gmtime(int(round(EotEndTime
- EotStartTime
))))
1192 EdkLogger
.quiet("EOT run time: %s\n" % EotDuration
)
1195 # Parse the output of EOT tool
1197 for Line
in open(DispatchList
):
1198 if len(Line
.split()) < 4:
1200 (Guid
, Phase
, FfsName
, FilePath
) = Line
.split()
1201 Symbol
= self
._FfsEntryPoint
.get(Guid
, [FfsName
, ""])[0]
1202 if len(Symbol
) > self
.MaxLen
:
1203 self
.MaxLen
= len(Symbol
)
1204 self
.ItemList
.append((Phase
, Symbol
, FilePath
))
1206 EdkLogger
.quiet("(Python %s on %s\n%s)" % (platform
.python_version(), sys
.platform
, traceback
.format_exc()))
1207 EdkLogger
.warn(None, "Failed to generate execution order prediction report, for some error occurred in executing EOT.")
1211 # Generate platform execution order report
1213 # This function generates the predicted module execution order.
1215 # @param self The object pointer
1216 # @param File The file object for report
1218 def _GenerateExecutionOrderReport(self
, File
):
1219 self
._InvokeEotTool
()
1220 if len(self
.ItemList
) == 0:
1222 FileWrite(File
, gSectionStart
)
1223 FileWrite(File
, "Execution Order Prediction")
1224 FileWrite(File
, "*P PEI phase")
1225 FileWrite(File
, "*D DXE phase")
1226 FileWrite(File
, "*E Module INF entry point name")
1227 FileWrite(File
, "*N Module notification function name")
1229 FileWrite(File
, "Type %-*s %s" % (self
.MaxLen
, "Symbol", "Module INF Path"))
1230 FileWrite(File
, gSectionSep
)
1231 for Item
in self
.ItemList
:
1232 FileWrite(File
, "*%sE %-*s %s" % (Item
[0], self
.MaxLen
, Item
[1], Item
[2]))
1234 FileWrite(File
, gSectionStart
)
1237 # Generate Fixed Address report.
1239 # This function generate the predicted fixed address report for a module
1240 # specified by Guid.
1242 # @param self The object pointer
1243 # @param File The file object for report
1244 # @param Guid The module Guid value.
1245 # @param NotifyList The list of all notify function in a module
1247 def _GenerateFixedAddressReport(self
, File
, Guid
, NotifyList
):
1248 self
._ParseMapFile
()
1249 FixedAddressList
= self
.FixedMapDict
.get(Guid
)
1250 if not FixedAddressList
:
1253 FileWrite(File
, gSubSectionStart
)
1254 FileWrite(File
, "Fixed Address Prediction")
1255 FileWrite(File
, "*I Image Loading Address")
1256 FileWrite(File
, "*E Entry Point Address")
1257 FileWrite(File
, "*N Notification Function Address")
1258 FileWrite(File
, "*F Flash Address")
1259 FileWrite(File
, "*M Memory Address")
1260 FileWrite(File
, "*S SMM RAM Offset")
1261 FileWrite(File
, "TOM Top of Memory")
1263 FileWrite(File
, "Type Address Name")
1264 FileWrite(File
, gSubSectionSep
)
1265 for Item
in FixedAddressList
:
1270 Name
= "(Image Base)"
1271 elif Symbol
== "*E":
1272 Name
= self
._FfsEntryPoint
.get(Guid
, ["", "_ModuleEntryPoint"])[1]
1273 elif Symbol
in NotifyList
:
1281 elif "Memory" in Type
:
1287 Value
= "TOM" + Value
1289 FileWrite(File
, "%s %-16s %s" % (Symbol
, Value
, Name
))
1292 # Generate report for the prediction part
1294 # This function generate the predicted fixed address report for a module or
1295 # predicted module execution order for a platform.
1296 # If the input Guid is None, then, it generates the predicted module execution order;
1297 # otherwise it generated the module fixed loading address for the module specified by
1300 # @param self The object pointer
1301 # @param File The file object for report
1302 # @param Guid The module Guid value.
1304 def GenerateReport(self
, File
, Guid
):
1306 self
._GenerateFixedAddressReport
(File
, Guid
.upper(), [])
1308 self
._GenerateExecutionOrderReport
(File
)
1311 # Reports FD region information
1313 # This class reports the FD subsection in the build report file.
1314 # It collects region information of platform flash device.
1315 # If the region is a firmware volume, it lists the set of modules
1316 # and its space information; otherwise, it only lists its region name,
1317 # base address and size in its sub-section header.
1318 # If there are nesting FVs, the nested FVs will list immediate after
1319 # this FD region subsection
1321 class FdRegionReport(object):
1323 # Discover all the nested FV name list.
1325 # This is an internal worker function to discover the all the nested FV information
1326 # in the parent firmware volume. It uses deep first search algorithm recursively to
1327 # find all the FV list name and append them to the list.
1329 # @param self The object pointer
1330 # @param FvName The name of current firmware file system
1331 # @param Wa Workspace context information
1333 def _DiscoverNestedFvList(self
, FvName
, Wa
):
1334 FvDictKey
=FvName
.upper()
1335 if FvDictKey
in Wa
.FdfProfile
.FvDict
:
1336 for Ffs
in Wa
.FdfProfile
.FvDict
[FvName
.upper()].FfsList
:
1337 for Section
in Ffs
.SectionList
:
1339 for FvSection
in Section
.SectionList
:
1340 if FvSection
.FvName
in self
.FvList
:
1342 self
._GuidsDb
[Ffs
.NameGuid
.upper()] = FvSection
.FvName
1343 self
.FvList
.append(FvSection
.FvName
)
1344 self
.FvInfo
[FvSection
.FvName
] = ("Nested FV", 0, 0)
1345 self
._DiscoverNestedFvList
(FvSection
.FvName
, Wa
)
1346 except AttributeError:
1350 # Constructor function for class FdRegionReport
1352 # This constructor function generates FdRegionReport object for a specified FdRegion.
1353 # If the FdRegion is a firmware volume, it will recursively find all its nested Firmware
1354 # volume list. This function also collects GUID map in order to dump module identification
1355 # in the final report.
1357 # @param self: The object pointer
1358 # @param FdRegion The current FdRegion object
1359 # @param Wa Workspace context information
1361 def __init__(self
, FdRegion
, Wa
):
1362 self
.Type
= FdRegion
.RegionType
1363 self
.BaseAddress
= FdRegion
.Offset
1364 self
.Size
= FdRegion
.Size
1368 self
._FvDir
= Wa
.FvDir
1371 # If the input FdRegion is not a firmware volume,
1374 if self
.Type
!= "FV":
1378 # Find all nested FVs in the FdRegion
1380 for FvName
in FdRegion
.RegionDataList
:
1381 if FvName
in self
.FvList
:
1383 self
.FvList
.append(FvName
)
1384 self
.FvInfo
[FvName
] = ("Fd Region", self
.BaseAddress
, self
.Size
)
1385 self
._DiscoverNestedFvList
(FvName
, Wa
)
1389 # Collect PCDs declared in DEC files.
1391 for Pa
in Wa
.AutoGenObjectList
:
1392 for Package
in Pa
.PackageList
:
1393 for (TokenCName
, TokenSpaceGuidCName
, DecType
) in Package
.Pcds
:
1394 DecDefaultValue
= Package
.Pcds
[TokenCName
, TokenSpaceGuidCName
, DecType
].DefaultValue
1395 PlatformPcds
[(TokenCName
, TokenSpaceGuidCName
)] = DecDefaultValue
1397 # Collect PCDs defined in DSC file
1399 for arch
in Wa
.ArchList
:
1400 Platform
= Wa
.BuildDatabase
[Wa
.MetaFile
, arch
]
1401 for (TokenCName
, TokenSpaceGuidCName
) in Platform
.Pcds
:
1402 DscDefaultValue
= Platform
.Pcds
[(TokenCName
, TokenSpaceGuidCName
)].DefaultValue
1403 PlatformPcds
[(TokenCName
, TokenSpaceGuidCName
)] = DscDefaultValue
1406 # Add PEI and DXE a priori files GUIDs defined in PI specification.
1408 self
._GuidsDb
["1B45CC0A-156A-428A-AF62-49864DA0E6E6"] = "PEI Apriori"
1409 self
._GuidsDb
["FC510EE7-FFDC-11D4-BD41-0080C73C8881"] = "DXE Apriori"
1411 # Add ACPI table storage file
1413 self
._GuidsDb
["7E374E25-8E01-4FEE-87F2-390C23C606CD"] = "ACPI table storage"
1415 for Pa
in Wa
.AutoGenObjectList
:
1416 for ModuleKey
in Pa
.Platform
.Modules
:
1417 M
= Pa
.Platform
.Modules
[ModuleKey
].M
1418 InfPath
= mws
.join(Wa
.WorkspaceDir
, M
.MetaFile
.File
)
1419 self
._GuidsDb
[M
.Guid
.upper()] = "%s (%s)" % (M
.Module
.BaseName
, InfPath
)
1422 # Collect the GUID map in the FV firmware volume
1424 for FvName
in self
.FvList
:
1425 FvDictKey
=FvName
.upper()
1426 if FvDictKey
in Wa
.FdfProfile
.FvDict
:
1427 for Ffs
in Wa
.FdfProfile
.FvDict
[FvName
.upper()].FfsList
:
1430 # collect GUID map for binary EFI file in FDF file.
1432 Guid
= Ffs
.NameGuid
.upper()
1433 Match
= gPcdGuidPattern
.match(Ffs
.NameGuid
)
1435 PcdTokenspace
= Match
.group(1)
1436 PcdToken
= Match
.group(2)
1437 if (PcdToken
, PcdTokenspace
) in PlatformPcds
:
1438 GuidValue
= PlatformPcds
[(PcdToken
, PcdTokenspace
)]
1439 Guid
= GuidStructureByteArrayToGuidString(GuidValue
).upper()
1440 for Section
in Ffs
.SectionList
:
1442 ModuleSectFile
= mws
.join(Wa
.WorkspaceDir
, Section
.SectFileName
)
1443 self
._GuidsDb
[Guid
] = ModuleSectFile
1444 except AttributeError:
1446 except AttributeError:
1451 # Internal worker function to generate report for the FD region
1453 # This internal worker function to generate report for the FD region.
1454 # It the type is firmware volume, it lists offset and module identification.
1456 # @param self The object pointer
1457 # @param File The file object for report
1458 # @param Title The title for the FD subsection
1459 # @param BaseAddress The base address for the FD region
1460 # @param Size The size of the FD region
1461 # @param FvName The FV name if the FD region is a firmware volume
1463 def _GenerateReport(self
, File
, Title
, Type
, BaseAddress
, Size
=0, FvName
=None):
1464 FileWrite(File
, gSubSectionStart
)
1465 FileWrite(File
, Title
)
1466 FileWrite(File
, "Type: %s" % Type
)
1467 FileWrite(File
, "Base Address: 0x%X" % BaseAddress
)
1469 if self
.Type
== "FV":
1473 FvReportFileName
= os
.path
.join(self
._FvDir
, FvName
+ ".Fv.txt")
1476 # Collect size info in the firmware volume.
1478 FvReport
= open(FvReportFileName
).read()
1479 Match
= gFvTotalSizePattern
.search(FvReport
)
1481 FvTotalSize
= int(Match
.group(1), 16)
1482 Match
= gFvTakenSizePattern
.search(FvReport
)
1484 FvTakenSize
= int(Match
.group(1), 16)
1485 FvFreeSize
= FvTotalSize
- FvTakenSize
1487 # Write size information to the report file.
1489 FileWrite(File
, "Size: 0x%X (%.0fK)" % (FvTotalSize
, FvTotalSize
/ 1024.0))
1490 FileWrite(File
, "Fv Name: %s (%.1f%% Full)" % (FvName
, FvTakenSize
* 100.0 / FvTotalSize
))
1491 FileWrite(File
, "Occupied Size: 0x%X (%.0fK)" % (FvTakenSize
, FvTakenSize
/ 1024.0))
1492 FileWrite(File
, "Free Size: 0x%X (%.0fK)" % (FvFreeSize
, FvFreeSize
/ 1024.0))
1493 FileWrite(File
, "Offset Module")
1494 FileWrite(File
, gSubSectionSep
)
1496 # Write module offset and module identification to the report file.
1499 for Match
in gOffsetGuidPattern
.finditer(FvReport
):
1500 Guid
= Match
.group(2).upper()
1501 OffsetInfo
[Match
.group(1)] = self
._GuidsDb
.get(Guid
, Guid
)
1502 OffsetList
= OffsetInfo
.keys()
1504 for Offset
in OffsetList
:
1505 FileWrite (File
, "%s %s" % (Offset
, OffsetInfo
[Offset
]))
1507 EdkLogger
.warn(None, "Fail to read report file", FvReportFileName
)
1509 FileWrite(File
, "Size: 0x%X (%.0fK)" % (Size
, Size
/ 1024.0))
1510 FileWrite(File
, gSubSectionEnd
)
1513 # Generate report for the FD region
1515 # This function generates report for the FD region.
1517 # @param self The object pointer
1518 # @param File The file object for report
1520 def GenerateReport(self
, File
):
1521 if (len(self
.FvList
) > 0):
1522 for FvItem
in self
.FvList
:
1523 Info
= self
.FvInfo
[FvItem
]
1524 self
._GenerateReport
(File
, Info
[0], "FV", Info
[1], Info
[2], FvItem
)
1526 self
._GenerateReport
(File
, "FD Region", self
.Type
, self
.BaseAddress
, self
.Size
)
1529 # Reports FD information
1531 # This class reports the FD section in the build report file.
1532 # It collects flash device information for a platform.
1534 class FdReport(object):
1536 # Constructor function for class FdReport
1538 # This constructor function generates FdReport object for a specified
1541 # @param self The object pointer
1542 # @param Fd The current Firmware device object
1543 # @param Wa Workspace context information
1545 def __init__(self
, Fd
, Wa
):
1546 self
.FdName
= Fd
.FdUiName
1547 self
.BaseAddress
= Fd
.BaseAddress
1549 self
.FdRegionList
= [FdRegionReport(FdRegion
, Wa
) for FdRegion
in Fd
.RegionList
]
1550 self
.FvPath
= os
.path
.join(Wa
.BuildDir
, "FV")
1551 self
.VpdFilePath
= os
.path
.join(self
.FvPath
, "%s.map" % Wa
.Platform
.VpdToolGuid
)
1552 self
.VPDBaseAddress
= 0
1554 self
.VPDInfoList
= []
1555 for index
, FdRegion
in enumerate(Fd
.RegionList
):
1556 if str(FdRegion
.RegionType
) is 'FILE' and Wa
.Platform
.VpdToolGuid
in str(FdRegion
.RegionDataList
):
1557 self
.VPDBaseAddress
= self
.FdRegionList
[index
].BaseAddress
1558 self
.VPDSize
= self
.FdRegionList
[index
].Size
1561 if os
.path
.isfile(self
.VpdFilePath
):
1562 fd
= open(self
.VpdFilePath
, "r")
1563 Lines
= fd
.readlines()
1566 if len(Line
) == 0 or Line
.startswith("#"):
1569 PcdName
, SkuId
, Offset
, Size
, Value
= Line
.split("#")[0].split("|")
1570 PcdName
, SkuId
, Offset
, Size
, Value
= PcdName
.strip(), SkuId
.strip(), Offset
.strip(), Size
.strip(), Value
.strip()
1571 if Offset
.lower().startswith('0x'):
1572 Offset
= '0x%08X' % (int(Offset
, 16) + self
.VPDBaseAddress
)
1574 Offset
= '0x%08X' % (int(Offset
, 10) + self
.VPDBaseAddress
)
1575 self
.VPDInfoList
.append("%s | %s | %s | %s | %s" % (PcdName
, SkuId
, Offset
, Size
, Value
))
1577 EdkLogger
.error("BuildReport", CODE_ERROR
, "Fail to parse VPD information file %s" % self
.VpdFilePath
)
1581 # Generate report for the firmware device.
1583 # This function generates report for the firmware device.
1585 # @param self The object pointer
1586 # @param File The file object for report
1588 def GenerateReport(self
, File
):
1589 FileWrite(File
, gSectionStart
)
1590 FileWrite(File
, "Firmware Device (FD)")
1591 FileWrite(File
, "FD Name: %s" % self
.FdName
)
1592 FileWrite(File
, "Base Address: %s" % self
.BaseAddress
)
1593 FileWrite(File
, "Size: 0x%X (%.0fK)" % (self
.Size
, self
.Size
/ 1024.0))
1594 if len(self
.FdRegionList
) > 0:
1595 FileWrite(File
, gSectionSep
)
1596 for FdRegionItem
in self
.FdRegionList
:
1597 FdRegionItem
.GenerateReport(File
)
1599 if len(self
.VPDInfoList
) > 0:
1600 FileWrite(File
, gSubSectionStart
)
1601 FileWrite(File
, "FD VPD Region")
1602 FileWrite(File
, "Base Address: 0x%X" % self
.VPDBaseAddress
)
1603 FileWrite(File
, "Size: 0x%X (%.0fK)" % (self
.VPDSize
, self
.VPDSize
/ 1024.0))
1604 FileWrite(File
, gSubSectionSep
)
1605 for item
in self
.VPDInfoList
:
1606 FileWrite(File
, item
)
1607 FileWrite(File
, gSubSectionEnd
)
1608 FileWrite(File
, gSectionEnd
)
1613 # Reports platform information
1615 # This class reports the whole platform information
1617 class PlatformReport(object):
1619 # Constructor function for class PlatformReport
1621 # This constructor function generates PlatformReport object a platform build.
1622 # It generates report for platform summary, flash, global PCDs and detailed
1623 # module information for modules involved in platform build.
1625 # @param self The object pointer
1626 # @param Wa Workspace context information
1627 # @param MaList The list of modules in the platform build
1629 def __init__(self
, Wa
, MaList
, ReportType
):
1630 self
._WorkspaceDir
= Wa
.WorkspaceDir
1631 self
.PlatformName
= Wa
.Name
1632 self
.PlatformDscPath
= Wa
.Platform
1633 self
.Architectures
= " ".join(Wa
.ArchList
)
1634 self
.ToolChain
= Wa
.ToolChain
1635 self
.Target
= Wa
.BuildTarget
1636 self
.OutputPath
= os
.path
.join(Wa
.WorkspaceDir
, Wa
.OutputDir
)
1637 self
.BuildEnvironment
= platform
.platform()
1639 self
.PcdReport
= None
1640 if "PCD" in ReportType
:
1641 self
.PcdReport
= PcdReport(Wa
)
1643 self
.FdReportList
= []
1644 if "FLASH" in ReportType
and Wa
.FdfProfile
and MaList
== None:
1645 for Fd
in Wa
.FdfProfile
.FdDict
:
1646 self
.FdReportList
.append(FdReport(Wa
.FdfProfile
.FdDict
[Fd
], Wa
))
1648 self
.PredictionReport
= None
1649 if "FIXED_ADDRESS" in ReportType
or "EXECUTION_ORDER" in ReportType
:
1650 self
.PredictionReport
= PredictionReport(Wa
)
1652 self
.DepexParser
= None
1653 if "DEPEX" in ReportType
:
1654 self
.DepexParser
= DepexParser(Wa
)
1656 self
.ModuleReportList
= []
1658 self
._IsModuleBuild
= True
1660 self
.ModuleReportList
.append(ModuleReport(Ma
, ReportType
))
1662 self
._IsModuleBuild
= False
1663 for Pa
in Wa
.AutoGenObjectList
:
1664 ModuleAutoGenList
= []
1665 for ModuleKey
in Pa
.Platform
.Modules
:
1666 ModuleAutoGenList
.append(Pa
.Platform
.Modules
[ModuleKey
].M
)
1667 if GlobalData
.gFdfParser
!= None:
1668 if Pa
.Arch
in GlobalData
.gFdfParser
.Profile
.InfDict
:
1669 INFList
= GlobalData
.gFdfParser
.Profile
.InfDict
[Pa
.Arch
]
1670 for InfName
in INFList
:
1671 InfClass
= PathClass(NormPath(InfName
), Wa
.WorkspaceDir
, Pa
.Arch
)
1672 Ma
= ModuleAutoGen(Wa
, InfClass
, Pa
.BuildTarget
, Pa
.ToolChain
, Pa
.Arch
, Wa
.MetaFile
)
1675 if Ma
not in ModuleAutoGenList
:
1676 ModuleAutoGenList
.append(Ma
)
1677 for MGen
in ModuleAutoGenList
:
1678 self
.ModuleReportList
.append(ModuleReport(MGen
, ReportType
))
1683 # Generate report for the whole platform.
1685 # This function generates report for platform information.
1686 # It comprises of platform summary, global PCD, flash and
1687 # module list sections.
1689 # @param self The object pointer
1690 # @param File The file object for report
1691 # @param BuildDuration The total time to build the modules
1692 # @param ReportType The kind of report items in the final report file
1694 def GenerateReport(self
, File
, BuildDuration
, ReportType
):
1695 FileWrite(File
, "Platform Summary")
1696 FileWrite(File
, "Platform Name: %s" % self
.PlatformName
)
1697 FileWrite(File
, "Platform DSC Path: %s" % self
.PlatformDscPath
)
1698 FileWrite(File
, "Architectures: %s" % self
.Architectures
)
1699 FileWrite(File
, "Tool Chain: %s" % self
.ToolChain
)
1700 FileWrite(File
, "Target: %s" % self
.Target
)
1701 FileWrite(File
, "Output Path: %s" % self
.OutputPath
)
1702 FileWrite(File
, "Build Environment: %s" % self
.BuildEnvironment
)
1703 FileWrite(File
, "Build Duration: %s" % BuildDuration
)
1704 FileWrite(File
, "Report Content: %s" % ", ".join(ReportType
))
1706 if GlobalData
.MixedPcd
:
1707 FileWrite(File
, gSectionStart
)
1708 FileWrite(File
, "The following PCDs use different access methods:")
1709 FileWrite(File
, gSectionSep
)
1710 for PcdItem
in GlobalData
.MixedPcd
:
1711 FileWrite(File
, "%s.%s" % (str(PcdItem
[1]), str(PcdItem
[0])))
1712 FileWrite(File
, gSectionEnd
)
1714 if not self
._IsModuleBuild
:
1715 if "PCD" in ReportType
:
1716 self
.PcdReport
.GenerateReport(File
, None)
1718 if "FLASH" in ReportType
:
1719 for FdReportListItem
in self
.FdReportList
:
1720 FdReportListItem
.GenerateReport(File
)
1722 for ModuleReportItem
in self
.ModuleReportList
:
1723 ModuleReportItem
.GenerateReport(File
, self
.PcdReport
, self
.PredictionReport
, self
.DepexParser
, ReportType
)
1725 if not self
._IsModuleBuild
:
1726 if "EXECUTION_ORDER" in ReportType
:
1727 self
.PredictionReport
.GenerateReport(File
, None)
1729 ## BuildReport class
1731 # This base class contain the routines to collect data and then
1732 # applies certain format to the output report
1734 class BuildReport(object):
1736 # Constructor function for class BuildReport
1738 # This constructor function generates BuildReport object a platform build.
1739 # It generates report for platform summary, flash, global PCDs and detailed
1740 # module information for modules involved in platform build.
1742 # @param self The object pointer
1743 # @param ReportFile The file name to save report file
1744 # @param ReportType The kind of report items in the final report file
1746 def __init__(self
, ReportFile
, ReportType
):
1747 self
.ReportFile
= ReportFile
1749 self
.ReportList
= []
1750 self
.ReportType
= []
1752 for ReportTypeItem
in ReportType
:
1753 if ReportTypeItem
not in self
.ReportType
:
1754 self
.ReportType
.append(ReportTypeItem
)
1756 self
.ReportType
= ["PCD", "LIBRARY", "BUILD_FLAGS", "DEPEX", "HASH", "FLASH", "FIXED_ADDRESS"]
1758 # Adds platform report to the list
1760 # This function adds a platform report to the final report list.
1762 # @param self The object pointer
1763 # @param Wa Workspace context information
1764 # @param MaList The list of modules in the platform build
1766 def AddPlatformReport(self
, Wa
, MaList
=None):
1768 self
.ReportList
.append((Wa
, MaList
))
1771 # Generates the final report.
1773 # This function generates platform build report. It invokes GenerateReport()
1774 # method for every platform report in the list.
1776 # @param self The object pointer
1777 # @param BuildDuration The total time to build the modules
1779 def GenerateReport(self
, BuildDuration
):
1783 for (Wa
, MaList
) in self
.ReportList
:
1784 PlatformReport(Wa
, MaList
, self
.ReportType
).GenerateReport(File
, BuildDuration
, self
.ReportType
)
1785 Content
= FileLinesSplit(File
.getvalue(), gLineMaxLength
)
1786 SaveFileOnChange(self
.ReportFile
, Content
, True)
1787 EdkLogger
.quiet("Build report can be found at %s" % os
.path
.abspath(self
.ReportFile
))
1789 EdkLogger
.error(None, FILE_WRITE_FAILURE
, ExtraData
=self
.ReportFile
)
1791 EdkLogger
.error("BuildReport", CODE_ERROR
, "Unknown fatal error when generating build report", ExtraData
=self
.ReportFile
, RaiseError
=False)
1792 EdkLogger
.quiet("(Python %s on %s\n%s)" % (platform
.python_version(), sys
.platform
, traceback
.format_exc()))
1795 # This acts like the main() function for the script, unless it is 'import'ed into another script.
1796 if __name__
== '__main__':