From c97dba3a4f2af33091112a347e181a5a2edc9914 Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Mon, 20 Feb 2023 16:09:02 +0300 Subject: Add type and language package manifest values Also add manifest.ixx. --- libbpkg/manifest.cxx | 148 ++++++++--------- libbpkg/manifest.hxx | 319 ++++++++++++------------------------ libbpkg/manifest.ixx | 402 ++++++++++++++++++++++++++++++++++++++++++++++ tests/manifest/testscript | 172 ++++++++++++++++++++ 4 files changed, 742 insertions(+), 299 deletions(-) create mode 100644 libbpkg/manifest.ixx diff --git a/libbpkg/manifest.cxx b/libbpkg/manifest.cxx index 162de5a..8db4026 100644 --- a/libbpkg/manifest.cxx +++ b/libbpkg/manifest.cxx @@ -9,10 +9,10 @@ #include #include #include // strtoull() -#include // strncmp(), strcmp(), strchr() +#include // strncmp(), strcmp(), strchr(), strcspn() #include // move() #include // uint*_t -#include // find(), find_if_not(), find_first_of(), replace() +#include // find(), find_if(), find_first_of(), replace() #include // invalid_argument #include // remove_reference @@ -1112,20 +1112,6 @@ namespace bpkg } } - std::string dependency:: - string () const - { - std::string r (name.string ()); - - if (constraint) - { - r += ' '; - r += constraint->string (); - } - - return r; - } - // dependency_alternative // string dependency_alternative:: @@ -1226,14 +1212,6 @@ namespace bpkg return r; } - bool dependency_alternative:: - single_line () const - { - return !prefer && - !require && - (!reflect || reflect->find ('\n') == string::npos); - } - // dependency_alternatives // class dependency_alternatives_lexer: public char_scanner @@ -2419,18 +2397,6 @@ namespace bpkg return serializer::merge_comment (r, comment); } - bool dependency_alternatives:: - conditional () const - { - for (const dependency_alternative& da: *this) - { - if (da.enable) - return true; - } - - return false; - } - // requirement_alternative // string requirement_alternative:: @@ -2515,12 +2481,6 @@ namespace bpkg return r; } - bool requirement_alternative:: - single_line () const - { - return !reflect || reflect->find ('\n') == string::npos; - } - // requirement_alternatives // requirement_alternatives:: @@ -2620,18 +2580,6 @@ namespace bpkg return serializer::merge_comment (r, comment); } - bool requirement_alternatives:: - conditional () const - { - for (const requirement_alternative& ra: *this) - { - if (ra.enable) - return true; - } - - return false; - } - // build_class_term // build_class_term:: @@ -3240,25 +3188,6 @@ 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 @@ -3709,6 +3638,64 @@ namespace bpkg upstream_version = move (nv); } + else if (n == "type") + { + if (m.type) + bad_name ("package type redefinition"); + + // Strip the type extra information, if present. + // + size_t p (v.find (',')); + if (p != string::npos) + { + v.resize (p); + trim_right (v); + } + + if (v.empty ()) + bad_value ("empty package type"); + + m.type = move (v); + } + else if (n == "language") + { + // Strip the language extra information, if present. + // + size_t p (v.find (',')); + if (p != string::npos) + v.resize (p); + + // Determine the language impl flag. + // + bool impl (false); + p = v.find ('='); + if (p != string::npos) + { + string s (trim (string (v, p + 1))); + if (s != "impl") + bad_value (!s.empty () + ? "unexpected '" + s + "' value after '='" + : "expected 'impl' after '='"); + + impl = true; + + v.resize (p); + } + + // Finally, validate and add the language. + // + trim_right (v); + + if (v.empty ()) + bad_value ("empty package language"); + + if (find_if (m.languages.begin (), m.languages.end (), + [&v] (const language& l) {return l.name == v;}) != + m.languages.end ()) + bad_value ("duplicate package language"); + + m.languages.emplace_back (move (v), impl); + } else if (n == "project") { if (m.project) @@ -4452,15 +4439,6 @@ namespace bpkg } package_manifest:: - package_manifest (manifest_parser& p, - bool iu, - bool cv, - package_manifest_flags fl) - : package_manifest (p, function (), iu, cv, fl) - { - } - - package_manifest:: package_manifest (const string& name, vector&& vs, const function& tf, @@ -4977,6 +4955,12 @@ namespace bpkg if (m.upstream_version) s.next ("upstream-version", *m.upstream_version); + if (m.type) + s.next ("type", *m.type); + + for (const language& l: m.languages) + s.next ("language", !l.impl ? l.name : l.name + "=impl"); + if (m.project) s.next ("project", m.project->string ()); diff --git a/libbpkg/manifest.hxx b/libbpkg/manifest.hxx index 6779881..1d45edf 100644 --- a/libbpkg/manifest.hxx +++ b/libbpkg/manifest.hxx @@ -11,7 +11,6 @@ #include // uint*_t #include #include // move() -#include // logic_error #include #include @@ -111,23 +110,12 @@ namespace bpkg std::string string (bool ignore_revision = false, bool ignore_iteration = false) const; - bool - operator< (const version& v) const noexcept {return compare (v) < 0;} - - bool - operator> (const version& v) const noexcept {return compare (v) > 0;} - - bool - operator== (const version& v) const noexcept {return compare (v) == 0;} - - bool - operator<= (const version& v) const noexcept {return compare (v) <= 0;} - - bool - operator>= (const version& v) const noexcept {return compare (v) >= 0;} - - bool - operator!= (const version& v) const noexcept {return compare (v) != 0;} + bool operator< (const version& v) const noexcept; + bool operator> (const version& v) const noexcept; + bool operator== (const version& v) const noexcept; + bool operator<= (const version& v) const noexcept; + bool operator>= (const version& v) const noexcept; + bool operator!= (const version& v) const noexcept; // If the revision is ignored, then the iteration is also ignored, // regardless of the argument (see above for details). @@ -135,28 +123,7 @@ namespace bpkg int compare (const version& v, bool ignore_revision = false, - bool ignore_iteration = false) const noexcept - { - if (epoch != v.epoch) - return epoch < v.epoch ? -1 : 1; - - if (int c = canonical_upstream.compare (v.canonical_upstream)) - return c; - - if (int c = canonical_release.compare (v.canonical_release)) - return c; - - if (!ignore_revision) - { - if (revision != v.revision) - return revision < v.revision ? -1 : 1; - - if (!ignore_iteration && iteration != v.iteration) - return iteration < v.iteration ? -1 : 1; - } - - return 0; - } + bool ignore_iteration = false) const noexcept; bool empty () const noexcept @@ -207,33 +174,10 @@ namespace bpkg return os << (v.empty () ? "" : v.string ()); } - inline version::flags - operator&= (version::flags& x, version::flags y) - { - return x = static_cast ( - static_cast (x) & - static_cast (y)); - } - - inline version::flags - operator|= (version::flags& x, version::flags y) - { - return x = static_cast ( - static_cast (x) | - static_cast (y)); - } - - inline version::flags - operator& (version::flags x, version::flags y) - { - return x &= y; - } - - inline version::flags - operator| (version::flags x, version::flags y) - { - return x |= y; - } + version::flags operator& (version::flags, version::flags); + version::flags operator| (version::flags, version::flags); + version::flags operator&= (version::flags&, version::flags); + version::flags operator|= (version::flags&, version::flags); // priority // @@ -251,6 +195,17 @@ namespace bpkg operator value_type () const {return value;} }; + // language + // + struct language + { + std::string name; + bool impl; // True if implementation-only. + + language (): impl (false) {} + language (std::string n, bool i): name (std::move (n)), impl (i) {} + }; + // description // description-file // change @@ -415,17 +370,10 @@ namespace bpkg } inline bool - operator== (const version_constraint& x, const version_constraint& y) - { - return x.min_version == y.min_version && x.max_version == y.max_version && - x.min_open == y.min_open && x.max_open == y.max_open; - } + operator== (const version_constraint&, const version_constraint&); inline bool - operator!= (const version_constraint& x, const version_constraint& y) - { - return !(x == y); - } + operator!= (const version_constraint&, const version_constraint&); struct LIBBPKG_EXPORT dependency { @@ -447,11 +395,8 @@ namespace bpkg string () const; }; - inline std::ostream& - operator<< (std::ostream& os, const dependency& d) - { - return os << d.string (); - } + std::ostream& + operator<< (std::ostream&, const dependency&); // depends // @@ -593,8 +538,13 @@ namespace bpkg // Return true if the string() function would return the single-line // representation. // - LIBBPKG_EXPORT bool - single_line () const; + bool + single_line () const + { + return !prefer && + !require && + (!reflect || reflect->find ('\n') == std::string::npos); + } }; inline std::ostream& @@ -650,7 +600,7 @@ namespace bpkg // Return true if there is a conditional alternative in the list. // - LIBBPKG_EXPORT bool + bool conditional () const; }; @@ -701,8 +651,11 @@ namespace bpkg // Return true if the string() function would return the single-line // representation. // - LIBBPKG_EXPORT bool - single_line () const; + bool + single_line () const + { + return !reflect || reflect->find ('\n') == std::string::npos; + } // Return true if this is a single requirement with an empty id or an // empty enable condition. @@ -755,7 +708,7 @@ namespace bpkg // Return true if there is a conditional alternative in the list. // - LIBBPKG_EXPORT bool + bool conditional () const; // Return true if this is a single simple requirement alternative. @@ -825,33 +778,17 @@ namespace bpkg require_bootstrap_build = 0x100 }; - inline package_manifest_flags - operator&= (package_manifest_flags& x, package_manifest_flags y) - { - return x = static_cast ( - static_cast (x) & - static_cast (y)); - } + package_manifest_flags + operator& (package_manifest_flags, package_manifest_flags); - inline package_manifest_flags - operator|= (package_manifest_flags& x, package_manifest_flags y) - { - return x = static_cast ( - static_cast (x) | - static_cast (y)); - } + package_manifest_flags + operator| (package_manifest_flags, package_manifest_flags); - inline package_manifest_flags - operator& (package_manifest_flags x, package_manifest_flags y) - { - return x &= y; - } + package_manifest_flags + operator&= (package_manifest_flags&, package_manifest_flags); - inline package_manifest_flags - operator| (package_manifest_flags x, package_manifest_flags y) - { - return x |= y; - } + package_manifest_flags + operator|= (package_manifest_flags&, package_manifest_flags); // Target build configuration class term. // @@ -973,12 +910,7 @@ namespace bpkg bool& result) const; bool - match (const strings& cs, const build_class_inheritance_map& bs) const - { - bool r (false); - match (cs, bs, r); - return r; - } + match (const strings&, const build_class_inheritance_map&) const; }; inline std::ostream& @@ -1147,7 +1079,7 @@ namespace bpkg // // Note that the value format/semantics can be distribution-specific. // - struct LIBBPKG_EXPORT distribution_name_value + struct distribution_name_value { std::string name; std::string value; @@ -1174,11 +1106,13 @@ namespace bpkg package_name name; version_type version; butl::optional upstream_version; + butl::optional type; // [, ...] + butl::small_vector languages; // [=impl][, ...] butl::optional project; butl::optional priority; std::string summary; - butl::small_vector license_alternatives; + butl::small_vector topics; butl::small_vector keywords; butl::optional description; @@ -1234,6 +1168,35 @@ namespace bpkg butl::optional sha256sum; butl::optional fragment; + // Translate optional type to either `exe`, `lib`, or `other`. + // + // Specifically, if type is present but is not one of the recognized + // names, then return `other`. If type is absent and the package name + // starts with the `lib` prefix, then return `lib`. Otherwise, return + // `exe`. + // + std::string + effective_type () const; + + static std::string + effective_type (const butl::optional&, const package_name&); + + // Translate the potentially empty list of languages to a non-empty one. + // + // Specifically, if the list of languages is not empty, then return it as + // is. Otherwise, if the package name has an extension (as in, say, + // libbutl.bash), then return it as the language. Otherwise, return `cc` + // (unspecified c-common language). + // + butl::small_vector + effective_languages () const; + + static butl::small_vector + effective_languages (const butl::small_vector&, + const package_name&); + + // Return effective project name. + // const package_name& effective_project () const noexcept {return project ? *project : name;} @@ -1417,13 +1380,10 @@ namespace bpkg // Create individual package manifest. // - inline package_manifest + package_manifest pkg_package_manifest (butl::manifest_parser& p, bool ignore_unknown = false, - bool complete_values = true) - { - return package_manifest (p, ignore_unknown, complete_values); - } + bool complete_values = true); LIBBPKG_EXPORT package_manifest dir_package_manifest (butl::manifest_parser&, bool ignore_unknown = false); @@ -1729,9 +1689,8 @@ namespace bpkg repository_type, const repository_location& base); - repository_location (const repository_location& l, - const repository_location& base) - : repository_location (l.url (), l.type (), base) {} + repository_location (const repository_location&, + const repository_location& base); // Note that relative locations have no canonical name. Canonical name of // an empty location is the empty name. @@ -1749,59 +1708,22 @@ namespace bpkg empty () const noexcept {return url_.empty ();} bool - local () const - { - if (empty ()) - throw std::logic_error ("empty location"); - - return url_.scheme == repository_protocol::file; - } + local () const; bool - remote () const - { - return !local (); - } + remote () const; bool - absolute () const - { - if (empty ()) - throw std::logic_error ("empty location"); - - // Note that in remote locations path is always relative. - // - return url_.path->absolute (); - } + absolute () const; bool - relative () const - { - return local () && url_.path->relative (); - } + relative () const; repository_type - type () const - { - if (empty ()) - throw std::logic_error ("empty location"); - - return type_; - } + type () const; repository_basis - basis () const - { - switch (type ()) - { - case repository_type::pkg: return repository_basis::archive; - case repository_type::dir: return repository_basis::directory; - case repository_type::git: return repository_basis::version_control; - } - - assert (false); // Can't be here. - return repository_basis::archive; - } + basis () const; // Note that the URL of an empty location is empty. // @@ -1815,69 +1737,30 @@ namespace bpkg // "directories" it always contains the trailing slash. // const butl::path& - path () const - { - if (empty ()) - throw std::logic_error ("empty location"); - - return *url_.path; - } + path () const; const std::string& - host () const - { - if (local ()) - throw std::logic_error ("local location"); - - return url_.authority->host; - } + host () const; // Value 0 indicated that no port was specified explicitly. // std::uint16_t - port () const - { - if (local ()) - throw std::logic_error ("local location"); - - return url_.authority->port; - } + port () const; repository_protocol - proto () const - { - if (empty ()) - throw std::logic_error ("empty location"); - - return url_.scheme; - } + proto () const; const butl::optional& - fragment () const - { - if (relative ()) - throw std::logic_error ("relative filesystem path"); - - return url_.fragment; - } + fragment () const; bool - archive_based () const - { - return basis () == repository_basis::archive; - } + archive_based () const; bool - directory_based () const - { - return basis () == repository_basis::directory; - } + directory_based () const; bool - version_control_based () const - { - return basis () == repository_basis::version_control; - } + version_control_based () const; // Return an untyped URL if the correct type can be guessed just from // the URL. Otherwise, return the typed URL. @@ -2174,4 +2057,6 @@ namespace bpkg } } +#include + #endif // LIBBPKG_MANIFEST_HXX diff --git a/libbpkg/manifest.ixx b/libbpkg/manifest.ixx new file mode 100644 index 0000000..d6eb4a6 --- /dev/null +++ b/libbpkg/manifest.ixx @@ -0,0 +1,402 @@ +// file : libbpkg/manifest.ixx -*- C++ -*- +// license : MIT; see accompanying LICENSE file + +#include // logic_error + +namespace bpkg +{ + // version + // + inline int version:: + compare (const version& v, bool ir, bool ii) const noexcept + { + if (epoch != v.epoch) + return epoch < v.epoch ? -1 : 1; + + if (int c = canonical_upstream.compare (v.canonical_upstream)) + return c; + + if (int c = canonical_release.compare (v.canonical_release)) + return c; + + if (!ir) + { + if (revision != v.revision) + return revision < v.revision ? -1 : 1; + + if (!ii && iteration != v.iteration) + return iteration < v.iteration ? -1 : 1; + } + + return 0; + } + + inline bool version:: + operator< (const version& v) const noexcept + { + return compare (v) < 0; + } + + inline bool version:: + operator> (const version& v) const noexcept + { + return compare (v) > 0; + } + + inline bool version:: + operator== (const version& v) const noexcept + { + return compare (v) == 0; + } + + inline bool version:: + operator<= (const version& v) const noexcept + { + return compare (v) <= 0; + } + + inline bool version:: + operator>= (const version& v) const noexcept + { + return compare (v) >= 0; + } + + inline bool version:: + operator!= (const version& v) const noexcept + { + return compare (v) != 0; + } + + inline version::flags + operator&= (version::flags& x, version::flags y) + { + return x = static_cast ( + static_cast (x) & + static_cast (y)); + } + + inline version::flags + operator|= (version::flags& x, version::flags y) + { + return x = static_cast ( + static_cast (x) | + static_cast (y)); + } + + inline version::flags + operator& (version::flags x, version::flags y) + { + return x &= y; + } + + inline version::flags + operator| (version::flags x, version::flags y) + { + return x |= y; + } + + // version_constraint + // + inline bool + operator== (const version_constraint& x, const version_constraint& y) + { + return x.min_version == y.min_version && x.max_version == y.max_version && + x.min_open == y.min_open && x.max_open == y.max_open; + } + + inline bool + operator!= (const version_constraint& x, const version_constraint& y) + { + return !(x == y); + } + + // dependency + // + inline std::string dependency:: + string () const + { + std::string r (name.string ()); + + if (constraint) + { + r += ' '; + r += constraint->string (); + } + + return r; + } + + inline std::ostream& + operator<< (std::ostream& os, const dependency& d) + { + return os << d.string (); + } + + // dependency_alternatives + // + inline bool dependency_alternatives:: + conditional () const + { + for (const dependency_alternative& da: *this) + { + if (da.enable) + return true; + } + + return false; + } + + // requirement_alternatives + // + inline bool requirement_alternatives:: + conditional () const + { + for (const requirement_alternative& ra: *this) + { + if (ra.enable) + return true; + } + + return false; + } + + // distribution_name_value + // + inline butl::optional distribution_name_value:: + distribution (const std::string& s) const + { + using namespace std; + + 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 butl::nullopt; + } + + // package_manifest_flags + // + inline package_manifest_flags + operator&= (package_manifest_flags& x, package_manifest_flags y) + { + return x = static_cast ( + static_cast (x) & + static_cast (y)); + } + + inline package_manifest_flags + operator|= (package_manifest_flags& x, package_manifest_flags y) + { + return x = static_cast ( + static_cast (x) | + static_cast (y)); + } + + inline package_manifest_flags + operator& (package_manifest_flags x, package_manifest_flags y) + { + return x &= y; + } + + inline package_manifest_flags + operator| (package_manifest_flags x, package_manifest_flags y) + { + return x |= y; + } + + // build_class_expr + // + inline bool build_class_expr:: + match (const strings& cs, const build_class_inheritance_map& bs) const + { + bool r (false); + match (cs, bs, r); + return r; + } + + // package_manifest + // + inline package_manifest:: + package_manifest (butl::manifest_parser& p, + bool iu, + bool cv, + package_manifest_flags fl) + : package_manifest (p, std::function (), iu, cv, fl) + { + } + + inline package_manifest + pkg_package_manifest (butl::manifest_parser& p, bool iu, bool cvs) + { + return package_manifest (p, iu, cvs); + } + + inline std::string package_manifest:: + effective_type (const butl::optional& t, const package_name& n) + { + if (t) + return *t == "exe" || *t == "lib" ? *t : "other"; + + const std::string& s (n.string ()); + return s.size () > 3 && s.compare (0, 3, "lib") == 0 ? "lib" : "exe"; + } + + inline std::string package_manifest:: + effective_type () const + { + return effective_type (type, name); + } + + inline butl::small_vector package_manifest:: + effective_languages (const butl::small_vector& ls, + const package_name& n) + { + if (!ls.empty ()) + return ls; + + std::string ext (n.extension ()); + return butl::small_vector ( + 1, + language (!ext.empty () ? move (ext) : "cc", false /* impl */)); + } + + inline butl::small_vector package_manifest:: + effective_languages () const + { + return effective_languages (languages, name); + } + + // repository_location + // + inline repository_type repository_location:: + type () const + { + if (empty ()) + throw std::logic_error ("empty location"); + + return type_; + } + + inline repository_location:: + repository_location (const repository_location& l, + const repository_location& base) + : repository_location (l.url (), l.type (), base) + { + } + + inline bool repository_location:: + local () const + { + if (empty ()) + throw std::logic_error ("empty location"); + + return url_.scheme == repository_protocol::file; + } + + inline bool repository_location:: + remote () const + { + return !local (); + } + + inline bool repository_location:: + absolute () const + { + if (empty ()) + throw std::logic_error ("empty location"); + + // Note that in remote locations path is always relative. + // + return url_.path->absolute (); + } + + inline bool repository_location:: + relative () const + { + return local () && url_.path->relative (); + } + + inline repository_basis repository_location:: + basis () const + { + switch (type ()) + { + case repository_type::pkg: return repository_basis::archive; + case repository_type::dir: return repository_basis::directory; + case repository_type::git: return repository_basis::version_control; + } + + assert (false); // Can't be here. + return repository_basis::archive; + } + + inline bool repository_location:: + archive_based () const + { + return basis () == repository_basis::archive; + } + + inline bool repository_location:: + directory_based () const + { + return basis () == repository_basis::directory; + } + + inline bool repository_location:: + version_control_based () const + { + return basis () == repository_basis::version_control; + } + + inline const butl::path& repository_location:: + path () const + { + if (empty ()) + throw std::logic_error ("empty location"); + + return *url_.path; + } + + inline const std::string& repository_location:: + host () const + { + if (local ()) + throw std::logic_error ("local location"); + + return url_.authority->host; + } + + inline std::uint16_t repository_location:: + port () const + { + if (local ()) + throw std::logic_error ("local location"); + + return url_.authority->port; + } + + inline repository_protocol repository_location:: + proto () const + { + if (empty ()) + throw std::logic_error ("empty location"); + + return url_.scheme; + } + + inline const butl::optional& repository_location:: + fragment () const + { + if (relative ()) + throw std::logic_error ("relative filesystem path"); + + return url_.fragment; + } +} diff --git a/tests/manifest/testscript b/tests/manifest/testscript index f4a5282..6f714ad 100644 --- a/tests/manifest/testscript +++ b/tests/manifest/testscript @@ -102,6 +102,176 @@ EOE } + : type + : + { + : valid + : + $* <>EOF + : 1 + name: libfoo + version: 2.0.0 + type: lib + summary: Modern C++ parser + license: LGPLv2 + EOF + + : extras + : + $* <>EOO + : 1 + name: foo + version: 2.0.0 + type: bash, something extra + summary: Modern C++ parser + license: LGPLv2 + EOI + : 1 + name: foo + version: 2.0.0 + type: bash + summary: Modern C++ parser + license: LGPLv2 + EOO + + : duplicate + : + $* <'stdin:5:1: error: package type redefinition' != 0 + : 1 + name: libfoo + version: 2.0.0 + type: lib + type: exe + summary: Modern C++ parser + license: LGPLv2 + EOI + + : empty + : + $* <'stdin:4:6: error: empty package type' != 0 + : 1 + name: libfoo + version: 2.0.0 + type: + summary: Modern C++ parser + license: LGPLv2 + EOI + + : empty-extras + : + $* <'stdin:4:7: error: empty package type' != 0 + : 1 + name: libfoo + version: 2.0.0 + type: , extras + summary: Modern C++ parser + license: LGPLv2 + EOI + } + + : language + : + { + : valid + : + $* <>EOF + : 1 + name: libfoo + version: 2.0.0 + language: c++ + language: c=impl + summary: Modern C++ parser + license: LGPLv2 + EOF + + : extras + : + $* <>EOO + : 1 + name: foo + version: 2.0.0 + language: c++, something extra + language: c=impl, something extra + summary: Modern C++ parser + license: LGPLv2 + EOI + : 1 + name: foo + version: 2.0.0 + language: c++ + language: c=impl + summary: Modern C++ parser + license: LGPLv2 + EOO + + : empty + : + $* <'stdin:4:10: error: empty package language' != 0 + : 1 + name: libfoo + version: 2.0.0 + language: + summary: Modern C++ parser + license: LGPLv2 + EOI + + : empty-extras + : + $* <'stdin:4:11: error: empty package language' != 0 + : 1 + name: libfoo + version: 2.0.0 + language: , extras + summary: Modern C++ parser + license: LGPLv2 + EOI + + : empty-impl + : + $* <'stdin:4:11: error: empty package language' != 0 + : 1 + name: libfoo + version: 2.0.0 + language: =impl + summary: Modern C++ parser + license: LGPLv2 + EOI + + : invalid-value + : + $* <"stdin:4:11: error: unexpected 'imp' value after '='" != 0 + : 1 + name: libfoo + version: 2.0.0 + language: c++=imp + summary: Modern C++ parser + license: LGPLv2 + EOI + + : empty-value + : + $* <"stdin:4:11: error: expected 'impl' after '='" != 0 + : 1 + name: libfoo + version: 2.0.0 + language: c++= + summary: Modern C++ parser + license: LGPLv2 + EOI + + : duplicate + : + $* <"stdin:5:11: error: duplicate package language" != 0 + : 1 + name: libfoo + version: 2.0.0 + language: c++=impl + language: c++ + summary: Modern C++ parser + license: LGPLv2 + EOI + } + : license : { @@ -3897,6 +4067,8 @@ : name: libfoo version: 1.2.3+2 + type: lib + language: c++ project: foo priority: high; Due to critical bug fix. summary: Modern XML parser -- cgit v1.1