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