From e4c0168e4e8834aea0cfe06b8b2fc8bd63f591bb Mon Sep 17 00:00:00 2001 From: Bent Bisballe Nyeng Date: Wed, 23 Jun 2021 07:37:08 +0200 Subject: Split libcppbuild.cc into multiple files/modules. --- .gitignore | 3 + Makefile | 3 + configure.cc | 153 ++++++++++++++++++++++ configure.h | 19 +++ libcppbuild.cc | 401 +-------------------------------------------------------- rebuild.cc | 137 ++++++++++++++++++++ rebuild.h | 24 ++++ tasks.cc | 123 ++++++++++++++++++ tasks.h | 18 +++ toolchain.cc | 17 +++ toolchain.h | 14 ++ 11 files changed, 515 insertions(+), 397 deletions(-) create mode 100644 configure.cc create mode 100644 configure.h create mode 100644 rebuild.cc create mode 100644 rebuild.h create mode 100644 tasks.cc create mode 100644 tasks.h create mode 100644 toolchain.cc create mode 100644 toolchain.h diff --git a/.gitignore b/.gitignore index 7d69636..94dd838 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,6 @@ build/ cppbuild *.a *.o +*.d +configuration.cc +config.h diff --git a/Makefile b/Makefile index 14d909e..59e883e 100644 --- a/Makefile +++ b/Makefile @@ -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 +#include +#include + +#include + +#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 default_configuration{}; +const std::map& __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& configuration()\n"; + istr << "{\n"; + istr << " static std::map 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 +#include +#include + +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& configuration(); + +extern const std::map 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 #include #include +#include #include #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 - - -namespace -{ -std::filesystem::path configurationFile("configuration.cc"); -const std::map default_configuration{}; -} -const std::map& __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> 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 objects; - std::list> tasks; - for(const auto& file : config.sources) - { - tasks.emplace_back(std::make_shared(config, settings, - sourceDir, file)); - objects.push_back(tasks.back()->target()); - } - - switch(target_type) - { - case TargetType::StaticLibrary: - tasks.emplace_back(std::make_shared(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(config, settings, config.target, - objects)); - break; - - case TargetType::Executable: - tasks.emplace_back(std::make_shared(config, settings, config.target, - objects)); - break; - } - - return tasks; -} - -std::shared_ptr getNextTask(const std::list>& allTasks, - std::list>& 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 (*cb)(); -}; -std::array 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 (*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 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 args; - for(int i = 1; i < argc; ++i) - { - args.push_back(argv[i]); - } - exit(execute(argv[0], args, settings.verbose > 0)); - } - } -} - -std::list> getTasks(const Settings& settings) -{ - static std::deque build_configs; - std::list> 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 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& configuration()\n"; - istr << "{\n"; - istr << " static std::map 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 +#include +#include + +#include "execute.h" +#include "configure.h" +#include "settings.h" +#include "libcppbuild.h" + +std::array 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 (*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 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 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 +#include + +#include "libcppbuild.h" + +class Settings; + +struct BuildConfigurationEntry +{ + const char* file; + std::vector (*cb)(); +}; + +extern std::array configFiles; +extern std::size_t numConfigFiles; + +//int reg(const char* location, std::vector (*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 +#include +#include + +#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> 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 objects; + std::list> tasks; + for(const auto& file : config.sources) + { + tasks.emplace_back(std::make_shared(config, settings, + sourceDir, file)); + objects.push_back(tasks.back()->target()); + } + + switch(target_type) + { + case TargetType::StaticLibrary: + tasks.emplace_back(std::make_shared(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(config, settings, config.target, + objects)); + break; + + case TargetType::Executable: + tasks.emplace_back(std::make_shared(config, settings, config.target, + objects)); + break; + } + + return tasks; +} + +std::shared_ptr getNextTask(const std::list>& allTasks, + std::list>& 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> getTasks(const Settings& settings) +{ + static std::deque build_configs; + std::list> 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 objects; + auto t = taskFactory(build_config, settings, path); + tasks.insert(tasks.end(), t.begin(), t.end()); + } + } + + return tasks; +} diff --git a/tasks.h b/tasks.h new file mode 100644 index 0000000..119c7d6 --- /dev/null +++ b/tasks.h @@ -0,0 +1,18 @@ +// -*- c++ -*- +#pragma once + +#include +#include +#include + +#include "task.h" + +class BuildConfiguration; +class Settings; + +std::list> taskFactory(const BuildConfiguration& config, + const Settings& settings, + const std::string& sourceDir); +std::shared_ptr getNextTask(const std::list>& allTasks, + std::list>& dirtyTasks); +std::list> 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 + +enum class Tool +{ + CCompiler, + CppCompiler, + Archiver, + Linker, +} + +std::string getTool(Tool tool); -- cgit v1.2.3