| @@ -1,9 +1,9 @@ | |||
| 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/toolchain.cc src/main.cc | |||
| 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/toolchain.h | |||
| BUILD = build | |||
| @@ -98,6 +98,10 @@ void CompileStep::doBuild(const std::string &outDir) { | |||
| 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) { | |||
| BBVariables &vars = variables(); | |||
| auto it = vars.find("ldflags"); | |||
| @@ -15,6 +15,7 @@ private: | |||
| bool checkHasChanged(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> getPublicLDLibs(const std::string &outDir) override; | |||
| std::vector<std::string> getPublicObjects(const std::string &outDir) override; | |||
| @@ -62,7 +62,7 @@ bool DepNode::haveDepsChanged(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_) { | |||
| return true; | |||
| @@ -74,8 +74,12 @@ bool DepNode::hasChanged(const std::string &outDir) { | |||
| return false; | |||
| } else { | |||
| bool changed = checkHasChanged(outDir); | |||
| if (!changed) { | |||
| // Check children without blocking this node | |||
| glock.unlock(); | |||
| changed = haveDepsChanged(outDir); | |||
| glock.lock(); | |||
| } | |||
| if (changed) { | |||
| @@ -90,13 +94,15 @@ bool DepNode::hasChanged(const std::string &outDir) { | |||
| void DepNode::build(const std::string &outDir) { | |||
| bool changed = hasChanged(outDir); | |||
| std::lock_guard<std::mutex> lock(mut_);\ | |||
| std::unique_lock<std::mutex> glock(mut_);\ | |||
| if (!was_rebuilt_ && changed) { | |||
| int workers = 0; | |||
| std::mutex mut; | |||
| std::condition_variable cond; | |||
| // Build children without blocking this node | |||
| glock.unlock(); | |||
| std::unique_lock<std::mutex> lock(mut, std::defer_lock); | |||
| for (auto &dep: deps_) { | |||
| if (dep->hasChanged(outDir)) { | |||
| @@ -116,11 +122,23 @@ void DepNode::build(const std::string &outDir) { | |||
| lock.lock(); | |||
| cond.wait(lock, [&] { return workers == 0; }); | |||
| glock.lock(); | |||
| doBuild(outDir); | |||
| 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::unordered_set<std::string> set; | |||
| std::vector<std::string> flags; | |||
| @@ -4,12 +4,16 @@ | |||
| #include <memory> | |||
| #include <mutex> | |||
| #include <string> | |||
| #include <ostream> | |||
| #include "compdb.h" | |||
| class DepNode { | |||
| public: | |||
| void addChild(std::shared_ptr<DepNode> node); | |||
| bool hasChanged(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> publicLDLibs(const std::string &outDir); | |||
| virtual std::vector<std::string> publicObjects(const std::string &outDir); | |||
| @@ -21,6 +25,7 @@ protected: | |||
| virtual bool checkHasChanged(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> getPublicLDLibs(const std::string &outDir) { return {}; } | |||
| virtual std::vector<std::string> getPublicObjects(const std::string &outDir) { return {}; } | |||
| @@ -0,0 +1,57 @@ | |||
| #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"; | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,23 @@ | |||
| #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; | |||
| }; | |||
| } | |||
| @@ -2,6 +2,7 @@ | |||
| #include <getopt.h> | |||
| #include <iostream> | |||
| #include <fstream> | |||
| #include "BBParser.h" | |||
| #include "parallel.h" | |||
| @@ -10,6 +11,7 @@ | |||
| #include "logger.h" | |||
| #include "sys.h" | |||
| #include "build.h" | |||
| #include "compdb.h" | |||
| int main(int argc, char **argv) { | |||
| std::string outDir = "bbbuild"; | |||
| @@ -105,7 +107,17 @@ int main(int argc, char **argv) { | |||
| std::unique_ptr<DepNode> root = buildDepTree(outDir, vars); | |||
| if (root == nullptr) { | |||
| 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); | |||
| } else { | |||
| logger::log("Nothing to do."); | |||