diff options
| -rw-r--r-- | src/MainWindow.h | 1 | ||||
| -rw-r--r-- | src/ModuleCache.cc | 12 | ||||
| -rw-r--r-- | src/mainwin.cc | 21 | ||||
| -rw-r--r-- | src/module.cc | 62 | ||||
| -rw-r--r-- | src/module.h | 23 | ||||
| -rw-r--r-- | testdata/modulecache-tests/README.txt | 30 | ||||
| -rwxr-xr-x | testdata/modulecache-tests/cascade2.sh | 11 | 
7 files changed, 94 insertions, 66 deletions
| diff --git a/src/MainWindow.h b/src/MainWindow.h index fc64137..79e2080 100644 --- a/src/MainWindow.h +++ b/src/MainWindow.h @@ -77,7 +77,6 @@ private:  	void refreshDocument();  	void updateTemporalVariables();  	bool fileChangedOnDisk(); -	bool includesChanged();  	void compileTopLevelDocument();  	void compile(bool reload, bool forcedone = false);  	void compileCSG(bool procevents); diff --git a/src/ModuleCache.cc b/src/ModuleCache.cc index 0e443e8..5ebd549 100644 --- a/src/ModuleCache.cc +++ b/src/ModuleCache.cc @@ -38,6 +38,9 @@ FileModule *ModuleCache::evaluate(const std::string &filename)  	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; +  	std::string cache_id = str(boost::format("%x.%x") % st.st_mtime % st.st_size);  	// Lookup in cache @@ -46,12 +49,9 @@ FileModule *ModuleCache::evaluate(const std::string &filename)  		if (this->entries[filename].cache_id == cache_id) {  			shouldCompile = false; -			BOOST_FOREACH(const FileModule::IncludeContainer::value_type &include, lib_mod->includes) { -				if (lib_mod->include_modified(include.second)) { -					lib_mod = NULL; -					shouldCompile = true; -					break; -				} +			if (lib_mod->includesChanged()) { +				lib_mod = NULL; +				shouldCompile = true;  			}  		}  	} diff --git a/src/mainwin.cc b/src/mainwin.cc index 52b432c..9652d6c 100644 --- a/src/mainwin.cc +++ b/src/mainwin.cc @@ -636,11 +636,12 @@ void MainWindow::compile(bool reload, bool forcedone)  		shouldcompiletoplevel = true;  	} -	if (!shouldcompiletoplevel && includesChanged()) { +	if (!shouldcompiletoplevel && this->root_module && this->root_module->includesChanged()) {  		shouldcompiletoplevel = true;  	}  	if (shouldcompiletoplevel) { +		console->clear();  		compileTopLevelDocument();  		didcompile = true;  	} @@ -655,8 +656,8 @@ void MainWindow::compile(bool reload, bool forcedone)  	// 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->includes.size() > 0 || -				this->root_module->usedlibs.size() > 0) { +		if (this->root_module->hasIncludes() || +				this->root_module->usesLibraries()) {  			this->waitAfterReloadTimer->start();  			return;  		} @@ -1116,23 +1117,11 @@ 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; -} -  /*!  	Returns true if anything was compiled.  */  void MainWindow::compileTopLevelDocument()  { -	console->clear(); -	  	updateTemporalVariables();  	this->last_compiled_doc = editor->toPlainText(); @@ -1233,7 +1222,6 @@ void MainWindow::actionRenderCSG()  	GuiLocker::lock();  	autoReloadTimer->stop();  	setCurrentOutput(); -	console->clear();  	PRINT("Parsing design (AST generation)...");  	QApplication::processEvents(); @@ -1278,7 +1266,6 @@ void MainWindow::actionRenderCGAL()  	GuiLocker::lock();  	autoReloadTimer->stop();  	setCurrentOutput(); -	console->clear();  	PRINT("Parsing design (AST generation)...");  	QApplication::processEvents(); diff --git a/src/module.cc b/src/module.cc index 080a3dd..6cd8322 100644 --- a/src/module.cc +++ b/src/module.cc @@ -207,6 +207,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. @@ -217,12 +239,13 @@ 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++; @@ -237,16 +260,27 @@ bool FileModule::handleDependencies()  			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 && !wasmissing) { -			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; @@ -269,17 +303,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 5dfb8c4..ad2a46c 100644 --- a/src/module.h +++ b/src/module.h @@ -77,12 +77,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 @@ -94,15 +88,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/testdata/modulecache-tests/README.txt b/testdata/modulecache-tests/README.txt index a46e3d3..c38822f 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  ------ @@ -86,8 +79,11 @@ o Open includemissing.scad  o Compile (F5)  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,25 +92,33 @@ 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 rm cascade-*.scad +o ./cascade.sh  o Open cascadetest.scad  o Turn on Automatic Reload and Compile  o Verify that the 4 objects render correctly -o ./cascade.sh +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/cascade2.sh b/testdata/modulecache-tests/cascade2.sh new file mode 100755 index 0000000..aa17c6c --- /dev/null +++ b/testdata/modulecache-tests/cascade2.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +rm cascade-*.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 | 
