aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2018-04-26 09:48:58 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2018-04-26 09:48:58 +0200
commite86333fa8790f624d82ecea2a8da40f85a1d3630 (patch)
tree91166f5070c5a43310faa3883030ccdf23182193
parent7839410d934bb36fdac2e4125c72febf73991320 (diff)
Add support for forwarded configurations
-rw-r--r--bdep/bdep.cli14
-rw-r--r--bdep/config.cli39
-rw-r--r--bdep/config.cxx66
-rw-r--r--bdep/config.hxx7
-rw-r--r--bdep/init.cli16
-rw-r--r--bdep/init.cxx40
-rw-r--r--bdep/init.hxx3
-rw-r--r--bdep/new.cli16
-rw-r--r--bdep/new.cxx30
-rw-r--r--bdep/project.cli25
-rw-r--r--bdep/project.cxx148
-rw-r--r--bdep/project.hxx4
-rw-r--r--bdep/project.xml1
-rw-r--r--bdep/sync.cxx78
-rw-r--r--bdep/utility.cxx8
-rw-r--r--bdep/utility.hxx13
-rw-r--r--bdep/utility.txx75
-rwxr-xr-xdoc/cli.sh1
18 files changed, 423 insertions, 161 deletions
diff --git a/bdep/bdep.cli b/bdep/bdep.cli
index 19c79af..d7ce274 100644
--- a/bdep/bdep.cli
+++ b/bdep/bdep.cli
@@ -217,7 +217,17 @@ namespace bdep
-a} in the previous example), then it will print a line identifying
each configuration before printing the command's result.
- @@ build and run?
+ By default the project's source directory is configured to forward to
+ the default build configuration. That is, we can run the build system
+ in the source directory and it will automatically build in the
+ forwarded configuration as well as link the results back to the
+ source directory using symlinks or a another suitable mechanism (see
+ \l{bdep-config(1)} for details). For example:
+
+ \
+ $ b # build in gcc
+ $ ./hello # run the result
+ \
|
@@ -272,6 +282,8 @@ namespace bdep
libhello >= 1.0.0 configured 1.0.0
\
+ @@ TODO: build system hook.
+
|
\li|\b{Upgrade or Downgrade Dependencies}\n
diff --git a/bdep/config.cli b/bdep/config.cli
index 62ef5da..c9b2ce8 100644
--- a/bdep/config.cli
+++ b/bdep/config.cli
@@ -24,13 +24,22 @@ namespace bdep
// @@ Should we be able to remove config with init'ed packages? Who
// is going to drop them in configs? Or require them to be de-init'ed?
//
+ // @@ Should set be able to alter forward flag? This will require both
+ // disfiguring forward on old and configuring on new. Perhaps best
+ // done by sync? I.e., for this change to take effect, one has to
+ // run sync on both configs? Then sync would somehow need to know
+ // to remove forwards. Maybe it should always check for stray
+ // forwards (via out-root.build).
+ //
"\h|SYNOPSIS|
\c{\b{bdep config add} \ \ \ [<options>] [<prj-spec>] [\b{@}<cfg-name>] <cfg-dir>\n
\b{bdep config create} [<options>] [<prj-spec>] [\b{@}<cfg-name>] <cfg-dir> [<cfg-args>]\n
\b{bdep config remove} [<options>] [<prj-spec>] <cfg-spec>... | \b{--all}|\b{-a}\n
\b{bdep config rename} [<options>] [<prj-spec>] <cfg-spec> <cfg-name>\n
- \b{bdep config set} \ \ \ [<options>] [<prj-spec>] <cfg-spec> \b{--}[\b{no-}]\b{default}}
+ \b{bdep config set} \ \ \ [<options>] [<prj-spec>] <cfg-spec>\n
+ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ [\b{--}[\b{no-}]\b{default}]\n
+ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ [\b{--}[\b{no-}]\b{forward}]}
\c{<cfg-spec> = \b{@}<cfg-name> | \b{--config}|\b{-c} <cfg-dir>\n
<prj-spec> = \b{--directory}|\b{-d} <prj-dir>\n
@@ -72,7 +81,15 @@ namespace bdep
\cb{bdep} commands use such a configuration by default if no
configuration was specified explicitly (see
\l{bdep-projects-configs(1)} for details). To make a subsequently
- added configuration the default use the \cb{--default} option.|
+ added configuration the default use the \cb{--default} option.
+
+ The default build configuration is also designated as forwarded
+ unless the \cb{--no-forward} option is specified or another
+ configuration is already designated as forwarded. When a project is
+ initialized in a forwarded build configuration, its source directory
+ is configured to forward to this configuration (see \l{b(1)} for
+ details on forwarded configurations). To forward to a non-default
+ configuration use the \cb{--forward} option.|
\li|\cb{remove}
@@ -89,8 +106,10 @@ namespace bdep
\li|\cb{set}
The \cb{set} subcommand sets various properties of the specified
- build configuration. These include the default flag
- (\c{\b{--}[\b{no-}]\b{default}}).||"
+ build configuration. These include the default
+ (\c{\b{--}[\b{no-}]\b{default}}) and forward
+ (\c{\b{--}[\b{no-}]\b{forward}}) flags. Note that changing the
+ forward flag requires an explicit \cb{sync} command to take effect.||"
bool add;
bool create;
@@ -102,18 +121,8 @@ namespace bdep
// Note that not all project/configuration options are valid for all
// subcommands.
//
- class cmd_config_options: project_options
+ class cmd_config_options: configuration_add_options, project_options
{
"\h|CONFIG OPTIONS|"
-
- bool --default
- {
- "Make the added or created configuration the default."
- }
-
- bool --no-default
- {
- "Don't make the first added or created configuration then default."
- }
};
}
diff --git a/bdep/config.cxx b/bdep/config.cxx
index 0acd227..0dcea48 100644
--- a/bdep/config.cxx
+++ b/bdep/config.cxx
@@ -18,6 +18,7 @@ namespace bdep
dir_path path,
optional<string> name,
optional<bool> def,
+ optional<bool> fwd,
optional<uint64_t> id,
const char* what)
{
@@ -27,19 +28,32 @@ namespace bdep
transaction t (db.begin ());
using count = configuration_count;
- using query = bdep::query<count>;
- // By default the first added configuration is the default.
- //
- if (!def)
- def = (db.query_value<count> () == 0);
- else if (*def)
{
using query = bdep::query<configuration>;
- if (auto p = db.query_one<configuration> (query::default_))
- fail << "configuration " << *p << " is already the default" <<
- info << "use 'bdep config set --no-default' to clear";
+ // By default the first added configuration is the default.
+ //
+ if (!def)
+ def = (db.query_value<count> () == 0);
+ else if (*def)
+ {
+ if (auto p = db.query_one<configuration> (query::default_))
+ fail << "configuration " << *p << " is already the default" <<
+ info << "use 'bdep config set --no-default' to clear";
+ }
+
+ // By default the default configuration is forwarded unless another
+ // is already forwarded.
+ //
+ if (!fwd)
+ fwd = *def && db.query_one<configuration> (query::forward) == nullptr;
+ else if (*fwd)
+ {
+ if (auto p = db.query_one<configuration> (query::forward))
+ fail << "configuration " << *p << " is already forwarded" <<
+ info << "use 'bdep config set --no-forward' to clear";
+ }
}
// Make sure the configuration path is absolute and normalized. Also
@@ -58,6 +72,7 @@ namespace bdep
path,
move (rel_path),
*def,
+ *fwd,
{} /* packages */});
try
@@ -66,6 +81,8 @@ namespace bdep
}
catch (const odb::exception&)
{
+ using query = bdep::query<count>;
+
// See if this is id, name, or path conflict.
//
if (id && db.query_value<count> (query::id == *id) != 0)
@@ -94,6 +111,7 @@ namespace bdep
if (r->name) dr << '@' << *r->name << ' ';
/* */ dr << r->path << " (" << *r->id;
if (r->default_) dr << ", default";
+ if (r->forward) dr << ", forwarded";
/* */ dr << ')';
}
@@ -114,6 +132,7 @@ namespace bdep
cli::scanner& cfg_args,
optional<string> name,
optional<bool> def,
+ optional<bool> fwd,
optional<uint64_t> id)
{
// Call bpkg to create the configuration.
@@ -131,6 +150,7 @@ namespace bdep
move (path),
move (name),
def,
+ fwd,
id,
"created");
}
@@ -159,6 +179,25 @@ namespace bdep
fail << "@@ TODO" << endf;
}
+ const char*
+ cmd_config_validate_add (const configuration_add_options& o)
+ {
+ // --[no-]default
+ //
+ if (o.default_ () && o.no_default ())
+ fail << "both --default and --no-default specified";
+
+ // --[no-]forward
+ //
+ if (o.forward () && o.no_forward ())
+ fail << "both --forward and --no-forward specified";
+
+ return (o.default_ () ? "--default" :
+ o.forward () ? "--forward" :
+ o.no_default () ? "--no-default" :
+ o.no_forward () ? "--no-forward" : nullptr);
+ }
+
int
cmd_config (const cmd_config_options& o, cli::scanner& scan)
{
@@ -171,14 +210,7 @@ namespace bdep
// Validate options/subcommands.
//
-
- // --[no-]default
- //
- if (o.default_ () && o.no_default ())
- fail << "both --default and --no-default specified";
-
- if (const char* n = (o.default_ () ? "--default" :
- o.no_default () ? "--no-default" : nullptr))
+ if (const char* n = cmd_config_validate_add (o))
{
if (!(c.add () || c.create () || c.set ()))
fail << n << " not valid for this command";
diff --git a/bdep/config.hxx b/bdep/config.hxx
index 2ebda45..b521bac 100644
--- a/bdep/config.hxx
+++ b/bdep/config.hxx
@@ -19,6 +19,7 @@ namespace bdep
dir_path path,
optional<string> name,
optional<bool> default_ = nullopt,
+ optional<bool> forward = nullopt,
optional<uint64_t> id = nullopt,
const char* what = "added");
@@ -30,10 +31,16 @@ namespace bdep
cli::scanner& args,
optional<string> name,
optional<bool> default_ = nullopt,
+ optional<bool> forward = nullopt,
optional<uint64_t> id = nullopt);
int
cmd_config (const cmd_config_options&, cli::scanner& args);
+
+ // Validate returning one of the options or NULL if none specified.
+ //
+ const char*
+ cmd_config_validate_add (const configuration_add_options&);
}
#endif // BDEP_CONFIG_HXX
diff --git a/bdep/init.cli b/bdep/init.cli
index c72e4c3..57b1abb 100644
--- a/bdep/init.cli
+++ b/bdep/init.cli
@@ -137,7 +137,7 @@ namespace bdep
"
}
- class cmd_init_options: project_options
+ class cmd_init_options: configuration_add_options, project_options
{
"\h|INIT OPTIONS|"
@@ -157,19 +157,5 @@ namespace bdep
"<dir>",
"Create a new build configuration in <dir>."
}
-
- bool --default
- {
- "Make the added or created configuration the default. Only valid with
- \cb{--config-add} or \cb{--config-create}, see \l{bdep-config(1)} for
- details."
- }
-
- bool --no-default
- {
- "Don't make the first added or created configuration then default. Only
- valid with \cb{--config-add} or \cb{--config-create}, see
- \l{bdep-config(1)} for details."
- }
};
}
diff --git a/bdep/init.cxx b/bdep/init.cxx
index 49a2de4..1875c42 100644
--- a/bdep/init.cxx
+++ b/bdep/init.cxx
@@ -24,7 +24,8 @@ namespace bdep
cli::scanner& args,
bool ca,
bool cc,
- optional<bool> cd)
+ optional<bool> cd,
+ optional<bool> cf)
{
const char* m (!ca ? "--config-create" :
!cc ? "--config-add" : nullptr);
@@ -32,13 +33,13 @@ namespace bdep
if (m == nullptr)
fail << "both --config-add and --config-create specified";
- optional<string> name;
+ optional<string> nm;
if (size_t n = o.config_name ().size ())
{
if (n > 1)
fail << "multiple configuration names specified for " << m;
- name = o.config_name ()[0];
+ nm = o.config_name ()[0];
}
optional<uint64_t> id;
@@ -51,8 +52,8 @@ namespace bdep
}
return ca
- ? cmd_config_add ( prj, db, cfg, move (name), cd, move (id))
- : cmd_config_create (o, prj, db, cfg, args, move (name), cd, move (id));
+ ? cmd_config_add ( prj, db, cfg, move (nm), cd, cf, move (id))
+ : cmd_config_create (o, prj, db, cfg, args, move (nm), cd, cf, move (id));
}
void
@@ -128,24 +129,18 @@ namespace bdep
bool ca (o.config_add_specified ());
bool cc (o.config_create_specified ());
- optional<bool> cd;
- if (o.default_ () || o.no_default ())
- {
- if (!ca && !cc)
- fail << "--[no-]default specified without --config-(add|create)";
-
- if (o.default_ () && o.no_default ())
- fail << "both --default and --no-default specified";
-
- cd = o.default_ () && !o.no_default ();
- }
-
if (o.empty ())
{
if (ca) fail << "both --empty and --config-add specified";
if (cc) fail << "both --empty and --config-create specified";
}
+ if (const char* n = cmd_config_validate_add (o))
+ {
+ if (!ca && !cc)
+ fail << n << " specified without --config-(add|create)";
+ }
+
project_packages pp (
find_project_packages (o, o.empty () /* ignore_packages */));
@@ -186,6 +181,14 @@ namespace bdep
configurations cfgs;
if (ca || cc)
{
+ optional<bool> cd;
+ if (o.default_ () || o.no_default ())
+ cd = o.default_ () && !o.no_default ();
+
+ optional<bool> cf;
+ if (o.forward () || o.no_forward ())
+ cf = o.forward () && !o.no_forward ();
+
cfgs.push_back (
cmd_init_config (
o,
@@ -195,7 +198,8 @@ namespace bdep
args,
ca,
cc,
- cd));
+ cd,
+ cf));
// Fall through.
}
diff --git a/bdep/init.hxx b/bdep/init.hxx
index 7905306..6e52737 100644
--- a/bdep/init.hxx
+++ b/bdep/init.hxx
@@ -23,7 +23,8 @@ namespace bdep
cli::scanner& cfg_args,
bool config_add_specified,
bool config_create_specified,
- optional<bool> config_default);
+ optional<bool> config_default,
+ optional<bool> config_forward);
// Initialize each package in each configuration skipping those that are
// already initialized. Then synchronize each configuration.
diff --git a/bdep/new.cli b/bdep/new.cli
index 377dbec..f5f2a14 100644
--- a/bdep/new.cli
+++ b/bdep/new.cli
@@ -145,7 +145,7 @@ namespace bdep
{
};
- class cmd_new_options: configuration_name_options
+ class cmd_new_options: configuration_add_options, configuration_name_options
{
"\h|NEW OPTIONS|"
@@ -189,19 +189,5 @@ namespace bdep
\cb{git} (default) and \cb{none}. Valid values for <opt> are
system-specific."
}
-
- bool --default
- {
- "Make the added or created configuration the default. Only valid with
- \cb{--config-add} or \cb{--config-create}, see \l{bdep-config(1)} for
- details."
- }
-
- bool --no-default
- {
- "Don't make the first added or created configuration then default. Only
- valid with \cb{--config-add} or \cb{--config-create}, see
- \l{bdep-config(1)} for details."
- }
};
}
diff --git a/bdep/new.cxx b/bdep/new.cxx
index 97c8352..5f99e6c 100644
--- a/bdep/new.cxx
+++ b/bdep/new.cxx
@@ -9,6 +9,7 @@
#include <bdep/diagnostics.hxx>
#include <bdep/init.hxx>
+#include <bdep/config.hxx>
using namespace std;
@@ -26,24 +27,18 @@ namespace bdep
bool ca (o.config_add_specified ());
bool cc (o.config_create_specified ());
- optional<bool> cd;
- if (o.default_ () || o.no_default ())
- {
- if (!ca && !cc)
- fail << "--[no-]default specified without --config-(add|create)";
-
- if (o.default_ () && o.no_default ())
- fail << "both --default and --no-default specified";
-
- cd = o.default_ () && !o.no_default ();
- }
-
if (o.no_init ())
{
if (ca) fail << "both --no-init and --config-add specified";
if (cc) fail << "both --no-init and --config-create specified";
}
+ if (const char* n = cmd_config_validate_add (o))
+ {
+ if (!ca && !cc)
+ fail << n << " specified without --config-(add|create)";
+ }
+
// Validate type options.
//
const type& t (o.type ());
@@ -621,6 +616,14 @@ namespace bdep
if (ca || cc)
{
+ optional<bool> cd;
+ if (o.default_ () || o.no_default ())
+ cd = o.default_ () && !o.no_default ();
+
+ optional<bool> cf;
+ if (o.forward () || o.no_forward ())
+ cf = o.forward () && !o.no_forward ();
+
configurations cfgs {
cmd_init_config (
o,
@@ -630,7 +633,8 @@ namespace bdep
args,
ca,
cc,
- cd)};
+ cd,
+ cf)};
package_locations pkgs {{n, dir_path ()}}; // project == package
diff --git a/bdep/project.cli b/bdep/project.cli
index 15faea8..137c77d 100644
--- a/bdep/project.cli
+++ b/bdep/project.cli
@@ -8,6 +8,31 @@ include <bdep/common.cli>;
namespace bdep
{
+ // Common options for commands that add/create configurations.
+ //
+ class configuration_add_options
+ {
+ bool --default
+ {
+ "Make the added or created configuration the default."
+ }
+
+ bool --no-default
+ {
+ "Don't make the first added or created configuration then default."
+ }
+
+ bool --forward
+ {
+ "Make the added or created configuration forwarded."
+ }
+
+ bool --no-forward
+ {
+ "Don't make the added or created configuration forwarded."
+ }
+ };
+
// Common options for commands that accept --config-id and @<cfg-name>.
//
class configuration_name_options: common_options
diff --git a/bdep/project.cxx b/bdep/project.cxx
index 6b5ee30..8d922b6 100644
--- a/bdep/project.cxx
+++ b/bdep/project.cxx
@@ -110,7 +110,7 @@ namespace bdep
return r;
}
- // Given a directory which can a project root, a package root, or one of
+ // Given a directory which can be a project root, a package root, or one of
// their subdirectories, return the absolute project (first) and relative
// package (second) directories. The package directory may be absent if the
// given directory is not within a package root or empty if the project and
@@ -186,6 +186,69 @@ namespace bdep
return project_package {move (prj), move (pkg)};
}
+ static package_locations
+ load_package_locations (const dir_path& prj)
+ {
+ package_locations pls;
+
+ // If exists, load packages.manifest from the project root. Otherwise,
+ // this must be a simple, single-package project.
+ //
+ path f (prj / packages_file);
+
+ if (exists (f))
+ {
+ using bpkg::package_manifest;
+ using bpkg::dir_package_manifests;
+
+ auto ms (parse_manifest<dir_package_manifests> (f, "packages"));
+
+ // While an empty repository is legal, in our case it doesn't make much
+ // sense and will just further complicate things.
+ //
+ if (ms.empty ())
+ fail << "no packages listed in " << f;
+
+ for (package_manifest& m: ms)
+ {
+ // Convert the package location from POSIX to the host form and make
+ // sure the current directory is represented as an empty path.
+ //
+ assert (m.location);
+ dir_path d (path_cast<dir_path> (move (*m.location)));
+ d.normalize (false /* actualize */, true /* cur_empty */);
+
+ pls.push_back (package_location {string (), move (d)});
+ }
+ }
+ else
+ pls.push_back (package_location {string (), dir_path ()});
+
+ return pls;
+ }
+
+ static void
+ load_package_names (const dir_path& prj, package_locations& pls)
+ {
+ // Load each package's manifest and obtain its name (name is normally the
+ // first value so we could optimize this, if necessary).
+ //
+ for (package_location& pl: pls)
+ {
+ path f (prj / pl.path / manifest_file);
+ auto m (parse_manifest<bpkg::package_manifest> (f, "package"));
+ pl.name = move (m.name);
+ }
+ }
+
+ package_locations
+ load_packages (const dir_path& prj)
+ {
+ package_locations pls (load_package_locations (prj));
+ load_package_names (prj, pls);
+ return pls;
+ }
+
project_packages
find_project_packages (const project_options& po,
bool ignore_packages,
@@ -246,82 +309,37 @@ namespace bdep
if (!ignore_packages)
{
- // If exists, load packages.manifest from the project root and either
- // verify that the discovered packages are in it or, if nothing was
- // discovered, use it as the source for the package list.
+ // Load the package locations and either verify that the discovered
+ // packages are in it or, if nothing was discovered, use it as the
+ // source for the package list.
//
- path f (r.project / packages_file);
+ package_locations pls (load_package_locations (r.project));
- if (exists (f))
+ if (!r.packages.empty ())
{
- using bpkg::package_manifest;
- using bpkg::dir_package_manifests;
-
- auto ms (parse_manifest<dir_package_manifests> (f, "packages"));
-
- // While an empty repository is legal, in our case it doesn't make
- // much sense and will just further complicate things.
- //
- if (ms.empty ())
- fail << "no packages listed in " << f;
-
- // Convert the package location from POSIX to the host form and make
- // sure the current directory is represented as an empty path.
- //
- auto location = [] (const package_manifest& m)
+ for (const package_location& x: r.packages)
{
- assert (m.location);
- dir_path d (path_cast<dir_path> (*m.location));
- d.normalize (false /* actualize */, true /* cur_empty */);
- return d;
- };
-
- if (!r.packages.empty ())
- {
- // It could be costly to normalize the location for each
- // comparison. We, however, do not expect more than a handful of
- // packages so we are probably ok.
- //
- for (const package_location& pl: r.packages)
+ if (find_if (pls.begin (),
+ pls.end (),
+ [&x] (const package_location& y)
+ {
+ return x.path == y.path;
+ }) == pls.end ())
{
- const dir_path& p (pl.path);
-
- if (find_if (ms.begin (),
- ms.end (),
- [&p, &location] (const package_manifest& m)
- {
- return p == location (m);
- }) == ms.end ())
- {
- fail << "package directory " << p << " is not listed in " << f;
- }
+ fail << "package directory " << x.path << " is not listed in "
+ << r.project;
}
}
- else if (load_packages)
- {
- // Name is to be extracted later.
- //
- for (package_manifest& m: ms)
- r.packages.push_back (package_location {"", location (m)});
- }
}
- else
+ else if (load_packages)
{
- // If packages.manifest does not exist, then this must be a simple
- // project.
+ // Names to be extracted later.
//
- assert (r.packages.size () == 1 && r.packages[0].path.empty ());
+ r.packages = move (pls);
}
- // Load each package's manifest and obtain its name (name is normally
- // the first value so we could optimize this, if necessary).
- //
- for (package_location& pl: r.packages)
- {
- path f (r.project / pl.path / manifest_file);
- auto m (parse_manifest<bpkg::package_manifest> (f, "package"));
- pl.name = move (m.name);
- }
+ if (!r.packages.empty ())
+ load_package_names (r.project, r.packages);
}
return r;
diff --git a/bdep/project.hxx b/bdep/project.hxx
index 2b1894c..2e8d366 100644
--- a/bdep/project.hxx
+++ b/bdep/project.hxx
@@ -78,6 +78,7 @@ namespace bdep
optional_dir_path relative_path;
bool default_;
+ bool forward;
// We made it a vector instead of set/map since we are unlikely to have
// more than a handful of packages. We may, however, want to use a change-
@@ -162,6 +163,9 @@ namespace bdep
using package_locations = vector<package_location>;
+ package_locations
+ load_packages (const dir_path& prj);
+
struct project_packages
{
dir_path project;
diff --git a/bdep/project.xml b/bdep/project.xml
index 5685945..8d1bf26 100644
--- a/bdep/project.xml
+++ b/bdep/project.xml
@@ -6,6 +6,7 @@
<column name="path" type="TEXT" null="true"/>
<column name="relative_path" type="TEXT" null="true"/>
<column name="default" type="INTEGER" null="true"/>
+ <column name="forward" type="INTEGER" null="true"/>
<primary-key auto="true">
<column name="id"/>
</primary-key>
diff --git a/bdep/sync.cxx b/bdep/sync.cxx
index 370e4f4..6c29a50 100644
--- a/bdep/sync.cxx
+++ b/bdep/sync.cxx
@@ -70,7 +70,7 @@ namespace bdep
}) != prj_pkgs.end ())
{
// The project package itself must always be upgraded to the latest
- // version/iteration. So we have to translate our option to their
+ // version/iteration. So we have to translate our options to their
// dependency-only --{upgrade,patch}-{recursive,immediate}.
//
args.push_back ("{");
@@ -119,6 +119,82 @@ namespace bdep
"--plan", "synchronizing:",
(yes ? "--yes" : nullptr),
args);
+
+
+ // Handle configuration forwarding.
+ //
+ // We do it here (instead of, say init) because a change in a package may
+ // introduce new subprojects. Though it would be nice to only do this if
+ // the package was upgraded (probably by comparing before/after versions
+ // @@ TODO: changed flag below).
+ //
+ // Also, the current thinking is that config set --[no-]forward is best
+ // implemented by just changing the flag on the configuration and then
+ // requiring an explicit sync to configure/disfigure forwards.
+ //
+ // @@ TODO: could optimize out version query/comparison if pkg-build
+ // signalled that nothing has changed (the --exit-result idea). Would
+ // be nice to optimize the whole thing. Maybe --force flag (for things
+ // like config set --no-forward)? Or maybe we should require explicit
+ // sync for certain changes (and pass --implicit or some such in hook
+ // -- after all, configuring forward of a project being bootstrapped
+ // might get tricky).
+ //
+ package_locations pls (load_packages (prj));
+
+ for (const package_state& p: c->packages)
+ {
+ // If this is a forwarded configuration, make sure forwarding is
+ // configured and is up-to-date. Otherwise, make sure it is disfigured
+ // (the config set --no-forward case).
+ //
+ dir_path src (prj);
+ {
+ auto i (find_if (pls.begin (),
+ pls.end (),
+ [&p] (const package_location& pl)
+ {
+ return p.name == pl.name;
+ }));
+
+ if (i == pls.end ())
+ fail << "package " << p.name << " is not listed in " << prj;
+
+ src /= i->path;
+ }
+
+ // We could run 'b info' and used the 'forwarded' value but this is
+ // both faster and simpler.
+ //
+ path f (src / "build" / "bootstrap" / "out-root.build");
+ bool e (exists (f));
+
+ const char* o (nullptr);
+ if (c->forward)
+ {
+ bool changed (true);
+
+ if (changed || !e)
+ o = "configure:";
+ }
+ else
+ {
+ //@@ This is broken: we will disfigure forwards to other configs.
+ // Looks like we will need to test that the forward is to this
+ // config. 'b info' here we come?
+
+ //if (e)
+ // o = "disfigure:";
+ }
+
+ if (o != nullptr)
+ {
+ dir_path out (dir_path (c->path) /= p.name);
+ string arg (src.representation () + '@' + out.representation () +
+ ",forward");
+ run_b (co, o, arg);
+ }
+ }
}
void
diff --git a/bdep/utility.cxx b/bdep/utility.cxx
index f8ef1df..a902273 100644
--- a/bdep/utility.cxx
+++ b/bdep/utility.cxx
@@ -107,4 +107,12 @@ namespace bdep
? co.bpkg ().string ().c_str ()
: "bpkg" BDEP_EXE_SUFFIX;
}
+
+ const char*
+ name_b (const common_options& co)
+ {
+ return co.build_specified ()
+ ? co.build ().string ().c_str ()
+ : "b" BDEP_EXE_SUFFIX;
+ }
}
diff --git a/bdep/utility.hxx b/bdep/utility.hxx
index 4831bab..31cd0cb 100644
--- a/bdep/utility.hxx
+++ b/bdep/utility.hxx
@@ -114,6 +114,19 @@ namespace bdep
void
run_bpkg (const common_options&, A&&... args);
+ // Run the b process.
+ //
+ const char*
+ name_b (const common_options&);
+
+ template <typename O, typename E, typename... A>
+ process
+ start_b (const common_options&, O&& out, E&& err, A&&... args);
+
+ template <typename... A>
+ void
+ run_b (const common_options&, A&&... args);
+
// Manifest parsing and serialization.
//
// For parsing, if path is '-', then read from stdin.
diff --git a/bdep/utility.txx b/bdep/utility.txx
index 2d6de1d..aa75952 100644
--- a/bdep/utility.txx
+++ b/bdep/utility.txx
@@ -144,6 +144,81 @@ namespace bdep
}
}
+ // *_b()
+ //
+ template <typename O, typename E, typename... A>
+ process
+ start_b (const common_options& co,
+ O&& out,
+ E&& err,
+ A&&... args)
+ {
+ const char* b (name_b (co));
+
+ try
+ {
+ process_path pp (process::path_search (b, exec_dir));
+
+ small_vector<const char*, 1> ops;
+
+ // Map verbosity level. If we are running quiet or at level 1, then run
+ // b quiet. Otherwise, run it at the same level as us.
+ //
+ bool quiet (true); // Maybe will become an argument one day.
+
+ string vl;
+ switch (verb)
+ {
+ case 0: ops.push_back ("-q"); break;
+ case 1: if (quiet) ops.push_back ("-q"); break;
+ case 2: ops.push_back ("-v"); break;
+ default:
+ {
+ vl = to_string (verb);
+ ops.push_back ("--verbose");
+ ops.push_back (vl.c_str ());
+ }
+ }
+
+ return process_start_callback (
+ [] (const char* const args[], size_t n)
+ {
+ if (verb >= 2)
+ print_process (args, n);
+ },
+ 0 /* stdin */,
+ forward<O> (out),
+ forward<E> (err),
+ pp,
+ ops,
+ co.build_option (),
+ forward<A> (args)...);
+ }
+ catch (const process_error& e)
+ {
+ fail << "unable to execute " << b << ": " << e << endf;
+ }
+ }
+
+ template <typename... A>
+ void
+ run_b (const common_options& co, A&&... args)
+ {
+ process pr (start_b (co,
+ 1 /* stdout */,
+ 2 /* stderr */,
+ forward<A> (args)...));
+ if (!pr.wait ())
+ {
+ const process_exit& e (*pr.exit);
+
+ if (e.normal ())
+ throw failed (); // Assume the child issued diagnostics.
+
+ fail << "process " << name_b (co) << " " << e;
+ }
+ }
+
// *_manifest()
//
template <typename T>
diff --git a/doc/cli.sh b/doc/cli.sh
index fb19aa2..e51aacf 100755
--- a/doc/cli.sh
+++ b/doc/cli.sh
@@ -39,6 +39,7 @@ function compile ()
cli -I .. -v project="bdep" -v version="$version" -v date="$date" \
--include-base-last "${o[@]}" --generate-html --html-prologue-file \
man-prologue.xhtml --html-epilogue-file man-epilogue.xhtml --html-suffix .xhtml \
+--link-regex '%b([-.].+)%../../build2/doc/b$1%' \
--link-regex '%bpkg([-.].+)%../../bpkg/doc/bpkg$1%' \
--link-regex '%bpkg(#.+)?%../../bpkg/doc/build2-package-manager-manual.xhtml$1%' \
--link-regex '%bdep(#.+)?%build2-project-manager-manual.xhtml$1%' \