From 221f0250fcd7cba4fa4b5e4fd6c0d410eb6e5811 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Mon, 21 Sep 2015 18:08:39 +0200 Subject: Implement rep-add command --- bpkg/bpkg-options.cli | 7 +++ bpkg/bpkg.cxx | 3 + bpkg/buildfile | 4 ++ bpkg/cfg-create.cxx | 10 ++- bpkg/database.cxx | 3 - bpkg/package | 159 +++++++++++++++++++++++++++++++++++++++++++++-- bpkg/package.cxx | 65 +++++++++++++++++++ bpkg/package.xml | 90 +++++++++++++++++++++++++++ bpkg/pkg-fetch.cxx | 2 - bpkg/pkg-purge.cxx | 2 - bpkg/pkg-unpack.cxx | 2 - bpkg/rep-add | 17 +++++ bpkg/rep-add-options.cli | 35 +++++++++++ bpkg/rep-add.cxx | 82 ++++++++++++++++++++++++ bpkg/test.sh | 25 ++++++++ bpkg/types | 16 ++++- 16 files changed, 505 insertions(+), 17 deletions(-) create mode 100644 bpkg/rep-add create mode 100644 bpkg/rep-add-options.cli create mode 100644 bpkg/rep-add.cxx 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 + { + "", + "Add repository to configuration.", + "" + }; + bool rep-create { "[]", 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 #include + +#include #include 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 #include +#include +#include #include #include #include @@ -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 -#include // unique_ptr -#include // move() - #include #include 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 // shared_ptr +#include +#include #include // uint16 #include #include // move() +#include // uint16 #include #include +#include #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 + bool + operator() (const P& x, const P& y) const + { + return x.object_id () < y.object_id (); + } + }; + // path // using optional_string = optional; @@ -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, compare_lazy_ptr>; + using prerequisites_type = + std::set, 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; + 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 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 +#include #include // 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 @@ + + + + + + +
+ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
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 -#include // shared_ptr - #include #include 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 -#include // shared_ptr - #include #include #include 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 -#include // shared_ptr - #include #include 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 +#include + +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 ; + +/* +"\section=1" +"\name=bpkg-rep-add" + +"\h{SYNOPSIS} + +bpkg rep-add [] " + +"\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 (".") + { + "", + "Assume configuration is in 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 + +#include // invalid_argument + +#include +#include +#include +#include +#include +#include + +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 r (db.find (rl.canonical_name ())); + + if (r == nullptr) + { + r.reset (new repository (rl)); + db.persist (r); + } + + shared_ptr root (db.load ("")); + + if (!root->complements.insert (lazy_shared_ptr (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 #include -#include // shared_ptr +#include // shared_ptr, unique_ptr #include +#include + #include #include @@ -22,11 +24,19 @@ namespace bpkg using strings = std::vector; using cstrings = std::vector; - 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; + // // using butl::path; -- cgit v1.1