aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore6
-rw-r--r--LICENSE2
-rw-r--r--build/root.build7
-rw-r--r--libbpkg/manifest.cxx1684
-rw-r--r--libbpkg/manifest.hxx706
-rw-r--r--libbpkg/manifest.ixx412
-rw-r--r--manifest10
-rw-r--r--tests/build/root.build7
-rw-r--r--tests/manifest/driver.cxx29
-rw-r--r--tests/manifest/testscript1113
-rw-r--r--tests/overrides/driver.cxx2
-rw-r--r--tests/overrides/testscript391
12 files changed, 3642 insertions, 727 deletions
diff --git a/.gitignore b/.gitignore
index c3de2e7..5046596 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,10 +5,16 @@
*.d
*.t
*.i
+*.i.*
*.ii
+*.ii.*
*.o
*.obj
+*.gcm
+*.pcm
+*.ifc
*.so
+*.dylib
*.dll
*.a
*.lib
diff --git a/LICENSE b/LICENSE
index 7b90b56..e63bcec 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
MIT License
-Copyright (c) 2014-2022 the build2 authors (see the AUTHORS file).
+Copyright (c) 2014-2024 the build2 authors (see the AUTHORS file).
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/build/root.build b/build/root.build
index 75ac4be..c9ab5dc 100644
--- a/build/root.build
+++ b/build/root.build
@@ -16,4 +16,11 @@ if ($cxx.target.system == 'win32-msvc')
if ($cxx.class == 'msvc')
cxx.coptions += /wd4251 /wd4275 /wd4800
elif ($cxx.id == 'gcc')
+{
cxx.coptions += -Wno-maybe-uninitialized -Wno-free-nonheap-object # libbutl
+
+ if ($cxx.version.major >= 13)
+ cxx.coptions += -Wno-dangling-reference
+}
+elif ($cxx.id.type == 'clang' && $cxx.version.major >= 15)
+ cxx.coptions += -Wno-unqualified-std-cast-call
diff --git a/libbpkg/manifest.cxx b/libbpkg/manifest.cxx
index b5a654c..bd69b85 100644
--- a/libbpkg/manifest.cxx
+++ b/libbpkg/manifest.cxx
@@ -9,10 +9,10 @@
#include <sstream>
#include <cassert>
#include <cstdlib> // strtoull()
-#include <cstring> // strncmp(), strcmp(), strchr()
+#include <cstring> // strncmp(), strcmp(), strchr(), strcspn()
#include <utility> // move()
#include <cstdint> // uint*_t
-#include <algorithm> // find(), find_if_not(), find_first_of(), replace()
+#include <algorithm> // find(), find_if(), find_first_of(), replace()
#include <stdexcept> // invalid_argument
#include <type_traits> // remove_reference
@@ -566,7 +566,7 @@ namespace bpkg
}
version& version::
- operator= (version&& v)
+ operator= (version&& v) noexcept
{
if (this != &v)
{
@@ -624,7 +624,8 @@ namespace bpkg
}
text_file::
- text_file (text_file&& f): file (f.file), comment (move (f.comment))
+ text_file (text_file&& f) noexcept
+ : file (f.file), comment (move (f.comment))
{
if (file)
new (&path) path_type (move (f.path));
@@ -642,12 +643,12 @@ namespace bpkg
}
text_file& text_file::
- operator= (text_file&& f)
+ operator= (text_file&& f) noexcept
{
if (this != &f)
{
this->~text_file ();
- new (this) text_file (move (f)); // Assume noexcept move-construction.
+ new (this) text_file (move (f)); // Rely on noexcept move-construction.
}
return *this;
}
@@ -660,6 +661,132 @@ namespace bpkg
return *this;
}
+ // text_type
+ //
+ string
+ to_string (text_type t)
+ {
+ switch (t)
+ {
+ case text_type::plain: return "text/plain";
+ case text_type::github_mark: return "text/markdown;variant=GFM";
+ case text_type::common_mark: return "text/markdown;variant=CommonMark";
+ }
+
+ assert (false); // Can't be here.
+ return string ();
+ }
+
+ optional<text_type>
+ to_text_type (const string& t)
+ {
+ auto bad_type = [] (const string& d) {throw invalid_argument (d);};
+
+ // Parse the media type representation (see RFC2045 for details) into the
+ // type/subtype value and the parameter list. Note: we don't support
+ // parameter quoting and comments for simplicity.
+ //
+ size_t p (t.find (';'));
+ const string& tp (p != string::npos ? trim (string (t, 0, p)) : t);
+
+ small_vector<pair<string, string>, 1> ps;
+
+ while (p != string::npos)
+ {
+ // Extract parameter name.
+ //
+ size_t b (p + 1);
+ p = t.find ('=', b);
+
+ if (p == string::npos)
+ bad_type ("missing '='");
+
+ string n (trim (string (t, b, p - b)));
+
+ // Extract parameter value.
+ //
+ b = p + 1;
+ p = t.find (';', b);
+
+ string v (trim (string (t,
+ b,
+ p != string::npos ? p - b : string::npos)));
+
+ ps.emplace_back (move (n), move (v));
+ }
+
+ // Calculate the resulting text type, failing on unrecognized media type,
+ // unexpected parameter name or value.
+ //
+ // Note that type, subtype, and parameter names are matched
+ // case-insensitively.
+ //
+ optional<text_type> r;
+
+ // Currently only the plain and markdown text types are allowed. Later we
+ // can potentially introduce some other text types.
+ //
+ if (icasecmp (tp, "text/plain") == 0)
+ {
+ // Currently, we don't expect parameters for plain text. Later we can
+ // potentially introduce some plain text variants.
+ //
+ if (ps.empty ())
+ r = text_type::plain;
+ }
+ else if (icasecmp (tp, "text/markdown") == 0)
+ {
+ // Currently, a single optional variant parameter with the two possible
+ // values is allowed for markdown. Later we can potentially introduce
+ // some other markdown variants.
+ //
+ if (ps.empty () ||
+ (ps.size () == 1 && icasecmp (ps[0].first, "variant") == 0))
+ {
+ // Note that markdown variants are matched case-insensitively (see
+ // RFC7763 for details).
+ //
+ string v;
+ if (ps.empty () || icasecmp (v = move (ps[0].second), "GFM") == 0)
+ r = text_type::github_mark;
+ else if (icasecmp (v, "CommonMark") == 0)
+ r = text_type::common_mark;
+ }
+ }
+ else if (icasecmp (tp, "text/", 5) != 0)
+ bad_type ("text type expected");
+
+ return r;
+ }
+
+ // typed_text_file
+ //
+ optional<text_type> typed_text_file::
+ effective_type (bool iu) const
+ {
+ optional<text_type> r;
+
+ if (type)
+ {
+ r = to_text_type (*type);
+ }
+ else if (file)
+ {
+ string ext (path.extension ());
+ if (ext.empty () || icasecmp (ext, "txt") == 0)
+ r = text_type::plain;
+ else if (icasecmp (ext, "md") == 0 || icasecmp (ext, "markdown") == 0)
+ r = text_type::github_mark;
+ }
+ else
+ r = text_type::plain;
+
+ if (!r && !iu)
+ throw invalid_argument ("unknown text type");
+
+ return r;
+ }
+
// manifest_url
//
manifest_url::
@@ -1111,20 +1238,6 @@ namespace bpkg
}
}
- std::string dependency::
- string () const
- {
- std::string r (name.string ());
-
- if (constraint)
- {
- r += ' ';
- r += constraint->string ();
- }
-
- return r;
- }
-
// dependency_alternative
//
string dependency_alternative::
@@ -1225,14 +1338,6 @@ namespace bpkg
return r;
}
- bool dependency_alternative::
- single_line () const
- {
- return !prefer &&
- !require &&
- (!reflect || reflect->find ('\n') == string::npos);
- }
-
// dependency_alternatives
//
class dependency_alternatives_lexer: public char_scanner<utf8_validator>
@@ -2418,18 +2523,6 @@ namespace bpkg
return serializer::merge_comment (r, comment);
}
- bool dependency_alternatives::
- conditional () const
- {
- for (const dependency_alternative& da: *this)
- {
- if (da.enable)
- return true;
- }
-
- return false;
- }
-
// requirement_alternative
//
string requirement_alternative::
@@ -2514,12 +2607,6 @@ namespace bpkg
return r;
}
- bool requirement_alternative::
- single_line () const
- {
- return !reflect || reflect->find ('\n') == string::npos;
- }
-
// requirement_alternatives
//
requirement_alternatives::
@@ -2619,18 +2706,6 @@ namespace bpkg
return serializer::merge_comment (r, comment);
}
- bool requirement_alternatives::
- conditional () const
- {
- for (const requirement_alternative& ra: *this)
- {
- if (ra.enable)
- return true;
- }
-
- return false;
- }
-
// build_class_term
//
build_class_term::
@@ -2643,7 +2718,7 @@ namespace bpkg
}
build_class_term::
- build_class_term (build_class_term&& t)
+ build_class_term (build_class_term&& t) noexcept
: operation (t.operation),
inverted (t.inverted),
simple (t.simple)
@@ -2667,13 +2742,13 @@ namespace bpkg
}
build_class_term& build_class_term::
- operator= (build_class_term&& t)
+ operator= (build_class_term&& t) noexcept
{
if (this != &t)
{
this->~build_class_term ();
- // Assume noexcept move-construction.
+ // Rely on noexcept move-construction.
//
new (this) build_class_term (move (t));
}
@@ -2994,102 +3069,42 @@ namespace bpkg
match_classes (cs, im, expr, r);
}
- // text_type
+ // build_auxiliary
//
- string
- to_string (text_type t)
- {
- switch (t)
- {
- case text_type::plain: return "text/plain";
- case text_type::github_mark: return "text/markdown;variant=GFM";
- case text_type::common_mark: return "text/markdown;variant=CommonMark";
- }
-
- assert (false); // Can't be here.
- return string ();
- }
-
- optional<text_type>
- to_text_type (const string& t)
+ optional<pair<string, string>> build_auxiliary::
+ parse_value_name (const string& n)
{
- auto bad_type = [] (const string& d) {throw invalid_argument (d);};
-
- // Parse the media type representation (see RFC2045 for details) into the
- // type/subtype value and the parameter list. Note: we don't support
- // parameter quoting and comments for simplicity.
+ // Check if the value name matches exactly.
//
- size_t p (t.find (';'));
- const string& tp (p != string::npos ? trim (string (t, 0, p)) : t);
+ if (n == "build-auxiliary")
+ return make_pair (string (), string ());
- small_vector<pair<string, string>, 1> ps;
-
- while (p != string::npos)
+ // Check if this is a *-build-auxiliary name.
+ //
+ if (n.size () > 16 &&
+ n.compare (n.size () - 16, 16, "-build-auxiliary") == 0)
{
- // Extract parameter name.
- //
- size_t b (p + 1);
- p = t.find ('=', b);
-
- if (p == string::npos)
- bad_type ("missing '='");
-
- string n (trim (string (t, b, p - b)));
-
- // Extract parameter value.
- //
- b = p + 1;
- p = t.find (';', b);
-
- string v (trim (string (t,
- b,
- p != string::npos ? p - b : string::npos)));
-
- ps.emplace_back (move (n), move (v));
+ return make_pair (string (n, 0, n.size () - 16), string ());
}
- // Calculate the resulting text type, failing on unrecognized media type,
- // unexpected parameter name or value.
+ // Check if this is a build-auxiliary-* name.
//
- // Note that type, subtype, and parameter names are matched
- // case-insensitively.
- //
- optional<text_type> r;
+ if (n.size () > 16 && n.compare (0, 16, "build-auxiliary-") == 0)
+ return make_pair (string (), string (n, 16));
- // Currently only the plain and markdown text types are allowed. Later we
- // can potentially introduce some other text types.
+ // Check if this is a *-build-auxiliary-* name.
//
- if (icasecmp (tp, "text/plain") == 0)
- {
- // Currently, we don't expect parameters for plain text. Later we can
- // potentially introduce some plain text variants.
- //
- if (ps.empty ())
- r = text_type::plain;
- }
- else if (icasecmp (tp, "text/markdown") == 0)
+ size_t p (n.find ("-build-auxiliary-"));
+
+ if (p != string::npos &&
+ p != 0 && // Not '-build-auxiliary-*'?
+ p + 17 != n.size () && // Not '*-build-auxiliary-'?
+ n.find ("-build-auxiliary-", p + 17) == string::npos) // Unambiguous?
{
- // Currently, a single optional variant parameter with the two possible
- // values is allowed for markdown. Later we can potentially introduce
- // some other markdown variants.
- //
- if (ps.empty () ||
- (ps.size () == 1 && icasecmp (ps[0].first, "variant") == 0))
- {
- // Note that markdown variants are matched case-insensitively (see
- // RFC7763 for details).
- //
- string v;
- if (ps.empty () || icasecmp (v = move (ps[0].second), "GFM") == 0)
- r = text_type::github_mark;
- else if (icasecmp (v, "CommonMark") == 0)
- r = text_type::common_mark;
- }
+ return make_pair (string (n, 0, p), string (n, p + 17));
}
- else if (icasecmp (tp, "text/", 5) != 0)
- bad_type ("text type expected");
- return r;
+ return nullopt;
}
// test_dependency_type
@@ -3127,11 +3142,12 @@ namespace bpkg
using std::string;
// We will use the dependency alternatives parser to parse the
- // `<name> [<version-constraint>] [<reflect-config>]` representation into
- // a temporary dependency alternatives object. Then we will verify that
- // the result has no multiple alternatives/dependency packages and
- // unexpected clauses and will move the required information (dependency,
- // reflection, etc) into the being created test dependency object.
+ // `<name> [<version-constraint>] ['?' <enable-condition>] [<reflect-config>]`
+ // representation into a temporary dependency alternatives object. Then we
+ // will verify that the result has no multiple alternatives/dependency
+ // packages and unexpected clauses and will move the required information
+ // (dependency, reflection, etc) into the being created test dependency
+ // object.
// Verify that there is no newline characters to forbid the multi-line
// dependency alternatives representation.
@@ -3210,16 +3226,14 @@ namespace bpkg
//
// Note that the require, prefer, and accept clauses can only be present
// in the multi-line representation and we have already verified that this
- // is not the case.
- //
- if (da.enable)
- throw invalid_argument ("unexpected enable clause");
+ // is not the case. So there is nothing to verify here.
- // Move the dependency and the reflect clause into the being created test
- // dependency object.
+ // Move the dependency and the enable and reflect clauses into the being
+ // created test dependency object.
//
static_cast<dependency&> (*this) = move (da[0]);
+ enable = move (da.enable);
reflect = move (da.reflect);
}
@@ -3230,6 +3244,13 @@ namespace bpkg
? "* " + dependency::string ()
: dependency::string ());
+ if (enable)
+ {
+ r += " ? (";
+ r += *enable;
+ r += ')';
+ }
+
if (reflect)
{
r += ' ';
@@ -3335,6 +3356,62 @@ namespace bpkg
return email (move (v), move (c));
}
+ // Parse the [*-]build-auxiliary[-*] manifest value.
+ //
+ // Note that the environment name is expected to already be retrieved using
+ // build_auxiliary::parse_value_name().
+ //
+ static build_auxiliary
+ parse_build_auxiliary (const name_value& nv,
+ string&& env_name,
+ const string& source_name)
+ {
+ auto bad_value = [&nv, &source_name] (const string& d)
+ {
+ throw !source_name.empty ()
+ ? parsing (source_name, nv.value_line, nv.value_column, d)
+ : parsing (d);
+ };
+
+ pair<string, string> vc (parser::split_comment (nv.value));
+ string& v (vc.first);
+ string& c (vc.second);
+
+ if (v.empty ())
+ bad_value ("empty build auxiliary configuration name pattern");
+
+ return build_auxiliary (move (env_name), move (v), move (c));
+ }
+
+ // Parse the [*-]build-bot manifest value and append it to the specified
+ // custom bot public keys list. Make sure the specified key is not empty and
+ // is not a duplicate and throw parsing if that's not the case.
+ //
+ // Note: value name is not used by this function (and so can be moved out,
+ // etc before the call).
+ //
+ static void
+ parse_build_bot (const name_value& nv, const string& source_name, strings& r)
+ {
+ const string& v (nv.value);
+
+ auto bad_value = [&nv, &source_name, &v] (const string& d,
+ bool add_key = true)
+ {
+ throw !source_name.empty ()
+ ? parsing (source_name, nv.value_line, nv.value_column, d)
+ : parsing (!add_key ? d : (d + ":\n" + v));
+ };
+
+ if (v.empty ())
+ bad_value ("empty custom build bot public key", false /* add_key */);
+
+ if (find (r.begin (), r.end (), v) != r.end ())
+ bad_value ("duplicate custom build bot public key");
+
+ r.push_back (v);
+ }
+
const version stub_version (0, "0", nullopt, nullopt, 0);
// Parse until next() returns end-of-manifest value.
@@ -3345,7 +3422,7 @@ namespace bpkg
const function<name_value ()>& next,
const function<package_manifest::translate_function>& translate,
bool iu,
- bool cd,
+ bool cv,
package_manifest_flags fl,
package_manifest& m)
{
@@ -3357,16 +3434,37 @@ namespace bpkg
auto bad_value ([&name, &nv](const string& d) {
throw parsing (name, nv.value_line, nv.value_column, d);});
- auto parse_email = [&bad_name] (const name_value& nv,
- optional<email>& r,
- const char* what,
- const string& source_name,
- bool empty = false)
+ auto parse_email = [&bad_name, &name] (const name_value& nv,
+ optional<email>& r,
+ const char* what,
+ bool empty = false)
{
if (r)
bad_name (what + string (" email redefinition"));
- r = bpkg::parse_email (nv, what, source_name, empty);
+ r = bpkg::parse_email (nv, what, name, empty);
+ };
+
+ // Parse the [*-]build-auxiliary[-*] manifest value and append it to the
+ // specified build auxiliary list. Make sure that the list contains not
+ // more than one entry with unspecified environment name and throw parsing
+ // if that's not the case. Also make sure that there are no entry
+ // redefinitions (multiple entries with the same environment name).
+ //
+ auto parse_build_auxiliary = [&bad_name, &name] (const name_value& nv,
+ string&& en,
+ vector<build_auxiliary>& r)
+ {
+ build_auxiliary a (bpkg::parse_build_auxiliary (nv, move (en), name));
+
+ if (find_if (r.begin (), r.end (),
+ [&a] (const build_auxiliary& ba)
+ {
+ return ba.environment_name == a.environment_name;
+ }) != r.end ())
+ bad_name ("build auxiliary environment redefinition");
+
+ r.push_back (move (a));
};
auto parse_url = [&bad_value] (const string& v,
@@ -3430,6 +3528,43 @@ namespace bpkg
}
};
+ // Note: the n argument is the distribution name length.
+ //
+ auto parse_distribution = [&bad_name, &bad_value] (string&& nm, size_t n,
+ string&& vl)
+ {
+ size_t p (nm.find ('-'));
+
+ // Distribution-related manifest value name always has a dash-starting
+ // suffix (-name, etc).
+ //
+ assert (p != string::npos);
+
+ if (p < n)
+ bad_name ("distribution name '" + string (nm, 0, n) + "' contains '-'");
+
+ if (vl.empty ())
+ bad_value ("empty package distribution value");
+
+ return distribution_name_value (move (nm), move (vl));
+ };
+
+ auto add_distribution = [&m, &bad_name] (distribution_name_value&& nv,
+ bool unique)
+ {
+ vector<distribution_name_value>& dvs (m.distribution_values);
+
+ if (unique &&
+ find_if (dvs.begin (), dvs.end (),
+ [&nv] (const distribution_name_value& dnv)
+ {return dnv.name == nv.name;}) != dvs.end ())
+ {
+ bad_name ("package distribution value redefinition");
+ }
+
+ dvs.push_back (move (nv));
+ };
+
auto flag = [fl] (package_manifest_flags f)
{
return (fl & f) != package_manifest_flags::none;
@@ -3543,10 +3678,21 @@ namespace bpkg
};
// Return the package build configuration with the specified name, if
- // already exists, or the newly created configuration otherwise.
- //
- auto build_conf = [&m] (string&& nm) -> build_package_config&
- {
+ // already exists. If no configuration matches, then create one, if
+ // requested, and throw manifest_parsing otherwise. If the new
+ // configuration creation is not allowed, then the description for a
+ // potential manifest_parsing exception needs to also be specified.
+ //
+ auto build_conf = [&m, &bad_name] (string&& nm,
+ bool create = true,
+ const string& desc = "")
+ -> build_package_config&
+ {
+ // The error description must only be specified if the creation of the
+ // package build configuration is not allowed.
+ //
+ assert (desc.empty () == create);
+
small_vector<build_package_config, 1>& cs (m.build_configs);
auto i (find_if (cs.begin (), cs.end (),
@@ -3556,6 +3702,9 @@ namespace bpkg
if (i != cs.end ())
return *i;
+ if (!create)
+ bad_name (desc + ": no build package configuration '" + nm + '\'');
+
// Add the new build configuration (arguments, builds, etc will come
// later).
//
@@ -3577,11 +3726,25 @@ namespace bpkg
vector<name_value> requirements;
small_vector<name_value, 1> tests;
- // We will cache the description and its type values to validate them
- // later, after both are parsed.
+ // We will cache the descriptions and changes and their type values to
+ // validate them later, after all are parsed.
//
optional<name_value> description;
optional<name_value> description_type;
+ optional<name_value> package_description;
+ optional<name_value> package_description_type;
+ vector<name_value> changes;
+ optional<name_value> changes_type;
+
+ // It doesn't make sense for only emails to be specified for a package
+ // build configuration. Thus, we will cache the build configuration email
+ // manifest values to parse them later, after all other build
+ // configuration values are parsed, and to make sure that the build
+ // configurations they refer to are also specified.
+ //
+ vector<name_value> build_config_emails;
+ vector<name_value> build_config_warning_emails;
+ vector<name_value> build_config_error_emails;
m.build_configs.emplace_back ("default");
@@ -3652,6 +3815,55 @@ namespace bpkg
upstream_version = move (nv);
}
+ else if (n == "type")
+ {
+ if (m.type)
+ bad_name ("package type redefinition");
+
+ if (v.empty () || v.find (',') == 0)
+ bad_value ("empty package type");
+
+ m.type = move (v);
+ }
+ else if (n == "language")
+ {
+ // Strip the language extra information, if present.
+ //
+ size_t p (v.find (','));
+ if (p != string::npos)
+ v.resize (p);
+
+ // Determine the language impl flag.
+ //
+ bool impl (false);
+ p = v.find ('=');
+ if (p != string::npos)
+ {
+ string s (trim (string (v, p + 1)));
+ if (s != "impl")
+ bad_value (!s.empty ()
+ ? "unexpected '" + s + "' value after '='"
+ : "expected 'impl' after '='");
+
+ impl = true;
+
+ v.resize (p);
+ }
+
+ // Finally, validate and add the language.
+ //
+ trim_right (v);
+
+ if (v.empty ())
+ bad_value ("empty package language");
+
+ if (find_if (m.languages.begin (), m.languages.end (),
+ [&v] (const language& l) {return l.name == v;}) !=
+ m.languages.end ())
+ bad_value ("duplicate package language");
+
+ m.languages.emplace_back (move (v), impl);
+ }
else if (n == "project")
{
if (m.project)
@@ -3708,28 +3920,28 @@ namespace bpkg
if (description)
{
if (description->name == "description-file")
- bad_name ("package description and description-file are "
+ bad_name ("project description and description file are "
"mutually exclusive");
else
- bad_name ("package description redefinition");
+ bad_name ("project description redefinition");
}
if (v.empty ())
- bad_value ("empty package description");
+ bad_value ("empty project description");
description = move (nv);
}
else if (n == "description-file")
{
if (flag (package_manifest_flags::forbid_file))
- bad_name ("package description-file not allowed");
+ bad_name ("project description file not allowed");
if (description)
{
if (description->name == "description-file")
- bad_name ("package description-file redefinition");
+ bad_name ("project description file redefinition");
else
- bad_name ("package description-file and description are "
+ bad_name ("project description file and description are "
"mutually exclusive");
}
@@ -3738,32 +3950,69 @@ namespace bpkg
else if (n == "description-type")
{
if (description_type)
- bad_name ("package description-type redefinition");
+ bad_name ("project description type redefinition");
description_type = move (nv);
}
+ else if (n == "package-description")
+ {
+ if (package_description)
+ {
+ if (package_description->name == "package-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");
+
+ package_description = move (nv);
+ }
+ else if (n == "package-description-file")
+ {
+ if (flag (package_manifest_flags::forbid_file))
+ bad_name ("package description file not allowed");
+
+ if (package_description)
+ {
+ if (package_description->name == "package-description-file")
+ bad_name ("package description file redefinition");
+ else
+ bad_name ("package description file and description are "
+ "mutually exclusive");
+ }
+
+ package_description = move (nv);
+ }
+ else if (n == "package-description-type")
+ {
+ if (package_description_type)
+ bad_name ("package description type redefinition");
+
+ package_description_type = move (nv);
+ }
else if (n == "changes")
{
if (v.empty ())
bad_value ("empty package changes specification");
- m.changes.emplace_back (move (v));
+ changes.emplace_back (move (nv));
}
else if (n == "changes-file")
{
if (flag (package_manifest_flags::forbid_file))
bad_name ("package changes-file not allowed");
- auto vc (parser::split_comment (v));
- path p (move (vc.first));
-
- if (p.empty ())
- bad_value ("no path in package changes-file");
-
- if (p.absolute ())
- bad_value ("package changes-file path is absolute");
+ changes.emplace_back (move (nv));
+ }
+ else if (n == "changes-type")
+ {
+ if (changes_type)
+ bad_name ("package changes type redefinition");
- m.changes.emplace_back (move (p), move (vc.second));
+ changes_type = move (nv);
}
else if (n == "url")
{
@@ -3774,7 +4023,7 @@ namespace bpkg
}
else if (n == "email")
{
- parse_email (nv, m.email, "project", name);
+ parse_email (nv, m.email, "project");
}
else if (n == "doc-url")
{
@@ -3799,19 +4048,19 @@ namespace bpkg
}
else if (n == "package-email")
{
- parse_email (nv, m.package_email, "package", name);
+ parse_email (nv, m.package_email, "package");
}
else if (n == "build-email")
{
- parse_email (nv, m.build_email, "build", name, true /* empty */);
+ parse_email (nv, m.build_email, "build", true /* empty */);
}
else if (n == "build-warning-email")
{
- parse_email (nv, m.build_warning_email, "build warning", name);
+ parse_email (nv, m.build_warning_email, "build warning");
}
else if (n == "build-error-email")
{
- parse_email (nv, m.build_error_email, "build error", name);
+ parse_email (nv, m.build_error_email, "build error");
}
else if (n == "priority")
{
@@ -3885,8 +4134,25 @@ namespace bpkg
m.build_constraints.push_back (
parse_build_constraint (nv, true /* exclusion */, name));
}
- else if ((n.size () > 13 &&
- n.compare (n.size () - 13, 13, "-build-config") == 0))
+ else if (optional<pair<string, string>> ba =
+ build_auxiliary::parse_value_name (n))
+ {
+ if (ba->first.empty ()) // build-auxiliary*?
+ {
+ parse_build_auxiliary (nv, move (ba->second), m.build_auxiliaries);
+ }
+ else // *-build-auxiliary*
+ {
+ build_package_config& bc (build_conf (move (ba->first)));
+ parse_build_auxiliary (nv, move (ba->second), bc.auxiliaries);
+ }
+ }
+ else if (n == "build-bot")
+ {
+ parse_build_bot (nv, name, m.build_bot_keys);
+ }
+ else if (n.size () > 13 &&
+ n.compare (n.size () - 13, 13, "-build-config") == 0)
{
auto vc (parser::split_comment (v));
@@ -3900,7 +4166,7 @@ namespace bpkg
bc.arguments = move (vc.first);
bc.comment = move (vc.second);
}
- else if ((n.size () > 7 && n.compare (n.size () - 7, 7, "-builds") == 0))
+ else if (n.size () > 7 && n.compare (n.size () - 7, 7, "-builds") == 0)
{
n.resize (n.size () - 7);
@@ -3909,8 +4175,8 @@ namespace bpkg
bc.builds.push_back (
parse_build_class_expr (nv, bc.builds.empty (), name));
}
- else if ((n.size () > 14 &&
- n.compare (n.size () - 14, 14, "-build-include") == 0))
+ else if (n.size () > 14 &&
+ n.compare (n.size () - 14, 14, "-build-include") == 0)
{
n.resize (n.size () - 14);
@@ -3919,8 +4185,8 @@ namespace bpkg
bc.constraints.push_back (
parse_build_constraint (nv, false /* exclusion */, name));
}
- else if ((n.size () > 14 &&
- n.compare (n.size () - 14, 14, "-build-exclude") == 0))
+ else if (n.size () > 14 &&
+ n.compare (n.size () - 14, 14, "-build-exclude") == 0)
{
n.resize (n.size () - 14);
@@ -3929,6 +4195,32 @@ namespace bpkg
bc.constraints.push_back (
parse_build_constraint (nv, true /* exclusion */, name));
}
+ else if (n.size () > 10 &&
+ n.compare (n.size () - 10, 10, "-build-bot") == 0)
+ {
+ n.resize (n.size () - 10);
+
+ build_package_config& bc (build_conf (move (n)));
+ parse_build_bot (nv, name, bc.bot_keys);
+ }
+ else if (n.size () > 12 &&
+ n.compare (n.size () - 12, 12, "-build-email") == 0)
+ {
+ n.resize (n.size () - 12);
+ build_config_emails.push_back (move (nv));
+ }
+ else if (n.size () > 20 &&
+ n.compare (n.size () - 20, 20, "-build-warning-email") == 0)
+ {
+ n.resize (n.size () - 20);
+ build_config_warning_emails.push_back (move (nv));
+ }
+ else if (n.size () > 18 &&
+ n.compare (n.size () - 18, 18, "-build-error-email") == 0)
+ {
+ n.resize (n.size () - 18);
+ build_config_error_emails.push_back (move (nv));
+ }
// @@ TMP time to drop *-0.14.0?
//
else if (n == "tests" || n == "tests-0.14.0" ||
@@ -4000,6 +4292,41 @@ namespace bpkg
bad_value ("path with build or build2 extension expected");
}
+ else if (n.size () > 5 && n.compare (n.size () - 5, 5, "-name") == 0)
+ {
+ add_distribution (
+ parse_distribution (move (n), n.size () - 5, move (v)),
+ false /* unique */);
+ }
+ // Note: must precede the check for the "-version" suffix.
+ //
+ else if (n.size () > 22 &&
+ n.compare (n.size () - 22, 22, "-to-downstream-version") == 0)
+ {
+ add_distribution (
+ parse_distribution (move (n), n.size () - 22, move (v)),
+ false /* unique */);
+ }
+ // Note: must follow the check for "upstream-version".
+ //
+ else if (n.size () > 8 && n.compare (n.size () - 8, 8, "-version") == 0)
+ {
+ // If the value is forbidden then throw, but only after the name is
+ // validated. Thus, check for that before we move the value from.
+ //
+ bool bad (v == "$" &&
+ flag (package_manifest_flags::forbid_incomplete_values));
+
+ // Can throw.
+ //
+ distribution_name_value d (
+ parse_distribution (move (n), n.size () - 8, move (v)));
+
+ if (bad)
+ bad_value ("$ not allowed");
+
+ add_distribution (move (d), true /* unique */);
+ }
else if (n == "location")
{
if (flag (package_manifest_flags::forbid_location))
@@ -4080,23 +4407,24 @@ namespace bpkg
m.upstream_version = move (nv.value);
}
- // Verify that description is specified if the description type is
- // specified.
+ // Parse and validate a text/file manifest value and its respective type
+ // value, if present. Return a typed_text_file object.
//
- if (description_type && !description)
- bad_value ("no package description for specified description type");
-
- // Validate (and set) description and its type.
- //
- if (description)
+ auto parse_text_file = [iu, &nv, &bad_value] (name_value&& text_file,
+ optional<name_value>&& type,
+ const char* what)
+ -> typed_text_file
{
+ typed_text_file r;
+
// Restore as bad_value() uses its line/column.
//
- nv = move (*description);
+ nv = move (text_file);
string& v (nv.value);
+ const string& n (nv.name);
- if (nv.name == "description-file")
+ if (n.size () > 5 && n.compare (n.size () - 5, 5, "-file") == 0)
{
auto vc (parser::split_comment (v));
@@ -4107,51 +4435,201 @@ namespace bpkg
}
catch (const invalid_path& e)
{
- bad_value (string ("invalid package description file: ") +
- e.what ());
+ bad_value (string ("invalid ") + what + " file: " + e.what ());
}
if (p.empty ())
- bad_value ("no path in package description-file");
+ bad_value (string ("no path in ") + what + " file");
if (p.absolute ())
- bad_value ("package description-file path is absolute");
+ bad_value (string (what) + " file path is absolute");
- m.description = text_file (move (p), move (vc.second));
+ r = typed_text_file (move (p), move (vc.second));
}
else
- m.description = text_file (move (v));
+ r = typed_text_file (move (v));
- if (description_type)
- m.description_type = move (description_type->value);
+ if (type)
+ r.type = move (type->value);
- // Verify the description type.
+ // Verify the text type.
//
try
{
- m.effective_description_type (iu);
+ r.effective_type (iu);
}
catch (const invalid_argument& e)
{
- if (description_type)
+ if (type)
{
- // Restore as bad_value() uses its line/column.
+ // Restore as bad_value() uses its line/column. Note that we don't
+ // need to restore the moved out type value.
//
- nv = move (*description_type);
+ nv = move (*type);
- bad_value (string ("invalid package description type: ") +
- e.what ());
+ bad_value (string ("invalid ") + what + " type: " + e.what ());
}
else
- bad_value (string ("invalid package description file: ") +
- e.what ());
+ {
+ // Note that this can only happen due to inability to guess the
+ // type from the file extension. Let's help the user here a bit.
+ //
+ assert (r.file);
+
+ bad_value (string ("invalid ") + what + " file: " + e.what () +
+ " (use " + string (n, 0, n.size () - 5) +
+ "-type manifest value to specify explicitly)");
+ }
+ }
+
+ return r;
+ };
+
+ // As above but also accepts nullopt as the text_file argument, in which
+ // case throws manifest_parsing if the type is specified and return
+ // nullopt otherwise.
+ //
+ auto parse_text_file_opt = [&nv, &bad_name, &parse_text_file]
+ (optional<name_value>&& text_file,
+ optional<name_value>&& type,
+ const char* what) -> optional<typed_text_file>
+ {
+ // Verify that the text/file value is specified if the type value is
+ // specified.
+ //
+ if (!text_file)
+ {
+ if (type)
+ {
+ // Restore as bad_name() uses its line/column.
+ //
+ nv = move (*type);
+
+ bad_name (string ("no ") + what + " for specified type");
+ }
+
+ return nullopt;
+ }
+
+ return parse_text_file (move (*text_file), move (type), what);
+ };
+
+ // Parse the project/package descriptions/types.
+ //
+ m.description = parse_text_file_opt (move (description),
+ move (description_type),
+ "project description");
+
+ m.package_description =
+ parse_text_file_opt (move (package_description),
+ move (package_description_type),
+ "package description");
+
+ // Parse the package changes/types.
+ //
+ // Note: at the end of the loop the changes_type variable may contain
+ // value in unspecified state but we can still check for the value
+ // presence.
+ //
+ for (name_value& c: changes)
+ {
+ // Move the changes_type value from for the last changes entry.
+ //
+ m.changes.push_back (
+ parse_text_file (move (c),
+ (&c != &changes.back ()
+ ? optional<name_value> (changes_type)
+ : move (changes_type)),
+ "changes"));
+ }
+
+ // If there are multiple changes and the changes type is not explicitly
+ // specified, then verify that all changes effective types are the same.
+ // Note that in the "ignore unknown" mode there can be unresolved
+ // effective types which we just skip.
+ //
+ if (changes.size () > 1 && !changes_type)
+ {
+ optional<text_type> type;
+
+ for (size_t i (0); i != m.changes.size (); ++i)
+ {
+ const typed_text_file& c (m.changes[i]);
+
+ if (optional<text_type> t = c.effective_type (iu))
+ {
+ if (!type)
+ {
+ type = *t;
+ }
+ else if (*t != *type)
+ {
+ // Restore as bad_value() uses its line/column.
+ //
+ nv = move (changes[i]);
+
+ bad_value ("changes type '" + to_string (*t) + "' differs from " +
+ " previous type '" + to_string (*type) + "'");
+ }
+ }
}
}
+ // Parse the build configuration emails.
+ //
+ // Note: the argument can only be one of the build_config_*emails
+ // variables (see above) to distinguish between the email kinds.
+ //
+ auto parse_build_config_emails = [&nv,
+ &build_config_emails,
+ &build_config_warning_emails,
+ &build_config_error_emails,
+ &build_conf,
+ &parse_email]
+ (vector<name_value>&& emails)
+ {
+ enum email_kind {build, warning, error};
+
+ email_kind ek (
+ &emails == &build_config_emails ? email_kind::build :
+ &emails == &build_config_warning_emails ? email_kind::warning :
+ email_kind::error);
+
+ // The argument can only be one of the build_config_*emails variables.
+ //
+ assert (ek != email_kind::error || &emails == &build_config_error_emails);
+
+ for (name_value& e: emails)
+ {
+ // Restore as bad_name() and bad_value() use its line/column.
+ //
+ nv = move (e);
+
+ build_package_config& bc (
+ build_conf (move (nv.name),
+ false /* create */,
+ "stray build notification email"));
+
+ parse_email (
+ nv,
+ (ek == email_kind::build ? bc.email :
+ ek == email_kind::warning ? bc.warning_email :
+ bc.error_email),
+ (ek == email_kind::build ? "build configuration" :
+ ek == email_kind::warning ? "build configuration warning" :
+ "build configuration error"),
+ ek == email_kind::build /* empty */);
+ }
+ };
+
+ parse_build_config_emails (move (build_config_emails));
+ parse_build_config_emails (move (build_config_warning_emails));
+ parse_build_config_emails (move (build_config_error_emails));
+
// Now, when the version manifest value is parsed, we can parse the
// dependencies and complete their constraints, if requested.
//
- auto complete_constraint = [&m, cd, &flag] (auto&& dep)
+ auto complete_constraint = [&m, cv, &flag] (auto&& dep)
{
if (dep.constraint)
try
@@ -4159,12 +4637,12 @@ namespace bpkg
version_constraint& vc (*dep.constraint);
if (!vc.complete () &&
- flag (package_manifest_flags::forbid_incomplete_dependencies))
+ flag (package_manifest_flags::forbid_incomplete_values))
throw invalid_argument ("$ not allowed");
// Complete the constraint.
//
- if (cd)
+ if (cv)
vc = vc.effective (m.version);
}
catch (const invalid_argument& e)
@@ -4237,16 +4715,61 @@ namespace bpkg
}
}
+ // Now, when the version manifest value is parsed, we complete the
+ // <distribution>-version values, if requested.
+ //
+ if (cv)
+ {
+ for (distribution_name_value& nv: m.distribution_values)
+ {
+ const string& n (nv.name);
+ string& v (nv.value);
+
+ if (v == "$" &&
+ (n.size () > 8 && n.compare (n.size () - 8, 8, "-version") == 0) &&
+ n.find ('-') == n.size () - 8)
+ {
+ v = version (default_epoch (m.version),
+ move (m.version.upstream),
+ nullopt /* release */,
+ nullopt /* revision */,
+ 0 /* iteration */).string ();
+ }
+ }
+ }
+
if (!m.location && flag (package_manifest_flags::require_location))
bad_name ("no package location specified");
if (!m.sha256sum && flag (package_manifest_flags::require_sha256sum))
bad_name ("no package sha256sum specified");
- if (m.description &&
- !m.description_type &&
- flag (package_manifest_flags::require_description_type))
- bad_name ("no package description type specified");
+ if (flag (package_manifest_flags::require_text_type))
+ {
+ if (m.description && !m.description->type)
+ bad_name ("no project description type specified");
+
+ if (m.package_description && !m.package_description->type)
+ bad_name ("no package description type specified");
+
+ // Note that changes either all have the same explicitly specified type
+ // or have no type.
+ //
+ if (!m.changes.empty () && !m.changes.front ().type)
+ {
+ // @@ TMP To support older repositories allow absent changes type
+ // until toolchain 0.16.0 is released.
+ //
+ // Note that for such repositories the packages may not have
+ // changes values other than plan text. Thus, we can safely set
+ // this type, if they are absent, so that the caller can always
+ // be sure that these values are always present for package
+ // manifest lists.
+ //bad_name ("no package changes type specified");
+ for (typed_text_file& c: m.changes)
+ c.type = "text/plain";
+ }
+ }
if (!m.bootstrap_build &&
flag (package_manifest_flags::require_bootstrap_build))
@@ -4276,7 +4799,7 @@ namespace bpkg
name_value nv,
const function<package_manifest::translate_function>& tf,
bool iu,
- bool cd,
+ bool cv,
package_manifest_flags fl,
package_manifest& m)
{
@@ -4296,7 +4819,7 @@ namespace bpkg
[&p] () {return p.next ();},
tf,
iu,
- cd,
+ cv,
fl,
m);
}
@@ -4308,12 +4831,12 @@ namespace bpkg
p,
move (nv),
iu,
- false /* complete_depends */,
- package_manifest_flags::forbid_file |
- package_manifest_flags::forbid_fragment |
- package_manifest_flags::forbid_incomplete_dependencies |
- package_manifest_flags::require_location |
- package_manifest_flags::require_description_type |
+ false /* complete_values */,
+ package_manifest_flags::forbid_file |
+ package_manifest_flags::forbid_fragment |
+ package_manifest_flags::forbid_incomplete_values |
+ package_manifest_flags::require_location |
+ package_manifest_flags::require_text_type |
package_manifest_flags::require_bootstrap_build);
}
@@ -4323,10 +4846,10 @@ namespace bpkg
package_manifest (manifest_parser& p,
const function<translate_function>& tf,
bool iu,
- bool cd,
+ bool cv,
package_manifest_flags fl)
{
- parse_package_manifest (p, p.next (), tf, iu, cd, fl, *this);
+ parse_package_manifest (p, p.next (), tf, iu, cv, fl, *this);
// Make sure this is the end.
//
@@ -4337,20 +4860,11 @@ namespace bpkg
}
package_manifest::
- package_manifest (manifest_parser& p,
- bool iu,
- bool cd,
- package_manifest_flags fl)
- : package_manifest (p, function<translate_function> (), iu, cd, fl)
- {
- }
-
- package_manifest::
package_manifest (const string& name,
vector<name_value>&& vs,
const function<translate_function>& tf,
bool iu,
- bool cd,
+ bool cv,
package_manifest_flags fl)
{
auto i (vs.begin ());
@@ -4365,7 +4879,7 @@ namespace bpkg
},
tf,
iu,
- cd,
+ cv,
fl,
*this);
}
@@ -4374,13 +4888,13 @@ namespace bpkg
package_manifest (const string& name,
vector<name_value>&& vs,
bool iu,
- bool cd,
+ bool cv,
package_manifest_flags fl)
: package_manifest (name,
move (vs),
function<translate_function> (),
iu,
- cd,
+ cv,
fl)
{
}
@@ -4389,36 +4903,26 @@ namespace bpkg
package_manifest (manifest_parser& p,
name_value nv,
bool iu,
- bool cd,
+ bool cv,
package_manifest_flags fl)
{
parse_package_manifest (
- p, move (nv), function<translate_function> (), iu, cd, fl, *this);
+ p, move (nv), function<translate_function> (), iu, cv, fl, *this);
}
- optional<text_type> package_manifest::
- effective_description_type (bool iu) const
+ strings package_manifest::
+ effective_type_sub_options (const optional<string>& t)
{
- if (!description)
- throw logic_error ("absent description");
-
- optional<text_type> r;
+ strings r;
- if (description_type)
- r = to_text_type (*description_type);
- else if (description->file)
+ if (t)
{
- string ext (description->path.extension ());
- if (ext.empty () || icasecmp (ext, "txt") == 0)
- r = text_type::plain;
- else if (icasecmp (ext, "md") == 0 || icasecmp (ext, "markdown") == 0)
- r = text_type::github_mark;
+ for (size_t b (0), e (0); next_word (*t, b, e, ','); )
+ {
+ if (b != 0)
+ r.push_back (trim (string (*t, b, e - b)));
+ }
}
- else
- r = text_type::plain;
-
- if (!r && !iu)
- throw invalid_argument ("unknown text type");
return r;
}
@@ -4445,16 +4949,45 @@ namespace bpkg
//
const manifest_name_value* pbc (nullptr);
+ // The first {build-bot} override value.
+ //
+ const manifest_name_value* cbb (nullptr);
+
+ // The first {*-build-bot} override value.
+ //
+ const manifest_name_value* pbb (nullptr);
+
// The first {build-*email} override value.
//
- const manifest_name_value* be (nullptr);
+ const manifest_name_value* cbe (nullptr);
+
+ // The first {*-build-*email} override value.
+ //
+ const manifest_name_value* pbe (nullptr);
- // List of indexes of the overridden build configurations together with
- // flags which indicate if the *-builds override value was encountered for
- // this configuration.
+ // List of indexes of the build configurations with the overridden build
+ // constraints together with flags which indicate if the *-builds override
+ // value was encountered for this configuration.
//
vector<pair<size_t, bool>> obcs;
+ // List of indexes of the build configurations with the overridden bots.
+ //
+ vector<size_t> obbs;
+
+ // List of indexes of the build configurations with the overridden emails.
+ //
+ vector<size_t> obes;
+
+ // Return true if the specified package build configuration is newly
+ // created by the *-build-config override.
+ //
+ auto config_created = [&m, confs_num = m.build_configs.size ()]
+ (const build_package_config& c)
+ {
+ return &c >= m.build_configs.data () + confs_num;
+ };
+
// Apply overrides.
//
for (const manifest_name_value& nv: nvs)
@@ -4497,17 +5030,56 @@ namespace bpkg
}
};
- // Return the reference to the package build configuration matching the
- // build config-specific builds group value override, if exists. If no
- // configuration matches, then throw manifest_parsing, except for the
- // validate-only mode in which case just add an empty configuration with
- // this name and return the reference to it.
+ // Return the reference to the package build configuration which matches
+ // the build config value override, if exists. If no configuration
+ // matches, then create one, if requested, and throw manifest_parsing
+ // otherwise.
//
// The n argument specifies the length of the configuration name in
- // {*-builds, *-build-{include,exclude}} values.
+ // *-build-config, *-builds, *-build-{include,exclude}, *-build-bot, and
+ // *-build-*email values.
//
- auto build_conf = [&pbc, &cbc, &nv, &obcs, &bad_name, &m, validate_only]
- (size_t n) -> build_package_config&
+ auto build_conf =
+ [&nv, &bad_name, &m] (size_t n, bool create) -> build_package_config&
+ {
+ const string& nm (nv.name);
+ small_vector<build_package_config, 1>& cs (m.build_configs);
+
+ // Find the build package configuration. If no configuration is found,
+ // then create one, if requested, and throw otherwise.
+ //
+ auto i (find_if (cs.begin (), cs.end (),
+ [&nm, n] (const build_package_config& c)
+ {return nm.compare (0, n, c.name) == 0;}));
+
+ if (i == cs.end ())
+ {
+ string cn (nm, 0, n);
+
+ if (create)
+ {
+ cs.emplace_back (move (cn));
+ return cs.back ();
+ }
+ else
+ bad_name ("cannot override '" + nm + "' value: no build " +
+ "package configuration '" + cn + '\'');
+ }
+
+ return *i;
+ };
+
+ // Return the reference to the package build configuration which matches
+ // the build config-specific builds group value override, if exists. If
+ // no configuration matches, then throw manifest_parsing, except for the
+ // validate-only mode in which case just add an empty configuration with
+ // this name and return the reference to it. Also verify that no common
+ // build constraints group value overrides are applied yet and throw if
+ // that's not the case.
+ //
+ auto build_conf_constr =
+ [&pbc, &cbc, &nv, &obcs, &bad_name, &build_conf, &m, validate_only]
+ (size_t n) -> build_package_config&
{
const string& nm (nv.name);
@@ -4533,30 +5105,8 @@ namespace bpkg
// Note that we are using indexes rather then configuration addresses
// due to potential reallocations.
//
- size_t ci (0); // Silence Clang's 'uninitialized use' warning.
- {
- auto i (find_if (cs.begin (), cs.end (),
- [&nm, n] (const build_package_config& c)
- {return nm.compare (0, n, c.name) == 0;}));
-
- if (i == cs.end ())
- {
- string cn (nm, 0, n);
-
- if (validate_only)
- {
- ci = cs.size ();
- cs.emplace_back (move (cn));
- }
- else
- bad_name ("cannot override '" + nm + "' value: no build " +
- "package configuration '" + cn + '\'');
- }
- else
- ci = i - cs.begin ();
- }
-
- build_package_config& r (cs[ci]);
+ build_package_config& r (build_conf (n, validate_only));
+ size_t ci (&r - cs.data ());
bool bv (nm.compare (n, nm.size () - n, "-builds") == 0);
// If this is the first encountered
@@ -4590,16 +5140,184 @@ namespace bpkg
return r;
};
- // Reset the {build-*email} value group on the first call.
+ // Reset the {build-bot} value group on the first call but throw if any
+ // of the {*-build-bot} override values are already encountered.
+ //
+ auto reset_build_bots = [&cbb, &pbb, &nv, &bad_name, &m] ()
+ {
+ if (cbb == nullptr)
+ {
+ if (pbb != nullptr)
+ bad_name ('\'' + nv.name + "' override specified together with '" +
+ pbb->name + "' override");
+
+ m.build_bot_keys.clear ();
+ cbb = &nv;
+ }
+ };
+
+ // Return the reference to the package build configuration which matches
+ // the build config-specific build bot value override, if exists. If no
+ // configuration matches, then throw manifest_parsing, except for the
+ // validate-only mode in which case just add an empty configuration with
+ // this name and return the reference to it. Also verify that no common
+ // build bot value overrides are applied yet and throw if that's not the
+ // case.
+ //
+ auto build_conf_bot =
+ [&pbb, &cbb, &nv, &obbs, &bad_name, &build_conf, &m, validate_only]
+ (size_t n) -> build_package_config&
+ {
+ const string& nm (nv.name);
+
+ // If this is the first build config override value, then save its
+ // address. But first verify that no common build bot value overrides
+ // are applied yet and throw if that's not the case.
+ //
+ if (pbb == nullptr)
+ {
+ if (cbb != nullptr)
+ bad_name ('\'' + nm + "' override specified together with '" +
+ cbb->name + "' override");
+
+ pbb = &nv;
+ }
+
+ small_vector<build_package_config, 1>& cs (m.build_configs);
+
+ // Find the build package configuration. If there is no such a
+ // configuration then throw, except for the validate-only mode in
+ // which case just add an empty configuration with this name.
+ //
+ // Note that we are using indexes rather then configuration addresses
+ // due to potential reallocations.
+ //
+ build_package_config& r (build_conf (n, validate_only));
+ size_t ci (&r - cs.data ());
+
+ // If this is the first encountered {*-build-bot} override for this
+ // build config, then clear this config' bot_keys members and add an
+ // entry to the overridden configs list.
+ //
+ if (find (obbs.begin (), obbs.end (), ci) == obbs.end ())
+ {
+ r.bot_keys.clear ();
+
+ obbs.push_back (ci);
+ }
+
+ return r;
+ };
+
+ // Reset the {build-*email} value group on the first call but throw if
+ // any of the {*-build-*email} override values are already encountered.
//
- auto reset_build_emails = [&be, &nv, &m] ()
+ auto reset_build_emails = [&cbe, &pbe, &nv, &bad_name, &m] ()
{
- if (be == nullptr)
+ if (cbe == nullptr)
{
+ if (pbe != nullptr)
+ bad_name ('\'' + nv.name + "' override specified together with '" +
+ pbe->name + "' override");
+
m.build_email = nullopt;
m.build_warning_email = nullopt;
m.build_error_email = nullopt;
- be = &nv;
+ cbe = &nv;
+ }
+ };
+
+ // Return the reference to the package build configuration which matches
+ // the build config-specific emails group value override, if exists. If
+ // no configuration matches, then throw manifest_parsing, except for the
+ // validate-only mode in which case just add an empty configuration with
+ // this name and return the reference to it. Also verify that no common
+ // build emails group value overrides are applied yet and throw if
+ // that's not the case.
+ //
+ auto build_conf_email =
+ [&pbe, &cbe, &nv, &obes, &bad_name, &build_conf, &m, validate_only]
+ (size_t n) -> build_package_config&
+ {
+ const string& nm (nv.name);
+
+ // If this is the first build config override value, then save its
+ // address. But first verify that no common build emails group value
+ // overrides are applied yet and throw if that's not the case.
+ //
+ if (pbe == nullptr)
+ {
+ if (cbe != nullptr)
+ bad_name ('\'' + nm + "' override specified together with '" +
+ cbe->name + "' override");
+
+ pbe = &nv;
+ }
+
+ small_vector<build_package_config, 1>& cs (m.build_configs);
+
+ // Find the build package configuration. If there is no such a
+ // configuration then throw, except for the validate-only mode in
+ // which case just add an empty configuration with this name.
+ //
+ // Note that we are using indexes rather then configuration addresses
+ // due to potential reallocations.
+ //
+ build_package_config& r (build_conf (n, validate_only));
+ size_t ci (&r - cs.data ());
+
+ // If this is the first encountered {*-build-*email} override for this
+ // build config, then clear this config' email members and add an
+ // entry to the overridden configs list.
+ //
+ if (find (obes.begin (), obes.end (), ci) == obes.end ())
+ {
+ r.email = nullopt;
+ r.warning_email = nullopt;
+ r.error_email = nullopt;
+
+ obes.push_back (ci);
+ }
+
+ return r;
+ };
+
+ // Parse the [*-]build-auxiliary[-*] value override. If the mode is not
+ // validate-only, then override the matching value and throw
+ // manifest_parsing if no match. But throw only unless this is a
+ // configuration-specific override (build_config is not NULL) for a
+ // newly created configuration, in which case add the value instead.
+ //
+ auto override_build_auxiliary =
+ [&bad_name,
+ &name,
+ &config_created,
+ validate_only] (const name_value& nv,
+ string&& en,
+ vector<build_auxiliary>& r,
+ build_package_config* build_config = nullptr)
+ {
+ build_auxiliary a (bpkg::parse_build_auxiliary (nv, move (en), name));
+
+ if (!validate_only)
+ {
+ auto i (find_if (r.begin (), r.end (),
+ [&a] (const build_auxiliary& ba)
+ {
+ return ba.environment_name == a.environment_name;
+ }));
+
+ if (i != r.end ())
+ {
+ *i = move (a);
+ }
+ else
+ {
+ if (build_config != nullptr && config_created (*build_config))
+ r.emplace_back (move (a));
+ else
+ bad_name ("no match for '" + nv.name + "' value override");
+ }
}
};
@@ -4626,9 +5344,26 @@ namespace bpkg
m.build_constraints.push_back (
parse_build_constraint (nv, true /* exclusion */, name));
}
+ else if (n == "build-bot")
+ {
+ reset_build_bots ();
+
+ parse_build_bot (nv, name, m.build_bot_keys);
+ }
+ else if ((n.size () > 13 &&
+ n.compare (n.size () - 13, 13, "-build-config") == 0))
+ {
+ build_package_config& bc (
+ build_conf (n.size () - 13, true /* create */));
+
+ auto vc (parser::split_comment (nv.value));
+
+ bc.arguments = move (vc.first);
+ bc.comment = move (vc.second);
+ }
else if (n.size () > 7 && n.compare (n.size () - 7, 7, "-builds") == 0)
{
- build_package_config& bc (build_conf (n.size () - 7));
+ build_package_config& bc (build_conf_constr (n.size () - 7));
bc.builds.push_back (
parse_build_class_expr (nv, bc.builds.empty (), name));
@@ -4636,7 +5371,7 @@ namespace bpkg
else if (n.size () > 14 &&
n.compare (n.size () - 14, 14, "-build-include") == 0)
{
- build_package_config& bc (build_conf (n.size () - 14));
+ build_package_config& bc (build_conf_constr (n.size () - 14));
bc.constraints.push_back (
parse_build_constraint (nv, false /* exclusion */, name));
@@ -4644,11 +5379,17 @@ namespace bpkg
else if (n.size () > 14 &&
n.compare (n.size () - 14, 14, "-build-exclude") == 0)
{
- build_package_config& bc (build_conf (n.size () - 14));
+ build_package_config& bc (build_conf_constr (n.size () - 14));
bc.constraints.push_back (
parse_build_constraint (nv, true /* exclusion */, name));
}
+ else if (n.size () > 10 &&
+ n.compare (n.size () - 10, 10, "-build-bot") == 0)
+ {
+ build_package_config& bc (build_conf_bot (n.size () - 10));
+ parse_build_bot (nv, name, bc.bot_keys);
+ }
else if (n == "build-email")
{
reset_build_emails ();
@@ -4664,6 +5405,44 @@ namespace bpkg
reset_build_emails ();
m.build_error_email = parse_email (nv, "build error", name);
}
+ else if (n.size () > 12 &&
+ n.compare (n.size () - 12, 12, "-build-email") == 0)
+ {
+ build_package_config& bc (build_conf_email (n.size () - 12));
+
+ bc.email = parse_email (
+ nv, "build configuration", name, true /* empty */);
+ }
+ else if (n.size () > 20 &&
+ n.compare (n.size () - 20, 20, "-build-warning-email") == 0)
+ {
+ build_package_config& bc (build_conf_email (n.size () - 20));
+
+ bc.warning_email = parse_email (
+ nv, "build configuration warning", name);
+ }
+ else if (n.size () > 18 &&
+ n.compare (n.size () - 18, 18, "-build-error-email") == 0)
+ {
+ build_package_config& bc (build_conf_email (n.size () - 18));
+
+ bc.error_email = parse_email (nv, "build configuration error", name);
+ }
+ else if (optional<pair<string, string>> ba =
+ build_auxiliary::parse_value_name (n))
+ {
+ if (ba->first.empty ()) // build-auxiliary*?
+ {
+ override_build_auxiliary (nv, move (ba->second), m.build_auxiliaries);
+ }
+ else // *-build-auxiliary*
+ {
+ build_package_config& bc (
+ build_conf (ba->first.size (), validate_only));
+
+ override_build_auxiliary (nv, move (ba->second), bc.auxiliaries, &bc);
+ }
+ }
else
bad_name ("cannot override '" + n + "' value");
}
@@ -4674,8 +5453,9 @@ namespace bpkg
assert (cbc == nullptr || pbc == nullptr);
// Now, if not in the validate-only mode, as all the potential build
- // constraint overrides are applied, perform the final adjustments to the
- // build config constraints.
+ // constraint, bot keys, and email overrides are applied, perform the
+ // final adjustments to the build config constraints, bot keys, and
+ // emails.
//
if (!validate_only)
{
@@ -4703,6 +5483,36 @@ namespace bpkg
}
}
}
+
+ if (cbb != nullptr) // Common build bots are overridden?
+ {
+ for (build_package_config& c: m.build_configs)
+ c.bot_keys.clear ();
+ }
+
+ if (cbe != nullptr) // Common build emails are overridden?
+ {
+ for (build_package_config& c: m.build_configs)
+ {
+ c.email = nullopt;
+ c.warning_email = nullopt;
+ c.error_email = nullopt;
+ }
+ }
+ else if (pbe != nullptr) // Build config emails are overridden?
+ {
+ for (size_t i (0); i != m.build_configs.size (); ++i)
+ {
+ if (find (obes.begin (), obes.end (), i) == obes.end ())
+ {
+ build_package_config& c (m.build_configs[i]);
+
+ c.email = email ();
+ c.warning_email = nullopt;
+ c.error_email = nullopt;
+ }
+ }
+ }
}
}
@@ -4720,66 +5530,87 @@ namespace bpkg
bpkg::override (nvs, name, p, true /* validate_only */);
}
- static const string description_file ("description-file");
- static const string changes_file ("changes-file");
- static const string build_file ("build-file");
+ static const string description_file ("description-file");
+ static const string package_description_file ("package-description-file");
+ static const string changes_file ("changes-file");
+ static const string build_file ("build-file");
void package_manifest::
load_files (const function<load_function>& loader, bool iu)
{
- // Load a file and verify that its content is not empty, if the loader
- // returns the content.
+ // If required, load a file and verify that its content is not empty, if
+ // the loader returns the content. Make the text type explicit.
//
- auto load = [&loader] (const string& n, const path& p)
+ auto load = [iu, &loader] (typed_text_file& text,
+ const string& file_value_name)
{
- optional<string> r (loader (n, p));
+ // Make the type explicit.
+ //
+ optional<text_type> t;
- if (r && r->empty ())
- throw parsing ("package " + n + " references empty file");
+ // Convert the potential invalid_argument exception to the
+ // manifest_parsing exception similar to what we do in the manifest
+ // parser.
+ //
+ try
+ {
+ t = text.effective_type (iu);
+ }
+ catch (const invalid_argument& e)
+ {
+ if (text.type)
+ {
+ // Strip trailing "-file".
+ //
+ string prefix (file_value_name, 0, file_value_name.size () - 5);
- return r;
- };
+ throw parsing ("invalid " + prefix + "-type package manifest " +
+ "value: " + e.what ());
+ }
+ else
+ {
+ throw parsing ("invalid " + file_value_name + " package " +
+ "manifest value: " + e.what ());
+ }
+ }
- // Load the description-file manifest value.
- //
- if (description)
- {
- // Make the description type explicit.
- //
- optional<text_type> t (effective_description_type (iu)); // Can throw.
assert (t || iu); // Can only be absent if we ignore unknown.
- if (!description_type && t)
- description_type = to_string (*t);
+ if (!text.type && t)
+ text.type = to_string (*t);
- // At this point the description type can only be absent if the
- // description comes from a file. Otherwise, we would end up with the
- // plain text.
+ // At this point the type can only be absent if the text comes from a
+ // file. Otherwise, we would end up with the plain text.
//
- assert (description_type || description->file);
+ assert (text.type || text.file);
- if (description->file)
+ if (text.file)
{
- if (!description_type)
- description_type = "text/unknown; extension=" +
- description->path.extension ();
+ if (!text.type)
+ text.type = "text/unknown; extension=" + text.path.extension ();
- if (optional<string> fc = load (description_file, description->path))
- description = text_file (move (*fc));
+ if (optional<string> fc = loader (file_value_name, text.path))
+ {
+ if (fc->empty ())
+ throw parsing ("package manifest value " + file_value_name +
+ " references empty file");
+
+ text = typed_text_file (move (*fc), move (text.type));
+ }
}
- }
+ };
- // Load the changes-file manifest values.
+ // Load the descriptions and changes, if present.
//
- for (text_file& c: changes)
- {
- if (c.file)
- {
- if (optional<string> fc = load (changes_file, c.path))
- c = text_file (move (*fc));
- }
- }
+ if (description)
+ load (*description, description_file);
+
+ if (package_description)
+ load (*package_description, package_description_file);
+
+ for (typed_text_file& c: changes)
+ load (c, changes_file);
// Load the build-file manifest values.
//
@@ -4837,6 +5668,12 @@ namespace bpkg
if (m.upstream_version)
s.next ("upstream-version", *m.upstream_version);
+ if (m.type)
+ s.next ("type", *m.type);
+
+ for (const language& l: m.languages)
+ s.next ("language", !l.impl ? l.name : l.name + "=impl");
+
if (m.project)
s.next ("project", m.project->string ());
@@ -4864,26 +5701,46 @@ namespace bpkg
if (!m.keywords.empty ())
s.next ("keywords", concatenate (m.keywords, " "));
- if (m.description)
+ auto serialize_text_file = [&s] (const text_file& v, const string& n)
{
- if (m.description->file)
- s.next ("description-file",
- serializer::merge_comment (m.description->path.string (),
- m.description->comment));
+ if (v.file)
+ s.next (n + "-file",
+ serializer::merge_comment (v.path.string (), v.comment));
else
- s.next ("description", m.description->text);
+ s.next (n, v.text);
+ };
- if (m.description_type)
- s.next ("description-type", *m.description_type);
- }
+ auto serialize_description = [&s, &serialize_text_file]
+ (const optional<typed_text_file>& desc,
+ const char* prefix)
+ {
+ if (desc)
+ {
+ string p (prefix);
+ serialize_text_file (*desc, p + "description");
+
+ if (desc->type)
+ s.next (p + "description-type", *desc->type);
+ }
+ };
+
+ serialize_description (m.description, "" /* prefix */);
+ serialize_description (m.package_description, "package-");
for (const auto& c: m.changes)
+ serialize_text_file (c, "changes");
+
+ // If there are any changes, then serialize the type of the first
+ // changes entry, if present. Note that if it is present, then we assume
+ // that the type was specified explicitly and so it is the same for all
+ // entries.
+ //
+ if (!m.changes.empty ())
{
- if (c.file)
- s.next ("changes-file",
- serializer::merge_comment (c.path.string (), c.comment));
- else
- s.next ("changes", c.text);
+ const typed_text_file& c (m.changes.front ());
+
+ if (c.type)
+ s.next ("changes-type", *c.type);
}
if (m.url)
@@ -4964,12 +5821,17 @@ namespace bpkg
: c.config + '/' + *c.target,
c.comment));
+ for (const build_auxiliary& ba: m.build_auxiliaries)
+ s.next ((!ba.environment_name.empty ()
+ ? "build-auxiliary-" + ba.environment_name
+ : "build-auxiliary"),
+ serializer::merge_comment (ba.config, ba.comment));
+
+ for (const string& k: m.build_bot_keys)
+ s.next ("build-bot", k);
+
for (const build_package_config& bc: m.build_configs)
{
- if (!bc.arguments.empty () || !bc.comment.empty ())
- s.next (bc.name + "-build-config",
- serializer::merge_comment (bc.arguments, bc.comment));
-
if (!bc.builds.empty ())
{
string n (bc.name + "-builds");
@@ -4989,6 +5851,43 @@ namespace bpkg
: c.config + '/' + *c.target,
c.comment));
}
+
+ if (!bc.auxiliaries.empty ())
+ {
+ string n (bc.name + "-build-auxiliary");
+
+ for (const build_auxiliary& ba: bc.auxiliaries)
+ s.next ((!ba.environment_name.empty ()
+ ? n + '-' + ba.environment_name
+ : n),
+ serializer::merge_comment (ba.config, ba.comment));
+ }
+
+ if (!bc.bot_keys.empty ())
+ {
+ string n (bc.name + "-build-bot");
+
+ for (const string& k: bc.bot_keys)
+ s.next (n, k);
+ }
+
+ if (!bc.arguments.empty () || !bc.comment.empty ())
+ s.next (bc.name + "-build-config",
+ serializer::merge_comment (bc.arguments, bc.comment));
+
+ if (bc.email)
+ s.next (bc.name + "-build-email",
+ serializer::merge_comment (*bc.email, bc.email->comment));
+
+ if (bc.warning_email)
+ s.next (bc.name + "-build-warning-email",
+ serializer::merge_comment (*bc.warning_email,
+ bc.warning_email->comment));
+
+ if (bc.error_email)
+ s.next (bc.name + "-build-error-email",
+ serializer::merge_comment (*bc.error_email,
+ bc.error_email->comment));
}
bool an (m.alt_naming && *m.alt_naming);
@@ -5007,6 +5906,9 @@ namespace bpkg
for (const path& f: m.buildfile_paths)
s.next ("build-file", f.posix_string () + (an ? ".build2" : ".build"));
+ for (const distribution_name_value& nv: m.distribution_values)
+ s.next (nv.name, nv.value);
+
if (m.location)
s.next ("location", m.location->posix_string ());
@@ -5258,20 +6160,26 @@ namespace bpkg
d + " for " + p.name.string () + '-' + p.version.string ());
};
- if (p.description)
+ // Throw manifest_serialization if the text is in a file or untyped.
+ //
+ auto verify_text_file = [&bad_value] (const typed_text_file& v,
+ const string& n)
{
- if (p.description->file)
- bad_value ("forbidden description-file");
+ if (v.file)
+ bad_value ("forbidden " + n + "-file");
- if (!p.description_type)
- bad_value ("no valid description-type");
- }
+ if (!v.type)
+ bad_value ("no valid " + n + "-type");
+ };
+
+ if (p.description)
+ verify_text_file (*p.description, "description");
+
+ if (p.package_description)
+ verify_text_file (*p.package_description, "package-description");
for (const auto& c: p.changes)
- {
- if (c.file)
- bad_value ("forbidden changes-file");
- }
+ verify_text_file (c, "changes");
if (!p.buildfile_paths.empty ())
bad_value ("forbidden build-file");
diff --git a/libbpkg/manifest.hxx b/libbpkg/manifest.hxx
index 3b75830..feb3b96 100644
--- a/libbpkg/manifest.hxx
+++ b/libbpkg/manifest.hxx
@@ -10,8 +10,7 @@
#include <cassert>
#include <cstdint> // uint*_t
#include <ostream>
-#include <utility> // move()
-#include <stdexcept> // logic_error
+#include <utility> // move(), pair
#include <functional>
#include <libbutl/url.hxx>
@@ -102,7 +101,7 @@ namespace bpkg
version (version&&) = default;
version (const version&) = default;
- version& operator= (version&&);
+ version& operator= (version&&) noexcept;
version& operator= (const version&);
// If the revision is ignored, then the iteration (that semantically
@@ -111,23 +110,12 @@ namespace bpkg
std::string
string (bool ignore_revision = false, bool ignore_iteration = false) const;
- bool
- operator< (const version& v) const noexcept {return compare (v) < 0;}
-
- bool
- operator> (const version& v) const noexcept {return compare (v) > 0;}
-
- bool
- operator== (const version& v) const noexcept {return compare (v) == 0;}
-
- bool
- operator<= (const version& v) const noexcept {return compare (v) <= 0;}
-
- bool
- operator>= (const version& v) const noexcept {return compare (v) >= 0;}
-
- bool
- operator!= (const version& v) const noexcept {return compare (v) != 0;}
+ bool operator< (const version& v) const noexcept;
+ bool operator> (const version& v) const noexcept;
+ bool operator== (const version& v) const noexcept;
+ bool operator<= (const version& v) const noexcept;
+ bool operator>= (const version& v) const noexcept;
+ bool operator!= (const version& v) const noexcept;
// If the revision is ignored, then the iteration is also ignored,
// regardless of the argument (see above for details).
@@ -135,28 +123,7 @@ namespace bpkg
int
compare (const version& v,
bool ignore_revision = false,
- bool ignore_iteration = false) const noexcept
- {
- if (epoch != v.epoch)
- return epoch < v.epoch ? -1 : 1;
-
- if (int c = canonical_upstream.compare (v.canonical_upstream))
- return c;
-
- if (int c = canonical_release.compare (v.canonical_release))
- return c;
-
- if (!ignore_revision)
- {
- if (revision != v.revision)
- return revision < v.revision ? -1 : 1;
-
- if (!ignore_iteration && iteration != v.iteration)
- return iteration < v.iteration ? -1 : 1;
- }
-
- return 0;
- }
+ bool ignore_iteration = false) const noexcept;
bool
empty () const noexcept
@@ -207,33 +174,10 @@ namespace bpkg
return os << (v.empty () ? "<empty-version>" : v.string ());
}
- inline version::flags
- operator&= (version::flags& x, version::flags y)
- {
- return x = static_cast<version::flags> (
- static_cast<std::uint16_t> (x) &
- static_cast<std::uint16_t> (y));
- }
-
- inline version::flags
- operator|= (version::flags& x, version::flags y)
- {
- return x = static_cast<version::flags> (
- static_cast<std::uint16_t> (x) |
- static_cast<std::uint16_t> (y));
- }
-
- inline version::flags
- operator& (version::flags x, version::flags y)
- {
- return x &= y;
- }
-
- inline version::flags
- operator| (version::flags x, version::flags y)
- {
- return x |= y;
- }
+ version::flags operator& (version::flags, version::flags);
+ version::flags operator| (version::flags, version::flags);
+ version::flags operator&= (version::flags&, version::flags);
+ version::flags operator|= (version::flags&, version::flags);
// priority
//
@@ -251,11 +195,17 @@ namespace bpkg
operator value_type () const {return value;}
};
- // description
- // description-file
- // change
- // change-file
+ // language
//
+ struct language
+ {
+ std::string name;
+ bool impl; // True if implementation-only.
+
+ language (): impl (false) {}
+ language (std::string n, bool i): name (std::move (n)), impl (i) {}
+ };
+
class LIBBPKG_EXPORT text_file
{
public:
@@ -281,14 +231,80 @@ namespace bpkg
text_file (path_type p, std::string c)
: file (true), path (std::move (p)), comment (std::move (c)) {}
- text_file (text_file&&);
+ text_file (text_file&&) noexcept;
text_file (const text_file&);
- text_file& operator= (text_file&&);
+ text_file& operator= (text_file&&) noexcept;
text_file& operator= (const text_file&);
~text_file ();
};
+ enum class text_type
+ {
+ plain,
+ common_mark,
+ github_mark
+ };
+
+ LIBBPKG_EXPORT std::string
+ to_string (text_type);
+
+ // Throw std::invalid_argument if the argument is not a well-formed text
+ // type. Otherwise, return nullopt for an unknown text variant.
+ //
+ LIBBPKG_EXPORT butl::optional<text_type>
+ to_text_type (const std::string&);
+
+ inline std::ostream&
+ operator<< (std::ostream& os, text_type t)
+ {
+ return os << to_string (t);
+ }
+
+ // description
+ // description-file
+ // description-type
+ // package-description
+ // package-description-file
+ // package-description-type
+ // change
+ // change-file
+ // change-type
+ //
+ class LIBBPKG_EXPORT typed_text_file: public text_file
+ {
+ public:
+ butl::optional<std::string> type;
+
+ // File text constructor.
+ //
+ explicit
+ typed_text_file (std::string s = "",
+ butl::optional<std::string> t = butl::nullopt)
+ : text_file (std::move (s)), type (std::move (t)) {}
+
+ // File reference constructor.
+ //
+ typed_text_file (path_type p,
+ std::string c,
+ butl::optional<std::string> t = butl::nullopt)
+ : text_file (std::move (p), std::move (c)), type (std::move (t)) {}
+
+ // Return the type value if present, text_type::github_mark if it refers
+ // to a file with the .md or .markdown extension and text_type::plain if
+ // it refers to a file with the .txt extension or no extension or the text
+ // does not come from a file. Depending on the ignore_unknown value either
+ // throw std::invalid_argument or return nullopt if the type value or the
+ // file extension is unknown.
+ //
+ // Note: also throws std::invalid_argument if the type is not well-formed.
+ // This, however, may not happen for an object created by the package
+ // manifest parser since it has already verified that.
+ //
+ butl::optional<text_type>
+ effective_type (bool ignore_unknown = false) const;
+ };
+
// license
//
class licenses: public butl::small_vector<std::string, 1>
@@ -415,17 +431,10 @@ namespace bpkg
}
inline bool
- operator== (const version_constraint& x, const version_constraint& y)
- {
- return x.min_version == y.min_version && x.max_version == y.max_version &&
- x.min_open == y.min_open && x.max_open == y.max_open;
- }
+ operator== (const version_constraint&, const version_constraint&);
inline bool
- operator!= (const version_constraint& x, const version_constraint& y)
- {
- return !(x == y);
- }
+ operator!= (const version_constraint&, const version_constraint&);
struct LIBBPKG_EXPORT dependency
{
@@ -447,11 +456,8 @@ namespace bpkg
string () const;
};
- inline std::ostream&
- operator<< (std::ostream& os, const dependency& d)
- {
- return os << d.string ();
- }
+ std::ostream&
+ operator<< (std::ostream&, const dependency&);
// depends
//
@@ -593,8 +599,13 @@ namespace bpkg
// Return true if the string() function would return the single-line
// representation.
//
- LIBBPKG_EXPORT bool
- single_line () const;
+ bool
+ single_line () const
+ {
+ return !prefer &&
+ !require &&
+ (!reflect || reflect->find ('\n') == std::string::npos);
+ }
};
inline std::ostream&
@@ -650,7 +661,7 @@ namespace bpkg
// Return true if there is a conditional alternative in the list.
//
- LIBBPKG_EXPORT bool
+ bool
conditional () const;
};
@@ -701,8 +712,11 @@ namespace bpkg
// Return true if the string() function would return the single-line
// representation.
//
- LIBBPKG_EXPORT bool
- single_line () const;
+ bool
+ single_line () const
+ {
+ return !reflect || reflect->find ('\n') == std::string::npos;
+ }
// Return true if this is a single requirement with an empty id or an
// empty enable condition.
@@ -755,7 +769,7 @@ namespace bpkg
// Return true if there is a conditional alternative in the list.
//
- LIBBPKG_EXPORT bool
+ bool
conditional () const;
// Return true if this is a single simple requirement alternative.
@@ -811,47 +825,31 @@ namespace bpkg
//
enum class package_manifest_flags: std::uint16_t
{
- none = 0x000,
-
- forbid_file = 0x001, // Forbid *-file manifest values.
- forbid_location = 0x002,
- forbid_sha256sum = 0x004,
- forbid_fragment = 0x008,
- forbid_incomplete_dependencies = 0x010,
-
- require_location = 0x020,
- require_sha256sum = 0x040,
- require_description_type = 0x080,
- require_bootstrap_build = 0x100
+ 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);
// Target build configuration class term.
//
@@ -882,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 ();
@@ -973,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&
@@ -987,12 +980,56 @@ namespace bpkg
return os << bce.string ();
}
- // Package build configuration. Includes comment and optional target build
- // configuration class expressions/constraints overrides.
+ // 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
+ {
+ 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.
//
- class build_package_config
+ // 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
@@ -1006,12 +1043,44 @@ namespace bpkg
butl::small_vector<build_class_expr, 1> builds;
std::vector<build_constraint> constraints;
- build_package_config () = default;
+ // 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 (std::string n): name (move (n)) {}
+ 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
@@ -1031,29 +1100,53 @@ namespace bpkg
{
return !builds.empty () || !constraints.empty () ? constraints : common;
}
- };
- enum class text_type
- {
- plain,
- common_mark,
- github_mark
- };
+ // 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;
+ }
- LIBBPKG_EXPORT std::string
- to_string (text_type);
+ // 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;
+ }
- // Throw std::invalid_argument if the argument is not a well-formed text
- // type. Otherwise, return nullopt for an unknown text variant.
- //
- LIBBPKG_EXPORT butl::optional<text_type>
- to_text_type (const std::string&); // May throw std::invalid_argument.
+ // 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;
+ }
- inline std::ostream&
- operator<< (std::ostream& os, text_type t)
- {
- return os << to_string (t);
- }
+ 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
{
@@ -1080,6 +1173,7 @@ namespace bpkg
{
test_dependency_type type;
bool buildtime;
+ butl::optional<std::string> enable;
butl::optional<std::string> reflect;
test_dependency () = default;
@@ -1087,15 +1181,17 @@ namespace bpkg
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>] [<reflect-config>]` form. Throw
- // std::invalid_argument if the value is invalid.
+ // `[*] <name> [<version-constraint>] ['?' <enable-condition>] [<reflect-config>]`
+ // form. Throw std::invalid_argument if the value is invalid.
//
// Verify that the reflect clause, if present, refers to the test
// dependency package configuration variable. Note that such variable
@@ -1130,6 +1226,40 @@ namespace bpkg
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:
@@ -1140,16 +1270,18 @@ namespace bpkg
package_name name;
version_type version;
butl::optional<std::string> upstream_version;
+ butl::optional<std::string> type; // <name>[, ...]
+ butl::small_vector<language, 1> languages; // <name>[=impl][, ...]
butl::optional<package_name> project;
butl::optional<priority_type> priority;
std::string summary;
-
butl::small_vector<licenses, 1> license_alternatives;
+
butl::small_vector<std::string, 5> topics;
butl::small_vector<std::string, 5> keywords;
- butl::optional<text_file> description;
- butl::optional<std::string> description_type;
- butl::small_vector<text_file, 1> changes;
+ butl::optional<typed_text_file> description;
+ butl::optional<typed_text_file> package_description;
+ butl::small_vector<typed_text_file, 1> changes;
butl::optional<manifest_url> url;
butl::optional<manifest_url> doc_url;
butl::optional<manifest_url> src_url;
@@ -1163,11 +1295,16 @@ namespace bpkg
std::vector<requirement_alternatives> requirements;
butl::small_vector<test_dependency, 1> tests;
- // Common build classes/constraints that apply to all configurations
- // unless overridden.
+ // 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()
@@ -1189,6 +1326,10 @@ namespace bpkg
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).
//
@@ -1196,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;
@@ -1220,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 |
@@ -1242,7 +1409,7 @@ namespace bpkg
package_manifest (butl::manifest_parser&,
const std::function<translate_function>&,
bool ignore_unknown = false,
- bool complete_depends = true,
+ bool complete_values = true,
package_manifest_flags =
package_manifest_flags::forbid_location |
package_manifest_flags::forbid_sha256sum |
@@ -1257,7 +1424,7 @@ namespace bpkg
package_manifest (const std::string& name,
std::vector<butl::manifest_name_value>&&,
bool ignore_unknown = false,
- bool complete_dependencies = true,
+ bool complete_values = true,
package_manifest_flags =
package_manifest_flags::forbid_location |
package_manifest_flags::forbid_sha256sum |
@@ -1267,7 +1434,7 @@ namespace bpkg
std::vector<butl::manifest_name_value>&&,
const std::function<translate_function>&,
bool ignore_unknown = false,
- bool complete_depends = true,
+ bool complete_values = true,
package_manifest_flags =
package_manifest_flags::forbid_location |
package_manifest_flags::forbid_sha256sum |
@@ -1278,20 +1445,38 @@ 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:
+ // 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
@@ -1305,6 +1490,22 @@ namespace bpkg
// 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's
@@ -1350,9 +1551,10 @@ namespace bpkg
// Load the *-file manifest values using the specified load function that
// returns the file contents passing through any exception it may throw.
// If nullopt is returned, then the respective *-file value is left
- // unexpanded. Set the potentially absent description type value to the
- // effective description type. If the effective type is nullopt then
- // assign a synthetic unknown type.
+ // 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
@@ -1370,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);
@@ -1682,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.
@@ -1702,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.
//
@@ -1768,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.
@@ -2127,4 +2249,6 @@ namespace bpkg
}
}
+#include <libbpkg/manifest.ixx>
+
#endif // LIBBPKG_MANIFEST_HXX
diff --git a/libbpkg/manifest.ixx b/libbpkg/manifest.ixx
new file mode 100644
index 0000000..589d00f
--- /dev/null
+++ b/libbpkg/manifest.ixx
@@ -0,0 +1,412 @@
+// file : libbpkg/manifest.ixx -*- C++ -*-
+// license : MIT; see accompanying LICENSE file
+
+#include <stdexcept> // logic_error
+
+namespace bpkg
+{
+ // version
+ //
+ inline int version::
+ compare (const version& v, bool ir, bool ii) 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 (!ir)
+ {
+ if (revision != v.revision)
+ return revision < v.revision ? -1 : 1;
+
+ if (!ii && iteration != v.iteration)
+ return iteration < v.iteration ? -1 : 1;
+ }
+
+ return 0;
+ }
+
+ inline bool version::
+ operator< (const version& v) const noexcept
+ {
+ return compare (v) < 0;
+ }
+
+ inline bool version::
+ operator> (const version& v) const noexcept
+ {
+ return compare (v) > 0;
+ }
+
+ inline bool version::
+ operator== (const version& v) const noexcept
+ {
+ return compare (v) == 0;
+ }
+
+ inline bool version::
+ operator<= (const version& v) const noexcept
+ {
+ return compare (v) <= 0;
+ }
+
+ inline bool version::
+ operator>= (const version& v) const noexcept
+ {
+ return compare (v) >= 0;
+ }
+
+ inline bool version::
+ operator!= (const version& v) const noexcept
+ {
+ return compare (v) != 0;
+ }
+
+ inline version::flags
+ operator&= (version::flags& x, version::flags y)
+ {
+ return x = static_cast<version::flags> (
+ static_cast<std::uint16_t> (x) &
+ static_cast<std::uint16_t> (y));
+ }
+
+ inline version::flags
+ operator|= (version::flags& x, version::flags y)
+ {
+ return x = static_cast<version::flags> (
+ static_cast<std::uint16_t> (x) |
+ static_cast<std::uint16_t> (y));
+ }
+
+ inline version::flags
+ operator& (version::flags x, version::flags y)
+ {
+ return x &= y;
+ }
+
+ inline version::flags
+ operator| (version::flags x, version::flags y)
+ {
+ return x |= y;
+ }
+
+ // version_constraint
+ //
+ 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;
+ }
+
+ inline bool
+ operator!= (const version_constraint& x, const version_constraint& y)
+ {
+ return !(x == y);
+ }
+
+ // dependency
+ //
+ inline std::string dependency::
+ string () const
+ {
+ std::string r (name.string ());
+
+ if (constraint)
+ {
+ r += ' ';
+ r += constraint->string ();
+ }
+
+ return r;
+ }
+
+ inline std::ostream&
+ operator<< (std::ostream& os, const dependency& d)
+ {
+ return os << d.string ();
+ }
+
+ // dependency_alternatives
+ //
+ inline bool dependency_alternatives::
+ conditional () const
+ {
+ for (const dependency_alternative& da: *this)
+ {
+ if (da.enable)
+ return true;
+ }
+
+ return false;
+ }
+
+ // requirement_alternatives
+ //
+ inline bool requirement_alternatives::
+ conditional () const
+ {
+ for (const requirement_alternative& ra: *this)
+ {
+ if (ra.enable)
+ return true;
+ }
+
+ return false;
+ }
+
+ // distribution_name_value
+ //
+ inline butl::optional<std::string> distribution_name_value::
+ distribution (const std::string& s) const
+ {
+ using namespace std;
+
+ size_t sn (s.size ());
+ size_t nn (name.size ());
+
+ if (nn > sn && name.compare (nn - sn, sn, s) == 0)
+ {
+ size_t p (name.find ('-'));
+
+ if (p == nn - sn)
+ return string (name, 0, p);
+ }
+
+ return butl::nullopt;
+ }
+
+ // 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));
+ }
+
+ 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));
+ }
+
+ inline package_manifest_flags
+ operator& (package_manifest_flags x, package_manifest_flags y)
+ {
+ return x &= y;
+ }
+
+ inline package_manifest_flags
+ operator| (package_manifest_flags x, package_manifest_flags y)
+ {
+ return x |= y;
+ }
+
+ // build_class_expr
+ //
+ inline bool build_class_expr::
+ match (const strings& cs, const build_class_inheritance_map& bs) const
+ {
+ bool r (false);
+ match (cs, bs, r);
+ return r;
+ }
+
+ // package_manifest
+ //
+ inline package_manifest::
+ package_manifest (butl::manifest_parser& p,
+ bool iu,
+ bool cv,
+ package_manifest_flags fl)
+ : package_manifest (p, std::function<translate_function> (), iu, cv, fl)
+ {
+ }
+
+ inline package_manifest
+ pkg_package_manifest (butl::manifest_parser& p, bool iu, bool cvs)
+ {
+ return package_manifest (p, iu, cvs);
+ }
+
+ inline std::string package_manifest::
+ effective_type (const butl::optional<std::string>& t, const package_name& n)
+ {
+ if (t)
+ {
+ std::string tp (*t, 0, t->find (','));
+ butl::trim (tp);
+ return tp == "exe" || tp == "lib" ? tp : "other";
+ }
+
+ const std::string& s (n.string ());
+ return s.size () > 3 && s.compare (0, 3, "lib") == 0 ? "lib" : "exe";
+ }
+
+ inline std::string package_manifest::
+ effective_type () const
+ {
+ return effective_type (type, name);
+ }
+
+ inline strings package_manifest::
+ effective_type_sub_options () const
+ {
+ return effective_type_sub_options (type);
+ }
+
+ inline butl::small_vector<language, 1> package_manifest::
+ effective_languages (const butl::small_vector<language, 1>& ls,
+ const package_name& n)
+ {
+ if (!ls.empty ())
+ return ls;
+
+ std::string ext (n.extension ());
+ return butl::small_vector<language, 1> (
+ 1,
+ language (!ext.empty () ? move (ext) : "cc", false /* impl */));
+ }
+
+ inline butl::small_vector<language, 1> package_manifest::
+ effective_languages () const
+ {
+ return effective_languages (languages, name);
+ }
+
+ // repository_location
+ //
+ inline repository_type repository_location::
+ type () const
+ {
+ if (empty ())
+ throw std::logic_error ("empty location");
+
+ return type_;
+ }
+
+ inline repository_location::
+ repository_location (const repository_location& l,
+ const repository_location& base)
+ : repository_location (l.url (), l.type (), base)
+ {
+ }
+
+ inline bool repository_location::
+ local () const
+ {
+ if (empty ())
+ throw std::logic_error ("empty location");
+
+ return url_.scheme == repository_protocol::file;
+ }
+
+ inline bool repository_location::
+ remote () const
+ {
+ return !local ();
+ }
+
+ inline bool repository_location::
+ absolute () const
+ {
+ if (empty ())
+ throw std::logic_error ("empty location");
+
+ // Note that in remote locations path is always relative.
+ //
+ return url_.path->absolute ();
+ }
+
+ inline bool repository_location::
+ relative () const
+ {
+ return local () && url_.path->relative ();
+ }
+
+ inline repository_basis repository_location::
+ 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;
+ }
+
+ inline bool repository_location::
+ archive_based () const
+ {
+ return basis () == repository_basis::archive;
+ }
+
+ inline bool repository_location::
+ directory_based () const
+ {
+ return basis () == repository_basis::directory;
+ }
+
+ inline bool repository_location::
+ version_control_based () const
+ {
+ return basis () == repository_basis::version_control;
+ }
+
+ inline const butl::path& repository_location::
+ path () const
+ {
+ if (empty ())
+ throw std::logic_error ("empty location");
+
+ return *url_.path;
+ }
+
+ inline const std::string& repository_location::
+ host () const
+ {
+ if (local ())
+ throw std::logic_error ("local location");
+
+ return url_.authority->host;
+ }
+
+ inline std::uint16_t repository_location::
+ port () const
+ {
+ if (local ())
+ throw std::logic_error ("local location");
+
+ return url_.authority->port;
+ }
+
+ inline repository_protocol repository_location::
+ proto () const
+ {
+ if (empty ())
+ throw std::logic_error ("empty location");
+
+ return url_.scheme;
+ }
+
+ inline const butl::optional<std::string>& repository_location::
+ fragment () const
+ {
+ if (relative ())
+ throw std::logic_error ("relative filesystem path");
+
+ return url_.fragment;
+ }
+}
diff --git a/manifest b/manifest
index 2d3e374..454925e 100644
--- a/manifest
+++ b/manifest
@@ -1,6 +1,6 @@
: 1
name: libbpkg
-version: 0.16.0-a.0.z
+version: 0.17.0-a.0.z
project: build2
summary: build2 package dependency manager utility library
license: MIT
@@ -12,8 +12,8 @@ doc-url: https://build2.org/doc.xhtml
src-url: https://git.build2.org/cgit/libbpkg/tree/
email: users@build2.org
build-warning-email: builds@build2.org
-builds: host
+builds: all : &host
requires: c++14
-depends: * build2 >= 0.15.0-
-depends: * bpkg >= 0.15.0-
-depends: libbutl [0.16.0-a.0.1 0.16.0-a.1)
+depends: * build2 >= 0.16.0-
+depends: * bpkg >= 0.16.0-
+depends: libbutl [0.17.0-a.0.1 0.17.0-a.1)
diff --git a/tests/build/root.build b/tests/build/root.build
index 8a3f2e7..f97c101 100644
--- a/tests/build/root.build
+++ b/tests/build/root.build
@@ -14,8 +14,15 @@ if ($cxx.target.system == 'win32-msvc')
if ($cxx.class == 'msvc')
cxx.coptions += /wd4251 /wd4275 /wd4800
elif ($cxx.id == 'gcc')
+{
cxx.coptions += -Wno-maybe-uninitialized -Wno-free-nonheap-object # libbutl
+ if ($cxx.version.major >= 13)
+ cxx.coptions += -Wno-dangling-reference
+}
+elif ($cxx.id.type == 'clang' && $cxx.version.major >= 15)
+ cxx.coptions += -Wno-unqualified-std-cast-call
+
# Every exe{} in this subproject is by default a test.
#
exe{*}: test = true
diff --git a/tests/manifest/driver.cxx b/tests/manifest/driver.cxx
index c0d8693..56c886d 100644
--- a/tests/manifest/driver.cxx
+++ b/tests/manifest/driver.cxx
@@ -23,6 +23,7 @@ using namespace bpkg;
// argv[0] (-pp|-dp|-gp|-pr|-dr|-gr|-s) [-l]
// argv[0] -p [-c] [-i] [-l]
// argv[0] -ec <version>
+// argv[0] -et <type> <name>
// argv[0] -v
//
// In the first form read and parse manifest list from stdin and serialize it
@@ -40,7 +41,7 @@ using namespace bpkg;
// In the second form read and parse the package manifest from stdin and
// serialize it to stdout.
//
-// -c complete the dependency constraints
+// -c complete the incomplete values (depends, <distribution>-version, etc)
// -i ignore unknown
//
// Note: the above options should go after -p on the command line.
@@ -52,7 +53,10 @@ using namespace bpkg;
// roundtrip them to stdout together with their effective constraints,
// calculated using version passed as an argument.
//
-// In the forth form print the libbpkg version to stdout and exit.
+// In the forth form print the effective type and the type sub-options to
+// stdout (one per line) and exit.
+//
+// In the fifth form print the libbpkg version to stdout and exit.
//
int
main (int argc, char* argv[])
@@ -74,7 +78,7 @@ main (int argc, char* argv[])
{
if (mode == "-p")
{
- bool complete_dependencies (false);
+ bool complete_values (false);
bool ignore_unknown (false);
bool long_lines (false);
@@ -83,7 +87,7 @@ main (int argc, char* argv[])
string o (argv[i]);
if (o == "-c")
- complete_dependencies = true;
+ complete_values = true;
else if (o == "-i")
ignore_unknown = true;
else if (o == "-l")
@@ -114,7 +118,7 @@ main (int argc, char* argv[])
}
},
ignore_unknown,
- complete_dependencies).serialize (s);
+ complete_values).serialize (s);
}
else if (mode == "-ec")
{
@@ -135,6 +139,21 @@ main (int argc, char* argv[])
cout << c << " " << ec << endl;
}
}
+ else if (mode == "-et")
+ {
+ assert (argc == 4);
+
+ optional<string> t (*argv[2] != '\0'
+ ? string (argv[2])
+ : optional<string> ());
+
+ package_name n (argv[3]);
+
+ cout << package_manifest::effective_type (t, n) << endl;
+
+ for (const string& so: package_manifest::effective_type_sub_options (t))
+ cout << so << endl;
+ }
else
{
bool long_lines (false);
diff --git a/tests/manifest/testscript b/tests/manifest/testscript
index 336b288..d7ec37f 100644
--- a/tests/manifest/testscript
+++ b/tests/manifest/testscript
@@ -102,6 +102,176 @@
EOE
}
+ : type
+ :
+ {
+ : valid
+ :
+ $* <<EOF >>EOF
+ : 1
+ name: libfoo
+ version: 2.0.0
+ type: lib
+ summary: Modern C++ parser
+ license: LGPLv2
+ EOF
+
+ : extras
+ :
+ $* <<EOI >>EOO
+ : 1
+ name: foo
+ version: 2.0.0
+ type: bash, something extra
+ summary: Modern C++ parser
+ license: LGPLv2
+ EOI
+ : 1
+ name: foo
+ version: 2.0.0
+ type: bash, something extra
+ summary: Modern C++ parser
+ license: LGPLv2
+ EOO
+
+ : duplicate
+ :
+ $* <<EOI 2>'stdin:5:1: error: package type redefinition' != 0
+ : 1
+ name: libfoo
+ version: 2.0.0
+ type: lib
+ type: exe
+ summary: Modern C++ parser
+ license: LGPLv2
+ EOI
+
+ : empty
+ :
+ $* <<EOI 2>'stdin:4:6: error: empty package type' != 0
+ : 1
+ name: libfoo
+ version: 2.0.0
+ type:
+ summary: Modern C++ parser
+ license: LGPLv2
+ EOI
+
+ : empty-extras
+ :
+ $* <<EOI 2>'stdin:4:7: error: empty package type' != 0
+ : 1
+ name: libfoo
+ version: 2.0.0
+ type: , extras
+ summary: Modern C++ parser
+ license: LGPLv2
+ EOI
+ }
+
+ : language
+ :
+ {
+ : valid
+ :
+ $* <<EOF >>EOF
+ : 1
+ name: libfoo
+ version: 2.0.0
+ language: c++
+ language: c=impl
+ summary: Modern C++ parser
+ license: LGPLv2
+ EOF
+
+ : extras
+ :
+ $* <<EOI >>EOO
+ : 1
+ name: foo
+ version: 2.0.0
+ language: c++, something extra
+ language: c=impl, something extra
+ summary: Modern C++ parser
+ license: LGPLv2
+ EOI
+ : 1
+ name: foo
+ version: 2.0.0
+ language: c++
+ language: c=impl
+ summary: Modern C++ parser
+ license: LGPLv2
+ EOO
+
+ : empty
+ :
+ $* <<EOI 2>'stdin:4:10: error: empty package language' != 0
+ : 1
+ name: libfoo
+ version: 2.0.0
+ language:
+ summary: Modern C++ parser
+ license: LGPLv2
+ EOI
+
+ : empty-extras
+ :
+ $* <<EOI 2>'stdin:4:11: error: empty package language' != 0
+ : 1
+ name: libfoo
+ version: 2.0.0
+ language: , extras
+ summary: Modern C++ parser
+ license: LGPLv2
+ EOI
+
+ : empty-impl
+ :
+ $* <<EOI 2>'stdin:4:11: error: empty package language' != 0
+ : 1
+ name: libfoo
+ version: 2.0.0
+ language: =impl
+ summary: Modern C++ parser
+ license: LGPLv2
+ EOI
+
+ : invalid-value
+ :
+ $* <<EOI 2>"stdin:4:11: error: unexpected 'imp' value after '='" != 0
+ : 1
+ name: libfoo
+ version: 2.0.0
+ language: c++=imp
+ summary: Modern C++ parser
+ license: LGPLv2
+ EOI
+
+ : empty-value
+ :
+ $* <<EOI 2>"stdin:4:11: error: expected 'impl' after '='" != 0
+ : 1
+ name: libfoo
+ version: 2.0.0
+ language: c++=
+ summary: Modern C++ parser
+ license: LGPLv2
+ EOI
+
+ : duplicate
+ :
+ $* <<EOI 2>"stdin:5:11: error: duplicate package language" != 0
+ : 1
+ name: libfoo
+ version: 2.0.0
+ language: c++=impl
+ language: c++
+ summary: Modern C++ parser
+ license: LGPLv2
+ EOI
+ }
+
: license
:
{
@@ -257,9 +427,9 @@
description-file: /README
EOI
%(
- stdin:6:19: error: package description-file path is absolute
+ stdin:6:19: error: project description file path is absolute
%|
- stdin:6:19: error: invalid package description file: invalid filesystem path
+ stdin:6:19: error: invalid project description file: invalid filesystem path
%)
EOE
}
@@ -289,7 +459,20 @@
description: libfoo is a very modern C++ XML parser.
description-type: image/gif
EOI
- stdin:7:19: error: invalid package description type: text type expected
+ stdin:7:19: error: invalid project description type: text type expected
+ EOE
+
+ : no-description
+ :
+ $* <<EOI 2>>EOE != 0
+ : 1
+ name: libfoo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ description-type: image/gif
+ EOI
+ stdin:6:1: error: no project description for specified type
EOE
: deducing
@@ -305,7 +488,7 @@
license: LGPLv2
description-file: README.rtf
EOI
- stdin:6:19: error: invalid package description file: unknown text type
+ stdin:6:19: error: invalid project description file: unknown text type (use description-type manifest value to specify explicitly)
EOE
: ignore-unknown
@@ -334,7 +517,7 @@
description: libfoo is a very modern C++ XML parser.
description-type: text/markdowns
EOI
- stdin:7:19: error: invalid package description type: unknown text type
+ stdin:7:19: error: invalid project description type: unknown text type
EOE
: ignore
@@ -376,7 +559,7 @@
description: libfoo is a very modern C++ XML parser.
description-type: text/plain;
EOI
- stdin:7:19: error: invalid package description type: missing '='
+ stdin:7:19: error: invalid project description type: missing '='
EOE
}
@@ -433,7 +616,7 @@
description: libfoo is a very modern C++ XML parser.
description-type: text/markdown; variant=Original
EOI
- stdin:7:19: error: invalid package description type: unknown text type
+ stdin:7:19: error: invalid project description type: unknown text type
EOE
: ignore
@@ -463,7 +646,7 @@
description: libfoo is a very modern C++ XML parser.
description-type: text/markdown; variants=GFM
EOI
- stdin:7:19: error: invalid package description type: unknown text type
+ stdin:7:19: error: invalid project description type: unknown text type
EOE
: ignore
@@ -481,6 +664,330 @@
}
}
+ : package-description-file
+ :
+ {
+ : absolute-path
+ :
+ $* <<EOI 2>>~%EOE% != 0
+ : 1
+ name: libfoo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ package-description-file: /README
+ EOI
+ %(
+ stdin:6:27: error: package description file path is absolute
+ %|
+ stdin:6:27: error: invalid package description file: invalid filesystem path
+ %)
+ EOE
+ }
+
+ : package-description-type
+ :
+ {
+ : absent
+ :
+ $* <<EOF >>EOF
+ : 1
+ name: libfoo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ package-description: libfoo is a very modern C++ XML parser.
+ EOF
+
+ : not-text
+ :
+ $* <<EOI 2>>EOE != 0
+ : 1
+ name: libfoo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ package-description: libfoo is a very modern C++ XML parser.
+ package-description-type: image/gif
+ EOI
+ stdin:7:27: error: invalid package description type: text type expected
+ EOE
+
+ : no-description
+ :
+ $* <<EOI 2>>EOE != 0
+ : 1
+ name: libfoo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ package-description-type: image/gif
+ EOI
+ stdin:6:1: error: no package description for specified type
+ EOE
+
+ : deducing
+ :
+ {
+ : fail
+ :
+ $* <<EOI 2>>EOE != 0
+ : 1
+ name: libfoo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ package-description-file: README.rtf
+ EOI
+ stdin:6:27: error: invalid package description file: unknown text type (use package-description-type manifest value to specify explicitly)
+ EOE
+
+ : ignore-unknown
+ :
+ $* -i <<EOF >>EOF
+ : 1
+ name: libfoo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ package-description-file: README.rtf
+ EOF
+ }
+
+ : unknown
+ :
+ {
+ : fail
+ :
+ $* <<EOI 2>>EOE != 0
+ : 1
+ name: libfoo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ package-description: libfoo is a very modern C++ XML parser.
+ package-description-type: text/markdowns
+ EOI
+ stdin:7:27: error: invalid package description type: unknown text type
+ EOE
+
+ : ignore
+ :
+ $* -i <<EOF >>EOF
+ : 1
+ name: libfoo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ package-description: libfoo is a very modern C++ XML parser.
+ package-description-type: text/markdowns
+ EOF
+ }
+
+ : plain
+ :
+ {
+ : valid
+ :
+ $* <<EOF >>EOF
+ : 1
+ name: libfoo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ package-description: libfoo is a very modern C++ XML parser.
+ package-description-type: text/plain
+ EOF
+
+ : invalid
+ :
+ $* <<EOI 2>>EOE != 0
+ : 1
+ name: libfoo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ package-description: libfoo is a very modern C++ XML parser.
+ package-description-type: text/plain;
+ EOI
+ stdin:7:27: error: invalid package description type: missing '='
+ EOE
+ }
+
+ : markdown
+ :
+ {
+ : default
+ :
+ $* <<EOF >>EOF
+ : 1
+ name: libfoo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ package-description: libfoo is a very modern C++ XML parser.
+ package-description-type: text/markdown
+ EOF
+
+ : gfm
+ :
+ $* <<EOF >>EOF
+ : 1
+ name: libfoo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ package-description: libfoo is a very modern C++ XML parser.
+ package-description-type: text/markdown; variant=GFM
+ EOF
+
+ : common-mark
+ :
+ $* <<EOF >>EOF
+ : 1
+ name: libfoo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ package-description: libfoo is a very modern C++ XML parser.
+ package-description-type: text/markdown; variant=CommonMark
+ EOF
+
+ : invalid-variant
+ :
+ {
+ : fail
+ :
+ $* <<EOI 2>>EOE != 0
+ : 1
+ name: libfoo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ package-description: libfoo is a very modern C++ XML parser.
+ package-description-type: text/markdown; variant=Original
+ EOI
+ stdin:7:27: error: invalid package description type: unknown text type
+ EOE
+
+ : ignore
+ :
+ $* -i <<EOF >>EOF
+ : 1
+ name: libfoo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ package-description: libfoo is a very modern C++ XML parser.
+ package-description-type: text/markdown; variant=Original
+ EOF
+ }
+
+ : invalid-parameter
+ :
+ {
+ : fail
+ :
+ $* <<EOI 2>>EOE != 0
+ : 1
+ name: libfoo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ package-description: libfoo is a very modern C++ XML parser.
+ package-description-type: text/markdown; variants=GFM
+ EOI
+ stdin:7:27: error: invalid package description type: unknown text type
+ EOE
+
+ : ignore
+ :
+ $* -i <<EOF >>EOF
+ : 1
+ name: libfoo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ package-description: libfoo is a very modern C++ XML parser.
+ package-description-type: text/markdown; variants=GFM
+ EOF
+ }
+ }
+ }
+
+ : changes-file
+ :
+ {
+ : absolute-path
+ :
+ $* <<EOI 2>>~%EOE% != 0
+ : 1
+ name: libfoo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ changes-file: /CHANGES
+ EOI
+ %(
+ stdin:6:15: error: changes file path is absolute
+ %|
+ stdin:6:15: error: invalid changes file: invalid filesystem path
+ %)
+ EOE
+
+ : unknown-text-type
+ :
+ $* <<EOI 2>>EOE != 0
+ : 1
+ name: libfoo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ changes-file: CHANGES.0
+ EOI
+ stdin:6:15: error: invalid changes file: unknown text type (use changes-type manifest value to specify explicitly)
+ EOE
+
+ : different-type
+ :
+ $* <<EOI 2>>EOE != 0
+ : 1
+ name: libfoo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ changes-file: CHANGES1
+ changes-file: CHANGES2.md
+ EOI
+ stdin:7:15: error: changes type 'text/markdown;variant=GFM' differs from previous type 'text/plain'
+ EOE
+
+ : same-type
+ :
+ $* <<EOF >>EOF
+ : 1
+ name: libfoo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ changes-file: CHANGES1.markdown
+ changes-file: CHANGES2.md
+ EOF
+
+ : explicit-type
+ :
+ $* <<EOF >>EOF
+ : 1
+ name: libfoo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ changes-file: CHANGES1
+ changes-file: CHANGES2.md
+ changes-type: text/plain
+ EOF
+ }
+
: src-url
:
{
@@ -541,6 +1048,225 @@
EOI
}
+ : build-auxiliary
+ :
+ {
+ : named
+ :
+ {
+ $* <<EOF >>EOF
+ : 1
+ name: foo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ build-auxiliary-pgsql: *-postgresql_*
+ build-auxiliary-mysql: *-mysql_*
+ EOF
+ }
+
+ : unnamed
+ :
+ {
+ $* <<EOF >>EOF
+ : 1
+ name: foo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ build-auxiliary: *-postgresql**
+ EOF
+ }
+
+ : empty-config-pattern
+ :
+ {
+ $* <<EOI 2>>EOE != 0
+ : 1
+ name: foo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ build-auxiliary:
+ EOI
+ stdin:6:17: error: empty build auxiliary configuration name pattern
+ EOE
+ }
+
+ : mixed
+ :
+ {
+ : named-unnamed
+ :
+ {
+ $* <<EOF >>EOF
+ : 1
+ name: foo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ build-auxiliary-pgsql: *-postgresql**
+ build-auxiliary: *-mysql**
+ EOF
+ }
+
+ : unnamed-named
+ :
+ {
+ $* <<EOF >>EOF
+ : 1
+ name: foo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ build-auxiliary: *-mysql**
+ build-auxiliary-pgsql: *-postgresql**
+ EOF
+ }
+
+ : unnamed-unnamed
+ :
+ {
+ $* <<EOI 2>>EOE != 0
+ : 1
+ name: foo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ build-auxiliary: *-mysql**
+ build-auxiliary: *-postgresql**
+ EOI
+ stdin:7:1: error: build auxiliary environment redefinition
+ EOE
+ }
+
+ : redefinition
+ :
+ {
+ $* <<EOI 2>>EOE != 0
+ : 1
+ name: foo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ build-auxiliary-pgsql: *-postgresql**
+ build-auxiliary-pgsql: *-postgresql**
+ EOI
+ stdin:7:1: error: build auxiliary environment redefinition
+ EOE
+ }
+ }
+ }
+
+ : build-bot
+ :
+ {
+ : basics
+ :
+ {
+ $* <<EOF >>EOF
+ : 1
+ name: foo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ build-bot:\
+ -----BEGIN PUBLIC KEY-----
+ MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAw5liP5pyU9ebC/nD3djZ
+ 1H2dlKmUyiX0Z8POvKhLREd0B3rM59bPcnbRB4HMIhj0J0hUBvS8xb4u5udCPToa
+ x0A/LMWZ6claiivNtJ3CdLV98eklWdNUg5WXOuqq9QDKXw2ZpGbwDwCOh6aHSWVq
+ 98N9AQx0ZMmMWz3qhRyxPfh+GeJ05uj2ohU9FeUJxeqUcgJT/UcMZ3+7KYbwr+Uq
+ /HCoX1BmN6nvzhQGHvJIZ2IcjvOQ0AUrPmpSZN01Zr3ZEpkHM3hJWNLu3ntJLGBQ
+ 0aT5kG3iqFyr9q3M3c4J8c0AWrnDjvj0qnCyjNwqW+qIpatmCNT43DmgYr9fQLW0
+ UHusburz53AbXs12zu3gZzkb0irlShatkMqqQaqaU0/+zw1LnoZ+rvmn2XV97UuK
+ LFKMKXCnyi2ZG65IZHGkjBVAPuvsX6RgLNyner/QtkDJTbfhktInbG08dCPqv1EF
+ 1OtcYKMTn8I5P2VmMO6SXXDLMSdU8b5DA5EY6Ca6JBB8g06S9sqGqXgQFysAnZs1
+ VFgMopf8WZqj23x+DX+9KKT2pVnjbwRvBAntuCDoO75gWoETDnCQXEei/PbyamPq
+ 9+NjNsTDn67iJTGncZbII+eciY2YiFHm6GMzBPsUYlQcxiuO4X36jW6m2rwuw37K
+ oFDbGI3uY4LnhwmDFLbjtk8CAwEAAQ==
+ -----END PUBLIC KEY-----
+ \
+ build-bot:\
+ -----BEGIN PUBLIC KEY-----
+ AIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAw5liP5pyU9ebC/nD3djZ
+ 2H2dlKmUyiX0Z8POvKhLREd0B3rM59bPcnbRB4HMIhj0J0hUBvS8xb4u5udCPToa
+ x0A/LMWZ6claiivNtJ3CdLV98eklWdNUg5WXOuqq9QDKXw2ZpGbwDwCOh6aHSWVq
+ 98N9AQx0ZMmMWz3qhRyxPfh+GeJ05uj2ohU9FeUJxeqUcgJT/UcMZ3+7KYbwr+Uq
+ /HCoX1BmN6nvzhQGHvJIZ2IcjvOQ0AUrPmpSZN01Zr3ZEpkHM3hJWNLu3ntJLGBQ
+ 0aT5kG3iqFyr9q3M3c4J8c0AWrnDjvj0qnCyjNwqW+qIpatmCNT43DmgYr9fQLW0
+ UHusburz53AbXs12zu3gZzkb0irlShatkMqqQaqaU0/+zw1LnoZ+rvmn2XV97UuK
+ LFKMKXCnyi2ZG65IZHGkjBVAPuvsX6RgLNyner/QtkDJTbfhktInbG08dCPqv1EF
+ 1OtcYKMTn8I5P2VmMO6SXXDLMSdU8b5DA5EY6Ca6JBB8g06S9sqGqXgQFysAnZs1
+ VFgMopf8WZqj23x+DX+9KKT2pVnjbwRvBAntuCDoO75gWoETDnCQXEei/PbyamPq
+ 9+NjNsTDn67iJTGncZbII+eciY2YiFHm6GMzBPsUYlQcxiuO4X36jW6m2rwuw37K
+ oFDbGI3uY4LnhwmDFLbjtk8CAwEAAQ==
+ -----END PUBLIC KEY-----
+ \
+ EOF
+ }
+
+ : empty
+ :
+ {
+ $* <<EOI 2>>EOE != 0
+ : 1
+ name: foo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ build-bot:
+ EOI
+ stdin:6:11: error: empty custom build bot public key
+ EOE
+ }
+
+ : duplicate
+ :
+ {
+ $* <<EOI 2>>EOE != 0
+ : 1
+ name: foo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ build-bot:\
+ -----BEGIN PUBLIC KEY-----
+ MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAw5liP5pyU9ebC/nD3djZ
+ 1H2dlKmUyiX0Z8POvKhLREd0B3rM59bPcnbRB4HMIhj0J0hUBvS8xb4u5udCPToa
+ x0A/LMWZ6claiivNtJ3CdLV98eklWdNUg5WXOuqq9QDKXw2ZpGbwDwCOh6aHSWVq
+ 98N9AQx0ZMmMWz3qhRyxPfh+GeJ05uj2ohU9FeUJxeqUcgJT/UcMZ3+7KYbwr+Uq
+ /HCoX1BmN6nvzhQGHvJIZ2IcjvOQ0AUrPmpSZN01Zr3ZEpkHM3hJWNLu3ntJLGBQ
+ 0aT5kG3iqFyr9q3M3c4J8c0AWrnDjvj0qnCyjNwqW+qIpatmCNT43DmgYr9fQLW0
+ UHusburz53AbXs12zu3gZzkb0irlShatkMqqQaqaU0/+zw1LnoZ+rvmn2XV97UuK
+ LFKMKXCnyi2ZG65IZHGkjBVAPuvsX6RgLNyner/QtkDJTbfhktInbG08dCPqv1EF
+ 1OtcYKMTn8I5P2VmMO6SXXDLMSdU8b5DA5EY6Ca6JBB8g06S9sqGqXgQFysAnZs1
+ VFgMopf8WZqj23x+DX+9KKT2pVnjbwRvBAntuCDoO75gWoETDnCQXEei/PbyamPq
+ 9+NjNsTDn67iJTGncZbII+eciY2YiFHm6GMzBPsUYlQcxiuO4X36jW6m2rwuw37K
+ oFDbGI3uY4LnhwmDFLbjtk8CAwEAAQ==
+ -----END PUBLIC KEY-----
+ \
+ build-bot:\
+ -----BEGIN PUBLIC KEY-----
+ MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAw5liP5pyU9ebC/nD3djZ
+ 1H2dlKmUyiX0Z8POvKhLREd0B3rM59bPcnbRB4HMIhj0J0hUBvS8xb4u5udCPToa
+ x0A/LMWZ6claiivNtJ3CdLV98eklWdNUg5WXOuqq9QDKXw2ZpGbwDwCOh6aHSWVq
+ 98N9AQx0ZMmMWz3qhRyxPfh+GeJ05uj2ohU9FeUJxeqUcgJT/UcMZ3+7KYbwr+Uq
+ /HCoX1BmN6nvzhQGHvJIZ2IcjvOQ0AUrPmpSZN01Zr3ZEpkHM3hJWNLu3ntJLGBQ
+ 0aT5kG3iqFyr9q3M3c4J8c0AWrnDjvj0qnCyjNwqW+qIpatmCNT43DmgYr9fQLW0
+ UHusburz53AbXs12zu3gZzkb0irlShatkMqqQaqaU0/+zw1LnoZ+rvmn2XV97UuK
+ LFKMKXCnyi2ZG65IZHGkjBVAPuvsX6RgLNyner/QtkDJTbfhktInbG08dCPqv1EF
+ 1OtcYKMTn8I5P2VmMO6SXXDLMSdU8b5DA5EY6Ca6JBB8g06S9sqGqXgQFysAnZs1
+ VFgMopf8WZqj23x+DX+9KKT2pVnjbwRvBAntuCDoO75gWoETDnCQXEei/PbyamPq
+ 9+NjNsTDn67iJTGncZbII+eciY2YiFHm6GMzBPsUYlQcxiuO4X36jW6m2rwuw37K
+ oFDbGI3uY4LnhwmDFLbjtk8CAwEAAQ==
+ -----END PUBLIC KEY-----
+ \
+ EOI
+ stdin:23:1: error: duplicate custom build bot public key
+ EOE
+ }
+ }
+
: build-config
:
{
@@ -553,8 +1279,8 @@
version: 2.0.0
summary: Modern C++ parser
license: LGPLv2
- bar-build-config: config.foo.bar = true; Bar.
bar-builds: all
+ bar-build-config: config.foo.bar = true; Bar.
baz-build-config: config.foo.baz = true; Baz.
EOF
}
@@ -604,6 +1330,287 @@
bar-builds: all
EOI
}
+
+ : auxiliary
+ {
+ : named
+ :
+ {
+ $* <<EOF >>EOF
+ : 1
+ name: foo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ bar-build-auxiliary-pgsql: *-postgresql_*
+ baz-build-auxiliary-mysql: *-mysql_*
+ EOF
+ }
+
+ : unnamed
+ :
+ {
+ $* <<EOF >>EOF
+ : 1
+ name: foo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ bar-build-auxiliary: *-postgresql**
+ EOF
+ }
+
+ : empty-config-pattern
+ :
+ {
+ $* <<EOI 2>>EOE != 0
+ : 1
+ name: foo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ bar-build-auxiliary:
+ EOI
+ stdin:6:21: error: empty build auxiliary configuration name pattern
+ EOE
+ }
+
+ : mixed
+ :
+ {
+ : named-unnamed
+ :
+ {
+ $* <<EOF >>EOF
+ : 1
+ name: foo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ bar-build-auxiliary-pgsql: *-postgresql**
+ bar-build-auxiliary: *-mysql**
+ EOF
+ }
+
+ : unnamed-named
+ :
+ {
+ $* <<EOF >>EOF
+ : 1
+ name: foo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ bar-build-auxiliary: *-mysql**
+ bar-build-auxiliary-pgsql: *-postgresql**
+ EOF
+ }
+
+ : unnamed-unnamed
+ :
+ {
+ $* <<EOI 2>>EOE != 0
+ : 1
+ name: foo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ bar-build-auxiliary: *-mysql**
+ bar-build-auxiliary: *-postgresql**
+ EOI
+ stdin:7:1: error: build auxiliary environment redefinition
+ EOE
+ }
+
+ : redefinition
+ :
+ {
+ $* <<EOI 2>>EOE != 0
+ : 1
+ name: foo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ bar-build-auxiliary-pgsql: *-postgresql**
+ bar-build-auxiliary-pgsql: *-postgresql**
+ EOI
+ stdin:7:1: error: build auxiliary environment redefinition
+ EOE
+ }
+ }
+ }
+
+ : email
+ :
+ {
+ : override
+ :
+ {
+ $* <<EOF >>EOF
+ : 1
+ name: foo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ package-email: package@example.com
+ build-email: build@example.com
+ build-warning-email: build-warning@example.com
+ build-error-email: build-error@example.com
+ bar-build-config: config.foo.bar = true; Bar.
+ bar-build-email: bar-build@example.com
+ bar-build-warning-email: bar-build-warning@example.com
+ bar-build-error-email: bar-build-error@example.com
+ EOF
+ }
+
+ : disabled
+ :
+ {
+ $* <<EOF >>EOF
+ : 1
+ name: foo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ package-email: package@example.com
+ build-email: build@example.com
+ build-warning-email: build-warning@example.com
+ build-error-email: build-error@example.com
+ bar-build-config: config.foo.bar = true; Bar.
+ bar-build-email:
+ EOF
+ }
+
+ : unrecognized
+ :
+ {
+ $* <<EOI 2>>EOE != 0
+ : 1
+ name: foo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ build-error-email: build-error@example.com
+ bar-build-email: bar-build@example.com
+ EOI
+ stdin:7:1: error: stray build notification email: no build package configuration 'bar'
+ EOE
+ }
+
+ : empty
+ :
+ {
+ $* <<EOI 2>>EOE != 0
+ : 1
+ name: foo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ build-error-email: build-error@example.com
+ bar-build-config: config.foo.bar = true; Bar.
+ bar-build-warning-email: ; Empty
+ EOI
+ stdin:8:26: error: empty build configuration warning email
+ EOE
+ }
+ }
+ }
+
+ : distribution
+ :
+ {
+ : incomplete
+ :
+ {
+ $* <<EOF >>EOF
+ : 1
+ name: libcrypto
+ version: 1.1.1+18
+ upstream-version: 1.1.1n
+ summary: C library providing general cryptography and X.509 support
+ license: OpenSSL
+ debian-name: libssl1.1 libssl-dev
+ debian-version: 1.1.1n
+ debian-to-downstream-version: /([^.])\.([^.])\.([^.])n/\1.\2.\3+18/
+ debian-to-downstream-version: /([^.])\.([^.])\.([^.])o/\1.\2.\3+19/
+ debian-to-downstream-version: /([^.])\.([^.])\.([^.])p/\1.\2.\3+20/
+ fedora-name: openssl-libs openssl-devel
+ fedora-version: $
+ EOF
+ }
+
+ : complete
+ :
+ {
+ $* -c <<EOI >>EOO
+ : 1
+ name: libcrypto
+ version: +2-1.1.1-a.1+2
+ upstream-version: 1.1.1n
+ summary: C library providing general cryptography and X.509 support
+ license: OpenSSL
+ fedora-name: openssl-libs openssl-devel
+ fedora-version: $
+ fedora-to-downstream-version: $
+ EOI
+ : 1
+ name: libcrypto
+ version: +2-1.1.1-a.1+2
+ upstream-version: 1.1.1n
+ summary: C library providing general cryptography and X.509 support
+ license: OpenSSL
+ fedora-name: openssl-libs openssl-devel
+ fedora-version: 1.1.1
+ fedora-to-downstream-version: $
+ EOO
+ }
+
+ : multiple-names
+ :
+ {
+ $* <<EOO >>EOO
+ : 1
+ name: libcrypto
+ version: 1.1.1+18
+ upstream-version: 1.1.1n
+ summary: C library providing general cryptography and X.509 support
+ license: OpenSSL
+ debian-name: libcurl4 libcurl4-doc libcurl4-openssl-dev
+ debian-name: libcurl3-gnutls libcurl4-gnutls-dev
+ EOO
+ }
+
+ : dash-in-name
+ :
+ {
+ $* <<EOI 2>>EOE != 0
+ : 1
+ name: libcrypto
+ version: 1.1.1+18
+ upstream-version: 1.1.1n
+ summary: C library providing general cryptography and X.509 support
+ license: OpenSSL
+ de-bian-name: libssl1.1 libssl-dev
+ EOI
+ stdin:7:1: error: distribution name 'de-bian' contains '-'
+ EOE
+ }
+
+ : empty-value
+ :
+ {
+ $* <<EOI 2>>EOE != 0
+ : 1
+ name: libcrypto
+ version: 1.1.1+18
+ upstream-version: 1.1.1n
+ summary: C library providing general cryptography and X.509 support
+ license: OpenSSL
+ debian-name:
+ EOI
+ stdin:7:13: error: empty package distribution value
+ EOE
+ }
}
: depends
@@ -3344,6 +4351,17 @@
tests: bar config.bar.test = foo
EOF
+ : enable
+ :
+ $* <<EOF >>EOF
+ : 1
+ name: foo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ tests: bar == 1.0.0 ? ($windows) config.bar.test = foo
+ EOF
+
: invalid-variable
:
$* <<EOI 2>>EOE != 0
@@ -3358,6 +4376,45 @@
EOE
}
+ : enable
+ :
+ {
+ : after-version-constraint
+ :
+ $* <<EOF >>EOF
+ : 1
+ name: foo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ tests: bar == 1.0.0 ? ($windows)
+ EOF
+
+ : no-version-constraint
+ :
+ $* <<EOF >>EOF
+ : 1
+ name: foo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ tests: bar ? ($windows)
+ EOF
+
+ : unterminated
+ :
+ $* <<EOI 2>>EOE != 0
+ : 1
+ name: foo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ tests: bar ? ($windows
+ EOI
+ stdin:6:8: error: unterminated evaluation context
+ EOE
+ }
+
: newline
:
$* <<EOI 2>>EOE != 0
@@ -3412,19 +4469,6 @@
EOI
stdin:6:8: error: only single package allowed
EOE
-
- : enable
- :
- $* <<EOI 2>>EOE != 0
- : 1
- name: foo
- version: 2.0.0
- summary: Modern C++ parser
- license: LGPLv2
- tests: bar ? (windows)
- EOI
- stdin:6:8: error: unexpected enable clause
- EOE
}
: buildfile
@@ -3800,6 +4844,8 @@
:
name: libfoo
version: 1.2.3+2
+ type: lib
+ language: c++
project: foo
priority: high; Due to critical bug fix.
summary: Modern XML parser
@@ -3808,8 +4854,11 @@
keywords: c++ xml parser serializer pull
description: libfoo is a very modern C++ XML parser.
description-type: text/plain
+ package-description: packaged for build2.
+ package-description-type: text/plain
changes: 1.2.3+2: applied upstream patch for critical bug bar
changes: 1.2.3+1: applied upstream patch for critical bug foo
+ changes-type: text/plain
url: http://www.example.org/projects/libfoo/; libfoo project page url
doc-url: http://www.example.org/projects/libfoo/man.xhtml; documentation page
src-url: http://scm.example.com/?p=odb/libodb.git\;a=tree; source tree
@@ -3838,10 +4887,10 @@
build-include: linux*
build-include: freebsd*
build-exclude: *; Only supports Linux and FreeBSD.
- network-build-config: config.libfoo.network=true; Enable networking API.
network-builds: default
network-build-include: linux*
network-build-exclude: *; Only supports Linux.
+ network-build-config: config.libfoo.network=true; Enable networking API.
bootstrap-build:\
project = libfoo
@@ -4488,3 +5537,21 @@
}
}
}
+
+: effective-type
+:
+{
+ test.options += -et
+
+ $* '' libfoo >'lib' : lib-prefix
+ $* '' foo >'exe' : no-lib-prefix
+ $* 'mixed' foo >'other' : other
+
+ : lib-binless
+ :
+ $* 'lib,binless,extras' libfoo >>EOO
+ lib
+ binless
+ extras
+ EOO
+}
diff --git a/tests/overrides/driver.cxx b/tests/overrides/driver.cxx
index 62ac7f8..c4a09ef 100644
--- a/tests/overrides/driver.cxx
+++ b/tests/overrides/driver.cxx
@@ -86,7 +86,7 @@ main (int argc, char* argv[])
{
package_manifest::validate_overrides (overrides, name);
}
- catch (const manifest_parsing& e)
+ catch (const manifest_parsing&)
{
assert (false); // Validation must never fail if override succeeds.
}
diff --git a/tests/overrides/testscript b/tests/overrides/testscript
index 95a5593..a903d05 100644
--- a/tests/overrides/testscript
+++ b/tests/overrides/testscript
@@ -15,6 +15,10 @@
build-email: foo@example.com
build-error-email: error@example.com
build-warning-email: warning@example.com
+ network-build-config: config.libfoo.network=true
+ network-build-email: network-foo@example.com
+ network-build-error-email: network-error@example.com
+ network-build-warning-email: network-warning@example.com
EOI
: 1
name: libfoo
@@ -22,6 +26,7 @@
summary: Modern C++ parser
license: LGPLv2
build-email: bar@example.com
+ network-build-config: config.libfoo.network=true
EOO
: builds
@@ -99,7 +104,12 @@
: build-configs
:
$* 'network-builds: all' 'network-build-include: windows*' 'network-build-exclude: *' \
+ 'network-build-warning-email: network-warning@example.com' 'sys-build-email:' \
+ 'cache-build-error-email: cache-error@example.com' \
'cache-build-include: freebsd*' 'cache-build-exclude: *' 'cache-builds: legacy' \
+ 'cache-build-config: config.libfoo.cache=true config.libfoo.buffer=1028' \
+ 'deprecated-api-build-config: config.libfoo.deprecated_api=true' 'deprecated-api-builds: windows' \
+ 'experimental-api-build-config: config.libfoo.experimental_api=true' \
'sys-build-include: linux*' 'sys-build-exclude: *' \
'fancy-builds: gcc' <<EOI >>EOO
: 1
@@ -107,62 +117,82 @@
version: 2.0.0
summary: Modern C++ parser
license: LGPLv2
+ build-email: foo@example.com
+ build-error-email: error@example.com
+ build-warning-email: warning@example.com
builds: all
build-include: linux*
build-include: macos*
build-include: freebsd*
build-exclude: *
- network-build-config: config.libfoo.network=true
network-builds: default
network-build-include: linux*
network-build-exclude: *
- cache-build-config: config.libfoo.cache=true
+ network-build-config: config.libfoo.network=true
+ network-build-error-email: network-error@example.com
cache-builds: default
cache-build-include: macos*
cache-build-exclude: *
- sys-build-config: ?sys:libcrypto
+ cache-build-config: config.libfoo.cache=true
+ cache-build-email: cache@example.com
sys-builds: default
sys-build-include: freebsd*
sys-build-exclude: *
- older-build-config: ?libbar/1.0.0
+ sys-build-config: ?sys:libcrypto
+ sys-build-email: sys@example.com
older-builds: default
older-build-include: windows*
older-build-exclude: *
- fancy-build-config: config.libfoo.fancy=true
+ older-build-config: ?libbar/1.0.0
fancy-builds: default
fancy-build-include: windows*
fancy-build-exclude: *
+ fancy-build-config: config.libfoo.fancy=true
EOI
: 1
name: libfoo
version: 2.0.0
summary: Modern C++ parser
license: LGPLv2
+ build-email: foo@example.com
+ build-warning-email: warning@example.com
+ build-error-email: error@example.com
builds: all
build-include: linux*
build-include: macos*
build-include: freebsd*
build-exclude: *
default-builds: none
- network-build-config: config.libfoo.network=true
+ default-build-email:
network-builds: all
network-build-include: windows*
network-build-exclude: *
- cache-build-config: config.libfoo.cache=true
+ network-build-config: config.libfoo.network=true
+ network-build-warning-email: network-warning@example.com
cache-builds: legacy
cache-build-include: freebsd*
cache-build-exclude: *
- sys-build-config: ?sys:libcrypto
+ cache-build-config: config.libfoo.cache=true config.libfoo.buffer=1028
+ cache-build-error-email: cache-error@example.com
sys-builds: default
sys-build-include: linux*
sys-build-exclude: *
- older-build-config: ?libbar/1.0.0
+ sys-build-config: ?sys:libcrypto
+ sys-build-email:
older-builds: none
- fancy-build-config: config.libfoo.fancy=true
+ older-build-config: ?libbar/1.0.0
+ older-build-email:
fancy-builds: gcc
+ fancy-build-config: config.libfoo.fancy=true
+ fancy-build-email:
+ deprecated-api-builds: windows
+ deprecated-api-build-config: config.libfoo.deprecated_api=true
+ deprecated-api-build-email:
+ experimental-api-builds: none
+ experimental-api-build-config: config.libfoo.experimental_api=true
+ experimental-api-build-email:
EOO
-
: build-config-default
:
$* 'default-builds: all' 'default-build-include: windows*' 'default-build-exclude: *' <<EOI >>EOO
@@ -171,8 +201,8 @@
version: 2.0.0
summary: Modern C++ parser
license: LGPLv2
- network-build-config: config.libfoo.network=true
network-builds: all
+ network-build-config: config.libfoo.network=true
EOI
: 1
name: libfoo
@@ -182,8 +212,29 @@
default-builds: all
default-build-include: windows*
default-build-exclude: *
- network-build-config: config.libfoo.network=true
network-builds: none
+ network-build-config: config.libfoo.network=true
+ EOO
+
+ : add-build-config
+ :
+ $* 'experimental-api-build-config: config.libfoo.experimental_api=true' <<EOI >>EOO
+ : 1
+ name: libfoo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ network-build-config: config.libfoo.network=true
+ network-builds: all
+ EOI
+ : 1
+ name: libfoo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ network-builds: all
+ network-build-config: config.libfoo.network=true
+ experimental-api-build-config: config.libfoo.experimental_api=true
EOO
: none
@@ -203,6 +254,153 @@
license: LGPLv2
build-email: foo@example.com
EOO
+
+ : build-auxiliary
+ :
+ {
+ : named
+ :
+ $* 'build-auxiliary-pgsql: *-postgresql**' \
+ 'foo-build-auxiliary-oracle: *-oracle**' <<EOI >>EOO
+ : 1
+ name: libfoo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ build-auxiliary-pgsql: *-postgresql_*
+ build-auxiliary-mysql: *-mysql_*
+ foo-build-auxiliary-mssql: *-mssql_*
+ foo-build-auxiliary-oracle: *-oracle_*
+ EOI
+ : 1
+ name: libfoo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ build-auxiliary-pgsql: *-postgresql**
+ build-auxiliary-mysql: *-mysql_*
+ foo-build-auxiliary-mssql: *-mssql_*
+ foo-build-auxiliary-oracle: *-oracle**
+ EOO
+
+ : unnamed
+ :
+ $* 'build-auxiliary: *-postgresql**' \
+ 'foo-build-auxiliary: *-oracle**' <<EOI >>EOO
+ : 1
+ name: libfoo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ build-auxiliary: *-postgresql_*
+ foo-build-auxiliary: *-oracle_*
+ EOI
+ : 1
+ name: libfoo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ build-auxiliary: *-postgresql**
+ foo-build-auxiliary: *-oracle**
+ EOO
+
+ : new-config
+ :
+ $* 'bar-build-config:' \
+ 'bar-build-auxiliary-mysql: *-mysql_8' \
+ 'bar-build-auxiliary-pgsql: *-postgresql_16' <<EOI >>EOO
+ : 1
+ name: libfoo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ build-auxiliary-pgsql: *-postgresql_*
+ build-auxiliary-mssql: *-mssql_*
+ foo-build-auxiliary-mysql: *-mysql_*
+ foo-build-auxiliary-oracle: *-oracle_*
+ EOI
+ : 1
+ name: libfoo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ build-auxiliary-pgsql: *-postgresql_*
+ build-auxiliary-mssql: *-mssql_*
+ foo-build-auxiliary-mysql: *-mysql_*
+ foo-build-auxiliary-oracle: *-oracle_*
+ bar-build-auxiliary-mysql: *-mysql_8
+ bar-build-auxiliary-pgsql: *-postgresql_16
+ EOO
+ }
+
+ : build-bot
+ :
+ {
+ : common
+ :
+ $* 'build-bot: key3' 'build-bot: key4' <<EOI >>EOO
+ : 1
+ name: libfoo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ build-bot: key1
+ foo-build-bot: key2
+ EOI
+ : 1
+ name: libfoo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ build-bot: key3
+ build-bot: key4
+ EOO
+
+ : config
+ :
+ $* 'foo-build-bot: key3' 'foo-build-bot: key4' <<EOI >>EOO
+ : 1
+ name: libfoo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ build-bot: key1
+ foo-build-bot: key2
+ EOI
+ : 1
+ name: libfoo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ build-bot: key1
+ foo-build-bot: key3
+ foo-build-bot: key4
+ EOO
+
+ : new-config
+ :
+ $* 'bar-build-config:' \
+ 'bar-build-bot: key1' \
+ 'bar-build-bot: key2' <<EOI >>EOO
+ : 1
+ name: libfoo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ build-bot: key1
+ foo-build-bot: key2
+ EOI
+ : 1
+ name: libfoo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ build-bot: key1
+ foo-build-bot: key2
+ bar-build-bot: key1
+ bar-build-bot: key2
+ EOO
+ }
}
: invalid
@@ -306,4 +504,171 @@
EOI
'build-exclude' override specified together with 'network-builds' override
EOE
+
+ : build-config-after-config-builds
+ :
+ $* 'deprecated-api-builds: windows' 'deprecated-api-build-config: config.libfoo.deprecated-api=true' <<EOI 2>>EOE != 0
+ : 1
+ name: libfoo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ EOI
+ cannot override 'deprecated-api-builds' value: no build package configuration 'deprecated-api'
+ EOE
+
+ : config-bot-after-built-bot
+ :
+ $* 'build-bot: key1' 'foo-build-bot: key2' <<EOI 2>>EOE != 0
+ : 1
+ name: libfoo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ foo-build-config:
+ EOI
+ 'foo-build-bot' override specified together with 'build-bot' override
+ EOE
+
+ : built-bot-after-config-bot
+ :
+ $* 'foo-build-bot: key1' 'build-bot: key2' <<EOI 2>>EOE != 0
+ : 1
+ name: libfoo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ foo-build-config:
+ EOI
+ 'build-bot' override specified together with 'foo-build-bot' override
+ EOE
+
+ : no-build-bot-config
+ :
+ $* 'foo-build-bot: key1' <<EOI 2>>EOE != 0
+ : 1
+ name: libfoo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ EOI
+ cannot override 'foo-build-bot' value: no build package configuration 'foo'
+ EOE
+
+ : config-email-after-email
+ :
+ $* 'build-email: foo@example.com' 'network-build-warning-email: warning@example.com' <<EOI 2>>EOE != 0
+ : 1
+ name: libfoo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ network-build-config: config.libfoo.network=true
+ EOI
+ 'network-build-warning-email' override specified together with 'build-email' override
+ EOE
+
+ : email-after-config-email
+ :
+ $* 'network-build-warning-email: warning@example.com' 'build-email: foo@example.com' <<EOI 2>>EOE != 0
+ : 1
+ name: libfoo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ network-build-config: config.libfoo.network=true
+ EOI
+ 'build-email' override specified together with 'network-build-warning-email' override
+ EOE
+
+ : build-auxiliary
+ :
+ {
+ : named-common
+ :
+ $* 'build-auxiliary-mysql: *-mysql_*' <<EOI 2>>EOE != 0
+ : 1
+ name: libfoo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ build-auxiliary-pgsql: *-postgresql_*
+ foo-build-auxiliary-mssql: *-mssql_*
+ foo-build-auxiliary-oracle: *-oracle_*
+ EOI
+ no match for 'build-auxiliary-mysql' value override
+ EOE
+
+ : named-config1
+ :
+ $* 'foo-build-auxiliary-mysql: *-mysql_*' <<EOI 2>>EOE != 0
+ : 1
+ name: libfoo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ build-auxiliary-pgsql: *-postgresql_*
+ foo-build-auxiliary-mssql: *-mssql_*
+ foo-build-auxiliary-oracle: *-oracle_*
+ EOI
+ no match for 'foo-build-auxiliary-mysql' value override
+ EOE
+
+ : named-config2
+ :
+ $* 'bar-build-auxiliary-oracle: *-oracle**' <<EOI 2>>EOE != 0
+ : 1
+ name: libfoo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ build-auxiliary-pgsql: *-postgresql_*
+ foo-build-auxiliary-mssql: *-mssql_*
+ foo-build-auxiliary-oracle: *-oracle_*
+ EOI
+ cannot override 'bar-build-auxiliary-oracle' value: no build package configuration 'bar'
+ EOE
+
+ : unnamed-common
+ :
+ $* 'build-auxiliary-mysql: *-mysql_*' <<EOI 2>>EOE != 0
+ : 1
+ name: libfoo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ build-auxiliary: *-postgresql_*
+ foo-build-auxiliary: *-oracle_*
+ EOI
+ no match for 'build-auxiliary-mysql' value override
+ EOE
+
+ : unnamed-config1
+ :
+ $* 'foo-build-auxiliary-mysql: *-mysql_*' <<EOI 2>>EOE != 0
+ : 1
+ name: libfoo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ build-auxiliary: *-postgresql_*
+ foo-build-auxiliary: *-oracle_*
+ EOI
+ no match for 'foo-build-auxiliary-mysql' value override
+ EOE
+
+ : unnamed-config2
+ :
+ $* 'bar-build-auxiliary: *-mysql_*' <<EOI 2>>EOE != 0
+ : 1
+ name: libfoo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ build-auxiliary: *-postgresql_*
+ foo-build-auxiliary: *-oracle_*
+ EOI
+ cannot override 'bar-build-auxiliary' value: no build package configuration 'bar'
+ EOE
+ }
}