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 if len(self
.LibraryList
) > 0:
322 FileWrite(File
, gSubSectionStart
)
323 FileWrite(File
, TAB_BRG_LIBRARY
)
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
):
415 FileWrite(File
, gSubSectionStart
)
416 if os
.path
.isfile(self
._DepexFileName
):
418 DepexStatements
= GlobalDepexParser
.ParseDepexFile(self
._DepexFileName
)
419 FileWrite(File
, "Final Dependency Expression (DEPEX) Instructions")
420 for DepexStatement
in DepexStatements
:
421 FileWrite(File
, " %s" % DepexStatement
)
422 FileWrite(File
, gSubSectionSep
)
424 EdkLogger
.warn(None, "Dependency expression file is corrupted", self
._DepexFileName
)
426 FileWrite(File
, "Dependency Expression (DEPEX) from %s" % self
.Source
)
428 if self
.Source
== "INF":
429 FileWrite(File
, "%s" % self
.Depex
, True)
430 FileWrite(File
, gSubSectionSep
)
431 FileWrite(File
, "From Module INF: %s" % self
.ModuleDepex
, True)
432 FileWrite(File
, "From Library INF: %s" % self
.LibraryDepex
, True)
434 FileWrite(File
, "%s" % self
.Depex
)
435 FileWrite(File
, gSubSectionEnd
)
438 # Reports dependency expression information
440 # This class reports the module build flags subsection in the build report file.
442 class BuildFlagsReport(object):
444 # Constructor function for class BuildFlagsReport
446 # This constructor function generates BuildFlagsReport object for
447 # a module. It reports the build tool chain tag and all relevant
448 # build flags to build the module.
450 # @param self The object pointer
451 # @param M Module context information
453 def __init__(self
, M
):
456 # Add build flags according to source file extension so that
457 # irrelevant ones can be filtered out.
459 for Source
in M
.SourceFileList
:
460 Ext
= os
.path
.splitext(Source
.File
)[1].lower()
461 if Ext
in [".c", ".cc", ".cpp"]:
462 BuildOptions
["CC"] = 1
463 elif Ext
in [".s", ".asm"]:
464 BuildOptions
["PP"] = 1
465 BuildOptions
["ASM"] = 1
466 elif Ext
in [".vfr"]:
467 BuildOptions
["VFRPP"] = 1
468 BuildOptions
["VFR"] = 1
469 elif Ext
in [".dxs"]:
470 BuildOptions
["APP"] = 1
471 BuildOptions
["CC"] = 1
472 elif Ext
in [".asl"]:
473 BuildOptions
["ASLPP"] = 1
474 BuildOptions
["ASL"] = 1
475 elif Ext
in [".aslc"]:
476 BuildOptions
["ASLCC"] = 1
477 BuildOptions
["ASLDLINK"] = 1
478 BuildOptions
["CC"] = 1
479 elif Ext
in [".asm16"]:
480 BuildOptions
["ASMLINK"] = 1
481 BuildOptions
["SLINK"] = 1
482 BuildOptions
["DLINK"] = 1
485 # Save module build flags.
487 self
.ToolChainTag
= M
.ToolChain
489 for Tool
in BuildOptions
:
490 self
.BuildFlags
[Tool
+ "_FLAGS"] = M
.BuildOption
.get(Tool
, {}).get("FLAGS", "")
493 # Generate report for module build flags information
495 # This function generates report for the module build flags expression.
497 # @param self The object pointer
498 # @param File The file object for report
500 def GenerateReport(self
, File
):
501 FileWrite(File
, gSubSectionStart
)
502 FileWrite(File
, "Build Flags")
503 FileWrite(File
, "Tool Chain Tag: %s" % self
.ToolChainTag
)
504 for Tool
in self
.BuildFlags
:
505 FileWrite(File
, gSubSectionSep
)
506 FileWrite(File
, "%s = %s" % (Tool
, self
.BuildFlags
[Tool
]), True)
508 FileWrite(File
, gSubSectionEnd
)
512 # Reports individual module information
514 # This class reports the module section in the build report file.
515 # It comprises of module summary, module PCD, library, dependency expression,
516 # build flags sections.
518 class ModuleReport(object):
520 # Constructor function for class ModuleReport
522 # This constructor function generates ModuleReport object for
523 # a separate module in a platform build.
525 # @param self The object pointer
526 # @param M Module context information
527 # @param ReportType The kind of report items in the final report file
529 def __init__(self
, M
, ReportType
):
530 self
.ModuleName
= M
.Module
.BaseName
531 self
.ModuleInfPath
= M
.MetaFile
.File
532 self
.FileGuid
= M
.Guid
534 self
.BuildTimeStamp
= None
538 ModuleType
= M
.ModuleType
540 ModuleType
= gComponentType2ModuleType
.get(M
.ComponentType
, "")
542 # If a module complies to PI 1.1, promote Module type to "SMM_DRIVER"
544 if ModuleType
== "DXE_SMM_DRIVER":
545 PiSpec
= M
.Module
.Specification
.get("PI_SPECIFICATION_VERSION", "0x00010000")
546 if int(PiSpec
, 0) >= 0x0001000A:
547 ModuleType
= "SMM_DRIVER"
548 self
.DriverType
= gDriverTypeMap
.get(ModuleType
, "0x2 (FREE_FORM)")
549 self
.UefiSpecVersion
= M
.Module
.Specification
.get("UEFI_SPECIFICATION_VERSION", "")
550 self
.PiSpecVersion
= M
.Module
.Specification
.get("PI_SPECIFICATION_VERSION", "")
551 self
.PciDeviceId
= M
.Module
.Defines
.get("PCI_DEVICE_ID", "")
552 self
.PciVendorId
= M
.Module
.Defines
.get("PCI_VENDOR_ID", "")
553 self
.PciClassCode
= M
.Module
.Defines
.get("PCI_CLASS_CODE", "")
555 self
._BuildDir
= M
.BuildDir
556 self
.ModulePcdSet
= {}
557 if "PCD" in ReportType
:
559 # Collect all module used PCD set: module INF referenced directly or indirectly.
560 # It also saves module INF default values of them in case they exist.
562 for Pcd
in M
.ModulePcdList
+ M
.LibraryPcdList
:
563 self
.ModulePcdSet
.setdefault((Pcd
.TokenCName
, Pcd
.TokenSpaceGuidCName
, Pcd
.Type
), (Pcd
.InfDefaultValue
, Pcd
.DefaultValue
))
565 self
.LibraryReport
= None
566 if "LIBRARY" in ReportType
:
567 self
.LibraryReport
= LibraryReport(M
)
569 self
.DepexReport
= None
570 if "DEPEX" in ReportType
:
571 self
.DepexReport
= DepexReport(M
)
573 if "BUILD_FLAGS" in ReportType
:
574 self
.BuildFlagsReport
= BuildFlagsReport(M
)
578 # Generate report for module information
580 # This function generates report for separate module expression
581 # in a platform build.
583 # @param self The object pointer
584 # @param File The file object for report
585 # @param GlobalPcdReport The platform global PCD report object
586 # @param GlobalPredictionReport The platform global Prediction report object
587 # @param GlobalDepexParser The platform global Dependency expression parser object
588 # @param ReportType The kind of report items in the final report file
590 def GenerateReport(self
, File
, GlobalPcdReport
, GlobalPredictionReport
, GlobalDepexParser
, ReportType
):
591 FileWrite(File
, gSectionStart
)
593 FwReportFileName
= os
.path
.join(self
._BuildDir
, "DEBUG", self
.ModuleName
+ ".txt")
594 if os
.path
.isfile(FwReportFileName
):
596 FileContents
= open(FwReportFileName
).read()
597 Match
= gModuleSizePattern
.search(FileContents
)
599 self
.Size
= int(Match
.group(1))
601 Match
= gTimeStampPattern
.search(FileContents
)
603 self
.BuildTimeStamp
= datetime
.fromtimestamp(int(Match
.group(1)))
605 EdkLogger
.warn(None, "Fail to read report file", FwReportFileName
)
607 if "HASH" in ReportType
:
608 OutputDir
= os
.path
.join(self
._BuildDir
, "OUTPUT")
609 DefaultEFIfile
= os
.path
.join(OutputDir
, self
.ModuleName
+ ".efi")
610 if os
.path
.isfile(DefaultEFIfile
):
611 Tempfile
= os
.path
.join(OutputDir
, self
.ModuleName
+ "_hash.tmp")
612 # rebase the efi image since its base address may not zero
613 cmd
= ["GenFw", "--rebase", str(0), "-o", Tempfile
, DefaultEFIfile
]
615 PopenObject
= subprocess
.Popen(' '.join(cmd
), stdout
=subprocess
.PIPE
, stderr
=subprocess
.PIPE
, shell
=True)
617 EdkLogger
.error("GenFw", COMMAND_FAILURE
, ExtraData
="%s: %s" % (str(X
), cmd
[0]))
618 EndOfProcedure
= threading
.Event()
619 EndOfProcedure
.clear()
620 if PopenObject
.stderr
:
621 StdErrThread
= threading
.Thread(target
=ReadMessage
, args
=(PopenObject
.stderr
, EdkLogger
.quiet
, EndOfProcedure
))
622 StdErrThread
.setName("STDERR-Redirector")
623 StdErrThread
.setDaemon(False)
625 # waiting for program exit
627 if PopenObject
.stderr
:
629 if PopenObject
.returncode
!= 0:
630 EdkLogger
.error("GenFw", COMMAND_FAILURE
, "Failed to generate firmware hash image for %s" % (DefaultEFIfile
))
631 if os
.path
.isfile(Tempfile
):
632 self
.Hash
= hashlib
.sha1()
633 buf
= open(Tempfile
, 'rb').read()
634 if self
.Hash
.update(buf
):
635 self
.Hash
= self
.Hash
.update(buf
)
636 self
.Hash
= self
.Hash
.hexdigest()
639 FileWrite(File
, "Module Summary")
640 FileWrite(File
, "Module Name: %s" % self
.ModuleName
)
641 FileWrite(File
, "Module INF Path: %s" % self
.ModuleInfPath
)
642 FileWrite(File
, "File GUID: %s" % self
.FileGuid
)
644 FileWrite(File
, "Size: 0x%X (%.2fK)" % (self
.Size
, self
.Size
/ 1024.0))
646 FileWrite(File
, "SHA1 HASH: %s *%s" % (self
.Hash
, self
.ModuleName
+ ".efi"))
647 if self
.BuildTimeStamp
:
648 FileWrite(File
, "Build Time Stamp: %s" % self
.BuildTimeStamp
)
650 FileWrite(File
, "Driver Type: %s" % self
.DriverType
)
651 if self
.UefiSpecVersion
:
652 FileWrite(File
, "UEFI Spec Version: %s" % self
.UefiSpecVersion
)
653 if self
.PiSpecVersion
:
654 FileWrite(File
, "PI Spec Version: %s" % self
.PiSpecVersion
)
656 FileWrite(File
, "PCI Device ID: %s" % self
.PciDeviceId
)
658 FileWrite(File
, "PCI Vendor ID: %s" % self
.PciVendorId
)
659 if self
.PciClassCode
:
660 FileWrite(File
, "PCI Class Code: %s" % self
.PciClassCode
)
662 FileWrite(File
, gSectionSep
)
664 if "PCD" in ReportType
:
665 GlobalPcdReport
.GenerateReport(File
, self
.ModulePcdSet
)
667 if "LIBRARY" in ReportType
:
668 self
.LibraryReport
.GenerateReport(File
)
670 if "DEPEX" in ReportType
:
671 self
.DepexReport
.GenerateReport(File
, GlobalDepexParser
)
673 if "BUILD_FLAGS" in ReportType
:
674 self
.BuildFlagsReport
.GenerateReport(File
)
676 if "FIXED_ADDRESS" in ReportType
and self
.FileGuid
:
677 GlobalPredictionReport
.GenerateReport(File
, self
.FileGuid
)
679 FileWrite(File
, gSectionEnd
)
681 def ReadMessage(From
, To
, ExitFlag
):
683 # read one line a time
684 Line
= From
.readline()
685 # empty string means "end"
686 if Line
!= None and Line
!= "":
694 # Reports platform and module PCD information
696 # This class reports the platform PCD section and module PCD subsection
697 # in the build report file.
699 class PcdReport(object):
701 # Constructor function for class PcdReport
703 # This constructor function generates PcdReport object a platform build.
704 # It collects the whole PCD database from platform DSC files, platform
705 # flash description file and package DEC files.
707 # @param self The object pointer
708 # @param Wa Workspace context information
710 def __init__(self
, Wa
):
713 self
.ConditionalPcds
= {}
716 self
.FdfPcdSet
= Wa
.FdfProfile
.PcdDict
720 self
.ModulePcdOverride
= {}
721 for Pa
in Wa
.AutoGenObjectList
:
723 # Collect all platform referenced PCDs and grouped them by PCD token space
726 for Pcd
in Pa
.AllPcdList
:
727 PcdList
= self
.AllPcds
.setdefault(Pcd
.TokenSpaceGuidCName
, {}).setdefault(Pcd
.Type
, [])
728 if Pcd
not in PcdList
:
730 if len(Pcd
.TokenCName
) > self
.MaxLen
:
731 self
.MaxLen
= len(Pcd
.TokenCName
)
733 # Collect the PCD defined in DSC/FDF file, but not used in module
735 UnusedPcdFullList
= []
736 for item
in Pa
.Platform
.Pcds
:
737 Pcd
= Pa
.Platform
.Pcds
[item
]
740 for package
in Pa
.PackageList
:
741 for T
in ["FixedAtBuild", "PatchableInModule", "FeatureFlag", "Dynamic", "DynamicEx"]:
742 if (Pcd
.TokenCName
, Pcd
.TokenSpaceGuidCName
, T
) in package
.Pcds
:
745 if not Pcd
.DatumType
:
746 Pcd
.DatumType
= package
.Pcds
[(Pcd
.TokenCName
, Pcd
.TokenSpaceGuidCName
, T
)].DatumType
750 if not Pcd
.DatumType
:
752 # Try to remove Hii and Vpd suffix
753 if PcdType
.startswith("DynamicEx"):
754 PcdType
= "DynamicEx"
755 elif PcdType
.startswith("Dynamic"):
757 for package
in Pa
.PackageList
:
758 if (Pcd
.TokenCName
, Pcd
.TokenSpaceGuidCName
, PcdType
) in package
.Pcds
:
759 Pcd
.DatumType
= package
.Pcds
[(Pcd
.TokenCName
, Pcd
.TokenSpaceGuidCName
, PcdType
)].DatumType
762 PcdList
= self
.AllPcds
.setdefault(Pcd
.TokenSpaceGuidCName
, {}).setdefault(Pcd
.Type
, [])
763 if Pcd
not in PcdList
and Pcd
not in UnusedPcdFullList
:
764 UnusedPcdFullList
.append(Pcd
)
765 if len(Pcd
.TokenCName
) > self
.MaxLen
:
766 self
.MaxLen
= len(Pcd
.TokenCName
)
768 if GlobalData
.gConditionalPcds
:
769 for PcdItem
in GlobalData
.gConditionalPcds
:
771 (TokenSpaceGuidCName
, TokenCName
) = PcdItem
.split('.')
772 if (TokenCName
, TokenSpaceGuidCName
) in Pa
.Platform
.Pcds
.keys():
773 Pcd
= Pa
.Platform
.Pcds
[(TokenCName
, TokenSpaceGuidCName
)]
774 PcdList
= self
.ConditionalPcds
.setdefault(Pcd
.TokenSpaceGuidCName
, {}).setdefault(Pcd
.Type
, [])
775 if Pcd
not in PcdList
:
779 if UnusedPcdFullList
:
780 for Pcd
in UnusedPcdFullList
:
781 if Pcd
.TokenSpaceGuidCName
+ '.' + Pcd
.TokenCName
in GlobalData
.gConditionalPcds
:
783 UnusedPcdList
.append(Pcd
)
785 for Pcd
in UnusedPcdList
:
786 PcdList
= self
.UnusedPcds
.setdefault(Pcd
.TokenSpaceGuidCName
, {}).setdefault(Pcd
.Type
, [])
787 if Pcd
not in PcdList
:
790 for Module
in Pa
.Platform
.Modules
.values():
792 # Collect module override PCDs
794 for ModulePcd
in Module
.M
.ModulePcdList
+ Module
.M
.LibraryPcdList
:
795 TokenCName
= ModulePcd
.TokenCName
796 TokenSpaceGuid
= ModulePcd
.TokenSpaceGuidCName
797 ModuleDefault
= ModulePcd
.DefaultValue
798 ModulePath
= os
.path
.basename(Module
.M
.MetaFile
.File
)
799 self
.ModulePcdOverride
.setdefault((TokenCName
, TokenSpaceGuid
), {})[ModulePath
] = ModuleDefault
803 # Collect PCD DEC default value.
805 self
.DecPcdDefault
= {}
806 for Pa
in Wa
.AutoGenObjectList
:
807 for Package
in Pa
.PackageList
:
808 for (TokenCName
, TokenSpaceGuidCName
, DecType
) in Package
.Pcds
:
809 DecDefaultValue
= Package
.Pcds
[TokenCName
, TokenSpaceGuidCName
, DecType
].DefaultValue
810 self
.DecPcdDefault
.setdefault((TokenCName
, TokenSpaceGuidCName
, DecType
), DecDefaultValue
)
812 # Collect PCDs defined in DSC common section
814 self
.DscPcdDefault
= {}
815 for Arch
in Wa
.ArchList
:
816 Platform
= Wa
.BuildDatabase
[Wa
.MetaFile
, Arch
, Wa
.BuildTarget
, Wa
.ToolChain
]
817 for (TokenCName
, TokenSpaceGuidCName
) in Platform
.Pcds
:
818 DscDefaultValue
= Platform
.Pcds
[(TokenCName
, TokenSpaceGuidCName
)].DefaultValue
820 self
.DscPcdDefault
[(TokenCName
, TokenSpaceGuidCName
)] = DscDefaultValue
822 def GenerateReport(self
, File
, ModulePcdSet
):
823 if self
.ConditionalPcds
:
824 self
.GenerateReportDetail(File
, ModulePcdSet
, 1)
826 self
.GenerateReportDetail(File
, ModulePcdSet
, 2)
827 self
.GenerateReportDetail(File
, ModulePcdSet
)
830 # Generate report for PCD information
832 # This function generates report for separate module expression
833 # in a platform build.
835 # @param self The object pointer
836 # @param File The file object for report
837 # @param ModulePcdSet Set of all PCDs referenced by module or None for
838 # platform PCD report
839 # @param ReportySubType 0 means platform/module PCD report, 1 means Conditional
840 # directives section report, 2 means Unused Pcds section report
841 # @param DscOverridePcds Module DSC override PCDs set
843 def GenerateReportDetail(self
, File
, ModulePcdSet
, ReportSubType
= 0):
844 PcdDict
= self
.AllPcds
845 if ReportSubType
== 1:
846 PcdDict
= self
.ConditionalPcds
847 elif ReportSubType
== 2:
848 PcdDict
= self
.UnusedPcds
850 if ModulePcdSet
== None:
851 FileWrite(File
, gSectionStart
)
852 if ReportSubType
== 1:
853 FileWrite(File
, "Conditional Directives used by the build system")
854 elif ReportSubType
== 2:
855 FileWrite(File
, "PCDs not used by modules or in conditional directives")
857 FileWrite(File
, "Platform Configuration Database Report")
859 FileWrite(File
, " *B - PCD override in the build option")
860 FileWrite(File
, " *P - Platform scoped PCD override in DSC file")
861 FileWrite(File
, " *F - Platform scoped PCD override in FDF file")
862 if not ReportSubType
:
863 FileWrite(File
, " *M - Module scoped PCD override")
864 FileWrite(File
, gSectionSep
)
866 if not ReportSubType
and ModulePcdSet
:
868 # For module PCD sub-section
870 FileWrite(File
, gSubSectionStart
)
871 FileWrite(File
, TAB_BRG_PCD
)
872 FileWrite(File
, gSubSectionSep
)
876 # Group PCD by their token space GUID C Name
879 for Type
in PcdDict
[Key
]:
881 # Group PCD by their usage type
883 TypeName
, DecType
= gPcdTypeMap
.get(Type
, ("", Type
))
884 for Pcd
in PcdDict
[Key
][Type
]:
885 PcdTokenCName
= Pcd
.TokenCName
887 if GlobalData
.MixedPcd
:
888 for PcdKey
in GlobalData
.MixedPcd
:
889 if (Pcd
.TokenCName
, Pcd
.TokenSpaceGuidCName
) in GlobalData
.MixedPcd
[PcdKey
]:
890 PcdTokenCName
= PcdKey
[0]
892 if MixedPcdFlag
and not ModulePcdSet
:
895 # Get PCD default value and their override relationship
897 DecDefaultValue
= self
.DecPcdDefault
.get((Pcd
.TokenCName
, Pcd
.TokenSpaceGuidCName
, DecType
))
898 DscDefaultValue
= self
.DscPcdDefault
.get((Pcd
.TokenCName
, Pcd
.TokenSpaceGuidCName
))
899 DscDefaultValue
= self
.FdfPcdSet
.get((Pcd
.TokenCName
, Key
), DscDefaultValue
)
900 InfDefaultValue
= None
902 PcdValue
= DecDefaultValue
904 PcdValue
= DscDefaultValue
905 if ModulePcdSet
!= None:
906 if (Pcd
.TokenCName
, Pcd
.TokenSpaceGuidCName
, Type
) not in ModulePcdSet
:
908 InfDefault
, PcdValue
= ModulePcdSet
[Pcd
.TokenCName
, Pcd
.TokenSpaceGuidCName
, Type
]
912 BuildOptionMatch
= False
913 if GlobalData
.BuildOptionPcd
:
914 for pcd
in GlobalData
.BuildOptionPcd
:
915 if (Pcd
.TokenSpaceGuidCName
, Pcd
.TokenCName
) == (pcd
[0], pcd
[1]):
917 BuildOptionMatch
= True
921 if ModulePcdSet
== None:
927 if Pcd
.DatumType
in ('UINT8', 'UINT16', 'UINT32', 'UINT64'):
928 PcdValueNumber
= int(PcdValue
.strip(), 0)
929 if DecDefaultValue
== None:
932 DecDefaultValueNumber
= int(DecDefaultValue
.strip(), 0)
933 DecMatch
= (DecDefaultValueNumber
== PcdValueNumber
)
935 if InfDefaultValue
== None:
938 InfDefaultValueNumber
= int(InfDefaultValue
.strip(), 0)
939 InfMatch
= (InfDefaultValueNumber
== PcdValueNumber
)
941 if DscDefaultValue
== None:
944 DscDefaultValueNumber
= int(DscDefaultValue
.strip(), 0)
945 DscMatch
= (DscDefaultValueNumber
== PcdValueNumber
)
947 if DecDefaultValue
== None:
950 DecMatch
= (DecDefaultValue
.strip() == PcdValue
.strip())
952 if InfDefaultValue
== None:
955 InfMatch
= (InfDefaultValue
.strip() == PcdValue
.strip())
957 if DscDefaultValue
== None:
960 DscMatch
= (DscDefaultValue
.strip() == PcdValue
.strip())
963 # Report PCD item according to their override relationship
966 FileWrite(File
, ' *B %-*s: %6s %10s = %-22s' % (self
.MaxLen
, PcdTokenCName
, TypeName
, '(' + Pcd
.DatumType
+ ')', PcdValue
.strip()))
967 elif DecMatch
and InfMatch
:
968 FileWrite(File
, ' %-*s: %6s %10s = %-22s' % (self
.MaxLen
, PcdTokenCName
, TypeName
, '(' + Pcd
.DatumType
+ ')', PcdValue
.strip()))
971 if (Pcd
.TokenCName
, Key
) in self
.FdfPcdSet
:
972 FileWrite(File
, ' *F %-*s: %6s %10s = %-22s' % (self
.MaxLen
, PcdTokenCName
, TypeName
, '(' + Pcd
.DatumType
+ ')', PcdValue
.strip()))
974 FileWrite(File
, ' *P %-*s: %6s %10s = %-22s' % (self
.MaxLen
, PcdTokenCName
, TypeName
, '(' + Pcd
.DatumType
+ ')', PcdValue
.strip()))
976 FileWrite(File
, ' *M %-*s: %6s %10s = %-22s' % (self
.MaxLen
, PcdTokenCName
, TypeName
, '(' + Pcd
.DatumType
+ ')', PcdValue
.strip()))
978 if TypeName
in ('DYNHII', 'DEXHII', 'DYNVPD', 'DEXVPD'):
979 for SkuInfo
in Pcd
.SkuInfoList
.values():
980 if TypeName
in ('DYNHII', 'DEXHII'):
981 FileWrite(File
, '%*s: %s: %s' % (self
.MaxLen
+ 4, SkuInfo
.VariableGuid
, SkuInfo
.VariableName
, SkuInfo
.VariableOffset
))
983 FileWrite(File
, '%*s' % (self
.MaxLen
+ 4, SkuInfo
.VpdOffset
))
985 if not DscMatch
and DscDefaultValue
!= None:
986 FileWrite(File
, ' %*s = %s' % (self
.MaxLen
+ 19, 'DSC DEFAULT', DscDefaultValue
.strip()))
988 if not InfMatch
and InfDefaultValue
!= None:
989 FileWrite(File
, ' %*s = %s' % (self
.MaxLen
+ 19, 'INF DEFAULT', InfDefaultValue
.strip()))
991 if not DecMatch
and DecDefaultValue
!= None:
992 FileWrite(File
, ' %*s = %s' % (self
.MaxLen
+ 19, 'DEC DEFAULT', DecDefaultValue
.strip()))
994 if ModulePcdSet
== None:
995 if not BuildOptionMatch
:
996 ModuleOverride
= self
.ModulePcdOverride
.get((Pcd
.TokenCName
, Pcd
.TokenSpaceGuidCName
), {})
997 for ModulePath
in ModuleOverride
:
998 ModuleDefault
= ModuleOverride
[ModulePath
]
999 if Pcd
.DatumType
in ('UINT8', 'UINT16', 'UINT32', 'UINT64'):
1000 ModulePcdDefaultValueNumber
= int(ModuleDefault
.strip(), 0)
1001 Match
= (ModulePcdDefaultValueNumber
== PcdValueNumber
)
1003 Match
= (ModuleDefault
.strip() == PcdValue
.strip())
1006 FileWrite(File
, ' *M %-*s = %s' % (self
.MaxLen
+ 19, ModulePath
, ModuleDefault
.strip()))
1008 if ModulePcdSet
== None:
1009 FileWrite(File
, gSectionEnd
)
1011 if not ReportSubType
and ModulePcdSet
:
1012 FileWrite(File
, gSubSectionEnd
)
1017 # Reports platform and module Prediction information
1019 # This class reports the platform execution order prediction section and
1020 # module load fixed address prediction subsection in the build report file.
1022 class PredictionReport(object):
1024 # Constructor function for class PredictionReport
1026 # This constructor function generates PredictionReport object for the platform.
1028 # @param self: The object pointer
1029 # @param Wa Workspace context information
1031 def __init__(self
, Wa
):
1032 self
._MapFileName
= os
.path
.join(Wa
.BuildDir
, Wa
.Name
+ ".map")
1033 self
._MapFileParsed
= False
1034 self
._EotToolInvoked
= False
1035 self
._FvDir
= Wa
.FvDir
1036 self
._EotDir
= Wa
.BuildDir
1037 self
._FfsEntryPoint
= {}
1039 self
._SourceList
= []
1040 self
.FixedMapDict
= {}
1045 # Collect all platform reference source files and GUID C Name
1047 for Pa
in Wa
.AutoGenObjectList
:
1048 for Module
in Pa
.LibraryAutoGenList
+ Pa
.ModuleAutoGenList
:
1050 # BASE typed modules are EFI agnostic, so we need not scan
1051 # their source code to find PPI/Protocol produce or consume
1054 if Module
.ModuleType
== "BASE":
1057 # Add module referenced source files
1059 self
._SourceList
.append(str(Module
))
1061 for Source
in Module
.SourceFileList
:
1062 if os
.path
.splitext(str(Source
))[1].lower() == ".c":
1063 self
._SourceList
.append(" " + str(Source
))
1064 FindIncludeFiles(Source
.Path
, Module
.IncludePathList
, IncludeList
)
1065 for IncludeFile
in IncludeList
.values():
1066 self
._SourceList
.append(" " + IncludeFile
)
1068 for Guid
in Module
.PpiList
:
1069 self
._GuidMap
[Guid
] = GuidStructureStringToGuidString(Module
.PpiList
[Guid
])
1070 for Guid
in Module
.ProtocolList
:
1071 self
._GuidMap
[Guid
] = GuidStructureStringToGuidString(Module
.ProtocolList
[Guid
])
1072 for Guid
in Module
.GuidList
:
1073 self
._GuidMap
[Guid
] = GuidStructureStringToGuidString(Module
.GuidList
[Guid
])
1075 if Module
.Guid
and not Module
.IsLibrary
:
1076 EntryPoint
= " ".join(Module
.Module
.ModuleEntryPointList
)
1077 if int(str(Module
.AutoGenVersion
), 0) >= 0x00010005:
1078 RealEntryPoint
= "_ModuleEntryPoint"
1080 RealEntryPoint
= EntryPoint
1081 if EntryPoint
== "_ModuleEntryPoint":
1082 CCFlags
= Module
.BuildOption
.get("CC", {}).get("FLAGS", "")
1083 Match
= gGlueLibEntryPoint
.search(CCFlags
)
1085 EntryPoint
= Match
.group(1)
1087 self
._FfsEntryPoint
[Module
.Guid
.upper()] = (EntryPoint
, RealEntryPoint
)
1091 # Collect platform firmware volume list as the input of EOT.
1095 for Fd
in Wa
.FdfProfile
.FdDict
:
1096 for FdRegion
in Wa
.FdfProfile
.FdDict
[Fd
].RegionList
:
1097 if FdRegion
.RegionType
!= "FV":
1099 for FvName
in FdRegion
.RegionDataList
:
1100 if FvName
in self
._FvList
:
1102 self
._FvList
.append(FvName
)
1103 for Ffs
in Wa
.FdfProfile
.FvDict
[FvName
.upper()].FfsList
:
1104 for Section
in Ffs
.SectionList
:
1106 for FvSection
in Section
.SectionList
:
1107 if FvSection
.FvName
in self
._FvList
:
1109 self
._FvList
.append(FvSection
.FvName
)
1110 except AttributeError:
1115 # Parse platform fixed address map files
1117 # This function parses the platform final fixed address map file to get
1118 # the database of predicted fixed address for module image base, entry point
1121 # @param self: The object pointer
1123 def _ParseMapFile(self
):
1124 if self
._MapFileParsed
:
1126 self
._MapFileParsed
= True
1127 if os
.path
.isfile(self
._MapFileName
):
1129 FileContents
= open(self
._MapFileName
).read()
1130 for Match
in gMapFileItemPattern
.finditer(FileContents
):
1131 AddressType
= Match
.group(1)
1132 BaseAddress
= Match
.group(2)
1133 EntryPoint
= Match
.group(3)
1134 Guid
= Match
.group(4).upper()
1135 List
= self
.FixedMapDict
.setdefault(Guid
, [])
1136 List
.append((AddressType
, BaseAddress
, "*I"))
1137 List
.append((AddressType
, EntryPoint
, "*E"))
1139 EdkLogger
.warn(None, "Cannot open file to read", self
._MapFileName
)
1142 # Invokes EOT tool to get the predicted the execution order.
1144 # This function invokes EOT tool to calculate the predicted dispatch order
1146 # @param self: The object pointer
1148 def _InvokeEotTool(self
):
1149 if self
._EotToolInvoked
:
1152 self
._EotToolInvoked
= True
1154 for FvName
in self
._FvList
:
1155 FvFile
= os
.path
.join(self
._FvDir
, FvName
+ ".Fv")
1156 if os
.path
.isfile(FvFile
):
1157 FvFileList
.append(FvFile
)
1159 if len(FvFileList
) == 0:
1162 # Write source file list and GUID file list to an intermediate file
1163 # as the input for EOT tool and dispatch List as the output file
1166 SourceList
= os
.path
.join(self
._EotDir
, "SourceFile.txt")
1167 GuidList
= os
.path
.join(self
._EotDir
, "GuidList.txt")
1168 DispatchList
= os
.path
.join(self
._EotDir
, "Dispatch.txt")
1170 TempFile
= open(SourceList
, "w+")
1171 for Item
in self
._SourceList
:
1172 FileWrite(TempFile
, Item
)
1174 TempFile
= open(GuidList
, "w+")
1175 for Key
in self
._GuidMap
:
1176 FileWrite(TempFile
, "%s %s" % (Key
, self
._GuidMap
[Key
]))
1180 from Eot
.Eot
import Eot
1183 # Invoke EOT tool and echo its runtime performance
1185 EotStartTime
= time
.time()
1186 Eot(CommandLineOption
=False, SourceFileList
=SourceList
, GuidList
=GuidList
,
1187 FvFileList
=' '.join(FvFileList
), Dispatch
=DispatchList
, IsInit
=True)
1188 EotEndTime
= time
.time()
1189 EotDuration
= time
.strftime("%H:%M:%S", time
.gmtime(int(round(EotEndTime
- EotStartTime
))))
1190 EdkLogger
.quiet("EOT run time: %s\n" % EotDuration
)
1193 # Parse the output of EOT tool
1195 for Line
in open(DispatchList
):
1196 if len(Line
.split()) < 4:
1198 (Guid
, Phase
, FfsName
, FilePath
) = Line
.split()
1199 Symbol
= self
._FfsEntryPoint
.get(Guid
, [FfsName
, ""])[0]
1200 if len(Symbol
) > self
.MaxLen
:
1201 self
.MaxLen
= len(Symbol
)
1202 self
.ItemList
.append((Phase
, Symbol
, FilePath
))
1204 EdkLogger
.quiet("(Python %s on %s\n%s)" % (platform
.python_version(), sys
.platform
, traceback
.format_exc()))
1205 EdkLogger
.warn(None, "Failed to generate execution order prediction report, for some error occurred in executing EOT.")
1209 # Generate platform execution order report
1211 # This function generates the predicted module execution order.
1213 # @param self The object pointer
1214 # @param File The file object for report
1216 def _GenerateExecutionOrderReport(self
, File
):
1217 self
._InvokeEotTool
()
1218 if len(self
.ItemList
) == 0:
1220 FileWrite(File
, gSectionStart
)
1221 FileWrite(File
, "Execution Order Prediction")
1222 FileWrite(File
, "*P PEI phase")
1223 FileWrite(File
, "*D DXE phase")
1224 FileWrite(File
, "*E Module INF entry point name")
1225 FileWrite(File
, "*N Module notification function name")
1227 FileWrite(File
, "Type %-*s %s" % (self
.MaxLen
, "Symbol", "Module INF Path"))
1228 FileWrite(File
, gSectionSep
)
1229 for Item
in self
.ItemList
:
1230 FileWrite(File
, "*%sE %-*s %s" % (Item
[0], self
.MaxLen
, Item
[1], Item
[2]))
1232 FileWrite(File
, gSectionStart
)
1235 # Generate Fixed Address report.
1237 # This function generate the predicted fixed address report for a module
1238 # specified by Guid.
1240 # @param self The object pointer
1241 # @param File The file object for report
1242 # @param Guid The module Guid value.
1243 # @param NotifyList The list of all notify function in a module
1245 def _GenerateFixedAddressReport(self
, File
, Guid
, NotifyList
):
1246 self
._ParseMapFile
()
1247 FixedAddressList
= self
.FixedMapDict
.get(Guid
)
1248 if not FixedAddressList
:
1251 FileWrite(File
, gSubSectionStart
)
1252 FileWrite(File
, "Fixed Address Prediction")
1253 FileWrite(File
, "*I Image Loading Address")
1254 FileWrite(File
, "*E Entry Point Address")
1255 FileWrite(File
, "*N Notification Function Address")
1256 FileWrite(File
, "*F Flash Address")
1257 FileWrite(File
, "*M Memory Address")
1258 FileWrite(File
, "*S SMM RAM Offset")
1259 FileWrite(File
, "TOM Top of Memory")
1261 FileWrite(File
, "Type Address Name")
1262 FileWrite(File
, gSubSectionSep
)
1263 for Item
in FixedAddressList
:
1268 Name
= "(Image Base)"
1269 elif Symbol
== "*E":
1270 Name
= self
._FfsEntryPoint
.get(Guid
, ["", "_ModuleEntryPoint"])[1]
1271 elif Symbol
in NotifyList
:
1279 elif "Memory" in Type
:
1285 Value
= "TOM" + Value
1287 FileWrite(File
, "%s %-16s %s" % (Symbol
, Value
, Name
))
1290 # Generate report for the prediction part
1292 # This function generate the predicted fixed address report for a module or
1293 # predicted module execution order for a platform.
1294 # If the input Guid is None, then, it generates the predicted module execution order;
1295 # otherwise it generated the module fixed loading address for the module specified by
1298 # @param self The object pointer
1299 # @param File The file object for report
1300 # @param Guid The module Guid value.
1302 def GenerateReport(self
, File
, Guid
):
1304 self
._GenerateFixedAddressReport
(File
, Guid
.upper(), [])
1306 self
._GenerateExecutionOrderReport
(File
)
1309 # Reports FD region information
1311 # This class reports the FD subsection in the build report file.
1312 # It collects region information of platform flash device.
1313 # If the region is a firmware volume, it lists the set of modules
1314 # and its space information; otherwise, it only lists its region name,
1315 # base address and size in its sub-section header.
1316 # If there are nesting FVs, the nested FVs will list immediate after
1317 # this FD region subsection
1319 class FdRegionReport(object):
1321 # Discover all the nested FV name list.
1323 # This is an internal worker function to discover the all the nested FV information
1324 # in the parent firmware volume. It uses deep first search algorithm recursively to
1325 # find all the FV list name and append them to the list.
1327 # @param self The object pointer
1328 # @param FvName The name of current firmware file system
1329 # @param Wa Workspace context information
1331 def _DiscoverNestedFvList(self
, FvName
, Wa
):
1332 FvDictKey
=FvName
.upper()
1333 if FvDictKey
in Wa
.FdfProfile
.FvDict
:
1334 for Ffs
in Wa
.FdfProfile
.FvDict
[FvName
.upper()].FfsList
:
1335 for Section
in Ffs
.SectionList
:
1337 for FvSection
in Section
.SectionList
:
1338 if FvSection
.FvName
in self
.FvList
:
1340 self
._GuidsDb
[Ffs
.NameGuid
.upper()] = FvSection
.FvName
1341 self
.FvList
.append(FvSection
.FvName
)
1342 self
.FvInfo
[FvSection
.FvName
] = ("Nested FV", 0, 0)
1343 self
._DiscoverNestedFvList
(FvSection
.FvName
, Wa
)
1344 except AttributeError:
1348 # Constructor function for class FdRegionReport
1350 # This constructor function generates FdRegionReport object for a specified FdRegion.
1351 # If the FdRegion is a firmware volume, it will recursively find all its nested Firmware
1352 # volume list. This function also collects GUID map in order to dump module identification
1353 # in the final report.
1355 # @param self: The object pointer
1356 # @param FdRegion The current FdRegion object
1357 # @param Wa Workspace context information
1359 def __init__(self
, FdRegion
, Wa
):
1360 self
.Type
= FdRegion
.RegionType
1361 self
.BaseAddress
= FdRegion
.Offset
1362 self
.Size
= FdRegion
.Size
1366 self
._FvDir
= Wa
.FvDir
1369 # If the input FdRegion is not a firmware volume,
1372 if self
.Type
!= "FV":
1376 # Find all nested FVs in the FdRegion
1378 for FvName
in FdRegion
.RegionDataList
:
1379 if FvName
in self
.FvList
:
1381 self
.FvList
.append(FvName
)
1382 self
.FvInfo
[FvName
] = ("Fd Region", self
.BaseAddress
, self
.Size
)
1383 self
._DiscoverNestedFvList
(FvName
, Wa
)
1387 # Collect PCDs declared in DEC files.
1389 for Pa
in Wa
.AutoGenObjectList
:
1390 for Package
in Pa
.PackageList
:
1391 for (TokenCName
, TokenSpaceGuidCName
, DecType
) in Package
.Pcds
:
1392 DecDefaultValue
= Package
.Pcds
[TokenCName
, TokenSpaceGuidCName
, DecType
].DefaultValue
1393 PlatformPcds
[(TokenCName
, TokenSpaceGuidCName
)] = DecDefaultValue
1395 # Collect PCDs defined in DSC file
1397 for arch
in Wa
.ArchList
:
1398 Platform
= Wa
.BuildDatabase
[Wa
.MetaFile
, arch
]
1399 for (TokenCName
, TokenSpaceGuidCName
) in Platform
.Pcds
:
1400 DscDefaultValue
= Platform
.Pcds
[(TokenCName
, TokenSpaceGuidCName
)].DefaultValue
1401 PlatformPcds
[(TokenCName
, TokenSpaceGuidCName
)] = DscDefaultValue
1404 # Add PEI and DXE a priori files GUIDs defined in PI specification.
1406 self
._GuidsDb
["1B45CC0A-156A-428A-AF62-49864DA0E6E6"] = "PEI Apriori"
1407 self
._GuidsDb
["FC510EE7-FFDC-11D4-BD41-0080C73C8881"] = "DXE Apriori"
1409 # Add ACPI table storage file
1411 self
._GuidsDb
["7E374E25-8E01-4FEE-87F2-390C23C606CD"] = "ACPI table storage"
1413 for Pa
in Wa
.AutoGenObjectList
:
1414 for ModuleKey
in Pa
.Platform
.Modules
:
1415 M
= Pa
.Platform
.Modules
[ModuleKey
].M
1416 InfPath
= mws
.join(Wa
.WorkspaceDir
, M
.MetaFile
.File
)
1417 self
._GuidsDb
[M
.Guid
.upper()] = "%s (%s)" % (M
.Module
.BaseName
, InfPath
)
1420 # Collect the GUID map in the FV firmware volume
1422 for FvName
in self
.FvList
:
1423 FvDictKey
=FvName
.upper()
1424 if FvDictKey
in Wa
.FdfProfile
.FvDict
:
1425 for Ffs
in Wa
.FdfProfile
.FvDict
[FvName
.upper()].FfsList
:
1428 # collect GUID map for binary EFI file in FDF file.
1430 Guid
= Ffs
.NameGuid
.upper()
1431 Match
= gPcdGuidPattern
.match(Ffs
.NameGuid
)
1433 PcdTokenspace
= Match
.group(1)
1434 PcdToken
= Match
.group(2)
1435 if (PcdToken
, PcdTokenspace
) in PlatformPcds
:
1436 GuidValue
= PlatformPcds
[(PcdToken
, PcdTokenspace
)]
1437 Guid
= GuidStructureByteArrayToGuidString(GuidValue
).upper()
1438 for Section
in Ffs
.SectionList
:
1440 ModuleSectFile
= mws
.join(Wa
.WorkspaceDir
, Section
.SectFileName
)
1441 self
._GuidsDb
[Guid
] = ModuleSectFile
1442 except AttributeError:
1444 except AttributeError:
1449 # Internal worker function to generate report for the FD region
1451 # This internal worker function to generate report for the FD region.
1452 # It the type is firmware volume, it lists offset and module identification.
1454 # @param self The object pointer
1455 # @param File The file object for report
1456 # @param Title The title for the FD subsection
1457 # @param BaseAddress The base address for the FD region
1458 # @param Size The size of the FD region
1459 # @param FvName The FV name if the FD region is a firmware volume
1461 def _GenerateReport(self
, File
, Title
, Type
, BaseAddress
, Size
=0, FvName
=None):
1462 FileWrite(File
, gSubSectionStart
)
1463 FileWrite(File
, Title
)
1464 FileWrite(File
, "Type: %s" % Type
)
1465 FileWrite(File
, "Base Address: 0x%X" % BaseAddress
)
1467 if self
.Type
== "FV":
1471 FvReportFileName
= os
.path
.join(self
._FvDir
, FvName
+ ".Fv.txt")
1474 # Collect size info in the firmware volume.
1476 FvReport
= open(FvReportFileName
).read()
1477 Match
= gFvTotalSizePattern
.search(FvReport
)
1479 FvTotalSize
= int(Match
.group(1), 16)
1480 Match
= gFvTakenSizePattern
.search(FvReport
)
1482 FvTakenSize
= int(Match
.group(1), 16)
1483 FvFreeSize
= FvTotalSize
- FvTakenSize
1485 # Write size information to the report file.
1487 FileWrite(File
, "Size: 0x%X (%.0fK)" % (FvTotalSize
, FvTotalSize
/ 1024.0))
1488 FileWrite(File
, "Fv Name: %s (%.1f%% Full)" % (FvName
, FvTakenSize
* 100.0 / FvTotalSize
))
1489 FileWrite(File
, "Occupied Size: 0x%X (%.0fK)" % (FvTakenSize
, FvTakenSize
/ 1024.0))
1490 FileWrite(File
, "Free Size: 0x%X (%.0fK)" % (FvFreeSize
, FvFreeSize
/ 1024.0))
1491 FileWrite(File
, "Offset Module")
1492 FileWrite(File
, gSubSectionSep
)
1494 # Write module offset and module identification to the report file.
1497 for Match
in gOffsetGuidPattern
.finditer(FvReport
):
1498 Guid
= Match
.group(2).upper()
1499 OffsetInfo
[Match
.group(1)] = self
._GuidsDb
.get(Guid
, Guid
)
1500 OffsetList
= OffsetInfo
.keys()
1502 for Offset
in OffsetList
:
1503 FileWrite (File
, "%s %s" % (Offset
, OffsetInfo
[Offset
]))
1505 EdkLogger
.warn(None, "Fail to read report file", FvReportFileName
)
1507 FileWrite(File
, "Size: 0x%X (%.0fK)" % (Size
, Size
/ 1024.0))
1508 FileWrite(File
, gSubSectionEnd
)
1511 # Generate report for the FD region
1513 # This function generates report for the FD region.
1515 # @param self The object pointer
1516 # @param File The file object for report
1518 def GenerateReport(self
, File
):
1519 if (len(self
.FvList
) > 0):
1520 for FvItem
in self
.FvList
:
1521 Info
= self
.FvInfo
[FvItem
]
1522 self
._GenerateReport
(File
, Info
[0], "FV", Info
[1], Info
[2], FvItem
)
1524 self
._GenerateReport
(File
, "FD Region", self
.Type
, self
.BaseAddress
, self
.Size
)
1527 # Reports FD information
1529 # This class reports the FD section in the build report file.
1530 # It collects flash device information for a platform.
1532 class FdReport(object):
1534 # Constructor function for class FdReport
1536 # This constructor function generates FdReport object for a specified
1539 # @param self The object pointer
1540 # @param Fd The current Firmware device object
1541 # @param Wa Workspace context information
1543 def __init__(self
, Fd
, Wa
):
1544 self
.FdName
= Fd
.FdUiName
1545 self
.BaseAddress
= Fd
.BaseAddress
1547 self
.FdRegionList
= [FdRegionReport(FdRegion
, Wa
) for FdRegion
in Fd
.RegionList
]
1548 self
.FvPath
= os
.path
.join(Wa
.BuildDir
, "FV")
1549 self
.VpdFilePath
= os
.path
.join(self
.FvPath
, "%s.map" % Wa
.Platform
.VpdToolGuid
)
1550 self
.VPDBaseAddress
= 0
1552 self
.VPDInfoList
= []
1553 for index
, FdRegion
in enumerate(Fd
.RegionList
):
1554 if str(FdRegion
.RegionType
) is 'FILE' and Wa
.Platform
.VpdToolGuid
in str(FdRegion
.RegionDataList
):
1555 self
.VPDBaseAddress
= self
.FdRegionList
[index
].BaseAddress
1556 self
.VPDSize
= self
.FdRegionList
[index
].Size
1559 if os
.path
.isfile(self
.VpdFilePath
):
1560 fd
= open(self
.VpdFilePath
, "r")
1561 Lines
= fd
.readlines()
1564 if len(Line
) == 0 or Line
.startswith("#"):
1567 PcdName
, SkuId
, Offset
, Size
, Value
= Line
.split("#")[0].split("|")
1568 PcdName
, SkuId
, Offset
, Size
, Value
= PcdName
.strip(), SkuId
.strip(), Offset
.strip(), Size
.strip(), Value
.strip()
1569 if Offset
.lower().startswith('0x'):
1570 Offset
= '0x%08X' % (int(Offset
, 16) + self
.VPDBaseAddress
)
1572 Offset
= '0x%08X' % (int(Offset
, 10) + self
.VPDBaseAddress
)
1573 self
.VPDInfoList
.append("%s | %s | %s | %s | %s" % (PcdName
, SkuId
, Offset
, Size
, Value
))
1575 EdkLogger
.error("BuildReport", CODE_ERROR
, "Fail to parse VPD information file %s" % self
.VpdFilePath
)
1579 # Generate report for the firmware device.
1581 # This function generates report for the firmware device.
1583 # @param self The object pointer
1584 # @param File The file object for report
1586 def GenerateReport(self
, File
):
1587 FileWrite(File
, gSectionStart
)
1588 FileWrite(File
, "Firmware Device (FD)")
1589 FileWrite(File
, "FD Name: %s" % self
.FdName
)
1590 FileWrite(File
, "Base Address: %s" % self
.BaseAddress
)
1591 FileWrite(File
, "Size: 0x%X (%.0fK)" % (self
.Size
, self
.Size
/ 1024.0))
1592 if len(self
.FdRegionList
) > 0:
1593 FileWrite(File
, gSectionSep
)
1594 for FdRegionItem
in self
.FdRegionList
:
1595 FdRegionItem
.GenerateReport(File
)
1597 if len(self
.VPDInfoList
) > 0:
1598 FileWrite(File
, gSubSectionStart
)
1599 FileWrite(File
, "FD VPD Region")
1600 FileWrite(File
, "Base Address: 0x%X" % self
.VPDBaseAddress
)
1601 FileWrite(File
, "Size: 0x%X (%.0fK)" % (self
.VPDSize
, self
.VPDSize
/ 1024.0))
1602 FileWrite(File
, gSubSectionSep
)
1603 for item
in self
.VPDInfoList
:
1604 FileWrite(File
, item
)
1605 FileWrite(File
, gSubSectionEnd
)
1606 FileWrite(File
, gSectionEnd
)
1611 # Reports platform information
1613 # This class reports the whole platform information
1615 class PlatformReport(object):
1617 # Constructor function for class PlatformReport
1619 # This constructor function generates PlatformReport object a platform build.
1620 # It generates report for platform summary, flash, global PCDs and detailed
1621 # module information for modules involved in platform build.
1623 # @param self The object pointer
1624 # @param Wa Workspace context information
1625 # @param MaList The list of modules in the platform build
1627 def __init__(self
, Wa
, MaList
, ReportType
):
1628 self
._WorkspaceDir
= Wa
.WorkspaceDir
1629 self
.PlatformName
= Wa
.Name
1630 self
.PlatformDscPath
= Wa
.Platform
1631 self
.Architectures
= " ".join(Wa
.ArchList
)
1632 self
.ToolChain
= Wa
.ToolChain
1633 self
.Target
= Wa
.BuildTarget
1634 self
.OutputPath
= os
.path
.join(Wa
.WorkspaceDir
, Wa
.OutputDir
)
1635 self
.BuildEnvironment
= platform
.platform()
1637 self
.PcdReport
= None
1638 if "PCD" in ReportType
:
1639 self
.PcdReport
= PcdReport(Wa
)
1641 self
.FdReportList
= []
1642 if "FLASH" in ReportType
and Wa
.FdfProfile
and MaList
== None:
1643 for Fd
in Wa
.FdfProfile
.FdDict
:
1644 self
.FdReportList
.append(FdReport(Wa
.FdfProfile
.FdDict
[Fd
], Wa
))
1646 self
.PredictionReport
= None
1647 if "FIXED_ADDRESS" in ReportType
or "EXECUTION_ORDER" in ReportType
:
1648 self
.PredictionReport
= PredictionReport(Wa
)
1650 self
.DepexParser
= None
1651 if "DEPEX" in ReportType
:
1652 self
.DepexParser
= DepexParser(Wa
)
1654 self
.ModuleReportList
= []
1656 self
._IsModuleBuild
= True
1658 self
.ModuleReportList
.append(ModuleReport(Ma
, ReportType
))
1660 self
._IsModuleBuild
= False
1661 for Pa
in Wa
.AutoGenObjectList
:
1662 ModuleAutoGenList
= []
1663 for ModuleKey
in Pa
.Platform
.Modules
:
1664 ModuleAutoGenList
.append(Pa
.Platform
.Modules
[ModuleKey
].M
)
1665 if GlobalData
.gFdfParser
!= None:
1666 if Pa
.Arch
in GlobalData
.gFdfParser
.Profile
.InfDict
:
1667 INFList
= GlobalData
.gFdfParser
.Profile
.InfDict
[Pa
.Arch
]
1668 for InfName
in INFList
:
1669 InfClass
= PathClass(NormPath(InfName
), Wa
.WorkspaceDir
, Pa
.Arch
)
1670 Ma
= ModuleAutoGen(Wa
, InfClass
, Pa
.BuildTarget
, Pa
.ToolChain
, Pa
.Arch
, Wa
.MetaFile
)
1673 if Ma
not in ModuleAutoGenList
:
1674 ModuleAutoGenList
.append(Ma
)
1675 for MGen
in ModuleAutoGenList
:
1676 self
.ModuleReportList
.append(ModuleReport(MGen
, ReportType
))
1681 # Generate report for the whole platform.
1683 # This function generates report for platform information.
1684 # It comprises of platform summary, global PCD, flash and
1685 # module list sections.
1687 # @param self The object pointer
1688 # @param File The file object for report
1689 # @param BuildDuration The total time to build the modules
1690 # @param ReportType The kind of report items in the final report file
1692 def GenerateReport(self
, File
, BuildDuration
, ReportType
):
1693 FileWrite(File
, "Platform Summary")
1694 FileWrite(File
, "Platform Name: %s" % self
.PlatformName
)
1695 FileWrite(File
, "Platform DSC Path: %s" % self
.PlatformDscPath
)
1696 FileWrite(File
, "Architectures: %s" % self
.Architectures
)
1697 FileWrite(File
, "Tool Chain: %s" % self
.ToolChain
)
1698 FileWrite(File
, "Target: %s" % self
.Target
)
1699 FileWrite(File
, "Output Path: %s" % self
.OutputPath
)
1700 FileWrite(File
, "Build Environment: %s" % self
.BuildEnvironment
)
1701 FileWrite(File
, "Build Duration: %s" % BuildDuration
)
1702 FileWrite(File
, "Report Content: %s" % ", ".join(ReportType
))
1704 if GlobalData
.MixedPcd
:
1705 FileWrite(File
, gSectionStart
)
1706 FileWrite(File
, "The following PCDs use different access methods:")
1707 FileWrite(File
, gSectionSep
)
1708 for PcdItem
in GlobalData
.MixedPcd
:
1709 FileWrite(File
, "%s.%s" % (str(PcdItem
[1]), str(PcdItem
[0])))
1710 FileWrite(File
, gSectionEnd
)
1712 if not self
._IsModuleBuild
:
1713 if "PCD" in ReportType
:
1714 self
.PcdReport
.GenerateReport(File
, None)
1716 if "FLASH" in ReportType
:
1717 for FdReportListItem
in self
.FdReportList
:
1718 FdReportListItem
.GenerateReport(File
)
1720 for ModuleReportItem
in self
.ModuleReportList
:
1721 ModuleReportItem
.GenerateReport(File
, self
.PcdReport
, self
.PredictionReport
, self
.DepexParser
, ReportType
)
1723 if not self
._IsModuleBuild
:
1724 if "EXECUTION_ORDER" in ReportType
:
1725 self
.PredictionReport
.GenerateReport(File
, None)
1727 ## BuildReport class
1729 # This base class contain the routines to collect data and then
1730 # applies certain format to the output report
1732 class BuildReport(object):
1734 # Constructor function for class BuildReport
1736 # This constructor function generates BuildReport object a platform build.
1737 # It generates report for platform summary, flash, global PCDs and detailed
1738 # module information for modules involved in platform build.
1740 # @param self The object pointer
1741 # @param ReportFile The file name to save report file
1742 # @param ReportType The kind of report items in the final report file
1744 def __init__(self
, ReportFile
, ReportType
):
1745 self
.ReportFile
= ReportFile
1747 self
.ReportList
= []
1748 self
.ReportType
= []
1750 for ReportTypeItem
in ReportType
:
1751 if ReportTypeItem
not in self
.ReportType
:
1752 self
.ReportType
.append(ReportTypeItem
)
1754 self
.ReportType
= ["PCD", "LIBRARY", "BUILD_FLAGS", "DEPEX", "HASH", "FLASH", "FIXED_ADDRESS"]
1756 # Adds platform report to the list
1758 # This function adds a platform report to the final report list.
1760 # @param self The object pointer
1761 # @param Wa Workspace context information
1762 # @param MaList The list of modules in the platform build
1764 def AddPlatformReport(self
, Wa
, MaList
=None):
1766 self
.ReportList
.append((Wa
, MaList
))
1769 # Generates the final report.
1771 # This function generates platform build report. It invokes GenerateReport()
1772 # method for every platform report in the list.
1774 # @param self The object pointer
1775 # @param BuildDuration The total time to build the modules
1777 def GenerateReport(self
, BuildDuration
):
1781 for (Wa
, MaList
) in self
.ReportList
:
1782 PlatformReport(Wa
, MaList
, self
.ReportType
).GenerateReport(File
, BuildDuration
, self
.ReportType
)
1783 Content
= FileLinesSplit(File
.getvalue(), gLineMaxLength
)
1784 SaveFileOnChange(self
.ReportFile
, Content
, True)
1785 EdkLogger
.quiet("Build report can be found at %s" % os
.path
.abspath(self
.ReportFile
))
1787 EdkLogger
.error(None, FILE_WRITE_FAILURE
, ExtraData
=self
.ReportFile
)
1789 EdkLogger
.error("BuildReport", CODE_ERROR
, "Unknown fatal error when generating build report", ExtraData
=self
.ReportFile
, RaiseError
=False)
1790 EdkLogger
.quiet("(Python %s on %s\n%s)" % (platform
.python_version(), sys
.platform
, traceback
.format_exc()))
1793 # This acts like the main() function for the script, unless it is 'import'ed into another script.
1794 if __name__
== '__main__':