aboutsummaryrefslogtreecommitdiff
path: root/toolsrc/src
diff options
context:
space:
mode:
authorAlexander Karatarakis <alkarata@microsoft.com>2016-09-18 20:50:08 -0700
committerAlexander Karatarakis <alkarata@microsoft.com>2016-09-18 20:54:03 -0700
commitccca198c1b1730b0241911cb56dc8e3504958b2a (patch)
treea2dd9b8b087a09afdcecc5cbb3377bed15127eb2 /toolsrc/src
downloadvcpkg-ccca198c1b1730b0241911cb56dc8e3504958b2a.tar.gz
vcpkg-ccca198c1b1730b0241911cb56dc8e3504958b2a.zip
Initial commit
Diffstat (limited to 'toolsrc/src')
-rw-r--r--toolsrc/src/BinaryParagraph.cpp82
-rw-r--r--toolsrc/src/SourceParagraph.cpp21
-rw-r--r--toolsrc/src/StatusParagraph.cpp87
-rw-r--r--toolsrc/src/StatusParagraphs.cpp67
-rw-r--r--toolsrc/src/commands_help.cpp55
-rw-r--r--toolsrc/src/commands_installation.cpp219
-rw-r--r--toolsrc/src/commands_integration.cpp297
-rw-r--r--toolsrc/src/commands_other.cpp293
-rw-r--r--toolsrc/src/commands_remove.cpp43
-rw-r--r--toolsrc/src/lib.cpp524
-rw-r--r--toolsrc/src/main.cpp245
-rw-r--r--toolsrc/src/metrics.cpp425
-rw-r--r--toolsrc/src/package_spec.cpp46
-rw-r--r--toolsrc/src/package_spec_parse_result.cpp45
-rw-r--r--toolsrc/src/post_build_lint.cpp356
-rw-r--r--toolsrc/src/test.cpp347
-rw-r--r--toolsrc/src/triplet.cpp59
-rw-r--r--toolsrc/src/vcpkg.cpp178
-rw-r--r--toolsrc/src/vcpkg_Checks.cpp41
-rw-r--r--toolsrc/src/vcpkg_Environment.cpp87
-rw-r--r--toolsrc/src/vcpkg_Files.cpp38
-rw-r--r--toolsrc/src/vcpkg_Strings.cpp60
-rw-r--r--toolsrc/src/vcpkg_System.cpp111
-rw-r--r--toolsrc/src/vcpkg_cmd_arguments.cpp255
-rw-r--r--toolsrc/src/vcpkg_metrics_uploader.cpp25
-rw-r--r--toolsrc/src/vcpkg_paths.cpp48
-rw-r--r--toolsrc/src/vcpkg_version.cpp25
-rw-r--r--toolsrc/src/vcpkglib_helpers.cpp50
28 files changed, 4129 insertions, 0 deletions
diff --git a/toolsrc/src/BinaryParagraph.cpp b/toolsrc/src/BinaryParagraph.cpp
new file mode 100644
index 000000000..274bd879e
--- /dev/null
+++ b/toolsrc/src/BinaryParagraph.cpp
@@ -0,0 +1,82 @@
+#include "BinaryParagraph.h"
+#include "vcpkglib_helpers.h"
+#include "vcpkg_Checks.h"
+
+using namespace vcpkg::details;
+
+namespace vcpkg
+{
+ BinaryParagraph::BinaryParagraph() = default;
+
+ BinaryParagraph::BinaryParagraph(const std::unordered_map<std::string, std::string>& fields)
+ {
+ details::required_field(fields, name, "Package");
+ required_field(fields, version, "Version");
+ required_field(fields, target_triplet.value, "Architecture");
+ {
+ std::string multi_arch;
+ required_field(fields, multi_arch, "Multi-Arch");
+ Checks::check_throw(multi_arch == "same", "Multi-Arch must be 'same' but was %s", multi_arch);
+ }
+ optional_field(fields, description, "Description");
+ std::string deps;
+ optional_field(fields, deps, "Depends");
+ if (!deps.empty())
+ {
+ depends.clear();
+ parse_depends(deps, depends);
+ }
+ optional_field(fields, maintainer, "Maintainer");
+ }
+
+ BinaryParagraph::BinaryParagraph(const SourceParagraph& spgh, const triplet& target_triplet)
+ {
+ this->name = spgh.name;
+ this->version = spgh.version;
+ this->description = spgh.description;
+ this->maintainer = spgh.maintainer;
+ this->depends = spgh.depends;
+ this->target_triplet = target_triplet;
+ }
+
+ std::string BinaryParagraph::displayname() const
+ {
+ return Strings::format("%s:%s", this->name, this->target_triplet);
+ }
+
+ std::string BinaryParagraph::dir() const
+ {
+ return Strings::format("%s_%s", this->name, this->target_triplet);
+ }
+
+ std::string BinaryParagraph::fullstem() const
+ {
+ return Strings::format("%s_%s_%s", this->name, this->version, this->target_triplet);
+ }
+
+ std::ostream& operator<<(std::ostream& os, const BinaryParagraph& p)
+ {
+ os << "Package: " << p.name << "\n";
+ os << "Version: " << p.version << "\n";
+ if (!p.depends.empty())
+ {
+ os << "Depends: " << p.depends.front();
+
+ auto b = p.depends.begin() + 1;
+ auto e = p.depends.end();
+ for (; b != e; ++b)
+ {
+ os << ", " << *b;
+ }
+
+ os << "\n";
+ }
+ os << "Architecture: " << p.target_triplet << "\n";
+ os << "Multi-Arch: same\n";
+ if (!p.maintainer.empty())
+ os << "Maintainer: " << p.maintainer << "\n";
+ if (!p.description.empty())
+ os << "Description: " << p.description << "\n";
+ return os;
+ }
+}
diff --git a/toolsrc/src/SourceParagraph.cpp b/toolsrc/src/SourceParagraph.cpp
new file mode 100644
index 000000000..7e3b0403e
--- /dev/null
+++ b/toolsrc/src/SourceParagraph.cpp
@@ -0,0 +1,21 @@
+#include "SourceParagraph.h"
+#include "vcpkglib_helpers.h"
+
+using namespace vcpkg::details;
+
+vcpkg::SourceParagraph::SourceParagraph() = default;
+
+vcpkg::SourceParagraph::SourceParagraph(const std::unordered_map<std::string, std::string>& fields)
+{
+ required_field(fields, name, "Source");
+ required_field(fields, version, "Version");
+ optional_field(fields, description, "Description");
+ std::string deps;
+ optional_field(fields, deps, "Build-Depends");
+ if (!deps.empty())
+ {
+ depends.clear();
+ parse_depends(deps, depends);
+ }
+ optional_field(fields, maintainer, "Maintainer");
+}
diff --git a/toolsrc/src/StatusParagraph.cpp b/toolsrc/src/StatusParagraph.cpp
new file mode 100644
index 000000000..09a3b4d45
--- /dev/null
+++ b/toolsrc/src/StatusParagraph.cpp
@@ -0,0 +1,87 @@
+#include "StatusParagraph.h"
+#include "vcpkglib_helpers.h"
+
+using namespace vcpkg::details;
+
+namespace vcpkg
+{
+ StatusParagraph::StatusParagraph() : want(want_t::error), state(install_state_t::error)
+ {
+ }
+
+ std::ostream& operator<<(std::ostream& os, const StatusParagraph& p)
+ {
+ os << p.package;
+ os << "Status: " << to_string(p.want) << " ok " << to_string(p.state) << "\n";
+ return os;
+ }
+
+ StatusParagraph::StatusParagraph(const std::unordered_map<std::string, std::string>& fields)
+ : package(fields)
+ {
+ std::string status_field;
+ required_field(fields, status_field, "Status");
+
+ auto b = status_field.begin();
+ auto mark = b;
+ auto e = status_field.end();
+
+ // Todo: improve error handling
+ while (b != e && *b != ' ')
+ ++b;
+
+ want = [](const std::string& text)
+ {
+ if (text == "unknown")
+ return want_t::unknown;
+ if (text == "install")
+ return want_t::install;
+ if (text == "hold")
+ return want_t::hold;
+ if (text == "deinstall")
+ return want_t::deinstall;
+ if (text == "purge")
+ return want_t::purge;
+ return want_t::error;
+ }(std::string(mark, b));
+
+ if (std::distance(b, e) < 4)
+ return;
+ b += 4;
+
+ state = [](const std::string& text)
+ {
+ if (text == "not-installed")
+ return install_state_t::not_installed;
+ if (text == "installed")
+ return install_state_t::installed;
+ if (text == "half-installed")
+ return install_state_t::half_installed;
+ return install_state_t::error;
+ }(std::string(b, e));
+ }
+
+ std::string to_string(install_state_t f)
+ {
+ switch (f)
+ {
+ case install_state_t::half_installed: return "half-installed";
+ case install_state_t::installed: return "installed";
+ case install_state_t::not_installed: return "not-installed";
+ default: return "error";
+ }
+ }
+
+ std::string to_string(want_t f)
+ {
+ switch (f)
+ {
+ case want_t::deinstall: return "deinstall";
+ case want_t::hold: return "hold";
+ case want_t::install: return "install";
+ case want_t::purge: return "purge";
+ case want_t::unknown: return "unknown";
+ default: return "error";
+ }
+ }
+}
diff --git a/toolsrc/src/StatusParagraphs.cpp b/toolsrc/src/StatusParagraphs.cpp
new file mode 100644
index 000000000..463e3e3b8
--- /dev/null
+++ b/toolsrc/src/StatusParagraphs.cpp
@@ -0,0 +1,67 @@
+#include "StatusParagraphs.h"
+#include <algorithm>
+#include "vcpkg_Checks.h"
+
+namespace vcpkg
+{
+ StatusParagraphs::StatusParagraphs() = default;
+
+ StatusParagraphs::StatusParagraphs(std::vector<std::unique_ptr<StatusParagraph>>&& ps)
+ : paragraphs(std::move(ps))
+ {
+ };
+
+ StatusParagraphs::const_iterator StatusParagraphs::find(const std::string& name, const triplet& target_triplet) const
+ {
+ return std::find_if(begin(), end(), [&](const auto& pgh)
+ {
+ return pgh->package.name == name && pgh->package.target_triplet == target_triplet;
+ });
+ }
+
+ StatusParagraphs::iterator StatusParagraphs::find(const std::string& name, const triplet& target_triplet)
+ {
+ return std::find_if(begin(), end(), [&](const auto& pgh)
+ {
+ return pgh->package.name == name && pgh->package.target_triplet == target_triplet;
+ });
+ }
+
+ StatusParagraphs::iterator StatusParagraphs::find_installed(const std::string& name, const triplet& target_triplet)
+ {
+ auto it = find(name, target_triplet);
+ if (it != end() && (*it)->want == want_t::install)
+ {
+ return it;
+ }
+
+ return end();
+ }
+
+ StatusParagraphs::iterator StatusParagraphs::insert(std::unique_ptr<StatusParagraph> pgh)
+ {
+ Checks::check_throw(pgh != nullptr, "Inserted null paragraph");
+ auto ptr = find(pgh->package.name, pgh->package.target_triplet);
+ if (ptr == end())
+ {
+ paragraphs.push_back(std::move(pgh));
+ return paragraphs.rbegin();
+ }
+ else
+ {
+ // consume data from provided pgh.
+ **ptr = std::move(*pgh);
+ return ptr;
+ }
+ }
+
+ std::ostream& vcpkg::operator<<(std::ostream& os, const StatusParagraphs& l)
+ {
+ for (auto& pgh : l.paragraphs)
+ {
+ os << *pgh;
+ os << "\n";
+ }
+ return os;
+ }
+}
diff --git a/toolsrc/src/commands_help.cpp b/toolsrc/src/commands_help.cpp
new file mode 100644
index 000000000..4e1ae9c49
--- /dev/null
+++ b/toolsrc/src/commands_help.cpp
@@ -0,0 +1,55 @@
+#include "vcpkg_Commands.h"
+#include "vcpkg.h"
+#include "vcpkg_System.h"
+
+namespace vcpkg
+{
+ void version_command(const vcpkg_cmd_arguments& args)
+ {
+ args.check_max_args(0);
+ System::println("Vcpkg package management program version %s\n"
+ "\n"
+ "Vcpkg is provided \"as-is\" without warranty of any kind, express or implied.\n"
+ "All rights reserved.", vcpkg::version()
+ );
+ exit(EXIT_SUCCESS);
+ }
+
+ void help_command(const vcpkg_cmd_arguments& args, const vcpkg_paths& paths)
+ {
+ args.check_max_args(1);
+ if (args.command_arguments.empty())
+ {
+ print_usage();
+ exit(EXIT_SUCCESS);
+ }
+ const auto& topic = args.command_arguments[0];
+ if (topic == "triplet")
+ {
+ help_topic_valid_triplet(paths);
+ }
+ else
+ {
+ System::println(System::color::error, "Error: unknown topic %s", topic);
+ print_usage();
+ exit(EXIT_FAILURE);
+ }
+ exit(EXIT_SUCCESS);
+ }
+
+ void contact_command(const vcpkg_cmd_arguments& /*args*/)
+ {
+ 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_installation.cpp b/toolsrc/src/commands_installation.cpp
new file mode 100644
index 000000000..f6a774b8d
--- /dev/null
+++ b/toolsrc/src/commands_installation.cpp
@@ -0,0 +1,219 @@
+#include "vcpkg_Commands.h"
+#include "vcpkg.h"
+#include <iostream>
+#include <fstream>
+#include <iomanip>
+#include "vcpkg_Environment.h"
+#include "metrics.h"
+#include "vcpkg_Files.h"
+#include "post_build_lint.h"
+#include "vcpkg_System.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;
+ const std::wstring vs140comntools = System::wdupenv_str(L"VS140COMNTOOLS");
+
+ const std::wstring command = Strings::format(LR"("%s..\..\VC\vcvarsall.bat" %s && cmake -DCMD=BUILD -DPORT=%s -DTARGET_TRIPLET=%s "-DCURRENT_PORT_DIR=%s/." -P "%s")",
+ vs140comntools,
+ Strings::utf8_to_utf16(spec.target_triplet.architecture()),
+ Strings::utf8_to_utf16(spec.name),
+ Strings::utf8_to_utf16(spec.target_triplet.value),
+ port_dir.generic_wstring(),
+ ports_cmake_script_path.generic_wstring());
+
+ System::Stopwatch 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: build command failed");
+ TrackProperty("error", "build failed");
+ TrackProperty("build_error", std::to_string(return_code));
+ exit(EXIT_FAILURE);
+ }
+
+ perform_all_checks(spec, paths);
+
+ create_binary_control_file(paths, port_dir, spec.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)
+ {
+ StatusParagraphs status_db = database_load_check(paths);
+
+ std::vector<package_spec> specs = args.extract_package_specs_with_unmet_dependencies(paths, default_target_triplet, status_db);
+ Checks::check_exit(!specs.empty(), "Specs cannot be empty");
+ std::string specs_string = to_string(specs[0]);
+ for (size_t i = 1; i < specs.size(); ++i)
+ {
+ specs_string.push_back(',');
+ specs_string.append(to_string(specs[i]));
+ }
+ TrackProperty("installplan", specs_string);
+ Environment::ensure_utilities_on_path(paths);
+
+ for (const package_spec& spec : specs)
+ {
+ 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 = find_available_package(paths, 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 search_command(const vcpkg_cmd_arguments& args, const vcpkg_paths& paths)
+ {
+ args.check_max_args(1);
+
+ if (args.command_arguments.size() == 1)
+ {
+ System::println(System::color::warning, "Search strings are not yet implemented; showing full list of packages.");
+ }
+
+ auto begin_it = fs::directory_iterator(paths.ports);
+ auto end_it = fs::directory_iterator();
+ for (; begin_it != end_it; ++begin_it)
+ {
+ const auto& path = begin_it->path();
+
+ try
+ {
+ auto pghs = get_paragraphs(path / "CONTROL");
+ if (pghs.empty())
+ continue;
+ auto srcpgh = SourceParagraph(pghs[0]);
+ std::cout << std::left
+ << std::setw(20) << srcpgh.name << ' '
+ << std::setw(16) << srcpgh.version << ' '
+ << shorten_description(srcpgh.description) << '\n';
+ }
+ catch (std::runtime_error const&)
+ {
+ }
+ }
+
+ System::println("\nIf your library is not listed, please open an issue at:\n"
+ " https://github.com/Microsoft/vcpkg/issues");
+
+ exit(EXIT_SUCCESS);
+ }
+
+ void cache_command(const vcpkg_cmd_arguments& args, const vcpkg_paths& paths)
+ {
+ args.check_max_args(0);
+
+ auto begin_it = fs::directory_iterator(paths.packages);
+ auto end_it = fs::directory_iterator();
+
+ if (begin_it == end_it)
+ {
+ System::println("No packages are cached.");
+ exit(EXIT_SUCCESS);
+ }
+
+ for (; begin_it != end_it; ++begin_it)
+ {
+ const auto& path = begin_it->path();
+
+ auto file_contents = Files::get_contents(path / "CONTROL");
+ if (auto text = file_contents.get())
+ {
+ auto pghs = parse_paragraphs(*text);
+ if (pghs.size() != 1)
+ continue;
+
+ auto src = BinaryParagraph(pghs[0]);
+ System::println(src.displayname().c_str());
+ }
+ }
+
+ exit(EXIT_SUCCESS);
+ }
+
+ void build_command(const vcpkg_cmd_arguments& args, const vcpkg_paths& paths, const triplet& default_target_triplet)
+ {
+ std::vector<package_spec> specs = args.parse_all_arguments_as_package_specs(default_target_triplet);
+ Environment::ensure_utilities_on_path(paths);
+ for (const package_spec& spec : specs)
+ {
+ 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)
+ {
+ if (args.command_arguments.size() != 2)
+ {
+ System::println(System::color::error, "Error: buildexternal requires the package name and the directory containing the CONTROL file");
+ print_example(R"(buildexternal mylib C:\path\to\mylib\)");
+ exit(EXIT_FAILURE);
+ }
+
+ expected<package_spec> current_spec = vcpkg::parse(args.command_arguments[0], default_target_triplet);
+ if (auto spec = current_spec.get())
+ {
+ 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_integration.cpp
new file mode 100644
index 000000000..e1b63038a
--- /dev/null
+++ b/toolsrc/src/commands_integration.cpp
@@ -0,0 +1,297 @@
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <shellapi.h>
+#include "vcpkg_Commands.h"
+#include "vcpkg.h"
+#include <fstream>
+#include <iostream>
+#include <regex>
+#include "vcpkg_Environment.h"
+#include "vcpkg_Checks.h"
+#include "vcpkg_System.h"
+
+namespace vcpkg
+{
+ static const fs::path old_system_wide_targets_file = "C:/Program Files (x86)/MSBuild/14.0/Microsoft.Common.Targets/ImportBefore/vcpkg.nuget.targets";
+ static const fs::path system_wide_targets_file = "C:/Program Files (x86)/MSBuild/14.0/Microsoft.Common.Targets/ImportBefore/vcpkg.system.targets";
+
+ static std::string create_appdata_targets_shortcut(const std::string& target_path) noexcept
+ {
+ return Strings::format(R"###(
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Condition="Exists('%s') and '$(VCPkgLocalAppDataDisabled)' == ''" Project="%s" />
+</Project>
+)###", target_path, target_path);
+ }
+
+ static std::string create_system_targets_shortcut() noexcept
+ {
+ return R"###(
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <VCLibPackagePath Condition="'$(VCLibPackagePath)' == ''">$(LOCALAPPDATA)\vcpkg\vcpkg.user</VCLibPackagePath>
+ </PropertyGroup>
+ <Import Condition="'$(VCLibPackagePath)' != '' and Exists('$(VCLibPackagePath).props')" Project="$(VCLibPackagePath).props" />
+</Project>
+)###";
+ }
+
+ static std::string create_nuget_targets_file(const fs::path& msbuild_vcpkg_targets_file) noexcept
+ {
+ const std::string as_string = msbuild_vcpkg_targets_file.string();
+
+ return Strings::format(R"###(
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="%s" Condition="Exists('%s')" />
+ <Target Name="CheckValidPlatform" BeforeTargets="Build">
+ <Error Text="Unsupported architecture combination. Remove the 'vcpkg' nuget package." Condition="'$(VCPkgEnabled)' != 'true' and '$(VCPkgDisableError)' == ''"/>
+ </Target>
+</Project>
+)###", as_string, as_string);
+ }
+
+ static std::string create_nuget_props_file() noexcept
+ {
+ return R"###(
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <VCPkgLocalAppDataDisabled>true</VCPkgLocalAppDataDisabled>
+ </PropertyGroup>
+</Project>
+)###";
+ }
+
+ static std::string get_nuget_id(const fs::path& vcpkg_root_dir)
+ {
+ std::string dir_id = vcpkg_root_dir.generic_string();
+ std::replace(dir_id.begin(), dir_id.end(), '/', '.');
+ dir_id.erase(1, 1); // Erasing the ":"
+
+ // NuGet id cannot have invalid characters. We will only use alphanumeric and dot.
+ dir_id.erase(std::remove_if(dir_id.begin(), dir_id.end(), [](char c)
+ {
+ return !isalnum(c) && (c != '.');
+ }), dir_id.end());
+
+ const std::string nuget_id = "vcpkg." + dir_id;
+ return nuget_id;
+ }
+
+ static std::string create_nuspec_file(const fs::path& vcpkg_root_dir, const std::string& nuget_id, const std::string& nupkg_version)
+ {
+ const std::string nuspec_file_content_template = R"(
+<package>
+ <metadata>
+ <id>@NUGET_ID@</id>
+ <version>@VERSION@</version>
+ <authors>cpp-packages</authors>
+ <description>
+ This package imports all libraries currently installed in @VCPKG_DIR@. This package does not contain any libraries and instead refers to the folder directly (like a symlink).
+ </description>
+ </metadata>
+ <files>
+ <file src="vcpkg.nuget.props" target="build\native\@NUGET_ID@.props" />
+ <file src="vcpkg.nuget.targets" target="build\native\@NUGET_ID@.targets" />
+ </files>
+</package>
+)";
+
+ std::string nuspec_file_content = std::regex_replace(nuspec_file_content_template, std::regex("@NUGET_ID@"), nuget_id);
+ nuspec_file_content = std::regex_replace(nuspec_file_content, std::regex("@VCPKG_DIR@"), vcpkg_root_dir.string());
+ nuspec_file_content = std::regex_replace(nuspec_file_content, std::regex("@VERSION@"), nupkg_version);
+ return nuspec_file_content;
+ }
+
+ enum class elevation_prompt_user_choice
+ {
+ yes,
+ no
+ };
+
+ static elevation_prompt_user_choice elevated_cmd_execute(const std::string& param)
+ {
+ SHELLEXECUTEINFO shExInfo = {0};
+ shExInfo.cbSize = sizeof(shExInfo);
+ shExInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
+ shExInfo.hwnd = nullptr;
+ shExInfo.lpVerb = "runas";
+ shExInfo.lpFile = "cmd"; // Application to start
+
+ shExInfo.lpParameters = param.c_str(); // Additional parameters
+ shExInfo.lpDirectory = nullptr;
+ shExInfo.nShow = SW_HIDE;
+ shExInfo.hInstApp = nullptr;
+
+ if (!ShellExecuteExA(&shExInfo))
+ {
+ return elevation_prompt_user_choice::no;
+ }
+ if (shExInfo.hProcess == nullptr)
+ {
+ return elevation_prompt_user_choice::no;
+ }
+ WaitForSingleObject(shExInfo.hProcess, INFINITE);
+ CloseHandle(shExInfo.hProcess);
+ return elevation_prompt_user_choice::yes;
+ }
+
+ static fs::path get_appdata_targets_path()
+ {
+ return fs::path(System::wdupenv_str(L"LOCALAPPDATA")) / "vcpkg" / "vcpkg.user.targets";
+ }
+
+ static void integrate_install(const vcpkg_paths& paths)
+ {
+ // TODO: This block of code should eventually be removed
+ {
+ if (fs::exists(old_system_wide_targets_file))
+ {
+ const std::string param = Strings::format(R"(/c DEL "%s" /Q > nul)", old_system_wide_targets_file.string());
+ elevation_prompt_user_choice user_choice = elevated_cmd_execute(param);
+ switch (user_choice)
+ {
+ case elevation_prompt_user_choice::yes:
+ break;
+ case elevation_prompt_user_choice::no:
+ System::println(System::color::warning, "Warning: Previous integration file was not removed");
+ exit(EXIT_FAILURE);
+ default:
+ Checks::unreachable();
+ }
+ }
+ }
+
+ const fs::path tmp_dir = paths.buildsystems / "tmp";
+ fs::create_directory(paths.buildsystems);
+ fs::create_directory(tmp_dir);
+
+ if (!fs::exists(system_wide_targets_file))
+ {
+ const fs::path sys_src_path = tmp_dir / "vcpkg.system.targets";
+ std::ofstream(sys_src_path) << create_system_targets_shortcut();
+
+ const std::string param = Strings::format(R"(/c COPY "%s" "%s" /Y > nul)", sys_src_path.string(), system_wide_targets_file.string());
+ elevation_prompt_user_choice user_choice = elevated_cmd_execute(param);
+ switch (user_choice)
+ {
+ case elevation_prompt_user_choice::yes:
+ break;
+ case elevation_prompt_user_choice::no:
+ System::println(System::color::warning, "Warning: integration was not applied");
+ exit(EXIT_FAILURE);
+ default:
+ Checks::unreachable();
+ }
+ }
+
+ const fs::path appdata_src_path = tmp_dir / "vcpkg.user.targets";
+ std::ofstream(appdata_src_path) << create_appdata_targets_shortcut(paths.buildsystems_msbuild_targets.string());
+ auto appdata_dst_path = get_appdata_targets_path();
+
+ if (!fs::copy_file(appdata_src_path, appdata_dst_path, fs::copy_options::overwrite_existing))
+ {
+ System::println(System::color::error, "Error: Failed to copy file: %s -> %s", appdata_src_path.string(), appdata_dst_path.string());
+ exit(EXIT_FAILURE);
+ }
+ System::println(System::color::success, "Applied user-wide integration for this vcpkg root.");
+ System::println("\n"
+ "All C++ projects can now #include any installed libraries.\n"
+ "Linking will be handled automatically.\n"
+ "Installing new libraries will make them instantly available.");
+
+ exit(EXIT_SUCCESS);
+ }
+
+ static void integrate_remove()
+ {
+ auto path = get_appdata_targets_path();
+ if (!fs::exists(path))
+ {
+ System::println(System::color::success, "User-wide integration is not installed");
+ exit(EXIT_SUCCESS);
+ }
+
+ const std::wstring cmd_line = Strings::format(LR"(DEL "%s")", get_appdata_targets_path().native());
+ const int exit_code = System::cmd_execute(cmd_line);
+ if (exit_code)
+ {
+ System::println(System::color::error, "Error: Unable to remove user-wide integration: %d", exit_code);
+ exit(exit_code);
+ }
+ System::println(System::color::success, "User-wide integration was removed");
+ exit(EXIT_SUCCESS);
+ }
+
+ static void integrate_project(const vcpkg_paths& paths)
+ {
+ Environment::ensure_nuget_on_path(paths);
+
+ const fs::path& buildsystems_dir = paths.buildsystems;
+ const fs::path tmp_dir = buildsystems_dir / "tmp";
+ fs::create_directory(buildsystems_dir);
+ fs::create_directory(tmp_dir);
+
+ const fs::path targets_file_path = tmp_dir / "vcpkg.nuget.targets";
+ const fs::path props_file_path = tmp_dir / "vcpkg.nuget.props";
+ const fs::path nuspec_file_path = tmp_dir / "vcpkg.nuget.nuspec";
+ const std::string nuget_id = get_nuget_id(paths.root);
+ const std::string nupkg_version = "1.0.0";
+
+ std::ofstream(targets_file_path) << create_nuget_targets_file(paths.buildsystems_msbuild_targets);
+ std::ofstream(props_file_path) << create_nuget_props_file();
+ std::ofstream(nuspec_file_path) << create_nuspec_file(paths.root, nuget_id, nupkg_version);
+
+ // Using all forward slashes for the command line
+ const std::wstring cmd_line = Strings::format(LR"(nuget.exe pack -OutputDirectory "%s" "%s" > nul)", buildsystems_dir.native(), nuspec_file_path.native());
+
+ const int exit_code = System::cmd_execute(cmd_line);
+
+ const fs::path nuget_package = buildsystems_dir / Strings::format("%s.%s.nupkg", nuget_id, nupkg_version);
+ if (exit_code != 0 || !fs::exists(nuget_package))
+ {
+ System::println(System::color::error, "Error: NuGet package creation failed");
+ exit(EXIT_FAILURE);
+ }
+
+ System::println(System::color::success, "Created nupkg: %s", nuget_package.string());
+
+ System::println(R"(
+With a project open, go to Tools->NuGet Package Manager->Package Manager Console and paste:
+ Install-Package %s -Source "%s"
+)", nuget_id, buildsystems_dir.generic_string());
+
+ exit(EXIT_SUCCESS);
+ }
+
+ const char* const INTEGRATE_COMMAND_HELPSTRING =
+ " vcpkg integrate install Make installed packages available user-wide. Requires admin privileges on first use\n"
+ " vcpkg integrate remove Remove user-wide integration\n"
+ " vcpkg integrate project Generate a referencing nuget package for individual VS project use\n";
+
+ void integrate_command(const vcpkg_cmd_arguments& args, const vcpkg_paths& paths)
+ {
+ if (args.command_arguments.size() != 1)
+ {
+ std::cout << "Commands:\n" <<
+ INTEGRATE_COMMAND_HELPSTRING <<
+ "\n";
+ exit(EXIT_FAILURE);
+ }
+
+ if (args.command_arguments[0] == "install")
+ {
+ return integrate_install(paths);
+ }
+ if (args.command_arguments[0] == "remove")
+ {
+ return integrate_remove();
+ }
+ if (args.command_arguments[0] == "project")
+ {
+ return integrate_project(paths);
+ }
+
+ System::println(System::color::error, "Unknown parameter %s for integrate", args.command_arguments[0]);
+ exit(EXIT_FAILURE);
+ }
+}
diff --git a/toolsrc/src/commands_other.cpp b/toolsrc/src/commands_other.cpp
new file mode 100644
index 000000000..c7dcc2586
--- /dev/null
+++ b/toolsrc/src/commands_other.cpp
@@ -0,0 +1,293 @@
+#include "vcpkg_Commands.h"
+#include <iostream>
+#include <unordered_set>
+#include "vcpkg_Environment.h"
+#include "vcpkg.h"
+#include "vcpkg_System.h"
+#include "vcpkg_Files.h"
+
+namespace vcpkg
+{
+ void print_usage()
+ {
+ std::cout << "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"
+ << INTEGRATE_COMMAND_HELPSTRING <<
+ "\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: x86-windows, 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."
+ "\n";
+ }
+
+ void print_example(const char* command_and_arguments)
+ {
+ std::cout <<
+ "Example:\n"
+ " vcpkg " << command_and_arguments << "\n";
+ }
+
+ void update_command(const vcpkg_cmd_arguments& /*args*/, const vcpkg_paths& paths)
+ {
+ auto status_db = database_load_check(paths);
+
+ std::unordered_map<std::string, std::string> src_names_to_versions;
+
+ auto begin_it = fs::directory_iterator(paths.ports);
+ auto end_it = fs::directory_iterator();
+ for (; begin_it != end_it; ++begin_it)
+ {
+ const auto& path = begin_it->path();
+ try
+ {
+ auto pghs = get_paragraphs(path / "CONTROL");
+ if (pghs.empty())
+ continue;
+ auto srcpgh = SourceParagraph(pghs[0]);
+ src_names_to_versions.emplace(srcpgh.name, srcpgh.version);
+ }
+ catch (std::runtime_error const&)
+ {
+ }
+ }
+
+ std::string packages_list;
+
+ std::vector<std::string> packages_output;
+ for (auto&& pgh : database_load_check(paths))
+ {
+ if (pgh->state == install_state_t::not_installed && pgh->want == want_t::purge)
+ continue;
+ auto it = src_names_to_versions.find(pgh->package.name);
+ if (it == src_names_to_versions.end())
+ {
+ // Package was not installed from portfile
+ continue;
+ }
+ if (it->second != pgh->package.version)
+ {
+ packages_output.push_back(Strings::format("%-27s %s -> %s",
+ pgh->package.displayname(),
+ pgh->package.version,
+ it->second));
+ packages_list.append(" " + pgh->package.displayname());
+ }
+ }
+ std::sort(packages_output.begin(), packages_output.end());
+ if (packages_output.empty())
+ {
+ System::println("No packages need updating.");
+ }
+ else
+ {
+ System::println("The following packages differ from their port versions:");
+ for (auto&& package : packages_output)
+ {
+ System::println(" %s", package.c_str());
+ }
+ 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");
+ 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);
+
+ 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.",
+ maj2, min2, rev2,
+ maj1, min1, rev1);
+ }
+ }
+ }
+
+ exit(EXIT_SUCCESS);
+ }
+
+ void edit_command(const vcpkg_cmd_arguments& args, const vcpkg_paths& paths, const triplet& default_target_triplet)
+ {
+ static auto example = "edit zlib";
+ args.check_max_args(1, example);
+ package_spec spec = args.parse_all_arguments_as_package_specs(default_target_triplet, example).at(0);
+
+ // Find editor
+ std::wstring env_EDITOR = System::wdupenv_str(L"EDITOR");
+ if (env_EDITOR.empty())
+ env_EDITOR = LR"(C:\Program Files (x86)\Microsoft VS Code\Code.exe)";
+
+ auto portpath = paths.ports / spec.name;
+ std::wstring cmdLine = Strings::format(LR"("%s" "%s" "%s")", env_EDITOR, portpath.native(), (portpath / "portfile.cmake").native());
+ exit(System::cmd_execute(cmdLine));
+ }
+
+ void create_command(const vcpkg_cmd_arguments& args, const vcpkg_paths& paths, const triplet& default_target_triplet)
+ {
+ args.check_max_args(3);
+ package_spec spec = args.parse_all_arguments_as_package_specs(default_target_triplet).at(0);
+ if (args.command_arguments.size() < 2)
+ {
+ System::println(System::color::error, "Error: create requires the archive's URL as the second argument.");
+ print_usage();
+ exit(EXIT_FAILURE);
+ }
+ Environment::ensure_utilities_on_path(paths);
+
+ // Space OR define the FILENAME with proper spacing
+ std::wstring custom_filename = L" ";
+ if (args.command_arguments.size() >= 3)
+ {
+ custom_filename = Strings::format(L" -DFILENAME=%s ", Strings::utf8_to_utf16(args.command_arguments.at(2)));
+ }
+
+ const std::wstring cmdline = Strings::format(LR"(cmake -DCMD=SCAFFOLD -DPORT=%s -DTARGET_TRIPLET=%s -DURL=%s%s-P "%s")",
+ Strings::utf8_to_utf16(spec.name),
+ Strings::utf8_to_utf16(spec.target_triplet.value),
+ Strings::utf8_to_utf16(args.command_arguments.at(1)),
+ custom_filename,
+ paths.ports_cmake.generic_wstring());
+
+ exit(System::cmd_execute(cmdline));
+ }
+
+ void list_command(const vcpkg_cmd_arguments& args, const vcpkg_paths& paths)
+ {
+ args.check_max_args(0);
+
+ std::vector<std::string> packages_output;
+ for (auto&& pgh : database_load_check(paths))
+ {
+ 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)));
+ }
+ std::sort(packages_output.begin(), packages_output.end());
+ for (auto&& package : packages_output)
+ {
+ System::println(package.c_str());
+ }
+ if (packages_output.empty())
+ {
+ System::println("No packages are installed. Did you mean `search`?");
+ }
+ exit(EXIT_SUCCESS);
+ }
+
+ void import_command(const vcpkg_cmd_arguments& args, const vcpkg_paths& paths)
+ {
+ if (args.command_arguments.size() != 3)
+ {
+ System::println(System::color::error, "Error: %s requires 3 parameters", args.command);
+ print_example(Strings::format(R"(%s C:\path\to\CONTROLfile C:\path\to\includedir C:\path\to\projectdir)", args.command).c_str());
+ exit(EXIT_FAILURE);
+ }
+
+ 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);
+ 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);
+ exit(EXIT_SUCCESS);
+ }
+
+ void owns_command(const vcpkg_cmd_arguments& args, const vcpkg_paths& paths)
+ {
+ args.check_max_args(1);
+ if (args.command_arguments.size() == 0)
+ {
+ System::println(System::color::error, "Error: owns requires a pattern to search for as the first argument.");
+ std::cout <<
+ "example:\n"
+ " vcpkg owns .dll\n";
+ exit(EXIT_FAILURE);
+ }
+ StatusParagraphs status_db = database_load_check(paths);
+ search_file(paths, args.command_arguments[0], status_db);
+ exit(EXIT_SUCCESS);
+ }
+
+ 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},
+ {"edit", edit_command},
+ {"create", create_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},
+ {"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_remove.cpp b/toolsrc/src/commands_remove.cpp
new file mode 100644
index 000000000..f5315ccb1
--- /dev/null
+++ b/toolsrc/src/commands_remove.cpp
@@ -0,0 +1,43 @@
+#include "vcpkg_Commands.h"
+#include "vcpkg.h"
+#include "vcpkg_System.h"
+
+namespace vcpkg
+{
+ static const std::string OPTION_PURGE = "--purge";
+
+ static void delete_directory(const fs::path& directory)
+ {
+ std::error_code ec;
+ fs::remove_all(directory, ec);
+ if (!ec)
+ {
+ System::println(System::color::success, "Cleaned up %s", directory.string());
+ }
+ if (fs::exists(directory))
+ {
+ System::println(System::color::warning, "Some files in %s were unable to be removed. Close any editors operating in this directory and retry.", directory.string());
+ }
+ }
+
+ void remove_command(const vcpkg_cmd_arguments& args, const vcpkg_paths& paths, const triplet& default_target_triplet)
+ {
+ const std::unordered_set<std::string> options = args.check_and_get_optional_command_arguments({OPTION_PURGE});
+ auto status_db = database_load_check(paths);
+
+ std::vector<package_spec> specs = args.parse_all_arguments_as_package_specs(default_target_triplet);
+ bool alsoRemoveFolderFromPackages = options.find(OPTION_PURGE) != options.end();
+
+ for (const package_spec& spec : specs)
+ {
+ deinstall_package(paths, spec, status_db);
+
+ if (alsoRemoveFolderFromPackages)
+ {
+ const fs::path spec_package_dir = paths.packages / spec.dir();
+ delete_directory(spec_package_dir);
+ }
+ }
+ exit(EXIT_SUCCESS);
+ }
+}
diff --git a/toolsrc/src/lib.cpp b/toolsrc/src/lib.cpp
new file mode 100644
index 000000000..2a3b95182
--- /dev/null
+++ b/toolsrc/src/lib.cpp
@@ -0,0 +1,524 @@
+#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;
+}
+
+fs::path vcpkg::control_file_for_package(const fs::path& package)
+{
+ return package / "CONTROL";
+}
+
+fs::path vcpkg::find_available_package(const vcpkg_paths& paths, const package_spec& spec)
+{
+ return paths.packages / Strings::format("%s_%s", spec.name, spec.target_triplet);
+}
+
+fs::path vcpkg::find_available_port_file(const vcpkg_paths& paths, const package_spec& spec)
+{
+ return paths.ports / spec.name;
+}
+
+static fs::path prefix_path_for_package(const vcpkg_paths& paths, const BinaryParagraph& pgh)
+{
+ return find_available_package(paths, {pgh.name, pgh.target_triplet});
+}
+
+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 = prefix_path_for_package(paths, bpgh);
+ auto prefix_length = package_prefix_path.native().size();
+
+ std::error_code ec;
+ fs::create_directory(paths.installed / bpgh.target_triplet.value, ec);
+ listfile << bpgh.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 / bpgh.target_triplet.value / suffix;
+
+ auto status = it->status(ec);
+ if (ec)
+ {
+ System::println(System::color::error, "failed: %s", ec.message());
+ continue;
+ }
+ if (fs::is_directory(status))
+ {
+ fs::create_directory(target, ec);
+ if (ec)
+ {
+ System::println(System::color::error, "failed: %s", ec.message());
+ }
+
+ listfile << bpgh.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", ec.message());
+ }
+ listfile << bpgh.target_triplet << "/" << suffix << "\n";
+ }
+ else if (!fs::status_known(status))
+ {
+ std::cout << "unknown status: " << *it << "\n";
+ }
+ else
+ std::cout << "warning: file does not exist: " << *it << "\n";
+ }
+
+ 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 = find_available_package(paths, spec) / "CONTROL";
+
+ if (fs::exists(packages_dir_control_file_path))
+ {
+ try
+ {
+ pghs = get_paragraphs(packages_dir_control_file_path);
+ }
+ catch (std::runtime_error)
+ {
+ // ??
+ }
+
+ Checks::check_throw(pghs.size() == 1, "Invalid control file for package");
+ return BinaryParagraph(pghs[0]).depends;
+ }
+
+ const fs::path ports_dir_control_file_path = find_available_port_file(paths, spec) / "CONTROL";
+ try
+ {
+ pghs = get_paragraphs(ports_dir_control_file_path);
+ }
+ catch (std::runtime_error)
+ {
+ // ??
+ }
+
+ Checks::check_exit(pghs.size() == 1, "Invalid control file for package %s", spec);
+ return SourceParagraph(pghs[0]).depends;
+}
+
+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.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.target_triplet != pkg.target_triplet)
+ continue;
+
+ const auto& deps = inst_pkg->package.depends;
+
+ if (std::find(deps.begin(), deps.end(), pkg.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", ec.message());
+ }
+ }
+ else if (!fs::status_known(status))
+ {
+ System::println(System::color::warning, "Warning: unknown status: %s", target.string());
+ }
+ else
+ {
+ System::println(System::color::warning, "Warning: ???: %s", target.string());
+ }
+ }
+
+ 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 = prefix_path_for_package(paths, control_file_data);
+ 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
new file mode 100644
index 000000000..bba1f3c63
--- /dev/null
+++ b/toolsrc/src/main.cpp
@@ -0,0 +1,245 @@
+#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"
+
+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 = fs::absolute(System::get_exe_path_of_current_process()).parent_path();
+ }
+
+ 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 = triplet::X86_WINDOWS;
+
+ if (args.target_triplet != nullptr)
+ {
+ const std::string& target_triplet = *args.target_triplet;
+
+ auto it = fs::directory_iterator(paths.triplets);
+ for (; it != fs::directory_iterator(); ++it)
+ {
+ std::string triplet_file_name = it->path().stem().generic_u8string();
+ if (target_triplet == triplet_file_name) // TODO: fuzzy compare
+ {
+ default_target_triplet = {triplet_file_name};
+ break;
+ }
+ }
+
+ if (it == fs::directory_iterator())
+ {
+ System::println(System::color::error, "Error: invalid triplet: %s", target_triplet);
+ TrackProperty("error", "invalid triplet: " + target_triplet);
+ help_topic_valid_triplet(paths);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ 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::Stopwatch 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
new file mode 100644
index 000000000..610c71ed1
--- /dev/null
+++ b/toolsrc/src/metrics.cpp
@@ -0,0 +1,425 @@
+#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 "vcpkg_Strings.h"
+#include "vcpkg_System.h"
+
+namespace fs = std::tr2::sys;
+
+namespace vcpkg
+{
+ static std::string GetCurrentDateTime()
+ {
+ struct tm newtime;
+ time_t now;
+ int milli;
+ std::array<char, 80> date;
+ date.fill(0);
+
+ struct _timeb timebuffer;
+
+ _ftime_s(&timebuffer);
+ now = timebuffer.time;
+ milli = timebuffer.millitm;
+
+ errno_t err = gmtime_s(&newtime, &now);
+ if (err)
+ {
+ return "";
+ }
+
+ strftime(&date[0], date.size(), "%Y-%m-%dT%H:%M:%S", &newtime);
+ return std::string(&date[0]) + "." + std::to_string(milli) + "Z";
+ }
+
+ static std::string GenerateRandomUUID()
+ {
+ int partSizes[] = {8, 4, 4, 4, 12};
+ char uuid[37];
+ memset(uuid, 0, sizeof(uuid));
+ int num;
+ srand(static_cast<int>(time(nullptr)));
+ int index = 0;
+ for (int part = 0; part < 5; part++)
+ {
+ if (part > 0)
+ {
+ uuid[index] = '-';
+ index++;
+ }
+
+ // Generating UUID format version 4
+ // http://en.wikipedia.org/wiki/Universally_unique_identifier
+ for (int i = 0; i < partSizes[part]; i++ , index++)
+ {
+ if (part == 2 && i == 0)
+ {
+ num = 4;
+ }
+ else if (part == 4 && i == 0)
+ {
+ num = (rand() % 4) + 8;
+ }
+ else
+ {
+ num = rand() % 16;
+ }
+
+ if (num < 10)
+ {
+ uuid[index] = static_cast<char>('0' + num);
+ }
+ else
+ {
+ uuid[index] = static_cast<char>('a' + (num - 10));
+ }
+ }
+ }
+
+ return uuid;
+ }
+
+ static const std::string& get_session_id()
+ {
+ static const std::string id = GenerateRandomUUID();
+ return id;
+ }
+
+ static std::string to_json_string(const std::string& str)
+ {
+ std::string encoded = "\"";
+ for (auto&& ch : str)
+ {
+ if (ch == '\\')
+ {
+ encoded.append("\\\\");
+ }
+ else if (ch == '"')
+ {
+ encoded.append("\\\"");
+ }
+ else if (ch < 0x20 || ch >= 0x80)
+ {
+ // Note: this treats incoming Strings as Latin-1
+ static constexpr const char hex[16] = {
+ '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
+ encoded.append("\\u00");
+ encoded.push_back(hex[ch / 16]);
+ encoded.push_back(hex[ch % 16]);
+ }
+ else
+ {
+ encoded.push_back(ch);
+ }
+ }
+ encoded.push_back('"');
+ return encoded;
+ }
+
+ static std::string get_os_version_string()
+ {
+ std::wstring path;
+ path.resize(MAX_PATH);
+ auto n = GetSystemDirectoryW(&path[0], static_cast<UINT>(path.size()));
+ path.resize(n);
+ path += L"\\kernel32.dll";
+
+ auto versz = GetFileVersionInfoSizeW(path.c_str(), nullptr);
+ if (versz == 0)
+ return "";
+
+ std::vector<char> verbuf;
+ verbuf.resize(versz);
+
+ if (!GetFileVersionInfoW(path.c_str(), 0, static_cast<DWORD>(verbuf.size()), &verbuf[0]))
+ return "";
+
+ void* rootblock;
+ UINT rootblocksize;
+ if (!VerQueryValueW(&verbuf[0], L"\\", &rootblock, &rootblocksize))
+ return "";
+
+ auto rootblock_ffi = static_cast<VS_FIXEDFILEINFO *>(rootblock);
+
+ return Strings::format("%d.%d.%d",
+ static_cast<int>(HIWORD(rootblock_ffi->dwProductVersionMS)),
+ static_cast<int>(LOWORD(rootblock_ffi->dwProductVersionMS)),
+ static_cast<int>(HIWORD(rootblock_ffi->dwProductVersionLS)));
+ }
+
+ struct MetricMessage
+ {
+ std::string user_id = GenerateRandomUUID();
+ std::string user_timestamp;
+ std::string timestamp = GetCurrentDateTime();
+ std::string properties;
+ std::string measurements;
+
+ void TrackProperty(const std::string& name, const std::string& value)
+ {
+ if (properties.size() != 0)
+ properties.push_back(',');
+ properties.append(to_json_string(name));
+ properties.push_back(':');
+ properties.append(to_json_string(value));
+ }
+
+ void TrackMetric(const std::string& name, double value)
+ {
+ if (measurements.size() != 0)
+ measurements.push_back(',');
+ measurements.append(to_json_string(name));
+ measurements.push_back(':');
+ measurements.append(std::to_string(value));
+ }
+
+ std::string format_event_data_template() const
+ {
+ const std::string& session_id = get_session_id();
+ return Strings::format(R"([{
+ "ver": 1,
+ "name": "Microsoft.ApplicationInsights.Event",
+ "time": "%s",
+ "sampleRate": 100.000000,
+ "seq": "0:0",
+ "iKey": "b4e88960-4393-4dd9-ab8e-97e8fe6d7603",
+ "flags": 0.000000,
+ "tags": {
+ "ai.device.os": "Windows",
+ "ai.device.osVersion": "%s",
+ "ai.session.id": "%s",
+ "ai.user.id": "%s",
+ "ai.user.accountAcquisitionDate": "%s"
+ },
+ "data": {
+ "baseType": "EventData",
+ "baseData": {
+ "ver": 2,
+ "name": "commandline_test7",
+ "properties": { %s },
+ "measurements": { %s }
+ }
+ }
+}])",
+ timestamp,
+ get_os_version_string(),
+ session_id,
+ user_id,
+ user_timestamp,
+ properties,
+ measurements);
+ }
+ };
+
+ static MetricMessage g_metricmessage;
+ static bool g_should_send_metrics =
+#if defined(NDEBUG) && (DISABLE_METRICS == 0)
+true
+#else
+ false
+#endif
+ ;
+ static bool g_should_print_metrics = false;
+
+ bool GetCompiledMetricsEnabled()
+ {
+ return DISABLE_METRICS == 0;
+ }
+
+ void SetUserInformation(const std::string& user_id, const std::string& first_use_time)
+ {
+ g_metricmessage.user_id = user_id;
+ g_metricmessage.user_timestamp = first_use_time;
+ }
+
+ void InitUserInformation(std::string& user_id, std::string& first_use_time)
+ {
+ user_id = GenerateRandomUUID();
+ first_use_time = GetCurrentDateTime();
+ }
+
+ void SetSendMetrics(bool should_send_metrics)
+ {
+ g_should_send_metrics = should_send_metrics;
+ }
+
+ void SetPrintMetrics(bool should_print_metrics)
+ {
+ g_should_print_metrics = should_print_metrics;
+ }
+
+ void TrackMetric(const std::string& name, double value)
+ {
+ g_metricmessage.TrackMetric(name, value);
+ }
+
+ void TrackProperty(const std::string& name, const std::wstring& value)
+ {
+ // Note: this is not valid UTF-16 -> UTF-8, it just yields a close enough approximation for our purposes.
+ std::string converted_value;
+ converted_value.resize(value.size());
+ std::transform(
+ value.begin(), value.end(),
+ converted_value.begin(),
+ [](wchar_t ch)
+ {
+ return static_cast<char>(ch);
+ });
+
+ g_metricmessage.TrackProperty(name, converted_value);
+ }
+
+ void TrackProperty(const std::string& name, const std::string& value)
+ {
+ g_metricmessage.TrackProperty(name, value);
+ }
+
+ void Upload(const std::string& payload)
+ {
+ HINTERNET hSession = nullptr, hConnect = nullptr, hRequest = nullptr;
+ BOOL bResults = FALSE;
+
+ hSession = WinHttpOpen(L"vcpkg/1.0",
+ WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
+ WINHTTP_NO_PROXY_NAME,
+ WINHTTP_NO_PROXY_BYPASS,
+ 0);
+ if (hSession)
+ hConnect = WinHttpConnect(hSession, L"dc.services.visualstudio.com", INTERNET_DEFAULT_HTTPS_PORT, 0);
+
+ if (hConnect)
+ hRequest = WinHttpOpenRequest(hConnect,
+ L"POST",
+ L"/v2/track",
+ nullptr,
+ WINHTTP_NO_REFERER,
+ WINHTTP_DEFAULT_ACCEPT_TYPES,
+ WINHTTP_FLAG_SECURE);
+
+ if (hRequest)
+ {
+ if (MAXDWORD <= payload.size())
+ abort();
+ std::wstring hdrs = L"Content-Type: application/json\r\n";
+ bResults = WinHttpSendRequest(hRequest,
+ hdrs.c_str(), static_cast<DWORD>(hdrs.size()),
+ (void*)&payload[0], static_cast<DWORD>(payload.size()), static_cast<DWORD>(payload.size()),
+ 0);
+ }
+
+ if (bResults)
+ {
+ bResults = WinHttpReceiveResponse(hRequest, nullptr);
+ }
+
+ DWORD http_code = 0, junk = sizeof(DWORD);
+
+ if (bResults)
+ {
+ bResults = WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, nullptr, &http_code, &junk, WINHTTP_NO_HEADER_INDEX);
+ }
+
+ std::vector<char> responseBuffer;
+ if (bResults)
+ {
+ DWORD availableData = 0, readData = 0, totalData = 0;
+
+ while ((bResults = WinHttpQueryDataAvailable(hRequest, &availableData)) && availableData > 0)
+ {
+ responseBuffer.resize(responseBuffer.size() + availableData);
+
+ bResults = WinHttpReadData(hRequest, &responseBuffer.data()[totalData], availableData, &readData);
+
+ if (!bResults)
+ {
+ break;
+ }
+
+ totalData += readData;
+
+ responseBuffer.resize(totalData);
+ }
+ }
+
+ if (!bResults)
+ {
+#ifndef NDEBUG
+ __debugbreak();
+ auto err = GetLastError();
+ std::cerr << "[DEBUG] failed to connect to server: " << err << "\n";
+#endif
+ }
+
+ if (hRequest)
+ WinHttpCloseHandle(hRequest);
+ if (hConnect)
+ WinHttpCloseHandle(hConnect);
+ if (hSession)
+ WinHttpCloseHandle(hSession);
+ }
+
+ static fs::path get_bindir()
+ {
+ wchar_t buf[_MAX_PATH ];
+ int bytes = GetModuleFileNameW(nullptr, buf, _MAX_PATH);
+ if (bytes == 0)
+ std::abort();
+ return fs::path(buf, buf + bytes);
+ }
+
+ void Flush()
+ {
+ std::string payload = g_metricmessage.format_event_data_template();
+ if (g_should_print_metrics)
+ std::cerr << payload << "\n";
+ if (!g_should_send_metrics)
+ return;
+
+ // Upload(payload);
+
+ wchar_t temp_folder[MAX_PATH];
+ GetTempPathW(MAX_PATH, temp_folder);
+
+ const fs::path temp_folder_path = temp_folder;
+ const fs::path temp_folder_path_exe = temp_folder_path / "vcpkgmetricsuploader.exe";
+
+ if (true)
+ {
+ const fs::path exe_path = []() -> fs::path
+ {
+ auto vcpkgdir = get_bindir().parent_path();
+ auto path = vcpkgdir / "vcpkgmetricsuploader.exe";
+ if (fs::exists(path))
+ return path;
+
+ path = vcpkgdir / "scripts" / "vcpkgmetricsuploader.exe";
+ if (fs::exists(path))
+ return path;
+
+ return L"";
+ }();
+
+ std::error_code ec;
+ fs::copy_file(exe_path, temp_folder_path_exe, fs::copy_options::skip_existing, ec);
+ if (ec)
+ return;
+ }
+
+ const fs::path vcpkg_metrics_txt_path = temp_folder_path / ("vcpkg" + GenerateRandomUUID() + ".txt");
+ std::ofstream(vcpkg_metrics_txt_path) << payload;
+
+ const std::wstring cmdLine = Strings::format(L"start %s %s", temp_folder_path_exe.native(), vcpkg_metrics_txt_path.native());
+ System::cmd_execute(cmdLine);
+ }
+}
diff --git a/toolsrc/src/package_spec.cpp b/toolsrc/src/package_spec.cpp
new file mode 100644
index 000000000..ece5f91e9
--- /dev/null
+++ b/toolsrc/src/package_spec.cpp
@@ -0,0 +1,46 @@
+#include "package_spec.h"
+
+namespace vcpkg
+{
+ expected<package_spec> parse(const std::string& spec, const triplet& default_target_triplet)
+ {
+ auto pos = spec.find(':');
+ if (pos == std::string::npos)
+ {
+ return package_spec{spec, default_target_triplet};
+ }
+
+ auto pos2 = spec.find(':', pos + 1);
+ if (pos2 != std::string::npos)
+ {
+ return std::error_code(package_spec_parse_result::too_many_colons);
+ }
+
+ return package_spec{spec.substr(0, pos), spec.substr(pos + 1)};
+ }
+
+ std::string package_spec::dir() const
+ {
+ return Strings::format("%s_%s", this->name, this->target_triplet);
+ }
+
+ std::string to_string(const package_spec& spec)
+ {
+ return Strings::format("%s:%s", spec.name, spec.target_triplet);
+ }
+
+ std::string to_printf_arg(const package_spec& spec)
+ {
+ return to_string(spec);
+ }
+
+ bool operator==(const package_spec& left, const package_spec& right)
+ {
+ return left.name == right.name && left.target_triplet == right.target_triplet;
+ }
+
+ std::ostream& operator<<(std::ostream& os, const package_spec& spec)
+ {
+ return os << to_string(spec);
+ }
+}
diff --git a/toolsrc/src/package_spec_parse_result.cpp b/toolsrc/src/package_spec_parse_result.cpp
new file mode 100644
index 000000000..757b6df53
--- /dev/null
+++ b/toolsrc/src/package_spec_parse_result.cpp
@@ -0,0 +1,45 @@
+#include <package_spec.h>
+#include <system_error>
+#include "package_spec_parse_result.h"
+
+namespace vcpkg
+{
+ const char* package_spec_parse_result_category_impl::name() const noexcept
+ {
+ return "package_spec_parse_result";
+ }
+
+ std::string package_spec_parse_result_category_impl::message(int ev) const noexcept
+ {
+ switch (static_cast<package_spec_parse_result>(ev))
+ {
+ case package_spec_parse_result::success:
+ return "OK";
+ case package_spec_parse_result::too_many_colons:
+ return "Too many colons";
+ default:
+ Checks::unreachable();
+ }
+ }
+
+ const std::error_category& package_spec_parse_result_category()
+ {
+ static package_spec_parse_result_category_impl instance;
+ return instance;
+ }
+
+ std::error_code make_error_code(package_spec_parse_result e)
+ {
+ return std::error_code(static_cast<int>(e), package_spec_parse_result_category());
+ }
+
+ package_spec_parse_result to_package_spec_parse_result(int i)
+ {
+ return static_cast<package_spec_parse_result>(i);
+ }
+
+ package_spec_parse_result to_package_spec_parse_result(std::error_code ec)
+ {
+ return to_package_spec_parse_result(ec.value());
+ }
+}
diff --git a/toolsrc/src/post_build_lint.cpp b/toolsrc/src/post_build_lint.cpp
new file mode 100644
index 000000000..3c4c5938d
--- /dev/null
+++ b/toolsrc/src/post_build_lint.cpp
@@ -0,0 +1,356 @@
+#include <filesystem>
+#include "vcpkg_paths.h"
+#include "package_spec.h"
+#include <iterator>
+#include <functional>
+#include "vcpkg_System.h"
+
+namespace fs = std::tr2::sys;
+
+namespace vcpkg
+{
+ enum class lint_status
+ {
+ SUCCESS = 0,
+ ERROR = 1
+ };
+
+ static const fs::path DUMPBIN_EXE = R"(C:\Program Files (x86)\Microsoft Visual Studio 14.0\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;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+ 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::format(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;
+ }
+
+ 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::format(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;
+ }
+
+ return lint_status::SUCCESS;
+ }
+
+ struct file_and_arch
+ {
+ fs::path file;
+ std::string actual_arch;
+ };
+
+ static lint_status check_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& f : files)
+ {
+ const std::wstring cmd_line = Strings::format(LR"("%s" /headers "%s" | findstr machine)", DUMPBIN_EXE.native(), f.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 (Strings::case_insensitive_find(ec_data.output, expected_architecture) == ec_data.output.end())
+ {
+ binaries_with_invalid_architecture.push_back({f, ec_data.output});
+ }
+ }
+
+ if (!binaries_with_invalid_architecture.empty())
+ {
+ 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:\n %s", expected_architecture, b.actual_arch);
+ }
+ System::println("");
+
+ return lint_status::ERROR;
+ }
+
+ return lint_status::SUCCESS;
+ }
+
+ static void operator +=(unsigned int& left, const lint_status& right)
+ {
+ left += static_cast<unsigned int>(right);
+ }
+
+ void perform_all_checks(const package_spec& spec, const vcpkg_paths& paths)
+ {
+ System::println("-- Performing post-build validation");
+ unsigned int 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);
+
+ 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_architecture(spec.target_triplet.architecture(), dlls);
+
+ 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_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 %d 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/test.cpp b/toolsrc/src/test.cpp
new file mode 100644
index 000000000..82113abaa
--- /dev/null
+++ b/toolsrc/src/test.cpp
@@ -0,0 +1,347 @@
+#include "CppUnitTest.h"
+#include "vcpkg.h"
+
+#pragma comment(lib,"version")
+#pragma comment(lib,"winhttp")
+
+using namespace Microsoft::VisualStudio::CppUnitTestFramework;
+
+namespace Microsoft { namespace VisualStudio { namespace 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
+{
+ TEST_CLASS(ControlParsing)
+ {
+ public:
+ TEST_METHOD(SourceParagraph_Construct_Minimum)
+ {
+ vcpkg::SourceParagraph pgh({
+ {"Source", "zlib"},
+ {"Version", "1.2.8"}
+ });
+
+ 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(0), pgh.depends.size());
+ }
+
+ TEST_METHOD(SourceParagraph_Construct_Maximum)
+ {
+ vcpkg::SourceParagraph pgh({
+ {"Source", "s"},
+ {"Version", "v"},
+ {"Maintainer", "m"},
+ {"Description", "d"},
+ {"Build-Depends", "bd"}
+ });
+ Assert::AreEqual("s", pgh.name.c_str());
+ Assert::AreEqual("v", pgh.version.c_str());
+ 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());
+ }
+
+ TEST_METHOD(SourceParagraph_Two_Depends)
+ {
+ vcpkg::SourceParagraph pgh({
+ {"Source", "zlib"},
+ {"Version", "1.2.8"},
+ {"Build-Depends", "z, openssl"}
+ });
+
+ Assert::AreEqual(size_t(2), pgh.depends.size());
+ Assert::AreEqual("z", pgh.depends[0].c_str());
+ Assert::AreEqual("openssl", pgh.depends[1].c_str());
+ }
+
+ TEST_METHOD(SourceParagraph_Three_Depends)
+ {
+ vcpkg::SourceParagraph pgh({
+ {"Source", "zlib"},
+ {"Version", "1.2.8"},
+ {"Build-Depends", "z, openssl, xyz"}
+ });
+
+ 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());
+ }
+
+ TEST_METHOD(BinaryParagraph_Construct_Minimum)
+ {
+ vcpkg::BinaryParagraph pgh({
+ {"Package", "zlib"},
+ {"Version", "1.2.8"},
+ {"Architecture", "a"},
+ {"Multi-Arch", "same"},
+ });
+
+ 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("a", pgh.target_triplet.value.c_str());
+ Assert::AreEqual(size_t(0), pgh.depends.size());
+ }
+
+ TEST_METHOD(BinaryParagraph_Construct_Maximum)
+ {
+ vcpkg::BinaryParagraph pgh({
+ {"Package", "s"},
+ {"Version", "v"},
+ {"Architecture", "a"},
+ {"Multi-Arch", "same"},
+ {"Maintainer", "m"},
+ {"Description", "d"},
+ {"Depends", "bd"}
+ });
+ Assert::AreEqual("s", pgh.name.c_str());
+ Assert::AreEqual("v", pgh.version.c_str());
+ 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());
+ }
+
+ TEST_METHOD(BinaryParagraph_Three_Depends)
+ {
+ vcpkg::BinaryParagraph pgh({
+ {"Package", "zlib"},
+ {"Version", "1.2.8"},
+ {"Architecture", "a"},
+ {"Multi-Arch", "same"},
+ {"Depends", "a, b, c"},
+ });
+
+ Assert::AreEqual(size_t(3), pgh.depends.size());
+ Assert::AreEqual("a", pgh.depends[0].c_str());
+ Assert::AreEqual("b", pgh.depends[1].c_str());
+ Assert::AreEqual("c", pgh.depends[2].c_str());
+ }
+
+ TEST_METHOD(parse_paragraphs_empty)
+ {
+ const char* str = "";
+ auto pghs = vcpkg::parse_paragraphs(str);
+ Assert::IsTrue(pghs.empty());
+ }
+
+ TEST_METHOD(parse_paragraphs_one_field)
+ {
+ const char* str = "f1: v1";
+ auto pghs = vcpkg::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());
+ }
+
+ TEST_METHOD(parse_paragraphs_one_pgh)
+ {
+ const char* str =
+ "f1: v1\n"
+ "f2: v2";
+ auto pghs = vcpkg::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());
+ Assert::AreEqual("v2", pghs[0]["f2"].c_str());
+ }
+
+ TEST_METHOD(parse_paragraphs_two_pgh)
+ {
+ const char* str =
+ "f1: v1\n"
+ "f2: v2\n"
+ "\n"
+ "f3: v3\n"
+ "f4: v4";
+ auto pghs = vcpkg::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());
+ Assert::AreEqual("v2", pghs[0]["f2"].c_str());
+ Assert::AreEqual(size_t(2), pghs[1].size());
+ Assert::AreEqual("v3", pghs[1]["f3"].c_str());
+ Assert::AreEqual("v4", pghs[1]["f4"].c_str());
+ }
+
+ TEST_METHOD(parse_paragraphs_field_names)
+ {
+ const char* str =
+ "1:\n"
+ "f:\n"
+ "F:\n"
+ "0:\n"
+ "F-2:\n";
+ auto pghs = vcpkg::parse_paragraphs(str);
+ Assert::AreEqual(size_t(1), pghs.size());
+ Assert::AreEqual(size_t(5), pghs[0].size());
+ }
+
+ TEST_METHOD(parse_paragraphs_multiple_blank_lines)
+ {
+ const char* str =
+ "f1: v1\n"
+ "f2: v2\n"
+ "\n"
+ "\n"
+ "f3: v3\n"
+ "f4: v4";
+ auto pghs = vcpkg::parse_paragraphs(str);
+ Assert::AreEqual(size_t(2), pghs.size());
+ }
+
+ TEST_METHOD(parse_paragraphs_empty_fields)
+ {
+ const char* str =
+ "f1:\n"
+ "f2: ";
+ auto pghs = vcpkg::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());
+ Assert::AreEqual("", pghs[0]["f2"].c_str());
+ Assert::AreEqual(size_t(2), pghs[0].size());
+ }
+
+ TEST_METHOD(parse_paragraphs_multiline_fields)
+ {
+ const char* str =
+ "f1: simple\n"
+ " f1\r\n"
+ "f2:\r\n"
+ " f2\r\n"
+ " continue\r\n";
+ auto pghs = vcpkg::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());
+ }
+
+ TEST_METHOD(parse_paragraphs_crlfs)
+ {
+ const char* str =
+ "f1: v1\r\n"
+ "f2: v2\r\n"
+ "\r\n"
+ "f3: v3\r\n"
+ "f4: v4";
+ auto pghs = vcpkg::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());
+ Assert::AreEqual("v2", pghs[0]["f2"].c_str());
+ Assert::AreEqual(size_t(2), pghs[1].size());
+ Assert::AreEqual("v3", pghs[1]["f3"].c_str());
+ Assert::AreEqual("v4", pghs[1]["f4"].c_str());
+ }
+
+ TEST_METHOD(BinaryParagraph_serialize_min)
+ {
+ std::stringstream ss;
+ vcpkg::BinaryParagraph pgh({
+ {"Package", "zlib"},
+ {"Version", "1.2.8"},
+ {"Architecture", "a"},
+ {"Multi-Arch", "same"},
+ });
+ ss << pgh;
+ auto pghs = vcpkg::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("same", pghs[0]["Multi-Arch"].c_str());
+ }
+
+ TEST_METHOD(BinaryParagraph_serialize_max)
+ {
+ std::stringstream ss;
+ vcpkg::BinaryParagraph pgh({
+ {"Package", "zlib"},
+ {"Version", "1.2.8"},
+ {"Architecture", "a"},
+ {"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());
+ 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("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());
+ }
+
+ TEST_METHOD(BinaryParagraph_serialize_multiple_deps)
+ {
+ std::stringstream ss;
+ vcpkg::BinaryParagraph pgh({
+ {"Package", "zlib"},
+ {"Version", "1.2.8"},
+ {"Architecture", "a"},
+ {"Multi-Arch", "same"},
+ {"Depends", "a, b, c"},
+ });
+ ss << pgh;
+ auto pghs = vcpkg::parse_paragraphs(ss.str());
+ Assert::AreEqual(size_t(1), pghs.size());
+ Assert::AreEqual("a, b, c", pghs[0]["Depends"].c_str());
+ }
+
+ TEST_METHOD(package_spec_parse)
+ {
+ vcpkg::expected<vcpkg::package_spec> spec = vcpkg::parse("zlib", vcpkg::triplet::X86_WINDOWS);
+ Assert::AreEqual(vcpkg::package_spec_parse_result::success, vcpkg::to_package_spec_parse_result(spec.error_code()));
+ Assert::AreEqual("zlib", spec.get()->name.c_str());
+ Assert::AreEqual(vcpkg::triplet::X86_WINDOWS.value, spec.get()->target_triplet.value);
+ }
+
+ TEST_METHOD(package_spec_parse_with_arch)
+ {
+ vcpkg::expected<vcpkg::package_spec> spec = vcpkg::parse("zlib:x64-uwp", vcpkg::triplet::X86_WINDOWS);
+ Assert::AreEqual(vcpkg::package_spec_parse_result::success, vcpkg::to_package_spec_parse_result(spec.error_code()));
+ Assert::AreEqual("zlib", spec.get()->name.c_str());
+ Assert::AreEqual(vcpkg::triplet::X64_UWP.value, spec.get()->target_triplet.value);
+ }
+
+ TEST_METHOD(package_spec_parse_with_multiple_colon)
+ {
+ auto ec = vcpkg::parse("zlib:x86-uwp:", vcpkg::triplet::X86_WINDOWS).error_code();
+ Assert::AreEqual(vcpkg::package_spec_parse_result::too_many_colons, vcpkg::to_package_spec_parse_result(ec));
+ }
+
+ TEST_METHOD(utf8_to_utf16)
+ {
+ auto str = vcpkg::Strings::utf8_to_utf16("abc");
+ Assert::AreEqual(L"abc", str.c_str());
+ }
+
+ TEST_METHOD(utf8_to_utf16_with_whitespace)
+ {
+ auto str = vcpkg::Strings::utf8_to_utf16("abc -x86-windows");
+ Assert::AreEqual(L"abc -x86-windows", str.c_str());
+ }
+ };
+
+ TEST_CLASS(Metrics)
+ {
+ };
+}
diff --git a/toolsrc/src/triplet.cpp b/toolsrc/src/triplet.cpp
new file mode 100644
index 000000000..9ad3d8847
--- /dev/null
+++ b/toolsrc/src/triplet.cpp
@@ -0,0 +1,59 @@
+#include "triplet.h"
+#include "vcpkg_System.h"
+#include "vcpkg_Checks.h"
+
+namespace vcpkg
+{
+ const triplet triplet::X86_WINDOWS = {"x86-windows"};
+ const triplet triplet::X64_WINDOWS = {"x64-windows"};
+ const triplet triplet::X86_UWP = {"x86-uwp"};
+ const triplet triplet::X64_UWP = {"x64-uwp"};
+ const triplet triplet::ARM_UWP = {"arm-uwp"};
+
+ std::string to_string(const triplet& t)
+ {
+ return t.value;
+ }
+
+ std::string to_printf_arg(const triplet& t)
+ {
+ return to_string(t);
+ }
+
+ bool operator==(const triplet& left, const triplet& right)
+ {
+ return left.value == right.value;
+ }
+
+ bool operator!=(const triplet& left, const triplet& right)
+ {
+ return !(left == right);
+ }
+
+ std::ostream& operator<<(std::ostream& os, const triplet& t)
+ {
+ return os << to_string(t);
+ }
+
+ std::string triplet::architecture() const
+ {
+ if (*this == X86_WINDOWS || *this == X86_UWP)
+ return "x86";
+ if (*this == X64_WINDOWS || *this == X64_UWP)
+ return "x64";
+ if (*this == ARM_UWP)
+ return "arm";
+
+ Checks::exit_with_message("Unknown architecture: %s", value);
+ }
+
+ std::string triplet::system() const
+ {
+ if (*this == X86_WINDOWS || *this == X64_WINDOWS)
+ return "windows";
+ if (*this == X86_UWP || *this == X64_UWP || *this == ARM_UWP)
+ return "uwp";
+
+ Checks::exit_with_message("Unknown system: %s", value);
+ }
+}
diff --git a/toolsrc/src/vcpkg.cpp b/toolsrc/src/vcpkg.cpp
new file mode 100644
index 000000000..f705858cc
--- /dev/null
+++ b/toolsrc/src/vcpkg.cpp
@@ -0,0 +1,178 @@
+#include "vcpkg.h"
+#include <regex>
+#include "vcpkg_Files.h"
+#include "vcpkglib_helpers.h"
+
+namespace
+{
+ using namespace vcpkg;
+
+ 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;
+ }
+ };
+}
+
+namespace vcpkg
+{
+ 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;
+ }
+
+ std::vector<std::unordered_map<std::string, std::string>> get_paragraphs(const fs::path& control_path)
+ {
+ return parse_paragraphs(Files::get_contents(control_path).get_or_throw());
+ }
+
+ 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/vcpkg_Checks.cpp b/toolsrc/src/vcpkg_Checks.cpp
new file mode 100644
index 000000000..d5433b1f5
--- /dev/null
+++ b/toolsrc/src/vcpkg_Checks.cpp
@@ -0,0 +1,41 @@
+#include "vcpkg_Checks.h"
+
+#include <stdexcept>
+#include "vcpkg_System.h"
+
+namespace vcpkg {namespace Checks
+{
+ void unreachable()
+ {
+ System::println(System::color::error, "Error: Unreachable code was reached");
+ exit(EXIT_FAILURE);
+ }
+
+ void exit_with_message(const char* errorMessage)
+ {
+ System::println(System::color::error, errorMessage);
+ exit(EXIT_FAILURE);
+ }
+
+ void throw_with_message(const char* errorMessage)
+ {
+ throw std::runtime_error(errorMessage);
+ }
+
+ void check_throw(bool expression, const char* errorMessage)
+ {
+ if (!expression)
+ {
+ throw_with_message(errorMessage);
+ }
+ }
+
+ void check_exit(bool expression, const char* errorMessage)
+ {
+ if (!expression)
+ {
+ System::println(System::color::error, errorMessage);
+ exit(EXIT_FAILURE);
+ }
+ }
+}}
diff --git a/toolsrc/src/vcpkg_Environment.cpp b/toolsrc/src/vcpkg_Environment.cpp
new file mode 100644
index 000000000..f70f2b893
--- /dev/null
+++ b/toolsrc/src/vcpkg_Environment.cpp
@@ -0,0 +1,87 @@
+#include <regex>
+#include <array>
+#include "vcpkg_Environment.h"
+#include "vcpkg_Commands.h"
+#include "vcpkg.h"
+#include "metrics.h"
+#include "vcpkg_System.h"
+
+namespace vcpkg {namespace 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)
+ {
+ System::exit_code_and_output ec_data = System::cmd_execute_and_capture_output(version_check_cmd);
+ if (ec_data.exit_code == 0)
+ {
+ // version check
+ std::regex re(R"###((\d+)\.(\d+)\.(\d+))###");
+ std::match_results<std::string::const_iterator> match;
+ auto found = std::regex_search(ec_data.output, match, re);
+ if (found)
+ {
+ int d1 = atoi(match[1].str().c_str());
+ int d2 = atoi(match[2].str().c_str());
+ int d3 = atoi(match[3].str().c_str());
+ if (d1 > version[0] || (d1 == version[0] && d2 > version[1]) || (d1 == version[0] && d2 == version[1] && d3 >= version[2]))
+ {
+ // satisfactory version found
+ return;
+ }
+ }
+ }
+
+ auto rc = System::cmd_execute(install_cmd);
+ if (rc)
+ {
+ System::println(System::color::error, "Launching powershell failed or was denied");
+ TrackProperty("error", "powershell install failed");
+ TrackProperty("installcmd", install_cmd);
+ exit(rc);
+ }
+ }
+
+ void ensure_git_on_path(const vcpkg_paths& paths)
+ {
+ const fs::path downloaded_git = paths.downloads / "PortableGit" / "cmd";
+ const std::wstring path_buf = Strings::format(L"%s;%s;%s;%s",
+ downloaded_git.native(),
+ System::wdupenv_str(L"PATH"),
+ default_git_installation_dir.native(),
+ default_git_installation_dir_x86.native());
+ _wputenv_s(L"PATH", path_buf.c_str());
+
+ static constexpr std::array<int, 3> git_version = {2,0,0};
+ // TODO: switch out ExecutionPolicy Bypass with "Remove Mark Of The Web" code and restore RemoteSigned
+ ensure_on_path(git_version, L"git --version 2>&1", L"powershell -ExecutionPolicy Bypass scripts\\fetchDependency.ps1 -Dependency git");
+ }
+
+ void ensure_cmake_on_path(const vcpkg_paths& paths)
+ {
+ const fs::path downloaded_cmake = paths.downloads / "cmake-3.5.2-win32-x86" / "bin";
+ const std::wstring path_buf = Strings::format(L"%s;%s;%s;%s",
+ downloaded_cmake.native(),
+ System::wdupenv_str(L"PATH"),
+ default_cmake_installation_dir.native(),
+ default_cmake_installation_dir_x86.native());
+ _wputenv_s(L"PATH", path_buf.c_str());
+
+ static constexpr std::array<int, 3> cmake_version = {3,5,0};
+ // TODO: switch out ExecutionPolicy Bypass with "Remove Mark Of The Web" code and restore RemoteSigned
+ ensure_on_path(cmake_version, L"cmake --version 2>&1", L"powershell -ExecutionPolicy Bypass scripts\\fetchDependency.ps1 -Dependency cmake");
+ }
+
+ void ensure_nuget_on_path(const vcpkg_paths& paths)
+ {
+ const std::wstring path_buf = Strings::format(L"%s;%s", paths.downloads.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");
+ }
+}}
diff --git a/toolsrc/src/vcpkg_Files.cpp b/toolsrc/src/vcpkg_Files.cpp
new file mode 100644
index 000000000..49a661157
--- /dev/null
+++ b/toolsrc/src/vcpkg_Files.cpp
@@ -0,0 +1,38 @@
+#include "vcpkg_Files.h"
+#include <fstream>
+#include <filesystem>
+
+namespace fs = std::tr2::sys;
+
+namespace vcpkg {namespace Files
+{
+ void check_is_directory(const fs::path& dirpath)
+ {
+ Checks::check_throw(fs::is_directory(dirpath), "The path %s is not a directory", dirpath.string());
+ }
+
+ expected<std::string> get_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())
+ {
+ return std::errc::no_such_file_or_directory;
+ }
+
+ file_stream.seekg(0, file_stream.end);
+ auto length = file_stream.tellg();
+ file_stream.seekg(0, file_stream.beg);
+
+ if (length > SIZE_MAX)
+ {
+ return std::errc::file_too_large;
+ }
+
+ std::string output;
+ output.resize(static_cast<size_t>(length));
+ file_stream.read(&output[0], length);
+ file_stream.close();
+
+ return std::move(output);
+ }
+}}
diff --git a/toolsrc/src/vcpkg_Strings.cpp b/toolsrc/src/vcpkg_Strings.cpp
new file mode 100644
index 000000000..b0312536a
--- /dev/null
+++ b/toolsrc/src/vcpkg_Strings.cpp
@@ -0,0 +1,60 @@
+#include "vcpkg_Strings.h"
+
+#include <cstdarg>
+#include <algorithm>
+#include <codecvt>
+#include <iterator>
+
+namespace vcpkg {namespace Strings {namespace details
+{
+ std::string format_internal(const char* fmtstr, ...)
+ {
+ va_list lst;
+ va_start(lst, fmtstr);
+
+ auto sz = _vscprintf(fmtstr, lst);
+ std::string output(sz, '\0');
+ _vsnprintf_s(&output[0], output.size() + 1, output.size() + 1, fmtstr, lst);
+ va_end(lst);
+
+ return output;
+ }
+
+ std::wstring format_internal(const wchar_t* fmtstr, ...)
+ {
+ va_list lst;
+ va_start(lst, fmtstr);
+
+ auto sz = _vscwprintf(fmtstr, lst);
+ std::wstring output(sz, '\0');
+ _vsnwprintf_s(&output[0], output.size() + 1, output.size() + 1, fmtstr, lst);
+ va_end(lst);
+
+ return output;
+ }
+}}}
+
+namespace vcpkg {namespace Strings
+{
+ std::wstring utf8_to_utf16(const std::string& s)
+ {
+ std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t> conversion;
+ return conversion.from_bytes(s);
+ }
+
+ std::string utf16_to_utf8(const std::wstring& w)
+ {
+ std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t> conversion;
+ return conversion.to_bytes(w);
+ }
+
+ std::string::const_iterator case_insensitive_find(const std::string& s, const std::string& pattern)
+ {
+ std::string patter_as_lower_case;
+ std::transform(pattern.begin(), pattern.end(), back_inserter(patter_as_lower_case), tolower);
+ return search(s.begin(), s.end(), patter_as_lower_case.begin(), patter_as_lower_case.end(), [](const char a, const char b)
+ {
+ return (tolower(a) == b);
+ });
+ }
+}}
diff --git a/toolsrc/src/vcpkg_System.cpp b/toolsrc/src/vcpkg_System.cpp
new file mode 100644
index 000000000..71b4087d2
--- /dev/null
+++ b/toolsrc/src/vcpkg_System.cpp
@@ -0,0 +1,111 @@
+#include "vcpkg_System.h"
+#include <iostream>
+#include <Windows.h>
+#include <regex>
+
+namespace fs = std::tr2::sys;
+
+namespace vcpkg {namespace System
+{
+ fs::path get_exe_path_of_current_process()
+ {
+ wchar_t buf[_MAX_PATH ];
+ int bytes = GetModuleFileNameW(nullptr, buf, _MAX_PATH);
+ if (bytes == 0)
+ std::abort();
+ return fs::path(buf, buf + bytes);
+ }
+
+ int cmd_execute(const wchar_t* cmd_line)
+ {
+ // Basically we are wrapping it in quotes
+ const std::wstring& actual_cmd_line = Strings::format(LR"###("%s")###", cmd_line);
+ int exit_code = _wsystem(actual_cmd_line.c_str());
+ return exit_code;
+ }
+
+ exit_code_and_output cmd_execute_and_capture_output(const wchar_t* cmd_line)
+ {
+ const std::wstring& actual_cmd_line = Strings::format(LR"###("%s")###", cmd_line);
+
+ std::string output;
+ char buf[1024];
+ auto pipe = _wpopen(actual_cmd_line.c_str(), L"r");
+ if (pipe == nullptr)
+ {
+ return {1, output};
+ }
+ while (fgets(buf, 1024, pipe))
+ {
+ output.append(buf);
+ }
+ if (!feof(pipe))
+ {
+ return {1, output};
+ }
+ auto ec = _pclose(pipe);
+ return {ec, output};
+ }
+
+ void print(const char* message)
+ {
+ std::cout << message;
+ }
+
+ void println(const char* message)
+ {
+ print(message);
+ std::cout << "\n";
+ }
+
+ void print(color c, const char* message)
+ {
+ HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
+
+ CONSOLE_SCREEN_BUFFER_INFO consoleScreenBufferInfo{};
+ GetConsoleScreenBufferInfo(hConsole, &consoleScreenBufferInfo);
+ auto original_color = consoleScreenBufferInfo.wAttributes;
+
+ SetConsoleTextAttribute(hConsole, static_cast<int>(c) | (original_color & 0xF0));
+ std::cout << message;
+ SetConsoleTextAttribute(hConsole, original_color);
+ }
+
+ void println(color c, const char* message)
+ {
+ print(c, message);
+ std::cout << "\n";
+ }
+
+ std::wstring wdupenv_str(const wchar_t* varname) noexcept
+ {
+ std::wstring ret;
+ wchar_t* buffer;
+ _wdupenv_s(&buffer, nullptr, varname);
+ if (buffer != nullptr)
+ {
+ ret = buffer;
+ free(buffer);
+ }
+ return ret;
+ }
+
+ void Stopwatch::start()
+ {
+ static_assert(sizeof(start_time) == sizeof(LARGE_INTEGER), "");
+
+ QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER*>(&start_time));
+ }
+
+ void Stopwatch::stop()
+ {
+ QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER*>(&end_time));
+ QueryPerformanceFrequency(reinterpret_cast<LARGE_INTEGER*>(&freq));
+ }
+
+ double Stopwatch::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;
+ }
+}}
diff --git a/toolsrc/src/vcpkg_cmd_arguments.cpp b/toolsrc/src/vcpkg_cmd_arguments.cpp
new file mode 100644
index 000000000..4cfc12716
--- /dev/null
+++ b/toolsrc/src/vcpkg_cmd_arguments.cpp
@@ -0,0 +1,255 @@
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.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
+{
+ static void parse_value(
+ const std::string* arg_begin,
+ const std::string* arg_end,
+ const std::string& option_name,
+ std::unique_ptr<std::string>& option_field)
+ {
+ if (arg_begin == arg_end)
+ {
+ System::println(System::color::error, "Error: expected value after %s", option_name);
+ TrackProperty("error", "error option name");
+ print_usage();
+ exit(EXIT_FAILURE);
+ }
+
+ if (option_field != nullptr)
+ {
+ System::println(System::color::error, "Error: %s specified multiple times", option_name);
+ TrackProperty("error", "error option specified multiple times");
+ print_usage();
+ exit(EXIT_FAILURE);
+ }
+
+ option_field = std::make_unique<std::string>(*arg_begin);
+ }
+
+ static void parse_switch(
+ opt_bool new_setting,
+ const std::string& option_name,
+ opt_bool& option_field)
+ {
+ if (option_field != opt_bool::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();
+ exit(EXIT_FAILURE);
+ }
+ option_field = new_setting;
+ }
+
+ vcpkg_cmd_arguments vcpkg_cmd_arguments::create_from_command_line(const int argc, const wchar_t* const* const argv)
+ {
+ std::vector<std::string> v;
+ for (int i = 1; i < argc; ++i)
+ {
+ v.push_back(Strings::utf16_to_utf8(argv[i]));
+ }
+
+ return vcpkg_cmd_arguments::create_from_arg_sequence(v.data(), v.data() + v.size());
+ }
+
+ vcpkg_cmd_arguments vcpkg_cmd_arguments::create_from_arg_sequence(const std::string* arg_begin, const std::string* arg_end)
+ {
+ vcpkg_cmd_arguments args;
+
+ for (; arg_begin != arg_end; ++arg_begin)
+ {
+ std::string arg = *arg_begin;
+
+ if (arg.empty())
+ {
+ continue;
+ }
+
+ if (arg[0] == '-' && arg[1] != '-')
+ {
+ System::println(System::color::error, "Error: short options are not supported: %s", arg);
+ TrackProperty("error", "error short options are not supported");
+ exit(EXIT_FAILURE);
+ }
+
+ if (arg[0] == '-' && arg[1] == '-')
+ {
+ // command switch
+ if (arg == "--vcpkg-root")
+ {
+ ++arg_begin;
+ parse_value(arg_begin, arg_end, "--vcpkg-root", args.vcpkg_root_dir);
+ continue;
+ }
+ if (arg == "--triplet")
+ {
+ ++arg_begin;
+ parse_value(arg_begin, arg_end, "--triplet", args.target_triplet);
+ continue;
+ }
+ if (arg == "--debug")
+ {
+ parse_switch(opt_bool::enabled, "debug", args.debug);
+ continue;
+ }
+ if (arg == "--sendmetrics")
+ {
+ parse_switch(opt_bool::enabled, "sendmetrics", args.sendmetrics);
+ continue;
+ }
+ if (arg == "--printmetrics")
+ {
+ parse_switch(opt_bool::enabled, "printmetrics", args.printmetrics);
+ continue;
+ }
+ if (arg == "--no-sendmetrics")
+ {
+ parse_switch(opt_bool::disabled, "sendmetrics", args.sendmetrics);
+ continue;
+ }
+ if (arg == "--no-printmetrics")
+ {
+ parse_switch(opt_bool::disabled, "printmetrics", args.printmetrics);
+ continue;
+ }
+
+ args.optional_command_arguments.insert(arg);
+ continue;
+ }
+
+ if (args.command.empty())
+ {
+ args.command = arg;
+ }
+ else
+ {
+ args.command_arguments.push_back(arg);
+ }
+ }
+
+ return args;
+ }
+
+ std::unordered_set<std::string> vcpkg_cmd_arguments::check_and_get_optional_command_arguments(const std::vector<std::string>& valid_options) const
+ {
+ std::unordered_set<std::string> output;
+ auto options_copy = this->optional_command_arguments;
+ for (const std::string& option : valid_options)
+ {
+ auto it = options_copy.find(option);
+ if (it != options_copy.end())
+ {
+ output.insert(option);
+ options_copy.erase(it);
+ }
+ }
+
+ if (!options_copy.empty())
+ {
+ 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());
+ }
+ exit(EXIT_FAILURE);
+ }
+
+ return output;
+ }
+
+ void vcpkg_cmd_arguments::check_max_args(size_t arg_count, const char* example_text) const
+ {
+ if (command_arguments.size() > arg_count)
+ {
+ System::println(System::color::error, "Error: too many arguments to command %s", command);
+ if (example_text != nullptr)
+ print_example(example_text);
+ else
+ print_usage();
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ std::vector<package_spec> vcpkg_cmd_arguments::extract_package_specs_with_unmet_dependencies(const vcpkg_paths& paths, const triplet& default_target_triplet, const StatusParagraphs& status_db) const
+ {
+ std::vector<package_spec> specs = parse_all_arguments_as_package_specs(default_target_triplet);
+ std::unordered_set<package_spec> had_its_immediate_dependencies_added;
+ Graphs::Graph<package_spec> graph;
+ graph.add_vertices(specs);
+
+ while (!specs.empty())
+ {
+ package_spec spec = specs.back();
+ specs.pop_back();
+
+ if (had_its_immediate_dependencies_added.find(spec) != had_its_immediate_dependencies_added.end())
+ {
+ continue;
+ }
+
+ std::vector<std::string> dependencies_as_string = get_unmet_package_dependencies(paths, spec, status_db);
+
+ for (const std::string& dep_as_string : dependencies_as_string)
+ {
+ package_spec current_dep = {dep_as_string, spec.target_triplet};
+ auto it = status_db.find(current_dep.name, current_dep.target_triplet);
+ if (it != status_db.end() && (*it)->want == want_t::install)
+ {
+ continue;
+ }
+
+ graph.add_edge(spec, current_dep);
+ if (had_its_immediate_dependencies_added.find(current_dep) == had_its_immediate_dependencies_added.end())
+ {
+ specs.push_back(std::move(current_dep));
+ }
+ }
+
+ had_its_immediate_dependencies_added.insert(spec);
+ }
+
+ return graph.find_topological_sort();
+ }
+
+ std::vector<package_spec> vcpkg_cmd_arguments::parse_all_arguments_as_package_specs(const triplet& default_target_triplet, const char* example_text) const
+ {
+ size_t arg_count = command_arguments.size();
+ if (arg_count < 1)
+ {
+ System::println(System::color::error, "Error: %s requires one or more package specifiers", this->command);
+ if (example_text == nullptr)
+ print_example(Strings::format("%s zlib zlib:x64-windows curl boost", this->command).c_str());
+ else
+ print_example(example_text);
+ exit(EXIT_FAILURE);
+ }
+ std::vector<package_spec> specs;
+ specs.reserve(arg_count);
+
+ for (const std::string& command_argument : command_arguments)
+ {
+ expected<package_spec> current_spec = vcpkg::parse(command_argument, default_target_triplet);
+ if (auto spec = current_spec.get())
+ {
+ specs.push_back(std::move(*spec));
+ }
+ else
+ {
+ System::println(System::color::error, "Error: %s: %s", current_spec.error_code().message(), command_argument);
+ print_example(Strings::format("%s zlib:x64-windows", this->command).c_str());
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ return specs;
+ }
+}
diff --git a/toolsrc/src/vcpkg_metrics_uploader.cpp b/toolsrc/src/vcpkg_metrics_uploader.cpp
new file mode 100644
index 000000000..f1f4a52ed
--- /dev/null
+++ b/toolsrc/src/vcpkg_metrics_uploader.cpp
@@ -0,0 +1,25 @@
+#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
+WinMain(
+ _In_ HINSTANCE hInstance,
+ _In_opt_ HINSTANCE hPrevInstance,
+ _In_ LPSTR lpCmdLine,
+ _In_ int nShowCmd
+)
+{
+ LPWSTR* szArgList;
+ int argCount;
+
+ 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());
+}
diff --git a/toolsrc/src/vcpkg_paths.cpp b/toolsrc/src/vcpkg_paths.cpp
new file mode 100644
index 000000000..10b6d992a
--- /dev/null
+++ b/toolsrc/src/vcpkg_paths.cpp
@@ -0,0 +1,48 @@
+#include <filesystem>
+#include "expected.h"
+#include "vcpkg_paths.h"
+#include "metrics.h"
+#include "vcpkg_System.h"
+
+namespace fs = std::tr2::sys;
+
+namespace vcpkg
+{
+ expected<vcpkg_paths> vcpkg_paths::create(const fs::path& vcpkg_root_dir)
+ {
+ std::error_code ec;
+ const fs::path canonical_vcpkg_root_dir = fs::canonical(vcpkg_root_dir, ec);
+ if (ec)
+ {
+ return ec;
+ }
+
+ vcpkg_paths paths;
+ paths.root = canonical_vcpkg_root_dir;
+
+ if (paths.root.empty())
+ {
+ System::println(System::color::error, "Invalid vcpkg root directory: %s", paths.root.string());
+ TrackProperty("error", "Invalid vcpkg root directory");
+ exit(EXIT_FAILURE);
+ }
+
+ paths.packages = paths.root / "packages";
+ paths.buildtrees = paths.root / "buildtrees";
+ paths.downloads = paths.root / "downloads";
+ paths.ports = paths.root / "ports";
+ paths.installed = paths.root / "installed";
+ paths.triplets = paths.root / "triplets";
+
+ paths.buildsystems = paths.root / "scripts" / "buildsystems";
+ paths.buildsystems_msbuild_targets = paths.buildsystems / "msbuild" / "vcpkg.targets";
+
+ paths.vcpkg_dir = paths.installed / "vcpkg";
+ paths.vcpkg_dir_status_file = paths.vcpkg_dir / "status";
+ paths.vcpkg_dir_info = paths.vcpkg_dir / "info";
+ paths.vcpkg_dir_updates = paths.vcpkg_dir / "updates";
+
+ paths.ports_cmake = paths.root / "scripts" / "ports.cmake";
+ return paths;
+ }
+}
diff --git a/toolsrc/src/vcpkg_version.cpp b/toolsrc/src/vcpkg_version.cpp
new file mode 100644
index 000000000..da52b7cab
--- /dev/null
+++ b/toolsrc/src/vcpkg_version.cpp
@@ -0,0 +1,25 @@
+#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_helpers.cpp b/toolsrc/src/vcpkglib_helpers.cpp
new file mode 100644
index 000000000..e947dc647
--- /dev/null
+++ b/toolsrc/src/vcpkglib_helpers.cpp
@@ -0,0 +1,50 @@
+#include "vcpkg_Checks.h"
+#include "vcpkglib_helpers.h"
+#include <unordered_map>
+
+namespace vcpkg {namespace details
+{
+ void optional_field(const std::unordered_map<std::string, std::string>& fields, std::string& out, const std::string& fieldname)
+ {
+ auto it = fields.find(fieldname);
+ if (it == fields.end())
+ {
+ out.clear();
+ }
+
+ else
+ {
+ out = it->second;
+ }
+ };
+
+ void required_field(const std::unordered_map<std::string, std::string>& fields, std::string& out, const std::string& fieldname)
+ {
+ auto it = fields.find(fieldname);
+ vcpkg::Checks::check_throw(it != fields.end(), "Required field not present: %s", fieldname);
+ out = it->second;
+ };
+
+ void parse_depends(const std::string& depends_string, 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));
+ return;
+ }
+ 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);
+ }
+}}