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