Teach overrides to understand embedded port versions
MODIFY
include/vcpkg/versiondeserializers.h
include/vcpkg/versiondeserializers.h
@@ -12,7 +12,6 @@ namespace vcpkg
{
Json::IDeserializer<VersionT>& get_versiont_deserializer_instance();
Json::IDeserializer<VersionT>& get_versiontag_deserializer_instance();
- std::unique_ptr<Json::IDeserializer<std::string>> make_version_deserializer(StringLiteral type_name);
struct SchemedVersion
{
@@ -25,12 +24,10 @@ namespace vcpkg
VersionT versiont;
};
- Optional<SchemedVersion> visit_optional_schemed_deserializer(StringView parent_type,
- Json::Reader& r,
- const Json::Object& obj);
SchemedVersion visit_required_schemed_deserializer(StringView parent_type,
Json::Reader& r,
- const Json::Object& obj);
+ const Json::Object& obj,
+ bool allow_hash_portversion = false);
View<StringView> schemed_deserializer_fields();
void serialize_schemed_version(Json::Object& out_obj,

MODIFY
src/vcpkg-test/manifests.cpp
src/vcpkg-test/manifests.cpp
@@ -102,12 +102,12 @@ TEST_CASE ("manifest versioning", "[manifests]")
for (auto v : data)
{
auto m_pgh = test_parse_manifest(std::get<0>(v));
-
REQUIRE(m_pgh.has_value());
auto& pgh = **m_pgh.get();
- REQUIRE(Json::stringify(serialize_manifest(pgh), Json::JsonStyle::with_spaces(4)) == std::get<0>(v));
- REQUIRE(pgh.core_paragraph->version_scheme == std::get<1>(v));
- REQUIRE(pgh.core_paragraph->version == std::get<2>(v));
+ CHECK(Json::stringify(serialize_manifest(pgh), Json::JsonStyle::with_spaces(4)) == std::get<0>(v));
+ CHECK(pgh.core_paragraph->version_scheme == std::get<1>(v));
+ CHECK(pgh.core_paragraph->version == std::get<2>(v));
+ CHECK(pgh.core_paragraph->port_version == 0);
}
test_parse_manifest(R"json({
@@ -211,7 +211,7 @@ TEST_CASE ("manifest constraints hash", "[manifests]")
true);
}
-TEST_CASE ("manifest overrides error hash", "[manifests]")
+TEST_CASE ("manifest overrides embedded port version", "[manifests]")
{
test_parse_manifest(R"json({
"name": "zlib",
@@ -219,7 +219,8 @@ TEST_CASE ("manifest overrides error hash", "[manifests]")
"overrides": [
{
"name": "d",
- "version-string": "abcd#1"
+ "version-string": "abcd#1",
+ "port-version": 1
}
]
})json",
@@ -230,7 +231,8 @@ TEST_CASE ("manifest overrides error hash", "[manifests]")
"overrides": [
{
"name": "d",
- "version-date": "2018-01-01#1"
+ "version-date": "2018-01-01#1",
+ "port-version": 1
}
]
})json",
@@ -241,7 +243,8 @@ TEST_CASE ("manifest overrides error hash", "[manifests]")
"overrides": [
{
"name": "d",
- "version": "1.2#1"
+ "version": "1.2#1",
+ "port-version": 1
}
]
})json",
@@ -252,11 +255,65 @@ TEST_CASE ("manifest overrides error hash", "[manifests]")
"overrides": [
{
"name": "d",
- "version-semver": "1.2#1"
+ "version-semver": "1.2.0#1",
+ "port-version": 1
}
]
})json",
true);
+
+ CHECK(unwrap(test_parse_manifest(R"json({
+ "name": "zlib",
+ "version-string": "abcd",
+ "overrides": [
+ {
+ "name": "d",
+ "version-string": "abcd#1"
+ }
+ ]
+})json",
+ false))
+ ->core_paragraph->overrides.at(0)
+ .port_version == 1);
+ CHECK(unwrap(test_parse_manifest(R"json({
+ "name": "zlib",
+ "version-string": "abcd",
+ "overrides": [
+ {
+ "name": "d",
+ "version-date": "2018-01-01#1"
+ }
+ ]
+})json",
+ false))
+ ->core_paragraph->overrides.at(0)
+ .port_version == 1);
+ CHECK(unwrap(test_parse_manifest(R"json({
+ "name": "zlib",
+ "version-string": "abcd",
+ "overrides": [
+ {
+ "name": "d",
+ "version": "1.2#1"
+ }
+ ]
+})json",
+ false))
+ ->core_paragraph->overrides.at(0)
+ .port_version == 1);
+ CHECK(unwrap(test_parse_manifest(R"json({
+ "name": "zlib",
+ "version-string": "abcd",
+ "overrides": [
+ {
+ "name": "d",
+ "version-semver": "1.2.0#1"
+ }
+ ]
+})json",
+ false))
+ ->core_paragraph->overrides.at(0)
+ .port_version == 1);
}
TEST_CASE ("manifest constraints", "[manifests]")

MODIFY
src/vcpkg/help.cpp
src/vcpkg/help.cpp
@@ -51,9 +51,6 @@ namespace vcpkg::Help
"your project from within your manifest file.");
tbl.blank();
tbl.blank();
- tbl.text("** This feature is experimental and requires `--feature-flags=versions` **");
- tbl.blank();
- tbl.blank();
tbl.header("Versions in vcpkg come in four primary flavors");
tbl.format("version", "A dot-separated sequence of numbers (1.2.3.4)");
tbl.format("version-date", "A date (2021-01-01.5)");

MODIFY
src/vcpkg/sourceparagraph.cpp
src/vcpkg/sourceparagraph.cpp
@@ -566,7 +566,7 @@ namespace vcpkg
{
r.required_object_field(type_name, obj, NAME, name, Json::IdentifierDeserializer::instance);
- auto schemed_version = visit_required_schemed_deserializer(type_name, r, obj);
+ auto schemed_version = visit_required_schemed_deserializer(type_name, r, obj, true);
version = schemed_version.versiont.text();
version_scheme = schemed_version.scheme;
port_version = schemed_version.versiont.port_version();
@@ -932,9 +932,11 @@ namespace vcpkg
static Json::StringDeserializer url_deserializer{"a url"};
- constexpr static StringView inner_type_name = "vcpkg.json";
- DependencyOverrideDeserializer::visit_impl(
- inner_type_name, r, obj, spgh->name, spgh->version, spgh->version_scheme, spgh->port_version);
+ r.required_object_field(type_name(), obj, NAME, spgh->name, Json::IdentifierDeserializer::instance);
+ auto schemed_version = visit_required_schemed_deserializer(type_name(), r, obj, false);
+ spgh->version = schemed_version.versiont.text();
+ spgh->version_scheme = schemed_version.scheme;
+ spgh->port_version = schemed_version.versiont.port_version();
r.optional_object_field(obj, MAINTAINERS, spgh->maintainers, Json::ParagraphDeserializer::instance);
r.optional_object_field(obj, DESCRIPTION, spgh->description, Json::ParagraphDeserializer::instance);

MODIFY
src/vcpkg/versiondeserializers.cpp
src/vcpkg/versiondeserializers.cpp
@@ -14,14 +14,37 @@ namespace
constexpr StringLiteral VERSION_DATE = "version-date";
constexpr StringLiteral PORT_VERSION = "port-version";
- struct VersionDeserializer final : Json::IDeserializer<std::string>
+ struct VersionDeserializer final : Json::IDeserializer<std::pair<std::string, Optional<int>>>
{
- VersionDeserializer(StringLiteral type) : m_type(type) { }
+ VersionDeserializer(StringLiteral type, bool allow_hash_portversion)
+ : m_type(type), m_allow_hash_portversion(allow_hash_portversion)
+ {
+ }
StringView type_name() const override { return m_type; }
- Optional<std::string> visit_string(Json::Reader& r, StringView sv) override
+ Optional<std::pair<std::string, Optional<int>>> visit_string(Json::Reader& r, StringView sv) override
+ {
+ auto it = std::find(sv.begin(), sv.end(), '#');
+ StringView pv(it, sv.end());
+ std::pair<std::string, Optional<int>> ret{std::string(sv.begin(), it), nullopt};
+ if (m_allow_hash_portversion)
+ {
+ if (pv.size() == 1)
+ {
+ r.add_generic_error(type_name(), "'#' in version text must be followed by a port version");
+ }
+ else if (pv.size() > 1)
+ {
+ ret.second = Strings::strto<int>(pv.substr(1).to_string());
+ if (ret.second.value_or(-1) < 0)
+ {
+ r.add_generic_error(type_name(),
+ "port versions after '#' in version text must be non-negative integers");
+ }
+ }
+ }
+ else
{
- 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");
@@ -33,9 +56,11 @@ namespace
pv.substr(1),
"?");
}
- return sv.to_string();
+ }
+ return std::move(ret);
}
StringLiteral m_type;
+ bool m_allow_hash_portversion;
};
struct GenericVersionTDeserializer : Json::IDeserializer<VersionT>
@@ -45,15 +70,15 @@ namespace
Optional<VersionT> visit_object(Json::Reader& r, const Json::Object& obj) override
{
- std::string version;
+ std::pair<std::string, Optional<int>> version;
int port_version = 0;
- static VersionDeserializer version_deserializer{"version"};
+ static VersionDeserializer version_deserializer{"version", false};
r.required_object_field(type_name(), obj, m_version_field, version, version_deserializer);
r.optional_object_field(obj, PORT_VERSION, port_version, Json::NaturalNumberDeserializer::instance);
- return VersionT{std::move(version), port_version};
+ return VersionT{std::move(version.first), port_version};
}
StringLiteral m_version_field;
};
@@ -61,45 +86,33 @@ namespace
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())
- {
- 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,
+ static Optional<SchemedVersion> visit_optional_schemed_deserializer(StringView parent_type,
Json::Reader& r,
- const Json::Object& obj)
+ const Json::Object& obj,
+ bool allow_hash_portversion)
{
Versions::Scheme version_scheme = Versions::Scheme::String;
- std::string version;
- int port_version = 0;
+ std::pair<std::string, Optional<int>> version;
- 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"};
+ VersionDeserializer version_exact_deserializer{"an exact version string", allow_hash_portversion};
+ VersionDeserializer version_relaxed_deserializer{"a relaxed version string", allow_hash_portversion};
+ VersionDeserializer version_semver_deserializer{"a semantic version string", allow_hash_portversion};
+ VersionDeserializer version_date_deserializer{"a date version string", allow_hash_portversion};
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;
+ int port_version = version.second.value_or(0);
bool has_port_version =
r.optional_object_field(obj, PORT_VERSION, port_version, Json::NaturalNumberDeserializer::instance);
+ if (has_port_version && version.second)
+ {
+ r.add_generic_error(parent_type, "\"port_version\" cannot be combined with an embedded '#' in the version");
+ }
+
if (num_versions == 0)
{
if (!has_port_version)
@@ -124,7 +137,7 @@ namespace vcpkg
else if (has_relax)
{
version_scheme = Versions::Scheme::Relaxed;
- auto v = Versions::RelaxedVersion::from_string(version);
+ auto v = Versions::RelaxedVersion::from_string(version.first);
if (!v.has_value())
{
r.add_generic_error(parent_type, "'version' text was not a relaxed version:\n", v.error());
@@ -133,7 +146,7 @@ namespace vcpkg
else if (has_semver)
{
version_scheme = Versions::Scheme::Semver;
- auto v = Versions::SemanticVersion::from_string(version);
+ auto v = Versions::SemanticVersion::from_string(version.first);
if (!v.has_value())
{
r.add_generic_error(parent_type, "'version-semver' text was not a semantic version:\n", v.error());
@@ -142,7 +155,7 @@ namespace vcpkg
else if (has_date)
{
version_scheme = Versions::Scheme::Date;
- auto v = Versions::DateVersion::from_string(version);
+ auto v = Versions::DateVersion::from_string(version.first);
if (!v.has_value())
{
r.add_generic_error(parent_type, "'version-date' text was not a date version:\n", v.error());
@@ -154,7 +167,24 @@ namespace vcpkg
}
}
- return SchemedVersion(version_scheme, VersionT{version, port_version});
+ return SchemedVersion(version_scheme, VersionT{std::move(version.first), port_version});
+ }
+
+ SchemedVersion visit_required_schemed_deserializer(StringView parent_type,
+ Json::Reader& r,
+ const Json::Object& obj,
+ bool allow_hash_portversion)
+ {
+ auto maybe_schemed_version = visit_optional_schemed_deserializer(parent_type, r, obj, allow_hash_portversion);
+ if (auto p = maybe_schemed_version.get())
+ {
+ return std::move(*p);
+ }
+ else
+ {
+ r.add_generic_error(parent_type, "expected a versioning field (example: ", VERSION_STRING, ")");
+ return {};
+ }
}
View<StringView> schemed_deserializer_fields()

Teach overrides to understand embedded port versions (#129)