Use cached Xcode build settings during iOS build (#80904)
This commit is contained in:
parent
2d5ee03c04
commit
db3f49b1b4
@ -176,9 +176,15 @@ Future<XcodeBuildResult> buildXcodeProject({
|
||||
}
|
||||
|
||||
Map<String, String> autoSigningConfigs;
|
||||
|
||||
final Map<String, String> buildSettings = await app.project.buildSettingsForBuildInfo(
|
||||
buildInfo,
|
||||
environmentType: buildForDevice ? EnvironmentType.physical : EnvironmentType.simulator,
|
||||
) ?? <String, String>{};
|
||||
|
||||
if (codesign && buildForDevice) {
|
||||
autoSigningConfigs = await getCodeSigningIdentityDevelopmentTeam(
|
||||
buildSettings: await app.project.buildSettingsForBuildInfo(buildInfo),
|
||||
buildSettings: buildSettings,
|
||||
processManager: globals.processManager,
|
||||
logger: globals.logger,
|
||||
config: globals.config,
|
||||
@ -353,45 +359,6 @@ Future<XcodeBuildResult> buildXcodeProject({
|
||||
);
|
||||
globals.flutterUsage.sendTiming(xcodeBuildActionToString(buildAction), 'xcode-ios', Duration(milliseconds: sw.elapsedMilliseconds));
|
||||
|
||||
// Run -showBuildSettings again but with the exact same parameters as the
|
||||
// build. showBuildSettings is reported to occasionally timeout. Here, we give
|
||||
// it a lot of wiggle room (locally on Flutter Gallery, this takes ~1s).
|
||||
// When there is a timeout, we retry once. See issue #35988.
|
||||
final List<String> showBuildSettingsCommand = (List<String>
|
||||
.of(buildCommands)
|
||||
..add('-showBuildSettings'))
|
||||
// Undocumented behavior: xcodebuild craps out if -showBuildSettings
|
||||
// is used together with -allowProvisioningUpdates or
|
||||
// -allowProvisioningDeviceRegistration and freezes forever.
|
||||
.where((String buildCommand) {
|
||||
return !const <String>[
|
||||
'-allowProvisioningUpdates',
|
||||
'-allowProvisioningDeviceRegistration',
|
||||
].contains(buildCommand);
|
||||
}).toList();
|
||||
const Duration showBuildSettingsTimeout = Duration(minutes: 1);
|
||||
Map<String, String> buildSettings;
|
||||
try {
|
||||
final RunResult showBuildSettingsResult = await globals.processUtils.run(
|
||||
showBuildSettingsCommand,
|
||||
throwOnError: true,
|
||||
workingDirectory: app.project.hostAppRoot.path,
|
||||
timeout: showBuildSettingsTimeout,
|
||||
timeoutRetries: 1,
|
||||
);
|
||||
final String showBuildSettings = showBuildSettingsResult.stdout.trim();
|
||||
buildSettings = parseXcodeBuildSettings(showBuildSettings);
|
||||
} on ProcessException catch (e) {
|
||||
if (e.toString().contains('timed out')) {
|
||||
BuildEvent('xcode-show-build-settings-timeout',
|
||||
type: 'ios',
|
||||
command: showBuildSettingsCommand.join(' '),
|
||||
flutterUsage: globals.flutterUsage,
|
||||
).send();
|
||||
}
|
||||
rethrow;
|
||||
}
|
||||
|
||||
if (buildResult.exitCode != 0) {
|
||||
globals.printStatus('Failed to build iOS app');
|
||||
if (buildResult.stderr.isNotEmpty) {
|
||||
|
@ -166,10 +166,12 @@ class XcodeProjectInterpreter {
|
||||
/// target (by default this is Runner).
|
||||
Future<Map<String, String>> getBuildSettings(
|
||||
String projectPath, {
|
||||
String? scheme,
|
||||
required XcodeProjectBuildContext buildContext,
|
||||
Duration timeout = const Duration(minutes: 1),
|
||||
}) async {
|
||||
final Status status = _logger.startSpinner();
|
||||
final String? scheme = buildContext.scheme;
|
||||
final String? configuration = buildContext.configuration;
|
||||
final List<String> showBuildSettingsCommand = <String>[
|
||||
...xcrunCommand(),
|
||||
'xcodebuild',
|
||||
@ -177,7 +179,12 @@ class XcodeProjectInterpreter {
|
||||
_fileSystem.path.absolute(projectPath),
|
||||
if (scheme != null)
|
||||
...<String>['-scheme', scheme],
|
||||
if (configuration != null)
|
||||
...<String>['-configuration', configuration],
|
||||
if (buildContext.environmentType == EnvironmentType.simulator)
|
||||
...<String>['-sdk', 'iphonesimulator'],
|
||||
'-showBuildSettings',
|
||||
'BUILD_DIR=${_fileSystem.path.absolute(getIosBuildDirectory())}',
|
||||
...environmentVariablesAsXcodeBuildSettings(_platform)
|
||||
];
|
||||
try {
|
||||
@ -201,7 +208,7 @@ class XcodeProjectInterpreter {
|
||||
flutterUsage: _usage,
|
||||
).send();
|
||||
}
|
||||
_logger.printTrace('Unexpected failure to get the build settings: $error.');
|
||||
_logger.printTrace('Unexpected failure to get Xcode build settings: $error.');
|
||||
return const <String, String>{};
|
||||
} finally {
|
||||
status.stop();
|
||||
@ -282,6 +289,29 @@ String substituteXcodeVariables(String str, Map<String, String> xcodeBuildSettin
|
||||
return str.replaceAllMapped(_varExpr, (Match m) => xcodeBuildSettings[m[1]!] ?? m[0]!);
|
||||
}
|
||||
|
||||
@immutable
|
||||
class XcodeProjectBuildContext {
|
||||
const XcodeProjectBuildContext({this.scheme, this.configuration, this.environmentType = EnvironmentType.physical});
|
||||
|
||||
final String? scheme;
|
||||
final String? configuration;
|
||||
final EnvironmentType environmentType;
|
||||
|
||||
@override
|
||||
int get hashCode => scheme.hashCode ^ configuration.hashCode ^ environmentType.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(other, this)) {
|
||||
return true;
|
||||
}
|
||||
return other is XcodeProjectBuildContext &&
|
||||
other.scheme == scheme &&
|
||||
other.configuration == configuration &&
|
||||
other.environmentType == environmentType;
|
||||
}
|
||||
}
|
||||
|
||||
/// Information about an Xcode project.
|
||||
///
|
||||
/// Represents the output of `xcodebuild -list`.
|
||||
@ -365,7 +395,7 @@ class XcodeProjectInfo {
|
||||
});
|
||||
}
|
||||
|
||||
void reportFlavorNotFoundAndExit() {
|
||||
Never reportFlavorNotFoundAndExit() {
|
||||
_logger.printError('');
|
||||
if (definesCustomSchemes) {
|
||||
_logger.printError('The Xcode project defines schemes: ${schemes.join(', ')}');
|
||||
@ -377,7 +407,10 @@ class XcodeProjectInfo {
|
||||
|
||||
/// Returns unique build configuration matching [buildInfo] and [scheme], or
|
||||
/// null, if there is no unique best match.
|
||||
String? buildConfigurationFor(BuildInfo buildInfo, String scheme) {
|
||||
String? buildConfigurationFor(BuildInfo? buildInfo, String scheme) {
|
||||
if (buildInfo == null) {
|
||||
return null;
|
||||
}
|
||||
final String expectedConfiguration = expectedBuildConfigurationFor(buildInfo, scheme);
|
||||
if (hasBuildConfigurationForBuildMode(expectedConfiguration)) {
|
||||
return expectedConfiguration;
|
||||
|
@ -248,6 +248,7 @@ class CocoaPods {
|
||||
} else {
|
||||
final bool isSwift = (await _xcodeProjectInterpreter.getBuildSettings(
|
||||
runnerProject.path,
|
||||
buildContext: const XcodeProjectBuildContext(),
|
||||
)).containsKey('SWIFT_VERSION');
|
||||
podfileTemplateName = isSwift ? 'Podfile-ios-swift' : 'Podfile-ios-objc';
|
||||
}
|
||||
|
@ -593,11 +593,10 @@ class IosProject extends FlutterProjectPlatform implements XcodeBasedProject {
|
||||
/// The build settings for the host app of this project, as a detached map.
|
||||
///
|
||||
/// Returns null, if iOS tooling is unavailable.
|
||||
Future<Map<String, String>> buildSettingsForBuildInfo(BuildInfo buildInfo) async {
|
||||
Future<Map<String, String>> buildSettingsForBuildInfo(BuildInfo buildInfo, { EnvironmentType environmentType = EnvironmentType.physical }) async {
|
||||
if (!existsSync()) {
|
||||
return null;
|
||||
}
|
||||
_buildSettingsByScheme ??= <String, Map<String, String>>{};
|
||||
final XcodeProjectInfo info = await projectInfo();
|
||||
if (info == null) {
|
||||
return null;
|
||||
@ -608,9 +607,15 @@ class IosProject extends FlutterProjectPlatform implements XcodeBasedProject {
|
||||
info.reportFlavorNotFoundAndExit();
|
||||
}
|
||||
|
||||
return _buildSettingsByScheme[scheme] ??= await _xcodeProjectBuildSettings(scheme);
|
||||
final String configuration = (await projectInfo()).buildConfigurationFor(
|
||||
buildInfo,
|
||||
scheme,
|
||||
);
|
||||
final XcodeProjectBuildContext buildContext = XcodeProjectBuildContext(environmentType: environmentType, scheme: scheme, configuration: configuration);
|
||||
return _buildSettingsByBuildContext[buildContext] ??= await _xcodeProjectBuildSettings(buildContext);
|
||||
}
|
||||
Map<String, Map<String, String>> _buildSettingsByScheme;
|
||||
|
||||
final Map<XcodeProjectBuildContext, Map<String, String>> _buildSettingsByBuildContext = <XcodeProjectBuildContext, Map<String, String>>{};
|
||||
|
||||
Future<XcodeProjectInfo> projectInfo() async {
|
||||
if (!xcodeProject.existsSync() || !globals.xcodeProjectInterpreter.isInstalled) {
|
||||
@ -620,13 +625,14 @@ class IosProject extends FlutterProjectPlatform implements XcodeBasedProject {
|
||||
}
|
||||
XcodeProjectInfo _projectInfo;
|
||||
|
||||
Future<Map<String, String>> _xcodeProjectBuildSettings(String scheme) async {
|
||||
Future<Map<String, String>> _xcodeProjectBuildSettings(XcodeProjectBuildContext buildContext) async {
|
||||
if (!globals.xcodeProjectInterpreter.isInstalled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final Map<String, String> buildSettings = await globals.xcodeProjectInterpreter.getBuildSettings(
|
||||
xcodeProject.path,
|
||||
scheme: scheme,
|
||||
buildContext: buildContext,
|
||||
);
|
||||
if (buildSettings != null && buildSettings.isNotEmpty) {
|
||||
// No timeouts, flakes, or errors.
|
||||
|
@ -22,12 +22,14 @@ class FakeXcodeProjectInterpreterWithBuildSettings extends FakeXcodeProjectInter
|
||||
@override
|
||||
Future<Map<String, String>> getBuildSettings(
|
||||
String projectPath, {
|
||||
String scheme,
|
||||
XcodeProjectBuildContext buildContext,
|
||||
Duration timeout = const Duration(minutes: 1),
|
||||
}) async {
|
||||
return <String, String>{
|
||||
'PRODUCT_BUNDLE_IDENTIFIER': 'io.flutter.someProject',
|
||||
'DEVELOPMENT_TEAM': 'abc',
|
||||
'TARGET_BUILD_DIR': 'build/ios/Release-iphoneos',
|
||||
'WRAPPER_NAME': 'Runner.app',
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -92,12 +94,16 @@ void main() {
|
||||
|
||||
// Creates a FakeCommand for the xcodebuild call to build the app
|
||||
// in the given configuration.
|
||||
FakeCommand _setUpFakeXcodeBuildHandler({ bool verbose = false, bool showBuildSettings = false, void Function() onRun }) {
|
||||
FakeCommand _setUpFakeXcodeBuildHandler({ bool verbose = false, bool simulator = false, void Function() onRun }) {
|
||||
return FakeCommand(
|
||||
command: <String>[
|
||||
'xcrun',
|
||||
'xcodebuild',
|
||||
'-configuration', 'Release',
|
||||
'-configuration',
|
||||
if (simulator)
|
||||
'Debug'
|
||||
else
|
||||
'Release',
|
||||
if (verbose)
|
||||
'VERBOSE_SCRIPT_LOGGING=YES'
|
||||
else
|
||||
@ -105,11 +111,13 @@ void main() {
|
||||
'-workspace', 'Runner.xcworkspace',
|
||||
'-scheme', 'Runner',
|
||||
'BUILD_DIR=/build/ios',
|
||||
'-sdk', 'iphoneos',
|
||||
'-sdk',
|
||||
if (simulator)
|
||||
'iphonesimulator'
|
||||
else
|
||||
'iphoneos',
|
||||
'FLUTTER_SUPPRESS_ANALYTICS=true',
|
||||
'COMPILER_INDEX_STORE_ENABLE=NO',
|
||||
if (showBuildSettings)
|
||||
'-showBuildSettings',
|
||||
],
|
||||
stdout: '''
|
||||
TARGET_BUILD_DIR=build/ios/Release-iphoneos
|
||||
@ -179,7 +187,26 @@ void main() {
|
||||
_setUpFakeXcodeBuildHandler(onRun: () {
|
||||
fileSystem.directory('build/ios/Release-iphoneos/Runner.app').createSync(recursive: true);
|
||||
}),
|
||||
_setUpFakeXcodeBuildHandler(showBuildSettings: true),
|
||||
_setUpRsyncCommand(),
|
||||
]),
|
||||
Platform: () => macosPlatform,
|
||||
XcodeProjectInterpreter: () => FakeXcodeProjectInterpreterWithBuildSettings(),
|
||||
});
|
||||
|
||||
testUsingContext('ios simulator build invokes xcode build', () async {
|
||||
final BuildCommand command = BuildCommand();
|
||||
_createMinimalMockProjectFiles();
|
||||
|
||||
await createTestCommandRunner(command).run(
|
||||
const <String>['build', 'ios', '--simulator', '--no-pub']
|
||||
);
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => fileSystem,
|
||||
ProcessManager: () => FakeProcessManager.list(<FakeCommand>[
|
||||
xattrCommand,
|
||||
_setUpFakeXcodeBuildHandler(simulator: true, onRun: () {
|
||||
fileSystem.directory('build/ios/Debug-iphonesimulator/Runner.app').createSync(recursive: true);
|
||||
}),
|
||||
_setUpRsyncCommand(),
|
||||
]),
|
||||
Platform: () => macosPlatform,
|
||||
@ -200,7 +227,6 @@ void main() {
|
||||
_setUpFakeXcodeBuildHandler(verbose: true, onRun: () {
|
||||
fileSystem.directory('build/ios/Release-iphoneos/Runner.app').createSync(recursive: true);
|
||||
}),
|
||||
_setUpFakeXcodeBuildHandler(verbose: true, showBuildSettings: true),
|
||||
_setUpRsyncCommand(),
|
||||
]),
|
||||
Platform: () => macosPlatform,
|
||||
@ -241,7 +267,6 @@ void main() {
|
||||
..createSync(recursive: true)
|
||||
..writeAsStringSync('{}');
|
||||
}),
|
||||
_setUpFakeXcodeBuildHandler(showBuildSettings: true),
|
||||
_setUpRsyncCommand(onRun: () => fileSystem.file('build/ios/iphoneos/Runner.app/Frameworks/App.framework/App')
|
||||
..createSync(recursive: true)
|
||||
..writeAsBytesSync(List<int>.generate(10000, (int index) => 0))),
|
||||
|
@ -21,7 +21,7 @@ class FakeXcodeProjectInterpreterWithBuildSettings extends FakeXcodeProjectInter
|
||||
@override
|
||||
Future<Map<String, String>> getBuildSettings(
|
||||
String projectPath, {
|
||||
String scheme,
|
||||
XcodeProjectBuildContext buildContext,
|
||||
Duration timeout = const Duration(minutes: 1),
|
||||
}) async {
|
||||
return <String, String>{
|
||||
@ -81,7 +81,7 @@ void main() {
|
||||
|
||||
// Creates a FakeCommand for the xcodebuild call to build the app
|
||||
// in the given configuration.
|
||||
FakeCommand setUpFakeXcodeBuildHandler({ bool verbose = false, bool showBuildSettings = false, void Function() onRun }) {
|
||||
FakeCommand setUpFakeXcodeBuildHandler({ bool verbose = false, void Function() onRun }) {
|
||||
return FakeCommand(
|
||||
command: <String>[
|
||||
'xcrun',
|
||||
@ -98,8 +98,6 @@ void main() {
|
||||
'COMPILER_INDEX_STORE_ENABLE=NO',
|
||||
'-archivePath', '/build/ios/archive/Runner',
|
||||
'archive',
|
||||
if (showBuildSettings)
|
||||
'-showBuildSettings',
|
||||
],
|
||||
stdout: 'STDOUT STUFF',
|
||||
onRun: onRun,
|
||||
@ -224,7 +222,6 @@ void main() {
|
||||
ProcessManager: () => FakeProcessManager.list(<FakeCommand>[
|
||||
xattrCommand,
|
||||
setUpFakeXcodeBuildHandler(),
|
||||
setUpFakeXcodeBuildHandler(showBuildSettings: true),
|
||||
]),
|
||||
Platform: () => macosPlatform,
|
||||
XcodeProjectInterpreter: () => FakeXcodeProjectInterpreterWithBuildSettings(),
|
||||
@ -242,7 +239,6 @@ void main() {
|
||||
ProcessManager: () => FakeProcessManager.list(<FakeCommand>[
|
||||
xattrCommand,
|
||||
setUpFakeXcodeBuildHandler(verbose: true),
|
||||
setUpFakeXcodeBuildHandler(verbose: true, showBuildSettings: true),
|
||||
]),
|
||||
Platform: () => macosPlatform,
|
||||
XcodeProjectInterpreter: () => FakeXcodeProjectInterpreterWithBuildSettings(),
|
||||
@ -303,7 +299,6 @@ void main() {
|
||||
..createSync(recursive: true)
|
||||
..writeAsStringSync('{}');
|
||||
}),
|
||||
setUpFakeXcodeBuildHandler(showBuildSettings: true),
|
||||
]),
|
||||
Platform: () => macosPlatform,
|
||||
FileSystemUtils: () => FileSystemUtils(fileSystem: fileSystem, platform: macosPlatform),
|
||||
@ -335,7 +330,6 @@ void main() {
|
||||
ProcessManager: () => FakeProcessManager.list(<FakeCommand>[
|
||||
xattrCommand,
|
||||
setUpFakeXcodeBuildHandler(),
|
||||
setUpFakeXcodeBuildHandler(showBuildSettings: true),
|
||||
exportArchiveCommand,
|
||||
]),
|
||||
Platform: () => macosPlatform,
|
||||
|
@ -105,6 +105,13 @@ void main() {
|
||||
));
|
||||
}
|
||||
);
|
||||
when(mockXcodeProjectInterpreter.getBuildSettings(any, buildContext: anyNamed('buildContext')))
|
||||
.thenAnswer((_) async => <String, String>{
|
||||
'TARGET_BUILD_DIR': 'build/ios/Release-iphoneos',
|
||||
'WRAPPER_NAME': 'My Super Awesome App.app',
|
||||
'DEVELOPMENT_TEAM': '3333CCCC33',
|
||||
});
|
||||
|
||||
xcode = Xcode.test(processManager: FakeProcessManager.any(), xcodeProjectInterpreter: mockXcodeProjectInterpreter);
|
||||
fileSystem.file('foo/.packages')
|
||||
..createSync(recursive: true)
|
||||
@ -125,11 +132,6 @@ void main() {
|
||||
|
||||
processManager.addCommand(FakeCommand(command: _xattrArgs(flutterProject)));
|
||||
processManager.addCommand(const FakeCommand(command: kRunReleaseArgs));
|
||||
processManager.addCommand(const FakeCommand(command: <String>[...kRunReleaseArgs, '-showBuildSettings'], stdout: r'''
|
||||
TARGET_BUILD_DIR=build/ios/Release-iphoneos
|
||||
WRAPPER_NAME=My Super Awesome App.app
|
||||
'''
|
||||
));
|
||||
processManager.addCommand(const FakeCommand(command: <String>[
|
||||
'rsync',
|
||||
'-av',
|
||||
@ -174,95 +176,6 @@ void main() {
|
||||
Xcode: () => xcode,
|
||||
});
|
||||
|
||||
testUsingContext('with flaky buildSettings call', () async {
|
||||
LaunchResult launchResult;
|
||||
FakeAsync().run((FakeAsync time) {
|
||||
final IOSDevice iosDevice = setUpIOSDevice(
|
||||
fileSystem: fileSystem,
|
||||
processManager: processManager,
|
||||
logger: logger,
|
||||
artifacts: artifacts,
|
||||
);
|
||||
setUpIOSProject(fileSystem);
|
||||
final FlutterProject flutterProject = FlutterProject.fromDirectory(fileSystem.currentDirectory);
|
||||
final BuildableIOSApp buildableIOSApp = BuildableIOSApp(flutterProject.ios, 'flutter', 'My Super Awesome App.app');
|
||||
fileSystem.directory('build/ios/Release-iphoneos/My Super Awesome App.app').createSync(recursive: true);
|
||||
|
||||
processManager.addCommand(FakeCommand(command: _xattrArgs(flutterProject)));
|
||||
processManager.addCommand(const FakeCommand(command: kRunReleaseArgs));
|
||||
// The first showBuildSettings call should timeout.
|
||||
processManager.addCommand(
|
||||
const FakeCommand(
|
||||
command: <String>[...kRunReleaseArgs, '-showBuildSettings'],
|
||||
duration: Duration(minutes: 5), // this is longer than the timeout of 1 minute.
|
||||
));
|
||||
// The second call succeeds and is made after the first times out.
|
||||
processManager.addCommand(
|
||||
const FakeCommand(
|
||||
command: <String>[...kRunReleaseArgs, '-showBuildSettings'],
|
||||
exitCode: 0,
|
||||
stdout: r'''
|
||||
TARGET_BUILD_DIR=build/ios/Release-iphoneos
|
||||
WRAPPER_NAME=My Super Awesome App.app
|
||||
'''
|
||||
));
|
||||
processManager.addCommand(const FakeCommand(command: <String>[
|
||||
'rsync',
|
||||
'-av',
|
||||
'--delete',
|
||||
'build/ios/Release-iphoneos/My Super Awesome App.app',
|
||||
'build/ios/iphoneos',
|
||||
]));
|
||||
processManager.addCommand(FakeCommand(
|
||||
command: <String>[
|
||||
iosDeployPath,
|
||||
'--id',
|
||||
'123',
|
||||
'--bundle',
|
||||
'build/ios/iphoneos/My Super Awesome App.app',
|
||||
'--app_deltas',
|
||||
'build/ios/app-delta',
|
||||
'--no-wifi',
|
||||
'--justlaunch',
|
||||
'--args',
|
||||
const <String>[
|
||||
'--enable-dart-profiling',
|
||||
'--disable-service-auth-codes',
|
||||
].join(' ')
|
||||
])
|
||||
);
|
||||
|
||||
iosDevice.startApp(
|
||||
buildableIOSApp,
|
||||
debuggingOptions: DebuggingOptions.disabled(BuildInfo.release),
|
||||
platformArgs: <String, Object>{},
|
||||
).then((LaunchResult result) {
|
||||
launchResult = result;
|
||||
});
|
||||
|
||||
// Elapse duration for process timeout.
|
||||
time.flushMicrotasks();
|
||||
time.elapse(const Duration(minutes: 1));
|
||||
|
||||
// Elapse duration for overall process timer.
|
||||
time.flushMicrotasks();
|
||||
time.elapse(const Duration(minutes: 5));
|
||||
|
||||
time.flushTimers();
|
||||
});
|
||||
|
||||
expect(launchResult?.started, true);
|
||||
expect(fileSystem.directory('build/ios/iphoneos'), exists);
|
||||
expect(processManager, hasNoRemainingExpectations);
|
||||
}, overrides: <Type, Generator>{
|
||||
ProcessManager: () => processManager,
|
||||
FileSystem: () => fileSystem,
|
||||
Logger: () => logger,
|
||||
Platform: () => macPlatform,
|
||||
XcodeProjectInterpreter: () => mockXcodeProjectInterpreter,
|
||||
Xcode: () => xcode,
|
||||
});
|
||||
|
||||
testUsingContext('with concurrent build failures', () async {
|
||||
final IOSDevice iosDevice = setUpIOSDevice(
|
||||
fileSystem: fileSystem,
|
||||
@ -284,11 +197,6 @@ void main() {
|
||||
stdout: kConcurrentBuildErrorMessage,
|
||||
));
|
||||
processManager.addCommand(const FakeCommand(command: kRunReleaseArgs));
|
||||
processManager.addCommand(
|
||||
const FakeCommand(
|
||||
command: <String>[...kRunReleaseArgs, '-showBuildSettings'],
|
||||
exitCode: 0,
|
||||
));
|
||||
processManager.addCommand(FakeCommand(
|
||||
command: <String>[
|
||||
iosDeployPath,
|
||||
|
@ -46,7 +46,7 @@ void main() {
|
||||
);
|
||||
});
|
||||
|
||||
testWithoutContext('xcodebuild build settings flakes', () async {
|
||||
testUsingContext('xcodebuild build settings flakes', () async {
|
||||
const Duration delay = Duration(seconds: 1);
|
||||
processManager.processFactory = mocks.flakyProcessFactory(
|
||||
flakes: 1,
|
||||
@ -59,8 +59,7 @@ void main() {
|
||||
when(processManager.runSync(<String>['sysctl', 'hw.optional.arm64']))
|
||||
.thenReturn(ProcessResult(0, 1, '', ''));
|
||||
|
||||
expect(await xcodeProjectInterpreter.getBuildSettings(
|
||||
'', scheme: 'Runner', timeout: delay),
|
||||
expect(await xcodeProjectInterpreter.getBuildSettings('', buildContext: const XcodeProjectBuildContext(scheme: 'Runner'), timeout: delay),
|
||||
const <String, String>{});
|
||||
// build settings times out and is killed once, then succeeds.
|
||||
verify(processManager.killPid(any)).called(1);
|
||||
@ -292,12 +291,12 @@ void main() {
|
||||
expect(fakeProcessManager.hasRemainingExpectations, isFalse);
|
||||
});
|
||||
|
||||
testWithoutContext('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 {
|
||||
platform.environment = const <String, String>{};
|
||||
|
||||
fakeProcessManager.addCommands(const <FakeCommand>[
|
||||
fakeProcessManager.addCommands(<FakeCommand>[
|
||||
kWhichSysctlCommand,
|
||||
FakeCommand(
|
||||
const FakeCommand(
|
||||
command: <String>[
|
||||
'sysctl',
|
||||
'hw.optional.arm64',
|
||||
@ -312,20 +311,26 @@ void main() {
|
||||
'/',
|
||||
'-scheme',
|
||||
'Free',
|
||||
'-showBuildSettings'
|
||||
'-showBuildSettings',
|
||||
'BUILD_DIR=${fileSystem.path.absolute('build', 'ios')}',
|
||||
],
|
||||
exitCode: 1,
|
||||
),
|
||||
]);
|
||||
|
||||
expect(await xcodeProjectInterpreter.getBuildSettings('', scheme: 'Free'), const <String, String>{});
|
||||
expect(
|
||||
await xcodeProjectInterpreter.getBuildSettings('', buildContext: const XcodeProjectBuildContext(scheme: 'Free')),
|
||||
const <String, String>{});
|
||||
expect(fakeProcessManager.hasRemainingExpectations, isFalse);
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => fileSystem,
|
||||
ProcessManager: () => FakeProcessManager.any(),
|
||||
});
|
||||
|
||||
testWithoutContext('build settings accepts an empty scheme', () async {
|
||||
testUsingContext('build settings passes in the simulator SDK', () async {
|
||||
platform.environment = const <String, String>{};
|
||||
|
||||
fakeProcessManager.addCommands(const <FakeCommand>[
|
||||
fakeProcessManager.addCommands(<FakeCommand>[
|
||||
kWhichSysctlCommand,
|
||||
kARMCheckCommand,
|
||||
FakeCommand(
|
||||
@ -334,17 +339,56 @@ void main() {
|
||||
'xcodebuild',
|
||||
'-project',
|
||||
'/',
|
||||
'-showBuildSettings'
|
||||
'-sdk',
|
||||
'iphonesimulator',
|
||||
'-showBuildSettings',
|
||||
'BUILD_DIR=${fileSystem.path.absolute('build', 'ios')}',
|
||||
],
|
||||
exitCode: 1,
|
||||
),
|
||||
]);
|
||||
|
||||
expect(await xcodeProjectInterpreter.getBuildSettings(''), const <String, String>{});
|
||||
expect(
|
||||
await xcodeProjectInterpreter.getBuildSettings(
|
||||
'',
|
||||
buildContext: const XcodeProjectBuildContext(environmentType: EnvironmentType.simulator),
|
||||
),
|
||||
const <String, String>{},
|
||||
);
|
||||
expect(fakeProcessManager.hasRemainingExpectations, isFalse);
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => fileSystem,
|
||||
ProcessManager: () => FakeProcessManager.any(),
|
||||
});
|
||||
|
||||
testWithoutContext('xcodebuild build settings contains Flutter Xcode environment variables', () async {
|
||||
testUsingContext('build settings accepts an empty scheme', () async {
|
||||
platform.environment = const <String, String>{};
|
||||
|
||||
fakeProcessManager.addCommands(<FakeCommand>[
|
||||
kWhichSysctlCommand,
|
||||
kARMCheckCommand,
|
||||
FakeCommand(
|
||||
command: <String>[
|
||||
'xcrun',
|
||||
'xcodebuild',
|
||||
'-project',
|
||||
'/',
|
||||
'-showBuildSettings',
|
||||
'BUILD_DIR=${fileSystem.path.absolute('build', 'ios')}',
|
||||
],
|
||||
exitCode: 1,
|
||||
),
|
||||
]);
|
||||
|
||||
expect(await xcodeProjectInterpreter.getBuildSettings('', buildContext: const XcodeProjectBuildContext()),
|
||||
const <String, String>{});
|
||||
expect(fakeProcessManager.hasRemainingExpectations, isFalse);
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => fileSystem,
|
||||
ProcessManager: () => FakeProcessManager.any(),
|
||||
});
|
||||
|
||||
testUsingContext('xcodebuild build settings contains Flutter Xcode environment variables', () async {
|
||||
platform.environment = const <String, String>{
|
||||
'FLUTTER_XCODE_CODE_SIGN_STYLE': 'Manual',
|
||||
'FLUTTER_XCODE_ARCHS': 'arm64'
|
||||
@ -361,13 +405,19 @@ void main() {
|
||||
'-scheme',
|
||||
'Free',
|
||||
'-showBuildSettings',
|
||||
'BUILD_DIR=${fileSystem.path.absolute('build', 'ios')}',
|
||||
'CODE_SIGN_STYLE=Manual',
|
||||
'ARCHS=arm64'
|
||||
],
|
||||
),
|
||||
]);
|
||||
expect(await xcodeProjectInterpreter.getBuildSettings('', scheme: 'Free'), const <String, String>{});
|
||||
expect(
|
||||
await xcodeProjectInterpreter.getBuildSettings('', buildContext: const XcodeProjectBuildContext(scheme: 'Free')),
|
||||
const <String, String>{});
|
||||
expect(fakeProcessManager.hasRemainingExpectations, isFalse);
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => fileSystem,
|
||||
ProcessManager: () => FakeProcessManager.any(),
|
||||
});
|
||||
|
||||
testWithoutContext('xcodebuild clean contains Flutter Xcode environment variables', () async {
|
||||
|
@ -794,7 +794,7 @@ class FakeXcodeProjectInterpreter extends Fake implements XcodeProjectInterprete
|
||||
@override
|
||||
Future<Map<String, String>> getBuildSettings(
|
||||
String projectPath, {
|
||||
String scheme,
|
||||
XcodeProjectBuildContext buildContext,
|
||||
Duration timeout = const Duration(minutes: 1),
|
||||
}) async => buildSettings;
|
||||
|
||||
|
@ -397,7 +397,7 @@ apply plugin: 'kotlin-android'
|
||||
testWithMocks('from build settings, if no plist', () async {
|
||||
final FlutterProject project = await someProject();
|
||||
project.ios.xcodeProject.createSync();
|
||||
when(mockXcodeProjectInterpreter.getBuildSettings(any, scheme: anyNamed('scheme'))).thenAnswer(
|
||||
when(mockXcodeProjectInterpreter.getBuildSettings(any, buildContext: anyNamed('buildContext'))).thenAnswer(
|
||||
(_) {
|
||||
return Future<Map<String,String>>.value(<String, String>{
|
||||
'PRODUCT_BUNDLE_IDENTIFIER': 'io.flutter.someProject',
|
||||
@ -428,7 +428,7 @@ apply plugin: 'kotlin-android'
|
||||
testWithMocks('from build settings and plist, if default variable', () async {
|
||||
final FlutterProject project = await someProject();
|
||||
project.ios.xcodeProject.createSync();
|
||||
when(mockXcodeProjectInterpreter.getBuildSettings(any, scheme: anyNamed('scheme'))).thenAnswer(
|
||||
when(mockXcodeProjectInterpreter.getBuildSettings(any, buildContext: anyNamed('buildContext'))).thenAnswer(
|
||||
(_) {
|
||||
return Future<Map<String,String>>.value(<String, String>{
|
||||
'PRODUCT_BUNDLE_IDENTIFIER': 'io.flutter.someProject',
|
||||
@ -447,7 +447,7 @@ apply plugin: 'kotlin-android'
|
||||
final FlutterProject project = await someProject();
|
||||
project.ios.xcodeProject.createSync();
|
||||
project.ios.defaultHostInfoPlist.createSync(recursive: true);
|
||||
when(mockXcodeProjectInterpreter.getBuildSettings(any, scheme: anyNamed('scheme'))).thenAnswer(
|
||||
when(mockXcodeProjectInterpreter.getBuildSettings(any, buildContext: anyNamed('buildContext'))).thenAnswer(
|
||||
(_) {
|
||||
return Future<Map<String,String>>.value(<String, String>{
|
||||
'PRODUCT_BUNDLE_IDENTIFIER': 'io.flutter.someProject',
|
||||
@ -478,7 +478,7 @@ apply plugin: 'kotlin-android'
|
||||
testWithMocks('handles case insensitive flavor', () async {
|
||||
final FlutterProject project = await someProject();
|
||||
project.ios.xcodeProject.createSync();
|
||||
when(mockXcodeProjectInterpreter.getBuildSettings(any, scheme: anyNamed('scheme'))).thenAnswer(
|
||||
when(mockXcodeProjectInterpreter.getBuildSettings(any, buildContext: anyNamed('buildContext'))).thenAnswer(
|
||||
(_) {
|
||||
return Future<Map<String,String>>.value(<String, String>{
|
||||
'PRODUCT_BUNDLE_IDENTIFIER': 'io.flutter.someProject',
|
||||
@ -550,7 +550,7 @@ apply plugin: 'kotlin-android'
|
||||
testUsingContext('app product name xcodebuild settings', () async {
|
||||
final FlutterProject project = await someProject();
|
||||
project.ios.xcodeProject.createSync();
|
||||
when(mockXcodeProjectInterpreter.getBuildSettings(any, scheme: anyNamed('scheme'))).thenAnswer((_) {
|
||||
when(mockXcodeProjectInterpreter.getBuildSettings(any, buildContext: anyNamed('buildContext'))).thenAnswer((_) {
|
||||
return Future<Map<String,String>>.value(<String, String>{
|
||||
'FULL_PRODUCT_NAME': 'My App.app'
|
||||
});
|
||||
@ -667,7 +667,7 @@ apply plugin: 'kotlin-android'
|
||||
|
||||
group('with bundle identifier', () {
|
||||
setUp(() {
|
||||
when(mockXcodeProjectInterpreter.getBuildSettings(any, scheme: anyNamed('scheme'))).thenAnswer(
|
||||
when(mockXcodeProjectInterpreter.getBuildSettings(any, buildContext: anyNamed('buildContext'))).thenAnswer(
|
||||
(_) {
|
||||
return Future<Map<String,String>>.value(<String, String>{
|
||||
'PRODUCT_BUNDLE_IDENTIFIER': 'io.flutter.someProject',
|
||||
|
@ -307,7 +307,7 @@ class FakeXcodeProjectInterpreter implements XcodeProjectInterpreter {
|
||||
@override
|
||||
Future<Map<String, String>> getBuildSettings(
|
||||
String projectPath, {
|
||||
String scheme,
|
||||
XcodeProjectBuildContext buildContext,
|
||||
Duration timeout = const Duration(minutes: 1),
|
||||
}) async {
|
||||
return <String, String>{};
|
||||
|
Loading…
x
Reference in New Issue
Block a user