Skip to content

Commit e70b618

Browse files
authored
Add a "status" API to DiagnosticContext, overhaul Downloads console output (microsoft#1565)
Extensive overhaul of our downloads handling and console output; @JavierMatosD and I have gone back and forth several times and yet kept introducing unintended bugs in other places, which led me to believe targeted fixes would no longer cut it. Fixes many longstanding bugs and hopefully makes our console output for this more understandable: * We no longer print 'error' when an asset cache misses but the authoritative download succeeds. This partially undoes microsoft#1541. It is good to print errors immediately when they happen, but if a subsequent authoritative download succeeds we need to not print those errors. * We now always and consistently print output from x-script s at the time that actually happens. Resolves https://devdiv.visualstudio.com/DevDiv/_workitems/edit/2300063 * We don't tell the user that proxy settings might fix a hash mismatch problem. * We do tell the user that proxy settings might fix a download from asset cache problem. * We now always tell the user the full command line we tried when invoking an x-script that fails. * We don't crash if an x-script doesn't create the file we expect, or creates a file with the wrong hash. * We now always print what we are doing *before* touching the network, so if we hang the user knows which server is being problematic. Note that this includes storing back to asset caches which we were previously entirely silent about except in case of failure. Other changes: * Removed debug output about asset cache configuration. The output was misleading / wrong depending on readwrite settings, and echoing to the user exactly what they said before we've interpreted it is not useful debug output. (Contrast with other `VcpkgPaths` debug output which tend to be paths we have likely changed from something a user said) Other notes: * This makes all dependencies of microsoft#908 speak `DiagnosticContext` so it will be easy to audit that the foreground/background thread behavior is correct after this. * I did test the curl status parsing on old Ubuntu again. Special thanks to @JavierMatosD for his help in review of the first console output attempts and for blowing the dust off this area in the first place.
1 parent 5b17460 commit e70b618

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+3682
-1885
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Param([string]$File)
2+
Write-Host "Creating file with the wrong hash"
3+
Set-Content -Path $File -Value "This is a file with the wrong hash" -Encoding Ascii -NoNewline
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
throw "Script download error"
1+
Write-Host "Script download error"
2+
exit 1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Write-Host "Not creating a file"

azure-pipelines/end-to-end-tests-dir/asset-caching.ps1

+337-98
Large diffs are not rendered by default.
+10-8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#pragma once
22

3-
#include <vcpkg/base/expected.h>
3+
#include <vcpkg/base/diagnostics.h>
4+
#include <vcpkg/base/optional.h>
45
#include <vcpkg/base/stringview.h>
56

67
#include <string>
@@ -10,21 +11,22 @@ namespace vcpkg
1011
namespace details
1112
{
1213
template<class F>
13-
void api_stable_format_cb(void* f, std::string& s, StringView sv)
14+
bool api_stable_format_cb(void* f, std::string& s, StringView sv)
1415
{
15-
(*(F*)(f))(s, sv);
16+
return (*(F*)(f))(s, sv);
1617
}
1718

18-
ExpectedL<std::string> api_stable_format_impl(StringView fmtstr,
19-
void (*cb)(void*, std::string&, StringView),
20-
void* data);
19+
Optional<std::string> api_stable_format_impl(DiagnosticContext& context,
20+
StringView fmtstr,
21+
bool (*cb)(void*, std::string&, StringView),
22+
void* data);
2123
}
2224

2325
// This function exists in order to provide an API-stable formatting function similar to `std::format()` that does
2426
// not depend on the feature set of fmt or the C++ standard library and thus can be contractual for user interfaces.
2527
template<class F>
26-
ExpectedL<std::string> api_stable_format(StringView fmtstr, F&& handler)
28+
Optional<std::string> api_stable_format(DiagnosticContext& context, StringView fmtstr, F&& handler)
2729
{
28-
return details::api_stable_format_impl(fmtstr, &details::api_stable_format_cb<F>, &handler);
30+
return details::api_stable_format_impl(context, fmtstr, &details::api_stable_format_cb<F>, &handler);
2931
}
3032
}

include/vcpkg/base/diagnostics.h

+133-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#pragma once
22

33
#include <vcpkg/base/expected.h>
4+
#include <vcpkg/base/message_sinks.h>
45
#include <vcpkg/base/messages.h>
56
#include <vcpkg/base/optional.h>
67

@@ -66,11 +67,22 @@ namespace vcpkg
6667
std::string to_string() const;
6768
void to_string(std::string& target) const;
6869

70+
MessageLine to_message_line() const;
71+
6972
LocalizedString to_json_reader_string(const std::string& path, const LocalizedString& type) const;
7073

7174
DiagKind kind() const noexcept { return m_kind; }
75+
// Returns this DiagnosticLine with kind == Error reduced to Warning.
76+
DiagnosticLine reduce_to_warning() const&;
77+
DiagnosticLine reduce_to_warning() &&;
7278

7379
private:
80+
DiagnosticLine(DiagKind kind,
81+
const Optional<std::string>& origin,
82+
TextRowCol position,
83+
const LocalizedString& message);
84+
DiagnosticLine(DiagKind kind, Optional<std::string>&& origin, TextRowCol position, LocalizedString&& message);
85+
7486
DiagKind m_kind;
7587
Optional<std::string> m_origin;
7688
TextRowCol m_position;
@@ -79,8 +91,11 @@ namespace vcpkg
7991

8092
struct DiagnosticContext
8193
{
94+
// The `report` family are used to report errors or warnings that may result in a function failing
95+
// to do what it is intended to do. Data sent to the `report` family is expected to not be printed
96+
// to the console if a caller decides to handle an error.
8297
virtual void report(const DiagnosticLine& line) = 0;
83-
virtual void report(DiagnosticLine&& line) { report(line); }
98+
virtual void report(DiagnosticLine&& line);
8499

85100
void report_error(const LocalizedString& message) { report(DiagnosticLine{DiagKind::Error, message}); }
86101
void report_error(LocalizedString&& message) { report(DiagnosticLine{DiagKind::Error, std::move(message)}); }
@@ -92,15 +107,64 @@ namespace vcpkg
92107
this->report_error(std::move(message));
93108
}
94109

110+
template<VCPKG_DECL_MSG_TEMPLATE>
111+
void report_error_with_log(StringView log_content, VCPKG_DECL_MSG_ARGS)
112+
{
113+
LocalizedString message;
114+
msg::format_to(message, VCPKG_EXPAND_MSG_ARGS);
115+
message.append_raw('\n');
116+
message.append_raw(log_content);
117+
this->report_error(std::move(message));
118+
}
119+
120+
void report_system_error(StringLiteral system_api_name, int error_value);
121+
122+
// The `status` family are used to report status or progress information that callers are expected
123+
// to show on the console, even if it would decide to handle errors or warnings itself.
124+
// Examples:
125+
// * "Downloading file..."
126+
// * "Building package 1 of 47..."
127+
//
128+
// Some implementations of DiagnosticContext may buffer these messages *anyway* if that makes sense,
129+
// for example, if the work is happening on a background thread.
130+
virtual void statusln(const LocalizedString& message) = 0;
131+
virtual void statusln(LocalizedString&& message) = 0;
132+
virtual void statusln(const MessageLine& message) = 0;
133+
virtual void statusln(MessageLine&& message) = 0;
134+
95135
protected:
96136
~DiagnosticContext() = default;
97137
};
98138

139+
struct PrintingDiagnosticContext final : DiagnosticContext
140+
{
141+
PrintingDiagnosticContext(MessageSink& sink) : sink(sink) { }
142+
143+
virtual void report(const DiagnosticLine& line) override;
144+
145+
virtual void statusln(const LocalizedString& message) override;
146+
virtual void statusln(LocalizedString&& message) override;
147+
virtual void statusln(const MessageLine& message) override;
148+
virtual void statusln(MessageLine&& message) override;
149+
150+
private:
151+
MessageSink& sink;
152+
};
153+
154+
// Stores all diagnostics into a vector, while passing through status lines to an underlying MessageSink.
99155
struct BufferedDiagnosticContext final : DiagnosticContext
100156
{
157+
BufferedDiagnosticContext(MessageSink& status_sink) : status_sink(status_sink) { }
158+
101159
virtual void report(const DiagnosticLine& line) override;
102160
virtual void report(DiagnosticLine&& line) override;
103161

162+
virtual void statusln(const LocalizedString& message) override;
163+
virtual void statusln(LocalizedString&& message) override;
164+
virtual void statusln(const MessageLine& message) override;
165+
virtual void statusln(MessageLine&& message) override;
166+
167+
MessageSink& status_sink;
104168
std::vector<DiagnosticLine> lines;
105169

106170
// Prints all diagnostics to the supplied sink.
@@ -111,9 +175,75 @@ namespace vcpkg
111175
void to_string(std::string& target) const;
112176

113177
bool any_errors() const noexcept;
178+
bool empty() const noexcept;
179+
};
180+
181+
// Stores all diagnostics and status messages into a vector. This is generally used for background thread or similar
182+
// scenarios where even status messages can't be immediately printed.
183+
struct FullyBufferedDiagnosticContext final : DiagnosticContext
184+
{
185+
virtual void report(const DiagnosticLine& line) override;
186+
virtual void report(DiagnosticLine&& line) override;
187+
188+
virtual void statusln(const LocalizedString& message) override;
189+
virtual void statusln(LocalizedString&& message) override;
190+
virtual void statusln(const MessageLine& message) override;
191+
virtual void statusln(MessageLine&& message) override;
192+
193+
std::vector<MessageLine> lines;
194+
195+
// Prints all diagnostics to the supplied sink.
196+
void print_to(MessageSink& sink) const;
197+
// Converts this message into a string
198+
// Prefer print() if possible because it applies color
199+
std::string to_string() const;
200+
void to_string(std::string& target) const;
201+
202+
bool empty() const noexcept;
203+
};
204+
205+
// DiagnosticContext for attempted operations that may be recovered.
206+
// Stores all diagnostics and passes through all status messages. Afterwards, call commit() to report all
207+
// diagnostics to the outer DiagnosticContext, or handle() to forget them.
208+
struct AttemptDiagnosticContext final : DiagnosticContext
209+
{
210+
AttemptDiagnosticContext(DiagnosticContext& inner_context) : inner_context(inner_context) { }
211+
212+
virtual void report(const DiagnosticLine& line) override;
213+
virtual void report(DiagnosticLine&& line) override;
214+
215+
virtual void statusln(const LocalizedString& message) override;
216+
virtual void statusln(LocalizedString&& message) override;
217+
virtual void statusln(const MessageLine& message) override;
218+
virtual void statusln(MessageLine&& message) override;
219+
220+
void commit();
221+
void handle();
222+
223+
~AttemptDiagnosticContext();
224+
225+
DiagnosticContext& inner_context;
226+
std::vector<DiagnosticLine> lines;
227+
};
228+
229+
// Wraps another DiagnosticContext and reduces the severity of any reported diagnostics to warning from error.
230+
struct WarningDiagnosticContext final : DiagnosticContext
231+
{
232+
WarningDiagnosticContext(DiagnosticContext& inner_context) : inner_context(inner_context) { }
233+
234+
virtual void report(const DiagnosticLine& line) override;
235+
virtual void report(DiagnosticLine&& line) override;
236+
237+
virtual void statusln(const LocalizedString& message) override;
238+
virtual void statusln(LocalizedString&& message) override;
239+
virtual void statusln(const MessageLine& message) override;
240+
virtual void statusln(MessageLine&& message) override;
241+
242+
DiagnosticContext& inner_context;
114243
};
115244

116245
extern DiagnosticContext& console_diagnostic_context;
246+
extern DiagnosticContext& status_only_diagnostic_context;
117247
extern DiagnosticContext& null_diagnostic_context;
118248

119249
// The following overloads are implementing
@@ -191,7 +321,7 @@ namespace vcpkg
191321
{
192322
using Unwrapper = AdaptContextUnwrapOptional<std::invoke_result_t<Fn, BufferedDiagnosticContext&, Args...>>;
193323
using ReturnType = ExpectedL<typename Unwrapper::type>;
194-
BufferedDiagnosticContext bdc;
324+
BufferedDiagnosticContext bdc{out_sink};
195325
decltype(auto) maybe_result = functor(bdc, std::forward<Args>(args)...);
196326
if (auto result = maybe_result.get())
197327
{
@@ -261,7 +391,7 @@ namespace vcpkg
261391
{
262392
using ReturnType = ExpectedL<
263393
typename AdaptContextDetectUniquePtr<std::invoke_result_t<Fn, BufferedDiagnosticContext&, Args...>>::type>;
264-
BufferedDiagnosticContext bdc;
394+
BufferedDiagnosticContext bdc{out_sink};
265395
decltype(auto) maybe_result = functor(bdc, std::forward<Args>(args)...);
266396
if (maybe_result)
267397
{

0 commit comments

Comments
 (0)