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