From 3f348ad66e9836e32e11086fa9b22a92d094d6a8 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Thu, 8 Oct 2015 12:15:03 +0200 Subject: Test dependency satisfaction, make various fixes --- bpkg/build-options.cli | 5 + bpkg/build.cxx | 121 ++++++--- bpkg/rep-info-options.cli | 3 +- bpkg/test.sh | 280 ++++++++++++++++++++- tests/repository/1/satisfy/libbar-1.0.0.tar.gz | Bin 0 -> 356 bytes tests/repository/1/satisfy/libbar-1.1.0.tar.gz | Bin 0 -> 360 bytes tests/repository/1/satisfy/libbaz-1.0.0.tar.gz | Bin 0 -> 359 bytes tests/repository/1/satisfy/libbaz-1.1.0.tar.gz | Bin 0 -> 363 bytes tests/repository/1/satisfy/libfoo-0.0.0.tar.gz | Bin 0 -> 348 bytes tests/repository/1/satisfy/libfoo-1.0.0.tar.gz | Bin 0 -> 348 bytes tests/repository/1/satisfy/libfoo-1.1.0.tar.gz | Bin 0 -> 349 bytes .../1/satisfy/libfoo-1.1.0/build/bootstrap.build | 2 + tests/repository/1/satisfy/libfoo-1.1.0/buildfile | 1 + tests/repository/1/satisfy/libfoo-1.1.0/manifest | 7 + tests/repository/1/satisfy/libfoo-1.2.0.tar.gz | Bin 0 -> 350 bytes tests/repository/1/satisfy/libfox-1.0.0.tar.gz | Bin 0 -> 349 bytes tests/repository/1/satisfy/repositories | 1 + tests/repository/1/satisfy/t1/libfoo-1.0.0.tar.gz | 1 + tests/repository/1/satisfy/t1/repositories | 1 + tests/repository/1/satisfy/t2/libbar-1.0.0.tar.gz | 1 + tests/repository/1/satisfy/t2/libfoo-1.0.0.tar.gz | 1 + tests/repository/1/satisfy/t2/repositories | 1 + tests/repository/1/satisfy/t3/libbaz-1.0.0.tar.gz | 1 + tests/repository/1/satisfy/t3/libfox-1.0.0.tar.gz | 1 + tests/repository/1/satisfy/t3/repositories | 4 + tests/repository/1/satisfy/t4a/libfoo-1.1.0.tar.gz | 1 + tests/repository/1/satisfy/t4a/repositories | 1 + tests/repository/1/satisfy/t4b/libbar-1.1.0.tar.gz | 1 + tests/repository/1/satisfy/t4b/repositories | 3 + tests/repository/1/satisfy/t4c/libbaz-1.1.0.tar.gz | 1 + tests/repository/1/satisfy/t4c/libfoo-1.0.0.tar.gz | 1 + tests/repository/1/satisfy/t4c/repositories | 3 + 32 files changed, 409 insertions(+), 33 deletions(-) create mode 100644 tests/repository/1/satisfy/libbar-1.0.0.tar.gz create mode 100644 tests/repository/1/satisfy/libbar-1.1.0.tar.gz create mode 100644 tests/repository/1/satisfy/libbaz-1.0.0.tar.gz create mode 100644 tests/repository/1/satisfy/libbaz-1.1.0.tar.gz create mode 100644 tests/repository/1/satisfy/libfoo-0.0.0.tar.gz create mode 100644 tests/repository/1/satisfy/libfoo-1.0.0.tar.gz create mode 100644 tests/repository/1/satisfy/libfoo-1.1.0.tar.gz create mode 100644 tests/repository/1/satisfy/libfoo-1.1.0/build/bootstrap.build create mode 100644 tests/repository/1/satisfy/libfoo-1.1.0/buildfile create mode 100644 tests/repository/1/satisfy/libfoo-1.1.0/manifest create mode 100644 tests/repository/1/satisfy/libfoo-1.2.0.tar.gz create mode 100644 tests/repository/1/satisfy/libfox-1.0.0.tar.gz create mode 100644 tests/repository/1/satisfy/repositories create mode 120000 tests/repository/1/satisfy/t1/libfoo-1.0.0.tar.gz create mode 120000 tests/repository/1/satisfy/t1/repositories create mode 120000 tests/repository/1/satisfy/t2/libbar-1.0.0.tar.gz create mode 120000 tests/repository/1/satisfy/t2/libfoo-1.0.0.tar.gz create mode 120000 tests/repository/1/satisfy/t2/repositories create mode 120000 tests/repository/1/satisfy/t3/libbaz-1.0.0.tar.gz create mode 120000 tests/repository/1/satisfy/t3/libfox-1.0.0.tar.gz create mode 100644 tests/repository/1/satisfy/t3/repositories create mode 120000 tests/repository/1/satisfy/t4a/libfoo-1.1.0.tar.gz create mode 120000 tests/repository/1/satisfy/t4a/repositories create mode 120000 tests/repository/1/satisfy/t4b/libbar-1.1.0.tar.gz create mode 100644 tests/repository/1/satisfy/t4b/repositories create mode 120000 tests/repository/1/satisfy/t4c/libbaz-1.1.0.tar.gz create mode 120000 tests/repository/1/satisfy/t4c/libfoo-1.0.0.tar.gz create mode 100644 tests/repository/1/satisfy/t4c/repositories diff --git a/bpkg/build-options.cli b/bpkg/build-options.cli index 9782f95..1a8c035 100644 --- a/bpkg/build-options.cli +++ b/bpkg/build-options.cli @@ -29,5 +29,10 @@ namespace bpkg { class build_options: configuration_options { + bool --print-only|-p + { + "Print to \cb{STDOUT} what would be done without actually doing + anything." + }; }; } diff --git a/bpkg/build.cxx b/bpkg/build.cxx index a518000..b91609d 100644 --- a/bpkg/build.cxx +++ b/bpkg/build.cxx @@ -7,6 +7,7 @@ #include #include #include // make_move_iterator() +#include // cout #include // reference_wrapper #include // reverse_iterate() @@ -88,6 +89,7 @@ namespace bpkg // std::pair, shared_ptr> make_available (const common_options& options, + const dir_path& cd, database& db, const shared_ptr& sp) { @@ -102,12 +104,15 @@ namespace bpkg // The package is in at least fetched state, which means we should // be able to get its manifest. // - shared_ptr ap (make_shared ( + const optional& a (sp->archive); + const optional& d (sp->src_root); + + package_manifest m ( sp->state == package_state::fetched - ? pkg_verify (options, *sp->archive) - : pkg_verify (*sp->src_root))); + ? pkg_verify (options, a->absolute () ? *a : cd / *a) + : pkg_verify (d->absolute () ? *d : cd / *d)); - return make_pair (move (ap), move (ar)); + return make_pair (make_shared (move (m)), move (ar)); } // A "dependency-ordered" list of packages and their prerequisites. @@ -191,9 +196,12 @@ namespace bpkg // bool collect (const common_options& options, + const dir_path& cd, database& db, satisfied_package&& pkg) { + tracer trace ("collect"); + auto i (map_.find (pkg.available->id.name)); // If we already have an entry for this package name, then we @@ -201,6 +209,8 @@ namespace bpkg // if (i != map_.end ()) { + const string& n (i->first); + // At the end we want p1 to point to the object that we keep // and p2 to the object whose constraints we should copy. // @@ -244,11 +254,10 @@ namespace bpkg // if (auto c1 = test (p2, p1)) { - const string& n (i->first); const string& d1 (c1->dependent); const string& d2 (c2->dependent); - fail << "unable to satisfy package " << n << " constraints" << + fail << "unable to satisfy constraints on package " << n << info << d1 << " depends on (" << n << " " << c1->value << ")" << info << d2 << " depends on (" << n << " " << c2->value << ")" << info << "available " << n << " " << p1->available->version << @@ -259,6 +268,9 @@ namespace bpkg else swap (p1, p2); } + + level4 ([&]{trace << "pick " << n << " " << p1->available->version + << " over " << p2->available->version;}); } // See if we are replacing the object. If not, then we don't @@ -285,6 +297,8 @@ namespace bpkg { string n (pkg.available->id.name); // Note: copy; see emplace() below. + level4 ([&]{trace << "add " << n << " " << pkg.available->version;}); + // This is the first time we are adding this package name to the // map. If it is already selected, then we need to make sure that // packages that already depend on it (called dependents) are ok @@ -401,7 +415,7 @@ namespace bpkg info << "use 'pkg-purge --force' to remove"; if (satisfies (dsp->version, d.constraint)) - rp = make_available (options, db, dsp); + rp = make_available (options, cd, db, dsp); else // Remember that we may be forcing up/downgrade; we will deal // with it below. @@ -420,17 +434,21 @@ namespace bpkg // (i.e., it wasn't already there) and we are forcing an upgrade, // then warn. Downgrade -- outright refuse. // - if (collect (options, db, move (dp)) && force) + if (collect (options, cd, db, move (dp)) && force) { const version& v (rp.first->version); - if (v > dsp->version) - warn << "package " << name << " dependency " << d << " is forcing " - << "upgrade of " << d.name << " to " << v; - else - fail << "package " << name << " dependency " << d << " is forcing " - << "downgrade of " << d.name << " to " << v << - info << "explicitly request downgraded version to continue"; + bool u (v > dsp->version); + bool c (d.constraint); + diag_record dr; + + (u ? dr << warn : dr << fail) + << "package " << name << " dependency on " + << (c ? "(" : "") << d << (c ? ")" : "") << " is forcing " + << (u ? "up" : "down") << "grade of " << d.name << " to " << v; + + if (!u) + dr << info << "explicitly specify version downgrade to continue"; } } @@ -438,10 +456,11 @@ namespace bpkg } // Order the previously-collected package with the specified name - // returning its positions. + // returning its positions. If reorder is true, then reorder this + // package to be considered as "early" as possible. // iterator - order (const string& name) + order (const string& name, bool reorder = true) { // Every package that we order should have already be collected. // @@ -450,10 +469,16 @@ namespace bpkg // If this package is already in the list, then that would also // mean all its prerequisites are in the list and we can just - // return its position. + // return its position. Unless we want it reordered. // - if (mi->second.position != list_.end ()) - return mi->second.position; + iterator& pos (mi->second.position); + if (pos != list_.end ()) + { + if (reorder) + list_.erase (pos); + else + return pos; + } const satisfied_package& p (mi->second.package); @@ -471,12 +496,18 @@ namespace bpkg p.selected->version != p.available->version || p.selected->state != package_state::configured) { - for (const dependency_alternatives& da: p.available->dependencies) + // We are iterating in reverse so that when we iterate over + // the dependency list (also in reverse), prerequisites will + // be built in the order that is as close to the manifest as + // possible. + // + for (const dependency_alternatives& da: + reverse_iterate (p.available->dependencies)) { assert (!da.conditional && da.size () == 1); // @@ TODO const dependency& d (da.front ()); - iterator j (order (d.name)); + iterator j (order (d.name, false)); // Figure out if j is before i, in which case set i to j. The // goal here is to find the position of our first prerequisite. @@ -487,7 +518,7 @@ namespace bpkg } } - return mi->second.position = list_.insert (i, p); + return pos = list_.insert (i, p); } private: @@ -630,6 +661,8 @@ namespace bpkg fail << "unable to build broken package " << n << info << "use 'pkg-purge --force' to remove"; + bool found (true); + // If the user asked for a specific version, then that's what // we ought to be building. // @@ -646,7 +679,8 @@ namespace bpkg if (sp != nullptr && sp->version == v) break; // Derive ap from sp below. - fail << "unknown package " << n << " " << v; + found = false; + break; } } // @@ -667,12 +701,30 @@ namespace bpkg else { if (sp == nullptr) - fail << "unknown package " << n; + found = false; - // Derive ap from sp below. + // Otherwise, derive ap from sp below. } } + if (!found) + { + diag_record dr; + + dr << fail << "unknown package " << n; + if (!v.empty ()) + dr << " " << v; + + // Let's help the new user out here a bit. + // + if (db.query_value () == 0) + dr << info << "configuration " << c << " has no repositories" + << info << "use 'bpkg rep-add' to add a repository"; + else if (db.query_value () == 0) + dr << info << "configuration " << c << " has no available packages" + << info << "use 'bpkg rep-fetch' to fetch available packages list"; + } + // If the available_package object is still NULL, then it means // we need to get one corresponding to the selected package. // @@ -680,7 +732,7 @@ namespace bpkg { assert (sp != nullptr); - auto rp (make_available (o, db, sp)); + auto rp (make_available (o, c, db, sp)); ap = rp.first; ar = rp.second; // Could be NULL (orphan). } @@ -695,10 +747,10 @@ namespace bpkg // if (!v.empty ()) p.constraints.emplace_back ( - "", + "command line", dependency_constraint {comparison::eq, v}); - pkgs.collect (o, db, move (p)); + pkgs.collect (o, c, db, move (p)); names.push_back (n); } @@ -731,7 +783,16 @@ namespace bpkg else a = sp->version < ap->version ? "upgrade" : "downgrade"; - text << a << " " << ap->id.name << " " << ap->version; + if (o.print_only ()) + cout << a << " " << ap->id.name << " " << ap->version << endl; + else + text << a << " " << ap->id.name << " " << ap->version; + } + + if (o.print_only ()) + { + t.commit (); + return; } t.commit (); diff --git a/bpkg/rep-info-options.cli b/bpkg/rep-info-options.cli index 2d3aa33..6af380f 100644 --- a/bpkg/rep-info-options.cli +++ b/bpkg/rep-info-options.cli @@ -18,7 +18,8 @@ The \cb{rep-info} command prints information about the specified repository. By default it print the repository's name and location as the first line followed by the list of prerequisite/complement repositories and the list of available packages. This default behavior, however, can be altered in -various ways using options listed below." +various ways using options listed below. Note that the information is +written to \cb{STDOUT}, not \cb{STDERR}." */ namespace bpkg diff --git a/bpkg/test.sh b/bpkg/test.sh index 4b2db6f..815fc89 100755 --- a/bpkg/test.sh +++ b/bpkg/test.sh @@ -29,7 +29,14 @@ function test () ops="-d $cfg" fi - $bpkg $cmd $ops $* + if [ -t 0 ]; then + $bpkg $cmd $ops $* + else + # There is no way to get the exit code in process substiution + # so spoil the output. + # + diff -u - <($bpkg $cmd $ops $* || echo "") + fi if [ $? -ne 0 ]; then error "failed: $bpkg $cmd $ops $*" @@ -84,6 +91,8 @@ function gone () fi } +if false; then + ## ## rep-create ## @@ -584,7 +593,7 @@ test pkg-disfigure $pkg test pkg-purge $pkg ## -## Scenarios. +## Low-level command scenarios. ## # build package from remote repository @@ -599,3 +608,270 @@ test pkg-update $pkg test pkg-clean $pkg test pkg-disfigure $pkg test pkg-purge $pkg + +## +## High-level commands. +## + +fi + +## +## build +## + +# 1 +# +test rep-create ../tests/repository/1/satisfy/t1 +test cfg-create --wipe + +fail build -p # package name expected +fail build -p libfoo # unknown package +fail build -p libfoo/1.0.0 # unknown package +test build -p ../tests/repository/1/satisfy/libfoo-1.1.0.tar.gz <= 1.1.0) +# +test rep-create ../tests/repository/1/satisfy/t4a +test rep-create ../tests/repository/1/satisfy/t4b +test rep-create ../tests/repository/1/satisfy/t4c +test cfg-create --wipe +test rep-add ../tests/repository/1/satisfy/t4c +test rep-fetch + +test build -p libbaz <