Added conditional statement support for DSC file
[people/mcb30/basetools.git] / Source / Python / Common / Misc.py
1 ## @file\r
2 # Common routines used by all tools\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 \r
14 ##\r
15 # Import Modules\r
16 #\r
17 import os\r
18 import sys\r
19 import string\r
20 import thread\r
21 import threading\r
22 import time\r
23 import re\r
24 import cPickle\r
25 from UserDict import IterableUserDict\r
26 from UserList import UserList\r
27 \r
28 from Common import EdkLogger as EdkLogger\r
29 from BuildToolError import *\r
30 \r
31 ## Regular expression used to find out place holders in string template\r
32 gPlaceholderPattern = re.compile("\$\{([^$()\s]+)\}", re.MULTILINE|re.UNICODE)\r
33 \r
34 ## Dictionary used to store file time stamp for quick re-access\r
35 gFileTimeStampCache = {}    # {file path : file time stamp}\r
36 \r
37 ## Dictionary used to store dependencies of files\r
38 gDependencyDatabase = {}    # arch : {file path : [dependent files list]}\r
39 \r
40 ## callback routine for processing variable option\r
41 #\r
42 # This function can be used to process variable number of option values. The\r
43 # typical usage of it is specify architecure list on command line.\r
44 # (e.g. <tool> -a IA32 X64 IPF)\r
45 #\r
46 # @param  Option        Standard callback function parameter\r
47 # @param  OptionString  Standard callback function parameter\r
48 # @param  Value         Standard callback function parameter\r
49 # @param  Parser        Standard callback function parameter\r
50 #\r
51 # @retval\r
52 #\r
53 def ProcessVariableArgument(Option, OptionString, Value, Parser):\r
54     assert Value is None\r
55     Value = []\r
56     RawArgs = Parser.rargs\r
57     while RawArgs:\r
58         Arg = RawArgs[0]\r
59         if (Arg[:2] == "--" and len(Arg) > 2) or \\r
60            (Arg[:1] == "-" and len(Arg) > 1 and Arg[1] != "-"):\r
61             break\r
62         Value.append(Arg)\r
63         del RawArgs[0]\r
64     setattr(Parser.values, Option.dest, Value)\r
65 \r
66 ## Convert GUID string in xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx style to C structure style\r
67 #\r
68 #   @param      Guid    The GUID string\r
69 #\r
70 #   @retval     string  The GUID string in C structure style\r
71 #\r
72 def GuidStringToGuidStructureString(Guid):\r
73     GuidList = Guid.split('-')\r
74     Result = '{'\r
75     for Index in range(0,3,1):\r
76         Result = Result + '0x' + GuidList[Index] + ', '\r
77     Result = Result + '{0x' + GuidList[3][0:2] + ', 0x' + GuidList[3][2:4]\r
78     for Index in range(0,12,2):\r
79         Result = Result + ', 0x' + GuidList[4][Index:Index+2]\r
80     Result += '}}'\r
81     return Result\r
82 \r
83 ## Convert GUID string in C structure style to xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\r
84 #\r
85 #   @param      GuidValue   The GUID value in C structure format\r
86 #\r
87 #   @retval     string      The GUID value in xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx format\r
88 #\r
89 def GuidStructureStringToGuidString(GuidValue):\r
90     guidValueString = GuidValue.lower().replace("{", "").replace("}", "").replace(" ", "")\r
91     guidValueList = guidValueString.split(",")\r
92     if len(guidValueList) != 11:\r
93         EdkLogger.error(None, None, "Invalid GUID value string %s" % GuidValue)\r
94     return "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x" % (\r
95             int(guidValueList[0], 16),\r
96             int(guidValueList[1], 16),\r
97             int(guidValueList[2], 16),\r
98             int(guidValueList[3], 16),\r
99             int(guidValueList[4], 16),\r
100             int(guidValueList[5], 16),\r
101             int(guidValueList[6], 16),\r
102             int(guidValueList[7], 16),\r
103             int(guidValueList[8], 16),\r
104             int(guidValueList[9], 16),\r
105             int(guidValueList[10], 16)\r
106             )\r
107 \r
108 ## Convert GUID string in C structure style to xxxxxxxx_xxxx_xxxx_xxxx_xxxxxxxxxxxx\r
109 #\r
110 #   @param      GuidValue   The GUID value in C structure format\r
111 #\r
112 #   @retval     string      The GUID value in xxxxxxxx_xxxx_xxxx_xxxx_xxxxxxxxxxxx format\r
113 #\r
114 def GuidStructureStringToGuidValueName(GuidValue):\r
115     guidValueString = GuidValue.lower().replace("{", "").replace("}", "").replace(" ", "")\r
116     guidValueList = guidValueString.split(",")\r
117     if len(guidValueList) != 11:\r
118         EdkLogger.error(None, None, "Invalid GUID value string %s" % GuidValue)\r
119     return "%08x_%04x_%04x_%02x%02x_%02x%02x%02x%02x%02x%02x" % (\r
120             int(guidValueList[0], 16),\r
121             int(guidValueList[1], 16),\r
122             int(guidValueList[2], 16),\r
123             int(guidValueList[3], 16),\r
124             int(guidValueList[4], 16),\r
125             int(guidValueList[5], 16),\r
126             int(guidValueList[6], 16),\r
127             int(guidValueList[7], 16),\r
128             int(guidValueList[8], 16),\r
129             int(guidValueList[9], 16),\r
130             int(guidValueList[10], 16)\r
131             )\r
132 \r
133 ## Create directories\r
134 #\r
135 #   @param      Directory   The directory name\r
136 #\r
137 def CreateDirectory(Directory):\r
138     if Directory == None or Directory.strip() == "":\r
139         return True\r
140     try:\r
141         if not os.access(Directory, os.F_OK):\r
142             os.makedirs(Directory)\r
143     except:\r
144         return False\r
145     return True\r
146 \r
147 ## Check if given file is changed or not\r
148 #\r
149 #  This method is used to check if a file is changed or not between two build\r
150 #  actions. It makes use a cache to store files timestamp.\r
151 #\r
152 #   @param      File    The path of file\r
153 #\r
154 #   @retval     True    If the given file is changed, doesn't exist, or can't be\r
155 #                       found in timestamp cache\r
156 #   @retval     False   If the given file is changed\r
157 #\r
158 def IsChanged(File):\r
159     if not os.path.exists(File):\r
160         return True\r
161 \r
162     FileState = os.stat(File)\r
163     TimeStamp = FileState[-2]\r
164 \r
165     if File in gFileTimeStampCache and TimeStamp <= gFileTimeStampCache[File]:\r
166         FileChanged = False\r
167     else:\r
168         FileChanged = True\r
169         gFileTimeStampCache[File] = TimeStamp\r
170 \r
171     return FileChanged\r
172 \r
173 ## Store content in file\r
174 #\r
175 #  This method is used to save file only when its content is changed. This is\r
176 #  quite useful for "make" system to decide what will be re-built and what won't.\r
177 #\r
178 #   @param      File            The path of file\r
179 #   @param      Content         The new content of the file\r
180 #   @param      IsBinaryFile    The flag indicating if the file is binary file or not\r
181 #\r
182 #   @retval     True            If the file content is changed and the file is renewed\r
183 #   @retval     False           If the file content is the same\r
184 #\r
185 def SaveFileOnChange(File, Content, IsBinaryFile=True):\r
186     if IsBinaryFile:\r
187         BinaryFlag = 'b'\r
188     else:\r
189         BinaryFlag = ''\r
190     Fd = None\r
191     if os.path.exists(File):\r
192         try:\r
193             Fd = open(File, "r"+BinaryFlag)\r
194         except:\r
195             EdkLogger.error(None, FILE_OPEN_FAILURE, ExtraData=File)\r
196         FileSize = os.fstat(Fd.fileno()).st_size\r
197         if len(Content) == FileSize and Content == Fd.read():\r
198             Fd.close()\r
199             return False\r
200         Fd.close()\r
201         # os.remove(File) # seems creating new file is faster than overwriting old one\r
202     CreateDirectory(os.path.dirname(File))\r
203     try:\r
204         Fd = open(File, "w"+BinaryFlag)\r
205     except:\r
206         EdkLogger.error(None, FILE_CREATE_FAILURE, ExtraData=File)\r
207     Fd.write(Content)\r
208     Fd.close()\r
209     return True\r
210 \r
211 ## Make a Python object persistent on file system\r
212 #\r
213 #   @param      Data    The object to be stored in file\r
214 #   @param      File    The path of file to store the object\r
215 #\r
216 def DataDump(Data, File):\r
217     Fd = None\r
218     try:\r
219         Fd = open(File, 'wb')\r
220         cPickle.dump(Data, Fd, cPickle.HIGHEST_PROTOCOL)\r
221     except:\r
222         EdkLogger.error("", FILE_OPEN_FAILURE, ExtraData=File, RaiseError=False)\r
223     finally:\r
224         if Fd != None:\r
225             Fd.close()\r
226 \r
227 ## Restore a Python object from a file\r
228 #\r
229 #   @param      File    The path of file stored the object\r
230 #\r
231 #   @retval     object  A python object\r
232 #   @retval     None    If failure in file operation\r
233 #\r
234 def DataRestore(File):\r
235     Data = None\r
236     Fd = None\r
237     try:\r
238         Fd = open(File, 'rb')\r
239         Data = cPickle.load(Fd)\r
240     except Exception, e:\r
241         EdkLogger.verbose("Failed to load [%s]\n\t%s" % (File, str(e)))\r
242         Data = None\r
243     finally:\r
244         if Fd != None:\r
245             Fd.close()\r
246     return Data\r
247 \r
248 ## A string template class\r
249 #\r
250 #  This class implements a template for string replacement. A string template\r
251 #  looks like following\r
252 #\r
253 #       ${BEGIN} other_string ${placeholder_name} other_string ${END}\r
254 #\r
255 #  The string between ${BEGIN} and ${END} will be repeated as many times as the\r
256 #  length of "placeholder_name", which is a list passed through a dict. The\r
257 #  "placeholder_name" is the key name of the dict. The ${BEGIN} and ${END} can\r
258 #  be not used and, in this case, the "placeholder_name" must not a list and it\r
259 #  will just be replaced once.\r
260 #\r
261 class TemplateString(object):\r
262     ## Constructor\r
263     def __init__(self):\r
264         self.String = ''\r
265 \r
266     ## str() operator\r
267     #\r
268     #   @retval     string  The string replaced\r
269     #\r
270     def __str__(self):\r
271         return self.String\r
272 \r
273     ## Replace the string template with dictionary of placeholders\r
274     #\r
275     #   @param      AppendString    The string template to append\r
276     #   @param      Dictionary      The placeholder dictionaries\r
277     #\r
278     def Append(self, AppendString, Dictionary=None):\r
279         if Dictionary == None:\r
280             self.String += AppendString\r
281             return\r
282 \r
283         # replace repeat ones, enclosed by ${BEGIN} and $(END)\r
284         while True:\r
285             Start = AppendString.find('${BEGIN}')\r
286             if Start < 0:\r
287                 break\r
288             End   = AppendString.find('${END}')\r
289 \r
290             # exclude the ${BEGIN} and ${END}\r
291             SubString = AppendString[Start + 8 : End]\r
292 \r
293             RepeatTime = -1\r
294             SubDict = {}\r
295             PlaceholderList = gPlaceholderPattern.findall(SubString)\r
296             for Key in PlaceholderList:\r
297                 if Key not in Dictionary:\r
298                     continue\r
299 \r
300                 Value = Dictionary[Key]\r
301                 if type(Value) != type([]):\r
302                     continue\r
303 \r
304                 SubDict[Key] = ""\r
305                 if RepeatTime < 0:\r
306                     RepeatTime = len(Value)\r
307                 elif RepeatTime != len(Value):\r
308                     EdkLogger.error("TemplateString", PARAMETER_INVALID, Key + " has different repeat time from others!",\r
309                                     ExtraData=str(Dictionary))\r
310 \r
311             NewString = ''\r
312             for Index in range(0, RepeatTime):\r
313                 for Key in SubDict:\r
314                     SubDict[Key] = Dictionary[Key][Index]\r
315                 NewString += string.Template(SubString).safe_substitute(SubDict)\r
316             AppendString = AppendString[0:Start] + NewString + AppendString[End + 6:]\r
317 \r
318         # replace single ones\r
319         SubDict = {}\r
320         for Key in Dictionary:\r
321             Value = Dictionary[Key]\r
322             if type(Value) == type([]):\r
323                 continue\r
324             SubDict[Key] = Value\r
325         AppendString = string.Template(AppendString).safe_substitute(SubDict)\r
326 \r
327         self.String += AppendString\r
328 \r
329 ## Progress indicator class\r
330 #\r
331 #  This class makes use of thread to print progress on console.\r
332 #\r
333 class Progressor:\r
334     # for avoiding deadloop\r
335     _StopFlag = None\r
336     _ProgressThread = None\r
337     ## Constructor\r
338     #\r
339     #   @param      OpenMessage     The string printed before progress charaters\r
340     #   @param      CloseMessage    The string printed after progress charaters\r
341     #   @param      ProgressChar    The charater used to indicate the progress\r
342     #   @param      Interval        The interval in seconds between two progress charaters\r
343     #\r
344     def __init__(self, OpenMessage="", CloseMessage="", ProgressChar='.', Interval=1):\r
345         self.PromptMessage = OpenMessage\r
346         self.CodaMessage = CloseMessage\r
347         self.ProgressChar = ProgressChar\r
348         self.Interval = Interval\r
349         if Progressor._StopFlag == None:\r
350             Progressor._StopFlag = threading.Event()\r
351 \r
352     ## Start to print progress charater\r
353     #\r
354     #   @param      OpenMessage     The string printed before progress charaters\r
355     #\r
356     def Start(self, OpenMessage=None):\r
357         if OpenMessage != None:\r
358             self.PromptMessage = OpenMessage\r
359         Progressor._StopFlag.clear()\r
360         if Progressor._ProgressThread == None:\r
361             Progressor._ProgressThread = threading.Thread(target=self._ProgressThreadEntry)\r
362             Progressor._ProgressThread.setDaemon(False)\r
363             Progressor._ProgressThread.start()\r
364 \r
365     ## Stop printing progress charater\r
366     #\r
367     #   @param      CloseMessage    The string printed after progress charaters\r
368     #\r
369     def Stop(self, CloseMessage=None):\r
370         OriginalCodaMessage = self.CodaMessage\r
371         if CloseMessage != None:\r
372             self.CodaMessage = CloseMessage\r
373         self.Abort()\r
374         self.CodaMessage = OriginalCodaMessage\r
375 \r
376     ## Thread entry method\r
377     def _ProgressThreadEntry(self):\r
378         print self.PromptMessage,\r
379         sys.stdout.flush()\r
380         while not Progressor._StopFlag.isSet():\r
381             print self.ProgressChar,\r
382             sys.stdout.flush()\r
383             time.sleep(self.Interval)\r
384         print self.CodaMessage\r
385         sys.stdout.flush()\r
386 \r
387     ## Abort the progress display\r
388     @staticmethod\r
389     def Abort():\r
390         if Progressor._StopFlag != None:\r
391             Progressor._StopFlag.set()\r
392         if Progressor._ProgressThread != None:\r
393             Progressor._ProgressThread.join()\r
394             Progressor._ProgressThread = None\r
395 \r
396 ## A dict which can access its keys and/or values orderly\r
397 #\r
398 #  The class implements a new kind of dict which its keys or values can be\r
399 #  accessed in the order they are added into the dict. It guarantees the order\r
400 #  by making use of an internal list to keep a copy of keys.\r
401 #\r
402 class sdict(IterableUserDict):\r
403     ## Constructor\r
404     def __init__(self):\r
405         IterableUserDict.__init__(self)\r
406         self._key_list = []\r
407 \r
408     ## [] operator\r
409     def __setitem__(self, key, value):\r
410         if key not in self._key_list:\r
411             self._key_list.append(key)\r
412         IterableUserDict.__setitem__(self, key, value)\r
413 \r
414     ## del operator\r
415     def __delitem__(self, key):\r
416         self._key_list.remove(key)\r
417         IterableUserDict.__delitem__(self, key)\r
418 \r
419     ## used in "for k in dict" loop to ensure the correct order\r
420     def __iter__(self):\r
421         return self.iterkeys()\r
422 \r
423     ## len() support\r
424     def __len__(self):\r
425         return len(self._key_list)\r
426 \r
427     ## "in" test support\r
428     def __contains__(self, key):\r
429         return key in self._key_list\r
430 \r
431     def has_key(self, key):\r
432         return key in self._key_list\r
433 \r
434     ## Empty the dict\r
435     def clear(self):\r
436         self._key_list = []\r
437         IterableUserDict.clear(self)\r
438 \r
439     ## Return a copy of keys\r
440     def keys(self):\r
441         keys = []\r
442         for key in self._key_list:\r
443             keys.append(key)\r
444         return keys\r
445 \r
446     ## Return a copy of values\r
447     def values(self):\r
448         values = []\r
449         for key in self._key_list:\r
450             values.append(self[key])\r
451         return values\r
452 \r
453     ## Return a copy of (key, value) list\r
454     def items(self):\r
455         items = []\r
456         for key in self._key_list:\r
457             items.append((key, self[key]))\r
458         return items\r
459 \r
460     ## Iteration support\r
461     def iteritems(self):\r
462         return iter(self.items())\r
463 \r
464     ## Keys interation support\r
465     def iterkeys(self):\r
466         return iter(self.keys())\r
467 \r
468     ## Values interation support\r
469     def itervalues(self):\r
470         return iter(self.values())\r
471 \r
472     ## Return value related to a key, and remove the (key, value) from the dict\r
473     def pop(self, key, *dv):\r
474         value = None\r
475         if key in self._key_list:\r
476             value = self[key]\r
477             self.__delitem__(key)\r
478         elif len(dv) != 0 :\r
479             value = kv[0]\r
480         return value\r
481 \r
482     ## Return (key, value) pair, and remove the (key, value) from the dict\r
483     def popitem(self):\r
484         key = self._key_list[-1]\r
485         value = self[key]\r
486         self.__delitem__(key)\r
487         return key, value\r
488 \r
489 ## Dictionary with restricted keys\r
490 #\r
491 class rdict(dict):\r
492     ## Constructor\r
493     def __init__(self, KeyList):\r
494         for Key in KeyList:\r
495             dict.__setitem__(self, Key, "")\r
496 \r
497     ## []= operator\r
498     def __setitem__(self, key, value):\r
499         if key not in self:\r
500             EdkLogger.error("RestrictedDict", ATTRIBUTE_SET_FAILURE, "Key [%s] is not allowed" % key, \r
501                             ExtraData=", ".join(dict.keys(self)))\r
502         dict.__setitem__(self, key, value)\r
503 \r
504     ## =[] operator\r
505     def __getitem__(self, key):\r
506         if key not in self:\r
507             return ""\r
508         return dict.__getitem__(self, key)\r
509 \r
510     ## del operator\r
511     def __delitem__(self, key):\r
512         EdkLogger.error("RestrictedDict", ATTRIBUTE_ACCESS_DENIED, ExtraData="del")\r
513 \r
514     ## Empty the dict\r
515     def clear(self):\r
516         for Key in self:\r
517             self.__setitem__(Key, "")\r
518 \r
519     ## Return value related to a key, and remove the (key, value) from the dict\r
520     def pop(self, key, *dv):\r
521         EdkLogger.error("RestrictedDict", ATTRIBUTE_ACCESS_DENIED, ExtraData="pop")\r
522 \r
523     ## Return (key, value) pair, and remove the (key, value) from the dict\r
524     def popitem(self):\r
525         EdkLogger.error("RestrictedDict", ATTRIBUTE_ACCESS_DENIED, ExtraData="popitem")\r
526 \r
527 ## Dictionary using prioritized list as key\r
528 #\r
529 class tdict:\r
530     _ListType = type([])\r
531     _TupleType = type(())\r
532     _Wildcard = 'COMMON'\r
533     _ValidWildcardList = ['COMMON', 'DEFAULT', 'ALL', '', '*', 'PLATFORM']\r
534 \r
535     def __init__(self, _Single_=False, _Level_=2):\r
536         self._Level_ = _Level_\r
537         self.data = {}\r
538         self._Single_ = _Single_\r
539     \r
540     # =[] operator\r
541     def __getitem__(self, key):\r
542         KeyType = type(key)\r
543         RestKeys = None\r
544         if KeyType == self._ListType or KeyType == self._TupleType:\r
545             FirstKey = key[0]\r
546             if len(key) > 1:\r
547                 RestKeys = key[1:]\r
548             elif self._Level_ > 1:\r
549                 RestKeys = [self._Wildcard for i in range(0, self._Level_-1)]\r
550         else:\r
551             FirstKey = key\r
552             if self._Level_ > 1:\r
553                 RestKeys = [self._Wildcard for i in range(0, self._Level_-1)]\r
554 \r
555         if FirstKey == None or str(FirstKey).upper() in self._ValidWildcardList:\r
556             FirstKey = self._Wildcard\r
557 \r
558         if self._Single_:\r
559             return self._GetSingleValue(FirstKey, RestKeys)\r
560         else:\r
561             return self._GetAllValues(FirstKey, RestKeys)\r
562 \r
563     def _GetSingleValue(self, FirstKey, RestKeys):\r
564         Value = None\r
565         #print "%s-%s" % (FirstKey, self._Level_) ,\r
566         if self._Level_ > 1:\r
567             if FirstKey == self._Wildcard:\r
568                 for Key in self.data:\r
569                     Value = self.data[Key][RestKeys]\r
570                     if Value != None: break\r
571             else:\r
572                 if FirstKey in self.data:\r
573                     Value = self.data[FirstKey][RestKeys]\r
574                 if Value == None and self._Wildcard in self.data:\r
575                     #print "Value=None"\r
576                     Value = self.data[self._Wildcard][RestKeys]\r
577         else:\r
578             # "$"\r
579             if FirstKey == self._Wildcard:\r
580                 for Key in self.data:\r
581                     Value = self.data[Key]\r
582                     if Value != None: break\r
583             else:\r
584                 if FirstKey in self.data:\r
585                     Value = self.data[FirstKey]\r
586                 elif self._Wildcard in self.data:\r
587                     Value = self.data[self._Wildcard]\r
588         return Value\r
589 \r
590     def _GetAllValues(self, FirstKey, RestKeys):\r
591         Value = []\r
592         if self._Level_ > 1:\r
593             if FirstKey == self._Wildcard:\r
594                 for Key in self.data:\r
595                     Value += self.data[Key][RestKeys]\r
596             else:\r
597                 if FirstKey in self.data:\r
598                     Value += self.data[FirstKey][RestKeys]\r
599                 if self._Wildcard in self.data:\r
600                     Value += self.data[self._Wildcard][RestKeys]\r
601         else:\r
602             if FirstKey == self._Wildcard:\r
603                 for Key in self.data:\r
604                     Value.append(self.data[Key])\r
605             else:\r
606                 if FirstKey in self.data:\r
607                     Value.append(self.data[FirstKey])\r
608                 if self._Wildcard in self.data:\r
609                     Value.append(self.data[self._Wildcard])\r
610         return Value\r
611 \r
612     ## []= operator\r
613     def __setitem__(self, key, value):\r
614         KeyType = type(key)\r
615         RestKeys = None\r
616         if KeyType == self._ListType or KeyType == self._TupleType:\r
617             FirstKey = key[0]\r
618             if len(key) > 1:\r
619                 RestKeys = key[1:]\r
620             else:\r
621                 RestKeys = [self._Wildcard for i in range(0, self._Level_-1)]\r
622         else:\r
623             FirstKey = key\r
624             if self._Level_ > 1:\r
625                 RestKeys = [self._Wildcard for i in range(0, self._Level_-1)]\r
626 \r
627         if FirstKey in self._ValidWildcardList:\r
628             FirstKey = self._Wildcard\r
629         \r
630         if FirstKey not in self.data and self._Level_ > 0:\r
631             self.data[FirstKey] = tdict(self._Single_, self._Level_ - 1)\r
632 \r
633         if self._Level_ > 1:\r
634             self.data[FirstKey][RestKeys] = value\r
635         else:\r
636             self.data[FirstKey] = value\r
637 \r
638     def SetGreedyMode(self):\r
639         self._Single_ = False\r
640         if self._Level_ > 1:\r
641             for Key in self.data:\r
642                 self.data[Key].SetGreedyMode()\r
643 \r
644     def SetSingleMode(self):\r
645         self._Single_ = True\r
646         if self._Level_ > 1:\r
647             for Key in self.data:\r
648                 self.data[Key].SetSingleMode()\r
649 \r
650 ## Boolean chain list\r
651\r
652 class Blist(UserList):\r
653     def __init__(self, initlist=None):\r
654         UserList.__init__(self, initlist)\r
655     def __setitem__(self, i, item):\r
656         if item not in [True, False]:\r
657             if item == 0:\r
658                 item = False\r
659             else:\r
660                 item = True\r
661         self.data[i] = item\r
662     def _GetResult(self):\r
663         Value = True\r
664         for item in self.data:\r
665             Value &= item\r
666         return Value\r
667     Result = property(_GetResult)\r
668 \r
669 ##\r
670 #\r
671 # This acts like the main() function for the script, unless it is 'import'ed into another\r
672 # script.\r
673 #\r
674 if __name__ == '__main__':\r
675     d = tdict(True, 3)\r
676     d['COMMON', 'PEIM', "A",] = 1\r
677     d['COMMON', 'DXE_CORE', 'B'] = 2\r
678     d['IA32', 'DXE_CORE', 'C'] = 3\r
679 \r
680     print d['IA32', 'DXE_CORE', 'C']\r
681 \r