diff --git a/azure-pipelines/end-to-end-tests-dir/registries.ps1 b/azure-pipelines/end-to-end-tests-dir/registries.ps1 index 269e122fd..2e4d0f501 100644 --- a/azure-pipelines/end-to-end-tests-dir/registries.ps1 +++ b/azure-pipelines/end-to-end-tests-dir/registries.ps1 @@ -229,6 +229,52 @@ finally Pop-Location } +# test the filesystem registry with a relative path +Write-Trace "test the filesystem registry with a relative path" +$manifestDir = "$TestingRoot/filesystem-registry-test-manifest-dir" +Remove-Item -Recurse -Force $manifestDir -ErrorAction SilentlyContinue + +New-Item -Path $manifestDir -ItemType Directory +$manifestDir = (Get-Item $manifestDir).FullName + +Push-Location $manifestDir +try +{ + $vcpkgJson = @{ + "name" = "manifest-test"; + "version-string" = "1.0.0"; + "dependencies" = @( + "vcpkg-internal-e2e-test-port" + ); + # Use versioning features without a builtin-baseline + "overrides" = @(@{ + "name" = "unused"; + "version" = "0"; + }) + } + $vcpkgConfigurationJson = @{ + "default-registry" = $null; + "registries" = @( + @{ + "kind" = "filesystem"; + "path" = "../filesystem-registry"; + "packages" = @( "vcpkg-internal-e2e-test-port" ) + } + ) + } + New-Item -Path 'vcpkg.json' -ItemType File ` + -Value (ConvertTo-Json -Depth 5 -InputObject $vcpkgJson) + New-Item -Path 'vcpkg-configuration.json' -ItemType File ` + -Value (ConvertTo-Json -Depth 5 -InputObject $vcpkgConfigurationJson) + + Run-Vcpkg install @builtinRegistryArgs '--feature-flags=registries,manifests' + Throw-IfFailed +} +finally +{ + Pop-Location +} + # test the git registry Write-Trace "test the git registry" $manifestDir = "$TestingRoot/git-registry-test-manifest-dir" diff --git a/include/vcpkg-test/util.h b/include/vcpkg-test/util.h index 649ef98cf..ed389c077 100644 --- a/include/vcpkg-test/util.h +++ b/include/vcpkg-test/util.h @@ -121,5 +121,9 @@ namespace vcpkg::Test } } + void check_json_eq(const Json::Value& l, const Json::Value& r); + void check_json_eq(const Json::Object& l, const Json::Object& r); + void check_json_eq(const Json::Array& l, const Json::Array& r); + const Path& base_temporary_directory() noexcept; } diff --git a/include/vcpkg/base/jsonreader.h b/include/vcpkg/base/jsonreader.h index 257ac5ad4..7e285108c 100644 --- a/include/vcpkg/base/jsonreader.h +++ b/include/vcpkg/base/jsonreader.h @@ -281,7 +281,7 @@ namespace vcpkg::Json virtual StringView type_name() const override { return type_name_; } virtual Optional visit_string(Reader&, StringView sv) override { return sv.to_string(); } - explicit StringDeserializer(StringLiteral type_name_) : type_name_(type_name_) { } + constexpr explicit StringDeserializer(StringLiteral type_name_) : type_name_(type_name_) { } private: StringLiteral type_name_; @@ -327,7 +327,7 @@ namespace vcpkg::Json virtual StringView type_name() const override { return m_type_name; } - ArrayDeserializer(StringLiteral type_name_, Underlying&& t = {}) + constexpr ArrayDeserializer(StringLiteral type_name_, Underlying&& t = {}) : m_type_name(type_name_), m_underlying_visitor(static_cast(t)) { } diff --git a/include/vcpkg/base/optional.h b/include/vcpkg/base/optional.h index b0cbb9456..5fe97b56f 100644 --- a/include/vcpkg/base/optional.h +++ b/include/vcpkg/base/optional.h @@ -253,6 +253,12 @@ namespace vcpkg constexpr explicit operator bool() const { return this->m_base.has_value(); } + T& emplace() + { + this->m_base = T{}; + return this->m_base.value(); + } + constexpr bool has_value() const { return this->m_base.has_value(); } template diff --git a/include/vcpkg/configuration.h b/include/vcpkg/configuration.h index bfad46765..3bc02f778 100644 --- a/include/vcpkg/configuration.h +++ b/include/vcpkg/configuration.h @@ -10,21 +10,43 @@ namespace vcpkg { + struct RegistryConfig + { + // Missing kind means "null" + Optional kind; + Optional baseline; + Optional location; + Optional name; + Optional path; + Optional reference; + Optional repo; + Optional> packages; + + Json::Value serialize() const; + }; + struct Configuration { - // This member is set up via two different configuration options, - // `registries` and `default_registry`. The fall back logic is - // taken care of in RegistrySet. - RegistrySet registry_set; + Optional default_reg; + std::vector registries; Json::Object ce_metadata; Json::Object extra_info; - void validate_feature_flags(const FeatureFlagSettings& flags); + Json::Object serialize() const; + void validate_as_active(); + + std::unique_ptr instantiate_registry_set(const Path& config_dir) const; static View known_fields(); }; - std::unique_ptr> make_configuration_deserializer(const Path& config_directory); - Json::Object serialize_configuration(const Configuration& config); + struct ManifestConfiguration + { + Optional builtin_baseline; + Optional config; + }; + + Json::IDeserializer& get_configuration_deserializer(); + Json::IDeserializer& get_manifest_configuration_deserializer(); std::vector find_unknown_fields(const Configuration& config); } diff --git a/include/vcpkg/documentation.h b/include/vcpkg/documentation.h new file mode 100644 index 000000000..0cc43bed2 --- /dev/null +++ b/include/vcpkg/documentation.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +namespace vcpkg +{ + namespace docs + { + static constexpr StringLiteral registries_url = + "https://github.com/Microsoft/vcpkg/tree/master/docs/users/registries.md"; + static constexpr StringLiteral manifests_url = + "https://github.com/Microsoft/vcpkg/tree/master/docs/users/manifest.md"; + static constexpr StringLiteral assetcaching_url = + "https://github.com/Microsoft/vcpkg/tree/master/docs/users/assetcaching.md"; + static constexpr StringLiteral binarycaching_url = + "https://github.com/Microsoft/vcpkg/tree/master/docs/users/binarycaching.md"; + static constexpr StringLiteral versioning_url = + "https://github.com/Microsoft/vcpkg/tree/master/docs/users/versioning.md"; + static constexpr StringLiteral vcpkg_visual_studio_path_url = + "https://github.com/microsoft/vcpkg/blob/master/docs/users/triplets.md#VCPKG_VISUAL_STUDIO_PATH"; + } +} diff --git a/include/vcpkg/fwd/configuration.h b/include/vcpkg/fwd/configuration.h index 5fa9cee34..cddbbf024 100644 --- a/include/vcpkg/fwd/configuration.h +++ b/include/vcpkg/fwd/configuration.h @@ -3,4 +3,5 @@ namespace vcpkg { struct Configuration; + struct RegistryConfig; } diff --git a/include/vcpkg/registries.h b/include/vcpkg/registries.h index 9a310d24f..6a030b385 100644 --- a/include/vcpkg/registries.h +++ b/include/vcpkg/registries.h @@ -73,8 +73,6 @@ namespace vcpkg virtual Optional get_path_to_baseline_version(const VcpkgPaths& paths, StringView port_name) const; - virtual Json::Object serialize() const; - virtual ~RegistryImplementation() = default; }; @@ -104,7 +102,10 @@ namespace vcpkg // configuration fields. struct RegistrySet { - RegistrySet(); + RegistrySet(std::unique_ptr&& x, std::vector&& y) + : default_registry_(std::move(x)), registries_(std::move(y)) + { + } // finds the correct registry for the port name // Returns the null pointer if there is no registry set up for that name @@ -115,12 +116,7 @@ namespace vcpkg const RegistryImplementation* default_registry() const { return default_registry_.get(); } - // TODO: figure out how to get this to return an error (or maybe it should be a warning?) - void add_registry(Registry&& r); - void set_default_registry(std::unique_ptr&& r); - void set_default_registry(std::nullptr_t r); bool is_default_builtin_registry() const; - void set_default_builtin_registry_baseline(StringView baseline); // returns whether the registry set has any modifications to the default // (i.e., whether `default_registry` was set, or `registries` had any entries) @@ -132,13 +128,13 @@ namespace vcpkg std::vector registries_; }; - Json::Object serialize_registry_set(const RegistrySet& config); - - std::unique_ptr>> - get_registry_implementation_deserializer(const Path& configuration_directory); - - std::unique_ptr>> get_registry_array_deserializer( - const Path& configuration_directory); + std::unique_ptr make_builtin_registry(); + std::unique_ptr make_builtin_registry(std::string baseline); + std::unique_ptr make_git_registry(std::string repo, + std::string reference, + std::string baseline); + std::unique_ptr make_filesystem_registry(Path path, std::string baseline); + std::unique_ptr make_artifact_registry(std::string name, std::string location); ExpectedS>> get_builtin_versions(const VcpkgPaths& paths, StringView port_name); diff --git a/include/vcpkg/sourceparagraph.h b/include/vcpkg/sourceparagraph.h index d7092ee9a..e0f017f24 100644 --- a/include/vcpkg/sourceparagraph.h +++ b/include/vcpkg/sourceparagraph.h @@ -128,6 +128,9 @@ namespace vcpkg Json::Object serialize_manifest(const SourceControlFile& scf); Json::Object serialize_debug_manifest(const SourceControlFile& scf); + ExpectedS parse_manifest_configuration(StringView origin, + const Json::Object& manifest); + /// /// Named pair of a SourceControlFile and the location of this file /// diff --git a/include/vcpkg/vcpkgpaths.h b/include/vcpkg/vcpkgpaths.h index f26222038..4384f35a1 100644 --- a/include/vcpkg/vcpkgpaths.h +++ b/include/vcpkg/vcpkgpaths.h @@ -109,7 +109,6 @@ namespace vcpkg Path original_cwd; Path root; Path manifest_root_dir; - Path config_root_dir; Path downloads; Path triplets; Path community_triplets; @@ -159,8 +158,7 @@ namespace vcpkg Optional get_manifest() const; Optional get_manifest_path() const; - const Configuration& get_configuration() const; - void set_builtin_baseline(const std::string& baseline) const; + const RegistrySet& get_registry_set() const; // Retrieve a toolset matching the requirements in prebuildinfo const Toolset& get_toolset(const Build::PreBuildInfo& prebuildinfo) const; diff --git a/src/vcpkg-test/configmetadata.cpp b/src/vcpkg-test/configmetadata.cpp index 30276f4ff..21c7ea9b6 100644 --- a/src/vcpkg-test/configmetadata.cpp +++ b/src/vcpkg-test/configmetadata.cpp @@ -56,8 +56,7 @@ static Configuration parse_test_configuration(StringView text) auto object = parse_json_object(text); Json::Reader reader; - auto deserializer = make_configuration_deserializer(""); - auto parsed_config_opt = reader.visit(object, *deserializer); + auto parsed_config_opt = reader.visit(object, get_configuration_deserializer()); REQUIRE(reader.errors().empty()); return std::move(parsed_config_opt).value_or_exit(VCPKG_LINE_INFO); @@ -71,19 +70,12 @@ static void check_string(const Json::Object& obj, StringView key, StringView exp REQUIRE(value->string() == expected); } -static void compare_json_objects(const Json::Object& expected, const Json::Object& actual) -{ - REQUIRE(Json::stringify(expected, Json::JsonStyle::with_spaces(4)) == - Json::stringify(actual, Json::JsonStyle::with_spaces(4))); -} - static void check_errors(const std::string& config_text, const std::string& expected_errors) { auto object = parse_json_object(config_text); Json::Reader reader; - auto deserializer = make_configuration_deserializer(""); - auto parsed_config_opt = reader.visit(object, *deserializer); + auto parsed_config_opt = reader.visit(object, get_configuration_deserializer()); REQUIRE(!reader.errors().empty()); CHECK_LINES(Strings::join("\n", reader.errors()), expected_errors); @@ -114,6 +106,11 @@ TEST_CASE ("config registries only", "[ce-metadata]") "kind": "artifact", "name": "vcpkg-artifacts", "location": "https://github.com/microsoft/vcpkg-artifacts" + }, + { + "kind": "filesystem", + "path": "path/to/registry", + "packages": [ ] } ] })json"; @@ -121,39 +118,46 @@ TEST_CASE ("config registries only", "[ce-metadata]") auto config = parse_test_configuration(raw_config); REQUIRE(config.ce_metadata.is_empty()); REQUIRE(config.extra_info.is_empty()); - REQUIRE(config.registry_set.default_registry() != nullptr); - auto default_registry = config.registry_set.default_registry()->serialize(); + REQUIRE(config.default_reg.has_value()); + + auto default_registry = config.default_reg.get()->serialize().object(); check_string(default_registry, KIND, "builtin"); check_string(default_registry, BASELINE, "843e0ba0d8f9c9c572e45564263eedfc7745e74f"); - REQUIRE(config.registry_set.registries().size() == 3); + REQUIRE(config.registries.size() == 4); - const auto& git_registry = config.registry_set.registries()[0]; - auto serialized_git_registry = git_registry.implementation().serialize(); + const auto& git_registry = config.registries[0]; + auto serialized_git_registry = git_registry.serialize().object(); check_string(serialized_git_registry, KIND, "git"); check_string(serialized_git_registry, REPOSITORY, "https://github.com/northwindtraders/vcpkg-registry"); check_string(serialized_git_registry, BASELINE, "dacf4de488094a384ca2c202b923ccc097956e0c"); - REQUIRE(git_registry.packages().size() == 2); - REQUIRE(git_registry.packages()[0] == "beicode"); - REQUIRE(git_registry.packages()[1] == "beison"); + REQUIRE(git_registry.packages); + auto&& p = *git_registry.packages.get(); + REQUIRE(p.size() == 2); + REQUIRE(p[0] == "beicode"); + REQUIRE(p[1] == "beison"); - const auto& fs_registry = config.registry_set.registries()[1]; - auto serialized_fs_registry = fs_registry.implementation().serialize(); + const auto& fs_registry = config.registries[1]; + auto serialized_fs_registry = fs_registry.serialize().object(); check_string(serialized_fs_registry, KIND, "filesystem"); check_string(serialized_fs_registry, PATH, "path/to/registry"); - REQUIRE(fs_registry.packages().size() == 1); - REQUIRE(fs_registry.packages()[0] == "zlib"); + REQUIRE(fs_registry.packages); + REQUIRE(fs_registry.packages.get()->size() == 1); + REQUIRE((*fs_registry.packages.get())[0] == "zlib"); - const auto& artifact_registry = config.registry_set.registries()[2]; - auto serialized_art_registry = artifact_registry.implementation().serialize(); + const auto& artifact_registry = config.registries[2]; + auto serialized_art_registry = artifact_registry.serialize().object(); check_string(serialized_art_registry, KIND, "artifact"); check_string(serialized_art_registry, NAME, "vcpkg-artifacts"); check_string(serialized_art_registry, LOCATION, "https://github.com/microsoft/vcpkg-artifacts"); + REQUIRE(!artifact_registry.packages); + + REQUIRE(config.registries[3].packages); auto raw_obj = parse_json_object(raw_config); - auto serialized_obj = serialize_configuration(config); - compare_json_objects(raw_obj, serialized_obj); + auto serialized_obj = config.serialize(); + Test::check_json_eq(raw_obj, serialized_obj); } SECTION ("default invalid json") @@ -165,7 +169,6 @@ TEST_CASE ("config registries only", "[ce-metadata]") })json"; check_errors(raw_no_baseline, R"( $.default-registry (a builtin registry): missing required field 'baseline' (a baseline) -$.default-registry (a builtin registry): The baseline field of builtin registries must be a git commit SHA (40 lowercase hex characters) )"); std::string raw_with_packages = R"json({ @@ -187,7 +190,7 @@ $.default-registry (a registry): unexpected field 'packages', did you mean 'path } })json"; check_errors(raw_default_artifact, R"( -$ (a configuration object): default-registry cannot be of "artifact" kind +$ (a configuration object): default-registry cannot be of kind "artifact" )"); std::string raw_bad_kind = R"json({ "registries": [{ @@ -223,9 +226,9 @@ $.registries[0] (a registry): missing required field 'packages' (an array of pac })json"; check_errors(raw_bad_git_registry, R"( $.registries[0] (a registry): unexpected field 'no-repository', did you mean 'repository'? -$.registries[0] (a git registry): unexpected field 'no-repository', did you mean 'repository'? $.registries[0] (a git registry): missing required field 'repository' (a git repository URL) $.registries[0].reference: mismatched type: expected a git reference (for example, a branch) +$.registries[0] (a git registry): unexpected field 'no-repository', did you mean 'repository'? $.registries[0].packages: mismatched type: expected an array of package names )"); @@ -239,11 +242,11 @@ $.registries[0].packages: mismatched type: expected an array of package names })json"; check_errors(raw_bad_artifact_registry, R"( $.registries[0] (a registry): unexpected field 'no-location', did you mean 'location'? +$.registries[0] (an artifacts registry): missing required field 'name' (an identifier) +$.registries[0] (an artifacts registry): missing required field 'location' (an artifacts git repository URL) $.registries[0] (an artifacts registry): unexpected field 'no-location', did you mean 'location'? $.registries[0] (an artifacts registry): unexpected field 'baseline', did you mean 'kind'? $.registries[0] (an artifacts registry): unexpected field 'packages', did you mean 'name'? -$.registries[0] (an artifact registry): missing required field 'name' (an identifier) -$.registries[0] (an artifacts registry): missing required field 'location' (an artifacts git repository URL) )"); } } @@ -268,8 +271,8 @@ TEST_CASE ("config ce metadata only", "[ce-metadata]") })json"; auto config = parse_test_configuration(raw_config); - REQUIRE(!config.registry_set.registries().size()); - REQUIRE(config.registry_set.is_default_builtin_registry()); + REQUIRE(!config.registries.size()); + REQUIRE(config.instantiate_registry_set({})->is_default_builtin_registry()); REQUIRE(!config.extra_info.is_empty()); REQUIRE(config.extra_info.size() == 1); @@ -299,8 +302,8 @@ TEST_CASE ("config ce metadata only", "[ce-metadata]") REQUIRE(nested.contains("unexpected")); auto raw_obj = parse_json_object(raw_config); - auto serialized_obj = serialize_configuration(config); - compare_json_objects(raw_obj, serialized_obj); + auto serialized_obj = config.serialize(); + Test::check_json_eq(raw_obj, serialized_obj); } TEST_CASE ("metadata strings", "[ce-metadata]") @@ -320,7 +323,7 @@ TEST_CASE ("metadata strings", "[ce-metadata]") check_string(valid_config.ce_metadata, CE_ERROR, "this is a valid error"); auto raw_obj = parse_json_object(valid_raw); - compare_json_objects(raw_obj, serialize_configuration(valid_config)); + Test::check_json_eq(raw_obj, valid_config.serialize()); } SECTION ("invalid json") @@ -383,7 +386,7 @@ TEST_CASE ("metadata dictionaries", "[ce-metadata]") check_string(settings, "SETTING_2", "value2"); auto raw_obj = parse_json_object(valid_raw); - compare_json_objects(raw_obj, serialize_configuration(valid_config)); + Test::check_json_eq(raw_obj, valid_config.serialize()); } SECTION ("invalid json") @@ -466,7 +469,7 @@ TEST_CASE ("metadata demands", "[ce-metadata]") check_string(level1, CE_MESSAGE, "this is level 1"); auto raw_obj = parse_json_object(simple_raw); - compare_json_objects(raw_obj, serialize_configuration(config)); + Test::check_json_eq(raw_obj, config.serialize()); } SECTION ("invalid json") @@ -517,7 +520,7 @@ TEST_CASE ("serialize configuration", "[ce-metadata]") })json"; // parsing of configuration is tested elsewhere auto config = parse_test_configuration(raw); - compare_json_objects(parse_json_object(raw), serialize_configuration(config)); + Test::check_json_eq(parse_json_object(raw), config.serialize()); } SECTION ("overriden default registry and registries") @@ -538,7 +541,7 @@ TEST_CASE ("serialize configuration", "[ce-metadata]") })json"; // parsing of configuration is tested elsewhere auto config = parse_test_configuration(raw); - compare_json_objects(parse_json_object(raw), serialize_configuration(config)); + Test::check_json_eq(parse_json_object(raw), config.serialize()); } SECTION ("only registries") @@ -555,7 +558,7 @@ TEST_CASE ("serialize configuration", "[ce-metadata]") })json"; // parsing of configuration is tested elsewhere auto config = parse_test_configuration(raw); - compare_json_objects(parse_json_object(raw), serialize_configuration(config)); + Test::check_json_eq(parse_json_object(raw), config.serialize()); } SECTION ("preserve comments and unexpected fields") @@ -580,7 +583,7 @@ TEST_CASE ("serialize configuration", "[ce-metadata]") })json"; auto config = parse_test_configuration(raw); - compare_json_objects(parse_json_object(raw), serialize_configuration(config)); + Test::check_json_eq(parse_json_object(raw), config.serialize()); auto extra_fields = find_unknown_fields(config); CHECK(extra_fields.size() == 4); @@ -681,7 +684,7 @@ TEST_CASE ("serialize configuration", "[ce-metadata]") // demands // Object values in `demands` are also sorted recursively. auto config = parse_test_configuration(raw); - compare_json_objects(parse_json_object(formatted), serialize_configuration(config)); + Test::check_json_eq(parse_json_object(formatted), config.serialize()); } } @@ -749,21 +752,22 @@ TEST_CASE ("config with ce metadata full example", "[ce-metadata]") "}"); auto config = parse_test_configuration(raw_config); - REQUIRE(config.registry_set.default_registry() != nullptr); + REQUIRE(config.default_reg.has_value()); - auto default_registry = config.registry_set.default_registry()->serialize(); + auto default_registry = config.default_reg.get()->serialize().object(); check_string(default_registry, KIND, "builtin"); check_string(default_registry, BASELINE, "843e0ba0d8f9c9c572e45564263eedfc7745e74f"); - REQUIRE(config.registry_set.registries().size() == 1); - const auto& registry = *config.registry_set.registries().begin(); - auto serialized_registry = registry.implementation().serialize(); + REQUIRE(config.registries.size() == 1); + const auto& registry = *config.registries.begin(); + auto serialized_registry = registry.serialize().object(); check_string(serialized_registry, KIND, "git"); check_string(serialized_registry, REPOSITORY, "https://github.com/northwindtraders/vcpkg-registry"); check_string(serialized_registry, BASELINE, "dacf4de488094a384ca2c202b923ccc097956e0c"); - REQUIRE(registry.packages().size() == 2); - REQUIRE(registry.packages()[0] == "beicode"); - REQUIRE(registry.packages()[1] == "beison"); + REQUIRE(registry.packages); + REQUIRE(registry.packages.get()->size() == 2); + REQUIRE(registry.packages.get()->at(0) == "beicode"); + REQUIRE(registry.packages.get()->at(1) == "beison"); REQUIRE(!config.extra_info.is_empty()); REQUIRE(config.extra_info.size() == 2); @@ -913,6 +917,6 @@ TEST_CASE ("config with ce metadata full example", "[ce-metadata]") // finally test serialization is OK auto raw_obj = parse_json_object(raw_config); - auto serialized_obj = serialize_configuration(config); - compare_json_objects(raw_obj, serialized_obj); + auto serialized_obj = config.serialize(); + Test::check_json_eq(raw_obj, serialized_obj); } diff --git a/src/vcpkg-test/manifests.cpp b/src/vcpkg-test/manifests.cpp index 1250d8c63..236c72c19 100644 --- a/src/vcpkg-test/manifests.cpp +++ b/src/vcpkg-test/manifests.cpp @@ -681,10 +681,7 @@ TEST_CASE ("manifest embed configuration", "[manifests]") 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))); + check_json_eq(Json::Value::object(serialize_manifest(pgh)), as_json.first); REQUIRE(pgh.core_paragraph->builtin_baseline == "089fa4de7dca22c67dcab631f618d5cd0697c8d4"); REQUIRE(pgh.core_paragraph->dependencies.size() == 3); diff --git a/src/vcpkg-test/registries.cpp b/src/vcpkg-test/registries.cpp index 4e0b9f2c4..e9cc01e4d 100644 --- a/src/vcpkg-test/registries.cpp +++ b/src/vcpkg-test/registries.cpp @@ -2,6 +2,7 @@ #include +#include #include using namespace vcpkg; @@ -46,37 +47,48 @@ namespace TEST_CASE ("registry_set_selects_registry", "[registries]") { - RegistrySet set; - set.set_default_registry(std::make_unique(0)); + { + std::vector rs; + rs.push_back(make_registry(1, {"p1", "q1", "r1"})); + rs.push_back(make_registry(2, {"p2", "q2", "r2"})); + RegistrySet set(std::make_unique(0), std::move(rs)); - set.add_registry(make_registry(1, {"p1", "q1", "r1"})); - set.add_registry(make_registry(2, {"p2", "q2", "r2"})); + auto reg = set.registry_for_port("p1"); + REQUIRE(reg); + CHECK(get_tri_num(*reg) == 1); + reg = set.registry_for_port("r2"); + REQUIRE(reg); + CHECK(get_tri_num(*reg) == 2); + reg = set.registry_for_port("a"); + REQUIRE(reg); + CHECK(get_tri_num(*reg) == 0); + } + { + std::vector rs; + rs.push_back(make_registry(1, {"p1", "q1", "r1"})); + rs.push_back(make_registry(2, {"p2", "q2", "r2"})); + RegistrySet set(nullptr, std::move(rs)); - auto reg = set.registry_for_port("p1"); - REQUIRE(reg); - CHECK(get_tri_num(*reg) == 1); - reg = set.registry_for_port("r2"); - REQUIRE(reg); - CHECK(get_tri_num(*reg) == 2); - reg = set.registry_for_port("a"); - REQUIRE(reg); - CHECK(get_tri_num(*reg) == 0); + auto reg = set.registry_for_port("q1"); + REQUIRE(reg); + CHECK(get_tri_num(*reg) == 1); + reg = set.registry_for_port("p2"); + REQUIRE(reg); + CHECK(get_tri_num(*reg) == 2); + reg = set.registry_for_port("a"); + CHECK_FALSE(reg); + } +} - set.set_default_registry(nullptr); - - reg = set.registry_for_port("q1"); - REQUIRE(reg); - CHECK(get_tri_num(*reg) == 1); - reg = set.registry_for_port("p2"); - REQUIRE(reg); - CHECK(get_tri_num(*reg) == 2); - reg = set.registry_for_port("a"); - CHECK_FALSE(reg); +static vcpkg::Optional visit_default_registry(Json::Reader& r, Json::Value&& reg) +{ + Json::Object config; + config.insert("default-registry", std::move(reg)); + return r.visit(config, get_configuration_deserializer()); } TEST_CASE ("registry_parsing", "[registries]") { - auto registry_impl_des = get_registry_implementation_deserializer({}); { Json::Reader r; auto test_json = parse_json(R"json( @@ -84,7 +96,7 @@ TEST_CASE ("registry_parsing", "[registries]") "kind": "builtin" } )json"); - r.visit(test_json, *registry_impl_des); + visit_default_registry(r, std::move(test_json)); CHECK(!r.errors().empty()); } { @@ -95,8 +107,9 @@ TEST_CASE ("registry_parsing", "[registries]") "baseline": "hi" } )json"); - r.visit(test_json, *registry_impl_des); - CHECK(!r.errors().empty()); + visit_default_registry(r, std::move(test_json)); + // Non-SHA strings are allowed and will be diagnosed later + CHECK(r.errors().empty()); } { Json::Reader r; @@ -106,9 +119,8 @@ TEST_CASE ("registry_parsing", "[registries]") "baseline": "1234567890123456789012345678901234567890" } )json"); - auto registry_impl = r.visit(test_json, *registry_impl_des); + auto registry_impl = visit_default_registry(r, std::move(test_json)); REQUIRE(registry_impl); - CHECK(*registry_impl.get()); CHECK(r.errors().empty()); } { @@ -120,7 +132,7 @@ TEST_CASE ("registry_parsing", "[registries]") "path": "a/b" } )json"); - r.visit(test_json, *registry_impl_des); + visit_default_registry(r, std::move(test_json)); CHECK(!r.errors().empty()); } { @@ -131,9 +143,8 @@ TEST_CASE ("registry_parsing", "[registries]") "path": "a/b/c" } )json"); - auto registry_impl = r.visit(test_json, *registry_impl_des); + auto registry_impl = visit_default_registry(r, std::move(test_json)); REQUIRE(registry_impl); - CHECK(*registry_impl.get()); CHECK(r.errors().empty()); test_json = parse_json(R"json( @@ -142,9 +153,8 @@ TEST_CASE ("registry_parsing", "[registries]") "path": "/a/b/c" } )json"); - registry_impl = r.visit(test_json, *registry_impl_des); + registry_impl = visit_default_registry(r, std::move(test_json)); REQUIRE(registry_impl); - CHECK(*registry_impl.get()); CHECK(r.errors().empty()); } @@ -155,7 +165,7 @@ TEST_CASE ("registry_parsing", "[registries]") )json"); { Json::Reader r; - r.visit(test_json, *registry_impl_des); + visit_default_registry(r, std::move(test_json)); CHECK(!r.errors().empty()); } test_json = parse_json(R"json( @@ -166,7 +176,7 @@ TEST_CASE ("registry_parsing", "[registries]") )json"); { Json::Reader r; - r.visit(test_json, *registry_impl_des); + visit_default_registry(r, std::move(test_json)); CHECK(!r.errors().empty()); } @@ -178,7 +188,7 @@ TEST_CASE ("registry_parsing", "[registries]") )json"); { Json::Reader r; - r.visit(test_json, *registry_impl_des); + visit_default_registry(r, std::move(test_json)); CHECK(!r.errors().empty()); } @@ -192,9 +202,8 @@ TEST_CASE ("registry_parsing", "[registries]") )json"); { Json::Reader r; - auto registry_impl = r.visit(test_json, *registry_impl_des); + auto registry_impl = visit_default_registry(r, std::move(test_json)); REQUIRE(registry_impl); - CHECK(*registry_impl.get()); INFO(Strings::join("\n", r.errors())); CHECK(r.errors().empty()); } @@ -207,9 +216,8 @@ TEST_CASE ("registry_parsing", "[registries]") } )json"); Json::Reader r; - auto registry_impl = r.visit(test_json, *registry_impl_des); + auto registry_impl = visit_default_registry(r, std::move(test_json)); REQUIRE(registry_impl); - CHECK(*registry_impl.get()); INFO(Strings::join("\n", r.errors())); CHECK(r.errors().empty()); } diff --git a/src/vcpkg-test/util.cpp b/src/vcpkg-test/util.cpp index 94ec78878..051398b0e 100644 --- a/src/vcpkg-test/util.cpp +++ b/src/vcpkg-test/util.cpp @@ -10,6 +10,7 @@ #include #include +#include #include #include @@ -105,4 +106,84 @@ namespace vcpkg::Test const static Path BASE_TEMPORARY_DIRECTORY = internal_base_temporary_directory(); return BASE_TEMPORARY_DIRECTORY; } + + static void check_json_eq(const Json::Value& l, const Json::Value& r, std::string& path); + + static void check_json_eq(const Json::Object& l, const Json::Object& r, std::string& path) + { + std::set keys_l; + for (auto&& kv : l) + { + keys_l.insert(kv.first.to_string()); + } + std::set keys_r; + for (auto&& kv : r) + { + keys_r.insert(kv.first.to_string()); + } + { + INFO(path) + CHECK(keys_l == keys_r); + } + const size_t orig_path_len = path.size(); + for (auto&& key : keys_l) + { + auto vl = l.get(key); + auto vr = r.get(key); + if (vl && vr) + { + path.push_back('.'); + path.append(key); + check_json_eq(*vl, *vr, path); + path.resize(orig_path_len); + } + } + } + static void check_json_eq(const Json::Array& l, const Json::Array& r, std::string& path) + { + { + INFO(path) + CHECK(l.size() == r.size()); + } + const size_t orig_path_len = path.size(); + for (size_t i = 0; i < l.size() && i < r.size(); ++i) + { + Strings::append(path, '[', i, ']'); + check_json_eq(r[i], l[i], path); + path.resize(orig_path_len); + } + } + static void check_json_eq(const Json::Value& l, const Json::Value& r, std::string& path) + { + if (l.is_object() && r.is_object()) + { + check_json_eq(l.object(), r.object(), path); + } + else if (l.is_array() && r.is_array()) + { + check_json_eq(l.array(), r.array(), path); + } + else + { + INFO(path); + REQUIRE(l == r); + } + } + + void check_json_eq(const Json::Value& l, const Json::Value& r) + { + std::string path = "$"; + check_json_eq(l, r, path); + } + void check_json_eq(const Json::Object& l, const Json::Object& r) + { + std::string path = "$"; + check_json_eq(l, r, path); + } + void check_json_eq(const Json::Array& l, const Json::Array& r) + { + std::string path = "$"; + check_json_eq(l, r, path); + } + } diff --git a/src/vcpkg/base/json.cpp b/src/vcpkg/base/json.cpp index 85fd8085d..a44661e5e 100644 --- a/src/vcpkg/base/json.cpp +++ b/src/vcpkg/base/json.cpp @@ -5,6 +5,8 @@ #include #include +#include + #include namespace vcpkg::Json @@ -1425,9 +1427,9 @@ namespace vcpkg::Json if (!is_ident(sv)) { r.add_generic_error(type_name(), - "must be lowercase alphanumeric+hyphens and not reserved (see " - "https://github.com/Microsoft/vcpkg/tree/master/docs/specifications/manifests.md for " - "more information)"); + Strings::concat("must be lowercase alphanumeric+hyphens and not reserved (see ", + vcpkg::docs::manifests_url, + " for more information)")); } return sv.to_string(); } diff --git a/src/vcpkg/binarycaching.cpp b/src/vcpkg/binarycaching.cpp index 06f848368..1d0a38ced 100644 --- a/src/vcpkg/binarycaching.cpp +++ b/src/vcpkg/binarycaching.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -21,11 +22,6 @@ using namespace vcpkg; namespace { - static constexpr StringLiteral s_assetcaching_doc_url = - "https://github.com/Microsoft/vcpkg/tree/master/docs/users/assetcaching.md"; - static constexpr StringLiteral s_binarycaching_doc_url = - "https://github.com/Microsoft/vcpkg/tree/master/docs/users/binarycaching.md"; - struct ConfigSegmentsParser : Parse::ParserBase { using Parse::ParserBase::ParserBase; @@ -633,9 +629,9 @@ namespace res.exit_code != 0) { print2(Color::warning, - "One or more NuGet credential providers failed to authenticate. See " - "https://github.com/Microsoft/vcpkg/tree/master/docs/users/binarycaching.md for " - "more details on how to provide credentials.\n"); + "One or more NuGet credential providers failed to authenticate. See ", + docs::binarycaching_url, + " for more details on how to provide credentials.\n"); } else if (res.output.find("for example \"-ApiKey AzureDevOps\"") != std::string::npos) { @@ -1802,21 +1798,21 @@ ExpectedS vcpkg::parse_download_configuration( parser.parse(); if (auto err = parser.get_error()) { - return Strings::concat(err->format(), "For more information, see ", s_assetcaching_doc_url, "\n"); + return Strings::concat(err->format(), "For more information, see ", docs::assetcaching_url, "\n"); } if (s.azblob_templates_to_put.size() > 1) { return Strings::concat("Error: a maximum of one asset write url can be specified\n" "For more information, see ", - s_assetcaching_doc_url, + docs::assetcaching_url, "\n"); } if (s.url_templates_to_get.size() > 1) { return Strings::concat("Error: a maximum of one asset read url can be specified\n" "For more information, see ", - s_assetcaching_doc_url, + docs::assetcaching_url, "\n"); } @@ -2108,7 +2104,7 @@ void vcpkg::help_topic_asset_caching(const VcpkgPaths&) tbl.blank(); print2(tbl.m_str); - print2("\nExtended documentation is available at ", s_assetcaching_doc_url, "\n"); + print2("\nExtended documentation is available at ", docs::assetcaching_url, "\n"); } void vcpkg::help_topic_binary_caching(const VcpkgPaths&) @@ -2175,7 +2171,7 @@ void vcpkg::help_topic_binary_caching(const VcpkgPaths&) "\nThis consults %LOCALAPPDATA%/%APPDATA% on Windows and $XDG_CACHE_HOME or $HOME on other platforms.\n"); } - print2("\nExtended documentation is available at ", s_binarycaching_doc_url, "\n"); + print2("\nExtended documentation is available at ", docs::binarycaching_url, "\n"); } std::string vcpkg::generate_nuget_packages_config(const Dependencies::ActionPlan& action) diff --git a/src/vcpkg/build.cpp b/src/vcpkg/build.cpp index 3b83719d8..1bf9d1eb0 100644 --- a/src/vcpkg/build.cpp +++ b/src/vcpkg/build.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -352,10 +353,7 @@ namespace vcpkg::Build msg::value = target_architecture, msg::path = toolset.visual_studio_root_path, msg::list = toolset_list); - msg::println( - msg::msgSeeURL, - msg::url = StringLiteral( - "https://github.com/microsoft/vcpkg/blob/master/docs/users/triplets.md#VCPKG_VISUAL_STUDIO_PATH")); + msg::println(msg::msgSeeURL, msg::url = docs::vcpkg_visual_studio_path_url); Checks::exit_maybe_upgrade(VCPKG_LINE_INFO); } #endif diff --git a/src/vcpkg/configuration.cpp b/src/vcpkg/configuration.cpp index 6b91620d7..4337d4f60 100644 --- a/src/vcpkg/configuration.cpp +++ b/src/vcpkg/configuration.cpp @@ -9,6 +9,204 @@ namespace { using namespace vcpkg; + struct RegistryConfigDeserializer : Json::IDeserializer + { + constexpr static StringLiteral KIND = "kind"; + constexpr static StringLiteral BASELINE = "baseline"; + constexpr static StringLiteral PATH = "path"; + constexpr static StringLiteral REPO = "repository"; + constexpr static StringLiteral REFERENCE = "reference"; + constexpr static StringLiteral NAME = "name"; + constexpr static StringLiteral LOCATION = "location"; + + constexpr static StringLiteral KIND_BUILTIN = "builtin"; + constexpr static StringLiteral KIND_FILESYSTEM = "filesystem"; + constexpr static StringLiteral KIND_GIT = "git"; + constexpr static StringLiteral KIND_ARTIFACT = "artifact"; + + virtual StringView type_name() const override { return "a registry"; } + virtual View valid_fields() const override; + + virtual Optional visit_null(Json::Reader&) override; + virtual Optional visit_object(Json::Reader&, const Json::Object&) override; + + static RegistryConfigDeserializer instance; + }; + RegistryConfigDeserializer RegistryConfigDeserializer::instance; + constexpr StringLiteral RegistryConfigDeserializer::KIND; + constexpr StringLiteral RegistryConfigDeserializer::BASELINE; + constexpr StringLiteral RegistryConfigDeserializer::PATH; + constexpr StringLiteral RegistryConfigDeserializer::REPO; + constexpr StringLiteral RegistryConfigDeserializer::REFERENCE; + constexpr StringLiteral RegistryConfigDeserializer::NAME; + constexpr StringLiteral RegistryConfigDeserializer::LOCATION; + constexpr StringLiteral RegistryConfigDeserializer::KIND_BUILTIN; + constexpr StringLiteral RegistryConfigDeserializer::KIND_FILESYSTEM; + constexpr StringLiteral RegistryConfigDeserializer::KIND_GIT; + constexpr StringLiteral RegistryConfigDeserializer::KIND_ARTIFACT; + + struct RegistryDeserializer final : Json::IDeserializer + { + constexpr static StringLiteral PACKAGES = "packages"; + + virtual StringView type_name() const override { return "a registry"; } + virtual View valid_fields() const override; + + virtual Optional visit_object(Json::Reader&, const Json::Object&) override; + + static RegistryDeserializer instance; + }; + RegistryDeserializer RegistryDeserializer::instance; + constexpr StringLiteral RegistryDeserializer::PACKAGES; + + View RegistryConfigDeserializer::valid_fields() const + { + static const StringView t[] = {KIND, BASELINE, PATH, REPO, REFERENCE, NAME, LOCATION}; + return t; + } + View valid_builtin_fields() + { + static const StringView t[] = { + RegistryConfigDeserializer::KIND, + RegistryConfigDeserializer::BASELINE, + RegistryDeserializer::PACKAGES, + }; + return t; + } + View valid_filesystem_fields() + { + static const StringView t[] = { + RegistryConfigDeserializer::KIND, + RegistryConfigDeserializer::BASELINE, + RegistryConfigDeserializer::PATH, + RegistryDeserializer::PACKAGES, + }; + return t; + } + View valid_git_fields() + { + static const StringView t[] = { + RegistryConfigDeserializer::KIND, + RegistryConfigDeserializer::BASELINE, + RegistryConfigDeserializer::REPO, + RegistryConfigDeserializer::REFERENCE, + RegistryDeserializer::PACKAGES, + }; + return t; + } + View valid_artifact_fields() + { + static const StringView t[] = { + RegistryConfigDeserializer::KIND, + RegistryConfigDeserializer::NAME, + RegistryConfigDeserializer::LOCATION, + }; + return t; + } + + Optional RegistryConfigDeserializer::visit_null(Json::Reader&) { return RegistryConfig(); } + + Optional RegistryConfigDeserializer::visit_object(Json::Reader& r, const Json::Object& obj) + { + static Json::StringDeserializer kind_deserializer{"a registry implementation kind"}; + static Json::StringDeserializer baseline_deserializer{"a baseline"}; + + RegistryConfig res; + auto& kind = res.kind.emplace(); + r.required_object_field(type_name(), obj, KIND, kind, kind_deserializer); + + if (kind == KIND_BUILTIN) + { + auto& baseline = res.baseline.emplace(); + r.required_object_field("a builtin registry", obj, BASELINE, baseline, baseline_deserializer); + r.check_for_unexpected_fields(obj, valid_builtin_fields(), "a builtin registry"); + } + else if (kind == KIND_FILESYSTEM) + { + std::string baseline; + if (r.optional_object_field(obj, BASELINE, baseline, baseline_deserializer)) + { + res.baseline = std::move(baseline); + } + + r.required_object_field( + "a filesystem registry", obj, PATH, res.path.emplace(), Json::PathDeserializer::instance); + + r.check_for_unexpected_fields(obj, valid_filesystem_fields(), "a filesystem registry"); + } + else if (kind == KIND_GIT) + { + static Json::StringDeserializer repo_des{"a git repository URL"}; + r.required_object_field("a git registry", obj, REPO, res.repo.emplace(), repo_des); + + static Json::StringDeserializer ref_des{"a git reference (for example, a branch)"}; + if (!r.optional_object_field(obj, REFERENCE, res.reference.emplace(), ref_des)) + { + res.reference = nullopt; + } + + r.required_object_field("a git registry", obj, BASELINE, res.baseline.emplace(), baseline_deserializer); + + r.check_for_unexpected_fields(obj, valid_git_fields(), "a git registry"); + } + else if (kind == KIND_ARTIFACT) + { + r.required_object_field( + "an artifacts registry", obj, NAME, res.name.emplace(), Json::IdentifierDeserializer::instance); + + static Json::StringDeserializer location_des{"an artifacts git repository URL"}; + r.required_object_field("an artifacts registry", obj, LOCATION, res.location.emplace(), location_des); + + r.check_for_unexpected_fields(obj, valid_artifact_fields(), "an artifacts registry"); + } + else + { + StringLiteral valid_kinds[] = {KIND_BUILTIN, KIND_FILESYSTEM, KIND_GIT, KIND_ARTIFACT}; + r.add_generic_error(type_name(), + "Field \"kind\" did not have an expected value (expected one of: \"", + Strings::join("\", \"", valid_kinds), + "\"; found \"", + kind, + "\")"); + return nullopt; + } + + return std::move(res); // gcc-7 bug workaround redundant move + } + + View RegistryDeserializer::valid_fields() const + { + static const StringView t[] = { + RegistryConfigDeserializer::KIND, + RegistryConfigDeserializer::BASELINE, + RegistryConfigDeserializer::PATH, + RegistryConfigDeserializer::REPO, + RegistryConfigDeserializer::REFERENCE, + RegistryConfigDeserializer::NAME, + RegistryConfigDeserializer::LOCATION, + PACKAGES, + }; + return t; + } + + Optional RegistryDeserializer::visit_object(Json::Reader& r, const Json::Object& obj) + { + auto impl = RegistryConfigDeserializer::instance.visit_object(r, obj); + + if (auto config = impl.get()) + { + static Json::ArrayDeserializer package_names_deserializer{ + "an array of package names"}; + + if (config->kind && *config->kind.get() != RegistryConfigDeserializer::KIND_ARTIFACT) + { + r.required_object_field( + type_name(), obj, PACKAGES, config->packages.emplace(), package_names_deserializer); + } + } + return impl; + } + struct DictionaryDeserializer final : Json::IDeserializer { virtual StringView type_name() const override { return "a `string: string` dictionary"; } @@ -66,11 +264,9 @@ namespace virtual Optional visit_object(Json::Reader& r, const Json::Object& obj) override; - ConfigurationDeserializer(const Path& configuration_directory); - - private: - Path configuration_directory; + static ConfigurationDeserializer instance; }; + ConfigurationDeserializer ConfigurationDeserializer::instance; constexpr StringLiteral ConfigurationDeserializer::DEFAULT_REGISTRY; constexpr StringLiteral ConfigurationDeserializer::REGISTRIES; @@ -186,9 +382,8 @@ namespace Optional ConfigurationDeserializer::visit_object(Json::Reader& r, const Json::Object& obj) { - static const StringView ARTIFACT = "artifact"; - - Json::Object extra_info; + Configuration ret; + Json::Object& extra_info = ret.extra_info; std::vector comment_keys; for (const auto& el : obj) @@ -201,30 +396,24 @@ namespace } } - RegistrySet registries; - - auto impl_des = get_registry_implementation_deserializer(configuration_directory); - - std::unique_ptr default_registry; - if (r.optional_object_field(obj, DEFAULT_REGISTRY, default_registry, *impl_des)) + RegistryConfig default_registry; + if (r.optional_object_field(obj, DEFAULT_REGISTRY, default_registry, RegistryConfigDeserializer::instance)) { - if (default_registry && default_registry->kind() == ARTIFACT) + if (default_registry.kind.value_or("") == RegistryConfigDeserializer::KIND_ARTIFACT) { - r.add_generic_error(type_name(), DEFAULT_REGISTRY, " cannot be of \"", ARTIFACT, "\" kind"); + r.add_generic_error(type_name(), + DEFAULT_REGISTRY, + " cannot be of kind \"", + RegistryConfigDeserializer::KIND_ARTIFACT, + "\""); } - registries.set_default_registry(std::move(default_registry)); + ret.default_reg = std::move(default_registry); } - auto reg_des = get_registry_array_deserializer(configuration_directory); - std::vector regs; - r.optional_object_field(obj, REGISTRIES, regs, *reg_des); + static Json::ArrayDeserializer regs_des("an array of registries"); + r.optional_object_field(obj, REGISTRIES, ret.registries, regs_des); - for (Registry& reg : regs) - { - registries.add_registry(std::move(reg)); - } - - Json::Object ce_metadata_obj; + Json::Object& ce_metadata_obj = ret.ce_metadata; auto maybe_ce_metadata = r.visit(obj, CeMetadataDeserializer::instance); if (maybe_ce_metadata.has_value()) { @@ -243,12 +432,7 @@ namespace ce_metadata_obj.remove(comment_key); } - return Configuration{std::move(registries), ce_metadata_obj, extra_info}; - } - - ConfigurationDeserializer::ConfigurationDeserializer(const Path& configuration_directory) - : configuration_directory(configuration_directory) - { + return std::move(ret); } static void serialize_ce_metadata(const Json::Object& ce_metadata, Json::Object& put_into) @@ -307,56 +491,6 @@ namespace serialize_demands(ce_metadata, put_into); } - 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.is_default_builtin_registry()) - { - if (auto default_registry = config.registry_set.default_registry()) - { - obj.insert(ConfigurationDeserializer::DEFAULT_REGISTRY, default_registry->serialize()); - } - else - { - obj.insert(ConfigurationDeserializer::DEFAULT_REGISTRY, Json::Value::null(nullptr)); - } - } - - 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(); - if (reg.packages().size()) - { - 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)); - } - } - - if (!config.ce_metadata.is_empty()) - { - serialize_ce_metadata(config.ce_metadata, obj); - } - - return obj; - } - static void find_unknown_fields_impl(const Json::Object& obj, std::vector& out, StringView path) { std::vector ret; @@ -420,19 +554,8 @@ namespace vcpkg return known_fields; } - void Configuration::validate_feature_flags(const FeatureFlagSettings& flags) + void Configuration::validate_as_active() { - if (!flags.registries && registry_set.has_modifications()) - { - LockGuardPtr(g_metrics)->track_property( - "registries-error-registry-modification-without-feature-flag", "defined"); - vcpkg::printf(Color::warning, - "Warning: configuration specified the \"registries\" or \"default-registries\" field, but " - "the %s feature flag was not enabled.\n", - VcpkgCmdArguments::REGISTRIES_FEATURE); - registry_set = RegistrySet(); - } - if (!ce_metadata.is_empty()) { auto unknown_fields = find_unknown_fields(*this); @@ -447,12 +570,114 @@ namespace vcpkg } } - std::unique_ptr> make_configuration_deserializer(const Path& config_directory) + Json::IDeserializer& get_configuration_deserializer() { return ConfigurationDeserializer::instance; } + + static std::unique_ptr instantiate_rconfig(const RegistryConfig& config, + const Path& config_dir) { - return std::make_unique(config_directory); + if (auto k = config.kind.get()) + { + if (*k == RegistryConfigDeserializer::KIND_BUILTIN) + { + return make_builtin_registry(config.baseline.value_or_exit(VCPKG_LINE_INFO)); + } + else if (*k == RegistryConfigDeserializer::KIND_GIT) + { + return make_git_registry(config.repo.value_or_exit(VCPKG_LINE_INFO), + config.reference.value_or("HEAD"), + config.baseline.value_or_exit(VCPKG_LINE_INFO)); + } + else if (*k == RegistryConfigDeserializer::KIND_ARTIFACT) + { + return make_artifact_registry(config.name.value_or_exit(VCPKG_LINE_INFO), + config.location.value_or_exit(VCPKG_LINE_INFO)); + } + else if (*k == RegistryConfigDeserializer::KIND_FILESYSTEM) + { + return make_filesystem_registry(config_dir / config.path.value_or_exit(VCPKG_LINE_INFO), + config.baseline.value_or("")); + } + else + { + Checks::unreachable(VCPKG_LINE_INFO); + } + } + else + { + return nullptr; + } } - Json::Object serialize_configuration(const Configuration& config) { return serialize_configuration_impl(config); } + std::unique_ptr Configuration::instantiate_registry_set(const Path& config_dir) const + { + std::vector r_impls; + for (auto&& reg : registries) + { + // packages will be null for artifact registries + if (auto p = reg.packages.get()) + { + r_impls.emplace_back(std::vector(*p), instantiate_rconfig(reg, config_dir)); + } + } + auto reg1 = default_reg ? instantiate_rconfig(*default_reg.get(), config_dir) : make_builtin_registry(); + return std::make_unique(std::move(reg1), std::move(r_impls)); + } + + Json::Object Configuration::serialize() const + { + Json::Object obj; + + for (const auto& el : extra_info) + { + obj.insert(el.first.to_string(), el.second); + } + + if (auto default_registry = default_reg.get()) + { + obj.insert(ConfigurationDeserializer::DEFAULT_REGISTRY, default_registry->serialize()); + } + + if (!registries.empty()) + { + auto& reg_arr = obj.insert(ConfigurationDeserializer::REGISTRIES, Json::Array()); + for (const auto& reg : registries) + { + reg_arr.push_back(reg.serialize()); + } + } + + if (!ce_metadata.is_empty()) + { + serialize_ce_metadata(ce_metadata, obj); + } + + return obj; + } + + Json::Value RegistryConfig::serialize() const + { + if (!kind) + { + return Json::Value::null(nullptr); + } + Json::Object obj; + obj.insert(RegistryConfigDeserializer::KIND, Json::Value::string(*kind.get())); + if (auto p = baseline.get()) obj.insert(RegistryConfigDeserializer::BASELINE, Json::Value::string(*p)); + if (auto p = location.get()) obj.insert(RegistryConfigDeserializer::LOCATION, Json::Value::string(*p)); + if (auto p = name.get()) obj.insert(RegistryConfigDeserializer::NAME, Json::Value::string(*p)); + if (auto p = path.get()) obj.insert(RegistryConfigDeserializer::PATH, Json::Value::string(p->native())); + if (auto p = reference.get()) obj.insert(RegistryConfigDeserializer::REFERENCE, Json::Value::string(*p)); + if (auto p = repo.get()) obj.insert(RegistryConfigDeserializer::REPO, Json::Value::string(*p)); + if (packages) + { + auto& arr = obj.insert(RegistryDeserializer::PACKAGES, Json::Array()); + for (auto&& p : *packages.get()) + { + arr.push_back(Json::Value::string(p)); + } + } + return Json::Value::object(std::move(obj)); + } std::vector find_unknown_fields(const Configuration& config) { diff --git a/src/vcpkg/help.cpp b/src/vcpkg/help.cpp index a55a7d11c..76a579e32 100644 --- a/src/vcpkg/help.cpp +++ b/src/vcpkg/help.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -112,9 +113,7 @@ namespace vcpkg::Help { "name": "rapidjson", "version": "2020-09-14" } ] })"); - print2(tbl.m_str, - "\nExtended documentation is available at " - "https://github.com/Microsoft/vcpkg/tree/master/docs/users/versioning.md\n"); + print2(tbl.m_str, "\nExtended documentation is available at ", docs::versioning_url, "\n"); } static constexpr std::array topics = {{ diff --git a/src/vcpkg/install.cpp b/src/vcpkg/install.cpp index 1b79002f9..2413dd3e6 100644 --- a/src/vcpkg/install.cpp +++ b/src/vcpkg/install.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -851,17 +852,14 @@ namespace vcpkg::Install if (!maybe_manifest_scf) { print_error_message(maybe_manifest_scf.error()); - print2("See https://github.com/Microsoft/vcpkg/tree/master/docs/users/manifests.md for " - "more information.\n"); + print2("See ", docs::manifests_url, " for 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(), - paths.get_configuration().registry_set.is_default_builtin_registry())) + manifest_path, paths.get_feature_flags(), paths.get_registry_set().is_default_builtin_registry())) { Checks::exit_with_message(VCPKG_LINE_INFO, maybe_error.value_or_exit(VCPKG_LINE_INFO)); } @@ -928,8 +926,7 @@ namespace vcpkg::Install extended_overlay_ports.reserve(args.overlay_ports.size() + 2); extended_overlay_ports.push_back(manifest_path.parent_path().to_string()); Util::Vectors::append(&extended_overlay_ports, args.overlay_ports); - if (paths.get_configuration().registry_set.is_default_builtin_registry() && - !paths.use_git_default_registry()) + if (paths.get_registry_set().is_default_builtin_registry() && !paths.use_git_default_registry()) { extended_overlay_ports.push_back(paths.builtin_ports_directory().native()); } diff --git a/src/vcpkg/paragraphs.cpp b/src/vcpkg/paragraphs.cpp index bdff8a588..3126dd546 100644 --- a/src/vcpkg/paragraphs.cpp +++ b/src/vcpkg/paragraphs.cpp @@ -412,7 +412,7 @@ namespace vcpkg::Paragraphs std::vector ports; - const auto& registries = paths.get_configuration().registry_set; + const auto& registries = paths.get_registry_set(); for (const auto& registry : registries.registries()) { diff --git a/src/vcpkg/portfileprovider.cpp b/src/vcpkg/portfileprovider.cpp index a61111e72..7984eeb0f 100644 --- a/src/vcpkg/portfileprovider.cpp +++ b/src/vcpkg/portfileprovider.cpp @@ -107,7 +107,7 @@ namespace vcpkg::PortFileProvider } else { - auto version = paths.get_configuration().registry_set.baseline_for_port(paths, port_name); + auto version = paths.get_registry_set().baseline_for_port(paths, port_name); m_baseline_cache.emplace(port_name.to_string(), version); return version; } @@ -129,7 +129,7 @@ namespace vcpkg::PortFileProvider auto entry_it = m_entry_cache.find(name); if (entry_it == m_entry_cache.end()) { - if (auto reg = paths.get_configuration().registry_set.registry_for_port(name)) + if (auto reg = paths.get_registry_set().registry_for_port(name)) { if (auto entry = reg->get_port_entry(paths, name)) { diff --git a/src/vcpkg/registries.cpp b/src/vcpkg/registries.cpp index d2e5a3577..2d7cd5309 100644 --- a/src/vcpkg/registries.cpp +++ b/src/vcpkg/registries.cpp @@ -64,8 +64,6 @@ namespace Optional get_baseline_version(const VcpkgPaths&, StringView) const override; - Json::Object serialize() const override; - private: friend struct GitRegistryEntry; @@ -231,8 +229,6 @@ namespace return paths.builtin_ports_directory() / port_name; } - Json::Object serialize() const override; - ~BuiltinFilesRegistry() = default; DelayedInit m_baseline; @@ -266,8 +262,6 @@ namespace Optional get_baseline_version(const VcpkgPaths& paths, StringView port_name) const override; - Json::Object serialize() const override; - ~BuiltinGitRegistry() = default; std::string m_baseline_identifier; @@ -292,7 +286,7 @@ namespace struct FilesystemRegistry final : RegistryImplementation { FilesystemRegistry(Path&& path, std::string&& baseline) - : m_path(std::move(path)), m_baseline_identifier(baseline) + : m_path(std::move(path)), m_baseline_identifier(std::move(baseline)) { } @@ -304,8 +298,6 @@ namespace Optional get_baseline_version(const VcpkgPaths&, StringView) const override; - Json::Object serialize() const override; - private: Path m_path; std::string m_baseline_identifier; @@ -336,8 +328,6 @@ namespace Checks::exit_fail(VCPKG_LINE_INFO); } - Json::Object serialize() const override; - ~ArtifactRegistry() = default; private: @@ -922,228 +912,6 @@ namespace }; BaselineDeserializer BaselineDeserializer::instance; - struct RegistryImplDeserializer : Json::IDeserializer> - { - constexpr static StringLiteral KIND = "kind"; - constexpr static StringLiteral BASELINE = "baseline"; - constexpr static StringLiteral PATH = "path"; - constexpr static StringLiteral REPO = "repository"; - constexpr static StringLiteral REFERENCE = "reference"; - constexpr static StringLiteral NAME = "name"; - constexpr static StringLiteral LOCATION = "location"; - - constexpr static StringLiteral KIND_BUILTIN = "builtin"; - constexpr static StringLiteral KIND_FILESYSTEM = "filesystem"; - constexpr static StringLiteral KIND_GIT = "git"; - constexpr static StringLiteral KIND_ARTIFACT = "artifact"; - - virtual StringView type_name() const override { return "a registry"; } - virtual View valid_fields() const override; - - virtual Optional> visit_null(Json::Reader&) override; - virtual Optional> visit_object(Json::Reader&, - const Json::Object&) override; - - RegistryImplDeserializer(const Path& configuration_directory) : config_directory(configuration_directory) { } - - Path config_directory; - }; - constexpr StringLiteral RegistryImplDeserializer::KIND; - constexpr StringLiteral RegistryImplDeserializer::BASELINE; - constexpr StringLiteral RegistryImplDeserializer::PATH; - constexpr StringLiteral RegistryImplDeserializer::REPO; - constexpr StringLiteral RegistryImplDeserializer::REFERENCE; - constexpr StringLiteral RegistryImplDeserializer::NAME; - constexpr StringLiteral RegistryImplDeserializer::LOCATION; - constexpr StringLiteral RegistryImplDeserializer::KIND_BUILTIN; - constexpr StringLiteral RegistryImplDeserializer::KIND_FILESYSTEM; - constexpr StringLiteral RegistryImplDeserializer::KIND_GIT; - constexpr StringLiteral RegistryImplDeserializer::KIND_ARTIFACT; - - struct RegistryDeserializer final : Json::IDeserializer - { - constexpr static StringLiteral PACKAGES = "packages"; - - virtual StringView type_name() const override { return "a registry"; } - virtual View valid_fields() const override; - - virtual Optional visit_object(Json::Reader&, const Json::Object&) override; - - explicit RegistryDeserializer(const Path& configuration_directory) : impl_des(configuration_directory) { } - - RegistryImplDeserializer impl_des; - }; - constexpr StringLiteral RegistryDeserializer::PACKAGES; - - View RegistryImplDeserializer::valid_fields() const - { - static const StringView t[] = {KIND, BASELINE, PATH, REPO, REFERENCE, NAME, LOCATION}; - return t; - } - View valid_builtin_fields() - { - static const StringView t[] = { - RegistryImplDeserializer::KIND, - RegistryImplDeserializer::BASELINE, - RegistryDeserializer::PACKAGES, - }; - return t; - } - View valid_filesystem_fields() - { - static const StringView t[] = { - RegistryImplDeserializer::KIND, - RegistryImplDeserializer::BASELINE, - RegistryImplDeserializer::PATH, - RegistryDeserializer::PACKAGES, - }; - return t; - } - View valid_git_fields() - { - static const StringView t[] = { - RegistryImplDeserializer::KIND, - RegistryImplDeserializer::BASELINE, - RegistryImplDeserializer::REPO, - RegistryImplDeserializer::REFERENCE, - RegistryDeserializer::PACKAGES, - }; - return t; - } - View valid_artifact_fields() - { - static const StringView t[] = { - RegistryImplDeserializer::KIND, - RegistryImplDeserializer::NAME, - RegistryImplDeserializer::LOCATION, - }; - return t; - } - - Optional> RegistryImplDeserializer::visit_null(Json::Reader&) - { - return nullptr; - } - - Optional> RegistryImplDeserializer::visit_object(Json::Reader& r, - const Json::Object& obj) - { - static Json::StringDeserializer kind_deserializer{"a registry implementation kind"}; - static Json::StringDeserializer baseline_deserializer{"a baseline"}; - std::string kind; - - r.required_object_field(type_name(), obj, KIND, kind, kind_deserializer); - - std::unique_ptr res; - - if (kind == KIND_BUILTIN) - { - std::string baseline; - r.required_object_field("a builtin registry", obj, BASELINE, baseline, baseline_deserializer); - if (!is_git_commit_sha(baseline)) - { - r.add_generic_error( - "a builtin registry", - "The baseline field of builtin registries must be a git commit SHA (40 lowercase hex characters)"); - } - r.check_for_unexpected_fields(obj, valid_builtin_fields(), "a builtin registry"); - res = std::make_unique(std::move(baseline)); - } - else if (kind == KIND_FILESYSTEM) - { - std::string baseline; - r.optional_object_field(obj, BASELINE, baseline, baseline_deserializer); - r.check_for_unexpected_fields(obj, valid_filesystem_fields(), "a filesystem registry"); - - Path p; - r.required_object_field("a filesystem registry", obj, PATH, p, Json::PathDeserializer::instance); - - res = std::make_unique(config_directory / p, std::move(baseline)); - } - else if (kind == KIND_GIT) - { - r.check_for_unexpected_fields(obj, valid_git_fields(), "a git registry"); - - std::string repo; - Json::StringDeserializer repo_des{"a git repository URL"}; - r.required_object_field("a git registry", obj, REPO, repo, repo_des); - - std::string ref; - Json::StringDeserializer ref_des{"a git reference (for example, a branch)"}; - if (!r.optional_object_field(obj, REFERENCE, ref, ref_des)) - { - ref = "HEAD"; - } - - std::string baseline; - r.required_object_field("a git registry", obj, BASELINE, baseline, baseline_deserializer); - - res = std::make_unique(std::move(repo), std::move(ref), std::move(baseline)); - } - else if (kind == KIND_ARTIFACT) - { - r.check_for_unexpected_fields(obj, valid_artifact_fields(), "an artifacts registry"); - - std::string name; - r.required_object_field("an artifact registry", obj, NAME, name, Json::IdentifierDeserializer::instance); - - std::string location; - Json::StringDeserializer location_des{"an artifacts git repository URL"}; - r.required_object_field("an artifacts registry", obj, LOCATION, location, location_des); - - res = std::make_unique(std::move(name), std::move(location)); - } - else - { - StringLiteral valid_kinds[] = {KIND_BUILTIN, KIND_FILESYSTEM, KIND_GIT, KIND_ARTIFACT}; - r.add_generic_error(type_name(), - "Field \"kind\" did not have an expected value (expected one of: \"", - Strings::join("\", \"", valid_kinds), - "\"; found \"", - kind, - "\")"); - return nullopt; - } - - return std::move(res); // gcc-7 bug workaround redundant move - } - - View RegistryDeserializer::valid_fields() const - { - static const StringView t[] = { - RegistryImplDeserializer::KIND, - RegistryImplDeserializer::BASELINE, - RegistryImplDeserializer::PATH, - RegistryImplDeserializer::REPO, - RegistryImplDeserializer::REFERENCE, - RegistryImplDeserializer::NAME, - RegistryImplDeserializer::LOCATION, - PACKAGES, - }; - return t; - } - - Optional RegistryDeserializer::visit_object(Json::Reader& r, const Json::Object& obj) - { - auto impl = impl_des.visit_object(r, obj); - - if (!impl.has_value()) - { - return nullopt; - } - - static Json::ArrayDeserializer package_names_deserializer{ - "an array of package names"}; - - std::vector packages; - if (impl.get()->get()->kind() != RegistryImplDeserializer::KIND_ARTIFACT) - { - r.required_object_field(type_name(), obj, PACKAGES, packages, package_names_deserializer); - } - - return Registry{std::move(packages), std::move(impl).value_or_exit(VCPKG_LINE_INFO)}; - } - Path relative_path_to_versions(StringView port_name) { char prefix[] = {port_name.byte_at_index(0), '-', '\0'}; @@ -1285,57 +1053,6 @@ Optional RegistryImplementation::get_path_to_baseline_version(const VcpkgP return nullopt; } -// serializers - -Json::Object RegistryImplementation::serialize() const -{ - Json::Object obj; - obj.insert(RegistryImplDeserializer::KIND, Json::Value::string(kind())); - return obj; -} - -Json::Object BuiltinGitRegistry::serialize() const -{ - Json::Object obj; - obj.insert(RegistryImplDeserializer::KIND, Json::Value::string("builtin")); - obj.insert(RegistryImplDeserializer::BASELINE, Json::Value::string(m_baseline_identifier)); - return obj; -} - -Json::Object BuiltinFilesRegistry::serialize() const -{ - Json::Object obj; - obj.insert(RegistryImplDeserializer::KIND, Json::Value::string("builtin")); - 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())); - if (!m_baseline_identifier.empty()) - { - obj.insert(RegistryImplDeserializer::BASELINE, Json::Value::string(m_baseline_identifier)); - } - return obj; -} - -Json::Object ArtifactRegistry::serialize() const -{ - Json::Object obj{RegistryImplementation::serialize()}; - obj.insert(RegistryImplDeserializer::NAME, Json::Value::string(m_name)); - obj.insert(RegistryImplDeserializer::LOCATION, Json::Value::string(m_location)); - return obj; -} - namespace vcpkg { constexpr StringLiteral VersionDbEntryDeserializer::GIT_TREE; @@ -1486,26 +1203,12 @@ namespace vcpkg } } - std::unique_ptr>> - get_registry_implementation_deserializer(const Path& configuration_directory) - { - return std::make_unique(configuration_directory); - } - std::unique_ptr>> get_registry_array_deserializer( - const Path& configuration_directory) - { - return std::make_unique>( - "an array of registries", RegistryDeserializer(configuration_directory)); - } - Registry::Registry(std::vector&& packages, std::unique_ptr&& impl) : packages_(std::move(packages)), implementation_(std::move(impl)) { Checks::check_exit(VCPKG_LINE_INFO, implementation_ != nullptr); } - RegistrySet::RegistrySet() : default_registry_(std::make_unique()) { } - const RegistryImplementation* RegistrySet::registry_for_port(StringView name) const { for (const auto& registry : registries()) @@ -1526,50 +1229,10 @@ namespace vcpkg return impl->get_baseline_version(paths, port_name); } - void RegistrySet::add_registry(Registry&& r) { registries_.push_back(std::move(r)); } - - void RegistrySet::set_default_registry(std::unique_ptr&& r) - { - default_registry_ = std::move(r); - } - void RegistrySet::set_default_registry(std::nullptr_t) { default_registry_.reset(); } - bool RegistrySet::is_default_builtin_registry() const { return default_registry_ && default_registry_->kind() == BuiltinFilesRegistry::s_kind; } - void RegistrySet::set_default_builtin_registry_baseline(StringView baseline) - { - if (auto default_registry = default_registry_.get()) - { - const auto k = default_registry->kind(); - if (k == BuiltinFilesRegistry::s_kind) - { - default_registry_ = std::make_unique(baseline.to_string()); - } - else if (k == BuiltinGitRegistry::s_kind) - { - print2(Color::warning, - R"(warning: attempting to set builtin baseline in both vcpkg.json and vcpkg-configuration.json - (only one of these should be used; the baseline from vcpkg-configuration.json will be used))"); - } - else - { - vcpkg::printf( - Color::warning, - "warning: the default registry has been replaced with a %s registry, but `builtin-baseline` " - "is specified in vcpkg.json. This field will have no effect.\n", - k); - } - } - else - { - print2(Color::warning, - "warning: the default registry has been disabled, but `builtin-baseline` is specified in " - "vcpkg.json. This field will have no effect.\n"); - } - } - bool RegistrySet::has_modifications() const { return !registries_.empty() || !is_default_builtin_registry(); } ExpectedS>> get_builtin_versions(const VcpkgPaths& paths, @@ -1610,4 +1273,25 @@ namespace vcpkg return sv.size() == 40 && std::all_of(sv.begin(), sv.end(), is_lcase_ascii_hex); } + + std::unique_ptr make_builtin_registry() { return std::make_unique(); } + std::unique_ptr make_builtin_registry(std::string baseline) + { + return std::make_unique(std::move(baseline)); + } + std::unique_ptr make_git_registry(std::string repo, + std::string reference, + std::string baseline) + { + return std::make_unique(std::move(repo), std::move(reference), std::move(baseline)); + } + std::unique_ptr make_filesystem_registry(Path path, std::string baseline) + { + return std::make_unique(std::move(path), std::move(baseline)); + } + std::unique_ptr make_artifact_registry(std::string name, std::string location) + { + return std::make_unique(std::move(name), std::move(location)); + } + } diff --git a/src/vcpkg/sourceparagraph.cpp b/src/vcpkg/sourceparagraph.cpp index ebd4d7cc2..f1627f13c 100644 --- a/src/vcpkg/sourceparagraph.cpp +++ b/src/vcpkg/sourceparagraph.cpp @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -993,6 +994,61 @@ namespace vcpkg constexpr StringLiteral ManifestDeserializer::BUILTIN_BASELINE; constexpr StringLiteral ManifestDeserializer::VCPKG_CONFIGURATION; + // Extracts just the configuration information from a manifest object + struct ManifestConfigurationDeserializer final : Json::IDeserializer + { + virtual StringView type_name() const override { return "a manifest"; } + + virtual Optional visit_object(Json::Reader& r, const Json::Object& obj) override + { + Optional x; + ManifestConfiguration& ret = x.emplace(); + if (!r.optional_object_field(obj, + ManifestDeserializer::VCPKG_CONFIGURATION, + ret.config.emplace(), + get_configuration_deserializer())) + { + ret.config = nullopt; + } + if (!r.optional_object_field(obj, + ManifestDeserializer::BUILTIN_BASELINE, + ret.builtin_baseline.emplace(), + BaselineCommitDeserializer::instance)) + { + ret.builtin_baseline = nullopt; + } + return x; + } + + static ManifestConfigurationDeserializer instance; + }; + ManifestConfigurationDeserializer ManifestConfigurationDeserializer::instance; + + ExpectedS parse_manifest_configuration(StringView origin, + const Json::Object& manifest) + { + Json::Reader reader; + + auto res = reader.visit(manifest, ManifestConfigurationDeserializer::instance); + + if (!reader.errors().empty()) + { + std::string ret = "Error: in the manifest "; + Strings::append(ret, origin, "\nwhile obtaining configuration information from the manifest:\n"); + for (auto&& err : reader.errors()) + { + Strings::append(ret, " ", err, "\n"); + } + print2("See ", docs::registries_url, " for more information.\n"); + print2("See ", docs::manifests_url, " for more information.\n"); + return std::move(ret); + } + else + { + return std::move(res).value_or_exit(VCPKG_LINE_INFO); + } + } + SourceControlFile SourceControlFile::clone() const { SourceControlFile ret; @@ -1370,19 +1426,18 @@ namespace vcpkg if (auto configuration = scf.core_paragraph->vcpkg_configuration.get()) { Json::Reader reader; - auto maybe_configuration = reader.visit(*configuration, *vcpkg::make_configuration_deserializer("")); + auto maybe_configuration = reader.visit(*configuration, get_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"); + print2("See ", docs::registries_url, " 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))); + maybe_configuration.value_or_exit(VCPKG_LINE_INFO).serialize()); } obj.insert(ManifestDeserializer::NAME, Json::Value::string(scf.core_paragraph->name)); diff --git a/src/vcpkg/vcpkgpaths.cpp b/src/vcpkg/vcpkgpaths.cpp index 0b6a7dc34..ad0687ed1 100644 --- a/src/vcpkg/vcpkgpaths.cpp +++ b/src/vcpkg/vcpkgpaths.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -57,36 +58,6 @@ namespace namespace vcpkg { - static Configuration deserialize_configuration(const Json::Object& obj, - const VcpkgCmdArguments& args, - const Path& filepath) - { - Json::Reader reader; - auto deserializer = make_configuration_deserializer(filepath.parent_path()); - - auto parsed_config_opt = reader.visit(obj, *deserializer); - if (!reader.errors().empty()) - { - print2(Color::error, "Errors occurred while parsing ", filepath, "\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); - } - - parsed_config_opt.get()->validate_feature_flags(args.feature_flag_settings()); - - return std::move(parsed_config_opt).value_or_exit(VCPKG_LINE_INFO); - } - - struct ManifestAndConfig - { - Path config_directory; - Configuration config; - }; - static std::pair load_manifest(const Filesystem& fs, const Path& manifest_dir) { std::error_code ec; @@ -116,69 +87,129 @@ namespace vcpkg return {std::move(manifest_value.first.object()), std::move(manifest_value.second)}; } - struct ConfigAndPath + static Optional config_from_manifest( + const Path& manifest_path, const Optional>& manifest_doc) { - Path config_directory; - Configuration config; - }; + if (auto manifest = manifest_doc.get()) + { + return parse_manifest_configuration(manifest_path, manifest->first).value_or_exit(VCPKG_LINE_INFO); + } + return nullopt; + } - // doesn't yet implement searching upwards for configurations, nor inheritance of configurations - static ConfigAndPath load_configuration(const Filesystem& fs, - const VcpkgCmdArguments& args, - const Path& vcpkg_root, - const Path& manifest_dir, - const Optional& configuration_from_manifest) + static Optional config_from_json(const Path& config_path, const Filesystem& fs) { - Path config_dir; - if (manifest_dir.empty()) + if (!fs.exists(config_path, VCPKG_LINE_INFO)) { - // classic mode - config_dir = vcpkg_root; - } - else - { - // manifest mode - config_dir = manifest_dir; + return nullopt; } - auto path_to_config = config_dir / "vcpkg-configuration.json"; - if (!fs.exists(path_to_config, IgnoreErrors{})) - { - if (!configuration_from_manifest.has_value()) - { - return {}; - } - - return {std::move(config_dir), - deserialize_configuration( - configuration_from_manifest.value_or_exit(VCPKG_LINE_INFO), args, manifest_dir / "vcpkg.json")}; - } - - if (configuration_from_manifest.has_value()) - { - print2(Color::error, - "Ambiguous vcpkg configuration provided by both manifest and configuration file.\n" - "-- Delete configuration file \"", - path_to_config, - "\"\n" - "-- Or remove \"vcpkg-configuration\" from the manifest file \"", - manifest_dir / "vcpkg.json", - "\"."); - Checks::exit_fail(VCPKG_LINE_INFO); - } - - auto parsed_config = Json::parse_file(VCPKG_LINE_INFO, fs, path_to_config); + auto parsed_config = Json::parse_file(VCPKG_LINE_INFO, fs, config_path); if (!parsed_config.first.is_object()) { - print2(Color::error, - "Failed to parse ", - path_to_config, - ": configuration files must have a top-level object\n"); + print2( + Color::error, "Failed to parse ", config_path, ": configuration files must have a top-level object\n"); + msg::println(Color::error, msg::msgSeeURL, msg::url = docs::registries_url); Checks::exit_fail(VCPKG_LINE_INFO); } - auto config_obj = std::move(parsed_config.first.object()); + const auto& obj = parsed_config.first.object(); - return {std::move(config_dir), deserialize_configuration(config_obj, args, path_to_config)}; + Json::Reader reader; + auto parsed_config_opt = reader.visit(obj, get_configuration_deserializer()); + if (!reader.errors().empty()) + { + print2(Color::error, "Error: while parsing ", config_path, "\n"); + for (auto&& msg : reader.errors()) + print2(" ", msg, '\n'); + + print2("See ", docs::registries_url, " for more information.\n"); + Checks::exit_fail(VCPKG_LINE_INFO); + } + + return parsed_config_opt; + } + + static Configuration merge_validate_configs(Optional&& manifest_data, + const Path& manifest_dir, + Optional&& config_data, + const Path& config_dir, + const VcpkgPaths& paths) + { + Configuration ret; + + if (auto manifest = manifest_data.get()) + { + if (auto config = manifest->config.get()) + { + print2(Color::warning, + "Embedding `vcpkg-configuration` in a manifest file is an EXPERIMENTAL feature.\n"); + + if (manifest->builtin_baseline && config->default_reg) + { + print2(Color::error, + "Error: Specifying vcpkg-configuration.default-registry in a manifest file conflicts with " + "builtin-baseline.\nPlease remove one of these conflicting settings.\n"); + Checks::exit_fail(VCPKG_LINE_INFO); + } + + config->validate_as_active(); + + if (config_data.has_value()) + { + print2(Color::error, + "Ambiguous vcpkg configuration provided by both manifest and configuration file.\n" + "-- Delete configuration file \"", + config_dir / "vcpkg-configuration.json", + "\"\n" + "-- Or remove \"vcpkg-configuration\" from the manifest file \"", + manifest_dir / "vcpkg.json", + "\"."); + Checks::exit_fail(VCPKG_LINE_INFO); + } + + ret = std::move(*config); + } + } + + if (auto config = config_data.get()) + { + config->validate_as_active(); + + ret = std::move(*config); + } + + if (auto manifest = manifest_data.get()) + { + if (auto p_baseline = manifest->builtin_baseline.get()) + { + LockGuardPtr(g_metrics)->track_property("manifest_baseline", "defined"); + if (!is_git_commit_sha(*p_baseline)) + { + LockGuardPtr(g_metrics)->track_property("versioning-error-baseline", "defined"); + Checks::exit_maybe_upgrade(VCPKG_LINE_INFO, + "Error: the top-level builtin-baseline%s was not a valid commit sha: " + "expected 40 lowercase hexadecimal characters.\n%s\n", + Strings::concat(" (", *p_baseline, ')'), + paths.get_current_git_sha_baseline_message()); + } + + if (ret.default_reg) + { + print2(Color::warning, + "warning: attempting to set builtin-baseline in vcpkg.json while overriding the " + "default-registry in vcpkg-configuration.json.\n The default-registry from " + "vcpkg-configuration.json will be used."); + } + else + { + auto& default_reg = ret.default_reg.emplace(); + default_reg.kind = "builtin"; + default_reg.baseline = std::move(*p_baseline); + } + } + } + + return ret; } namespace details @@ -265,7 +296,9 @@ namespace vcpkg Optional> m_manifest_doc; Path m_manifest_path; + Path m_config_dir; Configuration m_config; + std::unique_ptr m_registry_set; Downloads::DownloadManager m_download_manager; @@ -448,6 +481,7 @@ namespace vcpkg if (manifest_root_dir.empty()) { + m_pimpl->m_config_dir = root; if (!m_pimpl->m_readonly) { m_pimpl->installed = @@ -458,6 +492,7 @@ namespace vcpkg { Debug::print("Using manifest-root: ", manifest_root_dir, '\n'); + m_pimpl->m_config_dir = manifest_root_dir; m_pimpl->installed = process_output_directory( filesystem, args.install_root_dir.get(), manifest_root_dir / "vcpkg_installed"); @@ -486,59 +521,23 @@ namespace vcpkg m_pimpl->m_manifest_path = manifest_root_dir / "vcpkg.json"; } - vcpkg::Optional configuration_from_manifest; - if (auto manifest = m_pimpl->m_manifest_doc.get()) { - auto manifest_obj = manifest->first; - if (auto config_obj = manifest_obj.get("vcpkg-configuration")) - { - print2(Color::warning, - "Embedding `vcpkg-configuration` in a manifest file is an EXPERIMENTAL feature.\n" - "Loading configuration from: ", - m_pimpl->m_manifest_path, - "\n"); + auto maybe_manifest_config = config_from_manifest(m_pimpl->m_manifest_path, m_pimpl->m_manifest_doc); + auto maybe_config_json = config_from_json(m_pimpl->m_config_dir / "vcpkg-configuration.json", filesystem); - if (!config_obj->is_object()) - { - print2(Color::error, - "Failed to parse ", - m_pimpl->m_manifest_path, - ": vcpkg-configuration must be an object\n"); - Checks::exit_fail(VCPKG_LINE_INFO); - } + m_pimpl->m_config = merge_validate_configs(std::move(maybe_manifest_config), + manifest_root_dir, + std::move(maybe_config_json), + m_pimpl->m_config_dir, + *this); - configuration_from_manifest = make_optional(config_obj->object()); - } - } - auto config_file = load_configuration(filesystem, args, root, manifest_root_dir, configuration_from_manifest); - if (auto manifest = m_pimpl->m_manifest_doc.get()) - { - if (auto p_baseline = manifest->first.get("builtin-baseline")) - { - LockGuardPtr(g_metrics)->track_property("manifest_baseline", "defined"); - if (!p_baseline->is_string() || !is_git_commit_sha(p_baseline->string())) - { - std::string baseline_in_error; - if (p_baseline->is_string()) - { - baseline_in_error = Strings::concat(" (", p_baseline->string(), ')'); - } - LockGuardPtr(g_metrics)->track_property("versioning-error-baseline", "defined"); - Checks::exit_maybe_upgrade(VCPKG_LINE_INFO, - "Error: the top-level builtin-baseline%s was not a valid commit sha: " - "expected 40 lowercase hexadecimal characters.\n%s\n", - baseline_in_error, - get_current_git_sha_baseline_message()); - } - - config_file.config.registry_set.set_default_builtin_registry_baseline(p_baseline->string()); - } + m_pimpl->m_registry_set = m_pimpl->m_config.instantiate_registry_set(m_pimpl->m_config_dir); } // metrics from configuration { - auto default_registry = config_file.config.registry_set.default_registry(); - auto other_registries = config_file.config.registry_set.registries(); + auto default_registry = m_pimpl->m_registry_set->default_registry(); + auto other_registries = m_pimpl->m_registry_set->registries(); LockGuardPtr metrics(g_metrics); if (default_registry) { @@ -561,9 +560,6 @@ namespace vcpkg } } - config_root_dir = std::move(config_file.config_directory); - m_pimpl->m_config = std::move(config_file.config); - m_pimpl->buildtrees = maybe_get_tmp_path(args.buildtrees_root_dir.get(), "buildtrees", "blds", VCPKG_LINE_INFO); m_pimpl->packages = maybe_get_tmp_path(args.packages_root_dir.get(), "packages", "pkgs", VCPKG_LINE_INFO); @@ -1242,10 +1238,10 @@ namespace vcpkg } } - const Configuration& VcpkgPaths::get_configuration() const { return m_pimpl->m_config; } - void VcpkgPaths::set_builtin_baseline(const std::string& baseline) const + const RegistrySet& VcpkgPaths::get_registry_set() const { - m_pimpl->m_config.registry_set.set_default_builtin_registry_baseline(baseline); + Checks::check_exit(VCPKG_LINE_INFO, m_pimpl->m_registry_set != nullptr); + return *m_pimpl->m_registry_set; } const Downloads::DownloadManager& VcpkgPaths::get_download_manager() const { return m_pimpl->m_download_manager; }