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