summaryrefslogtreecommitdiff
path: root/src/ModuleCache.cc
blob: 505015efc8fe7c4d015d6cce05eeb036f3ade1d4 (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
#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 recompile if necessary.
	Returns NULL on any error (e.g. compile error or file not found)

	If the given filename is relative, it means that the module hasn't been
	previously located.
*/
FileModule *ModuleCache::evaluate(const std::string &filename)
{
	FileModule *lib_mod = NULL;

	// Create cache ID
	struct stat st;
	memset(&st, 0, sizeof(struct stat));
	stat(filename.c_str(), &st);

	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
		lib_mod = &(*this->entries[filename].module);

		BOOST_FOREACH(const FileModule::IncludeContainer::value_type &include, lib_mod->includes) {
			if (lib_mod->include_modified(include.second)) {
				lib_mod = NULL;
				break;
			}
		}
	}

	// If cache lookup failed (non-existing or old timestamp), compile module
	if (!lib_mod) {
#ifdef DEBUG
		if (this->entries.find(filename) != this->entries.end()) {
			PRINTB("Recompiling cached library: %s (%s)", filename % cache_id);
		}
		else {
			PRINTB("Compiling library '%s'.", filename);
		}
#endif

		std::ifstream ifs(filename.c_str());
		if (!ifs.is_open()) {
			PRINTB("WARNING: Can't open library file '%s'\n", filename);
			return NULL;
		}
		std::stringstream textbuf;
		textbuf << ifs.rdbuf();
		textbuf << "\n" << commandline_commands;

		print_messages_push();

		FileModule *oldmodule = NULL;
		cache_entry e = { NULL, cache_id };
		if (this->entries.find(filename) != this->entries.end()) {
			oldmodule = this->entries[filename].module;
		}
		this->entries[filename] = e;
		
		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);
		
		if (lib_mod) {
			// We defer deletion so we can ensure that the new module won't
			// have the same address as the old
			delete oldmodule;
			this->entries[filename].module = lib_mod;
		} else {
			this->entries.erase(filename);
		}
		
		print_messages_pop();
	}

	if (lib_mod) lib_mod->handleDependencies();

	return lib_mod;
}

void ModuleCache::clear()
{
	this->entries.clear();
}

contact: Jan Huwald // Impressum