Sync EDKII BaseTools to BaseTools project r1903.
[efi/edk2/.git] / edk2 / BaseTools / Source / Python / AutoGen / GenDepex.py
1 ## @file\r
2 # This file is used to generate DEPEX file for module's dependency expression\r
3 #\r
4 # Copyright (c) 2007 - 2010, Intel Corporation\r
5 # All rights reserved. This program and the accompanying materials\r
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
15 import sys\r
16 import os\r
17 import re\r
18 import traceback\r
19 \r
20 from StringIO import StringIO\r
21 from struct import pack\r
22 from Common.BuildToolError import *\r
23 from Common.Misc import SaveFileOnChange\r
24 from Common.Misc import GuidStructureStringToGuidString\r
25 from Common import EdkLogger as EdkLogger\r
26 \r
27 \r
28 ## Regular expression for matching "DEPENDENCY_START ... DEPENDENCY_END"\r
29 gStartClosePattern = re.compile(".*DEPENDENCY_START(.+)DEPENDENCY_END.*", re.S)\r
30 \r
31 ## Mapping between module type and EFI phase\r
32 gType2Phase = {\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
44     "SMM_CORE"          :   "DXE",\r
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
52 class 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
357 versionNumber = "0.04"\r
358 __version__ = "%prog Version " + versionNumber\r
359 __copyright__ = "Copyright (c) 2007-2010, Intel Corporation  All rights reserved."\r
360 __usage__ = "%prog [options] [dependency_expression_file]"\r
361 \r
362 ## Parse command line options\r
363 #\r
364 #   @retval OptionParser\r
365 #\r
366 def 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
393 def 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
426             Dpx.Generate(Option.OutputFile)\r
427         else:\r
428             Dpx.Generate()\r
429     except BaseException, X:\r
430         EdkLogger.quiet("")\r
431         if Option != None and Option.debug != None:\r
432             EdkLogger.quiet(traceback.format_exc())\r
433         else:\r
434             EdkLogger.quiet(str(X))\r
435         return 1\r
436 \r
437     return 0\r
438 \r
439 if __name__ == '__main__':\r
440     sys.exit(Main())\r
441 \r