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