]> git.proxmox.com Git - mirror_edk2.git/blob - BaseTools/Source/Python/AutoGen/GenDepex.py
BaseTools: Clean some coding style issues
[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 }
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" + " " + gBUILD_VERSION)
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 FileChangeFlag = Dpx.Generate(Option.OutputFile)
427 if not FileChangeFlag and DxsFile:
428 #
429 # Touch the output file if its time stamp is older than the original
430 # DXS file to avoid re-invoke this tool for the dependency check in build rule.
431 #
432 if os.stat(DxsFile)[8] > os.stat(Option.OutputFile)[8]:
433 os.utime(Option.OutputFile, None)
434 else:
435 Dpx.Generate()
436 except BaseException, X:
437 EdkLogger.quiet("")
438 if Option != None and Option.debug != None:
439 EdkLogger.quiet(traceback.format_exc())
440 else:
441 EdkLogger.quiet(str(X))
442 return 1
443
444 return 0
445
446 if __name__ == '__main__':
447 sys.exit(Main())
448