aboutsummaryrefslogtreecommitdiff
path: root/libbuild2
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2020-09-25 13:42:17 +0200
committerKaren Arutyunov <karen@codesynthesis.com>2020-09-29 15:19:19 +0300
commit55a9ff6c72da30ad8761938d00c94355a0cb1b04 (patch)
treeb9edefcb50cfaa975303b3d52f1cd8fc25998cad /libbuild2
parent9dba2e1e7a8aa8de4d5236ab6b5a81d6cf34df1a (diff)
Add bootstrap distribution mode (!config.dist.bootstrap=true)
In this mode the dist meta-operation does not load the project (but does bootstrap it) and adds all the source files into the distribution only ignoring files and directories that start with a dot. This mode is primarily meant for situation where the project cannot (yet) be loaded due to missing dependencies.
Diffstat (limited to 'libbuild2')
-rw-r--r--libbuild2/dist/init.cxx120
-rw-r--r--libbuild2/dist/module.hxx3
-rw-r--r--libbuild2/dist/operation.cxx542
-rw-r--r--libbuild2/dist/operation.hxx3
-rw-r--r--libbuild2/version/init.cxx83
-rw-r--r--libbuild2/version/module.hxx2
6 files changed, 476 insertions, 277 deletions
diff --git a/libbuild2/dist/init.cxx b/libbuild2/dist/init.cxx
index a96d10e..a3d1e3f 100644
--- a/libbuild2/dist/init.cxx
+++ b/libbuild2/dist/init.cxx
@@ -29,10 +29,6 @@ namespace build2
l5 ([&]{trace << "for " << rs;});
- // Register the meta-operation.
- //
- rs.insert_meta_operation (dist_id, mo_dist);
-
// Enter module variables. Do it during boot in case they get assigned
// in bootstrap.build (which is customary for, e.g., dist.package).
//
@@ -59,49 +55,47 @@ namespace build2
//
vp.insert<bool> ("config.dist.uncommitted");
+ // The bootstrap distribution mode. Note that it can only be specified
+ // as a global override and is thus marked as unsaved in init(). Unlike
+ // the normal load distribution mode, we can do in-source and multiple
+ // projects at once.
+ //
+ // Note also that other config.dist.* variables can only be specified as
+ // overrides (since config.build is not loaded) but do not have to be
+ // global.
+ //
+ auto& v_d_b (vp.insert<bool> ("config.dist.bootstrap"));
+
vp.insert<dir_path> ("dist.root");
vp.insert<process_path> ("dist.cmd");
vp.insert<paths> ("dist.archives");
vp.insert<paths> ("dist.checksums");
- vp.insert<paths> ("dist.uncommitted");
vp.insert<bool> ("dist", variable_visibility::target); // Flag.
- // Project's package name.
+ // Project's package name. Note: if set, must be in bootstrap.build.
//
auto& v_d_p (vp.insert<string> ("dist.package"));
+ // See if we need to use the bootstrap mode.
+ //
+ bool bm (cast_false<bool> (rs.global_scope ()[v_d_b]));
+
+ // Register the meta-operation.
+ //
+ rs.insert_meta_operation (dist_id,
+ bm ? mo_dist_bootstrap : mo_dist_load);
+
// Create the module.
//
extra.set_module (new module (v_d_p));
}
- bool
- init (scope& rs,
- scope&,
- const location& l,
- bool first,
- bool,
- module_init_extra&)
+ // This code is reused by the bootstrap mode.
+ //
+ void
+ init_config (scope& rs)
{
- tracer trace ("dist::init");
-
- if (!first)
- {
- warn (l) << "multiple dist module initializations";
- return true;
- }
-
- l5 ([&]{trace << "for " << rs;});
-
- // Register our wildcard rule. Do it explicitly for the alias to prevent
- // something like insert<target>(dist_id, test_id) taking precedence.
- //
- rs.insert_rule<target> (dist_id, 0, "dist", rule_);
- rs.insert_rule<alias> (dist_id, 0, "dist.alias", rule_); //@@ outer?
-
- // Configuration.
- //
// Note that we don't use any defaults for root -- the location
// must be explicitly specified or we will complain if and when
// we try to dist.
@@ -109,13 +103,9 @@ namespace build2
using config::lookup_config;
using config::specified_config;
- bool s (specified_config (rs, "dist"));
-
- // Adjust module priority so that the config.dist.* values are saved at
- // the end of config.build.
+ // Note: ignore config.dist.bootstrap.
//
- if (s)
- config::save_module (rs, "dist", INT32_MAX);
+ bool s (specified_config (rs, "dist", {"bootstrap"}));
// dist.root
//
@@ -172,6 +162,62 @@ namespace build2
// Omit it from the configuration unless specified.
//
lookup_config (rs, "config.dist.uncommitted");
+ }
+
+ bool
+ init (scope& rs,
+ scope&,
+ const location& l,
+ bool first,
+ bool,
+ module_init_extra&)
+ {
+ tracer trace ("dist::init");
+
+ if (!first)
+ {
+ warn (l) << "multiple dist module initializations";
+ return true;
+ }
+
+ l5 ([&]{trace << "for " << rs;});
+
+ auto& vp (rs.var_pool ());
+
+ // Register our wildcard rule. Do it explicitly for the alias to prevent
+ // something like insert<target>(dist_id, test_id) taking precedence.
+ //
+ rs.insert_rule<target> (dist_id, 0, "dist", rule_);
+ rs.insert_rule<alias> (dist_id, 0, "dist.alias", rule_); //@@ outer?
+
+ // Configuration.
+ //
+ // Adjust module priority so that the config.dist.* values are saved at
+ // the end of config.build.
+ //
+ // Note: must be done regardless of specified_config() result due to
+ // the unsave_variable() call below.
+ //
+ config::save_module (rs, "dist", INT32_MAX);
+
+ init_config (rs);
+
+ // dist.bootstrap
+ //
+ {
+ auto& v (*vp.find ("config.dist.bootstrap"));
+
+ // If specified, verify it is a global override.
+ //
+ if (lookup l = rs[v])
+ {
+ if (!l.belongs (rs.global_scope ()))
+ fail << "config.dist.bootstrap must be a global override" <<
+ info << "specify !config.dist.bootstrap=...";
+ }
+
+ config::unsave_variable (rs, v);
+ }
return true;
}
diff --git a/libbuild2/dist/module.hxx b/libbuild2/dist/module.hxx
index 95dbc53..e445d4a 100644
--- a/libbuild2/dist/module.hxx
+++ b/libbuild2/dist/module.hxx
@@ -40,6 +40,9 @@ namespace build2
// Note that if registered, the callbacks are also called (recursively)
// in subprojects.
//
+ // Note also that in the bootstrap distribution mode only callbacks
+ // registered during bootstrap will be called.
+ //
using callback_func = void (const path&, const scope&, void*);
void
diff --git a/libbuild2/dist/operation.cxx b/libbuild2/dist/operation.cxx
index cce2239..a5dfba0 100644
--- a/libbuild2/dist/operation.cxx
+++ b/libbuild2/dist/operation.cxx
@@ -61,38 +61,101 @@ namespace build2
dist_operation_pre (const values&, operation_id o)
{
if (o != default_id)
- fail << "explicit operation specified for meta-operation dist";
+ fail << "explicit operation specified for dist meta-operation";
return o;
}
+ // Enter the specified source file as a target of type T. The path is
+ // expected to be normalized and relative to src_root. If the third
+ // argument is false, then first check if the file exists. If the fourth
+ // argument is true, then set the target's path.
+ //
+ template <typename T>
+ static const T*
+ add_target (const scope& rs, const path& f, bool e = false, bool s = false)
+ {
+ tracer trace ("dist::add_target");
+
+ path p (rs.src_path () / f);
+ if (e || exists (p))
+ {
+ dir_path d (p.directory ());
+
+ // Figure out if we need out.
+ //
+ dir_path out (rs.src_path () != rs.out_path ()
+ ? out_src (d, rs)
+ : dir_path ());
+
+ const T& t (rs.ctx.targets.insert<T> (
+ move (d),
+ move (out),
+ p.leaf ().base ().string (),
+ p.extension (), // Specified.
+ trace));
+
+ if (s)
+ t.path (move (p));
+
+ return &t;
+ }
+
+ return nullptr;
+ }
+
+ // Recursively traverse an src_root subdirectory entering/collecting the
+ // contained files and file symlinks as the file targets and skipping
+ // entries that start with a dot. Follow directory symlinks (preserving
+ // their names) and fail on dangling symlinks.
+ //
static void
- dist_execute (const values&, action, action_targets& ts,
- uint16_t, bool prog)
+ add_subdir (const scope& rs, const dir_path& sd, action_targets& files)
{
- tracer trace ("dist_execute");
+ dir_path d (rs.src_path () / sd);
- // For now we assume all the targets are from the same project.
- //
- const target& t (ts[0].as<target> ());
- const scope* rs (t.base_scope ().root_scope ());
+ try
+ {
+ for (const dir_entry& e: dir_iterator (d, false /* ignore_dangling */))
+ {
+ const path& n (e.path ());
- if (rs == nullptr)
- fail << "out of project target " << t;
+ if (n.string ()[0] != '.')
+ try
+ {
+ if (e.type () == entry_type::directory) // Can throw.
+ add_subdir (rs, sd / path_cast<dir_path> (n), files);
+ else
+ files.push_back (add_target<file> (rs, sd / n, true, true));
+ }
+ catch (const system_error& e)
+ {
+ fail << "unable to stat " << (d / n) << ": " << e;
+ }
+ }
+ }
+ catch (const system_error& e)
+ {
+ fail << "unable to iterate over " << d << ": " << e;
+ }
+ }
- context& ctx (rs->ctx);
+ // If tgt is NULL, then this is the bootstrap mode.
+ //
+ static void
+ dist_project (const scope& rs, const target* tgt, bool prog)
+ {
+ tracer trace ("dist::dist_project");
- const dir_path& out_root (rs->out_path ());
- const dir_path& src_root (rs->src_path ());
+ context& ctx (rs.ctx);
- if (out_root == src_root)
- fail << "in-tree distribution of target " << t <<
- info << "distribution requires out-of-tree build";
+ const dir_path& out_root (rs.out_path ());
+ const dir_path& src_root (rs.src_path ());
// Make sure we have the necessary configuration before we get down to
// business.
//
- auto l (rs->vars["dist.root"]);
+ auto l (rs.vars["dist.root"]);
if (!l || l->empty ())
fail << "unknown root distribution directory" <<
@@ -104,26 +167,14 @@ namespace build2
//
const dir_path& dist_root (cast<dir_path> (l));
- l = rs->vars["dist.package"];
+ l = rs.vars["dist.package"];
if (!l || l->empty ())
fail << "unknown distribution package name" <<
info << "did you forget to set dist.package?";
const string& dist_package (cast<string> (l));
- const process_path& dist_cmd (cast<process_path> (rs->vars["dist.cmd"]));
-
- // Verify all the targets are from the same project.
- //
- for (const action_target& at: ts)
- {
- const target& t (at.as<target> ());
-
- if (rs != t.base_scope ().root_scope ())
- fail << "target " << t << " is from a different project" <<
- info << "one dist meta-operation can handle one project" <<
- info << "consider using several dist meta-operations";
- }
+ const process_path& dist_cmd (cast<process_path> (rs.vars["dist.cmd"]));
// We used to print 'dist <target>' at verbosity level 1 but that has
// proven to be just noise. Though we still want to print something
@@ -136,204 +187,211 @@ namespace build2
if (verb == 1)
text << "dist " << dist_package;
- // Match a rule for every operation supported by this project. Skip
- // default_id.
- //
- // Note that we are not calling operation_pre/post() callbacks here
- // since the meta operation is dist and we know what we are doing.
+ // Get the list of files to distribute.
//
- values params;
- path_name pn ("<dist>");
- const location loc (pn); // Dummy location.
+ action_targets files;
+
+ if (tgt != nullptr)
{
- auto mog = make_guard ([&ctx] () {ctx.match_only = false;});
- ctx.match_only = true;
+ l5 ([&]{trace << "load dist " << rs;});
- const operations& ops (rs->root_extra->operations);
- for (operations::size_type id (default_id + 1); // Skip default_id.
- id < ops.size ();
- ++id)
+ // Match a rule for every operation supported by this project. Skip
+ // default_id.
+ //
+ // Note that we are not calling operation_pre/post() callbacks here
+ // since the meta operation is dist and we know what we are doing.
+ //
+ values params;
+ path_name pn ("<dist>");
+ const location loc (pn); // Dummy location.
{
- if (const operation_info* oif = ops[id])
- {
- // Skip aliases (e.g., update-for-install). In fact, one can argue
- // the default update should be sufficient since it is assumed to
- // update all prerequisites and we no longer support ad hoc stuff
- // like test.input. Though here we are using the dist meta-
- // operation, not perform.
- //
- if (oif->id != id)
- continue;
+ action_targets ts {tgt};
- // Use standard (perform) match.
- //
- if (oif->pre != nullptr)
+ auto mog = make_guard ([&ctx] () {ctx.match_only = false;});
+ ctx.match_only = true;
+
+ const operations& ops (rs.root_extra->operations);
+ for (operations::size_type id (default_id + 1); // Skip default_id.
+ id < ops.size ();
+ ++id)
+ {
+ if (const operation_info* oif = ops[id])
{
- if (operation_id pid = oif->pre (params, dist_id, loc))
+ // Skip aliases (e.g., update-for-install). In fact, one can
+ // argue the default update should be sufficient since it is
+ // assumed to update all prerequisites and we no longer support
+ // ad hoc stuff like test.input. Though here we are using the
+ // dist meta-operation, not perform.
+ //
+ if (oif->id != id)
+ continue;
+
+ // Use standard (perform) match.
+ //
+ if (oif->pre != nullptr)
{
- const operation_info* poif (ops[pid]);
- ctx.current_operation (*poif, oif, false /* diag_noise */);
- action a (dist_id, poif->id, oif->id);
- match (params, a, ts,
- 1 /* diag (failures only) */,
- false /* progress */);
+ if (operation_id pid = oif->pre (params, dist_id, loc))
+ {
+ const operation_info* poif (ops[pid]);
+ ctx.current_operation (*poif, oif, false /* diag_noise */);
+ action a (dist_id, poif->id, oif->id);
+ match (params, a, ts,
+ 1 /* diag (failures only) */,
+ false /* progress */);
+ }
}
- }
- ctx.current_operation (*oif, nullptr, false /* diag_noise */);
- action a (dist_id, oif->id);
- match (params, a, ts,
- 1 /* diag (failures only) */,
- false /* progress */);
+ ctx.current_operation (*oif, nullptr, false /* diag_noise */);
+ action a (dist_id, oif->id);
+ match (params, a, ts,
+ 1 /* diag (failures only) */,
+ false /* progress */);
- if (oif->post != nullptr)
- {
- if (operation_id pid = oif->post (params, dist_id))
+ if (oif->post != nullptr)
{
- const operation_info* poif (ops[pid]);
- ctx.current_operation (*poif, oif, false /* diag_noise */);
- action a (dist_id, poif->id, oif->id);
- match (params, a, ts,
- 1 /* diag (failures only) */,
- false /* progress */);
+ if (operation_id pid = oif->post (params, dist_id))
+ {
+ const operation_info* poif (ops[pid]);
+ ctx.current_operation (*poif, oif, false /* diag_noise */);
+ action a (dist_id, poif->id, oif->id);
+ match (params, a, ts,
+ 1 /* diag (failures only) */,
+ false /* progress */);
+ }
}
}
}
}
- }
- // Add buildfiles that are not normally loaded as part of the project,
- // for example, the export stub. They will still be ignored on the next
- // step if the user explicitly marked them dist=false.
- //
- auto add_adhoc = [&trace] (const scope& rs, const path& f)
- {
- path p (rs.src_path () / f);
- if (exists (p))
+ // Add buildfiles that are not normally loaded as part of the project,
+ // for example, the export stub. They will still be ignored on the
+ // next step if the user explicitly marked them dist=false.
+ //
+ add_target<buildfile> (rs, rs.root_extra->export_file);
+
+ // The same for subprojects that have been loaded.
+ //
+ if (const subprojects* ps = *rs.root_extra->subprojects)
{
- dir_path d (p.directory ());
+ for (auto p: *ps)
+ {
+ const dir_path& pd (p.second);
+ dir_path out_nroot (out_root / pd);
+ const scope& nrs (ctx.scopes.find (out_nroot));
- // Figure out if we need out.
- //
- dir_path out (rs.src_path () != rs.out_path ()
- ? out_src (d, rs)
- : dir_path ());
-
- rs.ctx.targets.insert<buildfile> (
- move (d),
- move (out),
- p.leaf ().base ().string (),
- p.extension (), // Specified.
- trace);
+ if (nrs.out_path () != out_nroot) // This subproject not loaded.
+ continue;
+
+ if (!nrs.src_path ().sub (src_root)) // Not a strong amalgamation.
+ continue;
+
+ add_target<buildfile> (nrs, nrs.root_extra->export_file);
+ }
}
- };
- add_adhoc (*rs, rs->root_extra->export_file);
+ // Collect the files. We want to take the snapshot of targets since
+ // updating some of them may result in more targets being entered.
+ //
+ // Note that we are not showing progress here (e.g., "N targets to
+ // distribute") since it will be useless (too fast).
+ //
+ const variable& dist_var (ctx.var_pool["dist"]);
- // The same for subprojects that have been loaded.
- //
- if (const subprojects* ps = *rs->root_extra->subprojects)
- {
- for (auto p: *ps)
+ for (const auto& pt: ctx.targets)
{
- const dir_path& pd (p.second);
- dir_path out_nroot (out_root / pd);
- const scope& nrs (ctx.scopes.find (out_nroot));
+ file* ft (pt->is_a<file> ());
- if (nrs.out_path () != out_nroot) // This subproject not loaded.
+ if (ft == nullptr) // Not a file.
continue;
- if (!nrs.src_path ().sub (src_root)) // Not a strong amalgamation.
- continue;
-
- add_adhoc (nrs, nrs.root_extra->export_file);
- }
- }
-
- // Collect the files. We want to take the snapshot of targets since
- // updating some of them may result in more targets being entered.
- //
- // Note that we are not showing progress here (e.g., "N targets to
- // distribute") since it will be useless (too fast).
- //
- action_targets files;
- const variable& dist_var (ctx.var_pool["dist"]);
+ if (ft->dir.sub (src_root))
+ {
+ // Include unless explicitly excluded.
+ //
+ auto l ((*ft)[dist_var]);
- for (const auto& pt: ctx.targets)
- {
- file* ft (pt->is_a<file> ());
+ if (l && !cast<bool> (l))
+ l5 ([&]{trace << "excluding " << *ft;});
+ else
+ files.push_back (ft);
- if (ft == nullptr) // Not a file.
- continue;
+ continue;
+ }
- if (ft->dir.sub (src_root))
- {
- // Include unless explicitly excluded.
- //
- auto l ((*ft)[dist_var]);
+ if (ft->dir.sub (out_root))
+ {
+ // Exclude unless explicitly included.
+ //
+ auto l ((*ft)[dist_var]);
- if (l && !cast<bool> (l))
- l5 ([&]{trace << "excluding " << *ft;});
- else
- files.push_back (ft);
+ if (l && cast<bool> (l))
+ {
+ l5 ([&]{trace << "including " << *ft;});
+ files.push_back (ft);
+ }
- continue;
+ continue;
+ }
}
- if (ft->dir.sub (out_root))
+ // Make sure what we need to distribute is up to date.
+ //
{
- // Exclude unless explicitly included.
+ if (mo_perform.meta_operation_pre != nullptr)
+ mo_perform.meta_operation_pre (params, loc);
+
+ // This is a hack since according to the rules we need to completely
+ // reset the state. We could have done that (i.e., saved target
+ // names and then re-searched them in the new tree) but that would
+ // just slow things down while this little cheat seems harmless
+ // (i.e., assume the dist mete-opreation is "compatible" with
+ // perform).
//
- auto l ((*ft)[dist_var]);
-
- if (l && cast<bool> (l))
- {
- l5 ([&]{trace << "including " << *ft;});
- files.push_back (ft);
- }
-
- continue;
- }
- }
+ // Note also that we don't do any structured result printing.
+ //
+ size_t on (ctx.current_on);
+ ctx.current_meta_operation (mo_perform);
+ ctx.current_on = on + 1;
- // Make sure what we need to distribute is up to date.
- //
- {
- if (mo_perform.meta_operation_pre != nullptr)
- mo_perform.meta_operation_pre (params, loc);
-
- // This is a hack since according to the rules we need to completely
- // reset the state. We could have done that (i.e., saved target names
- // and then re-searched them in the new tree) but that would just slow
- // things down while this little cheat seems harmless (i.e., assume
- // the dist mete-opreation is "compatible" with perform).
- //
- // Note also that we don't do any structured result printing.
- //
- size_t on (ctx.current_on);
- ctx.current_meta_operation (mo_perform);
- ctx.current_on = on + 1;
+ if (mo_perform.operation_pre != nullptr)
+ mo_perform.operation_pre (params, update_id);
- if (mo_perform.operation_pre != nullptr)
- mo_perform.operation_pre (params, update_id);
+ ctx.current_operation (op_update, nullptr, false /* diag_noise */);
- ctx.current_operation (op_update, nullptr, false /* diag_noise */);
+ action a (perform_update_id);
- action a (perform_update_id);
+ mo_perform.match (params, a, files,
+ 1 /* diag (failures only) */,
+ prog /* progress */);
- mo_perform.match (params, a, files,
- 1 /* diag (failures only) */,
- prog /* progress */);
+ mo_perform.execute (params, a, files,
+ 1 /* diag (failures only) */,
+ prog /* progress */);
- mo_perform.execute (params, a, files,
- 1 /* diag (failures only) */,
- prog /* progress */);
+ if (mo_perform.operation_post != nullptr)
+ mo_perform.operation_post (params, update_id);
- if (mo_perform.operation_post != nullptr)
- mo_perform.operation_post (params, update_id);
+ if (mo_perform.meta_operation_post != nullptr)
+ mo_perform.meta_operation_post (params);
+ }
+ }
+ else
+ {
+ l5 ([&]{trace << "bootstrap dist " << rs;});
- if (mo_perform.meta_operation_post != nullptr)
- mo_perform.meta_operation_post (params);
+ // Recursively enter/collect file targets in src_root ignoring those
+ // that start with a dot.
+ //
+ // Note that, in particular, we also collect the symlinks which point
+ // outside src_root (think of third-party project packaging with the
+ // upstream git submodule at the root of the git project). Also note
+ // that we could probably exclude symlinks which point outside the VCS
+ // project (e.g., backlinks in a forwarded configuration) but that
+ // would require the user to supply this boundary (since we don't have
+ // the notion of VCS root at this level). So let's keep it simple for
+ // now.
+ //
+ add_subdir (rs, dir_path (), files);
}
dir_path td (dist_root / dir_path (dist_package));
@@ -348,7 +406,7 @@ namespace build2
// Copy over all the files. Apply post-processing callbacks.
//
- module& mod (*rs->find_module<module> (module::name));
+ module& mod (*rs.find_module<module> (module::name));
prog = prog && show_progress (1 /* max_verb */);
size_t prog_percent (0);
@@ -370,10 +428,10 @@ namespace build2
// See if this file is in a subproject.
//
- const scope* srs (rs);
+ const scope* srs (&rs);
const module::callbacks* cbs (&mod.callbacks_);
- if (const subprojects* ps = *rs->root_extra->subprojects)
+ if (const subprojects* ps = *rs.root_extra->subprojects)
{
for (auto p: *ps)
{
@@ -446,9 +504,9 @@ namespace build2
// Archive and checksum if requested.
//
- if (lookup as = rs->vars["dist.archives"])
+ if (lookup as = rs.vars["dist.archives"])
{
- lookup cs (rs->vars["dist.checksums"]);
+ lookup cs (rs.vars["dist.checksums"]);
// Split the dist.{archives,checksums} value into a directory and
// extension.
@@ -487,6 +545,33 @@ namespace build2
}
}
+ static void
+ dist_load_execute (const values&, action, action_targets& ts,
+ uint16_t, bool prog)
+ {
+ // We cannot do multiple projects because we need to start with a clean
+ // set of targets.
+ //
+ if (ts.size () != 1)
+ fail << "multiple targets in dist meta-operation" <<
+ info << "one dist meta-operation can handle one project" <<
+ info << "consider using several dist meta-operations";
+
+ const target& t (ts[0].as<target> ());
+ const scope* rs (t.base_scope ().root_scope ());
+
+ if (rs == nullptr ||
+ !t.is_a<dir> () ||
+ (rs->out_path () != t.dir && rs->src_path () != t.dir))
+ fail << "dist meta-operation target must be project root directory";
+
+ if (rs->out_path () == rs->src_path ())
+ fail << "in-tree distribution of target " << t <<
+ info << "distribution requires out-of-tree build";
+
+ dist_project (*rs, &t, prog);
+ }
+
// install -d <dir>
//
static void
@@ -838,7 +923,7 @@ namespace build2
const prerequisite_member& p,
include_type i)
{
- tracer trace ("dist_include");
+ tracer trace ("dist::dist_include");
// Override excluded to adhoc so that every source is included into the
// distribution. Note that this should be harmless to a custom rule
@@ -854,7 +939,7 @@ namespace build2
return i;
}
- const meta_operation_info mo_dist {
+ const meta_operation_info mo_dist_load {
dist_id,
"dist",
"distribute",
@@ -867,10 +952,77 @@ namespace build2
&load, // normal load
&search, // normal search
nullptr, // no match (see dist_execute()).
- &dist_execute,
+ &dist_load_execute,
nullptr, // operation post
nullptr, // meta-operation post
&dist_include
};
+
+ // The bootstrap distribution mode.
+ //
+ // Note: pretty similar overall idea as the info meta-operation.
+ //
+ void
+ init_config (scope&); // init.cxx
+
+ static void
+ dist_bootstrap_load (const values&,
+ scope& rs,
+ const path&,
+ const dir_path& out_base,
+ const dir_path& src_base,
+ const location& l)
+ {
+ if (rs.out_path () != out_base || rs.src_path () != src_base)
+ fail (l) << "dist meta-operation target must be project root directory";
+
+ setup_base (rs.ctx.scopes.rw (rs).insert (out_base), out_base, src_base);
+
+ // Also initialize the dist.* variables (needed in dist_project()).
+ //
+ init_config (rs);
+ }
+
+ void
+ dist_bootstrap_search (const values&,
+ const scope& rs,
+ const scope&,
+ const path&,
+ const target_key& tk,
+ const location& l,
+ action_targets& ts)
+ {
+ if (!tk.type->is_a<dir> ())
+ fail (l) << "dist meta-operation target must be project root directory";
+
+ ts.push_back (&rs);
+ }
+
+ static void
+ dist_bootstrap_execute (const values&, action, action_targets& ts,
+ uint16_t, bool prog)
+ {
+ for (const action_target& at: ts)
+ dist_project (at.as<scope> (), nullptr, prog);
+ }
+
+ const meta_operation_info mo_dist_bootstrap {
+ dist_id,
+ "dist",
+ "distribute",
+ "distributing",
+ "distributed",
+ "has nothing to distribute",
+ true, // bootstrap_outer //@@ Maybe not? (But will overrides work)?
+ nullptr, // meta-operation pre
+ &dist_operation_pre,
+ &dist_bootstrap_load,
+ &dist_bootstrap_search,
+ nullptr, // no match (see dist_bootstrap_execute()).
+ &dist_bootstrap_execute,
+ nullptr, // operation post
+ nullptr, // meta-operation post
+ nullptr // include
+ };
}
}
diff --git a/libbuild2/dist/operation.hxx b/libbuild2/dist/operation.hxx
index 8030590..b6730f4 100644
--- a/libbuild2/dist/operation.hxx
+++ b/libbuild2/dist/operation.hxx
@@ -13,7 +13,8 @@ namespace build2
{
namespace dist
{
- extern const meta_operation_info mo_dist;
+ extern const meta_operation_info mo_dist_load;
+ extern const meta_operation_info mo_dist_bootstrap;
}
}
diff --git a/libbuild2/version/init.cxx b/libbuild2/version/init.cxx
index d11b2f3..ced9c68 100644
--- a/libbuild2/version/init.cxx
+++ b/libbuild2/version/init.cxx
@@ -30,6 +30,44 @@ namespace build2
static const in_rule in_rule_;
static const manifest_install_rule manifest_install_rule_;
+ static void
+ dist_callback (const path&, const scope&, void*);
+
+ void
+ boot_post (scope& rs, const location&, module_boot_post_extra& extra)
+ {
+ // If the dist module is used, set its dist.package and register the
+ // post-processing callback.
+ //
+ if (auto* dm = rs.find_module<dist::module> (dist::module::name))
+ {
+ // Don't touch if dist.package was set by the user.
+ //
+ value& val (rs.assign (dm->var_dist_package));
+
+ if (!val)
+ {
+ auto& m (extra.module_as<module> ());
+ const standard_version& v (m.version);
+
+ // We've already verified in boot() it is named.
+ //
+ string p (project (rs).string ());
+ p += '-';
+ p += v.string ();
+ val = move (p);
+
+ // Only register the post-processing callback if this is a rewritten
+ // snapshot.
+ //
+ if (m.rewritten)
+ dm->register_callback (dir_path (".") / manifest_file,
+ &dist_callback,
+ &m);
+ }
+ }
+ }
+
void
boot (scope& rs, const location& l, module_boot_extra& extra)
{
@@ -281,19 +319,17 @@ namespace build2
// Initialize second (dist.package, etc).
//
+ extra.post = &boot_post;
extra.init = module_boot_init::before_second;
}
- static void
- dist_callback (const path&, const scope&, void*);
-
bool
init (scope& rs,
scope&,
const location& l,
bool first,
bool,
- module_init_extra& extra)
+ module_init_extra&)
{
tracer trace ("version::init");
@@ -304,43 +340,6 @@ namespace build2
//
load_module (rs, rs, "in.base", l);
- auto& m (extra.module_as<module> ());
- const standard_version& v (m.version);
-
- // If the dist module is used, set its dist.package and register the
- // post-processing callback.
- //
- if (auto* dm = rs.find_module<dist::module> (dist::module::name))
- {
- // Make sure dist is init'ed, not just boot'ed.
- //
- load_module (rs, rs, "dist", l);
-
- m.dist_uncommitted = cast_false<bool> (rs["config.dist.uncommitted"]);
-
- // Don't touch if dist.package was set by the user.
- //
- value& val (rs.assign (dm->var_dist_package));
-
- if (!val)
- {
- // We've already verified in boot() it is named.
- //
- string p (project (rs).string ());
- p += '-';
- p += v.string ();
- val = move (p);
-
- // Only register the post-processing callback if this is a rewritten
- // snapshot.
- //
- if (m.rewritten)
- dm->register_callback (dir_path (".") / manifest_file,
- &dist_callback,
- &m);
- }
- }
-
// Register rules.
//
rs.insert_rule<file> (perform_update_id, "version.in", in_rule_);
@@ -363,7 +362,7 @@ namespace build2
// Complain if this is an uncommitted snapshot.
//
- if (!m.committed && !m.dist_uncommitted)
+ if (!m.committed && !cast_false<bool> (rs["config.dist.uncommitted"]))
fail << "distribution of uncommitted project " << rs.src_path () <<
info << "specify config.dist.uncommitted=true to force";
diff --git a/libbuild2/version/module.hxx b/libbuild2/version/module.hxx
index bfa7d8c..26bd48a 100644
--- a/libbuild2/version/module.hxx
+++ b/libbuild2/version/module.hxx
@@ -44,8 +44,6 @@ namespace build2
dependencies_type dependencies;
- bool dist_uncommitted = false;
-
module (const project_name& p,
butl::standard_version v,
bool c,