[TIC-CORE] Modify data structure with tic-web 19/111619/1
authorChulwoo Shin <cw1.shin@samsung.com>
Mon, 23 Jan 2017 15:04:19 +0000 (00:04 +0900)
committerChulwoo Shin <cw1.shin@samsung.com>
Mon, 23 Jan 2017 15:04:19 +0000 (00:04 +0900)
- Modify data structure with tic-web
- Analyze install-dependency for default packages
- Support rpm selection algorithm for version

Change-Id: Ic2809c268271ff37eaea7f2ed0b94dd63a65e20e
Signed-off-by: Chulwoo Shin <cw1.shin@samsung.com>
tic/command.py
tic/dependency.py
tic/parser/recipe_parser.py
tic/parser/repo_parser.py
tic/parser/view_parser.py
tools/tic-core

index 51f41cc..024c390 100644 (file)
@@ -21,8 +21,9 @@
 
 import os
 import logging
+import time
 
-from tic.dependency import analyze_dependency, get_installed_packages
+from tic.dependency import get_installed_packages
 from tic.parser.recipe_parser import get_default_recipe, convert_recipe_to_yaml
 from tic.parser.repo_parser import RepodataParser
 from tic.parser.view_parser import make_view_data
@@ -36,6 +37,8 @@ from tic.utils import process
 DEFAULT_CACHEDIR='/var/tmp/tic-core/cached'
 DEFAULT_KICKSTARTDIR='/var/tmp/tic-core/kickstart'
 
+current_milli_time = lambda: int(round(time.time() * 1000))
+
 def analyze(repo_list, recipe_list=None):
     logger = logging.getLogger(__name__)
     
@@ -58,21 +61,34 @@ def analyze(repo_list, recipe_list=None):
                           'url': repo_url})
             number = number + 1
     
+    start_time = current_milli_time()
     #Download repodata from repositories (Remote/Local)
     repoinfo = get_repodata_from_repos(repos, DEFAULT_CACHEDIR)
+    logger.info('time to get repodata from repo: %d ms', current_milli_time() - start_time)
     
+    start_time = current_milli_time()
     # Parse the xml files for the analysis of package (.rpm)
     repo_parser = RepodataParser(repoinfo)
     pkg_group = repo_parser.parse()
-    logger.info('pkg_list: %d, pkg2id: %d', len(pkg_group['pkg_list']), len(pkg_group['pkg2id']))
+    logger.info('packages: %d, provides: %d, files: %d', len(pkg_group['pkg_dict']), len(pkg_group['provides']), len(pkg_group['files']))
+    logger.info('time to parse repodata: %d ms', current_milli_time() - start_time)
     
-    # package install-dependency analysis
-    analyze_dependency(pkg_group)
+    start_time = current_milli_time()
     # Make a data for TIC (Tizen image creation)
     view_data = make_view_data(pkg_group)
+    # analyze install-dependency
     inst_packages = get_installed_packages(recipe, repoinfo, pkg_group)
+    logger.info('installed package: %d', len(inst_packages))
+    logger.info('time to analyze dependency: %d ms', current_milli_time() - start_time)
+    
+    
+    start_time = current_milli_time()
     
-    result = {'packages': view_data,
+    result = {'view': view_data,
+              'data': {'packages': pkg_group['pkg_dict'],
+                       'provides': pkg_group['provides'],
+                       'files': pkg_group['files'],
+                       'groups': pkg_group.get('groups')},
               'repos': repos,
               'defaultpackages': inst_packages}
     
index 32b8994..fcca356 100644 (file)
 # Contributors:
 # - S-Core Co., Ltd
 
+import rpm
+import logging
 from lxml import etree
 from tic.utils.error import TICError
-import logging
 
 def analyze_dependency(pkg_group):
     
@@ -94,6 +95,152 @@ def analyze_dependency(pkg_group):
 def get_installed_packages(recipe, repoinfo, pkg_group):
     logger = logging.getLogger(__name__)
     
+    def _compare_ver(ver1, ver2):
+        return rpm.labelCompare((ver1.get('epoch'), ver1.get('ver'), ver1.get('rel')), 
+                                (ver2.get('epoch'), ver2.get('ver'), ver2.get('rel')))
+        
+    def _compare_req_cap_ver(req, cap):
+        epoch = cap.get('epoch')
+        ver = cap.get('ver')
+        rel = cap.get('rel')
+        if not req.get('epoch'): epoch = None
+        if not req.get('rel'): rel = None
+        return rpm.labelCompare((req.get('epoch'), req.get('ver'), req.get('rel')), (epoch, ver, rel))
+    
+    def _select_rpm(capability, require):
+        
+        # TODO: temporary code (to support efl-data capability)
+        if len(capability) == 1:
+            return pkg_dict.get(capability[0].get('name'))
+        
+        provide_list = []
+        # 1. Choose the rpm included in version from provides
+        if require.get('ver') is not None:
+            for provide in capability:
+                cap_info = provide.get('data')
+                cmp_ret = _compare_req_cap_ver(require, cap_info)
+                if cmp_ret == 0 and (require['flags'] == 'EQ' or require['flags'] == 'GE' or require['flags'] == 'LE'):
+                    provide_list.append(provide)
+                elif cmp_ret == 1 and (require['flags'] == 'LT' or require['flags'] == 'LE'):
+                    provide_list.append(provide)
+                elif cmp_ret == -1 and (require['flags'] == 'GT' or require['flags'] == 'GE'):
+                    provide_list.append(provide)
+        else:
+            provide_list = capability
+            
+        # error case (the rpm does not exist)
+        if not provide_list:
+            return None
+        
+        if len(provide_list) == 1:
+            return pkg_dict.get(provide_list[0].get('name'))
+        
+        # 2 Select one of the rpms by priority
+        # 2-1. Choose the default rpm or the selected rpm 
+        # TODO: default profile rpm should be selected
+        for i in range(0, len(provide_list)):
+            tmp_info = pkg_dict.get(provide_list[i].get('name'))
+            if tmp_info['name'] in pkg_set or selected[tmp_info['id']] == 1:
+                return tmp_info
+        # 2-2. Select the latest version of rpm
+        max_ver = provide_list[0]
+        for i in range(1, len(provide_list)):
+            cap_info = provide_list[i].get('data')
+            ret = _compare_ver(max_ver.get('data'), cap_info)
+            # cap_info is greater than max_ver
+            if ret == -1:
+                max_ver = provide_list[i]
+        
+        pkg_name = max_ver.get('name')
+        return pkg_dict.get(pkg_name)
+    
+    def _create_reference(pkg1, pkg2):
+        # duplicate check
+        if pkg1.get('forward') and pkg2['name'] in pkg1.get('forward'):
+            return
+        
+        if pkg1.get('forward'):
+            pkg1['forward'].append(pkg2['name'])
+        else:
+            pkg1['forward'] = [pkg2['name']]
+        
+        if pkg2.get('backward'):
+            pkg2['backward'].append(pkg1['name'])
+        else:
+            pkg2['backward'] = [pkg1['name']]
+            
+    def _make_scc(pkg_id):
+        scc_num[0] += 1
+        scc_list = []
+        # stack is not empty
+        while stack:
+            pkg = stack.pop()
+            scc_id[pkg['id']] = scc_num[0]
+            scc_list.append(pkg)
+            if pkg_id == pkg['id']:
+                break;
+            
+        # circular dependency
+        if len(scc_list) > 1:
+            group_num[0] += 1
+            group_names = []
+            # add group id
+            for pkg in scc_list:
+                pkg['group'] = group_num[0]
+                group_names.append(pkg['name'])
+            
+            # { group_id = [ pkg_name_1, pkg_name_2 ], ... }
+            groups[group_num[0]] = group_names
+            print(group_names)
+        
+    def _analyze_dep(pkg_info):
+        if not pkg_info:
+            return 
+        
+        pkg_id = pkg_info['id'];
+        number[0] += 1
+        selected[pkg_id] = number[0]
+        min_num[pkg_id] = number[0]
+        stack.append(pkg_info)
+        
+        dep_rpms = set([pkg_info['name']])
+        
+        # Installation dependency analysis of rpm
+        for dep_tag in ['requires']: # 'recommends'
+            if pkg_info.get(dep_tag):
+                for req in pkg_info['requires']:
+                    choose = None
+                    #  Find dependency rpm based on capability/files
+                    if req['name'] in provides:
+                        # capability : [provide_rpm_1, provide_rpm_2, ... ] 
+                        cap_list = provides.get(req['name'])
+                        # Select the rpm that meets the condition (version)
+                        choose = _select_rpm(cap_list, req)
+                    elif req['name'] in files:
+                        choose = pkg_dict.get(files.get(req['name'])[0])
+                        
+                    if choose:
+                        # add forward/backward reference
+                        _create_reference(pkg_info, choose)
+                        
+                        if selected[choose['id']] == 0:
+                            dep_rpms.update(_analyze_dep(choose))
+                            min_num[pkg_id] = min(min_num[pkg_id], min_num[choose['id']])
+                        elif scc_id[choose['id']] == 0: 
+                            # cross edge that can not be ignored
+                            min_num[pkg_id] = min(min_num[pkg_id], min_num[choose['id']])
+                    else:
+                        # the rpm does not exists
+                        # TODO: Error handle
+                        logger.info('the capability(%s) does not exist. should be checked for error' % req['name'])
+                        
+        if min_num[pkg_id] == selected[pkg_id]:
+            # scc(strong connected components)
+            _make_scc(pkg_id)
+        
+        return dep_rpms
+            
+    # recipe/repo
     if not recipe or not repoinfo:
         return []
     
@@ -104,12 +251,12 @@ def get_installed_packages(recipe, repoinfo, pkg_group):
     
     # check groups/extraPackages
     group_set = set([])
-    extrapkg_set = set([])
+    pkg_set = set([])
     for g in [default, platform, config]:
         if g.has_key('Groups'):
             group_set.update(g.get('Groups'))
         if g.has_key('ExtraPackages'):
-            extrapkg_set.update(g.get('ExtraPackages'))
+            pkg_set.update(g.get('ExtraPackages'))
     group_dict = dict.fromkeys(group_set)
     
     # parsing group.xml
@@ -120,7 +267,6 @@ def get_installed_packages(recipe, repoinfo, pkg_group):
         raise TICError('primary.xml syntax error. %s', e)
     
     # Convert groups to packages
-    pkg_set = set([])
     for elm in root.findall('group'):
         group_name = elm.find('name').text
         if group_dict.has_key(group_name):
@@ -130,12 +276,30 @@ def get_installed_packages(recipe, repoinfo, pkg_group):
                 plist.append(pkgreq.text)
             pkg_set.update(set(plist))
     
-    # set up required package from group packages
-    pkg2id = pkg_group.get('pkg2id')
-    pkg_list = pkg_group.get('pkg_list')
-    for pkg in pkg_set:
-        pkg_id = pkg2id.get(pkg)
-        if pkg_id and pkg_list[pkg_id].get('dependency'):
-            extrapkg_set.update(set(pkg_list[pkg_id].get('dependency')))
+    pkg_dict = pkg_group.get('pkg_dict')
+    provides = pkg_group.get('provides')
+    files = pkg_group.get('files')
+    groups = pkg_group.get('groups')
     
-    return list(extrapkg_set)
\ No newline at end of file
+    stack = []
+    number = [0]    # for pkg count
+    scc_num = [0]   # for scc count
+    group_num = [0]     # for group count
+    scc_id = [0] * len(pkg_dict)
+    min_num = [0] * len(pkg_dict)
+    selected = [0] * len(pkg_dict)
+    install_rpm = set([])
+    
+    for pkg_name in pkg_set:
+        pkg_info = pkg_dict.get(pkg_name)
+        
+        # TODO: temporary code (Define capability in group)
+        if not pkg_info:
+            pro = provides.get(pkg_name)[0]
+            pkg_info = pkg_dict.get(pro['name'])
+            
+        pkg_info['selfChecked'] = True;
+        if selected[pkg_info['id']] == 0:
+            install_rpm.update(_analyze_dep(pkg_info))
+            
+    return list(install_rpm)
\ No newline at end of file
index e9049d4..c36f89f 100644 (file)
@@ -54,24 +54,24 @@ def get_default_recipe():
             Part='mobile-mbr',
             UserGroups='audio,video',
             Groups=[
-                'Generic Base',
-                'Mobile Base',
-                'Mobile Console Tools',
-                'Mobile Adaptation',
-                'Mobile Wayland',
-                'Mobile Middleware',
-                'Mobile Applications',
-                'Generic Multimedia',
-                'Mobile Multimedia',
-                'Generic Desktop Applications',
-                'Mobile Dali',
-                'Mobile EFL',
-                'Mobile Enlightenment',
-                'Mobile Input Framework',
-                'Mobile Connectivity Framework',
-                'Mobile Bluetooth',
-                'Mobile Web Framework',
-                'Mobile Telephony'],
+                'Generic Base'],
+                'Mobile Base',
+                'Mobile Console Tools',
+                'Mobile Adaptation',
+                'Mobile Wayland',
+                'Mobile Middleware',
+                'Mobile Applications',
+                'Generic Multimedia',
+                'Mobile Multimedia',
+                'Generic Desktop Applications',
+                'Mobile Dali',
+                'Mobile EFL',
+                'Mobile Enlightenment',
+                'Mobile Input Framework',
+                'Mobile Connectivity Framework',
+                'Mobile Bluetooth',
+                'Mobile Web Framework',
+                'Mobile Telephony'],
             PostScripts=[],
             Repos= [],
             NoChrootScripts=[]
@@ -87,7 +87,8 @@ def get_default_recipe():
                 Mic2Options= '-f loop --pack-to=@NAME@.tar.gz',
                 FileName= 'mobile-emulator64-wayland',
                 Repos=['mobile-emulator64-wayland', 'base_emulator64'],
-                Groups=['Mobile Adaptation Emulator'],
+                Groups=[],
+#                 Groups=['Mobile Adaptation Emulator'],
                 ExtraPackages= [],
                 RemovePackages=[]
             )
index 80bccc0..5fcfcdf 100644 (file)
@@ -21,7 +21,6 @@
 
 from lxml import etree
 from tic.utils.error import TICError
-import os
 import logging
 import re
 
@@ -31,6 +30,17 @@ class RepodataParser(object):
         self.repodata_list = repodata_list
         
     def _xml_parse(self, pkg_group, root, tag_dic):
+        
+        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']
+                
         logger = logging.getLogger(__name__)
         meta_prefix = 'building-block'
         meta_prefix_root = 'building-block-root-'
@@ -39,29 +49,26 @@ class RepodataParser(object):
         meta_sub1_pattern = re.compile("(?P<root>.+)-(?P<sub1>.+)")
         meta_sub2_pattern = re.compile("(?P<root>.+)-(?P<sub1>.+)-(?P<sub2>.+)")
 
-        pkg_list = pkg_group.get('pkg_list')
-        pkg2id = pkg_group.get('pkg2id')
-        provides2id = pkg_group.get('provides2id')
-        file2id = pkg_group.get('file2id')
+        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_list)
+        pkg_id = len(pkg_dict)
         
         for pkg in root.findall(tag_dic['package']):
-                    
             pkg_name = pkg.findtext(tag_dic['name'])
             
             # check whether a package is duplicated. 
-            if pkg_name in pkg2id:
+            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['selfChecked'] = False # for web-ui tree
             
             # Parsing meta-pkg using meta naming rule
             meta_match = meta_pattern.search(pkg_info['name'])
@@ -81,10 +88,13 @@ class RepodataParser(object):
                                               ''.join([meta_prefix_root, sub2_match.group('root')]),
                                               ''.join([meta_prefix_sub1, sub2_match.group('root'),'-', sub2_match.group('sub1')])])
                     pkg_info['meta'] = 'sub2'
-                pkg_info['nodes'] = []
             
             pkg_info['arch'] = pkg.findtext(tag_dic['arch'])
-            pkg_info['version'] = pkg.find(tag_dic['version']).attrib['ver']
+            #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'])
@@ -99,38 +109,43 @@ 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 = {}
+                        require['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']
+                        _set_version(provide, rpm)
                         
-                        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 = {}
+                        conflict['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 = {}
+                        recommend['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 = []
@@ -140,17 +155,14 @@ class RepodataParser(object):
                 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):
@@ -196,6 +208,7 @@ 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
 
@@ -214,13 +227,14 @@ class RepodataParser(object):
         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-block-root-metapkg</name><arch>aarch64</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="aarch64/SDL2-2.0.4-4.2.aarch64.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-block-sub1-metapkg-extsub1" /><rpm:entry name="building-block-sub1-metapkg-extsub2" /></rpm:requires></format></package><package type="rpm"><name>building-block-sub1-metapkg-extsub1</name><arch>aarch64</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="aarch64/SDL2-2.0.4-4.2.aarch64.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-block-sub2-metapkg-extsub1-exts1sub1" /></rpm:requires></format></package><package type="rpm"><name>building-block-sub2-metapkg-extsub1-exts1sub1</name><arch>aarch64</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="aarch64/SDL2-2.0.4-4.2.aarch64.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-block-sub2-metapkg-extsub1-exts1sub2</name><arch>aarch64</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="aarch64/SDL2-2.0.4-4.2.aarch64.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-block-sub1-metapkg-extsub2</name><arch>aarch64</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="aarch64/SDL2-2.0.4-4.2.aarch64.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-block-sub2-metapkg-extsub2-exts2sub1" /></rpm:requires></format></package><package type="rpm"><name>building-block-sub2-metapkg-extsub2-exts2sub1</name><arch>aarch64</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="aarch64/SDL2-2.0.4-4.2.aarch64.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-block-sub2-metapkg-extsub2-exts2sub2</name><arch>aarch64</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="aarch64/SDL2-2.0.4-4.2.aarch64.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></metadata>'
+        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-block-root-metapkg</name><arch>aarch64</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="aarch64/SDL2-2.0.4-4.2.aarch64.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-block-sub1-metapkg-extsub1" /><rpm:entry name="building-block-sub1-metapkg-extsub2" /></rpm:requires></format></package><package type="rpm"><name>building-block-sub1-metapkg-extsub1</name><arch>aarch64</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="aarch64/SDL2-2.0.4-4.2.aarch64.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-block-sub2-metapkg-extsub1-exts1sub1" /></rpm:requires></format></package><package type="rpm"><name>building-block-sub2-metapkg-extsub1-exts1sub1</name><arch>aarch64</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="aarch64/SDL2-2.0.4-4.2.aarch64.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-block-sub2-metapkg-extsub1-exts1sub2</name><arch>aarch64</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="aarch64/SDL2-2.0.4-4.2.aarch64.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-block-sub1-metapkg-extsub2</name><arch>aarch64</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="aarch64/SDL2-2.0.4-4.2.aarch64.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-block-sub2-metapkg-extsub2-exts2sub1" /><rpm:entry name="SDL2" /></rpm:requires></format></package><package type="rpm"><name>building-block-sub2-metapkg-extsub2-exts2sub1</name><arch>aarch64</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="aarch64/SDL2-2.0.4-4.2.aarch64.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-block-sub2-metapkg-extsub2-exts2sub2</name><arch>aarch64</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="aarch64/SDL2-2.0.4-4.2.aarch64.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))
         
-        pkg_group = dict(pkg_list=[],
-                         pkg2id={},
-                         provides2id={},
-                         file2id={},
+        #pkg_group = dict(pkg_list=[], pkg2id={}, provides2id={}, file2id={}, meta_info=dict(root=[], sub1=[], sub2=[]))
+        pkg_group = dict(pkg_dict={}, 
+                         provides={}, 
+                         files={}, 
+                         groups={},
                          meta_info=dict(root=[], sub1=[], sub2=[]))
         
         # parses the repodata (primary.xml)
@@ -228,6 +242,6 @@ class RepodataParser(object):
             self._xml_parse(pkg_group, xml_root, 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
index 261027b..586307d 100644 (file)
@@ -1,39 +1,45 @@
+import logging
 from operator import itemgetter
 
 
 def make_view_data(pkg_group):
+    logger = logging.getLogger(__name__)
     
-    def set_meta_require(pkg_info):
-        pkg_nodes = pkg_info.get('nodes')
-        for child in pkg_nodes:
-            if 'meta' in child:
-                # meta-pkg
-                refer_count[pkg2id[child['name']]] += 1
-                set_meta_require(child)
+    def set_meta_require(meta_info):
+        meta_nodes = meta_info.get('nodes')
+        for child_meta in meta_nodes:
+            refer_count[child_meta['id']] += 1
+            set_meta_require(child_meta)
             
-        req_list = pkg_info.get('requires')
+        req_list = pkg_dict[meta_info['text']].get('requires')
         if req_list is not None:
             for req in req_list:
-                req_id = req.get('id')
-                if req_id is not None:
-                    if 'meta' not in pkg_list[req_id]:
-                        pkg_nodes.append(pkg_list[req_id])
-                        refer_count[req_id] += 1
+                if provides.get(req['name']):
+                    provide_info = provides[req['name']][0]
+                    pkg_name = provide_info['name']
+                    if 'meta' not in pkg_dict[pkg_name]:
+                        child_pkg = pkg_dict[pkg_name]
+                        meta_nodes.append(make_node(child_pkg))
+                        refer_count[child_pkg['id']] += 1
                     else:
-                        #TODO: The meta-pkg of the other group are excluded.
+                        # The meta-pkg of the other group are excluded.
+                        # the dependent package does not exist.
                         pass
-                else:
-                    #TODO: the dependent package does not exist.
-                    pass
+                
+    def make_node(pkg_info):
+        return dict(id=pkg_info['id'], text=pkg_info['name'], nodes=[])
             
+    # view_data for tree view on web-ui
     view_data = []
+    # temporary dictionary for referencing meta
+    view_ref = {}
     
     if not pkg_group:
         return view_data
       
-    pkg_list = pkg_group['pkg_list']
-    pkg2id = pkg_group.get('pkg2id')
-    refer_count = [0] * len(pkg_list)
+    pkg_dict = pkg_group['pkg_dict']
+    provides = pkg_group['provides']
+    refer_count = [0] * len(pkg_dict)
     
     # sort by pkg_name ([0]:name, [1]:root, [2]:sub1)
     meta_info = pkg_group.get('meta_info')
@@ -41,48 +47,52 @@ def make_view_data(pkg_group):
     meta_info['sub1'] = sorted(meta_info['sub1'], key=itemgetter(0))
     meta_info['sub2'] = sorted(meta_info['sub2'], key=itemgetter(0))
     
-    # make tree of meta information
+    # make tree of meta
     for root in meta_info['root']:
-        pkg_id = pkg2id[root[0]]
-        view_data.append(pkg_list[pkg_id])
+        root_node = make_node(pkg_dict[root[0]])
+        view_ref[root[0]] = root_node
+        view_data.append(root_node)
         
     for sub1 in meta_info['sub1']:
-        pkg_id = pkg2id[sub1[0]]
-        if sub1[1] in pkg2id:
-            root_id = pkg2id[sub1[1]]
-            root_nodes = pkg_list[root_id].get('nodes')
-            root_nodes.append(pkg_list[pkg_id])
+        sub1_node = make_node(pkg_dict[sub1[0]])
+        view_ref[sub1[0]] = sub1_node
+        # search root
+        if sub1[1] in view_ref:
+            # add to root node
+            view_ref[sub1[1]]['nodes'].append(sub1_node)
         else:
-            # If root-meta doest not exist, sub1 is added to top-level
-            view_data.append(pkg_list[pkg_id])
+            # If root-meta does not exist, sub1 is added to top-level
+            view_data.append(sub1_node)
             
     for sub2 in meta_info['sub2']:
-        pkg_id = pkg2id[sub2[0]]
-        if sub2[1] in pkg2id: # root
-            if sub2[2] in pkg2id: # sub1
-                sub1_id = pkg2id[sub2[2]]
-                sub1_nodes = pkg_list[sub1_id].get('nodes')
-                sub1_nodes.append(pkg_list[pkg_id])
-            else:
-                # If sub1-meta doest not exist, sub2 is added to child of root-meta
-                root_id = pkg2id[sub2[1]]
-                root_nodes = pkg_list[root_id].get('nodes')
-                root_nodes.append(pkg_list[pkg_id])
+        sub2_node = make_node(pkg_dict[sub2[0]])
+        view_ref[sub2[0]] = sub2_node
+        # search sub1
+        if sub2[2] in view_ref:
+            view_ref[sub2[2]]['nodes'].append(sub2_node)
+        # search root    
+        elif sub2[1] in view_ref: 
+            # If sub1-meta does not exist, sub2 is added to child of root-meta
+            view_ref[sub2[1]]['nodes'].append(sub2_node)
         else:
-            # If root-meta doest not exist, sub2 is added to top-level
-            view_data.append(pkg_list[pkg_id])
+            # If root-meta/sub1-meta does not exist, sub1 is added to top-level
+            view_data.append(sub2_node)
     
+    # configure meta dependency tree (requires)
     for meta_pkg in view_data:
-        refer_count[pkg2id[meta_pkg['name']]] += 1
+        refer_count[meta_pkg['id']] += 1
         set_meta_require(meta_pkg)
     
+    #The remaining rpms are grouped into a MISC tree
     misc_info = {}
     misc_info['text'] = 'MISC'
     misc_info['nodes'] = []
-    
-    for pkg_id in range(len(pkg_list)):
-        if refer_count[pkg_id] == 0:
-            misc_info['nodes'].append(pkg_list[pkg_id])
+    for k, v in pkg_dict.iteritems():
+        # Pkg is not referenced from Meta-pkg
+        if refer_count[v['id']] == 0:
+            misc_info['nodes'].append(make_node(v))
+            
+    logger.info('meta: %d, misc: %d', len(view_ref), len(misc_info['nodes']))
     
     view_data.append(misc_info)
     
index 2d8f7b2..176313e 100644 (file)
@@ -99,9 +99,9 @@ def main(argv):
         return 0
     except error.TICError as err:
         logger.error(err)
-    except Exception as ex:
-        logger.error(ex)
-        return 2
+    except Exception as ex:
+        logger.error(ex)
+        return 2
     
 if __name__ == "__main__":
     log.setup('tic')