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'
81 DEPEX_OPCODE_PUSH
: 0x02,
82 DEPEX_OPCODE_AND
: 0x03,
83 DEPEX_OPCODE_OR
: 0x04,
84 DEPEX_OPCODE_NOT
: 0x05,
85 DEPEX_OPCODE_TRUE
: 0x06,
86 DEPEX_OPCODE_FALSE
: 0x07,
87 DEPEX_OPCODE_END
: 0x08
91 DEPEX_OPCODE_BEFORE
: 0x00,
92 DEPEX_OPCODE_AFTER
: 0x01,
93 DEPEX_OPCODE_PUSH
: 0x02,
94 DEPEX_OPCODE_AND
: 0x03,
95 DEPEX_OPCODE_OR
: 0x04,
96 DEPEX_OPCODE_NOT
: 0x05,
97 DEPEX_OPCODE_TRUE
: 0x06,
98 DEPEX_OPCODE_FALSE
: 0x07,
99 DEPEX_OPCODE_END
: 0x08,
100 DEPEX_OPCODE_SOR
: 0x09
104 DEPEX_OPCODE_BEFORE
: 0x00,
105 DEPEX_OPCODE_AFTER
: 0x01,
106 DEPEX_OPCODE_PUSH
: 0x02,
107 DEPEX_OPCODE_AND
: 0x03,
108 DEPEX_OPCODE_OR
: 0x04,
109 DEPEX_OPCODE_NOT
: 0x05,
110 DEPEX_OPCODE_TRUE
: 0x06,
111 DEPEX_OPCODE_FALSE
: 0x07,
112 DEPEX_OPCODE_END
: 0x08,
113 DEPEX_OPCODE_SOR
: 0x09
117 # all supported op codes and operands
118 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
]
119 SupportedOperand
= [DEPEX_OPCODE_TRUE
, DEPEX_OPCODE_FALSE
]
121 OpcodeWithSingleOperand
= [DEPEX_OPCODE_NOT
, DEPEX_OPCODE_BEFORE
, DEPEX_OPCODE_AFTER
]
122 OpcodeWithTwoOperand
= [DEPEX_OPCODE_AND
, DEPEX_OPCODE_OR
]
124 # op code that should not be the last one
125 NonEndingOpcode
= [DEPEX_OPCODE_AND
, DEPEX_OPCODE_OR
, DEPEX_OPCODE_NOT
, DEPEX_OPCODE_SOR
]
126 # op code must not present at the same time
127 ExclusiveOpcode
= [DEPEX_OPCODE_BEFORE
, DEPEX_OPCODE_AFTER
]
128 # op code that should be the first one if it presents
129 AboveAllOpcode
= [DEPEX_OPCODE_SOR
, DEPEX_OPCODE_BEFORE
, DEPEX_OPCODE_AFTER
]
132 # open and close brace must be taken as individual tokens
134 TokenPattern
= re
.compile("(\(|\)|\{[^{}]+\{?[^{}]+\}?[ ]*\}|\w+)")
138 # @param Expression The list or string of dependency expression
139 # @param ModuleType The type of the module using the dependency expression
141 def __init__(self
, Expression
, ModuleType
, Optimize
=False):
142 self
.ModuleType
= ModuleType
143 self
.Phase
= gType2Phase
[ModuleType
]
144 if type(Expression
) == type([]):
145 self
.ExpressionString
= " ".join(Expression
)
146 self
.TokenList
= Expression
148 self
.ExpressionString
= Expression
149 self
.GetExpressionTokenList()
151 self
.PostfixNotation
= []
154 self
.GetPostfixNotation()
155 self
.ValidateOpcode()
157 EdkLogger
.debug(EdkLogger
.DEBUG_8
, repr(self
))
160 EdkLogger
.debug(EdkLogger
.DEBUG_8
, "\n Optimized: " + repr(self
))
163 return " ".join(self
.TokenList
)
167 for Token
in self
.PostfixNotation
:
168 if Token
in self
.SupportedOpcode
:
169 WellForm
+= "\n " + Token
171 WellForm
+= ' ' + Token
174 ## Split the expression string into token list
175 def GetExpressionTokenList(self
):
176 self
.TokenList
= self
.TokenPattern
.findall(self
.ExpressionString
)
178 ## Convert token list into postfix notation
179 def GetPostfixNotation(self
):
182 for Token
in self
.TokenList
:
184 if LastToken
not in self
.SupportedOpcode
+ ['(', '', None]:
185 EdkLogger
.error("GenDepex", PARSER_ERROR
, "Invalid dependency expression: missing operator before open parentheses",
186 ExtraData
="Near %s" % LastToken
)
190 EdkLogger
.error("GenDepex", PARSER_ERROR
, "Invalid dependency expression: mismatched parentheses",
192 elif LastToken
in self
.SupportedOpcode
+ ['', None]:
193 EdkLogger
.error("GenDepex", PARSER_ERROR
, "Invalid dependency expression: missing operand before close parentheses",
194 ExtraData
="Near %s" % LastToken
)
195 while len(Stack
) > 0:
199 self
.PostfixNotation
.append(Stack
.pop())
200 elif Token
in self
.OpcodePriority
:
201 if Token
== DEPEX_OPCODE_NOT
:
202 if LastToken
not in self
.SupportedOpcode
+ ['(', '', None]:
203 EdkLogger
.error("GenDepex", PARSER_ERROR
, "Invalid dependency expression: missing operator before NOT",
204 ExtraData
="Near %s" % LastToken
)
205 elif LastToken
in self
.SupportedOpcode
+ ['(', '', None]:
206 EdkLogger
.error("GenDepex", PARSER_ERROR
, "Invalid dependency expression: missing operand before " + Token
,
207 ExtraData
="Near %s" % LastToken
)
209 while len(Stack
) > 0:
210 if Stack
[-1] == "(" or self
.OpcodePriority
[Token
] >= self
.OpcodePriority
[Stack
[-1]]:
212 self
.PostfixNotation
.append(Stack
.pop())
214 self
.OpcodeList
.append(Token
)
216 if Token
not in self
.SupportedOpcode
:
217 # not OP, take it as GUID
218 if LastToken
not in self
.SupportedOpcode
+ ['(', '', None]:
219 EdkLogger
.error("GenDepex", PARSER_ERROR
, "Invalid dependency expression: missing operator before %s" % Token
,
220 ExtraData
="Near %s" % LastToken
)
221 if len(self
.OpcodeList
) == 0 or self
.OpcodeList
[-1] not in self
.ExclusiveOpcode
:
222 if Token
not in self
.SupportedOperand
:
223 self
.PostfixNotation
.append(DEPEX_OPCODE_PUSH
)
224 # check if OP is valid in this phase
225 elif Token
in self
.Opcode
[self
.Phase
]:
226 if Token
== DEPEX_OPCODE_END
:
228 self
.OpcodeList
.append(Token
)
230 EdkLogger
.error("GenDepex", PARSER_ERROR
,
231 "Opcode=%s doesn't supported in %s stage " % (Token
, self
.Phase
),
233 self
.PostfixNotation
.append(Token
)
236 # there should not be parentheses in Stack
237 if '(' in Stack
or ')' in Stack
:
238 EdkLogger
.error("GenDepex", PARSER_ERROR
, "Invalid dependency expression: mismatched parentheses",
240 while len(Stack
) > 0:
241 self
.PostfixNotation
.append(Stack
.pop())
242 if self
.PostfixNotation
[-1] != DEPEX_OPCODE_END
:
243 self
.PostfixNotation
.append(DEPEX_OPCODE_END
)
245 ## Validate the dependency expression
246 def ValidateOpcode(self
):
247 for Op
in self
.AboveAllOpcode
:
248 if Op
in self
.PostfixNotation
:
249 if Op
!= self
.PostfixNotation
[0]:
250 EdkLogger
.error("GenDepex", PARSER_ERROR
, "%s should be the first opcode in the expression" % Op
,
252 if len(self
.PostfixNotation
) < 3:
253 EdkLogger
.error("GenDepex", PARSER_ERROR
, "Missing operand for %s" % Op
,
255 for Op
in self
.ExclusiveOpcode
:
256 if Op
in self
.OpcodeList
:
257 if len(self
.OpcodeList
) > 1:
258 EdkLogger
.error("GenDepex", PARSER_ERROR
, "%s should be the only opcode in the expression" % Op
,
260 if len(self
.PostfixNotation
) < 3:
261 EdkLogger
.error("GenDepex", PARSER_ERROR
, "Missing operand for %s" % Op
,
263 if self
.TokenList
[-1] != DEPEX_OPCODE_END
and self
.TokenList
[-1] in self
.NonEndingOpcode
:
264 EdkLogger
.error("GenDepex", PARSER_ERROR
, "Extra %s at the end of the dependency expression" % self
.TokenList
[-1],
266 if self
.TokenList
[-1] == DEPEX_OPCODE_END
and self
.TokenList
[-2] in self
.NonEndingOpcode
:
267 EdkLogger
.error("GenDepex", PARSER_ERROR
, "Extra %s at the end of the dependency expression" % self
.TokenList
[-2],
269 if DEPEX_OPCODE_END
in self
.TokenList
and DEPEX_OPCODE_END
!= self
.TokenList
[-1]:
270 EdkLogger
.error("GenDepex", PARSER_ERROR
, "Extra expressions after END",
273 ## Simply optimize the dependency expression by removing duplicated operands
275 ValidOpcode
= list(set(self
.OpcodeList
))
276 if len(ValidOpcode
) != 1 or ValidOpcode
[0] not in [DEPEX_OPCODE_AND
, DEPEX_OPCODE_OR
]:
281 for Token
in self
.PostfixNotation
:
282 if Token
in self
.SupportedOpcode
or Token
in NewOperand
:
284 AllOperand
.add(Token
)
285 if Token
== DEPEX_OPCODE_TRUE
:
286 if Op
== DEPEX_OPCODE_AND
:
289 NewOperand
.append(Token
)
291 elif Token
== DEPEX_OPCODE_FALSE
:
292 if Op
== DEPEX_OPCODE_OR
:
295 NewOperand
.append(Token
)
297 NewOperand
.append(Token
)
299 # don't generate depex if only TRUE operand left
300 if self
.ModuleType
== SUP_MODULE_PEIM
and len(NewOperand
) == 1 and NewOperand
[0] == DEPEX_OPCODE_TRUE
:
301 self
.PostfixNotation
= []
304 # don't generate depex if all operands are architecture protocols
305 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 \
306 Op
== DEPEX_OPCODE_AND
and \
307 self
.ArchProtocols
== set([GuidStructureStringToGuidString(Guid
) for Guid
in AllOperand
]):
308 self
.PostfixNotation
= []
311 if len(NewOperand
) == 0:
312 self
.TokenList
= list(AllOperand
)
316 self
.TokenList
.append(NewOperand
.pop(0))
319 self
.TokenList
.append(Op
)
320 self
.PostfixNotation
= []
321 self
.GetPostfixNotation()
324 ## Convert a GUID value in C structure format into its binary form
326 # @param Guid The GUID value in C structure format
328 # @retval array The byte array representing the GUID value
330 def GetGuidValue(self
, Guid
):
331 GuidValueString
= Guid
.replace("{", "").replace("}", "").replace(" ", "")
332 GuidValueList
= GuidValueString
.split(",")
333 if len(GuidValueList
) != 11:
334 EdkLogger
.error("GenDepex", PARSER_ERROR
, "Invalid GUID value string or opcode: %s" % Guid
)
335 return pack("1I2H8B", *(int(value
, 16) for value
in GuidValueList
))
337 ## Save the binary form of dependency expression in file
339 # @param File The path of file. If None is given, put the data on console
341 # @retval True If the file doesn't exist or file is changed
342 # @retval False If file exists and is not changed.
344 def Generate(self
, File
=None):
346 if len(self
.PostfixNotation
) == 0:
349 for Item
in self
.PostfixNotation
:
350 if Item
in self
.Opcode
[self
.Phase
]:
351 Buffer
.write(pack("B", self
.Opcode
[self
.Phase
][Item
]))
352 elif Item
in self
.SupportedOpcode
:
353 EdkLogger
.error("GenDepex", FORMAT_INVALID
,
354 "Opcode [%s] is not expected in %s phase" % (Item
, self
.Phase
),
355 ExtraData
=self
.ExpressionString
)
357 Buffer
.write(self
.GetGuidValue(Item
))
360 FileChangeFlag
= True
362 sys
.stdout
.write(Buffer
.getvalue())
365 FileChangeFlag
= SaveFileOnChange(File
, Buffer
.getvalue(), True)
368 return FileChangeFlag
370 versionNumber
= ("0.04" + " " + gBUILD_VERSION
)
371 __version__
= "%prog Version " + versionNumber
372 __copyright__
= "Copyright (c) 2007-2018, Intel Corporation All rights reserved."
373 __usage__
= "%prog [options] [dependency_expression_file]"
375 ## Parse command line options
377 # @retval OptionParser
380 from optparse
import OptionParser
382 Parser
= OptionParser(description
=__copyright__
, version
=__version__
, usage
=__usage__
)
384 Parser
.add_option("-o", "--output", dest
="OutputFile", default
=None, metavar
="FILE",
385 help="Specify the name of depex file to be generated")
386 Parser
.add_option("-t", "--module-type", dest
="ModuleType", default
=None,
387 help="The type of module for which the dependency expression serves")
388 Parser
.add_option("-e", "--dependency-expression", dest
="Expression", default
="",
389 help="The string of dependency expression. If this option presents, the input file will be ignored.")
390 Parser
.add_option("-m", "--optimize", dest
="Optimize", default
=False, action
="store_true",
391 help="Do some simple optimization on the expression.")
392 Parser
.add_option("-v", "--verbose", dest
="verbose", default
=False, action
="store_true",
393 help="build with verbose information")
394 Parser
.add_option("-d", "--debug", action
="store", type="int", help="Enable debug messages at specified level.")
395 Parser
.add_option("-q", "--quiet", dest
="quiet", default
=False, action
="store_true",
396 help="build with little information")
398 return Parser
.parse_args()
403 # @retval 0 Tool was successful
404 # @retval 1 Tool failed
407 EdkLogger
.Initialize()
408 Option
, Input
= GetOptions()
412 EdkLogger
.SetLevel(EdkLogger
.QUIET
)
414 EdkLogger
.SetLevel(EdkLogger
.VERBOSE
)
415 elif Option
.debug
is not None:
416 EdkLogger
.SetLevel(Option
.debug
+ 1)
418 EdkLogger
.SetLevel(EdkLogger
.INFO
)
421 if Option
.ModuleType
is None or Option
.ModuleType
not in gType2Phase
:
422 EdkLogger
.error("GenDepex", OPTION_MISSING
, "Module type is not specified or supported")
425 if len(Input
) > 0 and Option
.Expression
== "":
427 DxsString
= open(DxsFile
, 'r').read().replace("\n", " ").replace("\r", " ")
428 DxsString
= gStartClosePattern
.sub("\\1", DxsString
)
429 elif Option
.Expression
!= "":
430 if Option
.Expression
[0] == '"':
431 DxsString
= Option
.Expression
[1:-1]
433 DxsString
= Option
.Expression
435 EdkLogger
.error("GenDepex", OPTION_MISSING
, "No expression string or file given")
437 Dpx
= DependencyExpression(DxsString
, Option
.ModuleType
, Option
.Optimize
)
438 if Option
.OutputFile
is not None:
439 FileChangeFlag
= Dpx
.Generate(Option
.OutputFile
)
440 if not FileChangeFlag
and DxsFile
:
442 # Touch the output file if its time stamp is older than the original
443 # DXS file to avoid re-invoke this tool for the dependency check in build rule.
445 if os
.stat(DxsFile
)[8] > os
.stat(Option
.OutputFile
)[8]:
446 os
.utime(Option
.OutputFile
, None)
449 except BaseException
, X
:
451 if Option
is not None and Option
.debug
is not None:
452 EdkLogger
.quiet(traceback
.format_exc())
454 EdkLogger
.quiet(str(X
))
459 if __name__
== '__main__':