From d2620cf02bf01bb3cd6873aa2ba7687644019ed0 Mon Sep 17 00:00:00 2001 From: nicole mazzuca Date: Tue, 14 Jul 2020 08:50:19 -0700 Subject: [vcpkg formatting] Turn off DeriveLineEnding (#12368) * [vcpkg formatting] Turn off DeriveLineEnding * format * Add newlines to the end of files Since we're reformatting anyways --- toolsrc/.clang-format | 1 + toolsrc/include/vcpkg-test/mockcmakevarprovider.h | 76 +- toolsrc/include/vcpkg/base/cache.h | 42 +- toolsrc/include/vcpkg/base/ignore_errors.h | 20 +- toolsrc/include/vcpkg/base/span.h | 114 +- toolsrc/include/vcpkg/base/view.h | 18 +- toolsrc/include/vcpkg/binarycaching.h | 106 +- toolsrc/include/vcpkg/binarycaching.private.h | 120 +- toolsrc/include/vcpkg/buildenvironment.h | 26 +- toolsrc/include/vcpkg/export.chocolatey.h | 38 +- toolsrc/include/vcpkg/platform-expression.h | 148 +- toolsrc/include/vcpkg/textrowcol.h | 34 +- toolsrc/include/vcpkg/visualstudio.h | 28 +- toolsrc/src/vcpkg-test/mockcmakevarsprovider.cpp | 56 +- toolsrc/src/vcpkg/binarycaching.cpp | 2112 ++++++++--------- toolsrc/src/vcpkg/build.cpp | 2526 ++++++++++----------- toolsrc/src/vcpkg/buildenvironment.cpp | 40 +- toolsrc/src/vcpkg/commands.ciclean.cpp | 84 +- toolsrc/src/vcpkg/commands.dependinfo.cpp | 662 +++--- toolsrc/src/vcpkg/commands.xvsinstances.cpp | 72 +- toolsrc/src/vcpkg/export.chocolatey.cpp | 316 +-- 21 files changed, 3320 insertions(+), 3319 deletions(-) diff --git a/toolsrc/.clang-format b/toolsrc/.clang-format index 925010602..6c32a733a 100644 --- a/toolsrc/.clang-format +++ b/toolsrc/.clang-format @@ -31,6 +31,7 @@ ForEachMacros: [TEST_CASE, SECTION] PenaltyReturnTypeOnItsOwnLine: 1000 SpaceAfterTemplateKeyword: false SpaceBeforeCpp11BracedList: false +DeriveLineEnding: false UseCRLF: false IncludeBlocks: Regroup diff --git a/toolsrc/include/vcpkg-test/mockcmakevarprovider.h b/toolsrc/include/vcpkg-test/mockcmakevarprovider.h index 21207c856..482f9ea40 100644 --- a/toolsrc/include/vcpkg-test/mockcmakevarprovider.h +++ b/toolsrc/include/vcpkg-test/mockcmakevarprovider.h @@ -1,38 +1,38 @@ -#pragma once - -#include - -namespace vcpkg::Test -{ - struct MockCMakeVarProvider : CMakeVars::CMakeVarProvider - { - void load_generic_triplet_vars(Triplet triplet) const override { generic_triplet_vars[triplet] = {}; } - - void load_dep_info_vars(Span specs) const override - { - for (auto&& spec : specs) - dep_info_vars[spec] = {}; - } - - void load_tag_vars(Span specs, - const PortFileProvider::PortFileProvider& port_provider) const override - { - for (auto&& spec : specs) - tag_vars[spec.package_spec] = {}; - Util::unused(port_provider); - } - - Optional&> get_generic_triplet_vars( - Triplet triplet) const override; - - Optional&> get_dep_info_vars( - const PackageSpec& spec) const override; - - Optional&> get_tag_vars( - const PackageSpec& spec) const override; - - mutable std::unordered_map> dep_info_vars; - mutable std::unordered_map> tag_vars; - mutable std::unordered_map> generic_triplet_vars; - }; -} +#pragma once + +#include + +namespace vcpkg::Test +{ + struct MockCMakeVarProvider : CMakeVars::CMakeVarProvider + { + void load_generic_triplet_vars(Triplet triplet) const override { generic_triplet_vars[triplet] = {}; } + + void load_dep_info_vars(Span specs) const override + { + for (auto&& spec : specs) + dep_info_vars[spec] = {}; + } + + void load_tag_vars(Span specs, + const PortFileProvider::PortFileProvider& port_provider) const override + { + for (auto&& spec : specs) + tag_vars[spec.package_spec] = {}; + Util::unused(port_provider); + } + + Optional&> get_generic_triplet_vars( + Triplet triplet) const override; + + Optional&> get_dep_info_vars( + const PackageSpec& spec) const override; + + Optional&> get_tag_vars( + const PackageSpec& spec) const override; + + mutable std::unordered_map> dep_info_vars; + mutable std::unordered_map> tag_vars; + mutable std::unordered_map> generic_triplet_vars; + }; +} diff --git a/toolsrc/include/vcpkg/base/cache.h b/toolsrc/include/vcpkg/base/cache.h index dfc7565b8..1996f2587 100644 --- a/toolsrc/include/vcpkg/base/cache.h +++ b/toolsrc/include/vcpkg/base/cache.h @@ -1,21 +1,21 @@ -#pragma once - -#include - -namespace vcpkg -{ - template - struct Cache - { - template - Value const& get_lazy(const Key& k, const F& f) const - { - auto it = m_cache.find(k); - if (it != m_cache.end()) return it->second; - return m_cache.emplace(k, f()).first->second; - } - - private: - mutable std::map m_cache; - }; -} +#pragma once + +#include + +namespace vcpkg +{ + template + struct Cache + { + template + Value const& get_lazy(const Key& k, const F& f) const + { + auto it = m_cache.find(k); + if (it != m_cache.end()) return it->second; + return m_cache.emplace(k, f()).first->second; + } + + private: + mutable std::map m_cache; + }; +} diff --git a/toolsrc/include/vcpkg/base/ignore_errors.h b/toolsrc/include/vcpkg/base/ignore_errors.h index 44077059a..ed8b38c56 100644 --- a/toolsrc/include/vcpkg/base/ignore_errors.h +++ b/toolsrc/include/vcpkg/base/ignore_errors.h @@ -1,10 +1,10 @@ -#pragma once - -namespace vcpkg -{ - struct ignore_errors_t - { - }; - - constexpr ignore_errors_t ignore_errors; -} +#pragma once + +namespace vcpkg +{ + struct ignore_errors_t + { + }; + + constexpr ignore_errors_t ignore_errors; +} diff --git a/toolsrc/include/vcpkg/base/span.h b/toolsrc/include/vcpkg/base/span.h index 482fe1b00..221cccc8c 100644 --- a/toolsrc/include/vcpkg/base/span.h +++ b/toolsrc/include/vcpkg/base/span.h @@ -1,57 +1,57 @@ -#pragma once - -#include -#include -#include -#include - -namespace vcpkg -{ - template - struct Span - { - public: - static_assert(std::is_object::value, "Span is illegal"); - - using value_type = std::decay_t; - using element_type = T; - using pointer = std::add_pointer_t; - using reference = std::add_lvalue_reference_t; - using iterator = pointer; - - constexpr Span() noexcept : m_ptr(nullptr), m_count(0) { } - constexpr Span(std::nullptr_t) noexcept : m_ptr(nullptr), m_count(0) { } - constexpr Span(pointer ptr, size_t count) noexcept : m_ptr(ptr), m_count(count) { } - constexpr Span(pointer ptr_begin, pointer ptr_end) noexcept : m_ptr(ptr_begin), m_count(ptr_end - ptr_begin) { } - - template - constexpr Span(T (&arr)[N]) noexcept : m_ptr(arr), m_count(N) - { - } - - template>> - constexpr Span(std::remove_const_t (&arr)[N]) noexcept : m_ptr(arr), m_count(N) - { - } - - template().data()), - class = std::enable_if_t, Span>::value>> - constexpr Span(Range&& v) noexcept : Span(v.data(), v.size()) - { - static_assert(std::is_same::value_type, value_type>::value, - "Cannot convert incompatible ranges"); - } - - constexpr iterator begin() const { return m_ptr; } - constexpr iterator end() const { return m_ptr + m_count; } - - constexpr reference operator[](size_t i) const { return m_ptr[i]; } - constexpr pointer data() const { return m_ptr; } - constexpr size_t size() const { return m_count; } - - private: - pointer m_ptr; - size_t m_count; - }; -} +#pragma once + +#include +#include +#include +#include + +namespace vcpkg +{ + template + struct Span + { + public: + static_assert(std::is_object::value, "Span is illegal"); + + using value_type = std::decay_t; + using element_type = T; + using pointer = std::add_pointer_t; + using reference = std::add_lvalue_reference_t; + using iterator = pointer; + + constexpr Span() noexcept : m_ptr(nullptr), m_count(0) { } + constexpr Span(std::nullptr_t) noexcept : m_ptr(nullptr), m_count(0) { } + constexpr Span(pointer ptr, size_t count) noexcept : m_ptr(ptr), m_count(count) { } + constexpr Span(pointer ptr_begin, pointer ptr_end) noexcept : m_ptr(ptr_begin), m_count(ptr_end - ptr_begin) { } + + template + constexpr Span(T (&arr)[N]) noexcept : m_ptr(arr), m_count(N) + { + } + + template>> + constexpr Span(std::remove_const_t (&arr)[N]) noexcept : m_ptr(arr), m_count(N) + { + } + + template().data()), + class = std::enable_if_t, Span>::value>> + constexpr Span(Range&& v) noexcept : Span(v.data(), v.size()) + { + static_assert(std::is_same::value_type, value_type>::value, + "Cannot convert incompatible ranges"); + } + + constexpr iterator begin() const { return m_ptr; } + constexpr iterator end() const { return m_ptr + m_count; } + + constexpr reference operator[](size_t i) const { return m_ptr[i]; } + constexpr pointer data() const { return m_ptr; } + constexpr size_t size() const { return m_count; } + + private: + pointer m_ptr; + size_t m_count; + }; +} diff --git a/toolsrc/include/vcpkg/base/view.h b/toolsrc/include/vcpkg/base/view.h index 8a9c40994..491e8351b 100644 --- a/toolsrc/include/vcpkg/base/view.h +++ b/toolsrc/include/vcpkg/base/view.h @@ -1,9 +1,9 @@ -#pragma once - -#include - -namespace vcpkg -{ - template - using View = Span; -} +#pragma once + +#include + +namespace vcpkg +{ + template + using View = Span; +} diff --git a/toolsrc/include/vcpkg/binarycaching.h b/toolsrc/include/vcpkg/binarycaching.h index 44c223084..be52d1e49 100644 --- a/toolsrc/include/vcpkg/binarycaching.h +++ b/toolsrc/include/vcpkg/binarycaching.h @@ -1,53 +1,53 @@ -#pragma once - -#include -#include - -#include -#include - -namespace vcpkg::Dependencies -{ - struct InstallPlanAction; - struct ActionPlan; -} -namespace vcpkg::Build -{ - struct AbiTagAndFile; - struct BuildPackageOptions; -} - -namespace vcpkg -{ - enum class RestoreResult - { - missing, - success, - build_failed, - }; - - struct IBinaryProvider - { - virtual ~IBinaryProvider() = default; - /// Gives the BinaryProvider an opportunity to batch any downloading or server communication for executing - /// `plan`. - virtual void prefetch(const VcpkgPaths& paths, const Dependencies::ActionPlan& plan) = 0; - /// Attempts to restore the package referenced by `action` into the packages directory. - virtual RestoreResult try_restore(const VcpkgPaths& paths, const Dependencies::InstallPlanAction& action) = 0; - /// Called upon a successful build of `action` - virtual void push_success(const VcpkgPaths& paths, const Dependencies::InstallPlanAction& action) = 0; - /// Requests the result of `try_restore()` without actually downloading the package. Used by CI to determine - /// missing packages. - virtual RestoreResult precheck(const VcpkgPaths& paths, const Dependencies::InstallPlanAction& action) = 0; - }; - - IBinaryProvider& null_binary_provider(); - - ExpectedS> create_binary_provider_from_configs(View args); - ExpectedS> create_binary_provider_from_configs_pure(const std::string& env_string, - View args); - - std::string generate_nuget_packages_config(const Dependencies::ActionPlan& action); - - void help_topic_binary_caching(const VcpkgPaths& paths); -} +#pragma once + +#include +#include + +#include +#include + +namespace vcpkg::Dependencies +{ + struct InstallPlanAction; + struct ActionPlan; +} +namespace vcpkg::Build +{ + struct AbiTagAndFile; + struct BuildPackageOptions; +} + +namespace vcpkg +{ + enum class RestoreResult + { + missing, + success, + build_failed, + }; + + struct IBinaryProvider + { + virtual ~IBinaryProvider() = default; + /// Gives the BinaryProvider an opportunity to batch any downloading or server communication for executing + /// `plan`. + virtual void prefetch(const VcpkgPaths& paths, const Dependencies::ActionPlan& plan) = 0; + /// Attempts to restore the package referenced by `action` into the packages directory. + virtual RestoreResult try_restore(const VcpkgPaths& paths, const Dependencies::InstallPlanAction& action) = 0; + /// Called upon a successful build of `action` + virtual void push_success(const VcpkgPaths& paths, const Dependencies::InstallPlanAction& action) = 0; + /// Requests the result of `try_restore()` without actually downloading the package. Used by CI to determine + /// missing packages. + virtual RestoreResult precheck(const VcpkgPaths& paths, const Dependencies::InstallPlanAction& action) = 0; + }; + + IBinaryProvider& null_binary_provider(); + + ExpectedS> create_binary_provider_from_configs(View args); + ExpectedS> create_binary_provider_from_configs_pure(const std::string& env_string, + View args); + + std::string generate_nuget_packages_config(const Dependencies::ActionPlan& action); + + void help_topic_binary_caching(const VcpkgPaths& paths); +} diff --git a/toolsrc/include/vcpkg/binarycaching.private.h b/toolsrc/include/vcpkg/binarycaching.private.h index 040b34597..1b5d587de 100644 --- a/toolsrc/include/vcpkg/binarycaching.private.h +++ b/toolsrc/include/vcpkg/binarycaching.private.h @@ -1,60 +1,60 @@ -#pragma once - -#include -#include -#include - -#include - -namespace vcpkg -{ - std::string reformat_version(const std::string& version, const std::string& abi_tag); - - struct NugetReference - { - explicit NugetReference(const Dependencies::InstallPlanAction& action) - : NugetReference(action.spec, - action.source_control_file_location.value_or_exit(VCPKG_LINE_INFO) - .source_control_file->core_paragraph->version, - action.abi_info.value_or_exit(VCPKG_LINE_INFO).package_abi) - { - } - - NugetReference(const PackageSpec& spec, const std::string& raw_version, const std::string& abi_tag) - : id(spec.dir()), version(reformat_version(raw_version, abi_tag)) - { - } - - std::string id; - std::string version; - - std::string nupkg_filename() const { return Strings::concat(id, '.', version, ".nupkg"); } - }; - - std::string generate_nuspec(const VcpkgPaths& paths, - const Dependencies::InstallPlanAction& action, - const NugetReference& ref); - - struct XmlSerializer - { - XmlSerializer& emit_declaration(); - XmlSerializer& open_tag(StringLiteral sl); - XmlSerializer& start_complex_open_tag(StringLiteral sl); - XmlSerializer& text_attr(StringLiteral name, StringView content); - XmlSerializer& finish_complex_open_tag(); - XmlSerializer& finish_self_closing_complex_tag(); - XmlSerializer& close_tag(StringLiteral sl); - XmlSerializer& text(StringView sv); - XmlSerializer& simple_tag(StringLiteral tag, StringView content); - XmlSerializer& line_break(); - - std::string buf; - - private: - XmlSerializer& emit_pending_indent(); - - int m_indent = 0; - bool m_pending_indent = false; - }; - -} \ No newline at end of file +#pragma once + +#include +#include +#include + +#include + +namespace vcpkg +{ + std::string reformat_version(const std::string& version, const std::string& abi_tag); + + struct NugetReference + { + explicit NugetReference(const Dependencies::InstallPlanAction& action) + : NugetReference(action.spec, + action.source_control_file_location.value_or_exit(VCPKG_LINE_INFO) + .source_control_file->core_paragraph->version, + action.abi_info.value_or_exit(VCPKG_LINE_INFO).package_abi) + { + } + + NugetReference(const PackageSpec& spec, const std::string& raw_version, const std::string& abi_tag) + : id(spec.dir()), version(reformat_version(raw_version, abi_tag)) + { + } + + std::string id; + std::string version; + + std::string nupkg_filename() const { return Strings::concat(id, '.', version, ".nupkg"); } + }; + + std::string generate_nuspec(const VcpkgPaths& paths, + const Dependencies::InstallPlanAction& action, + const NugetReference& ref); + + struct XmlSerializer + { + XmlSerializer& emit_declaration(); + XmlSerializer& open_tag(StringLiteral sl); + XmlSerializer& start_complex_open_tag(StringLiteral sl); + XmlSerializer& text_attr(StringLiteral name, StringView content); + XmlSerializer& finish_complex_open_tag(); + XmlSerializer& finish_self_closing_complex_tag(); + XmlSerializer& close_tag(StringLiteral sl); + XmlSerializer& text(StringView sv); + XmlSerializer& simple_tag(StringLiteral tag, StringView content); + XmlSerializer& line_break(); + + std::string buf; + + private: + XmlSerializer& emit_pending_indent(); + + int m_indent = 0; + bool m_pending_indent = false; + }; + +} diff --git a/toolsrc/include/vcpkg/buildenvironment.h b/toolsrc/include/vcpkg/buildenvironment.h index 921fb419d..1216caa2a 100644 --- a/toolsrc/include/vcpkg/buildenvironment.h +++ b/toolsrc/include/vcpkg/buildenvironment.h @@ -1,13 +1,13 @@ -#include - -#include - -#include -#include - -namespace vcpkg -{ - std::string make_cmake_cmd(const VcpkgPaths& paths, - const fs::path& cmake_script, - std::vector&& pass_variables); -} +#include + +#include + +#include +#include + +namespace vcpkg +{ + std::string make_cmake_cmd(const VcpkgPaths& paths, + const fs::path& cmake_script, + std::vector&& pass_variables); +} diff --git a/toolsrc/include/vcpkg/export.chocolatey.h b/toolsrc/include/vcpkg/export.chocolatey.h index 7804108fd..1e6f9a12b 100644 --- a/toolsrc/include/vcpkg/export.chocolatey.h +++ b/toolsrc/include/vcpkg/export.chocolatey.h @@ -1,19 +1,19 @@ -#pragma once - -#include -#include - -#include - -namespace vcpkg::Export::Chocolatey -{ - struct Options - { - Optional maybe_maintainer; - Optional maybe_version_suffix; - }; - - void do_export(const std::vector& export_plan, - const VcpkgPaths& paths, - const Options& chocolatey_options); -} +#pragma once + +#include +#include + +#include + +namespace vcpkg::Export::Chocolatey +{ + struct Options + { + Optional maybe_maintainer; + Optional maybe_version_suffix; + }; + + void do_export(const std::vector& export_plan, + const VcpkgPaths& paths, + const Options& chocolatey_options); +} diff --git a/toolsrc/include/vcpkg/platform-expression.h b/toolsrc/include/vcpkg/platform-expression.h index b9a6f6a88..fd2bb8589 100644 --- a/toolsrc/include/vcpkg/platform-expression.h +++ b/toolsrc/include/vcpkg/platform-expression.h @@ -1,74 +1,74 @@ -#pragma once - -#include -#include - -#include -#include - -namespace vcpkg::PlatformExpression -{ - // map of cmake variables and their values. - using Context = std::unordered_map; - - namespace detail - { - struct ExprImpl; - } - struct Expr - { - static Expr Identifier(StringView id); - static Expr Not(Expr&& e); - static Expr And(std::vector&& exprs); - static Expr Or(std::vector&& exprs); - - // The empty expression is always true - static Expr Empty() { return Expr(); } - - // since ExprImpl is not yet defined, we need to define the ctor and dtor in the C++ file - Expr(); - Expr(const Expr&); - Expr(Expr&&); - Expr& operator=(const Expr& e); - Expr& operator=(Expr&&); - - explicit Expr(std::unique_ptr&& e); - ~Expr(); - - bool evaluate(const Context& context) const; - bool is_empty() const { return !static_cast(underlying_); } - - private: - std::unique_ptr underlying_; - }; - - // Note: for backwards compatibility, in CONTROL files, - // multiple binary operators are allowed to be next to one another; i.e. - // (windows & arm) = (windows && arm) = (windows &&& arm), etc. - enum class MultipleBinaryOperators - { - Deny, - Allow, - }; - - // platform expression parses the following : - // : - // - // - // - // : - // ( ) - // - // : - // A lowercase alpha-numeric string - // : - // - // ! - // - // - // & - // - // - // | - ExpectedS parse_platform_expression(StringView expression, MultipleBinaryOperators multiple_binary_operators); -} +#pragma once + +#include +#include + +#include +#include + +namespace vcpkg::PlatformExpression +{ + // map of cmake variables and their values. + using Context = std::unordered_map; + + namespace detail + { + struct ExprImpl; + } + struct Expr + { + static Expr Identifier(StringView id); + static Expr Not(Expr&& e); + static Expr And(std::vector&& exprs); + static Expr Or(std::vector&& exprs); + + // The empty expression is always true + static Expr Empty() { return Expr(); } + + // since ExprImpl is not yet defined, we need to define the ctor and dtor in the C++ file + Expr(); + Expr(const Expr&); + Expr(Expr&&); + Expr& operator=(const Expr& e); + Expr& operator=(Expr&&); + + explicit Expr(std::unique_ptr&& e); + ~Expr(); + + bool evaluate(const Context& context) const; + bool is_empty() const { return !static_cast(underlying_); } + + private: + std::unique_ptr underlying_; + }; + + // Note: for backwards compatibility, in CONTROL files, + // multiple binary operators are allowed to be next to one another; i.e. + // (windows & arm) = (windows && arm) = (windows &&& arm), etc. + enum class MultipleBinaryOperators + { + Deny, + Allow, + }; + + // platform expression parses the following : + // : + // + // + // + // : + // ( ) + // + // : + // A lowercase alpha-numeric string + // : + // + // ! + // + // + // & + // + // + // | + ExpectedS parse_platform_expression(StringView expression, MultipleBinaryOperators multiple_binary_operators); +} diff --git a/toolsrc/include/vcpkg/textrowcol.h b/toolsrc/include/vcpkg/textrowcol.h index bf6f31d7c..b8269f697 100644 --- a/toolsrc/include/vcpkg/textrowcol.h +++ b/toolsrc/include/vcpkg/textrowcol.h @@ -1,17 +1,17 @@ -#pragma once - -namespace vcpkg::Parse -{ - struct TextRowCol - { - constexpr TextRowCol() noexcept = default; - constexpr TextRowCol(int row, int column) noexcept : row(row), column(column) { } - /// '0' indicates uninitialized; '1' is the first row. - int row = 0; - /// '0' indicates uninitialized; '1' is the first column. - int column = 0; - - constexpr int row_or(int def) const noexcept { return row ? row : def; } - constexpr int column_or(int def) const noexcept { return column ? column : def; } - }; -} +#pragma once + +namespace vcpkg::Parse +{ + struct TextRowCol + { + constexpr TextRowCol() noexcept = default; + constexpr TextRowCol(int row, int column) noexcept : row(row), column(column) { } + /// '0' indicates uninitialized; '1' is the first row. + int row = 0; + /// '0' indicates uninitialized; '1' is the first column. + int column = 0; + + constexpr int row_or(int def) const noexcept { return row ? row : def; } + constexpr int column_or(int def) const noexcept { return column ? column : def; } + }; +} diff --git a/toolsrc/include/vcpkg/visualstudio.h b/toolsrc/include/vcpkg/visualstudio.h index cd99db352..da6652b3e 100644 --- a/toolsrc/include/vcpkg/visualstudio.h +++ b/toolsrc/include/vcpkg/visualstudio.h @@ -1,14 +1,14 @@ -#pragma once - -#if defined(_WIN32) - -#include - -namespace vcpkg::VisualStudio -{ - std::vector get_visual_studio_instances(const VcpkgPaths& paths); - - std::vector find_toolset_instances_preferred_first(const VcpkgPaths& paths); -} - -#endif +#pragma once + +#if defined(_WIN32) + +#include + +namespace vcpkg::VisualStudio +{ + std::vector get_visual_studio_instances(const VcpkgPaths& paths); + + std::vector find_toolset_instances_preferred_first(const VcpkgPaths& paths); +} + +#endif diff --git a/toolsrc/src/vcpkg-test/mockcmakevarsprovider.cpp b/toolsrc/src/vcpkg-test/mockcmakevarsprovider.cpp index 655bc33b4..77a796fad 100644 --- a/toolsrc/src/vcpkg-test/mockcmakevarsprovider.cpp +++ b/toolsrc/src/vcpkg-test/mockcmakevarsprovider.cpp @@ -1,28 +1,28 @@ -#include - -namespace vcpkg::Test -{ - Optional&> MockCMakeVarProvider::get_generic_triplet_vars( - Triplet triplet) const - { - auto it = generic_triplet_vars.find(triplet); - if (it == generic_triplet_vars.end()) return nullopt; - return it->second; - } - - Optional&> MockCMakeVarProvider::get_dep_info_vars( - const PackageSpec& spec) const - { - auto it = dep_info_vars.find(spec); - if (it == dep_info_vars.end()) return nullopt; - return it->second; - } - - Optional&> MockCMakeVarProvider::get_tag_vars( - const PackageSpec& spec) const - { - auto it = tag_vars.find(spec); - if (it == tag_vars.end()) return nullopt; - return it->second; - } -} \ No newline at end of file +#include + +namespace vcpkg::Test +{ + Optional&> MockCMakeVarProvider::get_generic_triplet_vars( + Triplet triplet) const + { + auto it = generic_triplet_vars.find(triplet); + if (it == generic_triplet_vars.end()) return nullopt; + return it->second; + } + + Optional&> MockCMakeVarProvider::get_dep_info_vars( + const PackageSpec& spec) const + { + auto it = dep_info_vars.find(spec); + if (it == dep_info_vars.end()) return nullopt; + return it->second; + } + + Optional&> MockCMakeVarProvider::get_tag_vars( + const PackageSpec& spec) const + { + auto it = tag_vars.find(spec); + if (it == tag_vars.end()) return nullopt; + return it->second; + } +} diff --git a/toolsrc/src/vcpkg/binarycaching.cpp b/toolsrc/src/vcpkg/binarycaching.cpp index 0a0b100dc..d63d6b3d4 100644 --- a/toolsrc/src/vcpkg/binarycaching.cpp +++ b/toolsrc/src/vcpkg/binarycaching.cpp @@ -1,1056 +1,1056 @@ -#include "pch.h" - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -using namespace vcpkg; - -namespace -{ - static System::ExitCodeAndOutput decompress_archive(const VcpkgPaths& paths, - const PackageSpec& spec, - const fs::path& archive_path) - { - auto& fs = paths.get_filesystem(); - - auto pkg_path = paths.package_dir(spec); - fs.remove_all(pkg_path, VCPKG_LINE_INFO); - std::error_code ec; - fs.create_directories(pkg_path, ec); - auto files = fs.get_files_non_recursive(pkg_path); - Checks::check_exit(VCPKG_LINE_INFO, files.empty(), "unable to clear path: %s", pkg_path.u8string()); - -#if defined(_WIN32) - auto&& seven_zip_exe = paths.get_tool_exe(Tools::SEVEN_ZIP); - auto cmd = Strings::format( - R"("%s" x "%s" -o"%s" -y)", seven_zip_exe.u8string(), archive_path.u8string(), pkg_path.u8string()); -#else - auto cmd = Strings::format(R"(unzip -qq "%s" "-d%s")", archive_path.u8string(), pkg_path.u8string()); -#endif - return System::cmd_execute_and_capture_output(cmd, System::get_clean_environment()); - } - - // Compress the source directory into the destination file. - static void compress_directory(const VcpkgPaths& paths, const fs::path& source, const fs::path& destination) - { - auto& fs = paths.get_filesystem(); - - std::error_code ec; - - fs.remove(destination, ec); - Checks::check_exit( - VCPKG_LINE_INFO, !fs.exists(destination), "Could not remove file: %s", destination.u8string()); -#if defined(_WIN32) - auto&& seven_zip_exe = paths.get_tool_exe(Tools::SEVEN_ZIP); - - System::cmd_execute_and_capture_output( - Strings::format( - R"("%s" a "%s" "%s\*")", seven_zip_exe.u8string(), destination.u8string(), source.u8string()), - System::get_clean_environment()); -#else - System::cmd_execute_clean( - Strings::format(R"(cd '%s' && zip --quiet -r '%s' *)", source.u8string(), destination.u8string())); -#endif - } - - struct ArchivesBinaryProvider : IBinaryProvider - { - ArchivesBinaryProvider(std::vector&& read_dirs, std::vector&& write_dirs) - : m_read_dirs(std::move(read_dirs)), m_write_dirs(std::move(write_dirs)) - { - } - ~ArchivesBinaryProvider() = default; - void prefetch(const VcpkgPaths&, const Dependencies::ActionPlan&) override { } - RestoreResult try_restore(const VcpkgPaths& paths, const Dependencies::InstallPlanAction& action) override - { - const auto& abi_tag = action.abi_info.value_or_exit(VCPKG_LINE_INFO).package_abi; - auto& spec = action.spec; - auto& fs = paths.get_filesystem(); - std::error_code ec; - for (auto&& archives_root_dir : m_read_dirs) - { - const std::string archive_name = abi_tag + ".zip"; - const fs::path archive_subpath = fs::u8path(abi_tag.substr(0, 2)) / archive_name; - const fs::path archive_path = archives_root_dir / archive_subpath; - if (fs.exists(archive_path)) - { - System::print2("Using cached binary package: ", archive_path.u8string(), "\n"); - - int archive_result = decompress_archive(paths, spec, archive_path).exit_code; - - if (archive_result == 0) - { - return RestoreResult::success; - } - else - { - System::print2("Failed to decompress archive package\n"); - if (action.build_options.purge_decompress_failure == Build::PurgeDecompressFailure::NO) - { - return RestoreResult::build_failed; - } - else - { - System::print2("Purging bad archive\n"); - fs.remove(archive_path, ec); - } - } - } - - System::printf("Could not locate cached archive: %s\n", archive_path.u8string()); - } - - return RestoreResult::missing; - } - void push_success(const VcpkgPaths& paths, const Dependencies::InstallPlanAction& action) override - { - if (m_write_dirs.empty()) return; - const auto& abi_tag = action.abi_info.value_or_exit(VCPKG_LINE_INFO).package_abi; - auto& spec = action.spec; - auto& fs = paths.get_filesystem(); - const auto tmp_archive_path = paths.buildtrees / spec.name() / (spec.triplet().to_string() + ".zip"); - compress_directory(paths, paths.package_dir(spec), tmp_archive_path); - - for (auto&& m_directory : m_write_dirs) - { - const fs::path& archives_root_dir = m_directory; - const std::string archive_name = abi_tag + ".zip"; - const fs::path archive_subpath = fs::u8path(abi_tag.substr(0, 2)) / archive_name; - const fs::path archive_path = archives_root_dir / archive_subpath; - - fs.create_directories(archive_path.parent_path(), ignore_errors); - std::error_code ec; - if (m_write_dirs.size() > 1) - fs.copy_file(tmp_archive_path, archive_path, fs::copy_options::overwrite_existing, ec); - else - fs.rename_or_copy(tmp_archive_path, archive_path, ".tmp", ec); - if (ec) - { - System::printf(System::Color::warning, - "Failed to store binary cache %s: %s\n", - archive_path.u8string(), - ec.message()); - } - else - System::printf("Stored binary cache: %s\n", archive_path.u8string()); - } - if (m_write_dirs.size() > 1) fs.remove(tmp_archive_path, ignore_errors); - } - RestoreResult precheck(const VcpkgPaths& paths, const Dependencies::InstallPlanAction& action) override - { - const auto& abi_tag = action.abi_info.value_or_exit(VCPKG_LINE_INFO).package_abi; - auto& fs = paths.get_filesystem(); - std::error_code ec; - for (auto&& archives_root_dir : m_read_dirs) - { - const std::string archive_name = abi_tag + ".zip"; - const fs::path archive_subpath = fs::u8path(abi_tag.substr(0, 2)) / archive_name; - const fs::path archive_path = archives_root_dir / archive_subpath; - - if (fs.exists(archive_path)) - { - return RestoreResult::success; - } - } - return RestoreResult::missing; - } - - private: - std::vector m_read_dirs, m_write_dirs; - }; - - static std::string trim_leading_zeroes(std::string v) - { - auto n = v.find_first_not_of('0'); - if (n == std::string::npos) - { - v = "0"; - } - else if (n > 0) - { - v.erase(0, n); - } - return v; - } - - struct NugetBinaryProvider : IBinaryProvider - { - NugetBinaryProvider(std::vector&& read_sources, - std::vector&& write_sources, - std::vector&& read_configs, - std::vector&& write_configs, - bool interactive) - : m_read_sources(std::move(read_sources)) - , m_write_sources(std::move(write_sources)) - , m_read_configs(std::move(read_configs)) - , m_write_configs(std::move(write_configs)) - , m_interactive(interactive) - { - } - void prefetch(const VcpkgPaths& paths, const Dependencies::ActionPlan& plan) override - { - if (m_read_sources.empty() && m_read_configs.empty()) return; - - auto& fs = paths.get_filesystem(); - - std::vector> nuget_refs; - - for (auto&& action : plan.install_actions) - { - if (action.build_options.editable == Build::Editable::YES) continue; - - auto& spec = action.spec; - fs.remove_all(paths.package_dir(spec), VCPKG_LINE_INFO); - - nuget_refs.emplace_back(spec, NugetReference(action)); - } - - if (nuget_refs.empty()) return; - - System::print2("Attempting to fetch ", nuget_refs.size(), " packages from nuget.\n"); - - auto packages_config = paths.buildtrees / fs::u8path("packages.config"); - - auto generate_packages_config = [&] { - XmlSerializer xml; - xml.emit_declaration().line_break(); - xml.open_tag("packages").line_break(); - - for (auto&& nuget_ref : nuget_refs) - xml.start_complex_open_tag("package") - .text_attr("id", nuget_ref.second.id) - .text_attr("version", nuget_ref.second.version) - .finish_self_closing_complex_tag() - .line_break(); - - xml.close_tag("packages").line_break(); - paths.get_filesystem().write_contents(packages_config, xml.buf, VCPKG_LINE_INFO); - }; - - const auto& nuget_exe = paths.get_tool_exe("nuget"); - std::vector cmdlines; - - if (!m_read_sources.empty()) - { - // First check using all sources - System::CmdLineBuilder cmdline; -#ifndef _WIN32 - cmdline.path_arg(paths.get_tool_exe(Tools::MONO)); -#endif - cmdline.path_arg(nuget_exe) - .string_arg("install") - .path_arg(packages_config) - .string_arg("-OutputDirectory") - .path_arg(paths.packages) - .string_arg("-Source") - .string_arg(Strings::join(";", m_read_sources)) - .string_arg("-ExcludeVersion") - .string_arg("-NoCache") - .string_arg("-PreRelease") - .string_arg("-DirectDownload") - .string_arg("-PackageSaveMode") - .string_arg("nupkg") - .string_arg("-Verbosity") - .string_arg("detailed") - .string_arg("-ForceEnglishOutput"); - if (!m_interactive) cmdline.string_arg("-NonInteractive"); - cmdlines.push_back(cmdline.extract()); - } - for (auto&& cfg : m_read_configs) - { - // Then check using each config - System::CmdLineBuilder cmdline; -#ifndef _WIN32 - cmdline.path_arg(paths.get_tool_exe(Tools::MONO)); -#endif - cmdline.path_arg(nuget_exe) - .string_arg("install") - .path_arg(packages_config) - .string_arg("-OutputDirectory") - .path_arg(paths.packages) - .string_arg("-ConfigFile") - .path_arg(cfg) - .string_arg("-ExcludeVersion") - .string_arg("-NoCache") - .string_arg("-PreRelease") - .string_arg("-DirectDownload") - .string_arg("-PackageSaveMode") - .string_arg("nupkg") - .string_arg("-Verbosity") - .string_arg("detailed") - .string_arg("-ForceEnglishOutput"); - if (!m_interactive) cmdline.string_arg("-NonInteractive"); - cmdlines.push_back(cmdline.extract()); - } - - size_t num_restored = 0; - - for (const auto& cmdline : cmdlines) - { - if (nuget_refs.empty()) break; - - [&] { - generate_packages_config(); - if (Debug::g_debugging) - System::cmd_execute(cmdline); - else - { - auto res = System::cmd_execute_and_capture_output(cmdline); - if (res.output.find("Authentication may require manual action.") != std::string::npos) - { - System::print2( - System::Color::warning, - "One or more NuGet credential providers requested manual action. Add the binary " - "source 'interactive' to allow interactivity.\n"); - } - } - }(); - - Util::erase_remove_if(nuget_refs, [&](const std::pair& nuget_ref) -> bool { - auto nupkg_path = paths.package_dir(nuget_ref.first) / fs::u8path(nuget_ref.second.id + ".nupkg"); - if (fs.exists(nupkg_path, ignore_errors)) - { - fs.remove(nupkg_path, VCPKG_LINE_INFO); - Checks::check_exit(VCPKG_LINE_INFO, - !fs.exists(nupkg_path, ignore_errors), - "Unable to remove nupkg after restoring: %s", - nupkg_path.u8string()); - m_restored.emplace(nuget_ref.first); - ++num_restored; - return true; - } - else - { - return false; - } - }); - } - - System::print2("Restored ", num_restored, " packages. Use --debug for more information.\n"); - } - RestoreResult try_restore(const VcpkgPaths&, const Dependencies::InstallPlanAction& action) override - { - if (Util::Sets::contains(m_restored, action.spec)) - return RestoreResult::success; - else - return RestoreResult::missing; - } - void push_success(const VcpkgPaths& paths, const Dependencies::InstallPlanAction& action) override - { - if (m_write_sources.empty() && m_write_configs.empty()) return; - auto& spec = action.spec; - - NugetReference nuget_ref(action); - auto nuspec_path = paths.buildtrees / spec.name() / (spec.triplet().to_string() + ".nuspec"); - paths.get_filesystem().write_contents( - nuspec_path, generate_nuspec(paths, action, nuget_ref), VCPKG_LINE_INFO); - - const auto& nuget_exe = paths.get_tool_exe("nuget"); - System::CmdLineBuilder cmdline; -#ifndef _WIN32 - cmdline.path_arg(paths.get_tool_exe(Tools::MONO)); -#endif - cmdline.path_arg(nuget_exe) - .string_arg("pack") - .path_arg(nuspec_path) - .string_arg("-OutputDirectory") - .path_arg(paths.buildtrees) - .string_arg("-NoDefaultExcludes") - .string_arg("-ForceEnglishOutput"); - if (!m_interactive) cmdline.string_arg("-NonInteractive"); - - auto pack_rc = [&] { - if (Debug::g_debugging) - return System::cmd_execute(cmdline); - else - return System::cmd_execute_and_capture_output(cmdline).exit_code; - }(); - - if (pack_rc != 0) - { - System::print2(System::Color::error, "Packing NuGet failed. Use --debug for more information.\n"); - } - else - { - auto nupkg_path = paths.buildtrees / nuget_ref.nupkg_filename(); - for (auto&& write_src : m_write_sources) - { - System::CmdLineBuilder cmd; -#ifndef _WIN32 - cmd.path_arg(paths.get_tool_exe(Tools::MONO)); -#endif - cmd.path_arg(nuget_exe) - .string_arg("push") - .path_arg(nupkg_path) - .string_arg("-ApiKey") - .string_arg("AzureDevOps") - .string_arg("-ForceEnglishOutput") - .string_arg("-Source") - .string_arg(write_src); - if (!m_interactive) cmd.string_arg("-NonInteractive"); - - System::print2("Uploading binaries for ", spec, " to NuGet source ", write_src, ".\n"); - - auto rc = [&] { - if (Debug::g_debugging) - return System::cmd_execute(cmd); - else - return System::cmd_execute_and_capture_output(cmd).exit_code; - }(); - - if (rc != 0) - { - System::print2(System::Color::error, - "Pushing NuGet to ", - write_src, - " failed. Use --debug for more information.\n"); - } - } - for (auto&& write_cfg : m_write_configs) - { - System::CmdLineBuilder cmd; -#ifndef _WIN32 - cmd.path_arg(paths.get_tool_exe(Tools::MONO)); -#endif - cmd.path_arg(nuget_exe) - .string_arg("push") - .path_arg(nupkg_path) - .string_arg("-ApiKey") - .string_arg("AzureDevOps") - .string_arg("-ForceEnglishOutput") - .string_arg("-ConfigFile") - .path_arg(write_cfg); - if (!m_interactive) cmd.string_arg("-NonInteractive"); - - System::print2( - "Uploading binaries for ", spec, " using NuGet config ", write_cfg.u8string(), ".\n"); - - auto rc = [&] { - if (Debug::g_debugging) - return System::cmd_execute(cmd); - else - return System::cmd_execute_and_capture_output(cmd).exit_code; - }(); - - if (rc != 0) - { - System::print2(System::Color::error, - "Pushing NuGet with ", - write_cfg.u8string(), - " failed. Use --debug for more information.\n"); - } - } - paths.get_filesystem().remove(nupkg_path, ignore_errors); - } - } - RestoreResult precheck(const VcpkgPaths&, const Dependencies::InstallPlanAction&) override - { - return RestoreResult::missing; - } - - private: - std::vector m_read_sources; - std::vector m_write_sources; - - std::vector m_read_configs; - std::vector m_write_configs; - - std::set m_restored; - bool m_interactive; - }; - - struct MergeBinaryProviders : IBinaryProvider - { - explicit MergeBinaryProviders(std::vector>&& providers) - : m_providers(std::move(providers)) - { - } - - void prefetch(const VcpkgPaths& paths, const Dependencies::ActionPlan& plan) override - { - for (auto&& provider : m_providers) - { - provider->prefetch(paths, plan); - } - } - RestoreResult try_restore(const VcpkgPaths& paths, const Dependencies::InstallPlanAction& action) override - { - for (auto&& provider : m_providers) - { - auto result = provider->try_restore(paths, action); - switch (result) - { - case RestoreResult::build_failed: - case RestoreResult::success: return result; - case RestoreResult::missing: continue; - default: Checks::unreachable(VCPKG_LINE_INFO); - } - } - return RestoreResult::missing; - } - void push_success(const VcpkgPaths& paths, const Dependencies::InstallPlanAction& action) override - { - for (auto&& provider : m_providers) - { - provider->push_success(paths, action); - } - } - RestoreResult precheck(const VcpkgPaths& paths, const Dependencies::InstallPlanAction& action) override - { - for (auto&& provider : m_providers) - { - auto result = provider->precheck(paths, action); - switch (result) - { - case RestoreResult::build_failed: - case RestoreResult::success: return result; - case RestoreResult::missing: continue; - default: Checks::unreachable(VCPKG_LINE_INFO); - } - } - return RestoreResult::missing; - } - - private: - std::vector> m_providers; - }; - - struct NullBinaryProvider : IBinaryProvider - { - void prefetch(const VcpkgPaths&, const Dependencies::ActionPlan&) override { } - RestoreResult try_restore(const VcpkgPaths&, const Dependencies::InstallPlanAction&) override - { - return RestoreResult::missing; - } - void push_success(const VcpkgPaths&, const Dependencies::InstallPlanAction&) override { } - RestoreResult precheck(const VcpkgPaths&, const Dependencies::InstallPlanAction&) override - { - return RestoreResult::missing; - } - }; -} - -XmlSerializer& XmlSerializer::emit_declaration() -{ - buf.append(R"()"); - return *this; -} -XmlSerializer& XmlSerializer::open_tag(StringLiteral sl) -{ - emit_pending_indent(); - Strings::append(buf, '<', sl, '>'); - m_indent += 2; - return *this; -} -XmlSerializer& XmlSerializer::start_complex_open_tag(StringLiteral sl) -{ - emit_pending_indent(); - Strings::append(buf, '<', sl); - m_indent += 2; - return *this; -} -XmlSerializer& XmlSerializer::text_attr(StringLiteral name, StringView content) -{ - if (m_pending_indent) - { - m_pending_indent = false; - buf.append(m_indent, ' '); - } - else - { - buf.push_back(' '); - } - Strings::append(buf, name, "=\""); - text(content); - Strings::append(buf, '"'); - return *this; -} -XmlSerializer& XmlSerializer::finish_complex_open_tag() -{ - emit_pending_indent(); - Strings::append(buf, '>'); - return *this; -} -XmlSerializer& XmlSerializer::finish_self_closing_complex_tag() -{ - emit_pending_indent(); - Strings::append(buf, "/>"); - m_indent -= 2; - return *this; -} -XmlSerializer& XmlSerializer::close_tag(StringLiteral sl) -{ - m_indent -= 2; - emit_pending_indent(); - Strings::append(buf, "'); - return *this; -} -XmlSerializer& XmlSerializer::text(StringView sv) -{ - emit_pending_indent(); - for (auto ch : sv) - { - if (ch == '&') - { - buf.append("&"); - } - else if (ch == '<') - { - buf.append("<"); - } - else if (ch == '>') - { - buf.append(">"); - } - else if (ch == '"') - { - buf.append("""); - } - else if (ch == '\'') - { - buf.append("'"); - } - else - { - buf.push_back(ch); - } - } - return *this; -} -XmlSerializer& XmlSerializer::simple_tag(StringLiteral tag, StringView content) -{ - return emit_pending_indent().open_tag(tag).text(content).close_tag(tag); -} -XmlSerializer& XmlSerializer::line_break() -{ - buf.push_back('\n'); - m_pending_indent = true; - return *this; -} -XmlSerializer& XmlSerializer::emit_pending_indent() -{ - if (m_pending_indent) - { - m_pending_indent = false; - buf.append(m_indent, ' '); - } - return *this; -} - -IBinaryProvider& vcpkg::null_binary_provider() -{ - static NullBinaryProvider p; - return p; -} - -ExpectedS> vcpkg::create_binary_provider_from_configs(View args) -{ - std::string env_string = System::get_environment_variable("VCPKG_BINARY_SOURCES").value_or(""); - - return create_binary_provider_from_configs_pure(env_string, args); -} -namespace -{ - const ExpectedS& default_cache_path() - { - static auto cachepath = System::get_platform_cache_home().then([](fs::path p) -> ExpectedS { - p /= fs::u8path("vcpkg/archives"); - if (p.is_absolute()) - { - return {std::move(p), expected_left_tag}; - } - else - { - return {"default path was not absolute: " + p.u8string(), expected_right_tag}; - } - }); - return cachepath; - } - - struct State - { - bool m_cleared = false; - bool interactive = false; - - std::vector archives_to_read; - std::vector archives_to_write; - - std::vector sources_to_read; - std::vector sources_to_write; - - std::vector configs_to_read; - std::vector configs_to_write; - - void clear() - { - m_cleared = true; - interactive = false; - archives_to_read.clear(); - archives_to_write.clear(); - sources_to_read.clear(); - sources_to_write.clear(); - configs_to_read.clear(); - configs_to_write.clear(); - } - }; - - struct BinaryConfigParser : Parse::ParserBase - { - BinaryConfigParser(StringView text, StringView origin, State* state) - : Parse::ParserBase(text, origin), state(state) - { - } - - State* state; - - void parse() - { - while (!at_eof()) - { - std::vector> segments; - - for (;;) - { - SourceLoc loc = cur_loc(); - std::string segment; - for (;;) - { - auto n = match_until([](char32_t ch) { return ch == ',' || ch == '`' || ch == ';'; }); - Strings::append(segment, n); - auto ch = cur(); - if (ch == Unicode::end_of_file || ch == ',' || ch == ';') - break; - else if (ch == '`') - { - ch = next(); - if (ch == Unicode::end_of_file) - add_error("unexpected eof: trailing unescaped backticks (`) are not allowed"); - else - Unicode::utf8_append_code_point(segment, ch); - next(); - } - else - Checks::unreachable(VCPKG_LINE_INFO); - } - segments.emplace_back(std::move(loc), std::move(segment)); - - auto ch = cur(); - if (ch == Unicode::end_of_file || ch == ';') - break; - else if (ch == ',') - { - next(); - continue; - } - else - Checks::unreachable(VCPKG_LINE_INFO); - } - - if (segments.size() != 1 || !segments[0].second.empty()) handle_segments(std::move(segments)); - segments.clear(); - if (get_error()) return; - if (cur() == ';') next(); - } - } - - template - void handle_readwrite(std::vector& read, - std::vector& write, - T&& t, - const std::vector>& segments, - size_t segment_idx) - { - if (segment_idx >= segments.size()) - { - read.push_back(std::move(t)); - return; - } - - auto& mode = segments[segment_idx].second; - - if (mode == "read") - { - read.push_back(std::move(t)); - } - else if (mode == "write") - { - write.push_back(std::move(t)); - } - else if (mode == "readwrite") - { - read.push_back(t); - write.push_back(std::move(t)); - } - else - { - return add_error("unexpected argument: expected 'read', readwrite', or 'write'", - segments[segment_idx].first); - } - } - - void handle_segments(std::vector>&& segments) - { - if (segments.empty()) return; - if (segments[0].second == "clear") - { - if (segments.size() != 1) - return add_error("unexpected arguments: binary config 'clear' does not take arguments", - segments[1].first); - state->clear(); - } - else if (segments[0].second == "files") - { - if (segments.size() < 2) - { - return add_error("expected arguments: binary config 'files' requires at least a path argument", - segments[0].first); - } - - auto p = fs::u8path(segments[1].second); - if (!p.is_absolute()) - { - return add_error("expected arguments: path arguments for binary config strings must be absolute", - segments[1].first); - } - handle_readwrite(state->archives_to_read, state->archives_to_write, std::move(p), segments, 2); - if (segments.size() > 3) - return add_error("unexpected arguments: binary config 'files' requires 1 or 2 arguments", - segments[3].first); - } - else if (segments[0].second == "interactive") - { - if (segments.size() > 1) - return add_error("unexpected arguments: binary config 'interactive' does not accept any arguments", - segments[1].first); - state->interactive = true; - } - else if (segments[0].second == "nugetconfig") - { - if (segments.size() < 2) - return add_error( - "expected arguments: binary config 'nugetconfig' requires at least a source argument", - segments[0].first); - - auto p = fs::u8path(segments[1].second); - if (!p.is_absolute()) - return add_error("expected arguments: path arguments for binary config strings must be absolute", - segments[1].first); - handle_readwrite(state->configs_to_read, state->configs_to_write, std::move(p), segments, 2); - if (segments.size() > 3) - return add_error("unexpected arguments: binary config 'nugetconfig' requires 1 or 2 arguments", - segments[3].first); - } - else if (segments[0].second == "nuget") - { - if (segments.size() < 2) - return add_error("expected arguments: binary config 'nuget' requires at least a source argument", - segments[0].first); - - auto&& p = segments[1].second; - if (p.empty()) - return add_error("unexpected arguments: binary config 'nuget' requires non-empty source"); - - handle_readwrite(state->sources_to_read, state->sources_to_write, std::move(p), segments, 2); - if (segments.size() > 3) - return add_error("unexpected arguments: binary config 'nuget' requires 1 or 2 arguments", - segments[3].first); - } - else if (segments[0].second == "default") - { - if (segments.size() > 2) - { - return add_error("unexpected arguments: binary config 'default' does not take more than 1 argument", - segments[0].first); - } - - const auto& maybe_home = default_cache_path(); - if (!maybe_home.has_value()) return add_error(maybe_home.error(), segments[0].first); - - handle_readwrite( - state->archives_to_read, state->archives_to_write, fs::path(*maybe_home.get()), segments, 1); - } - else - { - return add_error( - "unknown binary provider type: valid providers are 'clear', 'default', 'nuget', 'nugetconfig', " - "'interactive', and 'files'", - segments[0].first); - } - } - }; -} - -ExpectedS> vcpkg::create_binary_provider_from_configs_pure( - const std::string& env_string, View args) -{ - State s; - - BinaryConfigParser default_parser("default,readwrite", "", &s); - default_parser.parse(); - - BinaryConfigParser env_parser(env_string, "VCPKG_BINARY_SOURCES", &s); - env_parser.parse(); - if (auto err = env_parser.get_error()) return err->format(); - for (auto&& arg : args) - { - BinaryConfigParser arg_parser(arg, "", &s); - arg_parser.parse(); - if (auto err = arg_parser.get_error()) return err->format(); - } - - std::vector> providers; - if (!s.archives_to_read.empty() || !s.archives_to_write.empty()) - providers.push_back( - std::make_unique(std::move(s.archives_to_read), std::move(s.archives_to_write))); - if (!s.sources_to_read.empty() || !s.sources_to_write.empty() || !s.configs_to_read.empty() || - !s.configs_to_write.empty()) - providers.push_back(std::make_unique(std::move(s.sources_to_read), - std::move(s.sources_to_write), - std::move(s.configs_to_read), - std::move(s.configs_to_write), - s.interactive)); - - return {std::make_unique(std::move(providers))}; -} - -std::string vcpkg::reformat_version(const std::string& version, const std::string& abi_tag) -{ - static const std::regex semver_matcher(R"(v?(\d+)(\.\d+|$)(\.\d+)?.*)"); - - std::smatch sm; - if (std::regex_match(version.cbegin(), version.cend(), sm, semver_matcher)) - { - auto major = trim_leading_zeroes(sm.str(1)); - auto minor = sm.size() > 2 && !sm.str(2).empty() ? trim_leading_zeroes(sm.str(2).substr(1)) : "0"; - auto patch = sm.size() > 3 && !sm.str(3).empty() ? trim_leading_zeroes(sm.str(3).substr(1)) : "0"; - return Strings::concat(major, '.', minor, '.', patch, "-", abi_tag); - } - - static const std::regex date_matcher(R"((\d\d\d\d)-(\d\d)-(\d\d).*)"); - if (std::regex_match(version.cbegin(), version.cend(), sm, date_matcher)) - { - return Strings::concat(trim_leading_zeroes(sm.str(1)), - '.', - trim_leading_zeroes(sm.str(2)), - '.', - trim_leading_zeroes(sm.str(3)), - "-", - abi_tag); - } - - return Strings::concat("0.0.0-", abi_tag); -} - -std::string vcpkg::generate_nuspec(const VcpkgPaths& paths, - const Dependencies::InstallPlanAction& action, - const vcpkg::NugetReference& ref) -{ - auto& spec = action.spec; - auto& scf = *action.source_control_file_location.value_or_exit(VCPKG_LINE_INFO).source_control_file; - auto& version = scf.core_paragraph->version; - std::string description = - Strings::concat("NOT FOR DIRECT USE. Automatically generated cache package.\n\n", - Strings::join("\n ", scf.core_paragraph->description), - "\n\nVersion: ", - version, - "\nTriplet/Compiler hash: ", - action.abi_info.value_or_exit(VCPKG_LINE_INFO).triplet_abi.value_or_exit(VCPKG_LINE_INFO), - "\nFeatures:", - Strings::join(",", action.feature_list, [](const std::string& s) { return " " + s; }), - "\nDependencies:\n"); - - for (auto&& dep : action.package_dependencies) - { - Strings::append(description, " ", dep.name(), '\n'); - } - XmlSerializer xml; - xml.open_tag("package").line_break(); - xml.open_tag("metadata").line_break(); - xml.simple_tag("id", ref.id).line_break(); - xml.simple_tag("version", ref.version).line_break(); - if (!scf.core_paragraph->homepage.empty()) xml.simple_tag("projectUrl", scf.core_paragraph->homepage); - xml.simple_tag("authors", "vcpkg").line_break(); - xml.simple_tag("description", description).line_break(); - xml.open_tag("packageTypes"); - xml.start_complex_open_tag("packageType").text_attr("name", "vcpkg").finish_self_closing_complex_tag(); - xml.close_tag("packageTypes").line_break(); - xml.close_tag("metadata").line_break(); - xml.open_tag("files"); - xml.start_complex_open_tag("file") - .text_attr("src", (paths.package_dir(spec) / fs::u8path("**")).u8string()) - .text_attr("target", "") - .finish_self_closing_complex_tag(); - xml.close_tag("files").line_break(); - xml.close_tag("package").line_break(); - return std::move(xml.buf); -} - -void vcpkg::help_topic_binary_caching(const VcpkgPaths&) -{ - HelpTableFormatter tbl; - tbl.text("Vcpkg can cache compiled packages to accelerate restoration on a single machine or across the network." - " This functionality is currently enabled by default and can be disabled by either passing " - "`--no-binarycaching` to every vcpkg command line or setting the environment variable " - "`VCPKG_FEATURE_FLAGS` to `-binarycaching`."); - tbl.blank(); - tbl.blank(); - tbl.text( - "Once caching is enabled, it can be further configured by either passing `--binarysource=` options " - "to every command line or setting the `VCPKG_BINARY_SOURCES` environment variable to a set of sources (Ex: " - "\";;...\"). Command line sources are interpreted after environment sources."); - tbl.blank(); - tbl.blank(); - tbl.header("Valid source strings"); - tbl.format("clear", "Removes all previous sources"); - tbl.format("default[,]", "Adds the default file-based location."); - tbl.format("files,[,]", "Adds a custom file-based location."); - tbl.format("nuget,[,]", - "Adds a NuGet-based source; equivalent to the `-Source` parameter of the NuGet CLI."); - tbl.format("nugetconfig,[,]", - "Adds a NuGet-config-file-based source; equivalent to the `-Config` parameter of the NuGet CLI. This " - "config should specify `defaultPushSource` for uploads."); - tbl.format("interactive", "Enables interactive credential management for some source types"); - tbl.blank(); - tbl.text("The `` optional parameter for certain strings controls whether they will be consulted for " - "downloading binaries and whether on-demand builds will be uploaded to that remote. It can be specified " - "as 'read', 'write', or 'readwrite'."); - tbl.blank(); - System::print2(tbl.m_str); - const auto& maybe_cachepath = default_cache_path(); - if (auto p = maybe_cachepath.get()) - { - auto p_preferred = *p; - System::print2( - "\nBased on your system settings, the default path to store binaries is\n ", - p_preferred.make_preferred().u8string(), - "\n\nThis consults %LOCALAPPDATA%/%APPDATA% on Windows and $XDG_CACHE_HOME or $HOME on other platforms."); - } -} - -std::string vcpkg::generate_nuget_packages_config(const Dependencies::ActionPlan& action) -{ - auto refs = Util::fmap(action.install_actions, - [&](const Dependencies::InstallPlanAction& ipa) { return NugetReference(ipa); }); - XmlSerializer xml; - xml.emit_declaration().line_break(); - xml.open_tag("packages").line_break(); - for (auto&& ref : refs) - { - xml.start_complex_open_tag("package") - .text_attr("id", ref.id) - .text_attr("version", ref.version) - .finish_self_closing_complex_tag() - .line_break(); - } - xml.close_tag("packages").line_break(); - return std::move(xml.buf); -} +#include "pch.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +using namespace vcpkg; + +namespace +{ + static System::ExitCodeAndOutput decompress_archive(const VcpkgPaths& paths, + const PackageSpec& spec, + const fs::path& archive_path) + { + auto& fs = paths.get_filesystem(); + + auto pkg_path = paths.package_dir(spec); + fs.remove_all(pkg_path, VCPKG_LINE_INFO); + std::error_code ec; + fs.create_directories(pkg_path, ec); + auto files = fs.get_files_non_recursive(pkg_path); + Checks::check_exit(VCPKG_LINE_INFO, files.empty(), "unable to clear path: %s", pkg_path.u8string()); + +#if defined(_WIN32) + auto&& seven_zip_exe = paths.get_tool_exe(Tools::SEVEN_ZIP); + auto cmd = Strings::format( + R"("%s" x "%s" -o"%s" -y)", seven_zip_exe.u8string(), archive_path.u8string(), pkg_path.u8string()); +#else + auto cmd = Strings::format(R"(unzip -qq "%s" "-d%s")", archive_path.u8string(), pkg_path.u8string()); +#endif + return System::cmd_execute_and_capture_output(cmd, System::get_clean_environment()); + } + + // Compress the source directory into the destination file. + static void compress_directory(const VcpkgPaths& paths, const fs::path& source, const fs::path& destination) + { + auto& fs = paths.get_filesystem(); + + std::error_code ec; + + fs.remove(destination, ec); + Checks::check_exit( + VCPKG_LINE_INFO, !fs.exists(destination), "Could not remove file: %s", destination.u8string()); +#if defined(_WIN32) + auto&& seven_zip_exe = paths.get_tool_exe(Tools::SEVEN_ZIP); + + System::cmd_execute_and_capture_output( + Strings::format( + R"("%s" a "%s" "%s\*")", seven_zip_exe.u8string(), destination.u8string(), source.u8string()), + System::get_clean_environment()); +#else + System::cmd_execute_clean( + Strings::format(R"(cd '%s' && zip --quiet -r '%s' *)", source.u8string(), destination.u8string())); +#endif + } + + struct ArchivesBinaryProvider : IBinaryProvider + { + ArchivesBinaryProvider(std::vector&& read_dirs, std::vector&& write_dirs) + : m_read_dirs(std::move(read_dirs)), m_write_dirs(std::move(write_dirs)) + { + } + ~ArchivesBinaryProvider() = default; + void prefetch(const VcpkgPaths&, const Dependencies::ActionPlan&) override { } + RestoreResult try_restore(const VcpkgPaths& paths, const Dependencies::InstallPlanAction& action) override + { + const auto& abi_tag = action.abi_info.value_or_exit(VCPKG_LINE_INFO).package_abi; + auto& spec = action.spec; + auto& fs = paths.get_filesystem(); + std::error_code ec; + for (auto&& archives_root_dir : m_read_dirs) + { + const std::string archive_name = abi_tag + ".zip"; + const fs::path archive_subpath = fs::u8path(abi_tag.substr(0, 2)) / archive_name; + const fs::path archive_path = archives_root_dir / archive_subpath; + if (fs.exists(archive_path)) + { + System::print2("Using cached binary package: ", archive_path.u8string(), "\n"); + + int archive_result = decompress_archive(paths, spec, archive_path).exit_code; + + if (archive_result == 0) + { + return RestoreResult::success; + } + else + { + System::print2("Failed to decompress archive package\n"); + if (action.build_options.purge_decompress_failure == Build::PurgeDecompressFailure::NO) + { + return RestoreResult::build_failed; + } + else + { + System::print2("Purging bad archive\n"); + fs.remove(archive_path, ec); + } + } + } + + System::printf("Could not locate cached archive: %s\n", archive_path.u8string()); + } + + return RestoreResult::missing; + } + void push_success(const VcpkgPaths& paths, const Dependencies::InstallPlanAction& action) override + { + if (m_write_dirs.empty()) return; + const auto& abi_tag = action.abi_info.value_or_exit(VCPKG_LINE_INFO).package_abi; + auto& spec = action.spec; + auto& fs = paths.get_filesystem(); + const auto tmp_archive_path = paths.buildtrees / spec.name() / (spec.triplet().to_string() + ".zip"); + compress_directory(paths, paths.package_dir(spec), tmp_archive_path); + + for (auto&& m_directory : m_write_dirs) + { + const fs::path& archives_root_dir = m_directory; + const std::string archive_name = abi_tag + ".zip"; + const fs::path archive_subpath = fs::u8path(abi_tag.substr(0, 2)) / archive_name; + const fs::path archive_path = archives_root_dir / archive_subpath; + + fs.create_directories(archive_path.parent_path(), ignore_errors); + std::error_code ec; + if (m_write_dirs.size() > 1) + fs.copy_file(tmp_archive_path, archive_path, fs::copy_options::overwrite_existing, ec); + else + fs.rename_or_copy(tmp_archive_path, archive_path, ".tmp", ec); + if (ec) + { + System::printf(System::Color::warning, + "Failed to store binary cache %s: %s\n", + archive_path.u8string(), + ec.message()); + } + else + System::printf("Stored binary cache: %s\n", archive_path.u8string()); + } + if (m_write_dirs.size() > 1) fs.remove(tmp_archive_path, ignore_errors); + } + RestoreResult precheck(const VcpkgPaths& paths, const Dependencies::InstallPlanAction& action) override + { + const auto& abi_tag = action.abi_info.value_or_exit(VCPKG_LINE_INFO).package_abi; + auto& fs = paths.get_filesystem(); + std::error_code ec; + for (auto&& archives_root_dir : m_read_dirs) + { + const std::string archive_name = abi_tag + ".zip"; + const fs::path archive_subpath = fs::u8path(abi_tag.substr(0, 2)) / archive_name; + const fs::path archive_path = archives_root_dir / archive_subpath; + + if (fs.exists(archive_path)) + { + return RestoreResult::success; + } + } + return RestoreResult::missing; + } + + private: + std::vector m_read_dirs, m_write_dirs; + }; + + static std::string trim_leading_zeroes(std::string v) + { + auto n = v.find_first_not_of('0'); + if (n == std::string::npos) + { + v = "0"; + } + else if (n > 0) + { + v.erase(0, n); + } + return v; + } + + struct NugetBinaryProvider : IBinaryProvider + { + NugetBinaryProvider(std::vector&& read_sources, + std::vector&& write_sources, + std::vector&& read_configs, + std::vector&& write_configs, + bool interactive) + : m_read_sources(std::move(read_sources)) + , m_write_sources(std::move(write_sources)) + , m_read_configs(std::move(read_configs)) + , m_write_configs(std::move(write_configs)) + , m_interactive(interactive) + { + } + void prefetch(const VcpkgPaths& paths, const Dependencies::ActionPlan& plan) override + { + if (m_read_sources.empty() && m_read_configs.empty()) return; + + auto& fs = paths.get_filesystem(); + + std::vector> nuget_refs; + + for (auto&& action : plan.install_actions) + { + if (action.build_options.editable == Build::Editable::YES) continue; + + auto& spec = action.spec; + fs.remove_all(paths.package_dir(spec), VCPKG_LINE_INFO); + + nuget_refs.emplace_back(spec, NugetReference(action)); + } + + if (nuget_refs.empty()) return; + + System::print2("Attempting to fetch ", nuget_refs.size(), " packages from nuget.\n"); + + auto packages_config = paths.buildtrees / fs::u8path("packages.config"); + + auto generate_packages_config = [&] { + XmlSerializer xml; + xml.emit_declaration().line_break(); + xml.open_tag("packages").line_break(); + + for (auto&& nuget_ref : nuget_refs) + xml.start_complex_open_tag("package") + .text_attr("id", nuget_ref.second.id) + .text_attr("version", nuget_ref.second.version) + .finish_self_closing_complex_tag() + .line_break(); + + xml.close_tag("packages").line_break(); + paths.get_filesystem().write_contents(packages_config, xml.buf, VCPKG_LINE_INFO); + }; + + const auto& nuget_exe = paths.get_tool_exe("nuget"); + std::vector cmdlines; + + if (!m_read_sources.empty()) + { + // First check using all sources + System::CmdLineBuilder cmdline; +#ifndef _WIN32 + cmdline.path_arg(paths.get_tool_exe(Tools::MONO)); +#endif + cmdline.path_arg(nuget_exe) + .string_arg("install") + .path_arg(packages_config) + .string_arg("-OutputDirectory") + .path_arg(paths.packages) + .string_arg("-Source") + .string_arg(Strings::join(";", m_read_sources)) + .string_arg("-ExcludeVersion") + .string_arg("-NoCache") + .string_arg("-PreRelease") + .string_arg("-DirectDownload") + .string_arg("-PackageSaveMode") + .string_arg("nupkg") + .string_arg("-Verbosity") + .string_arg("detailed") + .string_arg("-ForceEnglishOutput"); + if (!m_interactive) cmdline.string_arg("-NonInteractive"); + cmdlines.push_back(cmdline.extract()); + } + for (auto&& cfg : m_read_configs) + { + // Then check using each config + System::CmdLineBuilder cmdline; +#ifndef _WIN32 + cmdline.path_arg(paths.get_tool_exe(Tools::MONO)); +#endif + cmdline.path_arg(nuget_exe) + .string_arg("install") + .path_arg(packages_config) + .string_arg("-OutputDirectory") + .path_arg(paths.packages) + .string_arg("-ConfigFile") + .path_arg(cfg) + .string_arg("-ExcludeVersion") + .string_arg("-NoCache") + .string_arg("-PreRelease") + .string_arg("-DirectDownload") + .string_arg("-PackageSaveMode") + .string_arg("nupkg") + .string_arg("-Verbosity") + .string_arg("detailed") + .string_arg("-ForceEnglishOutput"); + if (!m_interactive) cmdline.string_arg("-NonInteractive"); + cmdlines.push_back(cmdline.extract()); + } + + size_t num_restored = 0; + + for (const auto& cmdline : cmdlines) + { + if (nuget_refs.empty()) break; + + [&] { + generate_packages_config(); + if (Debug::g_debugging) + System::cmd_execute(cmdline); + else + { + auto res = System::cmd_execute_and_capture_output(cmdline); + if (res.output.find("Authentication may require manual action.") != std::string::npos) + { + System::print2( + System::Color::warning, + "One or more NuGet credential providers requested manual action. Add the binary " + "source 'interactive' to allow interactivity.\n"); + } + } + }(); + + Util::erase_remove_if(nuget_refs, [&](const std::pair& nuget_ref) -> bool { + auto nupkg_path = paths.package_dir(nuget_ref.first) / fs::u8path(nuget_ref.second.id + ".nupkg"); + if (fs.exists(nupkg_path, ignore_errors)) + { + fs.remove(nupkg_path, VCPKG_LINE_INFO); + Checks::check_exit(VCPKG_LINE_INFO, + !fs.exists(nupkg_path, ignore_errors), + "Unable to remove nupkg after restoring: %s", + nupkg_path.u8string()); + m_restored.emplace(nuget_ref.first); + ++num_restored; + return true; + } + else + { + return false; + } + }); + } + + System::print2("Restored ", num_restored, " packages. Use --debug for more information.\n"); + } + RestoreResult try_restore(const VcpkgPaths&, const Dependencies::InstallPlanAction& action) override + { + if (Util::Sets::contains(m_restored, action.spec)) + return RestoreResult::success; + else + return RestoreResult::missing; + } + void push_success(const VcpkgPaths& paths, const Dependencies::InstallPlanAction& action) override + { + if (m_write_sources.empty() && m_write_configs.empty()) return; + auto& spec = action.spec; + + NugetReference nuget_ref(action); + auto nuspec_path = paths.buildtrees / spec.name() / (spec.triplet().to_string() + ".nuspec"); + paths.get_filesystem().write_contents( + nuspec_path, generate_nuspec(paths, action, nuget_ref), VCPKG_LINE_INFO); + + const auto& nuget_exe = paths.get_tool_exe("nuget"); + System::CmdLineBuilder cmdline; +#ifndef _WIN32 + cmdline.path_arg(paths.get_tool_exe(Tools::MONO)); +#endif + cmdline.path_arg(nuget_exe) + .string_arg("pack") + .path_arg(nuspec_path) + .string_arg("-OutputDirectory") + .path_arg(paths.buildtrees) + .string_arg("-NoDefaultExcludes") + .string_arg("-ForceEnglishOutput"); + if (!m_interactive) cmdline.string_arg("-NonInteractive"); + + auto pack_rc = [&] { + if (Debug::g_debugging) + return System::cmd_execute(cmdline); + else + return System::cmd_execute_and_capture_output(cmdline).exit_code; + }(); + + if (pack_rc != 0) + { + System::print2(System::Color::error, "Packing NuGet failed. Use --debug for more information.\n"); + } + else + { + auto nupkg_path = paths.buildtrees / nuget_ref.nupkg_filename(); + for (auto&& write_src : m_write_sources) + { + System::CmdLineBuilder cmd; +#ifndef _WIN32 + cmd.path_arg(paths.get_tool_exe(Tools::MONO)); +#endif + cmd.path_arg(nuget_exe) + .string_arg("push") + .path_arg(nupkg_path) + .string_arg("-ApiKey") + .string_arg("AzureDevOps") + .string_arg("-ForceEnglishOutput") + .string_arg("-Source") + .string_arg(write_src); + if (!m_interactive) cmd.string_arg("-NonInteractive"); + + System::print2("Uploading binaries for ", spec, " to NuGet source ", write_src, ".\n"); + + auto rc = [&] { + if (Debug::g_debugging) + return System::cmd_execute(cmd); + else + return System::cmd_execute_and_capture_output(cmd).exit_code; + }(); + + if (rc != 0) + { + System::print2(System::Color::error, + "Pushing NuGet to ", + write_src, + " failed. Use --debug for more information.\n"); + } + } + for (auto&& write_cfg : m_write_configs) + { + System::CmdLineBuilder cmd; +#ifndef _WIN32 + cmd.path_arg(paths.get_tool_exe(Tools::MONO)); +#endif + cmd.path_arg(nuget_exe) + .string_arg("push") + .path_arg(nupkg_path) + .string_arg("-ApiKey") + .string_arg("AzureDevOps") + .string_arg("-ForceEnglishOutput") + .string_arg("-ConfigFile") + .path_arg(write_cfg); + if (!m_interactive) cmd.string_arg("-NonInteractive"); + + System::print2( + "Uploading binaries for ", spec, " using NuGet config ", write_cfg.u8string(), ".\n"); + + auto rc = [&] { + if (Debug::g_debugging) + return System::cmd_execute(cmd); + else + return System::cmd_execute_and_capture_output(cmd).exit_code; + }(); + + if (rc != 0) + { + System::print2(System::Color::error, + "Pushing NuGet with ", + write_cfg.u8string(), + " failed. Use --debug for more information.\n"); + } + } + paths.get_filesystem().remove(nupkg_path, ignore_errors); + } + } + RestoreResult precheck(const VcpkgPaths&, const Dependencies::InstallPlanAction&) override + { + return RestoreResult::missing; + } + + private: + std::vector m_read_sources; + std::vector m_write_sources; + + std::vector m_read_configs; + std::vector m_write_configs; + + std::set m_restored; + bool m_interactive; + }; + + struct MergeBinaryProviders : IBinaryProvider + { + explicit MergeBinaryProviders(std::vector>&& providers) + : m_providers(std::move(providers)) + { + } + + void prefetch(const VcpkgPaths& paths, const Dependencies::ActionPlan& plan) override + { + for (auto&& provider : m_providers) + { + provider->prefetch(paths, plan); + } + } + RestoreResult try_restore(const VcpkgPaths& paths, const Dependencies::InstallPlanAction& action) override + { + for (auto&& provider : m_providers) + { + auto result = provider->try_restore(paths, action); + switch (result) + { + case RestoreResult::build_failed: + case RestoreResult::success: return result; + case RestoreResult::missing: continue; + default: Checks::unreachable(VCPKG_LINE_INFO); + } + } + return RestoreResult::missing; + } + void push_success(const VcpkgPaths& paths, const Dependencies::InstallPlanAction& action) override + { + for (auto&& provider : m_providers) + { + provider->push_success(paths, action); + } + } + RestoreResult precheck(const VcpkgPaths& paths, const Dependencies::InstallPlanAction& action) override + { + for (auto&& provider : m_providers) + { + auto result = provider->precheck(paths, action); + switch (result) + { + case RestoreResult::build_failed: + case RestoreResult::success: return result; + case RestoreResult::missing: continue; + default: Checks::unreachable(VCPKG_LINE_INFO); + } + } + return RestoreResult::missing; + } + + private: + std::vector> m_providers; + }; + + struct NullBinaryProvider : IBinaryProvider + { + void prefetch(const VcpkgPaths&, const Dependencies::ActionPlan&) override { } + RestoreResult try_restore(const VcpkgPaths&, const Dependencies::InstallPlanAction&) override + { + return RestoreResult::missing; + } + void push_success(const VcpkgPaths&, const Dependencies::InstallPlanAction&) override { } + RestoreResult precheck(const VcpkgPaths&, const Dependencies::InstallPlanAction&) override + { + return RestoreResult::missing; + } + }; +} + +XmlSerializer& XmlSerializer::emit_declaration() +{ + buf.append(R"()"); + return *this; +} +XmlSerializer& XmlSerializer::open_tag(StringLiteral sl) +{ + emit_pending_indent(); + Strings::append(buf, '<', sl, '>'); + m_indent += 2; + return *this; +} +XmlSerializer& XmlSerializer::start_complex_open_tag(StringLiteral sl) +{ + emit_pending_indent(); + Strings::append(buf, '<', sl); + m_indent += 2; + return *this; +} +XmlSerializer& XmlSerializer::text_attr(StringLiteral name, StringView content) +{ + if (m_pending_indent) + { + m_pending_indent = false; + buf.append(m_indent, ' '); + } + else + { + buf.push_back(' '); + } + Strings::append(buf, name, "=\""); + text(content); + Strings::append(buf, '"'); + return *this; +} +XmlSerializer& XmlSerializer::finish_complex_open_tag() +{ + emit_pending_indent(); + Strings::append(buf, '>'); + return *this; +} +XmlSerializer& XmlSerializer::finish_self_closing_complex_tag() +{ + emit_pending_indent(); + Strings::append(buf, "/>"); + m_indent -= 2; + return *this; +} +XmlSerializer& XmlSerializer::close_tag(StringLiteral sl) +{ + m_indent -= 2; + emit_pending_indent(); + Strings::append(buf, "'); + return *this; +} +XmlSerializer& XmlSerializer::text(StringView sv) +{ + emit_pending_indent(); + for (auto ch : sv) + { + if (ch == '&') + { + buf.append("&"); + } + else if (ch == '<') + { + buf.append("<"); + } + else if (ch == '>') + { + buf.append(">"); + } + else if (ch == '"') + { + buf.append("""); + } + else if (ch == '\'') + { + buf.append("'"); + } + else + { + buf.push_back(ch); + } + } + return *this; +} +XmlSerializer& XmlSerializer::simple_tag(StringLiteral tag, StringView content) +{ + return emit_pending_indent().open_tag(tag).text(content).close_tag(tag); +} +XmlSerializer& XmlSerializer::line_break() +{ + buf.push_back('\n'); + m_pending_indent = true; + return *this; +} +XmlSerializer& XmlSerializer::emit_pending_indent() +{ + if (m_pending_indent) + { + m_pending_indent = false; + buf.append(m_indent, ' '); + } + return *this; +} + +IBinaryProvider& vcpkg::null_binary_provider() +{ + static NullBinaryProvider p; + return p; +} + +ExpectedS> vcpkg::create_binary_provider_from_configs(View args) +{ + std::string env_string = System::get_environment_variable("VCPKG_BINARY_SOURCES").value_or(""); + + return create_binary_provider_from_configs_pure(env_string, args); +} +namespace +{ + const ExpectedS& default_cache_path() + { + static auto cachepath = System::get_platform_cache_home().then([](fs::path p) -> ExpectedS { + p /= fs::u8path("vcpkg/archives"); + if (p.is_absolute()) + { + return {std::move(p), expected_left_tag}; + } + else + { + return {"default path was not absolute: " + p.u8string(), expected_right_tag}; + } + }); + return cachepath; + } + + struct State + { + bool m_cleared = false; + bool interactive = false; + + std::vector archives_to_read; + std::vector archives_to_write; + + std::vector sources_to_read; + std::vector sources_to_write; + + std::vector configs_to_read; + std::vector configs_to_write; + + void clear() + { + m_cleared = true; + interactive = false; + archives_to_read.clear(); + archives_to_write.clear(); + sources_to_read.clear(); + sources_to_write.clear(); + configs_to_read.clear(); + configs_to_write.clear(); + } + }; + + struct BinaryConfigParser : Parse::ParserBase + { + BinaryConfigParser(StringView text, StringView origin, State* state) + : Parse::ParserBase(text, origin), state(state) + { + } + + State* state; + + void parse() + { + while (!at_eof()) + { + std::vector> segments; + + for (;;) + { + SourceLoc loc = cur_loc(); + std::string segment; + for (;;) + { + auto n = match_until([](char32_t ch) { return ch == ',' || ch == '`' || ch == ';'; }); + Strings::append(segment, n); + auto ch = cur(); + if (ch == Unicode::end_of_file || ch == ',' || ch == ';') + break; + else if (ch == '`') + { + ch = next(); + if (ch == Unicode::end_of_file) + add_error("unexpected eof: trailing unescaped backticks (`) are not allowed"); + else + Unicode::utf8_append_code_point(segment, ch); + next(); + } + else + Checks::unreachable(VCPKG_LINE_INFO); + } + segments.emplace_back(std::move(loc), std::move(segment)); + + auto ch = cur(); + if (ch == Unicode::end_of_file || ch == ';') + break; + else if (ch == ',') + { + next(); + continue; + } + else + Checks::unreachable(VCPKG_LINE_INFO); + } + + if (segments.size() != 1 || !segments[0].second.empty()) handle_segments(std::move(segments)); + segments.clear(); + if (get_error()) return; + if (cur() == ';') next(); + } + } + + template + void handle_readwrite(std::vector& read, + std::vector& write, + T&& t, + const std::vector>& segments, + size_t segment_idx) + { + if (segment_idx >= segments.size()) + { + read.push_back(std::move(t)); + return; + } + + auto& mode = segments[segment_idx].second; + + if (mode == "read") + { + read.push_back(std::move(t)); + } + else if (mode == "write") + { + write.push_back(std::move(t)); + } + else if (mode == "readwrite") + { + read.push_back(t); + write.push_back(std::move(t)); + } + else + { + return add_error("unexpected argument: expected 'read', readwrite', or 'write'", + segments[segment_idx].first); + } + } + + void handle_segments(std::vector>&& segments) + { + if (segments.empty()) return; + if (segments[0].second == "clear") + { + if (segments.size() != 1) + return add_error("unexpected arguments: binary config 'clear' does not take arguments", + segments[1].first); + state->clear(); + } + else if (segments[0].second == "files") + { + if (segments.size() < 2) + { + return add_error("expected arguments: binary config 'files' requires at least a path argument", + segments[0].first); + } + + auto p = fs::u8path(segments[1].second); + if (!p.is_absolute()) + { + return add_error("expected arguments: path arguments for binary config strings must be absolute", + segments[1].first); + } + handle_readwrite(state->archives_to_read, state->archives_to_write, std::move(p), segments, 2); + if (segments.size() > 3) + return add_error("unexpected arguments: binary config 'files' requires 1 or 2 arguments", + segments[3].first); + } + else if (segments[0].second == "interactive") + { + if (segments.size() > 1) + return add_error("unexpected arguments: binary config 'interactive' does not accept any arguments", + segments[1].first); + state->interactive = true; + } + else if (segments[0].second == "nugetconfig") + { + if (segments.size() < 2) + return add_error( + "expected arguments: binary config 'nugetconfig' requires at least a source argument", + segments[0].first); + + auto p = fs::u8path(segments[1].second); + if (!p.is_absolute()) + return add_error("expected arguments: path arguments for binary config strings must be absolute", + segments[1].first); + handle_readwrite(state->configs_to_read, state->configs_to_write, std::move(p), segments, 2); + if (segments.size() > 3) + return add_error("unexpected arguments: binary config 'nugetconfig' requires 1 or 2 arguments", + segments[3].first); + } + else if (segments[0].second == "nuget") + { + if (segments.size() < 2) + return add_error("expected arguments: binary config 'nuget' requires at least a source argument", + segments[0].first); + + auto&& p = segments[1].second; + if (p.empty()) + return add_error("unexpected arguments: binary config 'nuget' requires non-empty source"); + + handle_readwrite(state->sources_to_read, state->sources_to_write, std::move(p), segments, 2); + if (segments.size() > 3) + return add_error("unexpected arguments: binary config 'nuget' requires 1 or 2 arguments", + segments[3].first); + } + else if (segments[0].second == "default") + { + if (segments.size() > 2) + { + return add_error("unexpected arguments: binary config 'default' does not take more than 1 argument", + segments[0].first); + } + + const auto& maybe_home = default_cache_path(); + if (!maybe_home.has_value()) return add_error(maybe_home.error(), segments[0].first); + + handle_readwrite( + state->archives_to_read, state->archives_to_write, fs::path(*maybe_home.get()), segments, 1); + } + else + { + return add_error( + "unknown binary provider type: valid providers are 'clear', 'default', 'nuget', 'nugetconfig', " + "'interactive', and 'files'", + segments[0].first); + } + } + }; +} + +ExpectedS> vcpkg::create_binary_provider_from_configs_pure( + const std::string& env_string, View args) +{ + State s; + + BinaryConfigParser default_parser("default,readwrite", "", &s); + default_parser.parse(); + + BinaryConfigParser env_parser(env_string, "VCPKG_BINARY_SOURCES", &s); + env_parser.parse(); + if (auto err = env_parser.get_error()) return err->format(); + for (auto&& arg : args) + { + BinaryConfigParser arg_parser(arg, "", &s); + arg_parser.parse(); + if (auto err = arg_parser.get_error()) return err->format(); + } + + std::vector> providers; + if (!s.archives_to_read.empty() || !s.archives_to_write.empty()) + providers.push_back( + std::make_unique(std::move(s.archives_to_read), std::move(s.archives_to_write))); + if (!s.sources_to_read.empty() || !s.sources_to_write.empty() || !s.configs_to_read.empty() || + !s.configs_to_write.empty()) + providers.push_back(std::make_unique(std::move(s.sources_to_read), + std::move(s.sources_to_write), + std::move(s.configs_to_read), + std::move(s.configs_to_write), + s.interactive)); + + return {std::make_unique(std::move(providers))}; +} + +std::string vcpkg::reformat_version(const std::string& version, const std::string& abi_tag) +{ + static const std::regex semver_matcher(R"(v?(\d+)(\.\d+|$)(\.\d+)?.*)"); + + std::smatch sm; + if (std::regex_match(version.cbegin(), version.cend(), sm, semver_matcher)) + { + auto major = trim_leading_zeroes(sm.str(1)); + auto minor = sm.size() > 2 && !sm.str(2).empty() ? trim_leading_zeroes(sm.str(2).substr(1)) : "0"; + auto patch = sm.size() > 3 && !sm.str(3).empty() ? trim_leading_zeroes(sm.str(3).substr(1)) : "0"; + return Strings::concat(major, '.', minor, '.', patch, "-", abi_tag); + } + + static const std::regex date_matcher(R"((\d\d\d\d)-(\d\d)-(\d\d).*)"); + if (std::regex_match(version.cbegin(), version.cend(), sm, date_matcher)) + { + return Strings::concat(trim_leading_zeroes(sm.str(1)), + '.', + trim_leading_zeroes(sm.str(2)), + '.', + trim_leading_zeroes(sm.str(3)), + "-", + abi_tag); + } + + return Strings::concat("0.0.0-", abi_tag); +} + +std::string vcpkg::generate_nuspec(const VcpkgPaths& paths, + const Dependencies::InstallPlanAction& action, + const vcpkg::NugetReference& ref) +{ + auto& spec = action.spec; + auto& scf = *action.source_control_file_location.value_or_exit(VCPKG_LINE_INFO).source_control_file; + auto& version = scf.core_paragraph->version; + std::string description = + Strings::concat("NOT FOR DIRECT USE. Automatically generated cache package.\n\n", + Strings::join("\n ", scf.core_paragraph->description), + "\n\nVersion: ", + version, + "\nTriplet/Compiler hash: ", + action.abi_info.value_or_exit(VCPKG_LINE_INFO).triplet_abi.value_or_exit(VCPKG_LINE_INFO), + "\nFeatures:", + Strings::join(",", action.feature_list, [](const std::string& s) { return " " + s; }), + "\nDependencies:\n"); + + for (auto&& dep : action.package_dependencies) + { + Strings::append(description, " ", dep.name(), '\n'); + } + XmlSerializer xml; + xml.open_tag("package").line_break(); + xml.open_tag("metadata").line_break(); + xml.simple_tag("id", ref.id).line_break(); + xml.simple_tag("version", ref.version).line_break(); + if (!scf.core_paragraph->homepage.empty()) xml.simple_tag("projectUrl", scf.core_paragraph->homepage); + xml.simple_tag("authors", "vcpkg").line_break(); + xml.simple_tag("description", description).line_break(); + xml.open_tag("packageTypes"); + xml.start_complex_open_tag("packageType").text_attr("name", "vcpkg").finish_self_closing_complex_tag(); + xml.close_tag("packageTypes").line_break(); + xml.close_tag("metadata").line_break(); + xml.open_tag("files"); + xml.start_complex_open_tag("file") + .text_attr("src", (paths.package_dir(spec) / fs::u8path("**")).u8string()) + .text_attr("target", "") + .finish_self_closing_complex_tag(); + xml.close_tag("files").line_break(); + xml.close_tag("package").line_break(); + return std::move(xml.buf); +} + +void vcpkg::help_topic_binary_caching(const VcpkgPaths&) +{ + HelpTableFormatter tbl; + tbl.text("Vcpkg can cache compiled packages to accelerate restoration on a single machine or across the network." + " This functionality is currently enabled by default and can be disabled by either passing " + "`--no-binarycaching` to every vcpkg command line or setting the environment variable " + "`VCPKG_FEATURE_FLAGS` to `-binarycaching`."); + tbl.blank(); + tbl.blank(); + tbl.text( + "Once caching is enabled, it can be further configured by either passing `--binarysource=` options " + "to every command line or setting the `VCPKG_BINARY_SOURCES` environment variable to a set of sources (Ex: " + "\";;...\"). Command line sources are interpreted after environment sources."); + tbl.blank(); + tbl.blank(); + tbl.header("Valid source strings"); + tbl.format("clear", "Removes all previous sources"); + tbl.format("default[,]", "Adds the default file-based location."); + tbl.format("files,[,]", "Adds a custom file-based location."); + tbl.format("nuget,[,]", + "Adds a NuGet-based source; equivalent to the `-Source` parameter of the NuGet CLI."); + tbl.format("nugetconfig,[,]", + "Adds a NuGet-config-file-based source; equivalent to the `-Config` parameter of the NuGet CLI. This " + "config should specify `defaultPushSource` for uploads."); + tbl.format("interactive", "Enables interactive credential management for some source types"); + tbl.blank(); + tbl.text("The `` optional parameter for certain strings controls whether they will be consulted for " + "downloading binaries and whether on-demand builds will be uploaded to that remote. It can be specified " + "as 'read', 'write', or 'readwrite'."); + tbl.blank(); + System::print2(tbl.m_str); + const auto& maybe_cachepath = default_cache_path(); + if (auto p = maybe_cachepath.get()) + { + auto p_preferred = *p; + System::print2( + "\nBased on your system settings, the default path to store binaries is\n ", + p_preferred.make_preferred().u8string(), + "\n\nThis consults %LOCALAPPDATA%/%APPDATA% on Windows and $XDG_CACHE_HOME or $HOME on other platforms."); + } +} + +std::string vcpkg::generate_nuget_packages_config(const Dependencies::ActionPlan& action) +{ + auto refs = Util::fmap(action.install_actions, + [&](const Dependencies::InstallPlanAction& ipa) { return NugetReference(ipa); }); + XmlSerializer xml; + xml.emit_declaration().line_break(); + xml.open_tag("packages").line_break(); + for (auto&& ref : refs) + { + xml.start_complex_open_tag("package") + .text_attr("id", ref.id) + .text_attr("version", ref.version) + .finish_self_closing_complex_tag() + .line_break(); + } + xml.close_tag("packages").line_break(); + return std::move(xml.buf); +} diff --git a/toolsrc/src/vcpkg/build.cpp b/toolsrc/src/vcpkg/build.cpp index 70d097873..147ad46b2 100644 --- a/toolsrc/src/vcpkg/build.cpp +++ b/toolsrc/src/vcpkg/build.cpp @@ -1,1263 +1,1263 @@ -#include "pch.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace vcpkg; -using vcpkg::Build::BuildResult; -using vcpkg::Parse::ParseControlErrorInfo; -using vcpkg::Parse::ParseExpected; -using vcpkg::PortFileProvider::PathsPortFileProvider; - -namespace -{ - using vcpkg::PackageSpec; - using vcpkg::VcpkgPaths; - using vcpkg::Build::IBuildLogsRecorder; - struct NullBuildLogsRecorder final : IBuildLogsRecorder - { - void record_build_result(const VcpkgPaths& paths, const PackageSpec& spec, BuildResult result) const override - { - (void)paths; - (void)spec; - (void)result; - } - }; - - static const NullBuildLogsRecorder null_build_logs_recorder_instance; -} - -namespace vcpkg::Build -{ - using Dependencies::InstallPlanAction; - using Dependencies::InstallPlanType; - - void Command::perform_and_exit_ex(const FullPackageSpec& full_spec, - const SourceControlFileLocation& scfl, - const PathsPortFileProvider& provider, - IBinaryProvider& binaryprovider, - const IBuildLogsRecorder& build_logs_recorder, - const VcpkgPaths& paths) - { - Checks::exit_with_code(VCPKG_LINE_INFO, - perform_ex(full_spec, scfl, provider, binaryprovider, build_logs_recorder, paths)); - } - - const CommandStructure COMMAND_STRUCTURE = { - create_example_string("build zlib:x64-windows"), - 1, - 1, - {{}, {}}, - nullptr, - }; - - void Command::perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, Triplet default_triplet) - { - Checks::exit_with_code(VCPKG_LINE_INFO, perform(args, paths, default_triplet)); - } - - int Command::perform_ex(const FullPackageSpec& full_spec, - const SourceControlFileLocation& scfl, - const PathsPortFileProvider& provider, - IBinaryProvider& binaryprovider, - const IBuildLogsRecorder& build_logs_recorder, - const VcpkgPaths& paths) - { - auto var_provider_storage = CMakeVars::make_triplet_cmake_var_provider(paths); - auto& var_provider = *var_provider_storage; - var_provider.load_dep_info_vars(std::array{full_spec.package_spec}); - - StatusParagraphs status_db = database_load_check(paths); - - auto action_plan = Dependencies::create_feature_install_plan( - provider, var_provider, std::vector{full_spec}, status_db); - - var_provider.load_tag_vars(action_plan, provider); - - const PackageSpec& spec = full_spec.package_spec; - const SourceControlFile& scf = *scfl.source_control_file; - - Checks::check_exit(VCPKG_LINE_INFO, - spec.name() == scf.core_paragraph->name, - "The Source field inside the CONTROL file does not match the port directory: '%s' != '%s'", - scf.core_paragraph->name, - spec.name()); - - compute_all_abis(paths, action_plan, var_provider, status_db); - - InstallPlanAction* action = nullptr; - for (auto& install_action : action_plan.already_installed) - { - if (install_action.spec == full_spec.package_spec) - { - action = &install_action; - } - } - for (auto& install_action : action_plan.install_actions) - { - if (install_action.spec == full_spec.package_spec) - { - action = &install_action; - } - } - - Checks::check_exit(VCPKG_LINE_INFO, action != nullptr); - ASSUME(action != nullptr); - action->build_options = default_build_package_options; - action->build_options.editable = Editable::YES; - action->build_options.clean_buildtrees = CleanBuildtrees::NO; - action->build_options.clean_packages = CleanPackages::NO; - - const auto build_timer = Chrono::ElapsedTimer::create_started(); - const auto result = Build::build_package(paths, *action, binaryprovider, build_logs_recorder, status_db); - System::print2("Elapsed time for package ", spec, ": ", build_timer, '\n'); - - if (result.code == BuildResult::CASCADED_DUE_TO_MISSING_DEPENDENCIES) - { - System::print2(System::Color::error, - "The build command requires all dependencies to be already installed.\n"); - System::print2("The following dependencies are missing:\n\n"); - for (const auto& p : result.unmet_dependencies) - { - System::print2(" ", p, '\n'); - } - System::print2('\n'); - Checks::exit_fail(VCPKG_LINE_INFO); - } - - Checks::check_exit(VCPKG_LINE_INFO, result.code != BuildResult::EXCLUDED); - - if (result.code != BuildResult::SUCCEEDED) - { - System::print2(System::Color::error, Build::create_error_message(result.code, spec), '\n'); - System::print2(Build::create_user_troubleshooting_message(spec), '\n'); - return 1; - } - - return 0; - } - - int Command::perform(const VcpkgCmdArguments& args, const VcpkgPaths& paths, Triplet default_triplet) - { - // Build only takes a single package and all dependencies must already be installed - const ParsedArguments options = args.parse_arguments(COMMAND_STRUCTURE); - std::string first_arg = args.command_arguments.at(0); - - auto binaryprovider = create_binary_provider_from_configs(args.binary_sources).value_or_exit(VCPKG_LINE_INFO); - - const FullPackageSpec spec = Input::check_and_get_full_package_spec( - std::move(first_arg), default_triplet, COMMAND_STRUCTURE.example_text); - - Input::check_triplet(spec.package_spec.triplet(), paths); - - PathsPortFileProvider provider(paths, args.overlay_ports); - const auto port_name = spec.package_spec.name(); - const auto* scfl = provider.get_control_file(port_name).get(); - - Checks::check_exit(VCPKG_LINE_INFO, scfl != nullptr, "Error: Couldn't find port '%s'", port_name); - ASSUME(scfl != nullptr); - - return perform_ex(spec, - *scfl, - provider, - args.binary_caching_enabled() ? *binaryprovider : null_binary_provider(), - Build::null_build_logs_recorder(), - paths); - } -} - -namespace vcpkg::Build -{ - static const std::string NAME_EMPTY_PACKAGE = "PolicyEmptyPackage"; - static const std::string NAME_DLLS_WITHOUT_LIBS = "PolicyDLLsWithoutLIBs"; - static const std::string NAME_DLLS_WITHOUT_EXPORTS = "PolicyDLLsWithoutExports"; - static const std::string NAME_ONLY_RELEASE_CRT = "PolicyOnlyReleaseCRT"; - static const std::string NAME_EMPTY_INCLUDE_FOLDER = "PolicyEmptyIncludeFolder"; - static const std::string NAME_ALLOW_OBSOLETE_MSVCRT = "PolicyAllowObsoleteMsvcrt"; - static const std::string NAME_ALLOW_RESTRICTED_HEADERS = "PolicyAllowRestrictedHeaders"; - static const std::string NAME_SKIP_DUMPBIN_CHECKS = "PolicySkipDumpbinChecks"; - static const std::string NAME_SKIP_ARCHITECTURE_CHECK = "PolicySkipArchitectureCheck"; - - const std::string& to_string(BuildPolicy policy) - { - switch (policy) - { - case BuildPolicy::EMPTY_PACKAGE: return NAME_EMPTY_PACKAGE; - case BuildPolicy::DLLS_WITHOUT_LIBS: return NAME_DLLS_WITHOUT_LIBS; - case BuildPolicy::DLLS_WITHOUT_EXPORTS: return NAME_DLLS_WITHOUT_EXPORTS; - case BuildPolicy::ONLY_RELEASE_CRT: return NAME_ONLY_RELEASE_CRT; - case BuildPolicy::EMPTY_INCLUDE_FOLDER: return NAME_EMPTY_INCLUDE_FOLDER; - case BuildPolicy::ALLOW_OBSOLETE_MSVCRT: return NAME_ALLOW_OBSOLETE_MSVCRT; - case BuildPolicy::ALLOW_RESTRICTED_HEADERS: return NAME_ALLOW_RESTRICTED_HEADERS; - case BuildPolicy::SKIP_DUMPBIN_CHECKS: return NAME_SKIP_DUMPBIN_CHECKS; - case BuildPolicy::SKIP_ARCHITECTURE_CHECK: return NAME_SKIP_ARCHITECTURE_CHECK; - default: Checks::unreachable(VCPKG_LINE_INFO); - } - } - - CStringView to_cmake_variable(BuildPolicy policy) - { - switch (policy) - { - case BuildPolicy::EMPTY_PACKAGE: return "VCPKG_POLICY_EMPTY_PACKAGE"; - case BuildPolicy::DLLS_WITHOUT_LIBS: return "VCPKG_POLICY_DLLS_WITHOUT_LIBS"; - case BuildPolicy::DLLS_WITHOUT_EXPORTS: return "VCPKG_POLICY_DLLS_WITHOUT_EXPORTS"; - case BuildPolicy::ONLY_RELEASE_CRT: return "VCPKG_POLICY_ONLY_RELEASE_CRT"; - case BuildPolicy::EMPTY_INCLUDE_FOLDER: return "VCPKG_POLICY_EMPTY_INCLUDE_FOLDER"; - case BuildPolicy::ALLOW_OBSOLETE_MSVCRT: return "VCPKG_POLICY_ALLOW_OBSOLETE_MSVCRT"; - case BuildPolicy::ALLOW_RESTRICTED_HEADERS: return "VCPKG_POLICY_ALLOW_RESTRICTED_HEADERS"; - case BuildPolicy::SKIP_DUMPBIN_CHECKS: return "VCPKG_POLICY_SKIP_DUMPBIN_CHECKS"; - case BuildPolicy::SKIP_ARCHITECTURE_CHECK: return "VCPKG_POLICY_SKIP_ARCHITECTURE_CHECK"; - default: Checks::unreachable(VCPKG_LINE_INFO); - } - } - - static const std::string NAME_BUILD_IN_DOWNLOAD = "BUILT_IN"; - static const std::string NAME_ARIA2_DOWNLOAD = "ARIA2"; - - const std::string& to_string(DownloadTool tool) - { - switch (tool) - { - case DownloadTool::BUILT_IN: return NAME_BUILD_IN_DOWNLOAD; - case DownloadTool::ARIA2: return NAME_ARIA2_DOWNLOAD; - default: Checks::unreachable(VCPKG_LINE_INFO); - } - } - - Optional to_linkage_type(const std::string& str) - { - if (str == "dynamic") return LinkageType::DYNAMIC; - if (str == "static") return LinkageType::STATIC; - return nullopt; - } - - namespace BuildInfoRequiredField - { - static const std::string CRT_LINKAGE = "CRTLinkage"; - static const std::string LIBRARY_LINKAGE = "LibraryLinkage"; - } - - static CStringView to_vcvarsall_target(const std::string& cmake_system_name) - { - if (cmake_system_name.empty()) return ""; - if (cmake_system_name == "Windows") return ""; - if (cmake_system_name == "WindowsStore") return "store"; - - Checks::exit_with_message(VCPKG_LINE_INFO, "Unsupported vcvarsall target %s", cmake_system_name); - } - - static CStringView to_vcvarsall_toolchain(const std::string& target_architecture, const Toolset& toolset) - { - auto maybe_target_arch = System::to_cpu_architecture(target_architecture); - Checks::check_exit( - VCPKG_LINE_INFO, maybe_target_arch.has_value(), "Invalid architecture string: %s", target_architecture); - auto target_arch = maybe_target_arch.value_or_exit(VCPKG_LINE_INFO); - auto host_architectures = System::get_supported_host_architectures(); - - for (auto&& host : host_architectures) - { - const auto it = Util::find_if(toolset.supported_architectures, [&](const ToolsetArchOption& opt) { - return host == opt.host_arch && target_arch == opt.target_arch; - }); - if (it != toolset.supported_architectures.end()) return it->name; - } - - Checks::exit_with_message(VCPKG_LINE_INFO, - "Unsupported toolchain combination. Target was: %s but supported ones were:\n%s", - target_architecture, - Strings::join(",", toolset.supported_architectures, [](const ToolsetArchOption& t) { - return t.name.c_str(); - })); - } - - const System::Environment& EnvCache::get_action_env(const VcpkgPaths& paths, const AbiInfo& abi_info) - { -#if defined(_WIN32) - std::string build_env_cmd = - make_build_env_cmd(*abi_info.pre_build_info, abi_info.toolset.value_or_exit(VCPKG_LINE_INFO)); - - const auto& base_env = envs.get_lazy(abi_info.pre_build_info->passthrough_env_vars, [&]() -> EnvMapEntry { - std::unordered_map env; - - for (auto&& env_var : abi_info.pre_build_info->passthrough_env_vars) - { - auto env_val = System::get_environment_variable(env_var); - - if (env_val) - { - env[env_var] = env_val.value_or_exit(VCPKG_LINE_INFO); - } - } - - return {env}; - }); - - return base_env.cmd_cache.get_lazy(build_env_cmd, [&]() { - const fs::path& powershell_exe_path = paths.get_tool_exe("powershell-core"); - auto& fs = paths.get_filesystem(); - if (!fs.exists(powershell_exe_path.parent_path() / "powershell.exe")) - { - fs.copy( - powershell_exe_path, powershell_exe_path.parent_path() / "powershell.exe", fs::copy_options::none); - } - - auto clean_env = System::get_modified_clean_environment(base_env.env_map, - powershell_exe_path.parent_path().u8string() + ";"); - if (build_env_cmd.empty()) - return clean_env; - else - return System::cmd_execute_modify_env(build_env_cmd, clean_env); - }); -#else - return System::get_clean_environment(); -#endif - } - - static std::string load_compiler_hash(const VcpkgPaths& paths, const AbiInfo& abi_info); - - const std::string& EnvCache::get_triplet_info(const VcpkgPaths& paths, const AbiInfo& abi_info) - { - const auto& fs = paths.get_filesystem(); - Checks::check_exit(VCPKG_LINE_INFO, abi_info.pre_build_info != nullptr); - const fs::path triplet_file_path = paths.get_triplet_file_path(abi_info.pre_build_info->triplet); - - auto tcfile = abi_info.pre_build_info->toolchain_file(); - auto&& toolchain_hash = m_toolchain_cache.get_lazy( - tcfile, [&]() { return Hash::get_file_hash(VCPKG_LINE_INFO, fs, tcfile, Hash::Algorithm::Sha1); }); - - auto&& triplet_entry = m_triplet_cache.get_lazy(triplet_file_path, [&]() -> TripletMapEntry { - return TripletMapEntry{Hash::get_file_hash(VCPKG_LINE_INFO, fs, triplet_file_path, Hash::Algorithm::Sha1)}; - }); - - return triplet_entry.compiler_hashes.get_lazy(toolchain_hash, [&]() -> std::string { - if (m_compiler_tracking) - { - auto compiler_hash = load_compiler_hash(paths, abi_info); - return Strings::concat(triplet_entry.hash, '-', toolchain_hash, '-', compiler_hash); - } - else - { - return triplet_entry.hash + "-" + toolchain_hash; - } - }); - } - - std::string make_build_env_cmd(const PreBuildInfo& pre_build_info, const Toolset& toolset) - { - if (!pre_build_info.using_vcvars()) return ""; - - const char* tonull = " >nul"; - if (Debug::g_debugging) - { - tonull = ""; - } - - const auto arch = to_vcvarsall_toolchain(pre_build_info.target_architecture, toolset); - const auto target = to_vcvarsall_target(pre_build_info.cmake_system_name); - - return Strings::format(R"(cmd /c ""%s" %s %s %s %s 2>&1 create_binary_control_file( - const SourceParagraph& source_paragraph, - Triplet triplet, - const BuildInfo& build_info, - const std::string& abi_tag, - const std::vector& core_dependencies) - { - auto bcf = std::make_unique(); - BinaryParagraph bpgh(source_paragraph, triplet, abi_tag, core_dependencies); - if (const auto p_ver = build_info.version.get()) - { - bpgh.version = *p_ver; - } - - bcf->core_paragraph = std::move(bpgh); - return bcf; - } - - static void write_binary_control_file(const VcpkgPaths& paths, const BinaryControlFile& bcf) - { - std::string start = Strings::serialize(bcf.core_paragraph); - for (auto&& feature : bcf.features) - { - start += "\n" + Strings::serialize(feature); - } - const fs::path binary_control_file = paths.packages / bcf.core_paragraph.dir() / fs::u8path("CONTROL"); - paths.get_filesystem().write_contents(binary_control_file, start, VCPKG_LINE_INFO); - } - - static int get_concurrency() - { - static int concurrency = [] { - auto user_defined_concurrency = System::get_environment_variable("VCPKG_MAX_CONCURRENCY"); - if (user_defined_concurrency) - { - return std::stoi(user_defined_concurrency.value_or_exit(VCPKG_LINE_INFO)); - } - else - { - return System::get_num_logical_cores() + 1; - } - }(); - - return concurrency; - } - - static void get_generic_cmake_build_args(const VcpkgPaths& paths, - Triplet triplet, - const Toolset& toolset, - std::vector& out_vars) - { - Util::Vectors::append(&out_vars, - std::initializer_list{ - {"CMD", "BUILD"}, - {"TARGET_TRIPLET", triplet.canonical_name()}, - {"TARGET_TRIPLET_FILE", paths.get_triplet_file_path(triplet).u8string()}, - {"VCPKG_PLATFORM_TOOLSET", toolset.version.c_str()}, - {"DOWNLOADS", paths.downloads}, - {"VCPKG_CONCURRENCY", std::to_string(get_concurrency())}, - }); - if (!System::get_environment_variable("VCPKG_FORCE_SYSTEM_BINARIES").has_value()) - { - const fs::path& git_exe_path = paths.get_tool_exe(Tools::GIT); - out_vars.push_back({"GIT", git_exe_path}); - } - } - - static std::string load_compiler_hash(const VcpkgPaths& paths, const AbiInfo& abi_info) - { - auto triplet = abi_info.pre_build_info->triplet; - System::print2("Detecting compiler hash for triplet ", triplet, "...\n"); - auto buildpath = paths.buildtrees / "detect_compiler"; - -#if !defined(_WIN32) - // TODO: remove when vcpkg.exe is in charge for acquiring tools. Change introduced in vcpkg v0.0.107. - // bootstrap should have already downloaded ninja, but making sure it is present in case it was deleted. - vcpkg::Util::unused(paths.get_tool_exe(Tools::NINJA)); -#endif - std::vector cmake_args{ - {"CURRENT_PORT_DIR", paths.scripts / "detect_compiler"}, - {"CURRENT_BUILDTREES_DIR", buildpath}, - {"CURRENT_PACKAGES_DIR", paths.packages / ("detect_compiler_" + triplet.canonical_name())}, - }; - get_generic_cmake_build_args(paths, triplet, abi_info.toolset.value_or_exit(VCPKG_LINE_INFO), cmake_args); - - auto command = vcpkg::make_cmake_cmd(paths, paths.ports_cmake, std::move(cmake_args)); - - const auto& env = paths.get_action_env(abi_info); - auto& fs = paths.get_filesystem(); - if (!fs.exists(buildpath)) - { - std::error_code err; - fs.create_directory(buildpath, err); - Checks::check_exit(VCPKG_LINE_INFO, - !err.value(), - "Failed to create directory '%s', code: %d", - buildpath.u8string(), - err.value()); - } - auto stdoutlog = buildpath / ("stdout-" + triplet.canonical_name() + ".log"); - std::ofstream out_file(stdoutlog.native().c_str(), std::ios::out | std::ios::binary | std::ios::trunc); - Checks::check_exit(VCPKG_LINE_INFO, out_file, "Failed to open '%s' for writing", stdoutlog.u8string()); - std::string compiler_hash; - const int return_code = System::cmd_execute_and_stream_lines( - command, - [&](const std::string& s) { - static const StringLiteral s_marker = "#COMPILER_HASH#"; - if (Strings::starts_with(s, s_marker)) - { - compiler_hash = s.data() + s_marker.size(); - } - Debug::print(s, '\n'); - out_file.write(s.data(), s.size()).put('\n'); - Checks::check_exit( - VCPKG_LINE_INFO, out_file, "Error occurred while writing '%s'", stdoutlog.u8string()); - }, - env); - out_file.close(); - - if (compiler_hash.empty()) - { - Debug::print("Compiler information tracking can be disabled by passing --", - VcpkgCmdArguments::FEATURE_FLAGS_ARG, - "=-", - VcpkgCmdArguments::COMPILER_TRACKING_FEATURE, - "\n"); - } - Checks::check_exit(VCPKG_LINE_INFO, - !compiler_hash.empty(), - "Error occured while detecting compiler information. Pass `--debug` for more information."); - - Debug::print("Detecting compiler hash for triplet ", triplet, ": ", compiler_hash, "\n"); - return compiler_hash; - } - - static std::vector get_cmake_build_args(const VcpkgPaths& paths, - const Dependencies::InstallPlanAction& action, - Triplet triplet) - { -#if !defined(_WIN32) - // TODO: remove when vcpkg.exe is in charge for acquiring tools. Change introduced in vcpkg v0.0.107. - // bootstrap should have already downloaded ninja, but making sure it is present in case it was deleted. - vcpkg::Util::unused(paths.get_tool_exe(Tools::NINJA)); -#endif - auto& scfl = action.source_control_file_location.value_or_exit(VCPKG_LINE_INFO); - auto& scf = *scfl.source_control_file; - - std::string all_features; - for (auto& feature : scf.feature_paragraphs) - { - all_features.append(feature->name + ";"); - } - - std::vector variables{ - {"PORT", scf.core_paragraph->name}, - {"CURRENT_PORT_DIR", scfl.source_location}, - {"VCPKG_USE_HEAD_VERSION", Util::Enum::to_bool(action.build_options.use_head_version) ? "1" : "0"}, - {"_VCPKG_NO_DOWNLOADS", !Util::Enum::to_bool(action.build_options.allow_downloads) ? "1" : "0"}, - {"_VCPKG_DOWNLOAD_TOOL", to_string(action.build_options.download_tool)}, - {"_VCPKG_EDITABLE", Util::Enum::to_bool(action.build_options.editable) ? "1" : "0"}, - {"FEATURES", Strings::join(";", action.feature_list)}, - {"ALL_FEATURES", all_features}, - }; - get_generic_cmake_build_args( - paths, - triplet, - action.abi_info.value_or_exit(VCPKG_LINE_INFO).toolset.value_or_exit(VCPKG_LINE_INFO), - variables); - - if (Util::Enum::to_bool(action.build_options.only_downloads)) - { - variables.push_back({"VCPKG_DOWNLOAD_MODE", "true"}); - } - - const Files::Filesystem& fs = paths.get_filesystem(); - - std::vector port_configs; - for (const PackageSpec& dependency : action.package_dependencies) - { - const fs::path port_config_path = paths.installed / fs::u8path(dependency.triplet().canonical_name()) / - fs::u8path("share") / fs::u8path(dependency.name()) / - fs::u8path("vcpkg-port-config.cmake"); - - if (fs.is_regular_file(port_config_path)) - { - port_configs.emplace_back(port_config_path.u8string()); - } - } - - if (!port_configs.empty()) - { - variables.emplace_back("VCPKG_PORT_CONFIGS", Strings::join(";", port_configs)); - } - - return variables; - } - - bool PreBuildInfo::using_vcvars() const - { - return (!external_toolchain_file.has_value() || load_vcvars_env) && - (cmake_system_name.empty() || cmake_system_name == "WindowsStore"); - } - - fs::path PreBuildInfo::toolchain_file() const - { - if (auto p = external_toolchain_file.get()) - { - return fs::u8path(*p); - } - else if (cmake_system_name == "Linux") - { - return m_paths.scripts / fs::u8path("toolchains/linux.cmake"); - } - else if (cmake_system_name == "Darwin") - { - return m_paths.scripts / fs::u8path("toolchains/osx.cmake"); - } - else if (cmake_system_name == "FreeBSD") - { - return m_paths.scripts / fs::u8path("toolchains/freebsd.cmake"); - } - else if (cmake_system_name == "Android") - { - return m_paths.scripts / fs::u8path("toolchains/android.cmake"); - } - else if (cmake_system_name == "iOS") - { - return m_paths.scripts / fs::u8path("toolchains/ios.cmake"); - } - else if (cmake_system_name.empty() || cmake_system_name == "Windows" || cmake_system_name == "WindowsStore") - { - return m_paths.scripts / fs::u8path("toolchains/windows.cmake"); - } - else - { - Checks::exit_with_message(VCPKG_LINE_INFO, - "Unable to determine toolchain to use for triplet %s with CMAKE_SYSTEM_NAME %s", - triplet, - cmake_system_name); - } - } - - static ExtendedBuildResult do_build_package(const VcpkgPaths& paths, const Dependencies::InstallPlanAction& action) - { - const auto& pre_build_info = action.pre_build_info(VCPKG_LINE_INFO); - - auto& fs = paths.get_filesystem(); - auto&& scfl = action.source_control_file_location.value_or_exit(VCPKG_LINE_INFO); - - Triplet triplet = action.spec.triplet(); - const auto& triplet_file_path = paths.get_triplet_file_path(triplet).u8string(); - - if (Strings::case_insensitive_ascii_starts_with(triplet_file_path, paths.community_triplets.u8string())) - { - System::printf(vcpkg::System::Color::warning, - "-- Using community triplet %s. This triplet configuration is not guaranteed to succeed.\n", - triplet.canonical_name()); - System::printf("-- [COMMUNITY] Loading triplet configuration from: %s\n", triplet_file_path); - } - else if (!Strings::case_insensitive_ascii_starts_with(triplet_file_path, paths.triplets.u8string())) - { - System::printf("-- [OVERLAY] Loading triplet configuration from: %s\n", triplet_file_path); - } - - auto u8portdir = scfl.source_location.u8string(); - if (!Strings::case_insensitive_ascii_starts_with(u8portdir, paths.ports.u8string())) - { - System::printf("-- Installing port from location: %s\n", u8portdir); - } - - const auto timer = Chrono::ElapsedTimer::create_started(); - - auto command = vcpkg::make_cmake_cmd(paths, paths.ports_cmake, get_cmake_build_args(paths, action, triplet)); - - const auto& env = paths.get_action_env(action.abi_info.value_or_exit(VCPKG_LINE_INFO)); - - auto buildpath = paths.buildtrees / action.spec.name(); - if (!fs.exists(buildpath)) - { - std::error_code err; - fs.create_directory(buildpath, err); - Checks::check_exit(VCPKG_LINE_INFO, - !err.value(), - "Failed to create directory '%s', code: %d", - buildpath.u8string(), - err.value()); - } - auto stdoutlog = buildpath / ("stdout-" + action.spec.triplet().canonical_name() + ".log"); - std::ofstream out_file(stdoutlog.native().c_str(), std::ios::out | std::ios::binary | std::ios::trunc); - Checks::check_exit(VCPKG_LINE_INFO, out_file, "Failed to open '%s' for writing", stdoutlog.u8string()); - const int return_code = System::cmd_execute_and_stream_data( - command, - [&](StringView sv) { - System::print2(sv); - out_file.write(sv.data(), sv.size()); - Checks::check_exit( - VCPKG_LINE_INFO, out_file, "Error occurred while writing '%s'", stdoutlog.u8string()); - }, - env); - out_file.close(); - - // With the exception of empty packages, builds in "Download Mode" always result in failure. - if (action.build_options.only_downloads == Build::OnlyDownloads::YES) - { - // TODO: Capture executed command output and evaluate whether the failure was intended. - // If an unintended error occurs then return a BuildResult::DOWNLOAD_FAILURE status. - return BuildResult::DOWNLOADED; - } - - const auto buildtimeus = timer.microseconds(); - const auto spec_string = action.spec.to_string(); - - { - auto locked_metrics = Metrics::g_metrics.lock(); - - locked_metrics->track_buildtime(Hash::get_string_hash(spec_string, Hash::Algorithm::Sha256) + ":[" + - Strings::join(",", - action.feature_list, - [](const std::string& feature) { - return Hash::get_string_hash(feature, - Hash::Algorithm::Sha256); - }) + - "]", - buildtimeus); - if (return_code != 0) - { - locked_metrics->track_property("error", "build failed"); - locked_metrics->track_property("build_error", spec_string); - return BuildResult::BUILD_FAILED; - } - } - - const BuildInfo build_info = read_build_info(fs, paths.build_info_file_path(action.spec)); - const size_t error_count = - PostBuildLint::perform_all_checks(action.spec, paths, pre_build_info, build_info, scfl.source_location); - - auto find_itr = action.feature_dependencies.find("core"); - Checks::check_exit(VCPKG_LINE_INFO, find_itr != action.feature_dependencies.end()); - - std::unique_ptr bcf = create_binary_control_file(*scfl.source_control_file->core_paragraph, - triplet, - build_info, - action.public_abi(), - std::move(find_itr->second)); - - if (error_count != 0) - { - return BuildResult::POST_BUILD_CHECKS_FAILED; - } - for (auto&& feature : action.feature_list) - { - for (auto&& f_pgh : scfl.source_control_file->feature_paragraphs) - { - if (f_pgh->name == feature) - { - find_itr = action.feature_dependencies.find(feature); - Checks::check_exit(VCPKG_LINE_INFO, find_itr != action.feature_dependencies.end()); - - bcf->features.emplace_back( - *scfl.source_control_file->core_paragraph, *f_pgh, triplet, std::move(find_itr->second)); - } - } - } - - write_binary_control_file(paths, *bcf); - return {BuildResult::SUCCEEDED, std::move(bcf)}; - } - - static ExtendedBuildResult do_build_package_and_clean_buildtrees(const VcpkgPaths& paths, - const Dependencies::InstallPlanAction& action) - { - auto result = do_build_package(paths, action); - - if (action.build_options.clean_buildtrees == CleanBuildtrees::YES) - { - auto& fs = paths.get_filesystem(); - auto buildtree_files = fs.get_files_non_recursive(paths.build_dir(action.spec)); - for (auto&& file : buildtree_files) - { - if (fs.is_directory(file)) // Will only keep the logs - { - std::error_code ec; - fs::path failure_point; - fs.remove_all(file, ec, failure_point); - } - } - } - - return result; - } - - static void abi_entries_from_abi_info(const AbiInfo& abi_info, std::vector& abi_tag_entries) - { - abi_tag_entries.emplace_back("triplet", abi_info.triplet_abi.value_or_exit(VCPKG_LINE_INFO)); - - const auto& pre_build_info = *abi_info.pre_build_info; - if (pre_build_info.public_abi_override) - { - abi_tag_entries.emplace_back( - "public_abi_override", - Hash::get_string_hash(pre_build_info.public_abi_override.value_or_exit(VCPKG_LINE_INFO), - Hash::Algorithm::Sha1)); - } - - for (const auto& env_var : pre_build_info.passthrough_env_vars) - { - abi_tag_entries.emplace_back( - "ENV:" + env_var, - Hash::get_string_hash(System::get_environment_variable(env_var).value_or(""), Hash::Algorithm::Sha1)); - } - } - - static Optional compute_abi_tag(const VcpkgPaths& paths, - const Dependencies::InstallPlanAction& action, - Span dependency_abis) - { - auto& fs = paths.get_filesystem(); - Triplet triplet = action.spec.triplet(); - - std::vector abi_tag_entries(dependency_abis.begin(), dependency_abis.end()); - - abi_entries_from_abi_info(action.abi_info.value_or_exit(VCPKG_LINE_INFO), abi_tag_entries); - - // If there is an unusually large number of files in the port then - // something suspicious is going on. Rather than hash all of them - // just mark the port as no-hash - const int max_port_file_count = 100; - - auto&& port_dir = action.source_control_file_location.value_or_exit(VCPKG_LINE_INFO).source_location; - size_t port_file_count = 0; - for (auto& port_file : fs::stdfs::recursive_directory_iterator(port_dir)) - { - if (fs::is_regular_file(fs.status(VCPKG_LINE_INFO, port_file))) - { - abi_tag_entries.emplace_back( - port_file.path().filename().u8string(), - vcpkg::Hash::get_file_hash(VCPKG_LINE_INFO, fs, port_file, Hash::Algorithm::Sha1)); - - ++port_file_count; - if (port_file_count > max_port_file_count) - { - abi_tag_entries.emplace_back("no_hash_max_portfile", ""); - break; - } - } - } - - abi_tag_entries.emplace_back("cmake", paths.get_tool_version(Tools::CMAKE)); - -#if defined(_WIN32) - abi_tag_entries.emplace_back("powershell", paths.get_tool_version("powershell-core")); -#endif - - auto& helpers = paths.get_cmake_script_hashes(); - auto portfile_contents = - fs.read_contents(port_dir / fs::u8path("portfile.cmake")).value_or_exit(VCPKG_LINE_INFO); - for (auto&& helper : helpers) - { - if (Strings::case_insensitive_ascii_contains(portfile_contents, helper.first)) - { - abi_tag_entries.emplace_back(helper.first, helper.second); - } - } - - abi_tag_entries.emplace_back("post_build_checks", "2"); - std::vector sorted_feature_list = action.feature_list; - Util::sort(sorted_feature_list); - abi_tag_entries.emplace_back("features", Strings::join(";", sorted_feature_list)); - - if (action.build_options.use_head_version == UseHeadVersion::YES) abi_tag_entries.emplace_back("head", ""); - - Util::sort(abi_tag_entries); - - const std::string full_abi_info = - Strings::join("", abi_tag_entries, [](const AbiEntry& p) { return p.key + " " + p.value + "\n"; }); - - if (Debug::g_debugging) - { - std::string message = Strings::concat("[DEBUG] \n"); - for (auto&& entry : abi_tag_entries) - { - Strings::append(message, "[DEBUG] ", entry.key, "|", entry.value, "\n"); - } - Strings::append(message, "[DEBUG] \n"); - System::print2(message); - } - - auto abi_tag_entries_missing = Util::filter(abi_tag_entries, [](const AbiEntry& p) { return p.value.empty(); }); - - if (abi_tag_entries_missing.empty()) - { - auto current_build_tree = paths.build_dir(action.spec); - fs.create_directory(current_build_tree, VCPKG_LINE_INFO); - const auto abi_file_path = current_build_tree / (triplet.canonical_name() + ".vcpkg_abi_info.txt"); - fs.write_contents(abi_file_path, full_abi_info, VCPKG_LINE_INFO); - - return AbiTagAndFile{Hash::get_file_hash(VCPKG_LINE_INFO, fs, abi_file_path, Hash::Algorithm::Sha1), - abi_file_path}; - } - - Debug::print( - "Warning: abi keys are missing values:\n", - Strings::join("", abi_tag_entries_missing, [](const AbiEntry& e) { return " " + e.key + "\n"; }), - "\n"); - - return nullopt; - } - - void compute_all_abis(const VcpkgPaths& paths, - Dependencies::ActionPlan& action_plan, - const CMakeVars::CMakeVarProvider& var_provider, - const StatusParagraphs& status_db) - { - using Dependencies::InstallPlanAction; - for (auto it = action_plan.install_actions.begin(); it != action_plan.install_actions.end(); ++it) - { - auto& action = *it; - if (action.abi_info.has_value()) continue; - - std::vector dependency_abis; - if (!Util::Enum::to_bool(action.build_options.only_downloads)) - { - for (auto&& pspec : action.package_dependencies) - { - if (pspec == action.spec) continue; - - auto pred = [&](const InstallPlanAction& ipa) { return ipa.spec == pspec; }; - auto it2 = std::find_if(action_plan.install_actions.begin(), it, pred); - if (it2 == it) - { - // Finally, look in current installed - auto status_it = status_db.find(pspec); - if (status_it == status_db.end()) - { - Checks::exit_with_message( - VCPKG_LINE_INFO, "Failed to find dependency abi for %s -> %s", action.spec, pspec); - } - else - { - dependency_abis.emplace_back(AbiEntry{pspec.name(), status_it->get()->package.abi}); - } - } - else - { - dependency_abis.emplace_back(AbiEntry{pspec.name(), it2->public_abi()}); - } - } - } - - action.abi_info = AbiInfo(); - auto& abi_info = action.abi_info.value_or_exit(VCPKG_LINE_INFO); - - abi_info.pre_build_info = std::make_unique( - paths, action.spec.triplet(), var_provider.get_tag_vars(action.spec).value_or_exit(VCPKG_LINE_INFO)); - abi_info.toolset = paths.get_toolset(*abi_info.pre_build_info); - abi_info.triplet_abi = paths.get_triplet_info(abi_info); - - auto maybe_abi_tag_and_file = compute_abi_tag(paths, action, dependency_abis); - if (auto p = maybe_abi_tag_and_file.get()) - { - abi_info.package_abi = std::move(p->tag); - abi_info.abi_tag_file = std::move(p->tag_file); - } - } - } - - ExtendedBuildResult build_package(const VcpkgPaths& paths, - const Dependencies::InstallPlanAction& action, - IBinaryProvider& binaries_provider, - const IBuildLogsRecorder& build_logs_recorder, - const StatusParagraphs& status_db) - { - auto& fs = paths.get_filesystem(); - auto& spec = action.spec; - const std::string& name = action.source_control_file_location.value_or_exit(VCPKG_LINE_INFO) - .source_control_file->core_paragraph->name; - - std::vector missing_fspecs; - for (const auto& kv : action.feature_dependencies) - { - for (const FeatureSpec& fspec : kv.second) - { - if (!(status_db.is_installed(fspec) || fspec.name() == name)) - { - missing_fspecs.emplace_back(fspec); - } - } - } - - if (!missing_fspecs.empty() && !Util::Enum::to_bool(action.build_options.only_downloads)) - { - return {BuildResult::CASCADED_DUE_TO_MISSING_DEPENDENCIES, std::move(missing_fspecs)}; - } - - std::vector dependency_abis; - for (auto&& pspec : action.package_dependencies) - { - if (pspec == spec || Util::Enum::to_bool(action.build_options.only_downloads)) - { - continue; - } - const auto status_it = status_db.find_installed(pspec); - Checks::check_exit(VCPKG_LINE_INFO, status_it != status_db.end()); - dependency_abis.emplace_back( - AbiEntry{status_it->get()->package.spec.name(), status_it->get()->package.abi}); - } - - auto& abi_info = action.abi_info.value_or_exit(VCPKG_LINE_INFO); - if (!abi_info.abi_tag_file) - { - return do_build_package_and_clean_buildtrees(paths, action); - } - - auto& abi_file = *abi_info.abi_tag_file.get(); - - std::error_code ec; - const fs::path abi_package_dir = paths.package_dir(spec) / "share" / spec.name(); - const fs::path abi_file_in_package = paths.package_dir(spec) / "share" / spec.name() / "vcpkg_abi_info.txt"; - if (action.build_options.editable == Build::Editable::NO) - { - auto restore = binaries_provider.try_restore(paths, action); - if (restore == RestoreResult::build_failed) - { - return BuildResult::BUILD_FAILED; - } - else if (restore == RestoreResult::success) - { - auto maybe_bcf = Paragraphs::try_load_cached_package(paths, spec); - auto bcf = std::make_unique(std::move(maybe_bcf).value_or_exit(VCPKG_LINE_INFO)); - return {BuildResult::SUCCEEDED, std::move(bcf)}; - } - else - { - // missing package, proceed to build. - } - } - - ExtendedBuildResult result = do_build_package_and_clean_buildtrees(paths, action); - - fs.create_directories(abi_package_dir, ec); - fs.copy_file(abi_file, abi_file_in_package, fs::copy_options::none, ec); - Checks::check_exit(VCPKG_LINE_INFO, !ec, "Could not copy into file: %s", abi_file_in_package.u8string()); - - if (action.build_options.editable == Build::Editable::NO && result.code == BuildResult::SUCCEEDED) - { - binaries_provider.push_success(paths, action); - } - - build_logs_recorder.record_build_result(paths, spec, result.code); - - return result; - } - - const std::string& to_string(const BuildResult build_result) - { - static const std::string NULLVALUE_STRING = Enums::nullvalue_to_string("vcpkg::Commands::Build::BuildResult"); - static const std::string SUCCEEDED_STRING = "SUCCEEDED"; - static const std::string BUILD_FAILED_STRING = "BUILD_FAILED"; - static const std::string FILE_CONFLICTS_STRING = "FILE_CONFLICTS"; - static const std::string POST_BUILD_CHECKS_FAILED_STRING = "POST_BUILD_CHECKS_FAILED"; - static const std::string CASCADED_DUE_TO_MISSING_DEPENDENCIES_STRING = "CASCADED_DUE_TO_MISSING_DEPENDENCIES"; - static const std::string EXCLUDED_STRING = "EXCLUDED"; - static const std::string DOWNLOADED_STRING = "DOWNLOADED"; - - switch (build_result) - { - case BuildResult::NULLVALUE: return NULLVALUE_STRING; - case BuildResult::SUCCEEDED: return SUCCEEDED_STRING; - case BuildResult::BUILD_FAILED: return BUILD_FAILED_STRING; - case BuildResult::POST_BUILD_CHECKS_FAILED: return POST_BUILD_CHECKS_FAILED_STRING; - case BuildResult::FILE_CONFLICTS: return FILE_CONFLICTS_STRING; - case BuildResult::CASCADED_DUE_TO_MISSING_DEPENDENCIES: return CASCADED_DUE_TO_MISSING_DEPENDENCIES_STRING; - case BuildResult::EXCLUDED: return EXCLUDED_STRING; - case BuildResult::DOWNLOADED: return DOWNLOADED_STRING; - default: Checks::unreachable(VCPKG_LINE_INFO); - } - } - - std::string create_error_message(const BuildResult build_result, const PackageSpec& spec) - { - return Strings::format("Error: Building package %s failed with: %s", spec, Build::to_string(build_result)); - } - - std::string create_user_troubleshooting_message(const PackageSpec& spec) - { - return Strings::format("Please ensure you're using the latest portfiles with `.\\vcpkg update`, then\n" - "submit an issue at https://github.com/Microsoft/vcpkg/issues including:\n" - " Package: %s\n" - " Vcpkg version: %s\n" - "\n" - "Additionally, attach any relevant sections from the log files above.", - spec, - Commands::Version::version()); - } - - static BuildInfo inner_create_buildinfo(Parse::Paragraph pgh) - { - Parse::ParagraphParser parser(std::move(pgh)); - - BuildInfo build_info; - - { - std::string crt_linkage_as_string; - parser.required_field(BuildInfoRequiredField::CRT_LINKAGE, crt_linkage_as_string); - - auto crtlinkage = to_linkage_type(crt_linkage_as_string); - if (const auto p = crtlinkage.get()) - build_info.crt_linkage = *p; - else - Checks::exit_with_message(VCPKG_LINE_INFO, "Invalid crt linkage type: [%s]", crt_linkage_as_string); - } - - { - std::string library_linkage_as_string; - parser.required_field(BuildInfoRequiredField::LIBRARY_LINKAGE, library_linkage_as_string); - auto liblinkage = to_linkage_type(library_linkage_as_string); - if (const auto p = liblinkage.get()) - build_info.library_linkage = *p; - else - Checks::exit_with_message( - VCPKG_LINE_INFO, "Invalid library linkage type: [%s]", library_linkage_as_string); - } - std::string version = parser.optional_field("Version"); - if (!version.empty()) build_info.version = std::move(version); - - std::map policies; - for (auto policy : G_ALL_POLICIES) - { - const auto setting = parser.optional_field(to_string(policy)); - if (setting.empty()) continue; - if (setting == "enabled") - policies.emplace(policy, true); - else if (setting == "disabled") - policies.emplace(policy, false); - else - Checks::exit_with_message( - VCPKG_LINE_INFO, "Unknown setting for policy '%s': %s", to_string(policy), setting); - } - - if (const auto err = parser.error_info("PostBuildInformation")) - { - print_error_message(err); - Checks::exit_fail(VCPKG_LINE_INFO); - } - - build_info.policies = BuildPolicies(std::move(policies)); - - return build_info; - } - - BuildInfo read_build_info(const Files::Filesystem& fs, const fs::path& filepath) - { - const ExpectedS pghs = Paragraphs::get_single_paragraph(fs, filepath); - Checks::check_exit( - VCPKG_LINE_INFO, pghs.get() != nullptr, "Invalid BUILD_INFO file for package: %s", pghs.error()); - return inner_create_buildinfo(*pghs.get()); - } - - PreBuildInfo::PreBuildInfo(const VcpkgPaths& paths, - Triplet triplet, - const std::unordered_map& cmakevars) - : m_paths(paths), triplet(triplet) - { - enum class VcpkgTripletVar - { - TARGET_ARCHITECTURE = 0, - CMAKE_SYSTEM_NAME, - CMAKE_SYSTEM_VERSION, - PLATFORM_TOOLSET, - VISUAL_STUDIO_PATH, - CHAINLOAD_TOOLCHAIN_FILE, - BUILD_TYPE, - ENV_PASSTHROUGH, - PUBLIC_ABI_OVERRIDE, - LOAD_VCVARS_ENV, - }; - - static const std::vector> VCPKG_OPTIONS = { - {"VCPKG_TARGET_ARCHITECTURE", VcpkgTripletVar::TARGET_ARCHITECTURE}, - {"VCPKG_CMAKE_SYSTEM_NAME", VcpkgTripletVar::CMAKE_SYSTEM_NAME}, - {"VCPKG_CMAKE_SYSTEM_VERSION", VcpkgTripletVar::CMAKE_SYSTEM_VERSION}, - {"VCPKG_PLATFORM_TOOLSET", VcpkgTripletVar::PLATFORM_TOOLSET}, - {"VCPKG_VISUAL_STUDIO_PATH", VcpkgTripletVar::VISUAL_STUDIO_PATH}, - {"VCPKG_CHAINLOAD_TOOLCHAIN_FILE", VcpkgTripletVar::CHAINLOAD_TOOLCHAIN_FILE}, - {"VCPKG_BUILD_TYPE", VcpkgTripletVar::BUILD_TYPE}, - {"VCPKG_ENV_PASSTHROUGH", VcpkgTripletVar::ENV_PASSTHROUGH}, - {"VCPKG_PUBLIC_ABI_OVERRIDE", VcpkgTripletVar::PUBLIC_ABI_OVERRIDE}, - {"VCPKG_LOAD_VCVARS_ENV", VcpkgTripletVar::LOAD_VCVARS_ENV}, - }; - - std::string empty; - for (auto&& kv : VCPKG_OPTIONS) - { - const std::string& variable_value = [&]() -> const std::string& { - auto find_itr = cmakevars.find(kv.first); - if (find_itr == cmakevars.end()) - { - return empty; - } - else - { - return find_itr->second; - } - }(); - - switch (kv.second) - { - case VcpkgTripletVar::TARGET_ARCHITECTURE: target_architecture = variable_value; break; - case VcpkgTripletVar::CMAKE_SYSTEM_NAME: cmake_system_name = variable_value; break; - case VcpkgTripletVar::CMAKE_SYSTEM_VERSION: cmake_system_version = variable_value; break; - case VcpkgTripletVar::PLATFORM_TOOLSET: - platform_toolset = variable_value.empty() ? nullopt : Optional{variable_value}; - break; - case VcpkgTripletVar::VISUAL_STUDIO_PATH: - visual_studio_path = variable_value.empty() ? nullopt : Optional{variable_value}; - break; - case VcpkgTripletVar::CHAINLOAD_TOOLCHAIN_FILE: - external_toolchain_file = variable_value.empty() ? nullopt : Optional{variable_value}; - break; - case VcpkgTripletVar::BUILD_TYPE: - if (variable_value.empty()) - build_type = nullopt; - else if (Strings::case_insensitive_ascii_equals(variable_value, "debug")) - build_type = ConfigurationType::DEBUG; - else if (Strings::case_insensitive_ascii_equals(variable_value, "release")) - build_type = ConfigurationType::RELEASE; - else - Checks::exit_with_message( - VCPKG_LINE_INFO, - "Unknown setting for VCPKG_BUILD_TYPE: %s. Valid settings are '', 'debug' and 'release'.", - variable_value); - break; - case VcpkgTripletVar::ENV_PASSTHROUGH: - passthrough_env_vars = Strings::split(variable_value, ';'); - break; - case VcpkgTripletVar::PUBLIC_ABI_OVERRIDE: - public_abi_override = variable_value.empty() ? nullopt : Optional{variable_value}; - break; - case VcpkgTripletVar::LOAD_VCVARS_ENV: - if (variable_value.empty()) - { - load_vcvars_env = true; - if (external_toolchain_file) load_vcvars_env = false; - } - else if (Strings::case_insensitive_ascii_equals(variable_value, "1") || - Strings::case_insensitive_ascii_equals(variable_value, "on") || - Strings::case_insensitive_ascii_equals(variable_value, "true")) - load_vcvars_env = true; - else if (Strings::case_insensitive_ascii_equals(variable_value, "0") || - Strings::case_insensitive_ascii_equals(variable_value, "off") || - Strings::case_insensitive_ascii_equals(variable_value, "false")) - load_vcvars_env = false; - else - Checks::exit_with_message(VCPKG_LINE_INFO, - "Unknown boolean setting for VCPKG_LOAD_VCVARS_ENV: %s. Valid " - "settings are '', '1', '0', 'ON', 'OFF', 'TRUE', and 'FALSE'.", - variable_value); - break; - } - } - } - - ExtendedBuildResult::ExtendedBuildResult(BuildResult code) : code(code) { } - ExtendedBuildResult::ExtendedBuildResult(BuildResult code, std::unique_ptr&& bcf) - : code(code), binary_control_file(std::move(bcf)) - { - } - ExtendedBuildResult::ExtendedBuildResult(BuildResult code, std::vector&& unmet_deps) - : code(code), unmet_dependencies(std::move(unmet_deps)) - { - } - - const IBuildLogsRecorder& null_build_logs_recorder() noexcept { return null_build_logs_recorder_instance; } -} +#include "pch.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace vcpkg; +using vcpkg::Build::BuildResult; +using vcpkg::Parse::ParseControlErrorInfo; +using vcpkg::Parse::ParseExpected; +using vcpkg::PortFileProvider::PathsPortFileProvider; + +namespace +{ + using vcpkg::PackageSpec; + using vcpkg::VcpkgPaths; + using vcpkg::Build::IBuildLogsRecorder; + struct NullBuildLogsRecorder final : IBuildLogsRecorder + { + void record_build_result(const VcpkgPaths& paths, const PackageSpec& spec, BuildResult result) const override + { + (void)paths; + (void)spec; + (void)result; + } + }; + + static const NullBuildLogsRecorder null_build_logs_recorder_instance; +} + +namespace vcpkg::Build +{ + using Dependencies::InstallPlanAction; + using Dependencies::InstallPlanType; + + void Command::perform_and_exit_ex(const FullPackageSpec& full_spec, + const SourceControlFileLocation& scfl, + const PathsPortFileProvider& provider, + IBinaryProvider& binaryprovider, + const IBuildLogsRecorder& build_logs_recorder, + const VcpkgPaths& paths) + { + Checks::exit_with_code(VCPKG_LINE_INFO, + perform_ex(full_spec, scfl, provider, binaryprovider, build_logs_recorder, paths)); + } + + const CommandStructure COMMAND_STRUCTURE = { + create_example_string("build zlib:x64-windows"), + 1, + 1, + {{}, {}}, + nullptr, + }; + + void Command::perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, Triplet default_triplet) + { + Checks::exit_with_code(VCPKG_LINE_INFO, perform(args, paths, default_triplet)); + } + + int Command::perform_ex(const FullPackageSpec& full_spec, + const SourceControlFileLocation& scfl, + const PathsPortFileProvider& provider, + IBinaryProvider& binaryprovider, + const IBuildLogsRecorder& build_logs_recorder, + const VcpkgPaths& paths) + { + auto var_provider_storage = CMakeVars::make_triplet_cmake_var_provider(paths); + auto& var_provider = *var_provider_storage; + var_provider.load_dep_info_vars(std::array{full_spec.package_spec}); + + StatusParagraphs status_db = database_load_check(paths); + + auto action_plan = Dependencies::create_feature_install_plan( + provider, var_provider, std::vector{full_spec}, status_db); + + var_provider.load_tag_vars(action_plan, provider); + + const PackageSpec& spec = full_spec.package_spec; + const SourceControlFile& scf = *scfl.source_control_file; + + Checks::check_exit(VCPKG_LINE_INFO, + spec.name() == scf.core_paragraph->name, + "The Source field inside the CONTROL file does not match the port directory: '%s' != '%s'", + scf.core_paragraph->name, + spec.name()); + + compute_all_abis(paths, action_plan, var_provider, status_db); + + InstallPlanAction* action = nullptr; + for (auto& install_action : action_plan.already_installed) + { + if (install_action.spec == full_spec.package_spec) + { + action = &install_action; + } + } + for (auto& install_action : action_plan.install_actions) + { + if (install_action.spec == full_spec.package_spec) + { + action = &install_action; + } + } + + Checks::check_exit(VCPKG_LINE_INFO, action != nullptr); + ASSUME(action != nullptr); + action->build_options = default_build_package_options; + action->build_options.editable = Editable::YES; + action->build_options.clean_buildtrees = CleanBuildtrees::NO; + action->build_options.clean_packages = CleanPackages::NO; + + const auto build_timer = Chrono::ElapsedTimer::create_started(); + const auto result = Build::build_package(paths, *action, binaryprovider, build_logs_recorder, status_db); + System::print2("Elapsed time for package ", spec, ": ", build_timer, '\n'); + + if (result.code == BuildResult::CASCADED_DUE_TO_MISSING_DEPENDENCIES) + { + System::print2(System::Color::error, + "The build command requires all dependencies to be already installed.\n"); + System::print2("The following dependencies are missing:\n\n"); + for (const auto& p : result.unmet_dependencies) + { + System::print2(" ", p, '\n'); + } + System::print2('\n'); + Checks::exit_fail(VCPKG_LINE_INFO); + } + + Checks::check_exit(VCPKG_LINE_INFO, result.code != BuildResult::EXCLUDED); + + if (result.code != BuildResult::SUCCEEDED) + { + System::print2(System::Color::error, Build::create_error_message(result.code, spec), '\n'); + System::print2(Build::create_user_troubleshooting_message(spec), '\n'); + return 1; + } + + return 0; + } + + int Command::perform(const VcpkgCmdArguments& args, const VcpkgPaths& paths, Triplet default_triplet) + { + // Build only takes a single package and all dependencies must already be installed + const ParsedArguments options = args.parse_arguments(COMMAND_STRUCTURE); + std::string first_arg = args.command_arguments.at(0); + + auto binaryprovider = create_binary_provider_from_configs(args.binary_sources).value_or_exit(VCPKG_LINE_INFO); + + const FullPackageSpec spec = Input::check_and_get_full_package_spec( + std::move(first_arg), default_triplet, COMMAND_STRUCTURE.example_text); + + Input::check_triplet(spec.package_spec.triplet(), paths); + + PathsPortFileProvider provider(paths, args.overlay_ports); + const auto port_name = spec.package_spec.name(); + const auto* scfl = provider.get_control_file(port_name).get(); + + Checks::check_exit(VCPKG_LINE_INFO, scfl != nullptr, "Error: Couldn't find port '%s'", port_name); + ASSUME(scfl != nullptr); + + return perform_ex(spec, + *scfl, + provider, + args.binary_caching_enabled() ? *binaryprovider : null_binary_provider(), + Build::null_build_logs_recorder(), + paths); + } +} + +namespace vcpkg::Build +{ + static const std::string NAME_EMPTY_PACKAGE = "PolicyEmptyPackage"; + static const std::string NAME_DLLS_WITHOUT_LIBS = "PolicyDLLsWithoutLIBs"; + static const std::string NAME_DLLS_WITHOUT_EXPORTS = "PolicyDLLsWithoutExports"; + static const std::string NAME_ONLY_RELEASE_CRT = "PolicyOnlyReleaseCRT"; + static const std::string NAME_EMPTY_INCLUDE_FOLDER = "PolicyEmptyIncludeFolder"; + static const std::string NAME_ALLOW_OBSOLETE_MSVCRT = "PolicyAllowObsoleteMsvcrt"; + static const std::string NAME_ALLOW_RESTRICTED_HEADERS = "PolicyAllowRestrictedHeaders"; + static const std::string NAME_SKIP_DUMPBIN_CHECKS = "PolicySkipDumpbinChecks"; + static const std::string NAME_SKIP_ARCHITECTURE_CHECK = "PolicySkipArchitectureCheck"; + + const std::string& to_string(BuildPolicy policy) + { + switch (policy) + { + case BuildPolicy::EMPTY_PACKAGE: return NAME_EMPTY_PACKAGE; + case BuildPolicy::DLLS_WITHOUT_LIBS: return NAME_DLLS_WITHOUT_LIBS; + case BuildPolicy::DLLS_WITHOUT_EXPORTS: return NAME_DLLS_WITHOUT_EXPORTS; + case BuildPolicy::ONLY_RELEASE_CRT: return NAME_ONLY_RELEASE_CRT; + case BuildPolicy::EMPTY_INCLUDE_FOLDER: return NAME_EMPTY_INCLUDE_FOLDER; + case BuildPolicy::ALLOW_OBSOLETE_MSVCRT: return NAME_ALLOW_OBSOLETE_MSVCRT; + case BuildPolicy::ALLOW_RESTRICTED_HEADERS: return NAME_ALLOW_RESTRICTED_HEADERS; + case BuildPolicy::SKIP_DUMPBIN_CHECKS: return NAME_SKIP_DUMPBIN_CHECKS; + case BuildPolicy::SKIP_ARCHITECTURE_CHECK: return NAME_SKIP_ARCHITECTURE_CHECK; + default: Checks::unreachable(VCPKG_LINE_INFO); + } + } + + CStringView to_cmake_variable(BuildPolicy policy) + { + switch (policy) + { + case BuildPolicy::EMPTY_PACKAGE: return "VCPKG_POLICY_EMPTY_PACKAGE"; + case BuildPolicy::DLLS_WITHOUT_LIBS: return "VCPKG_POLICY_DLLS_WITHOUT_LIBS"; + case BuildPolicy::DLLS_WITHOUT_EXPORTS: return "VCPKG_POLICY_DLLS_WITHOUT_EXPORTS"; + case BuildPolicy::ONLY_RELEASE_CRT: return "VCPKG_POLICY_ONLY_RELEASE_CRT"; + case BuildPolicy::EMPTY_INCLUDE_FOLDER: return "VCPKG_POLICY_EMPTY_INCLUDE_FOLDER"; + case BuildPolicy::ALLOW_OBSOLETE_MSVCRT: return "VCPKG_POLICY_ALLOW_OBSOLETE_MSVCRT"; + case BuildPolicy::ALLOW_RESTRICTED_HEADERS: return "VCPKG_POLICY_ALLOW_RESTRICTED_HEADERS"; + case BuildPolicy::SKIP_DUMPBIN_CHECKS: return "VCPKG_POLICY_SKIP_DUMPBIN_CHECKS"; + case BuildPolicy::SKIP_ARCHITECTURE_CHECK: return "VCPKG_POLICY_SKIP_ARCHITECTURE_CHECK"; + default: Checks::unreachable(VCPKG_LINE_INFO); + } + } + + static const std::string NAME_BUILD_IN_DOWNLOAD = "BUILT_IN"; + static const std::string NAME_ARIA2_DOWNLOAD = "ARIA2"; + + const std::string& to_string(DownloadTool tool) + { + switch (tool) + { + case DownloadTool::BUILT_IN: return NAME_BUILD_IN_DOWNLOAD; + case DownloadTool::ARIA2: return NAME_ARIA2_DOWNLOAD; + default: Checks::unreachable(VCPKG_LINE_INFO); + } + } + + Optional to_linkage_type(const std::string& str) + { + if (str == "dynamic") return LinkageType::DYNAMIC; + if (str == "static") return LinkageType::STATIC; + return nullopt; + } + + namespace BuildInfoRequiredField + { + static const std::string CRT_LINKAGE = "CRTLinkage"; + static const std::string LIBRARY_LINKAGE = "LibraryLinkage"; + } + + static CStringView to_vcvarsall_target(const std::string& cmake_system_name) + { + if (cmake_system_name.empty()) return ""; + if (cmake_system_name == "Windows") return ""; + if (cmake_system_name == "WindowsStore") return "store"; + + Checks::exit_with_message(VCPKG_LINE_INFO, "Unsupported vcvarsall target %s", cmake_system_name); + } + + static CStringView to_vcvarsall_toolchain(const std::string& target_architecture, const Toolset& toolset) + { + auto maybe_target_arch = System::to_cpu_architecture(target_architecture); + Checks::check_exit( + VCPKG_LINE_INFO, maybe_target_arch.has_value(), "Invalid architecture string: %s", target_architecture); + auto target_arch = maybe_target_arch.value_or_exit(VCPKG_LINE_INFO); + auto host_architectures = System::get_supported_host_architectures(); + + for (auto&& host : host_architectures) + { + const auto it = Util::find_if(toolset.supported_architectures, [&](const ToolsetArchOption& opt) { + return host == opt.host_arch && target_arch == opt.target_arch; + }); + if (it != toolset.supported_architectures.end()) return it->name; + } + + Checks::exit_with_message(VCPKG_LINE_INFO, + "Unsupported toolchain combination. Target was: %s but supported ones were:\n%s", + target_architecture, + Strings::join(",", toolset.supported_architectures, [](const ToolsetArchOption& t) { + return t.name.c_str(); + })); + } + + const System::Environment& EnvCache::get_action_env(const VcpkgPaths& paths, const AbiInfo& abi_info) + { +#if defined(_WIN32) + std::string build_env_cmd = + make_build_env_cmd(*abi_info.pre_build_info, abi_info.toolset.value_or_exit(VCPKG_LINE_INFO)); + + const auto& base_env = envs.get_lazy(abi_info.pre_build_info->passthrough_env_vars, [&]() -> EnvMapEntry { + std::unordered_map env; + + for (auto&& env_var : abi_info.pre_build_info->passthrough_env_vars) + { + auto env_val = System::get_environment_variable(env_var); + + if (env_val) + { + env[env_var] = env_val.value_or_exit(VCPKG_LINE_INFO); + } + } + + return {env}; + }); + + return base_env.cmd_cache.get_lazy(build_env_cmd, [&]() { + const fs::path& powershell_exe_path = paths.get_tool_exe("powershell-core"); + auto& fs = paths.get_filesystem(); + if (!fs.exists(powershell_exe_path.parent_path() / "powershell.exe")) + { + fs.copy( + powershell_exe_path, powershell_exe_path.parent_path() / "powershell.exe", fs::copy_options::none); + } + + auto clean_env = System::get_modified_clean_environment(base_env.env_map, + powershell_exe_path.parent_path().u8string() + ";"); + if (build_env_cmd.empty()) + return clean_env; + else + return System::cmd_execute_modify_env(build_env_cmd, clean_env); + }); +#else + return System::get_clean_environment(); +#endif + } + + static std::string load_compiler_hash(const VcpkgPaths& paths, const AbiInfo& abi_info); + + const std::string& EnvCache::get_triplet_info(const VcpkgPaths& paths, const AbiInfo& abi_info) + { + const auto& fs = paths.get_filesystem(); + Checks::check_exit(VCPKG_LINE_INFO, abi_info.pre_build_info != nullptr); + const fs::path triplet_file_path = paths.get_triplet_file_path(abi_info.pre_build_info->triplet); + + auto tcfile = abi_info.pre_build_info->toolchain_file(); + auto&& toolchain_hash = m_toolchain_cache.get_lazy( + tcfile, [&]() { return Hash::get_file_hash(VCPKG_LINE_INFO, fs, tcfile, Hash::Algorithm::Sha1); }); + + auto&& triplet_entry = m_triplet_cache.get_lazy(triplet_file_path, [&]() -> TripletMapEntry { + return TripletMapEntry{Hash::get_file_hash(VCPKG_LINE_INFO, fs, triplet_file_path, Hash::Algorithm::Sha1)}; + }); + + return triplet_entry.compiler_hashes.get_lazy(toolchain_hash, [&]() -> std::string { + if (m_compiler_tracking) + { + auto compiler_hash = load_compiler_hash(paths, abi_info); + return Strings::concat(triplet_entry.hash, '-', toolchain_hash, '-', compiler_hash); + } + else + { + return triplet_entry.hash + "-" + toolchain_hash; + } + }); + } + + std::string make_build_env_cmd(const PreBuildInfo& pre_build_info, const Toolset& toolset) + { + if (!pre_build_info.using_vcvars()) return ""; + + const char* tonull = " >nul"; + if (Debug::g_debugging) + { + tonull = ""; + } + + const auto arch = to_vcvarsall_toolchain(pre_build_info.target_architecture, toolset); + const auto target = to_vcvarsall_target(pre_build_info.cmake_system_name); + + return Strings::format(R"(cmd /c ""%s" %s %s %s %s 2>&1 create_binary_control_file( + const SourceParagraph& source_paragraph, + Triplet triplet, + const BuildInfo& build_info, + const std::string& abi_tag, + const std::vector& core_dependencies) + { + auto bcf = std::make_unique(); + BinaryParagraph bpgh(source_paragraph, triplet, abi_tag, core_dependencies); + if (const auto p_ver = build_info.version.get()) + { + bpgh.version = *p_ver; + } + + bcf->core_paragraph = std::move(bpgh); + return bcf; + } + + static void write_binary_control_file(const VcpkgPaths& paths, const BinaryControlFile& bcf) + { + std::string start = Strings::serialize(bcf.core_paragraph); + for (auto&& feature : bcf.features) + { + start += "\n" + Strings::serialize(feature); + } + const fs::path binary_control_file = paths.packages / bcf.core_paragraph.dir() / fs::u8path("CONTROL"); + paths.get_filesystem().write_contents(binary_control_file, start, VCPKG_LINE_INFO); + } + + static int get_concurrency() + { + static int concurrency = [] { + auto user_defined_concurrency = System::get_environment_variable("VCPKG_MAX_CONCURRENCY"); + if (user_defined_concurrency) + { + return std::stoi(user_defined_concurrency.value_or_exit(VCPKG_LINE_INFO)); + } + else + { + return System::get_num_logical_cores() + 1; + } + }(); + + return concurrency; + } + + static void get_generic_cmake_build_args(const VcpkgPaths& paths, + Triplet triplet, + const Toolset& toolset, + std::vector& out_vars) + { + Util::Vectors::append(&out_vars, + std::initializer_list{ + {"CMD", "BUILD"}, + {"TARGET_TRIPLET", triplet.canonical_name()}, + {"TARGET_TRIPLET_FILE", paths.get_triplet_file_path(triplet).u8string()}, + {"VCPKG_PLATFORM_TOOLSET", toolset.version.c_str()}, + {"DOWNLOADS", paths.downloads}, + {"VCPKG_CONCURRENCY", std::to_string(get_concurrency())}, + }); + if (!System::get_environment_variable("VCPKG_FORCE_SYSTEM_BINARIES").has_value()) + { + const fs::path& git_exe_path = paths.get_tool_exe(Tools::GIT); + out_vars.push_back({"GIT", git_exe_path}); + } + } + + static std::string load_compiler_hash(const VcpkgPaths& paths, const AbiInfo& abi_info) + { + auto triplet = abi_info.pre_build_info->triplet; + System::print2("Detecting compiler hash for triplet ", triplet, "...\n"); + auto buildpath = paths.buildtrees / "detect_compiler"; + +#if !defined(_WIN32) + // TODO: remove when vcpkg.exe is in charge for acquiring tools. Change introduced in vcpkg v0.0.107. + // bootstrap should have already downloaded ninja, but making sure it is present in case it was deleted. + vcpkg::Util::unused(paths.get_tool_exe(Tools::NINJA)); +#endif + std::vector cmake_args{ + {"CURRENT_PORT_DIR", paths.scripts / "detect_compiler"}, + {"CURRENT_BUILDTREES_DIR", buildpath}, + {"CURRENT_PACKAGES_DIR", paths.packages / ("detect_compiler_" + triplet.canonical_name())}, + }; + get_generic_cmake_build_args(paths, triplet, abi_info.toolset.value_or_exit(VCPKG_LINE_INFO), cmake_args); + + auto command = vcpkg::make_cmake_cmd(paths, paths.ports_cmake, std::move(cmake_args)); + + const auto& env = paths.get_action_env(abi_info); + auto& fs = paths.get_filesystem(); + if (!fs.exists(buildpath)) + { + std::error_code err; + fs.create_directory(buildpath, err); + Checks::check_exit(VCPKG_LINE_INFO, + !err.value(), + "Failed to create directory '%s', code: %d", + buildpath.u8string(), + err.value()); + } + auto stdoutlog = buildpath / ("stdout-" + triplet.canonical_name() + ".log"); + std::ofstream out_file(stdoutlog.native().c_str(), std::ios::out | std::ios::binary | std::ios::trunc); + Checks::check_exit(VCPKG_LINE_INFO, out_file, "Failed to open '%s' for writing", stdoutlog.u8string()); + std::string compiler_hash; + const int return_code = System::cmd_execute_and_stream_lines( + command, + [&](const std::string& s) { + static const StringLiteral s_marker = "#COMPILER_HASH#"; + if (Strings::starts_with(s, s_marker)) + { + compiler_hash = s.data() + s_marker.size(); + } + Debug::print(s, '\n'); + out_file.write(s.data(), s.size()).put('\n'); + Checks::check_exit( + VCPKG_LINE_INFO, out_file, "Error occurred while writing '%s'", stdoutlog.u8string()); + }, + env); + out_file.close(); + + if (compiler_hash.empty()) + { + Debug::print("Compiler information tracking can be disabled by passing --", + VcpkgCmdArguments::FEATURE_FLAGS_ARG, + "=-", + VcpkgCmdArguments::COMPILER_TRACKING_FEATURE, + "\n"); + } + Checks::check_exit(VCPKG_LINE_INFO, + !compiler_hash.empty(), + "Error occured while detecting compiler information. Pass `--debug` for more information."); + + Debug::print("Detecting compiler hash for triplet ", triplet, ": ", compiler_hash, "\n"); + return compiler_hash; + } + + static std::vector get_cmake_build_args(const VcpkgPaths& paths, + const Dependencies::InstallPlanAction& action, + Triplet triplet) + { +#if !defined(_WIN32) + // TODO: remove when vcpkg.exe is in charge for acquiring tools. Change introduced in vcpkg v0.0.107. + // bootstrap should have already downloaded ninja, but making sure it is present in case it was deleted. + vcpkg::Util::unused(paths.get_tool_exe(Tools::NINJA)); +#endif + auto& scfl = action.source_control_file_location.value_or_exit(VCPKG_LINE_INFO); + auto& scf = *scfl.source_control_file; + + std::string all_features; + for (auto& feature : scf.feature_paragraphs) + { + all_features.append(feature->name + ";"); + } + + std::vector variables{ + {"PORT", scf.core_paragraph->name}, + {"CURRENT_PORT_DIR", scfl.source_location}, + {"VCPKG_USE_HEAD_VERSION", Util::Enum::to_bool(action.build_options.use_head_version) ? "1" : "0"}, + {"_VCPKG_NO_DOWNLOADS", !Util::Enum::to_bool(action.build_options.allow_downloads) ? "1" : "0"}, + {"_VCPKG_DOWNLOAD_TOOL", to_string(action.build_options.download_tool)}, + {"_VCPKG_EDITABLE", Util::Enum::to_bool(action.build_options.editable) ? "1" : "0"}, + {"FEATURES", Strings::join(";", action.feature_list)}, + {"ALL_FEATURES", all_features}, + }; + get_generic_cmake_build_args( + paths, + triplet, + action.abi_info.value_or_exit(VCPKG_LINE_INFO).toolset.value_or_exit(VCPKG_LINE_INFO), + variables); + + if (Util::Enum::to_bool(action.build_options.only_downloads)) + { + variables.push_back({"VCPKG_DOWNLOAD_MODE", "true"}); + } + + const Files::Filesystem& fs = paths.get_filesystem(); + + std::vector port_configs; + for (const PackageSpec& dependency : action.package_dependencies) + { + const fs::path port_config_path = paths.installed / fs::u8path(dependency.triplet().canonical_name()) / + fs::u8path("share") / fs::u8path(dependency.name()) / + fs::u8path("vcpkg-port-config.cmake"); + + if (fs.is_regular_file(port_config_path)) + { + port_configs.emplace_back(port_config_path.u8string()); + } + } + + if (!port_configs.empty()) + { + variables.emplace_back("VCPKG_PORT_CONFIGS", Strings::join(";", port_configs)); + } + + return variables; + } + + bool PreBuildInfo::using_vcvars() const + { + return (!external_toolchain_file.has_value() || load_vcvars_env) && + (cmake_system_name.empty() || cmake_system_name == "WindowsStore"); + } + + fs::path PreBuildInfo::toolchain_file() const + { + if (auto p = external_toolchain_file.get()) + { + return fs::u8path(*p); + } + else if (cmake_system_name == "Linux") + { + return m_paths.scripts / fs::u8path("toolchains/linux.cmake"); + } + else if (cmake_system_name == "Darwin") + { + return m_paths.scripts / fs::u8path("toolchains/osx.cmake"); + } + else if (cmake_system_name == "FreeBSD") + { + return m_paths.scripts / fs::u8path("toolchains/freebsd.cmake"); + } + else if (cmake_system_name == "Android") + { + return m_paths.scripts / fs::u8path("toolchains/android.cmake"); + } + else if (cmake_system_name == "iOS") + { + return m_paths.scripts / fs::u8path("toolchains/ios.cmake"); + } + else if (cmake_system_name.empty() || cmake_system_name == "Windows" || cmake_system_name == "WindowsStore") + { + return m_paths.scripts / fs::u8path("toolchains/windows.cmake"); + } + else + { + Checks::exit_with_message(VCPKG_LINE_INFO, + "Unable to determine toolchain to use for triplet %s with CMAKE_SYSTEM_NAME %s", + triplet, + cmake_system_name); + } + } + + static ExtendedBuildResult do_build_package(const VcpkgPaths& paths, const Dependencies::InstallPlanAction& action) + { + const auto& pre_build_info = action.pre_build_info(VCPKG_LINE_INFO); + + auto& fs = paths.get_filesystem(); + auto&& scfl = action.source_control_file_location.value_or_exit(VCPKG_LINE_INFO); + + Triplet triplet = action.spec.triplet(); + const auto& triplet_file_path = paths.get_triplet_file_path(triplet).u8string(); + + if (Strings::case_insensitive_ascii_starts_with(triplet_file_path, paths.community_triplets.u8string())) + { + System::printf(vcpkg::System::Color::warning, + "-- Using community triplet %s. This triplet configuration is not guaranteed to succeed.\n", + triplet.canonical_name()); + System::printf("-- [COMMUNITY] Loading triplet configuration from: %s\n", triplet_file_path); + } + else if (!Strings::case_insensitive_ascii_starts_with(triplet_file_path, paths.triplets.u8string())) + { + System::printf("-- [OVERLAY] Loading triplet configuration from: %s\n", triplet_file_path); + } + + auto u8portdir = scfl.source_location.u8string(); + if (!Strings::case_insensitive_ascii_starts_with(u8portdir, paths.ports.u8string())) + { + System::printf("-- Installing port from location: %s\n", u8portdir); + } + + const auto timer = Chrono::ElapsedTimer::create_started(); + + auto command = vcpkg::make_cmake_cmd(paths, paths.ports_cmake, get_cmake_build_args(paths, action, triplet)); + + const auto& env = paths.get_action_env(action.abi_info.value_or_exit(VCPKG_LINE_INFO)); + + auto buildpath = paths.buildtrees / action.spec.name(); + if (!fs.exists(buildpath)) + { + std::error_code err; + fs.create_directory(buildpath, err); + Checks::check_exit(VCPKG_LINE_INFO, + !err.value(), + "Failed to create directory '%s', code: %d", + buildpath.u8string(), + err.value()); + } + auto stdoutlog = buildpath / ("stdout-" + action.spec.triplet().canonical_name() + ".log"); + std::ofstream out_file(stdoutlog.native().c_str(), std::ios::out | std::ios::binary | std::ios::trunc); + Checks::check_exit(VCPKG_LINE_INFO, out_file, "Failed to open '%s' for writing", stdoutlog.u8string()); + const int return_code = System::cmd_execute_and_stream_data( + command, + [&](StringView sv) { + System::print2(sv); + out_file.write(sv.data(), sv.size()); + Checks::check_exit( + VCPKG_LINE_INFO, out_file, "Error occurred while writing '%s'", stdoutlog.u8string()); + }, + env); + out_file.close(); + + // With the exception of empty packages, builds in "Download Mode" always result in failure. + if (action.build_options.only_downloads == Build::OnlyDownloads::YES) + { + // TODO: Capture executed command output and evaluate whether the failure was intended. + // If an unintended error occurs then return a BuildResult::DOWNLOAD_FAILURE status. + return BuildResult::DOWNLOADED; + } + + const auto buildtimeus = timer.microseconds(); + const auto spec_string = action.spec.to_string(); + + { + auto locked_metrics = Metrics::g_metrics.lock(); + + locked_metrics->track_buildtime(Hash::get_string_hash(spec_string, Hash::Algorithm::Sha256) + ":[" + + Strings::join(",", + action.feature_list, + [](const std::string& feature) { + return Hash::get_string_hash(feature, + Hash::Algorithm::Sha256); + }) + + "]", + buildtimeus); + if (return_code != 0) + { + locked_metrics->track_property("error", "build failed"); + locked_metrics->track_property("build_error", spec_string); + return BuildResult::BUILD_FAILED; + } + } + + const BuildInfo build_info = read_build_info(fs, paths.build_info_file_path(action.spec)); + const size_t error_count = + PostBuildLint::perform_all_checks(action.spec, paths, pre_build_info, build_info, scfl.source_location); + + auto find_itr = action.feature_dependencies.find("core"); + Checks::check_exit(VCPKG_LINE_INFO, find_itr != action.feature_dependencies.end()); + + std::unique_ptr bcf = create_binary_control_file(*scfl.source_control_file->core_paragraph, + triplet, + build_info, + action.public_abi(), + std::move(find_itr->second)); + + if (error_count != 0) + { + return BuildResult::POST_BUILD_CHECKS_FAILED; + } + for (auto&& feature : action.feature_list) + { + for (auto&& f_pgh : scfl.source_control_file->feature_paragraphs) + { + if (f_pgh->name == feature) + { + find_itr = action.feature_dependencies.find(feature); + Checks::check_exit(VCPKG_LINE_INFO, find_itr != action.feature_dependencies.end()); + + bcf->features.emplace_back( + *scfl.source_control_file->core_paragraph, *f_pgh, triplet, std::move(find_itr->second)); + } + } + } + + write_binary_control_file(paths, *bcf); + return {BuildResult::SUCCEEDED, std::move(bcf)}; + } + + static ExtendedBuildResult do_build_package_and_clean_buildtrees(const VcpkgPaths& paths, + const Dependencies::InstallPlanAction& action) + { + auto result = do_build_package(paths, action); + + if (action.build_options.clean_buildtrees == CleanBuildtrees::YES) + { + auto& fs = paths.get_filesystem(); + auto buildtree_files = fs.get_files_non_recursive(paths.build_dir(action.spec)); + for (auto&& file : buildtree_files) + { + if (fs.is_directory(file)) // Will only keep the logs + { + std::error_code ec; + fs::path failure_point; + fs.remove_all(file, ec, failure_point); + } + } + } + + return result; + } + + static void abi_entries_from_abi_info(const AbiInfo& abi_info, std::vector& abi_tag_entries) + { + abi_tag_entries.emplace_back("triplet", abi_info.triplet_abi.value_or_exit(VCPKG_LINE_INFO)); + + const auto& pre_build_info = *abi_info.pre_build_info; + if (pre_build_info.public_abi_override) + { + abi_tag_entries.emplace_back( + "public_abi_override", + Hash::get_string_hash(pre_build_info.public_abi_override.value_or_exit(VCPKG_LINE_INFO), + Hash::Algorithm::Sha1)); + } + + for (const auto& env_var : pre_build_info.passthrough_env_vars) + { + abi_tag_entries.emplace_back( + "ENV:" + env_var, + Hash::get_string_hash(System::get_environment_variable(env_var).value_or(""), Hash::Algorithm::Sha1)); + } + } + + static Optional compute_abi_tag(const VcpkgPaths& paths, + const Dependencies::InstallPlanAction& action, + Span dependency_abis) + { + auto& fs = paths.get_filesystem(); + Triplet triplet = action.spec.triplet(); + + std::vector abi_tag_entries(dependency_abis.begin(), dependency_abis.end()); + + abi_entries_from_abi_info(action.abi_info.value_or_exit(VCPKG_LINE_INFO), abi_tag_entries); + + // If there is an unusually large number of files in the port then + // something suspicious is going on. Rather than hash all of them + // just mark the port as no-hash + const int max_port_file_count = 100; + + auto&& port_dir = action.source_control_file_location.value_or_exit(VCPKG_LINE_INFO).source_location; + size_t port_file_count = 0; + for (auto& port_file : fs::stdfs::recursive_directory_iterator(port_dir)) + { + if (fs::is_regular_file(fs.status(VCPKG_LINE_INFO, port_file))) + { + abi_tag_entries.emplace_back( + port_file.path().filename().u8string(), + vcpkg::Hash::get_file_hash(VCPKG_LINE_INFO, fs, port_file, Hash::Algorithm::Sha1)); + + ++port_file_count; + if (port_file_count > max_port_file_count) + { + abi_tag_entries.emplace_back("no_hash_max_portfile", ""); + break; + } + } + } + + abi_tag_entries.emplace_back("cmake", paths.get_tool_version(Tools::CMAKE)); + +#if defined(_WIN32) + abi_tag_entries.emplace_back("powershell", paths.get_tool_version("powershell-core")); +#endif + + auto& helpers = paths.get_cmake_script_hashes(); + auto portfile_contents = + fs.read_contents(port_dir / fs::u8path("portfile.cmake")).value_or_exit(VCPKG_LINE_INFO); + for (auto&& helper : helpers) + { + if (Strings::case_insensitive_ascii_contains(portfile_contents, helper.first)) + { + abi_tag_entries.emplace_back(helper.first, helper.second); + } + } + + abi_tag_entries.emplace_back("post_build_checks", "2"); + std::vector sorted_feature_list = action.feature_list; + Util::sort(sorted_feature_list); + abi_tag_entries.emplace_back("features", Strings::join(";", sorted_feature_list)); + + if (action.build_options.use_head_version == UseHeadVersion::YES) abi_tag_entries.emplace_back("head", ""); + + Util::sort(abi_tag_entries); + + const std::string full_abi_info = + Strings::join("", abi_tag_entries, [](const AbiEntry& p) { return p.key + " " + p.value + "\n"; }); + + if (Debug::g_debugging) + { + std::string message = Strings::concat("[DEBUG] \n"); + for (auto&& entry : abi_tag_entries) + { + Strings::append(message, "[DEBUG] ", entry.key, "|", entry.value, "\n"); + } + Strings::append(message, "[DEBUG] \n"); + System::print2(message); + } + + auto abi_tag_entries_missing = Util::filter(abi_tag_entries, [](const AbiEntry& p) { return p.value.empty(); }); + + if (abi_tag_entries_missing.empty()) + { + auto current_build_tree = paths.build_dir(action.spec); + fs.create_directory(current_build_tree, VCPKG_LINE_INFO); + const auto abi_file_path = current_build_tree / (triplet.canonical_name() + ".vcpkg_abi_info.txt"); + fs.write_contents(abi_file_path, full_abi_info, VCPKG_LINE_INFO); + + return AbiTagAndFile{Hash::get_file_hash(VCPKG_LINE_INFO, fs, abi_file_path, Hash::Algorithm::Sha1), + abi_file_path}; + } + + Debug::print( + "Warning: abi keys are missing values:\n", + Strings::join("", abi_tag_entries_missing, [](const AbiEntry& e) { return " " + e.key + "\n"; }), + "\n"); + + return nullopt; + } + + void compute_all_abis(const VcpkgPaths& paths, + Dependencies::ActionPlan& action_plan, + const CMakeVars::CMakeVarProvider& var_provider, + const StatusParagraphs& status_db) + { + using Dependencies::InstallPlanAction; + for (auto it = action_plan.install_actions.begin(); it != action_plan.install_actions.end(); ++it) + { + auto& action = *it; + if (action.abi_info.has_value()) continue; + + std::vector dependency_abis; + if (!Util::Enum::to_bool(action.build_options.only_downloads)) + { + for (auto&& pspec : action.package_dependencies) + { + if (pspec == action.spec) continue; + + auto pred = [&](const InstallPlanAction& ipa) { return ipa.spec == pspec; }; + auto it2 = std::find_if(action_plan.install_actions.begin(), it, pred); + if (it2 == it) + { + // Finally, look in current installed + auto status_it = status_db.find(pspec); + if (status_it == status_db.end()) + { + Checks::exit_with_message( + VCPKG_LINE_INFO, "Failed to find dependency abi for %s -> %s", action.spec, pspec); + } + else + { + dependency_abis.emplace_back(AbiEntry{pspec.name(), status_it->get()->package.abi}); + } + } + else + { + dependency_abis.emplace_back(AbiEntry{pspec.name(), it2->public_abi()}); + } + } + } + + action.abi_info = AbiInfo(); + auto& abi_info = action.abi_info.value_or_exit(VCPKG_LINE_INFO); + + abi_info.pre_build_info = std::make_unique( + paths, action.spec.triplet(), var_provider.get_tag_vars(action.spec).value_or_exit(VCPKG_LINE_INFO)); + abi_info.toolset = paths.get_toolset(*abi_info.pre_build_info); + abi_info.triplet_abi = paths.get_triplet_info(abi_info); + + auto maybe_abi_tag_and_file = compute_abi_tag(paths, action, dependency_abis); + if (auto p = maybe_abi_tag_and_file.get()) + { + abi_info.package_abi = std::move(p->tag); + abi_info.abi_tag_file = std::move(p->tag_file); + } + } + } + + ExtendedBuildResult build_package(const VcpkgPaths& paths, + const Dependencies::InstallPlanAction& action, + IBinaryProvider& binaries_provider, + const IBuildLogsRecorder& build_logs_recorder, + const StatusParagraphs& status_db) + { + auto& fs = paths.get_filesystem(); + auto& spec = action.spec; + const std::string& name = action.source_control_file_location.value_or_exit(VCPKG_LINE_INFO) + .source_control_file->core_paragraph->name; + + std::vector missing_fspecs; + for (const auto& kv : action.feature_dependencies) + { + for (const FeatureSpec& fspec : kv.second) + { + if (!(status_db.is_installed(fspec) || fspec.name() == name)) + { + missing_fspecs.emplace_back(fspec); + } + } + } + + if (!missing_fspecs.empty() && !Util::Enum::to_bool(action.build_options.only_downloads)) + { + return {BuildResult::CASCADED_DUE_TO_MISSING_DEPENDENCIES, std::move(missing_fspecs)}; + } + + std::vector dependency_abis; + for (auto&& pspec : action.package_dependencies) + { + if (pspec == spec || Util::Enum::to_bool(action.build_options.only_downloads)) + { + continue; + } + const auto status_it = status_db.find_installed(pspec); + Checks::check_exit(VCPKG_LINE_INFO, status_it != status_db.end()); + dependency_abis.emplace_back( + AbiEntry{status_it->get()->package.spec.name(), status_it->get()->package.abi}); + } + + auto& abi_info = action.abi_info.value_or_exit(VCPKG_LINE_INFO); + if (!abi_info.abi_tag_file) + { + return do_build_package_and_clean_buildtrees(paths, action); + } + + auto& abi_file = *abi_info.abi_tag_file.get(); + + std::error_code ec; + const fs::path abi_package_dir = paths.package_dir(spec) / "share" / spec.name(); + const fs::path abi_file_in_package = paths.package_dir(spec) / "share" / spec.name() / "vcpkg_abi_info.txt"; + if (action.build_options.editable == Build::Editable::NO) + { + auto restore = binaries_provider.try_restore(paths, action); + if (restore == RestoreResult::build_failed) + { + return BuildResult::BUILD_FAILED; + } + else if (restore == RestoreResult::success) + { + auto maybe_bcf = Paragraphs::try_load_cached_package(paths, spec); + auto bcf = std::make_unique(std::move(maybe_bcf).value_or_exit(VCPKG_LINE_INFO)); + return {BuildResult::SUCCEEDED, std::move(bcf)}; + } + else + { + // missing package, proceed to build. + } + } + + ExtendedBuildResult result = do_build_package_and_clean_buildtrees(paths, action); + + fs.create_directories(abi_package_dir, ec); + fs.copy_file(abi_file, abi_file_in_package, fs::copy_options::none, ec); + Checks::check_exit(VCPKG_LINE_INFO, !ec, "Could not copy into file: %s", abi_file_in_package.u8string()); + + if (action.build_options.editable == Build::Editable::NO && result.code == BuildResult::SUCCEEDED) + { + binaries_provider.push_success(paths, action); + } + + build_logs_recorder.record_build_result(paths, spec, result.code); + + return result; + } + + const std::string& to_string(const BuildResult build_result) + { + static const std::string NULLVALUE_STRING = Enums::nullvalue_to_string("vcpkg::Commands::Build::BuildResult"); + static const std::string SUCCEEDED_STRING = "SUCCEEDED"; + static const std::string BUILD_FAILED_STRING = "BUILD_FAILED"; + static const std::string FILE_CONFLICTS_STRING = "FILE_CONFLICTS"; + static const std::string POST_BUILD_CHECKS_FAILED_STRING = "POST_BUILD_CHECKS_FAILED"; + static const std::string CASCADED_DUE_TO_MISSING_DEPENDENCIES_STRING = "CASCADED_DUE_TO_MISSING_DEPENDENCIES"; + static const std::string EXCLUDED_STRING = "EXCLUDED"; + static const std::string DOWNLOADED_STRING = "DOWNLOADED"; + + switch (build_result) + { + case BuildResult::NULLVALUE: return NULLVALUE_STRING; + case BuildResult::SUCCEEDED: return SUCCEEDED_STRING; + case BuildResult::BUILD_FAILED: return BUILD_FAILED_STRING; + case BuildResult::POST_BUILD_CHECKS_FAILED: return POST_BUILD_CHECKS_FAILED_STRING; + case BuildResult::FILE_CONFLICTS: return FILE_CONFLICTS_STRING; + case BuildResult::CASCADED_DUE_TO_MISSING_DEPENDENCIES: return CASCADED_DUE_TO_MISSING_DEPENDENCIES_STRING; + case BuildResult::EXCLUDED: return EXCLUDED_STRING; + case BuildResult::DOWNLOADED: return DOWNLOADED_STRING; + default: Checks::unreachable(VCPKG_LINE_INFO); + } + } + + std::string create_error_message(const BuildResult build_result, const PackageSpec& spec) + { + return Strings::format("Error: Building package %s failed with: %s", spec, Build::to_string(build_result)); + } + + std::string create_user_troubleshooting_message(const PackageSpec& spec) + { + return Strings::format("Please ensure you're using the latest portfiles with `.\\vcpkg update`, then\n" + "submit an issue at https://github.com/Microsoft/vcpkg/issues including:\n" + " Package: %s\n" + " Vcpkg version: %s\n" + "\n" + "Additionally, attach any relevant sections from the log files above.", + spec, + Commands::Version::version()); + } + + static BuildInfo inner_create_buildinfo(Parse::Paragraph pgh) + { + Parse::ParagraphParser parser(std::move(pgh)); + + BuildInfo build_info; + + { + std::string crt_linkage_as_string; + parser.required_field(BuildInfoRequiredField::CRT_LINKAGE, crt_linkage_as_string); + + auto crtlinkage = to_linkage_type(crt_linkage_as_string); + if (const auto p = crtlinkage.get()) + build_info.crt_linkage = *p; + else + Checks::exit_with_message(VCPKG_LINE_INFO, "Invalid crt linkage type: [%s]", crt_linkage_as_string); + } + + { + std::string library_linkage_as_string; + parser.required_field(BuildInfoRequiredField::LIBRARY_LINKAGE, library_linkage_as_string); + auto liblinkage = to_linkage_type(library_linkage_as_string); + if (const auto p = liblinkage.get()) + build_info.library_linkage = *p; + else + Checks::exit_with_message( + VCPKG_LINE_INFO, "Invalid library linkage type: [%s]", library_linkage_as_string); + } + std::string version = parser.optional_field("Version"); + if (!version.empty()) build_info.version = std::move(version); + + std::map policies; + for (auto policy : G_ALL_POLICIES) + { + const auto setting = parser.optional_field(to_string(policy)); + if (setting.empty()) continue; + if (setting == "enabled") + policies.emplace(policy, true); + else if (setting == "disabled") + policies.emplace(policy, false); + else + Checks::exit_with_message( + VCPKG_LINE_INFO, "Unknown setting for policy '%s': %s", to_string(policy), setting); + } + + if (const auto err = parser.error_info("PostBuildInformation")) + { + print_error_message(err); + Checks::exit_fail(VCPKG_LINE_INFO); + } + + build_info.policies = BuildPolicies(std::move(policies)); + + return build_info; + } + + BuildInfo read_build_info(const Files::Filesystem& fs, const fs::path& filepath) + { + const ExpectedS pghs = Paragraphs::get_single_paragraph(fs, filepath); + Checks::check_exit( + VCPKG_LINE_INFO, pghs.get() != nullptr, "Invalid BUILD_INFO file for package: %s", pghs.error()); + return inner_create_buildinfo(*pghs.get()); + } + + PreBuildInfo::PreBuildInfo(const VcpkgPaths& paths, + Triplet triplet, + const std::unordered_map& cmakevars) + : m_paths(paths), triplet(triplet) + { + enum class VcpkgTripletVar + { + TARGET_ARCHITECTURE = 0, + CMAKE_SYSTEM_NAME, + CMAKE_SYSTEM_VERSION, + PLATFORM_TOOLSET, + VISUAL_STUDIO_PATH, + CHAINLOAD_TOOLCHAIN_FILE, + BUILD_TYPE, + ENV_PASSTHROUGH, + PUBLIC_ABI_OVERRIDE, + LOAD_VCVARS_ENV, + }; + + static const std::vector> VCPKG_OPTIONS = { + {"VCPKG_TARGET_ARCHITECTURE", VcpkgTripletVar::TARGET_ARCHITECTURE}, + {"VCPKG_CMAKE_SYSTEM_NAME", VcpkgTripletVar::CMAKE_SYSTEM_NAME}, + {"VCPKG_CMAKE_SYSTEM_VERSION", VcpkgTripletVar::CMAKE_SYSTEM_VERSION}, + {"VCPKG_PLATFORM_TOOLSET", VcpkgTripletVar::PLATFORM_TOOLSET}, + {"VCPKG_VISUAL_STUDIO_PATH", VcpkgTripletVar::VISUAL_STUDIO_PATH}, + {"VCPKG_CHAINLOAD_TOOLCHAIN_FILE", VcpkgTripletVar::CHAINLOAD_TOOLCHAIN_FILE}, + {"VCPKG_BUILD_TYPE", VcpkgTripletVar::BUILD_TYPE}, + {"VCPKG_ENV_PASSTHROUGH", VcpkgTripletVar::ENV_PASSTHROUGH}, + {"VCPKG_PUBLIC_ABI_OVERRIDE", VcpkgTripletVar::PUBLIC_ABI_OVERRIDE}, + {"VCPKG_LOAD_VCVARS_ENV", VcpkgTripletVar::LOAD_VCVARS_ENV}, + }; + + std::string empty; + for (auto&& kv : VCPKG_OPTIONS) + { + const std::string& variable_value = [&]() -> const std::string& { + auto find_itr = cmakevars.find(kv.first); + if (find_itr == cmakevars.end()) + { + return empty; + } + else + { + return find_itr->second; + } + }(); + + switch (kv.second) + { + case VcpkgTripletVar::TARGET_ARCHITECTURE: target_architecture = variable_value; break; + case VcpkgTripletVar::CMAKE_SYSTEM_NAME: cmake_system_name = variable_value; break; + case VcpkgTripletVar::CMAKE_SYSTEM_VERSION: cmake_system_version = variable_value; break; + case VcpkgTripletVar::PLATFORM_TOOLSET: + platform_toolset = variable_value.empty() ? nullopt : Optional{variable_value}; + break; + case VcpkgTripletVar::VISUAL_STUDIO_PATH: + visual_studio_path = variable_value.empty() ? nullopt : Optional{variable_value}; + break; + case VcpkgTripletVar::CHAINLOAD_TOOLCHAIN_FILE: + external_toolchain_file = variable_value.empty() ? nullopt : Optional{variable_value}; + break; + case VcpkgTripletVar::BUILD_TYPE: + if (variable_value.empty()) + build_type = nullopt; + else if (Strings::case_insensitive_ascii_equals(variable_value, "debug")) + build_type = ConfigurationType::DEBUG; + else if (Strings::case_insensitive_ascii_equals(variable_value, "release")) + build_type = ConfigurationType::RELEASE; + else + Checks::exit_with_message( + VCPKG_LINE_INFO, + "Unknown setting for VCPKG_BUILD_TYPE: %s. Valid settings are '', 'debug' and 'release'.", + variable_value); + break; + case VcpkgTripletVar::ENV_PASSTHROUGH: + passthrough_env_vars = Strings::split(variable_value, ';'); + break; + case VcpkgTripletVar::PUBLIC_ABI_OVERRIDE: + public_abi_override = variable_value.empty() ? nullopt : Optional{variable_value}; + break; + case VcpkgTripletVar::LOAD_VCVARS_ENV: + if (variable_value.empty()) + { + load_vcvars_env = true; + if (external_toolchain_file) load_vcvars_env = false; + } + else if (Strings::case_insensitive_ascii_equals(variable_value, "1") || + Strings::case_insensitive_ascii_equals(variable_value, "on") || + Strings::case_insensitive_ascii_equals(variable_value, "true")) + load_vcvars_env = true; + else if (Strings::case_insensitive_ascii_equals(variable_value, "0") || + Strings::case_insensitive_ascii_equals(variable_value, "off") || + Strings::case_insensitive_ascii_equals(variable_value, "false")) + load_vcvars_env = false; + else + Checks::exit_with_message(VCPKG_LINE_INFO, + "Unknown boolean setting for VCPKG_LOAD_VCVARS_ENV: %s. Valid " + "settings are '', '1', '0', 'ON', 'OFF', 'TRUE', and 'FALSE'.", + variable_value); + break; + } + } + } + + ExtendedBuildResult::ExtendedBuildResult(BuildResult code) : code(code) { } + ExtendedBuildResult::ExtendedBuildResult(BuildResult code, std::unique_ptr&& bcf) + : code(code), binary_control_file(std::move(bcf)) + { + } + ExtendedBuildResult::ExtendedBuildResult(BuildResult code, std::vector&& unmet_deps) + : code(code), unmet_dependencies(std::move(unmet_deps)) + { + } + + const IBuildLogsRecorder& null_build_logs_recorder() noexcept { return null_build_logs_recorder_instance; } +} diff --git a/toolsrc/src/vcpkg/buildenvironment.cpp b/toolsrc/src/vcpkg/buildenvironment.cpp index be61ba37c..9ef330e66 100644 --- a/toolsrc/src/vcpkg/buildenvironment.cpp +++ b/toolsrc/src/vcpkg/buildenvironment.cpp @@ -1,20 +1,20 @@ -#include "pch.h" - -#include - -namespace vcpkg -{ - std::string make_cmake_cmd(const VcpkgPaths& paths, - const fs::path& cmake_script, - std::vector&& pass_variables) - { - auto local_variables = std::move(pass_variables); - local_variables.emplace_back("VCPKG_ROOT_DIR", paths.root); - local_variables.emplace_back("PACKAGES_DIR", paths.packages); - local_variables.emplace_back("BUILDTREES_DIR", paths.buildtrees); - local_variables.emplace_back("_VCPKG_INSTALLED_DIR", paths.installed); - local_variables.emplace_back("DOWNLOADS", paths.downloads); - local_variables.emplace_back("VCPKG_MANIFEST_INSTALL", "OFF"); - return System::make_basic_cmake_cmd(paths.get_tool_exe(Tools::CMAKE), cmake_script, local_variables); - } -} +#include "pch.h" + +#include + +namespace vcpkg +{ + std::string make_cmake_cmd(const VcpkgPaths& paths, + const fs::path& cmake_script, + std::vector&& pass_variables) + { + auto local_variables = std::move(pass_variables); + local_variables.emplace_back("VCPKG_ROOT_DIR", paths.root); + local_variables.emplace_back("PACKAGES_DIR", paths.packages); + local_variables.emplace_back("BUILDTREES_DIR", paths.buildtrees); + local_variables.emplace_back("_VCPKG_INSTALLED_DIR", paths.installed); + local_variables.emplace_back("DOWNLOADS", paths.downloads); + local_variables.emplace_back("VCPKG_MANIFEST_INSTALL", "OFF"); + return System::make_basic_cmake_cmd(paths.get_tool_exe(Tools::CMAKE), cmake_script, local_variables); + } +} diff --git a/toolsrc/src/vcpkg/commands.ciclean.cpp b/toolsrc/src/vcpkg/commands.ciclean.cpp index 6c4a95d19..980e17da8 100644 --- a/toolsrc/src/vcpkg/commands.ciclean.cpp +++ b/toolsrc/src/vcpkg/commands.ciclean.cpp @@ -1,42 +1,42 @@ -#include "pch.h" - -#include -#include -#include - -#include -#include - -using namespace vcpkg; - -namespace -{ - void clear_directory(Files::Filesystem& fs, const fs::path& target) - { - using vcpkg::System::print2; - if (fs.is_directory(target)) - { - print2("Clearing contents of ", target.u8string(), "\n"); - fs.remove_all_inside(target, VCPKG_LINE_INFO); - } - else - { - print2("Skipping clearing contents of ", target.u8string(), " because it was not a directory\n"); - } - } -} - -namespace vcpkg::Commands::CIClean -{ - void perform_and_exit(const VcpkgCmdArguments&, const VcpkgPaths& paths) - { - using vcpkg::System::print2; - auto& fs = paths.get_filesystem(); - print2("Starting vcpkg CI clean\n"); - clear_directory(fs, paths.buildtrees); - clear_directory(fs, paths.installed); - clear_directory(fs, paths.packages); - print2("Completed vcpkg CI clean\n"); - Checks::exit_success(VCPKG_LINE_INFO); - } -} +#include "pch.h" + +#include +#include +#include + +#include +#include + +using namespace vcpkg; + +namespace +{ + void clear_directory(Files::Filesystem& fs, const fs::path& target) + { + using vcpkg::System::print2; + if (fs.is_directory(target)) + { + print2("Clearing contents of ", target.u8string(), "\n"); + fs.remove_all_inside(target, VCPKG_LINE_INFO); + } + else + { + print2("Skipping clearing contents of ", target.u8string(), " because it was not a directory\n"); + } + } +} + +namespace vcpkg::Commands::CIClean +{ + void perform_and_exit(const VcpkgCmdArguments&, const VcpkgPaths& paths) + { + using vcpkg::System::print2; + auto& fs = paths.get_filesystem(); + print2("Starting vcpkg CI clean\n"); + clear_directory(fs, paths.buildtrees); + clear_directory(fs, paths.installed); + clear_directory(fs, paths.packages); + print2("Completed vcpkg CI clean\n"); + Checks::exit_success(VCPKG_LINE_INFO); + } +} diff --git a/toolsrc/src/vcpkg/commands.dependinfo.cpp b/toolsrc/src/vcpkg/commands.dependinfo.cpp index 0431e0ad4..a6695cf2d 100644 --- a/toolsrc/src/vcpkg/commands.dependinfo.cpp +++ b/toolsrc/src/vcpkg/commands.dependinfo.cpp @@ -1,331 +1,331 @@ -#include "pch.h" - -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include - -using vcpkg::Dependencies::ActionPlan; -using vcpkg::Dependencies::InstallPlanAction; -using vcpkg::PortFileProvider::PathsPortFileProvider; - -namespace vcpkg::Commands::DependInfo -{ - namespace - { - constexpr StringLiteral OPTION_DOT = "--dot"; - constexpr StringLiteral OPTION_DGML = "--dgml"; - constexpr StringLiteral OPTION_SHOW_DEPTH = "--show-depth"; - constexpr StringLiteral OPTION_MAX_RECURSE = "--max-recurse"; - constexpr StringLiteral OPTION_SORT = "--sort"; - - constexpr int NO_RECURSE_LIMIT_VALUE = -1; - - constexpr std::array DEPEND_SWITCHES = { - {{OPTION_DOT, "Creates graph on basis of dot"}, - {OPTION_DGML, "Creates graph on basis of dgml"}, - {OPTION_SHOW_DEPTH, "Show recursion depth in output"}}}; - - constexpr std::array DEPEND_SETTINGS = { - {{OPTION_MAX_RECURSE, "Set max recursion depth, a value of -1 indicates no limit"}, - {OPTION_SORT, - "Set sort order for the list of dependencies, accepted values are: lexicographical, topological " - "(default), " - "reverse"}}}; - - struct PackageDependInfo - { - std::string package; - int depth; - std::unordered_set features; - std::vector dependencies; - }; - - enum SortMode - { - Lexicographical = 0, - Topological, - ReverseTopological, - Default = Topological - }; - - int get_max_depth(const ParsedArguments& options) - { - auto iter = options.settings.find(OPTION_MAX_RECURSE); - if (iter != options.settings.end()) - { - std::string value = iter->second; - try - { - return std::stoi(value); - } - catch (std::exception&) - { - Checks::exit_with_message(VCPKG_LINE_INFO, "Value of --max-depth must be an integer"); - } - } - // No --max-depth set, default to no limit. - return NO_RECURSE_LIMIT_VALUE; - } - - SortMode get_sort_mode(const ParsedArguments& options) - { - constexpr StringLiteral OPTION_SORT_LEXICOGRAPHICAL = "lexicographical"; - constexpr StringLiteral OPTION_SORT_TOPOLOGICAL = "topological"; - constexpr StringLiteral OPTION_SORT_REVERSE = "reverse"; - - static const std::map sortModesMap{{OPTION_SORT_LEXICOGRAPHICAL, Lexicographical}, - {OPTION_SORT_TOPOLOGICAL, Topological}, - {OPTION_SORT_REVERSE, ReverseTopological}}; - - auto iter = options.settings.find(OPTION_SORT); - if (iter != options.settings.end()) - { - const std::string value = Strings::ascii_to_lowercase(std::string{iter->second}); - auto it = sortModesMap.find(value); - if (it != sortModesMap.end()) - { - return it->second; - } - Checks::exit_with_message(VCPKG_LINE_INFO, - "Value of --sort must be one of `%s`, `%s`, or `%s`", - OPTION_SORT_LEXICOGRAPHICAL, - OPTION_SORT_TOPOLOGICAL, - OPTION_SORT_REVERSE); - } - return Default; - } - - std::string create_dot_as_string(const std::vector& depend_info) - { - int empty_node_count = 0; - - std::string s; - s.append("digraph G{ rankdir=LR; edge [minlen=3]; overlap=false;"); - - for (const auto& package : depend_info) - { - if (package.dependencies.empty()) - { - empty_node_count++; - continue; - } - - const std::string name = Strings::replace_all(std::string{package.package}, "-", "_"); - s.append(Strings::format("%s;", name)); - for (const auto& d : package.dependencies) - { - const std::string dependency_name = Strings::replace_all(std::string{d}, "-", "_"); - s.append(Strings::format("%s -> %s;", name, dependency_name)); - } - } - - s.append(Strings::format("empty [label=\"%d singletons...\"]; }", empty_node_count)); - return s; - } - - std::string create_dgml_as_string(const std::vector& depend_info) - { - std::string s; - s.append(""); - s.append(""); - - std::string nodes, links; - for (const auto& package : depend_info) - { - const std::string name = package.package; - nodes.append(Strings::format("", name)); - - // Iterate over dependencies. - for (const auto& d : package.dependencies) - { - links.append(Strings::format("", name, d)); - } - } - - s.append(Strings::format("%s", nodes)); - - s.append(Strings::format("%s", links)); - - s.append(""); - return s; - } - - std::string create_graph_as_string(const std::unordered_set& switches, - const std::vector& depend_info) - { - if (Util::Sets::contains(switches, OPTION_DOT)) - { - return create_dot_as_string(depend_info); - } - else if (Util::Sets::contains(switches, OPTION_DGML)) - { - return create_dgml_as_string(depend_info); - } - return ""; - } - - void assign_depth_to_dependencies(const std::string& package, - const int depth, - const int max_depth, - std::map& dependencies_map) - { - auto iter = dependencies_map.find(package); - Checks::check_exit( - VCPKG_LINE_INFO, iter != dependencies_map.end(), "Package not found in dependency graph"); - - PackageDependInfo& info = iter->second; - - if (depth > info.depth) - { - info.depth = depth; - if (depth < max_depth || max_depth == NO_RECURSE_LIMIT_VALUE) - { - for (auto&& dependency : info.dependencies) - { - assign_depth_to_dependencies(dependency, depth + 1, max_depth, dependencies_map); - } - } - } - } - - std::vector extract_depend_info(const std::vector& install_actions, - const int max_depth) - { - std::map package_dependencies; - for (const InstallPlanAction* pia : install_actions) - { - const InstallPlanAction& install_action = *pia; - - const std::vector dependencies = Util::fmap( - install_action.package_dependencies, [](const PackageSpec& spec) { return spec.name(); }); - - std::unordered_set features{install_action.feature_list.begin(), - install_action.feature_list.end()}; - features.erase("core"); - - std::string port_name = install_action.spec.name(); - - PackageDependInfo info{port_name, -1, features, dependencies}; - package_dependencies.emplace(port_name, std::move(info)); - } - - const InstallPlanAction& init = *install_actions.back(); - assign_depth_to_dependencies(init.spec.name(), 0, max_depth, package_dependencies); - - std::vector out = - Util::fmap(package_dependencies, [](auto&& kvpair) -> PackageDependInfo { return kvpair.second; }); - Util::erase_remove_if(out, [](auto&& info) { return info.depth < 0; }); - return out; - } - } - - const CommandStructure COMMAND_STRUCTURE = { - create_example_string("depend-info sqlite3"), - 1, - 1, - {DEPEND_SWITCHES, DEPEND_SETTINGS}, - nullptr, - }; - - void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, Triplet default_triplet) - { - const ParsedArguments options = args.parse_arguments(COMMAND_STRUCTURE); - const int max_depth = get_max_depth(options); - const SortMode sort_mode = get_sort_mode(options); - const bool show_depth = Util::Sets::contains(options.switches, OPTION_SHOW_DEPTH); - - const std::vector specs = Util::fmap(args.command_arguments, [&](auto&& arg) { - return Input::check_and_get_full_package_spec( - std::string{arg}, default_triplet, COMMAND_STRUCTURE.example_text); - }); - - for (auto&& spec : specs) - { - Input::check_triplet(spec.package_spec.triplet(), paths); - } - - PathsPortFileProvider provider(paths, args.overlay_ports); - auto var_provider_storage = CMakeVars::make_triplet_cmake_var_provider(paths); - auto& var_provider = *var_provider_storage; - - // By passing an empty status_db, we should get a plan containing all dependencies. - // All actions in the plan should be install actions, as there's no installed packages to remove. - StatusParagraphs status_db; - auto action_plan = Dependencies::create_feature_install_plan(provider, var_provider, specs, status_db); - Checks::check_exit( - VCPKG_LINE_INFO, action_plan.remove_actions.empty(), "Only install actions should exist in the plan"); - std::vector install_actions = - Util::fmap(action_plan.already_installed, [&](const auto& action) { return &action; }); - for (auto&& action : action_plan.install_actions) - install_actions.push_back(&action); - - std::vector depend_info = extract_depend_info(install_actions, max_depth); - - if (Util::Sets::contains(options.switches, OPTION_DOT) || Util::Sets::contains(options.switches, OPTION_DGML)) - { - const std::vector source_control_files = - Util::fmap(install_actions, [](const InstallPlanAction* install_action) { - const SourceControlFileLocation& scfl = - install_action->source_control_file_location.value_or_exit(VCPKG_LINE_INFO); - return const_cast(scfl.source_control_file.get()); - }); - - const std::string graph_as_string = create_graph_as_string(options.switches, depend_info); - System::print2(graph_as_string, '\n'); - Checks::exit_success(VCPKG_LINE_INFO); - } - - // TODO: Improve this code - auto lex = [](const PackageDependInfo& lhs, const PackageDependInfo& rhs) -> bool { - return lhs.package < rhs.package; - }; - auto topo = [](const PackageDependInfo& lhs, const PackageDependInfo& rhs) -> bool { - return lhs.depth > rhs.depth; - }; - auto reverse = [](const PackageDependInfo& lhs, const PackageDependInfo& rhs) -> bool { - return lhs.depth < rhs.depth; - }; - - switch (sort_mode) - { - case SortMode::Lexicographical: std::sort(std::begin(depend_info), std::end(depend_info), lex); break; - case SortMode::ReverseTopological: - std::sort(std::begin(depend_info), std::end(depend_info), reverse); - break; - case SortMode::Topological: std::sort(std::begin(depend_info), std::end(depend_info), topo); break; - default: Checks::unreachable(VCPKG_LINE_INFO); - } - - for (auto&& info : depend_info) - { - if (info.depth >= 0) - { - std::string features = Strings::join(", ", info.features); - const std::string dependencies = Strings::join(", ", info.dependencies); - - if (show_depth) - { - System::print2(System::Color::error, "(", info.depth, ") "); - } - System::print2(System::Color::success, info.package); - if (!features.empty()) - { - System::print2("["); - System::print2(System::Color::warning, features); - System::print2("]"); - } - System::print2(": ", dependencies, "\n"); - } - } - Checks::exit_success(VCPKG_LINE_INFO); - } -} +#include "pch.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +using vcpkg::Dependencies::ActionPlan; +using vcpkg::Dependencies::InstallPlanAction; +using vcpkg::PortFileProvider::PathsPortFileProvider; + +namespace vcpkg::Commands::DependInfo +{ + namespace + { + constexpr StringLiteral OPTION_DOT = "--dot"; + constexpr StringLiteral OPTION_DGML = "--dgml"; + constexpr StringLiteral OPTION_SHOW_DEPTH = "--show-depth"; + constexpr StringLiteral OPTION_MAX_RECURSE = "--max-recurse"; + constexpr StringLiteral OPTION_SORT = "--sort"; + + constexpr int NO_RECURSE_LIMIT_VALUE = -1; + + constexpr std::array DEPEND_SWITCHES = { + {{OPTION_DOT, "Creates graph on basis of dot"}, + {OPTION_DGML, "Creates graph on basis of dgml"}, + {OPTION_SHOW_DEPTH, "Show recursion depth in output"}}}; + + constexpr std::array DEPEND_SETTINGS = { + {{OPTION_MAX_RECURSE, "Set max recursion depth, a value of -1 indicates no limit"}, + {OPTION_SORT, + "Set sort order for the list of dependencies, accepted values are: lexicographical, topological " + "(default), " + "reverse"}}}; + + struct PackageDependInfo + { + std::string package; + int depth; + std::unordered_set features; + std::vector dependencies; + }; + + enum SortMode + { + Lexicographical = 0, + Topological, + ReverseTopological, + Default = Topological + }; + + int get_max_depth(const ParsedArguments& options) + { + auto iter = options.settings.find(OPTION_MAX_RECURSE); + if (iter != options.settings.end()) + { + std::string value = iter->second; + try + { + return std::stoi(value); + } + catch (std::exception&) + { + Checks::exit_with_message(VCPKG_LINE_INFO, "Value of --max-depth must be an integer"); + } + } + // No --max-depth set, default to no limit. + return NO_RECURSE_LIMIT_VALUE; + } + + SortMode get_sort_mode(const ParsedArguments& options) + { + constexpr StringLiteral OPTION_SORT_LEXICOGRAPHICAL = "lexicographical"; + constexpr StringLiteral OPTION_SORT_TOPOLOGICAL = "topological"; + constexpr StringLiteral OPTION_SORT_REVERSE = "reverse"; + + static const std::map sortModesMap{{OPTION_SORT_LEXICOGRAPHICAL, Lexicographical}, + {OPTION_SORT_TOPOLOGICAL, Topological}, + {OPTION_SORT_REVERSE, ReverseTopological}}; + + auto iter = options.settings.find(OPTION_SORT); + if (iter != options.settings.end()) + { + const std::string value = Strings::ascii_to_lowercase(std::string{iter->second}); + auto it = sortModesMap.find(value); + if (it != sortModesMap.end()) + { + return it->second; + } + Checks::exit_with_message(VCPKG_LINE_INFO, + "Value of --sort must be one of `%s`, `%s`, or `%s`", + OPTION_SORT_LEXICOGRAPHICAL, + OPTION_SORT_TOPOLOGICAL, + OPTION_SORT_REVERSE); + } + return Default; + } + + std::string create_dot_as_string(const std::vector& depend_info) + { + int empty_node_count = 0; + + std::string s; + s.append("digraph G{ rankdir=LR; edge [minlen=3]; overlap=false;"); + + for (const auto& package : depend_info) + { + if (package.dependencies.empty()) + { + empty_node_count++; + continue; + } + + const std::string name = Strings::replace_all(std::string{package.package}, "-", "_"); + s.append(Strings::format("%s;", name)); + for (const auto& d : package.dependencies) + { + const std::string dependency_name = Strings::replace_all(std::string{d}, "-", "_"); + s.append(Strings::format("%s -> %s;", name, dependency_name)); + } + } + + s.append(Strings::format("empty [label=\"%d singletons...\"]; }", empty_node_count)); + return s; + } + + std::string create_dgml_as_string(const std::vector& depend_info) + { + std::string s; + s.append(""); + s.append(""); + + std::string nodes, links; + for (const auto& package : depend_info) + { + const std::string name = package.package; + nodes.append(Strings::format("", name)); + + // Iterate over dependencies. + for (const auto& d : package.dependencies) + { + links.append(Strings::format("", name, d)); + } + } + + s.append(Strings::format("%s", nodes)); + + s.append(Strings::format("%s", links)); + + s.append(""); + return s; + } + + std::string create_graph_as_string(const std::unordered_set& switches, + const std::vector& depend_info) + { + if (Util::Sets::contains(switches, OPTION_DOT)) + { + return create_dot_as_string(depend_info); + } + else if (Util::Sets::contains(switches, OPTION_DGML)) + { + return create_dgml_as_string(depend_info); + } + return ""; + } + + void assign_depth_to_dependencies(const std::string& package, + const int depth, + const int max_depth, + std::map& dependencies_map) + { + auto iter = dependencies_map.find(package); + Checks::check_exit( + VCPKG_LINE_INFO, iter != dependencies_map.end(), "Package not found in dependency graph"); + + PackageDependInfo& info = iter->second; + + if (depth > info.depth) + { + info.depth = depth; + if (depth < max_depth || max_depth == NO_RECURSE_LIMIT_VALUE) + { + for (auto&& dependency : info.dependencies) + { + assign_depth_to_dependencies(dependency, depth + 1, max_depth, dependencies_map); + } + } + } + } + + std::vector extract_depend_info(const std::vector& install_actions, + const int max_depth) + { + std::map package_dependencies; + for (const InstallPlanAction* pia : install_actions) + { + const InstallPlanAction& install_action = *pia; + + const std::vector dependencies = Util::fmap( + install_action.package_dependencies, [](const PackageSpec& spec) { return spec.name(); }); + + std::unordered_set features{install_action.feature_list.begin(), + install_action.feature_list.end()}; + features.erase("core"); + + std::string port_name = install_action.spec.name(); + + PackageDependInfo info{port_name, -1, features, dependencies}; + package_dependencies.emplace(port_name, std::move(info)); + } + + const InstallPlanAction& init = *install_actions.back(); + assign_depth_to_dependencies(init.spec.name(), 0, max_depth, package_dependencies); + + std::vector out = + Util::fmap(package_dependencies, [](auto&& kvpair) -> PackageDependInfo { return kvpair.second; }); + Util::erase_remove_if(out, [](auto&& info) { return info.depth < 0; }); + return out; + } + } + + const CommandStructure COMMAND_STRUCTURE = { + create_example_string("depend-info sqlite3"), + 1, + 1, + {DEPEND_SWITCHES, DEPEND_SETTINGS}, + nullptr, + }; + + void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, Triplet default_triplet) + { + const ParsedArguments options = args.parse_arguments(COMMAND_STRUCTURE); + const int max_depth = get_max_depth(options); + const SortMode sort_mode = get_sort_mode(options); + const bool show_depth = Util::Sets::contains(options.switches, OPTION_SHOW_DEPTH); + + const std::vector specs = Util::fmap(args.command_arguments, [&](auto&& arg) { + return Input::check_and_get_full_package_spec( + std::string{arg}, default_triplet, COMMAND_STRUCTURE.example_text); + }); + + for (auto&& spec : specs) + { + Input::check_triplet(spec.package_spec.triplet(), paths); + } + + PathsPortFileProvider provider(paths, args.overlay_ports); + auto var_provider_storage = CMakeVars::make_triplet_cmake_var_provider(paths); + auto& var_provider = *var_provider_storage; + + // By passing an empty status_db, we should get a plan containing all dependencies. + // All actions in the plan should be install actions, as there's no installed packages to remove. + StatusParagraphs status_db; + auto action_plan = Dependencies::create_feature_install_plan(provider, var_provider, specs, status_db); + Checks::check_exit( + VCPKG_LINE_INFO, action_plan.remove_actions.empty(), "Only install actions should exist in the plan"); + std::vector install_actions = + Util::fmap(action_plan.already_installed, [&](const auto& action) { return &action; }); + for (auto&& action : action_plan.install_actions) + install_actions.push_back(&action); + + std::vector depend_info = extract_depend_info(install_actions, max_depth); + + if (Util::Sets::contains(options.switches, OPTION_DOT) || Util::Sets::contains(options.switches, OPTION_DGML)) + { + const std::vector source_control_files = + Util::fmap(install_actions, [](const InstallPlanAction* install_action) { + const SourceControlFileLocation& scfl = + install_action->source_control_file_location.value_or_exit(VCPKG_LINE_INFO); + return const_cast(scfl.source_control_file.get()); + }); + + const std::string graph_as_string = create_graph_as_string(options.switches, depend_info); + System::print2(graph_as_string, '\n'); + Checks::exit_success(VCPKG_LINE_INFO); + } + + // TODO: Improve this code + auto lex = [](const PackageDependInfo& lhs, const PackageDependInfo& rhs) -> bool { + return lhs.package < rhs.package; + }; + auto topo = [](const PackageDependInfo& lhs, const PackageDependInfo& rhs) -> bool { + return lhs.depth > rhs.depth; + }; + auto reverse = [](const PackageDependInfo& lhs, const PackageDependInfo& rhs) -> bool { + return lhs.depth < rhs.depth; + }; + + switch (sort_mode) + { + case SortMode::Lexicographical: std::sort(std::begin(depend_info), std::end(depend_info), lex); break; + case SortMode::ReverseTopological: + std::sort(std::begin(depend_info), std::end(depend_info), reverse); + break; + case SortMode::Topological: std::sort(std::begin(depend_info), std::end(depend_info), topo); break; + default: Checks::unreachable(VCPKG_LINE_INFO); + } + + for (auto&& info : depend_info) + { + if (info.depth >= 0) + { + std::string features = Strings::join(", ", info.features); + const std::string dependencies = Strings::join(", ", info.dependencies); + + if (show_depth) + { + System::print2(System::Color::error, "(", info.depth, ") "); + } + System::print2(System::Color::success, info.package); + if (!features.empty()) + { + System::print2("["); + System::print2(System::Color::warning, features); + System::print2("]"); + } + System::print2(": ", dependencies, "\n"); + } + } + Checks::exit_success(VCPKG_LINE_INFO); + } +} diff --git a/toolsrc/src/vcpkg/commands.xvsinstances.cpp b/toolsrc/src/vcpkg/commands.xvsinstances.cpp index 73b71a99d..718c73ec0 100644 --- a/toolsrc/src/vcpkg/commands.xvsinstances.cpp +++ b/toolsrc/src/vcpkg/commands.xvsinstances.cpp @@ -1,36 +1,36 @@ -#include "pch.h" - -#include - -#include -#include -#include - -namespace vcpkg::Commands::X_VSInstances -{ - const CommandStructure COMMAND_STRUCTURE = { - create_example_string("x-vsinstances"), - 0, - 0, - {{}, {}}, - nullptr, - }; - - void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths) - { -#if defined(_WIN32) - const ParsedArguments parsed_args = args.parse_arguments(COMMAND_STRUCTURE); - - const auto instances = vcpkg::VisualStudio::get_visual_studio_instances(paths); - for (const std::string& instance : instances) - { - System::print2(instance, '\n'); - } - - Checks::exit_success(VCPKG_LINE_INFO); -#else - Util::unused(args, paths); - Checks::exit_with_message(VCPKG_LINE_INFO, "This command is not supported on non-windows platforms."); -#endif - } -} +#include "pch.h" + +#include + +#include +#include +#include + +namespace vcpkg::Commands::X_VSInstances +{ + const CommandStructure COMMAND_STRUCTURE = { + create_example_string("x-vsinstances"), + 0, + 0, + {{}, {}}, + nullptr, + }; + + void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths) + { +#if defined(_WIN32) + const ParsedArguments parsed_args = args.parse_arguments(COMMAND_STRUCTURE); + + const auto instances = vcpkg::VisualStudio::get_visual_studio_instances(paths); + for (const std::string& instance : instances) + { + System::print2(instance, '\n'); + } + + Checks::exit_success(VCPKG_LINE_INFO); +#else + Util::unused(args, paths); + Checks::exit_with_message(VCPKG_LINE_INFO, "This command is not supported on non-windows platforms."); +#endif + } +} diff --git a/toolsrc/src/vcpkg/export.chocolatey.cpp b/toolsrc/src/vcpkg/export.chocolatey.cpp index a8b685988..b5a0a0a88 100644 --- a/toolsrc/src/vcpkg/export.chocolatey.cpp +++ b/toolsrc/src/vcpkg/export.chocolatey.cpp @@ -1,44 +1,44 @@ -#include "pch.h" - -#include -#include - -#include -#include -#include -#include - -namespace vcpkg::Export::Chocolatey -{ - using Dependencies::ExportPlanAction; - using Dependencies::ExportPlanType; - using Install::InstallDir; - - static std::string create_nuspec_dependencies(const BinaryParagraph& binary_paragraph, - const std::map& packages_version) - { - static constexpr auto CONTENT_TEMPLATE = R"()"; - - std::string nuspec_dependencies; - for (const std::string& depend : binary_paragraph.dependencies) - { - auto found = packages_version.find(depend); - if (found == packages_version.end()) - { - Checks::exit_with_message(VCPKG_LINE_INFO, "Cannot find desired dependency version."); - } - std::string nuspec_dependency = Strings::replace_all(CONTENT_TEMPLATE, "@PACKAGE_ID@", depend); - nuspec_dependency = Strings::replace_all(std::move(nuspec_dependency), "@PACKAGE_VERSION@", found->second); - nuspec_dependencies += nuspec_dependency; - } - return nuspec_dependencies; - } - - static std::string create_nuspec_file_contents(const std::string& exported_root_dir, - const BinaryParagraph& binary_paragraph, - const std::map& packages_version, - const Options& chocolatey_options) - { +#include "pch.h" + +#include +#include + +#include +#include +#include +#include + +namespace vcpkg::Export::Chocolatey +{ + using Dependencies::ExportPlanAction; + using Dependencies::ExportPlanType; + using Install::InstallDir; + + static std::string create_nuspec_dependencies(const BinaryParagraph& binary_paragraph, + const std::map& packages_version) + { + static constexpr auto CONTENT_TEMPLATE = R"()"; + + std::string nuspec_dependencies; + for (const std::string& depend : binary_paragraph.dependencies) + { + auto found = packages_version.find(depend); + if (found == packages_version.end()) + { + Checks::exit_with_message(VCPKG_LINE_INFO, "Cannot find desired dependency version."); + } + std::string nuspec_dependency = Strings::replace_all(CONTENT_TEMPLATE, "@PACKAGE_ID@", depend); + nuspec_dependency = Strings::replace_all(std::move(nuspec_dependency), "@PACKAGE_VERSION@", found->second); + nuspec_dependencies += nuspec_dependency; + } + return nuspec_dependencies; + } + + static std::string create_nuspec_file_contents(const std::string& exported_root_dir, + const BinaryParagraph& binary_paragraph, + const std::map& packages_version, + const Options& chocolatey_options) + { static constexpr auto CONTENT_TEMPLATE = R"( @@ -57,30 +57,30 @@ namespace vcpkg::Export::Chocolatey -)"; - auto package_version = packages_version.find(binary_paragraph.spec.name()); - if (package_version == packages_version.end()) - { - Checks::exit_with_message(VCPKG_LINE_INFO, "Cannot find desired package version."); - } - std::string nuspec_file_content = - Strings::replace_all(CONTENT_TEMPLATE, "@PACKAGE_ID@", binary_paragraph.spec.name()); - nuspec_file_content = - Strings::replace_all(std::move(nuspec_file_content), "@PACKAGE_VERSION@", package_version->second); - nuspec_file_content = Strings::replace_all( - std::move(nuspec_file_content), "@PACKAGE_MAINTAINER@", chocolatey_options.maybe_maintainer.value_or("")); - nuspec_file_content = Strings::replace_all( - std::move(nuspec_file_content), "@PACKAGE_DESCRIPTION@", Strings::join("\n", binary_paragraph.description)); - nuspec_file_content = - Strings::replace_all(std::move(nuspec_file_content), "@EXPORTED_ROOT_DIR@", exported_root_dir); - nuspec_file_content = Strings::replace_all(std::move(nuspec_file_content), - "@PACKAGE_DEPENDENCIES@", - create_nuspec_dependencies(binary_paragraph, packages_version)); - return nuspec_file_content; - } - - static std::string create_chocolatey_install_contents() - { +)"; + auto package_version = packages_version.find(binary_paragraph.spec.name()); + if (package_version == packages_version.end()) + { + Checks::exit_with_message(VCPKG_LINE_INFO, "Cannot find desired package version."); + } + std::string nuspec_file_content = + Strings::replace_all(CONTENT_TEMPLATE, "@PACKAGE_ID@", binary_paragraph.spec.name()); + nuspec_file_content = + Strings::replace_all(std::move(nuspec_file_content), "@PACKAGE_VERSION@", package_version->second); + nuspec_file_content = Strings::replace_all( + std::move(nuspec_file_content), "@PACKAGE_MAINTAINER@", chocolatey_options.maybe_maintainer.value_or("")); + nuspec_file_content = Strings::replace_all( + std::move(nuspec_file_content), "@PACKAGE_DESCRIPTION@", Strings::join("\n", binary_paragraph.description)); + nuspec_file_content = + Strings::replace_all(std::move(nuspec_file_content), "@EXPORTED_ROOT_DIR@", exported_root_dir); + nuspec_file_content = Strings::replace_all(std::move(nuspec_file_content), + "@PACKAGE_DEPENDENCIES@", + create_nuspec_dependencies(binary_paragraph, packages_version)); + return nuspec_file_content; + } + + static std::string create_chocolatey_install_contents() + { static constexpr auto CONTENT_TEMPLATE = R"###( $ErrorActionPreference = 'Stop'; @@ -93,12 +93,12 @@ $whereToInstall = (pwd).path $whereToInstallCache = Join-Path $rootDir 'install.txt' Set-Content -Path $whereToInstallCache -Value $whereToInstall Copy-Item $installedDir -destination $whereToInstall -recurse -force -)###"; - return CONTENT_TEMPLATE; - } - - static std::string create_chocolatey_uninstall_contents(const BinaryParagraph& binary_paragraph) - { +)###"; + return CONTENT_TEMPLATE; + } + + static std::string create_chocolatey_uninstall_contents(const BinaryParagraph& binary_paragraph) + { static constexpr auto CONTENT_TEMPLATE = R"###( $ErrorActionPreference = 'Stop'; @@ -143,90 +143,90 @@ if (Test-Path $installedDir) } ) { $empties | Remove-Item } } -)###"; - std::string chocolatey_uninstall_content = - Strings::replace_all(CONTENT_TEMPLATE, "@PACKAGE_FULLSTEM@", binary_paragraph.fullstem()); - return chocolatey_uninstall_content; - } - - void do_export(const std::vector& export_plan, - const VcpkgPaths& paths, - const Options& chocolatey_options) - { - Checks::check_exit( - VCPKG_LINE_INFO, chocolatey_options.maybe_maintainer.has_value(), "--x-maintainer option is required."); - - Files::Filesystem& fs = paths.get_filesystem(); - const fs::path vcpkg_root_path = paths.root; - const fs::path raw_exported_dir_path = vcpkg_root_path / "chocolatey"; - const fs::path exported_dir_path = vcpkg_root_path / "chocolatey_exports"; - const fs::path& nuget_exe = paths.get_tool_exe(Tools::NUGET); - - std::error_code ec; - fs.remove_all(raw_exported_dir_path, VCPKG_LINE_INFO); - fs.create_directory(raw_exported_dir_path, ec); - fs.remove_all(exported_dir_path, VCPKG_LINE_INFO); - fs.create_directory(exported_dir_path, ec); - - // execute the plan - std::map packages_version; - for (const ExportPlanAction& action : export_plan) - { - if (action.plan_type != ExportPlanType::ALREADY_BUILT) - { - Checks::unreachable(VCPKG_LINE_INFO); - } - - const BinaryParagraph& binary_paragraph = action.core_paragraph().value_or_exit(VCPKG_LINE_INFO); - auto norm_version = binary_paragraph.version; - - // normalize the version string to be separated by dots to be compliant with Nusepc. - norm_version = Strings::replace_all(std::move(norm_version), "-", "."); - norm_version = Strings::replace_all(std::move(norm_version), "_", "."); - norm_version = norm_version + chocolatey_options.maybe_version_suffix.value_or(""); - packages_version.insert(std::make_pair(binary_paragraph.spec.name(), norm_version)); - } - - for (const ExportPlanAction& action : export_plan) - { - const std::string display_name = action.spec.to_string(); - System::print2("Exporting package ", display_name, "...\n"); - - const fs::path per_package_dir_path = raw_exported_dir_path / action.spec.name(); - - const BinaryParagraph& binary_paragraph = action.core_paragraph().value_or_exit(VCPKG_LINE_INFO); - - const InstallDir dirs = InstallDir::from_destination_root( - per_package_dir_path / "installed", - action.spec.triplet().to_string(), - per_package_dir_path / "installed" / "vcpkg" / "info" / (binary_paragraph.fullstem() + ".list")); - - Install::install_package_and_write_listfile(paths, action.spec, dirs); - - const std::string nuspec_file_content = create_nuspec_file_contents( - per_package_dir_path.string(), binary_paragraph, packages_version, chocolatey_options); - const fs::path nuspec_file_path = - per_package_dir_path / Strings::concat(binary_paragraph.spec.name(), ".nuspec"); - fs.write_contents(nuspec_file_path, nuspec_file_content, VCPKG_LINE_INFO); - - fs.create_directory(per_package_dir_path / "tools", ec); - - const std::string chocolatey_install_content = create_chocolatey_install_contents(); - const fs::path chocolatey_install_file_path = per_package_dir_path / "tools" / "chocolateyInstall.ps1"; - fs.write_contents(chocolatey_install_file_path, chocolatey_install_content, VCPKG_LINE_INFO); - - const std::string chocolatey_uninstall_content = create_chocolatey_uninstall_contents(binary_paragraph); - const fs::path chocolatey_uninstall_file_path = per_package_dir_path / "tools" / "chocolateyUninstall.ps1"; - fs.write_contents(chocolatey_uninstall_file_path, chocolatey_uninstall_content, VCPKG_LINE_INFO); - - const auto cmd_line = Strings::format(R"("%s" pack -OutputDirectory "%s" "%s" -NoDefaultExcludes)", - nuget_exe.u8string(), - exported_dir_path.u8string(), - nuspec_file_path.u8string()); - - const int exit_code = - System::cmd_execute_and_capture_output(cmd_line, System::get_clean_environment()).exit_code; - Checks::check_exit(VCPKG_LINE_INFO, exit_code == 0, "Error: NuGet package creation failed"); - } - } -} +)###"; + std::string chocolatey_uninstall_content = + Strings::replace_all(CONTENT_TEMPLATE, "@PACKAGE_FULLSTEM@", binary_paragraph.fullstem()); + return chocolatey_uninstall_content; + } + + void do_export(const std::vector& export_plan, + const VcpkgPaths& paths, + const Options& chocolatey_options) + { + Checks::check_exit( + VCPKG_LINE_INFO, chocolatey_options.maybe_maintainer.has_value(), "--x-maintainer option is required."); + + Files::Filesystem& fs = paths.get_filesystem(); + const fs::path vcpkg_root_path = paths.root; + const fs::path raw_exported_dir_path = vcpkg_root_path / "chocolatey"; + const fs::path exported_dir_path = vcpkg_root_path / "chocolatey_exports"; + const fs::path& nuget_exe = paths.get_tool_exe(Tools::NUGET); + + std::error_code ec; + fs.remove_all(raw_exported_dir_path, VCPKG_LINE_INFO); + fs.create_directory(raw_exported_dir_path, ec); + fs.remove_all(exported_dir_path, VCPKG_LINE_INFO); + fs.create_directory(exported_dir_path, ec); + + // execute the plan + std::map packages_version; + for (const ExportPlanAction& action : export_plan) + { + if (action.plan_type != ExportPlanType::ALREADY_BUILT) + { + Checks::unreachable(VCPKG_LINE_INFO); + } + + const BinaryParagraph& binary_paragraph = action.core_paragraph().value_or_exit(VCPKG_LINE_INFO); + auto norm_version = binary_paragraph.version; + + // normalize the version string to be separated by dots to be compliant with Nusepc. + norm_version = Strings::replace_all(std::move(norm_version), "-", "."); + norm_version = Strings::replace_all(std::move(norm_version), "_", "."); + norm_version = norm_version + chocolatey_options.maybe_version_suffix.value_or(""); + packages_version.insert(std::make_pair(binary_paragraph.spec.name(), norm_version)); + } + + for (const ExportPlanAction& action : export_plan) + { + const std::string display_name = action.spec.to_string(); + System::print2("Exporting package ", display_name, "...\n"); + + const fs::path per_package_dir_path = raw_exported_dir_path / action.spec.name(); + + const BinaryParagraph& binary_paragraph = action.core_paragraph().value_or_exit(VCPKG_LINE_INFO); + + const InstallDir dirs = InstallDir::from_destination_root( + per_package_dir_path / "installed", + action.spec.triplet().to_string(), + per_package_dir_path / "installed" / "vcpkg" / "info" / (binary_paragraph.fullstem() + ".list")); + + Install::install_package_and_write_listfile(paths, action.spec, dirs); + + const std::string nuspec_file_content = create_nuspec_file_contents( + per_package_dir_path.string(), binary_paragraph, packages_version, chocolatey_options); + const fs::path nuspec_file_path = + per_package_dir_path / Strings::concat(binary_paragraph.spec.name(), ".nuspec"); + fs.write_contents(nuspec_file_path, nuspec_file_content, VCPKG_LINE_INFO); + + fs.create_directory(per_package_dir_path / "tools", ec); + + const std::string chocolatey_install_content = create_chocolatey_install_contents(); + const fs::path chocolatey_install_file_path = per_package_dir_path / "tools" / "chocolateyInstall.ps1"; + fs.write_contents(chocolatey_install_file_path, chocolatey_install_content, VCPKG_LINE_INFO); + + const std::string chocolatey_uninstall_content = create_chocolatey_uninstall_contents(binary_paragraph); + const fs::path chocolatey_uninstall_file_path = per_package_dir_path / "tools" / "chocolateyUninstall.ps1"; + fs.write_contents(chocolatey_uninstall_file_path, chocolatey_uninstall_content, VCPKG_LINE_INFO); + + const auto cmd_line = Strings::format(R"("%s" pack -OutputDirectory "%s" "%s" -NoDefaultExcludes)", + nuget_exe.u8string(), + exported_dir_path.u8string(), + nuspec_file_path.u8string()); + + const int exit_code = + System::cmd_execute_and_capture_output(cmd_line, System::get_clean_environment()).exit_code; + Checks::check_exit(VCPKG_LINE_INFO, exit_code == 0, "Error: NuGet package creation failed"); + } + } +} -- cgit v1.2.3