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