diff options
Diffstat (limited to 'libbuild2/build/script/parser.hxx')
-rw-r--r-- | libbuild2/build/script/parser.hxx | 281 |
1 files changed, 233 insertions, 48 deletions
diff --git a/libbuild2/build/script/parser.hxx b/libbuild2/build/script/parser.hxx index b5a45ca..8f86b24 100644 --- a/libbuild2/build/script/parser.hxx +++ b/libbuild2/build/script/parser.hxx @@ -8,7 +8,6 @@ #include <libbuild2/forward.hxx> #include <libbuild2/utility.hxx> -#include <libbuild2/depdb.hxx> #include <libbuild2/diagnostics.hxx> #include <libbuild2/script/parser.hxx> @@ -34,8 +33,21 @@ namespace build2 // Note that the returned script object references the passed path // name. // + // Note also that we use the scope to lookup variable values while + // trying to deduce the low verbosity diagnostics name (see code + // around pre_parse_suspended for details). But that means we may + // derive such a name based on the wrong value. This can happen if the + // expanded variable value is reset after the recipe has been + // pre-parsed or if such a value is set on the target (which is where + // we start when looking up variables during the real parse). The + // current thinking is that a remote possibility of this happening is + // acceptable in this situation -- the worst that can happen is that + // we will end up with mismatching diagnostics. + // script - pre_parse (const target&, const adhoc_actions& acts, + pre_parse (const scope&, + const target_type&, + const small_vector<action, 1>&, istream&, const path_name&, uint64_t line, optional<string> diag_name, const location& diag_loc); @@ -53,11 +65,18 @@ namespace build2 pre_parse_script (); void - pre_parse_line (token&, token_type&, bool if_line = false); + pre_parse_line (token&, token_type&, + optional<line_type> flow_control_type = nullopt); + + void + pre_parse_block_line (token&, token_type&, line_type block_type); void pre_parse_if_else (token&, token_type&); + void + pre_parse_loop (token&, token_type&, line_type); + command_expr parse_command_line (token&, token_type&); @@ -69,31 +88,122 @@ namespace build2 // initialize/clean up the environment before/after the script // execution. // + // Note: having both root and base scopes for testing (where we pass + // global scope for both). + // void execute_body (const scope& root, const scope& base, environment&, const script&, runner&, bool enter = true, bool leave = true); - + // Execute the first or the second (dyndep) half of the depdb + // preamble. + // // Note that it's the caller's responsibility to make sure that the // runner's enter() function is called before the first preamble/body // command execution and leave() -- after the last command. // + // Note: target must be file or group. + // void - execute_depdb_preamble (const scope& root, const scope& base, - environment&, const script&, runner&, - depdb&); + execute_depdb_preamble (action a, const scope& base, const target& t, + environment& e, const script& s, runner& r, + depdb& dd) + { + auto b (s.depdb_preamble.begin ()); + exec_depdb_preamble ( + a, base, t, + e, s, r, + b, + (s.depdb_dyndep + ? b + *s.depdb_dyndep + : s.depdb_preamble.end ()), + dd); + } + + struct dynamic_target + { + string type; // Target type name (absent if static member). + build2::path path; + }; + + using dynamic_targets = vector<dynamic_target>; - - // Parse a special builtin line into names, performing the variable - // and pattern expansions. If omit_builtin is true, then omit the - // builtin name from the result. + void + execute_depdb_preamble_dyndep ( + action a, const scope& base, target& t, + environment& e, const script& s, runner& r, + depdb& dd, + dynamic_targets& dyn_targets, + bool& update, timestamp mt, bool& deferred_failure) + { + exec_depdb_preamble ( + a, base, t, + e, s, r, + s.depdb_preamble.begin () + *s.depdb_dyndep, + s.depdb_preamble.end (), + dd, &dyn_targets, &update, mt, &deferred_failure); + } + + // This version doesn't actually execute the depdb-dyndep builtin (but + // may execute some variable assignments) instead returning all the + // information (extracted from options) necessary to implement the + // depdb-dyndep --byproduct logic (which fits better into the rule + // implementation). + // + enum class dyndep_format {make, lines}; + + struct dyndep_byproduct + { + location_value location; + dyndep_format format; + optional<dir_path> cwd; + path file; + string what; + const target_type* default_type; + bool drop_cycles; + }; + + dyndep_byproduct + execute_depdb_preamble_dyndep_byproduct ( + action a, const scope& base, const target& t, + environment& e, const script& s, runner& r, + depdb& dd, bool& update, timestamp mt) + { + // Dummies. + // + // This is getting a bit ugly (we also don't really need to pass + // depdb here). One day we will find a better way... + // + dynamic_targets dyn_targets; + bool deferred_failure; + + dyndep_byproduct v; + exec_depdb_preamble ( + a, base, t, + e, s, r, + s.depdb_preamble.begin () + *s.depdb_dyndep, + s.depdb_preamble.end (), + dd, &dyn_targets, &update, mt, &deferred_failure, &v); + return v; + } + + // If the diag argument is true, then execute the preamble including + // the (trailing) diagnostics line and return the resulting names and + // its location (see exec_special() for the diagnostics line execution + // semantics). Otherwise, execute the preamble excluding the + // diagnostics line and return an empty names list and location. If + // requested, call the runner's enter() and leave() functions that + // initialize/clean up the environment before/after the preamble + // execution. // - names - execute_special (const scope& root, const scope& base, - environment&, - const line&, - bool omit_builtin = true); + // Note: having both root and base scopes for testing (where we pass + // global scope for both). + // + pair<names, location> + execute_diag_preamble (const scope& root, const scope& base, + environment&, const script&, runner&, + bool diag, bool enter, bool leave); protected: // Setup the parser for subsequent exec_*() function calls. @@ -102,12 +212,50 @@ namespace build2 pre_exec (const scope& root, const scope& base, environment&, const script*, runner*); + using lines_iterator = lines::const_iterator; + void - exec_lines (const lines&, const function<exec_cmd_function>&); + exec_lines (lines_iterator, lines_iterator, + const function<exec_cmd_function>&); + void + exec_lines (const lines& l, const function<exec_cmd_function>& c) + { + exec_lines (l.begin (), l.end (), c); + } + + // Parse a special builtin line into names, performing the variable + // and pattern expansions. Optionally, skip the first token (builtin + // name, etc). + // names - exec_special (token& t, build2::script::token_type& tt, - bool omit_builtin = true); + exec_special (token&, build2::script::token_type&, bool skip_first); + + // Note: target must be file or group. + // + void + exec_depdb_preamble (action, const scope& base, const target&, + environment&, const script&, runner&, + lines_iterator begin, lines_iterator end, + depdb&, + dynamic_targets* dyn_targets = nullptr, + bool* update = nullptr, + optional<timestamp> mt = nullopt, + bool* deferred_failure = nullptr, + dyndep_byproduct* = nullptr); + + // Note: target must be file or group. + // + void + exec_depdb_dyndep (token&, build2::script::token_type&, + size_t line_index, const location&, + action, const scope& base, target&, + depdb&, + dynamic_targets& dyn_targets, + bool& update, + timestamp, + bool& deferred_failure, + dyndep_byproduct*); // Helpers. // @@ -119,7 +267,7 @@ namespace build2 // protected: virtual lookup - lookup_variable (name&&, string&&, const location&) override; + lookup_variable (names&&, string&&, const location&) override; virtual void lookup_function (string&&, const location&) override; @@ -137,17 +285,18 @@ namespace build2 // virtual optional<process_path> parse_program (token&, build2::script::token_type&, - bool first, - bool env, - names&) override; + bool first, bool env, + names&, parse_names_result&) override; protected: script* script_; - const adhoc_actions* actions_; // Non-NULL during pre-parsing. + const small_vector<action, 1>* actions_; // Non-NULL during pre-parse. - // True if performing update is one of the actions. Only set for the - // pre-parse mode. + // True if this script is for file- or file group-based targets and + // performing update is one of the actions, respectively. Only set for + // the pre-parse mode. // + bool file_based_; bool perform_update_; // Current low-verbosity script diagnostics and its weight. @@ -175,18 +324,24 @@ namespace build2 // // If the diag builtin is encountered, then its whole line is saved // (including the leading 'diag' word) for later execution and the - // diagnostics weight is set to 4. + // diagnostics weight is set to 4. The preceding lines, which can only + // contain variable assignments (including via the set builtin, + // potentially inside the flow control constructs), are also saved. // // Any attempt to manually set the custom diagnostics twice (the diag // builtin after the script name or after another diag builtin) is // reported as ambiguity. // - // At the end of pre-parsing either diag_name_ or diag_line_ (but not - // both) are present. + // If no script name is deduced by the end of pre-parsing and the + // script is used for a single operation, then use this operation's + // name as a script name. + // + // At the end of pre-parsing either diag_name_ is present or + // diag_preamble_ is not empty (but not both). // optional<pair<string, location>> diag_name_; optional<pair<string, location>> diag_name2_; // Ambiguous script name. - optional<pair<line, location>> diag_line_; + lines diag_preamble_; uint8_t diag_weight_ = 0; // Custom dependency change tracking. @@ -194,19 +349,38 @@ namespace build2 // The depdb builtin can be used to change the default dependency // change tracking: // - // depdb clear - Cancels the default variables, targets, and - // prerequisites change tracking. Can only be - // the first depdb builtin call. + // depdb clear - Cancel the default variables, targets, and + // prerequisites change tracking. Can only be + // the first depdb builtin call. + // + // depdb hash <args> - Track the argument list change as a hash. + // + // depdb string <arg> - Track the argument (single) change as string. + // + // depdb env <var-names> - Track the environment variables change as a + // hash. // - // depdb hash <args> - Track the argument list change as a hash. + // depdb dyndep ... - Extract dynamic dependency information. Can + // only be the last depdb builtin call in the + // preamble. Note that such dependencies don't + // end up in $<. We also don't cause clean of + // such dependencies (since there may be no .d + // file) -- they should also be listed as + // static prerequisites of some other target + // (e.g., lib{} for headers) or a custom clean + // recipe should be provided. // - // depdb string <arg> - Track the argument (single) change as string. // - optional<location> depdb_clear_; // 'depdb clear' location if any. - lines depdb_preamble_; // Note: excludes 'depdb clear'. + optional<location> depdb_clear_; // depdb-clear location. + bool depdb_value_ = false; // depdb-{string,hash} + optional<pair<location, size_t>> + depdb_dyndep_; // depdb-dyndep location/position. + bool depdb_dyndep_byproduct_ = false; // --byproduct + bool depdb_dyndep_dyn_target_ = false; // --dyn-target + lines depdb_preamble_; // Note: excluding depdb-clear. // If present, the first impure function called in the body of the - // script that performs update. + // script that performs update of a file-based target. // // Note that during the line pre-parsing we cannot tell if this is a // body or depdb preamble line. Thus, if we encounter an impure @@ -216,7 +390,18 @@ namespace build2 // optional<pair<string, location>> impure_func_; - // True during pre-parsing when the pre-parse mode is temporarily + // Similar to the impure function above but for a computed (e.g., + // target-qualified) variable expansion. In this case we don't have a + // name (it's computed). + // + optional<location> computed_var_; + + // True if we (rather than the base parser) turned on the pre-parse + // mode. + // + bool top_pre_parse_; + + // True during top-pre-parsing when the pre-parse mode is temporarily // suspended to perform expansion. // bool pre_parse_suspended_ = false; @@ -226,19 +411,19 @@ namespace build2 // Before the script line gets parsed, it is set to a temporary value // that will by default be appended to the script. However, // parse_program() can point it to a different location where the line - // should be saved instead (e.g., diag_line_, etc) or set it to NULL - // if the line is handled in an ad-hoc way and should be dropped - // (e.g., depdb_clear_, etc). + // should be saved instead (e.g., diag_preamble_ back, etc) or set it + // to NULL if the line is handled in an ad-hoc way and should be + // dropped (e.g., depdb_clear_, etc). // line* save_line_; - // The if-else nesting level (and in the future for other flow - // control constructs). + // The flow control constructs nesting level. // - // Maintained during pre-parsing and is incremented when the cmd_if or - // cmd_ifn lines are encountered, which in particular means that it is - // already incremented by the time the if-condition expression is - // pre-parsed. Decremented when the cmd_end line is encountered. + // Maintained during pre-parsing and is incremented when flow control + // construct condition lines are encountered, which in particular + // means that it is already incremented by the time the condition + // expression is pre-parsed. Decremented when the cmd_end line is + // encountered. // size_t level_ = 0; |