[vcpkg] Implement a warning when running in a mismatched Visual Studio prompt

This commit is contained in:
Billy O'Neal 2020-03-16 11:19:55 -07:00 коммит произвёл GitHub
Родитель ecff016663 3cb8570203
Коммит 31cca07563
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
9 изменённых файлов: 363 добавлений и 25 удалений

Просмотреть файл

@ -283,6 +283,21 @@ namespace vcpkg
}
}
friend bool operator==(const Optional& lhs, const Optional& rhs)
{
if (lhs.m_base.has_value())
{
if (rhs.m_base.has_value())
{
return lhs.m_base.value() == rhs.m_base.value();
}
return false;
}
return !rhs.m_base.has_value();
}
private:
details::OptionalStorage<T> m_base;
};
@ -317,4 +332,27 @@ namespace vcpkg
if (auto p = o.get()) return t != *p;
return true;
}
template<class Container, class Projection>
auto common_projection(const Container& input, Projection proj)
-> Optional<std::decay_t<decltype(proj(*(input.begin())))>>
{
const auto last = input.end();
auto first = input.begin();
if (first == last)
{
return nullopt;
}
const auto& prototype = proj(*first);
while (++first != last)
{
if (prototype != proj(*first))
{
return nullopt;
}
}
return prototype;
}
}

Просмотреть файл

@ -21,6 +21,8 @@ namespace vcpkg::System
Optional<CPUArchitecture> to_cpu_architecture(StringView arch);
ZStringView to_zstring_view(CPUArchitecture arch) noexcept;
CPUArchitecture get_host_processor();
std::vector<CPUArchitecture> get_supported_host_architectures();
@ -30,4 +32,6 @@ namespace vcpkg::System
const Optional<fs::path>& get_program_files_platform_bitness();
int get_num_logical_cores();
Optional<CPUArchitecture> guess_visual_studio_prompt_target_architecture();
}

Просмотреть файл

@ -1,6 +1,8 @@
#pragma once
#include <string>
#include <vcpkg/base/system.h>
#include <vcpkg/base/optional.h>
namespace vcpkg
{
@ -15,17 +17,18 @@ namespace vcpkg
static const Triplet X86_WINDOWS;
static const Triplet X64_WINDOWS;
static const Triplet ARM_WINDOWS;
static const Triplet ARM64_WINDOWS;
static const Triplet X86_UWP;
static const Triplet X64_UWP;
static const Triplet ARM_UWP;
static const Triplet ARM64_UWP;
static const Triplet ARM_WINDOWS;
static const Triplet ARM64_WINDOWS;
const std::string& canonical_name() const;
const std::string& to_string() const;
void to_string(std::string& out) const;
size_t hash_code() const;
Optional<System::CPUArchitecture> guess_architecture() const noexcept;
bool operator==(Triplet other) const { return this->m_instance == other.m_instance; }
bool operator<(Triplet other) const { return canonical_name() < other.canonical_name(); }

Просмотреть файл

@ -0,0 +1,39 @@
#include <catch2/catch.hpp>
#include <vcpkg/base/optional.h>
#include <vector>
namespace
{
struct identity_projection
{
template<class T>
const T& operator()(const T& val) noexcept
{
return val;
}
};
}
TEST_CASE ("equal", "[optional]")
{
using vcpkg::Optional;
CHECK(Optional<int>{} == Optional<int>{});
CHECK(!(Optional<int>{} == Optional<int>{42}));
CHECK(!(Optional<int>{42} == Optional<int>{}));
CHECK(!(Optional<int>{1729} == Optional<int>{42}));
CHECK(Optional<int>{42} == Optional<int>{42});
}
TEST_CASE ("common_projection", "[optional]")
{
using vcpkg::common_projection;
std::vector<int> input;
CHECK(!common_projection(input, identity_projection{}).has_value());
input.push_back(42);
CHECK(common_projection(input, identity_projection{}).value_or_exit(VCPKG_LINE_INFO) == 42);
input.push_back(42);
CHECK(common_projection(input, identity_projection{}).value_or_exit(VCPKG_LINE_INFO) == 42);
input.push_back(1729);
CHECK(!common_projection(input, identity_projection{}).has_value());
}

Просмотреть файл

@ -0,0 +1,159 @@
#include <catch2/catch.hpp>
#include <Windows.h>
#include <string>
#include <vcpkg/base/optional.h>
#include <vcpkg/base/stringview.h>
#include <vcpkg/base/zstringview.h>
#include <vcpkg/base/strings.h>
#include <vcpkg/base/system.h>
using vcpkg::Optional;
using vcpkg::StringView;
using vcpkg::ZStringView;
using vcpkg::Checks::check_exit;
using vcpkg::System::get_environment_variable;
using vcpkg::System::to_cpu_architecture;
using vcpkg::System::guess_visual_studio_prompt_target_architecture;
using vcpkg::nullopt;
using vcpkg::System::CPUArchitecture;
namespace
{
void set_environment_variable(StringView varname, Optional<std::string> value)
{
#if defined(_WIN32)
const auto w_varname = vcpkg::Strings::to_utf16(varname);
const auto w_varcstr = w_varname.c_str();
BOOL exit_code;
if (value)
{
const auto w_value = vcpkg::Strings::to_utf16(value.value_or_exit(VCPKG_LINE_INFO));
exit_code = SetEnvironmentVariableW(w_varcstr, w_value.c_str());
}
else
{
exit_code = SetEnvironmentVariableW(w_varcstr, nullptr);
}
check_exit(VCPKG_LINE_INFO, exit_code != 0);
#else // ^^^ defined(_WIN32) / !defined(_WIN32) vvv
std::string tmp;
tmp.reserve(varname.size() + value.size() + 1);
tmp.append(varname.data(), varname.size());
tmp.push_back('=');
if (value)
{
const auto& unpacked = value.value_or_exit(VCPKG_LINE_INFO);
tmp.append(unpacked);
}
const int exit_code = putenv(tmp.c_str());
check_exit(VCPKG_LINE_INFO, exit_code == 0);
#endif // defined(_WIN32)
}
struct environment_variable_resetter
{
explicit environment_variable_resetter(ZStringView varname_)
: varname(varname_), old_value(get_environment_variable(varname))
{
}
~environment_variable_resetter() { set_environment_variable(varname, old_value); }
environment_variable_resetter(const environment_variable_resetter&) = delete;
environment_variable_resetter& operator=(const environment_variable_resetter&) = delete;
private:
ZStringView varname;
Optional<std::string> old_value;
};
}
TEST_CASE ("[to_cpu_architecture]", "system")
{
struct test_case
{
Optional<CPUArchitecture> expected;
StringView input;
};
const test_case test_cases[] = {
{CPUArchitecture::X86, "x86"},
{CPUArchitecture::X86, "X86"},
{CPUArchitecture::X64, "x64"},
{CPUArchitecture::X64, "X64"},
{CPUArchitecture::X64, "AmD64"},
{CPUArchitecture::ARM, "ARM"},
{CPUArchitecture::ARM64, "ARM64"},
{nullopt, "ARM6"},
{nullopt, "AR"},
{nullopt, "Intel"},
};
for (auto&& instance : test_cases)
{
CHECK(to_cpu_architecture(instance.input) == instance.expected);
}
}
TEST_CASE ("from_cpu_architecture", "[system]")
{
struct test_case
{
CPUArchitecture input;
ZStringView expected;
};
const test_case test_cases[] = {
{CPUArchitecture::X86, "x86"},
{CPUArchitecture::X64, "x64"},
{CPUArchitecture::ARM, "arm"},
{CPUArchitecture::ARM64, "arm64"},
};
for (auto&& instance : test_cases)
{
CHECK(to_zstring_view(instance.input) == instance.expected);
}
}
TEST_CASE ("guess_visual_studio_prompt", "[system]")
{
environment_variable_resetter reset_VSCMD_ARG_TGT_ARCH{"VSCMD_ARG_TGT_ARCH"};
environment_variable_resetter reset_VCINSTALLDIR{"VCINSTALLDIR"};
environment_variable_resetter reset_Platform{"Platform"};
set_environment_variable("Platform", "x86"); // ignored if VCINSTALLDIR unset
set_environment_variable("VCINSTALLDIR", nullopt);
set_environment_variable("VSCMD_ARG_TGT_ARCH", nullopt);
CHECK(!guess_visual_studio_prompt_target_architecture().has_value());
set_environment_variable("VSCMD_ARG_TGT_ARCH", "x86");
CHECK(guess_visual_studio_prompt_target_architecture()
.value_or_exit(VCPKG_LINE_INFO) == CPUArchitecture::X86);
set_environment_variable("VSCMD_ARG_TGT_ARCH", "x64");
CHECK(guess_visual_studio_prompt_target_architecture()
.value_or_exit(VCPKG_LINE_INFO) == CPUArchitecture::X64);
set_environment_variable("VSCMD_ARG_TGT_ARCH", "arm");
CHECK(guess_visual_studio_prompt_target_architecture()
.value_or_exit(VCPKG_LINE_INFO) == CPUArchitecture::ARM);
set_environment_variable("VSCMD_ARG_TGT_ARCH", "arm64");
CHECK(guess_visual_studio_prompt_target_architecture()
.value_or_exit(VCPKG_LINE_INFO) == CPUArchitecture::ARM64);
// check that apparent "nested" prompts defer to "vsdevcmd"
set_environment_variable("VCINSTALLDIR", "anything");
CHECK(guess_visual_studio_prompt_target_architecture()
.value_or_exit(VCPKG_LINE_INFO) == CPUArchitecture::ARM64);
set_environment_variable("VSCMD_ARG_TGT_ARCH", nullopt);
set_environment_variable("Platform", nullopt);
CHECK(guess_visual_studio_prompt_target_architecture()
.value_or_exit(VCPKG_LINE_INFO) == CPUArchitecture::X86);
set_environment_variable("Platform", "x86");
CHECK(guess_visual_studio_prompt_target_architecture()
.value_or_exit(VCPKG_LINE_INFO) == CPUArchitecture::X86);
set_environment_variable("Platform", "x64");
CHECK(guess_visual_studio_prompt_target_architecture()
.value_or_exit(VCPKG_LINE_INFO) == CPUArchitecture::X64);
}

Просмотреть файл

@ -15,7 +15,7 @@ namespace vcpkg
g_shutdown_handler = func;
}
void Checks::final_cleanup_and_exit(const int exit_code)
[[noreturn]] void Checks::final_cleanup_and_exit(const int exit_code)
{
static std::atomic<bool> have_entered{false};
if (have_entered.exchange(true))
@ -37,7 +37,7 @@ namespace vcpkg
std::exit(exit_code);
}
void Checks::unreachable(const LineInfo& line_info)
[[noreturn]] void Checks::unreachable(const LineInfo& line_info)
{
System::print2(System::Color::error, "Error: Unreachable code was reached\n");
System::print2(System::Color::error, line_info, '\n'); // Always print line_info here
@ -48,13 +48,13 @@ namespace vcpkg
#endif
}
void Checks::exit_with_code(const LineInfo& line_info, const int exit_code)
[[noreturn]] void Checks::exit_with_code(const LineInfo& line_info, const int exit_code)
{
Debug::print(System::Color::error, line_info, '\n');
final_cleanup_and_exit(exit_code);
}
void Checks::exit_with_message(const LineInfo& line_info, StringView error_message)
[[noreturn]] void Checks::exit_with_message(const LineInfo& line_info, StringView error_message)
{
System::print2(System::Color::error, error_message, '\n');
exit_fail(line_info);

Просмотреть файл

@ -22,6 +22,18 @@ namespace vcpkg
return nullopt;
}
ZStringView System::to_zstring_view(CPUArchitecture arch) noexcept
{
switch (arch)
{
case CPUArchitecture::X86: return "x86";
case CPUArchitecture::X64: return "x64";
case CPUArchitecture::ARM: return "arm";
case CPUArchitecture::ARM64: return "arm64";
default: Checks::exit_with_message(VCPKG_LINE_INFO, "unexpected vcpkg::System::CPUArchitecture");
}
}
CPUArchitecture System::get_host_processor()
{
#if defined(_WIN32)
@ -30,7 +42,7 @@ namespace vcpkg
const auto procarch = get_environment_variable("PROCESSOR_ARCHITECTURE").value_or_exit(VCPKG_LINE_INFO);
return to_cpu_architecture(procarch).value_or_exit(VCPKG_LINE_INFO);
#else
#else // ^^^ defined(_WIN32) / !defined(_WIN32) vvv
#if defined(__x86_64__) || defined(_M_X64)
return CPUArchitecture::X64;
#elif defined(__x86__) || defined(_M_X86)
@ -39,10 +51,10 @@ namespace vcpkg
return CPUArchitecture::ARM;
#elif defined(__aarch64__) || defined(_M_ARM64)
return CPUArchitecture::ARM64;
#else
#else // choose architecture
#error "Unknown host architecture"
#endif
#endif
#endif // choose architecture
#endif // defined(_WIN32)
}
std::vector<CPUArchitecture> System::get_supported_host_architectures()
@ -67,7 +79,7 @@ namespace vcpkg
{
supported_architectures.push_back(CPUArchitecture::X86);
}
#endif
#endif // defined(_WIN32)
return supported_architectures;
}
@ -86,11 +98,11 @@ namespace vcpkg
Checks::check_exit(VCPKG_LINE_INFO, sz2 + 1 == sz);
ret.pop_back();
return Strings::to_utf8(ret.c_str());
#else
#else // ^^^ defined(_WIN32) / !defined(_WIN32) vvv
auto v = getenv(varname.c_str());
if (!v) return nullopt;
return std::string(v);
#endif
#endif // defined(_WIN32)
}
#if defined(_WIN32)
@ -125,13 +137,13 @@ namespace vcpkg
ret.pop_back(); // remove extra trailing null byte
return Strings::to_utf8(ret);
}
#else
#else // ^^^ defined(_WIN32) / !defined(_WIN32) vvv
Optional<std::string> System::get_registry_string(void*, StringView, StringView) { return nullopt; }
#endif
#endif // defined(_WIN32)
static const Optional<fs::path>& get_program_files()
{
static const auto PATH = []() -> Optional<fs::path> {
static const auto PROGRAMFILES = []() -> Optional<fs::path> {
auto value = System::get_environment_variable("PROGRAMFILES");
if (auto v = value.get())
{
@ -141,12 +153,12 @@ namespace vcpkg
return nullopt;
}();
return PATH;
return PROGRAMFILES;
}
const Optional<fs::path>& System::get_program_files_32_bit()
{
static const auto PATH = []() -> Optional<fs::path> {
static const auto PROGRAMFILES_x86 = []() -> Optional<fs::path> {
auto value = System::get_environment_variable("ProgramFiles(x86)");
if (auto v = value.get())
{
@ -154,12 +166,12 @@ namespace vcpkg
}
return get_program_files();
}();
return PATH;
return PROGRAMFILES_x86;
}
const Optional<fs::path>& System::get_program_files_platform_bitness()
{
static const auto PATH = []() -> Optional<fs::path> {
static const auto ProgramW6432 = []() -> Optional<fs::path> {
auto value = System::get_environment_variable("ProgramW6432");
if (auto v = value.get())
{
@ -167,10 +179,36 @@ namespace vcpkg
}
return get_program_files();
}();
return PATH;
return ProgramW6432;
}
int System::get_num_logical_cores() { return std::thread::hardware_concurrency(); }
Optional<CPUArchitecture> System::guess_visual_studio_prompt_target_architecture()
{
// Check for the "vsdevcmd" infrastructure used by Visual Studio 2017 and later
const auto VSCMD_ARG_TGT_ARCH = System::get_environment_variable("VSCMD_ARG_TGT_ARCH");
if (VSCMD_ARG_TGT_ARCH)
{
return to_cpu_architecture(VSCMD_ARG_TGT_ARCH.value_or_exit(VCPKG_LINE_INFO));
}
// Check for the "vcvarsall" infrastructure used by Visual Studio 2015
if (System::get_environment_variable("VCINSTALLDIR"))
{
const auto Platform = System::get_environment_variable("Platform");
if (Platform)
{
return to_cpu_architecture(Platform.value_or_exit(VCPKG_LINE_INFO));
}
else
{
return CPUArchitecture::X86;
}
}
return nullopt;
}
}
namespace vcpkg::Debug

Просмотреть файл

@ -699,15 +699,49 @@ namespace vcpkg::Install
std::string specs_string;
for (auto&& remove_action : action_plan.remove_actions)
{
if (!specs_string.empty()) specs_string += ",";
if (!specs_string.empty()) specs_string.push_back(',');
specs_string += "R$" + Hash::get_string_hash(remove_action.spec.to_string(), Hash::Algorithm::Sha256);
}
for (auto&& install_action : action_plan.install_actions)
{
if (!specs_string.empty()) specs_string += ",";
if (!specs_string.empty()) specs_string.push_back(',');
specs_string += Hash::get_string_hash(install_action.spec.to_string(), Hash::Algorithm::Sha256);
}
#if defined(_WIN32)
const auto maybe_common_triplet = common_projection(
action_plan.install_actions, [](const InstallPlanAction& to_install) { return to_install.spec.triplet(); });
if (maybe_common_triplet)
{
const auto& common_triplet = maybe_common_triplet.value_or_exit(VCPKG_LINE_INFO);
const auto maybe_common_arch = common_triplet.guess_architecture();
if (maybe_common_arch)
{
const auto maybe_vs_prompt = System::guess_visual_studio_prompt_target_architecture();
if (maybe_vs_prompt)
{
const auto common_arch = maybe_common_arch.value_or_exit(VCPKG_LINE_INFO);
const auto vs_prompt = maybe_vs_prompt.value_or_exit(VCPKG_LINE_INFO);
if (common_arch != vs_prompt)
{
const auto vs_prompt_view = to_zstring_view(vs_prompt);
System::print2(vcpkg::System::Color::warning,
"warning: vcpkg appears to be in a Visual Studio prompt targeting ",
vs_prompt_view,
" but is installing packages for ",
common_triplet.to_string(),
". Consider using --triplet ",
vs_prompt_view,
"-windows or --triplet ",
vs_prompt_view,
"-uwp.\n");
}
}
}
}
#endif // defined(_WIN32)
Metrics::g_metrics.lock()->track_property("installplan_1", specs_string);
Dependencies::print_plan(action_plan, is_recursive, paths.ports);

Просмотреть файл

@ -32,12 +32,12 @@ namespace vcpkg
const Triplet Triplet::X86_WINDOWS = from_canonical_name("x86-windows");
const Triplet Triplet::X64_WINDOWS = from_canonical_name("x64-windows");
const Triplet Triplet::ARM_WINDOWS = from_canonical_name("arm-windows");
const Triplet Triplet::ARM64_WINDOWS = from_canonical_name("arm64-windows");
const Triplet Triplet::X86_UWP = from_canonical_name("x86-uwp");
const Triplet Triplet::X64_UWP = from_canonical_name("x64-uwp");
const Triplet Triplet::ARM_UWP = from_canonical_name("arm-uwp");
const Triplet Triplet::ARM64_UWP = from_canonical_name("arm64-uwp");
const Triplet Triplet::ARM_WINDOWS = from_canonical_name("arm-windows");
const Triplet Triplet::ARM64_WINDOWS = from_canonical_name("arm64-windows");
Triplet Triplet::from_canonical_name(std::string&& triplet_as_string)
{
@ -51,4 +51,27 @@ namespace vcpkg
const std::string& Triplet::to_string() const { return this->canonical_name(); }
void Triplet::to_string(std::string& out) const { out.append(this->canonical_name()); }
size_t Triplet::hash_code() const { return m_instance->hash; }
Optional<System::CPUArchitecture> Triplet::guess_architecture() const noexcept
{
using System::CPUArchitecture;
if (*this == X86_WINDOWS || *this == X86_UWP)
{
return CPUArchitecture::X86;
}
else if (*this == X64_WINDOWS || *this == X64_UWP)
{
return CPUArchitecture::X64;
}
else if (*this == ARM_WINDOWS || *this == ARM_UWP)
{
return CPUArchitecture::ARM;
}
else if (*this == ARM64_WINDOWS || *this == ARM64_UWP)
{
return CPUArchitecture::ARM64;
}
return nullopt;
}
}