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.cxx2238
-rw-r--r--libbpkg/manifest.hxx836
-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/testscript1809
-rw-r--r--tests/overrides/driver.cxx18
-rw-r--r--tests/overrides/testscript423
-rw-r--r--tests/repository-location/driver.cxx6
13 files changed, 5056 insertions, 747 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 6a5ce25..065f055 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>
@@ -1257,7 +1362,6 @@ namespace bpkg
rsbrace, // ]
equal, // ==
- not_equal, // !=
less, // <
greater, // >
less_equal, // <=
@@ -1281,6 +1385,11 @@ namespace bpkg
string (bool diag = true) const;
};
+ // If true, then comments are allowed and are treated as whitespace
+ // characters.
+ //
+ bool comments = false;
+
public:
// Note that name is stored by shallow reference.
//
@@ -1406,12 +1515,11 @@ namespace bpkg
case ']': return make_token (type::rsbrace);
case '=':
- case '!':
{
- if ((peek ()) == '=')
+ if (peek () == '=')
{
get ();
- return make_token (c == '=' ? type::equal : type::not_equal);
+ return make_token (type::equal);
}
break;
}
@@ -1453,7 +1561,7 @@ namespace bpkg
// Add subsequent characters until eos or separator is encountered.
//
- const char* s (" \n\t?(){}[]=!<>~^|");
+ const char* s (" \n\t?(){}[]=<>~^|");
for (c = peek (); !eos (c) && strchr (s, c) == nullptr; c = peek ())
{
r += c;
@@ -1551,6 +1659,60 @@ namespace bpkg
case ' ':
case '\t': break;
+ case '#':
+ {
+ if (!comments)
+ return;
+
+ get (c);
+
+ // See if this is a multi-line comment in the form:
+ //
+ /*
+ #\
+ ...
+ #\
+ */
+ auto ml = [&c, this] () -> bool
+ {
+ if ((c = peek ()) == '\\')
+ {
+ get (c);
+ if ((c = peek ()) == '\n' || eos (c))
+ return true;
+ }
+
+ return false;
+ };
+
+ if (ml ())
+ {
+ // Scan until we see the closing one.
+ //
+ for (;;)
+ {
+ if (c == '#' && ml ())
+ break;
+
+ if (eos (c = peek ()))
+ throw parsing (name_,
+ c.line, c.column,
+ "unterminated multi-line comment");
+
+ get (c);
+ }
+ }
+ else
+ {
+ // Read until newline or eos.
+ //
+ for (; !eos (c) && c != '\n'; c = peek ())
+ get (c);
+ }
+
+ continue;
+ }
+
case '\n':
{
// Skip empty lines.
@@ -1579,22 +1741,21 @@ namespace bpkg
case token_type::buildfile: return (diag
? "<buildfile fragment>"
: value);
- case token_type::question: return q + "?" + q;
- case token_type::lparen: return q + "(" + q;
- case token_type::rparen: return q + ")" + q;
- case token_type::lcbrace: return q + "{" + q;
- case token_type::rcbrace: return q + "}" + q;
- case token_type::lsbrace: return q + "[" + q;
- case token_type::rsbrace: return q + "]" + q;
+ case token_type::question: return q + '?' + q;
+ case token_type::lparen: return q + '(' + q;
+ case token_type::rparen: return q + ')' + q;
+ case token_type::lcbrace: return q + '{' + q;
+ case token_type::rcbrace: return q + '}' + q;
+ case token_type::lsbrace: return q + '[' + q;
+ case token_type::rsbrace: return q + ']' + q;
case token_type::equal: return q + "==" + q;
- case token_type::not_equal: return q + "!=" + q;
- case token_type::less: return q + "<" + q;
- case token_type::greater: return q + ">" + q;
+ case token_type::less: return q + '<' + q;
+ case token_type::greater: return q + '>' + q;
case token_type::less_equal: return q + "<=" + q;
case token_type::greater_equal: return q + ">=" + q;
- case token_type::tilde: return q + "~" + q;
- case token_type::caret: return q + "^" + q;
- case token_type::bit_or: return q + "|" + q;
+ case token_type::tilde: return q + '~' + q;
+ case token_type::caret: return q + '^' + q;
+ case token_type::bit_or: return q + '|' + q;
}
assert (false); // Can't be here.
@@ -1778,7 +1939,7 @@ namespace bpkg
dependency_alternative r;
string what (requirements_ ? "requirement" : "dependency");
- string config ("config." + dependent_->variable () + ".");
+ string config ("config." + dependent_->variable () + '.');
auto bad_token = [&t, this] (string&& what)
{
@@ -1855,7 +2016,6 @@ namespace bpkg
}
case type::equal:
- case type::not_equal:
case type::less:
case type::greater:
case type::less_equal:
@@ -1989,9 +2149,26 @@ namespace bpkg
if (requirements_ && first && tt == type::question)
{
r.emplace_back (dependency ());
- r.enable = lexer_->peek_char () == '(' ? parse_eval () : string ();
+
+ bool eval (lexer_->peek_char () == '(');
+ r.enable = eval ? parse_eval () : string ();
next (t, tt);
+
+ // @@ TMP Treat requirements similar to `? cli` as `cli ?` until
+ // toolchain 0.15.0 and libodb-mssql 2.5.0-b.22 are both released.
+ //
+ // NOTE: don't forget to drop the temporary test in
+ // tests/manifest/testscript when dropping this workaround.
+ //
+ if (!eval && tt == type::word)
+ try
+ {
+ r.back ().name = package_name (move (t.value));
+ next (t, tt);
+ }
+ catch (const invalid_argument&) {}
+
return r;
}
@@ -2099,6 +2276,10 @@ namespace bpkg
t.column,
"multi-line " + what + " form with inline reflect clause");
+ // Allow comments.
+ //
+ lexer_->comments = true;
+
next (t, tt);
expect_token (type::newline);
@@ -2142,7 +2323,9 @@ namespace bpkg
// curly braces (expected to be on the separate lines) and return
// the enclosed fragment.
//
- auto parse_block = [&t, &tt, &expect_token, &bad_token, this] ()
+ // Note that an empty buildfile fragment is allowed.
+ //
+ auto parse_block = [&t, &tt, &expect_token, this] ()
{
next (t, tt);
expect_token (type::newline);
@@ -2155,14 +2338,7 @@ namespace bpkg
next_block (t, tt);
- string r (move (t.value));
-
- // Fail if the buildfile fragment is empty.
- //
- if (r.find_first_not_of (" \t\n") == string::npos)
- bad_token ("buildfile fragment");
-
- return r;
+ return move (t.value);
};
const string& v (t.value);
@@ -2252,6 +2428,11 @@ namespace bpkg
}
expect_token (type::rcbrace);
+
+ // Disallow comments.
+ //
+ lexer_->comments = false;
+
next (t, tt);
}
}
@@ -2342,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::
@@ -2438,12 +2607,6 @@ namespace bpkg
return r;
}
- bool requirement_alternative::
- single_line () const
- {
- return !reflect || reflect->find ('\n') == string::npos;
- }
-
// requirement_alternatives
//
requirement_alternatives::
@@ -2543,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::
@@ -2567,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)
@@ -2591,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));
}
@@ -2623,13 +2774,13 @@ namespace bpkg
if (!(alnum (c) || c == '_'))
throw invalid_argument (
- "class name '" + s + "' starts with '" + c + "'");
+ "class name '" + s + "' starts with '" + c + '\'');
for (; i != s.size (); ++i)
{
if (!(alnum (c = s[i]) || c == '+' || c == '-' || c == '_' || c == '.'))
throw invalid_argument (
- "class name '" + s + "' contains '" + c + "'");
+ "class name '" + s + "' contains '" + c + '\'');
}
return s[0] == '_';
@@ -2918,102 +3069,42 @@ namespace bpkg
match_classes (cs, im, expr, r);
}
- // text_type
+ // build_auxiliary
//
- string
- to_string (text_type t)
+ optional<pair<string, string>> build_auxiliary::
+ parse_value_name (const string& n)
{
- 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.
+ // Check if the value name matches exactly.
//
- size_t p (t.find (';'));
- const string& tp (p != string::npos ? trim (string (t, 0, p)) : t);
-
- small_vector<pair<string, string>, 1> ps;
+ if (n == "build-auxiliary")
+ return make_pair (string (), string ());
- 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
@@ -3038,7 +3129,7 @@ namespace bpkg
if (t == "tests") return test_dependency_type::tests;
else if (t == "examples") return test_dependency_type::examples;
else if (t == "benchmarks") return test_dependency_type::benchmarks;
- else throw invalid_argument ("invalid test dependency type '" + t + "'");
+ else throw invalid_argument ("invalid test dependency type '" + t + '\'');
}
@@ -3050,14 +3141,123 @@ namespace bpkg
{
using std::string;
+ // We will use the dependency alternatives parser to parse the
+ // `<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.
+ //
+ if (v.find ('\n') != string::npos)
+ throw invalid_argument ("unexpected <newline>");
+
buildtime = (v[0] == '*');
+
size_t p (v.find_first_not_of (spaces, buildtime ? 1 : 0));
if (p == string::npos)
throw invalid_argument ("no package name specified");
- static_cast<dependency&> (*this) =
- dependency (p == 0 ? move (v) : string (v, p));
+ string::const_iterator b (v.begin () + p);
+ string::const_iterator e (v.end ());
+
+ // Extract the dependency package name in advance, to pass it to the
+ // parser which will use it to verify the reflection variable name.
+ //
+ // Note that multiple packages can only be specified in {} to be accepted
+ // by the parser. In our case such '{' would be interpreted as a part of
+ // the package name and so would fail complaining about an invalid
+ // character. Let's handle this case manually to avoid the potentially
+ // confusing error description.
+ //
+ assert (b != e); // We would fail earlier otherwise.
+
+ if (*b == '{')
+ throw invalid_argument ("only single package allowed");
+
+ package_name dn;
+
+ try
+ {
+ p = v.find_first_of (" \t=<>[(~^", p); // End of the package name.
+ dn = package_name (string (b, p == string::npos ? e : v.begin () + p));
+ }
+ catch (const invalid_argument& e)
+ {
+ throw invalid_argument (string ("invalid package name: ") + e.what ());
+ }
+
+ // Parse the value into the temporary dependency alternatives object.
+ //
+ dependency_alternatives das;
+
+ try
+ {
+ dependency_alternatives_parser p;
+ istringstream is (b == v.begin () ? v : string (b, e));
+ p.parse (dn, is, "" /* name */, 1, 1, das);
+ }
+ catch (const manifest_parsing& e)
+ {
+ throw invalid_argument (e.description);
+ }
+
+ // Verify that there are no multiple dependency alternatives.
+ //
+ assert (!das.empty ()); // Enforced by the parser.
+
+ if (das.size () != 1)
+ throw invalid_argument ("unexpected '|'");
+
+ dependency_alternative& da (das[0]);
+
+ // Verify that there are no multiple dependencies in the alternative.
+ //
+ // The parser can never end up with no dependencies in an alternative and
+ // we already verified that there can't be multiple of them (see above).
+ //
+ assert (da.size () == 1);
+
+ // Verify that there are no unexpected clauses.
+ //
+ // 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. So there is nothing to verify here.
+
+ // 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);
+ }
+
+ string test_dependency::
+ string () const
+ {
+ std::string r (buildtime
+ ? "* " + dependency::string ()
+ : dependency::string ());
+
+ if (enable)
+ {
+ r += " ? (";
+ r += *enable;
+ r += ')';
+ }
+
+ if (reflect)
+ {
+ r += ' ';
+ r += *reflect;
+ }
+
+ return r;
}
// pkg_package_manifest
@@ -3114,7 +3314,7 @@ namespace bpkg
{
throw !source_name.empty ()
? parsing (source_name, nv.value_line, nv.value_column, d)
- : parsing (d + " in '" + v + "'");
+ : parsing (d + " in '" + v + '\'');
};
size_t p (v.find ('/'));
@@ -3156,6 +3356,33 @@ 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));
+ }
+
const version stub_version (0, "0", nullopt, nullopt, 0);
// Parse until next() returns end-of-manifest value.
@@ -3166,7 +3393,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)
{
@@ -3178,16 +3405,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,
@@ -3251,11 +3499,190 @@ 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;
};
+ // Based on the buildfile path specified via the `*-build[2]` value name
+ // or the `build-file` value set the manifest's alt_naming flag if absent
+ // and verify that it doesn't change otherwise. If it does, then return
+ // the error description and nullopt otherwise.
+ //
+ auto alt_naming = [&m] (const string& p) -> optional<string>
+ {
+ assert (!p.empty ());
+
+ bool an (p.back () == '2');
+
+ if (!m.alt_naming)
+ m.alt_naming = an;
+ else if (*m.alt_naming != an)
+ return string (*m.alt_naming ? "alternative" : "standard") +
+ " buildfile naming scheme is already used";
+
+ return nullopt;
+ };
+
+ // Try to parse and verify the buildfile path specified via the
+ // `*-build[2]` value name or the `build-file` value and set the
+ // manifest's alt_naming flag. On success return the normalized path with
+ // the suffix stripped and nullopt and the error description
+ // otherwise. Expects that the prefix is not empty.
+ //
+ // Specifically, verify that the path doesn't contain backslashes, is
+ // relative, doesn't refer outside the packages's build subdirectory, and
+ // was not specified yet. Also verify that the file name is not empty.
+ //
+ auto parse_buildfile_path =
+ [&m, &alt_naming] (string&& p, string& err) -> optional<path>
+ {
+ if (optional<string> e = alt_naming (p))
+ {
+ err = move (*e);
+ return nullopt;
+ }
+
+ // Verify that the path doesn't contain backslashes which would be
+ // interpreted differently on Windows and POSIX.
+ //
+ if (p.find ('\\') != string::npos)
+ {
+ err = "backslash in package buildfile path";
+ return nullopt;
+ }
+
+ // Strip the '(-|.)build' suffix.
+ //
+ size_t n (*m.alt_naming ? 7 : 6);
+ assert (p.size () > n);
+
+ p.resize (p.size () - n);
+
+ try
+ {
+ path f (move (p));
+
+ // Fail if the value name is something like `config/-build`.
+ //
+ if (f.to_directory ())
+ {
+ err = "empty package buildfile name";
+ return nullopt;
+ }
+
+ if (f.absolute ())
+ {
+ err = "absolute package buildfile path";
+ return nullopt;
+ }
+
+ // Verify that the path refers inside the package's build/
+ // subdirectory.
+ //
+ f.normalize (); // Note: can't throw since the path is relative.
+
+ if (dir_path::traits_type::parent (*f.begin ()))
+ {
+ err = "package buildfile path refers outside build/ subdirectory";
+ return nullopt;
+ }
+
+ // Check for duplicates.
+ //
+ const vector<buildfile>& bs (m.buildfiles);
+ const vector<path>& bps (m.buildfile_paths);
+
+ if (find_if (bs.begin (), bs.end (),
+ [&f] (const auto& v) {return v.path == f;})
+ != bs.end () ||
+ find (bps.begin (), bps.end (), f) != bps.end ())
+ {
+ err = "package buildfile redefinition";
+ return nullopt;
+ }
+
+ return f;
+ }
+ catch (const invalid_path&)
+ {
+ err = "invalid package buildfile path";
+ return nullopt;
+ }
+ };
+
+ // Return the package build configuration with the specified name, if
+ // 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 (),
+ [&nm] (const build_package_config& c)
+ {return c.name == nm;}));
+
+ 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).
+ //
+ cs.emplace_back (move (nm));
+ return cs.back ();
+ };
+
// Cache the upstream version manifest value and validate whether it's
// allowed later, after the version value is parsed.
//
@@ -3270,11 +3697,27 @@ 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");
for (nv = next (); !nv.empty (); nv = next ())
{
@@ -3343,6 +3786,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)
@@ -3399,28 +3891,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");
}
@@ -3429,32 +3921,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")
{
@@ -3465,7 +3994,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")
{
@@ -3490,19 +4019,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")
{
@@ -3576,6 +4105,81 @@ namespace bpkg
m.build_constraints.push_back (
parse_build_constraint (nv, true /* exclusion */, name));
}
+ 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.size () > 13 &&
+ n.compare (n.size () - 13, 13, "-build-config") == 0)
+ {
+ auto vc (parser::split_comment (v));
+
+ n.resize (n.size () - 13);
+
+ build_package_config& bc (build_conf (move (n)));
+
+ if (!bc.arguments.empty () || !bc.comment.empty ())
+ bad_name ("build configuration redefinition");
+
+ bc.arguments = move (vc.first);
+ bc.comment = move (vc.second);
+ }
+ else if (n.size () > 7 && n.compare (n.size () - 7, 7, "-builds") == 0)
+ {
+ n.resize (n.size () - 7);
+
+ build_package_config& bc (build_conf (move (n)));
+
+ 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)
+ {
+ n.resize (n.size () - 14);
+
+ build_package_config& bc (build_conf (move (n)));
+
+ 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)
+ {
+ n.resize (n.size () - 14);
+
+ build_package_config& bc (build_conf (move (n)));
+
+ bc.constraints.push_back (
+ parse_build_constraint (nv, true /* exclusion */, name));
+ }
+ 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" ||
@@ -3590,6 +4194,98 @@ namespace bpkg
tests.push_back (move (nv));
}
+ else if (n == "bootstrap-build" || n == "bootstrap-build2")
+ {
+ if (optional<string> e = alt_naming (n))
+ bad_name (*e);
+
+ if (m.bootstrap_build)
+ bad_name ("package " + n + " redefinition");
+
+ m.bootstrap_build = move (v);
+ }
+ else if (n == "root-build" || n == "root-build2")
+ {
+ if (optional<string> e = alt_naming (n))
+ bad_name (*e);
+
+ if (m.root_build)
+ bad_name ("package " + n + " redefinition");
+
+ m.root_build = move (v);
+ }
+ else if ((n.size () > 6 && n.compare (n.size () - 6, 6, "-build") == 0) ||
+ (n.size () > 7 && n.compare (n.size () - 7, 7, "-build2") == 0))
+ {
+ string err;
+ if (optional<path> p = parse_buildfile_path (move (n), err))
+ m.buildfiles.push_back (buildfile (move (*p), move (v)));
+ else
+ bad_name (err);
+ }
+ else if (n == "build-file")
+ {
+ if (flag (package_manifest_flags::forbid_file))
+ bad_name ("package build-file not allowed");
+
+ // Verify that the buildfile extension is either build or build2.
+ //
+ if ((v.size () > 6 && v.compare (v.size () - 6, 6, ".build") == 0) ||
+ (v.size () > 7 && v.compare (v.size () - 7, 7, ".build2") == 0))
+ {
+ string err;
+ if (optional<path> p = parse_buildfile_path (move (v), err))
+ {
+ // Verify that the resulting path differs from bootstrap and root.
+ //
+ const string& s (p->string ());
+ if (s == "bootstrap" || s == "root")
+ bad_value (s + " not allowed");
+
+ m.buildfile_paths.push_back (move (*p));
+ }
+ else
+ bad_value (err);
+ }
+ else
+ 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))
@@ -3670,23 +4366,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));
@@ -3697,51 +4394,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
@@ -3749,12 +4596,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)
@@ -3827,16 +4674,82 @@ namespace bpkg
}
}
- if (m.description &&
- !m.description_type &&
- flag (package_manifest_flags::require_description_type))
- bad_name ("no package description type specified");
+ // 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 (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))
+ {
+ // @@ TMP To support older repositories allow absent bootstrap build
+ // and alt_naming until toolchain 0.15.0 is released.
+ //
+ // Note that for such repositories the packages may not have any
+ // need for the bootstrap buildfile (may not have any dependency
+ // clauses, etc). Thus, we can safely set the bootstrap build and
+ // alt_naming values to an empty string and false, respectively,
+ // if they are absent, so that the caller can always be sure that
+ // these values are always present for package manifest lists.
+ //
+ // Note: don't forget to uncomment no-bootstrap test in
+ // tests/manifest/testscript when removing this workaround.
+ //
+ // bad_name ("no package bootstrap build specified");
+ m.bootstrap_build = "project = " + m.name.string () + '\n';
+ m.alt_naming = false;
+ }
}
static void
@@ -3845,7 +4758,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)
{
@@ -3865,7 +4778,7 @@ namespace bpkg
[&p] () {return p.next ();},
tf,
iu,
- cd,
+ cv,
fl,
m);
}
@@ -3877,12 +4790,13 @@ namespace bpkg
p,
move (nv),
iu,
- false /* complete_depends */,
+ false /* complete_values */,
package_manifest_flags::forbid_file |
- package_manifest_flags::require_description_type |
- package_manifest_flags::require_location |
package_manifest_flags::forbid_fragment |
- package_manifest_flags::forbid_incomplete_dependencies);
+ package_manifest_flags::forbid_incomplete_values |
+ package_manifest_flags::require_location |
+ package_manifest_flags::require_text_type |
+ package_manifest_flags::require_bootstrap_build);
}
// package_manifest
@@ -3891,10 +4805,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.
//
@@ -3905,20 +4819,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 ());
@@ -3933,7 +4838,7 @@ namespace bpkg
},
tf,
iu,
- cd,
+ cv,
fl,
*this);
}
@@ -3942,13 +4847,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)
{
}
@@ -3957,190 +4862,635 @@ 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;
}
- void package_manifest::
- override (const vector<manifest_name_value>& nvs, const string& name)
+ // If validate_only is true, then the package manifest is assumed to be
+ // default constructed and is used as a storage for convenience of the
+ // validation implementation.
+ //
+ static void
+ override (const vector<manifest_name_value>& nvs,
+ const string& name,
+ package_manifest& m,
+ bool validate_only)
{
- // Reset the build constraints value sub-group on the first call.
+ // The first {builds, build-{include,exclude}} override value.
//
- bool rbc (true);
- auto reset_build_constraints = [&rbc, this] ()
- {
- if (rbc)
- {
- build_constraints.clear ();
- rbc = false;
- }
- };
+ const manifest_name_value* cbc (nullptr);
- // Reset the builds value group on the first call.
+ // The first builds override value.
//
- bool rb (true);
- auto reset_builds = [&rb, &reset_build_constraints, this] ()
- {
- if (rb)
- {
- builds.clear ();
- reset_build_constraints ();
- rb = false;
- }
- };
+ const manifest_name_value* cb (nullptr);
+
+ // The first {*-builds, *-build-{include,exclude}} override value.
+ //
+ const manifest_name_value* pbc (nullptr);
- // Reset the build emails value group on the first call.
+ // The first {build-*email} override value.
//
- bool rbe (true);
- auto reset_build_emails = [&rbe, this] ()
+ const manifest_name_value* cbe (nullptr);
+
+ // The first {*-build-*email} override value.
+ //
+ const manifest_name_value* pbe (nullptr);
+
+ // 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 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)
{
- if (rbe)
- {
- build_email = nullopt;
- build_warning_email = nullopt;
- build_error_email = nullopt;
- rbe = false;
- }
+ return &c >= m.build_configs.data () + confs_num;
};
+ // Apply overrides.
+ //
for (const manifest_name_value& nv: nvs)
{
+ auto bad_name = [&name, &nv] (const string& d)
+ {
+ throw !name.empty ()
+ ? parsing (name, nv.name_line, nv.name_column, d)
+ : parsing (d);
+ };
+
+ // Reset the build-{include,exclude} value sub-group on the first call
+ // but throw if any of the {*-builds, *-build-{include,exclude}}
+ // override values are already encountered.
+ //
+ auto reset_build_constraints = [&cbc, &pbc, &nv, &bad_name, &m] ()
+ {
+ if (cbc == nullptr)
+ {
+ if (pbc != nullptr)
+ bad_name ('\'' + nv.name + "' override specified together with '" +
+ pbc->name + "' override");
+
+ m.build_constraints.clear ();
+ cbc = &nv;
+ }
+ };
+
+ // Reset the {builds, build-{include,exclude}} value group on the first
+ // call.
+ //
+ auto reset_builds = [&cb, &nv, &reset_build_constraints, &m] ()
+ {
+ if (cb == nullptr)
+ {
+ reset_build_constraints ();
+
+ m.builds.clear ();
+ cb = &nv;
+ }
+ };
+
+ // 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
+ // *-build-config, *-builds, *-build-{include,exclude}, and
+ // *-build-*email values.
+ //
+ 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.
+ //
+ 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);
+
+ // If this is the first build config override value, then save its
+ // address. But first verify that no common build constraints group
+ // value overrides are applied yet and throw if that's not the case.
+ //
+ if (pbc == nullptr)
+ {
+ if (cbc != nullptr)
+ bad_name ('\'' + nm + "' override specified together with '" +
+ cbc->name + "' override");
+
+ pbc = &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 ());
+ bool bv (nm.compare (n, nm.size () - n, "-builds") == 0);
+
+ // If this is the first encountered
+ // {*-builds, *-build-{include,exclude}} override for this build
+ // config, then clear this config' constraints member and add an entry
+ // to the overridden configs list.
+ //
+ auto i (find_if (obcs.begin (), obcs.end (),
+ [ci] (const auto& c) {return c.first == ci;}));
+
+ bool first (i == obcs.end ());
+
+ if (first)
+ {
+ r.constraints.clear ();
+
+ obcs.push_back (make_pair (ci, bv));
+ }
+
+ // If this is the first encountered *-builds override, then also clear
+ // this config' builds member.
+ //
+ if (bv && (first || !i->second))
+ {
+ r.builds.clear ();
+
+ if (!first)
+ i->second = true;
+ }
+
+ 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 = [&cbe, &pbe, &nv, &bad_name, &m] ()
+ {
+ 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;
+ 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.
+ //
+ 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");
+ }
+ }
+ };
+
const string& n (nv.name);
if (n == "builds")
{
reset_builds ();
- builds.push_back (parse_build_class_expr (nv, builds.empty (), name));
+
+ m.builds.push_back (
+ parse_build_class_expr (nv, m.builds.empty (), name));
}
else if (n == "build-include")
{
reset_build_constraints ();
- build_constraints.push_back (
+ m.build_constraints.push_back (
parse_build_constraint (nv, false /* exclusion */, name));
}
else if (n == "build-exclude")
{
reset_build_constraints ();
- build_constraints.push_back (
+ 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))
+ {
+ 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_constr (n.size () - 7));
+
+ 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)
+ {
+ build_package_config& bc (build_conf_constr (n.size () - 14));
+
+ 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)
+ {
+ build_package_config& bc (build_conf_constr (n.size () - 14));
+
+ bc.constraints.push_back (
parse_build_constraint (nv, true /* exclusion */, name));
}
else if (n == "build-email")
{
reset_build_emails ();
- build_email = parse_email (nv, "build", name, true /* empty */);
+ m.build_email = parse_email (nv, "build", name, true /* empty */);
}
else if (n == "build-warning-email")
{
reset_build_emails ();
- build_warning_email = parse_email (nv, "build warning", name);
+ m.build_warning_email = parse_email (nv, "build warning", name);
}
else if (n == "build-error-email")
{
reset_build_emails ();
- build_error_email = parse_email (nv, "build error", name);
+ 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");
+ }
+
+ // Common build constraints and build config overrides are mutually
+ // exclusive.
+ //
+ assert (cbc == nullptr || pbc == nullptr);
+
+ // Now, if not in the validate-only mode, as all the potential build
+ // constraint/email overrides are applied, perform the final adjustments
+ // to the build config constraints/emails.
+ //
+ if (!validate_only)
+ {
+ if (cbc != nullptr) // Common build constraints are overridden?
+ {
+ for (build_package_config& c: m.build_configs)
+ {
+ c.builds.clear ();
+ c.constraints.clear ();
+ }
+ }
+ else if (pbc != nullptr) // Build config constraints are overridden?
{
- string d ("cannot override '" + n + "' value");
+ for (size_t i (0); i != m.build_configs.size (); ++i)
+ {
+ if (find_if (obcs.begin (), obcs.end (),
+ [i] (const auto& pc) {return pc.first == i;}) ==
+ obcs.end ())
+ {
+ build_package_config& c (m.build_configs[i]);
- throw !name.empty ()
- ? parsing (name, nv.name_line, nv.name_column, d)
- : parsing (d);
+ c.builds.clear ();
+ c.constraints.clear ();
+ c.builds.emplace_back ("none", "" /* comment */);
+ }
+ }
+ }
+
+ 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;
+ }
+ }
}
}
}
void package_manifest::
+ override (const vector<manifest_name_value>& nvs, const string& name)
+ {
+ bpkg::override (nvs, name, *this, false /* validate_only */);
+ }
+
+ void package_manifest::
validate_overrides (const vector<manifest_name_value>& nvs,
const string& name)
{
package_manifest p;
- p.override (nvs, name);
+ 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 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)
{
- auto load = [&loader] (const string& n, const path& p)
+ // 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 = [iu, &loader] (typed_text_file& text,
+ const string& file_value_name)
{
- string r (loader (n, p));
+ // Make the type explicit.
+ //
+ optional<text_type> t;
- if (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 = loader (file_value_name, text.path))
+ {
+ if (fc->empty ())
+ throw parsing ("package manifest value " + file_value_name +
+ " references empty file");
- description = text_file (load (description_file, description->path));
+ 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 (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.
+ //
+ if (!buildfile_paths.empty ())
{
- if (c.file)
- c = text_file (load (changes_file, c.path));
+ // Must already be set if the build-file value is parsed.
+ //
+ assert (alt_naming);
+
+ dir_path d (*alt_naming ? "build2" : "build");
+
+ for (auto i (buildfile_paths.begin ()); i != buildfile_paths.end (); )
+ {
+ path& p (*i);
+ path f (d / p);
+ f += *alt_naming ? ".build2" : ".build";
+
+ if (optional<string> fc = loader (build_file, f))
+ {
+ buildfiles.emplace_back (move (p), move (*fc));
+ i = buildfile_paths.erase (i); // Moved to buildfiles.
+ }
+ else
+ ++i;
+ }
}
}
@@ -4173,6 +5523,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 ());
@@ -4200,26 +5556,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)
@@ -4297,9 +5673,86 @@ namespace bpkg
s.next (c.exclusion ? "build-exclude" : "build-include",
serializer::merge_comment (!c.target
? c.config
- : c.config + "/" + *c.target,
+ : 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 build_package_config& bc: m.build_configs)
+ {
+ if (!bc.builds.empty ())
+ {
+ string n (bc.name + "-builds");
+ for (const build_class_expr& e: bc.builds)
+ s.next (n, serializer::merge_comment (e.string (), e.comment));
+ }
+
+ if (!bc.constraints.empty ())
+ {
+ string in (bc.name + "-build-include");
+ string en (bc.name + "-build-exclude");
+
+ for (const build_constraint& c: bc.constraints)
+ s.next (c.exclusion ? en : in,
+ serializer::merge_comment (!c.target
+ ? c.config
+ : 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.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);
+
+ if (m.bootstrap_build)
+ s.next (an ? "bootstrap-build2" : "bootstrap-build",
+ *m.bootstrap_build);
+
+ if (m.root_build)
+ s.next (an ? "root-build2" : "root-build", *m.root_build);
+
+ for (const auto& bf: m.buildfiles)
+ s.next (bf.path.posix_string () + (an ? "-build2" : "-build"),
+ bf.content);
+
+ 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 ());
@@ -4548,21 +6001,32 @@ namespace bpkg
{
throw serialization (
s.name (),
- d + " for " + p.name.string () + "-" + p.version.string ());
+ 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");
if (!p.location)
bad_value ("no valid location");
@@ -4916,7 +6380,7 @@ namespace bpkg
if (optional<repository_type> r = parse_repository_type (t))
return *r;
- throw invalid_argument ("invalid repository type '" + t + "'");
+ throw invalid_argument ("invalid repository type '" + t + '\'');
}
repository_type
diff --git a/libbpkg/manifest.hxx b/libbpkg/manifest.hxx
index 499d64d..4b0ccc6 100644
--- a/libbpkg/manifest.hxx
+++ b/libbpkg/manifest.hxx
@@ -10,8 +10,7 @@
#include <cassert>
#include <cstdint> // uint*_t
#include <ostream>
-#include <utility> // move()
-#include <stdexcept> // logic_error
+#include <utility> // move(), pair
#include <functional>
#include <libbutl/url.hxx>
@@ -102,7 +101,7 @@ namespace bpkg
version (version&&) = default;
version (const version&) = default;
- version& operator= (version&&);
+ version& operator= (version&&) noexcept;
version& operator= (const version&);
// If the revision is ignored, then the iteration (that semantically
@@ -111,23 +110,12 @@ namespace bpkg
std::string
string (bool ignore_revision = false, bool ignore_iteration = false) const;
- bool
- operator< (const version& v) const noexcept {return compare (v) < 0;}
-
- bool
- operator> (const version& v) const noexcept {return compare (v) > 0;}
-
- bool
- operator== (const version& v) const noexcept {return compare (v) == 0;}
-
- bool
- operator<= (const version& v) const noexcept {return compare (v) <= 0;}
-
- bool
- operator>= (const version& v) const noexcept {return compare (v) >= 0;}
-
- bool
- operator!= (const version& v) const noexcept {return compare (v) != 0;}
+ bool operator< (const version& v) const noexcept;
+ bool operator> (const version& v) const noexcept;
+ bool operator== (const version& v) const noexcept;
+ bool operator<= (const version& v) const noexcept;
+ bool operator>= (const version& v) const noexcept;
+ bool operator!= (const version& v) const noexcept;
// If the revision is ignored, then the iteration is also ignored,
// regardless of the argument (see above for details).
@@ -135,28 +123,7 @@ namespace bpkg
int
compare (const version& v,
bool ignore_revision = false,
- bool ignore_iteration = false) const noexcept
- {
- if (epoch != v.epoch)
- return epoch < v.epoch ? -1 : 1;
-
- if (int c = canonical_upstream.compare (v.canonical_upstream))
- return c;
-
- if (int c = canonical_release.compare (v.canonical_release))
- return c;
-
- if (!ignore_revision)
- {
- if (revision != v.revision)
- return revision < v.revision ? -1 : 1;
-
- if (!ignore_iteration && iteration != v.iteration)
- return iteration < v.iteration ? -1 : 1;
- }
-
- return 0;
- }
+ bool ignore_iteration = false) const noexcept;
bool
empty () const noexcept
@@ -207,33 +174,10 @@ namespace bpkg
return os << (v.empty () ? "<empty-version>" : v.string ());
}
- inline version::flags
- operator&= (version::flags& x, version::flags y)
- {
- return x = static_cast<version::flags> (
- static_cast<std::uint16_t> (x) &
- static_cast<std::uint16_t> (y));
- }
-
- inline version::flags
- operator|= (version::flags& x, version::flags y)
- {
- return x = static_cast<version::flags> (
- static_cast<std::uint16_t> (x) |
- static_cast<std::uint16_t> (y));
- }
-
- inline version::flags
- operator& (version::flags x, version::flags y)
- {
- return x &= y;
- }
-
- inline version::flags
- operator| (version::flags x, version::flags y)
- {
- return x |= y;
- }
+ version::flags operator& (version::flags, version::flags);
+ version::flags operator| (version::flags, version::flags);
+ version::flags operator&= (version::flags&, version::flags);
+ version::flags operator|= (version::flags&, version::flags);
// priority
//
@@ -251,11 +195,17 @@ namespace bpkg
operator value_type () const {return value;}
};
- // description
- // description-file
- // change
- // change-file
+ // language
//
+ struct language
+ {
+ std::string name;
+ bool impl; // True if implementation-only.
+
+ language (): impl (false) {}
+ language (std::string n, bool i): name (std::move (n)), impl (i) {}
+ };
+
class LIBBPKG_EXPORT text_file
{
public:
@@ -281,14 +231,80 @@ namespace bpkg
text_file (path_type p, std::string c)
: file (true), path (std::move (p)), comment (std::move (c)) {}
- text_file (text_file&&);
+ text_file (text_file&&) noexcept;
text_file (const text_file&);
- text_file& operator= (text_file&&);
+ text_file& operator= (text_file&&) noexcept;
text_file& operator= (const text_file&);
~text_file ();
};
+ enum class text_type
+ {
+ plain,
+ common_mark,
+ github_mark
+ };
+
+ LIBBPKG_EXPORT std::string
+ to_string (text_type);
+
+ // Throw std::invalid_argument if the argument is not a well-formed text
+ // type. Otherwise, return nullopt for an unknown text variant.
+ //
+ LIBBPKG_EXPORT butl::optional<text_type>
+ to_text_type (const std::string&);
+
+ inline std::ostream&
+ operator<< (std::ostream& os, text_type t)
+ {
+ return os << to_string (t);
+ }
+
+ // description
+ // description-file
+ // description-type
+ // package-description
+ // package-description-file
+ // package-description-type
+ // change
+ // change-file
+ // change-type
+ //
+ class LIBBPKG_EXPORT typed_text_file: public text_file
+ {
+ public:
+ butl::optional<std::string> type;
+
+ // File text constructor.
+ //
+ explicit
+ typed_text_file (std::string s = "",
+ butl::optional<std::string> t = butl::nullopt)
+ : text_file (std::move (s)), type (std::move (t)) {}
+
+ // File reference constructor.
+ //
+ typed_text_file (path_type p,
+ std::string c,
+ butl::optional<std::string> t = butl::nullopt)
+ : text_file (std::move (p), std::move (c)), type (std::move (t)) {}
+
+ // Return the type value if present, text_type::github_mark if it refers
+ // to a file with the .md or .markdown extension and text_type::plain if
+ // it refers to a file with the .txt extension or no extension or the text
+ // does not come from a file. Depending on the ignore_unknown value either
+ // throw std::invalid_argument or return nullopt if the type value or the
+ // file extension is unknown.
+ //
+ // Note: also throws std::invalid_argument if the type is not well-formed.
+ // This, however, may not happen for an object created by the package
+ // manifest parser since it has already verified that.
+ //
+ butl::optional<text_type>
+ effective_type (bool ignore_unknown = false) const;
+ };
+
// license
//
class licenses: public butl::small_vector<std::string, 1>
@@ -415,17 +431,10 @@ namespace bpkg
}
inline bool
- operator== (const version_constraint& x, const version_constraint& y)
- {
- return x.min_version == y.min_version && x.max_version == y.max_version &&
- x.min_open == y.min_open && x.max_open == y.max_open;
- }
+ operator== (const version_constraint&, const version_constraint&);
inline bool
- operator!= (const version_constraint& x, const version_constraint& y)
- {
- return !(x == y);
- }
+ operator!= (const version_constraint&, const version_constraint&);
struct LIBBPKG_EXPORT dependency
{
@@ -447,11 +456,8 @@ namespace bpkg
string () const;
};
- inline std::ostream&
- operator<< (std::ostream& os, const dependency& d)
- {
- return os << d.string ();
- }
+ std::ostream&
+ operator<< (std::ostream&, const dependency&);
// depends
//
@@ -516,6 +522,12 @@ namespace bpkg
// <reflect-config> - buildfile fragment containing dependent package
// configuration variables assignments
//
+ // In the multi-line form the block may contain comments besides the
+ // clauses. The '#' character starts a single-line comment which spans
+ // until the end of the line. Unless it is followed with '\' followed by
+ // the newline in which case this is a multi-line comment which spans
+ // until the closing '#\' is encountered.
+ //
// The dependency alternative is only considered by bpkg if the enable
// condition evaluates to true. If the enable clause is not specified, then
// it is always considered.
@@ -561,6 +573,22 @@ namespace bpkg
accept (std::move (a)),
require (std::move (q)) {}
+ // Can be used to copy a dependency alternative object, while omitting
+ // some clauses which are no longer needed.
+ //
+ dependency_alternative (butl::optional<std::string> e,
+ butl::optional<std::string> r,
+ butl::optional<std::string> p,
+ butl::optional<std::string> a,
+ butl::optional<std::string> q,
+ butl::small_vector<dependency, 1> ds)
+ : small_vector<dependency, 1> (move (ds)),
+ enable (std::move (e)),
+ reflect (std::move (r)),
+ prefer (std::move (p)),
+ accept (std::move (a)),
+ require (std::move (q)) {}
+
// Return the single-line representation if possible (the prefer and
// require clauses are absent and the reflect clause either absent or
// contains no newlines).
@@ -571,10 +599,21 @@ namespace bpkg
// Return true if the string() function would return the single-line
// representation.
//
- LIBBPKG_EXPORT bool
- single_line () const;
+ bool
+ single_line () const
+ {
+ return !prefer &&
+ !require &&
+ (!reflect || reflect->find ('\n') == std::string::npos);
+ }
};
+ inline std::ostream&
+ operator<< (std::ostream& os, const dependency_alternative& da)
+ {
+ return os << da.string ();
+ }
+
class dependency_alternatives:
public butl::small_vector<dependency_alternative, 1>
{
@@ -622,14 +661,14 @@ namespace bpkg
// Return true if there is a conditional alternative in the list.
//
- LIBBPKG_EXPORT bool
+ bool
conditional () const;
};
inline std::ostream&
- operator<< (std::ostream& os, const dependency_alternatives& da)
+ operator<< (std::ostream& os, const dependency_alternatives& das)
{
- return os << da.string ();
+ return os << das.string ();
}
// requires
@@ -673,8 +712,11 @@ namespace bpkg
// Return true if the string() function would return the single-line
// representation.
//
- LIBBPKG_EXPORT bool
- single_line () const;
+ bool
+ single_line () const
+ {
+ return !reflect || reflect->find ('\n') == std::string::npos;
+ }
// Return true if this is a single requirement with an empty id or an
// empty enable condition.
@@ -727,7 +769,7 @@ namespace bpkg
// Return true if there is a conditional alternative in the list.
//
- LIBBPKG_EXPORT bool
+ bool
conditional () const;
// Return true if this is a single simple requirement alternative.
@@ -748,7 +790,7 @@ namespace bpkg
class build_constraint
{
public:
- // If true, then the package should not be built for matching
+ // If true, then the package should not be built for matching target
// configurations by automated build bots.
//
bool exclusion;
@@ -783,48 +825,33 @@ namespace bpkg
//
enum class package_manifest_flags: std::uint16_t
{
- none = 0x00,
-
- forbid_file = 0x01, // Forbid *-file manifest values.
- forbid_location = 0x02,
- forbid_sha256sum = 0x04,
- forbid_fragment = 0x08,
- forbid_incomplete_dependencies = 0x10,
-
- require_location = 0x20,
- require_sha256sum = 0x40,
- require_description_type = 0x80
+ none = 0x000,
+
+ forbid_file = 0x001, // Forbid *-file manifest values.
+ forbid_location = 0x002,
+ forbid_sha256sum = 0x004,
+ forbid_fragment = 0x008,
+ forbid_incomplete_values = 0x010, // depends, <distribution>-version, etc.
+
+ require_location = 0x020,
+ require_sha256sum = 0x040,
+ require_text_type = 0x080, // description-type, changes-type, etc.
+ require_bootstrap_build = 0x100
};
- inline package_manifest_flags
- operator&= (package_manifest_flags& x, package_manifest_flags y)
- {
- return x = static_cast<package_manifest_flags> (
- static_cast<std::uint16_t> (x) &
- static_cast<std::uint16_t> (y));
- }
+ package_manifest_flags
+ operator& (package_manifest_flags, package_manifest_flags);
- inline package_manifest_flags
- operator|= (package_manifest_flags& x, package_manifest_flags y)
- {
- return x = static_cast<package_manifest_flags> (
- static_cast<std::uint16_t> (x) |
- static_cast<std::uint16_t> (y));
- }
+ package_manifest_flags
+ operator| (package_manifest_flags, package_manifest_flags);
- inline package_manifest_flags
- operator& (package_manifest_flags x, package_manifest_flags y)
- {
- return x &= y;
- }
+ package_manifest_flags
+ operator&= (package_manifest_flags&, package_manifest_flags);
- inline package_manifest_flags
- operator| (package_manifest_flags x, package_manifest_flags y)
- {
- return x |= y;
- }
+ package_manifest_flags
+ operator|= (package_manifest_flags&, package_manifest_flags);
- // Build configuration class term.
+ // Target build configuration class term.
//
class LIBBPKG_EXPORT build_class_term
{
@@ -853,9 +880,9 @@ namespace bpkg
build_class_term ()
: operation ('\0'), inverted (false), simple (true), name () {}
- build_class_term (build_class_term&&);
+ build_class_term (build_class_term&&) noexcept;
build_class_term (const build_class_term&);
- build_class_term& operator= (build_class_term&&);
+ build_class_term& operator= (build_class_term&&) noexcept;
build_class_term& operator= (const build_class_term&);
~build_class_term ();
@@ -874,8 +901,8 @@ namespace bpkg
//
using build_class_inheritance_map = std::map<std::string, std::string>;
- // Build configuration class expression. Includes comment and optional
- // underlying set.
+ // Target build configuration class expression. Includes comment and
+ // optional underlying set.
//
class LIBBPKG_EXPORT build_class_expr
{
@@ -924,10 +951,10 @@ namespace bpkg
std::string
string () const;
- // Match a build configuration that belongs to the specified list of
- // classes (and recursively to their bases) against the expression. Either
- // return or update the result (the latter allows to sequentially matching
- // against a list of expressions).
+ // Match a target build configuration that belongs to the specified list
+ // of classes (and recursively to their bases) against the expression.
+ // Either return or update the result (the latter allows to sequentially
+ // matching against a list of expressions).
//
// Notes:
//
@@ -935,7 +962,8 @@ namespace bpkg
// inheritance cycles, etc.).
//
// - The underlying class set doesn't affect the match in any way (it
- // should have been used to pre-filter the set of build configurations).
+ // should have been used to pre-filter the set of target build
+ // configurations).
//
void
match (const strings&,
@@ -943,12 +971,7 @@ namespace bpkg
bool& result) const;
bool
- match (const strings& cs, const build_class_inheritance_map& bs) const
- {
- bool r (false);
- match (cs, bs, r);
- return r;
- }
+ match (const strings&, const build_class_inheritance_map&) const;
};
inline std::ostream&
@@ -957,27 +980,131 @@ namespace bpkg
return os << bce.string ();
}
- enum class text_type
+ // Build auxiliary configuration name-matching wildcard. Includes optional
+ // environment name (specified as a suffix in the [*-]build-auxiliary[-*]
+ // value name) and comment.
+ //
+ class LIBBPKG_EXPORT build_auxiliary
{
- plain,
- common_mark,
- github_mark
- };
+ public:
+ std::string environment_name;
- LIBBPKG_EXPORT std::string
- to_string (text_type);
+ // Filesystem wildcard pattern for the build auxiliary configuration name.
+ //
+ std::string config;
- // Throw std::invalid_argument if the argument is not a well-formed text
- // type. Otherwise, return nullopt for an unknown text variant.
- //
- LIBBPKG_EXPORT butl::optional<text_type>
- to_text_type (const std::string&); // May throw std::invalid_argument.
+ std::string comment;
- inline std::ostream&
- operator<< (std::ostream& os, text_type t)
+ build_auxiliary () = default;
+ build_auxiliary (std::string en,
+ std::string cf,
+ std::string cm)
+ : environment_name (std::move (en)),
+ config (std::move (cf)),
+ comment (std::move (cm)) {}
+
+ // Parse a package manifest value name in the [*-]build-auxiliary[-*] form
+ // into the pair of the build package configuration name (first) and the
+ // build auxiliary environment name (second), with an unspecified name
+ // represented as an empty string. Return nullopt if the value name
+ // doesn't match this form.
+ //
+ static butl::optional<std::pair<std::string, std::string>>
+ parse_value_name (const std::string&);
+ };
+
+ // Package build configuration. Includes comment and optional overrides for
+ // target build configuration class expressions/constraints and build
+ // notification emails.
+ //
+ class build_package_config
{
- return os << to_string (t);
- }
+ public:
+ using email_type = bpkg::email;
+
+ std::string name;
+
+ // Whitespace separated list of potentially double/single-quoted package
+ // configuration arguments for bpkg-pkg-build command executed by
+ // automated build bots.
+ //
+ std::string arguments;
+
+ std::string comment;
+
+ butl::small_vector<build_class_expr, 1> builds;
+ std::vector<build_constraint> constraints;
+
+ // Note that all entries in this list must have distinct environment names
+ // (with empty name being one of the possibilities).
+ //
+ std::vector<build_auxiliary> auxiliaries;
+
+ butl::optional<email_type> email;
+ butl::optional<email_type> warning_email;
+ butl::optional<email_type> error_email;
+
+ build_package_config () = default;
+
+ // Built incrementally.
+ //
+ explicit
+ build_package_config (std::string n): name (move (n)) {}
+
+ // Return the configuration's build class expressions/constraints if they
+ // override the specified common expressions/constraints and return the
+ // latter otherwise (see package_manifest::override() for the override
+ // semantics details).
+ //
+ const butl::small_vector<build_class_expr, 1>&
+ effective_builds (const butl::small_vector<build_class_expr, 1>& common)
+ const noexcept
+ {
+ return !builds.empty () ? builds : common;
+ }
+
+ const std::vector<build_constraint>&
+ effective_constraints (const std::vector<build_constraint>& common) const
+ noexcept
+ {
+ return !builds.empty () || !constraints.empty () ? constraints : common;
+ }
+
+ // Return the configuration's auxiliaries, if specified, and the common
+ // ones otherwise.
+ //
+ const std::vector<build_auxiliary>&
+ effective_auxiliaries (const std::vector<build_auxiliary>& common) const
+ noexcept
+ {
+ return !auxiliaries.empty () ? auxiliaries : common;
+ }
+
+ // Return the configuration's build notification emails if they override
+ // the specified common build notification emails and return the latter
+ // otherwise (see package_manifest::override() for the override semantics
+ // details).
+ //
+ const butl::optional<email_type>&
+ effective_email (const butl::optional<email_type>& common) const noexcept
+ {
+ return email || warning_email || error_email ? email : common;
+ }
+
+ const butl::optional<email_type>&
+ effective_warning_email (const butl::optional<email_type>& common) const
+ noexcept
+ {
+ return email || warning_email || error_email ? warning_email : common;
+ }
+
+ const butl::optional<email_type>&
+ effective_error_email (const butl::optional<email_type>& common) const
+ noexcept
+ {
+ return email || warning_email || error_email ? error_email : common;
+ }
+ };
enum class test_dependency_type
{
@@ -1004,27 +1131,91 @@ namespace bpkg
{
test_dependency_type type;
bool buildtime;
+ butl::optional<std::string> enable;
+ butl::optional<std::string> reflect;
test_dependency () = default;
test_dependency (package_name n,
test_dependency_type t,
bool b,
- butl::optional<version_constraint> c)
- : dependency {std::move (n), std::move (c)}, type (t), buildtime (b) {}
+ butl::optional<version_constraint> c,
+ butl::optional<std::string> e,
+ butl::optional<std::string> r)
+ : dependency {std::move (n), std::move (c)},
+ type (t),
+ buildtime (b),
+ enable (std::move (e)),
+ reflect (std::move (r)) {}
// Parse the test dependency string representation in the
- // `[*] <name> [<version-constraint>]` form. Throw std::invalid_argument
- // if the value is invalid.
+ // `[*] <name> [<version-constraint>] ['?' <enable-condition>] [<reflect-config>]`
+ // form. Throw std::invalid_argument if the value is invalid.
+ //
+ // Verify that the reflect clause, if present, refers to the test
+ // dependency package configuration variable. Note that such variable
+ // value normally signals the dependent package being tested.
//
test_dependency (std::string, test_dependency_type);
- inline std::string
- string () const
- {
- return buildtime
- ? "* " + dependency::string ()
- : dependency::string ();
- }
+ std::string
+ string () const;
+ };
+
+ // Package's buildfile path and content.
+ //
+ struct buildfile
+ {
+ // The path is relative to the package's build/ subdirectory with the
+ // extension stripped.
+ //
+ // For example, for the build/config/common.build file the path will be
+ // config/common.
+ //
+ // Note that the actual file path depends on the project's buildfile
+ // naming scheme and for the config/common example above the actual path
+ // can also be build2/config/common.build2.
+ //
+ butl::path path;
+ std::string content;
+
+ buildfile () = default;
+ buildfile (butl::path p, std::string c)
+ : path (std::move (p)),
+ content (std::move (c)) {}
+ };
+
+ // Binary distribution package information.
+ //
+ // The name is prefixed with the <distribution> id, typically name/version
+ // pair in the <name>[_<version>] form. For example:
+ //
+ // debian-name
+ // debian_10-name
+ // ubuntu_20.04-name
+ //
+ // Currently recognized names:
+ //
+ // <distribution>-name
+ // <distribution>-version
+ // <distribution>-to-downstream-version
+ //
+ // Note that the value format/semantics can be distribution-specific.
+ //
+ struct distribution_name_value
+ {
+ std::string name;
+ std::string value;
+
+ distribution_name_value () = default;
+ distribution_name_value (std::string n, std::string v)
+ : name (std::move (n)),
+ value (std::move (v)) {}
+
+ // Return the name's <distribution> component if the name has the
+ // specified suffix, which is assumed to be valid (-name, etc).
+ //
+ butl::optional<std::string>
+ distribution (const std::string& suffix) const;
};
class LIBBPKG_EXPORT package_manifest
@@ -1037,16 +1228,18 @@ namespace bpkg
package_name name;
version_type version;
butl::optional<std::string> upstream_version;
+ butl::optional<std::string> type; // <name>[, ...]
+ butl::small_vector<language, 1> languages; // <name>[=impl][, ...]
butl::optional<package_name> project;
butl::optional<priority_type> priority;
std::string summary;
-
butl::small_vector<licenses, 1> license_alternatives;
+
butl::small_vector<std::string, 5> topics;
butl::small_vector<std::string, 5> keywords;
- butl::optional<text_file> description;
- butl::optional<std::string> description_type;
- butl::small_vector<text_file, 1> changes;
+ butl::optional<typed_text_file> description;
+ butl::optional<typed_text_file> package_description;
+ butl::small_vector<typed_text_file, 1> changes;
butl::optional<manifest_url> url;
butl::optional<manifest_url> doc_url;
butl::optional<manifest_url> src_url;
@@ -1060,8 +1253,39 @@ namespace bpkg
std::vector<requirement_alternatives> requirements;
butl::small_vector<test_dependency, 1> tests;
+ // Common build classes, constraints, and auxiliaries 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;
+
+ // Note that the parsing constructor adds the implied (empty) default
+ // configuration at the beginning of the list. Also note that serialize()
+ // writes no values for such a configuration.
+ //
+ butl::small_vector<build_package_config, 1> build_configs; // 1 for default.
+
+ // If true, then this package use the alternative buildfile naming scheme
+ // (build2/, .build2). In the manifest serialization this is encoded as
+ // either *-build or *-build2 value names.
+ //
+ butl::optional<bool> alt_naming;
+
+ butl::optional<std::string> bootstrap_build;
+ butl::optional<std::string> root_build;
+
+ // Additional buildfiles which are potentially included by root.build.
+ //
+ std::vector<buildfile> buildfiles; // Buildfiles content.
+ std::vector<butl::path> buildfile_paths;
+
+ // The binary distributions package information.
+ //
+ std::vector<distribution_name_value> distribution_values;
// The following values are only valid in the manifest list (and only for
// certain repository types).
@@ -1070,19 +1294,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;
@@ -1094,7 +1344,7 @@ namespace bpkg
//
package_manifest (butl::manifest_parser&,
bool ignore_unknown = false,
- bool complete_dependencies = true,
+ bool complete_values = true,
package_manifest_flags =
package_manifest_flags::forbid_location |
package_manifest_flags::forbid_sha256sum |
@@ -1116,7 +1366,7 @@ namespace bpkg
package_manifest (butl::manifest_parser&,
const std::function<translate_function>&,
bool ignore_unknown = false,
- bool complete_depends = true,
+ bool complete_values = true,
package_manifest_flags =
package_manifest_flags::forbid_location |
package_manifest_flags::forbid_sha256sum |
@@ -1131,7 +1381,7 @@ namespace bpkg
package_manifest (const std::string& name,
std::vector<butl::manifest_name_value>&&,
bool ignore_unknown = false,
- bool complete_dependencies = true,
+ bool complete_values = true,
package_manifest_flags =
package_manifest_flags::forbid_location |
package_manifest_flags::forbid_sha256sum |
@@ -1141,7 +1391,7 @@ namespace bpkg
std::vector<butl::manifest_name_value>&&,
const std::function<translate_function>&,
bool ignore_unknown = false,
- bool complete_depends = true,
+ bool complete_values = true,
package_manifest_flags =
package_manifest_flags::forbid_location |
package_manifest_flags::forbid_sha256sum |
@@ -1152,27 +1402,63 @@ namespace bpkg
package_manifest (butl::manifest_parser&,
butl::manifest_name_value start,
bool ignore_unknown,
- bool complete_depends,
+ bool complete_values,
package_manifest_flags);
// Override manifest values with the specified. Throw manifest_parsing if
// any value is invalid, cannot be overridden, or its name is not
// recognized.
//
- // The specified values override the whole groups they belong to,
- // resetting all the group values prior to being applied. Currently, only
- // the following value groups can be overridden: {build-*email} and
- // {builds, build-{include,exclude}}.
- //
- // Note that the build constraints group values are overridden
- // hierarchically so that the build-{include,exclude} overrides don't
- // affect the builds values.
+ // The specified values other than [*-]build-auxiliary[-*] override the
+ // whole groups they belong to, resetting all the group values prior to
+ // being applied. The [*-]build-auxiliary[-*] values only override the
+ // matching values, which are expected to already be present in the
+ // manifest. Currently, only the following value groups/values can be
+ // overridden:
+ //
+ // {build-*email}
+ // {builds, build-{include,exclude}}
+ // {*-builds, *-build-{include,exclude}}
+ // {*-build-config}
+ // {*-build-*email}
+ //
+ // [*-]build-auxiliary[-*]
+ //
+ // Throw manifest_parsing if the configuration specified by the build
+ // package configuration-specific build constraint, email, or auxiliary
+ // 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, and auxiliary overrides for
+ // such a newly added configuration must follow the respective
+ // *-build-config override.
+ //
+ // Note that the build constraints group values (both common and build
+ // config-specific) are overridden hierarchically so that the
+ // [*-]build-{include,exclude} overrides don't affect the respective
+ // [*-]builds values.
+ //
+ // Also note that the common and build config-specific build constraints
+ // group value overrides are mutually exclusive. If the common build
+ // constraints are overridden, then all the build config-specific
+ // constraints are removed. Otherwise, if some build config-specific
+ // constraints are overridden, then for the remaining configs the build
+ // constraints are reset to `builds: none`.
+ //
+ // Similar to the build constraints groups, the common and build
+ // config-specific build emails group value overrides are mutually
+ // exclusive. If the common build emails are overridden, then all the
+ // build config-specific emails are reset to nullopt. Otherwise, if some
+ // build config-specific emails are overridden, then for the remaining
+ // configs the email is reset to the empty value and the warning and error
+ // emails are reset to nullopt (which effectively disables email
+ // notifications for such configurations).
//
// If a non-empty source name is specified, then the specified values are
// assumed to also include the line/column information and the possibly
- // thrown manifest_parsing exception will contain the invalid value
+ // thrown manifest_parsing exception will contain the invalid value's
// location information. Otherwise, the exception description will refer
- // to the invalid value name instead.
+ // to the invalid value instead.
//
void
override (const std::vector<butl::manifest_name_value>&,
@@ -1180,6 +1466,13 @@ namespace bpkg
// Validate the overrides without applying them to any manifest.
//
+ // Specifically, validate that the override values can be parsed according
+ // to their name semantics and that the value sequence makes sense (no
+ // mutually exclusive values, etc). Note, however, that the subsequent
+ // applying of the successfully validated overrides to a specific package
+ // manifest may still fail (no build config exists for specified *-builds,
+ // etc).
+ //
static void
validate_overrides (const std::vector<butl::manifest_name_value>&,
const std::string& source_name);
@@ -1205,17 +1498,20 @@ namespace bpkg
// Load the *-file manifest values using the specified load function that
// returns the file contents passing through any exception it may throw.
- // Set the potentially absent description type value to the effective
- // description type. If the effective type is nullopt then assign a
- // synthetic unknown type.
+ // If nullopt is returned, then the respective *-file value is left
+ // unexpanded. Set the potentially absent project description, package
+ // description, and changes type values to their effective types. If an
+ // effective type is nullopt then assign a synthetic unknown type if the
+ // ignore_unknown argument is true and throw manifest_parsing otherwise.
//
// Note that if the returned file contents is empty, load_files() makes
// sure that this is allowed by the value's semantics throwing
// manifest_parsing otherwise. However, the load function may want to
// recognize such cases itself in order to issue more precise diagnostics.
//
- using load_function = std::string (const std::string& name,
- const butl::path& value);
+ using load_function =
+ butl::optional<std::string> (const std::string& name,
+ const butl::path& value);
void
load_files (const std::function<load_function>&,
@@ -1224,13 +1520,10 @@ namespace bpkg
// Create individual package manifest.
//
- inline package_manifest
+ package_manifest
pkg_package_manifest (butl::manifest_parser& p,
bool ignore_unknown = false,
- bool complete_depends = true)
- {
- return package_manifest (p, ignore_unknown, complete_depends);
- }
+ bool complete_values = true);
LIBBPKG_EXPORT package_manifest
dir_package_manifest (butl::manifest_parser&, bool ignore_unknown = false);
@@ -1536,9 +1829,8 @@ namespace bpkg
repository_type,
const repository_location& base);
- repository_location (const repository_location& l,
- const repository_location& base)
- : repository_location (l.url (), l.type (), base) {}
+ repository_location (const repository_location&,
+ const repository_location& base);
// Note that relative locations have no canonical name. Canonical name of
// an empty location is the empty name.
@@ -1556,59 +1848,22 @@ namespace bpkg
empty () const noexcept {return url_.empty ();}
bool
- local () const
- {
- if (empty ())
- throw std::logic_error ("empty location");
-
- return url_.scheme == repository_protocol::file;
- }
+ local () const;
bool
- remote () const
- {
- return !local ();
- }
+ remote () const;
bool
- absolute () const
- {
- if (empty ())
- throw std::logic_error ("empty location");
-
- // Note that in remote locations path is always relative.
- //
- return url_.path->absolute ();
- }
+ absolute () const;
bool
- relative () const
- {
- return local () && url_.path->relative ();
- }
+ relative () const;
repository_type
- type () const
- {
- if (empty ())
- throw std::logic_error ("empty location");
-
- return type_;
- }
+ type () const;
repository_basis
- basis () const
- {
- switch (type ())
- {
- case repository_type::pkg: return repository_basis::archive;
- case repository_type::dir: return repository_basis::directory;
- case repository_type::git: return repository_basis::version_control;
- }
-
- assert (false); // Can't be here.
- return repository_basis::archive;
- }
+ basis () const;
// Note that the URL of an empty location is empty.
//
@@ -1622,69 +1877,30 @@ namespace bpkg
// "directories" it always contains the trailing slash.
//
const butl::path&
- path () const
- {
- if (empty ())
- throw std::logic_error ("empty location");
-
- return *url_.path;
- }
+ path () const;
const std::string&
- host () const
- {
- if (local ())
- throw std::logic_error ("local location");
-
- return url_.authority->host;
- }
+ host () const;
// Value 0 indicated that no port was specified explicitly.
//
std::uint16_t
- port () const
- {
- if (local ())
- throw std::logic_error ("local location");
-
- return url_.authority->port;
- }
+ port () const;
repository_protocol
- proto () const
- {
- if (empty ())
- throw std::logic_error ("empty location");
-
- return url_.scheme;
- }
+ proto () const;
const butl::optional<std::string>&
- fragment () const
- {
- if (relative ())
- throw std::logic_error ("relative filesystem path");
-
- return url_.fragment;
- }
+ fragment () const;
bool
- archive_based () const
- {
- return basis () == repository_basis::archive;
- }
+ archive_based () const;
bool
- directory_based () const
- {
- return basis () == repository_basis::directory;
- }
+ directory_based () const;
bool
- version_control_based () const
- {
- return basis () == repository_basis::version_control;
- }
+ version_control_based () const;
// Return an untyped URL if the correct type can be guessed just from
// the URL. Otherwise, return the typed URL.
@@ -1981,4 +2197,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 259cf0a..454925e 100644
--- a/manifest
+++ b/manifest
@@ -1,6 +1,6 @@
: 1
name: libbpkg
-version: 0.15.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.14.0-
-depends: * bpkg >= 0.14.0-
-depends: libbutl [0.15.0-a.0.1 0.15.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 9f8a476..488668c 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,462 @@
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-config
+ :
+ {
+ : multiple
+ :
+ {
+ $* <<EOF >>EOF
+ : 1
+ name: foo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ bar-builds: all
+ bar-build-config: config.foo.bar = true; Bar.
+ baz-build-config: config.foo.baz = true; Baz.
+ EOF
+ }
+
+ : empty
+ :
+ $* <<EOF >>EOF
+ : 1
+ name: foo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ network-build-config: ; None.
+ EOF
+
+ : undefined
+ :
+ {
+ $* <<EOF >>EOF
+ : 1
+ name: foo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ bar-builds: default
+ baz-build-config: config.foo.bar = true
+ EOF
+ }
+
+ : redefinition
+ :
+ {
+ $* <<EOI 2>"stdin:3:1: error: build configuration redefinition" != 0
+ : 1
+ bar-build-config: config.foo.bar = true
+ bar-build-config: config.foo.bar = true
+ EOI
+ }
+
+ : unexpected-underlying-class-set
+ :
+ {
+ $* <<EOI 2>"stdin:4:13: error: invalid package builds: unexpected underlying class set" != 0
+ : 1
+ bar-build-config: config.foo.bar = true
+ bar-builds: all
+ 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
:
{
@@ -953,6 +1916,29 @@
stdin:6:14: error: config.foo.* variable assignment expected instead of <buildfile fragment>
EOE
}
+
+ : comments
+ :
+ $* <<EOI 2>>EOE != 0
+ : 1
+ name: foo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ depends:\
+ bar
+ {
+ require
+ {
+ config.bar.frame=4016
+ config.bar.timeout=10
+ }
+ } |
+ {bar # Error.
+ \
+ EOI
+ stdin:15:6: error: invalid package name: length is less than two characters
+ EOE
}
: multi-line
@@ -972,7 +1958,8 @@
bar
- ; Comment.
+ ;
+ Comment.
\
EOI
: 1
@@ -1093,9 +2080,10 @@
\
EOF
- : no-accept-clause
+
+ : empty
:
- $* <<EOI 2>>EOE != 0
+ $* <<EOF >>EOF
: 1
name: foo
version: 2.0.0
@@ -1106,15 +2094,14 @@
{
prefer
{
- config.bar.frame=4016
}
+
+ accept (true)
}
\
- EOI
- stdin:13:1: error: accept clause expected instead of '}'
- EOE
+ EOF
- : empty
+ : no-accept-clause
:
$* <<EOI 2>>EOE != 0
: 1
@@ -1127,12 +2114,12 @@
{
prefer
{
-
+ config.bar.frame=4016
}
}
\
EOI
- stdin:11:1: error: buildfile fragment expected
+ stdin:13:1: error: accept clause expected instead of '}'
EOE
: enable-clause
@@ -1302,7 +2289,8 @@
{
require
{
- config.bar.baz=a;bc
+ config.bar.baz=a
+ ;
}
}
\
@@ -1323,7 +2311,7 @@
{
require
{
- # @@ TMP Version tagging? See
+ # @@ Version tagging? See
# upstream/qtbase/src/corelib/global/minimum-linux{.S,_p.h},
# global/qversiontagging.cpp, and QtCore.version (linker version script) in
# the upstream build.
@@ -1731,7 +2719,7 @@
# C sources from the Harfbuzz library.
#
- # @@ TMP Package this? (And double-conversion?)
+ # @@ Package this? (And double-conversion?)
#
harfbuzz_src = harfbuzz-buffer \\
harfbuzz-gdef \\
@@ -1765,7 +2753,7 @@
# Ensure included source files are distributed.
#
- # @@ TMP thread/qmutex_{linux,unix,mac,win}.cpp is included by
+ # @@ thread/qmutex_{linux,unix,mac,win}.cpp is included by
# thread/qmutex.cpp, but thread/thread.pri adds it to SOURCES, meaning
# the upstream build should compile it as well (which would lead to
# multiple definition errors). This is confirmed by the fact that the
@@ -1779,7 +2767,7 @@
# No idea what's going on but what we have here looks right and works
# and matches what the upstream build actually ends up doing.
#
- # @@ TMP mimetypes/qmimeprovider_database.cpp (note: ~2MB) is generated by
+ # @@ mimetypes/qmimeprovider_database.cpp (note: ~2MB) is generated by
# mimetypes/mime/generate.{pl,bat} from
# mimetypes/mime/packages/freedesktop.org.xml which is from
# http://www.freedesktop.org/wiki/Software/shared-mime-info/. This doesn't
@@ -1798,7 +2786,7 @@
# The "metadata library": its purpose is to make sure all the imported
# libraries are resolved for the ad hoc .mm compilation rules below.
#
- # @@ TMP Using cxx{dummy} as link rule hint (added `using c` recently).
+ # @@ Using cxx{dummy} as link rule hint (added `using c` recently).
#
libul{Qt5CoreMeta}: mkspecs/features/data/cxx{dummy} $impl_libs $intf_libs
@@ -1920,7 +2908,7 @@
#
# @@ Have to list hxx{moc_predefs} here to make dist work -- why?
#
- # @@ TMP Is this comment still relevant? moc_predefs is now only a
+ # @@ Is this comment still relevant? moc_predefs is now only a
# dependency in the ad hoc rule below.
#
for s: hxx{$moc_hdr}
@@ -2290,9 +3278,9 @@
# Build options.
#
# Add QtCore/3rdparty/ for include of <double-conversion/fixed-dtoa.h> (in
- # double-conversion/fixed-dtoa.cc)\; QtCore/3rdparty/double-conversion/include/
- # for includes of <double-conversion/*>\; QtCore/3rdparty/harfbuzz/src/ for an
- # include of <harfbuzz-shaper.h>\; and QtCore/3rdparty/forkfd/ for an include
+ # double-conversion/fixed-dtoa.cc); QtCore/3rdparty/double-conversion/include/
+ # for includes of <double-conversion/*>; QtCore/3rdparty/harfbuzz/src/ for an
+ # include of <harfbuzz-shaper.h>; and QtCore/3rdparty/forkfd/ for an include
# of <forkfd.h>.
#
cxx.poptions =+ "-I$out_root" "-I$src_root" \\
@@ -2317,7 +3305,7 @@
-DQT_DEPRECATED_WARNINGS_SINCE=0x060000 \\
-DQT_NO_VERSION_TAGGING
- # @@ TMP Are the hbmi* of any use?
+ # @@ Are the hbmi* of any use?
#
{hbmia obja}{*}: cxx.poptions += -DQT_STATIC
{hbmis objs}{*}: cxx.poptions += -DQT_SHARED
@@ -2348,13 +3336,13 @@
lib{Qt5Core}: cxx.libs += -ldl
}
- # @@ TMP Not sure if/when this is necessary.
+ # @@ Not sure if/when this is necessary.
#
# cxx.libs += -latomic
# libexecinfo is required for backtrace(3) on BSD (see global/qlogging.cpp).
#
- # @@ TMP When we did libboost-stacktrace we considered packaging libbacktrace:
+ # @@ When we did libboost-stacktrace we considered packaging libbacktrace:
# build2-packaging/boost/downstream/libs/stacktrace/src/buildfile.
#
if $bsd
@@ -2366,9 +3354,9 @@
# explained in global/global.pri but see also the SSE/AVX comments in
# qconfig.h.in.
#
- # @@ TMP Assuming the comments regarding special compiler options for
+ # @@ Assuming the comments regarding special compiler options for
# SSE/AVX/NEON in qconfig.h.in apply to F16C as well. global/global.pri
- # seems to make this connection as well (if only for GCC\; x86SimdAlways in
+ # seems to make this connection as well (if only for GCC; x86SimdAlways in
# global.pri corresponds to QT_COMPILER_SUPPORTS_SIMD_ALWAYS in
# qconfig.h.in).
#
@@ -2560,6 +3548,167 @@
stdin:9:1: error: multi-line dependency form with inline reflect clause
EOE
}
+
+ : comments
+ :
+ {
+ : single-line-separate
+ :
+ $* <<EOI >>EOO
+ : 1
+ name: foo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ depends:\
+ bar
+ {
+ # Configure bar.
+ #
+ require
+ {
+ config.bar.frame=4016
+ config.bar.timeout=10
+ }
+ }
+ \
+ EOI
+ : 1
+ name: foo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ depends:\
+ bar
+ {
+ require
+ {
+ config.bar.frame=4016
+ config.bar.timeout=10
+ }
+ }
+ \
+ EOO
+
+ : single-line-trailing
+ :
+ $* <<EOI >>EOO
+ : 1
+ name: foo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ depends:\
+ bar
+ {
+ require # Configure bar.
+ {
+ config.bar.frame=4016
+ config.bar.timeout=10
+ }
+ }
+ \
+ EOI
+ : 1
+ name: foo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ depends:\
+ bar
+ {
+ require
+ {
+ config.bar.frame=4016
+ config.bar.timeout=10
+ }
+ }
+ \
+ EOO
+
+ : multi-line
+ :
+ $* <<EOI >>EOO
+ : 1
+ name: foo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ depends:\
+ bar
+ {
+ #\\
+ # Configure bar.
+ #
+ require
+ {
+ config.bar.frame=4016
+ config.bar.timeout=10
+ }
+ #\\
+
+ prefer
+ {
+ config.bar.frame=4016
+ config.bar.timeout=10
+ }
+
+ accept ($config.bar.frame >= 1024 && config.bar.timeout < 20)
+ }
+ \
+ EOI
+ : 1
+ name: foo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ depends:\
+ bar
+ {
+ prefer
+ {
+ config.bar.frame=4016
+ config.bar.timeout=10
+ }
+
+ accept ($config.bar.frame >= 1024 && config.bar.timeout < 20)
+ }
+ \
+ EOO
+
+ : multi-line-unterm
+ :
+ $* <<EOI 2>>EOE != 0
+ : 1
+ name: foo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ depends:\
+ bar
+ {
+ #\\
+ # Configure bar.
+ #
+ require
+ {
+ config.bar.frame=4016
+ config.bar.timeout=10
+ }
+
+ prefer
+ {
+ config.bar.frame=4016
+ config.bar.timeout=10
+ }
+
+ accept ($config.bar.frame >= 1024 && config.bar.timeout < 20)
+ }
+ \
+ EOI
+ stdin:25:2: error: unterminated multi-line comment
+ EOE
+ }
}
: multiple-alternatives
@@ -2778,7 +3927,8 @@
license: LGPLv2
requires:\
- ; X11 libs.
+ ;
+ X11 libs.
\
EOI
: 1
@@ -2850,6 +4000,29 @@
stdin:6:13: error: end of simple requirement expected
EOE
+ # @@ TMP Drop this test and uncomment the next one when toolchain 0.15.0
+ # is released (see dependency_alternatives_parser::parse_alternative()
+ # for details).
+ #
+ : old-fashioned
+ :
+ $* <<EOI >>EOO
+ : 1
+ name: foo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ requires: ? vc15; VC 15 or later if targeting Windows.
+ EOI
+ : 1
+ name: foo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ requires: vc15 ? ; VC 15 or later if targeting Windows.
+ EOO
+
+ #\
: reflect
:
$* <<EOI 2>>EOE != 0
@@ -2862,6 +4035,7 @@
EOI
stdin:6:13: error: end of simple requirement expected
EOE
+ #\
: with-id
:
@@ -3042,6 +4216,506 @@
license: LGPLv2
tests: bar == $
EOF
+
+ : reflect
+ :
+ {
+ : after-version-constraint
+ :
+ $* <<EOF >>EOF
+ : 1
+ name: foo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ tests: bar == 1.0.0 config.bar.test = foo
+ EOF
+
+ : no-version-constraint
+ :
+ $* <<EOF >>EOF
+ : 1
+ name: foo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ 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
+ : 1
+ name: foo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ tests: bar config.foo.test = bar
+ EOI
+ stdin:6:8: error: config.bar.* variable assignment expected instead of <buildfile fragment>
+ 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
+ : 1
+ name: foo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ tests:\
+ *
+ bar
+ \
+ EOI
+ stdin:7:1: error: unexpected <newline>
+ EOE
+
+ : no-package
+ :
+ $* <<EOI 2>>EOE != 0
+ : 1
+ name: foo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ tests: *
+ EOI
+ stdin:6:8: error: no package name specified
+ EOE
+
+ : multiple-alternatives
+ :
+ $* <<EOI 2>>EOE != 0
+ : 1
+ name: foo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ tests: bar | baz
+ EOI
+ stdin:6:8: error: unexpected '|'
+ EOE
+
+ : multiple-dependencies
+ :
+ $* <<EOI 2>>EOE != 0
+ : 1
+ name: foo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ tests: {bar baz}
+ EOI
+ stdin:6:8: error: only single package allowed
+ EOE
+ }
+
+ : buildfile
+ :
+ {
+ : standard-naming
+ :
+ $* <<EOF >>EOF
+ : 1
+ name: libfoo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ bootstrap-build:\
+ project = libfoo
+
+ using version
+ using config
+ using dist
+ using test
+ using install
+ \
+ root-build:\
+ include config/common.build
+
+ cxx.std = latest
+
+ using cxx
+ \
+ config/common-build:\
+ {
+ config [bool] config.libfoo.extras ?= false
+ }
+ \
+ EOF
+
+ : alt-naming
+ :
+ $* <<EOF >>EOF
+ : 1
+ name: libfoo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ bootstrap-build2:\
+ project = libfoo
+
+ using version
+ using config
+ using dist
+ using test
+ using install
+ \
+ root-build2:\
+ include config/common.build2
+
+ cxx.std = latest
+
+ using cxx
+ \
+ config/common-build2:\
+ {
+ config [bool] config.libfoo.extras ?= false
+ }
+ \
+ EOF
+
+ : mixed-naming
+ :
+ $* <<EOI 2>>EOE != 0
+ : 1
+ name: libfoo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ bootstrap-build:\
+ project = libfoo
+
+ using version
+ using config
+ using dist
+ using test
+ using install
+ \
+ root-build:\
+ include config/common.build
+
+ cxx.std = latest
+
+ using cxx
+ \
+ config/common-build2:\
+ {
+ config [bool] config.libfoo.extras ?= false
+ }
+ \
+ EOI
+ stdin:22:1: error: standard buildfile naming scheme is already used
+ EOE
+
+ : backslash
+ :
+ $* <<EOI 2>>EOE != 0
+ : 1
+ name: libfoo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ config\common-build:\
+ {
+ config [bool] config.libfoo.extras ?= false
+ }
+ \
+ EOI
+ stdin:6:1: error: backslash in package buildfile path
+ EOE
+
+ : unknown
+ :
+ $* <<EOI 2>>EOE != 0
+ : 1
+ name: libfoo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ common.build:\
+ {
+ config [bool] config.libfoo.extras ?= false
+ }
+ \
+ EOI
+ stdin:6:1: error: unknown name 'common.build' in package manifest
+ EOE
+
+ : empty-name
+ :
+ $* <<EOI 2>>EOE != 0
+ : 1
+ name: libfoo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ config/-build:\
+ {
+ config [bool] config.libfoo.extras ?= false
+ }
+ \
+ EOI
+ stdin:6:1: error: empty package buildfile name
+ EOE
+
+ : absolute-invalid
+ :
+ $* <<EOI 2>>~%EOE% != 0
+ : 1
+ name: libfoo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ /config/common-build:\
+ {
+ config [bool] config.libfoo.extras ?= false
+ }
+ \
+ EOI
+ %stdin:6:1: error: (absolute|invalid) package buildfile path%
+ EOE
+
+ : outside
+ :
+ $* <<EOI 2>>EOE != 0
+ : 1
+ name: libfoo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ common/../../common-build:\
+ {
+ config [bool] config.libfoo.extras ?= false
+ }
+ \
+ EOI
+ stdin:6:1: error: package buildfile path refers outside build/ subdirectory
+ EOE
+
+ : redefinition
+ :
+ $* <<EOI 2>>EOE != 0
+ : 1
+ name: libfoo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ common-build:\
+ {
+ config [bool] config.libfoo.extras ?= false
+ }
+ \
+ common-build:\
+ {
+ config [bool] config.libfoo.extras ?= false
+ }
+ \
+ EOI
+ stdin:11:1: error: package buildfile redefinition
+ EOE
+ }
+
+ : buildfile-path
+ :
+ {
+ : standard-naming
+ :
+ $* <<EOF >>EOF
+ : 1
+ name: libfoo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ bootstrap-build:\
+ project = libfoo
+
+ using version
+ using config
+ using dist
+ using test
+ using install
+ \
+ root-build:\
+ include config/common.build
+ include config/extra.build
+
+ cxx.std = latest
+
+ using cxx
+ \
+ config/common-build:\
+ {
+ config [bool] config.libfoo.extras ?= false
+ }
+ \
+ build-file: config/extra.build
+ EOF
+
+ : alt-naming
+ :
+ $* <<EOF >>EOF
+ : 1
+ name: libfoo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ bootstrap-build2:\
+ project = libfoo
+
+ using version
+ using config
+ using dist
+ using test
+ using install
+ \
+ root-build2:\
+ include config/common.build2
+
+ cxx.std = latest
+
+ using cxx
+ \
+ build-file: config/common.build2
+ EOF
+
+ : mixed-naming
+ :
+ $* <<EOI 2>>EOE != 0
+ : 1
+ name: libfoo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ build-file: config/common.build
+ build-file: config/extra.build2
+ \
+ EOI
+ stdin:7:13: error: standard buildfile naming scheme is already used
+ EOE
+
+ : empty-path
+ :
+ $* <<EOI 2>>EOE != 0
+ : 1
+ name: libfoo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ build-file:
+ EOI
+ stdin:6:12: error: path with build or build2 extension expected
+ EOE
+
+ : invalid-extension
+ :
+ $* <<EOI 2>>EOE != 0
+ : 1
+ name: libfoo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ build-file: common.buildfile
+ EOI
+ stdin:6:13: error: path with build or build2 extension expected
+ EOE
+
+ : redefinition1
+ :
+ $* <<EOI 2>>EOE != 0
+ : 1
+ name: libfoo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ common-build:\
+ {
+ config [bool] config.libfoo.extras ?= false
+ }
+ \
+ build-file: common.build
+ EOI
+ stdin:11:13: error: package buildfile redefinition
+ EOE
+
+ : redefinition2
+ :
+ $* <<EOI 2>>EOE != 0
+ : 1
+ name: libfoo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ build-file: config/common.build
+ build-file: config/common.build
+ EOI
+ stdin:7:13: error: package buildfile redefinition
+ EOE
+
+ : bootstrap-build
+ :
+ $* <<EOI 2>>EOE != 0
+ : 1
+ name: libfoo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ build-file: bootstrap.build
+ EOI
+ stdin:6:13: error: bootstrap not allowed
+ EOE
}
}
@@ -3061,6 +4735,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
@@ -3069,8 +4745,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
@@ -3099,6 +4778,20 @@
build-include: linux*
build-include: freebsd*
build-exclude: *; Only supports Linux and FreeBSD.
+ 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
+
+ \
+ root-build:\
+ cxx.std = latest
+
+ using cxx
+
+ \
location: libfoo-1.2.3+2.tar.bz2
sha256sum: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
:
@@ -3114,6 +4807,10 @@
builds: default legacy; Default and legacy.
builds: -windows; Not on Windows.
build-exclude: *-msvc_14*/i?86-*; Linker crash.
+ bootstrap-build:\
+ project = libbar
+
+ \
location: bar/libbar-3.4A.5+6.tbz
sha256sum: d4b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
:
@@ -3125,6 +4822,10 @@
email: libbaz-users@example.org
build-error-email: libbaz-issues@example.org; Email for libbaz issues.
builds: default experimental
+ bootstrap-build:\
+ project = libbaz
+
+ \
location: libbaz/libbaz-+2-3.4A.5+3.tar.gz
sha256sum: b5b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
EOF
@@ -3145,6 +4846,28 @@
depends: bar == $
EOI
}
+
+ : buildfiles
+ :
+ {
+ # @@ TMP Uncomment when the missing bootstrap-build value related
+ # workaround is removed (see manifest.cxx for details).
+ #\
+ : no-bootstrap
+ :
+ $* <<EOI 2>"stdin:10:1: error: no package bootstrap build specified" != 0
+ : 1
+ sha256sum: a2b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
+ :
+ name: foo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ location: foo/foo-2.0.0.tar.gz
+ sha256sum: b5b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
+ EOI
+ #\
+ }
}
: dir
@@ -3705,3 +5428,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 be3e0ff..c4a09ef 100644
--- a/tests/overrides/driver.cxx
+++ b/tests/overrides/driver.cxx
@@ -33,7 +33,7 @@ main (int argc, char* argv[])
{
vector<manifest_name_value> overrides;
- bool name (false);
+ string name;
uint64_t l (1);
for (int i (1); i != argc; ++i)
@@ -42,7 +42,7 @@ main (int argc, char* argv[])
if (a == "-n")
{
- name = true;
+ name = "args";
}
else
{
@@ -78,7 +78,19 @@ main (int argc, char* argv[])
try
{
package_manifest m (p);
- m.override (overrides, name ? "args" : string ());
+ m.override (overrides, name);
+
+ // While at it, test validate_overrides().
+ //
+ try
+ {
+ package_manifest::validate_overrides (overrides, name);
+ }
+ catch (const manifest_parsing&)
+ {
+ assert (false); // Validation must never fail if override succeeds.
+ }
+
m.serialize (s);
}
catch (const manifest_parsing& e)
diff --git a/tests/overrides/testscript b/tests/overrides/testscript
index babe57d..60168a4 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
@@ -35,6 +40,10 @@
builds: default
build-include: linux*
build-exclude: *; Only supports Linux.
+ network-build-config: config.libfoo.network=true
+ network-builds: default
+ network-build-include: linux*
+ network-build-exclude: *
EOI
: 1
name: libfoo
@@ -42,6 +51,7 @@
summary: Modern C++ parser
license: LGPLv2
builds: gcc
+ network-build-config: config.libfoo.network=true
EOO
: build-include-exclude
@@ -54,6 +64,10 @@
license: LGPLv2
builds: default
build-exclude: freebsd*
+ network-build-config: config.libfoo.network=true
+ network-builds: default
+ network-build-include: linux*
+ network-build-exclude: *
EOI
: 1
name: libfoo
@@ -63,6 +77,7 @@
builds: default
build-include: linux*
build-exclude: *; Only supports Linux.
+ network-build-config: config.libfoo.network=true
EOO
: builds-build-include-exclude
@@ -86,6 +101,142 @@
build-exclude: *; Only supports Linux.
EOO
+ : 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
+ name: libfoo
+ 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-builds: default
+ network-build-include: linux*
+ network-build-exclude: *
+ 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: *
+ cache-build-config: config.libfoo.cache=true
+ cache-build-email: cache@example.com
+ sys-builds: default
+ sys-build-include: freebsd*
+ sys-build-exclude: *
+ sys-build-config: ?sys:libcrypto
+ sys-build-email: sys@example.com
+ older-builds: default
+ older-build-include: windows*
+ older-build-exclude: *
+ 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
+ default-build-email:
+ network-builds: all
+ network-build-include: windows*
+ network-build-exclude: *
+ 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: *
+ 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: *
+ sys-build-config: ?sys:libcrypto
+ sys-build-email:
+ older-builds: none
+ 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
+ : 1
+ name: libfoo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ network-builds: all
+ network-build-config: config.libfoo.network=true
+ EOI
+ : 1
+ name: libfoo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ default-builds: all
+ default-build-include: windows*
+ default-build-exclude: *
+ 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
:
$* <<EOI >>EOO
@@ -103,6 +254,84 @@
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
+ }
}
: invalid
@@ -141,4 +370,198 @@
EOI
args:2:8: error: invalid package builds: unexpected underlying class set
EOE
+
+
+ : no-build-config
+ :
+ $* 'network-builds: default' <<EOI 2>>EOE != 0
+ : 1
+ name: libfoo
+ version: 2.0.0
+ summary: Modern C++ parser
+ license: LGPLv2
+ EOI
+ cannot override 'network-builds' value: no build package configuration 'network'
+ EOE
+
+ : config-builds-after-builds
+ :
+ $* 'builds: all' 'network-builds: default' <<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-builds' override specified together with 'builds' override
+ EOE
+
+ : config-builds-after-build-exclude
+ :
+ $* 'build-exclude: *' 'network-builds: default' <<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-builds' override specified together with 'build-exclude' override
+ EOE
+
+ : builds-after-config-builds
+ :
+ $* 'network-builds: default' 'builds: all' <<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
+ 'builds' override specified together with 'network-builds' override
+ EOE
+
+ : build-exclude-after-config-builds
+ :
+ $* 'network-builds: default' 'build-exclude: *' <<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-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-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
+ }
}
diff --git a/tests/repository-location/driver.cxx b/tests/repository-location/driver.cxx
index c93b257..4a4bbe4 100644
--- a/tests/repository-location/driver.cxx
+++ b/tests/repository-location/driver.cxx
@@ -902,10 +902,10 @@ namespace bpkg
assert (git_ref_filter (n) == git_ref_filter (n, nullopt, false));
assert (git_ref_filter ('+' + n) == git_ref_filter (n, nullopt, false));
assert (git_ref_filter ('-' + n) == git_ref_filter (n, nullopt, true));
- assert (git_ref_filter (c + "@") == git_ref_filter (c, nullopt, false));
+ assert (git_ref_filter (c + '@') == git_ref_filter (c, nullopt, false));
assert (git_ref_filter (c) == git_ref_filter (nullopt, c, false));
- assert (git_ref_filter ("@" + c) == git_ref_filter (nullopt, c, false));
- assert (git_ref_filter (n + "@" + c) == git_ref_filter (n, c, false));
+ assert (git_ref_filter ('@' + c) == git_ref_filter (nullopt, c, false));
+ assert (git_ref_filter (n + '@' + c) == git_ref_filter (n, c, false));
assert (parse_git_ref_filters (nullopt) ==
git_ref_filters {git_ref_filter ()});