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