[vcpkg] Fix "just-in-time" requirements calculation
MODIFY
include/vcpkg/base/util.h
include/vcpkg/base/util.h
@@ -127,6 +127,15 @@ namespace vcpkg::Util
std::sort(begin(cont), end(cont));
}
+ template<class Range>
+ void sort_unique_erase(Range& cont)
+ {
+ using std::begin;
+ using std::end;
+ std::sort(begin(cont), end(cont));
+ cont.erase(std::unique(begin(cont), end(cont)), end(cont));
+ }
+
template<class Range1, class Range2>
bool all_equal(const Range1& r1, const Range2& r2)
{

MODIFY
include/vcpkg/build.h
include/vcpkg/build.h
#include <array>
#include <map>
+#include <set>
#include <vector>
namespace vcpkg::Build
@@ -125,7 +126,7 @@ namespace vcpkg::Build
const Triplet& triplet,
fs::path&& port_dir,
const BuildPackageOptions& build_package_options,
- const std::unordered_set<std::string>& feature_list)
+ const std::set<std::string>& feature_list)
: scf(src)
, triplet(triplet)
, port_dir(std::move(port_dir))
@@ -138,7 +139,7 @@ namespace vcpkg::Build
const Triplet& triplet;
fs::path port_dir;
const BuildPackageOptions& build_package_options;
- const std::unordered_set<std::string>& feature_list;
+ const std::set<std::string>& feature_list;
};
ExtendedBuildResult build_package(const VcpkgPaths& paths,

MODIFY
include/vcpkg/commands.h
include/vcpkg/commands.h
@@ -120,6 +120,7 @@ namespace vcpkg::Commands
namespace Hash
{
void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths);
+ std::string get_file_hash(fs::path const& cmake_exe_path, fs::path const& path, std::string const& hash_type);
}
template<class T>

MODIFY
include/vcpkg/dependencies.h
include/vcpkg/dependencies.h
@@ -39,12 +39,12 @@ namespace vcpkg::Dependencies
InstallPlanAction(const PackageSpec& spec,
InstalledPackageView&& spghs,
- const std::unordered_set<std::string>& features,
+ const std::set<std::string>& features,
const RequestType& request_type);
InstallPlanAction(const PackageSpec& spec,
const SourceControlFile& scf,
- const std::unordered_set<std::string>& features,
+ const std::set<std::string>& features,
const RequestType& request_type);
std::string displayname() const;
@@ -57,7 +57,7 @@ namespace vcpkg::Dependencies
InstallPlanType plan_type;
RequestType request_type;
Build::BuildPackageOptions build_options;
- std::unordered_set<std::string> feature_list;
+ std::set<std::string> feature_list;
};
enum class RemovePlanType

MODIFY
src/vcpkg/build.cpp
src/vcpkg/build.cpp
@@ -66,7 +66,8 @@ namespace vcpkg::Build::Command
Build::CleanBuildtrees::NO,
Build::CleanPackages::NO};
- const std::unordered_set<std::string> features_as_set(full_spec.features.begin(), full_spec.features.end());
+ std::set<std::string> features_as_set(full_spec.features.begin(), full_spec.features.end());
+ features_as_set.emplace("core");
const Build::BuildPackageConfig build_config{
*scf, spec.triplet(), fs::path{port_dir}, build_package_options, features_as_set};
@@ -260,57 +261,94 @@ namespace vcpkg::Build
paths.get_filesystem().write_contents(binary_control_file, start);
}
- static ExtendedBuildResult do_build_package(const VcpkgPaths& paths,
- const BuildPackageConfig& config,
+ static std::vector<FeatureSpec> compute_required_feature_specs(const BuildPackageConfig& config,
const StatusParagraphs& status_db)
{
- const PackageSpec spec = PackageSpec::from_name_and_triplet(config.scf.core_paragraph->name, config.triplet)
- .value_or_exit(VCPKG_LINE_INFO);
-
const Triplet& triplet = config.triplet;
+
+ auto dep_strings =
+ Util::fmap_flatten(config.feature_list, [&](std::string const& feature) -> std::vector<std::string> {
+ if (feature == "core")
{
- std::vector<FeatureSpec> missing_specs;
- for (auto&& dep : filter_dependencies(config.scf.core_paragraph->depends, triplet))
+ return filter_dependencies(config.scf.core_paragraph->depends, triplet);
+ }
+
+ auto it =
+ Util::find_if(config.scf.feature_paragraphs,
+ [&](std::unique_ptr<FeatureParagraph> const& fpgh) { return fpgh->name == feature; });
+ Checks::check_exit(VCPKG_LINE_INFO, it != config.scf.feature_paragraphs.end());
+
+ return filter_dependencies(it->get()->depends, triplet);
+ });
+
+ auto dep_fspecs = FeatureSpec::from_strings_and_triplet(dep_strings, triplet);
+ Util::sort_unique_erase(dep_fspecs);
+
+ // expand defaults
+ std::vector<FeatureSpec> ret;
+ for (auto&& fspec : dep_fspecs)
{
- auto dep_specs = FeatureSpec::from_strings_and_triplet({dep}, triplet);
- for (auto&& feature : dep_specs)
+ if (fspec.feature().empty())
{
- if (!status_db.is_installed(feature))
+ // reference to default features
+ auto it = status_db.find_installed(fspec.spec());
+ if (it == status_db.end())
{
- missing_specs.push_back(std::move(feature));
+ // not currently installed, so just leave the default reference so it will fail later
+ ret.push_back(fspec);
}
+ else
+ {
+ ret.push_back(FeatureSpec{fspec.spec(), "core"});
+ for (auto&& default_feature : it->get()->package.default_features)
+ ret.push_back(FeatureSpec{fspec.spec(), default_feature});
}
}
- // Fail the build if any dependencies were missing
- if (!missing_specs.empty())
+ else
{
- return {BuildResult::CASCADED_DUE_TO_MISSING_DEPENDENCIES, std::move(missing_specs)};
+ ret.push_back(fspec);
}
}
+ Util::sort_unique_erase(ret);
+
+ return ret;
+ }
+
+ static ExtendedBuildResult do_build_package(const VcpkgPaths& paths,
+ const BuildPackageConfig& config,
+ const StatusParagraphs& status_db)
+ {
+ auto& fs = paths.get_filesystem();
+ const Triplet& triplet = config.triplet;
+
+ const PackageSpec spec =
+ PackageSpec::from_name_and_triplet(config.scf.core_paragraph->name, triplet).value_or_exit(VCPKG_LINE_INFO);
+
+ std::vector<FeatureSpec> required_fspecs = compute_required_feature_specs(config, status_db);
+
+ // Find all features that aren't installed. This destroys required_fspecs.
+ Util::unstable_keep_if(required_fspecs,
+ [&](FeatureSpec const& fspec) { return !status_db.is_installed(fspec); });
+
+ if (!required_fspecs.empty())
+ {
+ return {BuildResult::CASCADED_DUE_TO_MISSING_DEPENDENCIES, std::move(required_fspecs)};
+ }
const fs::path& cmake_exe_path = paths.get_cmake_exe();
const fs::path& git_exe_path = paths.get_git_exe();
const fs::path ports_cmake_script_path = paths.ports_cmake;
+
const auto pre_build_info = PreBuildInfo::from_triplet_file(paths, triplet);
- std::string features;
+ std::string features = Strings::join(";", config.feature_list);
+
std::string all_features;
- if (GlobalState::feature_packages)
- {
- for (auto&& feature : config.feature_list)
- {
- features.append(feature + ";");
- }
- if (!features.empty())
- {
- features.pop_back();
- }
for (auto& feature : config.scf.feature_paragraphs)
{
all_features.append(feature->name + ";");
}
- }
const Toolset& toolset = paths.get_toolset(pre_build_info);
const std::string cmd_launch_cmake = System::make_cmake_cmd(
@@ -351,7 +389,7 @@ namespace vcpkg::Build
}
}
- const BuildInfo build_info = read_build_info(paths.get_filesystem(), paths.build_info_file_path(spec));
+ const BuildInfo build_info = read_build_info(fs, paths.build_info_file_path(spec));
const size_t error_count = PostBuildLint::perform_all_checks(spec, paths, pre_build_info, build_info);
auto bcf = create_binary_control_file(*config.scf.core_paragraph, triplet, build_info);
@@ -360,8 +398,6 @@ namespace vcpkg::Build
{
return BuildResult::POST_BUILD_CHECKS_FAILED;
}
- if (GlobalState::feature_packages)
- {
for (auto&& feature : config.feature_list)
{
for (auto&& f_pgh : config.scf.feature_paragraphs)
@@ -371,7 +407,6 @@ namespace vcpkg::Build
create_binary_feature_control_file(*config.scf.core_paragraph, *f_pgh, triplet));
}
}
- }
write_binary_control_file(paths, *bcf);

MODIFY
src/vcpkg/commands.hash.cpp
src/vcpkg/commands.hash.cpp
namespace vcpkg::Commands::Hash
{
- static void do_file_hash(fs::path const& cmake_exe_path, fs::path const& path, std::string const& hash_type)
+ std::string get_file_hash(fs::path const& cmake_exe_path, fs::path const& path, std::string const& hash_type)
{
const std::string cmd_line = Strings::format(
R"("%s" -E %ssum %s)", cmake_exe_path.u8string(), Strings::ascii_to_lowercase(hash_type), path.u8string());
@@ -27,7 +27,7 @@ namespace vcpkg::Commands::Hash
auto hash = output.substr(0, start);
Util::erase_remove_if(hash, isspace);
- System::println(hash);
+ return hash;
}
const CommandStructure COMMAND_STRUCTURE = {
@@ -45,11 +45,13 @@ namespace vcpkg::Commands::Hash
if (args.command_arguments.size() == 1)
{
- do_file_hash(paths.get_cmake_exe(), args.command_arguments[0], "SHA512");
+ auto hash = get_file_hash(paths.get_cmake_exe(), args.command_arguments[0], "SHA512");
+ System::println(hash);
}
if (args.command_arguments.size() == 2)
{
- do_file_hash(paths.get_cmake_exe(), args.command_arguments[0], args.command_arguments[1]);
+ auto hash = get_file_hash(paths.get_cmake_exe(), args.command_arguments[0], args.command_arguments[1]);
+ System::println(hash);
}
Checks::exit_success(VCPKG_LINE_INFO);

MODIFY
src/vcpkg/dependencies.cpp
src/vcpkg/dependencies.cpp
@@ -30,8 +30,8 @@ namespace vcpkg::Dependencies
Optional<const SourceControlFile*> source_control_file;
PackageSpec spec;
std::unordered_map<std::string, FeatureNodeEdges> edges;
- std::unordered_set<std::string> to_install_features;
- std::unordered_set<std::string> original_features;
+ std::set<std::string> to_install_features;
+ std::set<std::string> original_features;
bool will_remove = false;
bool transient_uninstalled = true;
RequestType request_type = RequestType::AUTO_SELECTED;
@@ -143,7 +143,7 @@ namespace vcpkg::Dependencies
InstallPlanAction::InstallPlanAction(const PackageSpec& spec,
const SourceControlFile& scf,
- const std::unordered_set<std::string>& features,
+ const std::set<std::string>& features,
const RequestType& request_type)
: spec(spec)
, source_control_file(scf)
@@ -155,7 +155,7 @@ namespace vcpkg::Dependencies
InstallPlanAction::InstallPlanAction(const PackageSpec& spec,
InstalledPackageView&& ipv,
- const std::unordered_set<std::string>& features,
+ const std::set<std::string>& features,
const RequestType& request_type)
: spec(spec)
, installed_package(std::move(ipv))

MODIFY
src/vcpkg/statusparagraphs.cpp
src/vcpkg/statusparagraphs.cpp
@@ -54,6 +54,11 @@ namespace vcpkg
const Triplet& triplet,
const std::string& feature)
{
+ if (feature == "core")
+ {
+ // The core feature maps to .feature == ""
+ return find(name, triplet, "");
+ }
return std::find_if(begin(), end(), [&](const std::unique_ptr<StatusParagraph>& pgh) {
const PackageSpec& spec = pgh->package.spec;
return spec.name() == name && spec.triplet() == triplet && pgh->package.feature == feature;
@@ -64,6 +69,11 @@ namespace vcpkg
const Triplet& triplet,
const std::string& feature) const
{
+ if (feature == "core")
+ {
+ // The core feature maps to .feature == ""
+ return find(name, triplet, "");
+ }
return std::find_if(begin(), end(), [&](const std::unique_ptr<StatusParagraph>& pgh) {
const PackageSpec& spec = pgh->package.spec;
return spec.name() == name && spec.triplet() == triplet && pgh->package.feature == feature;

[vcpkg] Fix "just-in-time" requirements calculation