aboutsummaryrefslogtreecommitdiff
path: root/bpkg
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2015-10-06 08:43:01 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2015-10-06 08:43:01 +0200
commitfd2c0dc9707714d82580dc61854efc06335e6091 (patch)
tree2ad76b0dad17b244c183405b02fa9138d0407e8d /bpkg
parenta23e8ac5de6170a4a82a2ac475aff707d5accbf6 (diff)
Track prerequisite packages, handle in pkg-{con,dis}figure
Diffstat (limited to 'bpkg')
-rw-r--r--bpkg/buildfile44
-rw-r--r--bpkg/package43
-rw-r--r--bpkg/package.xml24
-rw-r--r--bpkg/pkg-configure.cxx57
-rw-r--r--bpkg/pkg-disfigure.cxx31
-rw-r--r--bpkg/pkg-fetch.cxx3
-rw-r--r--bpkg/pkg-unpack.cxx3
-rw-r--r--bpkg/satisfaction39
-rw-r--r--bpkg/satisfaction.cxx101
-rwxr-xr-xbpkg/test.sh67
10 files changed, 387 insertions, 25 deletions
diff --git a/bpkg/buildfile b/bpkg/buildfile
index e9f00f2..12bb391 100644
--- a/bpkg/buildfile
+++ b/bpkg/buildfile
@@ -9,28 +9,28 @@ import libs += libbutl%lib{butl}
import libs += libodb%lib{odb}
import libs += libodb-sqlite%lib{odb-sqlite}
-exe{bpkg}: cxx{fetch package package-odb manifest-utility database \
- diagnostics utility} \
- cli.cxx{common-options} cxx{types-parsers} \
- cxx{bpkg} cli.cxx{bpkg-options} \
- cxx{help} cli.cxx{help-options} \
- cli.cxx{configuration-options} \
- cxx{build} cli.cxx{build-options} \
- cxx{pkg-command} \
- cxx{pkg-verify} cli.cxx{pkg-verify-options} \
- cxx{pkg-status} cli.cxx{pkg-status-options} \
- cxx{pkg-fetch} cli.cxx{pkg-fetch-options} \
- cxx{pkg-unpack} cli.cxx{pkg-unpack-options} \
- cxx{pkg-purge} cli.cxx{pkg-purge-options} \
- cxx{pkg-configure} cli.cxx{pkg-configure-options} \
- cxx{pkg-disfigure} cli.cxx{pkg-disfigure-options} \
- cli.cxx{pkg-update-options} \
- cli.cxx{pkg-clean-options} \
- cxx{cfg-create} cli.cxx{cfg-create-options} \
- cxx{rep-add} cli.cxx{rep-add-options} \
- cxx{rep-fetch} cli.cxx{rep-fetch-options} \
- cxx{rep-info} cli.cxx{rep-info-options} \
- cxx{rep-create} cli.cxx{rep-create-options} \
+exe{bpkg}: cxx{satisfaction fetch package package-odb manifest-utility \
+ database diagnostics utility} \
+ cli.cxx{common-options} cxx{types-parsers} \
+ cxx{bpkg} cli.cxx{bpkg-options} \
+ cxx{help} cli.cxx{help-options} \
+ cli.cxx{configuration-options} \
+ cxx{build} cli.cxx{build-options} \
+ cxx{pkg-command} \
+ cxx{pkg-verify} cli.cxx{pkg-verify-options} \
+ cxx{pkg-status} cli.cxx{pkg-status-options} \
+ cxx{pkg-fetch} cli.cxx{pkg-fetch-options} \
+ cxx{pkg-unpack} cli.cxx{pkg-unpack-options} \
+ cxx{pkg-purge} cli.cxx{pkg-purge-options} \
+ cxx{pkg-configure} cli.cxx{pkg-configure-options} \
+ cxx{pkg-disfigure} cli.cxx{pkg-disfigure-options} \
+ cli.cxx{pkg-update-options} \
+ cli.cxx{pkg-clean-options} \
+ cxx{cfg-create} cli.cxx{cfg-create-options} \
+ cxx{rep-add} cli.cxx{rep-add-options} \
+ cxx{rep-fetch} cli.cxx{rep-fetch-options} \
+ cxx{rep-info} cli.cxx{rep-info-options} \
+ cxx{rep-create} cli.cxx{rep-create-options} \
$libs
# Option length must be the same to get commands/topics/options aligned.
diff --git a/bpkg/package b/bpkg/package
index 419acdc..04cb821 100644
--- a/bpkg/package
+++ b/bpkg/package
@@ -387,15 +387,58 @@ namespace bpkg
//
optional<dir_path> out_root;
+ // A map of "effective" prerequisites (i.e., pointers to other
+ // selected packages) to optional dependency constraint.
+ //
+ using prerequisites_type = std::map<lazy_shared_ptr<package>,
+ optional<dependency_constraint>,
+ compare_lazy_ptr>;
+ prerequisites_type prerequisites;
+
// Database mapping.
//
#pragma db member(name) id
+ #pragma db member(prerequisites) id_column("package") \
+ key_column("prerequisite") value_column("") key_not_null
+
private:
friend class odb::access;
package () = default;
};
+ // Return a list of packages that depend on this package along with
+ // their constraints.
+ //
+ /*
+ #pragma db view object(package) \
+ container(package::prerequisites = pp inner: pp.key)
+ struct package_dependents
+ {
+ #pragma db column(pp.id)
+ string name;
+
+ #pragma db column(pp.value)
+ optional<dependency_constraint> constraint;
+ };
+ */
+
+ // @@ Using raw container table since ODB doesn't support containers
+ // in views yet.
+ //
+ #pragma db view object(package) \
+ table("package_prerequisites" = "pp" inner: \
+ "pp.prerequisite = " + package::name)
+ struct package_dependents
+ {
+ #pragma db column("pp.package")
+ string name;
+
+ #pragma db column("pp.")
+ optional<dependency_constraint> constraint;
+ };
+
+
// Version comparison operators.
//
// They allow comparing objects that have epoch, canonical_upstream,
diff --git a/bpkg/package.xml b/bpkg/package.xml
index 89ec0af..9935e06 100644
--- a/bpkg/package.xml
+++ b/bpkg/package.xml
@@ -169,5 +169,29 @@
<column name="name"/>
</primary-key>
</table>
+ <table name="package_prerequisites" kind="container">
+ <column name="package" type="TEXT" null="true"/>
+ <column name="prerequisite" type="TEXT" null="true"/>
+ <column name="operation" type="TEXT" null="true"/>
+ <column name="version_epoch" type="INTEGER" null="true"/>
+ <column name="version_canonical_upstream" type="TEXT" null="true"/>
+ <column name="version_revision" type="INTEGER" null="true"/>
+ <column name="version_upstream" type="TEXT" null="true"/>
+ <foreign-key name="package_fk" on-delete="CASCADE">
+ <column name="package"/>
+ <references table="package">
+ <column name="name"/>
+ </references>
+ </foreign-key>
+ <index name="package_prerequisites_package_i">
+ <column name="package"/>
+ </index>
+ <foreign-key name="prerequisite_fk" deferrable="DEFERRED">
+ <column name="prerequisite"/>
+ <references table="package">
+ <column name="name"/>
+ </references>
+ </foreign-key>
+ </table>
</model>
</changelog>
diff --git a/bpkg/pkg-configure.cxx b/bpkg/pkg-configure.cxx
index 169772c..208fad4 100644
--- a/bpkg/pkg-configure.cxx
+++ b/bpkg/pkg-configure.cxx
@@ -10,7 +10,9 @@
#include <bpkg/utility>
#include <bpkg/database>
#include <bpkg/diagnostics>
+#include <bpkg/satisfaction>
+#include <bpkg/pkg-verify>
#include <bpkg/pkg-disfigure>
using namespace std;
@@ -49,6 +51,7 @@ namespace bpkg
database db (open (c, trace));
transaction t (db.begin ());
+ session s;
shared_ptr<package> p (db.find<package> (n));
@@ -73,6 +76,60 @@ namespace bpkg
level4 ([&]{trace << "src_root: " << src_root << ", "
<< "out_root: " << out_root;});
+ // Verify all our prerequisites are configured and populate the
+ // prerequisites list.
+ //
+ {
+ assert (p->prerequisites.empty ());
+
+ package_manifest m (pkg_verify (src_root));
+
+ for (const dependency_alternatives& da: m.dependencies)
+ {
+ assert (!da.conditional); //@@ TODO
+
+ bool satisfied (false);
+ for (const dependency& d: da)
+ {
+ if (shared_ptr<package> dp = db.find<package> (d.name))
+ {
+ if (dp->state != state::configured)
+ continue;
+
+ if (!satisfies (dp->version, d.constraint))
+ continue;
+
+ auto r (p->prerequisites.emplace (dp, d.constraint));
+
+ // If we already have a dependency on this package, pick the
+ // stricter of the two constraints.
+ //
+ if (!r.second)
+ {
+ auto& c (r.first->second);
+
+ bool s1 (satisfies (c, d.constraint));
+ bool s2 (satisfies (d.constraint, c));
+
+ if (!s1 && !s2)
+ fail << "incompatible constraints "
+ << "(" << d.name << " " << *c << ") and "
+ << "(" << d.name << " " << *d.constraint << ")";
+
+ if (s2 && !s1)
+ c = d.constraint;
+ }
+
+ satisfied = true;
+ break;
+ }
+ }
+
+ if (!satisfied)
+ fail << "no configured package satisfies dependency on " << da;
+ }
+ }
+
// Form the buildspec.
//
string bspec;
diff --git a/bpkg/pkg-disfigure.cxx b/bpkg/pkg-disfigure.cxx
index 6c357f4..a3d0336 100644
--- a/bpkg/pkg-disfigure.cxx
+++ b/bpkg/pkg-disfigure.cxx
@@ -21,11 +21,40 @@ namespace bpkg
transaction& t,
const shared_ptr<package>& p)
{
+ assert (p->state == state::configured || p->state == state::broken);
+
tracer trace ("pkg_disfigure");
database& db (t.database ());
tracer_guard tg (db, trace);
+ // Check that we have no dependents.
+ //
+ if (p->state == state::configured)
+ {
+ using query = query<package_dependents>;
+
+ auto r (db.query<package_dependents> (query::name == p->name));
+
+ if (!r.empty ())
+ {
+ diag_record dr;
+ dr << fail << "package " << p->name << " still has dependencies:";
+
+ for (const package_dependents& pd: r)
+ {
+ dr << info << "package " << pd.name;
+
+ if (pd.constraint)
+ dr << " on " << p->name << " " << *pd.constraint;
+ }
+ }
+ }
+
+ // Since we are no longer configured, clear the prerequisites list.
+ //
+ p->prerequisites.clear ();
+
// Calculate package's src_root and out_root.
//
assert (p->src_root); // Must be set since unpacked.
@@ -43,7 +72,7 @@ namespace bpkg
//
string bspec;
- if (p->state != state::broken)
+ if (p->state == state::configured)
{
bspec = "clean(" + out_root.string () + "/) "
"disfigure(" + out_root.string () + "/)";
diff --git a/bpkg/pkg-fetch.cxx b/bpkg/pkg-fetch.cxx
index b1e5253..4a2a9ea 100644
--- a/bpkg/pkg-fetch.cxx
+++ b/bpkg/pkg-fetch.cxx
@@ -145,7 +145,8 @@ namespace bpkg
purge,
nullopt, // No source directory yet.
false,
- nullopt // No output directory yet.
+ nullopt, // No output directory yet.
+ {} // No prerequisites captured yet.
});
db.persist (p);
diff --git a/bpkg/pkg-unpack.cxx b/bpkg/pkg-unpack.cxx
index b6decca..32d2af3 100644
--- a/bpkg/pkg-unpack.cxx
+++ b/bpkg/pkg-unpack.cxx
@@ -69,7 +69,8 @@ namespace bpkg
false, // Don't purge archive.
move (ad),
purge,
- nullopt // No output directory yet.
+ nullopt, // No output directory yet.
+ {} // No prerequisites captured yet.
});
db.persist (p);
diff --git a/bpkg/satisfaction b/bpkg/satisfaction
new file mode 100644
index 0000000..2200688
--- /dev/null
+++ b/bpkg/satisfaction
@@ -0,0 +1,39 @@
+// file : bpkg/satisfaction -*- C++ -*-
+// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#ifndef BPKG_SATISFACTION
+#define BPKG_SATISFACTION
+
+#include <bpkg/types>
+#include <bpkg/package>
+#include <bpkg/utility>
+
+namespace bpkg
+{
+ // Return true if version satisfies the constraint.
+ //
+ bool
+ satisfies (const version&, const dependency_constraint&);
+
+ inline bool
+ satisfies (const version& v, const optional<dependency_constraint>& c)
+ {
+ return !c || satisfies (v, *c);
+ }
+
+ // Return true if any version that satisfies l also satisfies r, or, in
+ // other words, l is stricter than or equal to r.
+ //
+ bool
+ satisfies (const dependency_constraint& l, const dependency_constraint& r);
+
+ inline bool
+ satisfies (const optional<dependency_constraint>& l,
+ const optional<dependency_constraint>& r)
+ {
+ return l ? (!r || satisfies (*l, *r)) : !r;
+ }
+}
+
+#endif // BPKG_SATISFACTION
diff --git a/bpkg/satisfaction.cxx b/bpkg/satisfaction.cxx
new file mode 100644
index 0000000..0631b83
--- /dev/null
+++ b/bpkg/satisfaction.cxx
@@ -0,0 +1,101 @@
+// file : bpkg/satisfaction.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <bpkg/satisfaction>
+
+#include <bpkg/package-odb>
+#include <bpkg/diagnostics>
+
+using namespace std;
+using namespace butl;
+
+namespace bpkg
+{
+ bool
+ satisfies (const version& v, const dependency_constraint& c)
+ {
+ using op = comparison;
+
+ // Note that the constraint's version is always rhs (libfoo >= 1.2.3).
+ //
+ switch (c.operation)
+ {
+ case op::eq: return v == c.version;
+ case op::lt: return v < c.version;
+ case op::gt: return v > c.version;
+ case op::le: return v <= c.version;
+ case op::ge: return v >= c.version;
+ }
+
+ assert (false);
+ return false;
+ }
+
+ bool
+ satisfies (const dependency_constraint& l, const dependency_constraint& r)
+ {
+ using op = comparison;
+
+ op lo (l.operation);
+ op ro (r.operation);
+
+ const version& lv (l.version);
+ const version& rv (r.version);
+
+ switch (lo)
+ {
+ case op::eq: // ==
+ {
+ return ro == op::eq && lv == rv;
+ }
+ case op::lt: // <
+ {
+ switch (ro)
+ {
+ case op::eq: return rv < lv;
+ case op::lt: return rv <= lv;
+ case op::le: return rv < lv;
+ case op::gt:
+ case op::ge: return false;
+ }
+ }
+ case op::le: // <=
+ {
+ switch (ro)
+ {
+ case op::eq: return rv <= lv;
+ case op::lt: return rv < lv;
+ case op::le: return rv <= lv;
+ case op::gt:
+ case op::ge: return false;
+ }
+ }
+ case op::gt: // >
+ {
+ switch (ro)
+ {
+ case op::eq: return lv > rv;
+ case op::lt:
+ case op::le: return false;
+ case op::gt: return lv >= rv;
+ case op::ge: return lv > rv;
+ }
+ }
+ case op::ge: // >=
+ {
+ switch (ro)
+ {
+ case op::eq: return lv >= rv;
+ case op::lt:
+ case op::le: return false;
+ case op::gt: return lv > rv;
+ case op::ge: return lv >= rv;
+ }
+ }
+ }
+
+ assert (false);
+ return false;
+ }
+}
diff --git a/bpkg/test.sh b/bpkg/test.sh
index 5449883..4b2db6f 100755
--- a/bpkg/test.sh
+++ b/bpkg/test.sh
@@ -409,6 +409,73 @@ rm -r $out
test pkg-purge -f $pkg
stat unknown
+# dependency management
+#
+test rep-create ../tests/repository/1/depend/stable
+test cfg-create --wipe
+test rep-add ../tests/repository/1/depend/stable
+test rep-fetch
+
+test pkg-fetch libbar 1.0.0
+test pkg-unpack libbar
+fail pkg-configure libbar # no libfoo
+stat libbar 1.0.0 "unpacked"
+test pkg-fetch libfoo 1.0.0
+test pkg-unpack libfoo
+fail pkg-configure libbar # libfoo not configured
+test pkg-configure libfoo
+test pkg-configure libbar
+fail pkg-disfigure libfoo # libbar still depends on libfoo
+test pkg-disfigure libbar
+test pkg-disfigure libfoo
+test pkg-purge libbar
+test pkg-purge libfoo
+
+test pkg-fetch libfoo 1.0.0
+test pkg-unpack libfoo
+test pkg-configure libfoo
+test pkg-fetch libbar 1.1.0
+test pkg-unpack libbar
+fail pkg-configure libbar # libfoo >= 1.1.0
+test pkg-disfigure libfoo
+test pkg-purge libfoo
+test pkg-fetch libfoo 1.1.0
+test pkg-unpack libfoo
+test pkg-configure libfoo
+test pkg-configure libbar
+test pkg-disfigure libbar
+test pkg-disfigure libfoo
+test pkg-purge libfoo
+test pkg-purge libbar
+
+test pkg-fetch libfoo 1.1.0
+test pkg-unpack libfoo
+test pkg-configure libfoo
+test pkg-fetch libbar 1.2.0
+test pkg-unpack libbar
+fail pkg-configure libbar # libfoo >= 1.2.0
+test pkg-disfigure libfoo
+test pkg-purge libfoo
+test pkg-fetch libfoo 1.2.0
+test pkg-unpack libfoo
+test pkg-configure libfoo
+test pkg-configure libbar
+fail pkg-disfigure libfoo # "package libbar on libfoo >= 1.2.0"
+test pkg-disfigure libbar
+test pkg-disfigure libfoo
+test pkg-purge libfoo
+test pkg-purge libbar
+
+test pkg-fetch libfoo 1.1.0
+test pkg-unpack libfoo
+test pkg-configure libfoo
+test pkg-fetch libbar 1.3.0
+test pkg-unpack libbar
+fail pkg-configure libbar # incompatible constraints
+test pkg-disfigure libfoo
+test pkg-purge libfoo
+test pkg-purge libbar
+
##
## pkg-status (also tested in pkg-{fetch,unpack,configure,disfigure,purge}
##