]> git.proxmox.com Git - mirror_edk2.git/blob - BaseTools/Source/Python/AutoGen/GenDepex.py
BaseTools: Replace BSD License with BSD+Patent License
[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 # SPDX-License-Identifier: BSD-2-Clause-Patent
6
7 ## Import Modules
8 #
9 import sys
10 import Common.LongFilePathOs as os
11 import re
12 import traceback
13 from Common.LongFilePathSupport import OpenLongFilePath as open
14 from io import BytesIO
15 from struct import pack
16 from Common.BuildToolError import *
17 from Common.Misc import SaveFileOnChange
18 from Common.Misc import GuidStructureStringToGuidString
19 from Common.Misc import GuidStructureByteArrayToGuidString
20 from Common.Misc import GuidStringToGuidStructureString
21 from Common import EdkLogger as EdkLogger
22 from Common.BuildVersion import gBUILD_VERSION
23 from Common.DataType import *
24
25 ## Regular expression for matching "DEPENDENCY_START ... DEPENDENCY_END"
26 gStartClosePattern = re.compile(".*DEPENDENCY_START(.+)DEPENDENCY_END.*", re.S)
27
28 ## Mapping between module type and EFI phase
29 gType2Phase = {
30 SUP_MODULE_BASE : None,
31 SUP_MODULE_SEC : "PEI",
32 SUP_MODULE_PEI_CORE : "PEI",
33 SUP_MODULE_PEIM : "PEI",
34 SUP_MODULE_DXE_CORE : "DXE",
35 SUP_MODULE_DXE_DRIVER : "DXE",
36 SUP_MODULE_DXE_SMM_DRIVER : "DXE",
37 SUP_MODULE_DXE_RUNTIME_DRIVER: "DXE",
38 SUP_MODULE_DXE_SAL_DRIVER : "DXE",
39 SUP_MODULE_UEFI_DRIVER : "DXE",
40 SUP_MODULE_UEFI_APPLICATION : "DXE",
41 SUP_MODULE_SMM_CORE : "DXE",
42 SUP_MODULE_MM_STANDALONE : "MM",
43 SUP_MODULE_MM_CORE_STANDALONE : "MM",
44 }
45
46 ## Convert dependency expression string into EFI internal representation
47 #
48 # DependencyExpression class is used to parse dependency expression string and
49 # convert it into its binary form.
50 #
51 class DependencyExpression:
52
53 ArchProtocols = {
54 '665e3ff6-46cc-11d4-9a38-0090273fc14d', # 'gEfiBdsArchProtocolGuid'
55 '26baccb1-6f42-11d4-bce7-0080c73c8881', # 'gEfiCpuArchProtocolGuid'
56 '26baccb2-6f42-11d4-bce7-0080c73c8881', # 'gEfiMetronomeArchProtocolGuid'
57 '1da97072-bddc-4b30-99f1-72a0b56fff2a', # 'gEfiMonotonicCounterArchProtocolGuid'
58 '27cfac87-46cc-11d4-9a38-0090273fc14d', # 'gEfiRealTimeClockArchProtocolGuid'
59 '27cfac88-46cc-11d4-9a38-0090273fc14d', # 'gEfiResetArchProtocolGuid'
60 'b7dfb4e1-052f-449f-87be-9818fc91b733', # 'gEfiRuntimeArchProtocolGuid'
61 'a46423e3-4617-49f1-b9ff-d1bfa9115839', # 'gEfiSecurityArchProtocolGuid'
62 '26baccb3-6f42-11d4-bce7-0080c73c8881', # 'gEfiTimerArchProtocolGuid'
63 '6441f818-6362-4e44-b570-7dba31dd2453', # 'gEfiVariableWriteArchProtocolGuid'
64 '1e5668e2-8481-11d4-bcf1-0080c73c8881', # 'gEfiVariableArchProtocolGuid'
65 '665e3ff5-46cc-11d4-9a38-0090273fc14d' # 'gEfiWatchdogTimerArchProtocolGuid'
66 }
67
68 OpcodePriority = {
69 DEPEX_OPCODE_AND : 1,
70 DEPEX_OPCODE_OR : 1,
71 DEPEX_OPCODE_NOT : 2,
72 }
73
74 Opcode = {
75 "PEI" : {
76 DEPEX_OPCODE_PUSH : 0x02,
77 DEPEX_OPCODE_AND : 0x03,
78 DEPEX_OPCODE_OR : 0x04,
79 DEPEX_OPCODE_NOT : 0x05,
80 DEPEX_OPCODE_TRUE : 0x06,
81 DEPEX_OPCODE_FALSE : 0x07,
82 DEPEX_OPCODE_END : 0x08
83 },
84
85 "DXE" : {
86 DEPEX_OPCODE_BEFORE: 0x00,
87 DEPEX_OPCODE_AFTER : 0x01,
88 DEPEX_OPCODE_PUSH : 0x02,
89 DEPEX_OPCODE_AND : 0x03,
90 DEPEX_OPCODE_OR : 0x04,
91 DEPEX_OPCODE_NOT : 0x05,
92 DEPEX_OPCODE_TRUE : 0x06,
93 DEPEX_OPCODE_FALSE : 0x07,
94 DEPEX_OPCODE_END : 0x08,
95 DEPEX_OPCODE_SOR : 0x09
96 },
97
98 "MM" : {
99 DEPEX_OPCODE_BEFORE: 0x00,
100 DEPEX_OPCODE_AFTER : 0x01,
101 DEPEX_OPCODE_PUSH : 0x02,
102 DEPEX_OPCODE_AND : 0x03,
103 DEPEX_OPCODE_OR : 0x04,
104 DEPEX_OPCODE_NOT : 0x05,
105 DEPEX_OPCODE_TRUE : 0x06,
106 DEPEX_OPCODE_FALSE : 0x07,
107 DEPEX_OPCODE_END : 0x08,
108 DEPEX_OPCODE_SOR : 0x09
109 }
110 }
111
112 # all supported op codes and operands
113 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]
114 SupportedOperand = [DEPEX_OPCODE_TRUE, DEPEX_OPCODE_FALSE]
115
116 OpcodeWithSingleOperand = [DEPEX_OPCODE_NOT, DEPEX_OPCODE_BEFORE, DEPEX_OPCODE_AFTER]
117 OpcodeWithTwoOperand = [DEPEX_OPCODE_AND, DEPEX_OPCODE_OR]
118
119 # op code that should not be the last one
120 NonEndingOpcode = [DEPEX_OPCODE_AND, DEPEX_OPCODE_OR, DEPEX_OPCODE_NOT, DEPEX_OPCODE_SOR]
121 # op code must not present at the same time
122 ExclusiveOpcode = [DEPEX_OPCODE_BEFORE, DEPEX_OPCODE_AFTER]
123 # op code that should be the first one if it presents
124 AboveAllOpcode = [DEPEX_OPCODE_SOR, DEPEX_OPCODE_BEFORE, DEPEX_OPCODE_AFTER]
125
126 #
127 # open and close brace must be taken as individual tokens
128 #
129 TokenPattern = re.compile("(\(|\)|\{[^{}]+\{?[^{}]+\}?[ ]*\}|\w+)")
130
131 ## Constructor
132 #
133 # @param Expression The list or string of dependency expression
134 # @param ModuleType The type of the module using the dependency expression
135 #
136 def __init__(self, Expression, ModuleType, Optimize=False):
137 self.ModuleType = ModuleType
138 self.Phase = gType2Phase[ModuleType]
139 if isinstance(Expression, type([])):
140 self.ExpressionString = " ".join(Expression)
141 self.TokenList = Expression
142 else:
143 self.ExpressionString = Expression
144 self.GetExpressionTokenList()
145
146 self.PostfixNotation = []
147 self.OpcodeList = []
148
149 self.GetPostfixNotation()
150 self.ValidateOpcode()
151
152 EdkLogger.debug(EdkLogger.DEBUG_8, repr(self))
153 if Optimize:
154 self.Optimize()
155 EdkLogger.debug(EdkLogger.DEBUG_8, "\n Optimized: " + repr(self))
156
157 def __str__(self):
158 return " ".join(self.TokenList)
159
160 def __repr__(self):
161 WellForm = ''
162 for Token in self.PostfixNotation:
163 if Token in self.SupportedOpcode:
164 WellForm += "\n " + Token
165 else:
166 WellForm += ' ' + Token
167 return WellForm
168
169 ## Split the expression string into token list
170 def GetExpressionTokenList(self):
171 self.TokenList = self.TokenPattern.findall(self.ExpressionString)
172
173 ## Convert token list into postfix notation
174 def GetPostfixNotation(self):
175 Stack = []
176 LastToken = ''
177 for Token in self.TokenList:
178 if Token == "(":
179 if LastToken not in self.SupportedOpcode + ['(', '', None]:
180 EdkLogger.error("GenDepex", PARSER_ERROR, "Invalid dependency expression: missing operator before open parentheses",
181 ExtraData="Near %s" % LastToken)
182 Stack.append(Token)
183 elif Token == ")":
184 if '(' not in Stack:
185 EdkLogger.error("GenDepex", PARSER_ERROR, "Invalid dependency expression: mismatched parentheses",
186 ExtraData=str(self))
187 elif LastToken in self.SupportedOpcode + ['', None]:
188 EdkLogger.error("GenDepex", PARSER_ERROR, "Invalid dependency expression: missing operand before close parentheses",
189 ExtraData="Near %s" % LastToken)
190 while len(Stack) > 0:
191 if Stack[-1] == '(':
192 Stack.pop()
193 break
194 self.PostfixNotation.append(Stack.pop())
195 elif Token in self.OpcodePriority:
196 if Token == DEPEX_OPCODE_NOT:
197 if LastToken not in self.SupportedOpcode + ['(', '', None]:
198 EdkLogger.error("GenDepex", PARSER_ERROR, "Invalid dependency expression: missing operator before NOT",
199 ExtraData="Near %s" % LastToken)
200 elif LastToken in self.SupportedOpcode + ['(', '', None]:
201 EdkLogger.error("GenDepex", PARSER_ERROR, "Invalid dependency expression: missing operand before " + Token,
202 ExtraData="Near %s" % LastToken)
203
204 while len(Stack) > 0:
205 if Stack[-1] == "(" or self.OpcodePriority[Token] >= self.OpcodePriority[Stack[-1]]:
206 break
207 self.PostfixNotation.append(Stack.pop())
208 Stack.append(Token)
209 self.OpcodeList.append(Token)
210 else:
211 if Token not in self.SupportedOpcode:
212 # not OP, take it as GUID
213 if LastToken not in self.SupportedOpcode + ['(', '', None]:
214 EdkLogger.error("GenDepex", PARSER_ERROR, "Invalid dependency expression: missing operator before %s" % Token,
215 ExtraData="Near %s" % LastToken)
216 if len(self.OpcodeList) == 0 or self.OpcodeList[-1] not in self.ExclusiveOpcode:
217 if Token not in self.SupportedOperand:
218 self.PostfixNotation.append(DEPEX_OPCODE_PUSH)
219 # check if OP is valid in this phase
220 elif Token in self.Opcode[self.Phase]:
221 if Token == DEPEX_OPCODE_END:
222 break
223 self.OpcodeList.append(Token)
224 else:
225 EdkLogger.error("GenDepex", PARSER_ERROR,
226 "Opcode=%s doesn't supported in %s stage " % (Token, self.Phase),
227 ExtraData=str(self))
228 self.PostfixNotation.append(Token)
229 LastToken = Token
230
231 # there should not be parentheses in Stack
232 if '(' in Stack or ')' in Stack:
233 EdkLogger.error("GenDepex", PARSER_ERROR, "Invalid dependency expression: mismatched parentheses",
234 ExtraData=str(self))
235 while len(Stack) > 0:
236 self.PostfixNotation.append(Stack.pop())
237 if self.PostfixNotation[-1] != DEPEX_OPCODE_END:
238 self.PostfixNotation.append(DEPEX_OPCODE_END)
239
240 ## Validate the dependency expression
241 def ValidateOpcode(self):
242 for Op in self.AboveAllOpcode:
243 if Op in self.PostfixNotation:
244 if Op != self.PostfixNotation[0]:
245 EdkLogger.error("GenDepex", PARSER_ERROR, "%s should be the first 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 for Op in self.ExclusiveOpcode:
251 if Op in self.OpcodeList:
252 if len(self.OpcodeList) > 1:
253 EdkLogger.error("GenDepex", PARSER_ERROR, "%s should be the only 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 if self.TokenList[-1] != DEPEX_OPCODE_END and self.TokenList[-1] in self.NonEndingOpcode:
259 EdkLogger.error("GenDepex", PARSER_ERROR, "Extra %s at the end of the dependency expression" % self.TokenList[-1],
260 ExtraData=str(self))
261 if self.TokenList[-1] == DEPEX_OPCODE_END and self.TokenList[-2] in self.NonEndingOpcode:
262 EdkLogger.error("GenDepex", PARSER_ERROR, "Extra %s at the end of the dependency expression" % self.TokenList[-2],
263 ExtraData=str(self))
264 if DEPEX_OPCODE_END in self.TokenList and DEPEX_OPCODE_END != self.TokenList[-1]:
265 EdkLogger.error("GenDepex", PARSER_ERROR, "Extra expressions after END",
266 ExtraData=str(self))
267
268 ## Simply optimize the dependency expression by removing duplicated operands
269 def Optimize(self):
270 OpcodeSet = set(self.OpcodeList)
271 # if there are isn't one in the set, return
272 if len(OpcodeSet) != 1:
273 return
274 Op = OpcodeSet.pop()
275 #if Op isn't either OR or AND, return
276 if Op not in [DEPEX_OPCODE_AND, DEPEX_OPCODE_OR]:
277 return
278 NewOperand = []
279 AllOperand = set()
280 for Token in self.PostfixNotation:
281 if Token in self.SupportedOpcode or Token in NewOperand:
282 continue
283 AllOperand.add(Token)
284 if Token == DEPEX_OPCODE_TRUE:
285 if Op == DEPEX_OPCODE_AND:
286 continue
287 else:
288 NewOperand.append(Token)
289 break
290 elif Token == DEPEX_OPCODE_FALSE:
291 if Op == DEPEX_OPCODE_OR:
292 continue
293 else:
294 NewOperand.append(Token)
295 break
296 NewOperand.append(Token)
297
298 # don't generate depex if only TRUE operand left
299 if self.ModuleType == SUP_MODULE_PEIM and len(NewOperand) == 1 and NewOperand[0] == DEPEX_OPCODE_TRUE:
300 self.PostfixNotation = []
301 return
302
303 # don't generate depex if all operands are architecture protocols
304 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 \
305 Op == DEPEX_OPCODE_AND and \
306 self.ArchProtocols == set(GuidStructureStringToGuidString(Guid) for Guid in AllOperand):
307 self.PostfixNotation = []
308 return
309
310 if len(NewOperand) == 0:
311 self.TokenList = list(AllOperand)
312 else:
313 self.TokenList = []
314 while True:
315 self.TokenList.append(NewOperand.pop(0))
316 if NewOperand == []:
317 break
318 self.TokenList.append(Op)
319 self.PostfixNotation = []
320 self.GetPostfixNotation()
321
322
323 ## Convert a GUID value in C structure format into its binary form
324 #
325 # @param Guid The GUID value in C structure format
326 #
327 # @retval array The byte array representing the GUID value
328 #
329 def GetGuidValue(self, Guid):
330 GuidValueString = Guid.replace("{", "").replace("}", "").replace(" ", "")
331 GuidValueList = GuidValueString.split(",")
332 if len(GuidValueList) != 11 and len(GuidValueList) == 16:
333 GuidValueString = GuidStringToGuidStructureString(GuidStructureByteArrayToGuidString(Guid))
334 GuidValueString = GuidValueString.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 = BytesIO()
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-2018, 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 as 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