diff options
| author | Barath Kannan <barathsotd@gmail.com> | 2017-12-27 16:57:43 +1100 |
|---|---|---|
| committer | Barath Kannan <barathsotd@gmail.com> | 2017-12-27 16:57:43 +1100 |
| commit | d8f0ea999983892b8e5e49340ece3474ee257156 (patch) | |
| tree | ce9b615075350d90d0b7312874bae6dd202fe93b /toolsrc/src | |
| parent | 22e9a2b25db21e1d1a1f75786442f2c90ae0db0c (diff) | |
| parent | 6e05f9cdf5cf4d53153214f4d4b29133b368bc4d (diff) | |
| download | vcpkg-d8f0ea999983892b8e5e49340ece3474ee257156.tar.gz vcpkg-d8f0ea999983892b8e5e49340ece3474ee257156.zip | |
resolve merge conflicts
Diffstat (limited to 'toolsrc/src')
35 files changed, 1416 insertions, 635 deletions
diff --git a/toolsrc/src/tests.chrono.cpp b/toolsrc/src/tests.chrono.cpp new file mode 100644 index 000000000..269cdca58 --- /dev/null +++ b/toolsrc/src/tests.chrono.cpp @@ -0,0 +1,41 @@ +#include "tests.pch.h" + +using namespace Microsoft::VisualStudio::CppUnitTestFramework; + +namespace Chrono = vcpkg::Chrono; + +namespace UnitTest1 +{ + class ChronoTests : public TestClass<ChronoTests> + { + TEST_METHOD(parse_time) + { + auto timestring = "1990-02-03T04:05:06.0Z"; + auto maybe_time = Chrono::CTime::parse(timestring); + + Assert::IsTrue(maybe_time.has_value()); + + Assert::AreEqual(timestring, maybe_time.get()->to_string().c_str()); + } + + TEST_METHOD(parse_time_blank) + { + auto maybe_time = Chrono::CTime::parse(""); + + Assert::IsFalse(maybe_time.has_value()); + } + + TEST_METHOD(time_difference) + { + auto maybe_time1 = Chrono::CTime::parse("1990-02-03T04:05:06.0Z"); + auto maybe_time2 = Chrono::CTime::parse("1990-02-10T04:05:06.0Z"); + + Assert::IsTrue(maybe_time1.has_value()); + Assert::IsTrue(maybe_time2.has_value()); + + auto delta = maybe_time2.get()->to_time_point() - maybe_time1.get()->to_time_point(); + + Assert::AreEqual(24 * 7, std::chrono::duration_cast<std::chrono::hours>(delta).count()); + } + }; +} diff --git a/toolsrc/src/tests.plan.cpp b/toolsrc/src/tests.plan.cpp index 122a4ffef..781588c91 100644 --- a/toolsrc/src/tests.plan.cpp +++ b/toolsrc/src/tests.plan.cpp @@ -36,7 +36,7 @@ namespace UnitTest1 std::vector<std::string> vec, const Triplet& triplet = Triplet::X86_WINDOWS) { - const auto& plan = install_action->install_plan.value_or_exit(VCPKG_LINE_INFO); + const auto& plan = install_action->install_action.value_or_exit(VCPKG_LINE_INFO); const auto& feature_list = plan.feature_list; Assert::AreEqual(plan.spec.triplet().to_string().c_str(), triplet.to_string().c_str()); @@ -61,7 +61,7 @@ namespace UnitTest1 std::string pkg_name, const Triplet& triplet = Triplet::X86_WINDOWS) { - const auto& plan = remove_action->remove_plan.value_or_exit(VCPKG_LINE_INFO); + const auto& plan = remove_action->remove_action.value_or_exit(VCPKG_LINE_INFO); Assert::AreEqual(plan.spec.triplet().to_string().c_str(), triplet.to_string().c_str()); Assert::AreEqual(pkg_name.c_str(), plan.spec.name().c_str()); } @@ -98,7 +98,7 @@ namespace UnitTest1 auto spec_b = spec_map.emplace("b", "c"); auto spec_c = spec_map.emplace("c"); - Dependencies::MapPortFile map_port(spec_map.map); + Dependencies::MapPortFileProvider map_port(spec_map.map); auto install_plan = Dependencies::create_install_plan(map_port, {spec_a}, StatusParagraphs(std::move(status_paragraphs))); @@ -122,7 +122,7 @@ namespace UnitTest1 auto spec_g = spec_map.emplace("g"); auto spec_h = spec_map.emplace("h"); - Dependencies::MapPortFile map_port(spec_map.map); + Dependencies::MapPortFileProvider map_port(spec_map.map); auto install_plan = Dependencies::create_install_plan( map_port, {spec_a, spec_b, spec_c}, StatusParagraphs(std::move(status_paragraphs))); @@ -162,7 +162,7 @@ namespace UnitTest1 StatusParagraphs(std::move(status_paragraphs))); Assert::AreEqual(size_t(1), install_plan.size()); - auto p = install_plan[0].install_plan.get(); + auto p = install_plan[0].install_action.get(); Assert::IsNotNull(p); Assert::AreEqual("a", p->spec.name().c_str()); Assert::AreEqual(Dependencies::InstallPlanType::ALREADY_INSTALLED, p->plan_type); @@ -183,13 +183,13 @@ namespace UnitTest1 StatusParagraphs(std::move(status_paragraphs))); Assert::AreEqual(size_t(2), install_plan.size()); - auto p = install_plan[0].install_plan.get(); + auto p = install_plan[0].install_action.get(); Assert::IsNotNull(p); Assert::AreEqual("b", p->spec.name().c_str()); Assert::AreEqual(Dependencies::InstallPlanType::BUILD_AND_INSTALL, p->plan_type); Assert::AreEqual(Dependencies::RequestType::AUTO_SELECTED, p->request_type); - auto p2 = install_plan[1].install_plan.get(); + auto p2 = install_plan[1].install_action.get(); Assert::IsNotNull(p2); Assert::AreEqual("a", p2->spec.name().c_str()); Assert::AreEqual(Dependencies::InstallPlanType::BUILD_AND_INSTALL, p2->plan_type); @@ -215,7 +215,7 @@ namespace UnitTest1 auto spec_j = spec_map.emplace("j", "k"); auto spec_k = spec_map.emplace("k"); - Dependencies::MapPortFile map_port(spec_map.map); + Dependencies::MapPortFileProvider map_port(spec_map.map); auto install_plan = Dependencies::create_install_plan(map_port, {spec_a}, StatusParagraphs(std::move(status_paragraphs))); @@ -521,4 +521,138 @@ namespace UnitTest1 Assert::AreEqual("expat", remove_plan[2].spec.name().c_str()); } }; + + class UpgradePlanTests : public TestClass<UpgradePlanTests> + { + TEST_METHOD(basic_upgrade_scheme) + { + std::vector<std::unique_ptr<StatusParagraph>> pghs; + pghs.push_back(make_status_pgh("a")); + StatusParagraphs status_db(std::move(pghs)); + + PackageSpecMap spec_map(Triplet::X86_WINDOWS); + auto spec_a = spec_map.emplace("a"); + + Dependencies::MapPortFileProvider provider(spec_map.map); + Dependencies::PackageGraph graph(provider, status_db); + + graph.upgrade(spec_a); + + auto plan = graph.serialize(); + + Assert::AreEqual(size_t(2), plan.size()); + Assert::AreEqual("a", plan[0].spec().name().c_str()); + Assert::IsTrue(plan[0].remove_action.has_value()); + Assert::AreEqual("a", plan[1].spec().name().c_str()); + Assert::IsTrue(plan[1].install_action.has_value()); + } + + TEST_METHOD(basic_upgrade_scheme_with_recurse) + { + std::vector<std::unique_ptr<StatusParagraph>> pghs; + pghs.push_back(make_status_pgh("a")); + pghs.push_back(make_status_pgh("b", "a")); + StatusParagraphs status_db(std::move(pghs)); + + PackageSpecMap spec_map(Triplet::X86_WINDOWS); + auto spec_a = spec_map.emplace("a"); + spec_map.emplace("b", "a"); + + Dependencies::MapPortFileProvider provider(spec_map.map); + Dependencies::PackageGraph graph(provider, status_db); + + graph.upgrade(spec_a); + + auto plan = graph.serialize(); + + Assert::AreEqual(size_t(4), plan.size()); + Assert::AreEqual("b", plan[0].spec().name().c_str()); + Assert::IsTrue(plan[0].remove_action.has_value()); + + Assert::AreEqual("a", plan[1].spec().name().c_str()); + Assert::IsTrue(plan[1].remove_action.has_value()); + + Assert::AreEqual("a", plan[2].spec().name().c_str()); + Assert::IsTrue(plan[2].install_action.has_value()); + + Assert::AreEqual("b", plan[3].spec().name().c_str()); + Assert::IsTrue(plan[3].install_action.has_value()); + } + + TEST_METHOD(basic_upgrade_scheme_with_bystander) + { + std::vector<std::unique_ptr<StatusParagraph>> pghs; + pghs.push_back(make_status_pgh("a")); + pghs.push_back(make_status_pgh("b")); + StatusParagraphs status_db(std::move(pghs)); + + PackageSpecMap spec_map(Triplet::X86_WINDOWS); + auto spec_a = spec_map.emplace("a"); + spec_map.emplace("b", "a"); + + Dependencies::MapPortFileProvider provider(spec_map.map); + Dependencies::PackageGraph graph(provider, status_db); + + graph.upgrade(spec_a); + + auto plan = graph.serialize(); + + Assert::AreEqual(size_t(2), plan.size()); + Assert::AreEqual("a", plan[0].spec().name().c_str()); + Assert::IsTrue(plan[0].remove_action.has_value()); + Assert::AreEqual("a", plan[1].spec().name().c_str()); + Assert::IsTrue(plan[1].install_action.has_value()); + } + + TEST_METHOD(basic_upgrade_scheme_with_new_dep) + { + std::vector<std::unique_ptr<StatusParagraph>> pghs; + pghs.push_back(make_status_pgh("a")); + StatusParagraphs status_db(std::move(pghs)); + + PackageSpecMap spec_map(Triplet::X86_WINDOWS); + auto spec_a = spec_map.emplace("a", "b"); + spec_map.emplace("b"); + + Dependencies::MapPortFileProvider provider(spec_map.map); + Dependencies::PackageGraph graph(provider, status_db); + + graph.upgrade(spec_a); + + auto plan = graph.serialize(); + + Assert::AreEqual(size_t(3), plan.size()); + Assert::AreEqual("a", plan[0].spec().name().c_str()); + Assert::IsTrue(plan[0].remove_action.has_value()); + Assert::AreEqual("b", plan[1].spec().name().c_str()); + Assert::IsTrue(plan[1].install_action.has_value()); + Assert::AreEqual("a", plan[2].spec().name().c_str()); + Assert::IsTrue(plan[2].install_action.has_value()); + } + + TEST_METHOD(basic_upgrade_scheme_with_features) + { + std::vector<std::unique_ptr<StatusParagraph>> pghs; + pghs.push_back(make_status_pgh("a")); + pghs.push_back(make_status_feature_pgh("a", "a1")); + StatusParagraphs status_db(std::move(pghs)); + + PackageSpecMap spec_map(Triplet::X86_WINDOWS); + auto spec_a = spec_map.emplace("a", "", {{"a1", ""}}); + + Dependencies::MapPortFileProvider provider(spec_map.map); + Dependencies::PackageGraph graph(provider, status_db); + + graph.upgrade(spec_a); + + auto plan = graph.serialize(); + + Assert::AreEqual(size_t(2), plan.size()); + + Assert::AreEqual("a", plan[0].spec().name().c_str()); + Assert::IsTrue(plan[0].remove_action.has_value()); + + features_check(&plan[1], "a", {"core", "a1"}); + } + }; } diff --git a/toolsrc/src/tests.update.cpp b/toolsrc/src/tests.update.cpp index 06ae797f4..b6e487c17 100644 --- a/toolsrc/src/tests.update.cpp +++ b/toolsrc/src/tests.update.cpp @@ -9,6 +9,8 @@ using namespace vcpkg::Update; namespace UnitTest1 { + using Pgh = std::vector<std::unordered_map<std::string, std::string>>; + class UpdateTests : public TestClass<UpdateTests> { TEST_METHOD(find_outdated_packages_basic) @@ -19,10 +21,12 @@ namespace UnitTest1 StatusParagraphs status_db(std::move(status_paragraphs)); - std::map<std::string, VersionT> port_versions; - port_versions["a"] = VersionT("0"); + std::unordered_map<std::string, SourceControlFile> map; + auto scf = unwrap(SourceControlFile::parse_control_file(Pgh{{{"Source", "a"}, {"Version", "0"}}})); + map.emplace("a", std::move(*scf)); + Dependencies::MapPortFileProvider provider(map); - auto pkgs = SortedVector<OutdatedPackage>(Update::find_outdated_packages(port_versions, status_db), + auto pkgs = SortedVector<OutdatedPackage>(Update::find_outdated_packages(provider, status_db), &OutdatedPackage::compare_by_name); Assert::AreEqual(size_t(1), pkgs.size()); @@ -41,10 +45,12 @@ namespace UnitTest1 StatusParagraphs status_db(std::move(status_paragraphs)); - std::map<std::string, VersionT> port_versions; - port_versions["a"] = VersionT("0"); + std::unordered_map<std::string, SourceControlFile> map; + auto scf = unwrap(SourceControlFile::parse_control_file(Pgh{{{"Source", "a"}, {"Version", "0"}}})); + map.emplace("a", std::move(*scf)); + Dependencies::MapPortFileProvider provider(map); - auto pkgs = SortedVector<OutdatedPackage>(Update::find_outdated_packages(port_versions, status_db), + auto pkgs = SortedVector<OutdatedPackage>(Update::find_outdated_packages(provider, status_db), &OutdatedPackage::compare_by_name); Assert::AreEqual(size_t(1), pkgs.size()); @@ -65,10 +71,12 @@ namespace UnitTest1 StatusParagraphs status_db(std::move(status_paragraphs)); - std::map<std::string, VersionT> port_versions; - port_versions["a"] = VersionT("0"); + std::unordered_map<std::string, SourceControlFile> map; + auto scf = unwrap(SourceControlFile::parse_control_file(Pgh{{{"Source", "a"}, {"Version", "0"}}})); + map.emplace("a", std::move(*scf)); + Dependencies::MapPortFileProvider provider(map); - auto pkgs = SortedVector<OutdatedPackage>(Update::find_outdated_packages(port_versions, status_db), + auto pkgs = SortedVector<OutdatedPackage>(Update::find_outdated_packages(provider, status_db), &OutdatedPackage::compare_by_name); Assert::AreEqual(size_t(1), pkgs.size()); @@ -84,10 +92,12 @@ namespace UnitTest1 StatusParagraphs status_db(std::move(status_paragraphs)); - std::map<std::string, VersionT> port_versions; - port_versions["a"] = VersionT("2"); + std::unordered_map<std::string, SourceControlFile> map; + auto scf = unwrap(SourceControlFile::parse_control_file(Pgh{{{"Source", "a"}, {"Version", "2"}}})); + map.emplace("a", std::move(*scf)); + Dependencies::MapPortFileProvider provider(map); - auto pkgs = SortedVector<OutdatedPackage>(Update::find_outdated_packages(port_versions, status_db), + auto pkgs = SortedVector<OutdatedPackage>(Update::find_outdated_packages(provider, status_db), &OutdatedPackage::compare_by_name); Assert::AreEqual(size_t(0), pkgs.size()); diff --git a/toolsrc/src/vcpkg.cpp b/toolsrc/src/vcpkg.cpp index 5642f937c..094ea1dc5 100644 --- a/toolsrc/src/vcpkg.cpp +++ b/toolsrc/src/vcpkg.cpp @@ -20,11 +20,13 @@ #include <vcpkg/input.h> #include <vcpkg/metrics.h> #include <vcpkg/paragraphs.h> +#include <vcpkg/userconfig.h> #include <vcpkg/vcpkglib.h> #include <cassert> #include <fstream> #include <memory> +#include <random> #pragma comment(lib, "ole32") #pragma comment(lib, "shell32") @@ -110,6 +112,28 @@ static void inner(const VcpkgCmdArguments& args) if (args.command != "autocomplete") { Commands::Version::warn_if_vcpkg_version_mismatch(paths); + std::string surveydate = *GlobalState::g_surveydate.lock(); + auto maybe_surveydate = Chrono::CTime::parse(surveydate); + if (auto p_surveydate = maybe_surveydate.get()) + { + auto delta = std::chrono::system_clock::now() - p_surveydate->to_time_point(); + // 24 hours/day * 30 days/month + if (std::chrono::duration_cast<std::chrono::hours>(delta).count() > 24 * 30) + { + std::default_random_engine generator( + static_cast<unsigned int>(std::chrono::system_clock::now().time_since_epoch().count())); + std::uniform_int_distribution<int> distribution(1, 4); + + if (distribution(generator) == 1) + { + Metrics::g_metrics.lock()->track_property("surveyprompt", "true"); + System::println( + System::Color::success, + "Your feedback is important to improve Vcpkg! Please take 3 minutes to complete our survey " + "by running: vcpkg contact --survey"); + } + } + } } if (const auto command_function = find_command(Commands::get_available_commands_type_b())) @@ -148,80 +172,41 @@ static void inner(const VcpkgCmdArguments& args) static void load_config() { #if defined(_WIN32) - fs::path localappdata; - { - // Config path in AppDataLocal - wchar_t* localappdatapath = nullptr; - if (S_OK != SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, nullptr, &localappdatapath)) __fastfail(1); - localappdata = localappdatapath; - CoTaskMemFree(localappdatapath); - } - - std::string user_id, user_time, user_mac; - try - { - auto maybe_pghs = Paragraphs::get_paragraphs(Files::get_real_filesystem(), localappdata / "vcpkg" / "config"); - if (const auto p_pghs = maybe_pghs.get()) - { - const auto& pghs = *p_pghs; + auto& fs = Files::get_real_filesystem(); - std::unordered_map<std::string, std::string> keys; - if (pghs.size() > 0) keys = pghs[0]; - - for (size_t x = 1; x < pghs.size(); ++x) - { - for (auto&& p : pghs[x]) - keys.insert(p); - } - - user_id = keys["User-Id"]; - user_time = keys["User-Since"]; - user_mac = keys["Mac-Hash"]; - } - } - catch (...) - { - } + auto config = UserConfig::try_read_data(fs); bool write_config = false; // config file not found, could not be read, or invalid - if (user_id.empty() || user_time.empty()) + if (config.user_id.empty() || config.user_time.empty()) { - ::vcpkg::Metrics::Metrics::init_user_information(user_id, user_time); + ::vcpkg::Metrics::Metrics::init_user_information(config.user_id, config.user_time); write_config = true; } - if (user_mac.empty()) + if (config.user_mac.empty()) { - user_mac = Metrics::get_MAC_user(); + config.user_mac = Metrics::get_MAC_user(); write_config = true; } { auto locked_metrics = Metrics::g_metrics.lock(); - locked_metrics->set_user_information(user_id, user_time); - locked_metrics->track_property("user_mac", user_mac); + locked_metrics->set_user_information(config.user_id, config.user_time); + locked_metrics->track_property("user_mac", config.user_mac); } + if (config.last_completed_survey.empty()) + { + config.last_completed_survey = config.user_time; + } + + GlobalState::g_surveydate.lock()->assign(config.last_completed_survey); + if (write_config) { - try - { - std::error_code ec; - auto& fs = Files::get_real_filesystem(); - fs.create_directory(localappdata / "vcpkg", ec); - fs.write_contents(localappdata / "vcpkg" / "config", - Strings::format("User-Id: %s\n" - "User-Since: %s\n" - "Mac-Hash: %s\n", - user_id, - user_time, - user_mac)); - } - catch (...) - { - } + config.try_write_data(fs); } #endif } @@ -255,7 +240,7 @@ int main(const int argc, const char* const* const argv) { if (argc == 0) std::abort(); - *GlobalState::timer.lock() = Chrono::ElapsedTime::create_started(); + *GlobalState::timer.lock() = Chrono::ElapsedTimer::create_started(); #if defined(_WIN32) GlobalState::g_init_console_cp = GetConsoleCP(); diff --git a/toolsrc/src/vcpkg/base/checks.cpp b/toolsrc/src/vcpkg/base/checks.cpp index ed28d6e2b..23f2cc630 100644 --- a/toolsrc/src/vcpkg/base/checks.cpp +++ b/toolsrc/src/vcpkg/base/checks.cpp @@ -49,8 +49,7 @@ namespace vcpkg::Checks #else void register_console_ctrl_handler() {} #endif - - [[noreturn]] void unreachable(const LineInfo& line_info) + void unreachable(const LineInfo& line_info) { System::println(System::Color::error, "Error: Unreachable code was reached"); System::println(System::Color::error, line_info.to_string()); // Always print line_info here @@ -61,13 +60,13 @@ namespace vcpkg::Checks #endif } - [[noreturn]] void exit_with_code(const LineInfo& line_info, const int exit_code) + void exit_with_code(const LineInfo& line_info, const int exit_code) { Debug::println(System::Color::error, line_info.to_string()); cleanup_and_exit(exit_code); } - [[noreturn]] void exit_with_message(const LineInfo& line_info, const CStringView error_message) + void exit_with_message(const LineInfo& line_info, const CStringView error_message) { System::println(System::Color::error, error_message); exit_fail(line_info); @@ -77,7 +76,7 @@ namespace vcpkg::Checks { if (!expression) { - exit_with_message(line_info, ""); + exit_fail(line_info); } } diff --git a/toolsrc/src/vcpkg/base/chrono.cpp b/toolsrc/src/vcpkg/base/chrono.cpp index 5d28909fc..00f8ba3f1 100644 --- a/toolsrc/src/vcpkg/base/chrono.cpp +++ b/toolsrc/src/vcpkg/base/chrono.cpp @@ -50,12 +50,82 @@ namespace vcpkg::Chrono return Strings::format("%.4g ns", nanos_as_double); } - ElapsedTime ElapsedTime::create_started() + ElapsedTimer ElapsedTimer::create_started() { - ElapsedTime t; + ElapsedTimer t; t.m_start_tick = std::chrono::high_resolution_clock::now(); return t; } - std::string ElapsedTime::to_string() const { return format_time_userfriendly(elapsed<std::chrono::nanoseconds>()); } + std::string ElapsedTime::to_string() const { return format_time_userfriendly(as<std::chrono::nanoseconds>()); } + + std::string ElapsedTimer::to_string() const { return elapsed().to_string(); } + + Optional<CTime> CTime::get_current_date_time() + { + CTime ret; + +#if defined(_WIN32) + struct _timeb timebuffer; + + _ftime_s(&timebuffer); + + const errno_t err = gmtime_s(&ret.m_tm, &timebuffer.time); + + if (err) + { + return nullopt; + } +#else + time_t now = {0}; + time(&now); + auto null_if_failed = gmtime_r(&now, &ret.m_tm); + if (null_if_failed == nullptr) + { + return nullopt; + } +#endif + + return ret; + } + + Optional<CTime> CTime::parse(CStringView str) + { + CTime ret; + auto assigned = +#if defined(_WIN32) + sscanf_s +#else + sscanf +#endif + (str.c_str(), + "%d-%d-%dT%d:%d:%d.", + &ret.m_tm.tm_year, + &ret.m_tm.tm_mon, + &ret.m_tm.tm_mday, + &ret.m_tm.tm_hour, + &ret.m_tm.tm_min, + &ret.m_tm.tm_sec); + if (assigned != 6) return nullopt; + if (ret.m_tm.tm_year < 1900) return nullopt; + ret.m_tm.tm_year -= 1900; + if (ret.m_tm.tm_mon < 1) return nullopt; + ret.m_tm.tm_mon -= 1; + mktime(&ret.m_tm); + return ret; + } + + std::string CTime::to_string() const + { + std::array<char, 80> date; + date.fill(0); + + strftime(&date[0], date.size(), "%Y-%m-%dT%H:%M:%S.0Z", &m_tm); + return &date[0]; + } + std::chrono::system_clock::time_point CTime::to_time_point() const + { + auto t = mktime(&m_tm); + return std::chrono::system_clock::from_time_t(t); + } } diff --git a/toolsrc/src/vcpkg/base/files.cpp b/toolsrc/src/vcpkg/base/files.cpp index 6e43debb1..8c9e137ed 100644 --- a/toolsrc/src/vcpkg/base/files.cpp +++ b/toolsrc/src/vcpkg/base/files.cpp @@ -72,7 +72,8 @@ namespace vcpkg::Files { std::vector<fs::path> ret; - fs::stdfs::recursive_directory_iterator b(dir), e{}; + std::error_code ec; + fs::stdfs::recursive_directory_iterator b(dir, ec), e{}; for (; b != e; ++b) { ret.push_back(b->path()); diff --git a/toolsrc/src/vcpkg/base/system.cpp b/toolsrc/src/vcpkg/base/system.cpp index 47096ed63..625ee6ce0 100644 --- a/toolsrc/src/vcpkg/base/system.cpp +++ b/toolsrc/src/vcpkg/base/system.cpp @@ -51,11 +51,21 @@ namespace vcpkg::System CPUArchitecture get_host_processor() { +#if defined(_WIN32) auto w6432 = get_environment_variable("PROCESSOR_ARCHITEW6432"); if (const auto p = w6432.get()) return to_cpu_architecture(*p).value_or_exit(VCPKG_LINE_INFO); 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 +#if defined(__x86_64__) || defined(_M_X64) + return CPUArchitecture::X64; +#elif defined(__x86__) || defined(_M_X86) + return CPUArchitecture::X86; +#else +#error "Unknown host architecture" +#endif +#endif } std::vector<CPUArchitecture> get_supported_host_architectures() @@ -72,6 +82,53 @@ namespace vcpkg::System return supported_architectures; } + CMakeVariable::CMakeVariable(const CStringView varname, const char* varvalue) + : s(Strings::format(R"("-D%s=%s")", varname, varvalue)) + { + } + CMakeVariable::CMakeVariable(const CStringView varname, const std::string& varvalue) + : CMakeVariable(varname, varvalue.c_str()) + { + } + CMakeVariable::CMakeVariable(const CStringView varname, const fs::path& path) + : CMakeVariable(varname, path.generic_u8string()) + { + } + + std::string make_cmake_cmd(const fs::path& cmake_exe, + const fs::path& cmake_script, + const std::vector<CMakeVariable>& pass_variables) + { + const std::string cmd_cmake_pass_variables = Strings::join(" ", pass_variables, [](auto&& v) { return v.s; }); + return Strings::format( + R"("%s" %s -P "%s")", cmake_exe.u8string(), cmd_cmake_pass_variables, cmake_script.generic_u8string()); + } + + PowershellParameter::PowershellParameter(const CStringView varname, const char* varvalue) + : s(Strings::format(R"(-%s '%s')", varname, varvalue)) + { + } + + PowershellParameter::PowershellParameter(const CStringView varname, const std::string& varvalue) + : PowershellParameter(varname, varvalue.c_str()) + { + } + + PowershellParameter::PowershellParameter(const CStringView varname, const fs::path& path) + : PowershellParameter(varname, path.generic_u8string()) + { + } + + static std::string make_powershell_cmd(const fs::path& script_path, + const std::vector<PowershellParameter>& parameters) + { + const std::string args = Strings::join(" ", parameters, [](auto&& v) { return v.s; }); + + // TODO: switch out ExecutionPolicy Bypass with "Remove Mark Of The Web" code and restore RemoteSigned + return Strings::format( + R"(powershell -NoProfile -ExecutionPolicy Bypass -Command "& {& '%s' %s}")", script_path.u8string(), args); + } + int cmd_execute_clean(const CStringView cmd_line) { #if defined(_WIN32) @@ -145,6 +202,8 @@ namespace vcpkg::System env_cstr.append(Strings::to_utf16(NEW_PATH)); env_cstr.push_back(L'\0'); + env_cstr.append(L"VSLANG=1033"); + env_cstr.push_back(L'\0'); STARTUPINFOW startup_info; memset(&startup_info, 0, sizeof(STARTUPINFOW)); @@ -270,14 +329,37 @@ namespace vcpkg::System #endif } + void powershell_execute(const std::string& title, + const fs::path& script_path, + const std::vector<PowershellParameter>& parameters) + { + const std::string cmd = make_powershell_cmd(script_path, parameters); + const int rc = System::cmd_execute(cmd); + + if (rc) + { + System::println(Color::error, + "%s\n" + "Could not run:\n" + " '%s'", + title, + script_path.generic_string()); + + { + auto locked_metrics = Metrics::g_metrics.lock(); + locked_metrics->track_property("error", "powershell script failed"); + locked_metrics->track_property("title", title); + } + + Checks::exit_with_code(VCPKG_LINE_INFO, rc); + } + } + std::string powershell_execute_and_capture_output(const std::string& title, const fs::path& script_path, - const CStringView args) + const std::vector<PowershellParameter>& parameters) { - // TODO: switch out ExecutionPolicy Bypass with "Remove Mark Of The Web" code and restore RemoteSigned - const std::string cmd = Strings::format( - R"(powershell -NoProfile -ExecutionPolicy Bypass -Command "& {& '%s' %s}")", script_path.u8string(), args); - + const std::string cmd = make_powershell_cmd(script_path, parameters); auto rc = System::cmd_execute_and_capture_output(cmd); if (rc.exit_code) diff --git a/toolsrc/src/vcpkg/binaryparagraph.cpp b/toolsrc/src/vcpkg/binaryparagraph.cpp index c7136b713..7c9e905e8 100644 --- a/toolsrc/src/vcpkg/binaryparagraph.cpp +++ b/toolsrc/src/vcpkg/binaryparagraph.cpp @@ -92,8 +92,9 @@ namespace vcpkg std::string BinaryParagraph::displayname() const { - const auto f = this->feature.empty() ? "core" : this->feature; - return Strings::format("%s[%s]:%s", this->spec.name(), f, this->spec.triplet()); + if (this->feature.empty() || this->feature == "core") + return Strings::format("%s:%s", this->spec.name(), this->spec.triplet()); + return Strings::format("%s[%s]:%s", this->spec.name(), this->feature, this->spec.triplet()); } std::string BinaryParagraph::dir() const { return this->spec.dir(); } diff --git a/toolsrc/src/vcpkg/build.cpp b/toolsrc/src/vcpkg/build.cpp index e3787a97e..f43d8788e 100644 --- a/toolsrc/src/vcpkg/build.cpp +++ b/toolsrc/src/vcpkg/build.cpp @@ -67,7 +67,7 @@ namespace vcpkg::Build::Command const Build::BuildPackageConfig build_config{ *scf, spec.triplet(), fs::path{port_dir}, build_package_options, features_as_set}; - const auto build_timer = Chrono::ElapsedTime::create_started(); + const auto build_timer = Chrono::ElapsedTimer::create_started(); const auto result = Build::build_package(paths, build_config, status_db); System::println("Elapsed time for package %s: %s", spec.to_string(), build_timer.to_string()); @@ -203,6 +203,10 @@ namespace vcpkg::Build std::string make_build_env_cmd(const PreBuildInfo& pre_build_info, const Toolset& toolset) { + if (pre_build_info.external_toolchain_file) + return Strings::format( + R"("%s" %s 2>&1)", toolset.vcvarsall.u8string(), Strings::join(" ", toolset.vcvarsall_options)); + const char* tonull = " >nul"; if (GlobalState::debugging) { @@ -256,13 +260,13 @@ namespace vcpkg::Build const BuildPackageConfig& config, const StatusParagraphs& status_db) { - const PackageSpec spec = - PackageSpec::from_name_and_triplet(config.src.name, config.triplet).value_or_exit(VCPKG_LINE_INFO); + 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; { std::vector<PackageSpec> missing_specs; - for (auto&& dep : filter_dependencies(config.src.depends, triplet)) + for (auto&& dep : filter_dependencies(config.scf.core_paragraph->depends, triplet)) { if (status_db.find_installed(dep, triplet) == status_db.end()) { @@ -286,27 +290,23 @@ namespace vcpkg::Build std::string features; if (GlobalState::feature_packages) { - if (config.feature_list) + for (auto&& feature : config.feature_list) { - for (auto&& feature : *config.feature_list) - { - features.append(feature + ";"); - } - if (features.size() > 0) - { - features.pop_back(); - } + features.append(feature + ";"); + } + if (features.size() > 0) + { + features.pop_back(); } } - const Toolset& toolset = paths.get_toolset(pre_build_info.platform_toolset, pre_build_info.visual_studio_path); - - const std::string cmd_launch_cmake = make_cmake_cmd( + const Toolset& toolset = paths.get_toolset(pre_build_info); + const std::string cmd_launch_cmake = System::make_cmake_cmd( cmake_exe_path, ports_cmake_script_path, { {"CMD", "BUILD"}, - {"PORT", config.src.name}, + {"PORT", config.scf.core_paragraph->name}, {"CURRENT_PORT_DIR", config.port_dir / "/."}, {"TARGET_TRIPLET", triplet.canonical_name()}, {"VCPKG_PLATFORM_TOOLSET", toolset.version.c_str()}, @@ -320,7 +320,7 @@ namespace vcpkg::Build const auto cmd_set_environment = make_build_env_cmd(pre_build_info, toolset); const std::string command = Strings::format(R"(%s && %s)", cmd_set_environment, cmd_launch_cmake); - const auto timer = Chrono::ElapsedTime::create_started(); + const auto timer = Chrono::ElapsedTimer::create_started(); const int return_code = System::cmd_execute_clean(command); const auto buildtimeus = timer.microseconds(); @@ -340,7 +340,7 @@ namespace vcpkg::Build const BuildInfo build_info = read_build_info(paths.get_filesystem(), 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.src, triplet, build_info); + auto bcf = create_binary_control_file(*config.scf.core_paragraph, triplet, build_info); if (error_count != 0) { @@ -348,16 +348,13 @@ namespace vcpkg::Build } if (GlobalState::feature_packages) { - if (config.feature_list) + for (auto&& feature : config.feature_list) { - for (auto&& feature : *config.feature_list) + for (auto&& f_pgh : config.scf.feature_paragraphs) { - for (auto&& f_pgh : config.scf->feature_paragraphs) - { - if (f_pgh->name == feature) - bcf->features.push_back( - create_binary_feature_control_file(*config.scf->core_paragraph, *f_pgh, triplet)); - } + if (f_pgh->name == feature) + bcf->features.push_back( + create_binary_feature_control_file(*config.scf.core_paragraph, *f_pgh, triplet)); } } } @@ -493,11 +490,11 @@ namespace vcpkg::Build const fs::path ports_cmake_script_path = paths.scripts / "get_triplet_environment.cmake"; const fs::path triplet_file_path = paths.triplets / (triplet.canonical_name() + ".cmake"); - const auto cmd_launch_cmake = make_cmake_cmd(cmake_exe_path, - ports_cmake_script_path, - { - {"CMAKE_TRIPLET_FILE", triplet_file_path}, - }); + const auto cmd_launch_cmake = System::make_cmake_cmd(cmake_exe_path, + ports_cmake_script_path, + { + {"CMAKE_TRIPLET_FILE", triplet_file_path}, + }); const auto ec_data = System::cmd_execute_and_capture_output(cmd_launch_cmake); Checks::check_exit(VCPKG_LINE_INFO, ec_data.exit_code == 0, ec_data.output); @@ -555,6 +552,27 @@ namespace vcpkg::Build continue; } + if (variable_name == "VCPKG_CHAINLOAD_TOOLCHAIN_FILE") + { + pre_build_info.external_toolchain_file = + variable_value.empty() ? nullopt : Optional<std::string>{variable_value}; + continue; + } + + if (variable_name == "VCPKG_BUILD_TYPE") + { + if (variable_value.empty()) + pre_build_info.build_type = nullopt; + else if (Strings::case_insensitive_ascii_equals(variable_value, "debug")) + pre_build_info.build_type = ConfigurationType::DEBUG; + else if (Strings::case_insensitive_ascii_equals(variable_value, "release")) + pre_build_info.build_type = ConfigurationType::RELEASE; + else + Checks::exit_with_message( + VCPKG_LINE_INFO, "Unknown setting for VCPKG_BUILD_TYPE: %s", variable_value); + continue; + } + Checks::exit_with_message(VCPKG_LINE_INFO, "Unknown variable name %s", line); } diff --git a/toolsrc/src/vcpkg/commands.ci.cpp b/toolsrc/src/vcpkg/commands.ci.cpp index 8f79b83e1..1a2f9b47f 100644 --- a/toolsrc/src/vcpkg/commands.ci.cpp +++ b/toolsrc/src/vcpkg/commands.ci.cpp @@ -27,7 +27,7 @@ namespace vcpkg::Commands::CI const std::vector<PackageSpec> specs = PackageSpec::to_package_specs(ports, triplet); StatusParagraphs status_db = database_load_check(paths); - const auto& paths_port_file = Dependencies::PathsPortFile(paths); + const auto& paths_port_file = Dependencies::PathsPortFileProvider(paths); std::vector<InstallPlanAction> install_plan = Dependencies::create_install_plan(paths_port_file, specs, status_db); @@ -63,9 +63,11 @@ namespace vcpkg::Commands::CI }; static const std::string OPTION_EXCLUDE = "--exclude"; + static const std::string OPTION_XUNIT = "--x-xunit"; - static const std::array<CommandSetting, 1> CI_SETTINGS = {{ + static const std::array<CommandSetting, 2> CI_SETTINGS = {{ {OPTION_EXCLUDE, "Comma separated list of ports to skip"}, + {OPTION_XUNIT, "File to output results in XUnit format (internal)"}, }}; const CommandStructure COMMAND_STRUCTURE = { @@ -114,6 +116,18 @@ namespace vcpkg::Commands::CI result.summary.print(); } + auto it_xunit = options.settings.find(OPTION_XUNIT); + if (it_xunit != options.settings.end()) + { + std::string xunit_doc = "<assemblies><assembly><collection>\n"; + + for (auto&& result : results) + xunit_doc += result.summary.xunit_results(); + + xunit_doc += "</collection></assembly></assemblies>\n"; + paths.get_filesystem().write_contents(fs::u8path(it_xunit->second), xunit_doc); + } + Checks::exit_success(VCPKG_LINE_INFO); } } diff --git a/toolsrc/src/vcpkg/commands.contact.cpp b/toolsrc/src/vcpkg/commands.contact.cpp index 07dcea80e..8063fe317 100644 --- a/toolsrc/src/vcpkg/commands.contact.cpp +++ b/toolsrc/src/vcpkg/commands.contact.cpp @@ -1,8 +1,10 @@ #include "pch.h" +#include <vcpkg/base/chrono.h> #include <vcpkg/base/system.h> #include <vcpkg/commands.h> #include <vcpkg/help.h> +#include <vcpkg/userconfig.h> namespace vcpkg::Commands::Contact { @@ -28,6 +30,17 @@ namespace vcpkg::Commands::Contact if (Util::Sets::contains(parsed_args.switches, switches[0].name)) { +#if defined(_WIN32) + auto maybe_now = Chrono::CTime::get_current_date_time(); + if (auto p_now = maybe_now.get()) + { + auto& fs = Files::get_real_filesystem(); + auto config = UserConfig::try_read_data(fs); + config.last_completed_survey = p_now->to_string(); + config.try_write_data(fs); + } +#endif + System::cmd_execute("start https://aka.ms/NPS_vcpkg"); System::println("Default browser launched to https://aka.ms/NPS_vcpkg, thank you for your feedback!"); } diff --git a/toolsrc/src/vcpkg/commands.cpp b/toolsrc/src/vcpkg/commands.cpp index 15b10c7ea..ccf6fa729 100644 --- a/toolsrc/src/vcpkg/commands.cpp +++ b/toolsrc/src/vcpkg/commands.cpp @@ -13,9 +13,10 @@ namespace vcpkg::Commands Span<const PackageNameAndFunction<CommandTypeA>> get_available_commands_type_a() { static std::vector<PackageNameAndFunction<CommandTypeA>> t = { - PackageNameAndFunction<CommandTypeA>{"install", &Install::perform_and_exit}, + {"install", &Install::perform_and_exit}, {"ci", &CI::perform_and_exit}, {"remove", &Remove::perform_and_exit}, + {"upgrade", &Upgrade::perform_and_exit}, {"build", &Build::Command::perform_and_exit}, {"env", &Env::perform_and_exit}, {"build-external", &BuildExternal::perform_and_exit}, diff --git a/toolsrc/src/vcpkg/commands.create.cpp b/toolsrc/src/vcpkg/commands.create.cpp index c7183d257..44f5f7928 100644 --- a/toolsrc/src/vcpkg/commands.create.cpp +++ b/toolsrc/src/vcpkg/commands.create.cpp @@ -4,7 +4,6 @@ #include <vcpkg/base/system.h> #include <vcpkg/commands.h> #include <vcpkg/help.h> -#include <vcpkg/vcpkglib.h> namespace vcpkg::Commands::Create { @@ -25,7 +24,7 @@ namespace vcpkg::Commands::Create const fs::path& cmake_exe = paths.get_cmake_exe(); - std::vector<CMakeVariable> cmake_args{{"CMD", "CREATE"}, {"PORT", port_name}, {"URL", url}}; + std::vector<System::CMakeVariable> cmake_args{{"CMD", "CREATE"}, {"PORT", port_name}, {"URL", url}}; if (args.command_arguments.size() >= 3) { diff --git a/toolsrc/src/vcpkg/commands.env.cpp b/toolsrc/src/vcpkg/commands.env.cpp index 98b5aced9..6e52383d8 100644 --- a/toolsrc/src/vcpkg/commands.env.cpp +++ b/toolsrc/src/vcpkg/commands.env.cpp @@ -20,7 +20,7 @@ namespace vcpkg::Commands::Env args.parse_arguments(COMMAND_STRUCTURE); const auto pre_build_info = Build::PreBuildInfo::from_triplet_file(paths, default_triplet); - const Toolset& toolset = paths.get_toolset(pre_build_info.platform_toolset, pre_build_info.visual_studio_path); + const Toolset& toolset = paths.get_toolset(pre_build_info); System::cmd_execute_clean(Build::make_build_env_cmd(pre_build_info, toolset) + " && cmd"); Checks::exit_success(VCPKG_LINE_INFO); diff --git a/toolsrc/src/vcpkg/commands.integrate.cpp b/toolsrc/src/vcpkg/commands.integrate.cpp index 31b9ec722..460e99b88 100644 --- a/toolsrc/src/vcpkg/commands.integrate.cpp +++ b/toolsrc/src/vcpkg/commands.integrate.cpp @@ -139,7 +139,7 @@ namespace vcpkg::Commands::Integrate static fs::path get_appdata_targets_path() { static const fs::path LOCAL_APP_DATA = - fs::path(System::get_environment_variable("LOCALAPPDATA").value_or_exit(VCPKG_LINE_INFO)); + fs::u8path(System::get_environment_variable("LOCALAPPDATA").value_or_exit(VCPKG_LINE_INFO)); return LOCAL_APP_DATA / "vcpkg" / "vcpkg.user.targets"; } #endif @@ -257,7 +257,7 @@ CMake projects should use: "-DCMAKE_TOOLCHAIN_FILE=%s")", std::error_code ec; const bool was_deleted = fs.remove(path, ec); - Checks::check_exit(VCPKG_LINE_INFO, !ec, "Error: Unable to remove user-wide integration: %d", ec.message()); + Checks::check_exit(VCPKG_LINE_INFO, !ec, "Error: Unable to remove user-wide integration: %s", ec.message()); if (was_deleted) { @@ -324,18 +324,20 @@ With a project open, go to Tools->NuGet Package Manager->Package Manager Console " vcpkg integrate install Make installed packages available user-wide. Requires admin privileges on " "first use\n" " vcpkg integrate remove Remove user-wide integration\n" - " vcpkg integrate project Generate a referencing nuget package for individual VS project use\n"; + " vcpkg integrate project Generate a referencing nuget package for individual VS project use\n" + " vcpkg integrate powershell Enable PowerShell Tab-Completion\n"; namespace Subcommand { static const std::string INSTALL = "install"; static const std::string REMOVE = "remove"; static const std::string PROJECT = "project"; + static const std::string POWERSHELL = "powershell"; } static std::vector<std::string> valid_arguments(const VcpkgPaths&) { - return {Subcommand::INSTALL, Subcommand::REMOVE, Subcommand::PROJECT}; + return {Subcommand::INSTALL, Subcommand::REMOVE, Subcommand::PROJECT, Subcommand::POWERSHELL}; } const CommandStructure COMMAND_STRUCTURE = { @@ -365,6 +367,12 @@ With a project open, go to Tools->NuGet Package Manager->Package Manager Console { return integrate_project(paths); } + if (args.command_arguments[0] == Subcommand::POWERSHELL) + { + System::powershell_execute("PowerShell Tab-Completion", + paths.scripts / "addPoshVcpkgToPowershellProfile.ps1"); + Checks::exit_success(VCPKG_LINE_INFO); + } #endif Checks::exit_with_message(VCPKG_LINE_INFO, "Unknown parameter %s for integrate", args.command_arguments[0]); diff --git a/toolsrc/src/vcpkg/commands.list.cpp b/toolsrc/src/vcpkg/commands.list.cpp index 1f2387843..960c57225 100644 --- a/toolsrc/src/vcpkg/commands.list.cpp +++ b/toolsrc/src/vcpkg/commands.list.cpp @@ -13,14 +13,14 @@ namespace vcpkg::Commands::List { if (full_desc) { - System::println("%-30s %-16s %s", pgh.package.displayname(), pgh.package.version, pgh.package.description); + System::println("%-50s %-16s %s", pgh.package.displayname(), pgh.package.version, pgh.package.description); } else { - System::println("%-30s %-16s %s", - vcpkg::shorten_text(pgh.package.displayname(), 30), + System::println("%-50s %-16s %s", + vcpkg::shorten_text(pgh.package.displayname(), 50), vcpkg::shorten_text(pgh.package.version, 16), - vcpkg::shorten_text(pgh.package.description, 71)); + vcpkg::shorten_text(pgh.package.description, 51)); } } diff --git a/toolsrc/src/vcpkg/commands.portsdiff.cpp b/toolsrc/src/vcpkg/commands.portsdiff.cpp index 0277c8bdb..dba04ce5b 100644 --- a/toolsrc/src/vcpkg/commands.portsdiff.cpp +++ b/toolsrc/src/vcpkg/commands.portsdiff.cpp @@ -98,8 +98,11 @@ namespace vcpkg::Commands::PortsDiff ".vcpkg-root", git_exe.u8string()); System::cmd_execute_clean(cmd); - const std::map<std::string, VersionT> names_and_versions = Paragraphs::load_all_port_names_and_versions( - paths.get_filesystem(), temp_checkout_path / ports_dir_name_as_string); + const auto all_ports = + Paragraphs::load_all_ports(paths.get_filesystem(), temp_checkout_path / ports_dir_name_as_string); + std::map<std::string, VersionT> names_and_versions; + for (auto&& port : all_ports) + names_and_versions.emplace(port->core_paragraph->name, port->core_paragraph->version); fs.remove_all(temp_checkout_path, ec); return names_and_versions; } @@ -151,14 +154,14 @@ namespace vcpkg::Commands::PortsDiff const std::vector<std::string>& added_ports = setp.only_left; if (!added_ports.empty()) { - System::println("\nThe following %d ports were added:", added_ports.size()); + System::println("\nThe following %zd ports were added:", added_ports.size()); do_print_name_and_version(added_ports, current_names_and_versions); } const std::vector<std::string>& removed_ports = setp.only_right; if (!removed_ports.empty()) { - System::println("\nThe following %d ports were removed:", removed_ports.size()); + System::println("\nThe following %zd ports were removed:", removed_ports.size()); do_print_name_and_version(removed_ports, previous_names_and_versions); } @@ -168,7 +171,7 @@ namespace vcpkg::Commands::PortsDiff if (!updated_ports.empty()) { - System::println("\nThe following %d ports were updated:", updated_ports.size()); + System::println("\nThe following %zd ports were updated:", updated_ports.size()); for (const UpdatedPort& p : updated_ports) { System::println(" - %-14s %-16s", p.port, p.version_diff.to_string()); diff --git a/toolsrc/src/vcpkg/commands.upgrade.cpp b/toolsrc/src/vcpkg/commands.upgrade.cpp new file mode 100644 index 000000000..d2c868870 --- /dev/null +++ b/toolsrc/src/vcpkg/commands.upgrade.cpp @@ -0,0 +1,180 @@ +#include "pch.h" + +#include <vcpkg/base/util.h> +#include <vcpkg/commands.h> +#include <vcpkg/dependencies.h> +#include <vcpkg/help.h> +#include <vcpkg/input.h> +#include <vcpkg/install.h> +#include <vcpkg/statusparagraphs.h> +#include <vcpkg/update.h> +#include <vcpkg/vcpkglib.h> + +namespace vcpkg::Commands::Upgrade +{ + using Install::KeepGoing; + using Install::to_keep_going; + + static const std::string OPTION_NO_DRY_RUN = "--no-dry-run"; + static const std::string OPTION_KEEP_GOING = "--keep-going"; + + static const std::array<CommandSwitch, 2> INSTALL_SWITCHES = {{ + {OPTION_NO_DRY_RUN, "Actually upgrade"}, + {OPTION_KEEP_GOING, "Continue installing packages on failure"}, + }}; + + const CommandStructure COMMAND_STRUCTURE = { + Help::create_example_string("upgrade --no-dry-run"), + 0, + SIZE_MAX, + {INSTALL_SWITCHES, {}}, + nullptr, + }; + + void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, const Triplet& default_triplet) + { + const ParsedArguments options = args.parse_arguments(COMMAND_STRUCTURE); + + const bool no_dry_run = Util::Sets::contains(options.switches, OPTION_NO_DRY_RUN); + const KeepGoing keep_going = to_keep_going(Util::Sets::contains(options.switches, OPTION_KEEP_GOING)); + + StatusParagraphs status_db = database_load_check(paths); + + Dependencies::PathsPortFileProvider provider(paths); + Dependencies::PackageGraph graph(provider, status_db); + + // input sanitization + const std::vector<PackageSpec> specs = Util::fmap(args.command_arguments, [&](auto&& arg) { + return Input::check_and_get_package_spec(arg, default_triplet, COMMAND_STRUCTURE.example_text); + }); + + for (auto&& spec : specs) + { + Input::check_triplet(spec.triplet(), paths); + } + + if (specs.empty()) + { + // If no packages specified, upgrade all outdated packages. + auto outdated_packages = Update::find_outdated_packages(provider, status_db); + + if (outdated_packages.empty()) + { + System::println("All installed packages are up-to-date with the local portfiles."); + Checks::exit_success(VCPKG_LINE_INFO); + } + + for (auto&& outdated_package : outdated_packages) + graph.upgrade(outdated_package.spec); + } + else + { + std::vector<PackageSpec> not_installed; + std::vector<PackageSpec> no_portfile; + std::vector<PackageSpec> to_upgrade; + std::vector<PackageSpec> up_to_date; + + for (auto&& spec : specs) + { + auto it = status_db.find_installed(spec); + if (it == status_db.end()) + { + not_installed.push_back(spec); + } + + auto maybe_scf = provider.get_control_file(spec.name()); + if (auto p_scf = maybe_scf.get()) + { + if (it != status_db.end()) + { + if (p_scf->core_paragraph->version != (*it)->package.version) + { + to_upgrade.push_back(spec); + } + else + { + up_to_date.push_back(spec); + } + } + } + else + { + no_portfile.push_back(spec); + } + } + + Util::sort(not_installed); + Util::sort(no_portfile); + Util::sort(up_to_date); + Util::sort(to_upgrade); + + if (!up_to_date.empty()) + { + System::println(System::Color::success, "The following packages are up-to-date:"); + System::println(Strings::join( + "", up_to_date, [](const PackageSpec& spec) { return " " + spec.to_string() + "\n"; })); + } + + if (!not_installed.empty()) + { + System::println(System::Color::error, "The following packages are not installed:"); + System::println(Strings::join( + "", not_installed, [](const PackageSpec& spec) { return " " + spec.to_string() + "\n"; })); + } + + if (!no_portfile.empty()) + { + System::println(System::Color::error, "The following packages do not have a valid portfile:"); + System::println(Strings::join( + "", no_portfile, [](const PackageSpec& spec) { return " " + spec.to_string() + "\n"; })); + } + + Checks::check_exit(VCPKG_LINE_INFO, not_installed.empty() && no_portfile.empty()); + + if (to_upgrade.empty()) Checks::exit_success(VCPKG_LINE_INFO); + + for (auto&& spec : to_upgrade) + graph.upgrade(spec); + } + + auto plan = graph.serialize(); + + Checks::check_exit(VCPKG_LINE_INFO, !plan.empty()); + + const Build::BuildPackageOptions install_plan_options = { + Build::UseHeadVersion::NO, + Build::AllowDownloads::YES, + Build::CleanBuildtrees::NO, + }; + + // Set build settings for all install actions + for (auto&& action : plan) + { + if (auto p_install = action.install_action.get()) + { + p_install->build_options = install_plan_options; + } + } + + Dependencies::print_plan(plan, true); + + if (!no_dry_run) + { + System::println(System::Color::warning, + "If you are sure you want to rebuild the above packages, run this command with the " + "--no-dry-run option."); + Checks::exit_fail(VCPKG_LINE_INFO); + } + + const Install::InstallSummary summary = Install::perform(plan, keep_going, paths, status_db); + + System::println("\nTotal elapsed time: %s\n", summary.total_elapsed_time); + + if (keep_going == KeepGoing::YES) + { + summary.print(); + } + + Checks::exit_success(VCPKG_LINE_INFO); + } +} diff --git a/toolsrc/src/vcpkg/commands.version.cpp b/toolsrc/src/vcpkg/commands.version.cpp index 3f44cf1a2..c21e8cafd 100644 --- a/toolsrc/src/vcpkg/commands.version.cpp +++ b/toolsrc/src/vcpkg/commands.version.cpp @@ -12,6 +12,13 @@ namespace vcpkg::Commands::Version { + const char* base_version() + { + return +#include "../VERSION.txt" + ; + } + const std::string& version() { static const std::string S_VERSION = diff --git a/toolsrc/src/vcpkg/dependencies.cpp b/toolsrc/src/vcpkg/dependencies.cpp index 6f599afd4..76aeb0eaa 100644 --- a/toolsrc/src/vcpkg/dependencies.cpp +++ b/toolsrc/src/vcpkg/dependencies.cpp @@ -65,26 +65,19 @@ namespace vcpkg::Dependencies struct ClusterGraph : Util::MoveOnlyBase { - explicit ClusterGraph(std::unordered_map<std::string, const SourceControlFile*>&& ports) - : m_ports(std::move(ports)) - { - } + explicit ClusterGraph(const PortFileProvider& provider) : m_provider(provider) {} Cluster& get(const PackageSpec& spec) { auto it = m_graph.find(spec); if (it == m_graph.end()) { - // Load on-demand from m_ports - auto it_ports = m_ports.find(spec.name()); - if (it_ports != m_ports.end()) - { - auto& clust = m_graph[spec]; - clust.spec = spec; - cluster_from_scf(*it_ports->second, clust); - return clust; - } - return m_graph[spec]; + // Load on-demand from m_provider + auto maybe_scf = m_provider.get_control_file(spec.name()); + auto& clust = m_graph[spec]; + clust.spec = spec; + if (auto p_scf = maybe_scf.get()) cluster_from_scf(*p_scf, clust); + return clust; } return it->second; } @@ -107,26 +100,27 @@ namespace vcpkg::Dependencies } std::unordered_map<PackageSpec, Cluster> m_graph; - std::unordered_map<std::string, const SourceControlFile*> m_ports; + const PortFileProvider& m_provider; }; std::vector<PackageSpec> AnyParagraph::dependencies(const Triplet& triplet) const { if (const auto p = this->status_paragraph.get()) { - return PackageSpec::to_package_specs(p->package.depends, triplet); + return PackageSpec::from_dependencies_of_port(p->package.spec.name(), p->package.depends, triplet); } if (const auto p = this->binary_control_file.get()) { auto deps = Util::fmap_flatten(p->features, [](const BinaryParagraph& pgh) { return pgh.depends; }); deps.insert(deps.end(), p->core_paragraph.depends.cbegin(), p->core_paragraph.depends.cend()); - return PackageSpec::to_package_specs(deps, triplet); + return PackageSpec::from_dependencies_of_port(p->core_paragraph.spec.name(), deps, triplet); } - if (const auto p = this->source_paragraph.get()) + if (const auto p = this->source_control_file.value_or(nullptr)) { - return PackageSpec::to_package_specs(filter_dependencies(p->depends, triplet), triplet); + return PackageSpec::from_dependencies_of_port( + p->core_paragraph->name, filter_dependencies(p->core_paragraph->depends, triplet), triplet); } Checks::exit_with_message(VCPKG_LINE_INFO, @@ -192,11 +186,13 @@ namespace vcpkg::Dependencies return; } - if (auto p = any_paragraph.source_paragraph.get()) + if (auto p = any_paragraph.source_control_file.get()) { this->plan_type = InstallPlanType::BUILD_AND_INSTALL; return; } + + Checks::unreachable(VCPKG_LINE_INFO); } std::string InstallPlanAction::displayname() const @@ -226,12 +222,12 @@ namespace vcpkg::Dependencies const PackageSpec& AnyAction::spec() const { - if (const auto p = install_plan.get()) + if (const auto p = install_action.get()) { return p->spec; } - if (const auto p = remove_plan.get()) + if (const auto p = remove_action.get()) { return p->spec; } @@ -257,7 +253,7 @@ namespace vcpkg::Dependencies return; } - if (auto p = any_paragraph.source_paragraph.get()) + if (auto p = any_paragraph.source_control_file.get()) { this->plan_type = ExportPlanType::PORT_AVAILABLE_BUT_NOT_BUILT; return; @@ -269,21 +265,20 @@ namespace vcpkg::Dependencies return left->spec.name() < right->spec.name(); } - MapPortFile::MapPortFile(const std::unordered_map<std::string, SourceControlFile>& map) : ports(map) {} + MapPortFileProvider::MapPortFileProvider(const std::unordered_map<std::string, SourceControlFile>& map) : ports(map) + { + } - const SourceControlFile& MapPortFile::get_control_file(const std::string& spec) const + Optional<const SourceControlFile&> MapPortFileProvider::get_control_file(const std::string& spec) const { auto scf = ports.find(spec); - if (scf == ports.end()) - { - Checks::exit_fail(VCPKG_LINE_INFO); - } + if (scf == ports.end()) return nullopt; return scf->second; } - PathsPortFile::PathsPortFile(const VcpkgPaths& paths) : ports(paths) {} + PathsPortFileProvider::PathsPortFileProvider(const VcpkgPaths& paths) : ports(paths) {} - const SourceControlFile& PathsPortFile::get_control_file(const std::string& spec) const + Optional<const SourceControlFile&> PathsPortFileProvider::get_control_file(const std::string& spec) const { auto cache_it = cache.find(spec); if (cache_it != cache.end()) @@ -298,56 +293,34 @@ namespace vcpkg::Dependencies auto it = cache.emplace(spec, std::move(*scf->get())); return it.first->second; } - print_error_message(source_control_file.error()); - Checks::exit_fail(VCPKG_LINE_INFO); + return nullopt; } std::vector<InstallPlanAction> create_install_plan(const PortFileProvider& port_file_provider, const std::vector<PackageSpec>& specs, const StatusParagraphs& status_db) { - struct InstallAdjacencyProvider final : Graphs::AdjacencyProvider<PackageSpec, InstallPlanAction> - { - const PortFileProvider& port_file_provider; - const StatusParagraphs& status_db; - const std::unordered_set<PackageSpec>& specs_as_set; + auto fspecs = Util::fmap(specs, [](const PackageSpec& spec) { return FeatureSpec(spec, ""); }); + auto plan = create_feature_install_plan(port_file_provider, fspecs, status_db); - InstallAdjacencyProvider(const PortFileProvider& port_file_provider, - const StatusParagraphs& s, - const std::unordered_set<PackageSpec>& specs_as_set) - : port_file_provider(port_file_provider), status_db(s), specs_as_set(specs_as_set) - { - } + std::vector<InstallPlanAction> ret; + ret.reserve(plan.size()); - std::vector<PackageSpec> adjacency_list(const InstallPlanAction& plan) const override + for (auto&& action : plan) + { + if (auto p_install = action.install_action.get()) { - if (plan.any_paragraph.status_paragraph.get()) return std::vector<PackageSpec>{}; - return plan.any_paragraph.dependencies(plan.spec.triplet()); + ret.push_back(std::move(*p_install)); } - - InstallPlanAction load_vertex_data(const PackageSpec& spec) const override + else { - const RequestType request_type = specs_as_set.find(spec) != specs_as_set.end() - ? RequestType::USER_REQUESTED - : RequestType::AUTO_SELECTED; - auto it = status_db.find_installed(spec); - if (it != status_db.end()) return InstallPlanAction{spec, {*it->get(), nullopt, nullopt}, request_type}; - return InstallPlanAction{ - spec, - {nullopt, nullopt, *port_file_provider.get_control_file(spec.name()).core_paragraph}, - request_type}; + Checks::exit_with_message(VCPKG_LINE_INFO, + "The installation plan requires feature packages support. Please re-run the " + "command with --featurepackages."); } - }; - - const std::unordered_set<PackageSpec> specs_as_set(specs.cbegin(), specs.cend()); - std::vector<InstallPlanAction> toposort = - Graphs::topological_sort(specs, InstallAdjacencyProvider{port_file_provider, status_db, specs_as_set}); - Util::erase_remove_if(toposort, [](const InstallPlanAction& plan) { - return plan.request_type == RequestType::AUTO_SELECTED && - plan.plan_type == InstallPlanType::ALREADY_INSTALLED; - }); + } - return toposort; + return ret; } std::vector<RemovePlanAction> create_remove_plan(const std::vector<PackageSpec>& specs, @@ -400,6 +373,8 @@ namespace vcpkg::Dependencies } return RemovePlanAction{spec, RemovePlanType::REMOVE, request_type}; } + + std::string to_string(const PackageSpec& spec) const override { return spec.to_string(); } }; const std::vector<StatusParagraph*>& installed_ports = get_installed_ports(status_db); @@ -407,7 +382,8 @@ namespace vcpkg::Dependencies return Graphs::topological_sort(specs, RemoveAdjacencyProvider{status_db, installed_ports, specs_as_set}); } - std::vector<ExportPlanAction> create_export_plan(const VcpkgPaths& paths, + std::vector<ExportPlanAction> create_export_plan(const PortFileProvider& port_file_provider, + const VcpkgPaths& paths, const std::vector<PackageSpec>& specs, const StatusParagraphs& status_db) { @@ -415,12 +391,14 @@ namespace vcpkg::Dependencies { const VcpkgPaths& paths; const StatusParagraphs& status_db; + const PortFileProvider& provider; const std::unordered_set<PackageSpec>& specs_as_set; ExportAdjacencyProvider(const VcpkgPaths& p, const StatusParagraphs& s, + const PortFileProvider& prov, const std::unordered_set<PackageSpec>& specs_as_set) - : paths(p), status_db(s), specs_as_set(specs_as_set) + : paths(p), status_db(s), provider(prov), specs_as_set(specs_as_set) { } @@ -439,19 +417,18 @@ namespace vcpkg::Dependencies if (auto bcf = maybe_bpgh.get()) return ExportPlanAction{spec, AnyParagraph{nullopt, std::move(*bcf), nullopt}, request_type}; - auto maybe_scf = Paragraphs::try_load_port(paths.get_filesystem(), paths.port_dir(spec)); - if (auto scf = maybe_scf.get()) - return ExportPlanAction{spec, {nullopt, nullopt, *scf->get()->core_paragraph}, request_type}; - else - print_error_message(maybe_scf.error()); + auto maybe_scf = provider.get_control_file(spec.name()); + if (auto scf = maybe_scf.get()) return ExportPlanAction{spec, {nullopt, nullopt, scf}, request_type}; Checks::exit_with_message(VCPKG_LINE_INFO, "Could not find package %s", spec); } + + std::string to_string(const PackageSpec& spec) const override { return spec.to_string(); } }; const std::unordered_set<PackageSpec> specs_as_set(specs.cbegin(), specs.cend()); - std::vector<ExportPlanAction> toposort = - Graphs::topological_sort(specs, ExportAdjacencyProvider{paths, status_db, specs_as_set}); + std::vector<ExportPlanAction> toposort = Graphs::topological_sort( + specs, ExportAdjacencyProvider{paths, status_db, port_file_provider, specs_as_set}); return toposort; } @@ -461,11 +438,12 @@ namespace vcpkg::Dependencies SUCCESS, }; - MarkPlusResult mark_plus(const std::string& feature, - Cluster& cluster, - ClusterGraph& pkg_to_cluster, - GraphPlan& graph_plan); - void mark_minus(Cluster& cluster, ClusterGraph& pkg_to_cluster, GraphPlan& graph_plan); + static MarkPlusResult mark_plus(const std::string& feature, + Cluster& cluster, + ClusterGraph& pkg_to_cluster, + GraphPlan& graph_plan); + + static void mark_minus(Cluster& cluster, ClusterGraph& pkg_to_cluster, GraphPlan& graph_plan); MarkPlusResult mark_plus(const std::string& feature, Cluster& cluster, ClusterGraph& graph, GraphPlan& graph_plan) { @@ -557,111 +535,78 @@ namespace vcpkg::Dependencies } } - static ClusterGraph create_feature_install_graph(const std::unordered_map<std::string, SourceControlFile>& map, - const StatusParagraphs& status_db) + std::vector<AnyAction> create_feature_install_plan(const PortFileProvider& provider, + const std::vector<FeatureSpec>& specs, + const StatusParagraphs& status_db) { - std::unordered_map<std::string, const SourceControlFile*> ptr_map; - for (auto&& p : map) - ptr_map.emplace(p.first, &p.second); - ClusterGraph graph(std::move(ptr_map)); - - auto installed_ports = get_installed_ports(status_db); - - for (auto&& status_paragraph : installed_ports) - { - Cluster& cluster = graph.get(status_paragraph->package.spec); - - cluster.transient_uninstalled = false; - - cluster.status_paragraphs.emplace_back(status_paragraph); - - auto& status_paragraph_feature = status_paragraph->package.feature; - // In this case, empty string indicates the "core" paragraph for a package. - if (status_paragraph_feature.empty()) - { - cluster.original_features.insert("core"); - } - else - { - cluster.original_features.insert(status_paragraph_feature); - } - } - - for (auto&& status_paragraph : installed_ports) - { - auto& spec = status_paragraph->package.spec; - auto& status_paragraph_feature = status_paragraph->package.feature; - auto reverse_edges = FeatureSpec::from_strings_and_triplet(status_paragraph->package.depends, - status_paragraph->package.spec.triplet()); - - for (auto&& dependency : reverse_edges) - { - auto& dep_cluster = graph.get(dependency.spec()); - - auto depends_name = dependency.feature(); - if (depends_name.empty()) depends_name = "core"; + PackageGraph pgraph(provider, status_db); + for (auto&& spec : specs) + pgraph.install(spec); - auto& target_node = dep_cluster.edges[depends_name]; - target_node.remove_edges.emplace_back(FeatureSpec{spec, status_paragraph_feature}); - } - } - return graph; + return pgraph.serialize(); } std::vector<AnyAction> create_feature_install_plan(const std::unordered_map<std::string, SourceControlFile>& map, const std::vector<FeatureSpec>& specs, const StatusParagraphs& status_db) { - ClusterGraph graph = create_feature_install_graph(map, status_db); + MapPortFileProvider provider(map); + return create_feature_install_plan(provider, specs, status_db); + } - GraphPlan graph_plan; - for (auto&& spec : specs) + void PackageGraph::install(const FeatureSpec& spec) + { + Cluster& spec_cluster = m_graph->get(spec.spec()); + spec_cluster.request_type = RequestType::USER_REQUESTED; + if (spec.feature() == "*") { - Cluster& spec_cluster = graph.get(spec.spec()); - spec_cluster.request_type = RequestType::USER_REQUESTED; - if (spec.feature() == "*") + if (auto p_scf = spec_cluster.source_control_file.value_or(nullptr)) { - if (auto p_scf = spec_cluster.source_control_file.value_or(nullptr)) + for (auto&& feature : p_scf->feature_paragraphs) { - for (auto&& feature : p_scf->feature_paragraphs) - { - auto res = mark_plus(feature->name, spec_cluster, graph, graph_plan); - - Checks::check_exit(VCPKG_LINE_INFO, - res == MarkPlusResult::SUCCESS, - "Error: Unable to locate feature %s", - spec); - } - - auto res = mark_plus("core", spec_cluster, graph, graph_plan); + auto res = mark_plus(feature->name, spec_cluster, *m_graph, *m_graph_plan); Checks::check_exit( VCPKG_LINE_INFO, res == MarkPlusResult::SUCCESS, "Error: Unable to locate feature %s", spec); } - else - { - Checks::exit_with_message( - VCPKG_LINE_INFO, "Error: Unable to handle '*' because can't find CONTROL for %s", spec.spec()); - } - } - else - { - auto res = mark_plus(spec.feature(), spec_cluster, graph, graph_plan); + + auto res = mark_plus("core", spec_cluster, *m_graph, *m_graph_plan); Checks::check_exit( VCPKG_LINE_INFO, res == MarkPlusResult::SUCCESS, "Error: Unable to locate feature %s", spec); } + else + { + Checks::exit_with_message( + VCPKG_LINE_INFO, "Error: Unable to handle '*' because can't find CONTROL for %s", spec.spec()); + } + } + else + { + auto res = mark_plus(spec.feature(), spec_cluster, *m_graph, *m_graph_plan); - graph_plan.install_graph.add_vertex(ClusterPtr{&spec_cluster}); + Checks::check_exit( + VCPKG_LINE_INFO, res == MarkPlusResult::SUCCESS, "Error: Unable to locate feature %s", spec); } - Graphs::GraphAdjacencyProvider<ClusterPtr> adjacency_remove_graph(graph_plan.remove_graph.adjacency_list()); - auto remove_vertex_list = graph_plan.remove_graph.vertex_list(); - auto remove_toposort = Graphs::topological_sort(remove_vertex_list, adjacency_remove_graph); + m_graph_plan->install_graph.add_vertex(ClusterPtr{&spec_cluster}); + } + + void PackageGraph::upgrade(const PackageSpec& spec) + { + Cluster& spec_cluster = m_graph->get(spec); + spec_cluster.request_type = RequestType::USER_REQUESTED; + + mark_minus(spec_cluster, *m_graph, *m_graph_plan); + } + + std::vector<AnyAction> PackageGraph::serialize() const + { + auto remove_vertex_list = m_graph_plan->remove_graph.vertex_list(); + auto remove_toposort = Graphs::topological_sort(remove_vertex_list, m_graph_plan->remove_graph); - Graphs::GraphAdjacencyProvider<ClusterPtr> adjacency_install_graph(graph_plan.install_graph.adjacency_list()); - auto insert_vertex_list = graph_plan.install_graph.vertex_list(); - auto insert_toposort = Graphs::topological_sort(insert_vertex_list, adjacency_install_graph); + auto insert_vertex_list = m_graph_plan->install_graph.vertex_list(); + auto insert_toposort = Graphs::topological_sort(insert_vertex_list, m_graph_plan->install_graph); std::vector<AnyAction> plan; @@ -705,4 +650,162 @@ namespace vcpkg::Dependencies return plan; } + + static std::unique_ptr<ClusterGraph> create_feature_install_graph(const PortFileProvider& map, + const StatusParagraphs& status_db) + { + std::unique_ptr<ClusterGraph> graph = std::make_unique<ClusterGraph>(map); + + auto installed_ports = get_installed_ports(status_db); + + for (auto&& status_paragraph : installed_ports) + { + Cluster& cluster = graph->get(status_paragraph->package.spec); + + cluster.transient_uninstalled = false; + + cluster.status_paragraphs.emplace_back(status_paragraph); + + auto& status_paragraph_feature = status_paragraph->package.feature; + // In this case, empty string indicates the "core" paragraph for a package. + if (status_paragraph_feature.empty()) + { + cluster.original_features.insert("core"); + } + else + { + cluster.original_features.insert(status_paragraph_feature); + } + } + + // Populate the graph with "remove edges", which are the reverse of the Build-Depends edges. + for (auto&& status_paragraph : installed_ports) + { + auto& spec = status_paragraph->package.spec; + auto& status_paragraph_feature = status_paragraph->package.feature; + auto reverse_edges = FeatureSpec::from_strings_and_triplet(status_paragraph->package.depends, + status_paragraph->package.spec.triplet()); + + for (auto&& dependency : reverse_edges) + { + auto& dep_cluster = graph->get(dependency.spec()); + + auto depends_name = dependency.feature(); + if (depends_name.empty()) depends_name = "core"; + + auto& target_node = dep_cluster.edges[depends_name]; + target_node.remove_edges.emplace_back(FeatureSpec{spec, status_paragraph_feature}); + } + } + return graph; + } + + PackageGraph::PackageGraph(const PortFileProvider& provider, const StatusParagraphs& status_db) + : m_graph(create_feature_install_graph(provider, status_db)), m_graph_plan(std::make_unique<GraphPlan>()) + { + } + + PackageGraph::~PackageGraph() {} + + void print_plan(const std::vector<AnyAction>& action_plan, const bool is_recursive) + { + std::vector<const RemovePlanAction*> remove_plans; + std::vector<const InstallPlanAction*> rebuilt_plans; + std::vector<const InstallPlanAction*> only_install_plans; + std::vector<const InstallPlanAction*> new_plans; + std::vector<const InstallPlanAction*> already_installed_plans; + std::vector<const InstallPlanAction*> excluded; + + const bool has_non_user_requested_packages = Util::find_if(action_plan, [](const AnyAction& package) -> bool { + if (auto iplan = package.install_action.get()) + return iplan->request_type != RequestType::USER_REQUESTED; + else + return false; + }) != action_plan.cend(); + + for (auto&& action : action_plan) + { + if (auto install_action = action.install_action.get()) + { + // remove plans are guaranteed to come before install plans, so we know the plan will be contained if at + // all. + auto it = Util::find_if( + remove_plans, [&](const RemovePlanAction* plan) { return plan->spec == install_action->spec; }); + if (it != remove_plans.end()) + { + rebuilt_plans.emplace_back(install_action); + } + else + { + switch (install_action->plan_type) + { + case InstallPlanType::INSTALL: only_install_plans.emplace_back(install_action); break; + case InstallPlanType::ALREADY_INSTALLED: + if (install_action->request_type == RequestType::USER_REQUESTED) + already_installed_plans.emplace_back(install_action); + break; + case InstallPlanType::BUILD_AND_INSTALL: new_plans.emplace_back(install_action); break; + case InstallPlanType::EXCLUDED: excluded.emplace_back(install_action); break; + default: Checks::unreachable(VCPKG_LINE_INFO); + } + } + } + else if (auto remove_action = action.remove_action.get()) + { + remove_plans.emplace_back(remove_action); + } + } + + std::sort(remove_plans.begin(), remove_plans.end(), &RemovePlanAction::compare_by_name); + std::sort(rebuilt_plans.begin(), rebuilt_plans.end(), &InstallPlanAction::compare_by_name); + std::sort(only_install_plans.begin(), only_install_plans.end(), &InstallPlanAction::compare_by_name); + std::sort(new_plans.begin(), new_plans.end(), &InstallPlanAction::compare_by_name); + std::sort(already_installed_plans.begin(), already_installed_plans.end(), &InstallPlanAction::compare_by_name); + std::sort(excluded.begin(), excluded.end(), &InstallPlanAction::compare_by_name); + + static auto actions_to_output_string = [](const std::vector<const InstallPlanAction*>& v) { + return Strings::join("\n", v, [](const InstallPlanAction* p) { + return to_output_string(p->request_type, p->displayname(), p->build_options); + }); + }; + + if (excluded.size() > 0) + { + System::println("The following packages are excluded:\n%s", actions_to_output_string(excluded)); + } + + if (already_installed_plans.size() > 0) + { + System::println("The following packages are already installed:\n%s", + actions_to_output_string(already_installed_plans)); + } + + if (rebuilt_plans.size() > 0) + { + System::println("The following packages will be rebuilt:\n%s", actions_to_output_string(rebuilt_plans)); + } + + if (new_plans.size() > 0) + { + System::println("The following packages will be built and installed:\n%s", + actions_to_output_string(new_plans)); + } + + if (only_install_plans.size() > 0) + { + System::println("The following packages will be directly installed:\n%s", + actions_to_output_string(only_install_plans)); + } + + if (has_non_user_requested_packages) + System::println("Additional packages (*) will be modified to complete this operation."); + + if (remove_plans.size() > 0 && !is_recursive) + { + System::println(System::Color::warning, + "If you are sure you want to rebuild the above packages, run the command with the " + "--recurse option"); + Checks::exit_fail(VCPKG_LINE_INFO); + } + } } diff --git a/toolsrc/src/vcpkg/export.cpp b/toolsrc/src/vcpkg/export.cpp index 9b86863eb..e3221a12f 100644 --- a/toolsrc/src/vcpkg/export.cpp +++ b/toolsrc/src/vcpkg/export.cpp @@ -290,7 +290,7 @@ namespace vcpkg::Export {OPTION_IFW_INSTALLER_FILE_PATH, "Specify the file path for the exported installer"}, }}; - const CommandStructure vcpkg::Export::COMMAND_STRUCTURE = { + const CommandStructure COMMAND_STRUCTURE = { Help::create_example_string("export zlib zlib:x64-windows boost --nuget"), 0, SIZE_MAX, @@ -369,7 +369,8 @@ namespace vcpkg::Export static void print_next_step_info(const fs::path& prefix) { const fs::path cmake_toolchain = prefix / "scripts" / "buildsystems" / "vcpkg.cmake"; - const CMakeVariable cmake_variable = CMakeVariable("CMAKE_TOOLCHAIN_FILE", cmake_toolchain.generic_string()); + const System::CMakeVariable cmake_variable = + System::CMakeVariable("CMAKE_TOOLCHAIN_FILE", cmake_toolchain.generic_string()); System::println("\n" "To use the exported libraries in CMake projects use:" "\n" @@ -477,7 +478,9 @@ With a project open, go to Tools->NuGet Package Manager->Package Manager Console // create the plan const StatusParagraphs status_db = database_load_check(paths); - std::vector<ExportPlanAction> export_plan = Dependencies::create_export_plan(paths, opts.specs, status_db); + Dependencies::PathsPortFileProvider provider(paths); + std::vector<ExportPlanAction> export_plan = + Dependencies::create_export_plan(provider, paths, opts.specs, status_db); Checks::check_exit(VCPKG_LINE_INFO, !export_plan.empty(), "Export plan cannot be empty"); std::map<ExportPlanType, std::vector<const ExportPlanAction*>> group_by_plan_type; diff --git a/toolsrc/src/vcpkg/globalstate.cpp b/toolsrc/src/vcpkg/globalstate.cpp index 43230fa4b..123c77d46 100644 --- a/toolsrc/src/vcpkg/globalstate.cpp +++ b/toolsrc/src/vcpkg/globalstate.cpp @@ -4,7 +4,9 @@ namespace vcpkg { - Util::LockGuarded<Chrono::ElapsedTime> GlobalState::timer; + Util::LockGuarded<Chrono::ElapsedTimer> GlobalState::timer; + Util::LockGuarded<std::string> GlobalState::g_surveydate; + std::atomic<bool> GlobalState::debugging(false); std::atomic<bool> GlobalState::feature_packages(false); diff --git a/toolsrc/src/vcpkg/help.cpp b/toolsrc/src/vcpkg/help.cpp index c83f0277b..b7d355742 100644 --- a/toolsrc/src/vcpkg/help.cpp +++ b/toolsrc/src/vcpkg/help.cpp @@ -85,6 +85,7 @@ namespace vcpkg::Help " vcpkg remove --outdated Uninstall all out-of-date packages\n" " vcpkg list List installed packages\n" " vcpkg update Display list of packages for updating\n" + " vcpkg upgrade Rebuild all outdated packages\n" " vcpkg hash <file> [alg] Hash a file by specific algorithm, default SHA512\n" " vcpkg help topics Display the list of help topics\n" " vcpkg help <topic> Display help for a specific topic\n" diff --git a/toolsrc/src/vcpkg/install.cpp b/toolsrc/src/vcpkg/install.cpp index f48b04d68..dcc130be3 100644 --- a/toolsrc/src/vcpkg/install.cpp +++ b/toolsrc/src/vcpkg/install.cpp @@ -292,25 +292,13 @@ namespace vcpkg::Install System::println("Building package %s... ", display_name_with_features); auto result = [&]() -> Build::ExtendedBuildResult { - if (GlobalState::feature_packages) - { - const Build::BuildPackageConfig build_config{ - *action.any_paragraph.source_control_file.value_or_exit(VCPKG_LINE_INFO), - action.spec.triplet(), - paths.port_dir(action.spec), - action.build_options, - action.feature_list}; - return Build::build_package(paths, build_config, status_db); - } - else - { - const Build::BuildPackageConfig build_config{ - action.any_paragraph.source_paragraph.value_or_exit(VCPKG_LINE_INFO), - action.spec.triplet(), - paths.port_dir(action.spec), - action.build_options}; - return Build::build_package(paths, build_config, status_db); - } + const Build::BuildPackageConfig build_config{ + *action.any_paragraph.source_control_file.value_or_exit(VCPKG_LINE_INFO), + action.spec.triplet(), + paths.port_dir(action.spec), + action.build_options, + action.feature_list}; + return Build::build_package(paths, build_config, status_db); }(); if (result.code != Build::BuildResult::SUCCEEDED) @@ -348,108 +336,6 @@ namespace vcpkg::Install Checks::unreachable(VCPKG_LINE_INFO); } - static void print_plan(const std::vector<AnyAction>& action_plan, const bool is_recursive) - { - std::vector<const RemovePlanAction*> remove_plans; - std::vector<const InstallPlanAction*> rebuilt_plans; - std::vector<const InstallPlanAction*> only_install_plans; - std::vector<const InstallPlanAction*> new_plans; - std::vector<const InstallPlanAction*> already_installed_plans; - std::vector<const InstallPlanAction*> excluded; - - const bool has_non_user_requested_packages = Util::find_if(action_plan, [](const AnyAction& package) -> bool { - if (auto iplan = package.install_plan.get()) - return iplan->request_type != RequestType::USER_REQUESTED; - else - return false; - }) != action_plan.cend(); - - for (auto&& action : action_plan) - { - if (auto install_action = action.install_plan.get()) - { - // remove plans are guaranteed to come before install plans, so we know the plan will be contained if at - // all. - auto it = Util::find_if( - remove_plans, [&](const RemovePlanAction* plan) { return plan->spec == install_action->spec; }); - if (it != remove_plans.end()) - { - rebuilt_plans.emplace_back(install_action); - } - else - { - switch (install_action->plan_type) - { - case InstallPlanType::INSTALL: only_install_plans.emplace_back(install_action); break; - case InstallPlanType::ALREADY_INSTALLED: - if (install_action->request_type == RequestType::USER_REQUESTED) - already_installed_plans.emplace_back(install_action); - break; - case InstallPlanType::BUILD_AND_INSTALL: new_plans.emplace_back(install_action); break; - case InstallPlanType::EXCLUDED: excluded.emplace_back(install_action); break; - default: Checks::unreachable(VCPKG_LINE_INFO); - } - } - } - else if (auto remove_action = action.remove_plan.get()) - { - remove_plans.emplace_back(remove_action); - } - } - - std::sort(remove_plans.begin(), remove_plans.end(), &RemovePlanAction::compare_by_name); - std::sort(rebuilt_plans.begin(), rebuilt_plans.end(), &InstallPlanAction::compare_by_name); - std::sort(only_install_plans.begin(), only_install_plans.end(), &InstallPlanAction::compare_by_name); - std::sort(new_plans.begin(), new_plans.end(), &InstallPlanAction::compare_by_name); - std::sort(already_installed_plans.begin(), already_installed_plans.end(), &InstallPlanAction::compare_by_name); - std::sort(excluded.begin(), excluded.end(), &InstallPlanAction::compare_by_name); - - static auto actions_to_output_string = [](const std::vector<const InstallPlanAction*>& v) { - return Strings::join("\n", v, [](const InstallPlanAction* p) { - return to_output_string(p->request_type, p->displayname(), p->build_options); - }); - }; - - if (excluded.size() > 0) - { - System::println("The following packages are excluded:\n%s", actions_to_output_string(excluded)); - } - - if (already_installed_plans.size() > 0) - { - System::println("The following packages are already installed:\n%s", - actions_to_output_string(already_installed_plans)); - } - - if (rebuilt_plans.size() > 0) - { - System::println("The following packages will be rebuilt:\n%s", actions_to_output_string(rebuilt_plans)); - } - - if (new_plans.size() > 0) - { - System::println("The following packages will be built and installed:\n%s", - actions_to_output_string(new_plans)); - } - - if (only_install_plans.size() > 0) - { - System::println("The following packages will be directly installed:\n%s", - actions_to_output_string(only_install_plans)); - } - - if (has_non_user_requested_packages) - System::println("Additional packages (*) will be installed to complete this operation."); - - if (remove_plans.size() > 0 && !is_recursive) - { - System::println(System::Color::warning, - "If you are sure you want to rebuild the above packages, run the command with the " - "--recurse option"); - Checks::exit_fail(VCPKG_LINE_INFO); - } - } - void InstallSummary::print() const { System::println("RESULTS"); @@ -484,22 +370,22 @@ namespace vcpkg::Install { std::vector<SpecSummary> results; - const auto timer = Chrono::ElapsedTime::create_started(); + const auto timer = Chrono::ElapsedTimer::create_started(); size_t counter = 0; const size_t package_count = action_plan.size(); for (const auto& action : action_plan) { - const auto build_timer = Chrono::ElapsedTime::create_started(); + const auto build_timer = Chrono::ElapsedTimer::create_started(); counter++; const PackageSpec& spec = action.spec(); const std::string display_name = spec.to_string(); - System::println("Starting package %d/%d: %s", counter, package_count, display_name); + System::println("Starting package %zd/%zd: %s", counter, package_count, display_name); results.emplace_back(spec, &action); - if (const auto install_action = action.install_plan.get()) + if (const auto install_action = action.install_action.get()) { auto result = perform_install_plan_action(paths, *install_action, status_db); @@ -511,9 +397,8 @@ namespace vcpkg::Install results.back().build_result = std::move(result); } - else if (const auto remove_action = action.remove_plan.get()) + else if (const auto remove_action = action.remove_action.get()) { - Checks::check_exit(VCPKG_LINE_INFO, GlobalState::feature_packages); Remove::perform_remove_plan_action(paths, *remove_action, Remove::Purge::YES, status_db); } else @@ -521,8 +406,8 @@ namespace vcpkg::Install Checks::unreachable(VCPKG_LINE_INFO); } - results.back().timing = build_timer.to_string(); - System::println("Elapsed time for package %s: %s", display_name, build_timer.to_string()); + results.back().timing = build_timer.elapsed(); + System::println("Elapsed time for package %s: %s", display_name, results.back().timing.to_string()); } return InstallSummary{std::move(results), timer.to_string()}; @@ -533,6 +418,7 @@ namespace vcpkg::Install static const std::string OPTION_NO_DOWNLOADS = "--no-downloads"; static const std::string OPTION_RECURSE = "--recurse"; static const std::string OPTION_KEEP_GOING = "--keep-going"; + static const std::string OPTION_XUNIT = "--x-xunit"; static const std::array<CommandSwitch, 5> INSTALL_SWITCHES = {{ {OPTION_DRY_RUN, "Do not actually build or install"}, @@ -541,7 +427,9 @@ namespace vcpkg::Install {OPTION_RECURSE, "Allow removal of packages as part of installation"}, {OPTION_KEEP_GOING, "Continue installing packages on failure"}, }}; - static const std::array<std::string, 0> INSTALL_SETTINGS; + static const std::array<CommandSetting, 1> INSTALL_SETTINGS = {{ + {OPTION_XUNIT, "File to output results in XUnit format (Internal use)"}, + }}; std::vector<std::string> get_all_port_names(const VcpkgPaths& paths) { @@ -555,13 +443,13 @@ namespace vcpkg::Install Help::create_example_string("install zlib zlib:x64-windows curl boost"), 1, SIZE_MAX, - {INSTALL_SWITCHES, {}}, + {INSTALL_SWITCHES, INSTALL_SETTINGS}, &get_all_port_names, }; static void print_cmake_information(const BinaryParagraph& bpgh, const VcpkgPaths& paths) { - static const std::regex cmake_library_regex("^add_library\\(([^\\s\\$\\)]+)\\s"); + static const std::regex cmake_library_regex(R"(\badd_library\(([^\s\)]+)\s)", std::regex_constants::ECMAScript); auto& fs = paths.get_filesystem(); @@ -579,15 +467,17 @@ namespace vcpkg::Install auto files = fs.read_lines(paths.listfile_path(bpgh)); if (auto p_lines = files.get()) { + std::map<std::string, std::vector<std::string>> library_targets; + for (auto&& suffix : *p_lines) { - if (Strings::case_insensitive_ascii_find(suffix, "/share/") != suffix.end()) + if (Strings::case_insensitive_ascii_find(suffix, "/share/") != suffix.end() && + suffix.substr(suffix.size() - 6) == ".cmake") { - std::vector<std::string> library_targets; - // File is inside the share folder auto path = paths.installed / suffix; auto maybe_contents = fs.read_contents(path); + auto find_package_name = path.parent_path().filename().u8string(); if (auto p_contents = maybe_contents.get()) { std::sregex_iterator next(p_contents->begin(), p_contents->end(), cmake_library_regex); @@ -596,37 +486,40 @@ namespace vcpkg::Install while (next != last) { auto match = *next; - library_targets.push_back(match[1]); + library_targets[find_package_name].push_back(match[1]); ++next; } } + } + } - if (library_targets.empty()) - { - } - else if (library_targets.size() <= 4) + if (library_targets.empty()) + { + } + else + { + System::println("The package %s provides CMake targets:\n", bpgh.spec); + + for (auto&& library_target_pair : library_targets) + { + if (library_target_pair.second.size() <= 4) { - System::println("The package %s provides CMake targets:\n" - "\n" - " find_package(%s REQUIRED)\n" + System::println(" find_package(%s REQUIRED)\n" " target_link_libraries(main PRIVATE %s)\n", - bpgh.spec, - path.parent_path().filename().u8string(), - Strings::join(" ", library_targets)); + library_target_pair.first, + Strings::join(" ", library_target_pair.second)); } else { - auto omitted = library_targets.size() - 4; - library_targets.erase(library_targets.begin() + 4, library_targets.end()); - System::println("The package %s provides CMake targets:\n" - "\n" - " find_package(%s REQUIRED)\n" - " # Note: %d targets were omitted\n" + auto omitted = library_target_pair.second.size() - 4; + library_target_pair.second.erase(library_target_pair.second.begin() + 4, + library_target_pair.second.end()); + System::println(" find_package(%s REQUIRED)\n" + " # Note: %zd targets were omitted\n" " target_link_libraries(main PRIVATE %s)\n", - bpgh.spec, - path.parent_path().filename().u8string(), + library_target_pair.first, omitted, - Strings::join(" ", library_targets)); + Strings::join(" ", library_target_pair.second)); } } } @@ -668,31 +561,33 @@ namespace vcpkg::Install }; // Note: action_plan will hold raw pointers to SourceControlFiles from this map - std::unordered_map<std::string, SourceControlFile> scf_map; std::vector<AnyAction> action_plan; - if (GlobalState::feature_packages) + auto all_ports = Paragraphs::load_all_ports(paths.get_filesystem(), paths.ports); + std::unordered_map<std::string, SourceControlFile> scf_map; + for (auto&& port : all_ports) + scf_map[port->core_paragraph->name] = std::move(*port); + MapPortFileProvider provider(scf_map); + + action_plan = create_feature_install_plan(provider, FullPackageSpec::to_feature_specs(specs), status_db); + + if (!GlobalState::feature_packages) { - auto all_ports = Paragraphs::load_all_ports(paths.get_filesystem(), paths.ports); - for (auto&& port : all_ports) + for (auto&& action : action_plan) { - scf_map[port->core_paragraph->name] = std::move(*port); + if (action.remove_action.has_value()) + { + Checks::exit_with_message( + VCPKG_LINE_INFO, + "The installation plan requires feature packages support. Please re-run the " + "command with --featurepackages."); + } } - action_plan = create_feature_install_plan(scf_map, FullPackageSpec::to_feature_specs(specs), status_db); - } - else - { - Dependencies::PathsPortFile paths_port_file(paths); - auto install_plan = Dependencies::create_install_plan( - paths_port_file, Util::fmap(specs, [](auto&& spec) { return spec.package_spec; }), status_db); - - action_plan = Util::fmap( - install_plan, [](InstallPlanAction& install_action) { return AnyAction(std::move(install_action)); }); } for (auto&& action : action_plan) { - if (auto p_install = action.install_plan.get()) + if (auto p_install = action.install_action.get()) { p_install->build_options = install_plan_options; if (p_install->request_type != RequestType::USER_REQUESTED) @@ -705,16 +600,16 @@ namespace vcpkg::Install // log the plan const std::string specs_string = Strings::join(",", action_plan, [](const AnyAction& action) { - if (auto iaction = action.install_plan.get()) + if (auto iaction = action.install_action.get()) return iaction->spec.to_string(); - else if (auto raction = action.remove_plan.get()) + else if (auto raction = action.remove_action.get()) return "R$" + raction->spec.to_string(); Checks::unreachable(VCPKG_LINE_INFO); }); Metrics::g_metrics.lock()->track_property("installplan", specs_string); - print_plan(action_plan, is_recursive); + Dependencies::print_plan(action_plan, is_recursive); if (dry_run) { @@ -730,12 +625,21 @@ namespace vcpkg::Install summary.print(); } - auto& fs = paths.get_filesystem(); + auto it_xunit = options.settings.find(OPTION_XUNIT); + if (it_xunit != options.settings.end()) + { + std::string xunit_doc = "<assemblies><assembly><collection>\n"; + + xunit_doc += summary.xunit_results(); + + xunit_doc += "</collection></assembly></assemblies>\n"; + paths.get_filesystem().write_contents(fs::u8path(it_xunit->second), xunit_doc); + } for (auto&& result : summary.results) { if (!result.action) continue; - if (auto p_install_action = result.action->install_plan.get()) + if (auto p_install_action = result.action->install_action.get()) { if (p_install_action->request_type != RequestType::USER_REQUESTED) continue; auto bpgh = result.get_binary_paragraph(); @@ -748,7 +652,7 @@ namespace vcpkg::Install } SpecSummary::SpecSummary(const PackageSpec& spec, const Dependencies::AnyAction* action) - : spec(spec), build_result{BuildResult::NULLVALUE, nullptr}, timing("0"), action(action) + : spec(spec), build_result{BuildResult::NULLVALUE, nullptr}, action(action) { } @@ -756,7 +660,7 @@ namespace vcpkg::Install { if (build_result.binary_control_file) return &build_result.binary_control_file->core_paragraph; if (action) - if (auto p_install_plan = action->install_plan.get()) + if (auto p_install_plan = action->install_action.get()) { if (auto p_bcf = p_install_plan->any_paragraph.binary_control_file.get()) return &p_bcf->core_paragraph; @@ -767,4 +671,41 @@ namespace vcpkg::Install } return nullptr; } + + std::string InstallSummary::xunit_results() const + { + std::string xunit_doc; + for (auto&& result : results) + { + std::string inner_block; + const char* result_string = ""; + switch (result.build_result.code) + { + case BuildResult::POST_BUILD_CHECKS_FAILED: + case BuildResult::FILE_CONFLICTS: + case BuildResult::BUILD_FAILED: + result_string = "Fail"; + inner_block = Strings::format("<failure><message><![CDATA[%s]]></message></failure>", + to_string(result.build_result.code)); + break; + case BuildResult::EXCLUDED: + case BuildResult::CASCADED_DUE_TO_MISSING_DEPENDENCIES: + result_string = "Skip"; + inner_block = + Strings::format("<reason><![CDATA[%s]]></reason>", to_string(result.build_result.code)); + break; + case BuildResult::SUCCEEDED: result_string = "Pass"; break; + default: Checks::exit_fail(VCPKG_LINE_INFO); + } + + xunit_doc += Strings::format(R"(<test name="%s" method="%s" time="%lld" result="%s">%s</test>)" + "\n", + result.spec, + result.spec, + result.timing.as<std::chrono::seconds>().count(), + result_string, + inner_block); + } + return xunit_doc; + } } diff --git a/toolsrc/src/vcpkg/metrics.cpp b/toolsrc/src/vcpkg/metrics.cpp index d7e18a176..cdb21d260 100644 --- a/toolsrc/src/vcpkg/metrics.cpp +++ b/toolsrc/src/vcpkg/metrics.cpp @@ -1,7 +1,9 @@ #include "pch.h" +#include <vcpkg/commands.h> #include <vcpkg/metrics.h> +#include <vcpkg/base/chrono.h> #include <vcpkg/base/files.h> #include <vcpkg/base/strings.h> #include <vcpkg/base/system.h> @@ -15,32 +17,13 @@ namespace vcpkg::Metrics static std::string get_current_date_time() { - struct tm newtime; - std::array<char, 80> date; - date.fill(0); - -#if defined(_WIN32) - struct _timeb timebuffer; - - _ftime_s(&timebuffer); - time_t now = timebuffer.time; - const int milli = timebuffer.millitm; - - const errno_t err = gmtime_s(&newtime, &now); - - if (err) + auto maybe_time = Chrono::CTime::get_current_date_time(); + if (auto ptime = maybe_time.get()) { - return ""; + return ptime->to_string(); } -#else - time_t now; - time(&now); - gmtime_r(&now, &newtime); - const int milli = 0; -#endif - strftime(&date[0], date.size(), "%Y-%m-%dT%H:%M:%S", &newtime); - return std::string(&date[0]) + "." + std::to_string(milli) + "Z"; + return ""; } static std::string generate_random_UUID() @@ -255,9 +238,10 @@ namespace vcpkg::Metrics auto match = *next; if (match[0] != "00-00-00-00-00-00") { - std::string matchstr = match[0]; + const std::string matchstr = match[0]; + const System::PowershellParameter value("Value", matchstr); auto hash_result = System::powershell_execute_and_capture_output( - "SHA256Hash", get_vcpkg_root() / "SHA256Hash.ps1", Strings::format("-Value \"%s\"", matchstr)); + "SHA256Hash", get_vcpkg_root() / "scripts" / "SHA256Hash.ps1", {value}); Util::erase_remove_if(hash_result, [](char ch) { return !(ch >= 'A' && ch <= 'F') && !(ch >= '0' && ch <= '9'); }); hash_result = Strings::ascii_to_lowercase(hash_result); @@ -393,8 +377,9 @@ namespace vcpkg::Metrics wchar_t temp_folder[MAX_PATH]; GetTempPathW(MAX_PATH, temp_folder); - const fs::path temp_folder_path = temp_folder; - const fs::path temp_folder_path_exe = temp_folder_path / "vcpkgmetricsuploader.exe"; + const fs::path temp_folder_path = fs::path(temp_folder) / "vcpkg"; + const fs::path temp_folder_path_exe = + temp_folder_path / Strings::format("vcpkgmetricsuploader-%s.exe", Commands::Version::base_version()); auto& fs = Files::get_real_filesystem(); @@ -412,6 +397,8 @@ namespace vcpkg::Metrics }(); std::error_code ec; + fs.create_directories(temp_folder_path, ec); + if (ec) return; fs.copy_file(exe_path, temp_folder_path_exe, fs::copy_options::skip_existing, ec); if (ec) return; } @@ -419,8 +406,9 @@ namespace vcpkg::Metrics const fs::path vcpkg_metrics_txt_path = temp_folder_path / ("vcpkg" + generate_random_UUID() + ".txt"); fs.write_contents(vcpkg_metrics_txt_path, payload); - const std::string cmd_line = - Strings::format("start %s %s", temp_folder_path_exe.u8string(), vcpkg_metrics_txt_path.u8string()); + const std::string cmd_line = Strings::format("start \"vcpkgmetricsuploader.exe\" \"%s\" \"%s\"", + temp_folder_path_exe.u8string(), + vcpkg_metrics_txt_path.u8string()); System::cmd_execute_clean(cmd_line); #endif } diff --git a/toolsrc/src/vcpkg/packagespec.cpp b/toolsrc/src/vcpkg/packagespec.cpp index eeb9981af..a9e072094 100644 --- a/toolsrc/src/vcpkg/packagespec.cpp +++ b/toolsrc/src/vcpkg/packagespec.cpp @@ -2,6 +2,7 @@ #include <vcpkg/base/util.h> #include <vcpkg/packagespec.h> +#include <vcpkg/packagespecparseresult.h> #include <vcpkg/parse.h> using vcpkg::Parse::parse_comma_list; @@ -95,8 +96,40 @@ namespace vcpkg std::vector<PackageSpec> PackageSpec::to_package_specs(const std::vector<std::string>& ports, const Triplet& triplet) { - return Util::fmap(ports, [&](const std::string s) { - return PackageSpec::from_name_and_triplet(s, triplet).value_or_exit(VCPKG_LINE_INFO); + return Util::fmap(ports, [&](const std::string& spec_as_string) -> PackageSpec { + auto maybe_spec = PackageSpec::from_name_and_triplet(spec_as_string, triplet); + if (auto spec = maybe_spec.get()) + { + return std::move(*spec); + } + + const PackageSpecParseResult error_type = maybe_spec.error(); + Checks::exit_with_message(VCPKG_LINE_INFO, + "Invalid package: %s\n" + "%s", + spec_as_string, + vcpkg::to_string(error_type)); + }); + } + + std::vector<PackageSpec> PackageSpec::from_dependencies_of_port(const std::string& port, + const std::vector<std::string>& dependencies, + const Triplet& triplet) + { + return Util::fmap(dependencies, [&](const std::string& spec_as_string) -> PackageSpec { + auto maybe_spec = PackageSpec::from_name_and_triplet(spec_as_string, triplet); + if (auto spec = maybe_spec.get()) + { + return std::move(*spec); + } + + const PackageSpecParseResult error_type = maybe_spec.error(); + Checks::exit_with_message(VCPKG_LINE_INFO, + "Invalid dependency [%s] in package [%s]\n" + "%s", + spec_as_string, + port, + vcpkg::to_string(error_type)); }); } diff --git a/toolsrc/src/vcpkg/paragraphs.cpp b/toolsrc/src/vcpkg/paragraphs.cpp index b93de190c..b66d53994 100644 --- a/toolsrc/src/vcpkg/paragraphs.cpp +++ b/toolsrc/src/vcpkg/paragraphs.cpp @@ -289,16 +289,4 @@ namespace vcpkg::Paragraphs } return std::move(results.paragraphs); } - - std::map<std::string, VersionT> load_all_port_names_and_versions(const Files::Filesystem& fs, - const fs::path& ports_dir) - { - auto all_ports = load_all_ports(fs, ports_dir); - - std::map<std::string, VersionT> names_and_versions; - for (auto&& port : all_ports) - names_and_versions.emplace(port->core_paragraph->name, port->core_paragraph->version); - - return names_and_versions; - } } diff --git a/toolsrc/src/vcpkg/postbuildlint.buildtype.cpp b/toolsrc/src/vcpkg/postbuildlint.buildtype.cpp index 2baaddb5e..e966ce78a 100644 --- a/toolsrc/src/vcpkg/postbuildlint.buildtype.cpp +++ b/toolsrc/src/vcpkg/postbuildlint.buildtype.cpp @@ -3,6 +3,8 @@ #include <vcpkg/base/checks.h> #include <vcpkg/postbuildlint.buildtype.h> +using vcpkg::Build::ConfigurationType; + namespace vcpkg::PostBuildLint { BuildType BuildType::value_of(const ConfigurationType& config, const Build::LinkageType& linkage) diff --git a/toolsrc/src/vcpkg/postbuildlint.cpp b/toolsrc/src/vcpkg/postbuildlint.cpp index d83d656cf..2b427737a 100644 --- a/toolsrc/src/vcpkg/postbuildlint.cpp +++ b/toolsrc/src/vcpkg/postbuildlint.cpp @@ -38,27 +38,41 @@ namespace vcpkg::PostBuildLint } }; - Span<const OutdatedDynamicCrt> get_outdated_dynamic_crts(CStringView toolset) + Span<const OutdatedDynamicCrt> get_outdated_dynamic_crts(const Optional<std::string>& toolset_version) { - static const std::vector<OutdatedDynamicCrt> V_NO_MSVCRT = { + static const std::vector<OutdatedDynamicCrt> V_NO_120 = { {"msvcp100.dll", R"(msvcp100\.dll)"}, {"msvcp100d.dll", R"(msvcp100d\.dll)"}, {"msvcp110.dll", R"(msvcp110\.dll)"}, {"msvcp110_win.dll", R"(msvcp110_win\.dll)"}, - {"msvcp120.dll", R"(msvcp120\.dll)"}, - {"msvcp120_clr0400.dll", R"(msvcp120_clr0400\.dll)"}, {"msvcp60.dll", R"(msvcp60\.dll)"}, {"msvcp60.dll", R"(msvcp60\.dll)"}, + {"msvcrt.dll", R"(msvcrt\.dll)"}, {"msvcr100.dll", R"(msvcr100\.dll)"}, {"msvcr100d.dll", R"(msvcr100d\.dll)"}, {"msvcr100_clr0400.dll", R"(msvcr100_clr0400\.dll)"}, {"msvcr110.dll", R"(msvcr110\.dll)"}, - {"msvcr120.dll", R"(msvcr120\.dll)"}, - {"msvcr120_clr0400.dll", R"(msvcr120_clr0400\.dll)"}, {"msvcrt20.dll", R"(msvcrt20\.dll)"}, - {"msvcrt40.dll", R"(msvcrt40\.dll)"}}; + {"msvcrt40.dll", R"(msvcrt40\.dll)"}, + }; + + static const std::vector<OutdatedDynamicCrt> V_NO_MSVCRT = [&]() { + auto ret = V_NO_120; + ret.push_back({"msvcp120.dll", R"(msvcp120\.dll)"}); + ret.push_back({"msvcp120_clr0400.dll", R"(msvcp120_clr0400\.dll)"}); + ret.push_back({"msvcr120.dll", R"(msvcr120\.dll)"}); + ret.push_back({"msvcr120_clr0400.dll", R"(msvcr120_clr0400\.dll)"}); + return ret; + }(); + + const auto tsv = toolset_version.get(); + if (tsv && (*tsv) == "v120") + { + return V_NO_120; + } + // Default case for all version >= VS 2015. return V_NO_MSVCRT; } @@ -465,7 +479,7 @@ namespace vcpkg::PostBuildLint } System::println(System::Color::warning, - "Mismatching number of debug and release binaries. Found %d for debug but %d for release.", + "Mismatching number of debug and release binaries. Found %zd for debug but %zd for release.", debug_count, release_count); System::println("Debug binaries"); @@ -639,7 +653,8 @@ namespace vcpkg::PostBuildLint static LintStatus check_outdated_crt_linkage_of_dlls(const std::vector<fs::path>& dlls, const fs::path dumpbin_exe, - const BuildInfo& build_info) + const BuildInfo& build_info, + const PreBuildInfo& pre_build_info) { if (build_info.policies.is_enabled(BuildPolicy::ALLOW_OBSOLETE_MSVCRT)) return LintStatus::SUCCESS; @@ -651,7 +666,7 @@ namespace vcpkg::PostBuildLint System::ExitCodeAndOutput ec_data = System::cmd_execute_and_capture_output(cmd_line); Checks::check_exit(VCPKG_LINE_INFO, ec_data.exit_code == 0, "Running command:\n %s\n failed", cmd_line); - for (const OutdatedDynamicCrt& outdated_crt : get_outdated_dynamic_crts("v141")) + for (const OutdatedDynamicCrt& outdated_crt : get_outdated_dynamic_crts(pre_build_info.platform_toolset)) { if (std::regex_search(ec_data.output.cbegin(), ec_data.output.cend(), outdated_crt.regex)) { @@ -711,7 +726,7 @@ namespace vcpkg::PostBuildLint const auto& fs = paths.get_filesystem(); // for dumpbin - const Toolset& toolset = paths.get_toolset(pre_build_info.platform_toolset, pre_build_info.visual_studio_path); + const Toolset& toolset = paths.get_toolset(pre_build_info); const fs::path package_dir = paths.package_dir(spec); size_t error_count = 0; @@ -743,7 +758,8 @@ namespace vcpkg::PostBuildLint std::vector<fs::path> release_libs = fs.get_files_recursive(release_lib_dir); Util::unstable_keep_if(release_libs, has_extension_pred(fs, ".lib")); - error_count += check_matching_debug_and_release_binaries(debug_libs, release_libs); + if (!pre_build_info.build_type) + error_count += check_matching_debug_and_release_binaries(debug_libs, release_libs); { std::vector<fs::path> libs; @@ -762,7 +778,8 @@ namespace vcpkg::PostBuildLint { case Build::LinkageType::DYNAMIC: { - error_count += check_matching_debug_and_release_binaries(debug_dlls, release_dlls); + if (!pre_build_info.build_type) + error_count += check_matching_debug_and_release_binaries(debug_dlls, release_dlls); error_count += check_lib_files_are_available_if_dlls_are_available( build_info.policies, debug_libs.size(), debug_dlls.size(), debug_lib_dir); @@ -777,7 +794,7 @@ namespace vcpkg::PostBuildLint error_count += check_uwp_bit_of_dlls(pre_build_info.cmake_system_name, dlls, toolset.dumpbin); error_count += check_dll_architecture(pre_build_info.target_architecture, dlls); - error_count += check_outdated_crt_linkage_of_dlls(dlls, toolset.dumpbin, build_info); + error_count += check_outdated_crt_linkage_of_dlls(dlls, toolset.dumpbin, build_info, pre_build_info); break; } case Build::LinkageType::STATIC: @@ -790,15 +807,15 @@ namespace vcpkg::PostBuildLint if (!build_info.policies.is_enabled(BuildPolicy::ONLY_RELEASE_CRT)) { - error_count += - check_crt_linkage_of_libs(BuildType::value_of(ConfigurationType::DEBUG, build_info.crt_linkage), - debug_libs, - toolset.dumpbin); + error_count += check_crt_linkage_of_libs( + BuildType::value_of(Build::ConfigurationType::DEBUG, build_info.crt_linkage), + debug_libs, + toolset.dumpbin); } - error_count += - check_crt_linkage_of_libs(BuildType::value_of(ConfigurationType::RELEASE, build_info.crt_linkage), - release_libs, - toolset.dumpbin); + error_count += check_crt_linkage_of_libs( + BuildType::value_of(Build::ConfigurationType::RELEASE, build_info.crt_linkage), + release_libs, + toolset.dumpbin); break; } default: Checks::unreachable(VCPKG_LINE_INFO); diff --git a/toolsrc/src/vcpkg/remove.cpp b/toolsrc/src/vcpkg/remove.cpp index 8ae0bc881..4079d60c1 100644 --- a/toolsrc/src/vcpkg/remove.cpp +++ b/toolsrc/src/vcpkg/remove.cpp @@ -207,10 +207,11 @@ namespace vcpkg::Remove System::println(System::Color::error, "Error: 'remove' accepts either libraries or '--outdated'"); Checks::exit_fail(VCPKG_LINE_INFO); } - specs = Util::fmap( - Update::find_outdated_packages( - Paragraphs::load_all_port_names_and_versions(paths.get_filesystem(), paths.ports), status_db), - [](auto&& outdated) { return outdated.spec; }); + + Dependencies::PathsPortFileProvider provider(paths); + + specs = Util::fmap(Update::find_outdated_packages(provider, status_db), + [](auto&& outdated) { return outdated.spec; }); if (specs.empty()) { diff --git a/toolsrc/src/vcpkg/update.cpp b/toolsrc/src/vcpkg/update.cpp index 29baef91e..d6c5614ed 100644 --- a/toolsrc/src/vcpkg/update.cpp +++ b/toolsrc/src/vcpkg/update.cpp @@ -14,7 +14,7 @@ namespace vcpkg::Update return left.spec.name() < right.spec.name(); } - std::vector<OutdatedPackage> find_outdated_packages(const std::map<std::string, VersionT>& src_names_to_versions, + std::vector<OutdatedPackage> find_outdated_packages(const Dependencies::PortFileProvider& provider, const StatusParagraphs& status_db) { const std::vector<StatusParagraph*> installed_packages = get_installed_ports(status_db); @@ -24,19 +24,23 @@ namespace vcpkg::Update { if (!pgh->package.feature.empty()) { - // Skip feature packages; only consider master packages for needing updates. + // Skip feature paragraphs; only consider master paragraphs for needing updates. continue; } - const auto it = src_names_to_versions.find(pgh->package.spec.name()); - if (it == src_names_to_versions.end()) + auto maybe_scf = provider.get_control_file(pgh->package.spec.name()); + if (auto p_scf = maybe_scf.get()) { - // Package was not installed from portfile - continue; + auto&& port_version = p_scf->core_paragraph->version; + auto&& installed_version = pgh->package.version; + if (installed_version != port_version) + { + output.push_back({pgh->package.spec, VersionDiff(installed_version, port_version)}); + } } - if (it->second != pgh->package.version) + else { - output.push_back({pgh->package.spec, VersionDiff(pgh->package.version, it->second)}); + // No portfile available } } @@ -58,10 +62,10 @@ namespace vcpkg::Update const StatusParagraphs status_db = database_load_check(paths); - const auto outdated_packages = SortedVector<OutdatedPackage>( - find_outdated_packages(Paragraphs::load_all_port_names_and_versions(paths.get_filesystem(), paths.ports), - status_db), - &OutdatedPackage::compare_by_name); + Dependencies::PathsPortFileProvider provider(paths); + + const auto outdated_packages = SortedVector<OutdatedPackage>(find_outdated_packages(provider, status_db), + &OutdatedPackage::compare_by_name); if (outdated_packages.empty()) { @@ -69,19 +73,17 @@ namespace vcpkg::Update } else { - std::string install_line; System::println("The following packages differ from their port versions:"); for (auto&& package : outdated_packages) { - install_line += package.spec.to_string(); - install_line += " "; System::println(" %-32s %s", package.spec, package.version_diff.to_string()); } System::println("\n" - "To update these packages, run\n" - " .\\vcpkg remove --outdated\n" - " .\\vcpkg install " + - install_line); + "To update these packages and all dependencies, run\n" + " .\\vcpkg upgrade\n" + "\n" + "To only remove outdated packages, run\n" + " .\\vcpkg remove --outdated\n"); } Checks::exit_success(VCPKG_LINE_INFO); diff --git a/toolsrc/src/vcpkg/userconfig.cpp b/toolsrc/src/vcpkg/userconfig.cpp new file mode 100644 index 000000000..906594691 --- /dev/null +++ b/toolsrc/src/vcpkg/userconfig.cpp @@ -0,0 +1,88 @@ +#include "pch.h" + +#include <vcpkg/base/files.h> +#include <vcpkg/base/lazy.h> +#include <vcpkg/paragraphs.h> +#include <vcpkg/userconfig.h> + +#if defined(_WIN32) +namespace +{ + static vcpkg::Lazy<fs::path> s_localappdata; + + static const fs::path& get_localappdata() + { + return s_localappdata.get_lazy([]() { + fs::path localappdata; + { + // Config path in AppDataLocal + wchar_t* localappdatapath = nullptr; + if (S_OK != SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, nullptr, &localappdatapath)) __fastfail(1); + localappdata = localappdatapath; + CoTaskMemFree(localappdatapath); + } + return localappdata; + }); + } +} +#endif + +namespace vcpkg +{ + UserConfig UserConfig::try_read_data(const Files::Filesystem& fs) + { + UserConfig ret; +#if defined(_WIN32) + try + { + auto maybe_pghs = Paragraphs::get_paragraphs(fs, get_localappdata() / "vcpkg" / "config"); + if (const auto p_pghs = maybe_pghs.get()) + { + const auto& pghs = *p_pghs; + + std::unordered_map<std::string, std::string> keys; + if (pghs.size() > 0) keys = pghs[0]; + + for (size_t x = 1; x < pghs.size(); ++x) + { + for (auto&& p : pghs[x]) + keys.insert(p); + } + + ret.user_id = keys["User-Id"]; + ret.user_time = keys["User-Since"]; + ret.user_mac = keys["Mac-Hash"]; + ret.last_completed_survey = keys["Survey-Completed"]; + } + } + catch (...) + { + } +#endif + + return ret; + } + + void UserConfig::try_write_data(Files::Filesystem& fs) const + { +#if defined(_WIN32) + try + { + std::error_code ec; + fs.create_directory(get_localappdata() / "vcpkg", ec); + fs.write_contents(get_localappdata() / "vcpkg" / "config", + Strings::format("User-Id: %s\n" + "User-Since: %s\n" + "Mac-Hash: %s\n" + "Survey-Completed: %s\n", + user_id, + user_time, + user_mac, + last_completed_survey)); + } + catch (...) + { + } +#endif + } +} diff --git a/toolsrc/src/vcpkg/vcpkglib.cpp b/toolsrc/src/vcpkg/vcpkglib.cpp index 38b130f6f..5b2cec4d0 100644 --- a/toolsrc/src/vcpkg/vcpkglib.cpp +++ b/toolsrc/src/vcpkg/vcpkglib.cpp @@ -212,29 +212,7 @@ namespace vcpkg return installed_files; } - CMakeVariable::CMakeVariable(const CStringView varname, const char* varvalue) - : s(Strings::format(R"("-D%s=%s")", varname, varvalue)) - { - } - CMakeVariable::CMakeVariable(const CStringView varname, const std::string& varvalue) - : CMakeVariable(varname, varvalue.c_str()) - { - } - CMakeVariable::CMakeVariable(const CStringView varname, const fs::path& path) - : CMakeVariable(varname, path.generic_u8string()) - { - } - - std::string make_cmake_cmd(const fs::path& cmake_exe, - const fs::path& cmake_script, - const std::vector<CMakeVariable>& pass_variables) - { - const std::string cmd_cmake_pass_variables = Strings::join(" ", pass_variables, [](auto&& v) { return v.s; }); - return Strings::format( - R"("%s" %s -P "%s")", cmake_exe.u8string(), cmd_cmake_pass_variables, cmake_script.generic_u8string()); - } - - std::string shorten_text(const std::string& desc, size_t length) + std::string shorten_text(const std::string& desc, const size_t length) { Checks::check_exit(VCPKG_LINE_INFO, length >= 3); auto simple_desc = std::regex_replace(desc, std::regex("\\s+"), " "); diff --git a/toolsrc/src/vcpkg/vcpkgpaths.cpp b/toolsrc/src/vcpkg/vcpkgpaths.cpp index a553f4199..e64a681e2 100644 --- a/toolsrc/src/vcpkg/vcpkgpaths.cpp +++ b/toolsrc/src/vcpkg/vcpkgpaths.cpp @@ -4,6 +4,7 @@ #include <vcpkg/base/files.h> #include <vcpkg/base/system.h> #include <vcpkg/base/util.h> +#include <vcpkg/build.h> #include <vcpkg/metrics.h> #include <vcpkg/packagespec.h> #include <vcpkg/vcpkgpaths.h> @@ -91,9 +92,10 @@ namespace vcpkg tool_name, version_as_string); const fs::path script = scripts_folder / "fetchDependency.ps1"; - const std::string title = "Fetching %s version %s (No sufficient installed version was found)"; - const std::string output = - System::powershell_execute_and_capture_output(title, script, Strings::format("-Dependency %s", tool_name)); + const std::string title = Strings::format( + "Fetching %s version %s (No sufficient installed version was found)", tool_name, version_as_string); + const System::PowershellParameter dependency_param("Dependency", tool_name); + const std::string output = System::powershell_execute_and_capture_output(title, script, {dependency_param}); const std::vector<std::string> dependency_path = keep_data_lines(output); Checks::check_exit( @@ -112,14 +114,20 @@ namespace vcpkg static fs::path get_cmake_path(const fs::path& downloads_folder, const fs::path& scripts_folder) { - static constexpr std::array<int, 3> EXPECTED_VERSION = {3, 9, 5}; +#if defined(_WIN32) + static constexpr std::array<int, 3> EXPECTED_VERSION = {3, 10, 0}; +#else + static constexpr std::array<int, 3> EXPECTED_VERSION = {3, 5, 1}; +#endif static const std::string VERSION_CHECK_ARGUMENTS = "--version"; - const fs::path downloaded_copy = downloads_folder / "cmake-3.9.5-win32-x86" / "bin" / "cmake.exe"; const std::vector<fs::path> from_path = Files::find_from_PATH("cmake"); std::vector<fs::path> candidate_paths; + const fs::path downloaded_copy = downloads_folder / "cmake-3.10.0-win32-x86" / "bin" / "cmake.exe"; +#if defined(_WIN32) candidate_paths.push_back(downloaded_copy); +#endif candidate_paths.insert(candidate_paths.end(), from_path.cbegin(), from_path.cend()); #if defined(_WIN32) candidate_paths.push_back(System::get_program_files_platform_bitness() / "CMake" / "bin" / "cmake.exe"); @@ -158,14 +166,20 @@ namespace vcpkg fs::path get_git_path(const fs::path& downloads_folder, const fs::path& scripts_folder) { +#if defined(_WIN32) static constexpr std::array<int, 3> EXPECTED_VERSION = {2, 15, 0}; +#else + static constexpr std::array<int, 3> EXPECTED_VERSION = {2, 7, 4}; +#endif static const std::string VERSION_CHECK_ARGUMENTS = "--version"; - const fs::path downloaded_copy = downloads_folder / "MinGit-2.15.0-32-bit" / "cmd" / "git.exe"; const std::vector<fs::path> from_path = Files::find_from_PATH("git"); + const fs::path downloaded_copy = downloads_folder / "MinGit-2.15.0-32-bit" / "cmd" / "git.exe"; std::vector<fs::path> candidate_paths; +#if defined(_WIN32) candidate_paths.push_back(downloaded_copy); +#endif candidate_paths.insert(candidate_paths.end(), from_path.cbegin(), from_path.cend()); #if defined(_WIN32) candidate_paths.push_back(System::get_program_files_platform_bitness() / "git" / "cmd" / "git.exe"); @@ -267,7 +281,6 @@ namespace vcpkg const std::vector<std::string>& VcpkgPaths::get_available_triplets() const { return this->available_triplets.get_lazy([this]() -> std::vector<std::string> { - std::vector<std::string> output; for (auto&& path : this->get_filesystem().get_files_non_recursive(this->triplets)) { @@ -371,6 +384,7 @@ namespace vcpkg std::vector<fs::path> paths_examined; std::vector<Toolset> found_toolsets; + std::vector<Toolset> excluded_toolsets; const std::vector<VisualStudioInstance> vs_instances = get_visual_studio_instances(paths); const bool v140_is_available = Util::find_if(vs_instances, [&](const VisualStudioInstance& vs_instance) { @@ -427,17 +441,28 @@ namespace vcpkg paths_examined.push_back(dumpbin_path); if (fs.exists(dumpbin_path)) { - found_toolsets.push_back(Toolset{ - vs_instance.root_path, dumpbin_path, vcvarsall_bat, {}, V_141, supported_architectures}); + const Toolset v141toolset = Toolset{ + vs_instance.root_path, dumpbin_path, vcvarsall_bat, {}, V_141, supported_architectures}; + + auto english_language_pack = dumpbin_path.parent_path() / "1033"; + + if (!fs.exists(english_language_pack)) + { + excluded_toolsets.push_back(v141toolset); + break; + } + + found_toolsets.push_back(v141toolset); if (v140_is_available) { - found_toolsets.push_back(Toolset{vs_instance.root_path, - dumpbin_path, - vcvarsall_bat, - {"-vcvars_ver=14.0"}, - V_140, - supported_architectures}); + const Toolset v140toolset = Toolset{vs_instance.root_path, + dumpbin_path, + vcvarsall_bat, + {"-vcvars_ver=14.0"}, + V_140, + supported_architectures}; + found_toolsets.push_back(v140toolset); } break; @@ -447,44 +472,66 @@ namespace vcpkg continue; } - if (major_version == "14") + if (major_version == "14" || major_version == "12") { const fs::path vcvarsall_bat = vs_instance.root_path / "VC" / "vcvarsall.bat"; paths_examined.push_back(vcvarsall_bat); if (fs.exists(vcvarsall_bat)) { - const fs::path vs2015_dumpbin_exe = vs_instance.root_path / "VC" / "bin" / "dumpbin.exe"; - paths_examined.push_back(vs2015_dumpbin_exe); + const fs::path vs_dumpbin_exe = vs_instance.root_path / "VC" / "bin" / "dumpbin.exe"; + paths_examined.push_back(vs_dumpbin_exe); - const fs::path vs2015_bin_dir = vcvarsall_bat.parent_path() / "bin"; + const fs::path vs_bin_dir = vcvarsall_bat.parent_path() / "bin"; std::vector<ToolsetArchOption> supported_architectures; - if (fs.exists(vs2015_bin_dir / "vcvars32.bat")) + if (fs.exists(vs_bin_dir / "vcvars32.bat")) supported_architectures.push_back({"x86", CPU::X86, CPU::X86}); - if (fs.exists(vs2015_bin_dir / "amd64\\vcvars64.bat")) + if (fs.exists(vs_bin_dir / "amd64\\vcvars64.bat")) supported_architectures.push_back({"x64", CPU::X64, CPU::X64}); - if (fs.exists(vs2015_bin_dir / "x86_amd64\\vcvarsx86_amd64.bat")) + if (fs.exists(vs_bin_dir / "x86_amd64\\vcvarsx86_amd64.bat")) supported_architectures.push_back({"x86_amd64", CPU::X86, CPU::X64}); - if (fs.exists(vs2015_bin_dir / "x86_arm\\vcvarsx86_arm.bat")) + if (fs.exists(vs_bin_dir / "x86_arm\\vcvarsx86_arm.bat")) supported_architectures.push_back({"x86_arm", CPU::X86, CPU::ARM}); - if (fs.exists(vs2015_bin_dir / "amd64_x86\\vcvarsamd64_x86.bat")) + if (fs.exists(vs_bin_dir / "amd64_x86\\vcvarsamd64_x86.bat")) supported_architectures.push_back({"amd64_x86", CPU::X64, CPU::X86}); - if (fs.exists(vs2015_bin_dir / "amd64_arm\\vcvarsamd64_arm.bat")) + if (fs.exists(vs_bin_dir / "amd64_arm\\vcvarsamd64_arm.bat")) supported_architectures.push_back({"amd64_arm", CPU::X64, CPU::ARM}); - if (fs.exists(vs2015_dumpbin_exe)) + if (fs.exists(vs_dumpbin_exe)) { - found_toolsets.push_back({vs_instance.root_path, - vs2015_dumpbin_exe, - vcvarsall_bat, - {}, - V_140, - supported_architectures}); + const Toolset toolset = {vs_instance.root_path, + vs_dumpbin_exe, + vcvarsall_bat, + {}, + major_version == "14" ? V_140 : V_120, + supported_architectures}; + + auto english_language_pack = vs_dumpbin_exe.parent_path() / "1033"; + + if (!fs.exists(english_language_pack)) + { + excluded_toolsets.push_back(toolset); + break; + } + + found_toolsets.push_back(toolset); } } } } + if (!excluded_toolsets.empty()) + { + System::println( + System::Color::warning, + "Warning: The following VS instances are exluded because the English language pack is unavailable."); + for (const Toolset& toolset : excluded_toolsets) + { + System::println(" %s", toolset.visual_studio_root_path.u8string()); + } + System::println(System::Color::warning, "Please install the English language pack."); + } + if (found_toolsets.empty()) { System::println(System::Color::error, "Could not locate a complete toolset."); @@ -499,16 +546,36 @@ namespace vcpkg return found_toolsets; } - const Toolset& VcpkgPaths::get_toolset(const Optional<std::string>& toolset_version, - const Optional<fs::path>& visual_studio_path) const + const Toolset& VcpkgPaths::get_toolset(const Build::PreBuildInfo& prebuildinfo) const { + if (prebuildinfo.external_toolchain_file) + { + static Toolset external_toolset = []() -> Toolset { + Toolset ret; + ret.dumpbin = ""; + ret.supported_architectures = { + ToolsetArchOption{"", System::get_host_processor(), System::get_host_processor()}}; +#if defined(_WIN32) + ret.vcvarsall = "cmd"; + ret.vcvarsall_options = {"/c", "echo done"}; +#else + ret.vcvarsall = "true"; + ret.vcvarsall_options = {}; +#endif + ret.version = "external"; + ret.visual_studio_root_path = ""; + return ret; + }(); + return external_toolset; + } + // Invariant: toolsets are non-empty and sorted with newest at back() const std::vector<Toolset>& vs_toolsets = this->toolsets.get_lazy([this]() { return find_toolset_instances(*this); }); std::vector<const Toolset*> candidates = Util::element_pointers(vs_toolsets); - const auto tsv = toolset_version.get(); - const auto vsp = visual_studio_path.get(); + const auto tsv = prebuildinfo.platform_toolset.get(); + const auto vsp = prebuildinfo.visual_studio_path.get(); if (tsv && vsp) { @@ -542,6 +609,7 @@ namespace vcpkg vs_root_path.generic_string()); } + Checks::check_exit(VCPKG_LINE_INFO, !candidates.empty(), "No suitable Visual Studio instances were found"); return *candidates.front(); } |
