aboutsummaryrefslogtreecommitdiff
path: root/build/rule-map
blob: 74d1cfc66b5b9ace905ba0e6faa52fcab500f53f (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
// file      : build/rule-map -*- C++ -*-
// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
// license   : MIT; see accompanying LICENSE file

#ifndef BUILD_RULE_MAP
#define BUILD_RULE_MAP

#include <map>
#include <vector>
#include <string>
#include <memory>     // unique_ptr
#include <functional> // reference_wrapper

#include <butl/prefix-map>

#include <build/types>
#include <build/operation>

namespace build
{
  class rule;

  using target_type_rule_map = std::map<
    const target_type*,
    butl::prefix_map<std::string, // Rule hint.
                     std::reference_wrapper<rule>, '.'>>;

  // This is an "indexed map" with operation_id being the index. Entry
  // with id 0 is a wildcard.
  //
  class operation_rule_map
  {
  public:
    template <typename T>
    void
    insert (operation_id oid, const char* hint, rule& r)
    {
      // 3 is the number of builtin operations.
      //
      if (oid >= map_.size ())
        map_.resize ((oid < 3 ? 3 : oid) + 1);

      map_[oid][&T::static_type].emplace (hint, r);
    }

    // Return NULL if not found.
    //
    const target_type_rule_map*
    operator[] (operation_id oid) const
    {
      return map_.size () > oid ? &map_[oid] : nullptr;
    }

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

  private:
    std::vector<target_type_rule_map> map_;
  };

  // This is another indexed map but this time meta_operation_id is the
  // index. The implementation is different, however: here we use a linked
  // list with the first, statically-allocated node corresponding to the
  // perform meta-operation. The idea is to try and get away with a dynamic
  // allocation for the common cases since most rules will be registered
  // for perform, at least on non-root scopes.
  //
  class rule_map
  {
  public:

    template <typename T>
    void
    insert (action_id a, const char* hint, rule& r)
    {
      insert<T> (a >> 4, a & 0x0F, hint, r);
    }

    template <typename T>
    void
    insert (meta_operation_id mid, operation_id oid, const char* hint, rule& r)
    {
      if (mid_ == mid)
        map_.insert<T> (oid, hint, r);
      else
      {
        if (next_ == nullptr)
          next_.reset (new rule_map (mid));

        next_->insert<T> (mid, oid, hint, r);
      }
    }

    // Return NULL if not found.
    //
    const operation_rule_map*
    operator[] (meta_operation_id mid) const
    {
      return mid == mid_ ? &map_ : next_ == nullptr ? nullptr : (*next_)[mid];
    }

    explicit
    rule_map (meta_operation_id mid = perform_id): mid_ (mid) {}

    bool
    empty () const {return map_.empty () && next_ == nullptr;}

  private:
    meta_operation_id mid_;
    operation_rule_map map_;
    std::unique_ptr<rule_map> next_;
  };
}

#endif // BUILD_RULE_MAP