diff options
-rw-r--r-- | bpkg/bpkg-options.cli | 7 | ||||
-rw-r--r-- | bpkg/bpkg.cxx | 3 | ||||
-rw-r--r-- | bpkg/buildfile | 4 | ||||
-rw-r--r-- | bpkg/cfg-create.cxx | 10 | ||||
-rw-r--r-- | bpkg/database.cxx | 3 | ||||
-rw-r--r-- | bpkg/package | 159 | ||||
-rw-r--r-- | bpkg/package.cxx | 65 | ||||
-rw-r--r-- | bpkg/package.xml | 90 | ||||
-rw-r--r-- | bpkg/pkg-fetch.cxx | 2 | ||||
-rw-r--r-- | bpkg/pkg-purge.cxx | 2 | ||||
-rw-r--r-- | bpkg/pkg-unpack.cxx | 2 | ||||
-rw-r--r-- | bpkg/rep-add | 17 | ||||
-rw-r--r-- | bpkg/rep-add-options.cli | 35 | ||||
-rw-r--r-- | bpkg/rep-add.cxx | 82 | ||||
-rwxr-xr-x | bpkg/test.sh | 25 | ||||
-rw-r--r-- | bpkg/types | 16 |
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 @@ -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; |