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