diff options
| author | xoviat <xoviat@users.noreply.github.com> | 2017-12-20 13:24:59 -0600 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2017-12-20 13:24:59 -0600 |
| commit | f1f373b189453f33a944e9db8b3451b1c785e223 (patch) | |
| tree | 8713b7dc28734e4d4ad0ebba76459850955ce004 /toolsrc | |
| parent | 6363688b6d2f3522a4ce48cedb60da31775cd923 (diff) | |
| parent | 6cb6a61aaf5ef2c143f974e9f731778bcd3f5cbe (diff) | |
| download | vcpkg-f1f373b189453f33a944e9db8b3451b1c785e223.tar.gz vcpkg-f1f373b189453f33a944e9db8b3451b1c785e223.zip | |
Merge branch 'master' into fftw
Diffstat (limited to 'toolsrc')
51 files changed, 1412 insertions, 677 deletions
diff --git a/toolsrc/CMakeLists.txt b/toolsrc/CMakeLists.txt index af281f12f..0168e2de2 100644 --- a/toolsrc/CMakeLists.txt +++ b/toolsrc/CMakeLists.txt @@ -3,12 +3,25 @@ project(vcpkg CXX) add_compile_options(-std=c++1z)
+if(CMAKE_COMPILER_IS_GNUXX OR CMAKE_CXX_COMPILER_ID MATCHES "GNU")
+ set(GCC 1)
+elseif(CMAKE_CXX_COMPILER_ID MATCHES "[Cc]lang")
+ set(CLANG 1)
+else()
+ message(FATAL_ERROR "Unknown compiler: ${CMAKE_CXX_COMPILER_ID}")
+endif()
+
file(GLOB_RECURSE VCPKGLIB_SOURCES src/vcpkg/*.cpp)
add_library(vcpkglib STATIC ${VCPKGLIB_SOURCES})
target_compile_definitions(vcpkglib PRIVATE -DDISABLE_METRICS=0)
target_include_directories(vcpkglib PUBLIC include)
-target_link_libraries(vcpkglib PRIVATE stdc++fs)
+
+if(GCC)
+ target_link_libraries(vcpkglib PUBLIC stdc++fs)
+elseif(CLANG)
+ target_link_libraries(vcpkglib PUBLIC c++experimental)
+endif()
add_executable(vcpkg src/vcpkg.cpp)
target_link_libraries(vcpkg PRIVATE vcpkglib)
diff --git a/toolsrc/VERSION.txt b/toolsrc/VERSION.txt index 2f0f54a23..e45ea373f 100644 --- a/toolsrc/VERSION.txt +++ b/toolsrc/VERSION.txt @@ -1 +1 @@ -"0.0.99"
\ No newline at end of file +"0.0.100"
\ No newline at end of file diff --git a/toolsrc/include/pch.h b/toolsrc/include/pch.h index 5c31fbbd1..683bef171 100644 --- a/toolsrc/include/pch.h +++ b/toolsrc/include/pch.h @@ -41,6 +41,7 @@ #include <map> #include <memory> #include <mutex> +#include <random> #include <regex> #include <set> #include <stdexcept> diff --git a/toolsrc/include/tests.pch.h b/toolsrc/include/tests.pch.h index 0037af585..5c00fca4a 100644 --- a/toolsrc/include/tests.pch.h +++ b/toolsrc/include/tests.pch.h @@ -2,6 +2,7 @@ #include <CppUnitTest.h> +#include <vcpkg/base/chrono.h> #include <vcpkg/base/sortedvector.h> #include <vcpkg/base/strings.h> #include <vcpkg/base/util.h> diff --git a/toolsrc/include/tests.utils.h b/toolsrc/include/tests.utils.h index 485b8c294..0c0add7ab 100644 --- a/toolsrc/include/tests.utils.h +++ b/toolsrc/include/tests.utils.h @@ -23,4 +23,18 @@ std::unique_ptr<vcpkg::StatusParagraph> make_status_pgh(const char* name, std::unique_ptr<vcpkg::StatusParagraph> make_status_feature_pgh(const char* name, const char* feature, const char* depends = "", - const char* triplet = "x86-windows");
\ No newline at end of file + const char* triplet = "x86-windows"); + +template<class T, class S> +T&& unwrap(vcpkg::ExpectedT<T, S>&& p) +{ + Assert::IsTrue(p.has_value()); + return std::move(*p.get()); +} + +template<class T> +T&& unwrap(vcpkg::Optional<T>&& opt) +{ + Assert::IsTrue(opt.has_value()); + return std::move(*opt.get()); +} diff --git a/toolsrc/include/vcpkg/base/chrono.h b/toolsrc/include/vcpkg/base/chrono.h index b525852f2..4291115f7 100644 --- a/toolsrc/include/vcpkg/base/chrono.h +++ b/toolsrc/include/vcpkg/base/chrono.h @@ -2,27 +2,65 @@ #include <chrono> #include <string> +#include <time.h> +#include <vcpkg/base/optional.h> namespace vcpkg::Chrono { class ElapsedTime { - public: - static ElapsedTime create_started(); + using duration = std::chrono::high_resolution_clock::time_point::duration; - constexpr ElapsedTime() : m_start_tick() {} + public: + constexpr ElapsedTime() : m_duration() {} + constexpr ElapsedTime(duration d) : m_duration(d) {} template<class TimeUnit> - TimeUnit elapsed() const + TimeUnit as() const + { + return std::chrono::duration_cast<TimeUnit>(m_duration); + } + + std::string to_string() const; + + private: + std::chrono::high_resolution_clock::time_point::duration m_duration; + }; + + class ElapsedTimer + { + public: + static ElapsedTimer create_started(); + + constexpr ElapsedTimer() : m_start_tick() {} + + ElapsedTime elapsed() const { - return std::chrono::duration_cast<TimeUnit>(std::chrono::high_resolution_clock::now() - this->m_start_tick); + return ElapsedTime(std::chrono::high_resolution_clock::now() - this->m_start_tick); } - double microseconds() const { return elapsed<std::chrono::duration<double, std::micro>>().count(); } + double microseconds() const { return elapsed().as<std::chrono::duration<double, std::micro>>().count(); } std::string to_string() const; private: std::chrono::high_resolution_clock::time_point m_start_tick; }; + + class CTime + { + public: + static Optional<CTime> get_current_date_time(); + static Optional<CTime> parse(CStringView str); + + constexpr CTime() : m_tm{0} {} + explicit constexpr CTime(tm t) : m_tm{t} {} + + std::string to_string() const; + + std::chrono::system_clock::time_point to_time_point() const; + + private: + mutable tm m_tm; + }; } diff --git a/toolsrc/include/vcpkg/base/graphs.h b/toolsrc/include/vcpkg/base/graphs.h index b585d2bb9..bd22bbcb0 100644 --- a/toolsrc/include/vcpkg/base/graphs.h +++ b/toolsrc/include/vcpkg/base/graphs.h @@ -4,6 +4,7 @@ #include <unordered_set> #include <vcpkg/base/checks.h> +#include <vcpkg/base/span.h> namespace vcpkg::Graphs { @@ -23,139 +24,97 @@ namespace vcpkg::Graphs struct AdjacencyProvider { virtual std::vector<V> adjacency_list(const U& vertex) const = 0; - + virtual std::string to_string(const V& vertex) const = 0; virtual U load_vertex_data(const V& vertex) const = 0; }; - template<class V, class U> - static void topological_sort_internal(const V& vertex, - const AdjacencyProvider<V, U>& f, - std::unordered_map<V, ExplorationStatus>& exploration_status, - std::vector<U>& sorted) + namespace details { - ExplorationStatus& status = exploration_status[vertex]; - switch (status) + template<class V, class U> + void topological_sort_internal(const V& vertex, + const AdjacencyProvider<V, U>& f, + std::unordered_map<V, ExplorationStatus>& exploration_status, + std::vector<U>& sorted) { - case ExplorationStatus::FULLY_EXPLORED: return; - case ExplorationStatus::PARTIALLY_EXPLORED: Checks::exit_with_message(VCPKG_LINE_INFO, "cycle in graph"); - case ExplorationStatus::NOT_EXPLORED: + ExplorationStatus& status = exploration_status[vertex]; + switch (status) { - status = ExplorationStatus::PARTIALLY_EXPLORED; - U vertex_data = f.load_vertex_data(vertex); - for (const V& neighbour : f.adjacency_list(vertex_data)) - topological_sort_internal(neighbour, f, exploration_status, sorted); - - sorted.push_back(std::move(vertex_data)); - status = ExplorationStatus::FULLY_EXPLORED; - return; + case ExplorationStatus::FULLY_EXPLORED: return; + case ExplorationStatus::PARTIALLY_EXPLORED: + { + System::println("Cycle detected within graph:"); + for (auto&& node : exploration_status) + { + if (node.second == ExplorationStatus::PARTIALLY_EXPLORED) + { + System::println(" %s", f.to_string(node.first)); + } + } + Checks::exit_fail(VCPKG_LINE_INFO); + } + case ExplorationStatus::NOT_EXPLORED: + { + status = ExplorationStatus::PARTIALLY_EXPLORED; + U vertex_data = f.load_vertex_data(vertex); + for (const V& neighbour : f.adjacency_list(vertex_data)) + topological_sort_internal(neighbour, f, exploration_status, sorted); + + sorted.push_back(std::move(vertex_data)); + status = ExplorationStatus::FULLY_EXPLORED; + return; + } + default: Checks::unreachable(VCPKG_LINE_INFO); } - default: Checks::unreachable(VCPKG_LINE_INFO); } } - template<class V, class U> - std::vector<U> topological_sort(const std::vector<V>& starting_vertices, const AdjacencyProvider<V, U>& f) + template<class VertexRange, class V, class U> + std::vector<U> topological_sort(const VertexRange& starting_vertices, const AdjacencyProvider<V, U>& f) { std::vector<U> sorted; std::unordered_map<V, ExplorationStatus> exploration_status; - for (auto& vertex : starting_vertices) + for (auto&& vertex : starting_vertices) { - topological_sort_internal(vertex, f, exploration_status, sorted); + details::topological_sort_internal(vertex, f, exploration_status, sorted); } return sorted; } template<class V> - struct GraphAdjacencyProvider final : AdjacencyProvider<V, V> - { - const std::unordered_map<V, std::unordered_set<V>>& vertices; - - GraphAdjacencyProvider(const std::unordered_map<V, std::unordered_set<V>>& vertices) : vertices(vertices) {} - - std::vector<V> adjacency_list(const V& vertex) const override - { - const std::unordered_set<V>& as_set = this->vertices.at(vertex); - return std::vector<V>(as_set.cbegin(), as_set.cend()); // TODO: Avoid redundant copy - } - - V load_vertex_data(const V& vertex) const override { return vertex; } - }; - - template<class V> - struct Graph + struct Graph final : AdjacencyProvider<V, V> { public: - void add_vertex(V v) { this->vertices[v]; } + void add_vertex(const V& v) { this->m_edges[v]; } - // TODO: Change with iterators - void add_vertices(const std::vector<V>& vs) + void add_edge(const V& u, const V& v) { - for (const V& v : vs) - { - this->vertices[v]; - } + this->m_edges[v]; + this->m_edges[u].insert(v); } - void add_edge(V u, V v) + std::vector<V> vertex_list() const { - this->vertices[v]; - this->vertices[u].insert(v); + std::vector<V> vertex_list; + for (auto&& vertex : this->m_edges) + vertex_list.emplace_back(vertex.first); + return vertex_list; } - std::vector<V> topological_sort() const + std::vector<V> adjacency_list(const V& vertex) const override { - GraphAdjacencyProvider<V> adjacency_provider{this->vertices}; - std::unordered_map<V, int> indegrees = count_indegrees(); - - std::vector<V> sorted; - sorted.reserve(indegrees.size()); - - std::unordered_map<V, ExplorationStatus> exploration_status; - exploration_status.reserve(indegrees.size()); - - for (auto& pair : indegrees) - { - if (pair.second == 0) // Starting from vertices with indegree == 0. Not required. - { - V vertex = pair.first; - topological_sort_internal(vertex, adjacency_provider, exploration_status, sorted); - } - } - - return sorted; + const std::unordered_set<V>& as_set = this->m_edges.at(vertex); + return std::vector<V>(as_set.cbegin(), as_set.cend()); // TODO: Avoid redundant copy } - std::unordered_map<V, int> count_indegrees() const - { - std::unordered_map<V, int> indegrees; - - for (auto& pair : this->vertices) - { - indegrees[pair.first]; - for (V neighbour : pair.second) - { - ++indegrees[neighbour]; - } - } - - return indegrees; - } + V load_vertex_data(const V& vertex) const override { return vertex; } - const std::unordered_map<V, std::unordered_set<V>>& adjacency_list() const { return this->vertices; } - std::vector<V> vertex_list() const - { - // why no &? it returns 0 - std::vector<V> vertex_list; - for (const auto& vertex : this->vertices) - { - vertex_list.emplace_back(vertex.first); - } - return vertex_list; - } + // Note: this function indicates how tied this template is to the exact type it will be templated upon. + // Possible fix: This type shouldn't implement to_string() and should instead be derived from? + std::string to_string(const V& spec) const override { return spec->spec.to_string(); } private: - std::unordered_map<V, std::unordered_set<V>> vertices; + std::unordered_map<V, std::unordered_set<V>> m_edges; }; } diff --git a/toolsrc/include/vcpkg/base/optional.h b/toolsrc/include/vcpkg/base/optional.h index aa9e480fd..af2d297a6 100644 --- a/toolsrc/include/vcpkg/base/optional.h +++ b/toolsrc/include/vcpkg/base/optional.h @@ -53,10 +53,8 @@ namespace vcpkg // Constructors are intentionally implicit constexpr Optional(NullOpt) {} - Optional(const T& t) : m_base(t) {} - - template<class = std::enable_if_t<!std::is_reference<T>::value>> - Optional(T&& t) : m_base(std::move(t)) + template<class U> + Optional(U&& t) : m_base(std::forward<U>(t)) { } diff --git a/toolsrc/include/vcpkg/build.h b/toolsrc/include/vcpkg/build.h index 18408e207..e3f8bf79e 100644 --- a/toolsrc/include/vcpkg/build.h +++ b/toolsrc/include/vcpkg/build.h @@ -114,39 +114,24 @@ namespace vcpkg::Build struct BuildPackageConfig { - BuildPackageConfig(const SourceParagraph& src, - const Triplet& triplet, - fs::path&& port_dir, - const BuildPackageOptions& build_package_options) - : src(src) - , scf(nullptr) - , triplet(triplet) - , port_dir(std::move(port_dir)) - , build_package_options(build_package_options) - , feature_list(nullptr) - { - } - BuildPackageConfig(const SourceControlFile& src, const Triplet& triplet, fs::path&& port_dir, const BuildPackageOptions& build_package_options, const std::unordered_set<std::string>& feature_list) - : src(*src.core_paragraph) - , scf(&src) + : scf(src) , triplet(triplet) , port_dir(std::move(port_dir)) , build_package_options(build_package_options) - , feature_list(&feature_list) + , feature_list(feature_list) { } - const SourceParagraph& src; - const SourceControlFile* scf; + const SourceControlFile& scf; const Triplet& triplet; fs::path port_dir; const BuildPackageOptions& build_package_options; - const std::unordered_set<std::string>* feature_list; + const std::unordered_set<std::string>& feature_list; }; ExtendedBuildResult build_package(const VcpkgPaths& paths, diff --git a/toolsrc/include/vcpkg/commands.h b/toolsrc/include/vcpkg/commands.h index 74fd80c03..c82f504e0 100644 --- a/toolsrc/include/vcpkg/commands.h +++ b/toolsrc/include/vcpkg/commands.h @@ -39,6 +39,12 @@ namespace vcpkg::Commands void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths); } + namespace Upgrade + { + extern const CommandStructure COMMAND_STRUCTURE; + void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, const Triplet& default_triplet); + } + namespace Edit { extern const CommandStructure COMMAND_STRUCTURE; @@ -98,6 +104,7 @@ namespace vcpkg::Commands namespace Version { + const char* base_version(); const std::string& version(); void warn_if_vcpkg_version_mismatch(const VcpkgPaths& paths); void perform_and_exit(const VcpkgCmdArguments& args); diff --git a/toolsrc/include/vcpkg/dependencies.h b/toolsrc/include/vcpkg/dependencies.h index 5411ee166..8902a7b08 100644 --- a/toolsrc/include/vcpkg/dependencies.h +++ b/toolsrc/include/vcpkg/dependencies.h @@ -1,6 +1,5 @@ #pragma once -#include <vcpkg/base/graphs.h> #include <vcpkg/base/optional.h> #include <vcpkg/base/util.h> #include <vcpkg/build.h> @@ -30,7 +29,6 @@ namespace vcpkg::Dependencies Optional<StatusParagraph> status_paragraph; Optional<BinaryControlFile> binary_control_file; - Optional<SourceParagraph> source_paragraph; Optional<const SourceControlFile*> source_control_file; }; } @@ -92,11 +90,11 @@ namespace vcpkg::Dependencies struct AnyAction { - AnyAction(InstallPlanAction&& iplan) : install_plan(std::move(iplan)) {} - AnyAction(RemovePlanAction&& rplan) : remove_plan(std::move(rplan)) {} + AnyAction(InstallPlanAction&& iplan) : install_action(std::move(iplan)) {} + AnyAction(RemovePlanAction&& rplan) : remove_action(std::move(rplan)) {} - Optional<InstallPlanAction> install_plan; - Optional<RemovePlanAction> remove_plan; + Optional<InstallPlanAction> install_action; + Optional<RemovePlanAction> remove_action; const PackageSpec& spec() const; }; @@ -123,22 +121,44 @@ namespace vcpkg::Dependencies struct PortFileProvider { - virtual const SourceControlFile& get_control_file(const std::string& spec) const = 0; + virtual Optional<const SourceControlFile&> get_control_file(const std::string& src_name) const = 0; }; - struct MapPortFile : Util::ResourceBase, PortFileProvider + struct MapPortFileProvider : Util::ResourceBase, PortFileProvider { + explicit MapPortFileProvider(const std::unordered_map<std::string, SourceControlFile>& map); + Optional<const SourceControlFile&> get_control_file(const std::string& src_name) const override; + + private: const std::unordered_map<std::string, SourceControlFile>& ports; - explicit MapPortFile(const std::unordered_map<std::string, SourceControlFile>& map); - const SourceControlFile& get_control_file(const std::string& spec) const override; }; - struct PathsPortFile : Util::ResourceBase, PortFileProvider + struct PathsPortFileProvider : Util::ResourceBase, PortFileProvider { + explicit PathsPortFileProvider(const VcpkgPaths& paths); + Optional<const SourceControlFile&> get_control_file(const std::string& src_name) const override; + + private: const VcpkgPaths& ports; mutable std::unordered_map<std::string, SourceControlFile> cache; - explicit PathsPortFile(const VcpkgPaths& paths); - const SourceControlFile& get_control_file(const std::string& spec) const override; + }; + + struct ClusterGraph; + struct GraphPlan; + + struct PackageGraph + { + PackageGraph(const PortFileProvider& provider, const StatusParagraphs& status_db); + ~PackageGraph(); + + void install(const FeatureSpec& spec); + void upgrade(const PackageSpec& spec); + + std::vector<AnyAction> serialize() const; + + private: + std::unique_ptr<GraphPlan> m_graph_plan; + std::unique_ptr<ClusterGraph> m_graph; }; std::vector<InstallPlanAction> create_install_plan(const PortFileProvider& port_file_provider, @@ -148,11 +168,18 @@ namespace vcpkg::Dependencies std::vector<RemovePlanAction> create_remove_plan(const std::vector<PackageSpec>& specs, const StatusParagraphs& status_db); - 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); std::vector<AnyAction> create_feature_install_plan(const std::unordered_map<std::string, SourceControlFile>& map, const std::vector<FeatureSpec>& specs, const StatusParagraphs& status_db); + + std::vector<AnyAction> create_feature_install_plan(const PortFileProvider& port_file_provider, + const std::vector<FeatureSpec>& specs, + const StatusParagraphs& status_db); + + void print_plan(const std::vector<AnyAction>& action_plan, const bool is_recursive = true); } diff --git a/toolsrc/include/vcpkg/globalstate.h b/toolsrc/include/vcpkg/globalstate.h index 7cea361cf..360d3f43e 100644 --- a/toolsrc/include/vcpkg/globalstate.h +++ b/toolsrc/include/vcpkg/globalstate.h @@ -9,11 +9,13 @@ namespace vcpkg { struct GlobalState { - static Util::LockGuarded<Chrono::ElapsedTime> timer; + static Util::LockGuarded<Chrono::ElapsedTimer> timer; + static Util::LockGuarded<std::string> g_surveydate; + static std::atomic<bool> debugging; static std::atomic<bool> feature_packages; static std::atomic<int> g_init_console_cp; static std::atomic<int> g_init_console_output_cp; }; -}
\ No newline at end of file +} diff --git a/toolsrc/include/vcpkg/install.h b/toolsrc/include/vcpkg/install.h index 28896adee..2e92764dc 100644 --- a/toolsrc/include/vcpkg/install.h +++ b/toolsrc/include/vcpkg/install.h @@ -1,5 +1,6 @@ #pragma once +#include <vcpkg/base/chrono.h> #include <vcpkg/build.h> #include <vcpkg/dependencies.h> #include <vcpkg/vcpkgcmdarguments.h> @@ -25,7 +26,7 @@ namespace vcpkg::Install PackageSpec spec; Build::ExtendedBuildResult build_result; - std::string timing; + vcpkg::Chrono::ElapsedTime timing; const Dependencies::AnyAction* action; }; @@ -36,6 +37,7 @@ namespace vcpkg::Install std::string total_elapsed_time; void print() const; + std::string xunit_results() const; }; struct InstallDir diff --git a/toolsrc/include/vcpkg/packagespec.h b/toolsrc/include/vcpkg/packagespec.h index 0487ae6b8..f1119e2f3 100644 --- a/toolsrc/include/vcpkg/packagespec.h +++ b/toolsrc/include/vcpkg/packagespec.h @@ -22,6 +22,10 @@ namespace vcpkg static std::vector<PackageSpec> to_package_specs(const std::vector<std::string>& ports, const Triplet& triplet); + static std::vector<PackageSpec> from_dependencies_of_port(const std::string& port, + const std::vector<std::string>& dependencies, + const Triplet& triplet); + const std::string& name() const; const Triplet& triplet() const; @@ -30,6 +34,13 @@ namespace vcpkg std::string to_string() const; + bool operator<(const PackageSpec& other) const + { + if (name() < other.name()) return true; + if (name() > other.name()) return false; + return triplet() < other.triplet(); + } + private: std::string m_name; Triplet m_triplet; diff --git a/toolsrc/include/vcpkg/paragraphs.h b/toolsrc/include/vcpkg/paragraphs.h index c8dbea646..e2c7f2d99 100644 --- a/toolsrc/include/vcpkg/paragraphs.h +++ b/toolsrc/include/vcpkg/paragraphs.h @@ -32,7 +32,4 @@ namespace vcpkg::Paragraphs std::vector<std::unique_ptr<SourceControlFile>> load_all_ports(const Files::Filesystem& fs, const fs::path& ports_dir); - - std::map<std::string, VersionT> load_all_port_names_and_versions(const Files::Filesystem& fs, - const fs::path& ports_dir); } diff --git a/toolsrc/include/vcpkg/update.h b/toolsrc/include/vcpkg/update.h index 7587b9eb2..b85f7b2b3 100644 --- a/toolsrc/include/vcpkg/update.h +++ b/toolsrc/include/vcpkg/update.h @@ -1,5 +1,6 @@ #pragma once +#include <vcpkg/dependencies.h> #include <vcpkg/packagespec.h> #include <vcpkg/statusparagraphs.h> #include <vcpkg/vcpkgcmdarguments.h> @@ -16,7 +17,8 @@ namespace vcpkg::Update VersionDiff version_diff; }; - 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); + void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths); -}
\ No newline at end of file +} diff --git a/toolsrc/include/vcpkg/userconfig.h b/toolsrc/include/vcpkg/userconfig.h new file mode 100644 index 000000000..63b8e5481 --- /dev/null +++ b/toolsrc/include/vcpkg/userconfig.h @@ -0,0 +1,20 @@ +#pragma once + +#include <string> +#include <vcpkg/base/files.h> + +namespace vcpkg +{ + struct UserConfig + { + std::string user_id; + std::string user_time; + std::string user_mac; + + std::string last_completed_survey; + + static UserConfig try_read_data(const Files::Filesystem& fs); + + void try_write_data(Files::Filesystem& fs) const; + }; +} 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/system.cpp b/toolsrc/src/vcpkg/base/system.cpp index e3d3ad292..625ee6ce0 100644 --- a/toolsrc/src/vcpkg/base/system.cpp +++ b/toolsrc/src/vcpkg/base/system.cpp @@ -202,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)); 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 6ef72b639..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()); @@ -260,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()) { @@ -290,16 +290,13 @@ 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(); } } @@ -309,7 +306,7 @@ namespace vcpkg::Build 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()}, @@ -323,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(); @@ -343,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) { @@ -351,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)); } } } 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.integrate.cpp b/toolsrc/src/vcpkg/commands.integrate.cpp index f13a999fa..460e99b88 100644 --- a/toolsrc/src/vcpkg/commands.integrate.cpp +++ b/toolsrc/src/vcpkg/commands.integrate.cpp @@ -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 32cb6f03e..e3221a12f 100644 --- a/toolsrc/src/vcpkg/export.cpp +++ b/toolsrc/src/vcpkg/export.cpp @@ -478,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 0888e21d5..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,7 +443,7 @@ 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, }; @@ -627,7 +515,7 @@ namespace vcpkg::Install library_target_pair.second.erase(library_target_pair.second.begin() + 4, library_target_pair.second.end()); System::println(" find_package(%s REQUIRED)\n" - " # Note: %d targets were omitted\n" + " # Note: %zd targets were omitted\n" " target_link_libraries(main PRIVATE %s)\n", library_target_pair.first, omitted, @@ -673,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) @@ -710,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) { @@ -735,10 +625,21 @@ namespace vcpkg::Install summary.print(); } + 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(); @@ -751,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) { } @@ -759,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; @@ -770,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 69160705c..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() @@ -394,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(); @@ -413,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; } @@ -420,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.cpp b/toolsrc/src/vcpkg/postbuildlint.cpp index 0532bce58..2b427737a 100644 --- a/toolsrc/src/vcpkg/postbuildlint.cpp +++ b/toolsrc/src/vcpkg/postbuildlint.cpp @@ -479,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"); 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/vcpkgpaths.cpp b/toolsrc/src/vcpkg/vcpkgpaths.cpp index d5763921e..e64a681e2 100644 --- a/toolsrc/src/vcpkg/vcpkgpaths.cpp +++ b/toolsrc/src/vcpkg/vcpkgpaths.cpp @@ -92,7 +92,8 @@ 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 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}); @@ -280,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)) { @@ -384,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) { @@ -440,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; @@ -487,17 +499,39 @@ namespace vcpkg if (fs.exists(vs_dumpbin_exe)) { - found_toolsets.push_back({vs_instance.root_path, - vs_dumpbin_exe, - vcvarsall_bat, - {}, - major_version == "14" ? V_140 : V_120, - 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."); @@ -575,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(); } diff --git a/toolsrc/vcpkglib/vcpkglib.vcxproj b/toolsrc/vcpkglib/vcpkglib.vcxproj index ae332e015..455891f85 100644 --- a/toolsrc/vcpkglib/vcpkglib.vcxproj +++ b/toolsrc/vcpkglib/vcpkglib.vcxproj @@ -178,6 +178,7 @@ <ClInclude Include="..\include\vcpkg\statusparagraphs.h" /> <ClInclude Include="..\include\vcpkg\triplet.h" /> <ClInclude Include="..\include\vcpkg\update.h" /> + <ClInclude Include="..\include\vcpkg\userconfig.h" /> <ClInclude Include="..\include\vcpkg\vcpkgcmdarguments.h" /> <ClInclude Include="..\include\vcpkg\vcpkglib.h" /> <ClInclude Include="..\include\vcpkg\vcpkgpaths.h" /> @@ -219,6 +220,7 @@ <ClCompile Include="..\src\vcpkg\commands.owns.cpp" /> <ClCompile Include="..\src\vcpkg\commands.portsdiff.cpp" /> <ClCompile Include="..\src\vcpkg\commands.search.cpp" /> + <ClCompile Include="..\src\vcpkg\commands.upgrade.cpp" /> <ClCompile Include="..\src\vcpkg\commands.version.cpp" /> <ClCompile Include="..\src\vcpkg\dependencies.cpp" /> <ClCompile Include="..\src\vcpkg\export.cpp" /> @@ -240,6 +242,7 @@ <ClCompile Include="..\src\vcpkg\statusparagraphs.cpp" /> <ClCompile Include="..\src\vcpkg\triplet.cpp" /> <ClCompile Include="..\src\vcpkg\update.cpp" /> + <ClCompile Include="..\src\vcpkg\userconfig.cpp" /> <ClCompile Include="..\src\vcpkg\vcpkgcmdarguments.cpp" /> <ClCompile Include="..\src\vcpkg\vcpkglib.cpp" /> <ClCompile Include="..\src\vcpkg\vcpkgpaths.cpp" /> diff --git a/toolsrc/vcpkglib/vcpkglib.vcxproj.filters b/toolsrc/vcpkglib/vcpkglib.vcxproj.filters index e902bffbb..f8f517ad7 100644 --- a/toolsrc/vcpkglib/vcpkglib.vcxproj.filters +++ b/toolsrc/vcpkglib/vcpkglib.vcxproj.filters @@ -192,6 +192,12 @@ <ClCompile Include="..\src\vcpkg\base\system.cpp"> <Filter>Source Files\vcpkg\base</Filter> </ClCompile> + <ClCompile Include="..\src\vcpkg\userconfig.cpp"> + <Filter>Source Files\vcpkg</Filter> + </ClCompile> + <ClCompile Include="..\src\vcpkg\commands.upgrade.cpp"> + <Filter>Source Files\vcpkg</Filter> + </ClCompile> </ItemGroup> <ItemGroup> <ClInclude Include="..\include\pch.h"> @@ -332,5 +338,8 @@ <ClInclude Include="..\include\vcpkg\export.ifw.h"> <Filter>Header Files\vcpkg</Filter> </ClInclude> + <ClInclude Include="..\include\vcpkg\userconfig.h"> + <Filter>Header Files\vcpkg</Filter> + </ClInclude> </ItemGroup> </Project>
\ No newline at end of file diff --git a/toolsrc/vcpkgtest/vcpkgtest.vcxproj b/toolsrc/vcpkgtest/vcpkgtest.vcxproj index 9eafc1ada..166216c45 100644 --- a/toolsrc/vcpkgtest/vcpkgtest.vcxproj +++ b/toolsrc/vcpkgtest/vcpkgtest.vcxproj @@ -20,6 +20,7 @@ </ItemGroup> <ItemGroup> <ClCompile Include="..\src\tests.arguments.cpp" /> + <ClCompile Include="..\src\tests.chrono.cpp" /> <ClCompile Include="..\src\tests.dependencies.cpp" /> <ClCompile Include="..\src\tests.packagespec.cpp" /> <ClCompile Include="..\src\tests.paragraph.cpp" /> diff --git a/toolsrc/vcpkgtest/vcpkgtest.vcxproj.filters b/toolsrc/vcpkgtest/vcpkgtest.vcxproj.filters index 2121f9782..422f9298e 100644 --- a/toolsrc/vcpkgtest/vcpkgtest.vcxproj.filters +++ b/toolsrc/vcpkgtest/vcpkgtest.vcxproj.filters @@ -42,6 +42,9 @@ <ClCompile Include="..\src\tests.utils.cpp"> <Filter>Source Files</Filter> </ClCompile> + <ClCompile Include="..\src\tests.chrono.cpp"> + <Filter>Source Files</Filter> + </ClCompile> </ItemGroup> <ItemGroup> <ClInclude Include="..\include\tests.pch.h"> |
