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