diff options
Diffstat (limited to 'libbpkg/manifest.hxx')
-rw-r--r-- | libbpkg/manifest.hxx | 888 |
1 files changed, 579 insertions, 309 deletions
diff --git a/libbpkg/manifest.hxx b/libbpkg/manifest.hxx index 499d64d..feb3b96 100644 --- a/libbpkg/manifest.hxx +++ b/libbpkg/manifest.hxx @@ -10,8 +10,7 @@ #include <cassert> #include <cstdint> // uint*_t #include <ostream> -#include <utility> // move() -#include <stdexcept> // logic_error +#include <utility> // move(), pair #include <functional> #include <libbutl/url.hxx> @@ -102,7 +101,7 @@ namespace bpkg version (version&&) = default; version (const version&) = default; - version& operator= (version&&); + version& operator= (version&&) noexcept; version& operator= (const version&); // If the revision is ignored, then the iteration (that semantically @@ -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 () ? "<empty-version>" : v.string ()); } - inline version::flags - operator&= (version::flags& x, version::flags y) - { - return x = static_cast<version::flags> ( - static_cast<std::uint16_t> (x) & - static_cast<std::uint16_t> (y)); - } - - inline version::flags - operator|= (version::flags& x, version::flags y) - { - return x = static_cast<version::flags> ( - static_cast<std::uint16_t> (x) | - static_cast<std::uint16_t> (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,11 +195,17 @@ namespace bpkg operator value_type () const {return value;} }; - // description - // description-file - // change - // change-file + // 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) {} + }; + class LIBBPKG_EXPORT text_file { public: @@ -281,14 +231,80 @@ namespace bpkg text_file (path_type p, std::string c) : file (true), path (std::move (p)), comment (std::move (c)) {} - text_file (text_file&&); + text_file (text_file&&) noexcept; text_file (const text_file&); - text_file& operator= (text_file&&); + text_file& operator= (text_file&&) noexcept; text_file& operator= (const text_file&); ~text_file (); }; + enum class text_type + { + plain, + common_mark, + github_mark + }; + + LIBBPKG_EXPORT std::string + to_string (text_type); + + // Throw std::invalid_argument if the argument is not a well-formed text + // type. Otherwise, return nullopt for an unknown text variant. + // + LIBBPKG_EXPORT butl::optional<text_type> + to_text_type (const std::string&); + + inline std::ostream& + operator<< (std::ostream& os, text_type t) + { + return os << to_string (t); + } + + // description + // description-file + // description-type + // package-description + // package-description-file + // package-description-type + // change + // change-file + // change-type + // + class LIBBPKG_EXPORT typed_text_file: public text_file + { + public: + butl::optional<std::string> type; + + // File text constructor. + // + explicit + typed_text_file (std::string s = "", + butl::optional<std::string> t = butl::nullopt) + : text_file (std::move (s)), type (std::move (t)) {} + + // File reference constructor. + // + typed_text_file (path_type p, + std::string c, + butl::optional<std::string> t = butl::nullopt) + : text_file (std::move (p), std::move (c)), type (std::move (t)) {} + + // Return the type value if present, text_type::github_mark if it refers + // to a file with the .md or .markdown extension and text_type::plain if + // it refers to a file with the .txt extension or no extension or the text + // does not come from a file. Depending on the ignore_unknown value either + // throw std::invalid_argument or return nullopt if the type value or the + // file extension is unknown. + // + // Note: also throws std::invalid_argument if the type is not well-formed. + // This, however, may not happen for an object created by the package + // manifest parser since it has already verified that. + // + butl::optional<text_type> + effective_type (bool ignore_unknown = false) const; + }; + // license // class licenses: public butl::small_vector<std::string, 1> @@ -415,17 +431,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 +456,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 // @@ -516,6 +522,12 @@ namespace bpkg // <reflect-config> - buildfile fragment containing dependent package // configuration variables assignments // + // In the multi-line form the block may contain comments besides the + // clauses. The '#' character starts a single-line comment which spans + // until the end of the line. Unless it is followed with '\' followed by + // the newline in which case this is a multi-line comment which spans + // until the closing '#\' is encountered. + // // 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. @@ -561,6 +573,22 @@ namespace bpkg accept (std::move (a)), require (std::move (q)) {} + // Can be used to copy a dependency alternative object, while omitting + // some clauses which are no longer needed. + // + dependency_alternative (butl::optional<std::string> e, + butl::optional<std::string> r, + butl::optional<std::string> p, + butl::optional<std::string> a, + butl::optional<std::string> q, + butl::small_vector<dependency, 1> ds) + : small_vector<dependency, 1> (move (ds)), + 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). @@ -571,10 +599,21 @@ 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& + operator<< (std::ostream& os, const dependency_alternative& da) + { + return os << da.string (); + } + class dependency_alternatives: public butl::small_vector<dependency_alternative, 1> { @@ -622,14 +661,14 @@ namespace bpkg // Return true if there is a conditional alternative in the list. // - LIBBPKG_EXPORT bool + bool conditional () const; }; inline std::ostream& - operator<< (std::ostream& os, const dependency_alternatives& da) + operator<< (std::ostream& os, const dependency_alternatives& das) { - return os << da.string (); + return os << das.string (); } // requires @@ -673,8 +712,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. @@ -727,7 +769,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. @@ -748,7 +790,7 @@ namespace bpkg class build_constraint { public: - // If true, then the package should not be built for matching + // If true, then the package should not be built for matching target // configurations by automated build bots. // bool exclusion; @@ -783,48 +825,33 @@ namespace bpkg // enum class package_manifest_flags: std::uint16_t { - none = 0x00, - - forbid_file = 0x01, // Forbid *-file manifest values. - forbid_location = 0x02, - forbid_sha256sum = 0x04, - forbid_fragment = 0x08, - forbid_incomplete_dependencies = 0x10, - - require_location = 0x20, - require_sha256sum = 0x40, - require_description_type = 0x80 + none = 0x000, + + forbid_file = 0x001, // Forbid *-file manifest values. + forbid_location = 0x002, + forbid_sha256sum = 0x004, + forbid_fragment = 0x008, + forbid_incomplete_values = 0x010, // depends, <distribution>-version, etc. + + require_location = 0x020, + require_sha256sum = 0x040, + require_text_type = 0x080, // description-type, changes-type, etc. + require_bootstrap_build = 0x100 }; - inline package_manifest_flags - operator&= (package_manifest_flags& x, package_manifest_flags y) - { - return x = static_cast<package_manifest_flags> ( - static_cast<std::uint16_t> (x) & - static_cast<std::uint16_t> (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<package_manifest_flags> ( - static_cast<std::uint16_t> (x) | - static_cast<std::uint16_t> (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); - // Build configuration class term. + // Target build configuration class term. // class LIBBPKG_EXPORT build_class_term { @@ -853,9 +880,9 @@ namespace bpkg build_class_term () : operation ('\0'), inverted (false), simple (true), name () {} - build_class_term (build_class_term&&); + build_class_term (build_class_term&&) noexcept; build_class_term (const build_class_term&); - build_class_term& operator= (build_class_term&&); + build_class_term& operator= (build_class_term&&) noexcept; build_class_term& operator= (const build_class_term&); ~build_class_term (); @@ -874,8 +901,8 @@ namespace bpkg // using build_class_inheritance_map = std::map<std::string, std::string>; - // Build configuration class expression. Includes comment and optional - // underlying set. + // Target build configuration class expression. Includes comment and + // optional underlying set. // class LIBBPKG_EXPORT build_class_expr { @@ -924,10 +951,10 @@ namespace bpkg std::string string () const; - // Match a build configuration that belongs to the specified list of - // classes (and recursively to their bases) against the expression. Either - // return or update the result (the latter allows to sequentially matching - // against a list of expressions). + // Match a target build configuration that belongs to the specified list + // of classes (and recursively to their bases) against the expression. + // Either return or update the result (the latter allows to sequentially + // matching against a list of expressions). // // Notes: // @@ -935,7 +962,8 @@ namespace bpkg // inheritance cycles, etc.). // // - The underlying class set doesn't affect the match in any way (it - // should have been used to pre-filter the set of build configurations). + // should have been used to pre-filter the set of target build + // configurations). // void match (const strings&, @@ -943,12 +971,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& @@ -957,27 +980,173 @@ namespace bpkg return os << bce.string (); } - enum class text_type + // Build auxiliary configuration name-matching wildcard. Includes optional + // environment name (specified as a suffix in the [*-]build-auxiliary[-*] + // value name) and comment. + // + class LIBBPKG_EXPORT build_auxiliary { - plain, - common_mark, - github_mark - }; + public: + std::string environment_name; - LIBBPKG_EXPORT std::string - to_string (text_type); + // Filesystem wildcard pattern for the build auxiliary configuration name. + // + std::string config; - // Throw std::invalid_argument if the argument is not a well-formed text - // type. Otherwise, return nullopt for an unknown text variant. - // - LIBBPKG_EXPORT butl::optional<text_type> - to_text_type (const std::string&); // May throw std::invalid_argument. + std::string comment; - inline std::ostream& - operator<< (std::ostream& os, text_type t) + build_auxiliary () = default; + build_auxiliary (std::string en, + std::string cf, + std::string cm) + : environment_name (std::move (en)), + config (std::move (cf)), + comment (std::move (cm)) {} + + // Parse a package manifest value name in the [*-]build-auxiliary[-*] form + // into the pair of the build package configuration name (first) and the + // build auxiliary environment name (second), with an unspecified name + // represented as an empty string. Return nullopt if the value name + // doesn't match this form. + // + static butl::optional<std::pair<std::string, std::string>> + parse_value_name (const std::string&); + }; + + // Package build configuration. Includes comment and optional overrides for + // target build configuration class expressions/constraints, auxiliaries, + // custom bot public keys, and notification emails. + // + // Note that in the package manifest the build bot keys list contains the + // public keys data (std::string type). However, for other use cases it may + // be convenient to store some other key representations (public key object + // pointers represented as key fingerprints, etc; see brep for such a use + // case). + // + template <typename K> + class build_package_config_template { - return os << to_string (t); - } + public: + using email_type = bpkg::email; + using key_type = K; + + std::string name; + + // Whitespace separated list of potentially double/single-quoted package + // configuration arguments for bpkg-pkg-build command executed by + // automated build bots. + // + std::string arguments; + + std::string comment; + + butl::small_vector<build_class_expr, 1> builds; + std::vector<build_constraint> constraints; + + // Note that all entries in this list must have distinct environment names + // (with empty name being one of the possibilities). + // + std::vector<build_auxiliary> auxiliaries; + + std::vector<key_type> bot_keys; + + butl::optional<email_type> email; + butl::optional<email_type> warning_email; + butl::optional<email_type> error_email; + + build_package_config_template () = default; + + build_package_config_template (std::string n, + std::string a, + std::string c, + butl::small_vector<build_class_expr, 1> bs, + std::vector<build_constraint> cs, + std::vector<build_auxiliary> as, + std::vector<key_type> bks, + butl::optional<email_type> e, + butl::optional<email_type> we, + butl::optional<email_type> ee) + : name (move (n)), + arguments (move (a)), + comment (move (c)), + builds (move (bs)), + constraints (move (cs)), + auxiliaries (move (as)), + bot_keys (move (bks)), + email (move (e)), + warning_email (move (we)), + error_email (move (ee)) {} + + // Built incrementally. + // + explicit + build_package_config_template (std::string n): name (move (n)) {} + + // Return the configuration's build class expressions/constraints if they + // override the specified common expressions/constraints and return the + // latter otherwise (see package_manifest::override() for the override + // semantics details). + // + const butl::small_vector<build_class_expr, 1>& + effective_builds (const butl::small_vector<build_class_expr, 1>& common) + const noexcept + { + return !builds.empty () ? builds : common; + } + + const std::vector<build_constraint>& + effective_constraints (const std::vector<build_constraint>& common) const + noexcept + { + return !builds.empty () || !constraints.empty () ? constraints : common; + } + + // Return the configuration's auxiliaries, if specified, and the common + // ones otherwise. + // + const std::vector<build_auxiliary>& + effective_auxiliaries (const std::vector<build_auxiliary>& common) const + noexcept + { + return !auxiliaries.empty () ? auxiliaries : common; + } + + // Return the configuration's custom bot public keys, if specified, and + // the common ones otherwise. + // + const std::vector<key_type>& + effective_bot_keys (const std::vector<key_type>& common) const noexcept + { + return !bot_keys.empty () ? bot_keys : common; + } + + // Return the configuration's build notification emails if they override + // the specified common build notification emails and return the latter + // otherwise (see package_manifest::override() for the override semantics + // details). + // + const butl::optional<email_type>& + effective_email (const butl::optional<email_type>& common) const noexcept + { + return email || warning_email || error_email ? email : common; + } + + const butl::optional<email_type>& + effective_warning_email (const butl::optional<email_type>& common) const + noexcept + { + return email || warning_email || error_email ? warning_email : common; + } + + const butl::optional<email_type>& + effective_error_email (const butl::optional<email_type>& common) const + noexcept + { + return email || warning_email || error_email ? error_email : common; + } + }; + + using build_package_config = build_package_config_template<std::string>; enum class test_dependency_type { @@ -1004,27 +1173,91 @@ namespace bpkg { test_dependency_type type; bool buildtime; + butl::optional<std::string> enable; + butl::optional<std::string> reflect; test_dependency () = default; test_dependency (package_name n, test_dependency_type t, bool b, - butl::optional<version_constraint> c) - : dependency {std::move (n), std::move (c)}, type (t), buildtime (b) {} + butl::optional<version_constraint> c, + butl::optional<std::string> e, + butl::optional<std::string> r) + : dependency {std::move (n), std::move (c)}, + type (t), + buildtime (b), + enable (std::move (e)), + reflect (std::move (r)) {} // Parse the test dependency string representation in the - // `[*] <name> [<version-constraint>]` form. Throw std::invalid_argument - // if the value is invalid. + // `[*] <name> [<version-constraint>] ['?' <enable-condition>] [<reflect-config>]` + // form. Throw std::invalid_argument if the value is invalid. + // + // Verify that the reflect clause, if present, refers to the test + // dependency package configuration variable. Note that such variable + // value normally signals the dependent package being tested. // test_dependency (std::string, test_dependency_type); - inline std::string - string () const - { - return buildtime - ? "* " + dependency::string () - : dependency::string (); - } + std::string + string () const; + }; + + // Package's buildfile path and content. + // + struct buildfile + { + // The path is relative to the package's build/ subdirectory with the + // extension stripped. + // + // For example, for the build/config/common.build file the path will be + // config/common. + // + // Note that the actual file path depends on the project's buildfile + // naming scheme and for the config/common example above the actual path + // can also be build2/config/common.build2. + // + butl::path path; + std::string content; + + buildfile () = default; + buildfile (butl::path p, std::string c) + : path (std::move (p)), + content (std::move (c)) {} + }; + + // Binary distribution package information. + // + // The name is prefixed with the <distribution> id, typically name/version + // pair in the <name>[_<version>] form. For example: + // + // debian-name + // debian_10-name + // ubuntu_20.04-name + // + // Currently recognized names: + // + // <distribution>-name + // <distribution>-version + // <distribution>-to-downstream-version + // + // Note that the value format/semantics can be distribution-specific. + // + struct 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 <distribution> component if the name has the + // specified suffix, which is assumed to be valid (-name, etc). + // + butl::optional<std::string> + distribution (const std::string& suffix) const; }; class LIBBPKG_EXPORT package_manifest @@ -1037,16 +1270,18 @@ namespace bpkg package_name name; version_type version; butl::optional<std::string> upstream_version; + butl::optional<std::string> type; // <name>[, ...] + butl::small_vector<language, 1> languages; // <name>[=impl][, ...] butl::optional<package_name> project; butl::optional<priority_type> priority; std::string summary; - butl::small_vector<licenses, 1> license_alternatives; + butl::small_vector<std::string, 5> topics; butl::small_vector<std::string, 5> keywords; - butl::optional<text_file> description; - butl::optional<std::string> description_type; - butl::small_vector<text_file, 1> changes; + butl::optional<typed_text_file> description; + butl::optional<typed_text_file> package_description; + butl::small_vector<typed_text_file, 1> changes; butl::optional<manifest_url> url; butl::optional<manifest_url> doc_url; butl::optional<manifest_url> src_url; @@ -1060,8 +1295,40 @@ namespace bpkg std::vector<requirement_alternatives> requirements; butl::small_vector<test_dependency, 1> tests; + // Common build classes, constraints, auxiliaries, and custom bot public + // keys that apply to all configurations unless overridden. + // + // Note that all entries in build_auxiliaries must have distinct + // environment names (with empty name being one of the possibilities). + // butl::small_vector<build_class_expr, 1> builds; std::vector<build_constraint> build_constraints; + std::vector<build_auxiliary> build_auxiliaries; + strings build_bot_keys; + + // Note that the parsing constructor adds the implied (empty) default + // configuration at the beginning of the list. Also note that serialize() + // writes no values for such a configuration. + // + butl::small_vector<build_package_config, 1> build_configs; // 1 for default. + + // If true, then this package use the alternative buildfile naming scheme + // (build2/, .build2). In the manifest serialization this is encoded as + // either *-build or *-build2 value names. + // + butl::optional<bool> alt_naming; + + butl::optional<std::string> bootstrap_build; + butl::optional<std::string> root_build; + + // Additional buildfiles which are potentially included by root.build. + // + std::vector<buildfile> buildfiles; // Buildfiles content. + std::vector<butl::path> buildfile_paths; + + // The binary distributions package information. + // + std::vector<distribution_name_value> distribution_values; // The following values are only valid in the manifest list (and only for // certain repository types). @@ -1070,19 +1337,45 @@ namespace bpkg butl::optional<std::string> sha256sum; butl::optional<std::string> fragment; - const package_name& - effective_project () const noexcept {return project ? *project : name;} + // Extract the name from optional type, returning either `exe`, `lib`, or + // `other`. + // + // Specifically, if type is present but the name is not recognized, 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; - // Return the description type value if present, text_type::github_mark if - // the description refers to a file with the .md or .markdown extension - // and text_type::plain if it refers to a file with the .txt extension or - // no extension or the description does not come from a file. Depending on - // the ignore_unknown value either throw std::invalid_argument or return - // nullopt if the description value or the file extension is unknown. - // Throw std::logic_error if the description value is nullopt. + static std::string + effective_type (const butl::optional<std::string>&, const package_name&); + + // Extract sub-options from optional type. // - butl::optional<text_type> - effective_description_type (bool ignore_unknown = false) const; + strings + effective_type_sub_options () const; + + static strings + effective_type_sub_options (const butl::optional<std::string>&); + + // 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<language, 1> + effective_languages () const; + + static butl::small_vector<language, 1> + effective_languages (const butl::small_vector<language, 1>&, + const package_name&); + + // Return effective project name. + // + const package_name& + effective_project () const noexcept {return project ? *project : name;} public: package_manifest () = default; @@ -1094,7 +1387,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 | @@ -1116,7 +1409,7 @@ namespace bpkg package_manifest (butl::manifest_parser&, const std::function<translate_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 | @@ -1131,7 +1424,7 @@ namespace bpkg package_manifest (const std::string& name, std::vector<butl::manifest_name_value>&&, 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 | @@ -1141,7 +1434,7 @@ namespace bpkg std::vector<butl::manifest_name_value>&&, const std::function<translate_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 | @@ -1152,27 +1445,72 @@ 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 // any value is invalid, cannot be overridden, or its name is not // recognized. // - // The specified values override the whole groups they belong to, - // resetting all the group values prior to being applied. Currently, only - // the following value groups can be overridden: {build-*email} and - // {builds, build-{include,exclude}}. - // - // Note that the build constraints group values are overridden - // hierarchically so that the build-{include,exclude} overrides don't - // affect the builds values. + // The specified values other than [*-]build-auxiliary[-*] override the + // whole groups they belong to, resetting all the group values prior to + // being applied. The [*-]build-auxiliary[-*] values only override the + // matching values, which are expected to already be present in the + // manifest. Currently, only the following value groups/values can be + // overridden: + // + // {build-*email} + // {builds, build-{include,exclude}} + // {build-bot} + // {*-builds, *-build-{include,exclude}} + // {*-build-bot} + // {*-build-config} + // {*-build-*email} + // + // [*-]build-auxiliary[-*] + // + // Throw manifest_parsing if the configuration specified by the build + // package configuration-specific build constraint, email, auxiliary, or + // custom bot public key value override doesn't exists. In contrast, for + // the build config override add a new configuration if it doesn't exist + // and update the arguments of the existing configuration otherwise. In + // the former case, all the potential build constraint, email, auxiliary, + // and bot key overrides for such a newly added configuration must follow + // the respective *-build-config override. + // + // Note that the build constraints group values (both common and build + // config-specific) are overridden hierarchically so that the + // [*-]build-{include,exclude} overrides don't affect the respective + // [*-]builds values. + // + // Also note that the common and build config-specific build constraints + // group value overrides are mutually exclusive. If the common build + // constraints are overridden, then all the build config-specific + // constraints are removed. Otherwise, if some build config-specific + // constraints are overridden, then for the remaining configs the build + // constraints are reset to `builds: none`. + // + // Similar to the build constraints groups, the common and build + // config-specific custom bot key value overrides are mutually + // exclusive. If the common custom bot keys are overridden, then all the + // build config-specific custom bot keys are removed. Otherwise, if some + // build config-specific custom bot keys are overridden, then for the + // remaining configs the custom bot keys are left unchanged. + // + // Similar to the above, the common and build config-specific build emails + // group value overrides are mutually exclusive. If the common build + // emails are overridden, then all the build config-specific emails are + // reset to nullopt. Otherwise, if some build config-specific emails are + // overridden, then for the remaining configs the email is reset to the + // empty value and the warning and error emails are reset to nullopt + // (which effectively disables email notifications for such + // configurations). // // If a non-empty source name is specified, then the specified values are // assumed to also include the line/column information and the possibly - // thrown manifest_parsing exception will contain the invalid value + // thrown manifest_parsing exception will contain the invalid value's // location information. Otherwise, the exception description will refer - // to the invalid value name instead. + // to the invalid value instead. // void override (const std::vector<butl::manifest_name_value>&, @@ -1180,6 +1518,13 @@ namespace bpkg // Validate the overrides without applying them to any manifest. // + // Specifically, validate that the override values can be parsed according + // to their name semantics and that the value sequence makes sense (no + // mutually exclusive values, etc). Note, however, that the subsequent + // applying of the successfully validated overrides to a specific package + // manifest may still fail (no build config exists for specified *-builds, + // etc). + // static void validate_overrides (const std::vector<butl::manifest_name_value>&, const std::string& source_name); @@ -1205,17 +1550,20 @@ namespace bpkg // Load the *-file manifest values using the specified load function that // returns the file contents passing through any exception it may throw. - // Set the potentially absent description type value to the effective - // description type. If the effective type is nullopt then assign a - // synthetic unknown type. + // If nullopt is returned, then the respective *-file value is left + // unexpanded. Set the potentially absent project description, package + // description, and changes type values to their effective types. If an + // effective type is nullopt then assign a synthetic unknown type if the + // ignore_unknown argument is true and throw manifest_parsing otherwise. // // Note that if the returned file contents is empty, load_files() makes // sure that this is allowed by the value's semantics throwing // manifest_parsing otherwise. However, the load function may want to // recognize such cases itself in order to issue more precise diagnostics. // - using load_function = std::string (const std::string& name, - const butl::path& value); + using load_function = + butl::optional<std::string> (const std::string& name, + const butl::path& value); void load_files (const std::function<load_function>&, @@ -1224,13 +1572,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_depends = true) - { - return package_manifest (p, ignore_unknown, complete_depends); - } + bool complete_values = true); LIBBPKG_EXPORT package_manifest dir_package_manifest (butl::manifest_parser&, bool ignore_unknown = false); @@ -1536,9 +1881,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. @@ -1556,59 +1900,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. // @@ -1622,69 +1929,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<std::string>& - 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. @@ -1981,4 +2249,6 @@ namespace bpkg } } +#include <libbpkg/manifest.ixx> + #endif // LIBBPKG_MANIFEST_HXX |