From 0ff39fd77b3127c7a250e7f817e34dfaecbcc208 Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Fri, 20 Nov 2020 22:07:37 +0300 Subject: Add support for buildscript depdb preamble --- libbuild2/build/script/parser+body.test.testscript | 133 ++++++++ .../build/script/parser+depdb.test.testscript | 90 ++++++ libbuild2/build/script/parser+diag.test.testscript | 37 ++- libbuild2/build/script/parser+line.test.testscript | 78 ----- libbuild2/build/script/parser.cxx | 340 +++++++++++++++------ libbuild2/build/script/parser.hxx | 41 ++- libbuild2/build/script/parser.test.cxx | 75 +++-- libbuild2/build/script/script.cxx | 17 +- libbuild2/build/script/script.hxx | 22 +- 9 files changed, 631 insertions(+), 202 deletions(-) create mode 100644 libbuild2/build/script/parser+body.test.testscript create mode 100644 libbuild2/build/script/parser+depdb.test.testscript delete mode 100644 libbuild2/build/script/parser+line.test.testscript (limited to 'libbuild2/build/script') diff --git a/libbuild2/build/script/parser+body.test.testscript b/libbuild2/build/script/parser+body.test.testscript new file mode 100644 index 0000000..11c2609 --- /dev/null +++ b/libbuild2/build/script/parser+body.test.testscript @@ -0,0 +1,133 @@ +# file : libbuild2/build/script/parser+body.test.testscript +# license : MIT; see accompanying LICENSE file + +test.options += -b + +: lines +: +$* <>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 + : + $* <false + foo + EOI + + : yes + : + $* <true + foo + f = $~/f + EOI + + : preamble-no + : + $* <false + f1 = $~/f2 + depdb string "$f1" + f2 = $~/f3 + depdb string "$f2" + foo "$f1" "$f2" + EOI + + : preamble-yes + : + $* <true + f1 = $~/f1 + depdb string "$f1" + f2 = $~/f2 + foo "$f2" + EOI +} + +: command +: +$* <>EOF + foo >| 2>- &a &?b + foo >=c 2>~/error:.*/ &!c + foo >>:/~%EOS% + %.* + abc + %xyz.*% + EOS + EOF + +: if-else +: +$* <>EOF + if foo + bar + elif fox + if fix + baz + end + biz + end + if! foo + bar + elif! fox + baz + end + EOF + +: quoting +: +$* <>EOO + foo 'bar' "baz" '' "" + "$foo" + "foo$bar" + "fo"o + "foo"\" + "foo\\" + "foo\"<" + fo\"o + fo\\o + fo\>EOE != 0 + buildfile:11:4: error: expected newline instead of + EOE 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 + : + $* <>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 + : + $* <>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 + : + $* <>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 + : + $* <false + f = foo + depdb hash "$f" + f = $~/f + foo "$f" + EOI + + : yes + : + $* <true + f = $~/f + depdb hash "$f" + foo "$f" + EOI + + : yes-mult + : + $* <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 @@ $* <>~%EOO% : ambiguity : { -: name -: + : name + : $* test <>EOE != 0 echo abc diag xyz @@ -54,6 +54,29 @@ $* <>~%EOO% buildfile:14:1: error: multiple 'diag' builtin calls buildfile:12:1: info: previous call is here EOE + + : names + : + $* <>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 + : + $* <>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 @@ $* <>EOE != 0 EOI buildfile:11:8: error: 'diag' call via 'env' builtin EOE + +: before-depdb +: +$* <>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+line.test.testscript b/libbuild2/build/script/parser+line.test.testscript deleted file mode 100644 index 1b39265..0000000 --- a/libbuild2/build/script/parser+line.test.testscript +++ /dev/null @@ -1,78 +0,0 @@ -# file : libbuild2/build/script/parser+line.test.testscript -# license : MIT; see accompanying LICENSE file - -test.options += -d - -: command -: -$* <>EOF - foo >| 2>- &a &?b - foo >=c 2>~/error:.*/ &!c - foo >>:/~%EOS% - %.* - abc - %xyz.*% - EOS - EOF - -: if-else -: -$* <>EOF - if foo - bar - elif fox - if fix - baz - end - biz - end - if! foo - bar - elif! fox - baz - end - EOF - -: quoting -: -$* <>EOO - foo 'bar' "baz" '' "" - "$foo" - "foo$bar" - "fo"o - "foo"\" - "foo\\" - "foo\"<" - fo\"o - fo\\o - fo\>EOE != 0 - buildfile:11:4: error: expected newline instead of - 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 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 (&tg); - scope_ = const_cast (&tg.base_scope ()); - root_ = scope_->root_scope (); + target_ = const_cast (&tg); + actions_ = &acts; + scope_ = const_cast (&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 (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 ()); // ... + + const string& cmd (ns[0].value); + + if (cmd == "hash") + { + sha256 cs; + for (auto i (ns.begin () + 1); i != ns.end (); ++i) // Skip . + 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 ( + 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 (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 (&bs); pbase_ = scope_->src_path_; - script_ = const_cast (&s); - runner_ = &r; + script_ = const_cast (s); + runner_ = r; environment_ = &e; - - exec_script (); } void parser:: - exec_script () + exec_lines (const lines& lns, + const 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 (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 (&rs); - scope_ = const_cast (&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 #include +#include #include #include @@ -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 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&); + + 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 - Track the argument (single) change as string. // - optional depdb_clear_; // 'depdb clear' location if any. - lines depdb_lines_; // Note: excludes 'depdb clear'. + optional 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 [] // // 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 [/] 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 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 ("~")) = temp_dir.path; - } + + assign (var_pool.insert ("~")) = 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 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 script_deadline; optional 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&&, -- cgit v1.1