diff options
| author | Thomas Fussell <thomas.fussell@gmail.com> | 2017-03-12 17:44:06 -0400 |
|---|---|---|
| committer | Thomas Fussell <thomas.fussell@gmail.com> | 2017-03-12 17:44:06 -0400 |
| commit | 2f42035ab43dd50cd863b51944aa099a99ae60f0 (patch) | |
| tree | 4b6cfd43955e946721c58028fd8564c861d5c331 /toolsrc/src | |
| parent | e02e85626f3206feda86a6f5757009005e0cfe3e (diff) | |
| parent | 1c9873a0daf625f67474aaf3e163c592c27ecb65 (diff) | |
| download | vcpkg-2f42035ab43dd50cd863b51944aa099a99ae60f0.tar.gz vcpkg-2f42035ab43dd50cd863b51944aa099a99ae60f0.zip | |
Merge branch 'master' of git://github.com/Microsoft/vcpkg
Diffstat (limited to 'toolsrc/src')
61 files changed, 2717 insertions, 1816 deletions
diff --git a/toolsrc/src/BinaryParagraph.cpp b/toolsrc/src/BinaryParagraph.cpp index ad85a1f8a..8605cd276 100644 --- a/toolsrc/src/BinaryParagraph.cpp +++ b/toolsrc/src/BinaryParagraph.cpp @@ -1,3 +1,4 @@ +#include "pch.h" #include "BinaryParagraph.h" #include "vcpkglib_helpers.h" #include "vcpkg_Checks.h" @@ -22,22 +23,6 @@ namespace vcpkg static const std::string DEPENDS = "Depends"; } - static const std::vector<std::string>& get_list_of_valid_fields() - { - static const std::vector<std::string> valid_fields = - { - BinaryParagraphRequiredField::PACKAGE, - BinaryParagraphRequiredField::VERSION, - BinaryParagraphRequiredField::ARCHITECTURE, - - BinaryParagraphOptionalField::DESCRIPTION, - BinaryParagraphOptionalField::MAINTAINER, - BinaryParagraphOptionalField::DEPENDS - }; - - return valid_fields; - } - BinaryParagraph::BinaryParagraph() = default; BinaryParagraph::BinaryParagraph(std::unordered_map<std::string, std::string> fields) @@ -70,7 +55,7 @@ namespace vcpkg std::string BinaryParagraph::displayname() const { - return Strings::format("%s:%s", this->spec.name(), this->spec.target_triplet()); + return this->spec.display_name(); } std::string BinaryParagraph::dir() const diff --git a/toolsrc/src/BuildInfo.cpp b/toolsrc/src/BuildInfo.cpp deleted file mode 100644 index f151a3ea5..000000000 --- a/toolsrc/src/BuildInfo.cpp +++ /dev/null @@ -1,164 +0,0 @@ -#include "BuildInfo.h" -#include "vcpkg_Checks.h" -#include "vcpkglib_helpers.h" - -namespace vcpkg { namespace PostBuildLint -{ - const ConfigurationType& BuildType::config() const - { - return this->m_config; - } - - const LinkageType& BuildType::linkage() const - { - return this->m_linkage; - } - - std::regex BuildType::crt_regex() const - { - const std::regex r(this->m_crt_regex_as_string, std::regex_constants::icase); - return r; - } - - std::string BuildType::toString() const - { - const std::string s = Strings::format("[%s,%s]", to_string(this->m_config), to_string(this->m_linkage)); - return s; - } - - bool operator==(const BuildType& lhs, const BuildType& rhs) - { - return lhs.config() == rhs.config() && lhs.linkage() == rhs.linkage(); - } - - bool operator!=(const BuildType& lhs, const BuildType& rhs) - { - return !(lhs == rhs); - } - - // - namespace BuildInfoRequiredField - { - static const std::string CRT_LINKAGE = "CRTLinkage"; - static const std::string LIBRARY_LINKAGE = "LibraryLinkage"; - } - - BuildInfo BuildInfo::create(const std::unordered_map<std::string, std::string>& pgh) - { - BuildInfo build_info; - build_info.crt_linkage = details::required_field(pgh, BuildInfoRequiredField::CRT_LINKAGE); - build_info.library_linkage = details::required_field(pgh, BuildInfoRequiredField::LIBRARY_LINKAGE); - - return build_info; - } - - const BuildType BuildType::DEBUG_STATIC = BuildType(ConfigurationType::DEBUG, LinkageType::STATIC, R"(/DEFAULTLIB:LIBCMTD)"); - const BuildType BuildType::DEBUG_DYNAMIC = BuildType(ConfigurationType::DEBUG, LinkageType::DYNAMIC, R"(/DEFAULTLIB:MSVCRTD)"); - const BuildType BuildType::RELEASE_STATIC = BuildType(ConfigurationType::RELEASE, LinkageType::STATIC, R"(/DEFAULTLIB:LIBCMT[^D])"); - const BuildType BuildType::RELEASE_DYNAMIC = BuildType(ConfigurationType::RELEASE, LinkageType::DYNAMIC, R"(/DEFAULTLIB:MSVCRT[^D])"); - - LinkageType linkage_type_value_of(const std::string& as_string) - - { - if (as_string == "dynamic") - { - return LinkageType::DYNAMIC; - } - - if (as_string == "static") - { - return LinkageType::STATIC; - } - - return LinkageType::UNKNOWN; - } - - std::string to_string(const LinkageType& build_info) - { - switch (build_info) - { - case LinkageType::STATIC: - return "static"; - case LinkageType::DYNAMIC: - return "dynamic"; - default: - Checks::unreachable(); - } - } - - std::string to_string(const ConfigurationType& conf) - { - switch (conf) - { - case ConfigurationType::DEBUG: - return "Debug"; - case ConfigurationType::RELEASE: - return "Release"; - default: - Checks::unreachable(); - } - } - - BuildType BuildType::value_of(const ConfigurationType& config, const LinkageType& linkage) - { - if (config == ConfigurationType::DEBUG && linkage == LinkageType::STATIC) - { - return DEBUG_STATIC; - } - - if (config == ConfigurationType::DEBUG && linkage == LinkageType::DYNAMIC) - { - return DEBUG_DYNAMIC; - } - - if (config == ConfigurationType::RELEASE && linkage == LinkageType::STATIC) - { - return RELEASE_STATIC; - } - - if (config == ConfigurationType::RELEASE && linkage == LinkageType::DYNAMIC) - { - return RELEASE_DYNAMIC; - } - - Checks::unreachable(); - } - - BuildInfo read_build_info(const fs::path& filepath) - { - const std::vector<std::unordered_map<std::string, std::string>> pghs = Paragraphs::get_paragraphs(filepath); - Checks::check_exit(pghs.size() == 1, "Invalid BUILD_INFO file for package"); - - return BuildInfo::create(pghs[0]); - } - - const OutdatedDynamicCrt OutdatedDynamicCrt::MSVCP100_DLL = OutdatedDynamicCrt("msvcp100.dll", R"(msvcp100\.dll)"); - const OutdatedDynamicCrt OutdatedDynamicCrt::MSVCP100D_DLL = OutdatedDynamicCrt("msvcp100d.dll", R"(msvcp100d\.dll)"); - const OutdatedDynamicCrt OutdatedDynamicCrt::MSVCP110_DLL = OutdatedDynamicCrt("msvcp110.dll", R"(msvcp110\.dll)"); - const OutdatedDynamicCrt OutdatedDynamicCrt::MSVCP110_WIN_DLL = OutdatedDynamicCrt("msvcp110_win.dll", R"(msvcp110_win\.dll)"); - const OutdatedDynamicCrt OutdatedDynamicCrt::MSVCP120_DLL = OutdatedDynamicCrt("msvcp120.dll", R"(msvcp120\.dll)"); - const OutdatedDynamicCrt OutdatedDynamicCrt::MSVCP120_CLR0400_DLL = OutdatedDynamicCrt("msvcp120_clr0400.dll", R"(msvcp120_clr0400\.dll)"); - const OutdatedDynamicCrt OutdatedDynamicCrt::MSVCP60_DLL = OutdatedDynamicCrt("msvcp60.dll", R"(msvcp60\.dll)"); - const OutdatedDynamicCrt OutdatedDynamicCrt::MSVCP_WIN_DLL = OutdatedDynamicCrt("msvcp60.dll", R"(msvcp60\.dll)");; - - const OutdatedDynamicCrt OutdatedDynamicCrt::MSVCR100_DLL = OutdatedDynamicCrt("msvcr100.dll", R"(msvcr100\.dll)"); - const OutdatedDynamicCrt OutdatedDynamicCrt::MSVCR100D_DLL = OutdatedDynamicCrt("msvcr100d.dll", R"(msvcr100d\.dll)"); - const OutdatedDynamicCrt OutdatedDynamicCrt::MSVCR100_CLR0400_DLL = OutdatedDynamicCrt("msvcr100_clr0400.dll", R"(msvcr100_clr0400\.dll)"); - const OutdatedDynamicCrt OutdatedDynamicCrt::MSVCR110_DLL = OutdatedDynamicCrt("msvcr110.dll", R"(msvcr110\.dll)"); - const OutdatedDynamicCrt OutdatedDynamicCrt::MSVCR120_DLL = OutdatedDynamicCrt("msvcr120.dll", R"(msvcr120\.dll)"); - const OutdatedDynamicCrt OutdatedDynamicCrt::MSVCR120_CLR0400_DLL = OutdatedDynamicCrt("msvcr120_clr0400.dll", R"(msvcr120_clr0400\.dll)"); - const OutdatedDynamicCrt OutdatedDynamicCrt::MSVCRT_DLL = OutdatedDynamicCrt("msvcrt.dll", R"(msvcrt\.dll)"); - const OutdatedDynamicCrt OutdatedDynamicCrt::MSVCRT20_DLL = OutdatedDynamicCrt("msvcrt20.dll", R"(msvcrt20\.dll)");; - const OutdatedDynamicCrt OutdatedDynamicCrt::MSVCRT40_DLL = OutdatedDynamicCrt("msvcrt40.dll", R"(msvcrt40\.dll)");; - - std::regex OutdatedDynamicCrt::crt_regex() const - { - const std::regex r(this->m_crt_regex_as_string, std::regex_constants::icase); - return r; - } - - const std::string& OutdatedDynamicCrt::toString() const - { - return this->m_dll_name; - } -}} diff --git a/toolsrc/src/MachineType.cpp b/toolsrc/src/MachineType.cpp new file mode 100644 index 000000000..81012234d --- /dev/null +++ b/toolsrc/src/MachineType.cpp @@ -0,0 +1,42 @@ +#include "pch.h" +#include "MachineType.h" +#include "vcpkg_Checks.h" + +namespace vcpkg +{ + MachineType getMachineType(const uint16_t value) + { + MachineType t = static_cast<MachineType>(value); + switch (t) + { + case MachineType::UNKNOWN: + case MachineType::AM33: + case MachineType::AMD64: + case MachineType::ARM: + case MachineType::ARM64: + case MachineType::ARMNT: + case MachineType::EBC: + case MachineType::I386: + case MachineType::IA64: + case MachineType::M32R: + case MachineType::MIPS16: + case MachineType::MIPSFPU: + case MachineType::MIPSFPU16: + case MachineType::POWERPC: + case MachineType::POWERPCFP: + case MachineType::R4000: + case MachineType::RISCV32: + case MachineType::RISCV64: + case MachineType::RISCV128: + case MachineType::SH3: + case MachineType::SH3DSP: + case MachineType::SH4: + case MachineType::SH5: + case MachineType::THUMB: + case MachineType::WCEMIPSV2: + return t; + default: + Checks::exit_with_message("Unknown machine type code 0x%x", value); + } + } +} diff --git a/toolsrc/src/Paragraphs.cpp b/toolsrc/src/Paragraphs.cpp index 823b4a85e..cb90ed1ec 100644 --- a/toolsrc/src/Paragraphs.cpp +++ b/toolsrc/src/Paragraphs.cpp @@ -1,13 +1,12 @@ +#include "pch.h" #include "Paragraphs.h" #include "vcpkg_Files.h" -namespace vcpkg { namespace Paragraphs +namespace vcpkg::Paragraphs { struct Parser { - Parser(const char* c, const char* e) : cur(c), end(e) - { - } + Parser(const char* c, const char* e) : cur(c), end(e) { } private: const char* cur; @@ -41,8 +40,8 @@ namespace vcpkg { namespace Paragraphs static bool is_alphanum(char ch) { return (ch >= 'A' && ch <= 'Z') - || (ch >= 'a' && ch <= 'z') - || (ch >= '0' && ch <= '9'); + || (ch >= 'a' && ch <= 'z') + || (ch >= '0' && ch <= '9'); } static bool is_lineend(char ch) @@ -100,7 +99,7 @@ namespace vcpkg { namespace Paragraphs auto begin_fieldname = cur; while (is_alphanum(ch) || ch == '-') next(ch); - Checks::check_throw(ch == ':', "Expected ':'"); + Checks::check_exit(ch == ':', "Expected ':'"); fieldname = std::string(begin_fieldname, cur); // skip ': ' @@ -118,7 +117,7 @@ namespace vcpkg { namespace Paragraphs get_fieldname(ch, fieldname); auto it = fields.find(fieldname); - Checks::check_throw(it == fields.end(), "Duplicate field"); + Checks::check_exit(it == fields.end(), "Duplicate field"); get_fieldvalue(ch, fieldvalue); @@ -153,11 +152,76 @@ namespace vcpkg { namespace Paragraphs std::vector<std::unordered_map<std::string, std::string>> get_paragraphs(const fs::path& control_path) { - return parse_paragraphs(Files::read_contents(control_path).get_or_throw()); + const expected<std::string> contents = Files::read_contents(control_path); + if (auto spgh = contents.get()) + { + return parse_paragraphs(*spgh); + } + + Checks::exit_with_message("Error while reading %s: %s", control_path.generic_string(), contents.error_code().message()); } std::vector<std::unordered_map<std::string, std::string>> parse_paragraphs(const std::string& str) { return Parser(str.c_str(), str.c_str() + str.size()).get_paragraphs(); } -}} + + expected<SourceParagraph> try_load_port(const fs::path& path) + { + try + { + auto pghs = get_paragraphs(path / "CONTROL"); + Checks::check_exit(pghs.size() == 1, "Invalid control file at %s\\CONTROL", path.string()); + return SourceParagraph(pghs[0]); + } + catch (std::runtime_error const&) {} + + return std::errc::no_such_file_or_directory; + } + + expected<BinaryParagraph> try_load_cached_package(const vcpkg_paths& paths, const package_spec& spec) + { + const fs::path path = paths.package_dir(spec) / "CONTROL"; + + auto control_contents_maybe = Files::read_contents(path); + if (auto control_contents = control_contents_maybe.get()) + { + std::vector<std::unordered_map<std::string, std::string>> pghs; + try + { + pghs = parse_paragraphs(*control_contents); + } + catch (std::runtime_error) {} + Checks::check_exit(pghs.size() == 1, "Invalid control file at %s", path.string()); + return BinaryParagraph(pghs[0]); + } + return control_contents_maybe.error_code(); + } + + std::vector<SourceParagraph> load_all_ports(const fs::path& ports_dir) + { + std::vector<SourceParagraph> output; + for (auto it = fs::directory_iterator(ports_dir); it != fs::directory_iterator(); ++it) + { + const fs::path& path = it->path(); + expected<SourceParagraph> source_paragraph = try_load_port(path); + if (auto srcpgh = source_paragraph.get()) + { + output.emplace_back(std::move(*srcpgh)); + } + } + + return output; + } + + std::map<std::string, std::string> extract_port_names_and_versions(const std::vector<SourceParagraph>& source_paragraphs) + { + std::map<std::string, std::string> names_and_versions; + for (const SourceParagraph& port : source_paragraphs) + { + names_and_versions.emplace(port.name, port.version); + } + + return names_and_versions; + } +} diff --git a/toolsrc/src/post_build_lint.cpp b/toolsrc/src/PostBuildLint.cpp index 4f0adf677..8abf71bb3 100644 --- a/toolsrc/src/post_build_lint.cpp +++ b/toolsrc/src/PostBuildLint.cpp @@ -1,13 +1,14 @@ +#include "pch.h" #include "vcpkg_paths.h" #include "package_spec.h" #include "vcpkg_Files.h" -#include <functional> #include "vcpkg_System.h" +#include "vcpkg_Environment.h" #include "coff_file_reader.h" -#include "BuildInfo.h" -#include <regex> +#include "PostBuildLint_BuildInfo.h" +#include "PostBuildLint_BuildType.h" -namespace vcpkg { namespace PostBuildLint +namespace vcpkg::PostBuildLint { enum class lint_status { @@ -15,7 +16,41 @@ namespace vcpkg { namespace PostBuildLint ERROR_DETECTED = 1 }; - static const fs::path DUMPBIN_EXE = R"(%VS140COMNTOOLS%\..\..\VC\bin\dumpbin.exe)"; + struct OutdatedDynamicCrt + { + std::string name; + std::regex regex; + + OutdatedDynamicCrt(const std::string& name, const std::string& regex_as_string) + : name(name), + regex(std::regex(regex_as_string, std::regex_constants::icase)) {} + }; + + const std::vector<OutdatedDynamicCrt>& get_outdated_dynamic_crts() + { + static const std::vector<OutdatedDynamicCrt> v = { + { "msvcp100.dll", R"(msvcp100\.dll)" }, + { "msvcp100d.dll", R"(msvcp100d\.dll)" }, + { "msvcp110.dll", R"(msvcp110\.dll)" }, + { "msvcp110_win.dll", R"(msvcp110_win\.dll)" }, + { "msvcp120.dll", R"(msvcp120\.dll)" }, + { "msvcp120_clr0400.dll", R"(msvcp120_clr0400\.dll)" }, + { "msvcp60.dll", R"(msvcp60\.dll)" }, + { "msvcp60.dll", R"(msvcp60\.dll)" }, + + { "msvcr100.dll", R"(msvcr100\.dll)" }, + { "msvcr100d.dll", R"(msvcr100d\.dll)" }, + { "msvcr100_clr0400.dll", R"(msvcr100_clr0400\.dll)" }, + { "msvcr110.dll", R"(msvcr110\.dll)" }, + { "msvcr120.dll", R"(msvcr120\.dll)" }, + { "msvcr120_clr0400.dll", R"(msvcr120_clr0400\.dll)" }, + { "msvcrt.dll", R"(msvcrt\.dll)" }, + { "msvcrt20.dll", R"(msvcrt20\.dll)" }, + { "msvcrt40.dll", R"(msvcrt40\.dll)" } + }; + + return v; + } static lint_status check_for_files_in_include_directory(const fs::path& package_dir) { @@ -185,12 +220,12 @@ namespace vcpkg { namespace PostBuildLint return lint_status::SUCCESS; } - static lint_status check_exports_of_dlls(const std::vector<fs::path>& dlls) + static lint_status check_exports_of_dlls(const std::vector<fs::path>& dlls, const fs::path& dumpbin_exe) { std::vector<fs::path> dlls_with_no_exports; for (const fs::path& dll : dlls) { - const std::wstring cmd_line = Strings::wformat(LR"("%s" /exports "%s")", DUMPBIN_EXE.native(), dll.native()); + const std::wstring cmd_line = Strings::wformat(LR"("%s" /exports "%s")", dumpbin_exe.native(), dll.native()); System::exit_code_and_output ec_data = System::cmd_execute_and_capture_output(cmd_line); Checks::check_exit(ec_data.exit_code == 0, "Running command:\n %s\n failed", Strings::utf16_to_utf8(cmd_line)); @@ -211,7 +246,7 @@ namespace vcpkg { namespace PostBuildLint return lint_status::SUCCESS; } - static lint_status check_uwp_bit_of_dlls(const std::string& expected_system_name, const std::vector<fs::path>& dlls) + static lint_status check_uwp_bit_of_dlls(const std::string& expected_system_name, const std::vector<fs::path>& dlls, const fs::path dumpbin_exe) { if (expected_system_name != "uwp") { @@ -221,7 +256,7 @@ namespace vcpkg { namespace PostBuildLint std::vector<fs::path> dlls_with_improper_uwp_bit; for (const fs::path& dll : dlls) { - const std::wstring cmd_line = Strings::wformat(LR"("%s" /headers "%s")", DUMPBIN_EXE.native(), dll.native()); + const std::wstring cmd_line = Strings::wformat(LR"("%s" /headers "%s")", dumpbin_exe.native(), dll.native()); System::exit_code_and_output ec_data = System::cmd_execute_and_capture_output(cmd_line); Checks::check_exit(ec_data.exit_code == 0, "Running command:\n %s\n failed", Strings::utf16_to_utf8(cmd_line)); @@ -289,7 +324,7 @@ namespace vcpkg { namespace PostBuildLint if (expected_architecture != actual_architecture) { - binaries_with_invalid_architecture.push_back({file, actual_architecture}); + binaries_with_invalid_architecture.push_back({ file, actual_architecture }); } } @@ -315,7 +350,7 @@ namespace vcpkg { namespace PostBuildLint const std::string actual_architecture = get_actual_architecture(info.machine_types.at(0)); if (expected_architecture != actual_architecture) { - binaries_with_invalid_architecture.push_back({file, actual_architecture}); + binaries_with_invalid_architecture.push_back({ file, actual_architecture }); } } @@ -370,29 +405,20 @@ namespace vcpkg { namespace PostBuildLint return lint_status::ERROR_DETECTED; } - static lint_status check_lib_files_are_available_if_dlls_are_available(const size_t lib_count, const size_t dll_count, const fs::path& lib_dir) + static lint_status check_lib_files_are_available_if_dlls_are_available(const std::map<BuildPolicies::type, opt_bool_t>& policies, const size_t lib_count, const size_t dll_count, const fs::path& lib_dir) { - if (lib_count == 0 && dll_count != 0) + auto it = policies.find(BuildPolicies::DLLS_WITHOUT_LIBS); + if (it != policies.cend() && it->second == opt_bool_t::DISABLED) { - System::println(System::color::warning, "Import libs were not present in %s", lib_dir.generic_string()); - return lint_status::ERROR_DETECTED; + return lint_status::SUCCESS; } - return lint_status::SUCCESS; - } - - static lint_status check_no_subdirectories(const fs::path& dir) - { - const std::vector<fs::path> subdirectories = Files::recursive_find_matching_paths_in_dir(dir, [&](const fs::path& current) - { - return fs::is_directory(current); - }); - - if (!subdirectories.empty()) + if (lib_count == 0 && dll_count != 0) { - System::println(System::color::warning, "Directory %s should have no subdirectories", dir.generic_string()); - System::println("The following subdirectories were found: "); - Files::print_paths(subdirectories); + System::println(System::color::warning, "Import libs were not present in %s", lib_dir.generic_string()); + System::println(System::color::warning, + "If this is intended, add the following line in the portfile:\n" + " SET(%s disabled)", BuildPolicies::DLLS_WITHOUT_LIBS.cmake_variable()); return lint_status::ERROR_DETECTED; } @@ -443,7 +469,7 @@ namespace vcpkg { namespace PostBuildLint System::println("The following empty directories were found: "); Files::print_paths(empty_directories); System::println(System::color::warning, "If a directory should be populated but is not, this might indicate an error in the portfile.\n" - "If the directories are not needed and their creation cannot be disabled, use something like this in the portfile to remove them)\n" + "If the directories are not needed and their creation cannot be disabled, use something like this in the portfile to remove them:\n" "\n" R"###( file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/a/dir ${CURRENT_PACKAGES_DIR}/some/other/dir))###""\n" "\n"); @@ -456,27 +482,27 @@ namespace vcpkg { namespace PostBuildLint struct BuildType_and_file { fs::path file; - BuildType build_type; + BuildType::type build_type; }; - static lint_status check_crt_linkage_of_libs(const BuildType& expected_build_type, const std::vector<fs::path>& libs) + static lint_status check_crt_linkage_of_libs(const BuildType::type& expected_build_type, const std::vector<fs::path>& libs, const fs::path dumpbin_exe) { - std::vector<BuildType> bad_build_types = BuildType::values(); + std::vector<BuildType::type> bad_build_types(BuildType::values.cbegin(), BuildType::values.cend()); bad_build_types.erase(std::remove(bad_build_types.begin(), bad_build_types.end(), expected_build_type), bad_build_types.end()); std::vector<BuildType_and_file> libs_with_invalid_crt; for (const fs::path& lib : libs) { - const std::wstring cmd_line = Strings::wformat(LR"("%s" /directives "%s")", DUMPBIN_EXE.native(), lib.native()); + const std::wstring cmd_line = Strings::wformat(LR"("%s" /directives "%s")", dumpbin_exe.native(), lib.native()); System::exit_code_and_output ec_data = System::cmd_execute_and_capture_output(cmd_line); Checks::check_exit(ec_data.exit_code == 0, "Running command:\n %s\n failed", Strings::utf16_to_utf8(cmd_line)); - for (const BuildType& bad_build_type : bad_build_types) + for (const BuildType::type& bad_build_type : bad_build_types) { if (std::regex_search(ec_data.output.cbegin(), ec_data.output.cend(), bad_build_type.crt_regex())) { - libs_with_invalid_crt.push_back({lib, bad_build_type}); + libs_with_invalid_crt.push_back({ lib, bad_build_type }); break; } } @@ -503,25 +529,27 @@ namespace vcpkg { namespace PostBuildLint { fs::path file; OutdatedDynamicCrt outdated_crt; + + OutdatedDynamicCrt_and_file() = delete; }; - static lint_status check_outdated_crt_linkage_of_dlls(const std::vector<fs::path>& dlls) + static lint_status check_outdated_crt_linkage_of_dlls(const std::vector<fs::path>& dlls, const fs::path dumpbin_exe) { - const std::vector<OutdatedDynamicCrt>& outdated_crts = OutdatedDynamicCrt::values(); + const std::vector<OutdatedDynamicCrt>& outdated_crts = get_outdated_dynamic_crts(); std::vector<OutdatedDynamicCrt_and_file> dlls_with_outdated_crt; for (const fs::path& dll : dlls) { - const std::wstring cmd_line = Strings::wformat(LR"("%s" /dependents "%s")", DUMPBIN_EXE.native(), dll.native()); + const std::wstring cmd_line = Strings::wformat(LR"("%s" /dependents "%s")", dumpbin_exe.native(), dll.native()); System::exit_code_and_output ec_data = System::cmd_execute_and_capture_output(cmd_line); Checks::check_exit(ec_data.exit_code == 0, "Running command:\n %s\n failed", Strings::utf16_to_utf8(cmd_line)); for (const OutdatedDynamicCrt& outdated_crt : outdated_crts) { - if (std::regex_search(ec_data.output.cbegin(), ec_data.output.cend(), outdated_crt.crt_regex())) + if (std::regex_search(ec_data.output.cbegin(), ec_data.output.cend(), outdated_crt.regex)) { - dlls_with_outdated_crt.push_back({dll, outdated_crt}); + dlls_with_outdated_crt.push_back({ dll, outdated_crt }); break; } } @@ -533,7 +561,7 @@ namespace vcpkg { namespace PostBuildLint System::println(""); for (const OutdatedDynamicCrt_and_file btf : dlls_with_outdated_crt) { - System::println(" %s: %s", btf.file.generic_string(), btf.outdated_crt.toString()); + System::println(" %s: %s", btf.file.generic_string(), btf.outdated_crt.name); } System::println(""); @@ -573,14 +601,32 @@ namespace vcpkg { namespace PostBuildLint left += static_cast<size_t>(right); } - void perform_all_checks(const package_spec& spec, const vcpkg_paths& paths) + template <class T> + static bool contains_and_enabled(const std::map<T, opt_bool_t> map, const T& key) { - System::println("-- Performing post-build validation"); + auto it = map.find(key); + if (it != map.cend() && it->second == opt_bool_t::ENABLED) + { + return true; + } + + return false; + } + + static size_t perform_all_checks_and_return_error_count(const package_spec& spec, const vcpkg_paths& paths) + { + const fs::path dumpbin_exe = Environment::get_dumpbin_exe(paths); BuildInfo build_info = read_build_info(paths.build_info_file_path(spec)); const fs::path package_dir = paths.package_dir(spec); size_t error_count = 0; + + if (contains_and_enabled(build_info.policies, BuildPolicies::EMPTY_PACKAGE)) + { + return error_count; + } + error_count += check_for_files_in_include_directory(package_dir); error_count += check_for_files_in_debug_include_directory(package_dir); error_count += check_for_files_in_debug_share_directory(package_dir); @@ -607,30 +653,30 @@ namespace vcpkg { namespace PostBuildLint error_count += check_lib_architecture(spec.target_triplet().architecture(), libs); - switch (linkage_type_value_of(build_info.library_linkage)) + switch (build_info.library_linkage) { - case LinkageType::DYNAMIC: + case LinkageType::backing_enum_t::DYNAMIC: { const std::vector<fs::path> debug_dlls = Files::recursive_find_files_with_extension_in_dir(debug_bin_dir, ".dll"); const std::vector<fs::path> release_dlls = Files::recursive_find_files_with_extension_in_dir(release_bin_dir, ".dll"); error_count += check_matching_debug_and_release_binaries(debug_dlls, release_dlls); - error_count += check_lib_files_are_available_if_dlls_are_available(debug_libs.size(), debug_dlls.size(), debug_lib_dir); - error_count += check_lib_files_are_available_if_dlls_are_available(release_libs.size(), release_dlls.size(), release_lib_dir); + error_count += check_lib_files_are_available_if_dlls_are_available(build_info.policies, debug_libs.size(), debug_dlls.size(), debug_lib_dir); + error_count += check_lib_files_are_available_if_dlls_are_available(build_info.policies, release_libs.size(), release_dlls.size(), release_lib_dir); std::vector<fs::path> dlls; dlls.insert(dlls.cend(), debug_dlls.cbegin(), debug_dlls.cend()); dlls.insert(dlls.cend(), release_dlls.cbegin(), release_dlls.cend()); - error_count += check_exports_of_dlls(dlls); - error_count += check_uwp_bit_of_dlls(spec.target_triplet().system(), dlls); + error_count += check_exports_of_dlls(dlls, dumpbin_exe); + error_count += check_uwp_bit_of_dlls(spec.target_triplet().system(), dlls, dumpbin_exe); error_count += check_dll_architecture(spec.target_triplet().architecture(), dlls); - error_count += check_outdated_crt_linkage_of_dlls(dlls); + error_count += check_outdated_crt_linkage_of_dlls(dlls, dumpbin_exe); break; } - case LinkageType::STATIC: + case LinkageType::backing_enum_t::STATIC: { std::vector<fs::path> dlls; Files::recursive_find_files_with_extension_in_dir(package_dir, ".dll", &dlls); @@ -638,34 +684,36 @@ namespace vcpkg { namespace PostBuildLint error_count += check_bin_folders_are_not_present_in_static_build(package_dir); - error_count += check_crt_linkage_of_libs(BuildType::value_of(ConfigurationType::DEBUG, linkage_type_value_of(build_info.crt_linkage)), debug_libs); - error_count += check_crt_linkage_of_libs(BuildType::value_of(ConfigurationType::RELEASE, linkage_type_value_of(build_info.crt_linkage)), release_libs); - break; - } - case LinkageType::UNKNOWN: - { - error_count += 1; - System::println(System::color::warning, "Unknown library_linkage architecture: [ %s ]", build_info.library_linkage); + if (!contains_and_enabled(build_info.policies, BuildPolicies::ONLY_RELEASE_CRT)) + { + error_count += check_crt_linkage_of_libs(BuildType::value_of(ConfigurationType::DEBUG, build_info.crt_linkage), debug_libs, dumpbin_exe); + } + error_count += check_crt_linkage_of_libs(BuildType::value_of(ConfigurationType::RELEASE, build_info.crt_linkage), release_libs, dumpbin_exe); break; } + case LinkageType::backing_enum_t::NULLVALUE: default: Checks::unreachable(); } -#if 0 - error_count += check_no_subdirectories(package_dir / "lib"); - error_count += check_no_subdirectories(package_dir / "debug" / "lib"); -#endif error_count += check_no_empty_folders(package_dir); error_count += check_no_files_in_package_dir_and_debug_dir(package_dir); + return error_count; + } + + size_t perform_all_checks(const package_spec& spec, const vcpkg_paths& paths) + { + System::println("-- Performing post-build validation"); + const size_t error_count = perform_all_checks_and_return_error_count(spec, paths); + System::println("-- Performing post-build validation done"); + if (error_count != 0) { const fs::path portfile = paths.ports / spec.name() / "portfile.cmake"; System::println(System::color::error, "Found %u error(s). Please correct the portfile:\n %s", error_count, portfile.string()); - exit(EXIT_FAILURE); } - System::println("-- Performing post-build validation done"); + return error_count; } -}} +} diff --git a/toolsrc/src/PostBuildLint_BuildInfo.cpp b/toolsrc/src/PostBuildLint_BuildInfo.cpp new file mode 100644 index 000000000..0d1d480b8 --- /dev/null +++ b/toolsrc/src/PostBuildLint_BuildInfo.cpp @@ -0,0 +1,48 @@ +#include "pch.h" +#include "PostBuildLint_BuildInfo.h" +#include "vcpkg_Checks.h" +#include "opt_bool.h" +#include "vcpkglib_helpers.h" +#include "Paragraphs.h" + +namespace vcpkg::PostBuildLint +{ + // + namespace BuildInfoRequiredField + { + static const std::string CRT_LINKAGE = "CRTLinkage"; + static const std::string LIBRARY_LINKAGE = "LibraryLinkage"; + } + + BuildInfo BuildInfo::create(std::unordered_map<std::string, std::string> pgh) + { + BuildInfo build_info; + const std::string crt_linkage_as_string = details::remove_required_field(&pgh, BuildInfoRequiredField::CRT_LINKAGE); + build_info.crt_linkage = LinkageType::value_of(crt_linkage_as_string); + Checks::check_exit(build_info.crt_linkage != LinkageType::NULLVALUE, "Invalid crt linkage type: [%s]", crt_linkage_as_string); + + const std::string library_linkage_as_string = details::remove_required_field(&pgh, BuildInfoRequiredField::LIBRARY_LINKAGE); + build_info.library_linkage = LinkageType::value_of(library_linkage_as_string); + Checks::check_exit(build_info.library_linkage != LinkageType::NULLVALUE, "Invalid library linkage type: [%s]", library_linkage_as_string); + + // The remaining entries are policies + for (const std::unordered_map<std::string, std::string>::value_type& p : pgh) + { + const BuildPolicies::type policy = BuildPolicies::parse(p.first); + Checks::check_exit(policy != BuildPolicies::NULLVALUE, "Unknown policy found: %s", p.first); + const opt_bool_t status = opt_bool::parse(p.second); + build_info.policies.emplace(policy, status); + } + + return build_info; + } + + BuildInfo read_build_info(const fs::path& filepath) + { + const std::vector<std::unordered_map<std::string, std::string>> pghs = Paragraphs::get_paragraphs(filepath); + Checks::check_exit(pghs.size() == 1, "Invalid BUILD_INFO file for package"); + + return BuildInfo::create(pghs[0]); + } + +} diff --git a/toolsrc/src/PostBuildLint_BuildPolicies.cpp b/toolsrc/src/PostBuildLint_BuildPolicies.cpp new file mode 100644 index 000000000..f070a2a42 --- /dev/null +++ b/toolsrc/src/PostBuildLint_BuildPolicies.cpp @@ -0,0 +1,70 @@ +#include "pch.h" +#include "PostBuildLint_BuildPolicies.h" +#include "vcpkg_Enums.h" + +namespace vcpkg::PostBuildLint::BuildPolicies +{ + static const std::string NULLVALUE_STRING = Enums::nullvalue_toString(ENUM_NAME); + + static const std::string NAME_EMPTY_PACKAGE = "PolicyEmptyPackage"; + static const std::string NAME_DLLS_WITHOUT_LIBS = "PolicyDLLsWithoutLIBs"; + static const std::string NAME_ONLY_RELEASE_CRT = "PolicyOnlyReleaseCRT"; + + const std::string& type::toString() const + { + switch (this->backing_enum) + { + case EMPTY_PACKAGE: + return NAME_EMPTY_PACKAGE; + case DLLS_WITHOUT_LIBS: + return NAME_DLLS_WITHOUT_LIBS; + case ONLY_RELEASE_CRT: + return NAME_ONLY_RELEASE_CRT; + case NULLVALUE: + return NULLVALUE_STRING; + default: + Enums::unreachable(ENUM_NAME); + } + } + + const std::string& type::cmake_variable() const + { + static const std::string CMAKE_VARIABLE_EMPTY_PACKAGE = "VCPKG_POLICY_EMPTY_PACKAGE"; + static const std::string CMAKE_VARIABLE_DLLS_WITHOUT_LIBS = "VCPKG_POLICY_DLLS_WITHOUT_LIBS"; + static const std::string CMAKE_VARIABLE_ONLY_RELEASE_CRT = "VCPKG_POLICY_ONLY_RELEASE_CRT"; + + switch (this->backing_enum) + { + case EMPTY_PACKAGE: + return CMAKE_VARIABLE_EMPTY_PACKAGE; + case DLLS_WITHOUT_LIBS: + return CMAKE_VARIABLE_DLLS_WITHOUT_LIBS; + case ONLY_RELEASE_CRT: + return CMAKE_VARIABLE_ONLY_RELEASE_CRT; + case NULLVALUE: + Enums::nullvalue_used(ENUM_NAME); + default: + Enums::unreachable(ENUM_NAME); + } + } + + type parse(const std::string& s) + { + if (s == NAME_EMPTY_PACKAGE) + { + return BuildPolicies::EMPTY_PACKAGE; + } + + if (s == NAME_DLLS_WITHOUT_LIBS) + { + return BuildPolicies::DLLS_WITHOUT_LIBS; + } + + if (s == NAME_ONLY_RELEASE_CRT) + { + return BuildPolicies::ONLY_RELEASE_CRT; + } + + return BuildPolicies::NULLVALUE; + } +} diff --git a/toolsrc/src/PostBuildLint_BuildType.cpp b/toolsrc/src/PostBuildLint_BuildType.cpp new file mode 100644 index 000000000..f2fb292d7 --- /dev/null +++ b/toolsrc/src/PostBuildLint_BuildType.cpp @@ -0,0 +1,85 @@ +#include "pch.h" +#include "PostBuildLint_BuildType.h" +#include "vcpkg_Enums.h" + +namespace vcpkg::PostBuildLint::BuildType +{ + type value_of(const ConfigurationType::type& config, const LinkageType::type& linkage) + { + if (config == ConfigurationType::DEBUG && linkage == LinkageType::STATIC) + { + return DEBUG_STATIC; + } + + if (config == ConfigurationType::DEBUG && linkage == LinkageType::DYNAMIC) + { + return DEBUG_DYNAMIC; + } + + if (config == ConfigurationType::RELEASE && linkage == LinkageType::STATIC) + { + return RELEASE_STATIC; + } + + if (config == ConfigurationType::RELEASE && linkage == LinkageType::DYNAMIC) + { + return RELEASE_DYNAMIC; + } + + Enums::unreachable(ENUM_NAME); + } + + const ConfigurationType::type& type::config() const + { + return this->m_config; + } + + const LinkageType::type& type::linkage() const + { + return this->m_linkage; + } + + const std::regex& type::crt_regex() const + { + static const std::regex REGEX_DEBUG_STATIC(R"(/DEFAULTLIB:LIBCMTD)", std::regex_constants::icase); + static const std::regex REGEX_DEBUG_DYNAMIC(R"(/DEFAULTLIB:MSVCRTD)", std::regex_constants::icase); + static const std::regex REGEX_RELEASE_STATIC(R"(/DEFAULTLIB:LIBCMT[^D])", std::regex_constants::icase); + static const std::regex REGEX_RELEASE_DYNAMIC(R"(/DEFAULTLIB:MSVCRT[^D])", std::regex_constants::icase); + + switch (backing_enum) + { + case BuildType::DEBUG_STATIC: + return REGEX_DEBUG_STATIC; + case BuildType::DEBUG_DYNAMIC: + return REGEX_DEBUG_DYNAMIC; + case BuildType::RELEASE_STATIC: + return REGEX_RELEASE_STATIC; + case BuildType::RELEASE_DYNAMIC: + return REGEX_RELEASE_DYNAMIC; + default: + Enums::unreachable(ENUM_NAME); + } + } + + const std::string& type::toString() const + { + static const std::string NAME_DEBUG_STATIC("Debug,Static"); + static const std::string NAME_DEBUG_DYNAMIC("Debug,Dynamic"); + static const std::string NAME_RELEASE_STATIC("Release,Static"); + static const std::string NAME_RELEASE_DYNAMIC("Release,Dynamic"); + + switch (backing_enum) + { + case BuildType::DEBUG_STATIC: + return NAME_DEBUG_STATIC; + case BuildType::DEBUG_DYNAMIC: + return NAME_DEBUG_DYNAMIC; + case BuildType::RELEASE_STATIC: + return NAME_RELEASE_STATIC; + case BuildType::RELEASE_DYNAMIC: + return NAME_RELEASE_DYNAMIC; + default: + Enums::unreachable(ENUM_NAME); + } + } +} diff --git a/toolsrc/src/PostBuildLint_ConfigurationType.cpp b/toolsrc/src/PostBuildLint_ConfigurationType.cpp new file mode 100644 index 000000000..990b10a37 --- /dev/null +++ b/toolsrc/src/PostBuildLint_ConfigurationType.cpp @@ -0,0 +1,26 @@ +#include "pch.h" +#include "PostBuildLint_ConfigurationType.h" +#include "vcpkg_Enums.h" + +namespace vcpkg::PostBuildLint::ConfigurationType +{ + static const std::string NULLVALUE_STRING = Enums::nullvalue_toString(ENUM_NAME); + + static const std::string NAME_DEBUG = "Debug"; + static const std::string NAME_RELEASE = "Release"; + + const std::string& type::toString() const + { + switch (this->backing_enum) + { + case ConfigurationType::DEBUG: + return NAME_DEBUG; + case ConfigurationType::RELEASE: + return NAME_RELEASE; + case ConfigurationType::NULLVALUE: + return NULLVALUE_STRING; + default: + Enums::unreachable(ENUM_NAME); + } + } +} diff --git a/toolsrc/src/PostBuildLint_LinkageType.cpp b/toolsrc/src/PostBuildLint_LinkageType.cpp new file mode 100644 index 000000000..6d2c2c935 --- /dev/null +++ b/toolsrc/src/PostBuildLint_LinkageType.cpp @@ -0,0 +1,41 @@ +#include "pch.h" +#include "PostBuildLint_LinkageType.h" +#include "vcpkg_Enums.h" + +namespace vcpkg::PostBuildLint::LinkageType +{ + static const std::string NULLVALUE_STRING = Enums::nullvalue_toString(ENUM_NAME); + + static const std::string NAME_DYNAMIC = "dynamic"; + static const std::string NAME_STATIC = "static"; + + const std::string& type::toString() const + { + switch (this->backing_enum) + { + case LinkageType::DYNAMIC: + return NAME_DYNAMIC; + case LinkageType::STATIC: + return NAME_STATIC; + case LinkageType::NULLVALUE: + return NULLVALUE_STRING; + default: + Enums::unreachable(ENUM_NAME); + } + } + + type value_of(const std::string& as_string) + { + if (as_string == NAME_DYNAMIC) + { + return LinkageType::DYNAMIC; + } + + if (as_string == NAME_STATIC) + { + return LinkageType::STATIC; + } + + return LinkageType::NULLVALUE; + } +} diff --git a/toolsrc/src/SourceParagraph.cpp b/toolsrc/src/SourceParagraph.cpp index bdf15a737..4d144191f 100644 --- a/toolsrc/src/SourceParagraph.cpp +++ b/toolsrc/src/SourceParagraph.cpp @@ -1,3 +1,4 @@ +#include "pch.h" #include "SourceParagraph.h" #include "vcpkglib_helpers.h" #include "vcpkg_System.h" @@ -52,8 +53,8 @@ namespace vcpkg const std::vector<std::string> remaining_fields = Maps::extract_keys(fields); const std::vector<std::string>& valid_fields = get_list_of_valid_fields(); - const std::string remaining_fields_as_string = Strings::join(remaining_fields, "\n "); - const std::string valid_fields_as_string = Strings::join(valid_fields, "\n "); + const std::string remaining_fields_as_string = Strings::join("\n ", remaining_fields); + const std::string valid_fields_as_string = Strings::join("\n ", valid_fields); System::println(System::color::error, "Error: There are invalid fields in the Source Paragraph of %s", this->name); System::println("The following fields were not expected:\n\n %s\n\n", remaining_fields_as_string); diff --git a/toolsrc/src/StatusParagraph.cpp b/toolsrc/src/StatusParagraph.cpp index bf12ae89a..3f07689ca 100644 --- a/toolsrc/src/StatusParagraph.cpp +++ b/toolsrc/src/StatusParagraph.cpp @@ -1,3 +1,4 @@ +#include "pch.h" #include "StatusParagraph.h" #include "vcpkglib_helpers.h" diff --git a/toolsrc/src/StatusParagraphs.cpp b/toolsrc/src/StatusParagraphs.cpp index 3e23c519a..9a440fbb8 100644 --- a/toolsrc/src/StatusParagraphs.cpp +++ b/toolsrc/src/StatusParagraphs.cpp @@ -1,5 +1,5 @@ +#include "pch.h" #include "StatusParagraphs.h" -#include <algorithm> #include "vcpkg_Checks.h" namespace vcpkg @@ -29,9 +29,9 @@ namespace vcpkg }); } - StatusParagraphs::iterator StatusParagraphs::find_installed(const std::string& name, const triplet& target_triplet) + StatusParagraphs::const_iterator StatusParagraphs::find_installed(const std::string& name, const triplet& target_triplet) const { - auto it = find(name, target_triplet); + const const_iterator it = find(name, target_triplet); if (it != end() && (*it)->want == want_t::install) { return it; @@ -42,7 +42,7 @@ namespace vcpkg StatusParagraphs::iterator StatusParagraphs::insert(std::unique_ptr<StatusParagraph> pgh) { - Checks::check_throw(pgh != nullptr, "Inserted null paragraph"); + Checks::check_exit(pgh != nullptr, "Inserted null paragraph"); const package_spec& spec = pgh->package.spec; auto ptr = find(spec.name(), spec.target_triplet()); if (ptr == end()) diff --git a/toolsrc/src/coff_file_reader.cpp b/toolsrc/src/coff_file_reader.cpp index 1f30ea70b..f48f912c1 100644 --- a/toolsrc/src/coff_file_reader.cpp +++ b/toolsrc/src/coff_file_reader.cpp @@ -1,12 +1,10 @@ +#include "pch.h" #include "coff_file_reader.h" -#include <iostream> #include "vcpkg_Checks.h" -#include <set> -#include <fstream> using namespace std; -namespace vcpkg { namespace COFFFileReader +namespace vcpkg::COFFFileReader { template <class T> static T reinterpret_bytes(const char* data) @@ -306,4 +304,4 @@ namespace vcpkg { namespace COFFFileReader return {std::vector<MachineType>(machine_types.cbegin(), machine_types.cend())}; } -}} +} diff --git a/toolsrc/src/commands_available_commands.cpp b/toolsrc/src/commands_available_commands.cpp new file mode 100644 index 000000000..4c7e0df2c --- /dev/null +++ b/toolsrc/src/commands_available_commands.cpp @@ -0,0 +1,46 @@ +#include "pch.h" +#include "vcpkg_Commands.h" + +namespace vcpkg::Commands +{ + const std::vector<package_name_and_function<command_type_a>>& get_available_commands_type_a() + { + static std::vector<package_name_and_function<command_type_a>> t = { + {"install", &Install::perform_and_exit}, + { "ci", &CI::perform_and_exit }, + {"remove", &Remove::perform_and_exit}, + {"build", &Build::perform_and_exit}, + {"build_external", &BuildExternal::perform_and_exit} + }; + return t; + } + + const std::vector<package_name_and_function<command_type_b>>& get_available_commands_type_b() + { + static std::vector<package_name_and_function<command_type_b>> t = { + {"/?", &Help::perform_and_exit}, + {"help", &Help::perform_and_exit}, + {"search", &Search::perform_and_exit}, + {"list", &List::perform_and_exit}, + {"integrate", &Integrate::perform_and_exit}, + {"owns", &Owns::perform_and_exit}, + {"update", &Update::perform_and_exit}, + {"edit", &Edit::perform_and_exit}, + {"create", &Create::perform_and_exit}, + {"import", &Import::perform_and_exit}, + {"cache", &Cache::perform_and_exit}, + {"portsdiff", &PortsDiff::perform_and_exit} + }; + return t; + } + + const std::vector<package_name_and_function<command_type_c>>& get_available_commands_type_c() + { + static std::vector<package_name_and_function<command_type_c>> t = { + {"version", &Version::perform_and_exit}, + {"contact", &Contact::perform_and_exit}, + {"hash", &Hash::perform_and_exit}, + }; + return t; + } +} diff --git a/toolsrc/src/commands_build.cpp b/toolsrc/src/commands_build.cpp new file mode 100644 index 000000000..a47083cbd --- /dev/null +++ b/toolsrc/src/commands_build.cpp @@ -0,0 +1,183 @@ +#include "pch.h" +#include "vcpkg_Commands.h" +#include "StatusParagraphs.h" +#include "vcpkglib.h" +#include "vcpkg_Input.h" +#include "PostBuildLint.h" +#include "vcpkg_Dependencies.h" +#include "vcpkg_System.h" +#include "vcpkg_Chrono.h" +#include "vcpkg_Environment.h" +#include "metrics.h" +#include "vcpkg_Enums.h" +#include "Paragraphs.h" + +namespace vcpkg::Commands::Build +{ + using Dependencies::package_spec_with_install_plan; + using Dependencies::install_plan_type; + + static const std::string OPTION_CHECKS_ONLY = "--checks-only"; + + static void create_binary_control_file(const vcpkg_paths& paths, const SourceParagraph& source_paragraph, const triplet& target_triplet) + { + const BinaryParagraph bpgh = BinaryParagraph(source_paragraph, target_triplet); + const fs::path binary_control_file = paths.packages / bpgh.dir() / "CONTROL"; + std::ofstream(binary_control_file) << bpgh; + } + + BuildResult build_package(const SourceParagraph& source_paragraph, const package_spec& spec, const vcpkg_paths& paths, const fs::path& port_dir, const StatusParagraphs& status_db) + { + Checks::check_exit(spec.name() == source_paragraph.name, "inconsistent arguments to build_package()"); + + const triplet& target_triplet = spec.target_triplet(); + for (auto&& dep : source_paragraph.depends) + { + if (status_db.find_installed(dep.name, target_triplet) == status_db.end()) + { + return BuildResult::CASCADED_DUE_TO_MISSING_DEPENDENCIES; + } + } + + const fs::path& cmake_exe_path = paths.get_cmake_exe(); + const fs::path& git_exe_path = paths.get_git_exe(); + + const fs::path ports_cmake_script_path = paths.ports_cmake; + const Environment::vcvarsall_and_platform_toolset vcvarsall_bat = Environment::get_vcvarsall_bat(paths); + const std::wstring cmd_set_environment = Strings::wformat(LR"("%s" %s >nul 2>&1)", vcvarsall_bat.path.native(), Strings::utf8_to_utf16(target_triplet.architecture())); + + const std::wstring cmd_launch_cmake = make_cmake_cmd(cmake_exe_path, ports_cmake_script_path, + { + { L"CMD", L"BUILD" }, + { L"PORT", source_paragraph.name }, + { L"CURRENT_PORT_DIR", port_dir / "/." }, + { L"TARGET_TRIPLET", target_triplet.canonical_name() }, + { L"VCPKG_PLATFORM_TOOLSET", vcvarsall_bat.platform_toolset }, + { L"GIT", git_exe_path } + }); + + const std::wstring command = Strings::wformat(LR"(%s && %s)", cmd_set_environment, cmd_launch_cmake); + + const ElapsedTime timer = ElapsedTime::createStarted(); + + int return_code = System::cmd_execute_clean(command); + auto buildtimeus = timer.microseconds(); + TrackMetric("buildtimeus-" + spec.toString(), buildtimeus); + + if (return_code != 0) + { + TrackProperty("error", "build failed"); + TrackProperty("build_error", spec.toString()); + return BuildResult::BUILD_FAILED; + } + + const size_t error_count = PostBuildLint::perform_all_checks(spec, paths); + + if (error_count != 0) + { + return BuildResult::POST_BUILD_CHECKS_FAILED; + } + + create_binary_control_file(paths, source_paragraph, target_triplet); + + // const fs::path port_buildtrees_dir = paths.buildtrees / spec.name; + // delete_directory(port_buildtrees_dir); + + return BuildResult::SUCCEEDED; + } + + const std::string& to_string(const BuildResult build_result) + { + static const std::string NULLVALUE_STRING = Enums::nullvalue_toString("vcpkg::Commands::Build::BuildResult"); + static const std::string SUCCEEDED_STRING = "SUCCEEDED"; + static const std::string BUILD_FAILED_STRING = "BUILD_FAILED"; + 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"; + + 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::CASCADED_DUE_TO_MISSING_DEPENDENCIES: return CASCADED_DUE_TO_MISSING_DEPENDENCIES_STRING; + default: Checks::unreachable(); + } + } + + std::string create_error_message(const BuildResult build_result, const package_spec& spec) + { + return Strings::format("Error: Building package %s failed with: %s", spec.toString(), Build::to_string(build_result)); + } + + std::string create_user_troubleshooting_message(const package_spec& spec) + { + return Strings::format("Please ensure sure 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.toString(), Version::version()); + } + + void perform_and_exit(const package_spec& spec, const fs::path& port_dir, const std::unordered_set<std::string>& options, const vcpkg_paths& paths) + { + if (options.find(OPTION_CHECKS_ONLY) != options.end()) + { + const size_t error_count = PostBuildLint::perform_all_checks(spec, paths); + if (error_count > 0) + { + exit(EXIT_FAILURE); + } + exit(EXIT_SUCCESS); + } + + const expected<SourceParagraph> maybe_spgh = Paragraphs::try_load_port(port_dir); + Checks::check_exit(!maybe_spgh.error_code(), "Could not find package named %s: %s", spec, maybe_spgh.error_code().message()); + const SourceParagraph& spgh = *maybe_spgh.get(); + + StatusParagraphs status_db = database_load_check(paths); + const BuildResult result = build_package(spgh, spec, paths, paths.port_dir(spec), status_db); + if (result == BuildResult::CASCADED_DUE_TO_MISSING_DEPENDENCIES) + { + std::vector<package_spec_with_install_plan> unmet_dependencies = Dependencies::create_install_plan(paths, { spec }, status_db); + unmet_dependencies.erase( + std::remove_if(unmet_dependencies.begin(), unmet_dependencies.end(), [&spec](const package_spec_with_install_plan& p) + { + return (p.spec == spec) || (p.plan.plan_type == install_plan_type::ALREADY_INSTALLED); + }), + unmet_dependencies.end()); + + Checks::check_exit(!unmet_dependencies.empty()); + System::println(System::color::error, "The build command requires all dependencies to be already installed."); + System::println("The following dependencies are missing:"); + System::println(""); + for (const package_spec_with_install_plan& p : unmet_dependencies) + { + System::println(" %s", p.spec.toString()); + } + System::println(""); + exit(EXIT_FAILURE); + } + + if (result != BuildResult::SUCCEEDED) + { + System::println(System::color::error, Build::create_error_message(result, spec)); + System::println(Build::create_user_troubleshooting_message(spec)); + exit(EXIT_FAILURE); + } + + exit(EXIT_SUCCESS); + } + + void perform_and_exit(const vcpkg_cmd_arguments& args, const vcpkg_paths& paths, const triplet& default_target_triplet) + { + static const std::string example = Commands::Help::create_example_string("build zlib:x64-windows"); + args.check_exact_arg_count(1, example); // Build only takes a single package and all dependencies must already be installed + const package_spec spec = Input::check_and_get_package_spec(args.command_arguments.at(0), default_target_triplet, example); + Input::check_triplet(spec.target_triplet(), paths); + const std::unordered_set<std::string> options = args.check_and_get_optional_command_arguments({ OPTION_CHECKS_ONLY }); + perform_and_exit(spec, paths.port_dir(spec), options, paths); + } +} diff --git a/toolsrc/src/commands_build_external.cpp b/toolsrc/src/commands_build_external.cpp new file mode 100644 index 000000000..06bd1374c --- /dev/null +++ b/toolsrc/src/commands_build_external.cpp @@ -0,0 +1,20 @@ +#include "pch.h" +#include "vcpkg_Commands.h" +#include "vcpkg_System.h" +#include "vcpkg_Environment.h" +#include "vcpkg_Input.h" + +namespace vcpkg::Commands::BuildExternal +{ + void perform_and_exit(const vcpkg_cmd_arguments& args, const vcpkg_paths& paths, const triplet& default_target_triplet) + { + static const std::string example = Commands::Help::create_example_string(R"(build_external zlib2 C:\path\to\dir\with\controlfile\)"); + args.check_exact_arg_count(2, example); + const package_spec spec = Input::check_and_get_package_spec(args.command_arguments.at(0), default_target_triplet, example); + Input::check_triplet(spec.target_triplet(), paths); + const std::unordered_set<std::string> options = args.check_and_get_optional_command_arguments({}); + + const fs::path port_dir = args.command_arguments.at(1); + Build::perform_and_exit(spec, port_dir, options, paths); + } +} diff --git a/toolsrc/src/commands_cache.cpp b/toolsrc/src/commands_cache.cpp index 1a10b93cf..e255b5dff 100644 --- a/toolsrc/src/commands_cache.cpp +++ b/toolsrc/src/commands_cache.cpp @@ -1,10 +1,11 @@ +#include "pch.h" #include "vcpkg_Commands.h" #include "vcpkg_System.h" #include "vcpkg_Files.h" #include "Paragraphs.h" #include "BinaryParagraph.h" -namespace vcpkg +namespace vcpkg::Commands::Cache { static std::vector<BinaryParagraph> read_all_binary_paragraphs(const vcpkg_paths& paths) { @@ -34,11 +35,12 @@ namespace vcpkg return output; } - void cache_command(const vcpkg_cmd_arguments& args, const vcpkg_paths& paths) + void perform_and_exit(const vcpkg_cmd_arguments& args, const vcpkg_paths& paths) { static const std::string example = Strings::format( - "The argument should be a substring to search for, or no argument to display all cached libraries.\n%s", create_example_string("cache png")); + "The argument should be a substring to search for, or no argument to display all cached libraries.\n%s", Commands::Help::create_example_string("cache png")); args.check_max_arg_count(1, example); + args.check_and_get_optional_command_arguments({}); const std::vector<BinaryParagraph> binary_paragraphs = read_all_binary_paragraphs(paths); if (binary_paragraphs.empty()) diff --git a/toolsrc/src/commands_ci.cpp b/toolsrc/src/commands_ci.cpp new file mode 100644 index 000000000..315308a62 --- /dev/null +++ b/toolsrc/src/commands_ci.cpp @@ -0,0 +1,120 @@ +#include "pch.h" +#include "vcpkg_Commands.h" +#include "vcpkglib.h" +#include "vcpkg_Files.h" +#include "vcpkg_System.h" +#include "vcpkg_Dependencies.h" +#include "vcpkg_Input.h" +#include "vcpkg_Chrono.h" +#include "Paragraphs.h" + +namespace vcpkg::Commands::CI +{ + using Dependencies::package_spec_with_install_plan; + using Dependencies::install_plan_type; + using Build::BuildResult; + + static std::vector<package_spec> load_all_package_specs(const fs::path& ports_directory, const triplet& target_triplet) + { + std::vector<SourceParagraph> ports = Paragraphs::load_all_ports(ports_directory); + std::vector<package_spec> specs; + for (const SourceParagraph& p : ports) + { + specs.push_back(package_spec::from_name_and_triplet(p.name, target_triplet).get_or_throw()); + } + + return specs; + } + + void perform_and_exit(const vcpkg_cmd_arguments& args, const vcpkg_paths& paths, const triplet& default_target_triplet) + { + static const std::string example = Commands::Help::create_example_string("ci x64-windows"); + args.check_max_arg_count(1, example); + const triplet target_triplet = args.command_arguments.size() == 1 ? triplet::from_canonical_name(args.command_arguments.at(0)) : default_target_triplet; + Input::check_triplet(target_triplet, paths); + args.check_and_get_optional_command_arguments({}); + const std::vector<package_spec> specs = load_all_package_specs(paths.ports, target_triplet); + + StatusParagraphs status_db = database_load_check(paths); + const std::vector<package_spec_with_install_plan> install_plan = Dependencies::create_install_plan(paths, specs, status_db); + Checks::check_exit(!install_plan.empty(), "Install plan cannot be empty"); + + std::vector<BuildResult> results; + std::vector<std::chrono::milliseconds::rep> timing; + const ElapsedTime timer = ElapsedTime::createStarted(); + size_t counter = 0; + const size_t package_count = install_plan.size(); + for (const package_spec_with_install_plan& action : install_plan) + { + const ElapsedTime build_timer = ElapsedTime::createStarted(); + counter++; + System::println("Starting package %d/%d: %s", counter, package_count, action.spec.toString()); + + timing.push_back(-1); + results.push_back(BuildResult::NULLVALUE); + + try + { + if (action.plan.plan_type == install_plan_type::ALREADY_INSTALLED) + { + results.back() = BuildResult::SUCCEEDED; + System::println(System::color::success, "Package %s is already installed", action.spec); + } + else if (action.plan.plan_type == install_plan_type::BUILD_AND_INSTALL) + { + const BuildResult result = Commands::Build::build_package(*action.plan.source_pgh, action.spec, paths, paths.port_dir(action.spec), status_db); + timing.back() = build_timer.elapsed<std::chrono::milliseconds>().count(); + results.back() = result; + if (result != BuildResult::SUCCEEDED) + { + System::println(System::color::error, Build::create_error_message(result, action.spec)); + continue; + } + const BinaryParagraph bpgh = Paragraphs::try_load_cached_package(paths, action.spec).get_or_throw(); + Install::install_package(paths, bpgh, &status_db); + System::println(System::color::success, "Package %s is installed", action.spec); + } + else if (action.plan.plan_type == install_plan_type::INSTALL) + { + results.back() = BuildResult::SUCCEEDED; + Install::install_package(paths, *action.plan.binary_pgh, &status_db); + System::println(System::color::success, "Package %s is installed from cache", action.spec); + } + else + Checks::unreachable(); + } + catch (const std::exception& e) + { + System::println(System::color::error, "Error: Could not install package %s: %s", action.spec, e.what()); + results.back() = BuildResult::NULLVALUE; + } + System::println("Elapsed time for package %s: %s", action.spec, build_timer.toString()); + } + + System::println("Total time taken: %s", timer.toString()); + + for (size_t i = 0; i < results.size(); i++) + { + System::println("%s: %s: %dms", install_plan[i].spec.toString(), Build::to_string(results[i]), timing[i]); + } + + std::map<BuildResult, int> summary; + for (const BuildResult& v : Build::BuildResult_values) + { + summary[v] = 0; + } + + for (const BuildResult& r : results) + { + summary[r]++; + } + + System::println("\n\nSUMMARY"); + for (const std::pair<const BuildResult, int>& entry : summary) + { + System::println(" %s: %d", Build::to_string(entry.first), entry.second); + } + + exit(EXIT_SUCCESS); + } +} diff --git a/toolsrc/src/commands_contact.cpp b/toolsrc/src/commands_contact.cpp new file mode 100644 index 000000000..29f0d4d27 --- /dev/null +++ b/toolsrc/src/commands_contact.cpp @@ -0,0 +1,21 @@ +#include "pch.h" +#include "vcpkg_Commands.h" +#include "vcpkg_System.h" + +namespace vcpkg::Commands::Contact +{ + const std::string& email() + { + static const std::string s_email = R"(vcpkg@microsoft.com)"; + return s_email; + } + + void perform_and_exit(const vcpkg_cmd_arguments& args) + { + args.check_exact_arg_count(0); + args.check_and_get_optional_command_arguments({}); + + System::println("Send an email to %s with any feedback.", email()); + exit(EXIT_SUCCESS); + } +} diff --git a/toolsrc/src/commands_create.cpp b/toolsrc/src/commands_create.cpp index ad00cd676..b74693ed5 100644 --- a/toolsrc/src/commands_create.cpp +++ b/toolsrc/src/commands_create.cpp @@ -1,37 +1,41 @@ +#include "pch.h" #include "vcpkg_Commands.h" #include "vcpkg_System.h" #include "vcpkg_Environment.h" #include "vcpkg_Files.h" #include "vcpkg_Input.h" +#include "vcpkglib.h" -namespace vcpkg +namespace vcpkg::Commands::Create { - void create_command(const vcpkg_cmd_arguments& args, const vcpkg_paths& paths) + void perform_and_exit(const vcpkg_cmd_arguments& args, const vcpkg_paths& paths) { - static const std::string example = create_example_string(R"###(create zlib2 http://zlib.net/zlib128.zip "zlib128-2.zip")###"); + static const std::string example = Commands::Help::create_example_string(R"###(create zlib2 http://zlib.net/zlib1211.zip "zlib1211-2.zip")###"); args.check_max_arg_count(3, example); args.check_min_arg_count(2, example); - + args.check_and_get_optional_command_arguments({}); const std::string port_name = args.command_arguments.at(0); - Environment::ensure_utilities_on_path(paths); + const std::string url = args.command_arguments.at(1); + + const fs::path& cmake_exe = paths.get_cmake_exe(); + + std::vector<CMakeVariable> cmake_args + { + { L"CMD", L"CREATE" }, + { L"PORT", port_name }, + { L"URL", url } + }; - // Space OR define the FILENAME with proper spacing - std::wstring custom_filename = L" "; if (args.command_arguments.size() >= 3) { const std::string& zip_file_name = args.command_arguments.at(2); Checks::check_exit(!Files::has_invalid_chars_for_filesystem(zip_file_name), R"(Filename cannot contain invalid chars %s, but was %s)", Files::FILESYSTEM_INVALID_CHARACTERS, zip_file_name); - custom_filename = Strings::wformat(LR"( -DFILENAME="%s" )", Strings::utf8_to_utf16(zip_file_name)); + cmake_args.push_back({ L"FILENAME", zip_file_name }); } - const std::wstring cmdline = Strings::wformat(LR"(cmake -DCMD=CREATE -DPORT=%s -DURL=%s%s-P "%s")", - Strings::utf8_to_utf16(port_name), - Strings::utf8_to_utf16(args.command_arguments.at(1)), - custom_filename, - paths.ports_cmake.generic_wstring()); - - exit(System::cmd_execute(cmdline)); + const std::wstring cmd_launch_cmake = make_cmake_cmd(cmake_exe, paths.ports_cmake, cmake_args); + exit(System::cmd_execute_clean(cmd_launch_cmake)); } } diff --git a/toolsrc/src/commands_edit.cpp b/toolsrc/src/commands_edit.cpp index fbf4eab62..ce0557e09 100644 --- a/toolsrc/src/commands_edit.cpp +++ b/toolsrc/src/commands_edit.cpp @@ -1,33 +1,71 @@ +#include "pch.h" #include "vcpkg_Commands.h" #include "vcpkg_System.h" #include "vcpkg_Input.h" +#include "vcpkg_Environment.h" -namespace vcpkg +namespace vcpkg::Commands::Edit { - void edit_command(const vcpkg_cmd_arguments& args, const vcpkg_paths& paths) + void perform_and_exit(const vcpkg_cmd_arguments& args, const vcpkg_paths& paths) { - static const std::string example = create_example_string("edit zlib"); + static const std::string example = Commands::Help::create_example_string("edit zlib"); args.check_exact_arg_count(1, example); + args.check_and_get_optional_command_arguments({}); const std::string port_name = args.command_arguments.at(0); const fs::path portpath = paths.ports / port_name; + Checks::check_exit(fs::is_directory(portpath), R"(Could not find port named "%s")", port_name); + + // Find the user's selected editor + std::wstring env_EDITOR; + + if (env_EDITOR.empty()) + { + const optional<std::wstring> env_EDITOR_optional = System::get_environmental_variable(L"EDITOR"); + if (env_EDITOR_optional) + { + env_EDITOR = *env_EDITOR_optional; + } + } - // Find editor - std::wstring env_EDITOR = System::wdupenv_str(L"EDITOR"); if (env_EDITOR.empty()) { - static const std::wstring CODE_EXE_PATH = LR"(C:\Program Files (x86)\Microsoft VS Code\Code.exe)"; + const fs::path CODE_EXE_PATH = Environment::get_ProgramFiles_32_bit() / "Microsoft VS Code/Code.exe"; if (fs::exists(CODE_EXE_PATH)) { env_EDITOR = CODE_EXE_PATH; } - else + } + + if (env_EDITOR.empty()) + { + static const std::array<const wchar_t*, 4> regkeys = { + LR"(SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{C26E74D1-022E-4238-8B9D-1E7564A36CC9}_is1)", + LR"(SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{C26E74D1-022E-4238-8B9D-1E7564A36CC9}_is1)", + LR"(SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{F8A2A208-72B3-4D61-95FC-8A65D340689B}_is1)", + LR"(SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{F8A2A208-72B3-4D61-95FC-8A65D340689B}_is1)", + }; + for (auto&& keypath : regkeys) { - Checks::exit_with_message("Visual Studio Code was not found and the environmental variable EDITOR is not set"); + auto code_installpath = System::get_registry_string(HKEY_LOCAL_MACHINE, keypath, L"InstallLocation"); + if (code_installpath) + { + auto p = fs::path(*code_installpath) / "Code.exe"; + if (fs::exists(p)) + { + env_EDITOR = p.native(); + break; + } + } } } - std::wstring cmdLine = Strings::wformat(LR"("%s" "%s" "%s")", env_EDITOR, portpath.native(), (portpath / "portfile.cmake").native()); + if (env_EDITOR.empty()) + { + Checks::exit_with_message("Visual Studio Code was not found and the environment variable EDITOR is not set"); + } + + std::wstring cmdLine = Strings::wformat(LR"("%s" "%s" "%s" -n)", env_EDITOR, portpath.native(), (portpath / "portfile.cmake").native()); exit(System::cmd_execute(cmdLine)); } } diff --git a/toolsrc/src/commands_hash.cpp b/toolsrc/src/commands_hash.cpp index 0e3e8a77c..805da4153 100644 --- a/toolsrc/src/commands_hash.cpp +++ b/toolsrc/src/commands_hash.cpp @@ -1,7 +1,8 @@ +#include "pch.h" #include "vcpkg_Commands.h" #include "vcpkg_System.h" -namespace vcpkg +namespace vcpkg::Commands::Hash { static void do_file_hash(fs::path const& path, std::wstring const& hashType) { @@ -23,12 +24,13 @@ namespace vcpkg System::println(hash); } - void hash_command(const vcpkg_cmd_arguments& args) + void perform_and_exit(const vcpkg_cmd_arguments& args) { static const std::string example = Strings::format( - "The argument should be a file path\n%s", create_example_string("hash boost_1_62_0.tar.bz2")); + "The argument should be a file path\n%s", Commands::Help::create_example_string("hash boost_1_62_0.tar.bz2")); args.check_min_arg_count(1, example); args.check_max_arg_count(2, example); + args.check_and_get_optional_command_arguments({}); if (args.command_arguments.size() == 1) { diff --git a/toolsrc/src/commands_help.cpp b/toolsrc/src/commands_help.cpp index fd02d948e..49b5697c3 100644 --- a/toolsrc/src/commands_help.cpp +++ b/toolsrc/src/commands_help.cpp @@ -1,22 +1,74 @@ +#include "pch.h" #include "vcpkg_Commands.h" #include "vcpkg_System.h" -#include "vcpkg_info.h" -namespace vcpkg +namespace vcpkg::Commands::Help { - void version_command(const vcpkg_cmd_arguments& args) + void help_topic_valid_triplet(const vcpkg_paths& paths) { - args.check_exact_arg_count(0); - System::println("Vcpkg package management program version %s\n" - "\n" - "See LICENSE.txt for license information.", Info::version() - ); - exit(EXIT_SUCCESS); + System::println("Available architecture triplets:"); + auto it = fs::directory_iterator(paths.triplets); + for (; it != fs::directory_iterator(); ++it) + { + System::println(" %s", it->path().stem().filename().string()); + } + } + + void print_usage() + { + System::println( + "Commands:\n" + " vcpkg search [pat] Search for packages available to be built\n" + " vcpkg install <pkg> Install a package\n" + " vcpkg remove <pkg> Uninstall a package. \n" + " vcpkg remove --purge <pkg> Uninstall and delete a package. \n" + " vcpkg list List installed packages\n" + " vcpkg update Display list of packages for updating\n" + " vcpkg hash <file> [alg] Hash a file by specific algorithm, default SHA512\n" + "\n" + "%s" // Integration help + "\n" + " vcpkg edit <pkg> Open up a port for editing (uses %%EDITOR%%, default 'code')\n" + " vcpkg import <pkg> Import a pre-built library\n" + " vcpkg create <pkg> <url>\n" + " [archivename] Create a new package\n" + " vcpkg owns <pat> Search for files in installed packages\n" + " vcpkg cache List cached compiled packages\n" + " vcpkg version Display version information\n" + " vcpkg contact Display contact information to send feedback\n" + "\n" + //"internal commands:\n" + //" --check-build-deps <controlfile>\n" + //" --create-binary-control <controlfile>\n" + //"\n" + "Options:\n" + " --triplet <t> Specify the target architecture triplet.\n" + " (default: %%VCPKG_DEFAULT_TRIPLET%%, see 'vcpkg help triplet')\n" + "\n" + " --vcpkg-root <path> Specify the vcpkg root directory\n" + " (default: %%VCPKG_ROOT%%)\n" + "\n" + "For more help (including examples) see the accompanying README.md." + , Integrate::INTEGRATE_COMMAND_HELPSTRING); + } + + std::string create_example_string(const std::string& command_and_arguments) + { + std::string cs = Strings::format("Example:\n" + " vcpkg %s", command_and_arguments); + return cs; + } + + void print_example(const std::string& command_and_arguments) + { + System::println(create_example_string(command_and_arguments)); } - void help_command(const vcpkg_cmd_arguments& args, const vcpkg_paths& paths) + void perform_and_exit(const vcpkg_cmd_arguments& args, const vcpkg_paths& paths) { args.check_max_arg_count(1); + args.check_and_get_optional_command_arguments({}); + if (args.command_arguments.empty()) { print_usage(); @@ -35,21 +87,4 @@ namespace vcpkg } exit(EXIT_SUCCESS); } - - void contact_command(const vcpkg_cmd_arguments& args) - { - args.check_exact_arg_count(0); - System::println("Send an email to %s with any feedback.", Info::email()); - exit(EXIT_SUCCESS); - } - - void help_topic_valid_triplet(const vcpkg_paths& paths) - { - System::println("Available architecture triplets:"); - auto it = fs::directory_iterator(paths.triplets); - for (; it != fs::directory_iterator(); ++it) - { - System::println(" %s", it->path().stem().filename().string()); - } - } } diff --git a/toolsrc/src/commands_helpers.cpp b/toolsrc/src/commands_helpers.cpp new file mode 100644 index 000000000..0c7ce15bb --- /dev/null +++ b/toolsrc/src/commands_helpers.cpp @@ -0,0 +1,7 @@ +#include "vcpkg_Commands.h" +#include "vcpkg_System.h" + +namespace vcpkg::Commands::Helpers +{ + +} diff --git a/toolsrc/src/commands_import.cpp b/toolsrc/src/commands_import.cpp index 3832f0e7b..11924b4b2 100644 --- a/toolsrc/src/commands_import.cpp +++ b/toolsrc/src/commands_import.cpp @@ -1,10 +1,10 @@ +#include "pch.h" #include "vcpkg_Commands.h" #include "Paragraphs.h" #include "StatusParagraph.h" #include "vcpkg_Files.h" -#include <fstream> -namespace vcpkg +namespace vcpkg::Commands::Import { struct Binaries { @@ -12,26 +12,13 @@ namespace vcpkg std::vector<fs::path> libs; }; - static Binaries detect_files_in_directory_ending_with(const fs::path& path) + static Binaries find_binaries_in_dir(const fs::path& path) { Files::check_is_directory(path); Binaries binaries; - - for (auto it = fs::recursive_directory_iterator(path); it != fs::recursive_directory_iterator(); ++it) - { - fs::path file = *it; - // Skip if directory ????? - if (file.extension() == ".dll") - { - binaries.dlls.push_back(file); - } - else if (file.extension() == ".lib") - { - binaries.libs.push_back(file); - } - } - + binaries.dlls = Files::recursive_find_files_with_extension_in_dir(path, ".dll"); + binaries.libs = Files::recursive_find_files_with_extension_in_dir(path, ".lib"); return binaries; } @@ -51,8 +38,8 @@ namespace vcpkg Files::check_is_directory(include_directory); Files::check_is_directory(project_directory); Files::check_is_directory(destination_path); - Binaries debug_binaries = detect_files_in_directory_ending_with(project_directory / "Debug"); - Binaries release_binaries = detect_files_in_directory_ending_with(project_directory / "Release"); + Binaries debug_binaries = find_binaries_in_dir(project_directory / "Debug"); + Binaries release_binaries = find_binaries_in_dir(project_directory / "Release"); fs::path destination_include_directory = destination_path / "include"; fs::copy(include_directory, destination_include_directory, fs::copy_options::recursive | fs::copy_options::overwrite_existing); @@ -75,17 +62,18 @@ namespace vcpkg std::ofstream(control_file_path) << control_file_data; } - void import_command(const vcpkg_cmd_arguments& args, const vcpkg_paths& paths) + void perform_and_exit(const vcpkg_cmd_arguments& args, const vcpkg_paths& paths) { - static const std::string example = create_example_string(R"(import C:\path\to\CONTROLfile C:\path\to\includedir C:\path\to\projectdir)"); + static const std::string example = Commands::Help::create_example_string(R"(import C:\path\to\CONTROLfile C:\path\to\includedir C:\path\to\projectdir)"); args.check_exact_arg_count(3, example); + args.check_and_get_optional_command_arguments({}); const fs::path control_file_path(args.command_arguments[0]); const fs::path include_directory(args.command_arguments[1]); const fs::path project_directory(args.command_arguments[2]); auto pghs = Paragraphs::get_paragraphs(control_file_path); - Checks::check_throw(pghs.size() == 1, "Invalid control file for package"); + Checks::check_exit(pghs.size() == 1, "Invalid control file %s for package", control_file_path.generic_string()); StatusParagraph spgh; spgh.package = BinaryParagraph(pghs[0]); diff --git a/toolsrc/src/commands_install.cpp b/toolsrc/src/commands_install.cpp new file mode 100644 index 000000000..bb3df943c --- /dev/null +++ b/toolsrc/src/commands_install.cpp @@ -0,0 +1,247 @@ +#include "pch.h" +#include "vcpkg_Commands.h" +#include "vcpkglib.h" +#include "metrics.h" +#include "vcpkg_Files.h" +#include "vcpkg_System.h" +#include "vcpkg_Dependencies.h" +#include "vcpkg_Input.h" +#include "Paragraphs.h" + +namespace vcpkg::Commands::Install +{ + using Dependencies::package_spec_with_install_plan; + using Dependencies::install_plan_type; + + static void install_and_write_listfile(const vcpkg_paths& paths, const BinaryParagraph& bpgh) + { + std::vector<std::string> output; + + const fs::path package_prefix_path = paths.package_dir(bpgh.spec); + const size_t prefix_length = package_prefix_path.native().size(); + + const triplet& target_triplet = bpgh.spec.target_triplet(); + const std::string& target_triplet_as_string = target_triplet.canonical_name(); + std::error_code ec; + fs::create_directory(paths.installed / target_triplet_as_string, ec); + output.push_back(Strings::format(R"(%s/)", target_triplet_as_string)); + + for (auto it = fs::recursive_directory_iterator(package_prefix_path); it != fs::recursive_directory_iterator(); ++it) + { + const std::string filename = it->path().filename().generic_string(); + if (fs::is_regular_file(it->status()) && (_stricmp(filename.c_str(), "CONTROL") == 0 || _stricmp(filename.c_str(), "BUILD_INFO") == 0)) + { + // Do not copy the control file + continue; + } + + const std::string suffix = it->path().generic_u8string().substr(prefix_length + 1); + const fs::path target = paths.installed / target_triplet_as_string / suffix; + + auto status = it->status(ec); + if (ec) + { + System::println(System::color::error, "failed: %s: %s", it->path().u8string(), ec.message()); + continue; + } + + if (fs::is_directory(status)) + { + fs::create_directory(target, ec); + if (ec) + { + System::println(System::color::error, "failed: %s: %s", target.u8string(), ec.message()); + } + + // Trailing backslash for directories + output.push_back(Strings::format(R"(%s/%s/)", target_triplet_as_string, suffix)); + continue; + } + + if (fs::is_regular_file(status)) + { + if (fs::exists(target)) + { + System::println(System::color::warning, "File %s was already present and will be overwritten", target.u8string(), ec.message()); + } + fs::copy_file(*it, target, fs::copy_options::overwrite_existing, ec); + if (ec) + { + System::println(System::color::error, "failed: %s: %s", target.u8string(), ec.message()); + } + output.push_back(Strings::format(R"(%s/%s)", target_triplet_as_string, suffix)); + continue; + } + + if (!fs::status_known(status)) + { + System::println(System::color::error, "failed: %s: unknown status", it->path().u8string()); + continue; + } + + System::println(System::color::error, "failed: %s: cannot handle file type", it->path().u8string()); + } + + std::sort(output.begin(), output.end()); + + Files::write_all_lines(paths.listfile_path(bpgh), output); + } + + static void remove_first_n_chars(std::vector<std::string>* strings, const size_t n) + { + for (std::string& s : *strings) + { + s.erase(0, n); + } + }; + + static std::vector<std::string> extract_files_in_triplet(const std::vector<StatusParagraph_and_associated_files>& pgh_and_files, const triplet& triplet) + { + std::vector<std::string> output; + for (const StatusParagraph_and_associated_files& t : pgh_and_files) + { + if (t.pgh.package.spec.target_triplet() != triplet) + { + continue; + } + + output.insert(output.end(), t.files.cbegin(), t.files.cend()); + } + + std::sort(output.begin(), output.end()); + return output; + } + + static ImmutableSortedVector<std::string> build_list_of_package_files(const fs::path& package_dir) + { + const std::vector<fs::path> package_file_paths = Files::recursive_find_all_files_in_dir(package_dir); + std::vector<std::string> package_files; + const size_t package_remove_char_count = package_dir.generic_string().size() + 1; // +1 for the slash + std::transform(package_file_paths.cbegin(), package_file_paths.cend(), std::back_inserter(package_files), [package_remove_char_count](const fs::path& path) + { + std::string as_string = path.generic_string(); + as_string.erase(0, package_remove_char_count); + return std::move(as_string); + }); + + return ImmutableSortedVector<std::string>::create(std::move(package_files)); + } + + static ImmutableSortedVector<std::string> build_list_of_installed_files(const std::vector<StatusParagraph_and_associated_files>& pgh_and_files, const triplet& triplet) + { + std::vector<std::string> installed_files = extract_files_in_triplet(pgh_and_files, triplet); + const size_t installed_remove_char_count = triplet.canonical_name().size() + 1; // +1 for the slash + remove_first_n_chars(&installed_files, installed_remove_char_count); + + return ImmutableSortedVector<std::string>::create(std::move(installed_files)); + } + + void install_package(const vcpkg_paths& paths, const BinaryParagraph& binary_paragraph, StatusParagraphs* status_db) + { + const fs::path package_dir = paths.package_dir(binary_paragraph.spec); + const triplet& triplet = binary_paragraph.spec.target_triplet(); + const std::vector<StatusParagraph_and_associated_files> pgh_and_files = get_installed_files(paths, *status_db); + + const ImmutableSortedVector<std::string> package_files = build_list_of_package_files(package_dir); + const ImmutableSortedVector<std::string> installed_files = build_list_of_installed_files(pgh_and_files, triplet); + + std::vector<std::string> intersection; + std::set_intersection(package_files.cbegin(), package_files.cend(), + installed_files.cbegin(), installed_files.cend(), + std::back_inserter(intersection)); + + if (!intersection.empty()) + { + const fs::path triplet_install_path = paths.installed / triplet.canonical_name(); + System::println(System::color::error, "The following files are already installed in %s and are in conflict with %s", + triplet_install_path.generic_string(), + binary_paragraph.spec); + System::print("\n "); + System::println(Strings::join("\n ", intersection)); + System::println(""); + exit(EXIT_FAILURE); + } + + StatusParagraph spgh; + spgh.package = binary_paragraph; + spgh.want = want_t::install; + spgh.state = install_state_t::half_installed; + for (auto&& dep : spgh.package.depends) + { + if (status_db->find_installed(dep, spgh.package.spec.target_triplet()) == status_db->end()) + { + Checks::unreachable(); + } + } + write_update(paths, spgh); + status_db->insert(std::make_unique<StatusParagraph>(spgh)); + + install_and_write_listfile(paths, spgh.package); + + spgh.state = install_state_t::installed; + write_update(paths, spgh); + status_db->insert(std::make_unique<StatusParagraph>(spgh)); + } + + void perform_and_exit(const vcpkg_cmd_arguments& args, const vcpkg_paths& paths, const triplet& default_target_triplet) + { + static const std::string example = Commands::Help::create_example_string("install zlib zlib:x64-windows curl boost"); + args.check_min_arg_count(1, example); + std::vector<package_spec> specs = Input::check_and_get_package_specs(args.command_arguments, default_target_triplet, example); + Input::check_triplets(specs, paths); + args.check_and_get_optional_command_arguments({}); + + StatusParagraphs status_db = database_load_check(paths); + std::vector<package_spec_with_install_plan> install_plan = Dependencies::create_install_plan(paths, specs, status_db); + Checks::check_exit(!install_plan.empty(), "Install plan cannot be empty"); + + std::string specs_string = install_plan[0].spec.toString(); + for (size_t i = 1; i < install_plan.size(); ++i) + { + specs_string.push_back(','); + specs_string.append(install_plan[i].spec.toString()); + } + TrackProperty("installplan", specs_string); + + for (const package_spec_with_install_plan& action : install_plan) + { + try + { + if (action.plan.plan_type == install_plan_type::ALREADY_INSTALLED) + { + if (std::find(specs.begin(), specs.end(), action.spec) != specs.end()) + { + System::println(System::color::success, "Package %s is already installed", action.spec); + } + } + else if (action.plan.plan_type == install_plan_type::BUILD_AND_INSTALL) + { + const Build::BuildResult result = Commands::Build::build_package(*action.plan.source_pgh, action.spec, paths, paths.port_dir(action.spec), status_db); + if (result != Build::BuildResult::SUCCEEDED) + { + System::println(System::color::error, Build::create_error_message(result, action.spec)); + System::println(Build::create_user_troubleshooting_message(action.spec)); + exit(EXIT_FAILURE); + } + const BinaryParagraph bpgh = Paragraphs::try_load_cached_package(paths, action.spec).get_or_throw(); + install_package(paths, bpgh, &status_db); + System::println(System::color::success, "Package %s is installed", action.spec); + } + else if (action.plan.plan_type == install_plan_type::INSTALL) + { + install_package(paths, *action.plan.binary_pgh, &status_db); + System::println(System::color::success, "Package %s is installed", action.spec); + } + else + Checks::unreachable(); + } + catch (const std::exception& e) + { + System::println(System::color::error, "Error: Could not install package %s: %s", action.spec, e.what()); + exit(EXIT_FAILURE); + } + } + + exit(EXIT_SUCCESS); + } +} diff --git a/toolsrc/src/commands_installation.cpp b/toolsrc/src/commands_installation.cpp deleted file mode 100644 index 8d940bc9d..000000000 --- a/toolsrc/src/commands_installation.cpp +++ /dev/null @@ -1,368 +0,0 @@ -#include "vcpkg_Commands.h" -#include "vcpkg.h" -#include <fstream> -#include "vcpkg_Environment.h" -#include "metrics.h" -#include "vcpkg_Files.h" -#include "post_build_lint.h" -#include "vcpkg_System.h" -#include "vcpkg_Dependencies.h" -#include "vcpkg_Input.h" -#include "vcpkg_Maps.h" -#include "vcpkg_info.h" - -namespace vcpkg -{ - using Dependencies::package_spec_with_install_plan; - using Dependencies::install_plan_type; - - static const std::string OPTION_CHECKS_ONLY = "--checks-only"; - - static void create_binary_control_file(const vcpkg_paths& paths, const SourceParagraph& source_paragraph, const triplet& target_triplet) - { - const BinaryParagraph bpgh = BinaryParagraph(source_paragraph, target_triplet); - const fs::path binary_control_file = paths.packages / bpgh.dir() / "CONTROL"; - std::ofstream(binary_control_file) << bpgh; - } - - static void build_internal(const SourceParagraph& source_paragraph, const package_spec& spec, const vcpkg_paths& paths, const fs::path& port_dir) - { - Checks::check_exit(spec.name() == source_paragraph.name, "inconsistent arguments to build_internal()"); - const triplet& target_triplet = spec.target_triplet(); - - const fs::path ports_cmake_script_path = paths.ports_cmake; - const std::wstring command = Strings::wformat(LR"("%%VS140COMNTOOLS%%..\..\VC\vcvarsall.bat" %s && cmake -DCMD=BUILD -DPORT=%s -DTARGET_TRIPLET=%s "-DCURRENT_PORT_DIR=%s/." -P "%s")", - Strings::utf8_to_utf16(target_triplet.architecture()), - Strings::utf8_to_utf16(source_paragraph.name), - Strings::utf8_to_utf16(target_triplet.canonical_name()), - port_dir.generic_wstring(), - ports_cmake_script_path.generic_wstring()); - - System::Stopwatch2 timer; - timer.start(); - int return_code = System::cmd_execute(command); - timer.stop(); - TrackMetric("buildtimeus-" + to_string(spec), timer.microseconds()); - - if (return_code != 0) - { - System::println(System::color::error, "Error: building package %s failed", to_string(spec)); - System::println("Please ensure sure 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." - , to_string(spec), Info::version()); - TrackProperty("error", "build failed"); - TrackProperty("build_error", to_string(spec)); - exit(EXIT_FAILURE); - } - - PostBuildLint::perform_all_checks(spec, paths); - - create_binary_control_file(paths, source_paragraph, target_triplet); - - // const fs::path port_buildtrees_dir = paths.buildtrees / spec.name; - // delete_directory(port_buildtrees_dir); - } - - static void install_and_write_listfile(const vcpkg_paths& paths, const BinaryParagraph& bpgh) - { - std::vector<std::string> output; - - const fs::path package_prefix_path = paths.package_dir(bpgh.spec); - const size_t prefix_length = package_prefix_path.native().size(); - - const triplet& target_triplet = bpgh.spec.target_triplet(); - const std::string& target_triplet_as_string = target_triplet.canonical_name(); - std::error_code ec; - fs::create_directory(paths.installed / target_triplet_as_string, ec); - output.push_back(Strings::format(R"(%s)", target_triplet_as_string)); - - for (auto it = fs::recursive_directory_iterator(package_prefix_path); it != fs::recursive_directory_iterator(); ++it) - { - const std::string filename = it->path().filename().generic_string(); - if (fs::is_regular_file(it->status()) && (_stricmp(filename.c_str(), "CONTROL") == 0 || _stricmp(filename.c_str(), "BUILD_INFO") == 0)) - { - // Do not copy the control file - continue; - } - - const std::string suffix = it->path().generic_u8string().substr(prefix_length + 1); - const fs::path target = paths.installed / target_triplet_as_string / suffix; - - auto status = it->status(ec); - if (ec) - { - System::println(System::color::error, "failed: %s: %s", it->path().u8string(), ec.message()); - continue; - } - - if (fs::is_directory(status)) - { - fs::create_directory(target, ec); - if (ec) - { - System::println(System::color::error, "failed: %s: %s", target.u8string(), ec.message()); - } - - // Trailing backslash for directories - output.push_back(Strings::format(R"(%s/%s)", target_triplet_as_string, suffix)); - continue; - } - - if (fs::is_regular_file(status)) - { - if (fs::exists(target)) - { - System::println(System::color::warning, "File %s was already present and will be overwritten", target.u8string(), ec.message()); - } - fs::copy_file(*it, target, fs::copy_options::overwrite_existing, ec); - if (ec) - { - System::println(System::color::error, "failed: %s: %s", target.u8string(), ec.message()); - } - output.push_back(Strings::format(R"(%s/%s)", target_triplet_as_string, suffix)); - continue; - } - - if (!fs::status_known(status)) - { - System::println(System::color::error, "failed: %s: unknown status", it->path().u8string()); - continue; - } - - System::println(System::color::error, "failed: %s: cannot handle file type", it->path().u8string()); - } - - Files::write_all_lines(paths.listfile_path(bpgh), output); - } - - static void remove_first_n_chars(std::vector<std::string>* strings, const size_t n) - { - for (std::string& s : *strings) - { - s.erase(0, n); - } - }; - - static std::vector<std::string> extract_files_in_triplet(const std::vector<StatusParagraph_and_associated_files>& pgh_and_files, const triplet& triplet) - { - std::vector<std::string> output; - for (const StatusParagraph_and_associated_files& t : pgh_and_files) - { - if (t.pgh.package.spec.target_triplet() != triplet) - { - continue; - } - - output.insert(output.end(), t.files.cbegin(), t.files.cend()); - } - - std::sort(output.begin(), output.end()); - return output; - } - - void install_package(const vcpkg_paths& paths, const BinaryParagraph& binary_paragraph, StatusParagraphs& status_db) - { - const fs::path package_dir = paths.package_dir(binary_paragraph.spec); - const std::vector<fs::path> package_file_paths = Files::recursive_find_all_files_in_dir(package_dir); - std::vector<std::string> package_files; - const size_t package_remove_char_count = package_dir.generic_string().size() + 1; // +1 for the slash - std::transform(package_file_paths.cbegin(), package_file_paths.cend(), std::back_inserter(package_files), [package_remove_char_count](const fs::path& path) - { - std::string as_string = path.generic_string(); - as_string.erase(0, package_remove_char_count); - return std::move(as_string); - }); - std::sort(package_files.begin(), package_files.end()); - - const std::vector<StatusParagraph_and_associated_files>& pgh_and_files = get_installed_files(paths, status_db); - const triplet& triplet = binary_paragraph.spec.target_triplet(); - std::vector<std::string> installed_files = extract_files_in_triplet(pgh_and_files, triplet); - const size_t installed_remove_char_count = triplet.canonical_name().size() + 1; // +1 for the slash - remove_first_n_chars(&installed_files, installed_remove_char_count); - std::sort(installed_files.begin(), installed_files.end()); // Should already be sorted - - std::vector<std::string> intersection; - std::set_intersection(package_files.cbegin(), package_files.cend(), - installed_files.cbegin(), installed_files.cend(), - std::back_inserter(intersection)); - - if (!intersection.empty()) - { - const fs::path triplet_install_path = paths.installed / triplet.canonical_name(); - System::println(System::color::error, "The following files are already installed in %s and are in conflict with %s", - triplet_install_path.generic_string(), - binary_paragraph.spec); - System::println(""); - for (const std::string& s : intersection) - { - System::println(" %s", s); - } - System::println(""); - exit(EXIT_FAILURE); - } - - StatusParagraph spgh; - spgh.package = binary_paragraph; - spgh.want = want_t::install; - spgh.state = install_state_t::half_installed; - for (auto&& dep : spgh.package.depends) - { - if (status_db.find_installed(dep, spgh.package.spec.target_triplet()) == status_db.end()) - { - Checks::unreachable(); - } - } - write_update(paths, spgh); - status_db.insert(std::make_unique<StatusParagraph>(spgh)); - - install_and_write_listfile(paths, spgh.package); - - spgh.state = install_state_t::installed; - write_update(paths, spgh); - status_db.insert(std::make_unique<StatusParagraph>(spgh)); - } - - void install_command(const vcpkg_cmd_arguments& args, const vcpkg_paths& paths, const triplet& default_target_triplet) - { - static const std::string example = create_example_string("install zlib zlib:x64-windows curl boost"); - args.check_min_arg_count(1, example); - StatusParagraphs status_db = database_load_check(paths); - - std::vector<package_spec> specs = Input::check_and_get_package_specs(args.command_arguments, default_target_triplet, example); - Input::check_triplets(specs, paths); - std::vector<package_spec_with_install_plan> install_plan = Dependencies::create_install_plan(paths, specs, status_db); - Checks::check_exit(!install_plan.empty(), "Install plan cannot be empty"); - - std::string specs_string = to_string(install_plan[0].spec); - for (size_t i = 1; i < install_plan.size(); ++i) - { - specs_string.push_back(','); - specs_string.append(to_string(install_plan[i].spec)); - } - TrackProperty("installplan", specs_string); - Environment::ensure_utilities_on_path(paths); - - for (const package_spec_with_install_plan& action : install_plan) - { - try - { - if (action.plan.type == install_plan_type::ALREADY_INSTALLED) - { - if (std::find(specs.begin(), specs.end(), action.spec) != specs.end()) - { - System::println(System::color::success, "Package %s is already installed", action.spec); - } - } - else if (action.plan.type == install_plan_type::BUILD_AND_INSTALL) - { - build_internal(*action.plan.spgh, action.spec, paths, paths.port_dir(action.spec)); - const BinaryParagraph bpgh = try_load_cached_package(paths, action.spec).get_or_throw(); - install_package(paths, bpgh, status_db); - System::println(System::color::success, "Package %s is installed", action.spec); - } - else if (action.plan.type == install_plan_type::INSTALL) - { - install_package(paths, *action.plan.bpgh, status_db); - System::println(System::color::success, "Package %s is installed", action.spec); - } - else - Checks::unreachable(); - } - catch (const std::exception& e) - { - System::println(System::color::error, "Error: Could not install package %s: %s", action.spec, e.what()); - exit(EXIT_FAILURE); - } - } - - exit(EXIT_SUCCESS); - } - - void build_command(const vcpkg_cmd_arguments& args, const vcpkg_paths& paths, const triplet& default_target_triplet) - { - static const std::string example = create_example_string("build zlib:x64-windows"); - - // Installing multiple packages leads to unintuitive behavior if one of them depends on another. - // Allowing only 1 package for now. - - args.check_exact_arg_count(1, example); - - StatusParagraphs status_db = database_load_check(paths); - - const package_spec spec = Input::check_and_get_package_spec(args.command_arguments.at(0), default_target_triplet, example); - Input::check_triplet(spec.target_triplet(), paths); - - const std::unordered_set<std::string> options = args.check_and_get_optional_command_arguments({OPTION_CHECKS_ONLY}); - if (options.find(OPTION_CHECKS_ONLY) != options.end()) - { - PostBuildLint::perform_all_checks(spec, paths); - exit(EXIT_SUCCESS); - } - - // Explicitly load and use the portfile's build dependencies when resolving the build command (instead of a cached package's dependencies). - const expected<SourceParagraph> maybe_spgh = try_load_port(paths, spec.name()); - Checks::check_exit(!maybe_spgh.error_code(), "Could not find package named %s: %s", spec, maybe_spgh.error_code().message()); - const SourceParagraph& spgh = *maybe_spgh.get(); - - const std::vector<std::string> first_level_deps = filter_dependencies(spgh.depends, spec.target_triplet()); - - std::vector<package_spec> first_level_deps_specs; - for (const std::string& dep : first_level_deps) - { - first_level_deps_specs.push_back(package_spec::from_name_and_triplet(dep, spec.target_triplet()).get_or_throw()); - } - - std::vector<package_spec_with_install_plan> unmet_dependencies = Dependencies::create_install_plan(paths, first_level_deps_specs, status_db); - unmet_dependencies.erase( - std::remove_if(unmet_dependencies.begin(), unmet_dependencies.end(), [](const package_spec_with_install_plan& p) - { - return p.plan.type == install_plan_type::ALREADY_INSTALLED; - }), - unmet_dependencies.end()); - - if (!unmet_dependencies.empty()) - { - System::println(System::color::error, "The build command requires all dependencies to be already installed."); - System::println("The following dependencies are missing:"); - System::println(""); - for (const package_spec_with_install_plan& p : unmet_dependencies) - { - System::println(" %s", to_string(p.spec)); - } - System::println(""); - exit(EXIT_FAILURE); - } - - Environment::ensure_utilities_on_path(paths); - build_internal(spgh, spec, paths, paths.port_dir(spec)); - exit(EXIT_SUCCESS); - } - - void build_external_command(const vcpkg_cmd_arguments& args, const vcpkg_paths& paths, const triplet& default_target_triplet) - { - static const std::string example = create_example_string(R"(build_external zlib2 C:\path\to\dir\with\controlfile\)"); - args.check_exact_arg_count(2, example); - - expected<package_spec> maybe_current_spec = package_spec::from_string(args.command_arguments[0], default_target_triplet); - if (auto spec = maybe_current_spec.get()) - { - Input::check_triplet(spec->target_triplet(), paths); - Environment::ensure_utilities_on_path(paths); - const fs::path port_dir = args.command_arguments.at(1); - const expected<SourceParagraph> maybe_spgh = try_load_port(port_dir); - if (auto spgh = maybe_spgh.get()) - { - build_internal(*spgh, *spec, paths, port_dir); - exit(EXIT_SUCCESS); - } - } - - System::println(System::color::error, "Error: %s: %s", maybe_current_spec.error_code().message(), args.command_arguments[0]); - print_example(Strings::format("%s zlib:x64-windows", args.command)); - exit(EXIT_FAILURE); - } -} diff --git a/toolsrc/src/commands_integration.cpp b/toolsrc/src/commands_integrate.cpp index e7e5b2d8d..f98b9f77c 100644 --- a/toolsrc/src/commands_integration.cpp +++ b/toolsrc/src/commands_integrate.cpp @@ -1,22 +1,17 @@ -#define WIN32_LEAN_AND_MEAN -#include <windows.h> -#include <shellapi.h> +#include "pch.h" #include "vcpkg_Commands.h" -#include <fstream> -#include <regex> -#include <array> #include "vcpkg_Environment.h" #include "vcpkg_Checks.h" #include "vcpkg_System.h" #include "vcpkg_Files.h" -namespace vcpkg +namespace vcpkg::Commands::Integrate { static const std::array<fs::path, 2> old_system_target_files = { - "C:/Program Files (x86)/MSBuild/14.0/Microsoft.Common.Targets/ImportBefore/vcpkg.nuget.targets", - "C:/Program Files (x86)/MSBuild/14.0/Microsoft.Common.Targets/ImportBefore/vcpkg.system.targets" + Environment::get_ProgramFiles_32_bit() / "MSBuild/14.0/Microsoft.Common.Targets/ImportBefore/vcpkg.nuget.targets", + Environment::get_ProgramFiles_32_bit() / "MSBuild/14.0/Microsoft.Common.Targets/ImportBefore/vcpkg.system.targets" }; - static const fs::path system_wide_targets_file = "C:/Program Files (x86)/MSBuild/Microsoft.Cpp/v4.0/V140/ImportBefore/Default/vcpkg.system.props"; + static const fs::path system_wide_targets_file = Environment::get_ProgramFiles_32_bit() / "MSBuild/Microsoft.Cpp/v4.0/V140/ImportBefore/Default/vcpkg.system.props"; static std::string create_appdata_targets_shortcut(const std::string& target_path) noexcept { @@ -114,7 +109,7 @@ namespace vcpkg static elevation_prompt_user_choice elevated_cmd_execute(const std::string& param) { - SHELLEXECUTEINFO shExInfo = {0}; + SHELLEXECUTEINFO shExInfo = { 0 }; shExInfo.cbSize = sizeof(shExInfo); shExInfo.fMask = SEE_MASK_NOCLOSEPROCESS; shExInfo.hwnd = nullptr; @@ -141,7 +136,7 @@ namespace vcpkg static fs::path get_appdata_targets_path() { - return fs::path(System::wdupenv_str(L"LOCALAPPDATA")) / "vcpkg" / "vcpkg.user.targets"; + return fs::path(*System::get_environmental_variable(L"LOCALAPPDATA")) / "vcpkg" / "vcpkg.user.targets"; } static void integrate_install(const vcpkg_paths& paths) @@ -171,20 +166,17 @@ namespace vcpkg fs::create_directory(tmp_dir); bool should_install_system = true; - if (fs::exists(system_wide_targets_file)) + const expected<std::string> system_wide_file_contents = Files::read_contents(system_wide_targets_file); + if (auto contents_data = system_wide_file_contents.get()) { - auto system_wide_file_contents = Files::read_contents(system_wide_targets_file); - if (auto contents_data = system_wide_file_contents.get()) + std::regex re(R"###(<!-- version (\d+) -->)###"); + std::match_results<std::string::const_iterator> match; + auto found = std::regex_search(*contents_data, match, re); + if (found) { - std::regex re(R"###(<!-- version (\d+) -->)###"); - std::match_results<std::string::const_iterator> match; - auto found = std::regex_search(*contents_data, match, re); - if (found) - { - int ver = atoi(match[1].str().c_str()); - if (ver >= 1) - should_install_system = false; - } + int ver = atoi(match[1].str().c_str()); + if (ver >= 1) + should_install_system = false; } } @@ -219,37 +211,45 @@ namespace vcpkg exit(EXIT_FAILURE); } System::println(System::color::success, "Applied user-wide integration for this vcpkg root."); + const fs::path cmake_toolchain = paths.buildsystems / "vcpkg.cmake"; System::println("\n" - "All C++ projects can now #include any installed libraries.\n" - "Linking will be handled automatically.\n" - "Installing new libraries will make them instantly available."); + "All MSBuild C++ projects can now #include any installed libraries.\n" + "Linking will be handled automatically.\n" + "Installing new libraries will make them instantly available.\n" + "\n" + "CMake projects should use -DCMAKE_TOOLCHAIN_FILE=%s", cmake_toolchain.generic_string()); exit(EXIT_SUCCESS); } static void integrate_remove() { - auto path = get_appdata_targets_path(); - if (!fs::exists(path)) + const fs::path path = get_appdata_targets_path(); + + std::error_code ec; + bool was_deleted = fs::remove(path, ec); + + if (ec) { - System::println(System::color::success, "User-wide integration is not installed"); - exit(EXIT_SUCCESS); + System::println(System::color::error, "Error: Unable to remove user-wide integration: %d", ec.message()); + exit(EXIT_FAILURE); } - const std::wstring cmd_line = Strings::wformat(LR"(DEL "%s")", get_appdata_targets_path().native()); - const int exit_code = System::cmd_execute(cmd_line); - if (exit_code) + if (was_deleted) + { + System::println(System::color::success, "User-wide integration was removed"); + } + else { - System::println(System::color::error, "Error: Unable to remove user-wide integration: %d", exit_code); - exit(exit_code); + System::println(System::color::success, "User-wide integration is not installed"); } - System::println(System::color::success, "User-wide integration was removed"); + exit(EXIT_SUCCESS); } static void integrate_project(const vcpkg_paths& paths) { - Environment::ensure_nuget_on_path(paths); + const fs::path& nuget_exe = paths.get_nuget_exe(); const fs::path& buildsystems_dir = paths.buildsystems; const fs::path tmp_dir = buildsystems_dir / "tmp"; @@ -267,17 +267,12 @@ namespace vcpkg std::ofstream(nuspec_file_path) << create_nuspec_file(paths.root, nuget_id, nupkg_version); // Using all forward slashes for the command line - const std::wstring cmd_line = Strings::wformat(LR"(nuget.exe pack -OutputDirectory "%s" "%s" > nul)", buildsystems_dir.native(), nuspec_file_path.native()); + const std::wstring cmd_line = Strings::wformat(LR"("%s" pack -OutputDirectory "%s" "%s" > nul)", nuget_exe.native(), buildsystems_dir.native(), nuspec_file_path.native()); - const int exit_code = System::cmd_execute(cmd_line); + const int exit_code = System::cmd_execute_clean(cmd_line); const fs::path nuget_package = buildsystems_dir / Strings::format("%s.%s.nupkg", nuget_id, nupkg_version); - if (exit_code != 0 || !fs::exists(nuget_package)) - { - System::println(System::color::error, "Error: NuGet package creation failed"); - exit(EXIT_FAILURE); - } - + Checks::check_exit(exit_code == 0 && fs::exists(nuget_package), "Error: NuGet package creation failed"); System::println(System::color::success, "Created nupkg: %s", nuget_package.string()); System::println(R"( @@ -293,11 +288,12 @@ With a project open, go to Tools->NuGet Package Manager->Package Manager Console " vcpkg integrate remove Remove user-wide integration\n" " vcpkg integrate project Generate a referencing nuget package for individual VS project use\n"; - void integrate_command(const vcpkg_cmd_arguments& args, const vcpkg_paths& paths) + void perform_and_exit(const vcpkg_cmd_arguments& args, const vcpkg_paths& paths) { static const std::string example = Strings::format("Commands:\n" "%s", INTEGRATE_COMMAND_HELPSTRING); args.check_exact_arg_count(1, example); + args.check_and_get_optional_command_arguments({}); if (args.command_arguments[0] == "install") { diff --git a/toolsrc/src/commands_list.cpp b/toolsrc/src/commands_list.cpp index cc51232e9..7bfc11f1b 100644 --- a/toolsrc/src/commands_list.cpp +++ b/toolsrc/src/commands_list.cpp @@ -1,9 +1,10 @@ +#include "pch.h" #include "vcpkg_Commands.h" -#include "vcpkg.h" +#include "vcpkglib.h" #include "vcpkg_System.h" #include "vcpkglib_helpers.h" -namespace vcpkg +namespace vcpkg::Commands::List { static void do_print(const StatusParagraph& pgh) { @@ -13,11 +14,12 @@ namespace vcpkg details::shorten_description(pgh.package.description)); } - void list_command(const vcpkg_cmd_arguments& args, const vcpkg_paths& paths) + void perform_and_exit(const vcpkg_cmd_arguments& args, const vcpkg_paths& paths) { static const std::string example = Strings::format( - "The argument should be a substring to search for, or no argument to display all installed libraries.\n%s", create_example_string("list png")); + "The argument should be a substring to search for, or no argument to display all installed libraries.\n%s", Commands::Help::create_example_string("list png")); args.check_max_arg_count(1, example); + args.check_and_get_optional_command_arguments({}); const StatusParagraphs status_paragraphs = database_load_check(paths); std::vector<StatusParagraph> installed_packages; diff --git a/toolsrc/src/commands_other.cpp b/toolsrc/src/commands_other.cpp deleted file mode 100644 index 6df325100..000000000 --- a/toolsrc/src/commands_other.cpp +++ /dev/null @@ -1,103 +0,0 @@ -#include "vcpkg_Commands.h" -#include "vcpkg_System.h" - -namespace vcpkg -{ - void print_usage() - { - System::println( - "Commands:\n" - " vcpkg search [pat] Search for packages available to be built\n" - " vcpkg install <pkg> Install a package\n" - " vcpkg remove <pkg> Uninstall a package. \n" - " vcpkg remove --purge <pkg> Uninstall and delete a package. \n" - " vcpkg list List installed packages\n" - " vcpkg update Display list of packages for updating\n" - " vcpkg hash <file> [alg] Hash a file by specific algorithm, default SHA512\n" - "\n" - "%s" // Integration help - "\n" - " vcpkg edit <pkg> Open up a port for editing (uses %%EDITOR%%, default 'code')\n" - " vcpkg import <pkg> Import a pre-built library\n" - " vcpkg create <pkg> <url>\n" - " [archivename] Create a new package\n" - " vcpkg owns <pat> Search for files in installed packages\n" - " vcpkg cache List cached compiled packages\n" - " vcpkg version Display version information\n" - " vcpkg contact Display contact information to send feedback\n" - "\n" - //"internal commands:\n" - //" --check-build-deps <controlfile>\n" - //" --create-binary-control <controlfile>\n" - //"\n" - "Options:\n" - " --triplet <t> Specify the target architecture triplet.\n" - " (default: %%VCPKG_DEFAULT_TRIPLET%%, see 'vcpkg help triplet')\n" - "\n" - " --vcpkg-root <path> Specify the vcpkg root directory\n" - " (default: %%VCPKG_ROOT%%)\n" - "\n" - "For more help (including examples) see the accompanying README.md." - , INTEGRATE_COMMAND_HELPSTRING); - } - - std::string create_example_string(const std::string& command_and_arguments) - { - std::string cs = Strings::format("Example:\n" - " vcpkg %s", command_and_arguments); - return cs; - } - - void print_example(const std::string& command_and_arguments) - { - System::println(create_example_string(command_and_arguments)); - } - - void internal_test_command(const vcpkg_cmd_arguments& /*args*/, const vcpkg_paths& /*paths*/) - { - // auto data = FormatEventData("test"); - // Track(data); - exit(EXIT_SUCCESS); - } - - const std::vector<package_name_and_function<command_type_a>>& get_available_commands_type_a() - { - static std::vector<package_name_and_function<command_type_a>> t = { - {"install", install_command}, - {"remove", remove_command}, - {"build", build_command}, - {"build_external", build_external_command} - }; - return t; - } - - const std::vector<package_name_and_function<command_type_b>>& get_available_commands_type_b() - { - static std::vector<package_name_and_function<command_type_b>> t = { - {"/?", help_command}, - {"help", help_command}, - {"search", search_command}, - {"list", list_command}, - {"integrate", integrate_command}, - {"owns", owns_command}, - {"update", update_command}, - {"edit", edit_command}, - {"create", create_command}, - {"import", import_command}, - {"cache", cache_command}, - {"internal_test", internal_test_command}, - {"portsdiff", portsdiff_command} - }; - return t; - } - - const std::vector<package_name_and_function<command_type_c>>& get_available_commands_type_c() - { - static std::vector<package_name_and_function<command_type_c>> t = { - {"version", &version_command}, - {"contact", &contact_command}, - {"hash", &hash_command}, - }; - return t; - } -} diff --git a/toolsrc/src/commands_owns.cpp b/toolsrc/src/commands_owns.cpp index 62dac57eb..16bb986e2 100644 --- a/toolsrc/src/commands_owns.cpp +++ b/toolsrc/src/commands_owns.cpp @@ -1,8 +1,9 @@ +#include "pch.h" #include "vcpkg_Commands.h" #include "vcpkg_System.h" -#include "vcpkg.h" +#include "vcpkglib.h" -namespace vcpkg +namespace vcpkg::Commands::Owns { static void search_file(const vcpkg_paths& paths, const std::string& file_substr, const StatusParagraphs& status_db) { @@ -21,10 +22,11 @@ namespace vcpkg } } - void owns_command(const vcpkg_cmd_arguments& args, const vcpkg_paths& paths) + void perform_and_exit(const vcpkg_cmd_arguments& args, const vcpkg_paths& paths) { - static const std::string example = Strings::format("The argument should be a pattern to search for. %s", create_example_string("owns zlib.dll")); + static const std::string example = Strings::format("The argument should be a pattern to search for. %s", Commands::Help::create_example_string("owns zlib.dll")); args.check_exact_arg_count(1, example); + args.check_and_get_optional_command_arguments({}); StatusParagraphs status_db = database_load_check(paths); search_file(paths, args.command_arguments[0], status_db); diff --git a/toolsrc/src/commands_portsdiff.cpp b/toolsrc/src/commands_portsdiff.cpp index 46c6c90c7..4d5a589f6 100644 --- a/toolsrc/src/commands_portsdiff.cpp +++ b/toolsrc/src/commands_portsdiff.cpp @@ -1,26 +1,19 @@ +#include "pch.h" #include "vcpkg_Commands.h" #include "vcpkg_System.h" -#include <map> -#include <iterator> #include "vcpkg_Maps.h" -#include <iostream> -#include <iomanip> -#include <set> -#include "Paragraphs.h" #include "SourceParagraph.h" #include "vcpkg_Environment.h" +#include "Paragraphs.h" -namespace vcpkg +namespace vcpkg::Commands::PortsDiff { static void do_print_name_and_version(const std::vector<std::string>& ports_to_print, const std::map<std::string, std::string>& names_and_versions) { for (const std::string& name : ports_to_print) { const std::string& version = names_and_versions.at(name); - std::cout << std::left - << std::setw(20) << name << ' ' - << std::setw(16) << version << ' ' - << '\n'; + System::println("%-20s %-16s", name, version); } } @@ -30,85 +23,59 @@ namespace vcpkg { for (const std::string& name : ports_to_print) { - if (name == "") - { - continue; - } - const std::string& previous_version = previous_names_and_versions.at(name); const std::string& current_version = current_names_and_versions.at(name); - std::cout << std::left - << std::setw(20) << name << ' ' - << std::setw(16) << previous_version << " -> " << current_version - << '\n'; - } - } - - static std::map<std::string, std::string> read_all_ports(const fs::path& ports_folder_path) - { - std::map<std::string, std::string> names_and_versions; - - for (auto it = fs::directory_iterator(ports_folder_path); it != fs::directory_iterator(); ++it) - { - const fs::path& path = it->path(); - - try - { - auto pghs = Paragraphs::get_paragraphs(path / "CONTROL"); - if (pghs.empty()) - continue; - auto srcpgh = SourceParagraph(pghs[0]); - names_and_versions.emplace(srcpgh.name, srcpgh.version); - } - catch (std::runtime_error const&) - { - } + System::println("%-20s %-16s -> %s", name, previous_version, current_version); } - - return names_and_versions; } static std::map<std::string, std::string> read_ports_from_commit(const vcpkg_paths& paths, const std::wstring& git_commit_id) { + const fs::path& git_exe = paths.get_git_exe(); const fs::path dot_git_dir = paths.root / ".git"; const std::wstring ports_dir_name_as_string = paths.ports.filename().native(); const fs::path temp_checkout_path = paths.root / Strings::wformat(L"%s-%s", ports_dir_name_as_string, git_commit_id); fs::create_directory(temp_checkout_path); const std::wstring checkout_this_dir = Strings::wformat(LR"(.\%s)", ports_dir_name_as_string); // Must be relative to the root of the repository - const std::wstring cmd = Strings::wformat(LR"(git --git-dir="%s" --work-tree="%s" checkout %s -f -q -- %s %s & git reset >NUL)", + const std::wstring cmd = Strings::wformat(LR"("%s" --git-dir="%s" --work-tree="%s" checkout %s -f -q -- %s %s & "%s" reset >NUL)", + git_exe.native(), dot_git_dir.native(), temp_checkout_path.native(), git_commit_id, checkout_this_dir, - L".vcpkg-root"); - System::cmd_execute(cmd); - std::map<std::string, std::string> names_and_versions = read_all_ports(temp_checkout_path / ports_dir_name_as_string); + L".vcpkg-root", + git_exe.native()); + System::cmd_execute_clean(cmd); + const std::vector<SourceParagraph> source_paragraphs = Paragraphs::load_all_ports(temp_checkout_path / ports_dir_name_as_string); + const std::map<std::string, std::string> names_and_versions = Paragraphs::extract_port_names_and_versions(source_paragraphs); fs::remove_all(temp_checkout_path); return names_and_versions; } - static void check_commit_exists(const std::wstring& git_commit_id) + static void check_commit_exists(const fs::path& git_exe, const std::wstring& git_commit_id) { static const std::string VALID_COMMIT_OUTPUT = "commit\n"; - const std::wstring cmd = Strings::wformat(LR"(git cat-file -t %s 2>NUL)", git_commit_id); + const std::wstring cmd = Strings::wformat(LR"("%s" cat-file -t %s 2>NUL)", git_exe.native(), git_commit_id); const System::exit_code_and_output output = System::cmd_execute_and_capture_output(cmd); Checks::check_exit(output.output == VALID_COMMIT_OUTPUT, "Invalid commit id %s", Strings::utf16_to_utf8(git_commit_id)); } - void portsdiff_command(const vcpkg_cmd_arguments& args, const vcpkg_paths& paths) + void perform_and_exit(const vcpkg_cmd_arguments& args, const vcpkg_paths& paths) { - static const std::string example = Strings::format("The argument should be a branch/tag/hash to checkout.\n%s", create_example_string("portsdiff mybranchname")); + static const std::string example = Strings::format("The argument should be a branch/tag/hash to checkout.\n%s", Commands::Help::create_example_string("portsdiff mybranchname")); args.check_min_arg_count(1, example); args.check_max_arg_count(2, example); + args.check_and_get_optional_command_arguments({}); + + const fs::path& git_exe = paths.get_git_exe(); - Environment::ensure_git_on_path(paths); const std::wstring git_commit_id_for_previous_snapshot = Strings::utf8_to_utf16(args.command_arguments.at(0)); const std::wstring git_commit_id_for_current_snapshot = args.command_arguments.size() < 2 ? L"HEAD" : Strings::utf8_to_utf16(args.command_arguments.at(1)); - check_commit_exists(git_commit_id_for_current_snapshot); - check_commit_exists(git_commit_id_for_previous_snapshot); + check_commit_exists(git_exe, git_commit_id_for_current_snapshot); + check_commit_exists(git_exe, git_commit_id_for_previous_snapshot); const std::map<std::string, std::string> current_names_and_versions = read_ports_from_commit(paths, git_commit_id_for_current_snapshot); const std::map<std::string, std::string> previous_names_and_versions = read_ports_from_commit(paths, git_commit_id_for_previous_snapshot); diff --git a/toolsrc/src/commands_remove.cpp b/toolsrc/src/commands_remove.cpp index 445213fc2..1b7b7923a 100644 --- a/toolsrc/src/commands_remove.cpp +++ b/toolsrc/src/commands_remove.cpp @@ -1,12 +1,18 @@ +#include "pch.h" #include "vcpkg_Commands.h" -#include "vcpkg.h" +#include "vcpkglib.h" #include "vcpkg_System.h" #include "vcpkg_Input.h" -#include <fstream> +#include "vcpkg_Dependencies.h" -namespace vcpkg +namespace vcpkg::Commands::Remove { + using Dependencies::package_spec_with_remove_plan; + using Dependencies::remove_plan_type; + using Dependencies::request_type; + static const std::string OPTION_PURGE = "--purge"; + static const std::string OPTION_RECURSE = "--recurse"; static void delete_directory(const fs::path& directory) { @@ -22,78 +28,9 @@ namespace vcpkg } } - enum class deinstall_plan - { - not_installed, - dependencies_not_satisfied, - should_deinstall - }; - - static deinstall_plan deinstall_package_plan( - const StatusParagraphs::iterator package_it, - const StatusParagraphs& status_db, - std::vector<const StatusParagraph*>& dependencies_out) - { - dependencies_out.clear(); - - if (package_it == status_db.end() || (*package_it)->state == install_state_t::not_installed) - { - return deinstall_plan::not_installed; - } - - auto& pkg = (*package_it)->package; - - for (auto&& inst_pkg : status_db) - { - if (inst_pkg->want != want_t::install) - continue; - if (inst_pkg->package.spec.target_triplet() != pkg.spec.target_triplet()) - continue; - - const auto& deps = inst_pkg->package.depends; - - if (std::find(deps.begin(), deps.end(), pkg.spec.name()) != deps.end()) - { - dependencies_out.push_back(inst_pkg.get()); - } - } - - if (!dependencies_out.empty()) - return deinstall_plan::dependencies_not_satisfied; - - return deinstall_plan::should_deinstall; - } - - static void deinstall_package(const vcpkg_paths& paths, const package_spec& spec, StatusParagraphs& status_db) + static void remove_package(const vcpkg_paths& paths, const package_spec& spec, StatusParagraphs* status_db) { - auto package_it = status_db.find(spec.name(), spec.target_triplet()); - if (package_it == status_db.end()) - { - System::println(System::color::success, "Package %s is not installed", spec); - return; - } - - auto& pkg = **package_it; - - std::vector<const StatusParagraph*> deps; - auto plan = deinstall_package_plan(package_it, status_db, deps); - switch (plan) - { - case deinstall_plan::not_installed: - System::println(System::color::success, "Package %s is not installed", spec); - return; - case deinstall_plan::dependencies_not_satisfied: - System::println(System::color::error, "Error: Cannot remove package %s:", spec); - for (auto&& dep : deps) - { - System::println(" %s depends on %s", dep->package.displayname(), pkg.package.displayname()); - } - exit(EXIT_FAILURE); - case deinstall_plan::should_deinstall: - break; - default: - Checks::unreachable(); - } + StatusParagraph& pkg = **status_db->find(spec.name(), spec.target_triplet()); pkg.want = want_t::purge; pkg.state = install_state_t::half_installed; @@ -163,31 +100,125 @@ namespace vcpkg pkg.state = install_state_t::not_installed; write_update(paths, pkg); - System::println(System::color::success, "Package %s was successfully removed", pkg.package.displayname()); } - void remove_command(const vcpkg_cmd_arguments& args, const vcpkg_paths& paths, const triplet& default_target_triplet) + static void sort_packages_by_name(std::vector<const package_spec_with_remove_plan*>* packages) { - static const std::string example = create_example_string("remove zlib zlib:x64-windows curl boost"); - args.check_min_arg_count(1, example); + std::sort(packages->begin(), packages->end(), [](const package_spec_with_remove_plan* left, const package_spec_with_remove_plan* right) -> bool + { + return left->spec.name() < right->spec.name(); + }); + } - const std::unordered_set<std::string> options = args.check_and_get_optional_command_arguments({OPTION_PURGE}); - auto status_db = database_load_check(paths); + static void print_plan(const std::vector<package_spec_with_remove_plan>& plan) + { + std::vector<const package_spec_with_remove_plan*> not_installed; + std::vector<const package_spec_with_remove_plan*> remove; + for (const package_spec_with_remove_plan& i : plan) + { + if (i.plan.plan_type == remove_plan_type::NOT_INSTALLED) + { + not_installed.push_back(&i); + continue; + } + + if (i.plan.plan_type == remove_plan_type::REMOVE) + { + remove.push_back(&i); + continue; + } + + Checks::unreachable(); + } + + if (!not_installed.empty()) + { + sort_packages_by_name(¬_installed); + System::println("The following packages are not installed, so not removed:\n%s", + Strings::join("\n ", not_installed, [](const package_spec_with_remove_plan* p) + { + return " " + p->spec.toString(); + })); + } + + if (!remove.empty()) + { + sort_packages_by_name(&remove); + System::println("The following packages will be removed:\n%s", + Strings::join("\n", remove, [](const package_spec_with_remove_plan* p) + { + if (p->plan.request_type == Dependencies::request_type::AUTO_SELECTED) + { + return " * " + p->spec.toString(); + } + + if (p->plan.request_type == Dependencies::request_type::USER_REQUESTED) + { + return " " + p->spec.toString(); + } + + Checks::unreachable(); + })); + } + } + + void perform_and_exit(const vcpkg_cmd_arguments& args, const vcpkg_paths& paths, const triplet& default_target_triplet) + { + static const std::string example = Commands::Help::create_example_string("remove zlib zlib:x64-windows curl boost"); + args.check_min_arg_count(1, example); std::vector<package_spec> specs = Input::check_and_get_package_specs(args.command_arguments, default_target_triplet, example); Input::check_triplets(specs, paths); - bool alsoRemoveFolderFromPackages = options.find(OPTION_PURGE) != options.end(); + const std::unordered_set<std::string> options = args.check_and_get_optional_command_arguments({ OPTION_PURGE, OPTION_RECURSE }); + const bool alsoRemoveFolderFromPackages = options.find(OPTION_PURGE) != options.end(); + const bool isRecursive = options.find(OPTION_RECURSE) != options.end(); + + auto status_db = database_load_check(paths); + const std::vector<package_spec_with_remove_plan> remove_plan = Dependencies::create_remove_plan(specs, status_db); + Checks::check_exit(!remove_plan.empty(), "Remove plan cannot be empty"); + + print_plan(remove_plan); + + const bool has_non_user_requested_packages = std::find_if(remove_plan.cbegin(), remove_plan.cend(), [](const package_spec_with_remove_plan& package)-> bool + { + return package.plan.request_type != request_type::USER_REQUESTED; + }) != remove_plan.cend(); - for (const package_spec& spec : specs) + if (has_non_user_requested_packages && !isRecursive) { - deinstall_package(paths, spec, status_db); + System::println(System::color::warning, + "Additional packages (*) need to be removed to complete this operation.\n" + "If you are sure you want to remove them, run the command with the --recurse option"); + exit(EXIT_FAILURE); + } + + for (const package_spec_with_remove_plan& action : remove_plan) + { + const std::string display_name = action.spec.display_name(); + + switch (action.plan.plan_type) + { + case remove_plan_type::NOT_INSTALLED: + System::println(System::color::success, "Package %s is not installed", display_name); + break; + case remove_plan_type::REMOVE: + System::println("Removing package %s... ", display_name); + remove_package(paths, action.spec, &status_db); + System::println(System::color::success, "Removing package %s... done", display_name); + break; + case remove_plan_type::UNKNOWN: + default: + Checks::unreachable(); + } if (alsoRemoveFolderFromPackages) { - const fs::path spec_package_dir = paths.packages / spec.dir(); - delete_directory(spec_package_dir); + System::println("Purging package %s... ", display_name); + delete_directory(paths.packages / action.spec.dir()); + System::println(System::color::success, "Purging package %s... done", display_name); } } + exit(EXIT_SUCCESS); } } diff --git a/toolsrc/src/commands_search.cpp b/toolsrc/src/commands_search.cpp index a4714477e..8bac858f1 100644 --- a/toolsrc/src/commands_search.cpp +++ b/toolsrc/src/commands_search.cpp @@ -1,35 +1,47 @@ +#include "pch.h" #include "vcpkg_Commands.h" #include "vcpkg_System.h" #include "Paragraphs.h" #include "vcpkglib_helpers.h" #include "SourceParagraph.h" -namespace vcpkg +namespace vcpkg::Commands::Search { - static std::vector<SourceParagraph> read_all_source_paragraphs(const vcpkg_paths& paths) + static const std::string OPTION_GRAPH = "--graph"; //TODO: This should find a better home, eventually + + static std::string replace_dashes_with_underscore(const std::string& input) { - std::vector<SourceParagraph> output; - for (auto it = fs::directory_iterator(paths.ports); it != fs::directory_iterator(); ++it) - { - const fs::path& path = it->path(); + std::string output = input; + std::replace(output.begin(), output.end(), '-', '_'); + return output; + } - try - { - auto pghs = Paragraphs::get_paragraphs(path / "CONTROL"); - if (pghs.empty()) - { - continue; - } + static std::string create_graph_as_string(const std::vector<SourceParagraph>& source_paragraphs) + { + int empty_node_count = 0; + + std::string s; + s.append("digraph G{ rankdir=LR; edge [minlen=3]; overlap=false;"); - auto srcpgh = SourceParagraph(pghs[0]); - output.push_back(srcpgh); + for (const SourceParagraph& source_paragraph : source_paragraphs) + { + if (source_paragraph.depends.empty()) + { + empty_node_count++; + continue; } - catch (std::runtime_error const&) + + const std::string name = replace_dashes_with_underscore(source_paragraph.name); + s.append(Strings::format("%s;", name)); + for (const dependency& d : source_paragraph.depends) { + const std::string dependency_name = replace_dashes_with_underscore(d.name); + s.append(Strings::format("%s -> %s;", name, dependency_name)); } } - return output; + s.append(Strings::format("empty [label=\"%d singletons...\"]; }", empty_node_count)); + return s; } static void do_print(const SourceParagraph& source_paragraph) @@ -40,14 +52,22 @@ namespace vcpkg details::shorten_description(source_paragraph.description)); } - void search_command(const vcpkg_cmd_arguments& args, const vcpkg_paths& paths) + void perform_and_exit(const vcpkg_cmd_arguments& args, const vcpkg_paths& paths) { - static const std::string example = Strings::format("The argument should be a substring to search for, or no argument to display all libraries.\n%s", create_example_string("search png")); + static const std::string example = Strings::format("The argument should be a substring to search for, or no argument to display all libraries.\n%s", + Commands::Help::create_example_string("search png")); args.check_max_arg_count(1, example); + const std::unordered_set<std::string> options = args.check_and_get_optional_command_arguments({ OPTION_GRAPH }); - const std::vector<SourceParagraph> source_paragraphs = read_all_source_paragraphs(paths); + const std::vector<SourceParagraph> source_paragraphs = Paragraphs::load_all_ports(paths.ports); + if (options.find(OPTION_GRAPH) != options.cend()) + { + const std::string graph_as_string = create_graph_as_string(source_paragraphs); + System::println(graph_as_string); + exit(EXIT_SUCCESS); + } - if (args.command_arguments.size() == 0) + if (args.command_arguments.empty()) { for (const SourceParagraph& source_paragraph : source_paragraphs) { diff --git a/toolsrc/src/commands_update.cpp b/toolsrc/src/commands_update.cpp index a4ab7c6e7..8131d9a73 100644 --- a/toolsrc/src/commands_update.cpp +++ b/toolsrc/src/commands_update.cpp @@ -1,38 +1,22 @@ +#include "pch.h" #include "vcpkg_Commands.h" -#include "vcpkg.h" +#include "vcpkglib.h" #include "vcpkg_System.h" #include "vcpkg_Files.h" #include "Paragraphs.h" -#include "vcpkg_info.h" -namespace vcpkg +namespace vcpkg::Commands::Update { - void update_command(const vcpkg_cmd_arguments& args, const vcpkg_paths& paths) + void perform_and_exit(const vcpkg_cmd_arguments& args, const vcpkg_paths& paths) { args.check_exact_arg_count(0); + args.check_and_get_optional_command_arguments({}); System::println("Using local portfile versions. To update the local portfiles, use `git pull`."); auto status_db = database_load_check(paths); - std::unordered_map<std::string, std::string> src_names_to_versions; - - auto begin_it = fs::directory_iterator(paths.ports); - auto end_it = fs::directory_iterator(); - for (; begin_it != end_it; ++begin_it) - { - const auto& path = begin_it->path(); - try - { - auto pghs = Paragraphs::get_paragraphs(path / "CONTROL"); - if (pghs.empty()) - continue; - auto srcpgh = SourceParagraph(pghs[0]); - src_names_to_versions.emplace(srcpgh.name, srcpgh.version); - } - catch (std::runtime_error const&) - { - } - } + const std::vector<SourceParagraph> source_paragraphs = Paragraphs::load_all_ports(paths.ports); + const std::map<std::string, std::string> src_names_to_versions = Paragraphs::extract_port_names_and_versions(source_paragraphs); std::string packages_list; @@ -78,13 +62,13 @@ namespace vcpkg auto num1 = sscanf_s(version_contents->c_str(), "\"%d.%d.%d\"", &maj1, &min1, &rev1); int maj2, min2, rev2; - auto num2 = sscanf_s(Info::version().c_str(), "%d.%d.%d-", &maj2, &min2, &rev2); + auto num2 = sscanf_s(Version::version().c_str(), "%d.%d.%d-", &maj2, &min2, &rev2); if (num1 == 3 && num2 == 3) { if (maj1 != maj2 || min1 != min2 || rev1 != rev2) { - System::println("Different source is available for vcpkg (%d.%d.%d -> %d.%d.%d). Use scripts\\bootstrap.ps1 to update.", + System::println("Different source is available for vcpkg (%d.%d.%d -> %d.%d.%d). Use powershell -exec bypass scripts/bootstrap.ps1 to update.", maj2, min2, rev2, maj1, min1, rev1); } diff --git a/toolsrc/src/commands_version.cpp b/toolsrc/src/commands_version.cpp new file mode 100644 index 000000000..4789e2409 --- /dev/null +++ b/toolsrc/src/commands_version.cpp @@ -0,0 +1,37 @@ +#include "pch.h" +#include "vcpkg_Commands.h" +#include "vcpkg_System.h" +#include "metrics.h" + +#define STRINGIFY(...) #__VA_ARGS__ +#define MACRO_TO_STRING(X) STRINGIFY(X) + +#define VCPKG_VERSION_AS_STRING MACRO_TO_STRING(VCPKG_VERSION) + +namespace vcpkg::Commands::Version +{ + const std::string& version() + { + static const std::string s_version = +#include "../VERSION.txt" + + + std::string(VCPKG_VERSION_AS_STRING) +#ifndef NDEBUG + + std::string("-debug") +#endif + + std::string(GetCompiledMetricsEnabled() ? "" : "-external"); + return s_version; + } + + void perform_and_exit(const vcpkg_cmd_arguments& args) + { + args.check_exact_arg_count(0); + args.check_and_get_optional_command_arguments({}); + + System::println("Vcpkg package management program version %s\n" + "\n" + "See LICENSE.txt for license information.", version() + ); + exit(EXIT_SUCCESS); + } +} diff --git a/toolsrc/src/main.cpp b/toolsrc/src/main.cpp deleted file mode 100644 index 7703c541f..000000000 --- a/toolsrc/src/main.cpp +++ /dev/null @@ -1,248 +0,0 @@ -#define WIN32_LEAN_AND_MEAN -#include <Windows.h> - -#include <iostream> -#include <fstream> -#include <memory> -#include <cassert> -#include "vcpkg_Commands.h" -#include "metrics.h" -#include <Shlobj.h> -#include "vcpkg_Files.h" -#include "vcpkg_System.h" -#include "vcpkg_Input.h" -#include "Paragraphs.h" -#include "vcpkg_info.h" - -using namespace vcpkg; - -bool g_debugging = false; - -void invalid_command(const std::string& cmd) -{ - System::println(System::color::error, "invalid command: %s", cmd); - print_usage(); - exit(EXIT_FAILURE); -} - -static void inner(const vcpkg_cmd_arguments& args) -{ - TrackProperty("command", args.command); - if (args.command.empty()) - { - print_usage(); - exit(EXIT_FAILURE); - } - - if (auto command_function = find_command(args.command, get_available_commands_type_c())) - { - return command_function(args); - } - - fs::path vcpkg_root_dir; - if (args.vcpkg_root_dir != nullptr) - { - vcpkg_root_dir = fs::absolute(Strings::utf8_to_utf16(*args.vcpkg_root_dir)); - } - else - { - auto vcpkg_root_dir_env = System::wdupenv_str(L"VCPKG_ROOT"); - - if (!vcpkg_root_dir_env.empty()) - { - vcpkg_root_dir = fs::absolute(vcpkg_root_dir_env); - } - else - { - vcpkg_root_dir = Files::find_file_recursively_up(fs::absolute(System::get_exe_path_of_current_process()), ".vcpkg-root"); - } - } - - Checks::check_exit(!vcpkg_root_dir.empty(), "Error: Could not detect vcpkg-root."); - - const expected<vcpkg_paths> expected_paths = vcpkg_paths::create(vcpkg_root_dir); - Checks::check_exit(!expected_paths.error_code(), "Error: Invalid vcpkg root directory %s: %s", vcpkg_root_dir.string(), expected_paths.error_code().message()); - const vcpkg_paths paths = expected_paths.get_or_throw(); - int exit_code = _wchdir(paths.root.c_str()); - Checks::check_exit(exit_code == 0, "Changing the working dir failed"); - - if (auto command_function = find_command(args.command, get_available_commands_type_b())) - { - return command_function(args, paths); - } - - triplet default_target_triplet; - if (args.target_triplet != nullptr) - { - default_target_triplet = triplet::from_canonical_name(*args.target_triplet); - } - else - { - const auto vcpkg_default_triplet_env = System::wdupenv_str(L"VCPKG_DEFAULT_TRIPLET"); - if (!vcpkg_default_triplet_env.empty()) - { - default_target_triplet = triplet::from_canonical_name(Strings::utf16_to_utf8(vcpkg_default_triplet_env)); - } - else - { - default_target_triplet = triplet::X86_WINDOWS; - } - } - - Input::check_triplet(default_target_triplet, paths); - - if (auto command_function = find_command(args.command, get_available_commands_type_a())) - { - return command_function(args, paths, default_target_triplet); - } - - return invalid_command(args.command); -} - -static void loadConfig() -{ - fs::path localappdata; - { - // Config path in AppDataLocal - wchar_t* localappdatapath = nullptr; - if (S_OK != SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, nullptr, &localappdatapath)) - __fastfail(1); - localappdata = localappdatapath; - CoTaskMemFree(localappdatapath); - } - - try - { - std::string config_contents = Files::read_contents(localappdata / "vcpkg" / "config").get_or_throw(); - - std::unordered_map<std::string, std::string> keys; - auto pghs = Paragraphs::parse_paragraphs(config_contents); - if (pghs.size() > 0) - keys = pghs[0]; - - for (size_t x = 1; x < pghs.size(); ++x) - { - for (auto&& p : pghs[x]) - keys.insert(p); - } - - auto user_id = keys["User-Id"]; - auto user_time = keys["User-Since"]; - Checks::check_throw(!user_id.empty() && !user_time.empty(), ""); // Use as goto to the catch statement - - SetUserInformation(user_id, user_time); - return; - } - catch (...) - { - } - - // config file not found, could not be read, or invalid - std::string user_id, user_time; - InitUserInformation(user_id, user_time); - SetUserInformation(user_id, user_time); - try - { - std::error_code ec; - fs::create_directory(localappdata / "vcpkg", ec); - std::ofstream(localappdata / "vcpkg" / "config", std::ios_base::out | std::ios_base::trunc) - << "User-Id: " << user_id << "\n" - << "User-Since: " << user_time << "\n"; - } - catch (...) - { - } -} - -static System::Stopwatch2 g_timer; - -static std::string trim_path_from_command_line(const std::string& full_command_line) -{ - Checks::check_exit(full_command_line.size() > 0, "Internal failure - cannot have empty command line"); - - if (full_command_line[0] == '"') - { - auto it = std::find(full_command_line.cbegin() + 1, full_command_line.cend(), '"'); - if (it != full_command_line.cend()) // Skip over the quote - ++it; - while (it != full_command_line.cend() && *it == ' ') // Skip over a space - ++it; - return std::string(it, full_command_line.cend()); - } - - auto it = std::find(full_command_line.cbegin(), full_command_line.cend(), ' '); - while (it != full_command_line.cend() && *it == ' ') - ++it; - return std::string(it, full_command_line.cend()); -} - -int wmain(const int argc, const wchar_t* const* const argv) -{ - if (argc == 0) - std::abort(); - - std::cout.sync_with_stdio(false); - std::cout.imbue(std::locale::classic()); - - g_timer.start(); - atexit([]() - { - g_timer.stop(); - TrackMetric("elapsed_us", g_timer.microseconds()); - Flush(); - }); - - TrackProperty("version", Info::version()); - - const std::string trimmed_command_line = trim_path_from_command_line(Strings::utf16_to_utf8(GetCommandLineW())); - TrackProperty("cmdline", trimmed_command_line); - loadConfig(); - TrackProperty("sqmuser", GetSQMUser()); - - const vcpkg_cmd_arguments args = vcpkg_cmd_arguments::create_from_command_line(argc, argv); - - if (args.printmetrics != opt_bool::unspecified) - SetPrintMetrics(args.printmetrics == opt_bool::enabled); - if (args.sendmetrics != opt_bool::unspecified) - SetSendMetrics(args.sendmetrics == opt_bool::enabled); - - if (args.debug != opt_bool::unspecified) - { - g_debugging = (args.debug == opt_bool::enabled); - } - - if (g_debugging) - { - inner(args); - exit(EXIT_FAILURE); - } - - std::string exc_msg; - try - { - inner(args); - exit(EXIT_FAILURE); - } - catch (std::exception& e) - { - exc_msg = e.what(); - } - catch (...) - { - exc_msg = "unknown error(...)"; - } - TrackProperty("error", exc_msg); - std::cerr - << "vcpkg.exe has crashed.\n" - << "Please send an email to:\n" - << " " << Info::email() << "\n" - << "containing a brief summary of what you were trying to do and the following data blob:\n" - << "\n" - << "Version=" << Info::version() << "\n" - << "EXCEPTION='" << exc_msg << "'\n" - << "CMD=\n"; - for (int x = 0; x < argc; ++x) - std::cerr << argv[x] << "|\n"; - std::cerr - << "\n"; -} diff --git a/toolsrc/src/metrics.cpp b/toolsrc/src/metrics.cpp index 51c7179c8..d0e20fe2d 100644 --- a/toolsrc/src/metrics.cpp +++ b/toolsrc/src/metrics.cpp @@ -1,15 +1,5 @@ +#include "pch.h" #include "metrics.h" -#include <utility> -#include <array> -#include <string> -#include <iostream> -#include <vector> -#include <sys/timeb.h> -#include <time.h> -#define WIN32_LEAN_AND_MEAN -#include <Windows.h> -#include <winhttp.h> -#include <fstream> #include "filesystem_fs.h" #include "vcpkg_Strings.h" #include "vcpkg_System.h" @@ -42,7 +32,7 @@ namespace vcpkg static std::string GenerateRandomUUID() { - int partSizes[] = {8, 4, 4, 4, 12}; + int partSizes[] = { 8, 4, 4, 4, 12 }; char uuid[37]; memset(uuid, 0, sizeof(uuid)); int num; @@ -111,7 +101,7 @@ namespace vcpkg // Note: this treats incoming Strings as Latin-1 static constexpr const char hex[16] = { '0', '1', '2', '3', '4', '5', '6', '7', - '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; + '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; encoded.append("\\u00"); encoded.push_back(hex[ch / 16]); encoded.push_back(hex[ch % 16]); @@ -237,36 +227,12 @@ true std::wstring GetSQMUser() { - LONG err = NULL; + auto hkcu_sqmclient = System::get_registry_string(HKEY_CURRENT_USER, LR"(Software\Microsoft\SQMClient)", L"UserId"); - struct RAII_HKEY { - HKEY hkey = NULL; - ~RAII_HKEY() - { - if (hkey != NULL) - RegCloseKey(hkey); - } - } HKCU_SQMClient; - - err = RegOpenKeyExW(HKEY_CURRENT_USER, LR"(Software\Microsoft\SQMClient)", NULL, KEY_READ, &HKCU_SQMClient.hkey); - if (err != ERROR_SUCCESS) - { + if (hkcu_sqmclient) + return std::move(*hkcu_sqmclient); + else return L"{}"; - } - - std::array<wchar_t,128> buffer; - DWORD lType = 0; - DWORD dwBufferSize = static_cast<DWORD>(buffer.size() * sizeof(wchar_t)); - err = RegQueryValueExW(HKCU_SQMClient.hkey, L"UserId", NULL, &lType, reinterpret_cast<LPBYTE>(buffer.data()), &dwBufferSize); - if (err == ERROR_SUCCESS && lType == REG_SZ && dwBufferSize >= sizeof(wchar_t)) - { - size_t sz = dwBufferSize / sizeof(wchar_t); - if (buffer[sz - 1] == '\0') - --sz; - return std::wstring(buffer.begin(), buffer.begin() + sz); - } - - return L"{}"; } void SetUserInformation(const std::string& user_id, const std::string& first_use_time) @@ -366,8 +332,7 @@ true if (bResults) { DWORD availableData = 0, readData = 0, totalData = 0; - - while ((bResults = WinHttpQueryDataAvailable(hRequest, &availableData)) && availableData > 0) + while ((bResults = WinHttpQueryDataAvailable(hRequest, &availableData)) == TRUE && availableData > 0) { responseBuffer.resize(responseBuffer.size() + availableData); @@ -452,6 +417,6 @@ true std::ofstream(vcpkg_metrics_txt_path) << payload; const std::wstring cmdLine = Strings::wformat(L"start %s %s", temp_folder_path_exe.native(), vcpkg_metrics_txt_path.native()); - System::cmd_execute(cmdLine); + System::cmd_execute_clean(cmdLine); } } diff --git a/toolsrc/src/opt_bool.cpp b/toolsrc/src/opt_bool.cpp new file mode 100644 index 000000000..324936fb4 --- /dev/null +++ b/toolsrc/src/opt_bool.cpp @@ -0,0 +1,29 @@ +#include "pch.h" +#include "opt_bool.h" +#include "vcpkg_Checks.h" + +namespace vcpkg::opt_bool +{ + static const std::string UNSPECIFIED_NAME = "unspecified"; + static const std::string ENABLED_NAME = "enabled"; + static const std::string DISABLED_NAME = "disabled"; + type parse(const std::string& s) + { + if (s == UNSPECIFIED_NAME) + { + return opt_bool_t::UNSPECIFIED; + } + + if (s == ENABLED_NAME) + { + return opt_bool_t::ENABLED; + } + + if (s == DISABLED_NAME) + { + return opt_bool_t::DISABLED; + } + + Checks::exit_with_message("Could not convert string [%s] to opt_bool", s); + } +} diff --git a/toolsrc/src/package_spec.cpp b/toolsrc/src/package_spec.cpp index 86d4393bd..2713e219f 100644 --- a/toolsrc/src/package_spec.cpp +++ b/toolsrc/src/package_spec.cpp @@ -1,5 +1,5 @@ +#include "pch.h" #include "package_spec.h" -#include <algorithm> namespace vcpkg { @@ -50,19 +50,24 @@ namespace vcpkg return this->m_target_triplet; } + std::string package_spec::display_name() const + { + return Strings::format("%s:%s", this->name(), this->target_triplet()); + } + std::string package_spec::dir() const { return Strings::format("%s_%s", this->m_name, this->m_target_triplet); } - std::string to_string(const package_spec& spec) + std::string package_spec::toString() const { - return Strings::format("%s:%s", spec.name(), spec.target_triplet()); + return this->display_name(); } std::string to_printf_arg(const package_spec& spec) { - return to_string(spec); + return spec.toString(); } bool operator==(const package_spec& left, const package_spec& right) @@ -72,6 +77,6 @@ namespace vcpkg std::ostream& operator<<(std::ostream& os, const package_spec& spec) { - return os << to_string(spec); + return os << spec.toString(); } } diff --git a/toolsrc/src/package_spec_parse_result.cpp b/toolsrc/src/package_spec_parse_result.cpp index dc377f656..892232c2e 100644 --- a/toolsrc/src/package_spec_parse_result.cpp +++ b/toolsrc/src/package_spec_parse_result.cpp @@ -1,5 +1,5 @@ -#include <package_spec.h> -#include <system_error> +#include "pch.h" +#include "package_spec.h" #include "package_spec_parse_result.h" namespace vcpkg diff --git a/toolsrc/src/pch.cpp b/toolsrc/src/pch.cpp new file mode 100644 index 000000000..17305716a --- /dev/null +++ b/toolsrc/src/pch.cpp @@ -0,0 +1 @@ +#include "pch.h"
\ No newline at end of file diff --git a/toolsrc/src/tests_paragraph.cpp b/toolsrc/src/tests_paragraph.cpp index 6d9e46fcf..fb20eee82 100644 --- a/toolsrc/src/tests_paragraph.cpp +++ b/toolsrc/src/tests_paragraph.cpp @@ -7,14 +7,14 @@ using namespace Microsoft::VisualStudio::CppUnitTestFramework; -namespace Microsoft { namespace VisualStudio { namespace CppUnitTestFramework +namespace Microsoft::VisualStudio::CppUnitTestFramework { template <> inline std::wstring ToString<vcpkg::package_spec_parse_result>(const vcpkg::package_spec_parse_result& t) { return ToString(static_cast<uint32_t>(t)); } -}}} +} namespace UnitTest1 { diff --git a/toolsrc/src/triplet.cpp b/toolsrc/src/triplet.cpp index a6816b445..d91e0e68f 100644 --- a/toolsrc/src/triplet.cpp +++ b/toolsrc/src/triplet.cpp @@ -1,6 +1,7 @@ +#include "pch.h" #include "triplet.h" #include "vcpkg_Checks.h" -#include <algorithm> +#include "vcpkg_Strings.h" namespace vcpkg { @@ -37,9 +38,7 @@ namespace vcpkg triplet triplet::from_canonical_name(const std::string& triplet_as_string) { - std::string s(triplet_as_string); - std::transform(s.begin(), s.end(), s.begin(), ::tolower); - + const std::string s(Strings::ascii_to_lowercase(triplet_as_string)); auto it = std::find(s.cbegin(), s.cend(), '-'); Checks::check_exit(it != s.cend(), "Invalid triplet: %s", triplet_as_string); diff --git a/toolsrc/src/vcpkg.cpp b/toolsrc/src/vcpkg.cpp index 4748aeb54..e94d2538b 100644 --- a/toolsrc/src/vcpkg.cpp +++ b/toolsrc/src/vcpkg.cpp @@ -1,248 +1,250 @@ -#include "vcpkg.h" +#define WIN32_LEAN_AND_MEAN +#include <Windows.h> + #include <iostream> -#include <iomanip> #include <fstream> -#include <functional> -#include <string> -#include <unordered_map> #include <memory> -#include <vector> +#include <cassert> +#include "vcpkg_Commands.h" +#include "metrics.h" +#include <Shlobj.h> #include "vcpkg_Files.h" +#include "vcpkg_System.h" +#include "vcpkg_Input.h" #include "Paragraphs.h" -#include <regex> -#include "metrics.h" +#include "vcpkg_Strings.h" +#include "vcpkg_Chrono.h" using namespace vcpkg; -static StatusParagraphs load_current_database(const fs::path& vcpkg_dir_status_file, const fs::path& vcpkg_dir_status_file_old) +bool g_debugging = false; + +void invalid_command(const std::string& cmd) { - if (!fs::exists(vcpkg_dir_status_file)) - { - if (!fs::exists(vcpkg_dir_status_file_old)) - { - // no status file, use empty db - return StatusParagraphs(); - } + System::println(System::color::error, "invalid command: %s", cmd); + Commands::Help::print_usage(); + exit(EXIT_FAILURE); +} - fs::rename(vcpkg_dir_status_file_old, vcpkg_dir_status_file); +static void inner(const vcpkg_cmd_arguments& args) +{ + TrackProperty("command", args.command); + if (args.command.empty()) + { + Commands::Help::print_usage(); + exit(EXIT_FAILURE); } - auto text = Files::read_contents(vcpkg_dir_status_file).get_or_throw(); - auto pghs = Paragraphs::parse_paragraphs(text); - - std::vector<std::unique_ptr<StatusParagraph>> status_pghs; - for (auto&& p : pghs) + if (auto command_function = Commands::find(args.command, Commands::get_available_commands_type_c())) { - status_pghs.push_back(std::make_unique<StatusParagraph>(p)); + return command_function(args); } - return StatusParagraphs(std::move(status_pghs)); -} - -StatusParagraphs vcpkg::database_load_check(const vcpkg_paths& paths) -{ - auto updates_dir = paths.vcpkg_dir_updates; - - std::error_code ec; - fs::create_directory(paths.installed, ec); - fs::create_directory(paths.vcpkg_dir, ec); - fs::create_directory(paths.vcpkg_dir_info, ec); - fs::create_directory(updates_dir, ec); + fs::path vcpkg_root_dir; + if (args.vcpkg_root_dir != nullptr) + { + vcpkg_root_dir = fs::absolute(Strings::utf8_to_utf16(*args.vcpkg_root_dir)); + } + else + { + const optional<std::wstring> vcpkg_root_dir_env = System::get_environmental_variable(L"VCPKG_ROOT"); + if (vcpkg_root_dir_env) + { + vcpkg_root_dir = fs::absolute(*vcpkg_root_dir_env); + } + else + { + vcpkg_root_dir = Files::find_file_recursively_up(fs::absolute(System::get_exe_path_of_current_process()), ".vcpkg-root"); + } + } - const fs::path& status_file = paths.vcpkg_dir_status_file; - const fs::path status_file_old = status_file.parent_path() / "status-old"; - const fs::path status_file_new = status_file.parent_path() / "status-new"; + Checks::check_exit(!vcpkg_root_dir.empty(), "Error: Could not detect vcpkg-root."); - StatusParagraphs current_status_db = load_current_database(status_file, status_file_old); + const expected<vcpkg_paths> expected_paths = vcpkg_paths::create(vcpkg_root_dir); + Checks::check_exit(!expected_paths.error_code(), "Error: Invalid vcpkg root directory %s: %s", vcpkg_root_dir.string(), expected_paths.error_code().message()); + const vcpkg_paths paths = expected_paths.get_or_throw(); + int exit_code = _wchdir(paths.root.c_str()); + Checks::check_exit(exit_code == 0, "Changing the working dir failed"); - auto b = fs::directory_iterator(updates_dir); - auto e = fs::directory_iterator(); - if (b == e) + if (auto command_function = Commands::find(args.command, Commands::get_available_commands_type_b())) { - // updates directory is empty, control file is up-to-date. - return current_status_db; + return command_function(args, paths); } - for (; b != e; ++b) + triplet default_target_triplet; + if (args.target_triplet != nullptr) { - if (!fs::is_regular_file(b->status())) - continue; - if (b->path().filename() == "incomplete") - continue; - - auto text = Files::read_contents(b->path()).get_or_throw(); - auto pghs = Paragraphs::parse_paragraphs(text); - for (auto&& p : pghs) + default_target_triplet = triplet::from_canonical_name(*args.target_triplet); + } + else + { + const optional<std::wstring> vcpkg_default_triplet_env = System::get_environmental_variable(L"VCPKG_DEFAULT_TRIPLET"); + if (vcpkg_default_triplet_env) { - current_status_db.insert(std::make_unique<StatusParagraph>(p)); + default_target_triplet = triplet::from_canonical_name(Strings::utf16_to_utf8(*vcpkg_default_triplet_env)); + } + else + { + default_target_triplet = triplet::X86_WINDOWS; } } - std::fstream(status_file_new, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc) << current_status_db; - - if (fs::exists(status_file_old)) - fs::remove(status_file_old); - if (fs::exists(status_file)) - fs::rename(status_file, status_file_old); - fs::rename(status_file_new, status_file); - fs::remove(status_file_old); + Input::check_triplet(default_target_triplet, paths); - b = fs::directory_iterator(updates_dir); - for (; b != e; ++b) + if (auto command_function = Commands::find(args.command, Commands::get_available_commands_type_a())) { - if (!fs::is_regular_file(b->status())) - continue; - fs::remove(b->path()); + return command_function(args, paths, default_target_triplet); } - return current_status_db; -} - -void vcpkg::write_update(const vcpkg_paths& paths, const StatusParagraph& p) -{ - static int update_id = 0; - auto my_update_id = update_id++; - auto tmp_update_filename = paths.vcpkg_dir_updates / "incomplete"; - auto update_filename = paths.vcpkg_dir_updates / std::to_string(my_update_id); - std::fstream fs(tmp_update_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc); - fs << p; - fs.close(); - fs::rename(tmp_update_filename, update_filename); + return invalid_command(args.command); } -static void upgrade_to_slash_terminated_sorted_format(std::vector<std::string>* lines, const fs::path& listfile_path) +static void loadConfig() { - static bool was_tracked = false; + fs::path localappdata; + { + // Config path in AppDataLocal + wchar_t* localappdatapath = nullptr; + if (S_OK != SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, nullptr, &localappdatapath)) + __fastfail(1); + localappdata = localappdatapath; + CoTaskMemFree(localappdatapath); + } - if (lines->empty()) + try { + std::string config_contents = Files::read_contents(localappdata / "vcpkg" / "config").get_or_throw(); + + std::unordered_map<std::string, std::string> keys; + auto pghs = Paragraphs::parse_paragraphs(config_contents); + if (pghs.size() > 0) + keys = pghs[0]; + + for (size_t x = 1; x < pghs.size(); ++x) + { + for (auto&& p : pghs[x]) + keys.insert(p); + } + + auto user_id = keys["User-Id"]; + auto user_time = keys["User-Since"]; + Checks::check_throw(!user_id.empty() && !user_time.empty(), ""); // Use as goto to the catch statement + + SetUserInformation(user_id, user_time); return; } - - if (lines->at(0).back() == '/') + catch (...) { - return; // File already in the new format } - if (!was_tracked) + // config file not found, could not be read, or invalid + std::string user_id, user_time; + InitUserInformation(user_id, user_time); + SetUserInformation(user_id, user_time); + try { - was_tracked = true; - TrackProperty("listfile", "update to new format"); + std::error_code ec; + fs::create_directory(localappdata / "vcpkg", ec); + std::ofstream(localappdata / "vcpkg" / "config", std::ios_base::out | std::ios_base::trunc) + << "User-Id: " << user_id << "\n" + << "User-Since: " << user_time << "\n"; } - - // The files are sorted such that directories are placed just before the files they contain - // (They are not necessarily sorted alphabetically, e.g. libflac) - // Therefore we can detect the entries that represent directories by comparing every element with the next one - // and checking if the next has a slash immediately after the current one's length - for (size_t i = 0; i < lines->size() - 1; i++) + catch (...) { - std::string& current_string = lines->at(i); - const std::string& next_string = lines->at(i + 1); + } +} - const size_t potential_slash_char_index = current_string.length(); - // Make sure the index exists first - if (next_string.size() > potential_slash_char_index && next_string.at(potential_slash_char_index) == '/') - { - current_string += '/'; // Mark as a directory - } +static std::string trim_path_from_command_line(const std::string& full_command_line) +{ + Checks::check_exit(full_command_line.size() > 0, "Internal failure - cannot have empty command line"); + + if (full_command_line[0] == '"') + { + auto it = std::find(full_command_line.cbegin() + 1, full_command_line.cend(), '"'); + if (it != full_command_line.cend()) // Skip over the quote + ++it; + while (it != full_command_line.cend() && *it == ' ') // Skip over a space + ++it; + return std::string(it, full_command_line.cend()); } - // After suffixing the directories with a slash, we can now sort. - // We cannot sort before adding the suffixes because the following (actual example): - /* - x86-windows/include/FLAC <<<<<< This would be separated from its group due to sorting - x86-windows/include/FLAC/all.h - x86-windows/include/FLAC/assert.h - x86-windows/include/FLAC/callback.h - x86-windows/include/FLAC++ - x86-windows/include/FLAC++/all.h - x86-windows/include/FLAC++/decoder.h - x86-windows/include/FLAC++/encoder.h - * - x86-windows/include/FLAC/ <<<<<< This will now be kept with its group when sorting - x86-windows/include/FLAC/all.h - x86-windows/include/FLAC/assert.h - x86-windows/include/FLAC/callback.h - x86-windows/include/FLAC++/ - x86-windows/include/FLAC++/all.h - x86-windows/include/FLAC++/decoder.h - x86-windows/include/FLAC++/encoder.h - */ - // Note that after sorting, the FLAC++/ group will be placed before the FLAC/ group - // The new format is lexicographically sorted - std::sort(lines->begin(), lines->end()); - -#if 0 - // Replace the listfile on disk - const fs::path updated_listfile_path = listfile_path.generic_string() + "_updated"; - Files::write_all_lines(updated_listfile_path, *lines); - fs::rename(updated_listfile_path, listfile_path); -#endif + auto it = std::find(full_command_line.cbegin(), full_command_line.cend(), ' '); + while (it != full_command_line.cend() && *it == ' ') + ++it; + return std::string(it, full_command_line.cend()); } -std::vector<StatusParagraph_and_associated_files> vcpkg::get_installed_files(const vcpkg_paths& paths, const StatusParagraphs& status_db) +static ElapsedTime g_timer; + +int wmain(const int argc, const wchar_t* const* const argv) { - std::vector<StatusParagraph_and_associated_files> installed_files; + if (argc == 0) + std::abort(); - for (const std::unique_ptr<StatusParagraph>& pgh : status_db) - { - if (pgh->state != install_state_t::installed) + g_timer = ElapsedTime::createStarted(); + atexit([]() { - continue; - } + auto elapsed_us = g_timer.microseconds(); + TrackMetric("elapsed_us", elapsed_us); + Flush(); + }); + + TrackProperty("version", Commands::Version::version()); + + const std::string trimmed_command_line = trim_path_from_command_line(Strings::utf16_to_utf8(GetCommandLineW())); + TrackProperty("cmdline", trimmed_command_line); + loadConfig(); + TrackProperty("sqmuser", GetSQMUser()); - const fs::path listfile_path = paths.listfile_path(pgh->package); - std::vector<std::string> installed_files_of_current_pgh = Files::read_all_lines(listfile_path).get_or_throw(); - Strings::trim_all_and_remove_whitespace_strings(&installed_files_of_current_pgh); - upgrade_to_slash_terminated_sorted_format(&installed_files_of_current_pgh, listfile_path); + const vcpkg_cmd_arguments args = vcpkg_cmd_arguments::create_from_command_line(argc, argv); - // Remove the directories - installed_files_of_current_pgh.erase( - std::remove_if(installed_files_of_current_pgh.begin(), installed_files_of_current_pgh.end(), [](const std::string& file) -> bool - { - return file.back() == '/'; - } - ), installed_files_of_current_pgh.end()); + if (args.printmetrics != opt_bool_t::UNSPECIFIED) + SetPrintMetrics(args.printmetrics == opt_bool_t::ENABLED); + if (args.sendmetrics != opt_bool_t::UNSPECIFIED) + SetSendMetrics(args.sendmetrics == opt_bool_t::ENABLED); - StatusParagraph_and_associated_files pgh_and_files = {*pgh, std::move(installed_files_of_current_pgh)}; - installed_files.push_back(std::move(pgh_and_files)); + if (args.debug != opt_bool_t::UNSPECIFIED) + { + g_debugging = (args.debug == opt_bool_t::ENABLED); } - return installed_files; -} + if (g_debugging) + { + inner(args); + exit(EXIT_FAILURE); + } -expected<SourceParagraph> vcpkg::try_load_port(const fs::path& path) -{ + std::string exc_msg; try { - auto pghs = Paragraphs::get_paragraphs(path / "CONTROL"); - Checks::check_exit(pghs.size() == 1, "Invalid control file at %s\\CONTROL", path.string()); - return SourceParagraph(pghs[0]); + inner(args); + exit(EXIT_FAILURE); } - catch (std::runtime_error const&) + catch (std::exception& e) { + exc_msg = e.what(); } - - return std::errc::no_such_file_or_directory; -} - -expected<BinaryParagraph> vcpkg::try_load_cached_package(const vcpkg_paths& paths, const package_spec& spec) -{ - const fs::path path = paths.package_dir(spec) / "CONTROL"; - - auto control_contents_maybe = Files::read_contents(path); - if (auto control_contents = control_contents_maybe.get()) + catch (...) { - std::vector<std::unordered_map<std::string, std::string>> pghs; - try - { - pghs = Paragraphs::parse_paragraphs(*control_contents); - } - catch (std::runtime_error) - { - } - Checks::check_exit(pghs.size() == 1, "Invalid control file at %s", path.string()); - return BinaryParagraph(pghs[0]); + exc_msg = "unknown error(...)"; } - return control_contents_maybe.error_code(); + TrackProperty("error", exc_msg); + + fflush(stdout); + System::print( + "vcpkg.exe has crashed.\n" + "Please send an email to:\n" + " %s\n" + "containing a brief summary of what you were trying to do and the following data blob:\n" + "\n" + "Version=%s\n" + "EXCEPTION='%s'\n" + "CMD=\n", + Commands::Contact::email(), + Commands::Version::version(), + exc_msg); + fflush(stdout); + for (int x = 0; x < argc; ++x) + System::println("%s|", Strings::utf16_to_utf8(argv[x])); + fflush(stdout); } diff --git a/toolsrc/src/vcpkg_Checks.cpp b/toolsrc/src/vcpkg_Checks.cpp index 817ac9e96..02d3480a2 100644 --- a/toolsrc/src/vcpkg_Checks.cpp +++ b/toolsrc/src/vcpkg_Checks.cpp @@ -1,11 +1,10 @@ +#include "pch.h" #include "vcpkg_Checks.h" - -#include <stdexcept> #include "vcpkg_System.h" -namespace vcpkg {namespace Checks +namespace vcpkg::Checks { - void unreachable() + __declspec(noreturn) void unreachable() { System::println(System::color::error, "Error: Unreachable code was reached"); #ifndef NDEBUG @@ -15,13 +14,13 @@ namespace vcpkg {namespace Checks #endif } - void exit_with_message(const char* errorMessage) + __declspec(noreturn) void exit_with_message(const char* errorMessage) { System::println(System::color::error, errorMessage); exit(EXIT_FAILURE); } - void throw_with_message(const char* errorMessage) + __declspec(noreturn) void throw_with_message(const char* errorMessage) { throw std::runtime_error(errorMessage); } @@ -34,6 +33,14 @@ namespace vcpkg {namespace Checks } } + void check_exit(bool expression) + { + if (!expression) + { + exit(EXIT_FAILURE); + } + } + void check_exit(bool expression, const char* errorMessage) { if (!expression) @@ -41,4 +48,4 @@ namespace vcpkg {namespace Checks exit_with_message(errorMessage); } } -}} +} diff --git a/toolsrc/src/Stopwatch.cpp b/toolsrc/src/vcpkg_Chrono.cpp index 550f1ebd8..e39842df9 100644 --- a/toolsrc/src/Stopwatch.cpp +++ b/toolsrc/src/vcpkg_Chrono.cpp @@ -1,48 +1,10 @@ -#include "Stopwatch.h" +#include "pch.h" +#include "vcpkg_Chrono.h" #include "vcpkg_Checks.h" namespace vcpkg { - Stopwatch Stopwatch::createUnstarted() - { - return Stopwatch(); - } - - Stopwatch Stopwatch::createStarted() - { - return Stopwatch().start(); - } - - bool Stopwatch::isRunning() const - { - return this->m_isRunning; - } - - const Stopwatch& Stopwatch::start() - { - Checks::check_exit(!this->m_isRunning, "This stopwatch is already running."); - this->m_isRunning = true; - this->m_startTick = std::chrono::high_resolution_clock::now(); - return *this; - } - - const Stopwatch& Stopwatch::stop() - { - auto tick = std::chrono::high_resolution_clock::now(); - Checks::check_exit(this->m_isRunning, "This stopwatch is already stopped."); - this->m_isRunning = false; - this->m_elapsedNanos += tick - this->m_startTick; - return *this; - } - - Stopwatch& Stopwatch::reset() - { - this->m_elapsedNanos = std::chrono::nanoseconds(); - this->m_isRunning = false; - return *this; - } - - std::string Stopwatch::toString() const + static std::string format_time_userfriendly(const std::chrono::nanoseconds& nanos) { using std::chrono::hours; using std::chrono::minutes; @@ -52,8 +14,7 @@ namespace vcpkg using std::chrono::nanoseconds; using std::chrono::duration_cast; - auto nanos = elapsedNanos(); - auto nanos_as_double = static_cast<double>(nanos.count()); + const double nanos_as_double = static_cast<double>(nanos.count()); if (duration_cast<hours>(nanos) > hours()) { @@ -88,17 +49,15 @@ namespace vcpkg return Strings::format("%.4g ns", nanos_as_double); } - Stopwatch::Stopwatch() : m_isRunning(false), m_elapsedNanos(), m_startTick() + ElapsedTime ElapsedTime::createStarted() { + ElapsedTime t; + t.m_startTick = std::chrono::high_resolution_clock::now(); + return t; } - std::chrono::nanoseconds Stopwatch::elapsedNanos() const + std::string ElapsedTime::toString() const { - if (this->m_isRunning) - { - return std::chrono::high_resolution_clock::now() - this->m_startTick + this->m_elapsedNanos; - } - - return this->m_elapsedNanos; + return format_time_userfriendly(elapsed<std::chrono::nanoseconds>()); } } diff --git a/toolsrc/src/vcpkg_Dependencies.cpp b/toolsrc/src/vcpkg_Dependencies.cpp index ae7f697fa..e8bf10617 100644 --- a/toolsrc/src/vcpkg_Dependencies.cpp +++ b/toolsrc/src/vcpkg_Dependencies.cpp @@ -1,16 +1,40 @@ +#include "pch.h" #include "vcpkg_Dependencies.h" -#include <vector> #include "vcpkg_Graphs.h" #include "vcpkg_paths.h" #include "package_spec.h" #include "StatusParagraphs.h" -#include <unordered_set> -#include "vcpkg_Maps.h" #include "vcpkg_Files.h" -#include "vcpkg.h" +#include "Paragraphs.h" -namespace vcpkg { namespace Dependencies +namespace vcpkg::Dependencies { + install_plan_action::install_plan_action() : plan_type(install_plan_type::UNKNOWN), binary_pgh(nullptr), source_pgh(nullptr) + { + } + + install_plan_action::install_plan_action(const install_plan_type& plan_type, optional<BinaryParagraph> binary_pgh, optional<SourceParagraph> source_pgh) + : plan_type(std::move(plan_type)), binary_pgh(std::move(binary_pgh)), source_pgh(std::move(source_pgh)) + { + } + + package_spec_with_install_plan::package_spec_with_install_plan(const package_spec& spec, install_plan_action&& plan) : spec(spec), plan(std::move(plan)) + { + } + + remove_plan_action::remove_plan_action() : plan_type(remove_plan_type::UNKNOWN), request_type(request_type::UNKNOWN) + { + } + + remove_plan_action::remove_plan_action(const remove_plan_type& plan_type, const Dependencies::request_type& request_type) : plan_type(plan_type), request_type(request_type) + { + } + + package_spec_with_remove_plan::package_spec_with_remove_plan(const package_spec& spec, remove_plan_action&& plan) + : spec(spec), plan(std::move(plan)) + { + } + std::vector<package_spec_with_install_plan> create_install_plan(const vcpkg_paths& paths, const std::vector<package_spec>& specs, const StatusParagraphs& status_db) { std::unordered_map<package_spec, install_plan_action> was_examined; // Examine = we have checked its immediate (non-recursive) dependencies @@ -48,7 +72,7 @@ namespace vcpkg { namespace Dependencies continue; } - expected<BinaryParagraph> maybe_bpgh = try_load_cached_package(paths, spec); + expected<BinaryParagraph> maybe_bpgh = Paragraphs::try_load_cached_package(paths, spec); if (BinaryParagraph* bpgh = maybe_bpgh.get()) { process_dependencies(bpgh->depends); @@ -56,7 +80,7 @@ namespace vcpkg { namespace Dependencies continue; } - expected<SourceParagraph> maybe_spgh = try_load_port(paths, spec.name()); + expected<SourceParagraph> maybe_spgh = Paragraphs::try_load_port(paths.port_dir(spec)); SourceParagraph* spgh = maybe_spgh.get(); Checks::check_exit(spgh != nullptr, "Cannot find package %s", spec.name()); process_dependencies(filter_dependencies(spgh->depends, spec.target_triplet())); @@ -68,8 +92,65 @@ namespace vcpkg { namespace Dependencies const std::vector<package_spec> pkgs = graph.find_topological_sort(); for (const package_spec& pkg : pkgs) { - ret.push_back({ pkg, std::move(was_examined[pkg]) }); + ret.push_back(package_spec_with_install_plan(pkg, std::move(was_examined[pkg]))); + } + return ret; + } + + std::vector<package_spec_with_remove_plan> create_remove_plan(const std::vector<package_spec>& specs, const StatusParagraphs& status_db) + { + std::unordered_set<package_spec> specs_as_set(specs.cbegin(), specs.cend()); + + std::unordered_map<package_spec, remove_plan_action> was_examined; // Examine = we have checked its immediate (non-recursive) dependencies + Graphs::Graph<package_spec> graph; + graph.add_vertices(specs); + + std::vector<package_spec> examine_stack(specs); + while (!examine_stack.empty()) + { + const package_spec spec = examine_stack.back(); + examine_stack.pop_back(); + + if (was_examined.find(spec) != was_examined.end()) + { + continue; + } + + const StatusParagraphs::const_iterator it = status_db.find(spec); + if (it == status_db.end() || (*it)->state == install_state_t::not_installed) + { + was_examined.emplace(spec, remove_plan_action(remove_plan_type::NOT_INSTALLED, request_type::USER_REQUESTED)); + continue; + } + + for (const std::unique_ptr<StatusParagraph>& an_installed_package : status_db) + { + if (an_installed_package->want != want_t::install) + continue; + if (an_installed_package->package.spec.target_triplet() != spec.target_triplet()) + continue; + + const std::vector<std::string>& deps = an_installed_package->package.depends; + if (std::find(deps.begin(), deps.end(), spec.name()) == deps.end()) + { + continue; + } + + graph.add_edge(spec, an_installed_package.get()->package.spec); + examine_stack.push_back(an_installed_package.get()->package.spec); + } + + const request_type request_type = specs_as_set.find(spec) != specs_as_set.end() ? request_type::USER_REQUESTED : request_type::AUTO_SELECTED; + was_examined.emplace(spec, remove_plan_action(remove_plan_type::REMOVE, request_type)); + } + + std::vector<package_spec_with_remove_plan> ret; + + const std::vector<package_spec> pkgs = graph.find_topological_sort(); + for (const package_spec& pkg : pkgs) + { + ret.push_back(package_spec_with_remove_plan(pkg, std::move(was_examined[pkg]))); } return ret; } -}} +} diff --git a/toolsrc/src/vcpkg_Enums.cpp b/toolsrc/src/vcpkg_Enums.cpp new file mode 100644 index 000000000..5e698659d --- /dev/null +++ b/toolsrc/src/vcpkg_Enums.cpp @@ -0,0 +1,21 @@ +#include "pch.h" +#include "vcpkg_Enums.h" +#include "vcpkg_Checks.h" + +namespace vcpkg::Enums +{ + std::string nullvalue_toString(const std::string& enum_name) + { + return Strings::format("%s_NULLVALUE", enum_name); + } + + void nullvalue_used(const std::string& enum_name) + { + Checks::exit_with_message("NULLVALUE of enum %s was used", enum_name); + } + + void unreachable(const std::string& enum_name) + { + Checks::exit_with_message("Unreachable code for enum, %s", enum_name); + } +} diff --git a/toolsrc/src/vcpkg_Environment.cpp b/toolsrc/src/vcpkg_Environment.cpp index ed70e6881..8aaaba8a8 100644 --- a/toolsrc/src/vcpkg_Environment.cpp +++ b/toolsrc/src/vcpkg_Environment.cpp @@ -1,86 +1,187 @@ -#include <regex> -#include <array> +#include "pch.h" #include "vcpkg_Environment.h" #include "vcpkg_Commands.h" -#include "metrics.h" #include "vcpkg_System.h" +#include "vcpkg_Strings.h" +#include "vcpkg_Files.h" -namespace vcpkg {namespace Environment +namespace vcpkg::Environment { - static const fs::path default_cmake_installation_dir = "C:/Program Files/CMake/bin"; - static const fs::path default_cmake_installation_dir_x86 = "C:/Program Files (x86)/CMake/bin"; - static const fs::path default_git_installation_dir = "C:/Program Files/git/cmd"; - static const fs::path default_git_installation_dir_x86 = "C:/Program Files (x86)/git/cmd"; + static std::vector<std::string> get_VS2017_installation_instances(const vcpkg_paths& paths) + { + const fs::path script = paths.scripts / "findVisualStudioInstallationInstances.ps1"; + const std::wstring cmd = System::create_powershell_script_cmd(script); + System::exit_code_and_output ec_data = System::cmd_execute_and_capture_output(cmd); + Checks::check_exit(ec_data.exit_code == 0, "Could not run script to detect VS 2017 instances"); + return Strings::split(ec_data.output, "\n"); + } + + static optional<fs::path> find_vs2015_installation_instance() + { + const optional<std::wstring> vs2015_cmntools_optional = System::get_environmental_variable(L"VS140COMNTOOLS"); + if (!vs2015_cmntools_optional) + { + return nullptr; + } + + static const fs::path vs2015_cmntools = fs::path(*vs2015_cmntools_optional).parent_path(); // The call to parent_path() is needed because the env variable has a trailing backslash + static const fs::path vs2015_path = vs2015_cmntools.parent_path().parent_path(); + return std::make_unique<fs::path>(vs2015_path); + } + + static const optional<fs::path>& get_VS2015_installation_instance() + { + static const optional<fs::path> vs2015_path = find_vs2015_installation_instance(); + return vs2015_path; + } - static void ensure_on_path(const std::array<int, 3>& version, const wchar_t* version_check_cmd, const wchar_t* install_cmd) + static fs::path find_dumpbin_exe(const vcpkg_paths& paths) { - System::exit_code_and_output ec_data = System::cmd_execute_and_capture_output(version_check_cmd); - if (ec_data.exit_code == 0) + const std::vector<std::string> vs2017_installation_instances = get_VS2017_installation_instances(paths); + std::vector<fs::path> paths_examined; + + // VS2017 + for (const std::string& instance : vs2017_installation_instances) { - // version check - std::regex re(R"###((\d+)\.(\d+)\.(\d+))###"); - std::match_results<std::string::const_iterator> match; - auto found = std::regex_search(ec_data.output, match, re); - if (found) + const fs::path msvc_path = Strings::format(R"(%s\VC\Tools\MSVC)", instance); + std::vector<fs::path> msvc_subdirectories; + Files::non_recursive_find_matching_paths_in_dir(msvc_path, [&](const fs::path& current) + { + return fs::is_directory(current); + }, &msvc_subdirectories); + + // Sort them so that latest comes first + std::sort(msvc_subdirectories.begin(), msvc_subdirectories.end(), [&](const fs::path& left, const fs::path& right) + { + return left.filename() > right.filename(); + }); + + for (const fs::path& subdir : msvc_subdirectories) { - int d1 = atoi(match[1].str().c_str()); - int d2 = atoi(match[2].str().c_str()); - int d3 = atoi(match[3].str().c_str()); - if (d1 > version[0] || (d1 == version[0] && d2 > version[1]) || (d1 == version[0] && d2 == version[1] && d3 >= version[2])) + const fs::path dumpbin_path = subdir / "bin" / "HostX86" / "x86" / "dumpbin.exe"; + paths_examined.push_back(dumpbin_path); + if (fs::exists(dumpbin_path)) { - // satisfactory version found - return; + return dumpbin_path; } } } - auto rc = System::cmd_execute(install_cmd); - if (rc) + // VS2015 + const optional<fs::path>& vs_2015_installation_instance = get_VS2015_installation_instance(); + if (vs_2015_installation_instance) + { + const fs::path vs2015_dumpbin_exe = *vs_2015_installation_instance / "VC" / "bin" / "dumpbin.exe"; + paths_examined.push_back(vs2015_dumpbin_exe); + if (fs::exists(vs2015_dumpbin_exe)) + { + return vs2015_dumpbin_exe; + } + } + + System::println(System::color::error, "Could not detect dumpbin.exe."); + System::println("The following paths were examined:"); + for (const fs::path& path : paths_examined) + { + System::println(" %s", path.generic_string()); + } + exit(EXIT_FAILURE); + } + + const fs::path& get_dumpbin_exe(const vcpkg_paths& paths) + { + static const fs::path dumpbin_exe = find_dumpbin_exe(paths); + return dumpbin_exe; + } + + static vcvarsall_and_platform_toolset find_vcvarsall_bat(const vcpkg_paths& paths) + { + const std::vector<std::string> vs2017_installation_instances = get_VS2017_installation_instances(paths); + std::vector<fs::path> paths_examined; + + // VS2017 + for (const fs::path& instance : vs2017_installation_instances) + { + const fs::path vcvarsall_bat = instance / "VC" / "Auxiliary" / "Build" / "vcvarsall.bat"; + paths_examined.push_back(vcvarsall_bat); + if (fs::exists(vcvarsall_bat)) + { + return { vcvarsall_bat , L"v141" }; + } + } + + // VS2015 + const optional<fs::path>& vs_2015_installation_instance = get_VS2015_installation_instance(); + if (vs_2015_installation_instance) + { + const fs::path vs2015_vcvarsall_bat = *vs_2015_installation_instance / "VC" / "vcvarsall.bat"; + + paths_examined.push_back(vs2015_vcvarsall_bat); + if (fs::exists(vs2015_vcvarsall_bat)) + { + return { vs2015_vcvarsall_bat, L"v140" }; + } + } + + System::println(System::color::error, "Could not detect vcvarsall.bat."); + System::println("The following paths were examined:"); + for (const fs::path& path : paths_examined) { - System::println(System::color::error, "Launching powershell failed or was denied"); - TrackProperty("error", "powershell install failed"); - TrackProperty("installcmd", install_cmd); - exit(rc); + System::println(" %s", path.generic_string()); } + exit(EXIT_FAILURE); + } + + const vcvarsall_and_platform_toolset& get_vcvarsall_bat(const vcpkg_paths& paths) + { + static const vcvarsall_and_platform_toolset vcvarsall_bat = find_vcvarsall_bat(paths); + return vcvarsall_bat; } - void ensure_git_on_path(const vcpkg_paths& paths) + static fs::path find_ProgramFiles() { - const fs::path downloaded_git = paths.downloads / "PortableGit" / "cmd"; - const std::wstring path_buf = Strings::wformat(L"%s;%s;%s;%s", - downloaded_git.native(), - System::wdupenv_str(L"PATH"), - default_git_installation_dir.native(), - default_git_installation_dir_x86.native()); - _wputenv_s(L"PATH", path_buf.c_str()); - - static constexpr std::array<int, 3> git_version = {2,0,0}; - // TODO: switch out ExecutionPolicy Bypass with "Remove Mark Of The Web" code and restore RemoteSigned - ensure_on_path(git_version, L"git --version 2>&1", L"powershell -ExecutionPolicy Bypass scripts\\fetchDependency.ps1 -Dependency git"); + const optional<std::wstring> program_files = System::get_environmental_variable(L"PROGRAMFILES"); + Checks::check_exit(program_files.get() != nullptr, "Could not detect the PROGRAMFILES environmental variable"); + return *program_files; } - void ensure_cmake_on_path(const vcpkg_paths& paths) + static const fs::path& get_ProgramFiles() { - const fs::path downloaded_cmake = paths.downloads / "cmake-3.5.2-win32-x86" / "bin"; - const std::wstring path_buf = Strings::wformat(L"%s;%s;%s;%s", - downloaded_cmake.native(), - System::wdupenv_str(L"PATH"), - default_cmake_installation_dir.native(), - default_cmake_installation_dir_x86.native()); - _wputenv_s(L"PATH", path_buf.c_str()); - - static constexpr std::array<int, 3> cmake_version = {3,5,0}; - // TODO: switch out ExecutionPolicy Bypass with "Remove Mark Of The Web" code and restore RemoteSigned - ensure_on_path(cmake_version, L"cmake --version 2>&1", L"powershell -ExecutionPolicy Bypass scripts\\fetchDependency.ps1 -Dependency cmake"); + static const fs::path p = find_ProgramFiles(); + return p; } - void ensure_nuget_on_path(const vcpkg_paths& paths) + static fs::path find_ProgramFiles_32_bit() { - const std::wstring path_buf = Strings::wformat(L"%s;%s", paths.downloads.native(), System::wdupenv_str(L"PATH")); - _wputenv_s(L"PATH", path_buf.c_str()); + const optional<std::wstring> program_files_X86 = System::get_environmental_variable(L"ProgramFiles(x86)"); + if (program_files_X86) + { + return *program_files_X86; + } + + return get_ProgramFiles(); + } + + const fs::path& get_ProgramFiles_32_bit() + { + static const fs::path p = find_ProgramFiles_32_bit(); + return p; + } - static constexpr std::array<int, 3> nuget_version = {1,0,0}; - // TODO: switch out ExecutionPolicy Bypass with "Remove Mark Of The Web" code and restore RemoteSigned - ensure_on_path(nuget_version, L"nuget 2>&1", L"powershell -ExecutionPolicy Bypass scripts\\fetchDependency.ps1 -Dependency nuget"); + static fs::path find_ProgramFiles_platform_bitness() + { + const optional<std::wstring> program_files_W6432 = System::get_environmental_variable(L"ProgramW6432"); + if (program_files_W6432) + { + return *program_files_W6432; + } + + return get_ProgramFiles(); + } + + const fs::path& get_ProgramFiles_platform_bitness() + { + static const fs::path p = find_ProgramFiles_platform_bitness(); + return p; } -}} +} diff --git a/toolsrc/src/vcpkg_Files.cpp b/toolsrc/src/vcpkg_Files.cpp index 48283e43f..57d4c665c 100644 --- a/toolsrc/src/vcpkg_Files.cpp +++ b/toolsrc/src/vcpkg_Files.cpp @@ -1,9 +1,8 @@ +#include "pch.h" #include "vcpkg_Files.h" -#include <fstream> -#include <regex> #include "vcpkg_System.h" -namespace vcpkg {namespace Files +namespace vcpkg::Files { static const std::regex FILESYSTEM_INVALID_CHARACTERS_REGEX = std::regex(R"([\/:*?"<>|])"); @@ -103,7 +102,7 @@ namespace vcpkg {namespace Files void recursive_find_all_files_in_dir(const fs::path& dir, std::vector<fs::path>* output) { - recursive_find_matching_paths_in_dir(dir, [&](const fs::path& current) + recursive_find_matching_paths_in_dir(dir, [](const fs::path& current) { return !fs::is_directory(current); }, output); @@ -118,7 +117,7 @@ namespace vcpkg {namespace Files void non_recursive_find_all_files_in_dir(const fs::path& dir, std::vector<fs::path>* output) { - non_recursive_find_matching_paths_in_dir(dir, [&](const fs::path& current) + non_recursive_find_matching_paths_in_dir(dir, [](const fs::path& current) { return !fs::is_directory(current); }, output); @@ -140,4 +139,4 @@ namespace vcpkg {namespace Files } System::println(""); } -}} +} diff --git a/toolsrc/src/vcpkg_Input.cpp b/toolsrc/src/vcpkg_Input.cpp index 29d487fdb..5720cadc0 100644 --- a/toolsrc/src/vcpkg_Input.cpp +++ b/toolsrc/src/vcpkg_Input.cpp @@ -1,9 +1,10 @@ +#include "pch.h" #include "vcpkg_Input.h" #include "vcpkg_System.h" #include "metrics.h" #include "vcpkg_Commands.h" -namespace vcpkg {namespace Input +namespace vcpkg::Input { package_spec check_and_get_package_spec(const std::string& package_spec_as_string, const triplet& default_target_triplet, const std::string& example_text) { @@ -37,16 +38,16 @@ namespace vcpkg {namespace Input { System::println(System::color::error, "Error: invalid triplet: %s", t.canonical_name()); TrackProperty("error", "invalid triplet: " + t.canonical_name()); - help_topic_valid_triplet(paths); + Commands::Help::help_topic_valid_triplet(paths); exit(EXIT_FAILURE); } } - void check_triplets(std::vector<package_spec> triplets, const vcpkg_paths& paths) + void check_triplets(const std::vector<package_spec>& triplets, const vcpkg_paths& paths) { for (const package_spec& spec : triplets) { check_triplet(spec.target_triplet(), paths); } } -}} +} diff --git a/toolsrc/src/vcpkg_Strings.cpp b/toolsrc/src/vcpkg_Strings.cpp index 46a4b1855..044fd3c05 100644 --- a/toolsrc/src/vcpkg_Strings.cpp +++ b/toolsrc/src/vcpkg_Strings.cpp @@ -1,13 +1,7 @@ +#include "pch.h" #include "vcpkg_Strings.h" -#include <cstdarg> -#include <algorithm> -#include <codecvt> -#include <iterator> -#include <functional> -#include <cctype> - -namespace vcpkg {namespace Strings {namespace details +namespace vcpkg::Strings::details { // To disambiguate between two overloads static const auto isspace = [](const char c) @@ -15,14 +9,26 @@ namespace vcpkg {namespace Strings {namespace details return std::isspace(c); }; + // Avoids C4244 warnings because of char<->int conversion that occur when using std::tolower() + static char tolower_char(const char c) + { + return static_cast<char>(std::tolower(c)); + } + + static _locale_t& c_locale() + { + static _locale_t c_locale_impl = _create_locale(LC_ALL, "C"); + return c_locale_impl; + } + std::string format_internal(const char* fmtstr, ...) { va_list lst; va_start(lst, fmtstr); - auto sz = _vscprintf(fmtstr, lst); + const int sz = _vscprintf_l(fmtstr, c_locale(), lst); std::string output(sz, '\0'); - _vsnprintf_s(&output[0], output.size() + 1, output.size() + 1, fmtstr, lst); + _vsnprintf_s_l(&output[0], output.size() + 1, output.size() + 1, fmtstr, c_locale(), lst); va_end(lst); return output; @@ -33,16 +39,16 @@ namespace vcpkg {namespace Strings {namespace details va_list lst; va_start(lst, fmtstr); - auto sz = _vscwprintf(fmtstr, lst); + const int sz = _vscwprintf_l(fmtstr, c_locale(), lst); std::wstring output(sz, '\0'); - _vsnwprintf_s(&output[0], output.size() + 1, output.size() + 1, fmtstr, lst); + _vsnwprintf_s_l(&output[0], output.size() + 1, output.size() + 1, fmtstr, c_locale(), lst); va_end(lst); return output; } -}}} +} -namespace vcpkg {namespace Strings +namespace vcpkg::Strings { std::wstring utf8_to_utf16(const std::string& s) { @@ -58,40 +64,28 @@ namespace vcpkg {namespace Strings std::string::const_iterator case_insensitive_ascii_find(const std::string& s, const std::string& pattern) { - std::string pattern_as_lower_case; - std::transform(pattern.begin(), pattern.end(), back_inserter(pattern_as_lower_case), tolower); + const std::string pattern_as_lower_case(ascii_to_lowercase(pattern)); return search(s.begin(), s.end(), pattern_as_lower_case.begin(), pattern_as_lower_case.end(), [](const char a, const char b) { - return tolower(a) == b; + return details::tolower_char(a) == b; }); } std::string ascii_to_lowercase(const std::string& input) { - std::string output = input; - std::transform(output.begin(), output.end(), output.begin(), ::tolower); + std::string output(input); + std::transform(output.begin(), output.end(), output.begin(), &details::tolower_char); return output; } - std::string join(const std::vector<std::string>& v, const std::string& delimiter) + std::string join(const std::string& delimiter, const std::vector<std::string>& v) { - if (v.empty()) - { - return std::string(); - } - - std::string output; - size_t size = v.size(); - - output.append(v.at(0)); - - for (size_t i = 1; i < size; ++i) - { - output.append(delimiter); - output.append(v.at(i)); - } + return join(delimiter, v, [](const std::string& p) -> const std::string& { return p; }); + } - return output; + std::wstring wjoin(const std::wstring& delimiter, const std::vector<std::wstring>& v) + { + return wjoin(delimiter, v, [](const std::wstring& p) -> const std::wstring&{ return p; }); } void trim(std::string* s) @@ -119,4 +113,24 @@ namespace vcpkg {namespace Strings return s == ""; }), strings->end()); } -}} + + std::vector<std::string> split(const std::string& s, const std::string& delimiter) + { + std::vector<std::string> output; + + size_t i = 0; + for (size_t pos = s.find(delimiter); pos != std::string::npos; pos = s.find(delimiter, pos)) + { + output.push_back(s.substr(i, pos - i)); + i = ++pos; + } + + // Add the rest of the string after the last delimiter, unless there is nothing after it + if (i != s.length()) + { + output.push_back(s.substr(i, s.length())); + } + + return output; + } +} diff --git a/toolsrc/src/vcpkg_System.cpp b/toolsrc/src/vcpkg_System.cpp index 43eae3412..472f8450f 100644 --- a/toolsrc/src/vcpkg_System.cpp +++ b/toolsrc/src/vcpkg_System.cpp @@ -1,21 +1,97 @@ +#include "pch.h" #include "vcpkg_System.h" -#include <iostream> -#include <Windows.h> -#include <regex> +#include "vcpkg_Checks.h" -namespace vcpkg {namespace System +namespace vcpkg::System { fs::path get_exe_path_of_current_process() { - wchar_t buf[_MAX_PATH ]; + wchar_t buf[_MAX_PATH]; int bytes = GetModuleFileNameW(nullptr, buf, _MAX_PATH); if (bytes == 0) std::abort(); return fs::path(buf, buf + bytes); } + int cmd_execute_clean(const wchar_t* cmd_line) + { + static const std::wstring system_root = *get_environmental_variable(L"SystemRoot"); + static const std::wstring system_32 = system_root + LR"(\system32)"; + static const std::wstring new_PATH = Strings::wformat(LR"(Path=%s;%s;%s\WindowsPowerShell\v1.0\)", system_32, system_root, system_32); + + std::vector<std::wstring> env_wstrings = + { + L"ALLUSERSPROFILE", + L"APPDATA", + L"CommonProgramFiles", + L"CommonProgramFiles(x86)", + L"CommonProgramW6432", + L"COMPUTERNAME", + L"ComSpec", + L"HOMEDRIVE", + L"HOMEPATH", + L"LOCALAPPDATA", + L"LOGONSERVER", + L"NUMBER_OF_PROCESSORS", + L"OS", + L"PATHEXT", + L"PROCESSOR_ARCHITECTURE", + L"PROCESSOR_IDENTIFIER", + L"PROCESSOR_LEVEL", + L"PROCESSOR_REVISION", + L"ProgramData", + L"ProgramFiles", + L"ProgramFiles(x86)", + L"ProgramW6432", + L"PROMPT", + L"PSModulePath", + L"PUBLIC", + L"SystemDrive", + L"SystemRoot", + L"TEMP", + L"TMP", + L"USERDNSDOMAIN", + L"USERDOMAIN", + L"USERDOMAIN_ROAMINGPROFILE", + L"USERNAME", + L"USERPROFILE", + L"windir", + // Enables proxy information to be passed to Curl, the underlying download library in cmake.exe + L"HTTP_PROXY", + L"HTTPS_PROXY", + }; + + // Flush stdout before launching external process + _flushall(); + + std::vector<const wchar_t*> env_cstr; + env_cstr.reserve(env_wstrings.size() + 2); + + for (auto&& env_wstring : env_wstrings) + { + auto v = System::get_environmental_variable(env_wstring.c_str()); + if (v == nullptr || v->empty()) + continue; + + env_wstring.push_back(L'='); + env_wstring.append(*v); + env_cstr.push_back(env_wstring.c_str()); + } + + env_cstr.push_back(new_PATH.c_str()); + env_cstr.push_back(nullptr); + + // Basically we are wrapping it in quotes + const std::wstring& actual_cmd_line = Strings::wformat(LR"###("%s")###", cmd_line); + auto exit_code = _wspawnlpe(_P_WAIT, L"cmd.exe", L"cmd.exe", L"/c", actual_cmd_line.c_str(), nullptr, env_cstr.data()); + return static_cast<int>(exit_code); + } + int cmd_execute(const wchar_t* cmd_line) { + // Flush stdout before launching external process + _flushall(); + // Basically we are wrapping it in quotes const std::wstring& actual_cmd_line = Strings::wformat(LR"###("%s")###", cmd_line); int exit_code = _wsystem(actual_cmd_line.c_str()); @@ -24,6 +100,9 @@ namespace vcpkg {namespace System exit_code_and_output cmd_execute_and_capture_output(const wchar_t* cmd_line) { + // Flush stdout before launching external process + fflush(stdout); + const std::wstring& actual_cmd_line = Strings::wformat(LR"###("%s")###", cmd_line); std::string output; @@ -31,7 +110,7 @@ namespace vcpkg {namespace System auto pipe = _wpopen(actual_cmd_line.c_str(), L"r"); if (pipe == nullptr) { - return {1, output}; + return { 1, output }; } while (fgets(buf, 1024, pipe)) { @@ -39,21 +118,32 @@ namespace vcpkg {namespace System } if (!feof(pipe)) { - return {1, output}; + return { 1, output }; } auto ec = _pclose(pipe); - return {ec, output}; + return { ec, output }; + } + + std::wstring create_powershell_script_cmd(const fs::path& script_path) + { + return create_powershell_script_cmd(script_path, L""); + } + + std::wstring create_powershell_script_cmd(const fs::path& script_path, const std::wstring& args) + { + // TODO: switch out ExecutionPolicy Bypass with "Remove Mark Of The Web" code and restore RemoteSigned + return Strings::wformat(LR"(powershell -ExecutionPolicy Bypass -Command "& {& '%s' %s}")", script_path.native(), args); } void print(const char* message) { - std::cout << message; + fputs(message, stdout); } void println(const char* message) { print(message); - std::cout << "\n"; + putchar('\n'); } void print(const color c, const char* message) @@ -64,46 +154,61 @@ namespace vcpkg {namespace System GetConsoleScreenBufferInfo(hConsole, &consoleScreenBufferInfo); auto original_color = consoleScreenBufferInfo.wAttributes; - SetConsoleTextAttribute(hConsole, static_cast<int>(c) | (original_color & 0xF0)); - std::cout << message; + SetConsoleTextAttribute(hConsole, static_cast<WORD>(c) | (original_color & 0xF0)); + print(message); SetConsoleTextAttribute(hConsole, original_color); } void println(const color c, const char* message) { print(c, message); - std::cout << "\n"; + putchar('\n'); } - std::wstring wdupenv_str(const wchar_t* varname) noexcept + optional<std::wstring> get_environmental_variable(const wchar_t* varname) noexcept { - std::wstring ret; - wchar_t* buffer; - _wdupenv_s(&buffer, nullptr, varname); - if (buffer != nullptr) - { - ret = buffer; - free(buffer); - } + auto sz = GetEnvironmentVariableW(varname, nullptr, 0); + if (sz == 0) + return nullptr; + + auto ret = std::make_unique<std::wstring>(sz, L'\0'); + Checks::check_exit(MAXDWORD >= ret->size()); + auto sz2 = GetEnvironmentVariableW(varname, ret->data(), static_cast<DWORD>(ret->size())); + Checks::check_exit(sz2 + 1 == sz); + ret->pop_back(); return ret; } - void Stopwatch2::start() + void set_environmental_variable(const wchar_t* varname, const wchar_t* varvalue) noexcept { - static_assert(sizeof(start_time) == sizeof(LARGE_INTEGER), ""); - - QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER*>(&start_time)); + _wputenv_s(varname, varvalue); } - void Stopwatch2::stop() + static bool is_string_keytype(DWORD hkey_type) { - QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER*>(&end_time)); - QueryPerformanceFrequency(reinterpret_cast<LARGE_INTEGER*>(&freq)); + return hkey_type == REG_SZ || hkey_type == REG_MULTI_SZ || hkey_type == REG_EXPAND_SZ; } - double Stopwatch2::microseconds() const + optional<std::wstring> get_registry_string(HKEY base, const wchar_t* subKey, const wchar_t* valuename) { - return (reinterpret_cast<const LARGE_INTEGER*>(&end_time)->QuadPart - - reinterpret_cast<const LARGE_INTEGER*>(&start_time)->QuadPart) * 1000000.0 / reinterpret_cast<const LARGE_INTEGER*>(&freq)->QuadPart; + HKEY k = nullptr; + LSTATUS ec = RegOpenKeyExW(base, subKey, NULL, KEY_READ, &k); + if (ec != ERROR_SUCCESS) + return nullptr; + + DWORD dwBufferSize = 0; + DWORD dwType = 0; + auto rc = RegQueryValueExW(k, valuename, nullptr, &dwType, nullptr, &dwBufferSize); + if (rc != ERROR_SUCCESS || !is_string_keytype(dwType) || dwBufferSize == 0 || dwBufferSize % sizeof(wchar_t) != 0) + return nullptr; + std::wstring ret; + ret.resize(dwBufferSize / sizeof(wchar_t)); + + rc = RegQueryValueExW(k, valuename, nullptr, &dwType, reinterpret_cast<LPBYTE>(ret.data()), &dwBufferSize); + if (rc != ERROR_SUCCESS || !is_string_keytype(dwType) || dwBufferSize != sizeof(wchar_t) * ret.size()) + return nullptr; + + ret.pop_back(); // remove extra trailing null byte + return std::make_unique<std::wstring>(std::move(ret)); } -}} +} diff --git a/toolsrc/src/vcpkg_cmd_arguments.cpp b/toolsrc/src/vcpkg_cmd_arguments.cpp index a3648668f..fdeb6e877 100644 --- a/toolsrc/src/vcpkg_cmd_arguments.cpp +++ b/toolsrc/src/vcpkg_cmd_arguments.cpp @@ -1,8 +1,6 @@ -#define WIN32_LEAN_AND_MEAN -#include <Windows.h> +#include "pch.h" #include "vcpkg_cmd_arguments.h" #include "vcpkg_Commands.h" -#include <unordered_set> #include "metrics.h" #include "vcpkg_System.h" @@ -18,7 +16,7 @@ namespace vcpkg { System::println(System::color::error, "Error: expected value after %s", option_name); TrackProperty("error", "error option name"); - print_usage(); + Commands::Help::print_usage(); exit(EXIT_FAILURE); } @@ -26,7 +24,7 @@ namespace vcpkg { System::println(System::color::error, "Error: %s specified multiple times", option_name); TrackProperty("error", "error option specified multiple times"); - print_usage(); + Commands::Help::print_usage(); exit(EXIT_FAILURE); } @@ -34,15 +32,15 @@ namespace vcpkg } static void parse_switch( - opt_bool new_setting, + opt_bool_t new_setting, const std::string& option_name, - opt_bool& option_field) + opt_bool_t& option_field) { - if (option_field != opt_bool::unspecified && option_field != new_setting) + if (option_field != opt_bool_t::UNSPECIFIED && option_field != new_setting) { System::println(System::color::error, "Error: conflicting values specified for --%s", option_name); TrackProperty("error", "error conflicting switches"); - print_usage(); + Commands::Help::print_usage(); exit(EXIT_FAILURE); } option_field = new_setting; @@ -96,27 +94,27 @@ namespace vcpkg } if (arg == "--debug") { - parse_switch(opt_bool::enabled, "debug", args.debug); + parse_switch(opt_bool_t::ENABLED, "debug", args.debug); continue; } if (arg == "--sendmetrics") { - parse_switch(opt_bool::enabled, "sendmetrics", args.sendmetrics); + parse_switch(opt_bool_t::ENABLED, "sendmetrics", args.sendmetrics); continue; } if (arg == "--printmetrics") { - parse_switch(opt_bool::enabled, "printmetrics", args.printmetrics); + parse_switch(opt_bool_t::ENABLED, "printmetrics", args.printmetrics); continue; } if (arg == "--no-sendmetrics") { - parse_switch(opt_bool::disabled, "sendmetrics", args.sendmetrics); + parse_switch(opt_bool_t::DISABLED, "sendmetrics", args.sendmetrics); continue; } if (arg == "--no-printmetrics") { - parse_switch(opt_bool::disabled, "printmetrics", args.printmetrics); + parse_switch(opt_bool_t::DISABLED, "printmetrics", args.printmetrics); continue; } diff --git a/toolsrc/src/vcpkg_info.cpp b/toolsrc/src/vcpkg_info.cpp deleted file mode 100644 index 25c09d6da..000000000 --- a/toolsrc/src/vcpkg_info.cpp +++ /dev/null @@ -1,34 +0,0 @@ -#include "vcpkg_info.h" -#include "metrics.h" - -#define STRINGIFY(X) #X -#define MACRO_TO_STRING(X) STRINGIFY(X) - -#define VCPKG_VERSION_AS_STRING MACRO_TO_STRING(VCPKG_VERSION)"" // Double quotes needed at the end to prevent blank token - -namespace vcpkg { namespace Info -{ - const std::string& version() - { - static const std::string s_version = -#include "../VERSION.txt" - - -#pragma warning( push ) -#pragma warning( disable : 4003) - // VCPKG_VERSION can be defined but have no value, which yields C4003. - + std::string(VCPKG_VERSION_AS_STRING) -#pragma warning( pop ) -#ifndef NDEBUG - + std::string("-debug") -#endif - + std::string(GetCompiledMetricsEnabled() ? "" : "-external"); - return s_version; - } - - const std::string& email() - { - static const std::string s_email = R"(vcpkg@microsoft.com)"; - return s_email; - } -}} diff --git a/toolsrc/src/vcpkg_metrics_uploader.cpp b/toolsrc/src/vcpkg_metrics_uploader.cpp index 14fc9ae48..82dcd4b03 100644 --- a/toolsrc/src/vcpkg_metrics_uploader.cpp +++ b/toolsrc/src/vcpkg_metrics_uploader.cpp @@ -5,13 +5,7 @@ using namespace vcpkg; -int WINAPI -WinMain( - _In_ HINSTANCE hInstance, - _In_opt_ HINSTANCE hPrevInstance, - _In_ LPSTR lpCmdLine, - _In_ int nShowCmd -) +int WINAPI WinMain(_In_ HINSTANCE, _In_opt_ HINSTANCE, _In_ LPSTR, _In_ int) { LPWSTR* szArgList; int argCount; diff --git a/toolsrc/src/vcpkg_paths.cpp b/toolsrc/src/vcpkg_paths.cpp index b7e716307..fa6fca370 100644 --- a/toolsrc/src/vcpkg_paths.cpp +++ b/toolsrc/src/vcpkg_paths.cpp @@ -1,11 +1,156 @@ +#include "pch.h" #include "expected.h" #include "vcpkg_paths.h" #include "metrics.h" #include "vcpkg_System.h" #include "package_spec.h" +#include "vcpkg_Environment.h" namespace vcpkg { + static bool exists_and_has_equal_or_greater_version(const std::wstring& version_cmd, const std::array<int, 3>& expected_version) + { + static const std::regex re(R"###((\d+)\.(\d+)\.(\d+))###"); + + auto rc = System::cmd_execute_and_capture_output(Strings::wformat(LR"(%s 2>&1)", version_cmd)); + if (rc.exit_code != 0) + { + return false; + } + + std::match_results<std::string::const_iterator> match; + auto found = std::regex_search(rc.output, match, re); + if (!found) + { + return false; + } + + int d1 = atoi(match[1].str().c_str()); + int d2 = atoi(match[2].str().c_str()); + int d3 = atoi(match[3].str().c_str()); + if (d1 > expected_version[0] || (d1 == expected_version[0] && d2 > expected_version[1]) || (d1 == expected_version[0] && d2 == expected_version[1] && d3 >= expected_version[2])) + { + // satisfactory version found + return true; + } + + return false; + } + + static optional<fs::path> find_if_has_equal_or_greater_version(const std::vector<fs::path>& candidate_paths, const std::wstring& version_check_arguments, const std::array<int, 3>& expected_version) + { + auto it = std::find_if(candidate_paths.cbegin(), candidate_paths.cend(), [&](const fs::path& p) + { + const std::wstring cmd = Strings::wformat(LR"("%s" %s)", p.native(), version_check_arguments); + return exists_and_has_equal_or_greater_version(cmd, expected_version); + }); + + if (it != candidate_paths.cend()) + { + return std::make_unique<fs::path>(std::move(*it)); + } + + return nullptr; + } + + static std::vector<fs::path> find_from_PATH(const std::wstring& name) + { + const std::wstring cmd = Strings::wformat(L"where.exe %s", name); + auto out = System::cmd_execute_and_capture_output(cmd); + if (out.exit_code != 0) + { + return {}; + } + + const std::vector<std::string> paths_to_add = Strings::split(out.output, "\n"); + std::vector<fs::path> v; + v.insert(v.end(), paths_to_add.cbegin(), paths_to_add.cend()); + return v; + } + + static fs::path fetch_dependency(const fs::path scripts_folder, const std::wstring& tool_name, const fs::path& expected_downloaded_path) + { + const fs::path script = scripts_folder / "fetchDependency.ps1"; + auto install_cmd = System::create_powershell_script_cmd(script, Strings::wformat(L"-Dependency %s", tool_name)); + System::exit_code_and_output rc = System::cmd_execute_and_capture_output(install_cmd); + if (rc.exit_code) + { + System::println(System::color::error, "Launching powershell failed or was denied"); + TrackProperty("error", "powershell install failed"); + TrackProperty("installcmd", install_cmd); + exit(rc.exit_code); + } + + const fs::path actual_downloaded_path = rc.output; + Checks::check_exit(expected_downloaded_path == actual_downloaded_path, "Expected dependency downloaded path to be %s, but was %s", + expected_downloaded_path.generic_string(), actual_downloaded_path.generic_string()); + return actual_downloaded_path; + } + + static fs::path get_cmake_path(const fs::path& downloads_folder, const fs::path scripts_folder) + { + static constexpr std::array<int, 3> expected_version = { 3,8,0 }; + static const std::wstring version_check_arguments = L"--version"; + + const fs::path downloaded_copy = downloads_folder / "cmake-3.8.0-rc1-win32-x86" / "bin" / "cmake.exe"; + const std::vector<fs::path> from_path = find_from_PATH(L"cmake"); + + std::vector<fs::path> candidate_paths; + candidate_paths.push_back(downloaded_copy); + candidate_paths.insert(candidate_paths.end(), from_path.cbegin(), from_path.cend()); + candidate_paths.push_back(Environment::get_ProgramFiles_platform_bitness() / "CMake" / "bin" / "cmake.exe"); + candidate_paths.push_back(Environment::get_ProgramFiles_32_bit() / "CMake" / "bin"); + + if (auto ret = find_if_has_equal_or_greater_version(candidate_paths, version_check_arguments, expected_version)) + { + return *ret; + } + + return fetch_dependency(scripts_folder, L"cmake", downloaded_copy); + } + + fs::path get_nuget_path(const fs::path& downloads_folder, const fs::path scripts_folder) + { + static constexpr std::array<int, 3> expected_version = { 3,3,0 }; + static const std::wstring version_check_arguments = L""; + + const fs::path downloaded_copy = downloads_folder / "nuget-3.5.0" / "nuget.exe"; + const std::vector<fs::path> from_path = find_from_PATH(L"nuget"); + + std::vector<fs::path> candidate_paths; + candidate_paths.push_back(downloaded_copy); + candidate_paths.insert(candidate_paths.end(), from_path.cbegin(), from_path.cend()); + + if (auto ret = find_if_has_equal_or_greater_version(candidate_paths, version_check_arguments, expected_version)) + { + return *ret; + } + + return fetch_dependency(scripts_folder, L"nuget", downloaded_copy); + } + + fs::path get_git_path(const fs::path& downloads_folder, const fs::path scripts_folder) + { + static constexpr std::array<int, 3> expected_version = { 2,0,0 }; + static const std::wstring version_check_arguments = L"--version"; + + const fs::path downloaded_copy = downloads_folder / "MinGit-2.11.1-32-bit" / "cmd" / "git.exe"; + const std::vector<fs::path> from_path = find_from_PATH(L"git"); + + std::vector<fs::path> candidate_paths; + candidate_paths.push_back(downloaded_copy); + candidate_paths.insert(candidate_paths.end(), from_path.cbegin(), from_path.cend()); + candidate_paths.push_back(Environment::get_ProgramFiles_platform_bitness() / "git" / "cmd" / "git.exe"); + candidate_paths.push_back(Environment::get_ProgramFiles_32_bit() / "git" / "cmd" / "git.exe"); + + if (auto ret = find_if_has_equal_or_greater_version(candidate_paths, version_check_arguments, expected_version)) + { + return *ret; + } + + return fetch_dependency(scripts_folder, L"git", downloaded_copy); + } + expected<vcpkg_paths> vcpkg_paths::create(const fs::path& vcpkg_root_dir) { std::error_code ec; @@ -31,8 +176,9 @@ namespace vcpkg paths.ports = paths.root / "ports"; paths.installed = paths.root / "installed"; paths.triplets = paths.root / "triplets"; + paths.scripts = paths.root / "scripts"; - paths.buildsystems = paths.root / "scripts" / "buildsystems"; + paths.buildsystems = paths.scripts / "buildsystems"; paths.buildsystems_msbuild_targets = paths.buildsystems / "msbuild" / "vcpkg.targets"; paths.vcpkg_dir = paths.installed / "vcpkg"; @@ -40,7 +186,8 @@ namespace vcpkg paths.vcpkg_dir_info = paths.vcpkg_dir / "info"; paths.vcpkg_dir_updates = paths.vcpkg_dir / "updates"; - paths.ports_cmake = paths.root / "scripts" / "ports.cmake"; + paths.ports_cmake = paths.scripts / "ports.cmake"; + return paths; } @@ -78,4 +225,19 @@ namespace vcpkg } return false; } + + const fs::path& vcpkg_paths::get_cmake_exe() const + { + return this->cmake_exe.get_lazy([this]() { return get_cmake_path(this->downloads, this->scripts); }); + } + + const fs::path& vcpkg_paths::get_git_exe() const + { + return this->git_exe.get_lazy([this]() { return get_git_path(this->downloads, this->scripts); }); + } + + const fs::path& vcpkg_paths::get_nuget_exe() const + { + return this->nuget_exe.get_lazy([this]() { return get_nuget_path(this->downloads, this->scripts); }); + } } diff --git a/toolsrc/src/vcpkglib.cpp b/toolsrc/src/vcpkglib.cpp new file mode 100644 index 000000000..7ea33da0a --- /dev/null +++ b/toolsrc/src/vcpkglib.cpp @@ -0,0 +1,215 @@ +#include "pch.h" +#include "vcpkglib.h" +#include "vcpkg_Files.h" +#include "Paragraphs.h" +#include "metrics.h" +#include "vcpkg_Strings.h" + +namespace vcpkg +{ + static StatusParagraphs load_current_database(const fs::path& vcpkg_dir_status_file, const fs::path& vcpkg_dir_status_file_old) + { + if (!fs::exists(vcpkg_dir_status_file)) + { + if (!fs::exists(vcpkg_dir_status_file_old)) + { + // no status file, use empty db + return StatusParagraphs(); + } + + fs::rename(vcpkg_dir_status_file_old, vcpkg_dir_status_file); + } + + auto text = Files::read_contents(vcpkg_dir_status_file).get_or_throw(); + auto pghs = Paragraphs::parse_paragraphs(text); + + std::vector<std::unique_ptr<StatusParagraph>> status_pghs; + for (auto&& p : pghs) + { + status_pghs.push_back(std::make_unique<StatusParagraph>(p)); + } + + return StatusParagraphs(std::move(status_pghs)); + } + + StatusParagraphs database_load_check(const vcpkg_paths& paths) + { + auto updates_dir = paths.vcpkg_dir_updates; + + std::error_code ec; + fs::create_directory(paths.installed, ec); + fs::create_directory(paths.vcpkg_dir, ec); + fs::create_directory(paths.vcpkg_dir_info, ec); + fs::create_directory(updates_dir, ec); + + const fs::path& status_file = paths.vcpkg_dir_status_file; + const fs::path status_file_old = status_file.parent_path() / "status-old"; + const fs::path status_file_new = status_file.parent_path() / "status-new"; + + StatusParagraphs current_status_db = load_current_database(status_file, status_file_old); + + auto b = fs::directory_iterator(updates_dir); + auto e = fs::directory_iterator(); + if (b == e) + { + // updates directory is empty, control file is up-to-date. + return current_status_db; + } + + for (; b != e; ++b) + { + if (!fs::is_regular_file(b->status())) + continue; + if (b->path().filename() == "incomplete") + continue; + + auto text = Files::read_contents(b->path()).get_or_throw(); + auto pghs = Paragraphs::parse_paragraphs(text); + for (auto&& p : pghs) + { + current_status_db.insert(std::make_unique<StatusParagraph>(p)); + } + } + + std::fstream(status_file_new, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc) << current_status_db; + + if (fs::exists(status_file_old)) + fs::remove(status_file_old); + if (fs::exists(status_file)) + fs::rename(status_file, status_file_old); + fs::rename(status_file_new, status_file); + fs::remove(status_file_old); + + b = fs::directory_iterator(updates_dir); + for (; b != e; ++b) + { + if (!fs::is_regular_file(b->status())) + continue; + fs::remove(b->path()); + } + + return current_status_db; + } + + void write_update(const vcpkg_paths& paths, const StatusParagraph& p) + { + static int update_id = 0; + auto my_update_id = update_id++; + auto tmp_update_filename = paths.vcpkg_dir_updates / "incomplete"; + auto update_filename = paths.vcpkg_dir_updates / std::to_string(my_update_id); + std::fstream fs(tmp_update_filename, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc); + fs << p; + fs.close(); + fs::rename(tmp_update_filename, update_filename); + } + + static void upgrade_to_slash_terminated_sorted_format(std::vector<std::string>* lines, const fs::path& listfile_path) + { + static bool was_tracked = false; + + if (lines->empty()) + { + return; + } + + if (lines->at(0).back() == '/') + { + return; // File already in the new format + } + + if (!was_tracked) + { + was_tracked = true; + TrackProperty("listfile", "update to new format"); + } + + // The files are sorted such that directories are placed just before the files they contain + // (They are not necessarily sorted alphabetically, e.g. libflac) + // Therefore we can detect the entries that represent directories by comparing every element with the next one + // and checking if the next has a slash immediately after the current one's length + for (size_t i = 0; i < lines->size() - 1; i++) + { + std::string& current_string = lines->at(i); + const std::string& next_string = lines->at(i + 1); + + const size_t potential_slash_char_index = current_string.length(); + // Make sure the index exists first + if (next_string.size() > potential_slash_char_index && next_string.at(potential_slash_char_index) == '/') + { + current_string += '/'; // Mark as a directory + } + } + + // After suffixing the directories with a slash, we can now sort. + // We cannot sort before adding the suffixes because the following (actual example): + /* + x86-windows/include/FLAC <<<<<< This would be separated from its group due to sorting + x86-windows/include/FLAC/all.h + x86-windows/include/FLAC/assert.h + x86-windows/include/FLAC/callback.h + x86-windows/include/FLAC++ + x86-windows/include/FLAC++/all.h + x86-windows/include/FLAC++/decoder.h + x86-windows/include/FLAC++/encoder.h + * + x86-windows/include/FLAC/ <<<<<< This will now be kept with its group when sorting + x86-windows/include/FLAC/all.h + x86-windows/include/FLAC/assert.h + x86-windows/include/FLAC/callback.h + x86-windows/include/FLAC++/ + x86-windows/include/FLAC++/all.h + x86-windows/include/FLAC++/decoder.h + x86-windows/include/FLAC++/encoder.h + */ + // Note that after sorting, the FLAC++/ group will be placed before the FLAC/ group + // The new format is lexicographically sorted + std::sort(lines->begin(), lines->end()); + + // Replace the listfile on disk + const fs::path updated_listfile_path = listfile_path.generic_string() + "_updated"; + Files::write_all_lines(updated_listfile_path, *lines); + fs::rename(updated_listfile_path, listfile_path); + } + + std::vector<StatusParagraph_and_associated_files> get_installed_files(const vcpkg_paths& paths, const StatusParagraphs& status_db) + { + std::vector<StatusParagraph_and_associated_files> installed_files; + + for (const std::unique_ptr<StatusParagraph>& pgh : status_db) + { + if (pgh->state != install_state_t::installed) + { + continue; + } + + const fs::path listfile_path = paths.listfile_path(pgh->package); + std::vector<std::string> installed_files_of_current_pgh = Files::read_all_lines(listfile_path).get_or_throw(); + Strings::trim_all_and_remove_whitespace_strings(&installed_files_of_current_pgh); + upgrade_to_slash_terminated_sorted_format(&installed_files_of_current_pgh, listfile_path); + + // Remove the directories + installed_files_of_current_pgh.erase( + std::remove_if(installed_files_of_current_pgh.begin(), installed_files_of_current_pgh.end(), [](const std::string& file) -> bool + { + return file.back() == '/'; + } + ), installed_files_of_current_pgh.end()); + + StatusParagraph_and_associated_files pgh_and_files = { *pgh, ImmutableSortedVector<std::string>::create(std::move(installed_files_of_current_pgh)) }; + installed_files.push_back(std::move(pgh_and_files)); + } + + return installed_files; + } + + CMakeVariable::CMakeVariable(const std::wstring& varname, const wchar_t* varvalue) : s(Strings::wformat(LR"("-D%s=%s")", varname, varvalue)) { } + CMakeVariable::CMakeVariable(const std::wstring& varname, const std::string& varvalue) : CMakeVariable(varname, Strings::utf8_to_utf16(varvalue).c_str()) { } + CMakeVariable::CMakeVariable(const std::wstring& varname, const std::wstring& varvalue) : CMakeVariable(varname, varvalue.c_str()) {} + CMakeVariable::CMakeVariable(const std::wstring& varname, const fs::path& path) : CMakeVariable(varname, path.generic_wstring()) {} + + std::wstring make_cmake_cmd(const fs::path& cmake_exe, const fs::path& cmake_script, const std::vector<CMakeVariable>& pass_variables) + { + std::wstring cmd_cmake_pass_variables = Strings::wjoin(L" ", pass_variables, [](auto&& v) { return v.s; }); + return Strings::wformat(LR"("%s" %s -P "%s")", cmake_exe.native(), cmd_cmake_pass_variables, cmake_script.generic_wstring()); + } +} diff --git a/toolsrc/src/vcpkglib_helpers.cpp b/toolsrc/src/vcpkglib_helpers.cpp index d104bb19d..6b96c25cb 100644 --- a/toolsrc/src/vcpkglib_helpers.cpp +++ b/toolsrc/src/vcpkglib_helpers.cpp @@ -1,9 +1,8 @@ +#include "pch.h" #include "vcpkg_Checks.h" #include "vcpkglib_helpers.h" -#include <unordered_map> -#include <regex> -namespace vcpkg {namespace details +namespace vcpkg::details { std::string optional_field(const std::unordered_map<std::string, std::string>& fields, const std::string& fieldname) { @@ -53,4 +52,4 @@ namespace vcpkg {namespace details simple_desc.append("..."); return simple_desc; } -}} +} |
