1 # Copyright 1999-2012 Gentoo Foundation
2 # Distributed under the terms of the GNU General Public License v2
10 from _emerge.AsynchronousLock import AsynchronousLock
11 from _emerge.BinpkgEnvExtractor import BinpkgEnvExtractor
12 from _emerge.MiscFunctionsProcess import MiscFunctionsProcess
13 from _emerge.EbuildProcess import EbuildProcess
14 from _emerge.CompositeTask import CompositeTask
15 from portage.util import writemsg
18 from portage.xml.metadata import MetaDataXML
19 except (SystemExit, KeyboardInterrupt):
21 except (ImportError, SystemError, RuntimeError, Exception):
22 # broken or missing xml support
23 # http://bugs.python.org/issue14988
27 portage.proxy.lazyimport.lazyimport(globals(),
28 'portage.elog:messages@elog_messages',
29 'portage.package.ebuild.doebuild:_check_build_log,' + \
30 '_post_phase_cmds,_post_phase_userpriv_perms,' + \
31 '_post_src_install_soname_symlinks,' + \
32 '_post_src_install_uid_fix,_postinst_bsdflags,' + \
33 '_post_src_install_write_metadata,' + \
36 from portage import os
37 from portage import _encodings
38 from portage import _unicode_encode
40 class EbuildPhase(CompositeTask):
42 __slots__ = ("actionmap", "phase", "settings") + \
45 # FEATURES displayed prior to setup phase
47 "ccache", "compressdebug", "distcc", "distcc-pump", "fakeroot",
48 "installsources", "keeptemp", "keepwork", "nostrip",
49 "preserve-libs", "sandbox", "selinux", "sesandbox",
50 "splitdebug", "suidctl", "test", "userpriv",
55 _locked_phases = ("setup", "preinst", "postinst", "prerm", "postrm")
59 need_builddir = self.phase not in EbuildProcess._phases_without_builddir
62 phase_completed_file = os.path.join(
63 self.settings['PORTAGE_BUILDDIR'],
64 ".%sed" % self.phase.rstrip('e'))
65 if not os.path.exists(phase_completed_file):
66 # If the phase is really going to run then we want
67 # to eliminate any stale elog messages that may
68 # exist from a previous run.
70 os.unlink(os.path.join(self.settings['T'],
71 'logging', self.phase))
75 if self.phase in ('nofetch', 'pretend', 'setup'):
77 use = self.settings.get('PORTAGE_BUILT_USE')
79 use = self.settings['PORTAGE_USE']
83 metadata_xml_path = os.path.join(os.path.dirname(self.settings['EBUILD']), "metadata.xml")
84 if MetaDataXML is not None and os.path.isfile(metadata_xml_path):
85 herds_path = os.path.join(self.settings['PORTDIR'],
88 metadata_xml = MetaDataXML(metadata_xml_path, herds_path)
89 maint_str = metadata_xml.format_maintainer_string()
90 upstr_str = metadata_xml.format_upstream_string()
92 maint_str = "<invalid metadata.xml>"
95 msg.append("Package: %s" % self.settings.mycpv)
96 if self.settings.get('PORTAGE_REPO_NAME'):
97 msg.append("Repository: %s" % self.settings['PORTAGE_REPO_NAME'])
99 msg.append("Maintainer: %s" % maint_str)
101 msg.append("Upstream: %s" % upstr_str)
103 msg.append("USE: %s" % use)
104 relevant_features = []
105 enabled_features = self.settings.features
106 for x in self._features_display:
107 if x in enabled_features:
108 relevant_features.append(x)
109 if relevant_features:
110 msg.append("FEATURES: %s" % " ".join(relevant_features))
112 # Force background=True for this header since it's intended
113 # for the log and it doesn't necessarily need to be visible
115 self._elog('einfo', msg, background=True)
117 if self.phase == 'package':
118 if 'PORTAGE_BINPKG_TMPFILE' not in self.settings:
119 self.settings['PORTAGE_BINPKG_TMPFILE'] = \
120 os.path.join(self.settings['PKGDIR'],
121 self.settings['CATEGORY'], self.settings['PF']) + '.tbz2'
123 if self.phase in ("pretend", "prerm"):
124 env_extractor = BinpkgEnvExtractor(background=self.background,
125 scheduler=self.scheduler, settings=self.settings)
126 if env_extractor.saved_env_exists():
127 self._start_task(env_extractor, self._env_extractor_exit)
129 # If the environment.bz2 doesn't exist, then ebuild.sh will
130 # source the ebuild as a fallback.
134 def _env_extractor_exit(self, env_extractor):
135 if self._default_exit(env_extractor) != os.EX_OK:
141 def _start_lock(self):
142 if (self.phase in self._locked_phases and
143 "ebuild-locks" in self.settings.features):
144 eroot = self.settings["EROOT"]
145 lock_path = os.path.join(eroot, portage.VDB_PATH + "-ebuild")
146 if os.access(os.path.dirname(lock_path), os.W_OK):
147 self._ebuild_lock = AsynchronousLock(path=lock_path,
148 scheduler=self.scheduler)
149 self._start_task(self._ebuild_lock, self._lock_exit)
154 def _lock_exit(self, ebuild_lock):
155 if self._default_exit(ebuild_lock) != os.EX_OK:
160 def _start_ebuild(self):
162 # Don't open the log file during the clean phase since the
163 # open file can result in an nfs lock on $T/build.log which
164 # prevents the clean phase from removing $T.
166 if self.phase not in ("clean", "cleanrm") and \
167 self.settings.get("PORTAGE_BACKGROUND") != "subprocess":
168 logfile = self.settings.get("PORTAGE_LOG_FILE")
171 if not self.background and self.phase == 'nofetch':
172 # All the pkg_nofetch output goes to stderr since
173 # it's considered to be an error message.
174 fd_pipes = {1 : sys.stderr.fileno()}
176 ebuild_process = EbuildProcess(actionmap=self.actionmap,
177 background=self.background, fd_pipes=fd_pipes, logfile=logfile,
178 phase=self.phase, scheduler=self.scheduler,
179 settings=self.settings)
181 self._start_task(ebuild_process, self._ebuild_exit)
183 def _ebuild_exit(self, ebuild_process):
185 if self._ebuild_lock is not None:
186 self._ebuild_lock.unlock()
187 self._ebuild_lock = None
190 if self._default_exit(ebuild_process) != os.EX_OK:
191 if self.phase == "test" and \
192 "test-fail-continue" in self.settings.features:
198 self.returncode = None
201 if self.settings.get("PORTAGE_BACKGROUND") != "subprocess":
202 logfile = self.settings.get("PORTAGE_LOG_FILE")
204 if self.phase == "install":
206 _check_build_log(self.settings, out=out)
208 self.scheduler.output(msg, log_path=logfile)
214 settings = self.settings
215 _post_phase_userpriv_perms(settings)
217 if self.phase == "unpack":
218 # Bump WORKDIR timestamp, in case tar gave it a timestamp
219 # that will interfere with distfiles / WORKDIR timestamp
220 # comparisons as reported in bug #332217.
221 timestamp = time.time()
222 os.utime(settings["WORKDIR"], (timestamp, timestamp))
223 elif self.phase == "install":
225 _post_src_install_write_metadata(settings)
226 _post_src_install_uid_fix(settings, out)
229 self.scheduler.output(msg, log_path=logfile)
230 elif self.phase == "preinst":
231 _preinst_bsdflags(settings)
232 elif self.phase == "postinst":
233 _postinst_bsdflags(settings)
235 post_phase_cmds = _post_phase_cmds.get(self.phase)
236 if post_phase_cmds is not None:
237 if logfile is not None and self.phase in ("install",):
238 # Log to a temporary file, since the code we are running
239 # reads PORTAGE_LOG_FILE for QA checks, and we want to
240 # avoid annoying "gzip: unexpected end of file" messages
241 # when FEATURES=compress-build-logs is enabled.
242 fd, logfile = tempfile.mkstemp()
244 post_phase = MiscFunctionsProcess(background=self.background,
245 commands=post_phase_cmds, logfile=logfile, phase=self.phase,
246 scheduler=self.scheduler, settings=settings)
247 self._start_task(post_phase, self._post_phase_exit)
250 # this point is not reachable if there was a failure and
251 # we returned for die_hooks above, so returncode must
252 # indicate success (especially if ebuild_process.returncode
253 # is unsuccessful and test-fail-continue came into play)
254 self.returncode = os.EX_OK
255 self._current_task = None
258 def _post_phase_exit(self, post_phase):
260 self._assert_current(post_phase)
263 if self.settings.get("PORTAGE_BACKGROUND") != "subprocess":
264 log_path = self.settings.get("PORTAGE_LOG_FILE")
266 if post_phase.logfile is not None and \
267 post_phase.logfile != log_path:
268 # We were logging to a temp file (see above), so append
269 # temp file to main log and remove temp file.
270 self._append_temp_log(post_phase.logfile, log_path)
272 if self._final_exit(post_phase) != os.EX_OK:
273 writemsg("!!! post %s failed; exiting.\n" % self.phase,
278 if self.phase == "install":
280 _post_src_install_soname_symlinks(self.settings, out)
283 self.scheduler.output(msg, log_path=log_path)
285 self._current_task = None
289 def _append_temp_log(self, temp_log, log_path):
291 temp_file = open(_unicode_encode(temp_log,
292 encoding=_encodings['fs'], errors='strict'), 'rb')
294 log_file, log_file_real = self._open_log(log_path)
296 for line in temp_file:
301 if log_file_real is not log_file:
302 log_file_real.close()
305 def _open_log(self, log_path):
307 f = open(_unicode_encode(log_path,
308 encoding=_encodings['fs'], errors='strict'),
312 if log_path.endswith('.gz'):
313 f = gzip.GzipFile(filename='', mode='ab', fileobj=f)
317 def _die_hooks(self):
318 self.returncode = None
320 die_hooks = MiscFunctionsProcess(background=self.background,
321 commands=[phase], phase=phase,
322 scheduler=self.scheduler, settings=self.settings)
323 self._start_task(die_hooks, self._die_hooks_exit)
325 def _die_hooks_exit(self, die_hooks):
326 if self.phase != 'clean' and \
327 'noclean' not in self.settings.features and \
328 'fail-clean' in self.settings.features:
329 self._default_exit(die_hooks)
332 self._final_exit(die_hooks)
336 def _fail_clean(self):
337 self.returncode = None
338 portage.elog.elog_process(self.settings.mycpv, self.settings)
340 clean_phase = EbuildPhase(background=self.background,
341 phase=phase, scheduler=self.scheduler, settings=self.settings)
342 self._start_task(clean_phase, self._fail_clean_exit)
345 def _fail_clean_exit(self, clean_phase):
346 self._final_exit(clean_phase)
350 def _elog(self, elog_funcname, lines, background=None):
351 if background is None:
352 background = self.background
355 elog_func = getattr(elog_messages, elog_funcname)
356 global_havecolor = portage.output.havecolor
358 portage.output.havecolor = \
359 self.settings.get('NOCOLOR', 'false').lower() in ('no', 'false')
361 elog_func(line, phase=phase, key=self.settings.mycpv, out=out)
363 portage.output.havecolor = global_havecolor
367 if self.settings.get("PORTAGE_BACKGROUND") != "subprocess":
368 log_path = self.settings.get("PORTAGE_LOG_FILE")
369 self.scheduler.output(msg, log_path=log_path,
370 background=background)