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