diff options
| author | ras0219 <533828+ras0219@users.noreply.github.com> | 2020-12-04 12:57:13 -0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-12-04 12:57:13 -0800 |
| commit | 60aa143e59687ed4b689d24d80a6f93ae123da86 (patch) | |
| tree | da874cbe00f8c14a47a71801df9045381aed7a96 /toolsrc/src | |
| parent | 99cfc38036f330a035b57aa67f661526153e31c7 (diff) | |
| download | vcpkg-60aa143e59687ed4b689d24d80a6f93ae123da86.tar.gz vcpkg-60aa143e59687ed4b689d24d80a6f93ae123da86.zip | |
[vcpkg] Error on '#' in version strings to avoid confusion (#14927)
* [vcpkg] Refactor deserializers to reduce duplicate functionality
* [vcpkg] Error on '#' in version strings to avoid confusion with port-version
* [vcpkg] Improve error message
* [vcpkg] Reorder field output
* [vcpkg] Fix tests
Co-authored-by: Robert Schumacher <roschuma@microsoft.com>
Diffstat (limited to 'toolsrc/src')
| -rw-r--r-- | toolsrc/src/vcpkg-test/manifests.cpp | 105 | ||||
| -rw-r--r-- | toolsrc/src/vcpkg/commands.porthistory.cpp | 6 | ||||
| -rw-r--r-- | toolsrc/src/vcpkg/sourceparagraph.cpp | 114 | ||||
| -rw-r--r-- | toolsrc/src/vcpkg/versiondeserializers.cpp | 209 |
4 files changed, 280 insertions, 154 deletions
diff --git a/toolsrc/src/vcpkg-test/manifests.cpp b/toolsrc/src/vcpkg-test/manifests.cpp index 32aa11f8a..f03caf3bf 100644 --- a/toolsrc/src/vcpkg-test/manifests.cpp +++ b/toolsrc/src/vcpkg-test/manifests.cpp @@ -115,6 +115,103 @@ TEST_CASE ("manifest versioning", "[manifests]") "version-semver": "1.2.3-rc3" })json", true); + + test_parse_manifest(R"json({ + "name": "zlib", + "version-string": "abcd#1" + })json", + true); + test_parse_manifest(R"json({ + "name": "zlib", + "version": "abcd#1" + })json", + true); + test_parse_manifest(R"json({ + "name": "zlib", + "version-date": "abcd#1" + })json", + true); + test_parse_manifest(R"json({ + "name": "zlib", + "version-semver": "abcd#1" + })json", + true); +} + +TEST_CASE ("manifest constraints error hash", "[manifests]") +{ + test_parse_manifest(R"json({ + "name": "zlib", + "version-string": "abcd", + "dependencies": [ + { + "name": "b", + "version=": "5#1" + } + ] +} +)json", + true); + + test_parse_manifest(R"json({ + "name": "zlib", + "version-string": "abcd", + "dependencies": [ + { + "name": "d", + "version>=": "2018-09-01#1" + } + ] +})json", + true); +} + +TEST_CASE ("manifest overrides error hash", "[manifests]") +{ + test_parse_manifest(R"json({ + "name": "zlib", + "version-string": "abcd", + "overrides": [ + { + "name": "d", + "version-string": "abcd#1" + } + ] +})json", + true); + test_parse_manifest(R"json({ + "name": "zlib", + "version-string": "abcd", + "overrides": [ + { + "name": "d", + "version-date": "2018-01-01#1" + } + ] +})json", + true); + test_parse_manifest(R"json({ + "name": "zlib", + "version-string": "abcd", + "overrides": [ + { + "name": "d", + "version": "1.2#1" + } + ] +})json", + true); + test_parse_manifest(R"json({ + "name": "zlib", + "version-string": "abcd", + "overrides": [ + { + "name": "d", + "version-semver": "1.2#1" + } + ] +})json", + true); } TEST_CASE ("manifest constraints", "[manifests]") @@ -286,13 +383,13 @@ TEST_CASE ("manifest overrides", "[manifests]") "overrides": [ { "name": "abc", - "port-version": 5, - "version-string": "hello" + "version-string": "hello", + "port-version": 5 }, { "name": "abcd", - "port-version": 7, - "version-string": "hello" + "version-string": "hello", + "port-version": 7 } ] } diff --git a/toolsrc/src/vcpkg/commands.porthistory.cpp b/toolsrc/src/vcpkg/commands.porthistory.cpp index 045562136..7efb36bd1 100644 --- a/toolsrc/src/vcpkg/commands.porthistory.cpp +++ b/toolsrc/src/vcpkg/commands.porthistory.cpp @@ -9,6 +9,7 @@ #include <vcpkg/tools.h> #include <vcpkg/vcpkgcmdarguments.h> #include <vcpkg/vcpkgpaths.h> +#include <vcpkg/versiondeserializers.h> #include <vcpkg/versions.h> namespace vcpkg::Commands::PortHistory @@ -230,8 +231,9 @@ namespace vcpkg::Commands::PortHistory { Json::Object object; object.insert("git-tree", Json::Value::string(version.git_tree)); - object.insert("version-string", Json::Value::string(version.version)); - object.insert("port-version", Json::Value::integer(version.port_version)); + + serialize_schemed_version( + object, Versions::Scheme::String, version.version, version.port_version, true); versions_json.push_back(std::move(object)); } diff --git a/toolsrc/src/vcpkg/sourceparagraph.cpp b/toolsrc/src/vcpkg/sourceparagraph.cpp index f05ebb619..f80a1494e 100644 --- a/toolsrc/src/vcpkg/sourceparagraph.cpp +++ b/toolsrc/src/vcpkg/sourceparagraph.cpp @@ -11,6 +11,7 @@ #include <vcpkg/sourceparagraph.h> #include <vcpkg/triplet.h> #include <vcpkg/vcpkgcmdarguments.h> +#include <vcpkg/versiondeserializers.h> namespace vcpkg { @@ -487,12 +488,12 @@ namespace vcpkg r.optional_object_field(obj, PLATFORM, dep.platform, PlatformExprDeserializer::instance); - static Json::StringDeserializer version_deserializer{"a version"}; + static auto version_deserializer = make_version_deserializer("a version"); auto has_eq_constraint = - r.optional_object_field(obj, VERSION_EQ, dep.constraint.value, version_deserializer); + r.optional_object_field(obj, VERSION_EQ, dep.constraint.value, *version_deserializer); auto has_ge_constraint = - r.optional_object_field(obj, VERSION_GE, dep.constraint.value, version_deserializer); + r.optional_object_field(obj, VERSION_GE, dep.constraint.value, *version_deserializer); auto has_port_ver = r.optional_object_field( obj, PORT_VERSION, dep.constraint.port_version, Json::NaturalNumberDeserializer::instance); @@ -548,23 +549,11 @@ namespace vcpkg virtual StringView type_name() const override { return "an override"; } constexpr static StringLiteral NAME = "name"; - constexpr static StringLiteral PORT_VERSION = "port-version"; - constexpr static StringLiteral VERSION_RELAXED = "version"; - constexpr static StringLiteral VERSION_SEMVER = "version-semver"; - constexpr static StringLiteral VERSION_DATE = "version-date"; - constexpr static StringLiteral VERSION_STRING = "version-string"; virtual Span<const StringView> valid_fields() const override { - static const StringView t[] = { - NAME, - PORT_VERSION, - VERSION_RELAXED, - VERSION_SEMVER, - VERSION_DATE, - VERSION_STRING, - }; - + static const StringView u[] = {NAME}; + static const auto t = Util::Vectors::concat<StringView>(schemed_deserializer_fields(), u); return t; } @@ -576,40 +565,12 @@ namespace vcpkg Versions::Scheme& version_scheme, int& port_version) { - static Json::StringDeserializer version_exact_deserializer{"an exact version string"}; - static Json::StringDeserializer version_relaxed_deserializer{"a relaxed version string"}; - static Json::StringDeserializer version_semver_deserializer{"a semantic version string"}; - static Json::StringDeserializer version_date_deserializer{"a date version string"}; - r.required_object_field(type_name, obj, NAME, name, Json::IdentifierDeserializer::instance); - bool has_exact = r.optional_object_field(obj, VERSION_STRING, version, version_exact_deserializer); - bool has_relax = r.optional_object_field(obj, VERSION_RELAXED, version, version_relaxed_deserializer); - bool has_semver = r.optional_object_field(obj, VERSION_SEMVER, version, version_semver_deserializer); - bool has_date = r.optional_object_field(obj, VERSION_DATE, version, version_date_deserializer); - int num_versions = (int)has_exact + (int)has_relax + (int)has_semver + (int)has_date; - if (num_versions == 0) - { - r.add_generic_error(type_name, "expected a versioning field (example: ", VERSION_STRING, ")"); - } - else if (num_versions > 1) - { - r.add_generic_error(type_name, "expected only one versioning field"); - } - else - { - if (has_exact) - version_scheme = Versions::Scheme::String; - else if (has_relax) - version_scheme = Versions::Scheme::Relaxed; - else if (has_semver) - version_scheme = Versions::Scheme::Semver; - else if (has_date) - version_scheme = Versions::Scheme::Date; - else - Checks::unreachable(VCPKG_LINE_INFO); - } - r.optional_object_field(obj, PORT_VERSION, port_version, Json::NaturalNumberDeserializer::instance); + auto schemed_version = visit_required_schemed_deserializer(type_name, r, obj); + version = schemed_version.versiont.text(); + version_scheme = schemed_version.scheme; + port_version = schemed_version.versiont.port_version(); } virtual Optional<DependencyOverride> visit_object(Json::Reader& r, const Json::Object& obj) override @@ -634,11 +595,6 @@ namespace vcpkg DependencyOverrideDeserializer DependencyOverrideDeserializer::instance; constexpr StringLiteral DependencyOverrideDeserializer::NAME; - constexpr StringLiteral DependencyOverrideDeserializer::VERSION_STRING; - constexpr StringLiteral DependencyOverrideDeserializer::VERSION_RELAXED; - constexpr StringLiteral DependencyOverrideDeserializer::VERSION_SEMVER; - constexpr StringLiteral DependencyOverrideDeserializer::VERSION_DATE; - constexpr StringLiteral DependencyOverrideDeserializer::PORT_VERSION; // reasoning for these two distinct types -- FeatureDeserializer and ArrayFeatureDeserializer: // `"features"` may be defined in one of two ways: @@ -909,15 +865,6 @@ namespace vcpkg virtual StringView type_name() const override { return "a manifest"; } constexpr static StringLiteral NAME = "name"; - - // Default is a relaxed semver-like version - constexpr static StringLiteral VERSION_RELAXED = "version"; - constexpr static StringLiteral VERSION_SEMVER = "version-semver"; - constexpr static StringLiteral VERSION_DATE = "version-date"; - // Legacy version string, accepts arbitrary string values. - constexpr static StringLiteral VERSION_STRING = "version-string"; - - constexpr static StringLiteral PORT_VERSION = "port-version"; constexpr static StringLiteral MAINTAINERS = "maintainers"; constexpr static StringLiteral DESCRIPTION = "description"; constexpr static StringLiteral HOMEPAGE = "homepage"; @@ -932,13 +879,8 @@ namespace vcpkg virtual Span<const StringView> valid_fields() const override { - static const StringView t[] = { + static const StringView u[] = { NAME, - VERSION_STRING, - VERSION_RELAXED, - VERSION_SEMVER, - VERSION_DATE, - PORT_VERSION, MAINTAINERS, DESCRIPTION, HOMEPAGE, @@ -951,6 +893,7 @@ namespace vcpkg SUPPORTS, OVERRIDES, }; + static const auto t = Util::Vectors::concat<StringView>(schemed_deserializer_fields(), u); return t; } @@ -1014,11 +957,6 @@ namespace vcpkg ManifestDeserializer ManifestDeserializer::instance; constexpr StringLiteral ManifestDeserializer::NAME; - constexpr StringLiteral ManifestDeserializer::VERSION_STRING; - constexpr StringLiteral ManifestDeserializer::VERSION_RELAXED; - constexpr StringLiteral ManifestDeserializer::VERSION_SEMVER; - constexpr StringLiteral ManifestDeserializer::VERSION_DATE; - constexpr StringLiteral ManifestDeserializer::PORT_VERSION; constexpr StringLiteral ManifestDeserializer::MAINTAINERS; constexpr StringLiteral ManifestDeserializer::DESCRIPTION; constexpr StringLiteral ManifestDeserializer::HOMEPAGE; @@ -1329,17 +1267,6 @@ namespace vcpkg } }; - auto version_field = [](Versions::Scheme version_scheme) { - switch (version_scheme) - { - case Versions::Scheme::String: return ManifestDeserializer::VERSION_STRING; - case Versions::Scheme::Semver: return ManifestDeserializer::VERSION_SEMVER; - case Versions::Scheme::Relaxed: return ManifestDeserializer::VERSION_RELAXED; - case Versions::Scheme::Date: return ManifestDeserializer::VERSION_DATE; - default: Checks::unreachable(VCPKG_LINE_INFO); - } - }; - auto serialize_override = [&](Json::Array& arr, const DependencyOverride& dep) { auto& dep_obj = arr.push_back(Json::Object()); for (const auto& el : dep.extra_info) @@ -1349,12 +1276,7 @@ namespace vcpkg dep_obj.insert(DependencyOverrideDeserializer::NAME, Json::Value::string(dep.name)); - if (dep.port_version != 0) - { - dep_obj.insert(DependencyDeserializer::PORT_VERSION, Json::Value::integer(dep.port_version)); - } - - dep_obj.insert(version_field(dep.version_scheme), Json::Value::string(dep.version)); + serialize_schemed_version(dep_obj, dep.version_scheme, dep.version, dep.port_version); }; Json::Object obj; @@ -1365,12 +1287,12 @@ namespace vcpkg } obj.insert(ManifestDeserializer::NAME, Json::Value::string(scf.core_paragraph->name)); - obj.insert(version_field(scf.core_paragraph->version_scheme), Json::Value::string(scf.core_paragraph->version)); - if (scf.core_paragraph->port_version != 0 || debug) - { - obj.insert(ManifestDeserializer::PORT_VERSION, Json::Value::integer(scf.core_paragraph->port_version)); - } + serialize_schemed_version(obj, + scf.core_paragraph->version_scheme, + scf.core_paragraph->version, + scf.core_paragraph->port_version, + debug); serialize_paragraph(obj, ManifestDeserializer::MAINTAINERS, scf.core_paragraph->maintainers); serialize_paragraph(obj, ManifestDeserializer::DESCRIPTION, scf.core_paragraph->description); diff --git a/toolsrc/src/vcpkg/versiondeserializers.cpp b/toolsrc/src/vcpkg/versiondeserializers.cpp index 276b70e4f..1557f4be9 100644 --- a/toolsrc/src/vcpkg/versiondeserializers.cpp +++ b/toolsrc/src/vcpkg/versiondeserializers.cpp @@ -1,71 +1,176 @@ +#include <vcpkg/base/util.h> + #include <vcpkg/versiondeserializers.h> using namespace vcpkg; using namespace vcpkg::Versions; +static constexpr StringLiteral VERSION_RELAXED = "version"; +static constexpr StringLiteral VERSION_SEMVER = "version-semver"; +static constexpr StringLiteral VERSION_STRING = "version-string"; +static constexpr StringLiteral VERSION_DATE = "version-date"; +static constexpr StringLiteral PORT_VERSION = "port-version"; + namespace { - struct VersionDbEntryDeserializer final : Json::IDeserializer<VersionDbEntry> + struct VersionDeserializer final : Json::IDeserializer<std::string> { - static constexpr StringLiteral VERSION_RELAXED = "version"; - static constexpr StringLiteral VERSION_SEMVER = "version-semver"; - static constexpr StringLiteral VERSION_STRING = "version-string"; - static constexpr StringLiteral VERSION_DATE = "version-date"; - static constexpr StringLiteral PORT_VERSION = "port-version"; - static constexpr StringLiteral GIT_TREE = "git-tree"; + VersionDeserializer(StringLiteral type) : m_type(type) { } + StringView type_name() const override { return m_type; } - StringView type_name() const override { return "a version database entry"; } - View<StringView> valid_fields() const override + Optional<std::string> visit_string(Json::Reader& r, StringView sv) override { - static const StringView t[] = { - VERSION_RELAXED, VERSION_SEMVER, VERSION_STRING, VERSION_DATE, PORT_VERSION, GIT_TREE}; - return t; + StringView pv(std::find(sv.begin(), sv.end(), '#'), sv.end()); + if (pv.size() == 1) + { + r.add_generic_error(type_name(), "invalid character '#' in version text"); + } + else if (pv.size() > 1) + { + r.add_generic_error(type_name(), + "invalid character '#' in version text. Did you mean \"port-version\": ", + pv.substr(1), + "?"); + } + return sv.to_string(); } + StringLiteral m_type; + }; +} - Optional<VersionDbEntry> visit_object(Json::Reader& r, const Json::Object& obj) override +namespace vcpkg +{ + std::unique_ptr<Json::IDeserializer<std::string>> make_version_deserializer(StringLiteral type_name) + { + return std::make_unique<VersionDeserializer>(type_name); + } + + SchemedVersion visit_required_schemed_deserializer(StringView parent_type, Json::Reader& r, const Json::Object& obj) + { + auto maybe_schemed_version = visit_optional_schemed_deserializer(parent_type, r, obj); + if (auto p = maybe_schemed_version.get()) { - std::string version; - int port_version = 0; - std::string git_tree; - Versions::Scheme version_scheme = Versions::Scheme::String; - - // Code copy-and-paste'd from sourceparagraph.cpp - static Json::StringDeserializer version_exact_deserializer{"an exact version string"}; - static Json::StringDeserializer version_relaxed_deserializer{"a relaxed version string"}; - static Json::StringDeserializer version_semver_deserializer{"a semantic version string"}; - static Json::StringDeserializer version_date_deserializer{"a date version string"}; - static Json::StringDeserializer git_tree_deserializer("a git object SHA"); + return std::move(*p); + } + else + { + r.add_generic_error(parent_type, "expected a versioning field (example: ", VERSION_STRING, ")"); + return {}; + } + } + Optional<SchemedVersion> visit_optional_schemed_deserializer(StringView parent_type, + Json::Reader& r, + const Json::Object& obj) + { + Versions::Scheme version_scheme = Versions::Scheme::String; + std::string version; + int port_version = 0; + + static VersionDeserializer version_exact_deserializer{"an exact version string"}; + static VersionDeserializer version_relaxed_deserializer{"a relaxed version string"}; + static VersionDeserializer version_semver_deserializer{"a semantic version string"}; + static VersionDeserializer version_date_deserializer{"a date version string"}; + + bool has_exact = r.optional_object_field(obj, VERSION_STRING, version, version_exact_deserializer); + bool has_relax = r.optional_object_field(obj, VERSION_RELAXED, version, version_relaxed_deserializer); + bool has_semver = r.optional_object_field(obj, VERSION_SEMVER, version, version_semver_deserializer); + bool has_date = r.optional_object_field(obj, VERSION_DATE, version, version_date_deserializer); + int num_versions = (int)has_exact + (int)has_relax + (int)has_semver + (int)has_date; + bool has_port_version = + r.optional_object_field(obj, PORT_VERSION, port_version, Json::NaturalNumberDeserializer::instance); - bool has_exact = r.optional_object_field(obj, VERSION_STRING, version, version_exact_deserializer); - bool has_relax = r.optional_object_field(obj, VERSION_RELAXED, version, version_relaxed_deserializer); - bool has_semver = r.optional_object_field(obj, VERSION_SEMVER, version, version_semver_deserializer); - bool has_date = r.optional_object_field(obj, VERSION_DATE, version, version_date_deserializer); - int num_versions = (int)has_exact + (int)has_relax + (int)has_semver + (int)has_date; - if (num_versions == 0) + if (num_versions == 0) + { + if (!has_port_version) { - r.add_generic_error(type_name(), "expected a versioning field (example: ", VERSION_STRING, ")"); + return nullopt; } - else if (num_versions > 1) + else { - r.add_generic_error(type_name(), "expected only one versioning field"); + r.add_generic_error(parent_type, "unexpected \"port_version\" without a versioning field"); } + } + else if (num_versions > 1) + { + r.add_generic_error(parent_type, "expected only one versioning field"); + } + else + { + if (has_exact) + version_scheme = Versions::Scheme::String; + else if (has_relax) + version_scheme = Versions::Scheme::Relaxed; + else if (has_semver) + version_scheme = Versions::Scheme::Semver; + else if (has_date) + version_scheme = Versions::Scheme::Date; else + Checks::unreachable(VCPKG_LINE_INFO); + } + + return SchemedVersion{version_scheme, {version, port_version}}; + } + + View<StringView> schemed_deserializer_fields() + { + static const StringView t[] = {VERSION_RELAXED, VERSION_SEMVER, VERSION_STRING, VERSION_DATE, PORT_VERSION}; + return t; + } + + void serialize_schemed_version(Json::Object& out_obj, + Versions::Scheme scheme, + const std::string& version, + int port_version, + bool always_emit_port_version) + { + auto version_field = [](Versions::Scheme version_scheme) { + switch (version_scheme) { - if (has_exact) - version_scheme = Versions::Scheme::String; - else if (has_relax) - version_scheme = Versions::Scheme::Relaxed; - else if (has_semver) - version_scheme = Versions::Scheme::Semver; - else if (has_date) - version_scheme = Versions::Scheme::Date; - else - Checks::unreachable(VCPKG_LINE_INFO); + case Versions::Scheme::String: return VERSION_STRING; + case Versions::Scheme::Semver: return VERSION_SEMVER; + case Versions::Scheme::Relaxed: return VERSION_RELAXED; + case Versions::Scheme::Date: return VERSION_DATE; + default: Checks::unreachable(VCPKG_LINE_INFO); } - r.optional_object_field(obj, PORT_VERSION, port_version, Json::NaturalNumberDeserializer::instance); - r.required_object_field(type_name(), obj, GIT_TREE, git_tree, git_tree_deserializer); + }; + + out_obj.insert(version_field(scheme), Json::Value::string(version)); - return VersionDbEntry(version, port_version, version_scheme, git_tree); + if (port_version != 0 || always_emit_port_version) + { + out_obj.insert(PORT_VERSION, Json::Value::integer(port_version)); + } + } + +} + +namespace +{ + struct VersionDbEntryDeserializer final : Json::IDeserializer<VersionDbEntry> + { + static constexpr StringLiteral GIT_TREE = "git-tree"; + + StringView type_name() const override { return "a version database entry"; } + View<StringView> valid_fields() const override + { + static const StringView u[] = {GIT_TREE}; + static const auto t = vcpkg::Util::Vectors::concat<StringView>(schemed_deserializer_fields(), u); + return t; + } + + Optional<VersionDbEntry> visit_object(Json::Reader& r, const Json::Object& obj) override + { + VersionDbEntry ret; + + auto schemed_version = visit_required_schemed_deserializer(type_name(), r, obj); + ret.scheme = schemed_version.scheme; + ret.version = std::move(schemed_version.versiont); + + static Json::StringDeserializer git_tree_deserializer("a git object SHA"); + + r.required_object_field(type_name(), obj, GIT_TREE, ret.git_tree, git_tree_deserializer); + + return std::move(ret); } static VersionDbEntryDeserializer instance; @@ -115,7 +220,7 @@ namespace StringView type_name() const override { return "a version object"; } View<StringView> valid_fields() const override { - static const StringView t[] = {"version-string", "port-version"}; + static const StringView t[] = {VERSION_STRING, PORT_VERSION}; return t; } @@ -124,16 +229,16 @@ namespace std::string version; int port_version = 0; - r.required_object_field(type_name(), obj, "version-string", version, version_deserializer); - r.optional_object_field(obj, "port-version", port_version, Json::NaturalNumberDeserializer::instance); + static VersionDeserializer version_deserializer{"version"}; + + r.required_object_field(type_name(), obj, VERSION_STRING, version, version_deserializer); + r.optional_object_field(obj, PORT_VERSION, port_version, Json::NaturalNumberDeserializer::instance); return VersionT{std::move(version), port_version}; } - - static Json::StringDeserializer version_deserializer; static VersionTDeserializer instance; }; - Json::StringDeserializer VersionTDeserializer::version_deserializer{"version"}; + VersionTDeserializer VersionTDeserializer::instance; } |
