Fix for issue #1928 Automatic Reload and Preview doesn't work properly with multiple documents open.
includesChanged() and handleDependencies()now retrun the latest modification time instead of a bool changed flag. It is then the resposibility of the client to decide if a change has occured.
This commit is contained in:
parent
757c43bdd3
commit
c27dad760b
6 changed files with 47 additions and 37 deletions
|
|
@ -66,45 +66,43 @@ void FileModule::registerUse(const std::string path) {
|
|||
void FileModule::registerInclude(const std::string &localpath,
|
||||
const std::string &fullpath)
|
||||
{
|
||||
struct stat st{};
|
||||
bool valid = stat(fullpath.c_str(), &st) == 0;
|
||||
this->includes[localpath] = {fullpath, valid, st.st_mtime};
|
||||
this->includes[localpath] = {fullpath};
|
||||
}
|
||||
|
||||
bool FileModule::includesChanged() const
|
||||
time_t FileModule::includesChanged() const
|
||||
{
|
||||
time_t latest = 0;
|
||||
for (const auto &item : this->includes) {
|
||||
if (include_modified(item.second)) return true;
|
||||
time_t mtime = include_modified(item.second);
|
||||
if (mtime > latest) latest = mtime;
|
||||
}
|
||||
return false;
|
||||
return latest;
|
||||
}
|
||||
|
||||
bool FileModule::include_modified(const IncludeFile &inc) const
|
||||
time_t FileModule::include_modified(const IncludeFile &inc) const
|
||||
{
|
||||
struct stat st{};
|
||||
|
||||
bool valid = (StatCache::stat(inc.filename.c_str(), &st) == 0);
|
||||
if (valid && !inc.valid) return true; // Detect appearance of file but not removal
|
||||
if (valid && st.st_mtime > inc.mtime) return true;
|
||||
|
||||
return false;
|
||||
if (StatCache::stat(inc.filename.c_str(), &st) == 0)
|
||||
return st.st_mtime;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
Check if any dependencies have been modified and recompile them.
|
||||
Returns true if anything was recompiled.
|
||||
*/
|
||||
bool FileModule::handleDependencies()
|
||||
time_t FileModule::handleDependencies()
|
||||
{
|
||||
if (this->is_handling_dependencies) return false;
|
||||
if (this->is_handling_dependencies) return 0;
|
||||
this->is_handling_dependencies = true;
|
||||
|
||||
bool somethingchanged = false;
|
||||
std::vector<std::pair<std::string,std::string>> updates;
|
||||
|
||||
// If a lib in usedlibs was previously missing, we need to relocate it
|
||||
// by searching the applicable paths. We can identify a previously missing module
|
||||
// as it will have a relative path.
|
||||
time_t latest = 0;
|
||||
for (auto filename : this->usedlibs) {
|
||||
|
||||
bool wasmissing = false;
|
||||
|
|
@ -127,7 +125,9 @@ bool FileModule::handleDependencies()
|
|||
bool wascached = ModuleCache::instance()->isCached(filename);
|
||||
FileModule *oldmodule = ModuleCache::instance()->lookup(filename);
|
||||
FileModule *newmodule;
|
||||
bool changed = ModuleCache::instance()->evaluate(filename, newmodule);
|
||||
time_t mtime = ModuleCache::instance()->evaluate(filename, newmodule);
|
||||
if (mtime > latest) latest = mtime;
|
||||
bool changed = newmodule && newmodule != oldmodule;
|
||||
// Detect appearance but not removal of files, and keep old module
|
||||
// on compile errors (FIXME: Is this correct behavior?)
|
||||
if (changed) {
|
||||
|
|
@ -136,7 +136,6 @@ bool FileModule::handleDependencies()
|
|||
else {
|
||||
PRINTDB(" %s: %p", filename % oldmodule);
|
||||
}
|
||||
somethingchanged |= changed;
|
||||
// Only print warning if we're not part of an automatic reload
|
||||
if (!newmodule && !wascached && !wasmissing) {
|
||||
PRINTB_NOCACHE("WARNING: Failed to compile library '%s'.", filename);
|
||||
|
|
@ -151,7 +150,7 @@ bool FileModule::handleDependencies()
|
|||
this->usedlibs.insert(files.second);
|
||||
}
|
||||
this->is_handling_dependencies = false;
|
||||
return somethingchanged;
|
||||
return latest;
|
||||
}
|
||||
|
||||
AbstractNode *FileModule::instantiate(const Context *ctx, const ModuleInstantiation *inst,
|
||||
|
|
|
|||
|
|
@ -23,8 +23,8 @@ public:
|
|||
const std::string &modulePath() const { return this->path; }
|
||||
void registerUse(const std::string path);
|
||||
void registerInclude(const std::string &localpath, const std::string &fullpath);
|
||||
bool includesChanged() const;
|
||||
bool handleDependencies();
|
||||
time_t includesChanged() const;
|
||||
time_t handleDependencies();
|
||||
bool hasIncludes() const { return !this->includes.empty(); }
|
||||
bool usesLibraries() const { return !this->usedlibs.empty(); }
|
||||
bool isHandlingDependencies() const { return this->is_handling_dependencies; }
|
||||
|
|
@ -35,11 +35,9 @@ public:
|
|||
private:
|
||||
struct IncludeFile {
|
||||
std::string filename;
|
||||
bool valid;
|
||||
time_t mtime;
|
||||
};
|
||||
|
||||
bool include_modified(const IncludeFile &inc) const;
|
||||
time_t include_modified(const IncludeFile &inc) const;
|
||||
|
||||
typedef std::unordered_map<std::string, struct IncludeFile> IncludeContainer;
|
||||
IncludeContainer includes;
|
||||
|
|
|
|||
|
|
@ -274,6 +274,8 @@ private:
|
|||
class CGALWorker *cgalworker;
|
||||
QMutex consolemutex;
|
||||
bool contentschanged; // Set if the source code has changes since the last render (F6)
|
||||
time_t includes_mtime; // latest include mod time
|
||||
time_t deps_mtime; // latest dependency mod time
|
||||
|
||||
signals:
|
||||
void highlightError(int);
|
||||
|
|
|
|||
|
|
@ -29,9 +29,9 @@ ModuleCache *ModuleCache::inst = nullptr;
|
|||
Sets the module reference to the new module, or nullptr on any error (e.g. compile
|
||||
error or file not found).
|
||||
|
||||
Returns true if anything was compiled (module or dependencies) and false otherwise.
|
||||
Returns the latest mod time of the modul or its dependencies or includes.
|
||||
*/
|
||||
bool ModuleCache::evaluate(const std::string &filename, FileModule *&module)
|
||||
time_t ModuleCache::evaluate(const std::string &filename, FileModule *&module)
|
||||
{
|
||||
module = nullptr;
|
||||
FileModule *lib_mod = nullptr;
|
||||
|
|
@ -43,14 +43,14 @@ bool ModuleCache::evaluate(const std::string &filename, FileModule *&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 false;
|
||||
if (lib_mod && lib_mod->isHandlingDependencies()) return 0;
|
||||
|
||||
// Create cache ID
|
||||
struct stat st{};
|
||||
bool valid = (StatCache::stat(filename.c_str(), &st) == 0);
|
||||
|
||||
// If file isn't there, just return and let the cache retain the old module
|
||||
if (!valid) return false;
|
||||
if (!valid) return 0;
|
||||
|
||||
// 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);
|
||||
|
|
@ -60,6 +60,7 @@ bool ModuleCache::evaluate(const std::string &filename, FileModule *&module)
|
|||
if (!found) {
|
||||
entry.module = nullptr;
|
||||
entry.cache_id = cache_id;
|
||||
entry.compile_time = 0;
|
||||
}
|
||||
|
||||
bool shouldCompile = true;
|
||||
|
|
@ -68,7 +69,7 @@ bool ModuleCache::evaluate(const std::string &filename, FileModule *&module)
|
|||
if (entry.cache_id == cache_id) {
|
||||
shouldCompile = false;
|
||||
// Recompile if includes changed
|
||||
if (lib_mod && lib_mod->includesChanged()) {
|
||||
if (lib_mod && lib_mod->includesChanged() > entry.compile_time) {
|
||||
lib_mod = nullptr;
|
||||
shouldCompile = true;
|
||||
}
|
||||
|
|
@ -91,12 +92,13 @@ bool ModuleCache::evaluate(const std::string &filename, FileModule *&module)
|
|||
}
|
||||
#endif
|
||||
|
||||
entry.compile_time = time(NULL); // update compile time before parsing to avoid race condition.
|
||||
std::stringstream textbuf;
|
||||
{
|
||||
std::ifstream ifs(filename.c_str());
|
||||
if (!ifs.is_open()) {
|
||||
PRINTB("WARNING: Can't open library file '%s'\n", filename);
|
||||
return false;
|
||||
return 0;
|
||||
}
|
||||
textbuf << ifs.rdbuf();
|
||||
}
|
||||
|
|
@ -120,9 +122,9 @@ bool ModuleCache::evaluate(const std::string &filename, FileModule *&module)
|
|||
}
|
||||
|
||||
module = lib_mod;
|
||||
bool depschanged = lib_mod ? lib_mod->handleDependencies() : false;
|
||||
time_t deps_mtime = lib_mod ? lib_mod->handleDependencies() : 0;
|
||||
|
||||
return shouldCompile || depschanged;
|
||||
return deps_mtime > entry.compile_time ? deps_mtime : entry.compile_time;
|
||||
}
|
||||
|
||||
void ModuleCache::clear()
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ class ModuleCache
|
|||
{
|
||||
public:
|
||||
static ModuleCache *instance() { if (!inst) inst = new ModuleCache; return inst; }
|
||||
bool evaluate(const std::string &filename, class FileModule *&module);
|
||||
time_t evaluate(const std::string &filename, class FileModule *&module);
|
||||
class FileModule *lookup(const std::string &filename);
|
||||
bool isCached(const std::string &filename);
|
||||
size_t size() { return this->entries.size(); }
|
||||
|
|
@ -25,6 +25,7 @@ private:
|
|||
struct cache_entry {
|
||||
class FileModule *module;
|
||||
std::string cache_id;
|
||||
time_t compile_time; // last time this module was compiled
|
||||
};
|
||||
std::unordered_map<std::string, cache_entry> entries;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -177,7 +177,7 @@ bool MainWindow::undockMode = false;
|
|||
bool MainWindow::reorderMode = false;
|
||||
|
||||
MainWindow::MainWindow(const QString &filename)
|
||||
: root_inst("group"), library_info_dialog(NULL), font_list_dialog(NULL), procevents(false), tempFile(NULL), progresswidget(NULL), contentschanged(false)
|
||||
: root_inst("group"), library_info_dialog(NULL), font_list_dialog(NULL), procevents(false), tempFile(NULL), progresswidget(NULL), contentschanged(false), includes_mtime(0), deps_mtime(0)
|
||||
{
|
||||
setupUi(this);
|
||||
|
||||
|
|
@ -969,8 +969,12 @@ void MainWindow::compile(bool reload, bool forcedone)
|
|||
shouldcompiletoplevel = true;
|
||||
}
|
||||
|
||||
if (!shouldcompiletoplevel && this->root_module && this->root_module->includesChanged()) {
|
||||
shouldcompiletoplevel = true;
|
||||
if (!shouldcompiletoplevel && this->root_module) {
|
||||
time_t mtime = this->root_module->includesChanged();
|
||||
if (mtime > this->includes_mtime) {
|
||||
this->includes_mtime = mtime;
|
||||
shouldcompiletoplevel = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldcompiletoplevel) {
|
||||
|
|
@ -981,7 +985,9 @@ void MainWindow::compile(bool reload, bool forcedone)
|
|||
}
|
||||
|
||||
if (this->root_module) {
|
||||
if (this->root_module->handleDependencies()) {
|
||||
time_t mtime = this->root_module->handleDependencies();
|
||||
if (mtime > this->deps_mtime) {
|
||||
this->deps_mtime = mtime;
|
||||
PRINTB("Module cache size: %d modules", ModuleCache::instance()->size());
|
||||
didcompile = true;
|
||||
}
|
||||
|
|
@ -1012,7 +1018,9 @@ void MainWindow::compile(bool reload, bool forcedone)
|
|||
|
||||
void MainWindow::waitAfterReload()
|
||||
{
|
||||
if (this->root_module->handleDependencies()) {
|
||||
time_t mtime = this->root_module->handleDependencies();
|
||||
if (mtime > this->deps_mtime) {
|
||||
this->deps_mtime = mtime;
|
||||
this->waitAfterReloadTimer->start();
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue