From c4d2ac250aee4102b519ce1db89bde3fe7855639 Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Mon, 1 May 2017 12:10:35 +0300 Subject: Add hxx extension for headers and lib prefix for library dirs --- libbpkg/manifest.hxx | 636 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 636 insertions(+) create mode 100644 libbpkg/manifest.hxx (limited to 'libbpkg/manifest.hxx') diff --git a/libbpkg/manifest.hxx b/libbpkg/manifest.hxx new file mode 100644 index 0000000..1b8c694 --- /dev/null +++ b/libbpkg/manifest.hxx @@ -0,0 +1,636 @@ +// file : libbpkg/manifest.hxx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef LIBBPKG_MANIFEST_HXX +#define LIBBPKG_MANIFEST_HXX + +#include +#include +#include +#include // uint16_t +#include +#include // move() +#include // logic_error + +#include +#include +#include + +#include +#include + +namespace bpkg +{ + using strings = std::vector; + + // @@ Let's create with "basic" package types. + // + class LIBBPKG_EXPORT version + { + public: + // Let's keep the members in the order they appear in the string + // representation. + // + const std::uint16_t epoch; + const std::string upstream; + const butl::optional release; + const std::uint16_t revision; + + // Upstream part canonical representation. + // + const std::string canonical_upstream; + + // Release part canonical representation. + // + const std::string canonical_release; + + // Create a special empty version. It is less than any other valid + // version (and is conceptually equivalent to 0-). + // + version (): epoch (0), release (""), revision (0) {} + + // Throw std::invalid_argument if the passed string is not a valid + // version representation. + // + explicit + version (const std::string& v): version (v.c_str ()) {} + + explicit + version (const char* v): version (data_type (v, data_type::parse::full)) {} + + // Create the version object from separate epoch, upstream, release, and + // revision parts. + // + // Note that it is possible (and legal) to create the special empty + // version via this interface as version(0, string(), string(), 0). + // + version (std::uint16_t epoch, + std::string upstream, + butl::optional release, + std::uint16_t revision); + + version (version&&) = default; + version (const version&) = default; + version& operator= (version&&); + version& operator= (const version&); + + std::string + string (bool ignore_revision = 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;} + + int + compare (const version& v, bool ignore_revision = 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 && revision != v.revision) + return revision < v.revision ? -1 : 1; + + return 0; + } + + bool + empty () const noexcept + { + bool e (upstream.empty ()); + assert (!e || + (epoch == 0 && release && release->empty () && revision == 0)); + return e; + } + + private: + struct LIBBPKG_EXPORT data_type + { + enum class parse {full, upstream, release}; + + data_type (const char*, parse); + + std::uint16_t epoch; + std::string upstream; + butl::optional release; + std::uint16_t revision; + std::string canonical_upstream; + std::string canonical_release; + }; + + explicit + version (data_type&& d) + : epoch (d.epoch), + upstream (std::move (d.upstream)), + release (std::move (d.release)), + revision (d.revision), + canonical_upstream (std::move (d.canonical_upstream)), + canonical_release (std::move (d.canonical_release)) {} + }; + + inline std::ostream& + operator<< (std::ostream& os, const version& v) + { + return os << (v.empty () ? "" : v.string ()); + } + + // priority + // + class priority + { + public: + enum value_type {low, medium, high, security}; + + value_type value; // Shouldn't be necessary to access directly. + std::string comment; + + priority (value_type v = low, std::string c = "") + : value (v), comment (std::move (c)) {} + + operator value_type () const {return value;} + }; + + // description + // description-file + // change + // change-file + // + class LIBBPKG_EXPORT text_file + { + public: + using path_type = butl::path; + + bool file; + + union + { + std::string text; + path_type path; + }; + + std::string comment; + + // File text constructor. + // + explicit + text_file (std::string t = ""): file (false), text (std::move (t)) {} + + // File reference constructor. + // + text_file (path_type p, std::string c) + : file (true), path (std::move (p)), comment (std::move (c)) {} + + text_file (text_file&&); + text_file (const text_file&); + text_file& operator= (text_file&&); + text_file& operator= (const text_file&); + + ~text_file (); + }; + + // license + // + class licenses: public strings + { + public: + std::string comment; + + explicit + licenses (std::string c = ""): comment (std::move (c)) {} + }; + + // url + // package-url + // + class url: public std::string + { + public: + std::string comment; + + explicit + url (std::string u = "", std::string c = "") + : std::string (std::move (u)), comment (std::move (c)) {} + }; + + // email + // package-email + // build-email + // + class email: public std::string + { + public: + std::string comment; + + explicit + email (std::string e = "", std::string c = "") + : std::string (std::move (e)), comment (std::move (c)) {} + }; + + // depends + // + struct LIBBPKG_EXPORT dependency_constraint + { + butl::optional min_version; + butl::optional max_version; + bool min_open; + bool max_open; + + dependency_constraint (butl::optional min_version, bool min_open, + butl::optional max_version, bool max_open); + + dependency_constraint (const version& v) + : dependency_constraint (v, false, v, false) {} + + dependency_constraint () = default; + + bool + empty () const noexcept {return !min_version && !max_version;} + }; + + LIBBPKG_EXPORT std::ostream& + operator<< (std::ostream&, const dependency_constraint&); + + inline bool + operator== (const dependency_constraint& x, const dependency_constraint& y) + { + return x.min_version == y.min_version && x.max_version == y.max_version && + x.min_open == y.min_open && x.max_open == y.max_open; + } + + inline bool + operator!= (const dependency_constraint& x, const dependency_constraint& y) + { + return !(x == y); + } + + struct dependency + { + std::string name; + butl::optional constraint; + }; + + LIBBPKG_EXPORT std::ostream& + operator<< (std::ostream&, const dependency&); + + class dependency_alternatives: public std::vector + { + public: + bool conditional; + bool buildtime; + std::string comment; + + dependency_alternatives () = default; + dependency_alternatives (bool d, bool b, std::string c) + : conditional (d), buildtime (b), comment (std::move (c)) {} + }; + + LIBBPKG_EXPORT std::ostream& + operator<< (std::ostream&, const dependency_alternatives&); + + // requires + // + class requirement_alternatives: public strings + { + 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)) {} + }; + + 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; + + std::string name; + version_type version; + butl::optional priority; + std::string summary; + std::vector license_alternatives; + strings tags; + butl::optional description; + std::vector changes; + url_type url; + butl::optional package_url; + email_type email; + butl::optional package_email; + butl::optional build_email; + std::vector dependencies; + std::vector requirements; + + // The following values are only valid in the manifest list. + // + butl::optional location; + butl::optional sha256sum; + + public: + package_manifest () = default; // VC export. + + // Create individual package manifest. + // + package_manifest (butl::manifest_parser&, bool ignore_unknown = false); + + // Create an element of the package list manifest. + // + package_manifest (butl::manifest_parser&, + butl::manifest_name_value start, + bool ignore_unknown = false); + + void + serialize (butl::manifest_serializer&) const; + + private: + package_manifest (butl::manifest_parser&, + butl::manifest_name_value start, + bool in_list, + bool ignore_unknown); + }; + + class LIBBPKG_EXPORT package_manifests: public std::vector + { + public: + using base_type = std::vector; + + using base_type::base_type; + + // Checksum of the corresponding repository_manifests. + // + std::string sha256sum; + + public: + package_manifests () = default; + package_manifests (butl::manifest_parser&, bool ignore_unknown = false); + + void + serialize (butl::manifest_serializer&) const; + }; + + class LIBBPKG_EXPORT repository_location + { + public: + // Create a special empty repository_location. + // + repository_location () = default; + + // If the argument is not empty, create remote/absolute repository + // location. Throw std::invalid_argument if the location is a relative + // path. If the argument is empty, then create the special empty + // location. + // + explicit + repository_location (const std::string&); + + // Create a potentially relative repository location. If base is not + // empty, use it to complete the relative location to remote/absolute. + // Throw std::invalid_argument if base is not empty but the location is + // empty, base itself is relative, or the resulting completed location + // is invalid. + // + repository_location (const std::string&, const repository_location& base); + + repository_location (const repository_location& l, + const repository_location& base) + : repository_location (l.string (), base) {} + + // Note that relative locations have no canonical name. Canonical + // name of an empty location is the empty name. + // + const std::string& + canonical_name () const noexcept {return canonical_name_;} + + // There are 3 types of locations: remote, local absolute filesystem + // path and local relative filesystem path. Plus there is the special + // empty location. The following predicates can be used to determine + // what kind of location it is. Note that except for empty(), all the + // other predicates throw std::logic_error for an empty location. + // + bool + empty () const noexcept {return path_.empty ();} + + bool + local () const + { + if (empty ()) + throw std::logic_error ("empty location"); + + return host_.empty (); + } + + bool + remote () const + { + return !local (); + } + + bool + absolute () const + { + if (empty ()) + throw std::logic_error ("empty location"); + + // Note that in remote locations path is always relative. + // + return path_.absolute (); + } + + bool + relative () const + { + return local () && path_.relative (); + } + + const butl::dir_path& + path () const + { + if (empty ()) + throw std::logic_error ("empty location"); + + return path_; + } + + const std::string& + host () const + { + if (local ()) + throw std::logic_error ("local location"); + + return host_; + } + + // Value 0 indicated that no port was specified explicitly. + // + std::uint16_t + port () const + { + if (local ()) + throw std::logic_error ("local location"); + + return port_; + } + + enum class protocol {http, https}; + + protocol + proto () const + { + if (local ()) + throw std::logic_error ("local location"); + + return proto_; + } + + // Note that this is not necessarily syntactically the same string + // as what was used to initialize this location. But it should be + // semantically equivalent. String representation of an empty + // location is the empty string. + // + std::string + string () const; + + private: + std::string canonical_name_; + protocol proto_; + std::string host_; + std::uint16_t port_; + butl::dir_path path_; + }; + + inline std::ostream& + operator<< (std::ostream& os, const repository_location& l) + { + return os << l.string (); + } + + enum class repository_role + { + base, + prerequisite, + complement + }; + + class LIBBPKG_EXPORT repository_manifest + { + public: + using email_type = bpkg::email; + + repository_location location; + butl::optional role; + + // The following values may only be present for the base repository. + // + butl::optional url; + butl::optional email; + butl::optional summary; + butl::optional description; + butl::optional certificate; + + // Return the effective role of the repository. If the role is not + // explicitly specified (see the role member above), then calculate + // the role based on the location. Specifically, if the location is + // empty, then the effective role is base. Otherwise -- prerequisite. + // If the role is specified, then verify that it is consistent with + // the location value (that is, base if the location is empty and + // prerequisite or complement if not) and return that. Otherwise, + // throw std::logic_error. + // + repository_role + effective_role () const; + + // Return the effective web interface URL based on the specified remote + // repository location. If url is not present or doesn't start with '.', + // then return it unchanged. Otherwise, process the relative format + // as described in the manifest specification. Throw std::invalid_argument + // if the relative url format is invalid or if the repository location is + // empty or local. + // + butl::optional + effective_url (const repository_location&) const; + + public: + repository_manifest () = default; // VC export. + repository_manifest (butl::manifest_parser&, bool ignore_unknown = false); + repository_manifest (butl::manifest_parser&, + butl::manifest_name_value start, + bool ignore_unknown = false); + + void + serialize (butl::manifest_serializer&) const; + }; + + class LIBBPKG_EXPORT repository_manifests: + public std::vector + { + public: + using base_type = std::vector; + + using base_type::base_type; + + repository_manifests () = default; + repository_manifests (butl::manifest_parser&, bool ignore_unknown = false); + + void + serialize (butl::manifest_serializer&) const; + }; + + class LIBBPKG_EXPORT signature_manifest + { + public: + // Checksum of the corresponding package_manifests. + // + std::string sha256sum; + + // Signature of the corresponding package_manifests. Calculated by + // encrypting package_manifests checksum (stored in sha256sum) with the + // repository certificate private key. + // + std::vector signature; + + public: + signature_manifest () = default; + signature_manifest (butl::manifest_parser&, bool ignore_unknown = false); + + // Serialize sha256sum and base64-encoded representation of the signature. + // + void + serialize (butl::manifest_serializer&) const; + + private: + // Used for delegating in public constructor. Strictly speaking is not + // required, as a signature_manifest currently never appears as a part of + // a manifest list, but kept for the consistency with other manifests + // implementations. + // + signature_manifest (butl::manifest_parser&, + butl::manifest_name_value start, + bool ignore_unknown); + }; +} + +#endif // LIBBPKG_MANIFEST_HXX -- cgit v1.1