]> git.proxmox.com Git - mirror_edk2.git/blame_incremental - BaseTools/Source/Python/AutoGen/GenDepex.py
BaseTools: Enable structure pcd in FDF file
[mirror_edk2.git] / BaseTools / Source / Python / AutoGen / GenDepex.py
... / ...
CommitLineData
1## @file\r
2# This file is used to generate DEPEX file for module's dependency expression\r
3#\r
4# Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>\r
5# This program and the accompanying materials\r
6# are licensed and made available under the terms and conditions of the BSD License\r
7# which accompanies this distribution. The full text of the license may be found at\r
8# http://opensource.org/licenses/bsd-license.php\r
9#\r
10# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
11# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
12\r
13## Import Modules\r
14#\r
15import sys\r
16import Common.LongFilePathOs as os\r
17import re\r
18import traceback\r
19from Common.LongFilePathSupport import OpenLongFilePath as open\r
20from io import BytesIO\r
21from struct import pack\r
22from Common.BuildToolError import *\r
23from Common.Misc import SaveFileOnChange\r
24from Common.Misc import GuidStructureStringToGuidString\r
25from Common import EdkLogger as EdkLogger\r
26from Common.BuildVersion import gBUILD_VERSION\r
27from Common.DataType import *\r
28\r
29## Regular expression for matching "DEPENDENCY_START ... DEPENDENCY_END"\r
30gStartClosePattern = re.compile(".*DEPENDENCY_START(.+)DEPENDENCY_END.*", re.S)\r
31\r
32## Mapping between module type and EFI phase\r
33gType2Phase = {\r
34 SUP_MODULE_BASE : None,\r
35 SUP_MODULE_SEC : "PEI",\r
36 SUP_MODULE_PEI_CORE : "PEI",\r
37 SUP_MODULE_PEIM : "PEI",\r
38 SUP_MODULE_DXE_CORE : "DXE",\r
39 SUP_MODULE_DXE_DRIVER : "DXE",\r
40 SUP_MODULE_DXE_SMM_DRIVER : "DXE",\r
41 SUP_MODULE_DXE_RUNTIME_DRIVER: "DXE",\r
42 SUP_MODULE_DXE_SAL_DRIVER : "DXE",\r
43 SUP_MODULE_UEFI_DRIVER : "DXE",\r
44 SUP_MODULE_UEFI_APPLICATION : "DXE",\r
45 SUP_MODULE_SMM_CORE : "DXE",\r
46 SUP_MODULE_MM_STANDALONE : "MM",\r
47 SUP_MODULE_MM_CORE_STANDALONE : "MM",\r
48}\r
49\r
50## Convert dependency expression string into EFI internal representation\r
51#\r
52# DependencyExpression class is used to parse dependency expression string and\r
53# convert it into its binary form.\r
54#\r
55class DependencyExpression:\r
56\r
57 ArchProtocols = {\r
58 '665e3ff6-46cc-11d4-9a38-0090273fc14d', # 'gEfiBdsArchProtocolGuid'\r
59 '26baccb1-6f42-11d4-bce7-0080c73c8881', # 'gEfiCpuArchProtocolGuid'\r
60 '26baccb2-6f42-11d4-bce7-0080c73c8881', # 'gEfiMetronomeArchProtocolGuid'\r
61 '1da97072-bddc-4b30-99f1-72a0b56fff2a', # 'gEfiMonotonicCounterArchProtocolGuid'\r
62 '27cfac87-46cc-11d4-9a38-0090273fc14d', # 'gEfiRealTimeClockArchProtocolGuid'\r
63 '27cfac88-46cc-11d4-9a38-0090273fc14d', # 'gEfiResetArchProtocolGuid'\r
64 'b7dfb4e1-052f-449f-87be-9818fc91b733', # 'gEfiRuntimeArchProtocolGuid'\r
65 'a46423e3-4617-49f1-b9ff-d1bfa9115839', # 'gEfiSecurityArchProtocolGuid'\r
66 '26baccb3-6f42-11d4-bce7-0080c73c8881', # 'gEfiTimerArchProtocolGuid'\r
67 '6441f818-6362-4e44-b570-7dba31dd2453', # 'gEfiVariableWriteArchProtocolGuid'\r
68 '1e5668e2-8481-11d4-bcf1-0080c73c8881', # 'gEfiVariableArchProtocolGuid'\r
69 '665e3ff5-46cc-11d4-9a38-0090273fc14d' # 'gEfiWatchdogTimerArchProtocolGuid'\r
70 }\r
71\r
72 OpcodePriority = {\r
73 DEPEX_OPCODE_AND : 1,\r
74 DEPEX_OPCODE_OR : 1,\r
75 DEPEX_OPCODE_NOT : 2,\r
76 }\r
77\r
78 Opcode = {\r
79 "PEI" : {\r
80 DEPEX_OPCODE_PUSH : 0x02,\r
81 DEPEX_OPCODE_AND : 0x03,\r
82 DEPEX_OPCODE_OR : 0x04,\r
83 DEPEX_OPCODE_NOT : 0x05,\r
84 DEPEX_OPCODE_TRUE : 0x06,\r
85 DEPEX_OPCODE_FALSE : 0x07,\r
86 DEPEX_OPCODE_END : 0x08\r
87 },\r
88\r
89 "DXE" : {\r
90 DEPEX_OPCODE_BEFORE: 0x00,\r
91 DEPEX_OPCODE_AFTER : 0x01,\r
92 DEPEX_OPCODE_PUSH : 0x02,\r
93 DEPEX_OPCODE_AND : 0x03,\r
94 DEPEX_OPCODE_OR : 0x04,\r
95 DEPEX_OPCODE_NOT : 0x05,\r
96 DEPEX_OPCODE_TRUE : 0x06,\r
97 DEPEX_OPCODE_FALSE : 0x07,\r
98 DEPEX_OPCODE_END : 0x08,\r
99 DEPEX_OPCODE_SOR : 0x09\r
100 },\r
101\r
102 "MM" : {\r
103 DEPEX_OPCODE_BEFORE: 0x00,\r
104 DEPEX_OPCODE_AFTER : 0x01,\r
105 DEPEX_OPCODE_PUSH : 0x02,\r
106 DEPEX_OPCODE_AND : 0x03,\r
107 DEPEX_OPCODE_OR : 0x04,\r
108 DEPEX_OPCODE_NOT : 0x05,\r
109 DEPEX_OPCODE_TRUE : 0x06,\r
110 DEPEX_OPCODE_FALSE : 0x07,\r
111 DEPEX_OPCODE_END : 0x08,\r
112 DEPEX_OPCODE_SOR : 0x09\r
113 }\r
114 }\r
115\r
116 # all supported op codes and operands\r
117 SupportedOpcode = [DEPEX_OPCODE_BEFORE, DEPEX_OPCODE_AFTER, DEPEX_OPCODE_PUSH, DEPEX_OPCODE_AND, DEPEX_OPCODE_OR, DEPEX_OPCODE_NOT, DEPEX_OPCODE_END, DEPEX_OPCODE_SOR]\r
118 SupportedOperand = [DEPEX_OPCODE_TRUE, DEPEX_OPCODE_FALSE]\r
119\r
120 OpcodeWithSingleOperand = [DEPEX_OPCODE_NOT, DEPEX_OPCODE_BEFORE, DEPEX_OPCODE_AFTER]\r
121 OpcodeWithTwoOperand = [DEPEX_OPCODE_AND, DEPEX_OPCODE_OR]\r
122\r
123 # op code that should not be the last one\r
124 NonEndingOpcode = [DEPEX_OPCODE_AND, DEPEX_OPCODE_OR, DEPEX_OPCODE_NOT, DEPEX_OPCODE_SOR]\r
125 # op code must not present at the same time\r
126 ExclusiveOpcode = [DEPEX_OPCODE_BEFORE, DEPEX_OPCODE_AFTER]\r
127 # op code that should be the first one if it presents\r
128 AboveAllOpcode = [DEPEX_OPCODE_SOR, DEPEX_OPCODE_BEFORE, DEPEX_OPCODE_AFTER]\r
129\r
130 #\r
131 # open and close brace must be taken as individual tokens\r
132 #\r
133 TokenPattern = re.compile("(\(|\)|\{[^{}]+\{?[^{}]+\}?[ ]*\}|\w+)")\r
134\r
135 ## Constructor\r
136 #\r
137 # @param Expression The list or string of dependency expression\r
138 # @param ModuleType The type of the module using the dependency expression\r
139 #\r
140 def __init__(self, Expression, ModuleType, Optimize=False):\r
141 self.ModuleType = ModuleType\r
142 self.Phase = gType2Phase[ModuleType]\r
143 if isinstance(Expression, type([])):\r
144 self.ExpressionString = " ".join(Expression)\r
145 self.TokenList = Expression\r
146 else:\r
147 self.ExpressionString = Expression\r
148 self.GetExpressionTokenList()\r
149\r
150 self.PostfixNotation = []\r
151 self.OpcodeList = []\r
152\r
153 self.GetPostfixNotation()\r
154 self.ValidateOpcode()\r
155\r
156 EdkLogger.debug(EdkLogger.DEBUG_8, repr(self))\r
157 if Optimize:\r
158 self.Optimize()\r
159 EdkLogger.debug(EdkLogger.DEBUG_8, "\n Optimized: " + repr(self))\r
160\r
161 def __str__(self):\r
162 return " ".join(self.TokenList)\r
163\r
164 def __repr__(self):\r
165 WellForm = ''\r
166 for Token in self.PostfixNotation:\r
167 if Token in self.SupportedOpcode:\r
168 WellForm += "\n " + Token\r
169 else:\r
170 WellForm += ' ' + Token\r
171 return WellForm\r
172\r
173 ## Split the expression string into token list\r
174 def GetExpressionTokenList(self):\r
175 self.TokenList = self.TokenPattern.findall(self.ExpressionString)\r
176\r
177 ## Convert token list into postfix notation\r
178 def GetPostfixNotation(self):\r
179 Stack = []\r
180 LastToken = ''\r
181 for Token in self.TokenList:\r
182 if Token == "(":\r
183 if LastToken not in self.SupportedOpcode + ['(', '', None]:\r
184 EdkLogger.error("GenDepex", PARSER_ERROR, "Invalid dependency expression: missing operator before open parentheses",\r
185 ExtraData="Near %s" % LastToken)\r
186 Stack.append(Token)\r
187 elif Token == ")":\r
188 if '(' not in Stack:\r
189 EdkLogger.error("GenDepex", PARSER_ERROR, "Invalid dependency expression: mismatched parentheses",\r
190 ExtraData=str(self))\r
191 elif LastToken in self.SupportedOpcode + ['', None]:\r
192 EdkLogger.error("GenDepex", PARSER_ERROR, "Invalid dependency expression: missing operand before close parentheses",\r
193 ExtraData="Near %s" % LastToken)\r
194 while len(Stack) > 0:\r
195 if Stack[-1] == '(':\r
196 Stack.pop()\r
197 break\r
198 self.PostfixNotation.append(Stack.pop())\r
199 elif Token in self.OpcodePriority:\r
200 if Token == DEPEX_OPCODE_NOT:\r
201 if LastToken not in self.SupportedOpcode + ['(', '', None]:\r
202 EdkLogger.error("GenDepex", PARSER_ERROR, "Invalid dependency expression: missing operator before NOT",\r
203 ExtraData="Near %s" % LastToken)\r
204 elif LastToken in self.SupportedOpcode + ['(', '', None]:\r
205 EdkLogger.error("GenDepex", PARSER_ERROR, "Invalid dependency expression: missing operand before " + Token,\r
206 ExtraData="Near %s" % LastToken)\r
207\r
208 while len(Stack) > 0:\r
209 if Stack[-1] == "(" or self.OpcodePriority[Token] >= self.OpcodePriority[Stack[-1]]:\r
210 break\r
211 self.PostfixNotation.append(Stack.pop())\r
212 Stack.append(Token)\r
213 self.OpcodeList.append(Token)\r
214 else:\r
215 if Token not in self.SupportedOpcode:\r
216 # not OP, take it as GUID\r
217 if LastToken not in self.SupportedOpcode + ['(', '', None]:\r
218 EdkLogger.error("GenDepex", PARSER_ERROR, "Invalid dependency expression: missing operator before %s" % Token,\r
219 ExtraData="Near %s" % LastToken)\r
220 if len(self.OpcodeList) == 0 or self.OpcodeList[-1] not in self.ExclusiveOpcode:\r
221 if Token not in self.SupportedOperand:\r
222 self.PostfixNotation.append(DEPEX_OPCODE_PUSH)\r
223 # check if OP is valid in this phase\r
224 elif Token in self.Opcode[self.Phase]:\r
225 if Token == DEPEX_OPCODE_END:\r
226 break\r
227 self.OpcodeList.append(Token)\r
228 else:\r
229 EdkLogger.error("GenDepex", PARSER_ERROR,\r
230 "Opcode=%s doesn't supported in %s stage " % (Token, self.Phase),\r
231 ExtraData=str(self))\r
232 self.PostfixNotation.append(Token)\r
233 LastToken = Token\r
234\r
235 # there should not be parentheses in Stack\r
236 if '(' in Stack or ')' in Stack:\r
237 EdkLogger.error("GenDepex", PARSER_ERROR, "Invalid dependency expression: mismatched parentheses",\r
238 ExtraData=str(self))\r
239 while len(Stack) > 0:\r
240 self.PostfixNotation.append(Stack.pop())\r
241 if self.PostfixNotation[-1] != DEPEX_OPCODE_END:\r
242 self.PostfixNotation.append(DEPEX_OPCODE_END)\r
243\r
244 ## Validate the dependency expression\r
245 def ValidateOpcode(self):\r
246 for Op in self.AboveAllOpcode:\r
247 if Op in self.PostfixNotation:\r
248 if Op != self.PostfixNotation[0]:\r
249 EdkLogger.error("GenDepex", PARSER_ERROR, "%s should be the first opcode in the expression" % Op,\r
250 ExtraData=str(self))\r
251 if len(self.PostfixNotation) < 3:\r
252 EdkLogger.error("GenDepex", PARSER_ERROR, "Missing operand for %s" % Op,\r
253 ExtraData=str(self))\r
254 for Op in self.ExclusiveOpcode:\r
255 if Op in self.OpcodeList:\r
256 if len(self.OpcodeList) > 1:\r
257 EdkLogger.error("GenDepex", PARSER_ERROR, "%s should be the only opcode in the expression" % Op,\r
258 ExtraData=str(self))\r
259 if len(self.PostfixNotation) < 3:\r
260 EdkLogger.error("GenDepex", PARSER_ERROR, "Missing operand for %s" % Op,\r
261 ExtraData=str(self))\r
262 if self.TokenList[-1] != DEPEX_OPCODE_END and self.TokenList[-1] in self.NonEndingOpcode:\r
263 EdkLogger.error("GenDepex", PARSER_ERROR, "Extra %s at the end of the dependency expression" % self.TokenList[-1],\r
264 ExtraData=str(self))\r
265 if self.TokenList[-1] == DEPEX_OPCODE_END and self.TokenList[-2] in self.NonEndingOpcode:\r
266 EdkLogger.error("GenDepex", PARSER_ERROR, "Extra %s at the end of the dependency expression" % self.TokenList[-2],\r
267 ExtraData=str(self))\r
268 if DEPEX_OPCODE_END in self.TokenList and DEPEX_OPCODE_END != self.TokenList[-1]:\r
269 EdkLogger.error("GenDepex", PARSER_ERROR, "Extra expressions after END",\r
270 ExtraData=str(self))\r
271\r
272 ## Simply optimize the dependency expression by removing duplicated operands\r
273 def Optimize(self):\r
274 OpcodeSet = set(self.OpcodeList)\r
275 # if there are isn't one in the set, return\r
276 if len(OpcodeSet) != 1:\r
277 return\r
278 Op = OpcodeSet.pop()\r
279 #if Op isn't either OR or AND, return\r
280 if Op not in [DEPEX_OPCODE_AND, DEPEX_OPCODE_OR]:\r
281 return\r
282 NewOperand = []\r
283 AllOperand = set()\r
284 for Token in self.PostfixNotation:\r
285 if Token in self.SupportedOpcode or Token in NewOperand:\r
286 continue\r
287 AllOperand.add(Token)\r
288 if Token == DEPEX_OPCODE_TRUE:\r
289 if Op == DEPEX_OPCODE_AND:\r
290 continue\r
291 else:\r
292 NewOperand.append(Token)\r
293 break\r
294 elif Token == DEPEX_OPCODE_FALSE:\r
295 if Op == DEPEX_OPCODE_OR:\r
296 continue\r
297 else:\r
298 NewOperand.append(Token)\r
299 break\r
300 NewOperand.append(Token)\r
301\r
302 # don't generate depex if only TRUE operand left\r
303 if self.ModuleType == SUP_MODULE_PEIM and len(NewOperand) == 1 and NewOperand[0] == DEPEX_OPCODE_TRUE:\r
304 self.PostfixNotation = []\r
305 return\r
306\r
307 # don't generate depex if all operands are architecture protocols\r
308 if self.ModuleType in [SUP_MODULE_UEFI_DRIVER, SUP_MODULE_DXE_DRIVER, SUP_MODULE_DXE_RUNTIME_DRIVER, SUP_MODULE_DXE_SAL_DRIVER, SUP_MODULE_DXE_SMM_DRIVER, SUP_MODULE_MM_STANDALONE] and \\r
309 Op == DEPEX_OPCODE_AND and \\r
310 self.ArchProtocols == set(GuidStructureStringToGuidString(Guid) for Guid in AllOperand):\r
311 self.PostfixNotation = []\r
312 return\r
313\r
314 if len(NewOperand) == 0:\r
315 self.TokenList = list(AllOperand)\r
316 else:\r
317 self.TokenList = []\r
318 while True:\r
319 self.TokenList.append(NewOperand.pop(0))\r
320 if NewOperand == []:\r
321 break\r
322 self.TokenList.append(Op)\r
323 self.PostfixNotation = []\r
324 self.GetPostfixNotation()\r
325\r
326\r
327 ## Convert a GUID value in C structure format into its binary form\r
328 #\r
329 # @param Guid The GUID value in C structure format\r
330 #\r
331 # @retval array The byte array representing the GUID value\r
332 #\r
333 def GetGuidValue(self, Guid):\r
334 GuidValueString = Guid.replace("{", "").replace("}", "").replace(" ", "")\r
335 GuidValueList = GuidValueString.split(",")\r
336 if len(GuidValueList) != 11:\r
337 EdkLogger.error("GenDepex", PARSER_ERROR, "Invalid GUID value string or opcode: %s" % Guid)\r
338 return pack("1I2H8B", *(int(value, 16) for value in GuidValueList))\r
339\r
340 ## Save the binary form of dependency expression in file\r
341 #\r
342 # @param File The path of file. If None is given, put the data on console\r
343 #\r
344 # @retval True If the file doesn't exist or file is changed\r
345 # @retval False If file exists and is not changed.\r
346 #\r
347 def Generate(self, File=None):\r
348 Buffer = BytesIO()\r
349 if len(self.PostfixNotation) == 0:\r
350 return False\r
351\r
352 for Item in self.PostfixNotation:\r
353 if Item in self.Opcode[self.Phase]:\r
354 Buffer.write(pack("B", self.Opcode[self.Phase][Item]))\r
355 elif Item in self.SupportedOpcode:\r
356 EdkLogger.error("GenDepex", FORMAT_INVALID,\r
357 "Opcode [%s] is not expected in %s phase" % (Item, self.Phase),\r
358 ExtraData=self.ExpressionString)\r
359 else:\r
360 Buffer.write(self.GetGuidValue(Item))\r
361\r
362 FilePath = ""\r
363 FileChangeFlag = True\r
364 if File is None:\r
365 sys.stdout.write(Buffer.getvalue())\r
366 FilePath = "STDOUT"\r
367 else:\r
368 FileChangeFlag = SaveFileOnChange(File, Buffer.getvalue(), True)\r
369\r
370 Buffer.close()\r
371 return FileChangeFlag\r
372\r
373versionNumber = ("0.04" + " " + gBUILD_VERSION)\r
374__version__ = "%prog Version " + versionNumber\r
375__copyright__ = "Copyright (c) 2007-2018, Intel Corporation All rights reserved."\r
376__usage__ = "%prog [options] [dependency_expression_file]"\r
377\r
378## Parse command line options\r
379#\r
380# @retval OptionParser\r
381#\r
382def GetOptions():\r
383 from optparse import OptionParser\r
384\r
385 Parser = OptionParser(description=__copyright__, version=__version__, usage=__usage__)\r
386\r
387 Parser.add_option("-o", "--output", dest="OutputFile", default=None, metavar="FILE",\r
388 help="Specify the name of depex file to be generated")\r
389 Parser.add_option("-t", "--module-type", dest="ModuleType", default=None,\r
390 help="The type of module for which the dependency expression serves")\r
391 Parser.add_option("-e", "--dependency-expression", dest="Expression", default="",\r
392 help="The string of dependency expression. If this option presents, the input file will be ignored.")\r
393 Parser.add_option("-m", "--optimize", dest="Optimize", default=False, action="store_true",\r
394 help="Do some simple optimization on the expression.")\r
395 Parser.add_option("-v", "--verbose", dest="verbose", default=False, action="store_true",\r
396 help="build with verbose information")\r
397 Parser.add_option("-d", "--debug", action="store", type="int", help="Enable debug messages at specified level.")\r
398 Parser.add_option("-q", "--quiet", dest="quiet", default=False, action="store_true",\r
399 help="build with little information")\r
400\r
401 return Parser.parse_args()\r
402\r
403\r
404## Entrance method\r
405#\r
406# @retval 0 Tool was successful\r
407# @retval 1 Tool failed\r
408#\r
409def Main():\r
410 EdkLogger.Initialize()\r
411 Option, Input = GetOptions()\r
412\r
413 # Set log level\r
414 if Option.quiet:\r
415 EdkLogger.SetLevel(EdkLogger.QUIET)\r
416 elif Option.verbose:\r
417 EdkLogger.SetLevel(EdkLogger.VERBOSE)\r
418 elif Option.debug is not None:\r
419 EdkLogger.SetLevel(Option.debug + 1)\r
420 else:\r
421 EdkLogger.SetLevel(EdkLogger.INFO)\r
422\r
423 try:\r
424 if Option.ModuleType is None or Option.ModuleType not in gType2Phase:\r
425 EdkLogger.error("GenDepex", OPTION_MISSING, "Module type is not specified or supported")\r
426\r
427 DxsFile = ''\r
428 if len(Input) > 0 and Option.Expression == "":\r
429 DxsFile = Input[0]\r
430 DxsString = open(DxsFile, 'r').read().replace("\n", " ").replace("\r", " ")\r
431 DxsString = gStartClosePattern.sub("\\1", DxsString)\r
432 elif Option.Expression != "":\r
433 if Option.Expression[0] == '"':\r
434 DxsString = Option.Expression[1:-1]\r
435 else:\r
436 DxsString = Option.Expression\r
437 else:\r
438 EdkLogger.error("GenDepex", OPTION_MISSING, "No expression string or file given")\r
439\r
440 Dpx = DependencyExpression(DxsString, Option.ModuleType, Option.Optimize)\r
441 if Option.OutputFile is not None:\r
442 FileChangeFlag = Dpx.Generate(Option.OutputFile)\r
443 if not FileChangeFlag and DxsFile:\r
444 #\r
445 # Touch the output file if its time stamp is older than the original\r
446 # DXS file to avoid re-invoke this tool for the dependency check in build rule.\r
447 #\r
448 if os.stat(DxsFile)[8] > os.stat(Option.OutputFile)[8]:\r
449 os.utime(Option.OutputFile, None)\r
450 else:\r
451 Dpx.Generate()\r
452 except BaseException as X:\r
453 EdkLogger.quiet("")\r
454 if Option is not None and Option.debug is not None:\r
455 EdkLogger.quiet(traceback.format_exc())\r
456 else:\r
457 EdkLogger.quiet(str(X))\r
458 return 1\r
459\r
460 return 0\r
461\r
462if __name__ == '__main__':\r
463 sys.exit(Main())\r
464\r