--- /dev/null
+#include <msp/core/maputils.h>
+#include <msp/fs/stat.h>
+#include <msp/io/file.h>
+#include <msp/io/print.h>
+#include <msp/strings/utils.h>
+#include "builder.h"
+#include "cache.h"
+#include "sourcepackage.h"
+#include "target.h"
+
+using namespace std;
+using namespace Msp;
+
+namespace {
+
+unsigned read_count(IO::Base &in)
+{
+ unsigned char head = in.get();
+ if((head&0xC0)==0xC0)
+ {
+ unsigned char tail = in.get();
+ return (head&0x3F)<<8 | tail;
+ }
+ else
+ return head;
+}
+
+string read_string(IO::Base &in)
+{
+ unsigned len = read_count(in);
+ char buf[0x4000];
+ len = in.read(buf, len);
+ return string(buf, len);
+}
+
+void write_count(IO::Base &out, unsigned count)
+{
+ if(count<0xC0)
+ out.put(count);
+ else if(count<0x4000)
+ {
+ out.put(0xC0 | (count>>8));
+ out.put(count&0xFF);
+ }
+ else
+ throw invalid_argument("write_count");
+}
+
+void write_string(IO::Base &out, const string &str)
+{
+ write_count(out, str.size());
+ out.write(str);
+}
+
+}
+
+
+Cache::Cache(SourcePackage &p):
+ package(p),
+ filename(package.get_temp_dir()/"../cache"),
+ changed(false)
+{ }
+
+void Cache::set_value(const Target *tgt, const string &k, const string &v)
+{
+ ValueList vl;
+ vl.push_back(v);
+ set_values(tgt, k, vl);
+}
+
+void Cache::append_value(const Target *tgt, const string &k, const string &v)
+{
+ Key key(tgt->get_name(), k);
+ DataMap::iterator i = data.find(key);
+ if(i==data.end())
+ i = data.insert(DataMap::value_type(key, ValueList())).first;
+ i->second.push_back(v);
+ changed = true;
+}
+
+void Cache::set_values(const Target *tgt, const string &k, const ValueList &v)
+{
+ data[Key(tgt->get_name(), k)] = v;
+ changed = true;
+}
+
+const string &Cache::get_value(const Target *tgt, const string &k)
+{
+ const ValueList &values = get_values(tgt, k);
+ if(values.empty())
+ throw logic_error("values.empty()");
+ return values.front();
+}
+
+const Cache::ValueList &Cache::get_values(const Target *tgt, const string &k)
+{
+ return get_item(data, Key(tgt->get_name(), k));
+}
+
+bool Cache::has_key(const Target *tgt, const string &k)
+{
+ return data.count(Key(tgt->get_name(), k));
+}
+
+void Cache::load()
+{
+ if(FS::Stat st = FS::stat(filename))
+ {
+ package.get_builder().get_logger().log("files", format("Reading %s", filename));
+ IO::BufferedFile in(filename.str());
+
+ while(!in.eof())
+ {
+ Key key;
+ key.first = read_string(in);
+ key.second = read_string(in);
+ if(key.first.empty() || key.second.empty())
+ break;
+ ValueList &values = data[key];
+ for(unsigned count = read_count(in); count; --count)
+ values.push_back(read_string(in));
+ }
+
+ mtime = st.get_modify_time();
+ }
+}
+
+void Cache::save() const
+{
+ if(data.empty() || !changed)
+ return;
+
+ package.get_builder().get_logger().log("files", format("Writing %s", filename));
+ IO::BufferedFile out(filename.str(), IO::M_WRITE);
+
+ for(DataMap::const_iterator i=data.begin(); i!=data.end(); ++i)
+ {
+ write_string(out, i->first.first);
+ write_string(out, i->first.second);
+ write_count(out, i->second.size());
+ for(ValueList::const_iterator j=i->second.begin(); j!=i->second.end(); ++j)
+ write_string(out, *j);
+ }
+}
--- /dev/null
+#ifndef CACHE_H_
+#define CACHE_H_
+
+#include <msp/time/timestamp.h>
+#include "misc.h"
+
+class SourcePackage;
+class Target;
+
+/**
+Stores data between build runs. This can be used to avoid scanning files again
+every time builder is run, or to detect outside changes.
+
+Data is stored as lists of strings and keyed to target and an arbitrary
+identifier. Any kind of strings can be stored, even ones that contain
+unprintable characters or nuls.
+*/
+class Cache
+{
+public:
+ typedef std::list<std::string> ValueList;
+private:
+ typedef std::pair<std::string, std::string> Key;
+ typedef std::map<Key, ValueList> DataMap;
+
+ SourcePackage &package;
+ Msp::FS::Path filename;
+ DataMap data;
+ Msp::Time::TimeStamp mtime;
+ bool changed;
+
+public:
+ Cache(SourcePackage &p);
+
+ /// Sets a key to a single value, replacing any existing values.
+ void set_value(const Target *, const std::string &, const std::string &);
+
+ /// Appends a value to a key. If the key does not exist, it is created.
+ void append_value(const Target *, const std::string &, const std::string &);
+
+ /// Sets a key to a list of values, replacing any existing values.
+ void set_values(const Target *, const std::string &, const ValueList &);
+
+ /** Returns the first value from a key. The key must exist and be
+ non-empty. */
+ const std::string &get_value(const Target *, const std::string &);
+
+ /// Returns the values from a key. The key must exist.
+ const ValueList &get_values(const Target *, const std::string &);
+
+ /// Indicates whether a key exists.
+ bool has_key(const Target *, const std::string &);
+
+ /// Returns the last modification timestamp of the cache.
+ const Msp::Time::TimeStamp &get_mtime() const { return mtime; }
+
+ /** Loads the cache file and sets the last modification timestamp
+ accordingly. */
+ void load();
+
+ /** Saves the cache. Does nothing if there is no data to save or nothing
+ has changed. */
+ void save() const;
+};
+
+#endif
return;
const SourcePackage &spkg = component->get_package();
- string deps_key = FS::relative(path, spkg.get_source_directory()).str();
- DependencyCache &deps_cache = spkg.get_dependency_cache();
- if(mtime<deps_cache.get_mtime() && deps_cache.has_deps(deps_key))
- includes = deps_cache.get_deps(deps_key);
+ Cache &cache = spkg.get_cache();
+ if(mtime<cache.get_mtime() && cache.has_key(this, "includes"))
+ includes = cache.get_values(this, "includes");
else
{
IO::BufferedFile in(path.str());
if(RegMatch match = r_include.match(line))
includes.push_back(match[1].str);
- deps_cache.set_deps(deps_key, includes);
+ cache.set_values(this, "includes", includes);
}
const BuildInfo::PathList &incpath = component->get_build_info().incpath;
+++ /dev/null
-#include <msp/core/maputils.h>
-#include <msp/fs/stat.h>
-#include <msp/io/file.h>
-#include <msp/io/print.h>
-#include <msp/strings/utils.h>
-#include "builder.h"
-#include "dependencycache.h"
-#include "sourcepackage.h"
-
-using namespace std;
-using namespace Msp;
-
-DependencyCache::DependencyCache(SourcePackage &p):
- package(p),
- changed(false)
-{ }
-
-void DependencyCache::set_deps(const string &tgt, const StringList &d)
-{
- deps[tgt] = d;
- changed = true;
-}
-
-bool DependencyCache::has_deps(const string &tgt) const
-{
- return deps.count(tgt);
-}
-
-const StringList &DependencyCache::get_deps(const string &tgt) const
-{
- return get_item(deps, tgt);
-}
-
-void DependencyCache::save() const
-{
- if(deps.empty() || !changed)
- return;
-
- FS::Path fn = package.get_source_directory()/".deps";
- package.get_builder().get_logger().log("files", format("Writing %s", fn));
- IO::BufferedFile out(fn.str(), IO::M_WRITE);
-
- for(DepsMap::const_iterator i=deps.begin(); i!=deps.end(); ++i)
- {
- IO::print(out, i->first);
- for(StringList::const_iterator j=i->second.begin(); j!=i->second.end(); ++j)
- IO::print(out, "|%s", *j);
- IO::print(out, "\n");
- }
-}
-
-void DependencyCache::load()
-{
- FS::Path fn = package.get_source_directory()/".deps";
-
- if(FS::Stat st = FS::stat(fn))
- {
- package.get_builder().get_logger().log("files", format("Reading %s", fn));
- IO::BufferedFile in(fn.str());
-
- string line;
- while(in.getline(line))
- {
- vector<string> parts = split(line, '|');
- deps[parts[0]] = StringList(parts.begin()+1, parts.end());
- }
-
- mtime = st.get_modify_time();
- }
-}
+++ /dev/null
-#ifndef DEPENDENCYCACHE_H_
-#define DEPENDENCYCACHE_H_
-
-#include <msp/time/timestamp.h>
-#include "misc.h"
-
-class Package;
-
-/**
-Stores dependencies to avoid expensive filesystem operations during DAG
-building phase. The dependencies are stored in a map with target name as key
-and a list of strings as value. The targets are free to store whatever they
-want here.
-*/
-class DependencyCache
-{
-private:
- typedef std::map<std::string, StringList> DepsMap;
-
- SourcePackage &package;
- DepsMap deps;
- Msp::Time::TimeStamp mtime;
- bool changed;
-
-public:
- DependencyCache(SourcePackage &p);
-
- void set_deps(const std::string &tgt, const StringList &d);
- bool has_deps(const std::string &tgt) const;
- const StringList &get_deps(const std::string &tgt) const;
- const Msp::Time::TimeStamp &get_mtime() const { return mtime; }
- void load();
-
- /** Saves the depencency cache. Does nothing if the cache is empty or
- nothing has changed. */
- void save() const;
-};
-
-#endif
source_dir(FS::dirname(f)),
build_type(0),
config(*this),
- deps_cache(*this)
+ cache(*this)
{
config.load();
void SourcePackage::create_targets()
{
- deps_cache.load();
+ cache.load();
bool pc_needed = false;
for(ComponentList::const_iterator i=components.begin(); i!=components.end(); ++i)
void SourcePackage::save_caches()
{
config.save();
- deps_cache.save();
+ cache.save();
}
#include <stdexcept>
#include <string>
#include "buildinfo.h"
+#include "cache.h"
#include "component.h"
#include "condition.h"
#include "config.h"
-#include "dependencycache.h"
#include "feature.h"
#include "package.h"
BuildInfo build_info;
ComponentList components;
Config config;
- mutable DependencyCache deps_cache;
+ mutable Cache cache;
public:
SourcePackage(Builder &, const std::string &, const Msp::FS::Path &);
const BuildInfo &get_build_info() const { return build_info; }
Builder &get_builder() const { return builder; }
- DependencyCache &get_dependency_cache() const { return deps_cache; }
+ Cache &get_cache() const { return cache; }
private:
virtual void create_build_info();