diff options
| author | ras0219 <533828+ras0219@users.noreply.github.com> | 2020-11-13 12:30:31 -0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-11-13 14:30:31 -0600 |
| commit | 783763162901f32e6d6270aede22519844b8e9ce (patch) | |
| tree | f955936fbd922a39aefba8faec6a40a0384d89c5 | |
| parent | 1d8755cb409f655d5135472970d1da0ce975681b (diff) | |
| download | vcpkg-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.h | 28 | ||||
| -rw-r--r-- | toolsrc/include/vcpkg/sourceparagraph.h | 17 | ||||
| -rw-r--r-- | toolsrc/include/vcpkg/versions.h | 10 | ||||
| -rw-r--r-- | toolsrc/src/vcpkg-test/manifests.cpp | 202 | ||||
| -rw-r--r-- | toolsrc/src/vcpkg/install.cpp | 8 | ||||
| -rw-r--r-- | toolsrc/src/vcpkg/packagespec.cpp | 18 | ||||
| -rw-r--r-- | toolsrc/src/vcpkg/portfileprovider.cpp | 14 | ||||
| -rw-r--r-- | toolsrc/src/vcpkg/sourceparagraph.cpp | 299 |
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))); |
