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