aboutsummaryrefslogtreecommitdiff
path: root/bdep
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2018-05-13 12:51:21 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2018-05-13 12:52:23 +0200
commit40632ed4dc0a51149ab034cebed6227db21c9dab (patch)
tree2f5654ff888ff7b1db8b4a374d4112b19d5f34fc /bdep
parent9721773b915adf0bf7fcfb2bf8550787588e0aa2 (diff)
Implement deinit command
Diffstat (limited to 'bdep')
-rw-r--r--bdep/bdep.cli12
-rw-r--r--bdep/bdep.cxx2
-rw-r--r--bdep/buildfile2
-rw-r--r--bdep/config.cli2
-rw-r--r--bdep/deinit.cli45
-rw-r--r--bdep/deinit.cxx210
-rw-r--r--bdep/deinit.hxx19
-rw-r--r--bdep/init.cxx18
-rw-r--r--bdep/status.cxx3
-rw-r--r--bdep/sync.cxx120
-rw-r--r--bdep/sync.hxx11
11 files changed, 385 insertions, 59 deletions
diff --git a/bdep/bdep.cli b/bdep/bdep.cli
index 0d30561..595e9d9 100644
--- a/bdep/bdep.cli
+++ b/bdep/bdep.cli
@@ -247,6 +247,13 @@ namespace bdep
<...>
\
+ To deinitialize a project in one or more build configurations we
+ can use the \l{bdep-deinit(1)} command. For example:
+
+ \
+ $ bdep deinit -a
+ \
+
|
\li|\b{Add, Remove, or Change Dependencies}\n
@@ -426,6 +433,11 @@ namespace bdep
"\l{bdep-status(1)} \- print status of project and/or its dependencies"
}
+ bool deinit
+ {
+ "\l{bdep-deinit(1)} \- deinitialize project in build configurations"
+ }
+
bool config
{
"\l{bdep-config(1)} \- manage project's build configurations"
diff --git a/bdep/bdep.cxx b/bdep/bdep.cxx
index b382776..c9d3234 100644
--- a/bdep/bdep.cxx
+++ b/bdep/bdep.cxx
@@ -27,6 +27,7 @@
#include <bdep/sync.hxx>
#include <bdep/fetch.hxx>
#include <bdep/status.hxx>
+#include <bdep/deinit.hxx>
#include <bdep/config.hxx>
#include <bdep/test.hxx>
#include <bdep/update.hxx>
@@ -264,6 +265,7 @@ try
COMMAND_IMPL (sync, sync, "sync");
COMMAND_IMPL (fetch, fetch, "fetch");
COMMAND_IMPL (status, status, "status");
+ COMMAND_IMPL (deinit, deinit, "deinit");
COMMAND_IMPL (config, config, "config");
COMMAND_IMPL (test, test, "test");
COMMAND_IMPL (update, update, "update");
diff --git a/bdep/buildfile b/bdep/buildfile
index 54ba5e4..efb747f 100644
--- a/bdep/buildfile
+++ b/bdep/buildfile
@@ -27,6 +27,7 @@ init-options \
sync-options \
fetch-options \
status-options \
+deinit-options \
config-options \
test-options \
update-options \
@@ -69,6 +70,7 @@ if $cli.configured
cli.cxx{sync-options}: cli{sync}
cli.cxx{fetch-options}: cli{fetch}
cli.cxx{status-options}: cli{status}
+ cli.cxx{deinit-options}: cli{deinit}
cli.cxx{config-options}: cli{config}
cli.cxx{test-options}: cli{test}
cli.cxx{update-options}: cli{update}
diff --git a/bdep/config.cli b/bdep/config.cli
index 1633edb..0a5dd74 100644
--- a/bdep/config.cli
+++ b/bdep/config.cli
@@ -23,6 +23,8 @@ 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?
+ // A: yes, only empty configs should be removed, if not empty, suggest
+ // to deinitialize with bdep-deinit.
//
// @@ Should set be able to alter forward flag? This will require both
// disfiguring forward on old and configuring on new. Perhaps best
diff --git a/bdep/deinit.cli b/bdep/deinit.cli
new file mode 100644
index 0000000..bfcec51
--- /dev/null
+++ b/bdep/deinit.cli
@@ -0,0 +1,45 @@
+// file : bdep/deinit.cli
+// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+include <bdep/project.cli>;
+
+"\section=1"
+"\name=bdep-deinit"
+"\summary=deinitialize project in build configurations"
+
+namespace bdep
+{
+ {
+ "<options>
+ <prj-spec> <prj-dir>
+ <pkg-spec> <pkg-dir>
+ <cfg-spec> <cfg-name> <cfg-dir>",
+
+ "\h|SYNOPSIS|
+
+ \c{\b{bdep deinit} [<options>] [<pkg-spec>] [<cfg-spec>]}
+
+ \c{<cfg-spec> = (\b{@}<cfg-name> | \b{--config}|\b{-c} <cfg-dir>)... | \b{--all}|\b{-a}\n
+ <pkg-spec> = (\b{--directory}|\b{-d} <pkg-dir>)... | <prj-spec>\n
+ <prj-spec> = \b{--directory}|\b{-d} <prj-dir>}
+
+ \h|DESCRIPTION|
+
+ The \cb{deinit} command deinitializes the specified project packages
+ (<pkg-spec>), or, if the project itself is specified (<prj-spec>), all
+ its previously initialized packages, in one or more build configurations
+ (<cfg-spec>).
+
+ If no project directory is specified, then the current working directory
+ is assumed. If no configuration is specified, then the default
+ configuration is assumed. See \l{bdep-projects-configs(1)} for details on
+ specifying projects and configurations.
+ "
+ }
+
+ class cmd_deinit_options: project_options
+ {
+ "\h|DEINIT OPTIONS|"
+ };
+}
diff --git a/bdep/deinit.cxx b/bdep/deinit.cxx
new file mode 100644
index 0000000..4a8c463
--- /dev/null
+++ b/bdep/deinit.cxx
@@ -0,0 +1,210 @@
+// file : bdep/deinit.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <bdep/deinit.hxx>
+
+#include <bdep/project.hxx>
+#include <bdep/project-odb.hxx>
+#include <bdep/database.hxx>
+#include <bdep/diagnostics.hxx>
+
+#include <bdep/sync.hxx> // configuration_projects(), hook_file
+
+using namespace std;
+
+namespace bdep
+{
+ static void
+ cmd_deinit (const cmd_deinit_options& o,
+ const dir_path& prj,
+ const shared_ptr<configuration>& c,
+ const strings& pkgs)
+ {
+ const dir_path& cfg (c->path);
+
+ // Remove auto-synchronization build system hook.
+ //
+ // We have to check if there are any other projects that share this
+ // configuration. Note that we don't load the other project's database,
+ // check if the configuration is also auto-synchronized (we assume things
+ // are consistent) or that it has initialized packages (we assume it would
+ // have been removed from the configuration's repositories if that were
+ // the case).
+ //
+ if (c->auto_sync &&
+ c->packages.empty () &&
+ configuration_projects (o, cfg, prj).empty ())
+ {
+ path f (cfg / hook_file);
+ if (exists (f))
+ rm (f);
+ }
+
+ // Disfigure configuration forwarding.
+ //
+ if (c->forward)
+ {
+ package_locations pls (load_packages (prj));
+
+ for (const string& n: pkgs)
+ {
+ dir_path out (dir_path (cfg) /= n);
+ dir_path src (prj);
+ {
+ auto i (find_if (pls.begin (),
+ pls.end (),
+ [&n] (const package_location& pl)
+ {
+ return pl.name == n;
+ }));
+
+ if (i == pls.end ())
+ fail << "package " << n << " is not listed in " << prj;
+
+ src /= i->path;
+ }
+
+ run_b (o,
+ "disfigure:",
+ src.representation () + '@' + out.representation () +
+ ",forward");
+ }
+ }
+
+ // Note that --keep-dependent is important: if we drop dependent packages
+ // that are managed by bdep, then its view of what has been initialized
+ // in the configuration will become invalid.
+ //
+ run_bpkg (2,
+ o,
+ "drop",
+ "-d", cfg,
+ "--keep-dependent",
+ "--plan", "synchronizing:",
+ "--yes",
+ pkgs);
+ }
+
+ int
+ cmd_deinit (const cmd_deinit_options& o, cli::scanner&)
+ {
+ tracer trace ("deinit");
+
+ // The same ignore/load story as in sync.
+ //
+ project_packages pp (
+ find_project_packages (o,
+ false /* ignore_packages */,
+ false /* load_packages */));
+
+ const dir_path& prj (pp.project);
+
+ if (verb)
+ text << "deinitializing in project " << prj;
+
+ database db (open (prj, trace));
+
+ transaction t (db.begin ());
+ configurations cfgs (find_configurations (prj, t, o));
+ t.commit ();
+
+ // If specified, verify packages are present in each configuration.
+ //
+ if (!pp.packages.empty ())
+ verify_project_packages (pp, cfgs);
+
+ // If no packages were explicitly specified, then we deinitalize all that
+ // have been initialized in each configuration.
+ //
+ strings pkgs;
+
+ bool all (pp.packages.empty ());
+ if (!all)
+ {
+ for (const package_location& p: pp.packages)
+ pkgs.push_back (p.name);
+ }
+
+ // Deinitialize in each configuration skipping empty ones.
+ //
+ // We do each configuration in a separate transaction so that our state
+ // reflects the bpkg configuration as closely as possible.
+ //
+ bool first (true);
+ for (const shared_ptr<configuration>& c: cfgs)
+ {
+ if (c->packages.empty ())
+ {
+ if (verb)
+ info << "skipping empty configuration " << *c;
+
+ continue;
+ }
+
+ // If we are printing multiple configurations, separate them with a
+ // blank line and print the configuration name/directory.
+ //
+ if (verb && cfgs.size () > 1)
+ {
+ text << (first ? "" : "\n")
+ << "in configuration " << *c << ':';
+
+ first = false;
+ }
+
+ transaction t (db.begin ());
+
+ // Collect packages to drop and remove them from the configuration.
+ //
+ if (all)
+ {
+ pkgs.clear ();
+
+ for (const package_state& p: c->packages)
+ pkgs.push_back (p.name);
+ }
+
+ c->packages.erase (
+ remove_if (c->packages.begin (),
+ c->packages.end (),
+ [&pkgs] (const package_state& p)
+ {
+ return find_if (pkgs.begin (),
+ pkgs.end (),
+ [&p] (const string& n)
+ {
+ return p.name == n;
+ }) != pkgs.end ();
+ }),
+ c->packages.end ());
+
+ // If we are deinitializing multiple packages, print their names.
+ //
+ if (verb && pkgs.size () > 1)
+ {
+ for (const string& n: pkgs)
+ text << "deinitializing package " << n;
+ }
+
+ // The same story as in init with regard to the state update order.
+ //
+ cmd_deinit (o, prj, c, pkgs);
+
+ db.update (c);
+ t.commit ();
+
+ // Remove our repository from the configuration if we have no more
+ // packages that are initialized in it.
+ //
+ if (c->packages.empty ())
+ run_bpkg (3,
+ o,
+ "remove",
+ "-d", c->path,
+ "dir:" + prj.string ());
+ }
+
+ return 0;
+ }
+}
diff --git a/bdep/deinit.hxx b/bdep/deinit.hxx
new file mode 100644
index 0000000..9a2099d
--- /dev/null
+++ b/bdep/deinit.hxx
@@ -0,0 +1,19 @@
+// file : bdep/deinit.hxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#ifndef BDEP_DEINIT_HXX
+#define BDEP_DEINIT_HXX
+
+#include <bdep/types.hxx>
+#include <bdep/utility.hxx>
+
+#include <bdep/deinit-options.hxx>
+
+namespace bdep
+{
+ int
+ cmd_deinit (const cmd_deinit_options&, cli::scanner& args);
+}
+
+#endif // BDEP_DEINIT_HXX
diff --git a/bdep/init.cxx b/bdep/init.cxx
index 1c885eb..9fe4334 100644
--- a/bdep/init.cxx
+++ b/bdep/init.cxx
@@ -80,8 +80,6 @@ namespace bdep
first = false;
}
- transaction t (db.begin ());
-
// Add project repository to the configuration. Note that we don't fetch
// it since sync is going to do it anyway.
//
@@ -92,6 +90,8 @@ namespace bdep
"--type", "dir",
prj);
+ transaction t (db.begin ());
+
for (const package_location& p: pkgs)
{
if (find_if (c->packages.begin (),
@@ -115,10 +115,18 @@ namespace bdep
c->packages.push_back (package_state {p.name});
}
+ // Should we sync then commit the database or commit and then sync?
+ // Either way we can end up with an incosistent state. Note, however,
+ // that the state in the build configuration can in most cases be
+ // corrected with a retry (e.g., "upgrade" the package to the fixed
+ // version, etc) while if we think (from the database state) that the
+ // package has already been initialized, then there will be no way to
+ // retry anything.
+ //
+ cmd_sync (o, prj, c, pkg_args, false /* implicit */);
+
db.update (c);
t.commit ();
-
- cmd_sync (o, prj, c, pkg_args, false /* implicit */);
}
}
@@ -148,7 +156,7 @@ namespace bdep
const dir_path& prj (pp.project);
if (verb)
- text << "initializing project " << prj;
+ text << "initializing in project " << prj;
// Create .bdep/.
//
diff --git a/bdep/status.cxx b/bdep/status.cxx
index 3c8dfe3..b460cd1 100644
--- a/bdep/status.cxx
+++ b/bdep/status.cxx
@@ -5,7 +5,6 @@
#include <bdep/status.hxx>
#include <bdep/project.hxx>
-#include <bdep/project-odb.hxx>
#include <bdep/database.hxx>
#include <bdep/diagnostics.hxx>
@@ -92,7 +91,7 @@ namespace bdep
strings dep_pkgs;
for (; args.more (); dep_pkgs.push_back (args.next ())) ;
- // For the project status the same story as in sync.
+ // The same ignore/load story as in sync.
//
project_packages pp (
find_project_packages (o,
diff --git a/bdep/sync.cxx b/bdep/sync.cxx
index 484a1ad..19afdce 100644
--- a/bdep/sync.cxx
+++ b/bdep/sync.cxx
@@ -18,29 +18,15 @@ using namespace std;
namespace bdep
{
- // Project to be synchronized.
- //
- struct project
- {
- dir_path path;
- shared_ptr<configuration> config;
-
- bool implicit;
- bool fetch;
- };
-
- using projects = small_vector<project, 1>;
+ const path hook_file (
+ dir_path ("build") / "bootstrap" / "pre-bdep-sync.build");
- // Append the list of additional (to origin, if not empty) projects that are
- // using this configuration.
- //
- static void
- load_implicit (const common_options& co,
- const dir_path& cfg,
- const dir_path& origin_prj,
- projects& r)
+ dir_paths
+ configuration_projects (const common_options& co,
+ const dir_path& cfg,
+ const dir_path& prj)
{
- tracer trace ("load_implicit");
+ dir_paths r;
// Use bpkg-rep-list to discover the list of project directories.
//
@@ -81,7 +67,7 @@ namespace bdep
d.normalize (); // For good measure.
- if (d == origin_prj)
+ if (d == prj)
continue;
// Next see if it looks like a bdep-managed project.
@@ -89,29 +75,7 @@ namespace bdep
if (!exists (d / bdep_file))
continue;
- shared_ptr<configuration> c;
- {
- using query = bdep::query<configuration>;
-
- database db (open (d, trace));
-
- transaction t (db.begin ());
- c = db.query_one<configuration> (query::path == cfg.string ());
- t.commit ();
- }
-
- // If the project is a repository of this configuration but the bdep
- // database has no knowledge of this configuration, then assume it is
- // not managed by bdep (i.e., the user added the project manually or
- // some such).
- //
- if (c == nullptr)
- continue;
-
- r.push_back (project {move (d),
- move (c),
- true /* implicit */,
- true /* fetch */});
+ r.push_back (move (d));
}
is.close (); // Detect errors.
@@ -125,6 +89,60 @@ namespace bdep
}
finish_bpkg (co, pr, io);
+
+ return r;
+ }
+
+ // Project to be synchronized.
+ //
+ struct project
+ {
+ dir_path path;
+ shared_ptr<configuration> config;
+
+ bool implicit;
+ bool fetch;
+ };
+
+ using projects = small_vector<project, 1>;
+
+ // Append the list of additional (to origin, if not empty) projects that are
+ // using this configuration.
+ //
+ static void
+ load_implicit (const common_options& co,
+ const dir_path& cfg,
+ const dir_path& origin_prj,
+ projects& r)
+ {
+ tracer trace ("load_implicit");
+
+ for (dir_path& d: configuration_projects (co, cfg, origin_prj))
+ {
+ shared_ptr<configuration> c;
+ {
+ using query = bdep::query<configuration>;
+
+ database db (open (d, trace));
+
+ transaction t (db.begin ());
+ c = db.query_one<configuration> (query::path == cfg.string ());
+ t.commit ();
+ }
+
+ // If the project is a repository of this configuration but the bdep
+ // database has no knowledge of this configuration, then assume it is
+ // not managed by bdep (i.e., the user added the project manually or
+ // some such).
+ //
+ if (c == nullptr)
+ continue;
+
+ r.push_back (project {move (d),
+ move (c),
+ true /* implicit */,
+ true /* fetch */});
+ }
}
// Sync with optional upgrade.
@@ -252,9 +270,6 @@ namespace bdep
// "synchronizing <cfg-dir>:". Maybe rep-fetch also needs something
// like --plan but for progress? Plus there might be no sync at all.
//
- // @@ TODO: remember to rep-remove in deinit if there are no more
- // init'ed packages in this configuration.
- //
if (!reps.empty ())
run_bpkg (3, co, "fetch", "-d", cfg, "--shallow", reps);
@@ -353,9 +368,10 @@ namespace bdep
if (o != nullptr)
{
dir_path out (dir_path (cfg) /= pkg.name);
- string arg (src.representation () + '@' + out.representation () +
- ",forward");
- run_b (co, o, arg);
+ run_b (co,
+ o,
+ src.representation () + '@' + out.representation () +
+ ",forward");
}
}
}
@@ -364,7 +380,7 @@ namespace bdep
//
if (origin_config != nullptr && !implicit)
{
- path f (cfg / "build" / "bootstrap" / "pre-bdep-sync.build");
+ path f (cfg / hook_file);
bool e (exists (f));
if (origin_config->auto_sync)
diff --git a/bdep/sync.hxx b/bdep/sync.hxx
index 1aebfe3..6ca3937 100644
--- a/bdep/sync.hxx
+++ b/bdep/sync.hxx
@@ -33,6 +33,17 @@ namespace bdep
int
cmd_sync (cmd_sync_options&&, cli::group_scanner& args);
+
+
+ // Return the list of additional (to prj, if not empty) projects that are
+ // using this configuration.
+ //
+ dir_paths
+ configuration_projects (const common_options& co,
+ const dir_path& cfg,
+ const dir_path& prj = dir_path ());
+
+ extern const path hook_file; // build/bootstrap/pre-bdep-sync.build
}
#endif // BDEP_SYNC_HXX