aboutsummaryrefslogtreecommitdiff
path: root/build2/depdb
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2016-02-29 10:57:40 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2016-02-29 10:57:40 +0200
commit3cf3b73ffc6881d5428a735736a347f6e143b366 (patch)
tree3559fa9d2d44cc11e07987752027f7c2a9e3e23e /build2/depdb
parent2a4f52c46f2081aaeb2664e8026d3d067142e3d5 (diff)
Implement auxiliary dependency database (.d files), use in cxx.compile
This is part of the "High Fidelity Build" work.
Diffstat (limited to 'build2/depdb')
-rw-r--r--build2/depdb143
1 files changed, 143 insertions, 0 deletions
diff --git a/build2/depdb b/build2/depdb
new file mode 100644
index 0000000..effdc92
--- /dev/null
+++ b/build2/depdb
@@ -0,0 +1,143 @@
+// file : build2/depdb -*- C++ -*-
+// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#ifndef BUILD2_DEPDB
+#define BUILD2_DEPDB
+
+#include <fstream>
+#include <cstring> // strlen()
+
+#include <build2/types>
+#include <build2/utility>
+
+namespace build2
+{
+ // Auxiliary dependency database (those .d files).
+ //
+ // This is a strange beast: a line-oriented, streaming database that can, at
+ // some point, be switched from reading to (over)writing. The idea is to
+ // store auxiliary/ad-hoc dependency information in the "invalidation"
+ // order. That is, if an earlier line is out of date, then all the
+ // subsequent ones are out of date as well.
+ //
+ // As an example, consider a dependency database for foo.o which is built
+ // from foo.cxx by the cxx.compile rule. The first line could be the rule
+ // name itself (perhaps with the version). If a different rule is now
+ // building foo.o, then any dep info that was saved by cxx.compile is
+ // probably useless. Next we can have the command line options that were
+ // used to build foo.o. Then could come the source file name followed by the
+ // extracted header dependencies. If the compile options or the source file
+ // name have changed, then the header dependencies are likely to have
+ // changed as well.
+ //
+ // As an example, here is what our foo.o.d could look like (the first line
+ // is the database format version and the last '\0' character is the end
+ // marker):
+ //
+ // 1
+ // cxx.compile 1
+ // g++-4.8 -I/tmp/foo -O3
+ // /tmp/foo/foo.cxx
+ // /tmp/foo/foo.hxx
+ // /usr/include/string.h
+ // /usr/include/stdlib.h
+ // /tmp/foo/bar.hxx
+ // ^@
+ //
+ // Uses iostream failure and system_error exceptions to signal errors.
+ //
+ class depdb
+ {
+ public:
+ // Open the database for reading. Note that if the file does not exist,
+ // has wrong format version, or is corrupt, then the database will be
+ // immediately switched to writing.
+ //
+ depdb (const path&);
+
+ // Return the modification time of the database. This value only makes
+ // sense while reading (in the write mode it will be timestamp_unknown).
+ //
+ timestamp
+ mtime () const {return mtime_;}
+
+ // Update the database modification time in close() even if otherwise
+ // no modifications are necessary (i.e., the database is in the read
+ // mode and is at eof).
+ //
+ void
+ touch () {touch_ = true;}
+
+ // Close the database. Note that if this function is not called, then
+ // the database may be left in the old/currupt state.
+ //
+ void
+ close ();
+
+ // Read the next line. If the result is not NULL, then it is a pointer to
+ // the next line in the database (which you are free to move from). If you
+ // then call write(), this line will be overwritten.
+ //
+ // If the result is NULL, then it means no next line is available. This
+ // can be due to several reasons:
+ //
+ // - eof reached (you can detect this by calling more() before read())
+ // - database is already in the write mode
+ // - the next line (and the rest of the database are corrupt)
+ //
+ string*
+ read () {return state_ == state::write ? nullptr : read_ ();}
+
+ // Return true if the database is in the read mode and there is at least
+ // one more line available. Note that there is no guarantee that the line
+ // is not corrupt. In other words, read() can still return NULL, it just
+ // won't be because of eof.
+ //
+ bool
+ more () {return state_ == state::read;}
+
+ bool
+ reading () {return state_ != state::write;}
+
+ bool
+ writing () {return state_ == state::write;}
+
+ // Write the next line. Note that this switches the database into the
+ // write mode and no further reading will be possible.
+ //
+ void
+ write (const string& l) {write (l.c_str (), l.size ());}
+
+ void
+ write (const path& p) {write (p.string ());}
+
+ void
+ write (const char* s) {write (s, std::strlen (s));}
+
+ void
+ write (const char*, size_t);
+
+ void
+ write (char);
+
+ private:
+ void
+ change (bool flush = true);
+
+ string*
+ read_ ();
+
+ private:
+ timestamp mtime_;
+ std::fstream fs_;
+
+ std::fstream::pos_type pos_; // Start of the last returned line.
+ string line_;
+
+ enum class state {read, read_eof, write} state_;
+ bool touch_;
+ };
+}
+
+#endif // BUILD2_DEPDB