aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2019-08-26 10:02:18 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2019-08-26 10:02:18 +0200
commit08de44303033cc5ad966f4e75a7fa4a3cb06635f (patch)
tree090b21375b91a6b7999813d17469d20d58ab6060
parent97e00dccb4a9d3abc3c896b33560ed6aed0a1763 (diff)
Implement updating build system modules
-rw-r--r--build/export.build2
-rw-r--r--build2/b.cxx4
-rw-r--r--libbuild2/algorithm.hxx2
-rw-r--r--libbuild2/context.cxx12
-rw-r--r--libbuild2/context.hxx23
-rw-r--r--libbuild2/function.cxx2
-rw-r--r--libbuild2/function.hxx2
-rw-r--r--libbuild2/module.cxx176
-rw-r--r--libbuild2/target-type.hxx3
-rw-r--r--libbuild2/target.cxx15
-rw-r--r--libbuild2/target.hxx4
11 files changed, 211 insertions, 34 deletions
diff --git a/build/export.build b/build/export.build
index 345baf0..e2f247c 100644
--- a/build/export.build
+++ b/build/export.build
@@ -19,7 +19,7 @@ else
}
d = [dir_path] $out_root/libbuild2/
- if ($import.target != lib{build2})
+ if ($name($import.target) != 'build2')
{
# Assume one of the modules.
#
diff --git a/build2/b.cxx b/build2/b.cxx
index 51b024e..49a4d34 100644
--- a/build2/b.cxx
+++ b/build2/b.cxx
@@ -621,9 +621,9 @@ main (int argc, char* argv[])
{
ctx = nullptr; // Free first.
ctx.reset (new context (sched,
- cmd_vars,
ops.dry_run (),
- !ops.serial_stop () /* keep_going */));
+ !ops.serial_stop () /* keep_going */,
+ cmd_vars));
};
new_context ();
diff --git a/libbuild2/algorithm.hxx b/libbuild2/algorithm.hxx
index cda464b..f105fbe 100644
--- a/libbuild2/algorithm.hxx
+++ b/libbuild2/algorithm.hxx
@@ -403,7 +403,7 @@ namespace build2
// Unless already known, match, and, if necessary, execute the group in
// order to resolve its members list. Note that even after that the member's
- // list might still not be available (e.g., if some wildcard/ fallback rule
+ // list might still not be available (e.g., if some wildcard/fallback rule
// matched).
//
// If the action is for an outer operation, then it is changed to inner
diff --git a/libbuild2/context.cxx b/libbuild2/context.cxx
index db46319..e35f308 100644
--- a/libbuild2/context.cxx
+++ b/libbuild2/context.cxx
@@ -54,7 +54,11 @@ namespace build2
};
context::
- context (scheduler& s, const strings& cmd_vars, bool dr, bool kg)
+ context (scheduler& s,
+ bool dr,
+ bool kg,
+ const strings& cmd_vars,
+ optional<unique_ptr<context>> mc)
: data_ (new data (*this)),
sched (s),
dry_run_option (dr),
@@ -67,7 +71,9 @@ namespace build2
functions (data_->functions),
global_scope (create_global_scope (data_->scopes)),
global_target_types (data_->global_target_types),
- global_override_cache (data_->global_override_cache)
+ global_override_cache (data_->global_override_cache),
+ module_context (mc ? mc->get () : nullptr),
+ module_context_storage (move (mc))
{
tracer trace ("context");
@@ -76,7 +82,7 @@ namespace build2
scope_map& sm (data_->scopes);
variable_pool& vp (data_->var_pool);
- register_builtin_functions (functions);
+ insert_builtin_functions (functions);
// Initialize the meta/operation tables. Note that the order should match
// the id constants in <libbuild2/operation.hxx>.
diff --git a/libbuild2/context.hxx b/libbuild2/context.hxx
index f9432ae..338b8a1 100644
--- a/libbuild2/context.hxx
+++ b/libbuild2/context.hxx
@@ -101,6 +101,11 @@ namespace build2
// @@ CTX: document (backlinks, non-overlap etc). RW story.
//
+ // A context can be preempted to execute another context (we do this, for
+ // example, to update build system modules).
+ //
+ // @@ Progress could clash.
+ //
class LIBBUILD2_SYMEXPORT context
{
struct data;
@@ -380,12 +385,26 @@ namespace build2
dir_path old_src_root;
dir_path new_src_root;
+ // Nested context for updating build system modules.
+ //
+ // Note that such a context itself should normally have modules_context
+ // setup to point to itself (see import_module() for details).
+ //
+ context* module_context;
+ optional<unique_ptr<context>> module_context_storage; //@@ CTX: shared_ptr
+
public:
+ // If mod_ctx is absent, then automatic updating of build system modules
+ // is disabled. If it is NULL, then the context will be created lazily if
+ // and when necessary. Otherwise, it should be a properly setup context
+ // (including, normally, a self-reference in modules_context).
+ //
explicit
context (scheduler&,
- const strings& cmd_vars = {},
bool dry_run = false,
- bool keep_going = true);
+ bool keep_going = true,
+ const strings& cmd_vars = {},
+ optional<unique_ptr<context>> mod_ctx = unique_ptr<context> ());
// Set current meta-operation and operation.
//
diff --git a/libbuild2/function.cxx b/libbuild2/function.cxx
index ebf880a..f790809 100644
--- a/libbuild2/function.cxx
+++ b/libbuild2/function.cxx
@@ -379,7 +379,7 @@ namespace build2
void project_name_functions (function_map&); // functions-target-triplet.cxx
void
- register_builtin_functions (function_map& m)
+ insert_builtin_functions (function_map& m)
{
builtin_functions (m);
filesystem_functions (m);
diff --git a/libbuild2/function.hxx b/libbuild2/function.hxx
index bb3fe3a..d1de4ac 100644
--- a/libbuild2/function.hxx
+++ b/libbuild2/function.hxx
@@ -217,7 +217,7 @@ namespace build2
};
LIBBUILD2_SYMEXPORT void
- register_builtin_functions (function_map&);
+ insert_builtin_functions (function_map&);
class LIBBUILD2_SYMEXPORT function_family
{
diff --git a/libbuild2/module.cxx b/libbuild2/module.cxx
index ff70de6..3a66750 100644
--- a/libbuild2/module.cxx
+++ b/libbuild2/module.cxx
@@ -12,9 +12,11 @@
# endif
#endif
-#include <libbuild2/file.hxx> // import()
+#include <libbuild2/file.hxx> // import_*()
#include <libbuild2/scope.hxx>
+#include <libbuild2/target.hxx>
#include <libbuild2/variable.hxx>
+#include <libbuild2/operation.hxx>
#include <libbuild2/diagnostics.hxx>
// Core modules bundled with libbuild2.
@@ -50,7 +52,7 @@ namespace build2
}
static module_load_function*
- import_module (scope& /*bs*/,
+ import_module (scope& bs,
const string& mod,
const location& loc,
bool boot,
@@ -87,38 +89,164 @@ namespace build2
fail (loc) << "unknown build system module " << mod <<
info << "running bootstrap build system";
#else
- path lib;
+ context& ctx (bs.ctx);
-#if 0
// See if we can import a target for this module.
//
- // Check if one of the bundled modules, if so, the project name is
- // build2, otherwise -- libbuild2-<mod>.
+ path lib;
+
+ // If this is a top-level module update, then we use the nested context.
+ // If, however, this is a nested module update (i.e., a module required
+ // while updating a module), then we reuse the same module context.
//
- // The target we are looking for is <prj>%lib{build2-<mod>}.
+ // If you are wondering why don't we always use the top-level context, the
+ // reason is that it might be running a different meta/operation (say,
+ // configure or clean); with the nested context we always know it is
+ // perform update.
//
- name tgt (
- import (bs,
- name (bundled ? "build2" : "libbuild2-" + mod,
- dir_path (),
- "lib",
- "build2-" + mod),
- loc));
-
- if (!tgt.qualified ())
+ // And the reason for not simply creating a nested context for each nested
+ // module update is due to the no-overlap requirement of contexts: while
+ // we can naturally expect the top-level project(s) and the modules they
+ // require to be in separate configurations that don't shared anything,
+ // the same does not hold for build system modules. In fact, it would be
+ // natural to have a single build configuration for all of them and they
+ // could plausibly share some common libraries.
+ //
+ bool nested (ctx.module_context == &ctx);
+
+ // If this is one of the bundled modules, the project name is build2,
+ // otherwise -- libbuild2-<mod>.
+ //
+ // The target we are looking for is <prj>%libs{build2-<mod>}.
+ //
+ // We only search in subprojects if this is a nested module update
+ // (remember, if it's top-level, then it must be in an isolated
+ // configuration).
+ //
+ pair<name, dir_path> ir (
+ import_search (
+ bs,
+ name (project_name (bundled ? "build2" : "libbuild2-" + mod),
+ dir_path (),
+ "libs",
+ "build2-" + mod),
+ loc,
+ nested /* subprojects */));
+
+ if (!ir.second.empty ())
{
- // Switch the phase and update the target. This will also give us the
- // shared library path.
+ // We found the module as a target in a project. Now we need to update
+ // the target (which will also give us the shared library path).
+
+ // Create the build context if necessary.
+ //
+ if (ctx.module_context == nullptr)
+ {
+ 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 mutex shards. Also disable nested module context for
+ // good measure.
+ //
+ ctx.module_context_storage->reset (
+ new context (ctx.sched,
+ false, /* dry_run */
+ ctx.keep_going,
+ {}, /* 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);
+
+ 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);
+ }
+
+ // "Switch" to the module context.
//
- // @@ TODO
+ context& ctx (*bs.ctx.module_context);
+
+ // Load the imported project in the module context.
//
+ pair<names, const scope&> lr (import_load (ctx, move (ir), loc));
+
+ // When happens next depends on whether this is a top-level or nested
+ // module update.
+ //
+ if (nested)
+ {
+ // This could be initial or exclusive load.
+ //
+ // @@ TODO
+ //
+ fail (loc) << "nested build system module updates not yet supported";
+ }
+ else
+ {
+ const scope& rs (lr.second);
+ target_key tk (rs.find_target_key (lr.first, loc));
+
+ action_targets tgs;
+ action a (perform_id, update_id);
+
+ // 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 */
+ tk,
+ 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);
+ const target& l (tgs[0].as_target ());
+
+ if (!l.is_a ("libs"))
+ fail (loc) << "wrong export from build system module " << mod;
+
+ lib = l.as<file> ().path ();
+ }
}
else
-#endif
{
- // No luck. Form the shared library name (incorporating build system
- // core version) and try using system-default search (installed, rpath,
- // etc).
+ // No module project found. Form the shared library name (incorporating
+ // build system core version) and try using system-default search
+ // (installed, rpath, etc).
// @@ This is unfortunate: it would have been nice to do something
// similar to what we've done for exe{}. While libs{} is in the bin
@@ -139,6 +267,8 @@ namespace build2
lib = path (pfx + mod + '-' + build_version_interface + sfx);
}
+ // The build2_<mod>_load() symbol name.
+ //
string sym (sanitize_identifier ("build2_" + mod + "_load"));
// Note that we don't unload our modules since it's not clear what would
diff --git a/libbuild2/target-type.hxx b/libbuild2/target-type.hxx
index 8b308c3..3325df1 100644
--- a/libbuild2/target-type.hxx
+++ b/libbuild2/target-type.hxx
@@ -101,6 +101,9 @@ namespace build2
}
bool
+ is_a (const char*) const; // Defined in target.cxx
+
+ bool
is_a_base (const target_type&) const; // Defined in target.cxx
};
diff --git a/libbuild2/target.cxx b/libbuild2/target.cxx
index dbca6cd..22f5e66 100644
--- a/libbuild2/target.cxx
+++ b/libbuild2/target.cxx
@@ -4,6 +4,8 @@
#include <libbuild2/target.hxx>
+#include <cstring> // strcmp()
+
#include <libbuild2/file.hxx>
#include <libbuild2/scope.hxx>
#include <libbuild2/search.hxx>
@@ -19,6 +21,19 @@ namespace build2
// target_type
//
bool target_type::
+ is_a (const char* n) const
+ {
+ if (strcmp (name, n) == 0)
+ return true;
+
+ for (const target_type* b (base); b != nullptr; b = b->base)
+ if (strcmp (b->name, n) == 0)
+ return true;
+
+ return false;
+ }
+
+ bool target_type::
is_a_base (const target_type& tt) const
{
for (const target_type* b (base); b != nullptr; b = b->base)
diff --git a/libbuild2/target.hxx b/libbuild2/target.hxx
index b0d46e9..d543da8 100644
--- a/libbuild2/target.hxx
+++ b/libbuild2/target.hxx
@@ -730,6 +730,10 @@ namespace build2
const T*
is_a () const {return dynamic_cast<const T*> (this);}
+ const target*
+ is_a (const char* n) const {
+ return type ().is_a (n) ? this : nullptr;}
+
// Unchecked cast.
//
template <typename T>