Case insensitive check flavor names against Xcode schemes (#61140)
This commit is contained in:
parent
39f93d18b1
commit
e110ca7209
@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Scheme
|
<Scheme
|
||||||
LastUpgradeVersion = "0830"
|
LastUpgradeVersion = "1200"
|
||||||
version = "1.3">
|
version = "1.3">
|
||||||
<BuildAction
|
<BuildAction
|
||||||
parallelizeBuildables = "YES"
|
parallelizeBuildables = "YES"
|
||||||
@ -27,15 +27,6 @@
|
|||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||||
<MacroExpansion>
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
|
|
||||||
BuildableName = "Free App.app"
|
|
||||||
BlueprintName = "Free App"
|
|
||||||
ReferencedContainer = "container:Runner.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</MacroExpansion>
|
|
||||||
<Testables>
|
<Testables>
|
||||||
</Testables>
|
</Testables>
|
||||||
</TestAction>
|
</TestAction>
|
@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Scheme
|
<Scheme
|
||||||
LastUpgradeVersion = "0830"
|
LastUpgradeVersion = "1200"
|
||||||
version = "1.3">
|
version = "1.3">
|
||||||
<BuildAction
|
<BuildAction
|
||||||
parallelizeBuildables = "YES"
|
parallelizeBuildables = "YES"
|
||||||
@ -27,15 +27,6 @@
|
|||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||||
<MacroExpansion>
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
|
|
||||||
BuildableName = "Free App.app"
|
|
||||||
BlueprintName = "Free App"
|
|
||||||
ReferencedContainer = "container:Runner.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</MacroExpansion>
|
|
||||||
<Testables>
|
<Testables>
|
||||||
</Testables>
|
</Testables>
|
||||||
</TestAction>
|
</TestAction>
|
@ -119,18 +119,10 @@ Future<XcodeBuildResult> buildXcodeProject({
|
|||||||
|
|
||||||
await removeFinderExtendedAttributes(app.project.hostAppRoot, processUtils, globals.logger);
|
await removeFinderExtendedAttributes(app.project.hostAppRoot, processUtils, globals.logger);
|
||||||
|
|
||||||
final XcodeProjectInfo projectInfo = await globals.xcodeProjectInterpreter.getInfo(app.project.hostAppRoot.path);
|
final XcodeProjectInfo projectInfo = await app.project.projectInfo();
|
||||||
final String scheme = projectInfo.schemeFor(buildInfo);
|
final String scheme = projectInfo.schemeFor(buildInfo);
|
||||||
if (scheme == null) {
|
if (scheme == null) {
|
||||||
globals.printError('');
|
projectInfo.reportFlavorNotFoundAndExit();
|
||||||
if (projectInfo.definesCustomSchemes) {
|
|
||||||
globals.printError('The Xcode project defines schemes: ${projectInfo.schemes.join(', ')}');
|
|
||||||
globals.printError('You must specify a --flavor option to select one of them.');
|
|
||||||
} else {
|
|
||||||
globals.printError('The Xcode project does not define custom schemes.');
|
|
||||||
globals.printError('You cannot use the --flavor option.');
|
|
||||||
}
|
|
||||||
return XcodeBuildResult(success: false);
|
|
||||||
}
|
}
|
||||||
final String configuration = projectInfo.buildConfigurationFor(buildInfo, scheme);
|
final String configuration = projectInfo.buildConfigurationFor(buildInfo, scheme);
|
||||||
if (configuration == null) {
|
if (configuration == null) {
|
||||||
|
@ -391,7 +391,7 @@ class XcodeProjectInterpreter {
|
|||||||
if (result.exitCode == missingProjectExitCode) {
|
if (result.exitCode == missingProjectExitCode) {
|
||||||
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());
|
return XcodeProjectInfo.fromXcodeBuildOutput(result.toString(), _logger);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -435,9 +435,14 @@ String substituteXcodeVariables(String str, Map<String, String> xcodeBuildSettin
|
|||||||
///
|
///
|
||||||
/// Represents the output of `xcodebuild -list`.
|
/// Represents the output of `xcodebuild -list`.
|
||||||
class XcodeProjectInfo {
|
class XcodeProjectInfo {
|
||||||
XcodeProjectInfo(this.targets, this.buildConfigurations, this.schemes);
|
XcodeProjectInfo(
|
||||||
|
this.targets,
|
||||||
|
this.buildConfigurations,
|
||||||
|
this.schemes,
|
||||||
|
Logger logger
|
||||||
|
) : _logger = logger;
|
||||||
|
|
||||||
factory XcodeProjectInfo.fromXcodeBuildOutput(String output) {
|
factory XcodeProjectInfo.fromXcodeBuildOutput(String output, Logger logger) {
|
||||||
final List<String> targets = <String>[];
|
final List<String> targets = <String>[];
|
||||||
final List<String> buildConfigurations = <String>[];
|
final List<String> buildConfigurations = <String>[];
|
||||||
final List<String> schemes = <String>[];
|
final List<String> schemes = <String>[];
|
||||||
@ -461,16 +466,18 @@ class XcodeProjectInfo {
|
|||||||
if (schemes.isEmpty) {
|
if (schemes.isEmpty) {
|
||||||
schemes.add('Runner');
|
schemes.add('Runner');
|
||||||
}
|
}
|
||||||
return XcodeProjectInfo(targets, buildConfigurations, schemes);
|
return XcodeProjectInfo(targets, buildConfigurations, schemes, logger);
|
||||||
}
|
}
|
||||||
|
|
||||||
final List<String> targets;
|
final List<String> targets;
|
||||||
final List<String> buildConfigurations;
|
final List<String> buildConfigurations;
|
||||||
final List<String> schemes;
|
final List<String> schemes;
|
||||||
|
final Logger _logger;
|
||||||
|
|
||||||
bool get definesCustomSchemes => !(schemes.contains('Runner') && schemes.length == 1);
|
bool get definesCustomSchemes => !(schemes.contains('Runner') && schemes.length == 1);
|
||||||
|
|
||||||
/// The expected scheme for [buildInfo].
|
/// The expected scheme for [buildInfo].
|
||||||
|
@visibleForTesting
|
||||||
static String expectedSchemeFor(BuildInfo buildInfo) {
|
static String expectedSchemeFor(BuildInfo buildInfo) {
|
||||||
return toTitleCase(buildInfo?.flavor ?? 'runner');
|
return toTitleCase(buildInfo?.flavor ?? 'runner');
|
||||||
}
|
}
|
||||||
@ -507,6 +514,16 @@ class XcodeProjectInfo {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void reportFlavorNotFoundAndExit() {
|
||||||
|
_logger.printError('');
|
||||||
|
if (definesCustomSchemes) {
|
||||||
|
_logger.printError('The Xcode project defines schemes: ${schemes.join(', ')}');
|
||||||
|
throwToolExit('You must specify a --flavor option to select one of the available schemes.');
|
||||||
|
} else {
|
||||||
|
throwToolExit('The Xcode project does not define custom schemes. You cannot use the --flavor option.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns unique build configuration matching [buildInfo] and [scheme], or
|
/// Returns unique build configuration matching [buildInfo] and [scheme], or
|
||||||
/// null, if there is no unique best match.
|
/// null, if there is no unique best match.
|
||||||
String buildConfigurationFor(BuildInfo buildInfo, String scheme) {
|
String buildConfigurationFor(BuildInfo buildInfo, String scheme) {
|
||||||
|
@ -61,7 +61,7 @@ Future<void> buildMacOS({
|
|||||||
);
|
);
|
||||||
final String scheme = projectInfo.schemeFor(buildInfo);
|
final String scheme = projectInfo.schemeFor(buildInfo);
|
||||||
if (scheme == null) {
|
if (scheme == null) {
|
||||||
throwToolExit('Unable to find expected scheme in Xcode project.');
|
projectInfo.reportFlavorNotFoundAndExit();
|
||||||
}
|
}
|
||||||
final String configuration = projectInfo.buildConfigurationFor(buildInfo, scheme);
|
final String configuration = projectInfo.buildConfigurationFor(buildInfo, scheme);
|
||||||
if (configuration == null) {
|
if (configuration == null) {
|
||||||
|
@ -20,6 +20,7 @@ import 'flutter_manifest.dart';
|
|||||||
import 'globals.dart' as globals;
|
import 'globals.dart' as globals;
|
||||||
import 'ios/plist_parser.dart';
|
import 'ios/plist_parser.dart';
|
||||||
import 'ios/xcodeproj.dart' as xcode;
|
import 'ios/xcodeproj.dart' as xcode;
|
||||||
|
import 'ios/xcodeproj.dart';
|
||||||
import 'platform_plugins.dart';
|
import 'platform_plugins.dart';
|
||||||
import 'plugins.dart';
|
import 'plugins.dart';
|
||||||
import 'template.dart';
|
import 'template.dart';
|
||||||
@ -105,11 +106,16 @@ class FlutterProject {
|
|||||||
// Don't require iOS build info, this method is only
|
// Don't require iOS build info, this method is only
|
||||||
// used during create as best-effort, use the
|
// used during create as best-effort, use the
|
||||||
// default target bundle identifier.
|
// default target bundle identifier.
|
||||||
await ios.productBundleIdentifier(null),
|
if (ios.existsSync())
|
||||||
android.applicationId,
|
await ios.productBundleIdentifier(null),
|
||||||
android.group,
|
if (android.existsSync()) ...<String>[
|
||||||
example.android.applicationId,
|
android.applicationId,
|
||||||
await example.ios.productBundleIdentifier(null),
|
android.group,
|
||||||
|
],
|
||||||
|
if (example.android.existsSync())
|
||||||
|
example.android.applicationId,
|
||||||
|
if (example.ios.existsSync())
|
||||||
|
await example.ios.productBundleIdentifier(null),
|
||||||
];
|
];
|
||||||
return Set<String>.of(candidates
|
return Set<String>.of(candidates
|
||||||
.map<String>(_organizationNameFromPackageName)
|
.map<String>(_organizationNameFromPackageName)
|
||||||
@ -434,8 +440,12 @@ class IosProject extends FlutterProjectPlatform implements XcodeBasedProject {
|
|||||||
|
|
||||||
/// The product bundle identifier of the host app, or null if not set or if
|
/// The product bundle identifier of the host app, or null if not set or if
|
||||||
/// iOS tooling needed to read it is not installed.
|
/// iOS tooling needed to read it is not installed.
|
||||||
Future<String> productBundleIdentifier(BuildInfo buildInfo) async =>
|
Future<String> productBundleIdentifier(BuildInfo buildInfo) async {
|
||||||
_productBundleIdentifier ??= await _parseProductBundleIdentifier(buildInfo);
|
if (!existsSync()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return _productBundleIdentifier ??= await _parseProductBundleIdentifier(buildInfo);
|
||||||
|
}
|
||||||
String _productBundleIdentifier;
|
String _productBundleIdentifier;
|
||||||
|
|
||||||
Future<String> _parseProductBundleIdentifier(BuildInfo buildInfo) async {
|
Future<String> _parseProductBundleIdentifier(BuildInfo buildInfo) async {
|
||||||
@ -481,8 +491,12 @@ class IosProject extends FlutterProjectPlatform implements XcodeBasedProject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The bundle name of the host app, `My App.app`.
|
/// The bundle name of the host app, `My App.app`.
|
||||||
Future<String> hostAppBundleName(BuildInfo buildInfo) async =>
|
Future<String> hostAppBundleName(BuildInfo buildInfo) async {
|
||||||
_hostAppBundleName ??= await _parseHostAppBundleName(buildInfo);
|
if (!existsSync()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return _hostAppBundleName ??= await _parseHostAppBundleName(buildInfo);
|
||||||
|
}
|
||||||
String _hostAppBundleName;
|
String _hostAppBundleName;
|
||||||
|
|
||||||
Future<String> _parseHostAppBundleName(BuildInfo buildInfo) async {
|
Future<String> _parseHostAppBundleName(BuildInfo buildInfo) async {
|
||||||
@ -507,12 +521,32 @@ class IosProject extends FlutterProjectPlatform implements XcodeBasedProject {
|
|||||||
///
|
///
|
||||||
/// Returns null, if iOS tooling is unavailable.
|
/// Returns null, if iOS tooling is unavailable.
|
||||||
Future<Map<String, String>> buildSettingsForBuildInfo(BuildInfo buildInfo) async {
|
Future<Map<String, String>> buildSettingsForBuildInfo(BuildInfo buildInfo) async {
|
||||||
|
if (!existsSync()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
_buildSettingsByScheme ??= <String, Map<String, String>>{};
|
_buildSettingsByScheme ??= <String, Map<String, String>>{};
|
||||||
final String scheme = xcode.XcodeProjectInfo.expectedSchemeFor(buildInfo);
|
final XcodeProjectInfo info = await projectInfo();
|
||||||
|
if (info == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String scheme = info.schemeFor(buildInfo);
|
||||||
|
if (scheme == null) {
|
||||||
|
info.reportFlavorNotFoundAndExit();
|
||||||
|
}
|
||||||
|
|
||||||
return _buildSettingsByScheme[scheme] ??= await _xcodeProjectBuildSettings(scheme);
|
return _buildSettingsByScheme[scheme] ??= await _xcodeProjectBuildSettings(scheme);
|
||||||
}
|
}
|
||||||
Map<String, Map<String, String>> _buildSettingsByScheme;
|
Map<String, Map<String, String>> _buildSettingsByScheme;
|
||||||
|
|
||||||
|
Future<XcodeProjectInfo> projectInfo() async {
|
||||||
|
if (!existsSync() || !globals.xcodeProjectInterpreter.isInstalled) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return _projectInfo ??= await globals.xcodeProjectInterpreter.getInfo(hostAppRoot.path);
|
||||||
|
}
|
||||||
|
XcodeProjectInfo _projectInfo;
|
||||||
|
|
||||||
Future<Map<String, String>> _xcodeProjectBuildSettings(String scheme) async {
|
Future<Map<String, String>> _xcodeProjectBuildSettings(String scheme) async {
|
||||||
if (!globals.xcodeProjectInterpreter.isInstalled) {
|
if (!globals.xcodeProjectInterpreter.isInstalled) {
|
||||||
return null;
|
return null;
|
||||||
@ -624,32 +658,6 @@ class IosProject extends FlutterProjectPlatform implements XcodeBasedProject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> makeHostAppEditable() async {
|
|
||||||
assert(isModule);
|
|
||||||
if (_editableDirectory.existsSync()) {
|
|
||||||
throwToolExit('iOS host app is already editable. To start fresh, delete the ios/ folder.');
|
|
||||||
}
|
|
||||||
_deleteIfExistsSync(ephemeralDirectory);
|
|
||||||
await _overwriteFromTemplate(
|
|
||||||
globals.fs.path.join('module', 'ios', 'library'),
|
|
||||||
ephemeralDirectory,
|
|
||||||
);
|
|
||||||
await _overwriteFromTemplate(
|
|
||||||
globals.fs.path.join('module', 'ios', 'host_app_ephemeral'),
|
|
||||||
_editableDirectory,
|
|
||||||
);
|
|
||||||
await _overwriteFromTemplate(
|
|
||||||
globals.fs.path.join('module', 'ios', 'host_app_ephemeral_cocoapods'),
|
|
||||||
_editableDirectory,
|
|
||||||
);
|
|
||||||
await _overwriteFromTemplate(
|
|
||||||
globals.fs.path.join('module', 'ios', 'host_app_editable_cocoapods'),
|
|
||||||
_editableDirectory,
|
|
||||||
);
|
|
||||||
await _updateGeneratedXcodeConfigIfNeeded();
|
|
||||||
await injectPlugins(parent);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
File get generatedXcodePropertiesFile => _flutterLibRoot
|
File get generatedXcodePropertiesFile => _flutterLibRoot
|
||||||
.childDirectory('Flutter')
|
.childDirectory('Flutter')
|
||||||
@ -788,20 +796,6 @@ class AndroidProject extends FlutterProjectPlatform {
|
|||||||
) || globals.cache.isOlderThanToolsStamp(ephemeralDirectory);
|
) || globals.cache.isOlderThanToolsStamp(ephemeralDirectory);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> makeHostAppEditable() async {
|
|
||||||
assert(isModule);
|
|
||||||
if (_editableHostAppDirectory.existsSync()) {
|
|
||||||
throwToolExit('Android host app is already editable. To start fresh, delete the android/ folder.');
|
|
||||||
}
|
|
||||||
await _regenerateLibrary();
|
|
||||||
await _overwriteFromTemplate(globals.fs.path.join('module', 'android', 'host_app_common'), _editableHostAppDirectory);
|
|
||||||
await _overwriteFromTemplate(globals.fs.path.join('module', 'android', 'host_app_editable'), _editableHostAppDirectory);
|
|
||||||
await _overwriteFromTemplate(globals.fs.path.join('module', 'android', 'gradle'), _editableHostAppDirectory);
|
|
||||||
gradle.gradleUtils.injectGradleWrapperIfNeeded(_editableHostAppDirectory);
|
|
||||||
gradle.writeLocalProperties(_editableHostAppDirectory.childFile('local.properties'));
|
|
||||||
await injectPlugins(parent);
|
|
||||||
}
|
|
||||||
|
|
||||||
File get localPropertiesFile => _flutterLibGradleRoot.childFile('local.properties');
|
File get localPropertiesFile => _flutterLibGradleRoot.childFile('local.properties');
|
||||||
|
|
||||||
Directory get pluginRegistrantHost => _flutterLibGradleRoot.childDirectory(isModule ? 'Flutter' : 'app');
|
Directory get pluginRegistrantHost => _flutterLibGradleRoot.childDirectory(isModule ? 'Flutter' : 'app');
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
import 'package:args/command_runner.dart';
|
import 'package:args/command_runner.dart';
|
||||||
import 'package:file/memory.dart';
|
import 'package:file/memory.dart';
|
||||||
import 'package:flutter_tools/src/base/file_system.dart';
|
import 'package:flutter_tools/src/base/file_system.dart';
|
||||||
|
import 'package:flutter_tools/src/base/logger.dart';
|
||||||
import 'package:flutter_tools/src/base/platform.dart';
|
import 'package:flutter_tools/src/base/platform.dart';
|
||||||
import 'package:flutter_tools/src/build_info.dart';
|
import 'package:flutter_tools/src/build_info.dart';
|
||||||
import 'package:flutter_tools/src/cache.dart';
|
import 'package:flutter_tools/src/cache.dart';
|
||||||
@ -26,6 +27,7 @@ class FakeXcodeProjectInterpreterWithProfile extends FakeXcodeProjectInterpreter
|
|||||||
<String>['Runner'],
|
<String>['Runner'],
|
||||||
<String>['Debug', 'Profile', 'Release'],
|
<String>['Debug', 'Profile', 'Release'],
|
||||||
<String>['Runner'],
|
<String>['Runner'],
|
||||||
|
BufferLogger.test(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
import 'package:file/memory.dart';
|
import 'package:file/memory.dart';
|
||||||
import 'package:flutter_tools/src/base/context.dart';
|
import 'package:flutter_tools/src/base/context.dart';
|
||||||
import 'package:flutter_tools/src/base/file_system.dart';
|
import 'package:flutter_tools/src/base/file_system.dart';
|
||||||
|
import 'package:flutter_tools/src/base/logger.dart';
|
||||||
import 'package:flutter_tools/src/base/platform.dart';
|
import 'package:flutter_tools/src/base/platform.dart';
|
||||||
import 'package:flutter_tools/src/commands/clean.dart';
|
import 'package:flutter_tools/src/commands/clean.dart';
|
||||||
import 'package:flutter_tools/src/ios/xcodeproj.dart';
|
import 'package:flutter_tools/src/ios/xcodeproj.dart';
|
||||||
@ -140,6 +141,6 @@ class MockXcode extends Mock implements Xcode {}
|
|||||||
class MockXcodeProjectInterpreter extends Mock implements XcodeProjectInterpreter {
|
class MockXcodeProjectInterpreter extends Mock implements XcodeProjectInterpreter {
|
||||||
@override
|
@override
|
||||||
Future<XcodeProjectInfo> getInfo(String projectPath, {String projectFilename}) async {
|
Future<XcodeProjectInfo> getInfo(String projectPath, {String projectFilename}) async {
|
||||||
return XcodeProjectInfo(null, null, <String>['Runner']);
|
return XcodeProjectInfo(null, null, <String>['Runner'], BufferLogger.test());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -87,6 +87,7 @@ void main() {
|
|||||||
<String>['Runner'],
|
<String>['Runner'],
|
||||||
<String>['Debug', 'Release'],
|
<String>['Debug', 'Release'],
|
||||||
<String>['Runner'],
|
<String>['Runner'],
|
||||||
|
logger,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -379,7 +379,7 @@ Information about project "Runner":
|
|||||||
Runner
|
Runner
|
||||||
|
|
||||||
''';
|
''';
|
||||||
final XcodeProjectInfo info = XcodeProjectInfo.fromXcodeBuildOutput(output);
|
final XcodeProjectInfo info = XcodeProjectInfo.fromXcodeBuildOutput(output, logger);
|
||||||
expect(info.targets, <String>['Runner']);
|
expect(info.targets, <String>['Runner']);
|
||||||
expect(info.schemes, <String>['Runner']);
|
expect(info.schemes, <String>['Runner']);
|
||||||
expect(info.buildConfigurations, <String>['Debug', 'Release']);
|
expect(info.buildConfigurations, <String>['Debug', 'Release']);
|
||||||
@ -404,7 +404,7 @@ Information about project "Runner":
|
|||||||
Paid
|
Paid
|
||||||
|
|
||||||
''';
|
''';
|
||||||
final XcodeProjectInfo info = XcodeProjectInfo.fromXcodeBuildOutput(output);
|
final XcodeProjectInfo info = XcodeProjectInfo.fromXcodeBuildOutput(output, logger);
|
||||||
expect(info.targets, <String>['Runner']);
|
expect(info.targets, <String>['Runner']);
|
||||||
expect(info.schemes, <String>['Free', 'Paid']);
|
expect(info.schemes, <String>['Free', 'Paid']);
|
||||||
expect(info.buildConfigurations, <String>['Debug (Free)', 'Debug (Paid)', 'Release (Free)', 'Release (Paid)']);
|
expect(info.buildConfigurations, <String>['Debug (Free)', 'Debug (Paid)', 'Release (Free)', 'Release (Paid)']);
|
||||||
@ -434,7 +434,7 @@ Information about project "Runner":
|
|||||||
});
|
});
|
||||||
|
|
||||||
testWithoutContext('scheme for default project is Runner', () {
|
testWithoutContext('scheme for default project is Runner', () {
|
||||||
final XcodeProjectInfo info = XcodeProjectInfo(<String>['Runner'], <String>['Debug', 'Release'], <String>['Runner']);
|
final XcodeProjectInfo info = XcodeProjectInfo(<String>['Runner'], <String>['Debug', 'Release'], <String>['Runner'], logger);
|
||||||
|
|
||||||
expect(info.schemeFor(BuildInfo.debug), 'Runner');
|
expect(info.schemeFor(BuildInfo.debug), 'Runner');
|
||||||
expect(info.schemeFor(BuildInfo.profile), 'Runner');
|
expect(info.schemeFor(BuildInfo.profile), 'Runner');
|
||||||
@ -443,7 +443,7 @@ Information about project "Runner":
|
|||||||
});
|
});
|
||||||
|
|
||||||
testWithoutContext('build configuration for default project is matched against BuildMode', () {
|
testWithoutContext('build configuration for default project is matched against BuildMode', () {
|
||||||
final XcodeProjectInfo info = XcodeProjectInfo(<String>['Runner'], <String>['Debug', 'Profile', 'Release'], <String>['Runner']);
|
final XcodeProjectInfo info = XcodeProjectInfo(<String>['Runner'], <String>['Debug', 'Profile', 'Release'], <String>['Runner'], logger);
|
||||||
|
|
||||||
expect(info.buildConfigurationFor(BuildInfo.debug, 'Runner'), 'Debug');
|
expect(info.buildConfigurationFor(BuildInfo.debug, 'Runner'), 'Debug');
|
||||||
expect(info.buildConfigurationFor(BuildInfo.profile, 'Runner'), 'Profile');
|
expect(info.buildConfigurationFor(BuildInfo.profile, 'Runner'), 'Profile');
|
||||||
@ -455,6 +455,7 @@ Information about project "Runner":
|
|||||||
<String>['Runner'],
|
<String>['Runner'],
|
||||||
<String>['Debug (Free)', 'Debug (Paid)', 'Release (Free)', 'Release (Paid)'],
|
<String>['Debug (Free)', 'Debug (Paid)', 'Release (Free)', 'Release (Paid)'],
|
||||||
<String>['Free', 'Paid'],
|
<String>['Free', 'Paid'],
|
||||||
|
logger,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(info.schemeFor(const BuildInfo(BuildMode.debug, 'free', treeShakeIcons: false)), 'Free');
|
expect(info.schemeFor(const BuildInfo(BuildMode.debug, 'free', treeShakeIcons: false)), 'Free');
|
||||||
@ -464,11 +465,44 @@ Information about project "Runner":
|
|||||||
expect(info.schemeFor(const BuildInfo(BuildMode.debug, 'unknown', treeShakeIcons: false)), isNull);
|
expect(info.schemeFor(const BuildInfo(BuildMode.debug, 'unknown', treeShakeIcons: false)), isNull);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWithoutContext('reports default scheme error and exit', () {
|
||||||
|
final XcodeProjectInfo defaultInfo = XcodeProjectInfo(
|
||||||
|
<String>[],
|
||||||
|
<String>[],
|
||||||
|
<String>['Runner'],
|
||||||
|
logger,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
() => defaultInfo.reportFlavorNotFoundAndExit(),
|
||||||
|
throwsToolExit(
|
||||||
|
message: 'The Xcode project does not define custom schemes. You cannot use the --flavor option.'
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWithoutContext('reports custom scheme error and exit', () {
|
||||||
|
final XcodeProjectInfo info = XcodeProjectInfo(
|
||||||
|
<String>[],
|
||||||
|
<String>[],
|
||||||
|
<String>['Free', 'Paid'],
|
||||||
|
logger,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
() => info.reportFlavorNotFoundAndExit(),
|
||||||
|
throwsToolExit(
|
||||||
|
message: 'You must specify a --flavor option to select one of the available schemes.'
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
testWithoutContext('build configuration for project with custom schemes is matched against BuildMode and flavor', () {
|
testWithoutContext('build configuration for project with custom schemes is matched against BuildMode and flavor', () {
|
||||||
final XcodeProjectInfo info = XcodeProjectInfo(
|
final XcodeProjectInfo info = XcodeProjectInfo(
|
||||||
<String>['Runner'],
|
<String>['Runner'],
|
||||||
<String>['debug (free)', 'Debug paid', 'profile - Free', 'Profile-Paid', 'release - Free', 'Release-Paid'],
|
<String>['debug (free)', 'Debug paid', 'profile - Free', 'Profile-Paid', 'release - Free', 'Release-Paid'],
|
||||||
<String>['Free', 'Paid'],
|
<String>['Free', 'Paid'],
|
||||||
|
logger,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(info.buildConfigurationFor(const BuildInfo(BuildMode.debug, 'free', treeShakeIcons: false), 'Free'), 'debug (free)');
|
expect(info.buildConfigurationFor(const BuildInfo(BuildMode.debug, 'free', treeShakeIcons: false), 'Free'), 'debug (free)');
|
||||||
@ -482,6 +516,7 @@ Information about project "Runner":
|
|||||||
<String>['Runner'],
|
<String>['Runner'],
|
||||||
<String>['Debug-F', 'Dbg Paid', 'Rel Free', 'Release Full'],
|
<String>['Debug-F', 'Dbg Paid', 'Rel Free', 'Release Full'],
|
||||||
<String>['Free', 'Paid'],
|
<String>['Free', 'Paid'],
|
||||||
|
logger,
|
||||||
);
|
);
|
||||||
expect(info.buildConfigurationFor(const BuildInfo(BuildMode.debug, 'Free', treeShakeIcons: false), 'Free'), null);
|
expect(info.buildConfigurationFor(const BuildInfo(BuildMode.debug, 'Free', treeShakeIcons: false), 'Free'), null);
|
||||||
expect(info.buildConfigurationFor(const BuildInfo(BuildMode.profile, 'Free', treeShakeIcons: false), 'Free'), null);
|
expect(info.buildConfigurationFor(const BuildInfo(BuildMode.profile, 'Free', treeShakeIcons: false), 'Free'), null);
|
||||||
|
@ -9,6 +9,7 @@ import 'package:file/memory.dart';
|
|||||||
import 'package:flutter_tools/src/base/context.dart';
|
import 'package:flutter_tools/src/base/context.dart';
|
||||||
import 'package:flutter_tools/src/base/file_system.dart';
|
import 'package:flutter_tools/src/base/file_system.dart';
|
||||||
import 'package:flutter_tools/src/base/logger.dart';
|
import 'package:flutter_tools/src/base/logger.dart';
|
||||||
|
import 'package:flutter_tools/src/build_info.dart';
|
||||||
import 'package:flutter_tools/src/cache.dart';
|
import 'package:flutter_tools/src/cache.dart';
|
||||||
import 'package:flutter_tools/src/features.dart';
|
import 'package:flutter_tools/src/features.dart';
|
||||||
import 'package:flutter_tools/src/flutter_manifest.dart';
|
import 'package:flutter_tools/src/flutter_manifest.dart';
|
||||||
@ -109,52 +110,6 @@ void main() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
group('editable Android host app', () {
|
|
||||||
_testInMemory('fails on non-module', () async {
|
|
||||||
final FlutterProject project = await someProject();
|
|
||||||
await expectLater(
|
|
||||||
project.android.makeHostAppEditable(),
|
|
||||||
throwsAssertionError,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
_testInMemory('exits on already editable module', () async {
|
|
||||||
final FlutterProject project = await aModuleProject();
|
|
||||||
await project.android.makeHostAppEditable();
|
|
||||||
return expectToolExitLater(project.android.makeHostAppEditable(), contains('already editable'));
|
|
||||||
});
|
|
||||||
_testInMemory('creates android/app folder in place of .android/app', () async {
|
|
||||||
final FlutterProject project = await aModuleProject();
|
|
||||||
await project.android.makeHostAppEditable();
|
|
||||||
expectNotExists(project.directory.childDirectory('.android').childDirectory('app'));
|
|
||||||
expect(
|
|
||||||
project.directory.childDirectory('.android').childFile('settings.gradle').readAsStringSync(),
|
|
||||||
isNot(contains("include ':app'")),
|
|
||||||
);
|
|
||||||
expectExists(project.directory.childDirectory('android').childDirectory('app'));
|
|
||||||
expectExists(project.directory.childDirectory('android').childFile('local.properties'));
|
|
||||||
expect(
|
|
||||||
project.directory.childDirectory('android').childFile('settings.gradle').readAsStringSync(),
|
|
||||||
contains("include ':app'"),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
_testInMemory('retains .android/Flutter folder and references it', () async {
|
|
||||||
final FlutterProject project = await aModuleProject();
|
|
||||||
await project.android.makeHostAppEditable();
|
|
||||||
expectExists(project.directory.childDirectory('.android').childDirectory('Flutter'));
|
|
||||||
expect(
|
|
||||||
project.directory.childDirectory('android').childFile('settings.gradle').readAsStringSync(),
|
|
||||||
contains("new File(settingsDir.parentFile, '.android/include_flutter.groovy')"),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
_testInMemory('can be redone after deletion', () async {
|
|
||||||
final FlutterProject project = await aModuleProject();
|
|
||||||
await project.android.makeHostAppEditable();
|
|
||||||
project.directory.childDirectory('android').deleteSync(recursive: true);
|
|
||||||
await project.android.makeHostAppEditable();
|
|
||||||
expectExists(project.directory.childDirectory('android').childDirectory('app'));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
group('ensure ready for platform-specific tooling', () {
|
group('ensure ready for platform-specific tooling', () {
|
||||||
_testInMemory('does nothing, if project is not created', () async {
|
_testInMemory('does nothing, if project is not created', () async {
|
||||||
final FlutterProject project = FlutterProject(
|
final FlutterProject project = FlutterProject(
|
||||||
@ -384,6 +339,9 @@ apply plugin: 'kotlin-android'
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
when(mockXcodeProjectInterpreter.getInfo(any, projectFilename: anyNamed('projectFilename'))).thenAnswer( (_) {
|
||||||
|
return Future<XcodeProjectInfo>.value(XcodeProjectInfo(<String>[], <String>[], <String>['Runner'], logger));
|
||||||
|
});
|
||||||
expect(await project.ios.productBundleIdentifier(null), 'io.flutter.someProject');
|
expect(await project.ios.productBundleIdentifier(null), 'io.flutter.someProject');
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -411,6 +369,10 @@ apply plugin: 'kotlin-android'
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
when(mockXcodeProjectInterpreter.getInfo(any, projectFilename: anyNamed('projectFilename'))).thenAnswer( (_) {
|
||||||
|
return Future<XcodeProjectInfo>.value(XcodeProjectInfo(<String>[], <String>[], <String>['Runner'], logger));
|
||||||
|
});
|
||||||
|
|
||||||
when(mockPlistUtils.getValueFromFile(any, any)).thenReturn(r'$(PRODUCT_BUNDLE_IDENTIFIER)');
|
when(mockPlistUtils.getValueFromFile(any, any)).thenReturn(r'$(PRODUCT_BUNDLE_IDENTIFIER)');
|
||||||
expect(await project.ios.productBundleIdentifier(null), 'io.flutter.someProject');
|
expect(await project.ios.productBundleIdentifier(null), 'io.flutter.someProject');
|
||||||
});
|
});
|
||||||
@ -426,9 +388,55 @@ apply plugin: 'kotlin-android'
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
when(mockXcodeProjectInterpreter.getInfo(any, projectFilename: anyNamed('projectFilename'))).thenAnswer( (_) {
|
||||||
|
return Future<XcodeProjectInfo>.value(XcodeProjectInfo(<String>[], <String>[], <String>['Runner'], logger));
|
||||||
|
});
|
||||||
|
|
||||||
when(mockPlistUtils.getValueFromFile(any, any)).thenReturn(r'$(PRODUCT_BUNDLE_IDENTIFIER).$(SUFFIX)');
|
when(mockPlistUtils.getValueFromFile(any, any)).thenReturn(r'$(PRODUCT_BUNDLE_IDENTIFIER).$(SUFFIX)');
|
||||||
expect(await project.ios.productBundleIdentifier(null), 'io.flutter.someProject.suffix');
|
expect(await project.ios.productBundleIdentifier(null), 'io.flutter.someProject.suffix');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWithMocks('fails with no flavor and defined schemes', () async {
|
||||||
|
final FlutterProject project = await someProject();
|
||||||
|
when(mockXcodeProjectInterpreter.getInfo(any, projectFilename: anyNamed('projectFilename'))).thenAnswer( (_) {
|
||||||
|
return Future<XcodeProjectInfo>.value(XcodeProjectInfo(<String>[], <String>[], <String>['free', 'paid'], logger));
|
||||||
|
});
|
||||||
|
await expectToolExitLater(
|
||||||
|
project.ios.productBundleIdentifier(null),
|
||||||
|
contains('You must specify a --flavor option to select one of the available schemes.')
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWithMocks('handles case insensitive flavor', () async {
|
||||||
|
final FlutterProject project = await someProject();
|
||||||
|
when(mockXcodeProjectInterpreter.getBuildSettings(any, scheme: anyNamed('scheme'))).thenAnswer(
|
||||||
|
(_) {
|
||||||
|
return Future<Map<String,String>>.value(<String, String>{
|
||||||
|
'PRODUCT_BUNDLE_IDENTIFIER': 'io.flutter.someProject',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
when(mockXcodeProjectInterpreter.getInfo(any, projectFilename: anyNamed('projectFilename'))).thenAnswer( (_) {
|
||||||
|
return Future<XcodeProjectInfo>.value(XcodeProjectInfo(<String>[], <String>[], <String>['Free'], logger));
|
||||||
|
});
|
||||||
|
|
||||||
|
const BuildInfo buildInfo = BuildInfo(BuildMode.debug, 'free', treeShakeIcons: false);
|
||||||
|
expect(await project.ios.productBundleIdentifier(buildInfo), 'io.flutter.someProject');
|
||||||
|
});
|
||||||
|
|
||||||
|
testWithMocks('fails with flavor and default schemes', () async {
|
||||||
|
final FlutterProject project = await someProject();
|
||||||
|
when(mockXcodeProjectInterpreter.getInfo(any, projectFilename: anyNamed('projectFilename'))).thenAnswer( (_) {
|
||||||
|
return Future<XcodeProjectInfo>.value(XcodeProjectInfo(<String>[], <String>[], <String>['Runner'], logger));
|
||||||
|
});
|
||||||
|
|
||||||
|
const BuildInfo buildInfo = BuildInfo(BuildMode.debug, 'free', treeShakeIcons: false);
|
||||||
|
await expectToolExitLater(
|
||||||
|
project.ios.productBundleIdentifier(buildInfo),
|
||||||
|
contains('The Xcode project does not define custom schemes. You cannot use the --flavor option.')
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
testWithMocks('empty surrounded by quotes', () async {
|
testWithMocks('empty surrounded by quotes', () async {
|
||||||
final FlutterProject project = await someProject();
|
final FlutterProject project = await someProject();
|
||||||
addIosProjectFile(project.directory, projectFileContent: () {
|
addIosProjectFile(project.directory, projectFileContent: () {
|
||||||
@ -476,6 +484,9 @@ apply plugin: 'kotlin-android'
|
|||||||
'FULL_PRODUCT_NAME': 'My App.app'
|
'FULL_PRODUCT_NAME': 'My App.app'
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
when(mockXcodeProjectInterpreter.getInfo(any, projectFilename: anyNamed('projectFilename'))).thenAnswer( (_) {
|
||||||
|
return Future<XcodeProjectInfo>.value(XcodeProjectInfo(<String>[], <String>[], <String>['Runner'], logger));
|
||||||
|
});
|
||||||
|
|
||||||
expect(await project.ios.hostAppBundleName(null), 'My App.app');
|
expect(await project.ios.hostAppBundleName(null), 'My App.app');
|
||||||
}, overrides: <Type, Generator>{
|
}, overrides: <Type, Generator>{
|
||||||
@ -634,6 +645,9 @@ name: foo_bar
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
when(mockXcodeProjectInterpreter.getInfo(any, projectFilename: anyNamed('projectFilename'))).thenAnswer( (_) {
|
||||||
|
return Future<XcodeProjectInfo>.value(XcodeProjectInfo(<String>[], <String>[], <String>['Runner'], logger));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
testUsingContext('no Info.plist in target', () async {
|
testUsingContext('no Info.plist in target', () async {
|
||||||
|
@ -414,6 +414,7 @@ class FakeXcodeProjectInterpreter implements XcodeProjectInterpreter {
|
|||||||
<String>['Runner'],
|
<String>['Runner'],
|
||||||
<String>['Debug', 'Release'],
|
<String>['Debug', 'Release'],
|
||||||
<String>['Runner'],
|
<String>['Runner'],
|
||||||
|
BufferLogger.test(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user