| SRCS = \ | SRCS = \ | ||||
| src/BBBParser.cc src/SourceFile.cc src/toolchain.cc src/globals.cc \ | src/BBBParser.cc src/SourceFile.cc src/toolchain.cc src/globals.cc \ | ||||
| src/sys.cc src/parallel.cc src/main.cc | |||||
| src/logger.cc src/sys.cc src/parallel.cc src/main.cc | |||||
| HDRS = src/BBBParser.h src/SourceFile.h src/toolchain.h src/globals.h \ | HDRS = src/BBBParser.h src/SourceFile.h src/toolchain.h src/globals.h \ | ||||
| src/sys.h src/parallel.h | |||||
| src/logger.h src/sys.h src/parallel.h | |||||
| BUILD = build | BUILD = build | ||||
| OBJS = $(patsubst %,$(BUILD)/%.o,$(SRCS)) | OBJS = $(patsubst %,$(BUILD)/%.o,$(SRCS)) | ||||
| CFLAGS = -g -Wall -Wextra -Wno-unused-parameter | CFLAGS = -g -Wall -Wextra -Wno-unused-parameter |
| #include <string.h> | #include <string.h> | ||||
| #include "toolchain.h" | #include "toolchain.h" | ||||
| #include "logger.h" | |||||
| static bool startsWith(BBBParser &parser, const char *str) { | static bool startsWith(BBBParser &parser, const char *str) { | ||||
| for (size_t i = 0; str[i] != '\0'; ++i) { | for (size_t i = 0; str[i] != '\0'; ++i) { | ||||
| void SourceFile::compile(const std::string &outDir) const { | void SourceFile::compile(const std::string &outDir) const { | ||||
| // TODO: Send this as a message to some printer thread instead, | // TODO: Send this as a message to some printer thread instead, | ||||
| // because this happens in multiple threads | // because this happens in multiple threads | ||||
| std::cerr << "Compile " << objectPath(outDir) << '\n'; | |||||
| logger::log("Compile " + objectPath(outDir)); | |||||
| toolchain::compile(compileFlags(), type_, dir_, name_, outDir); | toolchain::compile(compileFlags(), type_, dir_, name_, outDir); | ||||
| // Nothing will need compile flags after it's compiled, so no reason to | // Nothing will need compile flags after it's compiled, so no reason to |
| #include "logger.h" | |||||
| #include <mutex> | |||||
| #include <vector> | |||||
| #include <condition_variable> | |||||
| #include <thread> | |||||
| #include <iostream> | |||||
| #include <atomic> | |||||
| namespace logger { | |||||
| static std::vector<std::string> queue; | |||||
| static std::mutex mut; | |||||
| static std::condition_variable cond; | |||||
| static std::thread thread; | |||||
| static std::atomic_bool running; | |||||
| static void print(const std::string &msg) { | |||||
| std::cerr << msg << '\n'; | |||||
| } | |||||
| LogContext::~LogContext() { | |||||
| running.store(false); | |||||
| cond.notify_one(); | |||||
| thread.join(); | |||||
| } | |||||
| void log(std::string msg) { | |||||
| std::unique_lock<std::mutex> lock(mut); | |||||
| queue.emplace_back(std::move(msg)); | |||||
| lock.unlock(); | |||||
| cond.notify_one(); | |||||
| } | |||||
| LogContext init() { | |||||
| running = true; | |||||
| thread = std::thread([&] { | |||||
| std::unique_lock<std::mutex> lock(mut); | |||||
| while (running.load()) { | |||||
| cond.wait(lock, [&] { return !queue.empty() || !running.load(); }); | |||||
| for (std::string &msg: queue) { | |||||
| print(msg); | |||||
| } | |||||
| queue.clear(); | |||||
| } | |||||
| }); | |||||
| return LogContext{}; | |||||
| } | |||||
| } |
| #pragma once | |||||
| #include <string> | |||||
| namespace logger { | |||||
| struct LogContext { | |||||
| ~LogContext(); | |||||
| }; | |||||
| void log(std::string msg); | |||||
| LogContext init(); | |||||
| } |
| #include "parallel.h" | #include "parallel.h" | ||||
| #include "toolchain.h" | #include "toolchain.h" | ||||
| #include "globals.h" | #include "globals.h" | ||||
| #include "logger.h" | |||||
| static void readDir(std::string dir, std::vector<SourceFile> &sources, | static void readDir(std::string dir, std::vector<SourceFile> &sources, | ||||
| BBBParser::Variables vars); | BBBParser::Variables vars); | ||||
| const std::vector<SourceFile> &sources, | const std::vector<SourceFile> &sources, | ||||
| const std::string &outDir, const std::string &name, | const std::string &outDir, const std::string &name, | ||||
| toolchain::TargetType targetType) { | toolchain::TargetType targetType) { | ||||
| std::cerr << "Link " << outDir << '/' << name << '\n'; | |||||
| logger::log("Link " + outDir + '/' + name); | |||||
| std::vector<std::string> ldFlags; | std::vector<std::string> ldFlags; | ||||
| std::unordered_set<std::string> ldFlagsSet; | std::unordered_set<std::string> ldFlagsSet; | ||||
| } | } | ||||
| int main(int argc, char **argv) { | int main(int argc, char **argv) { | ||||
| logger::LogContext logCtx = logger::init(); | |||||
| std::vector<std::string> srcDirs; | std::vector<std::string> srcDirs; | ||||
| std::string outDir = "bbbuild"; | std::string outDir = "bbbuild"; | ||||
| int jobs = parallel::coreCount(); | int jobs = parallel::coreCount(); | ||||
| case Action::BUILD: | case Action::BUILD: | ||||
| // TODO: Support more types than BINARY | // TODO: Support more types than BINARY | ||||
| if (!compileAndLink(sources, outDir, jobs, target, toolchain::TargetType::BINARY)) { | if (!compileAndLink(sources, outDir, jobs, target, toolchain::TargetType::BINARY)) { | ||||
| std::cerr << "Nothing to do.\n"; | |||||
| logger::log("Nothing to do.\n"); | |||||
| } | } | ||||
| break; | break; | ||||
| #include <stdexcept> | #include <stdexcept> | ||||
| #include "globals.h" | #include "globals.h" | ||||
| #include "logger.h" | |||||
| namespace sys { | namespace sys { | ||||
| void execute(std::vector<const char *> &args, std::string *output) { | void execute(std::vector<const char *> &args, std::string *output) { | ||||
| if (global::verbose) { | if (global::verbose) { | ||||
| std::string str; | |||||
| for (size_t i = 0; i < args.size(); ++i) { | for (size_t i = 0; i < args.size(); ++i) { | ||||
| if (i == 0) { | if (i == 0) { | ||||
| fprintf(stderr, " %s", args[i]); | |||||
| str += " "; | |||||
| } else { | } else { | ||||
| fprintf(stderr, " %s", args[i]); | |||||
| str += " "; | |||||
| } | } | ||||
| str += args[i]; | |||||
| } | } | ||||
| fprintf(stderr, "\n"); | |||||
| logger::log(str); | |||||
| } | } | ||||
| // argv[0] should be interpreted as a shell command, because being able to run | // argv[0] should be interpreted as a shell command, because being able to run | ||||
| perror(argv[0]); | perror(argv[0]); | ||||
| abort(); | abort(); | ||||
| } | } | ||||
| // This shouldn't happen | |||||
| exit(0); | |||||
| } else if (child < 0) { | } else if (child < 0) { | ||||
| throw std::runtime_error(std::string("fork: ") + strerror(errno)); | throw std::runtime_error(std::string("fork: ") + strerror(errno)); | ||||
| } | } | ||||
| ssize_t num = read(fds[0], buf, sizeof(buf) - 1); | ssize_t num = read(fds[0], buf, sizeof(buf) - 1); | ||||
| if (num < 0 && errno != EAGAIN) { | if (num < 0 && errno != EAGAIN) { | ||||
| close(fds[0]); | close(fds[0]); | ||||
| throw std::runtime_error(std::string("read: ") + strerror(errno)); | |||||
| throw std::runtime_error( | |||||
| std::string("read: ") + strerror(errno) + | |||||
| " (fd: " + std::to_string(fds[0]) + ")"); | |||||
| } else if (num < 0) { | } else if (num < 0) { | ||||
| continue; | continue; | ||||
| } | } | ||||
| if (num == 0) { | if (num == 0) { | ||||
| close(fds[1]); | |||||
| close(fds[0]); | |||||
| break; | break; | ||||
| } | } | ||||