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