From 209a5d12d829adcf7bff48d64c436a8c0ff4b30e Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Thu, 12 Jan 2023 21:51:27 +0300 Subject: Add *-name, *-version, and *-to-downstream-version package manifest values --- libbpkg/manifest.cxx | 175 ++++++++++++++++++++++++++++++++++++++-------- libbpkg/manifest.hxx | 76 +++++++++++++++----- tests/manifest/driver.cxx | 8 +-- tests/manifest/testscript | 97 +++++++++++++++++++++++++ 4 files changed, 304 insertions(+), 52 deletions(-) diff --git a/libbpkg/manifest.cxx b/libbpkg/manifest.cxx index 57684df..162de5a 100644 --- a/libbpkg/manifest.cxx +++ b/libbpkg/manifest.cxx @@ -3240,6 +3240,25 @@ namespace bpkg return r; } + // distribution_name_value + // + optional distribution_name_value:: + distribution (const string& s) const + { + size_t sn (s.size ()); + size_t nn (name.size ()); + + if (nn > sn && name.compare (nn - sn, sn, s) == 0) + { + size_t p (name.find ('-')); + + if (p == nn - sn) + return string (name, 0, p); + } + + return nullopt; + } + // pkg_package_manifest // static build_class_expr @@ -3346,7 +3365,7 @@ namespace bpkg const function& next, const function& translate, bool iu, - bool cd, + bool cv, package_manifest_flags fl, package_manifest& m) { @@ -3431,6 +3450,43 @@ namespace bpkg } }; + // Note: the n argument is the distribution name length. + // + auto parse_distribution = [&bad_name, &bad_value] (string&& nm, size_t n, + string&& vl) + { + size_t p (nm.find ('-')); + + // Distribution-related manifest value name always has a dash-starting + // suffix (-name, etc). + // + assert (p != string::npos); + + if (p < n) + bad_name ("distribution name '" + string (nm, 0, n) + "' contains '-'"); + + if (vl.empty ()) + bad_value ("empty package distribution value"); + + return distribution_name_value (move (nm), move (vl)); + }; + + auto add_distribution = [&m, &bad_name] (distribution_name_value&& nv, + bool unique) + { + vector& dvs (m.distribution_values); + + if (unique && + find_if (dvs.begin (), dvs.end (), + [&nv] (const distribution_name_value& dnv) + {return dnv.name == nv.name;}) != dvs.end ()) + { + bad_name ("package distribution value redefinition"); + } + + dvs.push_back (move (nv)); + }; + auto flag = [fl] (package_manifest_flags f) { return (fl & f) != package_manifest_flags::none; @@ -3886,8 +3942,8 @@ namespace bpkg m.build_constraints.push_back ( parse_build_constraint (nv, true /* exclusion */, name)); } - else if ((n.size () > 13 && - n.compare (n.size () - 13, 13, "-build-config") == 0)) + else if (n.size () > 13 && + n.compare (n.size () - 13, 13, "-build-config") == 0) { auto vc (parser::split_comment (v)); @@ -3901,7 +3957,7 @@ namespace bpkg bc.arguments = move (vc.first); bc.comment = move (vc.second); } - else if ((n.size () > 7 && n.compare (n.size () - 7, 7, "-builds") == 0)) + else if (n.size () > 7 && n.compare (n.size () - 7, 7, "-builds") == 0) { n.resize (n.size () - 7); @@ -3910,8 +3966,8 @@ namespace bpkg bc.builds.push_back ( parse_build_class_expr (nv, bc.builds.empty (), name)); } - else if ((n.size () > 14 && - n.compare (n.size () - 14, 14, "-build-include") == 0)) + else if (n.size () > 14 && + n.compare (n.size () - 14, 14, "-build-include") == 0) { n.resize (n.size () - 14); @@ -3920,8 +3976,8 @@ namespace bpkg bc.constraints.push_back ( parse_build_constraint (nv, false /* exclusion */, name)); } - else if ((n.size () > 14 && - n.compare (n.size () - 14, 14, "-build-exclude") == 0)) + else if (n.size () > 14 && + n.compare (n.size () - 14, 14, "-build-exclude") == 0) { n.resize (n.size () - 14); @@ -4001,6 +4057,41 @@ namespace bpkg bad_value ("path with build or build2 extension expected"); } + else if (n.size () > 5 && n.compare (n.size () - 5, 5, "-name") == 0) + { + add_distribution ( + parse_distribution (move (n), n.size () - 5, move (v)), + false /* unique */); + } + // Note: must precede the check for the "-version" suffix. + // + else if (n.size () > 22 && + n.compare (n.size () - 22, 22, "-to-downstream-version") == 0) + { + add_distribution ( + parse_distribution (move (n), n.size () - 22, move (v)), + false /* unique */); + } + // Note: must follow the check for "upstream-version". + // + else if (n.size () > 8 && n.compare (n.size () - 8, 8, "-version") == 0) + { + // If the value is forbidden then throw, but only after the name is + // validated. Thus, check for that before we move the value from. + // + bool bad (v == "$" && + flag (package_manifest_flags::forbid_incomplete_values)); + + // Can throw. + // + distribution_name_value d ( + parse_distribution (move (n), n.size () - 8, move (v))); + + if (bad) + bad_value ("$ not allowed"); + + add_distribution (move (d), true /* unique */); + } else if (n == "location") { if (flag (package_manifest_flags::forbid_location)) @@ -4152,7 +4243,7 @@ namespace bpkg // Now, when the version manifest value is parsed, we can parse the // dependencies and complete their constraints, if requested. // - auto complete_constraint = [&m, cd, &flag] (auto&& dep) + auto complete_constraint = [&m, cv, &flag] (auto&& dep) { if (dep.constraint) try @@ -4160,12 +4251,12 @@ namespace bpkg version_constraint& vc (*dep.constraint); if (!vc.complete () && - flag (package_manifest_flags::forbid_incomplete_dependencies)) + flag (package_manifest_flags::forbid_incomplete_values)) throw invalid_argument ("$ not allowed"); // Complete the constraint. // - if (cd) + if (cv) vc = vc.effective (m.version); } catch (const invalid_argument& e) @@ -4238,6 +4329,29 @@ namespace bpkg } } + // Now, when the version manifest value is parsed, we complete the + // -version values, if requested. + // + if (cv) + { + for (distribution_name_value& nv: m.distribution_values) + { + const string& n (nv.name); + string& v (nv.value); + + if (v == "$" && + (n.size () > 8 && n.compare (n.size () - 8, 8, "-version") == 0) && + n.find ('-') == n.size () - 8) + { + v = version (default_epoch (m.version), + move (m.version.upstream), + nullopt /* release */, + nullopt /* revision */, + 0 /* iteration */).string (); + } + } + } + if (!m.location && flag (package_manifest_flags::require_location)) bad_name ("no package location specified"); @@ -4277,7 +4391,7 @@ namespace bpkg name_value nv, const function& tf, bool iu, - bool cd, + bool cv, package_manifest_flags fl, package_manifest& m) { @@ -4297,7 +4411,7 @@ namespace bpkg [&p] () {return p.next ();}, tf, iu, - cd, + cv, fl, m); } @@ -4309,12 +4423,12 @@ namespace bpkg p, move (nv), iu, - false /* complete_depends */, - package_manifest_flags::forbid_file | - package_manifest_flags::forbid_fragment | - package_manifest_flags::forbid_incomplete_dependencies | - package_manifest_flags::require_location | - package_manifest_flags::require_description_type | + false /* complete_values */, + package_manifest_flags::forbid_file | + package_manifest_flags::forbid_fragment | + package_manifest_flags::forbid_incomplete_values | + package_manifest_flags::require_location | + package_manifest_flags::require_description_type | package_manifest_flags::require_bootstrap_build); } @@ -4324,10 +4438,10 @@ namespace bpkg package_manifest (manifest_parser& p, const function& tf, bool iu, - bool cd, + bool cv, package_manifest_flags fl) { - parse_package_manifest (p, p.next (), tf, iu, cd, fl, *this); + parse_package_manifest (p, p.next (), tf, iu, cv, fl, *this); // Make sure this is the end. // @@ -4340,9 +4454,9 @@ namespace bpkg package_manifest:: package_manifest (manifest_parser& p, bool iu, - bool cd, + bool cv, package_manifest_flags fl) - : package_manifest (p, function (), iu, cd, fl) + : package_manifest (p, function (), iu, cv, fl) { } @@ -4351,7 +4465,7 @@ namespace bpkg vector&& vs, const function& tf, bool iu, - bool cd, + bool cv, package_manifest_flags fl) { auto i (vs.begin ()); @@ -4366,7 +4480,7 @@ namespace bpkg }, tf, iu, - cd, + cv, fl, *this); } @@ -4375,13 +4489,13 @@ namespace bpkg package_manifest (const string& name, vector&& vs, bool iu, - bool cd, + bool cv, package_manifest_flags fl) : package_manifest (name, move (vs), function (), iu, - cd, + cv, fl) { } @@ -4390,11 +4504,11 @@ namespace bpkg package_manifest (manifest_parser& p, name_value nv, bool iu, - bool cd, + bool cv, package_manifest_flags fl) { parse_package_manifest ( - p, move (nv), function (), iu, cd, fl, *this); + p, move (nv), function (), iu, cv, fl, *this); } optional package_manifest:: @@ -5033,6 +5147,9 @@ namespace bpkg for (const path& f: m.buildfile_paths) s.next ("build-file", f.posix_string () + (an ? ".build2" : ".build")); + for (const distribution_name_value& nv: m.distribution_values) + s.next (nv.name, nv.value); + if (m.location) s.next ("location", m.location->posix_string ()); diff --git a/libbpkg/manifest.hxx b/libbpkg/manifest.hxx index 0fd5feb..6779881 100644 --- a/libbpkg/manifest.hxx +++ b/libbpkg/manifest.hxx @@ -811,18 +811,18 @@ namespace bpkg // enum class package_manifest_flags: std::uint16_t { - none = 0x000, - - forbid_file = 0x001, // Forbid *-file manifest values. - forbid_location = 0x002, - forbid_sha256sum = 0x004, - forbid_fragment = 0x008, - forbid_incomplete_dependencies = 0x010, - - require_location = 0x020, - require_sha256sum = 0x040, - require_description_type = 0x080, - require_bootstrap_build = 0x100 + none = 0x000, + + forbid_file = 0x001, // Forbid *-file manifest values. + forbid_location = 0x002, + forbid_sha256sum = 0x004, + forbid_fragment = 0x008, + forbid_incomplete_values = 0x010, // depends, -version, etc. + + require_location = 0x020, + require_sha256sum = 0x040, + require_description_type = 0x080, + require_bootstrap_build = 0x100 }; inline package_manifest_flags @@ -1130,6 +1130,40 @@ namespace bpkg content (std::move (c)) {} }; + // Binary distribution package information. + // + // The name is prefixed with the id, typically name/version + // pair in the [_] form. For example: + // + // debian-name + // debian_10-name + // ubuntu_20.04-name + // + // Currently recognized names: + // + // -name + // -version + // -to-downstream-version + // + // Note that the value format/semantics can be distribution-specific. + // + struct LIBBPKG_EXPORT distribution_name_value + { + std::string name; + std::string value; + + distribution_name_value () = default; + distribution_name_value (std::string n, std::string v) + : name (std::move (n)), + value (std::move (v)) {} + + // Return the name's component if the name has the + // specified suffix, which is assumed to be valid (-name, etc). + // + butl::optional + distribution (const std::string& suffix) const; + }; + class LIBBPKG_EXPORT package_manifest { public: @@ -1189,6 +1223,10 @@ namespace bpkg std::vector buildfiles; // Buildfiles content. std::vector buildfile_paths; + // The binary distributions package information. + // + std::vector distribution_values; + // The following values are only valid in the manifest list (and only for // certain repository types). // @@ -1220,7 +1258,7 @@ namespace bpkg // package_manifest (butl::manifest_parser&, bool ignore_unknown = false, - bool complete_dependencies = true, + bool complete_values = true, package_manifest_flags = package_manifest_flags::forbid_location | package_manifest_flags::forbid_sha256sum | @@ -1242,7 +1280,7 @@ namespace bpkg package_manifest (butl::manifest_parser&, const std::function&, bool ignore_unknown = false, - bool complete_depends = true, + bool complete_values = true, package_manifest_flags = package_manifest_flags::forbid_location | package_manifest_flags::forbid_sha256sum | @@ -1257,7 +1295,7 @@ namespace bpkg package_manifest (const std::string& name, std::vector&&, bool ignore_unknown = false, - bool complete_dependencies = true, + bool complete_values = true, package_manifest_flags = package_manifest_flags::forbid_location | package_manifest_flags::forbid_sha256sum | @@ -1267,7 +1305,7 @@ namespace bpkg std::vector&&, const std::function&, bool ignore_unknown = false, - bool complete_depends = true, + bool complete_values = true, package_manifest_flags = package_manifest_flags::forbid_location | package_manifest_flags::forbid_sha256sum | @@ -1278,7 +1316,7 @@ namespace bpkg package_manifest (butl::manifest_parser&, butl::manifest_name_value start, bool ignore_unknown, - bool complete_depends, + bool complete_values, package_manifest_flags); // Override manifest values with the specified. Throw manifest_parsing if @@ -1382,9 +1420,9 @@ namespace bpkg inline package_manifest pkg_package_manifest (butl::manifest_parser& p, bool ignore_unknown = false, - bool complete_depends = true) + bool complete_values = true) { - return package_manifest (p, ignore_unknown, complete_depends); + return package_manifest (p, ignore_unknown, complete_values); } LIBBPKG_EXPORT package_manifest diff --git a/tests/manifest/driver.cxx b/tests/manifest/driver.cxx index c0d8693..fb40f7e 100644 --- a/tests/manifest/driver.cxx +++ b/tests/manifest/driver.cxx @@ -40,7 +40,7 @@ using namespace bpkg; // In the second form read and parse the package manifest from stdin and // serialize it to stdout. // -// -c complete the dependency constraints +// -c complete the incomplete values (depends, -version, etc) // -i ignore unknown // // Note: the above options should go after -p on the command line. @@ -74,7 +74,7 @@ main (int argc, char* argv[]) { if (mode == "-p") { - bool complete_dependencies (false); + bool complete_values (false); bool ignore_unknown (false); bool long_lines (false); @@ -83,7 +83,7 @@ main (int argc, char* argv[]) string o (argv[i]); if (o == "-c") - complete_dependencies = true; + complete_values = true; else if (o == "-i") ignore_unknown = true; else if (o == "-l") @@ -114,7 +114,7 @@ main (int argc, char* argv[]) } }, ignore_unknown, - complete_dependencies).serialize (s); + complete_values).serialize (s); } else if (mode == "-ec") { diff --git a/tests/manifest/testscript b/tests/manifest/testscript index 336b288..f4a5282 100644 --- a/tests/manifest/testscript +++ b/tests/manifest/testscript @@ -606,6 +606,103 @@ } } + : distribution + : + { + : incomplete + : + { + $* <>EOF + : 1 + name: libcrypto + version: 1.1.1+18 + upstream-version: 1.1.1n + summary: C library providing general cryptography and X.509 support + license: OpenSSL + debian-name: libssl1.1 libssl-dev + debian-version: 1.1.1n + debian-to-downstream-version: /([^.])\.([^.])\.([^.])n/\1.\2.\3+18/ + debian-to-downstream-version: /([^.])\.([^.])\.([^.])o/\1.\2.\3+19/ + debian-to-downstream-version: /([^.])\.([^.])\.([^.])p/\1.\2.\3+20/ + fedora-name: openssl-libs openssl-devel + fedora-version: $ + EOF + } + + : complete + : + { + $* -c <>EOO + : 1 + name: libcrypto + version: +2-1.1.1-a.1+2 + upstream-version: 1.1.1n + summary: C library providing general cryptography and X.509 support + license: OpenSSL + fedora-name: openssl-libs openssl-devel + fedora-version: $ + fedora-to-downstream-version: $ + EOI + : 1 + name: libcrypto + version: +2-1.1.1-a.1+2 + upstream-version: 1.1.1n + summary: C library providing general cryptography and X.509 support + license: OpenSSL + fedora-name: openssl-libs openssl-devel + fedora-version: 1.1.1 + fedora-to-downstream-version: $ + EOO + } + + : multiple-names + : + { + $* <>EOO + : 1 + name: libcrypto + version: 1.1.1+18 + upstream-version: 1.1.1n + summary: C library providing general cryptography and X.509 support + license: OpenSSL + debian-name: libcurl4 libcurl4-doc libcurl4-openssl-dev + debian-name: libcurl3-gnutls libcurl4-gnutls-dev + EOO + } + + : dash-in-name + : + { + $* <>EOE != 0 + : 1 + name: libcrypto + version: 1.1.1+18 + upstream-version: 1.1.1n + summary: C library providing general cryptography and X.509 support + license: OpenSSL + de-bian-name: libssl1.1 libssl-dev + EOI + stdin:7:1: error: distribution name 'de-bian' contains '-' + EOE + } + + : empty-value + : + { + $* <>EOE != 0 + : 1 + name: libcrypto + version: 1.1.1+18 + upstream-version: 1.1.1n + summary: C library providing general cryptography and X.509 support + license: OpenSSL + debian-name: + EOI + stdin:7:13: error: empty package distribution value + EOE + } + } + : depends : { -- cgit v1.1