summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/bootstrap.cc83
-rw-r--r--src/build.cc87
-rw-r--r--src/build.h19
-rw-r--r--src/configure.cc388
-rw-r--r--src/configure.h14
-rw-r--r--src/externals.h (renamed from src/settings.h)9
-rw-r--r--src/externals_manual.cc37
-rw-r--r--src/externals_manual.h12
-rw-r--r--src/libctor.cc54
-rw-r--r--src/libctor.h93
-rw-r--r--src/rebuild.cc251
-rw-r--r--src/rebuild.h18
-rw-r--r--src/task.cc27
-rw-r--r--src/task.h34
-rw-r--r--src/task_ar.cc105
-rw-r--r--src/task_ar.h10
-rw-r--r--src/task_cc.cc235
-rw-r--r--src/task_cc.h15
-rw-r--r--src/task_fn.cc121
-rw-r--r--src/task_fn.h47
-rw-r--r--src/task_ld.cc113
-rw-r--r--src/task_ld.h10
-rw-r--r--src/task_so.cc111
-rw-r--r--src/task_so.h10
-rw-r--r--src/tasks.cc118
-rw-r--r--src/tasks.h20
-rw-r--r--src/tools.cc136
-rw-r--r--src/tools.h52
-rw-r--r--src/unittest.cc16
-rw-r--r--src/unittest.h4
-rw-r--r--src/util.cc137
-rw-r--r--src/util.h20
32 files changed, 1824 insertions, 582 deletions
diff --git a/src/bootstrap.cc b/src/bootstrap.cc
new file mode 100644
index 0000000..9a3c321
--- /dev/null
+++ b/src/bootstrap.cc
@@ -0,0 +1,83 @@
+// -*- c++ -*-
+// Distributed under the BSD 2-Clause License.
+// See accompanying file LICENSE for details.
+#include <iostream>
+#include <array>
+
+#define BOOTSTRAP
+
+#include "libctor.h"
+
+#include "util.cc"
+#include "rebuild.cc"
+#include "task.cc"
+#include "task_cc.cc"
+#include "task_ar.cc"
+#include "execute.cc"
+#include "tasks.cc"
+#include "build.cc"
+#include "tools.cc"
+
+std::filesystem::path configurationFile("configuration.cc");
+std::filesystem::path configHeaderFile("config.h");
+
+const Configuration default_configuration{};
+const Configuration& configuration()
+{
+ return default_configuration;
+}
+
+bool hasConfiguration(const std::string& key)
+{
+ return false;
+}
+
+const std::string& getConfiguration(const std::string& key,
+ const std::string& defaultValue)
+{
+ return defaultValue;
+}
+
+int main(int argc, char* argv[])
+{
+ if(argc > 1)
+ {
+ std::cerr << "This is a minimal bootstrap version of " << argv[0] <<
+ " which doesn't support any arguments\n";
+ return 1;
+ }
+
+ Settings settings{};
+
+ settings.builddir = getConfiguration(cfg::builddir, "build");
+ settings.parallel_processes =
+ std::max(1u, std::thread::hardware_concurrency() * 2 - 1);
+ settings.verbose = 0;
+ auto all_tasks = getTasks(settings, {}, false);
+ for(auto task : all_tasks)
+ {
+ if(task->registerDepTasks(all_tasks))
+ {
+ return 1;
+ }
+ }
+
+ std::vector<Target> non_unittest_targets;
+ auto& targets = getTargets(settings);
+ for(const auto& target : targets)
+ {
+ if(target.config.type != TargetType::UnitTest &&
+ target.config.type != TargetType::UnitTestLib)
+ {
+ non_unittest_targets.push_back(target);
+ }
+ }
+
+ auto ret = build(settings, "all", non_unittest_targets, all_tasks);
+ if(ret != 0)
+ {
+ return ret;
+ }
+
+ return 0;
+}
diff --git a/src/build.cc b/src/build.cc
index 1b70c5b..98952e0 100644
--- a/src/build.cc
+++ b/src/build.cc
@@ -9,31 +9,44 @@
#include <chrono>
#include <set>
#include <thread>
+#include <list>
+
+#include "libctor.h"
using namespace std::chrono_literals;
int build(const Settings& settings,
const std::string& name,
- const std::list<std::shared_ptr<Task>>& tasks,
- const std::list<std::shared_ptr<Task>>& all_tasks)
+ const std::set<std::shared_ptr<Task>>& tasks,
+ const std::set<std::shared_ptr<Task>>& all_tasks,
+ bool dryrun)
{
if(settings.verbose > 1)
{
std::cout << "Building '" << name << "'\n";
}
- std::list<std::shared_ptr<Task>> dirtyTasks;
+ std::set<std::shared_ptr<Task>> dirtyTasks;
for(auto task : tasks)
{
if(task->dirty())
{
- dirtyTasks.push_back(task);
+ dirtyTasks.insert(task);
}
}
+ // Dry-run returns number of dirty tasks but otherwise does nothing.
+ if(dryrun)
+ {
+ return dirtyTasks.size();
+ }
+
if(dirtyTasks.empty())
{
- std::cout << "Nothing to be done for '"<< name << "'\n";
+ if(settings.verbose > -1)
+ {
+ std::cout << "Nothing to be done for '"<< name << "'\n";
+ }
return 0;
}
@@ -71,32 +84,33 @@ int build(const Settings& settings,
return task->run();
}));
started_one = true;
- std::this_thread::sleep_for(2ms);
+ // Make sure we don't start tasks on top of each other to prevent
+ // straining the disk.
+ std::this_thread::sleep_for(50ms);
}
for(auto process = processes.begin();
process != processes.end();
++process)
{
- if(process->valid())
+ if(process->valid() == false)
{
- if(process->get() != 0)
- {
- // TODO: Wait for other processes to finish before returning
- return 1;
- }
- processes.erase(process);
- break;
+ continue;
}
- }
- if(started_one)
- {
- std::this_thread::sleep_for(2ms);
+ auto ret = process->get();
+ if(ret != 0)
+ {
+ // NOTE Wait for other processes to finish before returning
+ return ret;
+ }
+ processes.erase(process);
+ break;
}
- else
+
+ if(!started_one) // prevent polling too fast if no task is yet ready
{
- std::this_thread::sleep_for(200ms);
+ std::this_thread::sleep_for(10ms);
}
}
@@ -104,11 +118,15 @@ int build(const Settings& settings,
process != processes.end();
++process)
{
+ if(process->valid() == false)
+ {
+ continue;
+ }
process->wait();
auto ret = process->get();
if(ret != 0)
{
- return 1;
+ return ret;
}
}
@@ -138,23 +156,24 @@ std::set<std::shared_ptr<Task>> getDepTasks(std::shared_ptr<Task> task)
int build(const Settings& settings,
const std::string& name,
- const std::list<std::shared_ptr<Task>>& all_tasks)
+ const std::set<std::shared_ptr<Task>>& all_tasks,
+ bool dryrun)
{
bool task_found{false};
for(auto task : all_tasks)
{
- if(task->name() == name || task->target() == name)
+ if(*task == name)
{
- std::cout << name << "\n";
task_found = true;
auto depSet = getDepTasks(task);
- std::list<std::shared_ptr<Task>> ts;
+ std::set<std::shared_ptr<Task>> ts;
for(const auto& task : depSet)
{
- ts.push_back(task);
+ ts.insert(task);
}
- auto ret = build(settings, name, ts, all_tasks);
+
+ auto ret = build(settings, name, ts, all_tasks, dryrun);
if(ret != 0)
{
return ret;
@@ -176,25 +195,25 @@ int build(const Settings& settings,
int build(const Settings& settings,
const std::string& name,
const std::vector<Target>& targets,
- const std::list<std::shared_ptr<Task>>& all_tasks)
+ const std::set<std::shared_ptr<Task>>& all_tasks,
+ bool dryrun)
{
bool task_found{false};
- std::list<std::shared_ptr<Task>> ts;
+ std::set<std::shared_ptr<Task>> ts;
for(const auto& target : targets)
{
for(auto task : all_tasks)
{
- if(task->name() == target.config.target ||
- task->target() == target.config.target)
+ if(!task->derived() && // only consider non-derived tasks
+ task->buildConfig().target == target.config.target)
{
- std::cout << target.config.target << "\n";
task_found = true;
auto depSet = getDepTasks(task);
for(const auto& task : depSet)
{
- ts.push_back(task);
+ ts.insert(task);
}
}
}
@@ -206,5 +225,5 @@ int build(const Settings& settings,
return 1;
}
- return build(settings, name, ts, all_tasks);
+ return build(settings, name, ts, all_tasks, dryrun);
}
diff --git a/src/build.h b/src/build.h
index 1db3f5c..caf7a68 100644
--- a/src/build.h
+++ b/src/build.h
@@ -4,23 +4,30 @@
#pragma once
#include <string>
-#include <list>
+#include <set>
#include <memory>
#include "task.h"
-#include "settings.h"
#include "tasks.h"
+struct Settings;
+
+//! Dry-run returns number of dirty tasks but otherwise does nothing.
int build(const Settings& settings,
const std::string& name,
- const std::list<std::shared_ptr<Task>>& tasks,
- const std::list<std::shared_ptr<Task>>& all_tasks);
+ const std::set<std::shared_ptr<Task>>& tasks,
+ const std::set<std::shared_ptr<Task>>& all_tasks,
+ bool dryrun = false);
+//! Dry-run returns number of dirty tasks but otherwise does nothing.
int build(const Settings& settings,
const std::string& name,
- const std::list<std::shared_ptr<Task>>& all_tasks);
+ const std::set<std::shared_ptr<Task>>& all_tasks,
+ bool dryrun = false);
+//! Dry-run returns number of dirty tasks but otherwise does nothing.
int build(const Settings& settings,
const std::string& name,
const std::vector<Target>& targets,
- const std::list<std::shared_ptr<Task>>& all_tasks);
+ const std::set<std::shared_ptr<Task>>& all_tasks,
+ bool dryrun = false);
diff --git a/src/configure.cc b/src/configure.cc
index b3517dc..a81f8ad 100644
--- a/src/configure.cc
+++ b/src/configure.cc
@@ -6,36 +6,67 @@
#include <iostream>
#include <filesystem>
#include <fstream>
+#include <optional>
#include <getoptpp/getoptpp.hpp>
-#include "settings.h"
#include "execute.h"
#include "libctor.h"
#include "tasks.h"
+#include "rebuild.h"
+#include "externals.h"
std::filesystem::path configurationFile("configuration.cc");
std::filesystem::path configHeaderFile("config.h");
-const std::map<std::string, std::string> default_configuration{};
-const std::map<std::string, std::string>& __attribute__((weak)) configuration()
+std::map<std::string, std::string> external_includedir;
+std::map<std::string, std::string> external_libdir;
+
+const Configuration default_configuration{};
+const Configuration& __attribute__((weak)) configuration()
{
return default_configuration;
}
+namespace ctor
+{
+std::optional<std::string> includedir;
+std::optional<std::string> libdir;
+}
+
bool hasConfiguration(const std::string& key)
{
+ if(key == cfg::ctor_includedir && ctor::includedir)
+ {
+ return true;
+ }
+
+ if(key == cfg::ctor_libdir && ctor::libdir)
+ {
+ return true;
+ }
+
const auto& c = configuration();
- return c.find(key) != c.end();
+ return c.tools.find(key) != c.tools.end();
}
const std::string& getConfiguration(const std::string& key,
const std::string& defaultValue)
{
+ if(key == cfg::ctor_includedir && ctor::includedir)
+ {
+ return *ctor::includedir;
+ }
+
+ if(key == cfg::ctor_libdir && ctor::libdir)
+ {
+ return *ctor::libdir;
+ }
+
const auto& c = configuration();
if(hasConfiguration(key))
{
- return c.at(key);
+ return c.tools.at(key);
}
return defaultValue;
@@ -44,7 +75,7 @@ const std::string& getConfiguration(const std::string& key,
std::string locate(const std::string& arch, const std::string& app)
{
std::string path_env = std::getenv("PATH");
- std::cout << path_env << "\n";
+ //std::cout << path_env << "\n";
std::string program = app;
if(!arch.empty())
@@ -72,15 +103,15 @@ std::string locate(const std::string& arch, const std::string& app)
auto perms = std::filesystem::status(prog_path).permissions();
if((perms & std::filesystem::perms::owner_exec) != std::filesystem::perms::none)
{
- std::cout << " - executable by owner\n";
+ //std::cout << " - executable by owner\n";
}
if((perms & std::filesystem::perms::group_exec) != std::filesystem::perms::none)
{
- std::cout << " - executable by group\n";
+ //std::cout << " - executable by group\n";
}
if((perms & std::filesystem::perms::others_exec) != std::filesystem::perms::none)
{
- std::cout << " - executable by others\n";
+ //std::cout << " - executable by others\n";
}
return prog_path.string();
@@ -92,21 +123,38 @@ std::string locate(const std::string& arch, const std::string& app)
return {};
}
-int configure(int argc, char* argv[])
+class Args
+ : public std::vector<char*>
{
- Settings settings;
-
- settings.builddir = "build";
+public:
+ Args(const std::vector<std::string>& args)
+ {
+ resize(args.size() + 1);
+ (*this)[0] = strdup("./ctor");
+ for(std::size_t i = 0; i < size() - 1; ++i)
+ {
+ (*this)[i + 1] = strdup(args[i].data());
+ }
+ }
- std::string cmd_str;
- for(int i = 0; i < argc; ++i)
+ ~Args()
{
- if(i > 0)
+ for(std::size_t i = 0; i < size(); ++i)
{
- cmd_str += " ";
+ free((*this)[i]);
}
- cmd_str += argv[i];
}
+};
+
+// helper constant for the visitor
+template<class> inline constexpr bool always_false_v = false;
+
+int regenerateCache(const Settings& default_settings,
+ const std::vector<std::string>& args,
+ const std::map<std::string, std::string>& env)
+{
+ Settings settings{default_settings};
+ Args vargs(args);
dg::Options opt;
int key{128};
@@ -119,6 +167,8 @@ int configure(int argc, char* argv[])
std::string cxx_prog = "g++";
std::string ar_prog = "ar";
std::string ld_prog = "ld";
+ std::string ctor_includedir;
+ std::string ctor_libdir;
opt.add("build-dir", required_argument, 'b',
"Set output directory for build files (default: '" +
@@ -191,6 +241,65 @@ int configure(int argc, char* argv[])
return 0;
});
+ opt.add("ctor-includedir", required_argument, key++,
+ "Set path to ctor header file, used for re-compiling.",
+ [&]() {
+ ctor_includedir = optarg;
+ return 0;
+ });
+
+ opt.add("ctor-libdir", required_argument, key++,
+ "Set path to ctor library file, used for re-compiling.",
+ [&]() {
+ ctor_libdir = optarg;
+ return 0;
+ });
+
+ // Resolv externals
+ ExternalConfigurations externalConfigs;
+ for(std::size_t i = 0; i < numExternalConfigFiles; ++i)
+ {
+ auto newExternalConfigs = externalConfigFiles[i].cb(settings);
+ externalConfigs.insert(externalConfigs.end(),
+ newExternalConfigs.begin(),
+ newExternalConfigs.end());
+ }
+
+ auto add_path_args =
+ [&](const std::string& name)
+ {
+ opt.add(name + "-includedir", required_argument, key++,
+ "Set path to " + name + " header file.",
+ [&]() {
+ external_includedir[name] = optarg;
+ return 0;
+ });
+
+ opt.add(name + "-libdir", required_argument, key++,
+ "Set path to " + name + " libraries.",
+ [&]() {
+ external_libdir[name] = optarg;
+ return 0;
+ });
+ };
+
+ for(const auto& ext : externalConfigs)
+ {
+ std::visit([&](auto&& arg)
+ {
+ using T = std::decay_t<decltype(arg)>;
+ if constexpr (std::is_same_v<T, ExternalManual>)
+ {
+ add_path_args(ext.name);
+ }
+ else
+ {
+ static_assert(always_false_v<T>, "non-exhaustive visitor!");
+ }
+ }, ext.external);
+
+ }
+
opt.add("help", no_argument, 'h',
"Print this help text.",
[&]() {
@@ -200,14 +309,14 @@ int configure(int argc, char* argv[])
return 0;
});
- opt.process(argc, argv);
+ opt.process(vargs.size(), vargs.data());
if(host_arch.empty())
{
host_arch = build_arch;
}
- auto tasks = getTasks(settings);
+ auto tasks = getTasks(settings, {}, false);
/*
bool needs_cpp{false};
bool needs_c{false};
@@ -233,32 +342,29 @@ int configure(int argc, char* argv[])
}
}
*/
- auto cc_env = getenv("CC");
- if(cc_env)
+
+ auto cc_env = env.find("CC");
+ if(cc_env != env.end())
{
- cmd_str = std::string("CC=") + cc_env + " " + cmd_str;
- cc_prog = cc_env;
+ cc_prog = cc_env->second;
}
- auto cxx_env = getenv("CXX");
- if(cxx_env)
+ auto cxx_env = env.find("CXX");
+ if(cxx_env != env.end())
{
- cmd_str = std::string("CXX=") + cxx_env + " " + cmd_str;
- cxx_prog = cxx_env;
+ cxx_prog = cxx_env->second;
}
- auto ar_env = getenv("AR");
- if(ar_env)
+ auto ar_env = env.find("AR");
+ if(ar_env != env.end())
{
- cmd_str = std::string("AR=") + ar_env + " " + cmd_str;
- ar_prog = ar_env;
+ ar_prog = ar_env->second;
}
- auto ld_env = getenv("LD");
- if(ld_env)
+ auto ld_env = env.find("LD");
+ if(ld_env != env.end())
{
- cmd_str = std::string("LD=") + ld_env + " " + cmd_str;
- ld_prog = ld_env;
+ ld_prog = ld_env->second;
}
std::string host_cc = locate(host_arch, cc_prog);
@@ -273,24 +379,113 @@ int configure(int argc, char* argv[])
std::cout << "Writing results to: " << configurationFile.string() << "\n";
{
std::ofstream istr(configurationFile);
- istr << "#include \"libctor.h\"\n\n";
- istr << "const std::map<std::string, std::string>& configuration()\n";
+ istr << "#include <libctor.h>\n\n";
+ istr << "const Configuration& configuration()\n";
istr << "{\n";
- istr << " static std::map<std::string, std::string> c =\n";
+ istr << " static Configuration cfg =\n";
istr << " {\n";
- istr << " { \"cmd\", \"" << cmd_str << "\" },\n";
- istr << " { \"" << cfg::builddir << "\", \"" << settings.builddir << "\" },\n";
- istr << " { \"" << cfg::host_cc << "\", \"" << host_cc << "\" },\n";
- istr << " { \"" << cfg::host_cxx << "\", \"" << host_cxx << "\" },\n";
- istr << " { \"" << cfg::host_ar << "\", \"" << host_ar << "\" },\n";
- istr << " { \"" << cfg::host_ld << "\", \"" << host_ld << "\" },\n";
- istr << " { \"" << cfg::build_cc << "\", \"" << build_cc << "\" },\n";
- istr << " { \"" << cfg::build_cxx << "\", \"" << build_cxx << "\" },\n";
- istr << " { \"" << cfg::build_ar << "\", \"" << build_ar << "\" },\n";
- istr << " { \"" << cfg::build_ld << "\", \"" << build_ld << "\" },\n";
+ istr << " .args = {";
+ for(const auto& arg : args)
+ {
+ istr << "\"" << arg << "\",";
+ }
+ istr << "},\n";
+ istr << " .env = {";
+ for(const auto& e : env)
+ {
+ istr << "{\"" << e.first << "\", \"" << e.second << "\"}, ";
+ }
+ istr << "},\n";
+
+ istr << " .tools = {\n";
+ istr << " { \"" << cfg::builddir << "\", \"" << settings.builddir << "\" },\n";
+ istr << " { \"" << cfg::host_cc << "\", \"" << host_cc << "\" },\n";
+ istr << " { \"" << cfg::host_cxx << "\", \"" << host_cxx << "\" },\n";
+ istr << " { \"" << cfg::host_ar << "\", \"" << host_ar << "\" },\n";
+ istr << " { \"" << cfg::host_ld << "\", \"" << host_ld << "\" },\n";
+ istr << " { \"" << cfg::build_cc << "\", \"" << build_cc << "\" },\n";
+ istr << " { \"" << cfg::build_cxx << "\", \"" << build_cxx << "\" },\n";
+ istr << " { \"" << cfg::build_ar << "\", \"" << build_ar << "\" },\n";
+ istr << " { \"" << cfg::build_ld << "\", \"" << build_ld << "\" },\n";
+ if(!ctor_includedir.empty())
+ {
+ istr << " { \"" << cfg::ctor_includedir << "\", \"" << ctor_includedir << "\" },\n";
+ ctor::includedir = ctor_includedir;
+ }
+ if(!ctor_libdir.empty())
+ {
+ istr << " { \"" << cfg::ctor_libdir << "\", \"" << ctor_libdir << "\" },\n";
+ ctor::libdir = ctor_libdir;
+ }
+
+ istr << " },\n";
+ istr << " .externals = {\n";
+
+ for(const auto& ext : externalConfigs)
+ {
+ istr << " { \"" << ext.name << "\", {\n";
+ Flags resolved_flags;
+ if(std::holds_alternative<ExternalManual>(ext.external))
+ {
+ if(auto ret = resolv(settings, ext,
+ std::get<ExternalManual>(ext.external),
+ resolved_flags))
+ {
+ return ret;
+ }
+ }
+ else
+ {
+ std::cout << "Unknown external type\n";
+ return 1;
+ }
+
+ if(!resolved_flags.cxxflags.empty())
+ {
+ istr << " .cxxflags = {";
+ for(const auto& flag : resolved_flags.cxxflags)
+ {
+ istr << "\"" << flag << "\",";
+ }
+ istr << "},\n";
+ }
+
+ if(!resolved_flags.cflags.empty())
+ {
+ istr << " .cflags = {";
+ for(const auto& flag : resolved_flags.cflags)
+ {
+ istr << "\"" << flag << "\",";
+ }
+ istr << "},\n";
+ }
+
+ if(!resolved_flags.ldflags.empty())
+ {
+ istr << " .ldflags = {";
+ for(const auto& flag : resolved_flags.ldflags)
+ {
+ istr << "\"" << flag << "\",";
+ }
+ istr << "},\n";
+ }
+
+ if(!resolved_flags.asmflags.empty())
+ {
+ istr << " .asmflags = {";
+ for(const auto& flag : resolved_flags.asmflags)
+ {
+ istr << "\"" << flag << "\",";
+ }
+ istr << "},\n";
+ }
+ istr << " }},\n";
+ }
+
+ istr << " },\n";
istr << " };\n";
- istr << " return c;\n";
- istr << "}\n";
+ istr << " return cfg;\n";
+ istr << "}\n\n";
}
{
@@ -302,3 +497,94 @@ int configure(int argc, char* argv[])
return 0;
}
+
+int configure(const Settings& global_settings, int argc, char* argv[])
+{
+ Settings settings{global_settings};
+
+ std::vector<std::string> args;
+ for(int i = 2; i < argc; ++i) // skip command and the first 'configure' arg
+ {
+ args.push_back(argv[i]);
+ }
+
+ std::map<std::string, std::string> env;
+ auto cc_env = getenv("CC");
+ if(cc_env)
+ {
+ env["CC"] = cc_env;
+ }
+
+ auto cxx_env = getenv("CXX");
+ if(cxx_env)
+ {
+ env["CXX"] = cxx_env;
+ }
+
+ auto ar_env = getenv("AR");
+ if(ar_env)
+ {
+ env["AR"] = ar_env;
+ }
+
+ auto ld_env = getenv("LD");
+ if(ld_env)
+ {
+ env["LD"] = ld_env;
+ }
+
+ auto ret = regenerateCache(settings, args, env);
+ if(ret != 0)
+ {
+ return ret;
+ }
+
+ recompileCheck(settings, argc, argv, false);
+
+ return 0;
+}
+
+int reconfigure(const Settings& settings, int argc, char* argv[])
+{
+ bool no_rerun{false};
+
+ std::vector<std::string> args;
+ for(int i = 2; i < argc; ++i) // skip executable name and 'reconfigure' arg
+ {
+ if(i == 2 && std::string(argv[i]) == "--no-rerun")
+ {
+ no_rerun = true;
+ continue;
+ }
+ args.push_back(argv[i]);
+ }
+
+ const auto& cfg = configuration();
+
+ std::cout << "Re-running configure:\n";
+ for(const auto& e : cfg.env)
+ {
+ std::cout << e.first << "=\"" << e.second << "\" ";
+ }
+ std::cout << argv[0] << " configure ";
+ for(const auto& arg : cfg.args)
+ {
+ std::cout << arg << " ";
+ }
+ std::cout << "\n";
+
+ auto ret = regenerateCache(settings, cfg.args, cfg.env);
+ if(ret != 0)
+ {
+ return ret;
+ }
+
+ recompileCheck(settings, 1, argv, false);
+
+ if(no_rerun)
+ {
+ return 0; // this was originally invoked by configure, don't loop
+ }
+
+ return execute(argv[0], args);
+}
diff --git a/src/configure.h b/src/configure.h
index de1b7e0..16499d6 100644
--- a/src/configure.h
+++ b/src/configure.h
@@ -6,16 +6,12 @@
#include <filesystem>
#include <string>
#include <map>
+#include <vector>
+
+struct Settings;
extern std::filesystem::path configurationFile;;
extern std::filesystem::path configHeaderFile;
-int configure(int argc, char* argv[]);
-
-bool hasConfiguration(const std::string& key);
-const std::string& getConfiguration(const std::string& key,
- const std::string& defaultValue);
-
-const std::map<std::string, std::string>& configuration();
-
-extern const std::map<std::string, std::string> default_configuration;
+int configure(const Settings& settings, int argc, char* argv[]);
+int reconfigure(const Settings& settings, int argc, char* argv[]);
diff --git a/src/settings.h b/src/externals.h
index d71717a..7b4aa23 100644
--- a/src/settings.h
+++ b/src/externals.h
@@ -3,11 +3,4 @@
// See accompanying file LICENSE for details.
#pragma once
-#include <cstddef>
-
-struct Settings
-{
- std::string builddir;
- std::size_t parallel_processes;
- int verbose{1};
-};
+#include "externals_manual.h"
diff --git a/src/externals_manual.cc b/src/externals_manual.cc
new file mode 100644
index 0000000..0e3cdfd
--- /dev/null
+++ b/src/externals_manual.cc
@@ -0,0 +1,37 @@
+// -*- c++ -*-
+// Distributed under the BSD 2-Clause License.
+// See accompanying file LICENSE for details.
+#include "externals_manual.h"
+
+#include <map>
+
+#include "libctor.h"
+
+#include "util.h"
+#include "tools.h"
+
+extern std::map<std::string, std::string> external_includedir;
+extern std::map<std::string, std::string> external_libdir;
+
+int resolv(const Settings& settings, const ExternalConfiguration& config,
+ const ExternalManual& ext, Flags& flags)
+{
+ auto tool_chain = getToolChain(config.system);
+
+ flags = ext.flags;
+
+ auto inc = external_includedir.find(config.name);
+ if(inc != external_includedir.end())
+ {
+ append(flags.cflags, getOption(tool_chain, opt::include_path, inc->second));
+ append(flags.cxxflags, getOption(tool_chain, opt::include_path, inc->second));
+ }
+
+ auto lib = external_libdir.find(config.name);
+ if(lib != external_libdir.end())
+ {
+ append(flags.ldflags, getOption(tool_chain, opt::library_path, lib->second));
+ }
+
+ return 0;
+}
diff --git a/src/externals_manual.h b/src/externals_manual.h
new file mode 100644
index 0000000..7bd968d
--- /dev/null
+++ b/src/externals_manual.h
@@ -0,0 +1,12 @@
+// -*- c++ -*-
+// Distributed under the BSD 2-Clause License.
+// See accompanying file LICENSE for details.
+#pragma once
+
+struct Settings;
+struct ExternalConfiguration;
+struct ExternalManual;
+struct Flags;
+
+int resolv(const Settings& settings, const ExternalConfiguration& name,
+ const ExternalManual& ext, Flags& flags);
diff --git a/src/libctor.cc b/src/libctor.cc
index 4d0d6d9..d188771 100644
--- a/src/libctor.cc
+++ b/src/libctor.cc
@@ -20,25 +20,29 @@
#include <getoptpp/getoptpp.hpp>
#include "libctor.h"
-#include "settings.h"
#include "configure.h"
#include "rebuild.h"
#include "tasks.h"
#include "build.h"
#include "unittest.h"
+
int main(int argc, char* argv[])
{
+ Settings settings{};
+
+ settings.builddir = getConfiguration(cfg::builddir, settings.builddir);
+ settings.parallel_processes =
+ std::max(1u, std::thread::hardware_concurrency()) * 2 - 1;
+
if(argc > 1 && std::string(argv[1]) == "configure")
{
- return configure(argc, argv);
+ return configure(settings, argc, argv);
}
- Settings settings{};
-
- settings.builddir = getConfiguration(cfg::builddir, "build");
- settings.parallel_processes =
- std::max(1u, std::thread::hardware_concurrency() * 2 - 1);
- settings.verbose = 0;
+ if(argc > 1 && std::string(argv[1]) == "reconfigure")
+ {
+ return reconfigure(settings, argc, argv);
+ }
bool write_compilation_database{false};
std::string compilation_database;
@@ -85,6 +89,13 @@ int main(int argc, char* argv[])
return 0;
});
+ opt.add("quiet", no_argument, 'q',
+ "Be completely silent.",
+ [&]() {
+ settings.verbose = -1;
+ return 0;
+ });
+
opt.add("add", required_argument, 'a',
"Add specified file to the build configurations.",
[&]() {
@@ -148,9 +159,10 @@ int main(int argc, char* argv[])
std::cout << "Usage: " << argv[0] << " [options] [target] ...\n";
std::cout <<
R"_( where target can be either:
- configure - run configuration step (cannot be used with other targets).
- clean - clean all generated files.
- all - build all targets (default)
+ configure - run configuration step (cannot be used with other targets).
+ reconfigure - rerun configuration step with the same arguments as last (cannot be used with other targets).
+ clean - clean all generated files.
+ all - build all targets (default)
or the name of a target which will be built along with its dependencies.
Use '-l' to see a list of possible target names.
@@ -178,6 +190,11 @@ Options:
files.insert(configFiles[i].file);
}
+ for(std::size_t i = 0; i < numExternalConfigFiles; ++i)
+ {
+ files.insert(externalConfigFiles[i].file);
+ }
+
for(const auto& file : files)
{
std::cout << file << "\n";
@@ -189,7 +206,7 @@ Options:
no_default_build = true;
for(const auto& add_file : add_files)
{
- reg(add_file.data(), [](){ return std::vector<BuildConfiguration>{};});
+ reg(add_file.data());
}
for(const auto& remove_file : remove_files)
@@ -198,7 +215,7 @@ Options:
}
// Force rebuild if files were added
- recompileCheck(settings, 1, argv, true, no_relaunch == false);
+ recompileCheck(settings, 1, argv, no_relaunch == false);
}
recompileCheck(settings, argc, argv);
@@ -253,7 +270,7 @@ Options:
{
no_default_build = true;
const auto& c = configuration();
- for(const auto& config : c)
+ for(const auto& config : c.tools)
{
std::cout << config.first << ": " << config.second << "\n";
}
@@ -298,7 +315,8 @@ Options:
auto& targets = getTargets(settings);
for(const auto& target : targets)
{
- if(target.config.type == TargetType::UnitTest)
+ if(target.config.type == TargetType::UnitTest ||
+ target.config.type == TargetType::UnitTestLib)
{
unittest_targets.push_back(target);
}
@@ -320,7 +338,8 @@ Options:
auto& targets = getTargets(settings);
for(const auto& target : targets)
{
- if(target.config.type != TargetType::UnitTest)
+ if(target.config.type != TargetType::UnitTest &&
+ target.config.type != TargetType::UnitTestLib)
{
non_unittest_targets.push_back(target);
}
@@ -349,7 +368,8 @@ Options:
auto& targets = getTargets(settings);
for(const auto& target : targets)
{
- if(target.config.type != TargetType::UnitTest)
+ if(target.config.type != TargetType::UnitTest &&
+ target.config.type != TargetType::UnitTestLib)
{
non_unittest_targets.push_back(target);
}
diff --git a/src/libctor.h b/src/libctor.h
index 70c62a8..96e1115 100644
--- a/src/libctor.h
+++ b/src/libctor.h
@@ -3,9 +3,13 @@
// See accompanying file LICENSE for details.
#pragma once
+#include <source_location>
#include <string>
#include <vector>
#include <map>
+#include <variant>
+#include <cstddef>
+#include <functional>
enum class TargetType
{
@@ -16,6 +20,8 @@ enum class TargetType
DynamicLibrary,
Object,
UnitTest,
+ UnitTestLib,
+ Function,
};
enum class Language
@@ -33,29 +39,86 @@ enum class OutputSystem
Build, // Internal tool during cross-compilation
};
-struct BuildConfiguration
+struct Source
{
- TargetType type{TargetType::Auto};
+ Source(const char* file) : file(file) {}
+ Source(const std::string& file) : file(file) {}
+ Source(const char* file, Language lang) : file(file), language(lang) {}
+ Source(const std::string& file, Language lang) : file(file), language(lang) {}
+
+ Source(const char* file, const char* output) : file(file), output(output) {}
+ Source(const std::string& file, const std::string& output) : file(file), output(output) {}
+ Source(const char* file, Language lang, const char* output) : file(file), language(lang), output(output) {}
+ Source(const std::string& file, Language lang, const std::string& output) : file(file), language(lang), output(output) {}
+
+ std::string file;
Language language{Language::Auto};
- OutputSystem system{OutputSystem::Host};
- std::string target;
- std::vector<std::string> sources; // source list
- std::vector<std::string> depends; // internal dependencies
+ std::string output{};
+};
+
+struct Flags
+{
std::vector<std::string> cxxflags; // flags for c++ compiler
std::vector<std::string> cflags; // flags for c compiler
std::vector<std::string> ldflags; // flags for linker
std::vector<std::string> asmflags; // flags for asm translator
};
+struct Settings
+{
+ std::string builddir{"build"};
+ std::size_t parallel_processes{1};
+ int verbose{0}; // -1: completely silent, 0: normal, 1: verbose, ...
+};
+
+struct BuildConfiguration;
+using GeneratorCb = std::function<int(const std::string& input,
+ const std::string& output,
+ const BuildConfiguration& config,
+ const Settings& settings)>;
+
+struct BuildConfiguration
+{
+ std::string name; // Name - used for referring in other configurations.
+ TargetType type{TargetType::Auto};
+ OutputSystem system{OutputSystem::Host};
+ std::string target; // Output target file for this configuration
+ std::vector<Source> sources; // source list
+ std::vector<std::string> depends; // internal target dependencies
+ Flags flags;
+ std::vector<std::string> externals; // externals used by this configuration
+ GeneratorCb function;
+};
+
using BuildConfigurations = std::vector<BuildConfiguration>;
-int reg(const char* location, BuildConfigurations (*cb)());
+int reg(BuildConfigurations (*cb)(const Settings&),
+ const std::source_location location = std::source_location::current());
+
+// This type will use flags verbatim
+struct ExternalManual
+{
+ Flags flags;
+};
+
+
+struct ExternalConfiguration
+{
+ std::string name; // Name for configuration
+ OutputSystem system{OutputSystem::Host};
+ std::variant<ExternalManual> external;
+};
+
+using ExternalConfigurations = std::vector<ExternalConfiguration>;
+
+int reg(ExternalConfigurations (*cb)(const Settings&),
+ const std::source_location location = std::source_location::current());
// Convenience macro - ugly but keeps things simple(r)
#define CONCAT(a, b) CONCAT_INNER(a, b)
#define CONCAT_INNER(a, b) a ## b
#define UNIQUE_NAME(base) CONCAT(base, __LINE__)
-#define REG(cb) namespace { int UNIQUE_NAME(unique) = reg(__FILE__, cb); }
+#define REG(cb) namespace { int UNIQUE_NAME(unique) = reg(cb); }
// Predefined configuration keys
namespace cfg
@@ -71,9 +134,21 @@ constexpr auto build_cc = "build-cc";
constexpr auto build_cxx = "build-cpp";
constexpr auto build_ar = "build-ar";
constexpr auto build_ld = "build-ld";
+
+constexpr auto ctor_includedir = "ctor-includedir";
+constexpr auto ctor_libdir = "ctor-libdir";
}
-const std::map<std::string, std::string>& configuration();
+struct Configuration
+{
+ std::vector<std::string> args; // vector of arguments used when last calling configure
+ std::map<std::string, std::string> env; // env used when last calling configure
+
+ std::map<std::string, std::string> tools; // tools
+ std::map<std::string, Flags> externals;
+};
+
+const Configuration& configuration();
bool hasConfiguration(const std::string& key);
const std::string& getConfiguration(const std::string& key,
const std::string& defaultValue = {});
diff --git a/src/rebuild.cc b/src/rebuild.cc
index 353beb0..9ddf5ba 100644
--- a/src/rebuild.cc
+++ b/src/rebuild.cc
@@ -6,18 +6,22 @@
#include <iostream>
#include <filesystem>
#include <algorithm>
+#include <source_location>
+#include <cstring>
-#include "execute.h"
#include "configure.h"
-#include "settings.h"
#include "libctor.h"
+#include "tasks.h"
+#include "build.h"
+#include "execute.h"
+#include "tools.h"
+#include "util.h"
std::array<BuildConfigurationEntry, 1024> configFiles;
std::size_t numConfigFiles{0};
-// TODO: Use c++20 when ready, somehing like this:
-//int reg(const std::source_location location = std::source_location::current())
-int reg(const char* location, std::vector<BuildConfiguration> (*cb)())
+int reg(BuildConfigurations (*cb)(const Settings&),
+ const std::source_location location)
{
// NOTE: std::cout cannot be used here
if(numConfigFiles >= configFiles.size())
@@ -27,13 +31,41 @@ int reg(const char* location, std::vector<BuildConfiguration> (*cb)())
exit(1);
}
- configFiles[numConfigFiles].file = location;
+ 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
+ }
+ else
+ {
+ configFiles[numConfigFiles].file = location.file_name();
+ }
configFiles[numConfigFiles].cb = cb;
++numConfigFiles;
return 0;
}
+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);
+ }
+
+ configFiles[numConfigFiles].file = location;
+ configFiles[numConfigFiles].cb =
+ [](const Settings&){ return std::vector<BuildConfiguration>{}; };
+ ++numConfigFiles;
+
+ return 0;
+}
+
int unreg(const char* location)
{
std::size_t found{0};
@@ -54,91 +86,196 @@ int unreg(const char* location)
}
}
+ 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;
}
-void recompileCheck(const Settings& settings, int argc, char* argv[],
- bool force, bool relaunch_allowed)
+std::array<ExternalConfigurationEntry, 1024> externalConfigFiles;
+std::size_t numExternalConfigFiles{0};
+
+int reg(ExternalConfigurations (*cb)(const Settings&),
+ const std::source_location location)
{
- bool dirty{force};
+ // 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);
+ }
- std::vector<std::string> args;
- args.push_back("-s");
- args.push_back("-O3");
- args.push_back("-std=c++17");
- args.push_back("-pthread");
+ externalConfigFiles[numExternalConfigFiles].file = location.file_name();
+ externalConfigFiles[numExternalConfigFiles].cb = cb;
+ ++numExternalConfigFiles;
- std::filesystem::path binFile(argv[0]);
+ return 0;
+}
- if(std::filesystem::exists(configurationFile))
+namespace
+{
+bool contains(const std::vector<Source>& sources, const std::string& file)
+{
+ for(const auto& source : sources)
{
- args.push_back(configurationFile.string());
-
- if(std::filesystem::last_write_time(binFile) <=
- std::filesystem::last_write_time(configurationFile))
- {
- dirty = true;
- }
-
- const auto& c = configuration();
- if(&c == &default_configuration)
+ if(source.file == file)
{
- // configuration.cc exists, but currently compiled with the default one.
- dirty = true;
+ return true;
}
}
- if(settings.verbose > 1)
+ return false;
+}
+}
+
+bool recompileCheck(const Settings& global_settings, int argc, char* argv[],
+ bool relaunch_allowed)
+{
+ using namespace std::string_literals;
+
+ if(global_settings.verbose > 1)
{
std::cout << "Recompile check (" << numConfigFiles << "):\n";
}
+ BuildConfiguration config;
+
+ config.name = "ctor";
+ config.system = OutputSystem::Build;
+
+ auto tool_chain = getToolChain(config.system);
+
+ append(config.flags.cxxflags,
+ getOption(tool_chain, opt::optimization, "3"));
+ append(config.flags.cxxflags,
+ getOption(tool_chain, opt::cpp_std, "c++20"));
+ if(hasConfiguration(cfg::ctor_includedir))
+ {
+ append(config.flags.cxxflags,
+ getOption(tool_chain, opt::include_path,
+ getConfiguration(cfg::ctor_includedir)));
+ }
+ if(hasConfiguration(cfg::ctor_libdir))
+ {
+ append(config.flags.ldflags,
+ getOption(tool_chain, opt::library_path,
+ getConfiguration(cfg::ctor_libdir)));
+ }
+ append(config.flags.ldflags, getOption(tool_chain, opt::link, "ctor"));
+ append(config.flags.ldflags, getOption(tool_chain, opt::threads));
+
+ Settings settings{global_settings};
+ settings.verbose = -1; // Make check completely silent.
+ settings.builddir += "/ctor"; // override builddir to use ctor subdir
+
+ {
+ std::filesystem::path buildfile = settings.builddir;
+ std::filesystem::path currentfile = argv[0];
+ config.target = std::filesystem::relative(currentfile, buildfile).string();
+ }
+
+ if(std::filesystem::exists(configurationFile))
+ {
+ config.sources.push_back(configurationFile.string());
+ }
+
for(std::size_t i = 0; i < numConfigFiles; ++i)
{
std::string location = configFiles[i].file;
- if(settings.verbose > 1)
+ if(global_settings.verbose > 1)
{
std::cout << " - " << location << "\n";
}
- std::filesystem::path configFile(location);
- if(std::filesystem::last_write_time(binFile) <=
- std::filesystem::last_write_time(configFile))
+
+ // Ensure that files containing multiple configurations are only added once.
+ if(!contains(config.sources, location))
{
- dirty = true;
+ config.sources.push_back(location);
}
+ }
- // Support adding multiple config functions from the same file
- if(std::find(args.begin(), args.end(), location) == std::end(args))
+ for(std::size_t i = 0; i < numExternalConfigFiles; ++i)
+ {
+ std::string location = externalConfigFiles[i].file;
+ if(global_settings.verbose > 1)
{
- args.push_back(location);
+ std::cout << " - " << location << "\n";
+ }
+
+ // Ensure that files containing multiple configurations are only added once.
+ if(!contains(config.sources, location))
+ {
+ config.sources.push_back(location);
+ }
+ }
+
+ auto tasks = taskFactory({config}, settings, {});
+
+ for(auto task : tasks)
+ {
+ if(task->registerDepTasks(tasks))
+ {
+ return false;
+ }
+ }
+
+ // Find out if reconfigure is needed
+ bool reconfigure{false};
+ for(auto task : tasks)
+ {
+ if(task->dirty() &&
+ task->source() != "" && // only look at source files
+ task->source() != "configuration.cc") // don't reconfigure if only configuration.cc is changed.
+ {
+ reconfigure |= true;
}
}
- args.push_back("libctor.a");
- args.push_back("-o");
- args.push_back(binFile.string());
- if(dirty)
+ auto dirty_tasks = build(settings, "ctor", tasks, true); // dryrun
+ if(dirty_tasks)
{
- std::cout << "Rebuilding config\n";
- auto tool = getConfiguration(cfg::build_cxx, "/usr/bin/g++");
- auto ret = execute(tool, args, settings.verbose > 0);
+ std::cout << "Rebuilding config.\n";
+ auto ret = build(settings, "ctor", tasks); // run for real
if(ret != 0)
{
- std::cerr << "Failed: ." << ret << "\n";
- exit(1);
+ return ret;
}
- else
+ }
+
+ if(reconfigure)
+ {
+ std::vector<std::string> args;
+ args.push_back("reconfigure");
+ if(!relaunch_allowed)
{
- if(relaunch_allowed)
- {
- std::cout << "Re-launch\n";
- std::vector<std::string> args;
- for(int i = 1; i < argc; ++i)
- {
- args.push_back(argv[i]);
- }
- exit(execute(argv[0], args, settings.verbose > 0));
- }
+ args.push_back("--no-rerun");
}
+ for(int i = 1; i < argc; ++i)
+ {
+ args.push_back(argv[i]);
+ }
+ auto ret = execute(argv[0], args);
+ //if(ret != 0)
+ {
+ exit(ret);
+ }
+
}
+
+ return dirty_tasks;
}
diff --git a/src/rebuild.h b/src/rebuild.h
index 906d089..f1255c6 100644
--- a/src/rebuild.h
+++ b/src/rebuild.h
@@ -13,14 +13,24 @@ class Settings;
struct BuildConfigurationEntry
{
const char* file;
- std::vector<BuildConfiguration> (*cb)();
+ BuildConfigurations (*cb)(const Settings&);
+};
+
+struct ExternalConfigurationEntry
+{
+ const char* file;
+ ExternalConfigurations (*cb)(const Settings&);
};
extern std::array<BuildConfigurationEntry, 1024> configFiles;
extern std::size_t numConfigFiles;
-//int reg(const char* location, std::vector<BuildConfiguration> (*cb)());
+extern std::array<ExternalConfigurationEntry, 1024> externalConfigFiles;
+extern std::size_t numExternalConfigFiles;
+
+int reg(const char* location);
int unreg(const char* location);
-void recompileCheck(const Settings& settings, int argc, char* argv[],
- bool force = false, bool relaunch_allowed = true);
+//! Returns true of recompilation was needed.
+bool recompileCheck(const Settings& settings, int argc, char* argv[],
+ bool relaunch_allowed = true);
diff --git a/src/task.cc b/src/task.cc
index 1c6c233..fb50765 100644
--- a/src/task.cc
+++ b/src/task.cc
@@ -6,22 +6,25 @@
#include <unistd.h>
#include <iostream>
-Task::Task(const BuildConfiguration& config)
+Task::Task(const BuildConfiguration& config, const Settings& settings,
+ const std::string& sourceDir)
: config(config)
, output_system(config.system)
+ , settings(settings)
+ , sourceDir(sourceDir)
{
}
-int Task::registerDepTasks(const std::list<std::shared_ptr<Task>>& tasks)
+int Task::registerDepTasks(const std::set<std::shared_ptr<Task>>& tasks)
{
for(const auto& depStr : depends())
{
bool found{false};
for(const auto& task : tasks)
{
- if(task->target() == depStr)
+ if(*task == depStr)
{
- dependsTasks.push_back(task);
+ dependsTasks.insert(task);
found = true;
}
}
@@ -33,12 +36,22 @@ int Task::registerDepTasks(const std::list<std::shared_ptr<Task>>& tasks)
}
}
- return 0;
+ return registerDepTasksInner(tasks);
+}
+
+bool Task::operator==(const std::string& depStr)
+{
+ return
+ name() == depStr ||
+ target() == depStr ||
+ sourceDir + "/" + target() == depStr ||
+ targetFile().string() == depStr
+ ;
}
std::string Task::name() const
{
- return config.target;
+ return config.name;
}
bool Task::dirty()
@@ -141,7 +154,7 @@ std::string Task::compiler() const
}
}
-std::list<std::shared_ptr<Task>> Task::getDependsTasks()
+std::set<std::shared_ptr<Task>> Task::getDependsTasks()
{
return dependsTasks;
}
diff --git a/src/task.h b/src/task.h
index 78dc904..be3995f 100644
--- a/src/task.h
+++ b/src/task.h
@@ -6,8 +6,9 @@
#include <vector>
#include <string>
#include <atomic>
-#include <list>
+#include <set>
#include <memory>
+#include <filesystem>
#include "libctor.h"
@@ -20,12 +21,18 @@ enum class State
Error,
};
+struct Settings;
+
class Task
{
public:
- Task(const BuildConfiguration& config);
+ Task(const BuildConfiguration& config, const Settings& settings,
+ const std::string& sourceDir);
+
+ int registerDepTasks(const std::set<std::shared_ptr<Task>>& tasks);
+ virtual int registerDepTasksInner(const std::set<std::shared_ptr<Task>>& tasks) { return 0; }
- int registerDepTasks(const std::list<std::shared_ptr<Task>>& tasks);
+ bool operator==(const std::string& dep);
virtual std::string name() const;
bool dirty();
@@ -34,10 +41,23 @@ public:
State state() const;
virtual int clean() = 0 ;
virtual std::vector<std::string> depends() const = 0;
+
+ //! Raw target name as stated in ctor.cc config file or (in case of a derived
+ //! target) the calculated target without builddir prefix.
virtual std::string target() const = 0;
+ //! Target file with full path prefix
+ virtual std::filesystem::path targetFile() const = 0;
+
+ //! Returns true for tasks that are non-target tasks, ie. for example derived
+ //! objects files from target sources.
+ virtual bool derived() const = 0;
+
virtual std::string toJSON() const { return {}; };
+ //! Returns a reference to the originating build config.
+ //! Note: the build config of a derived task will be that of its parent
+ //! (target) task.
const BuildConfiguration& buildConfig() const;
TargetType targetType() const;
@@ -45,7 +65,9 @@ public:
OutputSystem outputSystem() const;
std::string compiler() const;
- std::list<std::shared_ptr<Task>> getDependsTasks();
+ std::set<std::shared_ptr<Task>> getDependsTasks();
+
+ virtual std::string source() const { return {}; }
protected:
std::atomic<State> task_state{State::Unknown};
@@ -53,9 +75,11 @@ protected:
virtual bool dirtyInner() { return false; }
std::vector<std::string> dependsStr;
- std::list<std::shared_ptr<Task>> dependsTasks;
+ std::set<std::shared_ptr<Task>> dependsTasks;
const BuildConfiguration& config;
TargetType target_type{TargetType::Auto};
Language source_language{Language::Auto};
OutputSystem output_system{OutputSystem::Host};
+ const Settings& settings;
+ std::string sourceDir;
};
diff --git a/src/task_ar.cc b/src/task_ar.cc
index e920571..3e1746c 100644
--- a/src/task_ar.cc
+++ b/src/task_ar.cc
@@ -7,36 +7,22 @@
#include <fstream>
#include "libctor.h"
-#include "settings.h"
#include "execute.h"
-
-namespace
-{
-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();
- ifs.seekg(0, std::ios::beg);
-
- std::vector<char> bytes(fileSize);
- ifs.read(bytes.data(), fileSize);
-
- return std::string(bytes.data(), fileSize);
-}
-} // namespace ::
+#include "util.h"
TaskAR::TaskAR(const BuildConfiguration& config,
const Settings& settings,
const std::string& target,
- const std::vector<std::string>& objects)
- : Task(config)
+ const std::vector<std::string>& objects,
+ const std::string& sourceDir)
+ : Task(config, settings, sourceDir)
, config(config)
, settings(settings)
+ , sourceDir(sourceDir)
{
- targetFile = settings.builddir;
- targetFile /= target;
+ std::filesystem::create_directories(std::filesystem::path(settings.builddir) / sourceDir);
+
+ _targetFile = target;
for(const auto& object : objects)
{
std::filesystem::path objectFile = object;
@@ -46,19 +32,18 @@ TaskAR::TaskAR(const BuildConfiguration& config,
for(const auto& dep : config.depends)
{
- std::filesystem::path depFile = settings.builddir;
- depFile /= dep;
- depFiles.push_back(depFile);
+ depFiles.push_back(dep);
}
- flagsFile = settings.builddir / targetFile.stem();
+ flagsFile = std::filesystem::path(settings.builddir) / cleanUp(sourceDir) / targetFile().stem();
flagsFile += ".flags";
target_type = TargetType::StaticLibrary;
source_language = Language::C;
for(const auto& source : config.sources)
{
- std::filesystem::path sourceFile(source);
+ std::filesystem::path sourceFile(source.file);
+ // TODO: Use task languages instead
if(sourceFile.extension().string() != ".c")
{
source_language = Language::Cpp;
@@ -68,7 +53,7 @@ TaskAR::TaskAR(const BuildConfiguration& config,
bool TaskAR::dirtyInner()
{
- if(!std::filesystem::exists(targetFile))
+ if(!std::filesystem::exists(targetFile()))
{
return true;
}
@@ -78,15 +63,6 @@ bool TaskAR::dirtyInner()
return true;
}
- for(const auto& objectFile : objectFiles)
- {
- if(std::filesystem::last_write_time(targetFile) <=
- std::filesystem::last_write_time(objectFile))
- {
- return true;
- }
- }
-
{
auto lastFlags = readFile(flagsFile.string());
if(flagsString() != lastFlags)
@@ -101,26 +77,12 @@ bool TaskAR::dirtyInner()
int TaskAR::runInner()
{
- std::string objectlist;
- for(const auto& objectFile : objectFiles)
- {
- if(!objectlist.empty())
- {
- objectlist += " ";
- }
- objectlist += objectFile.string();
- }
-
std::vector<std::string> args;
args.push_back("rcs");
- args.push_back(targetFile.string());
- for(const auto& objectFile : objectFiles)
+ args.push_back(targetFile().string());
+ for(const auto& task : getDependsTasks())
{
- args.push_back(objectFile.string());
- }
- for(const auto& flag : config.ldflags)
- {
- args.push_back(flag);
+ args.push_back(task->targetFile().string());
}
{ // Write flags to file.
@@ -130,7 +92,7 @@ int TaskAR::runInner()
if(settings.verbose == 0)
{
- std::cout << "AR => " << targetFile.string() << "\n";
+ std::cout << "AR => " << targetFile().string() << "\n";
}
std::string tool;
@@ -149,10 +111,10 @@ int TaskAR::runInner()
int TaskAR::clean()
{
- if(std::filesystem::exists(targetFile))
+ if(std::filesystem::exists(targetFile()))
{
- std::cout << "Removing " << targetFile.string() << "\n";
- std::filesystem::remove(targetFile);
+ std::cout << "Removing " << targetFile().string() << "\n";
+ std::filesystem::remove(targetFile());
}
if(std::filesystem::exists(flagsFile))
@@ -172,9 +134,9 @@ std::vector<std::string> TaskAR::depends() const
deps.push_back(objectFile.string());
}
- for(const auto& depFile : depFiles)
+ for(const auto& dep : config.depends)
{
- deps.push_back(depFile.string());
+ deps.push_back(dep);
}
return deps;
@@ -182,15 +144,25 @@ std::vector<std::string> TaskAR::depends() const
std::string TaskAR::target() const
{
- return targetFile.string();
+ return _targetFile.string();
+}
+
+std::filesystem::path TaskAR::targetFile() const
+{
+ return std::filesystem::path(settings.builddir) / sourceDir / _targetFile;
+}
+
+bool TaskAR::derived() const
+{
+ return false;
}
std::string TaskAR::flagsString() const
{
std::string flagsStr;
- for(const auto& flag : config.ldflags)
+ for(const auto& flag : config.flags.ldflags)
{
- if(flag != config.ldflags[0])
+ if(flag != config.flags.ldflags[0])
{
flagsStr += " ";
}
@@ -207,5 +179,12 @@ std::string TaskAR::flagsString() const
flagsStr += dep;
}
+ auto deps = depends();
+ for(const auto& dep : deps)
+ {
+ flagsStr += " ";
+ flagsStr += dep;
+ }
+
return flagsStr;
}
diff --git a/src/task_ar.h b/src/task_ar.h
index abdc3ae..c76a852 100644
--- a/src/task_ar.h
+++ b/src/task_ar.h
@@ -20,7 +20,9 @@ public:
TaskAR(const BuildConfiguration& config,
const Settings& settings,
const std::string& target,
- const std::vector<std::string>& objects);
+ const std::vector<std::string>& objects,
+ const std::string& sourceDir);
+ virtual ~TaskAR() = default;
bool dirtyInner() override;
@@ -30,15 +32,19 @@ public:
std::vector<std::string> depends() const override;
std::string target() const override;
+ std::filesystem::path targetFile() const override;
+
+ bool derived() const override;
private:
std::string flagsString() const;
std::vector<std::filesystem::path> objectFiles;
std::vector<std::filesystem::path> depFiles;
- std::filesystem::path targetFile;
+ std::filesystem::path _targetFile;
std::filesystem::path flagsFile;
const BuildConfiguration& config;
const Settings& settings;
+ std::string sourceDir;
};
diff --git a/src/task_cc.cc b/src/task_cc.cc
index 8256c70..c4343b6 100644
--- a/src/task_cc.cc
+++ b/src/task_cc.cc
@@ -5,132 +5,85 @@
#include <iostream>
#include <fstream>
+#include <cassert>
#include "libctor.h"
-#include "settings.h"
#include "execute.h"
-
-namespace
-{
-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();
- ifs.seekg(0, std::ios::beg);
-
- std::vector<char> bytes(fileSize);
- ifs.read(bytes.data(), fileSize);
-
- return std::string(bytes.data(), fileSize);
-}
-
-std::vector<std::string> readDeps(const std::string& depFile)
-{
- if(!std::filesystem::exists(depFile))
- {
- return {};
- }
-
- auto str = readFile(depFile);
-
- std::vector<std::string> output;
- std::string tmp;
- bool start{false};
- bool in_whitespace{false};
- for(const auto& c : str)
- {
- if(c == '\\' || c == '\n')
- {
- continue;
- }
-
- if(c == ':')
- {
- start = true;
- continue;
- }
-
- if(!start)
- {
- continue;
- }
-
- if(c == ' ' || c == '\t')
- {
- if(in_whitespace)
- {
- continue;
- }
-
- if(!tmp.empty())
- {
- output.push_back(tmp);
- }
- tmp.clear();
- in_whitespace = true;
- }
- else
- {
- in_whitespace = false;
- tmp += c;
- }
- }
-
- if(!tmp.empty())
- {
- output.push_back(tmp);
- }
-
- return output;
-}
-} // namespace ::
+#include "util.h"
+#include "tools.h"
TaskCC::TaskCC(const BuildConfiguration& config, const Settings& settings,
- const std::string& sourceDir, const std::string& source)
- : Task(config)
+ const std::string& sourceDir, const Source& source)
+ : Task(config, settings, sourceDir)
, config(config)
, settings(settings)
, sourceDir(sourceDir)
+ , _source(source)
{
sourceFile = sourceDir;
- sourceFile /= source;
+ sourceFile /= source.file;
+
+ std::filesystem::path base = sourceFile.parent_path();
+ std::filesystem::create_directories(std::filesystem::path(settings.builddir) / base);
- std::filesystem::path base = settings.builddir;
- base /= config.target;
+ base /= cleanUp(config.target);
base += "-";
base += sourceFile.stem();
- if(sourceFile.extension().string() == ".c")
+ target_type = TargetType::Object;
+ source_language = source.language;
+ if(source_language == Language::Auto)
{
- base += "_c";
+ source_language = languageFromExtension(sourceFile);
}
- else
+
+ switch(source_language)
{
+ case Language::C:
+ base += "_c";
+ break;
+ case Language::Cpp:
base += "_cc";
+ break;
+ case Language::Asm:
+ base += "_asm";
+ break;
+ case Language::Auto:
+ assert(0 && "This should never happen");
+ break;
}
- targetFile = base;
- targetFile += ".o";
- depsFile = base;
- depsFile += ".d";
- flagsFile = base;
- flagsFile += ".flags";
-
- target_type = TargetType::Object;
- if(sourceFile.extension().string() == ".c")
+ if(source.output.empty())
{
- source_language = Language::C;
+ _targetFile = base;
+ _targetFile += ".o";
}
else
{
- source_language = Language::Cpp;
+ _targetFile = source.output;
}
+ depsFile = targetFile().parent_path() / targetFile().stem();
+ depsFile += ".d";
+ flagsFile = targetFile().parent_path() / targetFile().stem();
+ flagsFile += ".flags";
+}
+
+int TaskCC::registerDepTasksInner(const std::set<std::shared_ptr<Task>>& tasks)
+{
+ for(const auto& task : tasks)
+ {
+ if(*task == _source.file)
+ {
+ dependsTasks.insert(task);
+ }
+ }
+
+ return 0;
}
std::string TaskCC::name() const
{
- return target();
+ return {};
}
bool TaskCC::dirtyInner()
@@ -141,7 +94,7 @@ bool TaskCC::dirtyInner()
return true;
}
- if(!std::filesystem::exists(targetFile))
+ if(!std::filesystem::exists(targetFile()))
{
//std::cout << "Missing targetFile\n";
return true;
@@ -179,7 +132,7 @@ bool TaskCC::dirtyInner()
for(const auto& dep : depList)
{
if(!std::filesystem::exists(dep) ||
- std::filesystem::last_write_time(targetFile) <
+ std::filesystem::last_write_time(targetFile()) <
std::filesystem::last_write_time(dep))
{
//std::cout << "The targetFile older than " << std::string(dep) << "\n";
@@ -188,7 +141,7 @@ bool TaskCC::dirtyInner()
}
if(std::filesystem::last_write_time(sourceFile) >
- std::filesystem::last_write_time(targetFile))
+ std::filesystem::last_write_time(targetFile()))
{
//std::cout << "The targetFile older than sourceFile\n";
return true;
@@ -214,9 +167,22 @@ int TaskCC::runInner()
if(settings.verbose == 0)
{
- std::cout << compiler() << " " <<
+ switch(sourceLanguage())
+ {
+ case Language::C:
+ std::cout << "CC ";
+ break;
+ case Language::Cpp:
+ std::cout << "CXX ";
+ break;
+ case Language::Auto:
+ case Language::Asm:
+ // Only c/c++ handled by this task type.
+ break;
+ }
+ std::cout <<
sourceFile.lexically_normal().string() << " => " <<
- targetFile.lexically_normal().string() << "\n";
+ targetFile().lexically_normal().string() << "\n";
}
return execute(compiler(), args, settings.verbose > 0);
@@ -224,10 +190,10 @@ int TaskCC::runInner()
int TaskCC::clean()
{
- if(std::filesystem::exists(targetFile))
+ if(std::filesystem::exists(targetFile()))
{
- std::cout << "Removing " << targetFile.string() << "\n";
- std::filesystem::remove(targetFile);
+ std::cout << "Removing " << targetFile().string() << "\n";
+ std::filesystem::remove(targetFile());
}
if(std::filesystem::exists(depsFile))
@@ -252,7 +218,17 @@ std::vector<std::string> TaskCC::depends() const
std::string TaskCC::target() const
{
- return targetFile.string();
+ return _targetFile.string();
+}
+
+std::filesystem::path TaskCC::targetFile() const
+{
+ return std::filesystem::path(settings.builddir) / _targetFile;
+}
+
+bool TaskCC::derived() const
+{
+ return true;
}
std::string TaskCC::toJSON() const
@@ -261,7 +237,7 @@ std::string TaskCC::toJSON() const
json += "\t{\n";
json += "\t\t\"directory\": \"" + sourceDir.string() + "\",\n";
json += "\t\t\"file\": \"" + sourceFile.lexically_normal().string() + "\",\n";
- json += "\t\t\"output\": \"" + targetFile.string() + "\",\n";
+ json += "\t\t\"output\": \"" + targetFile().string() + "\",\n";
json += "\t\t\"arguments\": [ \"" + compiler() + "\"";
auto args = getCompilerArgs();
for(const auto& arg : args)
@@ -273,14 +249,19 @@ std::string TaskCC::toJSON() const
return json;
}
+std::string TaskCC::source() const
+{
+ return sourceFile.string();
+}
+
std::vector<std::string> TaskCC::flags() const
{
switch(sourceLanguage())
{
case Language::C:
- return config.cflags;
+ return config.flags.cflags;
case Language::Cpp:
- return config.cxxflags;
+ return config.flags.cxxflags;
default:
std::cerr << "Unknown CC target type\n";
exit(1);
@@ -300,39 +281,41 @@ std::string TaskCC::flagsString() const
std::vector<std::string> TaskCC::getCompilerArgs() const
{
+ auto tool_chain = getToolChain(config.system);
+
auto compiler_flags = flags();
std::vector<std::string> args;
- args.push_back("-MMD");
+ append(args, getOption(tool_chain, opt::generate_dep_tree));
if(std::filesystem::path(config.target).extension() == ".so")
{
// Add -fPIC arg to all contained object files
- args.push_back("-fPIC");
+ append(args, getOption(tool_chain, opt::position_independent_code));
}
- args.push_back("-c");
+ append(args, getOption(tool_chain, opt::no_link));
args.push_back(sourceFile.string());
- args.push_back("-o");
- args.push_back(targetFile.string());
+ append(args, getOption(tool_chain, opt::output, targetFile().string()));
for(const auto& flag : compiler_flags)
{
- // Is arg an added include path?
- if(flag.substr(0, 2) == "-I")
+ auto option = getOption(flag);
+ switch(option.first)
{
- std::string include_path = flag.substr(2);
- include_path.erase(0, include_path.find_first_not_of(' '));
- std::filesystem::path path(include_path);
-
- // Is it relative?
- if(path.is_relative())
+ // Relative include paths has to be altered to be relative to sourceDir
+ case opt::include_path:
{
- path = (sourceDir / path).lexically_normal();
- std::string new_include_path = "-I" + path.string();
- args.push_back(new_include_path);
- continue;
+ std::filesystem::path path(option.second);
+ if(path.is_relative())
+ {
+ path = (sourceDir / path).lexically_normal();
+ append(args, getOption(tool_chain, opt::include_path, path.string()));
+ }
}
+ continue;
+ default:
+ break;
}
args.push_back(flag);
diff --git a/src/task_cc.h b/src/task_cc.h
index 10813a7..0a0d96a 100644
--- a/src/task_cc.h
+++ b/src/task_cc.h
@@ -19,7 +19,10 @@ class TaskCC
public:
TaskCC(const BuildConfiguration& config,
const Settings& settings,
- const std::string& sourceDir, const std::string& source);
+ const std::string& sourceDir, const Source& source);
+ virtual ~TaskCC() = default;
+
+ int registerDepTasksInner(const std::set<std::shared_ptr<Task>>& tasks) override;
std::string name() const override;
bool dirtyInner() override;
@@ -30,20 +33,26 @@ public:
std::vector<std::string> depends() const override;
std::string target() const override;
+ std::filesystem::path targetFile() const override;
+
+ bool derived() const override;
std::string toJSON() const override;
-private:
+ std::string source() const override;
+
+protected:
std::vector<std::string> flags() const;
std::string flagsString() const;
std::vector<std::string> getCompilerArgs() const;
std::filesystem::path sourceFile;
- std::filesystem::path targetFile;
+ std::filesystem::path _targetFile;
std::filesystem::path depsFile;
std::filesystem::path flagsFile;
const BuildConfiguration& config;
const Settings& settings;
std::filesystem::path sourceDir;
+ const Source& _source;
};
diff --git a/src/task_fn.cc b/src/task_fn.cc
new file mode 100644
index 0000000..ab00fae
--- /dev/null
+++ b/src/task_fn.cc
@@ -0,0 +1,121 @@
+// -*- c++ -*-
+// Distributed under the BSD 2-Clause License.
+// See accompanying file LICENSE for details.
+#include "task_fn.h"
+
+#include <iostream>
+#include <fstream>
+#include <cassert>
+
+#include "libctor.h"
+#include "execute.h"
+#include "util.h"
+
+TaskFn::TaskFn(const BuildConfiguration& config, const Settings& settings,
+ const std::string& sourceDir, const Source& source)
+ : Task(config, settings, sourceDir)
+ , config(config)
+ , settings(settings)
+{
+ sourceFile = sourceDir;
+ sourceFile /= source.file;
+
+ std::filesystem::create_directories(std::filesystem::path(settings.builddir) / sourceFile.parent_path());
+
+ target_type = config.type;
+ source_language = source.language;
+
+ if(source.output.empty())
+ {
+ std::cerr << "Missing output file for functional target\n";
+ exit(1);
+ }
+
+ _targetFile = source.output;
+}
+
+bool TaskFn::dirtyInner()
+{
+ if(!std::filesystem::exists(sourceFile))
+ {
+ //std::cout << "Missing source file: " << std::string(sourceFile) << "\n";
+ return true;
+ }
+
+ if(!std::filesystem::exists(targetFile()))
+ {
+ //std::cout << "Missing targetFile\n";
+ return true;
+ }
+
+ if(std::filesystem::last_write_time(sourceFile) >
+ std::filesystem::last_write_time(targetFile()))
+ {
+ //std::cout << "The targetFile older than sourceFile\n";
+ return true;
+ }
+
+ return false;
+}
+
+int TaskFn::runInner()
+{
+ if(!std::filesystem::exists(sourceFile))
+ {
+ std::cout << "Missing source file: " << sourceFile.string() << "\n";
+ return 1;
+ }
+
+ if(settings.verbose >= 0)
+ {
+ std::cout << "Fn" << " " <<
+ sourceFile.lexically_normal().string() << " => " <<
+ targetFile().lexically_normal().string() << "\n";
+ }
+
+ return config.function(sourceFile.string(),
+ targetFile().string(),
+ config,
+ settings);
+}
+
+int TaskFn::clean()
+{
+ if(std::filesystem::exists(targetFile()))
+ {
+ std::cout << "Removing " << targetFile().string() << "\n";
+ std::filesystem::remove(targetFile());
+ }
+
+ return 0;
+}
+
+std::vector<std::string> TaskFn::depends() const
+{
+ return {};
+}
+
+std::string TaskFn::target() const
+{
+ return _targetFile;
+}
+
+std::filesystem::path TaskFn::targetFile() const
+{
+ return std::filesystem::path(settings.builddir) / sourceDir / _targetFile;
+}
+
+bool TaskFn::derived() const
+{
+ return false;
+}
+
+std::string TaskFn::toJSON() const
+{
+ return {}; // TODO: Not sure how to express this...
+}
+
+std::string TaskFn::source() const
+{
+ return sourceFile.string();
+}
diff --git a/src/task_fn.h b/src/task_fn.h
new file mode 100644
index 0000000..e350395
--- /dev/null
+++ b/src/task_fn.h
@@ -0,0 +1,47 @@
+// -*- c++ -*-
+// Distributed under the BSD 2-Clause License.
+// See accompanying file LICENSE for details.
+#pragma once
+
+#include "task.h"
+
+#include <vector>
+#include <string>
+#include <future>
+#include <filesystem>
+
+struct BuildConfiguration;
+struct Settings;
+
+class TaskFn
+ : public Task
+{
+public:
+ TaskFn(const BuildConfiguration& config,
+ const Settings& settings,
+ const std::string& sourceDir, const Source& source);
+ virtual ~TaskFn() = default;
+
+ bool dirtyInner() override;
+
+ int runInner() override;
+ int clean() override;
+
+ std::vector<std::string> depends() const override;
+
+ std::string target() const override;
+ std::filesystem::path targetFile() const override;
+ bool derived() const override;
+
+ std::string toJSON() const override;
+
+ std::string source() const override;
+
+protected:
+ std::filesystem::path sourceFile;
+ std::filesystem::path _targetFile;
+
+ const BuildConfiguration& config;
+ const Settings& settings;
+ std::filesystem::path sourceDir;
+};
diff --git a/src/task_ld.cc b/src/task_ld.cc
index 1b5665e..20e823d 100644
--- a/src/task_ld.cc
+++ b/src/task_ld.cc
@@ -7,32 +7,19 @@
#include <fstream>
#include "libctor.h"
-#include "settings.h"
#include "execute.h"
-
-namespace
-{
-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();
- ifs.seekg(0, std::ios::beg);
-
- std::vector<char> bytes(fileSize);
- ifs.read(bytes.data(), fileSize);
-
- return std::string(bytes.data(), fileSize);
-}
-} // namespace ::
+#include "util.h"
+#include "tools.h"
TaskLD::TaskLD(const BuildConfiguration& config,
const Settings& settings,
const std::string& target,
- const std::vector<std::string>& objects)
- : Task(config)
+ const std::vector<std::string>& objects,
+ const std::string& sourceDir)
+ : Task(config, settings, sourceDir)
, config(config)
, settings(settings)
+ , sourceDir(sourceDir)
{
target_type = config.type;
if(target_type == TargetType::Auto)
@@ -40,8 +27,9 @@ TaskLD::TaskLD(const BuildConfiguration& config,
target_type = TargetType::Executable;
}
- targetFile = settings.builddir;
- targetFile /= target;
+ std::filesystem::create_directories(std::filesystem::path(settings.builddir) / sourceDir);
+
+ _targetFile = target;
for(const auto& object : objects)
{
std::filesystem::path objectFile = object;
@@ -51,18 +39,16 @@ TaskLD::TaskLD(const BuildConfiguration& config,
for(const auto& dep : config.depends)
{
- std::filesystem::path depFile = settings.builddir;
- depFile /= dep;
- depFiles.push_back(depFile);
+ depFiles.push_back(dep);
}
- flagsFile = settings.builddir / targetFile.stem();
+ flagsFile = std::filesystem::path(settings.builddir) / cleanUp(sourceDir) / targetFile().stem();
flagsFile += ".flags";
source_language = Language::C;
for(const auto& source : config.sources)
{
- std::filesystem::path sourceFile(source);
+ std::filesystem::path sourceFile(source.file);
if(sourceFile.extension().string() != ".c")
{
source_language = Language::Cpp;
@@ -72,7 +58,7 @@ TaskLD::TaskLD(const BuildConfiguration& config,
bool TaskLD::dirtyInner()
{
- if(!std::filesystem::exists(targetFile))
+ if(!std::filesystem::exists(targetFile()))
{
return true;
}
@@ -82,15 +68,6 @@ bool TaskLD::dirtyInner()
return true;
}
- for(const auto& objectFile : objectFiles)
- {
- if(std::filesystem::last_write_time(targetFile) <=
- std::filesystem::last_write_time(objectFile))
- {
- return true;
- }
- }
-
{
auto lastFlags = readFile(flagsFile.string());
if(flagsString() != lastFlags)
@@ -105,42 +82,27 @@ bool TaskLD::dirtyInner()
int TaskLD::runInner()
{
- std::string objectlist;
- for(const auto& objectFile : objectFiles)
- {
- if(!objectlist.empty())
- {
- objectlist += " ";
- }
- objectlist += objectFile.string();
- }
+ auto tool_chain = getToolChain(config.system);
std::vector<std::string> args;
- for(const auto& objectFile : objectFiles)
- {
- args.push_back(objectFile.string());
- }
-
- for(const auto& depFile : depFiles)
+ for(const auto& dep : getDependsTasks())
{
+ auto depFile = dep->targetFile();
if(depFile.extension() == ".so")
{
- args.push_back(std::string("-L") + settings.builddir);
+ append(args, getOption(tool_chain, opt::library_path,
+ targetFile().parent_path().string()));
auto lib = depFile.stem().string().substr(3); // strip 'lib' prefix
- args.push_back(std::string("-l") + lib);
+ append(args, getOption(tool_chain, opt::link, lib));
}
- else if(depFile.extension() == ".a")
+ else if(depFile.extension() == ".a" || depFile.extension() == ".o")
{
args.push_back(depFile.string());
}
}
- for(const auto& flag : config.ldflags)
- {
- args.push_back(flag);
- }
- args.push_back("-o");
- args.push_back(targetFile.string());
+ append(args, config.flags.ldflags);
+ append(args, getOption(tool_chain, opt::output, targetFile().string()));
{ // Write flags to file.
std::ofstream flagsStream(flagsFile);
@@ -149,7 +111,7 @@ int TaskLD::runInner()
if(settings.verbose == 0)
{
- std::cout << "LD => " << targetFile.string() << "\n";
+ std::cout << "LD => " << targetFile().string() << "\n";
}
auto tool = compiler();
@@ -158,10 +120,10 @@ int TaskLD::runInner()
int TaskLD::clean()
{
- if(std::filesystem::exists(targetFile))
+ if(std::filesystem::exists(targetFile()))
{
- std::cout << "Removing " << targetFile.string() << "\n";
- std::filesystem::remove(targetFile);
+ std::cout << "Removing " << targetFile().string() << "\n";
+ std::filesystem::remove(targetFile());
}
if(std::filesystem::exists(flagsFile))
@@ -191,15 +153,25 @@ std::vector<std::string> TaskLD::depends() const
std::string TaskLD::target() const
{
- return targetFile.string();
+ return _targetFile.string();
+}
+
+std::filesystem::path TaskLD::targetFile() const
+{
+ return std::filesystem::path(settings.builddir) / sourceDir / _targetFile;
+}
+
+bool TaskLD::derived() const
+{
+ return false;
}
std::string TaskLD::flagsString() const
{
std::string flagsStr;
- for(const auto& flag : config.ldflags)
+ for(const auto& flag : config.flags.ldflags)
{
- if(flag != config.ldflags[0])
+ if(flag != config.flags.ldflags[0])
{
flagsStr += " ";
}
@@ -216,5 +188,12 @@ std::string TaskLD::flagsString() const
flagsStr += dep;
}
+ auto deps = depends();
+ for(const auto& dep : deps)
+ {
+ flagsStr += " ";
+ flagsStr += dep;
+ }
+
return flagsStr;
}
diff --git a/src/task_ld.h b/src/task_ld.h
index 730975a..8625075 100644
--- a/src/task_ld.h
+++ b/src/task_ld.h
@@ -20,7 +20,9 @@ public:
TaskLD(const BuildConfiguration& config,
const Settings& settings,
const std::string& target,
- const std::vector<std::string>& objects);
+ const std::vector<std::string>& objects,
+ const std::string& _sourceDir);
+ virtual ~TaskLD() = default;
bool dirtyInner() override;
@@ -30,15 +32,19 @@ public:
std::vector<std::string> depends() const override;
std::string target() const override;
+ std::filesystem::path targetFile() const override;
+
+ bool derived() const override;
private:
std::string flagsString() const;
std::vector<std::filesystem::path> objectFiles;
std::vector<std::filesystem::path> depFiles;
- std::filesystem::path targetFile;
+ std::filesystem::path _targetFile;
std::filesystem::path flagsFile;
const BuildConfiguration& config;
const Settings& settings;
+ std::string sourceDir;
};
diff --git a/src/task_so.cc b/src/task_so.cc
index 5623bcf..8c6dbd4 100644
--- a/src/task_so.cc
+++ b/src/task_so.cc
@@ -7,35 +7,24 @@
#include <fstream>
#include "libctor.h"
-#include "settings.h"
#include "execute.h"
-
-namespace
-{
-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();
- ifs.seekg(0, std::ios::beg);
-
- std::vector<char> bytes(fileSize);
- ifs.read(bytes.data(), fileSize);
-
- return std::string(bytes.data(), fileSize);
-}
-} // namespace ::
+#include "util.h"
+#include "tools.h"
TaskSO::TaskSO(const BuildConfiguration& config,
const Settings& settings,
const std::string& target,
- const std::vector<std::string>& objects)
- : Task(config)
+ const std::vector<std::string>& objects,
+ const std::string& sourceDir)
+ : Task(config, settings, sourceDir)
, config(config)
, settings(settings)
+ , sourceDir(sourceDir)
{
- targetFile = settings.builddir;
- targetFile /= target;
+ std::filesystem::path base = sourceDir;
+ std::filesystem::create_directories(std::filesystem::path(settings.builddir) / base);
+
+ _targetFile = base / target;
for(const auto& object : objects)
{
std::filesystem::path objectFile = object;
@@ -45,19 +34,18 @@ TaskSO::TaskSO(const BuildConfiguration& config,
for(const auto& dep : config.depends)
{
- std::filesystem::path depFile = settings.builddir;
- depFile /= dep;
- depFiles.push_back(depFile);
+ depFiles.push_back(dep);
}
- flagsFile = settings.builddir / targetFile.stem();
+ flagsFile = std::filesystem::path(settings.builddir) / cleanUp(sourceDir) / targetFile().stem();
flagsFile += ".flags";
target_type = TargetType::DynamicLibrary;
source_language = Language::C;
for(const auto& source : config.sources)
{
- std::filesystem::path sourceFile(source);
+ std::filesystem::path sourceFile(source.file);
+ // TODO: Use task languages instead
if(sourceFile.extension().string() != ".c")
{
source_language = Language::Cpp;
@@ -67,7 +55,7 @@ TaskSO::TaskSO(const BuildConfiguration& config,
bool TaskSO::dirtyInner()
{
- if(!std::filesystem::exists(targetFile))
+ if(!std::filesystem::exists(targetFile()))
{
return true;
}
@@ -77,15 +65,6 @@ bool TaskSO::dirtyInner()
return true;
}
- for(const auto& objectFile : objectFiles)
- {
- if(std::filesystem::last_write_time(targetFile) <=
- std::filesystem::last_write_time(objectFile))
- {
- return true;
- }
- }
-
{
auto lastFlags = readFile(flagsFile.string());
if(flagsString() != lastFlags)
@@ -100,38 +79,21 @@ bool TaskSO::dirtyInner()
int TaskSO::runInner()
{
- std::string objectlist;
- for(const auto& objectFile : objectFiles)
- {
- if(!objectlist.empty())
- {
- objectlist += " ";
- }
- objectlist += objectFile.string();
- }
+ auto tool_chain = getToolChain(config.system);
std::vector<std::string> args;
- args.push_back("-fPIC");
- args.push_back("-shared");
-
- args.push_back("-o");
- args.push_back(targetFile.string());
+ append(args, getOption(tool_chain, opt::position_independent_code));
+ append(args, getOption(tool_chain, opt::build_shared));
- for(const auto& objectFile : objectFiles)
- {
- args.push_back(objectFile.string());
- }
+ append(args, getOption(tool_chain, opt::output, targetFile().string()));
- for(const auto& depFile : depFiles)
+ for(const auto& task : getDependsTasks())
{
- args.push_back(depFile.string());
+ args.push_back(task->targetFile().string());
}
- for(const auto& flag : config.ldflags)
- {
- args.push_back(flag);
- }
+ append(args, config.flags.ldflags);
{ // Write flags to file.
std::ofstream flagsStream(flagsFile);
@@ -140,7 +102,7 @@ int TaskSO::runInner()
if(settings.verbose == 0)
{
- std::cout << "LD => " << targetFile.string() << "\n";
+ std::cout << "LD => " << targetFile().string() << "\n";
}
auto tool = compiler();
@@ -149,10 +111,10 @@ int TaskSO::runInner()
int TaskSO::clean()
{
- if(std::filesystem::exists(targetFile))
+ if(std::filesystem::exists(targetFile()))
{
- std::cout << "Removing " << targetFile.string() << "\n";
- std::filesystem::remove(targetFile);
+ std::cout << "Removing " << targetFile().string() << "\n";
+ std::filesystem::remove(targetFile());
}
if(std::filesystem::exists(flagsFile))
@@ -182,13 +144,23 @@ std::vector<std::string> TaskSO::depends() const
std::string TaskSO::target() const
{
- return targetFile.string();
+ return _targetFile.string();
+}
+
+std::filesystem::path TaskSO::targetFile() const
+{
+ return std::filesystem::path(settings.builddir) / sourceDir / _targetFile;
+}
+
+bool TaskSO::derived() const
+{
+ return false;
}
std::string TaskSO::flagsString() const
{
std::string flagsStr = compiler();
- for(const auto& flag : config.ldflags)
+ for(const auto& flag : config.flags.ldflags)
{
flagsStr += " " + flag;
}
@@ -203,5 +175,12 @@ std::string TaskSO::flagsString() const
flagsStr += dep;
}
+ auto deps = depends();
+ for(const auto& dep : deps)
+ {
+ flagsStr += " ";
+ flagsStr += dep;
+ }
+
return flagsStr;
}
diff --git a/src/task_so.h b/src/task_so.h
index 1e65694..fe5d2fd 100644
--- a/src/task_so.h
+++ b/src/task_so.h
@@ -20,7 +20,9 @@ public:
TaskSO(const BuildConfiguration& config,
const Settings& settings,
const std::string& target,
- const std::vector<std::string>& objects);
+ const std::vector<std::string>& objects,
+ const std::string& sourceDir);
+ virtual ~TaskSO() = default;
bool dirtyInner() override;
@@ -30,15 +32,19 @@ public:
std::vector<std::string> depends() const override;
std::string target() const override;
+ std::filesystem::path targetFile() const override;
+
+ bool derived() const override;
private:
std::string flagsString() const;
std::vector<std::filesystem::path> objectFiles;
std::vector<std::filesystem::path> depFiles;
- std::filesystem::path targetFile;
+ std::filesystem::path _targetFile;
std::filesystem::path flagsFile;
const BuildConfiguration& config;
const Settings& settings;
+ std::string sourceDir;
};
diff --git a/src/tasks.cc b/src/tasks.cc
index a52b0be..68b2476 100644
--- a/src/tasks.cc
+++ b/src/tasks.cc
@@ -5,24 +5,28 @@
#include <filesystem>
#include <deque>
+#include <list>
#include <iostream>
#include <algorithm>
-#include "settings.h"
#include "libctor.h"
#include "task.h"
#include "task_cc.h"
#include "task_ld.h"
#include "task_ar.h"
#include "task_so.h"
+#include "task_fn.h"
#include "rebuild.h"
+#include "configure.h"
-const std::deque<Target>& getTargets(const Settings& settings)
+const std::deque<Target>& getTargets(const Settings& settings,
+ bool resolve_externals)
{
static bool initialised{false};
static std::deque<Target> targets;
if(!initialised)
{
+ const auto& externals = configuration().externals;
for(std::size_t i = 0; i < numConfigFiles; ++i)
{
std::string path =
@@ -31,9 +35,39 @@ const std::deque<Target>& getTargets(const Settings& settings)
{
std::cout << configFiles[i].file << " in path " << path << "\n";
}
- auto configs = configFiles[i].cb();
- for(const auto& config : configs)
+ auto configs = configFiles[i].cb(settings);
+ for(auto& config : configs)
{
+ if(resolve_externals)
+ {
+ // Resolv config externals
+ for(const auto& external : config.externals)
+ {
+ if(externals.find(external) == externals.end())
+ {
+ std::cout << "External '" << external <<
+ "' not found in cache - run configure.\n";
+ exit(1);
+ }
+ const auto& flags = externals.at(external);
+ config.flags.cflags.insert(config.flags.cflags.end(),
+ flags.cflags.begin(),
+ flags.cflags.end());
+ config.flags.cxxflags.insert(config.flags.cxxflags.end(),
+ flags.cxxflags.begin(),
+ flags.cxxflags.end());
+ config.flags.ldflags.insert(config.flags.ldflags.end(),
+ flags.ldflags.begin(),
+ flags.ldflags.end());
+ config.flags.asmflags.insert(config.flags.asmflags.end(),
+ flags.asmflags.begin(),
+ flags.asmflags.end());
+ //config.libs.insert(config.libs.end(),
+ // libs.begin(),
+ // libs.end());
+ }
+ }
+
targets.push_back({config, path});
}
}
@@ -43,18 +77,22 @@ const std::deque<Target>& getTargets(const Settings& settings)
return targets;
}
-namespace
-{
-std::list<std::shared_ptr<Task>> taskFactory(const BuildConfiguration& config,
- const Settings& settings,
- const std::string& sourceDir)
+std::set<std::shared_ptr<Task>> taskFactory(const BuildConfiguration& config,
+ const Settings& settings,
+ const std::string& sourceDir)
{
+ std::set<std::shared_ptr<Task>> tasks;
+
std::filesystem::path targetFile(config.target);
TargetType target_type{config.type};
if(target_type == TargetType::Auto)
{
- if(targetFile.extension() == ".a")
+ if(config.function != nullptr)
+ {
+ target_type = TargetType::Function;
+ }
+ else if(targetFile.extension() == ".a")
{
target_type = TargetType::StaticLibrary;
}
@@ -75,13 +113,26 @@ std::list<std::shared_ptr<Task>> taskFactory(const BuildConfiguration& config,
}
std::vector<std::string> objects;
- std::list<std::shared_ptr<Task>> tasks;
- for(const auto& file : config.sources)
+ if(target_type != TargetType::Function)
+ {
+ for(const auto& file : config.sources)
+ {
+ auto task = std::make_shared<TaskCC>(config, settings, sourceDir, file);
+ tasks.insert(task);
+ objects.push_back(task->targetFile().string());
+ }
+ }
+#ifndef BOOTSTRAP
+ else
{
- tasks.emplace_back(std::make_shared<TaskCC>(config, settings,
- sourceDir, file));
- objects.push_back(tasks.back()->target());
+ for(const auto& file : config.sources)
+ {
+ auto task = std::make_shared<TaskFn>(config, settings, sourceDir, file);
+ tasks.insert(task);
+ objects.push_back(task->target());
+ }
}
+#endif
switch(target_type)
{
@@ -90,36 +141,42 @@ std::list<std::shared_ptr<Task>> taskFactory(const BuildConfiguration& config,
break;
case TargetType::StaticLibrary:
- tasks.emplace_back(std::make_shared<TaskAR>(config, settings, config.target,
- objects));
+ case TargetType::UnitTestLib:
+ tasks.insert(std::make_shared<TaskAR>(config, settings, config.target,
+ objects, sourceDir));
break;
-
+#ifndef BOOTSTRAP
case TargetType::DynamicLibrary:
+ // 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.emplace_back(std::make_shared<TaskSO>(config, settings, config.target,
- objects));
+ tasks.insert(std::make_shared<TaskSO>(config, settings, config.target,
+ objects, sourceDir));
break;
case TargetType::Executable:
case TargetType::UnitTest:
- tasks.emplace_back(std::make_shared<TaskLD>(config, settings, config.target,
- objects));
+ tasks.insert(std::make_shared<TaskLD>(config, settings, config.target,
+ objects, sourceDir));
break;
case TargetType::Object:
+ case TargetType::Function:
+ break;
+#else
+ default:
break;
+#endif
}
return tasks;
}
-}
-std::shared_ptr<Task> getNextTask(const std::list<std::shared_ptr<Task>>& allTasks,
- std::list<std::shared_ptr<Task>>& dirtyTasks)
+std::shared_ptr<Task> getNextTask(const std::set<std::shared_ptr<Task>>& allTasks,
+ std::set<std::shared_ptr<Task>>& dirtyTasks)
{
for(auto dirtyTask = dirtyTasks.begin();
dirtyTask != dirtyTasks.end();
@@ -137,11 +194,12 @@ std::shared_ptr<Task> getNextTask(const std::list<std::shared_ptr<Task>>& allTas
return nullptr;
}
-std::list<std::shared_ptr<Task>> getTasks(const Settings& settings,
- const std::vector<std::string> names)
+std::set<std::shared_ptr<Task>> getTasks(const Settings& settings,
+ const std::vector<std::string> names,
+ bool resolve_externals)
{
- auto& targets = getTargets(settings);
- std::list<std::shared_ptr<Task>> tasks;
+ auto& targets = getTargets(settings, resolve_externals);
+ std::set<std::shared_ptr<Task>> tasks;
for(const auto& target : targets)
{
if(names.empty() ||
@@ -149,7 +207,7 @@ std::list<std::shared_ptr<Task>> getTasks(const Settings& settings,
{
std::vector<std::string> objects;
auto t = taskFactory(target.config, settings, target.path);
- tasks.insert(tasks.end(), t.begin(), t.end());
+ tasks.insert(t.begin(), t.end());
}
}
diff --git a/src/tasks.h b/src/tasks.h
index 54591a4..c547432 100644
--- a/src/tasks.h
+++ b/src/tasks.h
@@ -4,7 +4,7 @@
#pragma once
#include <string>
-#include <list>
+#include <set>
#include <memory>
#include <deque>
@@ -20,16 +20,24 @@ struct Target
};
//! Get list of all registered targets
-const std::deque<Target>& getTargets(const Settings& settings);
+const std::deque<Target>& getTargets(const Settings& settings,
+ bool resolve_externals = true);
//! Returns next dirty task from the dirtyTasks list that has all its dependencies
//! 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::list<std::shared_ptr<Task>>& allTasks,
- std::list<std::shared_ptr<Task>>& dirtyTasks);
+std::shared_ptr<Task> getNextTask(const std::set<std::shared_ptr<Task>>& allTasks,
+ std::set<std::shared_ptr<Task>>& dirtyTasks);
//! Get list of tasks filtered by name including each of their direct
//! dependency tasks (ie. objects tasks from their sources).
-std::list<std::shared_ptr<Task>> getTasks(const Settings& settings,
- const std::vector<std::string> names = {});
+std::set<std::shared_ptr<Task>> getTasks(const Settings& settings,
+ const std::vector<std::string> names = {},
+ bool resolve_externals = true);
+
+//! Generate list of targets from a single configuration, including the final
+//! link target and all its objects files (if any).
+std::set<std::shared_ptr<Task>> taskFactory(const BuildConfiguration& config,
+ const Settings& settings,
+ const std::string& sourceDir);
diff --git a/src/tools.cc b/src/tools.cc
new file mode 100644
index 0000000..7e8ac78
--- /dev/null
+++ b/src/tools.cc
@@ -0,0 +1,136 @@
+// -*- c++ -*-
+// Distributed under the BSD 2-Clause License.
+// See accompanying file LICENSE for details.
+#include "tools.h"
+
+#include <filesystem>
+#include <iostream>
+
+#include <cassert>
+
+ToolChain getToolChain(OutputSystem system)
+{
+ std::string compiler;
+ switch(system)
+ {
+ case OutputSystem::Host:
+ compiler = getConfiguration(cfg::host_cxx, "g++");
+ break;
+ case OutputSystem::Build:
+ compiler = getConfiguration(cfg::build_cxx, "g++");
+ break;
+ }
+
+ std::filesystem::path cc(compiler);
+ auto cc_cmd = cc.stem().string();
+
+ // Note: "g++" is a substring of "clang++" so "clang++" must be tested first.
+ if(cc_cmd.find("clang++") != std::string::npos)
+ {
+ return ToolChain::clang;
+ }
+ else if(cc_cmd.find("g++") != std::string::npos)
+ {
+ return ToolChain::gcc;
+ }
+
+ std::cerr << "Unsupported output system.\n";
+ return ToolChain::gcc;
+}
+
+namespace
+{
+std::vector<std::string> getOptionGcc(opt option, const std::string& arg)
+{
+ switch(option)
+ {
+ case opt::output:
+ return {"-o", arg};
+ case opt::debug:
+ return {"-g"};
+ case opt::strip:
+ return {"-s"};
+ case opt::warn_all:
+ return {"-Wall"};
+ case opt::warnings_as_errors:
+ return {"-Werror"};
+ case opt::generate_dep_tree:
+ return {"-MMD"};
+ case opt::no_link:
+ return {"-c"};
+ case opt::include_path:
+ return {"-I" + arg};
+ case opt::library_path:
+ return {"-L" + arg};
+ case opt::link:
+ return {"-l" + arg};
+ case opt::cpp_std:
+ return {"-std=" + arg};
+ case opt::build_shared:
+ return {"-shared"};
+ case opt::threads:
+ return {"-pthread"};
+ case opt::optimization:
+ return {"-O" + arg};
+ case opt::position_independent_code:
+ return {"-fPIC"};
+ case opt::position_independent_executable:
+ return {"-fPIE"};
+ case opt::custom:
+ return {arg};
+ }
+
+ std::cerr << "Unsupported compiler option.\n";
+ return {};
+}
+}
+
+std::vector<std::string> getOption(ToolChain tool_chain,
+ opt option,
+ const std::string& arg)
+{
+ switch(tool_chain)
+ {
+ case ToolChain::gcc:
+ case ToolChain::clang:
+ return getOptionGcc(option, arg);
+ }
+
+ std::cerr << "Unsupported tool-chain.\n";
+ return {};
+}
+
+namespace {
+std::pair<opt, std::string> getOptionGcc(const std::string& flag)
+{
+ if(flag.substr(0, 2) == "-I")
+ {
+ std::string path = flag.substr(2);
+ path.erase(0, path.find_first_not_of(' '));
+ return { opt::include_path, path };
+ }
+
+ if(flag.substr(0, 2) == "-L")
+ {
+ std::string path = flag.substr(2);
+ path.erase(0, path.find_first_not_of(' '));
+ return { opt::library_path, path };
+ }
+
+ return { opt::custom, flag };
+}
+}
+
+std::pair<opt, std::string> getOption(const std::string& flag,
+ ToolChain tool_chain)
+{
+ switch(tool_chain)
+ {
+ case ToolChain::gcc:
+ case ToolChain::clang:
+ return getOptionGcc(flag);
+ }
+
+ std::cerr << "Unsupported tool-chain.\n";
+ return { opt::custom, flag };
+}
diff --git a/src/tools.h b/src/tools.h
new file mode 100644
index 0000000..39118d2
--- /dev/null
+++ b/src/tools.h
@@ -0,0 +1,52 @@
+// -*- c++ -*-
+// Distributed under the BSD 2-Clause License.
+// See accompanying file LICENSE for details.
+#pragma once
+
+#include <vector>
+#include <string>
+
+#include "libctor.h"
+
+enum class ToolChain
+{
+ gcc,
+ clang,
+};
+
+enum class opt
+{
+ // gcc/clang
+ output, // -o
+ debug, // -g
+ strip, // -s
+ warn_all, // -Wall
+ warnings_as_errors, // -Werror
+ generate_dep_tree, // -MMD
+ no_link, // -c
+ include_path, // -I<arg>
+ library_path, // -L<arg>
+ link, // -l<arg>
+ cpp_std, // -std=<arg>
+ build_shared, // -shared
+ threads, // -pthread
+ optimization, // -O<arg>
+ position_independent_code, // -fPIC
+ position_independent_executable, // -fPIE
+ custom, // entire option taken verbatim from <arg>
+};
+
+//! Get tool-chain type from output system (via configuration)
+ToolChain getToolChain(OutputSystem system);
+
+//! Get tool argument(s) for specific option type matching the supplied
+//! tool-chain
+std::vector<std::string> getOption(ToolChain tool_chain,
+ opt option,
+ const std::string& arg = {});
+
+//! Get opt enum value and argument from string,
+//! ie. { opt::InludePath, "foo/bar" } from "-Ifoo/bar"
+//! Returns { opt::Custom, flag } if unknown.
+std::pair<opt, std::string> getOption(const std::string& flag,
+ ToolChain tool_chain = ToolChain::gcc);
diff --git a/src/unittest.cc b/src/unittest.cc
index ade2d0a..f18de47 100644
--- a/src/unittest.cc
+++ b/src/unittest.cc
@@ -6,10 +6,9 @@
#include <iostream>
#include "execute.h"
-#include "settings.h"
#include "task.h"
-int runUnitTests(std::list<std::shared_ptr<Task>>& tasks,
+int runUnitTests(std::set<std::shared_ptr<Task>>& tasks,
const Settings& settings)
{
bool ok{true};
@@ -19,16 +18,21 @@ int runUnitTests(std::list<std::shared_ptr<Task>>& tasks,
{
if(task->targetType() == TargetType::UnitTest)
{
- std::cout << task->name() << ": ";
- auto ret = execute(task->target(), {}, false);
+ auto name = task->name();
+ if(name.empty())
+ {
+ name = task->target();
+ }
+ std::cout << name << ": " << std::flush;
+ auto ret = execute(task->targetFile(), {}, settings.verbose > 0);
ok &= ret == 0;
if(ret == 0)
{
- std::cout << "OK\n";
+ std::cout << " OK\n";
}
else
{
- std::cout << "FAILED\n";
+ std::cout << " FAILED\n";
}
}
}
diff --git a/src/unittest.h b/src/unittest.h
index 7eef0e2..8dee33c 100644
--- a/src/unittest.h
+++ b/src/unittest.h
@@ -3,11 +3,11 @@
// See accompanying file LICENSE for details.
#pragma once
-#include <list>
+#include <set>
#include <memory>
class Task;
class Settings;
-int runUnitTests(std::list<std::shared_ptr<Task>>& tasks,
+int runUnitTests(std::set<std::shared_ptr<Task>>& tasks,
const Settings& settings);
diff --git a/src/util.cc b/src/util.cc
new file mode 100644
index 0000000..92560b6
--- /dev/null
+++ b/src/util.cc
@@ -0,0 +1,137 @@
+// -*- c++ -*-
+// Distributed under the BSD 2-Clause License.
+// See accompanying file LICENSE for details.
+#include "util.h"
+
+#include <iostream>
+#include <fstream>
+
+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();
+ ifs.seekg(0, std::ios::beg);
+
+ std::vector<char> bytes(fileSize);
+ ifs.read(bytes.data(), fileSize);
+
+ return std::string(bytes.data(), fileSize);
+}
+
+std::vector<std::string> readDeps(const std::string& depFile)
+{
+ if(!std::filesystem::exists(depFile))
+ {
+ return {};
+ }
+
+ auto str = readFile(depFile);
+
+ std::vector<std::string> output;
+ std::string tmp;
+ bool start{false};
+ bool in_whitespace{false};
+ for(const auto& c : str)
+ {
+ if(c == '\\' || c == '\n')
+ {
+ continue;
+ }
+
+ if(c == ':')
+ {
+ start = true;
+ continue;
+ }
+
+ if(!start)
+ {
+ continue;
+ }
+
+ if(c == ' ' || c == '\t')
+ {
+ if(in_whitespace)
+ {
+ continue;
+ }
+
+ if(!tmp.empty())
+ {
+ output.push_back(tmp);
+ }
+ tmp.clear();
+ in_whitespace = true;
+ }
+ else
+ {
+ in_whitespace = false;
+ tmp += c;
+ }
+ }
+
+ if(!tmp.empty())
+ {
+ output.push_back(tmp);
+ }
+
+ return output;
+}
+
+Language languageFromExtension(const std::filesystem::path& file)
+{
+ auto ext = file.extension().string();
+ if(ext == ".c")
+ {
+ return Language::C;
+ }
+
+ if(ext == ".C" ||
+ ext == ".cc" ||
+ ext == ".cpp" ||
+ ext == ".CPP" ||
+ ext == ".c++" ||
+ ext == ".cp" ||
+ ext == ".cxx")
+ {
+ return Language::Cpp;
+ }
+
+ if(ext == ".s" ||
+ ext == ".S" ||
+ ext == ".asm")
+ {
+ return Language::Asm;
+ }
+
+ std::cerr << "Could not deduce language from " << file.string() << "\n";
+ exit(1);
+ return {};
+}
+
+namespace
+{
+bool isClean(char c)
+{
+ return c != '.' && c != '/';
+}
+}
+
+std::string cleanUp(const std::string& path)
+{
+ std::string cleaned;
+ for(const auto& c : path)
+ {
+ if(isClean(c))
+ {
+ cleaned += c;
+ }
+ else
+ {
+ cleaned += '_';
+ }
+ }
+ return cleaned;
+}
diff --git a/src/util.h b/src/util.h
new file mode 100644
index 0000000..c8b591a
--- /dev/null
+++ b/src/util.h
@@ -0,0 +1,20 @@
+// -*- c++ -*-
+// Distributed under the BSD 2-Clause License.
+// See accompanying file LICENSE for details.
+#pragma once
+
+#include "libctor.h"
+
+#include <string>
+#include <filesystem>
+
+std::string readFile(const std::string& fileName);
+std::vector<std::string> readDeps(const std::string& depFile);
+Language languageFromExtension(const std::filesystem::path& file);
+std::string cleanUp(const std::string& path);
+
+template<typename T>
+void append(T& a, const T& b)
+{
+ a.insert(a.end(), b.begin(), b.end());
+}