summaryrefslogtreecommitdiff
path: root/src/ModuleCache.cc
blob: d79a02611b78a933e917f748481008e118222dcc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
#include "ModuleCache.h"
#include "module.h"
#include "printutils.h"
#include "openscad.h"

#include "boosty.h"
#include <boost/format.hpp>
#include <boost/filesystem.hpp>
#include <boost/foreach.hpp>

#include <stdio.h>
#include <fstream>
#include <sstream>
#include <time.h>
#include <sys/stat.h>

//#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 all it's dependencies and recompile anything
	needing reevaluation.
	The given filename must be absolute.

	Sets the module reference to the new modile, or NULL on any error (e.g. compile
	error or file not found)

	Returns true if anything was compiled (module or dependencies) and false otherwise.
*/
bool ModuleCache::evaluate(const std::string &filename, FileModule *&module)
{
	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<FileModule*>(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();
	}
	
	module = lib_mod;
	bool depschanged = lib_mod ? lib_mod->handleDependencies() : false;

	return shouldCompile || depschanged;
}

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();
}

contact: Jan Huwald // Impressum