aboutsummaryrefslogtreecommitdiff
path: root/libbuild2/build
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2020-11-20 22:07:37 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2020-12-02 17:31:04 +0300
commit0ff39fd77b3127c7a250e7f817e34dfaecbcc208 (patch)
treeedb20351f3d44558201b5668823c191a8722d3a5 /libbuild2/build
parent41a6f8b7d3036708f36ea1b5bd5b8d4289428fe5 (diff)
Add support for buildscript depdb preamble
Diffstat (limited to 'libbuild2/build')
-rw-r--r--libbuild2/build/script/parser+body.test.testscript (renamed from libbuild2/build/script/parser+line.test.testscript)59
-rw-r--r--libbuild2/build/script/parser+depdb.test.testscript90
-rw-r--r--libbuild2/build/script/parser+diag.test.testscript37
-rw-r--r--libbuild2/build/script/parser.cxx340
-rw-r--r--libbuild2/build/script/parser.hxx41
-rw-r--r--libbuild2/build/script/parser.test.cxx75
-rw-r--r--libbuild2/build/script/script.cxx17
-rw-r--r--libbuild2/build/script/script.hxx22
8 files changed, 555 insertions, 126 deletions
diff --git a/libbuild2/build/script/parser+line.test.testscript b/libbuild2/build/script/parser+body.test.testscript
index 1b39265..11c2609 100644
--- a/libbuild2/build/script/parser+line.test.testscript
+++ b/libbuild2/build/script/parser+body.test.testscript
@@ -1,7 +1,62 @@
-# file : libbuild2/build/script/parser+line.test.testscript
+# file : libbuild2/build/script/parser+body.test.testscript
# license : MIT; see accompanying LICENSE file
-test.options += -d
+test.options += -b
+
+: lines
+:
+$* <<EOI >>EOO
+ s = 'foo'
+ if echo "$s" | sed 's/o/a/p' >>>? 'bar'
+ f = 'baz'
+ else
+ f = 'fox'
+ end
+ depdb clear
+ depdb string "$s"
+ depdb hash "$f"
+ foo "$s" "$f"
+ EOI
+ foo "$s" "$f"
+ EOO
+
+: temp_dir
+:
+{
+ test.options += -t
+
+ : no
+ :
+ $* <<EOI >false
+ foo
+ EOI
+
+ : yes
+ :
+ $* <<EOI >true
+ foo
+ f = $~/f
+ EOI
+
+ : preamble-no
+ :
+ $* <<EOI >false
+ f1 = $~/f2
+ depdb string "$f1"
+ f2 = $~/f3
+ depdb string "$f2"
+ foo "$f1" "$f2"
+ EOI
+
+ : preamble-yes
+ :
+ $* <<EOI >true
+ f1 = $~/f1
+ depdb string "$f1"
+ f2 = $~/f2
+ foo "$f2"
+ EOI
+}
: command
:
diff --git a/libbuild2/build/script/parser+depdb.test.testscript b/libbuild2/build/script/parser+depdb.test.testscript
new file mode 100644
index 0000000..38c4236
--- /dev/null
+++ b/libbuild2/build/script/parser+depdb.test.testscript
@@ -0,0 +1,90 @@
+# file : libbuild2/build/script/parser+depdb.test.testscript
+# license : MIT; see accompanying LICENSE file
+
+test.options += -d
+
+: clear
+:
+{
+ : multiple
+ :
+ $* <<EOI 2>>EOE != 0
+ depdb clear
+ depdb clear
+ EOI
+ buildfile:12:1: error: multiple 'depdb clear' builtin calls
+ buildfile:11:1: info: previous call is here
+ EOE
+
+ : after-string
+ :
+ $* <<EOI 2>>EOE != 0
+ a = b
+ depdb string "$a"
+ depdb clear
+ EOI
+ buildfile:13:1: error: 'depdb clear' should be the first 'depdb' builtin call
+ buildfile:12:1: info: first 'depdb' call is here
+ EOE
+}
+
+: preamble
+:
+{
+ : no-body
+ :
+ $* <<EOI >>EOO
+ s = 'foo'
+ if echo "$s" | sed 's/o/a/p' >>>? 'bar'
+ f = 'baz'
+ else
+ f = 'fox'
+ end
+ depdb clear
+ depdb string "$s"
+ depdb hash "$f"
+ foo "$s" "$f"
+ EOI
+ s = 'foo'
+ if echo "$s" | sed 's/o/a/p' >>>? 'bar'
+ f = 'baz'
+ else
+ f = 'fox'
+ end
+ depdb string "$s"
+ depdb hash "$f"
+ EOO
+
+ : temp_dir
+ :
+ {
+ test.options += -t
+
+ : no
+ :
+ $* <<EOI >false
+ f = foo
+ depdb hash "$f"
+ f = $~/f
+ foo "$f"
+ EOI
+
+ : yes
+ :
+ $* <<EOI >true
+ f = $~/f
+ depdb hash "$f"
+ foo "$f"
+ EOI
+
+ : yes-mult
+ :
+ $* <<EOI >true
+ f = $~/f
+ depdb hash "$f"
+ s = "abc"
+ depdb string "$s"
+ foo "$f"
+ EOI
+ }
+}
diff --git a/libbuild2/build/script/parser+diag.test.testscript b/libbuild2/build/script/parser+diag.test.testscript
index 5b4e64a..30eb859 100644
--- a/libbuild2/build/script/parser+diag.test.testscript
+++ b/libbuild2/build/script/parser+diag.test.testscript
@@ -33,8 +33,8 @@ $* <<EOI >>~%EOO%
: ambiguity
:
{
-: name
-:
+ : name
+ :
$* test <<EOI 2>>EOE != 0
echo abc
diag xyz
@@ -54,6 +54,29 @@ $* <<EOI >>~%EOO%
buildfile:14:1: error: multiple 'diag' builtin calls
buildfile:12:1: info: previous call is here
EOE
+
+ : names
+ :
+ $* <<EOI 2>>EOE != 0
+ cp abc xyz
+ cat xyz
+ EOI
+ buildfile:11:1: error: low-verbosity script diagnostics name is ambiguous
+ buildfile:11:1: info: could be 'cp'
+ buildfile:12:1: info: could be 'cat'
+ info: consider specifying it explicitly with the 'diag' recipe attribute
+ info: or provide custom low-verbosity diagnostics with the 'diag' builtin
+ EOE
+
+ : none
+ :
+ $* <<EOI 2>>EOE != 0
+ a = 'b'
+ EOI
+ buildfile:11:1: error: unable to deduce low-verbosity script diagnostics name
+ info: consider specifying it explicitly with the 'diag' recipe attribute
+ info: or provide custom low-verbosity diagnostics with the 'diag' builtin
+ EOE
}
: inside-if
@@ -91,3 +114,13 @@ $* <<EOI 2>>EOE != 0
EOI
buildfile:11:8: error: 'diag' call via 'env' builtin
EOE
+
+: before-depdb
+:
+$* <<EOI 2>>EOE != 0
+ diag test
+ depdb clear
+ EOI
+ buildfile:11:1: error: 'diag' builtin call before 'depdb' call
+ buildfile:12:1: info: 'depdb' call is here
+ EOE
diff --git a/libbuild2/build/script/parser.cxx b/libbuild2/build/script/parser.cxx
index 8f2c46d..9021da1 100644
--- a/libbuild2/build/script/parser.cxx
+++ b/libbuild2/build/script/parser.cxx
@@ -26,7 +26,7 @@ namespace build2
//
script parser::
- pre_parse (const target& tg,
+ pre_parse (const target& tg, const adhoc_actions& acts,
istream& is, const path_name& pn, uint64_t line,
optional<string> diag, const location& diag_loc)
{
@@ -39,9 +39,10 @@ namespace build2
// The script shouldn't be able to modify the target/scopes.
//
- target_ = const_cast<target*> (&tg);
- scope_ = const_cast<scope*> (&tg.base_scope ());
- root_ = scope_->root_scope ();
+ target_ = const_cast<target*> (&tg);
+ actions_ = &acts;
+ scope_ = const_cast<scope*> (&tg.base_scope ());
+ root_ = scope_->root_scope ();
pbase_ = scope_->src_path_;
@@ -108,7 +109,7 @@ namespace build2
// Save the custom dependency change tracking lines, if present.
//
s.depdb_clear = depdb_clear_.has_value ();
- s.depdb_lines = move (depdb_lines_);
+ s.depdb_preamble = move (depdb_preamble_);
return s;
}
@@ -232,7 +233,7 @@ namespace build2
if (save_line_ != nullptr)
{
if (save_line_ == &ln)
- script_->lines.push_back (move (ln));
+ script_->body.push_back (move (ln));
else
*save_line_ = move (ln);
}
@@ -268,12 +269,12 @@ namespace build2
// cmd_if, not cmd_end. Thus remember the start position of the
// next logical line.
//
- size_t i (script_->lines.size ());
+ size_t i (script_->body.size ());
pre_parse_line (t, tt, true /* if_line */);
assert (tt == type::newline);
- line_type lt (script_->lines[i].type);
+ line_type lt (script_->body[i].type);
// First take care of 'end'.
//
@@ -407,8 +408,8 @@ namespace build2
// Instruct the parser to save the diag builtin line separately
// from the script lines, when it is fully parsed. Note that it
- // will be executed prior to the script execution to obtain the
- // custom diagnostics.
+ // will be executed prior to the script body execution to obtain
+ // the custom diagnostics.
//
diag_line_ = make_pair (line (), l);
save_line_ = &diag_line_->first;
@@ -428,9 +429,27 @@ namespace build2
{
verify ();
+ // Verify that depdb is not used for anything other than
+ // performing update.
+ //
+ assert (actions_ != nullptr);
+
+ for (const action& a: *actions_)
+ {
+ if (a != perform_update_id)
+ fail (l) << "'depdb' builtin cannot be used to "
+ << ctx.meta_operation_table[a.meta_operation ()].name
+ << ' ' << ctx.operation_table[a.operation ()];
+ }
+
+ if (diag_line_)
+ fail (diag_line_->second)
+ << "'diag' builtin call before 'depdb' call" <<
+ info (l) << "'depdb' call is here";
+
// Note that the rest of the line contains the builtin command
- // name, potentially followed by the arguments to be
- // hashed/saved. Thus, we parse it in the value lexer mode.
+ // name, potentially followed by the arguments to be hashed/saved.
+ // Thus, we parse it in the value lexer mode.
//
mode (lexer_mode::value);
@@ -453,11 +472,27 @@ namespace build2
fail (l) << "multiple 'depdb clear' builtin calls" <<
info (*depdb_clear_) << "previous call is here";
- if (!depdb_lines_.empty ())
- fail (l) << "'depdb clear' should be the first 'depdb' "
- << "builtin call" <<
- info (depdb_lines_[0].tokens[0].location ())
- << "first 'depdb' call is here";
+ if (!depdb_preamble_.empty ())
+ {
+ diag_record dr (fail (l));
+ dr << "'depdb clear' should be the first 'depdb' builtin call";
+
+ // Print the first depdb call location.
+ //
+ for (const line& l: depdb_preamble_)
+ {
+ const replay_tokens& rt (l.tokens);
+ assert (!rt.empty ());
+
+ const token& t (rt[0].token);
+ if (t.type == type::word && t.value == "depdb")
+ {
+ dr << info (rt[0].location ())
+ << "first 'depdb' call is here";
+ break;
+ }
+ }
+ }
// Save the builtin location, cancel the line saving, and clear
// the referenced variable list, since it won't be used.
@@ -469,13 +504,37 @@ namespace build2
}
else
{
+ // Move the script body to the end of the depdb preamble.
+ //
+ // Note that at this (pre-parsing) stage we cannot evaluate if
+ // all the script body lines are allowed for depdb preamble.
+ // That, in particular, would require to analyze pipelines to
+ // see if they are terminated with the set builtin, but this
+ // information is only available at the execution stage. Thus,
+ // we move into the preamble whatever is there and delay the
+ // check until the execution.
+ //
+ lines& ls (script_->body);
+ depdb_preamble_.insert (depdb_preamble_.end (),
+ make_move_iterator (ls.begin ()),
+ make_move_iterator (ls.end ()));
+ ls.clear ();
+
+ // Also move the body_temp_dir flag, if it is true.
+ //
+ if (script_->body_temp_dir)
+ {
+ script_->depdb_preamble_temp_dir = true;
+ script_->body_temp_dir = false;
+ }
+
// Instruct the parser to save the depdb builtin line separately
// from the script lines, when it is fully parsed. Note that the
// builtin command arguments will be validated during execution,
// when expanded.
//
- depdb_lines_.push_back (line ());
- save_line_ = &depdb_lines_.back ();
+ depdb_preamble_.push_back (line ());
+ save_line_ = &depdb_preamble_.back ();
}
// Parse the rest of the line and bail out.
@@ -531,8 +590,8 @@ namespace build2
{
if (pre_parse_suspended_)
{
- dr << info (l) << "while deducing low-verbosity script "
- << "diagnostics name";
+ dr << info (l)
+ << "while deducing low-verbosity script diagnostics name";
suggest_diag (dr);
}
@@ -757,8 +816,153 @@ namespace build2
}
void parser::
- execute (const scope& rs, const scope& bs,
- environment& e, const script& s, runner& r)
+ execute_body (const scope& rs, const scope& bs,
+ environment& e, const script& s, runner& r,
+ bool enter, bool leave)
+ {
+ pre_exec (rs, bs, e, &s, &r);
+
+ if (enter)
+ runner_->enter (e, s.start_loc);
+
+ // Note that we rely on "small function object" optimization here.
+ //
+ auto exec_cmd = [this] (token& t, build2::script::token_type& tt,
+ size_t li,
+ bool single,
+ const location& ll)
+ {
+ // We use the 0 index to signal that this is the only command.
+ //
+ if (single)
+ li = 0;
+
+ command_expr ce (
+ parse_command_line (t, static_cast<token_type&> (tt)));
+
+ runner_->run (*environment_, ce, li, ll);
+ };
+
+ exec_lines (s.body, exec_cmd);
+
+ if (leave)
+ runner_->leave (e, s.end_loc);
+ }
+
+ void parser::
+ execute_depdb_preamble (const scope& rs, const scope& bs,
+ environment& e, const script& s, runner& r,
+ depdb& dd)
+ {
+ tracer trace ("execute_depdb_preamble");
+
+ // The only valid lines in the depdb preamble are the depdb builtin
+ // itself as well as the variable assignments, including via the set
+ // builtin.
+
+ pre_exec (rs, bs, e, &s, &r);
+
+ // Let's "wrap up" the objects we operate upon into the single object
+ // to rely on "small function object" optimization.
+ //
+ struct
+ {
+ environment& env;
+ const script& scr;
+ depdb& dd;
+ tracer& trace;
+ } ctx {e, s, dd, trace};
+
+ auto exec_cmd = [&ctx, this]
+ (token& t,
+ build2::script::token_type& tt,
+ size_t li,
+ bool /* single */,
+ const location& ll)
+ {
+ if (tt == type::word && t.value == "depdb")
+ {
+ names ns (exec_special (t, tt));
+
+ // This should have been enforced during pre-parsing.
+ //
+ assert (!ns.empty ()); // <cmd> ... <newline>
+
+ const string& cmd (ns[0].value);
+
+ if (cmd == "hash")
+ {
+ sha256 cs;
+ for (auto i (ns.begin () + 1); i != ns.end (); ++i) // Skip <cmd>.
+ to_checksum (cs, *i);
+
+ if (ctx.dd.expect (cs.string ()) != nullptr)
+ l4 ([&] {
+ ctx.trace (ll)
+ << "'depdb hash' argument change forcing update of "
+ << ctx.env.target;});
+ }
+ else if (cmd == "string")
+ {
+ string s;
+ try
+ {
+ s = convert<string> (
+ names (make_move_iterator (ns.begin () + 1),
+ make_move_iterator (ns.end ())));
+ }
+ catch (const invalid_argument& e)
+ {
+ fail (ll) << "invalid 'depdb string' argument: " << e;
+ }
+
+ if (ctx.dd.expect (s) != nullptr)
+ l4 ([&] {
+ ctx.trace (ll)
+ << "'depdb string' argument change forcing update of "
+ << ctx.env.target;});
+ }
+ else
+ assert (false);
+ }
+ else
+ {
+ // Note that we don't reset the line index to zero (as we do in
+ // execute_body()) assuming that there are some script body
+ // commands to follow.
+ //
+ command_expr ce (
+ parse_command_line (t, static_cast<token_type&> (tt)));
+
+ // Verify that this expression executes the set builtin.
+ //
+ if (find_if (ce.begin (), ce.end (),
+ [] (const expr_term& et)
+ {
+ const process_path& p (et.pipe.back ().program);
+ return p.initial == nullptr &&
+ p.recall.string () == "set";
+ }) == ce.end ())
+ {
+ const replay_tokens& rt (ctx.scr.depdb_preamble.back ().tokens);
+ assert (!rt.empty ());
+
+ fail (ll) << "disallowed command in depdb preamble" <<
+ info << "only variable assignments are allowed in "
+ << "depdb preamble" <<
+ info (rt[0].location ()) << "depdb preamble ends here";
+ }
+
+ runner_->run (*environment_, ce, li, ll);
+ }
+ };
+
+ exec_lines (s.depdb_preamble, exec_cmd);
+ }
+
+ void parser::
+ pre_exec (const scope& rs, const scope& bs,
+ environment& e, const script* s, runner* r)
{
path_ = nullptr; // Set by replays.
@@ -766,6 +970,8 @@ namespace build2
set_lexer (nullptr);
+ actions_ = nullptr;
+
// The script shouldn't be able to modify the scopes.
//
// Note that for now we don't set target_ since it's not clear what
@@ -776,20 +982,15 @@ namespace build2
scope_ = const_cast<scope*> (&bs);
pbase_ = scope_->src_path_;
- script_ = const_cast<script*> (&s);
- runner_ = &r;
+ script_ = const_cast<script*> (s);
+ runner_ = r;
environment_ = &e;
-
- exec_script ();
}
void parser::
- exec_script ()
+ exec_lines (const lines& lns,
+ const function<exec_cmd_function>& exec_cmd)
{
- const script& s (*script_);
-
- runner_->enter (*environment_, s.start_loc);
-
// Note that we rely on "small function object" optimization for the
// exec_*() lambdas.
//
@@ -814,22 +1015,6 @@ namespace build2
apply_value_attributes (&var, lhs, move (rhs), kind);
};
- auto exec_cmd = [this] (token& t, build2::script::token_type& tt,
- size_t li,
- bool single,
- const location& ll)
- {
- // We use the 0 index to signal that this is the only command.
- //
- if (single)
- li = 0;
-
- command_expr ce (
- parse_command_line (t, static_cast<token_type&> (tt)));
-
- runner_->run (*environment_, ce, li, ll);
- };
-
auto exec_if = [this] (token& t, build2::script::token_type& tt,
size_t li,
const location& ll)
@@ -842,14 +1027,26 @@ namespace build2
return runner_->run_if (*environment_, ce, li, ll);
};
- size_t li (1);
+ build2::script::parser::exec_lines (lns.begin (), lns.end (),
+ exec_set, exec_cmd, exec_if,
+ environment_->exec_line,
+ &environment_->var_pool);
+ }
- exec_lines (s.lines.begin (), s.lines.end (),
- exec_set, exec_cmd, exec_if,
- li,
- &environment_->var_pool);
+ names parser::
+ exec_special (token& t, build2::script::token_type& tt,
+ bool omit_builtin)
+ {
+ if (omit_builtin)
+ {
+ assert (tt != type::newline && tt != type::eos);
- runner_->leave (*environment_, s.end_loc);
+ next (t, tt);
+ }
+
+ return tt != type::newline && tt != type::eos
+ ? parse_names (t, tt, pattern_mode::expand)
+ : names ();
}
names parser::
@@ -858,25 +1055,7 @@ namespace build2
const line& ln,
bool omit_builtin)
{
- path_ = nullptr; // Set by replays.
-
- pre_parse_ = false;
-
- set_lexer (nullptr);
-
- // The script shouldn't be able to modify the scopes.
- //
- // Note that for now we don't set target_ since it's not clear what
- // it could be used for (we need scope_ for calling functions such as
- // $target.path()).
- //
- root_ = const_cast<scope*> (&rs);
- scope_ = const_cast<scope*> (&bs);
- pbase_ = scope_->src_path_;
-
- script_ = nullptr;
- runner_ = nullptr;
- environment_ = &e;
+ pre_exec (rs, bs, e, nullptr /* script */, nullptr /* runner */);
// Copy the tokens and start playing.
//
@@ -886,16 +1065,7 @@ namespace build2
build2::script::token_type tt;
next (t, tt);
- if (omit_builtin)
- {
- assert (tt != type::newline && tt != type::eos);
-
- next (t, tt);
- }
-
- names r (tt != type::newline && tt != type::eos
- ? parse_names (t, tt, pattern_mode::expand)
- : names ());
+ names r (exec_special (t, tt, omit_builtin));
replay_stop ();
return r;
@@ -928,7 +1098,7 @@ namespace build2
if (special_variable (name))
{
if (name == "~")
- script_->temp_dir = true;
+ script_->body_temp_dir = true;
}
else if (!name.empty ())
{
@@ -970,7 +1140,7 @@ namespace build2
//
if (script_ != nullptr &&
!script_->depdb_clear &&
- script_->depdb_lines.empty ())
+ script_->depdb_preamble.empty ())
{
if (r.defined () && !r.belongs (*environment_))
{
diff --git a/libbuild2/build/script/parser.hxx b/libbuild2/build/script/parser.hxx
index 5ada8be..e96a806 100644
--- a/libbuild2/build/script/parser.hxx
+++ b/libbuild2/build/script/parser.hxx
@@ -8,6 +8,7 @@
#include <libbuild2/forward.hxx>
#include <libbuild2/utility.hxx>
+#include <libbuild2/depdb.hxx>
#include <libbuild2/diagnostics.hxx>
#include <libbuild2/script/parser.hxx>
@@ -34,7 +35,7 @@ namespace build2
// name.
//
script
- pre_parse (const target&,
+ pre_parse (const target&, const adhoc_actions& acts,
istream&, const path_name&, uint64_t line,
optional<string> diag_name, const location& diag_loc);
@@ -63,9 +64,26 @@ namespace build2
// 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.
+ //
+ void
+ execute_body (const scope& root, const scope& base,
+ environment&, const script&, runner&,
+ bool enter = true, bool leave = true);
+
+
+ // 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.
+ //
void
- execute (const scope& root, const scope& base,
- environment&, const script&, runner&);
+ execute_depdb_preamble (const scope& root, const scope& base,
+ environment&, const script&, runner&,
+ depdb&);
+
// Parse a special builtin line into names, performing the variable
// and pattern expansions. If omit_builtin is true, then omit the
@@ -78,8 +96,18 @@ namespace build2
bool omit_builtin = true);
protected:
+ // Setup the parser for subsequent exec_*() function calls.
+ //
+ void
+ pre_exec (const scope& root, const scope& base,
+ environment&, const script*, runner*);
+
void
- exec_script ();
+ exec_lines (const lines&, const function<exec_cmd_function>&);
+
+ names
+ exec_special (token& t, build2::script::token_type& tt,
+ bool omit_builtin = true);
// Helpers.
//
@@ -112,6 +140,7 @@ namespace build2
protected:
script* script_;
+ const adhoc_actions* actions_; // Non-NULL during pre-parsing.
// Current low-verbosity script diagnostics and its weight.
//
@@ -165,8 +194,8 @@ namespace build2
//
// depdb string <arg> - Track the argument (single) change as string.
//
- optional<location> depdb_clear_; // 'depdb clear' location if any.
- lines depdb_lines_; // Note: excludes 'depdb clear'.
+ optional<location> depdb_clear_; // 'depdb clear' location if any.
+ lines depdb_preamble_; // Note: excludes 'depdb clear'.
// True during pre-parsing when the pre-parse mode is temporarily
// suspended to perform expansion.
diff --git a/libbuild2/build/script/parser.test.cxx b/libbuild2/build/script/parser.test.cxx
index 7f2840d..4a3e8cc 100644
--- a/libbuild2/build/script/parser.test.cxx
+++ b/libbuild2/build/script/parser.test.cxx
@@ -71,20 +71,24 @@ namespace build2
// Usages:
//
// argv[0] [-l]
- // argv[0] -d
- // argv[0] -p
+ // argv[0] -b [-t]
+ // argv[0] -d [-t]
+ // argv[0] -q
// argv[0] -g [<diag-name>]
//
// In the first form read the script from stdin and trace the script
- // execution to stdout using the custom print runner.
+ // body execution to stdout using the custom print runner.
//
// In the second form read the script from stdin, parse it and dump the
- // resulting lines to stdout.
+ // script body lines to stdout.
//
- // In the third form read the script from stdin, parse it and print
+ // In the third form read the script from stdin, parse it and dump the
+ // depdb preamble lines to stdout.
+ //
+ // In the forth form read the script from stdin, parse it and print
// line tokens quoting information to stdout.
//
- // In the forth form read the script from stdin, parse it and print the
+ // In the fifth form read the script from stdin, parse it and print the
// low-verbosity script diagnostics name or custom low-verbosity
// diagnostics to stdout. If the script doesn't deduce any of them, then
// print the diagnostics and exit with non-zero code.
@@ -92,10 +96,17 @@ namespace build2
// -l
// Print the script line number for each executed expression.
//
+ // -b
+ // Dump the parsed script body to stdout.
+ //
// -d
- // Dump the parsed script to sdout.
+ // Dump the parsed script depdb preamble to stdout.
//
- // -p
+ // -t
+ // Print true if the body (-b) or depdb preamble (-d) references the
+ // temporary directory and false otherwise.
+ //
+ // -q
// Print the parsed script tokens quoting information to sdout. If a
// token is quoted follow its representation with its quoting
// information in the [<quoting>/<completeness>] form, where:
@@ -115,13 +126,15 @@ namespace build2
enum class mode
{
run,
- dump,
- print,
+ body,
+ depdb_preamble,
+ quoting,
diag
} m (mode::run);
bool print_line (false);
optional<string> diag_name;
+ bool temp_dir (false);
for (int i (1); i != argc; ++i)
{
@@ -129,10 +142,17 @@ namespace build2
if (a == "-l")
print_line = true;
+ else if (a == "-b")
+ m = mode::body;
else if (a == "-d")
- m = mode::dump;
- else if (a == "-p")
- m = mode::print;
+ m = mode::depdb_preamble;
+ else if (a == "-t")
+ {
+ assert (m == mode::body || m == mode::depdb_preamble);
+ temp_dir = true;
+ }
+ else if (a == "-q")
+ m = mode::quoting;
else if (a == "-g")
m = mode::diag;
else
@@ -179,12 +199,14 @@ namespace build2
tt.path (path ("driver"));
+ adhoc_actions acts {perform_update_id};
+
// Parse and run.
//
parser p (ctx);
path_name nm ("buildfile");
- script s (p.pre_parse (tt,
+ script s (p.pre_parse (tt, acts,
cin, nm,
11 /* line */,
(m != mode::diag
@@ -196,9 +218,9 @@ namespace build2
{
case mode::run:
{
- environment e (perform_update_id, tt, false /* temp_dir */);
+ environment e (perform_update_id, tt, s.body_temp_dir);
print_runner r (print_line);
- p.execute (ctx.global_scope, ctx.global_scope, e, s, r);
+ p.execute_body (ctx.global_scope, ctx.global_scope, e, s, r);
break;
}
case mode::diag:
@@ -221,14 +243,27 @@ namespace build2
break;
}
- case mode::dump:
+ case mode::body:
{
- dump (cout, "", s.lines);
+ if (!temp_dir)
+ dump (cout, "", s.body);
+ else
+ cout << (s.body_temp_dir ? "true" : "false") << endl;
+
+ break;
+ }
+ case mode::depdb_preamble:
+ {
+ if (!temp_dir)
+ dump (cout, "", s.depdb_preamble);
+ else
+ cout << (s.depdb_preamble_temp_dir ? "true" : "false") << endl;
+
break;
}
- case mode::print:
+ case mode::quoting:
{
- for (const line& l: s.lines)
+ for (const line& l: s.body)
{
for (const replay_token& rt: l.tokens)
{
diff --git a/libbuild2/build/script/script.cxx b/libbuild2/build/script/script.cxx
index c6b57c3..e003b6a 100644
--- a/libbuild2/build/script/script.cxx
+++ b/libbuild2/build/script/script.cxx
@@ -74,13 +74,20 @@ namespace build2
assign (var_pool.insert ("<")) = move (ns);
}
- // Set the $~ special variable.
- //
if (temp)
- {
+ set_temp_dir_variable ();
+ }
+
+ void environment::
+ set_temp_dir_variable ()
+ {
+ // Note that the temporary directory could have been created
+ // implicitly by the runner.
+ //
+ if (temp_dir.path.empty ())
create_temp_dir ();
- assign (var_pool.insert<dir_path> ("~")) = temp_dir.path;
- }
+
+ assign (var_pool.insert<dir_path> ("~")) = temp_dir.path;
}
void environment::
diff --git a/libbuild2/build/script/script.hxx b/libbuild2/build/script/script.hxx
index 9284813..e11cb45 100644
--- a/libbuild2/build/script/script.hxx
+++ b/libbuild2/build/script/script.hxx
@@ -45,7 +45,8 @@ namespace build2
// Note that the variables are not pre-entered into a pool during the
// parsing phase, so the line variable pointers are NULL.
//
- lines_type lines;
+ lines_type body;
+ bool body_temp_dir = false; // True if the body references $~.
// Referenced ordinary (non-special) variables.
//
@@ -59,10 +60,6 @@ namespace build2
//
small_vector<string, 2> vars; // 2 for command and options.
- // True if script references the $~ special variable.
- //
- bool temp_dir = false;
-
// Command name for low-verbosity diagnostics and custom low-verbosity
// diagnostics line. Note: cannot be both (see the script parser for
// details).
@@ -74,7 +71,8 @@ namespace build2
// script parser for details).
//
bool depdb_clear;
- lines_type depdb_lines;
+ lines_type depdb_preamble;
+ bool depdb_preamble_temp_dir = false; // True if references $~.
location start_loc;
location end_loc;
@@ -136,6 +134,18 @@ namespace build2
optional<deadline> script_deadline;
optional<deadline> fragment_deadline;
+ // Index of the next script line to be executed. Used and incremented
+ // by the parser's execute_depdb_preamble() and execute_body()
+ // function calls to produce special file names, etc.
+ //
+ size_t exec_line = 1;
+
+ // Create the temporary directory (if it doesn't exist yet) and set
+ // the $~ special variable to its path.
+ //
+ void
+ set_temp_dir_variable ();
+
virtual void
set_variable (string&& name,
names&&,