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