aboutsummaryrefslogtreecommitdiff
path: root/bpkg
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2017-05-01 12:10:35 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2017-05-01 15:07:36 +0300
commitc4d2ac250aee4102b519ce1db89bde3fe7855639 (patch)
tree2a4436fb314673ea836c4d9d19fc7ddf3e954554 /bpkg
parent5fa80655ae4c87598313641d7322d28007e5fb31 (diff)
Add hxx extension for headers and lib prefix for library dirs
Diffstat (limited to 'bpkg')
-rw-r--r--bpkg/.gitignore1
-rw-r--r--bpkg/buildfile36
-rw-r--r--bpkg/export41
-rw-r--r--bpkg/manifest636
-rw-r--r--bpkg/manifest.cxx2190
-rw-r--r--bpkg/version.in44
6 files changed, 0 insertions, 2948 deletions
diff --git a/bpkg/.gitignore b/bpkg/.gitignore
deleted file mode 100644
index 088eda4..0000000
--- a/bpkg/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-version
diff --git a/bpkg/buildfile b/bpkg/buildfile
deleted file mode 100644
index ae87591..0000000
--- a/bpkg/buildfile
+++ /dev/null
@@ -1,36 +0,0 @@
-# file : bpkg/buildfile
-# copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-# license : MIT; see accompanying LICENSE file
-
-import int_libs = libbutl%lib{butl}
-
-lib{bpkg}: \
-{hxx }{ export } \
-{hxx cxx}{ manifest } \
-{hxx }{ version } \
- $int_libs
-
-hxx{version}: in{version} $src_root/file{manifest}
-hxx{version}: dist = true
-
-# For pre-releases use the complete version to make sure they cannot be used
-# in place of another pre-release or the final version.
-#
-if $version.pre_release
- lib{bpkg}: bin.lib.version = @"-$version.project_id"
-else
- lib{bpkg}: bin.lib.version = @"-$version.major.$version.minor"
-
-cxx.poptions =+ "-I$out_root" "-I$src_root"
-obja{*}: cxx.poptions += -DLIBBPKG_STATIC_BUILD
-objs{*}: cxx.poptions += -DLIBBPKG_SHARED_BUILD
-
-lib{bpkg}: cxx.export.poptions = "-I$out_root" "-I$src_root"
-liba{bpkg}: cxx.export.poptions += -DLIBBPKG_STATIC
-libs{bpkg}: cxx.export.poptions += -DLIBBPKG_SHARED
-
-lib{bpkg}: cxx.export.libs = $int_libs
-
-# Install into the bpkg/ subdirectory of, say, /usr/include/.
-#
-install.include = $install.include/bpkg/
diff --git a/bpkg/export b/bpkg/export
deleted file mode 100644
index fadf41d..0000000
--- a/bpkg/export
+++ /dev/null
@@ -1,41 +0,0 @@
-// file : bpkg/export -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BPKG_EXPORT
-#define BPKG_EXPORT
-
-// Normally we don't export class templates (but do complete specializations),
-// inline functions, and classes with only inline member functions. Exporting
-// classes that inherit from non-exported/imported bases (e.g., std::string)
-// will end up badly. The only known workarounds are to not inherit or to not
-// export. Also, MinGW GCC doesn't like seeing non-exported function being
-// used before their inline definition. The workaround is to reorder code. In
-// the end it's all trial and error.
-
-#if defined(LIBBPKG_STATIC) // Using static.
-# define LIBBPKG_EXPORT
-#elif defined(LIBBPKG_STATIC_BUILD) // Building static.
-# define LIBBPKG_EXPORT
-#elif defined(LIBBPKG_SHARED) // Using shared.
-# ifdef _WIN32
-# define LIBBPKG_EXPORT __declspec(dllimport)
-# else
-# define LIBBPKG_EXPORT
-# endif
-#elif defined(LIBBPKG_SHARED_BUILD) // Building shared.
-# ifdef _WIN32
-# define LIBBPKG_EXPORT __declspec(dllexport)
-# else
-# define LIBBPKG_EXPORT
-# endif
-#else
-// If none of the above macros are defined, then we assume we are being used
-// by some third-party build system that cannot/doesn't signal the library
-// type. Note that this fallback works for both static and shared but in case
-// of shared will be sub-optimal compared to having dllimport.
-//
-# define LIBBPKG_EXPORT // Using static or shared.
-#endif
-
-#endif // BPKG_EXPORT
diff --git a/bpkg/manifest b/bpkg/manifest
deleted file mode 100644
index d6caabf..0000000
--- a/bpkg/manifest
+++ /dev/null
@@ -1,636 +0,0 @@
-// file : bpkg/manifest -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef BPKG_MANIFEST
-#define BPKG_MANIFEST
-
-#include <string>
-#include <vector>
-#include <cassert>
-#include <cstdint> // uint16_t
-#include <ostream>
-#include <utility> // move()
-#include <stdexcept> // logic_error
-
-#include <butl/path>
-#include <butl/optional>
-#include <butl/manifest-forward>
-
-#include <bpkg/export>
-#include <bpkg/version>
-
-namespace bpkg
-{
- using strings = std::vector<std::string>;
-
- // @@ Let's create <bpkg/types> 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<std::string> 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<std::string> 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<std::string> 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 () ? "<empty-version>" : 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<version> min_version;
- butl::optional<version> max_version;
- bool min_open;
- bool max_open;
-
- dependency_constraint (butl::optional<version> min_version, bool min_open,
- butl::optional<version> 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<dependency_constraint> constraint;
- };
-
- LIBBPKG_EXPORT std::ostream&
- operator<< (std::ostream&, const dependency&);
-
- class dependency_alternatives: public std::vector<dependency>
- {
- 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_type> priority;
- std::string summary;
- std::vector<licenses> license_alternatives;
- strings tags;
- butl::optional<text_file> description;
- std::vector<text_file> changes;
- url_type url;
- butl::optional<url_type> package_url;
- email_type email;
- butl::optional<email_type> package_email;
- butl::optional<email_type> build_email;
- std::vector<dependency_alternatives> dependencies;
- std::vector<requirement_alternatives> requirements;
-
- // The following values are only valid in the manifest list.
- //
- butl::optional<butl::path> location;
- butl::optional<std::string> 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<package_manifest>
- {
- public:
- using base_type = std::vector<package_manifest>;
-
- 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<repository_role> role;
-
- // The following values may only be present for the base repository.
- //
- butl::optional<std::string> url;
- butl::optional<email_type> email;
- butl::optional<std::string> summary;
- butl::optional<std::string> description;
- butl::optional<std::string> 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<std::string>
- 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<repository_manifest>
- {
- public:
- using base_type = std::vector<repository_manifest>;
-
- 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<char> 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 // BPKG_MANIFEST
diff --git a/bpkg/manifest.cxx b/bpkg/manifest.cxx
deleted file mode 100644
index ef86b99..0000000
--- a/bpkg/manifest.cxx
+++ /dev/null
@@ -1,2190 +0,0 @@
-// file : bpkg/manifest.cxx -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#include <bpkg/manifest>
-
-#include <string>
-#include <ostream>
-#include <sstream>
-#include <cassert>
-#include <cstring> // strncmp(), strcmp()
-#include <utility> // move()
-#include <cstdint> // uint64_t, uint16_t, UINT16_MAX
-#include <iterator> // back_insert_iterator
-#include <algorithm> // find(), transform()
-#include <stdexcept> // invalid_argument
-
-#include <butl/path>
-#include <butl/base64>
-#include <butl/utility> // casecmp(), lcase(), alpha(), digit()
-#include <butl/manifest-parser>
-#include <butl/manifest-serializer>
-
-using namespace std;
-using namespace butl;
-
-namespace bpkg
-{
- using parser = manifest_parser;
- using parsing = manifest_parsing;
- using serializer = manifest_serializer;
- using serialization = manifest_serialization;
- using name_value = manifest_name_value;
-
- // Utility functions
- //
- static const strings priority_names ({"low", "medium", "high", "security"});
- static const strings repository_role_names (
- {"base", "prerequisite", "complement"});
-
- static const string spaces (" \t");
-
- inline static bool
- space (char c) noexcept
- {
- return c == ' ' || c == '\t';
- }
-
- inline static bool
- valid_sha256 (const string& s) noexcept
- {
- if (s.size () != 64)
- return false;
-
- for (const auto& c: s)
- {
- if ((c < 'a' || c > 'f' ) && !digit (c))
- return false;
- }
-
- return true;
- }
-
- // Resize v up to ';', return what goes after ';'.
- //
- inline static string
- add_comment (const string& v, const string& c)
- {
- return c.empty () ? v : (v + "; " + c);
- }
-
- static string
- split_comment (string& v)
- {
- using iterator = string::const_iterator;
-
- iterator b (v.begin ());
- iterator i (b);
- iterator ve (b); // End of value.
- iterator e (v.end ());
-
- // Find end of value (ve).
- //
- for (char c; i != e && (c = *i) != ';'; ++i)
- if (!space (c))
- ve = i + 1;
-
- // Find beginning of a comment (i).
- //
- if (i != e)
- {
- // Skip spaces.
- //
- for (++i; i != e && space (*i); ++i);
- }
-
- string c (i, e);
- v.resize (ve - b);
- return c;
- }
-
- template <typename T>
- static string
- concatenate (const T& s, const char* delim = ", ")
- {
- ostringstream o;
- for (auto b (s.begin ()), i (b), e (s.end ()); i != e; ++i)
- {
- if (i != b)
- o << delim;
-
- o << *i;
- }
-
- return o.str ();
- }
-
- // list_parser
- //
- class list_parser
- {
- public:
- using iterator = string::const_iterator;
-
- public:
- list_parser (iterator b, iterator e, char d = ',')
- : pos_ (b), end_ (e), delim_ (d) {}
-
- string
- next ();
-
- private:
- iterator pos_;
- iterator end_;
- char delim_;
- };
-
- string list_parser::
- next ()
- {
- string r;
-
- // Continue until get non empty list item.
- //
- while (pos_ != end_ && r.empty ())
- {
- // Skip spaces.
- //
- for (; pos_ != end_ && space (*pos_); ++pos_);
-
- iterator i (pos_);
- iterator e (pos_); // End of list item.
-
- for (char c; i != end_ && (c = *i) != delim_; ++i)
- {
- if (!space (c))
- e = i + 1;
- }
-
- if (e - pos_ > 0)
- r.assign (pos_, e);
-
- pos_ = i == end_ ? i : i + 1;
- }
-
- return r;
- }
-
- // version
- //
- version::
- version (uint16_t e, std::string u, optional<std::string> l, uint16_t r)
- : epoch (e),
- upstream (move (u)),
- release (move (l)),
- revision (r),
- canonical_upstream (
- data_type (upstream.c_str (), data_type::parse::upstream).
- canonical_upstream),
- canonical_release (
- data_type (release ? release->c_str () : nullptr,
- data_type::parse::release).
- canonical_release)
- {
- // Check members constrains.
- //
- if (upstream.empty ()) // Constructing empty version.
- {
- if (epoch != 0)
- throw invalid_argument ("epoch for empty version");
-
- if (!release || !release->empty ())
- throw invalid_argument ("not-empty release for empty version");
-
- if (revision != 0)
- throw invalid_argument ("revision for empty version");
- }
- else if (release && release->empty () && revision != 0)
- // Empty release signifies the earliest possible release. Revision is
- // meaningless in such a context.
- //
- throw invalid_argument ("revision for earliest possible release");
- }
-
- // Builder of the upstream or release version part canonical representation.
- //
- struct canonical_part: string
- {
- string
- final () const {return substr (0, len_);}
-
- void
- add (const char* begin, const char* end, bool numeric)
- {
- if (!empty ())
- append (1, '.');
-
- bool zo (false); // Digit-only zero component.
- if (numeric)
- {
- if (end - begin > 16)
- throw invalid_argument ("16 digits maximum allowed in a component");
-
- append (16 - (end - begin), '0'); // Add padding zeros.
-
- string c (begin, end);
- append (c);
- zo = stoul (c) == 0;
- }
- else
- append (lcase (begin, end - begin));
-
- if (!zo)
- len_ = size ();
- }
-
- private:
- size_t len_ = 0; // Length without the trailing digit-only zero components.
- };
-
- version::data_type::
- data_type (const char* v, parse pr): epoch (0), revision (0)
- {
- // Otherwise compiler gets confused with string() member.
- //
- using std::string;
-
- if (pr == parse::release && v == nullptr)
- {
- // Special case: final version release part.
- //
- canonical_release = "~";
- return;
- }
-
- assert (v != nullptr);
-
- auto bad_arg ([](const string& d) {throw invalid_argument (d);});
-
- auto uint16 (
- [&bad_arg](const string& s, const char* what) -> uint16_t
- {
- unsigned long long v (stoull (s));
-
- if (v > UINT16_MAX) // From <cstdint>.
- bad_arg (string (what) + " should be 2-byte unsigned integer");
-
- return static_cast<uint16_t> (v);
- });
-
- enum class mode {epoch, upstream, release, revision};
- mode m (pr == parse::full
- ? mode::epoch
- : pr == parse::upstream
- ? mode::upstream
- : mode::release);
-
- canonical_part canon_upstream;
- canonical_part canon_release;
-
- canonical_part* canon_part (
- pr == parse::release ? &canon_release : &canon_upstream);
-
- const char* cb (v); // Begin of a component.
- const char* ub (v); // Begin of upstream part.
- const char* ue (v); // End of upstream part.
- const char* rb (v); // Begin of release part.
- const char* re (v); // End of release part.
- const char* lnn (v - 1); // Last non numeric char.
-
- const char* p (v);
- for (char c; (c = *p) != '\0'; ++p)
- {
- switch (c)
- {
- case '~':
- {
- if (pr != parse::full)
- bad_arg ("unexpected '~' character");
-
- // Process the epoch part.
- //
- if (m != mode::epoch || p == v)
- bad_arg ("unexpected '~' character position");
-
- if (lnn >= cb) // Contains non-digits.
- bad_arg ("epoch should be 2-byte unsigned integer");
-
- epoch = uint16 (string (cb, p), "epoch");
-
- m = mode::upstream;
- cb = p + 1;
- ub = cb;
- break;
- }
-
- case '+':
- case '-':
- case '.':
- {
- // Process the upsteam or release part component.
- //
-
- // Characters '+', '-' are only valid for the full version parsing.
- //
- if (c != '.' && pr != parse::full)
- bad_arg (string ("unexpected '") + c + "' character");
-
- // Check if the component ending is valid for the current parsing
- // state.
- //
- if (m == mode::revision || (c == '-' && m == mode::release) ||
- p == cb)
- bad_arg (string ("unexpected '") + c + "' character position");
-
- // Append the component to the current canonical part.
- //
- canon_part->add (cb, p, lnn < cb);
-
- // Update the parsing state.
- //
- cb = p + 1;
-
- if (m == mode::upstream || m == mode::epoch)
- ue = p;
- else if (m == mode::release)
- re = p;
- else
- assert (false);
-
- if (c == '+')
- m = mode::revision;
- else if (c == '-')
- {
- m = mode::release;
- canon_part = &canon_release;
- rb = cb;
- re = cb;
- }
- else if (m == mode::epoch)
- m = mode::upstream;
-
- break;
- }
- default:
- {
- if (!digit (c) && !alpha (c))
- bad_arg ("alpha-numeric characters expected in a component");
- }
- }
-
- if (!digit (c))
- lnn = p;
- }
-
- assert (p >= cb); // 'p' denotes the end of the last component.
-
- // An empty component is valid for the release part, and for the upstream
- // part when constructing empty or max limit version.
- //
- if (p == cb && m != mode::release && pr != parse::upstream)
- bad_arg ("unexpected end");
-
- // Parse the last component.
- //
- if (m == mode::revision)
- {
- if (lnn >= cb) // Contains non-digits.
- bad_arg ("revision should be 2-byte unsigned integer");
-
- revision = uint16 (cb, "revision");
- }
- else if (cb != p)
- {
- canon_part->add (cb, p, lnn < cb);
-
- if (m == mode::epoch || m == mode::upstream)
- ue = p;
- else if (m == mode::release)
- re = p;
- }
-
- // Upstream and release pointer ranges are valid at the end of the day.
- //
- assert (ub <= ue && rb <= re);
-
- if (pr != parse::release)
- {
- // Fill upstream original and canonical parts.
- //
- if (!canon_upstream.empty ())
- {
- assert (ub != ue); // Can't happen if through all previous checks.
- canonical_upstream = canon_upstream.final ();
-
- if (pr == parse::full)
- upstream.assign (ub, ue);
- }
- }
-
- if (pr != parse::upstream)
- {
- // Fill release original and canonical parts.
- //
- if (!canon_release.empty ())
- {
- assert (rb != re); // Can't happen if through all previous checks.
- canonical_release = canon_release.final ();
-
- if (pr == parse::full)
- release = string (rb, re);
- }
- else
- {
- if (m == mode::release)
- {
- // Empty release part signifies the earliest possible version
- // release. Make original, and keep canonical representations empty.
- //
- if (pr == parse::full)
- release = "";
- }
- else
- {
- // Absent release part signifies the final (max) version release.
- // Assign the special value to the canonical representation, keep
- // the original one nullopt.
- //
- canonical_release = "~";
- }
- }
- }
-
- if (pr == parse::full && epoch == 0 && canonical_upstream.empty () &&
- canonical_release.empty ())
- {
- assert (revision == 0); // Can't happen if through all previous checks.
- bad_arg ("empty version");
- }
- }
-
- version& version::
- operator= (const version& v)
- {
- if (this != &v)
- *this = version (v); // Reduce to move-assignment.
- return *this;
- }
-
- version& version::
- operator= (version&& v)
- {
- if (this != &v)
- {
- this->~version ();
- new (this) version (move (v)); // Assume noexcept move-construction.
- }
- return *this;
- }
-
- string version::
- string (bool ignore_revision) const
- {
- if (empty ())
- throw logic_error ("empty version");
-
- std::string v (epoch != 0 ? to_string (epoch) + "~" + upstream : upstream);
-
- if (release)
- {
- v += '-';
- v += *release;
- }
-
- if (!ignore_revision && revision != 0)
- {
- v += '+';
- v += to_string (revision);
- }
-
- return v;
- }
-
- // text_file
- //
- text_file::
- ~text_file ()
- {
- if (file)
- path.~path_type ();
- else
- text.~string ();
- }
-
- text_file::
- text_file (text_file&& f): file (f.file), comment (move (f.comment))
- {
- if (file)
- new (&path) path_type (move (f.path));
- else
- new (&text) string (move (f.text));
- }
-
- text_file::
- text_file (const text_file& f): file (f.file), comment (f.comment)
- {
- if (file)
- new (&path) path_type (f.path);
- else
- new (&text) string (f.text);
- }
-
- text_file& text_file::
- operator= (text_file&& f)
- {
- if (this != &f)
- {
- this->~text_file ();
- new (this) text_file (move (f)); // Assume noexcept move-construction.
- }
- return *this;
- }
-
- text_file& text_file::
- operator= (const text_file& f)
- {
- if (this != &f)
- *this = text_file (f); // Reduce to move-assignment.
- return *this;
- }
-
- // depends
- //
-
- dependency_constraint::
- dependency_constraint (optional<version> mnv, bool mno,
- optional<version> mxv, bool mxo)
- : min_version (move (mnv)),
- max_version (move (mxv)),
- min_open (mno),
- max_open (mxo)
- {
- assert (
- // Min and max versions can't both be absent.
- //
- (min_version || max_version) &&
-
- // Version should be non-empty.
- //
- (!min_version || !min_version->empty ()) &&
- (!max_version || !max_version->empty ()) &&
-
- // Absent version endpoint (infinity) should be open.
- //
- (min_version || min_open) && (max_version || max_open));
-
- if (min_version && max_version)
- {
- if (*min_version > *max_version)
- throw invalid_argument ("min version is greater than max version");
-
- if (*min_version == *max_version && (min_open || max_open))
- throw invalid_argument ("equal version endpoints not closed");
- }
- }
-
- ostream&
- operator<< (ostream& o, const dependency_constraint& c)
- {
- assert (!c.empty ());
-
- if (!c.min_version)
- return o << (c.max_open ? "< " : "<= ") << *c.max_version;
-
- if (!c.max_version)
- return o << (c.min_open ? "> " : ">= ") << *c.min_version;
-
- if (*c.min_version == *c.max_version)
- return o << "== " << *c.min_version;
-
- return o << (c.min_open ? '(' : '[') << *c.min_version << " "
- << *c.max_version << (c.max_open ? ')' : ']');
- }
-
- ostream&
- operator<< (ostream& o, const dependency& d)
- {
- o << d.name;
-
- if (d.constraint)
- o << ' ' << *d.constraint;
-
- return o;
- }
-
- ostream&
- operator<< (ostream& o, const dependency_alternatives& as)
- {
- if (as.conditional)
- o << '?';
-
- if (as.buildtime)
- o << '*';
-
- if (as.conditional || as.buildtime)
- o << ' ';
-
- bool f (true);
- for (const dependency& a: as)
- o << (f ? (f = false, "") : " | ") << a;
-
- if (!as.comment.empty ())
- o << "; " << as.comment;
-
- return o;
- }
-
- // package_manifest
- //
- package_manifest::
- package_manifest (parser& p, bool iu)
- : package_manifest (p, p.next (), false, iu) // Delegate
- {
- // Make sure this is the end.
- //
- name_value nv (p.next ());
- if (!nv.empty ())
- throw parsing (p.name (), nv.name_line, nv.name_column,
- "single package manifest expected");
- }
-
- package_manifest::
- package_manifest (parser& p, name_value nv, bool iu)
- : package_manifest (p, nv, true, iu) // Delegate
- {
- }
-
- package_manifest::
- package_manifest (parser& p, name_value nv, bool il, bool iu)
- {
- auto bad_name ([&p, &nv](const string& d) {
- throw parsing (p.name (), nv.name_line, nv.name_column, d);});
-
- auto bad_value ([&p, &nv](const string& d) {
- throw parsing (p.name (), nv.value_line, nv.value_column, d);});
-
- // Make sure this is the start and we support the version.
- //
- if (!nv.name.empty ())
- bad_name ("start of package manifest expected");
-
- if (nv.value != "1")
- bad_value ("unsupported format version");
-
- for (nv = p.next (); !nv.empty (); nv = p.next ())
- {
- string& n (nv.name);
- string& v (nv.value);
-
- if (n == "name")
- {
- if (!name.empty ())
- bad_name ("package name redefinition");
-
- if (v.empty ())
- bad_value ("empty package name");
-
- name = move (v);
- }
- else if (n == "version")
- {
- if (!version.empty ())
- bad_name ("package version redefinition");
-
- try
- {
- version = version_type (move (v));
- }
- catch (const invalid_argument& e)
- {
- bad_value (string ("invalid package version: ") + e.what ());
- }
-
- // Versions like 1.2.3- are forbidden in manifest as intended to be
- // used for version constrains rather than actual releases.
- //
- if (version.release && version.release->empty ())
- bad_value ("invalid package version release");
- }
- else if (n == "summary")
- {
- if (!summary.empty ())
- bad_name ("package summary redefinition");
-
- if (v.empty ())
- bad_value ("empty package summary");
-
- summary = move (v);
- }
- else if (n == "tags")
- {
- if (!tags.empty ())
- bad_name ("package tags redefinition");
-
- list_parser lp (v.begin (), v.end ());
- for (string lv (lp.next ()); !lv.empty (); lv = lp.next ())
- {
- if (lv.find_first_of (spaces) != string::npos)
- bad_value ("only single-word tags allowed");
-
- tags.push_back (move (lv));
- }
-
- if (tags.empty ())
- bad_value ("empty package tags specification");
- }
- else if (n == "description")
- {
- if (description)
- {
- if (description->file)
- bad_name ("package description and description-file are "
- "mutually exclusive");
- else
- bad_name ("package description redefinition");
- }
-
- if (v.empty ())
- bad_value ("empty package description");
-
- description = text_file (move (v));
- }
- else if (n == "description-file")
- {
- if (il)
- bad_name ("package description-file not allowed");
-
- if (description)
- {
- if (description->file)
- bad_name ("package description-file redefinition");
- else
- bad_name ("package description-file and description are "
- "mutually exclusive");
- }
-
- string c (split_comment (v));
-
- if (v.empty ())
- bad_value ("no path in package description-file");
-
- path p (v);
-
- if (p.absolute ())
- bad_value ("package description-file path is absolute");
-
- description = text_file (move (p), move (c));
- }
- else if (n == "changes")
- {
- if (v.empty ())
- bad_value ("empty package changes specification");
-
- changes.emplace_back (move (v));
- }
- else if (n == "changes-file")
- {
- if (il)
- bad_name ("package changes-file not allowed");
-
- string c (split_comment (v));
-
- if (v.empty ())
- bad_value ("no path in package changes-file");
-
- path p (v);
-
- if (p.absolute ())
- bad_value ("package changes-file path is absolute");
-
- changes.emplace_back (move (p), move (c));
- }
- else if (n == "url")
- {
- if (!url.empty ())
- bad_name ("project url redefinition");
-
- string c (split_comment (v));
-
- if (v.empty ())
- bad_value ("empty project url");
-
- url = url_type (move (v), move (c));
- }
- else if (n == "email")
- {
- if (!email.empty ())
- bad_name ("project email redefinition");
-
- string c (split_comment (v));
-
- if (v.empty ())
- bad_value ("empty project email");
-
- email = email_type (move (v), move (c));
- }
- else if (n == "package-url")
- {
- if (package_url)
- bad_name ("package url redefinition");
-
- string c (split_comment (v));
-
- if (v.empty ())
- bad_value ("empty package url");
-
- package_url = url_type (move (v), move (c));
- }
- else if (n == "package-email")
- {
- if (package_email)
- bad_name ("package email redefinition");
-
- string c (split_comment (v));
-
- if (v.empty ())
- bad_value ("empty package email");
-
- package_email = email_type (move (v), move (c));
- }
- else if (n == "build-email")
- {
- if (build_email)
- bad_name ("build email redefinition");
-
- string c (split_comment (v));
-
- build_email = email_type (move (v), move (c));
- }
- else if (n == "priority")
- {
- if (priority)
- bad_name ("package priority redefinition");
-
- string c (split_comment (v));
- strings::const_iterator b (priority_names.begin ());
- strings::const_iterator e (priority_names.end ());
- strings::const_iterator i (find (b, e, v));
-
- if (i == e)
- bad_value ("invalid package priority");
-
- priority =
- priority_type (static_cast<priority_type::value_type> (i - b),
- move (c));
- }
- else if (n == "license")
- {
- licenses l (split_comment (v));
-
- list_parser lp (v.begin (), v.end ());
- for (string lv (lp.next ()); !lv.empty (); lv = lp.next ())
- l.push_back (move (lv));
-
- if (l.empty ())
- bad_value ("empty package license specification");
-
- license_alternatives.push_back (move (l));
- }
- else if (n == "requires")
- {
- // Allow specifying ?* in any order.
- //
- size_t m (v.size ());
- size_t cond ((m > 0 && v[0] == '?') || (m > 1 && v[1] == '?') ? 1 : 0);
- size_t btim ((m > 0 && v[0] == '*') || (m > 1 && v[1] == '*') ? 1 : 0);
-
- requirement_alternatives ra (cond != 0, btim != 0, split_comment (v));
- string::const_iterator b (v.begin ());
- string::const_iterator e (v.end ());
-
- if (ra.conditional || ra.buildtime)
- {
- string::size_type p (v.find_first_not_of (spaces, cond + btim));
- b = p == string::npos ? e : b + p;
- }
-
- list_parser lp (b, e, '|');
- for (string lv (lp.next ()); !lv.empty (); lv = lp.next ())
- ra.push_back (lv);
-
- if (ra.empty () && ra.comment.empty ())
- bad_value ("empty package requirement specification");
-
- requirements.push_back (move (ra));
- }
- else if (n == "depends")
- {
- // Allow specifying ?* in any order.
- //
- size_t m (v.size ());
- size_t cond ((m > 0 && v[0] == '?') || (m > 1 && v[1] == '?') ? 1 : 0);
- size_t btim ((m > 0 && v[0] == '*') || (m > 1 && v[1] == '*') ? 1 : 0);
-
- dependency_alternatives da (cond != 0, btim != 0, split_comment (v));
- string::const_iterator b (v.begin ());
- string::const_iterator e (v.end ());
-
- if (da.conditional || da.buildtime)
- {
- string::size_type p (v.find_first_not_of (spaces, cond + btim));
- b = p == string::npos ? e : b + p;
- }
-
- list_parser lp (b, e, '|');
- for (string lv (lp.next ()); !lv.empty (); lv = lp.next ())
- {
- using iterator = string::const_iterator;
-
- iterator b (lv.begin ());
- iterator i (b);
- iterator ne (b); // End of name.
- iterator e (lv.end ());
-
- // Find end of name (ne).
- //
- static const string cb ("=<>([");
- for (char c; i != e && cb.find (c = *i) == string::npos; ++i)
- {
- if (!space (c))
- ne = i + 1;
- }
-
- if (i == e)
- da.push_back (dependency {lv, nullopt});
- else
- {
- string nm (b, ne);
-
- if (nm.empty ())
- bad_value ("prerequisite package name not specified");
-
- // Got to version range.
- //
- dependency_constraint dc;
- const char* op (&*i);
- char mnv (*op);
- if (mnv == '(' || mnv == '[')
- {
- bool min_open (mnv == '(');
-
- string::size_type pos (lv.find_first_not_of (spaces, ++i - b));
- if (pos == string::npos)
- bad_value ("no prerequisite package min version specified");
-
- i = b + pos;
- pos = lv.find_first_of (spaces, pos);
-
- static const char* no_max_version (
- "no prerequisite package max version specified");
-
- if (pos == string::npos)
- bad_value (no_max_version);
-
- version_type min_version;
-
- try
- {
- min_version = version_type (string (i, b + pos));
- }
- catch (const invalid_argument& e)
- {
- bad_value (
- string ("invalid prerequisite package min version: ") +
- e.what ());
- }
-
- pos = lv.find_first_not_of (spaces, pos);
- if (pos == string::npos)
- bad_value (no_max_version);
-
- i = b + pos;
- static const string mve (spaces + "])");
- pos = lv.find_first_of (mve, pos);
-
- static const char* invalid_range (
- "invalid prerequisite package version range");
-
- if (pos == string::npos)
- bad_value (invalid_range);
-
- version_type max_version;
-
- try
- {
- max_version = version_type (string (i, b + pos));
- }
- catch (const invalid_argument& e)
- {
- bad_value (
- string ("invalid prerequisite package max version: ") +
- e.what ());
- }
-
- pos = lv.find_first_of ("])", pos); // Might be a space.
- if (pos == string::npos)
- bad_value (invalid_range);
-
- try
- {
- dc = dependency_constraint (move (min_version),
- min_open,
- move (max_version),
- lv[pos] == ')');
- }
- catch (const invalid_argument& e)
- {
- bad_value (
- string ("invalid dependency constraint: ") + e.what ());
- }
-
- if (lv[pos + 1] != '\0')
- bad_value (
- "unexpected text after prerequisite package version range");
- }
- else
- {
- // Version comparison notation.
- //
- enum comparison {eq, lt, gt, le, ge};
- comparison operation (eq); // Uninitialized warning.
-
- if (strncmp (op, "==", 2) == 0)
- {
- operation = eq;
- i += 2;
- }
- else if (strncmp (op, ">=", 2) == 0)
- {
- operation = ge;
- i += 2;
- }
- else if (strncmp (op, "<=", 2) == 0)
- {
- operation = le;
- i += 2;
- }
- else if (*op == '>')
- {
- operation = gt;
- ++i;
- }
- else if (*op == '<')
- {
- operation = lt;
- ++i;
- }
- else
- bad_value ("invalid prerequisite package version comparison");
-
- string::size_type pos (lv.find_first_not_of (spaces, i - b));
-
- if (pos == string::npos)
- bad_value ("no prerequisite package version specified");
-
- version_type v;
-
- try
- {
- v = version_type (lv.c_str () + pos);
- }
- catch (const invalid_argument& e)
- {
- bad_value (string ("invalid prerequisite package version: ") +
- e.what ());
- }
-
- switch (operation)
- {
- case comparison::eq:
- dc = dependency_constraint (v);
- break;
- case comparison::lt:
- dc = dependency_constraint (nullopt, true, move (v), true);
- break;
- case comparison::le:
- dc = dependency_constraint (nullopt, true, move (v), false);
- break;
- case comparison::gt:
- dc = dependency_constraint (move (v), true, nullopt, true);
- break;
- case comparison::ge:
- dc = dependency_constraint (move (v), false, nullopt, true);
- break;
- }
- }
-
- dependency d {move (nm), move (dc)};
- da.push_back (move (d));
- }
- }
-
- if (da.empty ())
- bad_value ("empty package dependency specification");
-
- dependencies.push_back (da);
- }
- else if (n == "location")
- {
- if (!il)
- bad_name ("package location not allowed");
-
- if (location)
- bad_name ("package location redefinition");
-
- try
- {
- path l (v);
-
- if (l.empty ())
- bad_value ("empty package location");
-
- if (l.absolute ())
- bad_value ("absolute package location");
-
- location = move (l);
- }
- catch (const invalid_path&)
- {
- bad_value ("invalid package location");
- }
- }
- else if (n == "sha256sum")
- {
- if (!il)
- bad_name ("package sha256sum not allowed");
-
- if (sha256sum)
- bad_name ("package sha256sum redefinition");
-
- if (!valid_sha256 (v))
- bad_value ("invalid package sha256sum");
-
- sha256sum = move (v);
- }
- else if (!iu)
- bad_name ("unknown name '" + n + "' in package manifest");
- }
-
- // Verify all non-optional values were specified.
- //
- if (name.empty ())
- bad_value ("no package name specified");
- else if (version.empty ())
- bad_value ("no package version specified");
- else if (summary.empty ())
- bad_value ("no package summary specified");
- else if (url.empty ())
- bad_value ("no project url specified");
- else if (email.empty ())
- bad_value ("no project email specified");
- else if (license_alternatives.empty ())
- bad_value ("no project license specified");
-
- if (il)
- {
- if (!location)
- bad_name ("no package location specified");
-
- if (!sha256sum)
- bad_name ("no package sha256sum specified");
- }
- }
-
- void package_manifest::
- serialize (serializer& s) const
- {
- // @@ Should we check that all non-optional values are specified ?
- // @@ Should we check that values are valid: name is not empty, version
- // release is not empty, sha256sum is a proper string, ...?
- // @@ Currently we don't know if we are serializing the individual package
- // manifest or the package list manifest, so can't ensure all values
- // allowed in the current context (location, sha256sum, *-file values).
- //
-
- s.next ("", "1"); // Start of manifest.
- s.next ("name", name);
- s.next ("version", version.string ());
-
- if (priority)
- {
- priority::value_type v (*priority);
- assert (v < priority_names.size ());
- s.next ("priority", add_comment (priority_names[v], priority->comment));
- }
-
- s.next ("summary", summary);
-
- for (const auto& la: license_alternatives)
- s.next ("license", add_comment (concatenate (la), la.comment));
-
- if (!tags.empty ())
- s.next ("tags", concatenate (tags));
-
- if (description)
- {
- if (description->file)
- s.next ("description-file",
- add_comment (
- description->path.string (), description->comment));
- else
- s.next ("description", description->text);
- }
-
- for (const auto& c: changes)
- {
- if (c.file)
- s.next ("changes-file", add_comment (c.path.string (), c.comment));
- else
- s.next ("changes", c.text);
- }
-
- s.next ("url", add_comment (url, url.comment));
-
- if (package_url)
- s.next ("package-url", add_comment (*package_url, package_url->comment));
-
- s.next ("email", add_comment (email, email.comment));
-
- if (package_email)
- s.next ("package-email",
- add_comment (*package_email, package_email->comment));
-
- if (build_email)
- s.next ("build-email",
- add_comment (*build_email, build_email->comment));
-
- for (const auto& d: dependencies)
- s.next ("depends",
- (d.conditional
- ? (d.buildtime ? "?* " : "? ")
- : (d.buildtime ? "* " : "")) +
- add_comment (concatenate (d, " | "), d.comment));
-
- for (const auto& r: requirements)
- s.next ("requires",
- (r.conditional
- ? (r.buildtime ? "?* " : "? ")
- : (r.buildtime ? "* " : "")) +
- add_comment (concatenate (r, " | "), r.comment));
-
- if (location)
- s.next ("location", location->posix_string ());
-
- if (sha256sum)
- s.next ("sha256sum", *sha256sum);
-
- s.next ("", ""); // End of manifest.
- }
-
- // package_manifests
- //
- package_manifests::
- package_manifests (parser& p, bool iu)
- {
- name_value nv (p.next ());
-
- auto bad_name ([&p, &nv](const string& d) {
- throw parsing (p.name (), nv.name_line, nv.name_column, d);});
-
- auto bad_value ([&p, &nv](const string& d) {
- throw parsing (p.name (), nv.value_line, nv.value_column, d);});
-
- // Make sure this is the start and we support the version.
- //
- if (!nv.name.empty ())
- bad_name ("start of package list manifest expected");
-
- if (nv.value != "1")
- bad_value ("unsupported format version");
-
- // Parse the package list manifest.
- //
- for (nv = p.next (); !nv.empty (); nv = p.next ())
- {
- string& n (nv.name);
- string& v (nv.value);
-
- if (n == "sha256sum")
- {
- if (!sha256sum.empty ())
- bad_name ("sha256sum redefinition");
-
- if (!valid_sha256 (v))
- bad_value ("invalid sha256sum");
-
- sha256sum = move (v);
- }
- else if (!iu)
- bad_name ("unknown name '" + n + "' in package list manifest");
- }
-
- // Verify all non-optional values were specified.
- //
- if (sha256sum.empty ())
- bad_value ("no sha256sum specified");
-
- // Parse package manifests.
- //
- for (nv = p.next (); !nv.empty (); nv = p.next ())
- push_back (package_manifest (p, nv, iu));
- }
-
- void package_manifests::
- serialize (serializer& s) const
- {
- // Serialize the package list manifest.
- //
- // @@ Should we check that values are valid ?
- //
- s.next ("", "1"); // Start of manifest.
- s.next ("sha256sum", sha256sum);
- s.next ("", ""); // End of manifest.
-
- // Serialize package manifests.
- //
- for (const package_manifest& p: *this)
- {
- auto bad_value = [&p, &s](const string& d)
- {
- throw
- serialization (
- s.name (), d + " for " + p.name + "-" + p.version.string ());
- };
-
- if (p.description && p.description->file)
- bad_value ("forbidden description-file");
-
- for (const auto& c: p.changes)
- if (c.file)
- bad_value ("forbidden changes-file");
-
- if (!p.location)
- bad_value ("no valid location");
-
- if (!p.sha256sum)
- bad_value ("no valid sha256sum");
-
- p.serialize (s);
- }
-
- s.next ("", ""); // End of stream.
- }
-
- // url_parts
- //
- struct url_parts
- {
- using protocol = repository_location::protocol;
-
- protocol proto;
- string host;
- uint16_t port;
- dir_path path;
-
- explicit
- url_parts (const string&);
- };
-
- // Return the URL protocol, or nullopt if location is not a URL.
- //
- static optional<url_parts::protocol>
- is_url (const string& location)
- {
- using protocol = url_parts::protocol;
-
- optional<protocol> p;
- if (casecmp (location, "http://", 7) == 0)
- p = protocol::http;
- else if (casecmp (location, "https://", 8) == 0)
- p = protocol::https;
-
- return p;
- }
-
- static string
- to_string (url_parts::protocol proto,
- const string& host,
- uint16_t port,
- const dir_path& path)
- {
- string u (
- (proto == url_parts::protocol::http ? "http://" : "https://") + host);
-
- if (port != 0)
- u += ":" + std::to_string (port);
-
- if (!path.empty ())
- u += "/" + path.posix_string ();
-
- return u;
- }
-
- url_parts::
- url_parts (const string& s)
- {
- optional<protocol> pr (is_url (s));
- if (!pr)
- throw invalid_argument ("invalid protocol");
-
- proto = *pr;
-
- string::size_type host_offset (s.find ("//"));
- assert (host_offset != string::npos);
- host_offset += 2;
-
- string::size_type p (s.find ('/', host_offset));
-
- if (p != string::npos)
- // Chop the path part. Path is saved as a relative one to be of the
- // same type on different operating systems including Windows.
- //
- path = dir_path (s, p + 1, string::npos);
-
- // Put the lower-cased version of the host part into host.
- // Chances are good it will stay unmodified.
- //
- transform (s.cbegin () + host_offset,
- p == string::npos ? s.cend () : s.cbegin () + p,
- back_inserter (host),
- static_cast<char (*) (char)> (lcase));
-
- // Validate host name according to "2.3.1. Preferred name syntax" and
- // "2.3.4. Size limits" of https://tools.ietf.org/html/rfc1035.
- //
- // Check that there is no empty labels and ones containing chars
- // different from alpha-numeric and hyphen. Label should start from
- // letter, do not end with hypen and be not longer than 63 chars.
- // Total host name length should be not longer than 255 chars.
- //
- auto hb (host.cbegin ());
- auto he (host.cend ());
- auto ls (hb); // Host domain name label begin.
- auto pt (he); // Port begin.
-
- for (auto i (hb); i != he; ++i)
- {
- char c (*i);
-
- if (pt == he) // Didn't reach port specification yet.
- {
- if (c == ':') // Port specification reached.
- pt = i;
- else
- {
- auto n (i + 1);
-
- // Validate host name.
- //
-
- // Is first label char.
- //
- bool flc (i == ls);
-
- // Is last label char.
- //
- bool llc (n == he || *n == '.' || *n == ':');
-
- // Validate char.
- //
- bool valid (alpha (c) ||
- (digit (c) && !flc) ||
- ((c == '-' || c == '.') && !flc && !llc));
-
- // Validate length.
- //
- if (valid)
- valid = i - ls < 64 && i - hb < 256;
-
- if (!valid)
- throw invalid_argument ("invalid host");
-
- if (c == '.')
- ls = n;
- }
- }
- else
- {
- // Validate port.
- //
- if (!digit (c))
- throw invalid_argument ("invalid port");
- }
- }
-
- // Chop the port, if present.
- //
- if (pt == he)
- port = 0;
- else
- {
- unsigned long long n (++pt == he ? 0 : stoull (string (pt, he)));
- if (n == 0 || n > UINT16_MAX)
- throw invalid_argument ("invalid port");
-
- port = static_cast<uint16_t> (n);
- host.resize (pt - hb - 1);
- }
-
- if (host.empty ())
- throw invalid_argument ("invalid host");
- }
-
- // repository_location
- //
- static string
- strip_domain (const string& host)
- {
- assert (!host.empty ()); // Should be repository location host.
-
- string h;
- bool bpkg (false);
-
- if (host.compare (0, 4, "www.") == 0 ||
- host.compare (0, 4, "pkg.") == 0 ||
- (bpkg = host.compare (0, 5, "bpkg.") == 0))
- {
- if (h.assign (host, bpkg ? 5 : 4, string::npos).empty ())
- throw invalid_argument ("invalid host");
- }
- else
- h = host;
-
- return h;
- }
-
- // The 'pkg' path component stripping mode.
- //
- enum class strip_mode {version, component, path};
-
- static dir_path
- strip_path (const dir_path& path, strip_mode mode)
- {
- // Should be repository location path.
- //
- assert (!path.empty () && *path.begin () != "..");
-
- auto rb (path.rbegin ()), i (rb), re (path.rend ());
-
- // Find the version component.
- //
- for (; i != re; ++i)
- {
- const string& c (*i);
-
- if (!c.empty () && c.find_first_not_of ("1234567890") == string::npos)
- break;
- }
-
- if (i == re)
- throw invalid_argument ("missing repository version");
-
- // Validate the version. At the moment the only valid value is 1.
- //
- if (stoul (*i) != 1)
- throw invalid_argument ("unsupported repository version");
-
- dir_path res (rb, i);
-
- // Canonical name prefix part ends with the special "pkg" component.
- //
- bool pc (++i != re && (*i == "pkg" || *i == "bpkg"));
-
- if (pc && mode == strip_mode::component)
- ++i; // Strip the "pkg" component.
-
- if (!pc || mode != strip_mode::path)
- res = dir_path (i, re) / res; // Concatenate prefix and path parts.
-
- return res;
- }
-
- // Location parameter type is fully qualified as compiler gets confused with
- // string() member.
- //
- repository_location::
- repository_location (const std::string& l)
- : repository_location (l, repository_location ()) // Delegate.
- {
- if (!empty () && relative ())
- throw invalid_argument ("relative filesystem path");
- }
-
- repository_location::
- repository_location (const std::string& l, const repository_location& b)
- {
- // Otherwise compiler gets confused with string() member.
- //
- using std::string;
-
- if (l.empty ())
- {
- if (!b.empty ())
- throw invalid_argument ("empty location");
-
- return;
- }
-
- // Base repository location can not be a relative path.
- //
- if (!b.empty () && b.relative ())
- throw invalid_argument ("base relative filesystem path");
-
- if (is_url (l))
- {
- url_parts u (l);
- proto_ = u.proto;
- host_ = move (u.host);
- port_ = u.port;
- path_ = move (u.path);
-
- canonical_name_ = strip_domain (host_);
-
- // For canonical name and for the HTTP protocol, treat a.com and
- // a.com:80 as the same name. The same rule applies to the HTTPS
- // protocol and port 443.
- //
- if (port_ != 0 && port_ != (proto_ == protocol::http ? 80 : 443))
- canonical_name_ += ':' + std::to_string (port_);
- }
- else
- {
- path_ = dir_path (l);
-
- // Complete if we are relative and have base.
- //
- if (!b.empty () && path_.relative ())
- {
- // Convert the relative path location to an absolute or remote one.
- //
- proto_ = b.proto_;
- host_ = b.host_;
- port_ = b.port_;
- path_ = b.path_ / path_;
-
- // Set canonical name to the base location canonical name host
- // part. The path part of the canonical name is calculated below.
- //
- if (b.remote ())
- canonical_name_ =
- b.canonical_name_.substr (0, b.canonical_name_.find ("/"));
- }
- }
-
- // Normalize path to avoid different representations of the same location
- // and canonical name. So a/b/../c/1/x/../y and a/c/1/y to be considered
- // as same location.
- //
- try
- {
- path_.normalize ();
- }
- catch (const invalid_path&)
- {
- throw invalid_argument ("invalid path");
- }
-
- // Need to check path for emptiness before proceeding further as a valid
- // non empty location can not have an empty path_ member (which can be the
- // case for the remote location, but not for the relative or absolute).
- //
- if (path_.empty ())
- throw invalid_argument ("empty path");
-
- // Need to check that URL path do not go past the root directory of a WEB
- // server. We can not rely on the above normalize() function call doing
- // this check as soon as path_ member contains a relative directory for the
- // remote location.
- //
- if (remote () && *path_.begin () == "..")
- throw invalid_argument ("invalid path");
-
- // Finish calculating the canonical name, unless we are relative.
- //
- if (relative ())
- {
- assert (canonical_name_.empty ());
- return;
- }
-
- // Canonical name <prefix>/<path> part.
- //
- dir_path sp (strip_path (
- path_, remote () ? strip_mode::component : strip_mode::path));
-
- // If for an absolute path location the stripping result is empty (which
- // also means <path> part is empty as well) then fallback to stripping
- // just the version component.
- //
- if (absolute () && sp.empty ())
- sp = strip_path (path_, strip_mode::version);
-
- string cp (sp.relative () ? sp.posix_string () : sp.string ());
-
- // Note: allow empty paths (e.g., http://stable.cppget.org/1/).
- //
- if (!canonical_name_.empty () && !cp.empty ()) // If we have host and dir.
- canonical_name_ += '/';
-
- canonical_name_ += cp;
-
- // But don't allow empty canonical names.
- //
- if (canonical_name_.empty ())
- throw invalid_argument ("empty repository name");
- }
-
- string repository_location::
- string () const
- {
- if (empty ())
- return std::string (); // Also function name.
-
- if (local ())
- return relative () ? path_.posix_string () : path_.string ();
-
- return to_string (proto_, host_, port_, path_);
- }
-
- // repository_manifest
- //
- repository_manifest::
- repository_manifest (parser& p, bool iu)
- : repository_manifest (p, p.next (), iu) // Delegate
- {
- // Make sure this is the end.
- //
- name_value nv (p.next ());
- if (!nv.empty ())
- throw parsing (p.name (), nv.name_line, nv.name_column,
- "single repository manifest expected");
- }
-
- repository_manifest::
- repository_manifest (parser& p, name_value nv, bool iu)
- {
- auto bad_name ([&p, &nv](const string& d) {
- throw parsing (p.name (), nv.name_line, nv.name_column, d);});
-
- auto bad_value ([&p, &nv](const string& d) {
- throw parsing (p.name (), nv.value_line, nv.value_column, d);});
-
- // Make sure this is the start and we support the version.
- //
- if (!nv.name.empty ())
- bad_name ("start of repository manifest expected");
-
- if (nv.value != "1")
- bad_value ("unsupported format version");
-
- for (nv = p.next (); !nv.empty (); nv = p.next ())
- {
- string& n (nv.name);
- string& v (nv.value);
-
- if (n == "location")
- {
- if (!location.empty ())
- bad_name ("location redefinition");
-
- if (v.empty ())
- bad_value ("empty location");
-
- try
- {
- // Call prerequisite repository location constructor, do not
- // ammend relative path.
- //
- location = repository_location (move (v), repository_location ());
- }
- catch (const invalid_argument& e)
- {
- bad_value (e.what ());
- }
- }
- else if (n == "role")
- {
- if (role)
- bad_name ("role redefinition");
-
- auto b (repository_role_names.cbegin ());
- auto e (repository_role_names.cend ());
- auto i (find (b, e, v));
-
- if (i == e)
- bad_value ("unrecognized role");
-
- role = static_cast<repository_role> (i - b);
- }
- else if (n == "url")
- {
- if (url)
- bad_name ("url redefinition");
-
- if (v.empty ())
- bad_value ("empty url");
-
- url = move (v);
- }
- else if (n == "email")
- {
- if (email)
- bad_name ("email redefinition");
-
- string c (split_comment (v));
-
- if (v.empty ())
- bad_value ("empty email");
-
- email = email_type (move (v), move (c));
- }
- else if (n == "summary")
- {
- if (summary)
- bad_name ("summary redefinition");
-
- if (v.empty ())
- bad_value ("empty summary");
-
- summary = move (v);
- }
- else if (n == "description")
- {
- if (description)
- bad_name ("description redefinition");
-
- if (v.empty ())
- bad_value ("empty description");
-
- description = move (v);
- }
- else if (n == "certificate")
- {
- if (certificate)
- bad_name ("certificate redefinition");
-
- if (v.empty ())
- bad_value ("empty certificate");
-
- certificate = move (v);
- }
- else if (!iu)
- bad_name ("unknown name '" + n + "' in repository manifest");
- }
-
- // Verify all non-optional values were specified.
- //
- // - location can be omitted
- // - role can be omitted
- //
- if (role && location.empty () != (*role == repository_role::base))
- bad_value ("invalid role");
-
- if (effective_role () != repository_role::base)
- {
- if (url)
- bad_value ("url not allowed");
-
- if (email)
- bad_value ("email not allowed");
-
- if (summary)
- bad_value ("summary not allowed");
-
- if (description)
- bad_value ("description not allowed");
-
- if (certificate)
- bad_value ("certificate not allowed");
- }
- }
-
- void repository_manifest::
- serialize (serializer& s) const
- {
- auto bad_value ([&s](const string& d) {
- throw serialization (s.name (), d);});
-
- s.next ("", "1"); // Start of manifest.
-
- if (!location.empty ())
- s.next ("location", location.string ());
-
- if (role)
- {
- if (location.empty () != (*role == repository_role::base))
- bad_value ("invalid role");
-
- auto r (static_cast<size_t> (*role));
- assert (r < repository_role_names.size ());
- s.next ("role", repository_role_names[r]);
- }
-
- bool b (effective_role () == repository_role::base);
-
- if (url)
- {
- if (!b)
- bad_value ("url not allowed");
-
- s.next ("url", *url);
- }
-
- if (email)
- {
- if (!b)
- bad_value ("email not allowed");
-
- s.next ("email", add_comment (*email, email->comment));
- }
-
- if (summary)
- {
- if (!b)
- bad_value ("summary not allowed");
-
- s.next ("summary", *summary);
- }
-
- if (description)
- {
- if (!b)
- bad_value ("description not allowed");
-
- s.next ("description", *description);
- }
-
- if (certificate)
- {
- if (!b)
- bad_value ("certificate not allowed");
-
- s.next ("certificate", *certificate);
- }
-
- s.next ("", ""); // End of manifest.
- }
-
- repository_role repository_manifest::
- effective_role () const
- {
- if (role)
- {
- if (location.empty () != (*role == repository_role::base))
- throw logic_error ("invalid role");
-
- return *role;
- }
- else
- return location.empty ()
- ? repository_role::base
- : repository_role::prerequisite;
- }
-
- optional<string> repository_manifest::
- effective_url (const repository_location& l) const
- {
- if (!url || (*url)[0] != '.')
- return url;
-
- const dir_path rp (*url);
- auto i (rp.begin ());
-
- static const char* invalid_url ("invalid relative url");
-
- auto strip ([&i, &rp]() -> bool {
- if (i != rp.end ())
- {
- const auto& c (*i++);
- if (c == "..")
- return true;
-
- if (c == ".")
- return false;
- }
-
- throw invalid_argument (invalid_url);
- });
-
- bool strip_d (strip ()); // Strip domain.
- bool strip_p (strip ()); // Strip path.
-
- // The web interface relative path with the special first two components
- // stripped.
- //
- const dir_path rpath (i, rp.end ());
- assert (rpath.relative ());
-
- url_parts u (l.string ());
-
- // Web interface URL path part.
- //
- // It is important to call strip_path() before appending the relative
- // path. Otherwise the effective URL for the path ./../../.. and the
- // repository location http://a.com/foo/pkg/1/math will wrongly be
- // http://a.com/foo/pkg instead of http://a.com.
- //
- dir_path ipath (
- strip_path (
- u.path,
- strip_p ? strip_mode::component : strip_mode::version) / rpath);
-
- static const char* invalid_location ("invalid repository location");
-
- try
- {
- ipath.normalize (false, true); // Current dir collapses to an empty one.
- }
- catch (const invalid_path&)
- {
- throw invalid_argument (invalid_location);
- }
-
- assert (ipath.relative ());
-
- if (!ipath.empty () && *ipath.begin () == "..")
- throw invalid_argument (invalid_location);
-
- return to_string (
- u.proto, strip_d ? strip_domain (u.host) : u.host, u.port, ipath);
- }
-
- // repository_manifests
- //
- repository_manifests::
- repository_manifests (parser& p, bool iu)
- {
- name_value nv (p.next ());
- while (!nv.empty ())
- {
- push_back (repository_manifest (p, nv, iu));
- nv = p.next ();
-
- // Make sure there is location in all except the last entry.
- //
- if (back ().location.empty () && !nv.empty ())
- throw parsing (p.name (), nv.name_line, nv.name_column,
- "repository location expected");
- }
-
- if (empty () || !back ().location.empty ())
- throw parsing (p.name (), nv.name_line, nv.name_column,
- "base repository manifest expected");
- }
-
- void repository_manifests::
- serialize (serializer& s) const
- {
- if (empty () || !back ().location.empty ())
- throw serialization (s.name (), "base repository manifest expected");
-
- // @@ Should we check that there is location in all except the last
- // entry?
- //
- for (const repository_manifest& r: *this)
- r.serialize (s);
-
- s.next ("", ""); // End of stream.
- }
-
- // signature_manifest
- //
- signature_manifest::
- signature_manifest (parser& p, bool iu)
- : signature_manifest (p, p.next (), iu) // Delegate
- {
- // Make sure this is the end.
- //
- name_value nv (p.next ());
- if (!nv.empty ())
- throw parsing (p.name (), nv.name_line, nv.name_column,
- "single signature manifest expected");
- }
-
- signature_manifest::
- signature_manifest (parser& p, name_value nv, bool iu)
- {
- auto bad_name ([&p, &nv](const string& d) {
- throw parsing (p.name (), nv.name_line, nv.name_column, d);});
-
- auto bad_value ([&p, &nv](const string& d) {
- throw parsing (p.name (), nv.value_line, nv.value_column, d);});
-
- // Make sure this is the start and we support the version.
- //
- if (!nv.name.empty ())
- bad_name ("start of signature manifest expected");
-
- if (nv.value != "1")
- bad_value ("unsupported format version");
-
- for (nv = p.next (); !nv.empty (); nv = p.next ())
- {
- string& n (nv.name);
- string& v (nv.value);
-
- if (n == "sha256sum")
- {
- if (!sha256sum.empty ())
- bad_name ("sha256sum redefinition");
-
- if (v.empty ())
- bad_value ("empty sha256sum");
-
- if (!valid_sha256 (v))
- bad_value ("invalid sha256sum");
-
- sha256sum = move (v);
- }
- else if (n == "signature")
- {
- if (!signature.empty ())
- bad_name ("signature redefinition");
-
- if (v.empty ())
- bad_value ("empty signature");
-
- // Try to base64-decode as a sanity check.
- //
- try
- {
- signature = base64_decode (v);
- }
- catch (const invalid_argument&)
- {
- bad_value ("invalid signature");
- }
- }
- else if (!iu)
- bad_name ("unknown name '" + n + "' in signature manifest");
- }
-
- // Verify all non-optional values were specified.
- //
- if (sha256sum.empty ())
- bad_value ("no sha256sum specified");
- else if (signature.empty ())
- bad_value ("no signature specified");
-
- // Make sure this is the end.
- //
- nv = p.next ();
- if (!nv.empty ())
- throw parsing (p.name (), nv.name_line, nv.name_column,
- "single signature manifest expected");
- }
-
- void signature_manifest::
- serialize (serializer& s) const
- {
- // @@ Should we check that values are valid ?
- //
- s.next ("", "1"); // Start of manifest.
-
- s.next ("sha256sum", sha256sum);
- s.next ("signature", base64_encode (signature));
-
- s.next ("", ""); // End of manifest.
- }
-}
diff --git a/bpkg/version.in b/bpkg/version.in
deleted file mode 100644
index 113c9bf..0000000
--- a/bpkg/version.in
+++ /dev/null
@@ -1,44 +0,0 @@
-// file : bpkg/version.in -*- C++ -*-
-// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
-// license : MIT; see accompanying LICENSE file
-
-#ifndef LIBBPKG_VERSION // Note: using the version macro itself.
-
-// Note: using build2 standard versioning scheme. The numeric version format
-// is AAABBBCCCDDDE where:
-//
-// AAA - major version number
-// BBB - minor version number
-// CCC - bugfix version number
-// DDD - alpha / beta (DDD + 500) version number
-// E - final (0) / snapshot (1)
-//
-// When DDDE is not 0, 1 is subtracted from AAABBBCCC. For example:
-//
-// Version AAABBBCCCDDDE
-//
-// 0.1.0 0000010000000
-// 0.1.2 0000010010000
-// 1.2.3 0010020030000
-// 2.2.0-a.1 0020019990010
-// 3.0.0-b.2 0029999995020
-// 2.2.0-a.1.z 0020019990011
-//
-#define LIBBPKG_VERSION $libbpkg.version.project_number$ULL
-#define LIBBPKG_VERSION_STR "$libbpkg.version.project$"
-#define LIBBPKG_VERSION_ID "$libbpkg.version.project_id$"
-
-#define LIBBPKG_VERSION_MAJOR $libbpkg.version.major$
-#define LIBBPKG_VERSION_MINOR $libbpkg.version.minor$
-#define LIBBPKG_VERSION_PATCH $libbpkg.version.patch$
-
-#define LIBBPKG_PRE_RELEASE $libbpkg.version.pre_release$
-
-#define LIBBPKG_SNAPSHOT $libbpkg.version.snapshot_sn$ULL
-#define LIBBPKG_SNAPSHOT_ID "$libbpkg.version.snapshot_id$"
-
-#include <butl/version>
-
-$libbutl.check(LIBBUTL_VERSION, LIBBUTL_SNAPSHOT)$
-
-#endif // LIBBPKG_VERSION