aboutsummaryrefslogtreecommitdiff
path: root/libbuild2/dyndep.hxx
blob: a0949c44388f17c08898d435d1aa1b0a8056956f (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
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
// 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 newer 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 or was updated
    // during match and issue diagnostics and fail otherwise (regardless of
    // the fail flag). Pass 0 for pts_n if don't want the "was updated during
    // match" part.
    //
    // 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).
    //
    // Note that this function expects all the static prerequisites of the
    // target to already be matched and their number passed in pts_n.
    //
    static optional<bool>
    inject_existing_file (tracer&, const char* what,
                          action, target&, size_t pts_n,
                          const file& prerequiste,
                          timestamp,
                          bool fail,
                          bool adhoc = false,
                          uintptr_t data = 0);

    // Verify the file is matched with noop_recipe or was updated during match
    // 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). Pass 0 for pts_n if don't want the "was updated during
    // match" part.
    //
    // Note: can only be called in the execute phase.
    //
    static void
    verify_existing_file (tracer&, const char* what,
                          action, const target&, size_t pts_n,
                          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.
    //
    // It's possible the extension-to-target type mapping is ambiguous (for
    // example, because both C and C++-language headers use the same .h
    // extension). So this function can return multiple target types.
    //
    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 dynamic (that
    // is, not real or implied), unless the dynamic argument is true.
    //
    static pair<const file*, bool>
    find_file (tracer&, const char* what,
               action, const scope& base, const target&,
               path& prerequisite, bool cache, bool normalized,
               bool dynamic,
               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 and
    // set its path. Return the target and an indication of whether it was
    // made a member (can only be false if a filter is provided; see below).
    //
    // The file path must be absolute and normalized. Note that this function
    // assumes that this member can only be matched via this group. The group
    // type must have the target_type::flag::dyn_members flag.
    //
    // If specified, the group_filter function is called on the target before
    // making it a group member, skipping it if this function returns false.
    // Note that the filter is skipped if the target is newly inserted (the
    // filter is meant to be used to skip duplicates).
    //
    using group_filter_func = bool (mtime_target& g, const file&);

    static pair<const file&, bool>
    inject_group_member (action, const scope& base, mtime_target&,
                         path,
                         const target_type&,
                         const function<group_filter_func>& = nullptr);

    template <typename T>
    static pair<const T&, bool>
    inject_group_member (action a, const scope& bs, mtime_target& g,
                         path f,
                         const function<group_filter_func>& filter = nullptr)
    {
      auto p (inject_group_member (a, bs, g, move (f), T::static_type, filter));
      return pair<const T&, bool> (p.first.template as<T> (), p.second);
    }

    // As above but the target type is determined using the map_extension
    // function if specified, falling back to the fallback type if unable to
    // (the what argument is used for diagnostics during this process).
    //
    static pair<const file&, bool>
    inject_group_member (const char* what,
                         action, const scope& base, mtime_target& g,
                         path,
                         const function<map_extension_func>&,
                         const target_type& fallback,
                         const function<group_filter_func>& = nullptr);


    // Find or insert a target file path as a target, make it a member of the
    // specified ad hoc group unless it already is, and set its path. Return
    // the target and an indication of whether it was added as a member.
    //
    // The file path must be absolute and normalized. Note that this function
    // assumes that this target can only be known as a member of this group.
    //
    static pair<const file&, bool>
    inject_adhoc_group_member (action, const scope& base, target& g,
                               path,
                               const target_type&);

    // As above but the target type is determined using the map_extension
    // function if specified, falling back to the fallback type if unable to
    // (the what argument is used for diagnostics during this process).
    //
    static pair<const file&, bool>
    inject_adhoc_group_member (const char* what,
                               action, const scope& base, target& g,
                               path,
                               const function<map_extension_func>&,
                               const target_type& fallback);
  };
}

#endif // LIBBUILD2_DYNDEP_HXX