mirror of https://github.com/mamba-org/mamba.git
parallel downloads with librepo
This commit is contained in:
parent
72fbadb3ef
commit
78589fbfae
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
};
|
|
@ -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()
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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 };
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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_
|
|
@ -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
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
16
setup.py
16
setup.py
|
@ -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++'
|
||||
),
|
||||
]
|
||||
|
|
Loading…
Reference in New Issue