1 # Copyright 2010 Gentoo Foundation
2 # Distributed under the terms of the GNU General Public License v2
4 from itertools import chain, permutations
9 from portage.dbapi.vartree import vartree
10 from portage.dbapi.porttree import portagetree
11 from portage.dbapi.bintree import binarytree
12 from portage.dep import Atom
13 from portage.package.ebuild.config import config
14 from portage.sets import SetConfig
15 from portage.versions import catsplit
17 from _emerge.Blocker import Blocker
18 from _emerge.create_depgraph_params import create_depgraph_params
19 from _emerge.depgraph import backtrack_depgraph
20 from _emerge.RootConfig import RootConfig
21 from _emerge.main import setconfig_fallback
23 class ResolverPlayground(object):
25 This class help to create the necessary files on disk and
26 the needed settings instances, etc. for the resolver to do
30 def __init__(self, ebuilds={}, installed={}, profile={}):
32 ebuilds: cpv -> metadata mapping simulating avaiable ebuilds.
33 installed: cpv -> metadata mapping simulating installed packages.
34 If a metadata key is missing, it gets a default value.
35 profile: settings defined by the profile.
37 self.root = tempfile.mkdtemp() + os.path.sep
38 self.portdir = os.path.join(self.root, "usr/portage")
39 self.vdbdir = os.path.join(self.root, "var/db/pkg")
40 os.makedirs(self.portdir)
41 os.makedirs(self.vdbdir)
43 self._create_ebuilds(ebuilds)
44 self._create_installed(installed)
45 self._create_profile(ebuilds, installed, profile)
47 self.settings, self.trees = self._load_config()
49 self._create_ebuild_manifests(ebuilds)
51 def _create_ebuilds(self, ebuilds):
54 ebuild_dir = os.path.join(self.portdir, a.cp)
55 ebuild_path = os.path.join(ebuild_dir, a.cpv.split("/")[1] + ".ebuild")
57 os.makedirs(ebuild_dir)
61 metadata = ebuilds[cpv]
62 eapi = metadata.get("EAPI", 0)
63 slot = metadata.get("SLOT", 0)
64 keywords = metadata.get("KEYWORDS", "x86")
65 iuse = metadata.get("IUSE", "")
66 depend = metadata.get("DEPEND", "")
67 rdepend = metadata.get("RDEPEND", None)
68 pdepend = metadata.get("PDEPEND", None)
69 required_use = metadata.get("REQUIRED_USE", None)
71 f = open(ebuild_path, "w")
72 f.write('EAPI="' + str(eapi) + '"\n')
73 f.write('SLOT="' + str(slot) + '"\n')
74 f.write('KEYWORDS="' + str(keywords) + '"\n')
75 f.write('IUSE="' + str(iuse) + '"\n')
76 f.write('DEPEND="' + str(depend) + '"\n')
77 if rdepend is not None:
78 f.write('RDEPEND="' + str(rdepend) + '"\n')
79 if rdepend is not None:
80 f.write('PDEPEND="' + str(pdepend) + '"\n')
81 if required_use is not None:
82 f.write('REQUIRED_USE="' + str(required_use) + '"\n')
85 def _create_ebuild_manifests(self, ebuilds):
88 ebuild_dir = os.path.join(self.portdir, a.cp)
89 ebuild_path = os.path.join(ebuild_dir, a.cpv.split("/")[1] + ".ebuild")
91 portage.util.noiselimit = -1
92 tmpsettings = config(clone=self.settings)
93 portdb = self.trees[self.root]["porttree"].dbapi
94 portage.doebuild(ebuild_path, "digest", self.root, tmpsettings,
95 tree="porttree", mydbapi=portdb)
96 portage.util.noiselimit = 0
98 def _create_installed(self, installed):
101 vdb_pkg_dir = os.path.join(self.vdbdir, a.cpv)
103 os.makedirs(vdb_pkg_dir)
107 metadata = installed[cpv]
108 eapi = metadata.get("EAPI", 0)
109 slot = metadata.get("SLOT", 0)
110 keywords = metadata.get("KEYWORDS", "~x86")
111 iuse = metadata.get("IUSE", "")
112 use = metadata.get("USE", "")
113 depend = metadata.get("DEPEND", "")
114 rdepend = metadata.get("RDEPEND", None)
115 pdepend = metadata.get("PDEPEND", None)
116 required_use = metadata.get("REQUIRED_USE", None)
118 def write_key(key, value):
119 f = open(os.path.join(vdb_pkg_dir, key), "w")
120 f.write(str(value) + "\n")
123 write_key("EAPI", eapi)
124 write_key("SLOT", slot)
125 write_key("KEYWORDS", keywords)
126 write_key("IUSE", iuse)
127 write_key("USE", use)
128 write_key("DEPEND", depend)
129 if rdepend is not None:
130 write_key("RDEPEND", rdepend)
131 if rdepend is not None:
132 write_key("PDEPEND", pdepend)
133 if required_use is not None:
134 write_key("REQUIRED_USE", required_use)
136 def _create_profile(self, ebuilds, installed, profile):
137 #Create $PORTDIR/profiles/categories
139 for cpv in chain(ebuilds.keys(), installed.keys()):
140 categories.add(catsplit(cpv)[0])
142 profile_dir = os.path.join(self.portdir, "profiles")
144 os.makedirs(profile_dir)
148 categories_file = os.path.join(profile_dir, "categories")
150 f = open(categories_file, "w")
151 for cat in categories:
155 #Create $PORTDIR/eclass (we fail to digest the ebuilds if it's not there)
156 os.makedirs(os.path.join(self.portdir, "eclass"))
159 #This is meant to allow the consumer to set up his own profile,
160 #with package.mask and what not.
161 raise NotImplentedError()
163 def _load_config(self):
165 "ACCEPT_KEYWORDS": "x86",
166 "PORTDIR": self.portdir,
168 'PORTAGE_TMPDIR' : os.path.join(self.root, 'var/tmp')
171 settings = config(config_root=self.root, target_root=self.root, env=env)
176 "virtuals": settings.getvirtuals(),
177 "vartree": vartree(self.root, categories=settings.categories, settings=settings),
178 "porttree": portagetree(self.root, settings=settings),
179 "bintree": binarytree(self.root, os.path.join(self.root, "usr/portage/packages"), settings=settings)
183 for root, root_trees in trees.items():
184 settings = root_trees["vartree"].settings
185 settings._init_dirs()
186 setconfig = SetConfig([], settings, root_trees)
187 root_trees["root_config"] = RootConfig(settings, root_trees, setconfig)
188 setconfig_fallback(root_trees["root_config"])
190 return settings, trees
192 def run(self, atoms, options={}, action=None):
193 options = options.copy()
194 options["--pretend"] = True
195 options["--quiet"] = True
196 options["--root"] = self.root
197 options["--config-root"] = self.root
198 options["--root-deps"] = "rdeps"
199 # Add a fake _test_ option that can be used for
200 # conditional test code.
201 options["_test_"] = True
203 portage.util.noiselimit = -2
204 params = create_depgraph_params(options, action)
205 success, depgraph, favorites = backtrack_depgraph(
206 self.settings, self.trees, options, params, action, atoms, None)
207 depgraph.display_problems()
208 result = ResolverPlaygroundResult(atoms, success, depgraph, favorites)
209 portage.util.noiselimit = 0
213 def run_TestCase(self, test_case):
214 if not isinstance(test_case, ResolverPlaygroundTestCase):
215 raise TypeError("ResolverPlayground needs a ResolverPlaygroundTestCase")
216 for atoms in test_case.requests:
217 result = self.run(atoms, test_case.options, test_case.action)
218 if not test_case.compare_with_result(result):
222 shutil.rmtree(self.root)
224 class ResolverPlaygroundTestCase(object):
226 def __init__(self, request, **kwargs):
231 "unstable_keywords": None,
232 "slot_collision_solutions": None,
235 self.all_permutations = kwargs.pop("all_permutations", False)
236 self.ignore_mergelist_order = kwargs.pop("ignore_mergelist_order", False)
238 if self.all_permutations:
239 self.requests = list(permutations(request))
241 self.requests = [request]
243 self.options = kwargs.pop("options", {})
244 self.action = kwargs.pop("action", None)
245 self.test_success = True
248 for key, value in kwargs.items():
249 if not key in self.checks:
250 raise KeyError("Not an avaiable check: '%s'" % key)
251 self.checks[key] = value
253 def compare_with_result(self, result):
255 for key, value in self.checks.items():
256 got = getattr(result, key)
258 if key == "mergelist" and self.ignore_mergelist_order and value is not None :
260 expected = set(expected)
261 elif key == "unstable_keywords" and expected is not None:
262 expected = set(expected)
265 fail_msgs.append("atoms: (" + ", ".join(result.atoms) + "), key: " + \
266 key + ", expected: " + str(expected) + ", got: " + str(got))
268 self.test_success = False
269 self.fail_msg = "\n".join(fail_msgs)
273 class ResolverPlaygroundResult(object):
274 def __init__(self, atoms, success, mydepgraph, favorites):
276 self.success = success
277 self.depgraph = mydepgraph
278 self.favorites = favorites
279 self.mergelist = None
280 self.use_changes = None
281 self.unstable_keywords = None
282 self.slot_collision_solutions = None
284 if self.depgraph._dynamic_config._serialized_tasks_cache is not None:
286 for x in self.depgraph._dynamic_config._serialized_tasks_cache:
287 if isinstance(x, Blocker):
288 self.mergelist.append(x.atom)
290 self.mergelist.append(x.cpv)
292 if self.depgraph._dynamic_config._needed_use_config_changes:
293 self.use_changes = {}
294 for pkg, needed_use_config_changes in \
295 self.depgraph._dynamic_config._needed_use_config_changes.items():
296 new_use, changes = needed_use_config_changes
297 self.use_changes[pkg.cpv] = changes
299 if self.depgraph._dynamic_config._needed_unstable_keywords:
300 self.unstable_keywords = set()
301 for pkg in self.depgraph._dynamic_config._needed_unstable_keywords:
302 self.unstable_keywords.add(pkg.cpv)
304 if self.depgraph._dynamic_config._slot_conflict_handler is not None:
305 self.slot_collision_solutions = []
306 handler = self.depgraph._dynamic_config._slot_conflict_handler
308 for solution in handler.solutions:
312 for flag, state in solution[pkg].items():
313 if state == "enabled":
316 changes[flag] = False
318 self.slot_collision_solutions.append(s)