From 0588e48ac1499388f4d2ad5bc03fe9f63782f161 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Wed, 25 Mar 2020 07:36:31 +0200 Subject: Enforce config directives only appearing in project's root.build --- libbuild2/file.cxx | 64 +++++++++++++++++++++++++++++----------------------- libbuild2/parser.cxx | 13 ++++++----- libbuild2/parser.hxx | 16 +++++++++---- 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 [] [?=[]] // - // @@ 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. prefix. + // Enforce the config. 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_; -- cgit v1.1