blob: a32f80d98d0751c512e96d79fb2e1fa61eed8c25 [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
Jonah Williamsf6f59c52021-04-16 14:29:32 -07005import 'dart:async';
Alexander Aprelev391e91c2018-08-30 07:30:25 -07006import '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
Jonah Williamsf6f59c52021-04-16 14:29:32 -070015import 'allowlist.dart';
Alexander Aprelev391e91c2018-08-30 07:30:25 -070016import 'run_command.dart';
Dan Field24f39d42020-01-02 11:47:28 -080017import 'utils.dart';
Alexander Aprelev391e91c2018-08-30 07:30:25 -070018
Alexander Aprelev391e91c2018-08-30 07:30:25 -070019final String flutterRoot = path.dirname(path.dirname(path.dirname(path.fromUri(Platform.script))));
20final String flutter = path.join(flutterRoot, 'bin', Platform.isWindows ? 'flutter.bat' : 'flutter');
21final String dart = path.join(flutterRoot, 'bin', 'cache', 'dart-sdk', 'bin', Platform.isWindows ? 'dart.exe' : 'dart');
22final String pub = path.join(flutterRoot, 'bin', 'cache', 'dart-sdk', 'bin', Platform.isWindows ? 'pub.bat' : 'pub');
23final String pubCache = path.join(flutterRoot, '.pub-cache');
24
25/// When you call this, you can pass additional arguments to pass custom
26/// arguments to flutter analyze. For example, you might want to call this
27/// script with the parameter --dart-sdk to use custom dart sdk.
28///
29/// For example:
30/// bin/cache/dart-sdk/bin/dart dev/bots/analyze.dart --dart-sdk=/tmp/dart-sdk
Ian Hicksoneae05c72019-11-14 13:19:40 -080031Future<void> main(List<String> arguments) async {
Ian Hickson449f4a62019-11-27 15:04:02 -080032 print('$clock STARTING ANALYSIS');
Ian Hicksoneae05c72019-11-14 13:19:40 -080033 try {
34 await run(arguments);
35 } on ExitException catch (error) {
36 error.apply();
37 }
Dan Field24f39d42020-01-02 11:47:28 -080038 print('$clock ${bold}Analysis successful.$reset');
Ian Hicksoneae05c72019-11-14 13:19:40 -080039}
40
41Future<void> run(List<String> arguments) async {
Ian Hickson58939b72019-02-12 12:29:36 -080042 bool assertsEnabled = false;
43 assert(() { assertsEnabled = true; return true; }());
44 if (!assertsEnabled) {
Dan Field24f39d42020-01-02 11:47:28 -080045 exitWithError(<String>['The analyze.dart script must be run with --enable-asserts.']);
Ian Hickson58939b72019-02-12 12:29:36 -080046 }
Alexander Aprelev391e91c2018-08-30 07:30:25 -070047
Dan Field3e634112020-01-14 16:43:01 -080048 print('$clock runtimeType in toString...');
49 await verifyNoRuntimeTypeInToString(flutterRoot);
50
Dan Field24f39d42020-01-02 11:47:28 -080051 print('$clock Unexpected binaries...');
52 await verifyNoBinaries(flutterRoot);
53
54 print('$clock Trailing spaces...');
55 await verifyNoTrailingSpaces(flutterRoot); // assumes no unexpected binaries, so should be after verifyNoBinaries
56
Ian Hickson449f4a62019-11-27 15:04:02 -080057 print('$clock Deprecations...');
Ian Hickson62e4ab82019-11-15 19:21:53 -080058 await verifyDeprecations(flutterRoot);
Ian Hickson449f4a62019-11-27 15:04:02 -080059
60 print('$clock Licenses...');
Ian Hicksoneae05c72019-11-14 13:19:40 -080061 await verifyNoMissingLicense(flutterRoot);
Ian Hickson449f4a62019-11-27 15:04:02 -080062
63 print('$clock Test imports...');
Ian Hicksoneae05c72019-11-14 13:19:40 -080064 await verifyNoTestImports(flutterRoot);
Ian Hickson449f4a62019-11-27 15:04:02 -080065
Ian Hickson449f4a62019-11-27 15:04:02 -080066 print('$clock Bad imports (framework)...');
Ian Hicksoneae05c72019-11-14 13:19:40 -080067 await verifyNoBadImportsInFlutter(flutterRoot);
Ian Hickson449f4a62019-11-27 15:04:02 -080068
69 print('$clock Bad imports (tools)...');
Ian Hicksoneae05c72019-11-14 13:19:40 -080070 await verifyNoBadImportsInFlutterTools(flutterRoot);
Ian Hickson449f4a62019-11-27 15:04:02 -080071
72 print('$clock Internationalization...');
Ian Hicksoneae05c72019-11-14 13:19:40 -080073 await verifyInternationalizations();
Ian Hickson449f4a62019-11-27 15:04:02 -080074
Alexander Aprelev391e91c2018-08-30 07:30:25 -070075 // Ensure that all package dependencies are in sync.
Ian Hickson449f4a62019-11-27 15:04:02 -080076 print('$clock Package dependencies...');
Alexander Aprelev391e91c2018-08-30 07:30:25 -070077 await runCommand(flutter, <String>['update-packages', '--verify-only'],
78 workingDirectory: flutterRoot,
79 );
80
Jonah Williamsf6f59c52021-04-16 14:29:32 -070081 /// Ensure that no new dependencies have been accidentally
82 /// added to core packages.
83 print('$clock Package Allowlist...');
84 await _checkConsumerDependencies();
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,
Michael Goderbauer7e017d32021-03-02 15:24:04 -0800106 <String>[path.join(flutterRoot, 'dev', 'bots', 'analyze_sample_code.dart')],
Dan Field24f39d42020-01-02 11:47:28 -0800107 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');
Michael Goderbauer197b4402021-03-19 15:33:05 -0700136final RegExp _deprecationPattern1 = RegExp(r'^( *)@Deprecated\($'); // flutter_ignore: deprecation_syntax (see analyze.dart)
Ian Hickson62e4ab82019-11-15 19:21:53 -0800137final RegExp _deprecationPattern2 = RegExp(r"^ *'(.+) '$");
Alexandre Ardhuina6832d42021-03-30 06:29:02 +0200138final RegExp _deprecationPattern3 = RegExp(r"^ *'This feature was deprecated after v([0-9]+)\.([0-9]+)\.([0-9]+)(\-[0-9]+\.[0-9]+\.pre)?\.',?$");
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...)
Michael Goderbauer197b4402021-03-19 15:33:05 -0700146const String _ignoreDeprecation = ' // flutter_ignore: deprecation_syntax (see analyze.dart)';
Ian Hickson62e4ab82019-11-15 19:21:53 -0800147
Michael Goderbauer584fd5f2020-06-16 09:15:43 -0700148/// Some deprecation notices are exempt for historical reasons. They must have an issue listed.
Michael Goderbauer197b4402021-03-19 15:33:05 -0700149final RegExp _legacyDeprecation = RegExp(r' // flutter_ignore: deprecation_syntax, https://github.com/flutter/flutter/issues/[0-9]+$');
Ian Hickson62e4ab82019-11-15 19:21:53 -0800150
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>[];
Yuqian Lifb552ed2020-11-07 05:19:02 -0800153 await 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) &&
Michael Goderbauer584fd5f2020-06-16 09:15:43 -0700160 !line.contains(_legacyDeprecation)) {
Ian Hickson62e4ab82019-11-15 19:21:53 -0800161 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]);
Tong Mu7abee112021-03-09 13:21:45 -0800178 if (match2 == null) {
179 String possibleReason = '';
180 if (lines[lineNumber].trimLeft().startsWith('"')) {
181 possibleReason = ' You might have used double quotes (") for the string instead of single quotes (\').';
182 }
183 throw 'Deprecation notice does not match required pattern.$possibleReason';
184 }
Ian Hickson62e4ab82019-11-15 19:21:53 -0800185 if (!lines[lineNumber].startsWith("$indent '"))
186 throw 'Unexpected deprecation notice indent.';
187 if (message == null) {
188 final String firstChar = String.fromCharCode(match2[1].runes.first);
189 if (firstChar.toUpperCase() != firstChar)
Christopher Fujino5cfb16b2020-06-19 12:03:38 -0700190 throw 'Deprecation notice should be a grammatically correct sentence and start with a capital letter; see style guide: https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo';
Ian Hickson62e4ab82019-11-15 19:21:53 -0800191 }
192 message = match2[1];
193 lineNumber += 1;
194 if (lineNumber >= lines.length)
195 throw 'Incomplete deprecation notice.';
196 match3 = _deprecationPattern3.firstMatch(lines[lineNumber]);
197 } while (match3 == null);
Christopher Fujino5cfb16b2020-06-19 12:03:38 -0700198 final int v1 = int.parse(match3[1]);
199 final int v2 = int.parse(match3[2]);
200 final bool hasV4 = match3[4] != null;
201 if (v1 > 1 || (v1 == 1 && v2 >= 20)) {
202 if (!hasV4)
203 throw 'Deprecation notice does not accurately indicate a dev branch version number; please see https://flutter.dev/docs/development/tools/sdk/releases to find the latest dev build version number.';
204 }
Ian Hickson62e4ab82019-11-15 19:21:53 -0800205 if (!message.endsWith('.') && !message.endsWith('!') && !message.endsWith('?'))
206 throw 'Deprecation notice should be a grammatically correct sentence and end with a period.';
207 if (!lines[lineNumber].startsWith("$indent '"))
208 throw 'Unexpected deprecation notice indent.';
Ian Hickson62e4ab82019-11-15 19:21:53 -0800209 lineNumber += 1;
210 if (lineNumber >= lines.length)
211 throw 'Incomplete deprecation notice.';
212 if (!lines[lineNumber].contains(_deprecationPattern4))
213 throw 'End of deprecation notice does not match required pattern.';
214 if (!lines[lineNumber].startsWith('$indent)'))
215 throw 'Unexpected deprecation notice indent.';
216 } catch (error) {
217 errors.add('${file.path}:${lineNumber + 1}: $error');
218 }
219 }
220 }
221 // Fail if any errors
222 if (errors.isNotEmpty) {
Dan Field24f39d42020-01-02 11:47:28 -0800223 exitWithError(<String>[
224 ...errors,
225 '${bold}See: https://github.com/flutter/flutter/wiki/Tree-hygiene#handling-breaking-changes$reset',
226 ]);
Ian Hickson62e4ab82019-11-15 19:21:53 -0800227 }
228}
229
Ian Hickson449f4a62019-11-27 15:04:02 -0800230String _generateLicense(String prefix) {
231 assert(prefix != null);
232 return '${prefix}Copyright 2014 The Flutter Authors. All rights reserved.\n'
233 '${prefix}Use of this source code is governed by a BSD-style license that can be\n'
234 '${prefix}found in the LICENSE file.';
235}
236
Dan Field24f39d42020-01-02 11:47:28 -0800237Future<void> verifyNoMissingLicense(String workingDirectory, { bool checkMinimums = true }) async {
238 final int overrideMinimumMatches = checkMinimums ? null : 0;
239 await _verifyNoMissingLicenseForExtension(workingDirectory, 'dart', overrideMinimumMatches ?? 2000, _generateLicense('// '));
Jenn Magder13e30aa2020-02-06 11:39:55 -0800240 await _verifyNoMissingLicenseForExtension(workingDirectory, 'java', overrideMinimumMatches ?? 39, _generateLicense('// '));
Dan Field24f39d42020-01-02 11:47:28 -0800241 await _verifyNoMissingLicenseForExtension(workingDirectory, 'h', overrideMinimumMatches ?? 30, _generateLicense('// '));
242 await _verifyNoMissingLicenseForExtension(workingDirectory, 'm', overrideMinimumMatches ?? 30, _generateLicense('// '));
243 await _verifyNoMissingLicenseForExtension(workingDirectory, 'swift', overrideMinimumMatches ?? 10, _generateLicense('// '));
Jenn Magder65e94f52021-02-23 19:56:02 -0800244 await _verifyNoMissingLicenseForExtension(workingDirectory, 'gradle', overrideMinimumMatches ?? 80, _generateLicense('// '));
Dan Field24f39d42020-01-02 11:47:28 -0800245 await _verifyNoMissingLicenseForExtension(workingDirectory, 'gn', overrideMinimumMatches ?? 0, _generateLicense('# '));
246 await _verifyNoMissingLicenseForExtension(workingDirectory, 'sh', overrideMinimumMatches ?? 1, '#!/usr/bin/env bash\n' + _generateLicense('# '));
247 await _verifyNoMissingLicenseForExtension(workingDirectory, 'bat', overrideMinimumMatches ?? 1, '@ECHO off\n' + _generateLicense('REM '));
248 await _verifyNoMissingLicenseForExtension(workingDirectory, 'ps1', overrideMinimumMatches ?? 1, _generateLicense('# '));
249 await _verifyNoMissingLicenseForExtension(workingDirectory, 'html', overrideMinimumMatches ?? 1, '<!DOCTYPE HTML>\n<!-- ${_generateLicense('')} -->', trailingBlank: false);
250 await _verifyNoMissingLicenseForExtension(workingDirectory, 'xml', overrideMinimumMatches ?? 1, '<!-- ${_generateLicense('')} -->');
Ian Hickson449f4a62019-11-27 15:04:02 -0800251}
252
Dan Field24f39d42020-01-02 11:47:28 -0800253Future<void> _verifyNoMissingLicenseForExtension(String workingDirectory, String extension, int minimumMatches, String license, { bool trailingBlank = true }) async {
Ian Hickson449f4a62019-11-27 15:04:02 -0800254 assert(!license.endsWith('\n'));
255 final String licensePattern = license + '\n' + (trailingBlank ? '\n' : '');
Ian Hicksoneae05c72019-11-14 13:19:40 -0800256 final List<String> errors = <String>[];
Yuqian Lifb552ed2020-11-07 05:19:02 -0800257 await for (final File file in _allFiles(workingDirectory, extension, minimumMatches: minimumMatches)) {
Ian Hickson449f4a62019-11-27 15:04:02 -0800258 final String contents = file.readAsStringSync().replaceAll('\r\n', '\n');
259 if (contents.isEmpty)
260 continue; // let's not go down the /bin/true rabbit hole
261 if (!contents.startsWith(licensePattern))
Ian Hicksoneae05c72019-11-14 13:19:40 -0800262 errors.add(file.path);
Alexander Aprelev391e91c2018-08-30 07:30:25 -0700263 }
Ian Hicksoneae05c72019-11-14 13:19:40 -0800264 // Fail if any errors
265 if (errors.isNotEmpty) {
Ian Hickson449f4a62019-11-27 15:04:02 -0800266 final String s = errors.length == 1 ? ' does' : 's do';
Dan Field24f39d42020-01-02 11:47:28 -0800267 exitWithError(<String>[
268 '${bold}The following ${errors.length} file$s not have the right license header:$reset',
269 ...errors,
270 'The expected license header is:',
271 license,
272 if (trailingBlank) '...followed by a blank line.',
273 ]);
xsterc7a09a42019-04-24 12:40:17 -0700274 }
Alexander Aprelev391e91c2018-08-30 07:30:25 -0700275}
276
Ian Hicksoneae05c72019-11-14 13:19:40 -0800277final RegExp _testImportPattern = RegExp(r'''import (['"])([^'"]+_test\.dart)\1''');
278const Set<String> _exemptTestImports = <String>{
279 'package:flutter_test/flutter_test.dart',
280 'hit_test.dart',
281 'package:test_api/src/backend/live_test.dart',
Dan Field76784652020-11-05 17:28:47 -0800282 'package:integration_test/integration_test.dart',
Ian Hicksoneae05c72019-11-14 13:19:40 -0800283};
Alexander Aprelev391e91c2018-08-30 07:30:25 -0700284
Ian Hicksoneae05c72019-11-14 13:19:40 -0800285Future<void> verifyNoTestImports(String workingDirectory) async {
286 final List<String> errors = <String>[];
287 assert("// foo\nimport 'binding_test.dart' as binding;\n'".contains(_testImportPattern));
Yuqian Lifb552ed2020-11-07 05:19:02 -0800288 final List<File> dartFiles = await _allFiles(path.join(workingDirectory, 'packages'), 'dart', minimumMatches: 1500).toList();
Alexandre Ardhuin4f9b6cf2020-01-07 16:32:04 +0100289 for (final File file in dartFiles) {
290 for (final String line in file.readAsLinesSync()) {
Ian Hicksoneae05c72019-11-14 13:19:40 -0800291 final Match match = _testImportPattern.firstMatch(line);
292 if (match != null && !_exemptTestImports.contains(match.group(2)))
293 errors.add(file.path);
Alexander Aprelev391e91c2018-08-30 07:30:25 -0700294 }
295 }
Ian Hicksoneae05c72019-11-14 13:19:40 -0800296 // Fail if any errors
297 if (errors.isNotEmpty) {
Ian Hicksoneae05c72019-11-14 13:19:40 -0800298 final String s = errors.length == 1 ? '' : 's';
Dan Field24f39d42020-01-02 11:47:28 -0800299 exitWithError(<String>[
300 '${bold}The following file$s import a test directly. Test utilities should be in their own file.$reset',
301 ...errors,
302 ]);
Alexander Aprelev391e91c2018-08-30 07:30:25 -0700303 }
Alexander Aprelev391e91c2018-08-30 07:30:25 -0700304}
305
Ian Hicksoneae05c72019-11-14 13:19:40 -0800306Future<void> verifyNoBadImportsInFlutter(String workingDirectory) async {
Alexander Aprelev391e91c2018-08-30 07:30:25 -0700307 final List<String> errors = <String>[];
308 final String libPath = path.join(workingDirectory, 'packages', 'flutter', 'lib');
309 final String srcPath = path.join(workingDirectory, 'packages', 'flutter', 'lib', 'src');
310 // Verify there's one libPath/*.dart for each srcPath/*/.
Alexandre Ardhuind927c932018-09-12 08:29:29 +0200311 final List<String> packages = Directory(libPath).listSync()
Alexander Aprelev391e91c2018-08-30 07:30:25 -0700312 .where((FileSystemEntity entity) => entity is File && path.extension(entity.path) == '.dart')
313 .map<String>((FileSystemEntity entity) => path.basenameWithoutExtension(entity.path))
314 .toList()..sort();
Alexandre Ardhuind927c932018-09-12 08:29:29 +0200315 final List<String> directories = Directory(srcPath).listSync()
Alexander Aprelev391e91c2018-08-30 07:30:25 -0700316 .whereType<Directory>()
317 .map<String>((Directory entity) => path.basename(entity.path))
318 .toList()..sort();
Ian Hicksoneae05c72019-11-14 13:19:40 -0800319 if (!_listEquals<String>(packages, directories)) {
Alexander Aprelev391e91c2018-08-30 07:30:25 -0700320 errors.add(
321 'flutter/lib/*.dart does not match flutter/lib/src/*/:\n'
322 'These are the exported packages:\n' +
Alexandre Ardhuinf62afdc2018-10-01 21:29:08 +0200323 packages.map<String>((String path) => ' lib/$path.dart').join('\n') +
Alexander Aprelev391e91c2018-08-30 07:30:25 -0700324 'These are the directories:\n' +
Alexandre Ardhuinf62afdc2018-10-01 21:29:08 +0200325 directories.map<String>((String path) => ' lib/src/$path/').join('\n')
Alexander Aprelev391e91c2018-08-30 07:30:25 -0700326 );
327 }
328 // Verify that the imports are well-ordered.
329 final Map<String, Set<String>> dependencyMap = <String, Set<String>>{};
Alexandre Ardhuin4f9b6cf2020-01-07 16:32:04 +0100330 for (final String directory in directories) {
Yuqian Lifb552ed2020-11-07 05:19:02 -0800331 dependencyMap[directory] = await _findFlutterDependencies(path.join(srcPath, directory), errors, checkForMeta: directory != 'foundation');
Alexander Aprelev391e91c2018-08-30 07:30:25 -0700332 }
Ian Hickson58939b72019-02-12 12:29:36 -0800333 assert(dependencyMap['material'].contains('widgets') &&
334 dependencyMap['widgets'].contains('rendering') &&
335 dependencyMap['rendering'].contains('painting')); // to make sure we're convinced _findFlutterDependencies is finding some
Alexandre Ardhuin4f9b6cf2020-01-07 16:32:04 +0100336 for (final String package in dependencyMap.keys) {
Alexander Aprelev391e91c2018-08-30 07:30:25 -0700337 if (dependencyMap[package].contains(package)) {
338 errors.add(
339 'One of the files in the $yellow$package$reset package imports that package recursively.'
340 );
341 }
342 }
LongCatIsLooongd291de02020-01-09 10:25:58 -0800343
344 for (final String key in dependencyMap.keys) {
345 for (final String dependency in dependencyMap[key]) {
346 if (dependencyMap[dependency] != null)
347 continue;
348 // Sanity check before performing _deepSearch, to ensure there's no rogue
349 // dependencies.
350 final String validFilenames = dependencyMap.keys.map((String name) => name + '.dart').join(', ');
351 errors.add(
352 '$key imported package:flutter/$dependency.dart '
353 'which is not one of the valid exports { $validFilenames }.\n'
354 'Consider changing $dependency.dart to one of them.'
355 );
356 }
357 }
358
Alexandre Ardhuin4f9b6cf2020-01-07 16:32:04 +0100359 for (final String package in dependencyMap.keys) {
Alexandre Ardhuinf62afdc2018-10-01 21:29:08 +0200360 final List<String> loop = _deepSearch<String>(dependencyMap, package);
Alexander Aprelev391e91c2018-08-30 07:30:25 -0700361 if (loop != null) {
362 errors.add(
363 '${yellow}Dependency loop:$reset ' +
364 loop.join(' depends on ')
365 );
366 }
367 }
368 // Fail if any errors
369 if (errors.isNotEmpty) {
Dan Field24f39d42020-01-02 11:47:28 -0800370 exitWithError(<String>[
371 if (errors.length == 1)
372 '${bold}An error was detected when looking at import dependencies within the Flutter package:$reset'
373 else
374 '${bold}Multiple errors were detected when looking at import dependencies within the Flutter package:$reset',
375 ...errors,
376 ]);
Alexander Aprelev391e91c2018-08-30 07:30:25 -0700377 }
378}
379
Ian Hicksoneae05c72019-11-14 13:19:40 -0800380Future<void> verifyNoBadImportsInFlutterTools(String workingDirectory) async {
381 final List<String> errors = <String>[];
Yuqian Lifb552ed2020-11-07 05:19:02 -0800382 final List<File> files = await _allFiles(path.join(workingDirectory, 'packages', 'flutter_tools', 'lib'), 'dart', minimumMatches: 200).toList();
Alexandre Ardhuin4f9b6cf2020-01-07 16:32:04 +0100383 for (final File file in files) {
Ian Hicksoneae05c72019-11-14 13:19:40 -0800384 if (file.readAsStringSync().contains('package:flutter_tools/')) {
385 errors.add('$yellow${file.path}$reset imports flutter_tools.');
386 }
387 }
388 // Fail if any errors
389 if (errors.isNotEmpty) {
Dan Field24f39d42020-01-02 11:47:28 -0800390 exitWithError(<String>[
391 if (errors.length == 1)
392 '${bold}An error was detected when looking at import dependencies within the flutter_tools package:$reset'
393 else
394 '${bold}Multiple errors were detected when looking at import dependencies within the flutter_tools package:$reset',
395 ...errors.map((String paragraph) => '$paragraph\n'),
396 ]);
Ian Hicksoneae05c72019-11-14 13:19:40 -0800397 }
398}
399
400Future<void> verifyInternationalizations() async {
401 final EvalResult materialGenResult = await _evalCommand(
402 dart,
403 <String>[
Shi-Hao Hong7874bca2019-12-16 17:30:57 -0800404 path.join('dev', 'tools', 'localization', 'bin', 'gen_localizations.dart'),
Ian Hicksoneae05c72019-11-14 13:19:40 -0800405 '--material',
406 ],
407 workingDirectory: flutterRoot,
408 );
409 final EvalResult cupertinoGenResult = await _evalCommand(
410 dart,
411 <String>[
Shi-Hao Hong7874bca2019-12-16 17:30:57 -0800412 path.join('dev', 'tools', 'localization', 'bin', 'gen_localizations.dart'),
Ian Hicksoneae05c72019-11-14 13:19:40 -0800413 '--cupertino',
414 ],
415 workingDirectory: flutterRoot,
416 );
417
418 final String materialLocalizationsFile = path.join('packages', 'flutter_localizations', 'lib', 'src', 'l10n', 'generated_material_localizations.dart');
419 final String cupertinoLocalizationsFile = path.join('packages', 'flutter_localizations', 'lib', 'src', 'l10n', 'generated_cupertino_localizations.dart');
420 final String expectedMaterialResult = await File(materialLocalizationsFile).readAsString();
421 final String expectedCupertinoResult = await File(cupertinoLocalizationsFile).readAsString();
422
423 if (materialGenResult.stdout.trim() != expectedMaterialResult.trim()) {
Dan Field24f39d42020-01-02 11:47:28 -0800424 exitWithError(<String>[
425 '<<<<<<< $materialLocalizationsFile',
426 expectedMaterialResult.trim(),
427 '=======',
428 materialGenResult.stdout.trim(),
429 '>>>>>>> gen_localizations',
430 'The contents of $materialLocalizationsFile are different from that produced by gen_localizations.',
431 '',
432 'Did you forget to run gen_localizations.dart after updating a .arb file?',
433 ]);
Ian Hicksoneae05c72019-11-14 13:19:40 -0800434 }
435 if (cupertinoGenResult.stdout.trim() != expectedCupertinoResult.trim()) {
Dan Field24f39d42020-01-02 11:47:28 -0800436 exitWithError(<String>[
437 '<<<<<<< $cupertinoLocalizationsFile',
438 expectedCupertinoResult.trim(),
439 '=======',
440 cupertinoGenResult.stdout.trim(),
441 '>>>>>>> gen_localizations',
442 'The contents of $cupertinoLocalizationsFile are different from that produced by gen_localizations.',
443 '',
444 'Did you forget to run gen_localizations.dart after updating a .arb file?',
445 ]);
Ian Hicksoneae05c72019-11-14 13:19:40 -0800446 }
447}
448
Dan Field3e634112020-01-14 16:43:01 -0800449Future<void> verifyNoRuntimeTypeInToString(String workingDirectory) async {
450 final String flutterLib = path.join(workingDirectory, 'packages', 'flutter', 'lib');
451 final Set<String> excludedFiles = <String>{
452 path.join(flutterLib, 'src', 'foundation', 'object.dart'), // Calls this from within an assert.
453 };
Yuqian Lifb552ed2020-11-07 05:19:02 -0800454 final List<File> files = await _allFiles(flutterLib, 'dart', minimumMatches: 400)
Dan Field3e634112020-01-14 16:43:01 -0800455 .where((File file) => !excludedFiles.contains(file.path))
456 .toList();
457 final RegExp toStringRegExp = RegExp(r'^\s+String\s+to(.+?)?String(.+?)?\(\)\s+(\{|=>)');
458 final List<String> problems = <String>[];
459 for (final File file in files) {
460 final List<String> lines = file.readAsLinesSync();
461 for (int index = 0; index < lines.length; index++) {
462 if (toStringRegExp.hasMatch(lines[index])) {
463 final int sourceLine = index + 1;
464 bool _checkForRuntimeType(String line) {
465 if (line.contains(r'$runtimeType') || line.contains('runtimeType.toString()')) {
466 problems.add('${file.path}:$sourceLine}: toString calls runtimeType.toString');
467 return true;
468 }
469 return false;
470 }
471 if (_checkForRuntimeType(lines[index])) {
472 continue;
473 }
474 if (lines[index].contains('=>')) {
475 while (!lines[index].contains(';')) {
476 index++;
477 assert(index < lines.length, 'Source file $file has unterminated toString method.');
478 if (_checkForRuntimeType(lines[index])) {
479 break;
480 }
481 }
482 } else {
483 int openBraceCount = '{'.allMatches(lines[index]).length - '}'.allMatches(lines[index]).length;
484 while (!lines[index].contains('}') && openBraceCount > 0) {
485 index++;
486 assert(index < lines.length, 'Source file $file has unbalanced braces in a toString method.');
487 if (_checkForRuntimeType(lines[index])) {
488 break;
489 }
490 openBraceCount += '{'.allMatches(lines[index]).length;
491 openBraceCount -= '}'.allMatches(lines[index]).length;
492 }
493 }
494 }
495 }
496 }
497 if (problems.isNotEmpty)
498 exitWithError(problems);
499}
500
Dan Field24f39d42020-01-02 11:47:28 -0800501Future<void> verifyNoTrailingSpaces(String workingDirectory, { int minimumMatches = 4000 }) async {
Yuqian Lifb552ed2020-11-07 05:19:02 -0800502 final List<File> files = await _allFiles(workingDirectory, null, minimumMatches: minimumMatches)
Dan Field24f39d42020-01-02 11:47:28 -0800503 .where((File file) => path.basename(file.path) != 'serviceaccount.enc')
504 .where((File file) => path.basename(file.path) != 'Ahem.ttf')
505 .where((File file) => path.extension(file.path) != '.snapshot')
506 .where((File file) => path.extension(file.path) != '.png')
507 .where((File file) => path.extension(file.path) != '.jpg')
stuartmorgan685e9d12020-03-23 10:42:26 -0700508 .where((File file) => path.extension(file.path) != '.ico')
Dan Field24f39d42020-01-02 11:47:28 -0800509 .where((File file) => path.extension(file.path) != '.jar')
Jonah Williams0b3f5cf2020-04-21 20:39:36 -0700510 .where((File file) => path.extension(file.path) != '.swp')
Dan Field24f39d42020-01-02 11:47:28 -0800511 .toList();
512 final List<String> problems = <String>[];
Alexandre Ardhuin4f9b6cf2020-01-07 16:32:04 +0100513 for (final File file in files) {
Dan Field24f39d42020-01-02 11:47:28 -0800514 final List<String> lines = file.readAsLinesSync();
515 for (int index = 0; index < lines.length; index += 1) {
516 if (lines[index].endsWith(' ')) {
517 problems.add('${file.path}:${index + 1}: trailing U+0020 space character');
518 } else if (lines[index].endsWith('\t')) {
519 problems.add('${file.path}:${index + 1}: trailing U+0009 tab character');
520 }
Ian Hicksone768c922019-12-30 17:12:19 -0800521 }
Dan Field24f39d42020-01-02 11:47:28 -0800522 if (lines.isNotEmpty && lines.last == '')
523 problems.add('${file.path}:${lines.length}: trailing blank line');
524 }
525 if (problems.isNotEmpty)
526 exitWithError(problems);
527}
528
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200529@immutable
Dan Field24f39d42020-01-02 11:47:28 -0800530class Hash256 {
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200531 const Hash256(this.a, this.b, this.c, this.d);
Dan Field24f39d42020-01-02 11:47:28 -0800532
533 factory Hash256.fromDigest(Digest digest) {
534 assert(digest.bytes.length == 32);
535 return Hash256(
536 digest.bytes[ 0] << 56 |
537 digest.bytes[ 1] << 48 |
538 digest.bytes[ 2] << 40 |
539 digest.bytes[ 3] << 32 |
540 digest.bytes[ 4] << 24 |
541 digest.bytes[ 5] << 16 |
542 digest.bytes[ 6] << 8 |
543 digest.bytes[ 7] << 0,
544 digest.bytes[ 8] << 56 |
545 digest.bytes[ 9] << 48 |
546 digest.bytes[10] << 40 |
547 digest.bytes[11] << 32 |
548 digest.bytes[12] << 24 |
549 digest.bytes[13] << 16 |
550 digest.bytes[14] << 8 |
551 digest.bytes[15] << 0,
552 digest.bytes[16] << 56 |
553 digest.bytes[17] << 48 |
554 digest.bytes[18] << 40 |
555 digest.bytes[19] << 32 |
556 digest.bytes[20] << 24 |
557 digest.bytes[21] << 16 |
558 digest.bytes[22] << 8 |
559 digest.bytes[23] << 0,
560 digest.bytes[24] << 56 |
561 digest.bytes[25] << 48 |
562 digest.bytes[26] << 40 |
563 digest.bytes[27] << 32 |
564 digest.bytes[28] << 24 |
565 digest.bytes[29] << 16 |
566 digest.bytes[30] << 8 |
567 digest.bytes[31] << 0,
568 );
569 }
570
571 final int a;
572 final int b;
573 final int c;
574 final int d;
575
576 @override
577 bool operator ==(Object other) {
578 if (other.runtimeType != runtimeType)
579 return false;
580 return other is Hash256
581 && other.a == a
582 && other.b == b
583 && other.c == c
584 && other.d == d;
585 }
586
587 @override
588 int get hashCode => a ^ b ^ c ^ d;
589}
590
591// DO NOT ADD ANY ENTRIES TO THIS LIST.
592// We have a policy of not checking in binaries into this repository.
stuartmorgan685e9d12020-03-23 10:42:26 -0700593// If you are adding/changing template images, use the flutter_template_images
594// package and a .img.tmpl placeholder instead.
595// If you have other binaries to add, please consult Hixie for advice.
Michael Goderbauer584fd5f2020-06-16 09:15:43 -0700596final Set<Hash256> _legacyBinaries = <Hash256>{
Dan Field24f39d42020-01-02 11:47:28 -0800597 // DEFAULT ICON IMAGES
598
599 // packages/flutter_tools/templates/app/android.tmpl/app/src/main/res/mipmap-hdpi/ic_launcher.png
600 // packages/flutter_tools/templates/module/android/host_app_common/app.tmpl/src/main/res/mipmap-hdpi/ic_launcher.png
601 // (also used by many examples)
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200602 const Hash256(0x6A7C8F0D703E3682, 0x108F9662F8133022, 0x36240D3F8F638BB3, 0x91E32BFB96055FEF),
Dan Field24f39d42020-01-02 11:47:28 -0800603
604 // packages/flutter_tools/templates/app/android.tmpl/app/src/main/res/mipmap-mdpi/ic_launcher.png
605 // (also used by many examples)
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200606 const Hash256(0xC7C0C0189145E4E3, 0x2A401C61C9BDC615, 0x754B0264E7AFAE24, 0xE834BB81049EAF81),
Dan Field24f39d42020-01-02 11:47:28 -0800607
608 // packages/flutter_tools/templates/app/android.tmpl/app/src/main/res/mipmap-xhdpi/ic_launcher.png
609 // (also used by many examples)
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200610 const Hash256(0xE14AA40904929BF3, 0x13FDED22CF7E7FFC, 0xBF1D1AAC4263B5EF, 0x1BE8BFCE650397AA),
Dan Field24f39d42020-01-02 11:47:28 -0800611
612 // packages/flutter_tools/templates/app/android.tmpl/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
613 // (also used by many examples)
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200614 const Hash256(0x4D470BF22D5C17D8, 0x4EDC5F82516D1BA8, 0xA1C09559CD761CEF, 0xB792F86D9F52B540),
Dan Field24f39d42020-01-02 11:47:28 -0800615
616 // packages/flutter_tools/templates/app/android.tmpl/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
617 // (also used by many examples)
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200618 const Hash256(0x3C34E1F298D0C9EA, 0x3455D46DB6B7759C, 0x8211A49E9EC6E44B, 0x635FC5C87DFB4180),
Dan Field24f39d42020-01-02 11:47:28 -0800619
620 // packages/flutter_tools/templates/app/ios.tmpl/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
621 // packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
622 // (also used by a few examples)
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200623 const Hash256(0x7770183009E91411, 0x2DE7D8EF1D235A6A, 0x30C5834424858E0D, 0x2F8253F6B8D31926),
Dan Field24f39d42020-01-02 11:47:28 -0800624
625 // packages/flutter_tools/templates/app/ios.tmpl/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
626 // packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
627 // (also used by many examples)
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200628 const Hash256(0x5925DAB509451F9E, 0xCBB12CE8A625F9D4, 0xC104718EE20CAFF8, 0xB1B51032D1CD8946),
Dan Field24f39d42020-01-02 11:47:28 -0800629
630 // packages/flutter_tools/templates/app/ios.tmpl/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
631 // packages/flutter_tools/templates/app/ios.tmpl/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
632 // packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
633 // packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
634 // (also used by many examples)
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200635 const Hash256(0xC4D9A284C12301D0, 0xF50E248EC53ED51A, 0x19A10147B774B233, 0x08399250B0D44C55),
Dan Field24f39d42020-01-02 11:47:28 -0800636
637 // packages/flutter_tools/templates/app/ios.tmpl/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
638 // packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
639 // (also used by many examples)
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200640 const Hash256(0xBF97F9D3233F33E1, 0x389B09F7B8ADD537, 0x41300CB834D6C7A5, 0xCA32CBED363A4FB2),
Dan Field24f39d42020-01-02 11:47:28 -0800641
642 // packages/flutter_tools/templates/app/ios.tmpl/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
643 // packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
644 // (also used by many examples)
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200645 const Hash256(0x285442F69A06B45D, 0x9D79DF80321815B5, 0x46473548A37B7881, 0x9B68959C7B8ED237),
Dan Field24f39d42020-01-02 11:47:28 -0800646
647 // packages/flutter_tools/templates/app/ios.tmpl/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
648 // packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
649 // (also used by many examples)
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200650 const Hash256(0x2AB64AF8AC727EA9, 0x9C6AB9EAFF847F46, 0xFBF2A9A0A78A0ABC, 0xBF3180F3851645B4),
Dan Field24f39d42020-01-02 11:47:28 -0800651
652 // packages/flutter_tools/templates/app/ios.tmpl/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
653 // packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
654 // (also used by many examples)
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200655 const Hash256(0x9DCA09F4E5ED5684, 0xD3C4DFF41F4E8B7C, 0xB864B438172D72BE, 0x069315FA362930F9),
Dan Field24f39d42020-01-02 11:47:28 -0800656
657 // packages/flutter_tools/templates/app/ios.tmpl/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
658 // packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
659 // (also used by many examples)
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200660 const Hash256(0xD5AD04DE321EF37C, 0xACC5A7B960AFCCE7, 0x1BDCB96FA020C482, 0x49C1545DD1A0F497),
Dan Field24f39d42020-01-02 11:47:28 -0800661
662 // packages/flutter_tools/templates/app/ios.tmpl/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
663 // packages/flutter_tools/templates/app/ios.tmpl/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
664 // packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
665 // packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
666 // (also used by many examples)
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200667 const Hash256(0x809ABFE75C440770, 0xC13C4E2E46D09603, 0xC22053E9D4E0E227, 0x5DCB9C1DCFBB2C75),
Dan Field24f39d42020-01-02 11:47:28 -0800668
669 // packages/flutter_tools/templates/app/ios.tmpl/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
670 // packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
671 // (also used by many examples)
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200672 const Hash256(0x3DB08CB79E7B01B9, 0xE81F956E3A0AE101, 0x48D0FAFDE3EA7AA7, 0x0048DF905AA52CFD),
Dan Field24f39d42020-01-02 11:47:28 -0800673
674 // packages/flutter_tools/templates/app/ios.tmpl/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
675 // packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
676 // (also used by many examples)
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200677 const Hash256(0x23C13D463F5DCA5C, 0x1F14A14934003601, 0xC29F1218FD461016, 0xD8A22CEF579A665F),
Dan Field24f39d42020-01-02 11:47:28 -0800678
679 // packages/flutter_tools/templates/app/ios.tmpl/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
680 // packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
681 // (also used by many examples)
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200682 const Hash256(0x6DB7726530D71D3F, 0x52CB59793EB69131, 0x3BAA04796E129E1E, 0x043C0A58A1BFFD2F),
Dan Field24f39d42020-01-02 11:47:28 -0800683
684 // packages/flutter_tools/templates/app/ios.tmpl/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
685 // packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
686 // (also used by many examples)
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200687 const Hash256(0xCEE565F5E6211656, 0x9B64980B209FD5CA, 0x4B3D3739011F5343, 0x250B33A1A2C6EB65),
Dan Field24f39d42020-01-02 11:47:28 -0800688
689 // packages/flutter_tools/templates/app/ios.tmpl/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
690 // packages/flutter_tools/templates/app/ios.tmpl/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
691 // packages/flutter_tools/templates/app/ios.tmpl/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
692 // packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
693 // packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
694 // packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
695 // (also used by many examples)
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200696 const Hash256(0x93AE7D494FAD0FB3, 0x0CBF3AE746A39C4B, 0xC7A0F8BBF87FBB58, 0x7A3F3C01F3C5CE20),
Dan Field24f39d42020-01-02 11:47:28 -0800697
698 // packages/flutter_tools/templates/app/macos.tmpl/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png
699 // (also used by a few examples)
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200700 const Hash256(0xB18BEBAAD1AD6724, 0xE48BCDF699BA3927, 0xDF3F258FEBE646A3, 0xAB5C62767C6BAB40),
Dan Field24f39d42020-01-02 11:47:28 -0800701
702 // packages/flutter_tools/templates/app/macos.tmpl/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png
703 // (also used by a few examples)
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200704 const Hash256(0xF90D839A289ECADB, 0xF2B0B3400DA43EB8, 0x08B84908335AE4A0, 0x07457C4D5A56A57C),
Dan Field24f39d42020-01-02 11:47:28 -0800705
706 // packages/flutter_tools/templates/app/macos.tmpl/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png
707 // (also used by a few examples)
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200708 const Hash256(0x592C2ABF84ADB2D3, 0x91AED8B634D3233E, 0x2C65369F06018DCD, 0x8A4B27BA755EDCBE),
Dan Field24f39d42020-01-02 11:47:28 -0800709
710 // packages/flutter_tools/templates/app/macos.tmpl/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png
711 // (also used by a few examples)
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200712 const Hash256(0x75D9A0C034113CA8, 0xA1EC11C24B81F208, 0x6630A5A5C65C7D26, 0xA5DC03A1C0A4478C),
Dan Field24f39d42020-01-02 11:47:28 -0800713
714 // packages/flutter_tools/templates/app/macos.tmpl/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png
715 // (also used by a few examples)
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200716 const Hash256(0xA896E65745557732, 0xC72BD4EE3A10782F, 0xE2AA95590B5AF659, 0x869E5808DB9C01C1),
Dan Field24f39d42020-01-02 11:47:28 -0800717
718 // packages/flutter_tools/templates/app/macos.tmpl/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png
719 // (also used by a few examples)
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200720 const Hash256(0x3A69A8A1AAC5D9A8, 0x374492AF4B6D07A4, 0xCE637659EB24A784, 0x9C4DFB261D75C6A3),
Dan Field24f39d42020-01-02 11:47:28 -0800721
722 // packages/flutter_tools/templates/app/macos.tmpl/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png
723 // (also used by a few examples)
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200724 const Hash256(0xD29D4E0AF9256DC9, 0x2D0A8F8810608A5E, 0x64A132AD8B397CA2, 0xC4DDC0B1C26A68C3),
Dan Field24f39d42020-01-02 11:47:28 -0800725
Jonah Williams5d30c092020-01-10 09:37:20 -0800726 // packages/flutter_tools/templates/app/web/icons/Icon-192.png.copy.tmpl
Michael Thomsene1671812020-03-16 02:31:42 -0700727 // dev/integration_tests/flutter_gallery/web/icons/Icon-192.png
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200728 const Hash256(0x3DCE99077602F704, 0x21C1C6B2A240BC9B, 0x83D64D86681D45F2, 0x154143310C980BE3),
Jonah Williams5d30c092020-01-10 09:37:20 -0800729
730 // packages/flutter_tools/templates/app/web/icons/Icon-512.png.copy.tmpl
Michael Thomsene1671812020-03-16 02:31:42 -0700731 // dev/integration_tests/flutter_gallery/web/icons/Icon-512.png
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200732 const Hash256(0xBACCB205AE45f0B4, 0x21BE1657259B4943, 0xAC40C95094AB877F, 0x3BCBE12CD544DCBE),
Dan Field24f39d42020-01-02 11:47:28 -0800733
Jonah Williamsab426852020-01-28 13:09:18 -0800734 // packages/flutter_tools/templates/app/web/favicon.png.copy.tmpl
Michael Thomsene1671812020-03-16 02:31:42 -0700735 // dev/integration_tests/flutter_gallery/web/favicon.png
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200736 const Hash256(0x7AB2525F4B86B65D, 0x3E4C70358A17E5A1, 0xAAF6F437f99CBCC0, 0x46DAD73d59BB9015),
Jonah Williamsab426852020-01-28 13:09:18 -0800737
Dan Field24f39d42020-01-02 11:47:28 -0800738 // GALLERY ICONS
739
Michael Thomsene1671812020-03-16 02:31:42 -0700740 // dev/integration_tests/flutter_gallery/android/app/src/main/res/mipmap-hdpi/ic_background.png
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200741 const Hash256(0x03CFDE53C249475C, 0x277E8B8E90AC8A13, 0xE5FC13C358A94CCB, 0x67CA866C9862A0DD),
Dan Field24f39d42020-01-02 11:47:28 -0800742
Michael Thomsene1671812020-03-16 02:31:42 -0700743 // dev/integration_tests/flutter_gallery/android/app/src/main/res/mipmap-hdpi/ic_foreground.png
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200744 const Hash256(0x86A83E23A505EFCC, 0x39C358B699EDE12F, 0xC088EE516A1D0C73, 0xF3B5D74DDAD164B1),
Dan Field24f39d42020-01-02 11:47:28 -0800745
Michael Thomsene1671812020-03-16 02:31:42 -0700746 // dev/integration_tests/flutter_gallery/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200747 const Hash256(0xD813B1A77320355E, 0xB68C485CD47D0F0F, 0x3C7E1910DCD46F08, 0x60A6401B8DC13647),
Dan Field24f39d42020-01-02 11:47:28 -0800748
Michael Thomsene1671812020-03-16 02:31:42 -0700749 // dev/integration_tests/flutter_gallery/android/app/src/main/res/mipmap-xhdpi/ic_background.png
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200750 const Hash256(0x35AFA76BD5D6053F, 0xEE927436C78A8794, 0xA8BA5F5D9FC9653B, 0xE5B96567BB7215ED),
Dan Field24f39d42020-01-02 11:47:28 -0800751
Michael Thomsene1671812020-03-16 02:31:42 -0700752 // dev/integration_tests/flutter_gallery/android/app/src/main/res/mipmap-xhdpi/ic_foreground.png
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200753 const Hash256(0x263CE9B4F1F69B43, 0xEBB08AE9FE8F80E7, 0x95647A59EF2C040B, 0xA8AEB246861A7DFF),
Dan Field24f39d42020-01-02 11:47:28 -0800754
Michael Thomsene1671812020-03-16 02:31:42 -0700755 // dev/integration_tests/flutter_gallery/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200756 const Hash256(0x5E1A93C3653BAAFF, 0x1AAC6BCEB8DCBC2F, 0x2AE7D68ECB07E507, 0xCB1FA8354B28313A),
Dan Field24f39d42020-01-02 11:47:28 -0800757
Michael Thomsene1671812020-03-16 02:31:42 -0700758 // dev/integration_tests/flutter_gallery/android/app/src/main/res/mipmap-xxhdpi/ic_background.png
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200759 const Hash256(0xA5C77499151DDEC6, 0xDB40D0AC7321FD74, 0x0646C0C0F786743F, 0x8F3C3C408CAC5E8C),
Dan Field24f39d42020-01-02 11:47:28 -0800760
Michael Thomsene1671812020-03-16 02:31:42 -0700761 // dev/integration_tests/flutter_gallery/android/app/src/main/res/mipmap-xxhdpi/ic_foreground.png
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200762 const Hash256(0x33DE450980A2A16B, 0x1982AC7CDC1E7B01, 0x919E07E0289C2139, 0x65F85BCED8895FEF),
Dan Field24f39d42020-01-02 11:47:28 -0800763
Michael Thomsene1671812020-03-16 02:31:42 -0700764 // dev/integration_tests/flutter_gallery/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200765 const Hash256(0xC3B8577F4A89BA03, 0x830944FB06C3566B, 0x4C99140A2CA52958, 0x089BFDC3079C59B7),
Dan Field24f39d42020-01-02 11:47:28 -0800766
Michael Thomsene1671812020-03-16 02:31:42 -0700767 // dev/integration_tests/flutter_gallery/android/app/src/main/res/mipmap-xxxhdpi/ic_background.png
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200768 const Hash256(0xDEBC241D6F9C5767, 0x8980FDD46FA7ED0C, 0x5B8ACD26BCC5E1BC, 0x473C89B432D467AD),
Dan Field24f39d42020-01-02 11:47:28 -0800769
Michael Thomsene1671812020-03-16 02:31:42 -0700770 // dev/integration_tests/flutter_gallery/android/app/src/main/res/mipmap-xxxhdpi/ic_foreground.png
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200771 const Hash256(0xBEFE5F7E82BF8B64, 0x148D869E3742004B, 0xF821A9F5A1BCDC00, 0x357D246DCC659DC2),
Dan Field24f39d42020-01-02 11:47:28 -0800772
Michael Thomsene1671812020-03-16 02:31:42 -0700773 // dev/integration_tests/flutter_gallery/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200774 const Hash256(0xC385404341FF9EDD, 0x30FBE76F0EC99155, 0x8EA4F4AFE8CC0C60, 0x1CA3EDEF177E1DA8),
Dan Field24f39d42020-01-02 11:47:28 -0800775
Michael Thomsene1671812020-03-16 02:31:42 -0700776 // dev/integration_tests/flutter_gallery/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-1024.png
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200777 const Hash256(0x6BE5751A29F57A80, 0x36A4B31CC542C749, 0x984E49B22BD65CAA, 0x75AE8B2440848719),
Dan Field24f39d42020-01-02 11:47:28 -0800778
Michael Thomsene1671812020-03-16 02:31:42 -0700779 // dev/integration_tests/flutter_gallery/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-120.png
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200780 const Hash256(0x9972A2264BFA8F8D, 0x964AFE799EADC1FA, 0x2247FB31097F994A, 0x1495DC32DF071793),
Dan Field24f39d42020-01-02 11:47:28 -0800781
Michael Thomsene1671812020-03-16 02:31:42 -0700782 // dev/integration_tests/flutter_gallery/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-152.png
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200783 const Hash256(0x4C7CC9B09BEEDA24, 0x45F57D6967753910, 0x57D68E1A6B883D2C, 0x8C52701A74F1400F),
Dan Field24f39d42020-01-02 11:47:28 -0800784
Michael Thomsene1671812020-03-16 02:31:42 -0700785 // dev/integration_tests/flutter_gallery/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-167.png
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200786 const Hash256(0x66DACAC1CFE4D349, 0xDBE994CB9125FFD7, 0x2D795CFC9CF9F739, 0xEDBB06CE25082E9C),
Dan Field24f39d42020-01-02 11:47:28 -0800787
Michael Thomsene1671812020-03-16 02:31:42 -0700788 // dev/integration_tests/flutter_gallery/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-180.png
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200789 const Hash256(0x5188621015EBC327, 0xC9EF63AD76E60ECE, 0xE82BDC3E4ABF09E2, 0xEE0139FA7C0A2BE5),
Dan Field24f39d42020-01-02 11:47:28 -0800790
Michael Thomsene1671812020-03-16 02:31:42 -0700791 // dev/integration_tests/flutter_gallery/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-20.png
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200792 const Hash256(0x27D2752D04EE9A6B, 0x78410E208F74A6CD, 0xC90D9E03B73B8C60, 0xD05F7D623E790487),
Dan Field24f39d42020-01-02 11:47:28 -0800793
Michael Thomsene1671812020-03-16 02:31:42 -0700794 // dev/integration_tests/flutter_gallery/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-29.png
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200795 const Hash256(0xBB20556B2826CF85, 0xD5BAC73AA69C2AC3, 0x8E71DAD64F15B855, 0xB30CB73E0AF89307),
Dan Field24f39d42020-01-02 11:47:28 -0800796
Michael Thomsene1671812020-03-16 02:31:42 -0700797 // dev/integration_tests/flutter_gallery/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-40.png
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200798 const Hash256(0x623820FA45CDB0AC, 0x808403E34AD6A53E, 0xA3E9FDAE83EE0931, 0xB020A3A4EF2CDDE7),
Dan Field24f39d42020-01-02 11:47:28 -0800799
Michael Thomsene1671812020-03-16 02:31:42 -0700800 // dev/integration_tests/flutter_gallery/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-58.png
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200801 const Hash256(0xC6D631D1E107215E, 0xD4A58FEC5F3AA4B5, 0x0AE9724E07114C0C, 0x453E5D87C2CAD3B3),
Dan Field24f39d42020-01-02 11:47:28 -0800802
Michael Thomsene1671812020-03-16 02:31:42 -0700803 // dev/integration_tests/flutter_gallery/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-60.png
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200804 const Hash256(0x4B6F58D1EB8723C6, 0xE717A0D09FEC8806, 0x90C6D1EF4F71836E, 0x618672827979B1A2),
Dan Field24f39d42020-01-02 11:47:28 -0800805
Michael Thomsene1671812020-03-16 02:31:42 -0700806 // dev/integration_tests/flutter_gallery/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-76.png
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200807 const Hash256(0x0A1744CC7634D508, 0xE85DD793331F0C8A, 0x0B7C6DDFE0975D8F, 0x29E91C905BBB1BED),
Dan Field24f39d42020-01-02 11:47:28 -0800808
Michael Thomsene1671812020-03-16 02:31:42 -0700809 // dev/integration_tests/flutter_gallery/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-80.png
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200810 const Hash256(0x24032FBD1E6519D6, 0x0BA93C0D5C189554, 0xF50EAE23756518A2, 0x3FABACF4BD5DAF08),
Dan Field24f39d42020-01-02 11:47:28 -0800811
Michael Thomsene1671812020-03-16 02:31:42 -0700812 // dev/integration_tests/flutter_gallery/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-87.png
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200813 const Hash256(0xC17BAE6DF6BB234A, 0xE0AF4BEB0B805F12, 0x14E74EB7AA9A30F1, 0x5763689165DA7DDF),
Dan Field24f39d42020-01-02 11:47:28 -0800814
815
816 // STOCKS ICONS
817
Greg Spencer4b4cff92020-01-30 09:31:07 -0800818 // dev/benchmarks/test_apps/stocks/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200819 const Hash256(0x74052AB5241D4418, 0x7085180608BC3114, 0xD12493C50CD8BBC7, 0x56DED186C37ACE84),
Dan Field24f39d42020-01-02 11:47:28 -0800820
Greg Spencer4b4cff92020-01-30 09:31:07 -0800821 // dev/benchmarks/test_apps/stocks/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200822 const Hash256(0xE37947332E3491CB, 0x82920EE86A086FEA, 0xE1E0A70B3700A7DA, 0xDCAFBDD8F40E2E19),
Dan Field24f39d42020-01-02 11:47:28 -0800823
Greg Spencer4b4cff92020-01-30 09:31:07 -0800824 // dev/benchmarks/test_apps/stocks/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200825 const Hash256(0xE608CDFC0C8579FB, 0xE38873BAAF7BC944, 0x9C9D2EE3685A4FAE, 0x671EF0C8BC41D17C),
Dan Field24f39d42020-01-02 11:47:28 -0800826
Greg Spencer4b4cff92020-01-30 09:31:07 -0800827 // dev/benchmarks/test_apps/stocks/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200828 const Hash256(0xBD53D86977DF9C54, 0xF605743C5ABA114C, 0x9D51D1A8BB917E1A, 0x14CAA26C335CAEBD),
Dan Field24f39d42020-01-02 11:47:28 -0800829
Greg Spencer4b4cff92020-01-30 09:31:07 -0800830 // dev/benchmarks/test_apps/stocks/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200831 const Hash256(0x64E4D02262C4F3D0, 0xBB4FDC21CD0A816C, 0x4CD2A0194E00FB0F, 0x1C3AE4142FAC0D15),
Dan Field24f39d42020-01-02 11:47:28 -0800832
Greg Spencer4b4cff92020-01-30 09:31:07 -0800833 // dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png
834 // dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-Small-40@3x.png
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200835 const Hash256(0x5BA3283A76918FC0, 0xEE127D0F22D7A0B6, 0xDF03DAED61669427, 0x93D89DDD87A08117),
Dan Field24f39d42020-01-02 11:47:28 -0800836
Greg Spencer4b4cff92020-01-30 09:31:07 -0800837 // dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200838 const Hash256(0xCD7F26ED31DEA42A, 0x535D155EC6261499, 0x34E6738255FDB2C4, 0xBD8D4BDDE9A99B05),
Dan Field24f39d42020-01-02 11:47:28 -0800839
Greg Spencer4b4cff92020-01-30 09:31:07 -0800840 // dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-76.png
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200841 const Hash256(0x3FA1225FC9A96A7E, 0xCD071BC42881AB0E, 0x7747EB72FFB72459, 0xA37971BBAD27EE24),
Dan Field24f39d42020-01-02 11:47:28 -0800842
Greg Spencer4b4cff92020-01-30 09:31:07 -0800843 // dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200844 const Hash256(0xCD867001ACD7BBDB, 0x25CDFD452AE89FA2, 0x8C2DC980CAF55F48, 0x0B16C246CFB389BC),
Dan Field24f39d42020-01-02 11:47:28 -0800845
Greg Spencer4b4cff92020-01-30 09:31:07 -0800846 // dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-83.5@2x.png
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200847 const Hash256(0x848E9736E5C4915A, 0x7945BCF6B32FD56B, 0x1F1E7CDDD914352E, 0xC9681D38EF2A70DA),
Dan Field24f39d42020-01-02 11:47:28 -0800848
Greg Spencer4b4cff92020-01-30 09:31:07 -0800849 // dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-Notification.png
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200850 const Hash256(0x654BA7D6C4E05CA0, 0x7799878884EF8F11, 0xA383E1F24CEF5568, 0x3C47604A966983C8),
Dan Field24f39d42020-01-02 11:47:28 -0800851
Greg Spencer4b4cff92020-01-30 09:31:07 -0800852 // dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-Notification@2x.png
853 // dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-Small-40.png
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200854 const Hash256(0x743056FE7D83FE42, 0xA2990825B6AD0415, 0x1AF73D0D43B227AA, 0x07EBEA9B767381D9),
Dan Field24f39d42020-01-02 11:47:28 -0800855
Greg Spencer4b4cff92020-01-30 09:31:07 -0800856 // dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-Notification@3x.png
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200857 const Hash256(0xA7E1570812D119CF, 0xEF4B602EF28DD0A4, 0x100D066E66F5B9B9, 0x881765DC9303343B),
Dan Field24f39d42020-01-02 11:47:28 -0800858
Greg Spencer4b4cff92020-01-30 09:31:07 -0800859 // dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-Small-40@2x.png
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200860 const Hash256(0xB4102839A1E41671, 0x62DACBDEFA471953, 0xB1EE89A0AB7594BE, 0x1D9AC1E67DC2B2CE),
Dan Field24f39d42020-01-02 11:47:28 -0800861
Greg Spencer4b4cff92020-01-30 09:31:07 -0800862 // dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-Small.png
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200863 const Hash256(0x70AC6571B593A967, 0xF1CBAEC9BC02D02D, 0x93AD766D8290ADE6, 0x840139BF9F219019),
Dan Field24f39d42020-01-02 11:47:28 -0800864
Greg Spencer4b4cff92020-01-30 09:31:07 -0800865 // dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200866 const Hash256(0x5D87A78386DA2C43, 0xDDA8FEF2CA51438C, 0xE5A276FE28C6CF0A, 0xEBE89085B56665B6),
Dan Field24f39d42020-01-02 11:47:28 -0800867
Greg Spencer4b4cff92020-01-30 09:31:07 -0800868 // dev/benchmarks/test_apps/stocks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-Small@3x.png
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200869 const Hash256(0x4D9F5E81F668DA44, 0xB20A77F8BF7BA2E1, 0xF384533B5AD58F07, 0xB3A2F93F8635CD96),
Dan Field24f39d42020-01-02 11:47:28 -0800870
871
872 // LEGACY ICONS
873
874 // dev/benchmarks/complex_layout/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@3x.png
875 // dev/benchmarks/microbenchmarks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@3x.png
876 // examples/flutter_view/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@3x.png
877 // (not really sure where this came from, or why neither the template nor most examples use them)
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200878 const Hash256(0x6E645DC9ED913AAD, 0xB50ED29EEB16830D, 0xB32CA12F39121DB9, 0xB7BC1449DDDBF8B8),
Dan Field24f39d42020-01-02 11:47:28 -0800879
880 // dev/benchmarks/macrobenchmarks/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
881 // dev/integration_tests/codegen/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
882 // dev/integration_tests/ios_add2app/ios_add2app/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
883 // dev/integration_tests/release_smoke_test/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200884 const Hash256(0xDEFAC77E08EC71EC, 0xA04CCA3C95D1FC33, 0xB9F26E1CB15CB051, 0x47DEFC79CDD7C158),
Dan Field24f39d42020-01-02 11:47:28 -0800885
886 // examples/flutter_view/ios/Runner/ic_add.png
887 // examples/platform_view/ios/Runner/ic_add.png
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200888 const Hash256(0x3CCE7450334675E2, 0xE3AABCA20B028993, 0x127BE82FE0EB3DFF, 0x8B027B3BAF052F2F),
Dan Field24f39d42020-01-02 11:47:28 -0800889
890 // examples/image_list/images/coast.jpg
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200891 const Hash256(0xDA957FD30C51B8D2, 0x7D74C2C918692DC4, 0xD3C5C99BB00F0D6B, 0x5EBB30395A6EDE82),
Dan Field24f39d42020-01-02 11:47:28 -0800892
893 // examples/image_list/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200894 const Hash256(0xB5792CA06F48A431, 0xD4379ABA2160BD5D, 0xE92339FC64C6A0D3, 0x417AA359634CD905),
Dan Field24f39d42020-01-02 11:47:28 -0800895
896
897 // TEST ASSETS
898
899 // dev/benchmarks/macrobenchmarks/assets/999x1000.png
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200900 const Hash256(0x553E9C36DFF3E610, 0x6A608BDE822A0019, 0xDE4F1769B6FBDB97, 0xBC3C20E26B839F59),
Dan Field24f39d42020-01-02 11:47:28 -0800901
902 // dev/bots/test/analyze-test-input/root/packages/foo/serviceaccount.enc
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200903 const Hash256(0xA8100AE6AA1940D0, 0xB663BB31CD466142, 0xEBBDBD5187131B92, 0xD93818987832EB89),
Dan Field24f39d42020-01-02 11:47:28 -0800904
905 // dev/automated_tests/icon/test.png
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200906 const Hash256(0xE214B4A0FEEEC6FA, 0x8E7AA8CC9BFBEC40, 0xBCDAC2F2DEBC950F, 0x75AF8EBF02BCE459),
Dan Field24f39d42020-01-02 11:47:28 -0800907
908 // dev/integration_tests/android_splash_screens/splash_screen_kitchen_sink/android/app/src/main/res/drawable-land-xxhdpi/flutter_splash_screen.png
909 // dev/integration_tests/android_splash_screens/splash_screen_kitchen_sink/android/app/src/main/res/mipmap-land-xxhdpi/flutter_splash_screen.png
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200910 const Hash256(0x2D4F8D7A3DFEF9D3, 0xA0C66938E169AB58, 0x8C6BBBBD1973E34E, 0x03C428416D010182),
Dan Field24f39d42020-01-02 11:47:28 -0800911
912 // dev/integration_tests/android_splash_screens/splash_screen_kitchen_sink/android/app/src/main/res/drawable-xxhdpi/flutter_splash_screen.png
913 // dev/integration_tests/android_splash_screens/splash_screen_kitchen_sink/android/app/src/main/res/mipmap-xxhdpi/flutter_splash_screen.png
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200914 const Hash256(0xCD46C01BAFA3B243, 0xA6AA1645EEDDE481, 0x143AC8ABAB1A0996, 0x22CAA9D41F74649A),
Dan Field24f39d42020-01-02 11:47:28 -0800915
916 // dev/integration_tests/flutter_driver_screenshot_test/assets/red_square.png
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200917 const Hash256(0x40054377E1E084F4, 0x4F4410CE8F44C210, 0xABA945DFC55ED0EF, 0x23BDF9469E32F8D3),
Dan Field24f39d42020-01-02 11:47:28 -0800918
919 // dev/integration_tests/flutter_driver_screenshot_test/test_driver/goldens/red_square_image/iPhone7,2.png
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200920 const Hash256(0x7F9D27C7BC418284, 0x01214E21CA886B2F, 0x40D9DA2B31AE7754, 0x71D68375F9C8A824),
Dan Field24f39d42020-01-02 11:47:28 -0800921
922 // examples/flutter_view/assets/flutter-mark-square-64.png
923 // examples/platform_view/assets/flutter-mark-square-64.png
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200924 const Hash256(0xF416B0D8AC552EC8, 0x819D1F492D1AB5E6, 0xD4F20CF45DB47C22, 0x7BB431FEFB5B67B2),
Dan Field24f39d42020-01-02 11:47:28 -0800925
926 // packages/flutter_tools/test/data/intellij/plugins/Dart/lib/Dart.jar
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200927 const Hash256(0x576E489D788A13DB, 0xBF40E4A39A3DAB37, 0x15CCF0002032E79C, 0xD260C69B29E06646),
Dan Field24f39d42020-01-02 11:47:28 -0800928
929 // packages/flutter_tools/test/data/intellij/plugins/flutter-intellij.jar
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200930 const Hash256(0x4C67221E25626CB2, 0x3F94E1F49D34E4CF, 0x3A9787A514924FC5, 0x9EF1E143E5BC5690),
Dan Field24f39d42020-01-02 11:47:28 -0800931
932
Dan Field24f39d42020-01-02 11:47:28 -0800933 // MISCELLANEOUS
934
935 // dev/bots/serviceaccount.enc
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200936 const Hash256(0x1F19ADB4D80AFE8C, 0xE61899BA776B1A8D, 0xCA398C75F5F7050D, 0xFB0E72D7FBBBA69B),
Dan Field24f39d42020-01-02 11:47:28 -0800937
938 // dev/docs/favicon.ico
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200939 const Hash256(0x67368CA1733E933A, 0xCA3BC56EF0695012, 0xE862C371AD4412F0, 0x3EC396039C609965),
Dan Field24f39d42020-01-02 11:47:28 -0800940
941 // dev/snippets/assets/code_sample.png
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200942 const Hash256(0xAB2211A47BDA001D, 0x173A52FD9C75EBC7, 0xE158942FFA8243AD, 0x2A148871990D4297),
Dan Field24f39d42020-01-02 11:47:28 -0800943
944 // dev/snippets/assets/code_snippet.png
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200945 const Hash256(0xDEC70574DA46DFBB, 0xFA657A771F3E1FBD, 0xB265CFC6B2AA5FE3, 0x93BA4F325D1520BA),
Dan Field24f39d42020-01-02 11:47:28 -0800946
947 // packages/flutter_tools/static/Ahem.ttf
Alexandre Ardhuinf5a99022020-04-06 22:36:01 +0200948 const Hash256(0x63D2ABD0041C3E3B, 0x4B52AD8D382353B5, 0x3C51C6785E76CE56, 0xED9DACAD2D2E31C4),
Dan Field24f39d42020-01-02 11:47:28 -0800949};
950
Michael Goderbauer584fd5f2020-06-16 09:15:43 -0700951Future<void> verifyNoBinaries(String workingDirectory, { Set<Hash256> legacyBinaries }) async {
952 // Please do not add anything to the _legacyBinaries set above.
Dan Field24f39d42020-01-02 11:47:28 -0800953 // We have a policy of not checking in binaries into this repository.
stuartmorgan685e9d12020-03-23 10:42:26 -0700954 // If you are adding/changing template images, use the flutter_template_images
955 // package and a .img.tmpl placeholder instead.
956 // If you have other binaries to add, please consult Hixie for advice.
Dan Field24f39d42020-01-02 11:47:28 -0800957 assert(
Michael Goderbauer584fd5f2020-06-16 09:15:43 -0700958 _legacyBinaries
Dan Field24f39d42020-01-02 11:47:28 -0800959 .expand<int>((Hash256 hash) => <int>[hash.a, hash.b, hash.c, hash.d])
Casey Hillers781cd4d2020-02-11 18:25:17 -0800960 .reduce((int value, int element) => value ^ element) == 0x606B51C908B40BFA // Please do not modify this line.
Dan Field24f39d42020-01-02 11:47:28 -0800961 );
Michael Goderbauer584fd5f2020-06-16 09:15:43 -0700962 legacyBinaries ??= _legacyBinaries;
Dan Field24f39d42020-01-02 11:47:28 -0800963 if (!Platform.isWindows) { // TODO(ianh): Port this to Windows
Yuqian Lifb552ed2020-11-07 05:19:02 -0800964 final List<File> files = await _gitFiles(workingDirectory, runSilently: false);
Dan Field24f39d42020-01-02 11:47:28 -0800965 final List<String> problems = <String>[];
Alexandre Ardhuin4f9b6cf2020-01-07 16:32:04 +0100966 for (final File file in files) {
Dan Field24f39d42020-01-02 11:47:28 -0800967 final Uint8List bytes = file.readAsBytesSync();
968 try {
969 utf8.decode(bytes);
970 } on FormatException catch (error) {
Paul Berryc32d1382020-05-04 09:10:20 -0700971 final Digest digest = sha256.convert(bytes);
Michael Goderbauer584fd5f2020-06-16 09:15:43 -0700972 if (!legacyBinaries.contains(Hash256.fromDigest(digest)))
Paul Berryc32d1382020-05-04 09:10:20 -0700973 problems.add('${file.path}:${error.offset}: file is not valid UTF-8');
Dan Field24f39d42020-01-02 11:47:28 -0800974 }
975 }
976 if (problems.isNotEmpty) {
977 exitWithError(<String>[
978 ...problems,
979 'All files in this repository must be UTF-8. In particular, images and other binaries',
980 'must not be checked into this repository. This is because we are very sensitive to the',
981 'size of the repository as it is distributed to all our developers. If you have a binary',
982 'to which you need access, you should consider how to fetch it from another repository;',
983 'for example, the "assets-for-api-docs" repository is used for images in API docs.',
984 ]);
Ian Hicksoneae05c72019-11-14 13:19:40 -0800985 }
986 }
987}
988
989
990// UTILITY FUNCTIONS
991
992bool _listEquals<T>(List<T> a, List<T> b) {
Alexander Aprelev391e91c2018-08-30 07:30:25 -0700993 assert(a != null);
994 assert(b != null);
995 if (a.length != b.length)
996 return false;
997 for (int index = 0; index < a.length; index += 1) {
998 if (a[index] != b[index])
999 return false;
1000 }
1001 return true;
1002}
1003
Yuqian Lifb552ed2020-11-07 05:19:02 -08001004Future<List<File>> _gitFiles(String workingDirectory, {bool runSilently = true}) async {
1005 final EvalResult evalResult = await _evalCommand(
1006 'git', <String>['ls-files', '-z'],
1007 workingDirectory: workingDirectory,
1008 runSilently: runSilently,
1009 );
1010 if (evalResult.exitCode != 0) {
1011 exitWithError(<String>[
1012 'git ls-filese failed with exit code ${evalResult.exitCode}',
1013 '${bold}stdout:$reset',
1014 evalResult.stdout,
1015 '${bold}stderr:$reset',
1016 evalResult.stderr,
1017 ]);
1018 }
1019 final List<String> filenames = evalResult
1020 .stdout
1021 .split('\x00');
1022 assert(filenames.last.isEmpty); // git ls-files gives a trailing blank 0x00
1023 filenames.removeLast();
1024 return filenames
1025 .map<File>((String filename) => File(path.join(workingDirectory, filename)))
1026 .toList();
1027}
1028
1029Stream<File> _allFiles(String workingDirectory, String extension, { @required int minimumMatches }) async* {
1030 final Set<String> gitFileNamesSet = <String>{};
1031 gitFileNamesSet.addAll((await _gitFiles(workingDirectory)).map((File f) => path.canonicalize(f.absolute.path)));
1032
Dan Field24f39d42020-01-02 11:47:28 -08001033 assert(extension == null || !extension.startsWith('.'), 'Extension argument should not start with a period.');
Ian Hickson62e4ab82019-11-15 19:21:53 -08001034 final Set<FileSystemEntity> pending = <FileSystemEntity>{ Directory(workingDirectory) };
Dan Field24f39d42020-01-02 11:47:28 -08001035 int matches = 0;
Ian Hickson62e4ab82019-11-15 19:21:53 -08001036 while (pending.isNotEmpty) {
1037 final FileSystemEntity entity = pending.first;
1038 pending.remove(entity);
Ian Hickson449f4a62019-11-27 15:04:02 -08001039 if (path.extension(entity.path) == '.tmpl')
1040 continue;
Ian Hickson62e4ab82019-11-15 19:21:53 -08001041 if (entity is File) {
Yuqian Lifb552ed2020-11-07 05:19:02 -08001042 if (!gitFileNamesSet.contains(path.canonicalize(entity.absolute.path)))
1043 continue;
Ian Hickson449f4a62019-11-27 15:04:02 -08001044 if (_isGeneratedPluginRegistrant(entity))
1045 continue;
1046 if (path.basename(entity.path) == 'flutter_export_environment.sh')
1047 continue;
1048 if (path.basename(entity.path) == 'gradlew.bat')
1049 continue;
Darren Austin4253e422020-09-18 14:37:04 -07001050 if (path.basename(entity.path) == '.DS_Store')
1051 continue;
Dan Field24f39d42020-01-02 11:47:28 -08001052 if (extension == null || path.extension(entity.path) == '.$extension') {
1053 matches += 1;
Ian Hickson62e4ab82019-11-15 19:21:53 -08001054 yield entity;
Dan Field24f39d42020-01-02 11:47:28 -08001055 }
Ian Hickson62e4ab82019-11-15 19:21:53 -08001056 } else if (entity is Directory) {
1057 if (File(path.join(entity.path, '.dartignore')).existsSync())
1058 continue;
1059 if (path.basename(entity.path) == '.git')
1060 continue;
Jim Graham210f7682020-07-29 17:16:26 -07001061 if (path.basename(entity.path) == '.idea')
1062 continue;
Dan Field24f39d42020-01-02 11:47:28 -08001063 if (path.basename(entity.path) == '.gradle')
1064 continue;
Ian Hickson62e4ab82019-11-15 19:21:53 -08001065 if (path.basename(entity.path) == '.dart_tool')
1066 continue;
Darren Austin753b8482020-07-20 18:51:04 -07001067 if (path.basename(entity.path) == '.idea')
1068 continue;
Ian Hickson449f4a62019-11-27 15:04:02 -08001069 if (path.basename(entity.path) == 'build')
1070 continue;
Ian Hickson62e4ab82019-11-15 19:21:53 -08001071 pending.addAll(entity.listSync());
1072 }
1073 }
Dan Field24f39d42020-01-02 11:47:28 -08001074 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 -08001075}
1076
1077class EvalResult {
1078 EvalResult({
1079 this.stdout,
1080 this.stderr,
1081 this.exitCode = 0,
1082 });
1083
1084 final String stdout;
1085 final String stderr;
1086 final int exitCode;
1087}
1088
Dan Field24f39d42020-01-02 11:47:28 -08001089// TODO(ianh): Refactor this to reuse the code in run_command.dart
Ian Hicksoneae05c72019-11-14 13:19:40 -08001090Future<EvalResult> _evalCommand(String executable, List<String> arguments, {
1091 @required String workingDirectory,
1092 Map<String, String> environment,
1093 bool skip = false,
1094 bool allowNonZeroExit = false,
Yuqian Lifb552ed2020-11-07 05:19:02 -08001095 bool runSilently = false,
Ian Hicksoneae05c72019-11-14 13:19:40 -08001096}) async {
1097 final String commandDescription = '${path.relative(executable, from: workingDirectory)} ${arguments.join(' ')}';
1098 final String relativeWorkingDir = path.relative(workingDirectory);
1099 if (skip) {
1100 printProgress('SKIPPING', relativeWorkingDir, commandDescription);
1101 return null;
1102 }
Yuqian Lifb552ed2020-11-07 05:19:02 -08001103
1104 if (!runSilently) {
1105 printProgress('RUNNING', relativeWorkingDir, commandDescription);
1106 }
Ian Hicksoneae05c72019-11-14 13:19:40 -08001107
1108 final Stopwatch time = Stopwatch()..start();
1109 final Process process = await Process.start(executable, arguments,
1110 workingDirectory: workingDirectory,
1111 environment: environment,
1112 );
1113
1114 final Future<List<List<int>>> savedStdout = process.stdout.toList();
1115 final Future<List<List<int>>> savedStderr = process.stderr.toList();
1116 final int exitCode = await process.exitCode;
1117 final EvalResult result = EvalResult(
1118 stdout: utf8.decode((await savedStdout).expand<int>((List<int> ints) => ints).toList()),
1119 stderr: utf8.decode((await savedStderr).expand<int>((List<int> ints) => ints).toList()),
1120 exitCode: exitCode,
1121 );
1122
Yuqian Lifb552ed2020-11-07 05:19:02 -08001123 if (!runSilently) {
1124 print('$clock ELAPSED TIME: $bold${prettyPrintDuration(time.elapsed)}$reset for $commandDescription in $relativeWorkingDir');
1125 }
Ian Hicksoneae05c72019-11-14 13:19:40 -08001126
1127 if (exitCode != 0 && !allowNonZeroExit) {
1128 stderr.write(result.stderr);
Dan Field24f39d42020-01-02 11:47:28 -08001129 exitWithError(<String>[
1130 '${bold}ERROR:$red Last command exited with $exitCode.$reset',
1131 '${bold}Command:$red $commandDescription$reset',
1132 '${bold}Relative working directory:$red $relativeWorkingDir$reset',
1133 ]);
Ian Hicksoneae05c72019-11-14 13:19:40 -08001134 }
1135
1136 return result;
1137}
1138
Jonah Williamsf6f59c52021-04-16 14:29:32 -07001139Future<void> _checkConsumerDependencies() async {
1140 final ProcessResult result = await Process.run(flutter, <String>[
1141 'update-packages',
1142 '--transitive-closure',
1143 '--consumer-only',
1144 ]);
1145 if (result.exitCode != 0) {
1146 print(result.stdout);
1147 print(result.stderr);
1148 exit(result.exitCode);
1149 }
1150 final Set<String> dependencySet = <String>{};
1151 for (final String line in result.stdout.toString().split('\n')) {
1152 if (!line.contains('->')) {
1153 continue;
1154 }
1155 final List<String> parts = line.split('->');
1156 final String name = parts[0].trim();
1157 dependencySet.add(name);
1158 }
1159 final List<String> dependencies = dependencySet.toList()
1160 ..sort();
1161 final List<String> disallowed = <String>[];
1162 final StreamController<Digest> controller = StreamController<Digest>();
1163 final ByteConversionSink hasher = sha256.startChunkedConversion(controller.sink);
1164 for (final String dependency in dependencies) {
1165 hasher.add(utf8.encode(dependency));
1166 if (!kCorePackageAllowList.contains(dependency)) {
1167 disallowed.add(dependency);
1168 }
1169 }
1170 hasher.close();
1171 final Digest digest = await controller.stream.last;
1172 final String signature = base64.encode(digest.bytes);
1173
1174 // Do not change this signature without following the directions in
1175 // dev/bots/allowlist.dart
1176 const String kExpected = '3S20q38QbN+dDAp+jApXiTRaDgVGGBZ0t4bMJgD3AUY=';
1177
1178 if (disallowed.isNotEmpty) {
1179 exitWithError(<String>[
1180 'Warning: transitive closure contained non-allowlisted packages:',
1181 '${disallowed..join(', ')}',
1182 'See dev/bots/allowlist.dart for instructions on how to update the package allowlist.',
1183 ]);
1184 }
1185
1186 if (signature != kExpected) {
1187 exitWithError(<String>[
1188 'Warning: transitive closure sha256 does not match expected signature.',
1189 'See dev/bots/allowlist.dart for instructions on how to update the package allowlist.',
1190 '$signature != $kExpected',
1191 ]);
1192 }
1193}
1194
Ian Hicksoneae05c72019-11-14 13:19:40 -08001195Future<void> _runFlutterAnalyze(String workingDirectory, {
1196 List<String> options = const <String>[],
Dan Field24f39d42020-01-02 11:47:28 -08001197}) async {
Michael Goderbauercb867bb2021-03-05 18:38:15 -08001198 return runCommand(
Ian Hicksoneae05c72019-11-14 13:19:40 -08001199 flutter,
Jenn Magder95d7d672021-04-18 09:29:02 -07001200 <String>['analyze', ...options],
Ian Hicksoneae05c72019-11-14 13:19:40 -08001201 workingDirectory: workingDirectory,
1202 );
1203}
1204
Greg Spencer35fcd902019-01-14 13:49:50 -08001205final RegExp _importPattern = RegExp(r'''^\s*import (['"])package:flutter/([^.]+)\.dart\1''');
Ian Hickson58939b72019-02-12 12:29:36 -08001206final RegExp _importMetaPattern = RegExp(r'''^\s*import (['"])package:meta/meta\.dart\1''');
Alexander Aprelev391e91c2018-08-30 07:30:25 -07001207
Yuqian Lifb552ed2020-11-07 05:19:02 -08001208Future<Set<String>> _findFlutterDependencies(String srcPath, List<String> errors, { bool checkForMeta = false }) async {
Michael Goderbauercb867bb2021-03-05 18:38:15 -08001209 return _allFiles(srcPath, 'dart', minimumMatches: 1)
Alexandre Ardhuinec1a0152019-12-05 22:34:06 +01001210 .map<Set<String>>((File file) {
1211 final Set<String> result = <String>{};
Alexandre Ardhuin4f9b6cf2020-01-07 16:32:04 +01001212 for (final String line in file.readAsLinesSync()) {
Alexandre Ardhuinec1a0152019-12-05 22:34:06 +01001213 Match match = _importPattern.firstMatch(line);
1214 if (match != null)
1215 result.add(match.group(2));
1216 if (checkForMeta) {
1217 match = _importMetaPattern.firstMatch(line);
1218 if (match != null) {
1219 errors.add(
1220 '${file.path}\nThis package imports the ${yellow}meta$reset package.\n'
1221 'You should instead import the "foundation.dart" library.'
1222 );
1223 }
Alexander Aprelev391e91c2018-08-30 07:30:25 -07001224 }
1225 }
Alexandre Ardhuinec1a0152019-12-05 22:34:06 +01001226 return result;
1227 })
1228 .reduce((Set<String> value, Set<String> element) {
1229 value ??= <String>{};
1230 value.addAll(element);
1231 return value;
1232 });
Alexander Aprelev391e91c2018-08-30 07:30:25 -07001233}
1234
1235List<T> _deepSearch<T>(Map<T, Set<T>> map, T start, [ Set<T> seen ]) {
LongCatIsLooongd291de02020-01-09 10:25:58 -08001236 if (map[start] == null)
1237 return null; // We catch these separately.
1238
Alexandre Ardhuin4f9b6cf2020-01-07 16:32:04 +01001239 for (final T key in map[start]) {
Alexander Aprelev391e91c2018-08-30 07:30:25 -07001240 if (key == start)
1241 continue; // we catch these separately
1242 if (seen != null && seen.contains(key))
1243 return <T>[start, key];
Alexandre Ardhuinf62afdc2018-10-01 21:29:08 +02001244 final List<T> result = _deepSearch<T>(
Alexander Aprelev391e91c2018-08-30 07:30:25 -07001245 map,
1246 key,
Alexandre Ardhuin919dcf52019-06-27 21:23:16 +02001247 <T>{
1248 if (seen == null) start else ...seen,
1249 key,
1250 },
Alexander Aprelev391e91c2018-08-30 07:30:25 -07001251 );
1252 if (result != null) {
1253 result.insert(0, start);
1254 // Only report the shortest chains.
1255 // For example a->b->a, rather than c->a->b->a.
1256 // Since we visit every node, we know the shortest chains are those
1257 // that start and end on the loop.
1258 if (result.first == result.last)
1259 return result;
1260 }
1261 }
1262 return null;
1263}
1264
Alexander Aprelev391e91c2018-08-30 07:30:25 -07001265bool _isGeneratedPluginRegistrant(File file) {
1266 final String filename = path.basename(file.path);
1267 return !file.path.contains('.pub-cache')
1268 && (filename == 'GeneratedPluginRegistrant.java' ||
1269 filename == 'GeneratedPluginRegistrant.h' ||
Jonah Williams0b3f5cf2020-04-21 20:39:36 -07001270 filename == 'GeneratedPluginRegistrant.m' ||
Jonah Williams14722d32020-09-28 10:07:35 -07001271 filename == 'generated_plugin_registrant.dart' ||
1272 filename == 'generated_plugin_registrant.h');
Alexander Aprelev391e91c2018-08-30 07:30:25 -07001273}