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",
47 ## Convert dependency expression string into EFI internal representation
49 # DependencyExpression class is used to parse dependency expression string and
50 # convert it into its binary form.
52 class DependencyExpression
:
55 '665e3ff6-46cc-11d4-9a38-0090273fc14d', # 'gEfiBdsArchProtocolGuid'
56 '26baccb1-6f42-11d4-bce7-0080c73c8881', # 'gEfiCpuArchProtocolGuid'
57 '26baccb2-6f42-11d4-bce7-0080c73c8881', # 'gEfiMetronomeArchProtocolGuid'
58 '1da97072-bddc-4b30-99f1-72a0b56fff2a', # 'gEfiMonotonicCounterArchProtocolGuid'
59 '27cfac87-46cc-11d4-9a38-0090273fc14d', # 'gEfiRealTimeClockArchProtocolGuid'
60 '27cfac88-46cc-11d4-9a38-0090273fc14d', # 'gEfiResetArchProtocolGuid'
61 'b7dfb4e1-052f-449f-87be-9818fc91b733', # 'gEfiRuntimeArchProtocolGuid'
62 'a46423e3-4617-49f1-b9ff-d1bfa9115839', # 'gEfiSecurityArchProtocolGuid'
63 '26baccb3-6f42-11d4-bce7-0080c73c8881', # 'gEfiTimerArchProtocolGuid'
64 '6441f818-6362-4e44-b570-7dba31dd2453', # 'gEfiVariableWriteArchProtocolGuid'
65 '1e5668e2-8481-11d4-bcf1-0080c73c8881', # 'gEfiVariableArchProtocolGuid'
66 '665e3ff5-46cc-11d4-9a38-0090273fc14d' # 'gEfiWatchdogTimerArchProtocolGuid'
104 # all supported op codes and operands
105 SupportedOpcode
= ["BEFORE", "AFTER", "PUSH", "AND", "OR", "NOT", "END", "SOR"]
106 SupportedOperand
= ["TRUE", "FALSE"]
108 OpcodeWithSingleOperand
= ['NOT', 'BEFORE', 'AFTER']
109 OpcodeWithTwoOperand
= ['AND', 'OR']
111 # op code that should not be the last one
112 NonEndingOpcode
= ["AND", "OR", "NOT", 'SOR']
113 # op code must not present at the same time
114 ExclusiveOpcode
= ["BEFORE", "AFTER"]
115 # op code that should be the first one if it presents
116 AboveAllOpcode
= ["SOR", "BEFORE", "AFTER"]
119 # open and close brace must be taken as individual tokens
121 TokenPattern
= re
.compile("(\(|\)|\{[^{}]+\{?[^{}]+\}?[ ]*\}|\w+)")
125 # @param Expression The list or string of dependency expression
126 # @param ModuleType The type of the module using the dependency expression
128 def __init__(self
, Expression
, ModuleType
, Optimize
=False):
129 self
.ModuleType
= ModuleType
130 self
.Phase
= gType2Phase
[ModuleType
]
131 if type(Expression
) == type([]):
132 self
.ExpressionString
= " ".join(Expression
)
133 self
.TokenList
= Expression
135 self
.ExpressionString
= Expression
136 self
.GetExpressionTokenList()
138 self
.PostfixNotation
= []
141 self
.GetPostfixNotation()
142 self
.ValidateOpcode()
144 EdkLogger
.debug(EdkLogger
.DEBUG_8
, repr(self
))
147 EdkLogger
.debug(EdkLogger
.DEBUG_8
, "\n Optimized: " + repr(self
))
150 return " ".join(self
.TokenList
)
154 for Token
in self
.PostfixNotation
:
155 if Token
in self
.SupportedOpcode
:
156 WellForm
+= "\n " + Token
158 WellForm
+= ' ' + Token
161 ## Split the expression string into token list
162 def GetExpressionTokenList(self
):
163 self
.TokenList
= self
.TokenPattern
.findall(self
.ExpressionString
)
165 ## Convert token list into postfix notation
166 def GetPostfixNotation(self
):
169 for Token
in self
.TokenList
:
171 if LastToken
not in self
.SupportedOpcode
+ ['(', '', None]:
172 EdkLogger
.error("GenDepex", PARSER_ERROR
, "Invalid dependency expression: missing operator before open parentheses",
173 ExtraData
="Near %s" % LastToken
)
177 EdkLogger
.error("GenDepex", PARSER_ERROR
, "Invalid dependency expression: mismatched parentheses",
179 elif LastToken
in self
.SupportedOpcode
+ ['', None]:
180 EdkLogger
.error("GenDepex", PARSER_ERROR
, "Invalid dependency expression: missing operand before close parentheses",
181 ExtraData
="Near %s" % LastToken
)
182 while len(Stack
) > 0:
186 self
.PostfixNotation
.append(Stack
.pop())
187 elif Token
in self
.OpcodePriority
:
189 if LastToken
not in self
.SupportedOpcode
+ ['(', '', None]:
190 EdkLogger
.error("GenDepex", PARSER_ERROR
, "Invalid dependency expression: missing operator before NOT",
191 ExtraData
="Near %s" % LastToken
)
192 elif LastToken
in self
.SupportedOpcode
+ ['(', '', None]:
193 EdkLogger
.error("GenDepex", PARSER_ERROR
, "Invalid dependency expression: missing operand before " + Token
,
194 ExtraData
="Near %s" % LastToken
)
196 while len(Stack
) > 0:
197 if Stack
[-1] == "(" or self
.OpcodePriority
[Token
] >= self
.OpcodePriority
[Stack
[-1]]:
199 self
.PostfixNotation
.append(Stack
.pop())
201 self
.OpcodeList
.append(Token
)
203 if Token
not in self
.SupportedOpcode
:
204 # not OP, take it as GUID
205 if LastToken
not in self
.SupportedOpcode
+ ['(', '', None]:
206 EdkLogger
.error("GenDepex", PARSER_ERROR
, "Invalid dependency expression: missing operator before %s" % Token
,
207 ExtraData
="Near %s" % LastToken
)
208 if len(self
.OpcodeList
) == 0 or self
.OpcodeList
[-1] not in self
.ExclusiveOpcode
:
209 if Token
not in self
.SupportedOperand
:
210 self
.PostfixNotation
.append("PUSH")
211 # check if OP is valid in this phase
212 elif Token
in self
.Opcode
[self
.Phase
]:
215 self
.OpcodeList
.append(Token
)
217 EdkLogger
.error("GenDepex", PARSER_ERROR
,
218 "Opcode=%s doesn't supported in %s stage " % (Token
, self
.Phase
),
220 self
.PostfixNotation
.append(Token
)
223 # there should not be parentheses in Stack
224 if '(' in Stack
or ')' in Stack
:
225 EdkLogger
.error("GenDepex", PARSER_ERROR
, "Invalid dependency expression: mismatched parentheses",
227 while len(Stack
) > 0:
228 self
.PostfixNotation
.append(Stack
.pop())
229 if self
.PostfixNotation
[-1] != 'END':
230 self
.PostfixNotation
.append("END")
232 ## Validate the dependency expression
233 def ValidateOpcode(self
):
234 for Op
in self
.AboveAllOpcode
:
235 if Op
in self
.PostfixNotation
:
236 if Op
!= self
.PostfixNotation
[0]:
237 EdkLogger
.error("GenDepex", PARSER_ERROR
, "%s should be the first opcode in the expression" % Op
,
239 if len(self
.PostfixNotation
) < 3:
240 EdkLogger
.error("GenDepex", PARSER_ERROR
, "Missing operand for %s" % Op
,
242 for Op
in self
.ExclusiveOpcode
:
243 if Op
in self
.OpcodeList
:
244 if len(self
.OpcodeList
) > 1:
245 EdkLogger
.error("GenDepex", PARSER_ERROR
, "%s should be the only opcode in the expression" % Op
,
247 if len(self
.PostfixNotation
) < 3:
248 EdkLogger
.error("GenDepex", PARSER_ERROR
, "Missing operand for %s" % Op
,
250 if self
.TokenList
[-1] != 'END' and self
.TokenList
[-1] in self
.NonEndingOpcode
:
251 EdkLogger
.error("GenDepex", PARSER_ERROR
, "Extra %s at the end of the dependency expression" % self
.TokenList
[-1],
253 if self
.TokenList
[-1] == 'END' and self
.TokenList
[-2] in self
.NonEndingOpcode
:
254 EdkLogger
.error("GenDepex", PARSER_ERROR
, "Extra %s at the end of the dependency expression" % self
.TokenList
[-2],
256 if "END" in self
.TokenList
and "END" != self
.TokenList
[-1]:
257 EdkLogger
.error("GenDepex", PARSER_ERROR
, "Extra expressions after END",
260 ## Simply optimize the dependency expression by removing duplicated operands
262 ValidOpcode
= list(set(self
.OpcodeList
))
263 if len(ValidOpcode
) != 1 or ValidOpcode
[0] not in ['AND', 'OR']:
268 for Token
in self
.PostfixNotation
:
269 if Token
in self
.SupportedOpcode
or Token
in NewOperand
:
271 AllOperand
.add(Token
)
276 NewOperand
.append(Token
)
278 elif Token
== 'FALSE':
282 NewOperand
.append(Token
)
284 NewOperand
.append(Token
)
286 # don't generate depex if only TRUE operand left
287 if self
.ModuleType
== 'PEIM' and len(NewOperand
) == 1 and NewOperand
[0] == 'TRUE':
288 self
.PostfixNotation
= []
291 # don't generate depex if all operands are architecture protocols
292 if self
.ModuleType
in ['UEFI_DRIVER', 'DXE_DRIVER', 'DXE_RUNTIME_DRIVER', 'DXE_SAL_DRIVER', 'DXE_SMM_DRIVER'] and \
294 self
.ArchProtocols
== set([GuidStructureStringToGuidString(Guid
) for Guid
in AllOperand
]):
295 self
.PostfixNotation
= []
298 if len(NewOperand
) == 0:
299 self
.TokenList
= list(AllOperand
)
303 self
.TokenList
.append(NewOperand
.pop(0))
306 self
.TokenList
.append(Op
)
307 self
.PostfixNotation
= []
308 self
.GetPostfixNotation()
311 ## Convert a GUID value in C structure format into its binary form
313 # @param Guid The GUID value in C structure format
315 # @retval array The byte array representing the GUID value
317 def GetGuidValue(self
, Guid
):
318 GuidValueString
= Guid
.replace("{", "").replace("}", "").replace(" ", "")
319 GuidValueList
= GuidValueString
.split(",")
320 if len(GuidValueList
) != 11:
321 EdkLogger
.error("GenDepex", PARSER_ERROR
, "Invalid GUID value string or opcode: %s" % Guid
)
322 return pack("1I2H8B", *(int(value
, 16) for value
in GuidValueList
))
324 ## Save the binary form of dependency expression in file
326 # @param File The path of file. If None is given, put the data on console
328 # @retval True If the file doesn't exist or file is changed
329 # @retval False If file exists and is not changed.
331 def Generate(self
, File
=None):
333 if len(self
.PostfixNotation
) == 0:
336 for Item
in self
.PostfixNotation
:
337 if Item
in self
.Opcode
[self
.Phase
]:
338 Buffer
.write(pack("B", self
.Opcode
[self
.Phase
][Item
]))
339 elif Item
in self
.SupportedOpcode
:
340 EdkLogger
.error("GenDepex", FORMAT_INVALID
,
341 "Opcode [%s] is not expected in %s phase" % (Item
, self
.Phase
),
342 ExtraData
=self
.ExpressionString
)
344 Buffer
.write(self
.GetGuidValue(Item
))
347 FileChangeFlag
= True
349 sys
.stdout
.write(Buffer
.getvalue())
352 FileChangeFlag
= SaveFileOnChange(File
, Buffer
.getvalue(), True)
355 return FileChangeFlag
357 versionNumber
= ("0.04" + " " + gBUILD_VERSION
)
358 __version__
= "%prog Version " + versionNumber
359 __copyright__
= "Copyright (c) 2007-2010, Intel Corporation All rights reserved."
360 __usage__
= "%prog [options] [dependency_expression_file]"
362 ## Parse command line options
364 # @retval OptionParser
367 from optparse
import OptionParser
369 Parser
= OptionParser(description
=__copyright__
, version
=__version__
, usage
=__usage__
)
371 Parser
.add_option("-o", "--output", dest
="OutputFile", default
=None, metavar
="FILE",
372 help="Specify the name of depex file to be generated")
373 Parser
.add_option("-t", "--module-type", dest
="ModuleType", default
=None,
374 help="The type of module for which the dependency expression serves")
375 Parser
.add_option("-e", "--dependency-expression", dest
="Expression", default
="",
376 help="The string of dependency expression. If this option presents, the input file will be ignored.")
377 Parser
.add_option("-m", "--optimize", dest
="Optimize", default
=False, action
="store_true",
378 help="Do some simple optimization on the expression.")
379 Parser
.add_option("-v", "--verbose", dest
="verbose", default
=False, action
="store_true",
380 help="build with verbose information")
381 Parser
.add_option("-d", "--debug", action
="store", type="int", help="Enable debug messages at specified level.")
382 Parser
.add_option("-q", "--quiet", dest
="quiet", default
=False, action
="store_true",
383 help="build with little information")
385 return Parser
.parse_args()
390 # @retval 0 Tool was successful
391 # @retval 1 Tool failed
394 EdkLogger
.Initialize()
395 Option
, Input
= GetOptions()
399 EdkLogger
.SetLevel(EdkLogger
.QUIET
)
401 EdkLogger
.SetLevel(EdkLogger
.VERBOSE
)
402 elif Option
.debug
!= None:
403 EdkLogger
.SetLevel(Option
.debug
+ 1)
405 EdkLogger
.SetLevel(EdkLogger
.INFO
)
408 if Option
.ModuleType
== None or Option
.ModuleType
not in gType2Phase
:
409 EdkLogger
.error("GenDepex", OPTION_MISSING
, "Module type is not specified or supported")
412 if len(Input
) > 0 and Option
.Expression
== "":
414 DxsString
= open(DxsFile
, 'r').read().replace("\n", " ").replace("\r", " ")
415 DxsString
= gStartClosePattern
.sub("\\1", DxsString
)
416 elif Option
.Expression
!= "":
417 if Option
.Expression
[0] == '"':
418 DxsString
= Option
.Expression
[1:-1]
420 DxsString
= Option
.Expression
422 EdkLogger
.error("GenDepex", OPTION_MISSING
, "No expression string or file given")
424 Dpx
= DependencyExpression(DxsString
, Option
.ModuleType
, Option
.Optimize
)
425 if Option
.OutputFile
!= None:
426 FileChangeFlag
= Dpx
.Generate(Option
.OutputFile
)
427 if not FileChangeFlag
and DxsFile
:
429 # Touch the output file if its time stamp is older than the original
430 # DXS file to avoid re-invoke this tool for the dependency check in build rule.
432 if os
.stat(DxsFile
)[8] > os
.stat(Option
.OutputFile
)[8]:
433 os
.utime(Option
.OutputFile
, None)
436 except BaseException
, X
:
438 if Option
!= None and Option
.debug
!= None:
439 EdkLogger
.quiet(traceback
.format_exc())
441 EdkLogger
.quiet(str(X
))
446 if __name__
== '__main__':