Unverified Commit 61e2e866 authored by Jenn Magder's avatar Jenn Magder Committed by GitHub

Add iOS build -destination flag (#90915)

parent 905ac63e
...@@ -184,7 +184,8 @@ Future<XcodeBuildResult> buildXcodeProject({ ...@@ -184,7 +184,8 @@ Future<XcodeBuildResult> buildXcodeProject({
final Map<String, String> buildSettings = await app.project.buildSettingsForBuildInfo( final Map<String, String> buildSettings = await app.project.buildSettingsForBuildInfo(
buildInfo, buildInfo,
environmentType: environmentType environmentType: environmentType,
deviceId: deviceID,
) ?? <String, String>{}; ) ?? <String, String>{};
if (codesign && environmentType == EnvironmentType.physical) { if (codesign && environmentType == EnvironmentType.physical) {
...@@ -249,11 +250,12 @@ Future<XcodeBuildResult> buildXcodeProject({ ...@@ -249,11 +250,12 @@ Future<XcodeBuildResult> buildXcodeProject({
final bool hasWatchCompanion = await app.project.containsWatchCompanion( final bool hasWatchCompanion = await app.project.containsWatchCompanion(
projectInfo.targets, projectInfo.targets,
buildInfo, buildInfo,
deviceID,
); );
if (hasWatchCompanion) { if (hasWatchCompanion) {
// The -sdk argument has to be omitted if a watchOS companion app exists. // The -sdk argument has to be omitted if a watchOS companion app exists.
// Otherwise the build will fail as WatchKit dependencies cannot be build using the iOS SDK. // Otherwise the build will fail as WatchKit dependencies cannot be build using the iOS SDK.
globals.printStatus('Watch companion app found. Adjusting build settings.'); globals.printStatus('Watch companion app found.');
if (environmentType == EnvironmentType.simulator && (deviceID == null || deviceID == '')) { if (environmentType == EnvironmentType.simulator && (deviceID == null || deviceID == '')) {
globals.printError('No simulator device ID has been set.'); globals.printError('No simulator device ID has been set.');
globals.printError('A device ID is required to build an app with a watchOS companion app.'); globals.printError('A device ID is required to build an app with a watchOS companion app.');
...@@ -261,9 +263,6 @@ Future<XcodeBuildResult> buildXcodeProject({ ...@@ -261,9 +263,6 @@ Future<XcodeBuildResult> buildXcodeProject({
globals.printError('and specify one using the -d, --device-id flag.'); globals.printError('and specify one using the -d, --device-id flag.');
return XcodeBuildResult(success: false); return XcodeBuildResult(success: false);
} }
if (environmentType == EnvironmentType.simulator) {
buildCommands.addAll(<String>['-destination', 'id=$deviceID']);
}
} else { } else {
if (environmentType == EnvironmentType.physical) { if (environmentType == EnvironmentType.physical) {
buildCommands.addAll(<String>['-sdk', 'iphoneos']); buildCommands.addAll(<String>['-sdk', 'iphoneos']);
...@@ -272,6 +271,15 @@ Future<XcodeBuildResult> buildXcodeProject({ ...@@ -272,6 +271,15 @@ Future<XcodeBuildResult> buildXcodeProject({
} }
} }
buildCommands.add('-destination');
if (deviceID != null) {
buildCommands.add('id=$deviceID');
} else if (environmentType == EnvironmentType.physical) {
buildCommands.add('generic/platform=iOS');
} else {
buildCommands.add('generic/platform=iOS Simulator');
}
if (activeArch != null) { if (activeArch != null) {
final String activeArchName = getNameForDarwinArch(activeArch); final String activeArchName = getNameForDarwinArch(activeArch);
if (activeArchName != null) { if (activeArchName != null) {
......
...@@ -173,6 +173,7 @@ class XcodeProjectInterpreter { ...@@ -173,6 +173,7 @@ class XcodeProjectInterpreter {
final Status status = _logger.startSpinner(); final Status status = _logger.startSpinner();
final String? scheme = buildContext.scheme; final String? scheme = buildContext.scheme;
final String? configuration = buildContext.configuration; final String? configuration = buildContext.configuration;
final String? deviceId = buildContext.deviceId;
final List<String> showBuildSettingsCommand = <String>[ final List<String> showBuildSettingsCommand = <String>[
...xcrunCommand(), ...xcrunCommand(),
'xcodebuild', 'xcodebuild',
...@@ -184,6 +185,13 @@ class XcodeProjectInterpreter { ...@@ -184,6 +185,13 @@ class XcodeProjectInterpreter {
...<String>['-configuration', configuration], ...<String>['-configuration', configuration],
if (buildContext.environmentType == EnvironmentType.simulator) if (buildContext.environmentType == EnvironmentType.simulator)
...<String>['-sdk', 'iphonesimulator'], ...<String>['-sdk', 'iphonesimulator'],
'-destination',
if (deviceId != null)
'id=$deviceId'
else if (buildContext.environmentType == EnvironmentType.physical)
'generic/platform=iOS'
else
'generic/platform=iOS Simulator',
'-showBuildSettings', '-showBuildSettings',
'BUILD_DIR=${_fileSystem.path.absolute(getIosBuildDirectory())}', 'BUILD_DIR=${_fileSystem.path.absolute(getIosBuildDirectory())}',
...environmentVariablesAsXcodeBuildSettings(_platform) ...environmentVariablesAsXcodeBuildSettings(_platform)
...@@ -351,14 +359,20 @@ String substituteXcodeVariables(String str, Map<String, String> xcodeBuildSettin ...@@ -351,14 +359,20 @@ String substituteXcodeVariables(String str, Map<String, String> xcodeBuildSettin
@immutable @immutable
class XcodeProjectBuildContext { class XcodeProjectBuildContext {
const XcodeProjectBuildContext({this.scheme, this.configuration, this.environmentType = EnvironmentType.physical}); const XcodeProjectBuildContext({
this.scheme,
this.configuration,
this.environmentType = EnvironmentType.physical,
this.deviceId,
});
final String? scheme; final String? scheme;
final String? configuration; final String? configuration;
final EnvironmentType environmentType; final EnvironmentType environmentType;
final String? deviceId;
@override @override
int get hashCode => Object.hash(scheme, configuration, environmentType); int get hashCode => Object.hash(scheme, configuration, environmentType, deviceId);
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
...@@ -368,6 +382,7 @@ class XcodeProjectBuildContext { ...@@ -368,6 +382,7 @@ class XcodeProjectBuildContext {
return other is XcodeProjectBuildContext && return other is XcodeProjectBuildContext &&
other.scheme == scheme && other.scheme == scheme &&
other.configuration == configuration && other.configuration == configuration &&
other.deviceId == deviceId &&
other.environmentType == environmentType; other.environmentType == environmentType;
} }
} }
......
...@@ -244,7 +244,11 @@ class IosProject extends XcodeBasedProject { ...@@ -244,7 +244,11 @@ class IosProject extends XcodeBasedProject {
/// The build settings for the host app of this project, as a detached map. /// The build settings for the host app of this project, as a detached map.
/// ///
/// Returns null, if iOS tooling is unavailable. /// Returns null, if iOS tooling is unavailable.
Future<Map<String, String>?> buildSettingsForBuildInfo(BuildInfo? buildInfo, { EnvironmentType environmentType = EnvironmentType.physical }) async { Future<Map<String, String>?> buildSettingsForBuildInfo(
BuildInfo? buildInfo, {
EnvironmentType environmentType = EnvironmentType.physical,
String? deviceId,
}) async {
if (!existsSync()) { if (!existsSync()) {
return null; return null;
} }
...@@ -262,7 +266,12 @@ class IosProject extends XcodeBasedProject { ...@@ -262,7 +266,12 @@ class IosProject extends XcodeBasedProject {
buildInfo, buildInfo,
scheme, scheme,
); );
final XcodeProjectBuildContext buildContext = XcodeProjectBuildContext(environmentType: environmentType, scheme: scheme, configuration: configuration); final XcodeProjectBuildContext buildContext = XcodeProjectBuildContext(
environmentType: environmentType,
scheme: scheme,
configuration: configuration,
deviceId: deviceId,
);
final Map<String, String>? currentBuildSettings = _buildSettingsByBuildContext[buildContext]; final Map<String, String>? currentBuildSettings = _buildSettingsByBuildContext[buildContext];
if (currentBuildSettings == null) { if (currentBuildSettings == null) {
final Map<String, String>? calculatedBuildSettings = await _xcodeProjectBuildSettings(buildContext); final Map<String, String>? calculatedBuildSettings = await _xcodeProjectBuildSettings(buildContext);
...@@ -310,7 +319,7 @@ class IosProject extends XcodeBasedProject { ...@@ -310,7 +319,7 @@ class IosProject extends XcodeBasedProject {
} }
/// Check if one the [targets] of the project is a watchOS companion app target. /// Check if one the [targets] of the project is a watchOS companion app target.
Future<bool> containsWatchCompanion(List<String> targets, BuildInfo buildInfo) async { Future<bool> containsWatchCompanion(List<String> targets, BuildInfo buildInfo, String? deviceId) async {
final String? bundleIdentifier = await productBundleIdentifier(buildInfo); final String? bundleIdentifier = await productBundleIdentifier(buildInfo);
// A bundle identifier is required for a companion app. // A bundle identifier is required for a companion app.
if (bundleIdentifier == null) { if (bundleIdentifier == null) {
...@@ -330,7 +339,7 @@ class IosProject extends XcodeBasedProject { ...@@ -330,7 +339,7 @@ class IosProject extends XcodeBasedProject {
// The key WKCompanionAppBundleIdentifier might contain an xcode variable // The key WKCompanionAppBundleIdentifier might contain an xcode variable
// that needs to be substituted before comparing it with bundle id // that needs to be substituted before comparing it with bundle id
if (fromPlist != null && fromPlist.contains(r'$')) { if (fromPlist != null && fromPlist.contains(r'$')) {
final Map<String, String>? allBuildSettings = await buildSettingsForBuildInfo(buildInfo); final Map<String, String>? allBuildSettings = await buildSettingsForBuildInfo(buildInfo, deviceId: deviceId);
if (allBuildSettings != null) { if (allBuildSettings != null) {
final String substitutedVariable = substituteXcodeVariables(fromPlist, allBuildSettings); final String substitutedVariable = substituteXcodeVariables(fromPlist, allBuildSettings);
if (substitutedVariable == bundleIdentifier) { if (substitutedVariable == bundleIdentifier) {
......
...@@ -112,10 +112,15 @@ void main() { ...@@ -112,10 +112,15 @@ void main() {
'-scheme', 'Runner', '-scheme', 'Runner',
'BUILD_DIR=/build/ios', 'BUILD_DIR=/build/ios',
'-sdk', '-sdk',
if (simulator) if (simulator) ...<String>[
'iphonesimulator' 'iphonesimulator',
else '-destination',
'generic/platform=iOS Simulator',
] else ...<String>[
'iphoneos', 'iphoneos',
'-destination',
'generic/platform=iOS',
],
'FLUTTER_SUPPRESS_ANALYTICS=true', 'FLUTTER_SUPPRESS_ANALYTICS=true',
'COMPILER_INDEX_STORE_ENABLE=NO', 'COMPILER_INDEX_STORE_ENABLE=NO',
], ],
......
...@@ -96,6 +96,8 @@ void main() { ...@@ -96,6 +96,8 @@ void main() {
'-workspace', 'Runner.xcworkspace', '-workspace', 'Runner.xcworkspace',
'-scheme', 'Runner', '-scheme', 'Runner',
'-sdk', 'iphoneos', '-sdk', 'iphoneos',
'-destination',
'generic/platform=iOS',
'FLUTTER_SUPPRESS_ANALYTICS=true', 'FLUTTER_SUPPRESS_ANALYTICS=true',
'COMPILER_INDEX_STORE_ENABLE=NO', 'COMPILER_INDEX_STORE_ENABLE=NO',
'-archivePath', '/build/ios/archive/Runner', '-archivePath', '/build/ios/archive/Runner',
......
...@@ -54,6 +54,8 @@ const List<String> kRunReleaseArgs = <String>[ ...@@ -54,6 +54,8 @@ const List<String> kRunReleaseArgs = <String>[
'BUILD_DIR=/build/ios', 'BUILD_DIR=/build/ios',
'-sdk', '-sdk',
'iphoneos', 'iphoneos',
'-destination',
'id=123',
'ONLY_ACTIVE_ARCH=YES', 'ONLY_ACTIVE_ARCH=YES',
'ARCHS=arm64', 'ARCHS=arm64',
'FLUTTER_SUPPRESS_ANALYTICS=true', 'FLUTTER_SUPPRESS_ANALYTICS=true',
......
...@@ -278,6 +278,8 @@ void main() { ...@@ -278,6 +278,8 @@ void main() {
'/', '/',
'-scheme', '-scheme',
'Free', 'Free',
'-destination',
'id=123',
'-showBuildSettings', '-showBuildSettings',
'BUILD_DIR=${fileSystem.path.absolute('build', 'ios')}', 'BUILD_DIR=${fileSystem.path.absolute('build', 'ios')}',
], ],
...@@ -286,7 +288,7 @@ void main() { ...@@ -286,7 +288,7 @@ void main() {
]); ]);
expect( expect(
await xcodeProjectInterpreter.getBuildSettings('', buildContext: const XcodeProjectBuildContext(scheme: 'Free')), await xcodeProjectInterpreter.getBuildSettings('', buildContext: const XcodeProjectBuildContext(deviceId: '123', scheme: 'Free')),
const <String, String>{}); const <String, String>{});
expect(fakeProcessManager, hasNoRemainingExpectations); expect(fakeProcessManager, hasNoRemainingExpectations);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
...@@ -308,6 +310,8 @@ void main() { ...@@ -308,6 +310,8 @@ void main() {
'/', '/',
'-sdk', '-sdk',
'iphonesimulator', 'iphonesimulator',
'-destination',
'generic/platform=iOS Simulator',
'-showBuildSettings', '-showBuildSettings',
'BUILD_DIR=${fileSystem.path.absolute('build', 'ios')}', 'BUILD_DIR=${fileSystem.path.absolute('build', 'ios')}',
], ],
...@@ -340,6 +344,8 @@ void main() { ...@@ -340,6 +344,8 @@ void main() {
'xcodebuild', 'xcodebuild',
'-project', '-project',
'/', '/',
'-destination',
'generic/platform=iOS',
'-showBuildSettings', '-showBuildSettings',
'BUILD_DIR=${fileSystem.path.absolute('build', 'ios')}', 'BUILD_DIR=${fileSystem.path.absolute('build', 'ios')}',
], ],
...@@ -371,6 +377,8 @@ void main() { ...@@ -371,6 +377,8 @@ void main() {
fileSystem.path.separator, fileSystem.path.separator,
'-scheme', '-scheme',
'Free', 'Free',
'-destination',
'generic/platform=iOS',
'-showBuildSettings', '-showBuildSettings',
'BUILD_DIR=${fileSystem.path.absolute('build', 'ios')}', 'BUILD_DIR=${fileSystem.path.absolute('build', 'ios')}',
'CODE_SIGN_STYLE=Manual', 'CODE_SIGN_STYLE=Manual',
......
...@@ -628,7 +628,7 @@ apply plugin: 'kotlin-android' ...@@ -628,7 +628,7 @@ apply plugin: 'kotlin-android'
testUsingContext('cannot find bundle identifier', () async { testUsingContext('cannot find bundle identifier', () async {
final FlutterProject project = await someProject(); final FlutterProject project = await someProject();
expect(await project.ios.containsWatchCompanion(<String>['WatchTarget'], null), isFalse); expect(await project.ios.containsWatchCompanion(<String>['WatchTarget'], null, '123'), isFalse);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
FileSystem: () => fs, FileSystem: () => fs,
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => FakeProcessManager.any(),
...@@ -647,7 +647,7 @@ apply plugin: 'kotlin-android' ...@@ -647,7 +647,7 @@ apply plugin: 'kotlin-android'
testUsingContext('no Info.plist in target', () async { testUsingContext('no Info.plist in target', () async {
final FlutterProject project = await someProject(); final FlutterProject project = await someProject();
expect(await project.ios.containsWatchCompanion(<String>['WatchTarget'], null), isFalse); expect(await project.ios.containsWatchCompanion(<String>['WatchTarget'], null, '123'), isFalse);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
FileSystem: () => fs, FileSystem: () => fs,
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => FakeProcessManager.any(),
...@@ -660,7 +660,7 @@ apply plugin: 'kotlin-android' ...@@ -660,7 +660,7 @@ apply plugin: 'kotlin-android'
final FlutterProject project = await someProject(); final FlutterProject project = await someProject();
project.ios.hostAppRoot.childDirectory('WatchTarget').childFile('Info.plist').createSync(recursive: true); project.ios.hostAppRoot.childDirectory('WatchTarget').childFile('Info.plist').createSync(recursive: true);
expect(await project.ios.containsWatchCompanion(<String>['WatchTarget'], null), isFalse); expect(await project.ios.containsWatchCompanion(<String>['WatchTarget'], null, '123'), isFalse);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
FileSystem: () => fs, FileSystem: () => fs,
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => FakeProcessManager.any(),
...@@ -674,7 +674,7 @@ apply plugin: 'kotlin-android' ...@@ -674,7 +674,7 @@ apply plugin: 'kotlin-android'
project.ios.hostAppRoot.childDirectory('WatchTarget').childFile('Info.plist').createSync(recursive: true); project.ios.hostAppRoot.childDirectory('WatchTarget').childFile('Info.plist').createSync(recursive: true);
testPlistParser.setProperty('WKCompanionAppBundleIdentifier', 'io.flutter.someOTHERproject'); testPlistParser.setProperty('WKCompanionAppBundleIdentifier', 'io.flutter.someOTHERproject');
expect(await project.ios.containsWatchCompanion(<String>['WatchTarget'], null), isFalse); expect(await project.ios.containsWatchCompanion(<String>['WatchTarget'], null, '123'), isFalse);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
FileSystem: () => fs, FileSystem: () => fs,
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => FakeProcessManager.any(),
...@@ -689,7 +689,7 @@ apply plugin: 'kotlin-android' ...@@ -689,7 +689,7 @@ apply plugin: 'kotlin-android'
project.ios.hostAppRoot.childDirectory('WatchTarget').childFile('Info.plist').createSync(recursive: true); project.ios.hostAppRoot.childDirectory('WatchTarget').childFile('Info.plist').createSync(recursive: true);
testPlistParser.setProperty('WKCompanionAppBundleIdentifier', 'io.flutter.someProject'); testPlistParser.setProperty('WKCompanionAppBundleIdentifier', 'io.flutter.someProject');
expect(await project.ios.containsWatchCompanion(<String>['WatchTarget'], null), isTrue); expect(await project.ios.containsWatchCompanion(<String>['WatchTarget'], null, '123'), isTrue);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
FileSystem: () => fs, FileSystem: () => fs,
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => FakeProcessManager.any(),
...@@ -707,7 +707,7 @@ apply plugin: 'kotlin-android' ...@@ -707,7 +707,7 @@ apply plugin: 'kotlin-android'
project.ios.hostAppRoot.childDirectory('WatchTarget').childFile('Info.plist').createSync(recursive: true); project.ios.hostAppRoot.childDirectory('WatchTarget').childFile('Info.plist').createSync(recursive: true);
testPlistParser.setProperty('WKCompanionAppBundleIdentifier', r'$(PRODUCT_BUNDLE_IDENTIFIER)'); testPlistParser.setProperty('WKCompanionAppBundleIdentifier', r'$(PRODUCT_BUNDLE_IDENTIFIER)');
expect(await project.ios.containsWatchCompanion(<String>['WatchTarget'], null), isTrue); expect(await project.ios.containsWatchCompanion(<String>['WatchTarget'], null, '123'), isTrue);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
FileSystem: () => fs, FileSystem: () => fs,
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => FakeProcessManager.any(),
......
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