aboutsummaryrefslogtreecommitdiff
path: root/libbuild2/parser.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'libbuild2/parser.cxx')
-rw-r--r--libbuild2/parser.cxx162
1 files changed, 107 insertions, 55 deletions
diff --git a/libbuild2/parser.cxx b/libbuild2/parser.cxx
index cd6fe5e..a5c34d9 100644
--- a/libbuild2/parser.cxx
+++ b/libbuild2/parser.cxx
@@ -222,37 +222,53 @@ namespace build2
}
void parser::
- parse_buildfile (istream& is, const path_name& in, scope& root, scope& base)
+ parse_buildfile (istream& is,
+ const path_name& in,
+ scope* root,
+ scope& base,
+ target* tgt,
+ prerequisite* prq)
{
lexer l (is, in);
- parse_buildfile (l, root, base);
+ parse_buildfile (l, root, base, tgt, prq);
}
void parser::
- parse_buildfile (lexer& l, scope& root, scope& base)
+ parse_buildfile (lexer& l,
+ scope* root,
+ scope& base,
+ target* tgt,
+ prerequisite* prq)
{
path_ = &l.name ();
lexer_ = &l;
- root_ = &root;
+ root_ = root;
scope_ = &base;
- target_ = nullptr;
- prerequisite_ = nullptr;
+ target_ = tgt;
+ prerequisite_ = prq;
pbase_ = scope_->src_path_;
- enter_buildfile (*path_); // Needs scope_.
+ if (path_->path != nullptr)
+ enter_buildfile (*path_->path); // Note: needs scope_.
token t;
type tt;
next (t, tt);
- parse_clause (t, tt);
+ if (target_ != nullptr || prerequisite_ != nullptr)
+ {
+ parse_variable_block (t, tt);
+ }
+ else
+ {
+ parse_clause (t, tt);
+ process_default_target (t);
+ }
if (tt != type::eos)
fail (t) << "unexpected " << t;
-
- process_default_target (t);
}
token parser::
@@ -385,7 +401,9 @@ namespace build2
{
f = &parser::parse_run;
}
- else if (n == "import")
+ else if (n == "import" ||
+ n == "import?" ||
+ n == "import!")
{
f = &parser::parse_import;
}
@@ -892,7 +910,7 @@ namespace build2
// NULL, then this is a target type/pattern-specific block.
//
// enter: first token of first line in the block (normal lexer mode)
- // leave: rcbrace
+ // leave: rcbrace or eos
//
// This is a more restricted variant of parse_clause() that only allows
// variable assignments.
@@ -1098,6 +1116,8 @@ namespace build2
//
name n (tt != type::colon ? move (pn) : pn);
+ // See also scope::find_prerequisite_key().
+ //
auto rp (scope_->find_target_type (n, ploc));
const target_type* tt (rp.first);
optional<string>& e (rp.second);
@@ -1210,7 +1230,7 @@ namespace build2
{
next (t, tt); // First token inside the block.
- parse_variable_block (t, tt, nullptr, string ());
+ parse_variable_block (t, tt);
if (tt != type::rcbrace)
fail (t) << "expected '}' instead of " << t;
@@ -1318,7 +1338,7 @@ namespace build2
{
next (t, tt); // First token inside the block.
- parse_variable_block (t, tt, nullptr, string ());
+ parse_variable_block (t, tt);
if (tt != type::rcbrace)
fail (t) << "expected '}' instead of " << t;
@@ -1333,18 +1353,14 @@ namespace build2
}
void parser::
- source (istream& is,
- const path_name& in,
- const location& loc,
- bool enter,
- bool deft)
+ source (istream& is, const path_name& in, const location& loc, bool deft)
{
tracer trace ("parser::source", &path_);
l5 ([&]{trace (loc) << "entering " << in;});
- if (enter)
- enter_buildfile (in);
+ if (in.path != nullptr)
+ enter_buildfile (*in.path);
const path_name* op (path_);
path_ = &in;
@@ -1417,7 +1433,6 @@ namespace build2
source (ifs,
path_name (p),
get_location (t),
- true /* enter */,
false /* default_target */);
}
catch (const io_error& e)
@@ -1558,8 +1573,7 @@ namespace build2
source (ifs,
path_name (p),
get_location (t),
- true /* enter */,
- true /* default_target */);
+ true /* default_target */);
}
catch (const io_error& e)
{
@@ -1642,7 +1656,6 @@ namespace build2
source (is,
path_name ("<stdout>"),
l,
- false /* enter */,
false /* default_target */);
}
@@ -1689,13 +1702,10 @@ namespace build2
// the config[.**].<project>.** pattern where <project> is the innermost
// named project.
//
- // Note that we currently don't allow just the config.<project> name even
- // though this is used quite liberally in build system modules. Allowing
- // this will complicate the logic (and documentation) a bit and there are
- // no obvious use-cases. On the other hand, for tools that could be used
- // during the build (say yacc), such a variable would most likely be used
- // to specify its location (say config.yacc) . So let's "reserve" it for
- // now.
+ // Note that we also allow just the config.<project> name which can be
+ // used by tools (such as source code generators) that use themselves in
+ // their own build. This is a bit of an advanced/experimental setup so
+ // we leave this undocumented for now.
//
// What should we do if there is no named project? We used to fail but
// there are valid cases where this can happen, for example, a standalone
@@ -1788,7 +1798,7 @@ namespace build2
name.compare (0, 7, "config.") != 0)
{
if (!as.empty ())
- fail (t) << "unexpected attributes for report-only variable";
+ fail (as.loc) << "unexpected attributes for report-only variable";
attributes_pop ();
@@ -1822,9 +1832,15 @@ namespace build2
if (!proj.empty ())
{
- if (name.find ('.' + proj + '.') == string::npos)
+ size_t p (name.find ('.' + proj));
+
+ if (p == string::npos ||
+ ((p += proj.size () + 1) != name.size () && // config.<proj>
+ name[p] != '.')) // config.<proj>.
+ {
dr << fail (t) << "configuration variable '" << name
<< "' does not include project name";
+ }
}
if (!dr.empty ())
@@ -1937,8 +1953,11 @@ namespace build2
// General import format:
//
- // import [<var>=](<project>|<project>/<target>])+
+ // import[?!] [<attrs>] [<var>=](<project>|<project>%<target>])+
//
+ bool opt (t.value.back () == '?');
+ bool ph2 (opt || t.value.back () == '!');
+
type atype; // Assignment type.
value* val (nullptr);
const variable* var (nullptr);
@@ -1950,13 +1969,38 @@ namespace build2
// switch to the value mode, get the first token, and then re-parse it
// manually looking for =/=+/+=.
//
+ // Note that if we ever wanted to support value attributes, that would be
+ // non-trivial.
+ //
mode (lexer_mode::value, '@');
next_with_attributes (t, tt);
- // Get variable attributes, if any (note that here we will go into a
- // nested value mode with a different pair character).
+ // Get variable (or value) attributes, if any, and deal with the special
+ // metadata attribute. Since currently it can only appear in the import
+ // directive, we handle it in an ad hoc manner.
//
- auto at (attributes_push (t, tt));
+ attributes_push (t, tt);
+ attributes& as (attributes_top ());
+
+ bool meta (false);
+ for (auto i (as.begin ()); i != as.end (); )
+ {
+ if (i->name == "metadata")
+ {
+ if (!ph2)
+ fail (as.loc) << "loading metadata requires immediate import" <<
+ info << "consider using the import! directive instead";
+
+ meta = true;
+ }
+ else
+ {
+ ++i;
+ continue;
+ }
+
+ i = as.erase (i);
+ }
const location vloc (get_location (t));
@@ -1964,8 +2008,9 @@ namespace build2
{
// Split the token into the variable name and value at position (p) of
// '=', taking into account leading/trailing '+'. The variable name is
- // returned while the token is set to value. If the resulting token
- // value is empty, get the next token. Also set assignment type (at).
+ // returned while the token is set to the value part. If the resulting
+ // token value is empty, get the next token. Also set assignment type
+ // (at).
//
auto split = [&atype, &t, &tt, this] (size_t p) -> string
{
@@ -2042,10 +2087,10 @@ namespace build2
}
else
{
- if (at.first)
- fail (at.second) << "attributes without variable";
- else
- attributes_pop ();
+ if (!as.empty ())
+ fail (as.loc) << "attributes without variable";
+
+ attributes_pop ();
}
// The rest should be a list of projects and/or targets. Parse them as
@@ -2064,19 +2109,27 @@ namespace build2
// import() will check the name, if required.
//
- names r (import (*scope_, move (n), l));
+ names r (import (*scope_, move (n), ph2, opt, meta, l).first);
if (val != nullptr)
{
- if (atype == type::assign)
+ if (r.empty ()) // Optional not found.
{
- val->assign (move (r), var);
- atype = type::append; // Append subsequent values.
+ if (atype == type::assign)
+ *val = nullptr;
}
- else if (atype == type::prepend)
- val->prepend (move (r), var);
else
- val->append (move (r), var);
+ {
+ if (atype == type::assign)
+ val->assign (move (r), var);
+ else if (atype == type::prepend)
+ val->prepend (move (r), var);
+ else
+ val->append (move (r), var);
+ }
+
+ if (atype == type::assign)
+ atype = type::append; // Append subsequent values.
}
}
@@ -6170,11 +6223,10 @@ namespace build2
}
void parser::
- enter_buildfile (const path_name& pn)
+ enter_buildfile (const path& p)
{
tracer trace ("parser::enter_buildfile", &path_);
- const path& p (pn.path != nullptr ? *pn.path : path ());
dir_path d (p.directory ()); // Empty for a path name with the NULL path.
// Figure out if we need out.
@@ -6190,8 +6242,8 @@ namespace build2
ctx.targets.insert<buildfile> (
move (d),
move (out),
- pn.name ? *pn.name : p.leaf ().base ().string (),
- p.extension (), // Always specified.
+ p.leaf ().base ().string (),
+ p.extension (), // Always specified.
trace);
}