1
1
#include " progress-bar.hh"
2
2
#include " util.hh"
3
3
#include " sync.hh"
4
+ #include " store-api.hh"
4
5
5
6
#include < map>
7
+ #include < atomic>
8
+
9
+ #include < sys/ioctl.h>
6
10
7
11
namespace nix {
8
12
@@ -12,31 +16,47 @@ class ProgressBar : public Logger
12
16
13
17
struct ActInfo
14
18
{
15
- Activity * activity;
16
- Verbosity lvl;
17
- std::string s;
19
+ std::string s, s2;
18
20
};
19
21
20
- struct Progress
22
+ struct DownloadInfo
21
23
{
22
- uint64_t expected = 0 , progress = 0 ;
24
+ std::string uri;
25
+ uint64_t current = 0 ;
26
+ uint64_t expected = 0 ;
23
27
};
24
28
25
29
struct State
26
30
{
31
+ std::map<Activity::t, Path> builds;
32
+ std::set<Activity::t> runningBuilds;
33
+ uint64_t succeededBuilds = 0 ;
34
+ uint64_t failedBuilds = 0 ;
35
+ std::map<Activity::t, Path> substitutions;
36
+ std::set<Activity::t> runningSubstitutions;
37
+ uint64_t succeededSubstitutions = 0 ;
38
+ uint64_t downloadedBytes = 0 ; // finished downloads
39
+ std::map<Activity::t, DownloadInfo> downloads;
27
40
std::list<ActInfo> activities;
28
- std::map<Activity *, std::list<ActInfo>::iterator> its;
29
- std::map<std::string, Progress> progress;
41
+ std::map<Activity::t, std::list<ActInfo>::iterator> its;
30
42
};
31
43
32
44
Sync<State> state_;
33
45
46
+ int width = 0 ;
47
+
34
48
public:
35
49
50
+ ProgressBar ()
51
+ {
52
+ struct winsize ws;
53
+ if (ioctl (1 , TIOCGWINSZ, &ws) == 0 )
54
+ width = ws.ws_col ;
55
+ }
56
+
36
57
~ProgressBar ()
37
58
{
38
59
auto state (state_.lock ());
39
- assert (state->activities .empty ());
40
60
writeToStderr (" \r \e[K" );
41
61
}
42
62
@@ -52,52 +72,36 @@ class ProgressBar : public Logger
52
72
update (state);
53
73
}
54
74
55
- void startActivity (Activity & activity, Verbosity lvl, const FormatOrString & fs) override
56
- {
57
- if (lvl > verbosity) return ;
58
- auto state (state_.lock ());
59
- state->activities .emplace_back (ActInfo{&activity, lvl, fs.s });
60
- state->its .emplace (&activity, std::prev (state->activities .end ()));
61
- update (*state);
62
- }
63
-
64
- void stopActivity (Activity & activity) override
65
- {
66
- auto state (state_.lock ());
67
- auto i = state->its .find (&activity);
68
- if (i == state->its .end ()) return ;
69
- state->activities .erase (i->second );
70
- state->its .erase (i);
71
- update (*state);
72
- }
73
-
74
- void setExpected (const std::string & label, uint64_t value) override
75
+ void createActivity (State & state, Activity::t activity, const std::string & s)
75
76
{
76
- auto state (state_.lock ());
77
- state->progress [label].expected = value;
78
- }
79
-
80
- void setProgress (const std::string & label, uint64_t value) override
81
- {
82
- auto state (state_.lock ());
83
- state->progress [label].progress = value;
77
+ state.activities .emplace_back (ActInfo{s});
78
+ state.its .emplace (activity, std::prev (state.activities .end ()));
84
79
}
85
80
86
- void incExpected ( const std::string & label, uint64_t value) override
81
+ void deleteActivity (State & state, Activity::t activity)
87
82
{
88
- auto state (state_.lock ());
89
- state->progress [label].expected += value;
83
+ auto i = state.its .find (activity);
84
+ if (i != state.its .end ()) {
85
+ state.activities .erase (i->second );
86
+ state.its .erase (i);
87
+ }
90
88
}
91
89
92
- void incProgress ( const std::string & label, uint64_t value) override
90
+ void updateActivity (State & state, Activity::t activity, const std::string & s2)
93
91
{
94
- auto state (state_.lock ());
95
- state->progress [label].progress += value;
92
+ auto i = state.its .find (activity);
93
+ assert (i != state.its .end ());
94
+ ActInfo info = *i->second ;
95
+ state.activities .erase (i->second );
96
+ info.s2 = s2;
97
+ state.activities .emplace_back (info);
98
+ i->second = std::prev (state.activities .end ());
96
99
}
97
100
98
101
void update ()
99
102
{
100
103
auto state (state_.lock ());
104
+ update (*state);
101
105
}
102
106
103
107
void update (State & state)
@@ -113,28 +117,169 @@ class ProgressBar : public Logger
113
117
114
118
if (!state.activities .empty ()) {
115
119
if (!status.empty ()) line += " " ;
116
- line += state.activities .rbegin ()->s ;
120
+ auto i = state.activities .rbegin ();
121
+ line += i->s ;
122
+ if (!i->s2 .empty ()) {
123
+ line += " : " ;
124
+ line += i->s2 ;
125
+ }
117
126
}
118
127
119
128
line += " \e[K" ;
120
- writeToStderr (line);
129
+ writeToStderr (std::string ( line, 0 , width - 1 ) );
121
130
}
122
131
123
132
std::string getStatus (State & state)
124
133
{
125
134
std::string res;
126
- for (auto & p : state.progress )
127
- if (p.second .expected || p.second .progress ) {
128
- if (!res.empty ()) res += " , " ;
129
- res += std::to_string (p.second .progress );
130
- if (p.second .expected ) {
131
- res += " /" ;
132
- res += std::to_string (p.second .expected );
133
- }
134
- res += " " ; res += p.first ;
135
+
136
+ if (state.failedBuilds ) {
137
+ if (!res.empty ()) res += " , " ;
138
+ res += fmt (ANSI_RED " %d failed" ANSI_NORMAL, state.failedBuilds );
139
+ }
140
+
141
+ if (!state.builds .empty () || state.succeededBuilds )
142
+ {
143
+ if (!res.empty ()) res += " , " ;
144
+ if (!state.runningBuilds .empty ())
145
+ res += fmt (ANSI_BLUE " %d" " /" ANSI_NORMAL, state.runningBuilds .size ());
146
+ res += fmt (ANSI_GREEN " %d/%d built" ANSI_NORMAL,
147
+ state.succeededBuilds , state.succeededBuilds + state.builds .size ());
148
+ }
149
+
150
+ if (!state.substitutions .empty () || state.succeededSubstitutions ) {
151
+ if (!res.empty ()) res += " , " ;
152
+ if (!state.runningSubstitutions .empty ())
153
+ res += fmt (ANSI_BLUE " %d" " /" ANSI_NORMAL, state.runningSubstitutions .size ());
154
+ res += fmt (ANSI_GREEN " %d/%d fetched" ANSI_NORMAL,
155
+ state.succeededSubstitutions ,
156
+ state.succeededSubstitutions + state.substitutions .size ());
157
+ }
158
+
159
+ if (!state.downloads .empty () || state.downloadedBytes ) {
160
+ if (!res.empty ()) res += " , " ;
161
+ uint64_t expected = state.downloadedBytes , current = state.downloadedBytes ;
162
+ for (auto & i : state.downloads ) {
163
+ expected += i.second .expected ;
164
+ current += i.second .current ;
135
165
}
166
+ res += fmt (" %1$.0f/%2$.0f KiB" , current / 1024.0 , expected / 1024.0 );
167
+ }
168
+
136
169
return res;
137
170
}
171
+
172
+ void event (const Event & ev) override
173
+ {
174
+ if (ev.type == evBuildCreated) {
175
+ auto state (state_.lock ());
176
+ state->builds [ev.getI (0 )] = ev.getS (1 );
177
+ update (*state);
178
+ }
179
+
180
+ if (ev.type == evBuildStarted) {
181
+ auto state (state_.lock ());
182
+ Activity::t act = ev.getI (0 );
183
+ state->runningBuilds .insert (act);
184
+ auto name = storePathToName (state->builds [act]);
185
+ if (hasSuffix (name, " .drv" ))
186
+ name.resize (name.size () - 4 );
187
+ createActivity (*state, act, fmt (" building " ANSI_BOLD " %s" ANSI_NORMAL, name));
188
+ update (*state);
189
+ }
190
+
191
+ if (ev.type == evBuildFinished) {
192
+ auto state (state_.lock ());
193
+ Activity::t act = ev.getI (0 );
194
+ if (ev.getI (1 )) {
195
+ if (state->runningBuilds .count (act))
196
+ state->succeededBuilds ++;
197
+ } else
198
+ state->failedBuilds ++;
199
+ state->runningBuilds .erase (act);
200
+ state->builds .erase (act);
201
+ deleteActivity (*state, act);
202
+ update (*state);
203
+ }
204
+
205
+ if (ev.type == evBuildOutput) {
206
+ auto state (state_.lock ());
207
+ Activity::t act = ev.getI (0 );
208
+ assert (state->runningBuilds .count (act));
209
+ updateActivity (*state, act, ev.getS (1 ));
210
+ update (*state);
211
+ }
212
+
213
+ if (ev.type == evSubstitutionCreated) {
214
+ auto state (state_.lock ());
215
+ state->substitutions [ev.getI (0 )] = ev.getS (1 );
216
+ update (*state);
217
+ }
218
+
219
+ if (ev.type == evSubstitutionStarted) {
220
+ auto state (state_.lock ());
221
+ Activity::t act = ev.getI (0 );
222
+ state->runningSubstitutions .insert (act);
223
+ auto name = storePathToName (state->substitutions [act]);
224
+ createActivity (*state, act, fmt (" fetching " ANSI_BOLD " %s" ANSI_NORMAL, name));
225
+ update (*state);
226
+ }
227
+
228
+ if (ev.type == evSubstitutionFinished) {
229
+ auto state (state_.lock ());
230
+ Activity::t act = ev.getI (0 );
231
+ if (ev.getI (1 )) {
232
+ if (state->runningSubstitutions .count (act))
233
+ state->succeededSubstitutions ++;
234
+ }
235
+ state->runningSubstitutions .erase (act);
236
+ state->substitutions .erase (act);
237
+ deleteActivity (*state, act);
238
+ update (*state);
239
+ }
240
+
241
+ if (ev.type == evDownloadCreated) {
242
+ auto state (state_.lock ());
243
+ Activity::t act = ev.getI (0 );
244
+ std::string uri = ev.getS (1 );
245
+ state->downloads .emplace (act, DownloadInfo{uri});
246
+ if (state->runningSubstitutions .empty ()) // FIXME: hack
247
+ createActivity (*state, act, fmt (" downloading " ANSI_BOLD " %s" ANSI_NORMAL " " , uri));
248
+ update (*state);
249
+ }
250
+
251
+ if (ev.type == evDownloadProgress) {
252
+ auto state (state_.lock ());
253
+ Activity::t act = ev.getI (0 );
254
+ auto i = state->downloads .find (act);
255
+ assert (i != state->downloads .end ());
256
+ i->second .expected = ev.getI (1 );
257
+ i->second .current = ev.getI (2 );
258
+ update (*state);
259
+ }
260
+
261
+ if (ev.type == evDownloadSucceeded) {
262
+ auto state (state_.lock ());
263
+ Activity::t act = ev.getI (0 );
264
+ auto i = state->downloads .find (act);
265
+ assert (i != state->downloads .end ());
266
+ state->downloadedBytes += ev.getI (1 );
267
+ state->downloads .erase (i);
268
+ deleteActivity (*state, act);
269
+ update (*state);
270
+ }
271
+
272
+ if (ev.type == evDownloadDestroyed) {
273
+ auto state (state_.lock ());
274
+ Activity::t act = ev.getI (0 );
275
+ auto i = state->downloads .find (act);
276
+ if (i != state->downloads .end ()) {
277
+ state->downloads .erase (i);
278
+ deleteActivity (*state, act);
279
+ update (*state);
280
+ }
281
+ }
282
+ }
138
283
};
139
284
140
285
StartProgressBar::StartProgressBar ()
0 commit comments