aboutsummaryrefslogtreecommitdiff
path: root/libbuild2/target-type.hxx
blob: 0e24e3a767db6ccb0b524435f71b86a7937cd0d4 (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
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
// file      : libbuild2/target-type.hxx -*- C++ -*-
// license   : MIT; see accompanying LICENSE file

#ifndef LIBBUILD2_TARGET_TYPE_HXX
#define LIBBUILD2_TARGET_TYPE_HXX

#include <libbuild2/types.hxx>
#include <libbuild2/forward.hxx>
#include <libbuild2/utility.hxx>

#include <libbuild2/export.hxx>

namespace build2
{
  // Target type.
  //
  // Note that we assume there is always a single instance of this class for
  // any target type. As a result, we can use address comparison to determine
  // if two target types are the same.
  //
  // If the extension derivation functions are NULL, then it means this target
  // type does not use extensions. Note that this is relied upon when deciding
  // whether to print the extension.
  //
  // If the fixed extension function is specified, then it means that this
  // target type has a fixed extension (including the no-extension case) and
  // this function should return such a fixed extension (which, if overriding
  // by the user is allowed, can point to the key's ext member; note that for
  // performance reasons we currently only verify the explicitly specified
  // extension on target insersion -- see target_key comparison for details).
  // It is called eraly, during the target insertion, in contrast to the
  // default extension function described below (you would specify one or the
  // other).
  //
  // Note that the fixed no-extension case that allows overriding by the user
  // is used to implement the "if extension is not specified by the user then
  // there is no extension" semantics of the file{} and similar target types.
  // For such cases the target_extension_none() function should be used (we
  // compare to it's address to detect target types with such semantics).
  // Similarly, for cases where the user must specify the extension explicitly
  // (e.g., man{}), use target_extension_must().
  //
  // The root scope argument to the fixed extension function may be NULL which
  // means the root scope is not known. A target type that relies on this must
  // be prepared to resolve the root scope itself and handle the cases where
  // the target is not (yet) in any project (this is currently only used to
  // handle the alternative build file/directory naming scheme and hopefully
  // it will stay that way).
  //
  // The default extension is used in two key (there are others) places:
  // search_existing_file() (called for a prerequisite with the last argument
  // true) and in target::derive_extension() (called for a target with the
  // last argument false); see their respective implementations for details.
  // The third argument is the default extension that is supplied (e.g., by a
  // rule) to derive_extension(), if any. The implementation can decide which
  // takes precedence, etc (see the exe{} target type for some interesting
  // logic). If the default extension function returns NULL, then it means the
  // default extension for this target could not be derived.
  //
  // If the pattern function is not NULL, then it is used to amend a pattern
  // or match (reverse is false) and then, if the amendment call returned
  // true, to reverse it in the resulting matches. The pattern function for a
  // non-directory target must first call target::split_name() if reverse is
  // false.
  //
  struct LIBBUILD2_SYMEXPORT target_type
  {
    const char* name;
    const target_type* base;

    target* (*factory) (context&,
                        const target_type&,
                        dir_path,
                        dir_path,
                        string);

    const char*      (*fixed_extension)   (const target_key&,
                                           const scope* root);

    optional<string> (*default_extension) (const target_key&,
                                           const scope& base,
                                           const char*,
                                           bool search);

    bool (*pattern) (const target_type&,
                     const scope& base,
                     string& name,
                     optional<string>& extension,
                     const location&,
                     bool reverse);

    void (*print) (ostream&, const target_key&);

    const target* (*search) (const target&, const prerequisite_key&);

    bool see_through; // A group with the default "see through" semantics.

    template <typename T>
    bool
    is_a () const {return is_a (T::static_type);}

    bool
    is_a (const target_type& tt) const
    {
      return this == &tt || (base != nullptr && is_a_base (tt));
    }

    bool
    is_a (const char*) const; // Defined in target.cxx

    bool
    is_a_base (const target_type&) const; // Defined in target.cxx
  };

  inline bool
  operator< (const target_type& x, const target_type& y) {return &x < &y;}

  inline bool
  operator== (const target_type& x, const target_type& y) {return &x == &y;}

  inline bool
  operator!= (const target_type& x, const target_type& y) {return &x != &y;}

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

  // Target type map.
  //
  class target_type_map
  {
  public:
    // Target type name to target type mapping.
    //
    const target_type*
    find (const string& n) const
    {
      auto i (type_map_.find (n));
      return i != type_map_.end () ? &i->second.get () : nullptr;
    }

    bool
    empty () const
    {
      return type_map_.empty ();
    }

    const target_type&
    insert (const target_type& tt)
    {
      type_map_.emplace (tt.name, target_type_ref (tt));
      return tt;
    }

    template <typename T>
    const target_type&
    insert ()
    {
      return insert (T::static_type);
    }

    pair<reference_wrapper<const target_type>, bool>
    insert (const string& n, unique_ptr<target_type>&& tt)
    {
      target_type& rtt (*tt); // Save a non-const reference to the object.

      auto p (type_map_.emplace (n, target_type_ref (move (tt))));

      // Patch the alias name to use the map's key storage.
      //
      if (p.second)
        rtt.name = p.first->first.c_str ();

      return pair<reference_wrapper<const target_type>, bool> (
        p.first->second.get (), p.second);
    }

    // File name to target type mapping.
    //
    const target_type*
    find_file (const string& n) const
    {
      auto i (file_map_.find (n));
      return i != file_map_.end () ? &i->second.get () : nullptr;
    }

    void
    insert_file (const string& n, const target_type& tt)
    {
      file_map_.emplace (n, tt);
    }

  public:
    struct target_type_ref
    {
      // Like reference_wrapper except it sometimes deletes the target type.
      //
      explicit
      target_type_ref (const target_type& r): p_ (&r), d_ (false) {}

      explicit
      target_type_ref (unique_ptr<target_type>&& p)
          : p_ (p.release ()), d_ (true) {}

      target_type_ref (target_type_ref&& r)
          : p_ (r.p_), d_ (r.d_) {r.p_ = nullptr;}

      ~target_type_ref () {if (p_ != nullptr && d_) delete p_;}

      explicit operator const target_type& () const {return *p_;}
      const target_type& get () const {return *p_;}

    private:
      const target_type* p_;
      bool d_;
    };

    using type_map = map<string, target_type_ref>;
    using file_map = map<string, reference_wrapper<const target_type>>;

    using type_iterator = type_map::const_iterator;

    type_iterator type_begin () const {return type_map_.begin ();}
    type_iterator type_end ()   const {return type_map_.end ();}

  private:
    type_map type_map_;
    file_map file_map_;
  };
}

#endif // LIBBUILD2_TARGET_TYPE_HXX