mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
[reland] Support conditional bundling of assets based on --flavor
(#139834)
Reland of https://github.com/flutter/flutter/pull/132985. Fixes the path to AssetManifest.bin in flavors_test_ios
This commit is contained in:
parent
f5050cf4a4
commit
935775cb74
@ -58,6 +58,7 @@ dependencies:
|
|||||||
source_maps: 0.10.12 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
source_maps: 0.10.12 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||||
source_span: 1.10.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
source_span: 1.10.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||||
stack_trace: 1.11.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
stack_trace: 1.11.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||||
|
standard_message_codec: 0.0.1+4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||||
stream_channel: 2.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
stream_channel: 2.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||||
string_scanner: 1.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
string_scanner: 1.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||||
term_glyph: 1.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
term_glyph: 1.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||||
@ -74,4 +75,4 @@ dependencies:
|
|||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
test_api: 0.6.1
|
test_api: 0.6.1
|
||||||
|
|
||||||
# PUBSPEC CHECKSUM: 29d2
|
# PUBSPEC CHECKSUM: b875
|
||||||
|
@ -2,12 +2,17 @@
|
|||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'dart:io' show File;
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
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/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';
|
||||||
import 'package:path/path.dart' as path;
|
import 'package:path/path.dart' as path;
|
||||||
|
import 'package:standard_message_codec/standard_message_codec.dart';
|
||||||
|
|
||||||
Future<void> main() async {
|
Future<void> main() async {
|
||||||
deviceOperatingSystem = DeviceOperatingSystem.android;
|
deviceOperatingSystem = DeviceOperatingSystem.android;
|
||||||
@ -15,31 +20,20 @@ Future<void> main() async {
|
|||||||
await createFlavorsTest().call();
|
await createFlavorsTest().call();
|
||||||
await createIntegrationTestFlavorsTest().call();
|
await createIntegrationTestFlavorsTest().call();
|
||||||
|
|
||||||
|
final String projectPath = '${flutterDirectory.path}/dev/integration_tests/flavors';
|
||||||
final TaskResult installTestsResult = await inDirectory(
|
final TaskResult installTestsResult = await inDirectory(
|
||||||
'${flutterDirectory.path}/dev/integration_tests/flavors',
|
projectPath,
|
||||||
() async {
|
() async {
|
||||||
await flutter(
|
final List<TaskResult> testResults = <TaskResult>[
|
||||||
'install',
|
await _testInstallDebugPaidFlavor(projectPath),
|
||||||
options: <String>['--debug', '--flavor', 'paid'],
|
await _testInstallBogusFlavor(),
|
||||||
);
|
];
|
||||||
await flutter(
|
|
||||||
'install',
|
|
||||||
options: <String>['--debug', '--flavor', 'paid', '--uninstall-only'],
|
|
||||||
);
|
|
||||||
|
|
||||||
final StringBuffer stderr = StringBuffer();
|
final TaskResult? firstInstallFailure = testResults
|
||||||
await evalFlutter(
|
.firstWhereOrNull((TaskResult element) => element.failed);
|
||||||
'install',
|
|
||||||
canFail: true,
|
|
||||||
stderr: stderr,
|
|
||||||
options: <String>['--flavor', 'bogus'],
|
|
||||||
);
|
|
||||||
|
|
||||||
final String stderrString = stderr.toString();
|
if (firstInstallFailure != null) {
|
||||||
final String expectedApkPath = path.join('build', 'app', 'outputs', 'flutter-apk', 'app-bogus-release.apk');
|
return firstInstallFailure;
|
||||||
if (!stderrString.contains('"$expectedApkPath" does not exist.')) {
|
|
||||||
print(stderrString);
|
|
||||||
return TaskResult.failure('Should not succeed with bogus flavor');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return TaskResult.success(null);
|
return TaskResult.success(null);
|
||||||
@ -49,3 +43,49 @@ Future<void> main() async {
|
|||||||
return installTestsResult;
|
return installTestsResult;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensures installation works. Also tests asset bundling while we are at it.
|
||||||
|
Future<TaskResult> _testInstallDebugPaidFlavor(String projectDir) async {
|
||||||
|
await evalFlutter(
|
||||||
|
'install',
|
||||||
|
options: <String>['--debug', '--flavor', 'paid'],
|
||||||
|
);
|
||||||
|
|
||||||
|
final Uint8List assetManifestFileData = File(
|
||||||
|
path.join(projectDir, 'build', 'app', 'intermediates', 'assets', 'paidDebug', 'flutter_assets', 'AssetManifest.bin'),
|
||||||
|
).readAsBytesSync();
|
||||||
|
|
||||||
|
final Map<Object?, Object?> assetManifest = const StandardMessageCodec()
|
||||||
|
.decodeMessage(ByteData.sublistView(assetManifestFileData)) as Map<Object?, Object?>;
|
||||||
|
|
||||||
|
if (assetManifest.containsKey('assets/free/free.txt')) {
|
||||||
|
return TaskResult.failure('Assets declared with a flavor not equal to the '
|
||||||
|
'argued --flavor value should not be bundled.');
|
||||||
|
}
|
||||||
|
|
||||||
|
await flutter(
|
||||||
|
'install',
|
||||||
|
options: <String>['--debug', '--flavor', 'paid', '--uninstall-only'],
|
||||||
|
);
|
||||||
|
|
||||||
|
return TaskResult.success(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<TaskResult> _testInstallBogusFlavor() async {
|
||||||
|
final StringBuffer stderr = StringBuffer();
|
||||||
|
await evalFlutter(
|
||||||
|
'install',
|
||||||
|
canFail: true,
|
||||||
|
stderr: stderr,
|
||||||
|
options: <String>['--flavor', 'bogus'],
|
||||||
|
);
|
||||||
|
|
||||||
|
final String stderrString = stderr.toString();
|
||||||
|
final String expectedApkPath = path.join('build', 'app', 'outputs', 'flutter-apk', 'app-bogus-release.apk');
|
||||||
|
if (!stderrString.contains('"$expectedApkPath" does not exist.')) {
|
||||||
|
print(stderrString);
|
||||||
|
return TaskResult.failure('Should not succeed with bogus flavor');
|
||||||
|
}
|
||||||
|
|
||||||
|
return TaskResult.success(null);
|
||||||
|
}
|
||||||
|
@ -2,11 +2,17 @@
|
|||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'dart:io';
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
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/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';
|
||||||
|
import 'package:path/path.dart' as path;
|
||||||
|
import 'package:standard_message_codec/standard_message_codec.dart';
|
||||||
|
|
||||||
Future<void> main() async {
|
Future<void> main() async {
|
||||||
deviceOperatingSystem = DeviceOperatingSystem.ios;
|
deviceOperatingSystem = DeviceOperatingSystem.ios;
|
||||||
@ -14,29 +20,20 @@ Future<void> main() async {
|
|||||||
await createFlavorsTest().call();
|
await createFlavorsTest().call();
|
||||||
await createIntegrationTestFlavorsTest().call();
|
await createIntegrationTestFlavorsTest().call();
|
||||||
// test install and uninstall of flavors app
|
// test install and uninstall of flavors app
|
||||||
|
final String projectDir = '${flutterDirectory.path}/dev/integration_tests/flavors';
|
||||||
final TaskResult installTestsResult = await inDirectory(
|
final TaskResult installTestsResult = await inDirectory(
|
||||||
'${flutterDirectory.path}/dev/integration_tests/flavors',
|
projectDir,
|
||||||
() async {
|
() async {
|
||||||
await flutter(
|
final List<TaskResult> testResults = <TaskResult>[
|
||||||
'install',
|
await _testInstallDebugPaidFlavor(projectDir),
|
||||||
options: <String>['--flavor', 'paid'],
|
await _testInstallBogusFlavor(),
|
||||||
);
|
];
|
||||||
await flutter(
|
|
||||||
'install',
|
|
||||||
options: <String>['--flavor', 'paid', '--uninstall-only'],
|
|
||||||
);
|
|
||||||
final StringBuffer stderr = StringBuffer();
|
|
||||||
await evalFlutter(
|
|
||||||
'install',
|
|
||||||
canFail: true,
|
|
||||||
stderr: stderr,
|
|
||||||
options: <String>['--flavor', 'bogus'],
|
|
||||||
);
|
|
||||||
|
|
||||||
final String stderrString = stderr.toString();
|
final TaskResult? firstInstallFailure = testResults
|
||||||
if (!stderrString.contains('The Xcode project defines schemes: free, paid')) {
|
.firstWhereOrNull((TaskResult element) => element.failed);
|
||||||
print(stderrString);
|
|
||||||
return TaskResult.failure('Should not succeed with bogus flavor');
|
if (firstInstallFailure != null) {
|
||||||
|
return firstInstallFailure;
|
||||||
}
|
}
|
||||||
|
|
||||||
return TaskResult.success(null);
|
return TaskResult.success(null);
|
||||||
@ -46,3 +43,56 @@ Future<void> main() async {
|
|||||||
return installTestsResult;
|
return installTestsResult;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<TaskResult> _testInstallDebugPaidFlavor(String projectDir) async {
|
||||||
|
await evalFlutter(
|
||||||
|
'install',
|
||||||
|
options: <String>['--flavor', 'paid'],
|
||||||
|
);
|
||||||
|
final Uint8List assetManifestFileData = File(
|
||||||
|
path.join(
|
||||||
|
projectDir,
|
||||||
|
'build',
|
||||||
|
'ios',
|
||||||
|
'iphoneos',
|
||||||
|
'Paid App.app',
|
||||||
|
'Frameworks',
|
||||||
|
'App.framework',
|
||||||
|
'flutter_assets',
|
||||||
|
'AssetManifest.bin',
|
||||||
|
),
|
||||||
|
).readAsBytesSync();
|
||||||
|
|
||||||
|
final Map<Object?, Object?> assetManifest = const StandardMessageCodec()
|
||||||
|
.decodeMessage(ByteData.sublistView(assetManifestFileData)) as Map<Object?, Object?>;
|
||||||
|
|
||||||
|
if (assetManifest.containsKey('assets/free/free.txt')) {
|
||||||
|
return TaskResult.failure('Assets declared with a flavor not equal to the '
|
||||||
|
'argued --flavor value should not be bundled.');
|
||||||
|
}
|
||||||
|
|
||||||
|
await flutter(
|
||||||
|
'install',
|
||||||
|
options: <String>['--flavor', 'paid', '--uninstall-only'],
|
||||||
|
);
|
||||||
|
|
||||||
|
return TaskResult.success(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<TaskResult> _testInstallBogusFlavor() async {
|
||||||
|
final StringBuffer stderr = StringBuffer();
|
||||||
|
await evalFlutter(
|
||||||
|
'install',
|
||||||
|
canFail: true,
|
||||||
|
stderr: stderr,
|
||||||
|
options: <String>['--flavor', 'bogus'],
|
||||||
|
);
|
||||||
|
|
||||||
|
final String stderrString = stderr.toString();
|
||||||
|
if (!stderrString.contains('The Xcode project defines schemes: free, paid')) {
|
||||||
|
print(stderrString);
|
||||||
|
return TaskResult.failure('Should not succeed with bogus flavor');
|
||||||
|
}
|
||||||
|
|
||||||
|
return TaskResult.success(null);
|
||||||
|
}
|
||||||
|
@ -24,6 +24,7 @@ dependencies:
|
|||||||
web: 0.4.0
|
web: 0.4.0
|
||||||
webkit_inspection_protocol: 1.2.1
|
webkit_inspection_protocol: 1.2.1
|
||||||
xml: 6.5.0
|
xml: 6.5.0
|
||||||
|
standard_message_codec: 0.0.1+4
|
||||||
|
|
||||||
_discoveryapis_commons: 1.0.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
_discoveryapis_commons: 1.0.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||||
async: 2.11.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
async: 2.11.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||||
@ -72,4 +73,4 @@ dev_dependencies:
|
|||||||
watcher: 1.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
watcher: 1.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||||
web_socket_channel: 2.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
web_socket_channel: 2.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
|
||||||
|
|
||||||
# PUBSPEC CHECKSUM: 6040
|
# PUBSPEC CHECKSUM: 70e2
|
||||||
|
1
dev/integration_tests/flavors/assets/common/common.txt
Normal file
1
dev/integration_tests/flavors/assets/common/common.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
this is a test asset not meant for any specific flavor
|
1
dev/integration_tests/flavors/assets/free/free.txt
Normal file
1
dev/integration_tests/flavors/assets/free/free.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
this is a test asset for --flavor free
|
1
dev/integration_tests/flavors/assets/paid/paid.txt
Normal file
1
dev/integration_tests/flavors/assets/paid/paid.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
this is a test asset for --flavor paid
|
1
dev/integration_tests/flavors/assets/premium/premium.txt
Normal file
1
dev/integration_tests/flavors/assets/premium/premium.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
premium
|
@ -75,5 +75,13 @@ dev_dependencies:
|
|||||||
|
|
||||||
flutter:
|
flutter:
|
||||||
uses-material-design: true
|
uses-material-design: true
|
||||||
|
assets:
|
||||||
|
- assets/common/common.txt
|
||||||
|
- path: assets/paid/
|
||||||
|
flavors:
|
||||||
|
- paid
|
||||||
|
- path: assets/free/
|
||||||
|
flavors:
|
||||||
|
- free
|
||||||
|
|
||||||
# PUBSPEC CHECKSUM: 6bd3
|
# PUBSPEC CHECKSUM: 6bd3
|
||||||
|
@ -401,6 +401,7 @@ class Context {
|
|||||||
'-dTargetPlatform=ios',
|
'-dTargetPlatform=ios',
|
||||||
'-dTargetFile=$targetPath',
|
'-dTargetFile=$targetPath',
|
||||||
'-dBuildMode=$buildMode',
|
'-dBuildMode=$buildMode',
|
||||||
|
if (environment['FLAVOR'] != null) '-dFlavor=${environment['FLAVOR']}',
|
||||||
'-dIosArchs=${environment['ARCHS'] ?? ''}',
|
'-dIosArchs=${environment['ARCHS'] ?? ''}',
|
||||||
'-dSdkRoot=${environment['SDKROOT'] ?? ''}',
|
'-dSdkRoot=${environment['SDKROOT'] ?? ''}',
|
||||||
'-dSplitDebugInfo=${environment['SPLIT_DEBUG_INFO'] ?? ''}',
|
'-dSplitDebugInfo=${environment['SPLIT_DEBUG_INFO'] ?? ''}',
|
||||||
|
@ -1057,6 +1057,7 @@ class FlutterPlugin implements Plugin<Project> {
|
|||||||
boolean isAndroidLibraryValue = isBuildingAar || isUsedAsSubproject
|
boolean isAndroidLibraryValue = isBuildingAar || isUsedAsSubproject
|
||||||
|
|
||||||
String variantBuildMode = buildModeFor(variant.buildType)
|
String variantBuildMode = buildModeFor(variant.buildType)
|
||||||
|
String flavorValue = variant.getFlavorName()
|
||||||
String taskName = toCamelCase(["compile", FLUTTER_BUILD_PREFIX, variant.name])
|
String taskName = toCamelCase(["compile", FLUTTER_BUILD_PREFIX, variant.name])
|
||||||
// Be careful when configuring task below, Groovy has bizarre
|
// Be careful when configuring task below, Groovy has bizarre
|
||||||
// scoping rules: writing `verbose isVerbose()` means calling
|
// scoping rules: writing `verbose isVerbose()` means calling
|
||||||
@ -1094,6 +1095,7 @@ class FlutterPlugin implements Plugin<Project> {
|
|||||||
deferredComponents deferredComponentsValue
|
deferredComponents deferredComponentsValue
|
||||||
validateDeferredComponents validateDeferredComponentsValue
|
validateDeferredComponents validateDeferredComponentsValue
|
||||||
isAndroidLibrary isAndroidLibraryValue
|
isAndroidLibrary isAndroidLibraryValue
|
||||||
|
flavor flavorValue
|
||||||
}
|
}
|
||||||
File libJar = project.file("${project.buildDir}/$INTERMEDIATES_DIR/flutter/${variant.name}/libs.jar")
|
File libJar = project.file("${project.buildDir}/$INTERMEDIATES_DIR/flutter/${variant.name}/libs.jar")
|
||||||
Task packFlutterAppAotTask = project.tasks.create(name: "packLibs${FLUTTER_BUILD_PREFIX}${variant.name.capitalize()}", type: Jar) {
|
Task packFlutterAppAotTask = project.tasks.create(name: "packLibs${FLUTTER_BUILD_PREFIX}${variant.name.capitalize()}", type: Jar) {
|
||||||
@ -1380,6 +1382,8 @@ abstract class BaseFlutterTask extends DefaultTask {
|
|||||||
Boolean validateDeferredComponents
|
Boolean validateDeferredComponents
|
||||||
@Optional @Input
|
@Optional @Input
|
||||||
Boolean isAndroidLibrary
|
Boolean isAndroidLibrary
|
||||||
|
@Optional @Input
|
||||||
|
String flavor
|
||||||
|
|
||||||
@OutputFiles
|
@OutputFiles
|
||||||
FileCollection getDependenciesFiles() {
|
FileCollection getDependenciesFiles() {
|
||||||
@ -1460,6 +1464,9 @@ abstract class BaseFlutterTask extends DefaultTask {
|
|||||||
if (codeSizeDirectory != null) {
|
if (codeSizeDirectory != null) {
|
||||||
args "-dCodeSizeDirectory=${codeSizeDirectory}"
|
args "-dCodeSizeDirectory=${codeSizeDirectory}"
|
||||||
}
|
}
|
||||||
|
if (flavor != null) {
|
||||||
|
args "-dFlavor=${flavor}"
|
||||||
|
}
|
||||||
if (extraGenSnapshotOptions != null) {
|
if (extraGenSnapshotOptions != null) {
|
||||||
args "--ExtraGenSnapshotOptions=${extraGenSnapshotOptions}"
|
args "--ExtraGenSnapshotOptions=${extraGenSnapshotOptions}"
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ import 'package:meta/meta.dart';
|
|||||||
import 'package:package_config/package_config.dart';
|
import 'package:package_config/package_config.dart';
|
||||||
import 'package:standard_message_codec/standard_message_codec.dart';
|
import 'package:standard_message_codec/standard_message_codec.dart';
|
||||||
|
|
||||||
|
import 'base/common.dart';
|
||||||
import 'base/context.dart';
|
import 'base/context.dart';
|
||||||
import 'base/deferred_component.dart';
|
import 'base/deferred_component.dart';
|
||||||
import 'base/file_system.dart';
|
import 'base/file_system.dart';
|
||||||
@ -102,6 +103,7 @@ abstract class AssetBundle {
|
|||||||
required String packagesPath,
|
required String packagesPath,
|
||||||
bool deferredComponentsEnabled = false,
|
bool deferredComponentsEnabled = false,
|
||||||
TargetPlatform? targetPlatform,
|
TargetPlatform? targetPlatform,
|
||||||
|
String? flavor,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -216,8 +218,8 @@ class ManifestAssetBundle implements AssetBundle {
|
|||||||
required String packagesPath,
|
required String packagesPath,
|
||||||
bool deferredComponentsEnabled = false,
|
bool deferredComponentsEnabled = false,
|
||||||
TargetPlatform? targetPlatform,
|
TargetPlatform? targetPlatform,
|
||||||
|
String? flavor,
|
||||||
}) async {
|
}) async {
|
||||||
|
|
||||||
if (flutterProject == null) {
|
if (flutterProject == null) {
|
||||||
try {
|
try {
|
||||||
flutterProject = FlutterProject.fromDirectory(_fileSystem.file(manifestPath).parent);
|
flutterProject = FlutterProject.fromDirectory(_fileSystem.file(manifestPath).parent);
|
||||||
@ -268,6 +270,7 @@ class ManifestAssetBundle implements AssetBundle {
|
|||||||
wildcardDirectories,
|
wildcardDirectories,
|
||||||
assetBasePath,
|
assetBasePath,
|
||||||
targetPlatform,
|
targetPlatform,
|
||||||
|
flavor: flavor,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (assetVariants == null) {
|
if (assetVariants == null) {
|
||||||
@ -281,6 +284,7 @@ class ManifestAssetBundle implements AssetBundle {
|
|||||||
assetBasePath,
|
assetBasePath,
|
||||||
wildcardDirectories,
|
wildcardDirectories,
|
||||||
flutterProject.directory,
|
flutterProject.directory,
|
||||||
|
flavor: flavor,
|
||||||
);
|
);
|
||||||
if (!_splitDeferredAssets || !deferredComponentsEnabled) {
|
if (!_splitDeferredAssets || !deferredComponentsEnabled) {
|
||||||
// Include the assets in the regular set of assets if not using deferred
|
// Include the assets in the regular set of assets if not using deferred
|
||||||
@ -621,7 +625,7 @@ class ManifestAssetBundle implements AssetBundle {
|
|||||||
String assetBasePath,
|
String assetBasePath,
|
||||||
List<Uri> wildcardDirectories,
|
List<Uri> wildcardDirectories,
|
||||||
Directory projectDirectory, {
|
Directory projectDirectory, {
|
||||||
List<String> excludeDirs = const <String>[],
|
String? flavor,
|
||||||
}) {
|
}) {
|
||||||
final List<DeferredComponent>? components = flutterManifest.deferredComponents;
|
final List<DeferredComponent>? components = flutterManifest.deferredComponents;
|
||||||
final Map<String, Map<_Asset, List<_Asset>>> deferredComponentsAssetVariants = <String, Map<_Asset, List<_Asset>>>{};
|
final Map<String, Map<_Asset, List<_Asset>>> deferredComponentsAssetVariants = <String, Map<_Asset, List<_Asset>>>{};
|
||||||
@ -629,18 +633,18 @@ class ManifestAssetBundle implements AssetBundle {
|
|||||||
return deferredComponentsAssetVariants;
|
return deferredComponentsAssetVariants;
|
||||||
}
|
}
|
||||||
for (final DeferredComponent component in components) {
|
for (final DeferredComponent component in components) {
|
||||||
deferredComponentsAssetVariants[component.name] = <_Asset, List<_Asset>>{};
|
|
||||||
final _AssetDirectoryCache cache = _AssetDirectoryCache(_fileSystem);
|
final _AssetDirectoryCache cache = _AssetDirectoryCache(_fileSystem);
|
||||||
for (final Uri assetUri in component.assets) {
|
final Map<_Asset, List<_Asset>> componentAssets = <_Asset, List<_Asset>>{};
|
||||||
if (assetUri.path.endsWith('/')) {
|
for (final AssetsEntry assetsEntry in component.assets) {
|
||||||
wildcardDirectories.add(assetUri);
|
if (assetsEntry.uri.path.endsWith('/')) {
|
||||||
|
wildcardDirectories.add(assetsEntry.uri);
|
||||||
_parseAssetsFromFolder(
|
_parseAssetsFromFolder(
|
||||||
packageConfig,
|
packageConfig,
|
||||||
flutterManifest,
|
flutterManifest,
|
||||||
assetBasePath,
|
assetBasePath,
|
||||||
cache,
|
cache,
|
||||||
deferredComponentsAssetVariants[component.name]!,
|
componentAssets,
|
||||||
assetUri,
|
assetsEntry.uri,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
_parseAssetFromFile(
|
_parseAssetFromFile(
|
||||||
@ -648,12 +652,14 @@ class ManifestAssetBundle implements AssetBundle {
|
|||||||
flutterManifest,
|
flutterManifest,
|
||||||
assetBasePath,
|
assetBasePath,
|
||||||
cache,
|
cache,
|
||||||
deferredComponentsAssetVariants[component.name]!,
|
componentAssets,
|
||||||
assetUri,
|
assetsEntry.uri,
|
||||||
excludeDirs: excludeDirs,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentAssets.removeWhere((_Asset asset, List<_Asset> variants) => !asset.matchesFlavor(flavor));
|
||||||
|
deferredComponentsAssetVariants[component.name] = componentAssets;
|
||||||
}
|
}
|
||||||
return deferredComponentsAssetVariants;
|
return deferredComponentsAssetVariants;
|
||||||
}
|
}
|
||||||
@ -800,22 +806,24 @@ class ManifestAssetBundle implements AssetBundle {
|
|||||||
TargetPlatform? targetPlatform, {
|
TargetPlatform? targetPlatform, {
|
||||||
String? packageName,
|
String? packageName,
|
||||||
Package? attributedPackage,
|
Package? attributedPackage,
|
||||||
|
String? flavor,
|
||||||
}) {
|
}) {
|
||||||
final Map<_Asset, List<_Asset>> result = <_Asset, List<_Asset>>{};
|
final Map<_Asset, List<_Asset>> result = <_Asset, List<_Asset>>{};
|
||||||
|
|
||||||
final _AssetDirectoryCache cache = _AssetDirectoryCache(_fileSystem);
|
final _AssetDirectoryCache cache = _AssetDirectoryCache(_fileSystem);
|
||||||
for (final Uri assetUri in flutterManifest.assets) {
|
for (final AssetsEntry assetsEntry in flutterManifest.assets) {
|
||||||
if (assetUri.path.endsWith('/')) {
|
if (assetsEntry.uri.path.endsWith('/')) {
|
||||||
wildcardDirectories.add(assetUri);
|
wildcardDirectories.add(assetsEntry.uri);
|
||||||
_parseAssetsFromFolder(
|
_parseAssetsFromFolder(
|
||||||
packageConfig,
|
packageConfig,
|
||||||
flutterManifest,
|
flutterManifest,
|
||||||
assetBase,
|
assetBase,
|
||||||
cache,
|
cache,
|
||||||
result,
|
result,
|
||||||
assetUri,
|
assetsEntry.uri,
|
||||||
packageName: packageName,
|
packageName: packageName,
|
||||||
attributedPackage: attributedPackage,
|
attributedPackage: attributedPackage,
|
||||||
|
flavors: assetsEntry.flavors,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
_parseAssetFromFile(
|
_parseAssetFromFile(
|
||||||
@ -824,13 +832,24 @@ class ManifestAssetBundle implements AssetBundle {
|
|||||||
assetBase,
|
assetBase,
|
||||||
cache,
|
cache,
|
||||||
result,
|
result,
|
||||||
assetUri,
|
assetsEntry.uri,
|
||||||
packageName: packageName,
|
packageName: packageName,
|
||||||
attributedPackage: attributedPackage,
|
attributedPackage: attributedPackage,
|
||||||
|
flavors: assetsEntry.flavors,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
result.removeWhere((_Asset asset, List<_Asset> variants) {
|
||||||
|
if (!asset.matchesFlavor(flavor)) {
|
||||||
|
_logger.printTrace('Skipping assets entry "${asset.entryUri.path}" since '
|
||||||
|
'its configured flavor(s) did not match the provided flavor (if any).\n'
|
||||||
|
'Configured flavors: ${asset.flavors.join(', ')}\n');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
for (final Uri shaderUri in flutterManifest.shaders) {
|
for (final Uri shaderUri in flutterManifest.shaders) {
|
||||||
_parseAssetFromFile(
|
_parseAssetFromFile(
|
||||||
packageConfig,
|
packageConfig,
|
||||||
@ -878,6 +897,7 @@ class ManifestAssetBundle implements AssetBundle {
|
|||||||
result[baseAsset] = <_Asset>[];
|
result[baseAsset] = <_Asset>[];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -890,6 +910,7 @@ class ManifestAssetBundle implements AssetBundle {
|
|||||||
Uri assetUri, {
|
Uri assetUri, {
|
||||||
String? packageName,
|
String? packageName,
|
||||||
Package? attributedPackage,
|
Package? attributedPackage,
|
||||||
|
List<String>? flavors,
|
||||||
}) {
|
}) {
|
||||||
final String directoryPath = _fileSystem.path.join(
|
final String directoryPath = _fileSystem.path.join(
|
||||||
assetBase, assetUri.toFilePath(windows: _platform.isWindows));
|
assetBase, assetUri.toFilePath(windows: _platform.isWindows));
|
||||||
@ -915,6 +936,8 @@ class ManifestAssetBundle implements AssetBundle {
|
|||||||
uri,
|
uri,
|
||||||
packageName: packageName,
|
packageName: packageName,
|
||||||
attributedPackage: attributedPackage,
|
attributedPackage: attributedPackage,
|
||||||
|
originUri: assetUri,
|
||||||
|
flavors: flavors,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -926,10 +949,11 @@ class ManifestAssetBundle implements AssetBundle {
|
|||||||
_AssetDirectoryCache cache,
|
_AssetDirectoryCache cache,
|
||||||
Map<_Asset, List<_Asset>> result,
|
Map<_Asset, List<_Asset>> result,
|
||||||
Uri assetUri, {
|
Uri assetUri, {
|
||||||
List<String> excludeDirs = const <String>[],
|
Uri? originUri,
|
||||||
String? packageName,
|
String? packageName,
|
||||||
Package? attributedPackage,
|
Package? attributedPackage,
|
||||||
AssetKind assetKind = AssetKind.regular,
|
AssetKind assetKind = AssetKind.regular,
|
||||||
|
List<String>? flavors,
|
||||||
}) {
|
}) {
|
||||||
final _Asset asset = _resolveAsset(
|
final _Asset asset = _resolveAsset(
|
||||||
packageConfig,
|
packageConfig,
|
||||||
@ -938,9 +962,15 @@ class ManifestAssetBundle implements AssetBundle {
|
|||||||
packageName,
|
packageName,
|
||||||
attributedPackage,
|
attributedPackage,
|
||||||
assetKind: assetKind,
|
assetKind: assetKind,
|
||||||
|
originUri: originUri,
|
||||||
|
flavors: flavors,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
_checkForFlavorConflicts(asset, result.keys.toList());
|
||||||
|
|
||||||
final List<_Asset> variants = <_Asset>[];
|
final List<_Asset> variants = <_Asset>[];
|
||||||
final File assetFile = asset.lookupAssetFile(_fileSystem);
|
final File assetFile = asset.lookupAssetFile(_fileSystem);
|
||||||
|
|
||||||
for (final String path in cache.variantsFor(assetFile.path)) {
|
for (final String path in cache.variantsFor(assetFile.path)) {
|
||||||
final String relativePath = _fileSystem.path.relative(path, from: asset.baseDir);
|
final String relativePath = _fileSystem.path.relative(path, from: asset.baseDir);
|
||||||
final Uri relativeUri = _fileSystem.path.toUri(relativePath);
|
final Uri relativeUri = _fileSystem.path.toUri(relativePath);
|
||||||
@ -963,13 +993,83 @@ class ManifestAssetBundle implements AssetBundle {
|
|||||||
result[asset] = variants;
|
result[asset] = variants;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Since it is not clear how overlapping asset declarations should work in the
|
||||||
|
// presence of conditions such as `flavor`, we throw an Error.
|
||||||
|
//
|
||||||
|
// To be more specific, it is not clear if conditions should be combined with
|
||||||
|
// or-logic or and-logic, or if it should depend on the specificity of the
|
||||||
|
// declarations (file versus directory). If you would like examples, consider these:
|
||||||
|
//
|
||||||
|
// ```yaml
|
||||||
|
// # Should assets/free.mp3 always be included since "assets/" has no flavor?
|
||||||
|
// assets:
|
||||||
|
// - assets/
|
||||||
|
// - path: assets/free.mp3
|
||||||
|
// flavor: free
|
||||||
|
//
|
||||||
|
// # Should "assets/paid/pip.mp3" be included for both the "paid" and "free" flavors?
|
||||||
|
// # Or, since "assets/paid/pip.mp3" is more specific than "assets/paid/"", should
|
||||||
|
// # it take precedence over the latter (included only in "free" flavor)?
|
||||||
|
// assets:
|
||||||
|
// - path: assets/paid/
|
||||||
|
// flavor: paid
|
||||||
|
// - path: assets/paid/pip.mp3
|
||||||
|
// flavor: free
|
||||||
|
// - asset
|
||||||
|
// ```
|
||||||
|
//
|
||||||
|
// Since it is not obvious what logic (if any) would be intuitive and preferable
|
||||||
|
// to the vast majority of users (if any), we play it safe by throwing a `ToolExit`
|
||||||
|
// in any of these situations. We can always loosen up this restriction later
|
||||||
|
// without breaking anyone.
|
||||||
|
void _checkForFlavorConflicts(_Asset newAsset, List<_Asset> previouslyParsedAssets) {
|
||||||
|
bool cameFromDirectoryEntry(_Asset asset) {
|
||||||
|
return asset.originUri.path.endsWith('/');
|
||||||
|
}
|
||||||
|
|
||||||
|
String flavorErrorInfo(_Asset asset) {
|
||||||
|
if (asset.flavors.isEmpty) {
|
||||||
|
return 'An entry with the path "${asset.originUri}" does not specify any flavors.';
|
||||||
|
}
|
||||||
|
|
||||||
|
final Iterable<String> flavorsWrappedWithQuotes = asset.flavors.map((String e) => '"$e"');
|
||||||
|
return 'An entry with the path "${asset.originUri}" specifies the flavor(s): '
|
||||||
|
'${flavorsWrappedWithQuotes.join(', ')}.';
|
||||||
|
}
|
||||||
|
|
||||||
|
final _Asset? preExistingAsset = previouslyParsedAssets
|
||||||
|
.where((_Asset other) => other.entryUri == newAsset.entryUri)
|
||||||
|
.firstOrNull;
|
||||||
|
|
||||||
|
if (preExistingAsset == null || preExistingAsset.hasEquivalentFlavorsWith(newAsset)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final StringBuffer errorMessage = StringBuffer(
|
||||||
|
'Multiple assets entries include the file '
|
||||||
|
'"${newAsset.entryUri.path}", but they specify different lists of flavors.\n');
|
||||||
|
|
||||||
|
errorMessage.writeln(flavorErrorInfo(preExistingAsset));
|
||||||
|
errorMessage.writeln(flavorErrorInfo(newAsset));
|
||||||
|
|
||||||
|
if (cameFromDirectoryEntry(newAsset)|| cameFromDirectoryEntry(preExistingAsset)) {
|
||||||
|
errorMessage.writeln();
|
||||||
|
errorMessage.write('Consider organizing assets with different flavors '
|
||||||
|
'into different directories.');
|
||||||
|
}
|
||||||
|
|
||||||
|
throwToolExit(errorMessage.toString());
|
||||||
|
}
|
||||||
|
|
||||||
_Asset _resolveAsset(
|
_Asset _resolveAsset(
|
||||||
PackageConfig packageConfig,
|
PackageConfig packageConfig,
|
||||||
String assetsBaseDir,
|
String assetsBaseDir,
|
||||||
Uri assetUri,
|
Uri assetUri,
|
||||||
String? packageName,
|
String? packageName,
|
||||||
Package? attributedPackage, {
|
Package? attributedPackage, {
|
||||||
|
Uri? originUri,
|
||||||
AssetKind assetKind = AssetKind.regular,
|
AssetKind assetKind = AssetKind.regular,
|
||||||
|
List<String>? flavors,
|
||||||
}) {
|
}) {
|
||||||
final String assetPath = _fileSystem.path.fromUri(assetUri);
|
final String assetPath = _fileSystem.path.fromUri(assetUri);
|
||||||
if (assetUri.pathSegments.first == 'packages'
|
if (assetUri.pathSegments.first == 'packages'
|
||||||
@ -981,6 +1081,8 @@ class ManifestAssetBundle implements AssetBundle {
|
|||||||
packageConfig,
|
packageConfig,
|
||||||
attributedPackage,
|
attributedPackage,
|
||||||
assetKind: assetKind,
|
assetKind: assetKind,
|
||||||
|
originUri: originUri,
|
||||||
|
flavors: flavors,
|
||||||
);
|
);
|
||||||
if (packageAsset != null) {
|
if (packageAsset != null) {
|
||||||
return packageAsset;
|
return packageAsset;
|
||||||
@ -994,7 +1096,9 @@ class ManifestAssetBundle implements AssetBundle {
|
|||||||
: Uri(pathSegments: <String>['packages', packageName, ...assetUri.pathSegments]), // Asset from, and declared in $packageName.
|
: Uri(pathSegments: <String>['packages', packageName, ...assetUri.pathSegments]), // Asset from, and declared in $packageName.
|
||||||
relativeUri: assetUri,
|
relativeUri: assetUri,
|
||||||
package: attributedPackage,
|
package: attributedPackage,
|
||||||
|
originUri: originUri,
|
||||||
assetKind: assetKind,
|
assetKind: assetKind,
|
||||||
|
flavors: flavors,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1003,6 +1107,8 @@ class ManifestAssetBundle implements AssetBundle {
|
|||||||
PackageConfig packageConfig,
|
PackageConfig packageConfig,
|
||||||
Package? attributedPackage, {
|
Package? attributedPackage, {
|
||||||
AssetKind assetKind = AssetKind.regular,
|
AssetKind assetKind = AssetKind.regular,
|
||||||
|
Uri? originUri,
|
||||||
|
List<String>? flavors,
|
||||||
}) {
|
}) {
|
||||||
assert(assetUri.pathSegments.first == 'packages');
|
assert(assetUri.pathSegments.first == 'packages');
|
||||||
if (assetUri.pathSegments.length > 1) {
|
if (assetUri.pathSegments.length > 1) {
|
||||||
@ -1016,6 +1122,8 @@ class ManifestAssetBundle implements AssetBundle {
|
|||||||
relativeUri: Uri(pathSegments: assetUri.pathSegments.sublist(2)),
|
relativeUri: Uri(pathSegments: assetUri.pathSegments.sublist(2)),
|
||||||
package: attributedPackage,
|
package: attributedPackage,
|
||||||
assetKind: assetKind,
|
assetKind: assetKind,
|
||||||
|
originUri: originUri,
|
||||||
|
flavors: flavors,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1032,16 +1140,22 @@ class ManifestAssetBundle implements AssetBundle {
|
|||||||
class _Asset {
|
class _Asset {
|
||||||
const _Asset({
|
const _Asset({
|
||||||
required this.baseDir,
|
required this.baseDir,
|
||||||
|
Uri? originUri,
|
||||||
required this.relativeUri,
|
required this.relativeUri,
|
||||||
required this.entryUri,
|
required this.entryUri,
|
||||||
required this.package,
|
required this.package,
|
||||||
this.assetKind = AssetKind.regular,
|
this.assetKind = AssetKind.regular,
|
||||||
});
|
List<String>? flavors,
|
||||||
|
}): originUri = originUri ?? entryUri, flavors = flavors ?? const <String>[];
|
||||||
|
|
||||||
final String baseDir;
|
final String baseDir;
|
||||||
|
|
||||||
final Package? package;
|
final Package? package;
|
||||||
|
|
||||||
|
/// The platform-independent URL provided by the user in the pubspec that this
|
||||||
|
/// asset was found from.
|
||||||
|
final Uri originUri;
|
||||||
|
|
||||||
/// A platform-independent URL where this asset can be found on disk on the
|
/// A platform-independent URL where this asset can be found on disk on the
|
||||||
/// host system relative to [baseDir].
|
/// host system relative to [baseDir].
|
||||||
final Uri relativeUri;
|
final Uri relativeUri;
|
||||||
@ -1051,6 +1165,8 @@ class _Asset {
|
|||||||
|
|
||||||
final AssetKind assetKind;
|
final AssetKind assetKind;
|
||||||
|
|
||||||
|
final List<String> flavors;
|
||||||
|
|
||||||
File lookupAssetFile(FileSystem fileSystem) {
|
File lookupAssetFile(FileSystem fileSystem) {
|
||||||
return fileSystem.file(fileSystem.path.join(baseDir, fileSystem.path.fromUri(relativeUri)));
|
return fileSystem.file(fileSystem.path.join(baseDir, fileSystem.path.fromUri(relativeUri)));
|
||||||
}
|
}
|
||||||
@ -1065,6 +1181,26 @@ class _Asset {
|
|||||||
return index == -1 ? null : Uri(path: entryUri.path.substring(0, index));
|
return index == -1 ? null : Uri(path: entryUri.path.substring(0, index));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool matchesFlavor(String? flavor) {
|
||||||
|
if (flavors.isEmpty) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flavor == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return flavors.contains(flavor);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasEquivalentFlavorsWith(_Asset other) {
|
||||||
|
final Set<String> assetFlavors = flavors.toSet();
|
||||||
|
final Set<String> otherFlavors = other.flavors.toSet();
|
||||||
|
return assetFlavors.length == otherFlavors.length && assetFlavors.every(
|
||||||
|
(String e) => otherFlavors.contains(e),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => 'asset: $entryUri';
|
String toString() => 'asset: $entryUri';
|
||||||
|
|
||||||
@ -1080,11 +1216,18 @@ class _Asset {
|
|||||||
&& other.baseDir == baseDir
|
&& other.baseDir == baseDir
|
||||||
&& other.relativeUri == relativeUri
|
&& other.relativeUri == relativeUri
|
||||||
&& other.entryUri == entryUri
|
&& other.entryUri == entryUri
|
||||||
&& other.assetKind == assetKind;
|
&& other.assetKind == assetKind
|
||||||
|
&& hasEquivalentFlavorsWith(other);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode => Object.hash(baseDir, relativeUri, entryUri.hashCode);
|
int get hashCode => Object.hashAll(<Object>[
|
||||||
|
baseDir,
|
||||||
|
relativeUri,
|
||||||
|
entryUri,
|
||||||
|
assetKind,
|
||||||
|
...flavors,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Given an assets directory like this:
|
// Given an assets directory like this:
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
import '../base/file_system.dart';
|
import '../base/file_system.dart';
|
||||||
import '../base/logger.dart';
|
import '../base/logger.dart';
|
||||||
import '../convert.dart';
|
import '../convert.dart';
|
||||||
|
import '../flutter_manifest.dart';
|
||||||
|
|
||||||
/// Represents a configured deferred component as defined in
|
/// Represents a configured deferred component as defined in
|
||||||
/// the app's pubspec.yaml.
|
/// the app's pubspec.yaml.
|
||||||
@ -12,7 +13,7 @@ class DeferredComponent {
|
|||||||
DeferredComponent({
|
DeferredComponent({
|
||||||
required this.name,
|
required this.name,
|
||||||
this.libraries = const <String>[],
|
this.libraries = const <String>[],
|
||||||
this.assets = const <Uri>[],
|
this.assets = const <AssetsEntry>[],
|
||||||
}) : _assigned = false;
|
}) : _assigned = false;
|
||||||
|
|
||||||
/// The name of the deferred component. There should be a matching
|
/// The name of the deferred component. There should be a matching
|
||||||
@ -28,8 +29,8 @@ class DeferredComponent {
|
|||||||
/// libraries that are not listed here.
|
/// libraries that are not listed here.
|
||||||
final List<String> libraries;
|
final List<String> libraries;
|
||||||
|
|
||||||
/// Assets that are part of this component as a Uri relative to the project directory.
|
/// Assets that are part of this component.
|
||||||
final List<Uri> assets;
|
final List<AssetsEntry> assets;
|
||||||
|
|
||||||
/// The minimal set of [LoadingUnit]s needed that contain all of the dart libraries in
|
/// The minimal set of [LoadingUnit]s needed that contain all of the dart libraries in
|
||||||
/// [libraries].
|
/// [libraries].
|
||||||
@ -95,8 +96,11 @@ class DeferredComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
out.write('\n Assets:');
|
out.write('\n Assets:');
|
||||||
for (final Uri asset in assets) {
|
for (final AssetsEntry asset in assets) {
|
||||||
out.write('\n - ${asset.path}');
|
out.write('\n - ${asset.uri.path}');
|
||||||
|
if (asset.flavors.isNotEmpty) {
|
||||||
|
out.write(' (flavors: ${asset.flavors.join(', ')})');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return out.toString();
|
return out.toString();
|
||||||
}
|
}
|
||||||
|
@ -286,6 +286,8 @@ class BuildInfo {
|
|||||||
'PACKAGE_CONFIG': packagesPath,
|
'PACKAGE_CONFIG': packagesPath,
|
||||||
if (codeSizeDirectory != null)
|
if (codeSizeDirectory != null)
|
||||||
'CODE_SIZE_DIRECTORY': codeSizeDirectory!,
|
'CODE_SIZE_DIRECTORY': codeSizeDirectory!,
|
||||||
|
if (flavor != null)
|
||||||
|
'FLAVOR': flavor!,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -989,6 +991,9 @@ const String kBundleSkSLPath = 'BundleSkSLPath';
|
|||||||
/// The define to pass build name
|
/// The define to pass build name
|
||||||
const String kBuildName = 'BuildName';
|
const String kBuildName = 'BuildName';
|
||||||
|
|
||||||
|
/// The app flavor to build.
|
||||||
|
const String kFlavor = 'Flavor';
|
||||||
|
|
||||||
/// The define to pass build number
|
/// The define to pass build number
|
||||||
const String kBuildNumber = 'BuildNumber';
|
const String kBuildNumber = 'BuildNumber';
|
||||||
|
|
||||||
|
@ -46,6 +46,7 @@ abstract class AndroidAssetBundle extends Target {
|
|||||||
if (buildModeEnvironment == null) {
|
if (buildModeEnvironment == null) {
|
||||||
throw MissingDefineException(kBuildMode, name);
|
throw MissingDefineException(kBuildMode, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
final BuildMode buildMode = BuildMode.fromCliName(buildModeEnvironment);
|
final BuildMode buildMode = BuildMode.fromCliName(buildModeEnvironment);
|
||||||
final Directory outputDirectory = environment.outputDir
|
final Directory outputDirectory = environment.outputDir
|
||||||
.childDirectory('flutter_assets')
|
.childDirectory('flutter_assets')
|
||||||
@ -68,6 +69,7 @@ abstract class AndroidAssetBundle extends Target {
|
|||||||
targetPlatform: TargetPlatform.android,
|
targetPlatform: TargetPlatform.android,
|
||||||
buildMode: buildMode,
|
buildMode: buildMode,
|
||||||
shaderTarget: ShaderTarget.impellerAndroid,
|
shaderTarget: ShaderTarget.impellerAndroid,
|
||||||
|
flavor: environment.defines[kFlavor],
|
||||||
);
|
);
|
||||||
environment.depFileService.writeToFile(
|
environment.depFileService.writeToFile(
|
||||||
assetDepfile,
|
assetDepfile,
|
||||||
|
@ -34,6 +34,7 @@ Future<Depfile> copyAssets(
|
|||||||
BuildMode? buildMode,
|
BuildMode? buildMode,
|
||||||
required ShaderTarget shaderTarget,
|
required ShaderTarget shaderTarget,
|
||||||
List<File> additionalInputs = const <File>[],
|
List<File> additionalInputs = const <File>[],
|
||||||
|
String? flavor,
|
||||||
}) async {
|
}) async {
|
||||||
// Check for an SkSL bundle.
|
// Check for an SkSL bundle.
|
||||||
final String? shaderBundlePath = environment.defines[kBundleSkSLPath] ?? environment.inputs[kBundleSkSLPath];
|
final String? shaderBundlePath = environment.defines[kBundleSkSLPath] ?? environment.inputs[kBundleSkSLPath];
|
||||||
@ -58,6 +59,7 @@ Future<Depfile> copyAssets(
|
|||||||
packagesPath: environment.projectDir.childFile('.packages').path,
|
packagesPath: environment.projectDir.childFile('.packages').path,
|
||||||
deferredComponentsEnabled: environment.defines[kDeferredComponents] == 'true',
|
deferredComponentsEnabled: environment.defines[kDeferredComponents] == 'true',
|
||||||
targetPlatform: targetPlatform,
|
targetPlatform: targetPlatform,
|
||||||
|
flavor: flavor,
|
||||||
);
|
);
|
||||||
if (resultCode != 0) {
|
if (resultCode != 0) {
|
||||||
throw Exception('Failed to bundle asset files.');
|
throw Exception('Failed to bundle asset files.');
|
||||||
@ -323,6 +325,7 @@ class CopyAssets extends Target {
|
|||||||
output,
|
output,
|
||||||
targetPlatform: TargetPlatform.android,
|
targetPlatform: TargetPlatform.android,
|
||||||
shaderTarget: ShaderTarget.sksl,
|
shaderTarget: ShaderTarget.sksl,
|
||||||
|
flavor: environment.defines[kFlavor],
|
||||||
);
|
);
|
||||||
environment.depFileService.writeToFile(
|
environment.depFileService.writeToFile(
|
||||||
depfile,
|
depfile,
|
||||||
|
@ -58,6 +58,8 @@ class CopyFlutterBundle extends Target {
|
|||||||
if (buildModeEnvironment == null) {
|
if (buildModeEnvironment == null) {
|
||||||
throw MissingDefineException(kBuildMode, 'copy_flutter_bundle');
|
throw MissingDefineException(kBuildMode, 'copy_flutter_bundle');
|
||||||
}
|
}
|
||||||
|
final String? flavor = environment.defines[kFlavor];
|
||||||
|
|
||||||
final BuildMode buildMode = BuildMode.fromCliName(buildModeEnvironment);
|
final BuildMode buildMode = BuildMode.fromCliName(buildModeEnvironment);
|
||||||
environment.outputDir.createSync(recursive: true);
|
environment.outputDir.createSync(recursive: true);
|
||||||
|
|
||||||
@ -78,6 +80,7 @@ class CopyFlutterBundle extends Target {
|
|||||||
targetPlatform: TargetPlatform.android,
|
targetPlatform: TargetPlatform.android,
|
||||||
buildMode: buildMode,
|
buildMode: buildMode,
|
||||||
shaderTarget: ShaderTarget.sksl,
|
shaderTarget: ShaderTarget.sksl,
|
||||||
|
flavor: flavor,
|
||||||
);
|
);
|
||||||
environment.depFileService.writeToFile(
|
environment.depFileService.writeToFile(
|
||||||
assetDepfile,
|
assetDepfile,
|
||||||
|
@ -533,6 +533,7 @@ abstract class IosAssetBundle extends Target {
|
|||||||
flutterProject.ios.infoPlist,
|
flutterProject.ios.infoPlist,
|
||||||
flutterProject.ios.appFrameworkInfoPlist,
|
flutterProject.ios.appFrameworkInfoPlist,
|
||||||
],
|
],
|
||||||
|
flavor: environment.defines[kFlavor],
|
||||||
);
|
);
|
||||||
environment.depFileService.writeToFile(
|
environment.depFileService.writeToFile(
|
||||||
assetDepfile,
|
assetDepfile,
|
||||||
|
@ -393,6 +393,7 @@ abstract class MacOSBundleFlutterAssets extends Target {
|
|||||||
if (buildModeEnvironment == null) {
|
if (buildModeEnvironment == null) {
|
||||||
throw MissingDefineException(kBuildMode, 'compile_macos_framework');
|
throw MissingDefineException(kBuildMode, 'compile_macos_framework');
|
||||||
}
|
}
|
||||||
|
|
||||||
final BuildMode buildMode = BuildMode.fromCliName(buildModeEnvironment);
|
final BuildMode buildMode = BuildMode.fromCliName(buildModeEnvironment);
|
||||||
final Directory frameworkRootDirectory = environment
|
final Directory frameworkRootDirectory = environment
|
||||||
.outputDir
|
.outputDir
|
||||||
@ -439,6 +440,7 @@ abstract class MacOSBundleFlutterAssets extends Target {
|
|||||||
assetDirectory,
|
assetDirectory,
|
||||||
targetPlatform: TargetPlatform.darwin,
|
targetPlatform: TargetPlatform.darwin,
|
||||||
shaderTarget: ShaderTarget.sksl,
|
shaderTarget: ShaderTarget.sksl,
|
||||||
|
flavor: environment.defines[kFlavor],
|
||||||
);
|
);
|
||||||
environment.depFileService.writeToFile(
|
environment.depFileService.writeToFile(
|
||||||
assetDepfile,
|
assetDepfile,
|
||||||
|
@ -114,6 +114,7 @@ Future<AssetBundle?> buildAssets({
|
|||||||
String? assetDirPath,
|
String? assetDirPath,
|
||||||
String? packagesPath,
|
String? packagesPath,
|
||||||
TargetPlatform? targetPlatform,
|
TargetPlatform? targetPlatform,
|
||||||
|
String? flavor,
|
||||||
}) async {
|
}) async {
|
||||||
assetDirPath ??= getAssetBuildDirectory();
|
assetDirPath ??= getAssetBuildDirectory();
|
||||||
packagesPath ??= globals.fs.path.absolute('.packages');
|
packagesPath ??= globals.fs.path.absolute('.packages');
|
||||||
@ -124,6 +125,7 @@ Future<AssetBundle?> buildAssets({
|
|||||||
manifestPath: manifestPath,
|
manifestPath: manifestPath,
|
||||||
packagesPath: packagesPath,
|
packagesPath: packagesPath,
|
||||||
targetPlatform: targetPlatform,
|
targetPlatform: targetPlatform,
|
||||||
|
flavor: flavor,
|
||||||
);
|
);
|
||||||
if (result != 0) {
|
if (result != 0) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -231,29 +231,12 @@ class FlutterManifest {
|
|||||||
_logger.printError('Expected deferred component manifest to be a map.');
|
_logger.printError('Expected deferred component manifest to be a map.');
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
List<Uri> assetsUri = <Uri>[];
|
|
||||||
final List<Object?>? assets = component['assets'] as List<Object?>?;
|
|
||||||
if (assets == null) {
|
|
||||||
assetsUri = const <Uri>[];
|
|
||||||
} else {
|
|
||||||
for (final Object? asset in assets) {
|
|
||||||
if (asset is! String || asset == '') {
|
|
||||||
_logger.printError('Deferred component asset manifest contains a null or empty uri.');
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
assetsUri.add(Uri.parse(asset));
|
|
||||||
} on FormatException {
|
|
||||||
_logger.printError('Asset manifest contains invalid uri: $asset.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
components.add(
|
components.add(
|
||||||
DeferredComponent(
|
DeferredComponent(
|
||||||
name: component['name'] as String,
|
name: component['name'] as String,
|
||||||
libraries: component['libraries'] == null ?
|
libraries: component['libraries'] == null ?
|
||||||
<String>[] : (component['libraries'] as List<dynamic>).cast<String>(),
|
<String>[] : (component['libraries'] as List<dynamic>).cast<String>(),
|
||||||
assets: assetsUri,
|
assets: _computeAssets(component['assets']),
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -311,26 +294,7 @@ class FlutterManifest {
|
|||||||
: fontList.map<Map<String, Object?>?>(castStringKeyedMap).whereType<Map<String, Object?>>().toList();
|
: fontList.map<Map<String, Object?>?>(castStringKeyedMap).whereType<Map<String, Object?>>().toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
late final List<Uri> assets = _computeAssets();
|
late final List<AssetsEntry> assets = _computeAssets(_flutterDescriptor['assets']);
|
||||||
List<Uri> _computeAssets() {
|
|
||||||
final List<Object?>? assets = _flutterDescriptor['assets'] as List<Object?>?;
|
|
||||||
if (assets == null) {
|
|
||||||
return const <Uri>[];
|
|
||||||
}
|
|
||||||
final List<Uri> results = <Uri>[];
|
|
||||||
for (final Object? asset in assets) {
|
|
||||||
if (asset is! String || asset == '') {
|
|
||||||
_logger.printError('Asset manifest contains a null or empty uri.');
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
results.add(Uri(pathSegments: asset.split('/')));
|
|
||||||
} on FormatException {
|
|
||||||
_logger.printError('Asset manifest contains invalid uri: $asset.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
|
|
||||||
late final List<Font> fonts = _extractFonts();
|
late final List<Font> fonts = _extractFonts();
|
||||||
|
|
||||||
@ -521,15 +485,7 @@ void _validateFlutter(YamlMap? yaml, List<String> errors) {
|
|||||||
errors.add('Expected "$yamlKey" to be a bool, but got $yamlValue (${yamlValue.runtimeType}).');
|
errors.add('Expected "$yamlKey" to be a bool, but got $yamlValue (${yamlValue.runtimeType}).');
|
||||||
}
|
}
|
||||||
case 'assets':
|
case 'assets':
|
||||||
if (yamlValue is! YamlList) {
|
errors.addAll(_validateAssets(yamlValue));
|
||||||
errors.add('Expected "$yamlKey" to be a list, but got $yamlValue (${yamlValue.runtimeType}).');
|
|
||||||
} else if (yamlValue.isEmpty) {
|
|
||||||
break;
|
|
||||||
} else if (yamlValue[0] is! String) {
|
|
||||||
errors.add(
|
|
||||||
'Expected "$yamlKey" to be a list of strings, but the first element is $yamlValue (${yamlValue.runtimeType}).',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
case 'shaders':
|
case 'shaders':
|
||||||
if (yamlValue is! YamlList) {
|
if (yamlValue is! YamlList) {
|
||||||
errors.add('Expected "$yamlKey" to be a list, but got $yamlValue (${yamlValue.runtimeType}).');
|
errors.add('Expected "$yamlKey" to be a list, but got $yamlValue (${yamlValue.runtimeType}).');
|
||||||
@ -640,17 +596,52 @@ void _validateDeferredComponents(MapEntry<Object?, Object?> kvp, List<String> er
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (valueMap.containsKey('assets')) {
|
if (valueMap.containsKey('assets')) {
|
||||||
final Object? assets = valueMap['assets'];
|
errors.addAll(_validateAssets(valueMap['assets']));
|
||||||
if (assets is! YamlList) {
|
|
||||||
errors.add('Expected "assets" to be a list, but got $assets (${assets.runtimeType}).');
|
|
||||||
} else {
|
|
||||||
_validateListType<String>(assets, errors, '"assets" key in the $i element of "${kvp.key}"', 'file paths');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<String> _validateAssets(Object? yaml) {
|
||||||
|
final (_, List<String> errors) = _computeAssetsSafe(yaml);
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(andrewkolos): We end up parsing the assets section twice, once during
|
||||||
|
// validation and once when the assets getter is called. We should consider
|
||||||
|
// refactoring this class to parse and store everything in the constructor.
|
||||||
|
// https://github.com/flutter/flutter/issues/139183
|
||||||
|
(List<AssetsEntry>, List<String> errors) _computeAssetsSafe(Object? yaml) {
|
||||||
|
if (yaml == null) {
|
||||||
|
return (const <AssetsEntry>[], const <String>[]);
|
||||||
|
}
|
||||||
|
if (yaml is! YamlList) {
|
||||||
|
final String error = 'Expected "assets" to be a list, but got $yaml (${yaml.runtimeType}).';
|
||||||
|
return (const <AssetsEntry>[], <String>[error]);
|
||||||
|
}
|
||||||
|
final List<AssetsEntry> results = <AssetsEntry>[];
|
||||||
|
final List<String> errors = <String>[];
|
||||||
|
for (final Object? rawAssetEntry in yaml) {
|
||||||
|
final (AssetsEntry? parsed, String? error) = AssetsEntry.parseFromYamlSafe(rawAssetEntry);
|
||||||
|
if (parsed != null) {
|
||||||
|
results.add(parsed);
|
||||||
|
}
|
||||||
|
if (error != null) {
|
||||||
|
errors.add(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (results, errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<AssetsEntry> _computeAssets(Object? assetsSection) {
|
||||||
|
final (List<AssetsEntry> result, List<String> errors) = _computeAssetsSafe(assetsSection);
|
||||||
|
if (errors.isNotEmpty) {
|
||||||
|
throw Exception('Uncaught error(s) in assets section: '
|
||||||
|
'${errors.join('\n')}');
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
void _validateFonts(YamlList fonts, List<String> errors) {
|
void _validateFonts(YamlList fonts, List<String> errors) {
|
||||||
const Set<int> fontWeights = <int>{
|
const Set<int> fontWeights = <int>{
|
||||||
100, 200, 300, 400, 500, 600, 700, 800, 900,
|
100, 200, 300, 400, 500, 600, 700, 800, 900,
|
||||||
@ -703,3 +694,103 @@ void _validateFonts(YamlList fonts, List<String> errors) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Represents an entry under the `assets` section of a pubspec.
|
||||||
|
@immutable
|
||||||
|
class AssetsEntry {
|
||||||
|
const AssetsEntry({
|
||||||
|
required this.uri,
|
||||||
|
this.flavors = const <String>[],
|
||||||
|
});
|
||||||
|
|
||||||
|
final Uri uri;
|
||||||
|
final List<String> flavors;
|
||||||
|
|
||||||
|
static const String _pathKey = 'path';
|
||||||
|
static const String _flavorKey = 'flavors';
|
||||||
|
|
||||||
|
static AssetsEntry? parseFromYaml(Object? yaml) {
|
||||||
|
final (AssetsEntry? value, String? error) = parseFromYamlSafe(yaml);
|
||||||
|
if (error != null) {
|
||||||
|
throw Exception('Unexpected error when parsing assets entry');
|
||||||
|
}
|
||||||
|
return value!;
|
||||||
|
}
|
||||||
|
|
||||||
|
static (AssetsEntry? assetsEntry, String? error) parseFromYamlSafe(Object? yaml) {
|
||||||
|
|
||||||
|
(Uri?, String?) tryParseUri(String uri) {
|
||||||
|
try {
|
||||||
|
return (Uri(pathSegments: uri.split('/')), null);
|
||||||
|
} on FormatException {
|
||||||
|
return (null, 'Asset manifest contains invalid uri: $uri.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (yaml == null || yaml == '') {
|
||||||
|
return (null, 'Asset manifest contains a null or empty uri.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (yaml is String) {
|
||||||
|
final (Uri? uri, String? error) = tryParseUri(yaml);
|
||||||
|
return uri == null ? (null, error) : (AssetsEntry(uri: uri), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (yaml is Map) {
|
||||||
|
if (yaml.keys.isEmpty) {
|
||||||
|
return (null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
final Object? path = yaml[_pathKey];
|
||||||
|
final Object? flavors = yaml[_flavorKey];
|
||||||
|
|
||||||
|
if (path == null || path is! String) {
|
||||||
|
return (null, 'Asset manifest entry is malformed. '
|
||||||
|
'Expected asset entry to be either a string or a map '
|
||||||
|
'containing a "$_pathKey" entry. Got ${path.runtimeType} instead.');
|
||||||
|
}
|
||||||
|
|
||||||
|
final Uri uri = Uri(pathSegments: path.split('/'));
|
||||||
|
|
||||||
|
if (flavors == null) {
|
||||||
|
return (AssetsEntry(uri: uri), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flavors is! YamlList) {
|
||||||
|
return(null, 'Asset manifest entry is malformed. '
|
||||||
|
'Expected "$_flavorKey" entry to be a list of strings. '
|
||||||
|
'Got ${flavors.runtimeType} instead.');
|
||||||
|
}
|
||||||
|
|
||||||
|
final List<String> flavorsListErrors = <String>[];
|
||||||
|
_validateListType<String>(flavors, flavorsListErrors, 'flavors list of entry "$path"', 'String');
|
||||||
|
if (flavorsListErrors.isNotEmpty) {
|
||||||
|
return (null, 'Asset manifest entry is malformed. '
|
||||||
|
'Expected "$_flavorKey" entry to be a list of strings.\n'
|
||||||
|
'${flavorsListErrors.join('\n')}');
|
||||||
|
}
|
||||||
|
|
||||||
|
final AssetsEntry entry = AssetsEntry(
|
||||||
|
uri: Uri(pathSegments: path.split('/')),
|
||||||
|
flavors: List<String>.from(flavors),
|
||||||
|
);
|
||||||
|
|
||||||
|
return (entry, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (null, 'Assets entry had unexpected shape. '
|
||||||
|
'Expected a string or an object. Got ${yaml.runtimeType} instead.');
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
if (other is! AssetsEntry) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return uri == other.uri && flavors == other.flavors;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(uri.hashCode, flavors.hashCode);
|
||||||
|
}
|
||||||
|
@ -141,6 +141,8 @@ class HotRunner extends ResidentRunner {
|
|||||||
|
|
||||||
NativeAssetsBuildRunner? _buildRunner;
|
NativeAssetsBuildRunner? _buildRunner;
|
||||||
|
|
||||||
|
String? flavor;
|
||||||
|
|
||||||
Future<void> _calculateTargetPlatform() async {
|
Future<void> _calculateTargetPlatform() async {
|
||||||
if (_targetPlatform != null) {
|
if (_targetPlatform != null) {
|
||||||
return;
|
return;
|
||||||
@ -494,7 +496,10 @@ class HotRunner extends ResidentRunner {
|
|||||||
final bool rebuildBundle = assetBundle.needsBuild();
|
final bool rebuildBundle = assetBundle.needsBuild();
|
||||||
if (rebuildBundle) {
|
if (rebuildBundle) {
|
||||||
globals.printTrace('Updating assets');
|
globals.printTrace('Updating assets');
|
||||||
final int result = await assetBundle.build(packagesPath: '.packages');
|
final int result = await assetBundle.build(
|
||||||
|
packagesPath: '.packages',
|
||||||
|
flavor: debuggingOptions.buildInfo.flavor,
|
||||||
|
);
|
||||||
if (result != 0) {
|
if (result != 0) {
|
||||||
return UpdateFSReport();
|
return UpdateFSReport();
|
||||||
}
|
}
|
||||||
|
@ -1567,6 +1567,7 @@ class CapturingAppDomain extends AppDomain {
|
|||||||
bool machine = true,
|
bool machine = true,
|
||||||
String? userIdentifier,
|
String? userIdentifier,
|
||||||
bool enableDevTools = true,
|
bool enableDevTools = true,
|
||||||
|
String? flavor,
|
||||||
}) async {
|
}) async {
|
||||||
this.multidexEnabled = multidexEnabled;
|
this.multidexEnabled = multidexEnabled;
|
||||||
this.userIdentifier = userIdentifier;
|
this.userIdentifier = userIdentifier;
|
||||||
|
@ -9,11 +9,15 @@ import 'package:file/memory.dart';
|
|||||||
import 'package:flutter_tools/src/artifacts.dart';
|
import 'package:flutter_tools/src/artifacts.dart';
|
||||||
import 'package:flutter_tools/src/asset.dart';
|
import 'package:flutter_tools/src/asset.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/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/bundle_builder.dart';
|
import 'package:flutter_tools/src/bundle_builder.dart';
|
||||||
|
import 'package:flutter_tools/src/cache.dart';
|
||||||
import 'package:flutter_tools/src/devfs.dart';
|
import 'package:flutter_tools/src/devfs.dart';
|
||||||
import 'package:flutter_tools/src/globals.dart' as globals;
|
import 'package:flutter_tools/src/globals.dart' as globals;
|
||||||
|
import 'package:flutter_tools/src/project.dart';
|
||||||
import 'package:standard_message_codec/standard_message_codec.dart';
|
import 'package:standard_message_codec/standard_message_codec.dart';
|
||||||
|
|
||||||
import '../src/common.dart';
|
import '../src/common.dart';
|
||||||
@ -23,7 +27,9 @@ const String shaderLibDir = '/./shader_lib';
|
|||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
group('AssetBundle.build', () {
|
group('AssetBundle.build', () {
|
||||||
|
late Logger logger;
|
||||||
late FileSystem testFileSystem;
|
late FileSystem testFileSystem;
|
||||||
|
late Platform platform;
|
||||||
|
|
||||||
setUp(() async {
|
setUp(() async {
|
||||||
testFileSystem = MemoryFileSystem(
|
testFileSystem = MemoryFileSystem(
|
||||||
@ -32,6 +38,8 @@ void main() {
|
|||||||
: FileSystemStyle.posix,
|
: FileSystemStyle.posix,
|
||||||
);
|
);
|
||||||
testFileSystem.currentDirectory = testFileSystem.systemTempDirectory.createTempSync('flutter_asset_bundle_test.');
|
testFileSystem.currentDirectory = testFileSystem.systemTempDirectory.createTempSync('flutter_asset_bundle_test.');
|
||||||
|
logger = BufferLogger.test();
|
||||||
|
platform = FakePlatform(operatingSystem: globals.platform.operatingSystem);
|
||||||
});
|
});
|
||||||
|
|
||||||
testUsingContext('nonempty', () async {
|
testUsingContext('nonempty', () async {
|
||||||
@ -323,6 +331,185 @@ flutter:
|
|||||||
FileSystem: () => testFileSystem,
|
FileSystem: () => testFileSystem,
|
||||||
ProcessManager: () => FakeProcessManager.any(),
|
ProcessManager: () => FakeProcessManager.any(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
group('flavors feature', () {
|
||||||
|
Future<ManifestAssetBundle> buildBundleWithFlavor(String? flavor) async {
|
||||||
|
final ManifestAssetBundle bundle = ManifestAssetBundle(
|
||||||
|
logger: logger,
|
||||||
|
fileSystem: testFileSystem,
|
||||||
|
platform: platform,
|
||||||
|
splitDeferredAssets: true,
|
||||||
|
);
|
||||||
|
|
||||||
|
await bundle.build(
|
||||||
|
packagesPath: '.packages',
|
||||||
|
flutterProject: FlutterProject.fromDirectoryTest(testFileSystem.currentDirectory),
|
||||||
|
flavor: flavor,
|
||||||
|
);
|
||||||
|
return bundle;
|
||||||
|
}
|
||||||
|
|
||||||
|
late final String? previousCacheFlutterRootValue;
|
||||||
|
|
||||||
|
setUpAll(() {
|
||||||
|
previousCacheFlutterRootValue = Cache.flutterRoot;
|
||||||
|
Cache.flutterRoot = Cache.defaultFlutterRoot(platform: platform, fileSystem: testFileSystem, userMessages: UserMessages());
|
||||||
|
});
|
||||||
|
|
||||||
|
tearDownAll(() => Cache.flutterRoot = previousCacheFlutterRootValue);
|
||||||
|
|
||||||
|
testWithoutContext('correctly bundles assets given a simple asset manifest with flavors', () async {
|
||||||
|
testFileSystem.file('.packages').createSync();
|
||||||
|
testFileSystem.file(testFileSystem.path.join('assets', 'common', 'image.png')).createSync(recursive: true);
|
||||||
|
testFileSystem.file(testFileSystem.path.join('assets', 'vanilla', 'ice-cream.png')).createSync(recursive: true);
|
||||||
|
testFileSystem.file(testFileSystem.path.join('assets', 'strawberry', 'ice-cream.png')).createSync(recursive: true);
|
||||||
|
testFileSystem.file(testFileSystem.path.join('assets', 'orange', 'ice-cream.png')).createSync(recursive: true);
|
||||||
|
testFileSystem.file('pubspec.yaml')
|
||||||
|
..createSync()
|
||||||
|
..writeAsStringSync(r'''
|
||||||
|
name: example
|
||||||
|
flutter:
|
||||||
|
assets:
|
||||||
|
- assets/common/
|
||||||
|
- path: assets/vanilla/
|
||||||
|
flavors:
|
||||||
|
- vanilla
|
||||||
|
- path: assets/strawberry/
|
||||||
|
flavors:
|
||||||
|
- strawberry
|
||||||
|
- path: assets/orange/ice-cream.png
|
||||||
|
flavors:
|
||||||
|
- orange
|
||||||
|
''');
|
||||||
|
|
||||||
|
ManifestAssetBundle bundle;
|
||||||
|
bundle = await buildBundleWithFlavor(null);
|
||||||
|
expect(bundle.entries.keys, contains('assets/common/image.png'));
|
||||||
|
expect(bundle.entries.keys, isNot(contains('assets/vanilla/ice-cream.png')));
|
||||||
|
expect(bundle.entries.keys, isNot(contains('assets/strawberry/ice-cream.png')));
|
||||||
|
expect(bundle.entries.keys, isNot(contains('assets/orange/ice-cream.png')));
|
||||||
|
|
||||||
|
bundle = await buildBundleWithFlavor('strawberry');
|
||||||
|
expect(bundle.entries.keys, contains('assets/common/image.png'));
|
||||||
|
expect(bundle.entries.keys, isNot(contains('assets/vanilla/ice-cream.png')));
|
||||||
|
expect(bundle.entries.keys, contains('assets/strawberry/ice-cream.png'));
|
||||||
|
expect(bundle.entries.keys, isNot(contains('assets/orange/ice-cream.png')));
|
||||||
|
|
||||||
|
bundle = await buildBundleWithFlavor('orange');
|
||||||
|
expect(bundle.entries.keys, contains('assets/common/image.png'));
|
||||||
|
expect(bundle.entries.keys, isNot(contains('assets/vanilla/ice-cream.png')));
|
||||||
|
expect(bundle.entries.keys, isNot(contains('assets/strawberry/ice-cream.png')));
|
||||||
|
expect(bundle.entries.keys, contains('assets/orange/ice-cream.png'));
|
||||||
|
});
|
||||||
|
|
||||||
|
testWithoutContext('throws a tool exit when a non-flavored folder contains a flavored asset', () async {
|
||||||
|
testFileSystem.file('.packages').createSync();
|
||||||
|
testFileSystem.file(testFileSystem.path.join('assets', 'unflavored.png')).createSync(recursive: true);
|
||||||
|
testFileSystem.file(testFileSystem.path.join('assets', 'vanillaOrange.png')).createSync(recursive: true);
|
||||||
|
|
||||||
|
testFileSystem.file('pubspec.yaml')
|
||||||
|
..createSync()
|
||||||
|
..writeAsStringSync(r'''
|
||||||
|
name: example
|
||||||
|
flutter:
|
||||||
|
assets:
|
||||||
|
- assets/
|
||||||
|
- path: assets/vanillaOrange.png
|
||||||
|
flavors:
|
||||||
|
- vanilla
|
||||||
|
- orange
|
||||||
|
''');
|
||||||
|
|
||||||
|
expect(
|
||||||
|
buildBundleWithFlavor(null),
|
||||||
|
throwsToolExit(message: 'Multiple assets entries include the file '
|
||||||
|
'"assets/vanillaOrange.png", but they specify different lists of flavors.\n'
|
||||||
|
'An entry with the path "assets/" does not specify any flavors.\n'
|
||||||
|
'An entry with the path "assets/vanillaOrange.png" specifies the flavor(s): "vanilla", "orange".\n\n'
|
||||||
|
'Consider organizing assets with different flavors into different directories.'),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWithoutContext('throws a tool exit when a flavored folder contains a flavorless asset', () async {
|
||||||
|
testFileSystem.file('.packages').createSync();
|
||||||
|
testFileSystem.file(testFileSystem.path.join('vanilla', 'vanilla.png')).createSync(recursive: true);
|
||||||
|
testFileSystem.file(testFileSystem.path.join('vanilla', 'flavorless.png')).createSync(recursive: true);
|
||||||
|
|
||||||
|
testFileSystem.file('pubspec.yaml')
|
||||||
|
..createSync()
|
||||||
|
..writeAsStringSync(r'''
|
||||||
|
name: example
|
||||||
|
flutter:
|
||||||
|
assets:
|
||||||
|
- path: vanilla/
|
||||||
|
flavors:
|
||||||
|
- vanilla
|
||||||
|
- vanilla/flavorless.png
|
||||||
|
''');
|
||||||
|
expect(
|
||||||
|
buildBundleWithFlavor(null),
|
||||||
|
throwsToolExit(message: 'Multiple assets entries include the file '
|
||||||
|
'"vanilla/flavorless.png", but they specify different lists of flavors.\n'
|
||||||
|
'An entry with the path "vanilla/" specifies the flavor(s): "vanilla".\n'
|
||||||
|
'An entry with the path "vanilla/flavorless.png" does not specify any flavors.\n\n'
|
||||||
|
'Consider organizing assets with different flavors into different directories.'),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWithoutContext('tool exits when two file-explicit entries give the same asset different flavors', () {
|
||||||
|
testFileSystem.file('.packages').createSync();
|
||||||
|
testFileSystem.file('orange.png').createSync(recursive: true);
|
||||||
|
testFileSystem.file('pubspec.yaml')
|
||||||
|
..createSync()
|
||||||
|
..writeAsStringSync(r'''
|
||||||
|
name: example
|
||||||
|
flutter:
|
||||||
|
assets:
|
||||||
|
- path: orange.png
|
||||||
|
flavors:
|
||||||
|
- orange
|
||||||
|
- path: orange.png
|
||||||
|
flavors:
|
||||||
|
- mango
|
||||||
|
''');
|
||||||
|
|
||||||
|
expect(
|
||||||
|
buildBundleWithFlavor(null),
|
||||||
|
throwsToolExit(message: 'Multiple assets entries include the file '
|
||||||
|
'"orange.png", but they specify different lists of flavors.\n'
|
||||||
|
'An entry with the path "orange.png" specifies the flavor(s): "orange".\n'
|
||||||
|
'An entry with the path "orange.png" specifies the flavor(s): "mango".'),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWithoutContext('throws ToolExit when flavor from file-level declaration has different flavor from containing folder flavor declaration', () async {
|
||||||
|
testFileSystem.file('.packages').createSync();
|
||||||
|
testFileSystem.file(testFileSystem.path.join('vanilla', 'actually-strawberry.png')).createSync(recursive: true);
|
||||||
|
testFileSystem.file(testFileSystem.path.join('vanilla', 'vanilla.png')).createSync(recursive: true);
|
||||||
|
|
||||||
|
testFileSystem.file('pubspec.yaml')
|
||||||
|
..createSync()
|
||||||
|
..writeAsStringSync(r'''
|
||||||
|
name: example
|
||||||
|
flutter:
|
||||||
|
assets:
|
||||||
|
- path: vanilla/
|
||||||
|
flavors:
|
||||||
|
- vanilla
|
||||||
|
- path: vanilla/actually-strawberry.png
|
||||||
|
flavors:
|
||||||
|
- strawberry
|
||||||
|
''');
|
||||||
|
expect(
|
||||||
|
buildBundleWithFlavor(null),
|
||||||
|
throwsToolExit(message: 'Multiple assets entries include the file '
|
||||||
|
'"vanilla/actually-strawberry.png", but they specify different lists of flavors.\n'
|
||||||
|
'An entry with the path "vanilla/" specifies the flavor(s): "vanilla".\n'
|
||||||
|
'An entry with the path "vanilla/actually-strawberry.png" '
|
||||||
|
'specifies the flavor(s): "strawberry".'),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
group('AssetBundle.build (web builds)', () {
|
group('AssetBundle.build (web builds)', () {
|
||||||
|
@ -6,6 +6,7 @@ import 'package:file/memory.dart';
|
|||||||
import 'package:flutter_tools/src/base/deferred_component.dart';
|
import 'package:flutter_tools/src/base/deferred_component.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/flutter_manifest.dart';
|
||||||
|
|
||||||
import '../../src/common.dart';
|
import '../../src/common.dart';
|
||||||
|
|
||||||
@ -15,18 +16,27 @@ void main() {
|
|||||||
final DeferredComponent component = DeferredComponent(
|
final DeferredComponent component = DeferredComponent(
|
||||||
name: 'bestcomponent',
|
name: 'bestcomponent',
|
||||||
libraries: <String>['lib1', 'lib2'],
|
libraries: <String>['lib1', 'lib2'],
|
||||||
assets: <Uri>[Uri.file('asset1'), Uri.file('asset2')],
|
assets: <AssetsEntry>[
|
||||||
|
AssetsEntry(uri: Uri.file('asset1')),
|
||||||
|
AssetsEntry(uri: Uri.file('asset2')),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
expect(component.name, 'bestcomponent');
|
expect(component.name, 'bestcomponent');
|
||||||
expect(component.libraries, <String>['lib1', 'lib2']);
|
expect(component.libraries, <String>['lib1', 'lib2']);
|
||||||
expect(component.assets, <Uri>[Uri.file('asset1'), Uri.file('asset2')]);
|
expect(component.assets, <AssetsEntry>[
|
||||||
|
AssetsEntry(uri: Uri.file('asset1')),
|
||||||
|
AssetsEntry(uri: Uri.file('asset2')),
|
||||||
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWithoutContext('assignLoadingUnits selects the needed loading units and sets assigned', () {
|
testWithoutContext('assignLoadingUnits selects the needed loading units and sets assigned', () {
|
||||||
final DeferredComponent component = DeferredComponent(
|
final DeferredComponent component = DeferredComponent(
|
||||||
name: 'bestcomponent',
|
name: 'bestcomponent',
|
||||||
libraries: <String>['lib1', 'lib2'],
|
libraries: <String>['lib1', 'lib2'],
|
||||||
assets: <Uri>[Uri.file('asset1'), Uri.file('asset2')],
|
assets: <AssetsEntry>[
|
||||||
|
AssetsEntry(uri: Uri.file('asset1')),
|
||||||
|
AssetsEntry(uri: Uri.file('asset2')),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
expect(component.libraries, <String>['lib1', 'lib2']);
|
expect(component.libraries, <String>['lib1', 'lib2']);
|
||||||
expect(component.assigned, false);
|
expect(component.assigned, false);
|
||||||
@ -94,7 +104,10 @@ void main() {
|
|||||||
final DeferredComponent component = DeferredComponent(
|
final DeferredComponent component = DeferredComponent(
|
||||||
name: 'bestcomponent',
|
name: 'bestcomponent',
|
||||||
libraries: <String>['lib1', 'lib2'],
|
libraries: <String>['lib1', 'lib2'],
|
||||||
assets: <Uri>[Uri.file('asset1'), Uri.file('asset2')],
|
assets: <AssetsEntry>[
|
||||||
|
AssetsEntry(uri: Uri.file('asset1')),
|
||||||
|
AssetsEntry(uri: Uri.file('asset2')),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
expect(component.toString(), '\nDeferredComponent: bestcomponent\n Libraries:\n - lib1\n - lib2\n Assets:\n - asset1\n - asset2');
|
expect(component.toString(), '\nDeferredComponent: bestcomponent\n Libraries:\n - lib1\n - lib2\n Assets:\n - asset1\n - asset2');
|
||||||
});
|
});
|
||||||
@ -103,7 +116,10 @@ void main() {
|
|||||||
final DeferredComponent component = DeferredComponent(
|
final DeferredComponent component = DeferredComponent(
|
||||||
name: 'bestcomponent',
|
name: 'bestcomponent',
|
||||||
libraries: <String>['lib1', 'lib2'],
|
libraries: <String>['lib1', 'lib2'],
|
||||||
assets: <Uri>[Uri.file('asset1'), Uri.file('asset2')],
|
assets: <AssetsEntry>[
|
||||||
|
AssetsEntry(uri: Uri.file('asset1')),
|
||||||
|
AssetsEntry(uri: Uri.file('asset2')),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
component.assignLoadingUnits(<LoadingUnit>[LoadingUnit(id: 2, libraries: <String>['lib1'])]);
|
component.assignLoadingUnits(<LoadingUnit>[LoadingUnit(id: 2, libraries: <String>['lib1'])]);
|
||||||
expect(component.toString(), '\nDeferredComponent: bestcomponent\n Libraries:\n - lib1\n - lib2\n LoadingUnits:\n - 2\n Assets:\n - asset1\n - asset2');
|
expect(component.toString(), '\nDeferredComponent: bestcomponent\n Libraries:\n - lib1\n - lib2\n LoadingUnits:\n - 2\n Assets:\n - asset1\n - asset2');
|
||||||
|
@ -207,7 +207,7 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
testWithoutContext('toEnvironmentConfig encoding of standard values', () {
|
testWithoutContext('toEnvironmentConfig encoding of standard values', () {
|
||||||
const BuildInfo buildInfo = BuildInfo(BuildMode.debug, '',
|
const BuildInfo buildInfo = BuildInfo(BuildMode.debug, 'strawberry',
|
||||||
treeShakeIcons: true,
|
treeShakeIcons: true,
|
||||||
trackWidgetCreation: true,
|
trackWidgetCreation: true,
|
||||||
dartDefines: <String>['foo=2', 'bar=2'],
|
dartDefines: <String>['foo=2', 'bar=2'],
|
||||||
@ -220,7 +220,7 @@ void main() {
|
|||||||
packagesPath: 'foo/.dart_tool/package_config.json',
|
packagesPath: 'foo/.dart_tool/package_config.json',
|
||||||
codeSizeDirectory: 'foo/code-size',
|
codeSizeDirectory: 'foo/code-size',
|
||||||
// These values are ignored by toEnvironmentConfig
|
// These values are ignored by toEnvironmentConfig
|
||||||
androidProjectArgs: <String>['foo=bar', 'fizz=bazz']
|
androidProjectArgs: <String>['foo=bar', 'fizz=bazz'],
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(buildInfo.toEnvironmentConfig(), <String, String>{
|
expect(buildInfo.toEnvironmentConfig(), <String, String>{
|
||||||
@ -235,6 +235,7 @@ void main() {
|
|||||||
'BUNDLE_SKSL_PATH': 'foo/bar/baz.sksl.json',
|
'BUNDLE_SKSL_PATH': 'foo/bar/baz.sksl.json',
|
||||||
'PACKAGE_CONFIG': 'foo/.dart_tool/package_config.json',
|
'PACKAGE_CONFIG': 'foo/.dart_tool/package_config.json',
|
||||||
'CODE_SIZE_DIRECTORY': 'foo/code-size',
|
'CODE_SIZE_DIRECTORY': 'foo/code-size',
|
||||||
|
'FLAVOR': 'strawberry',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -32,6 +32,7 @@ void main() {
|
|||||||
fileSystem: fileSystem,
|
fileSystem: fileSystem,
|
||||||
logger: BufferLogger.test(),
|
logger: BufferLogger.test(),
|
||||||
platform: FakePlatform(),
|
platform: FakePlatform(),
|
||||||
|
defines: <String, String>{},
|
||||||
);
|
);
|
||||||
fileSystem.file(environment.buildDir.childFile('app.dill')).createSync(recursive: true);
|
fileSystem.file(environment.buildDir.childFile('app.dill')).createSync(recursive: true);
|
||||||
fileSystem.file('packages/flutter_tools/lib/src/build_system/targets/assets.dart')
|
fileSystem.file('packages/flutter_tools/lib/src/build_system/targets/assets.dart')
|
||||||
@ -93,6 +94,69 @@ flutter:
|
|||||||
ProcessManager: () => FakeProcessManager.any(),
|
ProcessManager: () => FakeProcessManager.any(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
group("Only copies assets with a flavor if the assets' flavor matches the flavor in the environment", () {
|
||||||
|
testUsingContext('When the environment does not have a flavor defined', () async {
|
||||||
|
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);
|
||||||
|
|
||||||
|
await const CopyAssets().build(environment);
|
||||||
|
|
||||||
|
expect(fileSystem.file('${environment.buildDir.path}/flutter_assets/assets/common/image.png'), exists);
|
||||||
|
expect(fileSystem.file('${environment.buildDir.path}/flutter_assets/assets/vanilla/ice-cream.png'), isNot(exists));
|
||||||
|
expect(fileSystem.file('${environment.buildDir.path}/flutter_assets/assets/strawberry/ice-cream.png'), isNot(exists));
|
||||||
|
}, overrides: <Type, Generator>{
|
||||||
|
FileSystem: () => fileSystem,
|
||||||
|
ProcessManager: () => FakeProcessManager.any(),
|
||||||
|
});
|
||||||
|
|
||||||
|
testUsingContext('When the environment has a flavor defined', () async {
|
||||||
|
environment.defines[kFlavor] = 'strawberry';
|
||||||
|
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);
|
||||||
|
|
||||||
|
await const CopyAssets().build(environment);
|
||||||
|
|
||||||
|
expect(fileSystem.file('${environment.buildDir.path}/flutter_assets/assets/common/image.png'), exists);
|
||||||
|
expect(fileSystem.file('${environment.buildDir.path}/flutter_assets/assets/vanilla/ice-cream.png'), isNot(exists));
|
||||||
|
expect(fileSystem.file('${environment.buildDir.path}/flutter_assets/assets/strawberry/ice-cream.png'), exists);
|
||||||
|
}, overrides: <Type, Generator>{
|
||||||
|
FileSystem: () => fileSystem,
|
||||||
|
ProcessManager: () => FakeProcessManager.any(),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
testUsingContext('Throws exception if pubspec contains missing files', () async {
|
testUsingContext('Throws exception if pubspec contains missing files', () async {
|
||||||
fileSystem.file('pubspec.yaml')
|
fileSystem.file('pubspec.yaml')
|
||||||
..createSync()
|
..createSync()
|
||||||
|
@ -733,7 +733,14 @@ class FakeBundle extends AssetBundle {
|
|||||||
List<File> get additionalDependencies => <File>[];
|
List<File> get additionalDependencies => <File>[];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<int> build({String manifestPath = defaultManifestPath, String? assetDirPath, String? packagesPath, bool deferredComponentsEnabled = false, TargetPlatform? targetPlatform}) async {
|
Future<int> build({
|
||||||
|
String manifestPath = defaultManifestPath,
|
||||||
|
String? assetDirPath,
|
||||||
|
String? packagesPath,
|
||||||
|
bool deferredComponentsEnabled = false,
|
||||||
|
TargetPlatform? targetPlatform,
|
||||||
|
String? flavor,
|
||||||
|
}) async {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,12 +13,17 @@ import 'package:flutter_tools/src/flutter_manifest.dart';
|
|||||||
import '../src/common.dart';
|
import '../src/common.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
|
late BufferLogger logger;
|
||||||
|
|
||||||
setUpAll(() {
|
setUpAll(() {
|
||||||
Cache.flutterRoot = getFlutterRoot();
|
Cache.flutterRoot = getFlutterRoot();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
setUp(() {
|
||||||
|
logger = BufferLogger.test();
|
||||||
|
});
|
||||||
|
|
||||||
testWithoutContext('FlutterManifest is empty when the pubspec.yaml file is empty', () async {
|
testWithoutContext('FlutterManifest is empty when the pubspec.yaml file is empty', () async {
|
||||||
final BufferLogger logger = BufferLogger.test();
|
|
||||||
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
||||||
'',
|
'',
|
||||||
logger: logger,
|
logger: logger,
|
||||||
@ -34,7 +39,6 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
testWithoutContext('FlutterManifest is null when the pubspec.yaml file is not a map', () async {
|
testWithoutContext('FlutterManifest is null when the pubspec.yaml file is not a map', () async {
|
||||||
final BufferLogger logger = BufferLogger.test();
|
|
||||||
expect(FlutterManifest.createFromString(
|
expect(FlutterManifest.createFromString(
|
||||||
'Not a map',
|
'Not a map',
|
||||||
logger: logger,
|
logger: logger,
|
||||||
@ -50,7 +54,6 @@ dependencies:
|
|||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
''';
|
''';
|
||||||
final BufferLogger logger = BufferLogger.test();
|
|
||||||
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
||||||
manifest,
|
manifest,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
@ -74,7 +77,6 @@ dependencies:
|
|||||||
flutter:
|
flutter:
|
||||||
uses-material-design: true
|
uses-material-design: true
|
||||||
''';
|
''';
|
||||||
final BufferLogger logger = BufferLogger.test();
|
|
||||||
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
||||||
manifest,
|
manifest,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
@ -92,7 +94,6 @@ dependencies:
|
|||||||
flutter:
|
flutter:
|
||||||
generate: true
|
generate: true
|
||||||
''';
|
''';
|
||||||
final BufferLogger logger = BufferLogger.test();
|
|
||||||
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
||||||
manifest,
|
manifest,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
@ -110,7 +111,6 @@ dependencies:
|
|||||||
flutter:
|
flutter:
|
||||||
generate: "invalid"
|
generate: "invalid"
|
||||||
''';
|
''';
|
||||||
final BufferLogger logger = BufferLogger.test();
|
|
||||||
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
||||||
manifest,
|
manifest,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
@ -128,7 +128,6 @@ dependencies:
|
|||||||
flutter:
|
flutter:
|
||||||
generate: false
|
generate: false
|
||||||
''';
|
''';
|
||||||
final BufferLogger logger = BufferLogger.test();
|
|
||||||
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
||||||
manifest,
|
manifest,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
@ -149,18 +148,38 @@ flutter:
|
|||||||
- a/foo
|
- a/foo
|
||||||
- a/bar
|
- a/bar
|
||||||
''';
|
''';
|
||||||
final BufferLogger logger = BufferLogger.test();
|
|
||||||
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
||||||
manifest,
|
manifest,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
)!;
|
)!;
|
||||||
|
|
||||||
expect(flutterManifest.assets, <Uri>[
|
expect(flutterManifest.assets, <AssetsEntry>[
|
||||||
Uri.parse('a/foo'),
|
AssetsEntry(uri: Uri.parse('a/foo')),
|
||||||
Uri.parse('a/bar'),
|
AssetsEntry(uri: Uri.parse('a/bar')),
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWithoutContext('FlutterManifest assets entry flavor is not a string', () async {
|
||||||
|
const String manifest = '''
|
||||||
|
name: test
|
||||||
|
dependencies:
|
||||||
|
flutter:
|
||||||
|
sdk: flutter
|
||||||
|
flutter:
|
||||||
|
uses-material-design: true
|
||||||
|
assets:
|
||||||
|
- assets/folder/
|
||||||
|
- path: assets/vanilla/
|
||||||
|
flavors:
|
||||||
|
- key1: value1
|
||||||
|
key2: value2
|
||||||
|
''';
|
||||||
|
FlutterManifest.createFromString(manifest, logger: logger);
|
||||||
|
expect(logger.errorText, contains('Asset manifest entry is malformed. '
|
||||||
|
'Expected "flavors" entry to be a list of strings.'));
|
||||||
|
});
|
||||||
|
|
||||||
testWithoutContext('FlutterManifest has one font family with one asset', () async {
|
testWithoutContext('FlutterManifest has one font family with one asset', () async {
|
||||||
const String manifest = '''
|
const String manifest = '''
|
||||||
name: test
|
name: test
|
||||||
@ -174,7 +193,7 @@ flutter:
|
|||||||
fonts:
|
fonts:
|
||||||
- asset: a/bar
|
- asset: a/bar
|
||||||
''';
|
''';
|
||||||
final BufferLogger logger = BufferLogger.test();
|
|
||||||
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
||||||
manifest,
|
manifest,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
@ -211,7 +230,7 @@ flutter:
|
|||||||
- asset: a/bar
|
- asset: a/bar
|
||||||
weight: 400
|
weight: 400
|
||||||
''';
|
''';
|
||||||
final BufferLogger logger = BufferLogger.test();
|
|
||||||
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
||||||
manifest,
|
manifest,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
@ -251,7 +270,7 @@ flutter:
|
|||||||
weight: 400
|
weight: 400
|
||||||
style: italic
|
style: italic
|
||||||
''';
|
''';
|
||||||
final BufferLogger logger = BufferLogger.test();
|
|
||||||
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
||||||
manifest,
|
manifest,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
@ -297,7 +316,7 @@ flutter:
|
|||||||
asset: a/baz
|
asset: a/baz
|
||||||
style: italic
|
style: italic
|
||||||
''';
|
''';
|
||||||
final BufferLogger logger = BufferLogger.test();
|
|
||||||
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
||||||
manifest,
|
manifest,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
@ -359,7 +378,7 @@ flutter:
|
|||||||
asset: a/baz
|
asset: a/baz
|
||||||
style: italic
|
style: italic
|
||||||
''';
|
''';
|
||||||
final BufferLogger logger = BufferLogger.test();
|
|
||||||
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
||||||
manifest,
|
manifest,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
@ -405,7 +424,7 @@ flutter:
|
|||||||
weight: 400
|
weight: 400
|
||||||
style: italic
|
style: italic
|
||||||
''';
|
''';
|
||||||
final BufferLogger logger = BufferLogger.test();
|
|
||||||
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
||||||
manifest,
|
manifest,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
@ -448,7 +467,7 @@ flutter:
|
|||||||
style: italic
|
style: italic
|
||||||
- family: bar
|
- family: bar
|
||||||
''';
|
''';
|
||||||
final BufferLogger logger = BufferLogger.test();
|
|
||||||
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
||||||
manifest,
|
manifest,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
@ -487,7 +506,7 @@ flutter:
|
|||||||
fonts:
|
fonts:
|
||||||
- weight: 400
|
- weight: 400
|
||||||
''';
|
''';
|
||||||
final BufferLogger logger = BufferLogger.test();
|
|
||||||
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
||||||
manifest,
|
manifest,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
@ -505,7 +524,7 @@ dependencies:
|
|||||||
sdk: flutter
|
sdk: flutter
|
||||||
flutter:
|
flutter:
|
||||||
''';
|
''';
|
||||||
final BufferLogger logger = BufferLogger.test();
|
|
||||||
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
||||||
manifest,
|
manifest,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
@ -526,7 +545,7 @@ flutter:
|
|||||||
androidPackage: com.example
|
androidPackage: com.example
|
||||||
androidX: true
|
androidX: true
|
||||||
''';
|
''';
|
||||||
final BufferLogger logger = BufferLogger.test();
|
|
||||||
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
||||||
manifest,
|
manifest,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
@ -544,7 +563,7 @@ flutter:
|
|||||||
plugin:
|
plugin:
|
||||||
androidPackage: com.example
|
androidPackage: com.example
|
||||||
''';
|
''';
|
||||||
final BufferLogger logger = BufferLogger.test();
|
|
||||||
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
||||||
manifest,
|
manifest,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
@ -565,7 +584,7 @@ flutter:
|
|||||||
package: com.example
|
package: com.example
|
||||||
pluginClass: TestPlugin
|
pluginClass: TestPlugin
|
||||||
''';
|
''';
|
||||||
final BufferLogger logger = BufferLogger.test();
|
|
||||||
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
||||||
manifest,
|
manifest,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
@ -585,7 +604,7 @@ flutter:
|
|||||||
ios:
|
ios:
|
||||||
pluginClass: HelloPlugin
|
pluginClass: HelloPlugin
|
||||||
''';
|
''';
|
||||||
final BufferLogger logger = BufferLogger.test();
|
|
||||||
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
||||||
manifest,
|
manifest,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
@ -601,7 +620,7 @@ name: test
|
|||||||
flutter:
|
flutter:
|
||||||
plugin:
|
plugin:
|
||||||
''';
|
''';
|
||||||
final BufferLogger logger = BufferLogger.test();
|
|
||||||
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
||||||
manifest,
|
manifest,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
@ -621,7 +640,7 @@ dependencies:
|
|||||||
sdk: flutter
|
sdk: flutter
|
||||||
flutter:
|
flutter:
|
||||||
''';
|
''';
|
||||||
final BufferLogger logger = BufferLogger.test();
|
|
||||||
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
||||||
manifest,
|
manifest,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
@ -643,7 +662,7 @@ dependencies:
|
|||||||
sdk: flutter
|
sdk: flutter
|
||||||
flutter:
|
flutter:
|
||||||
''';
|
''';
|
||||||
final BufferLogger logger = BufferLogger.test();
|
|
||||||
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
||||||
manifest,
|
manifest,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
@ -664,7 +683,7 @@ dependencies:
|
|||||||
sdk: flutter
|
sdk: flutter
|
||||||
flutter:
|
flutter:
|
||||||
''';
|
''';
|
||||||
final BufferLogger logger = BufferLogger.test();
|
|
||||||
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
||||||
manifest,
|
manifest,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
@ -686,7 +705,7 @@ dependencies:
|
|||||||
sdk: flutter
|
sdk: flutter
|
||||||
flutter:
|
flutter:
|
||||||
''';
|
''';
|
||||||
final BufferLogger logger = BufferLogger.test();
|
|
||||||
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
||||||
manifest,
|
manifest,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
@ -708,7 +727,7 @@ dependencies:
|
|||||||
sdk: flutter
|
sdk: flutter
|
||||||
flutter:
|
flutter:
|
||||||
''';
|
''';
|
||||||
final BufferLogger logger = BufferLogger.test();
|
|
||||||
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
||||||
manifest,
|
manifest,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
@ -725,7 +744,7 @@ dependencies:
|
|||||||
sdk: flutter
|
sdk: flutter
|
||||||
flutter:
|
flutter:
|
||||||
''';
|
''';
|
||||||
final BufferLogger logger = BufferLogger.test();
|
|
||||||
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
||||||
manifest,
|
manifest,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
@ -747,7 +766,7 @@ flutter:
|
|||||||
fonts:
|
fonts:
|
||||||
-asset: a/bar
|
-asset: a/bar
|
||||||
''';
|
''';
|
||||||
final BufferLogger logger = BufferLogger.test();
|
|
||||||
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
||||||
manifest,
|
manifest,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
@ -767,7 +786,7 @@ dependencies:
|
|||||||
flutter:
|
flutter:
|
||||||
fonts: []
|
fonts: []
|
||||||
''';
|
''';
|
||||||
final BufferLogger logger = BufferLogger.test();
|
|
||||||
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
||||||
manifest,
|
manifest,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
@ -786,7 +805,7 @@ dependencies:
|
|||||||
flutter:
|
flutter:
|
||||||
assets: []
|
assets: []
|
||||||
''';
|
''';
|
||||||
final BufferLogger logger = BufferLogger.test();
|
|
||||||
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
||||||
manifest,
|
manifest,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
@ -809,7 +828,7 @@ flutter:
|
|||||||
fonts:
|
fonts:
|
||||||
- asset
|
- asset
|
||||||
''';
|
''';
|
||||||
final BufferLogger logger = BufferLogger.test();
|
|
||||||
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
||||||
manifest,
|
manifest,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
@ -833,7 +852,7 @@ flutter:
|
|||||||
fonts:
|
fonts:
|
||||||
-asset: a/bar
|
-asset: a/bar
|
||||||
''';
|
''';
|
||||||
final BufferLogger logger = BufferLogger.test();
|
|
||||||
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
||||||
manifest,
|
manifest,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
@ -858,7 +877,7 @@ flutter:
|
|||||||
- asset: a/bar
|
- asset: a/bar
|
||||||
- string
|
- string
|
||||||
''';
|
''';
|
||||||
final BufferLogger logger = BufferLogger.test();
|
|
||||||
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
||||||
manifest,
|
manifest,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
@ -880,15 +899,13 @@ flutter:
|
|||||||
- lib/gallery/example_code.dart
|
- lib/gallery/example_code.dart
|
||||||
-
|
-
|
||||||
''';
|
''';
|
||||||
final BufferLogger logger = BufferLogger.test();
|
|
||||||
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
FlutterManifest.createFromString(
|
||||||
manifest,
|
manifest,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
)!;
|
);
|
||||||
final List<Uri> assets = flutterManifest.assets;
|
|
||||||
|
|
||||||
expect(logger.errorText, contains('Asset manifest contains a null or empty uri.'));
|
expect(logger.errorText, contains('Asset manifest contains a null or empty uri.'));
|
||||||
expect(assets, hasLength(1));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
testWithoutContext('FlutterManifest handles special characters in asset URIs', () {
|
testWithoutContext('FlutterManifest handles special characters in asset URIs', () {
|
||||||
@ -904,18 +921,18 @@ flutter:
|
|||||||
- lib/gallery/abc?xyz
|
- lib/gallery/abc?xyz
|
||||||
- lib/gallery/aaa bbb
|
- lib/gallery/aaa bbb
|
||||||
''';
|
''';
|
||||||
final BufferLogger logger = BufferLogger.test();
|
|
||||||
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
||||||
manifest,
|
manifest,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
)!;
|
)!;
|
||||||
final List<Uri> assets = flutterManifest.assets;
|
final List<AssetsEntry> assets = flutterManifest.assets;
|
||||||
|
|
||||||
expect(assets, hasLength(3));
|
expect(assets, hasLength(3));
|
||||||
expect(assets, <Uri>[
|
expect(assets, <AssetsEntry>[
|
||||||
Uri.parse('lib/gallery/abc%23xyz'),
|
AssetsEntry(uri: Uri.parse('lib/gallery/abc%23xyz')),
|
||||||
Uri.parse('lib/gallery/abc%3Fxyz'),
|
AssetsEntry(uri: Uri.parse('lib/gallery/abc%3Fxyz')),
|
||||||
Uri.parse('lib/gallery/aaa%20bbb'),
|
AssetsEntry(uri: Uri.parse('lib/gallery/aaa%20bbb')),
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -929,7 +946,7 @@ dependencies:
|
|||||||
flutter:
|
flutter:
|
||||||
- uses-material-design: true
|
- uses-material-design: true
|
||||||
''';
|
''';
|
||||||
final BufferLogger logger = BufferLogger.test();
|
|
||||||
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
||||||
manifest,
|
manifest,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
@ -954,7 +971,7 @@ flutter:
|
|||||||
''';
|
''';
|
||||||
final FileSystem fileSystem = MemoryFileSystem.test();
|
final FileSystem fileSystem = MemoryFileSystem.test();
|
||||||
fileSystem.file('pubspec.yaml').writeAsStringSync(manifest);
|
fileSystem.file('pubspec.yaml').writeAsStringSync(manifest);
|
||||||
final BufferLogger logger = BufferLogger.test();
|
|
||||||
final FlutterManifest flutterManifest = FlutterManifest.createFromPath(
|
final FlutterManifest flutterManifest = FlutterManifest.createFromPath(
|
||||||
'pubspec.yaml',
|
'pubspec.yaml',
|
||||||
fileSystem: fileSystem,
|
fileSystem: fileSystem,
|
||||||
@ -975,7 +992,7 @@ flutter:
|
|||||||
|
|
||||||
final FileSystem fileSystem = MemoryFileSystem.test(style: FileSystemStyle.windows);
|
final FileSystem fileSystem = MemoryFileSystem.test(style: FileSystemStyle.windows);
|
||||||
fileSystem.file('pubspec.yaml').writeAsStringSync(manifest);
|
fileSystem.file('pubspec.yaml').writeAsStringSync(manifest);
|
||||||
final BufferLogger logger = BufferLogger.test();
|
|
||||||
final FlutterManifest flutterManifest = FlutterManifest.createFromPath(
|
final FlutterManifest flutterManifest = FlutterManifest.createFromPath(
|
||||||
'pubspec.yaml',
|
'pubspec.yaml',
|
||||||
fileSystem: fileSystem,
|
fileSystem: fileSystem,
|
||||||
@ -992,7 +1009,7 @@ flutter:
|
|||||||
plugin:
|
plugin:
|
||||||
androidPackage: com.example
|
androidPackage: com.example
|
||||||
''';
|
''';
|
||||||
final BufferLogger logger = BufferLogger.test();
|
|
||||||
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
||||||
manifest,
|
manifest,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
@ -1011,7 +1028,7 @@ flutter:
|
|||||||
some_platform:
|
some_platform:
|
||||||
pluginClass: SomeClass
|
pluginClass: SomeClass
|
||||||
''';
|
''';
|
||||||
final BufferLogger logger = BufferLogger.test();
|
|
||||||
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
||||||
manifest,
|
manifest,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
@ -1032,7 +1049,7 @@ flutter:
|
|||||||
ios:
|
ios:
|
||||||
pluginClass: SomeClass
|
pluginClass: SomeClass
|
||||||
''';
|
''';
|
||||||
final BufferLogger logger = BufferLogger.test();
|
|
||||||
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
||||||
manifest,
|
manifest,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
@ -1058,7 +1075,7 @@ flutter:
|
|||||||
ios:
|
ios:
|
||||||
pluginClass: SomeClass
|
pluginClass: SomeClass
|
||||||
''';
|
''';
|
||||||
final BufferLogger logger = BufferLogger.test();
|
|
||||||
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
||||||
manifest,
|
manifest,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
@ -1082,7 +1099,7 @@ flutter:
|
|||||||
platforms:
|
platforms:
|
||||||
- android
|
- android
|
||||||
''';
|
''';
|
||||||
final BufferLogger logger = BufferLogger.test();
|
|
||||||
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
||||||
manifest,
|
manifest,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
@ -1104,7 +1121,7 @@ flutter:
|
|||||||
ios:
|
ios:
|
||||||
pluginClass: SomeClass
|
pluginClass: SomeClass
|
||||||
''';
|
''';
|
||||||
final BufferLogger logger = BufferLogger.test();
|
|
||||||
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
||||||
manifest,
|
manifest,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
@ -1124,7 +1141,7 @@ dependencies:
|
|||||||
flutter:
|
flutter:
|
||||||
licenses: []
|
licenses: []
|
||||||
''';
|
''';
|
||||||
final BufferLogger logger = BufferLogger.test();
|
|
||||||
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
||||||
manifest,
|
manifest,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
@ -1144,7 +1161,7 @@ flutter:
|
|||||||
licenses:
|
licenses:
|
||||||
- foo.txt
|
- foo.txt
|
||||||
''';
|
''';
|
||||||
final BufferLogger logger = BufferLogger.test();
|
|
||||||
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
||||||
manifest,
|
manifest,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
@ -1162,7 +1179,7 @@ dependencies:
|
|||||||
flutter:
|
flutter:
|
||||||
licenses: foo.txt
|
licenses: foo.txt
|
||||||
''';
|
''';
|
||||||
final BufferLogger logger = BufferLogger.test();
|
|
||||||
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
||||||
manifest,
|
manifest,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
@ -1183,7 +1200,7 @@ flutter:
|
|||||||
- foo.txt
|
- foo.txt
|
||||||
- bar: fizz
|
- bar: fizz
|
||||||
''';
|
''';
|
||||||
final BufferLogger logger = BufferLogger.test();
|
|
||||||
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
||||||
manifest,
|
manifest,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
@ -1207,7 +1224,7 @@ flutter:
|
|||||||
assets:
|
assets:
|
||||||
- path/to/asset.jpg
|
- path/to/asset.jpg
|
||||||
''';
|
''';
|
||||||
final BufferLogger logger = BufferLogger.test();
|
|
||||||
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
||||||
manifest,
|
manifest,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
@ -1220,7 +1237,7 @@ flutter:
|
|||||||
expect(deferredComponents[0].libraries.length, 1);
|
expect(deferredComponents[0].libraries.length, 1);
|
||||||
expect(deferredComponents[0].libraries[0], 'lib1');
|
expect(deferredComponents[0].libraries[0], 'lib1');
|
||||||
expect(deferredComponents[0].assets.length, 1);
|
expect(deferredComponents[0].assets.length, 1);
|
||||||
expect(deferredComponents[0].assets[0].path, 'path/to/asset.jpg');
|
expect(deferredComponents[0].assets[0].uri.path, 'path/to/asset.jpg');
|
||||||
});
|
});
|
||||||
|
|
||||||
testWithoutContext('FlutterManifest parses multiple deferred components', () async {
|
testWithoutContext('FlutterManifest parses multiple deferred components', () async {
|
||||||
@ -1243,7 +1260,7 @@ flutter:
|
|||||||
assets:
|
assets:
|
||||||
- path/to/asset2.jpg
|
- path/to/asset2.jpg
|
||||||
''';
|
''';
|
||||||
final BufferLogger logger = BufferLogger.test();
|
|
||||||
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
||||||
manifest,
|
manifest,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
@ -1256,14 +1273,14 @@ flutter:
|
|||||||
expect(deferredComponents[0].libraries.length, 1);
|
expect(deferredComponents[0].libraries.length, 1);
|
||||||
expect(deferredComponents[0].libraries[0], 'lib1');
|
expect(deferredComponents[0].libraries[0], 'lib1');
|
||||||
expect(deferredComponents[0].assets.length, 1);
|
expect(deferredComponents[0].assets.length, 1);
|
||||||
expect(deferredComponents[0].assets[0].path, 'path/to/asset.jpg');
|
expect(deferredComponents[0].assets[0].uri.path, 'path/to/asset.jpg');
|
||||||
|
|
||||||
expect(deferredComponents[1].name, 'component2');
|
expect(deferredComponents[1].name, 'component2');
|
||||||
expect(deferredComponents[1].libraries.length, 2);
|
expect(deferredComponents[1].libraries.length, 2);
|
||||||
expect(deferredComponents[1].libraries[0], 'lib2');
|
expect(deferredComponents[1].libraries[0], 'lib2');
|
||||||
expect(deferredComponents[1].libraries[1], 'lib3');
|
expect(deferredComponents[1].libraries[1], 'lib3');
|
||||||
expect(deferredComponents[1].assets.length, 1);
|
expect(deferredComponents[1].assets.length, 1);
|
||||||
expect(deferredComponents[1].assets[0].path, 'path/to/asset2.jpg');
|
expect(deferredComponents[1].assets[0].uri.path, 'path/to/asset2.jpg');
|
||||||
});
|
});
|
||||||
|
|
||||||
testWithoutContext('FlutterManifest parses empty deferred components', () async {
|
testWithoutContext('FlutterManifest parses empty deferred components', () async {
|
||||||
@ -1275,7 +1292,7 @@ dependencies:
|
|||||||
flutter:
|
flutter:
|
||||||
deferred-components:
|
deferred-components:
|
||||||
''';
|
''';
|
||||||
final BufferLogger logger = BufferLogger.test();
|
|
||||||
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
||||||
manifest,
|
manifest,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
@ -1296,7 +1313,7 @@ flutter:
|
|||||||
- libraries:
|
- libraries:
|
||||||
- lib1
|
- lib1
|
||||||
''';
|
''';
|
||||||
final BufferLogger logger = BufferLogger.test();
|
|
||||||
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
||||||
manifest,
|
manifest,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
@ -1315,7 +1332,7 @@ dependencies:
|
|||||||
flutter:
|
flutter:
|
||||||
deferred-components: blah
|
deferred-components: blah
|
||||||
''';
|
''';
|
||||||
final BufferLogger logger = BufferLogger.test();
|
|
||||||
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
||||||
manifest,
|
manifest,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
@ -1336,7 +1353,7 @@ flutter:
|
|||||||
- name: blah
|
- name: blah
|
||||||
libraries: blah
|
libraries: blah
|
||||||
''';
|
''';
|
||||||
final BufferLogger logger = BufferLogger.test();
|
|
||||||
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
||||||
manifest,
|
manifest,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
@ -1358,7 +1375,7 @@ flutter:
|
|||||||
libraries:
|
libraries:
|
||||||
- not-a-string:
|
- not-a-string:
|
||||||
''';
|
''';
|
||||||
final BufferLogger logger = BufferLogger.test();
|
|
||||||
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
||||||
manifest,
|
manifest,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
@ -1380,14 +1397,14 @@ flutter:
|
|||||||
assets:
|
assets:
|
||||||
- not-a-string:
|
- not-a-string:
|
||||||
''';
|
''';
|
||||||
final BufferLogger logger = BufferLogger.test();
|
|
||||||
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
||||||
manifest,
|
manifest,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(flutterManifest, null);
|
expect(flutterManifest, null);
|
||||||
expect(logger.errorText, 'Expected "assets" key in the 0 element of "deferred-components" to be a list of file paths, but element 0 was a YamlMap\n');
|
expect(logger.errorText, 'Asset manifest entry is malformed. Expected asset entry to be either a string or a map containing a "path" entry. Got Null instead.\n');
|
||||||
});
|
});
|
||||||
|
|
||||||
testWithoutContext('FlutterManifest deferred component multiple assets is string', () async {
|
testWithoutContext('FlutterManifest deferred component multiple assets is string', () async {
|
||||||
@ -1404,14 +1421,14 @@ flutter:
|
|||||||
- also-not-a-string:
|
- also-not-a-string:
|
||||||
- woo
|
- woo
|
||||||
''';
|
''';
|
||||||
final BufferLogger logger = BufferLogger.test();
|
|
||||||
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
||||||
manifest,
|
manifest,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(flutterManifest, null);
|
expect(flutterManifest, null);
|
||||||
expect(logger.errorText, 'Expected "assets" key in the 0 element of "deferred-components" to be a list of file paths, but element 1 was a YamlMap\n');
|
expect(logger.errorText, 'Asset manifest entry is malformed. Expected asset entry to be either a string or a map containing a "path" entry. Got Null instead.\n');
|
||||||
});
|
});
|
||||||
|
|
||||||
testWithoutContext('FlutterManifest multiple deferred components assets is string', () async {
|
testWithoutContext('FlutterManifest multiple deferred components assets is string', () async {
|
||||||
@ -1431,14 +1448,14 @@ flutter:
|
|||||||
- not-a-string:
|
- not-a-string:
|
||||||
- woo
|
- woo
|
||||||
''';
|
''';
|
||||||
final BufferLogger logger = BufferLogger.test();
|
|
||||||
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
||||||
manifest,
|
manifest,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(flutterManifest, null);
|
expect(flutterManifest, null);
|
||||||
expect(logger.errorText, 'Expected "assets" key in the 1 element of "deferred-components" to be a list of file paths, but element 1 was a YamlMap\n');
|
expect(logger.errorText, 'Asset manifest entry is malformed. Expected asset entry to be either a string or a map containing a "path" entry. Got Null instead.\n');
|
||||||
});
|
});
|
||||||
|
|
||||||
testWithoutContext('FlutterManifest deferred component assets is list', () async {
|
testWithoutContext('FlutterManifest deferred component assets is list', () async {
|
||||||
@ -1452,7 +1469,7 @@ flutter:
|
|||||||
- name: blah
|
- name: blah
|
||||||
assets: blah
|
assets: blah
|
||||||
''';
|
''';
|
||||||
final BufferLogger logger = BufferLogger.test();
|
|
||||||
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
||||||
manifest,
|
manifest,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
@ -1476,7 +1493,7 @@ flutter:
|
|||||||
- path/to/asset2.jpg
|
- path/to/asset2.jpg
|
||||||
- path/to/asset3.jpg
|
- path/to/asset3.jpg
|
||||||
''';
|
''';
|
||||||
final BufferLogger logger = BufferLogger.test();
|
|
||||||
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
||||||
manifest,
|
manifest,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
@ -1488,9 +1505,9 @@ flutter:
|
|||||||
expect(deferredComponents[0].name, 'component1');
|
expect(deferredComponents[0].name, 'component1');
|
||||||
expect(deferredComponents[0].libraries.length, 0);
|
expect(deferredComponents[0].libraries.length, 0);
|
||||||
expect(deferredComponents[0].assets.length, 3);
|
expect(deferredComponents[0].assets.length, 3);
|
||||||
expect(deferredComponents[0].assets[0].path, 'path/to/asset1.jpg');
|
expect(deferredComponents[0].assets[0].uri.path, 'path/to/asset1.jpg');
|
||||||
expect(deferredComponents[0].assets[1].path, 'path/to/asset2.jpg');
|
expect(deferredComponents[0].assets[1].uri.path, 'path/to/asset2.jpg');
|
||||||
expect(deferredComponents[0].assets[2].path, 'path/to/asset3.jpg');
|
expect(deferredComponents[0].assets[2].uri.path, 'path/to/asset3.jpg');
|
||||||
});
|
});
|
||||||
|
|
||||||
testWithoutContext('FlutterManifest can parse empty dependencies', () async {
|
testWithoutContext('FlutterManifest can parse empty dependencies', () async {
|
||||||
|
@ -160,6 +160,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,
|
||||||
|
'FLAVOR': 'strawberry',
|
||||||
'SPLIT_DEBUG_INFO': splitDebugInfo,
|
'SPLIT_DEBUG_INFO': splitDebugInfo,
|
||||||
'TRACK_WIDGET_CREATION': trackWidgetCreation,
|
'TRACK_WIDGET_CREATION': trackWidgetCreation,
|
||||||
'TREE_SHAKE_ICONS': treeShake,
|
'TREE_SHAKE_ICONS': treeShake,
|
||||||
@ -174,6 +175,7 @@ void main() {
|
|||||||
'-dTargetPlatform=ios',
|
'-dTargetPlatform=ios',
|
||||||
'-dTargetFile=lib/main.dart',
|
'-dTargetFile=lib/main.dart',
|
||||||
'-dBuildMode=${buildMode.toLowerCase()}',
|
'-dBuildMode=${buildMode.toLowerCase()}',
|
||||||
|
'-dFlavor=strawberry',
|
||||||
'-dIosArchs=$archs',
|
'-dIosArchs=$archs',
|
||||||
'-dSdkRoot=$sdkRoot',
|
'-dSdkRoot=$sdkRoot',
|
||||||
'-dSplitDebugInfo=$splitDebugInfo',
|
'-dSplitDebugInfo=$splitDebugInfo',
|
||||||
|
Loading…
Reference in New Issue
Block a user