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