aboutsummaryrefslogtreecommitdiff
path: root/libbpkg/manifest.hxx
diff options
context:
space:
mode:
Diffstat (limited to 'libbpkg/manifest.hxx')
-rw-r--r--libbpkg/manifest.hxx1310
1 files changed, 1013 insertions, 297 deletions
diff --git a/libbpkg/manifest.hxx b/libbpkg/manifest.hxx
index 80b410d..feb3b96 100644
--- a/libbpkg/manifest.hxx
+++ b/libbpkg/manifest.hxx
@@ -8,16 +8,16 @@
#include <string>
#include <vector>
#include <cassert>
-#include <cstdint> // uint16_t
+#include <cstdint> // uint*_t
#include <ostream>
-#include <utility> // move()
-#include <stdexcept> // logic_error
+#include <utility> // move(), pair
#include <functional>
-#include <libbutl/url.mxx>
-#include <libbutl/path.mxx>
-#include <libbutl/optional.mxx>
-#include <libbutl/small-vector.mxx>
+#include <libbutl/url.hxx>
+#include <libbutl/path.hxx>
+#include <libbutl/optional.hxx>
+#include <libbutl/small-vector.hxx>
+#include <libbutl/standard-version.hxx>
#include <libbutl/manifest-forward.hxx>
#include <libbpkg/package-name.hxx>
@@ -67,13 +67,20 @@ namespace bpkg
// std::invalid_argument if the passed string is not a valid version
// representation.
//
+ enum flags
+ {
+ none = 0,
+ fold_zero_revision = 0x01,
+ allow_iteration = 0x02
+ };
+
explicit
- version (const std::string& v, bool fold_zero_revision = true)
- : version (v.c_str (), fold_zero_revision) {}
+ version (const std::string& v, flags fl = fold_zero_revision)
+ : version (v.c_str (), fl) {}
explicit
- version (const char* v, bool fold_zero_revision = true)
- : version (data_type (v, data_type::parse::full, fold_zero_revision))
+ version (const char* v, flags fl = fold_zero_revision)
+ : version (data_type (v, data_type::parse::full, fl))
{
}
@@ -94,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
@@ -103,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).
@@ -127,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
@@ -168,7 +143,7 @@ namespace bpkg
{
enum class parse {full, upstream, release};
- data_type (const char*, parse, bool fold_zero_revision);
+ data_type (const char*, parse, flags);
// Note that there is no iteration component as it can't be present in
// the string representation passed to the ctor.
@@ -177,6 +152,7 @@ namespace bpkg
std::string upstream;
butl::optional<std::string> release;
butl::optional<std::uint16_t> revision;
+ std::uint32_t iteration;
std::string canonical_upstream;
std::string canonical_release;
};
@@ -187,7 +163,7 @@ namespace bpkg
upstream (std::move (d.upstream)),
release (std::move (d.release)),
revision (d.revision),
- iteration (0),
+ iteration (d.iteration),
canonical_upstream (std::move (d.canonical_upstream)),
canonical_release (std::move (d.canonical_release)) {}
};
@@ -198,6 +174,11 @@ namespace bpkg
return os << (v.empty () ? "<empty-version>" : v.string ());
}
+ 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
//
class priority
@@ -214,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:
@@ -244,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>
@@ -274,19 +327,21 @@ namespace bpkg
// - is not local (the scheme is not `file`)
// - authority is present and is not empty
//
- // See libbutl/url.mxx for details.
+ // See libbutl/url.hxx for details.
+ //
+ // NOTE: this class must not be DLL-exported wholesale (non-exported base).
//
- class url: public butl::url
+ class manifest_url: public butl::url
{
public:
std::string comment;
// Throw invalid_argument on parsing or constraints checking error.
//
- explicit
- url (const std::string& u, std::string c = "");
+ explicit LIBBPKG_EXPORT
+ manifest_url (const std::string& u, std::string c = "");
- url () = default;
+ manifest_url () = default;
};
// email
@@ -376,68 +431,366 @@ 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
{
package_name name;
butl::optional<version_constraint> constraint;
+ dependency () = default;
+ dependency (package_name n, butl::optional<version_constraint> c)
+ : name (std::move (n)), constraint (std::move (c)) {}
+
+ // Parse the dependency string representation in the
+ // `<name> [<version-constraint>]` form. Throw std::invalid_argument if
+ // the value is invalid.
+ //
+ explicit
+ dependency (std::string);
+
std::string
string () const;
};
+ std::ostream&
+ operator<< (std::ostream&, const dependency&);
+
+ // depends
+ //
+ // The dependency alternative can be represented in one of the following
+ // forms.
+ //
+ // Single-line form:
+ //
+ // <dependencies> ['?' <enable-condition>] [<reflect-config>]
+ //
+ // <dependencies> = <dependency> |
+ // ({ <dependency> [ <dependency>]* } [<version-constraint>])
+ //
+ // <enable-condition> - buildfile evaluation context
+ // <reflect-config> - 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:
+ //
+ // <dependencies>
+ // {
+ // enable <enable-condition>
+ //
+ // prefer
+ // {
+ // <prefer-config>
+ // }
+ //
+ // accept <accept-condition>
+ //
+ // reflect
+ // {
+ // <reflect-config>
+ // }
+ // }
+ // |
+ // <dependencies>
+ // {
+ // enable <enable-condition>
+ //
+ // require
+ // {
+ // <require-config>
+ // }
+ //
+ // reflect
+ // {
+ // <reflect-config>
+ // }
+ // }
+ //
+ // <prefer-config> - buildfile fragment containing dependency packages
+ // configuration variables assignments
+ //
+ // <accept-condition> - buildfile evaluation context
+ //
+ // <require-config> - buildfile fragment containing dependency packages
+ // configuration variables assignments
+ //
+ // <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.
+ //
+ // 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<dependency, 1>
+ {
+ public:
+ butl::optional<std::string> enable;
+ butl::optional<std::string> reflect;
+ butl::optional<std::string> prefer;
+ butl::optional<std::string> accept;
+ butl::optional<std::string> require;
+
+ dependency_alternative () = default;
+ 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)
+ : enable (std::move (e)),
+ reflect (std::move (r)),
+ prefer (std::move (p)),
+ 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).
+ //
+ LIBBPKG_EXPORT std::string
+ string () const;
+
+ // Return true if the string() function would return the single-line
+ // representation.
+ //
+ bool
+ single_line () const
+ {
+ return !prefer &&
+ !require &&
+ (!reflect || reflect->find ('\n') == std::string::npos);
+ }
+ };
+
inline std::ostream&
- operator<< (std::ostream& os, const dependency& d)
+ operator<< (std::ostream& os, const dependency_alternative& da)
{
- return os << d.string ();
+ return os << da.string ();
}
- // depends
- //
- class dependency_alternatives: public butl::small_vector<dependency, 1>
+ class dependency_alternatives:
+ public butl::small_vector<dependency_alternative, 1>
{
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:
+ //
+ // [*] <alternative> [ '|' <alternative>]* [; <comment>]
+ //
+ // Where <alternative> 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&,
+ 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.
+ //
+ bool
+ conditional () const;
};
- LIBBPKG_EXPORT std::ostream&
- operator<< (std::ostream&, const dependency_alternatives&);
+ inline std::ostream&
+ operator<< (std::ostream& os, const dependency_alternatives& das)
+ {
+ return os << das.string ();
+ }
// requires
//
- class requirement_alternatives: public butl::small_vector<std::string, 1>
+ // 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<std::string, 1>
+ {
+ public:
+ butl::optional<std::string> enable;
+ butl::optional<std::string> reflect;
+
+ requirement_alternative () = default;
+ requirement_alternative (butl::optional<std::string> e,
+ butl::optional<std::string> r)
+ : enable (std::move (e)), reflect (std::move (r)) {}
+
+ // Return the single-line representation if possible (the reflect clause
+ // either absent or contains no newlines).
+ //
+ LIBBPKG_EXPORT std::string
+ string () const;
+
+ // Return true if the string() function would return the single-line
+ // representation.
+ //
+ 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.
+ //
+ bool
+ simple () const
+ {
+ return size () == 1 && (back ().empty () || (enable && enable->empty ()));
+ }
+ };
+
+ class requirement_alternatives:
+ public butl::small_vector<requirement_alternative, 1>
{
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
+ // following forms:
+ //
+ // [*] <alternative> [ '|' <alternative>]* [; <comment>]
+ // [*] [<requirement-id>] [? [<enable-condition>]] ; <comment>
+ //
+ // 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&,
+ 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.
+ //
+ bool
+ conditional () const;
+
+ // Return true if this is a single simple requirement alternative.
+ //
+ bool
+ simple () const
+ {
+ return size () == 1 && back ().simple ();
+ }
};
+ inline std::ostream&
+ operator<< (std::ostream& os, const requirement_alternatives& ra)
+ {
+ return os << ra.string ();
+ }
+
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;
@@ -472,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
{
@@ -542,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 ();
@@ -563,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
{
@@ -613,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:
//
@@ -624,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&,
@@ -632,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&
@@ -646,58 +980,312 @@ 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;
+
+ // Filesystem wildcard pattern for the build auxiliary configuration name.
+ //
+ std::string config;
+
+ std::string comment;
+
+ 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
+ {
+ 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
+ {
+ tests,
+ examples,
+ benchmarks
};
LIBBPKG_EXPORT std::string
- to_string (text_type);
+ to_string (test_dependency_type);
- // Throw std::invalid_argument if the argument is not a well-formed text
- // type. Otherwise, return nullopt for an unknown text variant.
+ // May throw std::invalid_argument.
//
- LIBBPKG_EXPORT butl::optional<text_type>
- to_text_type (const std::string&); // May throw std::invalid_argument.
+ LIBBPKG_EXPORT test_dependency_type
+ to_test_dependency_type (const std::string&);
inline std::ostream&
- operator<< (std::ostream& os, text_type t)
+ operator<< (std::ostream& os, test_dependency_type t)
{
return os << to_string (t);
}
+ struct LIBBPKG_EXPORT test_dependency: dependency
+ {
+ 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,
+ 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>] ['?' <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);
+
+ 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
{
public:
using version_type = bpkg::version;
using priority_type = bpkg::priority;
- using url_type = bpkg::url;
using email_type = bpkg::email;
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;
-
- // @@ Replace with small_vector<licenses, 1>. Note that currently it is
- // unsupported by the odb::nested_*() functions that are
- // std::vector-specific.
- //
- std::vector<licenses> license_alternatives;
+ 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<url_type> url;
- butl::optional<url_type> doc_url;
- butl::optional<url_type> src_url;
- butl::optional<url_type> package_url;
+ 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;
+ butl::optional<manifest_url> package_url;
butl::optional<email_type> email;
butl::optional<email_type> package_email;
butl::optional<email_type> build_email;
@@ -705,12 +1293,42 @@ namespace bpkg
butl::optional<email_type> build_error_email;
std::vector<dependency_alternatives> dependencies;
std::vector<requirement_alternatives> requirements;
- butl::small_vector<dependency, 1> tests;
- butl::small_vector<dependency, 1> examples;
- butl::small_vector<dependency, 1> benchmarks;
+ 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).
@@ -719,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;
+
+ static std::string
+ effective_type (const butl::optional<std::string>&, const package_name&);
- // 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.
+ // 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;
@@ -743,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 |
@@ -755,7 +1399,7 @@ namespace bpkg
// release, etc).
//
// In particular, the translation function may "patch" the version with
- // the snapshot information (see <libbutl/standard-version.mxx> for
+ // the snapshot information (see <libbutl/standard-version.hxx> for
// details). This translation is normally required for manifests of
// packages that are accessed as directories (as opposed to package
// archives that should have their version already patched).
@@ -765,7 +1409,32 @@ 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 |
+ package_manifest_flags::forbid_fragment);
+
+ // As above but construct the package manifest from the pre-parsed
+ // manifest values list.
+ //
+ // Note that the list is expected not to contain the format version nor
+ // the end-of-manifest/stream pairs.
+ //
+ package_manifest (const std::string& name,
+ std::vector<butl::manifest_name_value>&&,
+ bool ignore_unknown = false,
+ bool complete_values = true,
+ package_manifest_flags =
+ package_manifest_flags::forbid_location |
+ package_manifest_flags::forbid_sha256sum |
+ package_manifest_flags::forbid_fragment);
+
+ package_manifest (const std::string& name,
+ std::vector<butl::manifest_name_value>&&,
+ const std::function<translate_function>&,
+ bool ignore_unknown = false,
+ bool complete_values = true,
package_manifest_flags =
package_manifest_flags::forbid_location |
package_manifest_flags::forbid_sha256sum |
@@ -776,23 +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}}.
+ // 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>&,
@@ -800,12 +1518,30 @@ 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);
+ // If the minimum libbpkg version is specified, then also apply the
+ // required backward compatibility workarounds to the serialized manifest
+ // so that clients of all libbpkg versions greater or equal to the
+ // specified version can parse it, ignoring unknown values.
+ //
+ // Note that clients of the latest major libbpkg version can fully
+ // recognize the produced manifest and thus can parse it without ignoring
+ // unknown values.
+ //
void
- serialize (butl::manifest_serializer&) const;
+ serialize (
+ butl::manifest_serializer&,
+ const butl::optional<butl::standard_version>& = butl::nullopt) const;
// Serialize only package manifest header values.
//
@@ -814,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>&,
@@ -833,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);
@@ -867,10 +1603,12 @@ namespace bpkg
// Serialize.
//
inline void
- pkg_package_manifest (butl::manifest_serializer& s,
- const package_manifest& m)
+ pkg_package_manifest (
+ butl::manifest_serializer& s,
+ const package_manifest& m,
+ const butl::optional<butl::standard_version>& min_ver = butl::nullopt)
{
- m.serialize (s);
+ m.serialize (s, min_ver);
}
// Normally there is no need to serialize dir and git package manifests,
@@ -899,8 +1637,14 @@ namespace bpkg
pkg_package_manifests (butl::manifest_parser&,
bool ignore_unknown = false);
+ // If the minimum libbpkg version is specified, then also apply the
+ // required backward compatibility workarounds to the serialized package
+ // manifests list (see package_manifest::serialize() for details).
+ //
void
- serialize (butl::manifest_serializer&) const;
+ serialize (
+ butl::manifest_serializer&,
+ const butl::optional<butl::standard_version>& = butl::nullopt) const;
};
class LIBBPKG_EXPORT dir_package_manifests:
@@ -991,8 +1735,8 @@ namespace bpkg
// non-empty object with non-empty path never contains the trailing slash
// (except for the root path on POSIX system).
//
- // - For the remote URL object the host name is in the lower case (IPv4/6 are
- // not supported) and the path is relative.
+ // - For the remote URL object the host component is normalized (see
+ // butl::basic_url_host for details) and the path is relative.
//
// - For the local URL object the path can be relative or absolute. Query
// can not be present. Represent the object using the file:// notation if
@@ -1137,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.
@@ -1157,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.
//
@@ -1223,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.
@@ -1446,6 +2113,13 @@ namespace bpkg
butl::manifest_name_value start,
bool ignore_unknown = false);
+ struct repositories_manifest_header
+ {
+ public:
+ butl::optional<butl::standard_version> min_bpkg_version;
+ butl::optional<std::string> compression;
+ };
+
class LIBBPKG_EXPORT pkg_repository_manifests:
public std::vector<repository_manifest>
{
@@ -1454,6 +2128,9 @@ namespace bpkg
using base_type::base_type;
+ butl::optional<repositories_manifest_header> header;
+
+ public:
pkg_repository_manifests () = default;
pkg_repository_manifests (butl::manifest_parser&,
bool ignore_unknown = false);
@@ -1470,6 +2147,9 @@ namespace bpkg
using base_type::base_type;
+ butl::optional<repositories_manifest_header> header;
+
+ public:
dir_repository_manifests () = default;
dir_repository_manifests (butl::manifest_parser&,
bool ignore_unknown = false);
@@ -1486,6 +2166,9 @@ namespace bpkg
using base_type::base_type;
+ butl::optional<repositories_manifest_header> header;
+
+ public:
git_repository_manifests () = default;
git_repository_manifests (butl::manifest_parser&,
bool ignore_unknown = false);
@@ -1533,6 +2216,39 @@ namespace bpkg
butl::manifest_name_value start,
bool ignore_unknown);
};
+
+ // Extract the package name component from <name>[/<version>] or
+ // <name><version-constraint>. Throw invalid_argument on parsing error.
+ //
+ // Note: the version and version constraint are not verified.
+ //
+ LIBBPKG_EXPORT package_name
+ extract_package_name (const char*, bool allow_version = true);
+
+ inline package_name
+ extract_package_name (const std::string& s, bool allow_version = true)
+ {
+ return extract_package_name (s.c_str (), allow_version);
+ }
+
+ // Extract the package version component from <name>[/<version>]. Return
+ // empty version if none is specified. Throw invalid_argument on parsing
+ // error and for the earliest and stub versions.
+ //
+ // Note: the package name is not verified.
+ //
+ LIBBPKG_EXPORT version
+ extract_package_version (const char*,
+ version::flags fl = version::fold_zero_revision);
+
+ inline version
+ extract_package_version (const std::string& s,
+ version::flags fl = version::fold_zero_revision)
+ {
+ return extract_package_version (s.c_str (), fl);
+ }
}
+#include <libbpkg/manifest.ixx>
+
#endif // LIBBPKG_MANIFEST_HXX