aboutsummaryrefslogtreecommitdiff
path: root/build2/test/script/parser.cxx
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2017-06-16 23:43:24 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2017-06-19 12:57:24 +0300
commit062e03325cf9bb7fecfb9ea254ceb5c0cf427a7a (patch)
treec9e29b96a4ae919dcd99541d38e191c7fe20c7b1 /build2/test/script/parser.cxx
parent4c2243f8c790207c0ee4119fcb081b69f06f476e (diff)
Add support for exit testscript builtin
Diffstat (limited to 'build2/test/script/parser.cxx')
-rw-r--r--build2/test/script/parser.cxx584
1 files changed, 317 insertions, 267 deletions
diff --git a/build2/test/script/parser.cxx b/build2/test/script/parser.cxx
index 6058ed2..b02c009 100644
--- a/build2/test/script/parser.cxx
+++ b/build2/test/script/parser.cxx
@@ -2895,147 +2895,171 @@ namespace build2
}
else if (group* g = dynamic_cast<group*> (scope_))
{
- exec_lines (
- g->setup_.begin (), g->setup_.end (), li, command_type::setup);
-
- atomic_count task_count (0);
- wait_guard wg (task_count);
+ bool exec_scope (
+ exec_lines (
+ g->setup_.begin (), g->setup_.end (), li, command_type::setup));
- // Start asynchronous execution of inner scopes keeping track of how
- // many we have handled.
- //
- for (unique_ptr<scope>& chain: g->scopes)
+ if (exec_scope)
{
- // Check if this scope is ignored (e.g., via config.test).
- //
- if (!runner_->test (*chain))
- {
- chain = nullptr;
- continue;
- }
+ atomic_count task_count (0);
+ wait_guard wg (task_count);
- // Pick a scope from the if-else chain.
- //
- // In fact, we are going to drop all but the selected (if any)
- // scope. This way we can re-examine the scope states later. It
- // will also free some memory.
+ // Start asynchronous execution of inner scopes keeping track of
+ // how many we have handled.
//
- unique_ptr<scope>* ps;
- for (ps = &chain; *ps != nullptr; ps = &ps->get ()->if_chain)
+ for (unique_ptr<scope>& chain: g->scopes)
{
- scope& s (**ps);
-
- if (!s.if_cond_) // Unconditional.
+ // Check if this scope is ignored (e.g., via config.test).
+ //
+ if (!runner_->test (*chain) || !exec_scope)
{
- assert (s.if_chain == nullptr);
- break;
+ chain = nullptr;
+ continue;
}
- line l (move (*s.if_cond_));
- line_type lt (l.type);
+ // Pick a scope from the if-else chain.
+ //
+ // In fact, we are going to drop all but the selected (if any)
+ // scope. This way we can re-examine the scope states later. It
+ // will also free some memory.
+ //
+ unique_ptr<scope>* ps;
+ for (ps = &chain; *ps != nullptr; ps = &ps->get ()->if_chain)
+ {
+ scope& s (**ps);
- replay_data (move (l.tokens));
+ if (!s.if_cond_) // Unconditional.
+ {
+ assert (s.if_chain == nullptr);
+ break;
+ }
- token t;
- type tt;
+ line l (move (*s.if_cond_));
+ line_type lt (l.type);
- next (t, tt);
- const location ll (get_location (t));
- next (t, tt); // Skip to start of command.
+ replay_data (move (l.tokens));
- bool take;
- if (lt != line_type::cmd_else)
- {
- // Note: the line index count continues from setup.
- //
- command_expr ce (parse_command_line (t, tt));
- take = runner_->run_if (*scope_, ce, ++li, ll);
+ token t;
+ type tt;
- if (lt == line_type::cmd_ifn || lt == line_type::cmd_elifn)
- take = !take;
- }
- else
- {
- assert (tt == type::newline);
- take = true;
+ next (t, tt);
+ const location ll (get_location (t));
+ next (t, tt); // Skip to start of command.
+
+ bool take;
+ if (lt != line_type::cmd_else)
+ {
+ // Note: the line index count continues from setup.
+ //
+ command_expr ce (parse_command_line (t, tt));
+
+ try
+ {
+ take = runner_->run_if (*scope_, ce, ++li, ll);
+ }
+ catch (const exit_scope& e)
+ {
+ // Bail out if the scope is exited with the failure status.
+ // Otherwise leave the scope normally.
+ //
+ if (!e.status)
+ throw failed ();
+
+ // Stop iterating through if conditions, and stop executing
+ // inner scopes.
+ //
+ exec_scope = false;
+ replay_stop ();
+ break;
+ }
+
+ if (lt == line_type::cmd_ifn || lt == line_type::cmd_elifn)
+ take = !take;
+ }
+ else
+ {
+ assert (tt == type::newline);
+ take = true;
+ }
+
+ replay_stop ();
+
+ if (take)
+ {
+ // Count the remaining conditions for the line index.
+ //
+ for (scope* r (s.if_chain.get ());
+ r != nullptr &&
+ r->if_cond_->type != line_type::cmd_else;
+ r = r->if_chain.get ())
+ ++li;
+
+ s.if_chain.reset (); // Drop remaining scopes.
+ break;
+ }
}
- replay_stop ();
+ chain.reset (*ps == nullptr || (*ps)->empty () || !exec_scope
+ ? nullptr
+ : ps->release ());
- if (take)
+ if (chain != nullptr)
{
- // Count the remaining conditions for the line index.
+ // Hand it off to a sub-parser potentially in another thread.
+ // But we could also have handled it serially in this parser:
//
- for (scope* r (s.if_chain.get ());
- r != nullptr && r->if_cond_->type != line_type::cmd_else;
- r = r->if_chain.get ())
- ++li;
+ // scope* os (scope_);
+ // scope_ = chain.get ();
+ // exec_scope_body ();
+ // scope_ = os;
- s.if_chain.reset (); // Drop remaining scopes.
- break;
+ // Pass our diagnostics stack (this is safe since we are going
+ // to wait for completion before unwinding the diag stack).
+ //
+ // If the scope was executed synchronously, check the status
+ // and bail out if we weren't asked to keep going.
+ //
+ if (!sched.async (task_count,
+ [] (scope& s,
+ script& scr,
+ runner& r,
+ const diag_frame* ds)
+ {
+ diag_frame df (ds);
+ execute_impl (s, scr, r);
+ },
+ ref (*chain),
+ ref (*script_),
+ ref (*runner_),
+ diag_frame::stack))
+ {
+ // Bail out if the scope has failed and we weren't instructed
+ // to keep going.
+ //
+ if (chain->state == scope_state::failed && !keep_going)
+ throw failed ();
+ }
}
}
- chain.reset (*ps == nullptr || (*ps)->empty ()
- ? nullptr
- : ps->release ());
+ wg.wait ();
- if (chain != nullptr)
+ // Re-examine the scopes we have executed collecting their state.
+ //
+ for (const unique_ptr<scope>& chain: g->scopes)
{
- // Hand it off to a sub-parser potentially in another thread.
- // But we could also have handled it serially in this parser:
- //
- // scope* os (scope_);
- // scope_ = chain.get ();
- // exec_scope_body ();
- // scope_ = os;
+ if (chain == nullptr)
+ continue;
- // Pass our diagnostics stack (this is safe since we are going
- // to wait for completion before unwinding the diag stack).
- //
- // If the scope was executed synchronously, check the status and
- // bail out if we weren't asked to keep going.
- //
- if (!sched.async (task_count,
- [] (scope& s,
- script& scr,
- runner& r,
- const diag_frame* ds)
- {
- diag_frame df (ds);
- execute_impl (s, scr, r);
- },
- ref (*chain),
- ref (*script_),
- ref (*runner_),
- diag_frame::stack))
+ switch (chain->state)
{
- // Bail out if the scope has failed and we weren't instructed
- // to keep going.
- //
- if (chain->state == scope_state::failed && !keep_going)
- throw failed ();
+ case scope_state::passed: break;
+ case scope_state::failed: throw failed ();
+ default: assert (false);
}
}
}
- wg.wait ();
-
- // Re-examine the scopes we have executed collecting their state.
- //
- for (const unique_ptr<scope>& chain: g->scopes)
- {
- if (chain == nullptr)
- continue;
-
- switch (chain->state)
- {
- case scope_state::passed: break;
- case scope_state::failed: throw failed ();
- default: assert (false);
- }
- }
-
exec_lines (
g->tdown_.begin (), g->tdown_.end (), li, command_type::teardown);
}
@@ -3047,205 +3071,231 @@ namespace build2
scope_->state = scope_state::passed;
}
- void parser::
+ bool parser::
exec_lines (lines::iterator i, lines::iterator e,
size_t& li,
command_type ct)
{
- token t;
- type tt;
-
- for (; i != e; ++i)
+ try
{
- line& ln (*i);
- line_type lt (ln.type);
-
- assert (path_ == nullptr);
- replay_data (move (ln.tokens)); // Set the tokens and start playing.
-
- // We don't really need to change the mode since we already know
- // the line type.
- //
- next (t, tt);
- const location ll (get_location (t));
+ token t;
+ type tt;
- switch (lt)
+ for (; i != e; ++i)
{
- case line_type::var:
- {
- // Parse.
- //
- string name (move (t.value));
+ line& ln (*i);
+ line_type lt (ln.type);
- next (t, tt);
- type kind (tt); // Assignment kind.
+ assert (path_ == nullptr);
- value rhs (parse_variable_line (t, tt));
+ // Set the tokens and start playing.
+ //
+ replay_data (move (ln.tokens));
- if (tt == type::semi)
- next (t, tt);
+ // We don't really need to change the mode since we already know
+ // the line type.
+ //
+ next (t, tt);
+ const location ll (get_location (t));
- assert (tt == type::newline);
+ switch (lt)
+ {
+ case line_type::var:
+ {
+ // Parse.
+ //
+ string name (move (t.value));
- // Assign.
- //
- const variable& var (*ln.var);
+ next (t, tt);
+ type kind (tt); // Assignment kind.
- value& lhs (kind == type::assign
- ? scope_->assign (var)
- : scope_->append (var));
+ value rhs (parse_variable_line (t, tt));
- build2::parser::apply_value_attributes (
- &var, lhs, move (rhs), kind);
+ if (tt == type::semi)
+ next (t, tt);
- // If we changes any of the test.* values, then reset the $*,
- // $N special aliases.
- //
- if (var.name == script_->test_var.name ||
- var.name == script_->options_var.name ||
- var.name == script_->arguments_var.name ||
- var.name == script_->redirects_var.name ||
- var.name == script_->cleanups_var.name)
- {
- scope_->reset_special ();
- }
+ assert (tt == type::newline);
- replay_stop ();
- break;
- }
- case line_type::cmd:
- {
- // We use the 0 index to signal that this is the only command.
- // Note that we only do this for test commands.
- //
- if (ct == command_type::test && li == 0)
- {
- lines::iterator j (i);
- for (++j; j != e && j->type == line_type::var; ++j) ;
+ // Assign.
+ //
+ const variable& var (*ln.var);
- if (j != e) // We have another command.
- ++li;
- }
- else
- ++li;
+ value& lhs (kind == type::assign
+ ? scope_->assign (var)
+ : scope_->append (var));
- command_expr ce (parse_command_line (t, tt));
- runner_->run (*scope_, ce, ct, li, ll);
+ build2::parser::apply_value_attributes (
+ &var, lhs, move (rhs), kind);
- replay_stop ();
- break;
- }
- case line_type::cmd_if:
- case line_type::cmd_ifn:
- case line_type::cmd_elif:
- case line_type::cmd_elifn:
- case line_type::cmd_else:
- {
- next (t, tt); // Skip to start of command.
+ // If we changes any of the test.* values, then reset the $*,
+ // $N special aliases.
+ //
+ if (var.name == script_->test_var.name ||
+ var.name == script_->options_var.name ||
+ var.name == script_->arguments_var.name ||
+ var.name == script_->redirects_var.name ||
+ var.name == script_->cleanups_var.name)
+ {
+ scope_->reset_special ();
+ }
- bool take;
- if (lt != line_type::cmd_else)
+ replay_stop ();
+ break;
+ }
+ case line_type::cmd:
{
- // Assume if-else always involves multiple commands.
+ // We use the 0 index to signal that this is the only command.
+ // Note that we only do this for test commands.
//
+ if (ct == command_type::test && li == 0)
+ {
+ lines::iterator j (i);
+ for (++j; j != e && j->type == line_type::var; ++j) ;
+
+ if (j != e) // We have another command.
+ ++li;
+ }
+ else
+ ++li;
+
command_expr ce (parse_command_line (t, tt));
- take = runner_->run_if (*scope_, ce, ++li, ll);
+ runner_->run (*scope_, ce, ct, li, ll);
- if (lt == line_type::cmd_ifn || lt == line_type::cmd_elifn)
- take = !take;
+ replay_stop ();
+ break;
}
- else
+ case line_type::cmd_if:
+ case line_type::cmd_ifn:
+ case line_type::cmd_elif:
+ case line_type::cmd_elifn:
+ case line_type::cmd_else:
{
- assert (tt == type::newline);
- take = true;
- }
+ next (t, tt); // Skip to start of command.
- replay_stop ();
-
- // If end is true, then find the 'end' line. Otherwise, find the
- // next if-else line. If skip is true then increment the command
- // line index.
- //
- auto next = [e, &li]
- (lines::iterator j, bool end, bool skip) -> lines::iterator
- {
- // We need to be aware of nested if-else chains.
- //
- size_t n (0);
+ bool take;
+ if (lt != line_type::cmd_else)
+ {
+ // Assume if-else always involves multiple commands.
+ //
+ command_expr ce (parse_command_line (t, tt));
+ take = runner_->run_if (*scope_, ce, ++li, ll);
- for (++j; j != e; ++j)
+ if (lt == line_type::cmd_ifn || lt == line_type::cmd_elifn)
+ take = !take;
+ }
+ else
{
- line_type lt (j->type);
+ assert (tt == type::newline);
+ take = true;
+ }
- if (lt == line_type::cmd_if ||
- lt == line_type::cmd_ifn)
- ++n;
+ replay_stop ();
- // If we are nested then we just wait until we get back to
- // the surface.
- //
- if (n == 0)
+ // If end is true, then find the 'end' line. Otherwise, find
+ // the next if-else line. If skip is true then increment the
+ // command line index.
+ //
+ auto next = [e, &li]
+ (lines::iterator j, bool end, bool skip) -> lines::iterator
{
- switch (lt)
+ // We need to be aware of nested if-else chains.
+ //
+ size_t n (0);
+
+ for (++j; j != e; ++j)
{
- case line_type::cmd_elif:
- case line_type::cmd_elifn:
- case line_type::cmd_else: if (end) break; // Fall through.
- case line_type::cmd_end: return j;
- default: break;
- }
- }
+ line_type lt (j->type);
- if (lt == line_type::cmd_end)
- --n;
+ if (lt == line_type::cmd_if ||
+ lt == line_type::cmd_ifn)
+ ++n;
- if (skip)
- {
- // Note that we don't count else and end as commands.
- //
- switch (lt)
- {
- case line_type::cmd:
- case line_type::cmd_if:
- case line_type::cmd_ifn:
- case line_type::cmd_elif:
- case line_type::cmd_elifn: ++li; break;
- default: break;
+ // If we are nested then we just wait until we get back
+ // to the surface.
+ //
+ if (n == 0)
+ {
+ switch (lt)
+ {
+ case line_type::cmd_elif:
+ case line_type::cmd_elifn:
+ case line_type::cmd_else:
+ {
+ if (end) break;
+
+ // Fall through.
+ }
+ case line_type::cmd_end: return j;
+ default: break;
+ }
+ }
+
+ if (lt == line_type::cmd_end)
+ --n;
+
+ if (skip)
+ {
+ // Note that we don't count else and end as commands.
+ //
+ switch (lt)
+ {
+ case line_type::cmd:
+ case line_type::cmd_if:
+ case line_type::cmd_ifn:
+ case line_type::cmd_elif:
+ case line_type::cmd_elifn: ++li; break;
+ default: break;
+ }
+ }
}
- }
- }
- assert (false); // Missing end.
- return e;
- };
+ assert (false); // Missing end.
+ return e;
+ };
- // If we are taking this branch then we need to parse all the
- // lines until the next if-else line and then skip all the lines
- // until the end (unless next is already end).
- //
- // Otherwise, we need to skip all the lines until the next
- // if-else line and then continue parsing.
- //
- if (take)
- {
- lines::iterator j (next (i, false, false)); // Next if-else.
- exec_lines (i + 1, j, li, ct);
- i = j->type == line_type::cmd_end ? j : next (j, true, true);
+ // If we are taking this branch then we need to parse all the
+ // lines until the next if-else line and then skip all the
+ // lines until the end (unless next is already end).
+ //
+ // Otherwise, we need to skip all the lines until the next
+ // if-else line and then continue parsing.
+ //
+ if (take)
+ {
+ lines::iterator j (next (i, false, false)); // Next if-else.
+ if (!exec_lines (i + 1, j, li, ct))
+ return false;
+
+ i = j->type == line_type::cmd_end ? j : next (j, true, true);
+ }
+ else
+ {
+ i = next (i, false, true);
+ if (i->type != line_type::cmd_end)
+ --i; // Continue with this line (e.g., elif or else).
+ }
+
+ break;
}
- else
+ case line_type::cmd_end:
{
- i = next (i, false, true);
- if (i->type != line_type::cmd_end)
- --i; // Continue with this line (e.g., elif or else).
+ assert (false);
}
-
- break;
- }
- case line_type::cmd_end:
- {
- assert (false);
}
}
+
+ return true;
+ }
+ catch (const exit_scope& e)
+ {
+ // Bail out if the scope is exited with the failure status. Otherwise
+ // leave the scope normally.
+ //
+ if (!e.status)
+ throw failed ();
+
+ replay_stop ();
+ return false;
}
}