]> git.proxmox.com Git - mirror_edk2.git/blame - BaseTools/Source/Python/AutoGen/GenDepex.py
There is a limitation on WINDOWS OS for the length of entire file path can’t be large...
[mirror_edk2.git] / BaseTools / Source / Python / AutoGen / GenDepex.py
CommitLineData
30fdf114
LG
1## @file\r
2# This file is used to generate DEPEX file for module's dependency expression\r
3#\r
1be2ed90 4# Copyright (c) 2007 - 2014, Intel Corporation. All rights reserved.<BR>\r
40d841f6 5# This program and the accompanying materials\r
30fdf114
LG
6# are licensed and made available under the terms and conditions of the BSD License\r
7# which accompanies this distribution. The full text of the license may be found at\r
8# http://opensource.org/licenses/bsd-license.php\r
9#\r
10# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
11# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
12\r
13## Import Modules\r
14#\r
15import sys\r
1be2ed90 16import Common.LongFilePathOs as os\r
30fdf114
LG
17import re\r
18import traceback\r
1be2ed90 19from Common.LongFilePathSupport import OpenLongFilePath as open\r
30fdf114
LG
20from StringIO import StringIO\r
21from struct import pack\r
22from Common.BuildToolError import *\r
23from Common.Misc import SaveFileOnChange\r
24from Common.Misc import GuidStructureStringToGuidString\r
25from Common import EdkLogger as EdkLogger\r
b36d134f 26from Common.BuildVersion import gBUILD_VERSION\r
30fdf114
LG
27\r
28## Regular expression for matching "DEPENDENCY_START ... DEPENDENCY_END"\r
29gStartClosePattern = re.compile(".*DEPENDENCY_START(.+)DEPENDENCY_END.*", re.S)\r
30\r
31## Mapping between module type and EFI phase\r
32gType2Phase = {\r
33 "BASE" : None,\r
34 "SEC" : "PEI",\r
35 "PEI_CORE" : "PEI",\r
36 "PEIM" : "PEI",\r
37 "DXE_CORE" : "DXE",\r
38 "DXE_DRIVER" : "DXE",\r
39 "DXE_SMM_DRIVER" : "DXE",\r
40 "DXE_RUNTIME_DRIVER": "DXE",\r
41 "DXE_SAL_DRIVER" : "DXE",\r
42 "UEFI_DRIVER" : "DXE",\r
43 "UEFI_APPLICATION" : "DXE",\r
b303ea72 44 "SMM_CORE" : "DXE",\r
30fdf114
LG
45}\r
46\r
47## Convert dependency expression string into EFI internal representation\r
48#\r
49# DependencyExpression class is used to parse dependency expression string and\r
50# convert it into its binary form.\r
51#\r
52class DependencyExpression:\r
53\r
54 ArchProtocols = set([\r
55 '665e3ff6-46cc-11d4-9a38-0090273fc14d', # 'gEfiBdsArchProtocolGuid'\r
56 '26baccb1-6f42-11d4-bce7-0080c73c8881', # 'gEfiCpuArchProtocolGuid'\r
57 '26baccb2-6f42-11d4-bce7-0080c73c8881', # 'gEfiMetronomeArchProtocolGuid'\r
58 '1da97072-bddc-4b30-99f1-72a0b56fff2a', # 'gEfiMonotonicCounterArchProtocolGuid'\r
59 '27cfac87-46cc-11d4-9a38-0090273fc14d', # 'gEfiRealTimeClockArchProtocolGuid'\r
60 '27cfac88-46cc-11d4-9a38-0090273fc14d', # 'gEfiResetArchProtocolGuid'\r
61 'b7dfb4e1-052f-449f-87be-9818fc91b733', # 'gEfiRuntimeArchProtocolGuid'\r
62 'a46423e3-4617-49f1-b9ff-d1bfa9115839', # 'gEfiSecurityArchProtocolGuid'\r
63 '26baccb3-6f42-11d4-bce7-0080c73c8881', # 'gEfiTimerArchProtocolGuid'\r
64 '6441f818-6362-4e44-b570-7dba31dd2453', # 'gEfiVariableWriteArchProtocolGuid'\r
65 '1e5668e2-8481-11d4-bcf1-0080c73c8881', # 'gEfiVariableArchProtocolGuid'\r
66 '665e3ff5-46cc-11d4-9a38-0090273fc14d' # 'gEfiWatchdogTimerArchProtocolGuid'\r
67 ]\r
68 )\r
69\r
70 OpcodePriority = {\r
71 "AND" : 1,\r
72 "OR" : 1,\r
73 "NOT" : 2,\r
74 # "SOR" : 9,\r
75 # "BEFORE": 9,\r
76 # "AFTER" : 9,\r
77 }\r
78\r
79 Opcode = {\r
80 "PEI" : {\r
81 "PUSH" : 0x02,\r
82 "AND" : 0x03,\r
83 "OR" : 0x04,\r
84 "NOT" : 0x05,\r
85 "TRUE" : 0x06,\r
86 "FALSE" : 0x07,\r
87 "END" : 0x08\r
88 },\r
89\r
90 "DXE" : {\r
91 "BEFORE": 0x00,\r
92 "AFTER" : 0x01,\r
93 "PUSH" : 0x02,\r
94 "AND" : 0x03,\r
95 "OR" : 0x04,\r
96 "NOT" : 0x05,\r
97 "TRUE" : 0x06,\r
98 "FALSE" : 0x07,\r
99 "END" : 0x08,\r
100 "SOR" : 0x09\r
101 }\r
102 }\r
103\r
104 # all supported op codes and operands\r
105 SupportedOpcode = ["BEFORE", "AFTER", "PUSH", "AND", "OR", "NOT", "END", "SOR"]\r
106 SupportedOperand = ["TRUE", "FALSE"]\r
107\r
108 OpcodeWithSingleOperand = ['NOT', 'BEFORE', 'AFTER']\r
109 OpcodeWithTwoOperand = ['AND', 'OR']\r
110\r
111 # op code that should not be the last one\r
112 NonEndingOpcode = ["AND", "OR", "NOT", 'SOR']\r
113 # op code must not present at the same time\r
114 ExclusiveOpcode = ["BEFORE", "AFTER"]\r
115 # op code that should be the first one if it presents\r
116 AboveAllOpcode = ["SOR", "BEFORE", "AFTER"]\r
117\r
118 #\r
119 # open and close brace must be taken as individual tokens\r
120 #\r
121 TokenPattern = re.compile("(\(|\)|\{[^{}]+\{?[^{}]+\}?[ ]*\}|\w+)")\r
122\r
123 ## Constructor\r
124 #\r
125 # @param Expression The list or string of dependency expression\r
126 # @param ModuleType The type of the module using the dependency expression\r
127 #\r
128 def __init__(self, Expression, ModuleType, Optimize=False):\r
129 self.ModuleType = ModuleType\r
130 self.Phase = gType2Phase[ModuleType]\r
131 if type(Expression) == type([]):\r
132 self.ExpressionString = " ".join(Expression)\r
133 self.TokenList = Expression\r
134 else:\r
135 self.ExpressionString = Expression\r
136 self.GetExpressionTokenList()\r
137\r
138 self.PostfixNotation = []\r
139 self.OpcodeList = []\r
140\r
141 self.GetPostfixNotation()\r
142 self.ValidateOpcode()\r
143\r
144 EdkLogger.debug(EdkLogger.DEBUG_8, repr(self))\r
145 if Optimize:\r
146 self.Optimize()\r
147 EdkLogger.debug(EdkLogger.DEBUG_8, "\n Optimized: " + repr(self))\r
148\r
149 def __str__(self):\r
150 return " ".join(self.TokenList)\r
151\r
152 def __repr__(self):\r
153 WellForm = ''\r
154 for Token in self.PostfixNotation:\r
155 if Token in self.SupportedOpcode:\r
156 WellForm += "\n " + Token\r
157 else:\r
158 WellForm += ' ' + Token\r
159 return WellForm\r
160\r
161 ## Split the expression string into token list\r
162 def GetExpressionTokenList(self):\r
163 self.TokenList = self.TokenPattern.findall(self.ExpressionString)\r
164\r
165 ## Convert token list into postfix notation\r
166 def GetPostfixNotation(self):\r
167 Stack = []\r
168 LastToken = ''\r
169 for Token in self.TokenList:\r
170 if Token == "(":\r
171 if LastToken not in self.SupportedOpcode + ['(', '', None]:\r
172 EdkLogger.error("GenDepex", PARSER_ERROR, "Invalid dependency expression: missing operator before open parentheses",\r
173 ExtraData="Near %s" % LastToken)\r
174 Stack.append(Token)\r
175 elif Token == ")":\r
176 if '(' not in Stack:\r
177 EdkLogger.error("GenDepex", PARSER_ERROR, "Invalid dependency expression: mismatched parentheses",\r
178 ExtraData=str(self))\r
179 elif LastToken in self.SupportedOpcode + ['', None]:\r
180 EdkLogger.error("GenDepex", PARSER_ERROR, "Invalid dependency expression: missing operand before close parentheses",\r
181 ExtraData="Near %s" % LastToken)\r
182 while len(Stack) > 0:\r
183 if Stack[-1] == '(':\r
184 Stack.pop()\r
185 break\r
186 self.PostfixNotation.append(Stack.pop())\r
187 elif Token in self.OpcodePriority:\r
188 if Token == "NOT":\r
189 if LastToken not in self.SupportedOpcode + ['(', '', None]:\r
190 EdkLogger.error("GenDepex", PARSER_ERROR, "Invalid dependency expression: missing operator before NOT",\r
191 ExtraData="Near %s" % LastToken)\r
192 elif LastToken in self.SupportedOpcode + ['(', '', None]:\r
193 EdkLogger.error("GenDepex", PARSER_ERROR, "Invalid dependency expression: missing operand before " + Token,\r
194 ExtraData="Near %s" % LastToken)\r
195\r
196 while len(Stack) > 0:\r
197 if Stack[-1] == "(" or self.OpcodePriority[Token] >= self.OpcodePriority[Stack[-1]]:\r
198 break\r
199 self.PostfixNotation.append(Stack.pop())\r
200 Stack.append(Token)\r
201 self.OpcodeList.append(Token)\r
202 else:\r
203 if Token not in self.SupportedOpcode:\r
204 # not OP, take it as GUID\r
205 if LastToken not in self.SupportedOpcode + ['(', '', None]:\r
206 EdkLogger.error("GenDepex", PARSER_ERROR, "Invalid dependency expression: missing operator before %s" % Token,\r
207 ExtraData="Near %s" % LastToken)\r
208 if len(self.OpcodeList) == 0 or self.OpcodeList[-1] not in self.ExclusiveOpcode:\r
209 if Token not in self.SupportedOperand:\r
210 self.PostfixNotation.append("PUSH")\r
211 # check if OP is valid in this phase\r
212 elif Token in self.Opcode[self.Phase]:\r
213 if Token == "END":\r
214 break\r
215 self.OpcodeList.append(Token)\r
216 else:\r
217 EdkLogger.error("GenDepex", PARSER_ERROR,\r
218 "Opcode=%s doesn't supported in %s stage " % (Token, self.Phase),\r
219 ExtraData=str(self))\r
220 self.PostfixNotation.append(Token)\r
221 LastToken = Token\r
222\r
223 # there should not be parentheses in Stack\r
224 if '(' in Stack or ')' in Stack:\r
225 EdkLogger.error("GenDepex", PARSER_ERROR, "Invalid dependency expression: mismatched parentheses",\r
226 ExtraData=str(self))\r
227 while len(Stack) > 0:\r
228 self.PostfixNotation.append(Stack.pop())\r
229 if self.PostfixNotation[-1] != 'END':\r
230 self.PostfixNotation.append("END")\r
231\r
232 ## Validate the dependency expression\r
233 def ValidateOpcode(self):\r
234 for Op in self.AboveAllOpcode:\r
235 if Op in self.PostfixNotation:\r
236 if Op != self.PostfixNotation[0]:\r
237 EdkLogger.error("GenDepex", PARSER_ERROR, "%s should be the first opcode in the expression" % Op,\r
238 ExtraData=str(self))\r
239 if len(self.PostfixNotation) < 3:\r
240 EdkLogger.error("GenDepex", PARSER_ERROR, "Missing operand for %s" % Op,\r
241 ExtraData=str(self))\r
242 for Op in self.ExclusiveOpcode:\r
243 if Op in self.OpcodeList:\r
244 if len(self.OpcodeList) > 1:\r
245 EdkLogger.error("GenDepex", PARSER_ERROR, "%s should be the only opcode in the expression" % Op,\r
246 ExtraData=str(self))\r
247 if len(self.PostfixNotation) < 3:\r
248 EdkLogger.error("GenDepex", PARSER_ERROR, "Missing operand for %s" % Op,\r
249 ExtraData=str(self))\r
250 if self.TokenList[-1] != 'END' and self.TokenList[-1] in self.NonEndingOpcode:\r
251 EdkLogger.error("GenDepex", PARSER_ERROR, "Extra %s at the end of the dependency expression" % self.TokenList[-1],\r
252 ExtraData=str(self))\r
253 if self.TokenList[-1] == 'END' and self.TokenList[-2] in self.NonEndingOpcode:\r
254 EdkLogger.error("GenDepex", PARSER_ERROR, "Extra %s at the end of the dependency expression" % self.TokenList[-2],\r
255 ExtraData=str(self))\r
256 if "END" in self.TokenList and "END" != self.TokenList[-1]:\r
257 EdkLogger.error("GenDepex", PARSER_ERROR, "Extra expressions after END",\r
258 ExtraData=str(self))\r
259\r
260 ## Simply optimize the dependency expression by removing duplicated operands\r
261 def Optimize(self):\r
262 ValidOpcode = list(set(self.OpcodeList))\r
263 if len(ValidOpcode) != 1 or ValidOpcode[0] not in ['AND', 'OR']:\r
264 return\r
265 Op = ValidOpcode[0]\r
266 NewOperand = []\r
267 AllOperand = set()\r
268 for Token in self.PostfixNotation:\r
269 if Token in self.SupportedOpcode or Token in NewOperand:\r
270 continue\r
271 AllOperand.add(Token)\r
272 if Token == 'TRUE':\r
273 if Op == 'AND':\r
274 continue\r
275 else:\r
276 NewOperand.append(Token)\r
277 break\r
278 elif Token == 'FALSE':\r
279 if Op == 'OR':\r
280 continue\r
281 else:\r
282 NewOperand.append(Token)\r
283 break\r
284 NewOperand.append(Token)\r
285\r
286 # don't generate depex if only TRUE operand left\r
287 if self.ModuleType == 'PEIM' and len(NewOperand) == 1 and NewOperand[0] == 'TRUE':\r
288 self.PostfixNotation = []\r
289 return \r
290\r
291 # don't generate depex if all operands are architecture protocols\r
292 if self.ModuleType in ['UEFI_DRIVER', 'DXE_DRIVER', 'DXE_RUNTIME_DRIVER', 'DXE_SAL_DRIVER', 'DXE_SMM_DRIVER'] and \\r
293 Op == 'AND' and \\r
294 self.ArchProtocols == set([GuidStructureStringToGuidString(Guid) for Guid in AllOperand]):\r
295 self.PostfixNotation = []\r
296 return\r
297\r
298 if len(NewOperand) == 0:\r
299 self.TokenList = list(AllOperand)\r
300 else:\r
301 self.TokenList = []\r
302 while True:\r
303 self.TokenList.append(NewOperand.pop(0))\r
304 if NewOperand == []:\r
305 break\r
306 self.TokenList.append(Op)\r
307 self.PostfixNotation = []\r
308 self.GetPostfixNotation()\r
309\r
310\r
311 ## Convert a GUID value in C structure format into its binary form\r
312 #\r
313 # @param Guid The GUID value in C structure format\r
314 #\r
315 # @retval array The byte array representing the GUID value\r
316 #\r
317 def GetGuidValue(self, Guid):\r
318 GuidValueString = Guid.replace("{", "").replace("}", "").replace(" ", "")\r
319 GuidValueList = GuidValueString.split(",")\r
320 if len(GuidValueList) != 11:\r
321 EdkLogger.error("GenDepex", PARSER_ERROR, "Invalid GUID value string or opcode: %s" % Guid)\r
322 return pack("1I2H8B", *(int(value, 16) for value in GuidValueList))\r
323\r
324 ## Save the binary form of dependency expression in file\r
325 #\r
326 # @param File The path of file. If None is given, put the data on console\r
327 #\r
328 # @retval True If the file doesn't exist or file is changed\r
329 # @retval False If file exists and is not changed.\r
330 #\r
331 def Generate(self, File=None):\r
332 Buffer = StringIO()\r
333 if len(self.PostfixNotation) == 0:\r
334 return False\r
335\r
336 for Item in self.PostfixNotation:\r
337 if Item in self.Opcode[self.Phase]:\r
338 Buffer.write(pack("B", self.Opcode[self.Phase][Item]))\r
339 elif Item in self.SupportedOpcode:\r
340 EdkLogger.error("GenDepex", FORMAT_INVALID,\r
341 "Opcode [%s] is not expected in %s phase" % (Item, self.Phase),\r
342 ExtraData=self.ExpressionString)\r
343 else:\r
344 Buffer.write(self.GetGuidValue(Item))\r
345\r
346 FilePath = ""\r
347 FileChangeFlag = True\r
348 if File == None:\r
349 sys.stdout.write(Buffer.getvalue())\r
350 FilePath = "STDOUT"\r
351 else:\r
352 FileChangeFlag = SaveFileOnChange(File, Buffer.getvalue(), True)\r
353\r
354 Buffer.close()\r
355 return FileChangeFlag\r
356\r
b36d134f 357versionNumber = ("0.04" + " " + gBUILD_VERSION)\r
30fdf114 358__version__ = "%prog Version " + versionNumber\r
52302d4d 359__copyright__ = "Copyright (c) 2007-2010, Intel Corporation All rights reserved."\r
30fdf114
LG
360__usage__ = "%prog [options] [dependency_expression_file]"\r
361\r
362## Parse command line options\r
363#\r
364# @retval OptionParser\r
365#\r
366def GetOptions():\r
367 from optparse import OptionParser\r
368\r
369 Parser = OptionParser(description=__copyright__, version=__version__, usage=__usage__)\r
370\r
371 Parser.add_option("-o", "--output", dest="OutputFile", default=None, metavar="FILE",\r
372 help="Specify the name of depex file to be generated")\r
373 Parser.add_option("-t", "--module-type", dest="ModuleType", default=None,\r
374 help="The type of module for which the dependency expression serves")\r
375 Parser.add_option("-e", "--dependency-expression", dest="Expression", default="",\r
376 help="The string of dependency expression. If this option presents, the input file will be ignored.")\r
377 Parser.add_option("-m", "--optimize", dest="Optimize", default=False, action="store_true",\r
378 help="Do some simple optimization on the expression.")\r
379 Parser.add_option("-v", "--verbose", dest="verbose", default=False, action="store_true",\r
380 help="build with verbose information")\r
381 Parser.add_option("-d", "--debug", action="store", type="int", help="Enable debug messages at specified level.")\r
382 Parser.add_option("-q", "--quiet", dest="quiet", default=False, action="store_true",\r
383 help="build with little information")\r
384\r
385 return Parser.parse_args()\r
386\r
387\r
388## Entrance method\r
389#\r
390# @retval 0 Tool was successful\r
391# @retval 1 Tool failed\r
392#\r
393def Main():\r
394 EdkLogger.Initialize()\r
395 Option, Input = GetOptions()\r
396\r
397 # Set log level\r
398 if Option.quiet:\r
399 EdkLogger.SetLevel(EdkLogger.QUIET)\r
400 elif Option.verbose:\r
401 EdkLogger.SetLevel(EdkLogger.VERBOSE)\r
402 elif Option.debug != None:\r
403 EdkLogger.SetLevel(Option.debug + 1)\r
404 else:\r
405 EdkLogger.SetLevel(EdkLogger.INFO)\r
406\r
407 try:\r
408 if Option.ModuleType == None or Option.ModuleType not in gType2Phase:\r
409 EdkLogger.error("GenDepex", OPTION_MISSING, "Module type is not specified or supported")\r
410\r
411 DxsFile = ''\r
412 if len(Input) > 0 and Option.Expression == "":\r
413 DxsFile = Input[0]\r
414 DxsString = open(DxsFile, 'r').read().replace("\n", " ").replace("\r", " ")\r
415 DxsString = gStartClosePattern.sub("\\1", DxsString)\r
416 elif Option.Expression != "":\r
417 if Option.Expression[0] == '"':\r
418 DxsString = Option.Expression[1:-1]\r
419 else:\r
420 DxsString = Option.Expression\r
421 else:\r
422 EdkLogger.error("GenDepex", OPTION_MISSING, "No expression string or file given")\r
423\r
424 Dpx = DependencyExpression(DxsString, Option.ModuleType, Option.Optimize)\r
425 if Option.OutputFile != None:\r
14c48571 426 FileChangeFlag = Dpx.Generate(Option.OutputFile)\r
427 if not FileChangeFlag and DxsFile: \r
428 #\r
429 # Touch the output file if its time stamp is older than the original\r
430 # DXS file to avoid re-invoke this tool for the dependency check in build rule.\r
431 #\r
432 if os.stat(DxsFile)[8] > os.stat(Option.OutputFile)[8]:\r
433 os.utime(Option.OutputFile, None)\r
30fdf114
LG
434 else:\r
435 Dpx.Generate()\r
436 except BaseException, X:\r
437 EdkLogger.quiet("")\r
438 if Option != None and Option.debug != None:\r
439 EdkLogger.quiet(traceback.format_exc())\r
440 else:\r
441 EdkLogger.quiet(str(X))\r
442 return 1\r
443\r
444 return 0\r
445\r
446if __name__ == '__main__':\r
447 sys.exit(Main())\r
448\r