aboutsummaryrefslogtreecommitdiff
path: root/libbuild2/dyndep.hxx
blob: 6632eb6591e8de044504b2654d73c4cc52cc0a97 (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
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
// file      : libbuild2/dyndep.hxx -*- C++ -*-
// license   : MIT; see accompanying LICENSE file

#ifndef LIBBUILD2_DYNDEP_HXX
#define LIBBUILD2_DYNDEP_HXX

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

#include <libbuild2/action.hxx>
#include <libbuild2/target.hxx>

#include <libbuild2/export.hxx>

// Additional functionality that is normally only useful for implementing
// rules with dynamic dependencies (usually prerequisites, but also target
// group members).
//
namespace build2
{
  class LIBBUILD2_SYMEXPORT dyndep_rule
  {
  public:
    // Update the target during the match phase. Return true if the target has
    // changed or, if the passed timestamp is not timestamp_unknown, it is
    // older than the target.
    //
    // Note that such a target must still be updated during the execute phase
    // in order to keep the dependency counts straight.
    //
    static bool
    update (tracer&, action, const target&, timestamp);

    // Update and add to the list of prerequisite targets a prerequisite file
    // target.
    //
    // Return the indication of whether it has changed or, if the passed
    // timestamp is not timestamp_unknown, is older than this timestamp. If
    // the prerequisite target does not exists nor can be generated (no rule),
    // then issue diagnostics and fail if the fail argument is true and return
    // nullopt otherwise.
    //
    // If adhoc is true, then add it as ad hoc to prerequisite targets. At
    // first it may seem like such dynamic prerequisites should always be ad
    // hoc. But on the other hand, taking headers as an example, if the same
    // header is listed as a static prerequisite, it will most definitely not
    // going to be ad hoc. So we leave it to the caller to make this decision.
    // Similarly, the data argument is passed to the prerequisite_target ctor.
    //
    static optional<bool>
    inject_file (tracer&, const char* what,
                 action, target&,
                 const file& prerequiste,
                 timestamp,
                 bool fail,
                 bool adhoc = false,
                 uintptr_t data = 0);

    // As above but verify the file is matched with noop_recipe and issue
    // diagnostics and fail otherwise (regardless of the fail flag).
    //
    // This version (together with verify_existing_file() below) is primarily
    // useful for handling dynamic dependencies that are produced as a
    // byproduct of recipe execution (and thus must have all the generated
    // prerequisites specified statically).
    //
    static optional<bool>
    inject_existing_file (tracer&, const char* what,
                          action, target&,
                          const file& prerequiste,
                          timestamp,
                          bool fail,
                          bool adhoc = false,
                          uintptr_t data = 0);

    // Verify the file is matched with noop_recipe and issue diagnostics and
    // fail otherwise. If the file is not matched, then fail if the target is
    // not implied (that is, declared in a buildfile).
    //
    // Note: can only be called in the execute phase.
    //
    static void
    verify_existing_file (tracer&, const char* what,
                          action, const target&,
                          const file& prerequiste);

    // Reverse-lookup target type(s) from file name/extension.
    //
    // If the list of base target types is specified, then only these types
    // and those derived from them are considered. Otherwise, any file-based
    // type is considered but not the file type itself.
    //
    static small_vector<const target_type*, 2>
    map_extension (const scope& base,
                   const string& name, const string& ext,
                   const target_type* const* bases);

    // Mapping of inclusion prefixes (e.g., foo in #include <foo/bar>) for
    // auto-generated files to inclusion search paths (e.g. -I) where they
    // will be generated.
    //
    // We are using a prefix map of directories (dir_path_map) instead of just
    // a map in order to also cover sub-paths (e.g., #include <foo/more/bar>
    // if we continue with the example). Specifically, we need to make sure we
    // don't treat foobar as a sub-directory of foo.
    //
    // The priority is used to decide who should override whom. Lesser values
    // are considered higher priority. Note that we allow multiple prefixless
    // mapping (where priority is used to determine the order). For details,
    // see append_prefix().
    //
    // Note that the keys should be normalized.
    //
    struct prefix_value
    {
      dir_path directory;
      size_t priority;
    };

    using prefix_map = dir_path_multimap<prefix_value>;

    // Add the specified absolute and normalized inclusion search path into
    // the prefix map of the specified target.
    //
    static void
    append_prefix (tracer&, prefix_map&, const target&, dir_path);

    // Mapping of src inclusion search paths to the corresponding out paths
    // for auto-generated files re-mapping. See cc::extract_headers() for
    // background.
    //
    // Note that we use path_map instead of dir_path_map to allow searching
    // using path (file path).
    //
    using srcout_map = path_map<dir_path>;

    class LIBBUILD2_SYMEXPORT srcout_builder
    {
    public:
      srcout_builder (context& ctx, srcout_map& map): ctx_ (ctx), map_ (map) {}

      // Process next -I path. Return true if an entry was added to the map,
      // in which case the passed path is moved from.
      //
      bool
      next (dir_path&&);

      // Skip the previously cached first half.
      //
      void
      skip ()
      {
        prev_ = nullptr;
      }

    private:
      context& ctx_;
      srcout_map& map_;

      // Previous -I's innermost scope if out_base plus the difference between
      // the scope path and the -I path (normally empty).
      //
      const scope* prev_ = nullptr;
      dir_path diff_;
    };

    // Find or insert a prerequisite file path as a target. If the path is
    // relative, then assume this is a non-existent generated file.
    //
    // Depending on the cache flag, the path is assumed to either have come
    // from the depdb cache or from the compiler run. If normalized is true,
    // then assume the absolute path is already normalized.
    //
    // Return the file target and an indication of whether it was remapped or
    // NULL if the file does not exist and cannot be generated. The passed by
    // reference file path is guaranteed to still be valid but might have been
    // adjusted (e.g., completed, normalized, remapped, etc). If the result is
    // not NULL, then it is the absolute and normalized path to the actual
    // file. If the result is NULL, then it can be used in diagnostics to
    // identify the origial file path.
    //
    // The map_extension function is used to reverse-map a file extension to
    // the target type. The fallback target type is used if it's NULL or
    // didn't return anything but only in situations where we are sure the
    // file is or should be there (see the implementation for details).
    //
    // The prefix map function is only called if this is a non-existent
    // generated file (so it can be initialized lazily). If it's NULL, then
    // generated files will not be supported. The srcout map is only consulted
    // if cache is false to re-map generated files (so its initialization can
    // be delayed until the call with cache=false).
    //
    using map_extension_func = small_vector<const target_type*, 2> (
      const scope& base, const string& name, const string& ext);

    using prefix_map_func = const prefix_map& (
      action, const scope& base, const target&);

    static pair<const file*, bool>
    enter_file (tracer&, const char* what,
                action, const scope& base, target&,
                path& prerequisite, bool cache, bool normalized,
                const function<map_extension_func>&,
                const target_type& fallback,
                const function<prefix_map_func>& = nullptr,
                const srcout_map& = {});

    // As above but do not insert the target if it doesn't already exist. This
    // function also returns NULL if the target exists but is implied (that
    // is, not declared in a buildfile).
    //
    static pair<const file*, bool>
    find_file (tracer&, const char* what,
               action, const scope& base, const target&,
               path& prerequisite, bool cache, bool normalized,
               const function<map_extension_func>&,
               const target_type& fallback,
               const function<prefix_map_func>& = nullptr,
               const srcout_map& = {});

    // Find or insert a target file path as a target of the specified type,
    // make it a member of the specified (non-ad hoc) mtime target group,
    // set its path, and match it with group_recipe.
    //
    // The file path must be absolute and normalized. Note that this function
    // assumes that this member can only be matched via this group.
    //
    // Note: we can split this function into {enter,match}_group_member()
    //       if necessary.
    //
    static const file&
    inject_group_member (action, const scope& base, mtime_target&,
                         path, const target_type&);

    template <typename T>
    static const T&
    inject_group_member (action a, const scope& bs, mtime_target& g, path p)
    {
      return inject_group_member (
        a, bs, g, move (p), T::static_type).template as<T> ();
    }
  };
}

#endif // LIBBUILD2_DYNDEP_HXX