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