aboutsummaryrefslogtreecommitdiff
path: root/toolsrc/src
diff options
context:
space:
mode:
authornicole mazzuca <mazzucan@outlook.com>2020-05-29 14:09:03 -0700
committerGitHub <noreply@github.com>2020-05-29 14:09:03 -0700
commit09319cd79e3bee6a41bb76db739aef3f3644f19f (patch)
tree36e6b5bd5c17268edc062aa9c462f47fc4f400cf /toolsrc/src
parenta64dc07690bc8806e717e190f62eb58e198b599c (diff)
downloadvcpkg-09319cd79e3bee6a41bb76db739aef3f3644f19f.tar.gz
vcpkg-09319cd79e3bee6a41bb76db739aef3f3644f19f.zip
[vcpkg metrics] Allow someone to opt out after build (#11542)
* [vcpkg metrics] start using json library Additionally, add floats to the JSON library since they're required. * [vcpkg metrics] allow users to disable metrics after the build Additionally, as a drive by, fix UUID generation * fix metrics data * code review
Diffstat (limited to 'toolsrc/src')
-rw-r--r--toolsrc/src/vcpkg-test/json.cpp64
-rw-r--r--toolsrc/src/vcpkg.cpp17
-rw-r--r--toolsrc/src/vcpkg/base/json.cpp443
-rw-r--r--toolsrc/src/vcpkg/base/system.process.cpp3
-rw-r--r--toolsrc/src/vcpkg/commands.version.cpp2
-rw-r--r--toolsrc/src/vcpkg/metrics.cpp311
-rw-r--r--toolsrc/src/vcpkg/vcpkgcmdarguments.cpp10
7 files changed, 540 insertions, 310 deletions
diff --git a/toolsrc/src/vcpkg-test/json.cpp b/toolsrc/src/vcpkg-test/json.cpp
index 09f5d98fc..a957618cb 100644
--- a/toolsrc/src/vcpkg-test/json.cpp
+++ b/toolsrc/src/vcpkg-test/json.cpp
@@ -4,6 +4,8 @@
#include <vcpkg/base/json.h>
#include <vcpkg/base/unicode.h>
+#include "math.h"
+
// TODO: remove this once we switch to C++20 completely
// This is the worst, but we also can't really deal with it any other way.
#if __cpp_char8_t
@@ -57,9 +59,7 @@ TEST_CASE ("JSON parse strings", "[json]")
REQUIRE(res.get()->first.is_string());
REQUIRE(res.get()->first.string() == "\xED\xA0\x80");
- const auto make_json_string = [] (vcpkg::StringView sv) {
- return '"' + sv.to_string() + '"';
- };
+ const auto make_json_string = [](vcpkg::StringView sv) { return '"' + sv.to_string() + '"'; };
const vcpkg::StringView radical = U8_STR("⎷");
const vcpkg::StringView grin = U8_STR("😁");
@@ -79,28 +79,51 @@ TEST_CASE ("JSON parse strings", "[json]")
REQUIRE(res.get()->first.string() == grin);
}
-TEST_CASE ("JSON parse numbers", "[json]")
+TEST_CASE ("JSON parse integers", "[json]")
{
auto res = Json::parse("0");
REQUIRE(res);
- REQUIRE(res.get()->first.is_number());
- REQUIRE(res.get()->first.number() == 0);
+ REQUIRE(res.get()->first.is_integer());
+ REQUIRE(res.get()->first.integer() == 0);
res = Json::parse("12345");
REQUIRE(res);
- REQUIRE(res.get()->first.is_number());
- REQUIRE(res.get()->first.number() == 12345);
+ REQUIRE(res.get()->first.is_integer());
+ REQUIRE(res.get()->first.integer() == 12345);
res = Json::parse("-12345");
REQUIRE(res);
- REQUIRE(res.get()->first.is_number());
- REQUIRE(res.get()->first.number() == -12345);
+ REQUIRE(res.get()->first.is_integer());
+ REQUIRE(res.get()->first.integer() == -12345);
res = Json::parse("9223372036854775807"); // INT64_MAX
REQUIRE(res);
- REQUIRE(res.get()->first.is_number());
- REQUIRE(res.get()->first.number() == 9223372036854775807);
+ REQUIRE(res.get()->first.is_integer());
+ REQUIRE(res.get()->first.integer() == 9223372036854775807);
res = Json::parse("-9223372036854775808");
REQUIRE(res);
+ REQUIRE(res.get()->first.is_integer());
+ REQUIRE(res.get()->first.integer() == (-9223372036854775807 - 1)); // INT64_MIN (C++'s parser is fun)
+}
+
+TEST_CASE ("JSON parse floats", "[json]")
+{
+ auto res = Json::parse("0.0");
+ REQUIRE(res);
+ REQUIRE(res.get()->first.is_number());
+ REQUIRE(!res.get()->first.is_integer());
+ REQUIRE(res.get()->first.number() == 0.0);
+ REQUIRE(!signbit(res.get()->first.number()));
+ res = Json::parse("-0.0");
+ REQUIRE(res);
+ REQUIRE(res.get()->first.is_number());
+ REQUIRE(res.get()->first.number() == 0.0);
+ REQUIRE(signbit(res.get()->first.number()));
+ res = Json::parse("12345.6789");
+ REQUIRE(res);
+ REQUIRE(res.get()->first.is_number());
+ REQUIRE_THAT(res.get()->first.number(), Catch::WithinULP(12345.6789, 3));
+ res = Json::parse("-12345.6789");
+ REQUIRE(res);
REQUIRE(res.get()->first.is_number());
- REQUIRE(res.get()->first.number() == (-9223372036854775807 - 1)); // INT64_MIN (C++'s parser is fun)
+ REQUIRE_THAT(res.get()->first.number(), Catch::WithinULP(-12345.6789, 3));
}
TEST_CASE ("JSON parse arrays", "[json]")
@@ -116,18 +139,18 @@ TEST_CASE ("JSON parse arrays", "[json]")
val = std::move(res.get()->first);
REQUIRE(val.is_array());
REQUIRE(val.array().size() == 1);
- REQUIRE(val.array()[0].is_number());
- REQUIRE(val.array()[0].number() == 123);
+ REQUIRE(val.array()[0].is_integer());
+ REQUIRE(val.array()[0].integer() == 123);
res = Json::parse("[123, 456]");
REQUIRE(res);
val = std::move(res.get()->first);
REQUIRE(val.is_array());
REQUIRE(val.array().size() == 2);
- REQUIRE(val.array()[0].is_number());
- REQUIRE(val.array()[0].number() == 123);
- REQUIRE(val.array()[1].is_number());
- REQUIRE(val.array()[1].number() == 456);
+ REQUIRE(val.array()[0].is_integer());
+ REQUIRE(val.array()[0].integer() == 123);
+ REQUIRE(val.array()[1].is_integer());
+ REQUIRE(val.array()[1].integer() == 456);
res = Json::parse("[123, 456, [null]]");
REQUIRE(res);
@@ -155,5 +178,8 @@ TEST_CASE ("JSON parse full file", "[json]")
;
auto res = Json::parse(json);
+ if (!res) {
+ std::cerr << res.error()->format() << '\n';
+ }
REQUIRE(res);
}
diff --git a/toolsrc/src/vcpkg.cpp b/toolsrc/src/vcpkg.cpp
index e1a0a2a0b..95cd39778 100644
--- a/toolsrc/src/vcpkg.cpp
+++ b/toolsrc/src/vcpkg.cpp
@@ -333,6 +333,11 @@ int main(const int argc, const char* const* const argv)
auto flags = Strings::split(*v, ',');
if (std::find(flags.begin(), flags.end(), "binarycaching") != flags.end()) GlobalState::g_binary_caching = true;
}
+ const auto vcpkg_disable_metrics_env = System::get_environment_variable("VCPKG_DISABLE_METRICS");
+ if (vcpkg_disable_metrics_env.has_value())
+ {
+ Metrics::g_metrics.lock()->set_disabled(true);
+ }
const VcpkgCmdArguments args = VcpkgCmdArguments::create_from_command_line(argc, argv);
@@ -340,8 +345,20 @@ int main(const int argc, const char* const* const argv)
if (const auto p = args.printmetrics.get()) Metrics::g_metrics.lock()->set_print_metrics(*p);
if (const auto p = args.sendmetrics.get()) Metrics::g_metrics.lock()->set_send_metrics(*p);
+ if (const auto p = args.disable_metrics.get()) Metrics::g_metrics.lock()->set_disabled(*p);
if (const auto p = args.debug.get()) Debug::g_debugging = *p;
+ if (args.sendmetrics.has_value() && !Metrics::g_metrics.lock()->metrics_enabled())
+ {
+ System::print2(System::Color::warning,
+ "Warning: passed either --sendmetrics or --no-sendmetrics, but metrics are disabled.\n");
+ }
+ if (args.printmetrics.has_value() && !Metrics::g_metrics.lock()->metrics_enabled())
+ {
+ System::print2(System::Color::warning,
+ "Warning: passed either --printmetrics or --no-printmetrics, but metrics are disabled.\n");
+ }
+
if (Debug::g_debugging)
{
inner(args);
diff --git a/toolsrc/src/vcpkg/base/json.cpp b/toolsrc/src/vcpkg/base/json.cpp
index d8d0faab0..3d6bfc1d2 100644
--- a/toolsrc/src/vcpkg/base/json.cpp
+++ b/toolsrc/src/vcpkg/base/json.cpp
@@ -5,6 +5,8 @@
#include <vcpkg/base/system.debug.h>
#include <vcpkg/base/unicode.h>
+#include <inttypes.h>
+
namespace vcpkg::Json
{
using VK = ValueKind;
@@ -23,7 +25,8 @@ namespace vcpkg::Json
{
std::nullptr_t null;
bool boolean;
- int64_t number;
+ int64_t integer;
+ double number;
std::string string;
Array array;
Object object;
@@ -31,7 +34,8 @@ namespace vcpkg::Json
ValueImpl(ValueKindConstant<VK::Null> vk, std::nullptr_t) : tag(vk), null() { }
ValueImpl(ValueKindConstant<VK::Boolean> vk, bool b) : tag(vk), boolean(b) { }
- ValueImpl(ValueKindConstant<VK::Number> vk, int64_t i) : tag(vk), number(i) { }
+ ValueImpl(ValueKindConstant<VK::Integer> vk, int64_t i) : tag(vk), integer(i) { }
+ ValueImpl(ValueKindConstant<VK::Number> vk, double d) : tag(vk), number(d) { }
ValueImpl(ValueKindConstant<VK::String> vk, std::string&& s) : tag(vk), string(std::move(s)) { }
ValueImpl(ValueKindConstant<VK::Array> vk, Array&& arr) : tag(vk), array(std::move(arr)) { }
ValueImpl(ValueKindConstant<VK::Object> vk, Object&& obj) : tag(vk), object(std::move(obj)) { }
@@ -42,6 +46,7 @@ namespace vcpkg::Json
{
case VK::Null: return internal_assign(VK::Null, &ValueImpl::null, other);
case VK::Boolean: return internal_assign(VK::Boolean, &ValueImpl::boolean, other);
+ case VK::Integer: return internal_assign(VK::Integer, &ValueImpl::integer, other);
case VK::Number: return internal_assign(VK::Number, &ValueImpl::number, other);
case VK::String: return internal_assign(VK::String, &ValueImpl::string, other);
case VK::Array: return internal_assign(VK::Array, &ValueImpl::array, other);
@@ -101,7 +106,12 @@ namespace vcpkg::Json
bool Value::is_null() const noexcept { return kind() == VK::Null; }
bool Value::is_boolean() const noexcept { return kind() == VK::Boolean; }
- bool Value::is_number() const noexcept { return kind() == VK::Number; }
+ bool Value::is_integer() const noexcept { return kind() == VK::Integer; }
+ bool Value::is_number() const noexcept
+ {
+ auto k = kind();
+ return k == VK::Integer || k == VK::Number;
+ }
bool Value::is_string() const noexcept { return kind() == VK::String; }
bool Value::is_array() const noexcept { return kind() == VK::Array; }
bool Value::is_object() const noexcept { return kind() == VK::Object; }
@@ -111,10 +121,22 @@ namespace vcpkg::Json
vcpkg::Checks::check_exit(VCPKG_LINE_INFO, is_boolean());
return underlying_->boolean;
}
- int64_t Value::number() const noexcept
+ int64_t Value::integer() const noexcept
+ {
+ vcpkg::Checks::check_exit(VCPKG_LINE_INFO, is_integer());
+ return underlying_->integer;
+ }
+ double Value::number() const noexcept
{
- vcpkg::Checks::check_exit(VCPKG_LINE_INFO, is_number());
- return underlying_->number;
+ auto k = kind();
+ if (k == VK::Number)
+ {
+ return underlying_->number;
+ }
+ else
+ {
+ return static_cast<double>(integer());
+ }
}
StringView Value::string() const noexcept
{
@@ -155,6 +177,7 @@ namespace vcpkg::Json
{
case ValueKind::Null: return Value::null(nullptr);
case ValueKind::Boolean: return Value::boolean(boolean());
+ case ValueKind::Integer: return Value::integer(integer());
case ValueKind::Number: return Value::number(number());
case ValueKind::String: return Value::string(string());
case ValueKind::Array: return Value::array(array().clone());
@@ -170,10 +193,17 @@ namespace vcpkg::Json
val.underlying_ = std::make_unique<ValueImpl>(ValueKindConstant<VK::Boolean>(), b);
return val;
}
- Value Value::number(int64_t i) noexcept
+ Value Value::integer(int64_t i) noexcept
{
Value val;
- val.underlying_ = std::make_unique<ValueImpl>(ValueKindConstant<VK::Number>(), i);
+ val.underlying_ = std::make_unique<ValueImpl>(ValueKindConstant<VK::Integer>(), i);
+ return val;
+ }
+ Value Value::number(double d) noexcept
+ {
+ vcpkg::Checks::check_exit(VCPKG_LINE_INFO, isfinite(d));
+ Value val;
+ val.underlying_ = std::make_unique<ValueImpl>(ValueKindConstant<VK::Number>(), d);
return val;
}
Value Value::string(StringView sv) noexcept
@@ -211,25 +241,66 @@ namespace vcpkg::Json
}
return arr;
}
+
+ Value& Array::push_back(Value&& value)
+ {
+ underlying_.push_back(std::move(value));
+ return underlying_.back();
+ }
+ Object& Array::push_back(Object&& obj) { return push_back(Value::object(std::move(obj))).object(); }
+ Array& Array::push_back(Array&& arr) { return push_back(Value::array(std::move(arr))).array(); }
+ Value& Array::insert_before(iterator it, Value&& value)
+ {
+ size_t index = it - underlying_.begin();
+ underlying_.insert(it, std::move(value));
+ return underlying_[index];
+ }
+ Object& Array::insert_before(iterator it, Object&& obj)
+ {
+ return insert_before(it, Value::object(std::move(obj))).object();
+ }
+ Array& Array::insert_before(iterator it, Array&& arr)
+ {
+ return insert_before(it, Value::array(std::move(arr))).array();
+ }
// } struct Array
// struct Object {
- void Object::insert(std::string key, Value value) noexcept
+ Value& Object::insert(std::string key, Value&& value)
{
vcpkg::Checks::check_exit(VCPKG_LINE_INFO, !contains(key));
- underlying_.push_back(std::make_pair(std::move(key), std::move(value)));
+ underlying_.push_back({std::move(key), std::move(value)});
+ return underlying_.back().second;
}
- void Object::insert_or_replace(std::string key, Value value) noexcept
+ Array& Object::insert(std::string key, Array&& value)
+ {
+ return insert(std::move(key), Value::array(std::move(value))).array();
+ }
+ Object& Object::insert(std::string key, Object&& value)
+ {
+ return insert(std::move(key), Value::object(std::move(value))).object();
+ }
+ Value& Object::insert_or_replace(std::string key, Value&& value)
{
auto v = get(key);
if (v)
{
*v = std::move(value);
+ return *v;
}
else
{
- underlying_.push_back(std::make_pair(std::move(key), std::move(value)));
+ underlying_.push_back({std::move(key), std::move(value)});
+ return underlying_.back().second;
}
}
+ Array& Object::insert_or_replace(std::string key, Array&& value)
+ {
+ return insert_or_replace(std::move(key), Value::array(std::move(value))).array();
+ }
+ Object& Object::insert_or_replace(std::string key, Object&& value)
+ {
+ return insert_or_replace(std::move(key), Value::object(std::move(value))).object();
+ }
auto Object::internal_find_key(StringView key) const noexcept -> underlying_t::const_iterator
{
@@ -463,11 +534,15 @@ namespace vcpkg::Json
Value parse_number() noexcept
{
Checks::check_exit(VCPKG_LINE_INFO, is_number_start(cur()));
- bool negative = false;
+
+ bool floating = false;
+ bool negative = false; // negative & 0 -> floating, so keep track of it
+ std::string number_to_parse;
char32_t current = cur();
if (cur() == '-')
{
+ number_to_parse.push_back('-');
negative = true;
current = next();
if (current == Unicode::end_of_file)
@@ -480,54 +555,81 @@ namespace vcpkg::Json
if (current == '0')
{
current = next();
- if (current != Unicode::end_of_file)
+ if (current == '.')
+ {
+ number_to_parse.append("0.");
+ floating = true;
+ current = next();
+ }
+ else if (is_digit(current))
+ {
+ add_error("Unexpected digits after a leading zero");
+ return Value();
+ }
+ else
{
- if (is_digit(current))
+ if (negative)
{
- add_error("Unexpected digits after a leading zero");
+ return Value::number(-0.0);
}
- if (current == '.')
+ else
{
- add_error("Found a `.` -- this JSON implementation does not support floating point");
+ return Value::integer(0);
}
}
- return Value::number(0);
}
- // parse as negative so that someone can write INT64_MIN; otherwise, they'd only be able to get
- // -INT64_MAX = INT64_MIN + 1
- constexpr auto min_value = std::numeric_limits<int64_t>::min();
- int64_t result = 0;
- while (current != Unicode::end_of_file && is_digit(current))
+ while (is_digit(current))
+ {
+ number_to_parse.push_back(static_cast<char>(current));
+ current = next();
+ }
+ if (!floating && current == '.')
{
- const int digit = current - '0';
- // result * 10 - digit < min_value : remember that result < 0
- if (result < (min_value + digit) / 10)
+ floating = true;
+ number_to_parse.push_back('.');
+ current = next();
+ if (!is_digit(current))
{
- add_error("Number is too big for an int64_t");
+ add_error("Expected digits after the decimal point");
return Value();
}
- result *= 10;
- result -= digit;
- current = next();
+ while (is_digit(current))
+ {
+ number_to_parse.push_back(static_cast<char>(current));
+ current = next();
+ }
}
- if (current == '.')
+
+#ifdef _MSC_VER
+#define SCANF sscanf_s
+#else
+#define SCANF sscanf
+#endif
+
+ // TODO: switch to `from_chars` once we are able to remove support for old compilers
+ if (floating)
{
- add_error("Found a `.` -- this JSON implementation doesn't support floating point");
- return Value();
+ double res;
+ if (SCANF(number_to_parse.c_str(), "%lf", &res) != 1)
+ {
+ add_error(Strings::format("Invalid floating point constant: %s", number_to_parse));
+ return Value();
+ }
+ return Value::number(res);
}
-
- if (!negative)
+ else
{
- if (result == min_value)
+ int64_t res;
+ if (SCANF(number_to_parse.c_str(), "%" SCNd64, &res) != 1)
{
- add_error("Number is too big for a uint64_t");
+ add_error(Strings::format("Invalid integer constant: %s", number_to_parse));
return Value();
}
- result = -result;
+ return Value::integer(res);
}
- return Value::number(result);
+#undef SCANF
}
Value parse_keyword() noexcept
@@ -630,7 +732,7 @@ namespace vcpkg::Json
auto current = cur();
- auto res = std::make_pair(std::string(""), Value());
+ std::pair<std::string, Value> res = {std::string(""), Value()};
if (current == Unicode::end_of_file)
{
@@ -804,128 +906,98 @@ namespace vcpkg::Json
}
// } auto parse()
- // auto stringify() {
- static std::string& append_unicode_escape(std::string& s, char16_t code_unit)
- {
- s.append("\\u");
-
- // AFAIK, there's no standard way of doing this?
- constexpr const char hex_digit[16] = {
- '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
-
- s.push_back(hex_digit[(code_unit >> 12) & 0x0F]);
- s.push_back(hex_digit[(code_unit >> 8) & 0x0F]);
- s.push_back(hex_digit[(code_unit >> 4) & 0x0F]);
- s.push_back(hex_digit[(code_unit >> 0) & 0x0F]);
-
- return s;
- }
-
- // taken from the ECMAScript 2020 standard, 24.5.2.2: Runtime Semantics: QuoteJSONString
- static std::string& append_quoted_json_string(std::string& product, StringView sv)
+ namespace
{
- // Table 66: JSON Single Character Escape Sequences
- constexpr static std::array<std::pair<char32_t, const char*>, 7> escape_sequences = {
- std::make_pair(0x0008, R"(\b)"), // BACKSPACE
- std::make_pair(0x0009, R"(\t)"), // CHARACTER TABULATION
- std::make_pair(0x000A, R"(\n)"), // LINE FEED (LF)
- std::make_pair(0x000C, R"(\f)"), // FORM FEED (FF)
- std::make_pair(0x000D, R"(\r)"), // CARRIAGE RETURN (CR)
- std::make_pair(0x0022, R"(\")"), // QUOTATION MARK
- std::make_pair(0x005C, R"(\\)") // REVERSE SOLIDUS
- };
- // 1. Let product be the String value consisting solely of the code unit 0x0022 (QUOTATION MARK).
- product.push_back('"');
-
- // 2. For each code point C in ! UTF16DecodeString(value), do
- // (note that we use utf8 instead of utf16)
- for (auto code_point : Unicode::Utf8Decoder(sv.begin(), sv.end()))
+ struct Stringifier
{
- bool matched = false; // early exit boolean
- // a. If C is listed in the "Code Point" column of Table 66, then
- for (auto pr : escape_sequences)
+ JsonStyle style;
+ std::string& buffer;
+
+ void append_indent(int indent)
{
- // i. Set product to the string-concatenation of product and the escape sequence for C as specified in
- // the "Escape Sequence" column of the corresponding row.
- if (code_point == pr.first)
+ if (style.use_tabs())
{
- product.append(pr.second);
- matched = true;
- break;
+ buffer.append(indent, '\t');
}
- }
- if (matched) break;
+ else
+ {
+ buffer.append(indent * style.spaces(), ' ');
+ }
+ };
- // b. Else if C has a numeric value less than 0x0020 (SPACE), or if C has the same numeric value as a
- // leading surrogate or trailing surrogate, then
- if (code_point < 0x0020 || Unicode::utf16_is_surrogate_code_point(code_point))
+ void append_unicode_escape(char16_t code_unit)
{
- // i. Let unit be the code unit whose numeric value is that of C.
- // ii. Set product to the string-concatenation of product and UnicodeEscape(unit).
- append_unicode_escape(product, static_cast<char16_t>(code_point));
- break;
- }
+ buffer.append("\\u");
- // c. Else,
- // i. Set product to the string-concatenation of product and the UTF16Encoding of C.
- // (again, we use utf-8 here instead)
- Unicode::utf8_append_code_point(product, code_point);
- }
+ // AFAIK, there's no standard way of doing this?
+ constexpr const char hex_digit[16] = {
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
- // 3. Set product to the string-concatenation of product and the code unit 0x0022 (QUOTATION MARK).
- product.push_back('"');
+ buffer.push_back(hex_digit[(code_unit >> 12) & 0x0F]);
+ buffer.push_back(hex_digit[(code_unit >> 8) & 0x0F]);
+ buffer.push_back(hex_digit[(code_unit >> 4) & 0x0F]);
+ buffer.push_back(hex_digit[(code_unit >> 0) & 0x0F]);
+ }
- // 4. Return product.
- return product;
- }
+ // taken from the ECMAScript 2020 standard, 24.5.2.2: Runtime Semantics: QuoteJSONString
+ void append_quoted_json_string(StringView sv)
+ {
+ // Table 66: JSON Single Character Escape Sequences
+ constexpr static std::array<std::pair<char32_t, const char*>, 7> escape_sequences = {{
+ {0x0008, R"(\b)"}, // BACKSPACE
+ {0x0009, R"(\t)"}, // CHARACTER TABULATION
+ {0x000A, R"(\n)"}, // LINE FEED (LF)
+ {0x000C, R"(\f)"}, // FORM FEED (FF)
+ {0x000D, R"(\r)"}, // CARRIAGE RETURN (CR)
+ {0x0022, R"(\")"}, // QUOTATION MARK
+ {0x005C, R"(\\)"} // REVERSE SOLIDUS
+ }};
+ // 1. Let product be the String value consisting solely of the code unit 0x0022 (QUOTATION MARK).
+ buffer.push_back('"');
+
+ // 2. For each code point C in ! UTF16DecodeString(value), do
+ // (note that we use utf8 instead of utf16)
+ for (auto code_point : Unicode::Utf8Decoder(sv.begin(), sv.end()))
+ {
+ // a. If C is listed in the "Code Point" column of Table 66, then
+ const auto match = std::find_if(begin(escape_sequences), end(escape_sequences), [code_point](const std::pair<char32_t, const char*>& attempt) {
+ return attempt.first == code_point;
+ });
+ // i. Set product to the string-concatenation of product and the escape sequence for C as
+ // specified in the "Escape Sequence" column of the corresponding row.
+ if (match != end(escape_sequences)) {
+ buffer.append(match->second);
+ continue;
+ }
- static std::string quote_json_string(StringView sv)
- {
- std::string product;
- append_quoted_json_string(product, sv);
- return product;
- }
+ // b. Else if C has a numeric value less than 0x0020 (SPACE), or if C has the same numeric value as
+ // a leading surrogate or trailing surrogate, then
+ if (code_point < 0x0020 || Unicode::utf16_is_surrogate_code_point(code_point))
+ {
+ // i. Let unit be the code unit whose numeric value is that of C.
+ // ii. Set product to the string-concatenation of product and UnicodeEscape(unit).
+ append_unicode_escape(static_cast<char16_t>(code_point));
+ break;
+ }
- static void internal_stringify(const Value& value, JsonStyle style, std::string& buffer, int current_indent)
- {
- const auto append_indent = [&](int indent) {
- if (style.use_tabs())
- {
- buffer.append(indent, '\t');
- }
- else
- {
- buffer.append(indent * style.spaces(), ' ');
- }
- };
- switch (value.kind())
- {
- case VK::Null: buffer.append("null"); break;
- case VK::Boolean:
- {
- auto v = value.boolean();
- buffer.append(v ? "true" : "false");
- break;
- }
- case VK::Number: buffer.append(std::to_string(value.number())); break;
- case VK::String:
- {
- append_quoted_json_string(buffer, value.string());
- break;
+ // c. Else,
+ // i. Set product to the string-concatenation of product and the UTF16Encoding of C.
+ // (again, we use utf-8 here instead)
+ Unicode::utf8_append_code_point(buffer, code_point);
+ }
+
+ // 3. Set product to the string-concatenation of product and the code unit 0x0022 (QUOTATION MARK).
+ buffer.push_back('"');
}
- case VK::Array:
+
+ void stringify_object(const Object& obj, int current_indent)
{
- const auto& arr = value.array();
- buffer.push_back('[');
- if (arr.size() == 0)
- {
- buffer.push_back(']');
- }
- else
+ buffer.push_back('{');
+ if (obj.size() != 0)
{
bool first = true;
- for (const auto& el : arr)
+ for (const auto& el : obj)
{
if (!first)
{
@@ -936,23 +1008,28 @@ namespace vcpkg::Json
buffer.append(style.newline());
append_indent(current_indent + 1);
- internal_stringify(el, style, buffer, current_indent + 1);
+ append_quoted_json_string(el.first);
+ buffer.append(": ");
+ stringify(el.second, current_indent + 1);
}
buffer.append(style.newline());
append_indent(current_indent);
- buffer.push_back(']');
}
- break;
+ buffer.push_back('}');
}
- case VK::Object:
+
+ void stringify_array(const Array& arr, int current_indent)
{
- const auto& obj = value.object();
- buffer.push_back('{');
- if (obj.size() != 0)
+ buffer.push_back('[');
+ if (arr.size() == 0)
+ {
+ buffer.push_back(']');
+ }
+ else
{
bool first = true;
- for (const auto& el : obj)
+ for (const auto& el : arr)
{
if (!first)
{
@@ -963,24 +1040,64 @@ namespace vcpkg::Json
buffer.append(style.newline());
append_indent(current_indent + 1);
- auto key = quote_json_string(el.first);
- buffer.append(key.begin(), key.end());
- buffer.append(": ");
- internal_stringify(el.second, style, buffer, current_indent + 1);
+ stringify(el, current_indent + 1);
}
buffer.append(style.newline());
append_indent(current_indent);
+ buffer.push_back(']');
}
- buffer.push_back('}');
- break;
}
- }
+
+ void stringify(const Value& value, int current_indent)
+ {
+ switch (value.kind())
+ {
+ case VK::Null: buffer.append("null"); break;
+ case VK::Boolean:
+ {
+ auto v = value.boolean();
+ buffer.append(v ? "true" : "false");
+ break;
+ }
+ // TODO: switch to `to_chars` once we are able to remove support for old compilers
+ case VK::Integer: buffer.append(std::to_string(value.integer())); break;
+ case VK::Number: buffer.append(std::to_string(value.number())); break;
+ case VK::String:
+ {
+ append_quoted_json_string(value.string());
+ break;
+ }
+ case VK::Array:
+ {
+ stringify_array(value.array(), current_indent);
+ break;
+ }
+ case VK::Object:
+ {
+ stringify_object(value.object(), current_indent);
+ break;
+ }
+ }
+ }
+ };
}
- std::string stringify(const Value& value, JsonStyle style) noexcept
+ std::string stringify(const Value& value, JsonStyle style)
+ {
+ std::string res;
+ Stringifier{style, res}.stringify(value, 0);
+ return res;
+ }
+ std::string stringify(const Object& obj, JsonStyle style)
+ {
+ std::string res;
+ Stringifier{style, res}.stringify_object(obj, 0);
+ return res;
+ }
+ std::string stringify(const Array& arr, JsonStyle style)
{
std::string res;
- internal_stringify(value, style, res, 0);
+ Stringifier{style, res}.stringify_array(arr, 0);
return res;
}
// } auto stringify()
diff --git a/toolsrc/src/vcpkg/base/system.process.cpp b/toolsrc/src/vcpkg/base/system.process.cpp
index e6e195010..3f72396d9 100644
--- a/toolsrc/src/vcpkg/base/system.process.cpp
+++ b/toolsrc/src/vcpkg/base/system.process.cpp
@@ -237,6 +237,7 @@ namespace vcpkg
L"USERDOMAIN_ROAMINGPROFILE",
L"USERNAME",
L"USERPROFILE",
+ L"VCPKG_DISABLE_METRICS",
L"windir",
// Enables proxy information to be passed to Curl, the underlying download library in cmake.exe
L"http_proxy",
@@ -558,6 +559,7 @@ VCPKG_MSVC_WARNING(suppress : 6335) // Leaking process information handle 'proce
(void)env;
Debug::print("system(", cmd_line, ")\n");
fflush(nullptr);
+
int exit_code = system(cmd_line.c_str());
Debug::print(
"system() returned ", exit_code, " after ", static_cast<unsigned int>(timer.microseconds()), " us\n");
@@ -617,6 +619,7 @@ VCPKG_MSVC_WARNING(suppress : 6335) // Leaking process information handle 'proce
Debug::print("popen(", actual_cmd_line, ")\n");
// Flush stdout before launching external process
fflush(stdout);
+
const auto pipe = popen(actual_cmd_line.c_str(), "r");
if (pipe == nullptr)
{
diff --git a/toolsrc/src/vcpkg/commands.version.cpp b/toolsrc/src/vcpkg/commands.version.cpp
index 7029c8bfd..8ef29b116 100644
--- a/toolsrc/src/vcpkg/commands.version.cpp
+++ b/toolsrc/src/vcpkg/commands.version.cpp
@@ -32,7 +32,7 @@ namespace vcpkg::Commands::Version
#ifndef NDEBUG
+ std::string("-debug")
#endif
- + std::string(Metrics::get_compiled_metrics_enabled() ? "" : "-external");
+ ;
return S_VERSION;
}
diff --git a/toolsrc/src/vcpkg/metrics.cpp b/toolsrc/src/vcpkg/metrics.cpp
index d85d58cd3..00c6d2f8f 100644
--- a/toolsrc/src/vcpkg/metrics.cpp
+++ b/toolsrc/src/vcpkg/metrics.cpp
@@ -6,6 +6,7 @@
#include <vcpkg/base/chrono.h>
#include <vcpkg/base/files.h>
#include <vcpkg/base/hash.h>
+#include <vcpkg/base/json.h>
#include <vcpkg/base/strings.h>
#include <vcpkg/base/system.process.h>
@@ -29,88 +30,79 @@ namespace vcpkg::Metrics
return "";
}
- static std::string generate_random_UUID()
+ // note: this ignores the bits of these numbers that would be where format and variant go
+ static std::string uuid_of_integers(uint64_t top, uint64_t bottom)
{
- int part_sizes[] = {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++)
+ // uuid_field_size in bytes, not hex characters
+ constexpr size_t uuid_top_field_size[] = {4, 2, 2};
+ constexpr size_t uuid_bottom_field_size[] = {2, 6};
+
+ // uuid_field_size in hex characters, not bytes
+ constexpr size_t uuid_size = 8 + 1 + 4 + 1 + 4 + 1 + 4 + 1 + 12;
+
+ constexpr static char hex[17] = "0123456789abcdef";
+ constexpr static auto write_byte = [](std::string& res, std::uint8_t bits) {
+ res.push_back(hex[(bits >> 4) & 0x0F]);
+ res.push_back(hex[(bits >> 0) & 0x0F]);
+ };
+
+ // set the version bits to 4
+ top &= 0xFFFF'FFFF'FFFF'0FFFULL;
+ top |= 0x0000'0000'0000'4000ULL;
+
+ // set the variant bits to 2 (variant one)
+ bottom &= 0x3FFF'FFFF'FFFF'FFFFULL;
+ bottom |= 0x8000'0000'0000'0000ULL;
+
+ std::string res;
+ res.reserve(uuid_size);
+
+ bool first = true;
+ size_t start_byte = 0;
+ for (auto field_size : uuid_top_field_size)
{
- if (part > 0)
+ if (!first)
{
- uuid[index] = '-';
- index++;
+ res.push_back('-');
}
-
- // Generating UUID format version 4
- // http://en.wikipedia.org/wiki/Universally_unique_identifier
- for (int i = 0; i < part_sizes[part]; i++, index++)
+ first = false;
+ for (size_t i = start_byte; i < start_byte + field_size; ++i)
{
- if (part == 2 && i == 0)
- {
- num = 4;
- }
- else if (part == 4 && i == 0)
- {
- num = (rand() % 4) + 8;
- }
- else
- {
- num = rand() % 16;
- }
+ auto shift = 64 - (i + 1) * 8;
+ write_byte(res, (top >> shift) & 0xFF);
+ }
+ start_byte += field_size;
+ }
- if (num < 10)
- {
- uuid[index] = static_cast<char>('0' + num);
- }
- else
- {
- uuid[index] = static_cast<char>('a' + (num - 10));
- }
+ start_byte = 0;
+ for (auto field_size : uuid_bottom_field_size)
+ {
+ res.push_back('-');
+ for (size_t i = start_byte; i < start_byte + field_size; ++i)
+ {
+ auto shift = 64 - (i + 1) * 8;
+ write_byte(res, (bottom >> shift) & 0xFF);
}
+ start_byte += field_size;
}
- return uuid;
+ return res;
}
- static const std::string& get_session_id()
+ // UUID format version 4, variant 1
+ // http://en.wikipedia.org/wiki/Universally_unique_identifier
+ // [0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}
+ static std::string generate_random_UUID()
{
- static const std::string ID = generate_random_UUID();
- return ID;
+ std::random_device rnd{};
+ std::uniform_int_distribution<std::uint64_t> uid{};
+ return uuid_of_integers(uid(rnd), uid(rnd));
}
- static std::string to_json_string(const std::string& str)
+ static const std::string& get_session_id()
{
- std::string encoded = "\"";
- for (auto&& ch : str)
- {
- if (ch == '\\')
- {
- encoded.append("\\\\");
- }
- else if (ch == '"')
- {
- encoded.append("\\\"");
- }
- else if (ch < 0x20 || static_cast<unsigned char>(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 const std::string ID = generate_random_UUID();
+ return ID;
}
static std::string get_os_version_string()
@@ -150,91 +142,89 @@ namespace vcpkg::Metrics
std::string user_id = generate_random_UUID();
std::string user_timestamp;
std::string timestamp = get_current_date_time();
- std::string properties;
- std::string measurements;
- std::vector<std::string> buildtime_names;
- std::vector<std::string> buildtime_times;
+ Json::Object properties;
+ Json::Object measurements;
+
+ Json::Array buildtime_names;
+ Json::Array buildtime_times;
void track_property(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));
+ properties.insert_or_replace(name, Json::Value::string(value));
}
void track_metric(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));
+ measurements.insert_or_replace(name, Json::Value::number(value));
}
void track_buildtime(const std::string& name, double value)
{
- buildtime_names.push_back(name);
- buildtime_times.push_back(std::to_string(value));
+ buildtime_names.push_back(Json::Value::string(name));
+ buildtime_times.push_back(Json::Value::number(value));
}
std::string format_event_data_template() const
{
- auto props_plus_buildtimes = properties;
+ auto props_plus_buildtimes = properties.clone();
if (buildtime_names.size() > 0)
{
- if (props_plus_buildtimes.size() > 0) props_plus_buildtimes.push_back(',');
- props_plus_buildtimes.append(Strings::format(R"("buildnames_1": [%s], "buildtimes": [%s])",
- Strings::join(",", buildtime_names, to_json_string),
- Strings::join(",", buildtime_times)));
+ props_plus_buildtimes.insert("buildnames_1", Json::Value::array(buildtime_names.clone()));
+ props_plus_buildtimes.insert("buildtimes", Json::Value::array(buildtime_times.clone()));
}
- 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": "Other",
- "ai.device.osVersion": "%s-%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,
+ Json::Array arr = Json::Array();
+ Json::Object& obj = arr.push_back(Json::Object());
+
+ obj.insert("ver", Json::Value::integer(1));
+ obj.insert("name", Json::Value::string("Microsoft.ApplicationInsights.Event"));
+ obj.insert("time", Json::Value::string(timestamp));
+ obj.insert("sampleRate", Json::Value::number(100.0));
+ obj.insert("seq", Json::Value::string("0:0"));
+ obj.insert("iKey", Json::Value::string("b4e88960-4393-4dd9-ab8e-97e8fe6d7603"));
+ obj.insert("flags", Json::Value::integer(0));
+
+ {
+ Json::Object& tags = obj.insert("tags", Json::Object());
+
+ tags.insert("ai.device.os", Json::Value::string("Other"));
+
+ const char* os_name =
#if defined(_WIN32)
- "Windows",
+ "Windows";
#elif defined(__APPLE__)
- "OSX",
+ "OSX";
#elif defined(__linux__)
- "Linux",
+ "Linux";
#elif defined(__FreeBSD__)
- "FreeBSD",
+ "FreeBSD";
#elif defined(__unix__)
- "Unix",
+ "Unix";
#else
- "Other",
+ "Other";
#endif
- get_os_version_string(),
- session_id,
- user_id,
- user_timestamp,
- props_plus_buildtimes,
- measurements);
+
+ tags.insert("ai.device.osVersion",
+ Json::Value::string(Strings::format("%s-%s", os_name, get_os_version_string())));
+ tags.insert("ai.session.id", Json::Value::string(get_session_id()));
+ tags.insert("ai.user.id", Json::Value::string(user_id));
+ tags.insert("ai.user.accountAcquisitionDate", Json::Value::string(user_timestamp));
+ }
+
+ {
+ Json::Object& data = obj.insert("data", Json::Object());
+
+ data.insert("baseType", Json::Value::string("EventData"));
+ Json::Object& base_data = data.insert("baseData", Json::Object());
+
+ base_data.insert("ver", Json::Value::integer(2));
+ base_data.insert("name", Json::Value::string("commandline_test7"));
+ base_data.insert("properties", Json::Value::object(std::move(props_plus_buildtimes)));
+ base_data.insert("measurements", Json::Value::object(measurements.clone()));
+ }
+
+ return Json::stringify(arr, vcpkg::Json::JsonStyle());
}
};
@@ -247,12 +237,39 @@ namespace vcpkg::Metrics
#endif
;
static bool g_should_print_metrics = false;
+ static bool g_metrics_disabled =
+#if VCPKG_DISABLE_METRICS
+ true
+#else
+ false
+#endif
+ ;
- bool get_compiled_metrics_enabled() { return !VCPKG_DISABLE_METRICS; }
+ // for child vcpkg processes, we also want to disable metrics
+ static void set_vcpkg_disable_metrics_environment_variable(bool disabled)
+ {
+#if defined(_WIN32)
+ SetEnvironmentVariableW(L"VCPKG_DISABLE_METRICS", disabled ? L"1" : nullptr);
+#else
+ if (disabled)
+ {
+ setenv("VCPKG_DISABLE_METRICS", "1", true);
+ }
+ else
+ {
+ unsetenv("VCPKG_DISABLE_METRICS");
+ }
+#endif
+ }
std::string get_MAC_user()
{
#if defined(_WIN32)
+ if (!g_metrics.lock()->metrics_enabled())
+ {
+ return "{}";
+ }
+
auto getmac = System::cmd_execute_and_capture_output("getmac");
if (getmac.exit_code != 0) return "0";
@@ -293,20 +310,55 @@ namespace vcpkg::Metrics
void Metrics::set_print_metrics(bool should_print_metrics) { g_should_print_metrics = should_print_metrics; }
- void Metrics::track_metric(const std::string& name, double value) { g_metricmessage.track_metric(name, value); }
+ void Metrics::set_disabled(bool disabled)
+ {
+ set_vcpkg_disable_metrics_environment_variable(disabled);
+ g_metrics_disabled = disabled;
+ }
+
+ bool Metrics::metrics_enabled()
+ {
+#if VCPKG_DISABLE_METRICS
+ return false;
+#else
+ return !g_metrics_disabled;
+#endif
+ }
+
+ void Metrics::track_metric(const std::string& name, double value)
+ {
+ if (!metrics_enabled())
+ {
+ return;
+ }
+ g_metricmessage.track_metric(name, value);
+ }
void Metrics::track_buildtime(const std::string& name, double value)
{
+ if (!metrics_enabled())
+ {
+ return;
+ }
g_metricmessage.track_buildtime(name, value);
}
void Metrics::track_property(const std::string& name, const std::string& value)
{
+ if (!metrics_enabled())
+ {
+ return;
+ }
g_metricmessage.track_property(name, value);
}
void Metrics::upload(const std::string& payload)
{
+ if (!metrics_enabled())
+ {
+ return;
+ }
+
#if !defined(_WIN32)
Util::unused(payload);
#else
@@ -402,6 +454,11 @@ namespace vcpkg::Metrics
void Metrics::flush()
{
+ if (!metrics_enabled())
+ {
+ return;
+ }
+
const std::string payload = g_metricmessage.format_event_data_template();
if (g_should_print_metrics) std::cerr << payload << "\n";
if (!g_should_send_metrics) return;
diff --git a/toolsrc/src/vcpkg/vcpkgcmdarguments.cpp b/toolsrc/src/vcpkg/vcpkgcmdarguments.cpp
index 4c49d1108..c2a9e7095 100644
--- a/toolsrc/src/vcpkg/vcpkgcmdarguments.cpp
+++ b/toolsrc/src/vcpkg/vcpkgcmdarguments.cpp
@@ -212,6 +212,11 @@ namespace vcpkg
parse_switch(true, "printmetrics", args.printmetrics);
continue;
}
+ if (arg == "--disable-metrics")
+ {
+ parse_switch(true, "printmetrics", args.disable_metrics);
+ continue;
+ }
if (arg == "--no-sendmetrics")
{
parse_switch(false, "sendmetrics", args.sendmetrics);
@@ -222,6 +227,11 @@ namespace vcpkg
parse_switch(false, "printmetrics", args.printmetrics);
continue;
}
+ if (arg == "--no-disable-metrics")
+ {
+ parse_switch(false, "printmetrics", args.disable_metrics);
+ continue;
+ }
if (arg == "--featurepackages")
{
parse_switch(true, "featurepackages", args.featurepackages);