mirror of
https://github.com/microsoft/vcpkg.git
synced 2025-06-07 21:42:48 +08:00
[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
This commit is contained in:
parent
a64dc07690
commit
09319cd79e
@ -80,6 +80,10 @@ Code licensed under the [MIT License](LICENSE.txt).
|
|||||||
|
|
||||||
## Telemetry
|
## Telemetry
|
||||||
|
|
||||||
vcpkg collects usage data in order to help us improve your experience. The data collected by Microsoft is anonymous. You can opt-out of telemetry by running `bootstrap-vcpkg.bat` or `bootstrap-vcpkg.sh` with `-disableMetrics`.
|
vcpkg collects usage data in order to help us improve your experience.
|
||||||
|
The data collected by Microsoft is anonymous.
|
||||||
|
You can opt-out of telemetry by re-running the bootstrap-vcpkg script with -disableMetrics,
|
||||||
|
passing --disable-metrics to vcpkg on the command line,
|
||||||
|
or by setting the VCPKG_DISABLE_METRICS environment variable.
|
||||||
|
|
||||||
Read more about vcpkg telemetry at docs/about/privacy.md
|
Read more about vcpkg telemetry at docs/about/privacy.md
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# Vcpkg telemetry and privacy
|
# Vcpkg telemetry and privacy
|
||||||
|
|
||||||
vcpkg collects telemetry data to understand usage issues, such as failing packages, and to guide tool improvements. The collected data is anonymous.
|
vcpkg collects telemetry data to understand usage issues, such as failing packages, and to guide tool improvements. The collected data is anonymous.
|
||||||
For more information about how Microsoft protects your privacy, see https://privacy.microsoft.com/en-US/privacystatement#mainenterprisedeveloperproductsmodule
|
For more information about how Microsoft protects your privacy, see https://privacy.microsoft.com/en-US/privacystatement#mainenterprisedeveloperproductsmodule
|
||||||
|
|
||||||
## Scope
|
## Scope
|
||||||
|
|
||||||
@ -22,7 +22,11 @@ vcpkg displays text similar to the following when you build vcpkg. This is how M
|
|||||||
```
|
```
|
||||||
Telemetry
|
Telemetry
|
||||||
---------
|
---------
|
||||||
vcpkg collects usage data in order to help us improve your experience. The data collected by Microsoft is anonymous. You can opt-out of telemetry by re-running the bootstrap-vcpkg script with -disableMetrics.
|
vcpkg collects usage data in order to help us improve your experience.
|
||||||
|
The data collected by Microsoft is anonymous.
|
||||||
|
You can opt-out of telemetry by re-running the bootstrap-vcpkg script with -disableMetrics,
|
||||||
|
passing --disable-metrics to vcpkg on the command line,
|
||||||
|
or by setting the VCPKG_DISABLE_METRICS environment variable.
|
||||||
|
|
||||||
Read more about vcpkg telemetry at docs/about/privacy.md
|
Read more about vcpkg telemetry at docs/about/privacy.md
|
||||||
```
|
```
|
||||||
@ -39,7 +43,7 @@ You can see the telemetry events any command by appending `--printmetrics` after
|
|||||||
|
|
||||||
In the source code (included in `toolsrc\`), you can search for calls to the functions `track_property()` and `track_metric()` to see every specific data point we collect.
|
In the source code (included in `toolsrc\`), you can search for calls to the functions `track_property()` and `track_metric()` to see every specific data point we collect.
|
||||||
|
|
||||||
## Avoid inadvertent disclosure information
|
## Avoid inadvertent disclosure information
|
||||||
|
|
||||||
vcpkg contributors and anyone else running a version of vcpkg that they built themselves should consider the path to their source code. If a crash occurs when using vcpkg, the file path from the build machine is collected as part of the stack trace and isn't hashed.
|
vcpkg contributors and anyone else running a version of vcpkg that they built themselves should consider the path to their source code. If a crash occurs when using vcpkg, the file path from the build machine is collected as part of the stack trace and isn't hashed.
|
||||||
Because of this, builds of vcpkg shouldn't be located in directories whose path names expose personal or sensitive information.
|
Because of this, builds of vcpkg shouldn't be located in directories whose path names expose personal or sensitive information.
|
||||||
|
@ -341,7 +341,7 @@ if ($disableMetrics)
|
|||||||
$platform = "x86"
|
$platform = "x86"
|
||||||
$vcpkgReleaseDir = "$vcpkgSourcesPath\msbuild.x86.release"
|
$vcpkgReleaseDir = "$vcpkgSourcesPath\msbuild.x86.release"
|
||||||
if($PSVersionTable.PSVersion.Major -le 2)
|
if($PSVersionTable.PSVersion.Major -le 2)
|
||||||
{
|
{
|
||||||
$architecture=(Get-WmiObject win32_operatingsystem | Select-Object osarchitecture).osarchitecture
|
$architecture=(Get-WmiObject win32_operatingsystem | Select-Object osarchitecture).osarchitecture
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -417,9 +417,13 @@ if (-not $disableMetrics)
|
|||||||
Write-Host @"
|
Write-Host @"
|
||||||
Telemetry
|
Telemetry
|
||||||
---------
|
---------
|
||||||
vcpkg collects usage data in order to help us improve your experience. The data collected by Microsoft is anonymous. You can opt-out of telemetry by re-running bootstrap-vcpkg.bat with -disableMetrics.
|
vcpkg collects usage data in order to help us improve your experience.
|
||||||
Read more about vcpkg telemetry at docs/about/privacy.md
|
The data collected by Microsoft is anonymous.
|
||||||
|
You can opt-out of telemetry by re-running the bootstrap-vcpkg script with -disableMetrics,
|
||||||
|
passing --disable-metrics to vcpkg on the command line,
|
||||||
|
or by setting the VCPKG_DISABLE_METRICS environment variable.
|
||||||
|
|
||||||
|
Read more about vcpkg telemetry at docs/about/privacy.md
|
||||||
"@
|
"@
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -256,9 +256,15 @@ rm -rf "$vcpkgRootDir/vcpkg"
|
|||||||
cp "$buildDir/vcpkg" "$vcpkgRootDir/"
|
cp "$buildDir/vcpkg" "$vcpkgRootDir/"
|
||||||
|
|
||||||
if ! [ "$vcpkgDisableMetrics" = "ON" ]; then
|
if ! [ "$vcpkgDisableMetrics" = "ON" ]; then
|
||||||
echo "Telemetry"
|
cat <<EOF
|
||||||
echo "---------"
|
Telemetry
|
||||||
echo "vcpkg collects usage data in order to help us improve your experience. The data collected by Microsoft is anonymous. You can opt-out of telemetry by re-running bootstrap-vcpkg.sh with -disableMetrics"
|
---------
|
||||||
echo "Read more about vcpkg telemetry at docs/about/privacy.md"
|
vcpkg collects usage data in order to help us improve your experience.
|
||||||
echo ""
|
The data collected by Microsoft is anonymous.
|
||||||
|
You can opt-out of telemetry by re-running the bootstrap-vcpkg script with -disableMetrics,
|
||||||
|
passing --disable-metrics to vcpkg on the command line,
|
||||||
|
or by setting the VCPKG_DISABLE_METRICS environment variable.
|
||||||
|
|
||||||
|
Read more about vcpkg telemetry at docs/about/privacy.md
|
||||||
|
EOF
|
||||||
fi
|
fi
|
||||||
|
@ -32,3 +32,6 @@ ForEachMacros: [TEST_CASE, SECTION]
|
|||||||
PenaltyReturnTypeOnItsOwnLine: 1000
|
PenaltyReturnTypeOnItsOwnLine: 1000
|
||||||
SpaceAfterTemplateKeyword: false
|
SpaceAfterTemplateKeyword: false
|
||||||
SpaceBeforeCpp11BracedList: false
|
SpaceBeforeCpp11BracedList: false
|
||||||
|
|
||||||
|
IncludeBlocks: Preserve
|
||||||
|
SortIncludes: false
|
||||||
|
@ -9,6 +9,12 @@
|
|||||||
#include <winhttp.h>
|
#include <winhttp.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
@ -16,10 +22,6 @@
|
|||||||
#include <cctype>
|
#include <cctype>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <codecvt>
|
#include <codecvt>
|
||||||
#include <cstdarg>
|
|
||||||
#include <cstddef>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <cstring>
|
|
||||||
|
|
||||||
#if VCPKG_USE_STD_FILESYSTEM
|
#if VCPKG_USE_STD_FILESYSTEM
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
@ -70,6 +70,7 @@ namespace vcpkg::Json
|
|||||||
{
|
{
|
||||||
Null,
|
Null,
|
||||||
Boolean,
|
Boolean,
|
||||||
|
Integer,
|
||||||
Number,
|
Number,
|
||||||
String,
|
String,
|
||||||
Array,
|
Array,
|
||||||
@ -84,10 +85,19 @@ namespace vcpkg::Json
|
|||||||
|
|
||||||
struct Value
|
struct Value
|
||||||
{
|
{
|
||||||
|
Value() noexcept; // equivalent to Value::null()
|
||||||
|
Value(Value&&) noexcept;
|
||||||
|
Value& operator=(Value&&) noexcept;
|
||||||
|
~Value();
|
||||||
|
|
||||||
|
Value clone() const noexcept;
|
||||||
|
|
||||||
ValueKind kind() const noexcept;
|
ValueKind kind() const noexcept;
|
||||||
|
|
||||||
bool is_null() const noexcept;
|
bool is_null() const noexcept;
|
||||||
bool is_boolean() const noexcept;
|
bool is_boolean() const noexcept;
|
||||||
|
bool is_integer() const noexcept;
|
||||||
|
// either integer _or_ number
|
||||||
bool is_number() const noexcept;
|
bool is_number() const noexcept;
|
||||||
bool is_string() const noexcept;
|
bool is_string() const noexcept;
|
||||||
bool is_array() const noexcept;
|
bool is_array() const noexcept;
|
||||||
@ -95,7 +105,8 @@ namespace vcpkg::Json
|
|||||||
|
|
||||||
// a.x() asserts when !a.is_x()
|
// a.x() asserts when !a.is_x()
|
||||||
bool boolean() const noexcept;
|
bool boolean() const noexcept;
|
||||||
int64_t number() const noexcept;
|
int64_t integer() const noexcept;
|
||||||
|
double number() const noexcept;
|
||||||
StringView string() const noexcept;
|
StringView string() const noexcept;
|
||||||
|
|
||||||
const Array& array() const noexcept;
|
const Array& array() const noexcept;
|
||||||
@ -104,18 +115,13 @@ namespace vcpkg::Json
|
|||||||
const Object& object() const noexcept;
|
const Object& object() const noexcept;
|
||||||
Object& object() noexcept;
|
Object& object() noexcept;
|
||||||
|
|
||||||
Value(Value&&) noexcept;
|
|
||||||
Value& operator=(Value&&) noexcept;
|
|
||||||
~Value();
|
|
||||||
|
|
||||||
Value() noexcept; // equivalent to Value::null()
|
|
||||||
static Value null(std::nullptr_t) noexcept;
|
static Value null(std::nullptr_t) noexcept;
|
||||||
static Value boolean(bool) noexcept;
|
static Value boolean(bool) noexcept;
|
||||||
static Value number(int64_t i) noexcept;
|
static Value integer(int64_t i) noexcept;
|
||||||
|
static Value number(double d) noexcept;
|
||||||
static Value string(StringView) noexcept;
|
static Value string(StringView) noexcept;
|
||||||
static Value array(Array&&) noexcept;
|
static Value array(Array&&) noexcept;
|
||||||
static Value object(Object&&) noexcept;
|
static Value object(Object&&) noexcept;
|
||||||
Value clone() const noexcept;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend struct impl::ValueImpl;
|
friend struct impl::ValueImpl;
|
||||||
@ -128,11 +134,24 @@ namespace vcpkg::Json
|
|||||||
using underlying_t = std::vector<Value>;
|
using underlying_t = std::vector<Value>;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
Array() = default;
|
||||||
|
Array(Array const&) = delete;
|
||||||
|
Array(Array&&) = default;
|
||||||
|
Array& operator=(Array const&) = delete;
|
||||||
|
Array& operator=(Array&&) = default;
|
||||||
|
~Array() = default;
|
||||||
|
|
||||||
|
Array clone() const noexcept;
|
||||||
|
|
||||||
using iterator = underlying_t::iterator;
|
using iterator = underlying_t::iterator;
|
||||||
using const_iterator = underlying_t::const_iterator;
|
using const_iterator = underlying_t::const_iterator;
|
||||||
|
|
||||||
void push_back(Value&& value) { this->underlying_.push_back(std::move(value)); }
|
Value& push_back(Value&& value);
|
||||||
void insert_before(iterator it, Value&& value) { this->underlying_.insert(it, std::move(value)); }
|
Object& push_back(Object&& value);
|
||||||
|
Array& push_back(Array&& value);
|
||||||
|
Value& insert_before(iterator it, Value&& value);
|
||||||
|
Object& insert_before(iterator it, Object&& value);
|
||||||
|
Array& insert_before(iterator it, Array&& value);
|
||||||
|
|
||||||
std::size_t size() const noexcept { return this->underlying_.size(); }
|
std::size_t size() const noexcept { return this->underlying_.size(); }
|
||||||
|
|
||||||
@ -148,8 +167,6 @@ namespace vcpkg::Json
|
|||||||
return this->underlying_[idx];
|
return this->underlying_[idx];
|
||||||
}
|
}
|
||||||
|
|
||||||
Array clone() const noexcept;
|
|
||||||
|
|
||||||
iterator begin() { return underlying_.begin(); }
|
iterator begin() { return underlying_.begin(); }
|
||||||
iterator end() { return underlying_.end(); }
|
iterator end() { return underlying_.end(); }
|
||||||
const_iterator begin() const { return cbegin(); }
|
const_iterator begin() const { return cbegin(); }
|
||||||
@ -160,7 +177,6 @@ namespace vcpkg::Json
|
|||||||
private:
|
private:
|
||||||
underlying_t underlying_;
|
underlying_t underlying_;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Object
|
struct Object
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
@ -169,12 +185,26 @@ namespace vcpkg::Json
|
|||||||
underlying_t::const_iterator internal_find_key(StringView key) const noexcept;
|
underlying_t::const_iterator internal_find_key(StringView key) const noexcept;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
// these are here for better diagnostics
|
||||||
|
Object() = default;
|
||||||
|
Object(Object const&) = delete;
|
||||||
|
Object(Object&&) = default;
|
||||||
|
Object& operator=(Object const&) = delete;
|
||||||
|
Object& operator=(Object&&) = default;
|
||||||
|
~Object() = default;
|
||||||
|
|
||||||
|
Object clone() const noexcept;
|
||||||
|
|
||||||
// asserts if the key is found
|
// asserts if the key is found
|
||||||
void insert(std::string key, Value value) noexcept;
|
Value& insert(std::string key, Value&& value);
|
||||||
|
Object& insert(std::string key, Object&& value);
|
||||||
|
Array& insert(std::string key, Array&& value);
|
||||||
|
|
||||||
// replaces the value if the key is found, otherwise inserts a new
|
// replaces the value if the key is found, otherwise inserts a new
|
||||||
// value.
|
// value.
|
||||||
void insert_or_replace(std::string key, Value value) noexcept;
|
Value& insert_or_replace(std::string key, Value&& value);
|
||||||
|
Object& insert_or_replace(std::string key, Object&& value);
|
||||||
|
Array& insert_or_replace(std::string key, Array&& value);
|
||||||
|
|
||||||
// returns whether the key existed
|
// returns whether the key existed
|
||||||
bool remove(StringView key) noexcept;
|
bool remove(StringView key) noexcept;
|
||||||
@ -200,8 +230,6 @@ namespace vcpkg::Json
|
|||||||
|
|
||||||
std::size_t size() const noexcept { return this->underlying_.size(); }
|
std::size_t size() const noexcept { return this->underlying_.size(); }
|
||||||
|
|
||||||
Object clone() const noexcept;
|
|
||||||
|
|
||||||
struct const_iterator
|
struct const_iterator
|
||||||
{
|
{
|
||||||
using value_type = std::pair<StringView, const Value&>;
|
using value_type = std::pair<StringView, const Value&>;
|
||||||
@ -245,6 +273,9 @@ namespace vcpkg::Json
|
|||||||
const Files::Filesystem&, const fs::path&, std::error_code& ec) noexcept;
|
const Files::Filesystem&, const fs::path&, std::error_code& ec) noexcept;
|
||||||
ExpectedT<std::pair<Value, JsonStyle>, std::unique_ptr<Parse::IParseError>> parse(
|
ExpectedT<std::pair<Value, JsonStyle>, std::unique_ptr<Parse::IParseError>> parse(
|
||||||
StringView text, const fs::path& filepath = "") noexcept;
|
StringView text, const fs::path& filepath = "") noexcept;
|
||||||
std::string stringify(const Value&, JsonStyle style) noexcept;
|
|
||||||
|
std::string stringify(const Value&, JsonStyle style);
|
||||||
|
std::string stringify(const Object&, JsonStyle style);
|
||||||
|
std::string stringify(const Array&, JsonStyle style);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ namespace vcpkg::Metrics
|
|||||||
{
|
{
|
||||||
void set_send_metrics(bool should_send_metrics);
|
void set_send_metrics(bool should_send_metrics);
|
||||||
void set_print_metrics(bool should_print_metrics);
|
void set_print_metrics(bool should_print_metrics);
|
||||||
|
void set_disabled(bool disabled);
|
||||||
void set_user_information(const std::string& user_id, const std::string& first_use_time);
|
void set_user_information(const std::string& user_id, const std::string& first_use_time);
|
||||||
static void init_user_information(std::string& user_id, std::string& first_use_time);
|
static void init_user_information(std::string& user_id, std::string& first_use_time);
|
||||||
|
|
||||||
@ -17,6 +18,8 @@ namespace vcpkg::Metrics
|
|||||||
void track_buildtime(const std::string& name, double value);
|
void track_buildtime(const std::string& name, double value);
|
||||||
void track_property(const std::string& name, const std::string& value);
|
void track_property(const std::string& name, const std::string& value);
|
||||||
|
|
||||||
|
bool metrics_enabled();
|
||||||
|
|
||||||
void upload(const std::string& payload);
|
void upload(const std::string& payload);
|
||||||
void flush();
|
void flush();
|
||||||
};
|
};
|
||||||
@ -24,5 +27,4 @@ namespace vcpkg::Metrics
|
|||||||
extern Util::LockGuarded<Metrics> g_metrics;
|
extern Util::LockGuarded<Metrics> g_metrics;
|
||||||
|
|
||||||
std::string get_MAC_user();
|
std::string get_MAC_user();
|
||||||
bool get_compiled_metrics_enabled();
|
|
||||||
}
|
}
|
||||||
|
@ -94,6 +94,8 @@ namespace vcpkg
|
|||||||
std::vector<std::string> binarysources;
|
std::vector<std::string> binarysources;
|
||||||
Optional<bool> debug = nullopt;
|
Optional<bool> debug = nullopt;
|
||||||
Optional<bool> sendmetrics = nullopt;
|
Optional<bool> sendmetrics = nullopt;
|
||||||
|
// fully disable metrics -- both printing and sending
|
||||||
|
Optional<bool> disable_metrics = nullopt;
|
||||||
Optional<bool> printmetrics = nullopt;
|
Optional<bool> printmetrics = nullopt;
|
||||||
|
|
||||||
// feature flags
|
// feature flags
|
||||||
|
@ -4,6 +4,8 @@
|
|||||||
#include <vcpkg/base/json.h>
|
#include <vcpkg/base/json.h>
|
||||||
#include <vcpkg/base/unicode.h>
|
#include <vcpkg/base/unicode.h>
|
||||||
|
|
||||||
|
#include "math.h"
|
||||||
|
|
||||||
// TODO: remove this once we switch to C++20 completely
|
// 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.
|
// This is the worst, but we also can't really deal with it any other way.
|
||||||
#if __cpp_char8_t
|
#if __cpp_char8_t
|
||||||
@ -57,9 +59,7 @@ TEST_CASE ("JSON parse strings", "[json]")
|
|||||||
REQUIRE(res.get()->first.is_string());
|
REQUIRE(res.get()->first.is_string());
|
||||||
REQUIRE(res.get()->first.string() == "\xED\xA0\x80");
|
REQUIRE(res.get()->first.string() == "\xED\xA0\x80");
|
||||||
|
|
||||||
const auto make_json_string = [] (vcpkg::StringView sv) {
|
const auto make_json_string = [](vcpkg::StringView sv) { return '"' + sv.to_string() + '"'; };
|
||||||
return '"' + sv.to_string() + '"';
|
|
||||||
};
|
|
||||||
const vcpkg::StringView radical = U8_STR("⎷");
|
const vcpkg::StringView radical = U8_STR("⎷");
|
||||||
const vcpkg::StringView grin = U8_STR("😁");
|
const vcpkg::StringView grin = U8_STR("😁");
|
||||||
|
|
||||||
@ -79,28 +79,51 @@ TEST_CASE ("JSON parse strings", "[json]")
|
|||||||
REQUIRE(res.get()->first.string() == grin);
|
REQUIRE(res.get()->first.string() == grin);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE ("JSON parse numbers", "[json]")
|
TEST_CASE ("JSON parse integers", "[json]")
|
||||||
{
|
{
|
||||||
auto res = Json::parse("0");
|
auto res = Json::parse("0");
|
||||||
REQUIRE(res);
|
REQUIRE(res);
|
||||||
REQUIRE(res.get()->first.is_number());
|
REQUIRE(res.get()->first.is_integer());
|
||||||
REQUIRE(res.get()->first.number() == 0);
|
REQUIRE(res.get()->first.integer() == 0);
|
||||||
res = Json::parse("12345");
|
res = Json::parse("12345");
|
||||||
REQUIRE(res);
|
REQUIRE(res);
|
||||||
REQUIRE(res.get()->first.is_number());
|
REQUIRE(res.get()->first.is_integer());
|
||||||
REQUIRE(res.get()->first.number() == 12345);
|
REQUIRE(res.get()->first.integer() == 12345);
|
||||||
res = Json::parse("-12345");
|
res = Json::parse("-12345");
|
||||||
REQUIRE(res);
|
REQUIRE(res);
|
||||||
REQUIRE(res.get()->first.is_number());
|
REQUIRE(res.get()->first.is_integer());
|
||||||
REQUIRE(res.get()->first.number() == -12345);
|
REQUIRE(res.get()->first.integer() == -12345);
|
||||||
res = Json::parse("9223372036854775807"); // INT64_MAX
|
res = Json::parse("9223372036854775807"); // INT64_MAX
|
||||||
REQUIRE(res);
|
REQUIRE(res);
|
||||||
REQUIRE(res.get()->first.is_number());
|
REQUIRE(res.get()->first.is_integer());
|
||||||
REQUIRE(res.get()->first.number() == 9223372036854775807);
|
REQUIRE(res.get()->first.integer() == 9223372036854775807);
|
||||||
res = Json::parse("-9223372036854775808");
|
res = Json::parse("-9223372036854775808");
|
||||||
REQUIRE(res);
|
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_number());
|
||||||
REQUIRE(res.get()->first.number() == (-9223372036854775807 - 1)); // INT64_MIN (C++'s parser is fun)
|
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_THAT(res.get()->first.number(), Catch::WithinULP(-12345.6789, 3));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE ("JSON parse arrays", "[json]")
|
TEST_CASE ("JSON parse arrays", "[json]")
|
||||||
@ -116,18 +139,18 @@ TEST_CASE ("JSON parse arrays", "[json]")
|
|||||||
val = std::move(res.get()->first);
|
val = std::move(res.get()->first);
|
||||||
REQUIRE(val.is_array());
|
REQUIRE(val.is_array());
|
||||||
REQUIRE(val.array().size() == 1);
|
REQUIRE(val.array().size() == 1);
|
||||||
REQUIRE(val.array()[0].is_number());
|
REQUIRE(val.array()[0].is_integer());
|
||||||
REQUIRE(val.array()[0].number() == 123);
|
REQUIRE(val.array()[0].integer() == 123);
|
||||||
|
|
||||||
res = Json::parse("[123, 456]");
|
res = Json::parse("[123, 456]");
|
||||||
REQUIRE(res);
|
REQUIRE(res);
|
||||||
val = std::move(res.get()->first);
|
val = std::move(res.get()->first);
|
||||||
REQUIRE(val.is_array());
|
REQUIRE(val.is_array());
|
||||||
REQUIRE(val.array().size() == 2);
|
REQUIRE(val.array().size() == 2);
|
||||||
REQUIRE(val.array()[0].is_number());
|
REQUIRE(val.array()[0].is_integer());
|
||||||
REQUIRE(val.array()[0].number() == 123);
|
REQUIRE(val.array()[0].integer() == 123);
|
||||||
REQUIRE(val.array()[1].is_number());
|
REQUIRE(val.array()[1].is_integer());
|
||||||
REQUIRE(val.array()[1].number() == 456);
|
REQUIRE(val.array()[1].integer() == 456);
|
||||||
|
|
||||||
res = Json::parse("[123, 456, [null]]");
|
res = Json::parse("[123, 456, [null]]");
|
||||||
REQUIRE(res);
|
REQUIRE(res);
|
||||||
@ -155,5 +178,8 @@ TEST_CASE ("JSON parse full file", "[json]")
|
|||||||
;
|
;
|
||||||
|
|
||||||
auto res = Json::parse(json);
|
auto res = Json::parse(json);
|
||||||
|
if (!res) {
|
||||||
|
std::cerr << res.error()->format() << '\n';
|
||||||
|
}
|
||||||
REQUIRE(res);
|
REQUIRE(res);
|
||||||
}
|
}
|
||||||
|
@ -333,6 +333,11 @@ int main(const int argc, const char* const* const argv)
|
|||||||
auto flags = Strings::split(*v, ',');
|
auto flags = Strings::split(*v, ',');
|
||||||
if (std::find(flags.begin(), flags.end(), "binarycaching") != flags.end()) GlobalState::g_binary_caching = true;
|
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);
|
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.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.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 (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)
|
if (Debug::g_debugging)
|
||||||
{
|
{
|
||||||
inner(args);
|
inner(args);
|
||||||
|
@ -5,6 +5,8 @@
|
|||||||
#include <vcpkg/base/system.debug.h>
|
#include <vcpkg/base/system.debug.h>
|
||||||
#include <vcpkg/base/unicode.h>
|
#include <vcpkg/base/unicode.h>
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
namespace vcpkg::Json
|
namespace vcpkg::Json
|
||||||
{
|
{
|
||||||
using VK = ValueKind;
|
using VK = ValueKind;
|
||||||
@ -23,7 +25,8 @@ namespace vcpkg::Json
|
|||||||
{
|
{
|
||||||
std::nullptr_t null;
|
std::nullptr_t null;
|
||||||
bool boolean;
|
bool boolean;
|
||||||
int64_t number;
|
int64_t integer;
|
||||||
|
double number;
|
||||||
std::string string;
|
std::string string;
|
||||||
Array array;
|
Array array;
|
||||||
Object object;
|
Object object;
|
||||||
@ -31,7 +34,8 @@ namespace vcpkg::Json
|
|||||||
|
|
||||||
ValueImpl(ValueKindConstant<VK::Null> vk, std::nullptr_t) : tag(vk), null() { }
|
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::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::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::Array> vk, Array&& arr) : tag(vk), array(std::move(arr)) { }
|
||||||
ValueImpl(ValueKindConstant<VK::Object> vk, Object&& obj) : tag(vk), object(std::move(obj)) { }
|
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::Null: return internal_assign(VK::Null, &ValueImpl::null, other);
|
||||||
case VK::Boolean: return internal_assign(VK::Boolean, &ValueImpl::boolean, 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::Number: return internal_assign(VK::Number, &ValueImpl::number, other);
|
||||||
case VK::String: return internal_assign(VK::String, &ValueImpl::string, other);
|
case VK::String: return internal_assign(VK::String, &ValueImpl::string, other);
|
||||||
case VK::Array: return internal_assign(VK::Array, &ValueImpl::array, 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_null() const noexcept { return kind() == VK::Null; }
|
||||||
bool Value::is_boolean() const noexcept { return kind() == VK::Boolean; }
|
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_string() const noexcept { return kind() == VK::String; }
|
||||||
bool Value::is_array() const noexcept { return kind() == VK::Array; }
|
bool Value::is_array() const noexcept { return kind() == VK::Array; }
|
||||||
bool Value::is_object() const noexcept { return kind() == VK::Object; }
|
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());
|
vcpkg::Checks::check_exit(VCPKG_LINE_INFO, is_boolean());
|
||||||
return underlying_->boolean;
|
return underlying_->boolean;
|
||||||
}
|
}
|
||||||
int64_t Value::number() const noexcept
|
int64_t Value::integer() const noexcept
|
||||||
{
|
{
|
||||||
vcpkg::Checks::check_exit(VCPKG_LINE_INFO, is_number());
|
vcpkg::Checks::check_exit(VCPKG_LINE_INFO, is_integer());
|
||||||
return underlying_->number;
|
return underlying_->integer;
|
||||||
|
}
|
||||||
|
double Value::number() const noexcept
|
||||||
|
{
|
||||||
|
auto k = kind();
|
||||||
|
if (k == VK::Number)
|
||||||
|
{
|
||||||
|
return underlying_->number;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return static_cast<double>(integer());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
StringView Value::string() const noexcept
|
StringView Value::string() const noexcept
|
||||||
{
|
{
|
||||||
@ -155,6 +177,7 @@ namespace vcpkg::Json
|
|||||||
{
|
{
|
||||||
case ValueKind::Null: return Value::null(nullptr);
|
case ValueKind::Null: return Value::null(nullptr);
|
||||||
case ValueKind::Boolean: return Value::boolean(boolean());
|
case ValueKind::Boolean: return Value::boolean(boolean());
|
||||||
|
case ValueKind::Integer: return Value::integer(integer());
|
||||||
case ValueKind::Number: return Value::number(number());
|
case ValueKind::Number: return Value::number(number());
|
||||||
case ValueKind::String: return Value::string(string());
|
case ValueKind::String: return Value::string(string());
|
||||||
case ValueKind::Array: return Value::array(array().clone());
|
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);
|
val.underlying_ = std::make_unique<ValueImpl>(ValueKindConstant<VK::Boolean>(), b);
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
Value Value::number(int64_t i) noexcept
|
Value Value::integer(int64_t i) noexcept
|
||||||
{
|
{
|
||||||
Value val;
|
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;
|
return val;
|
||||||
}
|
}
|
||||||
Value Value::string(StringView sv) noexcept
|
Value Value::string(StringView sv) noexcept
|
||||||
@ -211,25 +241,66 @@ namespace vcpkg::Json
|
|||||||
}
|
}
|
||||||
return arr;
|
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 Array
|
||||||
// struct Object {
|
// 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));
|
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);
|
auto v = get(key);
|
||||||
if (v)
|
if (v)
|
||||||
{
|
{
|
||||||
*v = std::move(value);
|
*v = std::move(value);
|
||||||
|
return *v;
|
||||||
}
|
}
|
||||||
else
|
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
|
auto Object::internal_find_key(StringView key) const noexcept -> underlying_t::const_iterator
|
||||||
{
|
{
|
||||||
@ -463,11 +534,15 @@ namespace vcpkg::Json
|
|||||||
Value parse_number() noexcept
|
Value parse_number() noexcept
|
||||||
{
|
{
|
||||||
Checks::check_exit(VCPKG_LINE_INFO, is_number_start(cur()));
|
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();
|
char32_t current = cur();
|
||||||
if (cur() == '-')
|
if (cur() == '-')
|
||||||
{
|
{
|
||||||
|
number_to_parse.push_back('-');
|
||||||
negative = true;
|
negative = true;
|
||||||
current = next();
|
current = next();
|
||||||
if (current == Unicode::end_of_file)
|
if (current == Unicode::end_of_file)
|
||||||
@ -480,54 +555,81 @@ namespace vcpkg::Json
|
|||||||
if (current == '0')
|
if (current == '0')
|
||||||
{
|
{
|
||||||
current = next();
|
current = next();
|
||||||
if (current != Unicode::end_of_file)
|
if (current == '.')
|
||||||
{
|
{
|
||||||
if (is_digit(current))
|
number_to_parse.append("0.");
|
||||||
{
|
floating = true;
|
||||||
add_error("Unexpected digits after a leading zero");
|
current = next();
|
||||||
}
|
|
||||||
if (current == '.')
|
|
||||||
{
|
|
||||||
add_error("Found a `.` -- this JSON implementation does not support floating point");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return Value::number(0);
|
else if (is_digit(current))
|
||||||
}
|
|
||||||
|
|
||||||
// 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))
|
|
||||||
{
|
|
||||||
const int digit = current - '0';
|
|
||||||
// result * 10 - digit < min_value : remember that result < 0
|
|
||||||
if (result < (min_value + digit) / 10)
|
|
||||||
{
|
{
|
||||||
add_error("Number is too big for an int64_t");
|
add_error("Unexpected digits after a leading zero");
|
||||||
return Value();
|
return Value();
|
||||||
}
|
}
|
||||||
result *= 10;
|
else
|
||||||
result -= digit;
|
{
|
||||||
|
if (negative)
|
||||||
|
{
|
||||||
|
return Value::number(-0.0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return Value::integer(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (is_digit(current))
|
||||||
|
{
|
||||||
|
number_to_parse.push_back(static_cast<char>(current));
|
||||||
current = next();
|
current = next();
|
||||||
}
|
}
|
||||||
if (current == '.')
|
if (!floating && current == '.')
|
||||||
{
|
{
|
||||||
add_error("Found a `.` -- this JSON implementation doesn't support floating point");
|
floating = true;
|
||||||
return Value();
|
number_to_parse.push_back('.');
|
||||||
}
|
current = next();
|
||||||
|
if (!is_digit(current))
|
||||||
if (!negative)
|
|
||||||
{
|
|
||||||
if (result == min_value)
|
|
||||||
{
|
{
|
||||||
add_error("Number is too big for a uint64_t");
|
add_error("Expected digits after the decimal point");
|
||||||
return Value();
|
return Value();
|
||||||
}
|
}
|
||||||
result = -result;
|
while (is_digit(current))
|
||||||
|
{
|
||||||
|
number_to_parse.push_back(static_cast<char>(current));
|
||||||
|
current = next();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Value::number(result);
|
#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)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int64_t res;
|
||||||
|
if (SCANF(number_to_parse.c_str(), "%" SCNd64, &res) != 1)
|
||||||
|
{
|
||||||
|
add_error(Strings::format("Invalid integer constant: %s", number_to_parse));
|
||||||
|
return Value();
|
||||||
|
}
|
||||||
|
return Value::integer(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef SCANF
|
||||||
}
|
}
|
||||||
|
|
||||||
Value parse_keyword() noexcept
|
Value parse_keyword() noexcept
|
||||||
@ -630,7 +732,7 @@ namespace vcpkg::Json
|
|||||||
|
|
||||||
auto current = cur();
|
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)
|
if (current == Unicode::end_of_file)
|
||||||
{
|
{
|
||||||
@ -804,118 +906,120 @@ namespace vcpkg::Json
|
|||||||
}
|
}
|
||||||
// } auto parse()
|
// } auto parse()
|
||||||
|
|
||||||
// auto stringify() {
|
namespace
|
||||||
static std::string& append_unicode_escape(std::string& s, char16_t code_unit)
|
|
||||||
{
|
{
|
||||||
s.append("\\u");
|
struct Stringifier
|
||||||
|
|
||||||
// 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)
|
|
||||||
{
|
|
||||||
// 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()))
|
|
||||||
{
|
{
|
||||||
bool matched = false; // early exit boolean
|
JsonStyle style;
|
||||||
// a. If C is listed in the "Code Point" column of Table 66, then
|
std::string& buffer;
|
||||||
for (auto pr : escape_sequences)
|
|
||||||
|
void append_indent(int indent)
|
||||||
{
|
{
|
||||||
// i. Set product to the string-concatenation of product and the escape sequence for C as specified in
|
if (style.use_tabs())
|
||||||
// the "Escape Sequence" column of the corresponding row.
|
|
||||||
if (code_point == pr.first)
|
|
||||||
{
|
{
|
||||||
product.append(pr.second);
|
buffer.append(indent, '\t');
|
||||||
matched = true;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
else
|
||||||
if (matched) break;
|
{
|
||||||
|
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
|
void append_unicode_escape(char16_t code_unit)
|
||||||
// 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.
|
buffer.append("\\u");
|
||||||
// ii. Set product to the string-concatenation of product and UnicodeEscape(unit).
|
|
||||||
append_unicode_escape(product, static_cast<char16_t>(code_point));
|
// AFAIK, there's no standard way of doing this?
|
||||||
break;
|
constexpr const char hex_digit[16] = {
|
||||||
|
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
|
||||||
|
|
||||||
|
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]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// c. Else,
|
// taken from the ECMAScript 2020 standard, 24.5.2.2: Runtime Semantics: QuoteJSONString
|
||||||
// i. Set product to the string-concatenation of product and the UTF16Encoding of C.
|
void append_quoted_json_string(StringView sv)
|
||||||
// (again, we use utf-8 here instead)
|
{
|
||||||
Unicode::utf8_append_code_point(product, code_point);
|
// 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('"');
|
||||||
|
|
||||||
// 3. Set product to the string-concatenation of product and the code unit 0x0022 (QUOTATION MARK).
|
// 2. For each code point C in ! UTF16DecodeString(value), do
|
||||||
product.push_back('"');
|
// (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;
|
||||||
|
}
|
||||||
|
|
||||||
// 4. Return product.
|
// b. Else if C has a numeric value less than 0x0020 (SPACE), or if C has the same numeric value as
|
||||||
return product;
|
// 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 std::string quote_json_string(StringView sv)
|
// c. Else,
|
||||||
{
|
// i. Set product to the string-concatenation of product and the UTF16Encoding of C.
|
||||||
std::string product;
|
// (again, we use utf-8 here instead)
|
||||||
append_quoted_json_string(product, sv);
|
Unicode::utf8_append_code_point(buffer, code_point);
|
||||||
return product;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
static void internal_stringify(const Value& value, JsonStyle style, std::string& buffer, int current_indent)
|
// 3. Set product to the string-concatenation of product and the code unit 0x0022 (QUOTATION MARK).
|
||||||
{
|
buffer.push_back('"');
|
||||||
const auto append_indent = [&](int indent) {
|
|
||||||
if (style.use_tabs())
|
|
||||||
{
|
|
||||||
buffer.append(indent, '\t');
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
void stringify_object(const Object& obj, int current_indent)
|
||||||
{
|
{
|
||||||
buffer.append(indent * style.spaces(), ' ');
|
buffer.push_back('{');
|
||||||
|
if (obj.size() != 0)
|
||||||
|
{
|
||||||
|
bool first = true;
|
||||||
|
|
||||||
|
for (const auto& el : obj)
|
||||||
|
{
|
||||||
|
if (!first)
|
||||||
|
{
|
||||||
|
buffer.push_back(',');
|
||||||
|
}
|
||||||
|
first = false;
|
||||||
|
|
||||||
|
buffer.append(style.newline());
|
||||||
|
append_indent(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('}');
|
||||||
}
|
}
|
||||||
};
|
|
||||||
switch (value.kind())
|
void stringify_array(const Array& arr, int current_indent)
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
case VK::Array:
|
|
||||||
{
|
|
||||||
const auto& arr = value.array();
|
|
||||||
buffer.push_back('[');
|
buffer.push_back('[');
|
||||||
if (arr.size() == 0)
|
if (arr.size() == 0)
|
||||||
{
|
{
|
||||||
@ -936,51 +1040,64 @@ namespace vcpkg::Json
|
|||||||
buffer.append(style.newline());
|
buffer.append(style.newline());
|
||||||
append_indent(current_indent + 1);
|
append_indent(current_indent + 1);
|
||||||
|
|
||||||
internal_stringify(el, style, buffer, current_indent + 1);
|
stringify(el, current_indent + 1);
|
||||||
}
|
}
|
||||||
buffer.append(style.newline());
|
buffer.append(style.newline());
|
||||||
append_indent(current_indent);
|
append_indent(current_indent);
|
||||||
buffer.push_back(']');
|
buffer.push_back(']');
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
case VK::Object:
|
|
||||||
|
void stringify(const Value& value, int current_indent)
|
||||||
{
|
{
|
||||||
const auto& obj = value.object();
|
switch (value.kind())
|
||||||
buffer.push_back('{');
|
|
||||||
if (obj.size() != 0)
|
|
||||||
{
|
{
|
||||||
bool first = true;
|
case VK::Null: buffer.append("null"); break;
|
||||||
|
case VK::Boolean:
|
||||||
for (const auto& el : obj)
|
|
||||||
{
|
{
|
||||||
if (!first)
|
auto v = value.boolean();
|
||||||
{
|
buffer.append(v ? "true" : "false");
|
||||||
buffer.push_back(',');
|
break;
|
||||||
}
|
}
|
||||||
first = false;
|
// 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;
|
||||||
buffer.append(style.newline());
|
case VK::Number: buffer.append(std::to_string(value.number())); break;
|
||||||
append_indent(current_indent + 1);
|
case VK::String:
|
||||||
|
{
|
||||||
auto key = quote_json_string(el.first);
|
append_quoted_json_string(value.string());
|
||||||
buffer.append(key.begin(), key.end());
|
break;
|
||||||
buffer.append(": ");
|
}
|
||||||
internal_stringify(el.second, style, buffer, current_indent + 1);
|
case VK::Array:
|
||||||
|
{
|
||||||
|
stringify_array(value.array(), current_indent);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case VK::Object:
|
||||||
|
{
|
||||||
|
stringify_object(value.object(), current_indent);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
buffer.append(style.newline());
|
|
||||||
append_indent(current_indent);
|
|
||||||
}
|
}
|
||||||
buffer.push_back('}');
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string stringify(const Value& value, JsonStyle style) noexcept
|
std::string stringify(const Value& value, JsonStyle style)
|
||||||
{
|
{
|
||||||
std::string res;
|
std::string res;
|
||||||
internal_stringify(value, style, res, 0);
|
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;
|
||||||
|
Stringifier{style, res}.stringify_array(arr, 0);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
// } auto stringify()
|
// } auto stringify()
|
||||||
|
@ -237,6 +237,7 @@ namespace vcpkg
|
|||||||
L"USERDOMAIN_ROAMINGPROFILE",
|
L"USERDOMAIN_ROAMINGPROFILE",
|
||||||
L"USERNAME",
|
L"USERNAME",
|
||||||
L"USERPROFILE",
|
L"USERPROFILE",
|
||||||
|
L"VCPKG_DISABLE_METRICS",
|
||||||
L"windir",
|
L"windir",
|
||||||
// Enables proxy information to be passed to Curl, the underlying download library in cmake.exe
|
// Enables proxy information to be passed to Curl, the underlying download library in cmake.exe
|
||||||
L"http_proxy",
|
L"http_proxy",
|
||||||
@ -558,6 +559,7 @@ VCPKG_MSVC_WARNING(suppress : 6335) // Leaking process information handle 'proce
|
|||||||
(void)env;
|
(void)env;
|
||||||
Debug::print("system(", cmd_line, ")\n");
|
Debug::print("system(", cmd_line, ")\n");
|
||||||
fflush(nullptr);
|
fflush(nullptr);
|
||||||
|
|
||||||
int exit_code = system(cmd_line.c_str());
|
int exit_code = system(cmd_line.c_str());
|
||||||
Debug::print(
|
Debug::print(
|
||||||
"system() returned ", exit_code, " after ", static_cast<unsigned int>(timer.microseconds()), " us\n");
|
"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");
|
Debug::print("popen(", actual_cmd_line, ")\n");
|
||||||
// Flush stdout before launching external process
|
// Flush stdout before launching external process
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
|
|
||||||
const auto pipe = popen(actual_cmd_line.c_str(), "r");
|
const auto pipe = popen(actual_cmd_line.c_str(), "r");
|
||||||
if (pipe == nullptr)
|
if (pipe == nullptr)
|
||||||
{
|
{
|
||||||
|
@ -32,7 +32,7 @@ namespace vcpkg::Commands::Version
|
|||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
+ std::string("-debug")
|
+ std::string("-debug")
|
||||||
#endif
|
#endif
|
||||||
+ std::string(Metrics::get_compiled_metrics_enabled() ? "" : "-external");
|
;
|
||||||
return S_VERSION;
|
return S_VERSION;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
#include <vcpkg/base/chrono.h>
|
#include <vcpkg/base/chrono.h>
|
||||||
#include <vcpkg/base/files.h>
|
#include <vcpkg/base/files.h>
|
||||||
#include <vcpkg/base/hash.h>
|
#include <vcpkg/base/hash.h>
|
||||||
|
#include <vcpkg/base/json.h>
|
||||||
#include <vcpkg/base/strings.h>
|
#include <vcpkg/base/strings.h>
|
||||||
#include <vcpkg/base/system.process.h>
|
#include <vcpkg/base/system.process.h>
|
||||||
|
|
||||||
@ -29,51 +30,73 @@ namespace vcpkg::Metrics
|
|||||||
return "";
|
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};
|
// uuid_field_size in bytes, not hex characters
|
||||||
char uuid[37];
|
constexpr size_t uuid_top_field_size[] = {4, 2, 2};
|
||||||
memset(uuid, 0, sizeof(uuid));
|
constexpr size_t uuid_bottom_field_size[] = {2, 6};
|
||||||
int num;
|
|
||||||
srand(static_cast<int>(time(nullptr)));
|
// uuid_field_size in hex characters, not bytes
|
||||||
int index = 0;
|
constexpr size_t uuid_size = 8 + 1 + 4 + 1 + 4 + 1 + 4 + 1 + 12;
|
||||||
for (int part = 0; part < 5; part++)
|
|
||||||
|
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] = '-';
|
res.push_back('-');
|
||||||
index++;
|
|
||||||
}
|
}
|
||||||
|
first = false;
|
||||||
// Generating UUID format version 4
|
for (size_t i = start_byte; i < start_byte + field_size; ++i)
|
||||||
// http://en.wikipedia.org/wiki/Universally_unique_identifier
|
|
||||||
for (int i = 0; i < part_sizes[part]; i++, index++)
|
|
||||||
{
|
{
|
||||||
if (part == 2 && i == 0)
|
auto shift = 64 - (i + 1) * 8;
|
||||||
{
|
write_byte(res, (top >> shift) & 0xFF);
|
||||||
num = 4;
|
|
||||||
}
|
|
||||||
else if (part == 4 && i == 0)
|
|
||||||
{
|
|
||||||
num = (rand() % 4) + 8;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
num = rand() % 16;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (num < 10)
|
|
||||||
{
|
|
||||||
uuid[index] = static_cast<char>('0' + num);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
uuid[index] = static_cast<char>('a' + (num - 10));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
start_byte += field_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
return uuid;
|
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 res;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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()
|
||||||
|
{
|
||||||
|
std::random_device rnd{};
|
||||||
|
std::uniform_int_distribution<std::uint64_t> uid{};
|
||||||
|
return uuid_of_integers(uid(rnd), uid(rnd));
|
||||||
}
|
}
|
||||||
|
|
||||||
static const std::string& get_session_id()
|
static const std::string& get_session_id()
|
||||||
@ -82,37 +105,6 @@ namespace vcpkg::Metrics
|
|||||||
return ID;
|
return ID;
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string to_json_string(const std::string& str)
|
|
||||||
{
|
|
||||||
std::string encoded = "\"";
|
|
||||||
for (auto&& ch : str)
|
|
||||||
{
|
|
||||||
if (ch == '\\')
|
|
||||||
{
|
|
||||||
encoded.append("\\\\");
|
|
||||||
}
|
|
||||||
else if (ch == '"')
|
|
||||||
{
|
|
||||||
encoded.append("\\\"");
|
|
||||||
}
|
|
||||||
else if (ch < 0x20 || 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 std::string get_os_version_string()
|
static std::string get_os_version_string()
|
||||||
{
|
{
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
@ -150,91 +142,89 @@ namespace vcpkg::Metrics
|
|||||||
std::string user_id = generate_random_UUID();
|
std::string user_id = generate_random_UUID();
|
||||||
std::string user_timestamp;
|
std::string user_timestamp;
|
||||||
std::string timestamp = get_current_date_time();
|
std::string timestamp = get_current_date_time();
|
||||||
std::string properties;
|
|
||||||
std::string measurements;
|
|
||||||
|
|
||||||
std::vector<std::string> buildtime_names;
|
Json::Object properties;
|
||||||
std::vector<std::string> buildtime_times;
|
Json::Object measurements;
|
||||||
|
|
||||||
|
Json::Array buildtime_names;
|
||||||
|
Json::Array buildtime_times;
|
||||||
|
|
||||||
void track_property(const std::string& name, const std::string& value)
|
void track_property(const std::string& name, const std::string& value)
|
||||||
{
|
{
|
||||||
if (properties.size() != 0) properties.push_back(',');
|
properties.insert_or_replace(name, Json::Value::string(value));
|
||||||
properties.append(to_json_string(name));
|
|
||||||
properties.push_back(':');
|
|
||||||
properties.append(to_json_string(value));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void track_metric(const std::string& name, double value)
|
void track_metric(const std::string& name, double value)
|
||||||
{
|
{
|
||||||
if (measurements.size() != 0) measurements.push_back(',');
|
measurements.insert_or_replace(name, Json::Value::number(value));
|
||||||
measurements.append(to_json_string(name));
|
|
||||||
measurements.push_back(':');
|
|
||||||
measurements.append(std::to_string(value));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void track_buildtime(const std::string& name, double value)
|
void track_buildtime(const std::string& name, double value)
|
||||||
{
|
{
|
||||||
buildtime_names.push_back(name);
|
buildtime_names.push_back(Json::Value::string(name));
|
||||||
buildtime_times.push_back(std::to_string(value));
|
buildtime_times.push_back(Json::Value::number(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string format_event_data_template() const
|
std::string format_event_data_template() const
|
||||||
{
|
{
|
||||||
auto props_plus_buildtimes = properties;
|
auto props_plus_buildtimes = properties.clone();
|
||||||
if (buildtime_names.size() > 0)
|
if (buildtime_names.size() > 0)
|
||||||
{
|
{
|
||||||
if (props_plus_buildtimes.size() > 0) props_plus_buildtimes.push_back(',');
|
props_plus_buildtimes.insert("buildnames_1", Json::Value::array(buildtime_names.clone()));
|
||||||
props_plus_buildtimes.append(Strings::format(R"("buildnames_1": [%s], "buildtimes": [%s])",
|
props_plus_buildtimes.insert("buildtimes", Json::Value::array(buildtime_times.clone()));
|
||||||
Strings::join(",", buildtime_names, to_json_string),
|
|
||||||
Strings::join(",", buildtime_times)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string& session_id = get_session_id();
|
Json::Array arr = Json::Array();
|
||||||
return Strings::format(R"([{
|
Json::Object& obj = arr.push_back(Json::Object());
|
||||||
"ver": 1,
|
|
||||||
"name": "Microsoft.ApplicationInsights.Event",
|
obj.insert("ver", Json::Value::integer(1));
|
||||||
"time": "%s",
|
obj.insert("name", Json::Value::string("Microsoft.ApplicationInsights.Event"));
|
||||||
"sampleRate": 100.000000,
|
obj.insert("time", Json::Value::string(timestamp));
|
||||||
"seq": "0:0",
|
obj.insert("sampleRate", Json::Value::number(100.0));
|
||||||
"iKey": "b4e88960-4393-4dd9-ab8e-97e8fe6d7603",
|
obj.insert("seq", Json::Value::string("0:0"));
|
||||||
"flags": 0.000000,
|
obj.insert("iKey", Json::Value::string("b4e88960-4393-4dd9-ab8e-97e8fe6d7603"));
|
||||||
"tags": {
|
obj.insert("flags", Json::Value::integer(0));
|
||||||
"ai.device.os": "Other",
|
|
||||||
"ai.device.osVersion": "%s-%s",
|
{
|
||||||
"ai.session.id": "%s",
|
Json::Object& tags = obj.insert("tags", Json::Object());
|
||||||
"ai.user.id": "%s",
|
|
||||||
"ai.user.accountAcquisitionDate": "%s"
|
tags.insert("ai.device.os", Json::Value::string("Other"));
|
||||||
},
|
|
||||||
"data": {
|
const char* os_name =
|
||||||
"baseType": "EventData",
|
|
||||||
"baseData": {
|
|
||||||
"ver": 2,
|
|
||||||
"name": "commandline_test7",
|
|
||||||
"properties": { %s },
|
|
||||||
"measurements": { %s }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}])",
|
|
||||||
timestamp,
|
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
"Windows",
|
"Windows";
|
||||||
#elif defined(__APPLE__)
|
#elif defined(__APPLE__)
|
||||||
"OSX",
|
"OSX";
|
||||||
#elif defined(__linux__)
|
#elif defined(__linux__)
|
||||||
"Linux",
|
"Linux";
|
||||||
#elif defined(__FreeBSD__)
|
#elif defined(__FreeBSD__)
|
||||||
"FreeBSD",
|
"FreeBSD";
|
||||||
#elif defined(__unix__)
|
#elif defined(__unix__)
|
||||||
"Unix",
|
"Unix";
|
||||||
#else
|
#else
|
||||||
"Other",
|
"Other";
|
||||||
#endif
|
#endif
|
||||||
get_os_version_string(),
|
|
||||||
session_id,
|
tags.insert("ai.device.osVersion",
|
||||||
user_id,
|
Json::Value::string(Strings::format("%s-%s", os_name, get_os_version_string())));
|
||||||
user_timestamp,
|
tags.insert("ai.session.id", Json::Value::string(get_session_id()));
|
||||||
props_plus_buildtimes,
|
tags.insert("ai.user.id", Json::Value::string(user_id));
|
||||||
measurements);
|
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
|
#endif
|
||||||
;
|
;
|
||||||
static bool g_should_print_metrics = false;
|
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()
|
std::string get_MAC_user()
|
||||||
{
|
{
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
|
if (!g_metrics.lock()->metrics_enabled())
|
||||||
|
{
|
||||||
|
return "{}";
|
||||||
|
}
|
||||||
|
|
||||||
auto getmac = System::cmd_execute_and_capture_output("getmac");
|
auto getmac = System::cmd_execute_and_capture_output("getmac");
|
||||||
|
|
||||||
if (getmac.exit_code != 0) return "0";
|
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::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)
|
void Metrics::track_buildtime(const std::string& name, double value)
|
||||||
{
|
{
|
||||||
|
if (!metrics_enabled())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
g_metricmessage.track_buildtime(name, value);
|
g_metricmessage.track_buildtime(name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Metrics::track_property(const std::string& name, const std::string& value)
|
void Metrics::track_property(const std::string& name, const std::string& value)
|
||||||
{
|
{
|
||||||
|
if (!metrics_enabled())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
g_metricmessage.track_property(name, value);
|
g_metricmessage.track_property(name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Metrics::upload(const std::string& payload)
|
void Metrics::upload(const std::string& payload)
|
||||||
{
|
{
|
||||||
|
if (!metrics_enabled())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
#if !defined(_WIN32)
|
#if !defined(_WIN32)
|
||||||
Util::unused(payload);
|
Util::unused(payload);
|
||||||
#else
|
#else
|
||||||
@ -402,6 +454,11 @@ namespace vcpkg::Metrics
|
|||||||
|
|
||||||
void Metrics::flush()
|
void Metrics::flush()
|
||||||
{
|
{
|
||||||
|
if (!metrics_enabled())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const std::string payload = g_metricmessage.format_event_data_template();
|
const std::string payload = g_metricmessage.format_event_data_template();
|
||||||
if (g_should_print_metrics) std::cerr << payload << "\n";
|
if (g_should_print_metrics) std::cerr << payload << "\n";
|
||||||
if (!g_should_send_metrics) return;
|
if (!g_should_send_metrics) return;
|
||||||
|
@ -212,6 +212,11 @@ namespace vcpkg
|
|||||||
parse_switch(true, "printmetrics", args.printmetrics);
|
parse_switch(true, "printmetrics", args.printmetrics);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (arg == "--disable-metrics")
|
||||||
|
{
|
||||||
|
parse_switch(true, "printmetrics", args.disable_metrics);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (arg == "--no-sendmetrics")
|
if (arg == "--no-sendmetrics")
|
||||||
{
|
{
|
||||||
parse_switch(false, "sendmetrics", args.sendmetrics);
|
parse_switch(false, "sendmetrics", args.sendmetrics);
|
||||||
@ -222,6 +227,11 @@ namespace vcpkg
|
|||||||
parse_switch(false, "printmetrics", args.printmetrics);
|
parse_switch(false, "printmetrics", args.printmetrics);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (arg == "--no-disable-metrics")
|
||||||
|
{
|
||||||
|
parse_switch(false, "printmetrics", args.disable_metrics);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (arg == "--featurepackages")
|
if (arg == "--featurepackages")
|
||||||
{
|
{
|
||||||
parse_switch(true, "featurepackages", args.featurepackages);
|
parse_switch(true, "featurepackages", args.featurepackages);
|
||||||
|
Loading…
Reference in New Issue
Block a user