#include #include #include #include #include #include #include #include #include #include #include #include #include "libcppbuild.h" #include "task_cc.h" #include "task_ld.h" #include "task_ar.h" #include "task_so.h" #include "settings.h" #include "execute.h" #include using namespace std::chrono_literals; std::list> taskFactory(const BuildConfiguration& config, const Settings& settings) { std::filesystem::path targetFile(config.target); std::vector objects; std::list> tasks; for(const auto& file : config.sources) { tasks.emplace_back(std::make_shared(config, settings, file)); objects.push_back(tasks.back()->target()); } if(targetFile.extension() == ".a") { // static lib tasks.emplace_back(std::make_shared(config, settings, config.target, objects)); } else if(targetFile.extension() == ".so") { if(targetFile.stem().string().substr(0, 3) != "lib") { std::cerr << "Dynamic library target must have 'lib' prefix\n"; exit(1); } // dynamic lib tasks.emplace_back(std::make_shared(config, settings, config.target, objects)); } else { // executable tasks.emplace_back(std::make_shared(config, settings, config.target, objects)); } return tasks; } std::shared_ptr getNextTask(const std::list>& allTasks, std::list>& dirtyTasks) { for(auto dirtyTask = dirtyTasks.begin(); dirtyTask != dirtyTasks.end(); ++dirtyTask) { //std::cout << "Examining target " << (*dirtyTask)->target() << "\n"; if((*dirtyTask)->ready()) { dirtyTasks.erase(dirtyTask); return *dirtyTask; } } //std::cout << "No task ready ... \n"; return nullptr; } namespace { // Hack to get command-line args for re-launch int g_argc; char** g_argv; } // TODO: Use c++20 when ready, somehing like this: //void reg(const std::source_location location = // std::source_location::current()) void reg(const std::string& location) { std::filesystem::path configFile(location); std::filesystem::path binFile(configFile.stem()); if(std::filesystem::last_write_time(binFile) <= std::filesystem::last_write_time(configFile)) { std::cout << "Rebuilding config\n"; auto ret = execute("/usr/bin/g++", { "-s", "-O3", "-std=c++17", "-pthread", configFile.string(), "libcppbuild.a", "-o", binFile.string(), }); if(ret != 0) { std::cerr << "Failed.\n"; exit(1); } else { std::cout << "Re-launch\n"; std::vector args; for(int i = 1; i < g_argc; ++i) { args.push_back(g_argv[i]); } exit(execute(g_argv[0], args)); } } // g++ -s -O3 -std=c++17 -pthread $0 libcppbuild.a -o cppbuild } int main(int argc, char* argv[]) { g_argc = argc; g_argv = argv; Settings settings; // TODO: Set from commandline settings.builddir = "build/foo"; settings.parallel_processes = std::max(1u, std::thread::hardware_concurrency() * 2 - 1); settings.verbose = 0; dg::Options opt; opt.add("jobs", required_argument, 'j', "Number of parallel jobs.", [&]() { try { settings.parallel_processes = std::stoi(optarg); } catch(...) { std::cerr << "Not a number\n"; return 1; } return 0; }); opt.add("verbose", no_argument, 'v', "Be verbose.", [&]() { settings.verbose++; return 0; }); opt.add("help", no_argument, 'h', "Print this help text.", [&]() { std::cout << "usage stuff\n"; opt.help(); return 0; }); opt.process(argc, argv); std::filesystem::path builddir(settings.builddir); std::filesystem::create_directories(builddir); auto build_configs = configs(); std::list> tasks; for(const auto& build_config : build_configs) { std::vector objects; auto t = taskFactory(build_config, settings); tasks.insert(tasks.end(), t.begin(), t.end()); } for(auto task : tasks) { task->registerDepTasks(tasks); } std::list> dirtyTasks; for(auto task : tasks) { if(task->dirty()) { dirtyTasks.push_back(task); } } for(auto const &arg : opt.arguments()) { if(arg == "clean") { std::cout << "Cleaning\n"; //std::filesystem::remove_all(builddir); for(auto& task : tasks) { if(task->clean() != 0) { return 1; } } return 0; } } std::cout << "Building\n"; 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(tasks, dirtyTasks); if(task == nullptr) { break; //return 1; } 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) { 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; }