mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00

When iOS and macOS Flutter apps build, they use [Xcode Run Scripts](https://developer.apple.com/documentation/xcode/running-custom-scripts-during-a-build) to call do various logic. For iOS, the Run Scripts calls a bash file ([xcode_backend.sh](https://github.com/flutter/flutter/blob/master/packages/flutter_tools/bin/xcode_backend.sh)), which then calls a dart file ([xcode_backend.dart](https://github.com/flutter/flutter/blob/master/packages/flutter_tools/bin/xcode_backend.dart)). `xcode_backend.dart` then calls `flutter assemble`. For macOS, the Run Scripts calls a bash file ([macos_assemble.sh](https://github.com/flutter/flutter/blob/master/packages/flutter_tools/bin/macos_assemble.sh)), which then calls `flutter assemble` directly. This PR changes `macos_assemble.sh` to call `xcode_backend.dart` like it does for iOS so code can be shared between them and written in dart instead of bash. Fixes https://github.com/flutter/flutter/issues/168033. ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
225 lines
7.9 KiB
Dart
225 lines
7.9 KiB
Dart
// Copyright 2014 The Flutter Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
import 'dart:io' as io;
|
|
|
|
import 'package:flutter_tools/src/base/file_system.dart';
|
|
import 'package:flutter_tools/src/base/io.dart';
|
|
import 'package:flutter_tools/src/globals.dart' as globals;
|
|
|
|
import '../src/common.dart';
|
|
import 'test_utils.dart';
|
|
|
|
const String xcodeBackendPath = 'bin/xcode_backend.sh';
|
|
const String xcodeBackendErrorHeader =
|
|
'========================================================================';
|
|
|
|
// Acceptable $CONFIGURATION/$FLUTTER_BUILD_MODE values should be debug, profile, or release
|
|
const Map<String, String> unknownConfiguration = <String, String>{'CONFIGURATION': 'Custom'};
|
|
|
|
// $FLUTTER_BUILD_MODE will override $CONFIGURATION
|
|
const Map<String, String> unknownFlutterBuildMode = <String, String>{
|
|
'FLUTTER_BUILD_MODE': 'Custom',
|
|
'CONFIGURATION': 'Debug',
|
|
};
|
|
|
|
void main() {
|
|
Future<void> expectXcodeBackendFails(Map<String, String> environment) async {
|
|
final ProcessResult result = await Process.run(xcodeBackendPath, <String>[
|
|
'build',
|
|
], environment: environment);
|
|
expect(result.stderr, startsWith(xcodeBackendErrorHeader));
|
|
expect(result.exitCode, isNot(0));
|
|
}
|
|
|
|
test('Xcode backend fails with no arguments', () async {
|
|
final ProcessResult result = await Process.run(
|
|
xcodeBackendPath,
|
|
<String>[],
|
|
environment: <String, String>{
|
|
'SOURCE_ROOT': '../examples/hello_world',
|
|
'FLUTTER_ROOT': '../..',
|
|
},
|
|
);
|
|
expect(
|
|
result.stderr,
|
|
startsWith('error: Your Xcode project is incompatible with this version of Flutter.'),
|
|
);
|
|
expect(result.exitCode, isNot(0));
|
|
}, skip: !io.Platform.isMacOS); // [intended] requires macos toolchain.
|
|
|
|
test('Xcode backend fails for on unsupported configuration combinations', () async {
|
|
await expectXcodeBackendFails(unknownConfiguration);
|
|
await expectXcodeBackendFails(unknownFlutterBuildMode);
|
|
}, skip: !io.Platform.isMacOS); // [intended] requires macos toolchain.
|
|
|
|
test('Xcode backend warns archiving a non-release build.', () async {
|
|
final ProcessResult result = await Process.run(
|
|
xcodeBackendPath,
|
|
<String>['build'],
|
|
environment: <String, String>{'CONFIGURATION': 'Debug', 'ACTION': 'install'},
|
|
);
|
|
expect(result.stderr, contains('warning: Flutter archive not built in Release mode.'));
|
|
expect(result.exitCode, isNot(0));
|
|
}, skip: !io.Platform.isMacOS); // [intended] requires macos toolchain.
|
|
|
|
test('Xcode backend warns when unable to determine platform', () async {
|
|
final ProcessResult result = await Process.run(
|
|
xcodeBackendPath,
|
|
<String>['build', 'asdf'],
|
|
environment: <String, String>{'CONFIGURATION': 'Debug', 'ACTION': 'install'},
|
|
);
|
|
expect(result.stderr, contains('warning: Unrecognized platform: asdf. Defaulting to iOS.'));
|
|
expect(result.exitCode, isNot(0));
|
|
}, skip: !io.Platform.isMacOS); // [intended] requires macos toolchain.
|
|
|
|
group('vmService Bonjour service keys', () {
|
|
late Directory buildDirectory;
|
|
late File infoPlist;
|
|
|
|
setUp(() {
|
|
buildDirectory = globals.fs.systemTempDirectory.createTempSync(
|
|
'flutter_tools_xcode_backend_test.',
|
|
);
|
|
infoPlist = buildDirectory.childFile('Info.plist');
|
|
});
|
|
|
|
test('handles when the Info.plist is missing', () async {
|
|
final ProcessResult result = await Process.run(
|
|
xcodeBackendPath,
|
|
<String>['test_vm_service_bonjour_service'],
|
|
environment: <String, String>{
|
|
'CONFIGURATION': 'Debug',
|
|
'BUILT_PRODUCTS_DIR': buildDirectory.path,
|
|
'INFOPLIST_PATH': 'Info.plist',
|
|
},
|
|
);
|
|
expect(result, const ProcessResultMatcher(stdoutPattern: 'Info.plist does not exist.'));
|
|
});
|
|
|
|
const String emptyPlist = '''
|
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
<plist version="1.0">
|
|
<dict>
|
|
</dict>
|
|
</plist>''';
|
|
|
|
test('does not add keys in Release', () async {
|
|
infoPlist.writeAsStringSync(emptyPlist);
|
|
|
|
final ProcessResult result = await Process.run(
|
|
xcodeBackendPath,
|
|
<String>['test_vm_service_bonjour_service'],
|
|
environment: <String, String>{
|
|
'CONFIGURATION': 'Release',
|
|
'BUILT_PRODUCTS_DIR': buildDirectory.path,
|
|
'INFOPLIST_PATH': 'Info.plist',
|
|
},
|
|
);
|
|
|
|
final String actualInfoPlist = infoPlist.readAsStringSync();
|
|
expect(actualInfoPlist, isNot(contains('NSBonjourServices')));
|
|
expect(actualInfoPlist, isNot(contains('dartVmService')));
|
|
expect(actualInfoPlist, isNot(contains('NSLocalNetworkUsageDescription')));
|
|
|
|
expect(result, const ProcessResultMatcher());
|
|
});
|
|
|
|
for (final String buildConfiguration in <String>['Debug', 'Profile']) {
|
|
test('add keys in $buildConfiguration', () async {
|
|
infoPlist.writeAsStringSync(emptyPlist);
|
|
|
|
final ProcessResult result = await Process.run(
|
|
xcodeBackendPath,
|
|
<String>['test_vm_service_bonjour_service'],
|
|
environment: <String, String>{
|
|
'CONFIGURATION': buildConfiguration,
|
|
'BUILT_PRODUCTS_DIR': buildDirectory.path,
|
|
'INFOPLIST_PATH': 'Info.plist',
|
|
},
|
|
);
|
|
|
|
final String actualInfoPlist = infoPlist.readAsStringSync();
|
|
expect(actualInfoPlist, contains('NSBonjourServices'));
|
|
expect(actualInfoPlist, contains('dartVmService'));
|
|
expect(actualInfoPlist, contains('NSLocalNetworkUsageDescription'));
|
|
|
|
expect(result, const ProcessResultMatcher());
|
|
});
|
|
}
|
|
|
|
test(
|
|
'adds to existing Bonjour services, does not override network usage description',
|
|
() async {
|
|
infoPlist.writeAsStringSync('''
|
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
<plist version="1.0">
|
|
<dict>
|
|
<key>NSBonjourServices</key>
|
|
<array>
|
|
<string>_bogus._tcp</string>
|
|
</array>
|
|
<key>NSLocalNetworkUsageDescription</key>
|
|
<string>Don't override this</string>
|
|
</dict>
|
|
</plist>''');
|
|
|
|
final ProcessResult result = await Process.run(
|
|
xcodeBackendPath,
|
|
<String>['test_vm_service_bonjour_service'],
|
|
environment: <String, String>{
|
|
'CONFIGURATION': 'Debug',
|
|
'BUILT_PRODUCTS_DIR': buildDirectory.path,
|
|
'INFOPLIST_PATH': 'Info.plist',
|
|
},
|
|
);
|
|
|
|
expect(infoPlist.readAsStringSync(), '''
|
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
<plist version="1.0">
|
|
<dict>
|
|
<key>NSBonjourServices</key>
|
|
<array>
|
|
<string>_dartVmService._tcp</string>
|
|
<string>_bogus._tcp</string>
|
|
</array>
|
|
<key>NSLocalNetworkUsageDescription</key>
|
|
<string>Don't override this</string>
|
|
</dict>
|
|
</plist>
|
|
''');
|
|
expect(result, const ProcessResultMatcher());
|
|
},
|
|
);
|
|
|
|
test('does not add bonjour settings when port publication is disabled', () async {
|
|
infoPlist.writeAsStringSync('''
|
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
<plist version="1.0">
|
|
<dict>
|
|
</dict>
|
|
</plist>''');
|
|
|
|
final ProcessResult result = await Process.run(
|
|
xcodeBackendPath,
|
|
<String>['test_vm_service_bonjour_service'],
|
|
environment: <String, String>{
|
|
'CONFIGURATION': 'Debug',
|
|
'BUILT_PRODUCTS_DIR': buildDirectory.path,
|
|
'INFOPLIST_PATH': 'Info.plist',
|
|
'DISABLE_PORT_PUBLICATION': 'YES',
|
|
},
|
|
);
|
|
|
|
expect(infoPlist.readAsStringSync().contains('NSBonjourServices'), isFalse);
|
|
expect(infoPlist.readAsStringSync().contains('NSLocalNetworkUsageDescription'), isFalse);
|
|
expect(result, const ProcessResultMatcher());
|
|
});
|
|
}, skip: !io.Platform.isMacOS); // [intended] requires macos toolchain.
|
|
}
|