From 5da56616cccf4e595ec6a556cf1aef40b37746e3 Mon Sep 17 00:00:00 2001 From: Bent Bisballe Nyeng Date: Sat, 28 Aug 2021 18:59:29 +0200 Subject: Move sources to ... well, src ;) --- bootstrap.sh | 2 +- build.cc | 173 --------------------------- build.h | 18 --- configure.cc | 301 ----------------------------------------------- configure.h | 19 --- cppbuild.cc | 22 ++-- execute.cc | 87 -------------- execute.h | 9 -- libcppbuild.cc | 316 ------------------------------------------------- libcppbuild.h | 76 ------------ rebuild.cc | 141 ---------------------- rebuild.h | 24 ---- settings.h | 11 -- src/build.cc | 173 +++++++++++++++++++++++++++ src/build.h | 18 +++ src/configure.cc | 301 +++++++++++++++++++++++++++++++++++++++++++++++ src/configure.h | 19 +++ src/execute.cc | 87 ++++++++++++++ src/execute.h | 9 ++ src/libcppbuild.cc | 316 +++++++++++++++++++++++++++++++++++++++++++++++++ src/libcppbuild.h | 76 ++++++++++++ src/rebuild.cc | 141 ++++++++++++++++++++++ src/rebuild.h | 24 ++++ src/settings.h | 11 ++ src/task.cc | 146 +++++++++++++++++++++++ src/task.h | 60 ++++++++++ src/task_ar.cc | 221 ++++++++++++++++++++++++++++++++++ src/task_ar.h | 42 +++++++ src/task_cc.cc | 339 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/task_cc.h | 47 ++++++++ src/task_ld.cc | 227 +++++++++++++++++++++++++++++++++++ src/task_ld.h | 42 +++++++ src/task_so.cc | 217 ++++++++++++++++++++++++++++++++++ src/task_so.h | 42 +++++++ src/tasks.cc | 130 ++++++++++++++++++++ src/tasks.h | 18 +++ task.cc | 146 ----------------------- task.h | 60 ---------- task_ar.cc | 221 ---------------------------------- task_ar.h | 42 ------- task_cc.cc | 339 ----------------------------------------------------- task_cc.h | 47 -------- task_ld.cc | 227 ----------------------------------- task_ld.h | 42 ------- task_so.cc | 217 ---------------------------------- task_so.h | 42 ------- tasks.cc | 130 -------------------- tasks.h | 18 --- 48 files changed, 2718 insertions(+), 2718 deletions(-) delete mode 100644 build.cc delete mode 100644 build.h delete mode 100644 configure.cc delete mode 100644 configure.h delete mode 100644 execute.cc delete mode 100644 execute.h delete mode 100644 libcppbuild.cc delete mode 100644 libcppbuild.h delete mode 100644 rebuild.cc delete mode 100644 rebuild.h delete mode 100644 settings.h create mode 100644 src/build.cc create mode 100644 src/build.h create mode 100644 src/configure.cc create mode 100644 src/configure.h create mode 100644 src/execute.cc create mode 100644 src/execute.h create mode 100644 src/libcppbuild.cc create mode 100644 src/libcppbuild.h create mode 100644 src/rebuild.cc create mode 100644 src/rebuild.h create mode 100644 src/settings.h create mode 100644 src/task.cc create mode 100644 src/task.h create mode 100644 src/task_ar.cc create mode 100644 src/task_ar.h create mode 100644 src/task_cc.cc create mode 100644 src/task_cc.h create mode 100644 src/task_ld.cc create mode 100644 src/task_ld.h create mode 100644 src/task_so.cc create mode 100644 src/task_so.h create mode 100644 src/tasks.cc create mode 100644 src/tasks.h delete mode 100644 task.cc delete mode 100644 task.h delete mode 100644 task_ar.cc delete mode 100644 task_ar.h delete mode 100644 task_cc.cc delete mode 100644 task_cc.h delete mode 100644 task_ld.cc delete mode 100644 task_ld.h delete mode 100644 task_so.cc delete mode 100644 task_so.h delete mode 100644 tasks.cc delete mode 100644 tasks.h diff --git a/bootstrap.sh b/bootstrap.sh index 3605a51..e1ee5eb 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -1,2 +1,2 @@ #!/bin/bash -g++ -std=c++17 -I. -pthread *.cc -o cppbuild +g++ -std=c++17 -I. -Isrc -pthread src/*.cc cppbuild.cc -o cppbuild diff --git a/build.cc b/build.cc deleted file mode 100644 index 445979e..0000000 --- a/build.cc +++ /dev/null @@ -1,173 +0,0 @@ -#include "build.h" - -#include -#include -#include -#include -#include -#include - -#include "tasks.h" - -using namespace std::chrono_literals; - -int build(const Settings& settings, - const std::string& name, - const std::list>& tasks, - const std::list>& all_tasks) -{ - if(settings.verbose > 1) - { - std::cout << "Building '" << name << "'\n"; - } - - std::list> dirtyTasks; - for(auto task : tasks) - { - if(task->dirty()) - { - dirtyTasks.push_back(task); - } - } - - if(dirtyTasks.empty()) - { - std::cout << "Nothing to be done for '"<< name << "'\n"; - return 0; - } - - std::list> processes; - - // Start all tasks - bool done{false}; - while(!done) - { - bool started_one{false}; - while(processes.size() < settings.parallel_processes) - { - if(dirtyTasks.empty()) - { - done = true; - break; - } - - auto task = getNextTask(all_tasks, dirtyTasks); - if(task == nullptr) - { - if(processes.empty() && !dirtyTasks.empty()) - { - // No running processes, yet no process to run. This is a dead-lock... - std::cout << "Dead-lock detected.\n"; - return 1; - } - break; - } - - processes.emplace_back( - std::async(std::launch::async, - [task]() - { - return task->run(); - })); - started_one = true; - std::this_thread::sleep_for(2ms); - } - - for(auto process = processes.begin(); - process != processes.end(); - ++process) - { - if(process->valid()) - { - if(process->get() != 0) - { - // TODO: Wait for other processes to finish before returning - return 1; - } - processes.erase(process); - break; - } - } - - if(started_one) - { - std::this_thread::sleep_for(2ms); - } - else - { - std::this_thread::sleep_for(200ms); - } - } - - for(auto process = processes.begin(); - process != processes.end(); - ++process) - { - process->wait(); - auto ret = process->get(); - if(ret != 0) - { - return 1; - } - } - - return 0; -} - -namespace -{ -std::set> getDepTasks(std::shared_ptr task) -{ - std::set> tasks; - tasks.insert(task); - - auto deps = task->getDependsTasks(); - for(const auto& dep : deps) - { - auto depSet = getDepTasks(dep); - for(const auto& dep : depSet) - { - tasks.insert(dep); - } - } - - return tasks; -} -} - -int build(const Settings& settings, - const std::string& name, - const std::list>& all_tasks) -{ - bool task_found{false}; - for(auto task : all_tasks) - { - if(task->name() == name || task->target() == name) - { - std::cout << name << "\n"; - task_found = true; - - auto depSet = getDepTasks(task); - std::list> ts; - for(const auto& task : depSet) - { - ts.push_back(task); - } - auto ret = build(settings, name, ts, all_tasks); - if(ret != 0) - { - return ret; - } - - break; - } - } - - if(!task_found) - { - std::cerr << "*** No rule to make target '" << name << "'. Stop.\n"; - return 1; - } - - return 0; -} diff --git a/build.h b/build.h deleted file mode 100644 index 36e48ad..0000000 --- a/build.h +++ /dev/null @@ -1,18 +0,0 @@ -// -*- c++ -*- -#pragma once - -#include -#include -#include - -#include "task.h" -#include "settings.h" - -int build(const Settings& settings, - const std::string& name, - const std::list>& tasks, - const std::list>& all_tasks); - -int build(const Settings& settings, - const std::string& name, - const std::list>& all_tasks); diff --git a/configure.cc b/configure.cc deleted file mode 100644 index ab2f837..0000000 --- a/configure.cc +++ /dev/null @@ -1,301 +0,0 @@ -#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; -} - -std::string locate(const std::string& arch, const std::string& app) -{ - std::string path_env = std::getenv("PATH"); - std::cout << path_env << "\n"; - - std::string program = app; - if(!arch.empty()) - { - program = arch + "-" + app; - } - std::cout << "Looking for: " << program << "\n"; - std::vector paths; - - { - std::stringstream ss(path_env); - std::string path; - while (std::getline(ss, path, ':')) - { - paths.push_back(path); - } - } - for(const auto& path_str : paths) - { - std::filesystem::path path(path_str); - auto prog_path = path / program; - if(std::filesystem::exists(prog_path)) - { - std::cout << "Found file " << app << " in path: " << path << "\n"; - auto perms = std::filesystem::status(prog_path).permissions(); - if((perms & std::filesystem::perms::owner_exec) != std::filesystem::perms::none) - { - std::cout << " - executable by owner\n"; - } - if((perms & std::filesystem::perms::group_exec) != std::filesystem::perms::none) - { - std::cout << " - executable by group\n"; - } - if((perms & std::filesystem::perms::others_exec) != std::filesystem::perms::none) - { - std::cout << " - executable by others\n"; - } - - return prog_path.string(); - } - } - - std::cerr << "Could not locate " << app << " for the " << arch << " architecture\n"; - exit(1); - return {}; -} - -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{128}; - - std::string build_arch; - std::string build_path; - std::string host_arch; - std::string host_path; - std::string cc_prog = "gcc"; - std::string cxx_prog = "g++"; - std::string ar_prog = "ar"; - std::string ld_prog = "ld"; - - 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("cc", required_argument, key++, - "Use specified c-compiler instead of gcc.", - [&]() { - cc_prog = optarg; - return 0; - }); - - opt.add("cxx", required_argument, key++, - "Use specified c++-compiler instead of g++.", - [&]() { - cxx_prog = optarg; - return 0; - }); - - opt.add("ar", required_argument, key++, - "Use specified archiver instead of ar.", - [&]() { - ar_prog = optarg; - return 0; - }); - - opt.add("ld", required_argument, key++, - "Use specified linker instead of ld.", - [&]() { - ld_prog = optarg; - return 0; - }); - - opt.add("build", required_argument, key++, - "Configure for building on specified architecture.", - [&]() { - build_arch = optarg; - return 0; - }); - - opt.add("build-path", required_argument, key++, - "Set path to build tool-chain.", - [&]() { - build_path = 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("host-path", required_argument, key++, - "Set path to cross-compile tool-chain.", - [&]() { - host_path = 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}; - bool needs_asm{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; - case Language::Asm: - needs_asm = true; - break; - } - } -*/ - auto cc_env = getenv("CC"); - if(cc_env) - { - cmd_str = std::string("CC=") + cc_env + " " + cmd_str; - cc_prog = cc_env; - } - - auto cxx_env = getenv("CXX"); - if(cxx_env) - { - cmd_str = std::string("CXX=") + cxx_env + " " + cmd_str; - cxx_prog = cxx_env; - } - - auto ar_env = getenv("AR"); - if(ar_env) - { - cmd_str = std::string("AR=") + ar_env + " " + cmd_str; - ar_prog = ar_env; - } - - auto ld_env = getenv("LD"); - if(ld_env) - { - cmd_str = std::string("LD=") + ld_env + " " + cmd_str; - ld_prog = ld_env; - } - - std::string host_cc = locate(host_arch, cc_prog); - std::string host_cxx = locate(host_arch, cxx_prog); - std::string host_ar = locate(host_arch, ar_prog); - std::string host_ld = locate(host_arch, ld_prog); - std::string build_cc = locate(build_arch, cc_prog); - std::string build_cxx = locate(build_arch, cxx_prog); - std::string build_ar = locate(build_arch, ar_prog); - std::string build_ld = locate(build_arch, ld_prog); - - std::cout << "Writing results to: " << configurationFile.string() << "\n"; - { - 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::host_cc << "\", \"" << host_cc << "\" },\n"; - istr << " { \"" << cfg::host_cxx << "\", \"" << host_cxx << "\" },\n"; - istr << " { \"" << cfg::host_ar << "\", \"" << host_ar << "\" },\n"; - istr << " { \"" << cfg::host_ld << "\", \"" << host_ld << "\" },\n"; - istr << " { \"" << cfg::build_cc << "\", \"" << build_cc << "\" },\n"; - istr << " { \"" << cfg::build_cxx << "\", \"" << build_cxx << "\" },\n"; - istr << " { \"" << cfg::build_ar << "\", \"" << build_ar << "\" },\n"; - istr << " { \"" << cfg::build_ld << "\", \"" << build_ld << "\" },\n"; - istr << " };\n"; - istr << " return c;\n"; - istr << "}\n"; - } - - { - std::ofstream istr(configHeaderFile); - istr << "#pragma once\n\n"; - istr << "#define HAS_FOO 1\n"; - istr << "//#define HAS_BAR 1\n"; - } - - return 0; -} diff --git a/configure.h b/configure.h deleted file mode 100644 index 95b6765..0000000 --- a/configure.h +++ /dev/null @@ -1,19 +0,0 @@ -// -*- 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/cppbuild.cc b/cppbuild.cc index c78def1..dc3ab19 100644 --- a/cppbuild.cc +++ b/cppbuild.cc @@ -10,17 +10,17 @@ BuildConfigurations cppbuildConfigs() .type = TargetType::StaticLibrary, .target = "libcppbuild.a", .sources = { - "build.cc", - "configure.cc", - "execute.cc", - "libcppbuild.cc", - "rebuild.cc", - "task.cc", - "task_ar.cc", - "task_cc.cc", - "task_ld.cc", - "task_so.cc", - "tasks.cc", + "src/build.cc", + "src/configure.cc", + "src/execute.cc", + "src/libcppbuild.cc", + "src/rebuild.cc", + "src/task.cc", + "src/task_ar.cc", + "src/task_cc.cc", + "src/task_ld.cc", + "src/task_so.cc", + "src/tasks.cc", }, .cxxflags = { "-std=c++17", diff --git a/execute.cc b/execute.cc deleted file mode 100644 index bc4cd5f..0000000 --- a/execute.cc +++ /dev/null @@ -1,87 +0,0 @@ -#include "execute.h" - -#include -#include -#include -#include -#include -#include - -/* -https://blog.famzah.net/2009/11/20/a-much-faster-popen-and-system-implementation-for-linux/ -https://github.com/famzah/popen-noshell/commit/1f9eaf4eeef348d1efe0f3c7fe8ab670653cfbb1 -https://blog.famzah.net/2018/12/19/posix_spawn-performance-benchmarks-and-usage-examples/ -https://stackoverflow.com/questions/4259629/what-is-the-difference-between-fork-and-vfork/5207945#5207945 - */ - - -namespace -{ -int parent_waitpid(pid_t pid) -{ - int status; - - if(waitpid(pid, &status, 0) != pid) - { - return 1; - } - - return status; -} -} // namespace :: - -int execute(const std::string& command, - const std::vector& args, - bool verbose) -{ - std::vector argv; - argv.push_back(command.data()); - for(const auto& arg : args) - { - argv.push_back(arg.data()); - } - argv.push_back(nullptr); - - if(verbose) - { - std::string cmd; - for(const auto& arg : argv) - { - if(arg == nullptr) - { - break; - } - if(!cmd.empty()) - { - cmd += " "; - } - cmd += arg; - } - - std::cout << cmd << "\n"; - } - -#if 1 - auto pid = vfork(); - if(pid == 0) - { - execv(command.data(), (char**)argv.data()); - std::cout << "Could not execute " << command << ": " << - strerror(errno) << "\n"; - _exit(1); // execv only returns if an error occurred - } - auto ret = parent_waitpid(pid); -#elif 0 - pid_t pid; - if(posix_spawn(&pid, command.data(), nullptr, nullptr, - (char**)argv.data(), nullptr)) - { - return 1; - } - auto ret = parent_waitpid(pid); -#else - auto ret = system(cmd.data()); -#endif - - return ret; -} diff --git a/execute.h b/execute.h deleted file mode 100644 index f284230..0000000 --- a/execute.h +++ /dev/null @@ -1,9 +0,0 @@ -// -*- c++ -*- -#pragma once - -#include -#include - -int execute(const std::string& command, - const std::vector& args, - bool verbose = true); diff --git a/libcppbuild.cc b/libcppbuild.cc deleted file mode 100644 index d3d8a51..0000000 --- a/libcppbuild.cc +++ /dev/null @@ -1,316 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "libcppbuild.h" -#include "settings.h" -#include "configure.h" -#include "rebuild.h" -#include "tasks.h" -#include "build.h" -int main(int argc, char* argv[]) -{ - if(argc > 1 && std::string(argv[1]) == "configure") - { - return configure(argc, argv); - } - - Settings settings{}; - - settings.builddir = getConfiguration(cfg::builddir, "build"); - settings.parallel_processes = - std::max(1u, std::thread::hardware_concurrency() * 2 - 1); - settings.verbose = 0; - - bool write_compilation_database{false}; - std::string compilation_database; - bool print_configure_cmd{false}; - bool print_configure_db{false}; - std::vector add_files; - std::vector remove_files; - bool list_files{false}; - bool list_targets{false}; - bool no_relaunch{false}; // true means no re-launch after rebuild. - - dg::Options opt; - int key{128}; - - opt.add("jobs", required_argument, 'j', - "Number of parallel jobs. (default: cpucount * 2 - 1)", - [&]() { - try - { - settings.parallel_processes = std::stoi(optarg); - } - catch(...) - { - std::cerr << "Not a number\n"; - return 1; - } - return 0; - }); - - opt.add("build-dir", required_argument, 'b', - "Overload 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("add", required_argument, 'a', - "Add specified file to the build configurations.", - [&]() { - no_relaunch = true; - add_files.push_back(optarg); - return 0; - }); - - opt.add("remove", required_argument, 'r', - "Remove specified file from the build configurations.", - [&]() { - no_relaunch = true; - remove_files.push_back(optarg); - return 0; - }); - - opt.add("list-files", no_argument, 'L', - "List files in the build configurations.", - [&]() { - no_relaunch = true; - list_files = true; - return 0; - }); - - opt.add("list-targets", no_argument, 'l', - "List targets.", - [&]() { - no_relaunch = true; - list_targets = true; - return 0; - }); - - opt.add("configure-cmd", no_argument, key++, - "Print commandline for last configure.", - [&]() { - no_relaunch = true; - print_configure_cmd = true; - return 0; - }); - - opt.add("configure-db", no_argument, key++, - "Print entire configure parameter database.", - [&]() { - no_relaunch = true; - print_configure_db = true; - return 0; - }); - - opt.add("database", required_argument, 'd', - "Write compilation database json file.", - [&]() { - no_relaunch = true; - write_compilation_database = true; - compilation_database = optarg; - return 0; - }); - - opt.add("help", no_argument, 'h', - "Print this help text.", - [&]() { - std::cout << "Usage: " << argv[0] << " [options] [target] ...\n"; - std::cout << -R"_( where target can be either: - configure - run configuration step (cannot be used with other targets). - clean - clean all generated files. - all - build all targets (default) - or the name of a target which will be built along with its dependencies. - Use '-l' to see a list of possible target names. - -Options: -)_"; - opt.help(); - exit(0); - return 0; - }); - - opt.process(argc, argv); - - auto verbose_env = std::getenv("V"); - if(verbose_env) - { - settings.verbose = std::atoi(verbose_env); - } - - if(list_files) - { - std::set files; - for(std::size_t i = 0; i < numConfigFiles; ++i) - { - files.insert(configFiles[i].file); - } - - for(const auto& file : files) - { - std::cout << file << "\n"; - } - } - - if(!add_files.empty() || !remove_files.empty()) - { - for(const auto& add_file : add_files) - { - reg(add_file.data(), [](){ return std::vector{};}); - } - - for(const auto& remove_file : remove_files) - { - unreg(remove_file.data()); - } - - // Force rebuild if files were added - recompileCheck(settings, 1, argv, true, no_relaunch == false); - } - - recompileCheck(settings, argc, argv); - - std::filesystem::path builddir(settings.builddir); - std::filesystem::create_directories(builddir); - - auto all_tasks = getTasks(settings); - - if(list_targets) - { - for(const auto& task : all_tasks) - { - if(task->targetType() != TargetType::Object) - { - std::cout << task->name() << "\n"; - } - } - } - - if(write_compilation_database) - { - std::ofstream istr(compilation_database); - istr << "["; - bool first{true}; - for(auto task : all_tasks) - { - auto s = task->toJSON(); - if(!s.empty()) - { - if(!first) - { - istr << ",\n"; - } - else - { - istr << "\n"; - } - first = false; - istr << s; - } - } - istr << "\n]\n"; - } - - if(print_configure_cmd) - { - std::cout << getConfiguration("cmd") << "\n"; - } - - if(print_configure_db) - { - const auto& c = configuration(); - for(const auto& config : c) - { - std::cout << config.first << ": " << config.second << "\n"; - } - } - - for(auto task : all_tasks) - { - if(task->registerDepTasks(all_tasks)) - { - return 1; - } - } - - bool build_all{true}; - for(const auto& arg : opt.arguments()) - { - if(arg == "configure") - { - std::cerr << "The 'configure' target must be the first argument.\n"; - return 1; - } - - if(arg == "clean") - { - build_all = false; - - std::cout << "Cleaning\n"; - for(auto& task : all_tasks) - { - if(task->clean() != 0) - { - return 1; - } - } - } - else - { - build_all = false; - - if(arg == "all") - { - auto ret = build(settings, "all", all_tasks, all_tasks); - if(ret != 0) - { - return ret; - } - } - else - { - auto ret = build(settings, arg, all_tasks); - if(ret != 0) - { - return ret; - } - } - } - } - - if(build_all) - { - auto ret = build(settings, "all", all_tasks, all_tasks); - if(ret != 0) - { - return ret; - } - } - - return 0; -} diff --git a/libcppbuild.h b/libcppbuild.h deleted file mode 100644 index d0a0080..0000000 --- a/libcppbuild.h +++ /dev/null @@ -1,76 +0,0 @@ -// -*- c++ -*- -#pragma once - -#include -#include -#include - -enum class TargetType -{ - Auto, // Default - deduce from target name and sources extensions - - Executable, - StaticLibrary, - DynamicLibrary, - Object, -}; - -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 BuildConfiguration -{ - TargetType type{TargetType::Auto}; - Language language{Language::Auto}; - OutputSystem system{OutputSystem::Host}; - std::string target; - std::vector sources; // source list - std::vector depends; // internal dependencies - std::vector cxxflags; // flags for c++ compiler - std::vector cflags; // flags for c compiler - std::vector ldflags; // flags for linker - std::vector asmflags; // flags for asm translator -}; - -using BuildConfigurations = std::vector; - -int reg(const char* location, BuildConfigurations (*cb)()); - -// Convenience macro - ugly but keeps things simple(r) -#define CONCAT(a, b) CONCAT_INNER(a, b) -#define CONCAT_INNER(a, b) a ## b -#define UNIQUE_NAME(base) CONCAT(base, __LINE__) -#define REG(cb) namespace { int UNIQUE_NAME(unique) = reg(__FILE__, cb); } - -// 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"; -} - -const std::map& configuration(); -bool hasConfiguration(const std::string& key); -const std::string& getConfiguration(const std::string& key, - const std::string& defaultValue = {}); diff --git a/rebuild.cc b/rebuild.cc deleted file mode 100644 index 43c4c98..0000000 --- a/rebuild.cc +++ /dev/null @@ -1,141 +0,0 @@ -#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 relaunch_allowed) -{ - 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(argv[0]); - - 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::build_cxx, "/usr/bin/g++"); - auto ret = execute(tool, args, settings.verbose > 0); - if(ret != 0) - { - std::cerr << "Failed: ." << ret << "\n"; - exit(1); - } - else - { - if(relaunch_allowed) - { - 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 deleted file mode 100644 index bc5d889..0000000 --- a/rebuild.h +++ /dev/null @@ -1,24 +0,0 @@ -// -*- 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, bool relaunch_allowed = true); diff --git a/settings.h b/settings.h deleted file mode 100644 index 6b8729f..0000000 --- a/settings.h +++ /dev/null @@ -1,11 +0,0 @@ -// -*- c++ -*- -#pragma once - -#include - -struct Settings -{ - std::string builddir; - std::size_t parallel_processes; - int verbose{1}; -}; diff --git a/src/build.cc b/src/build.cc new file mode 100644 index 0000000..445979e --- /dev/null +++ b/src/build.cc @@ -0,0 +1,173 @@ +#include "build.h" + +#include +#include +#include +#include +#include +#include + +#include "tasks.h" + +using namespace std::chrono_literals; + +int build(const Settings& settings, + const std::string& name, + const std::list>& tasks, + const std::list>& all_tasks) +{ + if(settings.verbose > 1) + { + std::cout << "Building '" << name << "'\n"; + } + + std::list> dirtyTasks; + for(auto task : tasks) + { + if(task->dirty()) + { + dirtyTasks.push_back(task); + } + } + + if(dirtyTasks.empty()) + { + std::cout << "Nothing to be done for '"<< name << "'\n"; + return 0; + } + + std::list> processes; + + // Start all tasks + bool done{false}; + while(!done) + { + bool started_one{false}; + while(processes.size() < settings.parallel_processes) + { + if(dirtyTasks.empty()) + { + done = true; + break; + } + + auto task = getNextTask(all_tasks, dirtyTasks); + if(task == nullptr) + { + if(processes.empty() && !dirtyTasks.empty()) + { + // No running processes, yet no process to run. This is a dead-lock... + std::cout << "Dead-lock detected.\n"; + return 1; + } + break; + } + + processes.emplace_back( + std::async(std::launch::async, + [task]() + { + return task->run(); + })); + started_one = true; + std::this_thread::sleep_for(2ms); + } + + for(auto process = processes.begin(); + process != processes.end(); + ++process) + { + if(process->valid()) + { + if(process->get() != 0) + { + // TODO: Wait for other processes to finish before returning + return 1; + } + processes.erase(process); + break; + } + } + + if(started_one) + { + std::this_thread::sleep_for(2ms); + } + else + { + std::this_thread::sleep_for(200ms); + } + } + + for(auto process = processes.begin(); + process != processes.end(); + ++process) + { + process->wait(); + auto ret = process->get(); + if(ret != 0) + { + return 1; + } + } + + return 0; +} + +namespace +{ +std::set> getDepTasks(std::shared_ptr task) +{ + std::set> tasks; + tasks.insert(task); + + auto deps = task->getDependsTasks(); + for(const auto& dep : deps) + { + auto depSet = getDepTasks(dep); + for(const auto& dep : depSet) + { + tasks.insert(dep); + } + } + + return tasks; +} +} + +int build(const Settings& settings, + const std::string& name, + const std::list>& all_tasks) +{ + bool task_found{false}; + for(auto task : all_tasks) + { + if(task->name() == name || task->target() == name) + { + std::cout << name << "\n"; + task_found = true; + + auto depSet = getDepTasks(task); + std::list> ts; + for(const auto& task : depSet) + { + ts.push_back(task); + } + auto ret = build(settings, name, ts, all_tasks); + if(ret != 0) + { + return ret; + } + + break; + } + } + + if(!task_found) + { + std::cerr << "*** No rule to make target '" << name << "'. Stop.\n"; + return 1; + } + + return 0; +} diff --git a/src/build.h b/src/build.h new file mode 100644 index 0000000..36e48ad --- /dev/null +++ b/src/build.h @@ -0,0 +1,18 @@ +// -*- c++ -*- +#pragma once + +#include +#include +#include + +#include "task.h" +#include "settings.h" + +int build(const Settings& settings, + const std::string& name, + const std::list>& tasks, + const std::list>& all_tasks); + +int build(const Settings& settings, + const std::string& name, + const std::list>& all_tasks); diff --git a/src/configure.cc b/src/configure.cc new file mode 100644 index 0000000..ab2f837 --- /dev/null +++ b/src/configure.cc @@ -0,0 +1,301 @@ +#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; +} + +std::string locate(const std::string& arch, const std::string& app) +{ + std::string path_env = std::getenv("PATH"); + std::cout << path_env << "\n"; + + std::string program = app; + if(!arch.empty()) + { + program = arch + "-" + app; + } + std::cout << "Looking for: " << program << "\n"; + std::vector paths; + + { + std::stringstream ss(path_env); + std::string path; + while (std::getline(ss, path, ':')) + { + paths.push_back(path); + } + } + for(const auto& path_str : paths) + { + std::filesystem::path path(path_str); + auto prog_path = path / program; + if(std::filesystem::exists(prog_path)) + { + std::cout << "Found file " << app << " in path: " << path << "\n"; + auto perms = std::filesystem::status(prog_path).permissions(); + if((perms & std::filesystem::perms::owner_exec) != std::filesystem::perms::none) + { + std::cout << " - executable by owner\n"; + } + if((perms & std::filesystem::perms::group_exec) != std::filesystem::perms::none) + { + std::cout << " - executable by group\n"; + } + if((perms & std::filesystem::perms::others_exec) != std::filesystem::perms::none) + { + std::cout << " - executable by others\n"; + } + + return prog_path.string(); + } + } + + std::cerr << "Could not locate " << app << " for the " << arch << " architecture\n"; + exit(1); + return {}; +} + +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{128}; + + std::string build_arch; + std::string build_path; + std::string host_arch; + std::string host_path; + std::string cc_prog = "gcc"; + std::string cxx_prog = "g++"; + std::string ar_prog = "ar"; + std::string ld_prog = "ld"; + + 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("cc", required_argument, key++, + "Use specified c-compiler instead of gcc.", + [&]() { + cc_prog = optarg; + return 0; + }); + + opt.add("cxx", required_argument, key++, + "Use specified c++-compiler instead of g++.", + [&]() { + cxx_prog = optarg; + return 0; + }); + + opt.add("ar", required_argument, key++, + "Use specified archiver instead of ar.", + [&]() { + ar_prog = optarg; + return 0; + }); + + opt.add("ld", required_argument, key++, + "Use specified linker instead of ld.", + [&]() { + ld_prog = optarg; + return 0; + }); + + opt.add("build", required_argument, key++, + "Configure for building on specified architecture.", + [&]() { + build_arch = optarg; + return 0; + }); + + opt.add("build-path", required_argument, key++, + "Set path to build tool-chain.", + [&]() { + build_path = 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("host-path", required_argument, key++, + "Set path to cross-compile tool-chain.", + [&]() { + host_path = 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}; + bool needs_asm{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; + case Language::Asm: + needs_asm = true; + break; + } + } +*/ + auto cc_env = getenv("CC"); + if(cc_env) + { + cmd_str = std::string("CC=") + cc_env + " " + cmd_str; + cc_prog = cc_env; + } + + auto cxx_env = getenv("CXX"); + if(cxx_env) + { + cmd_str = std::string("CXX=") + cxx_env + " " + cmd_str; + cxx_prog = cxx_env; + } + + auto ar_env = getenv("AR"); + if(ar_env) + { + cmd_str = std::string("AR=") + ar_env + " " + cmd_str; + ar_prog = ar_env; + } + + auto ld_env = getenv("LD"); + if(ld_env) + { + cmd_str = std::string("LD=") + ld_env + " " + cmd_str; + ld_prog = ld_env; + } + + std::string host_cc = locate(host_arch, cc_prog); + std::string host_cxx = locate(host_arch, cxx_prog); + std::string host_ar = locate(host_arch, ar_prog); + std::string host_ld = locate(host_arch, ld_prog); + std::string build_cc = locate(build_arch, cc_prog); + std::string build_cxx = locate(build_arch, cxx_prog); + std::string build_ar = locate(build_arch, ar_prog); + std::string build_ld = locate(build_arch, ld_prog); + + std::cout << "Writing results to: " << configurationFile.string() << "\n"; + { + 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::host_cc << "\", \"" << host_cc << "\" },\n"; + istr << " { \"" << cfg::host_cxx << "\", \"" << host_cxx << "\" },\n"; + istr << " { \"" << cfg::host_ar << "\", \"" << host_ar << "\" },\n"; + istr << " { \"" << cfg::host_ld << "\", \"" << host_ld << "\" },\n"; + istr << " { \"" << cfg::build_cc << "\", \"" << build_cc << "\" },\n"; + istr << " { \"" << cfg::build_cxx << "\", \"" << build_cxx << "\" },\n"; + istr << " { \"" << cfg::build_ar << "\", \"" << build_ar << "\" },\n"; + istr << " { \"" << cfg::build_ld << "\", \"" << build_ld << "\" },\n"; + istr << " };\n"; + istr << " return c;\n"; + istr << "}\n"; + } + + { + std::ofstream istr(configHeaderFile); + istr << "#pragma once\n\n"; + istr << "#define HAS_FOO 1\n"; + istr << "//#define HAS_BAR 1\n"; + } + + return 0; +} diff --git a/src/configure.h b/src/configure.h new file mode 100644 index 0000000..95b6765 --- /dev/null +++ b/src/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/src/execute.cc b/src/execute.cc new file mode 100644 index 0000000..bc4cd5f --- /dev/null +++ b/src/execute.cc @@ -0,0 +1,87 @@ +#include "execute.h" + +#include +#include +#include +#include +#include +#include + +/* +https://blog.famzah.net/2009/11/20/a-much-faster-popen-and-system-implementation-for-linux/ +https://github.com/famzah/popen-noshell/commit/1f9eaf4eeef348d1efe0f3c7fe8ab670653cfbb1 +https://blog.famzah.net/2018/12/19/posix_spawn-performance-benchmarks-and-usage-examples/ +https://stackoverflow.com/questions/4259629/what-is-the-difference-between-fork-and-vfork/5207945#5207945 + */ + + +namespace +{ +int parent_waitpid(pid_t pid) +{ + int status; + + if(waitpid(pid, &status, 0) != pid) + { + return 1; + } + + return status; +} +} // namespace :: + +int execute(const std::string& command, + const std::vector& args, + bool verbose) +{ + std::vector argv; + argv.push_back(command.data()); + for(const auto& arg : args) + { + argv.push_back(arg.data()); + } + argv.push_back(nullptr); + + if(verbose) + { + std::string cmd; + for(const auto& arg : argv) + { + if(arg == nullptr) + { + break; + } + if(!cmd.empty()) + { + cmd += " "; + } + cmd += arg; + } + + std::cout << cmd << "\n"; + } + +#if 1 + auto pid = vfork(); + if(pid == 0) + { + execv(command.data(), (char**)argv.data()); + std::cout << "Could not execute " << command << ": " << + strerror(errno) << "\n"; + _exit(1); // execv only returns if an error occurred + } + auto ret = parent_waitpid(pid); +#elif 0 + pid_t pid; + if(posix_spawn(&pid, command.data(), nullptr, nullptr, + (char**)argv.data(), nullptr)) + { + return 1; + } + auto ret = parent_waitpid(pid); +#else + auto ret = system(cmd.data()); +#endif + + return ret; +} diff --git a/src/execute.h b/src/execute.h new file mode 100644 index 0000000..f284230 --- /dev/null +++ b/src/execute.h @@ -0,0 +1,9 @@ +// -*- c++ -*- +#pragma once + +#include +#include + +int execute(const std::string& command, + const std::vector& args, + bool verbose = true); diff --git a/src/libcppbuild.cc b/src/libcppbuild.cc new file mode 100644 index 0000000..d3d8a51 --- /dev/null +++ b/src/libcppbuild.cc @@ -0,0 +1,316 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "libcppbuild.h" +#include "settings.h" +#include "configure.h" +#include "rebuild.h" +#include "tasks.h" +#include "build.h" +int main(int argc, char* argv[]) +{ + if(argc > 1 && std::string(argv[1]) == "configure") + { + return configure(argc, argv); + } + + Settings settings{}; + + settings.builddir = getConfiguration(cfg::builddir, "build"); + settings.parallel_processes = + std::max(1u, std::thread::hardware_concurrency() * 2 - 1); + settings.verbose = 0; + + bool write_compilation_database{false}; + std::string compilation_database; + bool print_configure_cmd{false}; + bool print_configure_db{false}; + std::vector add_files; + std::vector remove_files; + bool list_files{false}; + bool list_targets{false}; + bool no_relaunch{false}; // true means no re-launch after rebuild. + + dg::Options opt; + int key{128}; + + opt.add("jobs", required_argument, 'j', + "Number of parallel jobs. (default: cpucount * 2 - 1)", + [&]() { + try + { + settings.parallel_processes = std::stoi(optarg); + } + catch(...) + { + std::cerr << "Not a number\n"; + return 1; + } + return 0; + }); + + opt.add("build-dir", required_argument, 'b', + "Overload 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("add", required_argument, 'a', + "Add specified file to the build configurations.", + [&]() { + no_relaunch = true; + add_files.push_back(optarg); + return 0; + }); + + opt.add("remove", required_argument, 'r', + "Remove specified file from the build configurations.", + [&]() { + no_relaunch = true; + remove_files.push_back(optarg); + return 0; + }); + + opt.add("list-files", no_argument, 'L', + "List files in the build configurations.", + [&]() { + no_relaunch = true; + list_files = true; + return 0; + }); + + opt.add("list-targets", no_argument, 'l', + "List targets.", + [&]() { + no_relaunch = true; + list_targets = true; + return 0; + }); + + opt.add("configure-cmd", no_argument, key++, + "Print commandline for last configure.", + [&]() { + no_relaunch = true; + print_configure_cmd = true; + return 0; + }); + + opt.add("configure-db", no_argument, key++, + "Print entire configure parameter database.", + [&]() { + no_relaunch = true; + print_configure_db = true; + return 0; + }); + + opt.add("database", required_argument, 'd', + "Write compilation database json file.", + [&]() { + no_relaunch = true; + write_compilation_database = true; + compilation_database = optarg; + return 0; + }); + + opt.add("help", no_argument, 'h', + "Print this help text.", + [&]() { + std::cout << "Usage: " << argv[0] << " [options] [target] ...\n"; + std::cout << +R"_( where target can be either: + configure - run configuration step (cannot be used with other targets). + clean - clean all generated files. + all - build all targets (default) + or the name of a target which will be built along with its dependencies. + Use '-l' to see a list of possible target names. + +Options: +)_"; + opt.help(); + exit(0); + return 0; + }); + + opt.process(argc, argv); + + auto verbose_env = std::getenv("V"); + if(verbose_env) + { + settings.verbose = std::atoi(verbose_env); + } + + if(list_files) + { + std::set files; + for(std::size_t i = 0; i < numConfigFiles; ++i) + { + files.insert(configFiles[i].file); + } + + for(const auto& file : files) + { + std::cout << file << "\n"; + } + } + + if(!add_files.empty() || !remove_files.empty()) + { + for(const auto& add_file : add_files) + { + reg(add_file.data(), [](){ return std::vector{};}); + } + + for(const auto& remove_file : remove_files) + { + unreg(remove_file.data()); + } + + // Force rebuild if files were added + recompileCheck(settings, 1, argv, true, no_relaunch == false); + } + + recompileCheck(settings, argc, argv); + + std::filesystem::path builddir(settings.builddir); + std::filesystem::create_directories(builddir); + + auto all_tasks = getTasks(settings); + + if(list_targets) + { + for(const auto& task : all_tasks) + { + if(task->targetType() != TargetType::Object) + { + std::cout << task->name() << "\n"; + } + } + } + + if(write_compilation_database) + { + std::ofstream istr(compilation_database); + istr << "["; + bool first{true}; + for(auto task : all_tasks) + { + auto s = task->toJSON(); + if(!s.empty()) + { + if(!first) + { + istr << ",\n"; + } + else + { + istr << "\n"; + } + first = false; + istr << s; + } + } + istr << "\n]\n"; + } + + if(print_configure_cmd) + { + std::cout << getConfiguration("cmd") << "\n"; + } + + if(print_configure_db) + { + const auto& c = configuration(); + for(const auto& config : c) + { + std::cout << config.first << ": " << config.second << "\n"; + } + } + + for(auto task : all_tasks) + { + if(task->registerDepTasks(all_tasks)) + { + return 1; + } + } + + bool build_all{true}; + for(const auto& arg : opt.arguments()) + { + if(arg == "configure") + { + std::cerr << "The 'configure' target must be the first argument.\n"; + return 1; + } + + if(arg == "clean") + { + build_all = false; + + std::cout << "Cleaning\n"; + for(auto& task : all_tasks) + { + if(task->clean() != 0) + { + return 1; + } + } + } + else + { + build_all = false; + + if(arg == "all") + { + auto ret = build(settings, "all", all_tasks, all_tasks); + if(ret != 0) + { + return ret; + } + } + else + { + auto ret = build(settings, arg, all_tasks); + if(ret != 0) + { + return ret; + } + } + } + } + + if(build_all) + { + auto ret = build(settings, "all", all_tasks, all_tasks); + if(ret != 0) + { + return ret; + } + } + + return 0; +} diff --git a/src/libcppbuild.h b/src/libcppbuild.h new file mode 100644 index 0000000..d0a0080 --- /dev/null +++ b/src/libcppbuild.h @@ -0,0 +1,76 @@ +// -*- c++ -*- +#pragma once + +#include +#include +#include + +enum class TargetType +{ + Auto, // Default - deduce from target name and sources extensions + + Executable, + StaticLibrary, + DynamicLibrary, + Object, +}; + +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 BuildConfiguration +{ + TargetType type{TargetType::Auto}; + Language language{Language::Auto}; + OutputSystem system{OutputSystem::Host}; + std::string target; + std::vector sources; // source list + std::vector depends; // internal dependencies + std::vector cxxflags; // flags for c++ compiler + std::vector cflags; // flags for c compiler + std::vector ldflags; // flags for linker + std::vector asmflags; // flags for asm translator +}; + +using BuildConfigurations = std::vector; + +int reg(const char* location, BuildConfigurations (*cb)()); + +// Convenience macro - ugly but keeps things simple(r) +#define CONCAT(a, b) CONCAT_INNER(a, b) +#define CONCAT_INNER(a, b) a ## b +#define UNIQUE_NAME(base) CONCAT(base, __LINE__) +#define REG(cb) namespace { int UNIQUE_NAME(unique) = reg(__FILE__, cb); } + +// 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"; +} + +const std::map& 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 new file mode 100644 index 0000000..43c4c98 --- /dev/null +++ b/src/rebuild.cc @@ -0,0 +1,141 @@ +#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 relaunch_allowed) +{ + 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(argv[0]); + + 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::build_cxx, "/usr/bin/g++"); + auto ret = execute(tool, args, settings.verbose > 0); + if(ret != 0) + { + std::cerr << "Failed: ." << ret << "\n"; + exit(1); + } + else + { + if(relaunch_allowed) + { + 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/src/rebuild.h b/src/rebuild.h new file mode 100644 index 0000000..bc5d889 --- /dev/null +++ b/src/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, bool relaunch_allowed = true); diff --git a/src/settings.h b/src/settings.h new file mode 100644 index 0000000..6b8729f --- /dev/null +++ b/src/settings.h @@ -0,0 +1,11 @@ +// -*- c++ -*- +#pragma once + +#include + +struct Settings +{ + std::string builddir; + std::size_t parallel_processes; + int verbose{1}; +}; diff --git a/src/task.cc b/src/task.cc new file mode 100644 index 0000000..962a02b --- /dev/null +++ b/src/task.cc @@ -0,0 +1,146 @@ +#include "task.h" + +#include +#include + +Task::Task(const BuildConfiguration& config, + const std::vector& depends) + : dependsStr(depends) + , config(config) + , output_system(config.system) +{ +} + +int Task::registerDepTasks(const std::list>& tasks) +{ + for(auto const& depStr : dependsStr) + { + bool found{false}; + for(const auto& task : tasks) + { + if(task->target() == depStr) + { + dependsTasks.push_back(task); + found = true; + } + } + if(!found) + { + std::cerr << "Could not find dependency " << depStr << " needed by " << + target() << " target\n"; + return 1; + } + } + + return 0; +} + +std::string Task::name() const +{ + return config.target; +} + +bool Task::dirty() +{ + for(const auto& task : dependsTasks) + { + if(task->dirty()) + { + return true; + } + } + + return dirtyInner(); +} + +bool Task::ready() +{ + for(const auto& task : dependsTasks) + { + if(task->dirty() || task->state() == State::Running) + { + return false; + } + } + + task_state.store(State::Ready); + return true; +} + +int Task::run() +{ + if(task_state.load() == State::Done) + { + return 0; + } + + task_state.store(State::Running); + auto ret = runInner(); + if(ret == 0) + { + task_state.store(State::Done); + } + else + { + task_state.store(State::Error); + } + + return ret; +} + +State Task::state() const +{ + return task_state.load(); +} + +const BuildConfiguration& Task::buildConfig() const +{ + return config; +} + +TargetType Task::targetType() const +{ + return target_type; +} + +Language Task::sourceLanguage() const +{ + return source_language; +} + +OutputSystem Task::outputSystem() const +{ + return output_system; +} + +std::string Task::compiler() const +{ + switch(sourceLanguage()) + { + case 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 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++"); + } + default: + std::cerr << "Unknown CC target type\n"; + exit(1); + break; + } +} + +std::list> Task::getDependsTasks() +{ + return dependsTasks; +} diff --git a/src/task.h b/src/task.h new file mode 100644 index 0000000..98363a1 --- /dev/null +++ b/src/task.h @@ -0,0 +1,60 @@ +// -*- c++ -*- +#pragma once + +#include +#include +#include +#include +#include + +#include "libcppbuild.h" + +enum class State +{ + Unknown, + Ready, + Running, + Done, + Error, +}; + +class Task +{ +public: + Task(const BuildConfiguration& config, + const std::vector& depends = {}); + + int registerDepTasks(const std::list>& tasks); + + virtual std::string name() const; + bool dirty(); + bool ready(); + int run(); + State state() const; + virtual int clean() = 0 ; + virtual std::vector depends() const = 0; + virtual std::string target() const = 0; + + virtual std::string toJSON() const { return {}; }; + + const BuildConfiguration& buildConfig() const; + + TargetType targetType() const; + Language sourceLanguage() const; + OutputSystem outputSystem() const; + std::string compiler() const; + + std::list> getDependsTasks(); + +protected: + std::atomic task_state{State::Unknown}; + virtual int runInner() { return 0; }; + virtual bool dirtyInner() { return false; } + + std::vector dependsStr; + std::list> dependsTasks; + const BuildConfiguration& config; + TargetType target_type{TargetType::Auto}; + Language source_language{Language::Auto}; + OutputSystem output_system{OutputSystem::Host}; +}; diff --git a/src/task_ar.cc b/src/task_ar.cc new file mode 100644 index 0000000..5568629 --- /dev/null +++ b/src/task_ar.cc @@ -0,0 +1,221 @@ +#include "task_ar.h" + +#include +#include + +#include "libcppbuild.h" +#include "settings.h" +#include "execute.h" + +namespace +{ +std::string readFile(const std::string &fileName) +{ + std::ifstream ifs(fileName.c_str(), + std::ios::in | std::ios::binary | std::ios::ate); + + std::ifstream::pos_type fileSize = ifs.tellg(); + ifs.seekg(0, std::ios::beg); + + std::vector bytes(fileSize); + ifs.read(bytes.data(), fileSize); + + return std::string(bytes.data(), fileSize); +} + +std::vector addPrefix(const std::vector& lst, + const Settings& settings) +{ + std::vector out; + for(const auto& item : lst) + { + std::filesystem::path file = settings.builddir; + file /= item; + out.push_back(file.string()); + } + return out; +} +} // namespace :: + +TaskAR::TaskAR(const BuildConfiguration& config, + const Settings& settings, + const std::string& target, + const std::vector& objects) + : Task(config, addPrefix(config.depends, settings)) + , config(config) + , settings(settings) +{ + targetFile = settings.builddir; + targetFile /= target; + for(const auto& object : objects) + { + std::filesystem::path objectFile = object; + objectFiles.push_back(objectFile); + dependsStr.push_back(objectFile.string()); + } + + for(const auto& dep : config.depends) + { + std::filesystem::path depFile = settings.builddir; + depFile /= dep; + depFiles.push_back(depFile); + } + + flagsFile = settings.builddir / targetFile.stem(); + flagsFile += ".flags"; + + target_type = TargetType::StaticLibrary; + source_language = Language::C; + for(const auto& source : config.sources) + { + std::filesystem::path sourceFile(source); + if(sourceFile.extension().string() != ".c") + { + source_language = Language::Cpp; + } + } +} + +bool TaskAR::dirtyInner() +{ + if(!std::filesystem::exists(targetFile)) + { + return true; + } + + if(!std::filesystem::exists(flagsFile)) + { + 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) + { + //std::cout << "The compiler flags changed\n"; + return true; + } + } + + return false; +} + +int TaskAR::runInner() +{ + std::string objectlist; + for(const auto& objectFile : objectFiles) + { + if(!objectlist.empty()) + { + objectlist += " "; + } + objectlist += objectFile.string(); + } + + std::vector args; + args.push_back("rcs"); + args.push_back(targetFile.string()); + for(const auto& objectFile : objectFiles) + { + args.push_back(objectFile.string()); + } + for(const auto& flag : config.ldflags) + { + args.push_back(flag); + } + + { // Write flags to file. + std::ofstream flagsStream(flagsFile); + flagsStream << flagsString(); + } + + if(settings.verbose == 0) + { + std::cout << "AR => " << targetFile.string() << "\n"; + } + + std::string tool; + switch(outputSystem()) + { + case OutputSystem::Host: + tool = getConfiguration(cfg::host_ar, "/usr/bin/ar"); + break; + case OutputSystem::Build: + tool = getConfiguration(cfg::build_ar, "/usr/bin/ar"); + break; + } + + return execute(tool, args, settings.verbose > 0); +} + +int TaskAR::clean() +{ + if(std::filesystem::exists(targetFile)) + { + std::cout << "Removing " << targetFile.string() << "\n"; + std::filesystem::remove(targetFile); + } + + if(std::filesystem::exists(flagsFile)) + { + std::cout << "Removing " << flagsFile.string() << "\n"; + std::filesystem::remove(flagsFile); + } + + return 0; +} + +std::vector TaskAR::depends() const +{ + std::vector deps; + for(const auto& objectFile : objectFiles) + { + deps.push_back(objectFile.string()); + } + + for(const auto& depFile : depFiles) + { + deps.push_back(depFile.string()); + } + + return deps; +} + +std::string TaskAR::target() const +{ + return targetFile.string(); +} + +std::string TaskAR::flagsString() const +{ + std::string flagsStr; + for(const auto& flag : config.ldflags) + { + if(flag != config.ldflags[0]) + { + flagsStr += " "; + } + flagsStr += flag; + } + flagsStr += "\n"; + + for(const auto& dep : config.depends) + { + if(dep != config.depends[0]) + { + flagsStr += " "; + } + flagsStr += dep; + } + + return flagsStr; +} diff --git a/src/task_ar.h b/src/task_ar.h new file mode 100644 index 0000000..bfa21a2 --- /dev/null +++ b/src/task_ar.h @@ -0,0 +1,42 @@ +// -*- c++ -*- +#pragma once + +#include "task.h" + +#include +#include +#include +#include + +struct BuildConfiguration; +struct Settings; + +class TaskAR + : public Task +{ +public: + TaskAR(const BuildConfiguration& config, + const Settings& settings, + const std::string& target, + const std::vector& objects); + + bool dirtyInner() override; + + int runInner() override; + int clean() override; + + std::vector depends() const override; + + std::string target() const override; + +private: + std::string flagsString() const; + + std::vector objectFiles; + std::vector depFiles; + std::filesystem::path targetFile; + std::filesystem::path flagsFile; + + const BuildConfiguration& config; + const Settings& settings; +}; diff --git a/src/task_cc.cc b/src/task_cc.cc new file mode 100644 index 0000000..af9cf7a --- /dev/null +++ b/src/task_cc.cc @@ -0,0 +1,339 @@ +#include "task_cc.h" + +#include +#include + +#include "libcppbuild.h" +#include "settings.h" +#include "execute.h" + +namespace +{ +std::string readFile(const std::string &fileName) +{ + std::ifstream ifs(fileName.c_str(), std::ios::in | std::ios::binary | std::ios::ate); + + std::ifstream::pos_type fileSize = ifs.tellg(); + ifs.seekg(0, std::ios::beg); + + std::vector bytes(fileSize); + ifs.read(bytes.data(), fileSize); + + return std::string(bytes.data(), fileSize); +} + +std::vector readDeps(const std::string& depFile) +{ + if(!std::filesystem::exists(depFile)) + { + return {}; + } + + auto str = readFile(depFile); + + std::vector output; + std::string tmp; + bool start{false}; + bool in_whitespace{false}; + for(const auto& c : str) + { + if(c == '\\' || c == '\n') + { + continue; + } + + if(c == ':') + { + start = true; + continue; + } + + if(!start) + { + continue; + } + + if(c == ' ' || c == '\t') + { + if(in_whitespace) + { + continue; + } + + if(!tmp.empty()) + { + output.push_back(tmp); + } + tmp.clear(); + in_whitespace = true; + } + else + { + in_whitespace = false; + tmp += c; + } + } + + if(!tmp.empty()) + { + output.push_back(tmp); + } + + return output; +} +} // namespace :: + +TaskCC::TaskCC(const BuildConfiguration& config, const Settings& settings, + const std::string& sourceDir, const std::string& source) + : Task(config) + , config(config) + , settings(settings) + , sourceDir(sourceDir) +{ + sourceFile = sourceDir; + sourceFile /= source; + + std::filesystem::path base = settings.builddir; + base /= config.target; + base += "-"; + base += sourceFile.stem(); + + if(sourceFile.extension().string() == ".c") + { + base += "_c"; + } + else + { + base += "_cc"; + } + + targetFile = base; + targetFile += ".o"; + depsFile = base; + depsFile += ".d"; + flagsFile = base; + flagsFile += ".flags"; + + target_type = TargetType::Object; + if(sourceFile.extension().string() == ".c") + { + source_language = Language::C; + } + else + { + source_language = Language::Cpp; + } +} + +std::string TaskCC::name() const +{ + return target(); +} + +bool TaskCC::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::exists(depsFile)) + { + //std::cout << "Missing depsFile\n"; + return true; + } + + if(!std::filesystem::exists(flagsFile)) + { + //std::cout << "Missing flagsFile\n"; + return true; + } + + if(std::filesystem::last_write_time(sourceFile) > + std::filesystem::last_write_time(depsFile)) + { + //std::cout << "The sourceFile newer than depsFile\n"; + return true; + } + + { + auto lastFlags = readFile(flagsFile.string()); + if(flagsString() != lastFlags) + { + //std::cout << "The compiler flags changed\n"; + return true; + } + } + + auto depList = readDeps(depsFile.string()); + for(const auto& dep : depList) + { + if(!std::filesystem::exists(dep) || + std::filesystem::last_write_time(targetFile) < + std::filesystem::last_write_time(dep)) + { + //std::cout << "The targetFile older than " << std::string(dep) << "\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 TaskCC::runInner() +{ + if(!std::filesystem::exists(sourceFile)) + { + std::cout << "Missing source file: " << sourceFile.string() << "\n"; + return 1; + } + + auto args = getCompilerArgs(); + + { // Write flags to file. + std::ofstream flagsStream(flagsFile.string()); + flagsStream << flagsString(); + } + + if(settings.verbose == 0) + { + std::cout << compiler() << " " << + sourceFile.lexically_normal().string() << " => " << + targetFile.lexically_normal().string() << "\n"; + } + + return execute(compiler(), args, settings.verbose > 0); +} + +int TaskCC::clean() +{ + if(std::filesystem::exists(targetFile)) + { + std::cout << "Removing " << targetFile.string() << "\n"; + std::filesystem::remove(targetFile); + } + + if(std::filesystem::exists(depsFile)) + { + std::cout << "Removing " << depsFile.string() << "\n"; + std::filesystem::remove(depsFile); + } + + if(std::filesystem::exists(flagsFile)) + { + std::cout << "Removing " << flagsFile.string() << "\n"; + std::filesystem::remove(flagsFile); + } + + return 0; +} + +std::vector TaskCC::depends() const +{ + return {}; +} + +std::string TaskCC::target() const +{ + return targetFile.string(); +} + +std::string TaskCC::toJSON() const +{ + std::string json; + 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\"arguments\": [ \"" + compiler() + "\""; + auto args = getCompilerArgs(); + for(const auto& arg : args) + { + json += ", \"" + arg + "\""; + } + json += " ]\n"; + json += "\t}"; + return json; +} + +std::vector TaskCC::flags() const +{ + switch(sourceLanguage()) + { + case Language::C: + return config.cflags; + case Language::Cpp: + return config.cxxflags; + default: + std::cerr << "Unknown CC target type\n"; + exit(1); + break; + } +} + +std::string TaskCC::flagsString() const +{ + std::string flagsStr = compiler(); + for(const auto& flag : flags()) + { + flagsStr += " " + flag; + } + return flagsStr; +} + +std::vector TaskCC::getCompilerArgs() const +{ + auto compiler_flags = flags(); + + std::vector args; + args.push_back("-MMD"); + + if(std::filesystem::path(config.target).extension() == ".so") + { + // Add -fPIC arg to all contained object files + args.push_back("-fPIC"); + } + + args.push_back("-c"); + args.push_back(sourceFile.string()); + args.push_back("-o"); + args.push_back(targetFile.string()); + + for(const auto& flag : compiler_flags) + { + // Is arg an added include path? + if(flag.substr(0, 2) == "-I") + { + std::string include_path = flag.substr(2); + include_path.erase(0, include_path.find_first_not_of(' ')); + std::filesystem::path path(include_path); + + // Is it relative? + if(path.is_relative()) + { + path = (sourceDir / path).lexically_normal(); + std::string new_include_path = "-I" + path.string(); + args.push_back(new_include_path); + continue; + } + } + + args.push_back(flag); + } + + return args; +} diff --git a/src/task_cc.h b/src/task_cc.h new file mode 100644 index 0000000..0ce4947 --- /dev/null +++ b/src/task_cc.h @@ -0,0 +1,47 @@ +// -*- c++ -*- +#pragma once + +#include "task.h" + +#include +#include +#include +#include + +struct BuildConfiguration; +struct Settings; + +class TaskCC + : public Task +{ +public: + TaskCC(const BuildConfiguration& config, + const Settings& settings, + const std::string& sourceDir, const std::string& source); + + std::string name() const override; + bool dirtyInner() override; + + int runInner() override; + int clean() override; + + std::vector depends() const override; + + std::string target() const override; + + std::string toJSON() const override; + +private: + std::vector flags() const; + std::string flagsString() const; + std::vector getCompilerArgs() const; + + std::filesystem::path sourceFile; + std::filesystem::path targetFile; + std::filesystem::path depsFile; + std::filesystem::path flagsFile; + + const BuildConfiguration& config; + const Settings& settings; + std::filesystem::path sourceDir; +}; diff --git a/src/task_ld.cc b/src/task_ld.cc new file mode 100644 index 0000000..91f3316 --- /dev/null +++ b/src/task_ld.cc @@ -0,0 +1,227 @@ +#include "task_ld.h" + +#include +#include + +#include "libcppbuild.h" +#include "settings.h" +#include "execute.h" + +namespace +{ +std::string readFile(const std::string &fileName) +{ + std::ifstream ifs(fileName.c_str(), std::ios::in | std::ios::binary | std::ios::ate); + + std::ifstream::pos_type fileSize = ifs.tellg(); + ifs.seekg(0, std::ios::beg); + + std::vector bytes(fileSize); + ifs.read(bytes.data(), fileSize); + + return std::string(bytes.data(), fileSize); +} + +std::vector addPrefix(const std::vector& lst, + const Settings& settings) +{ + std::vector out; + for(const auto& item : lst) + { + std::filesystem::path file = settings.builddir; + file /= item; + out.push_back(file.string()); + } + return out; +} +} // namespace :: + +TaskLD::TaskLD(const BuildConfiguration& config, + const Settings& settings, + const std::string& target, + const std::vector& objects) + : Task(config, addPrefix(config.depends, settings)) + , config(config) + , settings(settings) +{ + target_type = TargetType::Executable; + + targetFile = settings.builddir; + targetFile /= target; + for(const auto& object : objects) + { + std::filesystem::path objectFile = object; + objectFiles.push_back(objectFile); + dependsStr.push_back(objectFile.string()); + } + + for(const auto& dep : config.depends) + { + std::filesystem::path depFile = settings.builddir; + depFile /= dep; + depFiles.push_back(depFile); + } + + flagsFile = settings.builddir / targetFile.stem(); + flagsFile += ".flags"; + + target_type = TargetType::Executable; + source_language = Language::C; + for(const auto& source : config.sources) + { + std::filesystem::path sourceFile(source); + if(sourceFile.extension().string() != ".c") + { + source_language = Language::Cpp; + } + } +} + +bool TaskLD::dirtyInner() +{ + if(!std::filesystem::exists(targetFile)) + { + return true; + } + + if(!std::filesystem::exists(flagsFile)) + { + 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) + { + //std::cout << "The compiler flags changed\n"; + return true; + } + } + + return false; +} + +int TaskLD::runInner() +{ + std::string objectlist; + for(const auto& objectFile : objectFiles) + { + if(!objectlist.empty()) + { + objectlist += " "; + } + objectlist += objectFile.string(); + } + + std::vector args; + for(const auto& objectFile : objectFiles) + { + args.push_back(objectFile.string()); + } + + for(const auto& depFile : depFiles) + { + if(depFile.extension() == ".so") + { + args.push_back(std::string("-L") + settings.builddir); + auto lib = depFile.stem().string().substr(3); // strip 'lib' prefix + args.push_back(std::string("-l") + lib); + } + else if(depFile.extension() == ".a") + { + args.push_back(depFile.string()); + } + } + + for(const auto& flag : config.ldflags) + { + args.push_back(flag); + } + args.push_back("-o"); + args.push_back(targetFile.string()); + + { // Write flags to file. + std::ofstream flagsStream(flagsFile); + flagsStream << flagsString(); + } + + if(settings.verbose == 0) + { + std::cout << "LD => " << targetFile.string() << "\n"; + } + + auto tool = compiler(); + return execute(tool, args, settings.verbose > 0); +} + +int TaskLD::clean() +{ + if(std::filesystem::exists(targetFile)) + { + std::cout << "Removing " << targetFile.string() << "\n"; + std::filesystem::remove(targetFile); + } + + if(std::filesystem::exists(flagsFile)) + { + std::cout << "Removing " << flagsFile.string() << "\n"; + std::filesystem::remove(flagsFile); + } + + return 0; +} + +std::vector TaskLD::depends() const +{ + std::vector deps; + for(const auto& objectFile : objectFiles) + { + deps.push_back(objectFile.string()); + } + + for(const auto& depFile : depFiles) + { + deps.push_back(depFile.string()); + } + + return deps; +} + +std::string TaskLD::target() const +{ + return targetFile.string(); +} + +std::string TaskLD::flagsString() const +{ + std::string flagsStr; + for(const auto& flag : config.ldflags) + { + if(flag != config.ldflags[0]) + { + flagsStr += " "; + } + flagsStr += flag; + } + flagsStr += "\n"; + + for(const auto& dep : config.depends) + { + if(dep != config.depends[0]) + { + flagsStr += " "; + } + flagsStr += dep; + } + + return flagsStr; +} diff --git a/src/task_ld.h b/src/task_ld.h new file mode 100644 index 0000000..f56f00d --- /dev/null +++ b/src/task_ld.h @@ -0,0 +1,42 @@ +// -*- c++ -*- +#pragma once + +#include "task.h" + +#include +#include +#include +#include + +struct BuildConfiguration; +struct Settings; + +class TaskLD + : public Task +{ +public: + TaskLD(const BuildConfiguration& config, + const Settings& settings, + const std::string& target, + const std::vector& objects); + + bool dirtyInner() override; + + int runInner() override; + int clean() override; + + std::vector depends() const override; + + std::string target() const override; + +private: + std::string flagsString() const; + + std::vector objectFiles; + std::vector depFiles; + std::filesystem::path targetFile; + std::filesystem::path flagsFile; + + const BuildConfiguration& config; + const Settings& settings; +}; diff --git a/src/task_so.cc b/src/task_so.cc new file mode 100644 index 0000000..eaf6a85 --- /dev/null +++ b/src/task_so.cc @@ -0,0 +1,217 @@ +#include "task_so.h" + +#include +#include + +#include "libcppbuild.h" +#include "settings.h" +#include "execute.h" + +namespace +{ +std::string readFile(const std::string &fileName) +{ + std::ifstream ifs(fileName.c_str(), std::ios::in | std::ios::binary | std::ios::ate); + + std::ifstream::pos_type fileSize = ifs.tellg(); + ifs.seekg(0, std::ios::beg); + + std::vector bytes(fileSize); + ifs.read(bytes.data(), fileSize); + + return std::string(bytes.data(), fileSize); +} + +std::vector addPrefix(const std::vector& lst, + const Settings& settings) +{ + std::vector out; + for(const auto& item : lst) + { + std::filesystem::path file = settings.builddir; + file /= item; + out.push_back(file.string()); + } + return out; +} +} // namespace :: + +TaskSO::TaskSO(const BuildConfiguration& config, + const Settings& settings, + const std::string& target, + const std::vector& objects) + : Task(config, addPrefix(config.depends, settings)) + , config(config) + , settings(settings) +{ + targetFile = settings.builddir; + targetFile /= target; + for(const auto& object : objects) + { + std::filesystem::path objectFile = object; + objectFiles.push_back(objectFile); + dependsStr.push_back(objectFile.string()); + } + + for(const auto& dep : config.depends) + { + std::filesystem::path depFile = settings.builddir; + depFile /= dep; + depFiles.push_back(depFile); + } + + flagsFile = settings.builddir / targetFile.stem(); + flagsFile += ".flags"; + + target_type = TargetType::DynamicLibrary; + source_language = Language::C; + for(const auto& source : config.sources) + { + std::filesystem::path sourceFile(source); + if(sourceFile.extension().string() != ".c") + { + source_language = Language::Cpp; + } + } +} + +bool TaskSO::dirtyInner() +{ + if(!std::filesystem::exists(targetFile)) + { + return true; + } + + if(!std::filesystem::exists(flagsFile)) + { + 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) + { + //std::cout << "The compiler flags changed\n"; + return true; + } + } + + return false; +} + +int TaskSO::runInner() +{ + std::string objectlist; + for(const auto& objectFile : objectFiles) + { + if(!objectlist.empty()) + { + objectlist += " "; + } + objectlist += objectFile.string(); + } + + std::vector args; + + args.push_back("-fPIC"); + args.push_back("-shared"); + + args.push_back("-o"); + args.push_back(targetFile.string()); + + for(const auto& objectFile : objectFiles) + { + args.push_back(objectFile.string()); + } + + for(const auto& depFile : depFiles) + { + args.push_back(depFile.string()); + } + + for(const auto& flag : config.ldflags) + { + args.push_back(flag); + } + + { // Write flags to file. + std::ofstream flagsStream(flagsFile); + flagsStream << flagsString(); + } + + if(settings.verbose == 0) + { + std::cout << "LD => " << targetFile.string() << "\n"; + } + + auto tool = compiler(); + return execute(tool, args, settings.verbose > 0); +} + +int TaskSO::clean() +{ + if(std::filesystem::exists(targetFile)) + { + std::cout << "Removing " << targetFile.string() << "\n"; + std::filesystem::remove(targetFile); + } + + if(std::filesystem::exists(flagsFile)) + { + std::cout << "Removing " << flagsFile.string() << "\n"; + std::filesystem::remove(flagsFile); + } + + return 0; +} + +std::vector TaskSO::depends() const +{ + std::vector deps; + for(const auto& objectFile : objectFiles) + { + deps.push_back(objectFile.string()); + } + + for(const auto& depFile : depFiles) + { + deps.push_back(depFile.string()); + } + + return deps; +} + +std::string TaskSO::target() const +{ + return targetFile.string(); +} + +std::string TaskSO::flagsString() const +{ + std::string flagsStr = compiler(); + for(const auto& flag : config.ldflags) + { + flagsStr += " " + flag; + } + flagsStr += "\n"; + + for(const auto& dep : config.depends) + { + if(dep != config.depends[0]) + { + flagsStr += " "; + } + flagsStr += dep; + } + + return flagsStr; +} diff --git a/src/task_so.h b/src/task_so.h new file mode 100644 index 0000000..864d108 --- /dev/null +++ b/src/task_so.h @@ -0,0 +1,42 @@ +// -*- c++ -*- +#pragma once + +#include "task.h" + +#include +#include +#include +#include + +struct BuildConfiguration; +struct Settings; + +class TaskSO + : public Task +{ +public: + TaskSO(const BuildConfiguration& config, + const Settings& settings, + const std::string& target, + const std::vector& objects); + + bool dirtyInner() override; + + int runInner() override; + int clean() override; + + std::vector depends() const override; + + std::string target() const override; + +private: + std::string flagsString() const; + + std::vector objectFiles; + std::vector depFiles; + std::filesystem::path targetFile; + std::filesystem::path flagsFile; + + const BuildConfiguration& config; + const Settings& settings; +}; diff --git a/src/tasks.cc b/src/tasks.cc new file mode 100644 index 0000000..93e5a8b --- /dev/null +++ b/src/tasks.cc @@ -0,0 +1,130 @@ +#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::Auto: + // The target_type cannot be Auto + break; + + 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; + + case TargetType::Object: + 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().string(); + 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/src/tasks.h b/src/tasks.h new file mode 100644 index 0000000..119c7d6 --- /dev/null +++ b/src/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/task.cc b/task.cc deleted file mode 100644 index 962a02b..0000000 --- a/task.cc +++ /dev/null @@ -1,146 +0,0 @@ -#include "task.h" - -#include -#include - -Task::Task(const BuildConfiguration& config, - const std::vector& depends) - : dependsStr(depends) - , config(config) - , output_system(config.system) -{ -} - -int Task::registerDepTasks(const std::list>& tasks) -{ - for(auto const& depStr : dependsStr) - { - bool found{false}; - for(const auto& task : tasks) - { - if(task->target() == depStr) - { - dependsTasks.push_back(task); - found = true; - } - } - if(!found) - { - std::cerr << "Could not find dependency " << depStr << " needed by " << - target() << " target\n"; - return 1; - } - } - - return 0; -} - -std::string Task::name() const -{ - return config.target; -} - -bool Task::dirty() -{ - for(const auto& task : dependsTasks) - { - if(task->dirty()) - { - return true; - } - } - - return dirtyInner(); -} - -bool Task::ready() -{ - for(const auto& task : dependsTasks) - { - if(task->dirty() || task->state() == State::Running) - { - return false; - } - } - - task_state.store(State::Ready); - return true; -} - -int Task::run() -{ - if(task_state.load() == State::Done) - { - return 0; - } - - task_state.store(State::Running); - auto ret = runInner(); - if(ret == 0) - { - task_state.store(State::Done); - } - else - { - task_state.store(State::Error); - } - - return ret; -} - -State Task::state() const -{ - return task_state.load(); -} - -const BuildConfiguration& Task::buildConfig() const -{ - return config; -} - -TargetType Task::targetType() const -{ - return target_type; -} - -Language Task::sourceLanguage() const -{ - return source_language; -} - -OutputSystem Task::outputSystem() const -{ - return output_system; -} - -std::string Task::compiler() const -{ - switch(sourceLanguage()) - { - case 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 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++"); - } - default: - std::cerr << "Unknown CC target type\n"; - exit(1); - break; - } -} - -std::list> Task::getDependsTasks() -{ - return dependsTasks; -} diff --git a/task.h b/task.h deleted file mode 100644 index 98363a1..0000000 --- a/task.h +++ /dev/null @@ -1,60 +0,0 @@ -// -*- c++ -*- -#pragma once - -#include -#include -#include -#include -#include - -#include "libcppbuild.h" - -enum class State -{ - Unknown, - Ready, - Running, - Done, - Error, -}; - -class Task -{ -public: - Task(const BuildConfiguration& config, - const std::vector& depends = {}); - - int registerDepTasks(const std::list>& tasks); - - virtual std::string name() const; - bool dirty(); - bool ready(); - int run(); - State state() const; - virtual int clean() = 0 ; - virtual std::vector depends() const = 0; - virtual std::string target() const = 0; - - virtual std::string toJSON() const { return {}; }; - - const BuildConfiguration& buildConfig() const; - - TargetType targetType() const; - Language sourceLanguage() const; - OutputSystem outputSystem() const; - std::string compiler() const; - - std::list> getDependsTasks(); - -protected: - std::atomic task_state{State::Unknown}; - virtual int runInner() { return 0; }; - virtual bool dirtyInner() { return false; } - - std::vector dependsStr; - std::list> dependsTasks; - const BuildConfiguration& config; - TargetType target_type{TargetType::Auto}; - Language source_language{Language::Auto}; - OutputSystem output_system{OutputSystem::Host}; -}; diff --git a/task_ar.cc b/task_ar.cc deleted file mode 100644 index 5568629..0000000 --- a/task_ar.cc +++ /dev/null @@ -1,221 +0,0 @@ -#include "task_ar.h" - -#include -#include - -#include "libcppbuild.h" -#include "settings.h" -#include "execute.h" - -namespace -{ -std::string readFile(const std::string &fileName) -{ - std::ifstream ifs(fileName.c_str(), - std::ios::in | std::ios::binary | std::ios::ate); - - std::ifstream::pos_type fileSize = ifs.tellg(); - ifs.seekg(0, std::ios::beg); - - std::vector bytes(fileSize); - ifs.read(bytes.data(), fileSize); - - return std::string(bytes.data(), fileSize); -} - -std::vector addPrefix(const std::vector& lst, - const Settings& settings) -{ - std::vector out; - for(const auto& item : lst) - { - std::filesystem::path file = settings.builddir; - file /= item; - out.push_back(file.string()); - } - return out; -} -} // namespace :: - -TaskAR::TaskAR(const BuildConfiguration& config, - const Settings& settings, - const std::string& target, - const std::vector& objects) - : Task(config, addPrefix(config.depends, settings)) - , config(config) - , settings(settings) -{ - targetFile = settings.builddir; - targetFile /= target; - for(const auto& object : objects) - { - std::filesystem::path objectFile = object; - objectFiles.push_back(objectFile); - dependsStr.push_back(objectFile.string()); - } - - for(const auto& dep : config.depends) - { - std::filesystem::path depFile = settings.builddir; - depFile /= dep; - depFiles.push_back(depFile); - } - - flagsFile = settings.builddir / targetFile.stem(); - flagsFile += ".flags"; - - target_type = TargetType::StaticLibrary; - source_language = Language::C; - for(const auto& source : config.sources) - { - std::filesystem::path sourceFile(source); - if(sourceFile.extension().string() != ".c") - { - source_language = Language::Cpp; - } - } -} - -bool TaskAR::dirtyInner() -{ - if(!std::filesystem::exists(targetFile)) - { - return true; - } - - if(!std::filesystem::exists(flagsFile)) - { - 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) - { - //std::cout << "The compiler flags changed\n"; - return true; - } - } - - return false; -} - -int TaskAR::runInner() -{ - std::string objectlist; - for(const auto& objectFile : objectFiles) - { - if(!objectlist.empty()) - { - objectlist += " "; - } - objectlist += objectFile.string(); - } - - std::vector args; - args.push_back("rcs"); - args.push_back(targetFile.string()); - for(const auto& objectFile : objectFiles) - { - args.push_back(objectFile.string()); - } - for(const auto& flag : config.ldflags) - { - args.push_back(flag); - } - - { // Write flags to file. - std::ofstream flagsStream(flagsFile); - flagsStream << flagsString(); - } - - if(settings.verbose == 0) - { - std::cout << "AR => " << targetFile.string() << "\n"; - } - - std::string tool; - switch(outputSystem()) - { - case OutputSystem::Host: - tool = getConfiguration(cfg::host_ar, "/usr/bin/ar"); - break; - case OutputSystem::Build: - tool = getConfiguration(cfg::build_ar, "/usr/bin/ar"); - break; - } - - return execute(tool, args, settings.verbose > 0); -} - -int TaskAR::clean() -{ - if(std::filesystem::exists(targetFile)) - { - std::cout << "Removing " << targetFile.string() << "\n"; - std::filesystem::remove(targetFile); - } - - if(std::filesystem::exists(flagsFile)) - { - std::cout << "Removing " << flagsFile.string() << "\n"; - std::filesystem::remove(flagsFile); - } - - return 0; -} - -std::vector TaskAR::depends() const -{ - std::vector deps; - for(const auto& objectFile : objectFiles) - { - deps.push_back(objectFile.string()); - } - - for(const auto& depFile : depFiles) - { - deps.push_back(depFile.string()); - } - - return deps; -} - -std::string TaskAR::target() const -{ - return targetFile.string(); -} - -std::string TaskAR::flagsString() const -{ - std::string flagsStr; - for(const auto& flag : config.ldflags) - { - if(flag != config.ldflags[0]) - { - flagsStr += " "; - } - flagsStr += flag; - } - flagsStr += "\n"; - - for(const auto& dep : config.depends) - { - if(dep != config.depends[0]) - { - flagsStr += " "; - } - flagsStr += dep; - } - - return flagsStr; -} diff --git a/task_ar.h b/task_ar.h deleted file mode 100644 index bfa21a2..0000000 --- a/task_ar.h +++ /dev/null @@ -1,42 +0,0 @@ -// -*- c++ -*- -#pragma once - -#include "task.h" - -#include -#include -#include -#include - -struct BuildConfiguration; -struct Settings; - -class TaskAR - : public Task -{ -public: - TaskAR(const BuildConfiguration& config, - const Settings& settings, - const std::string& target, - const std::vector& objects); - - bool dirtyInner() override; - - int runInner() override; - int clean() override; - - std::vector depends() const override; - - std::string target() const override; - -private: - std::string flagsString() const; - - std::vector objectFiles; - std::vector depFiles; - std::filesystem::path targetFile; - std::filesystem::path flagsFile; - - const BuildConfiguration& config; - const Settings& settings; -}; diff --git a/task_cc.cc b/task_cc.cc deleted file mode 100644 index af9cf7a..0000000 --- a/task_cc.cc +++ /dev/null @@ -1,339 +0,0 @@ -#include "task_cc.h" - -#include -#include - -#include "libcppbuild.h" -#include "settings.h" -#include "execute.h" - -namespace -{ -std::string readFile(const std::string &fileName) -{ - std::ifstream ifs(fileName.c_str(), std::ios::in | std::ios::binary | std::ios::ate); - - std::ifstream::pos_type fileSize = ifs.tellg(); - ifs.seekg(0, std::ios::beg); - - std::vector bytes(fileSize); - ifs.read(bytes.data(), fileSize); - - return std::string(bytes.data(), fileSize); -} - -std::vector readDeps(const std::string& depFile) -{ - if(!std::filesystem::exists(depFile)) - { - return {}; - } - - auto str = readFile(depFile); - - std::vector output; - std::string tmp; - bool start{false}; - bool in_whitespace{false}; - for(const auto& c : str) - { - if(c == '\\' || c == '\n') - { - continue; - } - - if(c == ':') - { - start = true; - continue; - } - - if(!start) - { - continue; - } - - if(c == ' ' || c == '\t') - { - if(in_whitespace) - { - continue; - } - - if(!tmp.empty()) - { - output.push_back(tmp); - } - tmp.clear(); - in_whitespace = true; - } - else - { - in_whitespace = false; - tmp += c; - } - } - - if(!tmp.empty()) - { - output.push_back(tmp); - } - - return output; -} -} // namespace :: - -TaskCC::TaskCC(const BuildConfiguration& config, const Settings& settings, - const std::string& sourceDir, const std::string& source) - : Task(config) - , config(config) - , settings(settings) - , sourceDir(sourceDir) -{ - sourceFile = sourceDir; - sourceFile /= source; - - std::filesystem::path base = settings.builddir; - base /= config.target; - base += "-"; - base += sourceFile.stem(); - - if(sourceFile.extension().string() == ".c") - { - base += "_c"; - } - else - { - base += "_cc"; - } - - targetFile = base; - targetFile += ".o"; - depsFile = base; - depsFile += ".d"; - flagsFile = base; - flagsFile += ".flags"; - - target_type = TargetType::Object; - if(sourceFile.extension().string() == ".c") - { - source_language = Language::C; - } - else - { - source_language = Language::Cpp; - } -} - -std::string TaskCC::name() const -{ - return target(); -} - -bool TaskCC::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::exists(depsFile)) - { - //std::cout << "Missing depsFile\n"; - return true; - } - - if(!std::filesystem::exists(flagsFile)) - { - //std::cout << "Missing flagsFile\n"; - return true; - } - - if(std::filesystem::last_write_time(sourceFile) > - std::filesystem::last_write_time(depsFile)) - { - //std::cout << "The sourceFile newer than depsFile\n"; - return true; - } - - { - auto lastFlags = readFile(flagsFile.string()); - if(flagsString() != lastFlags) - { - //std::cout << "The compiler flags changed\n"; - return true; - } - } - - auto depList = readDeps(depsFile.string()); - for(const auto& dep : depList) - { - if(!std::filesystem::exists(dep) || - std::filesystem::last_write_time(targetFile) < - std::filesystem::last_write_time(dep)) - { - //std::cout << "The targetFile older than " << std::string(dep) << "\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 TaskCC::runInner() -{ - if(!std::filesystem::exists(sourceFile)) - { - std::cout << "Missing source file: " << sourceFile.string() << "\n"; - return 1; - } - - auto args = getCompilerArgs(); - - { // Write flags to file. - std::ofstream flagsStream(flagsFile.string()); - flagsStream << flagsString(); - } - - if(settings.verbose == 0) - { - std::cout << compiler() << " " << - sourceFile.lexically_normal().string() << " => " << - targetFile.lexically_normal().string() << "\n"; - } - - return execute(compiler(), args, settings.verbose > 0); -} - -int TaskCC::clean() -{ - if(std::filesystem::exists(targetFile)) - { - std::cout << "Removing " << targetFile.string() << "\n"; - std::filesystem::remove(targetFile); - } - - if(std::filesystem::exists(depsFile)) - { - std::cout << "Removing " << depsFile.string() << "\n"; - std::filesystem::remove(depsFile); - } - - if(std::filesystem::exists(flagsFile)) - { - std::cout << "Removing " << flagsFile.string() << "\n"; - std::filesystem::remove(flagsFile); - } - - return 0; -} - -std::vector TaskCC::depends() const -{ - return {}; -} - -std::string TaskCC::target() const -{ - return targetFile.string(); -} - -std::string TaskCC::toJSON() const -{ - std::string json; - 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\"arguments\": [ \"" + compiler() + "\""; - auto args = getCompilerArgs(); - for(const auto& arg : args) - { - json += ", \"" + arg + "\""; - } - json += " ]\n"; - json += "\t}"; - return json; -} - -std::vector TaskCC::flags() const -{ - switch(sourceLanguage()) - { - case Language::C: - return config.cflags; - case Language::Cpp: - return config.cxxflags; - default: - std::cerr << "Unknown CC target type\n"; - exit(1); - break; - } -} - -std::string TaskCC::flagsString() const -{ - std::string flagsStr = compiler(); - for(const auto& flag : flags()) - { - flagsStr += " " + flag; - } - return flagsStr; -} - -std::vector TaskCC::getCompilerArgs() const -{ - auto compiler_flags = flags(); - - std::vector args; - args.push_back("-MMD"); - - if(std::filesystem::path(config.target).extension() == ".so") - { - // Add -fPIC arg to all contained object files - args.push_back("-fPIC"); - } - - args.push_back("-c"); - args.push_back(sourceFile.string()); - args.push_back("-o"); - args.push_back(targetFile.string()); - - for(const auto& flag : compiler_flags) - { - // Is arg an added include path? - if(flag.substr(0, 2) == "-I") - { - std::string include_path = flag.substr(2); - include_path.erase(0, include_path.find_first_not_of(' ')); - std::filesystem::path path(include_path); - - // Is it relative? - if(path.is_relative()) - { - path = (sourceDir / path).lexically_normal(); - std::string new_include_path = "-I" + path.string(); - args.push_back(new_include_path); - continue; - } - } - - args.push_back(flag); - } - - return args; -} diff --git a/task_cc.h b/task_cc.h deleted file mode 100644 index 0ce4947..0000000 --- a/task_cc.h +++ /dev/null @@ -1,47 +0,0 @@ -// -*- c++ -*- -#pragma once - -#include "task.h" - -#include -#include -#include -#include - -struct BuildConfiguration; -struct Settings; - -class TaskCC - : public Task -{ -public: - TaskCC(const BuildConfiguration& config, - const Settings& settings, - const std::string& sourceDir, const std::string& source); - - std::string name() const override; - bool dirtyInner() override; - - int runInner() override; - int clean() override; - - std::vector depends() const override; - - std::string target() const override; - - std::string toJSON() const override; - -private: - std::vector flags() const; - std::string flagsString() const; - std::vector getCompilerArgs() const; - - std::filesystem::path sourceFile; - std::filesystem::path targetFile; - std::filesystem::path depsFile; - std::filesystem::path flagsFile; - - const BuildConfiguration& config; - const Settings& settings; - std::filesystem::path sourceDir; -}; diff --git a/task_ld.cc b/task_ld.cc deleted file mode 100644 index 91f3316..0000000 --- a/task_ld.cc +++ /dev/null @@ -1,227 +0,0 @@ -#include "task_ld.h" - -#include -#include - -#include "libcppbuild.h" -#include "settings.h" -#include "execute.h" - -namespace -{ -std::string readFile(const std::string &fileName) -{ - std::ifstream ifs(fileName.c_str(), std::ios::in | std::ios::binary | std::ios::ate); - - std::ifstream::pos_type fileSize = ifs.tellg(); - ifs.seekg(0, std::ios::beg); - - std::vector bytes(fileSize); - ifs.read(bytes.data(), fileSize); - - return std::string(bytes.data(), fileSize); -} - -std::vector addPrefix(const std::vector& lst, - const Settings& settings) -{ - std::vector out; - for(const auto& item : lst) - { - std::filesystem::path file = settings.builddir; - file /= item; - out.push_back(file.string()); - } - return out; -} -} // namespace :: - -TaskLD::TaskLD(const BuildConfiguration& config, - const Settings& settings, - const std::string& target, - const std::vector& objects) - : Task(config, addPrefix(config.depends, settings)) - , config(config) - , settings(settings) -{ - target_type = TargetType::Executable; - - targetFile = settings.builddir; - targetFile /= target; - for(const auto& object : objects) - { - std::filesystem::path objectFile = object; - objectFiles.push_back(objectFile); - dependsStr.push_back(objectFile.string()); - } - - for(const auto& dep : config.depends) - { - std::filesystem::path depFile = settings.builddir; - depFile /= dep; - depFiles.push_back(depFile); - } - - flagsFile = settings.builddir / targetFile.stem(); - flagsFile += ".flags"; - - target_type = TargetType::Executable; - source_language = Language::C; - for(const auto& source : config.sources) - { - std::filesystem::path sourceFile(source); - if(sourceFile.extension().string() != ".c") - { - source_language = Language::Cpp; - } - } -} - -bool TaskLD::dirtyInner() -{ - if(!std::filesystem::exists(targetFile)) - { - return true; - } - - if(!std::filesystem::exists(flagsFile)) - { - 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) - { - //std::cout << "The compiler flags changed\n"; - return true; - } - } - - return false; -} - -int TaskLD::runInner() -{ - std::string objectlist; - for(const auto& objectFile : objectFiles) - { - if(!objectlist.empty()) - { - objectlist += " "; - } - objectlist += objectFile.string(); - } - - std::vector args; - for(const auto& objectFile : objectFiles) - { - args.push_back(objectFile.string()); - } - - for(const auto& depFile : depFiles) - { - if(depFile.extension() == ".so") - { - args.push_back(std::string("-L") + settings.builddir); - auto lib = depFile.stem().string().substr(3); // strip 'lib' prefix - args.push_back(std::string("-l") + lib); - } - else if(depFile.extension() == ".a") - { - args.push_back(depFile.string()); - } - } - - for(const auto& flag : config.ldflags) - { - args.push_back(flag); - } - args.push_back("-o"); - args.push_back(targetFile.string()); - - { // Write flags to file. - std::ofstream flagsStream(flagsFile); - flagsStream << flagsString(); - } - - if(settings.verbose == 0) - { - std::cout << "LD => " << targetFile.string() << "\n"; - } - - auto tool = compiler(); - return execute(tool, args, settings.verbose > 0); -} - -int TaskLD::clean() -{ - if(std::filesystem::exists(targetFile)) - { - std::cout << "Removing " << targetFile.string() << "\n"; - std::filesystem::remove(targetFile); - } - - if(std::filesystem::exists(flagsFile)) - { - std::cout << "Removing " << flagsFile.string() << "\n"; - std::filesystem::remove(flagsFile); - } - - return 0; -} - -std::vector TaskLD::depends() const -{ - std::vector deps; - for(const auto& objectFile : objectFiles) - { - deps.push_back(objectFile.string()); - } - - for(const auto& depFile : depFiles) - { - deps.push_back(depFile.string()); - } - - return deps; -} - -std::string TaskLD::target() const -{ - return targetFile.string(); -} - -std::string TaskLD::flagsString() const -{ - std::string flagsStr; - for(const auto& flag : config.ldflags) - { - if(flag != config.ldflags[0]) - { - flagsStr += " "; - } - flagsStr += flag; - } - flagsStr += "\n"; - - for(const auto& dep : config.depends) - { - if(dep != config.depends[0]) - { - flagsStr += " "; - } - flagsStr += dep; - } - - return flagsStr; -} diff --git a/task_ld.h b/task_ld.h deleted file mode 100644 index f56f00d..0000000 --- a/task_ld.h +++ /dev/null @@ -1,42 +0,0 @@ -// -*- c++ -*- -#pragma once - -#include "task.h" - -#include -#include -#include -#include - -struct BuildConfiguration; -struct Settings; - -class TaskLD - : public Task -{ -public: - TaskLD(const BuildConfiguration& config, - const Settings& settings, - const std::string& target, - const std::vector& objects); - - bool dirtyInner() override; - - int runInner() override; - int clean() override; - - std::vector depends() const override; - - std::string target() const override; - -private: - std::string flagsString() const; - - std::vector objectFiles; - std::vector depFiles; - std::filesystem::path targetFile; - std::filesystem::path flagsFile; - - const BuildConfiguration& config; - const Settings& settings; -}; diff --git a/task_so.cc b/task_so.cc deleted file mode 100644 index eaf6a85..0000000 --- a/task_so.cc +++ /dev/null @@ -1,217 +0,0 @@ -#include "task_so.h" - -#include -#include - -#include "libcppbuild.h" -#include "settings.h" -#include "execute.h" - -namespace -{ -std::string readFile(const std::string &fileName) -{ - std::ifstream ifs(fileName.c_str(), std::ios::in | std::ios::binary | std::ios::ate); - - std::ifstream::pos_type fileSize = ifs.tellg(); - ifs.seekg(0, std::ios::beg); - - std::vector bytes(fileSize); - ifs.read(bytes.data(), fileSize); - - return std::string(bytes.data(), fileSize); -} - -std::vector addPrefix(const std::vector& lst, - const Settings& settings) -{ - std::vector out; - for(const auto& item : lst) - { - std::filesystem::path file = settings.builddir; - file /= item; - out.push_back(file.string()); - } - return out; -} -} // namespace :: - -TaskSO::TaskSO(const BuildConfiguration& config, - const Settings& settings, - const std::string& target, - const std::vector& objects) - : Task(config, addPrefix(config.depends, settings)) - , config(config) - , settings(settings) -{ - targetFile = settings.builddir; - targetFile /= target; - for(const auto& object : objects) - { - std::filesystem::path objectFile = object; - objectFiles.push_back(objectFile); - dependsStr.push_back(objectFile.string()); - } - - for(const auto& dep : config.depends) - { - std::filesystem::path depFile = settings.builddir; - depFile /= dep; - depFiles.push_back(depFile); - } - - flagsFile = settings.builddir / targetFile.stem(); - flagsFile += ".flags"; - - target_type = TargetType::DynamicLibrary; - source_language = Language::C; - for(const auto& source : config.sources) - { - std::filesystem::path sourceFile(source); - if(sourceFile.extension().string() != ".c") - { - source_language = Language::Cpp; - } - } -} - -bool TaskSO::dirtyInner() -{ - if(!std::filesystem::exists(targetFile)) - { - return true; - } - - if(!std::filesystem::exists(flagsFile)) - { - 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) - { - //std::cout << "The compiler flags changed\n"; - return true; - } - } - - return false; -} - -int TaskSO::runInner() -{ - std::string objectlist; - for(const auto& objectFile : objectFiles) - { - if(!objectlist.empty()) - { - objectlist += " "; - } - objectlist += objectFile.string(); - } - - std::vector args; - - args.push_back("-fPIC"); - args.push_back("-shared"); - - args.push_back("-o"); - args.push_back(targetFile.string()); - - for(const auto& objectFile : objectFiles) - { - args.push_back(objectFile.string()); - } - - for(const auto& depFile : depFiles) - { - args.push_back(depFile.string()); - } - - for(const auto& flag : config.ldflags) - { - args.push_back(flag); - } - - { // Write flags to file. - std::ofstream flagsStream(flagsFile); - flagsStream << flagsString(); - } - - if(settings.verbose == 0) - { - std::cout << "LD => " << targetFile.string() << "\n"; - } - - auto tool = compiler(); - return execute(tool, args, settings.verbose > 0); -} - -int TaskSO::clean() -{ - if(std::filesystem::exists(targetFile)) - { - std::cout << "Removing " << targetFile.string() << "\n"; - std::filesystem::remove(targetFile); - } - - if(std::filesystem::exists(flagsFile)) - { - std::cout << "Removing " << flagsFile.string() << "\n"; - std::filesystem::remove(flagsFile); - } - - return 0; -} - -std::vector TaskSO::depends() const -{ - std::vector deps; - for(const auto& objectFile : objectFiles) - { - deps.push_back(objectFile.string()); - } - - for(const auto& depFile : depFiles) - { - deps.push_back(depFile.string()); - } - - return deps; -} - -std::string TaskSO::target() const -{ - return targetFile.string(); -} - -std::string TaskSO::flagsString() const -{ - std::string flagsStr = compiler(); - for(const auto& flag : config.ldflags) - { - flagsStr += " " + flag; - } - flagsStr += "\n"; - - for(const auto& dep : config.depends) - { - if(dep != config.depends[0]) - { - flagsStr += " "; - } - flagsStr += dep; - } - - return flagsStr; -} diff --git a/task_so.h b/task_so.h deleted file mode 100644 index 864d108..0000000 --- a/task_so.h +++ /dev/null @@ -1,42 +0,0 @@ -// -*- c++ -*- -#pragma once - -#include "task.h" - -#include -#include -#include -#include - -struct BuildConfiguration; -struct Settings; - -class TaskSO - : public Task -{ -public: - TaskSO(const BuildConfiguration& config, - const Settings& settings, - const std::string& target, - const std::vector& objects); - - bool dirtyInner() override; - - int runInner() override; - int clean() override; - - std::vector depends() const override; - - std::string target() const override; - -private: - std::string flagsString() const; - - std::vector objectFiles; - std::vector depFiles; - std::filesystem::path targetFile; - std::filesystem::path flagsFile; - - const BuildConfiguration& config; - const Settings& settings; -}; diff --git a/tasks.cc b/tasks.cc deleted file mode 100644 index 93e5a8b..0000000 --- a/tasks.cc +++ /dev/null @@ -1,130 +0,0 @@ -#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::Auto: - // The target_type cannot be Auto - break; - - 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; - - case TargetType::Object: - 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().string(); - 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 deleted file mode 100644 index 119c7d6..0000000 --- a/tasks.h +++ /dev/null @@ -1,18 +0,0 @@ -// -*- 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); -- cgit v1.2.3