Repository navigation
[flutter_tools] Do not crash if buildXcodeProject temporary directory is deleted before cleanup - #195918
Open
bkonyi wants to merge 8 commits into
Open
[flutter_tools] Do not crash if buildXcodeProject temporary directory is deleted before cleanup#195918bkonyi wants to merge 8 commits into
bkonyi wants to merge 8 commits into
Conversation
…leted before cleanup
During `buildXcodeProject`, `globals.fs.systemTempDirectory.createTempSync('flutter_ios_build_temp_dir.')` creates a temporary directory for the `xcresult` bundle and optional `pipe_to_stdout` FIFO. If `flutter` receives a termination signal (which triggers `LocalFileSystem.dispose()` and deletes the process-wide system temp directory) or external temp cleanup removes the directory while `xcodebuild` is running, `buildXcodeProject` previously crashed with an unhandled `PathNotFoundException` when writing to `scriptOutputPipeFile` or when calling `tempDir.deleteSync(recursive: true)` in the `finally` block (since `ErrorHandlingDirectory.deleteSync` passes `kSystemCodeCannotFindFile` in `ignoreErrorCodes`, causing `ENOENT` to be rethrown).
Guard `scriptOutputPipeFile.writeAsStringSync('all done')` with `scriptOutputPipeFile.existsSync()` and replace `tempDir.deleteSync(recursive: true)` with `ErrorHandlingFileSystem.deleteIfExists(tempDir, recursive: true)`.
Previous output:
```
+35 -1: xcresults simulator Do not crash if temporary build directory is deleted before cleanup. [E]
Expected: throws <Instance of 'ToolExit'> with `message`: contains 'Encountered error while building for simulator.'
Actual: <Instance of 'Future<void>'>
Which: threw FileSystemException:<FileSystemException: No such file or directory, path = '/.tmp_rand0/flutter_ios_build_temp_dirrand0' (OS Error: No such file or directory, errno = 2)>
stack package:file/src/backends/memory/common.dart 13:5 checkExists
package:file/src/backends/memory/memory_file_system_entity.dart 64:5 MemoryFileSystemEntity.backing
package:file/src/backends/memory/memory_file_system_entity.dart 292:17 MemoryFileSystemEntity.internalDeleteSync
package:file/src/backends/memory/memory_file_system_entity.dart 140:7 MemoryFileSystemEntity.deleteSync
package:flutter_tools/src/ios/mac.dart 637:13 buildXcodeProject
+35 -2: Some tests failed.
```
Current output:
```
+37: All tests passed!
```
…eted after existsSync in buildXcodeProject Wrap the terminal pipe 'all done' write in ErrorHandlingFileSystem.noExitOnFailure and catch FileSystemException so buildXcodeProject does not throw PathNotFoundException or ToolExit if the temporary build directory is removed between existsSync and writeAsStringSync. Because File.writeAsStringSync does not create missing parent directories, the deleted temporary directory is not recreated, and the finally block's ErrorHandlingFileSystem.deleteIfExists(tempDir, recursive: true) cleans up the directory if only the pipe file was removed. Fixes flutter#195906 Previous output: ```text +35 -1: xcresults simulator Do not crash if temporary build directory is deleted before cleanup. [E] Expected: throws <Instance of 'ToolExit'> with `message`: contains 'Encountered error while building for simulator.' Actual: <Instance of 'Future<void>'> Which: threw FileSystemException:<FileSystemException: No such file or directory, path = '/.tmp_rand0/flutter_ios_build_temp_dirrand0' (OS Error: No such file or directory, errno = 2)> stack package:file/src/backends/memory/common.dart 13:5 checkExists package:file/src/backends/memory/memory_file_system_entity.dart 64:5 MemoryFileSystemEntity.backing package:file/src/backends/memory/memory_file_system_entity.dart 292:17 MemoryFileSystemEntity.internalDeleteSync package:file/src/backends/memory/memory_file_system_entity.dart 140:7 MemoryFileSystemEntity.deleteSync package:flutter_tools/src/ios/mac.dart 637:13 buildXcodeProject +35 -3: Some tests failed. ``` Current output: ```text +38: All tests passed! ```
…estWithoutContext Convert the three temporary build directory deletion tests in `build_ios_test.dart` from `testUsingContext` to `testWithoutContext` using `createTestBuildCommand` and `createTestCommandRunner`. Thread `FeatureFlags` explicitly through `buildXcodeProject`, `processPodsIfNeeded`, `BuildIOSCommand`, `BuildIOSArchiveCommand`, and `BuildFrameworkCommand`, and defer `FlutterCommand.analytics` access in `BuildSwiftPackage` so `BuildCommand` and `BuildIOSCommand` can be constructed and run without ambient `AppContext` lookups.
…featureFlags imports
- Guard `listenToScriptOutputLine()` in `buildXcodeProject` with `if (!scriptOutputPipeFile!.existsSync()) { return; }` so the pipe listener exits cleanly if `tempDir` disappears while `xcodebuild` or `xcode_backend` is running.
- Wrap the terminal-session regression test in `ErrorHandlingFileSystem` and initialize an empty pipe file by default in `_PipeOperatingSystemUtils` so the test exercises `listenToScriptOutputLine()`.
- Restrict `features.dart` imports to `show FeatureFlags` in `build_ios_framework.dart`, `build_ios.dart`, `mac.dart`, and `cocoapod_utils.dart`, and privatize `_BuildIOSSubCommand._featureFlags`.
Replaces the ambient `featureFlags` getter reads in `IOSDevice` and `IOSSimulator` with constructor-injected `FeatureFlags` (`_featureFlags`), matching `MacOSDevice`. Threads `required FeatureFlags featureFlags` through `XCDevice`, `IOSSimulatorUtils`, `ToolDependencies`, `context_runner.dart`, and unit test callers, and extracts `kEnoent` in `build_ios_test.dart`.
bkonyi
marked this pull request as ready for review
November 12, 2026 03:22
Contributor
There was a problem hiding this comment.
Code Review
This pull request threads FeatureFlags through various build commands, device classes, and utility methods (such as XCDevice, IOSSimulatorUtils, and processPodsIfNeeded) to avoid relying on global state. Additionally, it hardens the Xcode build process in mac.dart against crashes caused by the temporary build directory or pipe file being deleted mid-build, using ErrorHandlingFileSystem and existence checks. The review feedback highlights a potential TOCTOU race condition in listenToScriptOutputLine where readAsLines() is called after existsSync(), suggesting wrapping it in a try-catch block to prevent unhandled FileSystemException crashes.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
In
buildXcodeProject,fs.systemTempDirectory.createTempSync('flutter_ios_build_temp_dir')creates a temporary directory under the per-invocationLocalFileSystemtemp root. If that temporary directory is deleted beforebuildXcodeProjectfinishes (for example, byLocalFileSystem's fatal-signal temp cleanup onSIGINT/SIGTERMor external cleanup whilexcodebuildis running),listenToScriptOutputLine(),scriptOutputPipeFile?.writeAsStringSync('all done'), and thefinallyblock'stempDir.deleteSync(recursive: true)throw an unhandledPathNotFoundExceptionorToolExit.This change:
listenToScriptOutputLine()withif (!scriptOutputPipeFile!.existsSync()) { return; }beforereadAsLines()so the pipe listener exits cleanly iftempDirdisappears whilexcodebuildorxcode_backendis running.if (scriptOutputPipeFile!.existsSync()) { scriptOutputPipeFile.writeAsStringSync('all done'); }inErrorHandlingFileSystem.noExitOnFailureandtry { ... } on FileSystemExceptionso terminal sessions do not throwPathNotFoundExceptionorToolExitif the temporary directory was removed either before or afterexistsSync()(andFile.writeAsStringSyncdoes not recreate missing parent directories).ErrorHandlingFileSystem.deleteIfExists(tempDir, recursive: true)inbuildXcodeProject'sfinallyblock so cleanup succeeds iftempDirwas already removed and removestempDirif onlypipe_to_stdoutwas unlinked.FeatureFlagstobuildXcodeProject,processPodsIfNeeded,BuildIOSCommand,BuildIOSArchiveCommand,BuildFrameworkCommand, andBuildSwiftPackageUtils(and resolvesFlutterCommand.analyticslazily inBuildSwiftPackage.utils) so the newbuild_ios_test.dartregression tests run undertestWithoutContext.Fixes #195906
Previous output:
Current output:
Pre-launch Checklist
///).