From d06e8d1d3b0594c74fa444da76c3c7925ed58f70 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Tue, 22 Sep 2020 11:28:53 +0200 Subject: Add ability to skip external modules during bootstrap (--no-external-modules) --- build2/b-options.cxx | 15 ++++++++ build2/b-options.hxx | 4 +++ build2/b-options.ixx | 6 ++++ build2/b.cli | 7 ++++ build2/b.cxx | 1 + libbuild2/context.cxx | 2 ++ libbuild2/context.hxx | 5 +++ libbuild2/file.cxx | 8 ++++- libbuild2/module.cxx | 94 +++++++++++++++++++++++++++++++++------------------ 9 files changed, 108 insertions(+), 34 deletions(-) diff --git a/build2/b-options.cxx b/build2/b-options.cxx index 95772bb..316d7ad 100644 --- a/build2/b-options.cxx +++ b/build2/b-options.cxx @@ -698,6 +698,7 @@ namespace build2 serial_stop_ (), dry_run_ (), match_only_ (), + no_external_modules_ (), structured_result_ (), mtime_check_ (), no_mtime_check_ (), @@ -898,6 +899,12 @@ namespace build2 this->match_only_, a.match_only_); } + if (a.no_external_modules_) + { + ::build2::cl::parser< bool>::merge ( + this->no_external_modules_, a.no_external_modules_); + } + if (a.structured_result_) { ::build2::cl::parser< bool>::merge ( @@ -1110,6 +1117,12 @@ namespace build2 << " mode is primarily useful for profiling." << ::std::endl; os << std::endl + << "\033[1m--no-external-modules\033[0m Don't load external modules during project bootstrap." << ::std::endl + << " Note that this option can only be used with" << ::std::endl + << " meta-operations that do not load the project's" << ::std::endl + << " \033[1mbuildfiles\033[0m, such as \033[1minfo\033[0m." << ::std::endl; + + os << std::endl << "\033[1m--structured-result\033[0m Write the result of execution in a structured form. In" << ::std::endl << " this mode, instead of printing to \033[1mSTDERR\033[0m diagnostics" << ::std::endl << " messages about the outcome of executing actions on" << ::std::endl @@ -1267,6 +1280,8 @@ namespace build2 &::build2::cl::thunk< options, bool, &options::dry_run_ >; _cli_options_map_["--match-only"] = &::build2::cl::thunk< options, bool, &options::match_only_ >; + _cli_options_map_["--no-external-modules"] = + &::build2::cl::thunk< options, bool, &options::no_external_modules_ >; _cli_options_map_["--structured-result"] = &::build2::cl::thunk< options, bool, &options::structured_result_ >; _cli_options_map_["--mtime-check"] = diff --git a/build2/b-options.hxx b/build2/b-options.hxx index c6f1f59..d55dd36 100644 --- a/build2/b-options.hxx +++ b/build2/b-options.hxx @@ -526,6 +526,9 @@ namespace build2 match_only () const; const bool& + no_external_modules () const; + + const bool& structured_result () const; const bool& @@ -628,6 +631,7 @@ namespace build2 bool serial_stop_; bool dry_run_; bool match_only_; + bool no_external_modules_; bool structured_result_; bool mtime_check_; bool no_mtime_check_; diff --git a/build2/b-options.ixx b/build2/b-options.ixx index 6444aa9..1d41af2 100644 --- a/build2/b-options.ixx +++ b/build2/b-options.ixx @@ -405,6 +405,12 @@ namespace build2 } inline const bool& options:: + no_external_modules () const + { + return this->no_external_modules_; + } + + inline const bool& options:: structured_result () const { return this->structured_result_; diff --git a/build2/b.cli b/build2/b.cli index 57640b6..a03e9cc 100644 --- a/build2/b.cli +++ b/build2/b.cli @@ -535,6 +535,13 @@ namespace build2 useful for profiling." } + bool --no-external-modules + { + "Don't load external modules during project bootstrap. Note that this + option can only be used with meta-operations that do not load the + project's \cb{buildfiles}, such as \cb{info}." + } + bool --structured-result { "Write the result of execution in a structured form. In this mode, diff --git a/build2/b.cxx b/build2/b.cxx index b7e6708..18326cc 100644 --- a/build2/b.cxx +++ b/build2/b.cxx @@ -785,6 +785,7 @@ main (int argc, char* argv[]) ctx.reset (new context (sched, mutexes, ops.match_only (), + ops.no_external_modules (), ops.dry_run (), !ops.serial_stop () /* keep_going */, cmd_vars)); diff --git a/libbuild2/context.cxx b/libbuild2/context.cxx index d62965c..b78cd27 100644 --- a/libbuild2/context.cxx +++ b/libbuild2/context.cxx @@ -59,6 +59,7 @@ namespace build2 context (scheduler& s, global_mutexes& ms, bool mo, + bool nem, bool dr, bool kg, const strings& cmd_vars, @@ -68,6 +69,7 @@ namespace build2 sched (s), mutexes (ms), match_only (mo), + no_external_modules (nem), dry_run_option (dr), keep_going (kg), phase_mutex (*this), diff --git a/libbuild2/context.hxx b/libbuild2/context.hxx index c4e1259..0790355 100644 --- a/libbuild2/context.hxx +++ b/libbuild2/context.hxx @@ -146,6 +146,10 @@ namespace build2 // bool match_only; + // Skip booting external modules flag (see --no-external-modules). + // + bool no_external_modules; + // Dry run flag (see --dry-run|-n). // // This flag is set (based on dry_run_option) only for the final execute @@ -479,6 +483,7 @@ namespace build2 context (scheduler&, global_mutexes&, bool match_only = false, + bool no_external_modules = false, bool dry_run = false, bool keep_going = true, const strings& cmd_vars = {}, diff --git a/libbuild2/file.cxx b/libbuild2/file.cxx index 2660b9e..67aeb77 100644 --- a/libbuild2/file.cxx +++ b/libbuild2/file.cxx @@ -1431,6 +1431,8 @@ namespace build2 { tracer trace ("load_root"); + context& ctx (root.ctx); + const dir_path& out_root (root.out_path ()); const dir_path& src_root (root.src_path ()); @@ -1442,6 +1444,10 @@ namespace build2 if (root.buildfiles.find (f) != root.buildfiles.end ()) return; + if (ctx.no_external_modules) + fail << "attempt to load project " << root << " after skipped loading " + << "external modules"; + // First load outer roots, if any. // if (scope* rs = root.parent_scope ()->root_scope ()) @@ -1483,7 +1489,7 @@ namespace build2 // Reuse the parser to accumulate the configuration variable information. // - parser p (root.ctx, load_stage::root); + parser p (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/module.cxx b/libbuild2/module.cxx index 11b32cb..14cf183 100644 --- a/libbuild2/module.cxx +++ b/libbuild2/module.cxx @@ -81,6 +81,7 @@ namespace build2 new context (ctx.sched, ctx.mutexes, false, /* match_only */ + false, /* no_external_modules */ false, /* dry_run */ ctx.keep_going, ctx.global_var_overrides, /* cmd_vars */ @@ -238,7 +239,11 @@ namespace build2 #endif const string& mod, const location& loc, - bool /* boot */, +#if defined(BUILD2_BOOTSTRAP) || defined(LIBBUILD2_STATIC_BUILD) + bool, +#else + bool boot, +#endif bool opt) { tracer trace ("import_module"); @@ -251,6 +256,26 @@ namespace build2 else if (mod == "install") return &install::build2_install_load; else if (mod == "test") return &test::build2_test_load; + module_load_function* r (nullptr); + + // No dynamic loading of build system modules during bootstrap or if + // statically-linked.. + // +#if defined(BUILD2_BOOTSTRAP) || defined(LIBBUILD2_STATIC_BUILD) + if (!opt) + { + fail (loc) << "unknown build system module " << mod << +#ifdef BUILD2_BOOTSTRAP + info << "running bootstrap build system"; +#else + info << "running statically-linked build system"; +#endif + } +#else + context& ctx (bs.ctx); + + bool bundled (bundled_module (mod)); + // Note that importing external modules during bootstrap is problematic // since we haven't loaded config.build nor entered non-global variable // overrides. We used to just not support external modules that require @@ -287,28 +312,20 @@ namespace build2 // And another case is the bdep-sync hook which also doesn't have the // global overrides propagated to it. // - // It does feel right to propagate global overrides to all the nested - // build system invocations. Maybe we should set an environment variable? - - module_load_function* r (nullptr); - - // No dynamic loading of build system modules during bootstrap or if - // statically-linked.. - // -#if defined(BUILD2_BOOTSTRAP) || defined(LIBBUILD2_STATIC_BUILD) - if (!opt) - { - fail (loc) << "unknown build system module " << mod << -#ifdef BUILD2_BOOTSTRAP - info << "running bootstrap build system"; -#else - info << "running statically-linked build system"; -#endif - } -#else - context& ctx (bs.ctx); - - bool bundled (bundled_module (mod)); + // And it turns out the story does not end here: without an external + // module we cannot do info or dist. So to support this we now allow + // skipping of loading of external modules (for dist this is only part of + // the solution with the other part being the bootstrap mode). While no + // doubt a hack, it feels like this is the time to cut of this complexity + // escalation. Essentially, we are saying external module that require + // bootstrap must be prepared to be skipped if the project is only being + // bootstrapped. Note also that the fact that a module boot was skipped + // can be detected by checking the module's *.booted variable. In case of + // a skip it will be false, as opposed to true if the module was booted + // and undefined if the module was not mentioned. + // + if (boot && !bundled && ctx.no_external_modules) + return nullptr; // See if we can import a target for this module. // @@ -570,7 +587,14 @@ namespace build2 << mmod; } else + { + // Reduce skipped external module to optional. + // + if (boot) + opt = true; + i = loaded_modules.emplace (move (mmod), nullptr).first; + } } } @@ -616,23 +640,27 @@ namespace build2 // Otherwise search for this module. // - const module_functions& mf ( - *find_module (rs, mod, loc, true /* boot */, false /* optional */)); + // Note that find_module() may return NULL in case of a skipped external + // module. + // + const module_functions* mf ( + find_module (rs, mod, loc, true /* boot */, false /* optional */)); - if (mf.boot == nullptr) - fail (loc) << "build system module " << mod << " should not be loaded " - << "during bootstrap"; + if (mf != nullptr) + { + if (mf->boot == nullptr) + fail (loc) << "build system module " << mod << " should not be loaded " + << "during bootstrap"; - lm.push_back (module_state {loc, mod, mf.init, nullptr, nullopt}); - i = lm.end () - 1; + lm.push_back (module_state {loc, mod, mf->init, nullptr, nullopt}); + i = lm.end () - 1; - { module_boot_extra e {nullptr, module_boot_init::before}; // Note: boot() can load additional modules invalidating the iterator. // size_t j (i - lm.begin ()); - mf.boot (rs, loc, e); + mf->boot (rs, loc, e); i = lm.begin () + j; if (e.module != nullptr) @@ -641,7 +669,7 @@ namespace build2 i->boot_init = e.init; } - rs.assign (rs.var_pool ().insert (mod + ".booted")) = true; + rs.assign (rs.var_pool ().insert (mod + ".booted")) = (mf != nullptr); } module_state* -- cgit v1.1