diff options
50 files changed, 3330 insertions, 825 deletions
diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 0000000..c95b0eb --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,56 @@ +pipeline { + agent { label 'c++20' } + + stages { + stage('Clean') { + steps { + echo 'Cleaning workspace ...' + sh 'rm -Rf build*' + } + } + stage('Build-gcc') { + steps { + echo 'Building (gcc) ...' + sh 'BUILDDIR=build-gcc CXX=g++ ./bootstrap.sh' + } + } + stage('Test-gcc') { + steps { + echo 'Testing (gcc) ...' + sh './ctor check' + } + } + stage('Test-suite-gcc') { + steps { + echo 'Testing suite (gcc) ...' + sh '(cd test/suite; CTORDIR=../../build-gcc CXX=g++ ./test.sh)' + } + } + stage('Build-clang') { + steps { + echo 'Building (clang) ...' + sh 'BUILDDIR=build-clang CXX=clang++ ./bootstrap.sh' + } + } + stage('Test-clang') { + steps { + echo 'Testing (clang) ...' + sh './ctor check' + } + } + stage('Test-suite-clang') { + steps { + echo 'Testing suite (clang) ...' + sh '(cd test/suite; CTORDIR=../../build-clang CXX=clang++ ./test.sh)' + } + } + } + + post { + always { + xunit(thresholds: [ skipped(failureThreshold: '0'), + failed(failureThreshold: '0') ], + tools: [ CppUnit(pattern: 'build-*/test/*.xml') ]) + } + } +} @@ -16,7 +16,7 @@ Step 1: Create a build configuration, in C++ A really simple example ('hello_config.cc'): ```c++ -#include "libctor.h" +#include "ctor.h" namespace { diff --git a/bootstrap.sh b/bootstrap.sh index a5c11ac..76503bb 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -1,7 +1,11 @@ #!/bin/sh +: ${CXX:=g++} +: ${BUILDDIR:=build} +CXX=$(which $CXX) + echo "Bootstrapping..." -g++ -std=c++20 -Wall -O3 -Isrc -pthread src/bootstrap.cc ctor.cc test/ctor.cc -o ctor && \ +$CXX -std=c++20 -Wall -O3 -Isrc -pthread src/bootstrap.cc ctor.cc test/ctor.cc -o ctor && \ ./ctor && \ -g++ -std=c++20 -Wall -O3 -Isrc -pthread ctor.cc test/ctor.cc -Lbuild -lctor -o ctor && \ -./ctor configure --ctor-includedir=src --ctor-libdir=build && \ +$CXX -std=c++20 -Wall -O3 -Isrc -pthread ctor.cc test/ctor.cc -L$BUILDDIR -lctor -o ctor && \ +./ctor configure --ctor-includedir=src --ctor-libdir=$BUILDDIR --build-dir=$BUILDDIR&& \ echo "Done. Now run ./ctor to (re)build." @@ -1,29 +1,32 @@ // -*- c++ -*- // Distributed under the BSD 2-Clause License. // See accompanying file LICENSE for details. -#include <libctor.h> +#include <ctor.h> namespace { -BuildConfigurations ctorConfigs() +ctor::build_configurations ctorConfigs(const ctor::settings& settings) { return { { - .type = TargetType::StaticLibrary, + .type = ctor::target_type::static_library, .target = "libctor.a", .sources = { "src/build.cc", "src/configure.cc", "src/execute.cc", + "src/externals_manual.cc", "src/libctor.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", }, diff --git a/examples/cppbuild.cc b/examples/cppbuild.cc index eafac8d..9e1014f 100644 --- a/examples/cppbuild.cc +++ b/examples/cppbuild.cc @@ -1,4 +1,4 @@ -#include "libctor.h" +#include "ctor.h" namespace { diff --git a/examples/ctor.cc b/examples/ctor.cc index 1a02e90..6ed8c6c 100644 --- a/examples/ctor.cc +++ b/examples/ctor.cc @@ -1,7 +1,7 @@ // -*- c++ -*- // Distributed under the BSD 2-Clause License. // See accompanying file LICENSE for details. -#include "libctor.h" +#include "ctor.h" namespace { diff --git a/examples/subdir/ctor.cc b/examples/subdir/ctor.cc index b5f5885..2e3de9f 100644 --- a/examples/subdir/ctor.cc +++ b/examples/subdir/ctor.cc @@ -1,7 +1,7 @@ // -*- c++ -*- // Distributed under the BSD 2-Clause License. // See accompanying file LICENSE for details. -#include "../libctor.h" +#include "../ctor.h" namespace { diff --git a/src/bootstrap.cc b/src/bootstrap.cc index 08f7b1f..0604527 100644 --- a/src/bootstrap.cc +++ b/src/bootstrap.cc @@ -3,11 +3,11 @@ // See accompanying file LICENSE for details. #include <iostream> #include <array> +#include <cstdlib> #define BOOTSTRAP -#include "libctor.h" -#include "settings.h" +#include "ctor.h" #include "util.cc" #include "rebuild.cc" @@ -17,25 +17,44 @@ #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() +const ctor::configuration& ctor::get_configuration() { - return default_configuration; + static ctor::configuration cfg; + static bool initialised{false}; + if(!initialised) + { + cfg.host_toolchain = getToolChain(cfg.get(ctor::cfg::host_cxx, "/usr/bin/g++")); + initialised = true; + } + + return cfg; } -bool hasConfiguration(const std::string& key) +bool ctor::configuration::has(const std::string& key) const { return false; } -const std::string& getConfiguration(const std::string& key, - const std::string& defaultValue) +const std::string& ctor::configuration::get(const std::string& key, const std::string& default_value) const { - return defaultValue; + if(key == ctor::cfg::host_cxx && std::getenv("CXX")) + { + static std::string s = std::getenv("CXX"); + return s; + } + + if(key == ctor::cfg::builddir && std::getenv("BUILDDIR")) + { + static std::string s = std::getenv("BUILDDIR"); + return s; + } + + return default_value; } int main(int argc, char* argv[]) @@ -47,9 +66,10 @@ int main(int argc, char* argv[]) return 1; } - Settings settings{}; + ctor::settings settings{}; - settings.builddir = getConfiguration(cfg::builddir, "build"); + const auto& c = ctor::get_configuration(); + settings.builddir = c.get(ctor::cfg::builddir, settings.builddir); settings.parallel_processes = std::max(1u, std::thread::hardware_concurrency() * 2 - 1); settings.verbose = 0; @@ -66,7 +86,8 @@ int main(int argc, char* argv[]) auto& targets = getTargets(settings); for(const auto& target : targets) { - if(target.config.type != TargetType::UnitTest) + if(target.config.type != ctor::target_type::unit_test && + target.config.type != ctor::target_type::unit_test_library) { non_unittest_targets.push_back(target); } diff --git a/src/build.cc b/src/build.cc index 8adbc54..ad30719 100644 --- a/src/build.cc +++ b/src/build.cc @@ -9,13 +9,16 @@ #include <chrono> #include <set> #include <thread> +#include <list> + +#include "ctor.h" using namespace std::chrono_literals; -int build(const Settings& settings, +int build(const ctor::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) @@ -23,12 +26,12 @@ int build(const Settings& settings, 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); } } @@ -81,7 +84,9 @@ 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(); @@ -103,13 +108,9 @@ int build(const Settings& settings, break; } - if(started_one) - { - std::this_thread::sleep_for(2ms); - } - 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); } } @@ -153,29 +154,23 @@ std::set<std::shared_ptr<Task>> getDepTasks(std::shared_ptr<Task> task) } } -int build(const Settings& settings, +int build(const ctor::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->target() == name || // match exact target output (ex. build/foo.o) - - (!task->derived() && // if non-derived task: - ( task->buildConfig().target == name || // match name - task->buildConfig().name == name ) // or target - ) - ) + if(*task == name) { 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, dryrun); @@ -197,14 +192,14 @@ int build(const Settings& settings, return 0; } -int build(const Settings& settings, +int build(const ctor::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) { @@ -218,7 +213,7 @@ int build(const Settings& settings, auto depSet = getDepTasks(task); for(const auto& task : depSet) { - ts.push_back(task); + ts.insert(task); } } } diff --git a/src/build.h b/src/build.h index 7be7517..d74642c 100644 --- a/src/build.h +++ b/src/build.h @@ -4,29 +4,32 @@ #pragma once #include <string> -#include <list> +#include <set> #include <memory> #include "task.h" -#include "settings.h" #include "tasks.h" +namespace ctor { +struct settings; +} // namespace ctor:: + //! Dry-run returns number of dirty tasks but otherwise does nothing. -int build(const Settings& settings, +int build(const ctor::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, +int build(const ctor::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, +int build(const ctor::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 4df6026..8e88092 100644 --- a/src/configure.cc +++ b/src/configure.cc @@ -10,63 +10,91 @@ #include <getoptpp/getoptpp.hpp> -#include "settings.h" #include "execute.h" -#include "libctor.h" +#include "ctor.h" #include "tasks.h" #include "rebuild.h" +#include "externals.h" +#include "tools.h" std::filesystem::path configurationFile("configuration.cc"); std::filesystem::path configHeaderFile("config.h"); -const Configuration default_configuration{}; -const Configuration& __attribute__((weak)) configuration() +std::map<std::string, std::string> external_includedir; +std::map<std::string, std::string> external_libdir; + +const ctor::configuration& __attribute__((weak)) ctor::get_configuration() { - return default_configuration; + static ctor::configuration cfg; + static bool initialised{false}; + if(!initialised) + { + cfg.host_toolchain = getToolChain(cfg.get(ctor::cfg::host_cxx, "g++")); + initialised = true; + } + return cfg; } -namespace ctor -{ +namespace ctor { std::optional<std::string> includedir; std::optional<std::string> libdir; -} +std::optional<std::string> builddir; +std::map<std::string, std::string> conf_values; +} // ctor:: -bool hasConfiguration(const std::string& key) +bool ctor::configuration::has(const std::string& key) const { - if(key == cfg::ctor_includedir && ctor::includedir) + if(key == ctor::cfg::ctor_includedir && ctor::includedir) { return true; } - if(key == cfg::ctor_libdir && ctor::libdir) + if(key == ctor::cfg::ctor_libdir && ctor::libdir) { return true; } - const auto& c = configuration(); - return c.tools.find(key) != c.tools.end(); + if(key == ctor::cfg::builddir && ctor::builddir) + { + return true; + } + + if(ctor::conf_values.find(key) != ctor::conf_values.end()) + { + return true; + } + + return tools.find(key) != tools.end(); } -const std::string& getConfiguration(const std::string& key, - const std::string& defaultValue) +const std::string& ctor::configuration::get(const std::string& key, const std::string& default_value) const { - if(key == cfg::ctor_includedir && ctor::includedir) + if(key == ctor::cfg::ctor_includedir && ctor::includedir) { return *ctor::includedir; } - if(key == cfg::ctor_libdir && ctor::libdir) + if(key == ctor::cfg::ctor_libdir && ctor::libdir) { return *ctor::libdir; } - const auto& c = configuration(); - if(hasConfiguration(key)) + if(key == ctor::cfg::builddir && ctor::builddir) + { + return *ctor::builddir; + } + + if(ctor::conf_values.find(key) != ctor::conf_values.end()) { - return c.tools.at(key); + return ctor::conf_values[key]; } - return defaultValue; + if(has(key)) + { + return tools.at(key); + } + + return default_value; } std::string locate(const std::string& arch, const std::string& app) @@ -143,11 +171,71 @@ public: } }; -int regenerateCache(const Settings& default_settings, +namespace { +std::ostream& operator<<(std::ostream& stream, const ctor::toolchain& toolchain) +{ + switch(toolchain) + { + case ctor::toolchain::any: + stream << "ctor::toolchain::any"; + break; + case ctor::toolchain::none: + stream << "ctor::toolchain::none"; + break; + case ctor::toolchain::gcc: + stream << "ctor::toolchain::gcc"; + break; + case ctor::toolchain::clang: + stream << "ctor::toolchain::clang"; + break; + } + return stream; +} + +std::ostream& operator<<(std::ostream& ostr, const ctor::c_flag& flag) +{ + for(const auto& s : to_strings(ctor::toolchain::any, flag)) + { + ostr << s; + } + return ostr; +} + +std::ostream& operator<<(std::ostream& ostr, const ctor::cxx_flag& flag) +{ + for(const auto& s : to_strings(ctor::toolchain::any, flag)) + { + ostr << s; + } + return ostr; +} + +std::ostream& operator<<(std::ostream& ostr, const ctor::ld_flag& flag) +{ + for(const auto& s : to_strings(ctor::toolchain::any, flag)) + { + ostr << s; + } + return ostr; +} + +std::ostream& operator<<(std::ostream& ostr, const ctor::asm_flag& flag) +{ + for(const auto& s : to_strings(ctor::toolchain::any, flag)) + { + ostr << s; + } + return ostr; +} +} + +// helper constant for the visitor +template<class> inline constexpr bool always_false_v = false; + +int regenerateCache(ctor::settings& 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; @@ -163,12 +251,14 @@ int regenerateCache(const Settings& default_settings, std::string ld_prog = "ld"; std::string ctor_includedir; 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; return 0; }); @@ -249,6 +339,51 @@ int regenerateCache(const Settings& default_settings, return 0; }); + // Resolv externals + ctor::external_configurations 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, ctor::external_manual>) + { + 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.", [&]() { @@ -325,24 +460,38 @@ int regenerateCache(const Settings& default_settings, std::string build_ar = locate(build_arch, ar_prog); std::string build_ld = locate(build_arch, ld_prog); - // Resolv externals - ExternalConfigurations externalConfigs; - for(std::size_t i = 0; i < numExternalConfigFiles; ++i) + if(!host_cxx.empty()) { - auto newExternalConfigs = externalConfigFiles[i].cb(); - externalConfigs.insert(externalConfigs.end(), - newExternalConfigs.begin(), - newExternalConfigs.end()); + // This is needed for bootstrapping (when running configure for the first time) + ctor::conf_values[ctor::cfg::host_cxx] = host_cxx; } + // Store current values for execution in this execution context. + if(!ctor_includedir.empty()) + { + ctor::conf_values[ctor::cfg::ctor_includedir] = ctor_includedir; + } + if(!ctor_libdir.empty()) + { + ctor::conf_values[ctor::cfg::ctor_libdir] = ctor_libdir; + } + if(!builddir.empty()) + { + 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); - istr << "#include <libctor.h>\n\n"; - istr << "const Configuration& configuration()\n"; + istr << "#include <ctor.h>\n\n"; + istr << "const ctor::configuration& ctor::get_configuration()\n"; istr << "{\n"; - istr << " static Configuration cfg =\n"; + istr << " static ctor::configuration cfg =\n"; istr << " {\n"; + istr << " .host_toolchain = " << getToolChain(host_cxx) << ",\n"; + istr << " .build_toolchain = " << getToolChain(build_cxx) << ",\n"; istr << " .args = {"; for(const auto& arg : args) { @@ -357,69 +506,88 @@ int regenerateCache(const Settings& default_settings, 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(!builddir.empty()) + { + istr << " { \"" << ctor::cfg::builddir << "\", \"" << builddir << "\" },\n"; + ctor::builddir = builddir; + } + istr << " { \"" << ctor::cfg::host_cc << "\", \"" << host_cc << "\" },\n"; + istr << " { \"" << ctor::cfg::host_cxx << "\", \"" << host_cxx << "\" },\n"; + istr << " { \"" << ctor::cfg::host_ar << "\", \"" << host_ar << "\" },\n"; + istr << " { \"" << ctor::cfg::host_ld << "\", \"" << host_ld << "\" },\n"; + istr << " { \"" << ctor::cfg::build_cc << "\", \"" << build_cc << "\" },\n"; + istr << " { \"" << ctor::cfg::build_cxx << "\", \"" << build_cxx << "\" },\n"; + istr << " { \"" << ctor::cfg::build_ar << "\", \"" << build_ar << "\" },\n"; + istr << " { \"" << ctor::cfg::build_ld << "\", \"" << build_ld << "\" },\n"; if(!ctor_includedir.empty()) { - istr << " { \"" << cfg::ctor_includedir << "\", \"" << ctor_includedir << "\" },\n"; + istr << " { \"" << ctor::cfg::ctor_includedir << "\", \"" << ctor_includedir << "\" },\n"; ctor::includedir = ctor_includedir; } if(!ctor_libdir.empty()) { - istr << " { \"" << cfg::ctor_libdir << "\", \"" << ctor_libdir << "\" },\n"; + istr << " { \"" << ctor::cfg::ctor_libdir << "\", \"" << ctor_libdir << "\" },\n"; ctor::libdir = ctor_libdir; } istr << " },\n"; istr << " .externals = {\n"; - for(const auto& externalConfig : externalConfigs) + for(const auto& ext : externalConfigs) { - istr << " { \"" << externalConfig.name << "\", {\n"; + istr << " { \"" << ext.name << "\", {\n"; + ctor::flags resolved_flags; + if(std::holds_alternative<ctor::external_manual>(ext.external)) + { + if(auto ret = resolv(settings, ext, + std::get<ctor::external_manual>(ext.external), + resolved_flags)) + { + return ret; + } + } + else + { + std::cout << "Unknown external type\n"; + return 1; + } - if(!externalConfig.flags.cxxflags.empty()) + if(!resolved_flags.cflags.empty()) { - istr << " .cxxflags = {"; - for(const auto& flag : externalConfig.flags.cxxflags) + istr << " .cflags = {"; + for(const auto& flag : resolved_flags.cflags) { - istr << "\"" << flag << "\","; + istr << flag << ","; } istr << "},\n"; } - if(!externalConfig.flags.cflags.empty()) + if(!resolved_flags.cxxflags.empty()) { - istr << " .cflags = {"; - for(const auto& flag : externalConfig.flags.cflags) + istr << " .cxxflags = {"; + for(const auto& flag : resolved_flags.cxxflags) { - istr << "\"" << flag << "\","; + istr << flag << ","; } istr << "},\n"; } - if(!externalConfig.flags.ldflags.empty()) + if(!resolved_flags.ldflags.empty()) { istr << " .ldflags = {"; - for(const auto& flag : externalConfig.flags.ldflags) + for(const auto& flag : resolved_flags.ldflags) { - istr << "\"" << flag << "\","; + istr << flag << ","; } istr << "},\n"; } - if(!externalConfig.flags.asmflags.empty()) + if(!resolved_flags.asmflags.empty()) { istr << " .asmflags = {"; - for(const auto& flag : externalConfig.flags.asmflags) + for(const auto& flag : resolved_flags.asmflags) { - istr << "\"" << flag << "\","; + istr << flag << ","; } istr << "},\n"; } @@ -429,7 +597,7 @@ int regenerateCache(const Settings& default_settings, istr << " },\n"; istr << " };\n"; istr << " return cfg;\n"; - istr << "}\n\n"; + istr << "}\n"; } { @@ -442,9 +610,9 @@ int regenerateCache(const Settings& default_settings, return 0; } -int configure(const Settings& global_settings, int argc, char* argv[]) +int configure(const ctor::settings& global_settings, int argc, char* argv[]) { - Settings settings{global_settings}; + ctor::settings settings{global_settings}; std::vector<std::string> args; for(int i = 2; i < argc; ++i) // skip command and the first 'configure' arg @@ -488,8 +656,10 @@ int configure(const Settings& global_settings, int argc, char* argv[]) return 0; } -int reconfigure(const Settings& settings, int argc, char* argv[]) +int reconfigure(const ctor::settings& global_settings, int argc, char* argv[]) { + ctor::settings settings{global_settings}; + bool no_rerun{false}; std::vector<std::string> args; @@ -503,7 +673,7 @@ int reconfigure(const Settings& settings, int argc, char* argv[]) args.push_back(argv[i]); } - const auto& cfg = configuration(); + const auto& cfg = ctor::get_configuration(); std::cout << "Re-running configure:\n"; for(const auto& e : cfg.env) diff --git a/src/configure.h b/src/configure.h index 16499d6..ac8d721 100644 --- a/src/configure.h +++ b/src/configure.h @@ -8,10 +8,12 @@ #include <map> #include <vector> -struct Settings; +namespace ctor { +struct settings; +} // namespace ctor:: extern std::filesystem::path configurationFile;; extern std::filesystem::path configHeaderFile; -int configure(const Settings& settings, int argc, char* argv[]); -int reconfigure(const Settings& settings, int argc, char* argv[]); +int configure(const ctor::settings& settings, int argc, char* argv[]); +int reconfigure(const ctor::settings& settings, int argc, char* argv[]); diff --git a/src/ctor.h b/src/ctor.h new file mode 100644 index 0000000..b8b3ae6 --- /dev/null +++ b/src/ctor.h @@ -0,0 +1,268 @@ +// -*- c++ -*- +// Distributed under the BSD 2-Clause License. +// See accompanying file LICENSE for details. +#pragma once + +#include <source_location> +#include <string> +#include <vector> +#include <map> +#include <variant> +#include <cstddef> +#include <functional> + +namespace ctor { + +enum class target_type +{ + automatic, // Default - deduce from target name and sources extensions + + executable, + static_library, + dynamic_library, + object, + unit_test, + unit_test_library, + function, +}; + +enum class language +{ + automatic, // Default - deduce language from source extensions + + c, + cpp, + assembler, +}; + +enum class output_system +{ + host, // Output for the target system + build, // Internal tool during cross-compilation +}; + +struct source +{ + source(const char* file) : file(file) {} + source(const std::string& file) : file(file) {} + source(const char* file, ctor::language lang) : file(file), language(lang) {} + source(const std::string& file, ctor::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, ctor::language lang, const char* output) : file(file), language(lang), output(output) {} + source(const std::string& file, ctor::language lang, const std::string& output) : file(file), language(lang), output(output) {} + + std::string file; + ctor::language language{ctor::language::automatic}; + std::string output{}; +}; + +enum class toolchain +{ + any, + none, + gcc, + clang, +}; + +enum class cxx_opt +{ + // gcc/clang + output, // -o + debug, // -g + warn_all, // -Wall + warnings_as_errors, // -Werror + generate_dep_tree, // -MMD + no_link, // -c + include_path, // -I<arg> + cpp_std, // -std=<arg> + optimization, // -O<arg> + position_independent_code, // -fPIC + position_independent_executable, // -fPIE + custom, // entire option taken verbatim from <arg> +}; + +enum class c_opt +{ + // gcc/clang + output, // -o + debug, // -g + warn_all, // -Wall + warnings_as_errors, // -Werror + generate_dep_tree, // -MMD + no_link, // -c + include_path, // -I<arg> + c_std, // -std=<arg> + optimization, // -O<arg> + position_independent_code, // -fPIC + position_independent_executable, // -fPIE + custom, // entire option taken verbatim from <arg> +}; + +enum class ld_opt +{ + // gcc/clang + output, // -o + strip, // -s + warn_all, // -Wall + warnings_as_errors, // -Werror + library_path, // -L<arg> + link, // -l<arg> + cpp_std, // -std=<arg> + build_shared, // -shared + threads, // -pthread + position_independent_code, // -fPIC + position_independent_executable, // -fPIE + custom, // entire option taken verbatim from <arg> +}; + +enum class ar_opt +{ + // gcc/clang + replace, // -r + add_index, // -s + create, // -c + output, // <arg> + + custom, // entire option taken verbatim from <arg> +}; + +enum class asm_opt +{ + // gcc/clang + custom, // entire option taken verbatim from <arg> +}; + +template<typename T> +class flag +{ +public: + flag(const std::string& str); + flag(const char* str); + flag(T opt) : opt(opt) {} + flag(T opt, const std::string& arg) : opt(opt), arg(arg) {} + flag(T opt, const char* arg) : opt(opt), arg(arg) {} + flag(ctor::toolchain toolchain, T opt) : toolchain(toolchain), opt(opt) {} + flag(ctor::toolchain toolchain, T opt, const char* arg) : toolchain(toolchain), opt(opt), arg(arg) {} + flag(ctor::toolchain toolchain, T opt, const std::string& arg) : toolchain(toolchain), opt(opt), arg(arg) {} + + ctor::toolchain toolchain{ctor::toolchain::any}; + T opt; + std::string arg; +}; + +using c_flag = ctor::flag<ctor::c_opt>; +using cxx_flag = ctor::flag<ctor::cxx_opt>; +using ld_flag = ctor::flag<ctor::ld_opt>; +using ar_flag = ctor::flag<ctor::ar_opt>; +using asm_flag = ctor::flag<ctor::asm_opt>; + +using c_flags = std::vector<ctor::c_flag>; +using cxx_flags = std::vector<ctor::cxx_flag>; +using ld_flags= std::vector<ctor::ld_flag>; +using ar_flags = std::vector<ctor::ar_flag>; +using asm_flags = std::vector<ctor::asm_flag>; + +struct flags +{ + ctor::c_flags cflags; // flags for c compiler + ctor::cxx_flags cxxflags; // flags for c++ compiler + ctor::ld_flags ldflags; // flags for linker + ctor::ar_flags arflags; // flags for archiver + ctor::asm_flags 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 build_configuration; +using GeneratorCb = std::function<int(const std::string& input, + const std::string& output, + const build_configuration& config, + const ctor::settings& settings)>; + +struct build_configuration +{ + 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::host}; + std::string target; // Output target file for this configuration + std::vector<ctor::source> sources; // source list + std::vector<std::string> depends; // internal target dependencies + ctor::flags flags; + std::vector<std::string> externals; // externals used by this configuration + GeneratorCb function; +}; + +using build_configurations = std::vector<build_configuration>; + +int reg(ctor::build_configurations (*cb)(const ctor::settings&), + const std::source_location location = std::source_location::current()); + +// This type will use flags verbatim +struct external_manual +{ + ctor::flags flags; +}; + + +struct external_configuration +{ + std::string name; // Name for configuration + ctor::output_system system{ctor::output_system::host}; + std::variant<ctor::external_manual> external; +}; + +using external_configurations = std::vector<ctor::external_configuration>; + +int reg(ctor::external_configurations (*cb)(const ctor::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(cb); } + +// Predefined configuration keys +namespace cfg +{ +constexpr auto builddir = "builddir"; + +constexpr auto host_cc = "host-cc"; +constexpr auto host_cxx = "host-cpp"; +constexpr auto host_ar = "host-ar"; +constexpr auto host_ld = "host-ld"; + +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"; +} + +struct configuration +{ + bool has(const std::string& key) const; + const std::string& get(const std::string& key, const std::string& default_value = {}) const; + + ctor::toolchain host_toolchain{ctor::toolchain::none}; + ctor::toolchain build_toolchain{ctor::toolchain::none}; + + 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, ctor::flags> externals; +}; + +const ctor::configuration& get_configuration(); + +} // ctor:: diff --git a/src/execute.cc b/src/execute.cc index 610ccdd..20a4a02 100644 --- a/src/execute.cc +++ b/src/execute.cc @@ -61,7 +61,7 @@ int execute(const std::string& command, cmd += arg; } - std::cout << cmd << "\n"; + std::cout << cmd << std::endl; } #if 1 diff --git a/src/externals.h b/src/externals.h new file mode 100644 index 0000000..7b4aa23 --- /dev/null +++ b/src/externals.h @@ -0,0 +1,6 @@ +// -*- c++ -*- +// Distributed under the BSD 2-Clause License. +// See accompanying file LICENSE for details. +#pragma once + +#include "externals_manual.h" diff --git a/src/externals_manual.cc b/src/externals_manual.cc new file mode 100644 index 0000000..3b96263 --- /dev/null +++ b/src/externals_manual.cc @@ -0,0 +1,35 @@ +// -*- c++ -*- +// Distributed under the BSD 2-Clause License. +// See accompanying file LICENSE for details. +#include "externals_manual.h" + +#include <map> + +#include "ctor.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 ctor::settings& settings, const ctor::external_configuration& config, + const ctor::external_manual& ext, ctor::flags& flags) +{ + flags = ext.flags; + + auto inc = external_includedir.find(config.name); + if(inc != external_includedir.end()) + { + flags.cflags.push_back({ctor::c_opt::include_path, inc->second}); + flags.cxxflags.push_back({ctor::cxx_opt::include_path, inc->second}); + } + + auto lib = external_libdir.find(config.name); + if(lib != external_libdir.end()) + { + flags.ldflags.push_back({ctor::ld_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..a906ab7 --- /dev/null +++ b/src/externals_manual.h @@ -0,0 +1,15 @@ +// -*- c++ -*- +// Distributed under the BSD 2-Clause License. +// See accompanying file LICENSE for details. +#pragma once + +namespace ctor { +struct settings; +struct external_configuration; +struct external_manual; +struct flags; +} // namespace ctor:: + +int resolv(const ctor::settings& settings, + const ctor::external_configuration& name, + const ctor::external_manual& ext, ctor::flags& flags); diff --git a/src/libctor.cc b/src/libctor.cc index a2c873e..21792e1 100644 --- a/src/libctor.cc +++ b/src/libctor.cc @@ -19,8 +19,7 @@ #include <getoptpp/getoptpp.hpp> -#include "libctor.h" -#include "settings.h" +#include "ctor.h" #include "configure.h" #include "rebuild.h" #include "tasks.h" @@ -29,9 +28,10 @@ int main(int argc, char* argv[]) { - Settings settings{}; + ctor::settings settings{}; + const auto& c = ctor::get_configuration(); - settings.builddir = getConfiguration(cfg::builddir, settings.builddir); + settings.builddir = c.get(ctor::cfg::builddir, settings.builddir); settings.parallel_processes = std::max(1u, std::thread::hardware_concurrency()) * 2 - 1; @@ -264,13 +264,13 @@ Options: if(print_configure_cmd) { no_default_build = true; - std::cout << getConfiguration("cmd") << "\n"; + std::cout << c.get("cmd") << "\n"; } if(print_configure_db) { no_default_build = true; - const auto& c = configuration(); + const auto& c = ctor::get_configuration(); for(const auto& config : c.tools) { std::cout << config.first << ": " << config.second << "\n"; @@ -316,7 +316,8 @@ Options: auto& targets = getTargets(settings); for(const auto& target : targets) { - if(target.config.type == TargetType::UnitTest) + if(target.config.type == ctor::target_type::unit_test || + target.config.type == ctor::target_type::unit_test_library) { unittest_targets.push_back(target); } @@ -338,7 +339,8 @@ Options: auto& targets = getTargets(settings); for(const auto& target : targets) { - if(target.config.type != TargetType::UnitTest) + if(target.config.type != ctor::target_type::unit_test && + target.config.type != ctor::target_type::unit_test_library) { non_unittest_targets.push_back(target); } @@ -367,7 +369,8 @@ Options: auto& targets = getTargets(settings); for(const auto& target : targets) { - if(target.config.type != TargetType::UnitTest) + if(target.config.type != ctor::target_type::unit_test && + target.config.type != ctor::target_type::unit_test_library) { non_unittest_targets.push_back(target); } diff --git a/src/libctor.h b/src/libctor.h deleted file mode 100644 index 0af33cb..0000000 --- a/src/libctor.h +++ /dev/null @@ -1,123 +0,0 @@ -// -*- c++ -*- -// Distributed under the BSD 2-Clause License. -// See accompanying file LICENSE for details. -#pragma once - -#include <source_location> -#include <string> -#include <vector> -#include <map> - -enum class TargetType -{ - Auto, // Default - deduce from target name and sources extensions - - Executable, - StaticLibrary, - DynamicLibrary, - Object, - UnitTest, -}; - -enum class Language -{ - Auto, // Default - deduce language from source extensions - - C, - Cpp, - Asm, -}; - -enum class OutputSystem -{ - Host, // Output for the target system - Build, // Internal tool during cross-compilation -}; - -struct Source -{ - 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) {} - - std::string file; - Language language{Language::Auto}; -}; - -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 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 -}; - -using BuildConfigurations = std::vector<BuildConfiguration>; - -int reg(BuildConfigurations (*cb)(), - const std::source_location location = std::source_location::current()); - - -struct ExternalConfiguration -{ - std::string name; // Name for configuration - Flags flags; - std::vector<std::string> libs; // libraries -}; - -using ExternalConfigurations = std::vector<ExternalConfiguration>; - -int reg(ExternalConfigurations (*cb)(), - 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(cb); } - -// Predefined configuration keys -namespace cfg -{ -constexpr auto builddir = "builddir"; - -constexpr auto host_cc = "host-cc"; -constexpr auto host_cxx = "host-cpp"; -constexpr auto host_ar = "host-ar"; -constexpr auto host_ld = "host-ld"; - -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"; -} - -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 50d7540..c97452d 100644 --- a/src/rebuild.cc +++ b/src/rebuild.cc @@ -7,18 +7,21 @@ #include <filesystem> #include <algorithm> #include <source_location> +#include <cstring> #include "configure.h" -#include "settings.h" -#include "libctor.h" +#include "ctor.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}; -int reg(BuildConfigurations (*cb)(), +namespace ctor { +int reg(ctor::build_configurations (*cb)(const ctor::settings&), const std::source_location location) { // NOTE: std::cout cannot be used here @@ -29,12 +32,23 @@ int reg(BuildConfigurations (*cb)(), exit(1); } - configFiles[numConfigFiles].file = location.file_name(); + 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; } +} // ctor:: int reg(const char* location) { @@ -48,7 +62,7 @@ int reg(const char* location) configFiles[numConfigFiles].file = location; configFiles[numConfigFiles].cb = - [](){ return std::vector<BuildConfiguration>{}; }; + [](const ctor::settings&){ return std::vector<ctor::build_configuration>{}; }; ++numConfigFiles; return 0; @@ -97,7 +111,8 @@ int unreg(const char* location) std::array<ExternalConfigurationEntry, 1024> externalConfigFiles; std::size_t numExternalConfigFiles{0}; -int reg(ExternalConfigurations (*cb)(), +namespace ctor { +int reg(ctor::external_configurations (*cb)(const ctor::settings&), const std::source_location location) { // NOTE: std::cout cannot be used here @@ -114,10 +129,11 @@ int reg(ExternalConfigurations (*cb)(), return 0; } +} // namespace ctor:: namespace { -bool contains(const std::vector<Source>& sources, const std::string& file) +bool contains(const std::vector<ctor::source>& sources, const std::string& file) { for(const auto& source : sources) { @@ -131,7 +147,7 @@ bool contains(const std::vector<Source>& sources, const std::string& file) } } -bool recompileCheck(const Settings& global_settings, int argc, char* argv[], +bool recompileCheck(const ctor::settings& global_settings, int argc, char* argv[], bool relaunch_allowed) { using namespace std::string_literals; @@ -141,24 +157,28 @@ bool recompileCheck(const Settings& global_settings, int argc, char* argv[], std::cout << "Recompile check (" << numConfigFiles << "):\n"; } - BuildConfiguration config; + ctor::build_configuration config; config.name = "ctor"; - config.flags.cxxflags = - std::vector<std::string>({ "-s", "-O3", "-std=c++20" }); - if(hasConfiguration(cfg::ctor_includedir)) + config.system = ctor::output_system::build; + + config.flags.cxxflags.push_back({ctor::cxx_opt::optimization, "3"}); + config.flags.cxxflags.push_back({ctor::cxx_opt::cpp_std, "c++20"}); + + const auto& c = ctor::get_configuration(); + if(c.has(ctor::cfg::ctor_includedir)) { - config.flags.cxxflags.push_back("-I"s + getConfiguration(cfg::ctor_includedir)); + config.flags.cxxflags.push_back({ctor::cxx_opt::include_path, + c.get(ctor::cfg::ctor_includedir)}); } - if(hasConfiguration(cfg::ctor_libdir)) + if(c.has(ctor::cfg::ctor_libdir)) { - config.flags.ldflags.push_back("-L"s + getConfiguration(cfg::ctor_libdir)); + config.flags.ldflags.push_back({ctor::ld_opt::library_path, c.get(ctor::cfg::ctor_libdir)}); } - config.flags.ldflags.push_back("-lctor"); - config.flags.ldflags.push_back("-pthread"); - + config.flags.ldflags.push_back({ctor::ld_opt::link, "ctor"}); + config.flags.ldflags.push_back({ctor::ld_opt::threads}); - Settings settings{global_settings}; + ctor::settings settings{global_settings}; settings.verbose = -1; // Make check completely silent. settings.builddir += "/ctor"; // override builddir to use ctor subdir @@ -229,7 +249,11 @@ bool recompileCheck(const Settings& global_settings, int argc, char* argv[], if(dirty_tasks) { std::cout << "Rebuilding config.\n"; - build(settings, "ctor", tasks); // run for real + auto ret = build(settings, "ctor", tasks); // run for real + if(ret != 0) + { + return ret; + } } if(reconfigure) diff --git a/src/rebuild.h b/src/rebuild.h index 0845d30..efa6d42 100644 --- a/src/rebuild.h +++ b/src/rebuild.h @@ -6,20 +6,18 @@ #include <vector> #include <array> -#include "libctor.h" - -class Settings; +#include "ctor.h" struct BuildConfigurationEntry { const char* file; - BuildConfigurations (*cb)(); + ctor::build_configurations (*cb)(const ctor::settings&); }; struct ExternalConfigurationEntry { const char* file; - ExternalConfigurations (*cb)(); + ctor::external_configurations (*cb)(const ctor::settings&); }; extern std::array<BuildConfigurationEntry, 1024> configFiles; @@ -32,5 +30,5 @@ int reg(const char* location); int unreg(const char* location); //! Returns true of recompilation was needed. -bool recompileCheck(const Settings& settings, int argc, char* argv[], +bool recompileCheck(const ctor::settings& settings, int argc, char* argv[], bool relaunch_allowed = true); diff --git a/src/settings.h b/src/settings.h deleted file mode 100644 index 48e3619..0000000 --- a/src/settings.h +++ /dev/null @@ -1,13 +0,0 @@ -// -*- c++ -*- -// Distributed under the BSD 2-Clause License. -// See accompanying file LICENSE for details. -#pragma once - -#include <cstddef> - -struct Settings -{ - std::string builddir{"build"}; - std::size_t parallel_processes{1}; - int verbose{0}; // -1: completely silent, 0: normal, 1: verbose, ... -}; diff --git a/src/task.cc b/src/task.cc index 8a9eefa..817ee3a 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 ctor::build_configuration& config, const ctor::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,16 +36,21 @@ 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 { - // If config name is not set, use target instead. - if(config.name.empty()) - { - return config.target; - } return config.name; } @@ -99,45 +107,46 @@ State Task::state() const return task_state.load(); } -const BuildConfiguration& Task::buildConfig() const +const ctor::build_configuration& Task::buildConfig() const { return config; } -TargetType Task::targetType() const +ctor::target_type Task::targetType() const { return target_type; } -Language Task::sourceLanguage() const +ctor::language Task::sourceLanguage() const { return source_language; } -OutputSystem Task::outputSystem() const +ctor::output_system Task::outputSystem() const { return output_system; } std::string Task::compiler() const { + const auto& c = ctor::get_configuration(); switch(sourceLanguage()) { - case Language::C: + case ctor::language::c: switch(outputSystem()) { - case OutputSystem::Host: - return getConfiguration(cfg::host_cc, "/usr/bin/gcc"); - case OutputSystem::Build: - return getConfiguration(cfg::build_cc, "/usr/bin/gcc"); + case ctor::output_system::host: + return c.get(ctor::cfg::host_cc, "/usr/bin/gcc"); + case ctor::output_system::build: + return c.get(ctor::cfg::build_cc, "/usr/bin/gcc"); } - case Language::Cpp: + case ctor::language::cpp: switch(outputSystem()) { - case OutputSystem::Host: - return getConfiguration(cfg::host_cxx, "/usr/bin/g++"); - case OutputSystem::Build: - return getConfiguration(cfg::build_cxx, "/usr/bin/g++"); + case ctor::output_system::host: + return c.get(ctor::cfg::host_cxx, "/usr/bin/g++"); + case ctor::output_system::build: + return c.get(ctor::cfg::build_cxx, "/usr/bin/g++"); } default: std::cerr << "Unknown CC target type\n"; @@ -146,7 +155,7 @@ std::string Task::compiler() const } } -std::list<std::shared_ptr<Task>> Task::getDependsTasks() +std::set<std::shared_ptr<Task>> Task::getDependsTasks() { return dependsTasks; } @@ -6,10 +6,11 @@ #include <vector> #include <string> #include <atomic> -#include <list> +#include <set> #include <memory> +#include <filesystem> -#include "libctor.h" +#include "ctor.h" enum class State { @@ -23,9 +24,14 @@ enum class State class Task { public: - Task(const BuildConfiguration& config); + Task(const ctor::build_configuration& config, const ctor::settings& settings, + const std::string& sourceDir); + virtual ~Task() = default; - int registerDepTasks(const std::list<std::shared_ptr<Task>>& tasks); + int registerDepTasks(const std::set<std::shared_ptr<Task>>& tasks); + virtual int registerDepTasksInner(const std::set<std::shared_ptr<Task>>& tasks) { return 0; } + + bool operator==(const std::string& dep); virtual std::string name() const; bool dirty(); @@ -34,8 +40,14 @@ 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; @@ -45,14 +57,14 @@ public: //! 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; + const ctor::build_configuration& buildConfig() const; - TargetType targetType() const; - Language sourceLanguage() const; - OutputSystem outputSystem() const; + ctor::target_type targetType() const; + ctor::language sourceLanguage() const; + ctor::output_system 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 {}; } @@ -62,9 +74,11 @@ protected: virtual bool dirtyInner() { return false; } std::vector<std::string> dependsStr; - std::list<std::shared_ptr<Task>> dependsTasks; - const BuildConfiguration& config; - TargetType target_type{TargetType::Auto}; - Language source_language{Language::Auto}; - OutputSystem output_system{OutputSystem::Host}; + std::set<std::shared_ptr<Task>> dependsTasks; + const ctor::build_configuration& config; + ctor::target_type target_type{ctor::target_type::automatic}; + ctor::language source_language{ctor::language::automatic}; + ctor::output_system output_system{ctor::output_system::host}; + const ctor::settings& settings; + std::string sourceDir; }; diff --git a/src/task_ar.cc b/src/task_ar.cc index d4a4447..426a576 100644 --- a/src/task_ar.cc +++ b/src/task_ar.cc @@ -6,25 +6,24 @@ #include <iostream> #include <fstream> -#include "libctor.h" -#include "settings.h" +#include "ctor.h" #include "execute.h" #include "util.h" +#include "tools.h" -TaskAR::TaskAR(const BuildConfiguration& config, - const Settings& settings, +TaskAR::TaskAR(const ctor::build_configuration& config, + const ctor::settings& settings, const std::string& target, const std::vector<std::string>& objects, - const std::string& sourcePath) - : Task(config) + const std::string& sourceDir) + : Task(config, settings, sourceDir) , config(config) , settings(settings) + , sourceDir(sourceDir) { - std::filesystem::path base = settings.builddir; - base /= sourcePath; - std::filesystem::create_directories(base); + std::filesystem::create_directories(std::filesystem::path(settings.builddir) / sourceDir); - targetFile = base / target; + _targetFile = target; for(const auto& object : objects) { std::filesystem::path objectFile = object; @@ -34,30 +33,28 @@ 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 = base / targetFile.stem(); + flagsFile = std::filesystem::path(settings.builddir) / cleanUp(sourceDir) / targetFile().stem(); flagsFile += ".flags"; - target_type = TargetType::StaticLibrary; - source_language = Language::C; + target_type = ctor::target_type::static_library; + source_language = ctor::language::c; for(const auto& source : config.sources) { std::filesystem::path sourceFile(source.file); // TODO: Use task languages instead if(sourceFile.extension().string() != ".c") { - source_language = Language::Cpp; + source_language = ctor::language::cpp; } } } bool TaskAR::dirtyInner() { - if(!std::filesystem::exists(targetFile)) + if(!std::filesystem::exists(targetFile())) { return true; } @@ -67,15 +64,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) @@ -90,22 +78,16 @@ bool TaskAR::dirtyInner() int TaskAR::runInner() { - std::string objectlist; - for(const auto& objectFile : objectFiles) - { - if(!objectlist.empty()) - { - objectlist += " "; - } - objectlist += objectFile.string(); - } + auto toolchain = getToolChain(config.system); std::vector<std::string> args; - args.push_back("rcs"); - args.push_back(targetFile.string()); - for(const auto& objectFile : objectFiles) + append(args, ar_option(toolchain, ctor::ar_opt::replace)); + append(args, ar_option(toolchain, ctor::ar_opt::add_index)); + append(args, ar_option(toolchain, ctor::ar_opt::create)); + append(args, ar_option(toolchain, ctor::ar_opt::output, targetFile().string())); + for(const auto& task : getDependsTasks()) { - args.push_back(objectFile.string()); + args.push_back(task->targetFile().string()); } { // Write flags to file. @@ -115,17 +97,18 @@ int TaskAR::runInner() if(settings.verbose == 0) { - std::cout << "AR => " << targetFile.string() << "\n"; + std::cout << "AR => " << targetFile().string() << std::endl; } + const auto& c = ctor::get_configuration(); std::string tool; switch(outputSystem()) { - case OutputSystem::Host: - tool = getConfiguration(cfg::host_ar, "/usr/bin/ar"); + case ctor::output_system::host: + tool = c.get(ctor::cfg::host_ar, "/usr/bin/ar"); break; - case OutputSystem::Build: - tool = getConfiguration(cfg::build_ar, "/usr/bin/ar"); + case ctor::output_system::build: + tool = c.get(ctor::cfg::build_ar, "/usr/bin/ar"); break; } @@ -134,10 +117,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)) @@ -157,9 +140,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; @@ -167,7 +150,12 @@ 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 @@ -177,14 +165,20 @@ bool TaskAR::derived() const std::string TaskAR::flagsString() const { + auto toolchain = getToolChain(config.system); std::string flagsStr; + bool first{true}; for(const auto& flag : config.flags.ldflags) { - if(flag != config.flags.ldflags[0]) + for(const auto& str : to_strings(toolchain, flag)) { - flagsStr += " "; + if(first) + { + flagsStr += " "; + first = false; + } + flagsStr += str; } - flagsStr += flag; } flagsStr += "\n"; diff --git a/src/task_ar.h b/src/task_ar.h index f540fef..601afeb 100644 --- a/src/task_ar.h +++ b/src/task_ar.h @@ -10,18 +10,16 @@ #include <future> #include <filesystem> -struct BuildConfiguration; -struct Settings; - class TaskAR : public Task { public: - TaskAR(const BuildConfiguration& config, - const Settings& settings, + TaskAR(const ctor::build_configuration& config, + const ctor::settings& settings, const std::string& target, const std::vector<std::string>& objects, - const std::string& sourcePath); + const std::string& sourceDir); + virtual ~TaskAR() = default; bool dirtyInner() override; @@ -31,6 +29,8 @@ public: std::vector<std::string> depends() const override; std::string target() const override; + std::filesystem::path targetFile() const override; + bool derived() const override; private: @@ -38,9 +38,10 @@ private: 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; + const ctor::build_configuration& config; + const ctor::settings& settings; + std::string sourceDir; }; diff --git a/src/task_cc.cc b/src/task_cc.cc index 1db9767..56915f5 100644 --- a/src/task_cc.cc +++ b/src/task_cc.cc @@ -7,88 +7,83 @@ #include <fstream> #include <cassert> -#include "libctor.h" -#include "settings.h" +#include "ctor.h" #include "execute.h" #include "util.h" +#include "tools.h" -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; -} -} - -TaskCC::TaskCC(const BuildConfiguration& config, const Settings& settings, - const std::string& sourceDir, const Source& source) - : Task(config) +TaskCC::TaskCC(const ctor::build_configuration& config, const ctor::settings& settings, + const std::string& sourceDir, const ctor::source& source) + : Task(config, settings, sourceDir) , config(config) , settings(settings) , sourceDir(sourceDir) + , _source(source) { sourceFile = sourceDir; sourceFile /= source.file; - std::filesystem::path base = settings.builddir; - base /= sourceFile.parent_path(); - std::filesystem::create_directories(base); + std::filesystem::path base = sourceFile.parent_path(); + std::filesystem::create_directories(std::filesystem::path(settings.builddir) / base); base /= cleanUp(config.target); base += "-"; base += sourceFile.stem(); - target_type = TargetType::Object; + target_type = ctor::target_type::object; source_language = source.language; - if(source_language == Language::Auto) + if(source_language == ctor::language::automatic) { source_language = languageFromExtension(sourceFile); } switch(source_language) { - case Language::C: + case ctor::language::c: base += "_c"; break; - case Language::Cpp: + case ctor::language::cpp: base += "_cc"; break; - case Language::Asm: + case ctor::language::assembler: base += "_asm"; break; - case Language::Auto: + case ctor::language::automatic: assert(0 && "This should never happen"); break; } - targetFile = base; - targetFile += ".o"; - depsFile = base; + if(source.output.empty()) + { + _targetFile = base; + _targetFile += ".o"; + } + else + { + _targetFile = source.output; + } + depsFile = targetFile().parent_path() / targetFile().stem(); depsFile += ".d"; - flagsFile = base; + 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() @@ -99,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; @@ -137,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"; @@ -146,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; @@ -172,9 +167,22 @@ int TaskCC::runInner() if(settings.verbose == 0) { - std::cout << compiler() << " " << + switch(sourceLanguage()) + { + case ctor::language::c: + std::cout << "CC "; + break; + case ctor::language::cpp: + std::cout << "CXX "; + break; + case ctor::language::automatic: + case ctor::language::assembler: + // Only c/c++ handled by this task type. + break; + } + std::cout << sourceFile.lexically_normal().string() << " => " << - targetFile.lexically_normal().string() << "\n"; + targetFile().lexically_normal().string() << std::endl; } return execute(compiler(), args, settings.verbose > 0); @@ -182,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)) @@ -210,7 +218,12 @@ 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 @@ -224,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) @@ -243,12 +256,23 @@ std::string TaskCC::source() const std::vector<std::string> TaskCC::flags() const { + std::vector<std::string> flags; + auto toolchain = getToolChain(config.system); + switch(sourceLanguage()) { - case Language::C: - return config.flags.cflags; - case Language::Cpp: - return config.flags.cxxflags; + case ctor::language::c: + for(const auto& flag : config.flags.cflags) + { + append(flags, to_strings(toolchain, flag)); + } + return flags; + case ctor::language::cpp: + for(const auto& flag : config.flags.cxxflags) + { + append(flags, to_strings(toolchain, flag)); + } + return flags; default: std::cerr << "Unknown CC target type\n"; exit(1); @@ -268,42 +292,100 @@ std::string TaskCC::flagsString() const std::vector<std::string> TaskCC::getCompilerArgs() const { + auto toolchain = getToolChain(config.system); auto compiler_flags = flags(); std::vector<std::string> args; - args.push_back("-MMD"); - if(std::filesystem::path(config.target).extension() == ".so") + switch(sourceLanguage()) { - // Add -fPIC arg to all contained object files - args.push_back("-fPIC"); - } + case ctor::language::c: + { + append(args, c_option(toolchain, ctor::c_opt::generate_dep_tree)); - args.push_back("-c"); - args.push_back(sourceFile.string()); - args.push_back("-o"); - args.push_back(targetFile.string()); + if(std::filesystem::path(config.target).extension() == ".so") + { + // Add -fPIC arg to all contained object files + append(args, c_option(toolchain, + ctor::c_opt::position_independent_code)); + } - for(const auto& flag : compiler_flags) - { - // Is arg an added include path? - if(flag.substr(0, 2) == "-I") + append(args, c_option(toolchain, ctor::c_opt::no_link)); + args.push_back(sourceFile.string()); + append(args, c_option(toolchain, + ctor::c_opt::output, targetFile().string())); + + // Relative include paths has to be altered to be relative to sourceDir + for(const auto& flag : compiler_flags) + { + auto option = c_option(flag, toolchain); + switch(option.opt) + { + case ctor::c_opt::include_path: + { + std::filesystem::path path(option.arg); + if(path.is_relative()) + { + path = (sourceDir / path).lexically_normal(); + append(args, c_option(toolchain, + ctor::c_opt::include_path, path.string())); + } + } + break; + default: + break; + } + + args.push_back(flag); + } + } + break; + + case ctor::language::cpp: { - std::string include_path = flag.substr(2); - include_path.erase(0, include_path.find_first_not_of(' ')); - std::filesystem::path path(include_path); + append(args, cxx_option(toolchain, ctor::cxx_opt::generate_dep_tree)); + + if(std::filesystem::path(config.target).extension() == ".so") + { + // Add -fPIC arg to all contained object files + append(args, cxx_option(toolchain, + ctor::cxx_opt::position_independent_code)); + } - // Is it relative? - if(path.is_relative()) + append(args, cxx_option(toolchain, ctor::cxx_opt::no_link)); + args.push_back(sourceFile.string()); + append(args, cxx_option(toolchain, + ctor::cxx_opt::output, targetFile().string())); + + // Relative include paths has to be altered to be relative to sourceDir + for(const auto& flag : compiler_flags) { - path = (sourceDir / path).lexically_normal(); - std::string new_include_path = "-I" + path.string(); - args.push_back(new_include_path); - continue; + auto option = cxx_option(flag, toolchain); + switch(option.opt) + { + case ctor::cxx_opt::include_path: + { + std::filesystem::path path(option.arg); + if(path.is_relative()) + { + path = (sourceDir / path).lexically_normal(); + append(args, cxx_option(toolchain, + ctor::cxx_opt::include_path, path.string())); + } + } + break; + default: + break; + } + + args.push_back(flag); } + } + break; - args.push_back(flag); + default: + break; } return args; diff --git a/src/task_cc.h b/src/task_cc.h index c309abd..70bb13c 100644 --- a/src/task_cc.h +++ b/src/task_cc.h @@ -10,16 +10,16 @@ #include <future> #include <filesystem> -struct BuildConfiguration; -struct Settings; - class TaskCC : public Task { public: - TaskCC(const BuildConfiguration& config, - const Settings& settings, - const std::string& sourceDir, const Source& source); + TaskCC(const ctor::build_configuration& config, + const ctor::settings& settings, + const std::string& sourceDir, const ctor::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,6 +30,8 @@ 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; @@ -42,11 +44,12 @@ protected: 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; + const ctor::build_configuration& config; + const ctor::settings& settings; std::filesystem::path sourceDir; + const ctor::source& _source; }; diff --git a/src/task_fn.cc b/src/task_fn.cc new file mode 100644 index 0000000..b11ff15 --- /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 "ctor.h" +#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) + , 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() << std::endl; + } + + 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..1bad32c --- /dev/null +++ b/src/task_fn.h @@ -0,0 +1,44 @@ +// -*- 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> + +class TaskFn + : public Task +{ +public: + TaskFn(const ctor::build_configuration& config, + const ctor::settings& settings, + const std::string& sourceDir, const ctor::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 ctor::build_configuration& config; + const ctor::settings& settings; + std::filesystem::path sourceDir; +}; diff --git a/src/task_ld.cc b/src/task_ld.cc index 82c26a9..3d917e4 100644 --- a/src/task_ld.cc +++ b/src/task_ld.cc @@ -6,46 +6,30 @@ #include <iostream> #include <fstream> -#include "libctor.h" -#include "settings.h" +#include "ctor.h" #include "execute.h" +#include "util.h" +#include "tools.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 :: - -TaskLD::TaskLD(const BuildConfiguration& config, - const Settings& settings, +TaskLD::TaskLD(const ctor::build_configuration& config, + const ctor::settings& settings, const std::string& target, const std::vector<std::string>& objects, - const std::string& sourcePath) - : Task(config) + const std::string& sourceDir) + : Task(config, settings, sourceDir) , config(config) , settings(settings) + , sourceDir(sourceDir) { target_type = config.type; - if(target_type == TargetType::Auto) + if(target_type == ctor::target_type::automatic) { - target_type = TargetType::Executable; + target_type = ctor::target_type::executable; } - std::filesystem::path base = settings.builddir; - base /= sourcePath; - std::filesystem::create_directories(base); + std::filesystem::create_directories(std::filesystem::path(settings.builddir) / sourceDir); - targetFile = base / target; + _targetFile = target; for(const auto& object : objects) { std::filesystem::path objectFile = object; @@ -55,28 +39,26 @@ 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 = base / targetFile.stem(); + flagsFile = std::filesystem::path(settings.builddir) / cleanUp(sourceDir) / targetFile().stem(); flagsFile += ".flags"; - source_language = Language::C; + source_language = ctor::language::c; for(const auto& source : config.sources) { std::filesystem::path sourceFile(source.file); if(sourceFile.extension().string() != ".c") { - source_language = Language::Cpp; + source_language = ctor::language::cpp; } } } bool TaskLD::dirtyInner() { - if(!std::filesystem::exists(targetFile)) + if(!std::filesystem::exists(targetFile())) { return true; } @@ -86,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) @@ -109,31 +82,20 @@ bool TaskLD::dirtyInner() int TaskLD::runInner() { - std::string objectlist; - for(const auto& objectFile : objectFiles) - { - if(!objectlist.empty()) - { - objectlist += " "; - } - objectlist += objectFile.string(); - } + auto toolchain = 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, ld_option(toolchain, ctor::ld_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, ld_option(toolchain, ctor::ld_opt::link, lib)); } - else if(depFile.extension() == ".a") + else if(depFile.extension() == ".a" || depFile.extension() == ".o") { args.push_back(depFile.string()); } @@ -141,10 +103,10 @@ int TaskLD::runInner() for(const auto& flag : config.flags.ldflags) { - args.push_back(flag); + append(args, to_strings(toolchain, flag)); } - args.push_back("-o"); - args.push_back(targetFile.string()); + + append(args, ld_option(toolchain, ctor::ld_opt::output, targetFile().string())); { // Write flags to file. std::ofstream flagsStream(flagsFile); @@ -153,7 +115,7 @@ int TaskLD::runInner() if(settings.verbose == 0) { - std::cout << "LD => " << targetFile.string() << "\n"; + std::cout << "LD => " << targetFile().string() << std::endl; } auto tool = compiler(); @@ -162,10 +124,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)) @@ -195,7 +157,12 @@ 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 @@ -205,14 +172,20 @@ bool TaskLD::derived() const std::string TaskLD::flagsString() const { + auto toolchain = getToolChain(config.system); std::string flagsStr; + bool first{true}; for(const auto& flag : config.flags.ldflags) { - if(flag != config.flags.ldflags[0]) + for(const auto& str : to_strings(toolchain, flag)) { - flagsStr += " "; + if(first) + { + flagsStr += " "; + first = false; + } + flagsStr += str; } - flagsStr += flag; } flagsStr += "\n"; diff --git a/src/task_ld.h b/src/task_ld.h index 516641f..dbe7db1 100644 --- a/src/task_ld.h +++ b/src/task_ld.h @@ -10,18 +10,16 @@ #include <future> #include <filesystem> -struct BuildConfiguration; -struct Settings; - class TaskLD : public Task { public: - TaskLD(const BuildConfiguration& config, - const Settings& settings, + TaskLD(const ctor::build_configuration& config, + const ctor::settings& settings, const std::string& target, const std::vector<std::string>& objects, - const std::string& sourcePath); + const std::string& _sourceDir); + virtual ~TaskLD() = default; bool dirtyInner() override; @@ -31,6 +29,8 @@ public: std::vector<std::string> depends() const override; std::string target() const override; + std::filesystem::path targetFile() const override; + bool derived() const override; private: @@ -38,9 +38,10 @@ private: 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; + const ctor::build_configuration& config; + const ctor::settings& settings; + std::string sourceDir; }; diff --git a/src/task_so.cc b/src/task_so.cc index bf47e05..9061591 100644 --- a/src/task_so.cc +++ b/src/task_so.cc @@ -6,25 +6,25 @@ #include <iostream> #include <fstream> -#include "libctor.h" -#include "settings.h" +#include "ctor.h" #include "execute.h" #include "util.h" +#include "tools.h" -TaskSO::TaskSO(const BuildConfiguration& config, - const Settings& settings, +TaskSO::TaskSO(const ctor::build_configuration& config, + const ctor::settings& settings, const std::string& target, const std::vector<std::string>& objects, - const std::string& sourcePath) - : Task(config) + const std::string& sourceDir) + : Task(config, settings, sourceDir) , config(config) , settings(settings) + , sourceDir(sourceDir) { - std::filesystem::path base = settings.builddir; - base /= sourcePath; - std::filesystem::create_directories(base); + std::filesystem::path base = sourceDir; + std::filesystem::create_directories(std::filesystem::path(settings.builddir) / base); - targetFile = base / target; + _targetFile = base / target; for(const auto& object : objects) { std::filesystem::path objectFile = object; @@ -34,30 +34,28 @@ 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 = base / targetFile.stem(); + flagsFile = std::filesystem::path(settings.builddir) / cleanUp(sourceDir) / targetFile().stem(); flagsFile += ".flags"; - target_type = TargetType::DynamicLibrary; - source_language = Language::C; + target_type = ctor::target_type::dynamic_library; + source_language = ctor::language::c; for(const auto& source : config.sources) { std::filesystem::path sourceFile(source.file); // TODO: Use task languages instead if(sourceFile.extension().string() != ".c") { - source_language = Language::Cpp; + source_language = ctor::language::cpp; } } } bool TaskSO::dirtyInner() { - if(!std::filesystem::exists(targetFile)) + if(!std::filesystem::exists(targetFile())) { return true; } @@ -67,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) @@ -90,37 +79,23 @@ bool TaskSO::dirtyInner() int TaskSO::runInner() { - std::string objectlist; - for(const auto& objectFile : objectFiles) - { - if(!objectlist.empty()) - { - objectlist += " "; - } - objectlist += objectFile.string(); - } + auto toolchain = 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, ld_option(toolchain, ctor::ld_opt::position_independent_code)); + append(args, ld_option(toolchain, ctor::ld_opt::build_shared)); - for(const auto& objectFile : objectFiles) - { - args.push_back(objectFile.string()); - } + append(args, ld_option(toolchain, ctor::ld_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.flags.ldflags) { - args.push_back(flag); + append(args, to_strings(toolchain, flag)); } { // Write flags to file. @@ -130,7 +105,7 @@ int TaskSO::runInner() if(settings.verbose == 0) { - std::cout << "LD => " << targetFile.string() << "\n"; + std::cout << "LD => " << targetFile().string() << std::endl; } auto tool = compiler(); @@ -139,10 +114,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)) @@ -172,7 +147,12 @@ 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 @@ -182,10 +162,20 @@ bool TaskSO::derived() const std::string TaskSO::flagsString() const { - std::string flagsStr = compiler(); + auto toolchain = getToolChain(config.system); + std::string flagsStr; + bool first{true}; for(const auto& flag : config.flags.ldflags) { - flagsStr += " " + flag; + for(const auto& str : to_strings(toolchain, flag)) + { + if(first) + { + flagsStr += " "; + first = false; + } + flagsStr += str; + } } flagsStr += "\n"; diff --git a/src/task_so.h b/src/task_so.h index a249421..60af225 100644 --- a/src/task_so.h +++ b/src/task_so.h @@ -10,18 +10,16 @@ #include <future> #include <filesystem> -struct BuildConfiguration; -struct Settings; - class TaskSO : public Task { public: - TaskSO(const BuildConfiguration& config, - const Settings& settings, + TaskSO(const ctor::build_configuration& config, + const ctor::settings& settings, const std::string& target, const std::vector<std::string>& objects, - const std::string& sourcePath); + const std::string& sourceDir); + virtual ~TaskSO() = default; bool dirtyInner() override; @@ -31,6 +29,8 @@ public: std::vector<std::string> depends() const override; std::string target() const override; + std::filesystem::path targetFile() const override; + bool derived() const override; private: @@ -38,9 +38,10 @@ private: 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; + const ctor::build_configuration& config; + const ctor::settings& settings; + std::string sourceDir; }; diff --git a/src/tasks.cc b/src/tasks.cc index 8ab296f..67bed2b 100644 --- a/src/tasks.cc +++ b/src/tasks.cc @@ -5,27 +5,28 @@ #include <filesystem> #include <deque> +#include <list> #include <iostream> #include <algorithm> -#include "settings.h" -#include "libctor.h" +#include "ctor.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 ctor::settings& settings, bool resolve_externals) { static bool initialised{false}; static std::deque<Target> targets; if(!initialised) { - const auto& externals = configuration().externals; + const auto& externals = ctor::get_configuration().externals; for(std::size_t i = 0; i < numConfigFiles; ++i) { std::string path = @@ -34,7 +35,7 @@ const std::deque<Target>& getTargets(const Settings& settings, { std::cout << configFiles[i].file << " in path " << path << "\n"; } - auto configs = configFiles[i].cb(); + auto configs = configFiles[i].cb(settings); for(auto& config : configs) { if(resolve_externals) @@ -76,26 +77,32 @@ const std::deque<Target>& getTargets(const Settings& settings, return targets; } -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 ctor::build_configuration& config, + const ctor::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) + ctor::target_type target_type{config.type}; + if(target_type == ctor::target_type::automatic) { - if(targetFile.extension() == ".a") + if(config.function != nullptr) + { + target_type = ctor::target_type::function; + } + else if(targetFile.extension() == ".a") { - target_type = TargetType::StaticLibrary; + target_type = ctor::target_type::static_library; } else if(targetFile.extension() == ".so") { - target_type = TargetType::DynamicLibrary; + target_type = ctor::target_type::dynamic_library; } else if(targetFile.extension() == "") { - target_type = TargetType::Executable; + target_type = ctor::target_type::executable; } else { @@ -106,43 +113,58 @@ 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 != ctor::target_type::function) { - 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<TaskCC>(config, settings, sourceDir, file); + tasks.insert(task); + objects.push_back(task->targetFile().string()); + } } +#ifndef BOOTSTRAP + else + { + 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) { - case TargetType::Auto: + case ctor::target_type::automatic: // The target_type cannot be Auto break; - case TargetType::StaticLibrary: - tasks.emplace_back(std::make_shared<TaskAR>(config, settings, config.target, - objects, sourceDir)); + case ctor::target_type::static_library: + case ctor::target_type::unit_test_library: + tasks.insert(std::make_shared<TaskAR>(config, settings, config.target, + objects, sourceDir)); break; #ifndef BOOTSTRAP - case TargetType::DynamicLibrary: + case ctor::target_type::dynamic_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.emplace_back(std::make_shared<TaskSO>(config, settings, config.target, - objects, sourceDir)); + 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, sourceDir)); + case ctor::target_type::executable: + case ctor::target_type::unit_test: + tasks.insert(std::make_shared<TaskLD>(config, settings, config.target, + objects, sourceDir)); break; - case TargetType::Object: + case ctor::target_type::object: + case ctor::target_type::function: break; #else default: @@ -153,8 +175,8 @@ std::list<std::shared_ptr<Task>> taskFactory(const BuildConfiguration& config, 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(); @@ -172,12 +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, - bool resolve_externals) +std::set<std::shared_ptr<Task>> getTasks(const ctor::settings& settings, + const std::vector<std::string> names, + bool resolve_externals) { auto& targets = getTargets(settings, resolve_externals); - std::list<std::shared_ptr<Task>> tasks; + std::set<std::shared_ptr<Task>> tasks; for(const auto& target : targets) { if(names.empty() || @@ -185,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 f2a77d4..ed1bf60 100644 --- a/src/tasks.h +++ b/src/tasks.h @@ -4,40 +4,37 @@ #pragma once #include <string> -#include <list> +#include <set> #include <memory> #include <deque> #include "task.h" -class BuildConfiguration; -class Settings; - struct Target { - BuildConfiguration config; + ctor::build_configuration config; std::string path; }; //! Get list of all registered targets -const std::deque<Target>& getTargets(const Settings& settings, +const std::deque<Target>& getTargets(const ctor::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 = {}, - bool resolve_externals = true); +std::set<std::shared_ptr<Task>> getTasks(const ctor::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::list<std::shared_ptr<Task>> taskFactory(const BuildConfiguration& config, - const Settings& settings, - const std::string& sourceDir); +std::set<std::shared_ptr<Task>> taskFactory(const ctor::build_configuration& config, + const ctor::settings& settings, + const std::string& sourceDir); diff --git a/src/tools.cc b/src/tools.cc new file mode 100644 index 0000000..28d3903 --- /dev/null +++ b/src/tools.cc @@ -0,0 +1,648 @@ +// -*- c++ -*- +// Distributed under the BSD 2-Clause License. +// See accompanying file LICENSE for details. +#include "tools.h" + +#include <filesystem> +#include <iostream> +#include <sstream> + +#include <cassert> + +std::ostream& operator<<(std::ostream& stream, const ctor::c_opt& opt) +{ + // Adding to this enum should also imply adding to the unit-tests below + switch(opt) + { + case ctor::c_opt::output: stream << "ctor::c_opt::output"; break; + case ctor::c_opt::debug: stream << "ctor::c_opt::debug"; break; + case ctor::c_opt::warn_all: stream << "ctor::c_opt::warn_all"; break; + case ctor::c_opt::warnings_as_errors: stream << "ctor::c_opt::warnings_as_errors"; break; + case ctor::c_opt::generate_dep_tree: stream << "ctor::c_opt::generate_dep_tree"; break; + case ctor::c_opt::no_link: stream << "ctor::c_opt::no_link"; break; + case ctor::c_opt::include_path: stream << "ctor::c_opt::include_path"; break; + case ctor::c_opt::c_std: stream << "ctor::c_opt::c_std"; break; + case ctor::c_opt::optimization: stream << "ctor::c_opt::optimization"; break; + case ctor::c_opt::position_independent_code: stream << "ctor::c_opt::position_independent_code"; break; + case ctor::c_opt::position_independent_executable: stream << "ctor::c_opt::position_independent_executable"; break; + case ctor::c_opt::custom: stream << "ctor::c_opt::custom"; break; + } + + return stream; +} + +std::ostream& operator<<(std::ostream& stream, const ctor::cxx_opt& opt) +{ + // Adding to this enum should also imply adding to the unit-tests below + switch(opt) + { + case ctor::cxx_opt::output: stream << "ctor::cxx_opt::output"; break; + case ctor::cxx_opt::debug: stream << "ctor::cxx_opt::debug"; break; + case ctor::cxx_opt::warn_all: stream << "ctor::cxx_opt::warn_all"; break; + case ctor::cxx_opt::warnings_as_errors: stream << "ctor::cxx_opt::warnings_as_errors"; 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; + case ctor::cxx_opt::cpp_std: stream << "ctor::cxx_opt::cpp_std"; break; + case ctor::cxx_opt::optimization: stream << "ctor::cxx_opt::optimization"; break; + case ctor::cxx_opt::position_independent_code: stream << "ctor::cxx_opt::position_independent_code"; break; + case ctor::cxx_opt::position_independent_executable: stream << "ctor::cxx_opt::position_independent_executable"; break; + case ctor::cxx_opt::custom: stream << "ctor::cxx_opt::custom"; break; + } + + return stream; +} + +std::ostream& operator<<(std::ostream& stream, const ctor::ld_opt& opt) +{ + // Adding to this enum should also imply adding to the unit-tests below + switch(opt) + { + case ctor::ld_opt::output: stream << "ctor::ld_opt::output"; break; + case ctor::ld_opt::strip: stream << "ctor::ld_opt::strip"; break; + case ctor::ld_opt::warn_all: stream << "ctor::ld_opt::warn_all"; break; + case ctor::ld_opt::warnings_as_errors: stream << "ctor::ld_opt::warnings_as_errors"; break; + case ctor::ld_opt::library_path: stream << "ctor::ld_opt::library_path"; break; + case ctor::ld_opt::link: stream << "ctor::ld_opt::link"; break; + case ctor::ld_opt::cpp_std: stream << "ctor::ld_opt::cpp_std"; break; + case ctor::ld_opt::build_shared: stream << "ctor::ld_opt::build_shared"; break; + case ctor::ld_opt::threads: stream << "ctor::ld_opt::threads"; break; + case ctor::ld_opt::position_independent_code: stream << "ctor::ld_opt::position_independent_code"; break; + case ctor::ld_opt::position_independent_executable: stream << "ctor::ld_opt::position_independent_executable"; break; + case ctor::ld_opt::custom: stream << "ctor::ld_opt::custom"; break; + } + + return stream; +} + +std::ostream& operator<<(std::ostream& stream, const ctor::ar_opt& opt) +{ + // Adding to this enum should also imply adding to the unit-tests below + switch(opt) + { + case ctor::ar_opt::replace: stream << "ctor::ar_opt::replace"; break; + case ctor::ar_opt::add_index: stream << "ctor::ar_opt::add_index"; break; + case ctor::ar_opt::create: stream << "ctor::ar_opt::create"; break; + case ctor::ar_opt::output: stream << "ctor::ar_opt::output"; break; + case ctor::ar_opt::custom: stream << "ctor::ar_opt::custom"; break; + } + + return stream; +} + +std::ostream& operator<<(std::ostream& stream, const ctor::asm_opt& opt) +{ + // Adding to this enum should also imply adding to the unit-tests below + switch(opt) + { + case ctor::asm_opt::custom: stream << "ctor::asm_opt::custom"; break; + } + + return stream; +} + +ctor::toolchain getToolChain(const std::string& compiler) +{ + 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 ctor::toolchain::clang; + } + else if(cc_cmd.find("g++") != std::string::npos) + { + return ctor::toolchain::gcc; + } + + std::cerr << "Unsupported output system.\n"; + return ctor::toolchain::gcc; +} + +ctor::toolchain getToolChain(ctor::output_system system) +{ + const auto& cfg = ctor::get_configuration(); + if(system == ctor::output_system::host) + { + if(cfg.host_toolchain != ctor::toolchain::none) + { + return cfg.host_toolchain; + } + return getToolChain(cfg.get(ctor::cfg::host_cxx, "g++")); + } + else + { + if(cfg.build_toolchain != ctor::toolchain::none) + { + return cfg.build_toolchain; + } + return getToolChain(cfg.get(ctor::cfg::build_cxx, "g++")); + } +} + +namespace gcc { +ctor::c_flag c_option(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 { ctor::c_opt::include_path, path }; + } + + return { ctor::c_opt::custom, flag }; +} + +ctor::cxx_flag cxx_option(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 { ctor::cxx_opt::include_path, path }; + } + + return { ctor::cxx_opt::custom, flag }; +} + +ctor::ld_flag ld_option(const std::string& flag) +{ + if(flag.substr(0, 2) == "-L") + { + std::string path = flag.substr(2); + path.erase(0, path.find_first_not_of(' ')); + return { ctor::ld_opt::library_path, path }; + } + + return { ctor::ld_opt::custom, flag }; +} + +ctor::ar_flag ar_option(const std::string& flag) +{ + return { ctor::ar_opt::custom, flag }; +} + +std::vector<std::string> cxx_option(ctor::cxx_opt opt, const std::string& arg) +{ + switch(opt) + { + case ctor::cxx_opt::output: + return {"-o", arg}; + case ctor::cxx_opt::debug: + return {"-g"}; + case ctor::cxx_opt::warn_all: + return {"-Wall"}; + case ctor::cxx_opt::warnings_as_errors: + return {"-Werror"}; + 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}; + case ctor::cxx_opt::cpp_std: + return {"-std=" + arg}; + case ctor::cxx_opt::optimization: + return {"-O" + arg}; + case ctor::cxx_opt::position_independent_code: + return {"-fPIC"}; + case ctor::cxx_opt::position_independent_executable: + return {"-fPIE"}; + case ctor::cxx_opt::custom: + return {arg}; + } + + std::cerr << "Unsupported compiler option.\n"; + return {}; +} + +std::vector<std::string> c_option(ctor::c_opt opt, const std::string& arg) +{ + switch(opt) + { + case ctor::c_opt::output: + return {"-o", arg}; + case ctor::c_opt::debug: + return {"-g"}; + case ctor::c_opt::warn_all: + return {"-Wall"}; + case ctor::c_opt::warnings_as_errors: + return {"-Werror"}; + case ctor::c_opt::generate_dep_tree: + return {"-MMD"}; + case ctor::c_opt::no_link: + return {"-c"}; + case ctor::c_opt::include_path: + return {"-I" + arg}; + case ctor::c_opt::c_std: + return {"-std=" + arg}; + case ctor::c_opt::optimization: + return {"-O" + arg}; + case ctor::c_opt::position_independent_code: + return {"-fPIC"}; + case ctor::c_opt::position_independent_executable: + return {"-fPIE"}; + case ctor::c_opt::custom: + return {arg}; + } + + std::cerr << "Unsupported compiler option.\n"; + return {}; +} + +std::vector<std::string> ld_option(ctor::ld_opt opt, const std::string& arg) +{ + switch(opt) + { + case ctor::ld_opt::output: + return {"-o", arg}; + case ctor::ld_opt::strip: + return {"-s"}; + 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}; + case ctor::ld_opt::link: + return {"-l" + arg}; + case ctor::ld_opt::cpp_std: + return {"-std=" + arg}; + case ctor::ld_opt::build_shared: + return {"-shared"}; + case ctor::ld_opt::threads: + return {"-pthread"}; + case ctor::ld_opt::position_independent_code: + return {"-fPIC"}; + case ctor::ld_opt::position_independent_executable: + return {"-fPIE"}; + case ctor::ld_opt::custom: + return {arg}; + } + + std::cerr << "Unsupported compiler option.\n"; + return {}; +} + +std::vector<std::string> ar_option(ctor::ar_opt opt, const std::string& arg) +{ + switch(opt) + { + case ctor::ar_opt::replace: + return {"-r"}; + case ctor::ar_opt::add_index: + return {"-s"}; + case ctor::ar_opt::create: + return {"-c"}; + case ctor::ar_opt::output: + return {arg}; + case ctor::ar_opt::custom: + return {arg}; + } + + std::cerr << "Unsupported compiler option.\n"; + return {}; +} + +std::vector<std::string> asm_option(ctor::asm_opt opt, const std::string& arg) +{ + switch(opt) + { + case ctor::asm_opt::custom: + return {arg}; + } + + std::cerr << "Unsupported compiler option.\n"; + return {}; +} +} // gcc:: + + +std::vector<std::string> c_option(ctor::toolchain toolchain, + ctor::c_opt opt, + const std::string& arg) +{ + switch(toolchain) + { + case ctor::toolchain::gcc: + case ctor::toolchain::clang: + return gcc::c_option(opt, arg); + case ctor::toolchain::any: + { + std::ostringstream ss; + ss << "{" << opt; + if(!arg.empty()) + { + ss << ", \"" << arg << "\""; + } + ss << "}"; + return { ss.str() }; + } + case ctor::toolchain::none: + break; + } + + std::cerr << "Unsupported tool-chain.\n"; + return {}; +} + +std::vector<std::string> cxx_option(ctor::toolchain toolchain, + ctor::cxx_opt opt, + const std::string& arg) +{ + switch(toolchain) + { + case ctor::toolchain::gcc: + case ctor::toolchain::clang: + return gcc::cxx_option(opt, arg); + case ctor::toolchain::any: + { + std::ostringstream ss; + ss << "{" << opt; + if(!arg.empty()) + { + ss << ", \"" << arg << "\""; + } + ss << "}"; + return { ss.str() }; + } + case ctor::toolchain::none: + break; + } + + std::cerr << "Unsupported tool-chain.\n"; + return {}; +} + +std::vector<std::string> ld_option(ctor::toolchain toolchain, + ctor::ld_opt opt, + const std::string& arg) +{ + switch(toolchain) + { + case ctor::toolchain::gcc: + case ctor::toolchain::clang: + return gcc::ld_option(opt, arg); + case ctor::toolchain::any: + { + std::ostringstream ss; + ss << "{" << opt; + if(!arg.empty()) + { + ss << ", \"" << arg << "\""; + } + ss << "}"; + return { ss.str() }; + } + case ctor::toolchain::none: + break; + } + + std::cerr << "Unsupported tool-chain.\n"; + return {}; +} + +std::vector<std::string> ar_option(ctor::toolchain toolchain, + ctor::ar_opt opt, + const std::string& arg) +{ + switch(toolchain) + { + case ctor::toolchain::gcc: + case ctor::toolchain::clang: + return gcc::ar_option(opt, arg); + case ctor::toolchain::any: + { + std::ostringstream ss; + ss << "{" << opt; + if(!arg.empty()) + { + ss << ", \"" << arg << "\""; + } + ss << "}"; + return { ss.str() }; + } + case ctor::toolchain::none: + break; + } + + std::cerr << "Unsupported tool-chain.\n"; + return {}; +} + +std::vector<std::string> asm_option(ctor::toolchain toolchain, + ctor::asm_opt opt, + const std::string& arg) +{ + switch(toolchain) + { + case ctor::toolchain::gcc: + case ctor::toolchain::clang: + return gcc::asm_option(opt, arg); + case ctor::toolchain::any: + { + std::ostringstream ss; + ss << "{" << opt; + if(!arg.empty()) + { + ss << ", \"" << arg << "\""; + } + ss << "}"; + return { ss.str() }; + } + case ctor::toolchain::none: + break; + } + + std::cerr << "Unsupported tool-chain.\n"; + return {}; +} + + +ctor::c_flag c_option(const std::string& flag, ctor::toolchain toolchain) +{ + switch(toolchain) + { + case ctor::toolchain::gcc: + case ctor::toolchain::clang: + return gcc::c_option(flag); + case ctor::toolchain::any: + case ctor::toolchain::none: + break; + } + + return { ctor::c_opt::custom, flag }; +} + +ctor::cxx_flag cxx_option(const std::string& flag, ctor::toolchain toolchain) +{ + switch(toolchain) + { + case ctor::toolchain::gcc: + case ctor::toolchain::clang: + return gcc::cxx_option(flag); + case ctor::toolchain::any: + case ctor::toolchain::none: + break; + } + + return { ctor::cxx_opt::custom, flag }; +} + +ctor::ld_flag ld_option(const std::string& flag, ctor::toolchain toolchain) +{ + switch(toolchain) + { + case ctor::toolchain::gcc: + case ctor::toolchain::clang: + return gcc::ld_option(flag); + case ctor::toolchain::any: + case ctor::toolchain::none: + break; + } + + return { ctor::ld_opt::custom, flag }; +} + +ctor::ar_flag ar_option(const std::string& flag, ctor::toolchain toolchain) +{ + switch(toolchain) + { + case ctor::toolchain::gcc: + case ctor::toolchain::clang: + return gcc::ar_option(flag); + case ctor::toolchain::any: + case ctor::toolchain::none: + break; + } + + return { ctor::ar_opt::custom, flag }; +} + +ctor::asm_flag asm_option(const std::string& flag, ctor::toolchain toolchain) +{ + switch(toolchain) + { + case ctor::toolchain::gcc: + case ctor::toolchain::clang: + case ctor::toolchain::any: + case ctor::toolchain::none: + break; + } + + return { ctor::asm_opt::custom, flag }; +} + +// Flag to string coversions + +std::vector<std::string> to_strings(ctor::toolchain toolchain, + const ctor::c_flag& flag) +{ + if(flag.toolchain == ctor::toolchain::any || + flag.toolchain == toolchain) + { + return c_option(toolchain, flag.opt, flag.arg); + } + + return {}; +} + +std::vector<std::string> to_strings(ctor::toolchain toolchain, + const ctor::cxx_flag& flag) +{ + if(flag.toolchain == ctor::toolchain::any || + flag.toolchain == toolchain) + { + return cxx_option(toolchain, flag.opt, flag.arg); + } + + return {}; +} + +std::vector<std::string> to_strings(ctor::toolchain toolchain, + const ctor::ld_flag& flag) +{ + if(flag.toolchain == ctor::toolchain::any || + flag.toolchain == toolchain) + { + return ld_option(toolchain, flag.opt, flag.arg); + } + + return {}; +} + +std::vector<std::string> to_strings(ctor::toolchain toolchain, + const ctor::ar_flag& flag) +{ + if(flag.toolchain == ctor::toolchain::any || + flag.toolchain == toolchain) + { + return ar_option(toolchain, flag.opt, flag.arg); + } + + return {}; +} + +std::vector<std::string> to_strings(ctor::toolchain toolchain, + const ctor::asm_flag& flag) +{ + if(flag.toolchain == ctor::toolchain::any || + flag.toolchain == toolchain) + { + return asm_option(toolchain, flag.opt, flag.arg); + } + + return {}; +} + +namespace { +ctor::toolchain guess_toolchain(const std::string& opt) +{ + if(opt.empty()) + { + return ctor::toolchain::any; + } + + if(opt[0] == '-') + { + return ctor::toolchain::gcc; + } + + //if(opt[0] == '/') + //{ + // return ctor::toolchain::msvc; + //} + return ctor::toolchain::any; +} +} + +template<> +ctor::flag<ctor::c_opt>::flag(const char* str) +{ + *this = c_option(str, guess_toolchain(str)); +} + +template<> +ctor::flag<ctor::cxx_opt>::flag(const char* str) +{ + *this = cxx_option(str, guess_toolchain(str)); +} + +template<> +ctor::flag<ctor::ld_opt>::flag(const char* str) +{ + *this = ld_option(str, guess_toolchain(str)); +} + +template<> +ctor::flag<ctor::ar_opt>::flag(const char* str) +{ + *this = ar_option(str, guess_toolchain(str)); +} + +template<> +ctor::flag<ctor::asm_opt>::flag(const char* str) +{ + *this = asm_option(str, guess_toolchain(str)); +} diff --git a/src/tools.h b/src/tools.h new file mode 100644 index 0000000..b1285ec --- /dev/null +++ b/src/tools.h @@ -0,0 +1,96 @@ +// -*- c++ -*- +// Distributed under the BSD 2-Clause License. +// See accompanying file LICENSE for details. +#pragma once + +#include <vector> +#include <string> +#include <ostream> + +#include "ctor.h" + + +std::ostream& operator<<(std::ostream& stream, const ctor::c_opt& opt); +std::ostream& operator<<(std::ostream& stream, const ctor::cxx_opt& opt); +std::ostream& operator<<(std::ostream& stream, const ctor::ld_opt& opt); +std::ostream& operator<<(std::ostream& stream, const ctor::ar_opt& opt); +std::ostream& operator<<(std::ostream& stream, const ctor::asm_opt& opt); + + +//! Get tool-chain type from compiler path string +ctor::toolchain getToolChain(const std::string& compiler); + +//! Get tool-chain type from output system (via configuration) +ctor::toolchain getToolChain(ctor::output_system system); + + + +//! Get tool argument(s) for specific option type matching the supplied +//! tool-chain +std::vector<std::string> c_option(ctor::toolchain toolchain, + ctor::c_opt option, + const std::string& arg = {}); + +//! 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 = {}); + +//! 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 = {}); + +//! 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 = {}); + +//! 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 = {}); + + + +//! 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); + +//! 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); + +//! 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); + +//! 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); + +//! 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); + + + +std::vector<std::string> to_strings(ctor::toolchain toolchain, + const ctor::cxx_flag& flag); +std::vector<std::string> to_strings(ctor::toolchain toolchain, + const ctor::c_flag& flag); +std::vector<std::string> to_strings(ctor::toolchain toolchain, + const ctor::ld_flag& flag); +std::vector<std::string> to_strings(ctor::toolchain toolchain, + const ctor::ar_flag& flag); +std::vector<std::string> to_strings(ctor::toolchain toolchain, + const ctor::asm_flag& flag); diff --git a/src/unittest.cc b/src/unittest.cc index ade2d0a..237b2e3 100644 --- a/src/unittest.cc +++ b/src/unittest.cc @@ -6,29 +6,33 @@ #include <iostream> #include "execute.h" -#include "settings.h" #include "task.h" -int runUnitTests(std::list<std::shared_ptr<Task>>& tasks, - const Settings& settings) +int runUnitTests(std::set<std::shared_ptr<Task>>& tasks, + const ctor::settings& settings) { bool ok{true}; - std::cout << "Running unit-tests:\n"; + std::cout << "Running unit-tests:" << std::endl; // Run unit-tests for(const auto& task : tasks) { - if(task->targetType() == TargetType::UnitTest) + if(task->targetType() == ctor::target_type::unit_test) { - 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" << std::endl; } else { - std::cout << "FAILED\n"; + std::cout << " FAILED" << std::endl; } } } diff --git a/src/unittest.h b/src/unittest.h index 7eef0e2..2880319 100644 --- a/src/unittest.h +++ b/src/unittest.h @@ -3,11 +3,14 @@ // 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, - const Settings& settings); +namespace ctor { +struct settings; +} // namespace ctor:: + +int runUnitTests(std::set<std::shared_ptr<Task>>& tasks, + const ctor::settings& settings); diff --git a/src/util.cc b/src/util.cc index 9bf83cc..ee56ede 100644 --- a/src/util.cc +++ b/src/util.cc @@ -80,12 +80,12 @@ std::vector<std::string> readDeps(const std::string& depFile) return output; } -Language languageFromExtension(const std::filesystem::path& file) +ctor::language languageFromExtension(const std::filesystem::path& file) { auto ext = file.extension().string(); if(ext == ".c") { - return Language::C; + return ctor::language::c; } if(ext == ".C" || @@ -96,17 +96,42 @@ Language languageFromExtension(const std::filesystem::path& file) ext == ".cp" || ext == ".cxx") { - return Language::Cpp; + return ctor::language::cpp; } if(ext == ".s" || ext == ".S" || ext == ".asm") { - return Language::Asm; + return ctor::language::assembler; } 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; +} @@ -3,11 +3,23 @@ // See accompanying file LICENSE for details. #pragma once -#include "libctor.h" +#include "ctor.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); +ctor::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()); +} + +//using cxx_flags = std::vector<ctor::cxx_flag>; +//using c_flags = std::vector<std::string>; +//using ld_flags= std::vector<std::string>; +//using asm_flags = std::vector<std::string>; diff --git a/test/ctor.cc b/test/ctor.cc index 6515c72..a2ad64d 100644 --- a/test/ctor.cc +++ b/test/ctor.cc @@ -1,16 +1,16 @@ // -*- c++ -*- // Distributed under the BSD 2-Clause License. // See accompanying file LICENSE for details. -#include <libctor.h> +#include <ctor.h> namespace { -BuildConfigurations ctorTestConfigs() +ctor::build_configurations ctorTestConfigs(const ctor::settings& settings) { return { { - .type = TargetType::UnitTest, + .type = ctor::target_type::unit_test, .target = "execute_test", .sources = { "execute_test.cc", @@ -19,7 +19,7 @@ BuildConfigurations ctorTestConfigs() }, .flags = { .cxxflags = { - "-std=c++20", "-O3", "-s", "-Wall", "-Werror", + "-std=c++20", "-O3", "-Wall", "-Werror", "-I../src", "-Iuunit", "-DOUTPUT=\"execute\"", }, @@ -27,16 +27,16 @@ BuildConfigurations ctorTestConfigs() }, }, { - .type = TargetType::UnitTest, + .type = ctor::target_type::unit_test, .target = "tasks_test", .sources = { "tasks_test.cc", "testmain.cc", }, - .depends = {"libctor.a"}, + .depends = { "libctor_nomain.a" }, .flags = { .cxxflags = { - "-std=c++20", "-O3", "-s", "-Wall", "-Werror", + "-std=c++20", "-O3", "-Wall", "-Werror", "-I../src", "-Iuunit", "-DOUTPUT=\"tasks\"", }, @@ -44,22 +44,66 @@ BuildConfigurations ctorTestConfigs() }, }, { - .type = TargetType::UnitTest, + .type = ctor::target_type::unit_test, .target = "source_type_test", .sources = { "source_type_test.cc", "testmain.cc", }, - .depends = {"libctor.a"}, + .depends = { "libctor_nomain.a" }, .flags = { .cxxflags = { - "-std=c++20", "-O3", "-s", "-Wall", "-Werror", + "-std=c++20", "-O3", "-Wall", "-Werror", "-I../src", "-Iuunit", "-DOUTPUT=\"source_type\"", }, .ldflags = { "-pthread" }, }, }, + { + .type = ctor::target_type::unit_test, + .target = "tools_test", + .sources = { + "tools_test.cc", + "testmain.cc", + "../src/tools.cc", + }, + //.depends = { "libctor_nomain.a" }, + .flags = { + .cxxflags = { + "-std=c++20", "-O3", "-Wall", "-Werror", + "-I../src", "-Iuunit", + "-DOUTPUT=\"tools\"", + }, + }, + }, + { + .type = ctor::target_type::unit_test_library, + .target = "libctor_nomain.a", + .sources = { + "../src/build.cc", + "../src/configure.cc", + "../src/execute.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", + "-I../src", + }, + .ldflags = { "-pthread" }, + }, + }, }; } } diff --git a/test/source_type_test.cc b/test/source_type_test.cc index 47d820d..288f1e5 100644 --- a/test/source_type_test.cc +++ b/test/source_type_test.cc @@ -1,38 +1,40 @@ -#include <uunit.h> - -#include <libctor.h> +#include <ctor.h> #include <task_cc.h> -#include <settings.h> -std::ostream& operator<<(std::ostream& stream, const Language& lang) +std::ostream& operator<<(std::ostream& stream, const ctor::language& lang); + +#include <uunit.h> + +std::ostream& operator<<(std::ostream& stream, const ctor::language& lang) { switch(lang) { - case Language::Auto: - stream << "Language::Auto"; + case ctor::language::automatic: + stream << "ctor::language::automatic"; break; - case Language::C: - stream << "Language::C"; + case ctor::language::c: + stream << "ctor::language::c"; break; - case Language::Cpp: - stream << "Language::Cpp"; + case ctor::language::cpp: + stream << "ctor::language::cpp"; break; - case Language::Asm: - stream << "Language::Asm"; + case ctor::language::assembler: + stream << "ctor::language::assembler"; break; } return stream; } + class TestableTaskCC : public TaskCC { public: - TestableTaskCC(const Source& source) + TestableTaskCC(const ctor::source& source) : TaskCC({}, {}, "build", source) {} - Language language() const + ctor::language language() const { return source_language; } @@ -51,22 +53,22 @@ public: { { // c++ TestableTaskCC task("hello.cc"); - uASSERT_EQUAL(Language::Cpp, task.language()); + uASSERT_EQUAL(ctor::language::cpp, task.language()); } { // c TestableTaskCC task("hello.c"); - uASSERT_EQUAL(Language::C, task.language()); + uASSERT_EQUAL(ctor::language::c, task.language()); } { // asm TestableTaskCC task("hello.s"); - uASSERT_EQUAL(Language::Asm, task.language()); + uASSERT_EQUAL(ctor::language::assembler, task.language()); } { // custom/explicit language - TestableTaskCC task( {"hello.foo", Language::Asm} ); - uASSERT_EQUAL(Language::Asm, task.language()); + TestableTaskCC task( {"hello.foo", ctor::language::assembler} ); + uASSERT_EQUAL(ctor::language::assembler, task.language()); } // Note: Failure state will result in exit(1) so cannot be tested diff --git a/test/suite/ctor_files/ctor.cc.bar b/test/suite/ctor_files/ctor.cc.bar index 92456cb..218f9cc 100644 --- a/test/suite/ctor_files/ctor.cc.bar +++ b/test/suite/ctor_files/ctor.cc.bar @@ -1,12 +1,12 @@ // -*- c++ -*- // Distributed under the BSD 2-Clause License. // See accompanying file LICENSE for details. -#include <libctor.h> +#include <ctor.h> //#include "config.h" namespace { -BuildConfigurations ctorConfigs() +ctor::build_configurations ctorConfigs(const ctor::settings& settings) { return { @@ -30,17 +30,19 @@ BuildConfigurations ctorConfigs() }; } -ExternalConfigurations ctorExtConfigs() +ctor::external_configurations ctorExtConfigs(const ctor::settings& settings) { return { { .name = "bar", - .flags = { - .cxxflags = { "-D_A_", "-DBAR"}, - .cflags = { "-D_B_" }, - .ldflags = { "-D_C_" }, - .asmflags = { "-D_D_" }, + .external = ctor::external_manual{ + .flags = { + .cflags = { "-D_B_" }, + .cxxflags = { "-D_A_", "-DBAR"}, + .ldflags = { "-D_C_" }, + .asmflags = { "-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 6c60513..eab39c4 100644 --- a/test/suite/ctor_files/ctor.cc.base +++ b/test/suite/ctor_files/ctor.cc.base @@ -1,12 +1,12 @@ // -*- c++ -*- // Distributed under the BSD 2-Clause License. // See accompanying file LICENSE for details. -#include <libctor.h> +#include <ctor.h> //#include "config.h" namespace { -BuildConfigurations ctorConfigs() +ctor::build_configurations ctorConfigs(const ctor::settings& settings) { return { @@ -30,17 +30,20 @@ BuildConfigurations ctorConfigs() }; } -ExternalConfigurations ctorExtConfigs() +ctor::external_configurations ctorExtConfigs(const ctor::settings& settings) { return { { .name = "bar", - .flags = { - .cxxflags = { "-D_A_", "-DFOO"}, - .cflags = { "-D_B_" }, - .ldflags = { "-D_C_" }, - .asmflags = { "-D_D_" }, + .external = ctor::external_manual + { + .flags = { + .cflags = { "-D_B_" }, + .cxxflags = { "-D_A_", "-DFOO"}, + .ldflags = { "-D_C_" }, + .asmflags = { "-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.multi b/test/suite/ctor_files/ctor.cc.multi index 9db2517..2b88afe 100644 --- a/test/suite/ctor_files/ctor.cc.multi +++ b/test/suite/ctor_files/ctor.cc.multi @@ -1,14 +1,14 @@ // -*- c++ -*- // Distributed under the BSD 2-Clause License. // See accompanying file LICENSE for details. -#include <libctor.h> +#include <ctor.h> //#include "config.h" #include "foobar.h" namespace { -BuildConfigurations ctorConfigs() +ctor::build_configurations ctorConfigs(const ctor::settings& settings) { return { @@ -32,17 +32,19 @@ BuildConfigurations ctorConfigs() }; } -ExternalConfigurations ctorExtConfigs() +ctor::external_configurations ctorExtConfigs(const ctor::settings& settings) { return { { .name = "bar", - .flags = { - .cxxflags = { "-D_A_", "-DFOO"}, - .cflags = { "-D_B_" }, - .ldflags = { "-D_C_" }, - .asmflags = { "-D_D_" }, + .external = ctor::external_manual{ + .flags = { + .cflags = { "-D_B_" }, + .cxxflags = { "-D_A_", "-DFOO"}, + .ldflags = { "-D_C_" }, + .asmflags = { "-D_D_" }, + }, }, // Creates --with-foo-prefix arg to configure which will be used for // -L and -I flags. diff --git a/test/suite/test.sh b/test/suite/test.sh index c980154..c112351 100755 --- a/test/suite/test.sh +++ b/test/suite/test.sh @@ -1,4 +1,9 @@ #!/bin/bash +: ${CXX:=g++} +: ${CTORDIR:=../../build} +: ${BUILDDIR:=build} + +CXX=$(which $CXX) function fail { @@ -13,21 +18,22 @@ function ctor } # Wipe the board -rm -Rf build +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 -g++ -pthread -std=c++20 -L../../build -lctor -I../../src ctor.cc -o ctor || fail ${LINENO} +$CXX -pthread -std=c++20 -L${CTORDIR} -lctor -I../../src ctor.cc -o ctor || fail ${LINENO} # No build files should have been created yet -[ -d build ] && fail ${LINENO} +[ -d ${BUILDDIR} ] && fail ${LINENO} # capture md5 sum of ctor binary before configure is called MD5=`md5sum ctor` -ctor configure --ctor-includedir ../../src --ctor-libdir ../../build +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} @@ -36,7 +42,7 @@ ctor configure --ctor-includedir ../../src --ctor-libdir ../../build [ ! -f configuration.cc ] && fail ${LINENO} # Shouldn't compile anything yet - only configure -[ -f build/hello-hello_cc.o ] && fail ${LINENO} +[ -f ${BUILDDIR}/hello-hello_cc.o ] && fail ${LINENO} MD5=`md5sum ctor` @@ -44,12 +50,12 @@ MD5=`md5sum ctor` ctor -v # Compiled object should now exist -[ ! -f build/hello-hello_cc.o ] && fail ${LINENO} +[ ! -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 -c %Y build/hello-hello_cc.o` +MOD1=`stat -c %Y ${BUILDDIR}/hello-hello_cc.o` touch hello.cc sleep 1.1 @@ -57,10 +63,11 @@ sleep 1.1 ctor -v # Object file should have been recompiled -MOD2=`stat -c %Y build/hello-hello_cc.o` +MOD2=`stat -c %Y ${BUILDDIR}/hello-hello_cc.o` [[ $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` @@ -71,22 +78,23 @@ sleep 1.1 # Run normally to reconfigure, rebuild ctor and rebuild hello.cc ctor -v -MOD2=`stat -c %Y build/hello-hello_cc.o` +MOD2=`stat -c %Y ${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 -c %Y build/hello-hello_cc.o` +MOD1=`stat -c %Y ${BUILDDIR}/hello-hello_cc.o` sleep 1.1 # Run normally to reconfigure, rebuild ctor and rebuild hello.cc ctor -v -MOD2=`stat -c %Y build/hello-hello_cc.o` +MOD2=`stat -c %Y ${BUILDDIR}/hello-hello_cc.o` [[ $MOD1 == $MOD2 ]] && fail ${LINENO} (echo $MD5C | md5sum --status -c) && fail ${LINENO} (echo $MD5 | md5sum --status -c) && fail ${LINENO} @@ -102,3 +110,5 @@ ctor -v MOD2=`stat -c %Y ctor` [[ $MOD1 == $MOD2 ]] && fail ${LINENO} + +exit 0 diff --git a/test/tasks_test.cc b/test/tasks_test.cc index 8a15fcd..5f1db26 100644 --- a/test/tasks_test.cc +++ b/test/tasks_test.cc @@ -1,12 +1,11 @@ #include <uunit.h> -#include <libctor.h> +#include <ctor.h> #include <tasks.h> -#include <settings.h> namespace { -BuildConfigurations ctorTestConfigs1() +ctor::build_configurations ctorTestConfigs1(const ctor::settings&) { return { @@ -20,7 +19,7 @@ BuildConfigurations ctorTestConfigs1() }; } -BuildConfigurations ctorTestConfigs2() +ctor::build_configurations ctorTestConfigs2(const ctor::settings&) { return { @@ -37,6 +36,19 @@ BuildConfigurations ctorTestConfigs2() REG(ctorTestConfigs1); REG(ctorTestConfigs2); +std::size_t count(const std::set<std::shared_ptr<Task>>& tasks, + const std::string& name) +{ + auto cnt{0u}; + for(const auto& task : tasks) + { + if(task->target() == name) + { + cnt++; + } + } + return cnt; +} class TestTask : public Task @@ -44,7 +56,7 @@ class TestTask public: TestTask(const std::string& name, bool dirty, const std::vector<std::string>& deps = {}) - : Task({}) + : Task({}, {}, {}) , task_name(name) , task_dirty(dirty) , task_deps(deps) @@ -55,6 +67,7 @@ public: int clean() override { return 0; } std::vector<std::string> depends() const override { return task_deps; } std::string target() const override { return task_name; } + std::filesystem::path targetFile() const override { return {}; } bool derived() const override { return false; } bool dirtyInner() override { return task_dirty; } @@ -78,7 +91,7 @@ public: void getTargets_test() { using namespace std::string_literals; - Settings settings{}; + ctor::settings settings{}; const auto& targets = getTargets(settings); uASSERT_EQUAL(4u, targets.size()); @@ -96,34 +109,26 @@ public: void getTasks_test() { using namespace std::string_literals; - Settings settings{ .builddir = "foo" }; + ctor::settings settings{ .builddir = "foo" }; { auto tasks = getTasks(settings); uASSERT_EQUAL(6u, tasks.size()); - auto task = tasks.begin(); - uASSERT_EQUAL("foo/test/target1-foo_cc.o"s, (*task)->target()); - task++; - uASSERT_EQUAL("foo/test/target1-bar_c.o"s, (*task)->target()); - task++; - uASSERT_EQUAL("foo/test/target1"s, (*task)->target()); - task++; - uASSERT_EQUAL("foo/test/target2"s, (*task)->target()); - task++; - uASSERT_EQUAL("foo/test/target3"s, (*task)->target()); - task++; - uASSERT_EQUAL("foo/test/target4"s, (*task)->target()); + // Note: count() is used here because the order of + // std::set<std::shared_ptr<T>> is not deterministic. + uASSERT_EQUAL(1u, count(tasks, "target1"s)); + uASSERT_EQUAL(1u, count(tasks, "target2"s)); + uASSERT_EQUAL(1u, count(tasks, "target3"s)); + uASSERT_EQUAL(1u, count(tasks, "target4"s)); + uASSERT_EQUAL(1u, count(tasks, "test/target1-foo_cc.o"s)); + uASSERT_EQUAL(1u, count(tasks, "test/target1-bar_c.o"s)); } { auto tasks = getTasks(settings, {"target1", "target3"}); uASSERT_EQUAL(4u, tasks.size()); - auto task = tasks.begin(); - uASSERT_EQUAL("foo/test/target1-foo_cc.o"s, (*task)->target()); - task++; - uASSERT_EQUAL("foo/test/target1-bar_c.o"s, (*task)->target()); - task++; - uASSERT_EQUAL("foo/test/target1"s, (*task)->target()); - task++; - uASSERT_EQUAL("foo/test/target3"s, (*task)->target()); + uASSERT_EQUAL(1u, count(tasks, "target1"s)); + uASSERT_EQUAL(1u, count(tasks, "target3"s)); + uASSERT_EQUAL(1u, count(tasks, "test/target1-foo_cc.o"s)); + uASSERT_EQUAL(1u, count(tasks, "test/target1-bar_c.o"s)); } { auto tasks = getTasks(settings, {"no-such-target"}); @@ -134,11 +139,11 @@ public: void getNextTask_test() { using namespace std::string_literals; - Settings settings{}; + ctor::settings settings{}; { // Zero (Empty) - std::list<std::shared_ptr<Task>> allTasks; - std::list<std::shared_ptr<Task>> dirtyTasks; + std::set<std::shared_ptr<Task>> allTasks; + std::set<std::shared_ptr<Task>> dirtyTasks; for(auto& task : dirtyTasks) { @@ -151,10 +156,10 @@ public: { // Zero (One task, no dirty) auto task1 = std::make_shared<TestTask>("task1", false); - std::list<std::shared_ptr<Task>> allTasks; - allTasks.push_back(task1); + std::set<std::shared_ptr<Task>> allTasks; + allTasks.insert(task1); - std::list<std::shared_ptr<Task>> dirtyTasks; + std::set<std::shared_ptr<Task>> dirtyTasks; for(auto& task : dirtyTasks) { @@ -167,11 +172,11 @@ public: { // One (One task, one dirty) auto task1 = std::make_shared<TestTask>("task1", true); - std::list<std::shared_ptr<Task>> allTasks; - allTasks.push_back(task1); + std::set<std::shared_ptr<Task>> allTasks; + allTasks.insert(task1); - std::list<std::shared_ptr<Task>> dirtyTasks; - dirtyTasks.push_back(task1); + std::set<std::shared_ptr<Task>> dirtyTasks; + dirtyTasks.insert(task1); for(auto& task : dirtyTasks) { @@ -186,12 +191,12 @@ public: auto task1 = std::make_shared<TestTask>("task1", false); auto task2 = std::make_shared<TestTask>("task2", true); - std::list<std::shared_ptr<Task>> allTasks; - allTasks.push_back(task1); - allTasks.push_back(task2); + std::set<std::shared_ptr<Task>> allTasks; + allTasks.insert(task1); + allTasks.insert(task2); - std::list<std::shared_ptr<Task>> dirtyTasks; - dirtyTasks.push_back(task2); + std::set<std::shared_ptr<Task>> dirtyTasks; + dirtyTasks.insert(task2); for(auto& task : dirtyTasks) { @@ -208,12 +213,12 @@ public: std::vector<std::string> deps = {"task1"}; auto task2 = std::make_shared<TestTask>("task2", true, deps); - std::list<std::shared_ptr<Task>> allTasks; - allTasks.push_back(task1); - allTasks.push_back(task2); + std::set<std::shared_ptr<Task>> allTasks; + allTasks.insert(task1); + allTasks.insert(task2); - std::list<std::shared_ptr<Task>> dirtyTasks; - dirtyTasks.push_back(task2); + std::set<std::shared_ptr<Task>> dirtyTasks; + dirtyTasks.insert(task2); for(auto& task : dirtyTasks) { @@ -230,13 +235,13 @@ public: std::vector<std::string> deps = {"task1"}; auto task2 = std::make_shared<TestTask>("task2", true, deps); - std::list<std::shared_ptr<Task>> allTasks; - allTasks.push_back(task2); - allTasks.push_back(task1); + std::set<std::shared_ptr<Task>> allTasks; + allTasks.insert(task2); + allTasks.insert(task1); - std::list<std::shared_ptr<Task>> dirtyTasks; - dirtyTasks.push_back(task2); - dirtyTasks.push_back(task1); + std::set<std::shared_ptr<Task>> dirtyTasks; + dirtyTasks.insert(task2); + dirtyTasks.insert(task1); for(auto& task : dirtyTasks) { diff --git a/test/tools_test.cc b/test/tools_test.cc new file mode 100644 index 0000000..7127b8d --- /dev/null +++ b/test/tools_test.cc @@ -0,0 +1,930 @@ +#include <vector> +#include <string> +#include <ostream> +#include <initializer_list> +#include <cassert> + +#include <tools.h> + +std::ostream& operator<<(std::ostream& stream, const ctor::toolchain& toolchain) +{ + switch(toolchain) + { + case ctor::toolchain::none: + stream << "ctor::toolchain::none"; + break; + case ctor::toolchain::any: + stream << "ctor::toolchain::any"; + break; + case ctor::toolchain::gcc: + stream << "ctor::toolchain::gcc"; + break; + case ctor::toolchain::clang: + stream << "ctor::toolchain::clang"; + break; + } + return stream; +} + +std::ostream& operator<<(std::ostream& stream, const std::vector<std::string>& vs) +{ + bool first{true}; + stream << "{ "; + for(const auto& v : vs) + { + if(!first) + { + stream << ", "; + } + stream << "'" << v << "'"; + first = false; + } + stream << " }"; + + return stream; +} + +std::ostream& operator<<(std::ostream& stream, const ctor::c_flag& flag) +{ + stream << "{" << flag.opt << ", \"" << flag.arg << "\", " << flag.toolchain << "}"; + return stream; +} + +std::ostream& operator<<(std::ostream& stream, const ctor::cxx_flag& flag) +{ + stream << "{" << flag.opt << ", \"" << flag.arg << "\", " << flag.toolchain << "}"; + return stream; +} + +std::ostream& operator<<(std::ostream& stream, const ctor::ld_flag& flag) +{ + stream << "{" << flag.opt << ", \"" << flag.arg << "\", " << flag.toolchain << "}"; + return stream; +} + +std::ostream& operator<<(std::ostream& stream, const ctor::ar_flag& flag) +{ + stream << "{" << flag.opt << ", \"" << flag.arg << "\", " << flag.toolchain << "}"; + return stream; +} + +std::ostream& operator<<(std::ostream& stream, const ctor::asm_flag& flag) +{ + stream << "{" << flag.opt << ", \"" << flag.arg << "\", " << flag.toolchain << "}"; + return stream; +} + +bool operator!=(const ctor::c_flag& a, const ctor::c_flag& b) +{ + return + a.opt != b.opt || + a.arg != b.arg || + a.toolchain != b.toolchain; +} + +bool operator!=(const ctor::cxx_flag& a, const ctor::cxx_flag& b) +{ + return + a.opt != b.opt || + a.arg != b.arg || + a.toolchain != b.toolchain; +} +bool operator!=(const ctor::ld_flag& a, const ctor::ld_flag& b) +{ + return + a.opt != b.opt || + a.arg != b.arg || + a.toolchain != b.toolchain; +} + +bool operator!=(const ctor::ar_flag& a, const ctor::ar_flag& b) +{ + return + a.opt != b.opt || + a.arg != b.arg || + a.toolchain != b.toolchain; +} +bool operator!=(const ctor::asm_flag& a, const ctor::asm_flag& b) +{ + return + a.opt != b.opt || + a.arg != b.arg || + a.toolchain != b.toolchain; +} + +#include <uunit.h> + +namespace { +std::string conf_host_cxx{}; +std::string conf_build_cxx{}; +} + +const ctor::configuration& ctor::get_configuration() +{ + static ctor::configuration cfg; + return cfg; +} + +const std::string& ctor::configuration::get(const std::string& key, const std::string& defval) const +{ + if(key == ctor::cfg::host_cxx) + { + return conf_host_cxx; + } + + if(key == ctor::cfg::build_cxx) + { + return conf_build_cxx; + } + + assert(false); // bad key + + static std::string res{}; + return res; +} + +class ToolsTest + : public uUnit +{ +public: + ToolsTest() + { + uTEST(ToolsTest::getToolChain_test); + + uTEST(ToolsTest::getOption_toolchain_c_test); + uTEST(ToolsTest::getOption_toolchain_cxx_test); + uTEST(ToolsTest::getOption_toolchain_ld_test); + uTEST(ToolsTest::getOption_toolchain_ar_test); + uTEST(ToolsTest::getOption_toolchain_asm_test); + + uTEST(ToolsTest::getOption_str_c_test); + uTEST(ToolsTest::getOption_str_cxx_test); + uTEST(ToolsTest::getOption_str_ld_test); + uTEST(ToolsTest::getOption_str_ar_test); + uTEST(ToolsTest::getOption_str_asm_test); + + uTEST(ToolsTest::to_strings_c_test); + uTEST(ToolsTest::to_strings_cxx_test); + uTEST(ToolsTest::to_strings_ld_test); + uTEST(ToolsTest::to_strings_ar_test); + uTEST(ToolsTest::to_strings_asm_test); + } + + void getToolChain_test() + { + uASSERT_EQUAL(ctor::toolchain::gcc, getToolChain("/usr/bin/g++")); + uASSERT_EQUAL(ctor::toolchain::gcc, getToolChain("/usr/bin/g++-10")); + uASSERT_EQUAL(ctor::toolchain::gcc, getToolChain("/usr/bin/x86_64-pc-linux-gnu-g++-9.3.0")); + uASSERT_EQUAL(ctor::toolchain::clang, getToolChain("/usr/bin/clang++")); + uASSERT_EQUAL(ctor::toolchain::clang, getToolChain("/usr/bin/clang++-16")); + uASSERT_EQUAL(ctor::toolchain::clang, getToolChain("/usr/lib/llvm/16/bin/i686-pc-linux-gnu-clang++-16")); + } + + + void getOption_toolchain_c_test() + { + std::vector<std::string> exp; + std::vector<std::string> act; + + // + // gcc + // + exp = { "-o", "foo" }; + act = c_option(ctor::toolchain::gcc, ctor::c_opt::output, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "-g" }; + act = c_option(ctor::toolchain::gcc, ctor::c_opt::debug); + uASSERT_EQUAL(exp, act); + + exp = { "-Wall" }; + act = c_option(ctor::toolchain::gcc, ctor::c_opt::warn_all); + uASSERT_EQUAL(exp, act); + + exp = { "-Werror" }; + act = c_option(ctor::toolchain::gcc, ctor::c_opt::warnings_as_errors); + uASSERT_EQUAL(exp, act); + + exp = { "-MMD" }; + act = c_option(ctor::toolchain::gcc, ctor::c_opt::generate_dep_tree); + uASSERT_EQUAL(exp, act); + + exp = { "-c" }; + act = c_option(ctor::toolchain::gcc, ctor::c_opt::no_link); + uASSERT_EQUAL(exp, act); + + exp = { "-Ifoo" }; + act = c_option(ctor::toolchain::gcc, ctor::c_opt::include_path, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "-std=foo" }; + act = c_option(ctor::toolchain::gcc, ctor::c_opt::c_std, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "-Ofoo" }; + act = c_option(ctor::toolchain::gcc, ctor::c_opt::optimization, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "-fPIC" }; + act = c_option(ctor::toolchain::gcc, ctor::c_opt::position_independent_code); + uASSERT_EQUAL(exp, act); + + exp = { "-fPIE" }; + act = c_option(ctor::toolchain::gcc, ctor::c_opt::position_independent_executable); + uASSERT_EQUAL(exp, act); + + exp = { "-foo" }; + act = c_option(ctor::toolchain::gcc, ctor::c_opt::custom, "-foo"); + uASSERT_EQUAL(exp, act); + + // + // clang + // + exp = { "-o", "foo" }; + act = c_option(ctor::toolchain::clang, ctor::c_opt::output, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "-g" }; + act = c_option(ctor::toolchain::clang, ctor::c_opt::debug); + uASSERT_EQUAL(exp, act); + + exp = { "-Wall" }; + act = c_option(ctor::toolchain::clang, ctor::c_opt::warn_all); + uASSERT_EQUAL(exp, act); + + exp = { "-Werror" }; + act = c_option(ctor::toolchain::clang, ctor::c_opt::warnings_as_errors); + uASSERT_EQUAL(exp, act); + + exp = { "-MMD" }; + act = c_option(ctor::toolchain::clang, ctor::c_opt::generate_dep_tree); + uASSERT_EQUAL(exp, act); + + exp = { "-c" }; + act = c_option(ctor::toolchain::clang, ctor::c_opt::no_link); + uASSERT_EQUAL(exp, act); + + exp = { "-Ifoo" }; + act = c_option(ctor::toolchain::clang, ctor::c_opt::include_path, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "-std=foo" }; + act = c_option(ctor::toolchain::clang, ctor::c_opt::c_std, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "-Ofoo" }; + act = c_option(ctor::toolchain::clang, ctor::c_opt::optimization, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "-fPIC" }; + act = c_option(ctor::toolchain::clang, ctor::c_opt::position_independent_code); + uASSERT_EQUAL(exp, act); + + exp = { "-fPIE" }; + act = c_option(ctor::toolchain::clang, ctor::c_opt::position_independent_executable); + uASSERT_EQUAL(exp, act); + + exp = { "-foo" }; + act = c_option(ctor::toolchain::clang, ctor::c_opt::custom, "-foo"); + uASSERT_EQUAL(exp, act); + + // + // any + // + exp = { "{ctor::c_opt::output, \"foo\"}" }; + act = c_option(ctor::toolchain::any, ctor::c_opt::output, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "{ctor::c_opt::debug}" }; + act = c_option(ctor::toolchain::any, ctor::c_opt::debug); + uASSERT_EQUAL(exp, act); + + exp = { "{ctor::c_opt::warn_all}" }; + act = c_option(ctor::toolchain::any, ctor::c_opt::warn_all); + uASSERT_EQUAL(exp, act); + + exp = { "{ctor::c_opt::warnings_as_errors}" }; + act = c_option(ctor::toolchain::any, ctor::c_opt::warnings_as_errors); + uASSERT_EQUAL(exp, act); + + exp = { "{ctor::c_opt::generate_dep_tree}" }; + act = c_option(ctor::toolchain::any, ctor::c_opt::generate_dep_tree); + uASSERT_EQUAL(exp, act); + + exp = { "{ctor::c_opt::no_link}" }; + act = c_option(ctor::toolchain::any, ctor::c_opt::no_link); + uASSERT_EQUAL(exp, act); + + exp = { "{ctor::c_opt::include_path, \"foo\"}" }; + act = c_option(ctor::toolchain::any, ctor::c_opt::include_path, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "{ctor::c_opt::c_std, \"foo\"}" }; + act = c_option(ctor::toolchain::any, ctor::c_opt::c_std, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "{ctor::c_opt::optimization, \"foo\"}" }; + act = c_option(ctor::toolchain::any, ctor::c_opt::optimization, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "{ctor::c_opt::position_independent_code}" }; + act = c_option(ctor::toolchain::any, ctor::c_opt::position_independent_code); + uASSERT_EQUAL(exp, act); + + exp = { "{ctor::c_opt::position_independent_executable}" }; + act = c_option(ctor::toolchain::any, ctor::c_opt::position_independent_executable); + uASSERT_EQUAL(exp, act); + + exp = { "{ctor::c_opt::custom, \"-foo\"}" }; + act = c_option(ctor::toolchain::any, ctor::c_opt::custom, "-foo"); + uASSERT_EQUAL(exp, act); + } + + void getOption_toolchain_cxx_test() + { + std::vector<std::string> exp; + std::vector<std::string> act; + + // + // gcc + // + exp = { "-o", "foo" }; + act = cxx_option(ctor::toolchain::gcc, ctor::cxx_opt::output, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "-g" }; + act = cxx_option(ctor::toolchain::gcc, ctor::cxx_opt::debug); + uASSERT_EQUAL(exp, act); + + exp = { "-Wall" }; + act = cxx_option(ctor::toolchain::gcc, ctor::cxx_opt::warn_all); + uASSERT_EQUAL(exp, act); + + exp = { "-Werror" }; + act = cxx_option(ctor::toolchain::gcc, ctor::cxx_opt::warnings_as_errors); + uASSERT_EQUAL(exp, act); + + exp = { "-MMD" }; + act = cxx_option(ctor::toolchain::gcc, ctor::cxx_opt::generate_dep_tree); + uASSERT_EQUAL(exp, act); + + exp = { "-c" }; + act = cxx_option(ctor::toolchain::gcc, ctor::cxx_opt::no_link); + uASSERT_EQUAL(exp, act); + + exp = { "-Ifoo" }; + act = cxx_option(ctor::toolchain::gcc, ctor::cxx_opt::include_path, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "-std=foo" }; + act = cxx_option(ctor::toolchain::gcc, ctor::cxx_opt::cpp_std, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "-Ofoo" }; + act = cxx_option(ctor::toolchain::gcc, ctor::cxx_opt::optimization, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "-fPIC" }; + act = cxx_option(ctor::toolchain::gcc, ctor::cxx_opt::position_independent_code); + uASSERT_EQUAL(exp, act); + + exp = { "-fPIE" }; + act = cxx_option(ctor::toolchain::gcc, ctor::cxx_opt::position_independent_executable); + uASSERT_EQUAL(exp, act); + + exp = { "-foo" }; + act = cxx_option(ctor::toolchain::gcc, ctor::cxx_opt::custom, "-foo"); + uASSERT_EQUAL(exp, act); + + // + // clang + // + exp = { "-o", "foo" }; + act = cxx_option(ctor::toolchain::clang, ctor::cxx_opt::output, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "-g" }; + act = cxx_option(ctor::toolchain::clang, ctor::cxx_opt::debug); + uASSERT_EQUAL(exp, act); + + exp = { "-Wall" }; + act = cxx_option(ctor::toolchain::clang, ctor::cxx_opt::warn_all); + uASSERT_EQUAL(exp, act); + + exp = { "-Werror" }; + act = cxx_option(ctor::toolchain::clang, ctor::cxx_opt::warnings_as_errors); + uASSERT_EQUAL(exp, act); + + exp = { "-MMD" }; + act = cxx_option(ctor::toolchain::clang, ctor::cxx_opt::generate_dep_tree); + uASSERT_EQUAL(exp, act); + + exp = { "-c" }; + act = cxx_option(ctor::toolchain::clang, ctor::cxx_opt::no_link); + uASSERT_EQUAL(exp, act); + + exp = { "-Ifoo" }; + act = cxx_option(ctor::toolchain::clang, ctor::cxx_opt::include_path, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "-std=foo" }; + act = cxx_option(ctor::toolchain::clang, ctor::cxx_opt::cpp_std, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "-Ofoo" }; + act = cxx_option(ctor::toolchain::clang, ctor::cxx_opt::optimization, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "-fPIC" }; + act = cxx_option(ctor::toolchain::clang, ctor::cxx_opt::position_independent_code); + uASSERT_EQUAL(exp, act); + + exp = { "-fPIE" }; + act = cxx_option(ctor::toolchain::clang, ctor::cxx_opt::position_independent_executable); + uASSERT_EQUAL(exp, act); + + exp = { "-foo" }; + act = cxx_option(ctor::toolchain::clang, ctor::cxx_opt::custom, "-foo"); + uASSERT_EQUAL(exp, act); + + // + // any + // + exp = { "{ctor::cxx_opt::output, \"foo\"}" }; + act = cxx_option(ctor::toolchain::any, ctor::cxx_opt::output, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "{ctor::cxx_opt::debug}" }; + act = cxx_option(ctor::toolchain::any, ctor::cxx_opt::debug); + uASSERT_EQUAL(exp, act); + + exp = { "{ctor::cxx_opt::warn_all}" }; + act = cxx_option(ctor::toolchain::any, ctor::cxx_opt::warn_all); + uASSERT_EQUAL(exp, act); + + exp = { "{ctor::cxx_opt::warnings_as_errors}" }; + act = cxx_option(ctor::toolchain::any, ctor::cxx_opt::warnings_as_errors); + uASSERT_EQUAL(exp, act); + + exp = { "{ctor::cxx_opt::generate_dep_tree}" }; + act = cxx_option(ctor::toolchain::any, ctor::cxx_opt::generate_dep_tree); + uASSERT_EQUAL(exp, act); + + exp = { "{ctor::cxx_opt::no_link}" }; + act = cxx_option(ctor::toolchain::any, ctor::cxx_opt::no_link); + uASSERT_EQUAL(exp, act); + + exp = { "{ctor::cxx_opt::include_path, \"foo\"}" }; + act = cxx_option(ctor::toolchain::any, ctor::cxx_opt::include_path, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "{ctor::cxx_opt::cpp_std, \"foo\"}" }; + act = cxx_option(ctor::toolchain::any, ctor::cxx_opt::cpp_std, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "{ctor::cxx_opt::optimization, \"foo\"}" }; + act = cxx_option(ctor::toolchain::any, ctor::cxx_opt::optimization, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "{ctor::cxx_opt::position_independent_code}" }; + act = cxx_option(ctor::toolchain::any, ctor::cxx_opt::position_independent_code); + uASSERT_EQUAL(exp, act); + + exp = { "{ctor::cxx_opt::position_independent_executable}" }; + act = cxx_option(ctor::toolchain::any, ctor::cxx_opt::position_independent_executable); + uASSERT_EQUAL(exp, act); + + exp = { "{ctor::cxx_opt::custom, \"-foo\"}" }; + act = cxx_option(ctor::toolchain::any, ctor::cxx_opt::custom, "-foo"); + uASSERT_EQUAL(exp, act); + } + + void getOption_toolchain_ld_test() + { + std::vector<std::string> exp; + std::vector<std::string> act; + + // + // gcc + // + exp = { "-o", "foo" }; + act = ld_option(ctor::toolchain::gcc, ctor::ld_opt::output, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "-s" }; + act = ld_option(ctor::toolchain::gcc, ctor::ld_opt::strip); + uASSERT_EQUAL(exp, act); + + exp = { "-Wall" }; + act = ld_option(ctor::toolchain::gcc, ctor::ld_opt::warn_all); + uASSERT_EQUAL(exp, act); + + exp = { "-Werror" }; + act = ld_option(ctor::toolchain::gcc, ctor::ld_opt::warnings_as_errors); + uASSERT_EQUAL(exp, act); + + exp = { "-Lfoo" }; + act = ld_option(ctor::toolchain::gcc, ctor::ld_opt::library_path, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "-lfoo" }; + act = ld_option(ctor::toolchain::gcc, ctor::ld_opt::link, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "-std=foo" }; + act = ld_option(ctor::toolchain::gcc, ctor::ld_opt::cpp_std, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "-shared" }; + act = ld_option(ctor::toolchain::gcc, ctor::ld_opt::build_shared); + uASSERT_EQUAL(exp, act); + + exp = { "-pthread" }; + act = ld_option(ctor::toolchain::gcc, ctor::ld_opt::threads); + uASSERT_EQUAL(exp, act); + + exp = { "-fPIC" }; + act = ld_option(ctor::toolchain::gcc, ctor::ld_opt::position_independent_code); + uASSERT_EQUAL(exp, act); + + exp = { "-fPIE" }; + act = ld_option(ctor::toolchain::gcc, ctor::ld_opt::position_independent_executable); + uASSERT_EQUAL(exp, act); + + exp = { "-foo" }; + act = ld_option(ctor::toolchain::gcc, ctor::ld_opt::custom, "-foo"); + uASSERT_EQUAL(exp, act); + + // + // clang + // + exp = { "-o", "foo" }; + act = ld_option(ctor::toolchain::clang, ctor::ld_opt::output, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "-s" }; + act = ld_option(ctor::toolchain::clang, ctor::ld_opt::strip); + uASSERT_EQUAL(exp, act); + + exp = { "-Wall" }; + act = ld_option(ctor::toolchain::clang, ctor::ld_opt::warn_all); + uASSERT_EQUAL(exp, act); + + exp = { "-Werror" }; + act = ld_option(ctor::toolchain::clang, ctor::ld_opt::warnings_as_errors); + uASSERT_EQUAL(exp, act); + + exp = { "-Lfoo" }; + act = ld_option(ctor::toolchain::clang, ctor::ld_opt::library_path, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "-lfoo" }; + act = ld_option(ctor::toolchain::clang, ctor::ld_opt::link, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "-std=foo" }; + act = ld_option(ctor::toolchain::clang, ctor::ld_opt::cpp_std, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "-shared" }; + act = ld_option(ctor::toolchain::clang, ctor::ld_opt::build_shared); + uASSERT_EQUAL(exp, act); + + exp = { "-pthread" }; + act = ld_option(ctor::toolchain::clang, ctor::ld_opt::threads); + uASSERT_EQUAL(exp, act); + + exp = { "-fPIC" }; + act = ld_option(ctor::toolchain::clang, ctor::ld_opt::position_independent_code); + uASSERT_EQUAL(exp, act); + + exp = { "-fPIE" }; + act = ld_option(ctor::toolchain::clang, ctor::ld_opt::position_independent_executable); + uASSERT_EQUAL(exp, act); + + exp = { "-foo" }; + act = ld_option(ctor::toolchain::clang, ctor::ld_opt::custom, "-foo"); + uASSERT_EQUAL(exp, act); + + // + // any + // + exp = { "{ctor::ld_opt::output, \"foo\"}" }; + act = ld_option(ctor::toolchain::any, ctor::ld_opt::output, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "{ctor::ld_opt::strip}" }; + act = ld_option(ctor::toolchain::any, ctor::ld_opt::strip); + uASSERT_EQUAL(exp, act); + + exp = { "{ctor::ld_opt::warn_all}" }; + act = ld_option(ctor::toolchain::any, ctor::ld_opt::warn_all); + uASSERT_EQUAL(exp, act); + + exp = { "{ctor::ld_opt::warnings_as_errors}" }; + act = ld_option(ctor::toolchain::any, ctor::ld_opt::warnings_as_errors); + uASSERT_EQUAL(exp, act); + + exp = { "{ctor::ld_opt::library_path, \"foo\"}" }; + act = ld_option(ctor::toolchain::any, ctor::ld_opt::library_path, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "{ctor::ld_opt::link, \"foo\"}" }; + act = ld_option(ctor::toolchain::any, ctor::ld_opt::link, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "{ctor::ld_opt::cpp_std, \"foo\"}" }; + act = ld_option(ctor::toolchain::any, ctor::ld_opt::cpp_std, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "{ctor::ld_opt::build_shared}" }; + act = ld_option(ctor::toolchain::any, ctor::ld_opt::build_shared); + uASSERT_EQUAL(exp, act); + + exp = { "{ctor::ld_opt::threads}" }; + act = ld_option(ctor::toolchain::any, ctor::ld_opt::threads); + uASSERT_EQUAL(exp, act); + + exp = { "{ctor::ld_opt::position_independent_code}" }; + act = ld_option(ctor::toolchain::any, ctor::ld_opt::position_independent_code); + uASSERT_EQUAL(exp, act); + + exp = { "{ctor::ld_opt::position_independent_executable}" }; + act = ld_option(ctor::toolchain::any, ctor::ld_opt::position_independent_executable); + uASSERT_EQUAL(exp, act); + + exp = { "{ctor::ld_opt::custom, \"-foo\"}" }; + act = ld_option(ctor::toolchain::any, ctor::ld_opt::custom, "-foo"); + uASSERT_EQUAL(exp, act); + } + + void getOption_toolchain_ar_test() + { + std::vector<std::string> exp; + std::vector<std::string> act; + + // + // gcc + // + exp = { "-r" }; + act = ar_option(ctor::toolchain::gcc, ctor::ar_opt::replace); + uASSERT_EQUAL(exp, act); + + exp = { "-s" }; + act = ar_option(ctor::toolchain::gcc, ctor::ar_opt::add_index); + uASSERT_EQUAL(exp, act); + + exp = { "-c" }; + act = ar_option(ctor::toolchain::gcc, ctor::ar_opt::create); + uASSERT_EQUAL(exp, act); + + exp = { "foo" }; + act = ar_option(ctor::toolchain::gcc, ctor::ar_opt::output, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "-foo" }; + act = ar_option(ctor::toolchain::gcc, ctor::ar_opt::custom, "-foo"); + uASSERT_EQUAL(exp, act); + + // + // clang + // + exp = { "-r" }; + act = ar_option(ctor::toolchain::clang, ctor::ar_opt::replace); + uASSERT_EQUAL(exp, act); + + exp = { "-s" }; + act = ar_option(ctor::toolchain::clang, ctor::ar_opt::add_index); + uASSERT_EQUAL(exp, act); + + exp = { "-c" }; + act = ar_option(ctor::toolchain::clang, ctor::ar_opt::create); + uASSERT_EQUAL(exp, act); + + exp = { "foo" }; + act = ar_option(ctor::toolchain::clang, ctor::ar_opt::output, "foo"); + uASSERT_EQUAL(exp, act); + + exp = { "-foo" }; + act = ar_option(ctor::toolchain::clang, ctor::ar_opt::custom, "-foo"); + uASSERT_EQUAL(exp, act); + + // + // any + // + exp = { "{ctor::ar_opt::custom, \"-foo\"}" }; + act = ar_option(ctor::toolchain::any, ctor::ar_opt::custom, "-foo"); + uASSERT_EQUAL(exp, act); +} + + void getOption_toolchain_asm_test() + { + std::vector<std::string> exp; + std::vector<std::string> act; + + // + // gcc + // + exp = { "-foo" }; + act = asm_option(ctor::toolchain::gcc, ctor::asm_opt::custom, "-foo"); + uASSERT_EQUAL(exp, act); + + // + // clang + // + exp = { "-foo" }; + act = asm_option(ctor::toolchain::clang, ctor::asm_opt::custom, "-foo"); + uASSERT_EQUAL(exp, act); + + // + // any + // + exp = { "{ctor::asm_opt::custom, \"-foo\"}" }; + act = asm_option(ctor::toolchain::any, ctor::asm_opt::custom, "-foo"); + uASSERT_EQUAL(exp, act); + } + + + void getOption_str_c_test() + { + ctor::c_flag exp(""); + ctor::c_flag act(""); + + // + // gcc + // + exp = { ctor::c_opt::include_path, "foo" }; + act = c_option("-Ifoo", ctor::toolchain::gcc); + uASSERT_EQUAL(exp, act); + + exp = { ctor::c_opt::custom, "foo" }; + act = c_option("foo", ctor::toolchain::gcc); + uASSERT_EQUAL(exp, act); + + // + // clang + // + exp = { ctor::c_opt::include_path, "foo" }; + act = c_option("-Ifoo", ctor::toolchain::clang); + uASSERT_EQUAL(exp, act); + + exp = { ctor::c_opt::custom, "foo" }; + act = c_option("foo", ctor::toolchain::clang); + uASSERT_EQUAL(exp, act); + } + + void getOption_str_cxx_test() + { + ctor::cxx_flag exp(""); + ctor::cxx_flag act(""); + + // + // gcc + // + exp = { ctor::cxx_opt::include_path, "foo" }; + act = cxx_option("-Ifoo", ctor::toolchain::gcc); + uASSERT_EQUAL(exp, act); + + exp = { ctor::cxx_opt::custom, "foo" }; + act = cxx_option("foo", ctor::toolchain::gcc); + uASSERT_EQUAL(exp, act); + + // + // clang + // + exp = { ctor::cxx_opt::include_path, "foo" }; + act = cxx_option("-Ifoo", ctor::toolchain::clang); + uASSERT_EQUAL(exp, act); + + exp = { ctor::cxx_opt::custom, "foo" }; + act = cxx_option("foo", ctor::toolchain::clang); + uASSERT_EQUAL(exp, act); + } + + void getOption_str_ld_test() + { + ctor::ld_flag exp(""); + ctor::ld_flag act(""); + + // + // gcc + // + exp = { ctor::ld_opt::library_path, "foo" }; + act = ld_option("-Lfoo", ctor::toolchain::gcc); + uASSERT_EQUAL(exp, act); + + exp = { ctor::ld_opt::custom, "foo" }; + act = ld_option("foo", ctor::toolchain::gcc); + uASSERT_EQUAL(exp, act); + + // + // clang + // + exp = { ctor::ld_opt::library_path, "foo" }; + act = ld_option("-Lfoo", ctor::toolchain::clang); + uASSERT_EQUAL(exp, act); + + exp = { ctor::ld_opt::custom, "foo" }; + act = ld_option("foo", ctor::toolchain::clang); + uASSERT_EQUAL(exp, act); + } + + void getOption_str_ar_test() + { + ctor::ar_flag exp(""); + ctor::ar_flag act(""); + + // + // gcc + // + exp = { ctor::ar_opt::custom, "foo" }; + act = ar_option("foo", ctor::toolchain::gcc); + uASSERT_EQUAL(exp, act); + + // + // clang + // + exp = { ctor::ar_opt::custom, "foo" }; + act = ar_option("foo", ctor::toolchain::clang); + uASSERT_EQUAL(exp, act); + } + + void getOption_str_asm_test() + { + ctor::asm_flag exp(""); + ctor::asm_flag act(""); + + // + // gcc + // + exp = { ctor::asm_opt::custom, "foo" }; + act = asm_option("foo", ctor::toolchain::gcc); + uASSERT_EQUAL(exp, act); + + // + // clang + // + exp = { ctor::asm_opt::custom, "foo" }; + act = asm_option("foo", ctor::toolchain::clang); + uASSERT_EQUAL(exp, act); + } + + + void to_strings_c_test() + { + std::vector<std::string> exp; + std::vector<std::string> act; + + // Mismatching toolchain (required vs actual) results in no output + // otherwise to_strings is just a proxy for c_option + act = to_strings(ctor::toolchain::gcc, {ctor::toolchain::clang, ctor::c_opt::no_link}); + uASSERT_EQUAL(exp, act); + } + + void to_strings_cxx_test() + { + std::vector<std::string> exp; + std::vector<std::string> act; + + // Mismatching toolchain (required vs actual) results in no output + // otherwise to_strings is just a proxy for cxx_option + act = to_strings(ctor::toolchain::gcc, {ctor::toolchain::clang, ctor::cxx_opt::no_link}); + uASSERT_EQUAL(exp, act); + } + + void to_strings_ld_test() + { + std::vector<std::string> exp; + std::vector<std::string> act; + + // Mismatching toolchain (required vs actual) results in no output + // otherwise to_strings is just a proxy for ld_option + act = to_strings(ctor::toolchain::gcc, {ctor::toolchain::clang, ctor::ld_opt::strip}); + uASSERT_EQUAL(exp, act); + } + + void to_strings_ar_test() + { + std::vector<std::string> exp; + std::vector<std::string> act; + + // Mismatching toolchain (required vs actual) results in no output + // otherwise to_strings is just a proxy for ar_option + act = to_strings(ctor::toolchain::gcc, {ctor::toolchain::clang, ctor::ar_opt::custom, "foo"}); + uASSERT_EQUAL(exp, act); + } + + void to_strings_asm_test() + { + std::vector<std::string> exp; + std::vector<std::string> act; + + // Mismatching toolchain (required vs actual) results in no output + // otherwise to_strings is just a proxy for asm_option + act = to_strings(ctor::toolchain::gcc, {ctor::toolchain::clang, ctor::asm_opt::custom, "foo"}); + uASSERT_EQUAL(exp, act); + } +}; + +// Registers the fixture into the 'registry' +static ToolsTest test; |