diff options
-rw-r--r-- | .gitignore | 3 | ||||
-rw-r--r-- | Makefile | 3 | ||||
-rw-r--r-- | configure.cc | 153 | ||||
-rw-r--r-- | configure.h | 19 | ||||
-rw-r--r-- | libcppbuild.cc | 401 | ||||
-rw-r--r-- | rebuild.cc | 137 | ||||
-rw-r--r-- | rebuild.h | 24 | ||||
-rw-r--r-- | tasks.cc | 123 | ||||
-rw-r--r-- | tasks.h | 18 | ||||
-rw-r--r-- | toolchain.cc | 17 | ||||
-rw-r--r-- | toolchain.h | 14 |
11 files changed, 515 insertions, 397 deletions
@@ -3,3 +3,6 @@ build/ cppbuild *.a *.o +*.d +configuration.cc +config.h @@ -8,6 +8,9 @@ SRC = \ task_so.cc \ task.cc \ execute.cc \ + configure.cc \ + rebuild.cc \ + tasks.cc \ OBJ = $(patsubst %.cc,%.o,$(SRC)) diff --git a/configure.cc b/configure.cc new file mode 100644 index 0000000..e84f808 --- /dev/null +++ b/configure.cc @@ -0,0 +1,153 @@ +#include "configure.h" + +#include <iostream> +#include <filesystem> +#include <fstream> + +#include <getoptpp/getoptpp.hpp> + +#include "settings.h" +#include "execute.h" +#include "libcppbuild.h" +#include "tasks.h" + +std::filesystem::path configurationFile("configuration.cc"); +std::filesystem::path configHeaderFile("config.h"); + +const std::map<std::string, std::string> default_configuration{}; +const std::map<std::string, std::string>& __attribute__((weak)) configuration() +{ + return default_configuration; +} + +bool hasConfiguration(const std::string& key) +{ + const auto& c = configuration(); + return c.find(key) != c.end(); +} + +const std::string& getConfiguration(const std::string& key, + const std::string& defaultValue) +{ + const auto& c = configuration(); + if(hasConfiguration(key)) + { + return c.at(key); + } + + return defaultValue; +} + +int configure(int argc, char* argv[]) +{ + Settings settings; + + settings.builddir = "build"; + + std::string cmd_str; + for(int i = 0; i < argc; ++i) + { + if(i > 0) + { + cmd_str += " "; + } + cmd_str += argv[i]; + } + + dg::Options opt; + int key{256}; + + std::string build_arch; + std::string host_arch; + + opt.add("build-dir", required_argument, 'b', + "Set output directory for build files (default: '" + + settings.builddir + "').", + [&]() { + settings.builddir = optarg; + return 0; + }); + + opt.add("verbose", no_argument, 'v', + "Be verbose. Add multiple times for more verbosity.", + [&]() { + settings.verbose++; + return 0; + }); + + opt.add("build", required_argument, key++, + "Configure for building on specified architecture.", + [&]() { + build_arch = optarg; + return 0; + }); + + opt.add("host", required_argument, key++, + "Cross-compile to build programs to run on specified architecture.", + [&]() { + host_arch = optarg; + return 0; + }); + + opt.add("help", no_argument, 'h', + "Print this help text.", + [&]() { + std::cout << "configure usage stuff\n"; + opt.help(); + exit(0); + return 0; + }); + + opt.process(argc, argv); + + if(host_arch.empty()) + { + host_arch = build_arch; + } + + auto tasks = getTasks(settings); + + bool needs_cpp{false}; + bool needs_c{false}; + bool needs_ar{false}; + for(const auto& task :tasks) + { + switch(task->sourceLanguage()) + { + case Language::Auto: + std::cerr << "TargetLanguage not deduced!\n"; + exit(1); + break; + case Language::C: + needs_cpp = false; + break; + case Language::Cpp: + needs_c = true; + break; + } + } + + { + std::ofstream istr(configurationFile); + istr << "#include \"libcppbuild.h\"\n\n"; + istr << "const std::map<std::string, std::string>& configuration()\n"; + istr << "{\n"; + istr << " static std::map<std::string, std::string> c =\n"; + istr << " {\n"; + istr << " { \"cmd\", \"" << cmd_str << "\" },\n"; + istr << " { \"" << cfg::builddir << "\", \"" << settings.builddir << "\" },\n"; + istr << " { \"" << cfg::target_cc << "\", \"/usr/bin/gcc\" },\n"; + istr << " { \"" << cfg::target_cpp << "\", \"/usr/bin/g++\" },\n"; + istr << " { \"" << cfg::target_ar << "\", \"/usr/bin/ar\" },\n"; + istr << " { \"" << cfg::target_ld << "\", \"/usr/bin/ld\" },\n"; + istr << " { \"" << cfg::host_cc << "\", \"/usr/bin/gcc\" },\n"; + istr << " { \"" << cfg::host_cpp << "\", \"/usr/bin/g++\" },\n"; + istr << " { \"" << cfg::host_ar << "\", \"/usr/bin/ar\" },\n"; + istr << " { \"" << cfg::host_ld << "\", \"/usr/bin/ld\" },\n"; + istr << " };\n"; + istr << " return c;\n"; + istr << "}\n"; + } + + return 0; +} diff --git a/configure.h b/configure.h new file mode 100644 index 0000000..95b6765 --- /dev/null +++ b/configure.h @@ -0,0 +1,19 @@ +// -*- c++ -*- +#pragma once + +#include <filesystem> +#include <string> +#include <map> + +extern std::filesystem::path configurationFile;; +extern std::filesystem::path configHeaderFile; + +int configure(int argc, char* argv[]); + +bool hasConfiguration(const std::string& key); +const std::string& getConfiguration(const std::string& key, + const std::string& defaultValue); + +const std::map<std::string, std::string>& configuration(); + +extern const std::map<std::string, std::string> default_configuration; diff --git a/libcppbuild.cc b/libcppbuild.cc index 2a2c40d..6d1d6e5 100644 --- a/libcppbuild.cc +++ b/libcppbuild.cc @@ -14,411 +14,18 @@ #include <fstream> #include <cstdlib> #include <set> +#include <future> #include <getoptpp/getoptpp.hpp> #include "libcppbuild.h" -#include "task_cc.h" -#include "task_ld.h" -#include "task_ar.h" -#include "task_so.h" #include "settings.h" -#include "execute.h" - -#include <unistd.h> - - -namespace -{ -std::filesystem::path configurationFile("configuration.cc"); -const std::map<std::string, std::string> default_configuration{}; -} -const std::map<std::string, std::string>& __attribute__((weak)) configuration() -{ - return default_configuration; -} - -bool hasConfiguration(const std::string& key) -{ - const auto& c = configuration(); - return c.find(key) != c.end(); -} - -const std::string& getConfiguration(const std::string& key, - const std::string& defaultValue) -{ - const auto& c = configuration(); - if(hasConfiguration(key)) - { - return c.at(key); - } - - return defaultValue; -} +#include "configure.h" +#include "rebuild.h" +#include "tasks.h" using namespace std::chrono_literals; -std::list<std::shared_ptr<Task>> taskFactory(const BuildConfiguration& config, - const Settings& settings, - const std::string& sourceDir) -{ - std::filesystem::path targetFile(config.target); - - TargetType target_type{config.type}; - if(target_type == TargetType::Auto) - { - if(targetFile.extension() == ".a") - { - target_type = TargetType::StaticLibrary; - } - else if(targetFile.extension() == ".so") - { - target_type = TargetType::DynamicLibrary; - } - else if(targetFile.extension() == "") - { - target_type = TargetType::Executable; - } - else - { - std::cerr << "Could not deduce target type from target " << - targetFile.string() << " please specify.\n"; - exit(1); - } - } - - std::vector<std::string> objects; - std::list<std::shared_ptr<Task>> tasks; - for(const auto& file : config.sources) - { - tasks.emplace_back(std::make_shared<TaskCC>(config, settings, - sourceDir, file)); - objects.push_back(tasks.back()->target()); - } - - switch(target_type) - { - case TargetType::StaticLibrary: - tasks.emplace_back(std::make_shared<TaskAR>(config, settings, config.target, - objects)); - break; - - case TargetType::DynamicLibrary: - 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)); - break; - - case TargetType::Executable: - tasks.emplace_back(std::make_shared<TaskLD>(config, settings, config.target, - objects)); - break; - } - - return tasks; -} - -std::shared_ptr<Task> getNextTask(const std::list<std::shared_ptr<Task>>& allTasks, - std::list<std::shared_ptr<Task>>& dirtyTasks) -{ - for(auto dirtyTask = dirtyTasks.begin(); - dirtyTask != dirtyTasks.end(); - ++dirtyTask) - { - //std::cout << "Examining target " << (*dirtyTask)->target() << "\n"; - if((*dirtyTask)->ready()) - { - dirtyTasks.erase(dirtyTask); - return *dirtyTask; - } - } - - //std::cout << "No task ready ... \n"; - return nullptr; -} - -namespace -{ -struct BuildConfigurationEntry -{ - const char* file; - std::vector<BuildConfiguration> (*cb)(); -}; -std::array<BuildConfigurationEntry, 1024> configFiles; -std::size_t numConfigFiles{0}; -} - -// TODO: Use c++20 when ready, somehing like this: -//int reg(const std::source_location location = std::source_location::current()) -int reg(const char* location, std::vector<BuildConfiguration> (*cb)()) -{ - // NOTE: std::cout cannot be used here - if(numConfigFiles >= configFiles.size()) - { - fprintf(stderr, "Max %d build configurations currently supported.\n", - (int)configFiles.size()); - exit(1); - } - - configFiles[numConfigFiles].file = location; - configFiles[numConfigFiles].cb = cb; - ++numConfigFiles; - - return 0; -} - -int unreg(const char* location) -{ - std::size_t found{0}; - for(std::size_t i = 0; i < numConfigFiles;) - { - if(std::string(location) == configFiles[i].file) - { - ++found; - for(std::size_t j = i; j < numConfigFiles; ++j) - { - configFiles[j] = configFiles[j + 1]; - } - --numConfigFiles; - } - else - { - ++i; - } - } - - return found; -} - -void recompileCheck(const Settings& settings, int argc, char* argv[], - bool force = false) -{ - bool dirty{force}; - - std::vector<std::string> args; - args.push_back("-s"); - args.push_back("-O3"); - args.push_back("-std=c++17"); - args.push_back("-pthread"); - - std::filesystem::path binFile("cppbuild"); - - if(std::filesystem::exists(configurationFile)) - { - args.push_back(configurationFile.string()); - - if(std::filesystem::last_write_time(binFile) <= - std::filesystem::last_write_time(configurationFile)) - { - dirty = true; - } - - const auto& c = configuration(); - if(&c == &default_configuration) - { - // configuration.cc exists, but currently compiled with the default one. - dirty = true; - } - } - - if(settings.verbose > 1) - { - std::cout << "Recompile check (" << numConfigFiles << "):\n"; - } - - for(std::size_t i = 0; i < numConfigFiles; ++i) - { - std::string location = configFiles[i].file; - if(settings.verbose > 1) - { - std::cout << " - " << location << "\n"; - } - std::filesystem::path configFile(location); - if(std::filesystem::last_write_time(binFile) <= - std::filesystem::last_write_time(configFile)) - { - dirty = true; - } - - // Support adding multiple config functions from the same file - if(std::find(args.begin(), args.end(), location) == std::end(args)) - { - args.push_back(location); - } - } - args.push_back("libcppbuild.a"); - args.push_back("-o"); - args.push_back(binFile.string()); - - if(dirty) - { - std::cout << "Rebuilding config\n"; - auto tool = getConfiguration(cfg::host_cpp, "/usr/bin/g++"); - auto ret = execute(tool, args, settings.verbose > 0); - if(ret != 0) - { - std::cerr << "Failed: ." << ret << "\n"; - exit(1); - } - else - { - std::cout << "Re-launch\n"; - std::vector<std::string> args; - for(int i = 1; i < argc; ++i) - { - args.push_back(argv[i]); - } - exit(execute(argv[0], args, settings.verbose > 0)); - } - } -} - -std::list<std::shared_ptr<Task>> getTasks(const Settings& settings) -{ - static std::deque<BuildConfiguration> build_configs; - std::list<std::shared_ptr<Task>> tasks; - for(std::size_t i = 0; i < numConfigFiles; ++i) - { - std::string path = - std::filesystem::path(configFiles[i].file).parent_path(); - if(settings.verbose > 1) - { - std::cout << configFiles[i].file << " in path " << path << "\n"; - } - auto configs = configFiles[i].cb(); - for(const auto& config : configs) - { - build_configs.push_back(config); - const auto& build_config = build_configs.back(); - std::vector<std::string> objects; - auto t = taskFactory(build_config, settings, path); - tasks.insert(tasks.end(), t.begin(), t.end()); - } - } - - return tasks; -} - -/* -int configure(int argc, char* argv[]) -{ - Settings settings; - - settings.builddir = "build"; - - std::string cmd_str; - for(int i = 0; i < argc; ++i) - { - if(i > 0) - { - cmd_str += " "; - } - cmd_str += argv[i]; - } - - dg::Options opt; - int key{256}; - - std::string build_arch; - std::string host_arch; - - opt.add("build-dir", required_argument, 'b', - "Set output directory for build files (default: '" + - settings.builddir + "').", - [&]() { - settings.builddir = optarg; - return 0; - }); - - opt.add("verbose", no_argument, 'v', - "Be verbose. Add multiple times for more verbosity.", - [&]() { - settings.verbose++; - return 0; - }); - - opt.add("build", required_argument, key++, - "Configure for building on specified architecture.", - [&]() { - build_arch = optarg; - return 0; - }); - - opt.add("host", required_argument, key++, - "Cross-compile to build programs to run on specified architecture.", - [&]() { - host_arch = optarg; - return 0; - }); - - opt.add("help", no_argument, 'h', - "Print this help text.", - [&]() { - std::cout << "configure usage stuff\n"; - opt.help(); - exit(0); - return 0; - }); - - opt.process(argc, argv); - - if(host_arch.empty()) - { - host_arch = build_arch; - } - - auto tasks = getTasks(settings); - - bool needs_cpp{false}; - bool needs_c{false}; - bool needs_ar{false}; - for(const auto& task :tasks) - { - switch(task->sourceLanguage()) - { - case Language::Auto: - std::cerr << "TargetLanguage not deduced!\n"; - exit(1); - break; - case Language::C: - needs_cpp = false; - break; - case Language::Cpp: - needs_c = true; - break; - } - } - - { - std::ofstream istr(configurationFile); - istr << "#include \"libcppbuild.h\"\n\n"; - istr << "const std::map<std::string, std::string>& configuration()\n"; - istr << "{\n"; - istr << " static std::map<std::string, std::string> c =\n"; - istr << " {\n"; - istr << " { \"cmd\", \"" << cmd_str << "\" },\n"; - istr << " { \"" << cfg::builddir << "\", \"" << settings.builddir << "\" },\n"; - istr << " { \"" << cfg::target_cc << "\", \"/usr/bin/gcc\" },\n"; - istr << " { \"" << cfg::target_cpp << "\", \"/usr/bin/g++\" },\n"; - istr << " { \"" << cfg::target_ar << "\", \"/usr/bin/ar\" },\n"; - istr << " { \"" << cfg::target_ld << "\", \"/usr/bin/ld\" },\n"; - istr << " { \"" << cfg::host_cc << "\", \"/usr/bin/gcc\" },\n"; - istr << " { \"" << cfg::host_cpp << "\", \"/usr/bin/g++\" },\n"; - istr << " { \"" << cfg::host_ar << "\", \"/usr/bin/ar\" },\n"; - istr << " { \"" << cfg::host_ld << "\", \"/usr/bin/ld\" },\n"; - istr << " };\n"; - istr << " return c;\n"; - istr << "}\n"; - } - - - return 0; -} - int main(int argc, char* argv[]) { if(argc > 1 && std::string(argv[1]) == "configure") diff --git a/rebuild.cc b/rebuild.cc new file mode 100644 index 0000000..0978eb1 --- /dev/null +++ b/rebuild.cc @@ -0,0 +1,137 @@ +#include "rebuild.h" + +#include <iostream> +#include <filesystem> +#include <algorithm> + +#include "execute.h" +#include "configure.h" +#include "settings.h" +#include "libcppbuild.h" + +std::array<BuildConfigurationEntry, 1024> configFiles; +std::size_t numConfigFiles{0}; + +// TODO: Use c++20 when ready, somehing like this: +//int reg(const std::source_location location = std::source_location::current()) +int reg(const char* location, std::vector<BuildConfiguration> (*cb)()) +{ + // NOTE: std::cout cannot be used here + if(numConfigFiles >= configFiles.size()) + { + fprintf(stderr, "Max %d build configurations currently supported.\n", + (int)configFiles.size()); + exit(1); + } + + configFiles[numConfigFiles].file = location; + configFiles[numConfigFiles].cb = cb; + ++numConfigFiles; + + return 0; +} + +int unreg(const char* location) +{ + std::size_t found{0}; + for(std::size_t i = 0; i < numConfigFiles;) + { + if(std::string(location) == configFiles[i].file) + { + ++found; + for(std::size_t j = i; j < numConfigFiles; ++j) + { + configFiles[j] = configFiles[j + 1]; + } + --numConfigFiles; + } + else + { + ++i; + } + } + + return found; +} + +void recompileCheck(const Settings& settings, int argc, char* argv[], bool force) +{ + bool dirty{force}; + + std::vector<std::string> args; + args.push_back("-s"); + args.push_back("-O3"); + args.push_back("-std=c++17"); + args.push_back("-pthread"); + + std::filesystem::path binFile("cppbuild"); + + if(std::filesystem::exists(configurationFile)) + { + args.push_back(configurationFile.string()); + + if(std::filesystem::last_write_time(binFile) <= + std::filesystem::last_write_time(configurationFile)) + { + dirty = true; + } + + const auto& c = configuration(); + if(&c == &default_configuration) + { + // configuration.cc exists, but currently compiled with the default one. + dirty = true; + } + } + + if(settings.verbose > 1) + { + std::cout << "Recompile check (" << numConfigFiles << "):\n"; + } + + for(std::size_t i = 0; i < numConfigFiles; ++i) + { + std::string location = configFiles[i].file; + if(settings.verbose > 1) + { + std::cout << " - " << location << "\n"; + } + std::filesystem::path configFile(location); + if(std::filesystem::last_write_time(binFile) <= + std::filesystem::last_write_time(configFile)) + { + dirty = true; + } + + // Support adding multiple config functions from the same file + if(std::find(args.begin(), args.end(), location) == std::end(args)) + { + args.push_back(location); + } + } + args.push_back("libcppbuild.a"); + args.push_back("-o"); + args.push_back(binFile.string()); + + if(dirty) + { + std::cout << "Rebuilding config\n"; + auto tool = getConfiguration(cfg::host_cpp, "/usr/bin/g++"); + auto ret = execute(tool, args, settings.verbose > 0); + if(ret != 0) + { + std::cerr << "Failed: ." << ret << "\n"; + exit(1); + } + else + { + std::cout << "Re-launch\n"; + std::vector<std::string> args; + for(int i = 1; i < argc; ++i) + { + args.push_back(argv[i]); + } + exit(execute(argv[0], args, settings.verbose > 0)); + } + } +} diff --git a/rebuild.h b/rebuild.h new file mode 100644 index 0000000..d7720c9 --- /dev/null +++ b/rebuild.h @@ -0,0 +1,24 @@ +// -*- c++ -*- +#pragma once + +#include <vector> +#include <array> + +#include "libcppbuild.h" + +class Settings; + +struct BuildConfigurationEntry +{ + const char* file; + std::vector<BuildConfiguration> (*cb)(); +}; + +extern std::array<BuildConfigurationEntry, 1024> configFiles; +extern std::size_t numConfigFiles; + +//int reg(const char* location, std::vector<BuildConfiguration> (*cb)()); +int unreg(const char* location); + +void recompileCheck(const Settings& settings, int argc, char* argv[], + bool force = false); diff --git a/tasks.cc b/tasks.cc new file mode 100644 index 0000000..641e05b --- /dev/null +++ b/tasks.cc @@ -0,0 +1,123 @@ +#include "tasks.h" + +#include <filesystem> +#include <deque> +#include <iostream> + +#include "settings.h" +#include "libcppbuild.h" +#include "task.h" +#include "task_cc.h" +#include "task_ld.h" +#include "task_ar.h" +#include "task_so.h" +#include "rebuild.h" + +std::list<std::shared_ptr<Task>> taskFactory(const BuildConfiguration& config, + const Settings& settings, + const std::string& sourceDir) +{ + std::filesystem::path targetFile(config.target); + + TargetType target_type{config.type}; + if(target_type == TargetType::Auto) + { + if(targetFile.extension() == ".a") + { + target_type = TargetType::StaticLibrary; + } + else if(targetFile.extension() == ".so") + { + target_type = TargetType::DynamicLibrary; + } + else if(targetFile.extension() == "") + { + target_type = TargetType::Executable; + } + else + { + std::cerr << "Could not deduce target type from target " << + targetFile.string() << " please specify.\n"; + exit(1); + } + } + + std::vector<std::string> objects; + std::list<std::shared_ptr<Task>> tasks; + for(const auto& file : config.sources) + { + tasks.emplace_back(std::make_shared<TaskCC>(config, settings, + sourceDir, file)); + objects.push_back(tasks.back()->target()); + } + + switch(target_type) + { + case TargetType::StaticLibrary: + tasks.emplace_back(std::make_shared<TaskAR>(config, settings, config.target, + objects)); + break; + + case TargetType::DynamicLibrary: + 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)); + break; + + case TargetType::Executable: + tasks.emplace_back(std::make_shared<TaskLD>(config, settings, config.target, + objects)); + break; + } + + return tasks; +} + +std::shared_ptr<Task> getNextTask(const std::list<std::shared_ptr<Task>>& allTasks, + std::list<std::shared_ptr<Task>>& dirtyTasks) +{ + for(auto dirtyTask = dirtyTasks.begin(); + dirtyTask != dirtyTasks.end(); + ++dirtyTask) + { + //std::cout << "Examining target " << (*dirtyTask)->target() << "\n"; + if((*dirtyTask)->ready()) + { + dirtyTasks.erase(dirtyTask); + return *dirtyTask; + } + } + + //std::cout << "No task ready ... \n"; + return nullptr; +} + +std::list<std::shared_ptr<Task>> getTasks(const Settings& settings) +{ + static std::deque<BuildConfiguration> build_configs; + std::list<std::shared_ptr<Task>> tasks; + for(std::size_t i = 0; i < numConfigFiles; ++i) + { + std::string path = + std::filesystem::path(configFiles[i].file).parent_path(); + if(settings.verbose > 1) + { + std::cout << configFiles[i].file << " in path " << path << "\n"; + } + auto configs = configFiles[i].cb(); + for(const auto& config : configs) + { + build_configs.push_back(config); + const auto& build_config = build_configs.back(); + std::vector<std::string> objects; + auto t = taskFactory(build_config, settings, path); + tasks.insert(tasks.end(), t.begin(), t.end()); + } + } + + return tasks; +} @@ -0,0 +1,18 @@ +// -*- c++ -*- +#pragma once + +#include <string> +#include <list> +#include <memory> + +#include "task.h" + +class BuildConfiguration; +class Settings; + +std::list<std::shared_ptr<Task>> taskFactory(const BuildConfiguration& config, + const Settings& settings, + const std::string& sourceDir); +std::shared_ptr<Task> getNextTask(const std::list<std::shared_ptr<Task>>& allTasks, + std::list<std::shared_ptr<Task>>& dirtyTasks); +std::list<std::shared_ptr<Task>> getTasks(const Settings& settings); diff --git a/toolchain.cc b/toolchain.cc new file mode 100644 index 0000000..0a8ea98 --- /dev/null +++ b/toolchain.cc @@ -0,0 +1,17 @@ +#include "toolchain.h" + +#include "libcppbuild.h" + +std::string getTool(Tool tool) +{ + auto prefix = getConfiguration("prefix"); + + switch(tool) + { + case Tool::CCompiler: + case Tool::CppCompiler: + case Tool::Archiver: + case Tool::Linker: + break; + } +} diff --git a/toolchain.h b/toolchain.h new file mode 100644 index 0000000..d3de9a1 --- /dev/null +++ b/toolchain.h @@ -0,0 +1,14 @@ +// -*- c++ -*- +#pragma once + +#include <string> + +enum class Tool +{ + CCompiler, + CppCompiler, + Archiver, + Linker, +} + +std::string getTool(Tool tool); |