| SRCS = \ | SRCS = \ | ||||
| src/BBParser.cc src/build.cc src/CompileStep.cc src/DepNode.cc \ | |||||
| src/BBParser.cc src/build.cc src/compdb.cc src/CompileStep.cc src/DepNode.cc \ | |||||
| src/globals.cc src/LinkStep.cc src/logger.cc src/parallel.cc src/sys.cc \ | src/globals.cc src/LinkStep.cc src/logger.cc src/parallel.cc src/sys.cc \ | ||||
| src/toolchain.cc src/main.cc | src/toolchain.cc src/main.cc | ||||
| HDRS = \ | HDRS = \ | ||||
| src/BBParser.h src/build.h src/CompileStep.h src/DepNode.h \ | |||||
| src/BBParser.h src/build.h src/compdb.h src/CompileStep.h src/DepNode.h \ | |||||
| src/globals.h src/LinkStep.h src/logger.h src/parallel.h src/sys.h \ | src/globals.h src/LinkStep.h src/logger.h src/parallel.h src/sys.h \ | ||||
| src/toolchain.h | src/toolchain.h | ||||
| BUILD = build | BUILD = build |
| sys::execute(command, nullptr, global::verbose >= 1); | sys::execute(command, nullptr, global::verbose >= 1); | ||||
| } | } | ||||
| void CompileStep::doWriteCompDB(const std::string &outDir, compdb::Writer &w) { | |||||
| w.write(sys::cwd(), path_, compileCommand(outDir)); | |||||
| } | |||||
| std::vector<std::string> CompileStep::getPublicLDFlags(const std::string &outDir) { | std::vector<std::string> CompileStep::getPublicLDFlags(const std::string &outDir) { | ||||
| BBVariables &vars = variables(); | BBVariables &vars = variables(); | ||||
| auto it = vars.find("ldflags"); | auto it = vars.find("ldflags"); |
| bool checkHasChanged(const std::string &outDir) override; | bool checkHasChanged(const std::string &outDir) override; | ||||
| void doBuild(const std::string &outDir) override; | void doBuild(const std::string &outDir) override; | ||||
| void doWriteCompDB(const std::string &outDir, compdb::Writer &w) override; | |||||
| std::vector<std::string> getPublicLDFlags(const std::string &outDir) override; | std::vector<std::string> getPublicLDFlags(const std::string &outDir) override; | ||||
| std::vector<std::string> getPublicLDLibs(const std::string &outDir) override; | std::vector<std::string> getPublicLDLibs(const std::string &outDir) override; | ||||
| std::vector<std::string> getPublicObjects(const std::string &outDir) override; | std::vector<std::string> getPublicObjects(const std::string &outDir) override; |
| } | } | ||||
| bool DepNode::hasChanged(const std::string &outDir) { | bool DepNode::hasChanged(const std::string &outDir) { | ||||
| std::lock_guard<std::mutex> lock(mut_); | |||||
| std::unique_lock<std::mutex> glock(mut_); | |||||
| if (was_rebuilt_) { | if (was_rebuilt_) { | ||||
| return true; | return true; | ||||
| return false; | return false; | ||||
| } else { | } else { | ||||
| bool changed = checkHasChanged(outDir); | bool changed = checkHasChanged(outDir); | ||||
| if (!changed) { | if (!changed) { | ||||
| // Check children without blocking this node | |||||
| glock.unlock(); | |||||
| changed = haveDepsChanged(outDir); | changed = haveDepsChanged(outDir); | ||||
| glock.lock(); | |||||
| } | } | ||||
| if (changed) { | if (changed) { | ||||
| void DepNode::build(const std::string &outDir) { | void DepNode::build(const std::string &outDir) { | ||||
| bool changed = hasChanged(outDir); | bool changed = hasChanged(outDir); | ||||
| std::lock_guard<std::mutex> lock(mut_);\ | |||||
| std::unique_lock<std::mutex> glock(mut_);\ | |||||
| if (!was_rebuilt_ && changed) { | if (!was_rebuilt_ && changed) { | ||||
| int workers = 0; | int workers = 0; | ||||
| std::mutex mut; | std::mutex mut; | ||||
| std::condition_variable cond; | std::condition_variable cond; | ||||
| // Build children without blocking this node | |||||
| glock.unlock(); | |||||
| std::unique_lock<std::mutex> lock(mut, std::defer_lock); | std::unique_lock<std::mutex> lock(mut, std::defer_lock); | ||||
| for (auto &dep: deps_) { | for (auto &dep: deps_) { | ||||
| if (dep->hasChanged(outDir)) { | if (dep->hasChanged(outDir)) { | ||||
| lock.lock(); | lock.lock(); | ||||
| cond.wait(lock, [&] { return workers == 0; }); | cond.wait(lock, [&] { return workers == 0; }); | ||||
| glock.lock(); | |||||
| doBuild(outDir); | doBuild(outDir); | ||||
| was_rebuilt_ = true; | was_rebuilt_ = true; | ||||
| } | } | ||||
| } | } | ||||
| void DepNode::writeCompDB(const std::string &outDir, compdb::Writer &w) { | |||||
| std::unique_lock<std::mutex> glock(mut_);\ | |||||
| doWriteCompDB(outDir, w); | |||||
| // Write children without blocking this node | |||||
| glock.unlock(); | |||||
| for (auto &dep: deps_) { | |||||
| dep->writeCompDB(outDir, w); | |||||
| } | |||||
| } | |||||
| std::vector<std::string> DepNode::publicLDFlags(const std::string &outDir) { | std::vector<std::string> DepNode::publicLDFlags(const std::string &outDir) { | ||||
| std::unordered_set<std::string> set; | std::unordered_set<std::string> set; | ||||
| std::vector<std::string> flags; | std::vector<std::string> flags; |
| #include <memory> | #include <memory> | ||||
| #include <mutex> | #include <mutex> | ||||
| #include <string> | #include <string> | ||||
| #include <ostream> | |||||
| #include "compdb.h" | |||||
| class DepNode { | class DepNode { | ||||
| public: | public: | ||||
| void addChild(std::shared_ptr<DepNode> node); | void addChild(std::shared_ptr<DepNode> node); | ||||
| bool hasChanged(const std::string &outDir); | bool hasChanged(const std::string &outDir); | ||||
| void build(const std::string &outDir); | void build(const std::string &outDir); | ||||
| void writeCompDB(const std::string &outDir, compdb::Writer &w); | |||||
| virtual std::vector<std::string> publicLDFlags(const std::string &outDir); | virtual std::vector<std::string> publicLDFlags(const std::string &outDir); | ||||
| virtual std::vector<std::string> publicLDLibs(const std::string &outDir); | virtual std::vector<std::string> publicLDLibs(const std::string &outDir); | ||||
| virtual std::vector<std::string> publicObjects(const std::string &outDir); | virtual std::vector<std::string> publicObjects(const std::string &outDir); | ||||
| virtual bool checkHasChanged(const std::string &outDir) = 0; | virtual bool checkHasChanged(const std::string &outDir) = 0; | ||||
| virtual void doBuild(const std::string &outDir) = 0; | virtual void doBuild(const std::string &outDir) = 0; | ||||
| virtual void doWriteCompDB(const std::string &outDir, compdb::Writer &w) {} | |||||
| virtual std::vector<std::string> getPublicLDFlags(const std::string &outDir) { return {}; } | virtual std::vector<std::string> getPublicLDFlags(const std::string &outDir) { return {}; } | ||||
| virtual std::vector<std::string> getPublicLDLibs(const std::string &outDir) { return {}; } | virtual std::vector<std::string> getPublicLDLibs(const std::string &outDir) { return {}; } | ||||
| virtual std::vector<std::string> getPublicObjects(const std::string &outDir) { return {}; } | virtual std::vector<std::string> getPublicObjects(const std::string &outDir) { return {}; } |
| #include "compdb.h" | |||||
| namespace compdb { | |||||
| std::string escape(const std::string &str) { | |||||
| std::string out; | |||||
| // TODO: Handle unicode according to JSON spec | |||||
| for (char ch: str) { | |||||
| if (ch == '\\' || ch == '\"') { | |||||
| out += '\\'; | |||||
| out += ch; | |||||
| } else if (ch == '\r') { | |||||
| out += '\\'; | |||||
| out += 'r'; | |||||
| } else if (ch == '\n') { | |||||
| out += '\\'; | |||||
| out += 'n'; | |||||
| } else { | |||||
| out += ch; | |||||
| } | |||||
| } | |||||
| return out; | |||||
| } | |||||
| void Writer::write( | |||||
| const std::string &dir, const std::string &file, | |||||
| const std::vector<std::string> &cmd) { | |||||
| if (first_) { | |||||
| stream_ << "[\n\t{\n"; | |||||
| first_ = false; | |||||
| } else { | |||||
| stream_ << ", {\n"; | |||||
| } | |||||
| stream_ | |||||
| << "\t\t\"directory\": \"" << escape(dir) << "\"\n" | |||||
| << "\t\t\"file\": \"" << escape(file) << "\"\n" | |||||
| << "\t\t\"command\": \"" << escape(cmd[0]); | |||||
| for (size_t i = 1; i < cmd.size(); ++i) { | |||||
| stream_ << ' ' << escape('"' + escape(cmd[i]) + '"'); | |||||
| } | |||||
| stream_ << "\"\n\t}"; | |||||
| } | |||||
| Writer::~Writer() { | |||||
| if (first_) { | |||||
| stream_ << "[]\n"; | |||||
| } else { | |||||
| stream_ << "\n]\n"; | |||||
| } | |||||
| } | |||||
| } |
| #pragma once | |||||
| #include <string> | |||||
| #include <vector> | |||||
| #include <ostream> | |||||
| namespace compdb { | |||||
| class Writer { | |||||
| public: | |||||
| Writer(std::ostream &stream): stream_(stream) {} | |||||
| ~Writer(); | |||||
| void write( | |||||
| const std::string &dir, const std::string &file, | |||||
| const std::vector<std::string> &cmd); | |||||
| private: | |||||
| std::ostream &stream_; | |||||
| bool first_ = true; | |||||
| }; | |||||
| } |
| #include <getopt.h> | #include <getopt.h> | ||||
| #include <iostream> | #include <iostream> | ||||
| #include <fstream> | |||||
| #include "BBParser.h" | #include "BBParser.h" | ||||
| #include "parallel.h" | #include "parallel.h" | ||||
| #include "logger.h" | #include "logger.h" | ||||
| #include "sys.h" | #include "sys.h" | ||||
| #include "build.h" | #include "build.h" | ||||
| #include "compdb.h" | |||||
| int main(int argc, char **argv) { | int main(int argc, char **argv) { | ||||
| std::string outDir = "bbbuild"; | std::string outDir = "bbbuild"; | ||||
| std::unique_ptr<DepNode> root = buildDepTree(outDir, vars); | std::unique_ptr<DepNode> root = buildDepTree(outDir, vars); | ||||
| if (root == nullptr) { | if (root == nullptr) { | ||||
| logger::log("No source files."); | logger::log("No source files."); | ||||
| } else if (root->hasChanged(outDir)) { | |||||
| return 0; | |||||
| } | |||||
| { | |||||
| std::ofstream f(outDir + "/compile_commands.json"); | |||||
| compdb::Writer writer(f); | |||||
| root->writeCompDB(outDir, writer); | |||||
| sys::symlink(outDir + "/compile_commands.json", "compile_commands.json"); | |||||
| } | |||||
| if (root->hasChanged(outDir)) { | |||||
| root->build(outDir); | root->build(outDir); | ||||
| } else { | } else { | ||||
| logger::log("Nothing to do."); | logger::log("Nothing to do."); |