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 ValidOpcode
= list(set(self
.OpcodeList
))
275 if len(ValidOpcode
) != 1 or ValidOpcode
[0] 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:
333 EdkLogger
.error("GenDepex", PARSER_ERROR
, "Invalid GUID value string or opcode: %s" % Guid
)
334 return pack("1I2H8B", *(int(value
, 16) for value
in GuidValueList
))
336 ## Save the binary form of dependency expression in file
338 # @param File The path of file. If None is given, put the data on console
340 # @retval True If the file doesn't exist or file is changed
341 # @retval False If file exists and is not changed.
343 def Generate(self
, File
=None):
345 if len(self
.PostfixNotation
) == 0:
348 for Item
in self
.PostfixNotation
:
349 if Item
in self
.Opcode
[self
.Phase
]:
350 Buffer
.write(pack("B", self
.Opcode
[self
.Phase
][Item
]))
351 elif Item
in self
.SupportedOpcode
:
352 EdkLogger
.error("GenDepex", FORMAT_INVALID
,
353 "Opcode [%s] is not expected in %s phase" % (Item
, self
.Phase
),
354 ExtraData
=self
.ExpressionString
)
356 Buffer
.write(self
.GetGuidValue(Item
))
359 FileChangeFlag
= True
361 sys
.stdout
.write(Buffer
.getvalue())
364 FileChangeFlag
= SaveFileOnChange(File
, Buffer
.getvalue(), True)
367 return FileChangeFlag
369 versionNumber
= ("0.04" + " " + gBUILD_VERSION
)
370 __version__
= "%prog Version " + versionNumber
371 __copyright__
= "Copyright (c) 2007-2018, Intel Corporation All rights reserved."
372 __usage__
= "%prog [options] [dependency_expression_file]"
374 ## Parse command line options
376 # @retval OptionParser
379 from optparse
import OptionParser
381 Parser
= OptionParser(description
=__copyright__
, version
=__version__
, usage
=__usage__
)
383 Parser
.add_option("-o", "--output", dest
="OutputFile", default
=None, metavar
="FILE",
384 help="Specify the name of depex file to be generated")
385 Parser
.add_option("-t", "--module-type", dest
="ModuleType", default
=None,
386 help="The type of module for which the dependency expression serves")
387 Parser
.add_option("-e", "--dependency-expression", dest
="Expression", default
="",
388 help="The string of dependency expression. If this option presents, the input file will be ignored.")
389 Parser
.add_option("-m", "--optimize", dest
="Optimize", default
=False, action
="store_true",
390 help="Do some simple optimization on the expression.")
391 Parser
.add_option("-v", "--verbose", dest
="verbose", default
=False, action
="store_true",
392 help="build with verbose information")
393 Parser
.add_option("-d", "--debug", action
="store", type="int", help="Enable debug messages at specified level.")
394 Parser
.add_option("-q", "--quiet", dest
="quiet", default
=False, action
="store_true",
395 help="build with little information")
397 return Parser
.parse_args()
402 # @retval 0 Tool was successful
403 # @retval 1 Tool failed
406 EdkLogger
.Initialize()
407 Option
, Input
= GetOptions()
411 EdkLogger
.SetLevel(EdkLogger
.QUIET
)
413 EdkLogger
.SetLevel(EdkLogger
.VERBOSE
)
414 elif Option
.debug
is not None:
415 EdkLogger
.SetLevel(Option
.debug
+ 1)
417 EdkLogger
.SetLevel(EdkLogger
.INFO
)
420 if Option
.ModuleType
is None or Option
.ModuleType
not in gType2Phase
:
421 EdkLogger
.error("GenDepex", OPTION_MISSING
, "Module type is not specified or supported")
424 if len(Input
) > 0 and Option
.Expression
== "":
426 DxsString
= open(DxsFile
, 'r').read().replace("\n", " ").replace("\r", " ")
427 DxsString
= gStartClosePattern
.sub("\\1", DxsString
)
428 elif Option
.Expression
!= "":
429 if Option
.Expression
[0] == '"':
430 DxsString
= Option
.Expression
[1:-1]
432 DxsString
= Option
.Expression
434 EdkLogger
.error("GenDepex", OPTION_MISSING
, "No expression string or file given")
436 Dpx
= DependencyExpression(DxsString
, Option
.ModuleType
, Option
.Optimize
)
437 if Option
.OutputFile
is not None:
438 FileChangeFlag
= Dpx
.Generate(Option
.OutputFile
)
439 if not FileChangeFlag
and DxsFile
:
441 # Touch the output file if its time stamp is older than the original
442 # DXS file to avoid re-invoke this tool for the dependency check in build rule.
444 if os
.stat(DxsFile
)[8] > os
.stat(Option
.OutputFile
)[8]:
445 os
.utime(Option
.OutputFile
, None)
448 except BaseException
, X
:
450 if Option
is not None and Option
.debug
is not None:
451 EdkLogger
.quiet(traceback
.format_exc())
453 EdkLogger
.quiet(str(X
))
458 if __name__
== '__main__':