Unverified Commit 9c0f2399 authored by Zachary Anderson's avatar Zachary Anderson Committed by GitHub

Put shaders under a 'shaders' section in the manifest (#106752)

parent 02f0b2dd
......@@ -63,9 +63,17 @@ abstract class AssetBundleFactory {
AssetBundle createBundle();
}
enum AssetKind {
regular,
font,
shader,
}
abstract class AssetBundle {
Map<String, DevFSContent> get entries;
Map<String, AssetKind> get entryKinds;
/// The files that were specified under the deferred components assets sections
/// in pubspec.
Map<String, Map<String, DevFSContent>> get deferredComponentsEntries;
......@@ -135,6 +143,9 @@ class ManifestAssetBundle implements AssetBundle {
@override
final Map<String, DevFSContent> entries = <String, DevFSContent>{};
@override
final Map<String, AssetKind> entryKinds = <String, AssetKind>{};
@override
final Map<String, Map<String, DevFSContent>> deferredComponentsEntries = <String, Map<String, DevFSContent>>{};
......@@ -218,6 +229,7 @@ class ManifestAssetBundle implements AssetBundle {
_lastBuildTimestamp = DateTime.now();
if (flutterManifest.isEmpty) {
entries[_kAssetManifestJson] = DevFSStringContent('{}');
entryKinds[_kAssetManifestJson] = AssetKind.regular;
return 0;
}
......@@ -370,6 +382,7 @@ class ManifestAssetBundle implements AssetBundle {
inputFiles.add(variantFile);
assert(variantFile.existsSync());
entries[variant.entryUri.path] ??= DevFSFileContent(variantFile);
entryKinds[variant.entryUri.path] ??= variant.assetKind;
}
}
// Save the contents of each deferred component image, image variant, and font
......@@ -418,6 +431,7 @@ class ManifestAssetBundle implements AssetBundle {
final File assetFile = asset.lookupAssetFile(_fileSystem);
assert(assetFile.existsSync(), 'Missing ${assetFile.path}');
entries[asset.entryUri.path] ??= DevFSFileContent(assetFile);
entryKinds[asset.entryUri.path] ??= asset.assetKind;
}
// Update wildcard directories we can detect changes in them.
......@@ -449,8 +463,8 @@ class ManifestAssetBundle implements AssetBundle {
_fileSystem.file('DOES_NOT_EXIST_RERUN_FOR_WILDCARD$suffix').absolute);
}
_setIfChanged(_kAssetManifestJson, assetManifest);
_setIfChanged(kFontManifestJson, fontManifest);
_setIfChanged(_kAssetManifestJson, assetManifest, AssetKind.regular);
_setIfChanged(kFontManifestJson, fontManifest, AssetKind.regular);
_setLicenseIfChanged(licenseResult.combinedLicenses, targetPlatform);
return 0;
}
......@@ -458,14 +472,16 @@ class ManifestAssetBundle implements AssetBundle {
@override
List<File> additionalDependencies = <File>[];
void _setIfChanged(String key, DevFSStringContent content) {
void _setIfChanged(String key, DevFSStringContent content, AssetKind assetKind) {
if (!entries.containsKey(key)) {
entries[key] = content;
entryKinds[key] = assetKind;
return;
}
final DevFSStringContent? oldContent = entries[key] as DevFSStringContent?;
if (oldContent?.string != content.string) {
entries[key] = content;
entryKinds[key] = assetKind;
}
}
......@@ -477,7 +493,7 @@ class ManifestAssetBundle implements AssetBundle {
// dart:io to decompress it. So use the standard _setIfChanged to check if
// the strings still match.
if (targetPlatform == TargetPlatform.web_javascript) {
_setIfChanged(_kNoticeFile, DevFSStringContent(combinedLicenses));
_setIfChanged(_kNoticeFile, DevFSStringContent(combinedLicenses), AssetKind.regular);
return;
}
......@@ -495,6 +511,7 @@ class ManifestAssetBundle implements AssetBundle {
// common English words with domain specific words like copyright.
hintString: 'copyrightsoftwaretothisinandorofthe',
);
entryKinds[_kNoticeZippedFile] = AssetKind.regular;
}
}
......@@ -519,6 +536,7 @@ class ManifestAssetBundle implements AssetBundle {
relativeUri: Uri(path: entryUri.pathSegments.last),
entryUri: entryUri,
package: null,
assetKind: AssetKind.font,
));
}
}
......@@ -546,6 +564,7 @@ class ManifestAssetBundle implements AssetBundle {
relativeUri: Uri(path: entryUri.pathSegments.last),
entryUri: entryUri,
package: null,
assetKind: AssetKind.shader,
));
}
......@@ -745,6 +764,21 @@ class ManifestAssetBundle implements AssetBundle {
}
}
for (final Uri shaderUri in flutterManifest.shaders) {
_parseAssetFromFile(
packageConfig,
flutterManifest,
assetBase,
cache,
result,
shaderUri,
excludeDirs: excludeDirs,
packageName: packageName,
attributedPackage: attributedPackage,
assetKind: AssetKind.shader,
);
}
// Add assets referenced in the fonts section of the manifest.
for (final Font font in flutterManifest.fonts) {
for (final FontAsset fontAsset in font.fontAssets) {
......@@ -754,6 +788,7 @@ class ManifestAssetBundle implements AssetBundle {
fontAsset.assetUri,
packageName,
attributedPackage,
assetKind: AssetKind.font,
);
final File baseAssetFile = baseAsset.lookupAssetFile(_fileSystem);
if (!baseAssetFile.existsSync()) {
......@@ -816,6 +851,7 @@ class ManifestAssetBundle implements AssetBundle {
List<String> excludeDirs = const <String>[],
String? packageName,
Package? attributedPackage,
AssetKind assetKind = AssetKind.regular,
}) {
final _Asset asset = _resolveAsset(
packageConfig,
......@@ -823,6 +859,7 @@ class ManifestAssetBundle implements AssetBundle {
assetUri,
packageName,
attributedPackage,
assetKind: assetKind,
);
final List<_Asset> variants = <_Asset>[];
final File assetFile = asset.lookupAssetFile(_fileSystem);
......@@ -839,6 +876,7 @@ class ManifestAssetBundle implements AssetBundle {
entryUri: entryUri,
relativeUri: relativeUri,
package: attributedPackage,
assetKind: assetKind,
),
);
}
......@@ -852,8 +890,9 @@ class ManifestAssetBundle implements AssetBundle {
String assetsBaseDir,
Uri assetUri,
String? packageName,
Package? attributedPackage,
) {
Package? attributedPackage, {
AssetKind assetKind = AssetKind.regular,
}) {
final String assetPath = _fileSystem.path.fromUri(assetUri);
if (assetUri.pathSegments.first == 'packages'
&& !_fileSystem.isFileSync(_fileSystem.path.join(assetsBaseDir, assetPath))) {
......@@ -863,6 +902,7 @@ class ManifestAssetBundle implements AssetBundle {
assetUri,
packageConfig,
attributedPackage,
assetKind: assetKind,
);
if (packageAsset != null) {
return packageAsset;
......@@ -876,10 +916,16 @@ class ManifestAssetBundle implements AssetBundle {
: Uri(pathSegments: <String>['packages', packageName, ...assetUri.pathSegments]), // Asset from, and declared in $packageName.
relativeUri: assetUri,
package: attributedPackage,
assetKind: assetKind,
);
}
_Asset? _resolvePackageAsset(Uri assetUri, PackageConfig packageConfig, Package? attributedPackage) {
_Asset? _resolvePackageAsset(
Uri assetUri,
PackageConfig packageConfig,
Package? attributedPackage, {
AssetKind assetKind = AssetKind.regular,
}) {
assert(assetUri.pathSegments.first == 'packages');
if (assetUri.pathSegments.length > 1) {
final String packageName = assetUri.pathSegments[1];
......@@ -891,6 +937,7 @@ class ManifestAssetBundle implements AssetBundle {
entryUri: assetUri,
relativeUri: Uri(pathSegments: assetUri.pathSegments.sublist(2)),
package: attributedPackage,
assetKind: assetKind,
);
}
}
......@@ -910,6 +957,7 @@ class _Asset {
required this.relativeUri,
required this.entryUri,
required this.package,
this.assetKind = AssetKind.regular,
});
final String baseDir;
......@@ -923,6 +971,8 @@ class _Asset {
/// A platform-independent URL representing the entry for the asset manifest.
final Uri entryUri;
final AssetKind assetKind;
File lookupAssetFile(FileSystem fileSystem) {
return fileSystem.file(fileSystem.path.join(baseDir, fileSystem.path.fromUri(relativeUri)));
}
......@@ -951,7 +1001,8 @@ class _Asset {
return other is _Asset
&& other.baseDir == baseDir
&& other.relativeUri == relativeUri
&& other.entryUri == entryUri;
&& other.entryUri == entryUri
&& other.assetKind == assetKind;
}
@override
......
......@@ -88,6 +88,9 @@ Future<Depfile> copyAssets(
if (skslBundle != null)
kSkSLShaderBundlePath: skslBundle,
};
final Map<String, AssetKind> entryKinds = <String, AssetKind>{
...assetBundle.entryKinds,
};
await Future.wait<void>(
assetEntries.entries.map<Future<void>>((MapEntry<String, DevFSContent> entry) async {
......@@ -100,19 +103,31 @@ Future<Depfile> copyAssets(
// and the native APIs will look for files this way.
final File file = environment.fileSystem.file(
environment.fileSystem.path.join(outputDirectory.path, entry.key));
final AssetKind assetKind = entryKinds[entry.key] ?? AssetKind.regular;
outputs.add(file);
file.parent.createSync(recursive: true);
final DevFSContent content = entry.value;
if (content is DevFSFileContent && content.file is File) {
inputs.add(content.file as File);
if (!await iconTreeShaker.subsetFont(
bool doCopy = true;
switch (assetKind) {
case AssetKind.regular:
break;
case AssetKind.font:
doCopy = !await iconTreeShaker.subsetFont(
input: content.file as File,
outputPath: file.path,
relativePath: entry.key,
) && !await shaderCompiler.compileShader(
);
break;
case AssetKind.shader:
doCopy = !await shaderCompiler.compileShader(
input: content.file as File,
outputPath: file.path,
)) {
);
break;
}
if (doCopy) {
await (content.file as File).copy(file.path);
}
} else {
......@@ -127,8 +142,8 @@ Future<Depfile> copyAssets(
// The assets are included in assetBundle.entries as a normal asset when
// building as debug.
if (environment.defines[kDeferredComponents] == 'true' && buildMode != null) {
await Future.wait<void>(
assetBundle.deferredComponentsEntries.entries.map<Future<void>>((MapEntry<String, Map<String, DevFSContent>> componentEntries) async {
await Future.wait<void>(assetBundle.deferredComponentsEntries.entries.map<Future<void>>(
(MapEntry<String, Map<String, DevFSContent>> componentEntries) async {
final Directory componentOutputDir =
environment.projectDir
.childDirectory('build')
......
......@@ -134,6 +134,7 @@ Future<AssetBundle?> buildAssets({
Future<void> writeBundle(
Directory bundleDir,
Map<String, DevFSContent> assetEntries,
Map<String, AssetKind> entryKinds,
{ Logger? loggerOverride }
) async {
loggerOverride ??= globals.logger;
......
......@@ -477,7 +477,7 @@ class TestCommand extends FlutterCommand with DeviceBasedDevelopmentArtifacts {
}
if (_needRebuild(assetBundle.entries)) {
await writeBundle(globals.fs.directory(globals.fs.path.join('build', 'unit_test_assets')),
assetBundle.entries);
assetBundle.entries, assetBundle.entryKinds);
}
}
......
......@@ -370,6 +370,33 @@ class FlutterManifest {
return fonts;
}
late final List<Uri> shaders = _extractShaders();
List<Uri> _extractShaders() {
if (!_flutterDescriptor.containsKey('shaders')) {
return <Uri>[];
}
final List<Object?>? shaders = _flutterDescriptor['shaders'] as List<Object?>?;
if (shaders == null) {
return const <Uri>[];
}
final List<Uri> results = <Uri>[];
for (final Object? shader in shaders) {
if (shader is! String || shader == null || shader == '') {
_logger.printError('Shader manifest contains a null or empty uri.');
continue;
}
try {
results.add(Uri(pathSegments: shader.split('/')));
} on FormatException {
_logger.printError('Shader manifest contains invalid uri: $shader.');
}
}
return results;
}
/// Whether a synthetic flutter_gen package should be generated.
///
/// This can be provided to the [Pub] interface to inject a new entry
......@@ -498,7 +525,17 @@ void _validateFlutter(YamlMap? yaml, List<String> errors) {
break;
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}).',
);
}
break;
case 'shaders':
if (yamlValue is! YamlList) {
errors.add('Expected "$yamlKey" to be a list, but got $yamlValue (${yamlValue.runtimeType}).');
} else if (yamlValue.isEmpty) {
break;
......
......@@ -124,9 +124,10 @@ Future<void> _buildAssets(
throwToolExit('Unable to find assets.', exitCode: 1);
}
final Map<String, DevFSContent> assetEntries =
Map<String, DevFSContent>.of(assets.entries);
await writeBundle(globals.fs.directory(assetDir), assetEntries);
final Map<String, DevFSContent> assetEntries = Map<String, DevFSContent>.of(
assets.entries,
);
await writeBundle(globals.fs.directory(assetDir), assetEntries, assets.entryKinds);
final String appName = fuchsiaProject.project.manifest.appName;
final String outDir = getFuchsiaBuildDirectory();
......
......@@ -843,6 +843,7 @@ class WebDevFS implements DevFS {
await writeBundle(
globals.fs.directory(getAssetBuildDirectory()),
bundle.entries,
bundle.entryKinds,
);
}
}
......
......@@ -308,7 +308,12 @@ flutter:
..createSync();
handler.addError(directory, FileSystemOp.delete, const FileSystemException('Expected Error Text'));
await writeBundle(directory, <String, DevFSContent>{}, loggerOverride: testLogger);
await writeBundle(
directory,
<String, DevFSContent>{},
<String, AssetKind>{},
loggerOverride: testLogger,
);
expect(testLogger.warningText, contains('Expected Error Text'));
});
......@@ -415,14 +420,19 @@ flutter:
..writeAsStringSync(r'''
name: example
flutter:
assets:
shaders:
- assets/shader.frag
''');
final AssetBundle bundle = AssetBundleFactory.instance.createBundle();
expect(await bundle.build(packagesPath: '.packages'), 0);
await writeBundle(output, bundle.entries, loggerOverride: testLogger);
await writeBundle(
output,
bundle.entries,
bundle.entryKinds,
loggerOverride: testLogger,
);
}, overrides: <Type, Generator>{
Artifacts: () => artifacts,
......
......@@ -632,6 +632,9 @@ class FakeBundle extends AssetBundle {
@override
Map<String, DevFSContent> get entries => <String, DevFSContent>{};
@override
Map<String, AssetKind> get entryKinds => <String, AssetKind>{};
@override
List<File> get inputFiles => <File>[];
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment