Res ParseValueStringOrMap(const Context* ctx, Config* cfg, const Json::Value& val, const string& equals_string, const string& separator_string, string* out) { if (val.isString()) { *out = val.asString(); } else if (val.isArray()) { // Evaluate as a lisp expression. Res err = EvalToString(ctx, cfg, val, out); if (!err.Ok()) { err.AppendDetail("in ParseValueStringOrMap(): array eval of '" + val.toStyledString() + "'"); return err; } } else if (val.isObject()) { // Evaluate as a named-value map. bool done_first = false; out->clear(); for (Json::Value::iterator it = val.begin(); it != val.end(); ++it) { if (!it.key().isString()) { return Res(ERR_PARSE, "ParseValueStringOrMap(): key is not a string: '" + it.key().toStyledString()); } const Json::Value& map_val = *it; string result; Res err = EvalToString(ctx, cfg, map_val, &result); if (!err.Ok()) { err.AppendDetail("in ParseValueStringOrMap(): key '" + it.key().toStyledString() + "'"); return err; } if (done_first) { *out += separator_string; } *out += it.key().asString(); *out += equals_string; *out += result; done_first = true; } } return Res(OK); }
void ExitIfError(const Res& res, const Context& context) { if (res.Ok()) { return; } if (res.value() == ERR_SHOW_USAGE) { PrintUsage(); exit(1); } fprintf(stderr, "dmb error\n"); fprintf(stderr, "%s\n", res.ToString().c_str()); if (context.log_verbose()) { context.LogAllTargets(); } exit(1); }
// DepHash(src_file) = Hash(src_file) + sum(for d in deps: DepHash(d)) // // The include_chain helps us avoid infinite recursion in case of // include cycles. Res AccumulateSrcFileDepHash(const Target* t, const Context* context, const string& src_path, const string& inc_dirs_str, std::set<string>* included, Hash* dep_hash) { if (included->find(src_path) != included->end()) { // Recursive include; we've already visited this file. Don't // recurse infinitely! *dep_hash << src_path; return Res(OK); } // Insert src_path into the include chain, and remove it before we // return. struct AutoInsertRemove { public: AutoInsertRemove(std::set<string>* included, const string& file) : included_(included), file_(file) { included_->insert(file_); } ~AutoInsertRemove() { included_->erase(file_); } private: std::set<string>* included_; const string& file_; }; AutoInsertRemove auto_insert_remove(included, src_path); Hash content_hash; Res res = context->ComputeOrGetFileContentHash(src_path, &content_hash); if (!res.Ok()) { return res; } // Check to see if we've already computed this dep hash. HashCache<Hash>* dep_hash_cache = context->GetDepHashCache(); assert(dep_hash_cache); Hash dep_cache_key; dep_cache_key << "dep_cache_key" << src_path << content_hash << inc_dirs_str; Hash cached_dep_hash; if (dep_hash_cache->Get(dep_cache_key, &cached_dep_hash)) { *dep_hash << cached_dep_hash; return Res(OK); } // Compute the dep hash. Hash computed_dep_hash; computed_dep_hash << content_hash; vector<string> includes; res = GetIncludes(t, context, src_path, inc_dirs_str, &includes); if (!res.Ok()) { return res; } for (size_t i = 0; i < includes.size(); i++) { res = AccumulateSrcFileDepHash(t, context, includes[i], inc_dirs_str, included, &computed_dep_hash); if (!res.Ok()) { return res; } } // Write the computed hash back to the cache, for re-use. dep_hash_cache->Insert(dep_cache_key, computed_dep_hash); *dep_hash << computed_dep_hash; return Res(OK); }
Res GetIncludes(const Target* t, const Context* context, const string& src_path, const string& inc_dirs_str, vector<string>* includes) { Hash src_hash; Res res = context->ComputeOrGetFileContentHash(src_path, &src_hash); if (!res.Ok()) { return res; } Hash deps_file_id; deps_file_id << "includes" << src_hash << inc_dirs_str; ObjectStore* ostore = context->GetObjectStore(); FILE* fp = ostore->Read(deps_file_id); if (fp) { // Read the deps file. static const int BUFSIZE = 1000; char linebuf[BUFSIZE]; while (fgets(linebuf, BUFSIZE, fp)) { if (linebuf[0] == '\r' || linebuf[0] == '\n' || linebuf[0] == '#') { // Skip. continue; } int len = strlen(linebuf); // Trim. while (len >= 0 && (linebuf[len - 1] == '\n' || linebuf[len - 1] == '\r')) { linebuf[len - 1] = 0; len--; } includes->push_back(linebuf); } fclose(fp); } else { // Scan the source file. FILE* fp_src = fopen(src_path.c_str(), "rb"); if (!fp_src) { return Res(ERR_FILE_ERROR, "Couldn't open file for include scanning: " + src_path); } string src_dir = FilenamePathPart(src_path); static const int BUFSIZE = 1000; char linebuf[BUFSIZE]; string header_file; string header_path; bool is_quoted = false; while (fgets(linebuf, BUFSIZE, fp_src)) { if (ParseIncludeLine(linebuf, &header_file, &is_quoted)) { if (FindHeader(src_dir, t, context, header_file, is_quoted, &header_path)) { includes->push_back(header_path); } } } fclose(fp_src); // Write the deps file. FILE* fp = ostore->Write(deps_file_id); if (!fp) { context->LogVerbose("Unable to write deps to ostore for file: " + src_path); } else { fprintf(fp, "# includes %s\n", src_path.c_str()); for (size_t i = 0; i < includes->size(); i++) { const string& header_path = (*includes)[i]; bool ok = (fwrite(header_path.c_str(), header_path.size(), 1, fp) == 1) && (fputc('\n', fp) != EOF); if (!ok) { fclose(fp); return Res(ERR_FILE_ERROR, "Error writing to deps file for " + src_path); } } fclose(fp); } } return Res(OK); }