aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2021-09-04 15:41:41 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2021-09-06 11:53:45 +0300
commit03c40ed68ce10b26a5f9f509e914b1b54f060215 (patch)
tree42e79327baa90f4cfdee1ba04dba98250293fcfd
parent22165c649ca2c5ef216ae3f99fbfb2dc0fff99ab (diff)
Add --backlink, --dangling, and --recursive options to cfg-info
-rw-r--r--bpkg/cfg-info.cli33
-rw-r--r--bpkg/cfg-info.cxx101
-rw-r--r--bpkg/cfg-link.cli2
-rw-r--r--bpkg/cfg-link.cxx2
-rw-r--r--bpkg/cfg-link.hxx2
-rw-r--r--bpkg/cfg-unlink.cli4
-rw-r--r--bpkg/cfg-unlink.cxx12
-rw-r--r--bpkg/database.cxx33
-rw-r--r--bpkg/database.hxx7
-rw-r--r--bpkg/package.hxx2
-rw-r--r--tests/cfg-info.testscript176
-rw-r--r--tests/cfg-link.testscript36
-rw-r--r--tests/cfg-unlink.testscript6
13 files changed, 356 insertions, 60 deletions
diff --git a/bpkg/cfg-info.cli b/bpkg/cfg-info.cli
index 8002abd..8801ec9 100644
--- a/bpkg/cfg-info.cli
+++ b/bpkg/cfg-info.cli
@@ -19,14 +19,22 @@ namespace bpkg
\h|DESCRIPTION|
The \cb{cfg-info} command prints the current configuration's absolute
- path, id, type, and name. If the \cb{--link} option is specified then
- this information is also printed for each linked configuration, if any.
+ path, id, type, and name. If the \cb{--link} and/or \cb{--backlink}
+ options are specified, then this information is also printed for each
+ linked and/or implicitly backlinked configuration, if any. Note that the
+ dangling implicit backlinks are silently skipped, unless \cb{--dangling}
+ is specified, in which case this information is also printed for them.
Note that the information is written to \cb{stdout}, not \cb{stderr}.
+ If the \cb{--recursive} option is specified together with \cb{--link}
+ and/or \cb{--backlink}, then this information is printed for linked
+ and/or implicitly backlinked configuration, recursively.
+
The output format is regular with each value printed on a separate line
- and prefixed with the value name. If the \cb{--link} option is specified
- then information blocks corresponding to configurations are separated
- with blank lines. For example:
+ and prefixed with the value name. If the \cb{--link}, \cb{--backlink},
+ and/or \cb{--dangling} options are specified, then information blocks
+ corresponding to linked configurations are separated with blank
+ lines. For example:
\
path: /path/to/cfg/
@@ -55,6 +63,21 @@ namespace bpkg
{
"Print linked configurations."
}
+
+ bool --backlink
+ {
+ "Print implicitly backlinked configurations."
+ }
+
+ bool --dangling
+ {
+ "Print dangling implicit backlinks."
+ }
+
+ bool --recursive
+ {
+ "Print linked configurations recursively."
+ }
};
"
diff --git a/bpkg/cfg-info.cxx b/bpkg/cfg-info.cxx
index da49d62..fc65b7b 100644
--- a/bpkg/cfg-info.cxx
+++ b/bpkg/cfg-info.cxx
@@ -3,8 +3,11 @@
#include <bpkg/cfg-info.hxx>
+#include <set>
#include <iostream> // cout
+#include <bpkg/package.hxx>
+#include <bpkg/package-odb.hxx>
#include <bpkg/database.hxx>
#include <bpkg/diagnostics.hxx>
@@ -20,35 +23,101 @@ namespace bpkg
dir_path c (o.directory ());
l4 ([&]{trace << "configuration: " << c;});
- database db (c, trace, o.link ());
+ if (o.recursive () && !o.link () && !o.backlink ())
+ fail << "--recursive requires --link or --backlink";
try
{
cout.exceptions (ostream::badbit | ostream::failbit);
- auto print = [] (const database& db)
+ // Return false if the configuration information has already been
+ // printed and print the information and return true otherwise.
+ //
+ auto print = [first = true,
+ printed = set<dir_path> {}]
+ (const dir_path& path,
+ const uuid& uid,
+ const string& type,
+ const optional<string>& name) mutable
{
- cout << "path: " << db.config << endl
- << "uuid: " << db.uuid << endl
- << "type: " << db.type << endl
- << "name: " << (db.name ? *db.name : "") << endl;
+ if (!printed.insert (path).second)
+ return false;
+
+ if (!first)
+ cout << endl;
+ else
+ first = false;
+
+ cout << "path: " << path << endl
+ << "uuid: " << uid << endl
+ << "type: " << type << endl
+ << "name: " << (name ? *name : "") << endl;
+
+ return true;
};
- print (db);
+ using query = odb::query<configuration>;
- // Note that there will be no explicit links loaded, unless the --link
- // option is specified.
+ query q (false);
+
+ if (o.link ())
+ q = q || query::expl;
+
+ if (o.backlink () || o.dangling ())
+ q = q || (!query::expl && query::id != 0);
+
+ // Make the output consistent across runs.
//
- for (const linked_config& lc: db.explicit_links ())
+ q = q + "ORDER BY" + query::id;
+
+ auto print_db = [&o, &q, &print] (database& db,
+ bool links,
+ const auto& print_db)
{
- // Skip the self-link.
- //
- if (lc.id != 0)
+ if (!print (db.config, db.uuid, db.type, db.name))
+ return;
+
+ if (links)
{
- cout << endl;
- print (lc.db);
+ for (auto& c: db.query<configuration> (q))
+ {
+ const dir_path& d (c.make_effective_path (db.config));
+
+ auto print_link = [&o, &db, &c, &print_db] ()
+ {
+ database& ldb (db.attach (c.path));
+ db.verify_link (c, ldb);
+
+ // While at it, also verify the backlink.
+ //
+ if (c.expl)
+ db.backlink (ldb);
+
+ print_db (ldb, o.recursive (), print_db);
+ };
+
+ if (c.expl)
+ {
+ if (o.link ())
+ print_link ();
+ }
+ else if (exists (d))
+ {
+ if (o.backlink ())
+ print_link ();
+ }
+ else if (o.dangling ())
+ print (d, c.uuid, c.type, c.name);
+ }
}
- }
+ };
+
+ database db (c, trace, false /* pre_attach */);
+ transaction t (db);
+
+ print_db (db, o.link () || o.backlink () || o.dangling (), print_db);
+
+ t.commit ();
}
catch (const io_error&)
{
diff --git a/bpkg/cfg-link.cli b/bpkg/cfg-link.cli
index 562d4c1..906a4d5 100644
--- a/bpkg/cfg-link.cli
+++ b/bpkg/cfg-link.cli
@@ -20,7 +20,7 @@ namespace bpkg
The \cb{cfg-link} command links the specified \cb{bpkg} configuration
with the current configuration. Note that it also establishes an implicit
- back-link from the specified to the current configuration. See
+ backlink from the specified to the current configuration. See
\l{bpkg-cfg-create(1)} for background on linked configurations. To unlink
previously linked configurations use \l{bpkg-cfg-unlink(1)}.
diff --git a/bpkg/cfg-link.cxx b/bpkg/cfg-link.cxx
index 5cbeef7..1146d22 100644
--- a/bpkg/cfg-link.cxx
+++ b/bpkg/cfg-link.cxx
@@ -139,7 +139,7 @@ namespace bpkg
//
if (lcf != nullptr)
{
- // Verify the back-link integrity.
+ // Verify the backlink integrity.
//
shared_ptr<configuration> cf (
ldb.query_one<configuration> (query::uuid == db.uuid.string ()));
diff --git a/bpkg/cfg-link.hxx b/bpkg/cfg-link.hxx
index 116512b..ee625fa 100644
--- a/bpkg/cfg-link.hxx
+++ b/bpkg/cfg-link.hxx
@@ -17,7 +17,7 @@ namespace bpkg
// Link the configuration specified as the directory path with the current
// configuration, attach the linked configuration database, and return the
- // link. Note that it also establishes an implicit back-link of the current
+ // link. Note that it also establishes an implicit backlink of the current
// configuration with the linked one.
//
// The specified configuration path must be absolute and normalized. If the
diff --git a/bpkg/cfg-unlink.cli b/bpkg/cfg-unlink.cli
index ade3373..6514882 100644
--- a/bpkg/cfg-unlink.cli
+++ b/bpkg/cfg-unlink.cli
@@ -21,7 +21,7 @@ namespace bpkg
The \cb{cfg-unlink} command unlinks the specified \cb{bpkg} configuration
from the current configuration (the first form) or removes dangling
- implicit back-links (the second form). See \l{bpkg-cfg-create(1)} for
+ implicit backlinks (the second form). See \l{bpkg-cfg-create(1)} for
background on linked configurations.
In the first form the configuration to unlink can be specified either as
@@ -54,7 +54,7 @@ namespace bpkg
bool --dangling
{
- "Remove dangling implicit back-links."
+ "Remove dangling implicit backlinks."
}
};
diff --git a/bpkg/cfg-unlink.cxx b/bpkg/cfg-unlink.cxx
index e29d949..52d8969 100644
--- a/bpkg/cfg-unlink.cxx
+++ b/bpkg/cfg-unlink.cxx
@@ -69,7 +69,7 @@ namespace bpkg
// Unlink the configuration in the in-memory model, so we can evaluate
// if the dependent configurations are still linked with it.
//
- // Note that we don't remove the back-link here, since this is not
+ // Note that we don't remove the backlink here, since this is not
// required for the check.
//
if (!priv)
@@ -162,7 +162,7 @@ namespace bpkg
//
assert (uc != nullptr);
- // Implicit back-link.
+ // Implicit backlink.
//
shared_ptr<configuration> cc (
udb.query_one<configuration> (query::uuid == mdb.uuid.string ()));
@@ -172,7 +172,7 @@ namespace bpkg
//
assert (cc != nullptr);
- // If the back-link turns out to be explicit, then, unless the
+ // If the backlink turns out to be explicit, then, unless the
// configuration being unlinked is private, we just turn the explicit
// link into an implicit one rather then remove the direct and back
// links.
@@ -181,7 +181,7 @@ namespace bpkg
{
info << "configurations " << udb.config_orig << " and "
<< mdb.config_orig << " are mutually linked, turning the link "
- << "to " << udb.config_orig << " into implicit back-link";
+ << "to " << udb.config_orig << " into implicit backlink";
uc->expl = false;
mdb.update (uc);
@@ -237,7 +237,7 @@ namespace bpkg
if (!exists (c.effective_path (db.config)))
{
if (verb > 1)
- text << "removing dangling implicit back-link " << c.path;
+ text << "removing dangling implicit backlink " << c.path;
db.erase (c);
++count;
@@ -247,7 +247,7 @@ namespace bpkg
t.commit ();
if (verb && !o.no_result ())
- text << "removed " << count << " dangling implicit back-link(s)";
+ text << "removed " << count << " dangling implicit backlink(s)";
return 0;
}
diff --git a/bpkg/database.cxx b/bpkg/database.cxx
index ab2bcfc..451ade3 100644
--- a/bpkg/database.cxx
+++ b/bpkg/database.cxx
@@ -697,7 +697,7 @@ namespace bpkg
if (!lc.expl && !exists (d))
{
if (verb > 1)
- info << "skipping dangling implicit back-link " << lc.path <<
+ info << "skipping dangling implicit backlink " << lc.path <<
info << "use 'cfg-unlink --dangling' to clean up";
continue;
@@ -714,17 +714,7 @@ namespace bpkg
//
if (lc.expl)
{
- shared_ptr<configuration> cf (
- db.query_one<configuration> (q::uuid == uuid.string ()));
-
- if (cf == nullptr)
- fail << "configuration " << db.config_orig << " is linked with "
- << config_orig << " but latter is not implicitly linked "
- << "with former";
-
- // While at it, verify the integrity of the other end of the link.
- //
- db.verify_link (*cf, *this);
+ shared_ptr<configuration> cf (backlink (db));
if (!cf->expl)
continue;
@@ -746,6 +736,25 @@ namespace bpkg
return implicit_links_;
}
+ shared_ptr<configuration> database::
+ backlink (database& db)
+ {
+ using q = odb::query<configuration>;
+
+ shared_ptr<configuration> cf (
+ db.query_one<configuration> (q::uuid == uuid.string ()));
+
+ if (cf == nullptr)
+ fail << "configuration " << db.config_orig << " is linked with "
+ << config_orig << " but latter is not implicitly linked "
+ << "with former";
+
+ // While at it, verify the integrity of the other end of the link.
+ //
+ db.verify_link (*cf, *this);
+ return cf;
+ }
+
linked_databases database::
dependent_configs (bool sys_rep)
{
diff --git a/bpkg/database.hxx b/bpkg/database.hxx
index 40474d3..889f8a0 100644
--- a/bpkg/database.hxx
+++ b/bpkg/database.hxx
@@ -376,6 +376,13 @@ namespace bpkg
void
verify_link (const configuration&, database&);
+ // Assuming that the passed configuration is explicitly linked to the
+ // current one, return the corresponding backlink. Issue diagnostics and
+ // fail if the backlink is not found.
+ //
+ shared_ptr<configuration>
+ backlink (database&);
+
// Set the specified tracer for the whole linked databases cluster.
//
using tracer_type = odb::tracer;
diff --git a/bpkg/package.hxx b/bpkg/package.hxx
index 6256481..13efcd4 100644
--- a/bpkg/package.hxx
+++ b/bpkg/package.hxx
@@ -160,7 +160,7 @@ namespace bpkg
dir_path path; // Empty for the self-link.
// True if the link is created explicitly by the user rather than
- // automatically as a back-link.
+ // automatically as a backlink.
//
bool expl;
diff --git a/tests/cfg-info.testscript b/tests/cfg-info.testscript
new file mode 100644
index 0000000..10e1e4c
--- /dev/null
+++ b/tests/cfg-info.testscript
@@ -0,0 +1,176 @@
+# file : tests/cfg-info.testscript
+# license : MIT; see accompanying LICENSE file
+
+.include common.testscript
+
+cfg_create += 2>!
+
+uuid1 = '18f48b4b-b5d9-4712-b98c-1930df1c4228'
+uuid2 = '28f48b4b-b5d9-4712-b98c-1930df1c4228'
+uuid3 = '38f48b4b-b5d9-4712-b98c-1930df1c4228'
+uuid4 = '48f48b4b-b5d9-4712-b98c-1930df1c4228'
+
++$cfg_create -d cfg1 --name 't1' --uuid "$uuid1" &cfg1/***
++$cfg_create -d cfg2 --name 't2' --uuid "$uuid2" &cfg2/***
++$cfg_create -d cfg3 --name 'h3' --uuid "$uuid3" --type host &cfg3/***
++$cfg_create -d cfg4 --name 'b4' --uuid "$uuid4" --type build2 &cfg4/***
+
++$cfg_link -d cfg1 cfg3 2>!
++$cfg_link -d cfg2 cfg3 2>!
++$cfg_link -d cfg3 cfg4 2>!
+
+clone_cfgs = cp -r ../cfg1 ../cfg2 ../cfg3 ../cfg4 ./
+
+sp = ' '
+
+: self
+:
+{
+ $clone_cfgs;
+
+ $* -d cfg1 >>/"EOO"
+ path: $~/cfg1/
+ uuid: $uuid1
+ type: target
+ name: t1
+ EOO
+}
+
+: links
+:
+{
+ $clone_cfgs;
+
+ $* -d cfg1 --link >>/"EOO"
+ path: $~/cfg1/
+ uuid: $uuid1
+ type: target
+ name: t1
+
+ path: $~/cfg3/
+ uuid: $uuid3
+ type: host
+ name: h3
+ EOO
+}
+
+: links-recursive
+:
+{
+ $clone_cfgs;
+
+ $* -d cfg1 --link --recursive >>/"EOO"
+ path: $~/cfg1/
+ uuid: $uuid1
+ type: target
+ name: t1
+
+ path: $~/cfg3/
+ uuid: $uuid3
+ type: host
+ name: h3
+
+ path: $~/cfg4/
+ uuid: $uuid4
+ type: build2
+ name: b4
+ EOO
+}
+
+: backlinks
+:
+{
+ $clone_cfgs;
+
+ $* -d cfg3 --backlink >>/"EOO";
+ path: $~/cfg3/
+ uuid: $uuid3
+ type: host
+ name: h3
+
+ path: $~/cfg1/
+ uuid: $uuid1
+ type: target
+ name: t1
+
+ path: $~/cfg2/
+ uuid: $uuid2
+ type: target
+ name: t2
+ EOO
+
+ mv cfg2 cfg2.tmp;
+
+ # Make sure that dangling links are silently skipped.
+ #
+ $* -d cfg3 --backlink >>/"EOO";
+ path: $~/cfg3/
+ uuid: $uuid3
+ type: host
+ name: h3
+
+ path: $~/cfg1/
+ uuid: $uuid1
+ type: target
+ name: t1
+ EOO
+
+ # While at it, test printing dangling links.
+ #
+ $* -d cfg3 --dangling >>/"EOO";
+ path: $~/cfg3/
+ uuid: $uuid3
+ type: host
+ name: h3
+
+ path: $~/cfg2/
+ uuid: $uuid2
+ type: target
+ name:$sp
+ EOO
+
+ $* -d cfg3 --dangling --backlink >>/"EOO"
+ path: $~/cfg3/
+ uuid: $uuid3
+ type: host
+ name: h3
+
+ path: $~/cfg1/
+ uuid: $uuid1
+ type: target
+ name: t1
+
+ path: $~/cfg2/
+ uuid: $uuid2
+ type: target
+ name:$sp
+ EOO
+}
+
+: all-links-recursive
+:
+{
+ $clone_cfgs;
+
+ $* -d cfg1 --link --backlink --recursive >>/"EOO"
+ path: $~/cfg1/
+ uuid: $uuid1
+ type: target
+ name: t1
+
+ path: $~/cfg3/
+ uuid: $uuid3
+ type: host
+ name: h3
+
+ path: $~/cfg2/
+ uuid: $uuid2
+ type: target
+ name: t2
+
+ path: $~/cfg4/
+ uuid: $uuid4
+ type: build2
+ name: b4
+ EOO
+}
diff --git a/tests/cfg-link.testscript b/tests/cfg-link.testscript
index 26496eb..091d2f0 100644
--- a/tests/cfg-link.testscript
+++ b/tests/cfg-link.testscript
@@ -17,7 +17,7 @@ acfg_uuid = '28f48b4b-b5d9-4712-b98c-1930df1c4228'
$cfg_create -d cfg --name 'main';
$cfg_create -d acfg --name 'shared' --uuid "$acfg_uuid" &acfg/***;
- # Try to link configuration under the same name.
+ # Try to link self configuration.
#
$* cfg 2>>/~"%EOE%" != 0;
%error: linking configuration .+/cfg/ with itself%
@@ -53,8 +53,11 @@ acfg_uuid = '28f48b4b-b5d9-4712-b98c-1930df1c4228'
name: shared
EOO
- $pkg_status -d cfg libfoo >'libfoo unknown';
- $pkg_status -d acfg libfoo >'libfoo unknown';
+ $cfg_info -d cfg2 --backlink >>/"EOO";
+ path: $~/cfg2/
+ uuid: $uuid2
+ type: target
+ name: shared
# Test that the recreated configuration can be implicitly re-linked.
#
@@ -76,9 +79,17 @@ acfg_uuid = '28f48b4b-b5d9-4712-b98c-1930df1c4228'
%error: configuration with uuid .{36} is already linked as \.\./acfg/%
EOE
- rm -r acfg;
+ # Test that the integrity check fails.
+ #
+ mv acfg acfg.tmp;
- $cfg_create -d acfg --name 'shared' &acfg/***;
+ $cfg_create -d acfg --name 'shared';
+
+ $pkg_status -d cfg libfoo 2>>/~"%EOE%" != 0;
+ error: configuration acfg/ uuid mismatch
+ % info: uuid .+%
+ info: linked with cfg/ as $acfg_uuid
+ EOE
# Test that the path clash is reported.
#
@@ -95,6 +106,8 @@ acfg_uuid = '28f48b4b-b5d9-4712-b98c-1930df1c4228'
info: consider specifying alternative name with --name
EOE
+ # Link the second configuration.
+ #
$* acfg2 2>>/~%EOE%;
warning: configuration with name shared is already linked as ../acfg/, linking as unnamed
%linked with configuration .+/acfg2/%
@@ -103,18 +116,17 @@ acfg_uuid = '28f48b4b-b5d9-4712-b98c-1930df1c4228'
id: 2
EOE
- # Test that the integrity check fails.
+ # Restore the integrity.
#
- $pkg_status -d cfg libfoo 2>>/~"%EOE%" != 0;
+
+ $cfg_info -d cfg >- 2>>/~%EOE% != 0;
error: configuration acfg/ uuid mismatch
- % info: uuid .+%
- info: linked with cfg/ as $acfg_uuid
+ % info: uuid .{36}%
+ % info: linked with cfg/ as .{36}%
EOE
- # Link the second configuration.
- #
rm -r acfg;
- $cfg_create -d acfg --name 'shared' --uuid "$acfg_uuid" &acfg/***;
+ mv acfg.tmp acfg;
$cfg_info -d cfg >>/~"%EOO%";
path: $~/cfg/
diff --git a/tests/cfg-unlink.testscript b/tests/cfg-unlink.testscript
index b65f4b1..2ca3783 100644
--- a/tests/cfg-unlink.testscript
+++ b/tests/cfg-unlink.testscript
@@ -148,7 +148,7 @@ clone_root_cfgs = cp -r $~/cfg1 $~/cfg2 ./
$cfg_link -d cfg2 cfg1 2>!;
$* -d cfg1 cfg2 2>>/"EOE";
- info: configurations cfg2/ and cfg1/ are mutually linked, turning the link to cfg2/ into implicit back-link
+ info: configurations cfg2/ and cfg1/ are mutually linked, turning the link to cfg2/ into implicit backlink
unlinked configuration $~/cfg2/
EOE
@@ -261,8 +261,8 @@ clone_root_cfgs = cp -r $~/cfg1 $~/cfg2 ./
mv cfg1 cfg3;
- $* -d cfg2 --dangling 2>'removed 1 dangling implicit back-link(s)';
- $* -d cfg2 --dangling 2>'removed 0 dangling implicit back-link(s)'
+ $* -d cfg2 --dangling 2>'removed 1 dangling implicit backlink(s)';
+ $* -d cfg2 --dangling 2>'removed 0 dangling implicit backlink(s)'
}
: error