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