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'))
# 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
# 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
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:
# { 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]
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:
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_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)
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