From cbcc03e19e5d310f304086b1bb0c0f0ac5c9d6f2 Mon Sep 17 00:00:00 2001 From: Chulwoo Shin Date: Fri, 3 Feb 2017 02:05:24 +0900 Subject: [PATCH] [TIC-CORE] provide conflict function - If there is a matching package the package cannot be installed Change-Id: I35127a889b667bff405a0c6cc5a6c9d6e6d11809 Signed-off-by: Chulwoo Shin --- tic/command.py | 9 +- tic/dependency.py | 215 ++++++++++++++++++++++++++++++++++++-------- tic/parser/recipe_parser.py | 25 ++---- tic/parser/repo_parser.py | 6 +- tic/server/tic_server.py | 6 +- 5 files changed, 198 insertions(+), 63 deletions(-) diff --git a/tic/command.py b/tic/command.py index 024c390..ea0e4e6 100644 --- a/tic/command.py +++ b/tic/command.py @@ -85,10 +85,11 @@ def analyze(repo_list, recipe_list=None): start_time = current_milli_time() result = {'view': view_data, - 'data': {'packages': pkg_group['pkg_dict'], - 'provides': pkg_group['provides'], - 'files': pkg_group['files'], - 'groups': pkg_group.get('groups')}, + 'data': {'packages': 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')}, 'repos': repos, 'defaultpackages': inst_packages} diff --git a/tic/dependency.py b/tic/dependency.py index fcca356..5698375 100644 --- a/tic/dependency.py +++ b/tic/dependency.py @@ -107,8 +107,17 @@ def get_installed_packages(recipe, repoinfo, pkg_group): if not req.get('rel'): rel = None return rpm.labelCompare((req.get('epoch'), req.get('ver'), req.get('rel')), (epoch, ver, rel)) + def _meetRequireVersion(req_ver, cmp_ver): + cmp_ret = _compare_req_cap_ver(req_ver, cmp_ver) + if cmp_ret == 0 and (req_ver['flags'] == 'EQ' or req_ver['flags'] == 'GE' or req_ver['flags'] == 'LE'): + return True + elif cmp_ret == 1 and (req_ver['flags'] == 'LT' or req_ver['flags'] == 'LE'): + return True + elif cmp_ret == -1 and (req_ver['flags'] == 'GT' or req_ver['flags'] == 'GE'): + return True + return False + 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')) @@ -117,13 +126,7 @@ def get_installed_packages(recipe, repoinfo, pkg_group): # 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'): + if _meetRequireVersion(require, provide.get('data')): provide_list.append(provide) else: provide_list = capability @@ -140,19 +143,26 @@ def get_installed_packages(recipe, repoinfo, pkg_group): # 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: + 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] + max_ver = None + for i in range(0, len(provide_list)): + if not _check_conflicts(pkg_dict.get(provide_list[i]['name'])): + if max_ver: + 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] + else: + max_ver = provide_list[i] - pkg_name = max_ver.get('name') - return pkg_dict.get(pkg_name) + # all of capability pkg are in conflict + if max_ver is None: + return pkg_dict.get(provide_list[i]['name']) + + return pkg_dict.get(max_ver.get('name')) def _create_reference(pkg1, pkg2): # duplicate check @@ -178,7 +188,7 @@ def get_installed_packages(recipe, repoinfo, pkg_group): scc_id[pkg['id']] = scc_num[0] scc_list.append(pkg) if pkg_id == pkg['id']: - break; + break # circular dependency if len(scc_list) > 1: @@ -191,13 +201,46 @@ def get_installed_packages(recipe, repoinfo, pkg_group): # { group_id = [ pkg_name_1, pkg_name_2 ], ... } groups[group_num[0]] = group_names - print(group_names) + + def _check_conflicts(pkg_info): + # 1. Check whether node can be installed + if pkg_info.get('provides') is not None: + for pro in pkg_info['provides']: + if pro['name'] in conflicts: + for con in conflicts[pro['name']]: + if not con['data'].get('ver') or _meetRequireVersion(con['data'], pro): + #package conflict + return True + #2. If the conflict package defined by node is installed + if pkg_info.get('conflicts') is not None: + for con in pkg_info['conflicts']: + if con['name'] in provides: + 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']): + return True + 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']): + return True + return False + + def _add_conflicts(pkg_info): + if pkg_info.get('conflicts'): + for con in pkg_info['conflicts']: + 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'])) + def _analyze_dep(pkg_info): if not pkg_info: return - pkg_id = pkg_info['id']; + pkg_id = pkg_info['id'] number[0] += 1 selected[pkg_id] = number[0] min_num[pkg_id] = number[0] @@ -205,40 +248,133 @@ def get_installed_packages(recipe, repoinfo, pkg_group): dep_rpms = set([pkg_info['name']]) + # check for conflicts + if _check_conflicts(pkg_info): + progress['status'] = False + stack.pop() + return + + # add rpms into conflicts table. + _add_conflicts(pkg_info) + # Installation dependency analysis of rpm for dep_tag in ['requires']: # 'recommends' if pkg_info.get(dep_tag): - for req in pkg_info['requires']: + for req in pkg_info.get(dep_tag): 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) + choose = _select_rpm(provides[req['name']], req) elif req['name'] in files: - choose = pkg_dict.get(files.get(req['name'])[0]) + choose = pkg_dict.get(files[req['name']][0]) + elif req['name'] in pkg_dict: + choose = pkg_dict.get(req['name']) if choose: - # add forward/backward reference - _create_reference(pkg_info, choose) - if selected[choose['id']] == 0: - dep_rpms.update(_analyze_dep(choose)) + 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: # cross edge that can not be ignored min_num[pkg_id] = min(min_num[pkg_id], min_num[choose['id']]) + + # add forward/backward reference + _create_reference(pkg_info, choose) else: # the rpm does not exists - # TODO: Error handle - logger.info('the capability(%s) does not exist. should be checked for error' % req['name']) - + logger.info('the capability(%s) needed by %s does not exist. should be checked for error' % (req['name'], pkg_info['name'])) + progress['status'] = False + break + + if not progress['status']: + break if min_num[pkg_id] == selected[pkg_id]: # scc(strong connected components) _make_scc(pkg_id) return dep_rpms + + def _check_circular_dep(node): + g_id = node.get('group') + g_pkg_list = groups[g_id] + g_dict = {} + + # Set dict for group + for pkgname in g_pkg_list: + g_dict[pkgname] = None + + for pkgname in g_pkg_list: + pkg = pkg_dict[pkgname] + # the node is selfchecked (the root node ignores selfchecked) + if stack[0]['id'] != pkg['id'] and pkg['selfChecked']: + return False + # check backward ref. + for bname in pkg.get('backward'): + # If node is Referenced by another node (Not a node in the group), + # unable to uncheck group nodes + if not bname in g_dict: + return False + + # init visited dict + group_visited[g_id] = {} + # delete backward reference of group node + for pkgname in g_pkg_list: + pkg = pkg_dict[pkgname] + pkg['backward'] = None; + group_visited[g_id][pkg['name']] = -1 + return True + + def _delete_conflictdata(node): + if node.get('conflicts'): + for con in node.get('conflicts'): + if con['name'] in conflicts: + con_list = conflicts[con['name']] + for i in range(len(con_list)): + if con_list[i]['name'] == node['name']: + del con_list[i] + break; + + def _remove_reference(parent, node): + if parent is not None: + # remove backward reference (parent) + if node.get('backward'): + for i in range(len(node['backward'])): + if node['backward'][i] == parent['name']: + del node['backward'][i] + break + # selfCheck node do not remove + if pkg_info.get('selfChecked'): + return + + if node.get('backward'): + if node.get('group') is None or _check_circular_dep(node): + return + + # the selected node is uncheckable + if node.get('group') and group_visited[node['group']]: + group_visited[node['group']][node['name']] = 1 + + # if selected node has forward references + if node.get('forward'): + for fname in node.get('forward'): + fnode = pkg_dict[fname] + + # If pkg has a circular dependency and is unchekcable, + # circular dep. pkgs can only be visited once + if fnode.get('group') and group_visited[fnode['group']][fnode['name']] == 1: + continue + + _remove_reference(node, fnode) + node['forward'] = None + node['group'] = None + # delete conflict data from conflicts dict + _delete_conflictdata(node) # recipe/repo if not recipe or not repoinfo: @@ -280,6 +416,7 @@ def get_installed_packages(recipe, repoinfo, pkg_group): 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 @@ -288,9 +425,12 @@ def get_installed_packages(recipe, repoinfo, pkg_group): scc_id = [0] * len(pkg_dict) min_num = [0] * len(pkg_dict) selected = [0] * len(pkg_dict) + group_visited = None install_rpm = set([]) + progress = dict(status=True, message=None) for pkg_name in pkg_set: + progress['status'] = True pkg_info = pkg_dict.get(pkg_name) # TODO: temporary code (Define capability in group) @@ -298,8 +438,13 @@ def get_installed_packages(recipe, repoinfo, pkg_group): pro = provides.get(pkg_name)[0] pkg_info = pkg_dict.get(pro['name']) - pkg_info['selfChecked'] = True; + pkg_info['selfChecked'] = True if selected[pkg_info['id']] == 0: - install_rpm.update(_analyze_dep(pkg_info)) - + dep_set = _analyze_dep(pkg_info) + if progress['status']: + install_rpm.update(dep_set) + else: + # delete forward/backward reference + group_visited = {} + _remove_reference(None, pkg_info) return list(install_rpm) \ No newline at end of file diff --git a/tic/parser/recipe_parser.py b/tic/parser/recipe_parser.py index c36f89f..971735a 100644 --- a/tic/parser/recipe_parser.py +++ b/tic/parser/recipe_parser.py @@ -53,25 +53,7 @@ def get_default_recipe(): Emulator64wayland=dict( 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'], + Groups=['Generic Base'], PostScripts=[], Repos= [], NoChrootScripts=[] @@ -89,11 +71,14 @@ def get_default_recipe(): Repos=['mobile-emulator64-wayland', 'base_emulator64'], Groups=[], # Groups=['Mobile Adaptation Emulator'], - ExtraPackages= [], + ExtraPackages= ['building-block-sub2-metapkg-extsub1-exts1sub1', 'building-block-sub2-metapkg-extsub1-exts1sub1', 'icecream'], RemovePackages=[] ) ], Repositories=[ +# dict(Name='mobile-emulator64-wayland', +# Url='http://download.tizen.org/live/devel:/Tizen:/Unified:/unmerged/standard/', +# Options='--ssl_verify=no'), dict(Name='mobile-emulator64-wayland', Url='http://download.tizen.org/snapshots/tizen/mobile/latest/repos/emulator64-wayland/packages/', Options='--ssl_verify=no'), diff --git a/tic/parser/repo_parser.py b/tic/parser/repo_parser.py index 5fcfcdf..2487336 100644 --- a/tic/parser/repo_parser.py +++ b/tic/parser/repo_parser.py @@ -121,6 +121,8 @@ class RepodataParser(object): provide = {} provide['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 provides_dict: provides_dict[rpm.attrib['name']].append({'name': pkg_name, 'data': provide}) @@ -227,7 +229,8 @@ class RepodataParser(object): tag_dic = self._get_tagname(xml_list[0]) #TODO: temporary code (should be deleted) - meta_string='building-block-root-metapkgaarch646f74666eb89e1addc4ce75e25f3e639bbfdd8798fc848a6b7027501070567dcfSimple DirectMedia LayerThis is the Simple DirectMedia Layer, a generic API that provides low level access to audio, keyboard, mouse, and display framebuffer across multiple platforms.http://www.libsdl.org/building-block-sub1-metapkg-extsub1aarch646f74666eb89e1addc4ce75e25f3e639bbfdd8798fc848a6b7027501070567dcfsub1-extensionThis is the Simple DirectMedia Layer, a generic API that provides low level access to audio, keyboard, mouse, and display framebuffer across multiple platforms.http://www.libsdl.org/building-block-sub2-metapkg-extsub1-exts1sub1aarch646f74666eb89e1addc4ce75e25f3e639bbfdd8798fc848a6b7027501070567dcfsub2-extensionThis is the Simple DirectMedia Layer, a generic API that provides low level access to audio, keyboard, mouse, and display framebuffer across multiple platforms.http://www.libsdl.org/building-block-sub2-metapkg-extsub1-exts1sub2aarch646f74666eb89e1addc4ce75e25f3e639bbfdd8798fc848a6b7027501070567dcfsub2-extensionThis is the Simple DirectMedia Layer, a generic API that provides low level access to audio, keyboard, mouse, and display framebuffer across multiple platforms.http://www.libsdl.org/building-block-sub1-metapkg-extsub2aarch646f74666eb89e1addc4ce75e25f3e639bbfdd8798fc848a6b7027501070567dcfsub1-extensionThis is the Simple DirectMedia Layer, a generic API that provides low level access to audio, keyboard, mouse, and display framebuffer across multiple platforms.http://www.libsdl.org/building-block-sub2-metapkg-extsub2-exts2sub1aarch646f74666eb89e1addc4ce75e25f3e639bbfdd8798fc848a6b7027501070567dcfsub2-extensionThis is the Simple DirectMedia Layer, a generic API that provides low level access to audio, keyboard, mouse, and display framebuffer across multiple platforms.http://www.libsdl.org/building-block-sub2-metapkg-extsub2-exts2sub2aarch646f74666eb89e1addc4ce75e25f3e639bbfdd8798fc848a6b7027501070567dcfsub2-extensionThis is the Simple DirectMedia Layer, a generic API that provides low level access to audio, keyboard, mouse, and display framebuffer across multiple platforms.http://www.libsdl.org/' + #meta_string='building-block-root-metapkgaarch646f74666eb89e1addc4ce75e25f3e639bbfdd8798fc848a6b7027501070567dcfSimple DirectMedia LayerThis is the Simple DirectMedia Layer, a generic API that provides low level access to audio, keyboard, mouse, and display framebuffer across multiple platforms.http://www.libsdl.org/building-block-sub1-metapkg-extsub1aarch646f74666eb89e1addc4ce75e25f3e639bbfdd8798fc848a6b7027501070567dcfsub1-extensionThis is the Simple DirectMedia Layer, a generic API that provides low level access to audio, keyboard, mouse, and display framebuffer across multiple platforms.http://www.libsdl.org/building-block-sub2-metapkg-extsub1-exts1sub1aarch646f74666eb89e1addc4ce75e25f3e639bbfdd8798fc848a6b7027501070567dcfsub2-extensionThis is the Simple DirectMedia Layer, a generic API that provides low level access to audio, keyboard, mouse, and display framebuffer across multiple platforms.http://www.libsdl.org/building-block-sub2-metapkg-extsub1-exts1sub2aarch646f74666eb89e1addc4ce75e25f3e639bbfdd8798fc848a6b7027501070567dcfsub2-extensionThis is the Simple DirectMedia Layer, a generic API that provides low level access to audio, keyboard, mouse, and display framebuffer across multiple platforms.http://www.libsdl.org/building-block-sub1-metapkg-extsub2aarch646f74666eb89e1addc4ce75e25f3e639bbfdd8798fc848a6b7027501070567dcfsub1-extensionThis is the Simple DirectMedia Layer, a generic API that provides low level access to audio, keyboard, mouse, and display framebuffer across multiple platforms.http://www.libsdl.org/building-block-sub2-metapkg-extsub2-exts2sub1aarch646f74666eb89e1addc4ce75e25f3e639bbfdd8798fc848a6b7027501070567dcfsub2-extensionThis is the Simple DirectMedia Layer, a generic API that provides low level access to audio, keyboard, mouse, and display framebuffer across multiple platforms.http://www.libsdl.org/building-block-sub2-metapkg-extsub2-exts2sub2aarch646f74666eb89e1addc4ce75e25f3e639bbfdd8798fc848a6b7027501070567dcfsub2-extensionThis is the Simple DirectMedia Layer, a generic API that provides low level access to audio, keyboard, mouse, and display framebuffer across multiple platforms.http://www.libsdl.org/' + meta_string='building-block-root-metapkgaarch646f74666eb89e1addc4ce75e25f3e639bbfdd8798fc848a6b7027501070567dcfSimple DirectMedia LayerThis is the Simple DirectMedia Layer, a generic API that provides low level access to audio, keyboard, mouse, and display framebuffer across multiple platforms.http://www.libsdl.org/building-block-sub1-metapkg-extsub1aarch646f74666eb89e1addc4ce75e25f3e639bbfdd8798fc848a6b7027501070567dcfsub1-extensionThis is the Simple DirectMedia Layer, a generic API that provides low level access to audio, keyboard, mouse, and display framebuffer across multiple platforms.http://www.libsdl.org/building-block-sub2-metapkg-extsub1-exts1sub1aarch646f74666eb89e1addc4ce75e25f3e639bbfdd8798fc848a6b7027501070567dcfsub2-extensionThis is the Simple DirectMedia Layer, a generic API that provides low level access to audio, keyboard, mouse, and display framebuffer across multiple platforms.http://www.libsdl.org/building-block-sub2-metapkg-extsub1-exts1sub2aarch646f74666eb89e1addc4ce75e25f3e639bbfdd8798fc848a6b7027501070567dcfsub2-extensionThis is the Simple DirectMedia Layer, a generic API that provides low level access to audio, keyboard, mouse, and display framebuffer across multiple platforms.http://www.libsdl.org/building-block-sub1-metapkg-extsub2aarch646f74666eb89e1addc4ce75e25f3e639bbfdd8798fc848a6b7027501070567dcfsub1-extensionThis is the Simple DirectMedia Layer, a generic API that provides low level access to audio, keyboard, mouse, and display framebuffer across multiple platforms.http://www.libsdl.org/building-block-sub2-metapkg-extsub2-exts2sub1aarch646f74666eb89e1addc4ce75e25f3e639bbfdd8798fc848a6b7027501070567dcfsub2-extensionThis is the Simple DirectMedia Layer, a generic API that provides low level access to audio, keyboard, mouse, and display framebuffer across multiple platforms.http://www.libsdl.org/building-block-sub2-metapkg-extsub2-exts2sub2aarch646f74666eb89e1addc4ce75e25f3e639bbfdd8798fc848a6b7027501070567dcfsub2-extensionThis is the Simple DirectMedia Layer, a generic API that provides low level access to audio, keyboard, mouse, and display framebuffer across multiple platforms.http://www.libsdl.org/' xml_list.append(etree.fromstring(meta_string)) #pkg_group = dict(pkg_list=[], pkg2id={}, provides2id={}, file2id={}, meta_info=dict(root=[], sub1=[], sub2=[])) @@ -235,6 +238,7 @@ class RepodataParser(object): provides={}, files={}, groups={}, + conflicts={}, meta_info=dict(root=[], sub1=[], sub2=[])) # parses the repodata (primary.xml) diff --git a/tic/server/tic_server.py b/tic/server/tic_server.py index f0f5fdf..d0b3a9a 100644 --- a/tic/server/tic_server.py +++ b/tic/server/tic_server.py @@ -34,9 +34,9 @@ def analysis(): except ValueError as ve: logger.error(ve) resp = makeresponse(str(ve), ve) - except Exception as ex: - logger.error(ex) - resp = makeresponse(str(ex), ex) +# except Exception as ex: +# logger.error(ex) +# resp = makeresponse(str(ex), ex) return resp -- 2.7.4