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