Get flavor/scheme in assemble command from the build configuration (#162907)
This moves the logic for `FLUTTER_APP_FLAVOR` into `flutter assemble`,
so that it also works when ran through Xcode and not just through the
Flutter CLI.
However, there's no definitive way to get the the flavor/scheme in
`flutter assemble`, so this makes a best effort to get it by parsing it
out of the `CONFIGURATION`. `CONFIGURATION` should have the name of the
scheme in it, although, this is only
[semi-enforced](1d85de0fc8/packages/flutter_tools/lib/src/ios/mac.dart (L201-L203)
),
so may not always work. If it's unable to get the scheme name from the
`CONFIGURATION`, it falls back to using the `FLAVOR` environment
variable, which is set by the Flutter CLI and used currently.
Verified `Mac_ios flavors_test_ios` passes:
https://ci.chromium.org/ui/p/flutter/builders/prod.shadow/Mac_ios%20flavors_test_ios/7/overview
Verified `Mac flavors_test_macos` passes:
https://ci.chromium.org/ui/p/flutter/builders/try.shadow/Mac%20flavors_test_macos/2/overview
Fixes https://github.com/flutter/flutter/issues/155951.
## 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
This commit is contained in:
parent
54b972084a
commit
8d100a6416
@ -8,6 +8,7 @@ import 'dart:typed_data';
|
|||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:flutter_devicelab/framework/devices.dart';
|
import 'package:flutter_devicelab/framework/devices.dart';
|
||||||
import 'package:flutter_devicelab/framework/framework.dart';
|
import 'package:flutter_devicelab/framework/framework.dart';
|
||||||
|
import 'package:flutter_devicelab/framework/ios.dart';
|
||||||
import 'package:flutter_devicelab/framework/task_result.dart';
|
import 'package:flutter_devicelab/framework/task_result.dart';
|
||||||
import 'package:flutter_devicelab/framework/utils.dart';
|
import 'package:flutter_devicelab/framework/utils.dart';
|
||||||
import 'package:flutter_devicelab/tasks/integration_tests.dart';
|
import 'package:flutter_devicelab/tasks/integration_tests.dart';
|
||||||
@ -34,6 +35,8 @@ Future<void> main() async {
|
|||||||
return firstInstallFailure ?? TaskResult.success(null);
|
return firstInstallFailure ?? TaskResult.success(null);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await _testFlavorWhenBuiltFromXcode(projectDir);
|
||||||
|
|
||||||
return installTestsResult;
|
return installTestsResult;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -97,3 +100,60 @@ Future<TaskResult> _testInstallBogusFlavor() async {
|
|||||||
|
|
||||||
return TaskResult.success(null);
|
return TaskResult.success(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<TaskResult> _testFlavorWhenBuiltFromXcode(String projectDir) async {
|
||||||
|
final Device device = await devices.workingDevice;
|
||||||
|
await inDirectory(projectDir, () async {
|
||||||
|
// This will put FLAVOR=free in the Flutter/Generated.xcconfig file
|
||||||
|
await flutter(
|
||||||
|
'build',
|
||||||
|
options: <String>['ios', '--config-only', '--debug', '--flavor', 'free'],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
final File generatedXcconfig = File(path.join(projectDir, 'ios/Flutter/Generated.xcconfig'));
|
||||||
|
if (!generatedXcconfig.existsSync()) {
|
||||||
|
throw TaskResult.failure('Unable to find Generated.xcconfig');
|
||||||
|
}
|
||||||
|
if (!generatedXcconfig.readAsStringSync().contains('FLAVOR=free')) {
|
||||||
|
throw TaskResult.failure('Generated.xcconfig does not contain FLAVOR=free');
|
||||||
|
}
|
||||||
|
|
||||||
|
const String configuration = 'Debug Paid';
|
||||||
|
const String productName = 'Paid App';
|
||||||
|
const String buildDir = 'build/ios';
|
||||||
|
|
||||||
|
// Delete app bundle before build to ensure checks below do not use previously
|
||||||
|
// built bundle.
|
||||||
|
final String appPath = '$projectDir/$buildDir/$configuration-iphoneos/$productName.app';
|
||||||
|
final Directory appBundle = Directory(appPath);
|
||||||
|
if (appBundle.existsSync()) {
|
||||||
|
appBundle.deleteSync(recursive: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!await runXcodeBuild(
|
||||||
|
platformDirectory: path.join(projectDir, 'ios'),
|
||||||
|
destination: 'id=${device.deviceId}',
|
||||||
|
testName: 'flavors_test_ios',
|
||||||
|
configuration: configuration,
|
||||||
|
scheme: 'paid',
|
||||||
|
actions: <String>['clean', 'build'],
|
||||||
|
extraOptions: <String>['BUILD_DIR=${path.join(projectDir, buildDir)}'],
|
||||||
|
)) {
|
||||||
|
throw TaskResult.failure('Build failed');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!appBundle.existsSync()) {
|
||||||
|
throw TaskResult.failure('App not found at $appPath');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!generatedXcconfig.readAsStringSync().contains('FLAVOR=free')) {
|
||||||
|
throw TaskResult.failure('Generated.xcconfig does not contain FLAVOR=free');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Despite FLAVOR=free being in the Generated.xcconfig, the flavor found in
|
||||||
|
// the test should be "paid" because it was built with the "Debug Paid" configuration.
|
||||||
|
return createFlavorsTest(
|
||||||
|
extraOptions: <String>['--flavor', 'paid', '--use-application-binary=$appPath'],
|
||||||
|
).call();
|
||||||
|
}
|
||||||
|
@ -7,6 +7,7 @@ import 'dart:typed_data';
|
|||||||
|
|
||||||
import 'package:flutter_devicelab/framework/devices.dart';
|
import 'package:flutter_devicelab/framework/devices.dart';
|
||||||
import 'package:flutter_devicelab/framework/framework.dart';
|
import 'package:flutter_devicelab/framework/framework.dart';
|
||||||
|
import 'package:flutter_devicelab/framework/ios.dart';
|
||||||
import 'package:flutter_devicelab/framework/task_result.dart';
|
import 'package:flutter_devicelab/framework/task_result.dart';
|
||||||
import 'package:flutter_devicelab/framework/utils.dart';
|
import 'package:flutter_devicelab/framework/utils.dart';
|
||||||
import 'package:flutter_devicelab/tasks/integration_tests.dart';
|
import 'package:flutter_devicelab/tasks/integration_tests.dart';
|
||||||
@ -83,6 +84,67 @@ Future<void> main() async {
|
|||||||
return TaskResult.success(null);
|
return TaskResult.success(null);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await _testFlavorWhenBuiltFromXcode(projectDir);
|
||||||
|
|
||||||
return installTestsResult;
|
return installTestsResult;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<TaskResult> _testFlavorWhenBuiltFromXcode(String projectDir) async {
|
||||||
|
await inDirectory(projectDir, () async {
|
||||||
|
// This will put FLAVOR=free in the Flutter/ephemeral/Flutter-Generated.xcconfig file
|
||||||
|
await flutter(
|
||||||
|
'build',
|
||||||
|
options: <String>['macos', '--config-only', '--debug', '--flavor', 'free'],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
final File generatedXcconfig = File(
|
||||||
|
path.join(projectDir, 'macos/Flutter/ephemeral/Flutter-Generated.xcconfig'),
|
||||||
|
);
|
||||||
|
if (!generatedXcconfig.existsSync()) {
|
||||||
|
throw TaskResult.failure('Unable to find Generated.xcconfig');
|
||||||
|
}
|
||||||
|
if (!generatedXcconfig.readAsStringSync().contains('FLAVOR=free')) {
|
||||||
|
throw TaskResult.failure('Generated.xcconfig does not contain FLAVOR=free');
|
||||||
|
}
|
||||||
|
|
||||||
|
const String configuration = 'Debug-paid';
|
||||||
|
const String productName = 'Debug Paid';
|
||||||
|
const String buildDir = 'build/macos';
|
||||||
|
final String appPath = '$projectDir/$buildDir/$configuration/$productName.app';
|
||||||
|
|
||||||
|
// Delete app bundle before build to ensure checks below do not use previously
|
||||||
|
// built bundle.
|
||||||
|
final Directory appBundle = Directory(appPath);
|
||||||
|
if (appBundle.existsSync()) {
|
||||||
|
appBundle.deleteSync(recursive: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!await runXcodeBuild(
|
||||||
|
platformDirectory: path.join(projectDir, 'macos'),
|
||||||
|
destination: 'platform=macOS',
|
||||||
|
testName: 'flavors_test_macos',
|
||||||
|
configuration: configuration,
|
||||||
|
scheme: 'paid',
|
||||||
|
actions: <String>['clean', 'build'],
|
||||||
|
extraOptions: <String>['BUILD_DIR=${path.join(projectDir, buildDir)}'],
|
||||||
|
skipCodesign: true,
|
||||||
|
)) {
|
||||||
|
throw TaskResult.failure('Build failed');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!appBundle.existsSync()) {
|
||||||
|
throw TaskResult.failure('App not found at $appPath');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!generatedXcconfig.readAsStringSync().contains('FLAVOR=free')) {
|
||||||
|
throw TaskResult.failure('Generated.xcconfig does not contain FLAVOR=free');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Despite FLAVOR=free being in the Generated.xcconfig, the flavor found in
|
||||||
|
// the test should be "paid" because it was built with the "Debug-paid" configuration.
|
||||||
|
return createFlavorsTest(
|
||||||
|
extraOptions: <String>['--flavor', 'paid', '--use-application-binary=$appPath'],
|
||||||
|
).call();
|
||||||
|
}
|
||||||
|
@ -142,7 +142,32 @@ Future<bool> runXcodeTests({
|
|||||||
required String platformDirectory,
|
required String platformDirectory,
|
||||||
required String destination,
|
required String destination,
|
||||||
required String testName,
|
required String testName,
|
||||||
|
List<String> actions = const <String>['test'],
|
||||||
String configuration = 'Release',
|
String configuration = 'Release',
|
||||||
|
List<String> extraOptions = const <String>[],
|
||||||
|
String scheme = 'Runner',
|
||||||
|
bool skipCodesign = false,
|
||||||
|
}) {
|
||||||
|
return runXcodeBuild(
|
||||||
|
platformDirectory: platformDirectory,
|
||||||
|
destination: destination,
|
||||||
|
testName: testName,
|
||||||
|
actions: actions,
|
||||||
|
configuration: configuration,
|
||||||
|
extraOptions: extraOptions,
|
||||||
|
scheme: scheme,
|
||||||
|
skipCodesign: skipCodesign,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> runXcodeBuild({
|
||||||
|
required String platformDirectory,
|
||||||
|
required String destination,
|
||||||
|
required String testName,
|
||||||
|
List<String> actions = const <String>['build'],
|
||||||
|
String configuration = 'Release',
|
||||||
|
List<String> extraOptions = const <String>[],
|
||||||
|
String scheme = 'Runner',
|
||||||
bool skipCodesign = false,
|
bool skipCodesign = false,
|
||||||
}) async {
|
}) async {
|
||||||
final Map<String, String> environment = Platform.environment;
|
final Map<String, String> environment = Platform.environment;
|
||||||
@ -170,14 +195,15 @@ Future<bool> runXcodeTests({
|
|||||||
'-workspace',
|
'-workspace',
|
||||||
'Runner.xcworkspace',
|
'Runner.xcworkspace',
|
||||||
'-scheme',
|
'-scheme',
|
||||||
'Runner',
|
scheme,
|
||||||
'-configuration',
|
'-configuration',
|
||||||
configuration,
|
configuration,
|
||||||
'-destination',
|
'-destination',
|
||||||
destination,
|
destination,
|
||||||
'-resultBundlePath',
|
'-resultBundlePath',
|
||||||
resultBundlePath,
|
resultBundlePath,
|
||||||
'test',
|
...actions,
|
||||||
|
...extraOptions,
|
||||||
'COMPILER_INDEX_STORE_ENABLE=NO',
|
'COMPILER_INDEX_STORE_ENABLE=NO',
|
||||||
if (developmentTeam != null) 'DEVELOPMENT_TEAM=$developmentTeam',
|
if (developmentTeam != null) 'DEVELOPMENT_TEAM=$developmentTeam',
|
||||||
if (codeSignStyle != null) 'CODE_SIGN_STYLE=$codeSignStyle',
|
if (codeSignStyle != null) 'CODE_SIGN_STYLE=$codeSignStyle',
|
||||||
|
@ -22,11 +22,11 @@ TaskFunction createPlatformInteractionTest() {
|
|||||||
).call;
|
).call;
|
||||||
}
|
}
|
||||||
|
|
||||||
TaskFunction createFlavorsTest({Map<String, String>? environment}) {
|
TaskFunction createFlavorsTest({Map<String, String>? environment, List<String>? extraOptions}) {
|
||||||
return DriverTest(
|
return DriverTest(
|
||||||
'${flutterDirectory.path}/dev/integration_tests/flavors',
|
'${flutterDirectory.path}/dev/integration_tests/flavors',
|
||||||
'lib/main.dart',
|
'lib/main.dart',
|
||||||
extraOptions: <String>['--flavor', 'paid'],
|
extraOptions: extraOptions ?? <String>['--flavor', 'paid'],
|
||||||
environment: environment,
|
environment: environment,
|
||||||
).call;
|
).call;
|
||||||
}
|
}
|
||||||
|
@ -141,6 +141,7 @@ BuildApp() {
|
|||||||
"-dTrackWidgetCreation=${TRACK_WIDGET_CREATION}"
|
"-dTrackWidgetCreation=${TRACK_WIDGET_CREATION}"
|
||||||
"-dAction=${ACTION}"
|
"-dAction=${ACTION}"
|
||||||
"-dFrontendServerStarterPath=${FRONTEND_SERVER_STARTER_PATH}"
|
"-dFrontendServerStarterPath=${FRONTEND_SERVER_STARTER_PATH}"
|
||||||
|
"-dConfiguration=${CONFIGURATION}"
|
||||||
"--DartDefines=${DART_DEFINES}"
|
"--DartDefines=${DART_DEFINES}"
|
||||||
"--ExtraGenSnapshotOptions=${EXTRA_GEN_SNAPSHOT_OPTIONS}"
|
"--ExtraGenSnapshotOptions=${EXTRA_GEN_SNAPSHOT_OPTIONS}"
|
||||||
"--ExtraFrontEndOptions=${EXTRA_FRONT_END_OPTIONS}"
|
"--ExtraFrontEndOptions=${EXTRA_FRONT_END_OPTIONS}"
|
||||||
@ -162,6 +163,8 @@ BuildApp() {
|
|||||||
flutter_args+=("-dPreBuildAction=PrepareFramework")
|
flutter_args+=("-dPreBuildAction=PrepareFramework")
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# FLAVOR is set by the Flutter CLI in the Flutter/ephemeral/Flutter-Generated.xcconfig
|
||||||
|
# file when the --flavor flag is used, so it may not always be present.
|
||||||
if [[ -n "$FLAVOR" ]]; then
|
if [[ -n "$FLAVOR" ]]; then
|
||||||
flutter_args+=("-dFlavor=${FLAVOR}")
|
flutter_args+=("-dFlavor=${FLAVOR}")
|
||||||
fi
|
fi
|
||||||
|
@ -439,7 +439,10 @@ class Context {
|
|||||||
'-dTargetPlatform=ios',
|
'-dTargetPlatform=ios',
|
||||||
'-dTargetFile=$targetPath',
|
'-dTargetFile=$targetPath',
|
||||||
'-dBuildMode=$buildMode',
|
'-dBuildMode=$buildMode',
|
||||||
|
// FLAVOR is set by the Flutter CLI in the Flutter/Generated.xcconfig file
|
||||||
|
// when the --flavor flag is used, so it may not always be present.
|
||||||
if (environment['FLAVOR'] != null) '-dFlavor=${environment['FLAVOR']}',
|
if (environment['FLAVOR'] != null) '-dFlavor=${environment['FLAVOR']}',
|
||||||
|
'-dConfiguration=${environment['CONFIGURATION']}',
|
||||||
'-dIosArchs=$archs',
|
'-dIosArchs=$archs',
|
||||||
'-dSdkRoot=${environment['SDKROOT'] ?? ''}',
|
'-dSdkRoot=${environment['SDKROOT'] ?? ''}',
|
||||||
'-dSplitDebugInfo=${environment['SPLIT_DEBUG_INFO'] ?? ''}',
|
'-dSplitDebugInfo=${environment['SPLIT_DEBUG_INFO'] ?? ''}',
|
||||||
|
@ -966,6 +966,13 @@ const String kBuildName = 'BuildName';
|
|||||||
/// The app flavor to build.
|
/// The app flavor to build.
|
||||||
const String kFlavor = 'Flavor';
|
const String kFlavor = 'Flavor';
|
||||||
|
|
||||||
|
/// Environment variable of the flavor to be set in dartDefines to be accessed
|
||||||
|
/// by the [appFlavor] service.
|
||||||
|
const String kAppFlavor = 'FLUTTER_APP_FLAVOR';
|
||||||
|
|
||||||
|
/// The Xcode configuration used to build the project.
|
||||||
|
const String kXcodeConfiguration = 'Configuration';
|
||||||
|
|
||||||
/// The define to pass build number
|
/// The define to pass build number
|
||||||
const String kBuildNumber = 'BuildNumber';
|
const String kBuildNumber = 'BuildNumber';
|
||||||
|
|
||||||
|
@ -6,13 +6,16 @@ import 'package:package_config/package_config.dart';
|
|||||||
|
|
||||||
import '../../artifacts.dart';
|
import '../../artifacts.dart';
|
||||||
import '../../base/build.dart';
|
import '../../base/build.dart';
|
||||||
|
import '../../base/common.dart';
|
||||||
import '../../base/file_system.dart';
|
import '../../base/file_system.dart';
|
||||||
import '../../base/io.dart';
|
import '../../base/io.dart';
|
||||||
import '../../build_info.dart';
|
import '../../build_info.dart';
|
||||||
import '../../compile.dart';
|
import '../../compile.dart';
|
||||||
import '../../dart/package_map.dart';
|
import '../../dart/package_map.dart';
|
||||||
import '../../devfs.dart';
|
import '../../devfs.dart';
|
||||||
import '../../globals.dart' as globals show xcode;
|
import '../../globals.dart' as globals show platform, xcode;
|
||||||
|
import '../../project.dart';
|
||||||
|
import '../../runner/flutter_command.dart';
|
||||||
import '../build_system.dart';
|
import '../build_system.dart';
|
||||||
import '../depfile.dart';
|
import '../depfile.dart';
|
||||||
import '../exceptions.dart';
|
import '../exceptions.dart';
|
||||||
@ -251,6 +254,9 @@ class KernelSnapshot extends Target {
|
|||||||
|
|
||||||
final String dillPath = environment.buildDir.childFile(dillName).path;
|
final String dillPath = environment.buildDir.childFile(dillName).path;
|
||||||
|
|
||||||
|
final List<String> dartDefines = decodeDartDefines(environment.defines, kDartDefines);
|
||||||
|
await _addFlavorToDartDefines(dartDefines, environment, targetPlatform);
|
||||||
|
|
||||||
final CompilerOutput? output = await compiler.compile(
|
final CompilerOutput? output = await compiler.compile(
|
||||||
sdkRoot: environment.artifacts.getArtifactPath(
|
sdkRoot: environment.artifacts.getArtifactPath(
|
||||||
Artifact.flutterPatchedSdkPath,
|
Artifact.flutterPatchedSdkPath,
|
||||||
@ -271,7 +277,7 @@ class KernelSnapshot extends Target {
|
|||||||
extraFrontEndOptions: extraFrontEndOptions,
|
extraFrontEndOptions: extraFrontEndOptions,
|
||||||
fileSystemRoots: fileSystemRoots,
|
fileSystemRoots: fileSystemRoots,
|
||||||
fileSystemScheme: fileSystemScheme,
|
fileSystemScheme: fileSystemScheme,
|
||||||
dartDefines: decodeDartDefines(environment.defines, kDartDefines),
|
dartDefines: dartDefines,
|
||||||
packageConfig: packageConfig,
|
packageConfig: packageConfig,
|
||||||
buildDir: environment.buildDir,
|
buildDir: environment.buildDir,
|
||||||
targetOS: targetOS,
|
targetOS: targetOS,
|
||||||
@ -281,6 +287,40 @@ class KernelSnapshot extends Target {
|
|||||||
throw Exception();
|
throw Exception();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _addFlavorToDartDefines(
|
||||||
|
List<String> dartDefines,
|
||||||
|
Environment environment,
|
||||||
|
TargetPlatform targetPlatform,
|
||||||
|
) async {
|
||||||
|
final String? flavor;
|
||||||
|
|
||||||
|
// For iOS and macOS projects, parse the flavor from the configuration, do
|
||||||
|
// not get from the FLAVOR environment variable. This is because when built
|
||||||
|
// from Xcode, the scheme/flavor can be changed through the UI and is not
|
||||||
|
// reflected in the environment variable.
|
||||||
|
if (targetPlatform == TargetPlatform.ios || targetPlatform == TargetPlatform.darwin) {
|
||||||
|
final FlutterProject flutterProject = FlutterProject.fromDirectory(environment.projectDir);
|
||||||
|
final XcodeBasedProject xcodeProject =
|
||||||
|
targetPlatform == TargetPlatform.ios ? flutterProject.ios : flutterProject.macos;
|
||||||
|
flavor = await xcodeProject.parseFlavorFromConfiguration(environment);
|
||||||
|
} else {
|
||||||
|
flavor = environment.defines[kFlavor];
|
||||||
|
}
|
||||||
|
if (flavor == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (globals.platform.environment[kAppFlavor] != null) {
|
||||||
|
throwToolExit('$kAppFlavor is used by the framework and cannot be set in the environment.');
|
||||||
|
}
|
||||||
|
if (dartDefines.any((String define) => define.startsWith(kAppFlavor))) {
|
||||||
|
throwToolExit(
|
||||||
|
'$kAppFlavor is used by the framework and cannot be '
|
||||||
|
'set using --${FlutterOptions.kDartDefinesOption} or --${FlutterOptions.kDartDefineFromFileOption}',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
dartDefines.add('$kAppFlavor=$flavor');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Supports compiling a dart kernel file to an ELF binary.
|
/// Supports compiling a dart kernel file to an ELF binary.
|
||||||
|
@ -526,6 +526,7 @@ abstract class IosAssetBundle extends Target {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final FlutterProject flutterProject = FlutterProject.fromDirectory(environment.projectDir);
|
final FlutterProject flutterProject = FlutterProject.fromDirectory(environment.projectDir);
|
||||||
|
final String? flavor = await flutterProject.ios.parseFlavorFromConfiguration(environment);
|
||||||
|
|
||||||
// Copy the assets.
|
// Copy the assets.
|
||||||
final Depfile assetDepfile = await copyAssets(
|
final Depfile assetDepfile = await copyAssets(
|
||||||
@ -542,7 +543,7 @@ abstract class IosAssetBundle extends Target {
|
|||||||
environment.buildDir.childFile('native_assets.json'),
|
environment.buildDir.childFile('native_assets.json'),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
flavor: environment.defines[kFlavor],
|
flavor: flavor,
|
||||||
);
|
);
|
||||||
environment.depFileService.writeToFile(
|
environment.depFileService.writeToFile(
|
||||||
assetDepfile,
|
assetDepfile,
|
||||||
|
@ -12,6 +12,7 @@ import '../../base/process.dart';
|
|||||||
import '../../build_info.dart';
|
import '../../build_info.dart';
|
||||||
import '../../devfs.dart';
|
import '../../devfs.dart';
|
||||||
import '../../globals.dart' as globals show xcode;
|
import '../../globals.dart' as globals show xcode;
|
||||||
|
import '../../project.dart';
|
||||||
import '../build_system.dart';
|
import '../build_system.dart';
|
||||||
import '../depfile.dart';
|
import '../depfile.dart';
|
||||||
import '../exceptions.dart';
|
import '../exceptions.dart';
|
||||||
@ -487,12 +488,15 @@ abstract class MacOSBundleFlutterAssets extends Target {
|
|||||||
.childDirectory('flutter_assets');
|
.childDirectory('flutter_assets');
|
||||||
assetDirectory.createSync(recursive: true);
|
assetDirectory.createSync(recursive: true);
|
||||||
|
|
||||||
|
final FlutterProject flutterProject = FlutterProject.fromDirectory(environment.projectDir);
|
||||||
|
final String? flavor = await flutterProject.macos.parseFlavorFromConfiguration(environment);
|
||||||
|
|
||||||
final Depfile assetDepfile = await copyAssets(
|
final Depfile assetDepfile = await copyAssets(
|
||||||
environment,
|
environment,
|
||||||
assetDirectory,
|
assetDirectory,
|
||||||
targetPlatform: TargetPlatform.darwin,
|
targetPlatform: TargetPlatform.darwin,
|
||||||
buildMode: buildMode,
|
buildMode: buildMode,
|
||||||
flavor: environment.defines[kFlavor],
|
flavor: flavor,
|
||||||
additionalContent: <String, DevFSContent>{
|
additionalContent: <String, DevFSContent>{
|
||||||
'NativeAssetsManifest.json': DevFSFileContent(
|
'NativeAssetsManifest.json': DevFSFileContent(
|
||||||
environment.buildDir.childFile('native_assets.json'),
|
environment.buildDir.childFile('native_assets.json'),
|
||||||
|
@ -1418,20 +1418,6 @@ abstract class FlutterCommand extends Command<void> {
|
|||||||
final String? defaultFlavor = project.manifest.defaultFlavor;
|
final String? defaultFlavor = project.manifest.defaultFlavor;
|
||||||
final String? cliFlavor = argParser.options.containsKey('flavor') ? stringArg('flavor') : null;
|
final String? cliFlavor = argParser.options.containsKey('flavor') ? stringArg('flavor') : null;
|
||||||
final String? flavor = cliFlavor ?? defaultFlavor;
|
final String? flavor = cliFlavor ?? defaultFlavor;
|
||||||
if (flavor != null) {
|
|
||||||
if (globals.platform.environment['FLUTTER_APP_FLAVOR'] != null) {
|
|
||||||
throwToolExit(
|
|
||||||
'FLUTTER_APP_FLAVOR is used by the framework and cannot be set in the environment.',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (dartDefines.any((String define) => define.startsWith('FLUTTER_APP_FLAVOR'))) {
|
|
||||||
throwToolExit(
|
|
||||||
'FLUTTER_APP_FLAVOR is used by the framework and cannot be '
|
|
||||||
'set using --${FlutterOptions.kDartDefinesOption} or --${FlutterOptions.kDartDefineFromFileOption}',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
dartDefines.add('FLUTTER_APP_FLAVOR=$flavor');
|
|
||||||
}
|
|
||||||
|
|
||||||
return BuildInfo(
|
return BuildInfo(
|
||||||
buildMode,
|
buildMode,
|
||||||
|
@ -7,6 +7,7 @@ import 'base/file_system.dart';
|
|||||||
import 'base/utils.dart';
|
import 'base/utils.dart';
|
||||||
import 'base/version.dart';
|
import 'base/version.dart';
|
||||||
import 'build_info.dart';
|
import 'build_info.dart';
|
||||||
|
import 'build_system/build_system.dart';
|
||||||
import 'bundle.dart' as bundle;
|
import 'bundle.dart' as bundle;
|
||||||
import 'convert.dart';
|
import 'convert.dart';
|
||||||
import 'features.dart';
|
import 'features.dart';
|
||||||
@ -283,6 +284,42 @@ abstract class XcodeBasedProject extends FlutterProjectPlatform {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// When flutter assemble runs within an Xcode run script, it does not know
|
||||||
|
/// the scheme and therefore doesn't know what flavor is being used. This
|
||||||
|
/// makes a best effort to parse the scheme name from the [kXcodeConfiguration].
|
||||||
|
/// Most flavor's [kXcodeConfiguration] should follow the naming convention
|
||||||
|
/// of '$baseConfiguration-$scheme'. This is only semi-enforced by
|
||||||
|
/// [buildXcodeProject], so it may not work. Also check if separated by a
|
||||||
|
/// space instead of a `-`. Once parsed, match it with a scheme/flavor name.
|
||||||
|
/// If the flavor cannot be parsed or matched, use the [kFlavor] environment
|
||||||
|
/// variable, which may or may not be set/correct, as a fallback.
|
||||||
|
Future<String?> parseFlavorFromConfiguration(Environment environment) async {
|
||||||
|
final String? configuration = environment.defines[kXcodeConfiguration];
|
||||||
|
final String? flavor = environment.defines[kFlavor];
|
||||||
|
if (configuration == null) {
|
||||||
|
return flavor;
|
||||||
|
}
|
||||||
|
List<String> splitConfiguration = configuration.split('-');
|
||||||
|
if (splitConfiguration.length == 1) {
|
||||||
|
splitConfiguration = configuration.split(' ');
|
||||||
|
}
|
||||||
|
if (splitConfiguration.length == 1) {
|
||||||
|
return flavor;
|
||||||
|
}
|
||||||
|
final String parsedScheme = splitConfiguration[1];
|
||||||
|
|
||||||
|
final XcodeProjectInfo? info = await projectInfo();
|
||||||
|
if (info == null) {
|
||||||
|
return flavor;
|
||||||
|
}
|
||||||
|
for (final String schemeName in info.schemes) {
|
||||||
|
if (schemeName.toLowerCase() == parsedScheme.toLowerCase()) {
|
||||||
|
return schemeName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return flavor;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents the iOS sub-project of a Flutter project.
|
/// Represents the iOS sub-project of a Flutter project.
|
||||||
|
@ -13,6 +13,8 @@ import 'package:flutter_tools/src/build_system/exceptions.dart';
|
|||||||
import 'package:flutter_tools/src/build_system/targets/common.dart';
|
import 'package:flutter_tools/src/build_system/targets/common.dart';
|
||||||
import 'package:flutter_tools/src/build_system/targets/ios.dart';
|
import 'package:flutter_tools/src/build_system/targets/ios.dart';
|
||||||
import 'package:flutter_tools/src/compile.dart';
|
import 'package:flutter_tools/src/compile.dart';
|
||||||
|
import 'package:flutter_tools/src/ios/xcodeproj.dart';
|
||||||
|
import 'package:test/fake.dart';
|
||||||
|
|
||||||
import '../../../src/common.dart';
|
import '../../../src/common.dart';
|
||||||
import '../../../src/context.dart';
|
import '../../../src/context.dart';
|
||||||
@ -346,7 +348,7 @@ void main() {
|
|||||||
expect(processManager, hasNoRemainingExpectations);
|
expect(processManager, hasNoRemainingExpectations);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWithoutContext(
|
testUsingContext(
|
||||||
'KernelSnapshot forces platform linking on debug for darwin target platforms',
|
'KernelSnapshot forces platform linking on debug for darwin target platforms',
|
||||||
() async {
|
() async {
|
||||||
fileSystem.file('.dart_tool/package_config.json')
|
fileSystem.file('.dart_tool/package_config.json')
|
||||||
@ -395,6 +397,214 @@ void main() {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
testUsingContext(
|
||||||
|
'KernelSnapshot sets flavor in dartDefines if found in environment variable for non ios/darwin app',
|
||||||
|
() async {
|
||||||
|
fileSystem.file('.dart_tool/package_config.json')
|
||||||
|
..createSync(recursive: true)
|
||||||
|
..writeAsStringSync('{"configVersion": 2, "packages":[]}');
|
||||||
|
final String build = androidEnvironment.buildDir.path;
|
||||||
|
final String flutterPatchedSdkPath = artifacts.getArtifactPath(
|
||||||
|
Artifact.flutterPatchedSdkPath,
|
||||||
|
platform: TargetPlatform.android,
|
||||||
|
mode: BuildMode.debug,
|
||||||
|
);
|
||||||
|
processManager.addCommands(<FakeCommand>[
|
||||||
|
FakeCommand(
|
||||||
|
command: <String>[
|
||||||
|
artifacts.getArtifactPath(Artifact.engineDartAotRuntime),
|
||||||
|
artifacts.getArtifactPath(Artifact.frontendServerSnapshotForEngineDartSdk),
|
||||||
|
'--sdk-root',
|
||||||
|
'$flutterPatchedSdkPath/',
|
||||||
|
'--target=flutter',
|
||||||
|
'--no-print-incremental-dependencies',
|
||||||
|
'-D$kAppFlavor=strawberry',
|
||||||
|
...buildModeOptions(BuildMode.debug, <String>[]),
|
||||||
|
'--no-link-platform',
|
||||||
|
'--packages',
|
||||||
|
'/.dart_tool/package_config.json',
|
||||||
|
'--output-dill',
|
||||||
|
'$build/app.dill',
|
||||||
|
'--depfile',
|
||||||
|
'$build/kernel_snapshot_program.d',
|
||||||
|
'--incremental',
|
||||||
|
'--initialize-from-dill',
|
||||||
|
'$build/app.dill',
|
||||||
|
'--verbosity=error',
|
||||||
|
'file:///lib/main.dart',
|
||||||
|
],
|
||||||
|
stdout: 'result $kBoundaryKey\n$kBoundaryKey\n$kBoundaryKey $build/app.dill 0\n',
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
testUsingContext(
|
||||||
|
"tool exits when $kAppFlavor is already set in user's environment",
|
||||||
|
() async {
|
||||||
|
fileSystem.file('.dart_tool/package_config.json')
|
||||||
|
..createSync(recursive: true)
|
||||||
|
..writeAsStringSync('{"configVersion": 2, "packages":[]}');
|
||||||
|
final Future<void> buildResult = const KernelSnapshot().build(
|
||||||
|
androidEnvironment
|
||||||
|
..defines[kTargetPlatform] = getNameForTargetPlatform(TargetPlatform.android)
|
||||||
|
..defines[kBuildMode] = BuildMode.debug.cliName
|
||||||
|
..defines[kFlavor] = 'strawberry'
|
||||||
|
..defines[kTrackWidgetCreation] = 'false',
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
buildResult,
|
||||||
|
throwsToolExit(
|
||||||
|
message: '$kAppFlavor is used by the framework and cannot be set in the environment.',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
overrides: <Type, Generator>{
|
||||||
|
Platform: () => FakePlatform(environment: <String, String>{kAppFlavor: 'I was already set'}),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
testUsingContext(
|
||||||
|
'tool exits when $kAppFlavor is set in --dart-define or --dart-define-from-file',
|
||||||
|
() async {
|
||||||
|
fileSystem.file('.dart_tool/package_config.json')
|
||||||
|
..createSync(recursive: true)
|
||||||
|
..writeAsStringSync('{"configVersion": 2, "packages":[]}');
|
||||||
|
final Future<void> buildResult = const KernelSnapshot().build(
|
||||||
|
androidEnvironment
|
||||||
|
..defines[kTargetPlatform] = getNameForTargetPlatform(TargetPlatform.android)
|
||||||
|
..defines[kBuildMode] = BuildMode.debug.cliName
|
||||||
|
..defines[kFlavor] = 'strawberry'
|
||||||
|
..defines[kDartDefines] = encodeDartDefines(<String>[kAppFlavor, 'strawberry'])
|
||||||
|
..defines[kTrackWidgetCreation] = 'false',
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
buildResult,
|
||||||
|
throwsToolExit(
|
||||||
|
message:
|
||||||
|
'$kAppFlavor is used by the framework and cannot be set using --dart-define or --dart-define-from-file',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
testUsingContext(
|
||||||
|
'KernelSnapshot sets flavor in dartDefines from Xcode build configuration if ios app',
|
||||||
|
() async {
|
||||||
|
fileSystem.file('.dart_tool/package_config.json')
|
||||||
|
..createSync(recursive: true)
|
||||||
|
..writeAsStringSync('{"configVersion": 2, "packages":[]}');
|
||||||
|
final String build = iosEnvironment.buildDir.path;
|
||||||
|
final String flutterPatchedSdkPath = artifacts.getArtifactPath(
|
||||||
|
Artifact.flutterPatchedSdkPath,
|
||||||
|
platform: TargetPlatform.ios,
|
||||||
|
mode: BuildMode.debug,
|
||||||
|
);
|
||||||
|
fileSystem.directory('/ios/Runner.xcodeproj').createSync(recursive: true);
|
||||||
|
processManager.addCommands(<FakeCommand>[
|
||||||
|
FakeCommand(
|
||||||
|
command: <String>[
|
||||||
|
artifacts.getArtifactPath(Artifact.engineDartAotRuntime),
|
||||||
|
artifacts.getArtifactPath(Artifact.frontendServerSnapshotForEngineDartSdk),
|
||||||
|
'--sdk-root',
|
||||||
|
'$flutterPatchedSdkPath/',
|
||||||
|
'--target=flutter',
|
||||||
|
'--no-print-incremental-dependencies',
|
||||||
|
'-D$kAppFlavor=chocolate',
|
||||||
|
...buildModeOptions(BuildMode.debug, <String>[]),
|
||||||
|
'--no-link-platform',
|
||||||
|
'--packages',
|
||||||
|
'/.dart_tool/package_config.json',
|
||||||
|
'--output-dill',
|
||||||
|
'$build/app.dill',
|
||||||
|
'--depfile',
|
||||||
|
'$build/kernel_snapshot_program.d',
|
||||||
|
'--incremental',
|
||||||
|
'--initialize-from-dill',
|
||||||
|
'$build/app.dill',
|
||||||
|
'--verbosity=error',
|
||||||
|
'file:///lib/main.dart',
|
||||||
|
],
|
||||||
|
stdout: 'result $kBoundaryKey\n$kBoundaryKey\n$kBoundaryKey $build/app.dill 0\n',
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
|
||||||
|
await const KernelSnapshot().build(
|
||||||
|
iosEnvironment
|
||||||
|
..defines[kTargetPlatform] = getNameForTargetPlatform(TargetPlatform.ios)
|
||||||
|
..defines[kBuildMode] = BuildMode.debug.cliName
|
||||||
|
..defines[kFlavor] = 'strawberry'
|
||||||
|
..defines[kXcodeConfiguration] = 'Debug-chocolate'
|
||||||
|
..defines[kTrackWidgetCreation] = 'false',
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(processManager, hasNoRemainingExpectations);
|
||||||
|
},
|
||||||
|
overrides: <Type, Generator>{
|
||||||
|
XcodeProjectInterpreter:
|
||||||
|
() => FakeXcodeProjectInterpreter(schemes: <String>['Runner', 'chocolate']),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
testUsingContext(
|
||||||
|
'KernelSnapshot sets flavor in dartDefines from Xcode build configuration if macos app',
|
||||||
|
() async {
|
||||||
|
fileSystem.file('.dart_tool/package_config.json')
|
||||||
|
..createSync(recursive: true)
|
||||||
|
..writeAsStringSync('{"configVersion": 2, "packages":[]}');
|
||||||
|
final String build = iosEnvironment.buildDir.path;
|
||||||
|
final String flutterPatchedSdkPath = artifacts.getArtifactPath(
|
||||||
|
Artifact.flutterPatchedSdkPath,
|
||||||
|
platform: TargetPlatform.darwin,
|
||||||
|
mode: BuildMode.debug,
|
||||||
|
);
|
||||||
|
fileSystem.directory('/macos/Runner.xcodeproj').createSync(recursive: true);
|
||||||
|
processManager.addCommands(<FakeCommand>[
|
||||||
|
FakeCommand(
|
||||||
|
command: <String>[
|
||||||
|
artifacts.getArtifactPath(Artifact.engineDartAotRuntime),
|
||||||
|
artifacts.getArtifactPath(Artifact.frontendServerSnapshotForEngineDartSdk),
|
||||||
|
'--sdk-root',
|
||||||
|
'$flutterPatchedSdkPath/',
|
||||||
|
'--target=flutter',
|
||||||
|
'--no-print-incremental-dependencies',
|
||||||
|
'-D$kAppFlavor=chocolate',
|
||||||
|
...buildModeOptions(BuildMode.debug, <String>[]),
|
||||||
|
'--packages',
|
||||||
|
'/.dart_tool/package_config.json',
|
||||||
|
'--output-dill',
|
||||||
|
'$build/app.dill',
|
||||||
|
'--depfile',
|
||||||
|
'$build/kernel_snapshot_program.d',
|
||||||
|
'--incremental',
|
||||||
|
'--initialize-from-dill',
|
||||||
|
'$build/app.dill',
|
||||||
|
'--verbosity=error',
|
||||||
|
'file:///lib/main.dart',
|
||||||
|
],
|
||||||
|
stdout: 'result $kBoundaryKey\n$kBoundaryKey\n$kBoundaryKey $build/app.dill 0\n',
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
|
||||||
|
await const KernelSnapshot().build(
|
||||||
|
iosEnvironment
|
||||||
|
..defines[kTargetPlatform] = getNameForTargetPlatform(TargetPlatform.darwin)
|
||||||
|
..defines[kBuildMode] = BuildMode.debug.cliName
|
||||||
|
..defines[kFlavor] = 'strawberry'
|
||||||
|
..defines[kXcodeConfiguration] = 'Debug-chocolate'
|
||||||
|
..defines[kTrackWidgetCreation] = 'false',
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(processManager, hasNoRemainingExpectations);
|
||||||
|
},
|
||||||
|
overrides: <Type, Generator>{
|
||||||
|
XcodeProjectInterpreter:
|
||||||
|
() => FakeXcodeProjectInterpreter(schemes: <String>['Runner', 'chocolate']),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
testWithoutContext('KernelSnapshot does use track widget creation on debug builds', () async {
|
testWithoutContext('KernelSnapshot does use track widget creation on debug builds', () async {
|
||||||
fileSystem.file('.dart_tool/package_config.json')
|
fileSystem.file('.dart_tool/package_config.json')
|
||||||
..createSync(recursive: true)
|
..createSync(recursive: true)
|
||||||
@ -707,3 +917,17 @@ void main() {
|
|||||||
expect(processManager, hasNoRemainingExpectations);
|
expect(processManager, hasNoRemainingExpectations);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class FakeXcodeProjectInterpreter extends Fake implements XcodeProjectInterpreter {
|
||||||
|
FakeXcodeProjectInterpreter({this.isInstalled = true, this.schemes = const <String>['Runner']});
|
||||||
|
|
||||||
|
@override
|
||||||
|
final bool isInstalled;
|
||||||
|
|
||||||
|
List<String> schemes;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<XcodeProjectInfo?> getInfo(String projectPath, {String? projectFilename}) async {
|
||||||
|
return XcodeProjectInfo(<String>[], <String>[], schemes, BufferLogger.test());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -11,7 +11,9 @@ 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/build_system/build_system.dart';
|
import 'package:flutter_tools/src/build_system/build_system.dart';
|
||||||
import 'package:flutter_tools/src/build_system/targets/ios.dart';
|
import 'package:flutter_tools/src/build_system/targets/ios.dart';
|
||||||
|
import 'package:flutter_tools/src/ios/xcodeproj.dart';
|
||||||
import 'package:flutter_tools/src/reporting/reporting.dart';
|
import 'package:flutter_tools/src/reporting/reporting.dart';
|
||||||
|
import 'package:test/fake.dart';
|
||||||
import 'package:unified_analytics/unified_analytics.dart';
|
import 'package:unified_analytics/unified_analytics.dart';
|
||||||
|
|
||||||
import '../../../src/common.dart';
|
import '../../../src/common.dart';
|
||||||
@ -267,6 +269,106 @@ void main() {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
testUsingContext(
|
||||||
|
'DebugIosApplicationBundle with flavor',
|
||||||
|
() async {
|
||||||
|
environment.defines[kBuildMode] = 'debug';
|
||||||
|
environment.defines[kCodesignIdentity] = 'ABC123';
|
||||||
|
environment.defines[kFlavor] = 'vanilla';
|
||||||
|
environment.defines[kXcodeConfiguration] = 'Debug-strawberry';
|
||||||
|
fileSystem.directory('/ios/Runner.xcodeproj').createSync(recursive: true);
|
||||||
|
fileSystem.file('pubspec.yaml')
|
||||||
|
..createSync()
|
||||||
|
..writeAsStringSync('''
|
||||||
|
name: example
|
||||||
|
flutter:
|
||||||
|
assets:
|
||||||
|
- assets/common/
|
||||||
|
- path: assets/vanilla/
|
||||||
|
flavors:
|
||||||
|
- vanilla
|
||||||
|
- path: assets/strawberry/
|
||||||
|
flavors:
|
||||||
|
- strawberry
|
||||||
|
''');
|
||||||
|
|
||||||
|
fileSystem.file('assets/common/image.png').createSync(recursive: true);
|
||||||
|
fileSystem.file('assets/vanilla/ice-cream.png').createSync(recursive: true);
|
||||||
|
fileSystem.file('assets/strawberry/ice-cream.png').createSync(recursive: true);
|
||||||
|
// Precompiled dart data
|
||||||
|
fileSystem
|
||||||
|
.file(artifacts.getArtifactPath(Artifact.vmSnapshotData, mode: BuildMode.debug))
|
||||||
|
.createSync();
|
||||||
|
fileSystem
|
||||||
|
.file(artifacts.getArtifactPath(Artifact.isolateSnapshotData, mode: BuildMode.debug))
|
||||||
|
.createSync();
|
||||||
|
// Project info
|
||||||
|
fileSystem
|
||||||
|
.directory('.dart_tool')
|
||||||
|
.childFile('package_config.json')
|
||||||
|
.createSync(recursive: true);
|
||||||
|
// Plist file
|
||||||
|
fileSystem
|
||||||
|
.file(fileSystem.path.join('ios', 'Flutter', 'AppFrameworkInfo.plist'))
|
||||||
|
.createSync(recursive: true);
|
||||||
|
// App kernel
|
||||||
|
environment.buildDir.childFile('app.dill').createSync(recursive: true);
|
||||||
|
environment.buildDir.childFile('native_assets.json').createSync();
|
||||||
|
// Stub framework
|
||||||
|
environment.buildDir
|
||||||
|
.childDirectory('App.framework')
|
||||||
|
.childFile('App')
|
||||||
|
.createSync(recursive: true);
|
||||||
|
|
||||||
|
final Directory frameworkDirectory = environment.outputDir.childDirectory('App.framework');
|
||||||
|
final File frameworkDirectoryBinary = frameworkDirectory.childFile('App');
|
||||||
|
processManager.addCommands(<FakeCommand>[
|
||||||
|
FakeCommand(
|
||||||
|
command: <String>[
|
||||||
|
'xattr',
|
||||||
|
'-r',
|
||||||
|
'-d',
|
||||||
|
'com.apple.FinderInfo',
|
||||||
|
frameworkDirectoryBinary.path,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
FakeCommand(
|
||||||
|
command: <String>[
|
||||||
|
'codesign',
|
||||||
|
'--force',
|
||||||
|
'--sign',
|
||||||
|
'ABC123',
|
||||||
|
'--timestamp=none',
|
||||||
|
frameworkDirectoryBinary.path,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
await const DebugIosApplicationBundle().build(environment);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
fileSystem.file('${frameworkDirectory.path}/flutter_assets/assets/common/image.png'),
|
||||||
|
exists,
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
fileSystem.file('${frameworkDirectory.path}/flutter_assets/assets/vanilla/ice-cream.png'),
|
||||||
|
isNot(exists),
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
fileSystem.file(
|
||||||
|
'${frameworkDirectory.path}/flutter_assets/assets/strawberry/ice-cream.png',
|
||||||
|
),
|
||||||
|
exists,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
overrides: <Type, Generator>{
|
||||||
|
FileSystem: () => fileSystem,
|
||||||
|
ProcessManager: () => processManager,
|
||||||
|
Platform: () => macPlatform,
|
||||||
|
XcodeProjectInterpreter:
|
||||||
|
() => FakeXcodeProjectInterpreter(schemes: <String>['Runner', 'strawberry']),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
testUsingContext(
|
testUsingContext(
|
||||||
'DebugIosApplicationBundle with impeller and shader compilation',
|
'DebugIosApplicationBundle with impeller and shader compilation',
|
||||||
() async {
|
() async {
|
||||||
@ -1058,3 +1160,17 @@ void main() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class FakeXcodeProjectInterpreter extends Fake implements XcodeProjectInterpreter {
|
||||||
|
FakeXcodeProjectInterpreter({this.isInstalled = true, this.schemes = const <String>['Runner']});
|
||||||
|
|
||||||
|
@override
|
||||||
|
final bool isInstalled;
|
||||||
|
|
||||||
|
List<String> schemes;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<XcodeProjectInfo?> getInfo(String projectPath, {String? projectFilename}) async {
|
||||||
|
return XcodeProjectInfo(<String>[], <String>[], schemes, BufferLogger.test());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -10,6 +10,8 @@ import 'package:flutter_tools/src/base/logger.dart';
|
|||||||
import 'package:flutter_tools/src/build_info.dart';
|
import 'package:flutter_tools/src/build_info.dart';
|
||||||
import 'package:flutter_tools/src/build_system/build_system.dart';
|
import 'package:flutter_tools/src/build_system/build_system.dart';
|
||||||
import 'package:flutter_tools/src/build_system/targets/macos.dart';
|
import 'package:flutter_tools/src/build_system/targets/macos.dart';
|
||||||
|
import 'package:flutter_tools/src/ios/xcodeproj.dart';
|
||||||
|
import 'package:test/fake.dart';
|
||||||
import 'package:unified_analytics/unified_analytics.dart';
|
import 'package:unified_analytics/unified_analytics.dart';
|
||||||
|
|
||||||
import '../../../src/common.dart';
|
import '../../../src/common.dart';
|
||||||
@ -465,6 +467,93 @@ void main() {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
testUsingContext(
|
||||||
|
'debug macOS application copies correct assets with flavor',
|
||||||
|
() async {
|
||||||
|
fileSystem
|
||||||
|
.directory(
|
||||||
|
artifacts.getArtifactPath(Artifact.flutterMacOSFramework, mode: BuildMode.debug),
|
||||||
|
)
|
||||||
|
.createSync();
|
||||||
|
fileSystem
|
||||||
|
.file(
|
||||||
|
artifacts.getArtifactPath(
|
||||||
|
Artifact.vmSnapshotData,
|
||||||
|
platform: TargetPlatform.darwin,
|
||||||
|
mode: BuildMode.debug,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.createSync(recursive: true);
|
||||||
|
fileSystem
|
||||||
|
.file(
|
||||||
|
artifacts.getArtifactPath(
|
||||||
|
Artifact.isolateSnapshotData,
|
||||||
|
platform: TargetPlatform.darwin,
|
||||||
|
mode: BuildMode.debug,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.createSync(recursive: true);
|
||||||
|
fileSystem.file('${environment.buildDir.path}/App.framework/App').createSync(recursive: true);
|
||||||
|
|
||||||
|
final String inputKernel = '${environment.buildDir.path}/app.dill';
|
||||||
|
fileSystem.file(inputKernel)
|
||||||
|
..createSync(recursive: true)
|
||||||
|
..writeAsStringSync('testing');
|
||||||
|
environment.buildDir.childFile('native_assets.json').createSync();
|
||||||
|
|
||||||
|
environment.defines[kXcodeConfiguration] = 'Debug-strawberry';
|
||||||
|
fileSystem.directory('/macos/Runner.xcodeproj').createSync(recursive: true);
|
||||||
|
|
||||||
|
fileSystem.file('pubspec.yaml')
|
||||||
|
..createSync()
|
||||||
|
..writeAsStringSync('''
|
||||||
|
name: example
|
||||||
|
flutter:
|
||||||
|
assets:
|
||||||
|
- assets/common/
|
||||||
|
- path: assets/vanilla/
|
||||||
|
flavors:
|
||||||
|
- vanilla
|
||||||
|
- path: assets/strawberry/
|
||||||
|
flavors:
|
||||||
|
- strawberry
|
||||||
|
''');
|
||||||
|
|
||||||
|
fileSystem.file('assets/common/image.png').createSync(recursive: true);
|
||||||
|
fileSystem.file('assets/vanilla/ice-cream.png').createSync(recursive: true);
|
||||||
|
fileSystem.file('assets/strawberry/ice-cream.png').createSync(recursive: true);
|
||||||
|
fileSystem
|
||||||
|
.directory('.dart_tool')
|
||||||
|
.childFile('package_config.json')
|
||||||
|
.createSync(recursive: true);
|
||||||
|
|
||||||
|
await const DebugMacOSBundleFlutterAssets().build(environment);
|
||||||
|
final Directory frameworkDirectory = environment.outputDir.childDirectory(
|
||||||
|
'/App.framework/Versions/A/Resources/',
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
fileSystem.file('${frameworkDirectory.path}/flutter_assets/assets/common/image.png'),
|
||||||
|
exists,
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
fileSystem.file('${frameworkDirectory.path}/flutter_assets/assets/vanilla/ice-cream.png'),
|
||||||
|
isNot(exists),
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
fileSystem.file(
|
||||||
|
'${frameworkDirectory.path}/flutter_assets/assets/strawberry/ice-cream.png',
|
||||||
|
),
|
||||||
|
exists,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
overrides: <Type, Generator>{
|
||||||
|
FileSystem: () => fileSystem,
|
||||||
|
ProcessManager: () => processManager,
|
||||||
|
XcodeProjectInterpreter:
|
||||||
|
() => FakeXcodeProjectInterpreter(schemes: <String>['Runner', 'strawberry']),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
testUsingContext(
|
testUsingContext(
|
||||||
'release/profile macOS application has no blob or precompiled runtime',
|
'release/profile macOS application has no blob or precompiled runtime',
|
||||||
() async {
|
() async {
|
||||||
@ -883,3 +972,17 @@ void main() {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class FakeXcodeProjectInterpreter extends Fake implements XcodeProjectInterpreter {
|
||||||
|
FakeXcodeProjectInterpreter({this.isInstalled = true, this.schemes = const <String>['Runner']});
|
||||||
|
|
||||||
|
@override
|
||||||
|
final bool isInstalled;
|
||||||
|
|
||||||
|
List<String> schemes;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<XcodeProjectInfo?> getInfo(String projectPath, {String? projectFilename}) async {
|
||||||
|
return XcodeProjectInfo(<String>[], <String>[], schemes, BufferLogger.test());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -18,7 +18,6 @@ import 'package:flutter_tools/src/base/time.dart';
|
|||||||
import 'package:flutter_tools/src/base/user_messages.dart';
|
import 'package:flutter_tools/src/base/user_messages.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';
|
||||||
import 'package:flutter_tools/src/commands/run.dart';
|
|
||||||
import 'package:flutter_tools/src/device.dart';
|
import 'package:flutter_tools/src/device.dart';
|
||||||
import 'package:flutter_tools/src/globals.dart' as globals;
|
import 'package:flutter_tools/src/globals.dart' as globals;
|
||||||
import 'package:flutter_tools/src/pre_run_validator.dart';
|
import 'package:flutter_tools/src/pre_run_validator.dart';
|
||||||
@ -1209,108 +1208,12 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
group('--flavor', () {
|
group('--flavor', () {
|
||||||
late _TestDeviceManager testDeviceManager;
|
|
||||||
late Logger logger;
|
|
||||||
late FileSystem fileSystem;
|
late FileSystem fileSystem;
|
||||||
|
|
||||||
setUp(() {
|
setUp(() {
|
||||||
logger = BufferLogger.test();
|
|
||||||
testDeviceManager = _TestDeviceManager(logger: logger);
|
|
||||||
fileSystem = MemoryFileSystem.test();
|
fileSystem = MemoryFileSystem.test();
|
||||||
});
|
});
|
||||||
|
|
||||||
testUsingContext(
|
|
||||||
"tool exits when FLUTTER_APP_FLAVOR is already set in user's environment",
|
|
||||||
() async {
|
|
||||||
fileSystem.file('lib/main.dart').createSync(recursive: true);
|
|
||||||
fileSystem.file('pubspec.yaml').createSync();
|
|
||||||
|
|
||||||
final FakeDevice device = FakeDevice(
|
|
||||||
'name',
|
|
||||||
'id',
|
|
||||||
type: PlatformType.android,
|
|
||||||
supportsFlavors: true,
|
|
||||||
);
|
|
||||||
testDeviceManager.devices = <Device>[device];
|
|
||||||
final _TestRunCommandThatOnlyValidates command = _TestRunCommandThatOnlyValidates();
|
|
||||||
final CommandRunner<void> runner = createTestCommandRunner(command);
|
|
||||||
|
|
||||||
expect(
|
|
||||||
runner.run(<String>['run', '--no-pub', '--no-hot', '--flavor=strawberry']),
|
|
||||||
throwsToolExit(
|
|
||||||
message:
|
|
||||||
'FLUTTER_APP_FLAVOR is used by the framework and cannot be set in the environment.',
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
overrides: <Type, Generator>{
|
|
||||||
DeviceManager: () => testDeviceManager,
|
|
||||||
Platform:
|
|
||||||
() => FakePlatform(
|
|
||||||
environment: <String, String>{'FLUTTER_APP_FLAVOR': 'I was already set'},
|
|
||||||
),
|
|
||||||
Cache: () => Cache.test(processManager: FakeProcessManager.any()),
|
|
||||||
FileSystem: () => fileSystem,
|
|
||||||
ProcessManager: () => FakeProcessManager.any(),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
testUsingContext(
|
|
||||||
'tool exits when FLUTTER_APP_FLAVOR is set in --dart-define or --dart-define-from-file',
|
|
||||||
() async {
|
|
||||||
fileSystem.file('lib/main.dart').createSync(recursive: true);
|
|
||||||
fileSystem.file('pubspec.yaml').createSync();
|
|
||||||
fileSystem.file('config.json')
|
|
||||||
..createSync()
|
|
||||||
..writeAsStringSync('{"FLUTTER_APP_FLAVOR": "strawberry"}');
|
|
||||||
|
|
||||||
final FakeDevice device = FakeDevice(
|
|
||||||
'name',
|
|
||||||
'id',
|
|
||||||
type: PlatformType.android,
|
|
||||||
supportsFlavors: true,
|
|
||||||
);
|
|
||||||
testDeviceManager.devices = <Device>[device];
|
|
||||||
final _TestRunCommandThatOnlyValidates command = _TestRunCommandThatOnlyValidates();
|
|
||||||
final CommandRunner<void> runner = createTestCommandRunner(command);
|
|
||||||
|
|
||||||
expect(
|
|
||||||
runner.run(<String>[
|
|
||||||
'run',
|
|
||||||
'--dart-define=FLUTTER_APP_FLAVOR=strawberry',
|
|
||||||
'--no-pub',
|
|
||||||
'--no-hot',
|
|
||||||
'--flavor=strawberry',
|
|
||||||
]),
|
|
||||||
throwsToolExit(
|
|
||||||
message:
|
|
||||||
'FLUTTER_APP_FLAVOR is used by the framework and cannot be set using --dart-define or --dart-define-from-file',
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(
|
|
||||||
runner.run(<String>[
|
|
||||||
'run',
|
|
||||||
'--dart-define-from-file=config.json',
|
|
||||||
'--no-pub',
|
|
||||||
'--no-hot',
|
|
||||||
'--flavor=strawberry',
|
|
||||||
]),
|
|
||||||
throwsToolExit(
|
|
||||||
message:
|
|
||||||
'FLUTTER_APP_FLAVOR is used by the framework and cannot be set using --dart-define or --dart-define-from-file',
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
overrides: <Type, Generator>{
|
|
||||||
DeviceManager: () => testDeviceManager,
|
|
||||||
Platform: () => FakePlatform(),
|
|
||||||
Cache: () => Cache.test(processManager: FakeProcessManager.any()),
|
|
||||||
FileSystem: () => fileSystem,
|
|
||||||
ProcessManager: () => FakeProcessManager.any(),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
testUsingContext(
|
testUsingContext(
|
||||||
'CLI option overrides default flavor from manifest',
|
'CLI option overrides default flavor from manifest',
|
||||||
() async {
|
() async {
|
||||||
@ -1475,22 +1378,3 @@ class FakeClock extends Fake implements SystemClock {
|
|||||||
return DateTime.fromMillisecondsSinceEpoch(times.removeAt(0));
|
return DateTime.fromMillisecondsSinceEpoch(times.removeAt(0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _TestDeviceManager extends DeviceManager {
|
|
||||||
_TestDeviceManager({required super.logger});
|
|
||||||
List<Device> devices = <Device>[];
|
|
||||||
|
|
||||||
@override
|
|
||||||
List<DeviceDiscovery> get deviceDiscoverers {
|
|
||||||
final FakePollingDeviceDiscovery discoverer = FakePollingDeviceDiscovery();
|
|
||||||
devices.forEach(discoverer.addDevice);
|
|
||||||
return <DeviceDiscovery>[discoverer];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _TestRunCommandThatOnlyValidates extends RunCommand {
|
|
||||||
@override
|
|
||||||
Future<FlutterCommandResult> runCommand() async {
|
|
||||||
return FlutterCommandResult.success();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -89,6 +89,7 @@ void main() {
|
|||||||
'-dTargetPlatform=ios',
|
'-dTargetPlatform=ios',
|
||||||
'-dTargetFile=lib/main.dart',
|
'-dTargetFile=lib/main.dart',
|
||||||
'-dBuildMode=${buildMode.toLowerCase()}',
|
'-dBuildMode=${buildMode.toLowerCase()}',
|
||||||
|
'-dConfiguration=$buildMode',
|
||||||
'-dIosArchs=',
|
'-dIosArchs=',
|
||||||
'-dSdkRoot=',
|
'-dSdkRoot=',
|
||||||
'-dSplitDebugInfo=',
|
'-dSplitDebugInfo=',
|
||||||
@ -139,7 +140,7 @@ void main() {
|
|||||||
'ARCHS': archs,
|
'ARCHS': archs,
|
||||||
'BUILT_PRODUCTS_DIR': buildDir.path,
|
'BUILT_PRODUCTS_DIR': buildDir.path,
|
||||||
'CODE_SIGNING_REQUIRED': 'YES',
|
'CODE_SIGNING_REQUIRED': 'YES',
|
||||||
'CONFIGURATION': buildMode,
|
'CONFIGURATION': '$buildMode-strawberry',
|
||||||
'DART_DEFINES': dartDefines,
|
'DART_DEFINES': dartDefines,
|
||||||
'DART_OBFUSCATION': dartObfuscation,
|
'DART_OBFUSCATION': dartObfuscation,
|
||||||
'EXPANDED_CODE_SIGN_IDENTITY': expandedCodeSignIdentity,
|
'EXPANDED_CODE_SIGN_IDENTITY': expandedCodeSignIdentity,
|
||||||
@ -165,6 +166,7 @@ void main() {
|
|||||||
'-dTargetFile=lib/main.dart',
|
'-dTargetFile=lib/main.dart',
|
||||||
'-dBuildMode=${buildMode.toLowerCase()}',
|
'-dBuildMode=${buildMode.toLowerCase()}',
|
||||||
'-dFlavor=strawberry',
|
'-dFlavor=strawberry',
|
||||||
|
'-dConfiguration=$buildMode-strawberry',
|
||||||
'-dIosArchs=$archs',
|
'-dIosArchs=$archs',
|
||||||
'-dSdkRoot=$sdkRoot',
|
'-dSdkRoot=$sdkRoot',
|
||||||
'-dSplitDebugInfo=$splitDebugInfo',
|
'-dSplitDebugInfo=$splitDebugInfo',
|
||||||
@ -284,6 +286,7 @@ void main() {
|
|||||||
'-dTargetPlatform=ios',
|
'-dTargetPlatform=ios',
|
||||||
'-dTargetFile=lib/main.dart',
|
'-dTargetFile=lib/main.dart',
|
||||||
'-dBuildMode=${buildMode.toLowerCase()}',
|
'-dBuildMode=${buildMode.toLowerCase()}',
|
||||||
|
'-dConfiguration=$buildMode',
|
||||||
'-dIosArchs=',
|
'-dIosArchs=',
|
||||||
'-dSdkRoot=',
|
'-dSdkRoot=',
|
||||||
'-dSplitDebugInfo=',
|
'-dSplitDebugInfo=',
|
||||||
@ -330,7 +333,6 @@ void main() {
|
|||||||
'ARCHS': archs,
|
'ARCHS': archs,
|
||||||
'BUILT_PRODUCTS_DIR': buildDir.path,
|
'BUILT_PRODUCTS_DIR': buildDir.path,
|
||||||
'CODE_SIGNING_REQUIRED': 'YES',
|
'CODE_SIGNING_REQUIRED': 'YES',
|
||||||
'CONFIGURATION': buildMode,
|
|
||||||
'DART_DEFINES': dartDefines,
|
'DART_DEFINES': dartDefines,
|
||||||
'DART_OBFUSCATION': dartObfuscation,
|
'DART_OBFUSCATION': dartObfuscation,
|
||||||
'EXPANDED_CODE_SIGN_IDENTITY': expandedCodeSignIdentity,
|
'EXPANDED_CODE_SIGN_IDENTITY': expandedCodeSignIdentity,
|
||||||
@ -340,6 +342,7 @@ void main() {
|
|||||||
'FRONTEND_SERVER_STARTER_PATH': frontendServerStarterPath,
|
'FRONTEND_SERVER_STARTER_PATH': frontendServerStarterPath,
|
||||||
'INFOPLIST_PATH': 'Info.plist',
|
'INFOPLIST_PATH': 'Info.plist',
|
||||||
'SDKROOT': sdkRoot,
|
'SDKROOT': sdkRoot,
|
||||||
|
'CONFIGURATION': '$buildMode-strawberry',
|
||||||
'FLAVOR': 'strawberry',
|
'FLAVOR': 'strawberry',
|
||||||
'SPLIT_DEBUG_INFO': splitDebugInfo,
|
'SPLIT_DEBUG_INFO': splitDebugInfo,
|
||||||
'TRACK_WIDGET_CREATION': trackWidgetCreation,
|
'TRACK_WIDGET_CREATION': trackWidgetCreation,
|
||||||
@ -356,6 +359,7 @@ void main() {
|
|||||||
'-dTargetFile=lib/main.dart',
|
'-dTargetFile=lib/main.dart',
|
||||||
'-dBuildMode=${buildMode.toLowerCase()}',
|
'-dBuildMode=${buildMode.toLowerCase()}',
|
||||||
'-dFlavor=strawberry',
|
'-dFlavor=strawberry',
|
||||||
|
'-dConfiguration=$buildMode-strawberry',
|
||||||
'-dIosArchs=$archs',
|
'-dIosArchs=$archs',
|
||||||
'-dSdkRoot=$sdkRoot',
|
'-dSdkRoot=$sdkRoot',
|
||||||
'-dSplitDebugInfo=$splitDebugInfo',
|
'-dSplitDebugInfo=$splitDebugInfo',
|
||||||
@ -406,6 +410,7 @@ void main() {
|
|||||||
'-dTargetPlatform=ios',
|
'-dTargetPlatform=ios',
|
||||||
'-dTargetFile=lib/main.dart',
|
'-dTargetFile=lib/main.dart',
|
||||||
'-dBuildMode=${buildMode.toLowerCase()}',
|
'-dBuildMode=${buildMode.toLowerCase()}',
|
||||||
|
'-dConfiguration=$buildMode',
|
||||||
'-dIosArchs=arm64',
|
'-dIosArchs=arm64',
|
||||||
'-dSdkRoot=',
|
'-dSdkRoot=',
|
||||||
'-dSplitDebugInfo=',
|
'-dSplitDebugInfo=',
|
||||||
@ -456,6 +461,7 @@ void main() {
|
|||||||
'-dTargetPlatform=ios',
|
'-dTargetPlatform=ios',
|
||||||
'-dTargetFile=lib/main.dart',
|
'-dTargetFile=lib/main.dart',
|
||||||
'-dBuildMode=${buildMode.toLowerCase()}',
|
'-dBuildMode=${buildMode.toLowerCase()}',
|
||||||
|
'-dConfiguration=$buildMode',
|
||||||
'-dIosArchs=arm64',
|
'-dIosArchs=arm64',
|
||||||
'-dSdkRoot=',
|
'-dSdkRoot=',
|
||||||
'-dSplitDebugInfo=',
|
'-dSplitDebugInfo=',
|
||||||
@ -505,6 +511,7 @@ void main() {
|
|||||||
'-dTargetPlatform=ios',
|
'-dTargetPlatform=ios',
|
||||||
'-dTargetFile=lib/main.dart',
|
'-dTargetFile=lib/main.dart',
|
||||||
'-dBuildMode=${buildMode.toLowerCase()}',
|
'-dBuildMode=${buildMode.toLowerCase()}',
|
||||||
|
'-dConfiguration=$buildMode',
|
||||||
'-dIosArchs=arm64 x86_64',
|
'-dIosArchs=arm64 x86_64',
|
||||||
'-dSdkRoot=',
|
'-dSdkRoot=',
|
||||||
'-dSplitDebugInfo=',
|
'-dSplitDebugInfo=',
|
||||||
|
@ -4,9 +4,12 @@
|
|||||||
|
|
||||||
import 'package:file/file.dart';
|
import 'package:file/file.dart';
|
||||||
import 'package:file/memory.dart';
|
import 'package:file/memory.dart';
|
||||||
|
import 'package:flutter_tools/src/artifacts.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/base/version.dart';
|
import 'package:flutter_tools/src/base/version.dart';
|
||||||
|
import 'package:flutter_tools/src/build_info.dart';
|
||||||
|
import 'package:flutter_tools/src/build_system/build_system.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';
|
||||||
import 'package:flutter_tools/src/ios/xcodeproj.dart';
|
import 'package:flutter_tools/src/ios/xcodeproj.dart';
|
||||||
@ -216,6 +219,102 @@ void main() {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
group('parseFlavorFromConfiguration', () {
|
||||||
|
testWithoutContext('from FLAVOR when CONFIGURATION is null', () async {
|
||||||
|
final MemoryFileSystem fs = MemoryFileSystem.test();
|
||||||
|
final IosProject project = IosProject.fromFlutter(FakeFlutterProject(fileSystem: fs));
|
||||||
|
final Environment env = Environment.test(
|
||||||
|
fs.currentDirectory,
|
||||||
|
fileSystem: fs,
|
||||||
|
logger: BufferLogger.test(),
|
||||||
|
artifacts: Artifacts.test(),
|
||||||
|
processManager: FakeProcessManager.any(),
|
||||||
|
defines: <String, String>{kFlavor: 'strawberry'},
|
||||||
|
);
|
||||||
|
expect(await project.parseFlavorFromConfiguration(env), 'strawberry');
|
||||||
|
});
|
||||||
|
|
||||||
|
testWithoutContext('from FLAVOR when CONFIGURATION is does not contain delimiter', () async {
|
||||||
|
final MemoryFileSystem fs = MemoryFileSystem.test();
|
||||||
|
final IosProject project = IosProject.fromFlutter(FakeFlutterProject(fileSystem: fs));
|
||||||
|
final Environment env = Environment.test(
|
||||||
|
fs.currentDirectory,
|
||||||
|
fileSystem: fs,
|
||||||
|
logger: BufferLogger.test(),
|
||||||
|
artifacts: Artifacts.test(),
|
||||||
|
processManager: FakeProcessManager.any(),
|
||||||
|
defines: <String, String>{kFlavor: 'strawberry', kXcodeConfiguration: 'Debug'},
|
||||||
|
);
|
||||||
|
expect(await project.parseFlavorFromConfiguration(env), 'strawberry');
|
||||||
|
});
|
||||||
|
|
||||||
|
testUsingContext(
|
||||||
|
'from CONFIGURATION when has flavor following a hyphen that matches a scheme',
|
||||||
|
() async {
|
||||||
|
final MemoryFileSystem fs = MemoryFileSystem.test();
|
||||||
|
final IosProject project = IosProject.fromFlutter(FakeFlutterProject(fileSystem: fs));
|
||||||
|
final Environment env = Environment.test(
|
||||||
|
fs.currentDirectory,
|
||||||
|
fileSystem: fs,
|
||||||
|
logger: BufferLogger.test(),
|
||||||
|
artifacts: Artifacts.test(),
|
||||||
|
processManager: FakeProcessManager.any(),
|
||||||
|
defines: <String, String>{kFlavor: 'strawberry', kXcodeConfiguration: 'Debug-vanilla'},
|
||||||
|
);
|
||||||
|
project.xcodeProject.createSync(recursive: true);
|
||||||
|
expect(await project.parseFlavorFromConfiguration(env), 'vanilla');
|
||||||
|
},
|
||||||
|
overrides: <Type, Generator>{
|
||||||
|
XcodeProjectInterpreter:
|
||||||
|
() => FakeXcodeProjectInterpreter(schemes: <String>['Runner', 'vanilla']),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
testUsingContext(
|
||||||
|
'from CONFIGURATION when has flavor following a space that matches a scheme',
|
||||||
|
() async {
|
||||||
|
final MemoryFileSystem fs = MemoryFileSystem.test();
|
||||||
|
final IosProject project = IosProject.fromFlutter(FakeFlutterProject(fileSystem: fs));
|
||||||
|
final Environment env = Environment.test(
|
||||||
|
fs.currentDirectory,
|
||||||
|
fileSystem: fs,
|
||||||
|
logger: BufferLogger.test(),
|
||||||
|
artifacts: Artifacts.test(),
|
||||||
|
processManager: FakeProcessManager.any(),
|
||||||
|
defines: <String, String>{kFlavor: 'strawberry', kXcodeConfiguration: 'Debug vanilla'},
|
||||||
|
);
|
||||||
|
project.xcodeProject.createSync(recursive: true);
|
||||||
|
expect(await project.parseFlavorFromConfiguration(env), 'vanilla');
|
||||||
|
},
|
||||||
|
overrides: <Type, Generator>{
|
||||||
|
XcodeProjectInterpreter:
|
||||||
|
() => FakeXcodeProjectInterpreter(schemes: <String>['Runner', 'vanilla']),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
testUsingContext(
|
||||||
|
'from FLAVOR when CONFIGURATION does not match a scheme',
|
||||||
|
() async {
|
||||||
|
final MemoryFileSystem fs = MemoryFileSystem.test();
|
||||||
|
final IosProject project = IosProject.fromFlutter(FakeFlutterProject(fileSystem: fs));
|
||||||
|
final Environment env = Environment.test(
|
||||||
|
fs.currentDirectory,
|
||||||
|
fileSystem: fs,
|
||||||
|
logger: BufferLogger.test(),
|
||||||
|
artifacts: Artifacts.test(),
|
||||||
|
processManager: FakeProcessManager.any(),
|
||||||
|
defines: <String, String>{kFlavor: 'strawberry', kXcodeConfiguration: 'Debug-random'},
|
||||||
|
);
|
||||||
|
project.xcodeProject.createSync(recursive: true);
|
||||||
|
expect(await project.parseFlavorFromConfiguration(env), 'strawberry');
|
||||||
|
},
|
||||||
|
overrides: <Type, Generator>{
|
||||||
|
XcodeProjectInterpreter:
|
||||||
|
() => FakeXcodeProjectInterpreter(schemes: <String>['Runner', 'vanilla']),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
group('MacOSProject', () {
|
group('MacOSProject', () {
|
||||||
@ -365,7 +464,11 @@ class FakeFlutterProject extends Fake implements FlutterProject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class FakeXcodeProjectInterpreter extends Fake implements XcodeProjectInterpreter {
|
class FakeXcodeProjectInterpreter extends Fake implements XcodeProjectInterpreter {
|
||||||
FakeXcodeProjectInterpreter({this.isInstalled = true, this.version});
|
FakeXcodeProjectInterpreter({
|
||||||
|
this.isInstalled = true,
|
||||||
|
this.version,
|
||||||
|
this.schemes = const <String>['Runner'],
|
||||||
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
final bool isInstalled;
|
final bool isInstalled;
|
||||||
@ -373,9 +476,11 @@ class FakeXcodeProjectInterpreter extends Fake implements XcodeProjectInterprete
|
|||||||
@override
|
@override
|
||||||
final Version? version;
|
final Version? version;
|
||||||
|
|
||||||
|
List<String> schemes;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<XcodeProjectInfo?> getInfo(String projectPath, {String? projectFilename}) async {
|
Future<XcodeProjectInfo?> getInfo(String projectPath, {String? projectFilename}) async {
|
||||||
return XcodeProjectInfo(<String>[], <String>[], <String>['Runner'], BufferLogger.test());
|
return XcodeProjectInfo(<String>[], <String>[], schemes, BufferLogger.test());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user