Implement a warning when running vcpkg inside a developer command prompt if the set of packages to install all target a different architecture than the prompt.
MODIFY
include/vcpkg/base/optional.h
include/vcpkg/base/optional.h
@@ -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;
+ }
}

MODIFY
include/vcpkg/base/system.h
include/vcpkg/base/system.h
@@ -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();
}

MODIFY
include/vcpkg/triplet.h
include/vcpkg/triplet.h
#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(); }

ADD
src/vcpkg-test/optional.cpp
+#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());
+}

ADD
src/vcpkg-test/system.cpp
+#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"};
+ 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);
+}

MODIFY
src/vcpkg/base/checks.cpp
src/vcpkg/base/checks.cpp
@@ -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);

MODIFY
src/vcpkg/base/system.cpp
src/vcpkg/base/system.cpp
@@ -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,22 @@ 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));
+ }
+
+ return nullopt;
+ }
}
namespace vcpkg::Debug

MODIFY
src/vcpkg/install.cpp
src/vcpkg/install.cpp
@@ -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);

MODIFY
src/vcpkg/triplet.cpp
src/vcpkg/triplet.cpp
@@ -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;
+ }
}

Implement a warning when running vcpkg inside a developer command prompt if the set of packages to install all target a different architecture than the prompt.