// file : libbuild2/rule-map.hxx -*- C++ -*- // license : MIT; see accompanying LICENSE file #ifndef LIBBUILD2_RULE_MAP_HXX #define LIBBUILD2_RULE_MAP_HXX #include #include #include #include #include namespace build2 { // A rule name is used both for diagnostics as well as to match rule hints // (see rule_hints). A rule hint is a potentially partial rule name. // // The recommended rule naming scheme is to start with the module name, for // example: cxx.compile, cxx.link. This way a rule hint can be just the // module name, for example [rule_hint=cxx]. If a module can only possibly // have a single rule, then the rule name can be just the module name (e.g., // `in`; though make doubly sure there is unlikely to be a need for another // rule, for example, for documentation generation, in the future). // // The two common choices of names for the second component in a rule name // is an action (e.g., cxx.compile, cxx.link) or a target type (e.g., // bin.def, bin.lib). The latter is a good choice when the action is // inherent to the target type (e.g., "generate def file", "see through lib // group"). Also note that a rule for compensating operations (e.g., // update/clean, install/uninstall) is customarily registered with the same // name. // struct name_rule_map: butl::prefix_map, '.'> { // Return true if the rule name matches a rule hint. // static bool sub (const string& hint, const string& name) { return compare_type ('.').prefix (hint, name); } }; using target_type_rule_map = map; // This is an "indexed map" with operation_id being the index. Entry // with id 0 is a wildcard. // // Note that while we may resize some vectors during non-initial load, this // is MT-safe since we never cache any references to their elements. // class operation_rule_map { public: // Return false in case of a duplicate. // bool insert (operation_id oid, const target_type& tt, string name, const rule& r) { // 3 is the number of builtin operations. // if (oid >= map_.size ()) map_.resize ((oid < 3 ? 3 : oid) + 1); return map_[oid][&tt].emplace (move (name), r).second; } // 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: vector 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. // // Note: duplicate insertions (e.g., to global scope rule map) are ignored. // // @@ Redo using small_vector? // class rule_map { public: // Return false in case of a duplicate. // bool insert (action_id a, const target_type& tt, string name, const rule& r) { return insert (a >> 4, a & 0x0F, tt, move (name), r); } template bool insert (action_id a, string name, const rule& r) { return insert (a, T::static_type, move (name), r); } // 0 oid is a wildcard. // bool insert (meta_operation_id mid, operation_id oid, const target_type& tt, string name, const rule& r) { if (mid_ == mid) return map_.insert (oid, tt, move (name), r); else { if (next_ == nullptr) next_.reset (new rule_map (mid)); return next_->insert (mid, oid, tt, move (name), r); } } template bool insert (meta_operation_id mid, operation_id oid, string name, const rule& r) { return insert (mid, oid, T::static_type, move (name), 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_; unique_ptr next_; }; } #endif // LIBBUILD2_RULE_MAP_HXX