Exheredludis/paludis/repositories/e/do_install_action.cc
Saleem Abdulrasool f3cd245511 modernize: convert to range based for-loops
Automated conversion to range based for loops.  NFC
2016-08-04 22:11:01 -07:00

539 lines
27 KiB
C++

/* vim: set sw=4 sts=4 et foldmethod=syntax : */
/*
* Copyright (c) 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 <paludis/repositories/e/do_install_action.hh>
#include <paludis/repositories/e/a_finder.hh>
#include <paludis/repositories/e/eapi.hh>
#include <paludis/repositories/e/a_finder.hh>
#include <paludis/repositories/e/aa_visitor.hh>
#include <paludis/repositories/e/make_use.hh>
#include <paludis/repositories/e/check_userpriv.hh>
#include <paludis/repositories/e/eapi_phase.hh>
#include <paludis/repositories/e/can_skip_phase.hh>
#include <paludis/repositories/e/e_stripper.hh>
#include <paludis/repositories/e/ebuild.hh>
#include <paludis/repositories/e/make_archive_strings.hh>
#include <paludis/repositories/e/permitted_directories.hh>
#include <paludis/util/indirect_iterator-impl.hh>
#include <paludis/util/strip.hh>
#include <paludis/util/fs_stat.hh>
#include <paludis/util/make_named_values.hh>
#include <paludis/util/log.hh>
#include <paludis/util/join.hh>
#include <paludis/util/return_literal_function.hh>
#include <paludis/util/tokeniser.hh>
#include <paludis/action.hh>
#include <paludis/dep_spec_flattener.hh>
#include <paludis/metadata_key.hh>
#include <paludis/choice.hh>
#include <paludis/elike_choices.hh>
#include <paludis/output_manager.hh>
#include <paludis/partitioning.hh>
#include <paludis/slot.hh>
#include <vector>
#include <algorithm>
#include <set>
#include <unistd.h>
using namespace paludis;
using namespace paludis::erepository;
namespace
{
struct AcceptLicenseFinder
{
const Environment * const env;
const std::shared_ptr<const PackageID> id;
std::stringstream s;
AcceptLicenseFinder(const Environment * const e, const std::shared_ptr<const PackageID> & i) :
env(e),
id(i)
{
s << "*";
}
void visit(const LicenseSpecTree::NodeType<AllDepSpec>::Type & node)
{
std::for_each(indirect_iterator(node.begin()), indirect_iterator(node.end()), accept_visitor(*this));
}
void visit(const LicenseSpecTree::NodeType<AnyDepSpec>::Type & node)
{
std::for_each(indirect_iterator(node.begin()), indirect_iterator(node.end()), accept_visitor(*this));
}
void visit(const LicenseSpecTree::NodeType<ConditionalDepSpec>::Type & node)
{
if (node.spec()->condition_met(env, id))
std::for_each(indirect_iterator(node.begin()), indirect_iterator(node.end()), accept_visitor(*this));
}
void visit(const LicenseSpecTree::NodeType<LicenseDepSpec>::Type & node)
{
s << " " << node.spec()->text();
}
};
void used_this_for_config_protect(std::string & s, const std::string & v)
{
s = v;
}
bool parallel_slot_is_same(const std::shared_ptr<const PackageID> & a,
const std::shared_ptr<const PackageID> & b)
{
if (a->slot_key())
return b->slot_key() && a->slot_key()->parse_value().parallel_value() == b->slot_key()->parse_value().parallel_value();
else
return ! b->slot_key();
}
bool ignore_merged(const std::shared_ptr<const FSPathSet> & s, const FSPath & f)
{
return s->end() != s->find(f);
}
std::shared_ptr<OutputManager> this_output_manager(const std::shared_ptr<OutputManager> & o, const Action &)
{
return o;
}
}
void
paludis::erepository::do_install_action(
const Environment * const env,
const ERepository * const repo,
const std::shared_ptr<const ERepositoryID> & id,
const InstallAction & install_action)
{
using namespace std::placeholders;
Context context("When installing '" + stringify(*id) + "'" +
(install_action.options.replacing()->empty() ? "" : " replacing { '"
+ join(indirect_iterator(install_action.options.replacing()->begin()),
indirect_iterator(install_action.options.replacing()->end()), "', '") + "' }") + ":");
std::shared_ptr<OutputManager> output_manager(install_action.options.make_output_manager()(install_action));
bool userpriv_restrict, test_restrict, strip_restrict;
{
DepSpecFlattener<PlainTextSpecTree, PlainTextDepSpec> restricts(env, id);
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"));
test_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), "test"));
strip_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), "strip")) ||
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), "nostrip"));
}
std::string archives, all_archives, accept_license;
std::tie(archives, all_archives) = make_archives_strings(env, id);
/* make ACCEPT_LICENSE */
if (! id->eapi()->supported()->ebuild_environment_variables()->env_accept_license().empty())
{
AcceptLicenseFinder g(env, id);
if (id->license_key())
id->license_key()->parse_value()->top()->accept(g);
accept_license = g.s.str();
}
else
accept_license = "ACCEPT_LICENSE-not-set-for-this-EAPI";
/* Strip trailing space. Some ebuilds rely upon this. From kde-meta.eclass:
* [[ -n ${A/${TARBALL}/} ]] && unpack ${A/${TARBALL}/}
* Rather annoying.
*/
archives = strip_trailing(archives, " ");
all_archives = strip_trailing(all_archives, " ");
/* make use */
std::string use(make_use(env, *id, repo->profile()));
/* add expand to use (iuse isn't reliable for use_expand things), and make the expand
* environment variables */
std::shared_ptr<Map<std::string, std::string> > expand_vars(make_expand(env, *id, repo->profile()));
std::shared_ptr<const FSPathSequence> exlibsdirs(repo->layout()->exlibsdirs(id->name()));
bool userpriv_ok((! userpriv_restrict) && (env->reduced_gid() != getgid()) &&
check_userpriv(FSPath(repo->params().distdir()), env, id->eapi()->supported()->userpriv_cannot_use_root()) &&
check_userpriv(FSPath(repo->params().builddir()), env, id->eapi()->supported()->userpriv_cannot_use_root()));
FSPath package_builddir(repo->params().builddir() / (stringify(id->name().category()) + "-" +
stringify(id->name().package()) + "-" + stringify(id->version())));
std::string used_config_protect;
auto merged_entries(std::make_shared<FSPathSet>());
auto permitted_directories(std::make_shared<PermittedDirectories>());
{
std::vector<std::string> tokens;
tokenise_whitespace(id->eapi()->supported()->permitted_directories(), std::back_inserter(tokens));
for (auto & token : tokens)
{
if (token.at(0) == '-')
permitted_directories->add(FSPath(token.substr(1)), false);
else if (token.at(0) == '+')
permitted_directories->add(FSPath(token.substr(1)), true);
else
throw InternalError(PALUDIS_HERE, "bad permitted_directories");
}
}
auto parts(std::make_shared<Partitioning>());
auto choices(id->choices_key()->parse_value());
auto work_choice(choices->find_by_name_with_prefix(ELikeWorkChoiceValue::canonical_name_with_prefix()));
auto volatile_files(std::make_shared<FSPathSet>());
auto destination = install_action.options.destination();
EAPIPhases phases(id->eapi()->supported()->ebuild_phases()->ebuild_install());
for (EAPIPhases::ConstIterator phase(phases.begin_phases()), phase_end(phases.end_phases()) ;
phase != phase_end ; ++phase)
{
bool skip(false);
do
{
switch (install_action.options.want_phase()(phase->equal_option("skipname")))
{
case wp_yes:
continue;
case wp_skip:
skip = true;
continue;
case wp_abort:
throw ActionAbortedError("Told to abort install");
case last_wp:
break;
}
throw InternalError(PALUDIS_HERE, "bad want_phase");
} while (false);
if (skip)
continue;
if (can_skip_phase(env, id, *phase))
{
output_manager->stdout_stream() << "--- No need to do anything for " << phase->equal_option("skipname") << " phase" << std::endl;
continue;
}
if (phase->option("tidyup") && work_choice && ! ELikeWorkChoiceValue::should_remove(work_choice->parameter()))
{
output_manager->stdout_stream() << "--- Skipping " << phase->equal_option("skipname")
<< " phase to preserve work" << std::endl;
continue;
}
if (phase->option("merge") || phase->option("check_merge"))
{
if (! destination->destination_interface())
throw ActionFailedError("Can't install '" + stringify(*id)
+ "' to destination '" + stringify(destination->name())
+ "' because destination does not provide destination_interface");
MergerOptions extra_merger_options;
if (work_choice && ELikeWorkChoiceValue::should_merge_nondestructively(work_choice->parameter()))
extra_merger_options += mo_nondestructive;
Timestamp build_start_time(FSPath(package_builddir / "temp" / "build_start_time").stat().mtim());
destination->destination_interface()->merge(
make_named_values<MergeParams>(
n::build_start_time() = build_start_time,
n::check() = phase->option("check_merge"),
n::environment_file() = package_builddir / "temp" / "loadsaveenv",
n::image_dir() = package_builddir / "image",
n::is_volatile() = [&] (const FSPath & f) { return volatile_files->end() != volatile_files->find(f); },
n::merged_entries() = merged_entries,
n::options() = id->eapi()->supported()->merger_options() | extra_merger_options,
n::output_manager() = output_manager,
n::package_id() = id,
n::parts() = parts,
n::perform_uninstall() = install_action.options.perform_uninstall(),
n::permit_destination() =
std::bind(&PermittedDirectories::permit,
permitted_directories,
std::placeholders::_1),
n::replacing() = install_action.options.replacing(),
n::used_this_for_config_protect() = std::bind(
&used_this_for_config_protect, std::ref(used_config_protect), std::placeholders::_1),
n::want_phase() = install_action.options.want_phase()
));
if (volatile_files && phase->option("check_merge")) {
for (auto & v : *volatile_files) {
auto vabs = (package_builddir / "image" / v);
auto vstat = vabs.stat();
if (! vstat.is_regular_file_or_symlink_to_regular_file())
throw ActionFailedError("Can't install '" + stringify(*id)
+ "' to destination '" + stringify(destination->name())
+ "' because '" + stringify(v) + "' was marked using exvolatile, but it is not a regular "
"file or a symlink to a regular file");
if (vstat.is_symlink() && 0 == vabs.readlink().compare(0, 1, "/", 0, 1))
throw ActionFailedError("Can't install '" + stringify(*id)
+ "' to destination '" + stringify(destination->name())
+ "' because '" + stringify(v) + "' was marked using exvolatile, but volatile symlinks"
"must not be absolute");
}
}
}
else if (phase->option("strip"))
{
if ((! id->eapi()->supported()->is_pbin()) && (! strip_restrict))
{
std::string libdir("lib");
FSPath root(destination->installed_root_key()
? stringify(destination->installed_root_key()->parse_value())
: "/");
if ((root / "usr" / "lib").stat().is_symlink())
{
libdir = (root / "usr" / "lib").readlink();
if (std::string::npos != libdir.find_first_of("./"))
libdir = "lib";
}
Log::get_instance()->message("e.ebuild.libdir", ll_debug, lc_context) << "Using '" << libdir << "' for libdir";
std::shared_ptr<const ChoiceValue> symbols_choice(choices->find_by_name_with_prefix(
ELikeSymbolsChoiceValue::canonical_name_with_prefix()));
auto dwarf_compression(choices->find_by_name_with_prefix(ELikeDwarfCompressionChoiceValue::canonical_name_with_prefix()));
EStripper stripper(make_named_values<EStripperOptions>(
n::compress_splits() = symbols_choice && symbols_choice->enabled() && ELikeSymbolsChoiceValue::should_compress(
symbols_choice->parameter()),
n::debug_dir() = package_builddir / "image" / "usr" / libdir / "debug",
n::dwarf_compression() = dwarf_compression && dwarf_compression->enabled(),
n::image_dir() = package_builddir / "image",
n::output_manager() = output_manager,
n::package_id() = id,
n::split() = symbols_choice && symbols_choice->enabled() && ELikeSymbolsChoiceValue::should_split(symbols_choice->parameter()),
n::strip() = symbols_choice && symbols_choice->enabled() && ELikeSymbolsChoiceValue::should_strip(symbols_choice->parameter())
));
stripper.strip();
}
}
else if ((! phase->option("prepost")) ||
(destination->destination_interface() &&
destination->destination_interface()->want_pre_post_phases()))
{
if (phase->option("optional_tests"))
{
if (test_restrict)
continue;
std::shared_ptr<const ChoiceValue> choice(choices->find_by_name_with_prefix(
ELikeOptionalTestsChoiceValue::canonical_name_with_prefix()));
if (choice && ! choice->enabled())
continue;
}
else if (phase->option("recommended_tests"))
{
if (test_restrict)
continue;
std::shared_ptr<const ChoiceValue> choice(choices->find_by_name_with_prefix(
ELikeRecommendedTestsChoiceValue::canonical_name_with_prefix()));
if (choice && ! choice->enabled())
continue;
}
else if (phase->option("expensive_tests"))
{
std::shared_ptr<const ChoiceValue> choice(choices->find_by_name_with_prefix(
ELikeExpensiveTestsChoiceValue::canonical_name_with_prefix()));
if (choice && ! choice->enabled())
continue;
}
const auto params = repo->params();
const auto profile = repo->profile();
EbuildCommandParams command_params(make_named_values<EbuildCommandParams>(
n::builddir() = params.builddir(),
n::clearenv() = phase->option("clearenv"),
n::commands() = join(phase->begin_commands(), phase->end_commands(), " "),
n::distdir() = params.distdir(),
n::ebuild_dir() = repo->layout()->package_directory(id->name()),
n::ebuild_file() = id->fs_location_key()->parse_value(),
n::eclassdirs() = params.eclassdirs(),
n::environment() = env,
n::exlibsdirs() = exlibsdirs,
n::files_dir() = repo->layout()->package_directory(id->name()) / "files",
n::maybe_output_manager() = output_manager,
n::package_builddir() = package_builddir,
n::package_id() = id,
n::parts() = parts,
n::permitted_directories() = permitted_directories,
n::portdir() =
(params.master_repositories() && ! params.master_repositories()->empty()) ?
(*params.master_repositories()->begin())->params().location() : params.location(),
n::root() = destination->installed_root_key()
? stringify(destination->installed_root_key()->parse_value())
: "/",
n::sandbox() = phase->option("sandbox"),
n::sydbox() = phase->option("sydbox"),
n::userpriv() = phase->option("userpriv") && userpriv_ok,
n::volatile_files() = volatile_files
));
EbuildInstallCommandParams install_params(
make_named_values<EbuildInstallCommandParams>(
n::a() = archives,
n::aa() = all_archives,
n::accept_license() = accept_license,
n::config_protect() = repo->environment_updated_profile_variable("CONFIG_PROTECT"),
n::config_protect_mask() = repo->environment_updated_profile_variable("CONFIG_PROTECT_MASK"),
n::destination() = destination,
n::expand_vars() = expand_vars,
n::is_from_pbin() = id->eapi()->supported()->is_pbin(),
n::loadsaveenv_dir() = package_builddir / "temp",
n::profiles() = params.profiles(),
n::profiles_with_parents() = profile->profiles_with_parents(),
n::replacing_ids() = install_action.options.replacing(),
n::slot() = id->slot_key() ? stringify(id->slot_key()->parse_value().raw_value()) : "",
n::use() = use,
n::use_expand() = join(profile->use_expand()->begin(), profile->use_expand()->end(), " "),
n::use_expand_hidden() = join(profile->use_expand_hidden()->begin(), profile->use_expand_hidden()->end(), " ")
));
EbuildInstallCommand cmd(command_params, install_params);
try
{
cmd();
}
catch (const ActionFailedError & e)
{
if (work_choice && ELikeWorkChoiceValue::should_remove_on_failure(work_choice->parameter()))
{
for (EAPIPhases::ConstIterator tidyup_phase(phases.begin_phases()), tidyup_phase_end(phases.end_phases()) ;
tidyup_phase != tidyup_phase_end ; ++tidyup_phase)
{
if (! tidyup_phase->option("tidyup"))
continue;
EbuildCommandParams tidyup_command_params(make_named_values<EbuildCommandParams>(
n::builddir() = params.builddir(),
n::clearenv() = tidyup_phase->option("clearenv"),
n::commands() = join(tidyup_phase->begin_commands(), tidyup_phase->end_commands(), " "),
n::distdir() = params.distdir(),
n::ebuild_dir() = repo->layout()->package_directory(id->name()),
n::ebuild_file() = id->fs_location_key()->parse_value(),
n::eclassdirs() = params.eclassdirs(),
n::environment() = env,
n::exlibsdirs() = exlibsdirs,
n::files_dir() = repo->layout()->package_directory(id->name()) / "files",
n::maybe_output_manager() = output_manager,
n::package_builddir() = package_builddir,
n::package_id() = id,
n::parts() = parts,
n::permitted_directories() = permitted_directories,
n::portdir() =
(params.master_repositories() && ! params.master_repositories()->empty())
? (*params.master_repositories()->begin())->params().location()
: params.location(),
n::root() = destination->installed_root_key()
? stringify(destination->installed_root_key()->parse_value())
: "/",
n::sandbox() = tidyup_phase->option("sandbox"),
n::sydbox() = tidyup_phase->option("sydbox"),
n::userpriv() = tidyup_phase->option("userpriv") && userpriv_ok,
n::volatile_files() = volatile_files
));
EbuildInstallCommandParams tidyup_install_params(
make_named_values<EbuildInstallCommandParams>(
n::a() = archives,
n::aa() = all_archives,
n::accept_license() = accept_license,
n::config_protect() = repo->environment_updated_profile_variable("CONFIG_PROTECT"),
n::config_protect_mask() = repo->environment_updated_profile_variable("CONFIG_PROTECT_MASK"),
n::destination() = destination,
n::expand_vars() = expand_vars,
n::is_from_pbin() = id->eapi()->supported()->is_pbin(),
n::loadsaveenv_dir() = package_builddir / "temp",
n::profiles() = params.profiles(),
n::profiles_with_parents() = profile->profiles_with_parents(),
n::replacing_ids() = install_action.options.replacing(),
n::slot() = id->slot_key() ? stringify(id->slot_key()->parse_value().raw_value()) : "",
n::use() = use,
n::use_expand() = join(profile->use_expand()->begin(), profile->use_expand()->end(), " "),
n::use_expand_hidden() = join(profile->use_expand_hidden()->begin(), profile->use_expand_hidden()->end(), " ")
));
EbuildInstallCommand tidyup_cmd(tidyup_command_params, tidyup_install_params);
tidyup_cmd();
}
}
throw;
}
}
}
/* replacing for pbins is done during the merge */
if (destination->installed_root_key())
for (PackageIDSequence::ConstIterator i(install_action.options.replacing()->begin()), i_end(install_action.options.replacing()->end()) ;
i != i_end ; ++i)
{
Context local_context("When cleaning '" + stringify(**i) + "':");
if ((*i)->name() == id->name() && (*i)->version() == id->version())
continue;
if (id->eapi()->supported()->ebuild_phases()->ebuild_new_upgrade_phase_order())
if ((*i)->name() == id->name() && parallel_slot_is_same(*i, id))
continue;
UninstallActionOptions uo(make_named_values<UninstallActionOptions>(
n::config_protect() = used_config_protect,
n::if_for_install_id() = id,
n::ignore_for_unmerge() = std::bind(&ignore_merged, merged_entries,
std::placeholders::_1),
n::is_overwrite() = false,
n::make_output_manager() = std::bind(&this_output_manager, output_manager, std::placeholders::_1),
n::override_contents() = nullptr,
n::want_phase() = install_action.options.want_phase()
));
install_action.options.perform_uninstall()(*i, uo);
}
output_manager->succeeded();
}