Fixed an issue in Optimize() when encounter expression like "TRUE AND TRUE"
[people/mcb30/basetools.git] / 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, 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 import EdkLogger as EdkLogger\r
25 \r
26 import antlr3\r
27 from DepexLexer import DepexLexer\r
28 from DepexParser import DepexParser\r
29 \r
30 ## Mapping between module type and EFI phase\r
31 gType2Phase = {\r
32     "BASE"              :   None,\r
33     "SEC"               :   "PEI",\r
34     "PEI_CORE"          :   "PEI",\r
35     "PEIM"              :   "PEI",\r
36     "DXE_CORE"          :   "DXE",\r
37     "DXE_DRIVER"        :   "DXE",\r
38     "DXE_SMM_DRIVER"    :   "DXE",\r
39     "DXE_RUNTIME_DRIVER":   "DXE",\r
40     "DXE_SAL_DRIVER"    :   "DXE",\r
41     "UEFI_DRIVER"       :   "DXE",\r
42     "UEFI_APPLICATION"  :   "DXE",\r
43 }\r
44 \r
45 ## Convert dependency expression string into EFI internal representation\r
46 #\r
47 #   DependencyExpression class is used to parse dependency expression string and\r
48 # convert it into its binary form.\r
49 #\r
50 class DependencyExpression:\r
51 \r
52     OpcodePriority = {\r
53         "AND"   :   1,\r
54         "OR"    :   1,\r
55         "NOT"   :   2,\r
56         # "SOR"   :   9,\r
57         # "BEFORE":   9,\r
58         # "AFTER" :   9,\r
59     }\r
60 \r
61     Opcode = {\r
62         "PEI"   : {\r
63             "PUSH"  :   0x02,\r
64             "AND"   :   0x03,\r
65             "OR"    :   0x04,\r
66             "NOT"   :   0x05,\r
67             "TRUE"  :   0x06,\r
68             "FALSE" :   0x07,\r
69             "END"   :   0x08\r
70         },\r
71 \r
72         "DXE"   : {\r
73             "BEFORE":   0x00,\r
74             "AFTER" :   0x01,\r
75             "PUSH"  :   0x02,\r
76             "AND"   :   0x03,\r
77             "OR"    :   0x04,\r
78             "NOT"   :   0x05,\r
79             "TRUE"  :   0x06,\r
80             "FALSE" :   0x07,\r
81             "END"   :   0x08,\r
82             "SOR"   :   0x09\r
83         }\r
84     }\r
85 \r
86     # all supported op codes and operands\r
87     SupportedOpcode = ["BEFORE", "AFTER", "PUSH", "AND", "OR", "NOT", "END", "SOR"]\r
88     SupportedOperand = ["TRUE", "FALSE"]\r
89     ## Constructor\r
90     #\r
91     #   @param  Expression  The list or string of dependency expression\r
92     #   @param  ModuleType  The type of the module using the dependency expression\r
93     #\r
94     def __init__(self, Expression, ModuleType, Optimize=False, File=''):\r
95         self.Phase = gType2Phase[ModuleType]\r
96         if type(Expression) == type([]):\r
97             self.ExpressionString = " ".join(Expression)\r
98         else:\r
99             self.ExpressionString = Expression\r
100 \r
101         self.File = File\r
102         self.Tokens = None\r
103         self.Parser = None\r
104 \r
105         self.PostfixNotation = []\r
106         self.TokenList = []\r
107         self.OpcodeList = []\r
108 \r
109         self.Parse()\r
110         if Optimize:\r
111             self.Optimize()\r
112 \r
113     def __str__(self):\r
114         return " ".join(self.PostfixNotation)\r
115 \r
116     def Parse(self):\r
117         self.Tokens = antlr3.CommonTokenStream(DepexLexer(antlr3.ANTLRStringStream(self.ExpressionString)))\r
118         self.Tokens.fillBuffer()\r
119         self.Parser = DepexParser(self.Tokens)\r
120         self.Parser.start(self.File)\r
121 \r
122         self.PostfixNotation = self.Parser.PostfixNotation\r
123         self.TokenList = self.Parser.TokenList\r
124         self.OpcodeList = self.Parser.OpcodeList\r
125 \r
126     ## Simply optimize the dependency expression by removing duplicated operands\r
127     def Optimize(self):\r
128         ValidOpcode = list(set(self.OpcodeList))\r
129         if len(ValidOpcode) != 1 or ValidOpcode[0] not in ['AND', 'OR']:\r
130             return\r
131         Op = ValidOpcode[0]\r
132         NewOperand = []\r
133         AllOperand = set()\r
134         for Token in self.PostfixNotation:\r
135             if Token in self.SupportedOpcode or Token in NewOperand:\r
136                 continue\r
137             AllOperand.add(Token)\r
138             if Token == 'TRUE':\r
139                 if Op == 'AND':\r
140                     continue\r
141                 else:\r
142                     NewOperand.append(Token)\r
143                     break\r
144             elif Token == 'FALSE':\r
145                 if Op == 'OR':\r
146                     continue\r
147                 else:\r
148                     NewOperand.append(Token)\r
149                     break\r
150             NewOperand.append(Token)\r
151 \r
152         if len(NewOperand) == 0:\r
153             self.TokenList = list(AllOperand)\r
154         else:\r
155             self.TokenList = []\r
156             while True:\r
157                 self.TokenList.append(NewOperand.pop(0))\r
158                 if NewOperand == []:\r
159                     break\r
160                 self.TokenList.append(Op)\r
161         self.PostfixNotation = []\r
162         self.ExpressionString = ' '.join(self.TokenList)\r
163         self.Parse()\r
164 \r
165     ## Convert a GUID value in C structure format into its binary form\r
166     #\r
167     #   @param  Guid    The GUID value in C structure format\r
168     #\r
169     #   @retval array   The byte array representing the GUID value\r
170     #\r
171     def GetGuidValue(self, Guid):\r
172         GuidValueString = Guid.replace("{", "").replace("}", "").replace(" ", "")\r
173         GuidValueList = GuidValueString.split(",")\r
174         if len(GuidValueList) != 11:\r
175             EdkLogger.error("GenDepex", PARSER_ERROR, "Invalid GUID value string or opcode: %s" % Guid)\r
176         return pack("1I2H8B", *(int(value, 16) for value in GuidValueList))\r
177 \r
178     ## Save the binary form of dependency expression in file\r
179     #\r
180     #   @param  File    The path of file. If None is given, put the data on console\r
181     #\r
182     #   @retval True    If the file doesn't exist or file is changed\r
183     #   @retval False   If file exists and is not changed.\r
184     #\r
185     def Generate(self, File=None):\r
186         Buffer = StringIO()\r
187         for Item in self.PostfixNotation:\r
188             if Item in self.Opcode[self.Phase]:\r
189                 Buffer.write(pack("B", self.Opcode[self.Phase][Item]))\r
190             elif Item in self.SupportedOpcode:\r
191                 EdkLogger.error("GenDepex", FORMAT_INVALID,\r
192                                 "Opcode [%s] is not expected in %s phase" % (Item, self.Phase),\r
193                                 ExtraData=self.ExpressionString)\r
194             else:\r
195                 Buffer.write(self.GetGuidValue(Item))\r
196 \r
197         FilePath = ""\r
198         FileChangeFlag = True\r
199         if File == None:\r
200             sys.stdout.write(Buffer.getvalue())\r
201             FilePath = "STDOUT"\r
202         else:\r
203             FileChangeFlag = SaveFileOnChange(File, Buffer.getvalue(), True)\r
204 \r
205         Buffer.close()\r
206         return FileChangeFlag\r
207 \r
208 versionNumber = "0.03"\r
209 __version__ = "%prog Version " + versionNumber\r
210 __copyright__ = "Copyright (c) 2007-2008, Intel Corporation  All rights reserved."\r
211 __usage__ = "%prog [options] [dependency_expression_file]"\r
212 \r
213 ## Parse command line options\r
214 #\r
215 #   @retval OptionParser\r
216 #\r
217 def GetOptions():\r
218     from optparse import OptionParser\r
219 \r
220     Parser = OptionParser(description=__copyright__, version=__version__, usage=__usage__)\r
221 \r
222     Parser.add_option("-o", "--output", dest="OutputFile", default=None, metavar="FILE",\r
223                       help="Specify the name of depex file to be generated")\r
224     Parser.add_option("-t", "--module-type", dest="ModuleType", default=None,\r
225                       help="The type of module for which the dependency expression serves")\r
226     Parser.add_option("-e", "--dependency-expression", dest="Expression", default="",\r
227                       help="The string of dependency expression. If this option presents, the input file will be ignored.")\r
228     Parser.add_option("-m", "--optimize", dest="Optimize", default=False, action="store_true",\r
229                       help="Do some simple optimization on the expression.")\r
230     Parser.add_option("-v", "--verbose", dest="verbose", default=False, action="store_true",\r
231                       help="build with verbose information")\r
232     Parser.add_option("-d", "--debug", action="store", type="int", help="Enable debug messages at specified level.")\r
233     Parser.add_option("-q", "--quiet", dest="quiet", default=False, action="store_true",\r
234                       help="build with little information")\r
235 \r
236     return Parser.parse_args()\r
237 \r
238 \r
239 ## Entrance method\r
240 #\r
241 # @retval 0     Tool was successful\r
242 # @retval 1     Tool failed\r
243 #\r
244 def Main():\r
245     EdkLogger.Initialize()\r
246     Option, Input = GetOptions()\r
247 \r
248     # Set log level\r
249     if Option.verbose != None:\r
250         EdkLogger.SetLevel(EdkLogger.VERBOSE)\r
251     elif Option.quiet != None:\r
252         EdkLogger.SetLevel(EdkLogger.QUIET)\r
253     elif Option.debug != None:\r
254         EdkLogger.SetLevel(Option.debug + 1)\r
255     else:\r
256         EdkLogger.SetLevel(EdkLogger.INFO)\r
257 \r
258     try:\r
259         if Option.ModuleType == None or Option.ModuleType not in gType2Phase:\r
260             EdkLogger.error("GenDepex", OPTION_MISSING, "Module type is not specified or supported")\r
261 \r
262         DxsFile = ''\r
263         if len(Input) > 0 and Option.Expression == "":\r
264             DxsFile = Input[0]\r
265             DxsString = open(DxsFile, 'r').read()\r
266         elif Option.Expression != "":\r
267             if Option.Expression[0] == '"':\r
268                 DxsString = Option.Expression[1:-1]\r
269             else:\r
270                 DxsString = Option.Expression\r
271         else:\r
272             EdkLogger.error("GenDepex", OPTION_MISSING, "No expression string or file given")\r
273 \r
274         DxsString += '\n'\r
275         Dpx = DependencyExpression(DxsString, Option.ModuleType, Option.Optimize, DxsFile)\r
276 \r
277         if Option.OutputFile != None:\r
278             Dpx.Generate(Option.OutputFile)\r
279         else:\r
280             Dpx.Generate()\r
281     except BaseException, X:\r
282         EdkLogger.quiet("")\r
283         if Option != None and Option.debug != None:\r
284             EdkLogger.quiet(traceback.format_exc())\r
285         else:\r
286             EdkLogger.quiet(str(X))\r
287         return 1\r
288 \r
289     return 0\r
290 \r
291 if __name__ == '__main__':\r
292     sys.exit(Main())\r
293 \r