blob: 325666ecd2c7b1002f04dd1a1a04a041326ed54a [file] [log] [blame]
Ian Hickson449f4a62019-11-27 15:04:02 -08001// Copyright 2014 The Flutter Authors. All rights reserved.
Alexander Aprelev391e91c2018-08-30 07:30:25 -07002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5import 'dart:async';
6import 'dart:convert';
Ian Hicksoneae05c72019-11-14 13:19:40 -08007import 'dart:core' hide print;
Ian Hicksoneae05c72019-11-14 13:19:40 -08008import 'dart:io' hide exit;
Dan Field24f39d42020-01-02 11:47:28 -08009import 'dart:typed_data';
Alexander Aprelev391e91c2018-08-30 07:30:25 -070010
Dan Field24f39d42020-01-02 11:47:28 -080011import 'package:crypto/crypto.dart';
Francisco Magdaleno04ea3182020-01-02 09:25:59 -080012import 'package:meta/meta.dart';
Dan Field24f39d42020-01-02 11:47:28 -080013import 'package:path/path.dart' as path;
Alexander Aprelev391e91c2018-08-30 07:30:25 -070014
15import 'run_command.dart';
Dan Field24f39d42020-01-02 11:47:28 -080016import 'utils.dart';
Alexander Aprelev391e91c2018-08-30 07:30:25 -070017
Alexander Aprelev391e91c2018-08-30 07:30:25 -070018final String flutterRoot = path.dirname(path.dirname(path.dirname(path.fromUri(Platform.script))));
19final String flutter = path.join(flutterRoot, 'bin', Platform.isWindows ? 'flutter.bat' : 'flutter');
20final String dart = path.join(flutterRoot, 'bin', 'cache', 'dart-sdk', 'bin', Platform.isWindows ? 'dart.exe' : 'dart');
21final String pub = path.join(flutterRoot, 'bin', 'cache', 'dart-sdk', 'bin', Platform.isWindows ? 'pub.bat' : 'pub');
22final String pubCache = path.join(flutterRoot, '.pub-cache');
23
24/// When you call this, you can pass additional arguments to pass custom
25/// arguments to flutter analyze. For example, you might want to call this
26/// script with the parameter --dart-sdk to use custom dart sdk.
27///
28/// For example:
29/// bin/cache/dart-sdk/bin/dart dev/bots/analyze.dart --dart-sdk=/tmp/dart-sdk
Ian Hicksoneae05c72019-11-14 13:19:40 -080030Future<void> main(List<String> arguments) async {
Ian Hickson449f4a62019-11-27 15:04:02 -080031 print('$clock STARTING ANALYSIS');
Ian Hicksoneae05c72019-11-14 13:19:40 -080032 try {
33 await run(arguments);
34 } on ExitException catch (error) {
35 error.apply();
36 }
Dan Field24f39d42020-01-02 11:47:28 -080037 print('$clock ${bold}Analysis successful.$reset');
Ian Hicksoneae05c72019-11-14 13:19:40 -080038}
39
40Future<void> run(List<String> arguments) async {
Ian Hickson58939b72019-02-12 12:29:36 -080041 bool assertsEnabled = false;
42 assert(() { assertsEnabled = true; return true; }());
43 if (!assertsEnabled) {
Dan Field24f39d42020-01-02 11:47:28 -080044 exitWithError(<String>['The analyze.dart script must be run with --enable-asserts.']);
Ian Hickson58939b72019-02-12 12:29:36 -080045 }
Alexander Aprelev391e91c2018-08-30 07:30:25 -070046
Dan Field3e634112020-01-14 16:43:01 -080047 print('$clock runtimeType in toString...');
48 await verifyNoRuntimeTypeInToString(flutterRoot);
49
Dan Field24f39d42020-01-02 11:47:28 -080050 print('$clock Unexpected binaries...');
51 await verifyNoBinaries(flutterRoot);
52
53 print('$clock Trailing spaces...');
54 await verifyNoTrailingSpaces(flutterRoot); // assumes no unexpected binaries, so should be after verifyNoBinaries
55
Ian Hickson449f4a62019-11-27 15:04:02 -080056 print('$clock Deprecations...');
Ian Hickson62e4ab82019-11-15 19:21:53 -080057 await verifyDeprecations(flutterRoot);
Ian Hickson449f4a62019-11-27 15:04:02 -080058
59 print('$clock Licenses...');
Ian Hicksoneae05c72019-11-14 13:19:40 -080060 await verifyNoMissingLicense(flutterRoot);
Ian Hickson449f4a62019-11-27 15:04:02 -080061
62 print('$clock Test imports...');
Ian Hicksoneae05c72019-11-14 13:19:40 -080063 await verifyNoTestImports(flutterRoot);
Ian Hickson449f4a62019-11-27 15:04:02 -080064
65 print('$clock Test package imports...');
Ian Hicksoneae05c72019-11-14 13:19:40 -080066 await verifyNoTestPackageImports(flutterRoot);
Ian Hickson449f4a62019-11-27 15:04:02 -080067
68 print('$clock Generated plugin registrants...');
Ian Hicksoneae05c72019-11-14 13:19:40 -080069 await verifyGeneratedPluginRegistrants(flutterRoot);
Ian Hickson449f4a62019-11-27 15:04:02 -080070
71 print('$clock Bad imports (framework)...');
Ian Hicksoneae05c72019-11-14 13:19:40 -080072 await verifyNoBadImportsInFlutter(flutterRoot);
Ian Hickson449f4a62019-11-27 15:04:02 -080073
74 print('$clock Bad imports (tools)...');
Ian Hicksoneae05c72019-11-14 13:19:40 -080075 await verifyNoBadImportsInFlutterTools(flutterRoot);
Ian Hickson449f4a62019-11-27 15:04:02 -080076
77 print('$clock Internationalization...');
Ian Hicksoneae05c72019-11-14 13:19:40 -080078 await verifyInternationalizations();
Ian Hickson449f4a62019-11-27 15:04:02 -080079
Alexander Aprelev391e91c2018-08-30 07:30:25 -070080 // Ensure that all package dependencies are in sync.
Ian Hickson449f4a62019-11-27 15:04:02 -080081 print('$clock Package dependencies...');
Alexander Aprelev391e91c2018-08-30 07:30:25 -070082 await runCommand(flutter, <String>['update-packages', '--verify-only'],
83 workingDirectory: flutterRoot,
84 );
85
Ian Hicksoneae05c72019-11-14 13:19:40 -080086 // Analyze all the Dart code in the repo.
Ian Hickson449f4a62019-11-27 15:04:02 -080087 print('$clock Dart analysis...');
Ian Hicksoneae05c72019-11-14 13:19:40 -080088 await _runFlutterAnalyze(flutterRoot, options: <String>[
89 '--flutter-repo',
90 ...arguments,
91 ]);
92
Alexander Aprelev391e91c2018-08-30 07:30:25 -070093 // Try with the --watch analyzer, to make sure it returns success also.
94 // The --benchmark argument exits after one run.
Ian Hickson449f4a62019-11-27 15:04:02 -080095 print('$clock Dart analysis (with --watch)...');
Ian Hicksoneae05c72019-11-14 13:19:40 -080096 await _runFlutterAnalyze(flutterRoot, options: <String>[
97 '--flutter-repo',
98 '--watch',
99 '--benchmark',
100 ...arguments,
101 ]);
Alexander Aprelev391e91c2018-08-30 07:30:25 -0700102
Dan Field24f39d42020-01-02 11:47:28 -0800103 // Analyze all the sample code in the repo
104 print('$clock Sample code...');
105 await runCommand(dart,
106 <String>[path.join(flutterRoot, 'dev', 'bots', 'analyze-sample-code.dart')],
107 workingDirectory: flutterRoot,
108 );
109
Alexander Aprelev391e91c2018-08-30 07:30:25 -0700110 // Try analysis against a big version of the gallery; generate into a temporary directory.
Ian Hickson449f4a62019-11-27 15:04:02 -0800111 print('$clock Dart analysis (mega gallery)...');
Alexander Aprelev391e91c2018-08-30 07:30:25 -0700112 final Directory outDir = Directory.systemTemp.createTempSync('flutter_mega_gallery.');
Alexander Aprelev391e91c2018-08-30 07:30:25 -0700113 try {
114 await runCommand(dart,
115 <String>[
Alexander Aprelev391e91c2018-08-30 07:30:25 -0700116 path.join(flutterRoot, 'dev', 'tools', 'mega_gallery.dart'),
117 '--out',
118 outDir.path,
119 ],
120 workingDirectory: flutterRoot,
121 );
Ian Hickson449f4a62019-11-27 15:04:02 -0800122 await _runFlutterAnalyze(outDir.path, options: <String>[
123 '--watch',
124 '--benchmark',
125 ...arguments,
126 ]);
Alexander Aprelev391e91c2018-08-30 07:30:25 -0700127 } finally {
128 outDir.deleteSync(recursive: true);
129 }
Alexander Aprelev391e91c2018-08-30 07:30:25 -0700130}
131
Ian Hickson62e4ab82019-11-15 19:21:53 -0800132
133// TESTS
134
135final RegExp _findDeprecationPattern = RegExp(r'@[Dd]eprecated');
136final RegExp _deprecationPattern1 = RegExp(r'^( *)@Deprecated\($'); // ignore: flutter_deprecation_syntax (see analyze.dart)
137final RegExp _deprecationPattern2 = RegExp(r"^ *'(.+) '$");
Michael Goderbauerf8cafd92019-11-20 12:45:04 -0800138final RegExp _deprecationPattern3 = RegExp(r"^ *'This feature was deprecated after v([0-9]+)\.([0-9]+)\.([0-9]+)\.'$");
Ian Hickson62e4ab82019-11-15 19:21:53 -0800139final RegExp _deprecationPattern4 = RegExp(r'^ *\)$');
140
141/// Some deprecation notices are special, for example they're used to annotate members that
142/// will never go away and were never allowed but which we are trying to show messages for.
143/// (One example would be a library that intentionally conflicts with a member in another
144/// library to indicate that it is incompatible with that other library. Another would be
145/// the regexp just above...)
146const String _ignoreDeprecation = ' // ignore: flutter_deprecation_syntax (see analyze.dart)';
147
148/// Some deprecation notices are grand-fathered in for now. They must have an issue listed.
149final RegExp _grandfatheredDeprecation = RegExp(r' // ignore: flutter_deprecation_syntax, https://github.com/flutter/flutter/issues/[0-9]+$');
150
Dan Field24f39d42020-01-02 11:47:28 -0800151Future<void> verifyDeprecations(String workingDirectory, { int minimumMatches = 2000 }) async {
Ian Hickson62e4ab82019-11-15 19:21:53 -0800152 final List<String> errors = <String>[];
Alexandre Ardhuin4f9b6cf2020-01-07 16:32:04 +0100153 for (final File file in _allFiles(workingDirectory, 'dart', minimumMatches: minimumMatches)) {
Ian Hickson62e4ab82019-11-15 19:21:53 -0800154 int lineNumber = 0;
155 final List<String> lines = file.readAsLinesSync();
156 final List<int> linesWithDeprecations = <int>[];
Alexandre Ardhuin4f9b6cf2020-01-07 16:32:04 +0100157 for (final String line in lines) {
Ian Hickson62e4ab82019-11-15 19:21:53 -0800158 if (line.contains(_findDeprecationPattern) &&
159 !line.endsWith(_ignoreDeprecation) &&
160 !line.contains(_grandfatheredDeprecation)) {
161 linesWithDeprecations.add(lineNumber);
162 }
163 lineNumber += 1;
164 }
165 for (int lineNumber in linesWithDeprecations) {
166 try {
167 final Match match1 = _deprecationPattern1.firstMatch(lines[lineNumber]);
168 if (match1 == null)
169 throw 'Deprecation notice does not match required pattern.';
170 final String indent = match1[1];
171 lineNumber += 1;
172 if (lineNumber >= lines.length)
173 throw 'Incomplete deprecation notice.';
174 Match match3;
175 String message;
176 do {
177 final Match match2 = _deprecationPattern2.firstMatch(lines[lineNumber]);
178 if (match2 == null)
179 throw 'Deprecation notice does not match required pattern.';
180 if (!lines[lineNumber].startsWith("$indent '"))
181 throw 'Unexpected deprecation notice indent.';
182 if (message == null) {
183 final String firstChar = String.fromCharCode(match2[1].runes.first);
184 if (firstChar.toUpperCase() != firstChar)
185 throw 'Deprecation notice should be a grammatically correct sentence and start with a capital letter; see style guide.';
186 }
187 message = match2[1];
188 lineNumber += 1;
189 if (lineNumber >= lines.length)
190 throw 'Incomplete deprecation notice.';
191 match3 = _deprecationPattern3.firstMatch(lines[lineNumber]);
192 } while (match3 == null);
193 if (!message.endsWith('.') && !message.endsWith('!') && !message.endsWith('?'))
194 throw 'Deprecation notice should be a grammatically correct sentence and end with a period.';
195 if (!lines[lineNumber].startsWith("$indent '"))
196 throw 'Unexpected deprecation notice indent.';
Ian Hickson62e4ab82019-11-15 19:21:53 -0800197 lineNumber += 1;
198 if (lineNumber >= lines.length)
199 throw 'Incomplete deprecation notice.';
200 if (!lines[lineNumber].contains(_deprecationPattern4))
201 throw 'End of deprecation notice does not match required pattern.';
202 if (!lines[lineNumber].startsWith('$indent)'))
203 throw 'Unexpected deprecation notice indent.';
204 } catch (error) {
205 errors.add('${file.path}:${lineNumber + 1}: $error');
206 }
207 }
208 }
209 // Fail if any errors
210 if (errors.isNotEmpty) {
Dan Field24f39d42020-01-02 11:47:28 -0800211 exitWithError(<String>[
212 ...errors,
213 '${bold}See: https://github.com/flutter/flutter/wiki/Tree-hygiene#handling-breaking-changes$reset',
214 ]);
Ian Hickson62e4ab82019-11-15 19:21:53 -0800215 }
216}
217
Ian Hickson449f4a62019-11-27 15:04:02 -0800218String _generateLicense(String prefix) {
219 assert(prefix != null);
220 return '${prefix}Copyright 2014 The Flutter Authors. All rights reserved.\n'
221 '${prefix}Use of this source code is governed by a BSD-style license that can be\n'
222 '${prefix}found in the LICENSE file.';
223}
224
Dan Field24f39d42020-01-02 11:47:28 -0800225Future<void> verifyNoMissingLicense(String workingDirectory, { bool checkMinimums = true }) async {
226 final int overrideMinimumMatches = checkMinimums ? null : 0;
227 await _verifyNoMissingLicenseForExtension(workingDirectory, 'dart', overrideMinimumMatches ?? 2000, _generateLicense('// '));
228 await _verifyNoMissingLicenseForExtension(workingDirectory, 'java', overrideMinimumMatches ?? 40, _generateLicense('// '));
229 await _verifyNoMissingLicenseForExtension(workingDirectory, 'h', overrideMinimumMatches ?? 30, _generateLicense('// '));
230 await _verifyNoMissingLicenseForExtension(workingDirectory, 'm', overrideMinimumMatches ?? 30, _generateLicense('// '));
231 await _verifyNoMissingLicenseForExtension(workingDirectory, 'swift', overrideMinimumMatches ?? 10, _generateLicense('// '));
232 await _verifyNoMissingLicenseForExtension(workingDirectory, 'gradle', overrideMinimumMatches ?? 100, _generateLicense('// '));
233 await _verifyNoMissingLicenseForExtension(workingDirectory, 'gn', overrideMinimumMatches ?? 0, _generateLicense('# '));
234 await _verifyNoMissingLicenseForExtension(workingDirectory, 'sh', overrideMinimumMatches ?? 1, '#!/usr/bin/env bash\n' + _generateLicense('# '));
235 await _verifyNoMissingLicenseForExtension(workingDirectory, 'bat', overrideMinimumMatches ?? 1, '@ECHO off\n' + _generateLicense('REM '));
236 await _verifyNoMissingLicenseForExtension(workingDirectory, 'ps1', overrideMinimumMatches ?? 1, _generateLicense('# '));
237 await _verifyNoMissingLicenseForExtension(workingDirectory, 'html', overrideMinimumMatches ?? 1, '<!DOCTYPE HTML>\n<!-- ${_generateLicense('')} -->', trailingBlank: false);
238 await _verifyNoMissingLicenseForExtension(workingDirectory, 'xml', overrideMinimumMatches ?? 1, '<!-- ${_generateLicense('')} -->');
Ian Hickson449f4a62019-11-27 15:04:02 -0800239}
240
Dan Field24f39d42020-01-02 11:47:28 -0800241Future<void> _verifyNoMissingLicenseForExtension(String workingDirectory, String extension, int minimumMatches, String license, { bool trailingBlank = true }) async {
Ian Hickson449f4a62019-11-27 15:04:02 -0800242 assert(!license.endsWith('\n'));
243 final String licensePattern = license + '\n' + (trailingBlank ? '\n' : '');
Ian Hicksoneae05c72019-11-14 13:19:40 -0800244 final List<String> errors = <String>[];
Alexandre Ardhuin4f9b6cf2020-01-07 16:32:04 +0100245 for (final File file in _allFiles(workingDirectory, extension, minimumMatches: minimumMatches)) {
Ian Hickson449f4a62019-11-27 15:04:02 -0800246 final String contents = file.readAsStringSync().replaceAll('\r\n', '\n');
247 if (contents.isEmpty)
248 continue; // let's not go down the /bin/true rabbit hole
249 if (!contents.startsWith(licensePattern))
Ian Hicksoneae05c72019-11-14 13:19:40 -0800250 errors.add(file.path);
Alexander Aprelev391e91c2018-08-30 07:30:25 -0700251 }
Ian Hicksoneae05c72019-11-14 13:19:40 -0800252 // Fail if any errors
253 if (errors.isNotEmpty) {
Ian Hickson449f4a62019-11-27 15:04:02 -0800254 final String s = errors.length == 1 ? ' does' : 's do';
Dan Field24f39d42020-01-02 11:47:28 -0800255 exitWithError(<String>[
256 '${bold}The following ${errors.length} file$s not have the right license header:$reset',
257 ...errors,
258 'The expected license header is:',
259 license,
260 if (trailingBlank) '...followed by a blank line.',
261 ]);
xsterc7a09a42019-04-24 12:40:17 -0700262 }
Alexander Aprelev391e91c2018-08-30 07:30:25 -0700263}
264
Ian Hicksoneae05c72019-11-14 13:19:40 -0800265final RegExp _testImportPattern = RegExp(r'''import (['"])([^'"]+_test\.dart)\1''');
266const Set<String> _exemptTestImports = <String>{
267 'package:flutter_test/flutter_test.dart',
268 'hit_test.dart',
269 'package:test_api/src/backend/live_test.dart',
270};
Alexander Aprelev391e91c2018-08-30 07:30:25 -0700271
Ian Hicksoneae05c72019-11-14 13:19:40 -0800272Future<void> verifyNoTestImports(String workingDirectory) async {
273 final List<String> errors = <String>[];
274 assert("// foo\nimport 'binding_test.dart' as binding;\n'".contains(_testImportPattern));
Dan Field24f39d42020-01-02 11:47:28 -0800275 final List<File> dartFiles = _allFiles(path.join(workingDirectory, 'packages'), 'dart', minimumMatches: 1500).toList();
Alexandre Ardhuin4f9b6cf2020-01-07 16:32:04 +0100276 for (final File file in dartFiles) {
277 for (final String line in file.readAsLinesSync()) {
Ian Hicksoneae05c72019-11-14 13:19:40 -0800278 final Match match = _testImportPattern.firstMatch(line);
279 if (match != null && !_exemptTestImports.contains(match.group(2)))
280 errors.add(file.path);
Alexander Aprelev391e91c2018-08-30 07:30:25 -0700281 }
282 }
Ian Hicksoneae05c72019-11-14 13:19:40 -0800283 // Fail if any errors
284 if (errors.isNotEmpty) {
Ian Hicksoneae05c72019-11-14 13:19:40 -0800285 final String s = errors.length == 1 ? '' : 's';
Dan Field24f39d42020-01-02 11:47:28 -0800286 exitWithError(<String>[
287 '${bold}The following file$s import a test directly. Test utilities should be in their own file.$reset',
288 ...errors,
289 ]);
Alexander Aprelev391e91c2018-08-30 07:30:25 -0700290 }
Alexander Aprelev391e91c2018-08-30 07:30:25 -0700291}
292
Ian Hicksoneae05c72019-11-14 13:19:40 -0800293Future<void> verifyNoTestPackageImports(String workingDirectory) async {
Alexander Aprelev391e91c2018-08-30 07:30:25 -0700294 // TODO(ianh): Remove this whole test once https://github.com/dart-lang/matcher/issues/98 is fixed.
295 final List<String> shims = <String>[];
Dan Field24f39d42020-01-02 11:47:28 -0800296 final List<String> errors = _allFiles(workingDirectory, 'dart', minimumMatches: 2000)
Alexandre Ardhuinec1a0152019-12-05 22:34:06 +0100297 .map<String>((File file) {
Alexander Apreleveab5cd92018-08-30 14:47:04 -0700298 final String name = Uri.file(path.relative(file.path,
299 from: workingDirectory)).toFilePath(windows: false);
Alexander Aprelev391e91c2018-08-30 07:30:25 -0700300 if (name.startsWith('bin/cache') ||
301 name == 'dev/bots/test.dart' ||
302 name.startsWith('.pub-cache'))
303 return null;
304 final String data = file.readAsStringSync();
305 if (data.contains("import 'package:test/test.dart'")) {
306 if (data.contains("// Defines a 'package:test' shim.")) {
307 shims.add(' $name');
308 if (!data.contains('https://github.com/dart-lang/matcher/issues/98'))
309 return ' $name: Shims must link to the isInstanceOf issue.';
310 if (data.contains("import 'package:test/test.dart' hide TypeMatcher, isInstanceOf;") &&
311 data.contains("export 'package:test/test.dart' hide TypeMatcher, isInstanceOf;"))
312 return null;
313 return ' $name: Shim seems to be missing the expected import/export lines.';
314 }
315 final int count = 'package:test'.allMatches(data).length;
Alexander Apreleveab5cd92018-08-30 14:47:04 -0700316 if (path.split(file.path).contains('test_driver') ||
Alexander Aprelev391e91c2018-08-30 07:30:25 -0700317 name.startsWith('dev/missing_dependency_tests/') ||
318 name.startsWith('dev/automated_tests/') ||
Greg Spencer65d3ddd2018-10-23 13:50:24 -0700319 name.startsWith('dev/snippets/') ||
Alexander Aprelev391e91c2018-08-30 07:30:25 -0700320 name.startsWith('packages/flutter/test/engine/') ||
321 name.startsWith('examples/layers/test/smoketests/raw/') ||
322 name.startsWith('examples/layers/test/smoketests/rendering/') ||
323 name.startsWith('examples/flutter_gallery/test/calculator')) {
324 // We only exempt driver tests, some of our special trivial tests.
325 // Driver tests aren't typically expected to use TypeMatcher and company.
326 // The trivial tests don't typically do anything at all and it would be
327 // a pain to have to give them a shim.
328 if (!data.contains("import 'package:test/test.dart' hide TypeMatcher, isInstanceOf;"))
329 return ' $name: test does not hide TypeMatcher and isInstanceOf from package:test; consider using a shim instead.';
330 assert(count > 0);
331 if (count == 1)
332 return null;
333 return ' $name: uses \'package:test\' $count times.';
334 }
335 if (name.startsWith('packages/flutter_test/')) {
336 // flutter_test has deep ties to package:test
337 return null;
338 }
339 if (data.contains("import 'package:test/test.dart' as test_package;") ||
340 data.contains("import 'package:test/test.dart' as test_package show ")) {
341 if (count == 1)
342 return null;
343 }
344 return ' $name: uses \'package:test\' directly';
345 }
346 return null;
347 })
348 .where((String line) => line != null)
349 .toList()
350 ..sort();
351
352 // Fail if any errors
353 if (errors.isNotEmpty) {
Alexander Aprelev391e91c2018-08-30 07:30:25 -0700354 final String s1 = errors.length == 1 ? 's' : '';
355 final String s2 = errors.length == 1 ? '' : 's';
Dan Field24f39d42020-01-02 11:47:28 -0800356 exitWithError(<String>[
357 '${bold}The following file$s2 use$s1 \'package:test\' incorrectly:$reset',
358 ...errors,
359 'Rather than depending on \'package:test\' directly, use one of the shims:',
360 ...shims,
361 'This insulates us from breaking changes in \'package:test\'.'
362 ]);
Alexander Aprelev391e91c2018-08-30 07:30:25 -0700363 }
364}
365
Ian Hicksoneae05c72019-11-14 13:19:40 -0800366Future<void> verifyGeneratedPluginRegistrants(String flutterRoot) async {
367 final Directory flutterRootDir = Directory(flutterRoot);
368
369 final Map<String, List<File>> packageToRegistrants = <String, List<File>>{};
370
Alexandre Ardhuin4f9b6cf2020-01-07 16:32:04 +0100371 for (final File file in flutterRootDir.listSync(recursive: true).whereType<File>().where(_isGeneratedPluginRegistrant)) {
Alexandre Ardhuinec1a0152019-12-05 22:34:06 +0100372 final String package = _getPackageFor(file, flutterRootDir);
373 final List<File> registrants = packageToRegistrants.putIfAbsent(package, () => <File>[]);
374 registrants.add(file);
Ian Hicksoneae05c72019-11-14 13:19:40 -0800375 }
376
377 final Set<String> outOfDate = <String>{};
378
Alexandre Ardhuin4f9b6cf2020-01-07 16:32:04 +0100379 for (final String package in packageToRegistrants.keys) {
Ian Hicksoneae05c72019-11-14 13:19:40 -0800380 final Map<File, String> fileToContent = <File, String>{};
Alexandre Ardhuin4f9b6cf2020-01-07 16:32:04 +0100381 for (final File f in packageToRegistrants[package]) {
Ian Hicksoneae05c72019-11-14 13:19:40 -0800382 fileToContent[f] = f.readAsStringSync();
383 }
384 await runCommand(flutter, <String>['inject-plugins'],
385 workingDirectory: package,
386 outputMode: OutputMode.discard,
387 );
Alexandre Ardhuin4f9b6cf2020-01-07 16:32:04 +0100388 for (final File registrant in fileToContent.keys) {
Ian Hicksoneae05c72019-11-14 13:19:40 -0800389 if (registrant.readAsStringSync() != fileToContent[registrant]) {
390 outOfDate.add(registrant.path);
391 }
392 }
393 }
394
395 if (outOfDate.isNotEmpty) {
Dan Field24f39d42020-01-02 11:47:28 -0800396 exitWithError(<String>[
397 '${bold}The following GeneratedPluginRegistrants are out of date:$reset',
398 for (String registrant in outOfDate) ' - $registrant',
399 '\nRun "flutter inject-plugins" in the package that\'s out of date.',
400 ]);
Ian Hicksoneae05c72019-11-14 13:19:40 -0800401 }
402}
403
404Future<void> verifyNoBadImportsInFlutter(String workingDirectory) async {
Alexander Aprelev391e91c2018-08-30 07:30:25 -0700405 final List<String> errors = <String>[];
406 final String libPath = path.join(workingDirectory, 'packages', 'flutter', 'lib');
407 final String srcPath = path.join(workingDirectory, 'packages', 'flutter', 'lib', 'src');
408 // Verify there's one libPath/*.dart for each srcPath/*/.
Alexandre Ardhuind927c932018-09-12 08:29:29 +0200409 final List<String> packages = Directory(libPath).listSync()
Alexander Aprelev391e91c2018-08-30 07:30:25 -0700410 .where((FileSystemEntity entity) => entity is File && path.extension(entity.path) == '.dart')
411 .map<String>((FileSystemEntity entity) => path.basenameWithoutExtension(entity.path))
412 .toList()..sort();
Alexandre Ardhuind927c932018-09-12 08:29:29 +0200413 final List<String> directories = Directory(srcPath).listSync()
Alexander Aprelev391e91c2018-08-30 07:30:25 -0700414 .whereType<Directory>()
415 .map<String>((Directory entity) => path.basename(entity.path))
416 .toList()..sort();
Ian Hicksoneae05c72019-11-14 13:19:40 -0800417 if (!_listEquals<String>(packages, directories)) {
Alexander Aprelev391e91c2018-08-30 07:30:25 -0700418 errors.add(
419 'flutter/lib/*.dart does not match flutter/lib/src/*/:\n'
420 'These are the exported packages:\n' +
Alexandre Ardhuinf62afdc2018-10-01 21:29:08 +0200421 packages.map<String>((String path) => ' lib/$path.dart').join('\n') +
Alexander Aprelev391e91c2018-08-30 07:30:25 -0700422 'These are the directories:\n' +
Alexandre Ardhuinf62afdc2018-10-01 21:29:08 +0200423 directories.map<String>((String path) => ' lib/src/$path/').join('\n')
Alexander Aprelev391e91c2018-08-30 07:30:25 -0700424 );
425 }
426 // Verify that the imports are well-ordered.
427 final Map<String, Set<String>> dependencyMap = <String, Set<String>>{};
Alexandre Ardhuin4f9b6cf2020-01-07 16:32:04 +0100428 for (final String directory in directories) {
Ian Hickson58939b72019-02-12 12:29:36 -0800429 dependencyMap[directory] = _findFlutterDependencies(path.join(srcPath, directory), errors, checkForMeta: directory != 'foundation');
Alexander Aprelev391e91c2018-08-30 07:30:25 -0700430 }
Ian Hickson58939b72019-02-12 12:29:36 -0800431 assert(dependencyMap['material'].contains('widgets') &&
432 dependencyMap['widgets'].contains('rendering') &&
433 dependencyMap['rendering'].contains('painting')); // to make sure we're convinced _findFlutterDependencies is finding some
Alexandre Ardhuin4f9b6cf2020-01-07 16:32:04 +0100434 for (final String package in dependencyMap.keys) {
Alexander Aprelev391e91c2018-08-30 07:30:25 -0700435 if (dependencyMap[package].contains(package)) {
436 errors.add(
437 'One of the files in the $yellow$package$reset package imports that package recursively.'
438 );
439 }
440 }
LongCatIsLooongd291de02020-01-09 10:25:58 -0800441
442 for (final String key in dependencyMap.keys) {
443 for (final String dependency in dependencyMap[key]) {
444 if (dependencyMap[dependency] != null)
445 continue;
446 // Sanity check before performing _deepSearch, to ensure there's no rogue
447 // dependencies.
448 final String validFilenames = dependencyMap.keys.map((String name) => name + '.dart').join(', ');
449 errors.add(
450 '$key imported package:flutter/$dependency.dart '
451 'which is not one of the valid exports { $validFilenames }.\n'
452 'Consider changing $dependency.dart to one of them.'
453 );
454 }
455 }
456
Alexandre Ardhuin4f9b6cf2020-01-07 16:32:04 +0100457 for (final String package in dependencyMap.keys) {
Alexandre Ardhuinf62afdc2018-10-01 21:29:08 +0200458 final List<String> loop = _deepSearch<String>(dependencyMap, package);
Alexander Aprelev391e91c2018-08-30 07:30:25 -0700459 if (loop != null) {
460 errors.add(
461 '${yellow}Dependency loop:$reset ' +
462 loop.join(' depends on ')
463 );
464 }
465 }
466 // Fail if any errors
467 if (errors.isNotEmpty) {
Dan Field24f39d42020-01-02 11:47:28 -0800468 exitWithError(<String>[
469 if (errors.length == 1)
470 '${bold}An error was detected when looking at import dependencies within the Flutter package:$reset'
471 else
472 '${bold}Multiple errors were detected when looking at import dependencies within the Flutter package:$reset',
473 ...errors,
474 ]);
Alexander Aprelev391e91c2018-08-30 07:30:25 -0700475 }
476}
477
Ian Hicksoneae05c72019-11-14 13:19:40 -0800478Future<void> verifyNoBadImportsInFlutterTools(String workingDirectory) async {
479 final List<String> errors = <String>[];
Dan Field24f39d42020-01-02 11:47:28 -0800480 final List<File> files = _allFiles(path.join(workingDirectory, 'packages', 'flutter_tools', 'lib'), 'dart', minimumMatches: 200).toList();
Alexandre Ardhuin4f9b6cf2020-01-07 16:32:04 +0100481 for (final File file in files) {
Ian Hicksoneae05c72019-11-14 13:19:40 -0800482 if (file.readAsStringSync().contains('package:flutter_tools/')) {
483 errors.add('$yellow${file.path}$reset imports flutter_tools.');
484 }
485 }
486 // Fail if any errors
487 if (errors.isNotEmpty) {
Dan Field24f39d42020-01-02 11:47:28 -0800488 exitWithError(<String>[
489 if (errors.length == 1)
490 '${bold}An error was detected when looking at import dependencies within the flutter_tools package:$reset'
491 else
492 '${bold}Multiple errors were detected when looking at import dependencies within the flutter_tools package:$reset',
493 ...errors.map((String paragraph) => '$paragraph\n'),
494 ]);
Ian Hicksoneae05c72019-11-14 13:19:40 -0800495 }
496}
497
498Future<void> verifyInternationalizations() async {
499 final EvalResult materialGenResult = await _evalCommand(
500 dart,
501 <String>[
Shi-Hao Hong7874bca2019-12-16 17:30:57 -0800502 path.join('dev', 'tools', 'localization', 'bin', 'gen_localizations.dart'),
Ian Hicksoneae05c72019-11-14 13:19:40 -0800503 '--material',
504 ],
505 workingDirectory: flutterRoot,
506 );
507 final EvalResult cupertinoGenResult = await _evalCommand(
508 dart,
509 <String>[
Shi-Hao Hong7874bca2019-12-16 17:30:57 -0800510 path.join('dev', 'tools', 'localization', 'bin', 'gen_localizations.dart'),
Ian Hicksoneae05c72019-11-14 13:19:40 -0800511 '--cupertino',
512 ],
513 workingDirectory: flutterRoot,
514 );
515
516 final String materialLocalizationsFile = path.join('packages', 'flutter_localizations', 'lib', 'src', 'l10n', 'generated_material_localizations.dart');
517 final String cupertinoLocalizationsFile = path.join('packages', 'flutter_localizations', 'lib', 'src', 'l10n', 'generated_cupertino_localizations.dart');
518 final String expectedMaterialResult = await File(materialLocalizationsFile).readAsString();
519 final String expectedCupertinoResult = await File(cupertinoLocalizationsFile).readAsString();
520
521 if (materialGenResult.stdout.trim() != expectedMaterialResult.trim()) {
Dan Field24f39d42020-01-02 11:47:28 -0800522 exitWithError(<String>[
523 '<<<<<<< $materialLocalizationsFile',
524 expectedMaterialResult.trim(),
525 '=======',
526 materialGenResult.stdout.trim(),
527 '>>>>>>> gen_localizations',
528 'The contents of $materialLocalizationsFile are different from that produced by gen_localizations.',
529 '',
530 'Did you forget to run gen_localizations.dart after updating a .arb file?',
531 ]);
Ian Hicksoneae05c72019-11-14 13:19:40 -0800532 }
533 if (cupertinoGenResult.stdout.trim() != expectedCupertinoResult.trim()) {
Dan Field24f39d42020-01-02 11:47:28 -0800534 exitWithError(<String>[
535 '<<<<<<< $cupertinoLocalizationsFile',
536 expectedCupertinoResult.trim(),
537 '=======',
538 cupertinoGenResult.stdout.trim(),
539 '>>>>>>> gen_localizations',
540 'The contents of $cupertinoLocalizationsFile are different from that produced by gen_localizations.',
541 '',
542 'Did you forget to run gen_localizations.dart after updating a .arb file?',
543 ]);
Ian Hicksoneae05c72019-11-14 13:19:40 -0800544 }
545}
546
Dan Field3e634112020-01-14 16:43:01 -0800547Future<void> verifyNoRuntimeTypeInToString(String workingDirectory) async {
548 final String flutterLib = path.join(workingDirectory, 'packages', 'flutter', 'lib');
549 final Set<String> excludedFiles = <String>{
550 path.join(flutterLib, 'src', 'foundation', 'object.dart'), // Calls this from within an assert.
551 };
552 final List<File> files = _allFiles(flutterLib, 'dart', minimumMatches: 400)
553 .where((File file) => !excludedFiles.contains(file.path))
554 .toList();
555 final RegExp toStringRegExp = RegExp(r'^\s+String\s+to(.+?)?String(.+?)?\(\)\s+(\{|=>)');
556 final List<String> problems = <String>[];
557 for (final File file in files) {
558 final List<String> lines = file.readAsLinesSync();
559 for (int index = 0; index < lines.length; index++) {
560 if (toStringRegExp.hasMatch(lines[index])) {
561 final int sourceLine = index + 1;
562 bool _checkForRuntimeType(String line) {
563 if (line.contains(r'$runtimeType') || line.contains('runtimeType.toString()')) {
564 problems.add('${file.path}:$sourceLine}: toString calls runtimeType.toString');
565 return true;
566 }
567 return false;
568 }
569 if (_checkForRuntimeType(lines[index])) {
570 continue;
571 }
572 if (lines[index].contains('=>')) {
573 while (!lines[index].contains(';')) {
574 index++;
575 assert(index < lines.length, 'Source file $file has unterminated toString method.');
576 if (_checkForRuntimeType(lines[index])) {
577 break;
578 }
579 }
580 } else {
581 int openBraceCount = '{'.allMatches(lines[index]).length - '}'.allMatches(lines[index]).length;
582 while (!lines[index].contains('}') && openBraceCount > 0) {
583 index++;
584 assert(index < lines.length, 'Source file $file has unbalanced braces in a toString method.');
585 if (_checkForRuntimeType(lines[index])) {
586 break;
587 }
588 openBraceCount += '{'.allMatches(lines[index]).length;
589 openBraceCount -= '}'.allMatches(lines[index]).length;
590 }
591 }
592 }
593 }
594 }
595 if (problems.isNotEmpty)
596 exitWithError(problems);
597}
598
Dan Field24f39d42020-01-02 11:47:28 -0800599Future<void> verifyNoTrailingSpaces(String workingDirectory, { int minimumMatches = 4000 }) async {
600 final List<File> files = _allFiles(workingDirectory, null, minimumMatches: minimumMatches)
601 .where((File file) => path.basename(file.path) != 'serviceaccount.enc')
602 .where((File file) => path.basename(file.path) != 'Ahem.ttf')
603 .where((File file) => path.extension(file.path) != '.snapshot')
604 .where((File file) => path.extension(file.path) != '.png')
605 .where((File file) => path.extension(file.path) != '.jpg')
606 .where((File file) => path.extension(file.path) != '.jar')
607 .toList();
608 final List<String> problems = <String>[];
Alexandre Ardhuin4f9b6cf2020-01-07 16:32:04 +0100609 for (final File file in files) {
Dan Field24f39d42020-01-02 11:47:28 -0800610 final List<String> lines = file.readAsLinesSync();
611 for (int index = 0; index < lines.length; index += 1) {
612 if (lines[index].endsWith(' ')) {
613 problems.add('${file.path}:${index + 1}: trailing U+0020 space character');
614 } else if (lines[index].endsWith('\t')) {
615 problems.add('${file.path}:${index + 1}: trailing U+0009 tab character');
616 }
Ian Hicksone768c922019-12-30 17:12:19 -0800617 }
Dan Field24f39d42020-01-02 11:47:28 -0800618 if (lines.isNotEmpty && lines.last == '')
619 problems.add('${file.path}:${lines.length}: trailing blank line');
620 }
621 if (problems.isNotEmpty)
622 exitWithError(problems);
623}
624
625class Hash256 {
626 Hash256(this.a, this.b, this.c, this.d);
627
628 factory Hash256.fromDigest(Digest digest) {
629 assert(digest.bytes.length == 32);
630 return Hash256(
631 digest.bytes[ 0] << 56 |
632 digest.bytes[ 1] << 48 |
633 digest.bytes[ 2] << 40 |
634 digest.bytes[ 3] << 32 |
635 digest.bytes[ 4] << 24 |
636 digest.bytes[ 5] << 16 |
637 digest.bytes[ 6] << 8 |
638 digest.bytes[ 7] << 0,
639 digest.bytes[ 8] << 56 |
640 digest.bytes[ 9] << 48 |
641 digest.bytes[10] << 40 |
642 digest.bytes[11] << 32 |
643 digest.bytes[12] << 24 |
644 digest.bytes[13] << 16 |
645 digest.bytes[14] << 8 |
646 digest.bytes[15] << 0,
647 digest.bytes[16] << 56 |
648 digest.bytes[17] << 48 |
649 digest.bytes[18] << 40 |
650 digest.bytes[19] << 32 |
651 digest.bytes[20] << 24 |
652 digest.bytes[21] << 16 |
653 digest.bytes[22] << 8 |
654 digest.bytes[23] << 0,
655 digest.bytes[24] << 56 |
656 digest.bytes[25] << 48 |
657 digest.bytes[26] << 40 |
658 digest.bytes[27] << 32 |
659 digest.bytes[28] << 24 |
660 digest.bytes[29] << 16 |
661 digest.bytes[30] << 8 |
662 digest.bytes[31] << 0,
663 );
664 }
665
666 final int a;
667 final int b;
668 final int c;
669 final int d;
670
671 @override
672 bool operator ==(Object other) {
673 if (other.runtimeType != runtimeType)
674 return false;
675 return other is Hash256
676 && other.a == a
677 && other.b == b
678 && other.c == c
679 && other.d == d;
680 }
681
682 @override
683 int get hashCode => a ^ b ^ c ^ d;
684}
685
686// DO NOT ADD ANY ENTRIES TO THIS LIST.
687// We have a policy of not checking in binaries into this repository.
688// If you have binaries to add, please consult Hixie for advice.
689final Set<Hash256> _grandfatheredBinaries = <Hash256>{
690 // DEFAULT ICON IMAGES
691
692 // packages/flutter_tools/templates/app/android.tmpl/app/src/main/res/mipmap-hdpi/ic_launcher.png
693 // packages/flutter_tools/templates/module/android/host_app_common/app.tmpl/src/main/res/mipmap-hdpi/ic_launcher.png
694 // (also used by many examples)
695 Hash256(0x6A7C8F0D703E3682, 0x108F9662F8133022, 0x36240D3F8F638BB3, 0x91E32BFB96055FEF),
696
697 // packages/flutter_tools/templates/app/android.tmpl/app/src/main/res/mipmap-mdpi/ic_launcher.png
698 // (also used by many examples)
699 Hash256(0xC7C0C0189145E4E3, 0x2A401C61C9BDC615, 0x754B0264E7AFAE24, 0xE834BB81049EAF81),
700
701 // packages/flutter_tools/templates/app/android.tmpl/app/src/main/res/mipmap-xhdpi/ic_launcher.png
702 // (also used by many examples)
703 Hash256(0xE14AA40904929BF3, 0x13FDED22CF7E7FFC, 0xBF1D1AAC4263B5EF, 0x1BE8BFCE650397AA),
704
705 // packages/flutter_tools/templates/app/android.tmpl/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
706 // (also used by many examples)
707 Hash256(0x4D470BF22D5C17D8, 0x4EDC5F82516D1BA8, 0xA1C09559CD761CEF, 0xB792F86D9F52B540),
708
709 // packages/flutter_tools/templates/app/android.tmpl/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
710 // (also used by many examples)
711 Hash256(0x3C34E1F298D0C9EA, 0x3455D46DB6B7759C, 0x8211A49E9EC6E44B, 0x635FC5C87DFB4180),
712
713 // packages/flutter_tools/templates/app/ios.tmpl/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
714 // packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
715 // (also used by a few examples)
716 Hash256(0x7770183009E91411, 0x2DE7D8EF1D235A6A, 0x30C5834424858E0D, 0x2F8253F6B8D31926),
717
718 // packages/flutter_tools/templates/app/ios.tmpl/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
719 // packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
720 // (also used by many examples)
721 Hash256(0x5925DAB509451F9E, 0xCBB12CE8A625F9D4, 0xC104718EE20CAFF8, 0xB1B51032D1CD8946),
722
723 // packages/flutter_tools/templates/app/ios.tmpl/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
724 // packages/flutter_tools/templates/app/ios.tmpl/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
725 // packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
726 // packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
727 // (also used by many examples)
728 Hash256(0xC4D9A284C12301D0, 0xF50E248EC53ED51A, 0x19A10147B774B233, 0x08399250B0D44C55),
729
730 // packages/flutter_tools/templates/app/ios.tmpl/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
731 // packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
732 // (also used by many examples)
733 Hash256(0xBF97F9D3233F33E1, 0x389B09F7B8ADD537, 0x41300CB834D6C7A5, 0xCA32CBED363A4FB2),
734
735 // packages/flutter_tools/templates/app/ios.tmpl/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
736 // packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
737 // (also used by many examples)
738 Hash256(0x285442F69A06B45D, 0x9D79DF80321815B5, 0x46473548A37B7881, 0x9B68959C7B8ED237),
739
740 // packages/flutter_tools/templates/app/ios.tmpl/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
741 // packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
742 // (also used by many examples)
743 Hash256(0x2AB64AF8AC727EA9, 0x9C6AB9EAFF847F46, 0xFBF2A9A0A78A0ABC, 0xBF3180F3851645B4),
744
745 // packages/flutter_tools/templates/app/ios.tmpl/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
746 // packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
747 // (also used by many examples)
748 Hash256(0x9DCA09F4E5ED5684, 0xD3C4DFF41F4E8B7C, 0xB864B438172D72BE, 0x069315FA362930F9),
749
750 // packages/flutter_tools/templates/app/ios.tmpl/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
751 // packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
752 // (also used by many examples)
753 Hash256(0xD5AD04DE321EF37C, 0xACC5A7B960AFCCE7, 0x1BDCB96FA020C482, 0x49C1545DD1A0F497),
754
755 // packages/flutter_tools/templates/app/ios.tmpl/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
756 // packages/flutter_tools/templates/app/ios.tmpl/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
757 // packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
758 // packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
759 // (also used by many examples)
760 Hash256(0x809ABFE75C440770, 0xC13C4E2E46D09603, 0xC22053E9D4E0E227, 0x5DCB9C1DCFBB2C75),
761
762 // packages/flutter_tools/templates/app/ios.tmpl/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
763 // packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
764 // (also used by many examples)
765 Hash256(0x3DB08CB79E7B01B9, 0xE81F956E3A0AE101, 0x48D0FAFDE3EA7AA7, 0x0048DF905AA52CFD),
766
767 // packages/flutter_tools/templates/app/ios.tmpl/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
768 // packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
769 // (also used by many examples)
770 Hash256(0x23C13D463F5DCA5C, 0x1F14A14934003601, 0xC29F1218FD461016, 0xD8A22CEF579A665F),
771
772 // packages/flutter_tools/templates/app/ios.tmpl/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
773 // packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
774 // (also used by many examples)
775 Hash256(0x6DB7726530D71D3F, 0x52CB59793EB69131, 0x3BAA04796E129E1E, 0x043C0A58A1BFFD2F),
776
777 // packages/flutter_tools/templates/app/ios.tmpl/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
778 // packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
779 // (also used by many examples)
780 Hash256(0xCEE565F5E6211656, 0x9B64980B209FD5CA, 0x4B3D3739011F5343, 0x250B33A1A2C6EB65),
781
782 // packages/flutter_tools/templates/app/ios.tmpl/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
783 // packages/flutter_tools/templates/app/ios.tmpl/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
784 // packages/flutter_tools/templates/app/ios.tmpl/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
785 // packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
786 // packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
787 // packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
788 // (also used by many examples)
789 Hash256(0x93AE7D494FAD0FB3, 0x0CBF3AE746A39C4B, 0xC7A0F8BBF87FBB58, 0x7A3F3C01F3C5CE20),
790
791 // packages/flutter_tools/templates/app/macos.tmpl/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png
792 // (also used by a few examples)
793 Hash256(0xB18BEBAAD1AD6724, 0xE48BCDF699BA3927, 0xDF3F258FEBE646A3, 0xAB5C62767C6BAB40),
794
795 // packages/flutter_tools/templates/app/macos.tmpl/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png
796 // (also used by a few examples)
797 Hash256(0xF90D839A289ECADB, 0xF2B0B3400DA43EB8, 0x08B84908335AE4A0, 0x07457C4D5A56A57C),
798
799 // packages/flutter_tools/templates/app/macos.tmpl/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png
800 // (also used by a few examples)
801 Hash256(0x592C2ABF84ADB2D3, 0x91AED8B634D3233E, 0x2C65369F06018DCD, 0x8A4B27BA755EDCBE),
802
803 // packages/flutter_tools/templates/app/macos.tmpl/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png
804 // (also used by a few examples)
805 Hash256(0x75D9A0C034113CA8, 0xA1EC11C24B81F208, 0x6630A5A5C65C7D26, 0xA5DC03A1C0A4478C),
806
807 // packages/flutter_tools/templates/app/macos.tmpl/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png
808 // (also used by a few examples)
809 Hash256(0xA896E65745557732, 0xC72BD4EE3A10782F, 0xE2AA95590B5AF659, 0x869E5808DB9C01C1),
810
811 // packages/flutter_tools/templates/app/macos.tmpl/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png
812 // (also used by a few examples)
813 Hash256(0x3A69A8A1AAC5D9A8, 0x374492AF4B6D07A4, 0xCE637659EB24A784, 0x9C4DFB261D75C6A3),
814
815 // packages/flutter_tools/templates/app/macos.tmpl/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png
816 // (also used by a few examples)
817 Hash256(0xD29D4E0AF9256DC9, 0x2D0A8F8810608A5E, 0x64A132AD8B397CA2, 0xC4DDC0B1C26A68C3),
818
Jonah Williams5d30c092020-01-10 09:37:20 -0800819 // packages/flutter_tools/templates/app/web/icons/Icon-192.png.copy.tmpl
820 // examples/flutter_gallery/web/icons/Icon-192.png
821 Hash256(0x3DCE99077602F704, 0x21C1C6B2A240BC9B, 0x83D64D86681D45F2, 0x154143310C980BE3),
822
823 // packages/flutter_tools/templates/app/web/icons/Icon-512.png.copy.tmpl
824 // examples/flutter_gallery/web/icons/Icon-512.png
825 Hash256(0xBACCB205AE45f0B4, 0x21BE1657259B4943, 0xAC40C95094AB877F, 0x3BCBE12CD544DCBE),
Dan Field24f39d42020-01-02 11:47:28 -0800826
Jonah Williamsab426852020-01-28 13:09:18 -0800827 // packages/flutter_tools/templates/app/web/favicon.png.copy.tmpl
828 // examples/flutter_gallery/web/favicon.png
829 Hash256(0x7AB2525F4B86B65D, 0x3E4C70358A17E5A1, 0xAAF6F437f99CBCC0, 0x46DAD73d59BB9015),
830
Dan Field24f39d42020-01-02 11:47:28 -0800831 // GALLERY ICONS
832
833 // examples/flutter_gallery/android/app/src/main/res/mipmap-hdpi/ic_background.png
834 Hash256(0x03CFDE53C249475C, 0x277E8B8E90AC8A13, 0xE5FC13C358A94CCB, 0x67CA866C9862A0DD),
835
836 // examples/flutter_gallery/android/app/src/main/res/mipmap-hdpi/ic_foreground.png
837 Hash256(0x86A83E23A505EFCC, 0x39C358B699EDE12F, 0xC088EE516A1D0C73, 0xF3B5D74DDAD164B1),
838
839 // examples/flutter_gallery/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
840 Hash256(0xD813B1A77320355E, 0xB68C485CD47D0F0F, 0x3C7E1910DCD46F08, 0x60A6401B8DC13647),
841
842 // examples/flutter_gallery/android/app/src/main/res/mipmap-xhdpi/ic_background.png
843 Hash256(0x35AFA76BD5D6053F, 0xEE927436C78A8794, 0xA8BA5F5D9FC9653B, 0xE5B96567BB7215ED),
844
845 // examples/flutter_gallery/android/app/src/main/res/mipmap-xhdpi/ic_foreground.png
846 Hash256(0x263CE9B4F1F69B43, 0xEBB08AE9FE8F80E7, 0x95647A59EF2C040B, 0xA8AEB246861A7DFF),
847
848 // examples/flutter_gallery/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
849 Hash256(0x5E1A93C3653BAAFF, 0x1AAC6BCEB8DCBC2F, 0x2AE7D68ECB07E507, 0xCB1FA8354B28313A),
850
851 // examples/flutter_gallery/android/app/src/main/res/mipmap-xxhdpi/ic_background.png
852 Hash256(0xA5C77499151DDEC6, 0xDB40D0AC7321FD74, 0x0646C0C0F786743F, 0x8F3C3C408CAC5E8C),
853
854 // examples/flutter_gallery/android/app/src/main/res/mipmap-xxhdpi/ic_foreground.png
855 Hash256(0x33DE450980A2A16B, 0x1982AC7CDC1E7B01, 0x919E07E0289C2139, 0x65F85BCED8895FEF),
856
857 // examples/flutter_gallery/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
858 Hash256(0xC3B8577F4A89BA03, 0x830944FB06C3566B, 0x4C99140A2CA52958, 0x089BFDC3079C59B7),
859
860 // examples/flutter_gallery/android/app/src/main/res/mipmap-xxxhdpi/ic_background.png
861 Hash256(0xDEBC241D6F9C5767, 0x8980FDD46FA7ED0C, 0x5B8ACD26BCC5E1BC, 0x473C89B432D467AD),
862
863 // examples/flutter_gallery/android/app/src/main/res/mipmap-xxxhdpi/ic_foreground.png
864 Hash256(0xBEFE5F7E82BF8B64, 0x148D869E3742004B, 0xF821A9F5A1BCDC00, 0x357D246DCC659DC2),
865
866 // examples/flutter_gallery/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
867 Hash256(0xC385404341FF9EDD, 0x30FBE76F0EC99155, 0x8EA4F4AFE8CC0C60, 0x1CA3EDEF177E1DA8),
868
869 // examples/flutter_gallery/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-1024.png
870 Hash256(0x6BE5751A29F57A80, 0x36A4B31CC542C749, 0x984E49B22BD65CAA, 0x75AE8B2440848719),
871
872 // examples/flutter_gallery/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-120.png
873 Hash256(0x9972A2264BFA8F8D, 0x964AFE799EADC1FA, 0x2247FB31097F994A, 0x1495DC32DF071793),
874
875 // examples/flutter_gallery/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-152.png
876 Hash256(0x4C7CC9B09BEEDA24, 0x45F57D6967753910, 0x57D68E1A6B883D2C, 0x8C52701A74F1400F),
877
878 // examples/flutter_gallery/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-167.png
879 Hash256(0x66DACAC1CFE4D349, 0xDBE994CB9125FFD7, 0x2D795CFC9CF9F739, 0xEDBB06CE25082E9C),
880
881 // examples/flutter_gallery/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-180.png
882 Hash256(0x5188621015EBC327, 0xC9EF63AD76E60ECE, 0xE82BDC3E4ABF09E2, 0xEE0139FA7C0A2BE5),
883
884 // examples/flutter_gallery/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-20.png
885 Hash256(0x27D2752D04EE9A6B, 0x78410E208F74A6CD, 0xC90D9E03B73B8C60, 0xD05F7D623E790487),
886
887 // examples/flutter_gallery/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-29.png
888 Hash256(0xBB20556B2826CF85, 0xD5BAC73AA69C2AC3, 0x8E71DAD64F15B855, 0xB30CB73E0AF89307),
889
890 // examples/flutter_gallery/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-40.png
891 Hash256(0x623820FA45CDB0AC, 0x808403E34AD6A53E, 0xA3E9FDAE83EE0931, 0xB020A3A4EF2CDDE7),
892
893 // examples/flutter_gallery/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-58.png
894 Hash256(0xC6D631D1E107215E, 0xD4A58FEC5F3AA4B5, 0x0AE9724E07114C0C, 0x453E5D87C2CAD3B3),
895
896 // examples/flutter_gallery/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-60.png
897 Hash256(0x4B6F58D1EB8723C6, 0xE717A0D09FEC8806, 0x90C6D1EF4F71836E, 0x618672827979B1A2),
898
899 // examples/flutter_gallery/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-76.png
900 Hash256(0x0A1744CC7634D508, 0xE85DD793331F0C8A, 0x0B7C6DDFE0975D8F, 0x29E91C905BBB1BED),
901
902 // examples/flutter_gallery/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-80.png
903 Hash256(0x24032FBD1E6519D6, 0x0BA93C0D5C189554, 0xF50EAE23756518A2, 0x3FABACF4BD5DAF08),
904
905 // examples/flutter_gallery/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-87.png
906 Hash256(0xC17BAE6DF6BB234A, 0xE0AF4BEB0B805F12, 0x14E74EB7AA9A30F1, 0x5763689165DA7DDF),
907
908
909 // STOCKS ICONS
910
Greg Spencer4b4cff92020-01-30 09:31:07 -0800911 // dev/benchmarks/test_apps/stocks/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
Dan Field24f39d42020-01-02 11:47:28 -0800912 Hash256(0x74052AB5241D4418, 0x7085180608BC3114, 0xD12493C50CD8BBC7, 0x56DED186C37ACE84),
913
Greg Spencer4b4cff92020-01-30 09:31:07 -0800914 // dev/benchmarks/test_apps/stocks/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
Dan Field24f39d42020-01-02 11:47:28 -0800915 Hash256(0xE37947332E3491CB, 0x82920EE86A086FEA, 0xE1E0A70B3700A7DA, 0xDCAFBDD8F40E2E19),
916
Greg Spencer4b4cff92020-01-30 09:31:07 -0800917 // dev/benchmarks/test_apps/stocks/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
Dan Field24f39d42020-01-02 11:47:28 -0800918 Hash256(0xE608CDFC0C8579FB, 0xE38873BAAF7BC944, 0x9C9D2EE3685A4FAE, 0x671EF0C8BC41D17C),
919
Greg Spencer4b4cff92020-01-30 09:31:07 -0800920 // dev/benchmarks/test_apps/stocks/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
Dan Field24f39d42020-01-02 11:47:28 -0800921 Hash256(0xBD53D86977DF9C54, 0xF605743C5ABA114C, 0x9D51D1A8BB917E1A, 0x14CAA26C335CAEBD),
922
Greg Spencer4b4cff92020-01-30 09:31:07 -0800923 // dev/benchmarks/test_apps/stocks/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Dan Field24f39d42020-01-02 11:47:28 -0800924 Hash256(0x64E4D02262C4F3D0, 0xBB4FDC21CD0A816C, 0x4CD2A0194E00FB0F, 0x1C3AE4142FAC0D15),
925
Greg Spencer4b4cff92020-01-30 09:31:07 -0800926 // dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png
927 // dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-Small-40@3x.png
Dan Field24f39d42020-01-02 11:47:28 -0800928 Hash256(0x5BA3283A76918FC0, 0xEE127D0F22D7A0B6, 0xDF03DAED61669427, 0x93D89DDD87A08117),
929
Greg Spencer4b4cff92020-01-30 09:31:07 -0800930 // dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png
Dan Field24f39d42020-01-02 11:47:28 -0800931 Hash256(0xCD7F26ED31DEA42A, 0x535D155EC6261499, 0x34E6738255FDB2C4, 0xBD8D4BDDE9A99B05),
932
Greg Spencer4b4cff92020-01-30 09:31:07 -0800933 // dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-76.png
Dan Field24f39d42020-01-02 11:47:28 -0800934 Hash256(0x3FA1225FC9A96A7E, 0xCD071BC42881AB0E, 0x7747EB72FFB72459, 0xA37971BBAD27EE24),
935
Greg Spencer4b4cff92020-01-30 09:31:07 -0800936 // dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png
Dan Field24f39d42020-01-02 11:47:28 -0800937 Hash256(0xCD867001ACD7BBDB, 0x25CDFD452AE89FA2, 0x8C2DC980CAF55F48, 0x0B16C246CFB389BC),
938
Greg Spencer4b4cff92020-01-30 09:31:07 -0800939 // dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-83.5@2x.png
Dan Field24f39d42020-01-02 11:47:28 -0800940 Hash256(0x848E9736E5C4915A, 0x7945BCF6B32FD56B, 0x1F1E7CDDD914352E, 0xC9681D38EF2A70DA),
941
Greg Spencer4b4cff92020-01-30 09:31:07 -0800942 // dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-Notification.png
Dan Field24f39d42020-01-02 11:47:28 -0800943 Hash256(0x654BA7D6C4E05CA0, 0x7799878884EF8F11, 0xA383E1F24CEF5568, 0x3C47604A966983C8),
944
Greg Spencer4b4cff92020-01-30 09:31:07 -0800945 // dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-Notification@2x.png
946 // dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-Small-40.png
Dan Field24f39d42020-01-02 11:47:28 -0800947 Hash256(0x743056FE7D83FE42, 0xA2990825B6AD0415, 0x1AF73D0D43B227AA, 0x07EBEA9B767381D9),
948
Greg Spencer4b4cff92020-01-30 09:31:07 -0800949 // dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-Notification@3x.png
Dan Field24f39d42020-01-02 11:47:28 -0800950 Hash256(0xA7E1570812D119CF, 0xEF4B602EF28DD0A4, 0x100D066E66F5B9B9, 0x881765DC9303343B),
951
Greg Spencer4b4cff92020-01-30 09:31:07 -0800952 // dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-Small-40@2x.png
Dan Field24f39d42020-01-02 11:47:28 -0800953 Hash256(0xB4102839A1E41671, 0x62DACBDEFA471953, 0xB1EE89A0AB7594BE, 0x1D9AC1E67DC2B2CE),
954
Greg Spencer4b4cff92020-01-30 09:31:07 -0800955 // dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-Small.png
Dan Field24f39d42020-01-02 11:47:28 -0800956 Hash256(0x70AC6571B593A967, 0xF1CBAEC9BC02D02D, 0x93AD766D8290ADE6, 0x840139BF9F219019),
957
Greg Spencer4b4cff92020-01-30 09:31:07 -0800958 // dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png
Dan Field24f39d42020-01-02 11:47:28 -0800959 Hash256(0x5D87A78386DA2C43, 0xDDA8FEF2CA51438C, 0xE5A276FE28C6CF0A, 0xEBE89085B56665B6),
960
Greg Spencer4b4cff92020-01-30 09:31:07 -0800961 // dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-Small@3x.png
Dan Field24f39d42020-01-02 11:47:28 -0800962 Hash256(0x4D9F5E81F668DA44, 0xB20A77F8BF7BA2E1, 0xF384533B5AD58F07, 0xB3A2F93F8635CD96),
963
964
965 // LEGACY ICONS
966
967 // dev/benchmarks/complex_layout/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@3x.png
968 // dev/benchmarks/microbenchmarks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@3x.png
969 // examples/flutter_view/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@3x.png
970 // (not really sure where this came from, or why neither the template nor most examples use them)
971 Hash256(0x6E645DC9ED913AAD, 0xB50ED29EEB16830D, 0xB32CA12F39121DB9, 0xB7BC1449DDDBF8B8),
972
973 // dev/benchmarks/macrobenchmarks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
974 // dev/integration_tests/codegen/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
975 // dev/integration_tests/ios_add2app/ios_add2app/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
976 // dev/integration_tests/release_smoke_test/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
977 Hash256(0xDEFAC77E08EC71EC, 0xA04CCA3C95D1FC33, 0xB9F26E1CB15CB051, 0x47DEFC79CDD7C158),
978
979 // examples/flutter_view/ios/Runner/ic_add.png
980 // examples/platform_view/ios/Runner/ic_add.png
981 Hash256(0x3CCE7450334675E2, 0xE3AABCA20B028993, 0x127BE82FE0EB3DFF, 0x8B027B3BAF052F2F),
982
983 // examples/image_list/images/coast.jpg
984 Hash256(0xDA957FD30C51B8D2, 0x7D74C2C918692DC4, 0xD3C5C99BB00F0D6B, 0x5EBB30395A6EDE82),
985
986 // examples/image_list/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
987 Hash256(0xB5792CA06F48A431, 0xD4379ABA2160BD5D, 0xE92339FC64C6A0D3, 0x417AA359634CD905),
988
989
990 // TEST ASSETS
991
992 // dev/benchmarks/macrobenchmarks/assets/999x1000.png
993 Hash256(0x553E9C36DFF3E610, 0x6A608BDE822A0019, 0xDE4F1769B6FBDB97, 0xBC3C20E26B839F59),
994
995 // dev/bots/test/analyze-test-input/root/packages/foo/serviceaccount.enc
996 Hash256(0xA8100AE6AA1940D0, 0xB663BB31CD466142, 0xEBBDBD5187131B92, 0xD93818987832EB89),
997
998 // dev/automated_tests/icon/test.png
999 Hash256(0xE214B4A0FEEEC6FA, 0x8E7AA8CC9BFBEC40, 0xBCDAC2F2DEBC950F, 0x75AF8EBF02BCE459),
1000
1001 // dev/integration_tests/android_splash_screens/splash_screen_kitchen_sink/android/app/src/main/res/drawable-land-xxhdpi/flutter_splash_screen.png
1002 // dev/integration_tests/android_splash_screens/splash_screen_kitchen_sink/android/app/src/main/res/mipmap-land-xxhdpi/flutter_splash_screen.png
1003 Hash256(0x2D4F8D7A3DFEF9D3, 0xA0C66938E169AB58, 0x8C6BBBBD1973E34E, 0x03C428416D010182),
1004
1005 // dev/integration_tests/android_splash_screens/splash_screen_kitchen_sink/android/app/src/main/res/drawable-xxhdpi/flutter_splash_screen.png
1006 // dev/integration_tests/android_splash_screens/splash_screen_kitchen_sink/android/app/src/main/res/mipmap-xxhdpi/flutter_splash_screen.png
1007 Hash256(0xCD46C01BAFA3B243, 0xA6AA1645EEDDE481, 0x143AC8ABAB1A0996, 0x22CAA9D41F74649A),
1008
1009 // dev/integration_tests/flutter_driver_screenshot_test/assets/red_square.png
1010 Hash256(0x40054377E1E084F4, 0x4F4410CE8F44C210, 0xABA945DFC55ED0EF, 0x23BDF9469E32F8D3),
1011
1012 // dev/integration_tests/flutter_driver_screenshot_test/test_driver/goldens/red_square_image/iPhone7,2.png
1013 Hash256(0x7F9D27C7BC418284, 0x01214E21CA886B2F, 0x40D9DA2B31AE7754, 0x71D68375F9C8A824),
1014
1015 // examples/flutter_view/assets/flutter-mark-square-64.png
1016 // examples/platform_view/assets/flutter-mark-square-64.png
1017 Hash256(0xF416B0D8AC552EC8, 0x819D1F492D1AB5E6, 0xD4F20CF45DB47C22, 0x7BB431FEFB5B67B2),
1018
1019 // packages/flutter_tools/test/data/intellij/plugins/Dart/lib/Dart.jar
1020 Hash256(0x576E489D788A13DB, 0xBF40E4A39A3DAB37, 0x15CCF0002032E79C, 0xD260C69B29E06646),
1021
1022 // packages/flutter_tools/test/data/intellij/plugins/flutter-intellij.jar
1023 Hash256(0x4C67221E25626CB2, 0x3F94E1F49D34E4CF, 0x3A9787A514924FC5, 0x9EF1E143E5BC5690),
1024
1025
1026 // HISTORICAL DEVICE LAB IMAGES
1027
1028 // dev/devicelab/images/agent-statuses.png
1029 Hash256(0x0A43016C84095771, 0x1C610E1C01B1C3B2, 0x681DA0B2062C02AA, 0x31CC7FB3FDC298FD),
1030
1031 // dev/devicelab/images/broken-test.png
1032 Hash256(0x319459F42967A888, 0x90B20063544D6707, 0x849E1E3447CC56A5, 0xE226C47DE34F13AD),
1033
1034 // dev/devicelab/images/legend.png
1035 Hash256(0x92A98975AF5F076C, 0xE7BFAB86B0DD7A3D, 0xC45287B706D10456, 0x4E512BA3C41B2749),
1036
1037
1038 // MISCELLANEOUS
1039
1040 // dev/bots/serviceaccount.enc
1041 Hash256(0x1F19ADB4D80AFE8C, 0xE61899BA776B1A8D, 0xCA398C75F5F7050D, 0xFB0E72D7FBBBA69B),
1042
1043 // dev/docs/favicon.ico
1044 Hash256(0x67368CA1733E933A, 0xCA3BC56EF0695012, 0xE862C371AD4412F0, 0x3EC396039C609965),
1045
1046 // dev/snippets/assets/code_sample.png
1047 Hash256(0xAB2211A47BDA001D, 0x173A52FD9C75EBC7, 0xE158942FFA8243AD, 0x2A148871990D4297),
1048
1049 // dev/snippets/assets/code_snippet.png
1050 Hash256(0xDEC70574DA46DFBB, 0xFA657A771F3E1FBD, 0xB265CFC6B2AA5FE3, 0x93BA4F325D1520BA),
1051
1052 // packages/flutter_tools/static/Ahem.ttf
1053 Hash256(0x63D2ABD0041C3E3B, 0x4B52AD8D382353B5, 0x3C51C6785E76CE56, 0xED9DACAD2D2E31C4),
1054};
1055
1056Future<void> verifyNoBinaries(String workingDirectory, { Set<Hash256> grandfatheredBinaries }) async {
1057 // Please do not add anything to the _grandfatheredBinaries set above.
1058 // We have a policy of not checking in binaries into this repository.
1059 // If you have binaries to add, please consult Hixie for advice.
1060 assert(
1061 _grandfatheredBinaries
1062 .expand<int>((Hash256 hash) => <int>[hash.a, hash.b, hash.c, hash.d])
Jonah Williamsab426852020-01-28 13:09:18 -08001063 .reduce((int value, int element) => value ^ element) == 0x17138C817203D1A7 // Please do not modify this line.
Dan Field24f39d42020-01-02 11:47:28 -08001064 );
1065 grandfatheredBinaries ??= _grandfatheredBinaries;
1066 if (!Platform.isWindows) { // TODO(ianh): Port this to Windows
1067 final EvalResult evalResult = await _evalCommand(
1068 'git', <String>['ls-files', '-z'],
1069 workingDirectory: workingDirectory,
1070 );
1071 if (evalResult.exitCode != 0) {
1072 exitWithError(<String>[
1073 'git ls-filese failed with exit code ${evalResult.exitCode}',
1074 '${bold}stdout:$reset',
1075 evalResult.stdout,
1076 '${bold}stderr:$reset',
1077 evalResult.stderr,
1078 ]);
1079 }
1080 final List<String> filenames = evalResult
1081 .stdout
1082 .split('\x00');
1083 assert(filenames.last.isEmpty); // git ls-files gives a trailing blank 0x00
1084 filenames.removeLast();
1085 final List<File> files = filenames
1086 .map<File>((String filename) => File(path.join(workingDirectory, filename)))
1087 .toList();
1088 final List<String> problems = <String>[];
Alexandre Ardhuin4f9b6cf2020-01-07 16:32:04 +01001089 for (final File file in files) {
Dan Field24f39d42020-01-02 11:47:28 -08001090 final Uint8List bytes = file.readAsBytesSync();
1091 try {
1092 utf8.decode(bytes);
1093 } on FormatException catch (error) {
1094 if (error.message.startsWith('Bad UTF-8 encoding ')) {
1095 final Digest digest = sha256.convert(bytes);
1096 if (!grandfatheredBinaries.contains(Hash256.fromDigest(digest)))
1097 problems.add('${file.path}:${error.offset}: file is not valid UTF-8');
1098 } else {
1099 rethrow;
1100 }
1101 }
1102 }
1103 if (problems.isNotEmpty) {
1104 exitWithError(<String>[
1105 ...problems,
1106 'All files in this repository must be UTF-8. In particular, images and other binaries',
1107 'must not be checked into this repository. This is because we are very sensitive to the',
1108 'size of the repository as it is distributed to all our developers. If you have a binary',
1109 'to which you need access, you should consider how to fetch it from another repository;',
1110 'for example, the "assets-for-api-docs" repository is used for images in API docs.',
1111 ]);
Ian Hicksoneae05c72019-11-14 13:19:40 -08001112 }
1113 }
1114}
1115
1116
1117// UTILITY FUNCTIONS
1118
1119bool _listEquals<T>(List<T> a, List<T> b) {
Alexander Aprelev391e91c2018-08-30 07:30:25 -07001120 assert(a != null);
1121 assert(b != null);
1122 if (a.length != b.length)
1123 return false;
1124 for (int index = 0; index < a.length; index += 1) {
1125 if (a[index] != b[index])
1126 return false;
1127 }
1128 return true;
1129}
1130
Dan Field24f39d42020-01-02 11:47:28 -08001131Iterable<File> _allFiles(String workingDirectory, String extension, { @required int minimumMatches }) sync* {
1132 assert(extension == null || !extension.startsWith('.'), 'Extension argument should not start with a period.');
Ian Hickson62e4ab82019-11-15 19:21:53 -08001133 final Set<FileSystemEntity> pending = <FileSystemEntity>{ Directory(workingDirectory) };
Dan Field24f39d42020-01-02 11:47:28 -08001134 int matches = 0;
Ian Hickson62e4ab82019-11-15 19:21:53 -08001135 while (pending.isNotEmpty) {
1136 final FileSystemEntity entity = pending.first;
1137 pending.remove(entity);
Ian Hickson449f4a62019-11-27 15:04:02 -08001138 if (path.extension(entity.path) == '.tmpl')
1139 continue;
Ian Hickson62e4ab82019-11-15 19:21:53 -08001140 if (entity is File) {
Ian Hickson449f4a62019-11-27 15:04:02 -08001141 if (_isGeneratedPluginRegistrant(entity))
1142 continue;
1143 if (path.basename(entity.path) == 'flutter_export_environment.sh')
1144 continue;
1145 if (path.basename(entity.path) == 'gradlew.bat')
1146 continue;
Dan Field24f39d42020-01-02 11:47:28 -08001147 if (extension == null || path.extension(entity.path) == '.$extension') {
1148 matches += 1;
Ian Hickson62e4ab82019-11-15 19:21:53 -08001149 yield entity;
Dan Field24f39d42020-01-02 11:47:28 -08001150 }
Ian Hickson62e4ab82019-11-15 19:21:53 -08001151 } else if (entity is Directory) {
1152 if (File(path.join(entity.path, '.dartignore')).existsSync())
1153 continue;
1154 if (path.basename(entity.path) == '.git')
1155 continue;
Dan Field24f39d42020-01-02 11:47:28 -08001156 if (path.basename(entity.path) == '.gradle')
1157 continue;
Ian Hickson62e4ab82019-11-15 19:21:53 -08001158 if (path.basename(entity.path) == '.dart_tool')
1159 continue;
Ian Hickson449f4a62019-11-27 15:04:02 -08001160 if (path.basename(entity.path) == 'build')
1161 continue;
Ian Hickson62e4ab82019-11-15 19:21:53 -08001162 pending.addAll(entity.listSync());
1163 }
1164 }
Dan Field24f39d42020-01-02 11:47:28 -08001165 assert(matches >= minimumMatches, 'Expected to find at least $minimumMatches files with extension ".$extension" in "$workingDirectory", but only found $matches.');
Ian Hicksoneae05c72019-11-14 13:19:40 -08001166}
1167
1168class EvalResult {
1169 EvalResult({
1170 this.stdout,
1171 this.stderr,
1172 this.exitCode = 0,
1173 });
1174
1175 final String stdout;
1176 final String stderr;
1177 final int exitCode;
1178}
1179
Dan Field24f39d42020-01-02 11:47:28 -08001180// TODO(ianh): Refactor this to reuse the code in run_command.dart
Ian Hicksoneae05c72019-11-14 13:19:40 -08001181Future<EvalResult> _evalCommand(String executable, List<String> arguments, {
1182 @required String workingDirectory,
1183 Map<String, String> environment,
1184 bool skip = false,
1185 bool allowNonZeroExit = false,
1186}) async {
1187 final String commandDescription = '${path.relative(executable, from: workingDirectory)} ${arguments.join(' ')}';
1188 final String relativeWorkingDir = path.relative(workingDirectory);
1189 if (skip) {
1190 printProgress('SKIPPING', relativeWorkingDir, commandDescription);
1191 return null;
1192 }
1193 printProgress('RUNNING', relativeWorkingDir, commandDescription);
1194
1195 final Stopwatch time = Stopwatch()..start();
1196 final Process process = await Process.start(executable, arguments,
1197 workingDirectory: workingDirectory,
1198 environment: environment,
1199 );
1200
1201 final Future<List<List<int>>> savedStdout = process.stdout.toList();
1202 final Future<List<List<int>>> savedStderr = process.stderr.toList();
1203 final int exitCode = await process.exitCode;
1204 final EvalResult result = EvalResult(
1205 stdout: utf8.decode((await savedStdout).expand<int>((List<int> ints) => ints).toList()),
1206 stderr: utf8.decode((await savedStderr).expand<int>((List<int> ints) => ints).toList()),
1207 exitCode: exitCode,
1208 );
1209
1210 print('$clock ELAPSED TIME: $bold${prettyPrintDuration(time.elapsed)}$reset for $commandDescription in $relativeWorkingDir');
1211
1212 if (exitCode != 0 && !allowNonZeroExit) {
1213 stderr.write(result.stderr);
Dan Field24f39d42020-01-02 11:47:28 -08001214 exitWithError(<String>[
1215 '${bold}ERROR:$red Last command exited with $exitCode.$reset',
1216 '${bold}Command:$red $commandDescription$reset',
1217 '${bold}Relative working directory:$red $relativeWorkingDir$reset',
1218 ]);
Ian Hicksoneae05c72019-11-14 13:19:40 -08001219 }
1220
1221 return result;
1222}
1223
1224Future<void> _runFlutterAnalyze(String workingDirectory, {
1225 List<String> options = const <String>[],
Dan Field24f39d42020-01-02 11:47:28 -08001226}) async {
1227 return await runCommand(
Ian Hicksoneae05c72019-11-14 13:19:40 -08001228 flutter,
1229 <String>['analyze', '--dartdocs', ...options],
1230 workingDirectory: workingDirectory,
1231 );
1232}
1233
Greg Spencer35fcd902019-01-14 13:49:50 -08001234final RegExp _importPattern = RegExp(r'''^\s*import (['"])package:flutter/([^.]+)\.dart\1''');
Ian Hickson58939b72019-02-12 12:29:36 -08001235final RegExp _importMetaPattern = RegExp(r'''^\s*import (['"])package:meta/meta\.dart\1''');
Alexander Aprelev391e91c2018-08-30 07:30:25 -07001236
Ian Hickson58939b72019-02-12 12:29:36 -08001237Set<String> _findFlutterDependencies(String srcPath, List<String> errors, { bool checkForMeta = false }) {
Dan Field24f39d42020-01-02 11:47:28 -08001238 return _allFiles(srcPath, 'dart', minimumMatches: 1)
Alexandre Ardhuinec1a0152019-12-05 22:34:06 +01001239 .map<Set<String>>((File file) {
1240 final Set<String> result = <String>{};
Alexandre Ardhuin4f9b6cf2020-01-07 16:32:04 +01001241 for (final String line in file.readAsLinesSync()) {
Alexandre Ardhuinec1a0152019-12-05 22:34:06 +01001242 Match match = _importPattern.firstMatch(line);
1243 if (match != null)
1244 result.add(match.group(2));
1245 if (checkForMeta) {
1246 match = _importMetaPattern.firstMatch(line);
1247 if (match != null) {
1248 errors.add(
1249 '${file.path}\nThis package imports the ${yellow}meta$reset package.\n'
1250 'You should instead import the "foundation.dart" library.'
1251 );
1252 }
Alexander Aprelev391e91c2018-08-30 07:30:25 -07001253 }
1254 }
Alexandre Ardhuinec1a0152019-12-05 22:34:06 +01001255 return result;
1256 })
1257 .reduce((Set<String> value, Set<String> element) {
1258 value ??= <String>{};
1259 value.addAll(element);
1260 return value;
1261 });
Alexander Aprelev391e91c2018-08-30 07:30:25 -07001262}
1263
1264List<T> _deepSearch<T>(Map<T, Set<T>> map, T start, [ Set<T> seen ]) {
LongCatIsLooongd291de02020-01-09 10:25:58 -08001265 if (map[start] == null)
1266 return null; // We catch these separately.
1267
Alexandre Ardhuin4f9b6cf2020-01-07 16:32:04 +01001268 for (final T key in map[start]) {
Alexander Aprelev391e91c2018-08-30 07:30:25 -07001269 if (key == start)
1270 continue; // we catch these separately
1271 if (seen != null && seen.contains(key))
1272 return <T>[start, key];
Alexandre Ardhuinf62afdc2018-10-01 21:29:08 +02001273 final List<T> result = _deepSearch<T>(
Alexander Aprelev391e91c2018-08-30 07:30:25 -07001274 map,
1275 key,
Alexandre Ardhuin919dcf52019-06-27 21:23:16 +02001276 <T>{
1277 if (seen == null) start else ...seen,
1278 key,
1279 },
Alexander Aprelev391e91c2018-08-30 07:30:25 -07001280 );
1281 if (result != null) {
1282 result.insert(0, start);
1283 // Only report the shortest chains.
1284 // For example a->b->a, rather than c->a->b->a.
1285 // Since we visit every node, we know the shortest chains are those
1286 // that start and end on the loop.
1287 if (result.first == result.last)
1288 return result;
1289 }
1290 }
1291 return null;
1292}
1293
Alexander Aprelev391e91c2018-08-30 07:30:25 -07001294String _getPackageFor(File entity, Directory flutterRootDir) {
1295 for (Directory dir = entity.parent; dir != flutterRootDir; dir = dir.parent) {
Alexandre Ardhuind927c932018-09-12 08:29:29 +02001296 if (File(path.join(dir.path, 'pubspec.yaml')).existsSync()) {
Alexander Aprelev391e91c2018-08-30 07:30:25 -07001297 return dir.path;
1298 }
1299 }
Alexandre Ardhuind927c932018-09-12 08:29:29 +02001300 throw ArgumentError('$entity is not within a dart package.');
Alexander Aprelev391e91c2018-08-30 07:30:25 -07001301}
1302
1303bool _isGeneratedPluginRegistrant(File file) {
1304 final String filename = path.basename(file.path);
1305 return !file.path.contains('.pub-cache')
1306 && (filename == 'GeneratedPluginRegistrant.java' ||
1307 filename == 'GeneratedPluginRegistrant.h' ||
1308 filename == 'GeneratedPluginRegistrant.m');
1309}