aboutsummaryrefslogtreecommitdiff
path: root/libbuild2/build/script/parser.hxx
diff options
context:
space:
mode:
Diffstat (limited to 'libbuild2/build/script/parser.hxx')
-rw-r--r--libbuild2/build/script/parser.hxx307
1 files changed, 270 insertions, 37 deletions
diff --git a/libbuild2/build/script/parser.hxx b/libbuild2/build/script/parser.hxx
index 5ada8be..8f86b24 100644
--- a/libbuild2/build/script/parser.hxx
+++ b/libbuild2/build/script/parser.hxx
@@ -33,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&,
+ 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);
@@ -52,34 +65,197 @@ 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&);
// Execute. Issue diagnostics and throw failed in case of an error.
//
public:
+
+ // By default call the runner's enter() and leave() functions that
+ // 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 (const scope& root, const scope& base,
- environment&, const script&, runner&);
+ 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>;
+
+ 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.
+ //
+ // 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.
+ //
+ void
+ pre_exec (const scope& root, const scope& base,
+ environment&, const script*, runner*);
+
+ using lines_iterator = lines::const_iterator;
+
+ void
+ 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. If omit_builtin is true, then omit the
- // builtin name from the result.
+ // and pattern expansions. Optionally, skip the first token (builtin
+ // name, etc).
//
names
- execute_special (const scope& root, const scope& base,
- environment&,
- const line&,
- bool omit_builtin = true);
+ exec_special (token&, build2::script::token_type&, bool skip_first);
- protected:
+ // 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_script ();
+ 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.
//
@@ -91,7 +267,10 @@ 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;
// During execution translate the process path and executable targets
// leaving the rest for the base parser to handle.
@@ -106,12 +285,19 @@ 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 small_vector<action, 1>* actions_; // Non-NULL during pre-parse.
+
+ // 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.
//
@@ -138,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.
@@ -157,18 +349,59 @@ 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 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 hash <args> - Track the argument list change as a hash.
//
- // depdb string <arg> - Track the argument (single) change as string.
+ 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 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
+ // function call we just save its name/location and postpone the
+ // potential failure till the end of the script pre-parsing, if it
+ // turns out to be a body line.
+ //
+ optional<pair<string, location>> impure_func_;
+
+ // 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.
//
- optional<location> depdb_clear_; // 'depdb clear' location if any.
- lines depdb_lines_; // Note: excludes 'depdb clear'.
+ bool top_pre_parse_;
- // True during pre-parsing when the pre-parse mode is temporarily
+ // True during top-pre-parsing when the pre-parse mode is temporarily
// suspended to perform expansion.
//
bool pre_parse_suspended_ = false;
@@ -178,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;