summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/AppleEvents.cc2
-rw-r--r--src/MainWindow.h21
-rw-r--r--src/ModuleCache.cc27
-rw-r--r--src/expr.cc2
-rw-r--r--src/lexer.l2
-rw-r--r--src/mainwin.cc251
-rw-r--r--src/module.cc66
-rw-r--r--src/module.h23
-rw-r--r--src/parsersettings.cc13
-rw-r--r--testdata/modulecache-tests/README.txt40
-rwxr-xr-xtestdata/modulecache-tests/cascade.sh12
-rwxr-xr-xtestdata/modulecache-tests/cascade2.sh12
12 files changed, 308 insertions, 163 deletions
diff --git a/src/AppleEvents.cc b/src/AppleEvents.cc
index 1fb4f99..3102792 100644
--- a/src/AppleEvents.cc
+++ b/src/AppleEvents.cc
@@ -15,7 +15,7 @@ OSErr eventHandler(const AppleEvent *, AppleEvent *, SRefCon )
if (mainwin) break;
}
if (mainwin) {
- mainwin->actionReloadCompile();
+ mainwin->actionReloadRenderCSG();
}
return noErr;
}
diff --git a/src/MainWindow.h b/src/MainWindow.h
index bd32bdd..79e2080 100644
--- a/src/MainWindow.h
+++ b/src/MainWindow.h
@@ -28,6 +28,7 @@ public:
QTimer *autoReloadTimer;
std::string autoReloadId;
+ QTimer *waitAfterReloadTimer;
ModuleContext top_ctx;
FileModule *root_module; // Result of parsing
@@ -76,9 +77,8 @@ private:
void refreshDocument();
void updateTemporalVariables();
bool fileChangedOnDisk();
- bool includesChanged();
- bool compileTopLevelDocument(bool reload);
- bool compile(bool reload, bool procevents);
+ void compileTopLevelDocument();
+ void compile(bool reload, bool forcedone = false);
void compileCSG(bool procevents);
bool maybeSave();
bool checkEditorModified();
@@ -103,6 +103,10 @@ private slots:
void actionReload();
void actionShowLibraryFolder();
+ void instantiateRoot();
+ void compileDone(bool didchange);
+ void compileEnded();
+
private slots:
void pasteViewportTranslation();
void pasteViewportRotation();
@@ -110,10 +114,13 @@ private slots:
void preferences();
private slots:
- void actionCompile();
+ void actionRenderCSG();
+ void csgRender();
+ void csgReloadRender();
#ifdef ENABLE_CGAL
void actionRenderCGAL();
void actionRenderCGALDone(class CGAL_Nef_polyhedron *);
+ void cgalRender();
#endif
void actionDisplayAST();
void actionDisplayCSGTree();
@@ -132,6 +139,7 @@ public:
void clearCurrentOutput();
public slots:
+ void actionReloadRenderCSG();
#ifdef ENABLE_OPENCSG
void viewModeOpenCSG();
#endif
@@ -164,13 +172,16 @@ public slots:
void helpManual();
void helpLibrary();
void quit();
- void actionReloadCompile();
void checkAutoReload();
+ void waitAfterReload();
void autoReloadSet(bool);
private:
static void report_func(const class AbstractNode*, void *vp, int mark);
+ char const * afterCompileSlot;
+ bool procevents;
+
class ProgressWidget *progresswidget;
class CGALWorker *cgalworker;
QMutex consolemutex;
diff --git a/src/ModuleCache.cc b/src/ModuleCache.cc
index 505015e..5ebd549 100644
--- a/src/ModuleCache.cc
+++ b/src/ModuleCache.cc
@@ -30,34 +30,37 @@ ModuleCache *ModuleCache::inst = NULL;
*/
FileModule *ModuleCache::evaluate(const std::string &filename)
{
+ bool shouldCompile = true;
FileModule *lib_mod = NULL;
// Create cache ID
struct stat st;
memset(&st, 0, sizeof(struct stat));
- stat(filename.c_str(), &st);
+ 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;
std::string cache_id = str(boost::format("%x.%x") % st.st_mtime % st.st_size);
// Lookup in cache
- if (this->entries.find(filename) != this->entries.end() &&
- this->entries[filename].cache_id == cache_id) {
-#ifdef DEBUG
-// Causes too much debug output
-// PRINTB("Using cached library: %s (%s)", filename % cache_id);
-#endif
+ if (this->entries.find(filename) != this->entries.end()) {
lib_mod = &(*this->entries[filename].module);
-
- BOOST_FOREACH(const FileModule::IncludeContainer::value_type &include, lib_mod->includes) {
- if (lib_mod->include_modified(include.second)) {
+ if (this->entries[filename].cache_id == cache_id) {
+ shouldCompile = false;
+
+ if (lib_mod->includesChanged()) {
lib_mod = NULL;
- break;
+ shouldCompile = true;
}
}
}
+ else {
+ shouldCompile = valid;
+ }
// If cache lookup failed (non-existing or old timestamp), compile module
- if (!lib_mod) {
+ if (shouldCompile) {
#ifdef DEBUG
if (this->entries.find(filename) != this->entries.end()) {
PRINTB("Recompiling cached library: %s (%s)", filename % cache_id);
diff --git a/src/expr.cc b/src/expr.cc
index 46d606f..2e069f0 100644
--- a/src/expr.cc
+++ b/src/expr.cc
@@ -69,7 +69,7 @@ public:
expr.recursioncount++;
}
~FuncRecursionGuard() { expr.recursioncount--; }
- bool recursion_detected() const { return (expr.recursioncount > 100); }
+ bool recursion_detected() const { return (expr.recursioncount > 1000); }
private:
const Expression &expr;
};
diff --git a/src/lexer.l b/src/lexer.l
index 0b8048f..3dd3b6b 100644
--- a/src/lexer.l
+++ b/src/lexer.l
@@ -100,7 +100,7 @@ E [Ee][+-]?{D}+
%%
-include[ \t\r\n>]*"<" { BEGIN(cond_include); filepath = filename = "";}
+include[ \t\r\n>]*"<" { BEGIN(cond_include); filepath = filename = ""; }
<cond_include>{
[^\t\r\n>]*"/" { filepath = yytext; }
[^\t\r\n>/]+ { filename = yytext; }
diff --git a/src/mainwin.cc b/src/mainwin.cc
index ac75269..eedd3a5 100644
--- a/src/mainwin.cc
+++ b/src/mainwin.cc
@@ -206,9 +206,15 @@ MainWindow::MainWindow(const QString &filename)
autoReloadTimer = new QTimer(this);
autoReloadTimer->setSingleShot(false);
+ autoReloadTimer->setInterval(200);
connect(autoReloadTimer, SIGNAL(timeout()), this, SLOT(checkAutoReload()));
- connect(this->e_tval, SIGNAL(textChanged(QString)), this, SLOT(actionCompile()));
+ waitAfterReloadTimer = new QTimer(this);
+ waitAfterReloadTimer->setSingleShot(true);
+ waitAfterReloadTimer->setInterval(200);
+ connect(waitAfterReloadTimer, SIGNAL(timeout()), this, SLOT(waitAfterReload()));
+
+ connect(this->e_tval, SIGNAL(textChanged(QString)), this, SLOT(actionRenderCSG()));
connect(this->e_fps, SIGNAL(textChanged(QString)), this, SLOT(updatedFps()));
animate_panel->hide();
@@ -288,8 +294,8 @@ MainWindow::MainWindow(const QString &filename)
// Design menu
connect(this->designActionAutoReload, SIGNAL(toggled(bool)), this, SLOT(autoReloadSet(bool)));
- connect(this->designActionReloadAndCompile, SIGNAL(triggered()), this, SLOT(actionReloadCompile()));
- connect(this->designActionCompile, SIGNAL(triggered()), this, SLOT(actionCompile()));
+ connect(this->designActionReloadAndCompile, SIGNAL(triggered()), this, SLOT(actionReloadRenderCSG()));
+ connect(this->designActionCompile, SIGNAL(triggered()), this, SLOT(actionRenderCSG()));
#ifdef ENABLE_CGAL
connect(this->designActionCompileAndRender, SIGNAL(triggered()), this, SLOT(actionRenderCGAL()));
#else
@@ -580,7 +586,7 @@ void MainWindow::updateTVal()
double fps = this->e_fps->text().toDouble(&fps_ok);
if (fps_ok) {
if (fps <= 0) {
- actionCompile();
+ actionRenderCSG();
} else {
double s = this->e_fsteps->text().toDouble();
double t = this->e_tval->text().toDouble() + 1/s;
@@ -612,14 +618,89 @@ void MainWindow::refreshDocument()
}
/*!
- Parse and evaluate the design => this->root_node
-
- Returns true if something was compiled, false if nothing was changed
- and the root_node was left untouched.
+ compiles the design. Calls compileDone() if anything was compiled
*/
-bool MainWindow::compile(bool reload, bool procevents)
+void MainWindow::compile(bool reload, bool forcedone)
+{
+ bool shouldcompiletoplevel = false;
+ bool didcompile = false;
+
+ // Reload checks the timestamp of the toplevel file and refreshes if necessary,
+ if (reload) {
+ if (fileChangedOnDisk() && checkEditorModified()) {
+ shouldcompiletoplevel = true;
+ refreshDocument();
+ }
+ }
+ else {
+ shouldcompiletoplevel = true;
+ }
+
+ if (!shouldcompiletoplevel && this->root_module && this->root_module->includesChanged()) {
+ shouldcompiletoplevel = true;
+ }
+
+ if (shouldcompiletoplevel) {
+ console->clear();
+ compileTopLevelDocument();
+ didcompile = true;
+ }
+
+ if (this->root_module) {
+ if (this->root_module->handleDependencies()) {
+ PRINTB("Module cache size: %d modules", ModuleCache::instance()->size());
+ didcompile = true;
+ }
+ }
+
+ // If we're auto-reloading, listen for a cascade of changes by starting a timer
+ // if something changed _and_ there are any external dependencies
+ if (reload && didcompile && this->root_module) {
+ if (this->root_module->hasIncludes() ||
+ this->root_module->usesLibraries()) {
+ this->waitAfterReloadTimer->start();
+ return;
+ }
+ }
+ compileDone(didcompile | forcedone);
+}
+
+void MainWindow::waitAfterReload()
+{
+ if (this->root_module->handleDependencies()) {
+ this->waitAfterReloadTimer->start();
+ return;
+ }
+ else {
+ compile(true, true); // In case file itself or top-level includes changed during dependency updates
+ }
+}
+
+void MainWindow::compileDone(bool didchange)
+{
+ const char *callslot;
+ if (didchange) {
+ instantiateRoot();
+ callslot = afterCompileSlot;
+ }
+ else {
+ callslot = "compileEnded";
+ }
+
+ this->procevents = false;
+ QMetaObject::invokeMethod(this, callslot);
+}
+
+void MainWindow::compileEnded()
+{
+ clearCurrentOutput();
+ GuiLocker::unlock();
+ if (designActionAutoReload->isChecked()) autoReloadTimer->start();
+}
+
+void MainWindow::instantiateRoot()
{
- if (!compileTopLevelDocument(reload)) return false;
+ // Go on and instantiate root_node, then call the continuation slot
// Invalidate renderers before we kill the CSG tree
this->qglview->setRenderer(NULL);
@@ -652,7 +733,7 @@ bool MainWindow::compile(bool reload, bool procevents)
if (this->root_module) {
// Evaluate CSG tree
PRINT("Compiling design (CSG Tree generation)...");
- if (procevents) QApplication::processEvents();
+ if (this->procevents) QApplication::processEvents();
AbstractNode::resetIndexCounter();
this->root_inst = ModuleInstantiation("group");
@@ -677,10 +758,8 @@ bool MainWindow::compile(bool reload, bool procevents)
} else {
PRINT("ERROR: Compilation failed!");
}
- if (procevents) QApplication::processEvents();
+ if (this->procevents) QApplication::processEvents();
}
-
- return true;
}
/*!
@@ -1027,7 +1106,10 @@ bool MainWindow::fileChangedOnDisk()
if (!this->fileName.isEmpty()) {
struct stat st;
memset(&st, 0, sizeof(struct stat));
- stat(this->fileName.toLocal8Bit(), &st);
+ bool valid = (stat(this->fileName.toLocal8Bit(), &st) == 0);
+ // If file isn't there, just return and use current editor text
+ if (!valid) return false;
+
std::string newid = str(boost::format("%x.%x") % st.st_mtime % st.st_size);
if (newid != this->autoReloadId) {
@@ -1038,77 +1120,43 @@ bool MainWindow::fileChangedOnDisk()
return false;
}
-bool MainWindow::includesChanged()
-{
- if (this->root_module) {
- BOOST_FOREACH(const FileModule::IncludeContainer::value_type &item, this->root_module->includes) {
- if (this->root_module->include_modified(item.second)) return true;
- }
- }
- return false;
-}
-
/*!
- If reload is true, does a timestamp check on the document and tries to reload it.
- Otherwise, just reparses the current document and any dependencies, updates the
- GUI accordingly and populates this->root_module.
-
Returns true if anything was compiled.
*/
-bool MainWindow::compileTopLevelDocument(bool reload)
+void MainWindow::compileTopLevelDocument()
{
- bool shouldcompiletoplevel = !reload;
-
- if (includesChanged()) shouldcompiletoplevel = true;
-
- if (reload && fileChangedOnDisk() && checkEditorModified()) {
- shouldcompiletoplevel = true;
- refreshDocument();
- }
+ updateTemporalVariables();
- if (shouldcompiletoplevel) {
- console->clear();
-
- updateTemporalVariables();
-
- this->last_compiled_doc = editor->toPlainText();
- std::string fulltext =
- std::string(this->last_compiled_doc.toLocal8Bit().constData()) +
- "\n" + commandline_commands;
-
- delete this->root_module;
- this->root_module = NULL;
-
- this->root_module = parse(fulltext.c_str(),
- this->fileName.isEmpty() ?
- "" :
- QFileInfo(this->fileName).absolutePath().toLocal8Bit(),
- false);
-
- if (!animate_panel->isVisible()) {
- highlighter->unhighlightLastError();
- if (!this->root_module) {
- QTextCursor cursor = editor->textCursor();
- cursor.setPosition(parser_error_pos);
- editor->setTextCursor(cursor);
- highlighter->highlightError( parser_error_pos );
- }
+ this->last_compiled_doc = editor->toPlainText();
+ std::string fulltext =
+ std::string(this->last_compiled_doc.toLocal8Bit().constData()) +
+ "\n" + commandline_commands;
+
+ delete this->root_module;
+ this->root_module = NULL;
+
+ this->root_module = parse(fulltext.c_str(),
+ this->fileName.isEmpty() ?
+ "" :
+ QFileInfo(this->fileName).absolutePath().toLocal8Bit(),
+ false);
+
+ if (!animate_panel->isVisible()) {
+ highlighter->unhighlightLastError();
+ if (!this->root_module) {
+ QTextCursor cursor = editor->textCursor();
+ cursor.setPosition(parser_error_pos);
+ editor->setTextCursor(cursor);
+ highlighter->highlightError( parser_error_pos );
}
-
}
-
- bool changed = shouldcompiletoplevel;
- if (this->root_module) {
- changed |= this->root_module->handleDependencies();
- if (changed) PRINTB("Module cache size: %d modules", ModuleCache::instance()->size());
- }
-
- return changed;
}
void MainWindow::checkAutoReload()
{
- if (!this->fileName.isEmpty()) actionReloadCompile();
+ if (!this->fileName.isEmpty()) {
+ actionReloadRenderCSG();
+ }
}
void MainWindow::autoReloadSet(bool on)
@@ -1117,7 +1165,7 @@ void MainWindow::autoReloadSet(bool on)
settings.setValue("design/autoReload",designActionAutoReload->isChecked());
if (on) {
autoReloadId = "";
- autoReloadTimer->start(200);
+ autoReloadTimer->start();
} else {
autoReloadTimer->stop();
}
@@ -1139,15 +1187,22 @@ bool MainWindow::checkEditorModified()
return true;
}
-void MainWindow::actionReloadCompile()
+void MainWindow::actionReloadRenderCSG()
{
if (GuiLocker::isLocked()) return;
- GuiLocker lock;
+ GuiLocker::lock();
+ autoReloadTimer->stop();
setCurrentOutput();
// PRINT("Parsing design (AST generation)...");
// QApplication::processEvents();
- if (!compile(true, true)) return;
+ this->afterCompileSlot = "csgReloadRender";
+ this->procevents = true;
+ compile(true);
+}
+
+void MainWindow::csgReloadRender()
+{
if (this->root_node) compileCSG(true);
// Go to non-CGAL view mode
@@ -1161,20 +1216,25 @@ void MainWindow::actionReloadCompile()
viewModeThrownTogether();
#endif
}
-
- clearCurrentOutput();
+ compileEnded();
}
-void MainWindow::actionCompile()
+void MainWindow::actionRenderCSG()
{
if (GuiLocker::isLocked()) return;
- GuiLocker lock;
+ GuiLocker::lock();
+ autoReloadTimer->stop();
setCurrentOutput();
- console->clear();
PRINT("Parsing design (AST generation)...");
QApplication::processEvents();
- compile(false, !viewActionAnimate->isChecked());
+ this->afterCompileSlot = "csgRender";
+ this->procevents = !viewActionAnimate->isChecked();
+ compile(false);
+}
+
+void MainWindow::csgRender()
+{
if (this->root_node) compileCSG(!viewActionAnimate->isChecked());
// Go to non-CGAL view mode
@@ -1198,7 +1258,7 @@ void MainWindow::actionCompile()
img.save(filename, "PNG");
}
- clearCurrentOutput();
+ compileEnded();
}
#ifdef ENABLE_CGAL
@@ -1206,15 +1266,19 @@ void MainWindow::actionCompile()
void MainWindow::actionRenderCGAL()
{
if (GuiLocker::isLocked()) return;
- GuiLocker lock;
-
+ GuiLocker::lock();
+ autoReloadTimer->stop();
setCurrentOutput();
- console->clear();
PRINT("Parsing design (AST generation)...");
QApplication::processEvents();
- compile(false, true);
+ this->afterCompileSlot = "cgalRender";
+ this->procevents = true;
+ compile(false);
+}
+void MainWindow::cgalRender()
+{
if (!this->root_module || !this->root_node) {
return;
}
@@ -1234,7 +1298,6 @@ void MainWindow::actionRenderCGAL()
progress_report_prep(this->root_node, report_func, this);
- GuiLocker::lock(); // Will be unlocked in actionRenderCGALDone()
this->cgalworker->start(this->tree);
}
@@ -1297,9 +1360,7 @@ void MainWindow::actionRenderCGALDone(CGAL_Nef_polyhedron *root_N)
this->statusBar()->removeWidget(this->progresswidget);
delete this->progresswidget;
this->progresswidget = NULL;
- clearCurrentOutput();
-
- GuiLocker::unlock();
+ compileEnded();
}
#endif /* ENABLE_CGAL */
@@ -1612,7 +1673,7 @@ void MainWindow::viewModeAnimate()
{
if (viewActionAnimate->isChecked()) {
animate_panel->show();
- actionCompile();
+ actionRenderCSG();
updatedFps();
} else {
animate_panel->hide();
diff --git a/src/module.cc b/src/module.cc
index cc0f99c..046d0c4 100644
--- a/src/module.cc
+++ b/src/module.cc
@@ -166,7 +166,7 @@ public:
~ModRecursionGuard() {
inst.recursioncount--;
}
- bool recursion_detected() const { return (inst.recursioncount > 100); }
+ bool recursion_detected() const { return (inst.recursioncount > 1000); }
private:
const ModuleInstantiation &inst;
};
@@ -226,6 +226,28 @@ void FileModule::registerInclude(const std::string &localpath,
this->includes[localpath] = inc;
}
+bool FileModule::includesChanged() const
+{
+ BOOST_FOREACH(const FileModule::IncludeContainer::value_type &item, this->includes) {
+ if (include_modified(item.second)) return true;
+ }
+ return false;
+}
+
+bool FileModule::include_modified(const IncludeFile &inc) const
+{
+ struct stat st;
+ memset(&st, 0, sizeof(struct stat));
+
+ fs::path fullpath = find_valid_path(this->path, inc.filename);
+ bool valid = !fullpath.empty() ? (stat(boosty::stringy(fullpath).c_str(), &st) == 0) : false;
+
+ if (valid && !inc.valid) return true; // Detect appearance of file but not removal
+ if (valid && st.st_mtime > inc.mtime) return true;
+
+ return false;
+}
+
/*!
Check if any dependencies have been modified and recompile them.
Returns true if anything was recompiled.
@@ -236,35 +258,49 @@ bool FileModule::handleDependencies()
this->is_handling_dependencies = true;
bool changed = false;
- // Iterating manually since we want to modify the container while iterating
-
// 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.
+
+ // Iterating manually since we want to modify the container while iterating
+ std::vector<std::pair<std::string, FileModule*> > modified_modules;
FileModule::ModuleContainer::iterator iter = this->usedlibs.begin();
while (iter != this->usedlibs.end()) {
FileModule::ModuleContainer::iterator curr = iter++;
FileModule *oldmodule = curr->second;
+ bool wasmissing = false;
// Get an absolute filename for the module
std::string filename = curr->first;
if (!boosty::is_absolute(filename)) {
+ wasmissing = true;
fs::path fullpath = find_valid_path(this->path, filename);
if (!fullpath.empty()) filename = boosty::stringy(fullpath);
}
- curr->second = ModuleCache::instance()->evaluate(filename);
- if (curr->second != oldmodule) {
+ FileModule *newmodule = ModuleCache::instance()->evaluate(filename);
+ // Detect appearance but not removal of files
+ if (newmodule && oldmodule != newmodule) {
changed = true;
#ifdef DEBUG
- PRINTB_NOCACHE(" %s: %p", filename % curr->second);
+ PRINTB_NOCACHE(" %s: %p", filename % newmodule);
#endif
}
- if (!curr->second) {
- PRINTB_NOCACHE("WARNING: Failed to compile library '%s'.", filename);
+ if (newmodule) {
+ modified_modules.push_back(std::make_pair(filename, newmodule));
+ this->usedlibs.erase(curr);
+ }
+ else {
+ // Only print warning if we're not part of an automatic reload
+ if (!oldmodule && !wasmissing) {
+ PRINTB_NOCACHE("WARNING: Failed to compile library '%s'.", filename);
+ }
}
}
+ BOOST_FOREACH(const FileModule::ModuleContainer::value_type &mod, modified_modules) {
+ this->usedlibs[mod.first] = mod.second;
+ }
this->is_handling_dependencies = false;
return changed;
@@ -286,17 +322,3 @@ AbstractNode *FileModule::instantiate(const Context *ctx, const ModuleInstantiat
return node;
}
-
-bool FileModule::include_modified(IncludeFile inc)
-{
- struct stat st;
- memset(&st, 0, sizeof(struct stat));
-
- fs::path fullpath = find_valid_path(this->path, inc.filename);
- bool valid = !fullpath.empty() ? (stat(boosty::stringy(fullpath).c_str(), &st) == 0) : false;
-
- if (valid != inc.valid) return true;
- if (valid && st.st_mtime > inc.mtime) return true;
-
- return false;
-}
diff --git a/src/module.h b/src/module.h
index 96e5649..682e65b 100644
--- a/src/module.h
+++ b/src/module.h
@@ -78,12 +78,6 @@ public:
LocalScope scope;
};
-struct IncludeFile {
- std::string filename;
- bool valid;
- time_t mtime;
-};
-
// FIXME: A FileModule doesn't have definition arguments, so we shouldn't really
// inherit from a Module
class FileModule : public Module
@@ -95,15 +89,28 @@ public:
void setModulePath(const std::string &path) { this->path = path; }
const std::string &modulePath() const { return this->path; }
void registerInclude(const std::string &localpath, const std::string &fullpath);
+ bool includesChanged() const;
bool handleDependencies();
virtual AbstractNode *instantiate(const Context *ctx, const ModuleInstantiation *inst, const EvalContext *evalctx = NULL) const;
+ bool hasIncludes() const { return !this->includes.empty(); }
+ bool usesLibraries() const { return !this->usedlibs.empty(); }
+
+
typedef boost::unordered_map<std::string, class FileModule*> ModuleContainer;
ModuleContainer usedlibs;
+private:
+ struct IncludeFile {
+ std::string filename;
+ bool valid;
+ time_t mtime;
+ };
+
+ bool include_modified(const IncludeFile &inc) const;
+
typedef boost::unordered_map<std::string, struct IncludeFile> IncludeContainer;
IncludeContainer includes;
- bool include_modified(struct IncludeFile inc);
-private:
+
bool is_handling_dependencies;
std::string path;
};
diff --git a/src/parsersettings.cc b/src/parsersettings.cc
index 8db33a8..04c20ed 100644
--- a/src/parsersettings.cc
+++ b/src/parsersettings.cc
@@ -30,23 +30,24 @@ fs::path search_libs(const fs::path &localpath)
}
// files must be 'ordinary' - they must exist and be non-directories
+// FIXME: Don't print anything from this function as it's called repeatedly
+// for the automatic reload function (FileModule::include_modified())
static bool check_valid(const fs::path &p, const std::vector<std::string> *openfilenames)
{
if (p.empty()) {
- PRINTB("WARNING: File path is blank: %s",p);
+ //PRINTB("WARNING: File path is blank: %s",p);
return false;
}
if (!p.has_parent_path()) {
- PRINTB("WARNING: No parent path: %s",p);
+ //PRINTB("WARNING: No parent path: %s",p);
return false;
}
if (!fs::exists(p)) {
- PRINTB("WARNING: File not found: %s",p);
- // searched ===
+ //PRINTB("WARNING: File not found: %s",p);
return false;
}
if (fs::is_directory(p)) {
- PRINTB("WARNING: %s invalid - points to a directory",p);
+ //PRINTB("WARNING: %s invalid - points to a directory",p);
return false;
}
std::string fullname = boosty::stringy(p);
@@ -54,7 +55,7 @@ static bool check_valid(const fs::path &p, const std::vector<std::string> *openf
if (openfilenames) {
BOOST_FOREACH(const std::string &s, *openfilenames) {
if (s == fullname) {
- PRINTB("WARNING: circular include file %s", fullname);
+ //PRINTB("WARNING: circular include file %s", fullname);
return false;
}
}
diff --git a/testdata/modulecache-tests/README.txt b/testdata/modulecache-tests/README.txt
index ce341ca..214acc5 100644
--- a/testdata/modulecache-tests/README.txt
+++ b/testdata/modulecache-tests/README.txt
@@ -26,13 +26,6 @@ o Open use-mcad.scad
o Compile (F5)
o Check that you get a rounded box
-Test4: USE Non-existing file
-------
-
-o Open usenonexsistingfile.scad
-o Compile (F5)
-o Verify that you get: WARNING: Can't open 'use' file 'nofile.scad'.
-
Test5: Overload USEd module
------
@@ -84,10 +77,13 @@ Test11: Missing include file appears
o rm missing.scad
o Open includemissing.scad
o Compile (F5)
-o Verify that you get: WARNING: Can't open 'use' file 'missing.scad'.
+o Verify that you get: WARNING: Can't open include file 'missing.scad'.
o echo "module missing() { sphere(10); }" > missing.scad
+o Reload and Compile (F4) - verify that the sphere appeared
o rm missing.scad
-o Reload and Compile (F4) - verify that the sphere is gone
+o Reload and Compile (F4) - verify that the sphere is still there
+o echo "module missing() { sphere(20); }" > missing.scad
+o Reload and Compile (F4) - verify that the sphere increased in size
Test12: Missing include file in subpath appears
------
@@ -96,15 +92,35 @@ o Open includemissingsub.scad
o Compile (F5)
o Verify that you get: WARNING: Can't open include file 'subdir/missingsub.scad'.
o echo "module missingsub() { sphere(10); }" > subdir/missingsub.scad
+o Reload and Compile (F4) - verify that the sphere appeared
o rm subdir/missingsub.scad
-o Reload and Compile (F4) - verify that the sphere is gone
+o Reload and Compile (F4) - verify that the sphere is still there
+o echo "module missingsub() { sphere(20); }" > subdir/missingsub.scad
+o Reload and Compile (F4) - verify that the sphere increased in size
Test13: Missing library file appears
-------
o rm missing.scad
o Open usemissing.scad
o Compile (F5)
-o Verify that you get: WARNING: Can't open 'use' file 'missing.scad'.
+o Verify that you get: WARNING: Can't open library file 'missing.scad'.
o echo "module missing() { sphere(10); }" > missing.scad
+o Reload and Compile (F4) - verify that the sphere appeared
o rm missing.scad
-o Compile (F5) - verify that the sphere is gone
+o Reload and Compile (F4) - verify that the sphere is still there
+o echo "module missing() { sphere(20); }" > missing.scad
+o Reload and Compile (F4) - verify that the sphere increased in size
+
+Test14: Automatic reload of cascading changes
+-------
+
+o ./cascade.sh
+o Open cascadetest.scad
+o Turn on Automatic Reload and Compile
+o Verify that the 4 objects render correctly
+o rm cascadetest.scad
+o Verify that no rerendering was triggered (the 4 objects are still there)
+o rm cascade*.scad
+o Verify that no rerendering was triggered (the 4 objects are still there)
+o ./cascade2.sh
+o Verify that everything reloads at once without flickering
diff --git a/testdata/modulecache-tests/cascade.sh b/testdata/modulecache-tests/cascade.sh
new file mode 100755
index 0000000..5dd0ef7
--- /dev/null
+++ b/testdata/modulecache-tests/cascade.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+rm cascade*.scad
+echo "include <cascade-A.scad> include <cascade-B.scad> use <cascade-C.scad> use <cascade-D.scad> A(); translate([11,0,0]) B(); translate([22,0,0]) C(); translate([33,0,0]) D();" > cascadetest.scad
+sleep 0.05
+echo "module A() { sphere(5); }" > cascade-A.scad
+sleep 0.05
+echo "module B() { cube([8,8,8], center=true); }" > cascade-B.scad
+sleep 0.05
+echo "module C() { cylinder(h=8, r=5, center=true); }" > cascade-C.scad
+sleep 0.05
+echo "module D() { cylinder(h=10, r1=5, r2=0, center=true); }" > cascade-D.scad
diff --git a/testdata/modulecache-tests/cascade2.sh b/testdata/modulecache-tests/cascade2.sh
new file mode 100755
index 0000000..50f98e0
--- /dev/null
+++ b/testdata/modulecache-tests/cascade2.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+rm cascade-*.scad
+echo "include <cascade-A.scad> include <cascade-B.scad> use <cascade-C.scad> use <cascade-D.scad> A(); translate([11,0,0]) B(); translate([22,0,0]) C(); translate([33,0,0]) D();" > cascadetest.scad
+sleep 0.1
+echo "module A() { sphere(6); }" > cascade-A.scad
+sleep 0.1
+echo "module B() { cube([10,10,10], center=true); }" > cascade-B.scad
+sleep 0.1
+echo "module C() { cylinder(h=10, r=6, center=true); }" > cascade-C.scad
+sleep 0.1
+echo "module D() { cylinder(h=12, r1=6, r2=0, center=true); }" > cascade-D.scad
contact: Jan Huwald // Impressum