2 # Copyright (c) 2000 - 2016 Samsung Electronics Co., Ltd. All rights reserved.
5 # @author Chulwoo Shin <cw1.shin@samsung.com>
7 # Licensed under the Apache License, Version 2.0 (the "License");
8 # you may not use this file except in compliance with the License.
9 # You may obtain a copy of the License at
11 # http://www.apache.org/licenses/LICENSE-2.0
13 # Unless required by applicable law or agreed to in writing, software
14 # distributed under the License is distributed on an "AS IS" BASIS,
15 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 # See the License for the specific language governing permissions and
17 # limitations under the License.
24 from lxml import etree
25 from tic.utils.error import TICError
27 def analyze_dependency(pkg_group):
30 logger = logging.getLogger(__name__)
31 if pkg_list[pkg_id].get('dependency') is not None:
32 return pkg_list[pkg_id].get('dependency')
35 visited[pkg_id] = number[0]
36 min_num[pkg_id] = number[0]
39 dep_set = set([pkg_list[pkg_id]['name']])
41 if pkg_list[pkg_id].get('requires'):
42 for req in pkg_list[pkg_id].get('requires'):
43 req_id = req.get('id')
44 if req_id is not None:
45 if scc_list[req_id] > 0:
46 dep_set.update(pkg_list[req_id].get('dependency'))
49 if visited[req_id] == 0:
50 dep_set.update(dep_dfs(req_id))
52 min_num[pkg_id] = min(min_num[pkg_id], min_num[req_id])
54 #TODO: package does not exist
55 #logger.warning('%s does not exist in repo', req['name'])
58 if min_num[pkg_id] == visited[pkg_id]:
59 # scc (string connected components)
60 make_scc(pkg_id, list(dep_set))
64 def make_scc(pkg_id, dep_list):
70 scc_list[p_id] = scc_num[0]
71 pkg_list[p_id]['dependency'] = dep_list
76 for pkg_id in range(len(pkg_list)):
77 if visited[pkg_id] == 0:
80 #TODO: Exception handling
84 # package install-dependency analysis
85 pkg_list = pkg_group.get('pkg_list')
88 visited = [0]*len(pkg_list)
89 min_num = [0]*len(pkg_list)
90 scc_list = [0]*len(pkg_list)
95 def get_installed_packages(recipe, repoinfo, pkg_group):
96 logger = logging.getLogger(__name__)
98 def _compare_ver(ver1, ver2):
99 return rpm.labelCompare((ver1.get('epoch'), ver1.get('ver'), ver1.get('rel')),
100 (ver2.get('epoch'), ver2.get('ver'), ver2.get('rel')))
102 def _compare_req_cap_ver(req, cap):
103 epoch = cap.get('epoch')
106 if not req.get('epoch'): epoch = None
107 if not req.get('rel'): rel = None
108 return rpm.labelCompare((req.get('epoch'), req.get('ver'), req.get('rel')), (epoch, ver, rel))
110 def _select_rpm(capability, require):
112 # TODO: temporary code (to support efl-data capability)
113 if len(capability) == 1:
114 return pkg_dict.get(capability[0].get('name'))
117 # 1. Choose the rpm included in version from provides
118 if require.get('ver') is not None:
119 for provide in capability:
120 cap_info = provide.get('data')
121 cmp_ret = _compare_req_cap_ver(require, cap_info)
122 if cmp_ret == 0 and (require['flags'] == 'EQ' or require['flags'] == 'GE' or require['flags'] == 'LE'):
123 provide_list.append(provide)
124 elif cmp_ret == 1 and (require['flags'] == 'LT' or require['flags'] == 'LE'):
125 provide_list.append(provide)
126 elif cmp_ret == -1 and (require['flags'] == 'GT' or require['flags'] == 'GE'):
127 provide_list.append(provide)
129 provide_list = capability
131 # error case (the rpm does not exist)
135 if len(provide_list) == 1:
136 return pkg_dict.get(provide_list[0].get('name'))
138 # 2 Select one of the rpms by priority
139 # 2-1. Choose the default rpm or the selected rpm
140 # TODO: default profile rpm should be selected
141 for i in range(0, len(provide_list)):
142 tmp_info = pkg_dict.get(provide_list[i].get('name'))
143 if tmp_info['name'] in pkg_set or selected[tmp_info['id']] == 1:
145 # 2-2. Select the latest version of rpm
146 max_ver = provide_list[0]
147 for i in range(1, len(provide_list)):
148 cap_info = provide_list[i].get('data')
149 ret = _compare_ver(max_ver.get('data'), cap_info)
150 # cap_info is greater than max_ver
152 max_ver = provide_list[i]
154 pkg_name = max_ver.get('name')
155 return pkg_dict.get(pkg_name)
157 def _create_reference(pkg1, pkg2):
159 if pkg1.get('forward') and pkg2['name'] in pkg1.get('forward'):
162 if pkg1.get('forward'):
163 pkg1['forward'].append(pkg2['name'])
165 pkg1['forward'] = [pkg2['name']]
167 if pkg2.get('backward'):
168 pkg2['backward'].append(pkg1['name'])
170 pkg2['backward'] = [pkg1['name']]
172 def _make_scc(pkg_id):
178 scc_id[pkg['id']] = scc_num[0]
180 if pkg_id == pkg['id']:
183 # circular dependency
184 if len(scc_list) > 1:
189 pkg['group'] = group_num[0]
190 group_names.append(pkg['name'])
192 # { group_id = [ pkg_name_1, pkg_name_2 ], ... }
193 groups[group_num[0]] = group_names
196 def _analyze_dep(pkg_info):
200 pkg_id = pkg_info['id'];
202 selected[pkg_id] = number[0]
203 min_num[pkg_id] = number[0]
204 stack.append(pkg_info)
206 dep_rpms = set([pkg_info['name']])
208 # Installation dependency analysis of rpm
209 for dep_tag in ['requires']: # 'recommends'
210 if pkg_info.get(dep_tag):
211 for req in pkg_info['requires']:
213 # Find dependency rpm based on capability/files
214 if req['name'] in provides:
215 # capability : [provide_rpm_1, provide_rpm_2, ... ]
216 cap_list = provides.get(req['name'])
217 # Select the rpm that meets the condition (version)
218 choose = _select_rpm(cap_list, req)
219 elif req['name'] in files:
220 choose = pkg_dict.get(files.get(req['name'])[0])
223 # add forward/backward reference
224 _create_reference(pkg_info, choose)
226 if selected[choose['id']] == 0:
227 dep_rpms.update(_analyze_dep(choose))
228 min_num[pkg_id] = min(min_num[pkg_id], min_num[choose['id']])
229 elif scc_id[choose['id']] == 0:
230 # cross edge that can not be ignored
231 min_num[pkg_id] = min(min_num[pkg_id], min_num[choose['id']])
233 # the rpm does not exists
235 logger.info('the capability(%s) does not exist. should be checked for error' % req['name'])
237 if min_num[pkg_id] == selected[pkg_id]:
238 # scc(strong connected components)
244 if not recipe or not repoinfo:
247 default = recipe.get('Default')
248 config = recipe.get('Configurations')[0]
249 platform_name = config.get('Platform')
250 platform = recipe.get(platform_name)
252 # check groups/extraPackages
255 for g in [default, platform, config]:
256 if g.has_key('Groups'):
257 group_set.update(g.get('Groups'))
258 if g.has_key('ExtraPackages'):
259 pkg_set.update(g.get('ExtraPackages'))
260 group_dict = dict.fromkeys(group_set)
264 tree = etree.parse(repoinfo[0].get('comps'))
265 root = tree.getroot()
266 except etree.XMLSyntaxError as e:
267 raise TICError('primary.xml syntax error. %s', e)
269 # Convert groups to packages
270 for elm in root.findall('group'):
271 group_name = elm.find('name').text
272 if group_dict.has_key(group_name):
273 pkglist = elm.find('packagelist')
275 for pkgreq in pkglist.findall('packagereq'):
276 plist.append(pkgreq.text)
277 pkg_set.update(set(plist))
279 pkg_dict = pkg_group.get('pkg_dict')
280 provides = pkg_group.get('provides')
281 files = pkg_group.get('files')
282 groups = pkg_group.get('groups')
285 number = [0] # for pkg count
286 scc_num = [0] # for scc count
287 group_num = [0] # for group count
288 scc_id = [0] * len(pkg_dict)
289 min_num = [0] * len(pkg_dict)
290 selected = [0] * len(pkg_dict)
291 install_rpm = set([])
293 for pkg_name in pkg_set:
294 pkg_info = pkg_dict.get(pkg_name)
296 # TODO: temporary code (Define capability in group)
298 pro = provides.get(pkg_name)[0]
299 pkg_info = pkg_dict.get(pro['name'])
301 pkg_info['selfChecked'] = True;
302 if selected[pkg_info['id']] == 0:
303 install_rpm.update(_analyze_dep(pkg_info))
305 return list(install_rpm)