aboutsummaryrefslogtreecommitdiff
path: root/build2
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2016-10-25 16:47:00 +0300
committerBoris Kolpackov <boris@codesynthesis.com>2016-11-04 09:26:35 +0200
commitb61e9e2ba8e625a598427cc2990806b69d104a18 (patch)
tree2e4b492a2ec00b325f52753ffa456f6f90eb78f2 /build2
parente3ff4880273746c34d07e641110abaf38a1f1fca (diff)
Add support of file redirects to testscript parser
Diffstat (limited to 'build2')
-rw-r--r--build2/test/script/lexer.cxx25
-rw-r--r--build2/test/script/parser.cxx91
-rw-r--r--build2/test/script/runner.cxx13
-rw-r--r--build2/test/script/script39
-rw-r--r--build2/test/script/script.cxx132
-rw-r--r--build2/test/script/token5
-rw-r--r--build2/test/script/token.cxx49
7 files changed, 296 insertions, 58 deletions
diff --git a/build2/test/script/lexer.cxx b/build2/test/script/lexer.cxx
index 7ced2b9..e5514bd 100644
--- a/build2/test/script/lexer.cxx
+++ b/build2/test/script/lexer.cxx
@@ -264,10 +264,13 @@ namespace build2
{
p = peek ();
- if (p == ':')
+ if (p == ':' || p == '<')
{
get ();
- return make_token (type::in_doc_nn);
+
+ return make_token (p == ':'
+ ? type::in_doc_nn
+ : type::in_file);
}
else
return make_token (type::in_doc);
@@ -297,10 +300,24 @@ namespace build2
{
p = peek ();
- if (p == ':')
+ if (p == ':' || p == '>')
{
get ();
- return make_token (type::out_doc_nn);
+
+ if (p == ':')
+ return make_token (type::out_doc_nn);
+
+ // File redirect.
+ //
+ p = peek ();
+
+ if (p == '&')
+ {
+ get ();
+ return make_token (type::out_file_app);
+ }
+ else
+ return make_token (type::out_file);
}
else
return make_token (type::out_doc);
diff --git a/build2/test/script/parser.cxx b/build2/test/script/parser.cxx
index 424029f..450b5ac 100644
--- a/build2/test/script/parser.cxx
+++ b/build2/test/script/parser.cxx
@@ -409,13 +409,17 @@ namespace build2
program,
in_string,
in_document,
+ in_file,
out_string,
out_document,
+ out_file,
err_string,
- err_document
+ err_document,
+ err_file
};
pending p (pending::program);
- bool nn (false); // True if pending here-{str,doc} is "no-newline".
+ bool nn (false); // True if pending here-{str,doc} is "no-newline".
+ bool app (false); // True if to append to pending file.
// Ordered sequence of here-document redirects that we can expect to
// see after the command line.
@@ -432,12 +436,12 @@ namespace build2
// to program arguments by default.
//
auto add_word =
- [&c, &p, &nn, &hd, this] (string&& w, const location& l)
+ [&c, &p, &nn, &app, &hd, this] (string&& w, const location& l)
{
- auto add_here_str = [&hd, &nn] (redirect& r, string&& w)
+ auto add_here_str = [&nn] (redirect& r, string&& w)
{
if (!nn) w += '\n';
- r.value = move (w);
+ r.str = move (w);
};
auto add_here_end = [&hd, &nn] (redirect& r, string&& w)
@@ -445,6 +449,26 @@ namespace build2
hd.push_back (here_doc {&r, move (w), nn});
};
+ auto add_file =
+ [&app, &l, this] (redirect& r, const char* n, string&& w)
+ {
+ try
+ {
+ r.file.path = path (move (w));
+
+ if (r.file.path.empty ())
+ fail (l) << "empty " << n << " redirect file path";
+
+ }
+ catch (const invalid_path& e)
+ {
+ fail (l) << "invalid " << n << "redirect file path '" << e.path
+ << "'";
+ }
+
+ r.file.append = app;
+ };
+
switch (p)
{
case pending::none: c.arguments.push_back (move (w)); break;
@@ -471,10 +495,15 @@ namespace build2
case pending::in_string: add_here_str (c.in, move (w)); break;
case pending::out_string: add_here_str (c.out, move (w)); break;
case pending::err_string: add_here_str (c.err, move (w)); break;
+
+ case pending::in_file: add_file (c.in, "stdin", move (w)); break;
+ case pending::out_file: add_file (c.out, "stdout", move (w)); break;
+ case pending::err_file: add_file (c.err, "stderr", move (w)); break;
}
p = pending::none;
nn = false;
+ app = false;
};
// Make sure we don't have any pending positions to fill.
@@ -489,10 +518,13 @@ namespace build2
case pending::program: what = "program"; break;
case pending::in_string: what = "stdin here-string"; break;
case pending::in_document: what = "stdin here-document end"; break;
+ case pending::in_file: what = "stdin file"; break;
case pending::out_string: what = "stdout here-string"; break;
case pending::out_document: what = "stdout here-document end"; break;
+ case pending::out_file: what = "stdout file"; break;
case pending::err_string: what = "stderr here-string"; break;
case pending::err_document: what = "stderr here-document end"; break;
+ case pending::err_file: what = "stderr file"; break;
}
if (what != nullptr)
@@ -502,11 +534,11 @@ namespace build2
// Parse the redirect operator.
//
auto parse_redirect =
- [&c, &p, &nn, this] (const token& t, const location& l)
+ [&c, &p, &nn, &app, this] (const token& t, const location& l)
{
// Our semantics is the last redirect seen takes effect.
//
- assert (p == pending::none && !nn);
+ assert (p == pending::none && !nn && !app);
// See if we have the file descriptor.
//
@@ -546,6 +578,7 @@ namespace build2
case type::in_str_nn:
case type::in_doc:
case type::in_doc_nn:
+ case type::in_file:
{
if ((fd = fd == 3 ? 0 : fd) != 0)
fail (l) << "invalid in redirect file descriptor " << fd;
@@ -558,6 +591,8 @@ namespace build2
case type::out_str_nn:
case type::out_doc:
case type::out_doc_nn:
+ case type::out_file:
+ case type::out_file_app:
{
if ((fd = fd == 3 ? 1 : fd) == 0)
fail (l) << "invalid out redirect file descriptor " << fd;
@@ -570,24 +605,28 @@ namespace build2
switch (tt)
{
case type::in_pass:
- case type::out_pass: rt = redirect_type::pass; break;
+ case type::out_pass: rt = redirect_type::pass; break;
case type::in_null:
- case type::out_null: rt = redirect_type::null; break;
+ case type::out_null: rt = redirect_type::null; break;
case type::in_str_nn:
- case type::out_str_nn: nn = true; // Fall through.
+ case type::out_str_nn: nn = true; // Fall through.
case type::in_str:
- case type::out_str: rt = redirect_type::here_string; break;
+ case type::out_str: rt = redirect_type::here_string; break;
case type::in_doc_nn:
- case type::out_doc_nn: nn = true; // Fall through.
+ case type::out_doc_nn: nn = true; // Fall through.
case type::in_doc:
- case type::out_doc: rt = redirect_type::here_document; break;
+ case type::out_doc: rt = redirect_type::here_document; break;
+
+ case type::out_file_app: app = true; // Fall through.
+ case type::in_file:
+ case type::out_file: rt = redirect_type::file; break;
}
redirect& r (fd == 0 ? c.in : fd == 1 ? c.out : c.err);
- r.type = rt;
+ r = redirect (rt);
switch (rt)
{
@@ -611,6 +650,14 @@ namespace build2
case 2: p = pending::err_document; break;
}
break;
+ case redirect_type::file:
+ switch (fd)
+ {
+ case 0: p = pending::in_file; break;
+ case 1: p = pending::out_file; break;
+ case 2: p = pending::err_file; break;
+ }
+ break;
}
};
@@ -650,6 +697,10 @@ namespace build2
case type::in_doc_nn:
case type::out_str_nn:
case type::out_doc_nn:
+
+ case type::in_file:
+ case type::out_file:
+ case type::out_file_app:
{
if (pre_parse_)
{
@@ -709,6 +760,10 @@ namespace build2
case type::out_str_nn:
case type::out_doc_nn:
+ case type::in_file:
+ case type::out_file:
+ case type::out_file_app:
+
parse_redirect (t, l);
next (t, tt);
break;
@@ -846,6 +901,10 @@ namespace build2
case type::in_str_nn:
case type::out_str_nn:
+
+ case type::in_file:
+ case type::out_file:
+ case type::out_file_app:
{
parse_redirect (t, l);
break;
@@ -916,8 +975,8 @@ namespace build2
if (!pre_parse_)
{
redirect& r (*h.redir);
- r.value = move (v);
- r.here_end = move (h.end);
+ r.doc.doc = move (v);
+ r.doc.end = move (h.end);
}
expire_mode ();
diff --git a/build2/test/script/runner.cxx b/build2/test/script/runner.cxx
index 995da4f..f065389 100644
--- a/build2/test/script/runner.cxx
+++ b/build2/test/script/runner.cxx
@@ -64,7 +64,11 @@ namespace build2
{
ofdstream os (orp);
sp.cleanups.emplace_back (orp);
- os << rd.value;
+
+ os << (rd.type == redirect_type::here_string
+ ? rd.str
+ : rd.doc.doc);
+
os.close ();
}
catch (const io_error& e)
@@ -298,11 +302,12 @@ namespace build2
so.close ();
se.close ();
- if (c.in.type == redirect_type::here_string ||
- c.in.type == redirect_type::here_document)
+ const redirect& r (c.in);
+ if (r.type == redirect_type::here_string ||
+ r.type == redirect_type::here_document)
{
ofdstream os (pr.out_fd);
- os << c.in.value;
+ os << (r.type == redirect_type::here_string ? r.str : r.doc.doc);
os.close ();
}
diff --git a/build2/test/script/script b/build2/test/script/script
index 2d74b16..79ada8e 100644
--- a/build2/test/script/script
+++ b/build2/test/script/script
@@ -43,15 +43,44 @@ namespace build2
none,
pass,
null,
- here_string, // Value is the string.
- here_document // Value is the document.
+ here_string,
+ here_document,
+ file
};
struct redirect
{
- redirect_type type = redirect_type::none;
- string value; // Note: includes trailing newline, if required.
- string here_end; // Only for here-documents.
+ redirect_type type;
+
+ struct doc_type
+ {
+ string doc; // Note: includes trailing newline, if required.
+ string end;
+ };
+
+ struct file_type
+ {
+ using path_type = build2::path;
+ path_type path;
+ bool append = false;
+ };
+
+ union
+ {
+ string str; // Note: includes trailing newline, if required.
+ doc_type doc;
+ file_type file;
+ };
+
+ explicit
+ redirect (redirect_type = redirect_type::none);
+
+ redirect (redirect&&);
+ redirect (const redirect&);
+ redirect& operator= (redirect&&);
+ redirect& operator= (const redirect&);
+
+ ~redirect ();
};
enum class exit_comparison {eq, ne};
diff --git a/build2/test/script/script.cxx b/build2/test/script/script.cxx
index 6f303d5..ac11546 100644
--- a/build2/test/script/script.cxx
+++ b/build2/test/script/script.cxx
@@ -14,6 +14,8 @@ namespace build2
{
namespace script
{
+ // Utility functions
+ //
// Quote if empty or contains spaces or any of the special characters.
//
// @@ What if it contains quotes, escapes?
@@ -37,8 +39,7 @@ namespace build2
size_t n (string::traits_type::length (prefix));
assert (n > 0);
- const string& v (r.value);
- bool nl (!v.empty () && v.back () == '\n');
+ char d (prefix[n - 1]); // Redirect direction.
switch (r.type)
{
@@ -48,6 +49,9 @@ namespace build2
case redirect_type::here_string:
{
+ const string& v (r.str);
+ bool nl (!v.empty () && v.back () == '\n');
+
if (!nl)
o << ':';
@@ -56,10 +60,22 @@ namespace build2
}
case redirect_type::here_document:
{
+ const string& v (r.doc.doc);
+ bool nl (!v.empty () && v.back () == '\n');
+
// Add another '>' or '<'. Note that here end marker never
// needs to be quoted.
//
- o << prefix[n - 1] << (nl ? "" : ":") << r.here_end;
+ o << d << (nl ? "" : ":") << r.doc.end;
+ break;
+ }
+ case redirect_type::file:
+ {
+ using build2::operator<<;
+
+ // Add '>>' or '<<' (and so make it '<<<' or '>>>').
+ //
+ o << d << d << (r.file.append ? "&" : "") << r.file.path;
break;
}
}
@@ -67,9 +83,9 @@ namespace build2
auto print_doc = [&o] (const redirect& r)
{
- const string& v (r.value);
+ const string& v (r.doc.doc);
bool nl (!v.empty () && v.back () == '\n');
- o << endl << v << (nl ? "" : "\n") << r.here_end;
+ o << endl << v << (nl ? "" : "\n") << r.doc.end;
};
if ((m & command_to_stream::header) == command_to_stream::header)
@@ -114,6 +130,108 @@ namespace build2
}
}
+ // redirect
+ //
+ redirect::
+ redirect (redirect_type t)
+ : type (t)
+ {
+ switch (type)
+ {
+ case redirect_type::none:
+ case redirect_type::pass:
+ case redirect_type::null: break;
+
+ case redirect_type::here_string: new (&str) string (); break;
+ case redirect_type::here_document: new (&doc) doc_type (); break;
+ case redirect_type::file: new (&file) file_type (); break;
+ }
+ }
+
+ redirect::
+ redirect (redirect&& r)
+ : type (r.type)
+ {
+ switch (type)
+ {
+ case redirect_type::none:
+ case redirect_type::pass:
+ case redirect_type::null: break;
+
+ case redirect_type::here_string:
+ {
+ new (&str) string (move (r.str));
+ break;
+ }
+ case redirect_type::here_document:
+ {
+ new (&doc) doc_type (move (r.doc));
+ break;
+ }
+ case redirect_type::file:
+ {
+ new (&file) file_type (move (r.file));
+ break;
+ }
+ }
+ }
+
+ redirect::
+ redirect (const redirect& r)
+ : type (r.type)
+ {
+ switch (type)
+ {
+ case redirect_type::none:
+ case redirect_type::pass:
+ case redirect_type::null: break;
+
+ case redirect_type::here_string: new (&str) string (r.str); break;
+ case redirect_type::here_document: new (&doc) doc_type (r.doc); break;
+ case redirect_type::file:
+ {
+ new (&file) file_type (r.file);
+ break;
+ }
+ }
+ }
+
+ redirect::
+ ~redirect ()
+ {
+ switch (type)
+ {
+ case redirect_type::none:
+ case redirect_type::pass:
+ case redirect_type::null: break;
+
+ case redirect_type::here_string: str.~string (); break;
+ case redirect_type::here_document: doc.~doc_type (); break;
+ case redirect_type::file: file.~file_type (); break;
+ }
+ }
+
+ redirect& redirect::
+ operator= (redirect&& r)
+ {
+ if (this != &r)
+ {
+ this->~redirect ();
+ new (this) redirect (move (r)); // Assume noexcept move-constructor.
+ }
+ return *this;
+ }
+
+ redirect& redirect::
+ operator= (const redirect& r)
+ {
+ if (this != &r)
+ *this = redirect (r); // Reduce to move-assignment.
+ return *this;
+ }
+
+ // scope
+ //
scope::
scope (const string& id, scope* p)
: parent (p),
@@ -143,6 +261,8 @@ namespace build2
const_cast<dir_path&> (wd_path) = dir_path (p->wd_path) /= id;
}
+ // script_base
+ //
script_base::
script_base ()
: // Enter the test* variables with the same variable types as in
@@ -156,6 +276,8 @@ namespace build2
wd_var (var_pool.insert<dir_path> ("~")),
id_var (var_pool.insert<path> ("@")) {}
+ // script
+ //
static inline string
script_id (const path& p)
{
diff --git a/build2/test/script/token b/build2/test/script/token
index f9352d2..d22b758 100644
--- a/build2/test/script/token
+++ b/build2/test/script/token
@@ -40,13 +40,16 @@ namespace build2
in_str_nn, // <:
in_doc, // <<
in_doc_nn, // <<:
+ in_file, // <<<
out_pass, // >+
out_null, // >-
out_str, // >
out_str_nn, // >:
out_doc, // >>
- out_doc_nn // >>:
+ out_doc_nn, // >>:
+ out_file, // >>>
+ out_file_app // >>>&
};
token_type () = default;
diff --git a/build2/test/script/token.cxx b/build2/test/script/token.cxx
index 549fe13..376a92e 100644
--- a/build2/test/script/token.cxx
+++ b/build2/test/script/token.cxx
@@ -21,29 +21,32 @@ namespace build2
switch (t.type)
{
- case token_type::semi: os << q << ';' << q; break;
-
- case token_type::plus: os << q << '+' << q; break;
- case token_type::minus: os << q << '-' << q; break;
-
- case token_type::pipe: os << q << '|' << q; break;
- case token_type::clean: os << q << '&' << q; break;
- case token_type::log_and: os << q << "&&" << q; break;
- case token_type::log_or: os << q << "||" << q; break;
-
- case token_type::in_pass: os << q << "<+" << q; break;
- case token_type::in_null: os << q << "<-" << q; break;
- case token_type::in_str: os << q << '<' << q; break;
- case token_type::in_str_nn: os << q << "<:" << q; break;
- case token_type::in_doc: os << q << "<<" << q; break;
- case token_type::in_doc_nn: os << q << "<<:" << q; break;
-
- case token_type::out_pass: os << q << ">+" << q; break;
- case token_type::out_null: os << q << ">-" << q; break;
- case token_type::out_str: os << q << '>' << q; break;
- case token_type::out_str_nn: os << q << ">:" << q; break;
- case token_type::out_doc: os << q << ">>" << q; break;
- case token_type::out_doc_nn: os << q << ">>:" << q; break;
+ case token_type::semi: os << q << ';' << q; break;
+
+ case token_type::plus: os << q << '+' << q; break;
+ case token_type::minus: os << q << '-' << q; break;
+
+ case token_type::pipe: os << q << '|' << q; break;
+ case token_type::clean: os << q << '&' << q; break;
+ case token_type::log_and: os << q << "&&" << q; break;
+ case token_type::log_or: os << q << "||" << q; break;
+
+ case token_type::in_pass: os << q << "<+" << q; break;
+ case token_type::in_null: os << q << "<-" << q; break;
+ case token_type::in_str: os << q << '<' << q; break;
+ case token_type::in_str_nn: os << q << "<:" << q; break;
+ case token_type::in_doc: os << q << "<<" << q; break;
+ case token_type::in_doc_nn: os << q << "<<:" << q; break;
+ case token_type::in_file: os << q << "<<<" << q; break;
+
+ case token_type::out_pass: os << q << ">+" << q; break;
+ case token_type::out_null: os << q << ">-" << q; break;
+ case token_type::out_str: os << q << '>' << q; break;
+ case token_type::out_str_nn: os << q << ">:" << q; break;
+ case token_type::out_doc: os << q << ">>" << q; break;
+ case token_type::out_doc_nn: os << q << ">>:" << q; break;
+ case token_type::out_file: os << q << ">>>" << q; break;
+ case token_type::out_file_app: os << q << ">>>&" << q; break;
default: build2::token_printer (os, t, d);
}