]> git.proxmox.com Git - mirror_edk2.git/blob - BaseTools/Source/Python/build/BuildReport.py
6a4d7e099b69806f631d6dea6b5f37a79ca0126d
[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)].DscDefaultValue
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 DscDefaultValBak= DscDefaultValue
918 DscDefaultValue = self.FdfPcdSet.get((Pcd.TokenCName, Key), DscDefaultValue)
919 InfDefaultValue = None
920
921 PcdValue = DecDefaultValue
922 if DscDefaultValue:
923 PcdValue = DscDefaultValue
924 if ModulePcdSet != None:
925 if (Pcd.TokenCName, Pcd.TokenSpaceGuidCName, Type) not in ModulePcdSet:
926 continue
927 InfDefault, PcdValue = ModulePcdSet[Pcd.TokenCName, Pcd.TokenSpaceGuidCName, Type]
928 if InfDefault == "":
929 InfDefault = None
930
931 BuildOptionMatch = False
932 if GlobalData.BuildOptionPcd:
933 for pcd in GlobalData.BuildOptionPcd:
934 if (Pcd.TokenSpaceGuidCName, Pcd.TokenCName) == (pcd[0], pcd[1]):
935 PcdValue = pcd[2]
936 BuildOptionMatch = True
937 break
938
939 if First:
940 if ModulePcdSet == None:
941 FileWrite(File, "")
942 FileWrite(File, Key)
943 First = False
944
945
946 if Pcd.DatumType in ('UINT8', 'UINT16', 'UINT32', 'UINT64'):
947 PcdValueNumber = int(PcdValue.strip(), 0)
948 if DecDefaultValue == None:
949 DecMatch = True
950 else:
951 DecDefaultValueNumber = int(DecDefaultValue.strip(), 0)
952 DecMatch = (DecDefaultValueNumber == PcdValueNumber)
953
954 if InfDefaultValue == None:
955 InfMatch = True
956 else:
957 InfDefaultValueNumber = int(InfDefaultValue.strip(), 0)
958 InfMatch = (InfDefaultValueNumber == PcdValueNumber)
959
960 if DscDefaultValue == None:
961 DscMatch = True
962 else:
963 DscDefaultValueNumber = int(DscDefaultValue.strip(), 0)
964 DscMatch = (DscDefaultValueNumber == PcdValueNumber)
965 else:
966 if DecDefaultValue == None:
967 DecMatch = True
968 else:
969 DecMatch = (DecDefaultValue.strip() == PcdValue.strip())
970
971 if InfDefaultValue == None:
972 InfMatch = True
973 else:
974 InfMatch = (InfDefaultValue.strip() == PcdValue.strip())
975
976 if DscDefaultValue == None:
977 DscMatch = True
978 else:
979 DscMatch = (DscDefaultValue.strip() == PcdValue.strip())
980
981 #
982 # Report PCD item according to their override relationship
983 #
984 if DecMatch and InfMatch:
985 FileWrite(File, ' %-*s: %6s %10s = %-22s' % (self.MaxLen, PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', PcdValue.strip()))
986 elif BuildOptionMatch:
987 FileWrite(File, ' *B %-*s: %6s %10s = %-22s' % (self.MaxLen, PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', PcdValue.strip()))
988 else:
989 if DscMatch:
990 if (Pcd.TokenCName, Key) in self.FdfPcdSet:
991 FileWrite(File, ' *F %-*s: %6s %10s = %-22s' % (self.MaxLen, PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', PcdValue.strip()))
992 else:
993 FileWrite(File, ' *P %-*s: %6s %10s = %-22s' % (self.MaxLen, PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', PcdValue.strip()))
994 else:
995 FileWrite(File, ' *M %-*s: %6s %10s = %-22s' % (self.MaxLen, PcdTokenCName, TypeName, '(' + Pcd.DatumType + ')', PcdValue.strip()))
996
997 if TypeName in ('DYNHII', 'DEXHII', 'DYNVPD', 'DEXVPD'):
998 for SkuInfo in Pcd.SkuInfoList.values():
999 if TypeName in ('DYNHII', 'DEXHII'):
1000 FileWrite(File, '%*s: %s: %s' % (self.MaxLen + 4, SkuInfo.VariableGuid, SkuInfo.VariableName, SkuInfo.VariableOffset))
1001 else:
1002 FileWrite(File, '%*s' % (self.MaxLen + 4, SkuInfo.VpdOffset))
1003
1004 if not DscMatch and DscDefaultValBak != None:
1005 FileWrite(File, ' %*s = %s' % (self.MaxLen + 19, 'DSC DEFAULT', DscDefaultValBak.strip()))
1006
1007 if not InfMatch and InfDefaultValue != None:
1008 FileWrite(File, ' %*s = %s' % (self.MaxLen + 19, 'INF DEFAULT', InfDefaultValue.strip()))
1009
1010 if not DecMatch and DecDefaultValue != None:
1011 FileWrite(File, ' %*s = %s' % (self.MaxLen + 19, 'DEC DEFAULT', DecDefaultValue.strip()))
1012
1013 if ModulePcdSet == None:
1014 if not BuildOptionMatch:
1015 ModuleOverride = self.ModulePcdOverride.get((Pcd.TokenCName, Pcd.TokenSpaceGuidCName), {})
1016 for ModulePath in ModuleOverride:
1017 ModuleDefault = ModuleOverride[ModulePath]
1018 if Pcd.DatumType in ('UINT8', 'UINT16', 'UINT32', 'UINT64'):
1019 ModulePcdDefaultValueNumber = int(ModuleDefault.strip(), 0)
1020 Match = (ModulePcdDefaultValueNumber == PcdValueNumber)
1021 else:
1022 Match = (ModuleDefault.strip() == PcdValue.strip())
1023 if Match:
1024 continue
1025 FileWrite(File, ' *M %-*s = %s' % (self.MaxLen + 19, ModulePath, ModuleDefault.strip()))
1026
1027 if ModulePcdSet == None:
1028 FileWrite(File, gSectionEnd)
1029 else:
1030 if not ReportSubType and ModulePcdSet:
1031 FileWrite(File, gSubSectionEnd)
1032
1033
1034
1035 ##
1036 # Reports platform and module Prediction information
1037 #
1038 # This class reports the platform execution order prediction section and
1039 # module load fixed address prediction subsection in the build report file.
1040 #
1041 class PredictionReport(object):
1042 ##
1043 # Constructor function for class PredictionReport
1044 #
1045 # This constructor function generates PredictionReport object for the platform.
1046 #
1047 # @param self: The object pointer
1048 # @param Wa Workspace context information
1049 #
1050 def __init__(self, Wa):
1051 self._MapFileName = os.path.join(Wa.BuildDir, Wa.Name + ".map")
1052 self._MapFileParsed = False
1053 self._EotToolInvoked = False
1054 self._FvDir = Wa.FvDir
1055 self._EotDir = Wa.BuildDir
1056 self._FfsEntryPoint = {}
1057 self._GuidMap = {}
1058 self._SourceList = []
1059 self.FixedMapDict = {}
1060 self.ItemList = []
1061 self.MaxLen = 0
1062
1063 #
1064 # Collect all platform reference source files and GUID C Name
1065 #
1066 for Pa in Wa.AutoGenObjectList:
1067 for Module in Pa.LibraryAutoGenList + Pa.ModuleAutoGenList:
1068 #
1069 # BASE typed modules are EFI agnostic, so we need not scan
1070 # their source code to find PPI/Protocol produce or consume
1071 # information.
1072 #
1073 if Module.ModuleType == "BASE":
1074 continue
1075 #
1076 # Add module referenced source files
1077 #
1078 self._SourceList.append(str(Module))
1079 IncludeList = {}
1080 for Source in Module.SourceFileList:
1081 if os.path.splitext(str(Source))[1].lower() == ".c":
1082 self._SourceList.append(" " + str(Source))
1083 FindIncludeFiles(Source.Path, Module.IncludePathList, IncludeList)
1084 for IncludeFile in IncludeList.values():
1085 self._SourceList.append(" " + IncludeFile)
1086
1087 for Guid in Module.PpiList:
1088 self._GuidMap[Guid] = GuidStructureStringToGuidString(Module.PpiList[Guid])
1089 for Guid in Module.ProtocolList:
1090 self._GuidMap[Guid] = GuidStructureStringToGuidString(Module.ProtocolList[Guid])
1091 for Guid in Module.GuidList:
1092 self._GuidMap[Guid] = GuidStructureStringToGuidString(Module.GuidList[Guid])
1093
1094 if Module.Guid and not Module.IsLibrary:
1095 EntryPoint = " ".join(Module.Module.ModuleEntryPointList)
1096 if int(str(Module.AutoGenVersion), 0) >= 0x00010005:
1097 RealEntryPoint = "_ModuleEntryPoint"
1098 else:
1099 RealEntryPoint = EntryPoint
1100 if EntryPoint == "_ModuleEntryPoint":
1101 CCFlags = Module.BuildOption.get("CC", {}).get("FLAGS", "")
1102 Match = gGlueLibEntryPoint.search(CCFlags)
1103 if Match:
1104 EntryPoint = Match.group(1)
1105
1106 self._FfsEntryPoint[Module.Guid.upper()] = (EntryPoint, RealEntryPoint)
1107
1108
1109 #
1110 # Collect platform firmware volume list as the input of EOT.
1111 #
1112 self._FvList = []
1113 if Wa.FdfProfile:
1114 for Fd in Wa.FdfProfile.FdDict:
1115 for FdRegion in Wa.FdfProfile.FdDict[Fd].RegionList:
1116 if FdRegion.RegionType != "FV":
1117 continue
1118 for FvName in FdRegion.RegionDataList:
1119 if FvName in self._FvList:
1120 continue
1121 self._FvList.append(FvName)
1122 for Ffs in Wa.FdfProfile.FvDict[FvName.upper()].FfsList:
1123 for Section in Ffs.SectionList:
1124 try:
1125 for FvSection in Section.SectionList:
1126 if FvSection.FvName in self._FvList:
1127 continue
1128 self._FvList.append(FvSection.FvName)
1129 except AttributeError:
1130 pass
1131
1132
1133 ##
1134 # Parse platform fixed address map files
1135 #
1136 # This function parses the platform final fixed address map file to get
1137 # the database of predicted fixed address for module image base, entry point
1138 # etc.
1139 #
1140 # @param self: The object pointer
1141 #
1142 def _ParseMapFile(self):
1143 if self._MapFileParsed:
1144 return
1145 self._MapFileParsed = True
1146 if os.path.isfile(self._MapFileName):
1147 try:
1148 FileContents = open(self._MapFileName).read()
1149 for Match in gMapFileItemPattern.finditer(FileContents):
1150 AddressType = Match.group(1)
1151 BaseAddress = Match.group(2)
1152 EntryPoint = Match.group(3)
1153 Guid = Match.group(4).upper()
1154 List = self.FixedMapDict.setdefault(Guid, [])
1155 List.append((AddressType, BaseAddress, "*I"))
1156 List.append((AddressType, EntryPoint, "*E"))
1157 except:
1158 EdkLogger.warn(None, "Cannot open file to read", self._MapFileName)
1159
1160 ##
1161 # Invokes EOT tool to get the predicted the execution order.
1162 #
1163 # This function invokes EOT tool to calculate the predicted dispatch order
1164 #
1165 # @param self: The object pointer
1166 #
1167 def _InvokeEotTool(self):
1168 if self._EotToolInvoked:
1169 return
1170
1171 self._EotToolInvoked = True
1172 FvFileList = []
1173 for FvName in self._FvList:
1174 FvFile = os.path.join(self._FvDir, FvName + ".Fv")
1175 if os.path.isfile(FvFile):
1176 FvFileList.append(FvFile)
1177
1178 if len(FvFileList) == 0:
1179 return
1180 #
1181 # Write source file list and GUID file list to an intermediate file
1182 # as the input for EOT tool and dispatch List as the output file
1183 # from EOT tool.
1184 #
1185 SourceList = os.path.join(self._EotDir, "SourceFile.txt")
1186 GuidList = os.path.join(self._EotDir, "GuidList.txt")
1187 DispatchList = os.path.join(self._EotDir, "Dispatch.txt")
1188
1189 TempFile = open(SourceList, "w+")
1190 for Item in self._SourceList:
1191 FileWrite(TempFile, Item)
1192 TempFile.close()
1193 TempFile = open(GuidList, "w+")
1194 for Key in self._GuidMap:
1195 FileWrite(TempFile, "%s %s" % (Key, self._GuidMap[Key]))
1196 TempFile.close()
1197
1198 try:
1199 from Eot.Eot import Eot
1200
1201 #
1202 # Invoke EOT tool and echo its runtime performance
1203 #
1204 EotStartTime = time.time()
1205 Eot(CommandLineOption=False, SourceFileList=SourceList, GuidList=GuidList,
1206 FvFileList=' '.join(FvFileList), Dispatch=DispatchList, IsInit=True)
1207 EotEndTime = time.time()
1208 EotDuration = time.strftime("%H:%M:%S", time.gmtime(int(round(EotEndTime - EotStartTime))))
1209 EdkLogger.quiet("EOT run time: %s\n" % EotDuration)
1210
1211 #
1212 # Parse the output of EOT tool
1213 #
1214 for Line in open(DispatchList):
1215 if len(Line.split()) < 4:
1216 continue
1217 (Guid, Phase, FfsName, FilePath) = Line.split()
1218 Symbol = self._FfsEntryPoint.get(Guid, [FfsName, ""])[0]
1219 if len(Symbol) > self.MaxLen:
1220 self.MaxLen = len(Symbol)
1221 self.ItemList.append((Phase, Symbol, FilePath))
1222 except:
1223 EdkLogger.quiet("(Python %s on %s\n%s)" % (platform.python_version(), sys.platform, traceback.format_exc()))
1224 EdkLogger.warn(None, "Failed to generate execution order prediction report, for some error occurred in executing EOT.")
1225
1226
1227 ##
1228 # Generate platform execution order report
1229 #
1230 # This function generates the predicted module execution order.
1231 #
1232 # @param self The object pointer
1233 # @param File The file object for report
1234 #
1235 def _GenerateExecutionOrderReport(self, File):
1236 self._InvokeEotTool()
1237 if len(self.ItemList) == 0:
1238 return
1239 FileWrite(File, gSectionStart)
1240 FileWrite(File, "Execution Order Prediction")
1241 FileWrite(File, "*P PEI phase")
1242 FileWrite(File, "*D DXE phase")
1243 FileWrite(File, "*E Module INF entry point name")
1244 FileWrite(File, "*N Module notification function name")
1245
1246 FileWrite(File, "Type %-*s %s" % (self.MaxLen, "Symbol", "Module INF Path"))
1247 FileWrite(File, gSectionSep)
1248 for Item in self.ItemList:
1249 FileWrite(File, "*%sE %-*s %s" % (Item[0], self.MaxLen, Item[1], Item[2]))
1250
1251 FileWrite(File, gSectionStart)
1252
1253 ##
1254 # Generate Fixed Address report.
1255 #
1256 # This function generate the predicted fixed address report for a module
1257 # specified by Guid.
1258 #
1259 # @param self The object pointer
1260 # @param File The file object for report
1261 # @param Guid The module Guid value.
1262 # @param NotifyList The list of all notify function in a module
1263 #
1264 def _GenerateFixedAddressReport(self, File, Guid, NotifyList):
1265 self._ParseMapFile()
1266 FixedAddressList = self.FixedMapDict.get(Guid)
1267 if not FixedAddressList:
1268 return
1269
1270 FileWrite(File, gSubSectionStart)
1271 FileWrite(File, "Fixed Address Prediction")
1272 FileWrite(File, "*I Image Loading Address")
1273 FileWrite(File, "*E Entry Point Address")
1274 FileWrite(File, "*N Notification Function Address")
1275 FileWrite(File, "*F Flash Address")
1276 FileWrite(File, "*M Memory Address")
1277 FileWrite(File, "*S SMM RAM Offset")
1278 FileWrite(File, "TOM Top of Memory")
1279
1280 FileWrite(File, "Type Address Name")
1281 FileWrite(File, gSubSectionSep)
1282 for Item in FixedAddressList:
1283 Type = Item[0]
1284 Value = Item[1]
1285 Symbol = Item[2]
1286 if Symbol == "*I":
1287 Name = "(Image Base)"
1288 elif Symbol == "*E":
1289 Name = self._FfsEntryPoint.get(Guid, ["", "_ModuleEntryPoint"])[1]
1290 elif Symbol in NotifyList:
1291 Name = Symbol
1292 Symbol = "*N"
1293 else:
1294 continue
1295
1296 if "Flash" in Type:
1297 Symbol += "F"
1298 elif "Memory" in Type:
1299 Symbol += "M"
1300 else:
1301 Symbol += "S"
1302
1303 if Value[0] == "-":
1304 Value = "TOM" + Value
1305
1306 FileWrite(File, "%s %-16s %s" % (Symbol, Value, Name))
1307
1308 ##
1309 # Generate report for the prediction part
1310 #
1311 # This function generate the predicted fixed address report for a module or
1312 # predicted module execution order for a platform.
1313 # If the input Guid is None, then, it generates the predicted module execution order;
1314 # otherwise it generated the module fixed loading address for the module specified by
1315 # Guid.
1316 #
1317 # @param self The object pointer
1318 # @param File The file object for report
1319 # @param Guid The module Guid value.
1320 #
1321 def GenerateReport(self, File, Guid):
1322 if Guid:
1323 self._GenerateFixedAddressReport(File, Guid.upper(), [])
1324 else:
1325 self._GenerateExecutionOrderReport(File)
1326
1327 ##
1328 # Reports FD region information
1329 #
1330 # This class reports the FD subsection in the build report file.
1331 # It collects region information of platform flash device.
1332 # If the region is a firmware volume, it lists the set of modules
1333 # and its space information; otherwise, it only lists its region name,
1334 # base address and size in its sub-section header.
1335 # If there are nesting FVs, the nested FVs will list immediate after
1336 # this FD region subsection
1337 #
1338 class FdRegionReport(object):
1339 ##
1340 # Discover all the nested FV name list.
1341 #
1342 # This is an internal worker function to discover the all the nested FV information
1343 # in the parent firmware volume. It uses deep first search algorithm recursively to
1344 # find all the FV list name and append them to the list.
1345 #
1346 # @param self The object pointer
1347 # @param FvName The name of current firmware file system
1348 # @param Wa Workspace context information
1349 #
1350 def _DiscoverNestedFvList(self, FvName, Wa):
1351 FvDictKey=FvName.upper()
1352 if FvDictKey in Wa.FdfProfile.FvDict:
1353 for Ffs in Wa.FdfProfile.FvDict[FvName.upper()].FfsList:
1354 for Section in Ffs.SectionList:
1355 try:
1356 for FvSection in Section.SectionList:
1357 if FvSection.FvName in self.FvList:
1358 continue
1359 self._GuidsDb[Ffs.NameGuid.upper()] = FvSection.FvName
1360 self.FvList.append(FvSection.FvName)
1361 self.FvInfo[FvSection.FvName] = ("Nested FV", 0, 0)
1362 self._DiscoverNestedFvList(FvSection.FvName, Wa)
1363 except AttributeError:
1364 pass
1365
1366 ##
1367 # Constructor function for class FdRegionReport
1368 #
1369 # This constructor function generates FdRegionReport object for a specified FdRegion.
1370 # If the FdRegion is a firmware volume, it will recursively find all its nested Firmware
1371 # volume list. This function also collects GUID map in order to dump module identification
1372 # in the final report.
1373 #
1374 # @param self: The object pointer
1375 # @param FdRegion The current FdRegion object
1376 # @param Wa Workspace context information
1377 #
1378 def __init__(self, FdRegion, Wa):
1379 self.Type = FdRegion.RegionType
1380 self.BaseAddress = FdRegion.Offset
1381 self.Size = FdRegion.Size
1382 self.FvList = []
1383 self.FvInfo = {}
1384 self._GuidsDb = {}
1385 self._FvDir = Wa.FvDir
1386
1387 #
1388 # If the input FdRegion is not a firmware volume,
1389 # we are done.
1390 #
1391 if self.Type != "FV":
1392 return
1393
1394 #
1395 # Find all nested FVs in the FdRegion
1396 #
1397 for FvName in FdRegion.RegionDataList:
1398 if FvName in self.FvList:
1399 continue
1400 self.FvList.append(FvName)
1401 self.FvInfo[FvName] = ("Fd Region", self.BaseAddress, self.Size)
1402 self._DiscoverNestedFvList(FvName, Wa)
1403
1404 PlatformPcds = {}
1405 #
1406 # Collect PCDs declared in DEC files.
1407 #
1408 for Pa in Wa.AutoGenObjectList:
1409 for Package in Pa.PackageList:
1410 for (TokenCName, TokenSpaceGuidCName, DecType) in Package.Pcds:
1411 DecDefaultValue = Package.Pcds[TokenCName, TokenSpaceGuidCName, DecType].DefaultValue
1412 PlatformPcds[(TokenCName, TokenSpaceGuidCName)] = DecDefaultValue
1413 #
1414 # Collect PCDs defined in DSC file
1415 #
1416 for arch in Wa.ArchList:
1417 Platform = Wa.BuildDatabase[Wa.MetaFile, arch]
1418 for (TokenCName, TokenSpaceGuidCName) in Platform.Pcds:
1419 DscDefaultValue = Platform.Pcds[(TokenCName, TokenSpaceGuidCName)].DefaultValue
1420 PlatformPcds[(TokenCName, TokenSpaceGuidCName)] = DscDefaultValue
1421
1422 #
1423 # Add PEI and DXE a priori files GUIDs defined in PI specification.
1424 #
1425 self._GuidsDb["1B45CC0A-156A-428A-AF62-49864DA0E6E6"] = "PEI Apriori"
1426 self._GuidsDb["FC510EE7-FFDC-11D4-BD41-0080C73C8881"] = "DXE Apriori"
1427 #
1428 # Add ACPI table storage file
1429 #
1430 self._GuidsDb["7E374E25-8E01-4FEE-87F2-390C23C606CD"] = "ACPI table storage"
1431
1432 for Pa in Wa.AutoGenObjectList:
1433 for ModuleKey in Pa.Platform.Modules:
1434 M = Pa.Platform.Modules[ModuleKey].M
1435 InfPath = mws.join(Wa.WorkspaceDir, M.MetaFile.File)
1436 self._GuidsDb[M.Guid.upper()] = "%s (%s)" % (M.Module.BaseName, InfPath)
1437
1438 #
1439 # Collect the GUID map in the FV firmware volume
1440 #
1441 for FvName in self.FvList:
1442 FvDictKey=FvName.upper()
1443 if FvDictKey in Wa.FdfProfile.FvDict:
1444 for Ffs in Wa.FdfProfile.FvDict[FvName.upper()].FfsList:
1445 try:
1446 #
1447 # collect GUID map for binary EFI file in FDF file.
1448 #
1449 Guid = Ffs.NameGuid.upper()
1450 Match = gPcdGuidPattern.match(Ffs.NameGuid)
1451 if Match:
1452 PcdTokenspace = Match.group(1)
1453 PcdToken = Match.group(2)
1454 if (PcdToken, PcdTokenspace) in PlatformPcds:
1455 GuidValue = PlatformPcds[(PcdToken, PcdTokenspace)]
1456 Guid = GuidStructureByteArrayToGuidString(GuidValue).upper()
1457 for Section in Ffs.SectionList:
1458 try:
1459 ModuleSectFile = mws.join(Wa.WorkspaceDir, Section.SectFileName)
1460 self._GuidsDb[Guid] = ModuleSectFile
1461 except AttributeError:
1462 pass
1463 except AttributeError:
1464 pass
1465
1466
1467 ##
1468 # Internal worker function to generate report for the FD region
1469 #
1470 # This internal worker function to generate report for the FD region.
1471 # It the type is firmware volume, it lists offset and module identification.
1472 #
1473 # @param self The object pointer
1474 # @param File The file object for report
1475 # @param Title The title for the FD subsection
1476 # @param BaseAddress The base address for the FD region
1477 # @param Size The size of the FD region
1478 # @param FvName The FV name if the FD region is a firmware volume
1479 #
1480 def _GenerateReport(self, File, Title, Type, BaseAddress, Size=0, FvName=None):
1481 FileWrite(File, gSubSectionStart)
1482 FileWrite(File, Title)
1483 FileWrite(File, "Type: %s" % Type)
1484 FileWrite(File, "Base Address: 0x%X" % BaseAddress)
1485
1486 if self.Type == "FV":
1487 FvTotalSize = 0
1488 FvTakenSize = 0
1489 FvFreeSize = 0
1490 FvReportFileName = os.path.join(self._FvDir, FvName + ".Fv.txt")
1491 try:
1492 #
1493 # Collect size info in the firmware volume.
1494 #
1495 FvReport = open(FvReportFileName).read()
1496 Match = gFvTotalSizePattern.search(FvReport)
1497 if Match:
1498 FvTotalSize = int(Match.group(1), 16)
1499 Match = gFvTakenSizePattern.search(FvReport)
1500 if Match:
1501 FvTakenSize = int(Match.group(1), 16)
1502 FvFreeSize = FvTotalSize - FvTakenSize
1503 #
1504 # Write size information to the report file.
1505 #
1506 FileWrite(File, "Size: 0x%X (%.0fK)" % (FvTotalSize, FvTotalSize / 1024.0))
1507 FileWrite(File, "Fv Name: %s (%.1f%% Full)" % (FvName, FvTakenSize * 100.0 / FvTotalSize))
1508 FileWrite(File, "Occupied Size: 0x%X (%.0fK)" % (FvTakenSize, FvTakenSize / 1024.0))
1509 FileWrite(File, "Free Size: 0x%X (%.0fK)" % (FvFreeSize, FvFreeSize / 1024.0))
1510 FileWrite(File, "Offset Module")
1511 FileWrite(File, gSubSectionSep)
1512 #
1513 # Write module offset and module identification to the report file.
1514 #
1515 OffsetInfo = {}
1516 for Match in gOffsetGuidPattern.finditer(FvReport):
1517 Guid = Match.group(2).upper()
1518 OffsetInfo[Match.group(1)] = self._GuidsDb.get(Guid, Guid)
1519 OffsetList = OffsetInfo.keys()
1520 OffsetList.sort()
1521 for Offset in OffsetList:
1522 FileWrite (File, "%s %s" % (Offset, OffsetInfo[Offset]))
1523 except IOError:
1524 EdkLogger.warn(None, "Fail to read report file", FvReportFileName)
1525 else:
1526 FileWrite(File, "Size: 0x%X (%.0fK)" % (Size, Size / 1024.0))
1527 FileWrite(File, gSubSectionEnd)
1528
1529 ##
1530 # Generate report for the FD region
1531 #
1532 # This function generates report for the FD region.
1533 #
1534 # @param self The object pointer
1535 # @param File The file object for report
1536 #
1537 def GenerateReport(self, File):
1538 if (len(self.FvList) > 0):
1539 for FvItem in self.FvList:
1540 Info = self.FvInfo[FvItem]
1541 self._GenerateReport(File, Info[0], "FV", Info[1], Info[2], FvItem)
1542 else:
1543 self._GenerateReport(File, "FD Region", self.Type, self.BaseAddress, self.Size)
1544
1545 ##
1546 # Reports FD information
1547 #
1548 # This class reports the FD section in the build report file.
1549 # It collects flash device information for a platform.
1550 #
1551 class FdReport(object):
1552 ##
1553 # Constructor function for class FdReport
1554 #
1555 # This constructor function generates FdReport object for a specified
1556 # firmware device.
1557 #
1558 # @param self The object pointer
1559 # @param Fd The current Firmware device object
1560 # @param Wa Workspace context information
1561 #
1562 def __init__(self, Fd, Wa):
1563 self.FdName = Fd.FdUiName
1564 self.BaseAddress = Fd.BaseAddress
1565 self.Size = Fd.Size
1566 self.FdRegionList = [FdRegionReport(FdRegion, Wa) for FdRegion in Fd.RegionList]
1567 self.FvPath = os.path.join(Wa.BuildDir, "FV")
1568 self.VpdFilePath = os.path.join(self.FvPath, "%s.map" % Wa.Platform.VpdToolGuid)
1569 self.VPDBaseAddress = 0
1570 self.VPDSize = 0
1571 self.VPDInfoList = []
1572 for index, FdRegion in enumerate(Fd.RegionList):
1573 if str(FdRegion.RegionType) is 'FILE' and Wa.Platform.VpdToolGuid in str(FdRegion.RegionDataList):
1574 self.VPDBaseAddress = self.FdRegionList[index].BaseAddress
1575 self.VPDSize = self.FdRegionList[index].Size
1576 break
1577
1578 if os.path.isfile(self.VpdFilePath):
1579 fd = open(self.VpdFilePath, "r")
1580 Lines = fd.readlines()
1581 for Line in Lines:
1582 Line = Line.strip()
1583 if len(Line) == 0 or Line.startswith("#"):
1584 continue
1585 try:
1586 PcdName, SkuId, Offset, Size, Value = Line.split("#")[0].split("|")
1587 PcdName, SkuId, Offset, Size, Value = PcdName.strip(), SkuId.strip(), Offset.strip(), Size.strip(), Value.strip()
1588 if Offset.lower().startswith('0x'):
1589 Offset = '0x%08X' % (int(Offset, 16) + self.VPDBaseAddress)
1590 else:
1591 Offset = '0x%08X' % (int(Offset, 10) + self.VPDBaseAddress)
1592 self.VPDInfoList.append("%s | %s | %s | %s | %s" % (PcdName, SkuId, Offset, Size, Value))
1593 except:
1594 EdkLogger.error("BuildReport", CODE_ERROR, "Fail to parse VPD information file %s" % self.VpdFilePath)
1595 fd.close()
1596
1597 ##
1598 # Generate report for the firmware device.
1599 #
1600 # This function generates report for the firmware device.
1601 #
1602 # @param self The object pointer
1603 # @param File The file object for report
1604 #
1605 def GenerateReport(self, File):
1606 FileWrite(File, gSectionStart)
1607 FileWrite(File, "Firmware Device (FD)")
1608 FileWrite(File, "FD Name: %s" % self.FdName)
1609 FileWrite(File, "Base Address: %s" % self.BaseAddress)
1610 FileWrite(File, "Size: 0x%X (%.0fK)" % (self.Size, self.Size / 1024.0))
1611 if len(self.FdRegionList) > 0:
1612 FileWrite(File, gSectionSep)
1613 for FdRegionItem in self.FdRegionList:
1614 FdRegionItem.GenerateReport(File)
1615
1616 if len(self.VPDInfoList) > 0:
1617 FileWrite(File, gSubSectionStart)
1618 FileWrite(File, "FD VPD Region")
1619 FileWrite(File, "Base Address: 0x%X" % self.VPDBaseAddress)
1620 FileWrite(File, "Size: 0x%X (%.0fK)" % (self.VPDSize, self.VPDSize / 1024.0))
1621 FileWrite(File, gSubSectionSep)
1622 for item in self.VPDInfoList:
1623 FileWrite(File, item)
1624 FileWrite(File, gSubSectionEnd)
1625 FileWrite(File, gSectionEnd)
1626
1627
1628
1629 ##
1630 # Reports platform information
1631 #
1632 # This class reports the whole platform information
1633 #
1634 class PlatformReport(object):
1635 ##
1636 # Constructor function for class PlatformReport
1637 #
1638 # This constructor function generates PlatformReport object a platform build.
1639 # It generates report for platform summary, flash, global PCDs and detailed
1640 # module information for modules involved in platform build.
1641 #
1642 # @param self The object pointer
1643 # @param Wa Workspace context information
1644 # @param MaList The list of modules in the platform build
1645 #
1646 def __init__(self, Wa, MaList, ReportType):
1647 self._WorkspaceDir = Wa.WorkspaceDir
1648 self.PlatformName = Wa.Name
1649 self.PlatformDscPath = Wa.Platform
1650 self.Architectures = " ".join(Wa.ArchList)
1651 self.ToolChain = Wa.ToolChain
1652 self.Target = Wa.BuildTarget
1653 self.OutputPath = os.path.join(Wa.WorkspaceDir, Wa.OutputDir)
1654 self.BuildEnvironment = platform.platform()
1655
1656 self.PcdReport = None
1657 if "PCD" in ReportType:
1658 self.PcdReport = PcdReport(Wa)
1659
1660 self.FdReportList = []
1661 if "FLASH" in ReportType and Wa.FdfProfile and MaList == None:
1662 for Fd in Wa.FdfProfile.FdDict:
1663 self.FdReportList.append(FdReport(Wa.FdfProfile.FdDict[Fd], Wa))
1664
1665 self.PredictionReport = None
1666 if "FIXED_ADDRESS" in ReportType or "EXECUTION_ORDER" in ReportType:
1667 self.PredictionReport = PredictionReport(Wa)
1668
1669 self.DepexParser = None
1670 if "DEPEX" in ReportType:
1671 self.DepexParser = DepexParser(Wa)
1672
1673 self.ModuleReportList = []
1674 if MaList != None:
1675 self._IsModuleBuild = True
1676 for Ma in MaList:
1677 self.ModuleReportList.append(ModuleReport(Ma, ReportType))
1678 else:
1679 self._IsModuleBuild = False
1680 for Pa in Wa.AutoGenObjectList:
1681 ModuleAutoGenList = []
1682 for ModuleKey in Pa.Platform.Modules:
1683 ModuleAutoGenList.append(Pa.Platform.Modules[ModuleKey].M)
1684 if GlobalData.gFdfParser != None:
1685 if Pa.Arch in GlobalData.gFdfParser.Profile.InfDict:
1686 INFList = GlobalData.gFdfParser.Profile.InfDict[Pa.Arch]
1687 for InfName in INFList:
1688 InfClass = PathClass(NormPath(InfName), Wa.WorkspaceDir, Pa.Arch)
1689 Ma = ModuleAutoGen(Wa, InfClass, Pa.BuildTarget, Pa.ToolChain, Pa.Arch, Wa.MetaFile)
1690 if Ma == None:
1691 continue
1692 if Ma not in ModuleAutoGenList:
1693 ModuleAutoGenList.append(Ma)
1694 for MGen in ModuleAutoGenList:
1695 self.ModuleReportList.append(ModuleReport(MGen, ReportType))
1696
1697
1698
1699 ##
1700 # Generate report for the whole platform.
1701 #
1702 # This function generates report for platform information.
1703 # It comprises of platform summary, global PCD, flash and
1704 # module list sections.
1705 #
1706 # @param self The object pointer
1707 # @param File The file object for report
1708 # @param BuildDuration The total time to build the modules
1709 # @param AutoGenTime The total time of AutoGen Phase
1710 # @param MakeTime The total time of Make Phase
1711 # @param GenFdsTime The total time of GenFds Phase
1712 # @param ReportType The kind of report items in the final report file
1713 #
1714 def GenerateReport(self, File, BuildDuration, AutoGenTime, MakeTime, GenFdsTime, ReportType):
1715 FileWrite(File, "Platform Summary")
1716 FileWrite(File, "Platform Name: %s" % self.PlatformName)
1717 FileWrite(File, "Platform DSC Path: %s" % self.PlatformDscPath)
1718 FileWrite(File, "Architectures: %s" % self.Architectures)
1719 FileWrite(File, "Tool Chain: %s" % self.ToolChain)
1720 FileWrite(File, "Target: %s" % self.Target)
1721 FileWrite(File, "Output Path: %s" % self.OutputPath)
1722 FileWrite(File, "Build Environment: %s" % self.BuildEnvironment)
1723 FileWrite(File, "Build Duration: %s" % BuildDuration)
1724 if AutoGenTime:
1725 FileWrite(File, "AutoGen Duration: %s" % AutoGenTime)
1726 if MakeTime:
1727 FileWrite(File, "Make Duration: %s" % MakeTime)
1728 if GenFdsTime:
1729 FileWrite(File, "GenFds Duration: %s" % GenFdsTime)
1730 FileWrite(File, "Report Content: %s" % ", ".join(ReportType))
1731
1732 if GlobalData.MixedPcd:
1733 FileWrite(File, gSectionStart)
1734 FileWrite(File, "The following PCDs use different access methods:")
1735 FileWrite(File, gSectionSep)
1736 for PcdItem in GlobalData.MixedPcd:
1737 FileWrite(File, "%s.%s" % (str(PcdItem[1]), str(PcdItem[0])))
1738 FileWrite(File, gSectionEnd)
1739
1740 if not self._IsModuleBuild:
1741 if "PCD" in ReportType:
1742 self.PcdReport.GenerateReport(File, None)
1743
1744 if "FLASH" in ReportType:
1745 for FdReportListItem in self.FdReportList:
1746 FdReportListItem.GenerateReport(File)
1747
1748 for ModuleReportItem in self.ModuleReportList:
1749 ModuleReportItem.GenerateReport(File, self.PcdReport, self.PredictionReport, self.DepexParser, ReportType)
1750
1751 if not self._IsModuleBuild:
1752 if "EXECUTION_ORDER" in ReportType:
1753 self.PredictionReport.GenerateReport(File, None)
1754
1755 ## BuildReport class
1756 #
1757 # This base class contain the routines to collect data and then
1758 # applies certain format to the output report
1759 #
1760 class BuildReport(object):
1761 ##
1762 # Constructor function for class BuildReport
1763 #
1764 # This constructor function generates BuildReport object a platform build.
1765 # It generates report for platform summary, flash, global PCDs and detailed
1766 # module information for modules involved in platform build.
1767 #
1768 # @param self The object pointer
1769 # @param ReportFile The file name to save report file
1770 # @param ReportType The kind of report items in the final report file
1771 #
1772 def __init__(self, ReportFile, ReportType):
1773 self.ReportFile = ReportFile
1774 if ReportFile:
1775 self.ReportList = []
1776 self.ReportType = []
1777 if ReportType:
1778 for ReportTypeItem in ReportType:
1779 if ReportTypeItem not in self.ReportType:
1780 self.ReportType.append(ReportTypeItem)
1781 else:
1782 self.ReportType = ["PCD", "LIBRARY", "BUILD_FLAGS", "DEPEX", "HASH", "FLASH", "FIXED_ADDRESS"]
1783 ##
1784 # Adds platform report to the list
1785 #
1786 # This function adds a platform report to the final report list.
1787 #
1788 # @param self The object pointer
1789 # @param Wa Workspace context information
1790 # @param MaList The list of modules in the platform build
1791 #
1792 def AddPlatformReport(self, Wa, MaList=None):
1793 if self.ReportFile:
1794 self.ReportList.append((Wa, MaList))
1795
1796 ##
1797 # Generates the final report.
1798 #
1799 # This function generates platform build report. It invokes GenerateReport()
1800 # method for every platform report in the list.
1801 #
1802 # @param self The object pointer
1803 # @param BuildDuration The total time to build the modules
1804 # @param AutoGenTime The total time of AutoGen phase
1805 # @param MakeTime The total time of Make phase
1806 # @param GenFdsTime The total time of GenFds phase
1807 #
1808 def GenerateReport(self, BuildDuration, AutoGenTime, MakeTime, GenFdsTime):
1809 if self.ReportFile:
1810 try:
1811 File = StringIO('')
1812 for (Wa, MaList) in self.ReportList:
1813 PlatformReport(Wa, MaList, self.ReportType).GenerateReport(File, BuildDuration, AutoGenTime, MakeTime, GenFdsTime, self.ReportType)
1814 Content = FileLinesSplit(File.getvalue(), gLineMaxLength)
1815 SaveFileOnChange(self.ReportFile, Content, True)
1816 EdkLogger.quiet("Build report can be found at %s" % os.path.abspath(self.ReportFile))
1817 except IOError:
1818 EdkLogger.error(None, FILE_WRITE_FAILURE, ExtraData=self.ReportFile)
1819 except:
1820 EdkLogger.error("BuildReport", CODE_ERROR, "Unknown fatal error when generating build report", ExtraData=self.ReportFile, RaiseError=False)
1821 EdkLogger.quiet("(Python %s on %s\n%s)" % (platform.python_version(), sys.platform, traceback.format_exc()))
1822 File.close()
1823
1824 # This acts like the main() function for the script, unless it is 'import'ed into another script.
1825 if __name__ == '__main__':
1826 pass
1827