]> git.proxmox.com Git - mirror_edk2.git/blame - BaseTools/Scripts/PackageDocumentTools/plugins/EdkPlugins/basemodel/ini.py
BaseTools: Use absolute import in Workspace
[mirror_edk2.git] / BaseTools / Scripts / PackageDocumentTools / plugins / EdkPlugins / basemodel / ini.py
CommitLineData
7ccc9c95
YZ
1## @file\r
2#\r
3# Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.<BR>\r
4#\r
5# This program and the accompanying materials are licensed and made available\r
6# under the terms and conditions of the BSD License which accompanies this\r
7# 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
14from message import *\r
15import re\r
16import os\r
17\r
18section_re = re.compile(r'^\[([\w., "]+)\]')\r
19\r
20class BaseINIFile(object):\r
21 _objs = {}\r
22 def __new__(cls, *args, **kwargs):\r
23 """Maintain only a single instance of this object\r
24 @return: instance of this class\r
25\r
26 """\r
27 if len(args) == 0: return object.__new__(cls, *args, **kwargs)\r
28 filename = args[0]\r
29 parent = None\r
30 if len(args) > 1:\r
31 parent = args[1]\r
32\r
33 key = os.path.normpath(filename)\r
34 if key not in cls._objs.keys():\r
35 cls._objs[key] = object.__new__(cls, *args, **kwargs)\r
36\r
4231a819 37 if parent is not None:\r
7ccc9c95
YZ
38 cls._objs[key].AddParent(parent)\r
39\r
40 return cls._objs[key]\r
41\r
42 def __init__(self, filename=None, parent=None):\r
43 self._lines = []\r
44 self._sections = {}\r
45 self._filename = filename\r
46 self._globals = []\r
47 self._isModify = True\r
48\r
49 def AddParent(self, parent):\r
4231a819 50 if parent is None: return\r
7ccc9c95
YZ
51 if not hasattr(self, "_parents"):\r
52 self._parents = []\r
53\r
54 if parent in self._parents:\r
55 ErrorMsg("Duplicate parent is found for INI file %s" % self._filename)\r
56 return\r
57 self._parents.append(parent)\r
58\r
59 def GetFilename(self):\r
60 return os.path.normpath(self._filename)\r
61\r
62 def IsModified(self):\r
63 return self._isModify\r
64\r
65 def Modify(self, modify=True, obj=None):\r
66 if modify == self._isModify: return\r
67 self._isModify = modify\r
68 if modify:\r
69 for parent in self._parents:\r
70 parent.Modify(True, self)\r
71\r
72 def _ReadLines(self, filename):\r
73 #\r
74 # try to open file\r
75 #\r
76 if not os.path.exists(filename):\r
77 return False\r
78\r
79 try:\r
80 handle = open(filename, 'r')\r
81 self._lines = handle.readlines()\r
82 handle.close()\r
83 except:\r
84 raise EdkException("Fail to open file %s" % filename)\r
85\r
86 return True\r
87\r
88 def GetSectionInstance(self, parent, name, isCombined=False):\r
89 return BaseINISection(parent, name, isCombined)\r
90\r
91 def GetSectionByName(self, name):\r
92 arr = []\r
93 for key in self._sections.keys():\r
94 if '.private' in key:\r
95 continue\r
96 for item in self._sections[key]:\r
97 if item.GetBaseName().lower().find(name.lower()) != -1:\r
98 arr.append(item)\r
99 return arr\r
100\r
101 def GetSectionObjectsByName(self, name):\r
102 arr = []\r
103 sects = self.GetSectionByName(name)\r
104 for sect in sects:\r
105 for obj in sect.GetObjects():\r
106 arr.append(obj)\r
107 return arr\r
108\r
109 def Parse(self):\r
110 if not self._isModify: return True\r
111 if not self._ReadLines(self._filename): return False\r
112\r
113 sObjs = []\r
114 inGlobal = True\r
115 # process line\r
116 for index in range(len(self._lines)):\r
117 templine = self._lines[index].strip()\r
118 # skip comments\r
119 if len(templine) == 0: continue\r
120 if re.match("^\[=*\]", templine) or re.match("^#", templine) or \\r
121 re.match("\*+/", templine):\r
122 continue\r
123\r
124 m = section_re.match(templine)\r
00eb12a2 125 if m is not None: # found a section\r
7ccc9c95
YZ
126 inGlobal = False\r
127 # Finish the latest section first\r
128 if len(sObjs) != 0:\r
129 for sObj in sObjs:\r
130 sObj._end = index - 1\r
131 if not sObj.Parse():\r
132 ErrorMsg("Fail to parse section %s" % sObj.GetBaseName(),\r
133 self._filename,\r
134 sObj._start)\r
135\r
136 # start new section\r
137 sname_arr = m.groups()[0].split(',')\r
138 sObjs = []\r
139 for name in sname_arr:\r
140 sObj = self.GetSectionInstance(self, name, (len(sname_arr) > 1))\r
141 sObj._start = index\r
142 sObjs.append(sObj)\r
27c4ceb4 143 if name.lower() not in self._sections:\r
7ccc9c95
YZ
144 self._sections[name.lower()] = [sObj]\r
145 else:\r
146 self._sections[name.lower()].append(sObj)\r
147 elif inGlobal: # not start any section and find global object\r
148 gObj = BaseINIGlobalObject(self)\r
149 gObj._start = index\r
150 gObj.Parse()\r
151 self._globals.append(gObj)\r
152\r
153 # Finish the last section\r
154 if len(sObjs) != 0:\r
155 for sObj in sObjs:\r
156 sObj._end = index\r
157 if not sObj.Parse():\r
158 ErrorMsg("Fail to parse section %s" % sObj.GetBaseName(),\r
159 self._filename,\r
160 sObj._start)\r
161\r
162 self._isModify = False\r
163 return True\r
164\r
165 def Destroy(self, parent):\r
166\r
167 # check referenced parent\r
4231a819 168 if parent is not None:\r
7ccc9c95
YZ
169 assert parent in self._parents, "when destory ini object, can not found parent reference!"\r
170 self._parents.remove(parent)\r
171\r
172 if len(self._parents) != 0: return\r
173\r
174 for sects in self._sections.values():\r
175 for sect in sects:\r
176 sect.Destroy()\r
177\r
178 # dereference from _objs array\r
179 assert self.GetFilename() in self._objs.keys(), "When destroy ini object, can not find obj reference!"\r
180 assert self in self._objs.values(), "When destroy ini object, can not find obj reference!"\r
181 del self._objs[self.GetFilename()]\r
182\r
183 # dereference self\r
184 self.Clear()\r
185\r
186 def GetDefine(self, name):\r
187 sects = self.GetSectionByName('Defines')\r
188 for sect in sects:\r
189 for obj in sect.GetObjects():\r
190 line = obj.GetLineByOffset(obj._start).split('#')[0].strip()\r
191 arr = line.split('=')\r
192 if arr[0].strip().lower() == name.strip().lower():\r
193 return arr[1].strip()\r
194 return None\r
195\r
196 def Clear(self):\r
197 for sects in self._sections.values():\r
198 for sect in sects:\r
199 del sect\r
200 self._sections.clear()\r
201 for gObj in self._globals:\r
202 del gObj\r
203\r
204 del self._globals[:]\r
205 del self._lines[:]\r
206\r
207 def Reload(self):\r
208 self.Clear()\r
209 ret = self.Parse()\r
210 if ret:\r
211 self._isModify = False\r
212 return ret\r
213\r
214 def AddNewSection(self, sectName):\r
215 if sectName.lower() in self._sections.keys():\r
216 ErrorMsg('Section %s can not be created for conflict with existing section')\r
217 return None\r
218\r
219 sectionObj = self.GetSectionInstance(self, sectName)\r
220 sectionObj._start = len(self._lines)\r
221 sectionObj._end = len(self._lines) + 1\r
222 self._lines.append('[%s]\n' % sectName)\r
223 self._lines.append('\n\n')\r
224 self._sections[sectName.lower()] = sectionObj\r
225 return sectionObj\r
226\r
227 def CopySectionsByName(self, oldDscObj, nameStr):\r
228 sects = oldDscObj.GetSectionByName(nameStr)\r
229 for sect in sects:\r
230 sectObj = self.AddNewSection(sect.GetName())\r
231 sectObj.Copy(sect)\r
232\r
233 def __str__(self):\r
234 return ''.join(self._lines)\r
235\r
236 ## Get file header's comment from basic INI file.\r
237 # The file comments has two style:\r
238 # 1) #/** @file\r
239 # 2) ## @file\r
240 #\r
241 def GetFileHeader(self):\r
242 desc = []\r
243 lineArr = self._lines\r
244 inHeader = False\r
245 for num in range(len(self._lines)):\r
246 line = lineArr[num].strip()\r
247 if not inHeader and (line.startswith("#/**") or line.startswith("##")) and \\r
248 line.find("@file") != -1:\r
249 inHeader = True\r
250 continue\r
251 if inHeader and (line.startswith("#**/") or line.startswith('##')):\r
252 inHeader = False\r
253 break\r
254 if inHeader:\r
255 prefixIndex = line.find('#')\r
256 if prefixIndex == -1:\r
257 desc.append(line)\r
258 else:\r
259 desc.append(line[prefixIndex + 1:])\r
260 return '<br>\n'.join(desc)\r
261\r
262class BaseINISection(object):\r
263 def __init__(self, parent, name, isCombined=False):\r
264 self._parent = parent\r
265 self._name = name\r
266 self._isCombined = isCombined\r
267 self._start = 0\r
268 self._end = 0\r
269 self._objs = []\r
270\r
271 def __del__(self):\r
272 for obj in self._objs:\r
273 del obj\r
274 del self._objs[:]\r
275\r
276 def GetName(self):\r
277 return self._name\r
278\r
279 def GetObjects(self):\r
280 return self._objs\r
281\r
282 def GetParent(self):\r
283 return self._parent\r
284\r
285 def GetStartLinenumber(self):\r
286 return self._start\r
287\r
288 def GetEndLinenumber(self):\r
289 return self._end\r
290\r
291 def GetLine(self, linenumber):\r
292 return self._parent._lines[linenumber]\r
293\r
294 def GetFilename(self):\r
295 return self._parent.GetFilename()\r
296\r
297 def GetSectionINIObject(self, parent):\r
298 return BaseINISectionObject(parent)\r
299\r
300 def Parse(self):\r
301 # skip first line in section, it is used by section name\r
302 visit = self._start + 1\r
303 iniObj = None\r
304 while (visit <= self._end):\r
305 line = self.GetLine(visit).strip()\r
306 if re.match("^\[=*\]", line) or re.match("^#", line) or len(line) == 0:\r
307 visit += 1\r
308 continue\r
309 line = line.split('#')[0].strip()\r
4231a819 310 if iniObj is not None:\r
7ccc9c95
YZ
311 if line.endswith('}'):\r
312 iniObj._end = visit - self._start\r
313 if not iniObj.Parse():\r
314 ErrorMsg("Fail to parse ini object",\r
315 self.GetFilename(),\r
316 iniObj.GetStartLinenumber())\r
317 else:\r
318 self._objs.append(iniObj)\r
319 iniObj = None\r
320 else:\r
321 iniObj = self.GetSectionINIObject(self)\r
322 iniObj._start = visit - self._start\r
323 if not line.endswith('{'):\r
324 iniObj._end = visit - self._start\r
325 if not iniObj.Parse():\r
326 ErrorMsg("Fail to parse ini object",\r
327 self.GetFilename(),\r
328 iniObj.GetStartLinenumber())\r
329 else:\r
330 self._objs.append(iniObj)\r
331 iniObj = None\r
332 visit += 1\r
333 return True\r
334\r
335 def Destroy(self):\r
336 for obj in self._objs:\r
337 obj.Destroy()\r
338\r
339 def GetBaseName(self):\r
340 return self._name\r
341\r
342 def AddLine(self, line):\r
343 end = self.GetEndLinenumber()\r
344 self._parent._lines.insert(end, line)\r
345 self._end += 1\r
346\r
347 def Copy(self, sectObj):\r
348 index = sectObj.GetStartLinenumber() + 1\r
349 while index < sectObj.GetEndLinenumber():\r
350 line = sectObj.GetLine(index)\r
351 if not line.strip().startswith('#'):\r
352 self.AddLine(line)\r
353 index += 1\r
354\r
355 def AddObject(self, obj):\r
356 lines = obj.GenerateLines()\r
357 for line in lines:\r
358 self.AddLine(line)\r
359\r
360 def GetComment(self):\r
361 comments = []\r
362 start = self._start - 1\r
363 bFound = False\r
364\r
365 while (start > 0):\r
366 line = self.GetLine(start).strip()\r
367 if len(line) == 0:\r
368 start -= 1\r
369 continue\r
370 if line.startswith('##'):\r
371 bFound = True\r
372 index = line.rfind('#')\r
373 if (index + 1) < len(line):\r
374 comments.append(line[index + 1:])\r
375 break\r
376 if line.startswith('#'):\r
377 start -= 1\r
378 continue\r
379 break\r
380 if bFound:\r
381 end = start + 1\r
382 while (end < self._start):\r
383 line = self.GetLine(end).strip()\r
384 if len(line) == 0: break\r
385 if not line.startswith('#'): break\r
386 index = line.rfind('#')\r
387 if (index + 1) < len(line):\r
388 comments.append(line[index + 1:])\r
389 end += 1\r
390 return comments\r
391\r
392class BaseINIGlobalObject(object):\r
393 def __init__(self, parent):\r
394 self._start = 0\r
395 self._end = 0\r
396\r
397 def Parse(self):\r
398 return True\r
399\r
400 def __str__(self):\r
401 return parent._lines[self._start]\r
402\r
403 def __del__(self):\r
404 pass\r
405\r
406class BaseINISectionObject(object):\r
407 def __init__(self, parent):\r
408 self._start = 0\r
409 self._end = 0\r
410 self._parent = parent\r
411\r
412 def __del__(self):\r
413 self._parent = None\r
414\r
415 def GetParent(self):\r
416 return self._parent\r
417\r
418 def GetFilename(self):\r
419 return self.GetParent().GetFilename()\r
420\r
421 def GetPackageName(self):\r
422 return self.GetFilename()\r
423\r
424 def GetFileObj(self):\r
425 return self.GetParent().GetParent()\r
426\r
427 def GetStartLinenumber(self):\r
428 return self.GetParent()._start + self._start\r
429\r
430 def GetLineByOffset(self, offset):\r
431 sect_start = self._parent.GetStartLinenumber()\r
432 linenumber = sect_start + offset\r
433 return self._parent.GetLine(linenumber)\r
434\r
435 def GetLinenumberByOffset(self, offset):\r
436 return offset + self._parent.GetStartLinenumber()\r
437\r
438 def Parse(self):\r
439 return True\r
440\r
441 def Destroy(self):\r
442 pass\r
443\r
444 def __str__(self):\r
445 return self.GetLineByOffset(self._start).strip()\r
446\r
447 def GenerateLines(self):\r
448 return ['default setion object string\n']\r
449\r
450 def GetComment(self):\r
451 comments = []\r
452 start = self.GetStartLinenumber() - 1\r
453 bFound = False\r
454\r
455 while (start > 0):\r
456 line = self.GetParent().GetLine(start).strip()\r
457 if len(line) == 0:\r
458 start -= 1\r
459 continue\r
460 if line.startswith('##'):\r
461 bFound = True\r
462 index = line.rfind('#')\r
463 if (index + 1) < len(line):\r
464 comments.append(line[index + 1:])\r
465 break\r
466 if line.startswith('#'):\r
467 start -= 1\r
468 continue\r
469 break\r
470 if bFound:\r
471 end = start + 1\r
472 while (end <= self.GetStartLinenumber() - 1):\r
473 line = self.GetParent().GetLine(end).strip()\r
474 if len(line) == 0: break\r
475 if not line.startswith('#'): break\r
476 index = line.rfind('#')\r
477 if (index + 1) < len(line):\r
478 comments.append(line[index + 1:])\r
479 end += 1\r
480 return comments\r