]> git.proxmox.com Git - mirror_edk2.git/blobdiff - BaseTools/Scripts/PackageDocumentTools/plugins/EdkPlugins/basemodel/ini.py
BaseTools: Add PackageDocumentTools into Scripts folder
[mirror_edk2.git] / BaseTools / Scripts / PackageDocumentTools / plugins / EdkPlugins / basemodel / ini.py
diff --git a/BaseTools/Scripts/PackageDocumentTools/plugins/EdkPlugins/basemodel/ini.py b/BaseTools/Scripts/PackageDocumentTools/plugins/EdkPlugins/basemodel/ini.py
new file mode 100644 (file)
index 0000000..515e7a4
--- /dev/null
@@ -0,0 +1,480 @@
+## @file\r
+#\r
+# Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.<BR>\r
+#\r
+# This program and the accompanying materials are licensed and made available\r
+# under the terms and conditions of the BSD License which accompanies this\r
+# distribution. The full text of the license may be found at\r
+# http://opensource.org/licenses/bsd-license.php\r
+#\r
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+#\r
+\r
+from message import *\r
+import re\r
+import os\r
+\r
+section_re = re.compile(r'^\[([\w., "]+)\]')\r
+\r
+class BaseINIFile(object):\r
+    _objs = {}\r
+    def __new__(cls, *args, **kwargs):\r
+        """Maintain only a single instance of this object\r
+        @return: instance of this class\r
+\r
+        """\r
+        if len(args) == 0: return object.__new__(cls, *args, **kwargs)\r
+        filename = args[0]\r
+        parent   = None\r
+        if len(args) > 1:\r
+            parent = args[1]\r
+\r
+        key = os.path.normpath(filename)\r
+        if key not in cls._objs.keys():\r
+            cls._objs[key] = object.__new__(cls, *args, **kwargs)\r
+\r
+        if parent != None:\r
+            cls._objs[key].AddParent(parent)\r
+\r
+        return cls._objs[key]\r
+\r
+    def __init__(self, filename=None, parent=None):\r
+        self._lines    = []\r
+        self._sections = {}\r
+        self._filename = filename\r
+        self._globals  = []\r
+        self._isModify = True\r
+\r
+    def AddParent(self, parent):\r
+        if parent == None: return\r
+        if not hasattr(self, "_parents"):\r
+            self._parents = []\r
+\r
+        if parent in self._parents:\r
+            ErrorMsg("Duplicate parent is found for INI file %s" % self._filename)\r
+            return\r
+        self._parents.append(parent)\r
+\r
+    def GetFilename(self):\r
+        return os.path.normpath(self._filename)\r
+\r
+    def IsModified(self):\r
+        return self._isModify\r
+\r
+    def Modify(self, modify=True, obj=None):\r
+        if modify == self._isModify: return\r
+        self._isModify = modify\r
+        if modify:\r
+            for parent in self._parents:\r
+                parent.Modify(True, self)\r
+\r
+    def _ReadLines(self, filename):\r
+        #\r
+        # try to open file\r
+        #\r
+        if not os.path.exists(filename):\r
+            return False\r
+\r
+        try:\r
+            handle = open(filename, 'r')\r
+            self._lines  = handle.readlines()\r
+            handle.close()\r
+        except:\r
+            raise EdkException("Fail to open file %s" % filename)\r
+\r
+        return True\r
+\r
+    def GetSectionInstance(self, parent, name, isCombined=False):\r
+        return BaseINISection(parent, name, isCombined)\r
+\r
+    def GetSectionByName(self, name):\r
+        arr = []\r
+        for key in self._sections.keys():\r
+            if '.private' in key:\r
+                continue\r
+            for item in self._sections[key]:\r
+                if item.GetBaseName().lower().find(name.lower()) != -1:\r
+                    arr.append(item)\r
+        return arr\r
+\r
+    def GetSectionObjectsByName(self, name):\r
+        arr = []\r
+        sects = self.GetSectionByName(name)\r
+        for sect in sects:\r
+            for obj in sect.GetObjects():\r
+                arr.append(obj)\r
+        return arr\r
+\r
+    def Parse(self):\r
+        if not self._isModify: return True\r
+        if not self._ReadLines(self._filename): return False\r
+\r
+        sObjs    = []\r
+        inGlobal = True\r
+        # process line\r
+        for index in range(len(self._lines)):\r
+            templine = self._lines[index].strip()\r
+            # skip comments\r
+            if len(templine) == 0: continue\r
+            if re.match("^\[=*\]", templine) or re.match("^#", templine) or \\r
+               re.match("\*+/", templine):\r
+                continue\r
+\r
+            m = section_re.match(templine)\r
+            if m!= None: # found a section\r
+                inGlobal = False\r
+                # Finish the latest section first\r
+                if len(sObjs) != 0:\r
+                    for sObj in sObjs:\r
+                        sObj._end = index - 1\r
+                        if not sObj.Parse():\r
+                            ErrorMsg("Fail to parse section %s" % sObj.GetBaseName(),\r
+                                     self._filename,\r
+                                     sObj._start)\r
+\r
+                # start new section\r
+                sname_arr = m.groups()[0].split(',')\r
+                sObjs = []\r
+                for name in sname_arr:\r
+                    sObj = self.GetSectionInstance(self, name, (len(sname_arr) > 1))\r
+                    sObj._start = index\r
+                    sObjs.append(sObj)\r
+                    if not self._sections.has_key(name.lower()):\r
+                        self._sections[name.lower()] = [sObj]\r
+                    else:\r
+                        self._sections[name.lower()].append(sObj)\r
+            elif inGlobal:  # not start any section and find global object\r
+                gObj = BaseINIGlobalObject(self)\r
+                gObj._start = index\r
+                gObj.Parse()\r
+                self._globals.append(gObj)\r
+\r
+        # Finish the last section\r
+        if len(sObjs) != 0:\r
+            for sObj in sObjs:\r
+                sObj._end = index\r
+                if not sObj.Parse():\r
+                    ErrorMsg("Fail to parse section %s" % sObj.GetBaseName(),\r
+                             self._filename,\r
+                             sObj._start)\r
+\r
+        self._isModify = False\r
+        return True\r
+\r
+    def Destroy(self, parent):\r
+\r
+        # check referenced parent\r
+        if parent != None:\r
+            assert parent in self._parents, "when destory ini object, can not found parent reference!"\r
+            self._parents.remove(parent)\r
+\r
+        if len(self._parents) != 0: return\r
+\r
+        for sects in self._sections.values():\r
+            for sect in sects:\r
+                sect.Destroy()\r
+\r
+        # dereference from _objs array\r
+        assert self.GetFilename() in self._objs.keys(), "When destroy ini object, can not find obj reference!"\r
+        assert self in self._objs.values(), "When destroy ini object, can not find obj reference!"\r
+        del self._objs[self.GetFilename()]\r
+\r
+        # dereference self\r
+        self.Clear()\r
+\r
+    def GetDefine(self, name):\r
+        sects = self.GetSectionByName('Defines')\r
+        for sect in sects:\r
+            for obj in sect.GetObjects():\r
+                line = obj.GetLineByOffset(obj._start).split('#')[0].strip()\r
+                arr = line.split('=')\r
+                if arr[0].strip().lower() == name.strip().lower():\r
+                    return arr[1].strip()\r
+        return None\r
+\r
+    def Clear(self):\r
+        for sects in self._sections.values():\r
+            for sect in sects:\r
+                del sect\r
+        self._sections.clear()\r
+        for gObj in self._globals:\r
+            del gObj\r
+\r
+        del self._globals[:]\r
+        del self._lines[:]\r
+\r
+    def Reload(self):\r
+        self.Clear()\r
+        ret = self.Parse()\r
+        if ret:\r
+            self._isModify = False\r
+        return ret\r
+\r
+    def AddNewSection(self, sectName):\r
+        if sectName.lower() in self._sections.keys():\r
+            ErrorMsg('Section %s can not be created for conflict with existing section')\r
+            return None\r
+\r
+        sectionObj = self.GetSectionInstance(self, sectName)\r
+        sectionObj._start = len(self._lines)\r
+        sectionObj._end   = len(self._lines) + 1\r
+        self._lines.append('[%s]\n' % sectName)\r
+        self._lines.append('\n\n')\r
+        self._sections[sectName.lower()] = sectionObj\r
+        return sectionObj\r
+\r
+    def CopySectionsByName(self, oldDscObj, nameStr):\r
+        sects = oldDscObj.GetSectionByName(nameStr)\r
+        for sect in sects:\r
+            sectObj = self.AddNewSection(sect.GetName())\r
+            sectObj.Copy(sect)\r
+\r
+    def __str__(self):\r
+        return ''.join(self._lines)\r
+\r
+    ## Get file header's comment from basic INI file.\r
+    #  The file comments has two style:\r
+    #  1) #/** @file\r
+    #  2) ## @file\r
+    #\r
+    def GetFileHeader(self):\r
+        desc = []\r
+        lineArr  = self._lines\r
+        inHeader = False\r
+        for num in range(len(self._lines)):\r
+            line = lineArr[num].strip()\r
+            if not inHeader and (line.startswith("#/**") or line.startswith("##")) and \\r
+                line.find("@file") != -1:\r
+                inHeader = True\r
+                continue\r
+            if inHeader and (line.startswith("#**/") or line.startswith('##')):\r
+                inHeader = False\r
+                break\r
+            if inHeader:\r
+                prefixIndex = line.find('#')\r
+                if prefixIndex == -1:\r
+                    desc.append(line)\r
+                else:\r
+                    desc.append(line[prefixIndex + 1:])\r
+        return '<br>\n'.join(desc)\r
+\r
+class BaseINISection(object):\r
+    def __init__(self, parent, name, isCombined=False):\r
+        self._parent     = parent\r
+        self._name       = name\r
+        self._isCombined = isCombined\r
+        self._start      = 0\r
+        self._end        = 0\r
+        self._objs       = []\r
+\r
+    def __del__(self):\r
+        for obj in self._objs:\r
+            del obj\r
+        del self._objs[:]\r
+\r
+    def GetName(self):\r
+        return self._name\r
+\r
+    def GetObjects(self):\r
+        return self._objs\r
+\r
+    def GetParent(self):\r
+        return self._parent\r
+\r
+    def GetStartLinenumber(self):\r
+        return self._start\r
+\r
+    def GetEndLinenumber(self):\r
+        return self._end\r
+\r
+    def GetLine(self, linenumber):\r
+        return self._parent._lines[linenumber]\r
+\r
+    def GetFilename(self):\r
+        return self._parent.GetFilename()\r
+\r
+    def GetSectionINIObject(self, parent):\r
+        return BaseINISectionObject(parent)\r
+\r
+    def Parse(self):\r
+        # skip first line in section, it is used by section name\r
+        visit = self._start + 1\r
+        iniObj = None\r
+        while (visit <= self._end):\r
+            line = self.GetLine(visit).strip()\r
+            if re.match("^\[=*\]", line) or re.match("^#", line) or len(line) == 0:\r
+                visit += 1\r
+                continue\r
+            line = line.split('#')[0].strip()\r
+            if iniObj != None:\r
+                if line.endswith('}'):\r
+                    iniObj._end = visit - self._start\r
+                    if not iniObj.Parse():\r
+                        ErrorMsg("Fail to parse ini object",\r
+                                 self.GetFilename(),\r
+                                 iniObj.GetStartLinenumber())\r
+                    else:\r
+                        self._objs.append(iniObj)\r
+                    iniObj = None\r
+            else:\r
+                iniObj = self.GetSectionINIObject(self)\r
+                iniObj._start = visit - self._start\r
+                if not line.endswith('{'):\r
+                    iniObj._end = visit - self._start\r
+                    if not iniObj.Parse():\r
+                        ErrorMsg("Fail to parse ini object",\r
+                                 self.GetFilename(),\r
+                                 iniObj.GetStartLinenumber())\r
+                    else:\r
+                        self._objs.append(iniObj)\r
+                    iniObj = None\r
+            visit += 1\r
+        return True\r
+\r
+    def Destroy(self):\r
+        for obj in self._objs:\r
+            obj.Destroy()\r
+\r
+    def GetBaseName(self):\r
+        return self._name\r
+\r
+    def AddLine(self, line):\r
+        end = self.GetEndLinenumber()\r
+        self._parent._lines.insert(end, line)\r
+        self._end += 1\r
+\r
+    def Copy(self, sectObj):\r
+        index = sectObj.GetStartLinenumber() + 1\r
+        while index < sectObj.GetEndLinenumber():\r
+            line = sectObj.GetLine(index)\r
+            if not line.strip().startswith('#'):\r
+                self.AddLine(line)\r
+            index += 1\r
+\r
+    def AddObject(self, obj):\r
+        lines = obj.GenerateLines()\r
+        for line in lines:\r
+            self.AddLine(line)\r
+\r
+    def GetComment(self):\r
+        comments = []\r
+        start  = self._start - 1\r
+        bFound = False\r
+\r
+        while (start > 0):\r
+            line = self.GetLine(start).strip()\r
+            if len(line) == 0:\r
+                start -= 1\r
+                continue\r
+            if line.startswith('##'):\r
+                bFound = True\r
+                index = line.rfind('#')\r
+                if (index + 1) < len(line):\r
+                    comments.append(line[index + 1:])\r
+                break\r
+            if line.startswith('#'):\r
+                start -= 1\r
+                continue\r
+            break\r
+        if bFound:\r
+            end = start + 1\r
+            while (end < self._start):\r
+                line = self.GetLine(end).strip()\r
+                if len(line) == 0: break\r
+                if not line.startswith('#'): break\r
+                index = line.rfind('#')\r
+                if (index + 1) < len(line):\r
+                    comments.append(line[index + 1:])\r
+                end += 1\r
+        return comments\r
+\r
+class BaseINIGlobalObject(object):\r
+    def __init__(self, parent):\r
+        self._start = 0\r
+        self._end   = 0\r
+\r
+    def Parse(self):\r
+        return True\r
+\r
+    def __str__(self):\r
+        return parent._lines[self._start]\r
+\r
+    def __del__(self):\r
+        pass\r
+\r
+class BaseINISectionObject(object):\r
+    def __init__(self, parent):\r
+        self._start  = 0\r
+        self._end    = 0\r
+        self._parent = parent\r
+\r
+    def __del__(self):\r
+        self._parent = None\r
+\r
+    def GetParent(self):\r
+        return self._parent\r
+\r
+    def GetFilename(self):\r
+        return self.GetParent().GetFilename()\r
+\r
+    def GetPackageName(self):\r
+        return self.GetFilename()\r
+\r
+    def GetFileObj(self):\r
+        return self.GetParent().GetParent()\r
+\r
+    def GetStartLinenumber(self):\r
+        return self.GetParent()._start + self._start\r
+\r
+    def GetLineByOffset(self, offset):\r
+        sect_start = self._parent.GetStartLinenumber()\r
+        linenumber = sect_start + offset\r
+        return self._parent.GetLine(linenumber)\r
+\r
+    def GetLinenumberByOffset(self, offset):\r
+        return offset + self._parent.GetStartLinenumber()\r
+\r
+    def Parse(self):\r
+        return True\r
+\r
+    def Destroy(self):\r
+        pass\r
+\r
+    def __str__(self):\r
+        return self.GetLineByOffset(self._start).strip()\r
+\r
+    def GenerateLines(self):\r
+        return ['default setion object string\n']\r
+\r
+    def GetComment(self):\r
+        comments = []\r
+        start  = self.GetStartLinenumber() - 1\r
+        bFound = False\r
+\r
+        while (start > 0):\r
+            line = self.GetParent().GetLine(start).strip()\r
+            if len(line) == 0:\r
+                start -= 1\r
+                continue\r
+            if line.startswith('##'):\r
+                bFound = True\r
+                index = line.rfind('#')\r
+                if (index + 1) < len(line):\r
+                    comments.append(line[index + 1:])\r
+                break\r
+            if line.startswith('#'):\r
+                start -= 1\r
+                continue\r
+            break\r
+        if bFound:\r
+            end = start + 1\r
+            while (end <= self.GetStartLinenumber() - 1):\r
+                line = self.GetParent().GetLine(end).strip()\r
+                if len(line) == 0: break\r
+                if not line.startswith('#'): break\r
+                index = line.rfind('#')\r
+                if (index + 1) < len(line):\r
+                    comments.append(line[index + 1:])\r
+                end += 1\r
+        return comments\r