From fe4b2a3fff9a1f61177494a8ebc512d1d12f228a Mon Sep 17 00:00:00 2001 From: Chulwoo Shin Date: Fri, 21 Apr 2017 14:01:23 +0900 Subject: [PATCH] [TIC-CORE] fix the issue that cannot analyze the install-dependency - fix the issue that cannot analyze the install-dependency - support recommends tag Change-Id: I0204c9b0c372d8f9735e19b7da7640c3bbc20d21 Signed-off-by: Chulwoo Shin --- tic/command.py | 4 +- tic/dependency.py | 260 +++++++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 212 insertions(+), 52 deletions(-) diff --git a/tic/command.py b/tic/command.py index 7e3598d..c66743a 100644 --- a/tic/command.py +++ b/tic/command.py @@ -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): diff --git a/tic/dependency.py b/tic/dependency.py index 19864b9..fefc53b 100644 --- a/tic/dependency.py +++ b/tic/dependency.py @@ -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) -- 2.7.4