#include "ModuleCache.h" #include "module.h" #include "printutils.h" #include "openscad.h" #include "boosty.h" #include #include #include #include #include #include #include #include //#include "parsersettings.h" /*! FIXME: Implement an LRU scheme to avoid having an ever-growing module cache */ ModuleCache *ModuleCache::inst = NULL; /*! Reevaluate the given file and recompile if necessary. Returns NULL on any error (e.g. compile error or file not found) The given filename must be absolute. */ FileModule *ModuleCache::evaluate(const std::string &filename) { FileModule *lib_mod = NULL; bool found = false; if (this->entries.find(filename) != this->entries.end()) { found = true; lib_mod = this->entries[filename].module; } // Don't try to recursively evaluate - if the file changes // during evaluation, that would be really bad. if (lib_mod && lib_mod->isHandlingDependencies()) return lib_mod; // Create cache ID struct stat st; memset(&st, 0, sizeof(struct stat)); bool valid = (stat(filename.c_str(), &st) == 0); // If file isn't there, just return and let the cache retain the old module if (!valid) return NULL; // If the file is present, we'll always cache some result std::string cache_id = str(boost::format("%x.%x") % st.st_mtime % st.st_size); cache_entry &entry = this->entries[filename]; // Initialize entry, if new if (!found) { entry.module = NULL; entry.cache_id = cache_id; } bool shouldCompile = true; if (found) { // Files should only be recompiled if the cache ID changed if (entry.cache_id == cache_id) { shouldCompile = false; // Recompile if includes changed if (lib_mod && lib_mod->includesChanged()) { lib_mod = NULL; shouldCompile = true; } } } #ifdef DEBUG // Causes too much debug output //if (!shouldCompile) PRINTB("Using cached library: %s (%p)", filename % lib_mod); #endif // If cache lookup failed (non-existing or old timestamp), compile module if (shouldCompile) { #ifdef DEBUG if (found) { PRINTB("Recompiling cached library: %s (%s)", filename % cache_id); } else { PRINTB("Compiling library '%s'.", filename); } #endif std::stringstream textbuf; { std::ifstream ifs(filename.c_str()); if (!ifs.is_open()) { PRINTB("WARNING: Can't open library file '%s'\n", filename); return NULL; } textbuf << ifs.rdbuf(); } textbuf << "\n" << commandline_commands; print_messages_push(); FileModule *oldmodule = lib_mod; std::string pathname = boosty::stringy(fs::path(filename).parent_path()); lib_mod = dynamic_cast(parse(textbuf.str().c_str(), pathname.c_str(), false)); PRINTB_NOCACHE(" compiled module: %p", lib_mod); // We defer deletion so we can ensure that the new module won't // have the same address as the old if (oldmodule) delete oldmodule; entry.module = lib_mod; entry.cache_id = cache_id; print_messages_pop(); } if (lib_mod) lib_mod->handleDependencies(); return lib_mod; } void ModuleCache::clear() { this->entries.clear(); } FileModule *ModuleCache::lookup(const std::string &filename) { return isCached(filename) ? this->entries[filename].module : NULL; } bool ModuleCache::isCached(const std::string &filename) { return this->entries.find(filename) != this->entries.end(); }