aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2018-04-28 16:41:02 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2018-04-28 16:41:02 +0200
commitb2d5f82512d0118a0668ce02f1a0730c3dcd50b8 (patch)
treeb470b17e276a67164212065f164823aa30b5165f
parent77a9dc17b487123dc1aaf5c41b539d9abfe32dee (diff)
Implement auto-synchronization via build system hook
-rw-r--r--bdep/bdep.cxx3
-rw-r--r--bdep/config.cli22
-rw-r--r--bdep/config.cxx73
-rw-r--r--bdep/config.hxx18
-rw-r--r--bdep/init.cxx24
-rw-r--r--bdep/init.hxx5
-rw-r--r--bdep/new.cxx13
-rw-r--r--bdep/project.cli11
-rw-r--r--bdep/project.hxx1
-rw-r--r--bdep/project.xml1
-rw-r--r--bdep/sync.cli23
-rw-r--r--bdep/sync.cxx178
-rw-r--r--bdep/sync.hxx3
-rw-r--r--bdep/utility.cxx1
-rw-r--r--bdep/utility.hxx8
15 files changed, 289 insertions, 95 deletions
diff --git a/bdep/bdep.cxx b/bdep/bdep.cxx
index 2541325..82a4259 100644
--- a/bdep/bdep.cxx
+++ b/bdep/bdep.cxx
@@ -121,7 +121,8 @@ try
{
using namespace cli;
- exec_dir = path (argv[0]).directory ();
+ argv0 = argv[0];
+ exec_dir = path (argv0).directory ();
// This is a little hack to make our baseutils for Windows work when called
// with absolute path. In a nutshell, MSYS2's exec*p() doesn't search in the
diff --git a/bdep/config.cli b/bdep/config.cli
index c9b2ce8..2c9e19f 100644
--- a/bdep/config.cli
+++ b/bdep/config.cli
@@ -39,7 +39,8 @@ namespace bdep
\b{bdep config rename} [<options>] [<prj-spec>] <cfg-spec> <cfg-name>\n
\b{bdep config set} \ \ \ [<options>] [<prj-spec>] <cfg-spec>\n
\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ [\b{--}[\b{no-}]\b{default}]\n
- \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ [\b{--}[\b{no-}]\b{forward}]}
+ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ [\b{--}[\b{no-}]\b{forward}]\n
+ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ [\b{--}[\b{no-}]\b{auto-sync}]}
\c{<cfg-spec> = \b{@}<cfg-name> | \b{--config}|\b{-c} <cfg-dir>\n
<prj-spec> = \b{--directory}|\b{-d} <prj-dir>\n
@@ -89,7 +90,14 @@ namespace bdep
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.|
+ configuration use the \cb{--forward} option.
+
+ Unless the \cb{--no-auto-sync} option is specified, an added or
+ created build configuration will be automatically synchronized on
+ every build system invocation. Note that this flag affects the entire
+ build configuration and if multiple projects share the same
+ configuration, then they must have a consistent auto-synchronization
+ setting.|
\li|\cb{remove}
@@ -106,10 +114,12 @@ namespace bdep
\li|\cb{set}
The \cb{set} subcommand sets various properties of the specified
- 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.||"
+ build configuration. These include the
+ default (\c{\b{--}[\b{no-}]\b{default}}),
+ forward (\c{\b{--}[\b{no-}]\b{forward}}), and
+ auto-synchronization (\c{\b{--}[\b{no-}]\b{auto-sync}}) flags.
+ Note that changing any of these flags requires an explicit
+ \cb{sync} command to take effect.||"
bool add;
bool create;
diff --git a/bdep/config.cxx b/bdep/config.cxx
index 0dcea48..aeb1a5f 100644
--- a/bdep/config.cxx
+++ b/bdep/config.cxx
@@ -13,14 +13,13 @@ using namespace std;
namespace bdep
{
shared_ptr<configuration>
- cmd_config_add (const dir_path& prj,
- database& db,
- dir_path path,
- optional<string> name,
- optional<bool> def,
- optional<bool> fwd,
- optional<uint64_t> id,
- const char* what)
+ cmd_config_add (const configuration_add_options& ao,
+ const dir_path& prj,
+ database& db,
+ dir_path path,
+ optional<string> name,
+ optional<uint64_t> id,
+ const char* what)
{
if (!exists (path))
fail << "configuration directory " << path << " does not exist";
@@ -29,11 +28,15 @@ namespace bdep
using count = configuration_count;
+ optional<bool> def, fwd;
{
using query = bdep::query<configuration>;
// By default the first added configuration is the default.
//
+ if (ao.default_ () || ao.no_default ())
+ def = ao.default_ () && !ao.no_default ();
+
if (!def)
def = (db.query_value<count> () == 0);
else if (*def)
@@ -46,6 +49,9 @@ namespace bdep
// By default the default configuration is forwarded unless another
// is already forwarded.
//
+ if (ao.forward () || ao.no_forward ())
+ fwd = ao.forward () && !ao.no_forward ();
+
if (!fwd)
fwd = *def && db.query_one<configuration> (query::forward) == nullptr;
else if (*fwd)
@@ -73,6 +79,7 @@ namespace bdep
move (rel_path),
*def,
*fwd,
+ !ao.no_auto_sync (),
{} /* packages */});
try
@@ -107,12 +114,13 @@ namespace bdep
if (verb)
{
diag_record dr (text);
- /* */ dr << what << " configuration ";
- if (r->name) dr << '@' << *r->name << ' ';
- /* */ dr << r->path << " (" << *r->id;
- if (r->default_) dr << ", default";
- if (r->forward) dr << ", forwarded";
- /* */ dr << ')';
+ /* */ dr << what << " configuration ";
+ if (r->name) dr << '@' << *r->name << ' ';
+ /* */ dr << r->path << " (" << *r->id;
+ if (r->default_) dr << ", default";
+ if (r->forward) dr << ", forwarded";
+ if (r->auto_sync) dr << ", auto-synchronized";
+ /* */ dr << ')';
}
return r;
@@ -125,15 +133,14 @@ namespace bdep
}
shared_ptr<configuration>
- cmd_config_create (const common_options& co,
- const dir_path& prj,
- database& db,
- dir_path path,
- cli::scanner& cfg_args,
- optional<string> name,
- optional<bool> def,
- optional<bool> fwd,
- optional<uint64_t> id)
+ cmd_config_create (const common_options& co,
+ const configuration_add_options& ao,
+ const dir_path& prj,
+ database& db,
+ dir_path path,
+ cli::scanner& cfg_args,
+ optional<string> name,
+ optional<uint64_t> id)
{
// Call bpkg to create the configuration.
//
@@ -145,12 +152,11 @@ namespace bdep
run_bpkg (co, "create", "-d", path, args);
}
- return cmd_config_add (prj,
+ return cmd_config_add (ao,
+ prj,
db,
move (path),
move (name),
- def,
- fwd,
id,
"created");
}
@@ -192,10 +198,17 @@ namespace bdep
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);
+ // --[no-]auto-sync
+ //
+ if (o.auto_sync () && o.no_auto_sync ())
+ fail << "both --auto-sync and --no-auto-sync specified";
+
+ return (o.default_ () ? "--default" :
+ o.no_default () ? "--no-default" :
+ o.forward () ? "--forward" :
+ o.no_forward () ? "--no-forward" :
+ o.auto_sync () ? "--auto-sync" :
+ o.no_auto_sync () ? "--no-auto-sync" : nullptr);
}
int
diff --git a/bdep/config.hxx b/bdep/config.hxx
index b521bac..6bc732e 100644
--- a/bdep/config.hxx
+++ b/bdep/config.hxx
@@ -14,25 +14,23 @@
namespace bdep
{
shared_ptr<configuration>
- cmd_config_add (const dir_path& prj,
+ cmd_config_add (const configuration_add_options&,
+ const dir_path& prj,
database&,
dir_path path,
optional<string> name,
- optional<bool> default_ = nullopt,
- optional<bool> forward = nullopt,
optional<uint64_t> id = nullopt,
const char* what = "added");
shared_ptr<configuration>
cmd_config_create (const common_options&,
- const dir_path& prj,
+ const configuration_add_options&,
+ const dir_path& prj,
database&,
- dir_path path,
- cli::scanner& args,
- optional<string> name,
- optional<bool> default_ = nullopt,
- optional<bool> forward = nullopt,
- optional<uint64_t> id = nullopt);
+ dir_path path,
+ cli::scanner& args,
+ optional<string> name,
+ optional<uint64_t> id = nullopt);
int
cmd_config (const cmd_config_options&, cli::scanner& args);
diff --git a/bdep/init.cxx b/bdep/init.cxx
index 1875c42..303c475 100644
--- a/bdep/init.cxx
+++ b/bdep/init.cxx
@@ -18,14 +18,13 @@ namespace bdep
{
shared_ptr<configuration>
cmd_init_config (const configuration_name_options& o,
+ const configuration_add_options& ao,
const dir_path& prj,
database& db,
const dir_path& cfg,
cli::scanner& args,
bool ca,
- bool cc,
- optional<bool> cd,
- optional<bool> cf)
+ bool cc)
{
const char* m (!ca ? "--config-create" :
!cc ? "--config-add" : nullptr);
@@ -52,8 +51,8 @@ namespace bdep
}
return ca
- ? 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));
+ ? cmd_config_add ( ao, prj, db, cfg, move (nm), move (id))
+ : cmd_config_create (o, ao, prj, db, cfg, args, move (nm), move (id));
}
void
@@ -117,7 +116,7 @@ namespace bdep
db.update (c);
t.commit ();
- cmd_sync (o, prj, c);
+ cmd_sync (o, prj, c, false /* implicit */);
}
}
@@ -181,25 +180,16 @@ 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,
+ o,
prj,
db,
ca ? o.config_add () : o.config_create (),
args,
ca,
- cc,
- cd,
- cf));
+ cc));
// Fall through.
}
diff --git a/bdep/init.hxx b/bdep/init.hxx
index 6e52737..1ac46c1 100644
--- a/bdep/init.hxx
+++ b/bdep/init.hxx
@@ -17,14 +17,13 @@ namespace bdep
//
shared_ptr<configuration>
cmd_init_config (const configuration_name_options&,
+ const configuration_add_options&,
const dir_path& prj,
database&,
const dir_path& cfg,
cli::scanner& cfg_args,
bool config_add_specified,
- bool config_create_specified,
- optional<bool> config_default,
- optional<bool> config_forward);
+ bool config_create_specified);
// Initialize each package in each configuration skipping those that are
// already initialized. Then synchronize each configuration.
diff --git a/bdep/new.cxx b/bdep/new.cxx
index a6074b8..85acad4 100644
--- a/bdep/new.cxx
+++ b/bdep/new.cxx
@@ -617,25 +617,16 @@ 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,
+ o,
prj,
db,
ca ? o.config_add () : o.config_create (),
args,
ca,
- cc,
- cd,
- cf)};
+ cc)};
package_locations pkgs {{n, dir_path ()}}; // project == package
diff --git a/bdep/project.cli b/bdep/project.cli
index 137c77d..2fe37e4 100644
--- a/bdep/project.cli
+++ b/bdep/project.cli
@@ -31,6 +31,17 @@ namespace bdep
{
"Don't make the added or created configuration forwarded."
}
+
+ bool --auto-sync
+ {
+ "Make the added or created configuration automatically synchronized."
+ }
+
+ bool --no-auto-sync
+ {
+ "Don't make the added or created configuration automatically
+ synchronized."
+ }
};
// Common options for commands that accept --config-id and @<cfg-name>.
diff --git a/bdep/project.hxx b/bdep/project.hxx
index 2e8d366..bbfd41d 100644
--- a/bdep/project.hxx
+++ b/bdep/project.hxx
@@ -79,6 +79,7 @@ namespace bdep
bool default_;
bool forward;
+ bool auto_sync;
// 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-
diff --git a/bdep/project.xml b/bdep/project.xml
index 8d1bf26..b9da262 100644
--- a/bdep/project.xml
+++ b/bdep/project.xml
@@ -7,6 +7,7 @@
<column name="relative_path" type="TEXT" null="true"/>
<column name="default" type="INTEGER" null="true"/>
<column name="forward" type="INTEGER" null="true"/>
+ <column name="auto_sync" type="INTEGER" null="true"/>
<primary-key auto="true">
<column name="id"/>
</primary-key>
diff --git a/bdep/sync.cli b/bdep/sync.cli
index 4fd5cb8..61d15cb 100644
--- a/bdep/sync.cli
+++ b/bdep/sync.cli
@@ -198,5 +198,28 @@ namespace bdep
{
"Don't prompt for confirmation when up/down-grading dependencies."
}
+
+ bool --implicit
+ {
+ "Perform implicit synchronization. This mode is normally used by other
+ tools (for example, a build system hook) to ensure packages and
+ configurations are synchronized. To improve performance, especially for
+ the \"everything is already synchronized\" case, \cb{sync} executed in
+ this mode assumes that no configuration flags (see \l{bdep-config(1)})
+ have changed since the last explicit synchronization.
+
+ To avoid recursive re-synchronization, the \cb{sync} command maintains
+ the \cb{BDEP_SYNCED_CONFIGS} environment variable. It contains a
+ space-separated, \cb{\"}-quoted list of configuration paths that have
+ been or are being synchronized by the current \cb{bdep} invocation
+ chain. The \cb{sync} command in the implicit mode also examines this
+ variable and silently ignores synchronization requests that have been
+ or are already being performed."
+ }
+
+ // The build system hook protocol version. Internal, undocumented, and
+ // implies --implicit.
+ //
+ uint16_t --hook = 0;
};
}
diff --git a/bdep/sync.cxx b/bdep/sync.cxx
index 6c29a50..b95aa54 100644
--- a/bdep/sync.cxx
+++ b/bdep/sync.cxx
@@ -4,6 +4,8 @@
#include <bdep/sync.hxx>
+#include <stdlib.h> // getenv() setenv()/_putenv()
+
#include <bdep/database.hxx>
#include <bdep/diagnostics.hxx>
@@ -22,6 +24,7 @@ namespace bdep
cmd_sync (const common_options& co,
const dir_path& prj,
const shared_ptr<configuration>& c,
+ bool implicit,
bool fetch,
bool yes,
optional<bool> upgrade, // true - upgrade, false - patch
@@ -44,6 +47,10 @@ namespace bdep
// We also use the repository name rather than the location as a sanity
// check (the repository must have been added as part of init).
//
+ // @@ For implicit fetch this will print diagnostics/progress before
+ // "synchronizing <cfg-dir>:". Maybe rep-fetch also needs something
+ // like --plan but for progress? Plus there might be no sync at all.
+ //
if (fetch)
run_bpkg (co,
"fetch",
@@ -110,36 +117,42 @@ namespace bdep
}
}
+ // For implicit sync (normally performed on one configuration at a time)
+ // add the configuration directory to the plan header.
+ //
+ // We use the configuration directory rather than the name because this
+ // could be a multi-project configuration and in the implicit mode there
+ // will normally be no "originating project" (unlike with explicit sync).
+ //
+ string plan (implicit
+ ? "synchronizing " + c->path.representation () + ':'
+ : "synchronizing:");
+
run_bpkg (co,
"build",
"-d", c->path,
"--no-fetch",
"--configure-only",
"--keep-out",
- "--plan", "synchronizing:",
+ "--plan", plan,
(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).
+ // the package was upgraded.
+ //
+ // @@ TODO: changed flag below: Probably by comparing before/after
+ // versions. Or, even simpler, if pkg-build signalled that
+ // nothing has changed (the --exit-result idea) -- then we
+ // can skip the whole thing.
//
// 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)
@@ -177,7 +190,7 @@ namespace bdep
if (changed || !e)
o = "configure:";
}
- else
+ else if (!implicit) // Requires explicit sync.
{
//@@ This is broken: we will disfigure forwards to other configs.
// Looks like we will need to test that the forward is to this
@@ -195,18 +208,71 @@ namespace bdep
run_b (co, o, arg);
}
}
+
+ // Add/remove auto-synchronization build system hook.
+ //
+ if (!implicit)
+ {
+ path f (c->path / "build" / "bootstrap" / "pre-bdep-sync.build");
+ bool e (exists (f));
+
+ if (c->auto_sync)
+ {
+ if (!e)
+ {
+ mk (f.directory ());
+
+ try
+ {
+ ofdstream os (f);
+
+ // Should we analyze BDEP_SYNCED_CONFIGS ourselves or should we
+ // let bdep-sync to it for us? Doing it here instead of spawning a
+ // process (which will loading the database, etc) will be faster.
+ // But, on the other hand, this is only an issue for commands like
+ // update and test that do their own implicit sync.
+ //
+ // cfgs = $getenv(BDEP_SYNCED_CONFIGS)
+ // if! $null($cfgs)
+ // cfgs = [dir_paths] $regex.split($cfgs, ' *"([^"]*)" *', '\1')
+ //
+ os << "# Created automatically by bdep." << endl
+ << "#" << endl
+ << "if ($build.meta_operation != 'info' && \\" << endl
+ << " $build.meta_operation != 'configure' && \\" << endl
+ << " $build.meta_operation != 'disfigure')" << endl
+ << " run " << argv0 << " sync --hook=1 " <<
+ "--config \"$out_root\" " <<
+ "-d '" << prj.string () << "'" << endl;
+
+ os.close ();
+ }
+ catch (const io_error& e)
+ {
+ fail << "unable to write " << f << ": " << e;
+ }
+ }
+ }
+ else
+ {
+ if (e)
+ rm (f);
+ }
+ }
}
void
cmd_sync (const common_options& co,
const dir_path& prj,
const shared_ptr<configuration>& c,
+ bool implicit,
bool fetch,
bool yes)
{
cmd_sync (co,
prj,
c,
+ implicit,
fetch,
yes,
nullopt /* upgrade */,
@@ -216,7 +282,7 @@ namespace bdep
}
int
- cmd_sync (const cmd_sync_options& o, cli::scanner& args)
+ cmd_sync (cmd_sync_options&& o, cli::scanner& args)
{
tracer trace ("sync");
@@ -242,6 +308,29 @@ namespace bdep
strings dep_pkgs;
for (; args.more (); dep_pkgs.push_back (args.next ())) ;
+ // --hook
+ //
+ if (o.hook_specified ())
+ {
+ if (o.hook () != 1)
+ fail << "unsupported build system hook protocol" <<
+ info << "project requires re-initialization";
+
+ o.implicit (true); // Implies --implicit.
+ }
+
+ // --implicit
+ //
+ if (o.implicit ())
+ {
+ if (const char* n = (o.upgrade () ? "--upgrade|-u" :
+ o.patch () ? "--patch|-p" : nullptr))
+ fail << "--implicit specified with " << n;
+
+ if (!dep_pkgs.empty ())
+ fail << "--implicit specified with dependency package";
+ }
+
// We could be running from a package directory (or the user specified one
// with -d) that has not been init'ed in this configuration. Unless we are
// upgrading specific dependencies, we want to diagnose that since such a
@@ -306,6 +395,7 @@ namespace bdep
cmd_sync (o,
prj,
c,
+ false /* implicit */,
!fetch,
o.recursive () || o.immediate () ? o.yes () : true,
!o.patch (), // Upgrade by default unless patch requested.
@@ -322,6 +412,7 @@ namespace bdep
cmd_sync (o,
prj,
c,
+ false /* implicit */,
!fetch,
o.yes (),
o.upgrade (),
@@ -331,9 +422,64 @@ namespace bdep
}
else
{
- // The first form: sync of project packages.
+ // The first form: sync of project packages (potentially implicit).
+ //
+
+ // Update the BDEP_SYNCED_CONFIGS environment variable.
//
- cmd_sync (o, prj, c, !fetch, true /* yes */);
+ // Note that it covers both depth and breadth (i.e., we don't restore
+ // the previous value before returning). The idea here is for commands
+ // like update or test would perform an implicit sync which will then
+ // be "noticed" by the build system hook. This should be both faster
+ // (no need to spawn multiple bdep processes) and simpler (no need to
+ // worry about who has the database open, etc).
+ //
+ {
+ const char n[] = "BDEP_SYNCED_CONFIGS";
+
+ string v;
+ const string& p (c->path.string ());
+
+ if (const char* e = getenv (n))
+ {
+ v = e;
+
+ // Check if this configuration is already (being) synchronized.
+ //
+ for (size_t b (0), e (0);
+ (e = v.find ('"', e)) != string::npos; // Skip leading ' '.
+ ++e) // Skip trailing '"'.
+ {
+ size_t n (next_word (v, b, e, '"'));
+
+ // Both paths are normilized so we can just compare them as
+ // strings.
+ //
+ if (path::traits::compare (v.c_str () + b, n,
+ p.c_str (), p.size ()) == 0)
+ {
+ if (o.implicit ())
+ return 0; // Ignore.
+ else
+ fail << "explicit re-synchronization of " << c->path;
+ }
+ }
+
+ v += ' ';
+ }
+
+ v += '"';
+ v += p;
+ v += '"';
+
+#ifndef _WIN32
+ setenv (n, v.c_str (), 1 /* overwrite */);
+#else
+ _putenv ((string (n) + '=' + v).c_str ());
+#endif
+ }
+
+ cmd_sync (o, prj, c, o.implicit (), !fetch, true /* yes */);
}
}
diff --git a/bdep/sync.hxx b/bdep/sync.hxx
index b397c10..8edd4b4 100644
--- a/bdep/sync.hxx
+++ b/bdep/sync.hxx
@@ -20,11 +20,12 @@ namespace bdep
cmd_sync (const common_options&,
const dir_path& prj,
const shared_ptr<configuration>&,
+ bool implicit,
bool fetch = true,
bool yes = true);
int
- cmd_sync (const cmd_sync_options&, cli::scanner& args);
+ cmd_sync (cmd_sync_options&&, cli::scanner& args);
}
#endif // BDEP_SYNC_HXX
diff --git a/bdep/utility.cxx b/bdep/utility.cxx
index a902273..e048a5a 100644
--- a/bdep/utility.cxx
+++ b/bdep/utility.cxx
@@ -26,6 +26,7 @@ namespace bdep
const path repositories_file ("repositories.manifest");
const path configurations_file ("configurations.manifest");
+ const char* argv0;
dir_path exec_dir;
bool
diff --git a/bdep/utility.hxx b/bdep/utility.hxx
index 31cd0cb..09e0e45 100644
--- a/bdep/utility.hxx
+++ b/bdep/utility.hxx
@@ -40,6 +40,10 @@ namespace bdep
using butl::ucase;
using butl::lcase;
using butl::casecmp;
+
+ using butl::trim;
+ using butl::next_word;
+
using butl::reverse_iterate;
using butl::exception_guard;
@@ -66,6 +70,10 @@ namespace bdep
extern const path repositories_file; // repositories.manifest
extern const path configurations_file; // configurations.manifest
+ // Process path (argv[0]).
+ //
+ extern const char* argv0;
+
// Directory extracted from argv[0] (i.e., this process' recall directory)
// or empty if there is none. Can be used as a search fallback.
//