Exheredludis/paludis/repositories/e/e_repository.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

1925 lines
79 KiB
C++

/* vim: set sw=4 sts=4 et foldmethod=syntax : */
/*
* Copyright (c) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2013 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 <paludis/repositories/e/aa_visitor.hh>
#include <paludis/repositories/e/e_key.hh>
#include <paludis/repositories/e/e_repository.hh>
#include <paludis/repositories/e/profile.hh>
#include <paludis/repositories/e/traditional_profile.hh>
#include <paludis/repositories/e/e_repository_news.hh>
#include <paludis/repositories/e/e_repository_sets.hh>
#include <paludis/repositories/e/e_repository_exceptions.hh>
#include <paludis/repositories/e/eapi.hh>
#include <paludis/repositories/e/eclass_mtimes.hh>
#include <paludis/repositories/e/use_desc.hh>
#include <paludis/repositories/e/layout.hh>
#include <paludis/repositories/e/info_metadata_key.hh>
#include <paludis/repositories/e/extra_distribution_data.hh>
#include <paludis/repositories/e/memoised_hashes.hh>
#include <paludis/repositories/e/ebuild_id.hh>
#include <paludis/repositories/e/eapi_phase.hh>
#include <paludis/repositories/e/can_skip_phase.hh>
#include <paludis/repositories/e/ebuild.hh>
#include <paludis/repositories/e/pbin_merger.hh>
#include <paludis/repositories/e/check_userpriv.hh>
#include <paludis/repositories/e/make_use.hh>
#include <paludis/repositories/e/a_finder.hh>
#include <paludis/repositories/e/file_suffixes.hh>
#include <paludis/repositories/e/licence_groups.hh>
#include <paludis/about.hh>
#include <paludis/action.hh>
#include <paludis/choice.hh>
#include <paludis/dep_spec.hh>
#include <paludis/dep_spec_flattener.hh>
#include <paludis/distribution.hh>
#include <paludis/elike_choices.hh>
#include <paludis/elike_package_dep_spec.hh>
#include <paludis/environment.hh>
#include <paludis/hook.hh>
#include <paludis/literal_metadata_key.hh>
#include <paludis/mask.hh>
#include <paludis/match_package.hh>
#include <paludis/output_manager.hh>
#include <paludis/repository_name_cache.hh>
#include <paludis/syncer.hh>
#include <paludis/util/active_object_ptr.hh>
#include <paludis/util/cookie.hh>
#include <paludis/util/config_file.hh>
#include <paludis/util/create_iterator-impl.hh>
#include <paludis/util/deferred_construction_ptr.hh>
#include <paludis/util/destringify.hh>
#include <paludis/util/digest_registry.hh>
#include <paludis/util/extract_host_from_url.hh>
#include <paludis/util/fs_stat.hh>
#include <paludis/util/fs_iterator.hh>
#include <paludis/util/hashes.hh>
#include <paludis/util/indirect_iterator-impl.hh>
#include <paludis/util/is_file_with_extension.hh>
#include <paludis/util/iterator_funcs.hh>
#include <paludis/util/join.hh>
#include <paludis/util/log.hh>
#include <paludis/util/make_named_values.hh>
#include <paludis/util/make_shared_copy.hh>
#include <paludis/util/map.hh>
#include <paludis/util/options.hh>
#include <paludis/util/pimp-impl.hh>
#include <paludis/util/process.hh>
#include <paludis/util/return_literal_function.hh>
#include <paludis/util/safe_ifstream.hh>
#include <paludis/util/safe_ofstream.hh>
#include <paludis/util/sequence.hh>
#include <paludis/util/set.hh>
#include <paludis/util/stringify.hh>
#include <paludis/util/strip.hh>
#include <paludis/util/system.hh>
#include <paludis/util/timestamp.hh>
#include <paludis/util/tokeniser.hh>
#include <paludis/util/upper_lower.hh>
#include <paludis/util/wrapped_forward_iterator.hh>
#include <paludis/util/wrapped_output_iterator.hh>
#include <random>
#include <functional>
#include <unordered_map>
#include <map>
#include <set>
#include <algorithm>
#include <vector>
#include <list>
#include <ctime>
#include <strings.h>
#include <ctype.h>
#include <dlfcn.h>
#include <stdint.h>
#include <fcntl.h>
#include "config.h"
#define STUPID_CAST(type, val) reinterpret_cast<type>(reinterpret_cast<uintptr_t>(val))
/** \file
* Imp of ERepository.
*
* \ingroup grperepository
*/
using namespace paludis;
using namespace paludis::erepository;
typedef std::unordered_map<std::string, std::shared_ptr<MirrorsSequence> > MirrorMap;
typedef std::map<FSPath, std::string, FSPathComparator> EAPIForFileMap;
namespace
{
const std::string pbin_tar_extension = ".tar";
std::shared_ptr<FSPathSequence> get_master_locations(
const std::shared_ptr<const ERepositorySequence> & r)
{
std::shared_ptr<FSPathSequence> result;
if (r)
{
result = std::make_shared<FSPathSequence>();
for (ERepositorySequence::ConstIterator e(r->begin()), e_end(r->end()) ;
e != e_end ; ++e)
result->push_back((*e)->location_key()->parse_value());
}
return result;
}
std::shared_ptr<Sequence<std::string> > get_master_names(
const std::shared_ptr<const ERepositorySequence> & r)
{
std::shared_ptr<Sequence<std::string> > result;
if (r)
{
result = std::make_shared<Sequence<std::string>>();
for (ERepositorySequence::ConstIterator e(r->begin()), e_end(r->end()) ;
e != e_end ; ++e)
result->push_back(stringify((*e)->name()));
}
return result;
}
std::shared_ptr<Set<std::string> > make_binary_keywords_filter(
const std::string & s)
{
auto result(std::make_shared<Set<std::string>>());
tokenise_whitespace(s, result->inserter());
return result;
}
bool is_arch_flag_func(const ERepository * const r, const UnprefixedChoiceName & p)
{
return r->arch_flags()->end() != r->arch_flags()->find(p);
}
std::shared_ptr<LicenceGroups>
make_licence_groups(const std::shared_ptr<const MetadataValueKey<FSPath> > & p)
{
auto result(std::make_shared<LicenceGroups>());
if (p)
result->add(p->parse_value());
return result;
}
}
namespace paludis
{
template <>
struct Imp<ERepository>
{
struct Mutexes
{
std::mutex arch_flags_mutex;
std::mutex mirrors_mutex;
std::mutex use_desc_mutex;
std::mutex profile_ptr_mutex;
std::mutex news_ptr_mutex;
std::mutex eapi_for_file_mutex;
};
ERepository * const repo;
const ERepositoryParams params;
const std::shared_ptr<Mutexes> mutexes;
std::shared_ptr<RepositoryNameCache> names_cache;
const std::map<QualifiedPackageName, QualifiedPackageName> provide_map;
mutable std::shared_ptr<Set<UnprefixedChoiceName> > arch_flags;
mutable std::shared_ptr<const UseDesc> use_desc;
mutable bool has_mirrors;
mutable MirrorMap mirrors;
mutable std::shared_ptr<erepository::Profile> profile_ptr;
mutable std::shared_ptr<const FSPath> main_profile_path;
mutable std::shared_ptr<ERepositoryNews> news_ptr;
mutable std::shared_ptr<ERepositorySets> sets_ptr;
const std::shared_ptr<Layout> layout;
mutable EAPIForFileMap eapi_for_file_map;
Imp(ERepository * const, const ERepositoryParams &, std::shared_ptr<Mutexes> = std::make_shared<Mutexes>());
~Imp();
void need_profiles() const;
std::shared_ptr<const MetadataValueKey<std::string> > format_key;
std::shared_ptr<const MetadataValueKey<std::string> > layout_key;
std::shared_ptr<const MetadataValueKey<std::string> > profile_layout_key;
std::shared_ptr<const MetadataValueKey<FSPath> > location_key;
std::shared_ptr<const MetadataCollectionKey<FSPathSequence> > profiles_key;
std::shared_ptr<const MetadataValueKey<FSPath> > cache_key;
std::shared_ptr<const MetadataValueKey<FSPath> > write_cache_key;
std::shared_ptr<const MetadataValueKey<bool> > append_repository_name_to_write_cache_key;
std::shared_ptr<const MetadataValueKey<bool> > ignore_deprecated_profiles;
std::shared_ptr<const MetadataValueKey<FSPath> > names_cache_key;
std::shared_ptr<const MetadataValueKey<FSPath> > distdir_key;
std::shared_ptr<const MetadataCollectionKey<FSPathSequence> > eclassdirs_key;
std::shared_ptr<const MetadataValueKey<FSPath> > securitydir_key;
std::shared_ptr<const MetadataValueKey<FSPath> > setsdir_key;
std::shared_ptr<const MetadataValueKey<FSPath> > newsdir_key;
std::shared_ptr<const MetadataCollectionKey<Map<std::string, std::string> > > sync_key;
std::shared_ptr<const MetadataCollectionKey<Map<std::string, std::string> > > sync_options_key;
std::shared_ptr<const MetadataValueKey<FSPath> > builddir_key;
std::shared_ptr<const MetadataCollectionKey<Sequence<std::string> > > master_repositories_key;
std::shared_ptr<const MetadataValueKey<std::string> > eapi_when_unknown_key;
std::shared_ptr<const MetadataValueKey<std::string> > eapi_when_unspecified_key;
std::shared_ptr<const MetadataValueKey<std::string> > profile_eapi_when_unspecified_key;
std::shared_ptr<const MetadataValueKey<std::string> > use_manifest_key;
std::shared_ptr<const MetadataCollectionKey<Set<std::string> > > manifest_hashes_key;
std::shared_ptr<const MetadataValueKey<bool> > thin_manifests_key;
std::shared_ptr<const MetadataSectionKey> info_pkgs_key;
std::shared_ptr<const MetadataCollectionKey<Set<std::string> > > info_vars_key;
std::shared_ptr<const MetadataValueKey<bool> > binary_destination_key;
std::shared_ptr<const MetadataValueKey<std::string> > binary_src_uri_prefix_key;
std::shared_ptr<const MetadataValueKey<std::string> > binary_distdir_key;
std::shared_ptr<const MetadataCollectionKey<Set<std::string> > > binary_keywords_filter;
std::shared_ptr<const MetadataValueKey<FSPath> > accounts_repository_data_location_key;
std::shared_ptr<const MetadataValueKey<FSPath> > e_updates_location_key;
std::shared_ptr<const MetadataValueKey<FSPath> > licence_groups_location_key;
std::shared_ptr<Map<std::string, std::string> > sync_hosts;
std::shared_ptr<const MetadataCollectionKey<Map<std::string, std::string> > > sync_host_key;
std::list<std::shared_ptr<const MetadataKey> > about_keys;
std::shared_ptr<EclassMtimes> eclass_mtimes;
time_t master_mtime;
const ActiveObjectPtr<DeferredConstructionPtr<std::shared_ptr<LicenceGroups> > > licence_groups;
};
Imp<ERepository>::Imp(ERepository * const r,
const ERepositoryParams & p, std::shared_ptr<Mutexes> m) :
repo(r),
params(p),
mutexes(m),
names_cache(std::make_shared<RepositoryNameCache>(p.names_cache(), r)),
has_mirrors(false),
sets_ptr(std::make_shared<ERepositorySets>(params.environment(), r, p)),
layout(LayoutFactory::get_instance()->create(params.layout(), params.environment(), r, params.location(), get_master_locations(
params.master_repositories()))),
format_key(std::make_shared<LiteralMetadataValueKey<std::string> >("format", "format",
mkt_significant, params.entry_format())),
layout_key(std::make_shared<LiteralMetadataValueKey<std::string> >("layout", "layout",
mkt_normal, params.layout())),
profile_layout_key(std::make_shared<LiteralMetadataValueKey<std::string> >("profile_layout", "profile_layout",
mkt_normal, params.profile_layout())),
location_key(std::make_shared<LiteralMetadataValueKey<FSPath> >("location", "location",
mkt_significant, params.location())),
profiles_key(std::make_shared<LiteralMetadataFSPathSequenceKey>(
"profiles", "profiles", mkt_normal, params.profiles())),
cache_key(std::make_shared<LiteralMetadataValueKey<FSPath> >("cache", "cache",
mkt_normal, params.cache())),
write_cache_key(std::make_shared<LiteralMetadataValueKey<FSPath> >("write_cache", "write_cache",
mkt_normal, params.write_cache())),
append_repository_name_to_write_cache_key(std::make_shared<LiteralMetadataValueKey<bool> >(
"append_repository_name_to_write_cache", "append_repository_name_to_write_cache",
mkt_internal, params.append_repository_name_to_write_cache())),
ignore_deprecated_profiles(std::make_shared<LiteralMetadataValueKey<bool> >(
"ignore_deprecated_profiles", "ignore_deprecated_profiles",
mkt_internal, params.ignore_deprecated_profiles())),
names_cache_key(std::make_shared<LiteralMetadataValueKey<FSPath> >(
"names_cache", "names_cache", mkt_normal, params.names_cache())),
distdir_key(std::make_shared<LiteralMetadataValueKey<FSPath> >(
"distdir", "distdir", mkt_normal, params.distdir())),
eclassdirs_key(std::make_shared<LiteralMetadataFSPathSequenceKey>(
"eclassdirs", "eclassdirs", mkt_normal, params.eclassdirs())),
securitydir_key(std::make_shared<LiteralMetadataValueKey<FSPath> >(
"securitydir", "securitydir", mkt_normal, params.securitydir())),
setsdir_key(std::make_shared<LiteralMetadataValueKey<FSPath> >(
"setsdir", "setsdir", mkt_normal, params.setsdir())),
newsdir_key(std::make_shared<LiteralMetadataValueKey<FSPath> >(
"newsdir", "newsdir", mkt_normal, params.newsdir())),
sync_key(std::make_shared<LiteralMetadataStringStringMapKey>(
"sync", "sync", mkt_normal, params.sync())),
sync_options_key(std::make_shared<LiteralMetadataStringStringMapKey>(
"sync_options", "sync_options", mkt_normal, params.sync_options())),
builddir_key(std::make_shared<LiteralMetadataValueKey<FSPath> >(
"builddir", "builddir", mkt_normal, params.builddir())),
master_repositories_key(params.master_repositories() ?
std::shared_ptr<MetadataCollectionKey<Sequence<std::string> > >(std::make_shared<LiteralMetadataStringSequenceKey>(
"master_repository", "master_repository", mkt_normal, get_master_names(params.master_repositories()))) :
std::shared_ptr<MetadataCollectionKey<Sequence<std::string> > >()),
eapi_when_unknown_key(std::make_shared<LiteralMetadataValueKey<std::string> >(
"eapi_when_unknown", "eapi_when_unknown", mkt_normal, params.eapi_when_unknown())),
eapi_when_unspecified_key(std::make_shared<LiteralMetadataValueKey<std::string> >(
"eapi_when_unspecified", "eapi_when_unspecified", mkt_normal, params.eapi_when_unspecified())),
profile_eapi_when_unspecified_key(std::make_shared<LiteralMetadataValueKey<std::string> >(
"profile_eapi_when_unspecified", "profile_eapi_when_unspecified", mkt_normal, params.profile_eapi_when_unspecified())),
use_manifest_key(std::make_shared<LiteralMetadataValueKey<std::string> >(
"use_manifest", "use_manifest", mkt_normal, stringify(params.use_manifest()))),
manifest_hashes_key(std::make_shared<LiteralMetadataStringSetKey>(
"manifest_hashes", "manifest_hashes", mkt_normal, params.manifest_hashes())),
thin_manifests_key(std::make_shared<LiteralMetadataValueKey<bool> >(
"thin_manifests", "thin_manifests", mkt_normal, params.thin_manifests())),
info_pkgs_key(layout->info_packages_files()->end() != std::find_if(layout->info_packages_files()->begin(),
layout->info_packages_files()->end(),
std::bind(std::mem_fn(&FSStat::is_regular_file_or_symlink_to_regular_file),
std::bind(std::mem_fn(&FSPath::stat), std::placeholders::_1))) ?
std::make_shared<InfoPkgsMetadataKey>(params.environment(), layout->info_packages_files(), repo) :
std::shared_ptr<InfoPkgsMetadataKey>()
),
info_vars_key(layout->info_variables_files()->end() != std::find_if(layout->info_variables_files()->begin(),
layout->info_variables_files()->end(),
std::bind(std::mem_fn(&FSStat::is_regular_file_or_symlink_to_regular_file),
std::bind(std::mem_fn(&FSPath::stat), std::placeholders::_1))) ?
std::make_shared<InfoVarsMetadataKey>(layout->info_variables_files()) :
std::shared_ptr<InfoVarsMetadataKey>()
),
binary_destination_key(std::make_shared<LiteralMetadataValueKey<bool> >(
"binary_destination", "binary_destination", params.binary_destination() ? mkt_normal : mkt_internal,
params.binary_destination())),
binary_src_uri_prefix_key(std::make_shared<LiteralMetadataValueKey<std::string> >(
"binary_uri_prefix", "binary_uri_prefix", params.binary_destination() ? mkt_normal : mkt_internal,
params.binary_uri_prefix())),
binary_distdir_key(std::make_shared<LiteralMetadataValueKey<std::string> >(
"binary_distdir", "binary_distdir", params.binary_destination() ? mkt_normal : mkt_internal,
stringify(params.binary_distdir()))),
binary_keywords_filter(std::make_shared<LiteralMetadataStringSetKey>(
"binary_keywords_filter", "binary_keywords_filter", params.binary_destination() ? mkt_normal : mkt_internal,
make_binary_keywords_filter(params.binary_keywords_filter()))),
accounts_repository_data_location_key(layout->accounts_repository_data_location_key()),
e_updates_location_key(layout->e_updates_location_key()),
licence_groups_location_key(layout->licence_groups_location_key()),
sync_hosts(std::make_shared<Map<std::string, std::string> >()),
sync_host_key(std::make_shared<LiteralMetadataStringStringMapKey>("sync_host", "sync_host", mkt_internal, sync_hosts)),
eclass_mtimes(std::make_shared<EclassMtimes>(r, params.eclassdirs())),
master_mtime(0),
licence_groups(DeferredConstructionPtr<std::shared_ptr<LicenceGroups> > (
std::bind(&make_licence_groups, std::cref(licence_groups_location_key))))
{
if ((params.location() / "metadata" / "about.conf").stat().is_regular_file_or_symlink_to_regular_file())
{
Context context("When loading about.conf:");
KeyValueConfigFile k(params.location() / "metadata" / "about.conf", { },
&KeyValueConfigFile::no_defaults, &KeyValueConfigFile::no_transformation);
if (! k.get("description").empty())
about_keys.push_back(std::make_shared<LiteralMetadataValueKey<std::string>>("description", "description",
mkt_significant, k.get("description")));
if (! k.get("summary").empty())
about_keys.push_back(std::make_shared<LiteralMetadataValueKey<std::string>>("summary", "summary",
mkt_significant, k.get("summary")));
if (! k.get("status").empty())
about_keys.push_back(std::make_shared<LiteralMetadataValueKey<std::string>>("status", "status",
mkt_significant, k.get("status")));
if (! k.get("maintainer").empty())
about_keys.push_back(std::make_shared<LiteralMetadataValueKey<std::string>>("maintainer", "maintainer",
mkt_significant, k.get("maintainer")));
if (! k.get("homepage").empty())
about_keys.push_back(std::make_shared<LiteralMetadataValueKey<std::string>>("homepage", "homepage",
mkt_significant, k.get("homepage")));
}
FSPath mtf(params.location() / "metadata" / "timestamp");
FSStat mtfs(mtf.stat());
if (mtfs.exists())
master_mtime = mtfs.mtim().seconds();
for (auto i(params.sync()->begin()), i_end(params.sync()->end()) ;
i != i_end ; ++i)
sync_hosts->insert(i->first, extract_host_from_url(i->second));
}
Imp<ERepository>::~Imp() = default;
void
Imp<ERepository>::need_profiles() const
{
std::unique_lock<std::mutex> l(mutexes->profile_ptr_mutex);
if (profile_ptr)
return;
std::shared_ptr<const FSPathSequence> profiles(params.profiles());
if (params.auto_profiles())
{
FSPath profiles_desc("/dev/null");
for (FSPathSequence::ConstIterator f(layout->profiles_desc_files()->begin()),
f_end(layout->profiles_desc_files()->end()) ;
f != f_end ; ++f)
if (f->stat().is_regular_file_or_symlink_to_regular_file())
profiles_desc = *f;
std::shared_ptr<FSPathSequence> auto_profiles(std::make_shared<FSPathSequence>());
if (profiles_desc == FSPath("/dev/null"))
{
auto_profiles->push_back(FSPath("/var/empty"));
main_profile_path = std::make_shared<FSPath>("/var/empty");
}
else
{
Context context("When loading profiles.desc file '" + stringify(profiles_desc) + "':");
LineConfigFile f(profiles_desc, { });
for (LineConfigFile::ConstIterator line(f.begin()), line_end(f.end()) ;
line != line_end ; ++line)
{
std::vector<std::string> tokens;
tokenise_whitespace(*line, std::back_inserter(tokens));
if (tokens.size() < 3)
continue;
FSPath p(profiles_desc.dirname().realpath().dirname() / "profiles" / tokens.at(1));
auto_profiles->push_back(p);
main_profile_path = std::make_shared<FSPath>(p);
break;
}
}
profiles = auto_profiles;
}
else if (params.profiles()->empty())
main_profile_path = std::make_shared<FSPath>("/var/empty");
else
main_profile_path = std::make_shared<FSPath>(*params.profiles()->begin());
profile_ptr = ProfileFactory::get_instance()->create(
params.profile_layout(),
params.environment(),
repo->name(),
EAPIForFileFunction(std::bind(&ERepository::eapi_for_file, repo, std::placeholders::_1)),
IsArchFlagFunction(std::bind(&is_arch_flag_func, repo, std::placeholders::_1)),
*profiles,
EAPIData::get_instance()->eapi_from_string(params.eapi_when_unknown())->supported()->ebuild_environment_variables()->env_arch(),
params.profiles_explicitly_set(),
bool(params.master_repositories()),
params.ignore_deprecated_profiles());
}
}
namespace
{
RepositoryName
fetch_repo_name(const FSPath & tree_root)
{
bool illegal(false);
try
{
FSPath name_file(tree_root);
name_file /= "profiles";
name_file /= "repo_name";
if (name_file.stat().is_regular_file())
{
LineConfigFile f(name_file,
{
lcfo_disallow_comments,
lcfo_disallow_continuations,
lcfo_no_skip_blank_lines
});
if (f.begin() != f.end())
return RepositoryName(*f.begin());
}
}
catch (const RepositoryNameError &)
{
illegal = true;
}
catch (...)
{
}
std::string modified_location(tree_root.basename());
std::replace(modified_location.begin(), modified_location.end(), '/', '-');
if (illegal)
Log::get_instance()->message("e.repo_name.invalid", ll_qa, lc_no_context)
<< "repo_name file in '" << tree_root << "/profiles/', specifies an illegal repository name, falling back to generated name 'x-"
<< modified_location << "'.";
else
Log::get_instance()->message("e.repo_name.unusable", ll_qa, lc_no_context)
<< "Couldn't open repo_name file in '" << tree_root << "/profiles/', falling back to generated name 'x-"
<< modified_location << "' (ignore this message if you have yet to sync this repository).";
return RepositoryName("x-" + modified_location);
}
}
ERepository::ERepository(const ERepositoryParams & p) :
Repository(
p.environment(),
fetch_repo_name(p.location()),
make_named_values<RepositoryCapabilities>(
n::destination_interface() = p.binary_destination() ? this : nullptr,
n::environment_variable_interface() = this,
n::manifest_interface() = this
)),
_imp(this, p)
{
_add_metadata_keys();
}
ERepository::~ERepository() = default;
void
ERepository::_add_metadata_keys() const
{
clear_metadata_keys();
add_metadata_key(_imp->format_key);
add_metadata_key(_imp->layout_key);
add_metadata_key(_imp->profile_layout_key);
add_metadata_key(_imp->location_key);
add_metadata_key(_imp->profiles_key);
add_metadata_key(_imp->cache_key);
add_metadata_key(_imp->write_cache_key);
add_metadata_key(_imp->append_repository_name_to_write_cache_key);
add_metadata_key(_imp->ignore_deprecated_profiles);
add_metadata_key(_imp->names_cache_key);
add_metadata_key(_imp->distdir_key);
add_metadata_key(_imp->eclassdirs_key);
add_metadata_key(_imp->securitydir_key);
add_metadata_key(_imp->setsdir_key);
add_metadata_key(_imp->newsdir_key);
add_metadata_key(_imp->sync_key);
add_metadata_key(_imp->sync_options_key);
add_metadata_key(_imp->builddir_key);
add_metadata_key(_imp->eapi_when_unknown_key);
add_metadata_key(_imp->eapi_when_unspecified_key);
add_metadata_key(_imp->profile_eapi_when_unspecified_key);
if (_imp->master_repositories_key)
add_metadata_key(_imp->master_repositories_key);
add_metadata_key(_imp->use_manifest_key);
add_metadata_key(_imp->manifest_hashes_key);
add_metadata_key(_imp->thin_manifests_key);
if (_imp->info_pkgs_key)
add_metadata_key(_imp->info_pkgs_key);
if (_imp->info_vars_key)
add_metadata_key(_imp->info_vars_key);
add_metadata_key(_imp->binary_destination_key);
add_metadata_key(_imp->binary_src_uri_prefix_key);
add_metadata_key(_imp->binary_distdir_key);
add_metadata_key(_imp->binary_keywords_filter);
if (_imp->accounts_repository_data_location_key)
add_metadata_key(_imp->accounts_repository_data_location_key);
if (_imp->e_updates_location_key)
add_metadata_key(_imp->e_updates_location_key);
if (_imp->licence_groups_location_key)
add_metadata_key(_imp->licence_groups_location_key);
add_metadata_key(_imp->sync_host_key);
std::for_each(_imp->about_keys.begin(), _imp->about_keys.end(), std::bind(
std::mem_fn(&ERepository::add_metadata_key), this, std::placeholders::_1));
}
bool
ERepository::has_category_named(const CategoryNamePart & c, const RepositoryContentMayExcludes &) const
{
return _imp->layout->has_category_named(c);
}
bool
ERepository::has_package_named(const QualifiedPackageName & q, const RepositoryContentMayExcludes &) const
{
return _imp->layout->has_package_named(q);
}
std::shared_ptr<const CategoryNamePartSet>
ERepository::category_names(const RepositoryContentMayExcludes &) const
{
return _imp->layout->category_names();
}
std::shared_ptr<const QualifiedPackageNameSet>
ERepository::package_names(const CategoryNamePart & c, const RepositoryContentMayExcludes &) const
{
return _imp->layout->package_names(c);
}
std::shared_ptr<const PackageIDSequence>
ERepository::package_ids(const QualifiedPackageName & n, const RepositoryContentMayExcludes &) const
{
return _imp->layout->package_ids(n);
}
const std::shared_ptr<const Set<UnprefixedChoiceName> >
ERepository::arch_flags() const
{
std::unique_lock<std::mutex> l(_imp->mutexes->arch_flags_mutex);
if (! _imp->arch_flags)
{
Context context("When loading arch list:");
_imp->arch_flags = std::make_shared<Set<UnprefixedChoiceName>>();
bool found_one(false);
std::shared_ptr<const FSPathSequence> arch_list_files(_imp->layout->arch_list_files());
for (FSPathSequence::ConstIterator p(arch_list_files->begin()), p_end(arch_list_files->end()) ;
p != p_end ; ++p)
{
if (! p->stat().exists())
continue;
LineConfigFile archs(*p, { lcfo_disallow_continuations });
std::copy(archs.begin(), archs.end(), create_inserter<UnprefixedChoiceName>(_imp->arch_flags->inserter()));
found_one = true;
}
if (! found_one)
{
Log::get_instance()->message("e.arch_list.missing", ll_qa, lc_no_context)
<< "Couldn't find arch.list file for repository '"
<< stringify(name()) << "', arch flags may incorrectly show up as unmasked";
}
}
return _imp->arch_flags;
}
namespace
{
struct Randomator
{
std::mt19937 & rand;
int operator() (int n)
{
return rand() % n;
}
};
}
void
ERepository::need_mirrors() const
{
std::unique_lock<std::mutex> l(_imp->mutexes->mirrors_mutex);
if (! _imp->has_mirrors)
{
bool found_one(false);
std::shared_ptr<const FSPathSequence> mirror_files(_imp->layout->mirror_files());
for (FSPathSequence::ConstIterator p(mirror_files->begin()), p_end(mirror_files->end()) ;
p != p_end ; ++p)
{
if (p->stat().exists())
{
LineConfigFile mirrors(*p, { lcfo_disallow_continuations });
for (LineConfigFile::ConstIterator line(mirrors.begin()) ; line != mirrors.end() ; ++line)
{
std::vector<std::string> ee;
tokenise_whitespace(*line, std::back_inserter(ee));
if (! ee.empty())
{
/* pick up to five random mirrors only */
std::mt19937 random(std::time(nullptr));
Randomator randomator{random};
std::random_shuffle(next(ee.begin()), ee.end(), randomator);
if (ee.size() > 6)
ee.resize(6);
std::shared_ptr<MirrorsSequence> ms(std::make_shared<MirrorsSequence>());
for (std::vector<std::string>::const_iterator e(next(ee.begin())),
e_end(ee.end()) ; e != e_end ; ++e)
ms->push_back(*e);
_imp->mirrors.insert(std::make_pair(ee.at(0), ms)).first->second = ms;
}
}
}
found_one = true;
}
if (! found_one)
Log::get_instance()->message("e.thirdpartymirrors.missing", ll_warning, lc_no_context) <<
"No thirdpartymirrors file found in '"
<< (_imp->params.location() / "profiles") << "', so mirror:// SRC_URI "
"components cannot be fetched";
_imp->has_mirrors = true;
}
}
bool
ERepository::sync(
const std::string & source,
const std::string & revision,
const std::shared_ptr<OutputManager> & output_manager) const
{
Context context("When syncing repository '" + stringify(name()) + "':");
std::string sync_uri, sync_options;
if (_imp->params.sync()->end() != _imp->params.sync()->find(source))
sync_uri = _imp->params.sync()->find(source)->second;
if (sync_uri.empty())
return false;
if (_imp->params.sync_options()->end() != _imp->params.sync_options()->find(source))
sync_options = _imp->params.sync_options()->find(source)->second;
std::list<std::string> sync_list;
tokenise_whitespace(sync_uri, std::back_inserter(sync_list));
bool ok(false);
for (std::list<std::string>::const_iterator s(sync_list.begin()),
s_end(sync_list.end()) ; s != s_end ; ++s)
{
DefaultSyncer syncer(make_named_values<SyncerParams>(
n::environment() = _imp->params.environment(),
n::local() = stringify(_imp->params.location()),
n::remote() = *s,
n::revision() = revision
));
SyncOptions opts(make_named_values<SyncOptions>(
n::filter_file() = _imp->layout->sync_filter_file(),
n::options() = sync_options,
n::output_manager() = output_manager
));
try
{
syncer.sync(opts);
}
catch (const SyncFailedError &)
{
continue;
}
ok = true;
break;
}
if (! ok)
throw SyncFailedError(stringify(_imp->params.location()), sync_uri);
return true;
}
void
ERepository::invalidate()
{
_imp.reset(new Imp<ERepository>(this, _imp->params, _imp->mutexes));
_add_metadata_keys();
}
void
ERepository::purge_invalid_cache() const
{
Context context("When purging invalid write_cache:");
FSPath write_cache(_imp->params.write_cache());
if (write_cache == FSPath("/var/empty"))
return;
if (_imp->params.append_repository_name_to_write_cache())
write_cache /= stringify(name());
if (! write_cache.stat().is_directory_or_symlink_to_directory())
return;
const std::shared_ptr<const EAPI> eapi(EAPIData::get_instance()->eapi_from_string(
_imp->params.eapi_when_unknown()));
std::shared_ptr<EclassMtimes> eclass_mtimes(std::make_shared<EclassMtimes>(this, _imp->params.eclassdirs()));
for (FSIterator dc(write_cache, { fsio_inode_sort, fsio_want_directories, fsio_deref_symlinks_for_wants }), dc_end ; dc != dc_end ; ++dc)
{
for (FSIterator dp(*dc, { fsio_inode_sort, fsio_want_regular_files, fsio_deref_symlinks_for_wants }), dp_end ; dp != dp_end ; ++dp)
{
try
{
CategoryNamePart cnp(dc->basename());
std::string pv(dp->basename());
VersionSpec v(elike_get_remove_trailing_version(pv, eapi->supported()->version_spec_options()));
PackageNamePart p(pv);
std::shared_ptr<const PackageIDSequence> ids(_imp->layout->package_ids(cnp + p));
bool found(false);
for (PackageIDSequence::ConstIterator i(ids->begin()), i_end(ids->end()) ;
i != i_end ; ++i)
{
/* 00 is *not* equal to 0 here */
if (stringify((*i)->version()) != stringify(v))
continue;
std::static_pointer_cast<const ERepositoryID>(*i)->purge_invalid_cache();
found = true;
break;
}
if (! found)
FSPath(*dp).unlink();
}
catch (const Exception & e)
{
Log::get_instance()->message("e.ebuild.purge_write_cache.ignoring", ll_warning, lc_context)
<< "Ignoring exception '" << e.message() << "' (" << e.what() << ") when purging invalid write_cache entries";
}
}
}
}
void
ERepository::update_news() const
{
std::unique_lock<std::mutex> l(_imp->mutexes->news_ptr_mutex);
if (! _imp->news_ptr)
_imp->news_ptr = std::make_shared<ERepositoryNews>(_imp->params.environment(), this, _imp->params);
_imp->news_ptr->update_news();
}
const std::shared_ptr<const Layout>
ERepository::layout() const
{
return _imp->layout;
}
const std::shared_ptr<const Profile>
ERepository::profile() const
{
_imp->need_profiles();
return _imp->profile_ptr;
}
std::string
ERepository::profile_variable(const std::string & s) const
{
_imp->need_profiles();
return _imp->profile_ptr->environment_variable(s);
}
std::string
ERepository::environment_updated_profile_variable(const std::string & var) const
{
std::vector<std::string> values;
std::vector<std::string>::iterator last;
_imp->need_profiles();
tokenise_whitespace(_imp->profile_ptr->environment_variable(var), std::back_inserter(values));
tokenise_whitespace(paludis::getenv_with_default(var, ""), std::back_inserter(values));
std::sort(values.begin(), values.end());
last = std::unique(values.begin(), values.end());
return join(values.begin(), last, " ");
}
void
ERepository::regenerate_cache() const
{
_imp->names_cache->regenerate_cache();
}
std::shared_ptr<const CategoryNamePartSet>
ERepository::category_names_containing_package(const PackageNamePart & p,
const RepositoryContentMayExcludes & x) const
{
if (! _imp->names_cache->usable())
return Repository::category_names_containing_package(p, { });
std::shared_ptr<const CategoryNamePartSet> result(
_imp->names_cache->category_names_containing_package(p));
return result ? result : Repository::category_names_containing_package(p, x);
}
const ERepositoryParams &
ERepository::params() const
{
return _imp->params;
}
bool
ERepository::is_suitable_destination_for(const std::shared_ptr<const PackageID> & id) const
{
auto repo(_imp->params.environment()->fetch_repository(id->repository_name()));
std::string f(repo->format_key() ? repo->format_key()->parse_value() : "");
if (f == "e")
return static_cast<const ERepositoryID &>(*id).eapi()->supported()->can_be_pbin();
else
return false;
}
bool
ERepository::want_pre_post_phases() const
{
return false;
}
HookResult
ERepository::perform_hook(const Hook & hook, const std::shared_ptr<OutputManager> &)
{
Context context("When performing hook '" + stringify(hook.name()) + "' for repository '"
+ stringify(name()) + "':");
if (hook.name() == "sync_all_post"
|| hook.name() == "install_all_post"
|| hook.name() == "uninstall_all_post")
update_news();
return make_named_values<HookResult>(n::max_exit_status() = 0, n::output() = "");
}
std::shared_ptr<const CategoryNamePartSet>
ERepository::unimportant_category_names(const RepositoryContentMayExcludes &) const
{
std::shared_ptr<CategoryNamePartSet> result(std::make_shared<CategoryNamePartSet>());
result->insert(CategoryNamePart("virtual"));
return result;
}
const bool
ERepository::is_unimportant() const
{
return false;
}
bool
ERepository::some_ids_might_support_action(const SupportsActionTestBase & a) const
{
return a.make_accept_returning(
[&] (const SupportsActionTest<InstallAction> &) { return true; },
[&] (const SupportsActionTest<ConfigAction> &) { return false; },
[&] (const SupportsActionTest<PretendAction> &) { return true; },
[&] (const SupportsActionTest<FetchAction> &) { return true; },
[&] (const SupportsActionTest<PretendFetchAction> &) { return true; },
[&] (const SupportsActionTest<UninstallAction> &) { return false; },
[&] (const SupportsActionTest<InfoAction> &) { return true; }
);
}
bool
ERepository::some_ids_might_not_be_masked() const
{
return true;
}
void
ERepository::make_manifest(const QualifiedPackageName & qpn)
{
for (Set<std::string>::ConstIterator it(_imp->params.manifest_hashes()->begin()),
it_end(_imp->params.manifest_hashes()->end()); it_end != it; ++it)
if (! DigestRegistry::get_instance()->get(*it))
throw ERepositoryConfigurationError("Manifest hash function '" + *it + "' is not supported");
FSPath package_dir = _imp->layout->package_directory(qpn);
std::vector<std::pair<std::pair<std::string, std::string>, std::string> > lines;
auto files(_imp->layout->manifest_files(qpn, package_dir));
if (! _imp->params.thin_manifests())
{
for (auto f(files->begin()) ; f != files->end() ; ++f)
{
FSPath file(f->first);
FSStat file_stat(file);
std::string filename = file.basename();
std::string file_type(f->second);
if ("AUX" == file_type)
{
filename = stringify(file).substr(stringify(package_dir / "files").length()+1);
}
SafeIFStream file_stream(file);
std::string line(file_type + " " + filename + " " + stringify(file.stat().file_size()));
for (Set<std::string>::ConstIterator it(_imp->params.manifest_hashes()->begin()),
it_end(_imp->params.manifest_hashes()->end()); it_end != it; ++it)
{
file_stream.clear();
file_stream.seekg(0, std::ios::beg);
line += " " + *it + " " + DigestRegistry::get_instance()->get(*it)(file_stream);
}
lines.push_back(std::make_pair(std::make_pair(file_type, filename), line));
}
}
std::shared_ptr<const PackageIDSequence> versions;
versions = package_ids(qpn, { });
std::set<std::string> done_files;
for (PackageIDSequence::ConstIterator v(versions->begin()),
v_end(versions->end()) ;
v != v_end ; ++v)
{
std::shared_ptr<const PackageID> id = (*v);
if (! id->fetches_key())
continue;
AAVisitor aa;
id->fetches_key()->parse_value()->top()->accept(aa);
for (AAVisitor::ConstIterator d(aa.begin()) ;
d != aa.end() ; ++d)
{
if (done_files.count(*d))
continue;
done_files.insert(*d);
FSPath f(params().distdir() / *d);
FSStat f_stat(f);
if (! f_stat.is_regular_file_or_symlink_to_regular_file())
throw MissingDistfileError("Distfile '" + f.basename() + "' does not exist");
SafeIFStream file_stream(f);
MemoisedHashes * hashes = MemoisedHashes::get_instance();
std::string line("DIST " + f.basename() + " " + stringify(f_stat.file_size()));
for (Set<std::string>::ConstIterator it(_imp->params.manifest_hashes()->begin()),
it_end(_imp->params.manifest_hashes()->end()); it_end != it; ++it)
line += " " + *it + " " + hashes->get(*it, f, file_stream);
lines.push_back(std::make_pair(std::make_pair("DIST", f.basename()), line));
}
}
std::sort(lines.begin(), lines.end());
FSPath(package_dir / "Manifest").unlink();
if (! lines.empty())
{
SafeOFStream manifest(FSPath(package_dir / "Manifest"), -1, true);
if (! manifest)
throw ERepositoryConfigurationError("Couldn't open Manifest for writing.");
for (auto it(lines.begin()), it_end(lines.end()); it_end != it; ++it)
manifest << it->second << std::endl;
}
}
void
ERepository::need_keys_added() const
{
}
const std::shared_ptr<const MetadataValueKey<std::string> >
ERepository::format_key() const
{
return _imp->format_key;
}
const std::shared_ptr<const MetadataValueKey<FSPath> >
ERepository::location_key() const
{
return _imp->location_key;
}
const std::shared_ptr<const MetadataValueKey<FSPath> >
ERepository::installed_root_key() const
{
return std::shared_ptr<const MetadataValueKey<FSPath> >();
}
const std::shared_ptr<const MetadataCollectionKey<Set<std::string> > >
ERepository::info_vars_key() const
{
return _imp->info_vars_key;
}
RepositoryName
ERepository::repository_factory_name(
const Environment * const,
const std::function<std::string (const std::string &)> & key_function)
{
Context context("When finding repository name for e repository from repo_file '" + key_function("repo_file") + "':");
if (key_function("location").empty())
throw ERepositoryConfigurationError("Key 'location' unspecified or empty");
return fetch_repo_name(FSPath(key_function("location")));
}
std::shared_ptr<Repository>
ERepository::repository_factory_create(
Environment * const env,
const std::function<std::string (const std::string &)> & f)
{
Context context("When making ebuild repository from repo_file '" + f("repo_file") + "':");
RepositoryName our_name(repository_factory_name(env, f));
std::string location(f("location"));
if (location.empty())
throw ERepositoryConfigurationError("Key 'location' not specified or empty");
if ('/' != location.at(0))
throw ERepositoryConfigurationError("Key 'location' must start with a / (relative paths are not allowed)");
std::shared_ptr<KeyValueConfigFile> layout_conf((FSPath(location) / "metadata/layout.conf").stat().exists() ?
new KeyValueConfigFile(FSPath(location) / "metadata/layout.conf", { },
&KeyValueConfigFile::no_defaults, &KeyValueConfigFile::no_transformation)
: nullptr);
std::shared_ptr<ERepositorySequence> master_repositories;
std::string master_repository_str(f("master_repository"));
if (master_repository_str.empty() && ((! layout_conf) || (layout_conf->get("masters").empty())))
master_repository_str = f("master_repository_if_unknown");
if (! master_repository_str.empty())
{
if (layout_conf && ! layout_conf->get("masters").empty())
{
Log::get_instance()->message("e.ebuild.configuration.master_repository", ll_warning, lc_context) << "Key 'master_repository' in '"
<< f("repo_file") << "' will override '" << (FSPath(location) / "metadata/layout.conf")
<< "'. You should probably remove the 'master_repository' setting from your repository config file.";
}
Context context_local("When finding configuration information for master_repository '" + stringify(master_repository_str) + "':");
RepositoryName master_repository_name(master_repository_str);
std::shared_ptr<Repository> master_repository_uncasted(
env->fetch_repository(master_repository_name));
std::string format("unknown");
if (master_repository_uncasted->format_key())
format = master_repository_uncasted->format_key()->parse_value();
if (format != "e")
throw ERepositoryConfigurationError("Master repository format is '" + stringify(format) + "', not 'ebuild'");
std::shared_ptr<ERepository> master_repository(std::static_pointer_cast<ERepository>(master_repository_uncasted));
master_repositories = std::make_shared<ERepositorySequence>();
master_repositories->push_back(master_repository);
}
else if (layout_conf)
{
std::list<std::string> tokens;
tokenise_whitespace(layout_conf->get("masters"), std::back_inserter(tokens));
for (std::list<std::string>::const_iterator t(tokens.begin()), t_end(tokens.end()) ;
t != t_end ; ++t)
{
Context context_local("When finding configuration information for master '" + *t + "':");
RepositoryName master_repository_name(*t);
if (master_repository_name == our_name)
{
Log::get_instance()->message("e.ebuild.configuration.own_master_repository", ll_warning, lc_context)
<< "According to '" << stringify(FSPath(location) / "metadata/layout.conf")
<< "', the repository '" << our_name << "' has itself as a master, which is invalid";
continue;
}
try
{
std::shared_ptr<Repository> master_repository_uncasted(
env->fetch_repository(master_repository_name));
std::string format("unknown");
if (master_repository_uncasted->format_key())
format = master_repository_uncasted->format_key()->parse_value();
if (format != "e")
throw ERepositoryConfigurationError("Master repository format is '" + stringify(format) + "', not 'ebuild'");
std::shared_ptr<ERepository> master_repository(std::static_pointer_cast<ERepository>(master_repository_uncasted));
if (! master_repositories)
master_repositories = std::make_shared<ERepositorySequence>();
master_repositories->push_back(master_repository);
}
catch (const NoSuchRepositoryError &)
{
throw ERepositoryConfigurationError("According to '" + stringify(FSPath(location) / "metadata/layout.conf")
+ "', the repository specified by '" + f("repo_file") + "' requires repository '" + *t +
"', which you do not have available");
}
}
}
std::shared_ptr<FSPathSequence> profiles(std::make_shared<FSPathSequence>());
bool profiles_explicitly_set(false), auto_profiles(false);
tokenise_whitespace(f("profiles"), create_inserter<FSPath>(std::back_inserter(*profiles)));
if (profiles->empty())
{
if (master_repositories)
std::copy((*master_repositories->begin())->params().profiles()->begin(),
(*master_repositories->begin())->params().profiles()->end(), profiles->back_inserter());
else if (FSPath(location).stat().is_directory_or_symlink_to_directory() &&
(FSIterator(FSPath(location), { fsio_inode_sort, fsio_first_only }) != FSIterator()))
{
/* only require profiles = if we've definitely been synced. requiring profiles = on
* unsynced doesn't play nice with layout.conf specifying masters. */
throw ERepositoryConfigurationError("No profiles have been specified");
}
}
else if (f("profiles") == "(auto)")
auto_profiles = true;
else
profiles_explicitly_set = true;
std::shared_ptr<FSPathSequence> eclassdirs(std::make_shared<FSPathSequence>());
tokenise_whitespace(f("eclassdirs"), create_inserter<FSPath>(std::back_inserter(*eclassdirs)));
if (eclassdirs->empty())
{
if (master_repositories)
{
for (ERepositorySequence::ConstIterator e(master_repositories->begin()),
e_end(master_repositories->end()) ; e != e_end ; ++e)
std::copy((*e)->params().eclassdirs()->begin(), (*e)->params().eclassdirs()->end(), eclassdirs->back_inserter());
}
eclassdirs->push_back(FSPath(location + "/eclass"));
}
std::string distdir(f("distdir"));
if (distdir.empty())
{
if (master_repositories)
distdir = stringify((*master_repositories->begin())->params().distdir());
else
{
distdir = EExtraDistributionData::get_instance()->data_from_distribution(
*DistributionData::get_instance()->distribution_from_string(
env->distribution()))->default_distdir();
if (distdir.empty())
distdir = location + "/distfiles";
else if ('/' != distdir.at(0))
distdir = location + "/" + distdir;
}
}
std::string setsdir(f("setsdir"));
if (setsdir.empty())
setsdir = location + "/sets";
std::string securitydir(f("securitydir"));
if (securitydir.empty())
securitydir = location + "/metadata/glsa";
std::string newsdir(f("newsdir"));
if (newsdir.empty())
newsdir = location + "/metadata/news";
std::string cache(f("cache"));
if (cache.empty())
{
cache = location + "/metadata/md5-cache";
if (! FSPath(cache).stat().exists())
{
cache = location + "/metadata/cache";
if (! FSPath(cache).stat().exists())
cache = "/var/empty";
}
}
std::string write_cache(f("write_cache"));
if (write_cache.empty())
write_cache = EExtraDistributionData::get_instance()->data_from_distribution(*DistributionData::get_instance()->distribution_from_string(
env->distribution()))->default_write_cache();
bool append_repository_name_to_write_cache(true);
if (! f("append_repository_name_to_write_cache").empty())
{
Context item_context("When handling append_repository_name_to_write_cache key:");
append_repository_name_to_write_cache = destringify<bool>(f("append_repository_name_to_write_cache"));
}
bool ignore_deprecated_profiles(false);
if (! f("ignore_deprecated_profiles").empty())
{
Context item_context("When handling ignore_deprecated_profiles key:");
ignore_deprecated_profiles = destringify<bool>(f("ignore_deprecated_profiles"));
}
std::string eapi_when_unknown(f("eapi_when_unknown"));
if (eapi_when_unknown.empty())
{
if (! layout_conf
|| (eapi_when_unknown = layout_conf->get("eapi_when_unknown")).empty())
eapi_when_unknown = EExtraDistributionData::get_instance()->data_from_distribution(
*DistributionData::get_instance()->distribution_from_string(
env->distribution()))->default_eapi_when_unknown();
}
std::string eapi_when_unspecified(f("eapi_when_unspecified"));
if (eapi_when_unspecified.empty())
{
if (! layout_conf
|| (eapi_when_unspecified = layout_conf->get("eapi_when_unspecified")).empty())
eapi_when_unspecified = EExtraDistributionData::get_instance()->data_from_distribution(
*DistributionData::get_instance()->distribution_from_string(
env->distribution()))->default_eapi_when_unspecified();
}
std::string profile_eapi(f("profile_eapi_when_unspecified"));
if (profile_eapi.empty())
{
if (! layout_conf
|| (profile_eapi = layout_conf->get("profile_eapi_when_unspecified")).empty())
profile_eapi = EExtraDistributionData::get_instance()->data_from_distribution(
*DistributionData::get_instance()->distribution_from_string(
env->distribution()))->default_profile_eapi();
}
std::string names_cache(f("names_cache"));
if (names_cache.empty())
{
names_cache = EExtraDistributionData::get_instance()->data_from_distribution(
*DistributionData::get_instance()->distribution_from_string(
env->distribution()))->default_names_cache();
if (names_cache.empty())
{
Log::get_instance()->message("e.ebuild.configuration.no_names_cache", ll_warning, lc_no_context)
<< "The names_cache key is not set in '" << f("repo_file")
<< "'. You should read the Paludis documentation and select an appropriate value.";
names_cache = "/var/empty";
}
}
auto sync(std::make_shared<Map<std::string, std::string> >());
std::vector<std::string> sync_tokens;
tokenise_whitespace(f("sync"), std::back_inserter(sync_tokens));
std::string source;
for (auto & sync_token : sync_tokens)
if ((! sync_token.empty()) && (':' == sync_token.at(sync_token.length() - 1)))
source = sync_token.substr(0, sync_token.length() - 1);
else
{
std::string v;
if (sync->end() != sync->find(source))
v = sync->find(source)->second + " ";
sync->erase(source);
sync->insert(source, v + sync_token);
}
auto sync_options(std::make_shared<Map<std::string, std::string> >());
std::vector<std::string> sync_options_tokens;
tokenise_whitespace(f("sync_options"), std::back_inserter(sync_options_tokens));
source = "";
for (auto & sync_options_token : sync_options_tokens)
if ((! sync_options_token.empty()) && (':' == sync_options_token.at(sync_options_token.length() - 1)))
source = sync_options_token.substr(0, sync_options_token.length() - 1);
else
{
std::string v;
if (sync_options->end() != sync_options->find(source))
v = sync_options->find(source)->second + " ";
sync_options->erase(source);
sync_options->insert(source, v + sync_options_token);
}
std::string builddir(f("builddir"));
if (builddir.empty())
{
if (master_repositories)
builddir = stringify((*master_repositories->begin())->params().builddir());
else
builddir = EExtraDistributionData::get_instance()->data_from_distribution(
*DistributionData::get_instance()->distribution_from_string(
env->distribution()))->default_buildroot();
}
std::string layout(f("layout"));
if (layout.empty())
{
if (! layout_conf
|| (layout = layout_conf->get("layout")).empty())
layout = EExtraDistributionData::get_instance()->data_from_distribution(
*DistributionData::get_instance()->distribution_from_string(
env->distribution()))->default_layout();
}
std::string profile_layout(f("profile_layout"));
if (profile_layout.empty())
{
if (! layout_conf
|| (profile_layout = layout_conf->get("profile_layout")).empty())
{
if (master_repositories)
profile_layout = (*master_repositories->begin())->params().profile_layout();
else
profile_layout = EExtraDistributionData::get_instance()->data_from_distribution(
*DistributionData::get_instance()->distribution_from_string(
env->distribution()))->default_profile_layout();
}
}
UseManifest use_manifest(manifest_use);
if (! f("use_manifest").empty())
{
Context item_context("When handling use_manifest key:");
use_manifest = destringify<UseManifest>(f("use_manifest"));
}
std::shared_ptr<Set<std::string> > manifest_hashes_writable(std::make_shared<Set<std::string> >());
tokenise_whitespace(toupper(f("manifest_hashes")), manifest_hashes_writable->inserter());
if (manifest_hashes_writable->empty() && layout_conf)
// manifest-hashes with a hyphen, not an underscore (grrr)
tokenise_whitespace(toupper(layout_conf->get("manifest-hashes")), manifest_hashes_writable->inserter());
std::shared_ptr<const Set<std::string> > manifest_hashes;
if (! manifest_hashes_writable->empty())
manifest_hashes = manifest_hashes_writable;
else
manifest_hashes = EExtraDistributionData::get_instance()->data_from_distribution(
*DistributionData::get_instance()->distribution_from_string(
env->distribution()))->default_manifest_hashes();
bool thin_manifests(false);
if (! f("thin_manifests").empty())
{
Context item_context("When handling thin_manifests key:");
thin_manifests = destringify<bool>(f("thin_manifests"));
}
else if (layout_conf && ! layout_conf->get("thin-manifests").empty())
// match Portage parsing
thin_manifests = tolower(layout_conf->get("thin-manifests")) == "true";
else
thin_manifests = EExtraDistributionData::get_instance()->data_from_distribution(
*DistributionData::get_instance()->distribution_from_string(
env->distribution()))->default_thin_manifests();
bool binary_destination(false);
if (! f("binary_destination").empty())
{
Context item_context("When handling binary_destination key:");
binary_destination = destringify<bool>(f("binary_destination"));
}
std::string binary_uri_prefix(f("binary_uri_prefix"));
std::string binary_distdir(f("binary_distdir"));
std::string binary_keywords_filter(f("binary_keywords_filter"));
if (binary_keywords_filter.empty())
{
if (binary_destination)
throw ERepositoryConfigurationError("binary_destination = true, but binary_keywords_filter is unset or empty");
}
return std::make_shared<ERepository>(make_named_values<ERepositoryParams>(
n::append_repository_name_to_write_cache() = append_repository_name_to_write_cache,
n::auto_profiles() = auto_profiles,
n::binary_destination() = binary_destination,
n::binary_distdir() = binary_distdir,
n::binary_keywords_filter() = binary_keywords_filter,
n::binary_uri_prefix() = binary_uri_prefix,
n::builddir() = FSPath(builddir).realpath_if_exists(),
n::cache() = cache,
n::distdir() = FSPath(distdir).realpath_if_exists(),
n::eapi_when_unknown() = eapi_when_unknown,
n::eapi_when_unspecified() = eapi_when_unspecified,
n::eclassdirs() = eclassdirs,
n::entry_format() = "e",
n::environment() = env,
n::ignore_deprecated_profiles() = ignore_deprecated_profiles,
n::layout() = layout,
n::location() = FSPath(location).realpath_if_exists(),
n::manifest_hashes() = manifest_hashes,
n::master_repositories() = master_repositories,
n::names_cache() = FSPath(names_cache).realpath_if_exists(),
n::newsdir() = FSPath(newsdir).realpath_if_exists(),
n::profile_eapi_when_unspecified() = profile_eapi,
n::profile_layout() = profile_layout,
n::profiles() = profiles,
n::profiles_explicitly_set() = profiles_explicitly_set,
n::securitydir() = FSPath(securitydir).realpath_if_exists(),
n::setsdir() = FSPath(setsdir).realpath_if_exists(),
n::sync() = sync,
n::sync_options() = sync_options,
n::thin_manifests() = thin_manifests,
n::use_manifest() = use_manifest,
n::write_bin_uri_prefix() = "",
n::write_cache() = FSPath(write_cache).realpath_if_exists()
));
}
std::shared_ptr<const RepositoryNameSet>
ERepository::repository_factory_dependencies(
const Environment * const,
const std::function<std::string (const std::string &)> & f)
{
std::shared_ptr<RepositoryNameSet> result(std::make_shared<RepositoryNameSet>());
if (! f("master_repository").empty())
result->insert(RepositoryName(f("master_repository")));
else
{
std::string location(f("location"));
if (location.empty())
throw ERepositoryConfigurationError("Key 'location' not specified or empty");
std::shared_ptr<KeyValueConfigFile> layout_conf((FSPath(location) / "metadata/layout.conf").stat().exists() ?
new KeyValueConfigFile(FSPath(location) / "metadata/layout.conf", { },
&KeyValueConfigFile::no_defaults, &KeyValueConfigFile::no_transformation)
: nullptr);
if (layout_conf)
{
std::list<std::string> tokens;
tokenise_whitespace(layout_conf->get("masters"), std::back_inserter(tokens));
for (std::list<std::string>::const_iterator t(tokens.begin()), t_end(tokens.end()) ;
t != t_end ; ++t)
result->insert(RepositoryName(*t));
}
if ((! layout_conf) || (layout_conf->get("masters").empty()))
if (! f("master_repository_if_unknown").empty())
result->insert(RepositoryName(f("master_repository_if_unknown")));
}
return result;
}
const std::shared_ptr<const UseDesc>
ERepository::use_desc() const
{
std::unique_lock<std::mutex> l(_imp->mutexes->use_desc_mutex);
if (! _imp->use_desc)
{
_imp->use_desc = std::make_shared<UseDesc>(_imp->layout->use_desc_files());
}
return _imp->use_desc;
}
const std::string
ERepository::eapi_for_file(const FSPath & f) const
{
FSPath dir(f.dirname());
std::unique_lock<std::mutex> lock(_imp->mutexes->eapi_for_file_mutex);
EAPIForFileMap::const_iterator i(_imp->eapi_for_file_map.find(dir));
if (i == _imp->eapi_for_file_map.end())
{
Context context("When finding the EAPI to use for file '" + stringify(f) + "':");
if ((dir / "eapi").stat().is_regular_file_or_symlink_to_regular_file())
{
LineConfigFile file(dir / "eapi", { lcfo_disallow_continuations });
if (file.begin() == file.end())
{
Log::get_instance()->message("e.ebuild.profile_eapi_file.empty", ll_warning, lc_no_context)
<< "File '" << (dir / "eapi") << "' has no content";
i = _imp->eapi_for_file_map.insert(std::make_pair(
dir, _imp->params.profile_eapi_when_unspecified())).first;
}
else
i = _imp->eapi_for_file_map.insert(std::make_pair(dir, *file.begin())).first;
}
else
i = _imp->eapi_for_file_map.insert(std::make_pair(
dir, _imp->params.profile_eapi_when_unspecified())).first;
}
return i->second;
}
namespace
{
std::shared_ptr<const SetSpecTree> get_system_set(const std::shared_ptr<const SetSpecTree> & s)
{
return s;
}
std::shared_ptr<const SetSpecTree> get_set(
const std::shared_ptr<const ERepositorySets> & s,
const SetName & n)
{
return s->package_set(n);
}
}
void
ERepository::populate_sets() const
{
const std::shared_ptr<const SetNameSet> sets(_imp->sets_ptr->sets_list());
for (SetNameSet::ConstIterator s(sets->begin()), s_end(sets->end()) ;
s != s_end ; ++s)
{
if (stringify(*s) == "system")
{
_imp->need_profiles();
_imp->params.environment()->add_set(
*s,
SetName(stringify(*s) + "::" + stringify(name())),
std::bind(&get_system_set, _imp->profile_ptr->system_packages()),
true);
}
else
{
_imp->params.environment()->add_set(
*s,
SetName(stringify(*s) + "::" + stringify(name())),
std::bind(&get_set, _imp->sets_ptr, *s),
true);
if (stringify(*s) != "security" && stringify(*s) != "insecurity")
_imp->params.environment()->add_set(
SetName(stringify(*s) + "*"),
SetName(stringify(*s) + "::" + stringify(name()) + "*"),
std::bind(&get_set, _imp->sets_ptr, SetName(stringify(*s) + "*")),
true);
}
}
}
const std::shared_ptr<const MetadataCollectionKey<Map<std::string, std::string> > >
ERepository::sync_host_key() const
{
return _imp->sync_host_key;
}
const std::shared_ptr<const ERepositoryID>
ERepository::make_id(const QualifiedPackageName & q, const FSPath & f) const
{
Context context("When creating ID for '" + stringify(q) + "' from '" + stringify(f) + "':");
std::string suffix_eapi(FileSuffixes::get_instance()->guess_eapi_from_filename(q, f));
std::string eapi(suffix_eapi.empty() ? _imp->params.eapi_when_unknown() : suffix_eapi);
std::shared_ptr<EbuildID> result(std::make_shared<EbuildID>(q, extract_package_file_version(q, f, eapi),
_imp->params.environment(),
name(), f, eapi, ! suffix_eapi.empty(),
_imp->master_mtime, _imp->eclass_mtimes));
return result;
}
std::string
ERepository::get_environment_variable(
const std::shared_ptr<const PackageID> & id_uncasted,
const std::string & var) const
{
const std::shared_ptr<const ERepositoryID> id(std::static_pointer_cast<const ERepositoryID>(id_uncasted));
EAPIPhases phases(id->eapi()->supported()->ebuild_phases()->ebuild_variable());
int c(std::distance(phases.begin_phases(), phases.end_phases()));
if (1 != c)
throw EAPIConfigurationError("EAPI '" + id->eapi()->name() + "' defines "
+ (c == 0 ? "no" : stringify(c)) + " ebuild variable phases but expected exactly one");
bool userpriv_restrict;
{
using namespace std::placeholders;
DepSpecFlattener<PlainTextSpecTree, PlainTextDepSpec> restricts(_imp->params.environment(), id_uncasted);
if (id->restrict_key())
id->restrict_key()->parse_value()->top()->accept(restricts);
userpriv_restrict =
indirect_iterator(restricts.end()) != std::find_if(indirect_iterator(restricts.begin()), indirect_iterator(restricts.end()),
std::bind(std::equal_to<std::string>(), std::bind(std::mem_fn(&StringDepSpec::text), _1), "userpriv")) ||
indirect_iterator(restricts.end()) != std::find_if(indirect_iterator(restricts.begin()), indirect_iterator(restricts.end()),
std::bind(std::equal_to<std::string>(), std::bind(std::mem_fn(&StringDepSpec::text), _1), "nouserpriv"));
}
bool userpriv_ok((! userpriv_restrict) && (_imp->params.environment()->reduced_gid() != getgid()) &&
check_userpriv(FSPath(_imp->params.builddir()), _imp->params.environment(), id->eapi()->supported()->userpriv_cannot_use_root()));
std::shared_ptr<const FSPathSequence> exlibsdirs(layout()->exlibsdirs(id->name()));
EbuildVariableCommand cmd(make_named_values<EbuildCommandParams>(
n::builddir() = _imp->params.builddir(),
n::clearenv() = phases.begin_phases()->option("clearenv"),
n::commands() = join(phases.begin_phases()->begin_commands(), phases.begin_phases()->end_commands(), " "),
n::distdir() = _imp->params.distdir(),
n::ebuild_dir() = layout()->package_directory(id->name()),
n::ebuild_file() = id->fs_location_key()->parse_value(),
n::eclassdirs() = _imp->params.eclassdirs(),
n::environment() = _imp->params.environment(),
n::exlibsdirs() = exlibsdirs,
n::files_dir() = layout()->package_directory(id->name()) / "files",
n::maybe_output_manager() = nullptr,
n::package_builddir() = _imp->params.builddir() / (stringify(id->name().category()) + "-" + stringify(id->name().package()) + "-" + stringify(id->version()) + "-variable"),
n::package_id() = id,
n::parts() = nullptr,
n::permitted_directories() = nullptr,
n::portdir() =
(_imp->params.master_repositories() && ! _imp->params.master_repositories()->empty()) ?
(*_imp->params.master_repositories()->begin())->params().location() : _imp->params.location(),
n::root() = "/",
n::sandbox() = phases.begin_phases()->option("sandbox"),
n::sydbox() = phases.begin_phases()->option("sydbox"),
n::userpriv() = phases.begin_phases()->option("userpriv") && userpriv_ok,
n::volatile_files() = nullptr
),
var);
if (! cmd())
throw ActionFailedError("Couldn't get environment variable '" + stringify(var) +
"' for package '" + stringify(*id) + "'");
return cmd.result();
}
namespace
{
std::shared_ptr<const PackageID> find_id(const std::shared_ptr<const PackageIDSequence> & ids, const VersionSpec & v)
{
for (auto i(ids->begin()), i_end(ids->end()) ;
i != i_end ; ++i)
if ((*i)->version() == v)
return *i;
return nullptr;
}
}
void
ERepository::merge(const MergeParams & m)
{
Context context("When merging '" + stringify(*m.package_id()) + "' at '" + stringify(m.image_dir())
+ "' to E repository '" + stringify(name()) + "':");
if (! is_suitable_destination_for(m.package_id()))
throw ActionFailedError("Not a suitable destination for '" + stringify(*m.package_id()) + "'");
auto is_replace(find_id(package_ids(m.package_id()->name(), { }), m.package_id()->version()));
bool fix_mtimes(std::static_pointer_cast<const ERepositoryID>(
m.package_id())->eapi()->supported()->ebuild_options()->fix_mtimes());
std::string bin_dist_base(stringify(name()) + "--" + stringify(m.package_id()->name().category())
+ "--" + stringify(m.package_id()->name().package()) + "-" + stringify(m.package_id()->version())
+ "--" + cookie());
PbinMerger merger(
make_named_values<PbinMergerParams>(
n::environment() = _imp->params.environment(),
n::environment_file() = m.environment_file(),
n::fix_mtimes_before() = fix_mtimes ? m.build_start_time() : Timestamp(0, 0),
n::image() = m.image_dir(),
n::install_under() = FSPath("/"),
n::merged_entries() = m.merged_entries(),
n::options() = m.options(),
n::output_manager() = m.output_manager(),
n::package_id() = m.package_id(),
n::permit_destination() = m.permit_destination(),
n::root() = FSPath("/"),
n::tar_file() = _imp->params.binary_distdir() / (bin_dist_base + pbin_tar_extension)
));
if (m.check())
{
if (! merger.check())
throw ActionFailedError("Not proceeding with install due to merge sanity check failing");
return;
}
merger.merge();
Process compress_process(ProcessCommand({"bzip2", stringify(_imp->params.binary_distdir() / (bin_dist_base + pbin_tar_extension)) }));
if (0 != compress_process.run().wait())
throw ActionFailedError("Compressing tarball failed");
FSPath binary_ebuild_location(layout()->binary_ebuild_directory(m.package_id()->name()) / binary_ebuild_name(
m.package_id()->name(), m.package_id()->version(),
"pbin-1+" + std::static_pointer_cast<const ERepositoryID>(m.package_id())->eapi()->name()));
binary_ebuild_location.dirname().dirname().mkdir(0755, { fspmkdo_ok_if_exists });
binary_ebuild_location.dirname().mkdir(0755, { fspmkdo_ok_if_exists });
std::string binary_keywords;
if (m.package_id()->keywords_key())
{
auto kk(m.package_id()->keywords_key()->parse_value());
auto binary_keywords_filter(_imp->binary_keywords_filter->parse_value());
for (auto k(kk->begin()), k_end(kk->end()) ; k != k_end ; ++k)
if (binary_keywords_filter->end() != binary_keywords_filter->find(stringify(*k)))
{
if (! binary_keywords.empty())
binary_keywords.append(" ");
binary_keywords.append(stringify(*k));
}
}
WriteBinaryEbuildCommand write_binary_ebuild_command(
make_named_values<WriteBinaryEbuildCommandParams>(
n::binary_dist_base() = bin_dist_base,
n::binary_distdir() = _imp->params.binary_distdir(),
n::binary_ebuild_location() = binary_ebuild_location,
n::binary_keywords() = binary_keywords,
n::binary_uri_extension() = pbin_tar_extension + ".bz2",
n::builddir() = _imp->params.builddir(),
n::destination_repository() = this,
n::environment() = _imp->params.environment(),
n::environment_file() = m.environment_file(),
n::image() = m.image_dir(),
n::maybe_output_manager() = m.output_manager(),
n::merger_options() = std::static_pointer_cast<const ERepositoryID>(m.package_id())->eapi()->supported()->merger_options(),
n::package_id() = std::static_pointer_cast<const ERepositoryID>(m.package_id())
));
write_binary_ebuild_command();
std::list<std::shared_ptr<const PackageID> > replaces;
if (is_replace)
{
/* 0.1 replacing 00.1 etc */
if (is_replace->fs_location_key()->parse_value() != binary_ebuild_location)
{
FSPath p(is_replace->fs_location_key()->parse_value());
m.output_manager()->stdout_stream() << "Deleting replaced pbin " << p << std::endl;
p.unlink();
}
replaces.push_back(is_replace);
}
for (auto r(m.replacing()->begin()), r_end(m.replacing()->end()) ;
r != r_end ; ++r)
{
if (is_replace && (*r)->name() == is_replace->name() && (*r)->version() == is_replace->version())
continue;
if ((*r)->repository_name() != name())
continue;
FSPath p((*r)->fs_location_key()->parse_value());
FSStat p_stat(p);
if (p_stat.exists())
{
m.output_manager()->stdout_stream() << "Deleting pbin " << p << std::endl;
p.unlink();
}
replaces.push_back(*r);
}
if (_imp->params.write_cache() != FSPath("/var/empty"))
for (auto & replace : replaces)
{
FSPath cache(_imp->params.write_cache());
if (_imp->params.append_repository_name_to_write_cache())
cache /= stringify(name());
cache /= stringify(replace->name().category());
cache /= (stringify(replace->name().package()) + "-" + stringify(replace->version()));
if (cache.stat().is_regular_file_or_symlink_to_regular_file())
{
m.output_manager()->stdout_stream() << "Deleting cache file " << cache << std::endl;
cache.unlink();
}
}
if (! has_category_named(m.package_id()->name().category(), { }))
{
SafeOFStream s(_imp->layout->categories_file(), O_CREAT | O_WRONLY | O_CLOEXEC | O_APPEND, true);
s << m.package_id()->name().category() << std::endl;
}
}
VersionSpec
ERepository::extract_package_file_version(const QualifiedPackageName & n, const FSPath & e, const std::string & eapi) const
{
Context context("When extracting version from '" + stringify(e) + "':");
std::string::size_type p(e.basename().rfind('.'));
if (std::string::npos == p)
throw InternalError(PALUDIS_HERE, "got npos");
return VersionSpec(strip_leading_string(e.basename().substr(0, p), stringify(n.package()) + "-"),
EAPIData::get_instance()->eapi_from_string(
eapi)->supported()->version_spec_options());
}
const std::string
ERepository::binary_ebuild_name(const QualifiedPackageName & q, const VersionSpec & v, const std::string & e) const
{
return stringify(q.package()) + "-" + stringify(v) + "." + e;
}
const std::shared_ptr<const MirrorsSequence>
ERepository::get_mirrors(const std::string & m) const
{
need_mirrors();
MirrorMap::const_iterator i(_imp->mirrors.find(m));
if (i == _imp->mirrors.end())
return std::make_shared<MirrorsSequence>();
else
return i->second;
}
const std::shared_ptr<const Set<std::string> >
ERepository::maybe_expand_licence_nonrecursively(const std::string & s) const
{
return _imp->licence_groups->maybe_expand_licence_nonrecursively(s);
}