Enable asset transformation for flutter run -d <browser> and flutter test (#144734)

Partial implementation of https://github.com/flutter/flutter/issues/143348.

The title says it all. Feel free to experiment with the feature using this project: https://github.com/andrewkolos/asset_transformers_test.
This commit is contained in:
Andrew Kolos 2024-03-07 15:38:40 -08:00 committed by GitHub
parent dfbf3d2346
commit 06ac042271
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 113 additions and 2 deletions

View file

@ -14,6 +14,7 @@ import 'base/logger.dart';
import 'build_info.dart';
import 'build_system/build_system.dart';
import 'build_system/depfile.dart';
import 'build_system/tools/asset_transformer.dart';
import 'build_system/tools/scene_importer.dart';
import 'build_system/tools/shader_compiler.dart';
import 'bundle.dart';
@ -145,6 +146,7 @@ Future<void> writeBundle(
required FileSystem fileSystem,
required Artifacts artifacts,
required Logger logger,
required Directory projectDir,
}) async {
if (bundleDir.existsSync()) {
try {
@ -172,6 +174,12 @@ Future<void> writeBundle(
artifacts: artifacts,
);
final AssetTransformer assetTransformer = AssetTransformer(
processManager: processManager,
fileSystem: fileSystem,
dartBinaryPath: artifacts.getArtifactPath(Artifact.engineDartBinary),
);
// Limit number of open files to avoid running out of file descriptors.
final Pool pool = Pool(64);
await Future.wait<void>(
@ -190,8 +198,20 @@ Future<void> writeBundle(
final File input = devFSContent.file as File;
bool doCopy = true;
switch (entry.value.kind) {
case AssetKind.regular:
case AssetKind.regular:
if (entry.value.transformers.isEmpty) {
break;
}
final AssetTransformationFailure? failure = await assetTransformer.transformAsset(
asset: input,
outputPath: file.path,
workingDirectory: projectDir.path,
transformerEntries: entry.value.transformers,
);
doCopy = false;
if (failure != null) {
throwToolExit(failure.message);
}
case AssetKind.font:
break;
case AssetKind.shader:

View file

@ -675,6 +675,7 @@ class TestCommand extends FlutterCommand with DeviceBasedDevelopmentArtifacts {
fileSystem: globals.fs,
artifacts: globals.artifacts!,
logger: globals.logger,
projectDir: globals.fs.currentDirectory,
);
}
}

View file

@ -946,6 +946,7 @@ class WebDevFS implements DevFS {
fileSystem: globals.fs,
artifacts: globals.artifacts!,
logger: globals.logger,
projectDir: rootDirectory,
);
}
}

View file

@ -543,6 +543,7 @@ flutter:
fileSystem: globals.fs,
artifacts: globals.artifacts!,
logger: testLogger,
projectDir: globals.fs.currentDirectory
);
expect(testLogger.warningText, contains('Expected Error Text'));
@ -668,6 +669,7 @@ flutter:
fileSystem: globals.fs,
artifacts: globals.artifacts!,
logger: testLogger,
projectDir: globals.fs.currentDirectory,
);
}, overrides: <Type, Generator>{
@ -719,6 +721,7 @@ flutter:
fileSystem: globals.fs,
artifacts: globals.artifacts!,
logger: testLogger,
projectDir: globals.fs.currentDirectory,
);
}, overrides: <Type, Generator>{
@ -805,6 +808,7 @@ flutter:
fileSystem: globals.fs,
artifacts: globals.artifacts!,
logger: testLogger,
projectDir: globals.fs.currentDirectory,
);
expect((globals.processManager as FakeProcessManager).hasRemainingExpectations, false);
}, overrides: <Type, Generator>{

View file

@ -2,15 +2,26 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:typed_data';
import 'package:args/args.dart';
import 'package:file/memory.dart';
import 'package:file_testing/file_testing.dart';
import 'package:flutter_tools/src/artifacts.dart';
import 'package:flutter_tools/src/asset.dart';
import 'package:flutter_tools/src/base/config.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/build_system/build_system.dart';
import 'package:flutter_tools/src/bundle.dart';
import 'package:flutter_tools/src/bundle.dart' hide defaultManifestPath;
import 'package:flutter_tools/src/bundle_builder.dart';
import 'package:flutter_tools/src/devfs.dart';
import 'package:flutter_tools/src/device.dart';
import 'package:flutter_tools/src/flutter_manifest.dart';
import 'package:flutter_tools/src/globals.dart' as globals;
import 'package:flutter_tools/src/project.dart';
import 'package:test/fake.dart';
import '../src/common.dart';
import '../src/context.dart';
@ -46,6 +57,75 @@ void main() {
ProcessManager: () => FakeProcessManager.any(),
});
testWithoutContext('writeBundle applies transformations to any assets that have them defined', () async {
final MemoryFileSystem fileSystem = MemoryFileSystem.test();
final File asset = fileSystem.file('my-asset.txt')
..createSync()
..writeAsBytesSync(<int>[1, 2, 3]);
final Artifacts artifacts = Artifacts.test();
final FakeProcessManager processManager = FakeProcessManager.list(
<FakeCommand>[
FakeCommand(
command: <Pattern>[
artifacts.getArtifactPath(Artifact.engineDartBinary),
'run',
'increment',
'--input=/.tmp_rand0/my-asset.txt-transformOutput0.txt',
'--output=/.tmp_rand0/my-asset.txt-transformOutput1.txt'
],
onRun: (List<String> command) {
final ArgResults argParseResults = (ArgParser()
..addOption('input', mandatory: true)
..addOption('output', mandatory: true))
.parse(command);
final File inputFile = fileSystem.file(argParseResults['input']);
final File outputFile = fileSystem.file(argParseResults['output']);
expect(inputFile, exists);
outputFile
..createSync()
..writeAsBytesSync(
Uint8List.fromList(
inputFile.readAsBytesSync().map((int b) => b + 1).toList(),
),
);
},
),
],
);
final FakeAssetBundle bundle = FakeAssetBundle()
..entries['my-asset.txt'] = AssetBundleEntry(
DevFSFileContent(asset),
kind: AssetKind.regular,
transformers: const <AssetTransformerEntry>[
AssetTransformerEntry(package: 'increment', args: <String>[]),
],
);
final Directory bundleDir = fileSystem.directory(
getAssetBuildDirectory(Config.test(), fileSystem),
);
await writeBundle(
bundleDir,
bundle.entries,
targetPlatform: TargetPlatform.tester,
impellerStatus: ImpellerStatus.platformDefault,
processManager: processManager,
fileSystem: fileSystem,
artifacts: artifacts,
logger: BufferLogger.test(),
projectDir: fileSystem.currentDirectory,
);
final File outputAssetFile = fileSystem.file('build/flutter_assets/my-asset.txt');
expect(outputAssetFile, exists);
expect(outputAssetFile.readAsBytesSync(), orderedEquals(<int>[2, 3, 4]));
});
testUsingContext('Handles build system failure', () {
expect(
() => BundleBuilder().build(
@ -157,3 +237,8 @@ void main() {
), 'build/95b595cca01caa5f0ca0a690339dd7f6.cache.dill.track.dill');
});
}
class FakeAssetBundle extends Fake implements AssetBundle {
@override
final Map<String, AssetBundleEntry> entries = <String, AssetBundleEntry>{};
}