From 6a68b1fd2161357a5905b875e9d59609a2b829b1 Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Wed, 8 Dec 2021 22:46:50 +0300 Subject: Add support for package dependency and requirement alternatives representation new syntax --- libbpkg/manifest.hxx | 248 +++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 221 insertions(+), 27 deletions(-) (limited to 'libbpkg/manifest.hxx') diff --git a/libbpkg/manifest.hxx b/libbpkg/manifest.hxx index d72a55e..499d64d 100644 --- a/libbpkg/manifest.hxx +++ b/libbpkg/manifest.hxx @@ -8,7 +8,7 @@ #include #include #include -#include // uint16_t +#include // uint*_t #include #include // move() #include // logic_error @@ -455,45 +455,175 @@ namespace bpkg // depends // + // The dependency alternative can be represented in one of the following + // forms. + // + // Single-line form: + // + // ['?' ] [] + // + // = | + // ({ [ ]* } []) + // + // - buildfile evaluation context + // - dependent package configuration variable assignment + // + // If the version constraint is specified after the dependency group, it + // only applies to dependencies without a version constraint. + // + // Multi-line forms: + // + // + // { + // enable + // + // prefer + // { + // + // } + // + // accept + // + // reflect + // { + // + // } + // } + // | + // + // { + // enable + // + // require + // { + // + // } + // + // reflect + // { + // + // } + // } + // + // - buildfile fragment containing dependency packages + // configuration variables assignments + // + // - buildfile evaluation context + // + // - buildfile fragment containing dependency packages + // configuration variables assignments + // + // - buildfile fragment containing dependent package + // configuration variables assignments + // + // The dependency alternative is only considered by bpkg if the enable + // condition evaluates to true. If the enable clause is not specified, then + // it is always considered. + // + // The prefer clause specifies the preferred dependency package + // configuration that may potentially differ from the resulting + // configuration after the preferred/required configurations from all the + // selected dependency alternatives of all the dependent packages are + // "negotiated" by bpkg. The accept clause is used to verify that the + // resulting configuration is still acceptable for the dependent + // package. The accept clause must always be specified if the prefer clause + // is specified. + // + // The require clause specifies the only acceptable dependency packages + // configuration. It is a shortcut for specifying the prefer/accept clauses, + // where the accept condition verifies all the variable values assigned in + // the prefer clause. The require clause and the prefer/accept clause pair + // are optional and are mutually exclusive. + // + // The reflect clause specifies the dependent package configuration that + // should be used if the alternative is selected. + // + // All clauses are optional but at least one of them must be specified. + // class dependency_alternative: public butl::small_vector { public: butl::optional enable; + butl::optional reflect; + butl::optional prefer; + butl::optional accept; + butl::optional require; dependency_alternative () = default; - dependency_alternative (butl::optional e) - : enable (std::move (e)) {} - - // Parse the dependency alternative string representation. + dependency_alternative (butl::optional e, + butl::optional r, + butl::optional p, + butl::optional a, + butl::optional q) + : enable (std::move (e)), + reflect (std::move (r)), + prefer (std::move (p)), + accept (std::move (a)), + require (std::move (q)) {} + + // Return the single-line representation if possible (the prefer and + // require clauses are absent and the reflect clause either absent or + // contains no newlines). // - explicit LIBBPKG_EXPORT - dependency_alternative (const std::string&); - LIBBPKG_EXPORT std::string string () const; + + // Return true if the string() function would return the single-line + // representation. + // + LIBBPKG_EXPORT bool + single_line () const; }; class dependency_alternatives: public butl::small_vector { public: - bool conditional; bool buildtime; std::string comment; dependency_alternatives () = default; - dependency_alternatives (bool d, bool b, std::string c) - : conditional (d), buildtime (b), comment (std::move (c)) {} + dependency_alternatives (bool b, std::string c) + : buildtime (b), comment (std::move (c)) {} - // Parse the dependency alternatives string representation in the - // `[?][*] [ '|' ]* [; ]` form. Throw - // std::invalid_argument if the value is invalid. @@ DEP @@ TMP update. + // Parse the dependency alternatives string representation in the form: + // + // [*] [ '|' ]* [; ] + // + // Where can be single or multi-line (see above). Note also + // that leading `*` and trailing comment can be on separate lines. Throw + // manifest_parsing if the value is invalid. + // + // Use the dependent package name to verify that the reflect clauses in + // the dependency alternative representations refer to the dependent + // package configuration variable. + // + // Optionally, specify the stream name to use when creating the + // manifest_parsing exception. The start line and column arguments can be + // used to align the exception information with a containing stream. This + // is useful when the alternatives representation is a part of some larger + // text (manifest, etc). + // + // Note that semicolons inside alternatives must be escaped with the + // backslash (not to be treated as the start of a comment). Backslashes at + // the end of buildfile fragment lines need to also be escaped, if + // dependency alternatives representation comes from the manifest file + // (since trailing backslashes in manifest lines has special semantics). // explicit LIBBPKG_EXPORT - dependency_alternatives (const std::string&); + dependency_alternatives (const std::string&, + const package_name& dependent, + const std::string& name = std::string (), + std::uint64_t line = 1, + std::uint64_t column = 1); LIBBPKG_EXPORT std::string string () const; + + // Return true if there is a conditional alternative in the list. + // + LIBBPKG_EXPORT bool + conditional () const; }; inline std::ostream& @@ -504,45 +634,109 @@ namespace bpkg // requires // + // The requirement alternative string representation is similar to that of + // the dependency alternative with the following differences: + // + // - The requirement id (with or without version) can mean anything (but + // must still be a valid package name). + // + // - Only the enable and reflect clauses are permitted (reflect is allowed + // for potential future support of recognized requirement alternatives, + // for example, C++ standard). + // + // - The simplified representation syntax, where the comment carries the + // main information and thus is mandatory, is also supported (see + // requirement_alternatives for details). For example: + // + // requires: ; X11 libs. + // requires: ? ($windows) ; Only 64-bit. + // requires: ? ; Only 64-bit if on Windows. + // requires: x86_64 ? ; Only if on Windows. + // class requirement_alternative: public butl::small_vector { public: butl::optional enable; + butl::optional reflect; requirement_alternative () = default; - requirement_alternative (butl::optional e) - : enable (std::move (e)) {} + requirement_alternative (butl::optional e, + butl::optional r) + : enable (std::move (e)), reflect (std::move (r)) {} - // Parse the requirement alternative string representation. + // Return the single-line representation if possible (the reflect clause + // either absent or contains no newlines). // - explicit LIBBPKG_EXPORT - requirement_alternative (const std::string&); - LIBBPKG_EXPORT std::string string () const; + + // Return true if the string() function would return the single-line + // representation. + // + LIBBPKG_EXPORT bool + single_line () const; + + // Return true if this is a single requirement with an empty id or an + // empty enable condition. + // + bool + simple () const + { + return size () == 1 && (back ().empty () || (enable && enable->empty ())); + } }; class requirement_alternatives: public butl::small_vector { public: - bool conditional; bool buildtime; std::string comment; requirement_alternatives () = default; - requirement_alternatives (bool d, bool b, std::string c) - : conditional (d), buildtime (b), comment (std::move (c)) {} + requirement_alternatives (bool b, std::string c) + : buildtime (b), comment (std::move (c)) {} // Parse the requirement alternatives string representation in the - // `[?] [ [ '|' ]*] [; ]` form. Throw - // std::invalid_argument if the value is invalid. @@ DEP @@ TMP update. + // following forms: + // + // [*] [ '|' ]* [; ] + // [*] [] [? []] ; + // + // Parsing the second form ends up with a single alternative with a single + // potentially empty requirement id, potentially with an enable condition + // with potentially empty value (see examples above). + // + // Throw manifest_parsing if the value is invalid. + // + // Optionally, specify the stream name to use when creating the + // manifest_parsing exception. The start line and column arguments can be + // used to align the exception information with a containing stream. This + // is useful when the alternatives representation is a part of some larger + // text (manifest, etc). // explicit LIBBPKG_EXPORT - requirement_alternatives (const std::string&); + requirement_alternatives (const std::string&, + const package_name& dependent, + const std::string& name = std::string (), + std::uint64_t line = 1, + std::uint64_t column = 1); LIBBPKG_EXPORT std::string string () const; + + // Return true if there is a conditional alternative in the list. + // + LIBBPKG_EXPORT bool + conditional () const; + + // Return true if this is a single simple requirement alternative. + // + bool + simple () const + { + return size () == 1 && back ().simple (); + } }; inline std::ostream& -- cgit v1.1