From 32474dca2002fcd84ba4f4ca9d29f9b0f32fef2f Mon Sep 17 00:00:00 2001 From: Jonah Williams Date: Thu, 11 Mar 2021 15:53:53 -0800 Subject: [PATCH] [flutter_tools] provide more context for build_system invalidation (#77961) --- .../lib/src/build_system/build_system.dart | 53 ++++++++++++++++--- .../build_system/invalidated_reason_test.dart | 30 +++++++++++ 2 files changed, 75 insertions(+), 8 deletions(-) create mode 100644 packages/flutter_tools/test/general.shard/build_system/invalidated_reason_test.dart diff --git a/packages/flutter_tools/lib/src/build_system/build_system.dart b/packages/flutter_tools/lib/src/build_system/build_system.dart index a476b8d277..caeb20e6fb 100644 --- a/packages/flutter_tools/lib/src/build_system/build_system.dart +++ b/packages/flutter_tools/lib/src/build_system/build_system.dart @@ -1027,12 +1027,16 @@ class Node { /// One or more reasons why a task was invalidated. /// /// May be empty if the task was skipped. - final Set invalidatedReasons = {}; + final Map invalidatedReasons = {}; /// Whether this node needs an action performed. bool get dirty => _dirty; bool _dirty = false; + InvalidatedReason _invalidate(InvalidatedReasonKind kind) { + return invalidatedReasons[kind] ??= InvalidatedReason(kind); + } + /// Collect hashes for all inputs to determine if any have changed. /// /// Returns whether this target can be skipped. @@ -1060,7 +1064,8 @@ class Node { if (fileStore.currentAssetKeys.containsKey(absolutePath)) { final String currentHash = fileStore.currentAssetKeys[absolutePath]; if (currentHash != previousAssetKey) { - invalidatedReasons.add(InvalidatedReason.inputChanged); + final InvalidatedReason reason = _invalidate(InvalidatedReasonKind.inputChanged); + reason.data.add(absolutePath); _dirty = true; } } else { @@ -1074,13 +1079,15 @@ class Node { // output paths changed. if (!currentOutputPaths.contains(previousOutput)) { _dirty = true; - invalidatedReasons.add(InvalidatedReason.outputSetChanged); + final InvalidatedReason reason = _invalidate(InvalidatedReasonKind.outputSetChanged); + reason.data.add(previousOutput); // if this isn't a current output file there is no reason to compute the key. continue; } final File file = fileSystem.file(previousOutput); if (!file.existsSync()) { - invalidatedReasons.add(InvalidatedReason.outputMissing); + final InvalidatedReason reason = _invalidate(InvalidatedReasonKind.outputMissing); + reason.data.add(file.path); _dirty = true; continue; } @@ -1089,7 +1096,8 @@ class Node { if (fileStore.currentAssetKeys.containsKey(absolutePath)) { final String currentHash = fileStore.currentAssetKeys[absolutePath]; if (currentHash != previousHash) { - invalidatedReasons.add(InvalidatedReason.outputChanged); + final InvalidatedReason reason = _invalidate(InvalidatedReasonKind.outputChanged); + reason.data.add(absolutePath); _dirty = true; } } else { @@ -1104,7 +1112,8 @@ class Node { _dirty = true; final String missingMessage = missingInputs.map((File file) => file.path).join(', '); logger.printTrace('invalidated build due to missing files: $missingMessage'); - invalidatedReasons.add(InvalidatedReason.inputMissing); + final InvalidatedReason reason = _invalidate(InvalidatedReasonKind.inputMissing); + reason.data.addAll(missingInputs.map((File file) => file.path)); } // If we have files to diff, compute them asynchronously and then @@ -1112,7 +1121,8 @@ class Node { if (sourcesToDiff.isNotEmpty) { final List dirty = fileStore.diffFileList(sourcesToDiff); if (dirty.isNotEmpty) { - invalidatedReasons.add(InvalidatedReason.inputChanged); + final InvalidatedReason reason = _invalidate(InvalidatedReasonKind.inputChanged); + reason.data.addAll(dirty.map((File file) => file.path)); _dirty = true; } } @@ -1120,8 +1130,35 @@ class Node { } } +/// Data about why a target was re-run. +class InvalidatedReason { + InvalidatedReason(this.kind); + + final InvalidatedReasonKind kind; + /// Absolute file paths of inputs or outputs, depending on [kind]. + final List data = []; + + @override + String toString() { + switch (kind) { + case InvalidatedReasonKind.inputMissing: + return 'The following inputs were missing: ${data.join(',')}'; + case InvalidatedReasonKind.inputChanged: + return 'The following inputs have updated contents: ${data.join(',')}'; + case InvalidatedReasonKind.outputChanged: + return 'The following outputs have updated contents: ${data.join(',')}'; + case InvalidatedReasonKind.outputMissing: + return 'The following outputs were missing: ${data.join(',')}'; + case InvalidatedReasonKind.outputSetChanged: + return 'The following outputs were removed from the output set: ${data.join(',')}'; + } + assert(false); + return null; + } +} + /// A description of why a target was rerun. -enum InvalidatedReason { +enum InvalidatedReasonKind { /// An input file that was expected is missing. This can occur when using /// depfile dependencies, or if a target is incorrectly specified. inputMissing, diff --git a/packages/flutter_tools/test/general.shard/build_system/invalidated_reason_test.dart b/packages/flutter_tools/test/general.shard/build_system/invalidated_reason_test.dart new file mode 100644 index 0000000000..312ea8f8c2 --- /dev/null +++ b/packages/flutter_tools/test/general.shard/build_system/invalidated_reason_test.dart @@ -0,0 +1,30 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// @dart = 2.8 + +import 'package:flutter_tools/src/build_system/build_system.dart'; + +import '../../src/common.dart'; + +void main() { + testWithoutContext('InvalidatedReason formats message per invalidation kind', () { + final InvalidatedReason inputChanged = InvalidatedReason(InvalidatedReasonKind.inputChanged) + ..data.add('a.dart'); + final InvalidatedReason outputChanged = InvalidatedReason(InvalidatedReasonKind.outputChanged) + ..data.add('b.dart'); + final InvalidatedReason inputMissing = InvalidatedReason(InvalidatedReasonKind.inputMissing) + ..data.add('c.dart'); + final InvalidatedReason outputMissing = InvalidatedReason(InvalidatedReasonKind.outputMissing) + ..data.add('d.dart'); + final InvalidatedReason outputSetChanged = InvalidatedReason(InvalidatedReasonKind.outputSetChanged) + ..data.add('e.dart'); + + expect(inputChanged.toString(), 'The following inputs have updated contents: a.dart'); + expect(outputChanged.toString(), 'The following outputs have updated contents: b.dart'); + expect(inputMissing.toString(), 'The following inputs were missing: c.dart'); + expect(outputMissing.toString(), 'The following outputs were missing: d.dart'); + expect(outputSetChanged.toString(), 'The following outputs were removed from the output set: e.dart'); + }); +}