Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[supports] Add native identifier expression and x-check-support command #29

Merged
merged 6 commits into from
Mar 30, 2021
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion include/vcpkg-test/mockcmakevarprovider.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@ namespace vcpkg::Test
}

void load_tag_vars(Span<const FullPackageSpec> specs,
const PortFileProvider::PortFileProvider& port_provider) const override
const PortFileProvider::PortFileProvider& port_provider,
Triplet host_triplet) const override
{
for (auto&& spec : specs)
tag_vars.emplace(spec.package_spec, SMap{});
(void)(port_provider);
(void)(host_triplet);
}

Optional<const std::unordered_map<std::string, std::string>&> get_generic_triplet_vars(
Expand Down
6 changes: 4 additions & 2 deletions include/vcpkg/cmakevars.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,12 @@ namespace vcpkg::CMakeVars
virtual void load_dep_info_vars(Span<const PackageSpec> specs) const = 0;

virtual void load_tag_vars(Span<const FullPackageSpec> specs,
const PortFileProvider::PortFileProvider& port_provider) const = 0;
const PortFileProvider::PortFileProvider& port_provider,
Triplet host_triplet) const = 0;

void load_tag_vars(const vcpkg::Dependencies::ActionPlan& action_plan,
const PortFileProvider::PortFileProvider& port_provider) const;
const PortFileProvider::PortFileProvider& port_provider,
Triplet host_triplet) const;
ras0219-msft marked this conversation as resolved.
Show resolved Hide resolved
};

std::unique_ptr<CMakeVarProvider> make_triplet_cmake_var_provider(const vcpkg::VcpkgPaths& paths);
Expand Down
19 changes: 19 additions & 0 deletions include/vcpkg/commands.check-support.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#pragma once

#include <vcpkg/commands.interface.h>

namespace vcpkg::Commands::CheckSupport
{
void perform_and_exit(const VcpkgCmdArguments& args,
const VcpkgPaths& paths,
Triplet default_triplet,
Triplet host_triplet);

struct CheckSupportCommand : TripletCommand
{
void perform_and_exit(const VcpkgCmdArguments& args,
const VcpkgPaths& paths,
Triplet default_triplet,
Triplet host_triplet) const override;
};
}
3 changes: 2 additions & 1 deletion include/vcpkg/commands.setinstalled.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ namespace vcpkg::Commands::SetInstalled
const CMakeVars::CMakeVarProvider& cmake_vars,
Dependencies::ActionPlan action_plan,
DryRun dry_run,
const Optional<fs::path>& pkgsconfig_path);
const Optional<fs::path>& pkgsconfig_path,
Triplet host_triplet);
void perform_and_exit(const VcpkgCmdArguments& args,
const VcpkgPaths& paths,
Triplet default_triplet,
Expand Down
2 changes: 1 addition & 1 deletion include/vcpkg/dependencies.h
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ namespace vcpkg::Dependencies
/// <param name="status_db">Status of installed packages in the current environment.</param>
ActionPlan create_feature_install_plan(const PortFileProvider::PortFileProvider& provider,
const CMakeVars::CMakeVarProvider& var_provider,
const std::vector<FullPackageSpec>& specs,
View<FullPackageSpec> specs,
const StatusParagraphs& status_db,
const CreateInstallPlanOptions& options = {Triplet{}});

Expand Down
1 change: 1 addition & 0 deletions src/vcpkg-test/commands.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ TEST_CASE ("get_available_commands_type_a works", "[commands]")
"build-external",
"export",
"depend-info",
"x-check-support"
});
}
// clang-format on
100 changes: 62 additions & 38 deletions src/vcpkg-test/plan.cpp

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions src/vcpkg-test/platform-expression.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,16 @@ TEST_CASE ("platform-expression-identifier", "[platform-expression]")
CHECK(expr.evaluate({{"VCPKG_CMAKE_SYSTEM_NAME", "WindowsStore"}}));
CHECK_FALSE(expr.evaluate({{"VCPKG_CMAKE_SYSTEM_NAME", "Linux"}}));
CHECK_FALSE(expr.evaluate({{"VCPKG_CMAKE_SYSTEM_NAME", "Darwin"}}));

m_expr = parse_expr("native");
CHECK(expr.evaluate({{"Z_VCPKG_IS_NATIVE", "1"}}));
CHECK_FALSE(expr.evaluate({{"Z_VCPKG_IS_NATIVE", "0"}}));

m_expr = parse_expr("staticcrt");
CHECK(expr.evaluate({{"VCPKG_CRT_LINKAGE", "static"}, {"VCPKG_LIBRARY_LINKAGE", "static"}}));
CHECK(expr.evaluate({{"VCPKG_CRT_LINKAGE", "static"}, {"VCPKG_LIBRARY_LINKAGE", "dynamic"}}));
CHECK_FALSE(expr.evaluate({{"VCPKG_CRT_LINKAGE", "dynamic"}, {"VCPKG_LIBRARY_LINKAGE", "static"}}));
CHECK_FALSE(expr.evaluate({{"VCPKG_CRT_LINKAGE", "dynamic"}, {"VCPKG_LIBRARY_LINKAGE", "dynamic"}}));
strega-nil marked this conversation as resolved.
Show resolved Hide resolved
}

TEST_CASE ("platform-expression-not", "[platform-expression]")
Expand Down
4 changes: 2 additions & 2 deletions src/vcpkg-test/versionplan.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,13 @@ TEST_CASE ("qualified dependency", "[dependencies]")
PortFileProvider::MapPortFileProvider map_port{spec_map.map};
MockCMakeVarProvider var_provider;

auto plan = vcpkg::Dependencies::create_feature_install_plan(map_port, var_provider, {spec_a}, {});
auto plan = vcpkg::Dependencies::create_feature_install_plan(map_port, var_provider, {&spec_a, 1}, {});
REQUIRE(plan.install_actions.size() == 2);
REQUIRE(plan.install_actions.at(0).feature_list == std::vector<std::string>{"core"});

FullPackageSpec linspec_a{{"a", Triplet::from_canonical_name("x64-linux")}, {}};
var_provider.dep_info_vars[linspec_a.package_spec].emplace("VCPKG_CMAKE_SYSTEM_NAME", "Linux");
auto plan2 = vcpkg::Dependencies::create_feature_install_plan(map_port, var_provider, {linspec_a}, {});
auto plan2 = vcpkg::Dependencies::create_feature_install_plan(map_port, var_provider, {&linspec_a, 1}, {});
REQUIRE(plan2.install_actions.size() == 2);
REQUIRE(plan2.install_actions.at(0).feature_list == std::vector<std::string>{"b1", "core"});
}
Expand Down
2 changes: 1 addition & 1 deletion src/vcpkg/build.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ namespace vcpkg::Build
auto action_plan = Dependencies::create_feature_install_plan(
provider, var_provider, std::vector<FullPackageSpec>{full_spec}, status_db, {host_triplet});

var_provider.load_tag_vars(action_plan, provider);
var_provider.load_tag_vars(action_plan, provider, host_triplet);

const PackageSpec& spec = full_spec.package_spec;
const SourceControlFile& scf = *scfl.source_control_file;
Expand Down
21 changes: 12 additions & 9 deletions src/vcpkg/cmakevars.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,16 @@ using vcpkg::Optional;
namespace vcpkg::CMakeVars
{
void CMakeVarProvider::load_tag_vars(const vcpkg::Dependencies::ActionPlan& action_plan,
const PortFileProvider::PortFileProvider& port_provider) const
const PortFileProvider::PortFileProvider& port_provider,
Triplet host_triplet) const
{
std::vector<FullPackageSpec> install_package_specs;
for (auto&& action : action_plan.install_actions)
{
install_package_specs.emplace_back(FullPackageSpec{action.spec, action.feature_list});
}

load_tag_vars(install_package_specs, port_provider);
load_tag_vars(install_package_specs, port_provider, host_triplet);
}

namespace
Expand All @@ -37,7 +38,8 @@ namespace vcpkg::CMakeVars
void load_dep_info_vars(View<PackageSpec> specs) const override;

void load_tag_vars(View<FullPackageSpec> specs,
const PortFileProvider::PortFileProvider& port_provider) const override;
const PortFileProvider::PortFileProvider& port_provider,
Triplet host_triplet) const override;

Optional<const std::unordered_map<std::string, std::string>&> get_generic_triplet_vars(
Triplet triplet) const override;
Expand Down Expand Up @@ -254,7 +256,8 @@ endmacro()
}

void TripletCMakeVarProvider::load_tag_vars(View<FullPackageSpec> specs,
const PortFileProvider::PortFileProvider& port_provider) const
const PortFileProvider::PortFileProvider& port_provider,
Triplet host_triplet) const
{
if (specs.size() == 0) return;
std::vector<std::pair<const FullPackageSpec*, std::string>> spec_abi_settings;
Expand All @@ -276,12 +279,12 @@ endmacro()
for (const auto& spec_abi_setting : spec_abi_settings)
{
const FullPackageSpec& spec = *spec_abi_setting.first;

tag_vars.emplace(std::piecewise_construct,
std::forward_as_tuple(spec.package_spec),
std::forward_as_tuple(std::make_move_iterator(var_list_itr->begin()),
std::make_move_iterator(var_list_itr->end())));
PlatformExpression::Context ctxt{std::make_move_iterator(var_list_itr->begin()), std::make_move_iterator(var_list_itr->end())};
++var_list_itr;

ctxt.emplace("Z_VCPKG_IS_NATIVE", host_triplet == spec.package_spec.triplet() ? "1" : "0");

tag_vars.emplace(spec.package_spec, std::move(ctxt));
}
}

Expand Down
204 changes: 204 additions & 0 deletions src/vcpkg/commands.check-support.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
#include <vcpkg/base/checks.h>
#include <vcpkg/base/json.h>
#include <vcpkg/base/strings.h>
#include <vcpkg/base/system.print.h>
#include <vcpkg/base/util.h>

#include <vcpkg/cmakevars.h>
#include <vcpkg/commands.check-support.h>
#include <vcpkg/dependencies.h>
#include <vcpkg/input.h>
#include <vcpkg/packagespec.h>
#include <vcpkg/platform-expression.h>
#include <vcpkg/portfileprovider.h>
#include <vcpkg/vcpkgcmdarguments.h>
#include <vcpkg/vcpkgpaths.h>

namespace vcpkg::Commands
{
const CommandStructure COMMAND_STRUCTURE = {
create_example_string(R"(x-check-support <package>...)"),
1,
SIZE_MAX,
{},
nullptr,
};

namespace
{
struct Port
{
std::string port_name;
std::vector<std::string> features;
Triplet triplet;
std::string supports_expr;
};

Json::Object to_object(const Port& p)
{
Json::Object res;
res.insert("name", Json::Value::string(p.port_name));
res.insert("triplet", Json::Value::string(p.triplet.to_string()));

Json::Array& features = res.insert("features", Json::Array{});
for (const auto& feature : p.features)
{
features.push_back(Json::Value::string(feature));
}

if (!p.supports_expr.empty())
{
res.insert("supports", Json::Value::string(p.supports_expr));
}

return res;
}

void print_port_supported(const Port& p, bool is_top_level_supported, View<Port> reasons)
{
const auto full_port_name = [](const Port& port) {
return Strings::format(
"%s[%s]:%s", port.port_name, Strings::join(",", port.features), port.triplet.to_string());
};

if (reasons.size() == 0)
{
if (is_top_level_supported)
{
// supported!
System::printf("port %s is supported\n", full_port_name(p));
}
else
{
System::printf(
"port %s is not supported (supports: \"%s\")\n", full_port_name(p), p.supports_expr);
}

return;
}

if (is_top_level_supported)
{
System::printf("port %s is not supported due to the following dependencies:\n", full_port_name(p));
}
else
{
System::printf(
"port %s is not supported (supports: \"%s\"), and has the following unsupported dependencies:\n",
full_port_name(p),
p.supports_expr);
}

for (const Port& reason : reasons)
{
System::printf(" - dependency %s is not supported (supports: \"%s\")\n",
full_port_name(reason),
reason.supports_expr);
}
}
}

void CheckSupport::perform_and_exit(const VcpkgCmdArguments& args,
const VcpkgPaths& paths,
Triplet default_triplet,
Triplet host_triplet)
{
const ParsedArguments options = args.parse_arguments(COMMAND_STRUCTURE);
const bool use_json = args.json.value_or(false);
Json::Array json_to_print; // only used when `use_json`

const std::vector<FullPackageSpec> specs = Util::fmap(args.command_arguments, [&](auto&& arg) {
return Input::check_and_get_full_package_spec(
std::string(arg), default_triplet, COMMAND_STRUCTURE.example_text);
});

for (auto&& spec : specs)
{
Input::check_triplet(spec.package_spec.triplet(), paths);
}

PortFileProvider::PathsPortFileProvider provider(paths, args.overlay_ports);
auto cmake_vars = CMakeVars::make_triplet_cmake_var_provider(paths);

// for each spec in the user-requested specs, check all dependencies
for (const auto& user_spec : specs)
{
auto action_plan =
Dependencies::create_feature_install_plan(provider, *cmake_vars, {&user_spec, 1}, {}, {host_triplet});

cmake_vars->load_tag_vars(action_plan, provider, host_triplet);

Port user_port;
user_port.port_name = user_spec.package_spec.name();
user_port.triplet = user_spec.package_spec.triplet();
bool user_supported = false;

std::vector<Port> dependencies_not_supported;
for (const auto& action : action_plan.install_actions)
{
const auto& spec = action.spec;
const auto& supports_expression = action.source_control_file_location.value_or_exit(VCPKG_LINE_INFO).source_control_file->core_paragraph->supports_expression;

PlatformExpression::Context context = cmake_vars->get_tag_vars(spec).value_or_exit(VCPKG_LINE_INFO);

if (spec.name() == user_port.port_name && spec.triplet() == user_port.triplet)
{
user_port.features = action.feature_list;
user_port.supports_expr = to_string(supports_expression);

if (supports_expression.evaluate(context))
{
user_supported = true;
}

continue;
}

if (!supports_expression.evaluate(context))
{
Port port;
port.port_name = spec.name();
port.features = action.feature_list;
port.triplet = spec.triplet();
port.supports_expr = to_string(supports_expression);

dependencies_not_supported.push_back(port);
}
}

if (use_json)
{
Json::Object& obj = json_to_print.push_back(Json::Object{});
obj.insert("port", to_object(user_port));
obj.insert("top-level-support", Json::Value::boolean(user_supported));
obj.insert("is-supported",
Json::Value::boolean(user_supported && dependencies_not_supported.empty()));
if (!dependencies_not_supported.empty())
{
Json::Array& deps = obj.insert("dependencies-not-supported", Json::Array{});
for (const Port& p : dependencies_not_supported)
{
deps.push_back(to_object(p));
}
}
}
else
{
print_port_supported(user_port, user_supported, dependencies_not_supported);
}
}

if (use_json)
{
System::print2(Json::stringify(json_to_print, {}));
}
}

void CheckSupport::CheckSupportCommand::perform_and_exit(const VcpkgCmdArguments& args,
const VcpkgPaths& paths,
Triplet default_triplet,
Triplet host_triplet) const
{
return CheckSupport::perform_and_exit(args, paths, default_triplet, host_triplet);
}
}
Loading