]> git.proxmox.com Git - mirror_edk2.git/blob - BaseTools/Source/Python/build/BuildReport.py
BaseTools: report build time measured by module of EDKII Build
[mirror_edk2.git] / BaseTools / Source / Python / build / BuildReport.py
1 ## @file
2 # Routines for generating build report.
3 #
4 # This module contains the functionality to generate build report after
5 # build all target completes successfully.
6 #
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
12 #
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.
15 #
16
17 ## Import Modules
18 #
19 import Common.LongFilePathOs as os
20 import re
21 import platform
22 import textwrap
23 import traceback
24 import sys
25 import time
26 import struct
27 import hashlib
28 import subprocess
29 import threading
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
53
54 ## Pattern to extract contents in EDK DXS files
55 gDxsDependencyPattern = re.compile(r"DEPENDENCY_START(.+)DEPENDENCY_END", re.DOTALL)
56
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]+)")
60
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+)")
64
65 ## Pattern to find GUID value in flash description files
66 gPcdGuidPattern = re.compile(r"PCD\((\w+)[.](\w+)\)")
67
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]+)")
70
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]+)"})
74
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*[)]")
78
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+)")
81
82 ## Tags for MaxLength of line in report
83 gLineMaxLength = 120
84
85 ## Tags for end of line in report
86 gEndOfLine = "\r\n"
87
88 ## Tags for section start, end and separator
89 gSectionStart = ">" + "=" * (gLineMaxLength - 2) + "<"
90 gSectionEnd = "<" + "=" * (gLineMaxLength - 2) + ">" + "\n"
91 gSectionSep = "=" * gLineMaxLength
92
93 ## Tags for subsection start, end and separator
94 gSubSectionStart = ">" + "-" * (gLineMaxLength - 2) + "<"
95 gSubSectionEnd = "<" + "-" * (gLineMaxLength - 2) + ">"
96 gSubSectionSep = "-" * gLineMaxLength
97
98
99 ## The look up table to map PCD type to pair of report display type and DEC type
100 gPcdTypeMap = {
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'),
110 }
111
112 ## The look up table to map module type to driver type
113 gDriverTypeMap = {
114 'SEC' : '0x3 (SECURITY_CORE)',
115 'PEI_CORE' : '0x4 (PEI_CORE)',
116 'PEIM' : '0x6 (PEIM)',
117 'DXE_CORE' : '0x5 (DXE_CORE)',
118 'DXE_DRIVER' : '0x7 (DRIVER)',
119 'DXE_SAL_DRIVER' : '0x7 (DRIVER)',
120 'DXE_SMM_DRIVER' : '0x7 (DRIVER)',
121 'DXE_RUNTIME_DRIVER': '0x7 (DRIVER)',
122 'UEFI_DRIVER' : '0x7 (DRIVER)',
123 'UEFI_APPLICATION' : '0x9 (APPLICATION)',
124 'SMM_CORE' : '0xD (SMM_CORE)',
125 'SMM_DRIVER' : '0xA (SMM)', # Extension of module type to support PI 1.1 SMM drivers
126 'MM_STANDALONE' : '0xE (MM_STANDALONE)',
127 'MM_CORE_STANDALONE' : '0xF (MM_CORE_STANDALONE)'
128 }
129
130 ## The look up table of the supported opcode in the dependency expression binaries
131 gOpCodeList = ["BEFORE", "AFTER", "PUSH", "AND", "OR", "NOT", "TRUE", "FALSE", "END", "SOR"]
132
133 ##
134 # Writes a string to the file object.
135 #
136 # This function writes a string to the file object and a new line is appended
137 # afterwards. It may optionally wraps the string for better readability.
138 #
139 # @File The file object to write
140 # @String The string to be written to the file
141 # @Wrapper Indicates whether to wrap the string
142 #
143 def FileWrite(File, String, Wrapper=False):
144 if Wrapper:
145 String = textwrap.fill(String, 120)
146 File.write(String + gEndOfLine)
147
148 ##
149 # Find all the header file that the module source directly includes.
150 #
151 # This function scans source code to find all header files the module may
152 # include. This is not accurate but very effective to find all the header
153 # file the module might include with #include statement.
154 #
155 # @Source The source file name
156 # @IncludePathList The list of include path to find the source file.
157 # @IncludeFiles The dictionary of current found include files.
158 #
159 def FindIncludeFiles(Source, IncludePathList, IncludeFiles):
160 FileContents = open(Source).read()
161 #
162 # Find header files with pattern #include "XXX.h" or #include <XXX.h>
163 #
164 for Match in gIncludePattern.finditer(FileContents):
165 FileName = Match.group(1).strip()
166 for Dir in [os.path.dirname(Source)] + IncludePathList:
167 FullFileName = os.path.normpath(os.path.join(Dir, FileName))
168 if os.path.exists(FullFileName):
169 IncludeFiles[FullFileName.lower().replace("\\", "/")] = FullFileName
170 break
171
172 #
173 # Find header files with pattern like #include EFI_PPI_CONSUMER(XXX)
174 #
175 for Match in gIncludePattern2.finditer(FileContents):
176 Key = Match.group(2)
177 Type = Match.group(1)
178 if "ARCH_PROTOCOL" in Type:
179 FileName = "ArchProtocol/%(Key)s/%(Key)s.h" % {"Key" : Key}
180 elif "PROTOCOL" in Type:
181 FileName = "Protocol/%(Key)s/%(Key)s.h" % {"Key" : Key}
182 elif "PPI" in Type:
183 FileName = "Ppi/%(Key)s/%(Key)s.h" % {"Key" : Key}
184 elif "GUID" in Type:
185 FileName = "Guid/%(Key)s/%(Key)s.h" % {"Key" : Key}
186 else:
187 continue
188 for Dir in IncludePathList:
189 FullFileName = os.path.normpath(os.path.join(Dir, FileName))
190 if os.path.exists(FullFileName):
191 IncludeFiles[FullFileName.lower().replace("\\", "/")] = FullFileName
192 break
193
194 ## Split each lines in file
195 #
196 # This method is used to split the lines in file to make the length of each line
197 # less than MaxLength.
198 #
199 # @param Content The content of file
200 # @param MaxLength The Max Length of the line
201 #
202 def FileLinesSplit(Content=None, MaxLength=None):
203 ContentList = Content.split(TAB_LINE_BREAK)
204 NewContent = ''
205 NewContentList = []
206 for Line in ContentList:
207 while len(Line.rstrip()) > MaxLength:
208 LineSpaceIndex = Line.rfind(TAB_SPACE_SPLIT, 0, MaxLength)
209 LineSlashIndex = Line.rfind(TAB_SLASH, 0, MaxLength)
210 LineBackSlashIndex = Line.rfind(TAB_BACK_SLASH, 0, MaxLength)
211 if max(LineSpaceIndex, LineSlashIndex, LineBackSlashIndex) > 0:
212 LineBreakIndex = max(LineSpaceIndex, LineSlashIndex, LineBackSlashIndex)
213 else:
214 LineBreakIndex = MaxLength
215 NewContentList.append(Line[:LineBreakIndex])
216 Line = Line[LineBreakIndex:]
217 if Line:
218 NewContentList.append(Line)
219 for NewLine in NewContentList:
220 NewContent += NewLine + TAB_LINE_BREAK
221
222 NewContent = NewContent.replace(TAB_LINE_BREAK, gEndOfLine).replace('\r\r\n', gEndOfLine)
223 return NewContent
224
225
226
227 ##
228 # Parse binary dependency expression section
229 #
230 # This utility class parses the dependency expression section and translate the readable
231 # GUID name and value.
232 #
233 class DepexParser(object):
234 ##
235 # Constructor function for class DepexParser
236 #
237 # This constructor function collect GUID values so that the readable
238 # GUID name can be translated.
239 #
240 # @param self The object pointer
241 # @param Wa Workspace context information
242 #
243 def __init__(self, Wa):
244 self._GuidDb = {}
245 for Pa in Wa.AutoGenObjectList:
246 for Package in Pa.PackageList:
247 for Protocol in Package.Protocols:
248 GuidValue = GuidStructureStringToGuidString(Package.Protocols[Protocol])
249 self._GuidDb[GuidValue.upper()] = Protocol
250 for Ppi in Package.Ppis:
251 GuidValue = GuidStructureStringToGuidString(Package.Ppis[Ppi])
252 self._GuidDb[GuidValue.upper()] = Ppi
253 for Guid in Package.Guids:
254 GuidValue = GuidStructureStringToGuidString(Package.Guids[Guid])
255 self._GuidDb[GuidValue.upper()] = Guid
256
257 ##
258 # Parse the binary dependency expression files.
259 #
260 # This function parses the binary dependency expression file and translate it
261 # to the instruction list.
262 #
263 # @param self The object pointer
264 # @param DepexFileName The file name of binary dependency expression file.
265 #
266 def ParseDepexFile(self, DepexFileName):
267 DepexFile = open(DepexFileName, "rb")
268 DepexStatement = []
269 OpCode = DepexFile.read(1)
270 while OpCode:
271 Statement = gOpCodeList[struct.unpack("B", OpCode)[0]]
272 if Statement in ["BEFORE", "AFTER", "PUSH"]:
273 GuidValue = "%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X" % \
274 struct.unpack("=LHHBBBBBBBB", DepexFile.read(16))
275 GuidString = self._GuidDb.get(GuidValue, GuidValue)
276 Statement = "%s %s" % (Statement, GuidString)
277 DepexStatement.append(Statement)
278 OpCode = DepexFile.read(1)
279
280 return DepexStatement
281
282 ##
283 # Reports library information
284 #
285 # This class reports the module library subsection in the build report file.
286 #
287 class LibraryReport(object):
288 ##
289 # Constructor function for class LibraryReport
290 #
291 # This constructor function generates LibraryReport object for
292 # a module.
293 #
294 # @param self The object pointer
295 # @param M Module context information
296 #
297 def __init__(self, M):
298 self.LibraryList = []
299 if int(str(M.AutoGenVersion), 0) >= 0x00010005:
300 self._EdkIIModule = True
301 else:
302 self._EdkIIModule = False
303
304 for Lib in M.DependentLibraryList:
305 LibInfPath = str(Lib)
306 LibClassList = Lib.LibraryClass[0].LibraryClass
307 LibConstructorList = Lib.ConstructorList
308 LibDesstructorList = Lib.DestructorList
309 LibDepexList = Lib.DepexExpression[M.Arch, M.ModuleType]
310 for LibAutoGen in M.LibraryAutoGenList:
311 if LibInfPath == LibAutoGen.MetaFile.Path:
312 LibTime = LibAutoGen.BuildTime
313 break
314 self.LibraryList.append((LibInfPath, LibClassList, LibConstructorList, LibDesstructorList, LibDepexList, LibTime))
315
316 ##
317 # Generate report for module library information
318 #
319 # This function generates report for the module library.
320 # If the module is EDKII style one, the additional library class, library
321 # constructor/destructor and dependency expression may also be reported.
322 #
323 # @param self The object pointer
324 # @param File The file object for report
325 #
326 def GenerateReport(self, File):
327 if len(self.LibraryList) > 0:
328 FileWrite(File, gSubSectionStart)
329 FileWrite(File, TAB_BRG_LIBRARY)
330 FileWrite(File, gSubSectionSep)
331 for LibraryItem in self.LibraryList:
332 LibInfPath = LibraryItem[0]
333 FileWrite(File, LibInfPath)
334
335 #
336 # Report library class, library constructor and destructor for
337 # EDKII style module.
338 #
339 if self._EdkIIModule:
340 LibClass = LibraryItem[1]
341 EdkIILibInfo = ""
342 LibConstructor = " ".join(LibraryItem[2])
343 if LibConstructor:
344 EdkIILibInfo += " C = " + LibConstructor
345 LibDestructor = " ".join(LibraryItem[3])
346 if LibDestructor:
347 EdkIILibInfo += " D = " + LibDestructor
348 LibDepex = " ".join(LibraryItem[4])
349 if LibDepex:
350 EdkIILibInfo += " Depex = " + LibDepex
351 if LibraryItem[5]:
352 EdkIILibInfo += " Time = " + LibraryItem[5]
353 if EdkIILibInfo:
354 FileWrite(File, "{%s: %s}" % (LibClass, EdkIILibInfo))
355 else:
356 FileWrite(File, "{%s}" % LibClass)
357
358 FileWrite(File, gSubSectionEnd)
359
360 ##
361 # Reports dependency expression information
362 #
363 # This class reports the module dependency expression subsection in the build report file.
364 #
365 class DepexReport(object):
366 ##
367 # Constructor function for class DepexReport
368 #
369 # This constructor function generates DepexReport object for
370 # a module. If the module source contains the DXS file (usually EDK
371 # style module), it uses the dependency in DXS file; otherwise,
372 # it uses the dependency expression from its own INF [Depex] section
373 # and then merges with the ones from its dependent library INF.
374 #
375 # @param self The object pointer
376 # @param M Module context information
377 #
378 def __init__(self, M):
379 self.Depex = ""
380 self._DepexFileName = os.path.join(M.BuildDir, "OUTPUT", M.Module.BaseName + ".depex")
381 ModuleType = M.ModuleType
382 if not ModuleType:
383 ModuleType = gComponentType2ModuleType.get(M.ComponentType, "")
384
385 if ModuleType in ["SEC", "PEI_CORE", "DXE_CORE", "SMM_CORE", "MM_CORE_STANDALONE", "UEFI_APPLICATION"]:
386 return
387
388 for Source in M.SourceFileList:
389 if os.path.splitext(Source.Path)[1].lower() == ".dxs":
390 Match = gDxsDependencyPattern.search(open(Source.Path).read())
391 if Match:
392 self.Depex = Match.group(1).strip()
393 self.Source = "DXS"
394 break
395 else:
396 self.Depex = M.DepexExpressionList.get(M.ModuleType, "")
397 self.ModuleDepex = " ".join(M.Module.DepexExpression[M.Arch, M.ModuleType])
398 if not self.ModuleDepex:
399 self.ModuleDepex = "(None)"
400
401 LibDepexList = []
402 for Lib in M.DependentLibraryList:
403 LibDepex = " ".join(Lib.DepexExpression[M.Arch, M.ModuleType]).strip()
404 if LibDepex != "":
405 LibDepexList.append("(" + LibDepex + ")")
406 self.LibraryDepex = " AND ".join(LibDepexList)
407 if not self.LibraryDepex:
408 self.LibraryDepex = "(None)"
409 self.Source = "INF"
410
411 ##
412 # Generate report for module dependency expression information
413 #
414 # This function generates report for the module dependency expression.
415 #
416 # @param self The object pointer
417 # @param File The file object for report
418 # @param GlobalDepexParser The platform global Dependency expression parser object
419 #
420 def GenerateReport(self, File, GlobalDepexParser):
421 if not self.Depex:
422 return
423 FileWrite(File, gSubSectionStart)
424 if os.path.isfile(self._DepexFileName):
425 try:
426 DepexStatements = GlobalDepexParser.ParseDepexFile(self._DepexFileName)
427 FileWrite(File, "Final Dependency Expression (DEPEX) Instructions")
428 for DepexStatement in DepexStatements:
429 FileWrite(File, " %s" % DepexStatement)
430 FileWrite(File, gSubSectionSep)
431 except:
432 EdkLogger.warn(None, "Dependency expression file is corrupted", self._DepexFileName)
433
434 FileWrite(File, "Dependency Expression (DEPEX) from %s" % self.Source)
435
436 if self.Source == "INF":
437 FileWrite(File, "%s" % self.Depex, True)
438 FileWrite(File, gSubSectionSep)
439 FileWrite(File, "From Module INF: %s" % self.ModuleDepex, True)
440 FileWrite(File, "From Library INF: %s" % self.LibraryDepex, True)
441 else:
442 FileWrite(File, "%s" % self.Depex)
443 FileWrite(File, gSubSectionEnd)
444
445 ##
446 # Reports dependency expression information
447 #
448 # This class reports the module build flags subsection in the build report file.
449 #
450 class BuildFlagsReport(object):
451 ##
452 # Constructor function for class BuildFlagsReport
453 #
454 # This constructor function generates BuildFlagsReport object for
455 # a module. It reports the build tool chain tag and all relevant
456 # build flags to build the module.
457 #
458 # @param self The object pointer
459 # @param M Module context information
460 #
461 def __init__(self, M):
462 BuildOptions = {}
463 #
464 # Add build flags according to source file extension so that
465 # irrelevant ones can be filtered out.
466 #
467 for Source in M.SourceFileList:
468 Ext = os.path.splitext(Source.File)[1].lower()
469 if Ext in [".c", ".cc", ".cpp"]:
470 BuildOptions["CC"] = 1
471 elif Ext in [".s", ".asm"]:
472 BuildOptions["PP"] = 1
473 BuildOptions["ASM"] = 1
474 elif Ext in [".vfr"]:
475 BuildOptions["VFRPP"] = 1
476 BuildOptions["VFR"] = 1
477 elif Ext in [".dxs"]:
478 BuildOptions["APP"] = 1
479 BuildOptions["CC"] = 1
480 elif Ext in [".asl"]:
481 BuildOptions["ASLPP"] = 1
482 BuildOptions["ASL"] = 1
483 elif Ext in [".aslc"]:
484 BuildOptions["ASLCC"] = 1
485 BuildOptions["ASLDLINK"] = 1
486 BuildOptions["CC"] = 1
487 elif Ext in [".asm16"]:
488 BuildOptions["ASMLINK"] = 1
489 BuildOptions["SLINK"] = 1
490 BuildOptions["DLINK"] = 1
491
492 #
493 # Save module build flags.
494 #
495 self.ToolChainTag = M.ToolChain
496 self.BuildFlags = {}
497 for Tool in BuildOptions:
498 self.BuildFlags[Tool + "_FLAGS"] = M.BuildOption.get(Tool, {}).get("FLAGS", "")
499
500 ##
501 # Generate report for module build flags information
502 #
503 # This function generates report for the module build flags expression.
504 #
505 # @param self The object pointer
506 # @param File The file object for report
507 #
508 def GenerateReport(self, File):
509 FileWrite(File, gSubSectionStart)
510 FileWrite(File, "Build Flags")
511 FileWrite(File, "Tool Chain Tag: %s" % self.ToolChainTag)
512 for Tool in self.BuildFlags:
513 FileWrite(File, gSubSectionSep)
514 FileWrite(File, "%s = %s" % (Tool, self.BuildFlags[Tool]), True)
515
516 FileWrite(File, gSubSectionEnd)
517
518
519 ##
520 # Reports individual module information
521 #
522 # This class reports the module section in the build report file.
523 # It comprises of module summary, module PCD, library, dependency expression,
524 # build flags sections.
525 #
526 class ModuleReport(object):
527 ##
528 # Constructor function for class ModuleReport
529 #
530 # This constructor function generates ModuleReport object for
531 # a separate module in a platform build.
532 #
533 # @param self The object pointer
534 # @param M Module context information
535 # @param ReportType The kind of report items in the final report file
536 #
537 def __init__(self, M, ReportType):
538 self.ModuleName = M.Module.BaseName
539 self.ModuleInfPath = M.MetaFile.File
540 self.FileGuid = M.Guid
541 self.Size = 0
542 self.BuildTimeStamp = None
543 self.Hash = 0
544 self.DriverType = ""
545 if not M.IsLibrary:
546 ModuleType = M.ModuleType
547 if not ModuleType:
548 ModuleType = gComponentType2ModuleType.get(M.ComponentType, "")
549 #
550 # If a module complies to PI 1.1, promote Module type to "SMM_DRIVER"
551 #
552 if ModuleType == "DXE_SMM_DRIVER":
553 PiSpec = M.Module.Specification.get("PI_SPECIFICATION_VERSION", "0x00010000")
554 if int(PiSpec, 0) >= 0x0001000A:
555 ModuleType = "SMM_DRIVER"
556 self.DriverType = gDriverTypeMap.get(ModuleType, "0x2 (FREE_FORM)")
557 self.UefiSpecVersion = M.Module.Specification.get("UEFI_SPECIFICATION_VERSION", "")
558 self.PiSpecVersion = M.Module.Specification.get("PI_SPECIFICATION_VERSION", "")
559 self.PciDeviceId = M.Module.Defines.get("PCI_DEVICE_ID", "")
560 self.PciVendorId = M.Module.Defines.get("PCI_VENDOR_ID", "")
561 self.PciClassCode = M.Module.Defines.get("PCI_CLASS_CODE", "")
562 self.BuildTime = M.BuildTime
563
564 self._BuildDir = M.BuildDir
565 self.ModulePcdSet = {}
566 if "PCD" in ReportType:
567 #
568 # Collect all module used PCD set: module INF referenced directly or indirectly.
569 # It also saves module INF default values of them in case they exist.
570 #
571 for Pcd in M.ModulePcdList + M.LibraryPcdList:
572 self.ModulePcdSet.setdefault((Pcd.TokenCName, Pcd.TokenSpaceGuidCName, Pcd.Type), (Pcd.InfDefaultValue, Pcd.DefaultValue))
573
574 self.LibraryReport = None
575 if "LIBRARY" in ReportType:
576 self.LibraryReport = LibraryReport(M)
577
578 self.DepexReport = None
579 if "DEPEX" in ReportType:
580 self.DepexReport = DepexReport(M)
581
582 if "BUILD_FLAGS" in ReportType:
583 self.BuildFlagsReport = BuildFlagsReport(M)
584
585
586 ##
587 # Generate report for module information
588 #
589 # This function generates report for separate module expression
590 # in a platform build.
591 #
592 # @param self The object pointer
593 # @param File The file object for report
594 # @param GlobalPcdReport The platform global PCD report object
595 # @param GlobalPredictionReport The platform global Prediction report object
596 # @param GlobalDepexParser The platform global Dependency expression parser object
597 # @param ReportType The kind of report items in the final report file
598 #
599 def GenerateReport(self, File, GlobalPcdReport, GlobalPredictionReport, GlobalDepexParser, ReportType):
600 FileWrite(File, gSectionStart)
601
602 FwReportFileName = os.path.join(self._BuildDir, "DEBUG", self.ModuleName + ".txt")
603 if os.path.isfile(FwReportFileName):
604 try:
605 FileContents = open(FwReportFileName).read()
606 Match = gModuleSizePattern.search(FileContents)
607 if Match:
608 self.Size = int(Match.group(1))
609
610 Match = gTimeStampPattern.search(FileContents)
611 if Match:
612 self.BuildTimeStamp = datetime.fromtimestamp(int(Match.group(1)))
613 except IOError:
614 EdkLogger.warn(None, "Fail to read report file", FwReportFileName)
615
616 if "HASH" in ReportType:
617 OutputDir = os.path.join(self._BuildDir, "OUTPUT")
618 DefaultEFIfile = os.path.join(OutputDir, self.ModuleName + ".efi")
619 if os.path.isfile(DefaultEFIfile):
620 Tempfile = os.path.join(OutputDir, self.ModuleName + "_hash.tmp")
621 # rebase the efi image since its base address may not zero
622 cmd = ["GenFw", "--rebase", str(0), "-o", Tempfile, DefaultEFIfile]
623 try:
624 PopenObject = subprocess.Popen(' '.join(cmd), stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
625 except Exception, X:
626 EdkLogger.error("GenFw", COMMAND_FAILURE, ExtraData="%s: %s" % (str(X), cmd[0]))
627 EndOfProcedure = threading.Event()
628 EndOfProcedure.clear()
629 if PopenObject.stderr:
630 StdErrThread = threading.Thread(target=ReadMessage, args=(PopenObject.stderr, EdkLogger.quiet, EndOfProcedure))
631 StdErrThread.setName("STDERR-Redirector")
632 StdErrThread.setDaemon(False)
633 StdErrThread.start()
634 # waiting for program exit
635 PopenObject.wait()
636 if PopenObject.stderr:
637 StdErrThread.join()
638 if PopenObject.returncode != 0:
639 EdkLogger.error("GenFw", COMMAND_FAILURE, "Failed to generate firmware hash image for %s" % (DefaultEFIfile))
640 if os.path.isfile(Tempfile):
641 self.Hash = hashlib.sha1()
642 buf = open(Tempfile, 'rb').read()
643 if self.Hash.update(buf):
644 self.Hash = self.Hash.update(buf)
645 self.Hash = self.Hash.hexdigest()
646 os.remove(Tempfile)
647
648 FileWrite(File, "Module Summary")
649 FileWrite(File, "Module Name: %s" % self.ModuleName)
650 FileWrite(File, "Module INF Path: %s" % self.ModuleInfPath)
651 FileWrite(File, "File GUID: %s" % self.FileGuid)
652 if self.Size:
653 FileWrite(File, "Size: 0x%X (%.2fK)" % (self.Size, self.Size / 1024.0))
654 if self.Hash:
655 FileWrite(File, "SHA1 HASH: %s *%s" % (self.Hash, self.ModuleName + ".efi"))
656 if self.BuildTimeStamp:
657 FileWrite(File, "Build Time Stamp: %s" % self.BuildTimeStamp)
658 if self.BuildTime:
659 FileWrite(File, "Module Build Time: %s" % self.BuildTime)
660 if self.DriverType:
661 FileWrite(File, "Driver Type: %s" % self.DriverType)
662 if self.UefiSpecVersion:
663 FileWrite(File, "UEFI Spec Version: %s" % self.UefiSpecVersion)
664 if self.PiSpecVersion:
665 FileWrite(File, "PI Spec Version: %s" % self.PiSpecVersion)
666 if self.PciDeviceId:
667 FileWrite(File, "PCI Device ID: %s" % self.PciDeviceId)
668 if self.PciVendorId:
669 FileWrite(File, "PCI Vendor ID: %s" % self.PciVendorId)
670 if self.PciClassCode:
671 FileWrite(File, "PCI Class Code: %s" % self.PciClassCode)
672
673 FileWrite(File, gSectionSep)
674
675 if "PCD" in ReportType:
676 GlobalPcdReport.GenerateReport(File, self.ModulePcdSet)
677
678 if "LIBRARY" in ReportType:
679 self.LibraryReport.GenerateReport(File)
680
681 if "DEPEX" in ReportType:
682 self.DepexReport.GenerateReport(File, GlobalDepexParser)
683
684 if "BUILD_FLAGS" in ReportType:
685 self.BuildFlagsReport.GenerateReport(File)
686
687 if "FIXED_ADDRESS" in ReportType and self.FileGuid:
688 GlobalPredictionReport.GenerateReport(File, self.FileGuid)
689
690 FileWrite(File, gSectionEnd)
691
692 def ReadMessage(From, To, ExitFlag):
693 while True:
694 # read one line a time
695 Line = From.readline()
696 # empty string means "end"
697 if Line != None and Line != "":
698 To(Line.rstrip())
699 else:
700 break
701 if ExitFlag.isSet():
702 break
703
704 ##
705 # Reports platform and module PCD information
706 #
707 # This class reports the platform PCD section and module PCD subsection
708 # in the build report file.
709 #
710 class PcdReport(object):
711 ##
712 # Constructor function for class PcdReport
713 #
714 # This constructor function generates PcdReport object a platform build.
715 # It collects the whole PCD database from platform DSC files, platform
716 # flash description file and package DEC files.
717 #
718 # @param self The object pointer
719 # @param Wa Workspace context information
720 #
721 def __init__(self, Wa):
722 self.AllPcds = {}
723 self.UnusedPcds = {}
724 self.ConditionalPcds = {}
725 self.MaxLen = 0
726 if Wa.FdfProfile:
727 self.FdfPcdSet = Wa.FdfProfile.PcdDict
728 else:
729 self.FdfPcdSet = {}
730
731 self.ModulePcdOverride = {}
732 for Pa in Wa.AutoGenObjectList:
733 #
734 # Collect all platform referenced PCDs and grouped them by PCD token space
735 # GUID C Names
736 #
737 for Pcd in Pa.AllPcdList:
738 PcdList = self.AllPcds.setdefault(Pcd.TokenSpaceGuidCName, {}).setdefault(Pcd.Type, [])
739 if Pcd not in PcdList:
740 PcdList.append(Pcd)
741 if len(Pcd.TokenCName) > self.MaxLen:
742 self.MaxLen = len(Pcd.TokenCName)
743 #
744 # Collect the PCD defined in DSC/FDF file, but not used in module
745 #
746 UnusedPcdFullList = []
747 for item in Pa.Platform.Pcds:
748 Pcd = Pa.Platform.Pcds[item]
749 if not Pcd.Type:
750 # check the Pcd in FDF file, whether it is used in module first
751 for T in ["FixedAtBuild", "PatchableInModule", "FeatureFlag", "Dynamic", "DynamicEx"]:
752 PcdList = self.AllPcds.setdefault(Pcd.TokenSpaceGuidCName, {}).setdefault(T, [])
753 if Pcd in PcdList:
754 Pcd.Type = T
755 break
756 if not Pcd.Type:
757 PcdTypeFlag = False
758 for package in Pa.PackageList:
759 for T in ["FixedAtBuild", "PatchableInModule", "FeatureFlag", "Dynamic", "DynamicEx"]:
760 if (Pcd.TokenCName, Pcd.TokenSpaceGuidCName, T) in package.Pcds:
761 Pcd.Type = T
762 PcdTypeFlag = True
763 if not Pcd.DatumType:
764 Pcd.DatumType = package.Pcds[(Pcd.TokenCName, Pcd.TokenSpaceGuidCName, T)].DatumType
765 break
766 if PcdTypeFlag:
767 break
768 if not Pcd.DatumType:
769 PcdType = Pcd.Type
770 # Try to remove Hii and Vpd suffix
771 if PcdType.startswith("DynamicEx"):
772 PcdType = "DynamicEx"
773 elif PcdType.startswith("Dynamic"):
774 PcdType = "Dynamic"
775 for package in Pa.PackageList:
776 if (Pcd.TokenCName, Pcd.TokenSpaceGuidCName, PcdType) in package.Pcds:
777 Pcd.DatumType = package.Pcds[(Pcd.TokenCName, Pcd.TokenSpaceGuidCName, PcdType)].DatumType
778 break
779
780 PcdList = self.AllPcds.setdefault(Pcd.TokenSpaceGuidCName, {}).setdefault(Pcd.Type, [])
781 if Pcd not in PcdList and Pcd not in UnusedPcdFullList:
782 UnusedPcdFullList.append(Pcd)
783 if len(Pcd.TokenCName) > self.MaxLen:
784 self.MaxLen = len(Pcd.TokenCName)
785
786 if GlobalData.gConditionalPcds:
787 for PcdItem in GlobalData.gConditionalPcds:
788 if '.' in PcdItem:
789 (TokenSpaceGuidCName, TokenCName) = PcdItem.split('.')
790 if (TokenCName, TokenSpaceGuidCName) in Pa.Platform.Pcds.keys():
791 Pcd = Pa.Platform.Pcds[(TokenCName, TokenSpaceGuidCName)]
792 PcdList = self.ConditionalPcds.setdefault(Pcd.TokenSpaceGuidCName, {}).setdefault(Pcd.Type, [])
793 if Pcd not in PcdList:
794 PcdList.append(Pcd)
795
796 UnusedPcdList = []
797 if UnusedPcdFullList:
798 for Pcd in UnusedPcdFullList:
799 if Pcd.TokenSpaceGuidCName + '.' + Pcd.TokenCName in GlobalData.gConditionalPcds:
800 continue
801 UnusedPcdList.append(Pcd)
802
803 for Pcd in UnusedPcdList:
804 PcdList = self.UnusedPcds.setdefault(Pcd.TokenSpaceGuidCName, {}).setdefault(Pcd.Type, [])
805 if Pcd not in PcdList:
806 PcdList.append(Pcd)
807
808 for Module in Pa.Platform.Modules.values():
809 #
810 # Collect module override PCDs
811 #
812 for ModulePcd in Module.M.ModulePcdList + Module.M.LibraryPcdList:
813 TokenCName = ModulePcd.TokenCName
814 TokenSpaceGuid = ModulePcd.TokenSpaceGuidCName
815 ModuleDefault = ModulePcd.DefaultValue
816 ModulePath = os.path.basename(Module.M.MetaFile.File)
817 self.ModulePcdOverride.setdefault((TokenCName, TokenSpaceGuid), {})[ModulePath] = ModuleDefault
818
819
820 #
821 # Collect PCD DEC default value.
822 #
823 self.DecPcdDefault = {}
824 for Pa in Wa.AutoGenObjectList:
825 for Package in Pa.PackageList:
826 for (TokenCName, TokenSpaceGuidCName, DecType) in Package.Pcds:
827 DecDefaultValue = Package.Pcds[TokenCName, TokenSpaceGuidCName, DecType].DefaultValue
828 self.DecPcdDefault.setdefault((TokenCName, TokenSpaceGuidCName, DecType), DecDefaultValue)
829 #
830 # Collect PCDs defined in DSC common section
831 #
832 self.DscPcdDefault = {}
833 for Arch in Wa.ArchList:
834 Platform = Wa.BuildDatabase[Wa.MetaFile, Arch, Wa.BuildTarget, Wa.ToolChain]
835 for (TokenCName, TokenSpaceGuidCName) in Platform.Pcds:
836 DscDefaultValue = Platform.Pcds[(TokenCName, TokenSpaceGuidCName)].DefaultValue
837 if DscDefaultValue:
838 self.DscPcdDefault[(TokenCName, TokenSpaceGuidCName)] = DscDefaultValue
839
840 def GenerateReport(self, File, ModulePcdSet):
841 if self.ConditionalPcds:
842 self.GenerateReportDetail(File, ModulePcdSet, 1)
843 if self.UnusedPcds:
844 self.GenerateReportDetail(File, ModulePcdSet, 2)
845 self.GenerateReportDetail(File, ModulePcdSet)
846
847 ##
848 # Generate report for PCD information
849 #
850 # This function generates report for separate module expression
851 # in a platform build.
852 #
853 # @param self The object pointer
854 # @param File The file object for report
855 # @param ModulePcdSet Set of all PCDs referenced by module or None for
856 # platform PCD report
857 # @param ReportySubType 0 means platform/module PCD report, 1 means Conditional
858 # directives section report, 2 means Unused Pcds section report
859 # @param DscOverridePcds Module DSC override PCDs set
860 #
861 def GenerateReportDetail(self, File, ModulePcdSet, ReportSubType = 0):
862 PcdDict = self.AllPcds
863 if ReportSubType == 1:
864 PcdDict = self.ConditionalPcds
865 elif ReportSubType == 2:
866 PcdDict = self.UnusedPcds
867
868 if ModulePcdSet == None:
869 FileWrite(File, gSectionStart)
870 if ReportSubType == 1:
871 FileWrite(File, "Conditional Directives used by the build system")
872 elif ReportSubType == 2:
873 FileWrite(File, "PCDs not used by modules or in conditional directives")
874 else:
875 FileWrite(File, "Platform Configuration Database Report")
876
877 FileWrite(File, " *B - PCD override in the build option")
878 FileWrite(File, " *P - Platform scoped PCD override in DSC file")
879 FileWrite(File, " *F - Platform scoped PCD override in FDF file")
880 if not ReportSubType:
881 FileWrite(File, " *M - Module scoped PCD override")
882 FileWrite(File, gSectionSep)
883 else:
884 if not ReportSubType and ModulePcdSet:
885 #
886 # For module PCD sub-section
887 #
888 FileWrite(File, gSubSectionStart)
889 FileWrite(File, TAB_BRG_PCD)
890 FileWrite(File, gSubSectionSep)
891
892 for Key in PcdDict:
893 #
894 # Group PCD by their token space GUID C Name
895 #
896 First = True
897 for Type in PcdDict[Key]:
898 #
899 # Group PCD by their usage type
900 #
901 TypeName, DecType = gPcdTypeMap.get(Type, ("", Type))
902 for Pcd in PcdDict[Key][Type]:
903 PcdTokenCName = Pcd.TokenCName
904 MixedPcdFlag = False
905 if GlobalData.MixedPcd:
906 for PcdKey in GlobalData.MixedPcd:
907 if (Pcd.TokenCName, Pcd.TokenSpaceGuidCName) in GlobalData.MixedPcd[PcdKey]:
908 PcdTokenCName = PcdKey[0]
909 MixedPcdFlag = True
910 if MixedPcdFlag and not ModulePcdSet:
911 continue
912 #
913 # Get PCD default value and their override relationship
914 #
915 DecDefaultValue = self.DecPcdDefault.get((Pcd.TokenCName, Pcd.TokenSpaceGuidCName, DecType))
916 DscDefaultValue = self.DscPcdDefault.get((Pcd.TokenCName, Pcd.TokenSpaceGuidCName))
917 DscDefaultValue = self.FdfPcdSet.get((Pcd.TokenCName, Key), DscDefaultValue)
918 InfDefaultValue = None
919
920 PcdValue = DecDefaultValue
921 if DscDefaultValue:
922 PcdValue = DscDefaultValue
923 if ModulePcdSet != None:
924 if (Pcd.TokenCName, Pcd.TokenSpaceGuidCName, Type) not in ModulePcdSet:
925 continue
926 InfDefault, PcdValue = ModulePcdSet[Pcd.TokenCName, Pcd.TokenSpaceGuidCName, Type]
927 if InfDefault == "":
928 InfDefault = None
929
930 BuildOptionMatch = False
931 if GlobalData.BuildOptionPcd:
932 for pcd in GlobalData.BuildOptionPcd:
933 if (Pcd.TokenSpaceGuidCName, Pcd.TokenCName) == (pcd[0], pcd[1]):
934 PcdValue = pcd[2]
935 BuildOptionMatch = True
936 break
937
938 if First:
939 if ModulePcdSet == None:
940 FileWrite(File, "")
941 FileWrite(File, Key)
942 First = False
943
944
945 if Pcd.DatumType in ('UINT8', 'UINT16', 'UINT32', 'UINT64'):
946 PcdValueNumber = int(PcdValue.strip(), 0)
947 if DecDefaultValue == None:
948 DecMatch = True
949 else:
950 DecDefaultValueNumber = int(DecDefaultValue.strip(), 0)
951 DecMatch = (DecDefaultValueNumber == PcdValueNumber)
952
953 if InfDefaultValue == None:
954 InfMatch = True
955 else:
956 InfDefaultValueNumber = int(InfDefaultValue.strip(), 0)
957 InfMatch = (InfDefaultValueNumber == PcdValueNumber)
958
959 if DscDefaultValue == None:
960 DscMatch = True
961 else:
962 DscDefaultValueNumber = int(DscDefaultValue.strip(), 0)
963 DscMatch = (DscDefaultValueNumber == PcdValueNumber)
964 else:
965 if DecDefaultValue == None:
966 DecMatch = True
967 else:
968 DecMatch = (DecDefaultValue.strip() == PcdValue.strip())
969
970 if InfDefaultValue == None:
971 InfMatch = True
972 else:
973 InfMatch = (InfDefaultValue.strip() == PcdValue.strip())
974
975 if DscDefaultValue == None:
976 DscMatch = True
977 else:
978 DscMatch = (DscDefaultValue.strip() == PcdValue.strip())
979
980 #
981 # Report PCD item according to their override relationship
982 #
983 if DecMatch and InfMatch:
984 FileWrite(File, ' %-*s: %6s %10s = %-22s' % (self.MaxLen, PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', PcdValue.strip()))
985 elif BuildOptionMatch:
986 FileWrite(File, ' *B %-*s: %6s %10s = %-22s' % (self.MaxLen, PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', PcdValue.strip()))
987 else:
988 if DscMatch:
989 if (Pcd.TokenCName, Key) in self.FdfPcdSet:
990 FileWrite(File, ' *F %-*s: %6s %10s = %-22s' % (self.MaxLen, PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', PcdValue.strip()))
991 else:
992 FileWrite(File, ' *P %-*s: %6s %10s = %-22s' % (self.MaxLen, PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', PcdValue.strip()))
993 else:
994 FileWrite(File, ' *M %-*s: %6s %10s = %-22s' % (self.MaxLen, PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', PcdValue.strip()))
995
996 if TypeName in ('DYNHII', 'DEXHII', 'DYNVPD', 'DEXVPD'):
997 for SkuInfo in Pcd.SkuInfoList.values():
998 if TypeName in ('DYNHII', 'DEXHII'):
999 FileWrite(File, '%*s: %s: %s' % (self.MaxLen + 4, SkuInfo.VariableGuid, SkuInfo.VariableName, SkuInfo.VariableOffset))
1000 else:
1001 FileWrite(File, '%*s' % (self.MaxLen + 4, SkuInfo.VpdOffset))
1002
1003 if not DscMatch and DscDefaultValue != None:
1004 FileWrite(File, ' %*s = %s' % (self.MaxLen + 19, 'DSC DEFAULT', DscDefaultValue.strip()))
1005
1006 if not InfMatch and InfDefaultValue != None:
1007 FileWrite(File, ' %*s = %s' % (self.MaxLen + 19, 'INF DEFAULT', InfDefaultValue.strip()))
1008
1009 if not DecMatch and DecDefaultValue != None:
1010 FileWrite(File, ' %*s = %s' % (self.MaxLen + 19, 'DEC DEFAULT', DecDefaultValue.strip()))
1011
1012 if ModulePcdSet == None:
1013 if not BuildOptionMatch:
1014 ModuleOverride = self.ModulePcdOverride.get((Pcd.TokenCName, Pcd.TokenSpaceGuidCName), {})
1015 for ModulePath in ModuleOverride:
1016 ModuleDefault = ModuleOverride[ModulePath]
1017 if Pcd.DatumType in ('UINT8', 'UINT16', 'UINT32', 'UINT64'):
1018 ModulePcdDefaultValueNumber = int(ModuleDefault.strip(), 0)
1019 Match = (ModulePcdDefaultValueNumber == PcdValueNumber)
1020 else:
1021 Match = (ModuleDefault.strip() == PcdValue.strip())
1022 if Match:
1023 continue
1024 FileWrite(File, ' *M %-*s = %s' % (self.MaxLen + 19, ModulePath, ModuleDefault.strip()))
1025
1026 if ModulePcdSet == None:
1027 FileWrite(File, gSectionEnd)
1028 else:
1029 if not ReportSubType and ModulePcdSet:
1030 FileWrite(File, gSubSectionEnd)
1031
1032
1033
1034 ##
1035 # Reports platform and module Prediction information
1036 #
1037 # This class reports the platform execution order prediction section and
1038 # module load fixed address prediction subsection in the build report file.
1039 #
1040 class PredictionReport(object):
1041 ##
1042 # Constructor function for class PredictionReport
1043 #
1044 # This constructor function generates PredictionReport object for the platform.
1045 #
1046 # @param self: The object pointer
1047 # @param Wa Workspace context information
1048 #
1049 def __init__(self, Wa):
1050 self._MapFileName = os.path.join(Wa.BuildDir, Wa.Name + ".map")
1051 self._MapFileParsed = False
1052 self._EotToolInvoked = False
1053 self._FvDir = Wa.FvDir
1054 self._EotDir = Wa.BuildDir
1055 self._FfsEntryPoint = {}
1056 self._GuidMap = {}
1057 self._SourceList = []
1058 self.FixedMapDict = {}
1059 self.ItemList = []
1060 self.MaxLen = 0
1061
1062 #
1063 # Collect all platform reference source files and GUID C Name
1064 #
1065 for Pa in Wa.AutoGenObjectList:
1066 for Module in Pa.LibraryAutoGenList + Pa.ModuleAutoGenList:
1067 #
1068 # BASE typed modules are EFI agnostic, so we need not scan
1069 # their source code to find PPI/Protocol produce or consume
1070 # information.
1071 #
1072 if Module.ModuleType == "BASE":
1073 continue
1074 #
1075 # Add module referenced source files
1076 #
1077 self._SourceList.append(str(Module))
1078 IncludeList = {}
1079 for Source in Module.SourceFileList:
1080 if os.path.splitext(str(Source))[1].lower() == ".c":
1081 self._SourceList.append(" " + str(Source))
1082 FindIncludeFiles(Source.Path, Module.IncludePathList, IncludeList)
1083 for IncludeFile in IncludeList.values():
1084 self._SourceList.append(" " + IncludeFile)
1085
1086 for Guid in Module.PpiList:
1087 self._GuidMap[Guid] = GuidStructureStringToGuidString(Module.PpiList[Guid])
1088 for Guid in Module.ProtocolList:
1089 self._GuidMap[Guid] = GuidStructureStringToGuidString(Module.ProtocolList[Guid])
1090 for Guid in Module.GuidList:
1091 self._GuidMap[Guid] = GuidStructureStringToGuidString(Module.GuidList[Guid])
1092
1093 if Module.Guid and not Module.IsLibrary:
1094 EntryPoint = " ".join(Module.Module.ModuleEntryPointList)
1095 if int(str(Module.AutoGenVersion), 0) >= 0x00010005:
1096 RealEntryPoint = "_ModuleEntryPoint"
1097 else:
1098 RealEntryPoint = EntryPoint
1099 if EntryPoint == "_ModuleEntryPoint":
1100 CCFlags = Module.BuildOption.get("CC", {}).get("FLAGS", "")
1101 Match = gGlueLibEntryPoint.search(CCFlags)
1102 if Match:
1103 EntryPoint = Match.group(1)
1104
1105 self._FfsEntryPoint[Module.Guid.upper()] = (EntryPoint, RealEntryPoint)
1106
1107
1108 #
1109 # Collect platform firmware volume list as the input of EOT.
1110 #
1111 self._FvList = []
1112 if Wa.FdfProfile:
1113 for Fd in Wa.FdfProfile.FdDict:
1114 for FdRegion in Wa.FdfProfile.FdDict[Fd].RegionList:
1115 if FdRegion.RegionType != "FV":
1116 continue
1117 for FvName in FdRegion.RegionDataList:
1118 if FvName in self._FvList:
1119 continue
1120 self._FvList.append(FvName)
1121 for Ffs in Wa.FdfProfile.FvDict[FvName.upper()].FfsList:
1122 for Section in Ffs.SectionList:
1123 try:
1124 for FvSection in Section.SectionList:
1125 if FvSection.FvName in self._FvList:
1126 continue
1127 self._FvList.append(FvSection.FvName)
1128 except AttributeError:
1129 pass
1130
1131
1132 ##
1133 # Parse platform fixed address map files
1134 #
1135 # This function parses the platform final fixed address map file to get
1136 # the database of predicted fixed address for module image base, entry point
1137 # etc.
1138 #
1139 # @param self: The object pointer
1140 #
1141 def _ParseMapFile(self):
1142 if self._MapFileParsed:
1143 return
1144 self._MapFileParsed = True
1145 if os.path.isfile(self._MapFileName):
1146 try:
1147 FileContents = open(self._MapFileName).read()
1148 for Match in gMapFileItemPattern.finditer(FileContents):
1149 AddressType = Match.group(1)
1150 BaseAddress = Match.group(2)
1151 EntryPoint = Match.group(3)
1152 Guid = Match.group(4).upper()
1153 List = self.FixedMapDict.setdefault(Guid, [])
1154 List.append((AddressType, BaseAddress, "*I"))
1155 List.append((AddressType, EntryPoint, "*E"))
1156 except:
1157 EdkLogger.warn(None, "Cannot open file to read", self._MapFileName)
1158
1159 ##
1160 # Invokes EOT tool to get the predicted the execution order.
1161 #
1162 # This function invokes EOT tool to calculate the predicted dispatch order
1163 #
1164 # @param self: The object pointer
1165 #
1166 def _InvokeEotTool(self):
1167 if self._EotToolInvoked:
1168 return
1169
1170 self._EotToolInvoked = True
1171 FvFileList = []
1172 for FvName in self._FvList:
1173 FvFile = os.path.join(self._FvDir, FvName + ".Fv")
1174 if os.path.isfile(FvFile):
1175 FvFileList.append(FvFile)
1176
1177 if len(FvFileList) == 0:
1178 return
1179 #
1180 # Write source file list and GUID file list to an intermediate file
1181 # as the input for EOT tool and dispatch List as the output file
1182 # from EOT tool.
1183 #
1184 SourceList = os.path.join(self._EotDir, "SourceFile.txt")
1185 GuidList = os.path.join(self._EotDir, "GuidList.txt")
1186 DispatchList = os.path.join(self._EotDir, "Dispatch.txt")
1187
1188 TempFile = open(SourceList, "w+")
1189 for Item in self._SourceList:
1190 FileWrite(TempFile, Item)
1191 TempFile.close()
1192 TempFile = open(GuidList, "w+")
1193 for Key in self._GuidMap:
1194 FileWrite(TempFile, "%s %s" % (Key, self._GuidMap[Key]))
1195 TempFile.close()
1196
1197 try:
1198 from Eot.Eot import Eot
1199
1200 #
1201 # Invoke EOT tool and echo its runtime performance
1202 #
1203 EotStartTime = time.time()
1204 Eot(CommandLineOption=False, SourceFileList=SourceList, GuidList=GuidList,
1205 FvFileList=' '.join(FvFileList), Dispatch=DispatchList, IsInit=True)
1206 EotEndTime = time.time()
1207 EotDuration = time.strftime("%H:%M:%S", time.gmtime(int(round(EotEndTime - EotStartTime))))
1208 EdkLogger.quiet("EOT run time: %s\n" % EotDuration)
1209
1210 #
1211 # Parse the output of EOT tool
1212 #
1213 for Line in open(DispatchList):
1214 if len(Line.split()) < 4:
1215 continue
1216 (Guid, Phase, FfsName, FilePath) = Line.split()
1217 Symbol = self._FfsEntryPoint.get(Guid, [FfsName, ""])[0]
1218 if len(Symbol) > self.MaxLen:
1219 self.MaxLen = len(Symbol)
1220 self.ItemList.append((Phase, Symbol, FilePath))
1221 except:
1222 EdkLogger.quiet("(Python %s on %s\n%s)" % (platform.python_version(), sys.platform, traceback.format_exc()))
1223 EdkLogger.warn(None, "Failed to generate execution order prediction report, for some error occurred in executing EOT.")
1224
1225
1226 ##
1227 # Generate platform execution order report
1228 #
1229 # This function generates the predicted module execution order.
1230 #
1231 # @param self The object pointer
1232 # @param File The file object for report
1233 #
1234 def _GenerateExecutionOrderReport(self, File):
1235 self._InvokeEotTool()
1236 if len(self.ItemList) == 0:
1237 return
1238 FileWrite(File, gSectionStart)
1239 FileWrite(File, "Execution Order Prediction")
1240 FileWrite(File, "*P PEI phase")
1241 FileWrite(File, "*D DXE phase")
1242 FileWrite(File, "*E Module INF entry point name")
1243 FileWrite(File, "*N Module notification function name")
1244
1245 FileWrite(File, "Type %-*s %s" % (self.MaxLen, "Symbol", "Module INF Path"))
1246 FileWrite(File, gSectionSep)
1247 for Item in self.ItemList:
1248 FileWrite(File, "*%sE %-*s %s" % (Item[0], self.MaxLen, Item[1], Item[2]))
1249
1250 FileWrite(File, gSectionStart)
1251
1252 ##
1253 # Generate Fixed Address report.
1254 #
1255 # This function generate the predicted fixed address report for a module
1256 # specified by Guid.
1257 #
1258 # @param self The object pointer
1259 # @param File The file object for report
1260 # @param Guid The module Guid value.
1261 # @param NotifyList The list of all notify function in a module
1262 #
1263 def _GenerateFixedAddressReport(self, File, Guid, NotifyList):
1264 self._ParseMapFile()
1265 FixedAddressList = self.FixedMapDict.get(Guid)
1266 if not FixedAddressList:
1267 return
1268
1269 FileWrite(File, gSubSectionStart)
1270 FileWrite(File, "Fixed Address Prediction")
1271 FileWrite(File, "*I Image Loading Address")
1272 FileWrite(File, "*E Entry Point Address")
1273 FileWrite(File, "*N Notification Function Address")
1274 FileWrite(File, "*F Flash Address")
1275 FileWrite(File, "*M Memory Address")
1276 FileWrite(File, "*S SMM RAM Offset")
1277 FileWrite(File, "TOM Top of Memory")
1278
1279 FileWrite(File, "Type Address Name")
1280 FileWrite(File, gSubSectionSep)
1281 for Item in FixedAddressList:
1282 Type = Item[0]
1283 Value = Item[1]
1284 Symbol = Item[2]
1285 if Symbol == "*I":
1286 Name = "(Image Base)"
1287 elif Symbol == "*E":
1288 Name = self._FfsEntryPoint.get(Guid, ["", "_ModuleEntryPoint"])[1]
1289 elif Symbol in NotifyList:
1290 Name = Symbol
1291 Symbol = "*N"
1292 else:
1293 continue
1294
1295 if "Flash" in Type:
1296 Symbol += "F"
1297 elif "Memory" in Type:
1298 Symbol += "M"
1299 else:
1300 Symbol += "S"
1301
1302 if Value[0] == "-":
1303 Value = "TOM" + Value
1304
1305 FileWrite(File, "%s %-16s %s" % (Symbol, Value, Name))
1306
1307 ##
1308 # Generate report for the prediction part
1309 #
1310 # This function generate the predicted fixed address report for a module or
1311 # predicted module execution order for a platform.
1312 # If the input Guid is None, then, it generates the predicted module execution order;
1313 # otherwise it generated the module fixed loading address for the module specified by
1314 # Guid.
1315 #
1316 # @param self The object pointer
1317 # @param File The file object for report
1318 # @param Guid The module Guid value.
1319 #
1320 def GenerateReport(self, File, Guid):
1321 if Guid:
1322 self._GenerateFixedAddressReport(File, Guid.upper(), [])
1323 else:
1324 self._GenerateExecutionOrderReport(File)
1325
1326 ##
1327 # Reports FD region information
1328 #
1329 # This class reports the FD subsection in the build report file.
1330 # It collects region information of platform flash device.
1331 # If the region is a firmware volume, it lists the set of modules
1332 # and its space information; otherwise, it only lists its region name,
1333 # base address and size in its sub-section header.
1334 # If there are nesting FVs, the nested FVs will list immediate after
1335 # this FD region subsection
1336 #
1337 class FdRegionReport(object):
1338 ##
1339 # Discover all the nested FV name list.
1340 #
1341 # This is an internal worker function to discover the all the nested FV information
1342 # in the parent firmware volume. It uses deep first search algorithm recursively to
1343 # find all the FV list name and append them to the list.
1344 #
1345 # @param self The object pointer
1346 # @param FvName The name of current firmware file system
1347 # @param Wa Workspace context information
1348 #
1349 def _DiscoverNestedFvList(self, FvName, Wa):
1350 FvDictKey=FvName.upper()
1351 if FvDictKey in Wa.FdfProfile.FvDict:
1352 for Ffs in Wa.FdfProfile.FvDict[FvName.upper()].FfsList:
1353 for Section in Ffs.SectionList:
1354 try:
1355 for FvSection in Section.SectionList:
1356 if FvSection.FvName in self.FvList:
1357 continue
1358 self._GuidsDb[Ffs.NameGuid.upper()] = FvSection.FvName
1359 self.FvList.append(FvSection.FvName)
1360 self.FvInfo[FvSection.FvName] = ("Nested FV", 0, 0)
1361 self._DiscoverNestedFvList(FvSection.FvName, Wa)
1362 except AttributeError:
1363 pass
1364
1365 ##
1366 # Constructor function for class FdRegionReport
1367 #
1368 # This constructor function generates FdRegionReport object for a specified FdRegion.
1369 # If the FdRegion is a firmware volume, it will recursively find all its nested Firmware
1370 # volume list. This function also collects GUID map in order to dump module identification
1371 # in the final report.
1372 #
1373 # @param self: The object pointer
1374 # @param FdRegion The current FdRegion object
1375 # @param Wa Workspace context information
1376 #
1377 def __init__(self, FdRegion, Wa):
1378 self.Type = FdRegion.RegionType
1379 self.BaseAddress = FdRegion.Offset
1380 self.Size = FdRegion.Size
1381 self.FvList = []
1382 self.FvInfo = {}
1383 self._GuidsDb = {}
1384 self._FvDir = Wa.FvDir
1385
1386 #
1387 # If the input FdRegion is not a firmware volume,
1388 # we are done.
1389 #
1390 if self.Type != "FV":
1391 return
1392
1393 #
1394 # Find all nested FVs in the FdRegion
1395 #
1396 for FvName in FdRegion.RegionDataList:
1397 if FvName in self.FvList:
1398 continue
1399 self.FvList.append(FvName)
1400 self.FvInfo[FvName] = ("Fd Region", self.BaseAddress, self.Size)
1401 self._DiscoverNestedFvList(FvName, Wa)
1402
1403 PlatformPcds = {}
1404 #
1405 # Collect PCDs declared in DEC files.
1406 #
1407 for Pa in Wa.AutoGenObjectList:
1408 for Package in Pa.PackageList:
1409 for (TokenCName, TokenSpaceGuidCName, DecType) in Package.Pcds:
1410 DecDefaultValue = Package.Pcds[TokenCName, TokenSpaceGuidCName, DecType].DefaultValue
1411 PlatformPcds[(TokenCName, TokenSpaceGuidCName)] = DecDefaultValue
1412 #
1413 # Collect PCDs defined in DSC file
1414 #
1415 for arch in Wa.ArchList:
1416 Platform = Wa.BuildDatabase[Wa.MetaFile, arch]
1417 for (TokenCName, TokenSpaceGuidCName) in Platform.Pcds:
1418 DscDefaultValue = Platform.Pcds[(TokenCName, TokenSpaceGuidCName)].DefaultValue
1419 PlatformPcds[(TokenCName, TokenSpaceGuidCName)] = DscDefaultValue
1420
1421 #
1422 # Add PEI and DXE a priori files GUIDs defined in PI specification.
1423 #
1424 self._GuidsDb["1B45CC0A-156A-428A-AF62-49864DA0E6E6"] = "PEI Apriori"
1425 self._GuidsDb["FC510EE7-FFDC-11D4-BD41-0080C73C8881"] = "DXE Apriori"
1426 #
1427 # Add ACPI table storage file
1428 #
1429 self._GuidsDb["7E374E25-8E01-4FEE-87F2-390C23C606CD"] = "ACPI table storage"
1430
1431 for Pa in Wa.AutoGenObjectList:
1432 for ModuleKey in Pa.Platform.Modules:
1433 M = Pa.Platform.Modules[ModuleKey].M
1434 InfPath = mws.join(Wa.WorkspaceDir, M.MetaFile.File)
1435 self._GuidsDb[M.Guid.upper()] = "%s (%s)" % (M.Module.BaseName, InfPath)
1436
1437 #
1438 # Collect the GUID map in the FV firmware volume
1439 #
1440 for FvName in self.FvList:
1441 FvDictKey=FvName.upper()
1442 if FvDictKey in Wa.FdfProfile.FvDict:
1443 for Ffs in Wa.FdfProfile.FvDict[FvName.upper()].FfsList:
1444 try:
1445 #
1446 # collect GUID map for binary EFI file in FDF file.
1447 #
1448 Guid = Ffs.NameGuid.upper()
1449 Match = gPcdGuidPattern.match(Ffs.NameGuid)
1450 if Match:
1451 PcdTokenspace = Match.group(1)
1452 PcdToken = Match.group(2)
1453 if (PcdToken, PcdTokenspace) in PlatformPcds:
1454 GuidValue = PlatformPcds[(PcdToken, PcdTokenspace)]
1455 Guid = GuidStructureByteArrayToGuidString(GuidValue).upper()
1456 for Section in Ffs.SectionList:
1457 try:
1458 ModuleSectFile = mws.join(Wa.WorkspaceDir, Section.SectFileName)
1459 self._GuidsDb[Guid] = ModuleSectFile
1460 except AttributeError:
1461 pass
1462 except AttributeError:
1463 pass
1464
1465
1466 ##
1467 # Internal worker function to generate report for the FD region
1468 #
1469 # This internal worker function to generate report for the FD region.
1470 # It the type is firmware volume, it lists offset and module identification.
1471 #
1472 # @param self The object pointer
1473 # @param File The file object for report
1474 # @param Title The title for the FD subsection
1475 # @param BaseAddress The base address for the FD region
1476 # @param Size The size of the FD region
1477 # @param FvName The FV name if the FD region is a firmware volume
1478 #
1479 def _GenerateReport(self, File, Title, Type, BaseAddress, Size=0, FvName=None):
1480 FileWrite(File, gSubSectionStart)
1481 FileWrite(File, Title)
1482 FileWrite(File, "Type: %s" % Type)
1483 FileWrite(File, "Base Address: 0x%X" % BaseAddress)
1484
1485 if self.Type == "FV":
1486 FvTotalSize = 0
1487 FvTakenSize = 0
1488 FvFreeSize = 0
1489 FvReportFileName = os.path.join(self._FvDir, FvName + ".Fv.txt")
1490 try:
1491 #
1492 # Collect size info in the firmware volume.
1493 #
1494 FvReport = open(FvReportFileName).read()
1495 Match = gFvTotalSizePattern.search(FvReport)
1496 if Match:
1497 FvTotalSize = int(Match.group(1), 16)
1498 Match = gFvTakenSizePattern.search(FvReport)
1499 if Match:
1500 FvTakenSize = int(Match.group(1), 16)
1501 FvFreeSize = FvTotalSize - FvTakenSize
1502 #
1503 # Write size information to the report file.
1504 #
1505 FileWrite(File, "Size: 0x%X (%.0fK)" % (FvTotalSize, FvTotalSize / 1024.0))
1506 FileWrite(File, "Fv Name: %s (%.1f%% Full)" % (FvName, FvTakenSize * 100.0 / FvTotalSize))
1507 FileWrite(File, "Occupied Size: 0x%X (%.0fK)" % (FvTakenSize, FvTakenSize / 1024.0))
1508 FileWrite(File, "Free Size: 0x%X (%.0fK)" % (FvFreeSize, FvFreeSize / 1024.0))
1509 FileWrite(File, "Offset Module")
1510 FileWrite(File, gSubSectionSep)
1511 #
1512 # Write module offset and module identification to the report file.
1513 #
1514 OffsetInfo = {}
1515 for Match in gOffsetGuidPattern.finditer(FvReport):
1516 Guid = Match.group(2).upper()
1517 OffsetInfo[Match.group(1)] = self._GuidsDb.get(Guid, Guid)
1518 OffsetList = OffsetInfo.keys()
1519 OffsetList.sort()
1520 for Offset in OffsetList:
1521 FileWrite (File, "%s %s" % (Offset, OffsetInfo[Offset]))
1522 except IOError:
1523 EdkLogger.warn(None, "Fail to read report file", FvReportFileName)
1524 else:
1525 FileWrite(File, "Size: 0x%X (%.0fK)" % (Size, Size / 1024.0))
1526 FileWrite(File, gSubSectionEnd)
1527
1528 ##
1529 # Generate report for the FD region
1530 #
1531 # This function generates report for the FD region.
1532 #
1533 # @param self The object pointer
1534 # @param File The file object for report
1535 #
1536 def GenerateReport(self, File):
1537 if (len(self.FvList) > 0):
1538 for FvItem in self.FvList:
1539 Info = self.FvInfo[FvItem]
1540 self._GenerateReport(File, Info[0], "FV", Info[1], Info[2], FvItem)
1541 else:
1542 self._GenerateReport(File, "FD Region", self.Type, self.BaseAddress, self.Size)
1543
1544 ##
1545 # Reports FD information
1546 #
1547 # This class reports the FD section in the build report file.
1548 # It collects flash device information for a platform.
1549 #
1550 class FdReport(object):
1551 ##
1552 # Constructor function for class FdReport
1553 #
1554 # This constructor function generates FdReport object for a specified
1555 # firmware device.
1556 #
1557 # @param self The object pointer
1558 # @param Fd The current Firmware device object
1559 # @param Wa Workspace context information
1560 #
1561 def __init__(self, Fd, Wa):
1562 self.FdName = Fd.FdUiName
1563 self.BaseAddress = Fd.BaseAddress
1564 self.Size = Fd.Size
1565 self.FdRegionList = [FdRegionReport(FdRegion, Wa) for FdRegion in Fd.RegionList]
1566 self.FvPath = os.path.join(Wa.BuildDir, "FV")
1567 self.VpdFilePath = os.path.join(self.FvPath, "%s.map" % Wa.Platform.VpdToolGuid)
1568 self.VPDBaseAddress = 0
1569 self.VPDSize = 0
1570 self.VPDInfoList = []
1571 for index, FdRegion in enumerate(Fd.RegionList):
1572 if str(FdRegion.RegionType) is 'FILE' and Wa.Platform.VpdToolGuid in str(FdRegion.RegionDataList):
1573 self.VPDBaseAddress = self.FdRegionList[index].BaseAddress
1574 self.VPDSize = self.FdRegionList[index].Size
1575 break
1576
1577 if os.path.isfile(self.VpdFilePath):
1578 fd = open(self.VpdFilePath, "r")
1579 Lines = fd.readlines()
1580 for Line in Lines:
1581 Line = Line.strip()
1582 if len(Line) == 0 or Line.startswith("#"):
1583 continue
1584 try:
1585 PcdName, SkuId, Offset, Size, Value = Line.split("#")[0].split("|")
1586 PcdName, SkuId, Offset, Size, Value = PcdName.strip(), SkuId.strip(), Offset.strip(), Size.strip(), Value.strip()
1587 if Offset.lower().startswith('0x'):
1588 Offset = '0x%08X' % (int(Offset, 16) + self.VPDBaseAddress)
1589 else:
1590 Offset = '0x%08X' % (int(Offset, 10) + self.VPDBaseAddress)
1591 self.VPDInfoList.append("%s | %s | %s | %s | %s" % (PcdName, SkuId, Offset, Size, Value))
1592 except:
1593 EdkLogger.error("BuildReport", CODE_ERROR, "Fail to parse VPD information file %s" % self.VpdFilePath)
1594 fd.close()
1595
1596 ##
1597 # Generate report for the firmware device.
1598 #
1599 # This function generates report for the firmware device.
1600 #
1601 # @param self The object pointer
1602 # @param File The file object for report
1603 #
1604 def GenerateReport(self, File):
1605 FileWrite(File, gSectionStart)
1606 FileWrite(File, "Firmware Device (FD)")
1607 FileWrite(File, "FD Name: %s" % self.FdName)
1608 FileWrite(File, "Base Address: %s" % self.BaseAddress)
1609 FileWrite(File, "Size: 0x%X (%.0fK)" % (self.Size, self.Size / 1024.0))
1610 if len(self.FdRegionList) > 0:
1611 FileWrite(File, gSectionSep)
1612 for FdRegionItem in self.FdRegionList:
1613 FdRegionItem.GenerateReport(File)
1614
1615 if len(self.VPDInfoList) > 0:
1616 FileWrite(File, gSubSectionStart)
1617 FileWrite(File, "FD VPD Region")
1618 FileWrite(File, "Base Address: 0x%X" % self.VPDBaseAddress)
1619 FileWrite(File, "Size: 0x%X (%.0fK)" % (self.VPDSize, self.VPDSize / 1024.0))
1620 FileWrite(File, gSubSectionSep)
1621 for item in self.VPDInfoList:
1622 FileWrite(File, item)
1623 FileWrite(File, gSubSectionEnd)
1624 FileWrite(File, gSectionEnd)
1625
1626
1627
1628 ##
1629 # Reports platform information
1630 #
1631 # This class reports the whole platform information
1632 #
1633 class PlatformReport(object):
1634 ##
1635 # Constructor function for class PlatformReport
1636 #
1637 # This constructor function generates PlatformReport object a platform build.
1638 # It generates report for platform summary, flash, global PCDs and detailed
1639 # module information for modules involved in platform build.
1640 #
1641 # @param self The object pointer
1642 # @param Wa Workspace context information
1643 # @param MaList The list of modules in the platform build
1644 #
1645 def __init__(self, Wa, MaList, ReportType):
1646 self._WorkspaceDir = Wa.WorkspaceDir
1647 self.PlatformName = Wa.Name
1648 self.PlatformDscPath = Wa.Platform
1649 self.Architectures = " ".join(Wa.ArchList)
1650 self.ToolChain = Wa.ToolChain
1651 self.Target = Wa.BuildTarget
1652 self.OutputPath = os.path.join(Wa.WorkspaceDir, Wa.OutputDir)
1653 self.BuildEnvironment = platform.platform()
1654
1655 self.PcdReport = None
1656 if "PCD" in ReportType:
1657 self.PcdReport = PcdReport(Wa)
1658
1659 self.FdReportList = []
1660 if "FLASH" in ReportType and Wa.FdfProfile and MaList == None:
1661 for Fd in Wa.FdfProfile.FdDict:
1662 self.FdReportList.append(FdReport(Wa.FdfProfile.FdDict[Fd], Wa))
1663
1664 self.PredictionReport = None
1665 if "FIXED_ADDRESS" in ReportType or "EXECUTION_ORDER" in ReportType:
1666 self.PredictionReport = PredictionReport(Wa)
1667
1668 self.DepexParser = None
1669 if "DEPEX" in ReportType:
1670 self.DepexParser = DepexParser(Wa)
1671
1672 self.ModuleReportList = []
1673 if MaList != None:
1674 self._IsModuleBuild = True
1675 for Ma in MaList:
1676 self.ModuleReportList.append(ModuleReport(Ma, ReportType))
1677 else:
1678 self._IsModuleBuild = False
1679 for Pa in Wa.AutoGenObjectList:
1680 ModuleAutoGenList = []
1681 for ModuleKey in Pa.Platform.Modules:
1682 ModuleAutoGenList.append(Pa.Platform.Modules[ModuleKey].M)
1683 if GlobalData.gFdfParser != None:
1684 if Pa.Arch in GlobalData.gFdfParser.Profile.InfDict:
1685 INFList = GlobalData.gFdfParser.Profile.InfDict[Pa.Arch]
1686 for InfName in INFList:
1687 InfClass = PathClass(NormPath(InfName), Wa.WorkspaceDir, Pa.Arch)
1688 Ma = ModuleAutoGen(Wa, InfClass, Pa.BuildTarget, Pa.ToolChain, Pa.Arch, Wa.MetaFile)
1689 if Ma == None:
1690 continue
1691 if Ma not in ModuleAutoGenList:
1692 ModuleAutoGenList.append(Ma)
1693 for MGen in ModuleAutoGenList:
1694 self.ModuleReportList.append(ModuleReport(MGen, ReportType))
1695
1696
1697
1698 ##
1699 # Generate report for the whole platform.
1700 #
1701 # This function generates report for platform information.
1702 # It comprises of platform summary, global PCD, flash and
1703 # module list sections.
1704 #
1705 # @param self The object pointer
1706 # @param File The file object for report
1707 # @param BuildDuration The total time to build the modules
1708 # @param AutoGenTime The total time of AutoGen Phase
1709 # @param MakeTime The total time of Make Phase
1710 # @param GenFdsTime The total time of GenFds Phase
1711 # @param ReportType The kind of report items in the final report file
1712 #
1713 def GenerateReport(self, File, BuildDuration, AutoGenTime, MakeTime, GenFdsTime, ReportType):
1714 FileWrite(File, "Platform Summary")
1715 FileWrite(File, "Platform Name: %s" % self.PlatformName)
1716 FileWrite(File, "Platform DSC Path: %s" % self.PlatformDscPath)
1717 FileWrite(File, "Architectures: %s" % self.Architectures)
1718 FileWrite(File, "Tool Chain: %s" % self.ToolChain)
1719 FileWrite(File, "Target: %s" % self.Target)
1720 FileWrite(File, "Output Path: %s" % self.OutputPath)
1721 FileWrite(File, "Build Environment: %s" % self.BuildEnvironment)
1722 FileWrite(File, "Build Duration: %s" % BuildDuration)
1723 if AutoGenTime:
1724 FileWrite(File, "AutoGen Duration: %s" % AutoGenTime)
1725 if MakeTime:
1726 FileWrite(File, "Make Duration: %s" % MakeTime)
1727 if GenFdsTime:
1728 FileWrite(File, "GenFds Duration: %s" % GenFdsTime)
1729 FileWrite(File, "Report Content: %s" % ", ".join(ReportType))
1730
1731 if GlobalData.MixedPcd:
1732 FileWrite(File, gSectionStart)
1733 FileWrite(File, "The following PCDs use different access methods:")
1734 FileWrite(File, gSectionSep)
1735 for PcdItem in GlobalData.MixedPcd:
1736 FileWrite(File, "%s.%s" % (str(PcdItem[1]), str(PcdItem[0])))
1737 FileWrite(File, gSectionEnd)
1738
1739 if not self._IsModuleBuild:
1740 if "PCD" in ReportType:
1741 self.PcdReport.GenerateReport(File, None)
1742
1743 if "FLASH" in ReportType:
1744 for FdReportListItem in self.FdReportList:
1745 FdReportListItem.GenerateReport(File)
1746
1747 for ModuleReportItem in self.ModuleReportList:
1748 ModuleReportItem.GenerateReport(File, self.PcdReport, self.PredictionReport, self.DepexParser, ReportType)
1749
1750 if not self._IsModuleBuild:
1751 if "EXECUTION_ORDER" in ReportType:
1752 self.PredictionReport.GenerateReport(File, None)
1753
1754 ## BuildReport class
1755 #
1756 # This base class contain the routines to collect data and then
1757 # applies certain format to the output report
1758 #
1759 class BuildReport(object):
1760 ##
1761 # Constructor function for class BuildReport
1762 #
1763 # This constructor function generates BuildReport object a platform build.
1764 # It generates report for platform summary, flash, global PCDs and detailed
1765 # module information for modules involved in platform build.
1766 #
1767 # @param self The object pointer
1768 # @param ReportFile The file name to save report file
1769 # @param ReportType The kind of report items in the final report file
1770 #
1771 def __init__(self, ReportFile, ReportType):
1772 self.ReportFile = ReportFile
1773 if ReportFile:
1774 self.ReportList = []
1775 self.ReportType = []
1776 if ReportType:
1777 for ReportTypeItem in ReportType:
1778 if ReportTypeItem not in self.ReportType:
1779 self.ReportType.append(ReportTypeItem)
1780 else:
1781 self.ReportType = ["PCD", "LIBRARY", "BUILD_FLAGS", "DEPEX", "HASH", "FLASH", "FIXED_ADDRESS"]
1782 ##
1783 # Adds platform report to the list
1784 #
1785 # This function adds a platform report to the final report list.
1786 #
1787 # @param self The object pointer
1788 # @param Wa Workspace context information
1789 # @param MaList The list of modules in the platform build
1790 #
1791 def AddPlatformReport(self, Wa, MaList=None):
1792 if self.ReportFile:
1793 self.ReportList.append((Wa, MaList))
1794
1795 ##
1796 # Generates the final report.
1797 #
1798 # This function generates platform build report. It invokes GenerateReport()
1799 # method for every platform report in the list.
1800 #
1801 # @param self The object pointer
1802 # @param BuildDuration The total time to build the modules
1803 # @param AutoGenTime The total time of AutoGen phase
1804 # @param MakeTime The total time of Make phase
1805 # @param GenFdsTime The total time of GenFds phase
1806 #
1807 def GenerateReport(self, BuildDuration, AutoGenTime, MakeTime, GenFdsTime):
1808 if self.ReportFile:
1809 try:
1810 File = StringIO('')
1811 for (Wa, MaList) in self.ReportList:
1812 PlatformReport(Wa, MaList, self.ReportType).GenerateReport(File, BuildDuration, AutoGenTime, MakeTime, GenFdsTime, self.ReportType)
1813 Content = FileLinesSplit(File.getvalue(), gLineMaxLength)
1814 SaveFileOnChange(self.ReportFile, Content, True)
1815 EdkLogger.quiet("Build report can be found at %s" % os.path.abspath(self.ReportFile))
1816 except IOError:
1817 EdkLogger.error(None, FILE_WRITE_FAILURE, ExtraData=self.ReportFile)
1818 except:
1819 EdkLogger.error("BuildReport", CODE_ERROR, "Unknown fatal error when generating build report", ExtraData=self.ReportFile, RaiseError=False)
1820 EdkLogger.quiet("(Python %s on %s\n%s)" % (platform.python_version(), sys.platform, traceback.format_exc()))
1821 File.close()
1822
1823 # This acts like the main() function for the script, unless it is 'import'ed into another script.
1824 if __name__ == '__main__':
1825 pass
1826