#include "CompileStep.h" #include #include "sys.h" #include "toolchain.h" #include "logger.h" #include "globals.h" static bool startsWith(BXParser &parser, const char *str) { for (size_t i = 0; str[i] != '\0'; ++i) { if (parser.peek() != str[i]) return false; parser.skip(); } return true; } bool CompileStep::checkHasChanged(const std::string &outDir) { std::string objPath = toolchain::objectFilePath(path_, outDir); if (!sys::fileExists(objPath)) { return true; } sys::FileInfo objInfo = sys::fileInfo(objPath); sys::FileInfo sourceInfo = sys::fileInfo(path_); if (objInfo.isOlderThan(sourceInfo)) { return true; } std::string confPath = this->confPath(outDir); if (!sys::fileExists(confPath)) { return true; } BXVariables cachedVariables; try { std::ifstream f = sys::ifstream(confPath); BXParser parser(f, BXParser::FLAG_NONE); parser.parse(cachedVariables); } catch (BXParseError &err) { logger::log(confPath + ": " + err.what()); return true; } // We can trust the cached "deps" value here, because we know // the object file hasn't actually changed. auto depsIt = cachedVariables.find("deps"); if (depsIt == cachedVariables.end()) { logger::log(confPath + ": Missing 'deps' field"); return true; } const std::vector &deps = depsIt->second; for (const auto &dep: deps) { if (!sys::fileExists(dep)) { return true; } sys::FileInfo depInfo = sys::fileInfo(dep); if (objInfo.isOlderThan(depInfo)) { return true; } } // Maybe the build command has changed? auto commandIt = cachedVariables.find("command"); if (commandIt == cachedVariables.end()) { logger::log(confPath + ": Missing 'command' field"); return true; } if (compileCommand(outDir) != commandIt->second) { return true; } return false; } void CompileStep::doBuild(const std::string &outDir) { bool verboseCommand = global::verbose >= 1; if (!verboseCommand) { logger::log("Compile " + path_); } std::string objPath = toolchain::objectFilePath(path_, outDir); std::string dirPath = sys::dirname(objPath); std::vector command = compileCommand(outDir); BXVariables newCachedVars = { { "deps", toolchain::getDependencies(flags(), type_, path_) }, { "command", command}, }; sys::mkdirp(dirPath); std::ofstream f = sys::ofstream(confPath(outDir)); BXWriter writer(f); writer.write(newCachedVars); sys::execute(command, nullptr, verboseCommand); } void CompileStep::doWriteCompDB(const std::string &outDir, compdb::Writer &w) { w.write(sys::cwd(), path_, compileCommand(outDir)); } std::vector CompileStep::getPublicLDFlags(const std::string &outDir) { BXVariables &vars = variables(); std::vector flags; toolchain::getLDFlags(vars, flags); return flags; } std::vector CompileStep::getPublicLDLibs(const std::string &outDir) { BXVariables &vars = variables(); std::vector libs; toolchain::getLDLibs(vars, libs); return libs; } std::vector CompileStep::getPublicObjects(const std::string &outDir) { return std::vector{ toolchain::objectFilePath(path_, outDir) }; } BXVariables &CompileStep::variables() { if (hasVariables_) { return variables_; } std::ifstream f = sys::ifstream(path_); BXParser parser(f, BXParser::FLAG_ONE_LINE); while (f.good()) { if (startsWith(parser, "//#bx")) { parser.parse(variables_); } else { while (f.good() && parser.get() != '\n'); } } hasVariables_ = true; return variables_; } std::vector &CompileStep::flags() { if (hasFlags_) { return flags_; } BXVariables &vars = variables(); toolchain::getFlags(vars, type_, flags_); hasFlags_ = true; return flags_; } std::vector &CompileStep::compileCommand(const std::string &outDir) { if (hasCompileCommand_) { return compileCommand_; } compileCommand_ = toolchain::getCompileCommand(flags(), type_, path_, outDir); hasCompileCommand_ = true; return compileCommand_; }