summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/argparser.h477
-rw-r--r--src/build.cc2
-rw-r--r--src/configure.cc308
-rw-r--r--src/ctor.h5
-rw-r--r--src/execute.cc5
m---------src/getoptpp0
-rw-r--r--src/libctor.cc207
-rw-r--r--src/rebuild.cc139
-rw-r--r--src/rebuild.h18
-rw-r--r--src/tasks.cc7
-rw-r--r--src/tasks.h3
-rw-r--r--src/util.cc48
-rw-r--r--src/util.h12
13 files changed, 920 insertions, 311 deletions
diff --git a/src/argparser.h b/src/argparser.h
new file mode 100644
index 0000000..c5337e0
--- /dev/null
+++ b/src/argparser.h
@@ -0,0 +1,477 @@
+// -*- c++ -*-
+// Distributed under the BSD 2-Clause License.
+// See accompanying file LICENSE for details.
+#pragma once
+
+#include <functional>
+#include <variant>
+#include <optional>
+#include <string>
+#include <type_traits>
+#include <stdexcept>
+#include <iostream>
+#include <limits>
+
+namespace arg
+{
+struct noarg {};
+template<typename T>
+struct Opt
+{
+ char shortopt;
+ std::string longopt;
+ std::function<int(T)> cb;
+ std::string help;
+ T t{};
+};
+
+template<>
+struct Opt<noarg>
+{
+ char shortopt;
+ std::string longopt;
+ std::function<int()> cb;
+ std::string help;
+ noarg t{};
+};
+
+template<typename Callable, typename... Args>
+auto call_if(Callable cb, Args... args)
+{
+ using Ret = std::invoke_result_t<decltype(cb), Args&&...>;
+ if constexpr (std::is_same_v<Ret, void>)
+ {
+ if(cb)
+ {
+ return cb(std::forward<Args>(args)...);
+ }
+ }
+ else
+ {
+ if(cb)
+ {
+ return cb(std::forward<Args>(args)...);
+ }
+ return Ret{};
+ }
+}
+
+enum class error
+{
+ missing_arg,
+ invalid_arg,
+ invalid_opt,
+};
+
+template<typename... Ts>
+class Parser
+{
+public:
+ struct missing_arg{};
+
+ Parser(int argc_, const char* const* argv_)
+ : argc(argc_)
+ , argv(argv_)
+ {
+ }
+
+ int parse() const
+ {
+ bool demarcate{false};
+ for(int i = 1; i < argc; ++i) // skip argv[0] which is program name
+ {
+ std::string_view arg{argv[i]};
+ if(arg.size() == 0)
+ {
+ // Empty arg - This shouldn't happen
+ continue;
+ }
+
+ if(arg[0] != '-' || demarcate) // positional arg
+ {
+ auto res = call_if(pos_cb, arg);
+ if(res != 0)
+ {
+ return res;
+ }
+ continue;
+ }
+
+ if(arg == "--")
+ {
+ demarcate = true;
+ continue;
+ }
+
+ bool was_handled{false};
+ enum class state { handled, unhandled };
+
+ if(arg.size() > 1 && arg[0] == '-' && arg[1] == '-') // long
+ {
+ for(const auto& option : options)
+ {
+ auto ret =
+ std::visit([&](auto&& opt) -> std::pair<int, state>
+ {
+ if(opt.longopt != arg &&
+ !arg.starts_with(opt.longopt+'='))
+ {
+ return {0, state::unhandled};
+ }
+ try
+ {
+ using T = std::decay_t<decltype(opt)>;
+ if constexpr (std::is_same_v<T, Opt<noarg>>)
+ {
+ return {opt.cb(), state::handled};
+ }
+ else
+ {
+ return {opt.cb(convert(i, opt.t)),
+ state::handled};
+ }
+ }
+ catch(std::invalid_argument&)
+ {
+ call_if(err_cb, error::invalid_arg, argv[i]);
+ return {1, state::handled};
+ }
+ catch(missing_arg&)
+ {
+ call_if(err_cb, error::missing_arg, argv[i]);
+ return {1, state::handled};
+ }
+ }, option);
+ if(ret.second == state::handled && ret.first != 0)
+ {
+ return ret.first;
+ }
+ was_handled |= ret.second == state::handled;
+ if(was_handled)
+ {
+ break;
+ }
+ }
+ }
+ else
+ if(arg.size() > 1 && arg[0] == '-') // short
+ {
+ for(auto index = 1u; index < arg.size(); ++index)
+ {
+ was_handled = false;
+ for(const auto& option : options)
+ {
+ auto ret =
+ std::visit([&](auto&& opt) -> std::pair<int, state>
+ {
+ char c = arg[index];
+ if(opt.shortopt != c)
+ {
+ return {0, state::unhandled};
+ }
+ try
+ {
+ using T = std::decay_t<decltype(opt)>;
+ if constexpr (std::is_same_v<T, Opt<noarg>>)
+ {
+ return {opt.cb(), state::handled};
+ }
+ else
+ {
+ // Note: the rest of arg is converted to opt
+ auto idx = index;
+ // set index out of range all was eaten as arg
+ index = std::numeric_limits<int>::max();
+ return {opt.cb(convert_short(&arg[idx],
+ i, opt.t)),
+ state::handled};
+ }
+ }
+ catch(std::invalid_argument&)
+ {
+ call_if(err_cb, error::invalid_arg, argv[i]);
+ return {1, state::handled};
+ }
+ catch(missing_arg&)
+ {
+ call_if(err_cb, error::missing_arg, argv[i]);
+ return {1, state::handled};
+ }
+ }, option);
+ if(ret.second == state::handled && ret.first != 0)
+ {
+ return ret.first;
+ }
+ was_handled |= ret.second == state::handled;
+ if(was_handled)
+ {
+ break;
+ }
+ }
+ }
+ }
+
+ if(!was_handled)
+ {
+ call_if(err_cb, error::invalid_opt, arg);
+ return 1;
+ }
+ }
+ return 0;
+ }
+
+ template<typename T>
+ void add(char shortopt,
+ const std::string& longopt,
+ std::function<int(T)> cb,
+ const std::string& help)
+ {
+ options.emplace_back(Opt<T>{shortopt, longopt, cb, help});
+ }
+
+ void add(char shortopt,
+ const std::string& longopt,
+ std::function<int()> cb,
+ const std::string& help)
+ {
+ options.emplace_back(Opt<noarg>{shortopt, longopt, cb, help});
+ }
+
+ void set_pos_cb(std::function<int(std::string_view)> cb)
+ {
+ pos_cb = cb;
+ }
+
+ void set_err_cb(std::function<void(error, std::string_view)> cb)
+ {
+ err_cb = cb;
+ }
+
+ std::string prog_name() const
+ {
+ if(argc < 1)
+ {
+ return {};
+ }
+ return argv[0];
+ }
+
+ void help() const
+ {
+ constexpr std::size_t width{26};
+ constexpr std::size_t column_width{80};
+
+ for(const auto& option : options)
+ {
+ std::visit(
+ [&](auto&& opt)
+ {
+ std::string _args;
+ using T = std::decay_t<decltype(opt)>;
+ if constexpr (std::is_same_v<T, Opt<noarg>>)
+ {
+ }
+ else if constexpr (std::is_same_v<T, Opt<int>>)
+ {
+ _args = "<int>";
+ }
+ else if constexpr (std::is_same_v<T, Opt<std::optional<int>>>)
+ {
+ _args = "[int]";
+ }
+ else if constexpr (std::is_same_v<T, Opt<std::string>>)
+ {
+ _args = "<str>";
+ }
+ else if constexpr (std::is_same_v<T, Opt<std::optional<std::string>>>)
+ {
+ _args = "[str]";
+ }
+ else if constexpr (std::is_same_v<T, Opt<double>>)
+ {
+ _args = "<real>";
+ }
+ else if constexpr (std::is_same_v<T, Opt<std::optional<double>>>)
+ {
+ _args = "[real]";
+ }
+ else
+ {
+ static_assert(std::is_same_v<T, void>, "missing");
+ }
+
+ std::string option_str;
+ if(opt.shortopt != '\0' && !opt.longopt.empty())
+ {
+ option_str = " -" + std::string(1, opt.shortopt) + ", " +
+ opt.longopt + " " + _args;
+ }
+ else if(opt.shortopt != '\0')
+ {
+ option_str = " -" + std::string(1, opt.shortopt) + _args;
+ }
+ else if(!opt.longopt.empty())
+ {
+ option_str = " " + std::string(opt.longopt) + " " + _args;
+ }
+
+ std::string padding;
+ if(option_str.size() < width)
+ {
+ padding.append(width - option_str.size(), ' ');
+ }
+ else
+ {
+ padding = "\n";
+ padding.append(width, ' ');
+ }
+
+ std::cout << option_str << padding;
+
+ auto i = width;
+ for(auto c : opt.help)
+ {
+ if((c == '\n') || (i > column_width && (c == ' ' || c == '\t')))
+ {
+ std::string _padding(width, ' ');
+ std::cout << '\n' << _padding;
+ i = width;
+ continue;
+ }
+ std::cout << c;
+ ++i;
+ }
+ std::cout << '\n';
+ }, option);
+ }
+ }
+
+private:
+ template<typename T>
+ T convert(int& i, T) const
+ {
+ auto opt = convert(i, std::optional<T>{});
+ if(!opt)
+ {
+ throw missing_arg{};
+ }
+ return *opt;
+ }
+
+ template<typename T>
+ std::optional<T> convert(int& i, std::optional<T>) const
+ {
+ std::string arg;
+ bool has_arg{false};
+ std::string opt = argv[i];
+ if(opt.starts_with("--"))
+ {
+ // long opt
+ auto equals_pos = opt.find('=');
+ if(equals_pos != std::string::npos)
+ {
+ arg = opt.substr(equals_pos + 1);
+ has_arg = true;
+ }
+ else if(i+1 < argc)
+ {
+ arg = argv[i+1];
+ has_arg = !arg.starts_with("-");
+ if(has_arg)
+ {
+ ++i;
+ }
+ }
+ }
+
+ if(!has_arg)
+ {
+ return {};
+ }
+
+ if constexpr (std::is_same_v<T, int>)
+ {
+ return std::stoi(arg);
+ }
+ else if constexpr (std::is_same_v<T, double>)
+ {
+ return std::stod(arg);
+ }
+ else if constexpr (std::is_same_v<T, std::string>)
+ {
+ return arg;
+ }
+ else
+ {
+ static_assert(std::is_same_v<T, void>, "missing");
+ }
+ return {};
+ }
+
+ template<typename T>
+ T convert_short(const char* arg_, int& i, T) const
+ {
+ auto opt = convert_short(arg_, i, std::optional<T>{}, false);
+ if(!opt)
+ {
+ throw missing_arg{};
+ }
+ return *opt;
+ }
+
+ template<typename T>
+ std::optional<T> convert_short(const char* arg_, int& i,
+ std::optional<T>, bool optional = true) const
+ {
+ std::string arg;
+ bool has_arg{false};
+ std::string opt = arg_;
+ if(opt.length() > 1)
+ {
+ // arg in same token
+ arg = opt.substr(1);
+ has_arg = true;
+ }
+ else if(!optional && i+1 < argc)
+ {
+ arg = argv[i+1];
+ has_arg = true;//!arg.starts_with("-");
+ if(has_arg)
+ {
+ ++i;
+ }
+ }
+
+ if(!has_arg)
+ {
+ return {};
+ }
+
+ if constexpr (std::is_same_v<T, int>)
+ {
+ return std::stoi(arg);
+ }
+ else if constexpr (std::is_same_v<T, double>)
+ {
+ return std::stod(arg);
+ }
+ else if constexpr (std::is_same_v<T, std::string>)
+ {
+ return arg;
+ }
+ else
+ {
+ static_assert(std::is_same_v<T, void>, "missing");
+ }
+ return {};
+ }
+
+ using Opts = std::variant<Opt<noarg>, Opt<Ts>...>;
+ std::vector<Opts> options;
+ std::function<int(std::string_view)> pos_cb;
+ int argc;
+ const char* const* argv;
+ std::function<void(error, std::string_view)> err_cb;
+};
+
+} // arg::
diff --git a/src/build.cc b/src/build.cc
index a31f6a5..5995fb7 100644
--- a/src/build.cc
+++ b/src/build.cc
@@ -65,7 +65,7 @@ int build(const ctor::settings& settings,
break;
}
- auto task = getNextTask(all_tasks, dirtyTasks);
+ auto task = getNextTask(settings, all_tasks, dirtyTasks);
if(task == nullptr)
{
if(processes.empty() && !dirtyTasks.empty())
diff --git a/src/configure.cc b/src/configure.cc
index a43152f..910b878 100644
--- a/src/configure.cc
+++ b/src/configure.cc
@@ -8,8 +8,7 @@
#include <fstream>
#include <optional>
#include <span>
-
-#include <getoptpp/getoptpp.hpp>
+#include <cstring>
#include "execute.h"
#include "ctor.h"
@@ -18,6 +17,7 @@
#include "externals.h"
#include "tools.h"
#include "util.h"
+#include "argparser.h"
const std::filesystem::path configurationFile("configuration.cc");
const std::filesystem::path configHeaderFile("config.h");
@@ -32,11 +32,7 @@ const ctor::configuration& __attribute__((weak)) ctor::get_configuration()
if(!initialised)
{
std::string cxx_prog{"c++"};
- auto cxx_env = std::getenv("CXX");
- if(cxx_env)
- {
- cxx_prog = cxx_env;
- }
+ get_env("CXX", cxx_prog);
cfg.build_toolchain = getToolChain(cfg.get(ctor::cfg::build_cxx, cxx_prog));
@@ -100,11 +96,32 @@ std::string ctor::configuration::get(const std::string& key,
return ctor::conf_values[key];
}
- if(has(key))
+ if(tools.find(key) != tools.end())
{
return tools.at(key);
}
+ std::string value;
+ if(key == ctor::cfg::build_cxx && get_env("CXX", value))
+ {
+ return value;
+ }
+
+ if(key == ctor::cfg::build_cc && get_env("CC", value))
+ {
+ return value;
+ }
+
+ if(key == ctor::cfg::build_ld && get_env("LD", value))
+ {
+ return value;
+ }
+
+ if(key == ctor::cfg::build_ar && get_env("AR", value))
+ {
+ return value;
+ }
+
return default_value;
}
@@ -116,10 +133,9 @@ std::string ctor::configuration::getenv(const std::string& key) const
return envit->second;
}
- auto sysenv = std::getenv(key.data());
- if(sysenv)
+ if(std::string value; get_env(key.data(), value))
{
- return sysenv;
+ return value;
}
return {};
@@ -132,20 +148,16 @@ public:
Args(const std::vector<std::string>& args)
{
resize(args.size() + 1);
- (*this)[0] = strdup("./ctor");
+ owning_container.push_back("./ctor");
+ (*this)[0] = owning_container.back().data();
for(std::size_t i = 0; i < size() - 1; ++i)
{
- (*this)[i + 1] = strdup(args[i].data());
+ owning_container.push_back(args[i]);
+ (*this)[i + 1] = owning_container.back().data();
}
}
- ~Args()
- {
- for(std::size_t i = 0; i < size(); ++i)
- {
- free((*this)[i]);
- }
- }
+ std::deque<std::string> owning_container;
};
namespace {
@@ -236,8 +248,7 @@ int regenerateCache(ctor::settings& settings,
{
Args vargs(args);
- dg::Options opt;
- int key{128};
+ arg::Parser<std::string> opt(static_cast<int>(vargs.size()), vargs.data());
std::string build_arch_prefix;
std::string build_path;
@@ -251,97 +262,110 @@ int regenerateCache(ctor::settings& settings,
std::string ctor_libdir;
std::string builddir;
- opt.add("build-dir", required_argument, 'b',
- "Set output directory for build files (default: '" +
- settings.builddir + "').",
- [&]() {
- settings.builddir = optarg;
- builddir = optarg;
+ opt.add('b', "--build-dir",
+ std::function([&](std::string arg)
+ {
+ settings.builddir = arg;
+ builddir = arg;
return 0;
- });
+ }),
+ "Set output directory for build files (default: '" +
+ settings.builddir + "').");
- opt.add("verbose", no_argument, 'v',
- "Be verbose. Add multiple times for more verbosity.",
- [&]() {
+ opt.add('v', "--verbose",
+ std::function([&]()
+ {
settings.verbose++;
return 0;
- });
+ }),
+ "Be verbose. Add multiple times for more verbosity.");
- opt.add("cc", required_argument, key++,
- "Use specified c-compiler instead of gcc.",
- [&]() {
- cc_prog = optarg;
+ opt.add({}, "--cc",
+ std::function([&](std::string arg)
+ {
+ cc_prog = arg;
return 0;
- });
+ }),
+ "Use specified c-compiler instead of gcc.");
- opt.add("cxx", required_argument, key++,
- "Use specified c++-compiler instead of g++.",
- [&]() {
- cxx_prog = optarg;
+ opt.add({}, "--cxx",
+ std::function([&](std::string arg)
+ {
+ cxx_prog = arg;
return 0;
- });
+ }),
+ "Use specified c++-compiler instead of g++.");
- opt.add("ar", required_argument, key++,
- "Use specified archiver instead of ar.",
- [&]() {
- ar_prog = optarg;
+ opt.add({}, "--ar",
+ std::function([&](std::string arg)
+ {
+ ar_prog = arg;
return 0;
- });
+ }),
+ "Use specified archiver instead of ar.");
- opt.add("ld", required_argument, key++,
- "Use specified linker instead of ld.",
- [&]() {
- ld_prog = optarg;
+ opt.add({}, "--ld",
+ std::function([&](std::string arg)
+ {
+ ld_prog = arg;
return 0;
- });
+ }),
+ "Use specified linker instead of ld.");
- opt.add("build", required_argument, key++,
- "Configure for building on specified architecture.",
- [&]() {
- build_arch_prefix = optarg;
+ opt.add({}, "--build",
+ std::function([&](std::string arg)
+ {
+ build_arch_prefix = arg;
return 0;
- });
+ }),
+ "Configure for building on specified architecture.");
- opt.add("build-path", required_argument, key++,
- "Set path to build tool-chain.",
- [&]() {
- build_path = optarg;
+ opt.add({}, "--build-path",
+ std::function([&](std::string arg)
+ {
+ build_path = arg;
return 0;
- });
+ }),
+ "Set path to build tool-chain.");
- opt.add("host", required_argument, key++,
- "Cross-compile to build programs to run on specified architecture.",
- [&]() {
- host_arch_prefix = optarg;
+ opt.add({}, "--host",
+ std::function([&](std::string arg)
+ {
+ host_arch_prefix = arg;
return 0;
- });
+ }),
+ "Cross-compile to build programs to run on specified architecture.");
- opt.add("host-path", required_argument, key++,
- "Set path to cross-compile tool-chain.",
- [&]() {
- host_path = optarg;
+ opt.add({}, "--host-path",
+ std::function([&](std::string arg)
+ {
+ host_path = arg;
return 0;
- });
+ }),
+ "Set path to cross-compile tool-chain.");
- opt.add("ctor-includedir", required_argument, key++,
- "Set path to ctor header file, used for re-compiling.",
- [&]() {
- ctor_includedir = optarg;
+ opt.add({}, "--ctor-includedir",
+ std::function([&](std::string arg)
+ {
+ ctor_includedir = arg;
return 0;
- });
+ }),
+ "Set path to ctor header file, used for re-compiling.");
- opt.add("ctor-libdir", required_argument, key++,
- "Set path to ctor library file, used for re-compiling.",
- [&]() {
- ctor_libdir = optarg;
+ opt.add({}, "--ctor-libdir",
+ std::function([&](std::string arg)
+ {
+ ctor_libdir = arg;
return 0;
- });
+ }),
+ "Set path to ctor library file, used for re-compiling.");
// Resolv externals
ctor::external_configurations externalConfigs;
- for(std::size_t i = 0; i < numExternalConfigFiles; ++i)
+ const auto& externalConfigFiles = getExternalConfigFileList();
+ for(const auto& externalConfigFile : externalConfigFiles)
{
- auto newExternalConfigs = externalConfigFiles[i].cb(settings);
+ auto newExternalConfigs = externalConfigFile.cb(settings);
externalConfigs.insert(externalConfigs.end(),
newExternalConfigs.begin(),
newExternalConfigs.end());
@@ -350,19 +374,21 @@ int regenerateCache(ctor::settings& settings,
auto add_path_args =
[&](const std::string& arg_name)
{
- opt.add(arg_name + "-includedir", required_argument, key++,
- "Set path to " + arg_name + " header file.",
- [&]() {
- external_includedir[arg_name] = optarg;
+ opt.add({}, "--" + arg_name + "-includedir",
+ std::function([&](std::string arg)
+ {
+ external_includedir[arg_name] = arg;
return 0;
- });
+ }),
+ "Set path to " + arg_name + " header file.");
- opt.add(arg_name + "-libdir", required_argument, key++,
- "Set path to " + arg_name + " libraries.",
- [&]() {
- external_libdir[arg_name] = optarg;
+ opt.add({}, "--" + arg_name + "-libdir",
+ std::function([&](std::string arg)
+ {
+ external_libdir[arg_name] = arg;
return 0;
- });
+ }),
+ "Set path to " + arg_name + " libraries.");
};
for(const auto& ext : externalConfigs)
@@ -382,17 +408,57 @@ int regenerateCache(ctor::settings& settings,
}
- opt.add("help", no_argument, 'h',
- "Print this help text.",
- [&]() -> int {
+ opt.add('h', "--help",
+ std::function([&]() -> int
+ {
std::cout << "Configure how to build with " << name << "\n";
std::cout << "Usage: " << name << " configure [options]\n\n";
std::cout << "Options:\n";
opt.help();
exit(0);
- });
+ }),
+ "Print this help text.");
+
+ opt.set_err_cb(
+ [&](arg::error err, std::string_view arg)
+ {
+ switch(err)
+ {
+ case arg::error::invalid_arg:
+ std::cerr << opt.prog_name() <<
+ ": invalid argument for option '" << arg << "'\n";
+ std::cerr << "Type '" << opt.prog_name() <<
+ " -h' for more information.\n";
+ break;
+
+ case arg::error::missing_arg:
+ std::cerr << opt.prog_name() << ": option requires and argument '" <<
+ arg << "'\n";
+ std::cerr << "Type '" << opt.prog_name() <<
+ " -h' for more information.\n";
+ break;
- opt.process(static_cast<int>(vargs.size()), vargs.data());
+ case arg::error::invalid_opt:
+ std::cerr << opt.prog_name() << ": invalid option '" << arg << "'\n";
+ std::cerr << "Type '" << opt.prog_name() <<
+ " -h' for more information.\n";
+ break;
+ }
+ });
+
+ opt.set_pos_cb(
+ [&](std::string_view)
+ {
+ std::cerr <<
+ "The configure subcommand doesn't use positional arguments.\n";
+ return 1;
+ });
+
+ auto res = opt.parse();
+ if(res != 0)
+ {
+ return res;
+ }
if(host_arch_prefix.empty())
{
@@ -676,6 +742,7 @@ int regenerateCache(ctor::settings& settings,
{
ctor::conf_values[ctor::cfg::builddir] = builddir;
}
+
ctor::conf_values[ctor::cfg::host_cxx] = host_cxx;
ctor::conf_values[ctor::cfg::build_cxx] = build_cxx;
@@ -858,52 +925,45 @@ int configure(const ctor::settings& global_settings, int argc, char* argv[])
}
std::map<std::string, std::string> env;
- auto cc_env = getenv("CC");
- if(cc_env)
+ std::string value;
+ if(get_env("CC", value))
{
- env["CC"] = cc_env;
+ env["CC"] = value;
}
- auto cflags_env = getenv("CFLAGS");
- if(cflags_env)
+ if(get_env("CFLAGS", value))
{
- env["CFLAGS"] = cflags_env;
+ env["CFLAGS"] = value;
}
- auto cxx_env = getenv("CXX");
- if(cxx_env)
+ if(get_env("CXX", value))
{
- env["CXX"] = cxx_env;
+ env["CXX"] = value;
}
- auto cxxflags_env = getenv("CXXFLAGS");
- if(cxxflags_env)
+ if(get_env("CXXFLAGS", value))
{
- env["CXXFLAGS"] = cxxflags_env;
+ env["CXXFLAGS"] = value;
}
- auto ar_env = getenv("AR");
- if(ar_env)
+ if(get_env("AR", value))
{
- env["AR"] = ar_env;
+ env["AR"] = value;
}
- auto ld_env = getenv("LD");
- if(ld_env)
+ if(get_env("LD", value))
{
- env["LD"] = ld_env;
+ env["LD"] = value;
}
- auto ldflags_env = getenv("LDFLAGS");
- if(ldflags_env)
+ if(get_env("LDFLAGS", value))
{
- env["LDFLAGS"] = ldflags_env;
+ env["LDFLAGS"] = value;
}
- auto path_env = getenv("PATH");
- if(path_env)
+ if(get_env("PATH", value))
{
- env["PATH"] = path_env;
+ env["PATH"] = value;
}
auto ret = regenerateCache(settings, args_span[0], args, env);
diff --git a/src/ctor.h b/src/ctor.h
index 6cb46a5..27b30af 100644
--- a/src/ctor.h
+++ b/src/ctor.h
@@ -209,6 +209,7 @@ struct settings
std::string builddir{"build"};
std::size_t parallel_processes{1};
int verbose{0}; // -1: completely silent, 0: normal, 1: verbose, ...
+ bool dry_run{false};
};
struct build_configuration;
@@ -232,7 +233,7 @@ struct build_configuration
using build_configurations = std::vector<build_configuration>;
-int reg(ctor::build_configurations (*cb)(const ctor::settings&),
+int reg(std::function<ctor::build_configurations (const ctor::settings&)> cb,
const std::source_location location = std::source_location::current());
// This type will use flags verbatim
@@ -251,7 +252,7 @@ struct external_configuration
using external_configurations = std::vector<ctor::external_configuration>;
-int reg(ctor::external_configurations (*cb)(const ctor::settings&),
+int reg(std::function<ctor::external_configurations (const ctor::settings&)> cb,
const std::source_location location = std::source_location::current());
// Convenience macro - ugly but keeps things simple(r)
diff --git a/src/execute.cc b/src/execute.cc
index ad6c2a2..c050732 100644
--- a/src/execute.cc
+++ b/src/execute.cc
@@ -107,7 +107,10 @@ int execute(const ctor::settings& settings,
{
envmap.insert(key + "=" + value);
}
-
+ if(settings.dry_run)
+ {
+ _exit(0);
+ }
auto [_, envv] = envmap.get();
execve(command.data(), const_cast<char* const *>(argv.data()),
const_cast<char* const *>(envv));
diff --git a/src/getoptpp b/src/getoptpp
deleted file mode 160000
-Subproject 5aba94355ec638c6f8612f86be309ed684979ae
diff --git a/src/libctor.cc b/src/libctor.cc
index aaf17c9..2685ec0 100644
--- a/src/libctor.cc
+++ b/src/libctor.cc
@@ -17,14 +17,14 @@
#include <cstdlib>
#include <span>
-#include <getoptpp/getoptpp.hpp>
-
#include "ctor.h"
#include "configure.h"
#include "rebuild.h"
#include "tasks.h"
#include "build.h"
#include "unittest.h"
+#include "argparser.h"
+#include "util.h"
int main(int argc, char* argv[])
{
@@ -58,108 +58,120 @@ int main(int argc, char* argv[])
bool list_targets{false};
bool no_relaunch{false}; // true means no re-launch after rebuild.
bool run_unittests{false};
-
- dg::Options opt;
- int key{128};
-
- opt.add("jobs", required_argument, 'j',
- "Number of parallel jobs. (default: cpucount * 2 - 1)",
- [&]() {
- try
- {
- settings.parallel_processes =
- static_cast<std::size_t>(std::stoi(optarg));
- }
- catch(...)
- {
- std::cerr << "Not a number\n";
- return 1;
- }
+ std::vector<std::string> arguments;
+ arg::Parser<int, std::string> opt(argc, argv);
+ opt.set_pos_cb(
+ [&](std::string_view arg)
+ {
+ arguments.emplace_back(std::string(arg));
+ return 0;
+ });
+
+ opt.add('j', "--jobs",
+ std::function([&](int jobs)
+ {
+ settings.parallel_processes = static_cast<std::size_t>(jobs);
return 0;
- });
+ }),
+ "Number of parallel jobs. (default: cpucount * 2 - 1)");
- opt.add("build-dir", required_argument, 'b',
- "Overload output directory for build files (default: '" +
- settings.builddir + "').",
- [&]() {
- settings.builddir = optarg;
+ opt.add('b', "--build-dir",
+ std::function([&](std::string builddir) {
+ settings.builddir = builddir;
return 0;
- });
+ }),
+ "Overload output directory for build files (default: '" +
+ settings.builddir + "').");
- opt.add("verbose", no_argument, 'v',
- "Be verbose. Add multiple times for more verbosity.",
- [&]() {
+ opt.add('v', "--verbose",
+ std::function([&]()
+ {
settings.verbose++;
return 0;
- });
+ }),
+ "Be verbose. Add multiple times for more verbosity.");
- opt.add("quiet", no_argument, 'q',
- "Be completely silent.",
- [&]() {
+ opt.add('q', "--quiet",
+ std::function([&]() {
settings.verbose = -1;
return 0;
- });
+ }),
+ "Be completely silent.");
- opt.add("add", required_argument, 'a',
- "Add specified file to the build configurations.",
- [&]() {
+ opt.add('a', "--add",
+ std::function([&](std::string filename) {
no_relaunch = true;
- add_files.emplace_back(optarg);
+ add_files.emplace_back(filename);
return 0;
- });
+ }),
+ "Add specified file to the build configurations.");
- opt.add("remove", required_argument, 'r',
- "Remove specified file from the build configurations.",
- [&]() {
+ opt.add('r', "--remove",
+ std::function([&](std::string filename)
+ {
no_relaunch = true;
- remove_files.emplace_back(optarg);
+ remove_files.emplace_back(filename);
return 0;
- });
+ }),
+ "Remove specified file from the build configurations.");
- opt.add("list-files", no_argument, 'L',
- "List files in the build configurations.",
- [&]() {
+ opt.add('L', "--list-files",
+ std::function([&]()
+ {
no_relaunch = true;
list_files = true;
return 0;
- });
+ }),
+ "List files in the build configurations.");
- opt.add("list-targets", no_argument, 'l',
- "List targets.",
- [&]() {
+ opt.add('l', "--list-targets",
+ std::function([&]()
+ {
no_relaunch = true;
list_targets = true;
return 0;
- });
+ }),
+ "List targets.");
+
+ opt.add('n', "--dry-run",
+ std::function([&]()
+ {
+ settings.dry_run = true;
+ return 0;
+ }),
+ "Print the commands to be executed, but do not execute them.");
- opt.add("configure-cmd", no_argument, key++,
- "Print commandline for last configure.",
- [&]() {
+ opt.add({}, "--configure-cmd",
+ std::function([&]()
+ {
no_relaunch = true;
print_configure_cmd = true;
return 0;
- });
+ }),
+ "Print commandline for last configure.");
- opt.add("configure-db", no_argument, key++,
- "Print entire configure parameter database.",
- [&]() {
+ opt.add({}, "--configure-db",
+ std::function([&]()
+ {
no_relaunch = true;
print_configure_db = true;
return 0;
- });
+ }),
+ "Print entire configure parameter database.");
- opt.add("database", required_argument, 'd',
- "Write compilation database json file.",
- [&]() {
+ opt.add('d', "--database",
+ std::function([&](std::string database)
+ {
no_relaunch = true;
write_compilation_database = true;
- compilation_database = optarg;
+ compilation_database = database;
return 0;
- });
+ }),
+ "Write compilation database json file.");
- opt.add("help", no_argument, 'h',
- "Print this help text.",
- [&]() -> int {
+ opt.add('h', "--help",
+ std::function([&]() -> int
+ {
std::cout << "Usage: " << args[0] << " [options] [target] ...\n";
std::cout <<
R"_( where target can be either:
@@ -174,28 +186,67 @@ Options:
)_";
opt.help();
exit(0);
- });
+ }),
+ "Print this help text.");
- opt.process(argc, argv);
+ opt.set_err_cb(
+ [&](arg::error err, std::string_view arg)
+ {
+ switch(err)
+ {
+ case arg::error::invalid_arg:
+ std::cerr << opt.prog_name() <<
+ ": invalid argument for option '" << arg << "'\n";
+ std::cerr << "Type '" << opt.prog_name() <<
+ " -h' for more information.\n";
+ break;
+
+ case arg::error::missing_arg:
+ std::cerr << opt.prog_name() << ": option requires and argument '" <<
+ arg << "'\n";
+ std::cerr << "Type '" << opt.prog_name() <<
+ " -h' for more information.\n";
+ break;
+
+ case arg::error::invalid_opt:
+ std::cerr << opt.prog_name() << ": invalid option '" << arg << "'\n";
+ std::cerr << "Type '" << opt.prog_name() <<
+ " -h' for more information.\n";
+ break;
+ }
+ });
+ auto res = opt.parse();
+ if(res != 0)
+ {
+ return res;
+ }
- auto verbose_env = std::getenv("V");
- if(verbose_env)
+ if(std::string value; get_env("V", value))
{
- settings.verbose = std::atoi(verbose_env);
+ try
+ {
+ settings.verbose = std::stoi(value);
+ }
+ catch(...)
+ {
+ // not an integer
+ }
}
if(list_files)
{
no_default_build = true;
std::vector<std::string> files;
- for(std::size_t i = 0; i < numConfigFiles; ++i)
+ const auto& configFiles = getConfigFileList();
+ for(const auto& configFile : configFiles)
{
- files.emplace_back(configFiles[i].file);
+ files.emplace_back(configFile.file);
}
- for(std::size_t i = 0; i < numExternalConfigFiles; ++i)
+ const auto& externalConfigFiles = getExternalConfigFileList();
+ for(const auto& externalConfigFile : externalConfigFiles)
{
- files.emplace_back(externalConfigFiles[i].file);
+ files.emplace_back(externalConfigFile.file);
}
std::sort(files.begin(), files.end());
@@ -288,7 +339,7 @@ Options:
}
bool build_all{!no_default_build};
- for(const auto& arg : opt.arguments())
+ for(const auto& arg : arguments)
{
if(arg == "configure")
{
diff --git a/src/rebuild.cc b/src/rebuild.cc
index a2b7ddd..d62e998 100644
--- a/src/rebuild.cc
+++ b/src/rebuild.cc
@@ -9,6 +9,7 @@
#include <source_location>
#include <cstring>
#include <span>
+#include <vector>
#include "configure.h"
#include "ctor.h"
@@ -18,34 +19,38 @@
#include "tools.h"
#include "util.h"
-std::array<BuildConfigurationEntry, 1024> configFiles;
-std::size_t numConfigFiles{0};
+std::vector<BuildConfigurationEntry>& getConfigFileList()
+{
+ static std::vector<BuildConfigurationEntry> configFiles;
+ return configFiles;
+}
+
+std::vector<ExternalConfigurationEntry>& getExternalConfigFileList()
+{
+ static std::vector<ExternalConfigurationEntry> externalConfigFiles;
+ return externalConfigFiles;
+}
namespace ctor {
-int reg(ctor::build_configurations (*cb)(const ctor::settings&),
+int reg(std::function<ctor::build_configurations (const ctor::settings&)> cb,
const std::source_location location)
{
- // NOTE: std::cout cannot be used here
- if(numConfigFiles >= configFiles.size())
- {
- fprintf(stderr, "Max %d build configurations currently supported.\n",
- (int)configFiles.size());
- exit(1);
- }
+ BuildConfigurationEntry entry;
auto loc = std::filesystem::path(location.file_name());
if(loc.is_absolute())
{
auto pwd = std::filesystem::current_path();
auto rel = std::filesystem::relative(loc, pwd);
- configFiles[numConfigFiles].file = strdup(rel.string().data()); // NOTE: This intentionally leaks memory
+ entry.file = rel.string();
}
else
{
- configFiles[numConfigFiles].file = location.file_name();
+ entry.file = location.file_name();
}
- configFiles[numConfigFiles].cb = cb;
- ++numConfigFiles;
+ entry.cb = cb;
+ auto& configFiles = getConfigFileList();
+ configFiles.push_back(entry);
return 0;
}
@@ -53,80 +58,50 @@ int reg(ctor::build_configurations (*cb)(const ctor::settings&),
int reg(const char* location)
{
- // NOTE: std::cout cannot be used here
- if(numConfigFiles >= configFiles.size())
- {
- fprintf(stderr, "Max %d build configurations currently supported.\n",
- (int)configFiles.size());
- exit(1);
- }
+ BuildConfigurationEntry entry;
- configFiles[numConfigFiles].file = location;
- configFiles[numConfigFiles].cb =
- [](const ctor::settings&){ return std::vector<ctor::build_configuration>{}; };
- ++numConfigFiles;
+ entry.file = location;
+ entry.cb =
+ [](const ctor::settings&)
+ {
+ return std::vector<ctor::build_configuration>{};
+ };
+ auto& configFiles = getConfigFileList();
+ configFiles.push_back(entry);
return 0;
}
int unreg(const char* location)
{
- int found{0};
- for(std::size_t i = 0; i < numConfigFiles;)
- {
- if(std::string(location) == configFiles[i].file)
- {
- ++found;
- for(std::size_t j = i; j < numConfigFiles; ++j)
- {
- configFiles[j] = configFiles[j + 1];
- }
- --numConfigFiles;
- }
- else
- {
- ++i;
- }
- }
-
- for(std::size_t i = 0; i < numExternalConfigFiles;)
- {
- if(std::string(location) == externalConfigFiles[i].file)
- {
- ++found;
- for(std::size_t j = i; j < numExternalConfigFiles; ++j)
- {
- externalConfigFiles[j] = externalConfigFiles[j + 1];
- }
- --numExternalConfigFiles;
- }
- else
- {
- ++i;
- }
- }
-
- return found;
+ auto& configFiles = getConfigFileList();
+ auto erasedConfigs =
+ std::erase_if(configFiles,
+ [&](const BuildConfigurationEntry& entry)
+ {
+ return entry.file == location;
+ });
+
+ auto& externalConfigFiles = getExternalConfigFileList();
+ auto erasedExternals =
+ std::erase_if(externalConfigFiles,
+ [&](const ExternalConfigurationEntry& entry)
+ {
+ return entry.file == location;
+ });
+
+ return static_cast<int>(erasedConfigs) + static_cast<int>(erasedExternals);
}
-std::array<ExternalConfigurationEntry, 1024> externalConfigFiles;
-std::size_t numExternalConfigFiles{0};
-
namespace ctor {
-int reg(ctor::external_configurations (*cb)(const ctor::settings&),
+int reg(std::function<ctor::external_configurations (const ctor::settings&)> cb,
const std::source_location location)
{
- // NOTE: std::cout cannot be used here
- if(numExternalConfigFiles >= externalConfigFiles.size())
- {
- fprintf(stderr, "Max %d external configurations currently supported.\n",
- (int)externalConfigFiles.size());
- exit(1);
- }
-
- externalConfigFiles[numExternalConfigFiles].file = location.file_name();
- externalConfigFiles[numExternalConfigFiles].cb = cb;
- ++numExternalConfigFiles;
+ ExternalConfigurationEntry entry;
+ entry.file = location.file_name();
+ entry.cb = cb;
+ auto& externalConfigFiles = getExternalConfigFileList();
+ externalConfigFiles.push_back(entry);
return 0;
}
@@ -155,9 +130,10 @@ bool recompileCheck(const ctor::settings& global_settings, int argc, char* argv[
using namespace std::string_literals;
+ const auto& configFiles = getConfigFileList();
if(global_settings.verbose > 1)
{
- std::cout << "Recompile check (" << numConfigFiles << "):\n";
+ std::cout << "Recompile check (" << configFiles.size() << "):\n";
}
ctor::build_configuration config;
@@ -201,9 +177,9 @@ bool recompileCheck(const ctor::settings& global_settings, int argc, char* argv[
config.sources.emplace_back(configurationFile.string());
}
- for(std::size_t i = 0; i < numConfigFiles; ++i)
+ for(const auto& configFile : configFiles)
{
- std::string location = configFiles[i].file;
+ std::string location = configFile.file;
if(global_settings.verbose > 1)
{
std::cout << " - " << location << "\n";
@@ -216,9 +192,10 @@ bool recompileCheck(const ctor::settings& global_settings, int argc, char* argv[
}
}
- for(std::size_t i = 0; i < numExternalConfigFiles; ++i)
+ const auto& externalConfigFiles = getExternalConfigFileList();
+ for(const auto& externalConfigFile : externalConfigFiles)
{
- std::string location = externalConfigFiles[i].file;
+ std::string location = externalConfigFile.file;
if(global_settings.verbose > 1)
{
std::cout << " - " << location << "\n";
diff --git a/src/rebuild.h b/src/rebuild.h
index efa6d42..8e0c78a 100644
--- a/src/rebuild.h
+++ b/src/rebuild.h
@@ -5,26 +5,26 @@
#include <vector>
#include <array>
+#include <string>
+#include <functional>
#include "ctor.h"
struct BuildConfigurationEntry
{
- const char* file;
- ctor::build_configurations (*cb)(const ctor::settings&);
+ std::string file;
+ std::function<ctor::build_configurations (const ctor::settings&)> cb;
};
+std::vector<BuildConfigurationEntry>& getConfigFileList();
+
struct ExternalConfigurationEntry
{
- const char* file;
- ctor::external_configurations (*cb)(const ctor::settings&);
+ std::string file;
+ std::function<ctor::external_configurations (const ctor::settings&)> cb;
};
-extern std::array<BuildConfigurationEntry, 1024> configFiles;
-extern std::size_t numConfigFiles;
-
-extern std::array<ExternalConfigurationEntry, 1024> externalConfigFiles;
-extern std::size_t numExternalConfigFiles;
+std::vector<ExternalConfigurationEntry>& getExternalConfigFileList();
int reg(const char* location);
int unreg(const char* location);
diff --git a/src/tasks.cc b/src/tasks.cc
index 2f9e47a..94fe269 100644
--- a/src/tasks.cc
+++ b/src/tasks.cc
@@ -25,7 +25,7 @@
const std::deque<Target>& getTargets(const ctor::settings& settings,
bool resolve_externals)
{
- auto config_files = std::span(configFiles).subspan(0, numConfigFiles);
+ auto& config_files = getConfigFileList();
static bool initialised{false};
static std::deque<Target> targets;
@@ -170,7 +170,8 @@ std::vector<std::shared_ptr<Task>> taskFactory(const ctor::build_configuration&
return tasks;
}
-std::shared_ptr<Task> getNextTask([[maybe_unused]]const std::vector<std::shared_ptr<Task>>& allTasks,
+std::shared_ptr<Task> getNextTask([[maybe_unused]]const ctor::settings& settings,
+ [[maybe_unused]]const std::vector<std::shared_ptr<Task>>& allTasks,
std::vector<std::shared_ptr<Task>>& dirtyTasks)
{
for(auto dirtyTask = dirtyTasks.begin();
@@ -179,7 +180,7 @@ std::shared_ptr<Task> getNextTask([[maybe_unused]]const std::vector<std::shared_
{
auto task = *dirtyTask;
//std::cout << "Examining target " << (*dirtyTask)->target() << "\n";
- if(task->ready())
+ if(task->ready() || settings.dry_run)
{
dirtyTasks.erase(dirtyTask);
return task;
diff --git a/src/tasks.h b/src/tasks.h
index 6573784..97fc84d 100644
--- a/src/tasks.h
+++ b/src/tasks.h
@@ -23,7 +23,8 @@ const std::deque<Target>& getTargets(const ctor::settings& settings,
//! fulfilled.
//! The returned task is removed from the dirty list.
//! Return nullptr if no dirty task is ready.
-std::shared_ptr<Task> getNextTask(const std::vector<std::shared_ptr<Task>>& allTasks,
+std::shared_ptr<Task> getNextTask(const ctor::settings& settings,
+ const std::vector<std::shared_ptr<Task>>& allTasks,
std::vector<std::shared_ptr<Task>>& dirtyTasks);
//! Get list of tasks filtered by name including each of their direct
diff --git a/src/util.cc b/src/util.cc
index 6fc650a..a4abd23 100644
--- a/src/util.cc
+++ b/src/util.cc
@@ -7,12 +7,17 @@
#include <fstream>
#include <algorithm>
#include <sstream>
+#include <cctype>
+#include <cstdlib>
-std::string to_lower(const std::string& str)
+std::string to_lower(std::string str)
{
- std::string out{str};
- std::transform(out.begin(), out.end(), out.begin(), ::tolower);
- return out;
+ std::transform(str.begin(), str.end(), str.begin(),
+ [](unsigned char c)
+ {
+ return std::tolower(c);
+ });
+ return str;
}
std::string readFile(const std::string& fileName)
@@ -20,13 +25,17 @@ std::string readFile(const std::string& fileName)
std::ifstream ifs(fileName.c_str(),
std::ios::in | std::ios::binary | std::ios::ate);
- std::ifstream::pos_type fileSize = ifs.tellg();
+ auto size = ifs.tellg();
+ if(size < 0)
+ {
+ return {};
+ }
ifs.seekg(0, std::ios::beg);
- std::vector<char> bytes(static_cast<std::size_t>(fileSize));
- ifs.read(bytes.data(), fileSize);
+ std::string bytes(static_cast<std::size_t>(size), '\0');
+ ifs.read(bytes.data(), static_cast<std::streamsize>(bytes.size()));
- return {bytes.data(), static_cast<std::size_t>(fileSize)};
+ return bytes;
}
ctor::language languageFromExtension(const std::filesystem::path& file)
@@ -109,8 +118,18 @@ std::string esc(const std::string& in)
return out;
}
-std::vector<std::string> get_paths(const std::string& path_env)
+std::vector<std::string> get_paths(const std::string& path_env_)
{
+ std::string path_env;
+ if(!path_env_.empty())
+ {
+ path_env = path_env_;
+ }
+ else
+ {
+ get_env("PATH", path_env);
+ }
+
std::vector<std::string> paths;
#ifdef _WIN32
@@ -277,3 +296,14 @@ std::vector<std::string> argsplit(const std::string& str)
}
return tokens;
}
+
+bool get_env(std::string_view name, std::string& value)
+{
+ auto var = getenv(name.data());
+ if(var)
+ {
+ value = var;
+ return true;
+ }
+ return false;
+}
diff --git a/src/util.h b/src/util.h
index 8b41014..38d89f2 100644
--- a/src/util.h
+++ b/src/util.h
@@ -9,7 +9,7 @@
#include <filesystem>
#include <cstdlib>
-std::string to_lower(const std::string& str);
+std::string to_lower(std::string str);
std::string readFile(const std::string& fileName);
ctor::language languageFromExtension(const std::filesystem::path& file);
@@ -23,7 +23,11 @@ void append(T& a, const T& b)
std::string esc(const std::string& in);
-std::vector<std::string> get_paths(const std::string& path_env = std::getenv("PATH"));
+//! Get system paths (ie. env var PATH).
+//! If path_env is provided, this search string will be used, other the PATH
+//! env variable is used.
+//! \returns a vector of the individual toknized paths.
+std::vector<std::string> get_paths(const std::string& path_env = {});
std::string locate(const std::string& app,
const std::vector<std::string>& paths,
@@ -31,3 +35,7 @@ std::string locate(const std::string& app,
//! Splits string into tokens adhering to quotations " and '
std::vector<std::string> argsplit(const std::string& str);
+
+//! Calls the system getenv and sets the string if the env name it exists.
+//! \returns true if the env name existed, false otherwise.
+bool get_env(std::string_view name, std::string& value);