1 # Copyright 1999-2011 Gentoo Foundation
2 # Distributed under the terms of the GNU General Public License v2
4 from _emerge.AsynchronousLock import AsynchronousLock
5 from _emerge.SpawnProcess import SpawnProcess
7 from urllib.parse import urlparse as urllib_parse_urlparse
9 from urlparse import urlparse as urllib_parse_urlparse
13 from portage import os
14 from portage.util._pty import _create_pty_or_pipe
16 if sys.hexversion >= 0x3000000:
19 class BinpkgFetcher(SpawnProcess):
21 __slots__ = ("pkg", "pretend",
22 "locked", "pkg_path", "_lock_obj")
24 def __init__(self, **kwargs):
25 SpawnProcess.__init__(self, **kwargs)
27 self.pkg_path = pkg.root_config.trees["bintree"].getname(pkg.cpv)
35 pretend = self.pretend
36 bintree = pkg.root_config.trees["bintree"]
37 settings = bintree.settings
38 use_locks = "distlocks" in settings.features
39 pkg_path = self.pkg_path
42 portage.util.ensure_dirs(os.path.dirname(pkg_path))
45 exists = os.path.exists(pkg_path)
46 resume = exists and os.path.basename(pkg_path) in bintree.invalids
47 if not (pretend or resume):
48 # Remove existing file or broken symlink.
54 # urljoin doesn't work correctly with
55 # unrecognized protocols like sftp
56 if bintree._remote_has_index:
57 rel_uri = bintree._remotepkgs[pkg.cpv].get("PATH")
59 rel_uri = pkg.cpv + ".tbz2"
60 remote_base_uri = bintree._remotepkgs[pkg.cpv]["BASE_URI"]
61 uri = remote_base_uri.rstrip("/") + "/" + rel_uri.lstrip("/")
63 uri = settings["PORTAGE_BINHOST"].rstrip("/") + \
64 "/" + pkg.pf + ".tbz2"
67 portage.writemsg_stdout("\n%s\n" % uri, noiselevel=-1)
68 self._set_returncode((self.pid, os.EX_OK << 8))
72 protocol = urllib_parse_urlparse(uri)[0]
73 fcmd_prefix = "FETCHCOMMAND"
75 fcmd_prefix = "RESUMECOMMAND"
76 fcmd = settings.get(fcmd_prefix + "_" + protocol.upper())
78 fcmd = settings.get(fcmd_prefix)
81 "DISTDIR" : os.path.dirname(pkg_path),
83 "FILE" : os.path.basename(pkg_path)
86 fetch_env = dict(settings.items())
87 fetch_args = [portage.util.varexpand(x, mydict=fcmd_vars) \
88 for x in portage.util.shlex_split(fcmd)]
90 if self.fd_pipes is None:
92 fd_pipes = self.fd_pipes
94 # Redirect all output to stdout since some fetchers like
95 # wget pollute stderr (if portage detects a problem then it
96 # can send it's own message to stderr).
97 fd_pipes.setdefault(0, sys.stdin.fileno())
98 fd_pipes.setdefault(1, sys.stdout.fileno())
99 fd_pipes.setdefault(2, sys.stdout.fileno())
101 self.args = fetch_args
103 if settings.selinux_enabled():
104 self._selinux_type = settings["PORTAGE_FETCH_T"]
105 SpawnProcess._start(self)
107 def _pipe(self, fd_pipes):
108 """When appropriate, use a pty so that fetcher progress bars,
109 like wget has, will work properly."""
110 if self.background or not sys.stdout.isatty():
111 # When the output only goes to a log file,
112 # there's no point in creating a pty.
115 if not self.background:
116 stdout_pipe = fd_pipes.get(1)
117 got_pty, master_fd, slave_fd = \
118 _create_pty_or_pipe(copy_term_size=stdout_pipe)
119 return (master_fd, slave_fd)
121 def _set_returncode(self, wait_retval):
122 SpawnProcess._set_returncode(self, wait_retval)
123 if not self.pretend and self.returncode == os.EX_OK:
124 # If possible, update the mtime to match the remote package if
125 # the fetcher didn't already do it automatically.
126 bintree = self.pkg.root_config.trees["bintree"]
127 if bintree._remote_has_index:
128 remote_mtime = bintree._remotepkgs[self.pkg.cpv].get("MTIME")
129 if remote_mtime is not None:
131 remote_mtime = long(remote_mtime)
136 local_mtime = os.stat(self.pkg_path)[stat.ST_MTIME]
140 if remote_mtime != local_mtime:
142 os.utime(self.pkg_path,
143 (remote_mtime, remote_mtime))
152 This raises an AlreadyLocked exception if lock() is called
153 while a lock is already held. In order to avoid this, call
154 unlock() or check whether the "locked" attribute is True
155 or False before calling lock().
157 if self._lock_obj is not None:
158 raise self.AlreadyLocked((self._lock_obj,))
160 async_lock = AsynchronousLock(path=self.pkg_path,
161 scheduler=self.scheduler)
164 if async_lock.wait() != os.EX_OK:
165 # TODO: Use CompositeTask for better handling, like in EbuildPhase.
166 raise AssertionError("AsynchronousLock failed with returncode %s" \
167 % (async_lock.returncode,))
169 self._lock_obj = async_lock
172 class AlreadyLocked(portage.exception.PortageException):
176 if self._lock_obj is None:
178 self._lock_obj.unlock()
179 self._lock_obj = None