diff options
-rw-r--r-- | libbpkg/manifest.cxx | 107 | ||||
-rw-r--r-- | libbpkg/manifest.hxx | 26 | ||||
-rw-r--r-- | tests/manifest/testscript | 107 |
3 files changed, 227 insertions, 13 deletions
diff --git a/libbpkg/manifest.cxx b/libbpkg/manifest.cxx index d9e8299..35c1217 100644 --- a/libbpkg/manifest.cxx +++ b/libbpkg/manifest.cxx @@ -3063,14 +3063,117 @@ namespace bpkg { using std::string; + // We will use the dependency alternatives parser to parse the + // `<name> [<version-constraint>] [<reflect-config>]` representation into + // a temporary dependency alternatives object. Then we will verify that + // the result has no multiple alternatives/dependency packages and + // unexpected clauses and will move the required information (dependency, + // reflection, etc) into the being created test dependency object. + + // 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. + // + if (da.enable) + throw invalid_argument ("unexpected enable clause"); + + // Move the dependency and the reflect clause into the being created test + // dependency object. + // + static_cast<dependency&> (*this) = move (da[0]); + + reflect = move (da.reflect); + } + + string test_dependency:: + string () const + { + std::string r (buildtime + ? "* " + dependency::string () + : dependency::string ()); + + if (reflect) + { + r += ' '; + r += *reflect; + } + + return r; } // pkg_package_manifest diff --git a/libbpkg/manifest.hxx b/libbpkg/manifest.hxx index 6428e3a..94206f3 100644 --- a/libbpkg/manifest.hxx +++ b/libbpkg/manifest.hxx @@ -1021,27 +1021,31 @@ namespace bpkg { test_dependency_type type; bool buildtime; + 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> r) + : dependency {std::move (n), std::move (c)}, + type (t), + buildtime (b), + 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>] [<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; }; class LIBBPKG_EXPORT package_manifest diff --git a/tests/manifest/testscript b/tests/manifest/testscript index 100f47d..50e7e51 100644 --- a/tests/manifest/testscript +++ b/tests/manifest/testscript @@ -3066,6 +3066,113 @@ 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 + + : 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 + } + + : 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 + + : enable + : + $* <<EOI 2>>EOE != 0 + : 1 + name: foo + version: 2.0.0 + summary: Modern C++ parser + license: LGPLv2 + tests: bar ? (windows) + EOI + stdin:6:8: error: unexpected enable clause + EOE } } |