aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAvatar Ciaran McCreesh <ciaran.mccreesh@googlemail.com> 2008-10-26 18:31:48 +0000
committerAvatar Ciaran McCreesh <ciaran.mccreesh@googlemail.com> 2008-10-26 18:31:48 +0000
commit20f5474fe42c320a3a1711e2ae245dee8c74402b (patch)
treec4672e13ad06831d701632b4a79803247f15e692
parentd6ea84ebfedb53646b94779c7f25dfd3bdc18142 (diff)
downloadpaludis-20f5474fe42c320a3a1711e2ae245dee8c74402b.tar.gz
paludis-20f5474fe42c320a3a1711e2ae245dee8c74402b.tar.xz
More cave work.
Support redirecting sync output.
-rw-r--r--configure.ac13
-rwxr-xr-xhooks/log.bash28
-rw-r--r--paludis/repositories/cran/cran_repository.cc62
-rw-r--r--paludis/repositories/cran/cran_repository.hh2
-rw-r--r--paludis/repositories/e/e_repository.cc3
-rw-r--r--paludis/repositories/e/e_repository.hh2
-rw-r--r--paludis/repositories/unavailable/unavailable_repository.cc3
-rw-r--r--paludis/repositories/unavailable/unavailable_repository.hh2
-rw-r--r--paludis/repositories/unwritten/unwritten_repository.cc3
-rw-r--r--paludis/repositories/unwritten/unwritten_repository.hh2
-rw-r--r--paludis/repository.hh4
-rw-r--r--paludis/sync_task.cc3
-rw-r--r--paludis/syncer.cc7
-rw-r--r--paludis/syncer.hh8
-rw-r--r--paludis/util/files.m41
-rw-r--r--paludis/util/output_deviator-fwd.hh29
-rw-r--r--paludis/util/output_deviator.cc132
-rw-r--r--paludis/util/output_deviator.hh66
-rw-r--r--src/clients/cave/Makefile.am5
-rw-r--r--src/clients/cave/cmd_sync.cc271
-rw-r--r--src/clients/cave/cmd_sync.hh44
-rw-r--r--src/clients/cave/command_factory.cc2
-rw-r--r--src/clients/cave/exceptions.cc5
-rw-r--r--src/clients/cave/exceptions.hh10
-rw-r--r--src/clients/cave/format_general.cc21
-rw-r--r--src/clients/cave/format_general.hh7
-rw-r--r--src/clients/cave/formats.cc54
-rw-r--r--src/clients/cave/formats.hh10
28 files changed, 763 insertions, 36 deletions
diff --git a/configure.ac b/configure.ac
index 62433f0..a1fdd14 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1308,6 +1308,19 @@ AC_SUBST([ALL_CLIENTS])
AC_SUBST([ALL_CLIENTS_HTML])
AC_SUBST([BUILD_CLIENTS])
+AC_MSG_CHECKING([whether threads must be enabled])
+if echo $clients | tr ' ' '\n' | grep '^cave$' >/dev/null ; then
+ AC_MSG_RESULT([yes])
+ AC_MSG_CHECKING([whether threads are enabled])
+ if test x$ENABLE_THREADS = xyes ; then
+ AC_MSG_RESULT([yes])
+ else
+ AC_MSG_ERROR([cave requires --enable-threads])
+ fi
+else
+ AC_MSG_RESULT([no])
+fi
+
if echo $clients | tr ' ' '\n' | grep '^inquisitio$' >/dev/null ; then
need_pcrepp_check=yes
fi
diff --git a/hooks/log.bash b/hooks/log.bash
index a245b7a..1536153 100755
--- a/hooks/log.bash
+++ b/hooks/log.bash
@@ -1,7 +1,7 @@
#!/usr/bin/env bash
# vim: set et sw=4 sts=4 :
-# Copyright (c) 2006 Ciaran McCreesh
+# Copyright (c) 2006, 2008 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
@@ -18,18 +18,22 @@
mkdir -p "${ROOT}/var/log"
-X_OF_Y="${X_OF_Y+ (${X_OF_Y})}"
+if [[ -n ${X_OF_Y} ]] ; then
+ COUNTS=" (${X_OF_Y})}"
+else
+ COUNTS=" [${NUMBER_PENDING}p ${NUMBER_ACTIVE}a ${NUMBER_DONE}d]"
+fi
(
echo -n "$(date +%s ): "
case "${HOOK}" in
install_pre)
- echo "starting install of package ${TARGET}${X_OF_Y}"
+ echo "starting install of package ${TARGET}${COUNTS}"
;;
install_post)
- echo "finished install of package ${TARGET}${X_OF_Y}"
+ echo "finished install of package ${TARGET}${COUNTS}"
;;
install_all_pre)
@@ -41,11 +45,11 @@ X_OF_Y="${X_OF_Y+ (${X_OF_Y})}"
;;
uninstall_pre)
- echo "starting uninstall of package ${TARGET}${X_OF_Y}"
+ echo "starting uninstall of package ${TARGET}${COUNTS}"
;;
uninstall_post)
- echo "finished uninstall of package ${TARGET}${X_OF_Y}"
+ echo "finished uninstall of package ${TARGET}${COUNTS}"
;;
uninstall_all_pre)
@@ -57,11 +61,11 @@ X_OF_Y="${X_OF_Y+ (${X_OF_Y})}"
;;
clean_pre)
- echo "starting clean of package ${TARGET}${X_OF_Y}"
+ echo "starting clean of package ${TARGET}${COUNTS}"
;;
clean_post)
- echo "finished clean of package ${TARGET}${X_OF_Y}"
+ echo "finished clean of package ${TARGET}${COUNTS}"
;;
clean_all_pre)
@@ -73,11 +77,11 @@ X_OF_Y="${X_OF_Y+ (${X_OF_Y})}"
;;
fetch_pre)
- echo "starting fetch of package ${TARGET}${X_OF_Y}"
+ echo "starting fetch of package ${TARGET}${COUNTS}"
;;
fetch_post)
- echo "finished fetch of package ${TARGET}${X_OF_Y}"
+ echo "finished fetch of package ${TARGET}${COUNTS}"
;;
fetch_all_pre)
@@ -89,11 +93,11 @@ X_OF_Y="${X_OF_Y+ (${X_OF_Y})}"
;;
sync_pre)
- echo "starting sync of repository ${TARGET}${X_OF_Y}"
+ echo "starting sync of repository ${TARGET}${COUNTS}"
;;
sync_post)
- echo "finished sync of repository ${TARGET}${X_OF_Y}"
+ echo "finished sync of repository ${TARGET}${COUNTS}"
;;
*)
diff --git a/paludis/repositories/cran/cran_repository.cc b/paludis/repositories/cran/cran_repository.cc
index 9af0595..1b216a7 100644
--- a/paludis/repositories/cran/cran_repository.cc
+++ b/paludis/repositories/cran/cran_repository.cc
@@ -45,6 +45,8 @@
#include <paludis/util/visitor-impl.hh>
#include <paludis/util/hashes.hh>
#include <paludis/util/make_named_values.hh>
+#include <paludis/util/output_deviator.hh>
+#include <paludis/syncer.hh>
#include <tr1/unordered_map>
#include <tr1/functional>
#include <functional>
@@ -368,7 +370,7 @@ CRANRepository::sets_list() const
}
bool
-CRANRepository::sync() const
+CRANRepository::sync(const std::tr1::shared_ptr<const OutputDeviant> & output_deviant) const
{
Context context("When syncing repository '" + stringify(name()) + "':");
Lock l(*_imp->big_nasty_mutex);
@@ -376,26 +378,62 @@ CRANRepository::sync() const
std::string cmd("rsync --delete --recursive --progress --exclude \"*.html\" --exclude \"*.INDEX\" '" +
_imp->params.sync + "/src/contrib/Descriptions/' ./");
- if (0 != run_command(Command(cmd).with_chdir(_imp->params.location)))
- return false;
+ Command command1(Command(cmd).with_chdir(_imp->params.location));
+
+ if (output_deviant)
+ command1
+ .with_captured_stdout_stream(output_deviant->stdout_stream())
+ .with_captured_stderr_stream(output_deviant->stderr_stream())
+ ;
+ else
+ command1
+ .with_stdout_prefix("sync " + stringify(name()) + "> ")
+ .with_stderr_prefix("sync " + stringify(name()) + "> ")
+ .with_prefix_blank_lines()
+ ;
+
+ if (0 != run_command(command1))
+ throw SyncFailedError(stringify(_imp->params.location), _imp->params.sync);
cmd = "rsync --progress '" + _imp->params.sync + "/src/contrib/PACKAGES' ./";
- if (0 != run_command(Command(cmd)
- .with_chdir(_imp->params.location)
- .with_stdout_prefix("sync " + stringify(name()) + "> ")
- .with_stderr_prefix("sync " + stringify(name()) + "> ")
- ))
- return false;
+ Command command2(Command(cmd).with_chdir(_imp->params.location));
+
+ if (output_deviant)
+ command2
+ .with_captured_stdout_stream(output_deviant->stdout_stream())
+ .with_captured_stderr_stream(output_deviant->stderr_stream())
+ ;
+ else
+ command2
+ .with_stdout_prefix("sync " + stringify(name()) + "> ")
+ .with_stderr_prefix("sync " + stringify(name()) + "> ")
+ .with_prefix_blank_lines()
+ ;
+
+ if (0 != run_command(command2))
+ throw SyncFailedError(stringify(_imp->params.location), _imp->params.sync);
cmd = "rsync --progress '" + _imp->params.sync + "/CRAN_mirrors.csv' ./";
- return 0 == run_command(Command(cmd)
- .with_chdir(_imp->params.location)
+ Command command3(Command(cmd).with_chdir(_imp->params.location));
+
+ if (output_deviant)
+ command3
+ .with_captured_stdout_stream(output_deviant->stdout_stream())
+ .with_captured_stderr_stream(output_deviant->stderr_stream())
+ ;
+ else
+ command3
.with_stdout_prefix("sync " + stringify(name()) + "> ")
.with_stderr_prefix("sync " + stringify(name()) + "> ")
.with_prefix_blank_lines()
- );
+ ;
+
+ if (0 != run_command(command3))
+ throw SyncFailedError(stringify(_imp->params.location), _imp->params.sync);
+
+ return true;
}
std::tr1::shared_ptr<Repository>
diff --git a/paludis/repositories/cran/cran_repository.hh b/paludis/repositories/cran/cran_repository.hh
index 6a3da7a..6155165 100644
--- a/paludis/repositories/cran/cran_repository.hh
+++ b/paludis/repositories/cran/cran_repository.hh
@@ -107,7 +107,7 @@ namespace paludis
/* RepositorySyncableInterface */
- virtual bool sync() const;
+ virtual bool sync(const std::tr1::shared_ptr<const OutputDeviant> & output_deviant) const;
/* Repository */
diff --git a/paludis/repositories/e/e_repository.cc b/paludis/repositories/e/e_repository.cc
index 55bcc92..7dad627 100644
--- a/paludis/repositories/e/e_repository.cc
+++ b/paludis/repositories/e/e_repository.cc
@@ -708,7 +708,7 @@ ERepository::sets_list() const
}
bool
-ERepository::sync() const
+ERepository::sync(const std::tr1::shared_ptr<const OutputDeviant> & output_deviant) const
{
Context context("When syncing repository '" + stringify(name()) + "':");
@@ -730,6 +730,7 @@ ERepository::sync() const
SyncOptions opts(make_named_values<SyncOptions>(
value_for<n::filter_file>(_imp->layout->sync_filter_file()),
value_for<n::options>(_imp->params.sync_options),
+ value_for<n::output_deviant>(output_deviant),
value_for<n::output_prefix>("sync " + stringify(name()) + "> ")
));
try
diff --git a/paludis/repositories/e/e_repository.hh b/paludis/repositories/e/e_repository.hh
index f083020..f587a42 100644
--- a/paludis/repositories/e/e_repository.hh
+++ b/paludis/repositories/e/e_repository.hh
@@ -133,7 +133,7 @@ namespace paludis
/* RepositorySyncableInterface */
- virtual bool sync() const;
+ virtual bool sync(const std::tr1::shared_ptr<const OutputDeviant> & output_deviant) const;
/* RepositoryEnvironmentVariableInterface */
diff --git a/paludis/repositories/unavailable/unavailable_repository.cc b/paludis/repositories/unavailable/unavailable_repository.cc
index df910b8..20ffc6b 100644
--- a/paludis/repositories/unavailable/unavailable_repository.cc
+++ b/paludis/repositories/unavailable/unavailable_repository.cc
@@ -257,7 +257,7 @@ UnavailableRepository::some_ids_might_support_action(const SupportsActionTestBas
}
bool
-UnavailableRepository::sync() const
+UnavailableRepository::sync(const std::tr1::shared_ptr<const OutputDeviant> & output_deviant) const
{
Context context("When syncing repository '" + stringify(name()) + "':");
@@ -279,6 +279,7 @@ UnavailableRepository::sync() const
SyncOptions opts(make_named_values<SyncOptions>(
value_for<n::filter_file>(FSEntry("/dev/null")),
value_for<n::options>(_imp->params.sync_options()),
+ value_for<n::output_deviant>(output_deviant),
value_for<n::output_prefix>("sync " + stringify(name()) + "> ")
));
try
diff --git a/paludis/repositories/unavailable/unavailable_repository.hh b/paludis/repositories/unavailable/unavailable_repository.hh
index 2998196..eb40d33 100644
--- a/paludis/repositories/unavailable/unavailable_repository.hh
+++ b/paludis/repositories/unavailable/unavailable_repository.hh
@@ -92,7 +92,7 @@ namespace paludis
virtual void invalidate();
virtual void invalidate_masks();
- virtual bool sync() const;
+ virtual bool sync(const std::tr1::shared_ptr<const OutputDeviant> &) const;
///\name RepositoryFactory functions
///\{
diff --git a/paludis/repositories/unwritten/unwritten_repository.cc b/paludis/repositories/unwritten/unwritten_repository.cc
index 1e4f3eb..e8b0864 100644
--- a/paludis/repositories/unwritten/unwritten_repository.cc
+++ b/paludis/repositories/unwritten/unwritten_repository.cc
@@ -257,7 +257,7 @@ UnwrittenRepository::some_ids_might_support_action(const SupportsActionTestBase
}
bool
-UnwrittenRepository::sync() const
+UnwrittenRepository::sync(const std::tr1::shared_ptr<const OutputDeviant> & output_deviant) const
{
Context context("When syncing repository '" + stringify(name()) + "':");
@@ -279,6 +279,7 @@ UnwrittenRepository::sync() const
SyncOptions opts(make_named_values<SyncOptions>(
value_for<n::filter_file>(FSEntry("/dev/null")),
value_for<n::options>(_imp->params.sync_options()),
+ value_for<n::output_deviant>(output_deviant),
value_for<n::output_prefix>("sync " + stringify(name()) + "> ")
));
try
diff --git a/paludis/repositories/unwritten/unwritten_repository.hh b/paludis/repositories/unwritten/unwritten_repository.hh
index e9da71b..a2a3e93 100644
--- a/paludis/repositories/unwritten/unwritten_repository.hh
+++ b/paludis/repositories/unwritten/unwritten_repository.hh
@@ -91,7 +91,7 @@ namespace paludis
virtual void invalidate();
virtual void invalidate_masks();
- virtual bool sync() const;
+ virtual bool sync(const std::tr1::shared_ptr<const OutputDeviant> & output_deviant) const;
///\name RepositoryFactory functions
///\{
diff --git a/paludis/repository.hh b/paludis/repository.hh
index 9ed986c..189a24b 100644
--- a/paludis/repository.hh
+++ b/paludis/repository.hh
@@ -34,6 +34,7 @@
#include <paludis/util/wrapped_forward_iterator-fwd.hh>
#include <paludis/util/options.hh>
#include <paludis/util/named_value.hh>
+#include <paludis/util/output_deviator-fwd.hh>
#include <paludis/version_spec.hh>
#include <paludis/metadata_key-fwd.hh>
#include <paludis/metadata_key_holder.hh>
@@ -426,9 +427,10 @@ namespace paludis
/**
* Sync, if necessary.
*
+ * \param output_deviant May be an empty pointer, for no deviation.
* \return True if we synced successfully, false if we skipped sync.
*/
- virtual bool sync() const = 0;
+ virtual bool sync(const std::tr1::shared_ptr<const OutputDeviant> & output_deviant) const = 0;
///\}
diff --git a/paludis/sync_task.cc b/paludis/sync_task.cc
index 69faa86..904eb04 100644
--- a/paludis/sync_task.cc
+++ b/paludis/sync_task.cc
@@ -24,6 +24,7 @@
#include <paludis/util/wrapped_forward_iterator-impl.hh>
#include <paludis/util/action_queue.hh>
#include <paludis/util/mutex.hh>
+#include <paludis/util/make_shared_ptr.hh>
#include <paludis/package_database.hh>
#include <paludis/hook.hh>
#include <tr1/functional>
@@ -112,7 +113,7 @@ namespace
}
std::tr1::shared_ptr<const Repository> rr(env->package_database()->fetch_repository(r));
- if ((*rr).syncable_interface() && (*rr).syncable_interface()->sync())
+ if ((*rr).syncable_interface() && (*rr).syncable_interface()->sync(make_null_shared_ptr()))
{
Lock l(mutex);
task->on_sync_succeed(r);
diff --git a/paludis/syncer.cc b/paludis/syncer.cc
index 085f043..22935c8 100644
--- a/paludis/syncer.cc
+++ b/paludis/syncer.cc
@@ -28,6 +28,7 @@
#include <paludis/util/join.hh>
#include <paludis/util/wrapped_forward_iterator.hh>
#include <paludis/util/sequence.hh>
+#include <paludis/util/output_deviator.hh>
#include <list>
using namespace paludis;
@@ -98,7 +99,11 @@ DefaultSyncer::sync(const SyncOptions & opts) const
.with_setenv("PALUDIS_EBUILD_DIR", getenv_with_default("PALUDIS_EBUILD_DIR", LIBEXECDIR "/paludis"))
.with_setenv("PALUDIS_SYNC_FILTER_FILE", stringify(opts.filter_file())));
- if (! opts.output_prefix().empty())
+ if (opts.output_deviant())
+ cmd
+ .with_captured_stderr_stream(opts.output_deviant()->stderr_stream())
+ .with_captured_stdout_stream(opts.output_deviant()->stdout_stream());
+ else if (! opts.output_prefix().empty())
cmd
.with_stdout_prefix(opts.output_prefix())
.with_stderr_prefix(opts.output_prefix())
diff --git a/paludis/syncer.hh b/paludis/syncer.hh
index e9360e9..2bf3a9c 100644
--- a/paludis/syncer.hh
+++ b/paludis/syncer.hh
@@ -43,6 +43,7 @@ namespace paludis
struct filter_file;
struct local;
struct options;
+ struct output_deviant;
struct output_prefix;
struct remote;
}
@@ -58,6 +59,13 @@ namespace paludis
{
NamedValue<n::filter_file, FSEntry> filter_file;
NamedValue<n::options, std::string> options;
+
+ /**
+ * May be a zero pointer.
+ * \since 0.32
+ */
+ NamedValue<n::output_deviant, std::tr1::shared_ptr<const OutputDeviant> > output_deviant;
+
NamedValue<n::output_prefix, std::string> output_prefix;
};
diff --git a/paludis/util/files.m4 b/paludis/util/files.m4
index 16e6366..4472511 100644
--- a/paludis/util/files.m4
+++ b/paludis/util/files.m4
@@ -44,6 +44,7 @@ add(`named_value', `hh', `cc', `fwd')
add(`no_type', `hh')
add(`operators', `hh')
add(`options', `hh', `fwd', `cc', `test')
+add(`output_deviator', `hh', `fwd', `cc')
add(`output_wrapper', `test', `testscript')
add(`pipe', `hh', `cc')
add(`pretty_print', `hh', `cc', `test')
diff --git a/paludis/util/output_deviator-fwd.hh b/paludis/util/output_deviator-fwd.hh
new file mode 100644
index 0000000..3819c35
--- /dev/null
+++ b/paludis/util/output_deviator-fwd.hh
@@ -0,0 +1,29 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2008 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
+ */
+
+#ifndef PALUDIS_GUARD_PALUDIS_UTIL_OUTPUT_DEVIATOR_FWD_HH
+#define PALUDIS_GUARD_PALUDIS_UTIL_OUTPUT_DEVIATOR_FWD_HH 1
+
+namespace paludis
+{
+ class OutputDeviator;
+ class OutputDeviant;
+}
+
+#endif
diff --git a/paludis/util/output_deviator.cc b/paludis/util/output_deviator.cc
new file mode 100644
index 0000000..3ba7d9f
--- /dev/null
+++ b/paludis/util/output_deviator.cc
@@ -0,0 +1,132 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2008 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/util/output_deviator.hh>
+#include <paludis/util/private_implementation_pattern-impl.hh>
+#include <paludis/util/fs_entry.hh>
+#include <paludis/util/make_shared_ptr.hh>
+#include <paludis/util/stringify.hh>
+#include <paludis/util/log.hh>
+#include <paludis/util/fd_output_stream.hh>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <ctime>
+#include <unistd.h>
+#include <iostream>
+
+namespace paludis
+{
+ template <>
+ struct Implementation<OutputDeviator>
+ {
+ const FSEntry log_dir;
+
+ Implementation(const FSEntry & l) :
+ log_dir(l)
+ {
+ }
+ };
+
+ template <>
+ struct Implementation<OutputDeviant>
+ {
+ const FSEntry file_name;
+ int descriptor;
+ std::tr1::shared_ptr<FDOutputStream> stream;
+
+ Implementation(const FSEntry & f) :
+ file_name(f),
+ descriptor(open(stringify(f).c_str(), O_CREAT | O_WRONLY, 0644))
+ {
+ if (-1 == descriptor)
+ Log::get_instance()->message("output_deviator.open_failed", ll_warning, lc_context) << "Cannot open '"
+ << f << + "' for write, sending output to stdout and stderr instead";
+ else
+ stream.reset(new FDOutputStream(descriptor));
+ }
+ };
+}
+
+OutputDeviator::OutputDeviator(const FSEntry & l) :
+ PrivateImplementationPattern<OutputDeviator>(new Implementation<OutputDeviator>(l))
+{
+}
+
+OutputDeviator::~OutputDeviator()
+{
+}
+
+const std::tr1::shared_ptr<OutputDeviant>
+OutputDeviator::make_output_deviant(const std::string & n)
+{
+ return make_shared_ptr(new OutputDeviant(_imp->log_dir / (n + "." + stringify(std::time(0)) + ".log")));
+}
+
+OutputDeviant::OutputDeviant(const FSEntry & f) :
+ PrivateImplementationPattern<OutputDeviant>(new Implementation<OutputDeviant>(f))
+{
+}
+
+OutputDeviant::~OutputDeviant()
+{
+ if (_imp->descriptor != 1)
+ {
+ if (0 != ::close(_imp->descriptor))
+ Log::get_instance()->message("output_deviant.close_failed", ll_warning, lc_context)
+ << "Cannot close '" << _imp->file_name << "'";
+ }
+}
+
+std::ostream *
+OutputDeviant::stdout_stream() const
+{
+ if (_imp->stream)
+ return _imp->stream.get();
+ else
+ return &std::cout;
+}
+
+std::ostream *
+OutputDeviant::stderr_stream() const
+{
+ if (_imp->stream)
+ return _imp->stream.get();
+ else
+ return &std::cerr;
+}
+
+void
+OutputDeviant::discard_log()
+{
+ if (-1 != _imp->descriptor)
+ if (-1 == ::unlink(stringify(_imp->file_name).c_str()))
+ Log::get_instance()->message("output_deviant.unlink_failed", ll_warning, lc_context)
+ << "Cannot unlink '" << _imp->file_name << "'";
+}
+
+const FSEntry
+OutputDeviant::log_file_name() const
+{
+ return _imp->file_name;
+}
+
+template class PrivateImplementationPattern<OutputDeviator>;
+template class PrivateImplementationPattern<OutputDeviant>;
+
diff --git a/paludis/util/output_deviator.hh b/paludis/util/output_deviator.hh
new file mode 100644
index 0000000..e1bb168
--- /dev/null
+++ b/paludis/util/output_deviator.hh
@@ -0,0 +1,66 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2008 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
+ */
+
+#ifndef PALUDIS_GUARD_PALUDIS_UTIL_OUTPUT_DEVIATOR_HH
+#define PALUDIS_GUARD_PALUDIS_UTIL_OUTPUT_DEVIATOR_HH 1
+
+#include <paludis/util/output_deviator-fwd.hh>
+#include <paludis/util/private_implementation_pattern.hh>
+#include <paludis/util/fs_entry-fwd.hh>
+#include <tr1/memory>
+
+using namespace paludis;
+
+namespace paludis
+{
+ class PALUDIS_VISIBLE OutputDeviator :
+ private PrivateImplementationPattern<OutputDeviator>
+ {
+ public:
+ OutputDeviator(const FSEntry & log_dir);
+ ~OutputDeviator();
+
+ const std::tr1::shared_ptr<OutputDeviant> make_output_deviant(const std::string &) PALUDIS_ATTRIBUTE((warn_unused_result));
+ };
+
+ class PALUDIS_VISIBLE OutputDeviant :
+ private PrivateImplementationPattern<OutputDeviant>
+ {
+ friend class OutputDeviator;
+
+ private:
+ OutputDeviant(const FSEntry &);
+
+ public:
+ ~OutputDeviant();
+
+ std::ostream * stdout_stream() const PALUDIS_ATTRIBUTE((warn_unused_result));
+ std::ostream * stderr_stream() const PALUDIS_ATTRIBUTE((warn_unused_result));
+
+ void discard_log();
+ const FSEntry log_file_name() const PALUDIS_ATTRIBUTE((warn_unused_result));
+ };
+
+#ifdef PALUDIS_HAVE_EXTERN_TEMPLATE
+ extern template class PrivateImplementationPattern<OutputDeviator>;
+ extern template class PrivateImplementationPattern<OutputDeviant>;
+#endif
+}
+
+#endif
diff --git a/src/clients/cave/Makefile.am b/src/clients/cave/Makefile.am
index df3a6dc..d0ae27f 100644
--- a/src/clients/cave/Makefile.am
+++ b/src/clients/cave/Makefile.am
@@ -21,7 +21,8 @@ command_MANS = \
cave-print-id-contents.1 \
cave-print-id-metadata.1 \
cave-print-ids.1 \
- cave-show.1
+ cave-show.1 \
+ cave-sync.1
man_MANS = \
cave.1 \
@@ -68,6 +69,7 @@ libcave_a_SOURCES = \
cmd_print_id_metadata.cc cmd_print_id_metadata.hh \
cmd_print_ids.cc cmd_print_ids.hh \
cmd_show.cc cmd_show.hh \
+ cmd_sync.cc cmd_sync.hh \
exceptions.cc exceptions.hh \
format_general.cc format_general.hh \
format_plain_contents_entry.cc format_plain_contents_entry.hh \
@@ -97,7 +99,6 @@ libcave_a_SOURCES = \
# cmd_regenerate_installable_cache.cc \
# cmd_regenerate_installed_cache.cc \
# cmd_report.cc \
-# cmd_sync.cc \
# cmd_uninstall.cc \
# cmd_uninstall_unused.cc
diff --git a/src/clients/cave/cmd_sync.cc b/src/clients/cave/cmd_sync.cc
new file mode 100644
index 0000000..22d4930
--- /dev/null
+++ b/src/clients/cave/cmd_sync.cc
@@ -0,0 +1,271 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2008 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 "cmd_sync.hh"
+#include "exceptions.hh"
+#include "formats.hh"
+#include "format_general.hh"
+#include <paludis/util/make_shared_ptr.hh>
+#include <paludis/package_database.hh>
+#include <paludis/util/action_queue.hh>
+#include <paludis/util/mutex.hh>
+#include <paludis/util/output_deviator.hh>
+#include <paludis/util/named_value.hh>
+#include <paludis/util/make_named_values.hh>
+#include <paludis/repository.hh>
+#include <paludis/environment.hh>
+#include <paludis/hook.hh>
+#include <paludis/syncer.hh>
+#include <cstdlib>
+#include <iostream>
+#include <map>
+#include <set>
+
+using namespace paludis;
+using namespace cave;
+using std::cout;
+using std::endl;
+
+namespace paludis
+{
+ namespace n
+ {
+ struct output_deviant;
+ struct success;
+ struct summary;
+ }
+}
+
+namespace
+{
+ struct SyncCommandLine :
+ args::ArgsHandler
+ {
+ virtual std::string app_name() const
+ {
+ return "cave sync";
+ }
+
+ virtual std::string app_synopsis() const
+ {
+ return "Sync all or specified repositories.";
+ }
+
+ virtual std::string app_description() const
+ {
+ return "Syncs repositories. If any repository names are specified, these repositories "
+ "are synced. Otherwise, all syncable repositories are synced.";
+ }
+
+ SyncCommandLine()
+ {
+ add_usage_line("[repository ...]");
+ }
+ };
+
+ struct Message
+ {
+ NamedValue<n::output_deviant, std::tr1::shared_ptr<OutputDeviant> > output_deviant;
+ NamedValue<n::success, bool> success;
+ NamedValue<n::summary, std::string> summary;
+ };
+
+ typedef std::map<std::string, Message> Messages;
+
+ void do_one_sync(const std::tr1::shared_ptr<Environment> & env, const RepositoryName & r, Mutex & mutex,
+ Messages & messages, int & retcode, int & np, int & na, int & nd,
+ OutputDeviator & output_deviator)
+ {
+ std::tr1::shared_ptr<OutputDeviant> output_deviant;
+
+ {
+ Lock lock(mutex);
+ output_deviant = output_deviator.make_output_deviant("sync-" + stringify(r));
+ }
+
+ bool done_decrement(false);
+
+ try
+ {
+ {
+ Lock lock(mutex);
+ ++na;
+ --np;
+ cout << format_general_spad(f::sync_repo_starting(), stringify(r), np, na, nd);
+ if (0 !=
+ env->perform_hook(Hook("sync_pre")
+ ("TARGET", stringify(r))
+ ("NUMBER_DONE", stringify(nd))
+ ("NUMBER_ACTIVE", stringify(na))
+ ("NUMBER_PENDING", stringify(np))
+ ).max_exit_status)
+ throw SyncFailedError("Sync of '" + stringify(r) + "' aborted by hook");
+ }
+
+ const std::tr1::shared_ptr<const Repository> repo(env->package_database()->fetch_repository(r));
+ if (! repo->syncable_interface())
+ throw BadRepositoryForCommand(r, "does not support syncing");
+
+ bool result(repo->syncable_interface()->sync(output_deviant));
+
+ {
+ Lock lock(mutex);
+ ++nd;
+ --na;
+ done_decrement = true;
+
+ if (0 !=
+ env->perform_hook(Hook("sync_post")
+ ("TARGET", stringify(r))
+ ("NUMBER_DONE", stringify(nd))
+ ("NUMBER_ACTIVE", stringify(na))
+ ("NUMBER_PENDING", stringify(np))
+ ).max_exit_status)
+ throw SyncFailedError("Sync of '" + stringify(r) + "' aborted by hook");
+ }
+
+ if (result)
+ {
+ Lock lock(mutex);
+ messages.insert(make_pair(stringify(r), make_named_values<Message>(
+ value_for<n::output_deviant>(output_deviant),
+ value_for<n::success>(true),
+ value_for<n::summary>("success")
+ )));
+ cout << format_general_spad(f::sync_repo_done_success(), stringify(r), np, na, nd);
+ }
+ else
+ {
+ Lock lock(mutex);
+ messages.insert(make_pair(stringify(r), make_named_values<Message>(
+ value_for<n::output_deviant>(output_deviant),
+ value_for<n::success>(true),
+ value_for<n::summary>("no syncing required")
+ )));
+ cout << format_general_spad(f::sync_repo_done_no_syncing_required(), stringify(r), np, na, nd);
+ }
+ }
+ catch (const Exception & e)
+ {
+ Lock lock(mutex);
+
+ if (! done_decrement)
+ {
+ --na;
+ ++nd;
+ done_decrement = true;
+ }
+
+ retcode |= 1;
+ messages.insert(make_pair(stringify(r), make_named_values<Message>(
+ value_for<n::output_deviant>(output_deviant),
+ value_for<n::success>(false),
+ value_for<n::summary>(e.message() + " (" + e.what() + ")")
+ )));
+
+ cout << format_general_spad(f::sync_repo_done_failure(), stringify(r), np, na, nd);
+
+ int PALUDIS_ATTRIBUTE((unused)) ignore(env->perform_hook(Hook("sync_fail")
+ ("TARGET", stringify(r))
+ ("NUMBER_DONE", stringify(nd))
+ ("NUMBER_ACTIVE", stringify(na))
+ ("NUMBER_PENDING", stringify(np))
+ ).max_exit_status);
+ }
+ }
+}
+
+int
+SyncCommand::run(
+ const std::tr1::shared_ptr<Environment> & env,
+ const std::tr1::shared_ptr<const Sequence<std::string > > & args
+ )
+{
+ SyncCommandLine cmdline;
+ cmdline.run(args, "CAVE", "CAVE_SYNC_OPTIONS", "CAVE_SYNC_CMDLINE");
+
+ int retcode(0);
+ Messages messages;
+
+ OutputDeviator output_deviator(FSEntry("/tmp"));
+
+ std::set<RepositoryName, RepositoryNameComparator> repos;
+ if (cmdline.begin_parameters() != cmdline.end_parameters())
+ for (SyncCommandLine::ParametersConstIterator p(cmdline.begin_parameters()), p_end(cmdline.end_parameters()) ;
+ p != p_end ; ++p)
+ repos.insert(RepositoryName(*p));
+ else
+ for (PackageDatabase::RepositoryConstIterator p(env->package_database()->begin_repositories()),
+ p_end(env->package_database()->end_repositories()) ;
+ p != p_end ; ++p)
+ if ((*p)->syncable_interface())
+ repos.insert((*p)->name());
+
+ cout << format_general_s(f::sync_heading(), "Starting sync");
+
+ if (0 != env->perform_hook(Hook("sync_all_pre")
+ ("TARGETS", join(repos.begin(), repos.end(), " ")
+ )).max_exit_status)
+ throw SyncFailedError("Sync aborted by hook");
+
+ cout << format_general_s(f::sync_repos_title(), "");
+
+ {
+ Mutex mutex;
+ ActionQueue actions(5);
+ int active(0), done(0), pending(repos.size());
+ for (std::set<RepositoryName, RepositoryNameComparator>::const_iterator r(repos.begin()), r_end(repos.end()) ;
+ r != r_end ; ++r)
+ actions.enqueue(std::tr1::bind(&do_one_sync, env, *r, std::tr1::ref(mutex),
+ std::tr1::ref(messages), std::tr1::ref(retcode), std::tr1::ref(pending), std::tr1::ref(active), std::tr1::ref(done),
+ std::tr1::ref(output_deviator)));
+ }
+
+ if (0 != env->perform_hook(Hook("sync_all_post")
+ ("TARGETS", join(repos.begin(), repos.end(), " ")
+ )).max_exit_status)
+ throw SyncFailedError("Sync aborted by hook");
+
+ cout << endl << format_general_s(f::sync_heading(), "Sync results");
+
+ for (Messages::const_iterator m(messages.begin()), m_end(messages.end()) ;
+ m != m_end ; ++m)
+ {
+ if (m->second.success())
+ {
+ cout << format_general_kv(f::sync_message_success(), m->first, m->second.summary());
+ m->second.output_deviant()->discard_log();
+ }
+ else
+ {
+ cout << format_general_kv(f::sync_message_failure(), m->first, m->second.summary());
+ cout << format_general_kv(f::sync_message_failure_message(), "Log file", stringify(m->second.output_deviant()->log_file_name()));
+ }
+ }
+ cout << endl;
+
+ return retcode;
+}
+
+std::tr1::shared_ptr<args::ArgsHandler>
+SyncCommand::make_doc_cmdline()
+{
+ return make_shared_ptr(new SyncCommandLine);
+}
+
diff --git a/src/clients/cave/cmd_sync.hh b/src/clients/cave/cmd_sync.hh
new file mode 100644
index 0000000..f589068
--- /dev/null
+++ b/src/clients/cave/cmd_sync.hh
@@ -0,0 +1,44 @@
+/* vim: set sw=4 sts=4 et foldmethod=syntax : */
+
+/*
+ * Copyright (c) 2008 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
+ */
+
+#ifndef PALUDIS_GUARD_SRC_CLIENTS_CAVE_CMD_SYNC_HH
+#define PALUDIS_GUARD_SRC_CLIENTS_CAVE_CMD_SYNC_HH 1
+
+#include "command.hh"
+
+namespace paludis
+{
+ namespace cave
+ {
+ class PALUDIS_VISIBLE SyncCommand :
+ public Command
+ {
+ public:
+ int run(
+ const std::tr1::shared_ptr<Environment> &,
+ const std::tr1::shared_ptr<const Sequence<std::string > > & args
+ );
+
+ std::tr1::shared_ptr<args::ArgsHandler> make_doc_cmdline();
+ };
+ }
+}
+
+
+#endif
diff --git a/src/clients/cave/command_factory.cc b/src/clients/cave/command_factory.cc
index 0afa094..a71134e 100644
--- a/src/clients/cave/command_factory.cc
+++ b/src/clients/cave/command_factory.cc
@@ -33,6 +33,7 @@
#include "cmd_print_id_metadata.hh"
#include "cmd_print_ids.hh"
#include "cmd_show.hh"
+#include "cmd_sync.hh"
using namespace paludis;
using namespace cave;
@@ -67,6 +68,7 @@ CommandFactory::CommandFactory() :
_imp->handlers.insert(std::make_pair("print-id-metadata", make_command<PrintIDMetadataCommand>));
_imp->handlers.insert(std::make_pair("print-ids", make_command<PrintIDsCommand>));
_imp->handlers.insert(std::make_pair("show", make_command<ShowCommand>));
+ _imp->handlers.insert(std::make_pair("sync", make_command<SyncCommand>));
}
CommandFactory::~CommandFactory()
diff --git a/src/clients/cave/exceptions.cc b/src/clients/cave/exceptions.cc
index fa8f291..43a622d 100644
--- a/src/clients/cave/exceptions.cc
+++ b/src/clients/cave/exceptions.cc
@@ -46,3 +46,8 @@ BadIDForCommand::BadIDForCommand(const PackageDepSpec & spec, const std::tr1::sh
{
}
+BadRepositoryForCommand::BadRepositoryForCommand(const RepositoryName & name, const std::string & r) throw () :
+ Exception("Repository '" + stringify(name) + "' unsuitable: " + r)
+{
+}
+
diff --git a/src/clients/cave/exceptions.hh b/src/clients/cave/exceptions.hh
index d714ed0..b157b07 100644
--- a/src/clients/cave/exceptions.hh
+++ b/src/clients/cave/exceptions.hh
@@ -23,6 +23,7 @@
#include <paludis/util/exception.hh>
#include <paludis/dep_spec-fwd.hh>
#include <paludis/package_id-fwd.hh>
+#include <paludis/name-fwd.hh>
#include <tr1/memory>
namespace paludis
@@ -52,6 +53,15 @@ namespace paludis
const std::tr1::shared_ptr<const PackageID> &,
const std::string & r) throw ();
};
+
+ class PALUDIS_VISIBLE BadRepositoryForCommand :
+ public Exception
+ {
+ public:
+ BadRepositoryForCommand(
+ const RepositoryName &,
+ const std::string & r) throw ();
+ };
}
}
diff --git a/src/clients/cave/format_general.cc b/src/clients/cave/format_general.cc
index 272c219..d93c6bf 100644
--- a/src/clients/cave/format_general.cc
+++ b/src/clients/cave/format_general.cc
@@ -20,6 +20,7 @@
#include "format_general.hh"
#include "format_string.hh"
#include <paludis/util/map.hh>
+#include <paludis/util/stringify.hh>
using namespace paludis;
using namespace cave;
@@ -33,6 +34,15 @@ paludis::cave::format_general_s(const std::string & f, const std::string & s)
}
std::string
+paludis::cave::format_general_kv(const std::string & f, const std::string & k, const std::string & v)
+{
+ std::tr1::shared_ptr<Map<char, std::string> > m(new Map<char, std::string>);
+ m->insert('k', k);
+ m->insert('v', v);
+ return format_string(f, m);
+}
+
+std::string
paludis::cave::format_general_sr(const std::string & f, const std::string & s, const std::string & r)
{
std::tr1::shared_ptr<Map<char, std::string> > m(new Map<char, std::string>);
@@ -42,6 +52,17 @@ paludis::cave::format_general_sr(const std::string & f, const std::string & s, c
}
std::string
+paludis::cave::format_general_spad(const std::string & f, const std::string & s, const int p, const int a, const int d)
+{
+ std::tr1::shared_ptr<Map<char, std::string> > m(new Map<char, std::string>);
+ m->insert('s', s);
+ m->insert('p', stringify(p));
+ m->insert('a', stringify(a));
+ m->insert('d', stringify(d));
+ return format_string(f, m);
+}
+
+std::string
paludis::cave::format_general_rhvib(const std::string & f, const std::string & r, const std::string & h,
const std::string & v, const int i, const bool b)
{
diff --git a/src/clients/cave/format_general.hh b/src/clients/cave/format_general.hh
index 7e3853b..7b7f6f5 100644
--- a/src/clients/cave/format_general.hh
+++ b/src/clients/cave/format_general.hh
@@ -33,10 +33,17 @@ namespace paludis
std::string format_general_sr(const std::string & f, const std::string & s, const std::string & r)
PALUDIS_VISIBLE PALUDIS_ATTRIBUTE((warn_unused_result));
+ std::string format_general_kv(const std::string & f, const std::string & k, const std::string & v)
+ PALUDIS_VISIBLE PALUDIS_ATTRIBUTE((warn_unused_result));
+
std::string format_general_rhvib(const std::string & f, const std::string & r,
const std::string & h, const std::string & v, const int i, const bool b)
PALUDIS_VISIBLE PALUDIS_ATTRIBUTE((warn_unused_result));
+ std::string format_general_spad(const std::string & f, const std::string & s,
+ const int p, const int a, const int d)
+ PALUDIS_VISIBLE PALUDIS_ATTRIBUTE((warn_unused_result));
+
std::string format_general_i(const std::string & f, const int i)
PALUDIS_VISIBLE PALUDIS_ATTRIBUTE((warn_unused_result));
}
diff --git a/src/clients/cave/formats.cc b/src/clients/cave/formats.cc
index a41aa52..b20114d 100644
--- a/src/clients/cave/formats.cc
+++ b/src/clients/cave/formats.cc
@@ -394,3 +394,57 @@ paludis::cave::f::colour_formatter_indent()
return "%{column 30}%i%i%i%i";
}
+const std::string
+paludis::cave::f::sync_heading()
+{
+ return c::bold_blue() + "%s" + c::normal() + "\\n\\n";
+}
+
+const std::string
+paludis::cave::f::sync_message_success()
+{
+ return "* " + c::bold_green() + "%k:" + c::normal() + "%{column 30}%v\\n";
+}
+
+const std::string
+paludis::cave::f::sync_message_failure()
+{
+ return "* " + c::bold_red() + "%k:" + c::normal() + "%{column 30}%v\\n";
+}
+
+const std::string
+paludis::cave::f::sync_message_failure_message()
+{
+ return " %k:%{column 30}%v\\n";
+}
+
+const std::string
+paludis::cave::f::sync_repos_title()
+{
+ return c::bold_normal() + "Repository%{column 30}Status%{column 52}Pending%{column 60}Active%{column 68}Done" + c::normal() + "\\n";
+}
+
+const std::string
+paludis::cave::f::sync_repo_starting()
+{
+ return "-> " + c::bold_blue() + "%s" + c::normal() + "%{column 30}starting%{column 52}%p%{column 60}%a%{column 68}%d\\n";
+}
+
+const std::string
+paludis::cave::f::sync_repo_done_success()
+{
+ return "-> " + c::bold_green() + "%s" + c::normal() + "%{column 30}success%{column 52}%p%{column 60}%a%{column 68}%d\\n";
+}
+
+const std::string
+paludis::cave::f::sync_repo_done_no_syncing_required()
+{
+ return "-> " + c::bold_green() + "%s" + c::normal() + "%{column 30}no syncing required%{column 52}%p%{column 60}%a%{column 68}%d\\n";
+}
+
+const std::string
+paludis::cave::f::sync_repo_done_failure()
+{
+ return "-> " + c::bold_red() + "%s" + c::normal() + "%{column 30}failed%{column 52}%p%{column 60}%a%{column 68}%d\\n";
+}
+
diff --git a/src/clients/cave/formats.hh b/src/clients/cave/formats.hh
index 505bd46..a3a26cd 100644
--- a/src/clients/cave/formats.hh
+++ b/src/clients/cave/formats.hh
@@ -115,6 +115,16 @@ namespace paludis
const std::string colour_formatter_named_set_dep_spec_plain();
const std::string colour_formatter_indent();
+
+ const std::string sync_heading();
+ const std::string sync_message_success();
+ const std::string sync_message_failure();
+ const std::string sync_message_failure_message();
+ const std::string sync_repos_title();
+ const std::string sync_repo_starting();
+ const std::string sync_repo_done_success();
+ const std::string sync_repo_done_no_syncing_required();
+ const std::string sync_repo_done_failure();
}
}
}