aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2023-09-28 22:15:02 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2023-10-02 16:40:22 +0300
commit523168b187b55085ff47064585838d321eb724a5 (patch)
tree5bf7934d583d01fbbed876b333921d9af4ce58a4
parent9f632c7e160639ca7a11fe1ac48598b67e36652e (diff)
Add support for *-build-*email package manifest values and their overrides
-rw-r--r--libbpkg/manifest.cxx261
-rw-r--r--libbpkg/manifest.hxx58
-rw-r--r--tests/manifest/testscript79
-rw-r--r--tests/overrides/testscript80
4 files changed, 434 insertions, 44 deletions
diff --git a/libbpkg/manifest.cxx b/libbpkg/manifest.cxx
index aeb9b0e..559e1c3 100644
--- a/libbpkg/manifest.cxx
+++ b/libbpkg/manifest.cxx
@@ -3557,10 +3557,21 @@ namespace bpkg
};
// Return the package build configuration with the specified name, if
- // already exists, or the newly created configuration otherwise.
- //
- auto build_conf = [&m] (string&& nm) -> build_package_config&
- {
+ // already exists. If no configuration matches, then create one, if
+ // requested, and throw manifest_parsing otherwise. If the new
+ // configuration creation is not allowed, then the description for a
+ // potential manifest_parsing exception needs to also be specified.
+ //
+ auto build_conf = [&m, &bad_name] (string&& nm,
+ bool create = true,
+ const string& desc = "")
+ -> build_package_config&
+ {
+ // The error description must only be specified if the creation of the
+ // package build configuration is not allowed.
+ //
+ assert (desc.empty () == create);
+
small_vector<build_package_config, 1>& cs (m.build_configs);
auto i (find_if (cs.begin (), cs.end (),
@@ -3570,6 +3581,9 @@ namespace bpkg
if (i != cs.end ())
return *i;
+ if (!create)
+ bad_name (desc + ": no build package configuration '" + nm + '\'');
+
// Add the new build configuration (arguments, builds, etc will come
// later).
//
@@ -3601,6 +3615,16 @@ namespace bpkg
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 ())
@@ -4033,6 +4057,24 @@ namespace bpkg
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" ||
@@ -4387,6 +4429,59 @@ namespace bpkg
}
}
+ // 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 = [&name,
+ &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"),
+ name,
+ 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.
//
@@ -4712,14 +4807,22 @@ namespace bpkg
// The first {build-*email} override value.
//
- const manifest_name_value* be (nullptr);
+ const manifest_name_value* cbe (nullptr);
+
+ // The first {*-build-*email} override value.
+ //
+ const manifest_name_value* pbe (nullptr);
- // List of indexes of the overridden build configurations together with
- // flags which indicate if the *-builds override value was encountered for
- // this configuration.
+ // List of indexes of the build configurations with the overridden build
+ // constraints together with flags which indicate if the *-builds override
+ // value was encountered for this configuration.
//
vector<pair<size_t, bool>> obcs;
+ // List of indexes of the build configurations with the overridden emails.
+ //
+ vector<size_t> obes;
+
// Apply overrides.
//
for (const manifest_name_value& nv: nvs)
@@ -4768,7 +4871,8 @@ namespace bpkg
// otherwise.
//
// The n argument specifies the length of the configuration name in
- // *-build-config, *-builds, and *-build-{include,exclude} values.
+ // *-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&
@@ -4869,19 +4973,77 @@ namespace bpkg
return r;
};
- // Reset the {build-*email} value group on the first call.
+ // Reset the {build-*email} value group on the first call but throw if
+ // any of the {*-build-*email} override values are already encountered.
//
- auto reset_build_emails = [&be, &nv, &m] ()
+ auto reset_build_emails = [&cbe, &pbe, &nv, &bad_name, &m] ()
{
- if (be == nullptr)
+ if (cbe == nullptr)
{
+ if (pbe != nullptr)
+ bad_name ('\'' + nv.name + "' override specified together with '" +
+ pbe->name + "' override");
+
m.build_email = nullopt;
m.build_warning_email = nullopt;
m.build_error_email = nullopt;
- be = &nv;
+ cbe = &nv;
}
};
+ // Return the reference to the package build configuration which matches
+ // the build config-specific emails group value override, if exists. If
+ // no configuration matches, then throw manifest_parsing, except for the
+ // validate-only mode in which case just add an empty configuration with
+ // this name and return the reference to it.
+ //
+ 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;
+ };
+
const string& n (nv.name);
if (n == "builds")
@@ -4954,6 +5116,29 @@ namespace bpkg
reset_build_emails ();
m.build_error_email = parse_email (nv, "build error", name);
}
+ else if (n.size () > 12 &&
+ n.compare (n.size () - 12, 12, "-build-email") == 0)
+ {
+ build_package_config& bc (build_conf_email (n.size () - 12));
+
+ bc.email = parse_email (
+ nv, "build configuration", name, true /* empty */);
+ }
+ else if (n.size () > 20 &&
+ n.compare (n.size () - 20, 20, "-build-warning-email") == 0)
+ {
+ build_package_config& bc (build_conf_email (n.size () - 20));
+
+ bc.warning_email = parse_email (
+ nv, "build configuration warning", name);
+ }
+ else if (n.size () > 18 &&
+ n.compare (n.size () - 18, 18, "-build-error-email") == 0)
+ {
+ build_package_config& bc (build_conf_email (n.size () - 18));
+
+ bc.error_email = parse_email (nv, "build configuration error", name);
+ }
else
bad_name ("cannot override '" + n + "' value");
}
@@ -4964,8 +5149,8 @@ namespace bpkg
assert (cbc == nullptr || pbc == nullptr);
// Now, if not in the validate-only mode, as all the potential build
- // constraint overrides are applied, perform the final adjustments to the
- // build config constraints.
+ // constraint/email overrides are applied, perform the final adjustments
+ // to the build config constraints/emails.
//
if (!validate_only)
{
@@ -4993,6 +5178,30 @@ namespace bpkg
}
}
}
+
+ 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;
+ }
+ }
+ }
}
}
@@ -5303,10 +5512,6 @@ namespace bpkg
for (const build_package_config& bc: m.build_configs)
{
- if (!bc.arguments.empty () || !bc.comment.empty ())
- s.next (bc.name + "-build-config",
- serializer::merge_comment (bc.arguments, bc.comment));
-
if (!bc.builds.empty ())
{
string n (bc.name + "-builds");
@@ -5326,6 +5531,24 @@ namespace bpkg
: c.config + '/' + *c.target,
c.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);
diff --git a/libbpkg/manifest.hxx b/libbpkg/manifest.hxx
index dce0870..8439cdf 100644
--- a/libbpkg/manifest.hxx
+++ b/libbpkg/manifest.hxx
@@ -980,12 +980,15 @@ namespace bpkg
return os << bce.string ();
}
- // Package build configuration. Includes comment and optional target build
- // configuration class expressions/constraints overrides.
+ // Package build configuration. Includes comment and optional overrides for
+ // target build configuration class expressions/constraints and build
+ // notification emails.
//
class build_package_config
{
public:
+ using email_type = bpkg::email;
+
std::string name;
// Whitespace separated list of potentially double/single-quoted package
@@ -999,6 +1002,10 @@ namespace bpkg
butl::small_vector<build_class_expr, 1> builds;
std::vector<build_constraint> constraints;
+ butl::optional<email_type> email;
+ butl::optional<email_type> warning_email;
+ butl::optional<email_type> error_email;
+
build_package_config () = default;
// Built incrementally.
@@ -1024,6 +1031,31 @@ namespace bpkg
{
return !builds.empty () || !constraints.empty () ? constraints : 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
@@ -1330,14 +1362,15 @@ namespace bpkg
// {builds, build-{include,exclude}}
// {*-builds, *-build-{include,exclude}}
// {*-build-config}
+ // {*-build-*email}
//
// Throw manifest_parsing if the configuration specified by the build
- // package configuration-specific build constraints group value overrides
- // 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 constraints overrides for such a newly added configuration must
- // follow the respective *-build-config override.
+ // package configuration-specific build constraints or emails group value
+ // overrides 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 constraints and emails 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
@@ -1351,6 +1384,15 @@ namespace bpkg
// constraints are overridden, then for the remaining configs the build
// constraints are reset to `builds: none`.
//
+ // Similar to the build constraints groups, the common and build
+ // config-specific build emails group value overrides are mutually
+ // exclusive. If the common build emails are overridden, then all the
+ // build config-specific emails are reset to nullopt. Otherwise, if some
+ // build config-specific emails are overridden, then for the remaining
+ // configs the email is reset to the empty value and the warning and error
+ // emails are reset to nullopt (which effectively disables email
+ // notifications for such configurations).
+ //
// If a non-empty source name is specified, then the specified values are
// assumed to also include the line/column information and the possibly
// thrown manifest_parsing exception will contain the invalid value's
diff --git a/tests/manifest/testscript b/tests/manifest/testscript
index 7d808e3..3179005 100644
--- a/tests/manifest/testscript
+++ b/tests/manifest/testscript
@@ -1060,8 +1060,8 @@
version: 2.0.0
summary: Modern C++ parser
license: LGPLv2
- bar-build-config: config.foo.bar = true; Bar.
bar-builds: all
+ bar-build-config: config.foo.bar = true; Bar.
baz-build-config: config.foo.baz = true; Baz.
EOF
}
@@ -1111,6 +1111,81 @@
bar-builds: all
EOI
}
+
+ : 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
@@ -4447,10 +4522,10 @@
build-include: linux*
build-include: freebsd*
build-exclude: *; Only supports Linux and FreeBSD.
- network-build-config: config.libfoo.network=true; Enable networking API.
network-builds: default
network-build-include: linux*
network-build-exclude: *; Only supports Linux.
+ network-build-config: config.libfoo.network=true; Enable networking API.
bootstrap-build:\
project = libfoo
diff --git a/tests/overrides/testscript b/tests/overrides/testscript
index 07c1451..ba66b7f 100644
--- a/tests/overrides/testscript
+++ b/tests/overrides/testscript
@@ -15,6 +15,10 @@
build-email: foo@example.com
build-error-email: error@example.com
build-warning-email: warning@example.com
+ network-build-config: config.libfoo.network=true
+ network-build-email: network-foo@example.com
+ network-build-error-email: network-error@example.com
+ network-build-warning-email: network-warning@example.com
EOI
: 1
name: libfoo
@@ -22,6 +26,7 @@
summary: Modern C++ parser
license: LGPLv2
build-email: bar@example.com
+ network-build-config: config.libfoo.network=true
EOO
: builds
@@ -99,6 +104,8 @@
: 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' \
@@ -110,63 +117,80 @@
version: 2.0.0
summary: Modern C++ parser
license: LGPLv2
+ build-email: foo@example.com
+ build-error-email: error@example.com
+ build-warning-email: warning@example.com
builds: all
build-include: linux*
build-include: macos*
build-include: freebsd*
build-exclude: *
- network-build-config: config.libfoo.network=true
network-builds: default
network-build-include: linux*
network-build-exclude: *
- cache-build-config: config.libfoo.cache=true
+ network-build-config: config.libfoo.network=true
+ network-build-error-email: network-error@example.com
cache-builds: default
cache-build-include: macos*
cache-build-exclude: *
- sys-build-config: ?sys:libcrypto
+ cache-build-config: config.libfoo.cache=true
+ cache-build-email: cache@example.com
sys-builds: default
sys-build-include: freebsd*
sys-build-exclude: *
- older-build-config: ?libbar/1.0.0
+ sys-build-config: ?sys:libcrypto
+ sys-build-email: sys@example.com
older-builds: default
older-build-include: windows*
older-build-exclude: *
- fancy-build-config: config.libfoo.fancy=true
+ older-build-config: ?libbar/1.0.0
fancy-builds: default
fancy-build-include: windows*
fancy-build-exclude: *
+ fancy-build-config: config.libfoo.fancy=true
EOI
: 1
name: libfoo
version: 2.0.0
summary: Modern C++ parser
license: LGPLv2
+ build-email: foo@example.com
+ build-warning-email: warning@example.com
+ build-error-email: error@example.com
builds: all
build-include: linux*
build-include: macos*
build-include: freebsd*
build-exclude: *
default-builds: none
- network-build-config: config.libfoo.network=true
+ default-build-email:
network-builds: all
network-build-include: windows*
network-build-exclude: *
- cache-build-config: config.libfoo.cache=true config.libfoo.buffer=1028
+ network-build-config: config.libfoo.network=true
+ network-build-warning-email: network-warning@example.com
cache-builds: legacy
cache-build-include: freebsd*
cache-build-exclude: *
- sys-build-config: ?sys:libcrypto
+ cache-build-config: config.libfoo.cache=true config.libfoo.buffer=1028
+ cache-build-error-email: cache-error@example.com
sys-builds: default
sys-build-include: linux*
sys-build-exclude: *
- older-build-config: ?libbar/1.0.0
+ sys-build-config: ?sys:libcrypto
+ sys-build-email:
older-builds: none
- fancy-build-config: config.libfoo.fancy=true
+ older-build-config: ?libbar/1.0.0
+ older-build-email:
fancy-builds: gcc
- deprecated-api-build-config: config.libfoo.deprecated_api=true
+ fancy-build-config: config.libfoo.fancy=true
+ fancy-build-email:
deprecated-api-builds: windows
- experimental-api-build-config: config.libfoo.experimental_api=true
+ 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
@@ -177,8 +201,8 @@
version: 2.0.0
summary: Modern C++ parser
license: LGPLv2
- network-build-config: config.libfoo.network=true
network-builds: all
+ network-build-config: config.libfoo.network=true
EOI
: 1
name: libfoo
@@ -188,8 +212,8 @@
default-builds: all
default-build-include: windows*
default-build-exclude: *
- network-build-config: config.libfoo.network=true
network-builds: none
+ network-build-config: config.libfoo.network=true
EOO
: add-build-config
@@ -208,8 +232,8 @@
version: 2.0.0
summary: Modern C++ parser
license: LGPLv2
- network-build-config: config.libfoo.network=true
network-builds: all
+ network-build-config: config.libfoo.network=true
experimental-api-build-config: config.libfoo.experimental_api=true
EOO
@@ -345,4 +369,30 @@
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
}