First import
Emulates gcc-config -l so far.
This commit is contained in:
commit
f58e278637
28 changed files with 1140 additions and 0 deletions
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
compile_commands.json
|
||||
tags
|
||||
build/
|
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
[submodule "subprojects/cxxopts"]
|
||||
path = subprojects/cxxopts
|
||||
url = https://github.com/jarro2783/cxxopts.git
|
14
meson.build
Normal file
14
meson.build
Normal file
|
@ -0,0 +1,14 @@
|
|||
project('user-gcc', 'cpp',
|
||||
version: '0.1.0',
|
||||
meson_version: '>=0.50.0',
|
||||
default_options: ['buildtype=debug', 'cpp_std=gnu++17']
|
||||
)
|
||||
|
||||
cxxopts_incl = include_directories('subprojects/cxxopts/include', is_system: true)
|
||||
|
||||
cpp = meson.get_compiler('cpp')
|
||||
conf = configuration_data()
|
||||
conf.set('PROJECT_NAME', meson.project_name())
|
||||
conf.set('PROJECT_VERSION', meson.project_version())
|
||||
|
||||
subdir('src')
|
83
src/chost.cpp
Normal file
83
src/chost.cpp
Normal file
|
@ -0,0 +1,83 @@
|
|||
#include "chost.hpp"
|
||||
#include "env.hpp"
|
||||
#include "run_cmd.hpp"
|
||||
#include "gentoofunctions.hpp"
|
||||
#include "replace_bash_vars.hpp"
|
||||
#include <stdexcept>
|
||||
#include <cassert>
|
||||
|
||||
namespace duck {
|
||||
namespace {
|
||||
std::string try_real_hard_to_find_CHOST() {
|
||||
assert(false);
|
||||
throw std::runtime_error("try_real_hard_to_find_CHOST() not implemented");
|
||||
//#
|
||||
//# First we read make.conf
|
||||
//#
|
||||
|
||||
//local varname=${1:-CHOST}
|
||||
//local conf=${EROOT}/etc/portage/make.conf
|
||||
//if [[ ! -e ${conf} && -e ${EROOT}/etc/make.conf ]] ; then
|
||||
// conf=${EROOT}/etc/make.conf
|
||||
//fi
|
||||
//local ret=$(source "${conf}" 2>/dev/null ; echo ${!varname})
|
||||
//if [[ -z ${ret} ]] ; then
|
||||
// # newer portage supports spaces between the var and =
|
||||
// # CHOST = "this-is-retarded"
|
||||
// ret=$(eval $(
|
||||
// ${SED} -n \
|
||||
// -e 's:[[:space:]]::g' \
|
||||
// -e "/^${varname}=/p" \
|
||||
// "${conf}"
|
||||
// ) ; echo ${!varname}
|
||||
// )
|
||||
//fi
|
||||
|
||||
//if [[ -n ${ret} ]] ; then
|
||||
// echo ${ret}
|
||||
// return 0
|
||||
//fi
|
||||
|
||||
//#
|
||||
//# Then we try /etc/env.d/gcc/config-${CTARGET}
|
||||
//#
|
||||
//if [[ -s ${EROOT}/etc/env.d/gcc/config-${CTARGET} ]] ; then
|
||||
// ret=$(split_gcc_ver $(show_var CURRENT "${EROOT}"/etc/env.d/gcc/config-${CTARGET}))
|
||||
// echo ${ret% *}
|
||||
//fi
|
||||
}
|
||||
} //unnamed namespace
|
||||
|
||||
std::string chost() {
|
||||
using std::string;
|
||||
|
||||
{
|
||||
// If it's set in the env, trust the setting. If it's wrong,
|
||||
// then that's the caller's problem.
|
||||
auto ret = env("CHOST");
|
||||
if (ret and not ret->empty())
|
||||
return string(*ret);
|
||||
}
|
||||
|
||||
const int python_ret = run_cmd_retcode("python -V &>/dev/null");
|
||||
std::string chost_str;
|
||||
|
||||
if (not python_ret) {
|
||||
chost_str = run_cmd("portageq", "envvar", "CHOST").out;
|
||||
}
|
||||
else {
|
||||
ewarn("Python seems to be broken, attempting to locate CHOST ourselves ...");
|
||||
chost_str = try_real_hard_to_find_CHOST();
|
||||
}
|
||||
|
||||
if (chost_str.empty()) {
|
||||
eerror(replace_bash_vars("${argv0}: Could not get portage CHOST!"));
|
||||
eerror(replace_bash_vars("${argv0}: You should verify that CHOST is set in one of these places:"));
|
||||
eerror(replace_bash_vars("${argv0}: - ${EROOT}/etc/portage/make.conf"));
|
||||
eerror(replace_bash_vars("${argv0}: - active environment"));
|
||||
throw std::runtime_error(replace_bash_vars("${argv0}: Could not get portage CHOST!"));
|
||||
}
|
||||
|
||||
return chost_str;
|
||||
}
|
||||
} //namespace duck
|
7
src/chost.hpp
Normal file
7
src/chost.hpp
Normal file
|
@ -0,0 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace duck {
|
||||
std::string chost();
|
||||
} //namespace duck
|
4
src/config.h.in
Normal file
4
src/config.h.in
Normal file
|
@ -0,0 +1,4 @@
|
|||
#pragma once
|
||||
|
||||
#define PROJECT_NAME "@PROJECT_NAME@"
|
||||
#define PROJECT_VERSION "@PROJECT_VERSION@"
|
76
src/env.cpp
Normal file
76
src/env.cpp
Normal file
|
@ -0,0 +1,76 @@
|
|||
#include "env.hpp"
|
||||
#include <unistd.h>
|
||||
#include <cassert>
|
||||
#include <stdexcept>
|
||||
|
||||
#if defined(__GNU_LIBRARY__) && ((__GLIBC__ == 2 && __GLIBC_MINOR__ >= 17) || __GLIBC__ > 2)
|
||||
# define HasSecureGetenv
|
||||
#endif
|
||||
|
||||
namespace duck {
|
||||
namespace {
|
||||
const char* raw_fetch_env (const char* name) {
|
||||
assert(name);
|
||||
if (not name)
|
||||
return nullptr;
|
||||
|
||||
#if defined(HasSecureGetenv)
|
||||
const char* const val_ptr = secure_getenv(name);
|
||||
#else
|
||||
const char* const val_ptr = getenv(name);
|
||||
#endif
|
||||
|
||||
return val_ptr;
|
||||
}
|
||||
|
||||
} //unnamed namespace
|
||||
|
||||
std::string_view env (const char* name, std::string_view def) noexcept {
|
||||
const auto ret = raw_fetch_env(name);
|
||||
return (ret ? std::string_view(ret) : def);
|
||||
}
|
||||
|
||||
std::optional<std::string_view> env (const char* name) noexcept {
|
||||
using optional = std::optional<std::string_view>;
|
||||
const auto ret = raw_fetch_env(name);
|
||||
return (ret ? optional{ret} : optional{});
|
||||
}
|
||||
|
||||
std::string_view env_throw (const char* name) {
|
||||
const auto ret = raw_fetch_env(name);
|
||||
if (ret) {
|
||||
return {ret};
|
||||
}
|
||||
else {
|
||||
if (name) {
|
||||
throw std::runtime_error(
|
||||
std::string("Environment variable \"") + name + "\" not set"
|
||||
);
|
||||
}
|
||||
else {
|
||||
throw std::runtime_error("Environment variable name is null");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool is_env_set (const char* name) noexcept {
|
||||
const auto ret = raw_fetch_env(name);
|
||||
return not not ret;
|
||||
}
|
||||
|
||||
std::string_view env (const std::string& name, std::string_view def) noexcept {
|
||||
return env(name.c_str(), def);
|
||||
}
|
||||
|
||||
std::optional<std::string_view> env (const std::string& name) noexcept {
|
||||
return env(name.c_str());
|
||||
}
|
||||
|
||||
std::string_view env_throw (const std::string& name) {
|
||||
return env_throw(name.c_str());
|
||||
}
|
||||
|
||||
bool is_env_set (const std::string& name) noexcept {
|
||||
return is_env_set(name.c_str());
|
||||
}
|
||||
} //namespace duck
|
19
src/env.hpp
Normal file
19
src/env.hpp
Normal file
|
@ -0,0 +1,19 @@
|
|||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
#include <string>
|
||||
|
||||
namespace duck {
|
||||
|
||||
std::string_view env (const char* name, std::string_view def) noexcept;
|
||||
std::optional<std::string_view> env (const char* name) noexcept;
|
||||
std::string_view env_throw (const char* name);
|
||||
bool is_env_set (const char* name) noexcept;
|
||||
|
||||
std::string_view env (const std::string& name, std::string_view def) noexcept;
|
||||
std::optional<std::string_view> env (const std::string& name) noexcept;
|
||||
std::string_view env_throw (const std::string& name);
|
||||
bool is_env_set (const std::string& name) noexcept;
|
||||
|
||||
} //namespace duck
|
153
src/gentoofunctions.cpp
Normal file
153
src/gentoofunctions.cpp
Normal file
|
@ -0,0 +1,153 @@
|
|||
#include "gentoofunctions.hpp"
|
||||
#include "env.hpp"
|
||||
#include "string_view_cat.hpp"
|
||||
#include "config.h"
|
||||
#include "run_cmd.hpp"
|
||||
#include <iostream>
|
||||
#if __cplusplus == 201703L
|
||||
# include <experimental/array>
|
||||
#else
|
||||
# error "If make_array() has been moved out of experimental please fix"
|
||||
#endif
|
||||
#include <algorithm>
|
||||
|
||||
namespace duck {
|
||||
namespace {
|
||||
std::string_view g_last_e_cmd;
|
||||
|
||||
enum YesNoIdk {
|
||||
No, Yes, Idk
|
||||
};
|
||||
|
||||
std::string lowercased (std::string_view in) {
|
||||
std::string retval('\0', in.size());
|
||||
std::transform(
|
||||
in.begin(),
|
||||
in.end(),
|
||||
retval.begin(),
|
||||
[](char c)->char{return std::tolower(c);}
|
||||
);
|
||||
return retval;
|
||||
}
|
||||
|
||||
template <typename I, typename T>
|
||||
bool has_item (I begin, const I& end, const T& val) {
|
||||
auto it = std::find(begin, end, val);
|
||||
return it != end;
|
||||
}
|
||||
|
||||
YesNoIdk yesno_impl (std::string_view val) {
|
||||
using std::experimental::make_array;
|
||||
using std::string_view;
|
||||
|
||||
auto lcval = lowercased(val);
|
||||
auto yesses = make_array<string_view>("yes", "true", "on", "1");
|
||||
auto noes = make_array<string_view>("no", "false", "off", "0");
|
||||
if (has_item(yesses.begin(), yesses.end(), lcval)) {
|
||||
return Yes;
|
||||
}
|
||||
else if (has_item(noes.begin(), noes.end(), lcval)) {
|
||||
return No;
|
||||
}
|
||||
else {
|
||||
return Idk;
|
||||
}
|
||||
}
|
||||
|
||||
void generic_eprint (
|
||||
std::ostream& stream,
|
||||
const char* quiet_var,
|
||||
const char* msg_prefix_var,
|
||||
std::string_view log_pri,
|
||||
std::string_view log_tag,
|
||||
std::string_view new_e_cmd,
|
||||
std::string_view msg,
|
||||
const char* newline
|
||||
) {
|
||||
if (yesno(env(quiet_var, ""))) {
|
||||
return;
|
||||
}
|
||||
else {
|
||||
if (not yesno(env("RC_ENDCOL", "")) and g_last_e_cmd == "ebegin") {
|
||||
stream << '\n';
|
||||
}
|
||||
stream << ' ' << env(msg_prefix_var, "") << '*'
|
||||
<< env("NORMAL", "") << ' ' << env("RC_INDENTATION", "")
|
||||
<< msg << newline;
|
||||
}
|
||||
|
||||
esyslog(log_pri, log_tag, msg);
|
||||
g_last_e_cmd = new_e_cmd;
|
||||
}
|
||||
} //unnamed namespace
|
||||
|
||||
void vewarn (std::string_view msg) {
|
||||
if (yesno(env("EINFO_VERBOSE", ""), true))
|
||||
ewarn(msg);
|
||||
}
|
||||
|
||||
void ewarn (std::string_view msg) {
|
||||
generic_eprint(
|
||||
std::clog,
|
||||
"EINFO_QUIET",
|
||||
"WARN",
|
||||
"daemon.warning",
|
||||
PROJECT_NAME,
|
||||
"ewarn",
|
||||
msg,
|
||||
"\n"
|
||||
);
|
||||
}
|
||||
|
||||
void esyslog(std::string_view pri, std::string_view tag, std::string_view msg) {
|
||||
if (not env("EINFO_LOG", "").empty() and not run_cmd_retcode("command -v logger > /dev/null 2>&1")) {
|
||||
if (msg.empty())
|
||||
return;
|
||||
|
||||
run_cmd("logger", "-p", pri, "-t", tag, "--", msg);
|
||||
}
|
||||
}
|
||||
|
||||
bool yesno (const std::string& var, bool force_quiet) {
|
||||
{
|
||||
const auto ret = yesno_impl(var);
|
||||
switch (ret) {
|
||||
case Yes: return true;
|
||||
case No: return false;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
auto val = env(var, "");
|
||||
if (val.empty())
|
||||
return false;
|
||||
|
||||
const auto ret = yesno_impl(val);
|
||||
switch (ret) {
|
||||
case Yes: return true;
|
||||
case No: return false;
|
||||
default:
|
||||
vewarn(std::string("\"") + var + "\" (\"" + val + "\") is not set properly");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool yesno (std::string_view var, bool force_quiet) {
|
||||
return yesno(to_string(var), force_quiet);
|
||||
}
|
||||
|
||||
void eerror (std::string_view msg) {
|
||||
generic_eprint(
|
||||
std::clog,
|
||||
"EERROR_QUIET",
|
||||
"BAD",
|
||||
"daemon.err",
|
||||
"rc-scripts",
|
||||
"eerrorn",
|
||||
msg,
|
||||
"\n"
|
||||
);
|
||||
}
|
||||
} //namespace duck
|
13
src/gentoofunctions.hpp
Normal file
13
src/gentoofunctions.hpp
Normal file
|
@ -0,0 +1,13 @@
|
|||
#pragma once
|
||||
|
||||
#include <string_view>
|
||||
#include <string>
|
||||
|
||||
namespace duck {
|
||||
void vewarn (std::string_view msg);
|
||||
void ewarn (std::string_view msg);
|
||||
bool yesno (const std::string& var, bool force_quiet=false);
|
||||
bool yesno (std::string_view var, bool force_quiet=false);
|
||||
void esyslog(std::string_view pri, std::string_view tag, std::string_view msg);
|
||||
void eerror (std::string_view msg);
|
||||
} //namespace duck
|
164
src/list_profiles.cpp
Normal file
164
src/list_profiles.cpp
Normal file
|
@ -0,0 +1,164 @@
|
|||
#include "list_profiles.hpp"
|
||||
#include "syspaths.hpp"
|
||||
#include "config.h"
|
||||
#include "chost.hpp"
|
||||
#include "to_var_map.hpp"
|
||||
#include "load_file.hpp"
|
||||
#include "string_view_cat.hpp"
|
||||
#include "gentoofunctions.hpp"
|
||||
#include <iostream>
|
||||
#include <filesystem>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#include <regex>
|
||||
|
||||
namespace duck {
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace {
|
||||
[[gnu::pure]]
|
||||
bool starts_with (std::string_view s, std::string_view prefix) {
|
||||
return (s.substr(0, std::min(prefix.size(), s.size())) == prefix);
|
||||
}
|
||||
|
||||
std::vector<int> decompose_version (const std::string& ver) {
|
||||
std::vector<int> ret;
|
||||
int curr = 0;
|
||||
bool push_last = false;
|
||||
for (char c : ver) {
|
||||
if (c >= '0' and c <= '9') {
|
||||
curr = (curr * 10) + (c - '0');
|
||||
push_last = true;
|
||||
}
|
||||
else if (push_last) {
|
||||
ret.push_back(curr);
|
||||
curr = 0;
|
||||
push_last = false;
|
||||
}
|
||||
}
|
||||
if (push_last)
|
||||
ret.push_back(curr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
[[gnu::pure]]
|
||||
bool version_comp (const std::string& a, const std::string& b) {
|
||||
std::vector<int> va(decompose_version(a));
|
||||
std::vector<int> vb(decompose_version(b));
|
||||
const std::size_t sz = std::max(va.size(), vb.size());
|
||||
va.resize(sz, 0);
|
||||
vb.resize(sz, 0);
|
||||
|
||||
for (std::size_t z = 0; z < sz; ++z) {
|
||||
if (va[z] < vb[z])
|
||||
return true;
|
||||
else if (va[z] > vb[z])
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string current_compiler (const fs::path& config_path, const std::string& ctarget) {
|
||||
if (fs::is_regular_file(config_path)) {
|
||||
auto text = load_file(config_path);
|
||||
auto var_map = to_var_map(text);
|
||||
auto found_it = var_map.find("CURRENT");
|
||||
if (var_map.end() == found_it)
|
||||
return {};
|
||||
else
|
||||
return to_string(found_it->second);
|
||||
}
|
||||
else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
} //unnamed namespace
|
||||
|
||||
std::vector<fs::path> version_sorted_paths (const fs::path path) {
|
||||
std::vector<fs::path> ret;
|
||||
for (const fs::directory_entry& curr : fs::directory_iterator(path)) {
|
||||
std::string filename = curr.path().filename();
|
||||
if (not starts_with(filename, "config") and curr.is_regular_file())
|
||||
ret.push_back(curr);
|
||||
}
|
||||
std::regex version_reg(
|
||||
R"(-(\d+(?:\.\d)*(?:r\d+)?))",
|
||||
std::regex_constants::ECMAScript | std::regex_constants::optimize
|
||||
);
|
||||
std::sort(
|
||||
ret.begin(),
|
||||
ret.end(),
|
||||
[&version_reg](const fs::path& a, const fs::path& b) {
|
||||
using std::regex_match;
|
||||
std::smatch ma, mb;
|
||||
std::string sa = a.filename();
|
||||
std::string sb = b.filename();
|
||||
return version_comp(
|
||||
(regex_search(sa, ma, version_reg) ? ma[1] : std::string("0")),
|
||||
(regex_search(sb, mb, version_reg) ? mb[1] : std::string("0"))
|
||||
);
|
||||
}
|
||||
);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void list_profiles(const SysPaths& sp) {
|
||||
using std::string;
|
||||
|
||||
if (sp.root() != "/") {
|
||||
//echo "Using gcc-config info in ${ROOT}"
|
||||
std::cout << "Using " << PROJECT_NAME << " info in " << sp.root() << '\n';
|
||||
}
|
||||
|
||||
std::string chost = duck::chost();
|
||||
const std::string& ctarget = chost;
|
||||
|
||||
std::string_view filter;
|
||||
auto config_path = sp.gcc_env_d() / ("config-" + ctarget);
|
||||
auto s = fs::status(config_path);
|
||||
if (not fs::is_regular_file(s)) {
|
||||
filter = ctarget;
|
||||
}
|
||||
|
||||
const std::string current = current_compiler(config_path, ctarget);
|
||||
std::string_view current_native(current);
|
||||
auto version_paths = version_sorted_paths(sp.gcc_env_d());
|
||||
std::string text;
|
||||
std::size_t z = 0;
|
||||
std::string target;
|
||||
for (const auto& ver_path : version_paths) {
|
||||
text = load_file(ver_path);
|
||||
auto var_map = to_var_map(text);
|
||||
auto curr_ctarget = var_map["CTARGET"];
|
||||
std::string filename = ver_path.filename();
|
||||
|
||||
if (curr_ctarget.empty()) {
|
||||
if (starts_with(filename, chost + "-"))
|
||||
curr_ctarget = chost;
|
||||
else
|
||||
ewarn("broken config file: " + std::string(ver_path));
|
||||
}
|
||||
|
||||
++z;
|
||||
if (not filter.empty() and filter != curr_ctarget)
|
||||
continue;
|
||||
if (target != curr_ctarget) {
|
||||
if (z > 1)
|
||||
std::cout << '\n';
|
||||
target = to_string(curr_ctarget);
|
||||
curr_ctarget = "";
|
||||
}
|
||||
|
||||
std::cout << " [" << z << "] " << filename;
|
||||
auto colours = sp.colours();
|
||||
if (filename == current_native) {
|
||||
std::cout << ' ' << colours["GOOD"] << '*' << colours["NORMAL"];
|
||||
}
|
||||
else if (fs::is_regular_file(sp.gcc_env_d() / ("config-" + target)) and filename == current) {
|
||||
std::cout << ' ' << colours["HILITE"] << '*' << colours["NORMAL"];
|
||||
}
|
||||
std::cout << '\n';
|
||||
}
|
||||
}
|
||||
} //namespace duck
|
7
src/list_profiles.hpp
Normal file
7
src/list_profiles.hpp
Normal file
|
@ -0,0 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
namespace duck {
|
||||
class SysPaths;
|
||||
|
||||
void list_profiles(const SysPaths& syspaths);
|
||||
} //namespace duck
|
14
src/load_file.cpp
Normal file
14
src/load_file.cpp
Normal file
|
@ -0,0 +1,14 @@
|
|||
#include "load_file.hpp"
|
||||
#include <fstream>
|
||||
|
||||
namespace duck {
|
||||
std::string load_file (const std::string& path) {
|
||||
std::ifstream ifs(path);
|
||||
ifs >> std::noskipws;
|
||||
|
||||
return std::string(
|
||||
std::istreambuf_iterator<char>(ifs),
|
||||
std::istreambuf_iterator<char>()
|
||||
);
|
||||
}
|
||||
} //namespace duck
|
7
src/load_file.hpp
Normal file
7
src/load_file.hpp
Normal file
|
@ -0,0 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace duck {
|
||||
std::string load_file (const std::string& path);
|
||||
} //namespace duck
|
50
src/main.cpp
Normal file
50
src/main.cpp
Normal file
|
@ -0,0 +1,50 @@
|
|||
#include <iostream>
|
||||
#include <cxxopts.hpp>
|
||||
#include <string>
|
||||
#include "config.h"
|
||||
#include "list_profiles.hpp"
|
||||
#include "syspaths.hpp"
|
||||
|
||||
namespace {
|
||||
const char g_simple_colours[] =
|
||||
"GOOD=\033[32;01m\n"
|
||||
"WARN=\033[33;01m\n"
|
||||
"BAD=\033[31;01m\n"
|
||||
"HILITE=\033[36;01m\n"
|
||||
"BRACKET=\033[34;01m\n"
|
||||
"NORMAL=\033[0m\n"
|
||||
;
|
||||
} //unnamed namespace
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
using std::string;
|
||||
cxxopts::Options options(PROJECT_NAME, "Change the current compiler profile, or give info about profiles");
|
||||
options.add_options()
|
||||
("h,help", "Show this help and quit")
|
||||
("version", "Print version info")
|
||||
("l,list-profiles", "Print a list of available profiles") //, cxxopts::value<string>()->default_value(""))
|
||||
;
|
||||
const int orig_argc = argc;
|
||||
auto command = options.parse(argc, argv);
|
||||
|
||||
if (command.count("help") or 1 == orig_argc) {
|
||||
std::cout << options.help() << std::endl;
|
||||
return 0;
|
||||
}
|
||||
if (command.count("version")) {
|
||||
std::cout << PROJECT_NAME << " v" << PROJECT_VERSION << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
duck::SysPaths syspaths;
|
||||
syspaths.set_colours(g_simple_colours);
|
||||
#if !defined(NDEBUG)
|
||||
std::cout << syspaths << '\n';
|
||||
#endif
|
||||
|
||||
if (command.count("list-profiles")) {
|
||||
duck::list_profiles(syspaths);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
26
src/meson.build
Normal file
26
src/meson.build
Normal file
|
@ -0,0 +1,26 @@
|
|||
fslib_dep = cpp.find_library('stdc++fs', required: false)
|
||||
cpp.has_header('pstream.h', required: true)
|
||||
|
||||
project_config_file = configure_file(
|
||||
input: 'config.h.in',
|
||||
output: 'config.h',
|
||||
configuration: conf
|
||||
)
|
||||
|
||||
executable(meson.project_name(),
|
||||
'main.cpp',
|
||||
'env.cpp',
|
||||
project_config_file,
|
||||
'list_profiles.cpp',
|
||||
'syspaths.cpp',
|
||||
'chost.cpp',
|
||||
'run_cmd.cpp',
|
||||
'gentoofunctions.cpp',
|
||||
'replace_bash_vars.cpp',
|
||||
'to_var_map.cpp',
|
||||
'load_file.cpp',
|
||||
'var_map.cpp',
|
||||
install: true,
|
||||
dependencies: [fslib_dep],
|
||||
include_directories: [cxxopts_incl],
|
||||
)
|
94
src/replace_bash_vars.cpp
Normal file
94
src/replace_bash_vars.cpp
Normal file
|
@ -0,0 +1,94 @@
|
|||
#include "replace_bash_vars.hpp"
|
||||
#include "config.h"
|
||||
#include "env.hpp"
|
||||
#include "string_view_cat.hpp"
|
||||
#include <cassert>
|
||||
#include <cctype>
|
||||
|
||||
namespace duck {
|
||||
namespace {
|
||||
struct VarInfo {
|
||||
std::string name;
|
||||
std::size_t orig_len{};
|
||||
};
|
||||
|
||||
//std::string replaced_all (std::string text, std::string_view search, std::string_view repl) {
|
||||
// std::size_t pos = 0;
|
||||
// while ((pos = text.find(search, pos)) != std::string::npos) {
|
||||
// text.replace(pos, search.size(), repl);
|
||||
// pos += repl.size();
|
||||
// }
|
||||
// return text;
|
||||
//}
|
||||
|
||||
VarInfo extract_var_name (std::string_view text) {
|
||||
assert(not text.empty());
|
||||
assert(text.front() == '$');
|
||||
if (text.size() == 1) {
|
||||
return {};
|
||||
}
|
||||
else if (text.size() == 2) {
|
||||
if (std::isalnum(text[1]))
|
||||
return {to_string(text.substr(1, 1)), 2};
|
||||
return {};
|
||||
}
|
||||
else {
|
||||
assert(text.size() >= 3);
|
||||
if (text[1] == '{') {
|
||||
const auto end = text.find('}', 2);
|
||||
if (text.npos == end) {
|
||||
return {};
|
||||
}
|
||||
else {
|
||||
std::string_view token(text.substr(2, end - 2));
|
||||
std::string retval(token.empty() || token.front() != '!' ? to_string(token) : to_string(token.substr(1)));
|
||||
if (not token.empty() and token.front() == '!') {
|
||||
return {to_string(env(retval, "")), end + 1};
|
||||
}
|
||||
else {
|
||||
return {retval, end + 1};
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
std::size_t z;
|
||||
for (z = 1; z < text.size() && std::isalnum(text[z]); ++z) {
|
||||
}
|
||||
return {to_string(text.substr(1, z - 1)), z + 1};
|
||||
}
|
||||
}
|
||||
}
|
||||
} //unnamed namespace
|
||||
|
||||
std::string replace_bash_vars (std::string_view text) {
|
||||
std::size_t pos = 0;
|
||||
std::size_t prev_start = 0;
|
||||
std::string rtext;
|
||||
rtext.reserve(text.size());
|
||||
|
||||
while ((pos = text.find('$', pos)) != std::string::npos) {
|
||||
if (0 == pos or rtext[pos-1] != '\\') {
|
||||
auto var = extract_var_name(text.substr(pos));
|
||||
if (not var.orig_len) {
|
||||
++pos;
|
||||
continue;
|
||||
}
|
||||
|
||||
assert(prev_start <= pos);
|
||||
rtext.append(text.begin() + prev_start, text.begin() + pos);
|
||||
if (var.name == "argv0")
|
||||
rtext += PROJECT_NAME;
|
||||
else
|
||||
rtext += env(var.name, "");
|
||||
pos += var.orig_len;
|
||||
prev_start = pos;
|
||||
}
|
||||
else {
|
||||
++pos;
|
||||
}
|
||||
}
|
||||
|
||||
rtext.append(text.begin() + prev_start, text.end());
|
||||
return rtext;
|
||||
}
|
||||
} //namespace duck
|
8
src/replace_bash_vars.hpp
Normal file
8
src/replace_bash_vars.hpp
Normal file
|
@ -0,0 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
#include <string_view>
|
||||
#include <string>
|
||||
|
||||
namespace duck {
|
||||
std::string replace_bash_vars (std::string_view text);
|
||||
} //namespace duck
|
74
src/run_cmd.cpp
Normal file
74
src/run_cmd.cpp
Normal file
|
@ -0,0 +1,74 @@
|
|||
#include "run_cmd.hpp"
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#if !defined(NDEBUG)
|
||||
# include <iostream>
|
||||
# include <algorithm>
|
||||
#endif
|
||||
#include <stdexcept>
|
||||
#include <memory>
|
||||
#include <pstream.h>
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
|
||||
namespace duck::detail {
|
||||
namespace {
|
||||
void trim_eol (std::string& val) {
|
||||
const auto pos = val.find_last_not_of('\n');
|
||||
if (val.npos != pos)
|
||||
val.resize(pos + 1);
|
||||
}
|
||||
} //unnamed namespace
|
||||
|
||||
RunCmdOutput run_cmd(const std::string& cmd, const std::vector<std::string>& argv) {
|
||||
assert(not argv.empty());
|
||||
assert(argv.front() == cmd);
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
std::cout << "Running command: " << cmd;
|
||||
std::for_each(argv.begin(), argv.end(), [](const auto& s){std::cout << ' ' << s;});
|
||||
std::cout << '\n';
|
||||
#endif
|
||||
|
||||
const redi::pstream::pmode mode = redi::pstreams::pstdout | redi::pstreams::pstderr;
|
||||
redi::ipstream ips(cmd, argv, mode);
|
||||
|
||||
std::streamsize n;
|
||||
RunCmdOutput ret;
|
||||
std::array<char, 64> buf;
|
||||
std::array<bool, 2> finished {false, false};
|
||||
|
||||
while (not finished[0] or not finished[1]) {
|
||||
if (not finished[0]) {
|
||||
while ((n = ips.err().readsome(buf.data(), buf.size())) > 0)
|
||||
ret.err.append(buf.begin(), buf.begin() + n);
|
||||
if (ips.eof()) {
|
||||
finished[0] = true;
|
||||
if (not finished[1])
|
||||
ips.clear();
|
||||
}
|
||||
}
|
||||
|
||||
if (!finished[1]) {
|
||||
while ((n = ips.out().readsome(buf.data(), buf.size())) > 0)
|
||||
ret.out.append(buf.begin(), buf.begin() + n);
|
||||
if (ips.eof()) {
|
||||
finished[1] = true;
|
||||
if (!finished[0])
|
||||
ips.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trim_eol(ret.out);
|
||||
trim_eol(ret.err);
|
||||
return ret;
|
||||
}
|
||||
} //namespace detail::duck
|
||||
|
||||
namespace duck {
|
||||
int run_cmd_retcode (const char* command) {
|
||||
assert(command);
|
||||
return system(command);
|
||||
}
|
||||
} //namespace duck
|
31
src/run_cmd.hpp
Normal file
31
src/run_cmd.hpp
Normal file
|
@ -0,0 +1,31 @@
|
|||
#pragma once
|
||||
|
||||
#include "string_view_cat.hpp"
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace duck {
|
||||
struct RunCmdOutput {
|
||||
std::string out;
|
||||
std::string err;
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
RunCmdOutput run_cmd(const std::string& cmd, const std::vector<std::string>& argv);
|
||||
} //namespace detail
|
||||
|
||||
template <typename... Args>
|
||||
inline RunCmdOutput run_cmd (std::string command, Args&&... args) {
|
||||
using duck::to_string;
|
||||
|
||||
std::vector<std::string> argv {
|
||||
command,
|
||||
to_string(std::forward<Args>(args))...
|
||||
};
|
||||
return detail::run_cmd(command, argv);
|
||||
}
|
||||
|
||||
int run_cmd_retcode (const char* command);
|
||||
} //namespace duck
|
39
src/string_view_cat.hpp
Normal file
39
src/string_view_cat.hpp
Normal file
|
@ -0,0 +1,39 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
namespace duck {
|
||||
inline std::string to_string (std::string_view sv) {
|
||||
return std::string((sv.empty() ? static_cast<const char*>("") : sv.data()), sv.size());
|
||||
}
|
||||
|
||||
inline std::string to_string (const char* str) {
|
||||
return std::string(str ? str : static_cast<const char*>(""));
|
||||
}
|
||||
|
||||
inline std::string to_string (std::string str) {
|
||||
return str;
|
||||
}
|
||||
|
||||
inline std::string operator+ (std::string_view a, std::string_view b) {
|
||||
std::string ret(to_string(a));
|
||||
ret += b;
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline std::string operator+ (std::string_view a, const std::string& b) {
|
||||
std::string ret(to_string(a));
|
||||
ret += b;
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline std::string operator+ (std::string a, std::string_view b) {
|
||||
a += b;
|
||||
return a;
|
||||
}
|
||||
|
||||
inline std::string operator+ (const char* a, std::string_view b) {
|
||||
return to_string(a) + b;
|
||||
}
|
||||
} //namespace duck
|
68
src/syspaths.cpp
Normal file
68
src/syspaths.cpp
Normal file
|
@ -0,0 +1,68 @@
|
|||
#include "syspaths.hpp"
|
||||
#include "env.hpp"
|
||||
#include "to_var_map.hpp"
|
||||
#include <unistd.h>
|
||||
|
||||
namespace duck {
|
||||
namespace {
|
||||
std::string fix_eprefix(std::string_view p) {
|
||||
if (p.size() >= 2 and p.front() == '@' and p.back() == '@')
|
||||
return "";
|
||||
else
|
||||
return std::string(p);
|
||||
}
|
||||
|
||||
std::string_view remove_front_slash(std::string_view text) {
|
||||
const std::size_t pos = text.find_first_not_of('/');
|
||||
return (text.npos == pos ? text : text.substr(pos));
|
||||
}
|
||||
} //unnamed namespace
|
||||
|
||||
SysPaths::SysPaths() :
|
||||
m_env(to_var_map_ignore_ownership(environ)),
|
||||
m_root(duck::env("ROOT", "/")),
|
||||
m_eprefix(fix_eprefix(duck::env("EPREFIX", ""))),
|
||||
m_eroot(m_root / remove_front_slash(std::string(m_eprefix))),
|
||||
m_env_d(m_eroot / "etc/env.d"),
|
||||
m_gcc_env_d(m_env_d / "gcc")
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void SysPaths::set_colours (std::string_view colours) {
|
||||
m_colours = to_var_map_ignore_ownership(colours);
|
||||
}
|
||||
|
||||
auto SysPaths::root() const -> const path_type& {
|
||||
return m_root;
|
||||
}
|
||||
|
||||
auto SysPaths::eroot() const -> const path_type& {
|
||||
return m_eroot;
|
||||
}
|
||||
|
||||
auto SysPaths::eprefix() const -> const path_type& {
|
||||
return m_eprefix;
|
||||
}
|
||||
|
||||
auto SysPaths::env_d() const -> const path_type& {
|
||||
return m_env_d;
|
||||
}
|
||||
|
||||
auto SysPaths::gcc_env_d() const -> const path_type& {
|
||||
return m_gcc_env_d;
|
||||
}
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
std::ostream& operator<< (std::ostream& oss, const SysPaths& sp) {
|
||||
oss << "SysPaths {\n" <<
|
||||
"\tROOT: " << sp.root() << '\n' <<
|
||||
"\tEPREFIX: " << sp.eprefix() << '\n' <<
|
||||
"\tEROOT: " << sp.eroot() << '\n' <<
|
||||
"\tENV_D: " << sp.env_d() << '\n' <<
|
||||
"\tGCC_ENV_D: " << sp.gcc_env_d() << '\n' <<
|
||||
'}';
|
||||
return oss;
|
||||
}
|
||||
#endif
|
||||
} //namespace duck
|
43
src/syspaths.hpp
Normal file
43
src/syspaths.hpp
Normal file
|
@ -0,0 +1,43 @@
|
|||
#pragma once
|
||||
|
||||
#include "var_map.hpp"
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#if !defined(NDEBUG)
|
||||
# include <iostream>
|
||||
#endif
|
||||
#include <unordered_map>
|
||||
#include <string_view>
|
||||
|
||||
namespace duck {
|
||||
class SysPaths {
|
||||
public:
|
||||
typedef std::filesystem::path path_type;
|
||||
|
||||
SysPaths();
|
||||
~SysPaths() noexcept = default;
|
||||
|
||||
void set_colours (std::string_view colours);
|
||||
VarMapView colours() const { return {&m_colours}; }
|
||||
VarMapView env() const { return {&m_env}; }
|
||||
|
||||
const path_type& root() const;
|
||||
const path_type& eroot() const;
|
||||
const path_type& eprefix() const;
|
||||
const path_type& env_d() const;
|
||||
const path_type& gcc_env_d() const;
|
||||
|
||||
private:
|
||||
VarMap m_env;
|
||||
VarMap m_colours;
|
||||
path_type m_root;
|
||||
path_type m_eprefix;
|
||||
path_type m_eroot;
|
||||
path_type m_env_d;
|
||||
path_type m_gcc_env_d;
|
||||
};
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
std::ostream& operator<< (std::ostream&, const SysPaths& sp);
|
||||
#endif
|
||||
} //namespace duck
|
87
src/to_var_map.cpp
Normal file
87
src/to_var_map.cpp
Normal file
|
@ -0,0 +1,87 @@
|
|||
#include "to_var_map.hpp"
|
||||
#include "string_view_cat.hpp"
|
||||
#include <stdexcept>
|
||||
#include <cassert>
|
||||
#include <cctype>
|
||||
#include <algorithm>
|
||||
|
||||
namespace duck {
|
||||
namespace {
|
||||
std::string_view trimmed (std::string_view text) {
|
||||
using std::find_if_not;
|
||||
|
||||
auto isspace_func = [](char c) {return std::isspace(c);};
|
||||
const auto new_start_it = find_if_not(text.cbegin(), text.cend(), isspace_func);
|
||||
const std::size_t new_start = (new_start_it == text.cend() ? text.size() : static_cast<std::size_t>(new_start_it - text.cbegin()));
|
||||
|
||||
const auto new_end_it = find_if_not(text.crbegin(), text.crend(), isspace_func);
|
||||
const std::size_t new_end = text.size() - (new_end_it == text.crend() ? std::size_t{} : static_cast<std::size_t>(new_end_it - text.crbegin()));
|
||||
|
||||
assert(new_end <= text.size());
|
||||
assert(new_end >= new_start);
|
||||
return text.substr(new_start, new_end - new_start);
|
||||
}
|
||||
|
||||
std::pair<std::string_view, std::string_view> split_key_val(std::string_view text) {
|
||||
const auto eq_pos = text.find('=');
|
||||
if (eq_pos == text.npos) {
|
||||
throw std::runtime_error("Unrecognised line \"" + text + "\": expected key=value format not found");
|
||||
}
|
||||
|
||||
return std::make_pair(
|
||||
text.substr(0, eq_pos),
|
||||
text.substr(eq_pos + 1)
|
||||
);
|
||||
}
|
||||
|
||||
std::string_view unquoted (std::string_view text) {
|
||||
std::string_view ret = trimmed(text);
|
||||
if (ret.size() < 2)
|
||||
return ret;
|
||||
else if (ret.front() == '"' and ret.back() == '"')
|
||||
return ret.substr(1, ret.size() - 2);
|
||||
else
|
||||
return ret;
|
||||
}
|
||||
|
||||
void add_key_val_text_to_map (std::string_view text, VarMap& out) {
|
||||
std::string_view line = trimmed(text);
|
||||
if (line.empty() or line.front() == '#')
|
||||
return;
|
||||
|
||||
auto new_item = split_key_val(line);
|
||||
out[new_item.first] = unquoted(new_item.second);
|
||||
}
|
||||
} //unnamed namespace
|
||||
|
||||
VarMap to_var_map (std::string& text) {
|
||||
return to_var_map_ignore_ownership(text);
|
||||
}
|
||||
|
||||
VarMap to_var_map_ignore_ownership (std::string_view text) {
|
||||
VarMap ret;
|
||||
std::size_t pos = 0;
|
||||
std::size_t start = 0;
|
||||
while ((pos = text.find('\n', pos)) != text.npos) {
|
||||
add_key_val_text_to_map(text.substr(start, pos - start), ret);
|
||||
start = pos + 1;
|
||||
++pos;
|
||||
}
|
||||
|
||||
add_key_val_text_to_map(text.substr(start), ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
VarMap to_var_map_ignore_ownership (char** env) {
|
||||
assert(env);
|
||||
|
||||
VarMap ret;
|
||||
std::size_t z = 0;
|
||||
while (env[z]) {
|
||||
std::string_view line(env[z]);
|
||||
add_key_val_text_to_map(line, ret);
|
||||
++z;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
} //namespace duck
|
13
src/to_var_map.hpp
Normal file
13
src/to_var_map.hpp
Normal file
|
@ -0,0 +1,13 @@
|
|||
#pragma once
|
||||
|
||||
#include "var_map.hpp"
|
||||
#include <string>
|
||||
|
||||
namespace duck {
|
||||
//parameter is non-const so caller hopefully remembers to keep it alive
|
||||
//(ie: they can't just pass a temporary in)
|
||||
VarMap to_var_map (std::string& text);
|
||||
|
||||
VarMap to_var_map_ignore_ownership (std::string_view text);
|
||||
VarMap to_var_map_ignore_ownership (char** env);
|
||||
} //namespace duck
|
22
src/var_map.cpp
Normal file
22
src/var_map.cpp
Normal file
|
@ -0,0 +1,22 @@
|
|||
#include "var_map.hpp"
|
||||
#include <cassert>
|
||||
|
||||
namespace duck {
|
||||
VarMapView::VarMapView (const VarMap* vm) :
|
||||
m_map(vm)
|
||||
{
|
||||
assert(m_map);
|
||||
}
|
||||
|
||||
std::string_view VarMapView::operator[] (std::string_view key) const {
|
||||
if (key.empty())
|
||||
return "";
|
||||
|
||||
assert(m_map);
|
||||
auto it_found = m_map->find(key);
|
||||
if (m_map->end() != it_found)
|
||||
return it_found->second;
|
||||
else
|
||||
return "";
|
||||
}
|
||||
} //namespace duck
|
17
src/var_map.hpp
Normal file
17
src/var_map.hpp
Normal file
|
@ -0,0 +1,17 @@
|
|||
#pragma once
|
||||
|
||||
#include <unordered_map>
|
||||
#include <string_view>
|
||||
|
||||
namespace duck {
|
||||
typedef std::unordered_map<std::string_view, std::string_view> VarMap;
|
||||
|
||||
class VarMapView {
|
||||
public:
|
||||
VarMapView (const VarMap* vm);
|
||||
std::string_view operator[] (std::string_view key) const;
|
||||
|
||||
private:
|
||||
const VarMap* m_map;
|
||||
};
|
||||
} //namespace duck
|
1
subprojects/cxxopts
Submodule
1
subprojects/cxxopts
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit b0f67a06de3446aa97a4943ad0ad6086460b2b61
|
Loading…
Reference in a new issue