[TIC-CORE] fix the issue that cannot analyze the install-dependency 48/126348/1
authorChulwoo Shin <cw1.shin@samsung.com>
Fri, 21 Apr 2017 05:01:23 +0000 (14:01 +0900)
committerChulwoo Shin <cw1.shin@samsung.com>
Fri, 21 Apr 2017 05:01:23 +0000 (14:01 +0900)
- fix the issue that cannot analyze the install-dependency
- support recommends tag

Change-Id: I0204c9b0c372d8f9735e19b7da7640c3bbc20d21
Signed-off-by: Chulwoo Shin <cw1.shin@samsung.com>
tic/command.py
tic/dependency.py

index 7e3598d..c66743a 100644 (file)
@@ -77,7 +77,9 @@ def analyze(recipe_list):
     start_time = misc.get_timestamp()
     # Make a data for TIC (Tizen image creation)
     view_data = make_view_data(pkg_group)
+    logger.info('time to create view-tree: %d ms', misc.get_timestamp() - start_time)
     # analyze install-dependency
+    start_time = misc.get_timestamp()
     inst_packages = get_installed_packages(recipe_info, repoinfo, pkg_group)
     logger.info('installed package: %d', len(inst_packages))
     logger.info('time to analyze dependency: %d ms', misc.get_timestamp() - start_time)
@@ -89,7 +91,7 @@ def analyze(recipe_list):
                        'groups': pkg_group.get('groups'),
                        'conflicts': pkg_group.get('conflicts')},
               'recipes': recipe_parser.getRepositories(),
-              'defaultpackages': inst_packages}
+              'installpackages': inst_packages}
     return result
 
 def imports(recipe_list):
index 19864b9..fefc53b 100644 (file)
@@ -36,7 +36,7 @@ def get_installed_packages(recipe, repoinfo, pkg_group):
         # 2-1. Choose the default rpm or the selected rpm
         for fname in fileList:
             file_info = pkg_dict.get(fname)
-            if fname in pkg_set or selected[file_info['id']] >= 1:
+            if fname in required_inst_rpms or selected[file_info['id']] >= 1:
                 return file_info
         # 2-2. Choose the rpm (it does not conflitcs)
         for fname in fileList:
@@ -65,12 +65,12 @@ def get_installed_packages(recipe, repoinfo, pkg_group):
         
         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        
         for pro in provide_list:
-            provide_info = pkg_dict.get(pro.get('name'))
-            if provide_info['name'] in pkg_set or selected[provide_info['id']] >= 1:
+            provide_info = pkg_dict.get(pro['name'])
+            if provide_info['name'] in required_inst_rpms or selected[provide_info['id']] >= 1:
                 return provide_info
 
         # 2-2. Choose the defualt profile
@@ -115,24 +115,24 @@ def get_installed_packages(recipe, repoinfo, pkg_group):
     def _create_reference(pkg1, pkg2):
         # duplicate check
         if pkg1.get('forward') and pkg2['name'] in pkg1.get('forward'):
-            return
-        
+            return False
         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']]
+        return True
             
     def _make_scc(pkg_id):
         scc_num[0] += 1
         scc_list = []
         # stack is not empty
-        while stack:
-            pkg = stack.pop()
+        while rpm_stack:
+            pkg = rpm_stack.pop()
             scc_id[pkg['id']] = scc_num[0]
             scc_list.append(pkg)
             if pkg_id == pkg['id']:
@@ -158,8 +158,8 @@ def get_installed_packages(recipe, repoinfo, pkg_group):
                     for con in conflicts[pro['name']]:
                         if not con['data'].get('ver') or meetRequireVersion(con['data'], pro):
                             #package conflict
-                            logger.info('Conflict %s and %s' % (pkg_info['name'], con['name']))
-                            return True
+                            #logger.info('Conflict Case-1) %s is conflict with %s' % (pkg_info['name'], con['name']))
+                            return pkg_dict.get(con['name'])
         
         #2. If the conflict package defined by node is installed
         if pkg_info.get('conflicts') is not None:
@@ -168,16 +168,26 @@ def get_installed_packages(recipe, repoinfo, pkg_group):
                     for pro in provides[con['name']]:
                         pkg = pkg_dict[pro['name']]
                         if selected[pkg['id']] != 0:
-                            if not con.get('ver') or meetRequireVersion(con, pkg['version']):
-                                logger.info('Conflict %s and %s' % (pkg_info['name'], pkg['name']))
-                                return True
+                            if not con.get('ver') or meetRequireVersion(con, pro['data']):
+                                #logger.info('Conflict Case-2) %s is conflict with %s' % (pkg['name'], pkg_info['name']))
+                                return pkg
                 elif con['name'] in pkg_dict:
                     pkg = pkg_dict[con['name']]
                     if selected[pkg['id']] != 0:
                         if not con.get('ver') or meetRequireVersion(con, pkg['version']):
-                            logger.info('Conflict %s and %s' % (pkg_info['name'], pkg['name']))
-                            return True
-        return False
+                            #logger.info('Conflict Case-2) %s is conflict with %s' % (pkg['name'], pkg_info['name']))
+                            return pkg
+        return None
+    
+    def _remove_conflicts(pkg_info):
+        if pkg_info.get('conflicts'):
+            for con in pkg_info['conflicts']:
+                if con['name'] in conflicts:
+                    c_list = conflicts[con['name']]
+                    for i in range(len(c_list)):
+                        if c_list[i]['name'] == pkg_info['name']:
+                            del c_list[i]
+                            break
     
     def _add_conflicts(pkg_info):
         if pkg_info.get('conflicts'):
@@ -185,7 +195,7 @@ def get_installed_packages(recipe, repoinfo, pkg_group):
                 if not con['name'] in conflicts:
                     conflicts[con['name']] = []
                 conflicts[con['name']].append(dict(name=pkg_info['name'], data=con))
-                logger.info('%s add conflict package : %s' % (pkg_info['name'], con['name']))
+                #logger.info('%s add conflict package : %s' % (pkg_info['name'], con['name']))
     
     def _analyze_dep(pkg_info):
         if not pkg_info:
@@ -195,14 +205,14 @@ def get_installed_packages(recipe, repoinfo, pkg_group):
         number[0] += 1
         selected[pkg_id] = number[0]
         min_num[pkg_id] = number[0]
-        stack.append(pkg_info)
+        rpm_stack.append(pkg_info)
         
         dep_rpms = set([pkg_info['name']])
         
         # check for conflicts
         if _check_conflicts(pkg_info):
             progress['status'] = False
-            stack.pop()
+            rpm_stack.pop()
             return
         
         # add rpms into conflicts table.
@@ -218,7 +228,6 @@ def get_installed_packages(recipe, repoinfo, pkg_group):
                         continue
                     #  Find dependency rpm based on capability/files
                     if req['name'] in provides:
-                        # capability : [provide_rpm_1, provide_rpm_2, ... ]
                         # Select the rpm that meets the condition (version)
                         if dep_tag == Dependency.REQUIRES:
                             choose = _select_rpm(provides[req['name']], req, pkg_info.get('recommends'))
@@ -231,22 +240,18 @@ def get_installed_packages(recipe, repoinfo, pkg_group):
                     
                     if dep_tag == Dependency.RECOMMENDS:
                         # A Recommends B: B is installed when A is installed and B has no conflicts.
-                        if not choose or _check_conflicts(choose):
-                            logger.info('%s recommended by %s is ignored for selection (Conflict)' % (req['name'], pkg_info['name']))
+                        if not choose or _check_conflicts(choose) is not None:
+                            #logger.info('%s recommended by %s is ignored for selection (Conflict)' % (req['name'], pkg_info['name']))
                             continue
 
                     if choose:
                         # add forward/backward reference
                         _create_reference(pkg_info, choose)
                         
-                        # ref count
-                        # choose['ref_cnt'] = {req['name']: 1}
-                        
                         if selected[choose['id']] == 0:
                             dep_set = _analyze_dep(choose)
                             if not progress['status']:
                                 break
-                            
                             dep_rpms.update(dep_set)
                             min_num[pkg_id] = min(min_num[pkg_id], min_num[choose['id']])
                         elif scc_id[choose['id']] == 0:
@@ -257,7 +262,6 @@ def get_installed_packages(recipe, repoinfo, pkg_group):
                         logger.info(configmgr.message['dependency_not_exist'] % (req['name'], pkg_info['name']))
                         progress['status'] = False
                         break
-            
             if not progress['status']:
                 break
         if min_num[pkg_id] == selected[pkg_id]:
@@ -342,7 +346,128 @@ def get_installed_packages(recipe, repoinfo, pkg_group):
             node['group'] = None
         # delete conflict data from conflicts dict
         _delete_conflictdata(node)
-            
+    
+    def _add_refer(node, require):
+        if node['name'] not in require_refer:
+            require_refer[node['name']] = {}
+        require_refer[node['name']][require['name']] = 1
+
+    def _get_pkg_info(pkg_name):
+        if pkg_name in pkg_dict:
+            return pkg_dict[pkg_name]
+        if pkg_name in provides:
+            pro = provides[pkg_name][0]
+            return pkg_dict[pro['name']]
+        return None
+    
+    def is_compatible_pkg (origin_pkg, new_pkg):
+        if origin_pkg['name'] in require_refer:
+            for r_name in require_refer[origin_pkg['name']]:
+                matched = False
+                if new_pkg.get('provides'):
+                    for pro in new_pkg['provides']:
+                        if r_name == pro['name']:
+                            matched = True
+                            break;
+                if not matched and new_pkg.get('file'): 
+                    for fname in new_pkg['file']:
+                        if r_name == fname:
+                            matched = True
+                            break;
+                if not matched:
+                    return False
+        return True
+    
+    def _is_selfchecked(pkg_info):
+        if pkg_info.get('selfChecked'):
+            return True
+        return False
+    
+    def _add_cap_info(pkg_info):
+        if not pkg_info.get('provides'):
+            return
+        for pro in pkg_info['provides']:
+            if not capabilities.get(pro['name']):
+                capabilities[pro['name']] = []
+            capabilities[pro['name']].append(pkg_info)
+        
+    def _remove_dep_rpm(select_list):
+        if not select_list:
+            return
+        for rpm_info in select_list:
+            selected[rpm_info['id']] = 0
+            _delete_conflictdata(rpm_info)
+    
+    def _check_dep_validation(pkg_info, select_list):
+        if not pkg_info:
+            return False
+        pkg_id = pkg_info['id']
+        number[0] += 1
+        selected[pkg_id] = number[0]
+        select_list.append(pkg_info)
+        
+        # add rpm's capabilities  
+        _add_cap_info(pkg_info)
+        
+        # check for conflicts
+        conflict_pkg = _check_conflicts(pkg_info)
+        if conflict_pkg is not None:
+            if not _is_selfchecked(conflict_pkg) and is_compatible_pkg(conflict_pkg, pkg_info):
+                logger.info(pkg_info['name'] + ' and ' + conflict_pkg['name'] + ' is compatible')
+                comp_rpms.add(pkg_info['name'])
+                #_remove_conflicts(conflict_pkg)
+            else:
+                logger.info('###_1 ' + pkg_info['name'] + ' is conflict with ' + conflict_pkg['name'])
+                return False
+        elif pkg_info.get('provides'):
+            for pro in pkg_info['provides']:
+                if pro['name'] in capabilities:
+                    for c_info in capabilities[pro['name']]:
+                        if c_info['id'] == pkg_info['id']:
+                            continue
+                        if not _is_selfchecked(c_info) and is_compatible_pkg(c_info, pkg_info):
+                            logger.info('###_2 ' + pkg_info['name'] + ' and ' + c_info['name'] + ' is compatible')
+                            comp_rpms.add(pkg_info['name'])
+
+        # add rpms into conflicts table.
+        _add_conflicts(pkg_info)
+        # Installation dependency analysis of rpm
+        for dep_tag in [Dependency.REQUIRES, Dependency.RECOMMENDS]:
+            if pkg_info.get(dep_tag):
+                for req in pkg_info.get(dep_tag):
+                    choose = None
+                    # self-reference (e.g. vim-base)
+                    if req['name'] == pkg_info['name']:
+                        continue
+                    if req['name'] in provides:
+                        if dep_tag == Dependency.REQUIRES:
+                            choose = _select_rpm(provides[req['name']], req, pkg_info.get('recommends'))
+                        else:
+                            choose = _select_rpm(provides[req['name']], req)
+                    elif req['name'] in files:
+                        choose = _select_rpm_from_files(files[req['name']], req)
+                    elif req['name'] in pkg_dict:
+                        choose = pkg_dict.get(req['name'])
+                        
+                    if dep_tag == Dependency.RECOMMENDS:
+                        # A Recommends B: B is installed when A is installed and B has no conflicts.
+                        if not choose or _check_conflicts(choose) is not None:
+                            #logger.info('%s recommended by %s is ignored for selection (Conflict)' % (req['name'], pkg_info['name']))
+                            continue
+
+                    if choose:
+                        # add refer count, only requires
+                        if dep_tag == Dependency.REQUIRES:
+                            _add_refer(choose, req)
+
+                        if selected[choose['id']] == 0:
+                            if not _check_dep_validation(choose, select_list):
+                                return False
+                    else:
+                        # the rpm does not exists
+                        return False
+        return True
+
     # recipe/repo
     if not recipe or not repoinfo:
         return []
@@ -365,7 +490,6 @@ def get_installed_packages(recipe, repoinfo, pkg_group):
                 except etree.XMLSyntaxError as e:
                     logger.info(e)
                     raise TICError(configmgr.message['xml_parse_error'] % ('group.xml', repo['baseurl']))
-                
                 # Convert groups to packages
                 for elm in root.findall('group'):
                     group_name = elm.find('name').text
@@ -376,14 +500,13 @@ def get_installed_packages(recipe, repoinfo, pkg_group):
                             plist.append(pkgreq.text)
                         pkg_set.update(set(plist))
                         group_set.discard(group_name);
-    
+
     pkg_dict = pkg_group.get('pkg_dict')
     provides = pkg_group.get('provides')
     files = pkg_group.get('files')
     groups = pkg_group.get('groups')
     conflicts = pkg_group.get('conflicts')
-    
-    stack = []
+      
     number = [0]    # for pkg count
     scc_num = [0]   # for scc count
     group_num = [0]     # for group count
@@ -394,25 +517,60 @@ def get_installed_packages(recipe, repoinfo, pkg_group):
     install_rpm = set([])
     progress = dict(status=True, message=None)
     
+    capabilities = {}
+    require_refer = {}
+    candidate_rpms = set([])
+    # add rpms to install
+    select_rpms = set([])
+    # add reference value for rpm selection
+    required_inst_rpms = pkg_set.copy()
+
+    # 1. Check whether dependencies of rpm are available.
     for pkg_name in pkg_set:
-        progress['status'] = True
-        selected_pkg = pkg_dict.get(pkg_name)
-        
-        if not selected_pkg:
-            if provides.get(pkg_name):
-                pro = provides.get(pkg_name)[0]
-                selected_pkg = pkg_dict.get(pro['name'])
+        selected_pkg = _get_pkg_info(pkg_name)
+        if selected_pkg:
+            if selected[selected_pkg['id']] == 0:
+                select_list = []
+                comp_rpms = set([])
+                if _check_dep_validation(selected_pkg, select_list):
+                    select_rpms.add(pkg_name)
+                    candidate_rpms.update(comp_rpms)
+                else:
+                    # case: conflict or rpm does not exist
+                    _remove_dep_rpm(select_list)
+                    logger.info('The %s install-dependencies are not valid, could not selected it' % pkg_name)
             else:
-                logger.info(configmgr.message['package_not_exist'] % pkg_name)
-                continue
+                select_rpms.add(pkg_name)
+        else:
+            logger.info(configmgr.message['package_not_exist'] % pkg_name)
+    
+    # init conflict table and reference
+    number[0] = 0
+    selected = [0] * len(pkg_dict)
+    conflicts.clear()
+    required_inst_rpms.clear()
+    required_inst_rpms.update(select_rpms)
+    required_inst_rpms.update(candidate_rpms)
+    
+    print(candidate_rpms)
 
-        selected_pkg['selfChecked'] = True
-        if selected[selected_pkg['id']] == 0:
-            dep_set = _analyze_dep(selected_pkg)
-            if progress['status']:
-                install_rpm.update(dep_set)
-            else:
-                # delete forward/backward reference
-                group_visited = {}
-                _remove_reference(None, selected_pkg)
+    # 2. Analyze rpm installation dependencies.
+    for pkg_name in select_rpms:
+        progress['status'] = True
+        selected_pkg = _get_pkg_info(pkg_name)
+        if selected_pkg:
+            rpm_stack = []
+            selected_pkg['selfChecked'] = True
+            if selected[selected_pkg['id']] == 0:
+                inst_rpms = _analyze_dep(selected_pkg)
+                if progress['status']:
+                    install_rpm.update(inst_rpms)
+                else:
+                    # Error Case
+                    logger.info("[Dependency Issue] Could not install the %s" % selected_pkg)
+                    # delete forward/backward reference
+                    group_visited = {}
+                    _remove_reference(None, selected_pkg)
+        else:
+            logger.info(configmgr.message['package_not_exist'] % pkg_name)
     return list(install_rpm)