BaseTools: Replace StringIO.StringIO with io.BytesIO
[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 io import BytesIO
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 isinstance(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 OpcodeSet = set(self.OpcodeList)
275 # if there are isn't one in the set, return
276 if len(OpcodeSet) != 1:
277 return
278 Op = OpcodeSet.pop()
279 #if Op isn't either OR or AND, return
280 if Op not in [DEPEX_OPCODE_AND, DEPEX_OPCODE_OR]:
281 return
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 == DEPEX_OPCODE_TRUE:
289 if Op == DEPEX_OPCODE_AND:
290 continue
291 else:
292 NewOperand.append(Token)
293 break
294 elif Token == DEPEX_OPCODE_FALSE:
295 if Op == DEPEX_OPCODE_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] == DEPEX_OPCODE_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 == DEPEX_OPCODE_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 = 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