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