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