aboutsummaryrefslogtreecommitdiff
path: root/build/target
blob: f57e8ccc91b078380cd02b82935b0ee43b3784e5 (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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
// file      : build/target -*- C++ -*-
// copyright : Copyright (c) 2014-2015 Code Synthesis Tools CC
// license   : MIT; see accompanying LICENSE file

#ifndef BUILD_TARGET
#define BUILD_TARGET

#include <set>
#include <map>
#include <string>
#include <vector>
#include <memory>     // unique_ptr
#include <functional> // function, reference_wrapper
#include <typeindex>
#include <ostream>
#include <cassert>
#include <utility>    // move

#include <build/path>
#include <build/timestamp>
#include <build/prerequisite>
#include <build/utility>       // compare_*, extension_pool

namespace build
{
  class target;

  enum class target_state {unknown, uptodate, updated, failed};
  typedef std::function<target_state (target&)> recipe;

  struct target_type
  {
    std::type_index id;
    const char* name;
    const target_type* base;
    target* (*const factory) (path, std::string, const std::string*);
  };

  inline std::ostream&
  operator<< (std::ostream& os, const target_type& tt)
  {
    return os << tt.name;
  }

  class target
  {
  public:
    target (path d, std::string n, const std::string* e)
        : directory (std::move (d)), name (std::move (n)), ext (e) {}

    const path directory;   // Absolute and normalized.
    const std::string name;
    const std::string* ext; // Extension, NULL means unspecified.

  public:
    typedef
    std::vector<std::reference_wrapper<prerequisite>>
    prerequisites_type;

    prerequisites_type prerequisites;

  public:
    typedef build::recipe recipe_type;

    const recipe_type&
    recipe () const {return recipe_;}

    void
    recipe (recipe_type r) {assert (!recipe_); recipe_ = r;}

  public:
    target_state
    state () const {return state_;}

    void
    state (target_state s) {state_ = s;}

  private:
    target (const target&) = delete;
    target& operator= (const target&) = delete;

  public:
    virtual const target_type& type () const = 0;
    static const target_type static_type;

  private:
    recipe_type recipe_;
    target_state state_ {target_state::unknown};
  };

  std::ostream&
  operator<< (std::ostream&, const target&);

  inline bool
  operator< (const target& x, const target& y)
  {
    std::type_index tx (typeid (x)), ty (typeid (y));

    //@@ TODO: use compare() to compare once.

    // Unspecified and specified extension are assumed equal. The
    // extension strings are from the pool, so we can just compare
    // pointers.
    //
    return
      (tx < ty) ||
      (tx == ty && x.name < y.name) ||
      (tx == ty && x.name == y.name && x.directory < y.directory) ||
      (tx == ty && x.name == y.name && x.directory == y.directory &&
       x.ext != nullptr && y.ext != nullptr && x.ext < y.ext);
  }

  struct target_set: std::set<std::unique_ptr<target>, compare_pointer_target>
  {
    std::pair<target&, bool>
    insert (const target_type&,
            path dir,
            std::string name,
            const std::string* ext,
            tracer&);
  };

  extern target_set targets;
  extern target* default_target;

  class target_type_map: public std::map<
    const char*,
    std::reference_wrapper<const target_type>,
    compare_c_string>
  {
  public:
    void
    insert (const target_type& tt) {emplace (tt.name, tt);}
  };

  extern target_type_map target_types;

  template <typename T>
  target*
  target_factory (path d, std::string n, const std::string* e)
  {
    return new T (std::move (d), std::move (n), e);
  }

  // Modification time-based target.
  //
  class mtime_target: public target
  {
  public:
    using target::target;

    timestamp
    mtime () const
    {
      if (mtime_ == timestamp_unknown)
        mtime_ = load_mtime ();

      return mtime_;
    }

    void
    mtime (timestamp mt) {mtime_ = mt;}

  protected:
    virtual timestamp
    load_mtime () const = 0;

  public:
    static const target_type static_type;

  private:
    mutable timestamp mtime_ {timestamp_unknown};
  };

  // Filesystem path-bases target.
  //
  class path_target: public mtime_target
  {
  public:
    using mtime_target::mtime_target;

    typedef build::path path_type;

    const path_type&
    path () const {return path_;}

    void
    path (path_type p) {assert (path_.empty ()); path_ = std::move (p);}

  protected:
    virtual timestamp
    load_mtime () const;

  public:
    static const target_type static_type;

  private:
    path_type path_;
  };

  // File target.
  //
  class file: public path_target
  {
  public:
    using path_target::path_target;

  public:
    virtual const target_type& type () const {return static_type;}
    static const target_type static_type;
  };
}

#endif // BUILD_TARGET