aboutsummaryrefslogtreecommitdiff
path: root/libbuild2/config/operation.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'libbuild2/config/operation.cxx')
-rw-r--r--libbuild2/config/operation.cxx264
1 files changed, 165 insertions, 99 deletions
diff --git a/libbuild2/config/operation.cxx b/libbuild2/config/operation.cxx
index 535018e..9050645 100644
--- a/libbuild2/config/operation.cxx
+++ b/libbuild2/config/operation.cxx
@@ -87,35 +87,45 @@ namespace build2
using project_set = set<const scope*>; // Use pointers to get comparison.
+ // If inherit is false, then don't rely on inheritance from outer scopes
+ // (used for config.export).
+ //
static void
- save_config (const scope& rs, const project_set& projects)
+ save_config (const scope& rs,
+ const path& f,
+ bool inherit,
+ const project_set& projects)
{
context& ctx (rs.ctx);
- path f (config_file (rs));
+ const module& mod (*rs.lookup_module<const module> (module::name));
- if (verb)
- text << (verb >= 2 ? "cat >" : "save ") << f;
+ const string& df (f.string () != "-" ? f.string () : "<stdout>");
- const module& mod (*rs.lookup_module<const module> (module::name));
+ if (verb)
+ text << (verb >= 2 ? "cat >" : "save ") << df;
try
{
- ofdstream ofs (f);
+ ofdstream ofs;
+ ostream& os (open_file_or_stdout (f, ofs));
- ofs << "# Created automatically by the config module, but feel " <<
+ os << "# Created automatically by the config module, but feel " <<
"free to edit." << endl
- << "#" << endl;
+ << "#" << endl;
- ofs << "config.version = " << module::version << endl;
+ os << "config.version = " << module::version << endl;
- if (auto l = rs.vars[ctx.var_amalgamation])
+ if (inherit)
{
- const dir_path& d (cast<dir_path> (l));
+ if (auto l = rs.vars[ctx.var_amalgamation])
+ {
+ const dir_path& d (cast<dir_path> (l));
- ofs << endl
- << "# Base configuration inherited from " << d << endl
- << "#" << endl;
+ os << endl
+ << "# Base configuration inherited from " << d << endl
+ << "#" << endl;
+ }
}
// Save config variables.
@@ -146,6 +156,11 @@ namespace build2
if (!l.defined ())
continue;
+ // Handle inherited from outer scope values.
+ //
+ // Note that we keep this logic (with warnings and all) even if
+ // inherit is false to make things easier to reason about.
+ //
if (!(l.belongs (rs) || l.belongs (ctx.global_scope)))
{
// This is presumably an inherited value. But it could also be
@@ -205,42 +220,50 @@ namespace build2
}
}
- if (found) // Inherited.
- continue;
-
- location loc (&f);
-
- // If this value is not defined in a project's root scope, then
- // something is broken.
- //
- if (r == nullptr)
- fail (loc) << "inherited variable " << var << " value "
- << "is not from a root scope";
-
- // If none of the outer project's configurations use this value,
- // then we warn and save as our own. One special case where we
- // don't want to warn the user is if the variable is overriden.
- //
- if (org.first == ovr.first)
+ if (found)
{
- diag_record dr;
- dr << warn (loc) << "saving previously inherited variable "
- << var;
-
- dr << info (loc) << "because project " << *r
- << " no longer uses it in its configuration";
-
- if (verb >= 2)
+ // Inherited.
+ //
+ if (inherit)
+ continue;
+ }
+ else
+ {
+ location loc (&f);
+
+ // If this value is not defined in a project's root scope,
+ // then something is broken.
+ //
+ if (r == nullptr)
+ fail (loc) << "inherited variable " << var << " value "
+ << "is not from a root scope";
+
+ // If none of the outer project's configurations use this
+ // value, then we warn and save as our own. One special case
+ // where we don't want to warn the user is if the variable is
+ // overriden.
+ //
+ if (org.first == ovr.first)
{
- dr << info (loc) << "variable value: ";
+ diag_record dr;
+ dr << warn (loc) << "saving previously inherited variable "
+ << var;
- if (*l)
+ dr << info (loc) << "because project " << *r
+ << " no longer uses it in its configuration";
+
+ if (verb >= 2)
{
- storage.clear ();
- dr << "'" << reverse (*l, storage) << "'";
+ dr << info (loc) << "variable value: ";
+
+ if (*l)
+ {
+ storage.clear ();
+ dr << "'" << reverse (*l, storage) << "'";
+ }
+ else
+ dr << "[null]";
}
- else
- dr << "[null]";
}
}
}
@@ -264,7 +287,7 @@ namespace build2
//
if (first)
{
- ofs << endl;
+ os << endl;
first = false;
}
@@ -274,7 +297,7 @@ namespace build2
org.first == ovr.first && // Not overriden.
(sv.flags & save_commented) == save_commented)
{
- ofs << '#' << n << " =" << endl;
+ os << '#' << n << " =" << endl;
continue;
}
@@ -283,20 +306,20 @@ namespace build2
storage.clear ();
names_view ns (reverse (v, storage));
- ofs << n;
+ os << n;
if (ns.empty ())
- ofs << " =";
+ os << " =";
else
{
- ofs << " = ";
- to_stream (ofs, ns, true, '@'); // Quote.
+ os << " = ";
+ to_stream (os, ns, true, '@'); // Quote.
}
- ofs << endl;
+ os << endl;
}
else
- ofs << n << " = [null]" << endl;
+ os << n << " = [null]" << endl;
}
}
@@ -304,12 +327,15 @@ namespace build2
}
catch (const io_error& e)
{
- fail << "unable to write " << f << ": " << e;
+ fail << "unable to write " << df << ": " << e;
}
}
static void
- configure_project (action a, const scope& rs, project_set& projects)
+ configure_project (action a,
+ const scope& rs,
+ const variable* c_e, // config.export
+ project_set& projects)
{
tracer trace ("configure_project");
@@ -332,8 +358,7 @@ namespace build2
mkdir (out_root / rs.root_extra->bootstrap_dir, 2);
}
- // We distinguish between a complete configure and operation-
- // specific.
+ // We distinguish between a complete configure and operation-specific.
//
if (a.operation () == default_id)
{
@@ -341,15 +366,48 @@ namespace build2
// Save src-root.build unless out_root is the same as src.
//
- if (out_root != src_root)
+ if (c_e == nullptr && out_root != src_root)
save_src_root (rs);
- // Save config.build.
+ // Save config.build unless an alternative is specified with
+ // config.export. Similar to config.import we will only save to that
+ // file if it is specified on our root scope or as a global override
+ // (the latter is a bit iffy but let's allow it, for example, to dump
+ // everything to stdout). Note that to save a subproject's config we
+ // will have to use a scope-specific override (since the default will
+ // apply to the amalgamation):
//
- save_config (rs, projects);
+ // b configure: subproj/ subproj/config.export=.../config.build
+ //
+ // Could be confusing but then normally it will be the amalgamation
+ // whose configuration we want to export.
+ //
+ // Note also that if config.export is specified we do not rewrite
+ // config.build files (say, of subprojects) as well as src-root.build
+ // above. Failed that, if we are running in a disfigured project, we
+ // may end up leaving it in partially configured state.
+ //
+ if (c_e == nullptr)
+ save_config (rs, config_file (rs), true /* inherit */, projects);
+ else
+ {
+ lookup l (rs[*c_e]);
+ if (l && (l.belongs (rs) || l.belongs (ctx.global_scope)))
+ {
+ // While writing the complete configuration seems like a natural
+ // default, there might be a desire to take inheritance into
+ // account (if, say, we are exporting at multiple levels). One can
+ // of course just copy the relevant config.build files, but we may
+ // still want to support this mode somehow in the future (maybe
+ // using `+` as a modifier, say config.export=+.../config.build).
+ //
+ save_config (rs, cast<path> (l), false /* inherit */, projects);
+ }
+ }
}
else
{
+ fail << "operation-specific configuration not yet supported";
}
// Configure subprojects that have been loaded.
@@ -368,7 +426,7 @@ namespace build2
if (nrs.out_path () != out_nroot) // This subproject not loaded.
continue;
- configure_project (a, nrs, projects);
+ configure_project (a, nrs, c_e, projects);
}
}
}
@@ -513,6 +571,15 @@ namespace build2
{
bool fwd (forward (params));
+ context& ctx (fwd ? ts[0].as<scope> ().ctx : ts[0].as<target> ().ctx);
+
+ const variable* c_e (ctx.var_pool.find ("config.export"));
+
+ if (c_e->overrides == nullptr)
+ c_e = nullptr;
+ else if (fwd)
+ fail << "config.export specified for forward configuration";
+
project_set projects;
for (const action_target& at: ts)
@@ -521,53 +588,52 @@ namespace build2
{
// Forward configuration.
//
- const scope& rs (*static_cast<const scope*> (at.target));
+ const scope& rs (at.as<scope> ());
configure_forward (rs, projects);
- continue;
}
+ else
+ {
+ // Normal configuration.
+ //
+ // Match rules to configure every operation supported by each
+ // project. Note that we are not calling operation_pre/post()
+ // callbacks here since the meta operation is configure and we know
+ // what we are doing.
+ //
+ // Note that we cannot do this in parallel. We cannot parallelize
+ // the outer loop because we should match for a single action at a
+ // time. And we cannot swap the loops because the list of operations
+ // is target-specific. However, inside match(), things can proceed
+ // in parallel.
+ //
+ const target& t (at.as<target> ());
+ const scope* rs (t.base_scope ().root_scope ());
- // Normal configuration.
- //
- // Match rules to configure every operation supported by each project.
- // Note that we are not calling operation_pre/post() callbacks here
- // since the meta operation is configure and we know what we are
- // doing.
- //
- // Note that we cannot do this in parallel. We cannot parallelize the
- // outer loop because we should match for a single action at a time.
- // And we cannot swap the loops because the list of operations is
- // target-specific. However, inside match(), things can proceed in
- // parallel.
- //
- const target& t (at.as_target ());
- const scope* rs (t.base_scope ().root_scope ());
-
- if (rs == nullptr)
- fail << "out of project target " << t;
-
- context& ctx (t.ctx);
+ if (rs == nullptr)
+ fail << "out of project target " << t;
- const operations& ops (rs->root_extra->operations);
+ const operations& ops (rs->root_extra->operations);
- for (operation_id id (default_id + 1); // Skip default_id.
- id < ops.size ();
- ++id)
- {
- if (const operation_info* oif = ops[id])
+ for (operation_id id (default_id + 1); // Skip default_id.
+ id < ops.size ();
+ ++id)
{
- // Skip aliases (e.g., update-for-install).
- //
- if (oif->id != id)
- continue;
+ if (const operation_info* oif = ops[id])
+ {
+ // Skip aliases (e.g., update-for-install).
+ //
+ if (oif->id != id)
+ continue;
- ctx.current_operation (*oif);
+ ctx.current_operation (*oif);
- phase_lock pl (ctx, run_phase::match);
- match (action (configure_id, id), t);
+ phase_lock pl (ctx, run_phase::match);
+ match (action (configure_id, id), t);
+ }
}
- }
- configure_project (a, *rs, projects);
+ configure_project (a, *rs, c_e, projects);
+ }
}
}
@@ -802,7 +868,7 @@ namespace build2
//
for (const action_target& at: ts)
{
- const scope& rs (*static_cast<const scope*> (at.target));
+ const scope& rs (at.as<scope> ());
if (!(fwd
? disfigure_forward ( rs, projects)