aboutsummaryrefslogtreecommitdiff
path: root/toolsrc/src
diff options
context:
space:
mode:
authorAlexander Neumann <alexander.neumann@hamburg.de>2019-09-20 12:24:23 +0200
committerAlexander Neumann <alexander.neumann@hamburg.de>2019-09-20 12:24:23 +0200
commit5b1e426929b40a9b60809284993b424b841a28fc (patch)
treebd12300ad859bababb7d4acc03700fd31949fddc /toolsrc/src
parent279e25aecfe30f55296881ea9b0236c1d6ee030a (diff)
parent358ec0954d9b71b0def4fd4b4dbafdd0b8478d81 (diff)
downloadvcpkg-5b1e426929b40a9b60809284993b424b841a28fc.tar.gz
vcpkg-5b1e426929b40a9b60809284993b424b841a28fc.zip
Merge remote-tracking branch 'upstream/master' into path_separator
# Conflicts: # scripts/cmake/vcpkg_common_definitions.cmake
Diffstat (limited to 'toolsrc/src')
-rw-r--r--toolsrc/src/vcpkg-test/arguments.cpp4
-rw-r--r--toolsrc/src/vcpkg-test/hash.cpp276
-rw-r--r--toolsrc/src/vcpkg-test/stringview.cpp17
-rw-r--r--toolsrc/src/vcpkg/base/downloads.cpp2
-rw-r--r--toolsrc/src/vcpkg/base/files.cpp36
-rw-r--r--toolsrc/src/vcpkg/base/hash.cpp830
-rw-r--r--toolsrc/src/vcpkg/base/stringview.cpp6
-rw-r--r--toolsrc/src/vcpkg/build.cpp75
-rw-r--r--toolsrc/src/vcpkg/commands.ci.cpp2
-rw-r--r--toolsrc/src/vcpkg/commands.cpp12
-rw-r--r--toolsrc/src/vcpkg/commands.create.cpp2
-rw-r--r--toolsrc/src/vcpkg/commands.dependinfo.cpp4
-rw-r--r--toolsrc/src/vcpkg/commands.porthistory.cpp91
-rw-r--r--toolsrc/src/vcpkg/commands.upgrade.cpp1
-rw-r--r--toolsrc/src/vcpkg/export.cpp1
-rw-r--r--toolsrc/src/vcpkg/help.cpp3
-rw-r--r--toolsrc/src/vcpkg/install.cpp18
-rw-r--r--toolsrc/src/vcpkg/metrics.cpp2
-rw-r--r--toolsrc/src/vcpkg/parse.cpp94
-rw-r--r--toolsrc/src/vcpkg/vcpkgcmdarguments.cpp8
-rw-r--r--toolsrc/src/vcpkg/vcpkgpaths.cpp2
21 files changed, 1273 insertions, 213 deletions
diff --git a/toolsrc/src/vcpkg-test/arguments.cpp b/toolsrc/src/vcpkg-test/arguments.cpp
index 326b07579..c63a31396 100644
--- a/toolsrc/src/vcpkg-test/arguments.cpp
+++ b/toolsrc/src/vcpkg-test/arguments.cpp
@@ -13,7 +13,7 @@ TEST_CASE ("VcpkgCmdArguments from lowercase argument sequence", "[arguments]")
{
std::vector<std::string> t = {"--vcpkg-root",
"C:\\vcpkg",
- "--scripts-root=C:\\scripts",
+ "--x-scripts-root=C:\\scripts",
"--debug",
"--sendmetrics",
"--printmetrics",
@@ -45,7 +45,7 @@ TEST_CASE ("VcpkgCmdArguments from uppercase argument sequence", "[arguments]")
{
std::vector<std::string> t = {"--VCPKG-ROOT",
"C:\\vcpkg",
- "--SCRIPTS-ROOT=C:\\scripts",
+ "--X-SCRIPTS-ROOT=C:\\scripts",
"--DEBUG",
"--SENDMETRICS",
"--PRINTMETRICS",
diff --git a/toolsrc/src/vcpkg-test/hash.cpp b/toolsrc/src/vcpkg-test/hash.cpp
new file mode 100644
index 000000000..9f3ccc25e
--- /dev/null
+++ b/toolsrc/src/vcpkg-test/hash.cpp
@@ -0,0 +1,276 @@
+#include <catch2/catch.hpp>
+
+#include <vcpkg/base/hash.h>
+
+#include <algorithm>
+#include <iostream>
+#include <iterator>
+#include <map>
+
+namespace Hash = vcpkg::Hash;
+using vcpkg::StringView;
+
+// Require algorithm: Hash::Algorithm::Tag to be in scope
+#define CHECK_HASH(size, value, real_hash) \
+ do \
+ { \
+ unsigned char data[size]; \
+ std::fill(std::begin(data), std::end(data), static_cast<unsigned char>(value)); \
+ const auto hash = Hash::get_bytes_hash(data, data + size, algorithm); \
+ REQUIRE(hash == real_hash); \
+ } while (0)
+
+#define CHECK_HASH_OF(data, real_hash) \
+ do \
+ { \
+ const auto hash = Hash::get_bytes_hash(std::begin(data), std::end(data), algorithm); \
+ REQUIRE(hash == real_hash); \
+ } while (0)
+
+#define CHECK_HASH_STRING(data, real_hash) \
+ do \
+ { \
+ const auto hash = Hash::get_string_hash(data, algorithm); \
+ REQUIRE(hash == real_hash); \
+ } while (0)
+
+// Requires hasher: std::unique_ptr<Hash::Hasher> to be in scope
+#define CHECK_HASH_LARGE(size, value, real_hash) \
+ do \
+ { \
+ hasher->clear(); \
+ std::uint64_t remaining = size; \
+ unsigned char buffer[512]; \
+ std::fill(std::begin(buffer), std::end(buffer), static_cast<unsigned char>(value)); \
+ while (remaining) \
+ { \
+ if (remaining < 512) \
+ { \
+ hasher->add_bytes(std::begin(buffer), std::begin(buffer) + remaining); \
+ remaining = 0; \
+ } \
+ else \
+ { \
+ hasher->add_bytes(std::begin(buffer), std::end(buffer)); \
+ remaining -= 512; \
+ } \
+ } \
+ REQUIRE(hasher->get_hash() == real_hash); \
+ } while (0)
+
+TEST_CASE ("SHA1: basic tests", "[hash][sha1]")
+{
+ const auto algorithm = Hash::Algorithm::Sha1;
+
+ CHECK_HASH_STRING("", "da39a3ee5e6b4b0d3255bfef95601890afd80709");
+ CHECK_HASH_STRING(";", "2d14ab97cc3dc294c51c0d6814f4ea45f4b4e312");
+ CHECK_HASH_STRING("asdifasdfnas", "b77eb8a1b4c2ef6716d7d302647e4511b1a638a6");
+ CHECK_HASH_STRING("asdfanvoinaoifawenflawenfiwnofvnasfjvnaslkdfjlkasjdfanm,"
+ "werflawoienfowanevoinwai32910u2740918741o;j;wejfqwioaher9283hrpf;asd",
+ "c69bcd30c196c7050906d212722dd7a7659aad04");
+}
+
+TEST_CASE ("SHA1: NIST test cases (small)", "[hash][sha1]")
+{
+ const auto algorithm = Hash::Algorithm::Sha1;
+
+ CHECK_HASH_STRING("abc", "a9993e364706816aba3e25717850c26c9cd0d89d");
+ CHECK_HASH_STRING("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
+ "84983e441c3bd26ebaae4aa1f95129e5e54670f1");
+}
+
+TEST_CASE ("SHA256: basic tests", "[hash][sha256]")
+{
+ const auto algorithm = Hash::Algorithm::Sha256;
+
+ CHECK_HASH_STRING("", "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855");
+ CHECK_HASH_STRING(";", "41b805ea7ac014e23556e98bb374702a08344268f92489a02f0880849394a1e4");
+ CHECK_HASH_STRING("asdifasdfnas", "2bb1fb910831fdc11d5a3996425a84ace27aeb81c9c20ace9f60ac1b3218b291");
+ CHECK_HASH_STRING("asdfanvoinaoifawenflawenfiwnofvnasfjvnaslkdfjlkasjdfanm,"
+ "werflawoienfowanevoinwai32910u2740918741o;j;wejfqwioaher9283hrpf;asd",
+ "10c98034b424d4e40ca933bc524ea38b4e53290d76e8b38edc4ea2fec7f529aa");
+}
+
+TEST_CASE ("SHA256: NIST test cases (small)", "[hash][sha256]")
+{
+ const auto algorithm = Hash::Algorithm::Sha256;
+
+ CHECK_HASH(1, 0xbd, "68325720aabd7c82f30f554b313d0570c95accbb7dc4b5aae11204c08ffe732b");
+ {
+ const unsigned char data[] = {0xc9, 0x8c, 0x8e, 0x55};
+ CHECK_HASH_OF(data, "7abc22c0ae5af26ce93dbb94433a0e0b2e119d014f8e7f65bd56c61ccccd9504");
+ }
+ CHECK_HASH(55, 0, "02779466cdec163811d078815c633f21901413081449002f24aa3e80f0b88ef7");
+ CHECK_HASH(56, 0, "d4817aa5497628e7c77e6b606107042bbba3130888c5f47a375e6179be789fbb");
+ CHECK_HASH(57, 0, "65a16cb7861335d5ace3c60718b5052e44660726da4cd13bb745381b235a1785");
+ CHECK_HASH(64, 0, "f5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b");
+ CHECK_HASH(1000, 0, "541b3e9daa09b20bf85fa273e5cbd3e80185aa4ec298e765db87742b70138a53");
+ CHECK_HASH(1000, 'A', "c2e686823489ced2017f6059b8b239318b6364f6dcd835d0a519105a1eadd6e4");
+ CHECK_HASH(1005, 'U', "f4d62ddec0f3dd90ea1380fa16a5ff8dc4c54b21740650f24afc4120903552b0");
+}
+
+TEST_CASE ("SHA512: NIST test cases (small)", "[hash][sha512]")
+{
+ const auto algorithm = Hash::Algorithm::Sha512;
+
+ CHECK_HASH_STRING("",
+ "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f"
+ "63b931bd47417a81a538327af927da3e");
+
+ CHECK_HASH(111,
+ 0,
+ "77ddd3a542e530fd047b8977c657ba6ce72f1492e360b2b2212cd264e75ec03882e4ff0525517ab4207d14c70c2259ba88d4d33"
+ "5ee0e7e20543d22102ab1788c");
+ CHECK_HASH(112,
+ 0,
+ "2be2e788c8a8adeaa9c89a7f78904cacea6e39297d75e0573a73c756234534d6627ab4156b48a6657b29ab8beb73334040ad39e"
+ "ad81446bb09c70704ec707952");
+ CHECK_HASH(113,
+ 0,
+ "0e67910bcf0f9ccde5464c63b9c850a12a759227d16b040d98986d54253f9f34322318e56b8feb86c5fb2270ed87f31252f7f68"
+ "493ee759743909bd75e4bb544");
+ CHECK_HASH(122,
+ 0,
+ "4f3f095d015be4a7a7cc0b8c04da4aa09e74351e3a97651f744c23716ebd9b3e822e5077a01baa5cc0ed45b9249e88ab343d433"
+ "3539df21ed229da6f4a514e0f");
+ CHECK_HASH(1000,
+ 0,
+ "ca3dff61bb23477aa6087b27508264a6f9126ee3a004f53cb8db942ed345f2f2d229b4b59c859220a1cf1913f34248e3803bab6"
+ "50e849a3d9a709edc09ae4a76");
+ CHECK_HASH(1000,
+ 'A',
+ "329c52ac62d1fe731151f2b895a00475445ef74f50b979c6f7bb7cae349328c1d4cb4f7261a0ab43f936a24b000651d4a824fcd"
+ "d577f211aef8f806b16afe8af");
+ CHECK_HASH(1005,
+ 'U',
+ "59f5e54fe299c6a8764c6b199e44924a37f59e2b56c3ebad939b7289210dc8e4c21b9720165b0f4d4374c90f1bf4fb4a5ace17a"
+ "1161798015052893a48c3d161");
+}
+
+TEST_CASE ("SHA256: NIST test cases (large)", "[.][hash-expensive][sha256-expensive]")
+{
+ auto hasher = Hash::get_hasher_for(Hash::Algorithm::Sha256);
+ CHECK_HASH_LARGE(1'000'000, 0, "d29751f2649b32ff572b5e0a9f541ea660a50f94ff0beedfb0b692b924cc8025");
+ CHECK_HASH_LARGE(0x2000'0000, 'Z', "15a1868c12cc53951e182344277447cd0979536badcc512ad24c67e9b2d4f3dd");
+ CHECK_HASH_LARGE(0x4100'0000, 0, "461c19a93bd4344f9215f5ec64357090342bc66b15a148317d276e31cbc20b53");
+ CHECK_HASH_LARGE(0x6000'003E, 'B', "c23ce8a7895f4b21ec0daf37920ac0a262a220045a03eb2dfed48ef9b05aabea");
+}
+
+TEST_CASE ("SHA512: NIST test cases (large)", "[.][hash-expensive][sha512-expensive]")
+{
+ auto hasher = Hash::get_hasher_for(Hash::Algorithm::Sha512);
+ CHECK_HASH_LARGE(1'000'000,
+ 0,
+ "ce044bc9fd43269d5bbc946cbebc3bb711341115cc4abdf2edbc3ff2c57ad4b15deb699bda257fea5aef9c6e55fcf4cf9"
+ "dc25a8c3ce25f2efe90908379bff7ed");
+ CHECK_HASH_LARGE(0x2000'0000,
+ 'Z',
+ "da172279f3ebbda95f6b6e1e5f0ebec682c25d3d93561a1624c2fa9009d64c7e9923f3b46bcaf11d39a531f43297992ba"
+ "4155c7e827bd0f1e194ae7ed6de4cac");
+ CHECK_HASH_LARGE(0x4100'0000,
+ 0,
+ "14b1be901cb43549b4d831e61e5f9df1c791c85b50e85f9d6bc64135804ad43ce8402750edbe4e5c0fc170b99cf78b9f4"
+ "ecb9c7e02a157911d1bd1832d76784f");
+ CHECK_HASH_LARGE(0x6000'003E,
+ 'B',
+ "fd05e13eb771f05190bd97d62647157ea8f1f6949a52bb6daaedbad5f578ec59b1b8d6c4a7ecb2feca6892b4dc1387716"
+ "70a0f3bd577eea326aed40ab7dd58b1");
+}
+
+#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
+using Catch::Benchmark::Chronometer;
+void benchmark_hasher(Chronometer& meter, Hash::Hasher& hasher, std::uint64_t size, unsigned char byte) noexcept
+{
+ unsigned char buffer[1024];
+ std::fill(std::begin(buffer), std::end(buffer), byte);
+
+ meter.measure([&] {
+ hasher.clear();
+ std::uint64_t remaining = size;
+ while (remaining)
+ {
+ if (remaining < 512)
+ {
+ hasher.add_bytes(std::begin(buffer), std::begin(buffer) + remaining);
+ remaining = 0;
+ }
+ else
+ {
+ hasher.add_bytes(std::begin(buffer), std::end(buffer));
+ remaining -= 512;
+ }
+ }
+ hasher.get_hash();
+ });
+}
+
+TEST_CASE ("SHA1: benchmark", "[.][hash][sha256][!benchmark]")
+{
+ using Catch::Benchmark::Chronometer;
+
+ auto hasher = Hash::get_hasher_for(Hash::Algorithm::Sha1);
+
+ BENCHMARK_ADVANCED("0 x 1'000'000")(Catch::Benchmark::Chronometer meter)
+ {
+ benchmark_hasher(meter, *hasher, 1'000'000, 0);
+ };
+ BENCHMARK_ADVANCED("'Z' x 0x2000'0000")(Catch::Benchmark::Chronometer meter)
+ {
+ benchmark_hasher(meter, *hasher, 0x2000'0000, 'Z');
+ };
+ BENCHMARK_ADVANCED("0 x 0x4100'0000")(Catch::Benchmark::Chronometer meter)
+ {
+ benchmark_hasher(meter, *hasher, 0x4100'0000, 0);
+ };
+ BENCHMARK_ADVANCED("'B' x 0x6000'003E")(Catch::Benchmark::Chronometer meter)
+ {
+ benchmark_hasher(meter, *hasher, 0x6000'003E, 'B');
+ };
+}
+
+TEST_CASE ("SHA256: benchmark", "[.][hash][sha256][!benchmark]")
+{
+ using Catch::Benchmark::Chronometer;
+
+ auto hasher = Hash::get_hasher_for(Hash::Algorithm::Sha256);
+
+ BENCHMARK_ADVANCED("0 x 1'000'000")(Catch::Benchmark::Chronometer meter)
+ {
+ benchmark_hasher(meter, *hasher, 1'000'000, 0);
+ };
+ BENCHMARK_ADVANCED("'Z' x 0x2000'0000")(Catch::Benchmark::Chronometer meter)
+ {
+ benchmark_hasher(meter, *hasher, 0x2000'0000, 'Z');
+ };
+ BENCHMARK_ADVANCED("0 x 0x4100'0000")(Catch::Benchmark::Chronometer meter)
+ {
+ benchmark_hasher(meter, *hasher, 0x4100'0000, 0);
+ };
+ BENCHMARK_ADVANCED("'B' x 0x6000'003E")(Catch::Benchmark::Chronometer meter)
+ {
+ benchmark_hasher(meter, *hasher, 0x6000'003E, 'B');
+ };
+}
+
+TEST_CASE ("SHA512: large -- benchmark", "[.][hash][sha512][!benchmark]")
+{
+ auto hasher = Hash::get_hasher_for(Hash::Algorithm::Sha512);
+
+ BENCHMARK_ADVANCED("0 x 1'000'000")(Catch::Benchmark::Chronometer meter)
+ {
+ benchmark_hasher(meter, *hasher, 1'000'000, 0);
+ };
+ BENCHMARK_ADVANCED("'Z' x 0x2000'0000")(Catch::Benchmark::Chronometer meter)
+ {
+ benchmark_hasher(meter, *hasher, 0x2000'0000, 'Z');
+ };
+ BENCHMARK_ADVANCED("0 x 0x4100'0000")(Catch::Benchmark::Chronometer meter)
+ {
+ benchmark_hasher(meter, *hasher, 0x4100'0000, 0);
+ };
+ BENCHMARK_ADVANCED("'B' x 0x6000'003E")(Catch::Benchmark::Chronometer meter)
+ {
+ benchmark_hasher(meter, *hasher, 0x6000'003E, 'B');
+ };
+}
+#endif
diff --git a/toolsrc/src/vcpkg-test/stringview.cpp b/toolsrc/src/vcpkg-test/stringview.cpp
new file mode 100644
index 000000000..4df8e6be5
--- /dev/null
+++ b/toolsrc/src/vcpkg-test/stringview.cpp
@@ -0,0 +1,17 @@
+#include <catch2/catch.hpp>
+
+#include <vcpkg/base/stringview.h>
+
+template <std::size_t N>
+static vcpkg::StringView sv(const char (&cstr)[N]) {
+ return cstr;
+}
+
+TEST_CASE("string view operator==", "[stringview]") {
+ // these are due to a bug in operator==
+ // see commit 782723959399a1a0725ac49
+ REQUIRE(sv("hey") != sv("heys"));
+ REQUIRE(sv("heys") != sv("hey"));
+ REQUIRE(sv("hey") == sv("hey"));
+ REQUIRE(sv("hey") != sv("hex"));
+}
diff --git a/toolsrc/src/vcpkg/base/downloads.cpp b/toolsrc/src/vcpkg/base/downloads.cpp
index 4bb2178e5..df6b1be09 100644
--- a/toolsrc/src/vcpkg/base/downloads.cpp
+++ b/toolsrc/src/vcpkg/base/downloads.cpp
@@ -127,7 +127,7 @@ namespace vcpkg::Downloads
const fs::path& path,
const std::string& sha512)
{
- std::string actual_hash = vcpkg::Hash::get_file_hash(fs, path, "SHA512");
+ std::string actual_hash = vcpkg::Hash::get_file_hash(VCPKG_LINE_INFO, fs, path, Hash::Algorithm::Sha512);
// <HACK to handle NuGet.org changing nupkg hashes.>
// This is the NEW hash for 7zip
diff --git a/toolsrc/src/vcpkg/base/files.cpp b/toolsrc/src/vcpkg/base/files.cpp
index bbf37fd25..3d7df1a68 100644
--- a/toolsrc/src/vcpkg/base/files.cpp
+++ b/toolsrc/src/vcpkg/base/files.cpp
@@ -8,9 +8,8 @@
#include <vcpkg/base/util.h>
#include <vcpkg/base/work_queue.h>
-#if defined(__linux__) || defined(__APPLE__)
+#if !defined(_WIN32)
#include <fcntl.h>
-#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
@@ -186,6 +185,7 @@ namespace vcpkg::Files
if (ec) Checks::exit_with_message(li, "error checking existence of file %s: %s", path.u8string(), ec.message());
return result;
}
+
bool Filesystem::exists(const fs::path& path) const
{
std::error_code ec;
@@ -396,7 +396,7 @@ namespace vcpkg::Files
{
this->rename(oldpath, newpath, ec);
Util::unused(temp_suffix);
-#if defined(__linux__) || defined(__APPLE__)
+#if !defined(_WIN32)
if (ec)
{
auto dst = newpath;
@@ -419,6 +419,33 @@ namespace vcpkg::Files
auto written_bytes = sendfile(o_fd, i_fd, &bytes, info.st_size);
#elif defined(__APPLE__)
auto written_bytes = fcopyfile(i_fd, o_fd, 0, COPYFILE_ALL);
+#else
+ ssize_t written_bytes = 0;
+ {
+ constexpr std::size_t buffer_length = 4096;
+ auto buffer = std::make_unique<unsigned char[]>(buffer_length);
+ while (auto read_bytes = read(i_fd, buffer.get(), buffer_length))
+ {
+ if (read_bytes == -1)
+ {
+ written_bytes = -1;
+ break;
+ }
+ auto remaining = read_bytes;
+ while (remaining > 0) {
+ auto read_result = write(o_fd, buffer.get(), remaining);
+ if (read_result == -1)
+ {
+ written_bytes = -1;
+ // break two loops
+ goto copy_failure;
+ }
+ remaining -= read_result;
+ }
+ }
+
+ copy_failure: ;
+ }
#endif
if (written_bytes == -1)
{
@@ -641,13 +668,14 @@ namespace vcpkg::Files
auto paths = Strings::split(System::get_environment_variable("PATH").value_or_exit(VCPKG_LINE_INFO), ";");
std::vector<fs::path> ret;
+ std::error_code ec;
for (auto&& path : paths)
{
auto base = path + "/" + name;
for (auto&& ext : EXTS)
{
auto p = fs::u8path(base + ext.c_str());
- if (Util::find(ret, p) == ret.end() && this->exists(VCPKG_LINE_INFO, p))
+ if (Util::find(ret, p) == ret.end() && this->exists(p, ec))
{
ret.push_back(p);
Debug::print("Found path: ", p.u8string(), '\n');
diff --git a/toolsrc/src/vcpkg/base/hash.cpp b/toolsrc/src/vcpkg/base/hash.cpp
index 62a01ed17..11df3e329 100644
--- a/toolsrc/src/vcpkg/base/hash.cpp
+++ b/toolsrc/src/vcpkg/base/hash.cpp
@@ -14,238 +14,758 @@
#ifndef NT_SUCCESS
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
#endif
+
#endif
namespace vcpkg::Hash
{
- static void verify_has_only_allowed_chars(const std::string& s)
+ using uchar = unsigned char;
+
+ Optional<Algorithm> algorithm_from_string(StringView sv) noexcept
+ {
+ if (Strings::case_insensitive_ascii_equals(sv, "SHA1"))
+ {
+ return {Algorithm::Sha1};
+ }
+ if (Strings::case_insensitive_ascii_equals(sv, "SHA256"))
+ {
+ return {Algorithm::Sha256};
+ }
+ if (Strings::case_insensitive_ascii_equals(sv, "SHA512"))
+ {
+ return {Algorithm::Sha512};
+ }
+
+ return {};
+ }
+
+ const char* to_string(Algorithm algo) noexcept
{
- static const std::regex ALLOWED_CHARS{"^[a-zA-Z0-9-]*$"};
- Checks::check_exit(VCPKG_LINE_INFO,
- std::regex_match(s, ALLOWED_CHARS),
- "Only alphanumeric chars and dashes are currently allowed. String was:\n"
- " % s",
- s);
+ switch (algo)
+ {
+ case Algorithm::Sha1: return "SHA1";
+ case Algorithm::Sha256: return "SHA256";
+ case Algorithm::Sha512: return "SHA512";
+ default: vcpkg::Checks::exit_fail(VCPKG_LINE_INFO);
+ }
}
-#if defined(_WIN32)
+
namespace
{
- std::string to_hex(const unsigned char* string, const size_t bytes)
+ struct UInt128
{
- static constexpr char HEX_MAP[] = "0123456789abcdef";
+ std::uint64_t top;
+ std::uint64_t bottom;
- std::string output;
- output.resize(2 * bytes);
+ UInt128() = default;
+ UInt128(std::uint64_t value) : top(0), bottom(value) {}
- size_t current_char = 0;
- for (size_t i = 0; i < bytes; i++)
+ UInt128& operator<<=(int by) noexcept
+ {
+ if (by == 0)
+ {
+ return *this;
+ }
+
+ if (by < 64)
+ {
+ top <<= by;
+ const auto shift_up = bottom >> (64 - by);
+ top |= shift_up;
+ bottom <<= by;
+ }
+ else
+ {
+ top = bottom;
+ top <<= (by - 64);
+ }
+
+ return *this;
+ }
+
+ UInt128& operator>>=(int by) noexcept
+ {
+ if (by == 0)
+ {
+ return *this;
+ }
+
+ if (by < 64)
+ {
+ bottom >>= by;
+ const auto shift_down = top << (64 - by);
+ bottom |= shift_down;
+ top >>= by;
+ }
+ else
+ {
+ bottom = top;
+ bottom >>= (by - 64);
+ }
+
+ return *this;
+ }
+
+ UInt128& operator+=(std::size_t lhs) noexcept
+ {
+ // bottom + lhs > uint64::max
+ if (bottom > std::numeric_limits<std::uint64_t>::max() - lhs)
+ {
+ top += 1;
+ }
+ bottom += lhs;
+ return *this;
+ }
+ };
+
+ }
+ template<class T>
+ void top_bits(T) = delete;
+
+ static constexpr uchar top_bits(uchar x) noexcept { return x; }
+ static constexpr uchar top_bits(std::uint32_t x) noexcept { return (x >> 24) & 0xFF; }
+ static constexpr uchar top_bits(std::uint64_t x) noexcept { return (x >> 56) & 0xFF; }
+ static constexpr uchar top_bits(UInt128 x) noexcept { return top_bits(x.top); }
+
+ // treats UIntTy as big endian for the purpose of this mapping
+ template<class UIntTy>
+ static std::string to_hex(const UIntTy* start, const UIntTy* end) noexcept
+ {
+ static constexpr char HEX_MAP[] = "0123456789abcdef";
+
+ std::string output;
+ output.resize(2 * sizeof(UIntTy) * (end - start));
+
+ std::size_t output_index = 0;
+ for (const UIntTy* it = start; it != end; ++it)
+ {
+ // holds *it in a big-endian buffer, for copying into output
+ uchar buff[sizeof(UIntTy)];
+ UIntTy tmp = *it;
+ for (uchar& ch : buff)
+ {
+ ch = top_bits(tmp);
+ tmp <<= 8;
+ }
+
+ for (const auto byte : buff)
{
// high
- output[current_char] = HEX_MAP[(string[i] & 0xF0) >> 4];
- ++current_char;
+ output[output_index] = HEX_MAP[(byte & 0xF0) >> 4];
+ ++output_index;
// low
- output[current_char] = HEX_MAP[(string[i] & 0x0F)];
- ++current_char;
+ output[output_index] = HEX_MAP[byte & 0x0F];
+ ++output_index;
}
-
- return output;
}
- class BCryptHasher
+ return output;
+ }
+
+ namespace
+ {
+#if defined(_WIN32)
+ BCRYPT_ALG_HANDLE get_alg_handle(LPCWSTR algorithm_identifier) noexcept
{
- struct BCryptAlgorithmHandle : Util::ResourceBase
+ BCRYPT_ALG_HANDLE result;
+ auto error = BCryptOpenAlgorithmProvider(&result, algorithm_identifier, nullptr, 0);
+ if (!NT_SUCCESS(error))
{
- BCRYPT_ALG_HANDLE handle = nullptr;
+ Checks::exit_with_message(VCPKG_LINE_INFO, "Failure to open algorithm: %ls", algorithm_identifier);
+ }
+
+ return result;
+ }
- ~BCryptAlgorithmHandle()
+ struct BCryptHasher : Hasher
+ {
+ static const BCRYPT_ALG_HANDLE sha1_alg_handle;
+ static const BCRYPT_ALG_HANDLE sha256_alg_handle;
+ static const BCRYPT_ALG_HANDLE sha512_alg_handle;
+
+ explicit BCryptHasher(Algorithm algo) noexcept
+ {
+ switch (algo)
{
- if (handle) BCryptCloseAlgorithmProvider(handle, 0);
+ case Algorithm::Sha1: alg_handle = sha1_alg_handle; break;
+ case Algorithm::Sha256: alg_handle = sha256_alg_handle; break;
+ case Algorithm::Sha512: alg_handle = sha512_alg_handle; break;
+ default: Checks::exit_with_message(VCPKG_LINE_INFO, "Unknown algorithm");
}
- };
- struct BCryptHashHandle : Util::ResourceBase
+ clear();
+ }
+
+ virtual void add_bytes(const void* start_, const void* end_) noexcept override
{
- BCRYPT_HASH_HANDLE handle = nullptr;
+ // BCryptHashData takes its input as non-const, but does not modify it
+ uchar* start = const_cast<uchar*>(static_cast<const uchar*>(start_));
+ const uchar* end = static_cast<const uchar*>(end_);
+ Checks::check_exit(VCPKG_LINE_INFO, end - start >= 0);
+
+ // only matters on 64-bit -- BCryptHasher takes an unsigned long
+ // length, so if you have an array bigger than 2**32-1 elements,
+ // you have a problem.
+#if defined(_M_AMD64) || defined(_M_ARM64)
+ constexpr std::ptrdiff_t max = std::numeric_limits<unsigned long>::max();
+ Checks::check_exit(VCPKG_LINE_INFO, end - start <= max);
+#endif
- ~BCryptHashHandle()
- {
- if (handle) BCryptDestroyHash(handle);
- }
- };
+ const auto length = static_cast<unsigned long>(end - start);
+ const NTSTATUS error_code = BCryptHashData(hash_handle, start, length, 0);
+ Checks::check_exit(VCPKG_LINE_INFO, NT_SUCCESS(error_code), "Failed to process a chunk");
+ }
- static void initialize_hash_handle(BCryptHashHandle& hash_handle,
- const BCryptAlgorithmHandle& algorithm_handle)
+ virtual void clear() noexcept override
{
- const NTSTATUS error_code =
- BCryptCreateHash(algorithm_handle.handle, &hash_handle.handle, nullptr, 0, nullptr, 0, 0);
+ if (hash_handle) BCryptDestroyHash(hash_handle);
+ const NTSTATUS error_code = BCryptCreateHash(alg_handle, &hash_handle, nullptr, 0, nullptr, 0, 0);
Checks::check_exit(VCPKG_LINE_INFO, NT_SUCCESS(error_code), "Failed to initialize the hasher");
}
- static void hash_data(BCryptHashHandle& hash_handle, const unsigned char* buffer, const size_t& data_size)
+ virtual std::string get_hash() noexcept override
{
- const NTSTATUS error_code = BCryptHashData(
- hash_handle.handle, const_cast<unsigned char*>(buffer), static_cast<ULONG>(data_size), 0);
- Checks::check_exit(VCPKG_LINE_INFO, NT_SUCCESS(error_code), "Failed to hash data");
+ const auto hash_size = get_hash_buffer_size();
+ const auto buffer = std::make_unique<uchar[]>(hash_size);
+ const auto hash = buffer.get();
+
+ const NTSTATUS error_code = BCryptFinishHash(hash_handle, hash, hash_size, 0);
+ Checks::check_exit(VCPKG_LINE_INFO, NT_SUCCESS(error_code), "Failed to finalize the hash");
+ return to_hex(hash, hash + hash_size);
}
- static std::string finalize_hash_handle(const BCryptHashHandle& hash_handle, const ULONG length_in_bytes)
+ ~BCryptHasher() { BCryptDestroyHash(hash_handle); }
+
+ private:
+ unsigned long get_hash_buffer_size() const
{
- std::unique_ptr<unsigned char[]> hash_buffer = std::make_unique<UCHAR[]>(length_in_bytes);
- const NTSTATUS error_code = BCryptFinishHash(hash_handle.handle, hash_buffer.get(), length_in_bytes, 0);
- Checks::check_exit(VCPKG_LINE_INFO, NT_SUCCESS(error_code), "Failed to finalize the hash");
- return to_hex(hash_buffer.get(), length_in_bytes);
- }
-
- public:
- explicit BCryptHasher(std::string hash_type)
- {
- NTSTATUS error_code = BCryptOpenAlgorithmProvider(
- &this->algorithm_handle.handle,
- Strings::to_utf16(Strings::ascii_to_uppercase(std::move(hash_type))).c_str(),
- nullptr,
- 0);
- Checks::check_exit(VCPKG_LINE_INFO, NT_SUCCESS(error_code), "Failed to open the algorithm provider");
-
- DWORD hash_buffer_bytes;
- DWORD cb_data;
- error_code = BCryptGetProperty(this->algorithm_handle.handle,
- BCRYPT_HASH_LENGTH,
- reinterpret_cast<PUCHAR>(&hash_buffer_bytes),
- sizeof(DWORD),
- &cb_data,
- 0);
+ unsigned long hash_buffer_bytes;
+ unsigned long cb_data;
+ const NTSTATUS error_code = BCryptGetProperty(alg_handle,
+ BCRYPT_HASH_LENGTH,
+ reinterpret_cast<uchar*>(&hash_buffer_bytes),
+ sizeof(hash_buffer_bytes),
+ &cb_data,
+ 0);
Checks::check_exit(VCPKG_LINE_INFO, NT_SUCCESS(error_code), "Failed to get hash length");
- this->length_in_bytes = hash_buffer_bytes;
+
+ return hash_buffer_bytes;
}
- std::string hash_file(const fs::path& path) const
- {
- BCryptHashHandle hash_handle;
- initialize_hash_handle(hash_handle, this->algorithm_handle);
+ BCRYPT_HASH_HANDLE hash_handle = nullptr;
+ BCRYPT_ALG_HANDLE alg_handle = nullptr;
+ };
+
+ const BCRYPT_ALG_HANDLE BCryptHasher::sha1_alg_handle = get_alg_handle(BCRYPT_SHA1_ALGORITHM);
+ const BCRYPT_ALG_HANDLE BCryptHasher::sha256_alg_handle = get_alg_handle(BCRYPT_SHA256_ALGORITHM);
+ const BCRYPT_ALG_HANDLE BCryptHasher::sha512_alg_handle = get_alg_handle(BCRYPT_SHA512_ALGORITHM);
+#else
+
+ template<class WordTy>
+ static WordTy shl(WordTy value, int by) noexcept
+ {
+ return value << by;
+ }
+
+ static std::uint32_t shr32(std::uint32_t value, int by) noexcept { return value >> by; }
+ static std::uint32_t rol32(std::uint32_t value, int by) noexcept
+ {
+ return (value << by) | (value >> (32 - by));
+ }
+ static std::uint32_t ror32(std::uint32_t value, int by) noexcept
+ {
+ return (value >> by) | (value << (32 - by));
+ }
+
+ static std::uint64_t shr64(std::uint64_t value, int by) noexcept { return value >> by; }
+ static std::uint64_t ror64(std::uint64_t value, int by) noexcept
+ {
+ return (value >> by) | (value << (64 - by));
+ }
+
+ template<class ShaAlgorithm>
+ struct ShaHasher final : Hasher
+ {
+ ShaHasher() = default;
- FILE* file = nullptr;
- const auto ec = _wfopen_s(&file, path.c_str(), L"rb");
- Checks::check_exit(VCPKG_LINE_INFO, ec == 0, "Failed to open file: %s", path.u8string());
- if (file != nullptr)
+ virtual void add_bytes(const void* start, const void* end) noexcept override
+ {
+ for (;;)
{
- unsigned char buffer[4096];
- while (const auto actual_size = fread(buffer, 1, sizeof(buffer), file))
+ start = add_to_unprocessed(start, end);
+ if (!start)
{
- hash_data(hash_handle, buffer, actual_size);
+ break; // done
}
- fclose(file);
+
+ m_impl.process_full_chunk(m_chunk);
+ m_current_chunk_size = 0;
}
+ }
+
+ virtual void clear() noexcept override
+ {
+ m_impl.clear();
- return finalize_hash_handle(hash_handle, length_in_bytes);
+ // m_chunk is theoretically uninitialized, so no need to reset it
+ m_current_chunk_size = 0;
+ m_message_length = 0;
}
- std::string hash_string(const std::string& s) const
+ virtual std::string get_hash() noexcept override
{
- BCryptHashHandle hash_handle;
- initialize_hash_handle(hash_handle, this->algorithm_handle);
- hash_data(hash_handle, reinterpret_cast<const unsigned char*>(s.c_str()), s.size());
- return finalize_hash_handle(hash_handle, length_in_bytes);
+ process_last_chunk();
+ return to_hex(m_impl.begin(), m_impl.end());
}
private:
- BCryptAlgorithmHandle algorithm_handle;
- ULONG length_in_bytes;
- };
- }
+ // if unprocessed gets filled,
+ // returns a pointer to the remainder of the block (which might be end)
+ // else, returns nullptr
+ const void* add_to_unprocessed(const void* start_, const void* end_) noexcept
+ {
+ const uchar* start = static_cast<const uchar*>(start_);
+ const uchar* end = static_cast<const uchar*>(end_);
- std::string get_file_hash(const Files::Filesystem& fs, const fs::path& path, const std::string& hash_type)
- {
- Checks::check_exit(VCPKG_LINE_INFO, fs.exists(path), "File %s does not exist", path.u8string());
- return BCryptHasher{hash_type}.hash_file(path);
- }
+ const auto remaining = chunk_size - m_current_chunk_size;
- std::string get_string_hash(const std::string& s, const std::string& hash_type)
- {
- verify_has_only_allowed_chars(s);
- return BCryptHasher{hash_type}.hash_string(s);
- }
+ const std::size_t message_length = end - start;
+ if (message_length >= remaining)
+ {
+ std::copy(start, start + remaining, chunk_begin());
+ m_current_chunk_size += remaining;
+ m_message_length += remaining * 8;
+ return start + remaining;
+ }
+ else
+ {
+ std::copy(start, end, chunk_begin());
+ m_current_chunk_size += message_length;
+ m_message_length += message_length * 8;
+ return nullptr;
+ }
+ }
-#else
- static std::string get_digest_size(const std::string& hash_type)
- {
- if (!Strings::case_insensitive_ascii_starts_with(hash_type, "SHA"))
+ // called before `get_hash`
+ void process_last_chunk() noexcept
+ {
+ const auto message_length = m_message_length;
+
+ // append the bit '1' to the message
+ {
+ const uchar temp = 0x80;
+ add_to_unprocessed(&temp, &temp + 1);
+ }
+
+ // append 0 to the message so that the resulting length is just enough
+ // to add the message length
+ if (chunk_size - m_current_chunk_size < sizeof(m_message_length))
+ {
+ // not enough space to add the message length
+ // just resize and process full chunk
+ std::fill(chunk_begin(), m_chunk.end(), static_cast<uchar>(0));
+ m_impl.process_full_chunk(m_chunk);
+ m_current_chunk_size = 0;
+ }
+
+ const auto before_length = m_chunk.end() - sizeof(m_message_length);
+ std::fill(chunk_begin(), before_length, static_cast<uchar>(0));
+ std::generate(before_length, m_chunk.end(), [length = message_length]() mutable {
+ const auto result = top_bits(length);
+ length <<= 8;
+ return result;
+ });
+
+ m_impl.process_full_chunk(m_chunk);
+ }
+
+ auto chunk_begin() { return m_chunk.begin() + m_current_chunk_size; }
+
+ using underlying_type = typename ShaAlgorithm::underlying_type;
+ using message_length_type = typename ShaAlgorithm::message_length_type;
+ constexpr static std::size_t chunk_size = ShaAlgorithm::chunk_size;
+
+ ShaAlgorithm m_impl{};
+
+ std::array<uchar, chunk_size> m_chunk{};
+ std::size_t m_current_chunk_size = 0;
+ message_length_type m_message_length = 0;
+ };
+ template<class WordTy>
+ inline void sha_fill_initial_words(const uchar* chunk, WordTy* words)
{
- Checks::exit_with_message(
- VCPKG_LINE_INFO, "shasum only supports SHA hashes, but %s was provided", hash_type);
+ // break chunk into 16 N-bit words
+ for (std::size_t word = 0; word < 16; ++word)
+ {
+ words[word] = 0;
+ // big-endian -- so the earliest i becomes the most significant
+ for (std::size_t byte = 0; byte < sizeof(WordTy); ++byte)
+ {
+ const auto bits_to_shift = static_cast<int>(8 * (sizeof(WordTy) - 1 - byte));
+ words[word] |= shl<WordTy>(chunk[word * sizeof(WordTy) + byte], bits_to_shift);
+ }
+ }
}
- return hash_type.substr(3, hash_type.length() - 3);
- }
+ struct Sha1Algorithm
+ {
+ using underlying_type = std::uint32_t;
+ using message_length_type = std::uint64_t;
+ constexpr static std::size_t chunk_size = 64; // = 512 / 8
+ constexpr static std::size_t number_of_rounds = 80;
- static std::string parse_shasum_output(const std::string& shasum_output)
- {
- std::vector<std::string> split = Strings::split(shasum_output, " ");
- // Checking if >= 3 because filenames with spaces will show up as multiple tokens.
- // The hash is the first token so we don't need to parse the filename anyway.
- Checks::check_exit(VCPKG_LINE_INFO,
- split.size() >= 3,
- "Expected output of the form [hash filename\n] (3+ tokens), but got\n"
- "[%s] (%s tokens)",
- shasum_output,
- std::to_string(split.size()));
-
- return split[0];
- }
+ Sha1Algorithm() noexcept { clear(); }
- std::string get_file_hash(const Files::Filesystem& fs, const fs::path& path, const std::string& hash_type)
- {
- const std::string digest_size = get_digest_size(hash_type);
- Checks::check_exit(VCPKG_LINE_INFO, fs.exists(path), "File %s does not exist", path.u8string());
+ void process_full_chunk(const std::array<uchar, chunk_size>& chunk) noexcept
+ {
+ std::uint32_t words[80];
+
+ sha_fill_initial_words(&chunk[0], words);
+ for (std::size_t i = 16; i < number_of_rounds; ++i)
+ {
+ const auto sum = words[i - 3] ^ words[i - 8] ^ words[i - 14] ^ words[i - 16];
+ words[i] = rol32(sum, 1);
+ }
+
+ std::uint32_t a = m_digest[0];
+ std::uint32_t b = m_digest[1];
+ std::uint32_t c = m_digest[2];
+ std::uint32_t d = m_digest[3];
+ std::uint32_t e = m_digest[4];
+
+ for (std::size_t i = 0; i < number_of_rounds; ++i)
+ {
+ std::uint32_t f;
+ std::uint32_t k;
+
+ if (i < 20)
+ {
+ f = (b & c) | (~b & d);
+ k = 0x5A827999;
+ }
+ else if (i < 40)
+ {
+ f = b ^ c ^ d;
+ k = 0x6ED9EBA1;
+ }
+ else if (i < 60)
+ {
+ f = (b & c) | (b & d) | (c & d);
+ k = 0x8F1BBCDC;
+ }
+ else
+ {
+ f = b ^ c ^ d;
+ k = 0xCA62C1D6;
+ }
+
+ auto tmp = rol32(a, 5) + f + e + k + words[i];
+ e = d;
+ d = c;
+ c = rol32(b, 30);
+ b = a;
+ a = tmp;
+ }
+
+ m_digest[0] += a;
+ m_digest[1] += b;
+ m_digest[2] += c;
+ m_digest[3] += d;
+ m_digest[4] += e;
+ }
- // Try hash-specific tools, like sha512sum
+ void clear() noexcept
+ {
+ m_digest[0] = 0x67452301;
+ m_digest[1] = 0xEFCDAB89;
+ m_digest[2] = 0x98BADCFE;
+ m_digest[3] = 0x10325476;
+ m_digest[4] = 0xC3D2E1F0;
+ }
+
+ const std::uint32_t* begin() const noexcept { return &m_digest[0]; }
+ const std::uint32_t* end() const noexcept { return &m_digest[5]; }
+
+ std::uint32_t m_digest[5];
+ };
+
+ struct Sha256Algorithm
{
- const auto ec_data = System::cmd_execute_and_capture_output(
- Strings::format(R"(sha%ssum "%s")", digest_size, path.u8string()));
- if (ec_data.exit_code == 0)
+ using underlying_type = std::uint32_t;
+ using message_length_type = std::uint64_t;
+ constexpr static std::size_t chunk_size = 64;
+
+ constexpr static std::size_t number_of_rounds = 64;
+
+ Sha256Algorithm() noexcept { clear(); }
+
+ void process_full_chunk(const std::array<uchar, chunk_size>& chunk) noexcept
{
- return parse_shasum_output(ec_data.output);
+ std::uint32_t words[64];
+
+ sha_fill_initial_words(&chunk[0], words);
+
+ for (std::size_t i = 16; i < number_of_rounds; ++i)
+ {
+ const auto w0 = words[i - 15];
+ const auto s0 = ror32(w0, 7) ^ ror32(w0, 18) ^ shr32(w0, 3);
+ const auto w1 = words[i - 2];
+ const auto s1 = ror32(w1, 17) ^ ror32(w1, 19) ^ shr32(w1, 10);
+ words[i] = words[i - 16] + s0 + words[i - 7] + s1;
+ }
+
+ std::uint32_t local[8];
+ std::copy(begin(), end(), std::begin(local));
+
+ for (std::size_t i = 0; i < number_of_rounds; ++i)
+ {
+ const auto a = local[0];
+ const auto b = local[1];
+ const auto c = local[2];
+
+ const auto s0 = ror32(a, 2) ^ ror32(a, 13) ^ ror32(a, 22);
+ const auto maj = (a & b) ^ (a & c) ^ (b & c);
+ const auto tmp1 = s0 + maj;
+
+ const auto e = local[4];
+
+ const auto s1 = ror32(e, 6) ^ ror32(e, 11) ^ ror32(e, 25);
+ const auto ch = (e & local[5]) ^ (~e & local[6]);
+ const auto tmp2 = local[7] + s1 + ch + round_constants[i] + words[i];
+
+ for (std::size_t j = 7; j > 0; --j)
+ {
+ local[j] = local[j - 1];
+ }
+ local[4] += tmp2;
+ local[0] = tmp1 + tmp2;
+ }
+
+ for (std::size_t i = 0; i < 8; ++i)
+ {
+ m_digest[i] += local[i];
+ }
}
- }
- // Try shasum
+ void clear() noexcept
+ {
+ m_digest[0] = 0x6a09e667;
+ m_digest[1] = 0xbb67ae85;
+ m_digest[2] = 0x3c6ef372;
+ m_digest[3] = 0xa54ff53a;
+ m_digest[4] = 0x510e527f;
+ m_digest[5] = 0x9b05688c;
+ m_digest[6] = 0x1f83d9ab;
+ m_digest[7] = 0x5be0cd19;
+ }
+
+ constexpr static std::array<std::uint32_t, number_of_rounds> round_constants = {
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
+ 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
+ 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
+ 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
+ 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
+ 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
+ 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
+ 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2};
+
+ std::uint32_t* begin() noexcept { return &m_digest[0]; }
+ std::uint32_t* end() noexcept { return &m_digest[8]; }
+
+ std::uint32_t m_digest[8];
+ };
+
+ struct Sha512Algorithm
{
- const auto ec_data = System::cmd_execute_and_capture_output(
- Strings::format(R"(shasum -a %s "%s")", digest_size, path.u8string()));
- if (ec_data.exit_code == 0)
+ using underlying_type = std::uint64_t;
+ using message_length_type = UInt128;
+ constexpr static std::size_t chunk_size = 128; // = 1024 / 8
+
+ constexpr static std::size_t number_of_rounds = 80;
+
+ Sha512Algorithm() noexcept { clear(); }
+
+ void process_full_chunk(const std::array<uchar, chunk_size>& chunk) noexcept
+ {
+ std::uint64_t words[80];
+
+ sha_fill_initial_words(&chunk[0], words);
+
+ for (std::size_t i = 16; i < number_of_rounds; ++i)
+ {
+ const auto w0 = words[i - 15];
+ const auto s0 = ror64(w0, 1) ^ ror64(w0, 8) ^ shr64(w0, 7);
+ const auto w1 = words[i - 2];
+ const auto s1 = ror64(w1, 19) ^ ror64(w1, 61) ^ shr64(w1, 6);
+ words[i] = words[i - 16] + s0 + words[i - 7] + s1;
+ }
+
+ std::uint64_t local[8];
+ std::copy(begin(), end(), std::begin(local));
+
+ for (std::size_t i = 0; i < number_of_rounds; ++i)
+ {
+ const auto a = local[0];
+ const auto b = local[1];
+ const auto c = local[2];
+
+ const auto s0 = ror64(a, 28) ^ ror64(a, 34) ^ ror64(a, 39);
+ const auto maj = (a & b) ^ (a & c) ^ (b & c);
+ const auto tmp0 = s0 + maj;
+
+ const auto e = local[4];
+
+ const auto s1 = ror64(e, 14) ^ ror64(e, 18) ^ ror64(e, 41);
+ const auto ch = (e & local[5]) ^ (~e & local[6]);
+ const auto tmp1 = local[7] + s1 + ch + round_constants[i] + words[i];
+
+ for (std::size_t j = 7; j > 0; --j)
+ {
+ local[j] = local[j - 1];
+ }
+ local[4] += tmp1;
+ local[0] = tmp0 + tmp1;
+ }
+
+ for (std::size_t i = 0; i < 8; ++i)
+ {
+ m_digest[i] += local[i];
+ }
+ }
+
+ void clear() noexcept
{
- return parse_shasum_output(ec_data.output);
+ m_digest[0] = 0x6a09e667f3bcc908;
+ m_digest[1] = 0xbb67ae8584caa73b;
+ m_digest[2] = 0x3c6ef372fe94f82b;
+ m_digest[3] = 0xa54ff53a5f1d36f1;
+ m_digest[4] = 0x510e527fade682d1;
+ m_digest[5] = 0x9b05688c2b3e6c1f;
+ m_digest[6] = 0x1f83d9abfb41bd6b;
+ m_digest[7] = 0x5be0cd19137e2179;
}
- }
- Checks::exit_with_message(VCPKG_LINE_INFO, "Could not hash file %s with %s", path.u8string(), hash_type);
+ constexpr static std::array<std::uint64_t, number_of_rounds> round_constants = {
+ 0x428a2f98d728ae22, 0x7137449123ef65cd, 0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc, 0x3956c25bf348b538,
+ 0x59f111f1b605d019, 0x923f82a4af194f9b, 0xab1c5ed5da6d8118, 0xd807aa98a3030242, 0x12835b0145706fbe,
+ 0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2, 0x72be5d74f27b896f, 0x80deb1fe3b1696b1, 0x9bdc06a725c71235,
+ 0xc19bf174cf692694, 0xe49b69c19ef14ad2, 0xefbe4786384f25e3, 0x0fc19dc68b8cd5b5, 0x240ca1cc77ac9c65,
+ 0x2de92c6f592b0275, 0x4a7484aa6ea6e483, 0x5cb0a9dcbd41fbd4, 0x76f988da831153b5, 0x983e5152ee66dfab,
+ 0xa831c66d2db43210, 0xb00327c898fb213f, 0xbf597fc7beef0ee4, 0xc6e00bf33da88fc2, 0xd5a79147930aa725,
+ 0x06ca6351e003826f, 0x142929670a0e6e70, 0x27b70a8546d22ffc, 0x2e1b21385c26c926, 0x4d2c6dfc5ac42aed,
+ 0x53380d139d95b3df, 0x650a73548baf63de, 0x766a0abb3c77b2a8, 0x81c2c92e47edaee6, 0x92722c851482353b,
+ 0xa2bfe8a14cf10364, 0xa81a664bbc423001, 0xc24b8b70d0f89791, 0xc76c51a30654be30, 0xd192e819d6ef5218,
+ 0xd69906245565a910, 0xf40e35855771202a, 0x106aa07032bbd1b8, 0x19a4c116b8d2d0c8, 0x1e376c085141ab53,
+ 0x2748774cdf8eeb99, 0x34b0bcb5e19b48a8, 0x391c0cb3c5c95a63, 0x4ed8aa4ae3418acb, 0x5b9cca4f7763e373,
+ 0x682e6ff3d6b2b8a3, 0x748f82ee5defb2fc, 0x78a5636f43172f60, 0x84c87814a1f0ab72, 0x8cc702081a6439ec,
+ 0x90befffa23631e28, 0xa4506cebde82bde9, 0xbef9a3f7b2c67915, 0xc67178f2e372532b, 0xca273eceea26619c,
+ 0xd186b8c721c0c207, 0xeada7dd6cde0eb1e, 0xf57d4f7fee6ed178, 0x06f067aa72176fba, 0x0a637dc5a2c898a6,
+ 0x113f9804bef90dae, 0x1b710b35131c471b, 0x28db77f523047d84, 0x32caab7b40c72493, 0x3c9ebe0a15c9bebc,
+ 0x431d67c49c100d4c, 0x4cc5d4becb3e42b6, 0x597f299cfc657e2a, 0x5fcb6fab3ad6faec, 0x6c44198c4a475817};
+
+ std::uint64_t* begin() noexcept { return &m_digest[0]; }
+ std::uint64_t* end() noexcept { return &m_digest[8]; }
+
+ std::uint64_t m_digest[8];
+ };
+
+ // This is required on older compilers, since it was required in C++14
+ constexpr std::array<std::uint32_t, Sha256Algorithm::number_of_rounds> Sha256Algorithm::round_constants;
+ constexpr std::array<std::uint64_t, Sha512Algorithm::number_of_rounds> Sha512Algorithm::round_constants;
+#endif
}
- std::string get_string_hash(const std::string& s, const std::string& hash_type)
+ std::unique_ptr<Hasher> get_hasher_for(Algorithm algo) noexcept
{
- const std::string digest_size = get_digest_size(hash_type);
- verify_has_only_allowed_chars(s);
+#if defined(_WIN32)
+ return std::make_unique<BCryptHasher>(algo);
+#else
+ switch (algo)
+ {
+ case Algorithm::Sha1: return std::make_unique<ShaHasher<Sha1Algorithm>>();
+ case Algorithm::Sha256: return std::make_unique<ShaHasher<Sha256Algorithm>>();
+ case Algorithm::Sha512: return std::make_unique<ShaHasher<Sha512Algorithm>>();
+ default: vcpkg::Checks::exit_with_message(VCPKG_LINE_INFO, "Unknown hashing algorithm: %s", algo);
+ }
+#endif
+ }
- // Try hash-specific tools, like sha512sum
+ template<class F>
+ static std::string do_hash(Algorithm algo, const F& f) noexcept
+ {
+#if defined(_WIN32)
+ auto hasher = BCryptHasher(algo);
+ return f(hasher);
+#else
+ switch (algo)
{
- const auto ec_data =
- System::cmd_execute_and_capture_output(Strings::format(R"(echo -n "%s" | sha%ssum)", s, digest_size));
- if (ec_data.exit_code == 0)
+ case Algorithm::Sha1:
+ {
+ auto hasher = ShaHasher<Sha1Algorithm>();
+ return f(hasher);
+ }
+ case Algorithm::Sha256:
+ {
+ auto hasher = ShaHasher<Sha256Algorithm>();
+ return f(hasher);
+ }
+ case Algorithm::Sha512:
{
- return parse_shasum_output(ec_data.output);
+ auto hasher = ShaHasher<Sha512Algorithm>();
+ return f(hasher);
}
+ default: vcpkg::Checks::exit_with_message(VCPKG_LINE_INFO, "Unknown hashing algorithm: %s", algo);
}
+#endif
+ }
+
+ std::string get_bytes_hash(const void* first, const void* last, Algorithm algo) noexcept
+ {
+ return do_hash(algo, [first, last](Hasher& hasher) {
+ hasher.add_bytes(first, last);
+ return hasher.get_hash();
+ });
+ }
- // Try shasum
+ std::string get_string_hash(StringView sv, Algorithm algo) noexcept
+ {
+ return get_bytes_hash(sv.data(), sv.data() + sv.size(), algo);
+ }
+
+ // TODO: use Files::Filesystem to open a file
+ std::string get_file_hash(const Files::Filesystem&,
+ const fs::path& path,
+ Algorithm algo,
+ std::error_code& ec) noexcept
+ {
+ auto file = std::fstream(path.c_str(), std::ios_base::in | std::ios_base::binary);
+ if (!file)
{
- const auto ec_data = System::cmd_execute_and_capture_output(
- Strings::format(R"(echo -n "%s" | shasum -a %s)", s, digest_size));
- if (ec_data.exit_code == 0)
- {
- return parse_shasum_output(ec_data.output);
- }
+ ec.assign(ENOENT, std::system_category());
+ return {};
}
- Checks::exit_with_message(VCPKG_LINE_INFO, "Could not hash input string with %s", hash_type);
+ return do_hash(algo, [&file, &ec](Hasher& hasher) {
+ constexpr std::size_t buffer_size = 4096;
+ auto buffer = std::make_unique<char[]>(buffer_size);
+ for (;;)
+ {
+ file.read(buffer.get(), buffer_size);
+ if (file.eof())
+ {
+ hasher.add_bytes(buffer.get(), buffer.get() + file.gcount());
+ return hasher.get_hash();
+ }
+ else if (file)
+ {
+ hasher.add_bytes(buffer.get(), buffer.get() + buffer_size);
+ }
+ else
+ {
+ ec = std::io_errc::stream;
+ return std::string();
+ }
+ }
+ });
}
-#endif
}
diff --git a/toolsrc/src/vcpkg/base/stringview.cpp b/toolsrc/src/vcpkg/base/stringview.cpp
index d0b2cd43a..6b159db48 100644
--- a/toolsrc/src/vcpkg/base/stringview.cpp
+++ b/toolsrc/src/vcpkg/base/stringview.cpp
@@ -76,8 +76,10 @@ namespace vcpkg
std::string StringView::to_string() const { return std::string(m_ptr, m_size); }
void StringView::to_string(std::string& s) const { s.append(m_ptr, m_size); }
- bool StringView::operator==(StringView other) const
+ bool operator==(StringView lhs, StringView rhs) noexcept
{
- return other.size() == size() && memcmp(data(), other.data(), size()) == 0;
+ return lhs.size() == rhs.size() && memcmp(lhs.data(), rhs.data(), lhs.size()) == 0;
}
+
+ bool operator!=(StringView lhs, StringView rhs) noexcept { return !(lhs == rhs); }
}
diff --git a/toolsrc/src/vcpkg/build.cpp b/toolsrc/src/vcpkg/build.cpp
index aeb01b27d..618e4126b 100644
--- a/toolsrc/src/vcpkg/build.cpp
+++ b/toolsrc/src/vcpkg/build.cpp
@@ -53,6 +53,7 @@ namespace vcpkg::Build::Command
const Build::BuildPackageOptions build_package_options{
Build::UseHeadVersion::NO,
Build::AllowDownloads::YES,
+ Build::OnlyDownloads::NO,
Build::CleanBuildtrees::NO,
Build::CleanPackages::NO,
Build::CleanDownloads::NO,
@@ -395,6 +396,7 @@ namespace vcpkg::Build
{"CMD", "BUILD"},
{"PORT", config.scf.core_paragraph->name},
{"CURRENT_PORT_DIR", config.port_dir},
+ {"VCPKG_ROOT_PATH", paths.root},
{"TARGET_TRIPLET", triplet.canonical_name()},
{"TARGET_TRIPLET_FILE", paths.get_triplet_file_path(triplet).u8string()},
{"VCPKG_PLATFORM_TOOLSET", toolset.version.c_str()},
@@ -407,6 +409,11 @@ namespace vcpkg::Build
{"VCPKG_CONCURRENCY", std::to_string(get_concurrency())},
};
+ if (Util::Enum::to_bool(config.build_package_options.only_downloads))
+ {
+ variables.push_back({"VCPKG_DOWNLOAD_MODE", "true"});
+ }
+
if (!System::get_environment_variable("VCPKG_FORCE_SYSTEM_BINARIES").has_value())
{
variables.push_back({"GIT", git_exe_path});
@@ -486,32 +493,33 @@ namespace vcpkg::Build
}
else
{
- hash = Hash::get_file_hash(fs, triplet_file_path, "SHA1");
+ const auto algo = Hash::Algorithm::Sha1;
+ hash = Hash::get_file_hash(VCPKG_LINE_INFO, fs, triplet_file_path, algo);
if (auto p = pre_build_info.external_toolchain_file.get())
{
hash += "-";
- hash += Hash::get_file_hash(fs, *p, "SHA1");
+ hash += Hash::get_file_hash(VCPKG_LINE_INFO, fs, *p, algo);
}
else if (pre_build_info.cmake_system_name == "Linux")
{
hash += "-";
- hash += Hash::get_file_hash(fs, paths.scripts / "toolchains" / "linux.cmake", "SHA1");
+ hash += Hash::get_file_hash(VCPKG_LINE_INFO, fs, paths.scripts / "toolchains" / "linux.cmake", algo);
}
else if (pre_build_info.cmake_system_name == "Darwin")
{
hash += "-";
- hash += Hash::get_file_hash(fs, paths.scripts / "toolchains" / "osx.cmake", "SHA1");
+ hash += Hash::get_file_hash(VCPKG_LINE_INFO, fs, paths.scripts / "toolchains" / "osx.cmake", algo);
}
else if (pre_build_info.cmake_system_name == "FreeBSD")
{
hash += "-";
- hash += Hash::get_file_hash(fs, paths.scripts / "toolchains" / "freebsd.cmake", "SHA1");
+ hash += Hash::get_file_hash(VCPKG_LINE_INFO, fs, paths.scripts / "toolchains" / "freebsd.cmake", algo);
}
else if (pre_build_info.cmake_system_name == "Android")
{
hash += "-";
- hash += Hash::get_file_hash(fs, paths.scripts / "toolchains" / "android.cmake", "SHA1");
+ hash += Hash::get_file_hash(VCPKG_LINE_INFO, fs, paths.scripts / "toolchains" / "android.cmake", algo);
}
s_hash_cache.emplace(triplet_file_path, hash);
@@ -527,6 +535,7 @@ namespace vcpkg::Build
const BuildPackageConfig& config)
{
auto& fs = paths.get_filesystem();
+
#if defined(_WIN32)
const fs::path& powershell_exe_path = paths.get_tool_exe("powershell-core");
if (!fs.exists(powershell_exe_path.parent_path() / "powershell.exe"))
@@ -558,6 +567,14 @@ namespace vcpkg::Build
#else
const int return_code = System::cmd_execute_clean(command, env);
#endif
+ // With the exception of empty packages, builds in "Download Mode" always result in failure.
+ if (config.build_package_options.only_downloads == Build::OnlyDownloads::YES)
+ {
+ // TODO: Capture executed command output and evaluate whether the failure was intended.
+ // If an unintended error occurs then return a BuildResult::DOWNLOAD_FAILURE status.
+ return BuildResult::DOWNLOADED;
+ }
+
const auto buildtimeus = timer.microseconds();
const auto spec_string = spec.to_string();
@@ -651,8 +668,9 @@ namespace vcpkg::Build
{
if (fs::is_regular_file(fs.status(VCPKG_LINE_INFO, port_file)))
{
- port_files.emplace_back(port_file.path().filename().u8string(),
- vcpkg::Hash::get_file_hash(fs, port_file, "SHA1"));
+ port_files.emplace_back(
+ port_file.path().filename().u8string(),
+ vcpkg::Hash::get_file_hash(VCPKG_LINE_INFO, fs, port_file, Hash::Algorithm::Sha1));
if (port_files.size() > max_port_file_count)
{
@@ -679,7 +697,10 @@ namespace vcpkg::Build
abi_tag_entries.emplace_back(
"vcpkg_fixup_cmake_targets",
- vcpkg::Hash::get_file_hash(fs, paths.scripts / "cmake" / "vcpkg_fixup_cmake_targets.cmake", "SHA1"));
+ vcpkg::Hash::get_file_hash(VCPKG_LINE_INFO,
+ fs,
+ paths.scripts / "cmake" / "vcpkg_fixup_cmake_targets.cmake",
+ Hash::Algorithm::Sha1));
abi_tag_entries.emplace_back("triplet", pre_build_info.triplet_abi_tag);
abi_tag_entries.emplace_back("features", Strings::join(";", config.feature_list));
@@ -688,7 +709,8 @@ namespace vcpkg::Build
{
abi_tag_entries.emplace_back(
"public_abi_override",
- Hash::get_string_hash(pre_build_info.public_abi_override.value_or_exit(VCPKG_LINE_INFO), "SHA1"));
+ Hash::get_string_hash(pre_build_info.public_abi_override.value_or_exit(VCPKG_LINE_INFO),
+ Hash::Algorithm::Sha1));
}
if (config.build_package_options.use_head_version == UseHeadVersion::YES)
@@ -717,7 +739,8 @@ namespace vcpkg::Build
const auto abi_file_path = paths.buildtrees / name / (triplet.canonical_name() + ".vcpkg_abi_info.txt");
fs.write_contents(abi_file_path, full_abi_info, VCPKG_LINE_INFO);
- return AbiTagAndFile{Hash::get_file_hash(fs, abi_file_path, "SHA1"), abi_file_path};
+ return AbiTagAndFile{Hash::get_file_hash(VCPKG_LINE_INFO, fs, abi_file_path, Hash::Algorithm::Sha1),
+ abi_file_path};
}
System::print2(
@@ -786,20 +809,23 @@ namespace vcpkg::Build
const std::string& name = config.scf.core_paragraph->name;
std::vector<FeatureSpec> required_fspecs = compute_required_feature_specs(config, status_db);
- std::vector<FeatureSpec> required_fspecs_copy = required_fspecs;
// extract out the actual package ids
auto dep_pspecs = Util::fmap(required_fspecs, [](FeatureSpec const& fspec) { return fspec.spec(); });
Util::sort_unique_erase(dep_pspecs);
// Find all features that aren't installed. This mutates required_fspecs.
- Util::erase_remove_if(required_fspecs, [&](FeatureSpec const& fspec) {
- return status_db.is_installed(fspec) || fspec.name() == name;
- });
-
- if (!required_fspecs.empty())
+ // Skip this validation when running in Download Mode.
+ if (config.build_package_options.only_downloads != Build::OnlyDownloads::YES)
{
- return {BuildResult::CASCADED_DUE_TO_MISSING_DEPENDENCIES, std::move(required_fspecs)};
+ Util::erase_remove_if(required_fspecs, [&](FeatureSpec const& fspec) {
+ return status_db.is_installed(fspec) || fspec.name() == name;
+ });
+
+ if (!required_fspecs.empty())
+ {
+ return {BuildResult::CASCADED_DUE_TO_MISSING_DEPENDENCIES, std::move(required_fspecs)};
+ }
}
const PackageSpec spec =
@@ -810,7 +836,10 @@ namespace vcpkg::Build
// dep_pspecs was not destroyed
for (auto&& pspec : dep_pspecs)
{
- if (pspec == spec) continue;
+ if (pspec == spec || Util::Enum::to_bool(config.build_package_options.only_downloads))
+ {
+ continue;
+ }
const auto status_it = status_db.find_installed(pspec);
Checks::check_exit(VCPKG_LINE_INFO, status_it != status_db.end());
dependency_abis.emplace_back(
@@ -872,14 +901,14 @@ namespace vcpkg::Build
System::printf("Could not locate cached archive: %s\n", archive_path.u8string());
}
+ ExtendedBuildResult result = do_build_package_and_clean_buildtrees(
+ paths, pre_build_info, spec, pre_build_info.public_abi_override.value_or(abi_tag_and_file->tag), config);
+
fs.create_directories(abi_package_dir, ec);
Checks::check_exit(VCPKG_LINE_INFO, !ec, "Coud not create directory %s", abi_package_dir.u8string());
fs.copy_file(abi_tag_and_file->tag_file, abi_file_in_package, fs::stdfs::copy_options::none, ec);
Checks::check_exit(VCPKG_LINE_INFO, !ec, "Could not copy into file: %s", abi_file_in_package.u8string());
- ExtendedBuildResult result = do_build_package_and_clean_buildtrees(
- paths, pre_build_info, spec, pre_build_info.public_abi_override.value_or(abi_tag_and_file->tag), config);
-
if (config.build_package_options.binary_caching == BinaryCaching::YES && result.code == BuildResult::SUCCEEDED)
{
const auto tmp_archive_path = paths.buildtrees / spec.name() / (spec.triplet().to_string() + ".zip");
@@ -942,6 +971,7 @@ namespace vcpkg::Build
static const std::string POST_BUILD_CHECKS_FAILED_STRING = "POST_BUILD_CHECKS_FAILED";
static const std::string CASCADED_DUE_TO_MISSING_DEPENDENCIES_STRING = "CASCADED_DUE_TO_MISSING_DEPENDENCIES";
static const std::string EXCLUDED_STRING = "EXCLUDED";
+ static const std::string DOWNLOADED_STRING = "DOWNLOADED";
switch (build_result)
{
@@ -952,6 +982,7 @@ namespace vcpkg::Build
case BuildResult::FILE_CONFLICTS: return FILE_CONFLICTS_STRING;
case BuildResult::CASCADED_DUE_TO_MISSING_DEPENDENCIES: return CASCADED_DUE_TO_MISSING_DEPENDENCIES_STRING;
case BuildResult::EXCLUDED: return EXCLUDED_STRING;
+ case BuildResult::DOWNLOADED: return DOWNLOADED_STRING;
default: Checks::unreachable(VCPKG_LINE_INFO);
}
}
diff --git a/toolsrc/src/vcpkg/commands.ci.cpp b/toolsrc/src/vcpkg/commands.ci.cpp
index f0f162f5c..6e0a71adf 100644
--- a/toolsrc/src/vcpkg/commands.ci.cpp
+++ b/toolsrc/src/vcpkg/commands.ci.cpp
@@ -212,6 +212,7 @@ namespace vcpkg::Commands::CI
const Build::BuildPackageOptions build_options = {
Build::UseHeadVersion::NO,
Build::AllowDownloads::YES,
+ Build::OnlyDownloads::NO,
Build::CleanBuildtrees::YES,
Build::CleanPackages::YES,
Build::CleanDownloads::NO,
@@ -356,6 +357,7 @@ namespace vcpkg::Commands::CI
const Build::BuildPackageOptions install_plan_options = {
Build::UseHeadVersion::NO,
Build::AllowDownloads::YES,
+ Build::OnlyDownloads::NO,
Build::CleanBuildtrees::YES,
Build::CleanPackages::YES,
Build::CleanDownloads::NO,
diff --git a/toolsrc/src/vcpkg/commands.cpp b/toolsrc/src/vcpkg/commands.cpp
index 3ac568979..1f424a559 100644
--- a/toolsrc/src/vcpkg/commands.cpp
+++ b/toolsrc/src/vcpkg/commands.cpp
@@ -47,6 +47,7 @@ namespace vcpkg::Commands
{"autocomplete", &Autocomplete::perform_and_exit},
{"hash", &Hash::perform_and_exit},
{"fetch", &Fetch::perform_and_exit},
+ {"x-history", &PortHistory::perform_and_exit},
{"x-vsinstances", &X_VSInstances::perform_and_exit},
};
return t;
@@ -99,8 +100,15 @@ namespace vcpkg::Commands::Hash
Util::unused(args.parse_arguments(COMMAND_STRUCTURE));
const fs::path file_to_hash = args.command_arguments[0];
- const std::string algorithm = args.command_arguments.size() == 2 ? args.command_arguments[1] : "SHA512";
- const std::string hash = vcpkg::Hash::get_file_hash(paths.get_filesystem(), file_to_hash, algorithm);
+
+ auto algorithm = vcpkg::Hash::Algorithm::Sha512;
+ if (args.command_arguments.size() == 2)
+ {
+ algorithm = vcpkg::Hash::algorithm_from_string(args.command_arguments[1]).value_or_exit(VCPKG_LINE_INFO);
+ }
+
+ const std::string hash =
+ vcpkg::Hash::get_file_hash(VCPKG_LINE_INFO, paths.get_filesystem(), file_to_hash, algorithm);
System::print2(hash, '\n');
Checks::exit_success(VCPKG_LINE_INFO);
}
diff --git a/toolsrc/src/vcpkg/commands.create.cpp b/toolsrc/src/vcpkg/commands.create.cpp
index 31bf97f30..e04599dfe 100644
--- a/toolsrc/src/vcpkg/commands.create.cpp
+++ b/toolsrc/src/vcpkg/commands.create.cpp
@@ -24,7 +24,7 @@ namespace vcpkg::Commands::Create
const fs::path& cmake_exe = paths.get_tool_exe(Tools::CMAKE);
- std::vector<System::CMakeVariable> cmake_args{{"CMD", "CREATE"}, {"PORT", port_name}, {"URL", url}};
+ std::vector<System::CMakeVariable> cmake_args{{"CMD", "CREATE"}, {"PORT", port_name}, {"URL", url}, {"VCPKG_ROOT_PATH", paths.root}};
if (args.command_arguments.size() >= 3)
{
diff --git a/toolsrc/src/vcpkg/commands.dependinfo.cpp b/toolsrc/src/vcpkg/commands.dependinfo.cpp
index faf207980..79cbba590 100644
--- a/toolsrc/src/vcpkg/commands.dependinfo.cpp
+++ b/toolsrc/src/vcpkg/commands.dependinfo.cpp
@@ -228,8 +228,8 @@ namespace vcpkg::Commands::DependInfo
const CommandStructure COMMAND_STRUCTURE = {
Help::create_example_string("depend-info sqlite3"),
- 0,
- SIZE_MAX,
+ 1,
+ 1,
{DEPEND_SWITCHES, DEPEND_SETTINGS},
nullptr,
};
diff --git a/toolsrc/src/vcpkg/commands.porthistory.cpp b/toolsrc/src/vcpkg/commands.porthistory.cpp
new file mode 100644
index 000000000..44fccbb7f
--- /dev/null
+++ b/toolsrc/src/vcpkg/commands.porthistory.cpp
@@ -0,0 +1,91 @@
+#include "pch.h"
+
+#include <vcpkg/commands.h>
+#include <vcpkg/help.h>
+
+#include <vcpkg/base/system.print.h>
+#include <vcpkg/base/system.process.h>
+#include <vcpkg/base/util.h>
+
+namespace vcpkg::Commands::PortHistory
+{
+ struct PortControlVersion
+ {
+ std::string commit_id;
+ std::string version;
+ std::string date;
+ };
+
+ static System::ExitCodeAndOutput run_git_command(const VcpkgPaths& paths, const std::string& cmd)
+ {
+ const fs::path& git_exe = paths.get_tool_exe(Tools::GIT);
+ const fs::path dot_git_dir = paths.root / ".git";
+
+ const std::string full_cmd =
+ Strings::format(R"("%s" --git-dir="%s" %s)", git_exe.u8string(), dot_git_dir.u8string(), cmd);
+
+ auto output = System::cmd_execute_and_capture_output(full_cmd);
+ Checks::check_exit(VCPKG_LINE_INFO, output.exit_code == 0, "Failed to run command: %s", full_cmd);
+ return output;
+ }
+
+ static std::string get_version_from_commit(const VcpkgPaths& paths,
+ const std::string& commit_id,
+ const std::string& port_name)
+ {
+ const std::string cmd = Strings::format(R"(show %s:ports/%s/CONTROL)", commit_id, port_name);
+ auto output = run_git_command(paths, cmd);
+
+ const auto version = Strings::find_at_most_one_enclosed(output.output, "Version: ", "\n");
+ Checks::check_exit(VCPKG_LINE_INFO, version.has_value(), "CONTROL file does not have a 'Version' field");
+ return version.get()->to_string();
+ }
+
+ static std::vector<PortControlVersion> read_versions_from_log(const VcpkgPaths& paths, const std::string& port_name)
+ {
+ const std::string cmd =
+ Strings::format(R"(log --format="%%H %%cd" --date=short --left-only -- ports/%s/.)", port_name);
+ auto output = run_git_command(paths, cmd);
+
+ auto commits = Util::fmap(
+ Strings::split(output.output, "\n"), [](const std::string& line) -> auto {
+ auto parts = Strings::split(line, " ");
+ return std::make_pair(parts[0], parts[1]);
+ });
+
+ std::vector<PortControlVersion> ret;
+ std::string last_version;
+ for (auto&& commit_date_pair : commits)
+ {
+ const std::string version = get_version_from_commit(paths, commit_date_pair.first, port_name);
+ if (last_version != version)
+ {
+ ret.emplace_back(PortControlVersion{commit_date_pair.first, version, commit_date_pair.second});
+ last_version = version;
+ }
+ }
+ return ret;
+ }
+
+ const CommandStructure COMMAND_STRUCTURE = {
+ Help::create_example_string("history <port>"),
+ 1,
+ 1,
+ {},
+ nullptr,
+ };
+
+ void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths)
+ {
+ Util::unused(args.parse_arguments(COMMAND_STRUCTURE));
+
+ std::string port_name = args.command_arguments.at(0);
+ std::vector<PortControlVersion> versions = read_versions_from_log(paths, port_name);
+ System::print2(" version date vcpkg commit\n");
+ for (auto&& version : versions)
+ {
+ System::printf("%20.20s %s %s\n", version.version, version.date, version.commit_id);
+ }
+ Checks::exit_success(VCPKG_LINE_INFO);
+ }
+}
diff --git a/toolsrc/src/vcpkg/commands.upgrade.cpp b/toolsrc/src/vcpkg/commands.upgrade.cpp
index 1e64b2eb6..b1dbf6194 100644
--- a/toolsrc/src/vcpkg/commands.upgrade.cpp
+++ b/toolsrc/src/vcpkg/commands.upgrade.cpp
@@ -154,6 +154,7 @@ namespace vcpkg::Commands::Upgrade
const Build::BuildPackageOptions install_plan_options = {
Build::UseHeadVersion::NO,
Build::AllowDownloads::YES,
+ Build::OnlyDownloads::NO,
Build::CleanBuildtrees::NO,
Build::CleanPackages::NO,
Build::CleanDownloads::NO,
diff --git a/toolsrc/src/vcpkg/export.cpp b/toolsrc/src/vcpkg/export.cpp
index 5ceb47adf..349d9aefd 100644
--- a/toolsrc/src/vcpkg/export.cpp
+++ b/toolsrc/src/vcpkg/export.cpp
@@ -73,6 +73,7 @@ namespace vcpkg::Export
static constexpr Build::BuildPackageOptions BUILD_OPTIONS = {
Build::UseHeadVersion::NO,
Build::AllowDownloads::YES,
+ Build::OnlyDownloads::NO,
Build::CleanBuildtrees::NO,
Build::CleanPackages::NO,
Build::CleanDownloads::NO,
diff --git a/toolsrc/src/vcpkg/help.cpp b/toolsrc/src/vcpkg/help.cpp
index 9b2751739..0c53536fb 100644
--- a/toolsrc/src/vcpkg/help.cpp
+++ b/toolsrc/src/vcpkg/help.cpp
@@ -91,6 +91,7 @@ namespace vcpkg::Help
" vcpkg list List installed packages\n"
" vcpkg update Display list of packages for updating\n"
" vcpkg upgrade Rebuild all outdated packages\n"
+ " vcpkg x-history <pkg> Shows the history of CONTROL versions of a package\n"
" vcpkg hash <file> [alg] Hash a file by specific algorithm, default SHA512\n"
" vcpkg help topics Display the list of help topics\n"
" vcpkg help <topic> Display help for a specific topic\n"
@@ -124,7 +125,7 @@ namespace vcpkg::Help
" (default: " ENVVAR(VCPKG_ROOT) //
")\n"
"\n"
- " --scripts-root=<path> Specify the scripts root directory\n"
+ " --x-scripts-root=<path> (Experimental) Specify the scripts root directory\n"
"\n"
" @response_file Specify a "
"response file to provide additional parameters\n"
diff --git a/toolsrc/src/vcpkg/install.cpp b/toolsrc/src/vcpkg/install.cpp
index 1812f1624..009965887 100644
--- a/toolsrc/src/vcpkg/install.cpp
+++ b/toolsrc/src/vcpkg/install.cpp
@@ -342,6 +342,13 @@ namespace vcpkg::Install
return Build::build_package(paths, build_config, status_db);
}();
+ if (BuildResult::DOWNLOADED == result.code)
+ {
+ System::print2(
+ System::Color::success, "Downloaded sources for package ", display_name_with_features, "\n");
+ return result;
+ }
+
if (result.code != Build::BuildResult::SUCCEEDED)
{
System::print2(System::Color::error, Build::create_error_message(result.code, action.spec), "\n");
@@ -467,16 +474,18 @@ namespace vcpkg::Install
static constexpr StringLiteral OPTION_DRY_RUN = "--dry-run";
static constexpr StringLiteral OPTION_USE_HEAD_VERSION = "--head";
static constexpr StringLiteral OPTION_NO_DOWNLOADS = "--no-downloads";
+ static constexpr StringLiteral OPTION_ONLY_DOWNLOADS = "--only-downloads";
static constexpr StringLiteral OPTION_RECURSE = "--recurse";
static constexpr StringLiteral OPTION_KEEP_GOING = "--keep-going";
static constexpr StringLiteral OPTION_XUNIT = "--x-xunit";
static constexpr StringLiteral OPTION_USE_ARIA2 = "--x-use-aria2";
static constexpr StringLiteral OPTION_CLEAN_AFTER_BUILD = "--clean-after-build";
- static constexpr std::array<CommandSwitch, 7> INSTALL_SWITCHES = {{
+ static constexpr std::array<CommandSwitch, 8> INSTALL_SWITCHES = {{
{OPTION_DRY_RUN, "Do not actually build or install"},
{OPTION_USE_HEAD_VERSION, "Install the libraries on the command line using the latest upstream sources"},
{OPTION_NO_DOWNLOADS, "Do not download new sources"},
+ {OPTION_ONLY_DOWNLOADS, "Download sources but don't build packages"},
{OPTION_RECURSE, "Allow removal of packages as part of installation"},
{OPTION_KEEP_GOING, "Continue installing packages on failure"},
{OPTION_USE_ARIA2, "Use aria2 to perform download tasks"},
@@ -631,10 +640,12 @@ namespace vcpkg::Install
const bool dry_run = Util::Sets::contains(options.switches, OPTION_DRY_RUN);
const bool use_head_version = Util::Sets::contains(options.switches, (OPTION_USE_HEAD_VERSION));
const bool no_downloads = Util::Sets::contains(options.switches, (OPTION_NO_DOWNLOADS));
+ const bool only_downloads = Util::Sets::contains(options.switches, (OPTION_ONLY_DOWNLOADS));
const bool is_recursive = Util::Sets::contains(options.switches, (OPTION_RECURSE));
const bool use_aria2 = Util::Sets::contains(options.switches, (OPTION_USE_ARIA2));
const bool clean_after_build = Util::Sets::contains(options.switches, (OPTION_CLEAN_AFTER_BUILD));
- const KeepGoing keep_going = to_keep_going(Util::Sets::contains(options.switches, OPTION_KEEP_GOING));
+ const KeepGoing keep_going =
+ to_keep_going(Util::Sets::contains(options.switches, OPTION_KEEP_GOING) || only_downloads);
auto& fs = paths.get_filesystem();
@@ -647,11 +658,12 @@ namespace vcpkg::Install
const Build::BuildPackageOptions install_plan_options = {
Util::Enum::to_enum<Build::UseHeadVersion>(use_head_version),
Util::Enum::to_enum<Build::AllowDownloads>(!no_downloads),
+ Util::Enum::to_enum<Build::OnlyDownloads>(only_downloads),
clean_after_build ? Build::CleanBuildtrees::YES : Build::CleanBuildtrees::NO,
clean_after_build ? Build::CleanPackages::YES : Build::CleanPackages::NO,
clean_after_build ? Build::CleanDownloads::YES : Build::CleanDownloads::NO,
download_tool,
- GlobalState::g_binary_caching ? Build::BinaryCaching::YES : Build::BinaryCaching::NO,
+ (GlobalState::g_binary_caching && !only_downloads) ? Build::BinaryCaching::YES : Build::BinaryCaching::NO,
Build::FailOnTombstone::NO,
};
diff --git a/toolsrc/src/vcpkg/metrics.cpp b/toolsrc/src/vcpkg/metrics.cpp
index 11f613830..b8c55919e 100644
--- a/toolsrc/src/vcpkg/metrics.cpp
+++ b/toolsrc/src/vcpkg/metrics.cpp
@@ -266,7 +266,7 @@ namespace vcpkg::Metrics
const auto match = *next;
if (match[0] != "00-00-00-00-00-00")
{
- return vcpkg::Hash::get_string_hash(match[0], "SHA256");
+ return vcpkg::Hash::get_string_hash(match[0].str(), Hash::Algorithm::Sha256);
}
++next;
}
diff --git a/toolsrc/src/vcpkg/parse.cpp b/toolsrc/src/vcpkg/parse.cpp
index 0509339c5..6015d9927 100644
--- a/toolsrc/src/vcpkg/parse.cpp
+++ b/toolsrc/src/vcpkg/parse.cpp
@@ -2,6 +2,7 @@
#include <vcpkg/parse.h>
+#include <vcpkg/base/system.print.h>
#include <vcpkg/base/util.h>
namespace vcpkg::Parse
@@ -44,6 +45,8 @@ namespace vcpkg::Parse
return nullptr;
}
+ static bool is_whitespace(char c) { return c == ' ' || c == '\t' || c == '\n' || c == '\r'; }
+
std::vector<std::string> parse_comma_list(const std::string& str)
{
if (str.empty())
@@ -53,26 +56,93 @@ namespace vcpkg::Parse
std::vector<std::string> out;
- size_t cur = 0;
+ auto iter = str.cbegin();
+
do
{
- auto pos = str.find(',', cur);
- if (pos == std::string::npos)
+ // Trim leading whitespace of each element
+ while (iter != str.cend() && is_whitespace(*iter))
+ {
+ ++iter;
+ }
+
+ // Allow commas inside of [].
+ bool bracket_nesting = false;
+
+ auto element_begin = iter;
+ auto element_end = iter;
+ while (iter != str.cend() && (*iter != ',' || bracket_nesting))
+ {
+ char value = *iter;
+
+ // do not support nested []
+ if (value == '[')
+ {
+ if (bracket_nesting)
+ {
+ Checks::exit_with_message(VCPKG_LINE_INFO,
+ "Lists do not support nested brackets, Did you forget a ']'?\n"
+ "> '%s'\n"
+ "> %s^\n",
+ str,
+ std::string(static_cast<int>(iter - str.cbegin()), ' '));
+ }
+ bracket_nesting = true;
+ }
+ else if (value == ']')
+ {
+ if (!bracket_nesting)
+ {
+ Checks::exit_with_message(VCPKG_LINE_INFO,
+ "Found unmatched ']'. Did you forget a '['?\n"
+ "> '%s'\n"
+ "> %s^\n",
+ str,
+ std::string(static_cast<int>(iter - str.cbegin()), ' '));
+ }
+ bracket_nesting = false;
+ }
+
+ ++iter;
+
+ // Trim ending whitespace
+ if (!is_whitespace(value))
+ {
+ // Update element_end after iter is incremented so it will be one past.
+ element_end = iter;
+ }
+ }
+
+ if (element_begin == element_end)
{
- out.push_back(str.substr(cur));
- break;
+ Checks::exit_with_message(VCPKG_LINE_INFO,
+ "Empty element in list\n"
+ "> '%s'\n"
+ "> %s^\n",
+ str,
+ std::string(static_cast<int>(element_begin - str.cbegin()), ' '));
}
- out.push_back(str.substr(cur, pos - cur));
+ out.push_back({element_begin, element_end});
- // skip comma and space
- ++pos;
- while (str[pos] == ' ')
+ if (iter != str.cend())
{
- ++pos;
+ Checks::check_exit(VCPKG_LINE_INFO, *iter == ',', "Internal parsing error - expected comma");
+
+ // Not at the end, must be at a comma that needs to be stepped over
+ ++iter;
+
+ if (iter == str.end())
+ {
+ Checks::exit_with_message(VCPKG_LINE_INFO,
+ "Empty element in list\n"
+ "> '%s'\n"
+ "> %s^\n",
+ str,
+ std::string(str.length(), ' '));
+ }
}
- cur = pos;
- } while (cur != std::string::npos);
+ } while (iter != str.cend());
return out;
}
diff --git a/toolsrc/src/vcpkg/vcpkgcmdarguments.cpp b/toolsrc/src/vcpkg/vcpkgcmdarguments.cpp
index e48340df7..5f99b85e5 100644
--- a/toolsrc/src/vcpkg/vcpkgcmdarguments.cpp
+++ b/toolsrc/src/vcpkg/vcpkgcmdarguments.cpp
@@ -146,10 +146,10 @@ namespace vcpkg
parse_value(arg_begin, arg_end, "--vcpkg-root", args.vcpkg_root_dir);
continue;
}
- if (Strings::starts_with(arg, "--scripts-root="))
+ if (Strings::starts_with(arg, "--x-scripts-root="))
{
parse_cojoined_value(
- arg.substr(sizeof("--scripts-root=") - 1), "--scripts-root", args.scripts_root_dir);
+ arg.substr(sizeof("--x-scripts-root=") - 1), "--x-scripts-root", args.scripts_root_dir);
continue;
}
if (arg == "--triplet")
@@ -430,7 +430,7 @@ namespace vcpkg
"--vcpkg-root <path>",
"Specify the vcpkg directory to use instead of current directory or tool directory");
System::printf(" %-40s %s\n",
- "--scripts-root=<path>",
- "Specify the scripts directory to use instead of default vcpkg scripts directory");
+ "--x-scripts-root=<path>",
+ "(Experimental) Specify the scripts directory to use instead of default vcpkg scripts directory");
}
}
diff --git a/toolsrc/src/vcpkg/vcpkgpaths.cpp b/toolsrc/src/vcpkg/vcpkgpaths.cpp
index 4f01ed03b..078121fcc 100644
--- a/toolsrc/src/vcpkg/vcpkgpaths.cpp
+++ b/toolsrc/src/vcpkg/vcpkgpaths.cpp
@@ -76,7 +76,7 @@ namespace vcpkg
Checks::exit_with_message(
VCPKG_LINE_INFO,
"Invalid scripts override directory: %s; "
- "create that directory or unset --scripts-root to use the default scripts location.",
+ "create that directory or unset --x-scripts-root to use the default scripts location.",
scripts_dir->u8string());
}