aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorras0219 <533828+ras0219@users.noreply.github.com>2020-11-13 12:30:31 -0800
committerGitHub <noreply@github.com>2020-11-13 14:30:31 -0600
commit783763162901f32e6d6270aede22519844b8e9ce (patch)
treef955936fbd922a39aefba8faec6a40a0384d89c5
parent1d8755cb409f655d5135472970d1da0ce975681b (diff)
downloadvcpkg-783763162901f32e6d6270aede22519844b8e9ce.tar.gz
vcpkg-783763162901f32e6d6270aede22519844b8e9ce.zip
[vcpkg] Implement constraints and overrides in manifests (#14183)
* [vcpkg] Implement constraints in manifests * [vcpkg] Add SourceControlFile::check_against_feature_flags to prevent accidentally ignoring versioning fields * [vcpkg] Switch check_against_feature_flags to accept fs::path * [vcpkg] Implement overrides parsing in manifests * [vcpkg] Address CR comments Co-authored-by: Robert Schumacher <roschuma@microsoft.com>
-rw-r--r--toolsrc/include/vcpkg/packagespec.h28
-rw-r--r--toolsrc/include/vcpkg/sourceparagraph.h17
-rw-r--r--toolsrc/include/vcpkg/versions.h10
-rw-r--r--toolsrc/src/vcpkg-test/manifests.cpp202
-rw-r--r--toolsrc/src/vcpkg/install.cpp8
-rw-r--r--toolsrc/src/vcpkg/packagespec.cpp18
-rw-r--r--toolsrc/src/vcpkg/portfileprovider.cpp14
-rw-r--r--toolsrc/src/vcpkg/sourceparagraph.cpp299
8 files changed, 531 insertions, 65 deletions
diff --git a/toolsrc/include/vcpkg/packagespec.h b/toolsrc/include/vcpkg/packagespec.h
index a362d8068..d45fb0182 100644
--- a/toolsrc/include/vcpkg/packagespec.h
+++ b/toolsrc/include/vcpkg/packagespec.h
@@ -6,6 +6,7 @@
#include <vcpkg/platform-expression.h>
#include <vcpkg/triplet.h>
+#include <vcpkg/versions.h>
namespace vcpkg::Parse
{
@@ -133,11 +134,25 @@ namespace vcpkg
static ExpectedS<Features> from_string(const std::string& input);
};
+ struct DependencyConstraint
+ {
+ Versions::Constraint::Type type = Versions::Constraint::Type::None;
+ std::string value;
+ int port_version = 0;
+
+ friend bool operator==(const DependencyConstraint& lhs, const DependencyConstraint& rhs);
+ friend bool operator!=(const DependencyConstraint& lhs, const DependencyConstraint& rhs)
+ {
+ return !(lhs == rhs);
+ }
+ };
+
struct Dependency
{
std::string name;
std::vector<std::string> features;
PlatformExpression::Expr platform;
+ DependencyConstraint constraint;
Json::Object extra_info;
@@ -145,6 +160,19 @@ namespace vcpkg
friend bool operator!=(const Dependency& lhs, const Dependency& rhs) { return !(lhs == rhs); }
};
+ struct DependencyOverride
+ {
+ std::string name;
+ Versions::Scheme version_scheme = Versions::Scheme::String;
+ std::string version;
+ int port_version = 0;
+
+ Json::Object extra_info;
+
+ friend bool operator==(const DependencyOverride& lhs, const DependencyOverride& rhs);
+ friend bool operator!=(const DependencyOverride& lhs, const DependencyOverride& rhs) { return !(lhs == rhs); }
+ };
+
struct ParsedQualifiedSpecifier
{
std::string name;
diff --git a/toolsrc/include/vcpkg/sourceparagraph.h b/toolsrc/include/vcpkg/sourceparagraph.h
index d88c0b0fd..2152e0237 100644
--- a/toolsrc/include/vcpkg/sourceparagraph.h
+++ b/toolsrc/include/vcpkg/sourceparagraph.h
@@ -2,6 +2,8 @@
#include <vcpkg/base/fwd/json.h>
+#include <vcpkg/fwd/vcpkgcmdarguments.h>
+
#include <vcpkg/base/expected.h>
#include <vcpkg/base/span.h>
#include <vcpkg/base/system.h>
@@ -63,6 +65,7 @@ namespace vcpkg
std::string homepage;
std::string documentation;
std::vector<Dependency> dependencies;
+ std::vector<DependencyOverride> overrides;
std::vector<std::string> default_features;
std::string license; // SPDX license expression
@@ -80,16 +83,7 @@ namespace vcpkg
/// </summary>
struct SourceControlFile
{
- SourceControlFile clone() const
- {
- SourceControlFile ret;
- ret.core_paragraph = std::make_unique<SourceParagraph>(*core_paragraph);
- for (const auto& feat_ptr : feature_paragraphs)
- {
- ret.feature_paragraphs.push_back(std::make_unique<FeatureParagraph>(*feat_ptr));
- }
- return ret;
- }
+ SourceControlFile clone() const;
static Parse::ParseExpected<SourceControlFile> parse_manifest_object(const std::string& origin,
const Json::Object& object);
@@ -107,6 +101,9 @@ namespace vcpkg
Optional<const FeatureParagraph&> find_feature(const std::string& featurename) const;
Optional<const std::vector<Dependency>&> find_dependencies_for_feature(const std::string& featurename) const;
+ Optional<std::string> check_against_feature_flags(const fs::path& origin,
+ const FeatureFlagSettings& flags) const;
+
friend bool operator==(const SourceControlFile& lhs, const SourceControlFile& rhs);
friend bool operator!=(const SourceControlFile& lhs, const SourceControlFile& rhs) { return !(lhs == rhs); }
};
diff --git a/toolsrc/include/vcpkg/versions.h b/toolsrc/include/vcpkg/versions.h
index 331bf7f8b..55586ddcc 100644
--- a/toolsrc/include/vcpkg/versions.h
+++ b/toolsrc/include/vcpkg/versions.h
@@ -9,4 +9,14 @@ namespace vcpkg::Versions
Date,
String
};
+
+ struct Constraint
+ {
+ enum class Type
+ {
+ None,
+ Minimum,
+ Exact
+ };
+ };
}
diff --git a/toolsrc/src/vcpkg-test/manifests.cpp b/toolsrc/src/vcpkg-test/manifests.cpp
index ee6b15e8d..32aa11f8a 100644
--- a/toolsrc/src/vcpkg-test/manifests.cpp
+++ b/toolsrc/src/vcpkg-test/manifests.cpp
@@ -44,6 +44,9 @@ static Parse::ParseExpected<SourceControlFile> test_parse_manifest(StringView sv
return res;
}
+static const FeatureFlagSettings feature_flags_with_versioning{false, false, false, true};
+static const FeatureFlagSettings feature_flags_without_versioning{false, false, false, false};
+
TEST_CASE ("manifest construct minimum", "[manifests]")
{
auto m_pgh = test_parse_manifest(R"json({
@@ -59,6 +62,8 @@ TEST_CASE ("manifest construct minimum", "[manifests]")
REQUIRE(pgh.core_paragraph->maintainers.empty());
REQUIRE(pgh.core_paragraph->description.empty());
REQUIRE(pgh.core_paragraph->dependencies.empty());
+
+ REQUIRE(!pgh.check_against_feature_flags({}, feature_flags_without_versioning));
}
TEST_CASE ("manifest versioning", "[manifests]")
@@ -112,6 +117,201 @@ TEST_CASE ("manifest versioning", "[manifests]")
true);
}
+TEST_CASE ("manifest constraints", "[manifests]")
+{
+ std::string raw = R"json({
+ "name": "zlib",
+ "version-string": "abcd",
+ "dependencies": [
+ "a",
+ {
+ "name": "b",
+ "port-version": 12,
+ "version=": "5"
+ },
+ {
+ "$extra": null,
+ "name": "c"
+ },
+ {
+ "name": "d",
+ "version>=": "2018-09-01"
+ }
+ ]
+}
+)json";
+ auto m_pgh = test_parse_manifest(raw);
+
+ REQUIRE(m_pgh.has_value());
+ auto& pgh = **m_pgh.get();
+ REQUIRE(pgh.check_against_feature_flags({}, feature_flags_without_versioning));
+ REQUIRE(!pgh.check_against_feature_flags({}, feature_flags_with_versioning));
+ REQUIRE(Json::stringify(serialize_manifest(pgh), Json::JsonStyle::with_spaces(4)) == raw);
+ REQUIRE(pgh.core_paragraph->dependencies.size() == 4);
+ REQUIRE(pgh.core_paragraph->dependencies[0].name == "a");
+ REQUIRE(pgh.core_paragraph->dependencies[0].constraint ==
+ DependencyConstraint{Versions::Constraint::Type::None, "", 0});
+ REQUIRE(pgh.core_paragraph->dependencies[1].name == "b");
+ REQUIRE(pgh.core_paragraph->dependencies[1].constraint ==
+ DependencyConstraint{Versions::Constraint::Type::Exact, "5", 12});
+ REQUIRE(pgh.core_paragraph->dependencies[2].name == "c");
+ REQUIRE(pgh.core_paragraph->dependencies[2].constraint ==
+ DependencyConstraint{Versions::Constraint::Type::None, "", 0});
+ REQUIRE(pgh.core_paragraph->dependencies[3].name == "d");
+ REQUIRE(pgh.core_paragraph->dependencies[3].constraint ==
+ DependencyConstraint{Versions::Constraint::Type::Minimum, "2018-09-01", 0});
+
+ test_parse_manifest(R"json({
+ "name": "zlib",
+ "version-string": "abcd",
+ "dependencies": [
+ {
+ "name": "d",
+ "version=": "2018-09-01",
+ "version>=": "2018-09-01"
+ }
+ ]
+ })json",
+ true);
+
+ test_parse_manifest(R"json({
+ "name": "zlib",
+ "version-string": "abcd",
+ "dependencies": [
+ {
+ "name": "d",
+ "port-version": 5
+ }
+ ]
+ })json",
+ true);
+}
+
+TEST_CASE ("manifest overrides", "[manifests]")
+{
+ std::tuple<StringLiteral, Versions::Scheme, StringLiteral> data[] = {
+ {R"json({
+ "name": "zlib",
+ "version-date": "2020-01-01",
+ "overrides": [
+ {
+ "name": "abc",
+ "version-string": "abcd"
+ }
+ ]
+}
+)json",
+ Versions::Scheme::String,
+ "abcd"},
+ {R"json({
+ "name": "zlib",
+ "version": "1.2.3.4.5",
+ "overrides": [
+ {
+ "name": "abc",
+ "version-date": "2020-01-01"
+ }
+ ]
+}
+)json",
+ Versions::Scheme::Date,
+ "2020-01-01"},
+ {R"json({
+ "name": "zlib",
+ "version-date": "2020-01-01",
+ "overrides": [
+ {
+ "name": "abc",
+ "version": "1.2.3.4.5"
+ }
+ ]
+}
+)json",
+ Versions::Scheme::Relaxed,
+ "1.2.3.4.5"},
+ {R"json({
+ "name": "zlib",
+ "version-date": "2020-01-01",
+ "overrides": [
+ {
+ "name": "abc",
+ "version-semver": "1.2.3-rc3"
+ }
+ ]
+}
+)json",
+ Versions::Scheme::Semver,
+ "1.2.3-rc3"},
+ };
+ 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->overrides.size() == 1);
+ REQUIRE(pgh.core_paragraph->overrides[0].version_scheme == std::get<1>(v));
+ REQUIRE(pgh.core_paragraph->overrides[0].version == std::get<2>(v));
+ REQUIRE(pgh.check_against_feature_flags({}, feature_flags_without_versioning));
+ REQUIRE(!pgh.check_against_feature_flags({}, feature_flags_with_versioning));
+ }
+
+ test_parse_manifest(R"json({
+ "name": "zlib",
+ "version-string": "abcd",
+ "overrides": [
+ {
+ "name": "abc",
+ "version-semver": "1.2.3-rc3",
+ "version-string": "1.2.3-rc3"
+ }
+ ]})json",
+ true);
+
+ test_parse_manifest(R"json({
+ "name": "zlib",
+ "version-string": "abcd",
+ "overrides": [
+ {
+ "name": "abc",
+ "port-version": 5
+ }
+ ]})json",
+ true);
+
+ std::string raw = R"json({
+ "name": "zlib",
+ "version-string": "abcd",
+ "overrides": [
+ {
+ "name": "abc",
+ "port-version": 5,
+ "version-string": "hello"
+ },
+ {
+ "name": "abcd",
+ "port-version": 7,
+ "version-string": "hello"
+ }
+ ]
+}
+)json";
+ auto m_pgh = test_parse_manifest(raw);
+
+ REQUIRE(m_pgh.has_value());
+ auto& pgh = **m_pgh.get();
+ REQUIRE(Json::stringify(serialize_manifest(pgh), Json::JsonStyle::with_spaces(4)) == raw);
+ REQUIRE(pgh.core_paragraph->overrides.size() == 2);
+ REQUIRE(pgh.core_paragraph->overrides[0].name == "abc");
+ REQUIRE(pgh.core_paragraph->overrides[0].port_version == 5);
+ REQUIRE(pgh.core_paragraph->overrides[1].name == "abcd");
+ REQUIRE(pgh.core_paragraph->overrides[1].port_version == 7);
+
+ REQUIRE(pgh.check_against_feature_flags({}, feature_flags_without_versioning));
+ REQUIRE(!pgh.check_against_feature_flags({}, feature_flags_with_versioning));
+}
+
TEST_CASE ("manifest construct maximum", "[manifests]")
{
auto m_pgh = test_parse_manifest(R"json({
@@ -181,6 +381,8 @@ TEST_CASE ("manifest construct maximum", "[manifests]")
REQUIRE(pgh.feature_paragraphs[1]->description.size() == 2);
REQUIRE(pgh.feature_paragraphs[1]->description[0] == "son of the fire lord");
REQUIRE(pgh.feature_paragraphs[1]->description[1] == "firebending 師父");
+
+ REQUIRE(!pgh.check_against_feature_flags({}, feature_flags_without_versioning));
}
TEST_CASE ("SourceParagraph manifest two dependencies", "[manifests]")
diff --git a/toolsrc/src/vcpkg/install.cpp b/toolsrc/src/vcpkg/install.cpp
index 0bae37e5a..2abe20774 100644
--- a/toolsrc/src/vcpkg/install.cpp
+++ b/toolsrc/src/vcpkg/install.cpp
@@ -797,7 +797,7 @@ namespace vcpkg::Install
Metrics::g_metrics.lock()->track_property("x-write-nuget-packages-config", "defined");
pkgsconfig = fs::u8path(it_pkgsconfig->second);
}
- auto manifest_path = paths.get_manifest_path().value_or_exit(VCPKG_LINE_INFO);
+ const auto& manifest_path = paths.get_manifest_path().value_or_exit(VCPKG_LINE_INFO);
auto maybe_manifest_scf = SourceControlFile::parse_manifest_file(manifest_path, *manifest);
if (!maybe_manifest_scf)
{
@@ -806,8 +806,14 @@ namespace vcpkg::Install
"more information.\n");
Checks::exit_fail(VCPKG_LINE_INFO);
}
+
auto& manifest_scf = *maybe_manifest_scf.value_or_exit(VCPKG_LINE_INFO);
+ if (auto maybe_error = manifest_scf.check_against_feature_flags(manifest_path, paths.get_feature_flags()))
+ {
+ Checks::exit_with_message(VCPKG_LINE_INFO, maybe_error.value_or_exit(VCPKG_LINE_INFO));
+ }
+
std::vector<std::string> features;
auto manifest_feature_it = options.multisettings.find(OPTION_MANIFEST_FEATURE);
if (manifest_feature_it != options.multisettings.end())
diff --git a/toolsrc/src/vcpkg/packagespec.cpp b/toolsrc/src/vcpkg/packagespec.cpp
index 0feb8412a..a40d30340 100644
--- a/toolsrc/src/vcpkg/packagespec.cpp
+++ b/toolsrc/src/vcpkg/packagespec.cpp
@@ -259,6 +259,14 @@ namespace vcpkg
return ret;
}
+ bool operator==(const DependencyConstraint& lhs, const DependencyConstraint& rhs)
+ {
+ if (lhs.type != rhs.type) return false;
+ if (lhs.value != rhs.value) return false;
+ return lhs.port_version == rhs.port_version;
+ }
+ bool operator!=(const DependencyConstraint& lhs, const DependencyConstraint& rhs);
+
bool operator==(const Dependency& lhs, const Dependency& rhs)
{
if (lhs.name != rhs.name) return false;
@@ -269,4 +277,14 @@ namespace vcpkg
return true;
}
bool operator!=(const Dependency& lhs, const Dependency& rhs);
+
+ bool operator==(const DependencyOverride& lhs, const DependencyOverride& rhs)
+ {
+ if (lhs.version_scheme != rhs.version_scheme) return false;
+ if (lhs.port_version != rhs.port_version) return false;
+ if (lhs.name != rhs.name) return false;
+ if (lhs.version != rhs.version) return false;
+ return lhs.extra_info == rhs.extra_info;
+ }
+ bool operator!=(const DependencyOverride& lhs, const DependencyOverride& rhs);
}
diff --git a/toolsrc/src/vcpkg/portfileprovider.cpp b/toolsrc/src/vcpkg/portfileprovider.cpp
index 391973187..9c09a23e4 100644
--- a/toolsrc/src/vcpkg/portfileprovider.cpp
+++ b/toolsrc/src/vcpkg/portfileprovider.cpp
@@ -182,6 +182,10 @@ namespace vcpkg::PortFileProvider
}
if (auto p = maybe_port.get())
{
+ auto maybe_error =
+ p->source_control_file->check_against_feature_flags(p->source_location, paths.get_feature_flags());
+ if (maybe_error) return std::move(*maybe_error.get());
+
cache_it = cache.emplace(spec, std::move(*p)).first;
}
}
@@ -192,16 +196,6 @@ namespace vcpkg::PortFileProvider
}
else
{
- if (!paths.get_feature_flags().versions)
- {
- if (cache_it->second.source_control_file->core_paragraph->version_scheme != Versions::Scheme::String)
- {
- return Strings::concat(
- "Port definition rejected because the `",
- VcpkgCmdArguments::VERSIONS_FEATURE,
- "` feature flag is disabled.\nThis can be fixed by using the \"version-string\" field.");
- }
- }
return cache_it->second;
}
}
diff --git a/toolsrc/src/vcpkg/sourceparagraph.cpp b/toolsrc/src/vcpkg/sourceparagraph.cpp
index 74858a738..f05ebb619 100644
--- a/toolsrc/src/vcpkg/sourceparagraph.cpp
+++ b/toolsrc/src/vcpkg/sourceparagraph.cpp
@@ -10,6 +10,7 @@
#include <vcpkg/platform-expression.h>
#include <vcpkg/sourceparagraph.h>
#include <vcpkg/triplet.h>
+#include <vcpkg/vcpkgcmdarguments.h>
namespace vcpkg
{
@@ -428,6 +429,9 @@ namespace vcpkg
constexpr static StringLiteral FEATURES = "features";
constexpr static StringLiteral DEFAULT_FEATURES = "default-features";
constexpr static StringLiteral PLATFORM = "platform";
+ constexpr static StringLiteral PORT_VERSION = "port-version";
+ constexpr static StringLiteral VERSION_EQ = "version=";
+ constexpr static StringLiteral VERSION_GE = "version>=";
virtual Span<const StringView> valid_fields() const override
{
@@ -436,6 +440,9 @@ namespace vcpkg
FEATURES,
DEFAULT_FEATURES,
PLATFORM,
+ PORT_VERSION,
+ VERSION_EQ,
+ VERSION_GE,
};
return t;
@@ -480,6 +487,34 @@ namespace vcpkg
r.optional_object_field(obj, PLATFORM, dep.platform, PlatformExprDeserializer::instance);
+ static Json::StringDeserializer version_deserializer{"a version"};
+
+ auto has_eq_constraint =
+ 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);
+ auto has_port_ver = r.optional_object_field(
+ obj, PORT_VERSION, dep.constraint.port_version, Json::NaturalNumberDeserializer::instance);
+
+ if (has_eq_constraint)
+ {
+ dep.constraint.type = Versions::Constraint::Type::Exact;
+ if (has_ge_constraint)
+ {
+ r.add_generic_error(type_name(), "cannot have both exact and minimum constraints simultaneously");
+ }
+ }
+ else if (has_ge_constraint)
+ {
+ dep.constraint.type = Versions::Constraint::Type::Minimum;
+ }
+ else if (has_port_ver) // does not have a primary constraint
+ {
+ r.add_generic_error(
+ type_name(),
+ "\"port-version\" cannot be used without a primary constraint (\"version=\" or \"version>=\")");
+ }
+
return dep;
}
@@ -504,6 +539,106 @@ namespace vcpkg
constexpr StringLiteral DependencyDeserializer::FEATURES;
constexpr StringLiteral DependencyDeserializer::DEFAULT_FEATURES;
constexpr StringLiteral DependencyDeserializer::PLATFORM;
+ constexpr StringLiteral DependencyDeserializer::VERSION_EQ;
+ constexpr StringLiteral DependencyDeserializer::VERSION_GE;
+ constexpr StringLiteral DependencyDeserializer::PORT_VERSION;
+
+ struct DependencyOverrideDeserializer : Json::IDeserializer<DependencyOverride>
+ {
+ 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,
+ };
+
+ return t;
+ }
+
+ static void visit_impl(StringView type_name,
+ Json::Reader& r,
+ const Json::Object& obj,
+ std::string& name,
+ std::string& version,
+ 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);
+ }
+
+ virtual Optional<DependencyOverride> visit_object(Json::Reader& r, const Json::Object& obj) override
+ {
+ DependencyOverride dep;
+
+ for (const auto& el : obj)
+ {
+ if (Strings::starts_with(el.first, "$"))
+ {
+ dep.extra_info.insert_or_replace(el.first.to_string(), el.second);
+ }
+ }
+
+ visit_impl(type_name(), r, obj, dep.name, dep.version, dep.version_scheme, dep.port_version);
+
+ return dep;
+ }
+
+ static DependencyOverrideDeserializer instance;
+ };
+ 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:
@@ -793,6 +928,7 @@ namespace vcpkg
constexpr static StringLiteral FEATURES = "features";
constexpr static StringLiteral DEFAULT_FEATURES = "default-features";
constexpr static StringLiteral SUPPORTS = "supports";
+ constexpr static StringLiteral OVERRIDES = "overrides";
virtual Span<const StringView> valid_fields() const override
{
@@ -813,6 +949,7 @@ namespace vcpkg
FEATURES,
DEFAULT_FEATURES,
SUPPORTS,
+ OVERRIDES,
};
return t;
@@ -835,48 +972,21 @@ namespace vcpkg
}
}
- 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 url_deserializer{"a url"};
constexpr static StringView type_name = "vcpkg.json";
- r.required_object_field(type_name, obj, NAME, spgh->name, Json::IdentifierDeserializer::instance);
- bool has_exact = r.optional_object_field(obj, VERSION_STRING, spgh->version, version_exact_deserializer);
- bool has_relax = r.optional_object_field(obj, VERSION_RELAXED, spgh->version, version_relaxed_deserializer);
- bool has_semver = r.optional_object_field(obj, VERSION_SEMVER, spgh->version, version_semver_deserializer);
- bool has_date = r.optional_object_field(obj, VERSION_DATE, spgh->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_RELAXED, ")");
- }
- else if (num_versions > 1)
- {
- r.add_generic_error(type_name, "expected only one versioning field");
- }
- else
- {
- if (has_exact)
- spgh->version_scheme = Versions::Scheme::String;
- else if (has_relax)
- spgh->version_scheme = Versions::Scheme::Relaxed;
- else if (has_semver)
- spgh->version_scheme = Versions::Scheme::Semver;
- else if (has_date)
- spgh->version_scheme = Versions::Scheme::Date;
- else
- Checks::unreachable(VCPKG_LINE_INFO);
- }
+ DependencyOverrideDeserializer::visit_impl(
+ type_name, r, obj, spgh->name, spgh->version, spgh->version_scheme, spgh->port_version);
- r.optional_object_field(obj, PORT_VERSION, spgh->port_version, Json::NaturalNumberDeserializer::instance);
r.optional_object_field(obj, MAINTAINERS, spgh->maintainers, Json::ParagraphDeserializer::instance);
r.optional_object_field(obj, DESCRIPTION, spgh->description, Json::ParagraphDeserializer::instance);
r.optional_object_field(obj, HOMEPAGE, spgh->homepage, url_deserializer);
r.optional_object_field(obj, DOCUMENTATION, spgh->documentation, url_deserializer);
r.optional_object_field(obj, LICENSE, spgh->license, LicenseExpressionDeserializer::instance);
r.optional_object_field(obj, DEPENDENCIES, spgh->dependencies, DependencyArrayDeserializer::instance);
+ static Json::ArrayDeserializer<DependencyOverrideDeserializer> overrides_deserializer{
+ "an array of overrides"};
+ r.optional_object_field(obj, OVERRIDES, spgh->overrides, overrides_deserializer);
if (obj.contains(DEV_DEPENDENCIES))
{
@@ -919,6 +1029,18 @@ namespace vcpkg
constexpr StringLiteral ManifestDeserializer::FEATURES;
constexpr StringLiteral ManifestDeserializer::DEFAULT_FEATURES;
constexpr StringLiteral ManifestDeserializer::SUPPORTS;
+ constexpr StringLiteral ManifestDeserializer::OVERRIDES;
+
+ SourceControlFile SourceControlFile::clone() const
+ {
+ SourceControlFile ret;
+ ret.core_paragraph = std::make_unique<SourceParagraph>(*core_paragraph);
+ for (const auto& feat_ptr : feature_paragraphs)
+ {
+ ret.feature_paragraphs.push_back(std::make_unique<FeatureParagraph>(*feat_ptr));
+ }
+ return ret;
+ }
Parse::ParseExpected<SourceControlFile> SourceControlFile::parse_manifest_object(const std::string& origin,
const Json::Object& manifest)
@@ -944,6 +1066,52 @@ namespace vcpkg
}
}
+ Optional<std::string> SourceControlFile::check_against_feature_flags(const fs::path& origin,
+ const FeatureFlagSettings& flags) const
+ {
+ if (!flags.versions)
+ {
+ if (core_paragraph->version_scheme != Versions::Scheme::String)
+ {
+ return Strings::concat(fs::u8string(origin),
+ " was rejected because it uses a non-string version scheme and the `",
+ VcpkgCmdArguments::VERSIONS_FEATURE,
+ "` feature flag is disabled.\nThis can be fixed by using \"version-string\".");
+ }
+
+ auto check_deps = [&](View<Dependency> deps) -> Optional<std::string> {
+ for (auto&& dep : deps)
+ {
+ if (dep.constraint.type != Versions::Constraint::Type::None)
+ {
+ return Strings::concat(fs::u8string(origin),
+ " was rejected because it uses constraints and the `",
+ VcpkgCmdArguments::VERSIONS_FEATURE,
+ "` feature flag is disabled.\nThis can be fixed by removing uses of "
+ "\"version>=\" and \"version=\".");
+ }
+ }
+ return nullopt;
+ };
+
+ if (auto r = check_deps(core_paragraph->dependencies)) return r;
+
+ for (auto&& fpgh : feature_paragraphs)
+ {
+ if (auto r = check_deps(fpgh->dependencies)) return r;
+ }
+
+ if (core_paragraph->overrides.size() != 0)
+ {
+ return Strings::concat(fs::u8string(origin),
+ " was rejected because it uses overrides and the `",
+ VcpkgCmdArguments::VERSIONS_FEATURE,
+ "` feature flag is disabled.\nThis can be fixed by removing \"overrides\".");
+ }
+ }
+ return nullopt;
+ }
+
Parse::ParseExpected<SourceControlFile> SourceControlFile::parse_manifest_file(const fs::path& path_to_manifest,
const Json::Object& manifest)
{
@@ -1119,7 +1287,8 @@ namespace vcpkg
}
};
auto serialize_dependency = [&](Json::Array& arr, const Dependency& dep) {
- if (dep.features.empty() && dep.platform.is_empty() && dep.extra_info.is_empty())
+ if (dep.features.empty() && dep.platform.is_empty() && dep.extra_info.is_empty() &&
+ dep.constraint.type == Versions::Constraint::Type::None)
{
arr.push_back(Json::Value::string(dep.name));
}
@@ -1143,9 +1312,51 @@ namespace vcpkg
serialize_optional_array(dep_obj, DependencyDeserializer::FEATURES, features_copy);
serialize_optional_string(dep_obj, DependencyDeserializer::PLATFORM, to_string(dep.platform));
+ if (dep.constraint.port_version != 0)
+ {
+ dep_obj.insert(DependencyDeserializer::PORT_VERSION,
+ Json::Value::integer(dep.constraint.port_version));
+ }
+
+ if (dep.constraint.type == Versions::Constraint::Type::Exact)
+ {
+ dep_obj.insert(DependencyDeserializer::VERSION_EQ, Json::Value::string(dep.constraint.value));
+ }
+ else if (dep.constraint.type == Versions::Constraint::Type::Minimum)
+ {
+ dep_obj.insert(DependencyDeserializer::VERSION_GE, Json::Value::string(dep.constraint.value));
+ }
}
};
+ 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)
+ {
+ dep_obj.insert(el.first.to_string(), el.second);
+ }
+
+ 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));
+ };
+
Json::Object obj;
for (const auto& el : scf.core_paragraph->extra_info)
@@ -1154,17 +1365,7 @@ namespace vcpkg
}
obj.insert(ManifestDeserializer::NAME, Json::Value::string(scf.core_paragraph->name));
- auto version_field = [&] {
- switch (scf.core_paragraph->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);
- }
- }();
- obj.insert(version_field, Json::Value::string(scf.core_paragraph->version));
+ obj.insert(version_field(scf.core_paragraph->version_scheme), Json::Value::string(scf.core_paragraph->version));
if (scf.core_paragraph->port_version != 0 || debug)
{
@@ -1216,6 +1417,16 @@ namespace vcpkg
}
}
+ if (!scf.core_paragraph->overrides.empty() || debug)
+ {
+ auto& overrides = obj.insert(ManifestDeserializer::OVERRIDES, Json::Array());
+
+ for (const auto& over : scf.core_paragraph->overrides)
+ {
+ serialize_override(overrides, over);
+ }
+ }
+
if (debug)
{
obj.insert("TYPE", Json::Value::string(Type::to_string(scf.core_paragraph->type)));