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