diff --git a/.gitignore b/.gitignore index c5b108250ff980..ba773a8d9c74a5 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ /.vscode +/build* +*.swp diff --git a/azure-pipelines/e2e_ports/overlays/vcpkg-default-features-fail-require-other-feature/portfile.cmake b/azure-pipelines/e2e_ports/overlays/vcpkg-default-features-fail-require-other-feature/portfile.cmake new file mode 100644 index 00000000000000..a30c43c1b94fc7 --- /dev/null +++ b/azure-pipelines/e2e_ports/overlays/vcpkg-default-features-fail-require-other-feature/portfile.cmake @@ -0,0 +1,7 @@ +set(VCPKG_POLICY_EMPTY_PACKAGE enabled) +if("default-feature" IN_LIST FEATURES) + message(FATAL_ERROR "the default feature was depended upon") +endif() +if(NOT "success" IN_LIST FEATURES) + message(FATAL_ERROR "the success feature was not depended upon") +endif() diff --git a/azure-pipelines/e2e_ports/overlays/vcpkg-default-features-fail-require-other-feature/vcpkg.json b/azure-pipelines/e2e_ports/overlays/vcpkg-default-features-fail-require-other-feature/vcpkg.json new file mode 100644 index 00000000000000..b58e41bce589e2 --- /dev/null +++ b/azure-pipelines/e2e_ports/overlays/vcpkg-default-features-fail-require-other-feature/vcpkg.json @@ -0,0 +1,15 @@ +{ + "name": "vcpkg-default-features-fail-require-other-feature", + "version": "0", + "default-features": [ + "default-feature" + ], + "features": { + "default-feature": { + "description": "" + }, + "success": { + "description": "" + } + } +} diff --git a/azure-pipelines/e2e_ports/overlays/vcpkg-default-features-fail/portfile.cmake b/azure-pipelines/e2e_ports/overlays/vcpkg-default-features-fail/portfile.cmake new file mode 100644 index 00000000000000..1c2d5baafd76f8 --- /dev/null +++ b/azure-pipelines/e2e_ports/overlays/vcpkg-default-features-fail/portfile.cmake @@ -0,0 +1,4 @@ +set(VCPKG_POLICY_EMPTY_PACKAGE enabled) +if("default-feature" IN_LIST FEATURES) + message(FATAL_ERROR "the default feature was depended upon") +endif() diff --git a/azure-pipelines/e2e_ports/overlays/vcpkg-default-features-fail/vcpkg.json b/azure-pipelines/e2e_ports/overlays/vcpkg-default-features-fail/vcpkg.json new file mode 100644 index 00000000000000..0146fbfe5e357b --- /dev/null +++ b/azure-pipelines/e2e_ports/overlays/vcpkg-default-features-fail/vcpkg.json @@ -0,0 +1,12 @@ +{ + "name": "vcpkg-default-features-fail", + "version": "0", + "default-features": [ + "default-feature" + ], + "features": { + "default-feature": { + "description": "" + } + } +} diff --git a/azure-pipelines/e2e_ports/overlays/vcpkg-empty-port/portfile.cmake b/azure-pipelines/e2e_ports/overlays/vcpkg-empty-port/portfile.cmake new file mode 100644 index 00000000000000..065116c276adcf --- /dev/null +++ b/azure-pipelines/e2e_ports/overlays/vcpkg-empty-port/portfile.cmake @@ -0,0 +1 @@ +set(VCPKG_POLICY_EMPTY_PACKAGE enabled) diff --git a/azure-pipelines/e2e_ports/overlays/vcpkg-empty-port/vcpkg.json b/azure-pipelines/e2e_ports/overlays/vcpkg-empty-port/vcpkg.json new file mode 100644 index 00000000000000..1f42aa9278cdd5 --- /dev/null +++ b/azure-pipelines/e2e_ports/overlays/vcpkg-empty-port/vcpkg.json @@ -0,0 +1,4 @@ +{ + "name": "vcpkg-empty-port", + "version": "0" +} diff --git a/azure-pipelines/e2e_ports/overlays/vcpkg-fail-if-depended-upon/portfile.cmake b/azure-pipelines/e2e_ports/overlays/vcpkg-fail-if-depended-upon/portfile.cmake new file mode 100644 index 00000000000000..47fc9d0aa9a404 --- /dev/null +++ b/azure-pipelines/e2e_ports/overlays/vcpkg-fail-if-depended-upon/portfile.cmake @@ -0,0 +1,2 @@ +set(VCPKG_POLICY_EMPTY_PACKAGE enabled) +message(FATAL_ERROR "this port should not be depended upon") diff --git a/azure-pipelines/e2e_ports/overlays/vcpkg-fail-if-depended-upon/vcpkg.json b/azure-pipelines/e2e_ports/overlays/vcpkg-fail-if-depended-upon/vcpkg.json new file mode 100644 index 00000000000000..6d59d6792502ac --- /dev/null +++ b/azure-pipelines/e2e_ports/overlays/vcpkg-fail-if-depended-upon/vcpkg.json @@ -0,0 +1,4 @@ +{ + "name": "vcpkg-fail-if-depended-upon", + "version": "0" +} diff --git a/azure-pipelines/end-to-end-tests-dir/manifests.ps1 b/azure-pipelines/end-to-end-tests-dir/manifests.ps1 new file mode 100644 index 00000000000000..75441f69b9a6aa --- /dev/null +++ b/azure-pipelines/end-to-end-tests-dir/manifests.ps1 @@ -0,0 +1,81 @@ +. "$PSScriptRoot/../end-to-end-tests-prelude.ps1" + + +Write-Trace "test manifest features" +$manifestDir = "$TestingRoot/manifest-dir" + +$manifestDirArgs = $commonArgs + @("--x-manifest-root=$manifestDir") +$noDefaultFeatureArgs = $manifestDirArgs + @('--x-no-default-features') + +function feature { + @{ + 'description' = ''; + 'dependencies' = $args; + } +} + +$vcpkgJson = @{ + 'name' = "manifest-test"; + 'version' = "1.0.0"; + 'default-features' = @( 'default-fail' ); + 'features' = @{ + 'default-fail' = feature 'vcpkg-fail-if-depended-upon'; + 'copied-feature' = feature 'vcpkg-empty-port' + 'multiple-dep-1' = feature 'vcpkg-empty-port' + 'multiple-dep-2' = feature 'vcpkg-empty-port' + 'no-default-features-1' = feature @{ + 'name' = 'vcpkg-default-features-fail'; + 'default-features' = $False; + }; + 'no-default-features-2' = feature @{ + 'name' = 'vcpkg-default-features-fail-require-other-feature'; + 'default-features' = $False; + 'features' = @( 'success' ) + }; + } +} + +New-Item -Path $manifestDir -ItemType Directory +$manifestDir = (Get-Item $manifestDir).FullName +New-Item -Path "$manifestDir/vcpkg.json" -ItemType File ` + -Value (ConvertTo-Json -Depth 5 -InputObject $vcpkgJson) + +Write-Trace "test manifest features: default-features, features = []" +Run-Vcpkg install @manifestDirArgs +Throw-IfNotFailed + +Write-Trace "test manifest features: no-default-features, features = []" +Run-Vcpkg install @manifestDirArgs --x-no-default-features +Throw-IfFailed +Write-Trace "test manifest features: default-features, features = [core]" +Run-Vcpkg install @manifestDirArgs --x-feature=core +Throw-IfFailed +# test having both +Write-Trace "test manifest features: no-default-features, features = [core]" +Run-Vcpkg install @manifestDirArgs --x-no-default-features --x-feature=core +Throw-IfFailed + +Write-Trace "test manifest features: no-default-features, features = [default-fail]" +Run-Vcpkg install @manifestDirArgs --x-no-default-features --x-feature=default-fail +Throw-IfNotFailed +Write-Trace "test manifest features: default-features, features = [core, default-fail]" +Run-Vcpkg install @manifestDirArgs --x-feature=core --x-feature=default-fail +Throw-IfNotFailed + +Write-Trace "test manifest features: no-default-features, features = [copied-feature]" +Run-Vcpkg install @noDefaultFeatureArgs --x-feature=copied-feature +Throw-IfFailed +Write-Trace "test manifest features: no-default-features, features = [copied-feature, copied-feature]" +Run-Vcpkg install @noDefaultFeatureArgs --x-feature=copied-feature --x-feature=copied-feature +Throw-IfFailed + +Write-Trace "test manifest features: no-default-features, features = [multiple-dep-1, multiple-dep-2]" +Run-Vcpkg install @noDefaultFeatureArgs --x-feature=multiple-dep-1 --x-feature=multiple-dep-2 +Throw-IfFailed + +Write-Trace "test manifest features: no-default-features, features = [no-default-features-1]" +Run-Vcpkg install @noDefaultFeatureArgs --x-feature=no-default-features-1 +Throw-IfFailed +Write-Trace "test manifest features: no-default-features, features = [no-default-features-2]" +Run-Vcpkg install @noDefaultFeatureArgs --x-feature=no-default-features-2 +Throw-IfFailed diff --git a/include/vcpkg/binaryparagraph.h b/include/vcpkg/binaryparagraph.h index f10c41af07bc01..a807797f887d04 100644 --- a/include/vcpkg/binaryparagraph.h +++ b/include/vcpkg/binaryparagraph.h @@ -22,6 +22,8 @@ namespace vcpkg Triplet triplet, const std::vector& deps); + void canonicalize(); + std::string displayname() const; std::string fullstem() const; diff --git a/src/vcpkg/binaryparagraph.cpp b/src/vcpkg/binaryparagraph.cpp index f8608122306bcb..d30fd47516ddfa 100644 --- a/src/vcpkg/binaryparagraph.cpp +++ b/src/vcpkg/binaryparagraph.cpp @@ -64,15 +64,7 @@ namespace vcpkg } this->description = Strings::split(parser.optional_field(Fields::DESCRIPTION), '\n'); - for (auto& desc : this->description) - { - desc = Strings::trim(std::move(desc)); - } this->maintainers = Strings::split(parser.optional_field(Fields::MAINTAINER), '\n'); - for (auto& maintainer : this->maintainers) - { - maintainer = Strings::trim(std::move(maintainer)); - } this->abi = parser.optional_field(Fields::ABI); @@ -102,6 +94,8 @@ namespace vcpkg // prefer failing above when possible because it gives better information Checks::check_exit(VCPKG_LINE_INFO, multi_arch == "same", "Multi-Arch must be 'same' but was %s", multi_arch); + + canonicalize(); } BinaryParagraph::BinaryParagraph(const SourceParagraph& spgh, @@ -120,7 +114,7 @@ namespace vcpkg , type(spgh.type) { this->dependencies = Util::fmap(deps, [](const FeatureSpec& spec) { return spec.spec().name(); }); - Util::sort_unique_erase(this->dependencies); + canonicalize(); } BinaryParagraph::BinaryParagraph(const SourceParagraph& spgh, @@ -139,7 +133,34 @@ namespace vcpkg , type(spgh.type) { this->dependencies = Util::fmap(deps, [](const FeatureSpec& spec) { return spec.spec().name(); }); + canonicalize(); + } + + void BinaryParagraph::canonicalize() + { + constexpr auto all_empty = [](const std::vector& range) { + return std::all_of(range.begin(), range.end(), [](const std::string& el) { return el.empty(); }); + }; + Util::sort_unique_erase(this->dependencies); + + for (auto& maintainer : this->maintainers) + { + maintainer = Strings::trim(std::move(maintainer)); + } + if (all_empty(this->maintainers)) + { + this->maintainers.clear(); + } + + for (auto& desc : this->description) + { + desc = Strings::trim(std::move(desc)); + } + if (all_empty(this->description)) + { + this->description.clear(); + } } std::string BinaryParagraph::displayname() const diff --git a/src/vcpkg/dependencies.cpp b/src/vcpkg/dependencies.cpp index aafdec065ee581..1c084846f63d78 100644 --- a/src/vcpkg/dependencies.cpp +++ b/src/vcpkg/dependencies.cpp @@ -1651,72 +1651,71 @@ namespace vcpkg::Dependencies auto& node = emplace_package(spec); auto maybe_overlay = m_o_provider.get_control_file(dep.name); + auto over_it = m_overrides.find(dep.name); if (auto p_overlay = maybe_overlay.get()) { auto ver = to_version(*p_overlay->source_control_file); m_roots.push_back(DepSpec{spec, ver, dep.features}); add_constraint(node, ver, toplevel.name()); - continue; } - - auto over_it = m_overrides.find(dep.name); - if (over_it != m_overrides.end()) + else if (over_it != m_overrides.end()) { m_roots.push_back(DepSpec{spec, over_it->second, dep.features}); add_constraint(node, over_it->second, toplevel.name()); - continue; } - - auto dep_ver = to_version(dep.constraint); - auto base_ver = m_base_provider.get_baseline_version(dep.name); - if (auto p_dep_ver = dep_ver.get()) + else { - m_roots.push_back(DepSpec{spec, *p_dep_ver, dep.features}); - if (auto p_base_ver = base_ver.get()) + auto dep_ver = to_version(dep.constraint); + auto base_ver = m_base_provider.get_baseline_version(dep.name); + if (auto p_dep_ver = dep_ver.get()) { - // Compare version constraint with baseline to only evaluate the "tighter" constraint - auto dep_scfl = m_ver_provider.get_control_file({dep.name, *p_dep_ver}); - auto base_scfl = m_ver_provider.get_control_file({dep.name, *p_base_ver}); - if (dep_scfl && base_scfl) + m_roots.push_back(DepSpec{spec, *p_dep_ver, dep.features}); + if (auto p_base_ver = base_ver.get()) { - auto r = - compare_versions(dep_scfl.get()->source_control_file->core_paragraph->version_scheme, - *p_dep_ver, - base_scfl.get()->source_control_file->core_paragraph->version_scheme, - *p_base_ver); - if (r == VerComp::lt) + // Compare version constraint with baseline to only evaluate the "tighter" constraint + auto dep_scfl = m_ver_provider.get_control_file({dep.name, *p_dep_ver}); + auto base_scfl = m_ver_provider.get_control_file({dep.name, *p_base_ver}); + if (dep_scfl && base_scfl) { - add_constraint(node, *p_base_ver, "baseline"); - add_constraint(node, *p_dep_ver, toplevel.name()); + auto r = compare_versions( + dep_scfl.get()->source_control_file->core_paragraph->version_scheme, + *p_dep_ver, + base_scfl.get()->source_control_file->core_paragraph->version_scheme, + *p_base_ver); + if (r == VerComp::lt) + { + add_constraint(node, *p_base_ver, "baseline"); + add_constraint(node, *p_dep_ver, toplevel.name()); + } + else + { + add_constraint(node, *p_dep_ver, toplevel.name()); + add_constraint(node, *p_base_ver, "baseline"); + } } else { - add_constraint(node, *p_dep_ver, toplevel.name()); - add_constraint(node, *p_base_ver, "baseline"); + if (!dep_scfl) m_errors.push_back(dep_scfl.error()); + if (!base_scfl) m_errors.push_back(base_scfl.error()); } } else { - if (!dep_scfl) m_errors.push_back(dep_scfl.error()); - if (!base_scfl) m_errors.push_back(base_scfl.error()); + add_constraint(node, *p_dep_ver, toplevel.name()); } } + else if (auto p_base_ver = base_ver.get()) + { + m_roots.push_back(DepSpec{spec, *p_base_ver, dep.features}); + add_constraint(node, *p_base_ver, toplevel.name()); + } else { - add_constraint(node, *p_dep_ver, toplevel.name()); + m_errors.push_back(Strings::concat("Cannot resolve unversioned dependency from top-level to ", + dep.name, + " without a baseline entry or override.")); } } - else if (auto p_base_ver = base_ver.get()) - { - m_roots.push_back(DepSpec{spec, *p_base_ver, dep.features}); - add_constraint(node, *p_base_ver, toplevel.name()); - } - else - { - m_errors.push_back(Strings::concat("Cannot resolve unversioned dependency from top-level to ", - dep.name, - " without a baseline entry or override.")); - } for (auto&& f : dep.features) { diff --git a/src/vcpkg/install.cpp b/src/vcpkg/install.cpp index 59212d65189423..5c62af4ede7a12 100644 --- a/src/vcpkg/install.cpp +++ b/src/vcpkg/install.cpp @@ -836,15 +836,40 @@ namespace vcpkg::Install features.insert(features.end(), manifest_feature_it->second.begin(), manifest_feature_it->second.end()); } auto core_it = Util::find(features, "core"); - if (core_it == features.end()) + if (core_it == features.end() && + !Util::Sets::contains(options.switches, OPTION_MANIFEST_NO_DEFAULT_FEATURES)) { - if (!Util::Sets::contains(options.switches, OPTION_MANIFEST_NO_DEFAULT_FEATURES)) - features.push_back("default"); + const auto& default_features = manifest_scf.core_paragraph->default_features; + features.insert(features.end(), default_features.begin(), default_features.end()); } else { // remove "core" because resolve_deps_as_top_level uses default-inversion - features.erase(core_it); + // support multiple core features + core_it = std::remove(core_it, features.end(), "core"); + features.erase(core_it, features.end()); + } + Util::sort_unique_erase(features); + + auto dependencies = manifest_scf.core_paragraph->dependencies; + for (const auto& feature : features) + { + auto it = Util::find_if( + manifest_scf.feature_paragraphs, + [&feature](const std::unique_ptr& fpgh) { return fpgh->name == feature; }); + + if (it == manifest_scf.feature_paragraphs.end()) + { + System::printf(System::Color::warning, + "Warning: feature %s was passed, but that is not a feature that %s supports.", + feature, + manifest_scf.core_paragraph->name); + } + else + { + dependencies.insert( + dependencies.end(), it->get()->dependencies.begin(), it->get()->dependencies.end()); + } } if (!manifest_scf.core_paragraph->overrides.empty()) @@ -876,7 +901,7 @@ namespace vcpkg::Install *baseprovider, *oprovider, var_provider, - manifest_scf.core_paragraph->dependencies, + dependencies, manifest_scf.core_paragraph->overrides, {manifest_scf.core_paragraph->name, default_triplet}) .value_or_exit(VCPKG_LINE_INFO);