25
25
import com .google .common .collect .ImmutableMap ;
26
26
import com .google .common .collect .ImmutableSet ;
27
27
import com .google .common .collect .Iterables ;
28
+ import com .google .common .collect .Maps ;
28
29
import com .google .common .collect .Sets ;
29
30
import com .google .devtools .build .lib .actions .ActionAnalysisMetadata ;
30
31
import com .google .devtools .build .lib .actions .ActionLookupKey ;
59
60
import com .google .devtools .build .lib .skyframe .TopLevelStatusEvents .SomeExecutionStartedEvent ;
60
61
import com .google .devtools .build .lib .skyframe .TopLevelStatusEvents .TestAnalyzedEvent ;
61
62
import com .google .devtools .build .lib .skyframe .TopLevelStatusEvents .TopLevelEntityAnalysisConcludedEvent ;
63
+ import com .google .devtools .build .lib .skyframe .TopLevelStatusEvents .TopLevelStatusEventWithType ;
62
64
import com .google .devtools .build .lib .skyframe .TopLevelStatusEvents .TopLevelTargetAnalyzedEvent ;
63
65
import com .google .devtools .build .lib .skyframe .TopLevelStatusEvents .TopLevelTargetPendingExecutionEvent ;
64
66
import com .google .devtools .build .lib .skyframe .TopLevelStatusEvents .TopLevelTargetReadyForSymlinkPlanting ;
72
74
import com .google .devtools .build .skyframe .SkyframeLookupResult ;
73
75
import java .util .ArrayList ;
74
76
import java .util .Collections ;
77
+ import java .util .HashSet ;
75
78
import java .util .List ;
79
+ import java .util .Map ;
76
80
import java .util .Set ;
77
81
import javax .annotation .Nullable ;
78
82
@@ -96,6 +100,18 @@ public class BuildDriverFunction implements SkyFunction {
96
100
// shutting down of the Executors could lead to a RejectedExecutionException.
97
101
private Set <BuildDriverKey > checkedForConflicts = Sets .newConcurrentHashSet ();
98
102
103
+ // Events coming from Skyframe may contain duplicates (because of resets). It would be better to
104
+ // de-duplicate at the source to avoid repeated work by each subscriber.
105
+ //
106
+ // Each top level key has at most 1 effective status event, e.g. a top level target can't be
107
+ // analyzed twice in a build. Therefore, to keep track of the posted events, we only need to keep
108
+ // the sent event types instead of the events themselves.
109
+ //
110
+ // We didn't use SkyKeyComputeState since it should only be used as a performance optimization,
111
+ // whereas in this situation the state determines the behavior of the SkyFunction.
112
+ private Map <BuildDriverKey , Set <TopLevelStatusEvents .Type >> keyToPostedEvents =
113
+ Maps .newConcurrentMap ();
114
+
99
115
BuildDriverFunction (
100
116
TransitiveActionLookupValuesHelper transitiveActionLookupValuesHelper ,
101
117
Supplier <IncrementalArtifactConflictFinder > incrementalArtifactConflictFinder ,
@@ -160,6 +176,9 @@ public SkyValue compute(SkyKey skyKey, Environment env)
160
176
}
161
177
}
162
178
179
+ Set <TopLevelStatusEvents .Type > postedEventsTypes =
180
+ keyToPostedEvents .computeIfAbsent (buildDriverKey , (unused ) -> new HashSet <>());
181
+
163
182
Preconditions .checkState (
164
183
topLevelSkyValue instanceof ConfiguredTargetValue
165
184
|| topLevelSkyValue instanceof TopLevelAspectsValue );
@@ -170,7 +189,18 @@ public SkyValue compute(SkyKey skyKey, Environment env)
170
189
// before the TopLevelEntityAnalysisConcludedEvent: when the last of the analysis work is
171
190
// concluded, we need to have the *complete* list of analyzed targets ready in
172
191
// BuildResultListener.
173
- postTopLevelTargetAnalyzedEvent (env , configuredTargetValue , configuredTarget );
192
+ postEventIfNecessary (
193
+ postedEventsTypes , env , TopLevelTargetAnalyzedEvent .create (configuredTarget ));
194
+ // It's possible that this code path is triggered AFTER the analysis cache clean up and the
195
+ // transitive packages for package root resolution is already cleared. In such a case, the
196
+ // symlinks should have already been planted.
197
+ if (configuredTargetValue .getTransitivePackages () != null ) {
198
+ postEventIfNecessary (
199
+ postedEventsTypes ,
200
+ env ,
201
+ TopLevelTargetReadyForSymlinkPlanting .create (
202
+ configuredTargetValue .getTransitivePackages ()));
203
+ }
174
204
175
205
BuildConfigurationValue buildConfigurationValue =
176
206
configuredTarget .getConfigurationKey () == null
@@ -196,44 +226,56 @@ public SkyValue compute(SkyKey skyKey, Environment env)
196
226
197
227
state .checkedForCompatibility = true ;
198
228
if (!isConfiguredTargetCompatible ) {
199
- env .getListener ().post (TopLevelTargetSkippedEvent .create (configuredTarget ));
229
+ postEventIfNecessary (
230
+ postedEventsTypes , env , TopLevelTargetSkippedEvent .create (configuredTarget ));
200
231
// We still record analyzed but skipped tests, as this information is needed for the
201
232
// result summary.
202
233
if (!NOT_TEST .equals (buildDriverKey .getTestType ())) {
203
- env .getListener ()
204
- .post (
205
- TestAnalyzedEvent .create (
206
- configuredTarget ,
207
- Preconditions .checkNotNull (buildConfigurationValue ),
208
- /*isSkipped=*/ true ));
234
+ postEventIfNecessary (
235
+ postedEventsTypes ,
236
+ env ,
237
+ TestAnalyzedEvent .create (
238
+ configuredTarget ,
239
+ Preconditions .checkNotNull (buildConfigurationValue ),
240
+ /* isSkipped= */ true ));
209
241
}
210
242
// Only send the event now to include the compatibility check in the measurement for
211
243
// time spent on analysis work.
212
- env .getListener ().post (TopLevelEntityAnalysisConcludedEvent .success (buildDriverKey ));
244
+ postEventIfNecessary (
245
+ postedEventsTypes ,
246
+ env ,
247
+ TopLevelEntityAnalysisConcludedEvent .success (buildDriverKey ));
213
248
// We consider the evaluation of this BuildDriverKey successful at this point, even when
214
249
// the target is skipped.
250
+ removeStatesForKey (buildDriverKey );
215
251
return new BuildDriverValue (topLevelSkyValue , /*skipped=*/ true );
216
252
}
217
253
} catch (TargetCompatibilityCheckException e ) {
218
254
throw new BuildDriverFunctionException (e );
219
255
}
220
256
}
221
257
222
- env .getListener ().post (TopLevelEntityAnalysisConcludedEvent .success (buildDriverKey ));
223
- env .getListener ()
224
- .post (
225
- TopLevelTargetPendingExecutionEvent .create (
226
- configuredTarget , buildDriverKey .isTest ()));
258
+ postEventIfNecessary (
259
+ postedEventsTypes , env , TopLevelEntityAnalysisConcludedEvent .success (buildDriverKey ));
260
+ postEventIfNecessary (
261
+ postedEventsTypes ,
262
+ env ,
263
+ TopLevelTargetPendingExecutionEvent .create (configuredTarget , buildDriverKey .isTest ()));
227
264
requestConfiguredTargetExecution (
228
265
configuredTarget ,
229
266
buildDriverKey ,
230
267
actionLookupKey ,
231
268
buildConfigurationValue ,
232
269
env ,
233
- topLevelArtifactContext );
270
+ topLevelArtifactContext ,
271
+ postedEventsTypes );
234
272
} else {
235
273
announceAspectAnalysisDoneAndRequestExecution (
236
- buildDriverKey , (TopLevelAspectsValue ) topLevelSkyValue , env , topLevelArtifactContext );
274
+ buildDriverKey ,
275
+ (TopLevelAspectsValue ) topLevelSkyValue ,
276
+ env ,
277
+ topLevelArtifactContext ,
278
+ postedEventsTypes );
237
279
}
238
280
239
281
if (env .valuesMissing ()) {
@@ -245,33 +287,35 @@ public SkyValue compute(SkyKey skyKey, Environment env)
245
287
if (EXCLUSIVE .equals (buildDriverKey .getTestType ())
246
288
|| EXCLUSIVE_IF_LOCAL .equals (buildDriverKey .getTestType ())) {
247
289
Preconditions .checkState (topLevelSkyValue instanceof ConfiguredTargetValue );
290
+ removeStatesForKey (buildDriverKey );
248
291
return new ExclusiveTestBuildDriverValue (
249
292
topLevelSkyValue , ((ConfiguredTargetValue ) topLevelSkyValue ).getConfiguredTarget ());
250
293
}
251
294
295
+ removeStatesForKey (buildDriverKey );
252
296
return new BuildDriverValue (topLevelSkyValue , /*skipped=*/ false );
253
297
}
254
298
255
- public void resetActionConflictCheckingStatus () {
299
+ public void resetStates () {
256
300
checkedForConflicts = Sets .newConcurrentHashSet ();
301
+ keyToPostedEvents = Maps .newConcurrentMap ();
257
302
}
258
303
259
- private static void postTopLevelTargetAnalyzedEvent (
304
+ private void removeStatesForKey (BuildDriverKey key ) {
305
+ checkedForConflicts .remove (key );
306
+ keyToPostedEvents .remove (key );
307
+ }
308
+
309
+ private static void postEventIfNecessary (
310
+ Set <TopLevelStatusEvents .Type > postedEventsTypes ,
260
311
Environment env ,
261
- ConfiguredTargetValue configuredTargetValue ,
262
- ConfiguredTarget configuredTarget ) {
263
- env .getListener ().post (TopLevelTargetAnalyzedEvent .create (configuredTarget ));
264
- // It's possible that this code path is triggered AFTER the analysis cache clean up and the
265
- // transitive packages for package root resolution is already cleared. In such a case, the
266
- // symlinks should have already been planted.
267
- if (configuredTargetValue .getTransitivePackages () != null ) {
268
- env .getListener ()
269
- .post (
270
- TopLevelTargetReadyForSymlinkPlanting .create (
271
- configuredTargetValue .getTransitivePackages ()));
312
+ TopLevelStatusEventWithType event ) {
313
+ if (postedEventsTypes .add (event .getType ())) {
314
+ env .getListener ().post (event );
272
315
}
273
316
}
274
317
318
+
275
319
/**
276
320
* Checks if a ConfiguredTarget is compatible with the platform/environment. See {@link
277
321
* TopLevelConstraintSemantics}.
@@ -352,12 +396,13 @@ private void requestConfiguredTargetExecution(
352
396
ActionLookupKey actionLookupKey ,
353
397
BuildConfigurationValue buildConfigurationValue ,
354
398
Environment env ,
355
- TopLevelArtifactContext topLevelArtifactContext )
399
+ TopLevelArtifactContext topLevelArtifactContext ,
400
+ Set <TopLevelStatusEvents .Type > postedEventsTypes )
356
401
throws InterruptedException {
357
402
ImmutableSet .Builder <Artifact > artifactsToBuild = ImmutableSet .builder ();
358
403
addExtraActionsIfRequested (
359
404
configuredTarget .getProvider (ExtraActionArtifactsProvider .class ), artifactsToBuild );
360
- env . getListener (). post ( SomeExecutionStartedEvent .create ());
405
+ postEventIfNecessary ( postedEventsTypes , env , SomeExecutionStartedEvent .create ());
361
406
if (NOT_TEST .equals (buildDriverKey .getTestType ())) {
362
407
declareDependenciesAndCheckValues (
363
408
env ,
@@ -369,12 +414,13 @@ private void requestConfiguredTargetExecution(
369
414
return ;
370
415
}
371
416
372
- env .getListener ()
373
- .post (
374
- TestAnalyzedEvent .create (
375
- configuredTarget ,
376
- Preconditions .checkNotNull (buildConfigurationValue ),
377
- /*isSkipped=*/ false ));
417
+ postEventIfNecessary (
418
+ postedEventsTypes ,
419
+ env ,
420
+ TestAnalyzedEvent .create (
421
+ configuredTarget ,
422
+ Preconditions .checkNotNull (buildConfigurationValue ),
423
+ /* isSkipped= */ false ));
378
424
379
425
if (PARALLEL .equals (buildDriverKey .getTestType ())) {
380
426
// Only run non-exclusive tests here. Exclusive tests need to be run sequentially later.
@@ -398,12 +444,19 @@ private void announceAspectAnalysisDoneAndRequestExecution(
398
444
BuildDriverKey buildDriverKey ,
399
445
TopLevelAspectsValue topLevelAspectsValue ,
400
446
Environment env ,
401
- TopLevelArtifactContext topLevelArtifactContext )
447
+ TopLevelArtifactContext topLevelArtifactContext ,
448
+ Set <TopLevelStatusEvents .Type > postedEventsTypes )
402
449
throws InterruptedException {
403
450
404
- env . getListener (). post ( SomeExecutionStartedEvent .create ());
451
+ postEventIfNecessary ( postedEventsTypes , env , SomeExecutionStartedEvent .create ());
405
452
ImmutableSet .Builder <Artifact > artifactsToBuild = ImmutableSet .builder ();
406
453
List <SkyKey > aspectCompletionKeys = new ArrayList <>();
454
+
455
+ boolean symlinkPlantingEventsSent =
456
+ !postedEventsTypes .add (
457
+ TopLevelStatusEvents .Type .TOP_LEVEL_TARGET_READY_FOR_SYMLINK_PLANTING );
458
+ boolean aspectAnalyzedEventsSent =
459
+ !postedEventsTypes .add (TopLevelStatusEvents .Type .ASPECT_ANALYZED );
407
460
for (AspectValue aspectValue : topLevelAspectsValue .getTopLevelAspectsValues ()) {
408
461
AspectKey aspectKey = aspectValue .getKey ();
409
462
ConfiguredAspect configuredAspect = aspectValue .getConfiguredAspect ();
@@ -413,21 +466,25 @@ private void announceAspectAnalysisDoneAndRequestExecution(
413
466
// It's possible that this code path is triggered AFTER the analysis cache clean up and the
414
467
// transitive packages for package root resolution is already cleared. In such a case, the
415
468
// symlinks should have already been planted.
416
- if (aspectValue .getTransitivePackages () != null ) {
469
+ if (aspectValue .getTransitivePackages () != null && ! symlinkPlantingEventsSent ) {
417
470
env .getListener ()
418
471
.post (
419
472
TopLevelTargetReadyForSymlinkPlanting .create (aspectValue .getTransitivePackages ()));
420
473
}
421
- env .getListener ().post (AspectAnalyzedEvent .create (aspectKey , configuredAspect ));
474
+ if (!aspectAnalyzedEventsSent ) {
475
+ env .getListener ().post (AspectAnalyzedEvent .create (aspectKey , configuredAspect ));
476
+ }
422
477
423
478
aspectCompletionKeys .add (AspectCompletionKey .create (aspectKey , topLevelArtifactContext ));
424
479
}
480
+
425
481
// Send the AspectAnalyzedEvents first to make sure the BuildResultListener is up-to-date before
426
482
// signaling that the analysis of this top level aspect has concluded.
427
- env . getListener (). post ( TopLevelEntityAnalysisConcludedEvent . success ( buildDriverKey ));
428
-
483
+ postEventIfNecessary (
484
+ postedEventsTypes , env , TopLevelEntityAnalysisConcludedEvent . success ( buildDriverKey ));
429
485
declareDependenciesAndCheckValues (
430
486
env , Iterables .concat (Artifact .keys (artifactsToBuild .build ()), aspectCompletionKeys ));
487
+
431
488
}
432
489
433
490
/**
0 commit comments