// file : libbuild2/rule.hxx -*- C++ -*- // license : MIT; see accompanying LICENSE file #ifndef LIBBUILD2_RULE_HXX #define LIBBUILD2_RULE_HXX #include #include #include #include #include #include #include namespace build2 { // Rule interface (see also simple_rule below for a simplified version). // // Once a rule is registered (for a scope), it is treated as immutable. If // you need to modify some state (e.g., counters or some such), then make // sure things are MT-safe. // // Note: match() could be called multiple times (so should be idempotent) // and it may not be followed by apply(). // // 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). // // 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 { public: virtual bool match (action, target&, const string& hint, match_extra&) const = 0; 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 ~rule (); 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& 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 hint or extras. // class LIBBUILD2_SYMEXPORT simple_rule: public rule { public: virtual bool match (action, target&) const = 0; virtual recipe apply (action, target&) const = 0; virtual bool match (action, target&, const string&, match_extra&) const override; 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. // // 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&, match_extra&) const override; virtual recipe apply (action, target&, match_extra&) const override; // 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; // 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 override; virtual recipe apply (action, target&) const override; alias_rule () {} static const alias_rule instance; }; // Note that this rule ignores the dry_run flag; see mkdir() in filesystem // for the rationale. // class LIBBUILD2_SYMEXPORT fsdir_rule: public simple_rule { public: virtual bool match (action, target&) const override; virtual recipe apply (action, target&) const override; static target_state perform_update (action, const target&); static target_state perform_clean (action, const target&); // Sometimes, as an optimization, we want to emulate execute_direct() // of fsdir{} without the overhead of switching to the execute phase. // static void perform_update_direct (action, const fsdir&); static void perform_clean_direct (action, const fsdir&); fsdir_rule () {} static const fsdir_rule instance; }; // Fallback rule that always matches and does nothing. // class LIBBUILD2_SYMEXPORT noop_rule: public simple_rule { public: virtual bool match (action, target&) const override; virtual recipe apply (action, target&) const override; // If exclude_group is true then exclude the group-based targets (since // their membership can only be accurately determined by the ad hoc // recipe). // explicit noop_rule (bool exclude_group = false): exclude_group_ (exclude_group) {} static const noop_rule instance; // Note: does not exclude group. private: bool exclude_group_; }; // 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. small_vector actions; // Actions this rule is for. // 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 (move (name), static_cast (*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 (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 reverse operation if it is // providing the other half. // virtual bool 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; // Dump support. // virtual void dump_attributes (ostream&) const; virtual void dump_text (ostream&, string& indentation) const = 0; // Implementation details. // public: // 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; static const dir_path recipes_build_dir; // Scope operation callback that cleans up ad hoc recipe builds. // static target_state clean_recipes_build (action, const scope&, const dir&); }; // A mix-in interface for ad hoc rules that support recipes with deadlines. // class adhoc_rule_with_deadline { public: virtual ~adhoc_rule_with_deadline (); // Return empty recipe if one with the deadline cannot be provided for // this action. In this case the caller may fallback to the normal // apply(). // virtual recipe apply (action, target&, match_extra&, const optional&) 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, 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, 1>& rules; explicit fallback_rule (const small_vector, 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