1 # Copyright 1999-2011 Gentoo Foundation
2 # Distributed under the terms of the GNU General Public License v2
8 from _emerge.AsynchronousLock import AsynchronousLock
9 from _emerge.BinpkgEnvExtractor import BinpkgEnvExtractor
10 from _emerge.MiscFunctionsProcess import MiscFunctionsProcess
11 from _emerge.EbuildProcess import EbuildProcess
12 from _emerge.CompositeTask import CompositeTask
13 from portage.util import writemsg
14 from portage.xml.metadata import MetaDataXML
16 portage.proxy.lazyimport.lazyimport(globals(),
17 'portage.elog:messages@elog_messages',
18 'portage.package.ebuild.doebuild:_check_build_log,' + \
19 '_post_phase_cmds,_post_phase_userpriv_perms,' + \
20 '_post_src_install_chost_fix,' + \
21 '_post_src_install_uid_fix,_postinst_bsdflags,' + \
24 from portage import os
25 from portage import StringIO
26 from portage import _encodings
27 from portage import _unicode_decode
28 from portage import _unicode_encode
30 class EbuildPhase(CompositeTask):
32 __slots__ = ("actionmap", "phase", "settings") + \
35 # FEATURES displayed prior to setup phase
36 _features_display = ("ccache", "distcc", "fakeroot",
37 "installsources", "keeptemp", "keepwork", "nostrip",
38 "preserve-libs", "sandbox", "selinux", "sesandbox",
39 "splitdebug", "suidctl", "test", "userpriv",
43 _locked_phases = ("setup", "preinst", "postinst", "prerm", "postrm")
47 need_builddir = self.phase not in EbuildProcess._phases_without_builddir
50 phase_completed_file = os.path.join(
51 self.settings['PORTAGE_BUILDDIR'],
52 ".%sed" % self.phase.rstrip('e'))
53 if not os.path.exists(phase_completed_file):
54 # If the phase is really going to run then we want
55 # to eliminate any stale elog messages that may
56 # exist from a previous run.
58 os.unlink(os.path.join(self.settings['T'],
59 'logging', self.phase))
63 if self.phase == 'setup':
65 use = self.settings.get('PORTAGE_BUILT_USE')
67 use = self.settings['PORTAGE_USE']
71 metadata_xml_path = os.path.join(os.path.dirname(self.settings['EBUILD']), "metadata.xml")
72 if os.path.isfile(metadata_xml_path):
73 herds_path = os.path.join(self.settings['PORTDIR'],
76 metadata_xml = MetaDataXML(metadata_xml_path, herds_path)
77 maint_str = metadata_xml.format_maintainer_string()
78 upstr_str = metadata_xml.format_upstream_string()
80 maint_str = "<invalid metadata.xml>"
83 msg.append("Package: %s" % self.settings.mycpv)
84 if self.settings.get('PORTAGE_REPO_NAME'):
85 msg.append("Repository: %s" % self.settings['PORTAGE_REPO_NAME'])
87 msg.append("Maintainer: %s" % maint_str)
89 msg.append("Upstream: %s" % upstr_str)
91 msg.append("USE: %s" % use)
92 relevant_features = []
93 enabled_features = self.settings.features
94 for x in self._features_display:
95 if x in enabled_features:
96 relevant_features.append(x)
98 msg.append("FEATURES: %s" % " ".join(relevant_features))
99 self._elog('einfo', msg)
101 if self.phase == 'package':
102 if 'PORTAGE_BINPKG_TMPFILE' not in self.settings:
103 self.settings['PORTAGE_BINPKG_TMPFILE'] = \
104 os.path.join(self.settings['PKGDIR'],
105 self.settings['CATEGORY'], self.settings['PF']) + '.tbz2'
107 if self.phase in ("pretend", "prerm"):
108 env_extractor = BinpkgEnvExtractor(background=self.background,
109 scheduler=self.scheduler, settings=self.settings)
110 if env_extractor.saved_env_exists():
111 self._start_task(env_extractor, self._env_extractor_exit)
113 # If the environment.bz2 doesn't exist, then ebuild.sh will
114 # source the ebuild as a fallback.
118 def _env_extractor_exit(self, env_extractor):
119 if self._default_exit(env_extractor) != os.EX_OK:
125 def _start_lock(self):
126 if (self.phase in self._locked_phases and
127 "ebuild-locks" in self.settings.features):
128 eroot = self.settings["EROOT"]
129 lock_path = os.path.join(eroot, portage.VDB_PATH + "-ebuild")
130 if os.access(os.path.dirname(lock_path), os.W_OK):
131 self._ebuild_lock = AsynchronousLock(path=lock_path,
132 scheduler=self.scheduler)
133 self._start_task(self._ebuild_lock, self._lock_exit)
138 def _lock_exit(self, ebuild_lock):
139 if self._default_exit(ebuild_lock) != os.EX_OK:
144 def _start_ebuild(self):
146 # Don't open the log file during the clean phase since the
147 # open file can result in an nfs lock on $T/build.log which
148 # prevents the clean phase from removing $T.
150 if self.phase not in ("clean", "cleanrm") and \
151 self.settings.get("PORTAGE_BACKGROUND") != "subprocess":
152 logfile = self.settings.get("PORTAGE_LOG_FILE")
155 if not self.background and self.phase == 'nofetch':
156 # All the pkg_nofetch output goes to stderr since
157 # it's considered to be an error message.
158 fd_pipes = {1 : sys.stderr.fileno()}
160 ebuild_process = EbuildProcess(actionmap=self.actionmap,
161 background=self.background, fd_pipes=fd_pipes, logfile=logfile,
162 phase=self.phase, scheduler=self.scheduler,
163 settings=self.settings)
165 self._start_task(ebuild_process, self._ebuild_exit)
167 def _ebuild_exit(self, ebuild_process):
169 if self._ebuild_lock is not None:
170 self._ebuild_lock.unlock()
171 self._ebuild_lock = None
174 if self._default_exit(ebuild_process) != os.EX_OK:
175 if self.phase == "test" and \
176 "test-fail-continue" in self.settings.features:
182 self.returncode = None
185 if self.settings.get("PORTAGE_BACKGROUND") != "subprocess":
186 logfile = self.settings.get("PORTAGE_LOG_FILE")
188 if self.phase == "install":
189 out = portage.StringIO()
190 _check_build_log(self.settings, out=out)
191 msg = _unicode_decode(out.getvalue(),
192 encoding=_encodings['content'], errors='replace')
193 self.scheduler.output(msg, log_path=logfile)
199 settings = self.settings
200 _post_phase_userpriv_perms(settings)
202 if self.phase == "install":
203 out = portage.StringIO()
204 _post_src_install_chost_fix(settings)
205 _post_src_install_uid_fix(settings, out)
206 msg = _unicode_decode(out.getvalue(),
207 encoding=_encodings['content'], errors='replace')
209 self.scheduler.output(msg, log_path=logfile)
210 elif self.phase == "preinst":
211 _preinst_bsdflags(settings)
212 elif self.phase == "postinst":
213 _postinst_bsdflags(settings)
215 post_phase_cmds = _post_phase_cmds.get(self.phase)
216 if post_phase_cmds is not None:
217 if logfile is not None and self.phase in ("install",):
218 # Log to a temporary file, since the code we are running
219 # reads PORTAGE_LOG_FILE for QA checks, and we want to
220 # avoid annoying "gzip: unexpected end of file" messages
221 # when FEATURES=compress-build-logs is enabled.
222 fd, logfile = tempfile.mkstemp()
224 post_phase = MiscFunctionsProcess(background=self.background,
225 commands=post_phase_cmds, logfile=logfile, phase=self.phase,
226 scheduler=self.scheduler, settings=settings)
227 self._start_task(post_phase, self._post_phase_exit)
230 # this point is not reachable if there was a failure and
231 # we returned for die_hooks above, so returncode must
232 # indicate success (especially if ebuild_process.returncode
233 # is unsuccessful and test-fail-continue came into play)
234 self.returncode = os.EX_OK
235 self._current_task = None
238 def _post_phase_exit(self, post_phase):
240 self._assert_current(post_phase)
243 if self.settings.get("PORTAGE_BACKGROUND") != "subprocess":
244 log_path = self.settings.get("PORTAGE_LOG_FILE")
246 if post_phase.logfile is not None and \
247 post_phase.logfile != log_path:
248 # We were logging to a temp file (see above), so append
249 # temp file to main log and remove temp file.
250 self._append_temp_log(post_phase.logfile, log_path)
252 if self._final_exit(post_phase) != os.EX_OK:
253 writemsg("!!! post %s failed; exiting.\n" % self.phase,
257 self._current_task = None
261 def _append_temp_log(self, temp_log, log_path):
263 temp_file = open(_unicode_encode(temp_log,
264 encoding=_encodings['fs'], errors='strict'), 'rb')
266 log_file = self._open_log(log_path)
268 for line in temp_file:
275 def _open_log(self, log_path):
277 f = open(_unicode_encode(log_path,
278 encoding=_encodings['fs'], errors='strict'),
281 if log_path.endswith('.gz'):
282 f = gzip.GzipFile(filename='', mode='ab', fileobj=f)
286 def _die_hooks(self):
287 self.returncode = None
289 die_hooks = MiscFunctionsProcess(background=self.background,
290 commands=[phase], phase=phase,
291 scheduler=self.scheduler, settings=self.settings)
292 self._start_task(die_hooks, self._die_hooks_exit)
294 def _die_hooks_exit(self, die_hooks):
295 if self.phase != 'clean' and \
296 'noclean' not in self.settings.features and \
297 'fail-clean' in self.settings.features:
298 self._default_exit(die_hooks)
301 self._final_exit(die_hooks)
305 def _fail_clean(self):
306 self.returncode = None
307 portage.elog.elog_process(self.settings.mycpv, self.settings)
309 clean_phase = EbuildPhase(background=self.background,
310 phase=phase, scheduler=self.scheduler, settings=self.settings)
311 self._start_task(clean_phase, self._fail_clean_exit)
314 def _fail_clean_exit(self, clean_phase):
315 self._final_exit(clean_phase)
319 def _elog(self, elog_funcname, lines):
322 elog_func = getattr(elog_messages, elog_funcname)
323 global_havecolor = portage.output.havecolor
325 portage.output.havecolor = \
326 self.settings.get('NOCOLOR', 'false').lower() in ('no', 'false')
328 elog_func(line, phase=phase, key=self.settings.mycpv, out=out)
330 portage.output.havecolor = global_havecolor
331 msg = _unicode_decode(out.getvalue(),
332 encoding=_encodings['content'], errors='replace')
335 if self.settings.get("PORTAGE_BACKGROUND") != "subprocess":
336 log_path = self.settings.get("PORTAGE_LOG_FILE")
337 self.scheduler.output(msg, log_path=log_path)