diff --git a/packages/flutter_tools/lib/src/base/build.dart b/packages/flutter_tools/lib/src/base/build.dart index 81aacb90609..ef00578ec10 100644 --- a/packages/flutter_tools/lib/src/base/build.dart +++ b/packages/flutter_tools/lib/src/base/build.dart @@ -7,7 +7,7 @@ import 'dart:convert' show JSON; import 'package:crypto/crypto.dart' show md5; import 'package:meta/meta.dart'; -import 'package:quiver/core.dart' show hash3; +import 'package:quiver/core.dart' show hash4; import '../artifacts.dart'; import '../build_info.dart'; @@ -62,7 +62,7 @@ class GenSnapshot { /// build step. This assumes that build outputs are strictly a product of the /// input files. class Checksum { - Checksum.fromFiles(SnapshotType type, Set inputPaths) { + Checksum.fromFiles(SnapshotType type, this._mainPath, Set inputPaths) { final Iterable files = inputPaths.map(fs.file); final Iterable missingInputs = files.where((File file) => !file.existsSync()); if (missingInputs.isNotEmpty) @@ -99,11 +99,16 @@ class Checksum { if (_targetPlatform == null) throw new ArgumentError('Target platform unspecified in checksum JSON'); + _mainPath = content['entrypoint']; + if (_mainPath == null) + throw new ArgumentError('Entrypoint unspecified in checksum JSON'); + _checksums = content['files']; if (_checksums == null) throw new ArgumentError('File checksums unspecified in checksum JSON'); } + String _mainPath; String _buildMode; String _targetPlatform; Map _checksums; @@ -111,6 +116,7 @@ class Checksum { String toJson() => JSON.encode({ 'version': FlutterVersion.instance.frameworkRevision, 'buildMode': _buildMode, + 'entrypoint': _mainPath, 'targetPlatform': _targetPlatform, 'files': _checksums, }); @@ -120,12 +126,13 @@ class Checksum { return other is Checksum && _buildMode == other._buildMode && _targetPlatform == other._targetPlatform && + _mainPath == other._mainPath && _checksums.length == other._checksums.length && _checksums.keys.every((String key) => _checksums[key] == other._checksums[key]); } @override - int get hashCode => hash3(_buildMode, _targetPlatform, _checksums); + int get hashCode => hash4(_buildMode, _targetPlatform, _mainPath, _checksums); } final RegExp _separatorExpr = new RegExp(r'([^\\]) '); @@ -238,7 +245,7 @@ class Snapshotter { final Checksum oldChecksum = new Checksum.fromJson(await checksumFile.readAsString()); final Set checksumPaths = await readDepfile(depfilePath) ..addAll([outputSnapshotPath, mainPath]); - final Checksum newChecksum = new Checksum.fromFiles(type, checksumPaths); + final Checksum newChecksum = new Checksum.fromFiles(type, mainPath, checksumPaths); return oldChecksum != newChecksum; } } catch (e, s) { @@ -252,7 +259,7 @@ class Snapshotter { try { final Set checksumPaths = await readDepfile(depfilePath) ..addAll([outputSnapshotPath, mainPath]); - final Checksum checksum = new Checksum.fromFiles(type, checksumPaths); + final Checksum checksum = new Checksum.fromFiles(type, mainPath, checksumPaths); await fs.file(checksumsPath).writeAsString(checksum.toJson()); } catch (e, s) { // Log exception and continue, this step is a performance improvement only. diff --git a/packages/flutter_tools/lib/src/commands/build_aot.dart b/packages/flutter_tools/lib/src/commands/build_aot.dart index 4409374dfb9..215d4d58d36 100644 --- a/packages/flutter_tools/lib/src/commands/build_aot.dart +++ b/packages/flutter_tools/lib/src/commands/build_aot.dart @@ -297,7 +297,7 @@ Future _buildAotSnapshot( final Set snapshotInputPaths = await readDepfile(dependencies) ..add(mainPath) ..addAll(outputPaths); - final Checksum newChecksum = new Checksum.fromFiles(snapshotType, snapshotInputPaths); + final Checksum newChecksum = new Checksum.fromFiles(snapshotType, mainPath, snapshotInputPaths); if (oldChecksum == newChecksum) { printStatus('Skipping AOT snapshot build. Checksums match.'); return outputPath; @@ -375,7 +375,7 @@ Future _buildAotSnapshot( final Set snapshotInputPaths = await readDepfile(dependencies) ..add(mainPath) ..addAll(outputPaths); - final Checksum checksum = new Checksum.fromFiles(snapshotType, snapshotInputPaths); + final Checksum checksum = new Checksum.fromFiles(snapshotType, mainPath, snapshotInputPaths); await checksumFile.writeAsString(checksum.toJson()); } catch (e, s) { // Log exception and continue, this step is a performance improvement only. diff --git a/packages/flutter_tools/test/base/build_test.dart b/packages/flutter_tools/test/base/build_test.dart index de3e28ce342..e8788784ebb 100644 --- a/packages/flutter_tools/test/base/build_test.dart +++ b/packages/flutter_tools/test/base/build_test.dart @@ -75,7 +75,7 @@ void main() { await fs.file('a.dart').create(); const SnapshotType snapshotType = const SnapshotType(TargetPlatform.ios, BuildMode.debug); expect( - () => new Checksum.fromFiles(snapshotType, ['a.dart', 'b.dart'].toSet()), + () => new Checksum.fromFiles(snapshotType, 'a.dart', ['a.dart', 'b.dart'].toSet()), throwsA(anything), ); }, overrides: { FileSystem: () => fs }); @@ -84,7 +84,7 @@ void main() { await fs.file('a.dart').create(); const SnapshotType snapshotType = const SnapshotType(TargetPlatform.ios, null); expect( - () => new Checksum.fromFiles(snapshotType, ['a.dart', 'b.dart'].toSet()), + () => new Checksum.fromFiles(snapshotType, 'a.dart', ['a.dart', 'b.dart'].toSet()), throwsA(anything), ); }, overrides: { FileSystem: () => fs }); @@ -93,7 +93,7 @@ void main() { await fs.file('a.dart').create(); const SnapshotType snapshotType = const SnapshotType(null, BuildMode.debug); expect( - new Checksum.fromFiles(snapshotType, ['a.dart'].toSet()), + new Checksum.fromFiles(snapshotType, 'a.dart', ['a.dart'].toSet()), isNotNull, ); }, overrides: { FileSystem: () => fs }); @@ -102,13 +102,14 @@ void main() { await fs.file('a.dart').writeAsString('This is a'); await fs.file('b.dart').writeAsString('This is b'); const SnapshotType snapshotType = const SnapshotType(TargetPlatform.ios, BuildMode.debug); - final Checksum checksum = new Checksum.fromFiles(snapshotType, ['a.dart', 'b.dart'].toSet()); + final Checksum checksum = new Checksum.fromFiles(snapshotType, 'a.dart', ['a.dart', 'b.dart'].toSet()); final Map json = JSON.decode(checksum.toJson()); - expect(json, hasLength(4)); + expect(json, hasLength(5)); expect(json['version'], mockVersion.frameworkRevision); expect(json['buildMode'], BuildMode.debug.toString()); expect(json['targetPlatform'], TargetPlatform.ios.toString()); + expect(json['entrypoint'], 'a.dart'); expect(json['files'], hasLength(2)); expect(json['files']['a.dart'], '8a21a15fad560b799f6731d436c1b698'); expect(json['files']['b.dart'], '6f144e08b58cd0925328610fad7ac07c'); @@ -130,6 +131,7 @@ void main() { 'version': kVersion, 'buildMode': BuildMode.release.toString(), 'targetPlatform': TargetPlatform.ios.toString(), + 'entrypoint': 'a.dart', 'files': { 'a.dart': '8a21a15fad560b799f6731d436c1b698', 'b.dart': '6f144e08b58cd0925328610fad7ac07c', @@ -138,10 +140,11 @@ void main() { final Checksum checksum = new Checksum.fromJson(json); final Map content = JSON.decode(checksum.toJson()); - expect(content, hasLength(4)); + expect(content, hasLength(5)); expect(content['version'], mockVersion.frameworkRevision); expect(content['buildMode'], BuildMode.release.toString()); expect(content['targetPlatform'], TargetPlatform.ios.toString()); + expect(content['entrypoint'], 'a.dart'); expect(content['files']['a.dart'], '8a21a15fad560b799f6731d436c1b698'); expect(content['files']['b.dart'], '6f144e08b58cd0925328610fad7ac07c'); }, overrides: { @@ -152,6 +155,8 @@ void main() { final String json = JSON.encode({ 'version': 'bad', 'buildMode': BuildMode.release.toString(), + 'targetPlatform': TargetPlatform.ios.toString(), + 'entrypoint': 'a.dart', 'files': { 'a.dart': '8a21a15fad560b799f6731d436c1b698', 'b.dart': '6f144e08b58cd0925328610fad7ac07c', @@ -169,6 +174,7 @@ void main() { 'version': kVersion, 'buildMode': BuildMode.debug.toString(), 'targetPlatform': TargetPlatform.ios.toString(), + 'entrypoint': 'a.dart', 'files': { 'a.dart': '8a21a15fad560b799f6731d436c1b698', 'b.dart': '6f144e08b58cd0925328610fad7ac07c', @@ -186,6 +192,7 @@ void main() { 'version': kVersion, 'buildMode': BuildMode.debug.toString(), 'targetPlatform': TargetPlatform.ios.toString(), + 'entrypoint': 'a.dart', 'files': { 'a.dart': '8a21a15fad560b799f6731d436c1b698', 'b.dart': '6f144e08b58cd0925328610fad7ac07c', @@ -198,11 +205,30 @@ void main() { FlutterVersion: () => mockVersion, }); + testUsingContext('reports not equal if entrypoints do not match', () async { + final Map a = { + 'version': kVersion, + 'buildMode': BuildMode.debug.toString(), + 'targetPlatform': TargetPlatform.ios.toString(), + 'entrypoint': 'a.dart', + 'files': { + 'a.dart': '8a21a15fad560b799f6731d436c1b698', + 'b.dart': '6f144e08b58cd0925328610fad7ac07c', + }, + }; + final Map b = new Map.from(a); + b['entrypoint'] = 'b.dart'; + expect(new Checksum.fromJson(JSON.encode(a)) == new Checksum.fromJson(JSON.encode(b)), isFalse); + }, overrides: { + FlutterVersion: () => mockVersion, + }); + testUsingContext('reports not equal if checksums do not match', () async { final Map a = { 'version': kVersion, 'buildMode': BuildMode.debug.toString(), 'targetPlatform': TargetPlatform.ios.toString(), + 'entrypoint': 'a.dart', 'files': { 'a.dart': '8a21a15fad560b799f6731d436c1b698', 'b.dart': '6f144e08b58cd0925328610fad7ac07c', @@ -223,6 +249,7 @@ void main() { 'version': kVersion, 'buildMode': BuildMode.debug.toString(), 'targetPlatform': TargetPlatform.ios.toString(), + 'entrypoint': 'a.dart', 'files': { 'a.dart': '8a21a15fad560b799f6731d436c1b698', 'b.dart': '6f144e08b58cd0925328610fad7ac07c', @@ -243,6 +270,7 @@ void main() { 'version': kVersion, 'buildMode': BuildMode.debug.toString(), 'targetPlatform': TargetPlatform.ios.toString(), + 'entrypoint': 'a.dart', 'files': { 'a.dart': '8a21a15fad560b799f6731d436c1b698', 'b.dart': '6f144e08b58cd0925328610fad7ac07c', @@ -398,12 +426,12 @@ void main() { final _FakeGenSnapshot genSnapshot = new _FakeGenSnapshot( snapshotPath: 'output.snapshot', depfilePath: 'output.snapshot.d', - depfileContent: 'output.snapshot : other.dart', + depfileContent: 'output.snapshot : main.dart other.dart', ); context.setVariable(GenSnapshot, genSnapshot); - await fs.file('main.dart').writeAsString('void main() {}'); - await fs.file('other.dart').writeAsString('void main() { print("Kanpai ima kimi wa jinsei no ookina ookina butai ni tachi"); }'); + await fs.file('main.dart').writeAsString('import "other.dart";\nvoid main() {}'); + await fs.file('other.dart').writeAsString('import "main.dart";\nvoid main() {}'); await fs.file('output.snapshot').create(); await fs.file('output.snapshot.d').writeAsString('output.snapshot : main.dart'); await fs.file('output.snapshot.d.checksums').writeAsString(JSON.encode({ @@ -411,7 +439,8 @@ void main() { 'buildMode': BuildMode.debug.toString(), 'targetPlatform': '', 'files': { - 'main.dart': '27f5ebf0f8c559b2af9419d190299a5e', + 'main.dart': 'bc096b33f14dde5e0ffaf93a1d03395c', + 'other.dart': 'e0c35f083f0ad76b2d87100ec678b516', 'output.snapshot': 'd41d8cd98f00b204e9800998ecf8427e', }, })); @@ -424,8 +453,9 @@ void main() { expect(genSnapshot.callCount, 1); final Map json = JSON.decode(await fs.file('output.snapshot.d.checksums').readAsString()); - expect(json['files'], hasLength(2)); - expect(json['files']['other.dart'], '3238d0ae341339b1731d3c2e195ad177'); + expect(json['files'], hasLength(3)); + expect(json['files']['main.dart'], 'bc096b33f14dde5e0ffaf93a1d03395c'); + expect(json['files']['other.dart'], 'e0c35f083f0ad76b2d87100ec678b516'); expect(json['files']['output.snapshot'], 'd41d8cd98f00b204e9800998ecf8427e'); }, overrides: { FileSystem: () => fs, @@ -440,6 +470,7 @@ void main() { 'version': '$kVersion', 'buildMode': BuildMode.debug.toString(), 'targetPlatform': '', + 'entrypoint': 'main.dart', 'files': { 'main.dart': '27f5ebf0f8c559b2af9419d190299a5e', 'output.snapshot': 'd41d8cd98f00b204e9800998ecf8427e',