/* vim: set sw=4 sts=4 et foldmethod=syntax : */ /* * Copyright (c) 2005, 2006, 2007, 2008 Ciaran McCreesh * Copyright (c) 2006 Danny van Dyk * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace paludis; using namespace paludis::erepository; typedef std::tr1::unordered_map > CategoryMap; typedef std::tr1::unordered_map > PackagesMap; typedef std::tr1::unordered_map, Hash > IDMap; namespace paludis { template<> struct Implementation { const ERepository * const repository; const FSEntry tree_root; mutable Mutex big_nasty_mutex; mutable bool has_category_names; mutable CategoryMap category_names; mutable PackagesMap package_names; mutable IDMap ids; mutable std::tr1::shared_ptr category_names_collection; std::tr1::shared_ptr entries; std::tr1::shared_ptr arch_list_files; std::tr1::shared_ptr repository_mask_files; std::tr1::shared_ptr profiles_desc_files; std::tr1::shared_ptr mirror_files; std::tr1::shared_ptr info_packages_files; std::tr1::shared_ptr info_variables_files; std::tr1::shared_ptr use_desc_files; Implementation(const ERepository * const r, const FSEntry & t, std::tr1::shared_ptr e) : repository(r), tree_root(t), has_category_names(false), entries(e), arch_list_files(new FSEntrySequence), repository_mask_files(new FSEntrySequence), profiles_desc_files(new FSEntrySequence), mirror_files(new FSEntrySequence), info_packages_files(new FSEntrySequence), info_variables_files(new FSEntrySequence), use_desc_files(new UseDescFileInfoSequence) { } }; } TraditionalLayout::TraditionalLayout(const ERepository * const repo, const FSEntry & tree_root, const std::tr1::shared_ptr & e, const std::tr1::shared_ptr & f) : Layout(f), PrivateImplementationPattern(new Implementation(repo, tree_root, e)) { if (master_repositories_locations()) { for (FSEntrySequence::ConstIterator l(master_repositories_locations()->begin()), l_end(master_repositories_locations()->end()) ; l != l_end ; ++l) { _imp->arch_list_files->push_back(*l / "profiles" / "arch.list"); _imp->repository_mask_files->push_back(*l / "profiles" / "package.mask"); _imp->profiles_desc_files->push_back(*l / "profiles" / "profiles.desc"); _imp->mirror_files->push_back(*l / "profiles" / "thirdpartymirrors"); _imp->info_variables_files->push_back(*l / "profiles" / "info_vars"); _imp->use_desc_files->push_back(std::make_pair(*l / "profiles" / "use.desc", "")); _imp->use_desc_files->push_back(std::make_pair(*l / "profiles" / "use.local.desc", "")); FSEntry descs(*l / "profiles" / "desc"); if (descs.is_directory_or_symlink_to_directory()) { for (DirIterator d(descs), d_end ; d != d_end ; ++d) { if (! is_file_with_extension(*d, ".desc", IsFileWithOptions())) continue; _imp->use_desc_files->push_back(std::make_pair(*d, strip_trailing_string(d->basename(), ".desc"))); } } } } _imp->arch_list_files->push_back(_imp->tree_root / "profiles" / "arch.list"); _imp->repository_mask_files->push_back(_imp->tree_root / "profiles" / "package.mask"); _imp->profiles_desc_files->push_back(_imp->tree_root / "profiles" / "profiles.desc"); _imp->mirror_files->push_back(_imp->tree_root / "profiles" / "thirdpartymirrors"); _imp->info_variables_files->push_back(_imp->tree_root / "profiles" / "info_vars"); _imp->info_packages_files->push_back(_imp->tree_root / "profiles" / "info_pkgs"); _imp->use_desc_files->push_back(std::make_pair(_imp->tree_root / "profiles" / "use.desc", "")); _imp->use_desc_files->push_back(std::make_pair(_imp->tree_root / "profiles" / "use.local.desc", "")); FSEntry descs(_imp->tree_root / "profiles" / "desc"); if (descs.is_directory_or_symlink_to_directory()) { for (DirIterator d(descs), d_end ; d != d_end ; ++d) { if (! is_file_with_extension(*d, ".desc", IsFileWithOptions())) continue; _imp->use_desc_files->push_back(std::make_pair(*d, strip_trailing_string(d->basename(), ".desc"))); } } } TraditionalLayout::~TraditionalLayout() { } FSEntry TraditionalLayout::categories_file() const { return _imp->tree_root / "profiles" / "categories"; } void TraditionalLayout::need_category_names() const { Lock l(_imp->big_nasty_mutex); if (_imp->has_category_names) return; Context context("When loading category names for " + stringify(_imp->repository->name()) + ":"); Log::get_instance()->message("e.traditional_layout.need_category_names", ll_debug, lc_context) << "need_category_names"; bool found_one(false); std::list cats_list; if (_imp->repository->params().master_repositories) for (ERepositorySequence::ConstIterator e(_imp->repository->params().master_repositories->begin()), e_end(_imp->repository->params().master_repositories->end()) ; e != e_end ; ++e) cats_list.push_back((*e)->layout()->categories_file()); cats_list.push_back(categories_file()); for (std::list::const_iterator i(cats_list.begin()), i_end(cats_list.end()) ; i != i_end ; ++i) { if (! i->exists()) continue; LineConfigFile cats(*i, LineConfigFileOptions() + lcfo_disallow_continuations); for (LineConfigFile::ConstIterator line(cats.begin()), line_end(cats.end()) ; line != line_end ; ++line) { try { _imp->category_names.insert(std::make_pair(CategoryNamePart(*line), false)); } catch (const NameError & e) { Log::get_instance()->message("e.traditional_layout.categories.failure", ll_warning, lc_context) << "Skipping line '" << *line << "' in '" << *i << "' due to exception '" << e.message() << "' ('" << e.what() << ")"; } } found_one = true; } if (! found_one) { Log::get_instance()->message("e.traditional_layout.categories.no_file", ll_qa, lc_context) << "No categories file for repository at '" << _imp->tree_root << "', faking it"; for (DirIterator d(_imp->tree_root, DirIteratorOptions() + dio_inode_sort), d_end ; d != d_end ; ++d) { if (! d->is_directory_or_symlink_to_directory()) continue; std::string n(d->basename()); if (n == "CVS" || n == "distfiles" || n == "scripts" || n == "eclass" || n == "licenses" || n == "packages") continue; try { _imp->category_names.insert(std::make_pair(CategoryNamePart(n), false)); } catch (const NameError &) { } } } _imp->has_category_names = true; } void TraditionalLayout::need_package_ids(const QualifiedPackageName & n) const { Lock l(_imp->big_nasty_mutex); using namespace std::tr1::placeholders; if (_imp->package_names[n]) return; Context context("When loading versions for '" + stringify(n) + "' in " + stringify(_imp->repository->name()) + ":"); std::tr1::shared_ptr v(new PackageIDSequence); FSEntry path(_imp->tree_root / stringify(n.category) / stringify(n.package)); for (DirIterator e(path, DirIteratorOptions() + dio_inode_sort), e_end ; e != e_end ; ++e) { if (! _imp->entries->is_package_file(n, *e)) continue; try { std::tr1::shared_ptr id(_imp->entries->make_id(n, *e)); if (indirect_iterator(v->end()) != std::find_if(indirect_iterator(v->begin()), indirect_iterator(v->end()), std::tr1::bind(std::equal_to(), id->version(), std::tr1::bind(std::tr1::mem_fn(&PackageID::version), _1)))) Log::get_instance()->message("e.traditional_layout.id.duplicate", ll_warning, lc_context) << "Ignoring entry '" << *e << "' for '" << n << "' in repository '" << _imp->repository->name() << "' because another equivalent version already exists"; else v->push_back(id); } catch (const InternalError &) { throw; } catch (const Exception & ee) { Log::get_instance()->message("e.traditional_layout.id.failure", ll_warning, lc_context) << "Skipping entry '" << *e << "' for '" << n << "' in repository '" << _imp->repository->name() << "' due to exception '" << ee.message() << "' (" << ee.what() << ")'"; } } _imp->ids.insert(std::make_pair(n, v)); _imp->package_names[n] = true; } bool TraditionalLayout::has_category_named(const CategoryNamePart & c) const { Lock l(_imp->big_nasty_mutex); Context context("When checking for category '" + stringify(c) + "' in '" + stringify(_imp->repository->name()) + "':"); need_category_names(); return _imp->category_names.end() != _imp->category_names.find(c); } bool TraditionalLayout::has_package_named(const QualifiedPackageName & q) const { Lock l(_imp->big_nasty_mutex); Context context("When checking for package '" + stringify(q) + "' in '" + stringify(_imp->repository->name()) + ":"); need_category_names(); CategoryMap::iterator cat_iter(_imp->category_names.find(q.category)); if (_imp->category_names.end() == cat_iter) return false; if (cat_iter->second) { /* this category's package names are fully loaded */ return _imp->package_names.find(q) != _imp->package_names.end(); } else { /* package names are only partially loaded or not loaded */ if (_imp->package_names.find(q) != _imp->package_names.end()) return true; FSEntry fs(_imp->tree_root); fs /= stringify(q.category); fs /= stringify(q.package); if (! fs.is_directory_or_symlink_to_directory()) return false; _imp->package_names.insert(std::make_pair(q, false)); return true; } } void TraditionalLayout::need_category_names_collection() const { Lock l(_imp->big_nasty_mutex); if (_imp->category_names_collection) return; need_category_names(); _imp->category_names_collection.reset(new CategoryNamePartSet); std::transform(_imp->category_names.begin(), _imp->category_names.end(), _imp->category_names_collection->inserter(), std::tr1::mem_fn(&std::pair::first)); } std::tr1::shared_ptr TraditionalLayout::category_names() const { Lock l(_imp->big_nasty_mutex); Context context("When fetching category names in " + stringify(stringify(_imp->repository->name())) + ":"); need_category_names_collection(); return _imp->category_names_collection; } std::tr1::shared_ptr TraditionalLayout::package_names(const CategoryNamePart & c) const { Lock l(_imp->big_nasty_mutex); using namespace std::tr1::placeholders; /* this isn't particularly fast because it isn't called very often. avoid * changing the data structures used to make this faster at the expense of * slowing down single item queries. */ Context context("When fetching package names in category '" + stringify(c) + "' in " + stringify(_imp->repository->name()) + ":"); need_category_names(); if (_imp->category_names.end() == _imp->category_names.find(c)) return std::tr1::shared_ptr(new QualifiedPackageNameSet); if ((_imp->tree_root / stringify(c)).is_directory_or_symlink_to_directory()) for (DirIterator d(_imp->tree_root / stringify(c), DirIteratorOptions() + dio_inode_sort), d_end ; d != d_end ; ++d) { try { if (! d->is_directory_or_symlink_to_directory()) continue; if (DirIterator() == std::find_if(DirIterator(*d, DirIteratorOptions() + dio_inode_sort), DirIterator(), std::tr1::bind(&ERepositoryEntries::is_package_file, _imp->entries.get(), c + PackageNamePart(d->basename()), _1))) continue; _imp->package_names.insert(std::make_pair(c + PackageNamePart(d->basename()), false)); } catch (const NameError & e) { Log::get_instance()->message("e.traditional_layout.packages.failure", ll_warning, lc_context) << "Skipping entry '" << d->basename() << "' in category '" << c << "' in repository '" << stringify(_imp->repository->name()) << "' (" << e.message() << ")"; } } _imp->category_names[c] = true; std::tr1::shared_ptr result(new QualifiedPackageNameSet); for (PackagesMap::const_iterator p(_imp->package_names.begin()), p_end(_imp->package_names.end()) ; p != p_end ; ++p) if (p->first.category == c) result->insert(p->first); return result; } std::tr1::shared_ptr TraditionalLayout::package_ids(const QualifiedPackageName & n) const { Lock l(_imp->big_nasty_mutex); Context context("When fetching versions of '" + stringify(n) + "' in " + stringify(_imp->repository->name()) + ":"); if (has_package_named(n)) { need_package_ids(n); return _imp->ids.find(n)->second; } else return std::tr1::shared_ptr(new PackageIDSequence); } const std::tr1::shared_ptr TraditionalLayout::info_packages_files() const { return _imp->info_packages_files; } const std::tr1::shared_ptr TraditionalLayout::info_variables_files() const { return _imp->info_variables_files; } FSEntry TraditionalLayout::package_directory(const QualifiedPackageName & qpn) const { return _imp->tree_root / stringify(qpn.category) / stringify(qpn.package); } FSEntry TraditionalLayout::category_directory(const CategoryNamePart & cat) const { return _imp->tree_root / stringify(cat); } std::tr1::shared_ptr TraditionalLayout::arch_list_files() const { return _imp->arch_list_files; } std::tr1::shared_ptr TraditionalLayout::repository_mask_files() const { return _imp->repository_mask_files; } std::tr1::shared_ptr TraditionalLayout::profiles_desc_files() const { return _imp->profiles_desc_files; } std::tr1::shared_ptr TraditionalLayout::mirror_files() const { return _imp->mirror_files; } std::tr1::shared_ptr TraditionalLayout::use_desc_files() const { return _imp->use_desc_files; } FSEntry TraditionalLayout::profiles_base_dir() const { if (master_repositories_locations() && ! master_repositories_locations()->empty()) return *master_repositories_locations()->begin() / "profiles"; else return _imp->tree_root / "profiles"; } std::tr1::shared_ptr TraditionalLayout::exlibsdirs(const QualifiedPackageName & q) const { std::tr1::shared_ptr result(new FSEntrySequence); std::tr1::shared_ptr global(exlibsdirs_global()); std::copy(global->begin(), global->end(), result->back_inserter()); std::tr1::shared_ptr category(exlibsdirs_category(q.category)); std::copy(category->begin(), category->end(), result->back_inserter()); std::tr1::shared_ptr package(exlibsdirs_package(q)); std::copy(package->begin(), package->end(), result->back_inserter()); return result; } std::tr1::shared_ptr TraditionalLayout::exlibsdirs_global() const { std::tr1::shared_ptr result(new FSEntrySequence); if (_imp->repository->params().master_repositories) { for (ERepositorySequence::ConstIterator e(_imp->repository->params().master_repositories->begin()), e_end(_imp->repository->params().master_repositories->end()) ; e != e_end ; ++e) { std::tr1::shared_ptr master((*e)->layout()->exlibsdirs_global()); std::copy(master->begin(), master->end(), result->back_inserter()); } } result->push_back(_imp->tree_root / "exlibs"); return result; } std::tr1::shared_ptr TraditionalLayout::exlibsdirs_category(const CategoryNamePart & c) const { std::tr1::shared_ptr result(new FSEntrySequence); if (_imp->repository->params().master_repositories) { for (ERepositorySequence::ConstIterator e(_imp->repository->params().master_repositories->begin()), e_end(_imp->repository->params().master_repositories->end()) ; e != e_end ; ++e) { std::tr1::shared_ptr master((*e)->layout()->exlibsdirs_category(c)); std::copy(master->begin(), master->end(), result->back_inserter()); } } result->push_back(category_directory(c) / "exlibs"); return result; } std::tr1::shared_ptr TraditionalLayout::exlibsdirs_package(const QualifiedPackageName & q) const { std::tr1::shared_ptr result(new FSEntrySequence); if (_imp->repository->params().master_repositories) { for (ERepositorySequence::ConstIterator e(_imp->repository->params().master_repositories->begin()), e_end(_imp->repository->params().master_repositories->end()) ; e != e_end ; ++e) { std::tr1::shared_ptr master((*e)->layout()->exlibsdirs_package(q)); std::copy(master->begin(), master->end(), result->back_inserter()); } } result->push_back(package_directory(q)); return result; } std::tr1::shared_ptr TraditionalLayout::licenses_dirs() const { std::tr1::shared_ptr result(new FSEntrySequence); if (_imp->repository->params().master_repositories) { for (ERepositorySequence::ConstIterator e(_imp->repository->params().master_repositories->begin()), e_end(_imp->repository->params().master_repositories->end()) ; e != e_end ; ++e) { std::tr1::shared_ptr master((*e)->layout()->licenses_dirs()); std::copy(master->begin(), master->end(), result->back_inserter()); } } result->push_back(_imp->tree_root / "licenses"); return result; } namespace { void aux_files_helper(const FSEntry & d, std::tr1::shared_ptr > & m, const QualifiedPackageName & qpn) { if (! d.exists()) return; std::list files((DirIterator(d, DirIteratorOptions() + dio_inode_sort)), DirIterator()); for (std::list::iterator f(files.begin()) ; f != files.end() ; ++f) { if (f->is_directory()) { if ("CVS" != f->basename()) aux_files_helper((*f), m, qpn); } else { if (! f->is_regular_file()) continue; if (is_file_with_prefix_extension((*f), ("digest-"+stringify(qpn.package)), "", IsFileWithOptions())) continue; m->insert((*f), "AUX"); } } } } std::tr1::shared_ptr > TraditionalLayout::manifest_files(const QualifiedPackageName & qpn) const { std::tr1::shared_ptr > result(new Map); FSEntry package_dir = _imp->repository->layout()->package_directory(qpn); std::list package_files((DirIterator(package_dir, DirIteratorOptions() + dio_inode_sort)), DirIterator()); for (std::list::iterator f(package_files.begin()) ; f != package_files.end() ; ++f) { if (! (*f).is_regular_file() || ((*f).basename() == "Manifest") ) continue; std::string file_type("MISC"); if (_imp->entries->is_package_file(qpn, (*f))) file_type=_imp->entries->get_package_file_manifest_key((*f), qpn); result->insert((*f), file_type); } aux_files_helper((package_dir / "files"), result, qpn); return result; } FSEntry TraditionalLayout::sync_filter_file() const { return FSEntry(DATADIR "/paludis/traditional.exclude"); } void TraditionalLayout::invalidate_masks() { Lock l(_imp->big_nasty_mutex); for (IDMap::iterator it(_imp->ids.begin()), it_end(_imp->ids.end()); it_end != it; ++it) for (PackageIDSequence::ConstIterator it2(it->second->begin()), it2_end(it->second->end()); it2_end != it2; ++it2) (*it2)->invalidate_masks(); } FSEntry TraditionalLayout::binary_ebuild_location(const QualifiedPackageName & q, const VersionSpec & v, const std::string & eapi) const { return package_directory(q) / _imp->entries->binary_ebuild_name(q, v, eapi); }