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 # 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
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.
16 import Common
.LongFilePathOs
as os
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 *
29 ## Regular expression for matching "DEPENDENCY_START ... DEPENDENCY_END"
30 gStartClosePattern
= re
.compile(".*DEPENDENCY_START(.+)DEPENDENCY_END.*", re
.S
)
32 ## Mapping between module type and EFI phase
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",
50 ## Convert dependency expression string into EFI internal representation
52 # DependencyExpression class is used to parse dependency expression string and
53 # convert it into its binary form.
55 class DependencyExpression
:
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'
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
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
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
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
]
120 OpcodeWithSingleOperand
= [DEPEX_OPCODE_NOT
, DEPEX_OPCODE_BEFORE
, DEPEX_OPCODE_AFTER
]
121 OpcodeWithTwoOperand
= [DEPEX_OPCODE_AND
, DEPEX_OPCODE_OR
]
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
]
131 # open and close brace must be taken as individual tokens
133 TokenPattern
= re
.compile("(\(|\)|\{[^{}]+\{?[^{}]+\}?[ ]*\}|\w+)")
137 # @param Expression The list or string of dependency expression
138 # @param ModuleType The type of the module using the dependency expression
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
147 self
.ExpressionString
= Expression
148 self
.GetExpressionTokenList()
150 self
.PostfixNotation
= []
153 self
.GetPostfixNotation()
154 self
.ValidateOpcode()
156 EdkLogger
.debug(EdkLogger
.DEBUG_8
, repr(self
))
159 EdkLogger
.debug(EdkLogger
.DEBUG_8
, "\n Optimized: " + repr(self
))
162 return " ".join(self
.TokenList
)
166 for Token
in self
.PostfixNotation
:
167 if Token
in self
.SupportedOpcode
:
168 WellForm
+= "\n " + Token
170 WellForm
+= ' ' + Token
173 ## Split the expression string into token list
174 def GetExpressionTokenList(self
):
175 self
.TokenList
= self
.TokenPattern
.findall(self
.ExpressionString
)
177 ## Convert token list into postfix notation
178 def GetPostfixNotation(self
):
181 for Token
in self
.TokenList
:
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
)
189 EdkLogger
.error("GenDepex", PARSER_ERROR
, "Invalid dependency expression: mismatched parentheses",
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:
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
)
208 while len(Stack
) > 0:
209 if Stack
[-1] == "(" or self
.OpcodePriority
[Token
] >= self
.OpcodePriority
[Stack
[-1]]:
211 self
.PostfixNotation
.append(Stack
.pop())
213 self
.OpcodeList
.append(Token
)
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
:
227 self
.OpcodeList
.append(Token
)
229 EdkLogger
.error("GenDepex", PARSER_ERROR
,
230 "Opcode=%s doesn't supported in %s stage " % (Token
, self
.Phase
),
232 self
.PostfixNotation
.append(Token
)
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",
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
)
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
,
251 if len(self
.PostfixNotation
) < 3:
252 EdkLogger
.error("GenDepex", PARSER_ERROR
, "Missing operand for %s" % Op
,
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
,
259 if len(self
.PostfixNotation
) < 3:
260 EdkLogger
.error("GenDepex", PARSER_ERROR
, "Missing operand for %s" % Op
,
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],
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],
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",
272 ## Simply optimize the dependency expression by removing duplicated operands
274 OpcodeSet
= set(self
.OpcodeList
)
275 # if there are isn't one in the set, return
276 if len(OpcodeSet
) != 1:
279 #if Op isn't either OR or AND, return
280 if Op
not in [DEPEX_OPCODE_AND
, DEPEX_OPCODE_OR
]:
284 for Token
in self
.PostfixNotation
:
285 if Token
in self
.SupportedOpcode
or Token
in NewOperand
:
287 AllOperand
.add(Token
)
288 if Token
== DEPEX_OPCODE_TRUE
:
289 if Op
== DEPEX_OPCODE_AND
:
292 NewOperand
.append(Token
)
294 elif Token
== DEPEX_OPCODE_FALSE
:
295 if Op
== DEPEX_OPCODE_OR
:
298 NewOperand
.append(Token
)
300 NewOperand
.append(Token
)
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
= []
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
= []
314 if len(NewOperand
) == 0:
315 self
.TokenList
= list(AllOperand
)
319 self
.TokenList
.append(NewOperand
.pop(0))
322 self
.TokenList
.append(Op
)
323 self
.PostfixNotation
= []
324 self
.GetPostfixNotation()
327 ## Convert a GUID value in C structure format into its binary form
329 # @param Guid The GUID value in C structure format
331 # @retval array The byte array representing the GUID value
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
))
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
, 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__':