Unverified Commit db3f49b1 authored by Jenn Magder's avatar Jenn Magder Committed by GitHub

Use cached Xcode build settings during iOS build (#80904)

parent 2d5ee03c
......@@ -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>{};
......
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