Unverified Commit a42c5679 authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

[flutter_tools] shrink fingerprinter API to currently used subset (#63840)

parent 39c735f4
...@@ -6,59 +6,46 @@ import 'package:crypto/crypto.dart' show md5; ...@@ -6,59 +6,46 @@ import 'package:crypto/crypto.dart' show md5;
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
import '../convert.dart' show json; import '../convert.dart' show json;
import '../globals.dart' as globals;
import 'file_system.dart'; import 'file_system.dart';
import 'logger.dart';
import 'utils.dart'; import 'utils.dart';
typedef FingerprintPathFilter = bool Function(String path);
/// A tool that can be used to compute, compare, and write [Fingerprint]s for a /// A tool that can be used to compute, compare, and write [Fingerprint]s for a
/// set of input files and associated build settings. /// set of input files and associated build settings.
/// ///
/// This class can be used during build actions to compute a fingerprint of the /// This class should only be used in situations where `assemble` is not appropriate,
/// build action inputs and options, and if unchanged from the previous build, /// such as checking if Cocoapods should be run.
/// skip the build step. This assumes that build outputs are strictly a product
/// of the fingerprint inputs.
class Fingerprinter { class Fingerprinter {
Fingerprinter({ Fingerprinter({
@required this.fingerprintPath, @required this.fingerprintPath,
@required Iterable<String> paths, @required Iterable<String> paths,
@required Map<String, String> properties, @required FileSystem fileSystem,
Iterable<String> depfilePaths = const <String>[], @required Logger logger,
FingerprintPathFilter pathFilter,
}) : _paths = paths.toList(), }) : _paths = paths.toList(),
_properties = Map<String, String>.of(properties),
_depfilePaths = depfilePaths.toList(),
_pathFilter = pathFilter,
assert(fingerprintPath != null), assert(fingerprintPath != null),
assert(paths != null && paths.every((String path) => path != null)), assert(paths != null && paths.every((String path) => path != null)),
assert(properties != null), _logger = logger,
assert(depfilePaths != null && depfilePaths.every((String path) => path != null)); _fileSystem = fileSystem;
final String fingerprintPath; final String fingerprintPath;
final List<String> _paths; final List<String> _paths;
final Map<String, String> _properties; final Logger _logger;
final List<String> _depfilePaths; final FileSystem _fileSystem;
final FingerprintPathFilter _pathFilter;
Fingerprint buildFingerprint() { Fingerprint buildFingerprint() {
final List<String> paths = _getPaths(); final List<String> paths = _getPaths();
return Fingerprint.fromBuildInputs(_properties, paths); return Fingerprint.fromBuildInputs(paths, _fileSystem);
} }
bool doesFingerprintMatch() { bool doesFingerprintMatch() {
try { try {
final File fingerprintFile = globals.fs.file(fingerprintPath); final File fingerprintFile = _fileSystem.file(fingerprintPath);
if (!fingerprintFile.existsSync()) { if (!fingerprintFile.existsSync()) {
return false; return false;
} }
if (!_depfilePaths.every(globals.fs.isFileSync)) {
return false;
}
final List<String> paths = _getPaths(); final List<String> paths = _getPaths();
if (!paths.every(globals.fs.isFileSync)) { if (!paths.every(_fileSystem.isFileSync)) {
return false; return false;
} }
...@@ -67,7 +54,7 @@ class Fingerprinter { ...@@ -67,7 +54,7 @@ class Fingerprinter {
return oldFingerprint == newFingerprint; return oldFingerprint == newFingerprint;
} on Exception catch (e) { } on Exception catch (e) {
// Log exception and continue, fingerprinting is only a performance improvement. // Log exception and continue, fingerprinting is only a performance improvement.
globals.printTrace('Fingerprint check error: $e'); _logger.printTrace('Fingerprint check error: $e');
} }
return false; return false;
} }
...@@ -75,22 +62,14 @@ class Fingerprinter { ...@@ -75,22 +62,14 @@ class Fingerprinter {
void writeFingerprint() { void writeFingerprint() {
try { try {
final Fingerprint fingerprint = buildFingerprint(); final Fingerprint fingerprint = buildFingerprint();
globals.fs.file(fingerprintPath).writeAsStringSync(fingerprint.toJson()); _fileSystem.file(fingerprintPath).writeAsStringSync(fingerprint.toJson());
} on Exception catch (e) { } on Exception catch (e) {
// Log exception and continue, fingerprinting is only a performance improvement. // Log exception and continue, fingerprinting is only a performance improvement.
globals.printTrace('Fingerprint write error: $e'); _logger.printTrace('Fingerprint write error: $e');
} }
} }
List<String> _getPaths() { List<String> _getPaths() => _paths;
final Set<String> paths = <String>{
..._paths,
for (final String depfilePath in _depfilePaths)
...readDepfile(depfilePath),
};
final FingerprintPathFilter filter = _pathFilter ?? (String path) => true;
return paths.where(filter).toList()..sort();
}
} }
/// A fingerprint that uniquely identifies a set of build input files and /// A fingerprint that uniquely identifies a set of build input files and
...@@ -101,23 +80,19 @@ class Fingerprinter { ...@@ -101,23 +80,19 @@ class Fingerprinter {
class Fingerprint { class Fingerprint {
const Fingerprint._({ const Fingerprint._({
Map<String, String> checksums, Map<String, String> checksums,
Map<String, String> properties, }) : _checksums = checksums;
}) : _checksums = checksums,
_properties = properties;
factory Fingerprint.fromBuildInputs(Map<String, String> properties, Iterable<String> inputPaths) { factory Fingerprint.fromBuildInputs(Iterable<String> inputPaths, FileSystem fileSystem) {
final Iterable<File> files = inputPaths.map<File>(globals.fs.file); final Iterable<File> files = inputPaths.map<File>(fileSystem.file);
final Iterable<File> missingInputs = files.where((File file) => !file.existsSync()); final Iterable<File> missingInputs = files.where((File file) => !file.existsSync());
if (missingInputs.isNotEmpty) { if (missingInputs.isNotEmpty) {
throw Exception('Missing input files:\n' + missingInputs.join('\n')); throw Exception('Missing input files:\n' + missingInputs.join('\n'));
} }
return Fingerprint._( return Fingerprint._(
// ignore: prefer_const_literals_to_create_immutables, https://github.com/dart-lang/linter/issues/2025
checksums: <String, String>{ checksums: <String, String>{
for (final File file in files) for (final File file in files)
file.path: md5.convert(file.readAsBytesSync()).toString(), file.path: md5.convert(file.readAsBytesSync()).toString(),
}, },
properties: <String, String>{...properties},
); );
} }
...@@ -127,37 +102,21 @@ class Fingerprint { ...@@ -127,37 +102,21 @@ class Fingerprint {
/// serializing framework and this framework. /// serializing framework and this framework.
factory Fingerprint.fromJson(String jsonData) { factory Fingerprint.fromJson(String jsonData) {
final Map<String, dynamic> content = castStringKeyedMap(json.decode(jsonData)); final Map<String, dynamic> content = castStringKeyedMap(json.decode(jsonData));
final String version = content['version'] as String;
if (version != globals.flutterVersion.frameworkRevision) {
throw Exception('Incompatible fingerprint version: $version');
}
return Fingerprint._( return Fingerprint._(
checksums: castStringKeyedMap(content['files'])?.cast<String,String>() ?? <String, String>{}, checksums: castStringKeyedMap(content['files'])?.cast<String,String>() ?? <String, String>{},
properties: castStringKeyedMap(content['properties'])?.cast<String,String>() ?? <String, String>{},
); );
} }
final Map<String, String> _checksums; final Map<String, String> _checksums;
final Map<String, String> _properties;
String toJson() => json.encode(<String, dynamic>{ String toJson() => json.encode(<String, dynamic>{
'version': globals.flutterVersion.frameworkRevision,
'properties': _properties,
'files': _checksums, 'files': _checksums,
}); });
@override @override
bool operator==(Object other) { bool operator==(Object other) {
if (identical(other, this)) {
return true;
}
if (other.runtimeType != runtimeType) {
return false;
}
return other is Fingerprint return other is Fingerprint
&& _equalMaps(other._checksums, _checksums) && _equalMaps(other._checksums, _checksums);
&& _equalMaps(other._properties, _properties);
} }
bool _equalMaps(Map<String, String> a, Map<String, String> b) { bool _equalMaps(Map<String, String> a, Map<String, String> b) {
...@@ -169,37 +128,8 @@ class Fingerprint { ...@@ -169,37 +128,8 @@ class Fingerprint {
// Ignore map entries here to avoid becoming inconsistent with equals // Ignore map entries here to avoid becoming inconsistent with equals
// due to differences in map entry order. This is a really bad hash // due to differences in map entry order. This is a really bad hash
// function and should eventually be deprecated and removed. // function and should eventually be deprecated and removed.
int get hashCode => _properties.length + _checksums.length; int get hashCode => _checksums.length.hashCode;
@override @override
String toString() => '{checksums: $_checksums, properties: $_properties}'; String toString() => '{checksums: $_checksums}';
}
final RegExp _separatorExpr = RegExp(r'([^\\]) ');
final RegExp _escapeExpr = RegExp(r'\\(.)');
/// Parses a VM snapshot dependency file.
///
/// Snapshot dependency files are a single line mapping the output snapshot to a
/// space-separated list of input files used to generate that output. Spaces and
/// backslashes are escaped with a backslash. For example:
///
/// outfile : file1.dart fil\\e2.dart fil\ e3.dart
///
/// will return a set containing: 'file1.dart', 'fil\e2.dart', 'fil e3.dart'.
Set<String> readDepfile(String depfilePath) {
// Depfile format:
// outfile1 outfile2 : file1.dart file2.dart file3.dart
final String contents = globals.fs.file(depfilePath).readAsStringSync();
final List<String> dependencies = contents.split(': ');
if (dependencies.length < 2) {
throw Exception('malformed depfile');
}
return dependencies[1]
.replaceAllMapped(_separatorExpr, (Match match) => '${match.group(1)}\n')
.split('\n')
.map<String>((String path) => path.replaceAllMapped(_escapeExpr, (Match match) => match.group(1)).trim())
.where((String path) => path.isNotEmpty)
.toSet();
} }
...@@ -13,7 +13,11 @@ import '../project.dart'; ...@@ -13,7 +13,11 @@ import '../project.dart';
/// For a given build, determines whether dependencies have changed since the /// For a given build, determines whether dependencies have changed since the
/// last call to processPods, then calls processPods with that information. /// last call to processPods, then calls processPods with that information.
Future<void> processPodsIfNeeded(XcodeBasedProject xcodeProject, String buildDirectory, BuildMode buildMode) async { Future<void> processPodsIfNeeded(
XcodeBasedProject xcodeProject,
String buildDirectory,
BuildMode buildMode,
) async {
final FlutterProject project = xcodeProject.parent; final FlutterProject project = xcodeProject.parent;
// Ensure that the plugin list is up to date, since hasPlugins relies on it. // Ensure that the plugin list is up to date, since hasPlugins relies on it.
await refreshPluginsList(project); await refreshPluginsList(project);
...@@ -29,7 +33,8 @@ Future<void> processPodsIfNeeded(XcodeBasedProject xcodeProject, String buildDir ...@@ -29,7 +33,8 @@ Future<void> processPodsIfNeeded(XcodeBasedProject xcodeProject, String buildDir
xcodeProject.podfile.path, xcodeProject.podfile.path,
xcodeProject.generatedXcodePropertiesFile.path, xcodeProject.generatedXcodePropertiesFile.path,
], ],
properties: <String, String>{}, fileSystem: globals.fs,
logger: globals.logger,
); );
final bool didPodInstall = await globals.cocoaPods.processPods( final bool didPodInstall = await globals.cocoaPods.processPods(
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment