2 # This file is used to generate DEPEX file for module's dependency expression
4 # Copyright (c) 2007 - 2014, 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
28 ## Regular expression for matching "DEPENDENCY_START ... DEPENDENCY_END"
29 gStartClosePattern
= re
.compile(".*DEPENDENCY_START(.+)DEPENDENCY_END.*", re
.S
)
31 ## Mapping between module type and EFI phase
39 "DXE_SMM_DRIVER" : "DXE",
40 "DXE_RUNTIME_DRIVER": "DXE",
41 "DXE_SAL_DRIVER" : "DXE",
42 "UEFI_DRIVER" : "DXE",
43 "UEFI_APPLICATION" : "DXE",
45 "MM_STANDALONE" : "MM",
46 "MM_CORE_STANDALONE" : "MM",
49 ## Convert dependency expression string into EFI internal representation
51 # DependencyExpression class is used to parse dependency expression string and
52 # convert it into its binary form.
54 class DependencyExpression
:
57 '665e3ff6-46cc-11d4-9a38-0090273fc14d', # 'gEfiBdsArchProtocolGuid'
58 '26baccb1-6f42-11d4-bce7-0080c73c8881', # 'gEfiCpuArchProtocolGuid'
59 '26baccb2-6f42-11d4-bce7-0080c73c8881', # 'gEfiMetronomeArchProtocolGuid'
60 '1da97072-bddc-4b30-99f1-72a0b56fff2a', # 'gEfiMonotonicCounterArchProtocolGuid'
61 '27cfac87-46cc-11d4-9a38-0090273fc14d', # 'gEfiRealTimeClockArchProtocolGuid'
62 '27cfac88-46cc-11d4-9a38-0090273fc14d', # 'gEfiResetArchProtocolGuid'
63 'b7dfb4e1-052f-449f-87be-9818fc91b733', # 'gEfiRuntimeArchProtocolGuid'
64 'a46423e3-4617-49f1-b9ff-d1bfa9115839', # 'gEfiSecurityArchProtocolGuid'
65 '26baccb3-6f42-11d4-bce7-0080c73c8881', # 'gEfiTimerArchProtocolGuid'
66 '6441f818-6362-4e44-b570-7dba31dd2453', # 'gEfiVariableWriteArchProtocolGuid'
67 '1e5668e2-8481-11d4-bcf1-0080c73c8881', # 'gEfiVariableArchProtocolGuid'
68 '665e3ff5-46cc-11d4-9a38-0090273fc14d' # 'gEfiWatchdogTimerArchProtocolGuid'
119 # all supported op codes and operands
120 SupportedOpcode
= ["BEFORE", "AFTER", "PUSH", "AND", "OR", "NOT", "END", "SOR"]
121 SupportedOperand
= ["TRUE", "FALSE"]
123 OpcodeWithSingleOperand
= ['NOT', 'BEFORE', 'AFTER']
124 OpcodeWithTwoOperand
= ['AND', 'OR']
126 # op code that should not be the last one
127 NonEndingOpcode
= ["AND", "OR", "NOT", 'SOR']
128 # op code must not present at the same time
129 ExclusiveOpcode
= ["BEFORE", "AFTER"]
130 # op code that should be the first one if it presents
131 AboveAllOpcode
= ["SOR", "BEFORE", "AFTER"]
134 # open and close brace must be taken as individual tokens
136 TokenPattern
= re
.compile("(\(|\)|\{[^{}]+\{?[^{}]+\}?[ ]*\}|\w+)")
140 # @param Expression The list or string of dependency expression
141 # @param ModuleType The type of the module using the dependency expression
143 def __init__(self
, Expression
, ModuleType
, Optimize
=False):
144 self
.ModuleType
= ModuleType
145 self
.Phase
= gType2Phase
[ModuleType
]
146 if type(Expression
) == type([]):
147 self
.ExpressionString
= " ".join(Expression
)
148 self
.TokenList
= Expression
150 self
.ExpressionString
= Expression
151 self
.GetExpressionTokenList()
153 self
.PostfixNotation
= []
156 self
.GetPostfixNotation()
157 self
.ValidateOpcode()
159 EdkLogger
.debug(EdkLogger
.DEBUG_8
, repr(self
))
162 EdkLogger
.debug(EdkLogger
.DEBUG_8
, "\n Optimized: " + repr(self
))
165 return " ".join(self
.TokenList
)
169 for Token
in self
.PostfixNotation
:
170 if Token
in self
.SupportedOpcode
:
171 WellForm
+= "\n " + Token
173 WellForm
+= ' ' + Token
176 ## Split the expression string into token list
177 def GetExpressionTokenList(self
):
178 self
.TokenList
= self
.TokenPattern
.findall(self
.ExpressionString
)
180 ## Convert token list into postfix notation
181 def GetPostfixNotation(self
):
184 for Token
in self
.TokenList
:
186 if LastToken
not in self
.SupportedOpcode
+ ['(', '', None]:
187 EdkLogger
.error("GenDepex", PARSER_ERROR
, "Invalid dependency expression: missing operator before open parentheses",
188 ExtraData
="Near %s" % LastToken
)
192 EdkLogger
.error("GenDepex", PARSER_ERROR
, "Invalid dependency expression: mismatched parentheses",
194 elif LastToken
in self
.SupportedOpcode
+ ['', None]:
195 EdkLogger
.error("GenDepex", PARSER_ERROR
, "Invalid dependency expression: missing operand before close parentheses",
196 ExtraData
="Near %s" % LastToken
)
197 while len(Stack
) > 0:
201 self
.PostfixNotation
.append(Stack
.pop())
202 elif Token
in self
.OpcodePriority
:
204 if LastToken
not in self
.SupportedOpcode
+ ['(', '', None]:
205 EdkLogger
.error("GenDepex", PARSER_ERROR
, "Invalid dependency expression: missing operator before NOT",
206 ExtraData
="Near %s" % LastToken
)
207 elif LastToken
in self
.SupportedOpcode
+ ['(', '', None]:
208 EdkLogger
.error("GenDepex", PARSER_ERROR
, "Invalid dependency expression: missing operand before " + Token
,
209 ExtraData
="Near %s" % LastToken
)
211 while len(Stack
) > 0:
212 if Stack
[-1] == "(" or self
.OpcodePriority
[Token
] >= self
.OpcodePriority
[Stack
[-1]]:
214 self
.PostfixNotation
.append(Stack
.pop())
216 self
.OpcodeList
.append(Token
)
218 if Token
not in self
.SupportedOpcode
:
219 # not OP, take it as GUID
220 if LastToken
not in self
.SupportedOpcode
+ ['(', '', None]:
221 EdkLogger
.error("GenDepex", PARSER_ERROR
, "Invalid dependency expression: missing operator before %s" % Token
,
222 ExtraData
="Near %s" % LastToken
)
223 if len(self
.OpcodeList
) == 0 or self
.OpcodeList
[-1] not in self
.ExclusiveOpcode
:
224 if Token
not in self
.SupportedOperand
:
225 self
.PostfixNotation
.append("PUSH")
226 # check if OP is valid in this phase
227 elif Token
in self
.Opcode
[self
.Phase
]:
230 self
.OpcodeList
.append(Token
)
232 EdkLogger
.error("GenDepex", PARSER_ERROR
,
233 "Opcode=%s doesn't supported in %s stage " % (Token
, self
.Phase
),
235 self
.PostfixNotation
.append(Token
)
238 # there should not be parentheses in Stack
239 if '(' in Stack
or ')' in Stack
:
240 EdkLogger
.error("GenDepex", PARSER_ERROR
, "Invalid dependency expression: mismatched parentheses",
242 while len(Stack
) > 0:
243 self
.PostfixNotation
.append(Stack
.pop())
244 if self
.PostfixNotation
[-1] != 'END':
245 self
.PostfixNotation
.append("END")
247 ## Validate the dependency expression
248 def ValidateOpcode(self
):
249 for Op
in self
.AboveAllOpcode
:
250 if Op
in self
.PostfixNotation
:
251 if Op
!= self
.PostfixNotation
[0]:
252 EdkLogger
.error("GenDepex", PARSER_ERROR
, "%s should be the first opcode in the expression" % Op
,
254 if len(self
.PostfixNotation
) < 3:
255 EdkLogger
.error("GenDepex", PARSER_ERROR
, "Missing operand for %s" % Op
,
257 for Op
in self
.ExclusiveOpcode
:
258 if Op
in self
.OpcodeList
:
259 if len(self
.OpcodeList
) > 1:
260 EdkLogger
.error("GenDepex", PARSER_ERROR
, "%s should be the only opcode in the expression" % Op
,
262 if len(self
.PostfixNotation
) < 3:
263 EdkLogger
.error("GenDepex", PARSER_ERROR
, "Missing operand for %s" % Op
,
265 if self
.TokenList
[-1] != 'END' and self
.TokenList
[-1] in self
.NonEndingOpcode
:
266 EdkLogger
.error("GenDepex", PARSER_ERROR
, "Extra %s at the end of the dependency expression" % self
.TokenList
[-1],
268 if self
.TokenList
[-1] == 'END' and self
.TokenList
[-2] in self
.NonEndingOpcode
:
269 EdkLogger
.error("GenDepex", PARSER_ERROR
, "Extra %s at the end of the dependency expression" % self
.TokenList
[-2],
271 if "END" in self
.TokenList
and "END" != self
.TokenList
[-1]:
272 EdkLogger
.error("GenDepex", PARSER_ERROR
, "Extra expressions after END",
275 ## Simply optimize the dependency expression by removing duplicated operands
277 ValidOpcode
= list(set(self
.OpcodeList
))
278 if len(ValidOpcode
) != 1 or ValidOpcode
[0] not in ['AND', 'OR']:
283 for Token
in self
.PostfixNotation
:
284 if Token
in self
.SupportedOpcode
or Token
in NewOperand
:
286 AllOperand
.add(Token
)
291 NewOperand
.append(Token
)
293 elif Token
== 'FALSE':
297 NewOperand
.append(Token
)
299 NewOperand
.append(Token
)
301 # don't generate depex if only TRUE operand left
302 if self
.ModuleType
== 'PEIM' and len(NewOperand
) == 1 and NewOperand
[0] == 'TRUE':
303 self
.PostfixNotation
= []
306 # don't generate depex if all operands are architecture protocols
307 if self
.ModuleType
in ['UEFI_DRIVER', 'DXE_DRIVER', 'DXE_RUNTIME_DRIVER', 'DXE_SAL_DRIVER', 'DXE_SMM_DRIVER', 'MM_STANDALONE'] and \
309 self
.ArchProtocols
== set([GuidStructureStringToGuidString(Guid
) for Guid
in AllOperand
]):
310 self
.PostfixNotation
= []
313 if len(NewOperand
) == 0:
314 self
.TokenList
= list(AllOperand
)
318 self
.TokenList
.append(NewOperand
.pop(0))
321 self
.TokenList
.append(Op
)
322 self
.PostfixNotation
= []
323 self
.GetPostfixNotation()
326 ## Convert a GUID value in C structure format into its binary form
328 # @param Guid The GUID value in C structure format
330 # @retval array The byte array representing the GUID value
332 def GetGuidValue(self
, Guid
):
333 GuidValueString
= Guid
.replace("{", "").replace("}", "").replace(" ", "")
334 GuidValueList
= GuidValueString
.split(",")
335 if len(GuidValueList
) != 11:
336 EdkLogger
.error("GenDepex", PARSER_ERROR
, "Invalid GUID value string or opcode: %s" % Guid
)
337 return pack("1I2H8B", *(int(value
, 16) for value
in GuidValueList
))
339 ## Save the binary form of dependency expression in file
341 # @param File The path of file. If None is given, put the data on console
343 # @retval True If the file doesn't exist or file is changed
344 # @retval False If file exists and is not changed.
346 def Generate(self
, File
=None):
348 if len(self
.PostfixNotation
) == 0:
351 for Item
in self
.PostfixNotation
:
352 if Item
in self
.Opcode
[self
.Phase
]:
353 Buffer
.write(pack("B", self
.Opcode
[self
.Phase
][Item
]))
354 elif Item
in self
.SupportedOpcode
:
355 EdkLogger
.error("GenDepex", FORMAT_INVALID
,
356 "Opcode [%s] is not expected in %s phase" % (Item
, self
.Phase
),
357 ExtraData
=self
.ExpressionString
)
359 Buffer
.write(self
.GetGuidValue(Item
))
362 FileChangeFlag
= True
364 sys
.stdout
.write(Buffer
.getvalue())
367 FileChangeFlag
= SaveFileOnChange(File
, Buffer
.getvalue(), True)
370 return FileChangeFlag
372 versionNumber
= ("0.04" + " " + gBUILD_VERSION
)
373 __version__
= "%prog Version " + versionNumber
374 __copyright__
= "Copyright (c) 2007-2010, Intel Corporation All rights reserved."
375 __usage__
= "%prog [options] [dependency_expression_file]"
377 ## Parse command line options
379 # @retval OptionParser
382 from optparse
import OptionParser
384 Parser
= OptionParser(description
=__copyright__
, version
=__version__
, usage
=__usage__
)
386 Parser
.add_option("-o", "--output", dest
="OutputFile", default
=None, metavar
="FILE",
387 help="Specify the name of depex file to be generated")
388 Parser
.add_option("-t", "--module-type", dest
="ModuleType", default
=None,
389 help="The type of module for which the dependency expression serves")
390 Parser
.add_option("-e", "--dependency-expression", dest
="Expression", default
="",
391 help="The string of dependency expression. If this option presents, the input file will be ignored.")
392 Parser
.add_option("-m", "--optimize", dest
="Optimize", default
=False, action
="store_true",
393 help="Do some simple optimization on the expression.")
394 Parser
.add_option("-v", "--verbose", dest
="verbose", default
=False, action
="store_true",
395 help="build with verbose information")
396 Parser
.add_option("-d", "--debug", action
="store", type="int", help="Enable debug messages at specified level.")
397 Parser
.add_option("-q", "--quiet", dest
="quiet", default
=False, action
="store_true",
398 help="build with little information")
400 return Parser
.parse_args()
405 # @retval 0 Tool was successful
406 # @retval 1 Tool failed
409 EdkLogger
.Initialize()
410 Option
, Input
= GetOptions()
414 EdkLogger
.SetLevel(EdkLogger
.QUIET
)
416 EdkLogger
.SetLevel(EdkLogger
.VERBOSE
)
417 elif Option
.debug
!= None:
418 EdkLogger
.SetLevel(Option
.debug
+ 1)
420 EdkLogger
.SetLevel(EdkLogger
.INFO
)
423 if Option
.ModuleType
== None or Option
.ModuleType
not in gType2Phase
:
424 EdkLogger
.error("GenDepex", OPTION_MISSING
, "Module type is not specified or supported")
427 if len(Input
) > 0 and Option
.Expression
== "":
429 DxsString
= open(DxsFile
, 'r').read().replace("\n", " ").replace("\r", " ")
430 DxsString
= gStartClosePattern
.sub("\\1", DxsString
)
431 elif Option
.Expression
!= "":
432 if Option
.Expression
[0] == '"':
433 DxsString
= Option
.Expression
[1:-1]
435 DxsString
= Option
.Expression
437 EdkLogger
.error("GenDepex", OPTION_MISSING
, "No expression string or file given")
439 Dpx
= DependencyExpression(DxsString
, Option
.ModuleType
, Option
.Optimize
)
440 if Option
.OutputFile
!= None:
441 FileChangeFlag
= Dpx
.Generate(Option
.OutputFile
)
442 if not FileChangeFlag
and DxsFile
:
444 # Touch the output file if its time stamp is older than the original
445 # DXS file to avoid re-invoke this tool for the dependency check in build rule.
447 if os
.stat(DxsFile
)[8] > os
.stat(Option
.OutputFile
)[8]:
448 os
.utime(Option
.OutputFile
, None)
451 except BaseException
, X
:
453 if Option
!= None and Option
.debug
!= None:
454 EdkLogger
.quiet(traceback
.format_exc())
456 EdkLogger
.quiet(str(X
))
461 if __name__
== '__main__':