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