aboutsummaryrefslogtreecommitdiff
path: root/libbuild2/prerequisite.hxx
blob: f79ce04e5319b31d6edb1e6720bf8867e8d122fe (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
// file      : libbuild2/prerequisite.hxx -*- C++ -*-
// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd
// license   : MIT; see accompanying LICENSE file

#ifndef LIBBUILD2_PREREQUISITE_HXX
#define LIBBUILD2_PREREQUISITE_HXX

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

#include <libbuild2/action.hxx>
#include <libbuild2/variable.hxx>
#include <libbuild2/target-key.hxx>
#include <libbuild2/diagnostics.hxx>

#include <libbuild2/export.hxx>

namespace build2
{
  class scope;
  class target;

  // Light-weight (by being shallow-pointing) prerequisite key, similar
  // to (and based on) target key.
  //
  // Note that unlike prerequisite, the key is not (necessarily) owned by a
  // target. So for the key we instead have the base scope of the target that
  // (would) own it. Note that we assume keys to be ephemeral enough for the
  // base scope to remain unchanged.
  //
  class prerequisite_key
  {
  public:
    typedef build2::scope scope_type;

    const optional<project_name>& proj;
    target_key tk;                // The .dir and .out members can be relative.
    const scope_type* scope;      // Can be NULL if tk.dir is absolute.

    template <typename T>
    bool is_a () const {return tk.is_a<T> ();}
    bool is_a (const target_type& tt) const {return tk.is_a (tt);}
  };

  LIBBUILD2_SYMEXPORT ostream&
  operator<< (ostream&, const prerequisite_key&);

  // Note that every data member except for the target is immutable (const).
  //
  class LIBBUILD2_SYMEXPORT prerequisite
  {
  public:
    using scope_type = build2::scope;
    using target_type = build2::target;
    using target_type_type = build2::target_type;

    // Note that unlike targets, for prerequisites an empty out directory
    // means undetermined rather than being definitely in the out tree.
    //
    // It might seem natural to keep the reference to the owner target instead
    // of to the scope. But that's not the semantics that we have, consider:
    //
    // foo/obj{x}: bar/cxx{y}
    //
    // bar/ here is relative to the scope, not to foo/. Plus, bar/ can resolve
    // to either src or out.
    //
    const optional<project_name> proj;
    const target_type_type& type;
    const dir_path dir;         // Normalized absolute or relative (to scope).
    const dir_path out;         // Empty, normalized absolute, or relative.
    const string name;
    const optional<string> ext; // Absent if unspecified.
    const scope_type& scope;

    // NULL if not yet resolved. Note that this should always be the "primary
    // target", not a member of a target group.
    //
    // While normally only a matching rule should change this, if the
    // prerequisite comes from the group, then it's possible that several
    // rules will try to update it simultaneously. Thus the atomic.
    //
    mutable atomic<const target_type*> target {nullptr};

    // Prerequisite-specific variables.
    //
    // Note that the lookup is often ad hoc (see bin.whole as an example).
    // But see also parser::lookup_variable() if adding something here.
    //
  public:
    variable_map vars;

    // Return a value suitable for assignment. See target for details.
    //
    value&
    assign (const variable& var) {return vars.assign (var);}

    // Return a value suitable for appending. See target for details. Note
    // that we have to explicitly pass the target that this prerequisite
    // belongs to.
    //
    value&
    append (const variable&, const target_type&);

  public:
    prerequisite (optional<project_name> p,
                  const target_type_type& t,
                  dir_path d,
                  dir_path o,
                  string n,
                  optional<string> e,
                  const scope_type& s)
        : proj (move (p)),
          type (t),
          dir (move (d)),
          out (move (o)),
          name (move (n)),
          ext (move (e)),
          scope (s),
          vars (false /* global */) {}

    // Make a prerequisite from a target.
    //
    explicit
    prerequisite (const target_type&);

    // Note that the returned key "tracks" the prerequisite; that is, any
    // updates to the prerequisite's members will be reflected in the key.
    //
    prerequisite_key
    key () const
    {
      return prerequisite_key {proj, {&type, &dir, &out, &name, ext}, &scope};
    }

    // As above but remap the target type to the specified.
    //
    prerequisite_key
    key (const target_type_type& tt) const
    {
      return prerequisite_key {proj, {&tt, &dir, &out, &name, ext}, &scope};
    }

    // Return true if this prerequisite instance (physically) belongs to the
    // target's prerequisite list. Note that this test only works if you use
    // references to the container elements and the container hasn't been
    // resized since such a reference was obtained. Normally this function is
    // used when iterating over a combined prerequisites range to detect if
    // the prerequisite came from the group (see group_prerequisites).
    //
    bool
    belongs (const target_type&) const;

    // Prerequisite (target) type.
    //
  public:
    template <typename T>
    bool
    is_a () const {return type.is_a<T> ();}

    bool
    is_a (const target_type_type& tt) const {return type.is_a (tt);}

  public:
    prerequisite (prerequisite&& x)
        : proj (move (x.proj)),
          type (x.type),
          dir (move (x.dir)),
          out (move (x.out)),
          name (move (x.name)),
          ext (move (x.ext)),
          scope (x.scope),
          target (x.target.load (memory_order_relaxed)),
          vars (move (x.vars)) {}

    prerequisite (const prerequisite& x, memory_order o = memory_order_consume)
        : proj (x.proj),
          type (x.type),
          dir (x.dir),
          out (x.out),
          name (x.name),
          ext (x.ext),
          scope (x.scope),
          target (x.target.load (o)),
          vars (x.vars) {}
  };

  inline ostream&
  operator<< (ostream& os, const prerequisite& p)
  {
    return os << p.key ();
  }

  using prerequisites = vector<prerequisite>;

  // Helpers for dealing with the prerequisite inclusion/exclusion (the
  // 'include' buildfile variable, see var_include in context.hxx).
  //
  // Note that the include(prerequisite_member) overload is also provided.
  //
  // @@ Maybe this filtering should be incorporated into *_prerequisites() and
  // *_prerequisite_members() logic? Could make normal > adhoc > excluded and
  // then pass the "threshold".
  //
  class include_type
  {
  public:
    enum value {excluded, adhoc, normal};

    include_type (value v): v_ (v) {}
    include_type (bool  v): v_ (v ? normal : excluded) {}

    operator         value () const {return v_;}
    explicit operator bool () const {return v_ != excluded;}

  private:
    value v_;
  };

  include_type
  include (action,
           const target&,
           const prerequisite&,
           const target* = nullptr);
}

#include <libbuild2/prerequisite.ixx>

#endif // LIBBUILD2_PREREQUISITE_HXX