From e253cd97c6a8d55a5be19731d58769f4663ab2d1 Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Wed, 6 Oct 2021 22:16:46 +0300 Subject: Add support for applying backward compatibility workarounds to packages.manifest file generated by rep-create --- bpkg/fetch-pkg.cxx | 20 ++++++++++++++++---- bpkg/rep-create.cli | 11 +++++++++++ bpkg/rep-create.cxx | 21 ++++++++++++++++++++- bpkg/rep-fetch.cxx | 3 ++- bpkg/types-parsers.cxx | 29 +++++++++++++++++++++++++++++ bpkg/types-parsers.hxx | 15 +++++++++++++++ doc/manual.cli | 45 +++++++++++++++++++++++++++++++++++++++------ 7 files changed, 132 insertions(+), 12 deletions(-) diff --git a/bpkg/fetch-pkg.cxx b/bpkg/fetch-pkg.cxx index 6d9e921..64abb43 100644 --- a/bpkg/fetch-pkg.cxx +++ b/bpkg/fetch-pkg.cxx @@ -168,8 +168,14 @@ namespace bpkg pkg_repository_manifests pkg_fetch_repositories (const dir_path& d, bool iu) { - return fetch_manifest ( - nullptr, d / repositories_file, iu).first; + pkg_repository_manifests r ( + fetch_manifest ( + nullptr, d / repositories_file, iu).first); + + if (r.empty ()) + r.emplace_back (repository_manifest ()); // Add the base repository. + + return r; } pair @@ -184,9 +190,15 @@ namespace bpkg path& f (*u.path); f /= repositories_file; - return rl.remote () + pair r ( + rl.remote () ? fetch_manifest (o, u, iu) - : fetch_manifest (&o, f, iu); + : fetch_manifest (&o, f, iu)); + + if (r.first.empty ()) + r.first.emplace_back (repository_manifest ()); // Add the base repository. + + return r; } pkg_package_manifests diff --git a/bpkg/rep-create.cli b/bpkg/rep-create.cli index be4cc42..9d062ec 100644 --- a/bpkg/rep-create.cli +++ b/bpkg/rep-create.cli @@ -36,6 +36,17 @@ namespace bpkg "Ignore unknown manifest entries." } + butl::standard_version --min-bpkg-version + { + "", + "Apply backward compatibility workarounds to the generated + \cb{packages.manifest} file so that it can be consumed by \cb{bpkg} + versions greater or equal to the specified version. If unspecified, + then the \cb{min-bpkg-version} value from the \cb{repositories.manifest} + file is used, if present. If the manifest value is not specified + either, then no backward compatibility workarounds are applied." + } + string --key { "", diff --git a/bpkg/rep-create.cxx b/bpkg/rep-create.cxx index d820836..8de8c22 100644 --- a/bpkg/rep-create.cxx +++ b/bpkg/rep-create.cxx @@ -162,6 +162,15 @@ namespace bpkg }) << " prerequisite repository(s)";}); + optional rmv ( + rms.header && rms.header->min_bpkg_version + ? rms.header->min_bpkg_version + : nullopt); + + optional opv (o.min_bpkg_version_specified () + ? o.min_bpkg_version () + : optional ()); + // While we could have serialized as we go along, the order of // packages will be pretty much random and not reproducible. By // collecting all the manifests in a map we get a sorted list. @@ -182,6 +191,16 @@ namespace bpkg manifests.emplace_back (move (m)); } + // Issue a warning if --min-bpkg-version option and the repositories + // manifest's min-bpkg-version value are both specified and don't match. + // Let's issue it after the added repositories are printed to stdout, so + // that it doesn't go unnoticed. + // + if (opv && rmv && *opv != *rmv) + warn << "--min-bpkg-version option value " << *opv << " differs from " + << "minimum bpkg version " << *rmv << " specified in " + << d / repositories_file; + // Serialize packages manifest, optionally generate the signature manifest. // path p (d / packages_file); @@ -197,7 +216,7 @@ namespace bpkg ofdstream ofs (p, fdopen_mode::binary); manifest_serializer s (ofs, p.string ()); - manifests.serialize (s); + manifests.serialize (s, opv ? opv : rmv); ofs.close (); } diff --git a/bpkg/rep-fetch.cxx b/bpkg/rep-fetch.cxx index 73c9058..cefa799 100644 --- a/bpkg/rep-fetch.cxx +++ b/bpkg/rep-fetch.cxx @@ -161,7 +161,8 @@ namespace bpkg M r; if (exists (f)) r = parse_manifest (f, iu, rl, fragment); - else + + if (r.empty ()) r.emplace_back (repository_manifest ()); // Add the base repository. return r; diff --git a/bpkg/types-parsers.cxx b/bpkg/types-parsers.cxx index d5ddb28..97ebafe 100644 --- a/bpkg/types-parsers.cxx +++ b/bpkg/types-parsers.cxx @@ -92,6 +92,35 @@ namespace bpkg } } + void parser:: + parse (butl::standard_version& x, bool& xs, scanner& s) + { + using butl::standard_version; + + xs = true; + + const char* o (s.next ()); + + if (!s.more ()) + throw missing_value (o); + + const char* v (s.next ()); + + try + { + // Note that we allow all kinds of versions, so that the caller can + // restrict them as they wish after the parsing. + // + x = standard_version (v, + standard_version::allow_earliest | + standard_version::allow_stub); + } + catch (const invalid_argument& e) + { + throw invalid_value (o, v, e.what ()); + } + } + void parser:: parse (auth& x, bool& xs, scanner& s) { diff --git a/bpkg/types-parsers.hxx b/bpkg/types-parsers.hxx index d687156..007d754 100644 --- a/bpkg/types-parsers.hxx +++ b/bpkg/types-parsers.hxx @@ -7,6 +7,8 @@ #ifndef BPKG_TYPES_PARSERS_HXX #define BPKG_TYPES_PARSERS_HXX +#include + #include #include @@ -59,6 +61,19 @@ namespace bpkg }; template <> + struct parser + { + static void + parse (butl::standard_version&, bool&, scanner&); + + static void + merge (butl::standard_version& b, const butl::standard_version& a) + { + b = a; + } + }; + + template <> struct parser { static void diff --git a/doc/manual.cli b/doc/manual.cli index cbca17d..86fe17f 100644 --- a/doc/manual.cli +++ b/doc/manual.cli @@ -1772,12 +1772,12 @@ The repository fragment id this repository belongs to. terminology and semantics. The repository list manifest (the \c{repositories.manifest} file found in the -repository root directory) describes the repository. It is a sequence of -repository manifests consisting of the base repository manifest (that is, the -manifest for the repository that is being described) as well as manifests for -its prerequisite and complement repositories. The individual repository -manifests can appear in any order and the base repository manifest can be -omitted. +repository root directory) describes the repository. It starts with an +optional header manifest optionally followed by a sequence of repository +manifests consisting of the base repository manifest (that is, the manifest +for the repository that is being described) as well as manifests for its +prerequisite and complement repositories. The individual repository manifests +can appear in any order and the base repository manifest can be omitted. The \c{fragment} values can only be present in a merged \c{repositories.manifest} file for a multi-fragment repository. @@ -1789,6 +1789,8 @@ repository could look like this: # math/testing # : 1 +min-bpkg-version: 0.14.0 +: email: math-pkg@example.org summary: Math package repository : @@ -1814,6 +1816,37 @@ Then the completement's location would be: https://pkg.example.org/1/math/stable \ +The header manifest synopsis is presented next followed by the detailed +description of each value in subsequent sections. + +\ +[min-bpkg-version]: +[compression]: +\ + +\h2#manifest-repository-list-header-min-bpkg-version|\c{min-bpkg-version}| + +\ +[min-bpkg-version]: +\ + +The earliest version of \cb{bpkg} that is compatible with this repository. +Note that if specified, it must be the first value in the header. + + +\h2#manifest-repository-list-header-compression|\c{compression}| + +\ +[compression]: + + = [ ]* +\ + +Available compressed variants of the \c{packages.manifest} file. The format is +a space-separated list of the compression methods. The \c{none} method means +no compression. Absent \c{compression} value is equivalent to specifying it +with the \c{none} value. + \h#manifest-signature-pkg|Signature Manifest for \cb{pkg} Repositories| -- cgit v1.1