summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitmodules3
-rw-r--r--Jenkinsfile12
-rw-r--r--ctor.cc66
-rw-r--r--src/argparser.h477
-rw-r--r--src/build.cc2
-rw-r--r--src/configure.cc381
-rw-r--r--src/ctor.h349
-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/task.cc19
-rw-r--r--src/task.h6
-rw-r--r--src/task_ar.cc29
-rw-r--r--src/task_ar.h4
-rw-r--r--src/task_cc.cc19
-rw-r--r--src/task_cc.h3
-rw-r--r--src/task_fn.cc187
-rw-r--r--src/task_fn.h8
-rw-r--r--src/task_ld.cc41
-rw-r--r--src/task_ld.h5
-rw-r--r--src/task_so.cc35
-rw-r--r--src/task_so.h4
-rw-r--r--src/tasks.cc65
-rw-r--r--src/tasks.h3
-rw-r--r--src/tools.cc151
-rw-r--r--src/tools.h34
-rw-r--r--src/util.cc54
-rw-r--r--src/util.h18
-rw-r--r--test/argparser_test.cc1019
-rw-r--r--test/ctor.cc337
-rw-r--r--test/cycle_test.cc14
-rw-r--r--test/execute_test.cc21
-rw-r--r--test/generated_sources_test.cc135
-rw-r--r--test/source_type_test.cc11
-rw-r--r--test/suite/ctor_files/ctor.cc.bar37
-rw-r--r--test/suite/ctor_files/ctor.cc.base37
-rw-r--r--test/suite/ctor_files/ctor.cc.generated104
-rw-r--r--test/suite/ctor_files/ctor.cc.generated287
-rw-r--r--test/suite/ctor_files/ctor.cc.multi37
-rw-r--r--test/suite/test.cc370
-rwxr-xr-xtest/suite/test.sh120
-rw-r--r--test/tasks_test.cc30
-rw-r--r--test/tmpfile.h44
-rw-r--r--test/tools_test.cc23
46 files changed, 3735 insertions, 1035 deletions
diff --git a/.gitmodules b/.gitmodules
index c765825..c12c38f 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,6 +1,3 @@
-[submodule "getoptpp"]
- path = src/getoptpp
- url = git://git.drumgizmo.org/getoptpp.git
[submodule "test/uunit"]
path = test/uunit
url = git://git.drumgizmo.org/uunit.git
diff --git a/Jenkinsfile b/Jenkinsfile
index a558359..d198a69 100644
--- a/Jenkinsfile
+++ b/Jenkinsfile
@@ -11,11 +11,11 @@ pipeline {
echo 'Cleaning workspace ...'
sh 'git clean -d -x -f'
echo 'Building (clang) ...'
- sh 'CXX=/usr/local/opt/llvm/bin/clang++ LDFLAGS="-L/usr/local/opt/llvm/lib/c++ -L/usr/local/opt/llvm/lib/unwind -lunwind" ./bootstrap.sh'
+ sh 'CXXFLAGS=-Werror CXX=/usr/local/opt/llvm/bin/clang++ LDFLAGS="-L/usr/local/opt/llvm/lib/c++ -L/usr/local/opt/llvm/lib/unwind -lunwind" ./bootstrap.sh'
echo 'Testing (clang) ...'
sh './ctor check'
echo 'Testing suite (clang) ...'
- sh '(cd test/suite; CTORDIR=../../build CXX=/usr/local/opt/llvm/bin/clang++ LDFLAGS="-L/usr/local/opt/llvm/lib/c++ -L/usr/local/opt/llvm/lib/unwind -lunwind" ./test.sh)'
+ sh '(cd test/suite; /usr/local/opt/llvm/bin/clang++ -std=c++20 test.cc -L/usr/local/opt/llvm/lib/c++ -L/usr/local/opt/llvm/lib/unwind -lunwind -o test && CTORDIR=../../build CXXFLAGS=-Werror CXX=/usr/local/opt/llvm/bin/clang++ LDFLAGS="-L/usr/local/opt/llvm/lib/c++ -L/usr/local/opt/llvm/lib/unwind -lunwind" ./test)'
}
post {
always {
@@ -32,11 +32,11 @@ pipeline {
echo 'Cleaning workspace ...'
sh 'git clean -d -x -f'
echo 'Building (gcc) ...'
- sh 'CXX=g++ ./bootstrap.sh'
+ sh 'CXXFLAGS="-Werror" CXX=g++ ./bootstrap.sh'
echo 'Testing (gcc) ...'
sh './ctor check'
echo 'Testing suite (gcc) ...'
- sh '(cd test/suite; CTORDIR=../../build CXX=g++ ./test.sh)'
+ sh '(cd test/suite; g++ -std=c++20 test.cc -o test && CTORDIR=../../build CXXFLAGS="-Werror" CXX=g++ ./test)'
}
post {
always {
@@ -53,11 +53,11 @@ pipeline {
echo 'Cleaning workspace ...'
sh 'git clean -d -x -f'
echo 'Building (clang) ...'
- sh 'CXX=clang++ ./bootstrap.sh'
+ sh 'CXXFLAGS=-Werror CXX=clang++ ./bootstrap.sh'
echo 'Testing (clang) ...'
sh './ctor check'
echo 'Testing suite (clang) ...'
- sh '(cd test/suite; CTORDIR=../../build CXX=clang++ ./test.sh)'
+ sh '(cd test/suite; clang++ -std=c++20 test.cc -o test && CTORDIR=../../build CXXFLAGS=-Werror CXX=clang++ ./test)'
}
post {
always {
diff --git a/ctor.cc b/ctor.cc
index 67b1465..76488a5 100644
--- a/ctor.cc
+++ b/ctor.cc
@@ -10,41 +10,39 @@ ctor::build_configurations ctorConfigs(const ctor::settings& settings)
return
{
{
- .system = ctor::output_system::build,
- .target = "libctor.a",
- .sources = {
- "src/build.cc",
- "src/configure.cc",
- "src/deps.cc",
- "src/execute.cc",
- "src/externals_manual.cc",
- "src/libctor.cc",
- "src/pointerlist.cc",
- "src/rebuild.cc",
- "src/task.cc",
- "src/task_ar.cc",
- "src/task_fn.cc",
- "src/task_cc.cc",
- "src/task_ld.cc",
- "src/task_so.cc",
- "src/tasks.cc",
- "src/tools.cc",
- "src/util.cc",
- "src/unittest.cc",
- },
- .flags = {
- .cxxflags = {
- "-std=c++20",
- "-O3",
- "-g",
- "-Wall",
- "-Werror",
- "-Wextra",
- "-Wshadow",
- "-Wconversion",
-// "-Wnrvo",
- "-Isrc",
+ ctor::output_system::build,
+ ctor::target("libctor.a"),
+ ctor::sources{
+ "src/build.cc",
+ "src/configure.cc",
+ "src/deps.cc",
+ "src/execute.cc",
+ "src/externals_manual.cc",
+ "src/libctor.cc",
+ "src/pointerlist.cc",
+ "src/rebuild.cc",
+ "src/task.cc",
+ "src/task_ar.cc",
+ "src/task_fn.cc",
+ "src/task_cc.cc",
+ "src/task_ld.cc",
+ "src/task_so.cc",
+ "src/tasks.cc",
+ "src/tools.cc",
+ "src/util.cc",
+ "src/unittest.cc",
},
+ ctor::cxx_flags{
+ "-std=c++20",
+ "-O3",
+ "-g",
+ "-Wall",
+ "-Wextra",
+ "-Wshadow",
+ "-Wconversion",
+// "-Wnrvo",
+ "-Isrc",
+ "-fexceptions",
},
}
};
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..5ba87c3 100644
--- a/src/configure.cc
+++ b/src/configure.cc
@@ -6,10 +6,11 @@
#include <iostream>
#include <filesystem>
#include <fstream>
+#include <sstream>
#include <optional>
#include <span>
-
-#include <getoptpp/getoptpp.hpp>
+#include <cstring>
+#include <functional>
#include "execute.h"
#include "ctor.h"
@@ -18,6 +19,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");
@@ -25,18 +27,36 @@ const std::filesystem::path configHeaderFile("config.h");
std::map<std::string, std::string> external_includedir;
std::map<std::string, std::string> external_libdir;
-const ctor::configuration& __attribute__((weak)) ctor::get_configuration()
+std::function<const ctor::configuration&()>& getConfigurationCallback()
+{
+ static std::function<const ctor::configuration&()> configuration_callback;
+ return configuration_callback;
+}
+
+namespace ctor {
+int reg(std::function<const ctor::configuration&()> cb,
+ [[maybe_unused]]const std::source_location location)
+{
+ auto& configuration_callback = getConfigurationCallback();
+ configuration_callback = cb;
+ return {};
+}
+} // ctor::
+
+const ctor::configuration& ctor::get_configuration()
{
+ auto& configuration_callback = getConfigurationCallback();
+ if(configuration_callback)
+ {
+ return configuration_callback();
+ }
+
static ctor::configuration cfg;
static bool initialised{false};
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 +120,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 +157,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 +172,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 +272,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 +286,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 +398,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 +432,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;
- opt.process(static_cast<int>(vargs.size()), vargs.data());
+ 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;
+ }
+ });
+
+ 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())
{
@@ -425,7 +515,8 @@ int regenerateCache(ctor::settings& settings,
{
case ctor::target_type::executable:
case ctor::target_type::unit_test:
- case ctor::target_type::dynamic_library:
+ case ctor::target_type::shared_library:
+ case ctor::target_type::module:
needs_build_ld = true;
break;
case ctor::target_type::static_library:
@@ -462,7 +553,8 @@ int regenerateCache(ctor::settings& settings,
{
case ctor::target_type::executable:
case ctor::target_type::unit_test:
- case ctor::target_type::dynamic_library:
+ case ctor::target_type::shared_library:
+ case ctor::target_type::module:
needs_host_ld = true;
break;
case ctor::target_type::static_library:
@@ -676,14 +768,14 @@ 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;
- std::cout << "Writing results to: " << configurationFile.string() << "\n";
- {
- std::ofstream istr(configurationFile);
+ std::cout << "Generating configuration: " << configurationFile.string() << "\n"; {
+ std::stringstream istr;
istr << "#include <ctor.h>\n\n";
- istr << "const ctor::configuration& ctor::get_configuration()\n";
+ istr << "const ctor::configuration& stored_configuration()\n";
istr << "{\n";
istr << " static ctor::configuration cfg =\n";
istr << " {\n";
@@ -770,7 +862,7 @@ int regenerateCache(ctor::settings& settings,
for(const auto& ext : externalConfigs)
{
- istr << " { \"" << esc(ext.name) << "\", {\n";
+ istr << " { \"" << esc(ext.name) << "\", ctor::flags{\n";
ctor::flags resolved_flags;
if(std::holds_alternative<ctor::external_manual>(ext.external))
{
@@ -789,7 +881,7 @@ int regenerateCache(ctor::settings& settings,
if(!resolved_flags.cflags.empty())
{
- istr << " .cflags = {";
+ istr << " ctor::c_flags{";
for(const auto& flag : resolved_flags.cflags)
{
istr << flag << ",";
@@ -799,7 +891,7 @@ int regenerateCache(ctor::settings& settings,
if(!resolved_flags.cxxflags.empty())
{
- istr << " .cxxflags = {";
+ istr << " ctor::cxx_flags{";
for(const auto& flag : resolved_flags.cxxflags)
{
istr << flag << ",";
@@ -809,7 +901,7 @@ int regenerateCache(ctor::settings& settings,
if(!resolved_flags.ldflags.empty())
{
- istr << " .ldflags = {";
+ istr << " ctor::ld_flags{";
for(const auto& flag : resolved_flags.ldflags)
{
istr << flag << ",";
@@ -819,7 +911,7 @@ int regenerateCache(ctor::settings& settings,
if(!resolved_flags.asmflags.empty())
{
- istr << " .asmflags = {";
+ istr << " ctor::asm_flags{";
for(const auto& flag : resolved_flags.asmflags)
{
istr << flag << ",";
@@ -833,13 +925,29 @@ int regenerateCache(ctor::settings& settings,
istr << " };\n";
istr << " return cfg;\n";
istr << "}\n";
+ istr << "\nREG(stored_configuration);\n";
+
+ auto new_configuration = istr.str();
+ auto current_configuration = readFile(configurationFile.string());
+ if(current_configuration != new_configuration)
+ {
+ std::ofstream configuration_stream(configurationFile.string());
+ configuration_stream << new_configuration;
+ }
}
{
- std::ofstream istr(configHeaderFile);
- istr << "#pragma once\n\n";
- istr << "#define HAS_FOO 1\n";
- istr << "//#define HAS_BAR 1\n";
+ std::stringstream new_config_stream;
+ new_config_stream << "#pragma once\n\n";
+ new_config_stream << "#define HAS_FOO 1\n";
+ new_config_stream << "//#define HAS_BAR 1\n";
+ auto new_config = new_config_stream.str();
+ auto current_config_content = readFile(configHeaderFile.string());
+ if(current_config_content != new_config)
+ {
+ std::ofstream config_file_stream(configHeaderFile.string());
+ config_file_stream << new_config;
+ }
}
return 0;
@@ -858,52 +966,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..8a7c809 100644
--- a/src/ctor.h
+++ b/src/ctor.h
@@ -11,6 +11,7 @@
#include <cstddef>
#include <functional>
#include <string_view>
+#include <cassert>
namespace ctor {
@@ -20,7 +21,9 @@ enum class target_type
executable,
static_library,
- dynamic_library,
+ shared_library,
+ dynamic_library = shared_library,
+ module,
object,
unit_test,
unit_test_library,
@@ -61,27 +64,59 @@ enum class toolchain
clang,
};
-struct source
+struct output_file
{
- source(const char* file_) : file(file_) {} // convenience ctor
-
- source(std::string_view file_) : source(file_, ctor::language::automatic) {}
- source(std::string_view file_, ctor::language lang_) : file(file_), language(lang_) {}
-
- source(std::string_view file_, std::string_view output_) : file(file_), output(output_) {}
- source(std::string_view file_, ctor::language lang_, std::string_view output_) : file(file_), language(lang_), output(output_) {}
-
- source(ctor::toolchain toolchain_, std::string_view file_) : file(file_), toolchain(toolchain_) {}
- source(ctor::toolchain toolchain_, std::string_view file_, ctor::language lang_) : file(file_), toolchain(toolchain_), language(lang_) {}
+ std::string file;
+};
- source(ctor::toolchain toolchain_, std::string_view file_, std::string_view output_) : file(file_), toolchain(toolchain_), output(output_) {}
+enum class source_type
+{
+ regular,
+ generated,
+};
- source(ctor::toolchain toolchain_, std::string_view file_, ctor::language lang_, std::string_view output_) : file(file_), toolchain(toolchain_), language(lang_), output(output_) {}
+struct source
+{
+ template <class ... Args>
+ requires ((
+ std::is_convertible_v<Args, std::string_view> ||
+ std::is_same_v<Args, ctor::toolchain> ||
+ std::is_same_v<Args, ctor::language> ||
+ std::is_same_v<Args, ctor::source_type> ||
+ std::is_same_v<Args, ctor::output_file>
+ ) && ...)
+ constexpr source(Args && ... arg)
+ {
+ ([&]
+ {
+ if constexpr(std::is_convertible_v<Args, std::string_view>)
+ {
+ file = arg;
+ }
+ else if constexpr(std::is_same_v<Args, ctor::toolchain>)
+ {
+ toolchain = arg;
+ }
+ else if constexpr(std::is_same_v<Args, ctor::language>)
+ {
+ language = arg;
+ }
+ else if constexpr(std::is_same_v<Args, ctor::output_file>)
+ {
+ output = arg.file;
+ }
+ else if constexpr(std::is_same_v<Args, ctor::source_type>)
+ {
+ source_type = arg;
+ }
+ }(), ...);
+ }
std::string file;
ctor::toolchain toolchain{ctor::toolchain::any};
ctor::language language{ctor::language::automatic};
std::string output{};
+ ctor::source_type source_type{ctor::source_type::regular};
};
enum class cxx_opt
@@ -94,6 +129,7 @@ enum class cxx_opt
warn_shadow, // -Wshadow
warn_extra, // -Wextra
warnings_as_errors, // -Werror
+ exceptions, // -fexceptions
generate_dep_tree, // -MMD
no_link, // -c
include_path, // -I<arg>
@@ -163,24 +199,60 @@ template<typename T>
class flag
{
public:
- flag(std::string_view str);
- flag(const char* str);
- flag(T opt_) : opt(opt_) {}
- flag(T opt_, std::string_view arg_, std::string_view arg2_ = "")
- : opt(opt_), arg(arg_), arg2(arg2_) {}
- flag(T opt_, const char* arg_, const char* arg2_ = "")
- : opt(opt_), arg(arg_), arg2(arg2_) {}
- flag(ctor::toolchain toolchain_, T opt_)
- : toolchain(toolchain_), opt(opt_) {}
- flag(ctor::toolchain toolchain_, T opt_, const char* arg_, const char* arg2_ = "")
- : toolchain(toolchain_), opt(opt_), arg(arg_), arg2(arg2_) {}
- flag(ctor::toolchain toolchain_, T opt_, std::string_view arg_, std::string_view arg2_ = "")
- : toolchain(toolchain_), opt(opt_), arg(arg_), arg2(arg2_) {}
+ template <class ... Args>
+ requires ((
+ std::is_convertible_v<Args, std::string_view> ||
+ std::is_same_v<Args, ctor::toolchain> ||
+ std::is_same_v<Args, T>
+ ) && ...)
+ constexpr flag(Args && ... _arg)
+ {
+ constexpr std::size_t n = sizeof...(Args);
+ int state{}; // 0: opt, 1: arg1, 2: arg2, 3: error
+ ([&]
+ {
+ if constexpr(std::is_convertible_v<Args, std::string_view>)
+ {
+ if constexpr(n == 1)
+ {
+ std::string str(_arg);
+ to_flag(str);
+ }
+ else
+ {
+ assert(state > 0); // opt must be before args
+ if(state == 1)
+ {
+ this->arg = _arg;
+ }
+ else
+ {
+ assert(state == 2); // up to 2 args supported
+ this->arg2 = _arg;
+ }
+ ++state;
+ }
+ }
+ else if constexpr(std::is_same_v<Args, ctor::toolchain>)
+ {
+ toolchain = _arg;
+ }
+ else if constexpr(std::is_same_v<Args, T>)
+ {
+ assert(state == 0); // opt must be before args
+ opt = _arg;
+ ++state;
+ }
+ }(), ...);
+ }
ctor::toolchain toolchain{ctor::toolchain::any};
T opt{};
std::string arg;
std::string arg2;
+
+private:
+ void to_flag(std::string_view str);
};
using c_flag = ctor::flag<ctor::c_opt>;
@@ -197,6 +269,41 @@ using asm_flags = std::vector<ctor::asm_flag>;
struct flags
{
+ template <class ... Args>
+ requires ((
+ std::is_same_v<Args, ctor::c_flags> ||
+ std::is_same_v<Args, ctor::cxx_flags> ||
+ std::is_same_v<Args, ctor::ld_flags> ||
+ std::is_same_v<Args, ctor::ar_flags> ||
+ std::is_same_v<Args, ctor::asm_flags>
+ ) && ...)
+ constexpr flags(Args && ... arg)
+ {
+ ([&]
+ {
+ if constexpr(std::is_same_v<Args, ctor::c_flags>)
+ {
+ cflags = arg;
+ }
+ else if constexpr(std::is_same_v<Args, ctor::cxx_flags>)
+ {
+ cxxflags = arg;
+ }
+ else if constexpr(std::is_same_v<Args, ctor::ld_flags>)
+ {
+ ldflags = arg;
+ }
+ else if constexpr(std::is_same_v<Args, ctor::ar_flags>)
+ {
+ arflags = arg;
+ }
+ else if constexpr(std::is_same_v<Args, ctor::asm_flags>)
+ {
+ asmflags = arg;
+ }
+ }(), ...);
+ }
+
ctor::c_flags cflags; // flags for c compiler
ctor::cxx_flags cxxflags; // flags for c++ compiler
ctor::ld_flags ldflags; // flags for linker
@@ -209,16 +316,126 @@ 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;
-using GeneratorCb = std::function<int(const std::string& input,
- const std::string& output,
- const build_configuration& config,
- const ctor::settings& settings)>;
+using GeneratorOneToOne =
+ std::function<int(const std::string& input,
+ const std::string& output,
+ const build_configuration& config,
+ const ctor::settings& settings)>;
+
+using GeneratorManyToOne =
+ std::function<int(const std::vector<std::string>& input,
+ const std::string& output,
+ const ctor::build_configuration& config,
+ const ctor::settings& settings)>;
+
+struct name
+{
+ std::string name;
+};
+
+struct target
+{
+ std::string target;
+};
+
+using sources = std::vector<ctor::source>;
+
+struct depends
+{
+ std::vector<std::string> depends;
+};
+
+struct externals
+{
+ std::vector<std::string> externals;
+};
struct build_configuration
{
+ template <class ... Args>
+ requires ((
+ std::is_same_v<Args, ctor::name> ||
+ std::is_same_v<Args, ctor::target_type> ||
+ std::is_same_v<Args, ctor::output_system> ||
+ std::is_same_v<Args, ctor::target> ||
+ std::is_same_v<Args, ctor::sources> ||
+ std::is_same_v<Args, ctor::depends> ||
+ std::is_same_v<Args, ctor::externals> ||
+ std::is_convertible_v<Args, ctor::GeneratorOneToOne> ||
+ std::is_convertible_v<Args, ctor::GeneratorManyToOne> ||
+ std::is_same_v<Args, ctor::c_flags> ||
+ std::is_same_v<Args, ctor::cxx_flags> ||
+ std::is_same_v<Args, ctor::ld_flags> ||
+ std::is_same_v<Args, ctor::ar_flags> ||
+ std::is_same_v<Args, ctor::asm_flags>
+ ) && ...)
+ constexpr build_configuration(Args && ... arg)
+ {
+ ([&]
+ {
+ if constexpr(std::is_same_v<Args, ctor::name>)
+ {
+ name = arg.name;
+ }
+ else if constexpr(std::is_same_v<Args, ctor::target_type>)
+ {
+ type = arg;
+ }
+ else if constexpr(std::is_same_v<Args, ctor::output_system>)
+ {
+ system = arg;
+ }
+ else if constexpr(std::is_same_v<Args, ctor::target>)
+ {
+ target = arg.target;
+ }
+ else if constexpr(std::is_same_v<Args, ctor::sources>)
+ {
+ sources = arg;
+ }
+ else if constexpr(std::is_same_v<Args, ctor::depends>)
+ {
+ depends = arg.depends;
+ }
+ else if constexpr(std::is_same_v<Args, ctor::externals>)
+ {
+ externals = arg.externals;
+ }
+ else if constexpr(std::is_convertible_v<Args, ctor::GeneratorOneToOne>)
+ {
+ function_one_to_one = arg;
+ }
+ else if constexpr(std::is_convertible_v<Args, ctor::GeneratorManyToOne>)
+ {
+ function_many_to_one = arg;
+ }
+ else if constexpr(std::is_same_v<Args, ctor::c_flags>)
+ {
+ flags.cflags = arg;
+ }
+ else if constexpr(std::is_same_v<Args, ctor::cxx_flags>)
+ {
+ flags.cxxflags = arg;
+ }
+ else if constexpr(std::is_same_v<Args, ctor::ld_flags>)
+ {
+ flags.ldflags = arg;
+ }
+ else if constexpr(std::is_same_v<Args, ctor::ar_flags>)
+ {
+ flags.arflags = arg;
+ }
+ else if constexpr(std::is_same_v<Args, ctor::asm_flags>)
+ {
+ flags.asmflags = arg;
+ }
+ }(), ...);
+ }
+
std::string name; // Name - used for referring in other configurations.
ctor::target_type type{ctor::target_type::automatic};
ctor::output_system system{ctor::output_system::build};
@@ -227,23 +444,84 @@ struct build_configuration
std::vector<std::string> depends; // internal target dependencies
ctor::flags flags;
std::vector<std::string> externals; // externals used by this configuration
- GeneratorCb function;
+ ctor::GeneratorOneToOne function_one_to_one;
+ ctor::GeneratorManyToOne function_many_to_one;
};
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
struct external_manual
{
+ template <class ... Args>
+ requires ((
+ std::is_same_v<Args, ctor::c_flags> ||
+ std::is_same_v<Args, ctor::cxx_flags> ||
+ std::is_same_v<Args, ctor::ld_flags> ||
+ std::is_same_v<Args, ctor::ar_flags> ||
+ std::is_same_v<Args, ctor::asm_flags>
+ ) && ...)
+ constexpr external_manual(Args && ... arg)
+ {
+ ([&]
+ {
+ if constexpr(std::is_same_v<Args, ctor::c_flags>)
+ {
+ flags.cflags = arg;
+ }
+ else if constexpr(std::is_same_v<Args, ctor::cxx_flags>)
+ {
+ flags.cxxflags = arg;
+ }
+ else if constexpr(std::is_same_v<Args, ctor::ld_flags>)
+ {
+ flags.ldflags = arg;
+ }
+ else if constexpr(std::is_same_v<Args, ctor::ar_flags>)
+ {
+ flags.arflags = arg;
+ }
+ else if constexpr(std::is_same_v<Args, ctor::asm_flags>)
+ {
+ flags.asmflags = arg;
+ }
+ }(), ...);
+ }
+
ctor::flags flags;
};
struct external_configuration
{
+ template <class ... Args>
+ requires ((
+ std::is_same_v<Args, ctor::name> ||
+ std::is_same_v<Args, ctor::output_system> ||
+ std::is_same_v<Args, ctor::external_manual>
+ ) && ...)
+ constexpr external_configuration(Args && ... arg)
+ {
+ ([&]
+ {
+ if constexpr(std::is_same_v<Args, ctor::name>)
+ {
+ name = arg.name;
+ }
+ else if constexpr(std::is_same_v<Args, ctor::output_system>)
+ {
+ system = arg;
+ }
+ else if constexpr(std::is_same_v<Args, ctor::external_manual>)
+ {
+ external = arg;
+ }
+ }(), ...);
+ }
+
std::string name; // Name for configuration
ctor::output_system system{ctor::output_system::build};
std::variant<ctor::external_manual> external;
@@ -251,7 +529,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)
@@ -301,4 +579,7 @@ struct configuration
const ctor::configuration& get_configuration();
+int reg(std::function<const ctor::configuration&()> cb,
+ const std::source_location location = std::source_location::current());
+
} // ctor::
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/task.cc b/src/task.cc
index ef7731b..3a7b895 100644
--- a/src/task.cc
+++ b/src/task.cc
@@ -7,18 +7,31 @@
#include <algorithm>
#include <utility>
-Task::Task(const ctor::build_configuration& config_, const ctor::settings& settings_,
+Task::Task(ctor::target_type resolved_target_type,
+ const ctor::build_configuration& config_,
+ const ctor::settings& settings_,
std::string sourceDir_)
: config(config_)
, output_system(config.system)
, settings(settings_)
- , sourceDir(std::move(sourceDir_))
+ , sourceDir(sourceDir_)
{
+ assert(resolved_target_type != ctor::target_type::automatic);
+ target_type = resolved_target_type;
+ output_system = config.system;
}
int Task::registerDepTasks(const std::vector<std::shared_ptr<Task>>& tasks)
{
- for(const auto& depStr : depends())
+ auto dependsList = depends();
+ if(!derived())
+ {
+ for(const auto& dep : config.depends)
+ {
+ dependsList.emplace_back(dep);
+ }
+ }
+ for(const auto& depStr : dependsList)
{
bool found{false};
for(const auto& task : tasks)
diff --git a/src/task.h b/src/task.h
index 32d0de3..f26d217 100644
--- a/src/task.h
+++ b/src/task.h
@@ -23,7 +23,9 @@ enum class State
class Task
{
public:
- Task(const ctor::build_configuration& config, const ctor::settings& settings,
+ Task(ctor::target_type resolved_target_type,
+ const ctor::build_configuration& config,
+ const ctor::settings& settings,
std::string sourceDir);
virtual ~Task() = default;
@@ -79,5 +81,5 @@ protected:
ctor::language source_language{ctor::language::automatic};
ctor::output_system output_system{ctor::output_system::host};
const ctor::settings& settings;
- std::string sourceDir;
+ std::filesystem::path sourceDir;
};
diff --git a/src/task_ar.cc b/src/task_ar.cc
index 3b45cc2..4123a81 100644
--- a/src/task_ar.cc
+++ b/src/task_ar.cc
@@ -11,20 +11,18 @@
#include "util.h"
#include "tools.h"
-TaskAR::TaskAR(const ctor::build_configuration& config_,
+TaskAR::TaskAR(ctor::target_type resolved_target_type,
+ const ctor::build_configuration& config_,
const ctor::settings& settings_,
const std::string& target,
const std::vector<std::string>& objects,
const std::string& sourceDir_)
- : Task(config_, settings_, sourceDir_)
+ : Task(resolved_target_type, config_, settings_, sourceDir_)
, _targetFile(target)
, config(config_)
, settings(settings_)
, sourceDir(sourceDir_)
{
- target_type = ctor::target_type::static_library;
- output_system = config.system;
-
auto toolchain = getToolChain(config.system);
_targetFile = extension(toolchain, target_type, config.system, _targetFile);
for(const auto& object : objects)
@@ -34,11 +32,6 @@ TaskAR::TaskAR(const ctor::build_configuration& config_,
dependsStr.push_back(objectFile.string());
}
- for(const auto& dep : config.depends)
- {
- depFiles.emplace_back(dep);
- }
-
flagsFile = std::filesystem::path(settings.builddir) / cleanUp(sourceDir) / targetFile().stem();
flagsFile += ".flags";
@@ -77,6 +70,15 @@ bool TaskAR::dirtyInner()
}
}
+ auto target_file_time = std::filesystem::last_write_time(targetFile());
+ for(const auto& object_file : objectFiles)
+ {
+ if(std::filesystem::last_write_time(object_file) > target_file_time)
+ {
+ return true;
+ }
+ }
+
return false;
}
@@ -155,11 +157,6 @@ std::vector<std::string> TaskAR::depends() const
deps.push_back(objectFile.string());
}
- for(const auto& dep : config.depends)
- {
- deps.push_back(dep);
- }
-
return deps;
}
@@ -198,7 +195,7 @@ std::string TaskAR::flagsString() const
for(const auto& dep : config.depends)
{
- if(dep != config.depends[0])
+ if(&dep != &config.depends[0])
{
flagsStr += " ";
}
diff --git a/src/task_ar.h b/src/task_ar.h
index 601afeb..2bb7735 100644
--- a/src/task_ar.h
+++ b/src/task_ar.h
@@ -14,7 +14,8 @@ class TaskAR
: public Task
{
public:
- TaskAR(const ctor::build_configuration& config,
+ TaskAR(ctor::target_type resolved_target_type,
+ const ctor::build_configuration& config,
const ctor::settings& settings,
const std::string& target,
const std::vector<std::string>& objects,
@@ -37,7 +38,6 @@ private:
std::string flagsString() const;
std::vector<std::filesystem::path> objectFiles;
- std::vector<std::filesystem::path> depFiles;
std::filesystem::path _targetFile;
std::filesystem::path flagsFile;
diff --git a/src/task_cc.cc b/src/task_cc.cc
index 9628455..479a453 100644
--- a/src/task_cc.cc
+++ b/src/task_cc.cc
@@ -14,15 +14,22 @@
#include "tools.h"
#include "deps.h"
-TaskCC::TaskCC(const ctor::build_configuration& config_, const ctor::settings& settings_,
- const std::string& sourceDir_, const ctor::source& source)
- : Task(config_, settings_, sourceDir_)
+TaskCC::TaskCC(ctor::target_type resolved_target_type,
+ const ctor::build_configuration& config_,
+ const ctor::settings& settings_,
+ const std::string& sourceDir_,
+ const ctor::source& source)
+ : Task(resolved_target_type, config_, settings_, sourceDir_)
, sourceFile(sourceDir_)
, config(config_)
, settings(settings_)
, sourceDir(sourceDir_)
, _source(source)
{
+ if(source.source_type == ctor::source_type::generated)
+ {
+ sourceFile = std::filesystem::path(settings.builddir) / sourceFile;
+ }
sourceFile /= source.file;
std::filesystem::path base = sourceFile.parent_path();
@@ -31,8 +38,6 @@ TaskCC::TaskCC(const ctor::build_configuration& config_, const ctor::settings& s
base += "-";
base += sourceFile.stem();
- target_type = ctor::target_type::object;
- output_system = config.system;
source_language = source.language;
if(source_language == ctor::language::automatic)
{
@@ -252,6 +257,10 @@ int TaskCC::clean()
std::vector<std::string> TaskCC::depends() const
{
+ if(_source.source_type == ctor::source_type::generated)
+ {
+ return {sourceFile.string()};
+ }
return {};
}
diff --git a/src/task_cc.h b/src/task_cc.h
index 2299fcd..1312417 100644
--- a/src/task_cc.h
+++ b/src/task_cc.h
@@ -14,7 +14,8 @@ class TaskCC
: public Task
{
public:
- TaskCC(const ctor::build_configuration& config,
+ TaskCC(ctor::target_type resolved_target_type,
+ const ctor::build_configuration& config,
const ctor::settings& settings,
const std::string& sourceDir, const ctor::source& source);
virtual ~TaskCC() = default;
diff --git a/src/task_fn.cc b/src/task_fn.cc
index b6b50ea..4f27cc7 100644
--- a/src/task_fn.cc
+++ b/src/task_fn.cc
@@ -11,45 +11,129 @@
#include "execute.h"
#include "util.h"
-TaskFn::TaskFn(const ctor::build_configuration& config_, const ctor::settings& settings_,
- const std::string& sourceDir_, const ctor::source& source)
- : Task(config_, settings_, sourceDir_)
- , sourceFile(sourceDir_)
+TaskFn::TaskFn(ctor::target_type resolved_target_type,
+ const ctor::build_configuration& config_,
+ const ctor::settings& settings_,
+ const std::string& sourceDir_,
+ const ctor::source& source)
+ : Task(resolved_target_type, config_, settings_, sourceDir_)
, config(config_)
, settings(settings_)
{
- sourceFile /= source.file;
-
- std::filesystem::create_directories(std::filesystem::path(settings.builddir) / sourceFile.parent_path());
-
- target_type = config.type;
+ std::filesystem::create_directories(std::filesystem::path(settings.builddir) /
+ sourceDir.parent_path());
source_language = source.language;
- if(source.output.empty())
+ if(config.function_one_to_one)
{
- std::cerr << "Missing output file for functional target\n";
- exit(1);
+ if(source.source_type == ctor::source_type::generated)
+ {
+ sourceFile = std::filesystem::path(settings.builddir);
+ }
+ sourceFile /= sourceDir / source.file;
+
+ std::filesystem::path base = sourceFile.parent_path();
+
+ if(source.output.empty())
+ {
+ std::cerr << "Missing target/output file for functional target.\n";
+ exit(1);
+ }
+ _targetFile = base / source.output;
}
+ else if(config.function_many_to_one)
+ {
+ for(const auto& src : config.sources)
+ {
+ std::filesystem::path _src;
+ if(src.source_type == ctor::source_type::generated)
+ {
+ _src = std::filesystem::path(settings.builddir);
+ }
+ _src /= sourceDir / src.file;
+ sources.push_back(_src.string());
+ }
- _targetFile = source.output;
+ std::filesystem::path base = sourceDir;
+ if(config.target.empty())
+ {
+ std::cerr << "Missing target file for functional target\n";
+ exit(1);
+ }
+ _targetFile = base / config.target;
+ sourceListFile = targetFile().parent_path() / targetFile().filename();
+ sourceListFile += ".sources";
+ }
}
bool TaskFn::dirtyInner()
{
- if(!std::filesystem::exists(sourceFile))
+ if(!std::filesystem::exists(targetFile()))
{
- //std::cout << "Missing source file: " << std::string(sourceFile) << "\n";
+ //std::cout << "Missing targetFile\n";
return true;
}
- if(!std::filesystem::exists(targetFile()))
+ if(config.function_many_to_one)
+ {
+ if(!std::filesystem::exists(sourceListFile))
+ {
+ //std::cout << "Missing sourceListFile\n";
+ return true;
+ }
+
+ {
+ auto lastSourceList = readFile(sourceListFile.string());
+ if(sourceListString() != lastSourceList)
+ {
+ //std::cout << "The compiler sourceList changed\n";
+ return true;
+ }
+ }
+ }
+
+ std::filesystem::file_time_type last_changed{};
+ bool missing{false};
+ if(config.function_one_to_one)
+ {
+ if(!std::filesystem::exists(sourceFile))
+ {
+ missing = true;
+ }
+ last_changed = std::filesystem::last_write_time(sourceFile);
+ }
+ else if(config.function_many_to_one)
+ {
+ bool first{true};
+ for(const auto& source : sources)
+ {
+ if(!std::filesystem::exists(source))
+ {
+ missing |= true;
+ }
+ else
+ {
+ if(first)
+ {
+ last_changed = std::filesystem::last_write_time(source);
+ }
+ else
+ {
+ last_changed =
+ std::max(last_changed,
+ std::filesystem::last_write_time(source));
+ }
+ }
+ first = false;
+ }
+ }
+
+ if(missing)
{
- //std::cout << "Missing targetFile\n";
return true;
}
- if(std::filesystem::last_write_time(sourceFile) >
- std::filesystem::last_write_time(targetFile()))
+ if(last_changed > std::filesystem::last_write_time(targetFile()))
{
//std::cout << "The targetFile older than sourceFile\n";
return true;
@@ -60,10 +144,11 @@ bool TaskFn::dirtyInner()
int TaskFn::runInner()
{
- if(!std::filesystem::exists(sourceFile))
+ if(config.function_many_to_one)
{
- std::cout << "Missing source file: " << sourceFile.string() << "\n";
- return 1;
+ // Write sourceList to file.
+ std::ofstream sourceListStream(sourceListFile.string());
+ sourceListStream << sourceListString();
}
if(settings.verbose >= 0)
@@ -74,10 +159,24 @@ int TaskFn::runInner()
std::cout << output << std::flush;
}
- auto res = config.function(sourceFile.string(),
- targetFile().string(),
- config,
- settings);
+ int res{};
+ if(config.function_one_to_one)
+ {
+ auto func = config.function_one_to_one;
+ res = func(sourceFile.string(),
+ targetFile().string(),
+ config,
+ settings);
+ }
+ else if(config.function_many_to_one)
+ {
+ auto func = config.function_many_to_one;
+ res = func(sources,
+ targetFile().string(),
+ config,
+ settings);
+ }
+
if(res != 0)
{
std::filesystem::remove(targetFile());
@@ -94,12 +193,34 @@ int TaskFn::clean()
std::filesystem::remove(targetFile());
}
+ if(config.function_many_to_one)
+ {
+ if(std::filesystem::exists(sourceListFile))
+ {
+ std::cout << "Removing " << sourceListFile.string() << "\n";
+ std::filesystem::remove(sourceListFile);
+ }
+ }
+
return 0;
}
std::vector<std::string> TaskFn::depends() const
{
- return {};
+ std::vector<std::string> deps;
+ if(config.function_many_to_one)
+ {
+ for(const auto& src : config.sources)
+ {
+ if(src.source_type == ctor::source_type::generated)
+ {
+ std::filesystem::path _src = std::filesystem::path(settings.builddir);
+ _src /= src.file;
+ deps.push_back(_src.string());
+ }
+ }
+ }
+ return deps;
}
std::string TaskFn::target() const
@@ -109,7 +230,17 @@ std::string TaskFn::target() const
std::filesystem::path TaskFn::targetFile() const
{
- return std::filesystem::path(settings.builddir) / sourceDir / _targetFile;
+ return std::filesystem::path(settings.builddir) / _targetFile;
+}
+
+std::string TaskFn::sourceListString() const
+{
+ std::string sourceListStr;
+ for(const auto& source : config.sources)
+ {
+ sourceListStr += " " + source.file;
+ }
+ return sourceListStr;
}
bool TaskFn::derived() const
diff --git a/src/task_fn.h b/src/task_fn.h
index 1bad32c..e8cc5ce 100644
--- a/src/task_fn.h
+++ b/src/task_fn.h
@@ -14,7 +14,8 @@ class TaskFn
: public Task
{
public:
- TaskFn(const ctor::build_configuration& config,
+ TaskFn(ctor::target_type resolved_target_type,
+ const ctor::build_configuration& config,
const ctor::settings& settings,
const std::string& sourceDir, const ctor::source& source);
virtual ~TaskFn() = default;
@@ -28,6 +29,7 @@ public:
std::string target() const override;
std::filesystem::path targetFile() const override;
+ std::string sourceListString() const;
bool derived() const override;
std::string toJSON() const override;
@@ -37,8 +39,10 @@ public:
protected:
std::filesystem::path sourceFile;
std::filesystem::path _targetFile;
+ std::filesystem::path sourceListFile;
const ctor::build_configuration& config;
const ctor::settings& settings;
- std::filesystem::path sourceDir;
+
+ std::vector<std::string> sources;
};
diff --git a/src/task_ld.cc b/src/task_ld.cc
index 03745be..2dceb59 100644
--- a/src/task_ld.cc
+++ b/src/task_ld.cc
@@ -11,26 +11,18 @@
#include "util.h"
#include "tools.h"
-TaskLD::TaskLD(const ctor::build_configuration& config_,
+TaskLD::TaskLD(ctor::target_type resolved_target_type,
+ const ctor::build_configuration& config_,
const ctor::settings& settings_,
const std::string& target,
const std::vector<std::string>& objects,
const std::string& sourceDir_,
bool is_self_)
- : Task(config_, settings_, sourceDir_)
+ : Task(resolved_target_type, config_, settings_, sourceDir_)
, config(config_)
, settings(settings_)
- , sourceDir(sourceDir_)
, is_self(is_self_)
{
- target_type = config.type;
- output_system = config.system;
-
- if(target_type == ctor::target_type::automatic)
- {
- target_type = ctor::target_type::executable;
- }
-
_targetFile = target;
auto toolchain = getToolChain(config.system);
_targetFile = extension(toolchain, target_type, config.system, _targetFile);
@@ -40,13 +32,8 @@ TaskLD::TaskLD(const ctor::build_configuration& config_,
objectFiles.push_back(objectFile);
dependsStr.push_back(objectFile.string());
}
-
- for(const auto& dep : config.depends)
- {
- depFiles.emplace_back(dep);
- }
-
- flagsFile = std::filesystem::path(settings.builddir) / cleanUp(sourceDir) / targetFile().stem();
+ auto cleaned_source_dir = cleanUp(sourceDir.string());
+ flagsFile = std::filesystem::path(settings.builddir) / cleaned_source_dir / targetFile().stem();
flagsFile += ".flags";
source_language = ctor::language::c;
@@ -83,6 +70,15 @@ bool TaskLD::dirtyInner()
}
}
+ auto target_file_time = std::filesystem::last_write_time(targetFile());
+ for(const auto& object_file : objectFiles)
+ {
+ if(std::filesystem::last_write_time(object_file) > target_file_time)
+ {
+ return true;
+ }
+ }
+
return false;
}
@@ -95,7 +91,7 @@ int TaskLD::runInner()
{
auto depFile = dep->targetFile();
auto dep_type = target_type_from_extension(toolchain, depFile);
- if(dep_type == ctor::target_type::dynamic_library)
+ if(dep_type == ctor::target_type::shared_library)
{
append(args, ld_option(toolchain, ctor::ld_opt::library_path,
targetFile().parent_path().string()));
@@ -168,11 +164,6 @@ std::vector<std::string> TaskLD::depends() const
deps.push_back(objectFile.string());
}
- for(const auto& depFile : depFiles)
- {
- deps.push_back(depFile.string());
- }
-
return deps;
}
@@ -211,7 +202,7 @@ std::string TaskLD::flagsString() const
for(const auto& dep : config.depends)
{
- if(dep != config.depends[0])
+ if(&dep != &config.depends[0])
{
flagsStr += " ";
}
diff --git a/src/task_ld.h b/src/task_ld.h
index c0e3ebb..d9156b2 100644
--- a/src/task_ld.h
+++ b/src/task_ld.h
@@ -14,7 +14,8 @@ class TaskLD
: public Task
{
public:
- TaskLD(const ctor::build_configuration& config,
+ TaskLD(ctor::target_type resolved_target_type,
+ const ctor::build_configuration& config,
const ctor::settings& settings,
const std::string& target,
const std::vector<std::string>& objects,
@@ -38,12 +39,10 @@ private:
std::string flagsString() const;
std::vector<std::filesystem::path> objectFiles;
- std::vector<std::filesystem::path> depFiles;
std::filesystem::path _targetFile;
std::filesystem::path flagsFile;
const ctor::build_configuration& config;
const ctor::settings& settings;
- std::string sourceDir;
bool is_self;
};
diff --git a/src/task_so.cc b/src/task_so.cc
index 92aeefe..e1ea6af 100644
--- a/src/task_so.cc
+++ b/src/task_so.cc
@@ -11,22 +11,18 @@
#include "util.h"
#include "tools.h"
-TaskSO::TaskSO(const ctor::build_configuration& config_,
+TaskSO::TaskSO(ctor::target_type resolved_target_type,
+ const ctor::build_configuration& config_,
const ctor::settings& settings_,
const std::string& target,
const std::vector<std::string>& objects,
const std::string& sourceDir_)
- : Task(config_, settings_, sourceDir_)
+ : Task(resolved_target_type, config_, settings_, sourceDir_)
+ , _targetFile(target)
, config(config_)
, settings(settings_)
, sourceDir(sourceDir_)
{
- std::filesystem::path base = sourceDir;
-
- target_type = ctor::target_type::dynamic_library;
- output_system = config.system;
-
- _targetFile = base / target;
auto toolchain = getToolChain(config.system);
_targetFile = extension(toolchain, target_type, config.system, _targetFile);
for(const auto& object : objects)
@@ -36,11 +32,6 @@ TaskSO::TaskSO(const ctor::build_configuration& config_,
dependsStr.push_back(objectFile.string());
}
- for(const auto& dep : config.depends)
- {
- depFiles.emplace_back(dep);
- }
-
flagsFile = std::filesystem::path(settings.builddir) / cleanUp(sourceDir) / targetFile().stem();
flagsFile += ".flags";
@@ -79,6 +70,15 @@ bool TaskSO::dirtyInner()
}
}
+ auto target_file_time = std::filesystem::last_write_time(targetFile());
+ for(const auto& object_file : objectFiles)
+ {
+ if(std::filesystem::last_write_time(object_file) > target_file_time)
+ {
+ return true;
+ }
+ }
+
return false;
}
@@ -90,7 +90,7 @@ int TaskSO::runInner()
append(args, ld_option(toolchain, ctor::ld_opt::position_independent_code));
append(args, ld_option(toolchain, ctor::ld_opt::build_shared));
-
+ append(args, ld_option(toolchain, ctor::ld_opt::custom, "-Wl,-module"));
append(args, ld_option(toolchain, ctor::ld_opt::output, targetFile().string()));
for(const auto& task : getDependsTasks())
@@ -155,11 +155,6 @@ std::vector<std::string> TaskSO::depends() const
deps.push_back(objectFile.string());
}
- for(const auto& depFile : depFiles)
- {
- deps.push_back(depFile.string());
- }
-
return deps;
}
@@ -198,7 +193,7 @@ std::string TaskSO::flagsString() const
for(const auto& dep : config.depends)
{
- if(dep != config.depends[0])
+ if(&dep != &config.depends[0])
{
flagsStr += " ";
}
diff --git a/src/task_so.h b/src/task_so.h
index 60af225..c4283c1 100644
--- a/src/task_so.h
+++ b/src/task_so.h
@@ -14,7 +14,8 @@ class TaskSO
: public Task
{
public:
- TaskSO(const ctor::build_configuration& config,
+ TaskSO(ctor::target_type resolved_target_type,
+ const ctor::build_configuration& config,
const ctor::settings& settings,
const std::string& target,
const std::vector<std::string>& objects,
@@ -37,7 +38,6 @@ private:
std::string flagsString() const;
std::vector<std::filesystem::path> objectFiles;
- std::vector<std::filesystem::path> depFiles;
std::filesystem::path _targetFile;
std::filesystem::path flagsFile;
diff --git a/src/tasks.cc b/src/tasks.cc
index 2f9e47a..5e1d235 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;
@@ -82,30 +82,34 @@ std::vector<std::shared_ptr<Task>> taskFactory(const ctor::build_configuration&
std::filesystem::path targetFile(config.target);
- ctor::target_type target_type{config.type};
- if(target_type == ctor::target_type::automatic)
+ ctor::target_type resolved_target_type{config.type};
+ if(resolved_target_type == ctor::target_type::automatic)
{
- if(config.function != nullptr)
+ if(config.function_one_to_one || config.function_many_to_one)
{
- target_type = ctor::target_type::function;
+ resolved_target_type = ctor::target_type::function;
}
else
{
- target_type = target_type_from_extension(ctor::toolchain::any, targetFile);
+ resolved_target_type =
+ target_type_from_extension(ctor::toolchain::any, targetFile);
}
}
const auto& c = ctor::get_configuration();
std::vector<std::string> objects;
- if(target_type != ctor::target_type::function)
+ if(resolved_target_type != ctor::target_type::function)
{
for(const auto& source : config.sources)
{
if(source.toolchain == ctor::toolchain::any ||
- (config.system == ctor::output_system::build && source.toolchain == c.build_toolchain) ||
- (config.system == ctor::output_system::host && source.toolchain == c.host_toolchain))
+ (config.system == ctor::output_system::build &&
+ source.toolchain == c.build_toolchain) ||
+ (config.system == ctor::output_system::host &&
+ source.toolchain == c.host_toolchain))
{
- auto task = std::make_shared<TaskCC>(config, settings, sourceDir, source);
+ auto task =
+ std::make_shared<TaskCC>(ctor::target_type::object, config, settings, sourceDir, source);
tasks.push_back(task);
objects.push_back(task->targetFile().string());
}
@@ -114,16 +118,27 @@ std::vector<std::shared_ptr<Task>> taskFactory(const ctor::build_configuration&
#ifndef BOOTSTRAP
else
{
- for(const auto& file : config.sources)
+ if(config.function_many_to_one)
{
- auto task = std::make_shared<TaskFn>(config, settings, sourceDir, file);
+ auto task =
+ std::make_shared<TaskFn>(resolved_target_type, config, settings, sourceDir, "");
tasks.push_back(task);
objects.push_back(task->target());
}
+ else
+ {
+ for(const auto& source : config.sources)
+ {
+ auto task =
+ std::make_shared<TaskFn>(resolved_target_type, config, settings, sourceDir, source);
+ tasks.push_back(task);
+ objects.push_back(task->target());
+ }
+ }
}
#endif
- switch(target_type)
+ switch(resolved_target_type)
{
case ctor::target_type::automatic:
// The target_type cannot be Auto
@@ -137,25 +152,30 @@ std::vector<std::shared_ptr<Task>> taskFactory(const ctor::build_configuration&
case ctor::target_type::static_library:
case ctor::target_type::unit_test_library:
- tasks.push_back(std::make_shared<TaskAR>(config, settings, config.target,
- objects, sourceDir));
+ tasks.push_back(std::make_shared<TaskAR>(resolved_target_type, config, settings,
+ config.target, objects, sourceDir));
break;
#ifndef BOOTSTRAP
- case ctor::target_type::dynamic_library:
+ case ctor::target_type::shared_library:
// TODO: Use C++20 starts_with
if(targetFile.stem().string().substr(0, 3) != "lib")
{
std::cerr << "Dynamic library target must have 'lib' prefix\n";
exit(1);
}
- tasks.push_back(std::make_shared<TaskSO>(config, settings, config.target,
- objects, sourceDir));
+ tasks.push_back(std::make_shared<TaskSO>(resolved_target_type, config, settings,
+ config.target, objects, sourceDir));
+ break;
+
+ case ctor::target_type::module:
+ tasks.push_back(std::make_shared<TaskSO>(resolved_target_type, config, settings,
+ config.target, objects, sourceDir));
break;
case ctor::target_type::executable:
case ctor::target_type::unit_test:
- tasks.push_back(std::make_shared<TaskLD>(config, settings, config.target,
- objects, sourceDir, is_self));
+ tasks.push_back(std::make_shared<TaskLD>(resolved_target_type, config, settings,
+ config.target, objects, sourceDir, is_self));
break;
case ctor::target_type::object:
@@ -170,7 +190,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 +200,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/tools.cc b/src/tools.cc
index dfabdff..e9c9b33 100644
--- a/src/tools.cc
+++ b/src/tools.cc
@@ -51,6 +51,7 @@ std::ostream& operator<<(std::ostream& stream, const ctor::cxx_opt& opt)
case ctor::cxx_opt::warn_shadow: stream << "ctor::cxx_opt::warn_shadow"; break;
case ctor::cxx_opt::warn_extra: stream << "ctor::cxx_opt::warn_extra"; break;
case ctor::cxx_opt::warnings_as_errors: stream << "ctor::cxx_opt::warnings_as_errors"; break;
+ case ctor::cxx_opt::exceptions: stream << "ctor::cxx_opt::exceptions"; break;
case ctor::cxx_opt::generate_dep_tree: stream << "ctor::cxx_opt::generate_dep_tree"; break;
case ctor::cxx_opt::no_link: stream << "ctor::cxx_opt::no_link"; break;
case ctor::cxx_opt::include_path: stream << "ctor::cxx_opt::include_path"; break;
@@ -112,7 +113,7 @@ std::ostream& operator<<(std::ostream& stream, const ctor::asm_opt& opt)
return stream;
}
-ctor::toolchain getToolChain(const std::string& compiler)
+ctor::toolchain getToolChain(std::string_view compiler)
{
std::filesystem::path cc(compiler);
auto cc_cmd = cc.stem().string();
@@ -209,7 +210,7 @@ std::string get_arch(ctor::output_system system)
return arch;
}
-ctor::arch get_arch(const std::string& str)
+ctor::arch get_arch(std::string_view str)
{
// gcc -v 2>&1 | grep Target
// Target: x86_64-apple-darwin19.6.0
@@ -244,24 +245,24 @@ ctor::arch get_arch(const std::string& str)
return ctor::arch::unknown;
}
-ctor::c_flag c_option(const std::string& flag)
+ctor::c_flag c_option(std::string_view flag)
{
if(flag.starts_with("-I"))
{
- std::string path = flag.substr(2);
+ std::string path(flag.substr(2));
path.erase(0, path.find_first_not_of(' '));
return { ctor::c_opt::include_path, path };
}
if(flag.starts_with("-std="))
{
- std::string std = flag.substr(5);
+ auto std = flag.substr(5);
return { ctor::c_opt::c_std, std };
}
if(flag.starts_with("-O"))
{
- std::string opt = flag.substr(2, 1);
+ auto opt = flag.substr(2, 1);
return { ctor::c_opt::optimization, opt };
}
@@ -297,7 +298,7 @@ ctor::c_flag c_option(const std::string& flag)
if(flag.starts_with("-D"))
{
- std::string def = flag.substr(2);
+ auto def = flag.substr(2);
auto pos = def.find('=');
if(pos != def.npos)
{
@@ -311,24 +312,24 @@ ctor::c_flag c_option(const std::string& flag)
return { ctor::c_opt::custom, flag };
}
-ctor::cxx_flag cxx_option(const std::string& flag)
+ctor::cxx_flag cxx_option(std::string_view flag)
{
if(flag.starts_with("-I"))
{
- std::string path = flag.substr(2);
+ std::string path(flag.substr(2));
path.erase(0, path.find_first_not_of(' '));
return { ctor::cxx_opt::include_path, path };
}
if(flag.starts_with("-std="))
{
- std::string std = flag.substr(5);
+ auto std = flag.substr(5);
return { ctor::cxx_opt::cpp_std, std };
}
if(flag.starts_with("-O"))
{
- std::string opt = flag.substr(2, 1);
+ auto opt = flag.substr(2, 1);
return { ctor::cxx_opt::optimization, opt };
}
@@ -357,6 +358,11 @@ ctor::cxx_flag cxx_option(const std::string& flag)
return { ctor::cxx_opt::warn_extra};
}
+ if(flag.starts_with("-fexceptions"))
+ {
+ return { ctor::cxx_opt::exceptions};
+ }
+
if(flag.starts_with("-g"))
{
return { ctor::cxx_opt::debug };
@@ -364,7 +370,7 @@ ctor::cxx_flag cxx_option(const std::string& flag)
if(flag.starts_with("-D"))
{
- std::string def = flag.substr(2);
+ auto def = flag.substr(2);
auto pos = def.find('=');
if(pos != def.npos)
{
@@ -379,11 +385,11 @@ ctor::cxx_flag cxx_option(const std::string& flag)
return { ctor::cxx_opt::custom, flag };
}
-ctor::ld_flag ld_option(const std::string& flag)
+ctor::ld_flag ld_option(std::string_view flag)
{
if(flag.starts_with("-L"))
{
- std::string path = flag.substr(2);
+ std::string path(flag.substr(2));
path.erase(0, path.find_first_not_of(' '));
return { ctor::ld_opt::library_path, path };
}
@@ -396,18 +402,18 @@ ctor::ld_flag ld_option(const std::string& flag)
return { ctor::ld_opt::custom, flag };
}
-ctor::ar_flag ar_option(const std::string& flag)
+ctor::ar_flag ar_option(std::string_view flag)
{
return { ctor::ar_opt::custom, flag };
}
-std::vector<std::string> cxx_option(ctor::cxx_opt opt, const std::string& arg,
- const std::string& arg2)
+std::vector<std::string> cxx_option(ctor::cxx_opt opt, std::string_view arg,
+ std::string_view arg2)
{
switch(opt)
{
case ctor::cxx_opt::output:
- return {"-o", arg};
+ return {"-o", std::string(arg)};
case ctor::cxx_opt::debug:
return {"-g"};
case ctor::cxx_opt::warn_all:
@@ -420,16 +426,18 @@ std::vector<std::string> cxx_option(ctor::cxx_opt opt, const std::string& arg,
return {"-Wextra"};
case ctor::cxx_opt::warnings_as_errors:
return {"-Werror"};
+ case ctor::cxx_opt::exceptions:
+ return {"-fexceptions"};
case ctor::cxx_opt::generate_dep_tree:
return {"-MMD"};
case ctor::cxx_opt::no_link:
return {"-c"};
case ctor::cxx_opt::include_path:
- return {"-I" + arg};
+ return {"-I" + std::string(arg)};
case ctor::cxx_opt::cpp_std:
- return {"-std=" + arg};
+ return {"-std=" + std::string(arg)};
case ctor::cxx_opt::optimization:
- return {"-O" + arg};
+ return {"-O" + std::string(arg)};
case ctor::cxx_opt::position_independent_code:
return {"-fPIC"};
case ctor::cxx_opt::position_independent_executable:
@@ -437,9 +445,9 @@ std::vector<std::string> cxx_option(ctor::cxx_opt opt, const std::string& arg,
case ctor::cxx_opt::define:
if(!arg2.empty())
{
- return {"-D" + arg + "=" + arg2};
+ return {"-D" + std::string(arg) + "=" + std::string(arg2)};
}
- return {"-D" + arg};
+ return {"-D" + std::string(arg)};
case ctor::cxx_opt::custom:
return argsplit(arg);
}
@@ -448,13 +456,13 @@ std::vector<std::string> cxx_option(ctor::cxx_opt opt, const std::string& arg,
return {};
}
-std::vector<std::string> c_option(ctor::c_opt opt, const std::string& arg,
- const std::string& arg2)
+std::vector<std::string> c_option(ctor::c_opt opt, std::string_view arg,
+ std::string_view arg2)
{
switch(opt)
{
case ctor::c_opt::output:
- return {"-o", arg};
+ return {"-o", std::string(arg)};
case ctor::c_opt::debug:
return {"-g"};
case ctor::c_opt::warn_all:
@@ -472,11 +480,11 @@ std::vector<std::string> c_option(ctor::c_opt opt, const std::string& arg,
case ctor::c_opt::no_link:
return {"-c"};
case ctor::c_opt::include_path:
- return {"-I" + arg};
+ return {"-I" + std::string(arg)};
case ctor::c_opt::c_std:
- return {"-std=" + arg};
+ return {"-std=" + std::string(arg)};
case ctor::c_opt::optimization:
- return {"-O" + arg};
+ return {"-O" + std::string(arg)};
case ctor::c_opt::position_independent_code:
return {"-fPIC"};
case ctor::c_opt::position_independent_executable:
@@ -484,9 +492,9 @@ std::vector<std::string> c_option(ctor::c_opt opt, const std::string& arg,
case ctor::c_opt::define:
if(!arg2.empty())
{
- return {"-D" + arg + "=" + arg2};
+ return {"-D" + std::string(arg) + "=" + std::string(arg2)};
}
- return {"-D" + arg};
+ return {"-D" + std::string(arg)};
case ctor::c_opt::custom:
return argsplit(arg);
}
@@ -495,23 +503,23 @@ std::vector<std::string> c_option(ctor::c_opt opt, const std::string& arg,
return {};
}
-std::vector<std::string> ld_option(ctor::ld_opt opt, const std::string& arg,
- [[maybe_unused]]const std::string& arg2)
+std::vector<std::string> ld_option(ctor::ld_opt opt, std::string_view arg,
+ [[maybe_unused]]std::string_view arg2)
{
switch(opt)
{
case ctor::ld_opt::output:
- return {"-o", arg};
+ return {"-o", std::string(arg)};
case ctor::ld_opt::warn_all:
return {"-Wall"};
case ctor::ld_opt::warnings_as_errors:
return {"-Werror"};
case ctor::ld_opt::library_path:
- return {"-L" + arg};
+ return {"-L" + std::string(arg)};
case ctor::ld_opt::link:
- return {"-l" + arg};
+ return {"-l" + std::string(arg)};
case ctor::ld_opt::cpp_std:
- return {"-std=" + arg};
+ return {"-std=" + std::string(arg)};
case ctor::ld_opt::build_shared:
return {"-shared"};
case ctor::ld_opt::threads:
@@ -528,8 +536,8 @@ std::vector<std::string> ld_option(ctor::ld_opt opt, const std::string& arg,
return {};
}
-std::vector<std::string> ar_option(ctor::ar_opt opt, const std::string& arg,
- [[maybe_unused]]const std::string& arg2)
+std::vector<std::string> ar_option(ctor::ar_opt opt, std::string_view arg,
+ [[maybe_unused]]std::string_view arg2)
{
switch(opt)
{
@@ -540,7 +548,7 @@ std::vector<std::string> ar_option(ctor::ar_opt opt, const std::string& arg,
case ctor::ar_opt::create:
return {"-c"};
case ctor::ar_opt::output:
- return {arg};
+ return {std::string(arg)};
case ctor::ar_opt::custom:
return argsplit(arg);
}
@@ -549,8 +557,8 @@ std::vector<std::string> ar_option(ctor::ar_opt opt, const std::string& arg,
return {};
}
-std::vector<std::string> asm_option(ctor::asm_opt opt, const std::string& arg,
- [[maybe_unused]]const std::string& arg2)
+std::vector<std::string> asm_option(ctor::asm_opt opt, std::string_view arg,
+ [[maybe_unused]]std::string_view arg2)
{
switch(opt)
{
@@ -578,7 +586,7 @@ std::string get_arch(ctor::output_system system)
return {};
}
-ctor::arch get_arch(ctor::output_system system, const std::string& str)
+ctor::arch get_arch(ctor::output_system system, std::string_view str)
{
auto toolchain = getToolChain(system);
switch(toolchain)
@@ -595,8 +603,8 @@ ctor::arch get_arch(ctor::output_system system, const std::string& str)
std::vector<std::string> c_option(ctor::toolchain toolchain,
ctor::c_opt opt,
- const std::string& arg,
- const std::string& arg2)
+ std::string_view arg,
+ std::string_view arg2)
{
switch(toolchain)
{
@@ -628,8 +636,8 @@ std::vector<std::string> c_option(ctor::toolchain toolchain,
std::vector<std::string> cxx_option(ctor::toolchain toolchain,
ctor::cxx_opt opt,
- const std::string& arg,
- const std::string& arg2)
+ std::string_view arg,
+ std::string_view arg2)
{
switch(toolchain)
{
@@ -661,8 +669,8 @@ std::vector<std::string> cxx_option(ctor::toolchain toolchain,
std::vector<std::string> ld_option(ctor::toolchain toolchain,
ctor::ld_opt opt,
- const std::string& arg,
- const std::string& arg2)
+ std::string_view arg,
+ std::string_view arg2)
{
switch(toolchain)
{
@@ -694,8 +702,8 @@ std::vector<std::string> ld_option(ctor::toolchain toolchain,
std::vector<std::string> ar_option(ctor::toolchain toolchain,
ctor::ar_opt opt,
- const std::string& arg,
- const std::string& arg2)
+ std::string_view arg,
+ std::string_view arg2)
{
switch(toolchain)
{
@@ -727,8 +735,8 @@ std::vector<std::string> ar_option(ctor::toolchain toolchain,
std::vector<std::string> asm_option(ctor::toolchain toolchain,
ctor::asm_opt opt,
- const std::string& arg,
- const std::string& arg2)
+ std::string_view arg,
+ std::string_view arg2)
{
switch(toolchain)
{
@@ -759,7 +767,7 @@ std::vector<std::string> asm_option(ctor::toolchain toolchain,
}
-ctor::c_flag c_option(const std::string& flag, ctor::toolchain toolchain)
+ctor::c_flag c_option(std::string_view flag, ctor::toolchain toolchain)
{
switch(toolchain)
{
@@ -774,7 +782,7 @@ ctor::c_flag c_option(const std::string& flag, ctor::toolchain toolchain)
return { ctor::c_opt::custom, flag };
}
-ctor::cxx_flag cxx_option(const std::string& flag, ctor::toolchain toolchain)
+ctor::cxx_flag cxx_option(std::string_view flag, ctor::toolchain toolchain)
{
switch(toolchain)
{
@@ -789,7 +797,7 @@ ctor::cxx_flag cxx_option(const std::string& flag, ctor::toolchain toolchain)
return { ctor::cxx_opt::custom, flag };
}
-ctor::ld_flag ld_option(const std::string& flag, ctor::toolchain toolchain)
+ctor::ld_flag ld_option(std::string_view flag, ctor::toolchain toolchain)
{
switch(toolchain)
{
@@ -804,7 +812,7 @@ ctor::ld_flag ld_option(const std::string& flag, ctor::toolchain toolchain)
return { ctor::ld_opt::custom, flag };
}
-ctor::ar_flag ar_option(const std::string& flag, ctor::toolchain toolchain)
+ctor::ar_flag ar_option(std::string_view flag, ctor::toolchain toolchain)
{
switch(toolchain)
{
@@ -819,7 +827,7 @@ ctor::ar_flag ar_option(const std::string& flag, ctor::toolchain toolchain)
return { ctor::ar_opt::custom, flag };
}
-ctor::asm_flag asm_option(const std::string& flag, ctor::toolchain toolchain)
+ctor::asm_flag asm_option(std::string_view flag, ctor::toolchain toolchain)
{
switch(toolchain)
{
@@ -896,7 +904,7 @@ std::vector<std::string> to_strings(ctor::toolchain toolchain,
}
namespace {
-ctor::toolchain guess_toolchain(const std::string& opt)
+ctor::toolchain guess_toolchain(std::string_view opt)
{
if(opt.empty())
{
@@ -916,32 +924,33 @@ ctor::toolchain guess_toolchain(const std::string& opt)
}
}
+
template<>
-ctor::flag<ctor::c_opt>::flag(const char* str)
+void ctor::flag<ctor::c_opt>::to_flag(std::string_view str)
{
*this = c_option(str, guess_toolchain(str));
}
template<>
-ctor::flag<ctor::cxx_opt>::flag(const char* str)
+void ctor::flag<ctor::cxx_opt>::to_flag(std::string_view str)
{
*this = cxx_option(str, guess_toolchain(str));
}
template<>
-ctor::flag<ctor::ld_opt>::flag(const char* str)
+void ctor::flag<ctor::ld_opt>::to_flag(std::string_view str)
{
*this = ld_option(str, guess_toolchain(str));
}
template<>
-ctor::flag<ctor::ar_opt>::flag(const char* str)
+void ctor::flag<ctor::ar_opt>::to_flag(std::string_view str)
{
*this = ar_option(str, guess_toolchain(str));
}
template<>
-ctor::flag<ctor::asm_opt>::flag(const char* str)
+void ctor::flag<ctor::asm_opt>::to_flag(std::string_view str)
{
*this = asm_option(str, guess_toolchain(str));
}
@@ -965,7 +974,14 @@ ctor::target_type target_type_from_extension(ctor::toolchain toolchain,
if(ext == ".so" ||
ext == ".dylib")
{
- return ctor::target_type::dynamic_library;
+ if(file.filename().string().substr(0, 3) != "lib")
+ {
+ return ctor::target_type::module;
+ }
+ else
+ {
+ return ctor::target_type::shared_library;
+ }
}
if(ext == ".o")
@@ -994,7 +1010,7 @@ ctor::target_type target_type_from_extension(ctor::toolchain toolchain,
if(ext == ".dll")
{
- return ctor::target_type::dynamic_library;
+ return ctor::target_type::shared_library;
}
if(ext == ".obj")
@@ -1078,7 +1094,8 @@ std::filesystem::path extension(ctor::toolchain toolchain,
break;
}
break;
- case ctor::target_type::dynamic_library:
+ case ctor::target_type::shared_library:
+ case ctor::target_type::module:
switch(arch)
{
case ctor::arch::unix:
diff --git a/src/tools.h b/src/tools.h
index 0e7fc15..b8df022 100644
--- a/src/tools.h
+++ b/src/tools.h
@@ -18,10 +18,10 @@ std::ostream& operator<<(std::ostream& stream, const ctor::ar_opt& opt);
std::ostream& operator<<(std::ostream& stream, const ctor::asm_opt& opt);
std::string get_arch(ctor::output_system system);
-ctor::arch get_arch(ctor::output_system system, const std::string& str);
+ctor::arch get_arch(ctor::output_system system, std::string_view str);
//! Get tool-chain type from compiler path string
-ctor::toolchain getToolChain(const std::string& compiler);
+ctor::toolchain getToolChain(std::string_view compiler);
//! Get tool-chain type from output system (via configuration)
ctor::toolchain getToolChain(ctor::output_system system);
@@ -32,63 +32,63 @@ ctor::toolchain getToolChain(ctor::output_system system);
//! tool-chain
std::vector<std::string> c_option(ctor::toolchain toolchain,
ctor::c_opt option,
- const std::string& arg = {},
- const std::string& arg2 = {});
+ std::string_view arg = {},
+ std::string_view arg2 = {});
//! Get tool argument(s) for specific option type matching the supplied
//! tool-chain
std::vector<std::string> cxx_option(ctor::toolchain toolchain,
ctor::cxx_opt option,
- const std::string& arg = {},
- const std::string& arg2 = {});
+ std::string_view arg = {},
+ std::string_view arg2 = {});
//! Get tool argument(s) for specific option type matching the supplied
//! tool-chain
std::vector<std::string> ld_option(ctor::toolchain toolchain,
ctor::ld_opt option,
- const std::string& arg = {},
- const std::string& arg2 = {});
+ std::string_view arg = {},
+ std::string_view arg2 = {});
//! Get tool argument(s) for specific option type matching the supplied
//! tool-chain
std::vector<std::string> ar_option(ctor::toolchain toolchain,
ctor::ar_opt option,
- const std::string& arg = {},
- const std::string& arg2 = {});
+ std::string_view arg = {},
+ std::string_view arg2 = {});
//! Get tool argument(s) for specific option type matching the supplied
//! tool-chain
std::vector<std::string> asm_option(ctor::toolchain toolchain,
ctor::asm_opt option,
- const std::string& arg = {},
- const std::string& arg2 = {});
+ std::string_view arg = {},
+ std::string_view arg2 = {});
//! Get ctor::c_opt enum value and argument from string,
//! ie. { ctor::c_opt::inlude_path, "foo/bar" } from "-Ifoo/bar"
//! Returns { ctor::c_opt::custom, flag } if unknown.
-ctor::c_flag c_option(const std::string& flag, ctor::toolchain toolchain);
+ctor::c_flag c_option(std::string_view flag, ctor::toolchain toolchain);
//! Get ctor::cxx_opt enum value and argument from string,
//! ie. { ctor::cxx_opt::inlude_path, "foo/bar" } from "-Ifoo/bar"
//! Returns { ctor::cxx_opt::custom, flag } if unknown.
-ctor::cxx_flag cxx_option(const std::string& flag, ctor::toolchain toolchain);
+ctor::cxx_flag cxx_option(std::string_view flag, ctor::toolchain toolchain);
//! Get ctor::ld_opt enum value and argument from string,
//! ie. { ctor::ld_opt::inlude_path, "foo/bar" } from "-Ifoo/bar"
//! Returns { ctor::ld_opt::custom, flag } if unknown.
-ctor::ld_flag ld_option(const std::string& flag, ctor::toolchain toolchain);
+ctor::ld_flag ld_option(std::string_view flag, ctor::toolchain toolchain);
//! Get ctor::ar_opt enum value and argument from string,
//! ie. { ctor::ar_opt::inlude_path, "foo/bar" } from "-Ifoo/bar"
//! Returns { ctor::ar_opt::custom, flag } if unknown.
-ctor::ar_flag ar_option(const std::string& flag, ctor::toolchain toolchain);
+ctor::ar_flag ar_option(std::string_view flag, ctor::toolchain toolchain);
//! Get ctor::asm_opt enum value and argument from string,
//! ie. { ctor::asm_opt::inlude_path, "foo/bar" } from "-Ifoo/bar"
//! Returns { ctor::asm_opt::custom, flag } if unknown.
-ctor::asm_flag asm_option(const std::string& flag, ctor::toolchain toolchain);
+ctor::asm_flag asm_option(std::string_view flag, ctor::toolchain toolchain);
diff --git a/src/util.cc b/src/util.cc
index 6fc650a..7ca9c11 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 static_cast<char>(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)
@@ -75,7 +84,7 @@ bool isClean(char c)
}
}
-std::string cleanUp(const std::string& path)
+std::string cleanUp(std::string_view path)
{
std::string cleaned;
for(const auto& c : path)
@@ -92,7 +101,7 @@ std::string cleanUp(const std::string& path)
return cleaned;
}
-std::string esc(const std::string& in)
+std::string esc(std::string_view in)
{
std::string out;
for(auto c : in)
@@ -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(std::string_view 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
@@ -186,7 +205,7 @@ std::string locate(const std::string& prog,
return {};
}
-std::vector<std::string> argsplit(const std::string& str)
+std::vector<std::string> argsplit(std::string_view str)
{
enum class state
{
@@ -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..7dbe2bb 100644
--- a/src/util.h
+++ b/src/util.h
@@ -9,11 +9,11 @@
#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);
-std::string cleanUp(const std::string& path);
+std::string cleanUp(std::string_view path);
template<typename T>
void append(T& a, const T& b)
@@ -21,13 +21,21 @@ void append(T& a, const T& b)
a.insert(a.end(), b.begin(), b.end());
}
-std::string esc(const std::string& in);
+std::string esc(std::string_view 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(std::string_view path_env = {});
std::string locate(const std::string& app,
const std::vector<std::string>& paths,
const std::string& arch = {});
//! Splits string into tokens adhering to quotations " and '
-std::vector<std::string> argsplit(const std::string& str);
+std::vector<std::string> argsplit(std::string_view 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);
diff --git a/test/argparser_test.cc b/test/argparser_test.cc
new file mode 100644
index 0000000..b649e8c
--- /dev/null
+++ b/test/argparser_test.cc
@@ -0,0 +1,1019 @@
+#include <argparser.h>
+
+#include <iostream>
+#include <string>
+
+std::ostream& operator<<(std::ostream& ostr, const arg::error& err)
+{
+ switch(err)
+ {
+ case arg::error::invalid_arg:
+ ostr << "arg::error::invalid_arg";
+ break;
+ case arg::error::invalid_opt:
+ ostr << "arg::error::invalid_opt";
+ break;
+ case arg::error::missing_arg:
+ ostr << "arg::error::missing_arg";
+ break;
+ }
+ return ostr;
+}
+
+#include <uunit.h>
+
+class ArgParserTest
+ : public uUnit
+{
+public:
+ ArgParserTest()
+ {
+ uTEST(ArgParserTest::test_zero);
+ uTEST(ArgParserTest::test_one);
+ uTEST(ArgParserTest::test_many);
+ uTEST(ArgParserTest::test_exceptional);
+ uTEST(ArgParserTest::test_err_callback);
+ uTEST(ArgParserTest::test_pos_callback);
+ uTEST(ArgParserTest::test_grouped);
+ uTEST(ArgParserTest::test_nullprogram);
+ }
+
+ void test_zero()
+ {
+ const char* const argv[] = { "app-name" };
+ int argc = sizeof(argv)/sizeof(*argv);
+
+ arg::Parser<int> args(argc, argv);
+
+ auto res = args.parse();
+ uASSERT_EQUAL(0, res);
+ }
+
+ void test_one()
+ {
+ const char* argv[] = { "app-name", "-x", "42" };
+ int argc = sizeof(argv)/sizeof(*argv);
+
+ arg::Parser<int> args(argc, argv);
+
+ int x{};
+ args.add('x', "--long-x",
+ std::function([&](int i){ x = i; return 0;}), "Help x");
+
+ auto res = args.parse();
+ uASSERT_EQUAL(0, res);
+ uASSERT_EQUAL(42, x);
+ }
+
+ void test_many()
+ {
+ const char* argv[] = { "app-name", "-x", "42", "-y17", "-z",
+ "--long-X=12", "--long-Y", "18" };
+ int argc = sizeof(argv)/sizeof(*argv);
+
+ arg::Parser<int> args(argc, argv);
+
+ int x{};
+ int y{};
+ bool z{false};
+ int X{};
+ int Y{};
+
+ args.add('x', "--long-x",
+ std::function([&](int i){ x = i; return 0;}), "Help x");
+
+ args.add('y', "--long-y",
+ std::function([&](int i){ y = i; return 0;}), "Help y");
+
+ args.add('z', "--long-z",
+ std::function([&](){ z = true; return 0;}), "Help z");
+
+ args.add('X', "--long-X",
+ std::function([&](int i){ X = i; return 0;}), "Help X");
+
+ args.add('Y', "--long-Y",
+ std::function([&](int i){ Y = i; return 0;}), "Help Y");
+
+ auto res = args.parse();
+ uASSERT_EQUAL(0, res);
+ uASSERT_EQUAL(42, x);
+ uASSERT_EQUAL(17, y);
+ uASSERT_EQUAL(true, z);
+ uASSERT_EQUAL(12, X);
+ uASSERT_EQUAL(18, Y);
+ }
+
+ void test_exceptional()
+ {
+
+ { // Missing arg at trailing opt
+ const char* argv[] = { "app-name", "-x" };
+ int argc = sizeof(argv)/sizeof(*argv);
+ arg::Parser<int> args(argc, argv);
+
+ int x{};
+ int y{};
+
+ args.add('x', "--long-x",
+ std::function([&](int i){ x = i; return 0;}), "Help x");
+
+ args.add('y', "--long-y",
+ std::function([&](int i){ y = i; return 0;}), "Help y");
+
+ auto res = args.parse();
+ uASSERT_EQUAL(1, res);
+ }
+
+ { // Missing arg before other opt
+ const char* argv[] = { "app-name", "-x", "-y" };
+ int argc = sizeof(argv)/sizeof(*argv);
+ arg::Parser<int> args(argc, argv);
+
+ int x{};
+ int y{};
+
+ args.add('x', "--long-x",
+ std::function([&](int i){ x = i; return 0;}), "Help x");
+
+ args.add('y', "--long-y",
+ std::function([&](int i){ y = i; return 0;}), "Help y");
+
+ auto res = args.parse();
+ uASSERT_EQUAL(1, res);
+ }
+
+ { // Unknown arg
+ const char* argv[] = { "app-name", "-T" };
+ int argc = sizeof(argv)/sizeof(*argv);
+ arg::Parser<int> args(argc, argv);
+
+ int x{};
+ int y{};
+
+ args.add('x', "--long-x",
+ std::function([&](int i){ x = i; return 0;}), "Help x");
+
+ args.add('y', "--long-y",
+ std::function([&](int i){ y = i; return 0;}), "Help y");
+
+ auto res = args.parse();
+ uASSERT_EQUAL(1, res);
+ }
+ }
+
+ void test_err_callback()
+ {
+ using namespace std::string_literals;
+
+ { // Missing arg at trailing opt
+ const char* argv[] = { "app-name", "-x" };
+ int argc = sizeof(argv)/sizeof(*argv);
+ arg::Parser<int> args(argc, argv);
+
+ int x{};
+ args.add('x', "--long-x",
+ std::function([&](int i){ x = i; return 0;}), "Help x");
+
+ bool called{false};
+ args.set_err_cb(
+ [&](arg::error err, std::string_view opt)
+ {
+ called = true;
+ uASSERT_EQUAL(arg::error::missing_arg, err);
+ uASSERT_EQUAL("-x"s, opt);
+ });
+ auto res = args.parse();
+ uASSERT_EQUAL(1, res);
+ uASSERT(called);
+ }
+
+ { // Invalid arg format
+ const char* argv[] = { "app-name", "-x", "abc" };
+ int argc = sizeof(argv)/sizeof(*argv);
+ arg::Parser<int> args(argc, argv);
+
+ int x{};
+ args.add('x', "--long-x",
+ std::function([&](int i){ x = i; return 0;}), "Help x");
+
+ bool called{false};
+ args.set_err_cb(
+ [&](arg::error err, std::string_view opt)
+ {
+ called = true;
+ uASSERT_EQUAL(arg::error::invalid_arg, err);
+ uASSERT_EQUAL("abc"s, opt);
+ });
+ auto res = args.parse();
+ uASSERT_EQUAL(1, res);
+ uASSERT(called);
+ }
+
+ { // Invalid opt
+ const char* argv[] = { "app-name", "-y" };
+ int argc = sizeof(argv)/sizeof(*argv);
+ arg::Parser<int> args(argc, argv);
+
+ int x{};
+ args.add('x', "--long-x",
+ std::function([&](int i){ x = i; return 0;}), "Help x");
+
+ bool called{false};
+ args.set_err_cb(
+ [&](arg::error err, std::string_view opt)
+ {
+ called = true;
+ uASSERT_EQUAL(arg::error::invalid_opt, err);
+ uASSERT_EQUAL("-y"s, opt);
+ });
+ auto res = args.parse();
+ uASSERT_EQUAL(1, res);
+ uASSERT(called);
+ }
+ }
+
+ void test_pos_callback()
+ {
+ using namespace std::string_literals;
+ const char* argv[] =
+ { "app-name", "foo", "-x", "42", "bar", "-X43", "-Y", "42" };
+ int argc = sizeof(argv)/sizeof(*argv);
+ arg::Parser<int, std::optional<int>> args(argc, argv);
+
+ int x{};
+ int X{};
+ int Y{};
+ args.add('x', "--long-x",
+ std::function([&](int i){ x = i; return 0;}), "Help x");
+
+ args.add('X', "--opt-x",
+ std::function([&](std::optional<int> i)
+ {
+ uASSERT(i.has_value());
+ X = *i;
+ return 0;
+ }),
+ "Help X");
+
+ args.add('Y', "--opt-y",
+ std::function([&](std::optional<int> i)
+ {
+ uASSERT(!i.has_value());
+ Y = 1;
+ return 0;
+ }),
+ "Help X");
+
+ std::vector<std::string> pos;
+ args.set_pos_cb(
+ [&](std::string_view sv)
+ {
+ pos.push_back(std::string(sv));
+ return 0;
+ });
+ auto res = args.parse();
+ uASSERT_EQUAL(0, res);
+ uASSERT_EQUAL(3u, pos.size());
+ uASSERT_EQUAL("foo"s, pos[0]);
+ uASSERT_EQUAL("bar"s, pos[1]);
+ uASSERT_EQUAL("42"s, pos[2]);
+ uASSERT_EQUAL(42, x);
+ uASSERT_EQUAL(43, X);
+ uASSERT_EQUAL(1, Y);
+ }
+
+ void test_grouped()
+ {
+ {
+ const char* argv[] = { "app-name", "-xyz", "42" };
+ int argc = sizeof(argv)/sizeof(*argv);
+
+ arg::Parser<int> args(argc, argv);
+
+ bool x{false};
+ bool y{false};
+ int z{};
+ args.add('x', "--long-x",
+ std::function([&](){ x = true; return 0;}), "Help x");
+
+ args.add('y', "--long-y",
+ std::function([&](){ y = true; return 0;}), "Help y");
+
+ args.add('z', "--long-z",
+ std::function([&](int i){ z = i; return 0;}), "Help z");
+
+ auto res = args.parse();
+ uASSERT_EQUAL(0, res);
+ uASSERT(x);
+ uASSERT(y);
+ uASSERT_EQUAL(42, z);
+ }
+
+ {
+ const char* argv[] = { "app-name", "-xyz42" };
+ int argc = sizeof(argv)/sizeof(*argv);
+
+ arg::Parser<int> args(argc, argv);
+
+ bool x{false};
+ bool y{false};
+ int z{};
+ args.add('x', "--long-x",
+ std::function([&](){ x = true; return 0;}), "Help x");
+
+ args.add('y', "--long-y",
+ std::function([&](){ y = true; return 0;}), "Help y");
+
+ args.add('z', "--long-z",
+ std::function([&](int i){ z = i; return 0;}), "Help z");
+
+ auto res = args.parse();
+ uASSERT_EQUAL(0, res);
+ uASSERT(x);
+ uASSERT(y);
+ uASSERT_EQUAL(42, z);
+ }
+
+
+ {
+ const char* argv[] = { "app-name", "-xyz42" };
+ int argc = sizeof(argv)/sizeof(*argv);
+
+ arg::Parser<int, std::optional<int>> args(argc, argv);
+
+ bool x{false};
+ bool y{false};
+ int z{};
+ args.add('x', "--long-x",
+ std::function([&](){ x = true; return 0;}), "Help x");
+
+ args.add('y', "--long-y",
+ std::function([&](){ y = true; return 0;}), "Help y");
+
+ args.add('z', "--long-z",
+ std::function([&](std::optional<int> i)
+ {
+ uASSERT(i.has_value());
+ z = *i; return 0;
+ }), "Help z");
+
+ auto res = args.parse();
+ uASSERT_EQUAL(0, res);
+ uASSERT(x);
+ uASSERT(y);
+ uASSERT_EQUAL(42, z);
+ }
+
+ {
+ const char* argv[] = { "app-name", "-xyz" };
+ int argc = sizeof(argv)/sizeof(*argv);
+
+ arg::Parser<int, std::optional<int>> args(argc, argv);
+
+ bool x{false};
+ bool y{false};
+ int z{};
+ args.add('x', "--long-x",
+ std::function([&](){ x = true; return 0;}), "Help x");
+
+ args.add('y', "--long-y",
+ std::function([&](){ y = true; return 0;}), "Help y");
+
+ args.add('z', "--long-z",
+ std::function([&](std::optional<int> i)
+ {
+ uASSERT(!i.has_value());
+ z = 1; return 0;
+ }), "Help z");
+
+ auto res = args.parse();
+ uASSERT_EQUAL(0, res);
+ uASSERT(x);
+ uASSERT(y);
+ uASSERT_EQUAL(1, z);
+ }
+
+ {
+ const char* argv[] = { "app-name", "-xyz", "42" };
+ int argc = sizeof(argv)/sizeof(*argv);
+
+ arg::Parser<int, std::optional<int>> args(argc, argv);
+
+ bool x{false};
+ bool y{false};
+ int z{};
+ args.add('x', "--long-x",
+ std::function([&](){ x = true; return 0;}), "Help x");
+
+ args.add('y', "--long-y",
+ std::function([&](){ y = true; return 0;}), "Help y");
+
+ args.add('z', "--long-z",
+ std::function([&](std::optional<int> i)
+ {
+ uASSERT(!i.has_value());
+ z = 1; return 0;
+ }), "Help z");
+
+ auto res = args.parse();
+ uASSERT_EQUAL(0, res);
+ uASSERT(x);
+ uASSERT(y);
+ uASSERT_EQUAL(1, z);
+ }
+ }
+
+ void test_nullprogram()
+ {
+ using namespace std::string_literals;
+ // Inspired by https://nullprogram.com/blog/2020/08/01/
+
+ //
+ // Short options
+ //
+ {
+ const char* argv[] = { "program", "-a", "-b", "-c" };
+ int argc = sizeof(argv)/sizeof(*argv);
+
+ arg::Parser<> args(argc, argv);
+
+ std::vector<char> r;
+ args.add('a', {}, std::function([&](){ r.push_back('a'); return 0;}), {});
+ args.add('b', {}, std::function([&](){ r.push_back('b'); return 0;}), {});
+ args.add('c', {}, std::function([&](){ r.push_back('c'); return 0;}), {});
+
+ auto res = args.parse();
+ uASSERT_EQUAL(0, res);
+ uASSERT_EQUAL(3u, r.size());
+ uASSERT_EQUAL('a', r[0]);
+ uASSERT_EQUAL('b', r[1]);
+ uASSERT_EQUAL('c', r[2]);
+ }
+
+ {
+ const char* argv[] = { "program", "-abc" };
+ int argc = sizeof(argv)/sizeof(*argv);
+
+ arg::Parser<> args(argc, argv);
+
+ std::vector<char> r;
+ args.add('a', {}, std::function([&](){ r.push_back('a'); return 0;}), {});
+ args.add('b', {}, std::function([&](){ r.push_back('b'); return 0;}), {});
+ args.add('c', {}, std::function([&](){ r.push_back('c'); return 0;}), {});
+
+ auto res = args.parse();
+ uASSERT_EQUAL(0, res);
+ uASSERT_EQUAL(3u, r.size());
+ uASSERT_EQUAL('a', r[0]);
+ uASSERT_EQUAL('b', r[1]);
+ uASSERT_EQUAL('c', r[2]);
+ }
+
+ {
+ const char* argv[] = { "program", "-acb" };
+ int argc = sizeof(argv)/sizeof(*argv);
+
+ arg::Parser<> args(argc, argv);
+
+ std::vector<char> r;
+ args.add('a', {}, std::function([&](){ r.push_back('a'); return 0;}), {});
+ args.add('b', {}, std::function([&](){ r.push_back('b'); return 0;}), {});
+ args.add('c', {}, std::function([&](){ r.push_back('c'); return 0;}), {});
+
+ auto res = args.parse();
+ uASSERT_EQUAL(0, res);
+ uASSERT_EQUAL(3u, r.size());
+ uASSERT_EQUAL('a', r[0]);
+ uASSERT_EQUAL('c', r[1]);
+ uASSERT_EQUAL('b', r[2]);
+ }
+
+ {
+ const char* argv[] = { "program", "-i", "input.txt", "-o", "output.txt" };
+ int argc = sizeof(argv)/sizeof(*argv);
+
+ arg::Parser<std::string> args(argc, argv);
+
+ std::vector<std::string> r;
+ args.add('i', {},
+ std::function([&](std::string input)
+ {
+ r.push_back(input);
+ return 0;
+ }), {});
+
+ args.add('o', {},
+ std::function([&](std::string input)
+ {
+ r.push_back(input);
+ return 0;
+ }), {});
+
+
+ auto res = args.parse();
+ uASSERT_EQUAL(0, res);
+ uASSERT_EQUAL(2u, r.size());
+ uASSERT_EQUAL("input.txt"s, r[0]);
+ uASSERT_EQUAL("output.txt"s, r[1]);
+ }
+
+ {
+ const char* argv[] = { "program", "-iinput.txt", "-ooutput.txt" };
+ int argc = sizeof(argv)/sizeof(*argv);
+
+ arg::Parser<std::string> args(argc, argv);
+
+ std::vector<std::string> r;
+ args.add('i', {},
+ std::function([&](std::string input)
+ {
+ r.push_back(input);
+ return 0;
+ }), {});
+ args.add('o', {},
+ std::function([&](std::string input)
+ {
+ r.push_back(input);
+ return 0;
+ }), {});
+
+
+ auto res = args.parse();
+ uASSERT_EQUAL(0, res);
+ uASSERT_EQUAL(2u, r.size());
+ uASSERT_EQUAL("input.txt"s, r[0]);
+ uASSERT_EQUAL("output.txt"s, r[1]);
+ }
+
+ {
+ const char* argv[] = { "program", "-abco", "output.txt" };
+ int argc = sizeof(argv)/sizeof(*argv);
+
+ arg::Parser<std::string> args(argc, argv);
+
+ std::vector<std::string> r;
+ args.add('a', {}, std::function([&](){ r.push_back("a"); return 0;}), {});
+ args.add('b', {}, std::function([&](){ r.push_back("b"); return 0;}), {});
+ args.add('c', {}, std::function([&](){ r.push_back("c"); return 0;}), {});
+ args.add('o', {},
+ std::function([&](std::string input)
+ {
+ r.push_back(input);
+ return 0;
+ }), {});
+
+
+ auto res = args.parse();
+ uASSERT_EQUAL(0, res);
+ uASSERT_EQUAL(4u, r.size());
+ uASSERT_EQUAL("a"s, r[0]);
+ uASSERT_EQUAL("b"s, r[1]);
+ uASSERT_EQUAL("c"s, r[2]);
+ uASSERT_EQUAL("output.txt"s, r[3]);
+ }
+
+ {
+ const char* argv[] = { "program", "-abcooutput.txt" };
+ int argc = sizeof(argv)/sizeof(*argv);
+
+ arg::Parser<std::string> args(argc, argv);
+
+ std::vector<std::string> r;
+ args.add('a', {}, std::function([&](){ r.push_back("a"); return 0;}), {});
+ args.add('b', {}, std::function([&](){ r.push_back("b"); return 0;}), {});
+ args.add('c', {}, std::function([&](){ r.push_back("c"); return 0;}), {});
+ args.add('o', {},
+ std::function([&](std::string input)
+ {
+ r.push_back(input);
+ return 0;
+ }), {});
+
+
+ auto res = args.parse();
+ uASSERT_EQUAL(0, res);
+ uASSERT_EQUAL(4u, r.size());
+ uASSERT_EQUAL("a"s, r[0]);
+ uASSERT_EQUAL("b"s, r[1]);
+ uASSERT_EQUAL("c"s, r[2]);
+ uASSERT_EQUAL("output.txt"s, r[3]);
+ }
+
+ {
+ // Optional omitted
+ const char* argv[] = { "program", "-c" };
+ int argc = sizeof(argv)/sizeof(*argv);
+
+ arg::Parser<std::optional<std::string>> args(argc, argv);
+
+ std::vector<std::string> r;
+ args.add('c', {},
+ std::function([&](std::optional<std::string> c)
+ {
+ uASSERT(!c.has_value());
+ r.push_back("c");
+ return 0;
+ }), {});
+
+ auto res = args.parse();
+ uASSERT_EQUAL(0, res);
+ uASSERT_EQUAL(1u, r.size());
+ uASSERT_EQUAL("c"s, r[0]);
+ }
+
+ {
+ // Optional provided
+ const char* argv[] = { "program", "-cblue" };
+ int argc = sizeof(argv)/sizeof(*argv);
+
+ arg::Parser<std::optional<std::string>> args(argc, argv);
+
+ std::vector<std::string> r;
+ args.add('c', {},
+ std::function([&](std::optional<std::string> c)
+ {
+ uASSERT(c.has_value());
+ r.push_back(*c);
+ return 0;
+ }), {});
+
+ auto res = args.parse();
+ uASSERT_EQUAL(0, res);
+ uASSERT_EQUAL(1u, r.size());
+ uASSERT_EQUAL("blue"s, r[0]);
+ }
+
+ {
+ // Optional omitted (blue is a new argument)
+ const char* argv[] = { "program", "-c", "blue" };
+ int argc = sizeof(argv)/sizeof(*argv);
+
+ arg::Parser<std::optional<std::string>> args(argc, argv);
+
+ std::vector<std::string> r;
+ args.add('c', {},
+ std::function([&](std::optional<std::string> c)
+ {
+ uASSERT(!c.has_value());
+ r.push_back("c");
+ return 0;
+ }), {});
+ args.set_pos_cb(std::function([&](std::string_view pos)
+ {
+ r.push_back(std::string(pos));
+ return 0;
+ }));
+
+ auto res = args.parse();
+ uASSERT_EQUAL(0, res);
+ uASSERT_EQUAL(2u, r.size());
+ uASSERT_EQUAL("c"s, r[0]);
+ uASSERT_EQUAL("blue"s, r[1]);
+ }
+
+ {
+ // Two seperate flags
+ const char* argv[] = { "program", "-c", "-x" };
+ int argc = sizeof(argv)/sizeof(*argv);
+
+ arg::Parser<std::optional<std::string>> args(argc, argv);
+
+ std::vector<std::string> r;
+ args.add('x', {}, std::function([&](){ r.push_back("x"); return 0;}), {});
+ args.add('c', {},
+ std::function([&](std::optional<std::string> c)
+ {
+ uASSERT(!c.has_value());
+ r.push_back("c");
+ return 0;
+ }), {});
+
+ auto res = args.parse();
+ uASSERT_EQUAL(0, res);
+ uASSERT_EQUAL(2u, r.size());
+ uASSERT_EQUAL("c"s, r[0]);
+ uASSERT_EQUAL("x"s, r[1]);
+ }
+
+ {
+ // -c with argument "-x"
+ const char* argv[] = { "program", "-c-x" };
+ int argc = sizeof(argv)/sizeof(*argv);
+
+ arg::Parser<std::optional<std::string>> args(argc, argv);
+
+ std::vector<std::string> r;
+ args.add('x', {}, std::function([&](){ r.push_back("x"); return 0;}), {});
+ args.add('c', {},
+ std::function([&](std::optional<std::string> c)
+ {
+ uASSERT(c.has_value());
+ r.push_back(*c);
+ return 0;
+ }), {});
+
+ auto res = args.parse();
+ uASSERT_EQUAL(0, res);
+ uASSERT_EQUAL(1u, r.size());
+ uASSERT_EQUAL("-x"s, r[0]);
+ }
+
+ {
+ const char* argv[] = { "program", "-a", "-b", "foo", "bar" };
+ int argc = sizeof(argv)/sizeof(*argv);
+
+ arg::Parser<> args(argc, argv);
+
+ std::vector<std::string> r;
+ args.add('a', {}, std::function([&](){ r.push_back("a"); return 0;}), {});
+ args.add('b', {}, std::function([&](){ r.push_back("b"); return 0;}), {});
+ args.set_pos_cb(std::function([&](std::string_view pos)
+ {
+ r.push_back(std::string(pos));
+ return 0;
+ }));
+
+ auto res = args.parse();
+ uASSERT_EQUAL(0, res);
+ uASSERT_EQUAL(4u, r.size());
+ uASSERT_EQUAL("a"s, r[0]);
+ uASSERT_EQUAL("b"s, r[1]);
+ uASSERT_EQUAL("foo"s, r[2]);
+ uASSERT_EQUAL("bar"s, r[3]);
+ }
+
+ {
+ const char* argv[] = { "program", "-b", "-a", "foo", "bar" };
+ int argc = sizeof(argv)/sizeof(*argv);
+
+ arg::Parser<> args(argc, argv);
+
+ std::vector<std::string> r;
+ args.add('a', {}, std::function([&](){ r.push_back("a"); return 0;}), {});
+ args.add('b', {}, std::function([&](){ r.push_back("b"); return 0;}), {});
+ args.set_pos_cb(std::function([&](std::string_view pos)
+ {
+ r.push_back(std::string(pos));
+ return 0;
+ }));
+
+ auto res = args.parse();
+ uASSERT_EQUAL(0, res);
+ uASSERT_EQUAL(4u, r.size());
+ uASSERT_EQUAL("b"s, r[0]);
+ uASSERT_EQUAL("a"s, r[1]);
+ uASSERT_EQUAL("foo"s, r[2]);
+ uASSERT_EQUAL("bar"s, r[3]);
+ }
+
+ {
+ const char* argv[] = { "program", "-a", "foo", "-b", "bar" };
+ int argc = sizeof(argv)/sizeof(*argv);
+
+ arg::Parser<> args(argc, argv);
+
+ std::vector<std::string> r;
+ args.add('a', {}, std::function([&](){ r.push_back("a"); return 0;}), {});
+ args.add('b', {}, std::function([&](){ r.push_back("b"); return 0;}), {});
+ args.set_pos_cb(std::function([&](std::string_view pos)
+ {
+ r.push_back(std::string(pos));
+ return 0;
+ }));
+
+ auto res = args.parse();
+ uASSERT_EQUAL(0, res);
+ uASSERT_EQUAL(4u, r.size());
+ uASSERT_EQUAL("a"s, r[0]);
+ uASSERT_EQUAL("foo"s, r[1]);
+ uASSERT_EQUAL("b"s, r[2]);
+ uASSERT_EQUAL("bar"s, r[3]);
+ }
+
+ {
+ const char* argv[] = { "program", "foo", "-a", "-b", "bar" };
+ int argc = sizeof(argv)/sizeof(*argv);
+
+ arg::Parser<> args(argc, argv);
+
+ std::vector<std::string> r;
+ args.add('a', {}, std::function([&](){ r.push_back("a"); return 0;}), {});
+ args.add('b', {}, std::function([&](){ r.push_back("b"); return 0;}), {});
+ args.set_pos_cb(std::function([&](std::string_view pos)
+ {
+ r.push_back(std::string(pos));
+ return 0;
+ }));
+
+ auto res = args.parse();
+ uASSERT_EQUAL(0, res);
+ uASSERT_EQUAL(4u, r.size());
+ uASSERT_EQUAL("foo"s, r[0]);
+ uASSERT_EQUAL("a"s, r[1]);
+ uASSERT_EQUAL("b"s, r[2]);
+ uASSERT_EQUAL("bar"s, r[3]);
+ }
+
+ {
+ const char* argv[] = { "program", "foo", "bar", "-a", "-b" };
+ int argc = sizeof(argv)/sizeof(*argv);
+
+ arg::Parser<> args(argc, argv);
+
+ std::vector<std::string> r;
+ args.add('a', {}, std::function([&](){ r.push_back("a"); return 0;}), {});
+ args.add('b', {}, std::function([&](){ r.push_back("b"); return 0;}), {});
+ args.set_pos_cb(std::function([&](std::string_view pos)
+ {
+ r.push_back(std::string(pos));
+ return 0;
+ }));
+
+ auto res = args.parse();
+ uASSERT_EQUAL(0, res);
+ uASSERT_EQUAL(4u, r.size());
+ uASSERT_EQUAL("foo"s, r[0]);
+ uASSERT_EQUAL("bar"s, r[1]);
+ uASSERT_EQUAL("a"s, r[2]);
+ uASSERT_EQUAL("b"s, r[3]);
+ }
+
+ {
+ const char* argv[] = { "program", "-a", "-b", "--", "-x", "foo", "bar" };
+ int argc = sizeof(argv)/sizeof(*argv);
+
+ arg::Parser<> args(argc, argv);
+
+ std::vector<std::string> r;
+ args.add('a', {}, std::function([&](){ r.push_back("a"); return 0;}), {});
+ args.add('b', {}, std::function([&](){ r.push_back("b"); return 0;}), {});
+ args.set_pos_cb(std::function([&](std::string_view pos)
+ {
+ r.push_back(std::string(pos));
+ return 0;
+ }));
+
+ auto res = args.parse();
+ uASSERT_EQUAL(0, res);
+ uASSERT_EQUAL(5u, r.size());
+ uASSERT_EQUAL("a"s, r[0]);
+ uASSERT_EQUAL("b"s, r[1]);
+ uASSERT_EQUAL("-x"s, r[2]);
+ uASSERT_EQUAL("foo"s, r[3]);
+ uASSERT_EQUAL("bar"s, r[4]);
+ }
+
+ //
+ // Long options
+ //
+ {
+ const char* argv[] = { "program", "--sort" };
+ int argc = sizeof(argv)/sizeof(*argv);
+
+ arg::Parser<> args(argc, argv);
+
+ std::vector<std::string> r;
+ args.add({}, "--sort",
+ std::function([&](){ r.push_back("sort"); return 0;}), {});
+
+ auto res = args.parse();
+ uASSERT_EQUAL(0, res);
+ uASSERT_EQUAL(1u, r.size());
+ uASSERT_EQUAL("sort"s, r[0]);
+ }
+
+ {
+ const char* argv[] = { "program", "--no-sort" };
+ int argc = sizeof(argv)/sizeof(*argv);
+
+ arg::Parser<> args(argc, argv);
+
+ std::vector<std::string> r;
+ args.add({}, "--no-sort",
+ std::function([&](){ r.push_back("no-sort"); return 0;}), {});
+
+ auto res = args.parse();
+ uASSERT_EQUAL(0, res);
+ uASSERT_EQUAL(1u, r.size());
+ uASSERT_EQUAL("no-sort"s, r[0]);
+ }
+
+ {
+ const char* argv[] =
+ { "program", "--output", "output.txt", "--block-size", "1024" };
+ int argc = sizeof(argv)/sizeof(*argv);
+
+ arg::Parser<std::string, int> args(argc, argv);
+
+ std::vector<std::string> r;
+ args.add({}, "--output",
+ std::function([&](std::string output)
+ {
+ r.push_back(output);
+ return 0;
+ }), {});
+ args.add({}, "--block-size",
+ std::function([&](int block_size)
+ {
+ r.push_back("["s + std::to_string(block_size) + "]"s);
+ return 0;
+ }), {});
+
+ auto res = args.parse();
+ uASSERT_EQUAL(0, res);
+ uASSERT_EQUAL(2u, r.size());
+ uASSERT_EQUAL("output.txt"s, r[0]);
+ uASSERT_EQUAL("[1024]"s, r[1]);
+ }
+
+ {
+ const char* argv[] =
+ { "program", "--output=output.txt", "--block-size=1024" };
+ int argc = sizeof(argv)/sizeof(*argv);
+
+ arg::Parser<std::string, int> args(argc, argv);
+
+ std::vector<std::string> r;
+ args.add({}, "--output",
+ std::function([&](std::string output)
+ {
+ r.push_back(output);
+ return 0;
+ }), {});
+ args.add({}, "--block-size",
+ std::function([&](int block_size)
+ {
+ r.push_back("["s + std::to_string(block_size) + "]"s);
+ return 0;
+ }), {});
+
+ auto res = args.parse();
+ uASSERT_EQUAL(0, res);
+ uASSERT_EQUAL(2u, r.size());
+ uASSERT_EQUAL("output.txt"s, r[0]);
+ uASSERT_EQUAL("[1024]"s, r[1]);
+ }
+
+ {
+ const char* argv[] =
+ { "program", "--color", "--reverse" };
+ int argc = sizeof(argv)/sizeof(*argv);
+
+ arg::Parser<std::optional<std::string>> args(argc, argv);
+
+ std::vector<std::string> r;
+ args.add({}, "--color",
+ std::function([&](std::optional<std::string> color)
+ {
+ uASSERT(!color.has_value());
+ r.push_back("color");
+ return 0;
+ }), {});
+ args.add({}, "--reverse",
+ std::function([&]()
+ {
+ r.push_back("reverse");
+ return 0;
+ }), {});
+
+ auto res = args.parse();
+ uASSERT_EQUAL(0, res);
+ uASSERT_EQUAL(2u, r.size());
+ uASSERT_EQUAL("color"s, r[0]);
+ uASSERT_EQUAL("reverse"s, r[1]);
+ }
+
+ {
+ const char* argv[] =
+ { "program", "--color=never", "--reverse" };
+ int argc = sizeof(argv)/sizeof(*argv);
+
+ arg::Parser<std::optional<std::string>> args(argc, argv);
+
+ std::vector<std::string> r;
+ args.add({}, "--color",
+ std::function([&](std::optional<std::string> color)
+ {
+ uASSERT(color.has_value());
+ r.push_back(*color);
+ return 0;
+ }), {});
+ args.add({}, "--reverse",
+ std::function([&]()
+ {
+ r.push_back("reverse");
+ return 0;
+ }), {});
+
+ auto res = args.parse();
+ uASSERT_EQUAL(0, res);
+ uASSERT_EQUAL(2u, r.size());
+ uASSERT_EQUAL("never"s, r[0]);
+ uASSERT_EQUAL("reverse"s, r[1]);
+ }
+
+ }
+};
+
+// Registers the fixture into the 'registry'
+static ArgParserTest test;
diff --git a/test/ctor.cc b/test/ctor.cc
index b7bcc6d..d1c0188 100644
--- a/test/ctor.cc
+++ b/test/ctor.cc
@@ -8,198 +8,221 @@ namespace
ctor::build_configurations ctorTestConfigs(const ctor::settings& settings)
{
return
- {
{
- .type = ctor::target_type::unit_test,
- .system = ctor::output_system::build,
- .target = "argsplit_test",
- .sources = {
- "argsplit_test.cc",
- "testmain.cc",
- "../src/util.cc",
+ {
+ ctor::target_type::unit_test,
+ ctor::output_system::build,
+ ctor::target("argparser_test"),
+ ctor::sources{
+ "argparser_test.cc",
+ "testmain.cc",
+ },
+ ctor::cxx_flags{
+ "-std=c++20", "-O3", "-Wall",
+ "-I../src", "-Iuunit",
+ "-DOUTPUT=\"argparser\"",
+ "-fexceptions",
+ },
},
- .flags = {
- .cxxflags = {
- "-std=c++20", "-O3", "-Wall", "-Werror",
+ {
+ ctor::target_type::unit_test,
+ ctor::output_system::build,
+ ctor::target("generated_sources_test"),
+ ctor::sources{
+ "generated_sources_test.cc",
+ "testmain.cc",
+ "../configuration.cc",
+ },
+ ctor::depends({"libctor_nomain.a"}),
+ ctor::cxx_flags{
+ "-std=c++20", "-O3", "-Wall",
"-I../src", "-Iuunit",
- "-DOUTPUT=\"argsplit\"",
+ "-DOUTPUT=\"generated_sources\"",
+ "-fexceptions",
},
},
- },
- {
- .type = ctor::target_type::unit_test,
- .system = ctor::output_system::build,
- .target = "pointerlist_test",
- .sources = {
- "pointerlist_test.cc",
- "testmain.cc",
- "../src/pointerlist.cc",
+ {
+ ctor::target_type::unit_test,
+ ctor::output_system::build,
+ ctor::target("argsplit_test"),
+ ctor::sources{
+ "argsplit_test.cc",
+ "testmain.cc",
+ "../src/util.cc",
+ },
+ ctor::cxx_flags{
+ "-std=c++20", "-O3", "-Wall",
+ "-I../src", "-Iuunit",
+ "-DOUTPUT=\"argsplit\"",
+ "-fexceptions",
+ },
},
- .flags = {
- .cxxflags = {
- "-std=c++20", "-O3", "-Wall", "-Werror",
+ {
+ ctor::target_type::unit_test,
+ ctor::output_system::build,
+ ctor::target("pointerlist_test"),
+ ctor::sources{
+ "pointerlist_test.cc",
+ "testmain.cc",
+ "../src/pointerlist.cc",
+ },
+ ctor::cxx_flags{
+ "-std=c++20", "-O3", "-Wall",
"-I../src", "-Iuunit",
"-DOUTPUT=\"pointerlist\"",
+ "-fexceptions",
},
},
- },
- {
- .type = ctor::target_type::unit_test,
- .system = ctor::output_system::build,
- .target = "deps_test",
- .sources = {
- "deps_test.cc",
- "testmain.cc",
- "../src/deps.cc",
- "../src/util.cc",
- },
- .depends = { "testprog", },
- .flags = {
- .cxxflags = {
- "-std=c++20", "-O3", "-Wall", "-Werror",
+ {
+ ctor::target_type::unit_test,
+ ctor::output_system::build,
+ ctor::target("deps_test"),
+ ctor::sources{
+ "deps_test.cc",
+ "testmain.cc",
+ "../src/deps.cc",
+ "../src/util.cc",
+ },
+ ctor::depends({"testprog"}),
+ ctor::cxx_flags{
+ "-std=c++20", "-O3", "-Wall",
"-I../src", "-Iuunit",
"-DOUTPUT=\"deps\"",
+ "-fexceptions",
},
},
- },
- {
- .type = ctor::target_type::unit_test,
- .system = ctor::output_system::build,
- .target = "testprog",
- .sources = {
- "testprog.cc",
- },
- .flags = {
- .cxxflags = {
- "-std=c++20", "-O3", "-Wall", "-Werror",
+ {
+ ctor::target_type::unit_test,
+ ctor::output_system::build,
+ ctor::target("testprog"),
+ ctor::sources{
+ "testprog.cc",
+ },
+ ctor::cxx_flags{
+ "-std=c++20", "-O3", "-Wall",
+ "-fexceptions",
},
},
- },
- {
- .type = ctor::target_type::unit_test,
- .system = ctor::output_system::build,
- .target = "execute_test",
- .sources = {
- "execute_test.cc",
- "testmain.cc",
- "../src/execute.cc",
- "../src/pointerlist.cc",
- "../src/util.cc",
- },
- .depends = { "testprog", },
- .flags = {
- .cxxflags = {
- "-std=c++20", "-O3", "-Wall", "-Werror",
+ {
+ ctor::target_type::unit_test,
+ ctor::output_system::build,
+ ctor::target("execute_test"),
+ ctor::sources{
+ "execute_test.cc",
+ "testmain.cc",
+ "../src/execute.cc",
+ "../src/pointerlist.cc",
+ "../src/util.cc",
+ },
+ ctor::depends({"testprog"}),
+ ctor::cxx_flags{
+ "-std=c++20", "-O3", "-Wall",
"-I../src", "-Iuunit",
"-DOUTPUT=\"execute\"",
+ "-fexceptions",
},
- .ldflags = { "-pthread" },
- },
- },
- {
- .type = ctor::target_type::unit_test,
- .system = ctor::output_system::build,
- .target = "tasks_test",
- .sources = {
- "tasks_test.cc",
- "testmain.cc",
- },
- .depends = { "libctor_nomain.a" },
- .flags = {
- .cxxflags = {
- "-std=c++20", "-O3", "-Wall", "-Werror",
+ ctor::ld_flags{ "-pthread" },
+ },
+ {
+ ctor::target_type::unit_test,
+ ctor::output_system::build,
+ ctor::target("tasks_test"),
+ ctor::sources{
+ "tasks_test.cc",
+ "testmain.cc",
+ },
+ ctor::depends({"libctor_nomain.a"}),
+ ctor::cxx_flags{
+ "-std=c++20", "-O3", "-Wall",
"-I../src", "-Iuunit",
"-DOUTPUT=\"tasks\"",
+ "-fexceptions",
},
- .ldflags = { "-pthread" },
- },
- },
- {
- .type = ctor::target_type::unit_test,
- .system = ctor::output_system::build,
- .target = "cycle_test",
- .sources = {
- "cycle_test.cc",
- "testmain.cc",
- },
- .depends = { "libctor_nomain.a" },
- .flags = {
- .cxxflags = {
- "-std=c++20", "-O3", "-Wall", "-Werror",
+ ctor::ld_flags{ "-pthread" },
+ },
+ {
+ ctor::target_type::unit_test,
+ ctor::output_system::build,
+ ctor::target("cycle_test"),
+ ctor::sources{
+ "cycle_test.cc",
+ "testmain.cc",
+ "../configuration.cc",
+ },
+ ctor::depends({"libctor_nomain.a"}),
+ ctor::cxx_flags{
+ "-std=c++20", "-O3", "-Wall",
"-I../src", "-Iuunit",
"-DOUTPUT=\"cycle\"",
+ "-fexceptions",
},
- .ldflags = { "-pthread" },
- },
- },
- {
- .type = ctor::target_type::unit_test,
- .system = ctor::output_system::build,
- .target = "source_type_test",
- .sources = {
- "source_type_test.cc",
- "testmain.cc",
- },
- .depends = { "libctor_nomain.a" },
- .flags = {
- .cxxflags = {
- "-std=c++20", "-O3", "-Wall", "-Werror",
+ ctor::ld_flags{ "-pthread" },
+ },
+ {
+ ctor::target_type::unit_test,
+ ctor::output_system::build,
+ ctor::target("source_type_test"),
+ ctor::sources{
+ "source_type_test.cc",
+ "testmain.cc",
+ "../configuration.cc",
+ },
+ ctor::depends({"libctor_nomain.a"}),
+ ctor::cxx_flags{
+ "-std=c++20", "-O3", "-Wall",
"-I../src", "-Iuunit",
"-DOUTPUT=\"source_type\"",
+ "-fexceptions",
},
- .ldflags = { "-pthread" },
- },
- },
- {
- .type = ctor::target_type::unit_test,
- .system = ctor::output_system::build,
- .target = "tools_test",
- .sources = {
- "tools_test.cc",
- "testmain.cc",
- "../src/util.cc",
- "../src/tools.cc",
- },
- //.depends = { "libctor_nomain.a" },
- .flags = {
- .cxxflags = {
- "-std=c++20", "-O3", "-Wall", "-Werror",
+ ctor::ld_flags{ "-pthread" },
+ },
+ {
+ ctor::target_type::unit_test,
+ ctor::output_system::build,
+ ctor::target("tools_test"),
+ ctor::sources{
+ "tools_test.cc",
+ "testmain.cc",
+ "../configuration.cc",
+ },
+ ctor::depends({"libctor_nomain.a"}),
+ ctor::cxx_flags{
+ "-std=c++20", "-O3", "-Wall",
"-I../src", "-Iuunit",
"-DOUTPUT=\"tools\"",
+ "-fexceptions",
},
},
- },
- {
- .type = ctor::target_type::unit_test_library,
- .system = ctor::output_system::build,
- .target = "libctor_nomain.a",
- .sources = {
- "../src/build.cc",
- "../src/configure.cc",
- "../src/deps.cc",
- "../src/execute.cc",
- "../src/pointerlist.cc",
- "../src/rebuild.cc",
- "../src/tasks.cc",
- "../src/task.cc",
- "../src/task_ar.cc",
- "../src/task_cc.cc",
- "../src/task_fn.cc",
- "../src/task_ld.cc",
- "../src/task_so.cc",
- "../src/tools.cc",
- "../src/util.cc",
- "../src/externals_manual.cc",
- },
- .flags = {
- .cxxflags = {
- "-std=c++20", "-O3", "-Wall", "-Werror",
+ {
+ ctor::target_type::unit_test_library,
+ ctor::output_system::build,
+ ctor::target("libctor_nomain.a"),
+ ctor::sources{
+ "../src/build.cc",
+ "../src/configure.cc",
+ "../src/deps.cc",
+ "../src/execute.cc",
+ "../src/pointerlist.cc",
+ "../src/rebuild.cc",
+ "../src/tasks.cc",
+ "../src/task.cc",
+ "../src/task_ar.cc",
+ "../src/task_cc.cc",
+ "../src/task_fn.cc",
+ "../src/task_ld.cc",
+ "../src/task_so.cc",
+ "../src/tools.cc",
+ "../src/util.cc",
+ "../src/externals_manual.cc",
+ },
+ ctor::cxx_flags{
+ "-std=c++20", "-O3", "-Wall",
"-I../src",
+ "-fexceptions",
},
- .ldflags = { "-pthread" },
+ ctor::ld_flags{ "-pthread" },
},
- },
- };
+ };
}
}
diff --git a/test/cycle_test.cc b/test/cycle_test.cc
index 3b45632..8f4c296 100644
--- a/test/cycle_test.cc
+++ b/test/cycle_test.cc
@@ -11,23 +11,23 @@ ctor::build_configurations ctorTestConfigsCyclic(const ctor::settings&)
{
// No dependency
{
- .target = "target0",
+ ctor::target("target0"),
},
// Direct (self-depends)
{
- .target = "target1",
- .depends = { "target1" },
+ ctor::target("target1"),
+ ctor::depends({ "target1" }),
},
// Indirect cyclic depends
{
- .target = "target2",
- .depends = { "target3" },
+ ctor::target("target2"),
+ ctor::depends({ "target3" }),
},
{
- .target = "target3",
- .depends = { "target2" },
+ ctor::target("target3"),
+ ctor::depends({ "target2" }),
},
};
}
diff --git a/test/execute_test.cc b/test/execute_test.cc
index 722b6ea..f8f902f 100644
--- a/test/execute_test.cc
+++ b/test/execute_test.cc
@@ -23,6 +23,8 @@ public:
void return_value()
{
+ constexpr int segfault_return_value = 11;
+ constexpr int exception_return_value = 6;
ctor::settings s;
auto cur_path = std::filesystem::path(paths::argv_0).parent_path();
std::vector<std::string> paths{{cur_path.string()}};
@@ -31,16 +33,21 @@ public:
auto value = execute(s, cmd, {"retval", "0"}, {}, false);
uASSERT_EQUAL(0, value);
+
value = execute(s, cmd, {"retval", "1"}, {}, false);
uASSERT_EQUAL(1, value);
+
value = execute(s, "no-such-binary", {}, {}, false);
uASSERT_EQUAL(1, value);
+
value = execute(s, cmd, {"segfault"}, {}, false);
- uASSERT_EQUAL(11, value);
+ uASSERT_EQUAL(segfault_return_value, value);
+
value = execute(s, cmd, {"throw"}, {}, false);
- uASSERT_EQUAL(6, value);
+ uASSERT_EQUAL(exception_return_value, value);
+
value = execute(s, cmd, {"abort"}, {}, false);
- uASSERT_EQUAL(6, value);
+ uASSERT_EQUAL(exception_return_value, value);
}
void env()
@@ -53,7 +60,7 @@ public:
auto cmd = locate("testprog", paths);
uASSERT(!cmd.empty());
- tmp_file tmp;
+ TmpFile tmp;
std::map<std::string, std::string> env;
@@ -83,11 +90,13 @@ public:
chk = std::find(vars.begin(), vars.end(), "bar=42"s);
uASSERT(chk != vars.end());
- // Check the one that should have overwritten the existing one (probably LANG=en_US.UTF-8 or something)
+ // Check the one that should have overwritten the existing one (probably
+ // LANG=en_US.UTF-8 or something)
chk = std::find(vars.begin(), vars.end(), "LANG=foo"s);
uASSERT(chk != vars.end());
- // Check that other vars are also there (ie. the env wasn't cleared on entry)
+ // Check that other vars are also there (ie. the env wasn't cleared on
+ // entry)
uASSERT(vars.size() > 3);
}
};
diff --git a/test/generated_sources_test.cc b/test/generated_sources_test.cc
new file mode 100644
index 0000000..29a48fc
--- /dev/null
+++ b/test/generated_sources_test.cc
@@ -0,0 +1,135 @@
+// -*- c++ -*-
+// Distributed under the BSD 2-Clause License.
+// See accompanying file LICENSE for details.
+#include <uunit.h>
+
+#include <ctor.h>
+#include <tasks.h>
+#include <rebuild.h>
+
+class GeneratedSourcesTest
+ : public uUnit
+{
+public:
+ GeneratedSourcesTest()
+ {
+ uTEST(GeneratedSourcesTest::test_custom_output);
+ uTEST(GeneratedSourcesTest::test_many_to_one_output);
+ }
+
+ void setup()
+ {
+ // Make sure we start from a clean slate
+ getConfigFileList().clear();
+ }
+
+ void test_custom_output()
+ {
+ using namespace std::string_literals;
+
+ ctor::reg(
+ [](const ctor::settings&)
+ {
+ return ctor::build_configurations{
+ {
+ ctor::target("test1"),
+ ctor::sources{
+ {"foo.cc", ctor::source_type::generated}
+ },
+ },
+ {
+ ctor::target("this_is_unused"),
+ ctor::sources{
+ {"bar.x", ctor::output_file{"foo.cc"}},
+ {"bar.y", ctor::output_file{"bar.cc"}},
+ },
+ []([[maybe_unused]]const std::string& input,
+ [[maybe_unused]]const std::string& output,
+ [[maybe_unused]]const ctor::build_configuration& config,
+ [[maybe_unused]]const ctor::settings& settings)
+ {
+ return 0;
+ },
+ },
+ };
+ });
+ ctor::settings settings{};
+ auto tasks = getTasks(settings);
+ for(auto task : tasks)
+ {
+ uASSERT(task->registerDepTasks(tasks) == 0);
+ }
+ uASSERT_EQUAL(4u, tasks.size());
+ bool found{false};
+ for(const auto& task : tasks)
+ {
+ if(task->target() == "test1")
+ {
+ auto deps_test1 = task->getDependsTasks();
+ uASSERT_EQUAL(1u, deps_test1.size());
+ auto deps_foo_o = deps_test1[0]->getDependsTasks();
+ uASSERT_EQUAL(1u, deps_foo_o.size());
+ uASSERT_EQUAL("test/bar.x"s, deps_foo_o[0]->source());
+ found = true;
+ }
+ }
+ uASSERT(found);
+ }
+
+ void test_many_to_one_output()
+ {
+ using namespace std::string_literals;
+
+ ctor::reg(
+ [](const ctor::settings&)
+ {
+ return ctor::build_configurations{
+ {
+ ctor::target("test1"),
+ ctor::sources{
+ {"foo.cc", ctor::source_type::generated}
+ },
+ },
+ {
+ ctor::target("foo.cc"),
+ ctor::sources{
+ "bar.x",
+ "bar.y",
+ },
+ []([[maybe_unused]]const std::vector<std::string>& input,
+ [[maybe_unused]]const std::string& output,
+ [[maybe_unused]]const ctor::build_configuration& config,
+ [[maybe_unused]]const ctor::settings& settings)
+ {
+ return 0;
+ },
+ },
+ };
+ });
+ ctor::settings settings{};
+ auto tasks = getTasks(settings);
+ for(auto task : tasks)
+ {
+ uASSERT(task->registerDepTasks(tasks) == 0);
+ }
+ uASSERT_EQUAL(4u, tasks.size());
+ bool found{false};
+ for(const auto& task : tasks)
+ {
+ if(task->target() == "test1")
+ {
+ auto deps_test1 = task->getDependsTasks();
+ uASSERT_EQUAL(1u, deps_test1.size());
+ auto deps_foo_o = deps_test1[0]->getDependsTasks();
+ uASSERT_EQUAL(1u, deps_foo_o.size());
+ uASSERT_EQUAL("test/bar.x"s, deps_foo_o[0]->source());
+ found = true;
+ }
+ }
+ uASSERT(found);
+ }
+
+};
+
+// Registers the fixture into the 'registry'
+static GeneratedSourcesTest test;
diff --git a/test/source_type_test.cc b/test/source_type_test.cc
index 288f1e5..345c591 100644
--- a/test/source_type_test.cc
+++ b/test/source_type_test.cc
@@ -26,12 +26,21 @@ std::ostream& operator<<(std::ostream& stream, const ctor::language& lang)
return stream;
}
+const ctor::configuration& test_configuration()
+{
+ static ctor::configuration cfg{};
+ cfg.build_toolchain = ctor::toolchain::gcc;
+ cfg.build_arch = ctor::arch::unix;
+ return cfg;
+}
+REG(test_configuration);
+
class TestableTaskCC
: public TaskCC
{
public:
TestableTaskCC(const ctor::source& source)
- : TaskCC({}, {}, "build", source)
+ : TaskCC(ctor::target_type::object, {}, {}, "build", source)
{}
ctor::language language() const
diff --git a/test/suite/ctor_files/ctor.cc.bar b/test/suite/ctor_files/ctor.cc.bar
index 218f9cc..d77eb70 100644
--- a/test/suite/ctor_files/ctor.cc.bar
+++ b/test/suite/ctor_files/ctor.cc.bar
@@ -11,21 +11,20 @@ ctor::build_configurations ctorConfigs(const ctor::settings& settings)
return
{
{
- .name = "hello",
- .target = "hello",
- .sources = {
+ ctor::name("hello"),
+ ctor::target("hello"),
+ ctor::sources{
"hello.cc",
},
- .flags = {
- .cxxflags = {
- "-std=c++20",
- "-O3",
- "-g",
- "-Wall",
- "-Werror",
- },
+ ctor::cxx_flags{
+ "-std=c++20",
+ "-O3",
+ "-g",
+ "-Wall",
+ "-Werror",
+ "-fexceptions",
},
- .externals = {"bar"},
+ ctor::externals({"bar"}),
}
};
}
@@ -35,14 +34,12 @@ ctor::external_configurations ctorExtConfigs(const ctor::settings& settings)
return
{
{
- .name = "bar",
- .external = ctor::external_manual{
- .flags = {
- .cflags = { "-D_B_" },
- .cxxflags = { "-D_A_", "-DBAR"},
- .ldflags = { "-D_C_" },
- .asmflags = { "-D_D_" },
- },
+ ctor::name("bar"),
+ ctor::external_manual{
+ ctor::c_flags{ "-D_B_" },
+ ctor::cxx_flags{ "-D_A_", "-DBAR"},
+ ctor::ld_flags{ "-D_C_" },
+ ctor::asm_flags{ "-D_D_" },
},
// Creates --with-foo-prefix arg to configure which will be used for
// -L and -I flags.
diff --git a/test/suite/ctor_files/ctor.cc.base b/test/suite/ctor_files/ctor.cc.base
index eab39c4..73b5cdb 100644
--- a/test/suite/ctor_files/ctor.cc.base
+++ b/test/suite/ctor_files/ctor.cc.base
@@ -11,21 +11,20 @@ ctor::build_configurations ctorConfigs(const ctor::settings& settings)
return
{
{
- .name = "hello",
- .target = "hello",
- .sources = {
+ ctor::name("hello"),
+ ctor::target("hello"),
+ ctor::sources{
"hello.cc",
},
- .flags = {
- .cxxflags = {
- "-std=c++20",
- "-O3",
- "-g",
- "-Wall",
- "-Werror",
- },
+ ctor::cxx_flags{
+ "-std=c++20",
+ "-O3",
+ "-g",
+ "-Wall",
+ "-Werror",
+ "-fexceptions",
},
- .externals = {"bar"},
+ ctor::externals({"bar"}),
}
};
}
@@ -35,15 +34,13 @@ ctor::external_configurations ctorExtConfigs(const ctor::settings& settings)
return
{
{
- .name = "bar",
- .external = ctor::external_manual
+ ctor::name("bar"),
+ ctor::external_manual
{
- .flags = {
- .cflags = { "-D_B_" },
- .cxxflags = { "-D_A_", "-DFOO"},
- .ldflags = { "-D_C_" },
- .asmflags = { "-D_D_" },
- },
+ ctor::c_flags{ "-D_B_" },
+ ctor::cxx_flags{ "-D_A_", "-DFOO"},
+ ctor::ld_flags{ "-D_C_" },
+ ctor::asm_flags{ "-D_D_" },
},
// Creates --with-foo-prefix arg to configure which will be used for
// -L and -I flags.
diff --git a/test/suite/ctor_files/ctor.cc.generated b/test/suite/ctor_files/ctor.cc.generated
new file mode 100644
index 0000000..96b6fb5
--- /dev/null
+++ b/test/suite/ctor_files/ctor.cc.generated
@@ -0,0 +1,104 @@
+// -*- c++ -*-
+// Distributed under the BSD 2-Clause License.
+// See accompanying file LICENSE for details.
+#include <ctor.h>
+#include <filesystem>
+#include <iostream>
+#include <fstream>
+
+namespace
+{
+ctor::build_configurations ctorConfigs(const ctor::settings& settings)
+{
+ return
+ {
+ {
+ ctor::target("world"),
+ ctor::sources{
+ { "world.cc", ctor::source_type::generated },
+ },
+ ctor::cxx_flags{
+ "-std=c++20",
+ "-O3",
+ "-g",
+ "-Wall",
+ "-Werror",
+ "-fexceptions",
+ },
+ },
+ {
+ ctor::target("foo"),
+ ctor::sources{
+ { "foo.cc", ctor::source_type::generated },
+ },
+ ctor::cxx_flags{
+ "-std=c++20",
+ "-O3",
+ "-g",
+ "-Wall",
+ "-Werror",
+ "-fexceptions",
+ },
+ },
+ {
+ ctor::target("this_is_unused"),
+ ctor::sources{
+ {"hello.cc", ctor::output_file{"world.cc"}},
+ {"hello.cc", ctor::output_file{"foo.cc"}},
+ },
+ [](const std::string& input,
+ const std::string& output,
+ const ctor::build_configuration& config,
+ const ctor::settings& settings)
+ {
+ namespace fs = std::filesystem;
+ std::cout << "Input: " << input << '\n';
+ std::cout << "Output: " << output << '\n';
+ fs::copy_file(input, output, fs::copy_options::overwrite_existing);
+ return 0;
+ },
+ },
+
+ {
+ ctor::target("many_to_one"),
+ ctor::sources{
+ {"many_to_one.cc", ctor::source_type::generated}
+ },
+ ctor::cxx_flags{"-fexceptions"},
+ },
+ {
+ ctor::target("many_to_one.cc"),
+ ctor::sources{
+ {"foo.cc", ctor::source_type::generated},
+ {"hello.cc"},
+ },
+ [](const std::vector<std::string>& input,
+ const std::string& output,
+ const ctor::build_configuration& config,
+ const ctor::settings& settings)
+ {
+ std::cout << "Output: " << output << '\n';
+ std::ofstream ofs(output);
+ bool comment{true};
+ for(const auto& input_file : input)
+ {
+ std::cout << "Input: " << input_file << '\n';
+ std::ifstream ifs(input_file);
+ std::string line;
+ while(std::getline(ifs, line))
+ {
+ ofs << line << '\n';
+ }
+ if(comment) ofs << "/*\n";
+ comment = false;
+ }
+ ofs << "*/\n";
+ return 0;
+ }
+ },
+
+ };
+}
+}
+
+REG(ctorConfigs);
diff --git a/test/suite/ctor_files/ctor.cc.generated2 b/test/suite/ctor_files/ctor.cc.generated2
new file mode 100644
index 0000000..2e36c0e
--- /dev/null
+++ b/test/suite/ctor_files/ctor.cc.generated2
@@ -0,0 +1,87 @@
+// -*- c++ -*-
+// Distributed under the BSD 2-Clause License.
+// See accompanying file LICENSE for details.
+#include <ctor.h>
+#include <filesystem>
+#include <iostream>
+#include <fstream>
+
+namespace
+{
+ctor::build_configurations ctorConfigs(const ctor::settings& settings)
+{
+ return
+ {
+ {
+ ctor::target("world"),
+ ctor::sources{
+ { "world.cc", ctor::source_type::generated },
+ },
+ },
+ {
+ ctor::target("foo"),
+ ctor::sources{
+ { "foo.cc", ctor::source_type::generated },
+ },
+ },
+ {
+ ctor::target("this_is_unused"),
+ ctor::sources{
+ {"hello.cc", ctor::output_file{"world.cc"}},
+ {"hello.cc", ctor::output_file{"foo.cc"}},
+ },
+ [](const std::string& input,
+ const std::string& output,
+ const ctor::build_configuration& config,
+ const ctor::settings& settings)
+ {
+ namespace fs = std::filesystem;
+ std::cout << "Input: " << input << '\n';
+ std::cout << "Output: " << output << '\n';
+ fs::copy_file(input, output, fs::copy_options::overwrite_existing);
+ return 0;
+ }
+ },
+
+ {
+ ctor::target("many_to_one"),
+ ctor::sources{
+ {"many_to_one.cc", ctor::source_type::generated}
+ },
+ ctor::cxx_flags{ "-fexceptions", },
+ },
+ {
+ ctor::target("many_to_one.cc"),
+ ctor::sources{
+ {"hello.cc"},
+ },
+ [](const std::vector<std::string>& input,
+ const std::string& output,
+ const ctor::build_configuration& config,
+ const ctor::settings& settings)
+ {
+ std::cout << "Output: " << output << '\n';
+ std::ofstream ofs(output);
+ bool comment{true};
+ for(const auto& input_file : input)
+ {
+ std::cout << "Input: " << input_file << '\n';
+ std::ifstream ifs(input_file);
+ std::string line;
+ while(std::getline(ifs, line))
+ {
+ ofs << line << '\n';
+ }
+ if(comment) ofs << "/*\n";
+ comment = false;
+ }
+ ofs << "*/\n";
+ return 0;
+ }
+ },
+
+ };
+}
+}
+
+REG(ctorConfigs);
diff --git a/test/suite/ctor_files/ctor.cc.multi b/test/suite/ctor_files/ctor.cc.multi
index 2b88afe..fc4c7a4 100644
--- a/test/suite/ctor_files/ctor.cc.multi
+++ b/test/suite/ctor_files/ctor.cc.multi
@@ -13,21 +13,20 @@ ctor::build_configurations ctorConfigs(const ctor::settings& settings)
return
{
{
- .name = "hello",
- .target = "hello",
- .sources = {
+ ctor::name("hello"),
+ ctor::target("hello"),
+ ctor::sources{
"hello.cc",
},
- .flags = {
- .cxxflags = {
- "-std=c++20",
- "-O3",
- "-g",
- "-Wall",
- "-Werror",
- },
+ ctor::cxx_flags{
+ "-std=c++20",
+ "-O3",
+ "-g",
+ "-Wall",
+ "-Werror",
+ "-fexceptions",
},
- .externals = {"bar"},
+ ctor::externals({"bar"}),
}
};
}
@@ -37,14 +36,12 @@ ctor::external_configurations ctorExtConfigs(const ctor::settings& settings)
return
{
{
- .name = "bar",
- .external = ctor::external_manual{
- .flags = {
- .cflags = { "-D_B_" },
- .cxxflags = { "-D_A_", "-DFOO"},
- .ldflags = { "-D_C_" },
- .asmflags = { "-D_D_" },
- },
+ ctor::name("bar"),
+ ctor::external_manual{
+ ctor::c_flags{ "-D_B_" },
+ ctor::cxx_flags{ "-D_A_", "-DFOO"},
+ ctor::ld_flags{ "-D_C_" },
+ ctor::asm_flags{ "-D_D_" },
},
// Creates --with-foo-prefix arg to configure which will be used for
// -L and -I flags.
diff --git a/test/suite/test.cc b/test/suite/test.cc
new file mode 100644
index 0000000..b096a8b
--- /dev/null
+++ b/test/suite/test.cc
@@ -0,0 +1,370 @@
+// -*- c++ -*-
+// Distributed under the BSD 2-Clause License.
+// See accompanying file LICENSE for details.
+#include "../../src/util.cc"
+#include "../../src/pointerlist.cc"
+#include "../../src/execute.cc"
+
+#include <iostream>
+#include <string>
+#include <filesystem>
+#include <thread>
+#include <chrono>
+#include <sstream>
+
+using namespace std::chrono_literals;
+
+int fail(int value = 1,
+ const std::source_location location = std::source_location::current())
+{
+ std::cout << "*** Failure at line " << location.line() << '\n';
+ exit(value);
+}
+
+const std::string ctor_exe{"./ctor"};
+const std::string obj_ext{".o"};
+
+void run_ctor(const std::vector<std::string>& args,
+ const std::source_location location = std::source_location::current())
+{
+ ctor::settings settings{.verbose = 2};
+ auto ret = execute(settings, ctor_exe, args);
+ if(ret != 0)
+ {
+ fail(ret, location);
+ }
+}
+
+void assert_not_exists(const std::string& path,
+ const std::source_location location = std::source_location::current())
+{
+ if(std::filesystem::exists(path))
+ {
+ fail(1, location);
+ }
+}
+
+void assert_exists(const std::string& path,
+ const std::source_location location = std::source_location::current())
+{
+ if(!std::filesystem::exists(path))
+ {
+ fail(1, location);
+ }
+}
+
+void copy_config(std::string cfg)
+{
+ std::cout << "** ctor_files/ctor.cc." + cfg + "\n";
+ std::filesystem::copy("ctor_files/ctor.cc." + cfg, "ctor.cc",
+ std::filesystem::copy_options::overwrite_existing);
+ if(std::filesystem::exists(ctor_exe))
+ {
+ auto ctor_exe_time = std::filesystem::last_write_time(ctor_exe);
+ std::filesystem::last_write_time("ctor.cc", ctor_exe_time + 1s);
+ }
+}
+
+class Tracker
+{
+public:
+ Tracker(const std::string& file_) : file(file_)
+ {
+ capture(); // capture initial value
+ }
+
+ void capture()
+ {
+ changed();
+ }
+
+ bool changed()
+ {
+ auto tmp = readFile(file);
+ auto content_changed = tmp != content;
+ content = tmp;
+ return content_changed;
+ }
+
+private:
+ std::string file;
+ std::string content;
+};
+
+void assert_not_changed(Tracker& tracker,
+ const std::source_location location = std::source_location::current())
+{
+ if(tracker.changed())
+ {
+ fail(1, location);
+ }
+}
+
+void assert_changed(Tracker& tracker,
+ const std::source_location location = std::source_location::current())
+{
+ if(!tracker.changed())
+ {
+ fail(1, location);
+ }
+}
+
+int main()
+{
+ ctor::settings settings{};
+ settings.verbose = 2;
+ auto paths = get_paths();
+
+ std::string CXX = "g++";
+ get_env("CXX", CXX);
+ std::string CTORDIR = "../../build";
+ get_env("CTORDIR", CTORDIR);
+ std::string BUILDDIR = "build";
+ get_env("BUILDDIR", BUILDDIR);
+ std::string CXXFLAGS;
+ get_env("CXXFLAGS", CXXFLAGS);
+ std::string LDFLAGS;
+ get_env("LDFLAGS", LDFLAGS);
+
+ // Wipe the board
+ std::filesystem::remove_all(BUILDDIR);
+ std::filesystem::remove("configuration.cc");
+ std::filesystem::remove("config.h");
+ std::filesystem::remove(ctor_exe);
+
+ //////////////////////////////////////////////////////////////////////////////
+ // bootstrap
+ {
+ auto cxx_prog = locate(CXX, paths);
+
+ std::vector<std::string> args =
+ {"-pthread", "-std=c++20", "-L", CTORDIR, "-lctor", "-I", "../../src",
+ "ctor.cc", "-o", ctor_exe};
+ if(!CXXFLAGS.empty())
+ {
+ auto tokens = argsplit(CXXFLAGS);
+ for(const auto& token : tokens)
+ {
+ args.push_back(token);
+ }
+ }
+ if(!LDFLAGS.empty())
+ {
+ auto tokens = argsplit(LDFLAGS);
+ for(const auto& token : tokens)
+ {
+ args.push_back(token);
+ }
+ }
+
+ // Compile bootstrap binary
+ copy_config("base");
+ auto ret = execute(settings, cxx_prog, args);
+ if(ret != 0)
+ {
+ fail(ret);
+ }
+ }
+ //////////////////////////////////////////////////////////////////////////////
+ // check if source file changes are tracked
+ {
+ // No build files should have been created yet
+ assert_not_exists(BUILDDIR);
+
+ // capture ctor binary before configure is called
+ Tracker ctor_bin(ctor_exe);
+ run_ctor(
+ {"configure", "--ctor-includedir", "../../src",
+ "--ctor-libdir=" + CTORDIR, "--build-dir=" + BUILDDIR});
+
+ // ctor should be rebuilt at this point, so binary should have changed
+ assert_changed(ctor_bin);
+
+ // configuration.cc should have been generated now
+ assert_exists("configuration.cc");
+ assert_exists("config.h");
+
+ // Shouldn't compile anything yet - only configure
+ assert_not_exists(BUILDDIR + "/hello-hello_cc" + obj_ext);
+
+ ctor_bin.capture();
+
+ // Run normally to build project
+ run_ctor({"-v"});
+
+ // Compiled object should now exist
+ assert_exists(BUILDDIR + "/hello-hello_cc" + obj_ext);
+
+ // ctor should not have been rebuilt, so binary should be the same
+ assert_not_changed(ctor_bin);
+
+ std::this_thread::sleep_for(1100ms);
+
+ auto time =
+ std::filesystem::last_write_time(BUILDDIR + "/hello-hello_cc" + obj_ext);
+ std::filesystem::last_write_time("hello.cc", time + 1s);
+ std::this_thread::sleep_for(1100ms);
+
+ // Run normally to rebuild hello.cc
+ run_ctor({"-v"});
+
+ // Object file should have been recompiled
+ auto time2 =
+ std::filesystem::last_write_time(BUILDDIR + "/hello-hello_cc" + obj_ext);
+ if(time == time2)
+ {
+ fail();
+ }
+ }
+
+ //////////////////////////////////////////////////////////////////////////////
+ // check if flags change trigger rebuild
+ {
+ // Replace -DFOO with -DBAR in foo external.cxxflags
+ copy_config("bar");
+ Tracker configuration_cc_bin("configuration.cc");
+ Tracker ctor_bin(ctor_exe);
+ auto time =
+ std::filesystem::last_write_time(BUILDDIR + "/hello-hello_cc" + obj_ext);
+ std::this_thread::sleep_for(1100ms);
+
+ // Run normally to reconfigure, rebuild ctor and rebuild hello.cc
+ run_ctor({"-v"});
+
+ auto time2 =
+ std::filesystem::last_write_time(BUILDDIR + "/hello-hello_cc" + obj_ext);
+ if(time == time2)
+ {
+ fail();
+ }
+
+ assert_changed(configuration_cc_bin);
+ assert_changed(ctor_bin);
+ }
+
+ //////////////////////////////////////////////////////////////////////////////
+ // check if included files in ctor.cc is tracked for changes
+ {
+ copy_config("multi");
+ Tracker configuration_cc_bin("configuration.cc");
+ Tracker ctor_bin(ctor_exe);
+ auto time =
+ std::filesystem::last_write_time(BUILDDIR + "/hello-hello_cc" + obj_ext);
+ std::this_thread::sleep_for(1100ms);
+
+ // Run normally to reconfigure, rebuild ctor and rebuild hello.cc
+ run_ctor({"-v"});
+
+ auto time2 =
+ std::filesystem::last_write_time(BUILDDIR + "/hello-hello_cc" + obj_ext);
+ if(time == time2)
+ {
+ fail();
+ }
+
+ assert_changed(configuration_cc_bin);
+ assert_changed(ctor_bin);
+
+ // now touching foobar.h, should retrigger re-configuration
+ time = std::filesystem::last_write_time(ctor_exe);
+ std::filesystem::last_write_time("foobar.h", time + 1s);
+ std::this_thread::sleep_for(1100ms);
+
+ // Run normally to reconfigure, rebuild ctor and rebuild hello.cc
+ run_ctor({"-v"});
+
+ time2 = std::filesystem::last_write_time(ctor_exe);
+ if(time == time2)
+ {
+ fail();
+ }
+ }
+
+ //////////////////////////////////////////////////////////////////////////////
+ // generated part1: one-to-one generation
+ {
+ copy_config("generated");
+ std::filesystem::remove(BUILDDIR + "/world.cc");
+ std::filesystem::remove(BUILDDIR + "/foo.cc");
+
+ Tracker configuration_cc_bin("configuration.cc");
+ Tracker ctor_bin(ctor_exe);
+ std::this_thread::sleep_for(1100ms);
+
+ // Run normally to reconfigure, rebuild ctor and build world.cc
+ run_ctor({"-v", "world"});
+
+ assert_changed(configuration_cc_bin);
+ assert_changed(ctor_bin);
+
+ // foo.cc should not be generated at this point
+ assert_not_exists(BUILDDIR+"/foo.cc");
+
+ auto time_w = std::filesystem::last_write_time(BUILDDIR + "/world.cc");
+ auto time_wo =
+ std::filesystem::last_write_time(BUILDDIR + "/" + BUILDDIR+
+ "/world-world_cc" + obj_ext);
+ std::this_thread::sleep_for(1100ms);
+
+ // now touching hello.cc, should trigger regeneration of world.cc and
+ // rebuild
+ std::filesystem::last_write_time("hello.cc", time_w + 1s);
+ run_ctor({"-v", "world"});
+
+ auto time_w2 = std::filesystem::last_write_time(BUILDDIR + "/world.cc");
+ auto time_wo2 =
+ std::filesystem::last_write_time(BUILDDIR + "/" + BUILDDIR +
+ "/world-world_cc" + obj_ext);
+ if(time_w == time_w2)
+ {
+ fail();
+ }
+ if(time_wo == time_wo2)
+ {
+ fail();
+ }
+ }
+
+ //////////////////////////////////////////////////////////////////////////////
+ // generated part2: many-to-one generation
+ {
+ std::filesystem::remove(BUILDDIR + "/world.cc");
+ std::filesystem::remove(BUILDDIR + "/foo.cc");
+ std::filesystem::remove(BUILDDIR + "/many_to_one.cc");
+
+ run_ctor({"-v", "many_to_one"});
+
+ // world.cc should not be generated at this point
+ assert_not_exists(BUILDDIR+"/world.cc");
+
+ // foo.cc should have been generated at this point
+ assert_exists(BUILDDIR+"/foo.cc");
+
+ // many_to_one.cc should have been generated
+ assert_exists(BUILDDIR+"/many_to_one.cc");
+
+ auto time = std::filesystem::last_write_time(BUILDDIR + "/many_to_one.cc");
+ std::this_thread::sleep_for(1100ms);
+
+ Tracker ctor_bin(ctor_exe);
+
+ // remove "foo.cc" from sources list, so it only contains hello.cc
+ copy_config("generated2");
+
+ // rebuild
+ run_ctor({"-v", "many_to_one"});
+
+ // Verify that the change resulted in a ctor rebuild
+ assert_changed(ctor_bin);
+
+ // Verify that source-list change triggered a new build of many_to_one.cc
+ auto time2 =
+ std::filesystem::last_write_time(BUILDDIR + "/many_to_one.cc");
+ if(time == time2)
+ {
+ fail();
+ }
+ }
+
+ return 0;
+}
diff --git a/test/suite/test.sh b/test/suite/test.sh
index 97d2551..4638c0d 100755
--- a/test/suite/test.sh
+++ b/test/suite/test.sh
@@ -1,122 +1,4 @@
#!/bin/bash
: ${CXX:=g++}
-: ${CTORDIR:=../../build}
-: ${BUILDDIR:=build}
-CXX=$(which $CXX)
-
-function fail
-{
- echo "*** Failure at line $1"
- exit 1
-}
-
-function ctor
-{
- echo "*** Running: ./ctor $*"
- ./ctor $*
-}
-
-STAT_FORMAT="-c %Y"
-if [[ "$OSTYPE" == "darwin"* ]]; then
- # Mac OSX
- STAT_FORMAT="-f %B"
-fi
-
-# Wipe the board
-rm -Rf ${BUILDDIR}
-rm -f configuration.cc
-rm -f ctor
-
-echo "** ctor_files/ctor.cc.base"
-cp ctor_files/ctor.cc.base ctor.cc
-
-# Compile bootstrap binary
-$CXX -pthread $LDFLAGS $CXXFLAGS -std=c++20 -L${CTORDIR} -lctor -I../../src ctor.cc -o ctor || fail ${LINENO}
-
-# No build files should have been created yet
-[ -d ${BUILDDIR} ] && fail ${LINENO}
-
-# capture md5 sum of ctor binary before configure is called
-MD5=`md5sum ctor`
-ctor configure --ctor-includedir ../../src --ctor-libdir=${CTORDIR} --build-dir=${BUILDDIR}
-
-# ctor should be rebuilt at this point, so md5 sum should have changed
-(echo $MD5 | md5sum --status -c) && fail ${LINENO}
-
-# configuration.cc should have been generated now
-[ ! -f configuration.cc ] && fail ${LINENO}
-
-# Shouldn't compile anything yet - only configure
-[ -f ${BUILDDIR}/hello-hello_cc.o ] && fail ${LINENO}
-
-MD5=`md5sum ctor`
-
-# Run normally to build project
-ctor -v
-
-# Compiled object should now exist
-[ ! -f ${BUILDDIR}/hello-hello_cc.o ] && fail ${LINENO}
-
-# ctor should not have been rebuilt, so md5 sum should be the same
-(echo $MD5 | md5sum --status -c) || fail ${LINENO}
-
-MOD1=`stat $STAT_FORMAT ${BUILDDIR}/hello-hello_cc.o`
-touch hello.cc
-sleep 1.1
-
-# Run normally to rebuild hello.cc
-ctor -v
-
-# Object file should have been recompiled
-MOD2=`stat $STAT_FORMAT ${BUILDDIR}/hello-hello_cc.o`
-echo $MOD1
-echo $MOD2
-[[ $MOD1 == $MOD2 ]] && fail ${LINENO}
-
-# Replacve -DFOO with -DBAR in foo external.cxxflags
-echo "** ctor_files/ctor.cc.bar"
-cp ctor_files/ctor.cc.bar ctor.cc
-
-MD5C=`md5sum configuration.cc`
-MD5=`md5sum ctor`
-MOD1=`stat $STAT_FORMAT build/hello-hello_cc.o`
-sleep 1.1
-
-# Run normally to reconfigure, rebuild ctor and rebuild hello.cc
-ctor -v
-
-MOD2=`stat $STAT_FORMAT ${BUILDDIR}/hello-hello_cc.o`
-[[ $MOD1 == $MOD2 ]] && fail ${LINENO}
-(echo $MD5C | md5sum --status -c) && fail ${LINENO}
-(echo $MD5 | md5sum --status -c) && fail ${LINENO}
-
-echo "** ctor_files/ctor.cc.multi"
-cp ctor_files/ctor.cc.multi ctor.cc
-
-MD5C=`md5sum configuration.cc`
-MD5=`md5sum ctor`
-MOD1=`stat $STAT_FORMAT ${BUILDDIR}/hello-hello_cc.o`
-sleep 1.1
-
-# Run normally to reconfigure, rebuild ctor and rebuild hello.cc
-ctor -v
-
-MOD2=`stat $STAT_FORMAT ${BUILDDIR}/hello-hello_cc.o`
-[[ $MOD1 == $MOD2 ]] && fail ${LINENO}
-(echo $MD5C | md5sum --status -c) && fail ${LINENO}
-(echo $MD5 | md5sum --status -c) && fail ${LINENO}
-
-# now touching foobar.h, should retrigger re-configuration
-touch foobar.h
-
-MOD1=`stat $STAT_FORMAT ctor`
-sleep 1.1
-
-# Run normally to reconfigure, rebuild ctor and rebuild hello.cc
-ctor -v
-
-MOD2=`stat $STAT_FORMAT ctor`
-[[ $MOD1 == $MOD2 ]] && fail ${LINENO}
-
-exit 0
+$CXX $LDFLAGS $CXXFLAGS -std=c++20 -Wall test.cc -o test && ./test
diff --git a/test/tasks_test.cc b/test/tasks_test.cc
index b444bd5..c8e4cf8 100644
--- a/test/tasks_test.cc
+++ b/test/tasks_test.cc
@@ -10,12 +10,12 @@ ctor::build_configurations ctorTestConfigs1(const ctor::settings&)
return
{
{
- .name = "Target1",
- .target = "target1",
- .sources = {"foo.cc", "bar.c"},
+ ctor::name("Target1"),
+ ctor::target("target1"),
+ ctor::sources({"foo.cc", "bar.c"}),
},
{
- .target = "target2",
+ ctor::target("target2"),
},
};
}
@@ -25,10 +25,10 @@ ctor::build_configurations ctorTestConfigs2(const ctor::settings&)
return
{
{
- .target = "target3",
+ ctor::target("target3"),
},
{
- .target = "target4",
+ ctor::target("target4"),
},
};
}
@@ -38,14 +38,14 @@ namespace test_global {
ctor::toolchain toolchain{};
ctor::arch arch{};
}
-const ctor::configuration& ctor::get_configuration()
+const ctor::configuration& test_configuration()
{
static ctor::configuration cfg{};
cfg.build_toolchain = test_global::toolchain;
cfg.build_arch = test_global::arch;
return cfg;
}
-
+REG(test_configuration);
REG(ctorTestConfigs1);
REG(ctorTestConfigs2);
@@ -72,7 +72,7 @@ public:
const ctor::settings& settings,
const std::string& name, bool dirty,
const std::vector<std::string>& deps = {})
- : Task(config, settings, {})
+ : Task(ctor::target_type::executable, config, settings, {})
, task_name(name)
, task_dirty(dirty)
, task_deps(deps)
@@ -170,7 +170,7 @@ public:
uASSERT_EQUAL(0, task->registerDepTasks(allTasks));
}
- uASSERT_EQUAL(nullptr, getNextTask(allTasks, dirtyTasks));
+ uASSERT_EQUAL(nullptr, getNextTask({}, allTasks, dirtyTasks));
}
{ // Zero (One task, no dirty)
@@ -187,7 +187,7 @@ public:
uASSERT_EQUAL(0, task->registerDepTasks(allTasks));
}
- uASSERT_EQUAL(nullptr, getNextTask(allTasks, dirtyTasks));
+ uASSERT_EQUAL(nullptr, getNextTask({}, allTasks, dirtyTasks));
}
{ // One (One task, one dirty)
@@ -205,7 +205,7 @@ public:
uASSERT_EQUAL(0, task->registerDepTasks(allTasks));
}
- uASSERT_EQUAL(task1, getNextTask(allTasks, dirtyTasks));
+ uASSERT_EQUAL(task1, getNextTask({}, allTasks, dirtyTasks));
uASSERT_EQUAL(0u, dirtyTasks.size());
}
@@ -226,7 +226,7 @@ public:
uASSERT_EQUAL(0, task->registerDepTasks(allTasks));
}
- uASSERT_EQUAL(task2, getNextTask(allTasks, dirtyTasks));
+ uASSERT_EQUAL(task2, getNextTask({}, allTasks, dirtyTasks));
uASSERT_EQUAL(0u, dirtyTasks.size());
}
@@ -250,7 +250,7 @@ public:
uASSERT_EQUAL(0, task->registerDepTasks(allTasks));
}
- uASSERT_EQUAL(task2, getNextTask(allTasks, dirtyTasks));
+ uASSERT_EQUAL(task2, getNextTask({}, allTasks, dirtyTasks));
uASSERT_EQUAL(0u, dirtyTasks.size());
}
@@ -275,7 +275,7 @@ public:
uASSERT_EQUAL(0, task->registerDepTasks(allTasks));
}
- uASSERT_EQUAL(task1, getNextTask(allTasks, dirtyTasks));
+ uASSERT_EQUAL(task1, getNextTask({}, allTasks, dirtyTasks));
uASSERT_EQUAL(1u, dirtyTasks.size());
}
}
diff --git a/test/tmpfile.h b/test/tmpfile.h
index 5d114d0..0f83a20 100644
--- a/test/tmpfile.h
+++ b/test/tmpfile.h
@@ -3,39 +3,29 @@
// See accompanying file LICENSE for details.
#pragma once
-#include <cstdlib>
-#include <unistd.h>
+#include <cstdio>
-#ifdef _WIN32
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#endif
-
-struct tmp_file
+class TmpFile
{
- tmp_file(const std::string& data = {})
+public:
+ TmpFile(const std::string& data = {})
{
- int fd;
-#ifdef _WIN32
- char templ[] = "ctor_tmp_file-XXXXXX"; // buffer for filename
- _mktemp_s(templ, sizeof(templ));
- fd = open(templ, O_CREAT | O_RDWR);
-#else
- char templ[] = "/tmp/ctor_tmp_file-XXXXXX"; // buffer for filename
- fd = mkstemp(templ);
-#endif
- filename = templ;
- auto sz = write(fd, data.data(), data.size());
- (void)sz;
- close(fd);
+ auto tmp_dir = std::filesystem::temp_directory_path();
+ auto tmp_file_template = tmp_dir / "ctor_tmp_file-";
+ std::FILE* fp{nullptr};
+ int counter{};
+ while(!fp)
+ {
+ filename = tmp_file_template.string() + std::to_string(counter++);
+ fp = std::fopen(filename.data(), "wx");
+ }
+ std::fwrite(data.data(), data.size(), 1, fp);
+ std::fclose(fp);
}
- ~tmp_file()
+ ~TmpFile()
{
- unlink(filename.data());
+ std::filesystem::remove(filename);
}
const std::string& get() const
diff --git a/test/tools_test.cc b/test/tools_test.cc
index 5ae04c3..cfbcbce 100644
--- a/test/tools_test.cc
+++ b/test/tools_test.cc
@@ -114,29 +114,6 @@ bool operator!=(const ctor::asm_flag& a, const ctor::asm_flag& b)
#include <uunit.h>
-const ctor::configuration& ctor::get_configuration()
-{
- static ctor::configuration cfg;
- return cfg;
-}
-
-std::string ctor::configuration::get(const std::string& key, [[maybe_unused]]const std::string& default_value) const
-{
- if(key == ctor::cfg::host_cxx)
- {
- return {};
- }
-
- if(key == ctor::cfg::build_cxx)
- {
- return {};
- }
-
- assert(false); // bad key
-
- return {};
-}
-
class ToolsTest
: public uUnit
{