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 io
import BytesIO
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
.Misc
import GuidStructureByteArrayToGuidString
26 from Common
.Misc
import GuidStringToGuidStructureString
27 from Common
import EdkLogger
as EdkLogger
28 from Common
.BuildVersion
import gBUILD_VERSION
29 from Common
.DataType
import *
31 ## Regular expression for matching "DEPENDENCY_START ... DEPENDENCY_END"
32 gStartClosePattern
= re
.compile(".*DEPENDENCY_START(.+)DEPENDENCY_END.*", re
.S
)
34 ## Mapping between module type and EFI phase
36 SUP_MODULE_BASE
: None,
37 SUP_MODULE_SEC
: "PEI",
38 SUP_MODULE_PEI_CORE
: "PEI",
39 SUP_MODULE_PEIM
: "PEI",
40 SUP_MODULE_DXE_CORE
: "DXE",
41 SUP_MODULE_DXE_DRIVER
: "DXE",
42 SUP_MODULE_DXE_SMM_DRIVER
: "DXE",
43 SUP_MODULE_DXE_RUNTIME_DRIVER
: "DXE",
44 SUP_MODULE_DXE_SAL_DRIVER
: "DXE",
45 SUP_MODULE_UEFI_DRIVER
: "DXE",
46 SUP_MODULE_UEFI_APPLICATION
: "DXE",
47 SUP_MODULE_SMM_CORE
: "DXE",
48 SUP_MODULE_MM_STANDALONE
: "MM",
49 SUP_MODULE_MM_CORE_STANDALONE
: "MM",
52 ## Convert dependency expression string into EFI internal representation
54 # DependencyExpression class is used to parse dependency expression string and
55 # convert it into its binary form.
57 class DependencyExpression
:
60 '665e3ff6-46cc-11d4-9a38-0090273fc14d', # 'gEfiBdsArchProtocolGuid'
61 '26baccb1-6f42-11d4-bce7-0080c73c8881', # 'gEfiCpuArchProtocolGuid'
62 '26baccb2-6f42-11d4-bce7-0080c73c8881', # 'gEfiMetronomeArchProtocolGuid'
63 '1da97072-bddc-4b30-99f1-72a0b56fff2a', # 'gEfiMonotonicCounterArchProtocolGuid'
64 '27cfac87-46cc-11d4-9a38-0090273fc14d', # 'gEfiRealTimeClockArchProtocolGuid'
65 '27cfac88-46cc-11d4-9a38-0090273fc14d', # 'gEfiResetArchProtocolGuid'
66 'b7dfb4e1-052f-449f-87be-9818fc91b733', # 'gEfiRuntimeArchProtocolGuid'
67 'a46423e3-4617-49f1-b9ff-d1bfa9115839', # 'gEfiSecurityArchProtocolGuid'
68 '26baccb3-6f42-11d4-bce7-0080c73c8881', # 'gEfiTimerArchProtocolGuid'
69 '6441f818-6362-4e44-b570-7dba31dd2453', # 'gEfiVariableWriteArchProtocolGuid'
70 '1e5668e2-8481-11d4-bcf1-0080c73c8881', # 'gEfiVariableArchProtocolGuid'
71 '665e3ff5-46cc-11d4-9a38-0090273fc14d' # 'gEfiWatchdogTimerArchProtocolGuid'
82 DEPEX_OPCODE_PUSH
: 0x02,
83 DEPEX_OPCODE_AND
: 0x03,
84 DEPEX_OPCODE_OR
: 0x04,
85 DEPEX_OPCODE_NOT
: 0x05,
86 DEPEX_OPCODE_TRUE
: 0x06,
87 DEPEX_OPCODE_FALSE
: 0x07,
88 DEPEX_OPCODE_END
: 0x08
92 DEPEX_OPCODE_BEFORE
: 0x00,
93 DEPEX_OPCODE_AFTER
: 0x01,
94 DEPEX_OPCODE_PUSH
: 0x02,
95 DEPEX_OPCODE_AND
: 0x03,
96 DEPEX_OPCODE_OR
: 0x04,
97 DEPEX_OPCODE_NOT
: 0x05,
98 DEPEX_OPCODE_TRUE
: 0x06,
99 DEPEX_OPCODE_FALSE
: 0x07,
100 DEPEX_OPCODE_END
: 0x08,
101 DEPEX_OPCODE_SOR
: 0x09
105 DEPEX_OPCODE_BEFORE
: 0x00,
106 DEPEX_OPCODE_AFTER
: 0x01,
107 DEPEX_OPCODE_PUSH
: 0x02,
108 DEPEX_OPCODE_AND
: 0x03,
109 DEPEX_OPCODE_OR
: 0x04,
110 DEPEX_OPCODE_NOT
: 0x05,
111 DEPEX_OPCODE_TRUE
: 0x06,
112 DEPEX_OPCODE_FALSE
: 0x07,
113 DEPEX_OPCODE_END
: 0x08,
114 DEPEX_OPCODE_SOR
: 0x09
118 # all supported op codes and operands
119 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
]
120 SupportedOperand
= [DEPEX_OPCODE_TRUE
, DEPEX_OPCODE_FALSE
]
122 OpcodeWithSingleOperand
= [DEPEX_OPCODE_NOT
, DEPEX_OPCODE_BEFORE
, DEPEX_OPCODE_AFTER
]
123 OpcodeWithTwoOperand
= [DEPEX_OPCODE_AND
, DEPEX_OPCODE_OR
]
125 # op code that should not be the last one
126 NonEndingOpcode
= [DEPEX_OPCODE_AND
, DEPEX_OPCODE_OR
, DEPEX_OPCODE_NOT
, DEPEX_OPCODE_SOR
]
127 # op code must not present at the same time
128 ExclusiveOpcode
= [DEPEX_OPCODE_BEFORE
, DEPEX_OPCODE_AFTER
]
129 # op code that should be the first one if it presents
130 AboveAllOpcode
= [DEPEX_OPCODE_SOR
, DEPEX_OPCODE_BEFORE
, DEPEX_OPCODE_AFTER
]
133 # open and close brace must be taken as individual tokens
135 TokenPattern
= re
.compile("(\(|\)|\{[^{}]+\{?[^{}]+\}?[ ]*\}|\w+)")
139 # @param Expression The list or string of dependency expression
140 # @param ModuleType The type of the module using the dependency expression
142 def __init__(self
, Expression
, ModuleType
, Optimize
=False):
143 self
.ModuleType
= ModuleType
144 self
.Phase
= gType2Phase
[ModuleType
]
145 if isinstance(Expression
, type([])):
146 self
.ExpressionString
= " ".join(Expression
)
147 self
.TokenList
= Expression
149 self
.ExpressionString
= Expression
150 self
.GetExpressionTokenList()
152 self
.PostfixNotation
= []
155 self
.GetPostfixNotation()
156 self
.ValidateOpcode()
158 EdkLogger
.debug(EdkLogger
.DEBUG_8
, repr(self
))
161 EdkLogger
.debug(EdkLogger
.DEBUG_8
, "\n Optimized: " + repr(self
))
164 return " ".join(self
.TokenList
)
168 for Token
in self
.PostfixNotation
:
169 if Token
in self
.SupportedOpcode
:
170 WellForm
+= "\n " + Token
172 WellForm
+= ' ' + Token
175 ## Split the expression string into token list
176 def GetExpressionTokenList(self
):
177 self
.TokenList
= self
.TokenPattern
.findall(self
.ExpressionString
)
179 ## Convert token list into postfix notation
180 def GetPostfixNotation(self
):
183 for Token
in self
.TokenList
:
185 if LastToken
not in self
.SupportedOpcode
+ ['(', '', None]:
186 EdkLogger
.error("GenDepex", PARSER_ERROR
, "Invalid dependency expression: missing operator before open parentheses",
187 ExtraData
="Near %s" % LastToken
)
191 EdkLogger
.error("GenDepex", PARSER_ERROR
, "Invalid dependency expression: mismatched parentheses",
193 elif LastToken
in self
.SupportedOpcode
+ ['', None]:
194 EdkLogger
.error("GenDepex", PARSER_ERROR
, "Invalid dependency expression: missing operand before close parentheses",
195 ExtraData
="Near %s" % LastToken
)
196 while len(Stack
) > 0:
200 self
.PostfixNotation
.append(Stack
.pop())
201 elif Token
in self
.OpcodePriority
:
202 if Token
== DEPEX_OPCODE_NOT
:
203 if LastToken
not in self
.SupportedOpcode
+ ['(', '', None]:
204 EdkLogger
.error("GenDepex", PARSER_ERROR
, "Invalid dependency expression: missing operator before NOT",
205 ExtraData
="Near %s" % LastToken
)
206 elif LastToken
in self
.SupportedOpcode
+ ['(', '', None]:
207 EdkLogger
.error("GenDepex", PARSER_ERROR
, "Invalid dependency expression: missing operand before " + Token
,
208 ExtraData
="Near %s" % LastToken
)
210 while len(Stack
) > 0:
211 if Stack
[-1] == "(" or self
.OpcodePriority
[Token
] >= self
.OpcodePriority
[Stack
[-1]]:
213 self
.PostfixNotation
.append(Stack
.pop())
215 self
.OpcodeList
.append(Token
)
217 if Token
not in self
.SupportedOpcode
:
218 # not OP, take it as GUID
219 if LastToken
not in self
.SupportedOpcode
+ ['(', '', None]:
220 EdkLogger
.error("GenDepex", PARSER_ERROR
, "Invalid dependency expression: missing operator before %s" % Token
,
221 ExtraData
="Near %s" % LastToken
)
222 if len(self
.OpcodeList
) == 0 or self
.OpcodeList
[-1] not in self
.ExclusiveOpcode
:
223 if Token
not in self
.SupportedOperand
:
224 self
.PostfixNotation
.append(DEPEX_OPCODE_PUSH
)
225 # check if OP is valid in this phase
226 elif Token
in self
.Opcode
[self
.Phase
]:
227 if Token
== DEPEX_OPCODE_END
:
229 self
.OpcodeList
.append(Token
)
231 EdkLogger
.error("GenDepex", PARSER_ERROR
,
232 "Opcode=%s doesn't supported in %s stage " % (Token
, self
.Phase
),
234 self
.PostfixNotation
.append(Token
)
237 # there should not be parentheses in Stack
238 if '(' in Stack
or ')' in Stack
:
239 EdkLogger
.error("GenDepex", PARSER_ERROR
, "Invalid dependency expression: mismatched parentheses",
241 while len(Stack
) > 0:
242 self
.PostfixNotation
.append(Stack
.pop())
243 if self
.PostfixNotation
[-1] != DEPEX_OPCODE_END
:
244 self
.PostfixNotation
.append(DEPEX_OPCODE_END
)
246 ## Validate the dependency expression
247 def ValidateOpcode(self
):
248 for Op
in self
.AboveAllOpcode
:
249 if Op
in self
.PostfixNotation
:
250 if Op
!= self
.PostfixNotation
[0]:
251 EdkLogger
.error("GenDepex", PARSER_ERROR
, "%s should be the first opcode in the expression" % Op
,
253 if len(self
.PostfixNotation
) < 3:
254 EdkLogger
.error("GenDepex", PARSER_ERROR
, "Missing operand for %s" % Op
,
256 for Op
in self
.ExclusiveOpcode
:
257 if Op
in self
.OpcodeList
:
258 if len(self
.OpcodeList
) > 1:
259 EdkLogger
.error("GenDepex", PARSER_ERROR
, "%s should be the only opcode in the expression" % Op
,
261 if len(self
.PostfixNotation
) < 3:
262 EdkLogger
.error("GenDepex", PARSER_ERROR
, "Missing operand for %s" % Op
,
264 if self
.TokenList
[-1] != DEPEX_OPCODE_END
and self
.TokenList
[-1] in self
.NonEndingOpcode
:
265 EdkLogger
.error("GenDepex", PARSER_ERROR
, "Extra %s at the end of the dependency expression" % self
.TokenList
[-1],
267 if self
.TokenList
[-1] == DEPEX_OPCODE_END
and self
.TokenList
[-2] in self
.NonEndingOpcode
:
268 EdkLogger
.error("GenDepex", PARSER_ERROR
, "Extra %s at the end of the dependency expression" % self
.TokenList
[-2],
270 if DEPEX_OPCODE_END
in self
.TokenList
and DEPEX_OPCODE_END
!= self
.TokenList
[-1]:
271 EdkLogger
.error("GenDepex", PARSER_ERROR
, "Extra expressions after END",
274 ## Simply optimize the dependency expression by removing duplicated operands
276 OpcodeSet
= set(self
.OpcodeList
)
277 # if there are isn't one in the set, return
278 if len(OpcodeSet
) != 1:
281 #if Op isn't either OR or AND, return
282 if Op
not in [DEPEX_OPCODE_AND
, DEPEX_OPCODE_OR
]:
286 for Token
in self
.PostfixNotation
:
287 if Token
in self
.SupportedOpcode
or Token
in NewOperand
:
289 AllOperand
.add(Token
)
290 if Token
== DEPEX_OPCODE_TRUE
:
291 if Op
== DEPEX_OPCODE_AND
:
294 NewOperand
.append(Token
)
296 elif Token
== DEPEX_OPCODE_FALSE
:
297 if Op
== DEPEX_OPCODE_OR
:
300 NewOperand
.append(Token
)
302 NewOperand
.append(Token
)
304 # don't generate depex if only TRUE operand left
305 if self
.ModuleType
== SUP_MODULE_PEIM
and len(NewOperand
) == 1 and NewOperand
[0] == DEPEX_OPCODE_TRUE
:
306 self
.PostfixNotation
= []
309 # don't generate depex if all operands are architecture protocols
310 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 \
311 Op
== DEPEX_OPCODE_AND
and \
312 self
.ArchProtocols
== set(GuidStructureStringToGuidString(Guid
) for Guid
in AllOperand
):
313 self
.PostfixNotation
= []
316 if len(NewOperand
) == 0:
317 self
.TokenList
= list(AllOperand
)
321 self
.TokenList
.append(NewOperand
.pop(0))
324 self
.TokenList
.append(Op
)
325 self
.PostfixNotation
= []
326 self
.GetPostfixNotation()
329 ## Convert a GUID value in C structure format into its binary form
331 # @param Guid The GUID value in C structure format
333 # @retval array The byte array representing the GUID value
335 def GetGuidValue(self
, Guid
):
336 GuidValueString
= Guid
.replace("{", "").replace("}", "").replace(" ", "")
337 GuidValueList
= GuidValueString
.split(",")
338 if len(GuidValueList
) != 11 and len(GuidValueList
) == 16:
339 GuidValueString
= GuidStringToGuidStructureString(GuidStructureByteArrayToGuidString(Guid
))
340 GuidValueString
= GuidValueString
.replace("{", "").replace("}", "").replace(" ", "")
341 GuidValueList
= GuidValueString
.split(",")
342 if len(GuidValueList
) != 11:
343 EdkLogger
.error("GenDepex", PARSER_ERROR
, "Invalid GUID value string or opcode: %s" % Guid
)
344 return pack("1I2H8B", *(int(value
, 16) for value
in GuidValueList
))
346 ## Save the binary form of dependency expression in file
348 # @param File The path of file. If None is given, put the data on console
350 # @retval True If the file doesn't exist or file is changed
351 # @retval False If file exists and is not changed.
353 def Generate(self
, File
=None):
355 if len(self
.PostfixNotation
) == 0:
358 for Item
in self
.PostfixNotation
:
359 if Item
in self
.Opcode
[self
.Phase
]:
360 Buffer
.write(pack("B", self
.Opcode
[self
.Phase
][Item
]))
361 elif Item
in self
.SupportedOpcode
:
362 EdkLogger
.error("GenDepex", FORMAT_INVALID
,
363 "Opcode [%s] is not expected in %s phase" % (Item
, self
.Phase
),
364 ExtraData
=self
.ExpressionString
)
366 Buffer
.write(self
.GetGuidValue(Item
))
369 FileChangeFlag
= True
371 sys
.stdout
.write(Buffer
.getvalue())
374 FileChangeFlag
= SaveFileOnChange(File
, Buffer
.getvalue(), True)
377 return FileChangeFlag
379 versionNumber
= ("0.04" + " " + gBUILD_VERSION
)
380 __version__
= "%prog Version " + versionNumber
381 __copyright__
= "Copyright (c) 2007-2018, Intel Corporation All rights reserved."
382 __usage__
= "%prog [options] [dependency_expression_file]"
384 ## Parse command line options
386 # @retval OptionParser
389 from optparse
import OptionParser
391 Parser
= OptionParser(description
=__copyright__
, version
=__version__
, usage
=__usage__
)
393 Parser
.add_option("-o", "--output", dest
="OutputFile", default
=None, metavar
="FILE",
394 help="Specify the name of depex file to be generated")
395 Parser
.add_option("-t", "--module-type", dest
="ModuleType", default
=None,
396 help="The type of module for which the dependency expression serves")
397 Parser
.add_option("-e", "--dependency-expression", dest
="Expression", default
="",
398 help="The string of dependency expression. If this option presents, the input file will be ignored.")
399 Parser
.add_option("-m", "--optimize", dest
="Optimize", default
=False, action
="store_true",
400 help="Do some simple optimization on the expression.")
401 Parser
.add_option("-v", "--verbose", dest
="verbose", default
=False, action
="store_true",
402 help="build with verbose information")
403 Parser
.add_option("-d", "--debug", action
="store", type="int", help="Enable debug messages at specified level.")
404 Parser
.add_option("-q", "--quiet", dest
="quiet", default
=False, action
="store_true",
405 help="build with little information")
407 return Parser
.parse_args()
412 # @retval 0 Tool was successful
413 # @retval 1 Tool failed
416 EdkLogger
.Initialize()
417 Option
, Input
= GetOptions()
421 EdkLogger
.SetLevel(EdkLogger
.QUIET
)
423 EdkLogger
.SetLevel(EdkLogger
.VERBOSE
)
424 elif Option
.debug
is not None:
425 EdkLogger
.SetLevel(Option
.debug
+ 1)
427 EdkLogger
.SetLevel(EdkLogger
.INFO
)
430 if Option
.ModuleType
is None or Option
.ModuleType
not in gType2Phase
:
431 EdkLogger
.error("GenDepex", OPTION_MISSING
, "Module type is not specified or supported")
434 if len(Input
) > 0 and Option
.Expression
== "":
436 DxsString
= open(DxsFile
, 'r').read().replace("\n", " ").replace("\r", " ")
437 DxsString
= gStartClosePattern
.sub("\\1", DxsString
)
438 elif Option
.Expression
!= "":
439 if Option
.Expression
[0] == '"':
440 DxsString
= Option
.Expression
[1:-1]
442 DxsString
= Option
.Expression
444 EdkLogger
.error("GenDepex", OPTION_MISSING
, "No expression string or file given")
446 Dpx
= DependencyExpression(DxsString
, Option
.ModuleType
, Option
.Optimize
)
447 if Option
.OutputFile
is not None:
448 FileChangeFlag
= Dpx
.Generate(Option
.OutputFile
)
449 if not FileChangeFlag
and DxsFile
:
451 # Touch the output file if its time stamp is older than the original
452 # DXS file to avoid re-invoke this tool for the dependency check in build rule.
454 if os
.stat(DxsFile
)[8] > os
.stat(Option
.OutputFile
)[8]:
455 os
.utime(Option
.OutputFile
, None)
458 except BaseException
as X
:
460 if Option
is not None and Option
.debug
is not None:
461 EdkLogger
.quiet(traceback
.format_exc())
463 EdkLogger
.quiet(str(X
))
468 if __name__
== '__main__':