64ba7d5be8
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.
319 lines
11 KiB
C++
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;
|
|
}
|
|
|