// -*- c++ -*- // Distributed under the BSD 2-Clause License. // See accompanying file LICENSE for details. #include "build.h" #include #include #include #include #include #include using namespace std::chrono_literals; int build(const Settings& settings, const std::string& name, const std::list>& tasks, const std::list>& all_tasks, bool dryrun) { if(settings.verbose > 1) { std::cout << "Building '" << name << "'\n"; } std::list> dirtyTasks; for(auto task : tasks) { if(task->dirty()) { dirtyTasks.push_back(task); } } // Dry-run returns number of dirty tasks but otherwise does nothing. if(dryrun) { return 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; std::this_thread::sleep_for(2ms); } 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) { std::this_thread::sleep_for(2ms); } else { std::this_thread::sleep_for(200ms); } } for(auto process = processes.begin(); process != processes.end(); ++process) { if(process->valid() == false) { continue; } process->wait(); auto ret = process->get(); if(ret != 0) { return ret; } } 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 dryrun) { bool task_found{false}; for(auto task : all_tasks) { if(task->target() == name || // match exact target output (ex. build/foo.o) (!task->derived() && // if non-derived task: ( task->buildConfig().target == name || // match name task->buildConfig().name == name ) // or target ) ) { 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, 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 Settings& settings, const std::string& name, const std::vector& targets, const std::list>& all_tasks, bool dryrun) { bool task_found{false}; std::list> 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 : depSet) { ts.push_back(task); } } } } if(!task_found) { std::cerr << "*** No rule to make target '" << name << "'. Stop.\n"; return 1; } return build(settings, name, ts, all_tasks, dryrun); }