From ab6917b035a8431a13d85396ed15b75dabe44b36 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Fri, 24 Apr 2020 15:11:51 +0200 Subject: Don't switch projects when switching scopes during bootstrap --- libbuild2/config/init.cxx | 5 ++++- libbuild2/file.cxx | 22 ++++++++++++---------- libbuild2/file.hxx | 22 ++++++++++++++++------ libbuild2/parser.cxx | 18 +++++++++++++----- libbuild2/parser.hxx | 10 ++-------- 5 files changed, 47 insertions(+), 30 deletions(-) diff --git a/libbuild2/config/init.cxx b/libbuild2/config/init.cxx index f0f4841..aa2c763 100644 --- a/libbuild2/config/init.cxx +++ b/libbuild2/config/init.cxx @@ -215,7 +215,10 @@ namespace build2 info << "consider reconfiguring " << project (rs) << '@' << rs.out_path (); - source (rs, rs, lex); + // Treat it as continuation of bootstrap to avoid project switching + // (see switch_scope() for details). + // + source (rs, rs, lex, load_stage::boot); }; auto load_config_file = [&load_config] (const path& f, const location& l) diff --git a/libbuild2/file.cxx b/libbuild2/file.cxx index cbad9ec..0bf3fd4 100644 --- a/libbuild2/file.cxx +++ b/libbuild2/file.cxx @@ -239,9 +239,9 @@ namespace build2 } void - source (scope& root, scope& base, lexer& l) + source (scope& root, scope& base, lexer& l, load_stage s) { - parser p (root.ctx); + parser p (root.ctx, s); source (p, root, base, l); } @@ -412,16 +412,17 @@ namespace build2 } pair - switch_scope (scope& root, const dir_path& p) + switch_scope (scope& root, const dir_path& p, bool proj) { // First, enter the scope into the map and see if it is in any project. If // it is not, then there is nothing else to do. // auto i (root.ctx.scopes.rw (root).insert (p)); scope& base (i->second); - scope* rs (base.root_scope ()); - if (rs != nullptr) + scope* rs (nullptr); + + if (proj && (rs = base.root_scope ()) != nullptr) { // Path p can be src_base or out_base. Figure out which one it is. // @@ -536,7 +537,8 @@ namespace build2 // prevent multiple sourcing. We handle it here but we still need // something like source_once (once [scope] source) in buildfiles. // - source_once (root, root, f); + parser p (root.ctx, load_stage::boot); + source_once (p, root, root, f, root); } pair @@ -814,7 +816,7 @@ namespace build2 // if (rs.buildfiles.insert (f).second) { - parser p (rs.ctx, parser::stage::boot); + parser p (rs.ctx, load_stage::boot); source (p, rs, rs, f); } else @@ -1047,7 +1049,7 @@ namespace build2 if (root.root_extra == nullptr) setup_root_extra (root, altn); - parser p (root.ctx, parser::stage::boot); + parser p (root.ctx, load_stage::boot); source_hooks (p, root, d, true /* pre */); } } @@ -1061,7 +1063,7 @@ namespace build2 if (exists (d)) { - parser p (root.ctx, parser::stage::boot); + parser p (root.ctx, load_stage::boot); source_hooks (p, root, d, false /* pre */); } } @@ -1294,7 +1296,7 @@ namespace build2 // Reuse the parser to accumulate the configuration variable information. // - parser p (root.ctx, parser::stage::root); + parser p (root.ctx, load_stage::root); if (he) {source_hooks (p, root, hd, true /* pre */); p.reset ();} if (fe) {source_once (p, root, root, f, root);} diff --git a/libbuild2/file.hxx b/libbuild2/file.hxx index 2ca72b1..b44efb6 100644 --- a/libbuild2/file.hxx +++ b/libbuild2/file.hxx @@ -67,6 +67,15 @@ namespace build2 LIBBUILD2_SYMEXPORT pair find_out_root (const dir_path&, optional& altn); + // Project's loading stage during which the parsing is performed. + // + enum class load_stage + { + boot, // Loading bootstrap.build (or bootstrap pre/post hooks). + root, // Loading root.build (or root pre/post hooks). + rest // Loading the rest (ordinary buildfiles, command line, etc). + }; + // If buildfile is '-', then read from STDIN. // LIBBUILD2_SYMEXPORT void @@ -82,7 +91,7 @@ namespace build2 // stdin that requires parse_variable()). // LIBBUILD2_SYMEXPORT void - source (scope& root, scope& base, lexer&); + source (scope& root, scope& base, lexer&, load_stage = load_stage::rest); // As above but first check if this buildfile has already been sourced for // the base scope. Return false if the file has already been sourced. @@ -115,13 +124,14 @@ namespace build2 const dir_path& out_base, const dir_path& src_base); - // Return a scope for the specified directory (first). Note that switching - // to this scope might also involve switch to a new root scope (second) if - // the new scope is in another project. If the new scope is not in any - // project, then NULL is returned in second. + // Return a scope for the specified directory (first). If project is true + // then switching to this scope might also involve switch to a new root + // scope (second) if the new scope is in another project. If project is + // false or the new scope is not in any project, then NULL is returned in + // second. // LIBBUILD2_SYMEXPORT pair - switch_scope (scope& root, const dir_path&); + switch_scope (scope& root, const dir_path&, bool project = true); // Bootstrap and optionally load an ad hoc (sub)project (i.e., the kind that // is not discovered and loaded automatically by bootstrap/load functions diff --git a/libbuild2/parser.cxx b/libbuild2/parser.cxx index e3f9605..cd6fe5e 100644 --- a/libbuild2/parser.cxx +++ b/libbuild2/parser.cxx @@ -10,7 +10,6 @@ #include #include -#include #include #include #include @@ -1435,7 +1434,7 @@ namespace build2 { tracer trace ("parser::parse_include", &path_); - if (root_->src_path_ == nullptr) + if (stage_ == stage::boot) fail (t) << "inclusion during bootstrap"; // The rest should be a list of buildfiles. Parse them as names in the @@ -1933,7 +1932,7 @@ namespace build2 { tracer trace ("parser::parse_import", &path_); - if (root_->src_path_ == nullptr) + if (stage_ == stage::boot) fail (t) << "import during bootstrap"; // General import format: @@ -6090,11 +6089,20 @@ namespace build2 { tracer trace ("parser::switch_scope", &path_); - auto p (build2::switch_scope (*root_, d)); + // Switching the project during bootstrap can result in bizarre nesting + // with unexpected loading order (e.g., config.build are loaded from inner + // to outter rather than the expected reverse). On the other hand, it can + // be handy to assign a variable for a nested scope in config.build. So + // for this stage we are going to switch the scope without switching the + // project expecting the user to know what they are doing. + // + bool proj (stage_ != stage::boot); + + auto p (build2::switch_scope (*root_, d, proj)); scope_ = &p.first; pbase_ = scope_->src_path_ != nullptr ? scope_->src_path_ : &d; - if (p.second != root_) + if (proj && p.second != root_) { root_ = p.second; l5 ([&] diff --git a/libbuild2/parser.hxx b/libbuild2/parser.hxx index b21336d..6552114 100644 --- a/libbuild2/parser.hxx +++ b/libbuild2/parser.hxx @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -21,14 +22,7 @@ namespace build2 class LIBBUILD2_SYMEXPORT parser { public: - // 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). - }; + using stage = load_stage; explicit parser (context& c, stage s = stage::rest) -- cgit v1.1