1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
|
#ifndef INCLUDED_SRC_BUILDTOOL_BUILD_ENGINE_TARGET_MAP_RESULT_MAP_HPP
#define INCLUDED_SRC_BUILDTOOL_BUILD_ENGINE_TARGET_MAP_RESULT_MAP_HPP
#include <algorithm>
#include <fstream>
#include <mutex>
#include <string>
#include <thread>
#include <vector>
#include "nlohmann/json.hpp"
#include "gsl-lite/gsl-lite.hpp"
#include "src/buildtool/build_engine/analysed_target/analysed_target.hpp"
#include "src/buildtool/build_engine/base_maps/entity_name.hpp"
#include "src/buildtool/build_engine/expression/expression.hpp"
#include "src/buildtool/build_engine/target_map/configured_target.hpp"
#include "src/buildtool/common/tree.hpp"
#include "src/buildtool/multithreading/task.hpp"
#include "src/buildtool/multithreading/task_system.hpp"
#include "src/utils/cpp/hash_combine.hpp"
namespace BuildMaps::Target {
// Class collecting analysed targets for their canonical configuration.
class ResultTargetMap {
public:
struct ActionWithOrigin {
ActionDescription desc;
nlohmann::json origin;
};
template <bool kIncludeOrigins = false>
struct ResultType {
std::vector<ActionDescription> actions{};
std::vector<std::string> blobs{};
std::vector<Tree> trees{};
};
template <>
struct ResultType</*kIncludeOrigins=*/true> {
std::vector<ActionWithOrigin> actions{};
std::vector<std::string> blobs{};
std::vector<Tree> trees{};
};
explicit ResultTargetMap(std::size_t jobs) : width_{ComputeWidth(jobs)} {}
ResultTargetMap() = default;
// \brief Add the analysed target for the given target and
// configuration, if no entry is present for the given
// target-configuration pair. \returns the analysed target that is
// element of the map after insertion.
[[nodiscard]] auto Add(BuildMaps::Base::EntityName name,
Configuration conf,
gsl::not_null<AnalysedTargetPtr> result)
-> AnalysedTargetPtr {
auto part = std::hash<BuildMaps::Base::EntityName>{}(name) % width_;
std::unique_lock lock{m_[part]};
auto [entry, inserted] = targets_[part].emplace(
ConfiguredTarget{std::move(name), std::move(conf)},
std::move(result));
if (inserted) {
num_actions_[part] += entry->second->Actions().size();
num_blobs_[part] += entry->second->Blobs().size();
num_trees_[part] += entry->second->Trees().size();
}
return entry->second;
}
[[nodiscard]] auto ConfiguredTargets() const noexcept
-> std::vector<ConfiguredTarget> {
std::vector<ConfiguredTarget> targets{};
size_t s = 0;
for (const auto& target : targets_) {
s += target.size();
}
targets.reserve(s);
for (const auto& i : targets_) {
std::transform(i.begin(),
i.end(),
std::back_inserter(targets),
[](auto const& target) { return target.first; });
}
std::sort(targets.begin(),
targets.end(),
[](auto const& lhs, auto const& rhs) {
return lhs.ToString() < rhs.ToString();
});
return targets;
}
template <bool kIncludeOrigins = false>
[[nodiscard]] auto ToResult() const -> ResultType<kIncludeOrigins> {
ResultType<kIncludeOrigins> result{};
size_t na = 0;
size_t nb = 0;
size_t nt = 0;
for (std::size_t i = 0; i < width_; i++) {
na += num_actions_[i];
nb += num_blobs_[i];
nt += num_trees_[i];
}
result.actions.reserve(na);
result.blobs.reserve(nb);
result.trees.reserve(nt);
std::unordered_map<
std::string,
std::vector<std::pair<ConfiguredTarget, std::size_t>>>
origin_map;
origin_map.reserve(na);
if constexpr (kIncludeOrigins) {
for (const auto& target : targets_) {
std::for_each(
target.begin(), target.end(), [&](auto const& el) {
auto const& actions = el.second->Actions();
std::size_t pos{};
std::for_each(
actions.begin(),
actions.end(),
[&origin_map, &pos, &el](auto const& action) {
std::pair<ConfiguredTarget, std::size_t> origin{
el.first, pos++};
auto id = action.Id();
if (origin_map.contains(id)) {
origin_map[id].push_back(origin);
}
else {
origin_map[id] =
std::vector<std::pair<ConfiguredTarget,
std::size_t>>{
origin};
}
});
});
}
// Sort origins to get a reproducible order. We don't expect many
// origins for a single action, so the cost of comparison is not
// too important. Moreover, we expect most actions to have a single
// origin, so any precomputation would be more expensive.
for (auto const& i : origin_map) {
std::sort(origin_map[i.first].begin(),
origin_map[i.first].end(),
[](auto const& left, auto const& right) {
auto left_target = left.first.ToString();
auto right_target = right.first.ToString();
return (left_target < right_target) ||
(left_target == right_target &&
left.second < right.second);
});
}
}
for (const auto& target : targets_) {
std::for_each(target.begin(), target.end(), [&](auto const& el) {
auto const& actions = el.second->Actions();
if constexpr (kIncludeOrigins) {
std::for_each(actions.begin(),
actions.end(),
[&result, &origin_map](auto const& action) {
auto origins = nlohmann::json::array();
for (auto const& [ct, count] :
origin_map[action.Id()]) {
origins.push_back(nlohmann::json{
{"target", ct.target.ToJson()},
{"subtask", count},
{"config", ct.config.ToJson()}});
}
result.actions.emplace_back(
ActionWithOrigin{action, origins});
});
}
else {
std::for_each(actions.begin(),
actions.end(),
[&result](auto const& action) {
result.actions.emplace_back(action);
});
}
auto const& blobs = el.second->Blobs();
auto const& trees = el.second->Trees();
result.blobs.insert(
result.blobs.end(), blobs.begin(), blobs.end());
result.trees.insert(
result.trees.end(), trees.begin(), trees.end());
});
}
std::sort(result.blobs.begin(), result.blobs.end());
auto lastblob = std::unique(result.blobs.begin(), result.blobs.end());
result.blobs.erase(lastblob, result.blobs.end());
std::sort(result.trees.begin(),
result.trees.end(),
[](auto left, auto right) { return left.Id() < right.Id(); });
auto lasttree = std::unique(
result.trees.begin(),
result.trees.end(),
[](auto left, auto right) { return left.Id() == right.Id(); });
result.trees.erase(lasttree, result.trees.end());
std::sort(result.actions.begin(),
result.actions.end(),
[](auto left, auto right) {
if constexpr (kIncludeOrigins) {
return left.desc.Id() < right.desc.Id();
}
else {
return left.Id() < right.Id();
}
});
auto lastaction =
std::unique(result.actions.begin(),
result.actions.end(),
[](auto left, auto right) {
if constexpr (kIncludeOrigins) {
return left.desc.Id() == right.desc.Id();
}
else {
return left.Id() == right.Id();
}
});
result.actions.erase(lastaction, result.actions.end());
return result;
}
template <bool kIncludeOrigins = false>
[[nodiscard]] auto ToJson() const -> nlohmann::json {
auto const result = ToResult<kIncludeOrigins>();
auto actions = nlohmann::json::object();
auto trees = nlohmann::json::object();
std::for_each(result.actions.begin(),
result.actions.end(),
[&actions](auto const& action) {
if constexpr (kIncludeOrigins) {
auto const& id = action.desc.GraphAction().Id();
actions[id] = action.desc.ToJson();
actions[id]["origins"] = action.origin;
}
else {
auto const& id = action.GraphAction().Id();
actions[id] = action.ToJson();
}
});
std::for_each(
result.trees.begin(),
result.trees.end(),
[&trees](auto const& tree) { trees[tree.Id()] = tree.ToJson(); });
return nlohmann::json{
{"actions", actions}, {"blobs", result.blobs}, {"trees", trees}};
}
template <bool kIncludeOrigins = true>
auto ToFile(std::string const& graph_file, int indent = 2) const -> void {
std::ofstream os(graph_file);
os << std::setw(indent) << ToJson<kIncludeOrigins>() << std::endl;
}
void Clear(gsl::not_null<TaskSystem*> const& ts) {
for (std::size_t i = 0; i < width_; ++i) {
ts->QueueTask([i, this]() { targets_[i].clear(); });
}
}
private:
constexpr static std::size_t kScalingFactor = 2;
std::size_t width_{ComputeWidth(0)};
std::vector<std::mutex> m_{width_};
std::vector<
std::unordered_map<ConfiguredTarget, gsl::not_null<AnalysedTargetPtr>>>
targets_{width_};
std::vector<std::size_t> num_actions_{std::vector<std::size_t>(width_)};
std::vector<std::size_t> num_blobs_{std::vector<std::size_t>(width_)};
std::vector<std::size_t> num_trees_{std::vector<std::size_t>(width_)};
constexpr static auto ComputeWidth(std::size_t jobs) -> std::size_t {
if (jobs <= 0) {
// Non-positive indicates to use the default value
return ComputeWidth(
std::max(1U, std::thread::hardware_concurrency()));
}
return jobs * kScalingFactor + 1;
}
}; // namespace BuildMaps::Target
} // namespace BuildMaps::Target
#endif // INCLUDED_SRC_BUILDTOOL_BUILD_ENGINE_TARGET_MAP_RESULT_MAP_HPP
|