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