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