2 # This file is used to generate DEPEX file for module's dependency expression
4 # Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>
5 # SPDX-License-Identifier: BSD-2-Clause-Patent
10 import Common
.LongFilePathOs
as os
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 *
25 ## Regular expression for matching "DEPENDENCY_START ... DEPENDENCY_END"
26 gStartClosePattern
= re
.compile(".*DEPENDENCY_START(.+)DEPENDENCY_END.*", re
.S
)
28 ## Mapping between module type and EFI phase
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",
46 ## Convert dependency expression string into EFI internal representation
48 # DependencyExpression class is used to parse dependency expression string and
49 # convert it into its binary form.
51 class DependencyExpression
:
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'
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
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
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
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
]
116 OpcodeWithSingleOperand
= [DEPEX_OPCODE_NOT
, DEPEX_OPCODE_BEFORE
, DEPEX_OPCODE_AFTER
]
117 OpcodeWithTwoOperand
= [DEPEX_OPCODE_AND
, DEPEX_OPCODE_OR
]
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
]
127 # open and close brace must be taken as individual tokens
129 TokenPattern
= re
.compile("(\(|\)|\{[^{}]+\{?[^{}]+\}?[ ]*\}|\w+)")
133 # @param Expression The list or string of dependency expression
134 # @param ModuleType The type of the module using the dependency expression
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
143 self
.ExpressionString
= Expression
144 self
.GetExpressionTokenList()
146 self
.PostfixNotation
= []
149 self
.GetPostfixNotation()
150 self
.ValidateOpcode()
152 EdkLogger
.debug(EdkLogger
.DEBUG_8
, repr(self
))
155 EdkLogger
.debug(EdkLogger
.DEBUG_8
, "\n Optimized: " + repr(self
))
158 return " ".join(self
.TokenList
)
162 for Token
in self
.PostfixNotation
:
163 if Token
in self
.SupportedOpcode
:
164 WellForm
+= "\n " + Token
166 WellForm
+= ' ' + Token
169 ## Split the expression string into token list
170 def GetExpressionTokenList(self
):
171 self
.TokenList
= self
.TokenPattern
.findall(self
.ExpressionString
)
173 ## Convert token list into postfix notation
174 def GetPostfixNotation(self
):
177 for Token
in self
.TokenList
:
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
)
185 EdkLogger
.error("GenDepex", PARSER_ERROR
, "Invalid dependency expression: mismatched parentheses",
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:
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
)
204 while len(Stack
) > 0:
205 if Stack
[-1] == "(" or self
.OpcodePriority
[Token
] >= self
.OpcodePriority
[Stack
[-1]]:
207 self
.PostfixNotation
.append(Stack
.pop())
209 self
.OpcodeList
.append(Token
)
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
:
223 self
.OpcodeList
.append(Token
)
225 EdkLogger
.error("GenDepex", PARSER_ERROR
,
226 "Opcode=%s doesn't supported in %s stage " % (Token
, self
.Phase
),
228 self
.PostfixNotation
.append(Token
)
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",
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
)
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
,
247 if len(self
.PostfixNotation
) < 3:
248 EdkLogger
.error("GenDepex", PARSER_ERROR
, "Missing operand for %s" % Op
,
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
,
255 if len(self
.PostfixNotation
) < 3:
256 EdkLogger
.error("GenDepex", PARSER_ERROR
, "Missing operand for %s" % Op
,
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],
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],
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",
268 ## Simply optimize the dependency expression by removing duplicated operands
270 OpcodeSet
= set(self
.OpcodeList
)
271 # if there are isn't one in the set, return
272 if len(OpcodeSet
) != 1:
275 #if Op isn't either OR or AND, return
276 if Op
not in [DEPEX_OPCODE_AND
, DEPEX_OPCODE_OR
]:
280 for Token
in self
.PostfixNotation
:
281 if Token
in self
.SupportedOpcode
or Token
in NewOperand
:
283 AllOperand
.add(Token
)
284 if Token
== DEPEX_OPCODE_TRUE
:
285 if Op
== DEPEX_OPCODE_AND
:
288 NewOperand
.append(Token
)
290 elif Token
== DEPEX_OPCODE_FALSE
:
291 if Op
== DEPEX_OPCODE_OR
:
294 NewOperand
.append(Token
)
296 NewOperand
.append(Token
)
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
= []
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
= []
310 if len(NewOperand
) == 0:
311 self
.TokenList
= list(AllOperand
)
315 self
.TokenList
.append(NewOperand
.pop(0))
318 self
.TokenList
.append(Op
)
319 self
.PostfixNotation
= []
320 self
.GetPostfixNotation()
323 ## Convert a GUID value in C structure format into its binary form
325 # @param Guid The GUID value in C structure format
327 # @retval array The byte array representing the GUID value
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
))
340 ## Save the binary form of dependency expression in file
342 # @param File The path of file. If None is given, put the data on console
344 # @retval True If the file doesn't exist or file is changed
345 # @retval False If file exists and is not changed.
347 def Generate(self
, File
=None):
349 if len(self
.PostfixNotation
) == 0:
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
)
360 Buffer
.write(self
.GetGuidValue(Item
))
363 FileChangeFlag
= True
365 sys
.stdout
.write(Buffer
.getvalue())
368 FileChangeFlag
= SaveFileOnChange(File
, Buffer
.getvalue(), True)
371 return FileChangeFlag
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]"
378 ## Parse command line options
380 # @retval OptionParser
383 from optparse
import OptionParser
385 Parser
= OptionParser(description
=__copyright__
, version
=__version__
, usage
=__usage__
)
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")
401 return Parser
.parse_args()
406 # @retval 0 Tool was successful
407 # @retval 1 Tool failed
410 EdkLogger
.Initialize()
411 Option
, Input
= GetOptions()
415 EdkLogger
.SetLevel(EdkLogger
.QUIET
)
417 EdkLogger
.SetLevel(EdkLogger
.VERBOSE
)
418 elif Option
.debug
is not None:
419 EdkLogger
.SetLevel(Option
.debug
+ 1)
421 EdkLogger
.SetLevel(EdkLogger
.INFO
)
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")
428 if len(Input
) > 0 and Option
.Expression
== "":
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]
436 DxsString
= Option
.Expression
438 EdkLogger
.error("GenDepex", OPTION_MISSING
, "No expression string or file given")
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
:
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.
448 if os
.stat(DxsFile
)[8] > os
.stat(Option
.OutputFile
)[8]:
449 os
.utime(Option
.OutputFile
, None)
452 except BaseException
as X
:
454 if Option
is not None and Option
.debug
is not None:
455 EdkLogger
.quiet(traceback
.format_exc())
457 EdkLogger
.quiet(str(X
))
462 if __name__
== '__main__':