parallel downloads with librepo

This commit is contained in:
Wolf Vollprecht 2020-04-06 18:55:18 +02:00
parent 72fbadb3ef
commit 78589fbfae
19 changed files with 2432 additions and 38 deletions

View File

@ -24,7 +24,7 @@ install:
- conda update -q conda
# Useful for debugging any issues with conda
- conda info -a
- conda install libsolv gxx_linux-64 pybind11 pip -c conda-forge
- conda install libsolv gxx_linux-64 pybind11 pip libarchive librepo -c conda-forge
- source activate root
- pip install -e .
script:

57
include/context.hpp Normal file
View File

@ -0,0 +1,57 @@
#pragma once
#include <vector>
#include <string>
#include <memory>
// Context singleton class
class Context
{
public:
// TODO $CONDA_PREFIX doesn't work.
std::vector<std::string> pkgs_dirs = {"$CONDA_PREFIX/pkgs"};
bool use_index_cache = false;
std::size_t local_repodata_ttl = 1; // take from header
bool offline = false;
int verbosity = 4;
void set_verbosity(int lvl)
{
if (lvl == 0)
{
minilog::global_log_severity = 3;
}
else if (lvl == 1)
{
minilog::global_log_severity = 1;
}
else
{
minilog::global_log_severity = 0;
}
this->verbosity = lvl;
}
Context(Context const&) = delete;
Context& operator=(Context const&) = delete;
Context& operator()()
{
return instance();
}
static Context& instance()
{
static Context ctx;
return ctx;
}
private:
Context() {
set_verbosity(0);
}
};

View File

@ -1,6 +1,7 @@
#pragma once
#include "thirdparty/minilog.hpp"
#include "context.hpp"
extern "C"
{
@ -16,6 +17,7 @@ namespace mamba
{
m_pool = pool_create();
pool_setdisttype(m_pool, DISTTYPE_CONDA);
set_debuglevel();
}
~MPool()
@ -24,21 +26,9 @@ namespace mamba
pool_free(m_pool);
}
void set_debuglevel(int lvl)
void set_debuglevel()
{
if (lvl == 0)
{
minilog::global_log_severity = 3;
}
else if (lvl == 1)
{
minilog::global_log_severity = 1;
}
else
{
minilog::global_log_severity = 0;
}
pool_setdebuglevel(m_pool, lvl);
pool_setdebuglevel(m_pool, Context::instance().verbosity);
}
void create_whatprovides()

View File

@ -6,6 +6,8 @@
#include "transaction.hpp"
#include "repo.hpp"
#include "query.hpp"
#include "subdirdata.hpp"
#include "context.hpp"
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
@ -51,6 +53,29 @@ PYBIND11_MODULE(mamba_api, m) {
.def(py::init<MPool&>())
.def("find", &Query::find)
.def("whatrequires", &Query::whatrequires)
py::class_<Handle>(m, "DownloadHandle")
.def(py::init<>())
;
py::class_<MSubdirData>(m, "SubdirData")
.def(py::init<const std::string&, const std::string&, const std::string&>())
.def("load", &MSubdirData::load)
.def("loaded", &MSubdirData::loaded)
;
py::class_<DownloadTargetList>(m, "DownloadTargetList")
.def(py::init<>())
.def("append", &DownloadTargetList::append)
.def("download", &DownloadTargetList::download)
;
py::class_<Context, std::unique_ptr<Context, py::nodelete>>(m, "Context")
.def(py::init([]() {
return std::unique_ptr<Context, py::nodelete>(&Context::instance());
}))
.def_readwrite("verbosity", &Context::verbosity)
;
m.attr("SOLVER_SOLVABLE") = SOLVER_SOLVABLE;

499
include/subdirdata.hpp Normal file
View File

@ -0,0 +1,499 @@
#include <string>
#include <filesystem>
#include <regex>
#include "nlohmann/json.hpp"
#include "thirdparty/indicators/dynamic_progress.hpp"
#include "thirdparty/indicators/progress_bar.hpp"
#include "context.hpp"
#include "util.hpp"
extern "C" {
#include <glib.h>
#include <stdlib.h>
#include <stdio.h>
#include <librepo/librepo.h>
#include <archive.h>
}
void to_human_readable_filesize(std::ostream& o, double bytes)
{
const char* sizes[] = { " B", "KB", "MB", "GB", "TB" };
int order = 0;
while (bytes >= 1024 && order < 5 - 1) {
order++;
bytes = bytes / 1024;
}
o << int(bytes) << sizes[order];
}
namespace fs = std::filesystem;
namespace decompress
{
bool raw(const std::string& in, const std::string& out)
{
int r;
ssize_t size;
struct archive *a = archive_read_new();
archive_read_support_filter_bzip2(a);
archive_read_support_format_raw(a);
// TODO figure out good value for this
const std::size_t BLOCKSIZE = 16384;
r = archive_read_open_filename(a, in.c_str(), BLOCKSIZE);
if (r != ARCHIVE_OK) {
return false;
}
struct archive_entry *entry;
std::ofstream out_file(out);
char buff[BLOCKSIZE];
std::size_t buffsize = BLOCKSIZE;
r = archive_read_next_header(a, &entry);
if (r != ARCHIVE_OK) {
return false;
}
while (true)
{
size = archive_read_data(a, &buff, buffsize);
if (size < 0) {
/* ERROR */
}
if (size == 0)
{
break;
}
out_file.write(buff, size);
}
archive_read_free(a);
return true;
}
}
namespace mamba
{
class Handle
{
public:
Handle() {
m_handle = lr_handle_init();
}
~Handle() {
lr_handle_free(m_handle);
}
operator LrHandle*() {
return m_handle;
}
// Result perform() {
// LrResult* result = lr_result_init();
// GError* tmp_err = NULL;
// std::cout << "Downloading! " << std::endl;
// lr_handle_perform(m_handle, result, &tmp_err);
// // g_error_free(tmp_err);
// char *destdir;
// lr_handle_getinfo(m_handle, NULL, LRI_DESTDIR, &destdir);
// if (result) {
// printf("Download successful (Destination dir: %s)\n", destdir);
// } else {
// fprintf(stderr, "Error encountered: %s\n", tmp_err->message);
// g_error_free(tmp_err);
// // rc = EXIT_FAILURE;
// }
// return result;
// }
LrHandle* m_handle;
};
class MSubdirData
{
public:
MSubdirData(const std::string& name, const std::string& url, const std::string& repodata_fn) :
m_name(name),
m_url(url),
m_repodata_fn(repodata_fn),
m_loaded(false),
m_download_complete(false),
m_target(nullptr)
{
}
ptrdiff_t check_cache()
{
try {
auto last_write = fs::last_write_time(m_repodata_fn);
auto cftime = decltype(last_write)::clock::now();
auto tdiff = cftime - last_write;
auto as_seconds = std::chrono::duration_cast<std::chrono::seconds>(tdiff);
return as_seconds.count();
}
catch(...)
{
// could not open the file...
return -1;
}
}
bool loaded()
{
return m_loaded;
}
bool forbid_cache()
{
return starts_with(m_url, "file://");
}
bool load(/*Handle& handle*/)
{
ptrdiff_t cache_age = check_cache();
nlohmann::json mod_etag_headers;
if (check_cache() != -1)
{
LOG(INFO) << "Found valid cache file.";
mod_etag_headers = read_mod_and_etag();
if (mod_etag_headers.size() != 0) {
std::cout << mod_etag_headers << std::endl;
int max_age = 0;
if (Context::instance().local_repodata_ttl > 1)
{
max_age = Context::instance().local_repodata_ttl;
}
else if (Context::instance().local_repodata_ttl == 1)
{
// TODO error handling if _cache_control key does not exist!
auto el = mod_etag_headers.value("_cache_control", std::string(""));
max_age = get_cache_control_max_age(el);
}
if ((max_age > cache_age || Context::instance().offline) && !forbid_cache())
{
// cache valid!
LOG(INFO) << "Using cache " << m_url << " age in seconds: " << cache_age << " / " << max_age;
m_loaded = true;
return true;
}
}
else
{
LOG(WARNING) << "Could not determine mod / etag headers.";
}
}
else
{
// now we need to download the file
LOG(INFO) << "Local cache timed out for " << m_url;
create_target(mod_etag_headers); //, handle);
}
// raw_repodata_str = fetch_repodata_remote_request(self.url_w_credentials,
// mod_etag_headers.get('_etag'),
// mod_etag_headers.get('_mod'),
// repodata_fn=self.repodata_fn)
// if not raw_repodata_str and self.repodata_fn != REPODATA_FN:
// raise UnavailableInvalidChannel(self.url_w_repodata_fn, 404)
return true;
}
LrDownloadTarget* target() const
{
return m_target;
}
const std::string& name() const
{
return m_name;
}
void set_progress_bar(std::size_t idx, indicators::DynamicProgress<indicators::ProgressBar>* ptr)
{
m_multi_progress_idx = idx;
p_multi_progress = ptr;
}
private:
std::string decompress()
{
LOG(INFO) << "Decompressing metadata";
auto json_temp_name = std::tmpnam(nullptr);
bool result = decompress::raw(m_temp_name, json_temp_name);
if (!result)
{
LOG(WARNING) << "Could not decompress " << m_temp_name;
}
return json_temp_name;
}
static int finalize_transfer(void* self, LrTransferStatus status, const char* msg)
{
auto* s = (MSubdirData*)self;
auto& mp = (*(s->p_multi_progress));
LOG(INFO) << "Finalized transfer: " << s->m_url;
nlohmann::json prepend_header;
// saved_fields = {'_url': url}
// add_http_value_to_dict(resp, 'Etag', saved_fields, '_etag')
// add_http_value_to_dict(resp, 'Last-Modified', saved_fields, '_mod')
// add_http_value_to_dict(resp, 'Cache-Control', saved_fields, '_cache_control')
prepend_header["_url"] = s->m_url;
LOG(INFO) << "Opening: " << s->m_repodata_fn;
std::ofstream final_file(s->m_repodata_fn);
// TODO make sure that cache directory exists!
if (!final_file.is_open())
{
throw std::runtime_error("Could not open file.");
}
if (ends_with(s->m_url, ".bz2"))
{
mp[s->m_multi_progress_idx].set_option(indicators::option::PostfixText{"Decomp..."});
s->m_temp_name = s->decompress();
}
mp[s->m_multi_progress_idx].set_option(indicators::option::PostfixText{"Finalizing..."});
std::ifstream temp_file(s->m_temp_name);
std::stringstream temp_json;
temp_json << prepend_header.dump();
// replace `}` with `,`
temp_json.seekp(-1, temp_json.cur); temp_json << ',';
final_file << temp_json.str();
temp_file.seekg(1);
std::copy(
std::istreambuf_iterator<char>(temp_file),
std::istreambuf_iterator<char>(),
std::ostreambuf_iterator<char>(final_file)
);
mp[s->m_multi_progress_idx].set_option(indicators::option::PostfixText{"Done"});
mp[s->m_multi_progress_idx].set_progress(100);
mp[s->m_multi_progress_idx].mark_as_completed();
return 0;
}
static int progress_callback(void *self, double total_to_download, double now_downloaded)
{
auto* s = (MSubdirData*)self;
auto& mp = (*(s->p_multi_progress));
if (!s->m_download_complete && total_to_download != 0)
{
if (total_to_download != 0)
{
std::stringstream postfix;
to_human_readable_filesize(postfix, now_downloaded);
postfix << " / ";
to_human_readable_filesize(postfix, total_to_download);
mp[s->m_multi_progress_idx].set_option(indicators::option::PostfixText{postfix.str()});
mp[s->m_multi_progress_idx].set_progress((now_downloaded / total_to_download) * 100);
}
if (int(now_downloaded / total_to_download) * 100 >= 100)
{
// mp[s->m_multi_progress_idx].mark_as_completed();
s->m_download_complete = true;
}
}
return 0;
}
void create_target(nlohmann::json& mod_etag /*, Handle& handle*/)
{
m_temp_name = std::tmpnam(nullptr);
m_target = lr_downloadtarget_new(
// handle, // ptr to handle
nullptr, // ptr to handle
m_url.c_str(), // url (absolute, or relative)
nullptr, // base url
-1, // file descriptor (opened)
m_temp_name.c_str(), // filename
nullptr, // possible checksums
0, // expected size
true, // resume
&MSubdirData::progress_callback,// progress cb
this, // cb data
// reinterpret_cast<int(void*, LrTransferStatus, const char*)>(&MSubdirData::finalize_transfer), // end cb
&MSubdirData::finalize_transfer, // end cb
nullptr, // mirror failure cb
nullptr, // user data
0, // byte range start (download only range)
0, // byte range end
nullptr, // range string (overrides start / end, usage unclear)
false, // no_cache (tell proxy server that we want fresh data)
false // is zchunk
);
// auto to_header = [](const std::string& key, const std::string& value) {
// return std::string(key + ": " + value);
// };
// struct curl_slist *headers = nullptr;
// if (mod_etag.find("_etag") != mod_etag.end())
// {
// headers = curl_slist_append(headers, to_header("If-None-Match", mod_etag["_etag"]).c_str());
// }
// if (mod_etag.find("_mod") != mod_etag.end())
// {
// headers = curl_slist_append(headers, to_header("If-Modified-Since", mod_etag["_mod"]).c_str());
// }
// headers = curl_slist_append(headers, "Accept-Encoding: gzip, deflate, compress, identity");
// headers = curl_slist_append(headers, "Content-Type: application/json");
// m_target->curl_rqheaders = headers;
}
std::size_t get_cache_control_max_age(const std::string& val)
{
std::regex max_age_re("max-age=(\\d+)");
std::smatch max_age_match;
bool matches = std::regex_search(val, max_age_match, max_age_re);
if (!matches) return 0;
return std::stoi(max_age_match[1]);
}
nlohmann::json read_mod_and_etag()
{
// parse json at the beginning of the stream such as
// {"_url": "https://conda.anaconda.org/conda-forge/linux-64", "_etag": "W/\"6092e6a2b6cec6ea5aade4e177c3edda-8\"", "_mod": "Sat, 04 Apr 2020 03:29:49 GMT", "_cache_control": "public, max-age=1200",
auto extract_subjson = [](std::ifstream& s)
{
char next;
std::string result;
bool escaped = false;
int i = 0, N = 4;
while (s.get(next)) {
if (next == '"')
{
if (!escaped)
{
i++;
}
else
{
escaped = false;
}
// 4 keys == 4 ticks
if (i == 4 * N)
{
return result + "\"}";
}
}
else if (next == '\\')
{
escaped = true;
}
result.push_back(next);
}
return std::string();
};
std::ifstream in_file(m_repodata_fn);
auto json = extract_subjson(in_file);
nlohmann::json result;
try {
result = nlohmann::json::parse(json);
return result;
}
catch(...)
{
LOG(WARNING) << "Could not parse mod / etag header!";
return nlohmann::json();
}
}
LrDownloadTarget* m_target;
indicators::DynamicProgress<indicators::ProgressBar>* p_multi_progress;
std::size_t m_multi_progress_idx;
bool m_loaded, m_download_complete;
std::string m_url;
std::string m_name;
std::string m_repodata_fn;
std::string m_temp_name;
};
class DownloadTargetList
{
public:
DownloadTargetList()
{
}
void append(MSubdirData& subdirdata)
{
const std::size_t PREFIX_LENGTH = 25;
if (subdirdata.target() != nullptr)
{
std::string prefix = subdirdata.name();
if (prefix.size() < (PREFIX_LENGTH - 1)) {
prefix += std::string(PREFIX_LENGTH - prefix.size(), ' ');
}
else if (prefix.size() > (PREFIX_LENGTH - 1))
{
prefix = prefix.substr(0, (PREFIX_LENGTH - 1)) + std::string(" ");
}
m_progress_bars.push_back(std::make_unique<indicators::ProgressBar>(
indicators::option::BarWidth{25},
indicators::option::ForegroundColor{indicators::Color::unspecified},
indicators::option::ShowElapsedTime{true},
indicators::option::ShowRemainingTime{false},
indicators::option::PrefixText(prefix)
));
auto idx = m_multi_progress.push_back(*m_progress_bars[m_progress_bars.size() - 1].get());
subdirdata.set_progress_bar(idx, &m_multi_progress);
m_download_targets = g_slist_append(m_download_targets, subdirdata.target());
}
}
bool download(bool failfast)
{
std::cout << "Downloading metadata\n\n";
m_multi_progress.set_option(indicators::option::HideBarWhenComplete{false});
for (auto& bar : m_progress_bars)
{
bar->set_progress(0);
}
LOG(INFO) << "Starting to download targets";
if (m_download_targets == nullptr)
{
LOG(WARNING) << "Nothing to download";
return true;
}
GError* tmp_err = NULL;
bool result = lr_download(m_download_targets, failfast, &tmp_err);
if (result) {
LOG(INFO) << "All downloads successful";
} else {
LOG(ERROR) << "Error encountered: " << tmp_err->message;
g_error_free(tmp_err);
}
return result;
}
std::vector<std::unique_ptr<indicators::ProgressBar>> m_progress_bars;
indicators::DynamicProgress<indicators::ProgressBar> m_multi_progress;
GSList* m_download_targets = nullptr;
};
}

View File

@ -0,0 +1,240 @@
/*
Activity Indicators for Modern C++
https://github.com/p-ranav/indicators
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
SPDX-License-Identifier: MIT
Copyright (c) 2019 Pranav Srinivas Kumar <pranav.srinivas.kumar@gmail.com>.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#pragma once
#include <indicators/color.hpp>
#include <indicators/details/stream_helper.hpp>
#include <algorithm>
#include <atomic>
#include <chrono>
#include <indicators/setting.hpp>
#include <iomanip>
#include <iostream>
#include <mutex>
#include <string>
#include <thread>
#include <tuple>
namespace indicators {
class BlockProgressBar {
using Settings = std::tuple<option::ForegroundColor, option::BarWidth, option::Start, option::End,
option::PrefixText, option::PostfixText, option::ShowPercentage,
option::ShowElapsedTime, option::ShowRemainingTime, option::Completed,
option::SavedStartTime, option::MaxPostfixTextLen>;
public:
template <typename... Args,
typename std::enable_if<details::are_settings_from_tuple<
Settings, typename std::decay<Args>::type...>::value,
void *>::type = nullptr>
explicit BlockProgressBar(Args &&... args)
: settings_(details::get<details::ProgressBarOption::foreground_color>(
option::ForegroundColor{Color::white}, std::forward<Args>(args)...),
details::get<details::ProgressBarOption::bar_width>(option::BarWidth{100},
std::forward<Args>(args)...),
details::get<details::ProgressBarOption::start>(option::Start{"["},
std::forward<Args>(args)...),
details::get<details::ProgressBarOption::end>(option::End{"]"},
std::forward<Args>(args)...),
details::get<details::ProgressBarOption::prefix_text>(
option::PrefixText{""}, std::forward<Args>(args)...),
details::get<details::ProgressBarOption::postfix_text>(
option::PostfixText{""}, std::forward<Args>(args)...),
details::get<details::ProgressBarOption::show_percentage>(
option::ShowPercentage{true}, std::forward<Args>(args)...),
details::get<details::ProgressBarOption::show_elapsed_time>(
option::ShowElapsedTime{false}, std::forward<Args>(args)...),
details::get<details::ProgressBarOption::show_remaining_time>(
option::ShowRemainingTime{false}, std::forward<Args>(args)...),
details::get<details::ProgressBarOption::completed>(option::Completed{false},
std::forward<Args>(args)...),
details::get<details::ProgressBarOption::saved_start_time>(
option::SavedStartTime{false}, std::forward<Args>(args)...),
details::get<details::ProgressBarOption::max_postfix_text_len>(
option::MaxPostfixTextLen{0}, std::forward<Args>(args)...)) {}
template <typename T, details::ProgressBarOption id>
void set_option(details::Setting<T, id> &&setting) {
static_assert(!std::is_same<T, typename std::decay<decltype(details::get_value<id>(
std::declval<Settings>()))>::type>::value,
"Setting has wrong type!");
std::lock_guard<std::mutex> lock(mutex_);
get_value<id>() = std::move(setting).value;
}
template <typename T, details::ProgressBarOption id>
void set_option(const details::Setting<T, id> &setting) {
static_assert(!std::is_same<T, typename std::decay<decltype(details::get_value<id>(
std::declval<Settings>()))>::type>::value,
"Setting has wrong type!");
std::lock_guard<std::mutex> lock(mutex_);
get_value<id>() = setting.value;
}
void set_option(
const details::Setting<std::string, details::ProgressBarOption::postfix_text> &setting) {
std::lock_guard<std::mutex> lock(mutex_);
get_value<details::ProgressBarOption::postfix_text>() = setting.value;
if (setting.value.length() > get_value<details::ProgressBarOption::max_postfix_text_len>()) {
get_value<details::ProgressBarOption::max_postfix_text_len>() = setting.value.length();
}
}
void
set_option(details::Setting<std::string, details::ProgressBarOption::postfix_text> &&setting) {
std::lock_guard<std::mutex> lock(mutex_);
get_value<details::ProgressBarOption::postfix_text>() = std::move(setting).value;
auto &new_value = get_value<details::ProgressBarOption::postfix_text>();
if (new_value.length() > get_value<details::ProgressBarOption::max_postfix_text_len>()) {
get_value<details::ProgressBarOption::max_postfix_text_len>() = new_value.length();
}
}
void set_progress(float value) {
{
std::lock_guard<std::mutex> lock{mutex_};
progress_ = value;
}
save_start_time();
print_progress();
}
void tick() {
{
std::lock_guard<std::mutex> lock{mutex_};
progress_ += 1;
}
save_start_time();
print_progress();
}
size_t current() {
std::lock_guard<std::mutex> lock{mutex_};
return std::min(static_cast<size_t>(progress_), size_t(100));
}
bool is_completed() const { return get_value<details::ProgressBarOption::completed>(); }
void mark_as_completed() {
get_value<details::ProgressBarOption::completed>() = true;
print_progress();
}
private:
template <details::ProgressBarOption id>
auto get_value() -> decltype((details::get_value<id>(std::declval<Settings &>()).value)) {
return details::get_value<id>(settings_).value;
}
template <details::ProgressBarOption id>
auto get_value() const
-> decltype((details::get_value<id>(std::declval<const Settings &>()).value)) {
return details::get_value<id>(settings_).value;
}
Settings settings_;
float progress_{0.0};
std::chrono::time_point<std::chrono::high_resolution_clock> start_time_point_;
std::mutex mutex_;
template <typename Indicator, size_t count> friend class MultiProgress;
template <typename Indicator> friend class DynamicProgress;
std::atomic<bool> multi_progress_mode_{false};
void save_start_time() {
auto &show_elapsed_time = get_value<details::ProgressBarOption::show_elapsed_time>();
auto &saved_start_time = get_value<details::ProgressBarOption::saved_start_time>();
auto &show_remaining_time = get_value<details::ProgressBarOption::show_remaining_time>();
if ((show_elapsed_time || show_remaining_time) && !saved_start_time) {
start_time_point_ = std::chrono::high_resolution_clock::now();
saved_start_time = true;
}
}
void print_progress(bool from_multi_progress = false) {
if (multi_progress_mode_ && !from_multi_progress) {
if (progress_ > 100.0) {
get_value<details::ProgressBarOption::completed>() = true;
}
return;
}
std::lock_guard<std::mutex> lock{mutex_};
auto now = std::chrono::high_resolution_clock::now();
auto elapsed = std::chrono::duration_cast<std::chrono::nanoseconds>(now - start_time_point_);
std::cout << termcolor::bold;
details::set_stream_color(std::cout, get_value<details::ProgressBarOption::foreground_color>());
std::cout << get_value<details::ProgressBarOption::prefix_text>();
std::cout << get_value<details::ProgressBarOption::start>();
details::BlockProgressScaleWriter writer{std::cout,
get_value<details::ProgressBarOption::bar_width>()};
writer.write(progress_);
std::cout << get_value<details::ProgressBarOption::end>();
if (get_value<details::ProgressBarOption::show_percentage>()) {
std::cout << " " << std::min(static_cast<size_t>(progress_), size_t(100)) << "%";
}
if (get_value<details::ProgressBarOption::show_elapsed_time>()) {
std::cout << " [";
details::write_duration(std::cout, elapsed);
}
if (get_value<details::ProgressBarOption::show_remaining_time>()) {
if (get_value<details::ProgressBarOption::show_elapsed_time>())
std::cout << "<";
else
std::cout << " [";
auto eta = std::chrono::nanoseconds(
progress_ > 0 ? static_cast<long long>(elapsed.count() * 100 / progress_) : 0);
auto remaining = eta > elapsed ? (eta - elapsed) : (elapsed - eta);
details::write_duration(std::cout, remaining);
std::cout << "]";
} else {
if (get_value<details::ProgressBarOption::show_elapsed_time>())
std::cout << "]";
}
if (get_value<details::ProgressBarOption::max_postfix_text_len>() == 0)
get_value<details::ProgressBarOption::max_postfix_text_len>() = 10;
std::cout << " " << get_value<details::ProgressBarOption::postfix_text>()
<< std::string(get_value<details::ProgressBarOption::max_postfix_text_len>(), ' ')
<< "\r";
std::cout.flush();
if (progress_ > 100.0) {
get_value<details::ProgressBarOption::completed>() = true;
}
if (get_value<details::ProgressBarOption::completed>() &&
!from_multi_progress) // Don't std::endl if calling from MultiProgress
std::cout << termcolor::reset << std::endl;
}
};
} // namespace indicators

32
include/thirdparty/indicators/color.hpp vendored Normal file
View File

@ -0,0 +1,32 @@
/*
Activity Indicators for Modern C++
https://github.com/p-ranav/indica
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
SPDX-License-Identifier: MIT
Copyright (c) 2019 Pranav Srinivas Kumar <pranav.srinivas.kumar@gmail.com>.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#pragma once
#include <indicators/termcolor.hpp>
namespace indicators {
enum class Color { grey, red, green, yellow, blue, magenta, cyan, white, unspecified };
}

View File

@ -0,0 +1,127 @@
#pragma once
#include <indicators/color.hpp>
#include <indicators/termcolor.hpp>
#include <algorithm>
#include <chrono>
#include <iomanip>
#include <ostream>
#include <string>
#include <vector>
#include <cassert>
#include <cmath>
namespace indicators {
namespace details {
inline void set_stream_color(std::ostream &os, Color color) {
switch (color) {
case Color::grey:
os << termcolor::grey;
break;
case Color::red:
os << termcolor::red;
break;
case Color::green:
os << termcolor::green;
break;
case Color::yellow:
os << termcolor::yellow;
break;
case Color::blue:
os << termcolor::blue;
break;
case Color::magenta:
os << termcolor::magenta;
break;
case Color::cyan:
os << termcolor::cyan;
break;
case Color::white:
os << termcolor::white;
break;
default:
assert(false);
}
}
inline std::ostream &write_duration(std::ostream &os, std::chrono::nanoseconds ns) {
using namespace std;
using namespace std::chrono;
using days = duration<int, ratio<86400>>;
char fill = os.fill();
os.fill('0');
auto d = duration_cast<days>(ns);
ns -= d;
auto h = duration_cast<hours>(ns);
ns -= h;
auto m = duration_cast<minutes>(ns);
ns -= m;
auto s = duration_cast<seconds>(ns);
if (d.count() > 0)
os << setw(2) << d.count() << "d:";
if (h.count() > 0)
os << setw(2) << h.count() << "h:";
os << setw(2) << m.count() << "m:" << setw(2) << s.count() << 's';
os.fill(fill);
return os;
}
class BlockProgressScaleWriter {
public:
BlockProgressScaleWriter(std::ostream &os, size_t bar_width) : os(os), bar_width(bar_width) {}
std::ostream &write(float progress) {
std::string fill_text{""};
std::vector<std::string> lead_characters{" ", "", "", "", "", "", "", ""};
auto value = std::min(1.0f, std::max(0.0f, progress / 100.0f));
auto whole_width = std::floor(value * bar_width);
auto remainder_width = fmod((value * bar_width), 1.0f);
auto part_width = std::floor(remainder_width * lead_characters.size());
std::string lead_text = lead_characters[size_t(part_width)];
if ((bar_width - whole_width - 1) < 0)
lead_text = "";
for (size_t i = 0; i < whole_width; ++i)
os << fill_text;
os << lead_text;
for (size_t i = 0; i < (bar_width - whole_width - 1); ++i)
os << " ";
return os;
}
private:
std::ostream &os;
size_t bar_width = 0;
};
class ProgressScaleWriter {
public:
ProgressScaleWriter(std::ostream &os, size_t bar_width, const std::string &fill,
const std::string &lead, const std::string &remainder)
: os(os), bar_width(bar_width), fill(fill), lead(lead), remainder(remainder) {}
std::ostream &write(size_t progress) {
auto pos = static_cast<size_t>(progress * bar_width / 100.0);
for (size_t i = 0; i < bar_width; ++i) {
if (i < pos)
os << fill;
else if (i == pos)
os << lead;
else
os << remainder;
}
return os;
}
private:
std::ostream &os;
size_t bar_width = 0;
std::string fill;
std::string lead;
std::string remainder;
};
} // namespace details
} // namespace indicators

View File

@ -0,0 +1,140 @@
/*
Activity Indicators for Modern C++
https://github.com/p-ranav/indicators
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
SPDX-License-Identifier: MIT
Copyright (c) 2019 Pranav Srinivas Kumar <pranav.srinivas.kumar@gmail.com>.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#pragma once
#include <atomic>
#include <functional>
#include <indicators/color.hpp>
#include <indicators/setting.hpp>
#include <iostream>
#include <mutex>
#include <vector>
namespace indicators {
template <typename Indicator> class DynamicProgress {
using Settings = std::tuple<option::HideBarWhenComplete>;
public:
template <typename... Indicators> explicit DynamicProgress(Indicators &... bars) {
bars_ = {bars...};
for (auto &bar : bars_) {
bar.get().multi_progress_mode_ = true;
++total_count_;
++incomplete_count_;
}
}
Indicator &operator[](size_t index) {
print_progress();
std::lock_guard<std::mutex> lock{mutex_};
return bars_[index].get();
}
size_t push_back(Indicator &bar) {
std::lock_guard<std::mutex> lock{mutex_};
bar.multi_progress_mode_ = true;
bars_.push_back(bar);
++total_count_;
++incomplete_count_;
return bars_.size() - 1;
}
template <typename T, details::ProgressBarOption id>
void set_option(details::Setting<T, id> &&setting) {
static_assert(!std::is_same<T, typename std::decay<decltype(details::get_value<id>(
std::declval<Settings>()))>::type>::value,
"Setting has wrong type!");
std::lock_guard<std::mutex> lock(mutex_);
get_value<id>() = std::move(setting).value;
}
template <typename T, details::ProgressBarOption id>
void set_option(const details::Setting<T, id> &setting) {
static_assert(!std::is_same<T, typename std::decay<decltype(details::get_value<id>(
std::declval<Settings>()))>::type>::value,
"Setting has wrong type!");
std::lock_guard<std::mutex> lock(mutex_);
get_value<id>() = setting.value;
}
private:
Settings settings_;
std::atomic<bool> started_{false};
std::mutex mutex_;
std::vector<std::reference_wrapper<Indicator>> bars_;
std::atomic<size_t> total_count_{0};
std::atomic<size_t> incomplete_count_{0};
template <details::ProgressBarOption id>
auto get_value() -> decltype((details::get_value<id>(std::declval<Settings &>()).value)) {
return details::get_value<id>(settings_).value;
}
template <details::ProgressBarOption id>
auto get_value() const
-> decltype((details::get_value<id>(std::declval<const Settings &>()).value)) {
return details::get_value<id>(settings_).value;
}
void print_progress() {
std::lock_guard<std::mutex> lock{mutex_};
auto &hide_bar_when_complete = get_value<details::ProgressBarOption::hide_bar_when_complete>();
if (hide_bar_when_complete) {
// Hide completed bars
if (started_) {
for (size_t i = 0; i < incomplete_count_; ++i)
std::cout << "\033[A\r\033[K" << std::flush;
}
incomplete_count_ = 0;
for (auto &bar : bars_) {
if (!bar.get().is_completed()) {
bar.get().print_progress(true);
std::cout << "\n";
++incomplete_count_;
}
}
if (!started_)
started_ = true;
} else {
// Don't hide any bars
if (started_) {
for (size_t i = 0; i < total_count_; ++i)
std::cout << "\x1b[A";
}
for (auto &bar : bars_) {
bar.get().print_progress(true);
std::cout << "\n";
}
if (!started_)
started_ = true;
}
total_count_ = bars_.size();
std::cout << termcolor::reset;
}
};
} // namespace indicators

View File

@ -0,0 +1,101 @@
/*
Activity Indicators for Modern C++
https://github.com/p-ranav/indicators
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
SPDX-License-Identifier: MIT
Copyright (c) 2019 Pranav Srinivas Kumar <pranav.srinivas.kumar@gmail.com>.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#pragma once
#include <atomic>
#include <functional>
#include <indicators/color.hpp>
#include <iostream>
#include <mutex>
#include <vector>
namespace indicators {
template <typename Indicator, size_t count> class MultiProgress {
public:
template <typename... Indicators,
typename = typename std::enable_if<(sizeof...(Indicators) == count)>::type>
explicit MultiProgress(Indicators &... bars) {
bars_ = {bars...};
for (auto &bar : bars_) {
bar.get().multi_progress_mode_ = true;
}
}
template <size_t index>
typename std::enable_if<(index >= 0 && index < count), void>::type set_progress(size_t value) {
if (!bars_[index].get().is_completed())
bars_[index].get().set_progress(value);
print_progress();
}
template <size_t index>
typename std::enable_if<(index >= 0 && index < count), void>::type set_progress(float value) {
if (!bars_[index].get().is_completed())
bars_[index].get().set_progress(value);
print_progress();
}
template <size_t index>
typename std::enable_if<(index >= 0 && index < count), void>::type tick() {
if (!bars_[index].get().is_completed())
bars_[index].get().tick();
print_progress();
}
template <size_t index>
typename std::enable_if<(index >= 0 && index < count), bool>::type is_completed() const {
return bars_[index].get().is_completed();
}
private:
std::atomic<bool> started_{false};
std::mutex mutex_;
std::vector<std::reference_wrapper<Indicator>> bars_;
bool _all_completed() {
bool result{true};
for (size_t i = 0; i < count; ++i)
result &= bars_[i].get().is_completed();
return result;
}
void print_progress() {
std::lock_guard<std::mutex> lock{mutex_};
if (started_)
for (size_t i = 0; i < count; ++i)
std::cout << "\x1b[A";
for (auto &bar : bars_) {
bar.get().print_progress(true);
std::cout << "\n";
}
std::cout << termcolor::reset;
if (!started_)
started_ = true;
}
};
} // namespace indicators

View File

@ -0,0 +1,259 @@
/*
Activity Indicators for Modern C++
https://github.com/p-ranav/indicators
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
SPDX-License-Identifier: MIT
Copyright (c) 2019 Pranav Srinivas Kumar <pranav.srinivas.kumar@gmail.com>.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#pragma once
#include <indicators/details/stream_helper.hpp>
#include <algorithm>
#include <atomic>
#include <chrono>
#include <cmath>
#include <indicators/color.hpp>
#include <indicators/setting.hpp>
#include <iomanip>
#include <iostream>
#include <mutex>
#include <string>
#include <thread>
#include <tuple>
#include <type_traits>
namespace indicators {
class ProgressBar {
using Settings =
std::tuple<option::BarWidth, option::PrefixText, option::PostfixText, option::Start,
option::End, option::Fill, option::Lead, option::Remainder,
option::MaxPostfixTextLen, option::Completed, option::ShowPercentage,
option::ShowElapsedTime, option::ShowRemainingTime, option::SavedStartTime,
option::ForegroundColor>;
public:
template <typename... Args,
typename std::enable_if<details::are_settings_from_tuple<
Settings, typename std::decay<Args>::type...>::value,
void *>::type = nullptr>
explicit ProgressBar(Args &&... args)
: settings_(details::get<details::ProgressBarOption::bar_width>(option::BarWidth{100},
std::forward<Args>(args)...),
details::get<details::ProgressBarOption::prefix_text>(
option::PrefixText{}, std::forward<Args>(args)...),
details::get<details::ProgressBarOption::postfix_text>(
option::PostfixText{}, std::forward<Args>(args)...),
details::get<details::ProgressBarOption::start>(option::Start{"["},
std::forward<Args>(args)...),
details::get<details::ProgressBarOption::end>(option::End{"]"},
std::forward<Args>(args)...),
details::get<details::ProgressBarOption::fill>(option::Fill{"="},
std::forward<Args>(args)...),
details::get<details::ProgressBarOption::lead>(option::Lead{">"},
std::forward<Args>(args)...),
details::get<details::ProgressBarOption::remainder>(option::Remainder{" "},
std::forward<Args>(args)...),
details::get<details::ProgressBarOption::max_postfix_text_len>(
option::MaxPostfixTextLen{0}, std::forward<Args>(args)...),
details::get<details::ProgressBarOption::completed>(option::Completed{false},
std::forward<Args>(args)...),
details::get<details::ProgressBarOption::show_percentage>(
option::ShowPercentage{false}, std::forward<Args>(args)...),
details::get<details::ProgressBarOption::show_elapsed_time>(
option::ShowElapsedTime{false}, std::forward<Args>(args)...),
details::get<details::ProgressBarOption::show_remaining_time>(
option::ShowRemainingTime{false}, std::forward<Args>(args)...),
details::get<details::ProgressBarOption::saved_start_time>(
option::SavedStartTime{false}, std::forward<Args>(args)...),
details::get<details::ProgressBarOption::foreground_color>(
option::ForegroundColor{Color::white}, std::forward<Args>(args)...)) {}
template <typename T, details::ProgressBarOption id>
void set_option(details::Setting<T, id> &&setting) {
static_assert(!std::is_same<T, typename std::decay<decltype(details::get_value<id>(
std::declval<Settings>()))>::type>::value,
"Setting has wrong type!");
std::lock_guard<std::mutex> lock(mutex_);
get_value<id>() = std::move(setting).value;
}
template <typename T, details::ProgressBarOption id>
void set_option(const details::Setting<T, id> &setting) {
static_assert(!std::is_same<T, typename std::decay<decltype(details::get_value<id>(
std::declval<Settings>()))>::type>::value,
"Setting has wrong type!");
std::lock_guard<std::mutex> lock(mutex_);
get_value<id>() = setting.value;
}
void set_option(
const details::Setting<std::string, details::ProgressBarOption::postfix_text> &setting) {
std::lock_guard<std::mutex> lock(mutex_);
get_value<details::ProgressBarOption::postfix_text>() = setting.value;
if (setting.value.length() > get_value<details::ProgressBarOption::max_postfix_text_len>()) {
get_value<details::ProgressBarOption::max_postfix_text_len>() = setting.value.length();
}
}
void
set_option(details::Setting<std::string, details::ProgressBarOption::postfix_text> &&setting) {
std::lock_guard<std::mutex> lock(mutex_);
get_value<details::ProgressBarOption::postfix_text>() = std::move(setting).value;
auto &new_value = get_value<details::ProgressBarOption::postfix_text>();
if (new_value.length() > get_value<details::ProgressBarOption::max_postfix_text_len>()) {
get_value<details::ProgressBarOption::max_postfix_text_len>() = new_value.length();
}
}
void set_progress(size_t new_progress) {
{
std::lock_guard<std::mutex> lock(mutex_);
progress_ = new_progress;
}
save_start_time();
print_progress();
}
void tick() {
{
std::lock_guard<std::mutex> lock{mutex_};
progress_ += 1;
}
save_start_time();
print_progress();
}
size_t current() {
std::lock_guard<std::mutex> lock{mutex_};
return std::min(progress_, size_t(100));
}
bool is_completed() const { return get_value<details::ProgressBarOption::completed>(); }
void mark_as_completed() {
get_value<details::ProgressBarOption::completed>() = true;
print_progress();
}
private:
template <details::ProgressBarOption id>
auto get_value() -> decltype((details::get_value<id>(std::declval<Settings &>()).value)) {
return details::get_value<id>(settings_).value;
}
template <details::ProgressBarOption id>
auto get_value() const
-> decltype((details::get_value<id>(std::declval<const Settings &>()).value)) {
return details::get_value<id>(settings_).value;
}
size_t progress_{0};
Settings settings_;
std::chrono::nanoseconds elapsed_;
std::chrono::time_point<std::chrono::high_resolution_clock> start_time_point_;
std::mutex mutex_;
template <typename Indicator, size_t count> friend class MultiProgress;
template <typename Indicator> friend class DynamicProgress;
std::atomic<bool> multi_progress_mode_{false};
void save_start_time() {
auto &show_elapsed_time = get_value<details::ProgressBarOption::show_elapsed_time>();
auto &saved_start_time = get_value<details::ProgressBarOption::saved_start_time>();
auto &show_remaining_time = get_value<details::ProgressBarOption::show_remaining_time>();
if ((show_elapsed_time || show_remaining_time) && !saved_start_time) {
start_time_point_ = std::chrono::high_resolution_clock::now();
saved_start_time = true;
}
}
void print_progress(bool from_multi_progress = false) {
std::lock_guard<std::mutex> lock{mutex_};
if (multi_progress_mode_ && !from_multi_progress) {
if (progress_ > 100) {
get_value<details::ProgressBarOption::completed>() = true;
}
return;
}
auto now = std::chrono::high_resolution_clock::now();
if (!get_value<details::ProgressBarOption::completed>())
elapsed_ = std::chrono::duration_cast<std::chrono::nanoseconds>(now - start_time_point_);
// std::cout << termcolor::bold;
if (get_value<details::ProgressBarOption::foreground_color>() != Color::unspecified)
details::set_stream_color(std::cout, get_value<details::ProgressBarOption::foreground_color>());
std::cout << get_value<details::ProgressBarOption::prefix_text>();
std::cout << get_value<details::ProgressBarOption::start>();
details::ProgressScaleWriter writer{std::cout,
get_value<details::ProgressBarOption::bar_width>(),
get_value<details::ProgressBarOption::fill>(),
get_value<details::ProgressBarOption::lead>(),
get_value<details::ProgressBarOption::remainder>()};
writer.write(progress_);
std::cout << get_value<details::ProgressBarOption::end>();
if (get_value<details::ProgressBarOption::show_percentage>()) {
std::cout << " " << std::min(progress_, size_t(100)) << "%";
}
if (get_value<details::ProgressBarOption::show_elapsed_time>()) {
std::cout << " [";
details::write_duration(std::cout, elapsed_);
}
if (get_value<details::ProgressBarOption::show_remaining_time>()) {
if (get_value<details::ProgressBarOption::show_elapsed_time>())
std::cout << "<";
else
std::cout << " [";
auto eta = std::chrono::nanoseconds(
progress_ > 0 ? static_cast<long long>(elapsed_.count() * 100 / progress_) : 0);
auto remaining = eta > elapsed_ ? (eta - elapsed_) : (elapsed_ - eta);
details::write_duration(std::cout, remaining);
std::cout << "]";
} else {
if (get_value<details::ProgressBarOption::show_elapsed_time>())
std::cout << "]";
}
if (get_value<details::ProgressBarOption::max_postfix_text_len>() == 0)
get_value<details::ProgressBarOption::max_postfix_text_len>() = 10;
std::cout << " " << get_value<details::ProgressBarOption::postfix_text>()
<< std::string(get_value<details::ProgressBarOption::max_postfix_text_len>(), ' ')
<< "\r";
std::cout.flush();
if (progress_ > 100) {
get_value<details::ProgressBarOption::completed>() = true;
}
if (get_value<details::ProgressBarOption::completed>() &&
!from_multi_progress) // Don't std::endl if calling from MultiProgress
std::cout << termcolor::reset << std::endl;
}
};
} // namespace indicators

View File

@ -0,0 +1,230 @@
/*
Activity Indicators for Modern C++
https://github.com/p-ranav/indicators
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
SPDX-License-Identifier: MIT
Copyright (c) 2019 Pranav Srinivas Kumar <pranav.srinivas.kumar@gmail.com>.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#pragma once
#include <indicators/details/stream_helper.hpp>
#include <algorithm>
#include <atomic>
#include <chrono>
#include <cmath>
#include <indicators/color.hpp>
#include <indicators/setting.hpp>
#include <iomanip>
#include <iostream>
#include <mutex>
#include <string>
#include <thread>
#include <tuple>
#include <vector>
namespace indicators {
class ProgressSpinner {
using Settings =
std::tuple<option::ForegroundColor, option::PrefixText, option::PostfixText,
option::ShowPercentage, option::ShowElapsedTime, option::ShowRemainingTime,
option::ShowSpinner, option::SavedStartTime, option::Completed,
option::MaxPostfixTextLen, option::SpinnerStates>;
public:
template <typename... Args,
typename std::enable_if<details::are_settings_from_tuple<
Settings, typename std::decay<Args>::type...>::value,
void *>::type = nullptr>
explicit ProgressSpinner(Args &&... args)
: settings_(details::get<details::ProgressBarOption::foreground_color>(
option::ForegroundColor{Color::white}, std::forward<Args>(args)...),
details::get<details::ProgressBarOption::prefix_text>(
option::PrefixText{}, std::forward<Args>(args)...),
details::get<details::ProgressBarOption::postfix_text>(
option::PostfixText{}, std::forward<Args>(args)...),
details::get<details::ProgressBarOption::show_percentage>(
option::ShowPercentage{true}, std::forward<Args>(args)...),
details::get<details::ProgressBarOption::show_elapsed_time>(
option::ShowElapsedTime{false}, std::forward<Args>(args)...),
details::get<details::ProgressBarOption::show_remaining_time>(
option::ShowRemainingTime{false}, std::forward<Args>(args)...),
details::get<details::ProgressBarOption::spinner_show>(
option::ShowSpinner{true}, std::forward<Args>(args)...),
details::get<details::ProgressBarOption::saved_start_time>(
option::SavedStartTime{false}, std::forward<Args>(args)...),
details::get<details::ProgressBarOption::completed>(option::Completed{false},
std::forward<Args>(args)...),
details::get<details::ProgressBarOption::max_postfix_text_len>(
option::MaxPostfixTextLen{0}, std::forward<Args>(args)...),
details::get<details::ProgressBarOption::spinner_states>(
option::SpinnerStates{std::vector<std::string>{"", "", "", "", "", "",
"", "", "", ""}},
std::forward<Args>(args)...)) {}
template <typename T, details::ProgressBarOption id>
void set_option(details::Setting<T, id> &&setting) {
static_assert(!std::is_same<T, typename std::decay<decltype(details::get_value<id>(
std::declval<Settings>()))>::type>::value,
"Setting has wrong type!");
std::lock_guard<std::mutex> lock(mutex_);
get_value<id>() = std::move(setting).value;
}
template <typename T, details::ProgressBarOption id>
void set_option(const details::Setting<T, id> &setting) {
static_assert(!std::is_same<T, typename std::decay<decltype(details::get_value<id>(
std::declval<Settings>()))>::type>::value,
"Setting has wrong type!");
std::lock_guard<std::mutex> lock(mutex_);
get_value<id>() = setting.value;
}
void set_option(
const details::Setting<std::string, details::ProgressBarOption::postfix_text> &setting) {
std::lock_guard<std::mutex> lock(mutex_);
get_value<details::ProgressBarOption::postfix_text>() = setting.value;
if (setting.value.length() > get_value<details::ProgressBarOption::max_postfix_text_len>()) {
get_value<details::ProgressBarOption::max_postfix_text_len>() = setting.value.length();
}
}
void
set_option(details::Setting<std::string, details::ProgressBarOption::postfix_text> &&setting) {
std::lock_guard<std::mutex> lock(mutex_);
get_value<details::ProgressBarOption::postfix_text>() = std::move(setting).value;
auto &new_value = get_value<details::ProgressBarOption::postfix_text>();
if (new_value.length() > get_value<details::ProgressBarOption::max_postfix_text_len>()) {
get_value<details::ProgressBarOption::max_postfix_text_len>() = new_value.length();
}
}
void set_progress(size_t value) {
{
std::lock_guard<std::mutex> lock{mutex_};
progress_ = value;
}
save_start_time();
print_progress();
}
void tick() {
{
std::lock_guard<std::mutex> lock{mutex_};
progress_ += 1;
}
save_start_time();
print_progress();
}
size_t current() {
std::lock_guard<std::mutex> lock{mutex_};
return std::min(progress_, size_t(100));
}
bool is_completed() const { return get_value<details::ProgressBarOption::completed>(); }
void mark_as_completed() {
get_value<details::ProgressBarOption::completed>() = true;
print_progress();
}
private:
Settings settings_;
size_t progress_{0};
size_t index_{0};
std::chrono::time_point<std::chrono::high_resolution_clock> start_time_point_;
std::mutex mutex_;
template <details::ProgressBarOption id>
auto get_value() -> decltype((details::get_value<id>(std::declval<Settings &>()).value)) {
return details::get_value<id>(settings_).value;
}
template <details::ProgressBarOption id>
auto get_value() const
-> decltype((details::get_value<id>(std::declval<const Settings &>()).value)) {
return details::get_value<id>(settings_).value;
}
void save_start_time() {
auto &show_elapsed_time = get_value<details::ProgressBarOption::show_elapsed_time>();
auto &show_remaining_time = get_value<details::ProgressBarOption::show_remaining_time>();
auto &saved_start_time = get_value<details::ProgressBarOption::saved_start_time>();
if ((show_elapsed_time || show_remaining_time) && !saved_start_time) {
start_time_point_ = std::chrono::high_resolution_clock::now();
saved_start_time = true;
}
}
void print_progress() {
std::lock_guard<std::mutex> lock{mutex_};
auto now = std::chrono::high_resolution_clock::now();
auto elapsed = std::chrono::duration_cast<std::chrono::nanoseconds>(now - start_time_point_);
std::cout << termcolor::bold;
details::set_stream_color(std::cout, get_value<details::ProgressBarOption::foreground_color>());
std::cout << get_value<details::ProgressBarOption::prefix_text>();
if (get_value<details::ProgressBarOption::spinner_show>())
std::cout << get_value<details::ProgressBarOption::spinner_states>()
[index_ % get_value<details::ProgressBarOption::spinner_states>().size()];
if (get_value<details::ProgressBarOption::show_percentage>()) {
std::cout << " " << std::min(progress_, size_t(100)) << "%";
}
if (get_value<details::ProgressBarOption::show_elapsed_time>()) {
std::cout << " [";
details::write_duration(std::cout, elapsed);
}
if (get_value<details::ProgressBarOption::show_remaining_time>()) {
if (get_value<details::ProgressBarOption::show_elapsed_time>())
std::cout << "<";
else
std::cout << " [";
auto eta = std::chrono::nanoseconds(
progress_ > 0 ? static_cast<long long>(elapsed.count() * 100 / progress_) : 0);
auto remaining = eta > elapsed ? (eta - elapsed) : (elapsed - eta);
details::write_duration(std::cout, remaining);
std::cout << "]";
} else {
if (get_value<details::ProgressBarOption::show_elapsed_time>())
std::cout << "]";
}
if (get_value<details::ProgressBarOption::max_postfix_text_len>() == 0)
get_value<details::ProgressBarOption::max_postfix_text_len>() = 10;
std::cout << " " << get_value<details::ProgressBarOption::postfix_text>()
<< std::string(get_value<details::ProgressBarOption::max_postfix_text_len>(), ' ')
<< "\r";
std::cout.flush();
index_ += 1;
if (progress_ > 100) {
get_value<details::ProgressBarOption::completed>() = true;
}
if (get_value<details::ProgressBarOption::completed>())
std::cout << termcolor::reset << std::endl;
}
};
} // namespace indicators

View File

@ -0,0 +1,204 @@
/*
Activity Indicators for Modern C++
https://github.com/p-ranav/indicators
Licensed under the MIT License <http://opensource.org/licenses/MIT>.
SPDX-License-Identifier: MIT
Copyright (c) 2019 Dawid Pilarski <dawid.pilarski@panicsoftware.com>.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#pragma once
#include <cstddef>
#include <indicators/color.hpp>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>
namespace indicators {
namespace details {
template <bool condition> struct if_else;
template <> struct if_else<true> { using type = std::true_type; };
template <> struct if_else<false> { using type = std::false_type; };
template <bool condition, typename True, typename False> struct if_else_type;
template <typename True, typename False> struct if_else_type<true, True, False> {
using type = True;
};
template <typename True, typename False> struct if_else_type<false, True, False> {
using type = False;
};
template <typename... Ops> struct conjuction;
template <> struct conjuction<> : std::true_type {};
template <typename Op, typename... TailOps>
struct conjuction<Op, TailOps...>
: if_else_type<!Op::value, std::false_type, conjuction<TailOps...>>::type {};
template <typename... Ops> struct disjunction;
template <> struct disjunction<> : std::false_type {};
template <typename Op, typename... TailOps>
struct disjunction<Op, TailOps...>
: if_else_type<Op::value, std::true_type, disjunction<TailOps...>>::type {};
enum class ProgressBarOption {
bar_width = 0,
prefix_text,
postfix_text,
start,
end,
fill,
lead,
remainder,
max_postfix_text_len,
completed,
show_percentage,
show_elapsed_time,
show_remaining_time,
saved_start_time,
foreground_color,
spinner_show,
spinner_states,
hide_bar_when_complete
};
template <typename T, ProgressBarOption Id> struct Setting {
template <typename... Args,
typename = typename std::enable_if<std::is_constructible<T, Args...>::value>::type>
explicit Setting(Args &&... args) : value(std::forward<Args>(args)...) {}
Setting(const Setting &) = default;
Setting(Setting &&) = default;
static constexpr auto id = Id;
using type = T;
T value{};
};
template <typename T> struct is_setting : std::false_type {};
template <ProgressBarOption Id, typename T> struct is_setting<Setting<T, Id>> : std::true_type {};
template <typename... Args>
struct are_settings : if_else<conjuction<is_setting<Args>...>::value>::type {};
template <> struct are_settings<> : std::true_type {};
template <typename Setting, typename Tuple> struct is_setting_from_tuple;
template <typename Setting> struct is_setting_from_tuple<Setting, std::tuple<>> : std::true_type {};
template <typename Setting, typename... TupleTypes>
struct is_setting_from_tuple<Setting, std::tuple<TupleTypes...>>
: if_else<disjunction<std::is_same<Setting, TupleTypes>...>::value>::type {};
template <typename Tuple, typename... Settings>
struct are_settings_from_tuple
: if_else<conjuction<is_setting_from_tuple<Settings, Tuple>...>::value>::type {};
template <ProgressBarOption Id> struct always_true { static constexpr auto value = true; };
template <ProgressBarOption Id, typename Default> Default &&get_impl(Default &&def) {
return std::forward<Default>(def);
}
template <ProgressBarOption Id, typename Default, typename T, typename... Args>
auto get_impl(Default &&/*def*/, T &&first, Args &&... /*tail*/) ->
typename std::enable_if<(std::decay<T>::type::id == Id),
decltype(std::forward<T>(first))>::type {
return std::forward<T>(first);
}
template <ProgressBarOption Id, typename Default, typename T, typename... Args>
auto get_impl(Default &&def, T &&/*first*/, Args &&... tail) ->
typename std::enable_if<(std::decay<T>::type::id != Id),
decltype(get_impl<Id>(std::forward<Default>(def),
std::forward<Args>(tail)...))>::type {
return get_impl<Id>(std::forward<Default>(def), std::forward<Args>(tail)...);
}
template <ProgressBarOption Id, typename Default, typename... Args,
typename = typename std::enable_if<are_settings<Args...>::value, void>::type>
auto get(Default &&def, Args &&... args)
-> decltype(details::get_impl<Id>(std::forward<Default>(def), std::forward<Args>(args)...)) {
return details::get_impl<Id>(std::forward<Default>(def), std::forward<Args>(args)...);
}
template <ProgressBarOption Id> using StringSetting = Setting<std::string, Id>;
template <ProgressBarOption Id> using IntegerSetting = Setting<std::size_t, Id>;
template <ProgressBarOption Id> using BooleanSetting = Setting<bool, Id>;
template <ProgressBarOption Id, typename Tuple, std::size_t counter = 0> struct option_idx;
template <ProgressBarOption Id, typename T, typename... Settings, std::size_t counter>
struct option_idx<Id, std::tuple<T, Settings...>, counter>
: if_else_type<(Id == T::id), std::integral_constant<std::size_t, counter>,
option_idx<Id, std::tuple<Settings...>, counter + 1>>::type {};
template <ProgressBarOption Id, std::size_t counter> struct option_idx<Id, std::tuple<>, counter> {
static_assert(always_true<(ProgressBarOption)Id>::value, "No such option was found");
};
template <ProgressBarOption Id, typename Settings>
auto get_value(Settings &&settings)
-> decltype((std::get<option_idx<Id, typename std::decay<Settings>::type>::value>(
std::declval<Settings &&>()))) {
return std::get<option_idx<Id, typename std::decay<Settings>::type>::value>(
std::forward<Settings>(settings));
}
} // namespace details
namespace option {
using BarWidth = details::IntegerSetting<details::ProgressBarOption::bar_width>;
using PrefixText = details::StringSetting<details::ProgressBarOption::prefix_text>;
using PostfixText = details::StringSetting<details::ProgressBarOption::postfix_text>;
using Start = details::StringSetting<details::ProgressBarOption::start>;
using End = details::StringSetting<details::ProgressBarOption::end>;
using Fill = details::StringSetting<details::ProgressBarOption::fill>;
using Lead = details::StringSetting<details::ProgressBarOption::lead>;
using Remainder = details::StringSetting<details::ProgressBarOption::remainder>;
using MaxPostfixTextLen = details::IntegerSetting<details::ProgressBarOption::max_postfix_text_len>;
using Completed = details::BooleanSetting<details::ProgressBarOption::completed>;
using ShowPercentage = details::BooleanSetting<details::ProgressBarOption::show_percentage>;
using ShowElapsedTime = details::BooleanSetting<details::ProgressBarOption::show_elapsed_time>;
using ShowRemainingTime = details::BooleanSetting<details::ProgressBarOption::show_remaining_time>;
using SavedStartTime = details::BooleanSetting<details::ProgressBarOption::saved_start_time>;
using ForegroundColor = details::Setting<Color, details::ProgressBarOption::foreground_color>;
using ShowSpinner = details::BooleanSetting<details::ProgressBarOption::spinner_show>;
using SpinnerStates =
details::Setting<std::vector<std::string>, details::ProgressBarOption::spinner_states>;
using HideBarWhenComplete =
details::BooleanSetting<details::ProgressBarOption::hide_bar_when_complete>;
} // namespace option
} // namespace indicators

View File

@ -0,0 +1,450 @@
//!
//! termcolor
//! ~~~~~~~~~
//!
//! termcolor is a header-only c++ library for printing colored messages
//! to the terminal. Written just for fun with a help of the Force.
//!
//! :copyright: (c) 2013 by Ihor Kalnytskyi
//! :license: BSD, see LICENSE for details
//!
#ifndef TERMCOLOR_HPP_
#define TERMCOLOR_HPP_
// the following snippet of code detects the current OS and
// defines the appropriate macro that is used to wrap some
// platform specific things
#if defined(_WIN32) || defined(_WIN64)
#define TERMCOLOR_OS_WINDOWS
#elif defined(__APPLE__)
#define TERMCOLOR_OS_MACOS
#elif defined(__unix__) || defined(__unix)
#define TERMCOLOR_OS_LINUX
#else
#error unsupported platform
#endif
// This headers provides the `isatty()`/`fileno()` functions,
// which are used for testing whether a standart stream refers
// to the terminal. As for Windows, we also need WinApi funcs
// for changing colors attributes of the terminal.
#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX)
#include <unistd.h>
#elif defined(TERMCOLOR_OS_WINDOWS)
#if !defined(NOMINMAX)
#define NOMINMAX
#endif
#include <io.h>
#include <windows.h>
#endif
#include <cstdio>
#include <iostream>
namespace termcolor {
// Forward declaration of the `_internal` namespace.
// All comments are below.
namespace _internal {
// An index to be used to access a private storage of I/O streams. See
// colorize / nocolorize I/O manipulators for details.
static int colorize_index = std::ios_base::xalloc();
inline FILE *get_standard_stream(const std::ostream &stream);
inline bool is_colorized(std::ostream &stream);
inline bool is_atty(const std::ostream &stream);
#if defined(TERMCOLOR_OS_WINDOWS)
inline void win_change_attributes(std::ostream &stream, int foreground, int background = -1);
#endif
} // namespace _internal
inline std::ostream &colorize(std::ostream &stream) {
stream.iword(_internal::colorize_index) = 1L;
return stream;
}
inline std::ostream &nocolorize(std::ostream &stream) {
stream.iword(_internal::colorize_index) = 0L;
return stream;
}
inline std::ostream &reset(std::ostream &stream) {
if (_internal::is_colorized(stream)) {
#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX)
stream << "\033[00m";
#elif defined(TERMCOLOR_OS_WINDOWS)
_internal::win_change_attributes(stream, -1, -1);
#endif
}
return stream;
}
inline std::ostream &bold(std::ostream &stream) {
if (_internal::is_colorized(stream)) {
#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX)
stream << "\033[1m";
#elif defined(TERMCOLOR_OS_WINDOWS)
#endif
}
return stream;
}
inline std::ostream &dark(std::ostream &stream) {
if (_internal::is_colorized(stream)) {
#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX)
stream << "\033[2m";
#elif defined(TERMCOLOR_OS_WINDOWS)
#endif
}
return stream;
}
inline std::ostream &italic(std::ostream &stream) {
if (_internal::is_colorized(stream)) {
#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX)
stream << "\033[3m";
#elif defined(TERMCOLOR_OS_WINDOWS)
#endif
}
return stream;
}
inline std::ostream &underline(std::ostream &stream) {
if (_internal::is_colorized(stream)) {
#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX)
stream << "\033[4m";
#elif defined(TERMCOLOR_OS_WINDOWS)
#endif
}
return stream;
}
inline std::ostream &blink(std::ostream &stream) {
if (_internal::is_colorized(stream)) {
#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX)
stream << "\033[5m";
#elif defined(TERMCOLOR_OS_WINDOWS)
#endif
}
return stream;
}
inline std::ostream &reverse(std::ostream &stream) {
if (_internal::is_colorized(stream)) {
#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX)
stream << "\033[7m";
#elif defined(TERMCOLOR_OS_WINDOWS)
#endif
}
return stream;
}
inline std::ostream &concealed(std::ostream &stream) {
if (_internal::is_colorized(stream)) {
#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX)
stream << "\033[8m";
#elif defined(TERMCOLOR_OS_WINDOWS)
#endif
}
return stream;
}
inline std::ostream &crossed(std::ostream &stream) {
if (_internal::is_colorized(stream)) {
#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX)
stream << "\033[9m";
#elif defined(TERMCOLOR_OS_WINDOWS)
#endif
}
return stream;
}
inline std::ostream &grey(std::ostream &stream) {
if (_internal::is_colorized(stream)) {
#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX)
stream << "\033[30m";
#elif defined(TERMCOLOR_OS_WINDOWS)
_internal::win_change_attributes(stream,
0 // grey (black)
);
#endif
}
return stream;
}
inline std::ostream &red(std::ostream &stream) {
if (_internal::is_colorized(stream)) {
#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX)
stream << "\033[31m";
#elif defined(TERMCOLOR_OS_WINDOWS)
_internal::win_change_attributes(stream, FOREGROUND_RED);
#endif
}
return stream;
}
inline std::ostream &green(std::ostream &stream) {
if (_internal::is_colorized(stream)) {
#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX)
stream << "\033[32m";
#elif defined(TERMCOLOR_OS_WINDOWS)
_internal::win_change_attributes(stream, FOREGROUND_GREEN);
#endif
}
return stream;
}
inline std::ostream &yellow(std::ostream &stream) {
if (_internal::is_colorized(stream)) {
#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX)
stream << "\033[33m";
#elif defined(TERMCOLOR_OS_WINDOWS)
_internal::win_change_attributes(stream, FOREGROUND_GREEN | FOREGROUND_RED);
#endif
}
return stream;
}
inline std::ostream &blue(std::ostream &stream) {
if (_internal::is_colorized(stream)) {
#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX)
stream << "\033[34m";
#elif defined(TERMCOLOR_OS_WINDOWS)
_internal::win_change_attributes(stream, FOREGROUND_BLUE);
#endif
}
return stream;
}
inline std::ostream &magenta(std::ostream &stream) {
if (_internal::is_colorized(stream)) {
#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX)
stream << "\033[35m";
#elif defined(TERMCOLOR_OS_WINDOWS)
_internal::win_change_attributes(stream, FOREGROUND_BLUE | FOREGROUND_RED);
#endif
}
return stream;
}
inline std::ostream &cyan(std::ostream &stream) {
if (_internal::is_colorized(stream)) {
#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX)
stream << "\033[36m";
#elif defined(TERMCOLOR_OS_WINDOWS)
_internal::win_change_attributes(stream, FOREGROUND_BLUE | FOREGROUND_GREEN);
#endif
}
return stream;
}
inline std::ostream &white(std::ostream &stream) {
if (_internal::is_colorized(stream)) {
#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX)
stream << "\033[37m";
#elif defined(TERMCOLOR_OS_WINDOWS)
_internal::win_change_attributes(stream, FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED);
#endif
}
return stream;
}
inline std::ostream &on_grey(std::ostream &stream) {
if (_internal::is_colorized(stream)) {
#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX)
stream << "\033[40m";
#elif defined(TERMCOLOR_OS_WINDOWS)
_internal::win_change_attributes(stream, -1,
0 // grey (black)
);
#endif
}
return stream;
}
inline std::ostream &on_red(std::ostream &stream) {
if (_internal::is_colorized(stream)) {
#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX)
stream << "\033[41m";
#elif defined(TERMCOLOR_OS_WINDOWS)
_internal::win_change_attributes(stream, -1, BACKGROUND_RED);
#endif
}
return stream;
}
inline std::ostream &on_green(std::ostream &stream) {
if (_internal::is_colorized(stream)) {
#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX)
stream << "\033[42m";
#elif defined(TERMCOLOR_OS_WINDOWS)
_internal::win_change_attributes(stream, -1, BACKGROUND_GREEN);
#endif
}
return stream;
}
inline std::ostream &on_yellow(std::ostream &stream) {
if (_internal::is_colorized(stream)) {
#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX)
stream << "\033[43m";
#elif defined(TERMCOLOR_OS_WINDOWS)
_internal::win_change_attributes(stream, -1, BACKGROUND_GREEN | BACKGROUND_RED);
#endif
}
return stream;
}
inline std::ostream &on_blue(std::ostream &stream) {
if (_internal::is_colorized(stream)) {
#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX)
stream << "\033[44m";
#elif defined(TERMCOLOR_OS_WINDOWS)
_internal::win_change_attributes(stream, -1, BACKGROUND_BLUE);
#endif
}
return stream;
}
inline std::ostream &on_magenta(std::ostream &stream) {
if (_internal::is_colorized(stream)) {
#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX)
stream << "\033[45m";
#elif defined(TERMCOLOR_OS_WINDOWS)
_internal::win_change_attributes(stream, -1, BACKGROUND_BLUE | BACKGROUND_RED);
#endif
}
return stream;
}
inline std::ostream &on_cyan(std::ostream &stream) {
if (_internal::is_colorized(stream)) {
#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX)
stream << "\033[46m";
#elif defined(TERMCOLOR_OS_WINDOWS)
_internal::win_change_attributes(stream, -1, BACKGROUND_GREEN | BACKGROUND_BLUE);
#endif
}
return stream;
}
inline std::ostream &on_white(std::ostream &stream) {
if (_internal::is_colorized(stream)) {
#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX)
stream << "\033[47m";
#elif defined(TERMCOLOR_OS_WINDOWS)
_internal::win_change_attributes(stream, -1,
BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_RED);
#endif
}
return stream;
}
//! Since C++ hasn't a way to hide something in the header from
//! the outer access, I have to introduce this namespace which
//! is used for internal purpose and should't be access from
//! the user code.
namespace _internal {
//! Since C++ hasn't a true way to extract stream handler
//! from the a given `std::ostream` object, I have to write
//! this kind of hack.
inline FILE *get_standard_stream(const std::ostream &stream) {
if (&stream == &std::cout)
return stdout;
else if ((&stream == &std::cerr) || (&stream == &std::clog))
return stderr;
return 0;
}
// Say whether a given stream should be colorized or not. It's always
// true for ATTY streams and may be true for streams marked with
// colorize flag.
inline bool is_colorized(std::ostream &stream) {
return is_atty(stream) || static_cast<bool>(stream.iword(colorize_index));
}
//! Test whether a given `std::ostream` object refers to
//! a terminal.
inline bool is_atty(const std::ostream &stream) {
FILE *std_stream = get_standard_stream(stream);
// Unfortunately, fileno() ends with segmentation fault
// if invalid file descriptor is passed. So we need to
// handle this case gracefully and assume it's not a tty
// if standard stream is not detected, and 0 is returned.
if (!std_stream)
return false;
#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX)
return ::isatty(fileno(std_stream));
#elif defined(TERMCOLOR_OS_WINDOWS)
return ::_isatty(_fileno(std_stream));
#endif
}
#if defined(TERMCOLOR_OS_WINDOWS)
//! Change Windows Terminal colors attribute. If some
//! parameter is `-1` then attribute won't changed.
inline void win_change_attributes(std::ostream &stream, int foreground, int background) {
// yeah, i know.. it's ugly, it's windows.
static WORD defaultAttributes = 0;
// Windows doesn't have ANSI escape sequences and so we use special
// API to change Terminal output color. That means we can't
// manipulate colors by means of "std::stringstream" and hence
// should do nothing in this case.
if (!_internal::is_atty(stream))
return;
// get terminal handle
HANDLE hTerminal = INVALID_HANDLE_VALUE;
if (&stream == &std::cout)
hTerminal = GetStdHandle(STD_OUTPUT_HANDLE);
else if (&stream == &std::cerr)
hTerminal = GetStdHandle(STD_ERROR_HANDLE);
// save default terminal attributes if it unsaved
if (!defaultAttributes) {
CONSOLE_SCREEN_BUFFER_INFO info;
if (!GetConsoleScreenBufferInfo(hTerminal, &info))
return;
defaultAttributes = info.wAttributes;
}
// restore all default settings
if (foreground == -1 && background == -1) {
SetConsoleTextAttribute(hTerminal, defaultAttributes);
return;
}
// get current settings
CONSOLE_SCREEN_BUFFER_INFO info;
if (!GetConsoleScreenBufferInfo(hTerminal, &info))
return;
if (foreground != -1) {
info.wAttributes &= ~(info.wAttributes & 0x0F);
info.wAttributes |= static_cast<WORD>(foreground);
}
if (background != -1) {
info.wAttributes &= ~(info.wAttributes & 0xF0);
info.wAttributes |= static_cast<WORD>(background);
}
SetConsoleTextAttribute(hTerminal, info.wAttributes);
}
#endif // TERMCOLOR_OS_WINDOWS
} // namespace _internal
} // namespace termcolor
#undef TERMCOLOR_OS_WINDOWS
#undef TERMCOLOR_OS_MACOS
#undef TERMCOLOR_OS_LINUX
#endif // TERMCOLOR_HPP_

View File

@ -35,6 +35,6 @@ namespace minilog {
// This is the set of log sinks. This must be in a separate library to ensure
// that there is only one instance of this across the entire program.
std::set<minilog::LogSink *> log_sinks_global;
int global_log_severity = INFO;
int global_log_severity = WARNING;
} // namespace ceres

View File

@ -99,7 +99,7 @@ class FastSubdirData(object):
@property
def url_w_repodata_fn(self):
return self.url_w_subdir + '/' + self.repodata_fn
return self.url_w_credentials + '/' + self.repodata_fn
@property
def cache_path_json(self):

View File

@ -189,6 +189,7 @@ def remove(args, parser):
prefix = context.target_prefix
check_non_admin()
api.Context.set_verbosity(context.verbosity);
if args.all and prefix == context.default_prefix:
raise CondaEnvironmentError("cannot remove current environment. \
@ -241,7 +242,6 @@ def remove(args, parser):
solver_options.append((api.SOLVER_FLAG_ALLOW_UNINSTALL, 1))
pool = api.Pool()
pool.set_debuglevel(context.verbosity)
repos = []
# add installed
@ -269,6 +269,8 @@ def install(args, parser, command='install'):
context.validate_configuration()
check_non_admin()
api.Context().verbosity = context.verbosity;
newenv = bool(command == 'create')
isinstall = bool(command == 'install')
solver_task = api.SOLVER_INSTALL
@ -442,7 +444,7 @@ def install(args, parser, command='install'):
print("\nLooking for: {}\n".format(mamba_solve_specs))
pool = api.Pool()
pool.set_debuglevel(context.verbosity)
repos = []
# add installed

View File

@ -16,6 +16,8 @@ import threading
import json
import os
import mamba.mamba_api as api
def load_channel(subdir_data, result_container):
if not context.quiet:
print("Getting ", subdir_data.channel.name, subdir_data.channel.platform)
@ -23,26 +25,54 @@ def load_channel(subdir_data, result_container):
def get_index(channel_urls=(), prepend=True, platform=None,
use_local=False, use_cache=False, unknown=None, prefix=None,
repodata_fn="repodata.json"):
repodata_fn="repodata.json.bz2"):
real_urls = calculate_channel_urls(channel_urls, prepend, platform, use_local)
check_whitelist(real_urls)
result = get_env_index(real_urls, repodata_fn)
return result
def get_env_index(channel_urls, repodata_fn="repodata.json"):
threads = []
result = []
sddata = [FastSubdirData(Channel(x), idx, repodata_fn) for idx, x in enumerate(channel_urls)]
for sd in sddata:
t = threading.Thread(target=load_channel, args=(sd, result))
threads.append(t)
t.start()
handle = api.DownloadHandle()
dlist = api.DownloadTargetList()
for t in threads:
t.join()
sddata = []
index = []
for idx, url in enumerate(real_urls):
fsd = FastSubdirData(Channel(url), idx, repodata_fn)
index.append(fsd)
full_url = fsd.url_w_repodata_fn
full_path_cache = fsd.cache_path_json
sd = api.SubdirData(fsd.channel.name + '/' + fsd.channel.subdir, full_url, full_path_cache)
sd.load()
sddata.append(sd)
dlist.append(sd)
result = sorted(result, key=lambda x: x.channel_idx)
return result
is_downloaded = dlist.download(True)
if not is_downloaded:
print("Error downloading repodata.")
for x in index:
x._loaded = True
return index
# def get_index(channel_urls=(), prepend=True, platform=None,
# use_local=False, use_cache=False, unknown=None, prefix=None,
# repodata_fn="repodata.json"):
# real_urls = calculate_channel_urls(channel_urls, prepend, platform, use_local)
# check_whitelist(real_urls)
# result = get_env_index(real_urls, repodata_fn)
# return result
# def get_env_index(channel_urls, repodata_fn="repodata.json"):
# threads = []
# result = []
# sddata = [FastSubdirData(Channel(x), idx, repodata_fn) for idx, x in enumerate(channel_urls)]
# for sd in sddata:
# t = threading.Thread(target=load_channel, args=(sd, result))
# threads.append(t)
# t.start()
# for t in threads:
# t.join()
# result = sorted(result, key=lambda x: x.channel_idx)
# return result
def to_package_record_from_subjson(subdir, pkg, jsn_string):
channel = subdir.channel

View File

@ -3,6 +3,7 @@
from setuptools import setup, Extension
from setuptools.command.build_ext import build_ext
import sys, os, platform
import pkgconfig
import setuptools
here = os.path.dirname(os.path.abspath(__file__))
@ -51,6 +52,12 @@ if sys.platform == 'win32':
except:
print("could not find conda prefix")
glib_config = pkgconfig.parse('glib-2.0')
print(glib_config)
glib_libs = pkgconfig.libs('glib-2.0')
glib_cflags = pkgconfig.cflags('glib-2.0')
ext_modules = [
Extension(
'mamba.mamba_api',
@ -58,10 +65,11 @@ ext_modules = [
include_dirs=[
get_pybind_include(),
get_pybind_include(user=True),
os.path.join(libsolv_prefix, 'include')
],
library_dirs=library_dir,
libraries=['solv', 'solvext'],
os.path.join(libsolv_prefix, 'include'),
"include/thirdparty/"
] + glib_config['include_dirs'],
library_dirs=library_dir + glib_config['library_dirs'],
libraries=['solv', 'solvext', 'repo', 'archive'] + glib_config['libraries'],
language='c++'
),
]