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
128 ## The look up table of the supported opcode in the dependency expression binaries
129 gOpCodeList
= ["BEFORE", "AFTER", "PUSH", "AND", "OR", "NOT", "TRUE", "FALSE", "END", "SOR"]
132 # Writes a string to the file object.
134 # This function writes a string to the file object and a new line is appended
135 # afterwards. It may optionally wraps the string for better readability.
137 # @File The file object to write
138 # @String The string to be written to the file
139 # @Wrapper Indicates whether to wrap the string
141 def FileWrite(File
, String
, Wrapper
=False):
143 String
= textwrap
.fill(String
, 120)
144 File
.write(String
+ gEndOfLine
)
147 # Find all the header file that the module source directly includes.
149 # This function scans source code to find all header files the module may
150 # include. This is not accurate but very effective to find all the header
151 # file the module might include with #include statement.
153 # @Source The source file name
154 # @IncludePathList The list of include path to find the source file.
155 # @IncludeFiles The dictionary of current found include files.
157 def FindIncludeFiles(Source
, IncludePathList
, IncludeFiles
):
158 FileContents
= open(Source
).read()
160 # Find header files with pattern #include "XXX.h" or #include <XXX.h>
162 for Match
in gIncludePattern
.finditer(FileContents
):
163 FileName
= Match
.group(1).strip()
164 for Dir
in [os
.path
.dirname(Source
)] + IncludePathList
:
165 FullFileName
= os
.path
.normpath(os
.path
.join(Dir
, FileName
))
166 if os
.path
.exists(FullFileName
):
167 IncludeFiles
[FullFileName
.lower().replace("\\", "/")] = FullFileName
171 # Find header files with pattern like #include EFI_PPI_CONSUMER(XXX)
173 for Match
in gIncludePattern2
.finditer(FileContents
):
175 Type
= Match
.group(1)
176 if "ARCH_PROTOCOL" in Type
:
177 FileName
= "ArchProtocol/%(Key)s/%(Key)s.h" % {"Key" : Key
}
178 elif "PROTOCOL" in Type
:
179 FileName
= "Protocol/%(Key)s/%(Key)s.h" % {"Key" : Key
}
181 FileName
= "Ppi/%(Key)s/%(Key)s.h" % {"Key" : Key
}
183 FileName
= "Guid/%(Key)s/%(Key)s.h" % {"Key" : Key
}
186 for Dir
in IncludePathList
:
187 FullFileName
= os
.path
.normpath(os
.path
.join(Dir
, FileName
))
188 if os
.path
.exists(FullFileName
):
189 IncludeFiles
[FullFileName
.lower().replace("\\", "/")] = FullFileName
192 ## Split each lines in file
194 # This method is used to split the lines in file to make the length of each line
195 # less than MaxLength.
197 # @param Content The content of file
198 # @param MaxLength The Max Length of the line
200 def FileLinesSplit(Content
=None, MaxLength
=None):
201 ContentList
= Content
.split(TAB_LINE_BREAK
)
204 for Line
in ContentList
:
205 while len(Line
.rstrip()) > MaxLength
:
206 LineSpaceIndex
= Line
.rfind(TAB_SPACE_SPLIT
, 0, MaxLength
)
207 LineSlashIndex
= Line
.rfind(TAB_SLASH
, 0, MaxLength
)
208 LineBackSlashIndex
= Line
.rfind(TAB_BACK_SLASH
, 0, MaxLength
)
209 if max(LineSpaceIndex
, LineSlashIndex
, LineBackSlashIndex
) > 0:
210 LineBreakIndex
= max(LineSpaceIndex
, LineSlashIndex
, LineBackSlashIndex
)
212 LineBreakIndex
= MaxLength
213 NewContentList
.append(Line
[:LineBreakIndex
])
214 Line
= Line
[LineBreakIndex
:]
216 NewContentList
.append(Line
)
217 for NewLine
in NewContentList
:
218 NewContent
+= NewLine
+ TAB_LINE_BREAK
220 NewContent
= NewContent
.replace(TAB_LINE_BREAK
, gEndOfLine
).replace('\r\r\n', gEndOfLine
)
226 # Parse binary dependency expression section
228 # This utility class parses the dependency expression section and translate the readable
229 # GUID name and value.
231 class DepexParser(object):
233 # Constructor function for class DepexParser
235 # This constructor function collect GUID values so that the readable
236 # GUID name can be translated.
238 # @param self The object pointer
239 # @param Wa Workspace context information
241 def __init__(self
, Wa
):
243 for Pa
in Wa
.AutoGenObjectList
:
244 for Package
in Pa
.PackageList
:
245 for Protocol
in Package
.Protocols
:
246 GuidValue
= GuidStructureStringToGuidString(Package
.Protocols
[Protocol
])
247 self
._GuidDb
[GuidValue
.upper()] = Protocol
248 for Ppi
in Package
.Ppis
:
249 GuidValue
= GuidStructureStringToGuidString(Package
.Ppis
[Ppi
])
250 self
._GuidDb
[GuidValue
.upper()] = Ppi
251 for Guid
in Package
.Guids
:
252 GuidValue
= GuidStructureStringToGuidString(Package
.Guids
[Guid
])
253 self
._GuidDb
[GuidValue
.upper()] = Guid
256 # Parse the binary dependency expression files.
258 # This function parses the binary dependency expression file and translate it
259 # to the instruction list.
261 # @param self The object pointer
262 # @param DepexFileName The file name of binary dependency expression file.
264 def ParseDepexFile(self
, DepexFileName
):
265 DepexFile
= open(DepexFileName
, "rb")
267 OpCode
= DepexFile
.read(1)
269 Statement
= gOpCodeList
[struct
.unpack("B", OpCode
)[0]]
270 if Statement
in ["BEFORE", "AFTER", "PUSH"]:
271 GuidValue
= "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X" % \
272 struct
.unpack("=LHHBBBBBBBB", DepexFile
.read(16))
273 GuidString
= self
._GuidDb
.get(GuidValue
, GuidValue
)
274 Statement
= "%s %s" % (Statement
, GuidString
)
275 DepexStatement
.append(Statement
)
276 OpCode
= DepexFile
.read(1)
278 return DepexStatement
281 # Reports library information
283 # This class reports the module library subsection in the build report file.
285 class LibraryReport(object):
287 # Constructor function for class LibraryReport
289 # This constructor function generates LibraryReport object for
292 # @param self The object pointer
293 # @param M Module context information
295 def __init__(self
, M
):
296 self
.LibraryList
= []
297 if int(str(M
.AutoGenVersion
), 0) >= 0x00010005:
298 self
._EdkIIModule
= True
300 self
._EdkIIModule
= False
302 for Lib
in M
.DependentLibraryList
:
303 LibInfPath
= str(Lib
)
304 LibClassList
= Lib
.LibraryClass
[0].LibraryClass
305 LibConstructorList
= Lib
.ConstructorList
306 LibDesstructorList
= Lib
.DestructorList
307 LibDepexList
= Lib
.DepexExpression
[M
.Arch
, M
.ModuleType
]
308 self
.LibraryList
.append((LibInfPath
, LibClassList
, LibConstructorList
, LibDesstructorList
, LibDepexList
))
311 # Generate report for module library information
313 # This function generates report for the module library.
314 # If the module is EDKII style one, the additional library class, library
315 # constructor/destructor and dependency expression may also be reported.
317 # @param self The object pointer
318 # @param File The file object for report
320 def GenerateReport(self
, File
):
321 FileWrite(File
, gSubSectionStart
)
322 FileWrite(File
, TAB_BRG_LIBRARY
)
323 if len(self
.LibraryList
) > 0:
324 FileWrite(File
, gSubSectionSep
)
325 for LibraryItem
in self
.LibraryList
:
326 LibInfPath
= LibraryItem
[0]
327 FileWrite(File
, LibInfPath
)
330 # Report library class, library constructor and destructor for
331 # EDKII style module.
333 if self
._EdkIIModule
:
334 LibClass
= LibraryItem
[1]
336 LibConstructor
= " ".join(LibraryItem
[2])
338 EdkIILibInfo
+= " C = " + LibConstructor
339 LibDestructor
= " ".join(LibraryItem
[3])
341 EdkIILibInfo
+= " D = " + LibDestructor
342 LibDepex
= " ".join(LibraryItem
[4])
344 EdkIILibInfo
+= " Depex = " + LibDepex
346 FileWrite(File
, "{%s: %s}" % (LibClass
, EdkIILibInfo
))
348 FileWrite(File
, "{%s}" % LibClass
)
350 FileWrite(File
, gSubSectionEnd
)
353 # Reports dependency expression information
355 # This class reports the module dependency expression subsection in the build report file.
357 class DepexReport(object):
359 # Constructor function for class DepexReport
361 # This constructor function generates DepexReport object for
362 # a module. If the module source contains the DXS file (usually EDK
363 # style module), it uses the dependency in DXS file; otherwise,
364 # it uses the dependency expression from its own INF [Depex] section
365 # and then merges with the ones from its dependent library INF.
367 # @param self The object pointer
368 # @param M Module context information
370 def __init__(self
, M
):
372 self
._DepexFileName
= os
.path
.join(M
.BuildDir
, "OUTPUT", M
.Module
.BaseName
+ ".depex")
373 ModuleType
= M
.ModuleType
375 ModuleType
= gComponentType2ModuleType
.get(M
.ComponentType
, "")
377 if ModuleType
in ["SEC", "PEI_CORE", "DXE_CORE", "SMM_CORE", "UEFI_APPLICATION"]:
380 for Source
in M
.SourceFileList
:
381 if os
.path
.splitext(Source
.Path
)[1].lower() == ".dxs":
382 Match
= gDxsDependencyPattern
.search(open(Source
.Path
).read())
384 self
.Depex
= Match
.group(1).strip()
388 self
.Depex
= M
.DepexExpressionList
.get(M
.ModuleType
, "")
389 self
.ModuleDepex
= " ".join(M
.Module
.DepexExpression
[M
.Arch
, M
.ModuleType
])
390 if not self
.ModuleDepex
:
391 self
.ModuleDepex
= "(None)"
394 for Lib
in M
.DependentLibraryList
:
395 LibDepex
= " ".join(Lib
.DepexExpression
[M
.Arch
, M
.ModuleType
]).strip()
397 LibDepexList
.append("(" + LibDepex
+ ")")
398 self
.LibraryDepex
= " AND ".join(LibDepexList
)
399 if not self
.LibraryDepex
:
400 self
.LibraryDepex
= "(None)"
404 # Generate report for module dependency expression information
406 # This function generates report for the module dependency expression.
408 # @param self The object pointer
409 # @param File The file object for report
410 # @param GlobalDepexParser The platform global Dependency expression parser object
412 def GenerateReport(self
, File
, GlobalDepexParser
):
414 FileWrite(File
, gSubSectionStart
)
415 FileWrite(File
, TAB_DEPEX
)
416 FileWrite(File
, gSubSectionEnd
)
418 FileWrite(File
, gSubSectionStart
)
419 if os
.path
.isfile(self
._DepexFileName
):
421 DepexStatements
= GlobalDepexParser
.ParseDepexFile(self
._DepexFileName
)
422 FileWrite(File
, "Final Dependency Expression (DEPEX) Instructions")
423 for DepexStatement
in DepexStatements
:
424 FileWrite(File
, " %s" % DepexStatement
)
425 FileWrite(File
, gSubSectionSep
)
427 EdkLogger
.warn(None, "Dependency expression file is corrupted", self
._DepexFileName
)
429 FileWrite(File
, "Dependency Expression (DEPEX) from %s" % self
.Source
)
431 if self
.Source
== "INF":
432 FileWrite(File
, "%s" % self
.Depex
, True)
433 FileWrite(File
, gSubSectionSep
)
434 FileWrite(File
, "From Module INF: %s" % self
.ModuleDepex
, True)
435 FileWrite(File
, "From Library INF: %s" % self
.LibraryDepex
, True)
437 FileWrite(File
, "%s" % self
.Depex
)
438 FileWrite(File
, gSubSectionEnd
)
441 # Reports dependency expression information
443 # This class reports the module build flags subsection in the build report file.
445 class BuildFlagsReport(object):
447 # Constructor function for class BuildFlagsReport
449 # This constructor function generates BuildFlagsReport object for
450 # a module. It reports the build tool chain tag and all relevant
451 # build flags to build the module.
453 # @param self The object pointer
454 # @param M Module context information
456 def __init__(self
, M
):
459 # Add build flags according to source file extension so that
460 # irrelevant ones can be filtered out.
462 for Source
in M
.SourceFileList
:
463 Ext
= os
.path
.splitext(Source
.File
)[1].lower()
464 if Ext
in [".c", ".cc", ".cpp"]:
465 BuildOptions
["CC"] = 1
466 elif Ext
in [".s", ".asm"]:
467 BuildOptions
["PP"] = 1
468 BuildOptions
["ASM"] = 1
469 elif Ext
in [".vfr"]:
470 BuildOptions
["VFRPP"] = 1
471 BuildOptions
["VFR"] = 1
472 elif Ext
in [".dxs"]:
473 BuildOptions
["APP"] = 1
474 BuildOptions
["CC"] = 1
475 elif Ext
in [".asl"]:
476 BuildOptions
["ASLPP"] = 1
477 BuildOptions
["ASL"] = 1
478 elif Ext
in [".aslc"]:
479 BuildOptions
["ASLCC"] = 1
480 BuildOptions
["ASLDLINK"] = 1
481 BuildOptions
["CC"] = 1
482 elif Ext
in [".asm16"]:
483 BuildOptions
["ASMLINK"] = 1
484 BuildOptions
["SLINK"] = 1
485 BuildOptions
["DLINK"] = 1
488 # Save module build flags.
490 self
.ToolChainTag
= M
.ToolChain
492 for Tool
in BuildOptions
:
493 self
.BuildFlags
[Tool
+ "_FLAGS"] = M
.BuildOption
.get(Tool
, {}).get("FLAGS", "")
496 # Generate report for module build flags information
498 # This function generates report for the module build flags expression.
500 # @param self The object pointer
501 # @param File The file object for report
503 def GenerateReport(self
, File
):
504 FileWrite(File
, gSubSectionStart
)
505 FileWrite(File
, "Build Flags")
506 FileWrite(File
, "Tool Chain Tag: %s" % self
.ToolChainTag
)
507 for Tool
in self
.BuildFlags
:
508 FileWrite(File
, gSubSectionSep
)
509 FileWrite(File
, "%s = %s" % (Tool
, self
.BuildFlags
[Tool
]), True)
511 FileWrite(File
, gSubSectionEnd
)
515 # Reports individual module information
517 # This class reports the module section in the build report file.
518 # It comprises of module summary, module PCD, library, dependency expression,
519 # build flags sections.
521 class ModuleReport(object):
523 # Constructor function for class ModuleReport
525 # This constructor function generates ModuleReport object for
526 # a separate module in a platform build.
528 # @param self The object pointer
529 # @param M Module context information
530 # @param ReportType The kind of report items in the final report file
532 def __init__(self
, M
, ReportType
):
533 self
.ModuleName
= M
.Module
.BaseName
534 self
.ModuleInfPath
= M
.MetaFile
.File
535 self
.FileGuid
= M
.Guid
537 self
.BuildTimeStamp
= None
541 ModuleType
= M
.ModuleType
543 ModuleType
= gComponentType2ModuleType
.get(M
.ComponentType
, "")
545 # If a module complies to PI 1.1, promote Module type to "SMM_DRIVER"
547 if ModuleType
== "DXE_SMM_DRIVER":
548 PiSpec
= M
.Module
.Specification
.get("PI_SPECIFICATION_VERSION", "0x00010000")
549 if int(PiSpec
, 0) >= 0x0001000A:
550 ModuleType
= "SMM_DRIVER"
551 self
.DriverType
= gDriverTypeMap
.get(ModuleType
, "0x2 (FREE_FORM)")
552 self
.UefiSpecVersion
= M
.Module
.Specification
.get("UEFI_SPECIFICATION_VERSION", "")
553 self
.PiSpecVersion
= M
.Module
.Specification
.get("PI_SPECIFICATION_VERSION", "")
554 self
.PciDeviceId
= M
.Module
.Defines
.get("PCI_DEVICE_ID", "")
555 self
.PciVendorId
= M
.Module
.Defines
.get("PCI_VENDOR_ID", "")
556 self
.PciClassCode
= M
.Module
.Defines
.get("PCI_CLASS_CODE", "")
558 self
._BuildDir
= M
.BuildDir
559 self
.ModulePcdSet
= {}
560 if "PCD" in ReportType
:
562 # Collect all module used PCD set: module INF referenced directly or indirectly.
563 # It also saves module INF default values of them in case they exist.
565 for Pcd
in M
.ModulePcdList
+ M
.LibraryPcdList
:
566 self
.ModulePcdSet
.setdefault((Pcd
.TokenCName
, Pcd
.TokenSpaceGuidCName
, Pcd
.Type
), (Pcd
.InfDefaultValue
, Pcd
.DefaultValue
))
568 self
.LibraryReport
= None
569 if "LIBRARY" in ReportType
:
570 self
.LibraryReport
= LibraryReport(M
)
572 self
.DepexReport
= None
573 if "DEPEX" in ReportType
:
574 self
.DepexReport
= DepexReport(M
)
576 if "BUILD_FLAGS" in ReportType
:
577 self
.BuildFlagsReport
= BuildFlagsReport(M
)
581 # Generate report for module information
583 # This function generates report for separate module expression
584 # in a platform build.
586 # @param self The object pointer
587 # @param File The file object for report
588 # @param GlobalPcdReport The platform global PCD report object
589 # @param GlobalPredictionReport The platform global Prediction report object
590 # @param GlobalDepexParser The platform global Dependency expression parser object
591 # @param ReportType The kind of report items in the final report file
593 def GenerateReport(self
, File
, GlobalPcdReport
, GlobalPredictionReport
, GlobalDepexParser
, ReportType
):
594 FileWrite(File
, gSectionStart
)
596 FwReportFileName
= os
.path
.join(self
._BuildDir
, "DEBUG", self
.ModuleName
+ ".txt")
597 if os
.path
.isfile(FwReportFileName
):
599 FileContents
= open(FwReportFileName
).read()
600 Match
= gModuleSizePattern
.search(FileContents
)
602 self
.Size
= int(Match
.group(1))
604 Match
= gTimeStampPattern
.search(FileContents
)
606 self
.BuildTimeStamp
= datetime
.fromtimestamp(int(Match
.group(1)))
608 EdkLogger
.warn(None, "Fail to read report file", FwReportFileName
)
610 if "HASH" in ReportType
:
611 OutputDir
= os
.path
.join(self
._BuildDir
, "OUTPUT")
612 DefaultEFIfile
= os
.path
.join(OutputDir
, self
.ModuleName
+ ".efi")
613 if os
.path
.isfile(DefaultEFIfile
):
614 Tempfile
= os
.path
.join(OutputDir
, self
.ModuleName
+ "_hash.tmp")
615 # rebase the efi image since its base address may not zero
616 cmd
= ["GenFw", "--rebase", str(0), "-o", Tempfile
, DefaultEFIfile
]
618 PopenObject
= subprocess
.Popen(' '.join(cmd
), stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
, shell
=True)
620 EdkLogger
.error("GenFw", COMMAND_FAILURE
, ExtraData
="%s: %s" % (str(X
), cmd
[0]))
621 EndOfProcedure
= threading
.Event()
622 EndOfProcedure
.clear()
623 if PopenObject
.stderr
:
624 StdErrThread
= threading
.Thread(target
=ReadMessage
, args
=(PopenObject
.stderr
, EdkLogger
.quiet
, EndOfProcedure
))
625 StdErrThread
.setName("STDERR-Redirector")
626 StdErrThread
.setDaemon(False)
628 # waiting for program exit
630 if PopenObject
.stderr
:
632 if PopenObject
.returncode
!= 0:
633 EdkLogger
.error("GenFw", COMMAND_FAILURE
, "Failed to generate firmware hash image for %s" % (DefaultEFIfile
))
634 if os
.path
.isfile(Tempfile
):
635 self
.Hash
= hashlib
.sha1()
636 buf
= open(Tempfile
, 'rb').read()
637 if self
.Hash
.update(buf
):
638 self
.Hash
= self
.Hash
.update(buf
)
639 self
.Hash
= self
.Hash
.hexdigest()
642 FileWrite(File
, "Module Summary")
643 FileWrite(File
, "Module Name: %s" % self
.ModuleName
)
644 FileWrite(File
, "Module INF Path: %s" % self
.ModuleInfPath
)
645 FileWrite(File
, "File GUID: %s" % self
.FileGuid
)
647 FileWrite(File
, "Size: 0x%X (%.2fK)" % (self
.Size
, self
.Size
/ 1024.0))
649 FileWrite(File
, "SHA1 HASH: %s *%s" % (self
.Hash
, self
.ModuleName
+ ".efi"))
650 if self
.BuildTimeStamp
:
651 FileWrite(File
, "Build Time Stamp: %s" % self
.BuildTimeStamp
)
653 FileWrite(File
, "Driver Type: %s" % self
.DriverType
)
654 if self
.UefiSpecVersion
:
655 FileWrite(File
, "UEFI Spec Version: %s" % self
.UefiSpecVersion
)
656 if self
.PiSpecVersion
:
657 FileWrite(File
, "PI Spec Version: %s" % self
.PiSpecVersion
)
659 FileWrite(File
, "PCI Device ID: %s" % self
.PciDeviceId
)
661 FileWrite(File
, "PCI Vendor ID: %s" % self
.PciVendorId
)
662 if self
.PciClassCode
:
663 FileWrite(File
, "PCI Class Code: %s" % self
.PciClassCode
)
665 FileWrite(File
, gSectionSep
)
667 if "PCD" in ReportType
:
668 GlobalPcdReport
.GenerateReport(File
, self
.ModulePcdSet
)
670 if "LIBRARY" in ReportType
:
671 self
.LibraryReport
.GenerateReport(File
)
673 if "DEPEX" in ReportType
:
674 self
.DepexReport
.GenerateReport(File
, GlobalDepexParser
)
676 if "BUILD_FLAGS" in ReportType
:
677 self
.BuildFlagsReport
.GenerateReport(File
)
679 if "FIXED_ADDRESS" in ReportType
and self
.FileGuid
:
680 GlobalPredictionReport
.GenerateReport(File
, self
.FileGuid
)
682 FileWrite(File
, gSectionEnd
)
684 def ReadMessage(From
, To
, ExitFlag
):
686 # read one line a time
687 Line
= From
.readline()
688 # empty string means "end"
689 if Line
!= None and Line
!= "":
697 # Reports platform and module PCD information
699 # This class reports the platform PCD section and module PCD subsection
700 # in the build report file.
702 class PcdReport(object):
704 # Constructor function for class PcdReport
706 # This constructor function generates PcdReport object a platform build.
707 # It collects the whole PCD database from platform DSC files, platform
708 # flash description file and package DEC files.
710 # @param self The object pointer
711 # @param Wa Workspace context information
713 def __init__(self
, Wa
):
716 self
.ConditionalPcds
= {}
719 self
.FdfPcdSet
= Wa
.FdfProfile
.PcdDict
723 self
.ModulePcdOverride
= {}
724 for Pa
in Wa
.AutoGenObjectList
:
726 # Collect all platform referenced PCDs and grouped them by PCD token space
729 for Pcd
in Pa
.AllPcdList
:
730 PcdList
= self
.AllPcds
.setdefault(Pcd
.TokenSpaceGuidCName
, {}).setdefault(Pcd
.Type
, [])
731 if Pcd
not in PcdList
:
733 if len(Pcd
.TokenCName
) > self
.MaxLen
:
734 self
.MaxLen
= len(Pcd
.TokenCName
)
736 # Collect the PCD defined in DSC/FDF file, but not used in module
738 UnusedPcdFullList
= []
739 for item
in Pa
.Platform
.Pcds
:
740 Pcd
= Pa
.Platform
.Pcds
[item
]
743 for package
in Pa
.PackageList
:
744 for T
in ["FixedAtBuild", "PatchableInModule", "FeatureFlag", "Dynamic", "DynamicEx"]:
745 if (Pcd
.TokenCName
, Pcd
.TokenSpaceGuidCName
, T
) in package
.Pcds
:
748 if not Pcd
.DatumType
:
749 Pcd
.DatumType
= package
.Pcds
[(Pcd
.TokenCName
, Pcd
.TokenSpaceGuidCName
, T
)].DatumType
753 if not Pcd
.DatumType
:
755 # Try to remove Hii and Vpd suffix
756 if PcdType
.startswith("DynamicEx"):
757 PcdType
= "DynamicEx"
758 elif PcdType
.startswith("Dynamic"):
760 for package
in Pa
.PackageList
:
761 if (Pcd
.TokenCName
, Pcd
.TokenSpaceGuidCName
, PcdType
) in package
.Pcds
:
762 Pcd
.DatumType
= package
.Pcds
[(Pcd
.TokenCName
, Pcd
.TokenSpaceGuidCName
, PcdType
)].DatumType
765 PcdList
= self
.AllPcds
.setdefault(Pcd
.TokenSpaceGuidCName
, {}).setdefault(Pcd
.Type
, [])
766 if Pcd
not in PcdList
and Pcd
not in UnusedPcdFullList
:
767 UnusedPcdFullList
.append(Pcd
)
768 if len(Pcd
.TokenCName
) > self
.MaxLen
:
769 self
.MaxLen
= len(Pcd
.TokenCName
)
771 if GlobalData
.gConditionalPcds
:
772 for PcdItem
in GlobalData
.gConditionalPcds
:
774 (TokenSpaceGuidCName
, TokenCName
) = PcdItem
.split('.')
775 if (TokenCName
, TokenSpaceGuidCName
) in Pa
.Platform
.Pcds
.keys():
776 Pcd
= Pa
.Platform
.Pcds
[(TokenCName
, TokenSpaceGuidCName
)]
777 PcdList
= self
.ConditionalPcds
.setdefault(Pcd
.TokenSpaceGuidCName
, {}).setdefault(Pcd
.Type
, [])
778 if Pcd
not in PcdList
:
782 if UnusedPcdFullList
:
783 for Pcd
in UnusedPcdFullList
:
784 if Pcd
.TokenSpaceGuidCName
+ '.' + Pcd
.TokenCName
in GlobalData
.gConditionalPcds
:
786 UnusedPcdList
.append(Pcd
)
788 for Pcd
in UnusedPcdList
:
789 PcdList
= self
.UnusedPcds
.setdefault(Pcd
.TokenSpaceGuidCName
, {}).setdefault(Pcd
.Type
, [])
790 if Pcd
not in PcdList
:
793 for Module
in Pa
.Platform
.Modules
.values():
795 # Collect module override PCDs
797 for ModulePcd
in Module
.M
.ModulePcdList
+ Module
.M
.LibraryPcdList
:
798 TokenCName
= ModulePcd
.TokenCName
799 TokenSpaceGuid
= ModulePcd
.TokenSpaceGuidCName
800 ModuleDefault
= ModulePcd
.DefaultValue
801 ModulePath
= os
.path
.basename(Module
.M
.MetaFile
.File
)
802 self
.ModulePcdOverride
.setdefault((TokenCName
, TokenSpaceGuid
), {})[ModulePath
] = ModuleDefault
806 # Collect PCD DEC default value.
808 self
.DecPcdDefault
= {}
809 for Pa
in Wa
.AutoGenObjectList
:
810 for Package
in Pa
.PackageList
:
811 for (TokenCName
, TokenSpaceGuidCName
, DecType
) in Package
.Pcds
:
812 DecDefaultValue
= Package
.Pcds
[TokenCName
, TokenSpaceGuidCName
, DecType
].DefaultValue
813 self
.DecPcdDefault
.setdefault((TokenCName
, TokenSpaceGuidCName
, DecType
), DecDefaultValue
)
815 # Collect PCDs defined in DSC common section
817 self
.DscPcdDefault
= {}
818 for Arch
in Wa
.ArchList
:
819 Platform
= Wa
.BuildDatabase
[Wa
.MetaFile
, Arch
, Wa
.BuildTarget
, Wa
.ToolChain
]
820 for (TokenCName
, TokenSpaceGuidCName
) in Platform
.Pcds
:
821 DscDefaultValue
= Platform
.Pcds
[(TokenCName
, TokenSpaceGuidCName
)].DefaultValue
823 self
.DscPcdDefault
[(TokenCName
, TokenSpaceGuidCName
)] = DscDefaultValue
825 def GenerateReport(self
, File
, ModulePcdSet
):
826 if self
.ConditionalPcds
:
827 self
.GenerateReportDetail(File
, ModulePcdSet
, 1)
829 self
.GenerateReportDetail(File
, ModulePcdSet
, 2)
830 self
.GenerateReportDetail(File
, ModulePcdSet
)
833 # Generate report for PCD information
835 # This function generates report for separate module expression
836 # in a platform build.
838 # @param self The object pointer
839 # @param File The file object for report
840 # @param ModulePcdSet Set of all PCDs referenced by module or None for
841 # platform PCD report
842 # @param ReportySubType 0 means platform/module PCD report, 1 means Conditional
843 # directives section report, 2 means Unused Pcds section report
844 # @param DscOverridePcds Module DSC override PCDs set
846 def GenerateReportDetail(self
, File
, ModulePcdSet
, ReportSubType
= 0):
847 PcdDict
= self
.AllPcds
848 if ReportSubType
== 1:
849 PcdDict
= self
.ConditionalPcds
850 elif ReportSubType
== 2:
851 PcdDict
= self
.UnusedPcds
853 if ModulePcdSet
== None:
854 FileWrite(File
, gSectionStart
)
855 if ReportSubType
== 1:
856 FileWrite(File
, "Conditional Directives used by the build system")
857 elif ReportSubType
== 2:
858 FileWrite(File
, "PCDs not used by modules or in conditional directives")
860 FileWrite(File
, "Platform Configuration Database Report")
862 FileWrite(File
, " *B - PCD override in the build option")
863 FileWrite(File
, " *P - Platform scoped PCD override in DSC file")
864 FileWrite(File
, " *F - Platform scoped PCD override in FDF file")
865 if not ReportSubType
:
866 FileWrite(File
, " *M - Module scoped PCD override")
867 FileWrite(File
, gSectionSep
)
869 if not ReportSubType
:
871 # For module PCD sub-section
873 FileWrite(File
, gSubSectionStart
)
874 FileWrite(File
, TAB_BRG_PCD
)
875 FileWrite(File
, gSubSectionSep
)
879 # Group PCD by their token space GUID C Name
882 for Type
in PcdDict
[Key
]:
884 # Group PCD by their usage type
886 TypeName
, DecType
= gPcdTypeMap
.get(Type
, ("", Type
))
887 for Pcd
in PcdDict
[Key
][Type
]:
888 PcdTokenCName
= Pcd
.TokenCName
890 if GlobalData
.MixedPcd
:
891 for PcdKey
in GlobalData
.MixedPcd
:
892 if (Pcd
.TokenCName
, Pcd
.TokenSpaceGuidCName
) in GlobalData
.MixedPcd
[PcdKey
]:
893 PcdTokenCName
= PcdKey
[0]
895 if MixedPcdFlag
and not ModulePcdSet
:
898 # Get PCD default value and their override relationship
900 DecDefaultValue
= self
.DecPcdDefault
.get((Pcd
.TokenCName
, Pcd
.TokenSpaceGuidCName
, DecType
))
901 DscDefaultValue
= self
.DscPcdDefault
.get((Pcd
.TokenCName
, Pcd
.TokenSpaceGuidCName
))
902 DscDefaultValue
= self
.FdfPcdSet
.get((Pcd
.TokenCName
, Key
), DscDefaultValue
)
903 InfDefaultValue
= None
905 PcdValue
= DecDefaultValue
907 PcdValue
= DscDefaultValue
908 if ModulePcdSet
!= None:
909 if (Pcd
.TokenCName
, Pcd
.TokenSpaceGuidCName
, Type
) not in ModulePcdSet
:
911 InfDefault
, PcdValue
= ModulePcdSet
[Pcd
.TokenCName
, Pcd
.TokenSpaceGuidCName
, Type
]
915 BuildOptionMatch
= False
916 if GlobalData
.BuildOptionPcd
:
917 for pcd
in GlobalData
.BuildOptionPcd
:
918 if (Pcd
.TokenSpaceGuidCName
, Pcd
.TokenCName
) == (pcd
[0], pcd
[1]):
920 BuildOptionMatch
= True
924 if ModulePcdSet
== None:
930 if Pcd
.DatumType
in ('UINT8', 'UINT16', 'UINT32', 'UINT64'):
931 PcdValueNumber
= int(PcdValue
.strip(), 0)
932 if DecDefaultValue
== None:
935 DecDefaultValueNumber
= int(DecDefaultValue
.strip(), 0)
936 DecMatch
= (DecDefaultValueNumber
== PcdValueNumber
)
938 if InfDefaultValue
== None:
941 InfDefaultValueNumber
= int(InfDefaultValue
.strip(), 0)
942 InfMatch
= (InfDefaultValueNumber
== PcdValueNumber
)
944 if DscDefaultValue
== None:
947 DscDefaultValueNumber
= int(DscDefaultValue
.strip(), 0)
948 DscMatch
= (DscDefaultValueNumber
== PcdValueNumber
)
950 if DecDefaultValue
== None:
953 DecMatch
= (DecDefaultValue
.strip() == PcdValue
.strip())
955 if InfDefaultValue
== None:
958 InfMatch
= (InfDefaultValue
.strip() == PcdValue
.strip())
960 if DscDefaultValue
== None:
963 DscMatch
= (DscDefaultValue
.strip() == PcdValue
.strip())
966 # Report PCD item according to their override relationship
969 FileWrite(File
, ' *B %-*s: %6s %10s = %-22s' % (self
.MaxLen
, PcdTokenCName
, TypeName
, '(' + Pcd
.DatumType
+ ')', PcdValue
.strip()))
970 elif DecMatch
and InfMatch
:
971 FileWrite(File
, ' %-*s: %6s %10s = %-22s' % (self
.MaxLen
, PcdTokenCName
, TypeName
, '(' + Pcd
.DatumType
+ ')', PcdValue
.strip()))
974 if (Pcd
.TokenCName
, Key
) in self
.FdfPcdSet
:
975 FileWrite(File
, ' *F %-*s: %6s %10s = %-22s' % (self
.MaxLen
, PcdTokenCName
, TypeName
, '(' + Pcd
.DatumType
+ ')', PcdValue
.strip()))
977 FileWrite(File
, ' *P %-*s: %6s %10s = %-22s' % (self
.MaxLen
, PcdTokenCName
, TypeName
, '(' + Pcd
.DatumType
+ ')', PcdValue
.strip()))
979 FileWrite(File
, ' *M %-*s: %6s %10s = %-22s' % (self
.MaxLen
, PcdTokenCName
, TypeName
, '(' + Pcd
.DatumType
+ ')', PcdValue
.strip()))
981 if TypeName
in ('DYNHII', 'DEXHII', 'DYNVPD', 'DEXVPD'):
982 for SkuInfo
in Pcd
.SkuInfoList
.values():
983 if TypeName
in ('DYNHII', 'DEXHII'):
984 FileWrite(File
, '%*s: %s: %s' % (self
.MaxLen
+ 4, SkuInfo
.VariableGuid
, SkuInfo
.VariableName
, SkuInfo
.VariableOffset
))
986 FileWrite(File
, '%*s' % (self
.MaxLen
+ 4, SkuInfo
.VpdOffset
))
988 if not DscMatch
and DscDefaultValue
!= None:
989 FileWrite(File
, ' %*s = %s' % (self
.MaxLen
+ 19, 'DSC DEFAULT', DscDefaultValue
.strip()))
991 if not InfMatch
and InfDefaultValue
!= None:
992 FileWrite(File
, ' %*s = %s' % (self
.MaxLen
+ 19, 'INF DEFAULT', InfDefaultValue
.strip()))
994 if not DecMatch
and DecDefaultValue
!= None:
995 FileWrite(File
, ' %*s = %s' % (self
.MaxLen
+ 19, 'DEC DEFAULT', DecDefaultValue
.strip()))
997 if ModulePcdSet
== None:
998 if not BuildOptionMatch
:
999 ModuleOverride
= self
.ModulePcdOverride
.get((Pcd
.TokenCName
, Pcd
.TokenSpaceGuidCName
), {})
1000 for ModulePath
in ModuleOverride
:
1001 ModuleDefault
= ModuleOverride
[ModulePath
]
1002 if Pcd
.DatumType
in ('UINT8', 'UINT16', 'UINT32', 'UINT64'):
1003 ModulePcdDefaultValueNumber
= int(ModuleDefault
.strip(), 0)
1004 Match
= (ModulePcdDefaultValueNumber
== PcdValueNumber
)
1006 Match
= (ModuleDefault
.strip() == PcdValue
.strip())
1009 FileWrite(File
, ' *M %-*s = %s' % (self
.MaxLen
+ 19, ModulePath
, ModuleDefault
.strip()))
1011 if ModulePcdSet
== None:
1012 FileWrite(File
, gSectionEnd
)
1014 if not ReportSubType
:
1015 FileWrite(File
, gSubSectionEnd
)
1020 # Reports platform and module Prediction information
1022 # This class reports the platform execution order prediction section and
1023 # module load fixed address prediction subsection in the build report file.
1025 class PredictionReport(object):
1027 # Constructor function for class PredictionReport
1029 # This constructor function generates PredictionReport object for the platform.
1031 # @param self: The object pointer
1032 # @param Wa Workspace context information
1034 def __init__(self
, Wa
):
1035 self
._MapFileName
= os
.path
.join(Wa
.BuildDir
, Wa
.Name
+ ".map")
1036 self
._MapFileParsed
= False
1037 self
._EotToolInvoked
= False
1038 self
._FvDir
= Wa
.FvDir
1039 self
._EotDir
= Wa
.BuildDir
1040 self
._FfsEntryPoint
= {}
1042 self
._SourceList
= []
1043 self
.FixedMapDict
= {}
1048 # Collect all platform reference source files and GUID C Name
1050 for Pa
in Wa
.AutoGenObjectList
:
1051 for Module
in Pa
.LibraryAutoGenList
+ Pa
.ModuleAutoGenList
:
1053 # BASE typed modules are EFI agnostic, so we need not scan
1054 # their source code to find PPI/Protocol produce or consume
1057 if Module
.ModuleType
== "BASE":
1060 # Add module referenced source files
1062 self
._SourceList
.append(str(Module
))
1064 for Source
in Module
.SourceFileList
:
1065 if os
.path
.splitext(str(Source
))[1].lower() == ".c":
1066 self
._SourceList
.append(" " + str(Source
))
1067 FindIncludeFiles(Source
.Path
, Module
.IncludePathList
, IncludeList
)
1068 for IncludeFile
in IncludeList
.values():
1069 self
._SourceList
.append(" " + IncludeFile
)
1071 for Guid
in Module
.PpiList
:
1072 self
._GuidMap
[Guid
] = GuidStructureStringToGuidString(Module
.PpiList
[Guid
])
1073 for Guid
in Module
.ProtocolList
:
1074 self
._GuidMap
[Guid
] = GuidStructureStringToGuidString(Module
.ProtocolList
[Guid
])
1075 for Guid
in Module
.GuidList
:
1076 self
._GuidMap
[Guid
] = GuidStructureStringToGuidString(Module
.GuidList
[Guid
])
1078 if Module
.Guid
and not Module
.IsLibrary
:
1079 EntryPoint
= " ".join(Module
.Module
.ModuleEntryPointList
)
1080 if int(str(Module
.AutoGenVersion
), 0) >= 0x00010005:
1081 RealEntryPoint
= "_ModuleEntryPoint"
1083 RealEntryPoint
= EntryPoint
1084 if EntryPoint
== "_ModuleEntryPoint":
1085 CCFlags
= Module
.BuildOption
.get("CC", {}).get("FLAGS", "")
1086 Match
= gGlueLibEntryPoint
.search(CCFlags
)
1088 EntryPoint
= Match
.group(1)
1090 self
._FfsEntryPoint
[Module
.Guid
.upper()] = (EntryPoint
, RealEntryPoint
)
1094 # Collect platform firmware volume list as the input of EOT.
1098 for Fd
in Wa
.FdfProfile
.FdDict
:
1099 for FdRegion
in Wa
.FdfProfile
.FdDict
[Fd
].RegionList
:
1100 if FdRegion
.RegionType
!= "FV":
1102 for FvName
in FdRegion
.RegionDataList
:
1103 if FvName
in self
._FvList
:
1105 self
._FvList
.append(FvName
)
1106 for Ffs
in Wa
.FdfProfile
.FvDict
[FvName
.upper()].FfsList
:
1107 for Section
in Ffs
.SectionList
:
1109 for FvSection
in Section
.SectionList
:
1110 if FvSection
.FvName
in self
._FvList
:
1112 self
._FvList
.append(FvSection
.FvName
)
1113 except AttributeError:
1118 # Parse platform fixed address map files
1120 # This function parses the platform final fixed address map file to get
1121 # the database of predicted fixed address for module image base, entry point
1124 # @param self: The object pointer
1126 def _ParseMapFile(self
):
1127 if self
._MapFileParsed
:
1129 self
._MapFileParsed
= True
1130 if os
.path
.isfile(self
._MapFileName
):
1132 FileContents
= open(self
._MapFileName
).read()
1133 for Match
in gMapFileItemPattern
.finditer(FileContents
):
1134 AddressType
= Match
.group(1)
1135 BaseAddress
= Match
.group(2)
1136 EntryPoint
= Match
.group(3)
1137 Guid
= Match
.group(4).upper()
1138 List
= self
.FixedMapDict
.setdefault(Guid
, [])
1139 List
.append((AddressType
, BaseAddress
, "*I"))
1140 List
.append((AddressType
, EntryPoint
, "*E"))
1142 EdkLogger
.warn(None, "Cannot open file to read", self
._MapFileName
)
1145 # Invokes EOT tool to get the predicted the execution order.
1147 # This function invokes EOT tool to calculate the predicted dispatch order
1149 # @param self: The object pointer
1151 def _InvokeEotTool(self
):
1152 if self
._EotToolInvoked
:
1155 self
._EotToolInvoked
= True
1157 for FvName
in self
._FvList
:
1158 FvFile
= os
.path
.join(self
._FvDir
, FvName
+ ".Fv")
1159 if os
.path
.isfile(FvFile
):
1160 FvFileList
.append(FvFile
)
1162 if len(FvFileList
) == 0:
1165 # Write source file list and GUID file list to an intermediate file
1166 # as the input for EOT tool and dispatch List as the output file
1169 SourceList
= os
.path
.join(self
._EotDir
, "SourceFile.txt")
1170 GuidList
= os
.path
.join(self
._EotDir
, "GuidList.txt")
1171 DispatchList
= os
.path
.join(self
._EotDir
, "Dispatch.txt")
1173 TempFile
= open(SourceList
, "w+")
1174 for Item
in self
._SourceList
:
1175 FileWrite(TempFile
, Item
)
1177 TempFile
= open(GuidList
, "w+")
1178 for Key
in self
._GuidMap
:
1179 FileWrite(TempFile
, "%s %s" % (Key
, self
._GuidMap
[Key
]))
1183 from Eot
.Eot
import Eot
1186 # Invoke EOT tool and echo its runtime performance
1188 EotStartTime
= time
.time()
1189 Eot(CommandLineOption
=False, SourceFileList
=SourceList
, GuidList
=GuidList
,
1190 FvFileList
=' '.join(FvFileList
), Dispatch
=DispatchList
, IsInit
=True)
1191 EotEndTime
= time
.time()
1192 EotDuration
= time
.strftime("%H:%M:%S", time
.gmtime(int(round(EotEndTime
- EotStartTime
))))
1193 EdkLogger
.quiet("EOT run time: %s\n" % EotDuration
)
1196 # Parse the output of EOT tool
1198 for Line
in open(DispatchList
):
1199 if len(Line
.split()) < 4:
1201 (Guid
, Phase
, FfsName
, FilePath
) = Line
.split()
1202 Symbol
= self
._FfsEntryPoint
.get(Guid
, [FfsName
, ""])[0]
1203 if len(Symbol
) > self
.MaxLen
:
1204 self
.MaxLen
= len(Symbol
)
1205 self
.ItemList
.append((Phase
, Symbol
, FilePath
))
1207 EdkLogger
.quiet("(Python %s on %s\n%s)" % (platform
.python_version(), sys
.platform
, traceback
.format_exc()))
1208 EdkLogger
.warn(None, "Failed to generate execution order prediction report, for some error occurred in executing EOT.")
1212 # Generate platform execution order report
1214 # This function generates the predicted module execution order.
1216 # @param self The object pointer
1217 # @param File The file object for report
1219 def _GenerateExecutionOrderReport(self
, File
):
1220 self
._InvokeEotTool
()
1221 if len(self
.ItemList
) == 0:
1223 FileWrite(File
, gSectionStart
)
1224 FileWrite(File
, "Execution Order Prediction")
1225 FileWrite(File
, "*P PEI phase")
1226 FileWrite(File
, "*D DXE phase")
1227 FileWrite(File
, "*E Module INF entry point name")
1228 FileWrite(File
, "*N Module notification function name")
1230 FileWrite(File
, "Type %-*s %s" % (self
.MaxLen
, "Symbol", "Module INF Path"))
1231 FileWrite(File
, gSectionSep
)
1232 for Item
in self
.ItemList
:
1233 FileWrite(File
, "*%sE %-*s %s" % (Item
[0], self
.MaxLen
, Item
[1], Item
[2]))
1235 FileWrite(File
, gSectionStart
)
1238 # Generate Fixed Address report.
1240 # This function generate the predicted fixed address report for a module
1241 # specified by Guid.
1243 # @param self The object pointer
1244 # @param File The file object for report
1245 # @param Guid The module Guid value.
1246 # @param NotifyList The list of all notify function in a module
1248 def _GenerateFixedAddressReport(self
, File
, Guid
, NotifyList
):
1249 self
._ParseMapFile
()
1250 FixedAddressList
= self
.FixedMapDict
.get(Guid
)
1251 if not FixedAddressList
:
1254 FileWrite(File
, gSubSectionStart
)
1255 FileWrite(File
, "Fixed Address Prediction")
1256 FileWrite(File
, "*I Image Loading Address")
1257 FileWrite(File
, "*E Entry Point Address")
1258 FileWrite(File
, "*N Notification Function Address")
1259 FileWrite(File
, "*F Flash Address")
1260 FileWrite(File
, "*M Memory Address")
1261 FileWrite(File
, "*S SMM RAM Offset")
1262 FileWrite(File
, "TOM Top of Memory")
1264 FileWrite(File
, "Type Address Name")
1265 FileWrite(File
, gSubSectionSep
)
1266 for Item
in FixedAddressList
:
1271 Name
= "(Image Base)"
1272 elif Symbol
== "*E":
1273 Name
= self
._FfsEntryPoint
.get(Guid
, ["", "_ModuleEntryPoint"])[1]
1274 elif Symbol
in NotifyList
:
1282 elif "Memory" in Type
:
1288 Value
= "TOM" + Value
1290 FileWrite(File
, "%s %-16s %s" % (Symbol
, Value
, Name
))
1293 # Generate report for the prediction part
1295 # This function generate the predicted fixed address report for a module or
1296 # predicted module execution order for a platform.
1297 # If the input Guid is None, then, it generates the predicted module execution order;
1298 # otherwise it generated the module fixed loading address for the module specified by
1301 # @param self The object pointer
1302 # @param File The file object for report
1303 # @param Guid The module Guid value.
1305 def GenerateReport(self
, File
, Guid
):
1307 self
._GenerateFixedAddressReport
(File
, Guid
.upper(), [])
1309 self
._GenerateExecutionOrderReport
(File
)
1312 # Reports FD region information
1314 # This class reports the FD subsection in the build report file.
1315 # It collects region information of platform flash device.
1316 # If the region is a firmware volume, it lists the set of modules
1317 # and its space information; otherwise, it only lists its region name,
1318 # base address and size in its sub-section header.
1319 # If there are nesting FVs, the nested FVs will list immediate after
1320 # this FD region subsection
1322 class FdRegionReport(object):
1324 # Discover all the nested FV name list.
1326 # This is an internal worker function to discover the all the nested FV information
1327 # in the parent firmware volume. It uses deep first search algorithm recursively to
1328 # find all the FV list name and append them to the list.
1330 # @param self The object pointer
1331 # @param FvName The name of current firmware file system
1332 # @param Wa Workspace context information
1334 def _DiscoverNestedFvList(self
, FvName
, Wa
):
1335 FvDictKey
=FvName
.upper()
1336 if FvDictKey
in Wa
.FdfProfile
.FvDict
:
1337 for Ffs
in Wa
.FdfProfile
.FvDict
[FvName
.upper()].FfsList
:
1338 for Section
in Ffs
.SectionList
:
1340 for FvSection
in Section
.SectionList
:
1341 if FvSection
.FvName
in self
.FvList
:
1343 self
._GuidsDb
[Ffs
.NameGuid
.upper()] = FvSection
.FvName
1344 self
.FvList
.append(FvSection
.FvName
)
1345 self
.FvInfo
[FvSection
.FvName
] = ("Nested FV", 0, 0)
1346 self
._DiscoverNestedFvList
(FvSection
.FvName
, Wa
)
1347 except AttributeError:
1351 # Constructor function for class FdRegionReport
1353 # This constructor function generates FdRegionReport object for a specified FdRegion.
1354 # If the FdRegion is a firmware volume, it will recursively find all its nested Firmware
1355 # volume list. This function also collects GUID map in order to dump module identification
1356 # in the final report.
1358 # @param self: The object pointer
1359 # @param FdRegion The current FdRegion object
1360 # @param Wa Workspace context information
1362 def __init__(self
, FdRegion
, Wa
):
1363 self
.Type
= FdRegion
.RegionType
1364 self
.BaseAddress
= FdRegion
.Offset
1365 self
.Size
= FdRegion
.Size
1369 self
._FvDir
= Wa
.FvDir
1372 # If the input FdRegion is not a firmware volume,
1375 if self
.Type
!= "FV":
1379 # Find all nested FVs in the FdRegion
1381 for FvName
in FdRegion
.RegionDataList
:
1382 if FvName
in self
.FvList
:
1384 self
.FvList
.append(FvName
)
1385 self
.FvInfo
[FvName
] = ("Fd Region", self
.BaseAddress
, self
.Size
)
1386 self
._DiscoverNestedFvList
(FvName
, Wa
)
1390 # Collect PCDs declared in DEC files.
1392 for Pa
in Wa
.AutoGenObjectList
:
1393 for Package
in Pa
.PackageList
:
1394 for (TokenCName
, TokenSpaceGuidCName
, DecType
) in Package
.Pcds
:
1395 DecDefaultValue
= Package
.Pcds
[TokenCName
, TokenSpaceGuidCName
, DecType
].DefaultValue
1396 PlatformPcds
[(TokenCName
, TokenSpaceGuidCName
)] = DecDefaultValue
1398 # Collect PCDs defined in DSC file
1400 for arch
in Wa
.ArchList
:
1401 Platform
= Wa
.BuildDatabase
[Wa
.MetaFile
, arch
]
1402 for (TokenCName
, TokenSpaceGuidCName
) in Platform
.Pcds
:
1403 DscDefaultValue
= Platform
.Pcds
[(TokenCName
, TokenSpaceGuidCName
)].DefaultValue
1404 PlatformPcds
[(TokenCName
, TokenSpaceGuidCName
)] = DscDefaultValue
1407 # Add PEI and DXE a priori files GUIDs defined in PI specification.
1409 self
._GuidsDb
["1B45CC0A-156A-428A-AF62-49864DA0E6E6"] = "PEI Apriori"
1410 self
._GuidsDb
["FC510EE7-FFDC-11D4-BD41-0080C73C8881"] = "DXE Apriori"
1412 # Add ACPI table storage file
1414 self
._GuidsDb
["7E374E25-8E01-4FEE-87F2-390C23C606CD"] = "ACPI table storage"
1416 for Pa
in Wa
.AutoGenObjectList
:
1417 for ModuleKey
in Pa
.Platform
.Modules
:
1418 M
= Pa
.Platform
.Modules
[ModuleKey
].M
1419 InfPath
= mws
.join(Wa
.WorkspaceDir
, M
.MetaFile
.File
)
1420 self
._GuidsDb
[M
.Guid
.upper()] = "%s (%s)" % (M
.Module
.BaseName
, InfPath
)
1423 # Collect the GUID map in the FV firmware volume
1425 for FvName
in self
.FvList
:
1426 FvDictKey
=FvName
.upper()
1427 if FvDictKey
in Wa
.FdfProfile
.FvDict
:
1428 for Ffs
in Wa
.FdfProfile
.FvDict
[FvName
.upper()].FfsList
:
1431 # collect GUID map for binary EFI file in FDF file.
1433 Guid
= Ffs
.NameGuid
.upper()
1434 Match
= gPcdGuidPattern
.match(Ffs
.NameGuid
)
1436 PcdTokenspace
= Match
.group(1)
1437 PcdToken
= Match
.group(2)
1438 if (PcdToken
, PcdTokenspace
) in PlatformPcds
:
1439 GuidValue
= PlatformPcds
[(PcdToken
, PcdTokenspace
)]
1440 Guid
= GuidStructureByteArrayToGuidString(GuidValue
).upper()
1441 for Section
in Ffs
.SectionList
:
1443 ModuleSectFile
= mws
.join(Wa
.WorkspaceDir
, Section
.SectFileName
)
1444 self
._GuidsDb
[Guid
] = ModuleSectFile
1445 except AttributeError:
1447 except AttributeError:
1452 # Internal worker function to generate report for the FD region
1454 # This internal worker function to generate report for the FD region.
1455 # It the type is firmware volume, it lists offset and module identification.
1457 # @param self The object pointer
1458 # @param File The file object for report
1459 # @param Title The title for the FD subsection
1460 # @param BaseAddress The base address for the FD region
1461 # @param Size The size of the FD region
1462 # @param FvName The FV name if the FD region is a firmware volume
1464 def _GenerateReport(self
, File
, Title
, Type
, BaseAddress
, Size
=0, FvName
=None):
1465 FileWrite(File
, gSubSectionStart
)
1466 FileWrite(File
, Title
)
1467 FileWrite(File
, "Type: %s" % Type
)
1468 FileWrite(File
, "Base Address: 0x%X" % BaseAddress
)
1470 if self
.Type
== "FV":
1474 FvReportFileName
= os
.path
.join(self
._FvDir
, FvName
+ ".Fv.txt")
1477 # Collect size info in the firmware volume.
1479 FvReport
= open(FvReportFileName
).read()
1480 Match
= gFvTotalSizePattern
.search(FvReport
)
1482 FvTotalSize
= int(Match
.group(1), 16)
1483 Match
= gFvTakenSizePattern
.search(FvReport
)
1485 FvTakenSize
= int(Match
.group(1), 16)
1486 FvFreeSize
= FvTotalSize
- FvTakenSize
1488 # Write size information to the report file.
1490 FileWrite(File
, "Size: 0x%X (%.0fK)" % (FvTotalSize
, FvTotalSize
/ 1024.0))
1491 FileWrite(File
, "Fv Name: %s (%.1f%% Full)" % (FvName
, FvTakenSize
* 100.0 / FvTotalSize
))
1492 FileWrite(File
, "Occupied Size: 0x%X (%.0fK)" % (FvTakenSize
, FvTakenSize
/ 1024.0))
1493 FileWrite(File
, "Free Size: 0x%X (%.0fK)" % (FvFreeSize
, FvFreeSize
/ 1024.0))
1494 FileWrite(File
, "Offset Module")
1495 FileWrite(File
, gSubSectionSep
)
1497 # Write module offset and module identification to the report file.
1500 for Match
in gOffsetGuidPattern
.finditer(FvReport
):
1501 Guid
= Match
.group(2).upper()
1502 OffsetInfo
[Match
.group(1)] = self
._GuidsDb
.get(Guid
, Guid
)
1503 OffsetList
= OffsetInfo
.keys()
1505 for Offset
in OffsetList
:
1506 FileWrite (File
, "%s %s" % (Offset
, OffsetInfo
[Offset
]))
1508 EdkLogger
.warn(None, "Fail to read report file", FvReportFileName
)
1510 FileWrite(File
, "Size: 0x%X (%.0fK)" % (Size
, Size
/ 1024.0))
1511 FileWrite(File
, gSubSectionEnd
)
1514 # Generate report for the FD region
1516 # This function generates report for the FD region.
1518 # @param self The object pointer
1519 # @param File The file object for report
1521 def GenerateReport(self
, File
):
1522 if (len(self
.FvList
) > 0):
1523 for FvItem
in self
.FvList
:
1524 Info
= self
.FvInfo
[FvItem
]
1525 self
._GenerateReport
(File
, Info
[0], "FV", Info
[1], Info
[2], FvItem
)
1527 self
._GenerateReport
(File
, "FD Region", self
.Type
, self
.BaseAddress
, self
.Size
)
1530 # Reports FD information
1532 # This class reports the FD section in the build report file.
1533 # It collects flash device information for a platform.
1535 class FdReport(object):
1537 # Constructor function for class FdReport
1539 # This constructor function generates FdReport object for a specified
1542 # @param self The object pointer
1543 # @param Fd The current Firmware device object
1544 # @param Wa Workspace context information
1546 def __init__(self
, Fd
, Wa
):
1547 self
.FdName
= Fd
.FdUiName
1548 self
.BaseAddress
= Fd
.BaseAddress
1550 self
.FdRegionList
= [FdRegionReport(FdRegion
, Wa
) for FdRegion
in Fd
.RegionList
]
1551 self
.FvPath
= os
.path
.join(Wa
.BuildDir
, "FV")
1552 self
.VpdFilePath
= os
.path
.join(self
.FvPath
, "%s.map" % Wa
.Platform
.VpdToolGuid
)
1553 self
.VPDBaseAddress
= 0
1555 self
.VPDInfoList
= []
1556 for index
, FdRegion
in enumerate(Fd
.RegionList
):
1557 if str(FdRegion
.RegionType
) is 'FILE' and Wa
.Platform
.VpdToolGuid
in str(FdRegion
.RegionDataList
):
1558 self
.VPDBaseAddress
= self
.FdRegionList
[index
].BaseAddress
1559 self
.VPDSize
= self
.FdRegionList
[index
].Size
1562 if os
.path
.isfile(self
.VpdFilePath
):
1563 fd
= open(self
.VpdFilePath
, "r")
1564 Lines
= fd
.readlines()
1567 if len(Line
) == 0 or Line
.startswith("#"):
1570 PcdName
, SkuId
, Offset
, Size
, Value
= Line
.split("#")[0].split("|")
1571 PcdName
, SkuId
, Offset
, Size
, Value
= PcdName
.strip(), SkuId
.strip(), Offset
.strip(), Size
.strip(), Value
.strip()
1572 if Offset
.lower().startswith('0x'):
1573 Offset
= '0x%08X' % (int(Offset
, 16) + self
.VPDBaseAddress
)
1575 Offset
= '0x%08X' % (int(Offset
, 10) + self
.VPDBaseAddress
)
1576 self
.VPDInfoList
.append("%s | %s | %s | %s | %s" % (PcdName
, SkuId
, Offset
, Size
, Value
))
1578 EdkLogger
.error("BuildReport", CODE_ERROR
, "Fail to parse VPD information file %s" % self
.VpdFilePath
)
1582 # Generate report for the firmware device.
1584 # This function generates report for the firmware device.
1586 # @param self The object pointer
1587 # @param File The file object for report
1589 def GenerateReport(self
, File
):
1590 FileWrite(File
, gSectionStart
)
1591 FileWrite(File
, "Firmware Device (FD)")
1592 FileWrite(File
, "FD Name: %s" % self
.FdName
)
1593 FileWrite(File
, "Base Address: %s" % self
.BaseAddress
)
1594 FileWrite(File
, "Size: 0x%X (%.0fK)" % (self
.Size
, self
.Size
/ 1024.0))
1595 if len(self
.FdRegionList
) > 0:
1596 FileWrite(File
, gSectionSep
)
1597 for FdRegionItem
in self
.FdRegionList
:
1598 FdRegionItem
.GenerateReport(File
)
1600 if len(self
.VPDInfoList
) > 0:
1601 FileWrite(File
, gSubSectionStart
)
1602 FileWrite(File
, "FD VPD Region")
1603 FileWrite(File
, "Base Address: 0x%X" % self
.VPDBaseAddress
)
1604 FileWrite(File
, "Size: 0x%X (%.0fK)" % (self
.VPDSize
, self
.VPDSize
/ 1024.0))
1605 FileWrite(File
, gSubSectionSep
)
1606 for item
in self
.VPDInfoList
:
1607 FileWrite(File
, item
)
1608 FileWrite(File
, gSubSectionEnd
)
1609 FileWrite(File
, gSectionEnd
)
1614 # Reports platform information
1616 # This class reports the whole platform information
1618 class PlatformReport(object):
1620 # Constructor function for class PlatformReport
1622 # This constructor function generates PlatformReport object a platform build.
1623 # It generates report for platform summary, flash, global PCDs and detailed
1624 # module information for modules involved in platform build.
1626 # @param self The object pointer
1627 # @param Wa Workspace context information
1628 # @param MaList The list of modules in the platform build
1630 def __init__(self
, Wa
, MaList
, ReportType
):
1631 self
._WorkspaceDir
= Wa
.WorkspaceDir
1632 self
.PlatformName
= Wa
.Name
1633 self
.PlatformDscPath
= Wa
.Platform
1634 self
.Architectures
= " ".join(Wa
.ArchList
)
1635 self
.ToolChain
= Wa
.ToolChain
1636 self
.Target
= Wa
.BuildTarget
1637 self
.OutputPath
= os
.path
.join(Wa
.WorkspaceDir
, Wa
.OutputDir
)
1638 self
.BuildEnvironment
= platform
.platform()
1640 self
.PcdReport
= None
1641 if "PCD" in ReportType
:
1642 self
.PcdReport
= PcdReport(Wa
)
1644 self
.FdReportList
= []
1645 if "FLASH" in ReportType
and Wa
.FdfProfile
and MaList
== None:
1646 for Fd
in Wa
.FdfProfile
.FdDict
:
1647 self
.FdReportList
.append(FdReport(Wa
.FdfProfile
.FdDict
[Fd
], Wa
))
1649 self
.PredictionReport
= None
1650 if "FIXED_ADDRESS" in ReportType
or "EXECUTION_ORDER" in ReportType
:
1651 self
.PredictionReport
= PredictionReport(Wa
)
1653 self
.DepexParser
= None
1654 if "DEPEX" in ReportType
:
1655 self
.DepexParser
= DepexParser(Wa
)
1657 self
.ModuleReportList
= []
1659 self
._IsModuleBuild
= True
1661 self
.ModuleReportList
.append(ModuleReport(Ma
, ReportType
))
1663 self
._IsModuleBuild
= False
1664 for Pa
in Wa
.AutoGenObjectList
:
1665 ModuleAutoGenList
= []
1666 for ModuleKey
in Pa
.Platform
.Modules
:
1667 ModuleAutoGenList
.append(Pa
.Platform
.Modules
[ModuleKey
].M
)
1668 if GlobalData
.gFdfParser
!= None:
1669 if Pa
.Arch
in GlobalData
.gFdfParser
.Profile
.InfDict
:
1670 INFList
= GlobalData
.gFdfParser
.Profile
.InfDict
[Pa
.Arch
]
1671 for InfName
in INFList
:
1672 InfClass
= PathClass(NormPath(InfName
), Wa
.WorkspaceDir
, Pa
.Arch
)
1673 Ma
= ModuleAutoGen(Wa
, InfClass
, Pa
.BuildTarget
, Pa
.ToolChain
, Pa
.Arch
, Wa
.MetaFile
)
1676 if Ma
not in ModuleAutoGenList
:
1677 ModuleAutoGenList
.append(Ma
)
1678 for MGen
in ModuleAutoGenList
:
1679 self
.ModuleReportList
.append(ModuleReport(MGen
, ReportType
))
1684 # Generate report for the whole platform.
1686 # This function generates report for platform information.
1687 # It comprises of platform summary, global PCD, flash and
1688 # module list sections.
1690 # @param self The object pointer
1691 # @param File The file object for report
1692 # @param BuildDuration The total time to build the modules
1693 # @param ReportType The kind of report items in the final report file
1695 def GenerateReport(self
, File
, BuildDuration
, ReportType
):
1696 FileWrite(File
, "Platform Summary")
1697 FileWrite(File
, "Platform Name: %s" % self
.PlatformName
)
1698 FileWrite(File
, "Platform DSC Path: %s" % self
.PlatformDscPath
)
1699 FileWrite(File
, "Architectures: %s" % self
.Architectures
)
1700 FileWrite(File
, "Tool Chain: %s" % self
.ToolChain
)
1701 FileWrite(File
, "Target: %s" % self
.Target
)
1702 FileWrite(File
, "Output Path: %s" % self
.OutputPath
)
1703 FileWrite(File
, "Build Environment: %s" % self
.BuildEnvironment
)
1704 FileWrite(File
, "Build Duration: %s" % BuildDuration
)
1705 FileWrite(File
, "Report Content: %s" % ", ".join(ReportType
))
1707 if GlobalData
.MixedPcd
:
1708 FileWrite(File
, gSectionStart
)
1709 FileWrite(File
, "The following PCDs use different access methods:")
1710 FileWrite(File
, gSectionSep
)
1711 for PcdItem
in GlobalData
.MixedPcd
:
1712 FileWrite(File
, "%s.%s" % (str(PcdItem
[1]), str(PcdItem
[0])))
1713 FileWrite(File
, gSectionEnd
)
1715 if not self
._IsModuleBuild
:
1716 if "PCD" in ReportType
:
1717 self
.PcdReport
.GenerateReport(File
, None)
1719 if "FLASH" in ReportType
:
1720 for FdReportListItem
in self
.FdReportList
:
1721 FdReportListItem
.GenerateReport(File
)
1723 for ModuleReportItem
in self
.ModuleReportList
:
1724 ModuleReportItem
.GenerateReport(File
, self
.PcdReport
, self
.PredictionReport
, self
.DepexParser
, ReportType
)
1726 if not self
._IsModuleBuild
:
1727 if "EXECUTION_ORDER" in ReportType
:
1728 self
.PredictionReport
.GenerateReport(File
, None)
1730 ## BuildReport class
1732 # This base class contain the routines to collect data and then
1733 # applies certain format to the output report
1735 class BuildReport(object):
1737 # Constructor function for class BuildReport
1739 # This constructor function generates BuildReport object a platform build.
1740 # It generates report for platform summary, flash, global PCDs and detailed
1741 # module information for modules involved in platform build.
1743 # @param self The object pointer
1744 # @param ReportFile The file name to save report file
1745 # @param ReportType The kind of report items in the final report file
1747 def __init__(self
, ReportFile
, ReportType
):
1748 self
.ReportFile
= ReportFile
1750 self
.ReportList
= []
1751 self
.ReportType
= []
1753 for ReportTypeItem
in ReportType
:
1754 if ReportTypeItem
not in self
.ReportType
:
1755 self
.ReportType
.append(ReportTypeItem
)
1757 self
.ReportType
= ["PCD", "LIBRARY", "BUILD_FLAGS", "DEPEX", "HASH", "FLASH", "FIXED_ADDRESS"]
1759 # Adds platform report to the list
1761 # This function adds a platform report to the final report list.
1763 # @param self The object pointer
1764 # @param Wa Workspace context information
1765 # @param MaList The list of modules in the platform build
1767 def AddPlatformReport(self
, Wa
, MaList
=None):
1769 self
.ReportList
.append((Wa
, MaList
))
1772 # Generates the final report.
1774 # This function generates platform build report. It invokes GenerateReport()
1775 # method for every platform report in the list.
1777 # @param self The object pointer
1778 # @param BuildDuration The total time to build the modules
1780 def GenerateReport(self
, BuildDuration
):
1784 for (Wa
, MaList
) in self
.ReportList
:
1785 PlatformReport(Wa
, MaList
, self
.ReportType
).GenerateReport(File
, BuildDuration
, self
.ReportType
)
1786 Content
= FileLinesSplit(File
.getvalue(), gLineMaxLength
)
1787 SaveFileOnChange(self
.ReportFile
, Content
, True)
1788 EdkLogger
.quiet("Build report can be found at %s" % os
.path
.abspath(self
.ReportFile
))
1790 EdkLogger
.error(None, FILE_WRITE_FAILURE
, ExtraData
=self
.ReportFile
)
1792 EdkLogger
.error("BuildReport", CODE_ERROR
, "Unknown fatal error when generating build report", ExtraData
=self
.ReportFile
, RaiseError
=False)
1793 EdkLogger
.quiet("(Python %s on %s\n%s)" % (platform
.python_version(), sys
.platform
, traceback
.format_exc()))
1796 # This acts like the main() function for the script, unless it is 'import'ed into another script.
1797 if __name__
== '__main__':