aboutsummaryrefslogtreecommitdiff
path: root/build2/depdb
blob: effdc920535449d873ad57f676a62cec36aa9135 (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
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