Exheredludis/paludis/repository_name_cache.cc
Saleem Abdulrasool 64ba7d5be8 modernize: use default method synthesis
Convert a number of destructors to default synthesized functions.  Try to
inline a few instances into the header.  It should be possible to inline all of
them, however, gcc seems to emit a number of warnings.  Furthermore, some of the
destructors are pure-virtualed, but provide an implementation.  Placing the
definition into the header causes ODR violations.
2016-08-06 11:58:04 -07:00

319 lines
11 KiB
C++

/* vim: set sw=4 sts=4 et foldmethod=syntax : */
/*
* Copyright (c) 2006, 2007, 2008, 2009, 2010, 2011, 2013 Ciaran McCreesh
*
* This file is part of the Paludis package manager. Paludis is free software;
* you can redistribute it and/or modify it under the terms of the GNU General
* Public License version 2, as published by the Free Software Foundation.
*
* Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 59 Temple
* Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "repository_name_cache.hh"
#include <paludis/repository.hh>
#include <paludis/util/log.hh>
#include <paludis/util/stringify.hh>
#include <paludis/util/set.hh>
#include <paludis/util/pimp-impl.hh>
#include <paludis/util/wrapped_forward_iterator.hh>
#include <paludis/util/wrapped_output_iterator.hh>
#include <paludis/util/hashes.hh>
#include <paludis/util/safe_ofstream.hh>
#include <paludis/util/safe_ifstream.hh>
#include <paludis/util/fs_iterator.hh>
#include <paludis/util/fs_stat.hh>
#include <paludis/util/fs_error.hh>
#include <unordered_map>
#include <memory>
#include <set>
#include <mutex>
#include <cstring>
#include <cerrno>
using namespace paludis;
namespace paludis
{
typedef std::unordered_map<PackageNamePart, std::set<CategoryNamePart>, Hash<PackageNamePart> > NameCacheMap;
template<>
struct Imp<RepositoryNameCache>
{
mutable std::mutex mutex;
mutable bool usable;
mutable FSPath location;
const Repository * const repo;
mutable NameCacheMap name_cache_map;
mutable bool checked_name_cache_map;
Imp(const FSPath & l, const Repository * const r) :
usable(l != FSPath("/var/empty")),
location(l == FSPath("/var/empty") ? l : l / stringify(r->name())),
repo(r),
checked_name_cache_map(false)
{
}
NameCacheMap::iterator find(const PackageNamePart &) const;
void update(const PackageNamePart & p, NameCacheMap::iterator r);
};
}
NameCacheMap::iterator
Imp<RepositoryNameCache>::find(const PackageNamePart & p) const
{
NameCacheMap::iterator r(name_cache_map.find(p));
location = FSPath(stringify(location));
if (name_cache_map.end() == r)
{
r = name_cache_map.insert(std::make_pair(p, std::set<CategoryNamePart>())).first;
if (! checked_name_cache_map)
{
if (location.stat().is_directory() && (location / "_VERSION_").stat().exists())
{
SafeIFStream vvf(location / "_VERSION_");
std::string line;
std::getline(vvf, line);
if (line != "paludis-2")
{
Log::get_instance()->message("repository.names_cache.unsupported", ll_warning, lc_context)
<< "Names cache for '" << repo->name() << "' has version string '" << line
<< "', which is not supported. Was it generated using a different Paludis version? Perhaps you need to regenerate "
"the cache using 'cave fix-cache'?";
usable = false;
return name_cache_map.end();
}
std::getline(vvf, line);
if (line != stringify(repo->name()))
{
Log::get_instance()->message("repository.names_cache.different", ll_warning, lc_context)
<< "Names cache for '" << repo->name() << "' was generated for repository '" << line
<< "', so it cannot be used. You must not have multiple name caches at the same location.";
usable = false;
return name_cache_map.end();
}
checked_name_cache_map = true;
}
else if ((location.dirname() / "_VERSION_").stat().exists())
{
Log::get_instance()->message("repository.names_cache.old", ll_warning, lc_context)
<< "Names cache for '" << repo->name() << "' does not exist at '" << location
<< "', but a names cache exists at '" << location.dirname()
<< "'. This was probably generated by a Paludis version "
"older than 0.18.0. The names cache now automatically appends the repository name to the "
"directory. You probably want to manually remove '" << location.dirname() <<
"' and then regenerate the cache.";
usable = false;
return name_cache_map.end();
}
else
{
Log::get_instance()->message("repository.names_cache.unversioned", ll_warning, lc_context)
<< "Names cache for '" << repo->name()
<< "' has no version information, so cannot be used. Either it was generated using "
"an older Paludis version or it has not yet been generated. Perhaps you need to regenerate "
"the cache using 'cave fix-cache'?";
usable = false;
return name_cache_map.end();
}
}
FSPath ff(location / stringify(p));
if (ff.stat().exists())
{
SafeIFStream f(ff);
std::string line;
while (std::getline(f, line))
r->second.insert(CategoryNamePart(line));
}
}
return r;
}
void
Imp<RepositoryNameCache>::update(const PackageNamePart & p, NameCacheMap::iterator r)
{
FSPath ff(location / stringify(p));
if (r->second.empty() && ff.stat().exists())
{
try
{
ff.unlink();
}
catch (const FSError & e)
{
Log::get_instance()->message("repository.names_cache.unlink_failed", ll_warning, lc_context)
<< "Cannot unlink '" << ff << "': " << e.message() << " (" << e.what() << ")";
}
return;
}
try
{
SafeOFStream f(ff, -1, true);
for (std::set<CategoryNamePart>::const_iterator it(r->second.begin()),
it_end(r->second.end()); it_end != it; ++it)
f << *it << std::endl;
}
catch (const SafeOFStreamError & e)
{
Log::get_instance()->message("repository.names_cache.write_failed", ll_warning, lc_context)
<< "Cannot write '" << ff << "': '" << e.message() << "' (" << e.what() << ")";
return;
}
}
RepositoryNameCache::RepositoryNameCache(
const FSPath & location,
const Repository * const repo) :
_imp(location, repo)
{
}
RepositoryNameCache::~RepositoryNameCache() = default;
std::shared_ptr<const CategoryNamePartSet>
RepositoryNameCache::category_names_containing_package(const PackageNamePart & p) const
{
std::unique_lock<std::mutex> l(_imp->mutex);
if (! usable())
return std::shared_ptr<const CategoryNamePartSet>();
Context context("When using name cache at '" + stringify(_imp->location) + "':");
std::shared_ptr<CategoryNamePartSet> result(std::make_shared<CategoryNamePartSet>());
NameCacheMap::iterator r(_imp->find(p));
if (_imp->name_cache_map.end() == r)
return std::shared_ptr<const CategoryNamePartSet>();
std::copy(r->second.begin(), r->second.end(), result->inserter());
return result;
}
void
RepositoryNameCache::regenerate_cache() const
{
std::unique_lock<std::mutex> l(_imp->mutex);
if (_imp->location == FSPath("/var/empty"))
return;
Context context("When generating repository names cache at '"
+ stringify(_imp->location) + "':");
if (_imp->location.stat().is_directory())
for (FSIterator i(_imp->location, { fsio_inode_sort }), i_end ; i != i_end ; ++i)
i->unlink();
FSPath main_cache_dir(_imp->location.dirname());
FSStat main_cache_dir_stat(main_cache_dir);
if (! main_cache_dir_stat.exists())
Log::get_instance()->message("repository.names_cache.no_dir", ll_warning, lc_context)
<< "Names cache directory '" << main_cache_dir << "' does not exist "
<< "(see the faq for why this directory will not be created automatically)";
if (_imp->location.mkdir(main_cache_dir_stat.permissions(), { fspmkdo_ok_if_exists }))
_imp->location.chmod(main_cache_dir_stat.permissions());
std::unordered_map<std::string, std::string, Hash<std::string> > m;
std::shared_ptr<const CategoryNamePartSet> cats(_imp->repo->category_names({ }));
for (CategoryNamePartSet::ConstIterator c(cats->begin()), c_end(cats->end()) ;
c != c_end ; ++c)
{
std::shared_ptr<const QualifiedPackageNameSet> pkgs(_imp->repo->package_names(*c, { }));
for (QualifiedPackageNameSet::ConstIterator p(pkgs->begin()), p_end(pkgs->end()) ;
p != p_end ; ++p)
m[stringify(p->package())].append(stringify(*c) + "\n");
}
for (std::unordered_map<std::string, std::string, Hash<std::string> >::const_iterator e(m.begin()), e_end(m.end()) ;
e != e_end ; ++e)
{
try
{
SafeOFStream f(_imp->location / stringify(e->first), -1, true);
f << e->second;
}
catch (const SafeOFStreamError & ee)
{
Log::get_instance()->message("repository.names_cache.write_failed", ll_warning, lc_context)
<< "Cannot write to '" << _imp->location << "': '" << ee.message() << "' (" << ee.what() << ")";
continue;
}
}
try
{
SafeOFStream f(_imp->location / "_VERSION_", -1, true);;
f << "paludis-2" << std::endl;
f << _imp->repo->name() << std::endl;
}
catch (const SafeOFStreamError & e)
{
Log::get_instance()->message("repository.names_cache.write_failed", ll_warning, lc_context)
<< "Cannot write to '" << _imp->location << "': '" << e.message() << "' (" << e.what() << ")";
}
}
void
RepositoryNameCache::add(const QualifiedPackageName & q)
{
std::unique_lock<std::mutex> l(_imp->mutex);
if (! usable())
return;
Context context("When adding '" + stringify(q) + "' to name cache at '" + stringify(_imp->location) + "':");
std::shared_ptr<CategoryNamePartSet> result(std::make_shared<CategoryNamePartSet>());
NameCacheMap::iterator r(_imp->find(q.package()));
if (_imp->name_cache_map.end() == r)
return;
r->second.insert(q.category());
_imp->update(q.package(), r);
}
void
RepositoryNameCache::remove(const QualifiedPackageName & q)
{
std::unique_lock<std::mutex> l(_imp->mutex);
if (! usable())
return;
Context context("When removing '" + stringify(q) + "' from name cache at '" + stringify(_imp->location) + "':");
std::shared_ptr<CategoryNamePartSet> result(std::make_shared<CategoryNamePartSet>());
NameCacheMap::iterator r(_imp->find(q.package()));
if (_imp->name_cache_map.end() == r)
return;
r->second.erase(q.category());
_imp->update(q.package(), r);
}
bool
RepositoryNameCache::usable() const noexcept
{
return _imp->usable;
}