[TIC-CORE] supports new recipe
[archive/20170607/tools/tic-core.git] / tic / parser / repo_parser.py
index f545bbb..2ee2c8e 100644 (file)
 # Contributors:
 # - S-Core Co., Ltd
 
+import re
+import logging
 from lxml import etree
 from tic.utils.error import TICError
-import re
+from tic.utils.rpmmisc import archPolicies, default_arch
+from tic.config import configmgr
+
+# meta pkg
+META_PREFIX = configmgr.regularexp['meta_prefix']
+META_PREFIX_ROOT = configmgr.regularexp['meta_prefix_root']
+META_PREFIX_SUB1 = configmgr.regularexp['meta_prefix_sub1']
+META_PATTERN = re.compile(''.join(['^', META_PREFIX, configmgr.regularexp['meta_pattern']]), re.I)
+META_SUB1_PATTERN = re.compile(configmgr.regularexp['meta_sub1_pattern'])
+META_SUB2_PATTERN = re.compile(configmgr.regularexp['meta_sub2_pattern'])
+# profile pkg
+PROFILE_PATTERN = re.compile(configmgr.regularexp['profile_pattern'])
 
 class RepodataParser(object):
     
-    def __init__(self, repodata_list):
+    def __init__(self, arch, repodata_list):
+        self.arch = arch
         self.repodata_list = repodata_list
         
-    def _xml_parse(self, pkg_group, tree, tag_dic):
-        meta_prefix = 'building-block'
-        meta_prefix_root = 'building-block-root-'
-        meta_prefix_sub1 = 'building-block-sub1-'
-        meta_pattern = re.compile(''.join(['^', meta_prefix, '-(?P<meta>root|sub1|sub2)-(?P<pkgname>.+)']), re.I)
-        meta_sub1_pattern = re.compile("(?P<root>.+)-(?P<sub1>.+)")
-        meta_sub2_pattern = re.compile("(?P<root>.+)-(?P<sub1>.+)-(?P<sub2>.+)")
-
-        root = tree.getroot()
+    def _xml_parse(self, pkg_group, pkg_list, tag_dic):
         
-        pkg_list = pkg_group.get('pkg_list')
-        pkg2id = pkg_group.get('pkg2id')
-        provides2id = pkg_group.get('provides2id')
-        file2id = pkg_group.get('file2id')
+        def _set_version(element, tag):
+            if 'ver' in tag.attrib:
+                element['ver'] = tag.attrib['ver']
+            if 'rel' in tag.attrib:
+                element['rel'] = tag.attrib['rel']
+            if 'epoch' in tag.attrib:
+                element['epoch'] = tag.attrib['epoch']
+            if 'flags' in tag.attrib:
+                element['flags'] = tag.attrib['flags']
+
+        pkg_dict = pkg_group.get('pkg_dict')
+        provides_dict = pkg_group.get('provides')
+        files_dict = pkg_group.get('files')
         meta_info = pkg_group.get('meta_info')
+        pkg_id = len(pkg_dict)
         
-        pkg_id = len(pkg_list)
-        
-        for pkg in root.findall(tag_dic['package']):
-                    
+        for pkg in pkg_list:
             pkg_name = pkg.findtext(tag_dic['name'])
             
             # check whether a package is duplicated. 
-            if pkg_name in pkg2id:
-                #TODO: Apply to policy of duplication
-                print('Package(', pkg_name, ') is duplicated. exclude the package')
+            if pkg_name in pkg_dict:
+                # TODO: Apply to policy of duplication
+                # logger.warning('package(%s) is duplicated. exclude this package', pkg_name)
                 continue
-            else:
-                pkg2id[pkg_name] = pkg_id
-                
+            
             pkg_info = {}
+            pkg_info['id'] = pkg_id
             pkg_info['name'] = pkg_name
-            pkg_info['text'] = pkg_name
+            pkg_info['arch'] = pkg.findtext(tag_dic['arch'])
+            pkg_info['summary'] = pkg.findtext(tag_dic['summary'])
+            pkg_info['selfChecked'] = False # for web-ui tree
             
             # Parsing meta-pkg using meta naming rule
-            meta_match = meta_pattern.search(pkg_info['name'])
+            meta_match = META_PATTERN.search(pkg_info['name'])
             if meta_match is not None:
-                #print(meta_match.group(0), ', ', meta_match.group('meta'), ', ', meta_match.group('pkgname'))
                 if meta_match.group('meta') == 'root':
-                    meta_info['root'].append([pkg_info['name']])
+                    meta_info['root'].append([pkg_info['name'], pkg_info['summary']])
                     pkg_info['meta'] = 'root'
                 elif meta_match.group('meta') == 'sub1':
-                    sub1_match = meta_sub1_pattern.search(meta_match.group('pkgname'))
-                    meta_info['sub1'].append([pkg_info['name'],
-                                              ''.join([meta_prefix_root, sub1_match.group('root')]),
-                                              ])
+                    sub1_match = META_SUB1_PATTERN.search(meta_match.group('pkgname'))
+                    meta_info['sub1'].append([pkg_info['name'], 
+                                              ''.join([META_PREFIX_ROOT, sub1_match.group('root')]),
+                                              pkg_info['summary']])
                     pkg_info['meta'] = 'sub1'
                 elif meta_match.group('meta') == 'sub2':
-                    sub2_match = meta_sub2_pattern.search(meta_match.group('pkgname'))
+                    sub2_match = META_SUB2_PATTERN.search(meta_match.group('pkgname'))
                     meta_info['sub2'].append([pkg_info['name'],
-                                              ''.join([meta_prefix_root, sub2_match.group('root')]),
-                                              ''.join([meta_prefix_sub1, sub2_match.group('root'),'-', sub2_match.group('sub1')])
-                                              ])
+                                              ''.join([META_PREFIX_ROOT, sub2_match.group('root')]),
+                                              ''.join([META_PREFIX_SUB1, sub2_match.group('root'),'-', sub2_match.group('sub1')]),
+                                              pkg_info['summary']])
                     pkg_info['meta'] = 'sub2'
-                pkg_info['nodes'] = []
+
+            # check profile pkg
+            profile_match = PROFILE_PATTERN.search(pkg_info['name']);
+            if profile_match and profile_match.group('profile'):
+                pkg_info['profile'] = profile_match.group('profile')
+            else:
+                pkg_info['profile'] = None
             
-            pkg_info['arch'] = pkg.findtext(tag_dic['arch'])
-            pkg_info['version'] = pkg.find(tag_dic['version']).attrib['ver']
+            ver_tag = pkg.find(tag_dic['version'])
+            pkg_info['version'] = {'epoch':ver_tag.attrib['epoch'],
+                                   'ver':ver_tag.attrib['ver'],
+                                   'rel':ver_tag.attrib['rel']}
             pkg_info['checksum'] = pkg.findtext(tag_dic['checksum'])
-            pkg_info['summary'] = pkg.findtext(tag_dic['summary'])
             pkg_info['description'] = pkg.findtext(tag_dic['description'])
             pkg_info['location'] = pkg.find(tag_dic['location']).attrib['href']
             size_tag = pkg.find(tag_dic['size'])
@@ -100,61 +120,63 @@ class RepodataParser(object):
                 if requires_tag is not None:
                     dep_list = []
                     for rpm in requires_tag:
-                        req_rpm = {}
-                        req_rpm['name'] = rpm.attrib['name']
-                        if 'ver' in rpm.attrib:
-                            req_rpm['ver'] = rpm.attrib['ver']
-                        if 'flags' in rpm.attrib:
-                            req_rpm['flags'] = rpm.attrib['flags']
-                        dep_list.append(req_rpm)
+                        require = dict(name= rpm.attrib['name'])
+                        _set_version(require, rpm)
+                        dep_list.append(require)
                     pkg_info['requires'] = dep_list;
                 provides_tag = format_tag.find(tag_dic['provides'])
                 if provides_tag is not None:
                     dep_list = []
                     for rpm in provides_tag:
-                        provide = {}
-                        provide['id'] = pkg_id
-                        provide['name'] = rpm.attrib['name']
-                        if 'ver' in rpm.attrib:
-                            provide['ver'] = rpm.attrib['ver']
-                        if 'flags' in rpm.attrib:
-                            provide['flags'] = rpm.attrib['flags']
+                        provide = dict(name= rpm.attrib['name'])
+                        _set_version(provide, rpm)
+                        if provide.get('ver') and not provide.get('rel') and pkg_info['version']['rel']:
+                            provide['rel'] = pkg_info['version']['rel'];
                         
-                        if rpm.attrib['name'] in provides2id:
-                            provides2id[rpm.attrib['name']].append(provide)
+                        if rpm.attrib['name'] in provides_dict:
+                            provides_dict[rpm.attrib['name']].append({'name': pkg_name, 'data': provide})
                         else:
-                            provides2id[rpm.attrib['name']] = [provide]
-                        dep_list.append(rpm.attrib['name'])
+                            provides_dict[rpm.attrib['name']] = [{'name': pkg_name, 'data': provide}]
+                        dep_list.append(provide)
                     pkg_info['provides'] = dep_list;
                 conflicts_tag = format_tag.find(tag_dic['conflicts'])
                 if conflicts_tag is not None:
                     dep_list = []
                     for rpm in conflicts_tag:
-                        dep_list.append(rpm.attrib['name'])
+                        conflict = dict(name= rpm.attrib['name'])
+                        _set_version(conflict, rpm)
+                        dep_list.append(conflict)
                     pkg_info['conflicts'] = dep_list;
+                recommends_tag = format_tag.find(tag_dic['recommends'])
+                if recommends_tag is not None:
+                    dep_list = []
+                    for rpm in recommends_tag:
+                        recommend = dict(name=rpm.attrib['name'])
+                        _set_version(recommend, rpm)
+                        dep_list.append(recommend)
+                    pkg_info['recommends'] = dep_list;
                 suggests_tag = format_tag.find(tag_dic['suggests'])
                 if suggests_tag is not None:
                     dep_list = []
                     for rpm in suggests_tag:
-                        dep_list.append(rpm.attrib['name'])
+                        suggests = dict(name= rpm.attrib['name'])
+                        dep_list.append(suggests)
                     pkg_info['suggests'] = dep_list;
                 file_tag = format_tag.find(tag_dic['file'])
                 if file_tag is not None:
                     dep_list = []
-                    for file in format_tag.findall(tag_dic['file']):
-                        f = {}
-                        f['id'] = pkg_id
-                        f['name'] = file.text
-                        if file.text in file2id:
-                            file2id[file.text].append(f)
+                    for file_t in format_tag.findall(tag_dic['file']):
+                        if file_t.text in files_dict:
+                            files_dict[file_t.text].append(pkg_name)
                         else:
-                            file2id[file.text] = [f]
-                        dep_list.append(file.text)
+                            files_dict[file_t.text] = [pkg_name]
+                        dep_list.append(file_t.text)
                     pkg_info['file'] = dep_list;
-            pkg_list.append(pkg_info)
+            pkg_dict[pkg_name] = pkg_info
             pkg_id += 1
     
     def _prepare_requires_id(self, pkg_group):
+        logger = logging.getLogger(__name__)
         pkg_list = pkg_group.get('pkg_list')
         pkg2id = pkg_group.get('pkg2id')
         provides2id = pkg_group.get('provides2id')
@@ -176,11 +198,9 @@ class RepodataParser(object):
                     else:
                         #TODO: Exception Check
                         # the dependent package does not exist.
-                        #print('_req_parse::', req_name, ' does not exist')
-                        pass
+                        logger.warning('"%s" required by "%s" does not exist.', req_name, pkg_list[pkg_id].get('name'))
     
-    def _get_tagname(self, tree):
-        root = tree.getroot()
+    def _get_tagname(self, root):
         tags = {}
         # xmlns = re.sub('metadata$', '', root.tag) 
         tags['metadata'] = '{%s}metadata' % root.nsmap[None]
@@ -198,36 +218,66 @@ class RepodataParser(object):
         tags['provides'] = '{%s}provides' % root.nsmap['rpm']
         tags['conflicts'] = '{%s}conflicts' % root.nsmap['rpm']
         tags['suggests'] = '{%s}suggests' % root.nsmap['rpm']
+        tags['recommends'] = '{%s}recommends' % root.nsmap['rpm']
         tags['file'] = '{%s}file' % root.nsmap[None]
         return tags
+    
+    def _filtering_data_based_arch(self, xml_list, tag_dic):
+        ret_list = []
+        for xml_root in xml_list:
+            pkg_data = {}
+            for pkg_elm in xml_root.findall(tag_dic['package']):
+                pkg_arch = pkg_elm.findtext(tag_dic['arch'])
+                if pkg_arch not in archPolicies[self.arch] and pkg_arch not in default_arch:
+                    continue;
+                if not pkg_data.get(pkg_arch):
+                    pkg_data[pkg_arch] = []
+                pkg_data[pkg_arch].append(pkg_elm)
+            ret_list.append(pkg_data)
+        return ret_list
 
     def parse(self):
+        logger = logging.getLogger(__name__)
         if not self.repodata_list:
             return None
         
-        #TODO: repo URL validation
-        
-        tree_list = []
+        xml_list = []
         try:
             for repodata in self.repodata_list:
                 tree = etree.parse(repodata['primary'])
-                tree_list.append(tree)
+                xml_list.append(tree.getroot())
         except etree.XMLSyntaxError as e:
-            raise TICError('primary.xml syntax error.')
+            logger.info(e)
+            raise TICError(configmgr.message['xml_parse_error'] % ('primary', repodata['baseurl']))
+        
+        tag_dic = self._get_tagname(xml_list[0])
+        
+        # TODO: temporary code (should be deleted)
+        # meta_string='<?xml version="1.0" encoding="UTF-8"?><metadata xmlns="http://linux.duke.edu/metadata/common" xmlns:rpm="http://linux.duke.edu/metadata/rpm" packages="1467"><package type="rpm"><name>building-blocks-root-metapkg</name><arch>armv7l</arch><version epoch="0" ver="2.0.4" rel="4.2" vcs="platform/upstream/SDL#5f9405ba696ad79a0e05150430fe69e874f8280d" /><checksum type="sha256" pkgid="YES">6f74666eb89e1addc4ce75e25f3e639bbfdd8798fc848a6b7027501070567dcf</checksum><summary>Simple DirectMedia Layer</summary><description>This is the Simple DirectMedia Layer, a generic API that provides low level access to audio, keyboard, mouse, and display framebuffer across multiple platforms.</description><packager /><url>http://www.libsdl.org/</url><time file="1476718677" build="1476718668" /><size package="264245" installed="956696" archive="957124" /><location href="armv7l/SDL2-2.0.4-4.2.armv7l.rpm" /><format><rpm:license>Zlib and SGI-B-2.0</rpm:license><rpm:vendor>tizen</rpm:vendor><rpm:group>System Environment/Libraries</rpm:group><rpm:buildhost>w36</rpm:buildhost><rpm:sourcerpm>SDL2-2.0.4-4.2.src.rpm</rpm:sourcerpm><rpm:header-range start="280" end="5958" /><rpm:requires><rpm:entry name="building-blocks-sub1-metapkg-extsub1" /><rpm:entry name="building-blocks-sub1-metapkg-extsub2" /></rpm:requires></format></package><package type="rpm"><name>building-blocks-sub1-metapkg-extsub1</name><arch>armv7l</arch><version epoch="0" ver="2.0.4" rel="4.2" vcs="platform/upstream/SDL#5f9405ba696ad79a0e05150430fe69e874f8280d" /><checksum type="sha256" pkgid="YES">6f74666eb89e1addc4ce75e25f3e639bbfdd8798fc848a6b7027501070567dcf</checksum><summary>sub1-extension</summary><description>This is the Simple DirectMedia Layer, a generic API that provides low level access to audio, keyboard, mouse, and display framebuffer across multiple platforms.</description><packager /><url>http://www.libsdl.org/</url><time file="1476718677" build="1476718668" /><size package="264245" installed="956696" archive="957124" /><location href="armv7l/SDL2-2.0.4-4.2.armv7l.rpm" /><format><rpm:license>Zlib and SGI-B-2.0</rpm:license><rpm:vendor>tizen</rpm:vendor><rpm:group>System Environment/Libraries</rpm:group><rpm:buildhost>w36</rpm:buildhost><rpm:sourcerpm>SDL2-2.0.4-4.2.src.rpm</rpm:sourcerpm><rpm:header-range start="280" end="5958" /><rpm:requires><rpm:entry name="building-blocks-sub2-metapkg-extsub1-exts1sub1" /></rpm:requires></format></package><package type="rpm"><name>building-blocks-sub2-metapkg-extsub1-exts1sub1</name><arch>armv7l</arch><version epoch="0" ver="2.0.4" rel="4.2" vcs="platform/upstream/SDL#5f9405ba696ad79a0e05150430fe69e874f8280d" /><checksum type="sha256" pkgid="YES">6f74666eb89e1addc4ce75e25f3e639bbfdd8798fc848a6b7027501070567dcf</checksum><summary>sub2-extension</summary><description>This is the Simple DirectMedia Layer, a generic API that provides low level access to audio, keyboard, mouse, and display framebuffer across multiple platforms.</description><packager /><url>http://www.libsdl.org/</url><time file="1476718677" build="1476718668" /><size package="264245" installed="956696" archive="957124" /><location href="armv7l/SDL2-2.0.4-4.2.armv7l.rpm" /><format><rpm:license>Zlib and SGI-B-2.0</rpm:license><rpm:vendor>tizen</rpm:vendor><rpm:group>System Environment/Libraries</rpm:group><rpm:buildhost>w36</rpm:buildhost><rpm:sourcerpm>SDL2-2.0.4-4.2.src.rpm</rpm:sourcerpm><rpm:header-range start="280" end="5958" /><rpm:conflicts><rpm:entry name="building-blocks-sub2-metapkg-extsub1-exts1sub2" /></rpm:conflicts></format></package><package type="rpm"><name>building-blocks-sub2-metapkg-extsub1-exts1sub2</name><arch>armv7l</arch><version epoch="0" ver="2.0.4" rel="4.2" vcs="platform/upstream/SDL#5f9405ba696ad79a0e05150430fe69e874f8280d" /><checksum type="sha256" pkgid="YES">6f74666eb89e1addc4ce75e25f3e639bbfdd8798fc848a6b7027501070567dcf</checksum><summary>sub2-extension</summary><description>This is the Simple DirectMedia Layer, a generic API that provides low level access to audio, keyboard, mouse, and display framebuffer across multiple platforms.</description><packager /><url>http://www.libsdl.org/</url><time file="1476718677" build="1476718668" /><size package="264245" installed="956696" archive="957124" /><location href="armv7l/SDL2-2.0.4-4.2.armv7l.rpm" /><format><rpm:license>Zlib and SGI-B-2.0</rpm:license><rpm:vendor>tizen</rpm:vendor><rpm:group>System Environment/Libraries</rpm:group><rpm:buildhost>w36</rpm:buildhost><rpm:sourcerpm>SDL2-2.0.4-4.2.src.rpm</rpm:sourcerpm><rpm:header-range start="280" end="5958" /><rpm:conflicts><rpm:entry name="building-blocks-sub2-metapkg-extsub1-exts1sub1" /></rpm:conflicts></format></package><package type="rpm"><name>building-blocks-sub1-metapkg-extsub2</name><arch>armv7l</arch><version epoch="0" ver="2.0.4" rel="4.2" vcs="platform/upstream/SDL#5f9405ba696ad79a0e05150430fe69e874f8280d" /><checksum type="sha256" pkgid="YES">6f74666eb89e1addc4ce75e25f3e639bbfdd8798fc848a6b7027501070567dcf</checksum><summary>sub1-extension</summary><description>This is the Simple DirectMedia Layer, a generic API that provides low level access to audio, keyboard, mouse, and display framebuffer across multiple platforms.</description><packager /><url>http://www.libsdl.org/</url><time file="1476718677" build="1476718668" /><size package="264245" installed="956696" archive="957124" /><location href="armv7l/SDL2-2.0.4-4.2.armv7l.rpm" /><format><rpm:license>Zlib and SGI-B-2.0</rpm:license><rpm:vendor>tizen</rpm:vendor><rpm:group>System Environment/Libraries</rpm:group><rpm:buildhost>w36</rpm:buildhost><rpm:sourcerpm>SDL2-2.0.4-4.2.src.rpm</rpm:sourcerpm><rpm:header-range start="280" end="5958" /><rpm:requires><rpm:entry name="building-blocks-sub2-metapkg-extsub2-exts2sub1" /><rpm:entry name="SDL2" /></rpm:requires></format></package><package type="rpm"><name>building-blocks-sub2-metapkg-extsub2-exts2sub1</name><arch>armv7l</arch><version epoch="0" ver="2.0.4" rel="4.2" vcs="platform/upstream/SDL#5f9405ba696ad79a0e05150430fe69e874f8280d" /><checksum type="sha256" pkgid="YES">6f74666eb89e1addc4ce75e25f3e639bbfdd8798fc848a6b7027501070567dcf</checksum><summary>sub2-extension</summary><description>This is the Simple DirectMedia Layer, a generic API that provides low level access to audio, keyboard, mouse, and display framebuffer across multiple platforms.</description><packager /><url>http://www.libsdl.org/</url><time file="1476718677" build="1476718668" /><size package="264245" installed="956696" archive="957124" /><location href="armv7l/SDL2-2.0.4-4.2.armv7l.rpm" /><format><rpm:license>Zlib and SGI-B-2.0</rpm:license><rpm:vendor>tizen</rpm:vendor><rpm:group>System Environment/Libraries</rpm:group><rpm:buildhost>w36</rpm:buildhost><rpm:sourcerpm>SDL2-2.0.4-4.2.src.rpm</rpm:sourcerpm><rpm:header-range start="280" end="5958" /></format></package><package type="rpm"><name>building-blocks-sub2-metapkg-extsub2-exts2sub2</name><arch>armv7l</arch><version epoch="0" ver="2.0.4" rel="4.2" vcs="platform/upstream/SDL#5f9405ba696ad79a0e05150430fe69e874f8280d" /><checksum type="sha256" pkgid="YES">6f74666eb89e1addc4ce75e25f3e639bbfdd8798fc848a6b7027501070567dcf</checksum><summary>sub2-extension</summary><description>This is the Simple DirectMedia Layer, a generic API that provides low level access to audio, keyboard, mouse, and display framebuffer across multiple platforms.</description><packager /><url>http://www.libsdl.org/</url><time file="1476718677" build="1476718668" /><size package="264245" installed="956696" archive="957124" /><location href="armv7l/SDL2-2.0.4-4.2.armv7l.rpm" /><format><rpm:license>Zlib and SGI-B-2.0</rpm:license><rpm:vendor>tizen</rpm:vendor><rpm:group>System Environment/Libraries</rpm:group><rpm:buildhost>w36</rpm:buildhost><rpm:sourcerpm>SDL2-2.0.4-4.2.src.rpm</rpm:sourcerpm><rpm:header-range start="280" end="5958" /><rpm:requires><rpm:entry name="SDL2" /></rpm:requires></format></package></metadata>'
+        # xml_list.append(etree.fromstring(meta_string))
         
-        tag_dic = self._get_tagname(tree_list[0])
+        filter_data = self._filtering_data_based_arch(xml_list, tag_dic)
         
-        pkg_group = dict(pkg_list=[],
-                         pkg2id={},
-                         provides2id={},
-                         file2id={},
-                         meta_info=dict(root=[],sub1=[],sub2=[]))
+        pkg_group = dict(pkg_dict={},
+                         provides={},
+                         files={},
+                         groups={},
+                         conflicts={},
+                         meta_info=dict(root=[], sub1=[], sub2=[]))
         
         # parses the repodata (primary.xml)
-        for tree in tree_list:
-            self._xml_parse(pkg_group, tree, tag_dic)
+        # for xml_root in xml_list:
+        #    self._xml_parse(pkg_group, xml_root, tag_dic)
+        for xml_data in filter_data:
+            for arch in archPolicies[self.arch]:
+                if arch in xml_data:
+                    self._xml_parse(pkg_group, xml_data[arch], tag_dic)
+            # noarch, src
+            for arch in default_arch:
+                if arch in xml_data:
+                    self._xml_parse(pkg_group, xml_data[arch], tag_dic)
             
         # set the requires id
-        self._prepare_requires_id(pkg_group)
+        #self._prepare_requires_id(pkg_group)
             
         return pkg_group
\ No newline at end of file