class EbuildFetcher(SpawnProcess):
- __slots__ = ("fetchonly", "fetchall", "pkg",)
+ __slots__ = ("config_pool", "fetchonly", "fetchall", "pkg", "prefetch") + \
+ ("_build_dir",)
def _start(self):
root_config = self.pkg.root_config
portdb = root_config.trees["porttree"].dbapi
ebuild_path = portdb.findname(self.pkg.cpv)
- settings = root_config.settings
+ settings = self.config_pool.allocate()
+ self._build_dir = EbuildBuildDir(pkg=self.pkg, settings=settings)
+ self._build_dir.lock()
+ if self.logfile is None:
+ self.logfile = settings.get("PORTAGE_LOG_FILE")
+
phase = "fetch"
if self.fetchall:
phase = "fetchall"
fetch_env = os.environ.copy()
fetch_env["PORTAGE_NICENESS"] = "0"
- if self.fetchonly:
+ if self.prefetch:
fetch_env["PORTAGE_PARALLEL_FETCHONLY"] = "1"
ebuild_binary = os.path.join(
self.env = fetch_env
SpawnProcess._start(self)
+ def _set_returncode(self, wait_retval):
+ SpawnProcess._set_returncode(self, wait_retval)
+ # Collect elog messages that might have been
+ # created by the pkg_nofetch phase.
+ if self._build_dir is not None:
+ # Skip elog messages for prefetch, in order to avoid duplicates.
+ if not self.prefetch and self.returncode != os.EX_OK:
+ elog_out = None
+ if self.logfile is not None:
+ if self.background:
+ elog_out = open(self.logfile, 'a')
+ eerror("Fetch failed for '%s'" % self.pkg.cpv,
+ phase="unpack", key=self.pkg.cpv, out=elog_out)
+ if elog_out is not None:
+ elog_out.close()
+ if not self.prefetch:
+ portage.elog.elog_process(self.pkg.cpv, self._build_dir.settings)
+ if self.fetchonly or self.returncode == os.EX_OK:
+ try:
+ shutil.rmtree(self._build_dir.settings["PORTAGE_BUILDDIR"])
+ except EnvironmentError, e:
+ if e.errno != errno.ENOENT:
+ raise
+ del e
+ self._build_dir.unlock()
+ self.config_pool.deallocate(self._build_dir.settings)
+ self._build_dir = None
+
class EbuildBuildDir(SlotObject):
__slots__ = ("dir_path", "pkg", "settings",
portdb = root_config.trees["porttree"].dbapi
ebuild_path = portdb.findname(self.pkg.cpv)
settings = self.settings
+ settings.setcpv(self.pkg)
debug = settings.get("PORTAGE_DEBUG") == "1"
use_cache = 1 # always true
portage.doebuild_environment(ebuild_path, "setup", root_config.root,
self.settings, debug, use_cache, portdb)
dir_path = self.settings["PORTAGE_BUILDDIR"]
+ portage.prepare_build_dirs(self.pkg.root, self.settings, 0)
catdir = os.path.dirname(dir_path)
self._catdir = catdir
class EbuildBuild(CompositeTask):
- __slots__ = ("args_set", "background", "find_blockers",
+ __slots__ = ("args_set", "config_pool", "find_blockers",
"ldpath_mtimes", "logger", "opts", "pkg", "pkg_count",
"prefetcher", "settings", "world_atom") + \
("_build_dir", "_buildpkg", "_ebuild_path", "_issyspkg", "_tree")
return
fetch_log = None
- if self.background:
- fetch_log = self.scheduler.fetch.log_file
- fetcher = EbuildFetcher(fetchall=opts.fetch_all_uri,
+ fetcher = EbuildFetcher(config_pool=self.config_pool,
+ fetchall=opts.fetch_all_uri,
fetchonly=opts.fetchonly,
background=self.background, logfile=fetch_log,
pkg=pkg, scheduler=self.scheduler)
self._start_task(fetcher, self._fetch_exit)
def _fetch_exit(self, fetcher):
-
opts = self.opts
pkg = self.pkg
+ fetch_failed = False
if opts.fetchonly:
- if self._final_exit(fetcher) != os.EX_OK:
- if not self.background:
- eerror("Fetch for %s failed, continuing..." % pkg.cpv,
- phase="unpack", key=pkg.cpv)
- self.wait()
- return
+ fetch_failed = self._final_exit(fetcher) != os.EX_OK
+ else:
+ fetch_failed = self._default_exit(fetcher) != os.EX_OK
+
+ if fetch_failed and fetcher.logfile is not None and \
+ os.path.exists(fetcher.logfile):
+ self.settings["PORTAGE_LOG_FILE"] = fetcher.logfile
- if self._default_exit(fetcher) != os.EX_OK:
+ if fetch_failed or opts.fetchonly:
self.wait()
return
"""
__slots__ = ("args_set",
- "binpkg_opts", "build_opts", "emerge_opts",
+ "binpkg_opts", "build_opts", "config_pool", "emerge_opts",
"failed_fetches", "find_blockers", "logger", "mtimedb", "pkg",
"pkg_count", "pkg_to_replace", "prefetcher",
"settings", "statusMessage", "world_atom") + \
build = EbuildBuild(args_set=args_set,
background=self.background,
+ config_pool=self.config_pool,
find_blockers=find_blockers,
ldpath_mtimes=ldpath_mtimes, logger=logger,
opts=build_opts, pkg=pkg, pkg_count=pkg_count,
__slots__ = ("build_dir", "build_log",
"fetch_log", "pkg", "returncode")
+ class _ConfigPool(object):
+ """Interface for a task to temporarily allocate a config
+ instance from a pool. This allows a task to be constructed
+ long before the config instance actually becomes needed, like
+ when prefetchers are constructed for the whole merge list."""
+ __slots__ = ("_root", "_allocate", "_deallocate")
+ def __init__(self, root, allocate, deallocate):
+ self._root = root
+ self._allocate = allocate
+ self._deallocate = deallocate
+ def allocate(self):
+ return self._allocate(self._root)
+ def deallocate(self, settings):
+ self._deallocate(settings)
+
def __init__(self, settings, trees, mtimedb, myopts,
spinner, mergelist, favorites, digraph):
PollScheduler.__init__(self)
prefetchers = self._prefetchers
getbinpkg = "--getbinpkg" in self.myopts
- for pkg in self._mergelist:
+ # In order to avoid "waiting for lock" messages
+ # at the beginning, which annoy users, never
+ # spawn a prefetcher for the first package.
+ for pkg in self._mergelist[1:]:
prefetcher = self._create_prefetcher(pkg)
if prefetcher is not None:
self._task_queues.fetch.add(prefetcher)
elif pkg.type_name == "ebuild":
prefetcher = EbuildFetcher(background=True,
+ config_pool=self._ConfigPool(pkg.root,
+ self._allocate_config, self._deallocate_config),
fetchonly=1, logfile=self._fetch_log,
- pkg=pkg, scheduler=self._sched_iface)
+ pkg=pkg, prefetch=True, scheduler=self._sched_iface)
elif pkg.type_name == "binary" and \
"--getbinpkg" in self.myopts and \
task = MergeListItem(args_set=self._args_set,
background=self._background, binpkg_opts=self._binpkg_opts,
build_opts=self._build_opts,
+ config_pool=self._ConfigPool(pkg.root,
+ self._allocate_config, self._deallocate_config),
emerge_opts=self.myopts,
failed_fetches=self._failed_fetches,
find_blockers=self._find_blockers(pkg), logger=self._logger,
for cpv in plibdata:
print colorize("WARN", ">>>") + " package: %s" % cpv
+ samefile_map = {}
for f in plibdata[cpv]:
- print colorize("WARN", " * ") + " - %s" % f
+ real_path = os.path.realpath(f)
+ alt_paths = samefile_map.get(real_path)
+ if alt_paths is None:
+ alt_paths = set()
+ samefile_map[real_path] = alt_paths
+ alt_paths.add(f)
+
+ for alt_paths in samefile_map.itervalues():
+ alt_paths = sorted(alt_paths)
+ for p in alt_paths:
+ print colorize("WARN", " * ") + " - %s" % (p,)
+ f = alt_paths[0]
consumers = consumer_map[f]
for c in consumers[:MAX_DISPLAY]:
print colorize("WARN", " * ") + " used by %s (%s)" % \
class SetConfigError(Exception):
pass
-class SetConfig(SafeConfigParser):
+class SetConfig(object):
def __init__(self, paths, settings, trees):
- SafeConfigParser.__init__(self)
- self.read(paths)
+ self._parser = SafeConfigParser()
+ self._parser.read(paths)
self.errors = []
self.psets = {}
self.trees = trees
self.active = []
def update(self, setname, options):
+ parser = self._parser
self.errors = []
if not setname in self.psets:
options["name"] = setname
# for the unlikely case that there is already a section with the requested setname
import random
- while setname in self.sections():
+ while setname in parser.sections():
setname = "%08d" % random.randint(0, 10**10)
- self.add_section(setname)
+ parser.add_section(setname)
for k, v in options.items():
- self.set(setname, k, v)
+ parser.set(setname, k, v)
else:
section = self.psets[setname].creator
- if self.has_option(section, "multiset") and self.getboolean(section, "multiset"):
+ if parser.has_option(section, "multiset") and \
+ parser.getboolean(section, "multiset"):
self.errors.append("Invalid request to reconfigure set '%s' generated by multiset section '%s'" % (setname, section))
return
for k, v in options.items():
- self.set(section, k, v)
+ parser.set(section, k, v)
self._parse(update=True)
def _parse(self, update=False):
if self._parsed and not update:
return
- for sname in self.sections():
+ parser = self._parser
+ for sname in parser.sections():
# find classname for current section, default to file based sets
- if not self.has_option(sname, "class"):
+ if not parser.has_option(sname, "class"):
classname = "portage.sets.files.StaticFileSet"
else:
- classname = self.get(sname, "class")
+ classname = parser.get(sname, "class")
# try to import the specified class
try:
continue
# prepare option dict for the current section
optdict = {}
- for oname in self.options(sname):
- optdict[oname] = self.get(sname, oname)
+ for oname in parser.options(sname):
+ optdict[oname] = parser.get(sname, oname)
# create single or multiple instances of the given class depending on configuration
- if self.has_option(sname, "multiset") and self.getboolean(sname, "multiset"):
+ if parser.has_option(sname, "multiset") and \
+ parser.getboolean(sname, "multiset"):
if hasattr(setclass, "multiBuilder"):
newsets = {}
try:
if x in self.psets and not update:
self.errors.append("Redefinition of set '%s' (sections: '%s', '%s')" % (x, self.psets[x].creator, sname))
newsets[x].creator = sname
- if self.has_option(sname, "world-candidate") and not self.getboolean(sname, "world-candidate"):
+ if parser.has_option(sname, "world-candidate") and \
+ not parser.getboolean(sname, "world-candidate"):
newsets[x].world_candidate = False
self.psets.update(newsets)
else:
continue
else:
try:
- setname = self.get(sname, "name")
+ setname = parser.get(sname, "name")
except NoOptionError:
setname = sname
if setname in self.psets and not update:
try:
self.psets[setname] = setclass.singleBuilder(optdict, self.settings, self.trees)
self.psets[setname].creator = sname
- if self.has_option(sname, "world-candidate") and not self.getboolean(sname, "world-candidate"):
+ if parser.has_option(sname, "world-candidate") and \
+ not parser.getboolean(sname, "world-candidate"):
self.psets[setname].world_candidate = False
except SetConfigError, e:
self.errors.append("Configuration error in section '%s': %s" % (sname, str(e)))
def getSetAtoms(self, setname, ignorelist=None):
myset = self.getSets()[setname]
myatoms = myset.getAtoms()
-
+ parser = self._parser
extend = set()
remove = set()
intersect = set()
if ignorelist is None:
ignorelist = set()
if not setname in ignorelist:
- if self.has_option(myset.creator, "extend"):
- extend.update(self.get(myset.creator, "extend").split())
- if self.has_option(myset.creator, "remove"):
- remove.update(self.get(myset.creator, "remove").split())
- if self.has_option(myset.creator, "intersect"):
- intersect.update(self.get(myset.creator, "intersect").split())
+ if parser.has_option(myset.creator, "extend"):
+ extend.update(parser.get(myset.creator, "extend").split())
+ if parser.has_option(myset.creator, "remove"):
+ remove.update(parser.get(myset.creator, "remove").split())
+ if parser.has_option(myset.creator, "intersect"):
+ intersect.update(parser.get(myset.creator, "intersect").split())
ignorelist.add(setname)
for n in myset.getNonAtoms():