-
Notifications
You must be signed in to change notification settings - Fork 30.4k
Move prefetchSwiftPackages to be per platform #186468
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
0e0f88b
eb602bf
d93499f
4aa7b55
8cc309d
bf69902
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -20,7 +20,6 @@ import '../base/terminal.dart'; | |
| import '../base/utils.dart'; | ||
| import '../base/version.dart'; | ||
| import '../build_info.dart'; | ||
| import '../convert.dart'; | ||
| import '../xcode_project.dart'; | ||
|
|
||
| final _settingExpr = RegExp(r'(\w+)\s*=\s*(.*)$'); | ||
|
|
@@ -193,7 +192,11 @@ class XcodeProjectInterpreter { | |
| // All `xcodebuild` project commands will download and resolve Swift packages. | ||
| // We should always prefetch Swift packages before running any `xcodebuild` project command | ||
| // to control the output. | ||
| await prefetchSwiftPackages(xcodeProject, buildDirectory: buildDirectory, quiet: false); | ||
| await prefetchSwiftPackagesForProject( | ||
| xcodeProject, | ||
| buildDirectory: buildDirectory, | ||
| quiet: false, | ||
| ); | ||
|
|
||
| return _xcodebuildProjectCommandArguments( | ||
| buildDirectory, | ||
|
|
@@ -386,117 +389,25 @@ class XcodeProjectInterpreter { | |
| ], workingDirectory: projectPath); | ||
| } | ||
|
|
||
| /// The process used to fetch Swift packages. | ||
| Process? _swiftPackageFetchProcess; | ||
|
|
||
| /// The stdout subscription for the Swift package fetch process. | ||
| StreamSubscription<String>? _swiftPackageFetchStdoutSubscription; | ||
|
|
||
| /// The stderr subscription for the Swift package fetch process. | ||
| StreamSubscription<String>? _swiftPackageFetchStderrSubscription; | ||
|
|
||
| /// Prefetches Swift packages for the given Xcode project. | ||
| /// | ||
| /// If a process is already running from a previous Flutter command, kill it before starting | ||
| /// the command. If the process is already running from the same Flutter command, wait for it to | ||
| /// complete if [waitForCompletion] is true. | ||
| /// | ||
| /// If [quiet] is false, it will print a spinner while the command is running and print logs of | ||
| /// what Swift packages are being fetched. | ||
| Future<void> prefetchSwiftPackages( | ||
| Future<void> prefetchSwiftPackagesForProject( | ||
| XcodeBasedProject xcodeProject, { | ||
| required Directory buildDirectory, | ||
| bool quiet = true, | ||
| bool waitForCompletion = true, | ||
| }) async { | ||
| final String projectPath = xcodeProject.hostAppRoot.path; | ||
| Status? status; | ||
| try { | ||
| final command = <String>[ | ||
| ..._xcodebuildProjectCommandArguments( | ||
| buildDirectory, | ||
| // skipPackageUpdatesAndValidation should be false so that when subsequent xcodebuild | ||
| // commands run, packages should already be resolved, downloaded, updated, and validated. | ||
| skipPackageUpdatesAndValidation: false, | ||
| ), | ||
| '-resolvePackageDependencies', | ||
| ]; | ||
| if (_swiftPackageFetchProcess == null) { | ||
| // Remove the `xcrun` prefixes from the command before comparing because the process name | ||
| // will resolve to the actual xcodebuild path, such as this: | ||
| // /Applications/Xcode.app/Contents/Developer/usr/bin/xcodebuild | ||
| final int xcodebuildIndex = command.indexOf('xcodebuild'); | ||
| if (xcodebuildIndex == -1) { | ||
| // This should never happen. The _xcodebuildProjectCommandArguments always includes | ||
| // xcodebuild. | ||
| throw StateError('Command "${command.join(' ')}" is expected to contain `xcodebuild`.'); | ||
| } | ||
| final String commandToMatch = command.sublist(xcodebuildIndex).join(' '); | ||
|
|
||
| // Check if process is already running from a previous Flutter command. If it is, kill it | ||
| // so we don't have the process running twice. When this process is run twice, it'll cause | ||
| // one to error. The new process will pick up where the old one left off. | ||
| final RunResult result = await _processUtils.run([ | ||
| 'pgrep', | ||
| '-n', // Select only the newest | ||
| '-f', // Match against full argument lists | ||
| '-l', // Print the process name and process ID | ||
| commandToMatch, // command must be a string rather than a list so it matches on all of it | ||
| ]); | ||
| if (result.exitCode == 0) { | ||
| final String processOutput = result.stdout.trim(); | ||
| // Process output is formatted like this: | ||
| // 89012 /Applications/Xcode.app/Contents/Developer/usr/bin/xcodebuild -clonedSourcePackagesDirPath... | ||
| final int? pid = int.tryParse(processOutput.split(' ').firstOrNull ?? ''); | ||
| if (pid != null && processOutput.endsWith(commandToMatch)) { | ||
| _logger.printTrace( | ||
| 'Swift Package Manager dependencies are already being fetched by PID $pid', | ||
| ); | ||
| await _processUtils.run(['kill', '$pid']); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| final Process process = | ||
| _swiftPackageFetchProcess ?? | ||
| await _processUtils.start(command, workingDirectory: projectPath); | ||
| _swiftPackageFetchProcess ??= process; | ||
| if (!waitForCompletion) { | ||
| return; | ||
| } | ||
| if (!quiet) { | ||
| var printFetchWarnings = false; | ||
| _swiftPackageFetchStdoutSubscription ??= process.stdout | ||
| .transform(utf8.decoder) | ||
| .transform(const LineSplitter()) | ||
| .listen((String line) { | ||
| if (line.startsWith('Fetching')) { | ||
| status?.cancel(); | ||
| if (!printFetchWarnings) { | ||
| _logger.printStatus( | ||
| 'Xcode is fetching Swift Package Manager dependencies. This may take several minutes...', | ||
| ); | ||
| printFetchWarnings = true; | ||
| } | ||
| status = _logger.startProgress(' $line...'); | ||
| } | ||
| }); | ||
| } | ||
| final stderrBuffer = StringBuffer(); | ||
| _swiftPackageFetchStderrSubscription ??= process.stderr | ||
| .transform<String>(const Utf8Decoder(reportErrors: false)) | ||
| .listen(stderrBuffer.write); | ||
|
|
||
| final int exitCode = await process.exitCode.whenComplete(() async { | ||
| await _swiftPackageFetchStdoutSubscription?.cancel(); | ||
| await _swiftPackageFetchStderrSubscription?.cancel(); | ||
| }); | ||
| if (exitCode != 0) { | ||
| throwToolExit('Xcode failed to resolve Swift Package Manager dependencies:\n$stderrBuffer'); | ||
| } | ||
| } finally { | ||
| status?.cancel(); | ||
| } | ||
| await xcodeProject.prefetchSwiftPackages( | ||
| xcodebuildProjectCommandArguments: _xcodebuildProjectCommandArguments( | ||
| buildDirectory, | ||
| // skipPackageUpdatesAndValidation should be false so that when subsequent xcodebuild | ||
| // commands run, packages should already be resolved, downloaded, updated, and validated. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is this comment accurate? what if subsequent xcodebuild command runs before packages are done fetching?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is accurate. Subsequent |
||
| skipPackageUpdatesAndValidation: false, | ||
| ), | ||
| processUtils: _processUtils, | ||
| logger: _logger, | ||
| quiet: quiet, | ||
| waitForCompletion: waitForCompletion, | ||
| ); | ||
| } | ||
|
|
||
| Future<XcodeProjectInfo?> getInfo( | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just to clarify - the original code prefetches for both macOS & iOS?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The original code prefetched for whichever was first (iOS) and then wouldn't prefetch for the next (macOS) because the process would have already been set.