@@ -29,6 +29,17 @@ class _ComputeJobsResult {
29
29
final bool sawMalformed;
30
30
}
31
31
32
+ enum _SetStatus {
33
+ Intersection ,
34
+ Difference ,
35
+ }
36
+
37
+ class _SetStatusCommand {
38
+ _SetStatusCommand (this .setStatus, this .command);
39
+ final _SetStatus setStatus;
40
+ final Command command;
41
+ }
42
+
32
43
/// A class that runs clang-tidy on all or only the changed files in a git
33
44
/// repo.
34
45
class ClangTidy {
@@ -92,14 +103,14 @@ class ClangTidy {
92
103
93
104
_outSink.writeln (_linterOutputHeader);
94
105
95
- final List <io.File > changedFiles = await computeChangedFiles ();
106
+ final List <io.File > filesOfInterest = await computeFilesOfInterest ();
96
107
97
108
if (options.verbose) {
98
109
_outSink.writeln ('Checking lint in repo at ${options .repoPath .path }.' );
99
110
if (options.checksArg.isNotEmpty) {
100
111
_outSink.writeln ('Checking for specific checks: ${options .checks }.' );
101
112
}
102
- final int changedFilesCount = changedFiles .length;
113
+ final int changedFilesCount = filesOfInterest .length;
103
114
if (options.lintAll) {
104
115
_outSink.writeln ('Checking all $changedFilesCount files the repo dir.' );
105
116
} else {
@@ -112,9 +123,16 @@ class ClangTidy {
112
123
final List <dynamic > buildCommandsData = jsonDecode (
113
124
options.buildCommandsPath.readAsStringSync (),
114
125
) as List <dynamic >;
115
- final List <Command > changedFileBuildCommands = await getLintCommandsForChangedFiles (
126
+ final List <List <dynamic >> shardBuildCommandsData = options
127
+ .shardCommandsPaths
128
+ .map ((io.File file) =>
129
+ jsonDecode (file.readAsStringSync ()) as List <dynamic >)
130
+ .toList ();
131
+ final List <Command > changedFileBuildCommands = await getLintCommandsForFiles (
116
132
buildCommandsData,
117
- changedFiles,
133
+ filesOfInterest,
134
+ shardBuildCommandsData,
135
+ options.shardId,
118
136
);
119
137
120
138
if (changedFileBuildCommands.isEmpty) {
@@ -153,7 +171,7 @@ class ClangTidy {
153
171
/// The files with local modifications or all the files if `lintAll` was
154
172
/// specified.
155
173
@visibleForTesting
156
- Future <List <io.File >> computeChangedFiles () async {
174
+ Future <List <io.File >> computeFilesOfInterest () async {
157
175
if (options.lintAll) {
158
176
return options.repoPath
159
177
.listSync (recursive: true )
@@ -171,23 +189,81 @@ class ClangTidy {
171
189
return repo.changedFiles;
172
190
}
173
191
192
+ Iterable <T > _takeShard <T >(Iterable <T > values, int id, int shardCount) sync * {
193
+ int count = 0 ;
194
+ for (final T val in values) {
195
+ if (count % shardCount == id) {
196
+ yield val;
197
+ }
198
+ count++ ;
199
+ }
200
+ }
201
+
202
+ Iterable <_SetStatusCommand > _calcIntersection (Iterable <Command > items, Iterable <List <Command >> sets) sync * {
203
+ bool allSetsContain (Command command) {
204
+ for (final List <Command > set in sets) {
205
+ final Iterable <String > filePaths = set .map ((Command e) => e.filePath);
206
+ if (! filePaths.contains (command.filePath)) {
207
+ return false ;
208
+ }
209
+ }
210
+ return true ;
211
+ }
212
+ for (final Command command in items) {
213
+ if (allSetsContain (command)) {
214
+ yield _SetStatusCommand (_SetStatus .Intersection , command);
215
+ } else {
216
+ yield _SetStatusCommand (_SetStatus .Difference , command);
217
+ }
218
+ }
219
+ }
220
+
174
221
/// Given a build commands json file, and the files with local changes,
175
222
/// compute the lint commands to run.
176
223
@visibleForTesting
177
- Future <List <Command >> getLintCommandsForChangedFiles (
224
+ Future <List <Command >> getLintCommandsForFiles (
178
225
List <dynamic > buildCommandsData,
179
- List <io.File > changedFiles,
226
+ List <io.File > files,
227
+ List <List <dynamic >> sharedBuildCommandsData,
228
+ int ? shardId,
180
229
) async {
181
- final List <Command > buildCommands = < Command > [];
182
- for (final dynamic data in buildCommandsData) {
183
- final Command command = Command .fromMap (data as Map <String , dynamic >);
184
- final LintAction lintAction = await command.lintAction;
185
- // Short-circuit the expensive containsAny call for the many third_party files.
186
- if (lintAction != LintAction .skipThirdParty && command.containsAny (changedFiles)) {
187
- buildCommands.add (command);
230
+ final List <Command > totalCommands = < Command > [];
231
+ if (sharedBuildCommandsData.isNotEmpty) {
232
+ final Iterable <Command > buildCommands = buildCommandsData
233
+ .map ((dynamic data) => Command .fromMap (data as Map <String , dynamic >));
234
+ final Iterable <List <Command >> shardBuildCommands =
235
+ sharedBuildCommandsData.map ((List <dynamic > list) => list
236
+ .map ((dynamic data) =>
237
+ Command .fromMap (data as Map <String , dynamic >))
238
+ .toList ());
239
+ final Iterable <_SetStatusCommand > intersectionResults =
240
+ _calcIntersection (buildCommands, shardBuildCommands);
241
+ totalCommands.addAll (intersectionResults
242
+ .where ((_SetStatusCommand element) =>
243
+ element.setStatus == _SetStatus .Difference )
244
+ .map ((_SetStatusCommand e) => e.command));
245
+ final List <Command > intersection = intersectionResults
246
+ .where ((_SetStatusCommand element) =>
247
+ element.setStatus == _SetStatus .Intersection )
248
+ .map ((_SetStatusCommand e) => e.command)
249
+ .toList ();
250
+ // Make sure to sort results, not sure if there is a defined order in the json file.
251
+ intersection.sort ((Command x, Command y) => x.filePath.compareTo (y.filePath));
252
+ totalCommands.addAll (
253
+ _takeShard (intersection, shardId! , 1 + shardBuildCommands.length));
254
+ } else {
255
+ totalCommands.addAll (buildCommandsData.map ((dynamic data) => Command .fromMap (data as Map <String , dynamic >)));
256
+ }
257
+ Stream <Command > filterCommands () async * {
258
+ for (final Command command in totalCommands) {
259
+ final LintAction lintAction = await command.lintAction;
260
+ // Short-circuit the expensive containsAny call for the many third_party files.
261
+ if (lintAction != LintAction .skipThirdParty && command.containsAny (files)) {
262
+ yield command;
263
+ }
188
264
}
189
265
}
190
- return buildCommands ;
266
+ return filterCommands (). toList () ;
191
267
}
192
268
193
269
Future <_ComputeJobsResult > _computeJobs (
0 commit comments