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