aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2020-03-25 07:36:31 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2020-03-25 07:36:31 +0200
commit0588e48ac1499388f4d2ad5bc03fe9f63782f161 (patch)
tree8a1785909c41dc159dce30f3682b84aa4f771661
parentd1663e509e8cc77a8fbb8345bb1fbccacacf2429 (diff)
Enforce config directives only appearing in project's root.build
-rw-r--r--libbuild2/file.cxx64
-rw-r--r--libbuild2/parser.cxx13
-rw-r--r--libbuild2/parser.hxx16
3 files changed, 54 insertions, 39 deletions
diff --git a/libbuild2/file.cxx b/libbuild2/file.cxx
index fb5b68a..7fd3f78 100644
--- a/libbuild2/file.cxx
+++ b/libbuild2/file.cxx
@@ -159,8 +159,10 @@ namespace build2
}
}
+ using stage = parser::stage;
+
static void
- source (scope& root, scope& base, lexer& l, bool boot)
+ source (scope& root, scope& base, lexer& l, stage s)
{
tracer trace ("source");
@@ -170,7 +172,7 @@ namespace build2
{
l5 ([&]{trace << "sourcing " << fn;});
- parser p (root.ctx, boot);
+ parser p (root.ctx, s);
p.parse_buildfile (l, root, base);
}
catch (const io_error& e)
@@ -179,25 +181,25 @@ namespace build2
}
}
- static void
+ static inline void
source (scope& root,
scope& base,
istream& is,
const path_name& in,
- bool boot)
+ stage s)
{
lexer l (is, in);
- source (root, base, l, boot);
+ source (root, base, l, s);
}
static void
- source (scope& root, scope& base, const path& bf, bool boot)
+ source (scope& root, scope& base, const path& bf, stage s)
{
path_name fn (bf);
try
{
ifdstream ifs;
- return source (root, base, open_file_or_stdin (fn, ifs), fn, boot);
+ return source (root, base, open_file_or_stdin (fn, ifs), fn, s);
}
catch (const io_error& e)
{
@@ -205,37 +207,43 @@ namespace build2
}
}
+ static bool
+ source_once (scope& root, scope& base, const path& bf, scope& once, stage s)
+ {
+ tracer trace ("source_once");
+
+ if (!once.buildfiles.insert (bf).second)
+ {
+ l5 ([&]{trace << "skipping already sourced " << bf;});
+ return false;
+ }
+
+ source (root, base, bf, s);
+ return true;
+ }
+
void
source (scope& root, scope& base, const path& bf)
{
- source (root, base, bf, false);
+ source (root, base, bf, stage::rest);
}
void
source (scope& root, scope& base, istream& is, const path_name& in)
{
- source (root, base, is, in, false /* boot */);
+ source (root, base, is, in, stage::rest);
}
void
source (scope& root, scope& base, lexer& l)
{
- source (root, base, l, false);
+ source (root, base, l, stage::rest);
}
bool
source_once (scope& root, scope& base, const path& bf, scope& once)
{
- tracer trace ("source_once");
-
- if (!once.buildfiles.insert (bf).second)
- {
- l5 ([&]{trace << "skipping already sourced " << bf;});
- return false;
- }
-
- source (root, base, bf, false);
- return true;
+ return source_once (root, base, bf, once, stage::rest);
}
// Source (once) pre-*.build (pre is true) or post-*.build (otherwise) hooks
@@ -243,7 +251,7 @@ namespace build2
// must exist.
//
static void
- source_hooks (scope& root, const dir_path& d, bool pre)
+ source_hooks (scope& root, const dir_path& d, stage s, bool pre)
{
// While we could have used the wildcard pattern matching functionality,
// our needs are pretty basic and performance is quite important, so let's
@@ -278,7 +286,7 @@ namespace build2
fail << "unable to read buildfile " << f << ": " << e;
}
- source_once (root, root, f);
+ source_once (root, root, f, root, s);
}
}
catch (const system_error& e)
@@ -799,7 +807,7 @@ namespace build2
// root scope multiple time.
//
if (rs.buildfiles.insert (f).second)
- source (rs, rs, f, true /* boot */);
+ source (rs, rs, f, stage::boot);
else
l5 ([&]{trace << "skipping already sourced " << f;});
@@ -1030,7 +1038,7 @@ namespace build2
if (root.root_extra == nullptr)
setup_root_extra (root, altn);
- source_hooks (root, d, true /* pre */);
+ source_hooks (root, d, stage::boot, true /* pre */);
}
}
@@ -1042,7 +1050,7 @@ namespace build2
dir_path d (out_root / root.root_extra->bootstrap_dir);
if (exists (d))
- source_hooks (root, d, false /* pre */);
+ source_hooks (root, d, stage::boot, false /* pre */);
}
bool
@@ -1268,9 +1276,9 @@ namespace build2
dir_path hd (out_root / root.root_extra->root_dir);
bool he (exists (hd));
- if (he) source_hooks (root, hd, true /* pre */);
- if (exists (f)) source_once (root, root, f);
- if (he) source_hooks (root, hd, false /* pre */);
+ if (he) source_hooks (root, hd, stage::root, true /* pre */);
+ if (exists (f)) source_once (root, root, f, root, stage::root);
+ if (he) source_hooks (root, hd, stage::root, false /* pre */);
}
scope&
diff --git a/libbuild2/parser.cxx b/libbuild2/parser.cxx
index f20a4c4..fbf3bda 100644
--- a/libbuild2/parser.cxx
+++ b/libbuild2/parser.cxx
@@ -1655,12 +1655,13 @@ namespace build2
// config [<var-attrs>] <var>[?=[<val-attrs>]<default-val>]
//
- // @@ TODO: enforce appears in root.build
+ // Make sure only appears in root.build.
//
- if (root_ != scope_)
- fail (t) << "configuration variable in non-root scope";
+ if (stage_ != stage::root)
+ fail (t) << "configuration variable outside of project's "
+ << root_->root_extra->root_file;
- // We enforce the config.<project> prefix.
+ // Enforce the config.<project> prefix.
//
// Note that this could be a subproject and it could be unnamed (e.g., the
// tests subproject). The current thinking is to use hierarchical names
@@ -1982,7 +1983,7 @@ namespace build2
bool optional (t.value.back () == '?');
- if (optional && boot_)
+ if (optional && stage_ == stage::boot)
fail (t) << "optional module in bootstrap";
// The rest should be a list of module names. Parse them as names in the
@@ -2035,7 +2036,7 @@ namespace build2
{
assert (v.empty ()); // Module versioning not yet implemented.
- if (boot_)
+ if (stage_ == stage::boot)
boot_module (*root_, n, l);
else
init_module (*root_, *scope_, n, l, optional);
diff --git a/libbuild2/parser.hxx b/libbuild2/parser.hxx
index dd5cbda..ba707da 100644
--- a/libbuild2/parser.hxx
+++ b/libbuild2/parser.hxx
@@ -23,12 +23,18 @@ namespace build2
class LIBBUILD2_SYMEXPORT parser
{
public:
- // If boot is true, then we are parsing bootstrap.build and modules
- // should only be bootstrapped.
+ // The project's loading stage during which the parsing is performed.
//
+ enum class stage
+ {
+ boot, // Parsing bootstrap.build (or bootstrap pre/post hooks).
+ root, // Parsing root.build (or root pre/post hooks).
+ rest // Parsing the rest (ordinary buildfiles, command line, etc).
+ };
+
explicit
- parser (context& c, bool boot = false)
- : fail ("error", &path_), ctx (c), boot_ (boot) {}
+ parser (context& c, stage s = stage::rest)
+ : fail ("error", &path_), ctx (c), stage_ (s) {}
// Issue diagnostics and throw failed in case of an error.
//
@@ -699,9 +705,9 @@ namespace build2
protected:
context& ctx;
+ stage stage_;
bool pre_parse_ = false;
- bool boot_;
const path_name* path_; // Current path name.
lexer* lexer_;