| Home | Trees | Indices | Help |
|
|---|
|
|
1 """
2 This class brings together a L{solve.Solver} to choose a set of implmentations, a
3 L{fetch.Fetcher} to download additional components, and the user's configuration
4 settings.
5 @since: 0.53
6 """
7
8 # Copyright (C) 2011, Thomas Leonard
9 # See the README file for details, or visit http://0install.net.
10
11 from zeroinstall import _, logger
12 import os
13
14 from zeroinstall.injector import arch
15 from zeroinstall.injector.model import network_offline
16 from zeroinstall.support import tasks
19 """Chooses a set of implementations based on a policy.
20 Typical use:
21 1. Create a Driver object, giving it the requirements about the program to be run.
22 2. Call L{solve_with_downloads}. If more information is needed, a L{fetch.Fetcher} will be used to download it.
23 3. When all downloads are complete, the L{solver} contains the chosen versions.
24 4. Use L{get_uncached_implementations} to find where to get these versions and download them
25 using L{download_uncached_implementations}.
26
27 @ivar target_arch: target architecture for binaries (deprecated)
28 @type target_arch: L{arch.Architecture}
29 @ivar solver: solver used to choose a set of implementations
30 @type solver: L{solve.Solver}
31 @ivar watchers: callbacks to invoke after solving
32 """
33 __slots__ = ['watchers', 'requirements', 'config', 'target_arch', 'solver']
34
36 """@param config: The configuration settings to use
37 @type config: L{config.Config}
38 @param requirements: Details about the program we want to run
39 @type requirements: L{requirements.Requirements}
40 @since: 0.53"""
41 self.watchers = []
42
43 assert config
44 self.config = config
45
46 assert requirements
47 self.requirements = requirements
48
49 self.target_arch = arch.get_architecture(requirements.os, requirements.cpu)
50
51 from zeroinstall.injector.solver import DefaultSolver
52 self.solver = DefaultSolver(self.config)
53
54 logger.debug(_("Supported systems: '%s'"), arch.os_ranks)
55 logger.debug(_("Supported processors: '%s'"), arch.machine_ranks)
56
57 self.solver.extra_restrictions = requirements.get_extra_restrictions(self.config.iface_cache)
58
60 """List all chosen implementations which aren't yet available locally.
61 @rtype: [(L{model.Interface}, L{model.Implementation})]"""
62 iface_cache = self.config.iface_cache
63 stores = self.config.stores
64 uncached = []
65 for uri, selection in self.solver.selections.selections.items():
66 impl = selection.impl
67 assert impl, self.solver.selections
68 if not impl.is_available(stores):
69 uncached.append((iface_cache.get_interface(uri), impl))
70 return uncached
71
72 @tasks.async
74 """Run the solver, then download any feeds that are missing or
75 that need to be updated. Each time a new feed is imported into
76 the cache, the solver is run again, possibly adding new downloads.
77 @param force: whether to download even if we're already ready to run.
78 @type force: bool
79 @param update_local: fetch PackageKit feeds even if we're ready to run.
80 @type update_local: bool"""
81
82 downloads_finished = set() # Successful or otherwise
83 downloads_in_progress = {} # URL -> Download
84
85 # There are three cases:
86 # 1. We want to run immediately if possible. If not, download all the information we can.
87 # (force = False, update_local = False)
88 # 2. We're in no hurry, but don't want to use the network unnecessarily.
89 # We should still update local information (from PackageKit).
90 # (force = False, update_local = True)
91 # 3. The user explicitly asked us to refresh everything.
92 # (force = True)
93
94 try_quick_exit = not (force or update_local)
95
96 while True:
97 self.solver.solve_for(self.requirements)
98 for w in self.watchers: w()
99
100 if try_quick_exit and self.solver.ready:
101 break
102 try_quick_exit = False
103
104 if not self.solver.ready:
105 force = True
106
107 for f in self.solver.feeds_used:
108 if f in downloads_finished or f in downloads_in_progress:
109 continue
110 if os.path.isabs(f):
111 if force:
112 self.config.iface_cache.get_feed(f, force = True)
113 downloads_in_progress[f] = tasks.IdleBlocker('Refresh local feed')
114 continue
115 elif f.startswith('distribution:'):
116 if force or update_local:
117 downloads_in_progress[f] = self.config.fetcher.download_and_import_feed(f, self.config.iface_cache)
118 elif force and self.config.network_use != network_offline:
119 downloads_in_progress[f] = self.config.fetcher.download_and_import_feed(f, self.config.iface_cache)
120 # Once we've starting downloading some things,
121 # we might as well get them all.
122 force = True
123
124 if not downloads_in_progress:
125 if self.config.network_use == network_offline:
126 logger.info(_("Can't choose versions and in off-line mode, so aborting"))
127 break
128
129 # Wait for at least one download to finish
130 blockers = downloads_in_progress.values()
131 yield blockers
132 tasks.check(blockers, self.config.handler.report_error)
133
134 for f in list(downloads_in_progress.keys()):
135 if f in downloads_in_progress and downloads_in_progress[f].happened:
136 del downloads_in_progress[f]
137 downloads_finished.add(f)
138
139 # Need to refetch any "distribution" feed that
140 # depends on this one
141 distro_feed_url = 'distribution:' + f
142 if distro_feed_url in downloads_finished:
143 downloads_finished.remove(distro_feed_url)
144 if distro_feed_url in downloads_in_progress:
145 del downloads_in_progress[distro_feed_url]
146
147 @tasks.async
149 """Run L{solve_with_downloads} and then get the selected implementations too.
150 @type refresh: bool
151 @type select_only: bool
152 @raise SafeException: if we couldn't select a set of implementations
153 @since: 0.40"""
154 refreshed = self.solve_with_downloads(refresh)
155 if refreshed:
156 yield refreshed
157 tasks.check(refreshed)
158
159 if not self.solver.ready:
160 raise self.solver.get_failure_reason()
161
162 if not select_only:
163 downloaded = self.download_uncached_implementations()
164 if downloaded:
165 yield downloaded
166 tasks.check(downloaded)
167
169 """Decide whether we need to download anything (but don't do it!)
170 @return: true if we MUST download something (feeds or implementations)
171 @rtype: bool"""
172 self.solver.solve_for(self.requirements)
173 for w in self.watchers: w()
174
175 if not self.solver.ready:
176 return True # Maybe a newer version will work?
177
178 if self.get_uncached_implementations():
179 return True
180
181 return False
182
184 """Download all implementations chosen by the solver that are missing from the cache.
185 @rtype: L{zeroinstall.support.tasks.Blocker}"""
186 assert self.solver.ready, "Solver is not ready!\n%s" % self.solver.selections
187 stores = self.config.stores
188 return self.config.fetcher.download_impls([impl for impl in self.solver.selections.values() if not impl.is_available(stores)],
189 stores)
190
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Tue Mar 26 18:14:09 2013 | http://epydoc.sourceforge.net |