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_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"
|
||||
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"
|
||||
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"
|
||||
@ -74,4 +75,4 @@ dependencies:
|
||||
dev_dependencies:
|
||||
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
|
||||
// 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/framework.dart';
|
||||
import 'package:flutter_devicelab/framework/task_result.dart';
|
||||
import 'package:flutter_devicelab/framework/utils.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 {
|
||||
deviceOperatingSystem = DeviceOperatingSystem.android;
|
||||
@ -15,31 +20,20 @@ Future<void> main() async {
|
||||
await createFlavorsTest().call();
|
||||
await createIntegrationTestFlavorsTest().call();
|
||||
|
||||
final String projectPath = '${flutterDirectory.path}/dev/integration_tests/flavors';
|
||||
final TaskResult installTestsResult = await inDirectory(
|
||||
'${flutterDirectory.path}/dev/integration_tests/flavors',
|
||||
projectPath,
|
||||
() async {
|
||||
await flutter(
|
||||
'install',
|
||||
options: <String>['--debug', '--flavor', 'paid'],
|
||||
);
|
||||
await flutter(
|
||||
'install',
|
||||
options: <String>['--debug', '--flavor', 'paid', '--uninstall-only'],
|
||||
);
|
||||
final List<TaskResult> testResults = <TaskResult>[
|
||||
await _testInstallDebugPaidFlavor(projectPath),
|
||||
await _testInstallBogusFlavor(),
|
||||
];
|
||||
|
||||
final StringBuffer stderr = StringBuffer();
|
||||
await evalFlutter(
|
||||
'install',
|
||||
canFail: true,
|
||||
stderr: stderr,
|
||||
options: <String>['--flavor', 'bogus'],
|
||||
);
|
||||
final TaskResult? firstInstallFailure = testResults
|
||||
.firstWhereOrNull((TaskResult element) => element.failed);
|
||||
|
||||
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');
|
||||
if (firstInstallFailure != null) {
|
||||
return firstInstallFailure;
|
||||
}
|
||||
|
||||
return TaskResult.success(null);
|
||||
@ -49,3 +43,49 @@ Future<void> main() async {
|
||||
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
|
||||
// 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/framework.dart';
|
||||
import 'package:flutter_devicelab/framework/task_result.dart';
|
||||
import 'package:flutter_devicelab/framework/utils.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 {
|
||||
deviceOperatingSystem = DeviceOperatingSystem.ios;
|
||||
@ -14,29 +20,20 @@ Future<void> main() async {
|
||||
await createFlavorsTest().call();
|
||||
await createIntegrationTestFlavorsTest().call();
|
||||
// test install and uninstall of flavors app
|
||||
final String projectDir = '${flutterDirectory.path}/dev/integration_tests/flavors';
|
||||
final TaskResult installTestsResult = await inDirectory(
|
||||
'${flutterDirectory.path}/dev/integration_tests/flavors',
|
||||
projectDir,
|
||||
() async {
|
||||
await flutter(
|
||||
'install',
|
||||
options: <String>['--flavor', 'paid'],
|
||||
);
|
||||
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 List<TaskResult> testResults = <TaskResult>[
|
||||
await _testInstallDebugPaidFlavor(projectDir),
|
||||
await _testInstallBogusFlavor(),
|
||||
];
|
||||
|
||||
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');
|
||||
final TaskResult? firstInstallFailure = testResults
|
||||
.firstWhereOrNull((TaskResult element) => element.failed);
|
||||
|
||||
if (firstInstallFailure != null) {
|
||||
return firstInstallFailure;
|
||||
}
|
||||
|
||||
return TaskResult.success(null);
|
||||
@ -46,3 +43,56 @@ Future<void> main() async {
|
||||
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
|
||||
webkit_inspection_protocol: 1.2.1
|
||||
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"
|
||||
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"
|
||||
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:
|
||||
uses-material-design: true
|
||||
assets:
|
||||
- assets/common/common.txt
|
||||
- path: assets/paid/
|
||||
flavors:
|
||||
- paid
|
||||
- path: assets/free/
|
||||
flavors:
|
||||
- free
|
||||
|
||||
# PUBSPEC CHECKSUM: 6bd3
|
||||
|
@ -401,6 +401,7 @@ class Context {
|
||||
'-dTargetPlatform=ios',
|
||||
'-dTargetFile=$targetPath',
|
||||
'-dBuildMode=$buildMode',
|
||||
if (environment['FLAVOR'] != null) '-dFlavor=${environment['FLAVOR']}',
|
||||
'-dIosArchs=${environment['ARCHS'] ?? ''}',
|
||||
'-dSdkRoot=${environment['SDKROOT'] ?? ''}',
|
||||
'-dSplitDebugInfo=${environment['SPLIT_DEBUG_INFO'] ?? ''}',
|
||||
|
@ -1057,6 +1057,7 @@ class FlutterPlugin implements Plugin<Project> {
|
||||
boolean isAndroidLibraryValue = isBuildingAar || isUsedAsSubproject
|
||||
|
||||
String variantBuildMode = buildModeFor(variant.buildType)
|
||||
String flavorValue = variant.getFlavorName()
|
||||
String taskName = toCamelCase(["compile", FLUTTER_BUILD_PREFIX, variant.name])
|
||||
// Be careful when configuring task below, Groovy has bizarre
|
||||
// scoping rules: writing `verbose isVerbose()` means calling
|
||||
@ -1094,6 +1095,7 @@ class FlutterPlugin implements Plugin<Project> {
|
||||
deferredComponents deferredComponentsValue
|
||||
validateDeferredComponents validateDeferredComponentsValue
|
||||
isAndroidLibrary isAndroidLibraryValue
|
||||
flavor flavorValue
|
||||
}
|
||||
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) {
|
||||
@ -1380,6 +1382,8 @@ abstract class BaseFlutterTask extends DefaultTask {
|
||||
Boolean validateDeferredComponents
|
||||
@Optional @Input
|
||||
Boolean isAndroidLibrary
|
||||
@Optional @Input
|
||||
String flavor
|
||||
|
||||
@OutputFiles
|
||||
FileCollection getDependenciesFiles() {
|
||||
@ -1460,6 +1464,9 @@ abstract class BaseFlutterTask extends DefaultTask {
|
||||
if (codeSizeDirectory != null) {
|
||||
args "-dCodeSizeDirectory=${codeSizeDirectory}"
|
||||
}
|
||||
if (flavor != null) {
|
||||
args "-dFlavor=${flavor}"
|
||||
}
|
||||
if (extraGenSnapshotOptions != null) {
|
||||
args "--ExtraGenSnapshotOptions=${extraGenSnapshotOptions}"
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ import 'package:meta/meta.dart';
|
||||
import 'package:package_config/package_config.dart';
|
||||
import 'package:standard_message_codec/standard_message_codec.dart';
|
||||
|
||||
import 'base/common.dart';
|
||||
import 'base/context.dart';
|
||||
import 'base/deferred_component.dart';
|
||||
import 'base/file_system.dart';
|
||||
@ -102,6 +103,7 @@ abstract class AssetBundle {
|
||||
required String packagesPath,
|
||||
bool deferredComponentsEnabled = false,
|
||||
TargetPlatform? targetPlatform,
|
||||
String? flavor,
|
||||
});
|
||||
}
|
||||
|
||||
@ -216,8 +218,8 @@ class ManifestAssetBundle implements AssetBundle {
|
||||
required String packagesPath,
|
||||
bool deferredComponentsEnabled = false,
|
||||
TargetPlatform? targetPlatform,
|
||||
String? flavor,
|
||||
}) async {
|
||||
|
||||
if (flutterProject == null) {
|
||||
try {
|
||||
flutterProject = FlutterProject.fromDirectory(_fileSystem.file(manifestPath).parent);
|
||||
@ -268,6 +270,7 @@ class ManifestAssetBundle implements AssetBundle {
|
||||
wildcardDirectories,
|
||||
assetBasePath,
|
||||
targetPlatform,
|
||||
flavor: flavor,
|
||||
);
|
||||
|
||||
if (assetVariants == null) {
|
||||
@ -281,6 +284,7 @@ class ManifestAssetBundle implements AssetBundle {
|
||||
assetBasePath,
|
||||
wildcardDirectories,
|
||||
flutterProject.directory,
|
||||
flavor: flavor,
|
||||
);
|
||||
if (!_splitDeferredAssets || !deferredComponentsEnabled) {
|
||||
// Include the assets in the regular set of assets if not using deferred
|
||||
@ -621,7 +625,7 @@ class ManifestAssetBundle implements AssetBundle {
|
||||
String assetBasePath,
|
||||
List<Uri> wildcardDirectories,
|
||||
Directory projectDirectory, {
|
||||
List<String> excludeDirs = const <String>[],
|
||||
String? flavor,
|
||||
}) {
|
||||
final List<DeferredComponent>? components = flutterManifest.deferredComponents;
|
||||
final Map<String, Map<_Asset, List<_Asset>>> deferredComponentsAssetVariants = <String, Map<_Asset, List<_Asset>>>{};
|
||||
@ -629,18 +633,18 @@ class ManifestAssetBundle implements AssetBundle {
|
||||
return deferredComponentsAssetVariants;
|
||||
}
|
||||
for (final DeferredComponent component in components) {
|
||||
deferredComponentsAssetVariants[component.name] = <_Asset, List<_Asset>>{};
|
||||
final _AssetDirectoryCache cache = _AssetDirectoryCache(_fileSystem);
|
||||
for (final Uri assetUri in component.assets) {
|
||||
if (assetUri.path.endsWith('/')) {
|
||||
wildcardDirectories.add(assetUri);
|
||||
final Map<_Asset, List<_Asset>> componentAssets = <_Asset, List<_Asset>>{};
|
||||
for (final AssetsEntry assetsEntry in component.assets) {
|
||||
if (assetsEntry.uri.path.endsWith('/')) {
|
||||
wildcardDirectories.add(assetsEntry.uri);
|
||||
_parseAssetsFromFolder(
|
||||
packageConfig,
|
||||
flutterManifest,
|
||||
assetBasePath,
|
||||
cache,
|
||||
deferredComponentsAssetVariants[component.name]!,
|
||||
assetUri,
|
||||
componentAssets,
|
||||
assetsEntry.uri,
|
||||
);
|
||||
} else {
|
||||
_parseAssetFromFile(
|
||||
@ -648,12 +652,14 @@ class ManifestAssetBundle implements AssetBundle {
|
||||
flutterManifest,
|
||||
assetBasePath,
|
||||
cache,
|
||||
deferredComponentsAssetVariants[component.name]!,
|
||||
assetUri,
|
||||
excludeDirs: excludeDirs,
|
||||
componentAssets,
|
||||
assetsEntry.uri,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
componentAssets.removeWhere((_Asset asset, List<_Asset> variants) => !asset.matchesFlavor(flavor));
|
||||
deferredComponentsAssetVariants[component.name] = componentAssets;
|
||||
}
|
||||
return deferredComponentsAssetVariants;
|
||||
}
|
||||
@ -800,22 +806,24 @@ class ManifestAssetBundle implements AssetBundle {
|
||||
TargetPlatform? targetPlatform, {
|
||||
String? packageName,
|
||||
Package? attributedPackage,
|
||||
String? flavor,
|
||||
}) {
|
||||
final Map<_Asset, List<_Asset>> result = <_Asset, List<_Asset>>{};
|
||||
|
||||
final _AssetDirectoryCache cache = _AssetDirectoryCache(_fileSystem);
|
||||
for (final Uri assetUri in flutterManifest.assets) {
|
||||
if (assetUri.path.endsWith('/')) {
|
||||
wildcardDirectories.add(assetUri);
|
||||
for (final AssetsEntry assetsEntry in flutterManifest.assets) {
|
||||
if (assetsEntry.uri.path.endsWith('/')) {
|
||||
wildcardDirectories.add(assetsEntry.uri);
|
||||
_parseAssetsFromFolder(
|
||||
packageConfig,
|
||||
flutterManifest,
|
||||
assetBase,
|
||||
cache,
|
||||
result,
|
||||
assetUri,
|
||||
assetsEntry.uri,
|
||||
packageName: packageName,
|
||||
attributedPackage: attributedPackage,
|
||||
flavors: assetsEntry.flavors,
|
||||
);
|
||||
} else {
|
||||
_parseAssetFromFile(
|
||||
@ -824,13 +832,24 @@ class ManifestAssetBundle implements AssetBundle {
|
||||
assetBase,
|
||||
cache,
|
||||
result,
|
||||
assetUri,
|
||||
assetsEntry.uri,
|
||||
packageName: packageName,
|
||||
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) {
|
||||
_parseAssetFromFile(
|
||||
packageConfig,
|
||||
@ -878,6 +897,7 @@ class ManifestAssetBundle implements AssetBundle {
|
||||
result[baseAsset] = <_Asset>[];
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -890,6 +910,7 @@ class ManifestAssetBundle implements AssetBundle {
|
||||
Uri assetUri, {
|
||||
String? packageName,
|
||||
Package? attributedPackage,
|
||||
List<String>? flavors,
|
||||
}) {
|
||||
final String directoryPath = _fileSystem.path.join(
|
||||
assetBase, assetUri.toFilePath(windows: _platform.isWindows));
|
||||
@ -915,6 +936,8 @@ class ManifestAssetBundle implements AssetBundle {
|
||||
uri,
|
||||
packageName: packageName,
|
||||
attributedPackage: attributedPackage,
|
||||
originUri: assetUri,
|
||||
flavors: flavors,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -926,10 +949,11 @@ class ManifestAssetBundle implements AssetBundle {
|
||||
_AssetDirectoryCache cache,
|
||||
Map<_Asset, List<_Asset>> result,
|
||||
Uri assetUri, {
|
||||
List<String> excludeDirs = const <String>[],
|
||||
Uri? originUri,
|
||||
String? packageName,
|
||||
Package? attributedPackage,
|
||||
AssetKind assetKind = AssetKind.regular,
|
||||
List<String>? flavors,
|
||||
}) {
|
||||
final _Asset asset = _resolveAsset(
|
||||
packageConfig,
|
||||
@ -938,9 +962,15 @@ class ManifestAssetBundle implements AssetBundle {
|
||||
packageName,
|
||||
attributedPackage,
|
||||
assetKind: assetKind,
|
||||
originUri: originUri,
|
||||
flavors: flavors,
|
||||
);
|
||||
|
||||
_checkForFlavorConflicts(asset, result.keys.toList());
|
||||
|
||||
final List<_Asset> variants = <_Asset>[];
|
||||
final File assetFile = asset.lookupAssetFile(_fileSystem);
|
||||
|
||||
for (final String path in cache.variantsFor(assetFile.path)) {
|
||||
final String relativePath = _fileSystem.path.relative(path, from: asset.baseDir);
|
||||
final Uri relativeUri = _fileSystem.path.toUri(relativePath);
|
||||
@ -963,13 +993,83 @@ class ManifestAssetBundle implements AssetBundle {
|
||||
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(
|
||||
PackageConfig packageConfig,
|
||||
String assetsBaseDir,
|
||||
Uri assetUri,
|
||||
String? packageName,
|
||||
Package? attributedPackage, {
|
||||
Uri? originUri,
|
||||
AssetKind assetKind = AssetKind.regular,
|
||||
List<String>? flavors,
|
||||
}) {
|
||||
final String assetPath = _fileSystem.path.fromUri(assetUri);
|
||||
if (assetUri.pathSegments.first == 'packages'
|
||||
@ -981,6 +1081,8 @@ class ManifestAssetBundle implements AssetBundle {
|
||||
packageConfig,
|
||||
attributedPackage,
|
||||
assetKind: assetKind,
|
||||
originUri: originUri,
|
||||
flavors: flavors,
|
||||
);
|
||||
if (packageAsset != null) {
|
||||
return packageAsset;
|
||||
@ -994,7 +1096,9 @@ class ManifestAssetBundle implements AssetBundle {
|
||||
: Uri(pathSegments: <String>['packages', packageName, ...assetUri.pathSegments]), // Asset from, and declared in $packageName.
|
||||
relativeUri: assetUri,
|
||||
package: attributedPackage,
|
||||
originUri: originUri,
|
||||
assetKind: assetKind,
|
||||
flavors: flavors,
|
||||
);
|
||||
}
|
||||
|
||||
@ -1003,6 +1107,8 @@ class ManifestAssetBundle implements AssetBundle {
|
||||
PackageConfig packageConfig,
|
||||
Package? attributedPackage, {
|
||||
AssetKind assetKind = AssetKind.regular,
|
||||
Uri? originUri,
|
||||
List<String>? flavors,
|
||||
}) {
|
||||
assert(assetUri.pathSegments.first == 'packages');
|
||||
if (assetUri.pathSegments.length > 1) {
|
||||
@ -1016,6 +1122,8 @@ class ManifestAssetBundle implements AssetBundle {
|
||||
relativeUri: Uri(pathSegments: assetUri.pathSegments.sublist(2)),
|
||||
package: attributedPackage,
|
||||
assetKind: assetKind,
|
||||
originUri: originUri,
|
||||
flavors: flavors,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1032,16 +1140,22 @@ class ManifestAssetBundle implements AssetBundle {
|
||||
class _Asset {
|
||||
const _Asset({
|
||||
required this.baseDir,
|
||||
Uri? originUri,
|
||||
required this.relativeUri,
|
||||
required this.entryUri,
|
||||
required this.package,
|
||||
this.assetKind = AssetKind.regular,
|
||||
});
|
||||
List<String>? flavors,
|
||||
}): originUri = originUri ?? entryUri, flavors = flavors ?? const <String>[];
|
||||
|
||||
final String baseDir;
|
||||
|
||||
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
|
||||
/// host system relative to [baseDir].
|
||||
final Uri relativeUri;
|
||||
@ -1051,6 +1165,8 @@ class _Asset {
|
||||
|
||||
final AssetKind assetKind;
|
||||
|
||||
final List<String> flavors;
|
||||
|
||||
File lookupAssetFile(FileSystem fileSystem) {
|
||||
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));
|
||||
}
|
||||
|
||||
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
|
||||
String toString() => 'asset: $entryUri';
|
||||
|
||||
@ -1080,11 +1216,18 @@ class _Asset {
|
||||
&& other.baseDir == baseDir
|
||||
&& other.relativeUri == relativeUri
|
||||
&& other.entryUri == entryUri
|
||||
&& other.assetKind == assetKind;
|
||||
&& other.assetKind == assetKind
|
||||
&& hasEquivalentFlavorsWith(other);
|
||||
}
|
||||
|
||||
@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:
|
||||
|
@ -5,6 +5,7 @@
|
||||
import '../base/file_system.dart';
|
||||
import '../base/logger.dart';
|
||||
import '../convert.dart';
|
||||
import '../flutter_manifest.dart';
|
||||
|
||||
/// Represents a configured deferred component as defined in
|
||||
/// the app's pubspec.yaml.
|
||||
@ -12,7 +13,7 @@ class DeferredComponent {
|
||||
DeferredComponent({
|
||||
required this.name,
|
||||
this.libraries = const <String>[],
|
||||
this.assets = const <Uri>[],
|
||||
this.assets = const <AssetsEntry>[],
|
||||
}) : _assigned = false;
|
||||
|
||||
/// The name of the deferred component. There should be a matching
|
||||
@ -28,8 +29,8 @@ class DeferredComponent {
|
||||
/// libraries that are not listed here.
|
||||
final List<String> libraries;
|
||||
|
||||
/// Assets that are part of this component as a Uri relative to the project directory.
|
||||
final List<Uri> assets;
|
||||
/// Assets that are part of this component.
|
||||
final List<AssetsEntry> assets;
|
||||
|
||||
/// The minimal set of [LoadingUnit]s needed that contain all of the dart libraries in
|
||||
/// [libraries].
|
||||
@ -95,8 +96,11 @@ class DeferredComponent {
|
||||
}
|
||||
}
|
||||
out.write('\n Assets:');
|
||||
for (final Uri asset in assets) {
|
||||
out.write('\n - ${asset.path}');
|
||||
for (final AssetsEntry asset in assets) {
|
||||
out.write('\n - ${asset.uri.path}');
|
||||
if (asset.flavors.isNotEmpty) {
|
||||
out.write(' (flavors: ${asset.flavors.join(', ')})');
|
||||
}
|
||||
}
|
||||
return out.toString();
|
||||
}
|
||||
|
@ -286,6 +286,8 @@ class BuildInfo {
|
||||
'PACKAGE_CONFIG': packagesPath,
|
||||
if (codeSizeDirectory != null)
|
||||
'CODE_SIZE_DIRECTORY': codeSizeDirectory!,
|
||||
if (flavor != null)
|
||||
'FLAVOR': flavor!,
|
||||
};
|
||||
}
|
||||
|
||||
@ -989,6 +991,9 @@ const String kBundleSkSLPath = 'BundleSkSLPath';
|
||||
/// The define to pass build name
|
||||
const String kBuildName = 'BuildName';
|
||||
|
||||
/// The app flavor to build.
|
||||
const String kFlavor = 'Flavor';
|
||||
|
||||
/// The define to pass build number
|
||||
const String kBuildNumber = 'BuildNumber';
|
||||
|
||||
|
@ -46,6 +46,7 @@ abstract class AndroidAssetBundle extends Target {
|
||||
if (buildModeEnvironment == null) {
|
||||
throw MissingDefineException(kBuildMode, name);
|
||||
}
|
||||
|
||||
final BuildMode buildMode = BuildMode.fromCliName(buildModeEnvironment);
|
||||
final Directory outputDirectory = environment.outputDir
|
||||
.childDirectory('flutter_assets')
|
||||
@ -68,6 +69,7 @@ abstract class AndroidAssetBundle extends Target {
|
||||
targetPlatform: TargetPlatform.android,
|
||||
buildMode: buildMode,
|
||||
shaderTarget: ShaderTarget.impellerAndroid,
|
||||
flavor: environment.defines[kFlavor],
|
||||
);
|
||||
environment.depFileService.writeToFile(
|
||||
assetDepfile,
|
||||
|
@ -34,6 +34,7 @@ Future<Depfile> copyAssets(
|
||||
BuildMode? buildMode,
|
||||
required ShaderTarget shaderTarget,
|
||||
List<File> additionalInputs = const <File>[],
|
||||
String? flavor,
|
||||
}) async {
|
||||
// Check for an SkSL bundle.
|
||||
final String? shaderBundlePath = environment.defines[kBundleSkSLPath] ?? environment.inputs[kBundleSkSLPath];
|
||||
@ -58,6 +59,7 @@ Future<Depfile> copyAssets(
|
||||
packagesPath: environment.projectDir.childFile('.packages').path,
|
||||
deferredComponentsEnabled: environment.defines[kDeferredComponents] == 'true',
|
||||
targetPlatform: targetPlatform,
|
||||
flavor: flavor,
|
||||
);
|
||||
if (resultCode != 0) {
|
||||
throw Exception('Failed to bundle asset files.');
|
||||
@ -323,6 +325,7 @@ class CopyAssets extends Target {
|
||||
output,
|
||||
targetPlatform: TargetPlatform.android,
|
||||
shaderTarget: ShaderTarget.sksl,
|
||||
flavor: environment.defines[kFlavor],
|
||||
);
|
||||
environment.depFileService.writeToFile(
|
||||
depfile,
|
||||
|
@ -58,6 +58,8 @@ class CopyFlutterBundle extends Target {
|
||||
if (buildModeEnvironment == null) {
|
||||
throw MissingDefineException(kBuildMode, 'copy_flutter_bundle');
|
||||
}
|
||||
final String? flavor = environment.defines[kFlavor];
|
||||
|
||||
final BuildMode buildMode = BuildMode.fromCliName(buildModeEnvironment);
|
||||
environment.outputDir.createSync(recursive: true);
|
||||
|
||||
@ -78,6 +80,7 @@ class CopyFlutterBundle extends Target {
|
||||
targetPlatform: TargetPlatform.android,
|
||||
buildMode: buildMode,
|
||||
shaderTarget: ShaderTarget.sksl,
|
||||
flavor: flavor,
|
||||
);
|
||||
environment.depFileService.writeToFile(
|
||||
assetDepfile,
|
||||
|
@ -533,6 +533,7 @@ abstract class IosAssetBundle extends Target {
|
||||
flutterProject.ios.infoPlist,
|
||||
flutterProject.ios.appFrameworkInfoPlist,
|
||||
],
|
||||
flavor: environment.defines[kFlavor],
|
||||
);
|
||||
environment.depFileService.writeToFile(
|
||||
assetDepfile,
|
||||
|
@ -393,6 +393,7 @@ abstract class MacOSBundleFlutterAssets extends Target {
|
||||
if (buildModeEnvironment == null) {
|
||||
throw MissingDefineException(kBuildMode, 'compile_macos_framework');
|
||||
}
|
||||
|
||||
final BuildMode buildMode = BuildMode.fromCliName(buildModeEnvironment);
|
||||
final Directory frameworkRootDirectory = environment
|
||||
.outputDir
|
||||
@ -439,6 +440,7 @@ abstract class MacOSBundleFlutterAssets extends Target {
|
||||
assetDirectory,
|
||||
targetPlatform: TargetPlatform.darwin,
|
||||
shaderTarget: ShaderTarget.sksl,
|
||||
flavor: environment.defines[kFlavor],
|
||||
);
|
||||
environment.depFileService.writeToFile(
|
||||
assetDepfile,
|
||||
|
@ -114,6 +114,7 @@ Future<AssetBundle?> buildAssets({
|
||||
String? assetDirPath,
|
||||
String? packagesPath,
|
||||
TargetPlatform? targetPlatform,
|
||||
String? flavor,
|
||||
}) async {
|
||||
assetDirPath ??= getAssetBuildDirectory();
|
||||
packagesPath ??= globals.fs.path.absolute('.packages');
|
||||
@ -124,6 +125,7 @@ Future<AssetBundle?> buildAssets({
|
||||
manifestPath: manifestPath,
|
||||
packagesPath: packagesPath,
|
||||
targetPlatform: targetPlatform,
|
||||
flavor: flavor,
|
||||
);
|
||||
if (result != 0) {
|
||||
return null;
|
||||
|
@ -231,29 +231,12 @@ class FlutterManifest {
|
||||
_logger.printError('Expected deferred component manifest to be a map.');
|
||||
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(
|
||||
DeferredComponent(
|
||||
name: component['name'] as String,
|
||||
libraries: component['libraries'] == null ?
|
||||
<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();
|
||||
}
|
||||
|
||||
late final List<Uri> assets = _computeAssets();
|
||||
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<AssetsEntry> assets = _computeAssets(_flutterDescriptor['assets']);
|
||||
|
||||
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}).');
|
||||
}
|
||||
case 'assets':
|
||||
if (yamlValue is! YamlList) {
|
||||
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}).',
|
||||
);
|
||||
}
|
||||
errors.addAll(_validateAssets(yamlValue));
|
||||
case 'shaders':
|
||||
if (yamlValue is! YamlList) {
|
||||
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')) {
|
||||
final Object? assets = 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');
|
||||
}
|
||||
errors.addAll(_validateAssets(valueMap['assets']));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
const Set<int> fontWeights = <int>{
|
||||
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;
|
||||
|
||||
String? flavor;
|
||||
|
||||
Future<void> _calculateTargetPlatform() async {
|
||||
if (_targetPlatform != null) {
|
||||
return;
|
||||
@ -494,7 +496,10 @@ class HotRunner extends ResidentRunner {
|
||||
final bool rebuildBundle = assetBundle.needsBuild();
|
||||
if (rebuildBundle) {
|
||||
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) {
|
||||
return UpdateFSReport();
|
||||
}
|
||||
|
@ -1567,6 +1567,7 @@ class CapturingAppDomain extends AppDomain {
|
||||
bool machine = true,
|
||||
String? userIdentifier,
|
||||
bool enableDevTools = true,
|
||||
String? flavor,
|
||||
}) async {
|
||||
this.multidexEnabled = multidexEnabled;
|
||||
this.userIdentifier = userIdentifier;
|
||||
|
@ -9,11 +9,15 @@ import 'package:file/memory.dart';
|
||||
import 'package:flutter_tools/src/artifacts.dart';
|
||||
import 'package:flutter_tools/src/asset.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/user_messages.dart';
|
||||
import 'package:flutter_tools/src/build_info.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/globals.dart' as globals;
|
||||
import 'package:flutter_tools/src/project.dart';
|
||||
import 'package:standard_message_codec/standard_message_codec.dart';
|
||||
|
||||
import '../src/common.dart';
|
||||
@ -23,7 +27,9 @@ const String shaderLibDir = '/./shader_lib';
|
||||
|
||||
void main() {
|
||||
group('AssetBundle.build', () {
|
||||
late Logger logger;
|
||||
late FileSystem testFileSystem;
|
||||
late Platform platform;
|
||||
|
||||
setUp(() async {
|
||||
testFileSystem = MemoryFileSystem(
|
||||
@ -32,6 +38,8 @@ void main() {
|
||||
: FileSystemStyle.posix,
|
||||
);
|
||||
testFileSystem.currentDirectory = testFileSystem.systemTempDirectory.createTempSync('flutter_asset_bundle_test.');
|
||||
logger = BufferLogger.test();
|
||||
platform = FakePlatform(operatingSystem: globals.platform.operatingSystem);
|
||||
});
|
||||
|
||||
testUsingContext('nonempty', () async {
|
||||
@ -323,6 +331,185 @@ flutter:
|
||||
FileSystem: () => testFileSystem,
|
||||
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)', () {
|
||||
|
@ -6,6 +6,7 @@ import 'package:file/memory.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/logger.dart';
|
||||
import 'package:flutter_tools/src/flutter_manifest.dart';
|
||||
|
||||
import '../../src/common.dart';
|
||||
|
||||
@ -15,18 +16,27 @@ void main() {
|
||||
final DeferredComponent component = DeferredComponent(
|
||||
name: 'bestcomponent',
|
||||
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.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', () {
|
||||
final DeferredComponent component = DeferredComponent(
|
||||
name: 'bestcomponent',
|
||||
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.assigned, false);
|
||||
@ -94,7 +104,10 @@ void main() {
|
||||
final DeferredComponent component = DeferredComponent(
|
||||
name: 'bestcomponent',
|
||||
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');
|
||||
});
|
||||
@ -103,7 +116,10 @@ void main() {
|
||||
final DeferredComponent component = DeferredComponent(
|
||||
name: 'bestcomponent',
|
||||
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'])]);
|
||||
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', () {
|
||||
const BuildInfo buildInfo = BuildInfo(BuildMode.debug, '',
|
||||
const BuildInfo buildInfo = BuildInfo(BuildMode.debug, 'strawberry',
|
||||
treeShakeIcons: true,
|
||||
trackWidgetCreation: true,
|
||||
dartDefines: <String>['foo=2', 'bar=2'],
|
||||
@ -220,7 +220,7 @@ void main() {
|
||||
packagesPath: 'foo/.dart_tool/package_config.json',
|
||||
codeSizeDirectory: 'foo/code-size',
|
||||
// These values are ignored by toEnvironmentConfig
|
||||
androidProjectArgs: <String>['foo=bar', 'fizz=bazz']
|
||||
androidProjectArgs: <String>['foo=bar', 'fizz=bazz'],
|
||||
);
|
||||
|
||||
expect(buildInfo.toEnvironmentConfig(), <String, String>{
|
||||
@ -235,6 +235,7 @@ void main() {
|
||||
'BUNDLE_SKSL_PATH': 'foo/bar/baz.sksl.json',
|
||||
'PACKAGE_CONFIG': 'foo/.dart_tool/package_config.json',
|
||||
'CODE_SIZE_DIRECTORY': 'foo/code-size',
|
||||
'FLAVOR': 'strawberry',
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -32,6 +32,7 @@ void main() {
|
||||
fileSystem: fileSystem,
|
||||
logger: BufferLogger.test(),
|
||||
platform: FakePlatform(),
|
||||
defines: <String, String>{},
|
||||
);
|
||||
fileSystem.file(environment.buildDir.childFile('app.dill')).createSync(recursive: true);
|
||||
fileSystem.file('packages/flutter_tools/lib/src/build_system/targets/assets.dart')
|
||||
@ -93,6 +94,69 @@ flutter:
|
||||
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 {
|
||||
fileSystem.file('pubspec.yaml')
|
||||
..createSync()
|
||||
|
@ -733,7 +733,14 @@ class FakeBundle extends AssetBundle {
|
||||
List<File> get additionalDependencies => <File>[];
|
||||
|
||||
@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;
|
||||
}
|
||||
|
||||
|
@ -13,12 +13,17 @@ import 'package:flutter_tools/src/flutter_manifest.dart';
|
||||
import '../src/common.dart';
|
||||
|
||||
void main() {
|
||||
late BufferLogger logger;
|
||||
|
||||
setUpAll(() {
|
||||
Cache.flutterRoot = getFlutterRoot();
|
||||
});
|
||||
|
||||
setUp(() {
|
||||
logger = BufferLogger.test();
|
||||
});
|
||||
|
||||
testWithoutContext('FlutterManifest is empty when the pubspec.yaml file is empty', () async {
|
||||
final BufferLogger logger = BufferLogger.test();
|
||||
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
||||
'',
|
||||
logger: logger,
|
||||
@ -34,7 +39,6 @@ void main() {
|
||||
});
|
||||
|
||||
testWithoutContext('FlutterManifest is null when the pubspec.yaml file is not a map', () async {
|
||||
final BufferLogger logger = BufferLogger.test();
|
||||
expect(FlutterManifest.createFromString(
|
||||
'Not a map',
|
||||
logger: logger,
|
||||
@ -50,7 +54,6 @@ dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
''';
|
||||
final BufferLogger logger = BufferLogger.test();
|
||||
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
||||
manifest,
|
||||
logger: logger,
|
||||
@ -74,7 +77,6 @@ dependencies:
|
||||
flutter:
|
||||
uses-material-design: true
|
||||
''';
|
||||
final BufferLogger logger = BufferLogger.test();
|
||||
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
||||
manifest,
|
||||
logger: logger,
|
||||
@ -92,7 +94,6 @@ dependencies:
|
||||
flutter:
|
||||
generate: true
|
||||
''';
|
||||
final BufferLogger logger = BufferLogger.test();
|
||||
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
||||
manifest,
|
||||
logger: logger,
|
||||
@ -110,7 +111,6 @@ dependencies:
|
||||
flutter:
|
||||
generate: "invalid"
|
||||
''';
|
||||
final BufferLogger logger = BufferLogger.test();
|
||||
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
||||
manifest,
|
||||
logger: logger,
|
||||
@ -128,7 +128,6 @@ dependencies:
|
||||
flutter:
|
||||
generate: false
|
||||
''';
|
||||
final BufferLogger logger = BufferLogger.test();
|
||||
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
||||
manifest,
|
||||
logger: logger,
|
||||
@ -149,18 +148,38 @@ flutter:
|
||||
- a/foo
|
||||
- a/bar
|
||||
''';
|
||||
final BufferLogger logger = BufferLogger.test();
|
||||
|
||||
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
||||
manifest,
|
||||
logger: logger,
|
||||
)!;
|
||||
|
||||
expect(flutterManifest.assets, <Uri>[
|
||||
Uri.parse('a/foo'),
|
||||
Uri.parse('a/bar'),
|
||||
expect(flutterManifest.assets, <AssetsEntry>[
|
||||
AssetsEntry(uri: Uri.parse('a/foo')),
|
||||
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 {
|
||||
const String manifest = '''
|
||||
name: test
|
||||
@ -174,7 +193,7 @@ flutter:
|
||||
fonts:
|
||||
- asset: a/bar
|
||||
''';
|
||||
final BufferLogger logger = BufferLogger.test();
|
||||
|
||||
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
||||
manifest,
|
||||
logger: logger,
|
||||
@ -211,7 +230,7 @@ flutter:
|
||||
- asset: a/bar
|
||||
weight: 400
|
||||
''';
|
||||
final BufferLogger logger = BufferLogger.test();
|
||||
|
||||
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
||||
manifest,
|
||||
logger: logger,
|
||||
@ -251,7 +270,7 @@ flutter:
|
||||
weight: 400
|
||||
style: italic
|
||||
''';
|
||||
final BufferLogger logger = BufferLogger.test();
|
||||
|
||||
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
||||
manifest,
|
||||
logger: logger,
|
||||
@ -297,7 +316,7 @@ flutter:
|
||||
asset: a/baz
|
||||
style: italic
|
||||
''';
|
||||
final BufferLogger logger = BufferLogger.test();
|
||||
|
||||
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
||||
manifest,
|
||||
logger: logger,
|
||||
@ -359,7 +378,7 @@ flutter:
|
||||
asset: a/baz
|
||||
style: italic
|
||||
''';
|
||||
final BufferLogger logger = BufferLogger.test();
|
||||
|
||||
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
||||
manifest,
|
||||
logger: logger,
|
||||
@ -405,7 +424,7 @@ flutter:
|
||||
weight: 400
|
||||
style: italic
|
||||
''';
|
||||
final BufferLogger logger = BufferLogger.test();
|
||||
|
||||
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
||||
manifest,
|
||||
logger: logger,
|
||||
@ -448,7 +467,7 @@ flutter:
|
||||
style: italic
|
||||
- family: bar
|
||||
''';
|
||||
final BufferLogger logger = BufferLogger.test();
|
||||
|
||||
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
||||
manifest,
|
||||
logger: logger,
|
||||
@ -487,7 +506,7 @@ flutter:
|
||||
fonts:
|
||||
- weight: 400
|
||||
''';
|
||||
final BufferLogger logger = BufferLogger.test();
|
||||
|
||||
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
||||
manifest,
|
||||
logger: logger,
|
||||
@ -505,7 +524,7 @@ dependencies:
|
||||
sdk: flutter
|
||||
flutter:
|
||||
''';
|
||||
final BufferLogger logger = BufferLogger.test();
|
||||
|
||||
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
||||
manifest,
|
||||
logger: logger,
|
||||
@ -526,7 +545,7 @@ flutter:
|
||||
androidPackage: com.example
|
||||
androidX: true
|
||||
''';
|
||||
final BufferLogger logger = BufferLogger.test();
|
||||
|
||||
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
||||
manifest,
|
||||
logger: logger,
|
||||
@ -544,7 +563,7 @@ flutter:
|
||||
plugin:
|
||||
androidPackage: com.example
|
||||
''';
|
||||
final BufferLogger logger = BufferLogger.test();
|
||||
|
||||
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
||||
manifest,
|
||||
logger: logger,
|
||||
@ -565,7 +584,7 @@ flutter:
|
||||
package: com.example
|
||||
pluginClass: TestPlugin
|
||||
''';
|
||||
final BufferLogger logger = BufferLogger.test();
|
||||
|
||||
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
||||
manifest,
|
||||
logger: logger,
|
||||
@ -585,7 +604,7 @@ flutter:
|
||||
ios:
|
||||
pluginClass: HelloPlugin
|
||||
''';
|
||||
final BufferLogger logger = BufferLogger.test();
|
||||
|
||||
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
||||
manifest,
|
||||
logger: logger,
|
||||
@ -601,7 +620,7 @@ name: test
|
||||
flutter:
|
||||
plugin:
|
||||
''';
|
||||
final BufferLogger logger = BufferLogger.test();
|
||||
|
||||
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
||||
manifest,
|
||||
logger: logger,
|
||||
@ -621,7 +640,7 @@ dependencies:
|
||||
sdk: flutter
|
||||
flutter:
|
||||
''';
|
||||
final BufferLogger logger = BufferLogger.test();
|
||||
|
||||
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
||||
manifest,
|
||||
logger: logger,
|
||||
@ -643,7 +662,7 @@ dependencies:
|
||||
sdk: flutter
|
||||
flutter:
|
||||
''';
|
||||
final BufferLogger logger = BufferLogger.test();
|
||||
|
||||
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
||||
manifest,
|
||||
logger: logger,
|
||||
@ -664,7 +683,7 @@ dependencies:
|
||||
sdk: flutter
|
||||
flutter:
|
||||
''';
|
||||
final BufferLogger logger = BufferLogger.test();
|
||||
|
||||
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
||||
manifest,
|
||||
logger: logger,
|
||||
@ -686,7 +705,7 @@ dependencies:
|
||||
sdk: flutter
|
||||
flutter:
|
||||
''';
|
||||
final BufferLogger logger = BufferLogger.test();
|
||||
|
||||
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
||||
manifest,
|
||||
logger: logger,
|
||||
@ -708,7 +727,7 @@ dependencies:
|
||||
sdk: flutter
|
||||
flutter:
|
||||
''';
|
||||
final BufferLogger logger = BufferLogger.test();
|
||||
|
||||
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
||||
manifest,
|
||||
logger: logger,
|
||||
@ -725,7 +744,7 @@ dependencies:
|
||||
sdk: flutter
|
||||
flutter:
|
||||
''';
|
||||
final BufferLogger logger = BufferLogger.test();
|
||||
|
||||
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
||||
manifest,
|
||||
logger: logger,
|
||||
@ -747,7 +766,7 @@ flutter:
|
||||
fonts:
|
||||
-asset: a/bar
|
||||
''';
|
||||
final BufferLogger logger = BufferLogger.test();
|
||||
|
||||
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
||||
manifest,
|
||||
logger: logger,
|
||||
@ -767,7 +786,7 @@ dependencies:
|
||||
flutter:
|
||||
fonts: []
|
||||
''';
|
||||
final BufferLogger logger = BufferLogger.test();
|
||||
|
||||
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
||||
manifest,
|
||||
logger: logger,
|
||||
@ -786,7 +805,7 @@ dependencies:
|
||||
flutter:
|
||||
assets: []
|
||||
''';
|
||||
final BufferLogger logger = BufferLogger.test();
|
||||
|
||||
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
||||
manifest,
|
||||
logger: logger,
|
||||
@ -809,7 +828,7 @@ flutter:
|
||||
fonts:
|
||||
- asset
|
||||
''';
|
||||
final BufferLogger logger = BufferLogger.test();
|
||||
|
||||
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
||||
manifest,
|
||||
logger: logger,
|
||||
@ -833,7 +852,7 @@ flutter:
|
||||
fonts:
|
||||
-asset: a/bar
|
||||
''';
|
||||
final BufferLogger logger = BufferLogger.test();
|
||||
|
||||
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
||||
manifest,
|
||||
logger: logger,
|
||||
@ -858,7 +877,7 @@ flutter:
|
||||
- asset: a/bar
|
||||
- string
|
||||
''';
|
||||
final BufferLogger logger = BufferLogger.test();
|
||||
|
||||
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
||||
manifest,
|
||||
logger: logger,
|
||||
@ -880,15 +899,13 @@ flutter:
|
||||
- lib/gallery/example_code.dart
|
||||
-
|
||||
''';
|
||||
final BufferLogger logger = BufferLogger.test();
|
||||
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
||||
|
||||
FlutterManifest.createFromString(
|
||||
manifest,
|
||||
logger: logger,
|
||||
)!;
|
||||
final List<Uri> assets = flutterManifest.assets;
|
||||
);
|
||||
|
||||
expect(logger.errorText, contains('Asset manifest contains a null or empty uri.'));
|
||||
expect(assets, hasLength(1));
|
||||
});
|
||||
|
||||
testWithoutContext('FlutterManifest handles special characters in asset URIs', () {
|
||||
@ -904,18 +921,18 @@ flutter:
|
||||
- lib/gallery/abc?xyz
|
||||
- lib/gallery/aaa bbb
|
||||
''';
|
||||
final BufferLogger logger = BufferLogger.test();
|
||||
|
||||
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
||||
manifest,
|
||||
logger: logger,
|
||||
)!;
|
||||
final List<Uri> assets = flutterManifest.assets;
|
||||
final List<AssetsEntry> assets = flutterManifest.assets;
|
||||
|
||||
expect(assets, hasLength(3));
|
||||
expect(assets, <Uri>[
|
||||
Uri.parse('lib/gallery/abc%23xyz'),
|
||||
Uri.parse('lib/gallery/abc%3Fxyz'),
|
||||
Uri.parse('lib/gallery/aaa%20bbb'),
|
||||
expect(assets, <AssetsEntry>[
|
||||
AssetsEntry(uri: Uri.parse('lib/gallery/abc%23xyz')),
|
||||
AssetsEntry(uri: Uri.parse('lib/gallery/abc%3Fxyz')),
|
||||
AssetsEntry(uri: Uri.parse('lib/gallery/aaa%20bbb')),
|
||||
]);
|
||||
});
|
||||
|
||||
@ -929,7 +946,7 @@ dependencies:
|
||||
flutter:
|
||||
- uses-material-design: true
|
||||
''';
|
||||
final BufferLogger logger = BufferLogger.test();
|
||||
|
||||
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
||||
manifest,
|
||||
logger: logger,
|
||||
@ -954,7 +971,7 @@ flutter:
|
||||
''';
|
||||
final FileSystem fileSystem = MemoryFileSystem.test();
|
||||
fileSystem.file('pubspec.yaml').writeAsStringSync(manifest);
|
||||
final BufferLogger logger = BufferLogger.test();
|
||||
|
||||
final FlutterManifest flutterManifest = FlutterManifest.createFromPath(
|
||||
'pubspec.yaml',
|
||||
fileSystem: fileSystem,
|
||||
@ -975,7 +992,7 @@ flutter:
|
||||
|
||||
final FileSystem fileSystem = MemoryFileSystem.test(style: FileSystemStyle.windows);
|
||||
fileSystem.file('pubspec.yaml').writeAsStringSync(manifest);
|
||||
final BufferLogger logger = BufferLogger.test();
|
||||
|
||||
final FlutterManifest flutterManifest = FlutterManifest.createFromPath(
|
||||
'pubspec.yaml',
|
||||
fileSystem: fileSystem,
|
||||
@ -992,7 +1009,7 @@ flutter:
|
||||
plugin:
|
||||
androidPackage: com.example
|
||||
''';
|
||||
final BufferLogger logger = BufferLogger.test();
|
||||
|
||||
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
||||
manifest,
|
||||
logger: logger,
|
||||
@ -1011,7 +1028,7 @@ flutter:
|
||||
some_platform:
|
||||
pluginClass: SomeClass
|
||||
''';
|
||||
final BufferLogger logger = BufferLogger.test();
|
||||
|
||||
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
||||
manifest,
|
||||
logger: logger,
|
||||
@ -1032,7 +1049,7 @@ flutter:
|
||||
ios:
|
||||
pluginClass: SomeClass
|
||||
''';
|
||||
final BufferLogger logger = BufferLogger.test();
|
||||
|
||||
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
||||
manifest,
|
||||
logger: logger,
|
||||
@ -1058,7 +1075,7 @@ flutter:
|
||||
ios:
|
||||
pluginClass: SomeClass
|
||||
''';
|
||||
final BufferLogger logger = BufferLogger.test();
|
||||
|
||||
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
||||
manifest,
|
||||
logger: logger,
|
||||
@ -1082,7 +1099,7 @@ flutter:
|
||||
platforms:
|
||||
- android
|
||||
''';
|
||||
final BufferLogger logger = BufferLogger.test();
|
||||
|
||||
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
||||
manifest,
|
||||
logger: logger,
|
||||
@ -1104,7 +1121,7 @@ flutter:
|
||||
ios:
|
||||
pluginClass: SomeClass
|
||||
''';
|
||||
final BufferLogger logger = BufferLogger.test();
|
||||
|
||||
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
||||
manifest,
|
||||
logger: logger,
|
||||
@ -1124,7 +1141,7 @@ dependencies:
|
||||
flutter:
|
||||
licenses: []
|
||||
''';
|
||||
final BufferLogger logger = BufferLogger.test();
|
||||
|
||||
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
||||
manifest,
|
||||
logger: logger,
|
||||
@ -1144,7 +1161,7 @@ flutter:
|
||||
licenses:
|
||||
- foo.txt
|
||||
''';
|
||||
final BufferLogger logger = BufferLogger.test();
|
||||
|
||||
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
||||
manifest,
|
||||
logger: logger,
|
||||
@ -1162,7 +1179,7 @@ dependencies:
|
||||
flutter:
|
||||
licenses: foo.txt
|
||||
''';
|
||||
final BufferLogger logger = BufferLogger.test();
|
||||
|
||||
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
||||
manifest,
|
||||
logger: logger,
|
||||
@ -1183,7 +1200,7 @@ flutter:
|
||||
- foo.txt
|
||||
- bar: fizz
|
||||
''';
|
||||
final BufferLogger logger = BufferLogger.test();
|
||||
|
||||
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
||||
manifest,
|
||||
logger: logger,
|
||||
@ -1207,7 +1224,7 @@ flutter:
|
||||
assets:
|
||||
- path/to/asset.jpg
|
||||
''';
|
||||
final BufferLogger logger = BufferLogger.test();
|
||||
|
||||
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
||||
manifest,
|
||||
logger: logger,
|
||||
@ -1220,7 +1237,7 @@ flutter:
|
||||
expect(deferredComponents[0].libraries.length, 1);
|
||||
expect(deferredComponents[0].libraries[0], 'lib1');
|
||||
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 {
|
||||
@ -1243,7 +1260,7 @@ flutter:
|
||||
assets:
|
||||
- path/to/asset2.jpg
|
||||
''';
|
||||
final BufferLogger logger = BufferLogger.test();
|
||||
|
||||
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
||||
manifest,
|
||||
logger: logger,
|
||||
@ -1256,14 +1273,14 @@ flutter:
|
||||
expect(deferredComponents[0].libraries.length, 1);
|
||||
expect(deferredComponents[0].libraries[0], 'lib1');
|
||||
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].libraries.length, 2);
|
||||
expect(deferredComponents[1].libraries[0], 'lib2');
|
||||
expect(deferredComponents[1].libraries[1], 'lib3');
|
||||
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 {
|
||||
@ -1275,7 +1292,7 @@ dependencies:
|
||||
flutter:
|
||||
deferred-components:
|
||||
''';
|
||||
final BufferLogger logger = BufferLogger.test();
|
||||
|
||||
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
||||
manifest,
|
||||
logger: logger,
|
||||
@ -1296,7 +1313,7 @@ flutter:
|
||||
- libraries:
|
||||
- lib1
|
||||
''';
|
||||
final BufferLogger logger = BufferLogger.test();
|
||||
|
||||
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
||||
manifest,
|
||||
logger: logger,
|
||||
@ -1315,7 +1332,7 @@ dependencies:
|
||||
flutter:
|
||||
deferred-components: blah
|
||||
''';
|
||||
final BufferLogger logger = BufferLogger.test();
|
||||
|
||||
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
||||
manifest,
|
||||
logger: logger,
|
||||
@ -1336,7 +1353,7 @@ flutter:
|
||||
- name: blah
|
||||
libraries: blah
|
||||
''';
|
||||
final BufferLogger logger = BufferLogger.test();
|
||||
|
||||
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
||||
manifest,
|
||||
logger: logger,
|
||||
@ -1358,7 +1375,7 @@ flutter:
|
||||
libraries:
|
||||
- not-a-string:
|
||||
''';
|
||||
final BufferLogger logger = BufferLogger.test();
|
||||
|
||||
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
||||
manifest,
|
||||
logger: logger,
|
||||
@ -1380,14 +1397,14 @@ flutter:
|
||||
assets:
|
||||
- not-a-string:
|
||||
''';
|
||||
final BufferLogger logger = BufferLogger.test();
|
||||
|
||||
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
||||
manifest,
|
||||
logger: logger,
|
||||
);
|
||||
|
||||
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 {
|
||||
@ -1404,14 +1421,14 @@ flutter:
|
||||
- also-not-a-string:
|
||||
- woo
|
||||
''';
|
||||
final BufferLogger logger = BufferLogger.test();
|
||||
|
||||
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
||||
manifest,
|
||||
logger: logger,
|
||||
);
|
||||
|
||||
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 {
|
||||
@ -1431,14 +1448,14 @@ flutter:
|
||||
- not-a-string:
|
||||
- woo
|
||||
''';
|
||||
final BufferLogger logger = BufferLogger.test();
|
||||
|
||||
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
||||
manifest,
|
||||
logger: logger,
|
||||
);
|
||||
|
||||
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 {
|
||||
@ -1452,7 +1469,7 @@ flutter:
|
||||
- name: blah
|
||||
assets: blah
|
||||
''';
|
||||
final BufferLogger logger = BufferLogger.test();
|
||||
|
||||
final FlutterManifest? flutterManifest = FlutterManifest.createFromString(
|
||||
manifest,
|
||||
logger: logger,
|
||||
@ -1476,7 +1493,7 @@ flutter:
|
||||
- path/to/asset2.jpg
|
||||
- path/to/asset3.jpg
|
||||
''';
|
||||
final BufferLogger logger = BufferLogger.test();
|
||||
|
||||
final FlutterManifest flutterManifest = FlutterManifest.createFromString(
|
||||
manifest,
|
||||
logger: logger,
|
||||
@ -1488,9 +1505,9 @@ flutter:
|
||||
expect(deferredComponents[0].name, 'component1');
|
||||
expect(deferredComponents[0].libraries.length, 0);
|
||||
expect(deferredComponents[0].assets.length, 3);
|
||||
expect(deferredComponents[0].assets[0].path, 'path/to/asset1.jpg');
|
||||
expect(deferredComponents[0].assets[1].path, 'path/to/asset2.jpg');
|
||||
expect(deferredComponents[0].assets[2].path, 'path/to/asset3.jpg');
|
||||
expect(deferredComponents[0].assets[0].uri.path, 'path/to/asset1.jpg');
|
||||
expect(deferredComponents[0].assets[1].uri.path, 'path/to/asset2.jpg');
|
||||
expect(deferredComponents[0].assets[2].uri.path, 'path/to/asset3.jpg');
|
||||
});
|
||||
|
||||
testWithoutContext('FlutterManifest can parse empty dependencies', () async {
|
||||
|
@ -160,6 +160,7 @@ void main() {
|
||||
'FRONTEND_SERVER_STARTER_PATH': frontendServerStarterPath,
|
||||
'INFOPLIST_PATH': 'Info.plist',
|
||||
'SDKROOT': sdkRoot,
|
||||
'FLAVOR': 'strawberry',
|
||||
'SPLIT_DEBUG_INFO': splitDebugInfo,
|
||||
'TRACK_WIDGET_CREATION': trackWidgetCreation,
|
||||
'TREE_SHAKE_ICONS': treeShake,
|
||||
@ -174,6 +175,7 @@ void main() {
|
||||
'-dTargetPlatform=ios',
|
||||
'-dTargetFile=lib/main.dart',
|
||||
'-dBuildMode=${buildMode.toLowerCase()}',
|
||||
'-dFlavor=strawberry',
|
||||
'-dIosArchs=$archs',
|
||||
'-dSdkRoot=$sdkRoot',
|
||||
'-dSplitDebugInfo=$splitDebugInfo',
|
||||
|
Loading…
Reference in New Issue
Block a user