aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2015-09-21 18:08:39 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2015-09-21 18:08:39 +0200
commit221f0250fcd7cba4fa4b5e4fd6c0d410eb6e5811 (patch)
tree3ea5eca558f612fcc9b27db2778d4bfa47e38556
parent9792fc9d137b4dd702360ac0242f9a7a26e675c2 (diff)
Implement rep-add command
-rw-r--r--bpkg/bpkg-options.cli7
-rw-r--r--bpkg/bpkg.cxx3
-rw-r--r--bpkg/buildfile4
-rw-r--r--bpkg/cfg-create.cxx10
-rw-r--r--bpkg/database.cxx3
-rw-r--r--bpkg/package159
-rw-r--r--bpkg/package.cxx65
-rw-r--r--bpkg/package.xml90
-rw-r--r--bpkg/pkg-fetch.cxx2
-rw-r--r--bpkg/pkg-purge.cxx2
-rw-r--r--bpkg/pkg-unpack.cxx2
-rw-r--r--bpkg/rep-add17
-rw-r--r--bpkg/rep-add-options.cli35
-rw-r--r--bpkg/rep-add.cxx82
-rwxr-xr-xbpkg/test.sh25
-rw-r--r--bpkg/types16
16 files changed, 505 insertions, 17 deletions
diff --git a/bpkg/bpkg-options.cli b/bpkg/bpkg-options.cli
index cac46ba..6ea7f91 100644
--- a/bpkg/bpkg-options.cli
+++ b/bpkg/bpkg-options.cli
@@ -91,6 +91,13 @@ namespace bpkg
""
};
+ bool rep-add
+ {
+ "<rep>",
+ "Add repository to configuration.",
+ ""
+ };
+
bool rep-create
{
"[<dir>]",
diff --git a/bpkg/bpkg.cxx b/bpkg/bpkg.cxx
index ffe41a6..c84a1f2 100644
--- a/bpkg/bpkg.cxx
+++ b/bpkg/bpkg.cxx
@@ -26,6 +26,8 @@
#include <bpkg/pkg-clean>
#include <bpkg/cfg-create>
+
+#include <bpkg/rep-add>
#include <bpkg/rep-create>
using namespace std;
@@ -194,6 +196,7 @@ try
//
#define REP_COMMAND(CMD) ANY_COMMAND(rep, CMD)
+ REP_COMMAND (add);
REP_COMMAND (create);
// @@ Would be nice to check that args doesn't contain any junk left.
diff --git a/bpkg/buildfile b/bpkg/buildfile
index 8d7a0f9..c016e09 100644
--- a/bpkg/buildfile
+++ b/bpkg/buildfile
@@ -25,6 +25,7 @@ exe{bpkg}: cxx{package package-odb database diagnostics utility} \
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-create} cli.cxx{rep-create-options} \
$libs
@@ -68,5 +69,8 @@ cli.cxx{cfg-create-options}: cli.options += --exclude-base
# rep-*
#
+cli.cxx{rep-add-options}: cli{rep-add-options}
+cli.cxx{rep-add-options}: cli.options += --exclude-base
+
cli.cxx{rep-create-options}: cli{rep-create-options}
cli.cxx{rep-create-options}: cli.options += --exclude-base
diff --git a/bpkg/cfg-create.cxx b/bpkg/cfg-create.cxx
index 44728b8..be85d92 100644
--- a/bpkg/cfg-create.cxx
+++ b/bpkg/cfg-create.cxx
@@ -9,6 +9,8 @@
#include <fstream>
#include <bpkg/types>
+#include <bpkg/package>
+#include <bpkg/package-odb>
#include <bpkg/utility>
#include <bpkg/database>
#include <bpkg/diagnostics>
@@ -116,7 +118,13 @@ namespace bpkg
// Create the database.
//
- open (c, trace, true);
+ database db (open (c, trace, true));
+
+ // Add the special, root repository object with empty location.
+ //
+ transaction t (db.begin ());
+ db.persist (repository (repository_location ()));
+ t.commit ();
if (verb)
{
diff --git a/bpkg/database.cxx b/bpkg/database.cxx
index 8553a15..b9397ad 100644
--- a/bpkg/database.cxx
+++ b/bpkg/database.cxx
@@ -4,9 +4,6 @@
#include <bpkg/database>
-#include <memory> // unique_ptr
-#include <utility> // move()
-
#include <odb/schema-catalog.hxx>
#include <odb/sqlite/exceptions.hxx>
diff --git a/bpkg/package b/bpkg/package
index aca883d..1c2a420 100644
--- a/bpkg/package
+++ b/bpkg/package
@@ -5,14 +5,17 @@
#ifndef BPKG_PACKAGE
#define BPKG_PACKAGE
-#include <memory> // shared_ptr
+#include <set>
+#include <vector>
#include <cstdint> // uint16
#include <ostream>
#include <utility> // move()
+#include <cstdint> // uint16
#include <odb/core.hxx>
#include <bpkg/types>
+#include <bpkg/utility>
#pragma db model version(1, 1, open)
@@ -25,9 +28,9 @@ namespace bpkg
struct _version
{
std::uint16_t epoch;
- std::string upstream;
+ string upstream;
std::uint16_t revision;
- std::string canonical_upstream;
+ string canonical_upstream;
};
}
@@ -35,6 +38,20 @@ namespace bpkg
namespace bpkg
{
+ // compare_lazy_ptr
+ //
+ // Compare two lazy pointers via the pointed-to object ids.
+ //
+ struct compare_lazy_ptr
+ {
+ template <typename P>
+ bool
+ operator() (const P& x, const P& y) const
+ {
+ return x.object_id () < y.object_id ();
+ }
+ };
+
// path
//
using optional_string = optional<string>;
@@ -64,6 +81,140 @@ namespace bpkg
(?).canonical_upstream ()}) \
from(bpkg::version ((?).epoch, std::move ((?).upstream), (?).revision))
+ // repository
+ //
+ #pragma db object pointer(std::shared_ptr) session
+ class repository
+ {
+ public:
+ // We use a weak pointer for prerequisite repositories because we
+ // might have cycles.
+ //
+ using complements_type =
+ std::set<lazy_shared_ptr<repository>, compare_lazy_ptr>;
+ using prerequisites_type =
+ std::set<lazy_weak_ptr<repository>, compare_lazy_ptr>;
+
+ repository_location location;
+ complements_type complements;
+ prerequisites_type prerequisites;
+
+ public:
+ explicit
+ repository (repository_location l): location (move (l)) {}
+
+ // Database mapping.
+ //
+ #pragma db value
+ struct _id_type
+ {
+ string name; // Canonical name.
+ string location;
+ };
+
+ _id_type
+ _id () const;
+
+ void
+ _id (_id_type&&);
+
+ #pragma db member(location) transient
+
+ #pragma db member(id) virtual(_id_type) before id(name) \
+ get(_id) set(_id (std::move (?))) column("")
+
+ #pragma db member(complements) id_column("repository") \
+ value_column("complement") value_not_null
+
+ #pragma db member(prerequisites) id_column("repository") \
+ value_column("prerequisite") value_not_null
+
+ private:
+ friend class odb::access;
+ repository () = default;
+ };
+
+ // package_version_id
+ //
+ #pragma db value
+ struct package_version_id
+ {
+ string name;
+ std::uint16_t epoch;
+ string upstream; // Canonical upstream.
+ std::uint16_t revision;
+
+ #pragma db member(epoch) column("version_epoch")
+ #pragma db member(upstream) column("version_upstream")
+ #pragma db member(revision) column("version_revision")
+ };
+
+ bool
+ operator< (const package_version_id&, const package_version_id&);
+
+ // package_location
+ //
+ #pragma db value
+ struct package_location
+ {
+ using repository_type = bpkg::repository;
+
+ lazy_shared_ptr<repository_type> repository;
+ path location; // Relative to the repository.
+ };
+
+ // available_package
+ //
+ #pragma db object pointer(shared_ptr) session
+ class available_package
+ {
+ public:
+ using version_type = bpkg::version;
+
+ string name;
+ version_type version;
+
+ // List of repositories to which this package version belongs (yes,
+ // in our world, it can be in multiple, unrelated repositories).
+ //
+ std::vector<package_location> locations;
+
+ // Database mapping.
+ //
+
+ // id
+ //
+ #pragma db value
+ struct _id_type
+ {
+ package_version_id data;
+ string version_original_upstream;
+
+ #pragma db member(data) column("")
+ };
+
+ _id_type
+ _id () const;
+
+ void
+ _id (_id_type&&);
+
+ #pragma db member(name) transient
+ #pragma db member(version) transient
+
+ #pragma db member(id) virtual(_id_type) before id(data) \
+ get(_id) set(_id (std::move (?))) column("")
+
+ // repositories
+ //
+ #pragma db member(locations) id_column("") value_column("") \
+ unordered value_not_null
+
+ private:
+ friend class odb::access;
+ available_package () = default;
+ };
+
// state
//
enum class state
@@ -89,7 +240,7 @@ namespace bpkg
// package
//
- #pragma db object pointer(shared_ptr)
+ #pragma db object pointer(shared_ptr) session
class package
{
public:
diff --git a/bpkg/package.cxx b/bpkg/package.cxx
index 66769d1..9528288 100644
--- a/bpkg/package.cxx
+++ b/bpkg/package.cxx
@@ -4,12 +4,77 @@
#include <bpkg/package>
+#include <cassert>
#include <stdexcept> // invalid_argument
using namespace std;
namespace bpkg
{
+ // repository
+ //
+ repository::_id_type repository::
+ _id () const
+ {
+ return _id_type {location.canonical_name (), location.string ()};
+ }
+
+ void repository::
+ _id (_id_type&& l)
+ {
+ location = repository_location (move (l.location));
+ assert (location.canonical_name () == l.name);
+ }
+
+ // package_version_id
+ //
+ bool
+ operator< (const package_version_id& x, const package_version_id& y)
+ {
+ int r (x.name.compare (y.name));
+
+ if (r != 0)
+ return r < 0;
+
+ if (x.epoch != y.epoch)
+ return x.epoch < y.epoch;
+
+ r = x.upstream.compare (y.upstream);
+
+ if (r != 0)
+ return r < 0;
+
+ return x.revision < y.revision;
+ }
+
+ // available_package
+ //
+ available_package::_id_type available_package::
+ _id () const
+ {
+ return _id_type {
+ {
+ name,
+ version.epoch (),
+ version.canonical_upstream (),
+ version.revision ()
+ },
+ version.upstream ()
+ };
+ }
+
+ void available_package::
+ _id (_id_type&& v)
+ {
+ name = move (v.data.name);
+ version = version_type (v.data.epoch,
+ move (v.version_original_upstream),
+ v.data.revision);
+ assert (version.canonical_upstream () == v.data.upstream);
+ }
+
+ // state
+ //
string
to_string (state s)
{
diff --git a/bpkg/package.xml b/bpkg/package.xml
index 4bea324..1508d61 100644
--- a/bpkg/package.xml
+++ b/bpkg/package.xml
@@ -1,5 +1,95 @@
<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="sqlite" version="1">
<model version="1">
+ <table name="repository" kind="object">
+ <column name="name" type="TEXT" null="true"/>
+ <column name="location" type="TEXT" null="true"/>
+ <primary-key>
+ <column name="name"/>
+ </primary-key>
+ </table>
+ <table name="repository_complements" kind="container">
+ <column name="repository" type="TEXT" null="true"/>
+ <column name="complement" type="TEXT" null="true"/>
+ <foreign-key name="repository_fk" on-delete="CASCADE">
+ <column name="repository"/>
+ <references table="repository">
+ <column name="name"/>
+ </references>
+ </foreign-key>
+ <index name="repository_complements_repository_i">
+ <column name="repository"/>
+ </index>
+ <foreign-key name="complement_fk" deferrable="DEFERRED">
+ <column name="complement"/>
+ <references table="repository">
+ <column name="name"/>
+ </references>
+ </foreign-key>
+ </table>
+ <table name="repository_prerequisites" kind="container">
+ <column name="repository" type="TEXT" null="true"/>
+ <column name="prerequisite" type="TEXT" null="true"/>
+ <foreign-key name="repository_fk" on-delete="CASCADE">
+ <column name="repository"/>
+ <references table="repository">
+ <column name="name"/>
+ </references>
+ </foreign-key>
+ <index name="repository_prerequisites_repository_i">
+ <column name="repository"/>
+ </index>
+ <foreign-key name="prerequisite_fk" deferrable="DEFERRED">
+ <column name="prerequisite"/>
+ <references table="repository">
+ <column name="name"/>
+ </references>
+ </foreign-key>
+ </table>
+ <table name="available_package" kind="object">
+ <column name="name" type="TEXT" null="true"/>
+ <column name="version_epoch" type="INTEGER" null="true"/>
+ <column name="version_upstream" type="TEXT" null="true"/>
+ <column name="version_revision" type="INTEGER" null="true"/>
+ <column name="version_original_upstream" type="TEXT" null="true"/>
+ <primary-key>
+ <column name="name"/>
+ <column name="version_epoch"/>
+ <column name="version_upstream"/>
+ <column name="version_revision"/>
+ </primary-key>
+ </table>
+ <table name="available_package_locations" kind="container">
+ <column name="name" type="TEXT" null="true"/>
+ <column name="version_epoch" type="INTEGER" null="true"/>
+ <column name="version_upstream" type="TEXT" null="true"/>
+ <column name="version_revision" type="INTEGER" null="true"/>
+ <column name="repository" type="TEXT" null="true"/>
+ <column name="location" type="TEXT" null="true"/>
+ <foreign-key name="object_id_fk" on-delete="CASCADE">
+ <column name="name"/>
+ <column name="version_epoch"/>
+ <column name="version_upstream"/>
+ <column name="version_revision"/>
+ <references table="available_package">
+ <column name="name"/>
+ <column name="version_epoch"/>
+ <column name="version_upstream"/>
+ <column name="version_revision"/>
+ </references>
+ </foreign-key>
+ <index name="available_package_locations_object_id_i">
+ <column name="name"/>
+ <column name="version_epoch"/>
+ <column name="version_upstream"/>
+ <column name="version_revision"/>
+ </index>
+ <foreign-key name="repository_fk" deferrable="DEFERRED">
+ <column name="repository"/>
+ <references table="repository">
+ <column name="name"/>
+ </references>
+ </foreign-key>
+ </table>
<table name="package" kind="object">
<column name="name" type="TEXT" null="true"/>
<column name="version_epoch" type="INTEGER" null="true"/>
diff --git a/bpkg/pkg-fetch.cxx b/bpkg/pkg-fetch.cxx
index a5d216b..63dd31f 100644
--- a/bpkg/pkg-fetch.cxx
+++ b/bpkg/pkg-fetch.cxx
@@ -4,8 +4,6 @@
#include <bpkg/pkg-fetch>
-#include <memory> // shared_ptr
-
#include <bpkg/manifest>
#include <bpkg/types>
diff --git a/bpkg/pkg-purge.cxx b/bpkg/pkg-purge.cxx
index 7663dea..76101ef 100644
--- a/bpkg/pkg-purge.cxx
+++ b/bpkg/pkg-purge.cxx
@@ -4,8 +4,6 @@
#include <bpkg/pkg-purge>
-#include <memory> // shared_ptr
-
#include <bpkg/types>
#include <bpkg/package>
#include <bpkg/package-odb>
diff --git a/bpkg/pkg-unpack.cxx b/bpkg/pkg-unpack.cxx
index 463531f..ba98b0b 100644
--- a/bpkg/pkg-unpack.cxx
+++ b/bpkg/pkg-unpack.cxx
@@ -4,8 +4,6 @@
#include <bpkg/pkg-unpack>
-#include <memory> // shared_ptr
-
#include <butl/process>
#include <bpkg/manifest>
diff --git a/bpkg/rep-add b/bpkg/rep-add
new file mode 100644
index 0000000..3aff082
--- /dev/null
+++ b/bpkg/rep-add
@@ -0,0 +1,17 @@
+// file : bpkg/rep-add -*- C++ -*-
+// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#ifndef BPKG_REP_ADD
+#define BPKG_REP_ADD
+
+#include <bpkg/types>
+#include <bpkg/rep-add-options>
+
+namespace bpkg
+{
+ void
+ rep_add (const rep_add_options&, cli::scanner& args);
+}
+
+#endif // BPKG_REP_ADD
diff --git a/bpkg/rep-add-options.cli b/bpkg/rep-add-options.cli
new file mode 100644
index 0000000..f798692
--- /dev/null
+++ b/bpkg/rep-add-options.cli
@@ -0,0 +1,35 @@
+// file : bpkg/rep-add-options.cli
+// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+include <bpkg/common-options.cli>;
+
+/*
+"\section=1"
+"\name=bpkg-rep-add"
+
+"\h{SYNOPSIS}
+
+bpkg rep-add [<options>] <rep>"
+
+"\h{DESCRIPTION}
+
+The \cb{rep-add} command adds the specified source repository to the
+configuration. Note that this command doesn't fetch the available
+packages list for the newly added repository. To do that, use the
+\cb{rep-fetch} command.
+"
+*/
+
+namespace bpkg
+{
+ class rep_add_options: common_options
+ {
+ dir_path --directory|-d (".")
+ {
+ "<dir>",
+ "Assume configuration is in <dir> rather than in the current working
+ directory."
+ };
+ };
+}
diff --git a/bpkg/rep-add.cxx b/bpkg/rep-add.cxx
new file mode 100644
index 0000000..803c68d
--- /dev/null
+++ b/bpkg/rep-add.cxx
@@ -0,0 +1,82 @@
+// file : bpkg/rep-add.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <bpkg/rep-add>
+
+#include <stdexcept> // invalid_argument
+
+#include <bpkg/types>
+#include <bpkg/package>
+#include <bpkg/package-odb>
+#include <bpkg/utility>
+#include <bpkg/database>
+#include <bpkg/diagnostics>
+
+using namespace std;
+using namespace butl;
+
+namespace bpkg
+{
+ void
+ rep_add (const rep_add_options& o, cli::scanner& args)
+ {
+ tracer trace ("rep_add");
+
+ dir_path c (o.directory ());
+ level4 ([&]{trace << "configuration: " << c;});
+
+ if (!args.more ())
+ fail << "repository location argument expected" <<
+ info << "run 'bpkg help rep-add' for more information";
+
+ // Figure out the repository location.
+ //
+ const char* s (args.next ());
+ repository_location rl;
+ try
+ {
+ rl = repository_location (s, repository_location ());
+
+ if (rl.relative ()) // Throws if location is empty.
+ rl = repository_location (
+ dir_path (s).complete ().normalize ().string ());
+ }
+ catch (const invalid_argument& e)
+ {
+ fail << "invalid repository location '" << s << "': " << e.what ();
+ }
+
+ const string& rn (rl.canonical_name ());
+
+ // Create the new repository and add is as a complement to the root.
+ //
+ database db (open (c, trace));
+ transaction t (db.begin ());
+
+ // It is possible that this repository is already in the database.
+ // For example, it might be a prerequisite of one of the already
+ // added repository.
+ //
+ shared_ptr<repository> r (db.find<repository> (rl.canonical_name ()));
+
+ if (r == nullptr)
+ {
+ r.reset (new repository (rl));
+ db.persist (r);
+ }
+
+ shared_ptr<repository> root (db.load<repository> (""));
+
+ if (!root->complements.insert (lazy_shared_ptr<repository> (db, r)).second)
+ {
+ fail << rn << " is already a repository of this configuration";
+ }
+
+ db.update (root);
+ t.commit ();
+
+ if (verb)
+ text << "added " << rn;
+ }
+}
diff --git a/bpkg/test.sh b/bpkg/test.sh
index 3ab76df..e25d2bb 100755
--- a/bpkg/test.sh
+++ b/bpkg/test.sh
@@ -71,9 +71,34 @@ test cfg-create --wipe config.cxx=g++-4.9 cxx config.install.root=/tmp/install
stat unknown
##
+## rep-add
+##
+
+fail rep-add # repository location expected
+fail rep-add stable # invalid location
+fail rep-add http:// # invalid location
+
+# relative path
+#
+test rep-add ./1/math/stable
+fail rep-add ./1/../1/math/stable # duplicate
+
+# absolute path
+#
+test rep-add /tmp/1/misc/stable
+fail rep-add /tmp/1/../1/misc/stable # duplicate
+
+# remote URL
+#
+test rep-add http://pkg.example.org/1/testing
+fail rep-add http://www.example.org/1/testing # duplicate
+
+
+##
## pkg-fetch
##
+
# fetch existing archive
#
stat unknown
diff --git a/bpkg/types b/bpkg/types
index 648c0f1..97b9ed9 100644
--- a/bpkg/types
+++ b/bpkg/types
@@ -7,9 +7,11 @@
#include <vector>
#include <string>
-#include <memory> // shared_ptr
+#include <memory> // shared_ptr, unique_ptr
#include <ostream>
+#include <odb/lazy-ptr.hxx>
+
#include <butl/path>
#include <butl/optional>
@@ -22,11 +24,19 @@ namespace bpkg
using strings = std::vector<string>;
using cstrings = std::vector<const char*>;
- using std::shared_ptr;
-
using butl::optional;
using butl::nullopt;
+ // Smart pointers
+ //
+ using std::unique_ptr;
+
+ using std::shared_ptr;
+ using std::weak_ptr;
+
+ using odb::lazy_shared_ptr;
+ using odb::lazy_weak_ptr;
+
// <butl/path>
//
using butl::path;