aboutsummaryrefslogtreecommitdiff
path: root/libbuild2/module.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'libbuild2/module.cxx')
-rw-r--r--libbuild2/module.cxx318
1 files changed, 180 insertions, 138 deletions
diff --git a/libbuild2/module.cxx b/libbuild2/module.cxx
index 3abb102..cfedefe 100644
--- a/libbuild2/module.cxx
+++ b/libbuild2/module.cxx
@@ -63,6 +63,162 @@ namespace build2
mod);
}
+ // Note: also used by ad hoc recipes thus not static.
+ //
+ void
+ create_module_context (context& ctx, const location& loc)
+ {
+ assert (ctx.module_context == nullptr);
+ assert (*ctx.module_context_storage == nullptr);
+
+ // Since we are using the same scheduler, it makes sense to reuse the
+ // same global mutexes. Also disable nested module context for good
+ // measure.
+ //
+ ctx.module_context_storage->reset (
+ new context (ctx.sched,
+ ctx.mutexes,
+ false, /* match_only */
+ false, /* dry_run */
+ ctx.keep_going,
+ ctx.global_var_overrides, /* cmd_vars */
+ nullopt)); /* module_context */
+
+ // We use the same context for building any nested modules that might be
+ // required while building modules.
+ //
+ ctx.module_context = ctx.module_context_storage->get ();
+ ctx.module_context->module_context = ctx.module_context;
+
+ // Setup the context to perform update. In a sense we have a long-running
+ // perform meta-operation batch (indefinite, in fact, since we never call
+ // the meta-operation's *_post() callbacks) in which we periodically
+ // execute update operations.
+ //
+ // Note that we perform each build in a separate update operation. Failed
+ // that, if the same target is update twice (which may happen with ad hoc
+ // recipes) we will see the old state.
+ //
+ if (mo_perform.meta_operation_pre != nullptr)
+ mo_perform.meta_operation_pre ({} /* parameters */, loc);
+
+ ctx.module_context->current_meta_operation (mo_perform);
+
+ if (mo_perform.operation_pre != nullptr)
+ mo_perform.operation_pre ({} /* parameters */, update_id);
+ }
+
+ // Note: also used by ad hoc recipes thus not static.
+ //
+ const target&
+ update_in_module_context (context& ctx, const scope& rs, names tgt,
+ const location& loc, const path& bf)
+ {
+ // New update operation.
+ //
+ ctx.module_context->current_operation (op_update);
+
+ // Un-tune the scheduler.
+ //
+ // Note that we can only do this if we are running serially because
+ // otherwise we cannot guarantee the scheduler is idle (we could have
+ // waiting threads from the outer context). This is fine for now since the
+ // only two tuning level we use are serial and full concurrency (turns out
+ // currently we don't really need this: we will always be called during
+ // load or match phases and we always do parallel match; but let's keep it
+ // in case things change).
+ //
+ auto sched_tune (ctx.sched.serial ()
+ ? scheduler::tune_guard (ctx.sched, 0)
+ : scheduler::tune_guard ());
+
+ // Remap verbosity level 0 to 1 unless we were requested to be silent.
+ // Failed that, we may have long periods of seemingly nothing happening
+ // while we quietly update the module, which may look like things have
+ // hung up.
+ //
+ // @@ CTX: modifying global verbosity level won't work if we have multiple
+ // top-level contexts running in parallel.
+ //
+ auto verbg = make_guard (
+ [z = !silent && verb == 0 ? (verb = 1, true) : false] ()
+ {
+ if (z)
+ verb = 0;
+ });
+
+ // Note that for now we suppress progress since it would clash with the
+ // progress of what we are already doing (maybe in the future we can do
+ // save/restore but then we would need some sort of diagnostics that we
+ // have switched to another task).
+ //
+ action a (perform_update_id);
+ action_targets tgs;
+
+ mo_perform.search ({}, /* parameters */
+ rs, /* root scope */
+ rs, /* base scope */
+ bf, /* buildfile */
+ rs.find_target_key (tgt, loc),
+ loc,
+ tgs);
+
+ mo_perform.match ({}, /* parameters */
+ a,
+ tgs,
+ 1, /* diag (failures only) */
+ false /* progress */);
+
+ mo_perform.execute ({}, /* parameters */
+ a,
+ tgs,
+ 1, /* diag (failures only) */
+ false /* progress */);
+
+ assert (tgs.size () == 1);
+ return tgs[0].as<target> ();
+ }
+
+ // Note: also used by ad hoc recipes thus not static.
+ //
+ pair<void* /* handle */, void* /* symbol */>
+ load_module_library (const path& lib, const string& sym, string& err)
+ {
+ // Note that we don't unload our modules since it's not clear what would
+ // the benefit be.
+ //
+ void* h (nullptr);
+ void* s (nullptr);
+
+#ifndef _WIN32
+ // Use RTLD_NOW instead of RTLD_LAZY to both speed things up (we are going
+ // to use this module now) and to detect any symbol mismatches.
+ //
+ if ((h = dlopen (lib.string ().c_str (), RTLD_NOW | RTLD_GLOBAL)))
+ {
+ s = dlsym (h, sym.c_str ());
+
+ if (s == nullptr)
+ err = dlerror ();
+ }
+ else
+ err = dlerror ();
+#else
+ if (HMODULE m = LoadLibrary (lib.string ().c_str ()))
+ {
+ h = static_cast<void*> (m);
+ s = function_cast<void*> (GetProcAddress (m, sym.c_str ()));
+
+ if (s == nullptr)
+ err = win32::last_error_msg ();
+ }
+ else
+ err = win32::last_error_msg ();
+#endif
+
+ return make_pair (h, s);
+ }
+
static module_load_function*
import_module (scope& bs,
const string& mod,
@@ -180,43 +336,9 @@ namespace build2
{
if (!ctx.module_context_storage)
fail (loc) << "unable to update build system module " << mod <<
- info << "updating of build system modules is disabled";
-
- assert (*ctx.module_context_storage == nullptr);
-
- // Since we are using the same scheduler, it makes sense to reuse the
- // same global mutexes. Also disable nested module context for good
- // measure.
- //
- ctx.module_context_storage->reset (
- new context (ctx.sched,
- ctx.mutexes,
- false, /* match_only */
- false, /* dry_run */
- ctx.keep_going,
- ctx.global_var_overrides, /* cmd_vars */
- nullopt)); /* module_context */
-
- // We use the same context for building any nested modules that
- // might be required while building modules.
- //
- ctx.module_context = ctx.module_context_storage->get ();
- ctx.module_context->module_context = ctx.module_context;
-
- // Setup the context to perform update. In a sense we have a long-
- // running perform meta-operation batch (indefinite, in fact, since we
- // never call the meta-operation's *_post() callbacks) in which we
- // periodically execute the update operation.
- //
- if (mo_perform.meta_operation_pre != nullptr)
- mo_perform.meta_operation_pre ({} /* parameters */, loc);
+ info << "building of build system modules is disabled";
- ctx.module_context->current_meta_operation (mo_perform);
-
- if (mo_perform.operation_pre != nullptr)
- mo_perform.operation_pre ({} /* parameters */, update_id);
-
- ctx.module_context->current_operation (op_update);
+ create_module_context (ctx, loc);
}
// Inherit loaded_modules lock from the outer context.
@@ -234,24 +356,20 @@ namespace build2
l5 ([&]{trace << "loaded " << lr.first;});
- // When happens next depends on whether this is a top-level or nested
+ // What happens next depends on whether this is a top-level or nested
// module update.
//
if (nested)
{
// This could be initial or exclusive load.
//
- // @@ TODO
+ // @@ TODO: see the ad hoc recipe case as a reference.
//
fail (loc) << "nested build system module updates not yet supported";
}
else
{
- const scope& rs (lr.second);
-
- action_targets tgs;
- action a (perform_id, update_id);
-
+ const target* l;
{
// Cutoff the existing diagnostics stack and push our own entry.
//
@@ -263,68 +381,15 @@ namespace build2
dr << info (loc) << "while loading build system module " << mod;
});
- // Un-tune the scheduler.
- //
- // Note that we can only do this if we are running serially because
- // otherwise we cannot guarantee the scheduler is idle (we could
- // have waiting threads from the outer context). This is fine for
- // now since the only two tuning level we use are serial and full
- // concurrency (turns out currently we don't really need this: we
- // will always be called during load or match phases and we always
- // do parallel match; but let's keep it in case things change).
- //
- auto sched_tune (ctx.sched.serial ()
- ? scheduler::tune_guard (ctx.sched, 0)
- : scheduler::tune_guard ());
-
- // Remap verbosity level 0 to 1 unless we were requested to be
- // silent. Failed that, we may have long periods of seemingly
- // nothing happening while we quietly update the module, which
- // may look like things have hung up.
- //
- // @@ CTX: modifying global verbosity level won't work if we have
- // multiple top-level contexts running in parallel.
- //
- auto verbg = make_guard (
- [z = !silent && verb == 0 ? (verb = 1, true) : false] ()
- {
- if (z)
- verb = 0;
- });
-
- // Note that for now we suppress progress since it would clash with
- // the progress of what we are already doing (maybe in the future we
- // can do save/restore but then we would need some sort of
- // diagnostics that we have switched to another task).
- //
- mo_perform.search ({}, /* parameters */
- rs, /* root scope */
- rs, /* base scope */
- path (), /* buildfile */
- rs.find_target_key (lr.first, loc),
- loc,
- tgs);
-
- mo_perform.match ({}, /* parameters */
- a,
- tgs,
- 1, /* diag (failures only) */
- false /* progress */);
-
- mo_perform.execute ({}, /* parameters */
- a,
- tgs,
- 1, /* diag (failures only) */
- false /* progress */);
+ l = &update_in_module_context (
+ ctx, lr.second, move (lr.first),
+ loc, path ());
}
- assert (tgs.size () == 1);
- const target& l (tgs[0].as<target> ());
-
- if (!l.is_a ("libs"))
+ if (!l->is_a ("libs"))
fail (loc) << "wrong export from build system module " << mod;
- lib = l.as<file> ().path ();
+ lib = l->as<file> ().path ();
l5 ([&]{trace << "updated " << lib;});
}
@@ -364,53 +429,30 @@ namespace build2
//
string sym (sanitize_identifier ("build2_" + mod + "_load"));
- // Note that we don't unload our modules since it's not clear what would
- // the benefit be.
- //
- diag_record dr;
+ string err;
+ pair<void*, void*> hs (load_module_library (lib, sym, err));
-#ifndef _WIN32
- // Use RTLD_NOW instead of RTLD_LAZY to both speed things up (we are going
- // to use this module now) and to detect any symbol mismatches.
- //
- if (void* h = dlopen (lib.string ().c_str (), RTLD_NOW | RTLD_GLOBAL))
+ if (hs.first != nullptr)
{
- r = function_cast<module_load_function*> (dlsym (h, sym.c_str ()));
-
// I don't think we should ignore this even if the module is optional.
//
- if (r == nullptr)
+ if (hs.second == nullptr)
fail (loc) << "unable to lookup " << sym << " in build system module "
- << mod << " (" << lib << "): " << dlerror ();
+ << mod << " (" << lib << "): " << err;
+
+ r = function_cast<module_load_function*> (hs.second);
}
else if (!opt)
- dr << fail (loc) << "unable to load build system module " << mod
- << " (" << lib << "): " << dlerror ();
- else
- l5 ([&]{trace << "unable to load " << lib << ": " << dlerror ();});
-#else
- if (HMODULE h = LoadLibrary (lib.string ().c_str ()))
{
- r = function_cast<module_load_function*> (
- GetProcAddress (h, sym.c_str ()));
-
- if (r == nullptr)
- fail (loc) << "unable to lookup " << sym << " in build system module "
- << mod << " (" << lib << "): " << win32::last_error_msg ();
+ // Add import suggestion similar to import phase 2.
+ //
+ fail (loc) << "unable to load build system module " << mod << " ("
+ << lib << "): " << err <<
+ info << "use config.import." << proj.variable () << " command "
+ << "line variable to specify its project out_root";
}
- else if (!opt)
- dr << fail (loc) << "unable to load build system module " << mod
- << " (" << lib << "): " << win32::last_error_msg ();
else
- l5 ([&]{trace << "unable to load " << lib << ": "
- << win32::last_error_msg ();});
-#endif
-
- // Add a suggestion similar to import phase 2.
- //
- if (!dr.empty ())
- dr << info << "use config.import." << proj.variable () << " command "
- << "line variable to specify its project out_root" << endf;
+ l5 ([&]{trace << "unable to load " << lib << ": " << err;});
#endif // BUILD2_BOOTSTRAP