diff --git a/include/vcpkg/base/jsonreader.h b/include/vcpkg/base/jsonreader.h index 7e285108cd..f503202699 100644 --- a/include/vcpkg/base/jsonreader.h +++ b/include/vcpkg/base/jsonreader.h @@ -160,6 +160,24 @@ namespace vcpkg::Json } } + // returns whether key \in obj + template + bool optional_object_field(const Object& obj, + StringView key, + Optional& place, + IDeserializer& visitor) + { + if (auto value = obj.get(key)) + { + visit_in_key(*value, key, place.emplace(), visitor); + return true; + } + else + { + return false; + } + } + template Optional visit(const Value& value, IDeserializer& visitor) { diff --git a/include/vcpkg/dependencies.h b/include/vcpkg/dependencies.h index 9e4d72e694..7190e48fa5 100644 --- a/include/vcpkg/dependencies.h +++ b/include/vcpkg/dependencies.h @@ -196,6 +196,12 @@ namespace vcpkg::Dependencies const StatusParagraphs& status_db, const CreateInstallPlanOptions& options = {Triplet{}}); + enum class DependDefaults + { + NO, + YES, + }; + ExpectedS create_versioned_install_plan(const PortFileProvider::IVersionedPortfileProvider& vprovider, const PortFileProvider::IBaselineProvider& bprovider, const PortFileProvider::IOverlayProvider& oprovider, @@ -204,7 +210,8 @@ namespace vcpkg::Dependencies const std::vector& overrides, const PackageSpec& toplevel, Triplet host_triplet, - UnsupportedPortAction unsupported_port_action); + UnsupportedPortAction unsupported_port_action, + DependDefaults depend_defaults); void print_plan(const ActionPlan& action_plan, const bool is_recursive = true, const Path& builtin_ports_dir = {}); } diff --git a/include/vcpkg/packagespec.h b/include/vcpkg/packagespec.h index a9b9ee6cbf..f5942ec0e4 100644 --- a/include/vcpkg/packagespec.h +++ b/include/vcpkg/packagespec.h @@ -149,10 +149,14 @@ namespace vcpkg struct Dependency { std::string name; + + // When created through parsing a manifest, `features` will be pre-normalized (containing 'core' and 'default' + // as appropriate) std::vector features; PlatformExpression::Expr platform; DependencyConstraint constraint; bool host = false; + Optional default_features; Json::Object extra_info; diff --git a/include/vcpkg/sourceparagraph.h b/include/vcpkg/sourceparagraph.h index 5c541bd1b1..b1bef046bf 100644 --- a/include/vcpkg/sourceparagraph.h +++ b/include/vcpkg/sourceparagraph.h @@ -64,6 +64,7 @@ namespace vcpkg VersionScheme version_scheme = VersionScheme::String; std::string raw_version; int port_version = 0; + bool depend_defaults = true; std::vector description; std::vector summary; std::vector maintainers; diff --git a/src/vcpkg-test/dependencies.cpp b/src/vcpkg-test/dependencies.cpp index e9db3d293f..05dedbaca0 100644 --- a/src/vcpkg-test/dependencies.cpp +++ b/src/vcpkg-test/dependencies.cpp @@ -92,6 +92,8 @@ struct MockVersionedPortfileProvider : PortFileProvider::IVersionedPortfileProvi } }; +using Dependencies::DependDefaults; + template T unwrap(ExpectedS e) { @@ -242,7 +244,8 @@ static ExpectedS create_versioned_install_plan( overrides, toplevel, Test::ARM_UWP, - Dependencies::UnsupportedPortAction::Error); + Dependencies::UnsupportedPortAction::Error, + DependDefaults::YES); } static ExpectedS create_versioned_install_plan( @@ -262,7 +265,30 @@ static ExpectedS create_versioned_install_plan( overrides, toplevel, Test::ARM_UWP, - Dependencies::UnsupportedPortAction::Error); + Dependencies::UnsupportedPortAction::Error, + Dependencies::DependDefaults::YES); +} + +static ExpectedS create_versioned_install_plan( + const PortFileProvider::IOverlayProvider& oprovider, + const std::vector& deps, + const PackageSpec& toplevel, + DependDefaults depend_defaults) +{ + MockVersionedPortfileProvider vp; + MockCMakeVarProvider var_provider; + MockBaselineProvider bp; + + return vcpkg::Dependencies::create_versioned_install_plan(vp, + bp, + oprovider, + var_provider, + deps, + {}, + toplevel, + Test::ARM_UWP, + Dependencies::UnsupportedPortAction::Error, + depend_defaults); } TEST_CASE ("basic version install single", "[versionplan]") @@ -1522,11 +1548,118 @@ TEST_CASE ("version install default features", "[versionplan]") MockBaselineProvider bp; bp.v["a"] = {"1", 0}; - auto install_plan = - unwrap(create_versioned_install_plan(vp, bp, var_provider, {Dependency{"a"}}, {}, toplevel_spec())); + SECTION ("toplevel requires defaults") + { + auto install_plan = + unwrap(create_versioned_install_plan(vp, bp, var_provider, {Dependency{"a"}}, {}, toplevel_spec())); - REQUIRE(install_plan.size() == 1); - check_name_and_version(install_plan.install_actions[0], "a", {"1", 0}, {"x"}); + REQUIRE(install_plan.size() == 1); + check_name_and_version(install_plan.install_actions[0], "a", {"1", 0}, {"x"}); + } + + SECTION ("default-features false") + { + auto install_plan = unwrap(create_versioned_install_plan( + vp, bp, var_provider, {Dependency{"a", {"core"}, {}, {}, false, false}}, {}, toplevel_spec())); + + REQUIRE(install_plan.size() == 1); + check_name_and_version(install_plan.install_actions[0], "a", {"1", 0}, {}); + } + + SECTION ("default-features true") + { + auto install_plan = unwrap(create_versioned_install_plan( + vp, bp, var_provider, {Dependency{"a", {"core", "default"}, {}, {}, false, false}}, {}, toplevel_spec())); + + REQUIRE(install_plan.size() == 1); + check_name_and_version(install_plan.install_actions[0], "a", {"1", 0}, {"x"}); + } + + SECTION ("default-features true 2") + { + auto install_plan = unwrap(create_versioned_install_plan( + vp, bp, var_provider, {Dependency{"a", {"core", "default"}, {}, {}, false, true}}, {}, toplevel_spec())); + + REQUIRE(install_plan.size() == 1); + check_name_and_version(install_plan.install_actions[0], "a", {"1", 0}, {"x"}); + } +} + +TEST_CASE ("version install depend-defaults", "[versionplan]") +{ + MockOverlayProvider op; + auto& a_scf = op.emplace("a"); + a_scf.source_control_file->core_paragraph->dependencies.push_back(Dependency{"b"}); + auto& b_scf = op.emplace("b"); + b_scf.source_control_file->core_paragraph->default_features.emplace_back("0"); + b_scf.source_control_file->feature_paragraphs.emplace_back(make_fpgh("0")); + auto& c_scf = op.emplace("c"); + auto& c0 = c_scf.source_control_file->feature_paragraphs.emplace_back(make_fpgh("0")); + c0->dependencies.push_back(Dependency{"b"}); + + auto& d_scf = op.emplace("d"); + d_scf.source_control_file->core_paragraph->dependencies.push_back(Dependency{"b", {"core"}}); + + auto& e_scf = op.emplace("e"); + e_scf.source_control_file->core_paragraph->dependencies.push_back(Dependency{"b"}); + e_scf.source_control_file->core_paragraph->depend_defaults = false; + + SECTION ("toplevel depend-defaults true") + { + auto install_plan = + unwrap(create_versioned_install_plan(op, {Dependency{"b"}}, toplevel_spec(), DependDefaults::YES)); + REQUIRE(install_plan.size() == 1); + check_name_and_features(install_plan.install_actions[0], "b", {"0"}); + } + + SECTION ("toplevel depend-defaults false") + { + auto install_plan = + unwrap(create_versioned_install_plan(op, {Dependency{"b"}}, toplevel_spec(), DependDefaults::NO)); + REQUIRE(install_plan.size() == 1); + check_name_and_features(install_plan.install_actions[0], "b", {}); + + // a -> b[default], so depend-defaults should not suppress it + install_plan = + unwrap(create_versioned_install_plan(op, {Dependency{"a"}}, toplevel_spec(), DependDefaults::NO)); + REQUIRE(install_plan.size() == 2); + check_name_and_features(install_plan.install_actions[0], "b", {"0"}); + } + + SECTION ("toplevel depend-defaults true (transitive)") + { + auto install_plan = + unwrap(create_versioned_install_plan(op, {Dependency{"d"}}, toplevel_spec(), DependDefaults::YES)); + REQUIRE(install_plan.size() == 2); + check_name_and_features(install_plan.install_actions[0], "b", {"0"}); + } + + SECTION ("toplevel depend-defaults false (transitive)") + { + auto install_plan = + unwrap(create_versioned_install_plan(op, {Dependency{"d"}}, toplevel_spec(), DependDefaults::NO)); + REQUIRE(install_plan.size() == 2); + check_name_and_features(install_plan.install_actions[0], "b", {}); + } + + SECTION ("transitive depend-defaults false") + { + SECTION ("toplevel false") + { + auto install_plan = + unwrap(create_versioned_install_plan(op, {Dependency{"e"}}, toplevel_spec(), DependDefaults::NO)); + REQUIRE(install_plan.size() == 2); + check_name_and_features(install_plan.install_actions[0], "b", {}); + } + + SECTION ("toplevel core") + { + auto install_plan = unwrap(create_versioned_install_plan( + op, {Dependency{"e"}, Dependency{"b", {"core"}}}, toplevel_spec(), DependDefaults::YES)); + REQUIRE(install_plan.size() == 2); + check_name_and_features(install_plan.install_actions[0], "b", {}); + } + } } TEST_CASE ("version dont install default features", "[versionplan]") diff --git a/src/vcpkg-test/manifests.cpp b/src/vcpkg-test/manifests.cpp index 460bac0557..ee0a3edaed 100644 --- a/src/vcpkg-test/manifests.cpp +++ b/src/vcpkg-test/manifests.cpp @@ -79,6 +79,7 @@ TEST_CASE ("manifest construct minimum", "[manifests]") REQUIRE(pgh.core_paragraph->name == "zlib"); REQUIRE(pgh.core_paragraph->raw_version == "1.2.8"); + REQUIRE(pgh.core_paragraph->depend_defaults == true); REQUIRE(pgh.core_paragraph->maintainers.empty()); REQUIRE(pgh.core_paragraph->contacts.is_empty()); REQUIRE(pgh.core_paragraph->summary.empty()); @@ -715,6 +716,7 @@ TEST_CASE ("manifest construct maximum", "[manifests]") "summary": "d", "description": "d", "builtin-baseline": "123", + "depend-defaults": false, "dependencies": ["bd"], "default-features": ["df"], "features": { @@ -729,11 +731,11 @@ TEST_CASE ("manifest construct maximum", "[manifests]") "name": "order.white-lotus", "features": [ "the-ancient-ways" ], "platform": "!(windows & arm)" - }, - { - "$extra": [], - "$my": [], - "name": "tea" + }, + { + "$extra": [], + "$my": [], + "name": "tea" } ] }, @@ -767,6 +769,7 @@ TEST_CASE ("manifest construct maximum", "[manifests]") REQUIRE(contact_a_aa->string() == "aa"); REQUIRE(pgh.core_paragraph->summary.size() == 1); REQUIRE(pgh.core_paragraph->summary[0] == "d"); + REQUIRE(pgh.core_paragraph->depend_defaults == false); REQUIRE(pgh.core_paragraph->description.size() == 1); REQUIRE(pgh.core_paragraph->description[0] == "d"); REQUIRE(pgh.core_paragraph->dependencies.size() == 1); diff --git a/src/vcpkg-test/plan.cpp b/src/vcpkg-test/plan.cpp index bf1d3b4b15..fc255c8273 100644 --- a/src/vcpkg-test/plan.cpp +++ b/src/vcpkg-test/plan.cpp @@ -756,6 +756,79 @@ TEST_CASE ("install with default features", "[plan]") features_check(install_plan.install_actions.at(1), "a", {"0", "core"}); } +TEST_CASE ("install with depend-defaults false", "[plan]") +{ + StatusParagraphs status_db; + + PackageSpecMap spec_map; + auto a_spec = spec_map.emplace("a", "b"); + auto b_spec = spec_map.emplace("b", "", {{"0", ""}}, {"0"}); + auto c_spec = spec_map.emplace("c", "", {{"0", "b"}}, {"0"}); + auto d_spec = spec_map.emplace("d", "b"); + spec_map.map["d"].source_control_file->core_paragraph->depend_defaults = false; + spec_map.map["d"].source_control_file->core_paragraph->dependencies[0].default_features = true; + + PortFileProvider::MapPortFileProvider map_port{spec_map.map}; + MockCMakeVarProvider var_provider; + + std::vector full_package_specs{ + FullPackageSpec{a_spec, {"core"}}, + FullPackageSpec{b_spec, {"core"}}, + }; + + std::vector full_package_specs2{ + FullPackageSpec{c_spec, {"default", "core"}}, + FullPackageSpec{b_spec, {"core"}}, + }; + + std::vector full_package_specs_d{ + FullPackageSpec{d_spec, {"core"}}, + }; + + // Install "a" and then "b" _should_ install default features + SECTION ("depend-defaults true from core") + { + auto install_plan = Dependencies::create_feature_install_plan(map_port, var_provider, full_package_specs, {}); + REQUIRE(install_plan.size() == 2); + features_check(install_plan.install_actions.at(0), "b", {"0", "core"}); + features_check(install_plan.install_actions.at(1), "a", {"core"}); + } + + SECTION ("depend-defaults true from feature") + { + auto install_plan = Dependencies::create_feature_install_plan(map_port, var_provider, full_package_specs2, {}); + REQUIRE(install_plan.size() == 2); + features_check(install_plan.install_actions.at(0), "b", {"0", "core"}); + features_check(install_plan.install_actions.at(1), "c", {"0", "core"}); + } + + SECTION ("depend-defaults false overridden from dependency") + { + auto install_plan = Dependencies::create_feature_install_plan(map_port, var_provider, full_package_specs_d, {}); + REQUIRE(install_plan.size() == 2); + features_check(install_plan.install_actions.at(0), "b", {"0", "core"}); + features_check(install_plan.install_actions.at(1), "d", {"core"}); + } + + // now, disable the implicit default dependency from `a` and `c[0]` + SECTION ("depend-defaults false from core") + { + spec_map.map["a"].source_control_file->core_paragraph->depend_defaults = false; + auto install_plan = Dependencies::create_feature_install_plan(map_port, var_provider, full_package_specs, {}); + REQUIRE(install_plan.size() == 2); + features_check(install_plan.install_actions.at(0), "b", {"core"}); + features_check(install_plan.install_actions.at(1), "a", {"core"}); + } + SECTION ("depend-defaults false from feature") + { + spec_map.map["c"].source_control_file->core_paragraph->depend_defaults = false; + auto install_plan = Dependencies::create_feature_install_plan(map_port, var_provider, full_package_specs2, {}); + REQUIRE(install_plan.size() == 2); + features_check(install_plan.install_actions.at(0), "b", {"core"}); + features_check(install_plan.install_actions.at(1), "c", {"0", "core"}); + } +} + TEST_CASE ("upgrade with default features 1", "[plan]") { std::vector> pghs; diff --git a/src/vcpkg/dependencies.cpp b/src/vcpkg/dependencies.cpp index c74287cfb1..50c7497fc9 100644 --- a/src/vcpkg/dependencies.cpp +++ b/src/vcpkg/dependencies.cpp @@ -123,12 +123,18 @@ namespace vcpkg::Dependencies } const std::vector* qualified_deps = &maybe_qualified_deps.value_or_exit(VCPKG_LINE_INFO); + const auto depend_defaults = scfl.source_control_file->core_paragraph->depend_defaults; + std::vector dep_list; if (auto vars = maybe_vars.get()) { // Qualified dependency resolution is available - auto fullspec_list = filter_dependencies( - *qualified_deps, m_spec.triplet(), host_triplet, *vars, ImplicitDefault::YES); + auto fullspec_list = + filter_dependencies(*qualified_deps, + m_spec.triplet(), + host_triplet, + *vars, + depend_defaults ? ImplicitDefault::YES : ImplicitDefault::NO); for (auto&& fspec : fullspec_list) { @@ -145,7 +151,9 @@ namespace vcpkg::Dependencies { if (dep.platform.is_empty()) { - dep.to_full_spec(m_spec.triplet(), host_triplet, ImplicitDefault::YES) + dep.to_full_spec(m_spec.triplet(), + host_triplet, + depend_defaults ? ImplicitDefault::YES : ImplicitDefault::NO) .expand_fspecs_to(dep_list); } else @@ -1193,12 +1201,14 @@ namespace vcpkg::Dependencies const IBaselineProvider& base_provider, const PortFileProvider::IOverlayProvider& oprovider, const CMakeVars::CMakeVarProvider& var_provider, - Triplet host_triplet) + Triplet host_triplet, + DependDefaults depend_defaults) : m_ver_provider(ver_provider) , m_base_provider(base_provider) , m_o_provider(oprovider) , m_var_provider(var_provider) , m_host_triplet(host_triplet) + , m_depend_defaults(depend_defaults == DependDefaults::YES) { } @@ -1215,6 +1225,7 @@ namespace vcpkg::Dependencies const PortFileProvider::IOverlayProvider& m_o_provider; const CMakeVars::CMakeVarProvider& m_var_provider; const Triplet m_host_triplet; + const bool m_depend_defaults; struct DepSpec { @@ -1259,7 +1270,7 @@ namespace vcpkg::Dependencies Optional> relaxed_semver; Optional> date; std::set requested_features; - bool default_features = true; + bool default_features; bool user_requested = false; VersionSchemeInfo* get_node(const Version& ver); @@ -1271,7 +1282,7 @@ namespace vcpkg::Dependencies // replaces the current entry for the scheme VersionSchemeInfo& emplace_node(VersionScheme scheme, const Version& ver); - PackageNode() = default; + explicit PackageNode(bool default_features) : default_features(default_features) { } PackageNode(const PackageNode&) = delete; PackageNode(PackageNode&&) = default; PackageNode& operator=(const PackageNode&) = delete; @@ -1307,6 +1318,7 @@ namespace vcpkg::Dependencies // the following functions will add stuff recursively void require_dependency(std::pair& ref, const Dependency& dep, + bool depend_defaults, const std::string& origin); void require_port_version(std::pair& graph_entry, const Version& ver, @@ -1475,7 +1487,10 @@ namespace vcpkg::Dependencies } else { - require_dependency(dep_node, dep, ref.first.name()); + require_dependency(dep_node, + dep, + vsi.scfl->source_control_file->core_paragraph->depend_defaults, + ref.first.name()); } p.first->second.emplace_back(dep_spec, "core"); @@ -1488,6 +1503,7 @@ namespace vcpkg::Dependencies void VersionedPackageGraph::require_dependency(std::pair& ref, const Dependency& dep, + bool depend_defaults, const std::string& origin) { const auto maybe_overlay = m_o_provider.get_control_file(ref.first.name()); @@ -1521,7 +1537,7 @@ namespace vcpkg::Dependencies require_port_feature(ref, f, origin); } - if (Util::find(dep.features, StringView{"core"}) == dep.features.end()) + if (depend_defaults && Util::find(dep.features, StringView{"core"}) == dep.features.end()) { require_port_defaults(ref, origin); } @@ -1643,7 +1659,12 @@ namespace vcpkg::Dependencies std::pair& VersionedPackageGraph::emplace_package( const PackageSpec& spec) { - return *m_graph.emplace(spec, PackageNode{}).first; + auto it = m_graph.find(spec); + if (it == m_graph.end()) + { + return *m_graph.emplace(spec, m_depend_defaults).first; + } + return *it; } Optional VersionedPackageGraph::dep_to_version(const std::string& name, const DependencyConstraint& dc) @@ -2055,9 +2076,10 @@ namespace vcpkg::Dependencies const std::vector& overrides, const PackageSpec& toplevel, Triplet host_triplet, - UnsupportedPortAction unsupported_port_action) + UnsupportedPortAction unsupported_port_action, + DependDefaults depend_defaults) { - VersionedPackageGraph vpg(provider, bprovider, oprovider, var_provider, host_triplet); + VersionedPackageGraph vpg(provider, bprovider, oprovider, var_provider, host_triplet, depend_defaults); for (auto&& o : overrides) vpg.add_override(o.name, {o.version, o.port_version}); vpg.add_roots(deps, toplevel); diff --git a/src/vcpkg/install.cpp b/src/vcpkg/install.cpp index 592c018458..34d8c44f89 100644 --- a/src/vcpkg/install.cpp +++ b/src/vcpkg/install.cpp @@ -1011,15 +1011,17 @@ namespace vcpkg::Install } auto oprovider = PortFileProvider::make_overlay_provider(paths, extended_overlay_ports); PackageSpec toplevel{manifest_scf.core_paragraph->name, default_triplet}; - auto install_plan = Dependencies::create_versioned_install_plan(*verprovider, - *baseprovider, - *oprovider, - var_provider, - dependencies, - manifest_scf.core_paragraph->overrides, - toplevel, - host_triplet, - unsupported_port_action) + auto install_plan = Dependencies::create_versioned_install_plan( + *verprovider, + *baseprovider, + *oprovider, + var_provider, + dependencies, + manifest_scf.core_paragraph->overrides, + toplevel, + host_triplet, + unsupported_port_action, + Util::Enum::to_enum(manifest_scf.core_paragraph->depend_defaults)) .value_or_exit(VCPKG_LINE_INFO); for (const auto& warning : install_plan.warnings) { diff --git a/src/vcpkg/packagespec.cpp b/src/vcpkg/packagespec.cpp index c2a67943a2..c6eb994bd2 100644 --- a/src/vcpkg/packagespec.cpp +++ b/src/vcpkg/packagespec.cpp @@ -48,7 +48,7 @@ namespace vcpkg "Error: Platform qualifier is not allowed in this context"); DECLARE_AND_REGISTER_MESSAGE(IllegalFeatures, (), "", "Error: List of features is not allowed in this contect"); - static InternalFeatureSet normalize_feature_list(View fs, ImplicitDefault id) + static InternalFeatureSet normalize_feature_list(View fs, bool implicit_default) { InternalFeatureSet ret; bool core = false; @@ -64,7 +64,7 @@ namespace vcpkg if (!core) { ret.emplace_back("core"); - if (id == ImplicitDefault::YES) + if (implicit_default) { ret.emplace_back("default"); } @@ -81,7 +81,7 @@ namespace vcpkg const Triplet t = triplet ? Triplet::from_canonical_name(*triplet.get()) : default_triplet; const View fs = !features.get() ? View{} : *features.get(); - return FullPackageSpec{{name, t}, normalize_feature_list(fs, id)}; + return FullPackageSpec{{name, t}, normalize_feature_list(fs, id == ImplicitDefault::YES)}; } ExpectedS ParsedQualifiedSpecifier::to_package_spec(Triplet default_triplet) const @@ -286,7 +286,8 @@ namespace vcpkg FullPackageSpec Dependency::to_full_spec(Triplet target, Triplet host_triplet, ImplicitDefault id) const { - return FullPackageSpec{{name, host ? host_triplet : target}, normalize_feature_list(features, id)}; + return FullPackageSpec{{name, host ? host_triplet : target}, + normalize_feature_list(features, default_features.value_or(id == ImplicitDefault::YES))}; } bool operator==(const Dependency& lhs, const Dependency& rhs) diff --git a/src/vcpkg/sourceparagraph.cpp b/src/vcpkg/sourceparagraph.cpp index ab26515267..bd3d6ece73 100644 --- a/src/vcpkg/sourceparagraph.cpp +++ b/src/vcpkg/sourceparagraph.cpp @@ -561,12 +561,7 @@ namespace vcpkg r.required_object_field(type_name(), obj, NAME, dep.name, Json::PackageNameDeserializer::instance); r.optional_object_field(obj, FEATURES, dep.features, arr_id_d); - bool default_features = true; - r.optional_object_field(obj, DEFAULT_FEATURES, default_features, Json::BooleanDeserializer::instance); - if (!default_features) - { - dep.features.push_back("core"); - } + r.optional_object_field(obj, DEFAULT_FEATURES, dep.default_features, Json::BooleanDeserializer::instance); r.optional_object_field(obj, HOST, dep.host, Json::BooleanDeserializer::instance); r.optional_object_field(obj, PLATFORM, dep.platform, PlatformExprDeserializer::instance); @@ -1072,12 +1067,13 @@ namespace vcpkg constexpr static StringLiteral DEV_DEPENDENCIES = "dev-dependencies"; constexpr static StringLiteral FEATURES = "features"; constexpr static StringLiteral DEFAULT_FEATURES = "default-features"; + constexpr static StringLiteral DEPEND_DEFAULTS = "depend-defaults"; 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 valid_fields() const override + virtual View valid_fields() const override { static const StringView u[] = { NAME, @@ -1089,6 +1085,7 @@ namespace vcpkg DOCUMENTATION, LICENSE, DEPENDENCIES, + DEPEND_DEFAULTS, DEV_DEPENDENCIES, FEATURES, DEFAULT_FEATURES, @@ -1125,6 +1122,7 @@ namespace vcpkg spgh->version_scheme = schemed_version.scheme; spgh->port_version = schemed_version.version.port_version(); + r.optional_object_field(obj, DEPEND_DEFAULTS, spgh->depend_defaults, Json::BooleanDeserializer::instance); r.optional_object_field(obj, MAINTAINERS, spgh->maintainers, Json::ParagraphDeserializer::instance); r.optional_object_field(obj, CONTACTS, spgh->contacts, ContactsDeserializer::instance); r.optional_object_field(obj, SUMMARY, spgh->summary, Json::ParagraphDeserializer::instance); @@ -1180,6 +1178,30 @@ namespace vcpkg Checks::exit_with_message(VCPKG_LINE_INFO, maybe_error->error); } + // This ugly fixup is needed because Dependency is serving two roles: one as a parse tree (context + // dependent) and another as a context-free description of a package dependency. This fixup applies the + // context from the parent SourceControlFile onto all child Dependencies. + auto fixup_dependencies = [&spgh](Span deps) { + for (auto&& dep : deps) + { + auto it = Util::find(dep.features, "core"); + if (it == dep.features.end()) + { + dep.features.push_back("core"); + it = Util::find(dep.features, "default"); + if (dep.default_features.value_or(spgh->depend_defaults) && it == dep.features.end()) + { + dep.features.push_back("default"); + } + } + } + }; + fixup_dependencies(spgh->dependencies); + for (auto&& fpgh : control_file->feature_paragraphs) + { + fixup_dependencies(fpgh->dependencies); + } + return std::move(control_file); // gcc-7 bug workaround redundant move } @@ -1522,7 +1544,7 @@ namespace vcpkg static bool is_dependency_trivial(const Dependency& dep) { return dep.features.empty() && dep.platform.is_empty() && dep.extra_info.is_empty() && - dep.constraint.type == VersionConstraintKind::None && !dep.host; + dep.constraint.type == VersionConstraintKind::None && !dep.host && !dep.default_features.has_value(); } static Json::Object serialize_manifest_impl(const SourceControlFile& scf, bool debug) @@ -1586,11 +1608,16 @@ namespace vcpkg auto features_copy = dep.features; auto core_it = std::find(features_copy.begin(), features_copy.end(), "core"); + auto default_features = dep.default_features; if (core_it != features_copy.end()) { - dep_obj.insert(DependencyDeserializer::DEFAULT_FEATURES, Json::Value::boolean(false)); + default_features = false; features_copy.erase(core_it); } + if (auto b = default_features.get()) + { + dep_obj.insert(DependencyDeserializer::DEFAULT_FEATURES, Json::Value::boolean(*b)); + } serialize_optional_array(dep_obj, DependencyDeserializer::FEATURES, features_copy); serialize_optional_string(dep_obj, DependencyDeserializer::PLATFORM, to_string(dep.platform)); @@ -1683,6 +1710,11 @@ namespace vcpkg Json::Value::string(scf.core_paragraph->builtin_baseline.value_or_exit(VCPKG_LINE_INFO))); } + if (!scf.core_paragraph->depend_defaults || debug) + { + obj.insert(ManifestDeserializer::DEPEND_DEFAULTS, Json::Value::boolean(false)); + } + if (!scf.core_paragraph->dependencies.empty() || debug) { auto& deps = obj.insert(ManifestDeserializer::DEPENDENCIES, Json::Array());