// -*- c++ -*- // Distributed under the BSD 2-Clause License. // See accompanying file LICENSE for details. #include "build.h" #include #include #include #include #include #include #include "ctor.h" using namespace std::chrono_literals; int build(const ctor::settings& settings, const std::string& name, const std::vector>& tasks, const std::vector>& all_tasks, bool dryrun) { if(settings.verbose > 1) { std::cout << "Building '" << name << "'\n"; } std::vector> dirtyTasks; for(auto task : tasks) { if(task->dirty() && std::find(dirtyTasks.begin(), dirtyTasks.end(), task) == dirtyTasks.end()) { dirtyTasks.push_back(task); } } // Dry-run returns number of dirty tasks but otherwise does nothing. if(dryrun) { return static_cast(dirtyTasks.size()); } if(dirtyTasks.empty()) { if(settings.verbose > -1) { 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; // Make sure we don't start tasks on top of each other to prevent // straining the disk. std::this_thread::sleep_for(50ms); } for(auto process = processes.begin(); process != processes.end(); ++process) { if(process->valid() == false) { continue; } auto ret = process->get(); if(ret != 0) { // NOTE Wait for other processes to finish before returning return ret; } processes.erase(process); break; } if(!started_one) // prevent polling too fast if no task is yet ready { std::this_thread::sleep_for(10ms); } } for(auto& process : processes) { if(process.valid() == false) { continue; } process.wait(); auto ret = process.get(); if (ret != 0) { return ret; } } return 0; } namespace { std::vector> getDepTasks(std::shared_ptr task) { std::vector> tasks; tasks.push_back(task); auto deps = task->getDependsTasks(); for(const auto& dep : deps) { auto depSet = getDepTasks(dep); for(const auto& dep_inner : depSet) { if(std::find(tasks.begin(), tasks.end(), dep_inner) == tasks.end()) { tasks.push_back(dep_inner); } } } return tasks; } } int build(const ctor::settings& settings, const std::string& name, const std::vector>& all_tasks, bool dryrun) { bool task_found{false}; for(auto task : all_tasks) { if(*task == name) { task_found = true; auto depSet = getDepTasks(task); std::vector> ts; for(const auto& task_inner : depSet) { if(std::find(ts.begin(), ts.end(), task_inner) == ts.end()) { ts.push_back(task_inner); } } auto ret = build(settings, name, ts, all_tasks, dryrun); if(ret != 0) { return ret; } break; } } if(!task_found) { std::cerr << "*** No rule to make target '" << name << "'. Stop.\n"; return 1; } return 0; } int build(const ctor::settings& settings, const std::string& name, const std::vector& targets, const std::vector>& all_tasks, bool dryrun) { bool task_found{false}; std::vector> ts; for(const auto& target : targets) { for(auto task : all_tasks) { if(!task->derived() && // only consider non-derived tasks task->buildConfig().target == target.config.target) { task_found = true; auto depSet = getDepTasks(task); for(const auto& task_inner : depSet) { if(std::find(ts.begin(), ts.end(), task_inner) == ts.end()) { ts.push_back(task_inner); } } } } } if(!task_found) { std::cerr << "*** No rule to make target '" << name << "'. Stop.\n"; return 1; } return build(settings, name, ts, all_tasks, dryrun); }