EbuildBuild: use fetch queue to cap fetch threads
[portage.git] / pym / _emerge / EbuildBuild.py
1 # Copyright 1999-2011 Gentoo Foundation
2 # Distributed under the terms of the GNU General Public License v2
3
4 from _emerge.EbuildExecuter import EbuildExecuter
5 from _emerge.EbuildPhase import EbuildPhase
6 from _emerge.EbuildBinpkg import EbuildBinpkg
7 from _emerge.EbuildFetcher import EbuildFetcher
8 from _emerge.CompositeTask import CompositeTask
9 from _emerge.EbuildMerge import EbuildMerge
10 from _emerge.EbuildFetchonly import EbuildFetchonly
11 from _emerge.EbuildBuildDir import EbuildBuildDir
12 from _emerge.MiscFunctionsProcess import MiscFunctionsProcess
13 from portage.util import writemsg
14 import portage
15 from portage import os
16 from portage.output import colorize
17 from portage.package.ebuild.digestcheck import digestcheck
18 from portage.package.ebuild.doebuild import _check_temp_dir
19 from portage.package.ebuild._spawn_nofetch import spawn_nofetch
20
21 class EbuildBuild(CompositeTask):
22
23         __slots__ = ("args_set", "config_pool", "find_blockers",
24                 "ldpath_mtimes", "logger", "opts", "pkg", "pkg_count",
25                 "prefetcher", "settings", "world_atom") + \
26                 ("_build_dir", "_buildpkg", "_ebuild_path", "_issyspkg", "_tree")
27
28         def _start(self):
29
30                 pkg = self.pkg
31                 settings = self.settings
32
33                 rval = _check_temp_dir(settings)
34                 if rval != os.EX_OK:
35                         self.returncode = rval
36                         self._current_task = None
37                         self.wait()
38                         return
39
40                 root_config = pkg.root_config
41                 tree = "porttree"
42                 self._tree = tree
43                 portdb = root_config.trees[tree].dbapi
44                 settings.setcpv(pkg)
45                 settings.configdict["pkg"]["EMERGE_FROM"] = "ebuild"
46                 if self.opts.buildpkgonly:
47                         settings.configdict["pkg"]["MERGE_TYPE"] = "buildonly"
48                 else:
49                         settings.configdict["pkg"]["MERGE_TYPE"] = "source"
50                 ebuild_path = portdb.findname(pkg.cpv, myrepo=pkg.repo)
51                 if ebuild_path is None:
52                         raise AssertionError("ebuild not found for '%s'" % pkg.cpv)
53                 self._ebuild_path = ebuild_path
54                 portage.doebuild_environment(ebuild_path, 'setup',
55                         settings=self.settings, db=portdb)
56
57                 # Check the manifest here since with --keep-going mode it's
58                 # currently possible to get this far with a broken manifest.
59                 if not self._check_manifest():
60                         self.returncode = 1
61                         self._current_task = None
62                         self.wait()
63                         return
64
65                 prefetcher = self.prefetcher
66                 if prefetcher is None:
67                         pass
68                 elif prefetcher.isAlive() and \
69                         prefetcher.poll() is None:
70
71                         waiting_msg = "Fetching files " + \
72                                 "in the background. " + \
73                                 "To view fetch progress, run `tail -f " + \
74                                 "/var/log/emerge-fetch.log` in another " + \
75                                 "terminal."
76                         msg_prefix = colorize("GOOD", " * ")
77                         from textwrap import wrap
78                         waiting_msg = "".join("%s%s\n" % (msg_prefix, line) \
79                                 for line in wrap(waiting_msg, 65))
80                         if not self.background:
81                                 writemsg(waiting_msg, noiselevel=-1)
82
83                         self._current_task = prefetcher
84                         prefetcher.addExitListener(self._prefetch_exit)
85                         return
86
87                 self._prefetch_exit(prefetcher)
88
89         def _check_manifest(self):
90                 success = True
91
92                 settings = self.settings
93                 if 'strict' in settings.features:
94                         settings['O'] = os.path.dirname(self._ebuild_path)
95                         quiet_setting = settings.get('PORTAGE_QUIET')
96                         settings['PORTAGE_QUIET'] = '1'
97                         try:
98                                 success = digestcheck([], settings, strict=True)
99                         finally:
100                                 if quiet_setting:
101                                         settings['PORTAGE_QUIET'] = quiet_setting
102                                 else:
103                                         del settings['PORTAGE_QUIET']
104
105                 return success
106
107         def _prefetch_exit(self, prefetcher):
108
109                 opts = self.opts
110                 pkg = self.pkg
111                 settings = self.settings
112
113                 if opts.fetchonly:
114                         if opts.pretend:
115                                 fetcher = EbuildFetchonly(
116                                         fetch_all=opts.fetch_all_uri,
117                                         pkg=pkg, pretend=opts.pretend,
118                                         settings=settings)
119                                 retval = fetcher.execute()
120                                 self.returncode = retval
121                                 self.wait()
122                                 return
123                         else:
124                                 fetcher = EbuildFetcher(
125                                         config_pool=self.config_pool,
126                                         fetchall=self.opts.fetch_all_uri,
127                                         fetchonly=self.opts.fetchonly,
128                                         background=False,
129                                         logfile=None,
130                                         pkg=self.pkg,
131                                         scheduler=self.scheduler)
132                                 self._start_task(fetcher, self._fetchonly_exit)
133                                 return
134
135                 self._build_dir = EbuildBuildDir(
136                         scheduler=self.scheduler, settings=settings)
137                 self._build_dir.lock()
138
139                 # Cleaning needs to happen before fetch, since the build dir
140                 # is used for log handling.
141                 msg = " === (%s of %s) Cleaning (%s::%s)" % \
142                         (self.pkg_count.curval, self.pkg_count.maxval,
143                         self.pkg.cpv, self._ebuild_path)
144                 short_msg = "emerge: (%s of %s) %s Clean" % \
145                         (self.pkg_count.curval, self.pkg_count.maxval, self.pkg.cpv)
146                 self.logger.log(msg, short_msg=short_msg)
147
148                 pre_clean_phase = EbuildPhase(background=self.background,
149                         phase='clean', scheduler=self.scheduler, settings=self.settings)
150                 self._start_task(pre_clean_phase, self._pre_clean_exit)
151
152         def _fetchonly_exit(self, fetcher):
153                 self._final_exit(fetcher)
154                 if self.returncode != os.EX_OK:
155                         portdb = self.pkg.root_config.trees[self._tree].dbapi
156                         spawn_nofetch(portdb, self._ebuild_path, settings=self.settings)
157                 self.wait()
158
159         def _pre_clean_exit(self, pre_clean_phase):
160                 if self._default_exit(pre_clean_phase) != os.EX_OK:
161                         self._unlock_builddir()
162                         self.wait()
163                         return
164
165                 # for log handling
166                 portage.prepare_build_dirs(self.pkg.root, self.settings, 1)
167
168                 fetcher = EbuildFetcher(config_pool=self.config_pool,
169                         fetchall=self.opts.fetch_all_uri,
170                         fetchonly=self.opts.fetchonly,
171                         background=self.background,
172                         logfile=self.settings.get('PORTAGE_LOG_FILE'),
173                         pkg=self.pkg, scheduler=self.scheduler)
174
175                 # Allow the Scheduler's fetch queue to control the
176                 # number of concurrent fetchers.
177                 fetcher.addExitListener(self._fetch_exit)
178                 self._task_queued(fetcher)
179                 self.scheduler.fetch.schedule(fetcher)
180
181         def _fetch_exit(self, fetcher):
182
183                 if self._default_exit(fetcher) != os.EX_OK:
184                         self._fetch_failed()
185                         return
186
187                 # discard successful fetch log
188                 self._build_dir.clean_log()
189                 pkg = self.pkg
190                 logger = self.logger
191                 opts = self.opts
192                 pkg_count = self.pkg_count
193                 scheduler = self.scheduler
194                 settings = self.settings
195                 features = settings.features
196                 ebuild_path = self._ebuild_path
197                 system_set = pkg.root_config.sets["system"]
198
199                 #buildsyspkg: Check if we need to _force_ binary package creation
200                 self._issyspkg = "buildsyspkg" in features and \
201                                 system_set.findAtomForPackage(pkg) and \
202                                 not opts.buildpkg
203
204                 if opts.buildpkg or self._issyspkg:
205
206                         self._buildpkg = True
207
208                         msg = " === (%s of %s) Compiling/Packaging (%s::%s)" % \
209                                 (pkg_count.curval, pkg_count.maxval, pkg.cpv, ebuild_path)
210                         short_msg = "emerge: (%s of %s) %s Compile" % \
211                                 (pkg_count.curval, pkg_count.maxval, pkg.cpv)
212                         logger.log(msg, short_msg=short_msg)
213
214                 else:
215                         msg = " === (%s of %s) Compiling/Merging (%s::%s)" % \
216                                 (pkg_count.curval, pkg_count.maxval, pkg.cpv, ebuild_path)
217                         short_msg = "emerge: (%s of %s) %s Compile" % \
218                                 (pkg_count.curval, pkg_count.maxval, pkg.cpv)
219                         logger.log(msg, short_msg=short_msg)
220
221                 build = EbuildExecuter(background=self.background, pkg=pkg,
222                         scheduler=scheduler, settings=settings)
223                 self._start_task(build, self._build_exit)
224
225         def _fetch_failed(self):
226                 # We only call the pkg_nofetch phase if either RESTRICT=fetch
227                 # is set or the package has explicitly overridden the default
228                 # pkg_nofetch implementation. This allows specialized messages
229                 # to be displayed for problematic packages even though they do
230                 # not set RESTRICT=fetch (bug #336499).
231
232                 if 'fetch' not in self.pkg.metadata.restrict and \
233                         'nofetch' not in self.pkg.metadata.defined_phases:
234                         self._unlock_builddir()
235                         self.wait()
236                         return
237
238                 self.returncode = None
239                 nofetch_phase = EbuildPhase(background=self.background,
240                         phase='nofetch', scheduler=self.scheduler, settings=self.settings)
241                 self._start_task(nofetch_phase, self._nofetch_exit)
242
243         def _nofetch_exit(self, nofetch_phase):
244                 self._final_exit(nofetch_phase)
245                 self._unlock_builddir()
246                 self.returncode = 1
247                 self.wait()
248
249         def _unlock_builddir(self):
250                 portage.elog.elog_process(self.pkg.cpv, self.settings)
251                 self._build_dir.unlock()
252
253         def _build_exit(self, build):
254                 if self._default_exit(build) != os.EX_OK:
255                         self._unlock_builddir()
256                         self.wait()
257                         return
258
259                 buildpkg = self._buildpkg
260
261                 if not buildpkg:
262                         self._final_exit(build)
263                         self.wait()
264                         return
265
266                 if self._issyspkg:
267                         msg = ">>> This is a system package, " + \
268                                 "let's pack a rescue tarball.\n"
269                         self.scheduler.output(msg,
270                                 log_path=self.settings.get("PORTAGE_LOG_FILE"))
271
272                 packager = EbuildBinpkg(background=self.background, pkg=self.pkg,
273                         scheduler=self.scheduler, settings=self.settings)
274
275                 self._start_task(packager, self._buildpkg_exit)
276
277         def _buildpkg_exit(self, packager):
278                 """
279                 Released build dir lock when there is a failure or
280                 when in buildpkgonly mode. Otherwise, the lock will
281                 be released when merge() is called.
282                 """
283
284                 if self._default_exit(packager) != os.EX_OK:
285                         self._unlock_builddir()
286                         self.wait()
287                         return
288
289                 if self.opts.buildpkgonly:
290                         phase = 'success_hooks'
291                         success_hooks = MiscFunctionsProcess(
292                                 background=self.background,
293                                 commands=[phase], phase=phase,
294                                 scheduler=self.scheduler, settings=self.settings)
295                         self._start_task(success_hooks,
296                                 self._buildpkgonly_success_hook_exit)
297                         return
298
299                 # Continue holding the builddir lock until
300                 # after the package has been installed.
301                 self._current_task = None
302                 self.returncode = packager.returncode
303                 self.wait()
304
305         def _buildpkgonly_success_hook_exit(self, success_hooks):
306                 self._default_exit(success_hooks)
307                 self.returncode = None
308                 # Need to call "clean" phase for buildpkgonly mode
309                 portage.elog.elog_process(self.pkg.cpv, self.settings)
310                 phase = 'clean'
311                 clean_phase = EbuildPhase(background=self.background,
312                         phase=phase, scheduler=self.scheduler, settings=self.settings)
313                 self._start_task(clean_phase, self._clean_exit)
314
315         def _clean_exit(self, clean_phase):
316                 if self._final_exit(clean_phase) != os.EX_OK or \
317                         self.opts.buildpkgonly:
318                         self._unlock_builddir()
319                 self.wait()
320
321         def create_install_task(self):
322                 """
323                 Install the package and then clean up and release locks.
324                 Only call this after the build has completed successfully
325                 and neither fetchonly nor buildpkgonly mode are enabled.
326                 """
327
328                 ldpath_mtimes = self.ldpath_mtimes
329                 logger = self.logger
330                 pkg = self.pkg
331                 pkg_count = self.pkg_count
332                 settings = self.settings
333                 world_atom = self.world_atom
334                 ebuild_path = self._ebuild_path
335                 tree = self._tree
336
337                 task = EbuildMerge(find_blockers=self.find_blockers,
338                         ldpath_mtimes=ldpath_mtimes, logger=logger, pkg=pkg,
339                         pkg_count=pkg_count, pkg_path=ebuild_path,
340                         scheduler=self.scheduler,
341                         settings=settings, tree=tree, world_atom=world_atom)
342
343                 msg = " === (%s of %s) Merging (%s::%s)" % \
344                         (pkg_count.curval, pkg_count.maxval,
345                         pkg.cpv, ebuild_path)
346                 short_msg = "emerge: (%s of %s) %s Merge" % \
347                         (pkg_count.curval, pkg_count.maxval, pkg.cpv)
348                 logger.log(msg, short_msg=short_msg)
349
350                 task.addExitListener(self._install_exit)
351                 return task
352
353         def _install_exit(self, task):
354                 self._unlock_builddir()