Skip to content

Commit

Permalink
Embed vcpkg configuration in manifest (#239)
Browse files Browse the repository at this point in the history
* [vcpkg] Read configuration from manifest

* format

* Add feature flag

* Improve ambiguous configuration error message

Co-authored-by: nicole mazzuca <[email protected]>

* Make x-vcpkg-configuration a proper field of the manifest

* add Configuration and RegistryImplementation serialize

* fix format

* Allow formatting embeded vcpkg configuration

* Do not use lambda to load optional configuration

Co-authored-by: Billy O'Neal <[email protected]>

* Minor fixes

* Fix format

* Add tests + allow $ extra-info on configuration files

* Remove feature flag and warn on use of embedded config

* Fix incorrect field name in error message

* Change field order of serialized manifests

Co-authored-by: nicole mazzuca <[email protected]>
Co-authored-by: Mathis Logemann <[email protected]>
Co-authored-by: Billy O'Neal <[email protected]>
  • Loading branch information
4 people authored Nov 4, 2021
1 parent e0aafeb commit 57708d0
Show file tree
Hide file tree
Showing 8 changed files with 278 additions and 12 deletions.
2 changes: 2 additions & 0 deletions include/vcpkg/configuration.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@ namespace vcpkg
// `registries` and `default_registry`. The fall back logic is
// taken care of in RegistrySet.
RegistrySet registry_set;
Json::Object extra_info;

void validate_feature_flags(const FeatureFlagSettings& flags);
};

std::unique_ptr<Json::IDeserializer<Configuration>> make_configuration_deserializer(const Path& config_directory);
Json::Object serialize_configuration(const Configuration& config);
}
4 changes: 4 additions & 0 deletions include/vcpkg/registries.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ namespace vcpkg

virtual Optional<VersionT> get_baseline_version(const VcpkgPaths& paths, StringView port_name) const = 0;

virtual Json::Object serialize() const;

virtual ~RegistryImplementation() = default;
};

Expand Down Expand Up @@ -124,6 +126,8 @@ namespace vcpkg
std::vector<Registry> registries_;
};

Json::Object serialize_registry_set(const RegistrySet& config);

std::unique_ptr<Json::IDeserializer<std::unique_ptr<RegistryImplementation>>>
get_registry_implementation_deserializer(const Path& configuration_directory);

Expand Down
1 change: 1 addition & 0 deletions include/vcpkg/sourceparagraph.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ namespace vcpkg
std::vector<std::string> default_features;
std::string license; // SPDX license expression
Optional<std::string> builtin_baseline;
Optional<Json::Object> vcpkg_configuration;

Type type = {Type::PORT};
PlatformExpression::Expr supports_expression;
Expand Down
89 changes: 89 additions & 0 deletions src/vcpkg-test/manifests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -612,6 +612,95 @@ TEST_CASE ("manifest overrides", "[manifests]")
REQUIRE(!pgh.check_against_feature_flags({}, feature_flags_with_versioning));
}

TEST_CASE ("manifest embed configuration", "[manifests]")
{
std::string raw_config = R"json({
"$extra-info": null,
"default-registry": {
"kind": "builtin",
"baseline": "089fa4de7dca22c67dcab631f618d5cd0697c8d4"
},
"registries": [
{
"kind": "filesystem",
"path": "a/b/c",
"baseline": "default",
"packages": [
"a",
"b",
"c"
]
},
{
"kind": "git",
"repository": "https://github.com/microsoft/vcpkg-ports",
"baseline": "089fa4de7dca22c67dcab631f618d5cd0697c8d4",
"packages": [
"zlib",
"rapidjson",
"fmt"
]
}
]
})json";

std::string raw = Strings::concat(R"json({
"vcpkg-configuration": )json",
raw_config,
R"json(,
"name": "zlib",
"version": "1.0.0",
"builtin-baseline": "089fa4de7dca22c67dcab631f618d5cd0697c8d4",
"dependencies": [
"a",
{
"$extra": null,
"name": "b"
},
{
"name": "c",
"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));

auto maybe_as_json = Json::parse(raw);
REQUIRE(maybe_as_json.has_value());
auto as_json = *maybe_as_json.get();
REQUIRE(as_json.first.is_object());
auto as_json_obj = as_json.first.object();
REQUIRE(Json::stringify(serialize_manifest(pgh), Json::JsonStyle::with_spaces(4)) ==
Json::stringify(as_json_obj, Json::JsonStyle::with_spaces(4)));

REQUIRE(pgh.core_paragraph->builtin_baseline == "089fa4de7dca22c67dcab631f618d5cd0697c8d4");
REQUIRE(pgh.core_paragraph->dependencies.size() == 3);
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::None, "", 0});
REQUIRE(pgh.core_paragraph->dependencies[2].name == "c");
REQUIRE(pgh.core_paragraph->dependencies[2].constraint ==
DependencyConstraint{Versions::Constraint::Type::Minimum, "2018-09-01", 0});

auto maybe_config = Json::parse(raw_config, "<test config>");
REQUIRE(maybe_config.has_value());
auto config = *maybe_config.get();
REQUIRE(config.first.is_object());
auto config_obj = config.first.object();
REQUIRE(pgh.core_paragraph->vcpkg_configuration.has_value());
auto parsed_config_obj = *pgh.core_paragraph->vcpkg_configuration.get();
REQUIRE(Json::stringify(parsed_config_obj, Json::JsonStyle::with_spaces(4)) ==
Json::stringify(config_obj, Json::JsonStyle::with_spaces(4)));
}

TEST_CASE ("manifest construct maximum", "[manifests]")
{
auto m_pgh = test_parse_manifest(R"json({
Expand Down
50 changes: 49 additions & 1 deletion src/vcpkg/configuration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,15 @@ namespace

Optional<Configuration> ConfigurationDeserializer::visit_object(Json::Reader& r, const Json::Object& obj)
{
Json::Object extra_info;
for (const auto& el : obj)
{
if (Strings::starts_with(el.first, "$"))
{
extra_info.insert_or_replace(el.first.to_string(), el.second);
}
}

RegistrySet registries;

auto impl_des = get_registry_implementation_deserializer(configuration_directory);
Expand All @@ -53,21 +62,60 @@ namespace
registries.add_registry(std::move(reg));
}

return Configuration{std::move(registries)};
return Configuration{std::move(registries), extra_info};
}

ConfigurationDeserializer::ConfigurationDeserializer(const Path& configuration_directory)
: configuration_directory(configuration_directory)
{
}

static Json::Object serialize_configuration_impl(const Configuration& config)
{
constexpr static StringLiteral REGISTRY_PACKAGES = "packages";

Json::Object obj;

for (const auto& el : config.extra_info)
{
obj.insert(el.first.to_string(), el.second);
}

if (config.registry_set.default_registry())
{
obj.insert(ConfigurationDeserializer::DEFAULT_REGISTRY,
config.registry_set.default_registry()->serialize());
}

auto reg_view = config.registry_set.registries();
if (reg_view.size() > 0)
{
auto& reg_arr = obj.insert(ConfigurationDeserializer::REGISTRIES, Json::Array());
for (const auto& reg : reg_view)
{
auto reg_obj = reg.implementation().serialize();
auto& packages = reg_obj.insert(REGISTRY_PACKAGES, Json::Array{});
for (const auto& pkg : reg.packages())
packages.push_back(Json::Value::string(pkg));
reg_arr.push_back(std::move(reg_obj));
}
}

return obj;
}

}

std::unique_ptr<Json::IDeserializer<Configuration>> vcpkg::make_configuration_deserializer(const Path& config_directory)
{
return std::make_unique<ConfigurationDeserializer>(config_directory);
}

Json::Object vcpkg::serialize_configuration(const Configuration& config)
{
return serialize_configuration_impl(config);
}

namespace vcpkg
{
void Configuration::validate_feature_flags(const FeatureFlagSettings& flags)
Expand Down
38 changes: 38 additions & 0 deletions src/vcpkg/registries.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ namespace

Optional<VersionT> get_baseline_version(const VcpkgPaths&, StringView) const override;

Json::Object serialize() const override;

private:
friend struct GitRegistryEntry;

Expand Down Expand Up @@ -218,6 +220,8 @@ namespace

Optional<VersionT> get_baseline_version(const VcpkgPaths& paths, StringView port_name) const override;

Json::Object serialize() const override;

~BuiltinRegistry() = default;

std::string m_baseline_identifier;
Expand All @@ -239,6 +243,8 @@ namespace

Optional<VersionT> get_baseline_version(const VcpkgPaths&, StringView) const override;

Json::Object serialize() const override;

private:
Path m_path;
std::string m_baseline_identifier;
Expand Down Expand Up @@ -1013,6 +1019,38 @@ namespace
}
}

// serializers

Json::Object RegistryImplementation::serialize() const
{
Json::Object obj;
obj.insert(RegistryImplDeserializer::KIND, Json::Value::string(kind()));
return obj;
}

Json::Object BuiltinRegistry::serialize() const
{
Json::Object obj{RegistryImplementation::serialize()};
obj.insert(RegistryImplDeserializer::BASELINE, Json::Value::string(m_baseline_identifier));
return obj;
}

Json::Object GitRegistry::serialize() const
{
Json::Object obj{RegistryImplementation::serialize()};
obj.insert(RegistryImplDeserializer::REPO, Json::Value::string(m_repo));
obj.insert(RegistryImplDeserializer::BASELINE, Json::Value::string(m_baseline_identifier));
return obj;
}

Json::Object FilesystemRegistry::serialize() const
{
Json::Object obj{RegistryImplementation::serialize()};
obj.insert(RegistryImplDeserializer::PATH, Json::Value::string(m_path.generic_u8string()));
obj.insert(RegistryImplDeserializer::BASELINE, Json::Value::string(m_baseline_identifier));
return obj;
}

namespace vcpkg
{
constexpr StringLiteral VersionDbEntryDeserializer::GIT_TREE;
Expand Down
55 changes: 47 additions & 8 deletions src/vcpkg/sourceparagraph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <vcpkg/base/system.print.h>
#include <vcpkg/base/util.h>

#include <vcpkg/configuration.h>
#include <vcpkg/metrics.h>
#include <vcpkg/packagespec.h>
#include <vcpkg/platform-expression.h>
Expand Down Expand Up @@ -857,6 +858,7 @@ namespace vcpkg
constexpr static StringLiteral SUPPORTS = "supports";
constexpr static StringLiteral OVERRIDES = "overrides";
constexpr static StringLiteral BUILTIN_BASELINE = "builtin-baseline";
constexpr static StringLiteral VCPKG_CONFIGURATION = "vcpkg-configuration";

virtual Span<const StringView> valid_fields() const override
{
Expand All @@ -874,6 +876,7 @@ namespace vcpkg
SUPPORTS,
OVERRIDES,
BUILTIN_BASELINE,
VCPKG_CONFIGURATION,
};
static const auto t = Util::Vectors::concat<StringView>(schemed_deserializer_fields(), u);

Expand Down Expand Up @@ -932,6 +935,18 @@ namespace vcpkg
r.optional_object_field(
obj, FEATURES, control_file->feature_paragraphs, FeaturesFieldDeserializer::instance);

if (auto configuration = obj.get(VCPKG_CONFIGURATION))
{
if (!configuration->is_object())
{
r.add_generic_error(type_name(), VCPKG_CONFIGURATION, " must be an object");
}
else
{
spgh->vcpkg_configuration = make_optional(configuration->object());
}
}

if (auto maybe_error = canonicalize(*control_file))
{
Checks::exit_with_message(VCPKG_LINE_INFO, maybe_error->error);
Expand All @@ -957,6 +972,7 @@ namespace vcpkg
constexpr StringLiteral ManifestDeserializer::SUPPORTS;
constexpr StringLiteral ManifestDeserializer::OVERRIDES;
constexpr StringLiteral ManifestDeserializer::BUILTIN_BASELINE;
constexpr StringLiteral ManifestDeserializer::VCPKG_CONFIGURATION;

SourceControlFile SourceControlFile::clone() const
{
Expand Down Expand Up @@ -998,6 +1014,14 @@ namespace vcpkg
bool is_default_builtin_registry) const
{
static constexpr StringLiteral s_extended_help = "See `vcpkg help versioning` for more information.";
auto format_error_message = [&](StringView manifest_field, StringView feature_flag) {
return Strings::format(" was rejected because it uses \"%s\" and the `%s` feature flag is disabled.\n"
"This can be fixed by removing \"%s\".\n",
manifest_field,
feature_flag,
manifest_field);
};

if (!flags.versions)
{
auto check_deps = [&](View<Dependency> deps) -> Optional<std::string> {
Expand Down Expand Up @@ -1027,21 +1051,18 @@ namespace vcpkg
if (core_paragraph->overrides.size() != 0)
{
LockGuardPtr<Metrics>(g_metrics)->track_property("error-versioning-disabled", "defined");
return Strings::concat(origin,
" was rejected because it uses overrides and the `",
VcpkgCmdArguments::VERSIONS_FEATURE,
"` feature flag is disabled.\nThis can be fixed by removing \"overrides\".\n",
s_extended_help);
return Strings::concat(
origin,
format_error_message(ManifestDeserializer::OVERRIDES, VcpkgCmdArguments::VERSIONS_FEATURE),
s_extended_help);
}

if (core_paragraph->builtin_baseline.has_value())
{
LockGuardPtr<Metrics>(g_metrics)->track_property("error-versioning-disabled", "defined");
return Strings::concat(
origin,
" was rejected because it uses builtin-baseline and the `",
VcpkgCmdArguments::VERSIONS_FEATURE,
"` feature flag is disabled.\nThis can be fixed by removing \"builtin-baseline\".\n",
format_error_message(ManifestDeserializer::BUILTIN_BASELINE, VcpkgCmdArguments::VERSIONS_FEATURE),
s_extended_help);
}
}
Expand Down Expand Up @@ -1327,6 +1348,24 @@ namespace vcpkg
obj.insert(el.first.to_string(), el.second);
}

if (auto configuration = scf.core_paragraph->vcpkg_configuration.get())
{
Json::Reader reader;
auto maybe_configuration = reader.visit(*configuration, *vcpkg::make_configuration_deserializer(""));
if (!reader.errors().empty())
{
print2(Color::error, "Errors occurred while parsing ", ManifestDeserializer::VCPKG_CONFIGURATION, "\n");
for (auto&& msg : reader.errors())
print2(" ", msg, '\n');

print2("See https://github.com/Microsoft/vcpkg/tree/master/docs/users/registries.md for "
"more information.\n");
Checks::exit_fail(VCPKG_LINE_INFO);
}
obj.insert(ManifestDeserializer::VCPKG_CONFIGURATION,
serialize_configuration(maybe_configuration.value_or_exit(VCPKG_LINE_INFO)));
}

obj.insert(ManifestDeserializer::NAME, Json::Value::string(scf.core_paragraph->name));

serialize_schemed_version(obj,
Expand Down
Loading

0 comments on commit 57708d0

Please sign in to comment.