Unverified Commit 8334fb0a authored by Jenn Magder's avatar Jenn Magder Committed by GitHub

Tool exit on xcodebuild -list when Xcode project is corrupted (#82476)

parent 4adf36a0
...@@ -235,6 +235,9 @@ class XcodeProjectInterpreter { ...@@ -235,6 +235,9 @@ class XcodeProjectInterpreter {
// * -project is passed and the given project isn't there, or // * -project is passed and the given project isn't there, or
// * no -project is passed and there isn't a project. // * no -project is passed and there isn't a project.
const int missingProjectExitCode = 66; const int missingProjectExitCode = 66;
// The exit code returned by 'xcodebuild -list' when the project is corrupted.
const int corruptedProjectExitCode = 74;
bool _allowedFailures(int c) => c == missingProjectExitCode || c == corruptedProjectExitCode;
final RunResult result = await _processUtils.run( final RunResult result = await _processUtils.run(
<String>[ <String>[
...xcrunCommand(), ...xcrunCommand(),
...@@ -243,10 +246,11 @@ class XcodeProjectInterpreter { ...@@ -243,10 +246,11 @@ class XcodeProjectInterpreter {
if (projectFilename != null) ...<String>['-project', projectFilename], if (projectFilename != null) ...<String>['-project', projectFilename],
], ],
throwOnError: true, throwOnError: true,
allowedFailures: (int c) => c == missingProjectExitCode, allowedFailures: _allowedFailures,
workingDirectory: projectPath, workingDirectory: projectPath,
); );
if (result.exitCode == missingProjectExitCode) { if (_allowedFailures(result.exitCode)) {
// User configuration error, tool exit instead of crashing.
throwToolExit('Unable to get Xcode project information:\n ${result.stderr}'); throwToolExit('Unable to get Xcode project information:\n ${result.stderr}');
} }
return XcodeProjectInfo.fromXcodeBuildOutput(result.toString(), _logger); return XcodeProjectInfo.fromXcodeBuildOutput(result.toString(), _logger);
......
...@@ -19,6 +19,7 @@ import 'package:flutter_tools/src/reporting/reporting.dart'; ...@@ -19,6 +19,7 @@ import 'package:flutter_tools/src/reporting/reporting.dart';
import '../../src/common.dart'; import '../../src/common.dart';
import '../../src/context.dart'; import '../../src/context.dart';
import '../../src/fake_process_manager.dart';
const String xcodebuild = '/usr/bin/xcodebuild'; const String xcodebuild = '/usr/bin/xcodebuild';
...@@ -80,7 +81,7 @@ void main() { ...@@ -80,7 +81,7 @@ void main() {
]); ]);
expect(xcodeProjectInterpreter.versionText, isNull); expect(xcodeProjectInterpreter.versionText, isNull);
expect(fakeProcessManager.hasRemainingExpectations, isFalse); expect(fakeProcessManager, hasNoRemainingExpectations);
}); });
testWithoutContext('xcodebuild versionText returns null when xcodebuild is not installed', () { testWithoutContext('xcodebuild versionText returns null when xcodebuild is not installed', () {
...@@ -107,7 +108,7 @@ void main() { ...@@ -107,7 +108,7 @@ void main() {
]); ]);
expect(xcodeProjectInterpreter.versionText, 'Xcode 8.3.3, Build version 8E3004b'); expect(xcodeProjectInterpreter.versionText, 'Xcode 8.3.3, Build version 8E3004b');
expect(fakeProcessManager.hasRemainingExpectations, isFalse); expect(fakeProcessManager, hasNoRemainingExpectations);
}); });
testWithoutContext('xcodebuild versionText handles Xcode version string with unexpected format', () { testWithoutContext('xcodebuild versionText handles Xcode version string with unexpected format', () {
...@@ -121,7 +122,7 @@ void main() { ...@@ -121,7 +122,7 @@ void main() {
]); ]);
expect(xcodeProjectInterpreter.versionText, 'Xcode Ultra5000, Build version 8E3004b'); expect(xcodeProjectInterpreter.versionText, 'Xcode Ultra5000, Build version 8E3004b');
expect(fakeProcessManager.hasRemainingExpectations, isFalse); expect(fakeProcessManager, hasNoRemainingExpectations);
}); });
testWithoutContext('xcodebuild version parts can be parsed', () { testWithoutContext('xcodebuild version parts can be parsed', () {
...@@ -135,7 +136,7 @@ void main() { ...@@ -135,7 +136,7 @@ void main() {
]); ]);
expect(xcodeProjectInterpreter.version, Version(11, 4, 1)); expect(xcodeProjectInterpreter.version, Version(11, 4, 1));
expect(fakeProcessManager.hasRemainingExpectations, isFalse); expect(fakeProcessManager, hasNoRemainingExpectations);
}); });
testWithoutContext('xcodebuild minor and patch version default to 0', () { testWithoutContext('xcodebuild minor and patch version default to 0', () {
...@@ -149,7 +150,7 @@ void main() { ...@@ -149,7 +150,7 @@ void main() {
]); ]);
expect(xcodeProjectInterpreter.version, Version(11, 0, 0)); expect(xcodeProjectInterpreter.version, Version(11, 0, 0));
expect(fakeProcessManager.hasRemainingExpectations, isFalse); expect(fakeProcessManager, hasNoRemainingExpectations);
}); });
testWithoutContext('xcodebuild version parts is null when version has unexpected format', () { testWithoutContext('xcodebuild version parts is null when version has unexpected format', () {
...@@ -162,7 +163,7 @@ void main() { ...@@ -162,7 +163,7 @@ void main() {
), ),
]); ]);
expect(xcodeProjectInterpreter.version, isNull); expect(xcodeProjectInterpreter.version, isNull);
expect(fakeProcessManager.hasRemainingExpectations, isFalse); expect(fakeProcessManager, hasNoRemainingExpectations);
}); });
testWithoutContext('xcodebuild isInstalled is false when not on MacOS', () { testWithoutContext('xcodebuild isInstalled is false when not on MacOS', () {
...@@ -177,14 +178,14 @@ void main() { ...@@ -177,14 +178,14 @@ void main() {
fileSystem.file(xcodebuild).deleteSync(); fileSystem.file(xcodebuild).deleteSync();
expect(xcodeProjectInterpreter.isInstalled, isFalse); expect(xcodeProjectInterpreter.isInstalled, isFalse);
expect(fakeProcessManager.hasRemainingExpectations, isFalse); expect(fakeProcessManager, hasNoRemainingExpectations);
}); });
testWithoutContext('xcodebuild isInstalled is false when xcodebuild does not exist', () { testWithoutContext('xcodebuild isInstalled is false when xcodebuild does not exist', () {
fileSystem.file(xcodebuild).deleteSync(); fileSystem.file(xcodebuild).deleteSync();
expect(xcodeProjectInterpreter.isInstalled, isFalse); expect(xcodeProjectInterpreter.isInstalled, isFalse);
expect(fakeProcessManager.hasRemainingExpectations, isFalse); expect(fakeProcessManager, hasNoRemainingExpectations);
}); });
testWithoutContext( testWithoutContext(
...@@ -202,7 +203,7 @@ void main() { ...@@ -202,7 +203,7 @@ void main() {
]); ]);
expect(xcodeProjectInterpreter.isInstalled, isFalse); expect(xcodeProjectInterpreter.isInstalled, isFalse);
expect(fakeProcessManager.hasRemainingExpectations, isFalse); expect(fakeProcessManager, hasNoRemainingExpectations);
}); });
testWithoutContext('xcodebuild isInstalled is false when version has unexpected format', () { testWithoutContext('xcodebuild isInstalled is false when version has unexpected format', () {
...@@ -216,7 +217,7 @@ void main() { ...@@ -216,7 +217,7 @@ void main() {
]); ]);
expect(xcodeProjectInterpreter.isInstalled, isFalse); expect(xcodeProjectInterpreter.isInstalled, isFalse);
expect(fakeProcessManager.hasRemainingExpectations, isFalse); expect(fakeProcessManager, hasNoRemainingExpectations);
}); });
testWithoutContext('xcodebuild isInstalled is true when version has expected format', () { testWithoutContext('xcodebuild isInstalled is true when version has expected format', () {
...@@ -230,7 +231,7 @@ void main() { ...@@ -230,7 +231,7 @@ void main() {
]); ]);
expect(xcodeProjectInterpreter.isInstalled, isTrue); expect(xcodeProjectInterpreter.isInstalled, isTrue);
expect(fakeProcessManager.hasRemainingExpectations, isFalse); expect(fakeProcessManager, hasNoRemainingExpectations);
}); });
testWithoutContext('xcrun runs natively on arm64', () { testWithoutContext('xcrun runs natively on arm64', () {
...@@ -250,7 +251,7 @@ void main() { ...@@ -250,7 +251,7 @@ void main() {
'-arm64e', '-arm64e',
'xcrun', 'xcrun',
]); ]);
expect(fakeProcessManager.hasRemainingExpectations, isFalse); expect(fakeProcessManager, hasNoRemainingExpectations);
}); });
testUsingContext('xcodebuild build settings is empty when xcodebuild failed to get the build settings', () async { testUsingContext('xcodebuild build settings is empty when xcodebuild failed to get the build settings', () async {
...@@ -283,7 +284,7 @@ void main() { ...@@ -283,7 +284,7 @@ void main() {
expect( expect(
await xcodeProjectInterpreter.getBuildSettings('', buildContext: const XcodeProjectBuildContext(scheme: 'Free')), await xcodeProjectInterpreter.getBuildSettings('', buildContext: const XcodeProjectBuildContext(scheme: 'Free')),
const <String, String>{}); const <String, String>{});
expect(fakeProcessManager.hasRemainingExpectations, isFalse); expect(fakeProcessManager, hasNoRemainingExpectations);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
FileSystem: () => fileSystem, FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => FakeProcessManager.any(),
...@@ -317,7 +318,7 @@ void main() { ...@@ -317,7 +318,7 @@ void main() {
), ),
const <String, String>{}, const <String, String>{},
); );
expect(fakeProcessManager.hasRemainingExpectations, isFalse); expect(fakeProcessManager, hasNoRemainingExpectations);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
FileSystem: () => fileSystem, FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => FakeProcessManager.any(),
...@@ -344,7 +345,7 @@ void main() { ...@@ -344,7 +345,7 @@ void main() {
expect(await xcodeProjectInterpreter.getBuildSettings('', buildContext: const XcodeProjectBuildContext()), expect(await xcodeProjectInterpreter.getBuildSettings('', buildContext: const XcodeProjectBuildContext()),
const <String, String>{}); const <String, String>{});
expect(fakeProcessManager.hasRemainingExpectations, isFalse); expect(fakeProcessManager, hasNoRemainingExpectations);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
FileSystem: () => fileSystem, FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => FakeProcessManager.any(),
...@@ -376,7 +377,7 @@ void main() { ...@@ -376,7 +377,7 @@ void main() {
expect( expect(
await xcodeProjectInterpreter.getBuildSettings('', buildContext: const XcodeProjectBuildContext(scheme: 'Free')), await xcodeProjectInterpreter.getBuildSettings('', buildContext: const XcodeProjectBuildContext(scheme: 'Free')),
const <String, String>{}); const <String, String>{});
expect(fakeProcessManager.hasRemainingExpectations, isFalse); expect(fakeProcessManager, hasNoRemainingExpectations);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
FileSystem: () => fileSystem, FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => FakeProcessManager.any(),
...@@ -408,7 +409,7 @@ void main() { ...@@ -408,7 +409,7 @@ void main() {
]); ]);
await xcodeProjectInterpreter.cleanWorkspace('workspace_path', 'Free'); await xcodeProjectInterpreter.cleanWorkspace('workspace_path', 'Free');
expect(fakeProcessManager.hasRemainingExpectations, isFalse); expect(fakeProcessManager, hasNoRemainingExpectations);
}); });
testWithoutContext('xcodebuild -list getInfo returns something when xcodebuild -list succeeds', () async { testWithoutContext('xcodebuild -list getInfo returns something when xcodebuild -list succeeds', () async {
...@@ -430,7 +431,7 @@ void main() { ...@@ -430,7 +431,7 @@ void main() {
); );
expect(await xcodeProjectInterpreter.getInfo(workingDirectory), isNotNull); expect(await xcodeProjectInterpreter.getInfo(workingDirectory), isNotNull);
expect(fakeProcessManager.hasRemainingExpectations, isFalse); expect(fakeProcessManager, hasNoRemainingExpectations);
}); });
testWithoutContext('xcodebuild -list getInfo throws a tool exit when it is unable to find a project', () async { testWithoutContext('xcodebuild -list getInfo throws a tool exit when it is unable to find a project', () async {
...@@ -455,10 +456,34 @@ void main() { ...@@ -455,10 +456,34 @@ void main() {
usage: TestUsage(), usage: TestUsage(),
); );
expect( expect(() => xcodeProjectInterpreter.getInfo(workingDirectory), throwsToolExit(message: stderr));
() async => xcodeProjectInterpreter.getInfo(workingDirectory), expect(fakeProcessManager, hasNoRemainingExpectations);
throwsToolExit(message: stderr)); });
expect(fakeProcessManager.hasRemainingExpectations, isFalse);
testWithoutContext('xcodebuild -list getInfo throws a tool exit when project is corrupted', () async {
const String workingDirectory = '/';
const String stderr = 'Useful Xcode failure message about corrupted project.';
fakeProcessManager.addCommands(const <FakeCommand>[
kWhichSysctlCommand,
kARMCheckCommand,
FakeCommand(
command: <String>['xcrun', 'xcodebuild', '-list'],
exitCode: 74,
stderr: stderr,
),
]);
final XcodeProjectInterpreter xcodeProjectInterpreter = XcodeProjectInterpreter(
logger: logger,
fileSystem: fileSystem,
platform: platform,
processManager: fakeProcessManager,
usage: TestUsage(),
);
expect(() => xcodeProjectInterpreter.getInfo(workingDirectory), throwsToolExit(message: stderr));
expect(fakeProcessManager, hasNoRemainingExpectations);
}); });
testWithoutContext('Xcode project properties from default project can be parsed', () { testWithoutContext('Xcode project properties from default project can be parsed', () {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment