aboutsummaryrefslogtreecommitdiff
path: root/toolsrc/src
diff options
context:
space:
mode:
authorRobert Schumacher <roschuma@microsoft.com>2017-02-08 15:12:28 -0800
committerGitHub <noreply@github.com>2017-02-08 15:12:28 -0800
commit7ddae17e2f520e83d25f78c078bf8b8a58fff447 (patch)
tree87e2fc5c57a685367ec051b1efbdeb5d3ab43f4d /toolsrc/src
parent5e588ddb5be9e6e27cebcc3be2e1a27f3ca83a50 (diff)
parenta9f7fc6e90feaad50c1221ef9bd56e2620302215 (diff)
downloadvcpkg-7ddae17e2f520e83d25f78c078bf8b8a58fff447.tar.gz
vcpkg-7ddae17e2f520e83d25f78c078bf8b8a58fff447.zip
Merge branch 'master' into master
Diffstat (limited to 'toolsrc/src')
-rw-r--r--toolsrc/src/BinaryParagraph.cpp66
-rw-r--r--toolsrc/src/MachineType.cpp42
-rw-r--r--toolsrc/src/Paragraphs.cpp170
-rw-r--r--toolsrc/src/PostBuildLint.cpp730
-rw-r--r--toolsrc/src/PostBuildLint_BuildInfo.cpp42
-rw-r--r--toolsrc/src/PostBuildLint_BuildPolicies.cpp66
-rw-r--r--toolsrc/src/PostBuildLint_BuildType.cpp68
-rw-r--r--toolsrc/src/PostBuildLint_ConfigurationType.cpp19
-rw-r--r--toolsrc/src/PostBuildLint_LinkageType.cpp34
-rw-r--r--toolsrc/src/SourceParagraph.cpp151
-rw-r--r--toolsrc/src/StatusParagraph.cpp9
-rw-r--r--toolsrc/src/StatusParagraphs.cpp2
-rw-r--r--toolsrc/src/Stopwatch.cpp3
-rw-r--r--toolsrc/src/coff_file_reader.cpp152
-rw-r--r--toolsrc/src/commands_available_commands.cpp45
-rw-r--r--toolsrc/src/commands_build.cpp131
-rw-r--r--toolsrc/src/commands_build_external.cpp33
-rw-r--r--toolsrc/src/commands_cache.cpp69
-rw-r--r--toolsrc/src/commands_contact.cpp14
-rw-r--r--toolsrc/src/commands_create.cpp11
-rw-r--r--toolsrc/src/commands_edit.cpp12
-rw-r--r--toolsrc/src/commands_hash.cpp45
-rw-r--r--toolsrc/src/commands_help.cpp87
-rw-r--r--toolsrc/src/commands_helpers.cpp7
-rw-r--r--toolsrc/src/commands_import.cpp86
-rw-r--r--toolsrc/src/commands_install.cpp239
-rw-r--r--toolsrc/src/commands_installation.cpp176
-rw-r--r--toolsrc/src/commands_integrate.cpp (renamed from toolsrc/src/commands_integration.cpp)17
-rw-r--r--toolsrc/src/commands_list.cpp66
-rw-r--r--toolsrc/src/commands_other.cpp100
-rw-r--r--toolsrc/src/commands_owns.cpp28
-rw-r--r--toolsrc/src/commands_portsdiff.cpp167
-rw-r--r--toolsrc/src/commands_remove.cpp202
-rw-r--r--toolsrc/src/commands_search.cpp74
-rw-r--r--toolsrc/src/commands_update.cpp19
-rw-r--r--toolsrc/src/commands_version.cpp17
-rw-r--r--toolsrc/src/lib.cpp511
-rw-r--r--toolsrc/src/main.cpp246
-rw-r--r--toolsrc/src/metrics.cpp50
-rw-r--r--toolsrc/src/opt_bool.cpp29
-rw-r--r--toolsrc/src/package_spec.cpp15
-rw-r--r--toolsrc/src/package_spec_parse_result.cpp4
-rw-r--r--toolsrc/src/pch.cpp1
-rw-r--r--toolsrc/src/post_build_lint.cpp433
-rw-r--r--toolsrc/src/tests_dependencies.cpp39
-rw-r--r--toolsrc/src/tests_paragraph.cpp (renamed from toolsrc/src/test.cpp)80
-rw-r--r--toolsrc/src/triplet.cpp12
-rw-r--r--toolsrc/src/vcpkg.cpp345
-rw-r--r--toolsrc/src/vcpkg_Checks.cpp11
-rw-r--r--toolsrc/src/vcpkg_Dependencies.cpp157
-rw-r--r--toolsrc/src/vcpkg_Environment.cpp153
-rw-r--r--toolsrc/src/vcpkg_Files.cpp101
-rw-r--r--toolsrc/src/vcpkg_Input.cpp13
-rw-r--r--toolsrc/src/vcpkg_Strings.cpp100
-rw-r--r--toolsrc/src/vcpkg_System.cpp16
-rw-r--r--toolsrc/src/vcpkg_cmd_arguments.cpp36
-rw-r--r--toolsrc/src/vcpkg_info.cpp35
-rw-r--r--toolsrc/src/vcpkg_metrics_uploader.cpp4
-rw-r--r--toolsrc/src/vcpkg_paths.cpp17
-rw-r--r--toolsrc/src/vcpkg_version.cpp25
-rw-r--r--toolsrc/src/vcpkglib.cpp240
-rw-r--r--toolsrc/src/vcpkglib_helpers.cpp62
62 files changed, 3869 insertions, 2065 deletions
diff --git a/toolsrc/src/BinaryParagraph.cpp b/toolsrc/src/BinaryParagraph.cpp
index 48d04f686..f949677a3 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"
@@ -6,28 +7,57 @@ using namespace vcpkg::details;
namespace vcpkg
{
+ //
+ namespace BinaryParagraphRequiredField
+ {
+ static const std::string PACKAGE = "Package";
+ static const std::string VERSION = "Version";
+ static const std::string ARCHITECTURE = "Architecture";
+ static const std::string MULTI_ARCH = "Multi-Arch";
+ }
+
+ namespace BinaryParagraphOptionalField
+ {
+ static const std::string DESCRIPTION = "Description";
+ static const std::string MAINTAINER = "Maintainer";
+ 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(const std::unordered_map<std::string, std::string>& fields) :
- version(required_field(fields, "Version")),
- description(optional_field(fields, "Description")),
- maintainer(optional_field(fields, "Maintainer"))
+ BinaryParagraph::BinaryParagraph(std::unordered_map<std::string, std::string> fields)
{
- const std::string name = required_field(fields, "Package");
- const triplet target_triplet = triplet::from_canonical_name(required_field(fields, "Architecture"));
+ const std::string name = details::remove_required_field(&fields, BinaryParagraphRequiredField::PACKAGE);
+ const std::string architecture = details::remove_required_field(&fields, BinaryParagraphRequiredField::ARCHITECTURE);
+ const triplet target_triplet = triplet::from_canonical_name(architecture);
+
this->spec = package_spec::from_name_and_triplet(name, target_triplet).get_or_throw();
+ this->version = details::remove_required_field(&fields, BinaryParagraphRequiredField::VERSION);
- {
- std::string multi_arch = required_field(fields, "Multi-Arch");
- Checks::check_throw(multi_arch == "same", "Multi-Arch must be 'same' but was %s", multi_arch);
- }
+ this->description = details::remove_optional_field(&fields, BinaryParagraphOptionalField::DESCRIPTION);
+ this->maintainer = details::remove_optional_field(&fields, BinaryParagraphOptionalField::MAINTAINER);
- std::string deps = optional_field(fields, "Depends");
- if (!deps.empty())
- {
- this->depends.clear();
- this->depends = parse_depends(deps);
- }
+ std::string multi_arch = details::remove_required_field(&fields, BinaryParagraphRequiredField::MULTI_ARCH);
+ Checks::check_exit(multi_arch == "same", "Multi-Arch must be 'same' but was %s", multi_arch);
+
+ std::string deps = details::remove_optional_field(&fields, BinaryParagraphOptionalField::DEPENDS);
+ this->depends = parse_depends(deps);
}
BinaryParagraph::BinaryParagraph(const SourceParagraph& spgh, const triplet& target_triplet)
@@ -36,12 +66,12 @@ namespace vcpkg
this->version = spgh.version;
this->description = spgh.description;
this->maintainer = spgh.maintainer;
- this->depends = spgh.depends;
+ this->depends = filter_dependencies(spgh.depends, target_triplet);
}
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/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
new file mode 100644
index 000000000..6dde5da7c
--- /dev/null
+++ b/toolsrc/src/Paragraphs.cpp
@@ -0,0 +1,170 @@
+#include "pch.h"
+#include "Paragraphs.h"
+#include "vcpkg_Files.h"
+
+namespace vcpkg::Paragraphs
+{
+ struct Parser
+ {
+ Parser(const char* c, const char* e) : cur(c), end(e)
+ {
+ }
+
+ private:
+ const char* cur;
+ const char* const end;
+
+ void peek(char& ch) const
+ {
+ if (cur == end)
+ ch = 0;
+ else
+ ch = *cur;
+ }
+
+ void next(char& ch)
+ {
+ if (cur == end)
+ ch = 0;
+ else
+ {
+ ++cur;
+ peek(ch);
+ }
+ }
+
+ void skip_spaces(char& ch)
+ {
+ while (ch == ' ' || ch == '\t')
+ next(ch);
+ }
+
+ static bool is_alphanum(char ch)
+ {
+ return (ch >= 'A' && ch <= 'Z')
+ || (ch >= 'a' && ch <= 'z')
+ || (ch >= '0' && ch <= '9');
+ }
+
+ static bool is_lineend(char ch)
+ {
+ return ch == '\r' || ch == '\n' || ch == 0;
+ }
+
+ void get_fieldvalue(char& ch, std::string& fieldvalue)
+ {
+ fieldvalue.clear();
+
+ auto beginning_of_line = cur;
+ do
+ {
+ // scan to end of current line (it is part of the field value)
+ while (!is_lineend(ch))
+ next(ch);
+
+ fieldvalue.append(beginning_of_line, cur);
+
+ if (ch == '\r')
+ next(ch);
+ if (ch == '\n')
+ next(ch);
+
+ if (is_alphanum(ch))
+ {
+ // Line begins a new field.
+ return;
+ }
+
+ beginning_of_line = cur;
+
+ // Line may continue the current field with data or terminate the paragraph,
+ // depending on first nonspace character.
+ skip_spaces(ch);
+
+ if (is_lineend(ch))
+ {
+ // Line was whitespace or empty.
+ // This terminates the field and the paragraph.
+ // We leave the blank line's whitespace consumed, because it doesn't matter.
+ return;
+ }
+
+ // First nonspace is not a newline. This continues the current field value.
+ // We forcibly convert all newlines into single '\n' for ease of text handling later on.
+ fieldvalue.push_back('\n');
+ }
+ while (true);
+ }
+
+ void get_fieldname(char& ch, std::string& fieldname)
+ {
+ auto begin_fieldname = cur;
+ while (is_alphanum(ch) || ch == '-')
+ next(ch);
+ Checks::check_throw(ch == ':', "Expected ':'");
+ fieldname = std::string(begin_fieldname, cur);
+
+ // skip ': '
+ next(ch);
+ skip_spaces(ch);
+ }
+
+ void get_paragraph(char& ch, std::unordered_map<std::string, std::string>& fields)
+ {
+ fields.clear();
+ std::string fieldname;
+ std::string fieldvalue;
+ do
+ {
+ get_fieldname(ch, fieldname);
+
+ auto it = fields.find(fieldname);
+ Checks::check_throw(it == fields.end(), "Duplicate field");
+
+ get_fieldvalue(ch, fieldvalue);
+
+ fields.emplace(fieldname, fieldvalue);
+ }
+ while (!is_lineend(ch));
+ }
+
+ public:
+ std::vector<std::unordered_map<std::string, std::string>> get_paragraphs()
+ {
+ std::vector<std::unordered_map<std::string, std::string>> paragraphs;
+
+ char ch;
+ peek(ch);
+
+ while (ch != 0)
+ {
+ if (ch == '\n' || ch == '\r' || ch == ' ' || ch == '\t')
+ {
+ next(ch);
+ continue;
+ }
+
+ paragraphs.emplace_back();
+ get_paragraph(ch, paragraphs.back());
+ }
+
+ return paragraphs;
+ }
+ };
+
+ std::vector<std::unordered_map<std::string, std::string>> get_paragraphs(const fs::path& control_path)
+ {
+ 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();
+ }
+}
diff --git a/toolsrc/src/PostBuildLint.cpp b/toolsrc/src/PostBuildLint.cpp
new file mode 100644
index 000000000..90bd55843
--- /dev/null
+++ b/toolsrc/src/PostBuildLint.cpp
@@ -0,0 +1,730 @@
+#include "pch.h"
+#include "vcpkg_paths.h"
+#include "package_spec.h"
+#include "vcpkg_Files.h"
+#include "vcpkg_System.h"
+#include "vcpkg_Environment.h"
+#include "coff_file_reader.h"
+#include "PostBuildLint_BuildInfo.h"
+#include "PostBuildLint_BuildType.h"
+
+namespace vcpkg::PostBuildLint
+{
+ enum class lint_status
+ {
+ SUCCESS = 0,
+ ERROR_DETECTED = 1
+ };
+
+ 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)
+ {
+ const fs::path include_dir = package_dir / "include";
+ if (!fs::exists(include_dir) || fs::is_empty(include_dir))
+ {
+ System::println(System::color::warning, "The folder /include is empty. This indicates the library was not correctly installed.");
+ return lint_status::ERROR_DETECTED;
+ }
+
+ return lint_status::SUCCESS;
+ }
+
+ static lint_status check_for_files_in_debug_include_directory(const fs::path& package_dir)
+ {
+ const fs::path debug_include_dir = package_dir / "debug" / "include";
+ std::vector<fs::path> files_found;
+
+ Files::recursive_find_matching_paths_in_dir(debug_include_dir, [&](const fs::path& current)
+ {
+ return !fs::is_directory(current) && current.extension() != ".ifc";
+ }, &files_found);
+
+ if (!files_found.empty())
+ {
+ System::println(System::color::warning, "Include files should not be duplicated into the /debug/include directory. If this cannot be disabled in the project cmake, use\n"
+ " file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/debug/include)"
+ );
+ return lint_status::ERROR_DETECTED;
+ }
+
+ return lint_status::SUCCESS;
+ }
+
+ static lint_status check_for_files_in_debug_share_directory(const fs::path& package_dir)
+ {
+ const fs::path debug_share = package_dir / "debug" / "share";
+
+ if (fs::exists(debug_share) && !fs::is_empty(debug_share))
+ {
+ System::println(System::color::warning, "No files should be present in /debug/share");
+ return lint_status::ERROR_DETECTED;
+ }
+
+ return lint_status::SUCCESS;
+ }
+
+ static lint_status check_folder_lib_cmake(const fs::path& package_dir)
+ {
+ const fs::path lib_cmake = package_dir / "lib" / "cmake";
+ if (fs::exists(lib_cmake))
+ {
+ System::println(System::color::warning, "The /lib/cmake folder should be moved to just /cmake");
+ return lint_status::ERROR_DETECTED;
+ }
+
+ return lint_status::SUCCESS;
+ }
+
+ static lint_status check_for_misplaced_cmake_files(const fs::path& package_dir, const package_spec& spec)
+ {
+ std::vector<fs::path> misplaced_cmake_files;
+ Files::recursive_find_files_with_extension_in_dir(package_dir / "cmake", ".cmake", &misplaced_cmake_files);
+ Files::recursive_find_files_with_extension_in_dir(package_dir / "debug" / "cmake", ".cmake", &misplaced_cmake_files);
+ Files::recursive_find_files_with_extension_in_dir(package_dir / "lib" / "cmake", ".cmake", &misplaced_cmake_files);
+ Files::recursive_find_files_with_extension_in_dir(package_dir / "debug" / "lib" / "cmake", ".cmake", &misplaced_cmake_files);
+
+ if (!misplaced_cmake_files.empty())
+ {
+ System::println(System::color::warning, "The following cmake files were found outside /share/%s. Please place cmake files in /share/%s.", spec.name(), spec.name());
+ Files::print_paths(misplaced_cmake_files);
+ return lint_status::ERROR_DETECTED;
+ }
+
+ return lint_status::SUCCESS;
+ }
+
+ static lint_status check_folder_debug_lib_cmake(const fs::path& package_dir)
+ {
+ const fs::path lib_cmake_debug = package_dir / "debug" / "lib" / "cmake";
+ if (fs::exists(lib_cmake_debug))
+ {
+ System::println(System::color::warning, "The /debug/lib/cmake folder should be moved to just /debug/cmake");
+ return lint_status::ERROR_DETECTED;
+ }
+
+ return lint_status::SUCCESS;
+ }
+
+ static lint_status check_for_dlls_in_lib_dirs(const fs::path& package_dir)
+ {
+ std::vector<fs::path> dlls;
+ Files::recursive_find_files_with_extension_in_dir(package_dir / "lib", ".dll", &dlls);
+ Files::recursive_find_files_with_extension_in_dir(package_dir / "debug" / "lib", ".dll", &dlls);
+
+ if (!dlls.empty())
+ {
+ System::println(System::color::warning, "\nThe following dlls were found in /lib and /debug/lib. Please move them to /bin or /debug/bin, respectively.");
+ Files::print_paths(dlls);
+ return lint_status::ERROR_DETECTED;
+ }
+
+ return lint_status::SUCCESS;
+ }
+
+ static lint_status check_for_copyright_file(const package_spec& spec, const vcpkg_paths& paths)
+ {
+ const fs::path packages_dir = paths.packages / spec.dir();
+ const fs::path copyright_file = packages_dir / "share" / spec.name() / "copyright";
+ if (fs::exists(copyright_file))
+ {
+ return lint_status::SUCCESS;
+ }
+ const fs::path current_buildtrees_dir = paths.buildtrees / spec.name();
+ const fs::path current_buildtrees_dir_src = current_buildtrees_dir / "src";
+
+ std::vector<fs::path> potential_copyright_files;
+ // Only searching one level deep
+ for (auto it = fs::recursive_directory_iterator(current_buildtrees_dir_src); it != fs::recursive_directory_iterator(); ++it)
+ {
+ if (it.depth() > 1)
+ {
+ continue;
+ }
+
+ const std::string filename = it->path().filename().string();
+ if (filename == "LICENSE" || filename == "LICENSE.txt" || filename == "COPYING")
+ {
+ potential_copyright_files.push_back(it->path());
+ }
+ }
+
+ System::println(System::color::warning, "The software license must be available at ${CURRENT_PACKAGES_DIR}/share/%s/copyright .", spec.name());
+ if (potential_copyright_files.size() == 1) // if there is only one candidate, provide the cmake lines needed to place it in the proper location
+ {
+ const fs::path found_file = potential_copyright_files[0];
+ const fs::path relative_path = found_file.string().erase(0, current_buildtrees_dir.string().size() + 1); // The +1 is needed to remove the "/"
+ System::println("\n file(COPY ${CURRENT_BUILDTREES_DIR}/%s DESTINATION ${CURRENT_PACKAGES_DIR}/share/%s)\n"
+ " file(RENAME ${CURRENT_PACKAGES_DIR}/share/%s/%s ${CURRENT_PACKAGES_DIR}/share/%s/copyright)",
+ relative_path.generic_string(), spec.name(), spec.name(), found_file.filename().generic_string(), spec.name());
+ return lint_status::ERROR_DETECTED;
+ }
+
+ if (potential_copyright_files.size() > 1)
+ {
+ System::println(System::color::warning, "The following files are potential copyright files:");
+ Files::print_paths(potential_copyright_files);
+ }
+
+ System::println(" %s/share/%s/copyright", packages_dir.generic_string(), spec.name());
+ return lint_status::ERROR_DETECTED;
+ }
+
+ static lint_status check_for_exes(const fs::path& package_dir)
+ {
+ std::vector<fs::path> exes;
+ Files::recursive_find_files_with_extension_in_dir(package_dir / "bin", ".exe", &exes);
+ Files::recursive_find_files_with_extension_in_dir(package_dir / "debug" / "bin", ".exe", &exes);
+
+ if (!exes.empty())
+ {
+ System::println(System::color::warning, "The following EXEs were found in /bin and /debug/bin. EXEs are not valid distribution targets.");
+ Files::print_paths(exes);
+ return lint_status::ERROR_DETECTED;
+ }
+
+ return lint_status::SUCCESS;
+ }
+
+ 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());
+ 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));
+
+ if (ec_data.output.find("ordinal hint RVA name") == std::string::npos)
+ {
+ dlls_with_no_exports.push_back(dll);
+ }
+ }
+
+ if (!dlls_with_no_exports.empty())
+ {
+ System::println(System::color::warning, "The following DLLs have no exports:");
+ Files::print_paths(dlls_with_no_exports);
+ System::println(System::color::warning, "DLLs without any exports are likely a bug in the build script.");
+ return lint_status::ERROR_DETECTED;
+ }
+
+ return lint_status::SUCCESS;
+ }
+
+ 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")
+ {
+ return lint_status::SUCCESS;
+ }
+
+ 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());
+ 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));
+
+ if (ec_data.output.find("App Container") == std::string::npos)
+ {
+ dlls_with_improper_uwp_bit.push_back(dll);
+ }
+ }
+
+ if (!dlls_with_improper_uwp_bit.empty())
+ {
+ System::println(System::color::warning, "The following DLLs do not have the App Container bit set:");
+ Files::print_paths(dlls_with_improper_uwp_bit);
+ System::println(System::color::warning, "This bit is required for Windows Store apps.");
+ return lint_status::ERROR_DETECTED;
+ }
+
+ return lint_status::SUCCESS;
+ }
+
+ struct file_and_arch
+ {
+ fs::path file;
+ std::string actual_arch;
+ };
+
+ static std::string get_actual_architecture(const MachineType& machine_type)
+ {
+ switch (machine_type)
+ {
+ case MachineType::AMD64:
+ case MachineType::IA64:
+ return "x64";
+ case MachineType::I386:
+ return "x86";
+ case MachineType::ARM:
+ case MachineType::ARMNT:
+ return "arm";
+ default:
+ return "Machine Type Code = " + std::to_string(static_cast<uint16_t>(machine_type));
+ }
+ }
+
+ static void print_invalid_architecture_files(const std::string& expected_architecture, std::vector<file_and_arch> binaries_with_invalid_architecture)
+ {
+ System::println(System::color::warning, "The following files were built for an incorrect architecture:");
+ System::println("");
+ for (const file_and_arch& b : binaries_with_invalid_architecture)
+ {
+ System::println(" %s", b.file.generic_string());
+ System::println("Expected %s, but was: %s", expected_architecture, b.actual_arch);
+ System::println("");
+ }
+ }
+
+ static lint_status check_dll_architecture(const std::string& expected_architecture, const std::vector<fs::path>& files)
+ {
+ std::vector<file_and_arch> binaries_with_invalid_architecture;
+
+ for (const fs::path& file : files)
+ {
+ Checks::check_exit(file.extension() == ".dll", "The file extension was not .dll: %s", file.generic_string());
+ COFFFileReader::dll_info info = COFFFileReader::read_dll(file);
+ const std::string actual_architecture = get_actual_architecture(info.machine_type);
+
+ if (expected_architecture != actual_architecture)
+ {
+ binaries_with_invalid_architecture.push_back({file, actual_architecture});
+ }
+ }
+
+ if (!binaries_with_invalid_architecture.empty())
+ {
+ print_invalid_architecture_files(expected_architecture, binaries_with_invalid_architecture);
+ return lint_status::ERROR_DETECTED;
+ }
+
+ return lint_status::SUCCESS;
+ }
+
+ static lint_status check_lib_architecture(const std::string& expected_architecture, const std::vector<fs::path>& files)
+ {
+ std::vector<file_and_arch> binaries_with_invalid_architecture;
+
+ for (const fs::path& file : files)
+ {
+ Checks::check_exit(file.extension() == ".lib", "The file extension was not .lib: %s", file.generic_string());
+ COFFFileReader::lib_info info = COFFFileReader::read_lib(file);
+ Checks::check_exit(info.machine_types.size() == 1, "Found more than 1 architecture in file %s", file.generic_string());
+
+ 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});
+ }
+ }
+
+ if (!binaries_with_invalid_architecture.empty())
+ {
+ print_invalid_architecture_files(expected_architecture, binaries_with_invalid_architecture);
+ return lint_status::ERROR_DETECTED;
+ }
+
+ return lint_status::SUCCESS;
+ }
+
+ static lint_status check_no_dlls_present(const std::vector<fs::path>& dlls)
+ {
+ if (dlls.empty())
+ {
+ return lint_status::SUCCESS;
+ }
+
+ System::println(System::color::warning, "DLLs should not be present in a static build, but the following DLLs were found:");
+ Files::print_paths(dlls);
+ return lint_status::ERROR_DETECTED;
+ }
+
+ static lint_status check_matching_debug_and_release_binaries(const std::vector<fs::path>& debug_binaries, const std::vector<fs::path>& release_binaries)
+ {
+ const size_t debug_count = debug_binaries.size();
+ const size_t release_count = release_binaries.size();
+ if (debug_count == release_count)
+ {
+ return lint_status::SUCCESS;
+ }
+
+ System::println(System::color::warning, "Mismatching number of debug and release binaries. Found %d for debug but %d for release.", debug_count, release_count);
+ System::println("Debug binaries");
+ Files::print_paths(debug_binaries);
+
+ System::println("Release binaries");
+ Files::print_paths(release_binaries);
+
+ if (debug_count == 0)
+ {
+ System::println(System::color::warning, "Debug binaries were not found");
+ }
+ if (release_count == 0)
+ {
+ System::println(System::color::warning, "Release binaries were not found");
+ }
+
+ System::println("");
+
+ return lint_status::ERROR_DETECTED;
+ }
+
+ 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)
+ {
+ auto it = policies.find(BuildPolicies::DLLS_WITHOUT_LIBS);
+ if (it != policies.cend() && it->second == opt_bool_t::DISABLED)
+ {
+ return lint_status::SUCCESS;
+ }
+
+ if (lib_count == 0 && dll_count != 0)
+ {
+ 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;
+ }
+
+ 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())
+ {
+ 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);
+ return lint_status::ERROR_DETECTED;
+ }
+
+ return lint_status::SUCCESS;
+ }
+
+ static lint_status check_bin_folders_are_not_present_in_static_build(const fs::path& package_dir)
+ {
+ const fs::path bin = package_dir / "bin";
+ const fs::path debug_bin = package_dir / "debug" / "bin";
+
+ if (!fs::exists(bin) && !fs::exists(debug_bin))
+ {
+ return lint_status::SUCCESS;
+ }
+
+ if (fs::exists(bin))
+ {
+ System::println(System::color::warning, R"(There should be no bin\ directory in a static build, but %s is present.)", bin.generic_string());
+ }
+
+ if (fs::exists(debug_bin))
+ {
+ System::println(System::color::warning, R"(There should be no debug\bin\ directory in a static build, but %s is present.)", debug_bin.generic_string());
+ }
+
+ System::println(System::color::warning, R"(If the creation of bin\ and/or debug\bin\ cannot be disabled, use this in the portfile to remove them)" "\n"
+ "\n"
+ R"###( if(VCPKG_LIBRARY_LINKAGE STREQUAL static))###""\n"
+ R"###( file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/bin ${CURRENT_PACKAGES_DIR}/debug/bin))###""\n"
+ R"###( endif())###"
+ "\n"
+ );
+
+ return lint_status::ERROR_DETECTED;
+ }
+
+ static lint_status check_no_empty_folders(const fs::path& dir)
+ {
+ const std::vector<fs::path> empty_directories = Files::recursive_find_matching_paths_in_dir(dir, [](const fs::path& current)
+ {
+ return fs::is_directory(current) && fs::is_empty(current);
+ });
+
+ if (!empty_directories.empty())
+ {
+ System::println(System::color::warning, "There should be no empty directories in %s", dir.generic_string());
+ 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"
+ "\n"
+ R"###( file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/a/dir ${CURRENT_PACKAGES_DIR}/some/other/dir))###""\n"
+ "\n");
+ return lint_status::ERROR_DETECTED;
+ }
+
+ return lint_status::SUCCESS;
+ }
+
+ struct BuildType_and_file
+ {
+ fs::path file;
+ BuildType build_type;
+ };
+
+ static lint_status check_crt_linkage_of_libs(const BuildType& expected_build_type, const std::vector<fs::path>& libs, const fs::path dumpbin_exe)
+ {
+ std::vector<BuildType> bad_build_types = BuildType::values();
+ 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());
+ 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)
+ {
+ 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});
+ break;
+ }
+ }
+ }
+
+ if (!libs_with_invalid_crt.empty())
+ {
+ System::println(System::color::warning, "Expected %s crt linkage, but the following libs had invalid crt linkage:", expected_build_type.toString());
+ System::println("");
+ for (const BuildType_and_file btf : libs_with_invalid_crt)
+ {
+ System::println(" %s: %s", btf.file.generic_string(), btf.build_type.toString());
+ }
+ System::println("");
+
+ System::println(System::color::warning, "To inspect the lib files, use:\n dumpbin.exe /directives mylibfile.lib");
+ return lint_status::ERROR_DETECTED;
+ }
+
+ return lint_status::SUCCESS;
+ }
+
+ struct OutdatedDynamicCrt_and_file
+ {
+ fs::path file;
+ OutdatedDynamicCrt outdated_crt;
+ };
+
+ 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 = 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());
+ 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.regex))
+ {
+ dlls_with_outdated_crt.push_back({dll, outdated_crt});
+ break;
+ }
+ }
+ }
+
+ if (!dlls_with_outdated_crt.empty())
+ {
+ System::println(System::color::warning, "Detected outdated dynamic CRT in the following files:");
+ System::println("");
+ for (const OutdatedDynamicCrt_and_file btf : dlls_with_outdated_crt)
+ {
+ System::println(" %s: %s", btf.file.generic_string(), btf.outdated_crt.name);
+ }
+ System::println("");
+
+ System::println(System::color::warning, "To inspect the dll files, use:\n dumpbin.exe /dependents mydllfile.dll");
+ return lint_status::ERROR_DETECTED;
+ }
+
+ return lint_status::SUCCESS;
+ }
+
+ static lint_status check_no_files_in_package_dir_and_debug_dir(const fs::path& package_dir)
+ {
+ std::vector<fs::path> misplaced_files;
+
+ Files::non_recursive_find_matching_paths_in_dir(package_dir, [](const fs::path& current)
+ {
+ const std::string filename = current.filename().generic_string();
+ return !fs::is_directory(current) && !((_stricmp(filename.c_str(), "CONTROL") == 0 || _stricmp(filename.c_str(), "BUILD_INFO") == 0));
+ }, &misplaced_files);
+
+ const fs::path debug_dir = package_dir / "debug";
+ Files::non_recursive_find_all_files_in_dir(debug_dir, &misplaced_files);
+
+ if (!misplaced_files.empty())
+ {
+ System::println(System::color::warning, "The following files are placed in\n%s and\n%s: ", package_dir.generic_string(), debug_dir.generic_string());
+ Files::print_paths(misplaced_files);
+ System::println(System::color::warning, "Files cannot be present in those directories.\n");
+ return lint_status::ERROR_DETECTED;
+ }
+
+ return lint_status::SUCCESS;
+ }
+
+ static void operator +=(size_t& left, const lint_status& right)
+ {
+ left += static_cast<size_t>(right);
+ }
+
+ 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;
+
+ auto it = build_info.policies.find(BuildPolicies::EMPTY_PACKAGE);
+ if (it != build_info.policies.cend() && it->second == opt_bool_t::ENABLED)
+ {
+ 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);
+ error_count += check_folder_lib_cmake(package_dir);
+ error_count += check_for_misplaced_cmake_files(package_dir, spec);
+ error_count += check_folder_debug_lib_cmake(package_dir);
+ error_count += check_for_dlls_in_lib_dirs(package_dir);
+ error_count += check_for_copyright_file(spec, paths);
+ error_count += check_for_exes(package_dir);
+
+ const fs::path debug_lib_dir = package_dir / "debug" / "lib";
+ const fs::path release_lib_dir = package_dir / "lib";
+ const fs::path debug_bin_dir = package_dir / "debug" / "bin";
+ const fs::path release_bin_dir = package_dir / "bin";
+
+ const std::vector<fs::path> debug_libs = Files::recursive_find_files_with_extension_in_dir(debug_lib_dir, ".lib");
+ const std::vector<fs::path> release_libs = Files::recursive_find_files_with_extension_in_dir(release_lib_dir, ".lib");
+
+ error_count += check_matching_debug_and_release_binaries(debug_libs, release_libs);
+
+ std::vector<fs::path> libs;
+ libs.insert(libs.cend(), debug_libs.cbegin(), debug_libs.cend());
+ libs.insert(libs.cend(), release_libs.cbegin(), release_libs.cend());
+
+ error_count += check_lib_architecture(spec.target_triplet().architecture(), libs);
+
+ switch (linkage_type_value_of(build_info.library_linkage))
+ {
+ case LinkageType::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(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, 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, dumpbin_exe);
+ break;
+ }
+ case LinkageType::STATIC:
+ {
+ std::vector<fs::path> dlls;
+ Files::recursive_find_files_with_extension_in_dir(package_dir, ".dll", &dlls);
+ error_count += check_no_dlls_present(dlls);
+
+ 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, dumpbin_exe);
+ error_count += check_crt_linkage_of_libs(BuildType::value_of(ConfigurationType::RELEASE, linkage_type_value_of(build_info.crt_linkage)), release_libs, dumpbin_exe);
+ break;
+ }
+ case LinkageType::UNKNOWN:
+ {
+ error_count += 1;
+ System::println(System::color::warning, "Unknown library_linkage architecture: [ %s ]", build_info.library_linkage);
+ break;
+ }
+ 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;
+ }
+
+ void 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);
+
+ 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");
+ }
+}
diff --git a/toolsrc/src/PostBuildLint_BuildInfo.cpp b/toolsrc/src/PostBuildLint_BuildInfo.cpp
new file mode 100644
index 000000000..63107acd1
--- /dev/null
+++ b/toolsrc/src/PostBuildLint_BuildInfo.cpp
@@ -0,0 +1,42 @@
+#include "pch.h"
+#include "PostBuildLint_BuildInfo.h"
+#include "vcpkg_Checks.h"
+#include "opt_bool.h"
+#include "vcpkglib_helpers.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;
+ build_info.crt_linkage = details::remove_required_field(&pgh, BuildInfoRequiredField::CRT_LINKAGE);
+ build_info.library_linkage = details::remove_required_field(&pgh, BuildInfoRequiredField::LIBRARY_LINKAGE);
+
+ // 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::UNKNOWN, "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..4e5ac3cea
--- /dev/null
+++ b/toolsrc/src/PostBuildLint_BuildPolicies.cpp
@@ -0,0 +1,66 @@
+#include "pch.h"
+#include "PostBuildLint_BuildPolicies.h"
+#include "vcpkg_Checks.h"
+
+namespace vcpkg::PostBuildLint::BuildPolicies
+{
+ static const std::string NAME_UNKNOWN = "PolicyUnknown";
+ static const std::string NAME_EMPTY_PACKAGE = "PolicyEmptyPackage";
+ static const std::string NAME_DLLS_WITHOUT_LIBS = "PolicyDLLsWithoutLIBs";
+
+ 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 UNKNOWN:
+ return NAME_UNKNOWN;
+ default:
+ Checks::unreachable();
+ }
+ }
+
+ 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";
+
+ switch (this->backing_enum)
+ {
+ case EMPTY_PACKAGE:
+ return CMAKE_VARIABLE_EMPTY_PACKAGE;
+ case DLLS_WITHOUT_LIBS:
+ return CMAKE_VARIABLE_DLLS_WITHOUT_LIBS;
+ case UNKNOWN:
+ Checks::exit_with_message("No CMake command corresponds to UNKNOWN");
+ default:
+ Checks::unreachable();
+ }
+ }
+
+ type::type(): backing_enum(backing_enum_t::UNKNOWN) {}
+
+ const std::vector<type>& values()
+ {
+ static const std::vector<type>& v = {UNKNOWN, EMPTY_PACKAGE, DLLS_WITHOUT_LIBS};
+ return v;
+ }
+
+ 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;
+ }
+
+ return BuildPolicies::UNKNOWN;
+ }
+}
diff --git a/toolsrc/src/PostBuildLint_BuildType.cpp b/toolsrc/src/PostBuildLint_BuildType.cpp
new file mode 100644
index 000000000..b4e199aee
--- /dev/null
+++ b/toolsrc/src/PostBuildLint_BuildType.cpp
@@ -0,0 +1,68 @@
+#include "pch.h"
+#include "PostBuildLint_BuildType.h"
+#include "vcpkg_Checks.h"
+
+namespace vcpkg::PostBuildLint
+{
+ 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])");
+
+ 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();
+ }
+
+ 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);
+ }
+}
diff --git a/toolsrc/src/PostBuildLint_ConfigurationType.cpp b/toolsrc/src/PostBuildLint_ConfigurationType.cpp
new file mode 100644
index 000000000..9c3499cac
--- /dev/null
+++ b/toolsrc/src/PostBuildLint_ConfigurationType.cpp
@@ -0,0 +1,19 @@
+#include "pch.h"
+#include "PostBuildLint_ConfigurationType.h"
+#include "vcpkg_Checks.h"
+
+namespace vcpkg::PostBuildLint
+{
+ std::string to_string(const ConfigurationType& conf)
+ {
+ switch (conf)
+ {
+ case ConfigurationType::DEBUG:
+ return "Debug";
+ case ConfigurationType::RELEASE:
+ return "Release";
+ default:
+ Checks::unreachable();
+ }
+ }
+}
diff --git a/toolsrc/src/PostBuildLint_LinkageType.cpp b/toolsrc/src/PostBuildLint_LinkageType.cpp
new file mode 100644
index 000000000..8a3f35be8
--- /dev/null
+++ b/toolsrc/src/PostBuildLint_LinkageType.cpp
@@ -0,0 +1,34 @@
+#include "pch.h"
+#include "PostBuildLint_LinkageType.h"
+#include "vcpkg_Checks.h"
+
+namespace vcpkg::PostBuildLint
+{
+ 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();
+ }
+ }
+}
diff --git a/toolsrc/src/SourceParagraph.cpp b/toolsrc/src/SourceParagraph.cpp
index 374121ae9..8bfe8e84d 100644
--- a/toolsrc/src/SourceParagraph.cpp
+++ b/toolsrc/src/SourceParagraph.cpp
@@ -1,20 +1,145 @@
+#include "pch.h"
#include "SourceParagraph.h"
#include "vcpkglib_helpers.h"
+#include "vcpkg_System.h"
+#include "vcpkg_Maps.h"
+#include "triplet.h"
-using namespace vcpkg::details;
+namespace vcpkg
+{
+ //
+ namespace SourceParagraphRequiredField
+ {
+ static const std::string SOURCE = "Source";
+ static const std::string VERSION = "Version";
+ }
-vcpkg::SourceParagraph::SourceParagraph() = default;
+ namespace SourceParagraphOptionalField
+ {
+ static const std::string DESCRIPTION = "Description";
+ static const std::string MAINTAINER = "Maintainer";
+ static const std::string BUILD_DEPENDS = "Build-Depends";
+ }
-vcpkg::SourceParagraph::SourceParagraph(const std::unordered_map<std::string, std::string>& fields):
- name(required_field(fields, "Source")),
- version(required_field(fields, "Version")),
- description(optional_field(fields, "Description")),
- maintainer(optional_field(fields, "Maintainer"))
-{
- std::string deps = optional_field(fields, "Build-Depends");
- if (!deps.empty())
+ static const std::vector<std::string>& get_list_of_valid_fields()
+ {
+ static const std::vector<std::string> valid_fields =
+ {
+ SourceParagraphRequiredField::SOURCE,
+ SourceParagraphRequiredField::VERSION,
+
+ SourceParagraphOptionalField::DESCRIPTION,
+ SourceParagraphOptionalField::MAINTAINER,
+ SourceParagraphOptionalField::BUILD_DEPENDS
+ };
+
+ return valid_fields;
+ }
+
+ SourceParagraph::SourceParagraph() = default;
+
+ SourceParagraph::SourceParagraph(std::unordered_map<std::string, std::string> fields)
+ {
+ this->name = details::remove_required_field(&fields, SourceParagraphRequiredField::SOURCE);
+ this->version = details::remove_required_field(&fields, SourceParagraphRequiredField::VERSION);
+ this->description = details::remove_optional_field(&fields, SourceParagraphOptionalField::DESCRIPTION);
+ this->maintainer = details::remove_optional_field(&fields, SourceParagraphOptionalField::MAINTAINER);
+
+ std::string deps = details::remove_optional_field(&fields, SourceParagraphOptionalField::BUILD_DEPENDS);
+ this->depends = expand_qualified_dependencies(parse_depends(deps));
+
+ if (!fields.empty())
+ {
+ 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::Joiner::on("\n ").join(remaining_fields);
+ const std::string valid_fields_as_string = Strings::Joiner::on("\n ").join(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);
+ System::println("This is the list of valid fields (case-sensitive): \n\n %s\n", valid_fields_as_string);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ std::vector<dependency> vcpkg::expand_qualified_dependencies(const std::vector<std::string>& depends)
+ {
+ auto convert = [&](const std::string& depend_string) -> dependency {
+ auto pos = depend_string.find(' ');
+ if (pos == std::string::npos)
+ return{ depend_string, "" };
+ // expect of the form "\w+ \[\w+\]"
+ dependency dep;
+ dep.name = depend_string.substr(0, pos);
+ if (depend_string.c_str()[pos + 1] != '[' || depend_string[depend_string.size() - 1] != ']')
+ {
+ // Error, but for now just slurp the entire string.
+ return{ depend_string, "" };
+ }
+ dep.qualifier = depend_string.substr(pos + 2, depend_string.size() - pos - 3);
+ return dep;
+ };
+
+ std::vector<vcpkg::dependency> ret;
+
+ for (auto&& depend_string : depends)
+ {
+ ret.push_back(convert(depend_string));
+ }
+
+ return ret;
+ }
+
+ std::vector<std::string> parse_depends(const std::string& depends_string)
+ {
+ if (depends_string.empty())
+ {
+ return{};
+ }
+
+ std::vector<std::string> out;
+
+ size_t cur = 0;
+ do
+ {
+ auto pos = depends_string.find(',', cur);
+ if (pos == std::string::npos)
+ {
+ out.push_back(depends_string.substr(cur));
+ break;
+ }
+ out.push_back(depends_string.substr(cur, pos - cur));
+
+ // skip comma and space
+ ++pos;
+ if (depends_string[pos] == ' ')
+ {
+ ++pos;
+ }
+
+ cur = pos;
+ } while (cur != std::string::npos);
+
+ return out;
+ }
+
+ std::vector<std::string> filter_dependencies(const std::vector<vcpkg::dependency>& deps, const triplet& t)
+ {
+ std::vector<std::string> ret;
+ for (auto&& dep : deps)
+ {
+ if (dep.qualifier.empty() || t.canonical_name().find(dep.qualifier) != std::string::npos)
+ {
+ ret.push_back(dep.name);
+ }
+ }
+ return ret;
+ }
+
+ std::ostream & operator<<(std::ostream & os, const dependency & p)
{
- this->depends.clear();
- this->depends = parse_depends(deps);
- };
+ os << p.name;
+ return os;
+ }
}
diff --git a/toolsrc/src/StatusParagraph.cpp b/toolsrc/src/StatusParagraph.cpp
index 5aa425969..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"
@@ -5,6 +6,12 @@ using namespace vcpkg::details;
namespace vcpkg
{
+ //
+ namespace BinaryParagraphRequiredField
+ {
+ static const std::string STATUS = "Status";
+ }
+
StatusParagraph::StatusParagraph() : want(want_t::error), state(install_state_t::error)
{
}
@@ -19,7 +26,7 @@ namespace vcpkg
StatusParagraph::StatusParagraph(const std::unordered_map<std::string, std::string>& fields)
: package(fields)
{
- std::string status_field = required_field(fields, "Status");
+ std::string status_field = required_field(fields, BinaryParagraphRequiredField::STATUS);
auto b = status_field.begin();
auto mark = b;
diff --git a/toolsrc/src/StatusParagraphs.cpp b/toolsrc/src/StatusParagraphs.cpp
index 3e23c519a..c2398d2b8 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
diff --git a/toolsrc/src/Stopwatch.cpp b/toolsrc/src/Stopwatch.cpp
index 550f1ebd8..ec7af60b6 100644
--- a/toolsrc/src/Stopwatch.cpp
+++ b/toolsrc/src/Stopwatch.cpp
@@ -1,3 +1,4 @@
+#include "pch.h"
#include "Stopwatch.h"
#include "vcpkg_Checks.h"
@@ -88,7 +89,7 @@ namespace vcpkg
return Strings::format("%.4g ns", nanos_as_double);
}
- Stopwatch::Stopwatch() : m_isRunning(false), m_elapsedNanos(), m_startTick()
+ Stopwatch::Stopwatch() : m_isRunning(false), m_elapsedNanos(0), m_startTick()
{
}
diff --git a/toolsrc/src/coff_file_reader.cpp b/toolsrc/src/coff_file_reader.cpp
index 593bb18d1..f48f912c1 100644
--- a/toolsrc/src/coff_file_reader.cpp
+++ b/toolsrc/src/coff_file_reader.cpp
@@ -1,14 +1,10 @@
+#include "pch.h"
#include "coff_file_reader.h"
-#include <iostream>
-#include <cstdint>
-#include <algorithm>
#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)
@@ -34,9 +30,9 @@ namespace vcpkg {namespace COFFFileReader
return data;
}
- static void verify_equal_strings(const char* expected, const char* actual, int size)
+ static void verify_equal_strings(const char* expected, const char* actual, int size, const char* label)
{
- Checks::check_exit(memcmp(expected, actual, size) == 0, "Incorrect string found. Expected: %s but found %s", expected, actual);
+ Checks::check_exit(memcmp(expected, actual, size) == 0, "Incorrect string (%s) found. Expected: (%s) but found (%s)", label, expected, actual);
}
static void read_and_verify_PE_signature(fstream& fs)
@@ -52,17 +48,17 @@ namespace vcpkg {namespace COFFFileReader
fs.seekg(offset_to_PE_signature);
char signature[PE_SIGNATURE_SIZE];
fs.read(signature, PE_SIGNATURE_SIZE);
- verify_equal_strings(PE_SIGNATURE, signature, PE_SIGNATURE_SIZE);
+ verify_equal_strings(PE_SIGNATURE, signature, PE_SIGNATURE_SIZE, "PE_SIGNATURE");
fs.seekg(offset_to_PE_signature + PE_SIGNATURE_SIZE, ios_base::beg);
}
- static fpos_t align_to(const fpos_t unaligned_offset, const int alignment_size)
+ static fpos_t align_to_size(const uint64_t unaligned, const uint64_t alignment_size)
{
- fpos_t aligned_offset = unaligned_offset - 1;
- aligned_offset /= alignment_size;
- aligned_offset += 1;
- aligned_offset *= alignment_size;
- return aligned_offset;
+ fpos_t aligned = unaligned - 1;
+ aligned /= alignment_size;
+ aligned += 1;
+ aligned *= alignment_size;
+ return aligned;
}
struct coff_file_header
@@ -77,14 +73,6 @@ namespace vcpkg {namespace COFFFileReader
return ret;
}
- static coff_file_header peek(fstream& fs)
- {
- auto original_pos = fs.tellg().seekpos();
- coff_file_header ret = read(fs);
- fs.seekg(original_pos);
- return ret;
- }
-
MachineType machineType() const
{
static const size_t MACHINE_TYPE_OFFSET = 0;
@@ -113,8 +101,11 @@ namespace vcpkg {namespace COFFFileReader
ret.data.resize(HEADER_SIZE);
fs.read(&ret.data[0], HEADER_SIZE);
- const std::string header_end = ret.data.substr(HEADER_END_OFFSET, HEADER_END_SIZE);
- verify_equal_strings(HEADER_END, header_end.c_str(), HEADER_END_SIZE);
+ if (ret.data[0] != '\0') // Due to freeglut. github issue #223
+ {
+ const std::string header_end = ret.data.substr(HEADER_END_OFFSET, HEADER_END_SIZE);
+ verify_equal_strings(HEADER_END, header_end.c_str(), HEADER_END_SIZE, "LIB HEADER_END");
+ }
return ret;
}
@@ -128,17 +119,53 @@ namespace vcpkg {namespace COFFFileReader
uint64_t member_size() const
{
+ static const size_t ALIGNMENT_SIZE = 2;
+
static const size_t HEADER_SIZE_OFFSET = 48;
static const size_t HEADER_SIZE_FIELD_SIZE = 10;
const std::string as_string = data.substr(HEADER_SIZE_OFFSET, HEADER_SIZE_FIELD_SIZE);
// This is in ASCII decimal representation
const uint64_t value = std::strtoull(as_string.c_str(), nullptr, 10);
- return value;
+
+ const uint64_t aligned = align_to_size(value, ALIGNMENT_SIZE);
+ return aligned;
}
std::string data;
};
+ struct offsets_array
+ {
+ static offsets_array read(fstream& fs, const uint32_t offset_count)
+ {
+ static const size_t OFFSET_WIDTH = 4;
+
+ std::string raw_offsets;
+ const size_t raw_offset_size = offset_count * OFFSET_WIDTH;
+ raw_offsets.resize(raw_offset_size);
+ fs.read(&raw_offsets[0], raw_offset_size);
+
+ offsets_array ret;
+ for (uint32_t i = 0; i < offset_count; ++i)
+ {
+ const std::string value_as_string = raw_offsets.substr(OFFSET_WIDTH * i, OFFSET_WIDTH * (i + 1));
+ const uint32_t value = reinterpret_bytes<uint32_t>(value_as_string.c_str());
+
+ // Ignore offsets that point to offset 0. See vcpkg github #223 #288 #292
+ if (value != 0)
+ {
+ ret.data.push_back(value);
+ }
+ }
+
+ // Sort the offsets, because it is possible for them to be unsorted. See vcpkg github #292
+ std::sort(ret.data.begin(), ret.data.end());
+ return ret;
+ }
+
+ std::vector<uint32_t> data;
+ };
+
struct import_header
{
static const size_t HEADER_SIZE = 20;
@@ -168,14 +195,6 @@ namespace vcpkg {namespace COFFFileReader
return ret;
}
- static import_header peek(fstream& fs)
- {
- auto original_pos = fs.tellg().seekpos();
- import_header ret = read(fs);
- fs.seekg(original_pos);
- return ret;
- }
-
MachineType machineType() const
{
static const size_t MACHINE_TYPE_OFFSET = 6;
@@ -190,14 +209,6 @@ namespace vcpkg {namespace COFFFileReader
std::string data;
};
- static void skip_archive_member(fstream& fs, uint64_t member_size)
- {
- static const size_t ALIGNMENT_SIZE = 2;
-
- const fpos_t new_offset = align_to(member_size, ALIGNMENT_SIZE);
- fs.seekg(new_offset, ios_base::cur);
- }
-
static void read_and_verify_archive_file_signature(fstream& fs)
{
static const char* FILE_START = "!<arch>\n";
@@ -207,10 +218,10 @@ namespace vcpkg {namespace COFFFileReader
char file_start[FILE_START_SIZE];
fs.read(file_start, FILE_START_SIZE);
- verify_equal_strings(FILE_START, file_start, FILE_START_SIZE);
+ verify_equal_strings(FILE_START, file_start, FILE_START_SIZE, "LIB FILE_START");
}
- dll_info read_dll(const fs::path path)
+ dll_info read_dll(const fs::path& path)
{
std::fstream fs(path, std::ios::in | std::ios::binary | std::ios::ate);
Checks::check_exit(fs.is_open(), "Could not open file %s for reading", path.generic_string());
@@ -221,43 +232,76 @@ namespace vcpkg {namespace COFFFileReader
return {machine};
}
- lib_info read_lib(const fs::path path)
+ struct marker_t
+ {
+ void set_to_offset(const fpos_t position)
+ {
+ this->m_absolute_position = position;
+ }
+
+ void set_to_current_pos(fstream& fs)
+ {
+ this->m_absolute_position = fs.tellg().seekpos();
+ }
+
+ void seek_to_marker(fstream& fs) const
+ {
+ fs.seekg(this->m_absolute_position, ios_base::beg);
+ }
+
+ void advance_by(const uint64_t offset)
+ {
+ this->m_absolute_position += offset;
+ }
+
+ private:
+ fpos_t m_absolute_position = 0;
+ };
+
+ lib_info read_lib(const fs::path& path)
{
std::fstream fs(path, std::ios::in | std::ios::binary | std::ios::ate);
Checks::check_exit(fs.is_open(), "Could not open file %s for reading", path.generic_string());
read_and_verify_archive_file_signature(fs);
+ marker_t marker;
+ marker.set_to_current_pos(fs);
+
// First Linker Member
const archive_member_header first_linker_member_header = archive_member_header::read(fs);
Checks::check_exit(first_linker_member_header.name().substr(0, 2) == "/ ", "Could not find proper first linker member");
- skip_archive_member(fs, first_linker_member_header.member_size());
+ marker.advance_by(archive_member_header::HEADER_SIZE + first_linker_member_header.member_size());
+ marker.seek_to_marker(fs);
const archive_member_header second_linker_member_header = archive_member_header::read(fs);
Checks::check_exit(second_linker_member_header.name().substr(0, 2) == "/ ", "Could not find proper second linker member");
// The first 4 bytes contains the number of archive members
- const uint32_t archive_member_count = peek_value_from_stream<uint32_t>(fs);
- skip_archive_member(fs, second_linker_member_header.member_size());
+ const uint32_t archive_member_count = read_value_from_stream<uint32_t>(fs);
+ const offsets_array offsets = offsets_array::read(fs, archive_member_count);
+ marker.advance_by(archive_member_header::HEADER_SIZE + second_linker_member_header.member_size());
+ marker.seek_to_marker(fs);
bool hasLongnameMemberHeader = peek_value_from_stream<uint16_t>(fs) == 0x2F2F;
if (hasLongnameMemberHeader)
{
const archive_member_header longnames_member_header = archive_member_header::read(fs);
- skip_archive_member(fs, longnames_member_header.member_size());
+ marker.advance_by(archive_member_header::HEADER_SIZE + longnames_member_header.member_size());
+ marker.seek_to_marker(fs);
}
std::set<MachineType> machine_types;
// Next we have the obj and pseudo-object files
- for (uint32_t i = 0; i < archive_member_count; i++)
+ for (const uint32_t offset : offsets.data)
{
- const archive_member_header header = archive_member_header::read(fs);
+ marker.set_to_offset(offset + archive_member_header::HEADER_SIZE); // Skip the header, no need to read it.
+ marker.seek_to_marker(fs);
const uint16_t first_two_bytes = peek_value_from_stream<uint16_t>(fs);
const bool isImportHeader = getMachineType(first_two_bytes) == MachineType::UNKNOWN;
- const MachineType machine = isImportHeader ? import_header::peek(fs).machineType() : coff_file_header::peek(fs).machineType();
+ const MachineType machine = isImportHeader ? import_header::read(fs).machineType() : coff_file_header::read(fs).machineType();
machine_types.insert(machine);
- skip_archive_member(fs, header.member_size());
}
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..56056218b
--- /dev/null
+++ b/toolsrc/src/commands_available_commands.cpp
@@ -0,0 +1,45 @@
+#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},
+ {"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..c5a278450
--- /dev/null
+++ b/toolsrc/src/commands_build.cpp
@@ -0,0 +1,131 @@
+#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_Environment.h"
+#include "metrics.h"
+#include "vcpkg_info.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;
+ }
+
+ void build_package(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 Environment::vcvarsall_and_platform_toolset vcvarsall_bat = Environment::get_vcvarsall_bat(paths);
+ const std::wstring command = Strings::wformat(LR"("%s" %s >nul 2>&1 && cmake -DCMD=BUILD -DPORT=%s -DTARGET_TRIPLET=%s -DVCPKG_PLATFORM_TOOLSET=%s "-DCURRENT_PORT_DIR=%s/." -P "%s")",
+ vcvarsall_bat.path.native(),
+ Strings::utf8_to_utf16(target_triplet.architecture()),
+ Strings::utf8_to_utf16(source_paragraph.name),
+ Strings::utf8_to_utf16(target_triplet.canonical_name()),
+ vcvarsall_bat.platform_toolset,
+ 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-" + spec.toString(), timer.microseconds());
+
+ if (return_code != 0)
+ {
+ System::println(System::color::error, "Error: building package %s failed", spec.toString());
+ 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."
+ , spec.toString(), Info::version());
+ TrackProperty("error", "build failed");
+ TrackProperty("build_error", spec.toString());
+ 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);
+ }
+
+ 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");
+
+ // 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.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", p.spec.toString());
+ }
+ System::println("");
+ exit(EXIT_FAILURE);
+ }
+
+ Environment::ensure_utilities_on_path(paths);
+ build_package(spgh, spec, paths, paths.port_dir(spec));
+ exit(EXIT_SUCCESS);
+ }
+}
diff --git a/toolsrc/src/commands_build_external.cpp b/toolsrc/src/commands_build_external.cpp
new file mode 100644
index 000000000..5c3fa9857
--- /dev/null
+++ b/toolsrc/src/commands_build_external.cpp
@@ -0,0 +1,33 @@
+#include "pch.h"
+#include "vcpkg_Commands.h"
+#include "vcpkg_System.h"
+#include "vcpkg_Environment.h"
+#include "vcpkg_Input.h"
+#include "vcpkglib.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);
+
+ 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())
+ {
+ Commands::Build::build_package(*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]);
+ Commands::Help::print_example(Strings::format("%s zlib:x64-windows", args.command));
+ exit(EXIT_FAILURE);
+ }
+}
diff --git a/toolsrc/src/commands_cache.cpp b/toolsrc/src/commands_cache.cpp
index 0d70f0f29..fa49a647f 100644
--- a/toolsrc/src/commands_cache.cpp
+++ b/toolsrc/src/commands_cache.cpp
@@ -1,36 +1,73 @@
+#include "pch.h"
#include "vcpkg_Commands.h"
#include "vcpkg_System.h"
#include "vcpkg_Files.h"
-#include "vcpkg.h"
+#include "Paragraphs.h"
+#include "BinaryParagraph.h"
-namespace vcpkg
+namespace vcpkg::Commands::Cache
{
- void cache_command(const vcpkg_cmd_arguments& args, const vcpkg_paths& paths)
+ static std::vector<BinaryParagraph> read_all_binary_paragraphs(const vcpkg_paths& paths)
{
- args.check_exact_arg_count(0);
+ std::vector<BinaryParagraph> output;
+ for (auto it = fs::directory_iterator(paths.packages); it != fs::directory_iterator(); ++it)
+ {
+ const fs::path& path = it->path();
+
+ try
+ {
+ auto file_contents = Files::read_contents(path / "CONTROL");
+ if (auto text = file_contents.get())
+ {
+ auto pghs = Paragraphs::parse_paragraphs(*text);
+ if (pghs.size() != 1)
+ continue;
+
+ const BinaryParagraph binary_paragraph = BinaryParagraph(pghs[0]);
+ output.push_back(binary_paragraph);
+ }
+ }
+ catch (std::runtime_error const&)
+ {
+ }
+ }
- auto begin_it = fs::directory_iterator(paths.packages);
- auto end_it = fs::directory_iterator();
+ return output;
+ }
+
+ 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", Commands::Help::create_example_string("cache png"));
+ args.check_max_arg_count(1, example);
- if (begin_it == end_it)
+ const std::vector<BinaryParagraph> binary_paragraphs = read_all_binary_paragraphs(paths);
+ if (binary_paragraphs.empty())
{
System::println("No packages are cached.");
exit(EXIT_SUCCESS);
}
- for (; begin_it != end_it; ++begin_it)
+ if (args.command_arguments.size() == 0)
{
- const auto& path = begin_it->path();
-
- auto file_contents = Files::get_contents(path / "CONTROL");
- if (auto text = file_contents.get())
+ for (const BinaryParagraph& binary_paragraph : binary_paragraphs)
+ {
+ const std::string displayname = binary_paragraph.displayname();
+ System::println(displayname);
+ }
+ }
+ else
+ {
+ // At this point there is 1 argument
+ for (const BinaryParagraph& binary_paragraph : binary_paragraphs)
{
- auto pghs = parse_paragraphs(*text);
- if (pghs.size() != 1)
+ const std::string displayname = binary_paragraph.displayname();
+ if (Strings::case_insensitive_ascii_find(displayname, args.command_arguments[0]) == displayname.end())
+ {
continue;
+ }
- auto src = BinaryParagraph(pghs[0]);
- System::println(src.displayname().c_str());
+ System::println(displayname);
}
}
diff --git a/toolsrc/src/commands_contact.cpp b/toolsrc/src/commands_contact.cpp
new file mode 100644
index 000000000..2be468fb8
--- /dev/null
+++ b/toolsrc/src/commands_contact.cpp
@@ -0,0 +1,14 @@
+#include "pch.h"
+#include "vcpkg_Commands.h"
+#include "vcpkg_System.h"
+#include "vcpkg_info.h"
+
+namespace vcpkg::Commands::Contact
+{
+ void perform_and_exit(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);
+ }
+}
diff --git a/toolsrc/src/commands_create.cpp b/toolsrc/src/commands_create.cpp
index d1611eb5c..5042ab97b 100644
--- a/toolsrc/src/commands_create.cpp
+++ b/toolsrc/src/commands_create.cpp
@@ -1,16 +1,17 @@
+#include "pch.h"
#include "vcpkg_Commands.h"
#include "vcpkg_System.h"
#include "vcpkg_Environment.h"
#include "vcpkg_Files.h"
#include "vcpkg_Input.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")###");
- args.check_max_arg_count(3, example.c_str());
- args.check_min_arg_count(2, example.c_str());
+ static const std::string example = Commands::Help::create_example_string(R"###(create zlib2 http://zlib.net/zlib128.zip "zlib128-2.zip")###");
+ args.check_max_arg_count(3, example);
+ args.check_min_arg_count(2, example);
const std::string port_name = args.command_arguments.at(0);
Environment::ensure_utilities_on_path(paths);
diff --git a/toolsrc/src/commands_edit.cpp b/toolsrc/src/commands_edit.cpp
index f07a15875..dff30f7ad 100644
--- a/toolsrc/src/commands_edit.cpp
+++ b/toolsrc/src/commands_edit.cpp
@@ -1,16 +1,18 @@
+#include "pch.h"
#include "vcpkg_Commands.h"
#include "vcpkg_System.h"
#include "vcpkg_Input.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");
- args.check_exact_arg_count(1, example.c_str());
+ static const std::string example = Commands::Help::create_example_string("edit zlib");
+ args.check_exact_arg_count(1, example);
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 editor
std::wstring env_EDITOR = System::wdupenv_str(L"EDITOR");
@@ -27,7 +29,7 @@ namespace vcpkg
}
}
- std::wstring cmdLine = Strings::wformat(LR"("%s" "%s" "%s")", env_EDITOR, portpath.native(), (portpath / "portfile.cmake").native());
+ 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
new file mode 100644
index 000000000..4c0028f53
--- /dev/null
+++ b/toolsrc/src/commands_hash.cpp
@@ -0,0 +1,45 @@
+#include "pch.h"
+#include "vcpkg_Commands.h"
+#include "vcpkg_System.h"
+
+namespace vcpkg::Commands::Hash
+{
+ static void do_file_hash(fs::path const& path, std::wstring const& hashType)
+ {
+ auto cmd_line = Strings::wformat(LR"(CertUtil.exe -hashfile "%s" %s)",
+ path.c_str(), hashType.c_str());
+ auto 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));
+
+ std::string const& output = ec_data.output;
+
+ auto start = output.find_first_of("\r\n");
+ Checks::check_exit(start != std::string::npos, "Unexpected output format from command: %s", Strings::utf16_to_utf8(cmd_line));
+
+ auto end = output.find_first_of("\r\n", start + 1);
+ Checks::check_exit(end != std::string::npos, "Unexpected output format from command: %s", Strings::utf16_to_utf8(cmd_line));
+
+ auto hash = output.substr(start, end - start);
+ hash.erase(std::remove_if(hash.begin(), hash.end(), isspace), hash.end());
+ System::println(hash);
+ }
+
+ 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", 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);
+
+ if (args.command_arguments.size() == 1)
+ {
+ do_file_hash(args.command_arguments[0], L"SHA512");
+ }
+ if (args.command_arguments.size() == 2)
+ {
+ do_file_hash(args.command_arguments[0], Strings::utf8_to_utf16(args.command_arguments[1]));
+ }
+
+ exit(EXIT_SUCCESS);
+ }
+}
diff --git a/toolsrc/src/commands_help.cpp b/toolsrc/src/commands_help.cpp
index 194e809b1..6068c22fb 100644
--- a/toolsrc/src/commands_help.cpp
+++ b/toolsrc/src/commands_help.cpp
@@ -1,20 +1,70 @@
+#include "pch.h"
#include "vcpkg_Commands.h"
-#include "vcpkg.h"
#include "vcpkg_System.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.", vcpkg::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 help_command(const vcpkg_cmd_arguments& args, const vcpkg_paths& paths)
+ 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 perform_and_exit(const vcpkg_cmd_arguments& args, const vcpkg_paths& paths)
{
args.check_max_arg_count(1);
if (args.command_arguments.empty())
@@ -35,21 +85,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 vcpkg@microsoft.com with any feedback.");
- 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 9cfc53d6c..7af2c7185 100644
--- a/toolsrc/src/commands_import.cpp
+++ b/toolsrc/src/commands_import.cpp
@@ -1,25 +1,97 @@
+#include "pch.h"
#include "vcpkg_Commands.h"
-#include "vcpkg.h"
+#include "Paragraphs.h"
+#include "StatusParagraph.h"
+#include "vcpkg_Files.h"
-namespace vcpkg
+namespace vcpkg::Commands::Import
{
- void import_command(const vcpkg_cmd_arguments& args, const vcpkg_paths& paths)
+ struct Binaries
{
- static const std::string example = create_example_string(R"(import C:\path\to\CONTROLfile C:\path\to\includedir C:\path\to\projectdir)");
- args.check_exact_arg_count(3, example.c_str());
+ std::vector<fs::path> dlls;
+ std::vector<fs::path> libs;
+ };
+
+ static Binaries detect_files_in_directory_ending_with(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);
+ }
+ }
+
+ return binaries;
+ }
+
+ static void copy_files_into_directory(const std::vector<fs::path>& files, const fs::path& destination_folder)
+ {
+ fs::create_directory(destination_folder);
+
+ for (auto const& src_path : files)
+ {
+ fs::path dest_path = destination_folder / src_path.filename();
+ fs::copy(src_path, dest_path, fs::copy_options::overwrite_existing);
+ }
+ }
+
+ static void place_library_files_in(const fs::path& include_directory, const fs::path& project_directory, const fs::path& destination_path)
+ {
+ 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");
+
+ fs::path destination_include_directory = destination_path / "include";
+ fs::copy(include_directory, destination_include_directory, fs::copy_options::recursive | fs::copy_options::overwrite_existing);
+
+ copy_files_into_directory(release_binaries.dlls, destination_path / "bin");
+ copy_files_into_directory(release_binaries.libs, destination_path / "lib");
+
+ fs::create_directory(destination_path / "debug");
+ copy_files_into_directory(debug_binaries.dlls, destination_path / "debug" / "bin");
+ copy_files_into_directory(debug_binaries.libs, destination_path / "debug" / "lib");
+ }
+
+ static void do_import(const vcpkg_paths& paths, const fs::path& include_directory, const fs::path& project_directory, const BinaryParagraph& control_file_data)
+ {
+ fs::path library_destination_path = paths.package_dir(control_file_data.spec);
+ fs::create_directory(library_destination_path);
+ place_library_files_in(include_directory, project_directory, library_destination_path);
+
+ fs::path control_file_path = library_destination_path / "CONTROL";
+ std::ofstream(control_file_path) << control_file_data;
+ }
+
+ void perform_and_exit(const vcpkg_cmd_arguments& args, const vcpkg_paths& paths)
+ {
+ 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);
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 = get_paragraphs(control_file_path);
+ auto pghs = Paragraphs::get_paragraphs(control_file_path);
Checks::check_throw(pghs.size() == 1, "Invalid control file for package");
StatusParagraph spgh;
spgh.package = BinaryParagraph(pghs[0]);
auto& control_file_data = spgh.package;
- vcpkg::binary_import(paths, include_directory, project_directory, control_file_data);
+ do_import(paths, include_directory, project_directory, control_file_data);
exit(EXIT_SUCCESS);
}
}
diff --git a/toolsrc/src/commands_install.cpp b/toolsrc/src/commands_install.cpp
new file mode 100644
index 000000000..1f5a2234d
--- /dev/null
+++ b/toolsrc/src/commands_install.cpp
@@ -0,0 +1,239 @@
+#include "pch.h"
+#include "vcpkg_Commands.h"
+#include "vcpkglib.h"
+#include "vcpkg_Environment.h"
+#include "metrics.h"
+#include "vcpkg_Files.h"
+#include "vcpkg_System.h"
+#include "vcpkg_Dependencies.h"
+#include "vcpkg_Input.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());
+ }
+
+ 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::Joiner::on("\n ").join(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);
+ 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 = 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);
+ Environment::ensure_utilities_on_path(paths);
+
+ 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)
+ {
+ Commands::Build::build_package(*action.plan.source_pgh, 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.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 7e7da9e3f..000000000
--- a/toolsrc/src/commands_installation.cpp
+++ /dev/null
@@ -1,176 +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"
-
-namespace vcpkg
-{
- static void create_binary_control_file(const vcpkg_paths& paths, const fs::path& port_dir, const triplet& target_triplet)
- {
- auto pghs = get_paragraphs(port_dir / "CONTROL");
- Checks::check_exit(pghs.size() == 1, "Error: invalid control file");
- auto bpgh = BinaryParagraph(SourceParagraph(pghs[0]), target_triplet);
- const fs::path binary_control_file = paths.packages / bpgh.dir() / "CONTROL";
- std::ofstream(binary_control_file) << bpgh;
- }
-
- static void build_internal(const package_spec& spec, const vcpkg_paths& paths, const fs::path& port_dir)
- {
- const fs::path ports_cmake_script_path = paths.ports_cmake;
- auto&& target_triplet = spec.target_triplet();
- 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(spec.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), version());
- TrackProperty("error", "build failed");
- TrackProperty("build_error", to_string(spec));
- exit(EXIT_FAILURE);
- }
-
- perform_all_checks(spec, paths);
-
- create_binary_control_file(paths, port_dir, target_triplet);
-
- // const fs::path port_buildtrees_dir = paths.buildtrees / spec.name;
- // delete_directory(port_buildtrees_dir);
- }
-
- static void build_internal(const package_spec& spec, const vcpkg_paths& paths)
- {
- return build_internal(spec, paths, paths.ports / spec.name());
- }
-
- 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.c_str());
- 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.c_str());
- Input::check_triplets(specs, paths);
- std::vector<package_spec> install_plan = Dependencies::create_dependency_ordered_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]);
- for (size_t i = 1; i < install_plan.size(); ++i)
- {
- specs_string.push_back(',');
- specs_string.append(to_string(install_plan[i]));
- }
- TrackProperty("installplan", specs_string);
- Environment::ensure_utilities_on_path(paths);
-
- for (const package_spec& spec : install_plan)
- {
- if (status_db.find_installed(spec.name(), spec.target_triplet()) != status_db.end())
- {
- System::println(System::color::success, "Package %s is already installed", spec);
- continue;
- }
-
- fs::path package_path = paths.package_dir(spec);
-
- expected<std::string> file_contents = Files::get_contents(package_path / "CONTROL");
-
- try
- {
- if (file_contents.error_code())
- {
- build_internal(spec, paths);
- file_contents = Files::get_contents(package_path / "CONTROL");
- if (file_contents.error_code())
- {
- file_contents.get_or_throw();
- }
- }
-
- auto pghs = parse_paragraphs(file_contents.get_or_throw());
- Checks::check_throw(pghs.size() == 1, "multiple paragraphs in control file");
- install_package(paths, BinaryParagraph(pghs[0]), status_db);
- System::println(System::color::success, "Package %s is installed", spec);
- }
- catch (const std::exception& e)
- {
- System::println(System::color::error, "Error: Could not install package %s: %s", 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.c_str());
- 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.c_str());
- Input::check_triplet(spec.target_triplet(), paths);
- std::unordered_set<package_spec> unmet_dependencies = Dependencies::find_unmet_dependencies(paths, spec, status_db);
- 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& p : unmet_dependencies)
- {
- System::println(" %s", to_string(p));
- }
- System::println("");
- exit(EXIT_FAILURE);
- }
-
- Environment::ensure_utilities_on_path(paths);
- build_internal(spec, paths);
- 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.c_str());
-
- expected<package_spec> current_spec = package_spec::from_string(args.command_arguments[0], default_target_triplet);
- if (auto spec = 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);
- build_internal(*spec, paths, port_dir);
- exit(EXIT_SUCCESS);
- }
-
- System::println(System::color::error, "Error: %s: %s", current_spec.error_code().message(), args.command_arguments[0]);
- print_example(Strings::format("%s zlib:x64-windows", args.command).c_str());
- exit(EXIT_FAILURE);
- }
-}
diff --git a/toolsrc/src/commands_integration.cpp b/toolsrc/src/commands_integrate.cpp
index 6a11d6ec4..03c51b5a7 100644
--- a/toolsrc/src/commands_integration.cpp
+++ b/toolsrc/src/commands_integrate.cpp
@@ -1,18 +1,11 @@
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
-#include <shellapi.h>
+#include "pch.h"
#include "vcpkg_Commands.h"
-#include "vcpkg.h"
-#include <fstream>
-#include <iostream>
-#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",
@@ -175,7 +168,7 @@ namespace vcpkg
bool should_install_system = true;
if (fs::exists(system_wide_targets_file))
{
- auto system_wide_file_contents = Files::get_contents(system_wide_targets_file);
+ 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+) -->)###");
@@ -295,11 +288,11 @@ 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.c_str());
+ args.check_exact_arg_count(1, example);
if (args.command_arguments[0] == "install")
{
diff --git a/toolsrc/src/commands_list.cpp b/toolsrc/src/commands_list.cpp
index 194e4b435..34a9c2fc8 100644
--- a/toolsrc/src/commands_list.cpp
+++ b/toolsrc/src/commands_list.cpp
@@ -1,32 +1,68 @@
+#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
{
- void list_command(const vcpkg_cmd_arguments& args, const vcpkg_paths& paths)
+ static void do_print(const StatusParagraph& pgh)
{
- args.check_exact_arg_count(0);
+ System::println("%-27s %-16s %s",
+ pgh.package.displayname(),
+ pgh.package.version,
+ details::shorten_description(pgh.package.description));
+ }
+
+ 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", Commands::Help::create_example_string("list png"));
+ args.check_max_arg_count(1, example);
- std::vector<std::string> packages_output;
- for (auto&& pgh : database_load_check(paths))
+ const StatusParagraphs status_paragraphs = database_load_check(paths);
+ std::vector<StatusParagraph> installed_packages;
+ for (auto&& pgh : status_paragraphs)
{
if (pgh->state == install_state_t::not_installed && pgh->want == want_t::purge)
continue;
- packages_output.push_back(Strings::format("%-27s %-16s %s",
- pgh->package.displayname(),
- pgh->package.version,
- shorten_description(pgh->package.description)));
+ installed_packages.push_back(*pgh);
+ }
+
+ if (installed_packages.empty())
+ {
+ System::println("No packages are installed. Did you mean `search`?");
+ exit(EXIT_SUCCESS);
}
- std::sort(packages_output.begin(), packages_output.end());
- for (auto&& package : packages_output)
+
+ std::sort(installed_packages.begin(), installed_packages.end(),
+ [ ]( const StatusParagraph& lhs, const StatusParagraph& rhs ) -> bool
+ {
+ return lhs.package.displayname() < rhs.package.displayname();
+ });
+
+ if (args.command_arguments.size() == 0)
{
- System::println(package.c_str());
+ for (const StatusParagraph& status_paragraph : installed_packages)
+ {
+ do_print(status_paragraph);
+ }
}
- if (packages_output.empty())
+ else
{
- System::println("No packages are installed. Did you mean `search`?");
+ // At this point there is 1 argument
+ for (const StatusParagraph& status_paragraph : installed_packages)
+ {
+ const std::string displayname = status_paragraph.package.displayname();
+ if (Strings::case_insensitive_ascii_find(displayname, args.command_arguments[0]) == displayname.end())
+ {
+ continue;
+ }
+
+ do_print(status_paragraph);
+ }
}
+
exit(EXIT_SUCCESS);
}
}
diff --git a/toolsrc/src/commands_other.cpp b/toolsrc/src/commands_other.cpp
deleted file mode 100644
index 07549a437..000000000
--- a/toolsrc/src/commands_other.cpp
+++ /dev/null
@@ -1,100 +0,0 @@
-#include "vcpkg_Commands.h"
-#include "vcpkg_System.h"
-#include "vcpkg.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"
- "\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 char* command_and_arguments)
- {
- std::string cs = Strings::format("Example:\n"
- " vcpkg %s", command_and_arguments);
- return cs;
- }
-
- void print_example(const char* command_and_arguments)
- {
- System::println(create_example_string(command_and_arguments).c_str());
- }
-
- 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", 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},
- };
- 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}
- };
- return t;
- }
-}
diff --git a/toolsrc/src/commands_owns.cpp b/toolsrc/src/commands_owns.cpp
index b3dab2e44..6c4c20c44 100644
--- a/toolsrc/src/commands_owns.cpp
+++ b/toolsrc/src/commands_owns.cpp
@@ -1,13 +1,31 @@
+#include "pch.h"
#include "vcpkg_Commands.h"
#include "vcpkg_System.h"
-#include "vcpkg.h"
+#include "vcpkglib.h"
-namespace vcpkg
+namespace vcpkg::Commands::Owns
{
- void owns_command(const vcpkg_cmd_arguments& args, const vcpkg_paths& paths)
+ static void search_file(const vcpkg_paths& paths, const std::string& file_substr, const StatusParagraphs& status_db)
{
- static const std::string example = Strings::format("The argument should be a pattern to search for. %s", create_example_string("owns zlib.dll"));
- args.check_exact_arg_count(1, example.c_str());
+ const std::vector<StatusParagraph_and_associated_files> installed_files = get_installed_files(paths, status_db);
+ for (const StatusParagraph_and_associated_files& pgh_and_file : installed_files)
+ {
+ const StatusParagraph& pgh = pgh_and_file.pgh;
+
+ for (const std::string& file : pgh_and_file.files)
+ {
+ if (file.find(file_substr) != std::string::npos)
+ {
+ System::println("%s: %s", pgh.package.displayname(), file);
+ }
+ }
+ }
+ }
+
+ 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", Commands::Help::create_example_string("owns zlib.dll"));
+ args.check_exact_arg_count(1, example);
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
new file mode 100644
index 000000000..e75633b3c
--- /dev/null
+++ b/toolsrc/src/commands_portsdiff.cpp
@@ -0,0 +1,167 @@
+#include "pch.h"
+#include "vcpkg_Commands.h"
+#include "vcpkg_System.h"
+#include "vcpkg_Maps.h"
+#include "Paragraphs.h"
+#include "SourceParagraph.h"
+#include "vcpkg_Environment.h"
+
+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';
+ }
+ }
+
+ static void do_print_name_and_previous_version_and_current_version(const std::vector<std::string>& ports_to_print,
+ const std::map<std::string, std::string>& previous_names_and_versions,
+ const std::map<std::string, std::string>& current_names_and_versions)
+ {
+ 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&)
+ {
+ }
+ }
+
+ 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 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)",
+ 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);
+ fs::remove_all(temp_checkout_path);
+ return names_and_versions;
+ }
+
+ static void check_commit_exists(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 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 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", Commands::Help::create_example_string("portsdiff mybranchname"));
+ args.check_min_arg_count(1, example);
+ args.check_max_arg_count(2, example);
+
+ 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);
+
+ 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);
+
+ // Already sorted, so set_difference can work on std::vector too
+ std::vector<std::string> current_ports = Maps::extract_keys(current_names_and_versions);
+ std::vector<std::string> previous_ports = Maps::extract_keys(previous_names_and_versions);
+
+ std::vector<std::string> added_ports;
+ std::set_difference(
+ current_ports.cbegin(), current_ports.cend(),
+ previous_ports.cbegin(), previous_ports.cend(),
+ std::back_inserter(added_ports));
+
+ if (!added_ports.empty())
+ {
+ System::println("\nThe following %d ports were added:\n", added_ports.size());
+ do_print_name_and_version(added_ports, current_names_and_versions);
+ }
+
+ std::vector<std::string> removed_ports;
+ std::set_difference(
+ previous_ports.cbegin(), previous_ports.cend(),
+ current_ports.cbegin(), current_ports.cend(),
+ std::back_inserter(removed_ports));
+
+ if (!removed_ports.empty())
+ {
+ System::println("\nThe following %d ports were removed:\n", removed_ports.size());
+ do_print_name_and_version(removed_ports, previous_names_and_versions);
+ }
+
+ std::vector<std::string> potentially_updated_ports;
+ std::set_intersection(
+ current_ports.cbegin(), current_ports.cend(),
+ previous_ports.cbegin(), previous_ports.cend(),
+ std::back_inserter(potentially_updated_ports));
+
+ std::vector<std::string> updated_ports;
+ std::copy_if(potentially_updated_ports.cbegin(), potentially_updated_ports.cend(), std::back_inserter(updated_ports),
+ [&](const std::string& port) -> bool
+ {
+ return current_names_and_versions.at(port) != previous_names_and_versions.at(port);
+ }
+ );
+
+ if (!updated_ports.empty())
+ {
+ System::println("\nThe following %d ports were updated:\n", updated_ports.size());
+ do_print_name_and_previous_version_and_current_version(updated_ports, previous_names_and_versions, current_names_and_versions);
+ }
+
+ if (added_ports.empty() && removed_ports.empty() && updated_ports.empty())
+ {
+ System::println("There were no changes in the ports between the two commits.");
+ }
+
+ exit(EXIT_SUCCESS);
+ }
+}
diff --git a/toolsrc/src/commands_remove.cpp b/toolsrc/src/commands_remove.cpp
index 5bb9ecc96..f49104d1e 100644
--- a/toolsrc/src/commands_remove.cpp
+++ b/toolsrc/src/commands_remove.cpp
@@ -1,11 +1,18 @@
+#include "pch.h"
#include "vcpkg_Commands.h"
-#include "vcpkg.h"
+#include "vcpkglib.h"
#include "vcpkg_System.h"
#include "vcpkg_Input.h"
+#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)
{
@@ -21,28 +28,199 @@ namespace vcpkg
}
}
- void remove_command(const vcpkg_cmd_arguments& args, const vcpkg_paths& paths, const triplet& default_target_triplet)
+ static void remove_package(const vcpkg_paths& paths, const package_spec& spec, StatusParagraphs* status_db)
+ {
+ StatusParagraph& pkg = **status_db->find(spec.name(), spec.target_triplet());
+
+ pkg.want = want_t::purge;
+ pkg.state = install_state_t::half_installed;
+ write_update(paths, pkg);
+
+ std::fstream listfile(paths.listfile_path(pkg.package), std::ios_base::in | std::ios_base::binary);
+ if (listfile)
+ {
+ std::vector<fs::path> dirs_touched;
+ std::string suffix;
+ while (std::getline(listfile, suffix))
+ {
+ if (!suffix.empty() && suffix.back() == '\r')
+ suffix.pop_back();
+
+ std::error_code ec;
+
+ auto target = paths.installed / suffix;
+
+ auto status = fs::status(target, ec);
+ if (ec)
+ {
+ System::println(System::color::error, "failed: %s", ec.message());
+ continue;
+ }
+
+ if (fs::is_directory(status))
+ {
+ dirs_touched.push_back(target);
+ }
+ else if (fs::is_regular_file(status))
+ {
+ fs::remove(target, ec);
+ if (ec)
+ {
+ System::println(System::color::error, "failed: %s: %s", target.u8string(), ec.message());
+ }
+ }
+ else if (!fs::status_known(status))
+ {
+ System::println(System::color::warning, "Warning: unknown status: %s", target.u8string());
+ }
+ else
+ {
+ System::println(System::color::warning, "Warning: %s: cannot handle file type", target.u8string());
+ }
+ }
+
+ auto b = dirs_touched.rbegin();
+ auto e = dirs_touched.rend();
+ for (; b != e; ++b)
+ {
+ if (fs::directory_iterator(*b) == fs::directory_iterator())
+ {
+ std::error_code ec;
+ fs::remove(*b, ec);
+ if (ec)
+ {
+ System::println(System::color::error, "failed: %s", ec.message());
+ }
+ }
+ }
+
+ listfile.close();
+ fs::remove(paths.listfile_path(pkg.package));
+ }
+
+ pkg.state = install_state_t::not_installed;
+ write_update(paths, pkg);
+ }
+
+ static void sort_packages_by_name(std::vector<const package_spec_with_remove_plan*>* packages)
+ {
+ 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();
+ });
+ }
+
+ 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(&not_installed);
+ System::println("The following packages are not installed, so not removed:\n%s",
+ Strings::Joiner::on("\n ").prefix(" ").join(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::Joiner::on("\n").join(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 = create_example_string("remove zlib zlib:x64-windows curl boost");
- args.check_min_arg_count(1, example.c_str());
+ static const std::string example = Commands::Help::create_example_string("remove zlib zlib:x64-windows curl boost");
+ args.check_min_arg_count(1, example);
- const std::unordered_set<std::string> options = args.check_and_get_optional_command_arguments({OPTION_PURGE});
+ const std::unordered_set<std::string> options = args.check_and_get_optional_command_arguments({OPTION_PURGE, OPTION_RECURSE});
auto status_db = database_load_check(paths);
- std::vector<package_spec> specs = Input::check_and_get_package_specs(args.command_arguments, default_target_triplet, example.c_str());
+ 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 bool alsoRemoveFolderFromPackages = options.find(OPTION_PURGE) != options.end();
+ const bool isRecursive = options.find(OPTION_RECURSE) != options.end();
+
+ const std::vector<package_spec_with_remove_plan> remove_plan = Dependencies::create_remove_plan(paths, specs, status_db);
+ Checks::check_exit(!remove_plan.empty(), "Remove plan cannot be empty");
- for (const package_spec& spec : specs)
+ 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();
+
+ 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 c90538e86..3a3226e4b 100644
--- a/toolsrc/src/commands_search.cpp
+++ b/toolsrc/src/commands_search.cpp
@@ -1,60 +1,74 @@
+#include "pch.h"
#include "vcpkg_Commands.h"
#include "vcpkg_System.h"
-#include "vcpkg.h"
-#include <iostream>
-#include <iomanip>
+#include "Paragraphs.h"
+#include "vcpkglib_helpers.h"
+#include "SourceParagraph.h"
-namespace fs = std::tr2::sys;
-
-namespace vcpkg
+namespace vcpkg::Commands::Search
{
- template <class Pred>
- static void do_print(const vcpkg_paths& paths, Pred predicate)
+ static std::vector<SourceParagraph> read_all_source_paragraphs(const vcpkg_paths& paths)
{
+ std::vector<SourceParagraph> output;
for (auto it = fs::directory_iterator(paths.ports); it != fs::directory_iterator(); ++it)
{
const fs::path& path = it->path();
try
{
- auto pghs = get_paragraphs(path / "CONTROL");
+ auto pghs = Paragraphs::get_paragraphs(path / "CONTROL");
if (pghs.empty())
- continue;
- auto srcpgh = SourceParagraph(pghs[0]);
-
- if (predicate(srcpgh.name))
{
- std::cout << std::left
- << std::setw(20) << srcpgh.name << ' '
- << std::setw(16) << srcpgh.version << ' '
- << shorten_description(srcpgh.description) << '\n';
+ continue;
}
+
+ auto srcpgh = SourceParagraph(pghs[0]);
+ output.push_back(srcpgh);
}
catch (std::runtime_error const&)
{
}
}
+
+ return output;
+ }
+
+ static void do_print(const SourceParagraph& source_paragraph)
+ {
+ System::println("%-20s %-16s %s",
+ source_paragraph.name,
+ source_paragraph.version,
+ 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"));
- args.check_max_arg_count(1, example.c_str());
+ 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::vector<SourceParagraph> source_paragraphs = read_all_source_paragraphs(paths);
if (args.command_arguments.size() == 0)
{
- do_print(paths, [](std::string&) -> bool
- {
- return true;
- });
- exit(EXIT_SUCCESS);
+ for (const SourceParagraph& source_paragraph : source_paragraphs)
+ {
+ do_print(source_paragraph);
+ }
}
+ else
+ {
+ // At this point there is 1 argument
+ for (const SourceParagraph& source_paragraph : source_paragraphs)
+ {
+ if (Strings::case_insensitive_ascii_find(source_paragraph.name, args.command_arguments[0]) == source_paragraph.name.end())
+ {
+ continue;
+ }
- // At this point there is 1 argument
- do_print(paths, [&](std::string& port_name) -> bool
- {
- return Strings::case_insensitive_ascii_find(port_name, args.command_arguments[0]) != port_name.end();
- });
+ do_print(source_paragraph);
+ }
+ }
System::println("\nIf your library is not listed, please open an issue at:\n"
" https://github.com/Microsoft/vcpkg/issues");
diff --git a/toolsrc/src/commands_update.cpp b/toolsrc/src/commands_update.cpp
index 5d531ef39..a42ae5341 100644
--- a/toolsrc/src/commands_update.cpp
+++ b/toolsrc/src/commands_update.cpp
@@ -1,11 +1,14 @@
+#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);
System::println("Using local portfile versions. To update the local portfiles, use `git pull`.");
@@ -21,7 +24,7 @@ namespace vcpkg
const auto& path = begin_it->path();
try
{
- auto pghs = get_paragraphs(path / "CONTROL");
+ auto pghs = Paragraphs::get_paragraphs(path / "CONTROL");
if (pghs.empty())
continue;
auto srcpgh = SourceParagraph(pghs[0]);
@@ -35,7 +38,7 @@ namespace vcpkg
std::string packages_list;
std::vector<std::string> packages_output;
- for (auto&& pgh : database_load_check(paths))
+ for (auto&& pgh : status_db)
{
if (pgh->state == install_state_t::not_installed && pgh->want == want_t::purge)
continue;
@@ -69,20 +72,20 @@ namespace vcpkg
System::println("\nTo update these packages, run\n vcpkg remove --purge <pkgs>...\n vcpkg install <pkgs>...");
}
- auto version_file = Files::get_contents(paths.root / "toolsrc" / "VERSION.txt");
+ auto version_file = Files::read_contents(paths.root / "toolsrc" / "VERSION.txt");
if (auto version_contents = version_file.get())
{
int maj1, min1, rev1;
auto num1 = sscanf_s(version_contents->c_str(), "\"%d.%d.%d\"", &maj1, &min1, &rev1);
int maj2, min2, rev2;
- auto num2 = sscanf_s(version().c_str(), "%d.%d.%d-", &maj2, &min2, &rev2);
+ auto num2 = sscanf_s(Info::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..a521b2567
--- /dev/null
+++ b/toolsrc/src/commands_version.cpp
@@ -0,0 +1,17 @@
+#include "pch.h"
+#include "vcpkg_Commands.h"
+#include "vcpkg_System.h"
+#include "vcpkg_info.h"
+
+namespace vcpkg::Commands::Version
+{
+ void perform_and_exit(const vcpkg_cmd_arguments& args)
+ {
+ 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);
+ }
+}
diff --git a/toolsrc/src/lib.cpp b/toolsrc/src/lib.cpp
deleted file mode 100644
index 45b73ee07..000000000
--- a/toolsrc/src/lib.cpp
+++ /dev/null
@@ -1,511 +0,0 @@
-#include "vcpkg.h"
-#include <iostream>
-#include <iomanip>
-#include <fstream>
-#include <functional>
-#include <string>
-#include <unordered_map>
-#include <memory>
-#include <filesystem>
-#include <vector>
-#include <cassert>
-#include "vcpkg_Files.h"
-#include "vcpkg_System.h"
-
-using namespace vcpkg;
-
-bool vcpkg::g_do_dry_run = false;
-
-namespace
-{
- template <class M, class K, class V>
- auto find_or_default(const M& map, const K& key, const V& val)
- {
- auto it = map.find(key);
- if (it == map.end())
- return decltype(it->second)(val);
- else
- return it->second;
- }
-}
-
-namespace
-{
- std::fstream open_status_file(const vcpkg_paths& paths, std::ios_base::openmode mode = std::ios_base::app | std::ios_base::in | std::ios_base::out | std::ios_base::binary)
- {
- return std::fstream(paths.vcpkg_dir_status_file, mode);
- }
-}
-
-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::get_contents(vcpkg_dir_status_file).get_or_throw();
- auto pghs = 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 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);
-
- 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::get_contents(b->path()).get_or_throw();
- auto pghs = 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;
-}
-
-static fs::path listfile_path(const vcpkg_paths& paths, const BinaryParagraph& pgh)
-{
- return paths.vcpkg_dir_info / (pgh.fullstem() + ".list");
-}
-
-static std::string get_fullpkgname_from_listfile(const fs::path& path)
-{
- auto ret = path.stem().generic_u8string();
- std::replace(ret.begin(), ret.end(), '_', ':');
- return ret;
-}
-
-static 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 install_and_write_listfile(const vcpkg_paths& paths, const BinaryParagraph& bpgh)
-{
- std::fstream listfile(listfile_path(paths, bpgh), std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
-
- auto package_prefix_path = paths.package_dir(bpgh.spec);
- auto 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);
- listfile << target_triplet << "\n";
-
- for (auto it = fs::recursive_directory_iterator(package_prefix_path); it != fs::recursive_directory_iterator(); ++it)
- {
- const auto& filename = it->path().filename();
- if (fs::is_regular_file(it->status()) && (filename == "CONTROL" || filename == "control"))
- {
- // Do not copy the control file
- continue;
- }
-
- auto suffix = it->path().generic_u8string().substr(prefix_length + 1);
- auto 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());
- }
-
- listfile << target_triplet << "/" << suffix << "\n";
- }
- else if (fs::is_regular_file(status))
- {
- fs::copy_file(*it, target, ec);
- if (ec)
- {
- System::println(System::color::error, "failed: %s: %s", target.u8string(), ec.message());
- }
- listfile << target_triplet << "/" << suffix << "\n";
- }
- else if (!fs::status_known(status))
- {
- System::println(System::color::error, "failed: %s: unknown status", it->path().u8string());
- }
- else
- System::println(System::color::error, "failed: %s: cannot handle file type", it->path().u8string());
- }
-
- listfile.close();
-}
-
-// TODO: Refactoring between this function and install_package
-std::vector<std::string> vcpkg::get_unmet_package_dependencies(const vcpkg_paths& paths, const package_spec& spec, const StatusParagraphs& status_db)
-{
- std::vector<std::unordered_map<std::string, std::string>> pghs;
- {
- const fs::path packages_dir_control_file_path = paths.package_dir(spec) / "CONTROL";
-
- auto control_contents_maybe = Files::get_contents(packages_dir_control_file_path);
- if (auto control_contents = control_contents_maybe.get())
- {
- try
- {
- pghs = parse_paragraphs(*control_contents);
- }
- catch (std::runtime_error)
- {
- }
- Checks::check_exit(pghs.size() == 1, "Invalid control file at %s", packages_dir_control_file_path.string());
- return BinaryParagraph(pghs[0]).depends;
- }
- }
-
- const fs::path ports_dir_control_file_path = paths.port_dir(spec) / "CONTROL";
- auto control_contents_maybe = Files::get_contents(ports_dir_control_file_path);
- if (auto control_contents = control_contents_maybe.get())
- {
- try
- {
- pghs = parse_paragraphs(*control_contents);
- }
- catch (std::runtime_error)
- {
- }
- Checks::check_exit(pghs.size() == 1, "Invalid control file at %s", ports_dir_control_file_path.string());
- return SourceParagraph(pghs[0]).depends;
- }
-
- Checks::exit_with_message("Could not find package named %s", spec);
-}
-
-void vcpkg::install_package(const vcpkg_paths& paths, const BinaryParagraph& binary_paragraph, StatusParagraphs& status_db)
-{
- StatusParagraph spgh;
- spgh.package = binary_paragraph;
- spgh.want = want_t::install;
- spgh.state = install_state_t::half_installed;
- for (const std::string& dependency : spgh.package.depends)
- {
- if (status_db.find_installed(dependency, spgh.package.spec.target_triplet()) == status_db.end())
- {
- std::abort();
- }
- }
- 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));
-}
-
-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;
-}
-
-void vcpkg::deinstall_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();
- }
-
- pkg.want = want_t::purge;
- pkg.state = install_state_t::half_installed;
- write_update(paths, pkg);
-
- std::fstream listfile(listfile_path(paths, pkg.package), std::ios_base::in | std::ios_base::binary);
- if (listfile)
- {
- std::vector<fs::path> dirs_touched;
- std::string suffix;
- while (std::getline(listfile, suffix))
- {
- if (!suffix.empty() && suffix.back() == '\r')
- suffix.pop_back();
-
- std::error_code ec;
-
- auto target = paths.installed / suffix;
-
- auto status = fs::status(target, ec);
- if (ec)
- {
- System::println(System::color::error, "failed: %s", ec.message());
- continue;
- }
-
- if (fs::is_directory(status))
- {
- dirs_touched.push_back(target);
- }
- else if (fs::is_regular_file(status))
- {
- fs::remove(target, ec);
- if (ec)
- {
- System::println(System::color::error, "failed: %s: %s", target.u8string(), ec.message());
- }
- }
- else if (!fs::status_known(status))
- {
- System::println(System::color::warning, "Warning: unknown status: %s", target.u8string());
- }
- else
- {
- System::println(System::color::warning, "Warning: %s: cannot handle file type", target.u8string());
- }
- }
-
- auto b = dirs_touched.rbegin();
- auto e = dirs_touched.rend();
- for (; b != e; ++b)
- {
- if (fs::directory_iterator(*b) == fs::directory_iterator())
- {
- std::error_code ec;
- fs::remove(*b, ec);
- if (ec)
- {
- System::println(System::color::error, "failed: %s", ec.message());
- }
- }
- }
-
- listfile.close();
- fs::remove(listfile_path(paths, pkg.package));
- }
-
- 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 vcpkg::search_file(const vcpkg_paths& paths, const std::string& file_substr, const StatusParagraphs& status_db)
-{
- std::string line;
-
- for (auto&& pgh : status_db)
- {
- if (pgh->state != install_state_t::installed)
- continue;
-
- std::fstream listfile(listfile_path(paths, pgh->package));
- while (std::getline(listfile, line))
- {
- if (line.empty())
- {
- continue;
- }
-
- if (line.find(file_substr) != std::string::npos)
- {
- System::println("%s: %s", pgh->package.displayname(), line);
- }
- }
- }
-}
-
-namespace
-{
- struct Binaries
- {
- std::vector<fs::path> dlls;
- std::vector<fs::path> libs;
- };
-
- Binaries detect_files_in_directory_ending_with(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);
- }
- }
-
- return binaries;
- }
-
- void copy_files_into_directory(const std::vector<fs::path>& files, const fs::path& destination_folder)
- {
- fs::create_directory(destination_folder);
-
- for (auto const& src_path : files)
- {
- fs::path dest_path = destination_folder / src_path.filename();
- fs::copy(src_path, dest_path, fs::copy_options::overwrite_existing);
- }
- }
-
- void place_library_files_in(const fs::path& include_directory, const fs::path& project_directory, const fs::path& destination_path)
- {
- 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");
-
- fs::path destination_include_directory = destination_path / "include";
- fs::copy(include_directory, destination_include_directory, fs::copy_options::recursive | fs::copy_options::overwrite_existing);
-
- copy_files_into_directory(release_binaries.dlls, destination_path / "bin");
- copy_files_into_directory(release_binaries.libs, destination_path / "lib");
-
- fs::create_directory(destination_path / "debug");
- copy_files_into_directory(debug_binaries.dlls, destination_path / "debug" / "bin");
- copy_files_into_directory(debug_binaries.libs, destination_path / "debug" / "lib");
- }
-}
-
-void vcpkg::binary_import(const vcpkg_paths& paths, const fs::path& include_directory, const fs::path& project_directory, const BinaryParagraph& control_file_data)
-{
- fs::path library_destination_path = paths.package_dir(control_file_data.spec);
- fs::create_directory(library_destination_path);
- place_library_files_in(include_directory, project_directory, library_destination_path);
-
- fs::path control_file_path = library_destination_path / "CONTROL";
- std::ofstream(control_file_path) << control_file_data;
-}
diff --git a/toolsrc/src/main.cpp b/toolsrc/src/main.cpp
deleted file mode 100644
index 2200cd105..000000000
--- a/toolsrc/src/main.cpp
+++ /dev/null
@@ -1,246 +0,0 @@
-#define WIN32_LEAN_AND_MEAN
-#include <Windows.h>
-
-#include <iostream>
-#include <fstream>
-#include <memory>
-#include <cassert>
-#include "vcpkg.h"
-#include "vcpkg_Commands.h"
-#include "metrics.h"
-#include <Shlobj.h>
-#include "vcpkg_Files.h"
-#include "vcpkg_System.h"
-#include "vcpkg_Input.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::get_contents(localappdata / "vcpkg" / "config").get_or_throw();
-
- std::unordered_map<std::string, std::string> keys;
- auto pghs = 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", version());
-
- const std::string trimmed_command_line = trim_path_from_command_line(Strings::utf16_to_utf8(GetCommandLineW()));
- TrackProperty("cmdline", trimmed_command_line);
- loadConfig();
-
- 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"
- << " vcpkg@microsoft.com\n"
- << "containing a brief summary of what you were trying to do and the following data blob:\n"
- << "\n"
- << "Version=" << 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 ada065fd6..263d6eb74 100644
--- a/toolsrc/src/metrics.cpp
+++ b/toolsrc/src/metrics.cpp
@@ -1,21 +1,9 @@
+#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>
+#include "filesystem_fs.h"
#include "vcpkg_Strings.h"
#include "vcpkg_System.h"
-namespace fs = std::tr2::sys;
-
namespace vcpkg
{
static std::string GetCurrentDateTime()
@@ -237,6 +225,40 @@ true
return DISABLE_METRICS == 0;
}
+ std::wstring GetSQMUser()
+ {
+ LONG err;
+
+ struct RAII_HKEY {
+ HKEY hkey = nullptr;
+ ~RAII_HKEY()
+ {
+ if (hkey != nullptr)
+ RegCloseKey(hkey);
+ }
+ } HKCU_SQMClient;
+
+ err = RegOpenKeyExW(HKEY_CURRENT_USER, LR"(Software\Microsoft\SQMClient)", NULL, KEY_READ, &HKCU_SQMClient.hkey);
+ if (err != ERROR_SUCCESS)
+ {
+ 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", nullptr, &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)
{
g_metricmessage.user_id = user_id;
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/post_build_lint.cpp b/toolsrc/src/post_build_lint.cpp
deleted file mode 100644
index c68148fb2..000000000
--- a/toolsrc/src/post_build_lint.cpp
+++ /dev/null
@@ -1,433 +0,0 @@
-#include <filesystem>
-#include "vcpkg_paths.h"
-#include "package_spec.h"
-#include <iterator>
-#include <functional>
-#include "vcpkg_System.h"
-#include "coff_file_reader.h"
-
-namespace fs = std::tr2::sys;
-
-namespace vcpkg
-{
- enum class lint_status
- {
- SUCCESS = 0,
- ERROR_DETECTED = 1
- };
-
- static const fs::path DUMPBIN_EXE = R"(%VS140COMNTOOLS%\..\..\VC\bin\dumpbin.exe)";
-
- namespace
- {
- void print_vector_of_files(const std::vector<fs::path>& paths)
- {
- System::println("");
- for (const fs::path& p : paths)
- {
- System::println(" %s", p.generic_string());
- }
- System::println("");
- }
-
- template <class Pred>
- void recursive_find_matching_paths_in_dir(const fs::path& dir, const Pred predicate, std::vector<fs::path>& output)
- {
- std::copy_if(fs::recursive_directory_iterator(dir), fs::recursive_directory_iterator(), std::back_inserter(output), predicate);
- }
-
- void recursive_find_files_with_extension_in_dir(const fs::path& dir, const std::string& extension, std::vector<fs::path>& output)
- {
- recursive_find_matching_paths_in_dir(dir, [&extension](const fs::path& current)
- {
- return !fs::is_directory(current) && current.extension() == extension;
- }, output);
- }
- }
-
- static lint_status check_for_files_in_include_directory(const package_spec& spec, const vcpkg_paths& paths)
- {
- const fs::path include_dir = paths.packages / spec.dir() / "include";
- if (!fs::exists(include_dir) || fs::is_empty(include_dir))
- {
- System::println(System::color::warning, "The folder /include is empty. This indicates the library was not correctly installed.");
- return lint_status::ERROR_DETECTED;
- }
-
- return lint_status::SUCCESS;
- }
-
- static lint_status check_for_files_in_debug_include_directory(const package_spec& spec, const vcpkg_paths& paths)
- {
- const fs::path debug_include_dir = paths.packages / spec.dir() / "debug" / "include";
- std::vector<fs::path> files_found;
-
- recursive_find_matching_paths_in_dir(debug_include_dir, [&](const fs::path& current)
- {
- return !fs::is_directory(current) && current.extension() != ".ifc";
- }, files_found);
-
- if (!files_found.empty())
- {
- System::println(System::color::warning, "Include files should not be duplicated into the /debug/include directory. If this cannot be disabled in the project cmake, use\n"
- " file(REMOVE_RECURSE ${CURRENT_PACKAGES_DIR}/debug/include)"
- );
- return lint_status::ERROR_DETECTED;
- }
-
- return lint_status::SUCCESS;
- }
-
- static lint_status check_for_files_in_debug_share_directory(const package_spec& spec, const vcpkg_paths& paths)
- {
- const fs::path debug_share = paths.packages / spec.dir() / "debug" / "share";
-
- if (fs::exists(debug_share) && !fs::is_empty(debug_share))
- {
- System::println(System::color::warning, "No files should be present in /debug/share");
- return lint_status::ERROR_DETECTED;
- }
-
- return lint_status::SUCCESS;
- }
-
- static lint_status check_folder_lib_cmake(const package_spec& spec, const vcpkg_paths& paths)
- {
- const fs::path lib_cmake = paths.packages / spec.dir() / "lib" / "cmake";
- if (fs::exists(lib_cmake))
- {
- System::println(System::color::warning, "The /lib/cmake folder should be moved to just /cmake");
- return lint_status::ERROR_DETECTED;
- }
-
- return lint_status::SUCCESS;
- }
-
- static lint_status check_for_misplaced_cmake_files(const package_spec& spec, const vcpkg_paths& paths)
- {
- const fs::path current_packages_dir = paths.packages / spec.dir();
- std::vector<fs::path> misplaced_cmake_files;
- recursive_find_files_with_extension_in_dir(current_packages_dir / "cmake", ".cmake", misplaced_cmake_files);
- recursive_find_files_with_extension_in_dir(current_packages_dir / "debug" / "cmake", ".cmake", misplaced_cmake_files);
- recursive_find_files_with_extension_in_dir(current_packages_dir / "lib" / "cmake", ".cmake", misplaced_cmake_files);
- recursive_find_files_with_extension_in_dir(current_packages_dir / "debug" / "lib" / "cmake", ".cmake", misplaced_cmake_files);
-
- if (!misplaced_cmake_files.empty())
- {
- System::println(System::color::warning, "The following cmake files were found outside /share/%s. Please place cmake files in /share/%s.", spec.name(), spec.name());
- print_vector_of_files(misplaced_cmake_files);
- return lint_status::ERROR_DETECTED;
- }
-
- return lint_status::SUCCESS;
- }
-
- static lint_status check_folder_debug_lib_cmake(const package_spec& spec, const vcpkg_paths& paths)
- {
- const fs::path lib_cmake_debug = paths.packages / spec.dir() / "debug" / "lib" / "cmake";
- if (fs::exists(lib_cmake_debug))
- {
- System::println(System::color::warning, "The /debug/lib/cmake folder should be moved to just /debug/cmake");
- return lint_status::ERROR_DETECTED;
- }
-
- return lint_status::SUCCESS;
- }
-
- static lint_status check_for_dlls_in_lib_dirs(const package_spec& spec, const vcpkg_paths& paths)
- {
- std::vector<fs::path> dlls;
- recursive_find_files_with_extension_in_dir(paths.packages / spec.dir() / "lib", ".dll", dlls);
- recursive_find_files_with_extension_in_dir(paths.packages / spec.dir() / "debug" / "lib", ".dll", dlls);
-
- if (!dlls.empty())
- {
- System::println(System::color::warning, "\nThe following dlls were found in /lib and /debug/lib. Please move them to /bin or /debug/bin, respectively.");
- print_vector_of_files(dlls);
- return lint_status::ERROR_DETECTED;
- }
-
- return lint_status::SUCCESS;
- }
-
- static lint_status check_for_copyright_file(const package_spec& spec, const vcpkg_paths& paths)
- {
- const fs::path copyright_file = paths.packages / spec.dir() / "share" / spec.name() / "copyright";
- if (fs::exists(copyright_file))
- {
- return lint_status::SUCCESS;
- }
- const fs::path current_buildtrees_dir = paths.buildtrees / spec.name();
- const fs::path current_buildtrees_dir_src = current_buildtrees_dir / "src";
-
- std::vector<fs::path> potential_copyright_files;
- // Only searching one level deep
- for (auto it = fs::recursive_directory_iterator(current_buildtrees_dir_src); it != fs::recursive_directory_iterator(); ++it)
- {
- if (it.depth() > 1)
- {
- continue;
- }
-
- const std::string filename = it->path().filename().string();
- if (filename == "LICENSE" || filename == "LICENSE.txt" || filename == "COPYING")
- {
- potential_copyright_files.push_back(it->path());
- }
- }
-
- System::println(System::color::warning, "The software license must be available at ${CURRENT_PACKAGES_DIR}/share/%s/copyright .", spec.name());
- if (potential_copyright_files.size() == 1) // if there is only one candidate, provide the cmake lines needed to place it in the proper location
- {
- const fs::path found_file = potential_copyright_files[0];
- const fs::path relative_path = found_file.string().erase(0, current_buildtrees_dir.string().size() + 1); // The +1 is needed to remove the "/"
- System::println("\n file(COPY ${CURRENT_BUILDTREES_DIR}/%s DESTINATION ${CURRENT_PACKAGES_DIR}/share/%s)\n"
- " file(RENAME ${CURRENT_PACKAGES_DIR}/share/%s/%s ${CURRENT_PACKAGES_DIR}/share/%s/copyright)",
- relative_path.generic_string(), spec.name(), spec.name(), found_file.filename().generic_string(), spec.name());
- return lint_status::ERROR_DETECTED;
- }
-
- if (potential_copyright_files.size() > 1)
- {
- System::println(System::color::warning, "The following files are potential copyright files:");
- print_vector_of_files(potential_copyright_files);
- }
-
- const fs::path current_packages_dir = paths.packages / spec.dir();
- System::println(" %s/share/%s/copyright", current_packages_dir.generic_string(), spec.name());
-
- return lint_status::ERROR_DETECTED;
- }
-
- static lint_status check_for_exes(const package_spec& spec, const vcpkg_paths& paths)
- {
- std::vector<fs::path> exes;
- recursive_find_files_with_extension_in_dir(paths.packages / spec.dir() / "bin", ".exe", exes);
- recursive_find_files_with_extension_in_dir(paths.packages / spec.dir() / "debug" / "bin", ".exe", exes);
-
- if (!exes.empty())
- {
- System::println(System::color::warning, "The following EXEs were found in /bin and /debug/bin. EXEs are not valid distribution targets.");
- print_vector_of_files(exes);
- return lint_status::ERROR_DETECTED;
- }
-
- return lint_status::SUCCESS;
- }
-
- static lint_status check_exports_of_dlls(const std::vector<fs::path>& dlls)
- {
- 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());
- 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));
-
- if (ec_data.output.find("ordinal hint RVA name") == std::string::npos)
- {
- dlls_with_no_exports.push_back(dll);
- }
- }
-
- if (!dlls_with_no_exports.empty())
- {
- System::println(System::color::warning, "The following DLLs have no exports:");
- print_vector_of_files(dlls_with_no_exports);
- System::println(System::color::warning, "DLLs without any exports are likely a bug in the build script.");
- return lint_status::ERROR_DETECTED;
- }
-
- return lint_status::SUCCESS;
- }
-
- static lint_status check_uwp_bit_of_dlls(const std::string& expected_system_name, const std::vector<fs::path>& dlls)
- {
- if (expected_system_name != "uwp")
- {
- return lint_status::SUCCESS;
- }
-
- 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());
- 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));
-
- if (ec_data.output.find("App Container") == std::string::npos)
- {
- dlls_with_improper_uwp_bit.push_back(dll);
- }
- }
-
- if (!dlls_with_improper_uwp_bit.empty())
- {
- System::println(System::color::warning, "The following DLLs do not have the App Container bit set:");
- print_vector_of_files(dlls_with_improper_uwp_bit);
- System::println(System::color::warning, "This bit is required for Windows Store apps.");
- return lint_status::ERROR_DETECTED;
- }
-
- return lint_status::SUCCESS;
- }
-
- struct file_and_arch
- {
- fs::path file;
- std::string actual_arch;
- };
-
- static std::string get_actual_architecture(const MachineType& machine_type)
- {
- switch (machine_type)
- {
- case MachineType::AMD64:
- case MachineType::IA64:
- return "x64";
- case MachineType::I386:
- return "x86";
- case MachineType::ARM:
- case MachineType::ARMNT:
- return "arm";
- default:
- return "Machine Type Code = " + std::to_string(static_cast<uint16_t>(machine_type));
- }
- }
-
- static void print_invalid_architecture_files(const std::string& expected_architecture, std::vector<file_and_arch> binaries_with_invalid_architecture)
- {
- System::println(System::color::warning, "The following files were built for an incorrect architecture:");
- System::println("");
- for (const file_and_arch& b : binaries_with_invalid_architecture)
- {
- System::println(" %s", b.file.generic_string());
- System::println("Expected %s, but was: %s", expected_architecture, b.actual_arch);
- System::println("");
- }
- }
-
- static lint_status check_dll_architecture(const std::string& expected_architecture, const std::vector<fs::path>& files)
- {
- std::vector<file_and_arch> binaries_with_invalid_architecture;
-
- for (const fs::path& file : files)
- {
- Checks::check_exit(file.extension() == ".dll", "The file extension was not .dll: %s", file.generic_string());
- COFFFileReader::dll_info info = COFFFileReader::read_dll(file);
- const std::string actual_architecture = get_actual_architecture(info.machine_type);
-
- if (expected_architecture != actual_architecture)
- {
- binaries_with_invalid_architecture.push_back({file, actual_architecture});
- }
- }
-
- if (!binaries_with_invalid_architecture.empty())
- {
- print_invalid_architecture_files(expected_architecture, binaries_with_invalid_architecture);
- return lint_status::ERROR_DETECTED;
- }
-
- return lint_status::SUCCESS;
- }
-
- static lint_status check_lib_architecture(const std::string& expected_architecture, const std::vector<fs::path>& files)
- {
- std::vector<file_and_arch> binaries_with_invalid_architecture;
-
- for (const fs::path& file : files)
- {
- Checks::check_exit(file.extension() == ".lib", "The file extension was not .lib: %s", file.generic_string());
- COFFFileReader::lib_info info = COFFFileReader::read_lib(file);
- Checks::check_exit(info.machine_types.size() == 1, "Found more than 1 architecture in file %s", file.generic_string());
-
- 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});
- }
- }
-
- if (!binaries_with_invalid_architecture.empty())
- {
- print_invalid_architecture_files(expected_architecture, binaries_with_invalid_architecture);
- return lint_status::ERROR_DETECTED;
- }
-
- return lint_status::SUCCESS;
- }
-
- static lint_status check_no_dlls_present(const std::vector<fs::path>& dlls)
- {
- if (dlls.empty())
- {
- return lint_status::SUCCESS;
- }
-
- System::println(System::color::warning, "DLLs should not be present in a static build, but the following DLLs were found:");
- print_vector_of_files(dlls);
- return lint_status::ERROR_DETECTED;
- }
-
- static void operator +=(size_t& left, const lint_status& right)
- {
- left += static_cast<size_t>(right);
- }
-
- void perform_all_checks(const package_spec& spec, const vcpkg_paths& paths)
- {
- System::println("-- Performing post-build validation");
- size_t error_count = 0;
- error_count += check_for_files_in_include_directory(spec, paths);
- error_count += check_for_files_in_debug_include_directory(spec, paths);
- error_count += check_for_files_in_debug_share_directory(spec, paths);
- error_count += check_folder_lib_cmake(spec, paths);
- error_count += check_for_misplaced_cmake_files(spec, paths);
- error_count += check_folder_debug_lib_cmake(spec, paths);
- error_count += check_for_dlls_in_lib_dirs(spec, paths);
- error_count += check_for_copyright_file(spec, paths);
- error_count += check_for_exes(spec, paths);
-
- triplet::BuildType build_type = spec.target_triplet().build_type();
- switch (build_type)
- {
- case triplet::BuildType::DYNAMIC:
- {
- std::vector<fs::path> dlls;
- recursive_find_files_with_extension_in_dir(paths.packages / spec.dir() / "bin", ".dll", dlls);
- recursive_find_files_with_extension_in_dir(paths.packages / spec.dir() / "debug" / "bin", ".dll", dlls);
-
- error_count += check_exports_of_dlls(dlls);
- error_count += check_uwp_bit_of_dlls(spec.target_triplet().system(), dlls);
- error_count += check_dll_architecture(spec.target_triplet().architecture(), dlls);
- break;
- }
- case triplet::BuildType::STATIC:
- {
- std::vector<fs::path> dlls;
- recursive_find_files_with_extension_in_dir(paths.packages / spec.dir(), ".dll", dlls);
- error_count += check_no_dlls_present(dlls);
-
- break;
- }
-
- default:
- Checks::unreachable();
- }
-
- std::vector<fs::path> libs;
- recursive_find_files_with_extension_in_dir(paths.packages / spec.dir() / "lib", ".lib", libs);
- recursive_find_files_with_extension_in_dir(paths.packages / spec.dir() / "debug" / "lib", ".lib", libs);
- error_count += check_lib_architecture(spec.target_triplet().architecture(), libs);
-
- 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");
- }
-}
diff --git a/toolsrc/src/tests_dependencies.cpp b/toolsrc/src/tests_dependencies.cpp
new file mode 100644
index 000000000..bce1cab0e
--- /dev/null
+++ b/toolsrc/src/tests_dependencies.cpp
@@ -0,0 +1,39 @@
+#include "CppUnitTest.h"
+#include "SourceParagraph.h"
+#include "triplet.h"
+
+#pragma comment(lib,"version")
+#pragma comment(lib,"winhttp")
+
+using namespace Microsoft::VisualStudio::CppUnitTestFramework;
+
+using namespace vcpkg;
+
+namespace UnitTest1
+{
+ TEST_CLASS(DependencyTests)
+ {
+ public:
+ TEST_METHOD(parse_depends_one)
+ {
+ auto v = expand_qualified_dependencies(parse_depends("libA [windows]"));
+ Assert::AreEqual(size_t(1), v.size());
+ Assert::AreEqual("libA", v[0].name.c_str());
+ Assert::AreEqual("windows", v[0].qualifier.c_str());
+ }
+
+ TEST_METHOD(filter_depends)
+ {
+ auto deps = expand_qualified_dependencies(parse_depends("libA [windows], libB, libC [uwp]"));
+ auto v = filter_dependencies(deps, triplet::X64_WINDOWS);
+ Assert::AreEqual(size_t(2), v.size());
+ Assert::AreEqual("libA", v[0].c_str());
+ Assert::AreEqual("libB", v[1].c_str());
+
+ auto v2 = filter_dependencies(deps, triplet::ARM_UWP);
+ Assert::AreEqual(size_t(2), v.size());
+ Assert::AreEqual("libB", v2[0].c_str());
+ Assert::AreEqual("libC", v2[1].c_str());
+ }
+ };
+}
diff --git a/toolsrc/src/test.cpp b/toolsrc/src/tests_paragraph.cpp
index fc49b362d..fb20eee82 100644
--- a/toolsrc/src/test.cpp
+++ b/toolsrc/src/tests_paragraph.cpp
@@ -1,19 +1,20 @@
#include "CppUnitTest.h"
-#include "vcpkg.h"
+#include "Paragraphs.h"
+#include "BinaryParagraph.h"
#pragma comment(lib,"version")
#pragma comment(lib,"winhttp")
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
{
@@ -48,7 +49,7 @@ namespace UnitTest1
Assert::AreEqual("m", pgh.maintainer.c_str());
Assert::AreEqual("d", pgh.description.c_str());
Assert::AreEqual(size_t(1), pgh.depends.size());
- Assert::AreEqual("bd", pgh.depends[0].c_str());
+ Assert::AreEqual("bd", pgh.depends[0].name.c_str());
}
TEST_METHOD(SourceParagraph_Two_Depends)
@@ -60,8 +61,8 @@ namespace UnitTest1
});
Assert::AreEqual(size_t(2), pgh.depends.size());
- Assert::AreEqual("z", pgh.depends[0].c_str());
- Assert::AreEqual("openssl", pgh.depends[1].c_str());
+ Assert::AreEqual("z", pgh.depends[0].name.c_str());
+ Assert::AreEqual("openssl", pgh.depends[1].name.c_str());
}
TEST_METHOD(SourceParagraph_Three_Depends)
@@ -73,9 +74,28 @@ namespace UnitTest1
});
Assert::AreEqual(size_t(3), pgh.depends.size());
- Assert::AreEqual("z", pgh.depends[0].c_str());
- Assert::AreEqual("openssl", pgh.depends[1].c_str());
- Assert::AreEqual("xyz", pgh.depends[2].c_str());
+ Assert::AreEqual("z", pgh.depends[0].name.c_str());
+ Assert::AreEqual("openssl", pgh.depends[1].name.c_str());
+ Assert::AreEqual("xyz", pgh.depends[2].name.c_str());
+ }
+
+ TEST_METHOD(SourceParagraph_Construct_Qualified_Depends)
+ {
+ vcpkg::SourceParagraph pgh({
+ {"Source", "zlib"},
+ {"Version", "1.2.8"},
+ {"Build-Depends", "libA [windows], libB [uwp]"}
+ });
+
+ Assert::AreEqual("zlib", pgh.name.c_str());
+ Assert::AreEqual("1.2.8", pgh.version.c_str());
+ Assert::AreEqual("", pgh.maintainer.c_str());
+ Assert::AreEqual("", pgh.description.c_str());
+ Assert::AreEqual(size_t(2), pgh.depends.size());
+ Assert::AreEqual("libA", pgh.depends[0].name.c_str());
+ Assert::AreEqual("windows", pgh.depends[0].qualifier.c_str());
+ Assert::AreEqual("libB", pgh.depends[1].name.c_str());
+ Assert::AreEqual("uwp", pgh.depends[1].qualifier.c_str());
}
TEST_METHOD(BinaryParagraph_Construct_Minimum)
@@ -83,7 +103,7 @@ namespace UnitTest1
vcpkg::BinaryParagraph pgh({
{"Package", "zlib"},
{"Version", "1.2.8"},
- {"Architecture", "a"},
+ {"Architecture", "x86-windows"},
{"Multi-Arch", "same"},
});
@@ -91,7 +111,7 @@ namespace UnitTest1
Assert::AreEqual("1.2.8", pgh.version.c_str());
Assert::AreEqual("", pgh.maintainer.c_str());
Assert::AreEqual("", pgh.description.c_str());
- Assert::AreEqual("a", pgh.spec.target_triplet().canonical_name().c_str());
+ Assert::AreEqual("x86-windows", pgh.spec.target_triplet().canonical_name().c_str());
Assert::AreEqual(size_t(0), pgh.depends.size());
}
@@ -100,7 +120,7 @@ namespace UnitTest1
vcpkg::BinaryParagraph pgh({
{"Package", "s"},
{"Version", "v"},
- {"Architecture", "a"},
+ {"Architecture", "x86-windows"},
{"Multi-Arch", "same"},
{"Maintainer", "m"},
{"Description", "d"},
@@ -119,7 +139,7 @@ namespace UnitTest1
vcpkg::BinaryParagraph pgh({
{"Package", "zlib"},
{"Version", "1.2.8"},
- {"Architecture", "a"},
+ {"Architecture", "x86-windows"},
{"Multi-Arch", "same"},
{"Depends", "a, b, c"},
});
@@ -133,14 +153,14 @@ namespace UnitTest1
TEST_METHOD(parse_paragraphs_empty)
{
const char* str = "";
- auto pghs = vcpkg::parse_paragraphs(str);
+ auto pghs = vcpkg::Paragraphs::parse_paragraphs(str);
Assert::IsTrue(pghs.empty());
}
TEST_METHOD(parse_paragraphs_one_field)
{
const char* str = "f1: v1";
- auto pghs = vcpkg::parse_paragraphs(str);
+ auto pghs = vcpkg::Paragraphs::parse_paragraphs(str);
Assert::AreEqual(size_t(1), pghs.size());
Assert::AreEqual(size_t(1), pghs[0].size());
Assert::AreEqual("v1", pghs[0]["f1"].c_str());
@@ -151,7 +171,7 @@ namespace UnitTest1
const char* str =
"f1: v1\n"
"f2: v2";
- auto pghs = vcpkg::parse_paragraphs(str);
+ auto pghs = vcpkg::Paragraphs::parse_paragraphs(str);
Assert::AreEqual(size_t(1), pghs.size());
Assert::AreEqual(size_t(2), pghs[0].size());
Assert::AreEqual("v1", pghs[0]["f1"].c_str());
@@ -166,7 +186,7 @@ namespace UnitTest1
"\n"
"f3: v3\n"
"f4: v4";
- auto pghs = vcpkg::parse_paragraphs(str);
+ auto pghs = vcpkg::Paragraphs::parse_paragraphs(str);
Assert::AreEqual(size_t(2), pghs.size());
Assert::AreEqual(size_t(2), pghs[0].size());
Assert::AreEqual("v1", pghs[0]["f1"].c_str());
@@ -184,7 +204,7 @@ namespace UnitTest1
"F:\n"
"0:\n"
"F-2:\n";
- auto pghs = vcpkg::parse_paragraphs(str);
+ auto pghs = vcpkg::Paragraphs::parse_paragraphs(str);
Assert::AreEqual(size_t(1), pghs.size());
Assert::AreEqual(size_t(5), pghs[0].size());
}
@@ -198,7 +218,7 @@ namespace UnitTest1
"\n"
"f3: v3\n"
"f4: v4";
- auto pghs = vcpkg::parse_paragraphs(str);
+ auto pghs = vcpkg::Paragraphs::parse_paragraphs(str);
Assert::AreEqual(size_t(2), pghs.size());
}
@@ -207,7 +227,7 @@ namespace UnitTest1
const char* str =
"f1:\n"
"f2: ";
- auto pghs = vcpkg::parse_paragraphs(str);
+ auto pghs = vcpkg::Paragraphs::parse_paragraphs(str);
Assert::AreEqual(size_t(1), pghs.size());
Assert::AreEqual(size_t(2), pghs[0].size());
Assert::AreEqual("", pghs[0]["f1"].c_str());
@@ -223,7 +243,7 @@ namespace UnitTest1
"f2:\r\n"
" f2\r\n"
" continue\r\n";
- auto pghs = vcpkg::parse_paragraphs(str);
+ auto pghs = vcpkg::Paragraphs::parse_paragraphs(str);
Assert::AreEqual(size_t(1), pghs.size());
Assert::AreEqual("simple\n f1", pghs[0]["f1"].c_str());
Assert::AreEqual("\n f2\n continue", pghs[0]["f2"].c_str());
@@ -237,7 +257,7 @@ namespace UnitTest1
"\r\n"
"f3: v3\r\n"
"f4: v4";
- auto pghs = vcpkg::parse_paragraphs(str);
+ auto pghs = vcpkg::Paragraphs::parse_paragraphs(str);
Assert::AreEqual(size_t(2), pghs.size());
Assert::AreEqual(size_t(2), pghs[0].size());
Assert::AreEqual("v1", pghs[0]["f1"].c_str());
@@ -253,16 +273,16 @@ namespace UnitTest1
vcpkg::BinaryParagraph pgh({
{"Package", "zlib"},
{"Version", "1.2.8"},
- {"Architecture", "a"},
+ {"Architecture", "x86-windows"},
{"Multi-Arch", "same"},
});
ss << pgh;
- auto pghs = vcpkg::parse_paragraphs(ss.str());
+ auto pghs = vcpkg::Paragraphs::parse_paragraphs(ss.str());
Assert::AreEqual(size_t(1), pghs.size());
Assert::AreEqual(size_t(4), pghs[0].size());
Assert::AreEqual("zlib", pghs[0]["Package"].c_str());
Assert::AreEqual("1.2.8", pghs[0]["Version"].c_str());
- Assert::AreEqual("a", pghs[0]["Architecture"].c_str());
+ Assert::AreEqual("x86-windows", pghs[0]["Architecture"].c_str());
Assert::AreEqual("same", pghs[0]["Multi-Arch"].c_str());
}
@@ -272,19 +292,19 @@ namespace UnitTest1
vcpkg::BinaryParagraph pgh({
{"Package", "zlib"},
{"Version", "1.2.8"},
- {"Architecture", "a"},
+ {"Architecture", "x86-windows"},
{"Description", "first line\n second line"},
{"Maintainer", "abc <abc@abc.abc>"},
{"Depends", "dep"},
{"Multi-Arch", "same"},
});
ss << pgh;
- auto pghs = vcpkg::parse_paragraphs(ss.str());
+ auto pghs = vcpkg::Paragraphs::parse_paragraphs(ss.str());
Assert::AreEqual(size_t(1), pghs.size());
Assert::AreEqual(size_t(7), pghs[0].size());
Assert::AreEqual("zlib", pghs[0]["Package"].c_str());
Assert::AreEqual("1.2.8", pghs[0]["Version"].c_str());
- Assert::AreEqual("a", pghs[0]["Architecture"].c_str());
+ Assert::AreEqual("x86-windows", pghs[0]["Architecture"].c_str());
Assert::AreEqual("same", pghs[0]["Multi-Arch"].c_str());
Assert::AreEqual("first line\n second line", pghs[0]["Description"].c_str());
Assert::AreEqual("dep", pghs[0]["Depends"].c_str());
@@ -296,12 +316,12 @@ namespace UnitTest1
vcpkg::BinaryParagraph pgh({
{"Package", "zlib"},
{"Version", "1.2.8"},
- {"Architecture", "a"},
+ {"Architecture", "x86-windows"},
{"Multi-Arch", "same"},
{"Depends", "a, b, c"},
});
ss << pgh;
- auto pghs = vcpkg::parse_paragraphs(ss.str());
+ auto pghs = vcpkg::Paragraphs::parse_paragraphs(ss.str());
Assert::AreEqual(size_t(1), pghs.size());
Assert::AreEqual("a, b, c", pghs[0]["Depends"].c_str());
}
diff --git a/toolsrc/src/triplet.cpp b/toolsrc/src/triplet.cpp
index af2ca2a72..e1302d9ed 100644
--- a/toolsrc/src/triplet.cpp
+++ b/toolsrc/src/triplet.cpp
@@ -1,6 +1,6 @@
+#include "pch.h"
#include "triplet.h"
#include "vcpkg_Checks.h"
-#include <algorithm>
namespace vcpkg
{
@@ -64,14 +64,4 @@ namespace vcpkg
auto it = std::find(this->m_canonical_name.cbegin(), this->m_canonical_name.cend(), '-');
return std::string(it + 1, this->m_canonical_name.cend());
}
-
- triplet::BuildType triplet::build_type() const
- {
- if (this->m_canonical_name.find("static") != std::string::npos)
- {
- return BuildType::STATIC;
- }
-
- return BuildType::DYNAMIC;
- }
}
diff --git a/toolsrc/src/vcpkg.cpp b/toolsrc/src/vcpkg.cpp
index f705858cc..3e313c702 100644
--- a/toolsrc/src/vcpkg.cpp
+++ b/toolsrc/src/vcpkg.cpp
@@ -1,178 +1,249 @@
-#include "vcpkg.h"
-#include <regex>
+#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 "vcpkglib_helpers.h"
+#include "vcpkg_System.h"
+#include "vcpkg_Input.h"
+#include "Paragraphs.h"
+#include "vcpkg_info.h"
+#include "vcpkg_Strings.h"
-namespace
+using namespace vcpkg;
+
+bool g_debugging = false;
+
+void invalid_command(const std::string& cmd)
{
- using namespace vcpkg;
+ System::println(System::color::error, "invalid command: %s", cmd);
+ Commands::Help::print_usage();
+ exit(EXIT_FAILURE);
+}
- struct Parser
+static void inner(const vcpkg_cmd_arguments& args)
+{
+ TrackProperty("command", args.command);
+ if (args.command.empty())
{
- Parser(const char* c, const char* e) : cur(c), end(e)
- {
- }
+ Commands::Help::print_usage();
+ exit(EXIT_FAILURE);
+ }
- private:
- const char* cur;
- const char* const end;
+ if (auto command_function = Commands::find(args.command, Commands::get_available_commands_type_c()))
+ {
+ return command_function(args);
+ }
- void peek(char& ch) const
- {
- if (cur == end)
- ch = 0;
- else
- ch = *cur;
- }
+ 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");
- void next(char& ch)
+ if (!vcpkg_root_dir_env.empty())
{
- if (cur == end)
- ch = 0;
- else
- {
- ++cur;
- peek(ch);
- }
+ vcpkg_root_dir = fs::absolute(vcpkg_root_dir_env);
}
-
- void skip_spaces(char& ch)
+ else
{
- while (ch == ' ' || ch == '\t')
- next(ch);
+ vcpkg_root_dir = Files::find_file_recursively_up(fs::absolute(System::get_exe_path_of_current_process()), ".vcpkg-root");
}
+ }
- static bool is_alphanum(char ch)
- {
- return (ch >= 'A' && ch <= 'Z')
- || (ch >= 'a' && ch <= 'z')
- || (ch >= '0' && ch <= '9');
- }
+ Checks::check_exit(!vcpkg_root_dir.empty(), "Error: Could not detect vcpkg-root.");
- static bool is_lineend(char ch)
- {
- return ch == '\r' || ch == '\n' || ch == 0;
- }
+ 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");
- void get_fieldvalue(char& ch, std::string& fieldvalue)
+ if (auto command_function = Commands::find(args.command, Commands::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())
{
- fieldvalue.clear();
-
- auto beginning_of_line = cur;
- do
- {
- // scan to end of current line (it is part of the field value)
- while (!is_lineend(ch))
- next(ch);
-
- fieldvalue.append(beginning_of_line, cur);
-
- if (ch == '\r')
- next(ch);
- if (ch == '\n')
- next(ch);
-
- if (is_alphanum(ch))
- {
- // Line begins a new field.
- return;
- }
-
- beginning_of_line = cur;
-
- // Line may continue the current field with data or terminate the paragraph,
- // depending on first nonspace character.
- skip_spaces(ch);
-
- if (is_lineend(ch))
- {
- // Line was whitespace or empty.
- // This terminates the field and the paragraph.
- // We leave the blank line's whitespace consumed, because it doesn't matter.
- return;
- }
-
- // First nonspace is not a newline. This continues the current field value.
- // We forcibly convert all newlines into single '\n' for ease of text handling later on.
- fieldvalue.push_back('\n');
- }
- while (true);
+ default_target_triplet = triplet::from_canonical_name(Strings::utf16_to_utf8(vcpkg_default_triplet_env));
}
-
- void get_fieldname(char& ch, std::string& fieldname)
+ else
{
- auto begin_fieldname = cur;
- while (is_alphanum(ch) || ch == '-')
- next(ch);
- Checks::check_throw(ch == ':', "Expected ':'");
- fieldname = std::string(begin_fieldname, cur);
-
- // skip ': '
- next(ch);
- skip_spaces(ch);
+ default_target_triplet = triplet::X86_WINDOWS;
}
+ }
- void get_paragraph(char& ch, std::unordered_map<std::string, std::string>& fields)
- {
- fields.clear();
- std::string fieldname;
- std::string fieldvalue;
- do
- {
- get_fieldname(ch, fieldname);
+ Input::check_triplet(default_target_triplet, paths);
- auto it = fields.find(fieldname);
- Checks::check_throw(it == fields.end(), "Duplicate field");
+ if (auto command_function = Commands::find(args.command, Commands::get_available_commands_type_a()))
+ {
+ return command_function(args, paths, default_target_triplet);
+ }
- get_fieldvalue(ch, fieldvalue);
+ return invalid_command(args.command);
+}
- fields.emplace(fieldname, fieldvalue);
- }
- while (!is_lineend(ch));
- }
+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];
- public:
- std::vector<std::unordered_map<std::string, std::string>> get_paragraphs()
+ for (size_t x = 1; x < pghs.size(); ++x)
{
- std::vector<std::unordered_map<std::string, std::string>> paragraphs;
+ for (auto&& p : pghs[x])
+ keys.insert(p);
+ }
- char ch;
- peek(ch);
+ 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
- while (ch != 0)
- {
- if (ch == '\n' || ch == '\r' || ch == ' ' || ch == '\t')
- {
- next(ch);
- continue;
- }
+ SetUserInformation(user_id, user_time);
+ return;
+ }
+ catch (...)
+ {
+ }
- paragraphs.emplace_back();
- get_paragraph(ch, paragraphs.back());
- }
+ // 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 (...)
+ {
+ }
+}
- return paragraphs;
- }
- };
+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());
}
-namespace vcpkg
+int wmain(const int argc, const wchar_t* const* const argv)
{
- std::string shorten_description(const std::string& desc)
+ 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_t::UNSPECIFIED)
+ SetPrintMetrics(args.printmetrics == opt_bool_t::ENABLED);
+ if (args.sendmetrics != opt_bool_t::UNSPECIFIED)
+ SetSendMetrics(args.sendmetrics == opt_bool_t::ENABLED);
+
+ if (args.debug != opt_bool_t::UNSPECIFIED)
{
- auto simple_desc = std::regex_replace(desc.substr(0, 49), std::regex("\\n( |\\t)?"), "");
- if (desc.size() > 49)
- simple_desc.append("...");
- return simple_desc;
+ g_debugging = (args.debug == opt_bool_t::ENABLED);
}
- std::vector<std::unordered_map<std::string, std::string>> get_paragraphs(const fs::path& control_path)
+ if (g_debugging)
{
- return parse_paragraphs(Files::get_contents(control_path).get_or_throw());
+ inner(args);
+ exit(EXIT_FAILURE);
}
- std::vector<std::unordered_map<std::string, std::string>> parse_paragraphs(const std::string& str)
+ std::string exc_msg;
+ try
+ {
+ inner(args);
+ exit(EXIT_FAILURE);
+ }
+ catch (std::exception& e)
+ {
+ exc_msg = e.what();
+ }
+ catch (...)
{
- return Parser(str.c_str(), str.c_str() + str.size()).get_paragraphs();
+ 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 << Strings::utf16_to_utf8(argv[x]) << "|\n";
+ std::cerr
+ << "\n";
}
diff --git a/toolsrc/src/vcpkg_Checks.cpp b/toolsrc/src/vcpkg_Checks.cpp
index db6c03480..5c3fef27a 100644
--- a/toolsrc/src/vcpkg_Checks.cpp
+++ b/toolsrc/src/vcpkg_Checks.cpp
@@ -1,14 +1,17 @@
+#include "pch.h"
#include "vcpkg_Checks.h"
-
-#include <stdexcept>
#include "vcpkg_System.h"
-namespace vcpkg {namespace Checks
+namespace vcpkg::Checks
{
void unreachable()
{
System::println(System::color::error, "Error: Unreachable code was reached");
+#ifndef NDEBUG
+ std::abort();
+#else
exit(EXIT_FAILURE);
+#endif
}
void exit_with_message(const char* errorMessage)
@@ -37,4 +40,4 @@ namespace vcpkg {namespace Checks
exit_with_message(errorMessage);
}
}
-}}
+}
diff --git a/toolsrc/src/vcpkg_Dependencies.cpp b/toolsrc/src/vcpkg_Dependencies.cpp
index 54b37cd11..5bd6c3eb9 100644
--- a/toolsrc/src/vcpkg_Dependencies.cpp
+++ b/toolsrc/src/vcpkg_Dependencies.cpp
@@ -1,23 +1,111 @@
+#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.h"
-#include "vcpkg_Maps.h"
-#include "vcpkg_Sets.h"
+#include "vcpkg_Files.h"
+#include "vcpkglib.h"
-namespace vcpkg { namespace Dependencies
+namespace vcpkg::Dependencies
{
- static Graphs::Graph<package_spec> build_dependency_graph(const vcpkg_paths& paths, const std::vector<package_spec>& specs, const StatusParagraphs& status_db)
+ 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
+ Graphs::Graph<package_spec> graph;
+ graph.add_vertices(specs);
+
std::vector<package_spec> examine_stack(specs);
- std::unordered_set<package_spec> was_examined; // Examine = we have checked its immediate (non-recursive) dependencies
+ while (!examine_stack.empty())
+ {
+ const package_spec spec = examine_stack.back();
+ examine_stack.pop_back();
+
+ if (was_examined.find(spec) != was_examined.end())
+ {
+ continue;
+ }
+
+ auto process_dependencies = [&](const std::vector<std::string>& dependencies_as_string)
+ {
+ for (const std::string& dep_as_string : dependencies_as_string)
+ {
+ const package_spec current_dep = package_spec::from_name_and_triplet(dep_as_string, spec.target_triplet()).get_or_throw();
+ graph.add_edge(spec, current_dep);
+ if (was_examined.find(current_dep) == was_examined.end())
+ {
+ examine_stack.push_back(std::move(current_dep));
+ }
+ }
+ };
+
+ auto it = status_db.find(spec);
+ if (it != status_db.end() && (*it)->want == want_t::install)
+ {
+ was_examined.emplace(spec, install_plan_action{install_plan_type::ALREADY_INSTALLED, nullptr, nullptr});
+ continue;
+ }
+
+ expected<BinaryParagraph> maybe_bpgh = try_load_cached_package(paths, spec);
+ if (BinaryParagraph* bpgh = maybe_bpgh.get())
+ {
+ process_dependencies(bpgh->depends);
+ was_examined.emplace(spec, install_plan_action{install_plan_type::INSTALL, std::make_unique<BinaryParagraph>(std::move(*bpgh)), nullptr});
+ continue;
+ }
+
+ expected<SourceParagraph> maybe_spgh = try_load_port(paths, spec.name());
+ 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()));
+ was_examined.emplace(spec, install_plan_action{install_plan_type::BUILD_AND_INSTALL, nullptr, std::make_unique<SourceParagraph>(std::move(*spgh))});
+ }
+
+ std::vector<package_spec_with_install_plan> ret;
+
+ const std::vector<package_spec> pkgs = graph.find_topological_sort();
+ for (const package_spec& pkg : pkgs)
+ {
+ 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 vcpkg_paths& paths, 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(examine_stack);
+ graph.add_vertices(specs);
+ std::vector<package_spec> examine_stack(specs);
while (!examine_stack.empty())
{
const package_spec spec = examine_stack.back();
@@ -28,40 +116,41 @@ namespace vcpkg { namespace Dependencies
continue;
}
- std::vector<std::string> dependencies_as_string = get_unmet_package_dependencies(paths, spec, status_db);
+ 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::string& dep_as_string : dependencies_as_string)
+ for (const std::unique_ptr<StatusParagraph>& an_installed_package : status_db)
{
- const package_spec current_dep = package_spec::from_name_and_triplet(dep_as_string, spec.target_triplet()).get_or_throw();
- auto it = status_db.find(current_dep.name(), current_dep.target_triplet());
- if (it != status_db.end() && (*it)->want == want_t::install)
- {
+ if (an_installed_package->want != want_t::install)
+ continue;
+ if (an_installed_package->package.spec.target_triplet() != spec.target_triplet())
continue;
- }
- graph.add_edge(spec, current_dep);
- if (was_examined.find(current_dep) == was_examined.end())
+ const std::vector<std::string>& deps = an_installed_package->package.depends;
+ if (std::find(deps.begin(), deps.end(), spec.name()) == deps.end())
{
- examine_stack.push_back(std::move(current_dep));
+ continue;
}
+
+ graph.add_edge(spec, an_installed_package.get()->package.spec);
+ examine_stack.push_back(an_installed_package.get()->package.spec);
}
- was_examined.insert(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));
}
- return graph;
- }
-
- std::vector<package_spec> create_dependency_ordered_install_plan(const vcpkg_paths& paths, const std::vector<package_spec>& specs, const StatusParagraphs& status_db)
- {
- return build_dependency_graph(paths, specs, status_db).find_topological_sort();
- }
+ std::vector<package_spec_with_remove_plan> ret;
- std::unordered_set<package_spec> find_unmet_dependencies(const vcpkg_paths& paths, const package_spec& spec, const StatusParagraphs& status_db)
- {
- const Graphs::Graph<package_spec> dependency_graph = build_dependency_graph(paths, {spec}, status_db);
- std::unordered_set<package_spec> key_set = Maps::extract_key_set(dependency_graph.adjacency_list());
- key_set.erase(spec);
- return key_set;
+ 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_Environment.cpp b/toolsrc/src/vcpkg_Environment.cpp
index d98b0f220..1babdc547 100644
--- a/toolsrc/src/vcpkg_Environment.cpp
+++ b/toolsrc/src/vcpkg_Environment.cpp
@@ -1,19 +1,19 @@
-#include <regex>
-#include <array>
+#include "pch.h"
#include "vcpkg_Environment.h"
#include "vcpkg_Commands.h"
-#include "vcpkg.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 void ensure_on_path(const std::array<int, 3>& version, const wchar_t* version_check_cmd, const wchar_t* install_cmd)
+ static void ensure_on_path(const std::array<int, 3>& version, const std::wstring& version_check_cmd, const std::wstring& install_cmd)
{
System::exit_code_and_output ec_data = System::cmd_execute_and_capture_output(version_check_cmd);
if (ec_data.exit_code == 0)
@@ -45,6 +45,13 @@ namespace vcpkg {namespace Environment
}
}
+ static std::wstring create_default_install_cmd(const vcpkg_paths& paths, const std::wstring& tool_name)
+ {
+ const fs::path script = paths.scripts / "fetchDependency.ps1";
+ // TODO: switch out ExecutionPolicy Bypass with "Remove Mark Of The Web" code and restore RemoteSigned
+ return Strings::wformat(L"powershell -ExecutionPolicy Bypass %s -Dependency %s", script.native(), tool_name);
+ }
+
void ensure_git_on_path(const vcpkg_paths& paths)
{
const fs::path downloaded_git = paths.downloads / "PortableGit" / "cmd";
@@ -56,13 +63,14 @@ namespace vcpkg {namespace Environment
_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");
+ static const std::wstring version_check_cmd = L"git --version 2>&1";
+ const std::wstring install_cmd = create_default_install_cmd(paths, L"git");
+ ensure_on_path(git_version, version_check_cmd, install_cmd);
}
void ensure_cmake_on_path(const vcpkg_paths& paths)
{
- const fs::path downloaded_cmake = paths.downloads / "cmake-3.5.2-win32-x86" / "bin";
+ const fs::path downloaded_cmake = paths.downloads / "cmake-3.7.2-win32-x86" / "bin";
const std::wstring path_buf = Strings::wformat(L"%s;%s;%s;%s",
downloaded_cmake.native(),
System::wdupenv_str(L"PATH"),
@@ -70,18 +78,131 @@ namespace vcpkg {namespace Environment
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 constexpr std::array<int, 3> cmake_version = {3,7,2};
+ static const std::wstring version_check_cmd = L"cmake --version 2>&1";
+ const std::wstring install_cmd = create_default_install_cmd(paths, L"cmake");
+ ensure_on_path(cmake_version, version_check_cmd, install_cmd);
}
void ensure_nuget_on_path(const vcpkg_paths& paths)
{
- const std::wstring path_buf = Strings::wformat(L"%s;%s", paths.downloads.native(), System::wdupenv_str(L"PATH"));
+ const fs::path downloaded_nuget = paths.downloads / "nuget-3.5.0";
+ const std::wstring path_buf = Strings::wformat(L"%s;%s", downloaded_nuget.native(), System::wdupenv_str(L"PATH"));
_wputenv_s(L"PATH", path_buf.c_str());
- 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 constexpr std::array<int, 3> nuget_version = {3,3,0};
+ static const std::wstring version_check_cmd = L"nuget 2>&1";
+ const std::wstring install_cmd = create_default_install_cmd(paths, L"nuget");
+ ensure_on_path(nuget_version, version_check_cmd, install_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 = Strings::wformat(L"powershell -ExecutionPolicy Bypass %s", script.native());
+ 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 const fs::path& get_VS2015_installation_instance()
+ {
+ static const fs::path vs2015_cmntools = fs::path(System::wdupenv_str(L"VS140COMNTOOLS")).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 vs2015_path;
+ }
+
+ static fs::path find_dumpbin_exe(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 std::string& instance : vs2017_installation_instances)
+ {
+ 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)
+ {
+ const fs::path dumpbin_path = subdir / "bin" / "HostX86" / "x86" / "dumpbin.exe";
+ paths_examined.push_back(dumpbin_path);
+ if (fs::exists(dumpbin_path))
+ {
+ return dumpbin_path;
+ }
+ }
+ }
+
+ // VS2015
+ const fs::path vs2015_dumpbin_exe = get_VS2015_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 fs::path vs2015_vcvarsall_bat = get_VS2015_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 vccarsall.bat.");
+ System::println("The following paths were examined:");
+ for (const fs::path& path : paths_examined)
+ {
+ 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;
}
-}}
+}
diff --git a/toolsrc/src/vcpkg_Files.cpp b/toolsrc/src/vcpkg_Files.cpp
index 611aa7450..87700238d 100644
--- a/toolsrc/src/vcpkg_Files.cpp
+++ b/toolsrc/src/vcpkg_Files.cpp
@@ -1,25 +1,22 @@
+#include "pch.h"
#include "vcpkg_Files.h"
-#include <fstream>
-#include <filesystem>
-#include <regex>
+#include "vcpkg_System.h"
-namespace fs = std::tr2::sys;
-
-namespace vcpkg {namespace Files
+namespace vcpkg::Files
{
static const std::regex FILESYSTEM_INVALID_CHARACTERS_REGEX = std::regex(R"([\/:*?"<>|])");
void check_is_directory(const fs::path& dirpath)
{
- Checks::check_throw(fs::is_directory(dirpath), "The path %s is not a directory", dirpath.string());
+ Checks::check_exit(fs::is_directory(dirpath), "The path %s is not a directory", dirpath.string());
}
- bool has_invalid_chars_for_filesystem(const std::string s)
+ bool has_invalid_chars_for_filesystem(const std::string& s)
{
return std::regex_search(s, FILESYSTEM_INVALID_CHARACTERS_REGEX);
}
- expected<std::string> get_contents(const fs::path& file_path) noexcept
+ expected<std::string> read_contents(const fs::path& file_path) noexcept
{
std::fstream file_stream(file_path, std::ios_base::in | std::ios_base::binary);
if (file_stream.fail())
@@ -44,6 +41,35 @@ namespace vcpkg {namespace Files
return std::move(output);
}
+ expected<std::vector<std::string>> read_all_lines(const fs::path& file_path)
+ {
+ std::fstream file_stream(file_path, std::ios_base::in | std::ios_base::binary);
+ if (file_stream.fail())
+ {
+ return std::errc::no_such_file_or_directory;
+ }
+
+ std::vector<std::string> output;
+ std::string line;
+ while (std::getline(file_stream, line))
+ {
+ output.push_back(line);
+ }
+ file_stream.close();
+
+ return std::move(output);
+ }
+
+ void write_all_lines(const fs::path& file_path, const std::vector<std::string>& lines)
+ {
+ std::fstream output(file_path, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc);
+ for (const std::string& line : lines)
+ {
+ output << line << "\n";
+ }
+ output.close();
+ }
+
fs::path find_file_recursively_up(const fs::path& starting_dir, const std::string& filename)
{
fs::path current_dir = starting_dir;
@@ -58,4 +84,59 @@ namespace vcpkg {namespace Files
return current_dir;
}
-}}
+
+ void recursive_find_files_with_extension_in_dir(const fs::path& dir, const std::string& extension, std::vector<fs::path>* output)
+ {
+ recursive_find_matching_paths_in_dir(dir, [&extension](const fs::path& current)
+ {
+ return !fs::is_directory(current) && current.extension() == extension;
+ }, output);
+ }
+
+ std::vector<fs::path> recursive_find_files_with_extension_in_dir(const fs::path& dir, const std::string& extension)
+ {
+ std::vector<fs::path> v;
+ recursive_find_files_with_extension_in_dir(dir, extension, &v);
+ return v;
+ }
+
+ 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)
+ {
+ return !fs::is_directory(current);
+ }, output);
+ }
+
+ std::vector<fs::path> recursive_find_all_files_in_dir(const fs::path& dir)
+ {
+ std::vector<fs::path> v;
+ recursive_find_all_files_in_dir(dir, &v);
+ return v;
+ }
+
+ 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)
+ {
+ return !fs::is_directory(current);
+ }, output);
+ }
+
+ std::vector<fs::path> non_recursive_find_all_files_in_dir(const fs::path& dir)
+ {
+ std::vector<fs::path> v;
+ non_recursive_find_all_files_in_dir(dir, &v);
+ return v;
+ }
+
+ void print_paths(const std::vector<fs::path>& paths)
+ {
+ System::println("");
+ for (const fs::path& p : paths)
+ {
+ System::println(" %s", p.generic_string());
+ }
+ System::println("");
+ }
+}
diff --git a/toolsrc/src/vcpkg_Input.cpp b/toolsrc/src/vcpkg_Input.cpp
index f7aae1929..5720cadc0 100644
--- a/toolsrc/src/vcpkg_Input.cpp
+++ b/toolsrc/src/vcpkg_Input.cpp
@@ -1,11 +1,12 @@
+#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 char* example_text)
+ package_spec check_and_get_package_spec(const std::string& package_spec_as_string, const triplet& default_target_triplet, const std::string& example_text)
{
const std::string as_lowercase = Strings::ascii_to_lowercase(package_spec_as_string);
expected<package_spec> expected_spec = package_spec::from_string(as_lowercase, default_target_triplet);
@@ -20,7 +21,7 @@ namespace vcpkg {namespace Input
exit(EXIT_FAILURE);
}
- std::vector<package_spec> check_and_get_package_specs(const std::vector<std::string>& package_specs_as_strings, const triplet& default_target_triplet, const char* example_text)
+ std::vector<package_spec> check_and_get_package_specs(const std::vector<std::string>& package_specs_as_strings, const triplet& default_target_triplet, const std::string& example_text)
{
std::vector<package_spec> specs;
for (const std::string& spec : package_specs_as_strings)
@@ -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 56eeae7a0..3b9d6a859 100644
--- a/toolsrc/src/vcpkg_Strings.cpp
+++ b/toolsrc/src/vcpkg_Strings.cpp
@@ -1,12 +1,14 @@
+#include "pch.h"
#include "vcpkg_Strings.h"
-#include <cstdarg>
-#include <algorithm>
-#include <codecvt>
-#include <iterator>
-
-namespace vcpkg {namespace Strings {namespace details
+namespace vcpkg::Strings::details
{
+ // To disambiguate between two overloads
+ static const auto isspace = [](const char c)
+ {
+ return std::isspace(c);
+ };
+
std::string format_internal(const char* fmtstr, ...)
{
va_list lst;
@@ -32,9 +34,9 @@ namespace vcpkg {namespace Strings {namespace details
return output;
}
-}}}
+}
-namespace vcpkg {namespace Strings
+namespace vcpkg::Strings
{
std::wstring utf8_to_utf16(const std::string& s)
{
@@ -64,4 +66,84 @@ namespace vcpkg {namespace Strings
std::transform(output.begin(), output.end(), output.begin(), ::tolower);
return output;
}
-}}
+
+ std::string join(const std::vector<std::string>& v, const std::string& prefix, const std::string& delimiter, const std::string& suffix)
+ {
+ return join(v, prefix, delimiter, suffix, [](const std::string& i) -> std::string
+ {
+ return i;
+ });
+ }
+
+ Joiner Joiner::on(const std::string& delimiter)
+ {
+ return Joiner(delimiter);
+ }
+
+ Joiner& Joiner::prefix(const std::string& prefix)
+ {
+ this->m_prefix = prefix;
+ return *this;
+ }
+
+ Joiner& Joiner::suffix(const std::string& suffix)
+ {
+ this->m_suffix = suffix;
+ return *this;
+ }
+
+ std::string Joiner::join(const std::vector<std::string>& v) const
+ {
+ return Strings::join(v, this->m_prefix, this->m_delimiter, this->m_suffix);
+ }
+
+ Joiner::Joiner(const std::string& delimiter) : m_prefix(""), m_delimiter(delimiter), m_suffix("")
+ {
+ }
+
+ void trim(std::string* s)
+ {
+ s->erase(std::find_if_not(s->rbegin(), s->rend(), details::isspace).base(), s->end());
+ s->erase(s->begin(), std::find_if_not(s->begin(), s->end(), details::isspace));
+ }
+
+ std::string trimmed(const std::string& s)
+ {
+ auto whitespace_back = std::find_if_not(s.rbegin(), s.rend(), details::isspace).base();
+ auto whitespace_front = std::find_if_not(s.begin(), whitespace_back, details::isspace);
+ return std::string(whitespace_front, whitespace_back);
+ }
+
+ void trim_all_and_remove_whitespace_strings(std::vector<std::string>* strings)
+ {
+ for (std::string& s : *strings)
+ {
+ trim(&s);
+ }
+
+ strings->erase(std::remove_if(strings->begin(), strings->end(), [](const std::string& s)-> bool
+ {
+ 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 cc7080069..754a26741 100644
--- a/toolsrc/src/vcpkg_System.cpp
+++ b/toolsrc/src/vcpkg_System.cpp
@@ -1,11 +1,7 @@
+#include "pch.h"
#include "vcpkg_System.h"
-#include <iostream>
-#include <Windows.h>
-#include <regex>
-namespace fs = std::tr2::sys;
-
-namespace vcpkg {namespace System
+namespace vcpkg::System
{
fs::path get_exe_path_of_current_process()
{
@@ -58,7 +54,7 @@ namespace vcpkg {namespace System
std::cout << "\n";
}
- void print(color c, const char* message)
+ void print(const color c, const char* message)
{
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
@@ -71,7 +67,7 @@ namespace vcpkg {namespace System
SetConsoleTextAttribute(hConsole, original_color);
}
- void println(color c, const char* message)
+ void println(const color c, const char* message)
{
print(c, message);
std::cout << "\n";
@@ -106,6 +102,6 @@ namespace vcpkg {namespace System
double Stopwatch2::microseconds() const
{
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;
+ reinterpret_cast<const LARGE_INTEGER*>(&start_time)->QuadPart) * 1000000.0 / reinterpret_cast<const LARGE_INTEGER*>(&freq)->QuadPart;
}
-}}
+}
diff --git a/toolsrc/src/vcpkg_cmd_arguments.cpp b/toolsrc/src/vcpkg_cmd_arguments.cpp
index a286ba9b7..fdeb6e877 100644
--- a/toolsrc/src/vcpkg_cmd_arguments.cpp
+++ b/toolsrc/src/vcpkg_cmd_arguments.cpp
@@ -1,11 +1,7 @@
-#define WIN32_LEAN_AND_MEAN
-#include <Windows.h>
+#include "pch.h"
#include "vcpkg_cmd_arguments.h"
#include "vcpkg_Commands.h"
-#include "vcpkg_Graphs.h"
-#include <unordered_set>
#include "metrics.h"
-#include "vcpkg.h"
#include "vcpkg_System.h"
namespace vcpkg
@@ -20,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);
}
@@ -28,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);
}
@@ -36,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;
@@ -98,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;
}
@@ -158,7 +154,7 @@ namespace vcpkg
System::println(System::color::error, "Unknown option(s) for command '%s':", this->command);
for (const std::string& option : options_copy)
{
- System::println(option.c_str());
+ System::println(option);
}
exit(EXIT_FAILURE);
}
@@ -181,7 +177,7 @@ namespace vcpkg
return check_exact_arg_count(expected_arg_count, "");
}
- void vcpkg_cmd_arguments::check_max_arg_count(const size_t expected_arg_count, const char* example_text) const
+ void vcpkg_cmd_arguments::check_max_arg_count(const size_t expected_arg_count, const std::string& example_text) const
{
const size_t actual_arg_count = command_arguments.size();
if (actual_arg_count > expected_arg_count)
@@ -192,7 +188,7 @@ namespace vcpkg
}
}
- void vcpkg_cmd_arguments::check_min_arg_count(const size_t expected_arg_count, const char* example_text) const
+ void vcpkg_cmd_arguments::check_min_arg_count(const size_t expected_arg_count, const std::string& example_text) const
{
const size_t actual_arg_count = command_arguments.size();
if (actual_arg_count < expected_arg_count)
@@ -203,7 +199,7 @@ namespace vcpkg
}
}
- void vcpkg_cmd_arguments::check_exact_arg_count(const size_t expected_arg_count, const char* example_text) const
+ void vcpkg_cmd_arguments::check_exact_arg_count(const size_t expected_arg_count, const std::string& example_text) const
{
const size_t actual_arg_count = command_arguments.size();
if (actual_arg_count != expected_arg_count)
diff --git a/toolsrc/src/vcpkg_info.cpp b/toolsrc/src/vcpkg_info.cpp
new file mode 100644
index 000000000..f8e214998
--- /dev/null
+++ b/toolsrc/src/vcpkg_info.cpp
@@ -0,0 +1,35 @@
+#include "pch.h"
+#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::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 f1f4a52ed..14fc9ae48 100644
--- a/toolsrc/src/vcpkg_metrics_uploader.cpp
+++ b/toolsrc/src/vcpkg_metrics_uploader.cpp
@@ -1,10 +1,8 @@
#include "metrics.h"
-#include <filesystem>
#include "vcpkg_Checks.h"
#include "vcpkg_Files.h"
#include <Windows.h>
-namespace fs = std::tr2::sys;
using namespace vcpkg;
int WINAPI
@@ -21,5 +19,5 @@ WinMain(
szArgList = CommandLineToArgvW(GetCommandLineW(), &argCount);
Checks::check_exit(argCount == 2, "Requires exactly one argument, the path to the payload file");
- Upload(Files::get_contents(szArgList[1]).get_or_throw());
+ Upload(Files::read_contents(szArgList[1]).get_or_throw());
}
diff --git a/toolsrc/src/vcpkg_paths.cpp b/toolsrc/src/vcpkg_paths.cpp
index 1f9eb0bc5..8d7060a02 100644
--- a/toolsrc/src/vcpkg_paths.cpp
+++ b/toolsrc/src/vcpkg_paths.cpp
@@ -1,4 +1,4 @@
-#include <filesystem>
+#include "pch.h"
#include "expected.h"
#include "vcpkg_paths.h"
#include "metrics.h"
@@ -32,8 +32,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";
@@ -41,7 +42,7 @@ 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;
}
@@ -55,6 +56,16 @@ namespace vcpkg
return this->ports / spec.name();
}
+ fs::path vcpkg_paths::build_info_file_path(const package_spec& spec) const
+ {
+ return this->package_dir(spec) / "BUILD_INFO";
+ }
+
+ fs::path vcpkg_paths::listfile_path(const BinaryParagraph& pgh) const
+ {
+ return this->vcpkg_dir_info / (pgh.fullstem() + ".list");
+ }
+
bool vcpkg_paths::is_valid_triplet(const triplet& t) const
{
auto it = fs::directory_iterator(this->triplets);
diff --git a/toolsrc/src/vcpkg_version.cpp b/toolsrc/src/vcpkg_version.cpp
deleted file mode 100644
index da52b7cab..000000000
--- a/toolsrc/src/vcpkg_version.cpp
+++ /dev/null
@@ -1,25 +0,0 @@
-#include "vcpkg.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
-
-const std::string& vcpkg::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;
-}
diff --git a/toolsrc/src/vcpkglib.cpp b/toolsrc/src/vcpkglib.cpp
new file mode 100644
index 000000000..06487684c
--- /dev/null
+++ b/toolsrc/src/vcpkglib.cpp
@@ -0,0 +1,240 @@
+#include "pch.h"
+#include "vcpkglib.h"
+#include "vcpkg_Files.h"
+#include "Paragraphs.h"
+#include "metrics.h"
+
+using 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 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);
+
+ 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 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);
+}
+
+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());
+
+#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
+}
+
+std::vector<StatusParagraph_and_associated_files> vcpkg::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;
+}
+
+expected<SourceParagraph> vcpkg::try_load_port(const fs::path& path)
+{
+ 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]);
+ }
+ catch (std::runtime_error const&)
+ {
+ }
+
+ 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())
+ {
+ 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]);
+ }
+ return control_contents_maybe.error_code();
+}
diff --git a/toolsrc/src/vcpkglib_helpers.cpp b/toolsrc/src/vcpkglib_helpers.cpp
index 3aa3735b0..6b96c25cb 100644
--- a/toolsrc/src/vcpkglib_helpers.cpp
+++ b/toolsrc/src/vcpkglib_helpers.cpp
@@ -1,8 +1,8 @@
+#include "pch.h"
#include "vcpkg_Checks.h"
#include "vcpkglib_helpers.h"
-#include <unordered_map>
-namespace vcpkg {namespace details
+namespace vcpkg::details
{
std::string optional_field(const std::unordered_map<std::string, std::string>& fields, const std::string& fieldname)
{
@@ -13,41 +13,43 @@ namespace vcpkg {namespace details
}
return it->second;
- };
+ }
+
+ std::string remove_optional_field(std::unordered_map<std::string, std::string>* fields, const std::string& fieldname)
+ {
+ auto it = fields->find(fieldname);
+ if (it == fields->end())
+ {
+ return std::string();
+ }
+
+ const std::string value = std::move(it->second);
+ fields->erase(it);
+ return value;
+ }
std::string required_field(const std::unordered_map<std::string, std::string>& fields, const std::string& fieldname)
{
auto it = fields.find(fieldname);
- vcpkg::Checks::check_throw(it != fields.end(), "Required field not present: %s", fieldname);
+ Checks::check_exit(it != fields.end(), "Required field not present: %s", fieldname);
return it->second;
- };
+ }
- std::vector<std::string> parse_depends(const std::string& depends_string)
+ std::string remove_required_field(std::unordered_map<std::string, std::string>* fields, const std::string& fieldname)
{
- std::vector<std::string> out;
+ auto it = fields->find(fieldname);
+ Checks::check_exit(it != fields->end(), "Required field not present: %s", fieldname);
- size_t cur = 0;
- do
- {
- auto pos = depends_string.find(',', cur);
- if (pos == std::string::npos)
- {
- out.push_back(depends_string.substr(cur));
- break;
- }
- out.push_back(depends_string.substr(cur, pos - cur));
-
- // skip comma and space
- ++pos;
- if (depends_string[pos] == ' ')
- {
- ++pos;
- }
-
- cur = pos;
- }
- while (cur != std::string::npos);
+ const std::string value = std::move(it->second);
+ fields->erase(it);
+ return value;
+ }
- return out;
+ std::string shorten_description(const std::string& desc)
+ {
+ auto simple_desc = std::regex_replace(desc.substr(0, 49), std::regex("\\n( |\\t)?"), "");
+ if (desc.size() > 49)
+ simple_desc.append("...");
+ return simple_desc;
}
-}}
+}