diff options
| author | nicole mazzuca <mazzucan@outlook.com> | 2020-07-14 08:50:19 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-07-14 08:50:19 -0700 |
| commit | d2620cf02bf01bb3cd6873aa2ba7687644019ed0 (patch) | |
| tree | fda361dda33da62d10553519d4bb1acc2f49caf7 | |
| parent | e55460813578f6a8633bbf76ef96e10c1fa22382 (diff) | |
| download | vcpkg-d2620cf02bf01bb3cd6873aa2ba7687644019ed0.tar.gz vcpkg-d2620cf02bf01bb3cd6873aa2ba7687644019ed0.zip | |
[vcpkg formatting] Turn off DeriveLineEnding (#12368)
* [vcpkg formatting] Turn off DeriveLineEnding
* format
* Add newlines to the end of files
Since we're reformatting anyways
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 <vcpkg/cmakevars.h>
-
-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<const PackageSpec> specs) const override
- {
- for (auto&& spec : specs)
- dep_info_vars[spec] = {};
- }
-
- void load_tag_vars(Span<const FullPackageSpec> specs,
- const PortFileProvider::PortFileProvider& port_provider) const override
- {
- for (auto&& spec : specs)
- tag_vars[spec.package_spec] = {};
- Util::unused(port_provider);
- }
-
- Optional<const std::unordered_map<std::string, std::string>&> get_generic_triplet_vars(
- Triplet triplet) const override;
-
- Optional<const std::unordered_map<std::string, std::string>&> get_dep_info_vars(
- const PackageSpec& spec) const override;
-
- Optional<const std::unordered_map<std::string, std::string>&> get_tag_vars(
- const PackageSpec& spec) const override;
-
- mutable std::unordered_map<PackageSpec, std::unordered_map<std::string, std::string>> dep_info_vars;
- mutable std::unordered_map<PackageSpec, std::unordered_map<std::string, std::string>> tag_vars;
- mutable std::unordered_map<Triplet, std::unordered_map<std::string, std::string>> generic_triplet_vars;
- };
-}
+#pragma once + +#include <vcpkg/cmakevars.h> + +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<const PackageSpec> specs) const override + { + for (auto&& spec : specs) + dep_info_vars[spec] = {}; + } + + void load_tag_vars(Span<const FullPackageSpec> specs, + const PortFileProvider::PortFileProvider& port_provider) const override + { + for (auto&& spec : specs) + tag_vars[spec.package_spec] = {}; + Util::unused(port_provider); + } + + Optional<const std::unordered_map<std::string, std::string>&> get_generic_triplet_vars( + Triplet triplet) const override; + + Optional<const std::unordered_map<std::string, std::string>&> get_dep_info_vars( + const PackageSpec& spec) const override; + + Optional<const std::unordered_map<std::string, std::string>&> get_tag_vars( + const PackageSpec& spec) const override; + + mutable std::unordered_map<PackageSpec, std::unordered_map<std::string, std::string>> dep_info_vars; + mutable std::unordered_map<PackageSpec, std::unordered_map<std::string, std::string>> tag_vars; + mutable std::unordered_map<Triplet, std::unordered_map<std::string, std::string>> 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 <map>
-
-namespace vcpkg
-{
- template<class Key, class Value>
- struct Cache
- {
- template<class F>
- 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<Key, Value> m_cache;
- };
-}
+#pragma once + +#include <map> + +namespace vcpkg +{ + template<class Key, class Value> + struct Cache + { + template<class F> + 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<Key, Value> 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 <array>
-#include <cstddef>
-#include <initializer_list>
-#include <vector>
-
-namespace vcpkg
-{
- template<class T>
- struct Span
- {
- public:
- static_assert(std::is_object<T>::value, "Span<non-object-type> is illegal");
-
- using value_type = std::decay_t<T>;
- using element_type = T;
- using pointer = std::add_pointer_t<T>;
- using reference = std::add_lvalue_reference_t<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<size_t N>
- constexpr Span(T (&arr)[N]) noexcept : m_ptr(arr), m_count(N)
- {
- }
-
- template<size_t N, class = std::enable_if_t<std::is_const_v<T>>>
- constexpr Span(std::remove_const_t<T> (&arr)[N]) noexcept : m_ptr(arr), m_count(N)
- {
- }
-
- template<class Range,
- class = decltype(std::declval<Range>().data()),
- class = std::enable_if_t<!std::is_same<std::decay_t<Range>, Span>::value>>
- constexpr Span(Range&& v) noexcept : Span(v.data(), v.size())
- {
- static_assert(std::is_same<typename std::decay_t<Range>::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 <array> +#include <cstddef> +#include <initializer_list> +#include <vector> + +namespace vcpkg +{ + template<class T> + struct Span + { + public: + static_assert(std::is_object<T>::value, "Span<non-object-type> is illegal"); + + using value_type = std::decay_t<T>; + using element_type = T; + using pointer = std::add_pointer_t<T>; + using reference = std::add_lvalue_reference_t<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<size_t N> + constexpr Span(T (&arr)[N]) noexcept : m_ptr(arr), m_count(N) + { + } + + template<size_t N, class = std::enable_if_t<std::is_const_v<T>>> + constexpr Span(std::remove_const_t<T> (&arr)[N]) noexcept : m_ptr(arr), m_count(N) + { + } + + template<class Range, + class = decltype(std::declval<Range>().data()), + class = std::enable_if_t<!std::is_same<std::decay_t<Range>, Span>::value>> + constexpr Span(Range&& v) noexcept : Span(v.data(), v.size()) + { + static_assert(std::is_same<typename std::decay_t<Range>::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 <vcpkg/base/span.h>
-
-namespace vcpkg
-{
- template<class T>
- using View = Span<const T>;
-}
+#pragma once + +#include <vcpkg/base/span.h> + +namespace vcpkg +{ + template<class T> + using View = Span<const T>; +} 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 <vcpkg/base/expected.h>
-#include <vcpkg/base/files.h>
-
-#include <vcpkg/packagespec.h>
-#include <vcpkg/vcpkgpaths.h>
-
-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<std::unique_ptr<IBinaryProvider>> create_binary_provider_from_configs(View<std::string> args);
- ExpectedS<std::unique_ptr<IBinaryProvider>> create_binary_provider_from_configs_pure(const std::string& env_string,
- View<std::string> args);
-
- std::string generate_nuget_packages_config(const Dependencies::ActionPlan& action);
-
- void help_topic_binary_caching(const VcpkgPaths& paths);
-}
+#pragma once + +#include <vcpkg/base/expected.h> +#include <vcpkg/base/files.h> + +#include <vcpkg/packagespec.h> +#include <vcpkg/vcpkgpaths.h> + +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<std::unique_ptr<IBinaryProvider>> create_binary_provider_from_configs(View<std::string> args); + ExpectedS<std::unique_ptr<IBinaryProvider>> create_binary_provider_from_configs_pure(const std::string& env_string, + View<std::string> 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 <vcpkg/dependencies.h>
-#include <vcpkg/packagespec.h>
-#include <vcpkg/vcpkgpaths.h>
-
-#include <string>
-
-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 <vcpkg/dependencies.h> +#include <vcpkg/packagespec.h> +#include <vcpkg/vcpkgpaths.h> + +#include <string> + +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 <vcpkg/base/system.process.h>
-
-#include <vcpkg/vcpkgpaths.h>
-
-#include <string>
-#include <vector>
-
-namespace vcpkg
-{
- std::string make_cmake_cmd(const VcpkgPaths& paths,
- const fs::path& cmake_script,
- std::vector<System::CMakeVariable>&& pass_variables);
-}
+#include <vcpkg/base/system.process.h> + +#include <vcpkg/vcpkgpaths.h> + +#include <string> +#include <vector> + +namespace vcpkg +{ + std::string make_cmake_cmd(const VcpkgPaths& paths, + const fs::path& cmake_script, + std::vector<System::CMakeVariable>&& 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 <vcpkg/dependencies.h>
-#include <vcpkg/vcpkgpaths.h>
-
-#include <vector>
-
-namespace vcpkg::Export::Chocolatey
-{
- struct Options
- {
- Optional<std::string> maybe_maintainer;
- Optional<std::string> maybe_version_suffix;
- };
-
- void do_export(const std::vector<Dependencies::ExportPlanAction>& export_plan,
- const VcpkgPaths& paths,
- const Options& chocolatey_options);
-}
+#pragma once + +#include <vcpkg/dependencies.h> +#include <vcpkg/vcpkgpaths.h> + +#include <vector> + +namespace vcpkg::Export::Chocolatey +{ + struct Options + { + Optional<std::string> maybe_maintainer; + Optional<std::string> maybe_version_suffix; + }; + + void do_export(const std::vector<Dependencies::ExportPlanAction>& 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 <vcpkg/base/expected.h>
-#include <vcpkg/base/stringview.h>
-
-#include <string>
-#include <unordered_map>
-
-namespace vcpkg::PlatformExpression
-{
- // map of cmake variables and their values.
- using Context = std::unordered_map<std::string, std::string>;
-
- namespace detail
- {
- struct ExprImpl;
- }
- struct Expr
- {
- static Expr Identifier(StringView id);
- static Expr Not(Expr&& e);
- static Expr And(std::vector<Expr>&& exprs);
- static Expr Or(std::vector<Expr>&& 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<detail::ExprImpl>&& e);
- ~Expr();
-
- bool evaluate(const Context& context) const;
- bool is_empty() const { return !static_cast<bool>(underlying_); }
-
- private:
- std::unique_ptr<detail::ExprImpl> 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 :
- // <platform-expression>:
- // <platform-expression.not>
- // <platform-expression.and>
- // <platform-expression.or>
- // <platform-expression.simple>:
- // ( <platform-expression> )
- // <platform-expression.identifier>
- // <platform-expression.identifier>:
- // A lowercase alpha-numeric string
- // <platform-expression.not>:
- // <platform-expression.simple>
- // ! <platform-expression.simple>
- // <platform-expression.and>
- // <platform-expression.not>
- // <platform-expression.and> & <platform-expression.not>
- // <platform-expression.or>
- // <platform-expression.not>
- // <platform-expression.or> | <platform-expression.not>
- ExpectedS<Expr> parse_platform_expression(StringView expression, MultipleBinaryOperators multiple_binary_operators);
-}
+#pragma once + +#include <vcpkg/base/expected.h> +#include <vcpkg/base/stringview.h> + +#include <string> +#include <unordered_map> + +namespace vcpkg::PlatformExpression +{ + // map of cmake variables and their values. + using Context = std::unordered_map<std::string, std::string>; + + namespace detail + { + struct ExprImpl; + } + struct Expr + { + static Expr Identifier(StringView id); + static Expr Not(Expr&& e); + static Expr And(std::vector<Expr>&& exprs); + static Expr Or(std::vector<Expr>&& 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<detail::ExprImpl>&& e); + ~Expr(); + + bool evaluate(const Context& context) const; + bool is_empty() const { return !static_cast<bool>(underlying_); } + + private: + std::unique_ptr<detail::ExprImpl> 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 : + // <platform-expression>: + // <platform-expression.not> + // <platform-expression.and> + // <platform-expression.or> + // <platform-expression.simple>: + // ( <platform-expression> ) + // <platform-expression.identifier> + // <platform-expression.identifier>: + // A lowercase alpha-numeric string + // <platform-expression.not>: + // <platform-expression.simple> + // ! <platform-expression.simple> + // <platform-expression.and> + // <platform-expression.not> + // <platform-expression.and> & <platform-expression.not> + // <platform-expression.or> + // <platform-expression.not> + // <platform-expression.or> | <platform-expression.not> + ExpectedS<Expr> 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 <vcpkg/vcpkgpaths.h>
-
-namespace vcpkg::VisualStudio
-{
- std::vector<std::string> get_visual_studio_instances(const VcpkgPaths& paths);
-
- std::vector<Toolset> find_toolset_instances_preferred_first(const VcpkgPaths& paths);
-}
-
-#endif
+#pragma once + +#if defined(_WIN32) + +#include <vcpkg/vcpkgpaths.h> + +namespace vcpkg::VisualStudio +{ + std::vector<std::string> get_visual_studio_instances(const VcpkgPaths& paths); + + std::vector<Toolset> 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 <vcpkg-test/mockcmakevarprovider.h>
-
-namespace vcpkg::Test
-{
- Optional<const std::unordered_map<std::string, std::string>&> 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<const std::unordered_map<std::string, std::string>&> 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<const std::unordered_map<std::string, std::string>&> 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 <vcpkg-test/mockcmakevarprovider.h> + +namespace vcpkg::Test +{ + Optional<const std::unordered_map<std::string, std::string>&> 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<const std::unordered_map<std::string, std::string>&> 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<const std::unordered_map<std::string, std::string>&> 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 <vcpkg/base/checks.h>
-#include <vcpkg/base/files.h>
-#include <vcpkg/base/parse.h>
-#include <vcpkg/base/system.debug.h>
-#include <vcpkg/base/system.print.h>
-#include <vcpkg/base/system.process.h>
-
-#include <vcpkg/binarycaching.h>
-#include <vcpkg/binarycaching.private.h>
-#include <vcpkg/build.h>
-#include <vcpkg/dependencies.h>
-
-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<fs::path>&& read_dirs, std::vector<fs::path>&& 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<fs::path> 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<std::string>&& read_sources,
- std::vector<std::string>&& write_sources,
- std::vector<fs::path>&& read_configs,
- std::vector<fs::path>&& 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<std::pair<PackageSpec, NugetReference>> 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<std::string> 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<PackageSpec, NugetReference>& 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<std::string> m_read_sources;
- std::vector<std::string> m_write_sources;
-
- std::vector<fs::path> m_read_configs;
- std::vector<fs::path> m_write_configs;
-
- std::set<PackageSpec> m_restored;
- bool m_interactive;
- };
-
- struct MergeBinaryProviders : IBinaryProvider
- {
- explicit MergeBinaryProviders(std::vector<std::unique_ptr<IBinaryProvider>>&& 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<std::unique_ptr<IBinaryProvider>> 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"(<?xml version="1.0" encoding="utf-8"?>)");
- 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, "</", sl, '>');
- 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<std::unique_ptr<IBinaryProvider>> vcpkg::create_binary_provider_from_configs(View<std::string> 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<fs::path>& default_cache_path()
- {
- static auto cachepath = System::get_platform_cache_home().then([](fs::path p) -> ExpectedS<fs::path> {
- 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<fs::path> archives_to_read;
- std::vector<fs::path> archives_to_write;
-
- std::vector<std::string> sources_to_read;
- std::vector<std::string> sources_to_write;
-
- std::vector<fs::path> configs_to_read;
- std::vector<fs::path> 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<std::pair<SourceLoc, std::string>> 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<class T>
- void handle_readwrite(std::vector<T>& read,
- std::vector<T>& write,
- T&& t,
- const std::vector<std::pair<SourceLoc, std::string>>& 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<std::pair<SourceLoc, std::string>>&& 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<std::unique_ptr<IBinaryProvider>> vcpkg::create_binary_provider_from_configs_pure(
- const std::string& env_string, View<std::string> args)
-{
- State s;
-
- BinaryConfigParser default_parser("default,readwrite", "<defaults>", &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, "<command>", &s);
- arg_parser.parse();
- if (auto err = arg_parser.get_error()) return err->format();
- }
-
- std::vector<std::unique_ptr<IBinaryProvider>> providers;
- if (!s.archives_to_read.empty() || !s.archives_to_write.empty())
- providers.push_back(
- std::make_unique<ArchivesBinaryProvider>(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<NugetBinaryProvider>(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<MergeBinaryProviders>(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=<source>` options "
- "to every command line or setting the `VCPKG_BINARY_SOURCES` environment variable to a set of sources (Ex: "
- "\"<source>;<source>;...\"). 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[,<rw>]", "Adds the default file-based location.");
- tbl.format("files,<path>[,<rw>]", "Adds a custom file-based location.");
- tbl.format("nuget,<uri>[,<rw>]",
- "Adds a NuGet-based source; equivalent to the `-Source` parameter of the NuGet CLI.");
- tbl.format("nugetconfig,<path>[,<rw>]",
- "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 `<rw>` 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 <vcpkg/base/checks.h> +#include <vcpkg/base/files.h> +#include <vcpkg/base/parse.h> +#include <vcpkg/base/system.debug.h> +#include <vcpkg/base/system.print.h> +#include <vcpkg/base/system.process.h> + +#include <vcpkg/binarycaching.h> +#include <vcpkg/binarycaching.private.h> +#include <vcpkg/build.h> +#include <vcpkg/dependencies.h> + +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<fs::path>&& read_dirs, std::vector<fs::path>&& 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<fs::path> 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<std::string>&& read_sources, + std::vector<std::string>&& write_sources, + std::vector<fs::path>&& read_configs, + std::vector<fs::path>&& 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<std::pair<PackageSpec, NugetReference>> 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<std::string> 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<PackageSpec, NugetReference>& 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<std::string> m_read_sources; + std::vector<std::string> m_write_sources; + + std::vector<fs::path> m_read_configs; + std::vector<fs::path> m_write_configs; + + std::set<PackageSpec> m_restored; + bool m_interactive; + }; + + struct MergeBinaryProviders : IBinaryProvider + { + explicit MergeBinaryProviders(std::vector<std::unique_ptr<IBinaryProvider>>&& 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<std::unique_ptr<IBinaryProvider>> 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"(<?xml version="1.0" encoding="utf-8"?>)"); + 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, "</", sl, '>'); + 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<std::unique_ptr<IBinaryProvider>> vcpkg::create_binary_provider_from_configs(View<std::string> 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<fs::path>& default_cache_path() + { + static auto cachepath = System::get_platform_cache_home().then([](fs::path p) -> ExpectedS<fs::path> { + 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<fs::path> archives_to_read; + std::vector<fs::path> archives_to_write; + + std::vector<std::string> sources_to_read; + std::vector<std::string> sources_to_write; + + std::vector<fs::path> configs_to_read; + std::vector<fs::path> 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<std::pair<SourceLoc, std::string>> 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<class T> + void handle_readwrite(std::vector<T>& read, + std::vector<T>& write, + T&& t, + const std::vector<std::pair<SourceLoc, std::string>>& 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<std::pair<SourceLoc, std::string>>&& 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<std::unique_ptr<IBinaryProvider>> vcpkg::create_binary_provider_from_configs_pure( + const std::string& env_string, View<std::string> args) +{ + State s; + + BinaryConfigParser default_parser("default,readwrite", "<defaults>", &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, "<command>", &s); + arg_parser.parse(); + if (auto err = arg_parser.get_error()) return err->format(); + } + + std::vector<std::unique_ptr<IBinaryProvider>> providers; + if (!s.archives_to_read.empty() || !s.archives_to_write.empty()) + providers.push_back( + std::make_unique<ArchivesBinaryProvider>(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<NugetBinaryProvider>(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<MergeBinaryProviders>(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=<source>` options " + "to every command line or setting the `VCPKG_BINARY_SOURCES` environment variable to a set of sources (Ex: " + "\"<source>;<source>;...\"). 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[,<rw>]", "Adds the default file-based location."); + tbl.format("files,<path>[,<rw>]", "Adds a custom file-based location."); + tbl.format("nuget,<uri>[,<rw>]", + "Adds a NuGet-based source; equivalent to the `-Source` parameter of the NuGet CLI."); + tbl.format("nugetconfig,<path>[,<rw>]", + "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 `<rw>` 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 <vcpkg/base/cache.h>
-#include <vcpkg/base/checks.h>
-#include <vcpkg/base/chrono.h>
-#include <vcpkg/base/enums.h>
-#include <vcpkg/base/hash.h>
-#include <vcpkg/base/optional.h>
-#include <vcpkg/base/stringliteral.h>
-#include <vcpkg/base/system.debug.h>
-#include <vcpkg/base/system.print.h>
-#include <vcpkg/base/system.process.h>
-#include <vcpkg/base/util.h>
-
-#include <vcpkg/binarycaching.h>
-#include <vcpkg/build.h>
-#include <vcpkg/buildenvironment.h>
-#include <vcpkg/commands.h>
-#include <vcpkg/dependencies.h>
-#include <vcpkg/globalstate.h>
-#include <vcpkg/help.h>
-#include <vcpkg/input.h>
-#include <vcpkg/metrics.h>
-#include <vcpkg/paragraphs.h>
-#include <vcpkg/postbuildlint.h>
-#include <vcpkg/statusparagraphs.h>
-#include <vcpkg/vcpkglib.h>
-
-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<PackageSpec, 1>{full_spec.package_spec});
-
- StatusParagraphs status_db = database_load_check(paths);
-
- auto action_plan = Dependencies::create_feature_install_plan(
- provider, var_provider, std::vector<FullPackageSpec>{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<LinkageType> 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<std::string, std::string> 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 <NUL")",
- toolset.vcvarsall.u8string(),
- Strings::join(" ", toolset.vcvarsall_options),
- arch,
- target,
- tonull);
- }
-
- static std::unique_ptr<BinaryControlFile> create_binary_control_file(
- const SourceParagraph& source_paragraph,
- Triplet triplet,
- const BuildInfo& build_info,
- const std::string& abi_tag,
- const std::vector<FeatureSpec>& core_dependencies)
- {
- auto bcf = std::make_unique<BinaryControlFile>();
- 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<System::CMakeVariable>& out_vars)
- {
- Util::Vectors::append(&out_vars,
- std::initializer_list<System::CMakeVariable>{
- {"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<System::CMakeVariable> 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<System::CMakeVariable> 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<System::CMakeVariable> 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<std::string> 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<BinaryControlFile> 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<AbiEntry>& 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<AbiTagAndFile> compute_abi_tag(const VcpkgPaths& paths,
- const Dependencies::InstallPlanAction& action,
- Span<const AbiEntry> dependency_abis)
- {
- auto& fs = paths.get_filesystem();
- Triplet triplet = action.spec.triplet();
-
- std::vector<AbiEntry> 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<std::string> 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] <abientries for ", action.spec, ">\n");
- for (auto&& entry : abi_tag_entries)
- {
- Strings::append(message, "[DEBUG] ", entry.key, "|", entry.value, "\n");
- }
- Strings::append(message, "[DEBUG] </abientries>\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<AbiEntry> 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<PreBuildInfo>(
- 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<FeatureSpec> 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<AbiEntry> 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<BinaryControlFile>(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<BuildPolicy, bool> 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<Parse::Paragraph> 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<std::string, std::string>& 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<std::pair<std::string, VcpkgTripletVar>> 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<std::string>{variable_value};
- break;
- case VcpkgTripletVar::VISUAL_STUDIO_PATH:
- visual_studio_path = variable_value.empty() ? nullopt : Optional<fs::path>{variable_value};
- break;
- case VcpkgTripletVar::CHAINLOAD_TOOLCHAIN_FILE:
- external_toolchain_file = variable_value.empty() ? nullopt : Optional<std::string>{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<std::string>{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<BinaryControlFile>&& bcf)
- : code(code), binary_control_file(std::move(bcf))
- {
- }
- ExtendedBuildResult::ExtendedBuildResult(BuildResult code, std::vector<FeatureSpec>&& 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 <vcpkg/base/cache.h> +#include <vcpkg/base/checks.h> +#include <vcpkg/base/chrono.h> +#include <vcpkg/base/enums.h> +#include <vcpkg/base/hash.h> +#include <vcpkg/base/optional.h> +#include <vcpkg/base/stringliteral.h> +#include <vcpkg/base/system.debug.h> +#include <vcpkg/base/system.print.h> +#include <vcpkg/base/system.process.h> +#include <vcpkg/base/util.h> + +#include <vcpkg/binarycaching.h> +#include <vcpkg/build.h> +#include <vcpkg/buildenvironment.h> +#include <vcpkg/commands.h> +#include <vcpkg/dependencies.h> +#include <vcpkg/globalstate.h> +#include <vcpkg/help.h> +#include <vcpkg/input.h> +#include <vcpkg/metrics.h> +#include <vcpkg/paragraphs.h> +#include <vcpkg/postbuildlint.h> +#include <vcpkg/statusparagraphs.h> +#include <vcpkg/vcpkglib.h> + +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<PackageSpec, 1>{full_spec.package_spec}); + + StatusParagraphs status_db = database_load_check(paths); + + auto action_plan = Dependencies::create_feature_install_plan( + provider, var_provider, std::vector<FullPackageSpec>{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<LinkageType> 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<std::string, std::string> 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 <NUL")", + toolset.vcvarsall.u8string(), + Strings::join(" ", toolset.vcvarsall_options), + arch, + target, + tonull); + } + + static std::unique_ptr<BinaryControlFile> create_binary_control_file( + const SourceParagraph& source_paragraph, + Triplet triplet, + const BuildInfo& build_info, + const std::string& abi_tag, + const std::vector<FeatureSpec>& core_dependencies) + { + auto bcf = std::make_unique<BinaryControlFile>(); + 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<System::CMakeVariable>& out_vars) + { + Util::Vectors::append(&out_vars, + std::initializer_list<System::CMakeVariable>{ + {"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<System::CMakeVariable> 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<System::CMakeVariable> 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<System::CMakeVariable> 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<std::string> 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<BinaryControlFile> 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<AbiEntry>& 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<AbiTagAndFile> compute_abi_tag(const VcpkgPaths& paths, + const Dependencies::InstallPlanAction& action, + Span<const AbiEntry> dependency_abis) + { + auto& fs = paths.get_filesystem(); + Triplet triplet = action.spec.triplet(); + + std::vector<AbiEntry> 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<std::string> 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] <abientries for ", action.spec, ">\n"); + for (auto&& entry : abi_tag_entries) + { + Strings::append(message, "[DEBUG] ", entry.key, "|", entry.value, "\n"); + } + Strings::append(message, "[DEBUG] </abientries>\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<AbiEntry> 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<PreBuildInfo>( + 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<FeatureSpec> 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<AbiEntry> 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<BinaryControlFile>(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<BuildPolicy, bool> 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<Parse::Paragraph> 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<std::string, std::string>& 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<std::pair<std::string, VcpkgTripletVar>> 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<std::string>{variable_value}; + break; + case VcpkgTripletVar::VISUAL_STUDIO_PATH: + visual_studio_path = variable_value.empty() ? nullopt : Optional<fs::path>{variable_value}; + break; + case VcpkgTripletVar::CHAINLOAD_TOOLCHAIN_FILE: + external_toolchain_file = variable_value.empty() ? nullopt : Optional<std::string>{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<std::string>{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<BinaryControlFile>&& bcf) + : code(code), binary_control_file(std::move(bcf)) + { + } + ExtendedBuildResult::ExtendedBuildResult(BuildResult code, std::vector<FeatureSpec>&& 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 <vcpkg/buildenvironment.h>
-
-namespace vcpkg
-{
- std::string make_cmake_cmd(const VcpkgPaths& paths,
- const fs::path& cmake_script,
- std::vector<System::CMakeVariable>&& 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 <vcpkg/buildenvironment.h> + +namespace vcpkg +{ + std::string make_cmake_cmd(const VcpkgPaths& paths, + const fs::path& cmake_script, + std::vector<System::CMakeVariable>&& 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 <vcpkg/base/checks.h>
-#include <vcpkg/base/files.h>
-#include <vcpkg/base/system.print.h>
-
-#include <vcpkg/commands.h>
-#include <vcpkg/vcpkgcmdarguments.h>
-
-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 <vcpkg/base/checks.h> +#include <vcpkg/base/files.h> +#include <vcpkg/base/system.print.h> + +#include <vcpkg/commands.h> +#include <vcpkg/vcpkgcmdarguments.h> + +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 <vcpkg/base/strings.h>
-#include <vcpkg/base/system.print.h>
-#include <vcpkg/base/util.h>
-
-#include <vcpkg/commands.h>
-#include <vcpkg/dependencies.h>
-#include <vcpkg/help.h>
-#include <vcpkg/input.h>
-#include <vcpkg/install.h>
-#include <vcpkg/packagespec.h>
-
-#include <vector>
-
-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<CommandSwitch, 3> 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<CommandSetting, 2> 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<std::string> features;
- std::vector<std::string> 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<std::string, SortMode> 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<PackageDependInfo>& 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<PackageDependInfo>& depend_info)
- {
- std::string s;
- s.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
- s.append("<DirectedGraph xmlns=\"http://schemas.microsoft.com/vs/2009/dgml\">");
-
- std::string nodes, links;
- for (const auto& package : depend_info)
- {
- const std::string name = package.package;
- nodes.append(Strings::format("<Node Id=\"%s\" />", name));
-
- // Iterate over dependencies.
- for (const auto& d : package.dependencies)
- {
- links.append(Strings::format("<Link Source=\"%s\" Target=\"%s\" />", name, d));
- }
- }
-
- s.append(Strings::format("<Nodes>%s</Nodes>", nodes));
-
- s.append(Strings::format("<Links>%s</Links>", links));
-
- s.append("</DirectedGraph>");
- return s;
- }
-
- std::string create_graph_as_string(const std::unordered_set<std::string>& switches,
- const std::vector<PackageDependInfo>& 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<std::string, PackageDependInfo>& 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<PackageDependInfo> extract_depend_info(const std::vector<const InstallPlanAction*>& install_actions,
- const int max_depth)
- {
- std::map<std::string, PackageDependInfo> package_dependencies;
- for (const InstallPlanAction* pia : install_actions)
- {
- const InstallPlanAction& install_action = *pia;
-
- const std::vector<std::string> dependencies = Util::fmap(
- install_action.package_dependencies, [](const PackageSpec& spec) { return spec.name(); });
-
- std::unordered_set<std::string> 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<PackageDependInfo> 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<FullPackageSpec> 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<const InstallPlanAction*> 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<PackageDependInfo> 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<const SourceControlFile*> 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<const SourceControlFile*>(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 <vcpkg/base/strings.h> +#include <vcpkg/base/system.print.h> +#include <vcpkg/base/util.h> + +#include <vcpkg/commands.h> +#include <vcpkg/dependencies.h> +#include <vcpkg/help.h> +#include <vcpkg/input.h> +#include <vcpkg/install.h> +#include <vcpkg/packagespec.h> + +#include <vector> + +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<CommandSwitch, 3> 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<CommandSetting, 2> 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<std::string> features; + std::vector<std::string> 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<std::string, SortMode> 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<PackageDependInfo>& 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<PackageDependInfo>& depend_info) + { + std::string s; + s.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>"); + s.append("<DirectedGraph xmlns=\"http://schemas.microsoft.com/vs/2009/dgml\">"); + + std::string nodes, links; + for (const auto& package : depend_info) + { + const std::string name = package.package; + nodes.append(Strings::format("<Node Id=\"%s\" />", name)); + + // Iterate over dependencies. + for (const auto& d : package.dependencies) + { + links.append(Strings::format("<Link Source=\"%s\" Target=\"%s\" />", name, d)); + } + } + + s.append(Strings::format("<Nodes>%s</Nodes>", nodes)); + + s.append(Strings::format("<Links>%s</Links>", links)); + + s.append("</DirectedGraph>"); + return s; + } + + std::string create_graph_as_string(const std::unordered_set<std::string>& switches, + const std::vector<PackageDependInfo>& 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<std::string, PackageDependInfo>& 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<PackageDependInfo> extract_depend_info(const std::vector<const InstallPlanAction*>& install_actions, + const int max_depth) + { + std::map<std::string, PackageDependInfo> package_dependencies; + for (const InstallPlanAction* pia : install_actions) + { + const InstallPlanAction& install_action = *pia; + + const std::vector<std::string> dependencies = Util::fmap( + install_action.package_dependencies, [](const PackageSpec& spec) { return spec.name(); }); + + std::unordered_set<std::string> 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<PackageDependInfo> 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<FullPackageSpec> 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<const InstallPlanAction*> 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<PackageDependInfo> 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<const SourceControlFile*> 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<const SourceControlFile*>(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 <vcpkg/base/system.print.h>
-
-#include <vcpkg/commands.h>
-#include <vcpkg/help.h>
-#include <vcpkg/visualstudio.h>
-
-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 <vcpkg/base/system.print.h> + +#include <vcpkg/commands.h> +#include <vcpkg/help.h> +#include <vcpkg/visualstudio.h> + +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 <vcpkg/base/system.print.h>
-#include <vcpkg/base/system.process.h>
-
-#include <vcpkg/commands.h>
-#include <vcpkg/export.chocolatey.h>
-#include <vcpkg/export.h>
-#include <vcpkg/install.h>
-
-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<std::string, std::string>& packages_version)
- {
- static constexpr auto CONTENT_TEMPLATE = R"(<dependency id="@PACKAGE_ID@" version="[@PACKAGE_VERSION@]" />)";
-
- 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<std::string, std::string>& packages_version,
- const Options& chocolatey_options)
- {
+#include "pch.h" + +#include <vcpkg/base/system.print.h> +#include <vcpkg/base/system.process.h> + +#include <vcpkg/commands.h> +#include <vcpkg/export.chocolatey.h> +#include <vcpkg/export.h> +#include <vcpkg/install.h> + +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<std::string, std::string>& packages_version) + { + static constexpr auto CONTENT_TEMPLATE = R"(<dependency id="@PACKAGE_ID@" version="[@PACKAGE_VERSION@]" />)"; + + 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<std::string, std::string>& packages_version, + const Options& chocolatey_options) + { static constexpr auto CONTENT_TEMPLATE = R"(<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
@@ -57,30 +57,30 @@ namespace vcpkg::Export::Chocolatey <file src="@EXPORTED_ROOT_DIR@\tools\**" target="tools" />
</files>
</package>
-)";
- 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<ExportPlanAction>& 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<std::string, std::string> 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<ExportPlanAction>& 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<std::string, std::string> 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"); + } + } +} |
