mirror of https://github.com/XS-MLVP/picker.git
592 lines
19 KiB
C++
592 lines
19 KiB
C++
|
|
#pragma once
|
|
|
|
#include <sys/time.h>
|
|
#include <chrono>
|
|
#include <regex>
|
|
#include <set>
|
|
#include <unistd.h>
|
|
#include <filesystem>
|
|
#include <functional>
|
|
#include "type.hpp"
|
|
#include "json.hpp"
|
|
#include "inja.hpp"
|
|
#include "CLI11.hpp"
|
|
#include "version.hpp"
|
|
#include "appimage/configuration.hpp"
|
|
|
|
extern std::map<std::string, std::string> lang_lib_map;
|
|
extern std::map<std::string, std::string> display_names;
|
|
|
|
namespace picker {
|
|
|
|
extern bool is_debug;
|
|
extern char *lib_random_hash;
|
|
|
|
#define PK_OUTPUT(o, fmt, ...) \
|
|
{ \
|
|
fprintf(o, fmt, ##__VA_ARGS__); \
|
|
}
|
|
#define PK_MESSAGE(fmt, ...) \
|
|
{ \
|
|
PK_OUTPUT(stdout, fmt, ##__VA_ARGS__); \
|
|
PK_OUTPUT(stdout, "%s\n", ""); \
|
|
}
|
|
#define PK_ERROR(fmt, ...) \
|
|
{ \
|
|
PK_OUTPUT(stderr, "%s", "\033[31m") \
|
|
PK_OUTPUT(stderr, fmt, ##__VA_ARGS__) \
|
|
PK_OUTPUT(stderr, "%s\n", "\033[0m") \
|
|
}
|
|
#define PK_DEBUG(fmt, ...) \
|
|
{ \
|
|
if (is_debug) { \
|
|
PK_OUTPUT(stderr, "%s", "debug> "); \
|
|
PK_OUTPUT(stderr, fmt, ##__VA_ARGS__); \
|
|
PK_OUTPUT(stderr, "%s\n", "") \
|
|
} \
|
|
}
|
|
#define PK_FATAL(fmt, ...) \
|
|
{ \
|
|
PK_OUTPUT(stderr, "%sFatal: ", "\033[31m") \
|
|
PK_OUTPUT(stderr, fmt, ##__VA_ARGS__); \
|
|
PK_OUTPUT(stderr, "%s\n", "\033[0m") \
|
|
exit(-1); \
|
|
}
|
|
|
|
inline void vassert(bool c, std::string msg = "")
|
|
{
|
|
if (!c) { PK_FATAL("Assert error: %s", msg.c_str()); }
|
|
}
|
|
|
|
inline long vtime()
|
|
{
|
|
struct timeval tv;
|
|
gettimeofday(&tv, NULL);
|
|
return tv.tv_sec * 1000000 + tv.tv_usec;
|
|
}
|
|
|
|
inline std::string fmtnow(std::string fmt = "%Y-%m-%d %H:%M:%S")
|
|
{
|
|
std::time_t t = std::time(nullptr);
|
|
std::tm tm = *std::localtime(&t);
|
|
std::stringstream buffer;
|
|
buffer << std::put_time(&tm, fmt.c_str());
|
|
return buffer.str();
|
|
}
|
|
|
|
inline std::string exec(const char *cmd)
|
|
{
|
|
struct PipeCloser {
|
|
void operator()(FILE *pipe) const
|
|
{
|
|
if (pipe != nullptr) { (void)pclose(pipe); }
|
|
}
|
|
};
|
|
|
|
std::array<char, 128> buffer;
|
|
std::string result;
|
|
std::unique_ptr<FILE, PipeCloser> pipe(popen(cmd, "r"));
|
|
if (!pipe) { throw std::runtime_error("popen() failed!"); }
|
|
while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) { result += buffer.data(); }
|
|
return result;
|
|
}
|
|
|
|
inline std::string trim(std::string s, std::string p)
|
|
{
|
|
if (s.empty()) { return s; }
|
|
s.erase(0, s.find_first_not_of(p));
|
|
size_t pos = s.find_last_not_of(p);
|
|
if (pos != std::string::npos) { s.erase(pos + 1); }
|
|
return s;
|
|
}
|
|
|
|
inline std::string ltrim(std::string s)
|
|
{
|
|
if (s.empty()) { return s; }
|
|
s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) { return !std::isspace(ch); }));
|
|
return s;
|
|
}
|
|
|
|
inline std::string rtrim(std::string s)
|
|
{
|
|
if (s.empty()) { return s; }
|
|
s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) { return !std::isspace(ch); }).base(), s.end());
|
|
return s;
|
|
}
|
|
|
|
inline std::string trim(std::string s)
|
|
{
|
|
if (s.empty()) { return s; }
|
|
return rtrim(ltrim(s));
|
|
}
|
|
|
|
inline std::vector<std::string> strsplit(std::string str, std::string s = " ")
|
|
{
|
|
std::cout << "strsplit: '" << str << "' by '" << s << "'" << std::endl;
|
|
std::vector<std::string> ret = {};
|
|
if (s.empty()) {
|
|
auto token = trim(str);
|
|
if (!token.empty()) ret.push_back(token);
|
|
return ret;
|
|
}
|
|
size_t start = 0;
|
|
size_t end = str.find(s);
|
|
while (end != std::string::npos) {
|
|
auto sub = str.substr(start, end - start);
|
|
sub = trim(sub);
|
|
if (!sub.empty()) ret.push_back(sub);
|
|
start = end + s.size();
|
|
end = str.find(s, start);
|
|
}
|
|
auto sub = str.substr(start);
|
|
sub = trim(sub);
|
|
if (!sub.empty()) ret.push_back(sub);
|
|
return ret;
|
|
}
|
|
|
|
inline std::string lower_case(std::string input)
|
|
{
|
|
auto str = input;
|
|
std::transform(str.begin(), str.end(), str.begin(), [](unsigned char c) { return std::tolower(c); });
|
|
return str;
|
|
}
|
|
|
|
inline std::string capitalize_first_letter(const std::string &input)
|
|
{
|
|
if (input.empty()) { return input; }
|
|
|
|
std::string result = input;
|
|
result[0] = std::toupper(result[0]);
|
|
return result;
|
|
}
|
|
|
|
// Correctly spelled helpers (preferred)
|
|
inline std::map<std::string, std::string> get_default_conflict_map()
|
|
{
|
|
std::map<std::string, std::string> pin_map;
|
|
std::vector<std::string> conflict_pin = {"xclock", "xport", "dut", "initclock",
|
|
"step", "stepris", "stepfal", "setwaveform",
|
|
"setcoverage", "finish", "refreshcomb"};
|
|
for (auto &p : conflict_pin) { pin_map[p] = "pin_"; }
|
|
return pin_map;
|
|
}
|
|
|
|
// Backward-compatible wrapper with previous misspelling
|
|
inline std::map<std::string, std::string> get_default_confilct_map()
|
|
{
|
|
return get_default_conflict_map();
|
|
}
|
|
|
|
inline std::string fix_conflict_pin_name(const std::string &in, std::map<std::string, std::string> &pin_map,
|
|
bool cap_first = false)
|
|
{
|
|
auto input = lower_case(in);
|
|
if (input.empty()) { PK_FATAL("input pin name is empty"); }
|
|
// not find in conflict map
|
|
if (pin_map.find(input) == pin_map.end()) {
|
|
if (cap_first) { return capitalize_first_letter(in); }
|
|
return in;
|
|
}
|
|
std::string result = pin_map[input] + in;
|
|
if (cap_first) { result = capitalize_first_letter(result); }
|
|
return result;
|
|
}
|
|
|
|
inline std::string strsplit(std::string str, std::initializer_list<std::string> s, bool front = true)
|
|
{
|
|
auto ret = str;
|
|
for (auto &v : s) {
|
|
if (front) {
|
|
ret = strsplit(ret, v).front();
|
|
} else {
|
|
ret = strsplit(ret, v).back();
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
inline bool strconatins(std::string str, std::string subs)
|
|
{
|
|
return str.find(subs) != std::string::npos;
|
|
}
|
|
|
|
// Correctly spelled alias
|
|
inline bool str_contains(const std::string &str, const std::string &subs)
|
|
{
|
|
return str.find(subs) != std::string::npos;
|
|
}
|
|
|
|
inline std::string streplace(std::string str, std::string s, std::string t = "")
|
|
{
|
|
std::string input = str;
|
|
size_t index = 0;
|
|
while (true) {
|
|
index = input.find(s, index);
|
|
if (index == std::string::npos) break;
|
|
input.replace(index, s.length(), t);
|
|
index += t.length();
|
|
}
|
|
return input;
|
|
}
|
|
|
|
inline std::string streplace(std::string str, std::initializer_list<std::string> s, std::string t = "")
|
|
{
|
|
auto ret = str;
|
|
for (auto &v : s) { ret = streplace(ret, v, t); }
|
|
return ret;
|
|
}
|
|
|
|
inline std::string str_replace_all(std::string str, const std::string &from, const std::string &to)
|
|
{
|
|
size_t start_pos = 0;
|
|
while ((start_pos = str.find(from, start_pos)) != std::string::npos) {
|
|
str.replace(start_pos, from.length(), to);
|
|
start_pos += to.length(); // Handles case where 'to' is a substring of 'from'
|
|
}
|
|
return str;
|
|
}
|
|
|
|
template <typename T>
|
|
inline bool contains(const std::vector<T> &v, const T &a)
|
|
{
|
|
for (const T &x : v) {
|
|
if (x == a) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Backward-compatible wrapper with previous misspelling
|
|
template <typename T>
|
|
inline bool contians(const std::vector<T> &v, const T &a)
|
|
{
|
|
return contains(v, a);
|
|
}
|
|
|
|
template <typename T, typename F>
|
|
inline std::vector<T> key_as_vector(std::map<T, F> &input)
|
|
{
|
|
std::vector<T> keys;
|
|
for (const auto &pair : input) { keys.push_back(pair.first); }
|
|
return keys;
|
|
}
|
|
|
|
inline std::string join_str_vec(const std::vector<std::string> &strings, const std::string &delimiter)
|
|
{
|
|
if (strings.empty()) return "";
|
|
std::string result = strings[0];
|
|
for (size_t i = 1; i < strings.size(); ++i) { result += delimiter + strings[i]; }
|
|
return result;
|
|
}
|
|
|
|
inline bool file_exists(std::string f)
|
|
{
|
|
return access(f.c_str(), F_OK) == 0;
|
|
}
|
|
|
|
inline std::string exec_result(std::string cmd, int index)
|
|
{
|
|
auto result = strsplit(exec(cmd.c_str()));
|
|
if (result.size() <= index) { return ""; }
|
|
return result[index];
|
|
}
|
|
|
|
inline std::string first_uppercase(const std::string &input)
|
|
{
|
|
return capitalize_first_letter(input);
|
|
}
|
|
|
|
// Backward-compatible wrapper with previous misspelling
|
|
inline std::string first_upercase(const std::string &input)
|
|
{
|
|
return first_uppercase(input);
|
|
}
|
|
|
|
inline std::string base_name(std::string const &path, bool with_subfix = true)
|
|
{
|
|
auto fname = path.substr(path.find_last_of("/\\") + 1);
|
|
if (with_subfix) { return fname; }
|
|
return strsplit(fname, ".")[0];
|
|
}
|
|
|
|
inline std::string get_path(std::string str)
|
|
{
|
|
auto found = str.find_last_of("/\\");
|
|
return str.substr(0, found);
|
|
}
|
|
|
|
inline std::string normal_path(std::string path)
|
|
{
|
|
return std::filesystem::absolute(path).lexically_normal().string();
|
|
}
|
|
|
|
inline std::string path_join(std::initializer_list<std::string> args)
|
|
{
|
|
std::string path, value;
|
|
auto size = args.size();
|
|
for (auto p : args) {
|
|
if (path.empty()) {
|
|
path = p;
|
|
path.erase(path.find_last_not_of("/\\") + 1);
|
|
continue;
|
|
}
|
|
auto v = trim(p, "/");
|
|
v = trim(v, "\\");
|
|
path.append("/" + v);
|
|
}
|
|
return normal_path(path);
|
|
}
|
|
|
|
inline std::string current_path()
|
|
{
|
|
return std::filesystem::current_path().string();
|
|
}
|
|
|
|
template <typename... Args>
|
|
std::string sfmt(const std::string &format, Args... args)
|
|
{
|
|
int size_s = std::snprintf(nullptr, 0, format.c_str(), args...) + 1; // Extra space for '\0'
|
|
if (size_s <= 0) { throw std::runtime_error("Error during formatting."); }
|
|
auto size = static_cast<size_t>(size_s);
|
|
std::unique_ptr<char[]> buf(new char[size]);
|
|
std::snprintf(buf.get(), size, format.c_str(), args...);
|
|
return std::string(buf.get(),
|
|
buf.get() + size - 1); // We don't want the '\0' inside
|
|
}
|
|
|
|
inline std::string version()
|
|
{
|
|
return "v0.1";
|
|
}
|
|
|
|
inline std::string find_file(std::vector<std::string> fs)
|
|
{
|
|
for (auto f : fs) {
|
|
if (file_exists(f)) {
|
|
PK_DEBUG("find file: %s", f.c_str());
|
|
return f;
|
|
}
|
|
}
|
|
vassert(false, "search file fail");
|
|
return "";
|
|
}
|
|
|
|
inline bool write_file(std::string fname, std::string text)
|
|
{
|
|
PK_MESSAGE("Write file: %s", fname.c_str());
|
|
std::ofstream ofile(fname, std::ios::trunc);
|
|
vassert(ofile.is_open(), "write file(" + fname + ") error");
|
|
ofile << text;
|
|
ofile.close();
|
|
return true;
|
|
}
|
|
|
|
inline std::string read_file(std::string fname)
|
|
{
|
|
std::ifstream ifile(fname, std::ios::in | std::ios::binary);
|
|
if (ifile) {
|
|
std::string content;
|
|
ifile.seekg(0, std::ios::end);
|
|
content.resize(ifile.tellg());
|
|
ifile.seekg(0, std::ios::beg);
|
|
ifile.read(&content[0], content.size());
|
|
ifile.close();
|
|
return content;
|
|
}
|
|
throw(errno);
|
|
}
|
|
|
|
inline std::string read_params(std::string fname)
|
|
{
|
|
PK_MESSAGE("Read params from: %s", fname.c_str());
|
|
std::ifstream ifile(fname);
|
|
std::string ret;
|
|
if (ifile.is_open()) {
|
|
std::string line_txt;
|
|
while (std::getline(ifile, line_txt)) {
|
|
// Remove comments (anything after '#') and trim
|
|
auto hash_pos = line_txt.find('#');
|
|
if (hash_pos != std::string::npos) { line_txt.erase(hash_pos); }
|
|
line_txt = trim(line_txt);
|
|
if (line_txt.empty()) { continue; }
|
|
if (ret.empty()) {
|
|
ret = line_txt;
|
|
} else {
|
|
ret = ret + " " + line_txt;
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
};
|
|
|
|
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) && !defined(__CYGWIN__)
|
|
#include <windows.h>
|
|
|
|
inline std::string get_executable_path()
|
|
{
|
|
char buffer[MAX_PATH];
|
|
GetModuleFileName(NULL, buffer, MAX_PATH);
|
|
return std::string(buffer);
|
|
}
|
|
|
|
#elif defined(__APPLE__)
|
|
#include <mach-o/dyld.h>
|
|
|
|
inline std::string get_executable_path()
|
|
{
|
|
char buffer[1024];
|
|
uint32_t size = sizeof(buffer);
|
|
if (_NSGetExecutablePath(buffer, &size) == 0)
|
|
return std::string(buffer);
|
|
else
|
|
return std::string("");
|
|
}
|
|
|
|
#elif defined(__linux__)
|
|
#include <unistd.h>
|
|
|
|
inline std::string get_executable_path()
|
|
{
|
|
char buffer[1024];
|
|
ssize_t len = ::readlink("/proc/self/exe", buffer, sizeof(buffer) - 1);
|
|
if (len != -1) {
|
|
buffer[len] = '\0';
|
|
return std::string(buffer);
|
|
} else {
|
|
return std::string("");
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
inline bool str_start_with(std::string str, std::string prefix)
|
|
{
|
|
return str.find(prefix) == 0;
|
|
}
|
|
|
|
inline bool str_start_with_digit(const std::string &str)
|
|
{
|
|
if (str.empty()) return false;
|
|
return std::isdigit(str[0]);
|
|
}
|
|
|
|
inline std::string get_target_path_from(std::string base, std::vector<std::string> paths)
|
|
{
|
|
auto path = get_executable_path();
|
|
for (auto &l : paths) {
|
|
auto p = std::string(l);
|
|
if (!str_start_with(p, "/")) { p = path_join({base, p}); }
|
|
if (file_exists(p)) { return p; }
|
|
}
|
|
return "";
|
|
}
|
|
|
|
inline std::string get_target_path_from(std::string base, std::initializer_list<std::string> paths)
|
|
{
|
|
return get_target_path_from(base, std::vector<std::string>(paths));
|
|
}
|
|
|
|
inline std::string get_xcomm_location()
|
|
{
|
|
if (appimage::is_running_as_appimage()) {
|
|
// If running as AppImage, use the appimage configuration to get the xcomm location
|
|
return appimage::get_picker_lib("");
|
|
}
|
|
auto path = get_executable_path();
|
|
return get_target_path_from(path, {"../../dependence/xcomm", // 1. search in build dir (for dev)
|
|
"./picker", // 2. search in current dir
|
|
"../../share/picker", // 3. search in share dir (for install)
|
|
"/usr/local/share/picker", // 4. search in /usr/local/share
|
|
"/usr/share/picker", // 5. search in /usr/share
|
|
"/etc/picker"}); // 6. search in /etc
|
|
}
|
|
|
|
inline std::string get_xcomm_lib(std::string lib_name, std::string &message)
|
|
{
|
|
auto path = get_xcomm_location();
|
|
if (path.empty()) {
|
|
message = "xcomm lib not found";
|
|
return "";
|
|
}
|
|
auto lib = path_join({path, lib_name});
|
|
if (file_exists(lib)) { return lib; }
|
|
message = lib + " not found";
|
|
return "";
|
|
}
|
|
|
|
inline std::string get_template_path()
|
|
{
|
|
if (appimage::is_running_as_appimage()) {
|
|
// If running as AppImage, use the appimage configuration to get the template path
|
|
return appimage::get_template_path();
|
|
}
|
|
auto path = get_executable_path();
|
|
auto tmp = get_target_path_from(path, {"../../../template", // 1. search in source dir (for dev)
|
|
"./template", // 2. search in current dir
|
|
"../../share/picker/template", // 3. search in share dir (for install)
|
|
"/usr/local/share/picker/template", // 4. search in /usr/local/share
|
|
"/etc/picker/template"}); // 5. search in /etc
|
|
if (!tmp.empty()) { return tmp; }
|
|
PK_FATAL("template not found, please check the installation or manually set the source dir path");
|
|
}
|
|
inline std::string extract_name(const std::string &input, char delimiter, int isFirst)
|
|
{
|
|
size_t pos = input.find(delimiter);
|
|
if (pos != std::string::npos) {
|
|
if (isFirst == 1) {
|
|
return input.substr(0, pos);
|
|
} else {
|
|
return input.substr(pos + 1);
|
|
}
|
|
}
|
|
return input; // 如果没有找到分隔符,则返回整个字符串
|
|
}
|
|
|
|
inline std::string get_env(std::string key, std::string default_value = "")
|
|
{
|
|
const char *value = std::getenv(key.c_str());
|
|
if (value) {
|
|
return std::string(value);
|
|
} else {
|
|
return default_value;
|
|
}
|
|
}
|
|
|
|
inline std::string getCurrentTime()
|
|
{
|
|
auto now = std::chrono::system_clock::now();
|
|
std::time_t now_c = std::chrono::system_clock::to_time_t(now);
|
|
std::tm now_tm = *std::localtime(&now_c);
|
|
std::ostringstream oss;
|
|
oss << std::put_time(&now_tm, "%Y-%m-%d %H:%M:%S");
|
|
return oss.str();
|
|
}
|
|
|
|
inline std::string get_hostname()
|
|
{
|
|
char hostname[1024];
|
|
gethostname(hostname, sizeof(hostname));
|
|
return std::string(hostname);
|
|
}
|
|
|
|
inline std::string get_node_uuid()
|
|
{
|
|
std::hash<std::string> hasher;
|
|
return std::to_string(hasher(get_hostname() + std::to_string(getpid())));
|
|
}
|
|
|
|
inline std::string get_shared_lib_suffix()
|
|
{
|
|
#if defined(_WIN32) || defined(_WIN64)
|
|
return "dll";
|
|
#elif defined(__linux__)
|
|
return "so";
|
|
#elif defined(__APPLE__)
|
|
return "dylib";
|
|
#else
|
|
return "";
|
|
#endif
|
|
}
|
|
|
|
} // namespace picker
|