diff options
Diffstat (limited to 'libbuild2/rule.hxx')
-rw-r--r-- | libbuild2/rule.hxx | 255 |
1 files changed, 218 insertions, 37 deletions
diff --git a/libbuild2/rule.hxx b/libbuild2/rule.hxx index 217632f..eceb6ad 100644 --- a/libbuild2/rule.hxx +++ b/libbuild2/rule.hxx @@ -22,15 +22,26 @@ namespace build2 // you need to modify some state (e.g., counters or some such), then make // sure things are MT-safe. // - // Note: match() is only called once but may not be followed by apply(). + // Note: match() could be called multiple times (so should be idempotent) + // and it may not be followed by apply(). // - // The match_extra argument is used to pass additional information that is - // only needed by some rule implementations. It is also a way for us to - // later pass more information without breaking source compatibility. + // The hint argument is the rule hint, if any, that was used to select this + // rule. While normally not factored into the match decision, a rule may + // "try harder" if a hint was specified (see cc::link_rule for an example). // - struct match_extra - { - }; + // The match_extra argument (the type is defined in target.hxx) is used to + // pass additional information that is only needed by some rule + // implementations. It is also a way for us to later pass more information + // without breaking source compatibility. + // + // A rule may adjust post hoc prerequisites by overriding apply_posthoc(). + // See match_extra::posthoc_prerequisite_targets for background and details. + // + // A rule may support match options and if such a rule is rematched with + // different options, then reapply() is called. See + // match_extra::{cur,new}_options for background and details. + // + struct match_extra; class LIBBUILD2_SYMEXPORT rule { @@ -41,6 +52,12 @@ namespace build2 virtual recipe apply (action, target&, match_extra&) const = 0; + virtual void + apply_posthoc (action, target&, match_extra&) const; + + virtual void + reapply (action, target&, match_extra&) const; + rule () = default; virtual @@ -48,15 +65,45 @@ namespace build2 rule (const rule&) = delete; rule& operator= (const rule&) = delete; + + // Resolve a project-qualified target in a rule-specific manner. + // + // This is optional functionality that may be provided by some rules to + // facilitate immediate importation of certain target types. See the + // import machinery for details. The default implementation always returns + // NULL. + // + // Note that if this function returns a target, it should have the + // extension assigned so that as_name() returns a stable name. + // + virtual const target* + import (const prerequisite_key&, + const optional<string>& metadata, + const location&) const; + + // Sometimes we want to match only if another rule of ours would match + // another operation. For example, we would want our install rule to match + // only if our update rule also matches. + // + // Arranging this, however, is not a simple matter of calling the other + // rule's match(): we also have to take into account ad hoc recipes and + // rule hints for that operation. This helper performs all the necessary + // checks. Note: should only be called from match() (see + // target::find_hint() for details). Note also that ad hoc recipes are + // checked for hint_op, not action's operation. + // + bool + sub_match (const string& rule_name, operation_id hint_op, + action, target&, match_extra&) const; }; - // Simplified interface for rules that don't care about the extras. + // Simplified interface for rules that don't care about the hint or extras. // class LIBBUILD2_SYMEXPORT simple_rule: public rule { public: virtual bool - match (action, target&, const string& hint) const = 0; + match (action, target&) const = 0; virtual recipe apply (action, target&) const = 0; @@ -66,31 +113,52 @@ namespace build2 virtual recipe apply (action, target&, match_extra&) const override; + + // The simplified version of sub_match() above. + // + // Note that it calls the simplified match() directly rather than going + // through the original. + // + bool + sub_match (const string& rule_name, operation_id hint_op, + action, target&) const; }; // Fallback rule that only matches if the file exists. It will also match // an mtime_target provided it has a set timestamp. // - class LIBBUILD2_SYMEXPORT file_rule: public simple_rule + // Note: this rule is "hot" because it matches every static source file and + // so we don't use simple_rule to avoid two extra virtual calls. + // + class LIBBUILD2_SYMEXPORT file_rule: public rule { public: virtual bool - match (action, target&, const string&) const override; + match (action, target&, const string&, match_extra&) const override; virtual recipe - apply (action, target&) const override; + apply (action, target&, match_extra&) const override; - file_rule () {} + // While this rule expects an mtime_target-based target, sometimes it's + // necessary to register it for something less specific (normally target) + // in order to achieve the desired rule matching priority (see the dist + // and config modules for an example). For such cases this rule can be + // instructed to check the type and only match if it's mtime_target-based. + // + file_rule (bool match_type = false): match_type_ (match_type) {} - static const file_rule instance; + static const file_rule instance; // Note: does not match the target type. static const build2::rule_match rule_match; + + private: + bool match_type_; }; class LIBBUILD2_SYMEXPORT alias_rule: public simple_rule { public: virtual bool - match (action, target&, const string&) const override; + match (action, target&) const override; virtual recipe apply (action, target&) const override; @@ -106,7 +174,7 @@ namespace build2 { public: virtual bool - match (action, target&, const string&) const override; + match (action, target&) const override; virtual recipe apply (action, target&) const override; @@ -121,7 +189,10 @@ namespace build2 // of fsdir{} without the overhead of switching to the execute phase. // static void - perform_update_direct (action, const target&); + perform_update_direct (action, const fsdir&); + + static void + perform_clean_direct (action, const fsdir&); fsdir_rule () {} static const fsdir_rule instance; @@ -133,7 +204,7 @@ namespace build2 { public: virtual bool - match (action, target&, const string&) const override; + match (action, target&) const override; virtual recipe apply (action, target&) const override; @@ -144,42 +215,78 @@ namespace build2 // Ad hoc rule. // + // Used for both ad hoc pattern rules and ad hoc recipes. For recipes, it's + // essentially a rule of one case. Note that when used as part of a pattern, + // the implementation cannot use the match_extra::data() facility nor the + // target auxiliary data storage until the pattern's apply_*() calls have + // been made. + // + // Note also that when used as part of a pattern, the rule is also register + // for the dist meta-operation (unless there is an explicit recipe for dist) + // in order to inject additional pattern prerequisites which may "pull" + // additional sources into the distribution. + // // Note: not exported. // + class adhoc_rule_pattern; + class adhoc_rule: public rule { public: - location_value loc; // Buildfile location of the recipe. - size_t braces; // Number of braces in multi-brace tokens. + location_value loc; // Buildfile location of the recipe. + size_t braces; // Number of braces in multi-brace tokens. + small_vector<action, 1> actions; // Actions this rule is for. - adhoc_rule (const char* name, const location& l, size_t b) + // If not NULL then this rule (recipe, really) belongs to an ad hoc + // pattern rule and match() should call the pattern's match() and + // apply() should call the pattern's apply_*() functions (see below). + // + const adhoc_rule_pattern* pattern = nullptr; + + adhoc_rule (string name, const location& l, size_t b) : loc (l), braces (b), - rule_match (name, static_cast<const rule&> (*this)) {} + rule_match (move (name), static_cast<const rule&> (*this)) {} // Set the rule text, handle any recipe-specific attributes, and return // true if the recipe builds anything in the build/recipes/ directory and // therefore requires cleanup. // + // Scope is the scope of the recipe and target type is the type of the + // first target (for ad hoc recipe) or primary group member type (for ad + // hoc pattern rule). The idea is that an implementation may make certain + // assumptions based on the first target type (e.g., file vs non-file + // based) in which case it should also enforce (e.g., in match()) that any + // other targets that share this recipe are also of suitable type. + // + // Note also that this function is called after the actions member has + // been populated. + // virtual bool - recipe_text (context&, const target&, const adhoc_actions&, - string&&, attributes&) = 0; + recipe_text (const scope&, const target_type&, string&&, attributes&) = 0; public: // Some of the operations come in compensating pairs, such as update and // clean, install and uninstall. An ad hoc rule implementation may choose - // to provide a fallback implementation of a compensating operation if it - // is providing the other half (passed in the fallback argument). - // - // The default implementation calls rule::match() if fallback is absent - // and returns false if fallback is present. So an implementation that - // doesn't care about this semantics can implement the straight rule - // interface. + // to provide a fallback implementation of a reverse operation if it is + // providing the other half. // virtual bool - match (action, target&, const string&, match_extra&, - optional<action> fallback) const; + reverse_fallback (action, const target_type&) const; + // The default implementation forwards to the pattern's match() if there + // is a pattern and returns true otherwise. + // + // Note also that in case of a member of a group-based target, match() is + // called on the group while apply() on the member (see match_rule_impl() + // in algorithms.cxx for details). This means that match() may be called + // without having the target locked and as a result match() should (unless + // known to only match a non-group) treat the target as const and only + // rely on immutable information (type, name, etc) since the group could + // be matched concurrenly. This case can be detected by examining + // match_extra::locked (see adhoc_rule_regex_pattern::match() for a + // use-case). + // virtual bool match (action, target&, const string&, match_extra&) const override; @@ -194,10 +301,9 @@ namespace build2 // Implementation details. // public: - // The name in rule_match is used as a hint and as a name in diagnostics. - // The former does not apply to us (but will apply to ad hoc rules) while - // latter does. As a result, we use special-looking "<ad hoc X recipe>" - // names. + // The name in rule_match is used to match hints and in diagnostics. The + // former does not apply to ad hoc recipes (but does apply to ad hoc + // rules). // const build2::rule_match rule_match; @@ -224,6 +330,81 @@ namespace build2 virtual recipe apply (action, target&, match_extra&, const optional<timestamp>&) const = 0; }; + + // Ad hoc rule pattern. + // + // Note: exported since may be accessed by ad hoc recipe implementation. + // + class LIBBUILD2_SYMEXPORT adhoc_rule_pattern + { + public: + const scope& rule_scope; + const string rule_name; + const target_type& type; // Primary target type. + small_vector<shared_ptr<adhoc_rule>, 1> rules; // Really a unique_ptr. + + adhoc_rule_pattern (const scope& s, string n, const target_type& t) + : rule_scope (s), + rule_name (move (n)), + type (t), + fallback_rule_ (rules) {} + + virtual + ~adhoc_rule_pattern (); + + public: + // Note: the adhoc_rule::match() restrictions apply here as well. + // + virtual bool + match (action, const target&, const string&, match_extra&) const = 0; + + // Append additional group members. Note that this function should handle + // both ad hoc and explicit groups. + // + virtual void + apply_group_members (action, target&, + const scope& base, + match_extra&) const = 0; + + // The implementation should append pattern prerequisites to + // t.prerequisite_targets[a] but not match. It should set bit 2 in + // prerequisite_target::include to indicate update=match and bit 3 + // to indicate update=unmatch. It should also avoid adding duplicate + // fsdir{} similar to the search_prerequisite*() functions. + // + virtual void + apply_prerequisites (action, target&, + const scope& base, + match_extra&) const = 0; + + // Dump support. + // + virtual void + dump (ostream&) const = 0; + + // Gory implementation details (see match_impl()). + // + public: + class fallback_rule: public rule + { + public: + const small_vector<shared_ptr<adhoc_rule>, 1>& rules; + + explicit + fallback_rule (const small_vector<shared_ptr<adhoc_rule>, 1>& rs) + : rules (rs) {} + + // Dummy (never called). + // + virtual bool + match (action, target&, const string&, match_extra&) const override; + + virtual recipe + apply (action, target&, match_extra&) const override; + }; + + fallback_rule fallback_rule_; + }; } #endif // LIBBUILD2_RULE_HXX |