1// Copyright 2014 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5import 'dart:convert';
6import 'dart:io'
7 as io
8 show
9 Directory,
10 File,
11 Link,
12 Process,
13 ProcessException,
14 ProcessResult,
15 ProcessSignal,
16 ProcessStartMode,
17 systemEncoding;
18import 'dart:typed_data';
19
20import 'package:file/file.dart';
21import 'package:path/path.dart' as p; // flutter_ignore: package_path_import
22import 'package:process/process.dart';
23
24import 'common.dart' show throwToolExit;
25import 'platform.dart';
26
27// The Flutter tool hits file system and process errors that only the end-user can address.
28// We would like these errors to not hit crash logging. In these cases, we
29// should exit gracefully and provide potentially useful advice. For example, if
30// a write fails because the target device is full, we can explain that with a
31// ToolExit and a message that is more clear than the FileSystemException by
32// itself.
33
34/// On Windows this is error code 2: ERROR_FILE_NOT_FOUND, and on
35/// macOS/Linux it is error code 2/ENOENT: No such file or directory.
36const int kSystemCodeCannotFindFile = 2;
37
38/// On Windows this error is 3: ERROR_PATH_NOT_FOUND, and on
39/// macOS/Linux, it is error code 3/ESRCH: No such process.
40const int kSystemCodePathNotFound = 3;
41
42/// A [FileSystem] that throws a [ToolExit] on certain errors.
43///
44/// If a [FileSystem] error is not caused by the Flutter tool, and can only be
45/// addressed by the user, it should be caught by this [FileSystem] and thrown
46/// as a [ToolExit] using [throwToolExit].
47///
48/// Cf. If there is some hope that the tool can continue when an operation fails
49/// with an error, then that error/operation should not be handled here. For
50/// example, the tool should generally be able to continue executing even if it
51/// fails to delete a file.
52class ErrorHandlingFileSystem extends ForwardingFileSystem {
53 ErrorHandlingFileSystem({required FileSystem delegate, required Platform platform})
54 : _platform = platform,
55 super(delegate);
56
57 FileSystem get fileSystem => delegate;
58
59 final Platform _platform;
60
61 /// Allow any file system operations executed within the closure to fail with any
62 /// operating system error, rethrowing an [Exception] instead of a [ToolExit].
63 ///
64 /// This should not be used with async file system operation.
65 ///
66 /// This can be used to bypass the [ErrorHandlingFileSystem] permission exit
67 /// checks for situations where failure is acceptable, such as the flutter
68 /// persistent settings cache.
69 static void noExitOnFailure(void Function() operation) {
70 final bool previousValue = ErrorHandlingFileSystem._noExitOnFailure;
71 try {
72 ErrorHandlingFileSystem._noExitOnFailure = true;
73 operation();
74 } finally {
75 ErrorHandlingFileSystem._noExitOnFailure = previousValue;
76 }
77 }
78
79 /// Delete the file or directory and return true if it exists, take no
80 /// action and return false if it does not.
81 ///
82 /// This method should be preferred to checking if it exists and
83 /// then deleting, because it handles the edge case where the file or directory
84 /// is deleted by a different program between the two calls.
85 static bool deleteIfExists(FileSystemEntity entity, {bool recursive = false}) {
86 if (!entity.existsSync()) {
87 return false;
88 }
89 try {
90 entity.deleteSync(recursive: recursive);
91 } on FileSystemException catch (err) {
92 // Certain error codes indicate the file could not be found. It could have
93 // been deleted by a different program while the tool was running.
94 // if it still exists, the file likely exists on a read-only volume.
95 // This check will falsely match "3/ESRCH: No such process" on Linux/macOS,
96 // but this should be fine since this code should never come up here.
97 final bool codeCorrespondsToPathOrFileNotFound =
98 err.osError?.errorCode == kSystemCodeCannotFindFile ||
99 err.osError?.errorCode == kSystemCodePathNotFound;
100 if (!codeCorrespondsToPathOrFileNotFound || _noExitOnFailure) {
101 rethrow;
102 }
103 if (entity.existsSync()) {
104 throwToolExit(
105 'Unable to delete file or directory at "${entity.path}". '
106 'This may be due to the project being in a read-only '
107 'volume. Consider relocating the project and trying again.',
108 );
109 }
110 }
111 return true;
112 }
113
114 static bool _noExitOnFailure = false;
115
116 @override
117 Directory get currentDirectory {
118 try {
119 return _runSync(() => directory(delegate.currentDirectory), platform: _platform);
120 } on FileSystemException catch (err) {
121 // Special handling for OS error 2 for current directory only.
122 if (err.osError?.errorCode == kSystemCodeCannotFindFile) {
123 throwToolExit(
124 'Unable to read current working directory. This can happen if the directory the '
125 'Flutter tool was run from was moved or deleted.',
126 );
127 }
128 rethrow;
129 }
130 }
131
132 @override
133 Directory get systemTempDirectory {
134 return _runSync(() => directory(delegate.systemTempDirectory), platform: _platform);
135 }
136
137 @override
138 File file(dynamic path) =>
139 ErrorHandlingFile(platform: _platform, fileSystem: this, delegate: delegate.file(path));
140
141 @override
142 Directory directory(dynamic path) => ErrorHandlingDirectory(
143 platform: _platform,
144 fileSystem: this,
145 delegate: delegate.directory(path),
146 );
147
148 @override
149 Link link(dynamic path) =>
150 ErrorHandlingLink(platform: _platform, fileSystem: this, delegate: delegate.link(path));
151
152 // Caching the path context here and clearing when the currentDirectory setter
153 // is updated works since the flutter tool restricts usage of dart:io directly
154 // via the forbidden import tests. Otherwise, the path context's current
155 // working directory might get out of sync, leading to unexpected results from
156 // methods like `path.relative`.
157 @override
158 p.Context get path => _cachedPath ??= delegate.path;
159 p.Context? _cachedPath;
160
161 @override
162 set currentDirectory(dynamic path) {
163 _cachedPath = null;
164 delegate.currentDirectory = path;
165 }
166
167 @override
168 String toString() => delegate.toString();
169}
170
171class ErrorHandlingFile extends ForwardingFileSystemEntity<File, io.File> with ForwardingFile {
172 ErrorHandlingFile({required Platform platform, required this.fileSystem, required this.delegate})
173 : _platform = platform;
174
175 @override
176 final io.File delegate;
177
178 @override
179 final ErrorHandlingFileSystem fileSystem;
180
181 final Platform _platform;
182
183 @override
184 File wrapFile(io.File delegate) =>
185 ErrorHandlingFile(platform: _platform, fileSystem: fileSystem, delegate: delegate);
186
187 @override
188 Directory wrapDirectory(io.Directory delegate) =>
189 ErrorHandlingDirectory(platform: _platform, fileSystem: fileSystem, delegate: delegate);
190
191 @override
192 Link wrapLink(io.Link delegate) =>
193 ErrorHandlingLink(platform: _platform, fileSystem: fileSystem, delegate: delegate);
194
195 @override
196 Future<File> writeAsBytes(
197 List<int> bytes, {
198 FileMode mode = FileMode.write,
199 bool flush = false,
200 }) async {
201 return _run<File>(
202 () async => wrap(await delegate.writeAsBytes(bytes, mode: mode, flush: flush)),
203 platform: _platform,
204 failureMessage: 'Flutter failed to write to a file at "${delegate.path}"',
205 posixPermissionSuggestion: _posixPermissionSuggestion(<String>[delegate.path]),
206 );
207 }
208
209 @override
210 String readAsStringSync({Encoding encoding = utf8}) {
211 return _runSync<String>(
212 () => delegate.readAsStringSync(),
213 platform: _platform,
214 failureMessage: 'Flutter failed to read a file at "${delegate.path}"',
215 posixPermissionSuggestion: _posixPermissionSuggestion(<String>[delegate.path]),
216 );
217 }
218
219 @override
220 void writeAsBytesSync(List<int> bytes, {FileMode mode = FileMode.write, bool flush = false}) {
221 _runSync<void>(
222 () => delegate.writeAsBytesSync(bytes, mode: mode, flush: flush),
223 platform: _platform,
224 failureMessage: 'Flutter failed to write to a file at "${delegate.path}"',
225 posixPermissionSuggestion: _posixPermissionSuggestion(<String>[delegate.path]),
226 );
227 }
228
229 @override
230 Future<File> writeAsString(
231 String contents, {
232 FileMode mode = FileMode.write,
233 Encoding encoding = utf8,
234 bool flush = false,
235 }) async {
236 return _run<File>(
237 () async => wrap(
238 await delegate.writeAsString(contents, mode: mode, encoding: encoding, flush: flush),
239 ),
240 platform: _platform,
241 failureMessage: 'Flutter failed to write to a file at "${delegate.path}"',
242 posixPermissionSuggestion: _posixPermissionSuggestion(<String>[delegate.path]),
243 );
244 }
245
246 @override
247 void writeAsStringSync(
248 String contents, {
249 FileMode mode = FileMode.write,
250 Encoding encoding = utf8,
251 bool flush = false,
252 }) {
253 _runSync<void>(
254 () => delegate.writeAsStringSync(contents, mode: mode, encoding: encoding, flush: flush),
255 platform: _platform,
256 failureMessage: 'Flutter failed to write to a file at "${delegate.path}"',
257 posixPermissionSuggestion: _posixPermissionSuggestion(<String>[delegate.path]),
258 );
259 }
260
261 // TODO(aam): Pass `exclusive` through after dartbug.com/49647 lands.
262 @override
263 void createSync({bool recursive = false, bool exclusive = false}) {
264 _runSync<void>(
265 () => delegate.createSync(recursive: recursive),
266 platform: _platform,
267 failureMessage: 'Flutter failed to create file at "${delegate.path}"',
268 posixPermissionSuggestion:
269 recursive ? null : _posixPermissionSuggestion(<String>[delegate.parent.path]),
270 );
271 }
272
273 @override
274 RandomAccessFile openSync({FileMode mode = FileMode.read}) {
275 return _runSync<RandomAccessFile>(
276 () => delegate.openSync(mode: mode),
277 platform: _platform,
278 failureMessage: 'Flutter failed to open a file at "${delegate.path}"',
279 posixPermissionSuggestion: _posixPermissionSuggestion(<String>[delegate.path]),
280 );
281 }
282
283 /// This copy method attempts to handle file system errors from both reading
284 /// and writing the copied file.
285 @override
286 File copySync(String newPath) {
287 final File resultFile = fileSystem.file(newPath);
288 // First check if the source file can be read. If not, bail through error
289 // handling.
290 _runSync<void>(
291 () => delegate.openSync().closeSync(),
292 platform: _platform,
293 failureMessage: 'Flutter failed to copy $path to $newPath due to source location error',
294 posixPermissionSuggestion: _posixPermissionSuggestion(<String>[path]),
295 );
296 // Next check if the destination file can be written. If not, bail through
297 // error handling.
298 _runSync<void>(
299 () => resultFile.createSync(recursive: true),
300 platform: _platform,
301 failureMessage: 'Flutter failed to copy $path to $newPath due to destination location error',
302 );
303 // If both of the above checks passed, attempt to copy the file and catch
304 // any thrown errors.
305 try {
306 return wrapFile(delegate.copySync(newPath));
307 } on FileSystemException {
308 // Proceed below
309 }
310 // If the copy failed but both of the above checks passed, copy the bytes
311 // directly.
312 _runSync(
313 () {
314 RandomAccessFile? source;
315 RandomAccessFile? sink;
316 try {
317 source = delegate.openSync();
318 sink = resultFile.openSync(mode: FileMode.writeOnly);
319 // 64k is the same sized buffer used by dart:io for `File.openRead`.
320 final Uint8List buffer = Uint8List(64 * 1024);
321 final int totalBytes = source.lengthSync();
322 int bytes = 0;
323 while (bytes < totalBytes) {
324 final int chunkLength = source.readIntoSync(buffer);
325 sink.writeFromSync(buffer, 0, chunkLength);
326 bytes += chunkLength;
327 }
328 } catch (err) {
329 ErrorHandlingFileSystem.deleteIfExists(resultFile, recursive: true);
330 rethrow;
331 } finally {
332 source?.closeSync();
333 sink?.closeSync();
334 }
335 },
336 platform: _platform,
337 failureMessage: 'Flutter failed to copy $path to $newPath due to unknown error',
338 posixPermissionSuggestion: _posixPermissionSuggestion(<String>[path, resultFile.parent.path]),
339 );
340 // The original copy failed, but the manual copy worked.
341 return wrapFile(resultFile);
342 }
343
344 String _posixPermissionSuggestion(List<String> paths) =>
345 'Try running:\n'
346 ' sudo chown -R \$(whoami) ${paths.map(fileSystem.path.absolute).join(' ')}';
347
348 @override
349 String toString() => delegate.toString();
350}
351
352class ErrorHandlingDirectory extends ForwardingFileSystemEntity<Directory, io.Directory>
353 with ForwardingDirectory<Directory> {
354 ErrorHandlingDirectory({
355 required Platform platform,
356 required this.fileSystem,
357 required this.delegate,
358 }) : _platform = platform;
359
360 @override
361 final io.Directory delegate;
362
363 @override
364 final ErrorHandlingFileSystem fileSystem;
365
366 final Platform _platform;
367
368 @override
369 File wrapFile(io.File delegate) =>
370 ErrorHandlingFile(platform: _platform, fileSystem: fileSystem, delegate: delegate);
371
372 @override
373 Directory wrapDirectory(io.Directory delegate) =>
374 ErrorHandlingDirectory(platform: _platform, fileSystem: fileSystem, delegate: delegate);
375
376 @override
377 Link wrapLink(io.Link delegate) =>
378 ErrorHandlingLink(platform: _platform, fileSystem: fileSystem, delegate: delegate);
379
380 @override
381 Directory childDirectory(String basename) {
382 return fileSystem.directory(fileSystem.path.join(path, basename));
383 }
384
385 @override
386 File childFile(String basename) {
387 return fileSystem.file(fileSystem.path.join(path, basename));
388 }
389
390 @override
391 Link childLink(String basename) {
392 return fileSystem.link(fileSystem.path.join(path, basename));
393 }
394
395 @override
396 void createSync({bool recursive = false}) {
397 return _runSync<void>(
398 () => delegate.createSync(recursive: recursive),
399 platform: _platform,
400 failureMessage: 'Flutter failed to create a directory at "${delegate.path}"',
401 posixPermissionSuggestion:
402 recursive ? null : _posixPermissionSuggestion(delegate.parent.path),
403 );
404 }
405
406 @override
407 Future<Directory> createTemp([String? prefix]) {
408 return _run<Directory>(
409 () async => wrap(await delegate.createTemp(prefix)),
410 platform: _platform,
411 failureMessage: 'Flutter failed to create a temporary directory with prefix "$prefix"',
412 );
413 }
414
415 @override
416 Directory createTempSync([String? prefix]) {
417 return _runSync<Directory>(
418 () => wrap(delegate.createTempSync(prefix)),
419 platform: _platform,
420 failureMessage: 'Flutter failed to create a temporary directory with prefix "$prefix"',
421 );
422 }
423
424 @override
425 Future<Directory> create({bool recursive = false}) {
426 return _run<Directory>(
427 () async => wrap(await delegate.create(recursive: recursive)),
428 platform: _platform,
429 failureMessage: 'Flutter failed to create a directory at "${delegate.path}"',
430 posixPermissionSuggestion:
431 recursive ? null : _posixPermissionSuggestion(delegate.parent.path),
432 );
433 }
434
435 @override
436 Future<Directory> delete({bool recursive = false}) {
437 return _run<Directory>(
438 () async => wrap(fileSystem.directory((await delegate.delete(recursive: recursive)).path)),
439 platform: _platform,
440 failureMessage: 'Flutter failed to delete a directory at "${delegate.path}"',
441 posixPermissionSuggestion: recursive ? null : _posixPermissionSuggestion(delegate.path),
442 );
443 }
444
445 @override
446 void deleteSync({bool recursive = false}) {
447 return _runSync<void>(
448 () => delegate.deleteSync(recursive: recursive),
449 platform: _platform,
450 failureMessage: 'Flutter failed to delete a directory at "${delegate.path}"',
451 posixPermissionSuggestion: recursive ? null : _posixPermissionSuggestion(delegate.path),
452 );
453 }
454
455 @override
456 bool existsSync() {
457 return _runSync<bool>(
458 () => delegate.existsSync(),
459 platform: _platform,
460 failureMessage: 'Flutter failed to check for directory existence at "${delegate.path}"',
461 posixPermissionSuggestion: _posixPermissionSuggestion(delegate.parent.path),
462 );
463 }
464
465 String _posixPermissionSuggestion(String path) =>
466 'Try running:\n'
467 ' sudo chown -R \$(whoami) ${fileSystem.path.absolute(path)}';
468
469 @override
470 String toString() => delegate.toString();
471}
472
473class ErrorHandlingLink extends ForwardingFileSystemEntity<Link, io.Link> with ForwardingLink {
474 ErrorHandlingLink({required Platform platform, required this.fileSystem, required this.delegate})
475 : _platform = platform;
476
477 @override
478 final io.Link delegate;
479
480 @override
481 final ErrorHandlingFileSystem fileSystem;
482
483 final Platform _platform;
484
485 @override
486 File wrapFile(io.File delegate) =>
487 ErrorHandlingFile(platform: _platform, fileSystem: fileSystem, delegate: delegate);
488
489 @override
490 Directory wrapDirectory(io.Directory delegate) =>
491 ErrorHandlingDirectory(platform: _platform, fileSystem: fileSystem, delegate: delegate);
492
493 @override
494 Link wrapLink(io.Link delegate) =>
495 ErrorHandlingLink(platform: _platform, fileSystem: fileSystem, delegate: delegate);
496
497 @override
498 String toString() => delegate.toString();
499}
500
501const String _kNoExecutableFound =
502 'The Flutter tool could not locate an executable with suitable permissions';
503
504Future<T> _run<T>(
505 Future<T> Function() op, {
506 required Platform platform,
507 String? failureMessage,
508 String? posixPermissionSuggestion,
509}) async {
510 try {
511 return await op();
512 } on ProcessPackageExecutableNotFoundException catch (e) {
513 if (e.candidates.isNotEmpty) {
514 throwToolExit('$_kNoExecutableFound: $e');
515 }
516 rethrow;
517 } on FileSystemException catch (e) {
518 if (platform.isWindows) {
519 _handleWindowsException(e, failureMessage, e.osError?.errorCode ?? 0);
520 } else if (platform.isLinux || platform.isMacOS) {
521 _handlePosixException(
522 e,
523 failureMessage,
524 e.osError?.errorCode ?? 0,
525 posixPermissionSuggestion,
526 );
527 }
528 rethrow;
529 } on io.ProcessException catch (e) {
530 if (platform.isWindows) {
531 _handleWindowsException(e, failureMessage, e.errorCode);
532 } else if (platform.isLinux) {
533 _handlePosixException(e, failureMessage, e.errorCode, posixPermissionSuggestion);
534 }
535 if (platform.isMacOS) {
536 _handleMacOSException(e, failureMessage, e.errorCode, posixPermissionSuggestion);
537 }
538 rethrow;
539 }
540}
541
542T _runSync<T>(
543 T Function() op, {
544 required Platform platform,
545 String? failureMessage,
546 String? posixPermissionSuggestion,
547}) {
548 try {
549 return op();
550 } on ProcessPackageExecutableNotFoundException catch (e) {
551 if (e.candidates.isNotEmpty) {
552 throwToolExit('$_kNoExecutableFound: $e');
553 }
554 rethrow;
555 } on FileSystemException catch (e) {
556 if (platform.isWindows) {
557 _handleWindowsException(e, failureMessage, e.osError?.errorCode ?? 0);
558 } else if (platform.isLinux || platform.isMacOS) {
559 _handlePosixException(
560 e,
561 failureMessage,
562 e.osError?.errorCode ?? 0,
563 posixPermissionSuggestion,
564 );
565 }
566 rethrow;
567 } on io.ProcessException catch (e) {
568 if (platform.isWindows) {
569 _handleWindowsException(e, failureMessage, e.errorCode);
570 } else if (platform.isLinux) {
571 _handlePosixException(e, failureMessage, e.errorCode, posixPermissionSuggestion);
572 }
573 if (platform.isMacOS) {
574 _handleMacOSException(e, failureMessage, e.errorCode, posixPermissionSuggestion);
575 }
576 rethrow;
577 }
578}
579
580/// A [ProcessManager] that throws a [ToolExit] on certain errors.
581///
582/// If a [ProcessException] is not caused by the Flutter tool, and can only be
583/// addressed by the user, it should be caught by this [ProcessManager] and thrown
584/// as a [ToolExit] using [throwToolExit].
585///
586/// See also:
587/// * [ErrorHandlingFileSystem], for a similar file system strategy.
588class ErrorHandlingProcessManager extends ProcessManager {
589 ErrorHandlingProcessManager({required ProcessManager delegate, required Platform platform})
590 : _delegate = delegate,
591 _platform = platform;
592
593 final ProcessManager _delegate;
594 final Platform _platform;
595
596 @override
597 bool canRun(dynamic executable, {String? workingDirectory}) {
598 return _runSync(
599 () => _delegate.canRun(executable, workingDirectory: workingDirectory),
600 platform: _platform,
601 failureMessage: 'Flutter failed to run "$executable"',
602 posixPermissionSuggestion:
603 'Try running:\n'
604 ' sudo chown -R \$(whoami) $executable && chmod u+rx $executable',
605 );
606 }
607
608 @override
609 bool killPid(int pid, [io.ProcessSignal signal = io.ProcessSignal.sigterm]) {
610 return _runSync(() => _delegate.killPid(pid, signal), platform: _platform);
611 }
612
613 @override
614 Future<io.ProcessResult> run(
615 List<Object> command, {
616 String? workingDirectory,
617 Map<String, String>? environment,
618 bool includeParentEnvironment = true,
619 bool runInShell = false,
620 Encoding? stdoutEncoding = io.systemEncoding,
621 Encoding? stderrEncoding = io.systemEncoding,
622 }) {
623 return _run(
624 () {
625 return _delegate.run(
626 command,
627 workingDirectory: workingDirectory,
628 environment: environment,
629 includeParentEnvironment: includeParentEnvironment,
630 runInShell: runInShell,
631 stdoutEncoding: stdoutEncoding,
632 stderrEncoding: stderrEncoding,
633 );
634 },
635 platform: _platform,
636 failureMessage: 'Flutter failed to run "${command.join(' ')}"',
637 );
638 }
639
640 @override
641 Future<io.Process> start(
642 List<Object> command, {
643 String? workingDirectory,
644 Map<String, String>? environment,
645 bool includeParentEnvironment = true,
646 bool runInShell = false,
647 io.ProcessStartMode mode = io.ProcessStartMode.normal,
648 }) {
649 return _run(
650 () {
651 return _delegate.start(
652 command,
653 workingDirectory: workingDirectory,
654 environment: environment,
655 includeParentEnvironment: includeParentEnvironment,
656 runInShell: runInShell,
657 mode: mode,
658 );
659 },
660 platform: _platform,
661 failureMessage: 'Flutter failed to run "${command.join(' ')}"',
662 );
663 }
664
665 @override
666 io.ProcessResult runSync(
667 List<Object> command, {
668 String? workingDirectory,
669 Map<String, String>? environment,
670 bool includeParentEnvironment = true,
671 bool runInShell = false,
672 Encoding? stdoutEncoding = io.systemEncoding,
673 Encoding? stderrEncoding = io.systemEncoding,
674 }) {
675 return _runSync(
676 () {
677 return _delegate.runSync(
678 command,
679 workingDirectory: workingDirectory,
680 environment: environment,
681 includeParentEnvironment: includeParentEnvironment,
682 runInShell: runInShell,
683 stdoutEncoding: stdoutEncoding,
684 stderrEncoding: stderrEncoding,
685 );
686 },
687 platform: _platform,
688 failureMessage: 'Flutter failed to run "${command.join(' ')}"',
689 );
690 }
691}
692
693void _handlePosixException(
694 Exception e,
695 String? message,
696 int errorCode,
697 String? posixPermissionSuggestion,
698) {
699 // From:
700 // https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/errno.h
701 // https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/errno-base.h
702 // https://github.com/apple/darwin-xnu/blob/main/bsd/dev/dtrace/scripts/errno.d
703 const int eperm = 1;
704 const int enospc = 28;
705 const int eacces = 13;
706 // Catch errors and bail when:
707 String? errorMessage;
708 switch (errorCode) {
709 case enospc:
710 errorMessage =
711 '$message. The target device is full.'
712 '\n$e\n'
713 'Free up space and try again.';
714 case eperm:
715 case eacces:
716 final StringBuffer errorBuffer = StringBuffer();
717 if (message != null && message.isNotEmpty) {
718 errorBuffer.writeln('$message.');
719 } else {
720 errorBuffer.writeln('The flutter tool cannot access the file or directory.');
721 }
722 errorBuffer.writeln(
723 'Please ensure that the SDK and/or project is installed in a location '
724 'that has read/write permissions for the current user.',
725 );
726 if (posixPermissionSuggestion != null && posixPermissionSuggestion.isNotEmpty) {
727 errorBuffer.writeln(posixPermissionSuggestion);
728 }
729 errorMessage = errorBuffer.toString();
730 default:
731 // Caller must rethrow the exception.
732 break;
733 }
734 _throwFileSystemException(errorMessage);
735}
736
737void _handleMacOSException(
738 Exception e,
739 String? message,
740 int errorCode,
741 String? posixPermissionSuggestion,
742) {
743 // https://github.com/apple/darwin-xnu/blob/main/bsd/dev/dtrace/scripts/errno.d
744 const int ebadarch = 86;
745 const int eagain = 35;
746 if (errorCode == ebadarch) {
747 final StringBuffer errorBuffer = StringBuffer();
748 if (message != null) {
749 errorBuffer.writeln('$message.');
750 }
751 errorBuffer.writeln(
752 'The binary was built with the incorrect architecture to run on this machine.',
753 );
754 errorBuffer.writeln(
755 'If you are on an ARM Apple Silicon Mac, Flutter requires the Rosetta translation environment. Try running:',
756 );
757 errorBuffer.writeln(' sudo softwareupdate --install-rosetta --agree-to-license');
758 _throwFileSystemException(errorBuffer.toString());
759 }
760 if (errorCode == eagain) {
761 final StringBuffer errorBuffer = StringBuffer();
762 if (message != null) {
763 errorBuffer.writeln('$message.');
764 }
765 errorBuffer.writeln(
766 'Your system may be running into its process limits. '
767 'Consider quitting unused apps and trying again.',
768 );
769 throwToolExit(errorBuffer.toString());
770 }
771 _handlePosixException(e, message, errorCode, posixPermissionSuggestion);
772}
773
774void _handleWindowsException(Exception e, String? message, int errorCode) {
775 // From:
776 // https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes
777 const int kDeviceFull = 112;
778 const int kUserMappedSectionOpened = 1224;
779 const int kAccessDenied = 5;
780 const int kFatalDeviceHardwareError = 483;
781 const int kDeviceDoesNotExist = 433;
782
783 // Catch errors and bail when:
784 String? errorMessage;
785 switch (errorCode) {
786 case kAccessDenied:
787 errorMessage =
788 '$message. The flutter tool cannot access the file or directory.\n'
789 'Please ensure that the SDK and/or project is installed in a location '
790 'that has read/write permissions for the current user.';
791 case kDeviceFull:
792 errorMessage =
793 '$message. The target device is full.'
794 '\n$e\n'
795 'Free up space and try again.';
796 case kUserMappedSectionOpened:
797 errorMessage =
798 '$message. The file is being used by another program.'
799 '\n$e\n'
800 'Do you have an antivirus program running? '
801 'Try disabling your antivirus program and try again.';
802 case kFatalDeviceHardwareError:
803 errorMessage =
804 '$message. There is a problem with the device driver '
805 'that this file or directory is stored on.';
806 case kDeviceDoesNotExist:
807 errorMessage =
808 '$message. The device was not found.'
809 '\n$e\n'
810 'Verify the device is mounted and try again.';
811 default:
812 // Caller must rethrow the exception.
813 break;
814 }
815 _throwFileSystemException(errorMessage);
816}
817
818void _throwFileSystemException(String? errorMessage) {
819 if (errorMessage == null) {
820 return;
821 }
822 if (ErrorHandlingFileSystem._noExitOnFailure) {
823 throw FileSystemException(errorMessage);
824 }
825 throwToolExit(errorMessage);
826}
827

Provided by KDAB

Privacy Policy
Learn more about Flutter for embedded and desktop on industrialflutter.com