Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit a306d32

Browse files
committed
Data assets
Refiling of #169273, which itself is a rebase of #159675 This PR adds bundling support for the experimental dart data asset feature: Dart packages with hooks can now emit data assets which the flutter tool will bundle. It relies on flutter's existing asset bundling mechanism (e.g. entries in AssetManifest.json, DevFS syncing in reload/restart, ...). The support is added under an experimental flag (similar to the existing native assets experimental flag). Also, kNativeAssets is removed to also bundle data assets on flutter build bundle. The chrome sandbox is disabled as per #165664. - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
1 parent dc56ed8 commit a306d32

50 files changed

Lines changed: 1811 additions & 605 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

packages/flutter_tools/lib/executable.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import 'src/base/template.dart';
1111
import 'src/base/terminal.dart';
1212
import 'src/base/user_messages.dart';
1313
import 'src/build_system/build_targets.dart';
14+
import 'src/build_system/targets/hook_runner_native.dart' show FlutterHookRunnerNative;
1415
import 'src/cache.dart';
1516
import 'src/commands/analyze.dart';
1617
import 'src/commands/assemble.dart';
@@ -48,6 +49,7 @@ import 'src/devtools_launcher.dart';
4849
import 'src/features.dart';
4950
import 'src/globals.dart' as globals;
5051
// Files in `isolated` are intentionally excluded from google3 tooling.
52+
import 'src/hook_runner.dart' show FlutterHookRunner;
5153
import 'src/isolated/build_targets.dart';
5254
import 'src/isolated/mustache_template.dart';
5355
import 'src/isolated/native_assets/test/native_assets.dart';
@@ -106,6 +108,7 @@ Future<void> main(List<String> args) async {
106108
muteCommandLogging: muteCommandLogging,
107109
verboseHelp: verboseHelp,
108110
overrides: <Type, Generator>{
111+
FlutterHookRunner: () => FlutterHookRunnerNative(),
109112
// The web runner is not supported in google3 because it depends
110113
// on dwds.
111114
WebRunnerFactory: () => DwdsWebRunnerFactory(),

packages/flutter_tools/lib/src/asset.dart

Lines changed: 127 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,84 @@ import 'license_collector.dart';
2525
import 'package_graph.dart';
2626
import 'project.dart';
2727

28+
class FlutterHookResult {
29+
const FlutterHookResult({
30+
required this.buildStart,
31+
required this.buildEnd,
32+
required this.dataAssets,
33+
required this.dependencies,
34+
});
35+
36+
FlutterHookResult.empty()
37+
: this(
38+
buildStart: DateTime.fromMillisecondsSinceEpoch(0),
39+
buildEnd: DateTime.fromMillisecondsSinceEpoch(0),
40+
dataAssets: <HookAsset>[],
41+
dependencies: <Uri>[],
42+
);
43+
44+
final List<HookAsset> dataAssets;
45+
46+
/// The timestamp at which we start a build - so the timestamp of the inputs.
47+
final DateTime buildStart;
48+
49+
/// The timestamp at which we finish a build - so the timestamp of the
50+
/// outputs.
51+
final DateTime buildEnd;
52+
53+
/// The dependencies of the build are used to check if the build needs to be
54+
/// rerun.
55+
final List<Uri> dependencies;
56+
57+
/// Whether caller may need to re-run the Dart build.
58+
bool hasAnyModifiedFiles(FileSystem fileSystem) =>
59+
_wasAnyFileModifiedSince(fileSystem, buildStart, dependencies);
60+
61+
/// Whether the files produced by the build are up-to-date.
62+
///
63+
/// NOTICE: The build itself may be up-to-date but the output may not be (as
64+
/// the output may be existing on disk and not be produced by the build
65+
/// itself - in which case we may not need to re-build if the file changes,
66+
/// but we may need to make a new asset bundle with the modified file).
67+
bool isOutputDirty(FileSystem fileSystem) => _wasAnyFileModifiedSince(
68+
fileSystem,
69+
buildEnd,
70+
dataAssets.map((HookAsset e) => e.file).toList(),
71+
);
72+
73+
static bool _wasAnyFileModifiedSince(FileSystem fileSystem, DateTime since, List<Uri> uris) {
74+
for (final Uri uri in uris) {
75+
final DateTime modified = fileSystem.statSync(uri.toFilePath()).modified;
76+
if (modified.isAfter(since)) {
77+
return true;
78+
}
79+
}
80+
return false;
81+
}
82+
83+
@override
84+
String toString() {
85+
return dataAssets.toString();
86+
}
87+
}
88+
89+
/// A convenience class to wrap native assets
90+
///
91+
/// When translating from a `DartHooksResult` to a [FlutterHookResult], where we
92+
/// need to have different classes to not import `isolated/` stuff.
93+
class HookAsset {
94+
const HookAsset({required this.file, required this.name, required this.package});
95+
96+
final Uri file;
97+
final String name;
98+
final String package;
99+
100+
@override
101+
String toString() {
102+
return 'HookAsset(file: $file, name: $name, package: $package)';
103+
}
104+
}
105+
28106
const String defaultManifestPath = 'pubspec.yaml';
29107

30108
const String kFontManifestJson = 'FontManifest.json';
@@ -113,6 +191,7 @@ abstract class AssetBundle {
113191

114192
/// Returns 0 for success; non-zero for failure.
115193
Future<int> build({
194+
FlutterHookResult? flutterHookResult,
116195
String manifestPath = defaultManifestPath,
117196
required String packageConfigPath,
118197
bool deferredComponentsEnabled = false,
@@ -163,7 +242,8 @@ class ManifestAssetBundle implements AssetBundle {
163242
_platform = platform,
164243
_flutterRoot = flutterRoot,
165244
_splitDeferredAssets = splitDeferredAssets,
166-
_licenseCollector = LicenseCollector(fileSystem: fileSystem);
245+
_licenseCollector = LicenseCollector(fileSystem: fileSystem),
246+
_lastHookResult = FlutterHookResult.empty();
167247

168248
final Logger _logger;
169249
final FileSystem _fileSystem;
@@ -189,6 +269,8 @@ class ManifestAssetBundle implements AssetBundle {
189269

190270
DateTime? _lastBuildTimestamp;
191271

272+
FlutterHookResult _lastHookResult;
273+
192274
// We assume the main asset is designed for a device pixel ratio of 1.0.
193275
static const String _kAssetManifestJsonFilename = 'AssetManifest.json';
194276
static const String _kAssetManifestBinFilename = 'AssetManifest.bin';
@@ -208,13 +290,19 @@ class ManifestAssetBundle implements AssetBundle {
208290

209291
@override
210292
bool needsBuild({String manifestPath = defaultManifestPath}) {
211-
final DateTime? lastBuildTimestamp = _lastBuildTimestamp;
212-
if (lastBuildTimestamp == null) {
293+
if (!wasBuiltOnce() ||
294+
// We need to re-run the Dart build.
295+
_lastHookResult.hasAnyModifiedFiles(_fileSystem) ||
296+
// We don't have to re-run the Dart build, but some files the Dart build
297+
// wants us to bundle have changed contents.
298+
_lastHookResult.isOutputDirty(_fileSystem)) {
213299
return true;
214300
}
301+
final DateTime lastBuildTimestamp = _lastBuildTimestamp!;
215302

216303
final FileStat manifestStat = _fileSystem.file(manifestPath).statSync();
217-
if (manifestStat.type == FileSystemEntityType.notFound) {
304+
if (manifestStat.type == FileSystemEntityType.notFound ||
305+
manifestStat.modified.isAfter(lastBuildTimestamp)) {
218306
return true;
219307
}
220308

@@ -223,18 +311,19 @@ class ManifestAssetBundle implements AssetBundle {
223311
return true; // directory was deleted.
224312
}
225313
for (final File file in directory.listSync().whereType<File>()) {
226-
final DateTime dateTime = file.statSync().modified;
227-
if (dateTime.isAfter(lastBuildTimestamp)) {
314+
final DateTime lastModified = file.statSync().modified;
315+
if (lastModified.isAfter(lastBuildTimestamp)) {
228316
return true;
229317
}
230318
}
231319
}
232320

233-
return manifestStat.modified.isAfter(lastBuildTimestamp);
321+
return false;
234322
}
235323

236324
@override
237325
Future<int> build({
326+
FlutterHookResult? flutterHookResult,
238327
String manifestPath = defaultManifestPath,
239328
FlutterProject? flutterProject,
240329
required String packageConfigPath,
@@ -258,6 +347,7 @@ class ManifestAssetBundle implements AssetBundle {
258347
// hang on hot reload, as the incremental dill files will never be copied to the
259348
// device.
260349
_lastBuildTimestamp = DateTime.now();
350+
_lastHookResult = flutterHookResult ?? FlutterHookResult.empty();
261351
if (flutterManifest.isEmpty) {
262352
entries[_kAssetManifestJsonFilename] = AssetBundleEntry(
263353
DevFSStringContent('{}'),
@@ -424,9 +514,38 @@ class ManifestAssetBundle implements AssetBundle {
424514
);
425515
}
426516
}
517+
for (final HookAsset dataAsset in flutterHookResult?.dataAssets ?? <HookAsset>[]) {
518+
final Package package = packageConfig[dataAsset.package]!;
519+
final Uri fileUri = dataAsset.file;
520+
521+
final String baseDir;
522+
final Uri relativeUri;
523+
if (fileUri.isAbsolute) {
524+
final String filePath = fileUri.toFilePath();
525+
baseDir = _fileSystem.path.dirname(filePath);
526+
relativeUri = Uri(path: _fileSystem.path.basename(filePath));
527+
} else {
528+
baseDir = package.root.toFilePath();
529+
relativeUri = fileUri;
530+
}
531+
532+
final _Asset asset = _Asset(
533+
baseDir: baseDir,
534+
relativeUri: relativeUri,
535+
entryUri: Uri.parse(_fileSystem.path.join('packages', dataAsset.package, dataAsset.name)),
536+
package: package,
537+
);
538+
if (assetVariants.containsKey(asset)) {
539+
_logger.printError(
540+
'Conflicting assets: The asset "$asset" was declared in the pubspec and the hook.',
541+
);
542+
return 1;
543+
}
544+
assetVariants[asset] = <_Asset>[asset];
545+
}
427546

428547
// Save the contents of each image, image variant, and font
429-
// asset in entries.
548+
// asset in [entries].
430549
for (final _Asset asset in assetVariants.keys) {
431550
final File assetFile = asset.lookupAssetFile(_fileSystem);
432551
final List<_Asset> variants = assetVariants[asset]!;

packages/flutter_tools/lib/src/build_info.dart

Lines changed: 38 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -611,6 +611,35 @@ enum TargetPlatform {
611611
}
612612
}
613613

614+
String get osName {
615+
switch (this) {
616+
case TargetPlatform.linux_x64:
617+
case TargetPlatform.linux_arm64:
618+
return 'linux';
619+
case TargetPlatform.darwin:
620+
return 'macos';
621+
case TargetPlatform.windows_x64:
622+
case TargetPlatform.windows_arm64:
623+
return 'windows';
624+
case TargetPlatform.android:
625+
case TargetPlatform.android_arm:
626+
case TargetPlatform.android_arm64:
627+
case TargetPlatform.android_x64:
628+
return 'android';
629+
case TargetPlatform.fuchsia_arm64:
630+
case TargetPlatform.fuchsia_x64:
631+
return 'fuchsia';
632+
case TargetPlatform.ios:
633+
return 'ios';
634+
case TargetPlatform.tester:
635+
return 'flutter-tester';
636+
case TargetPlatform.web_javascript:
637+
return 'web';
638+
case TargetPlatform.unsupported:
639+
throw UnsupportedError('Unexpected target platform $this');
640+
}
641+
}
642+
614643
String get simpleName {
615644
switch (this) {
616645
case TargetPlatform.linux_x64:
@@ -737,6 +766,15 @@ DarwinArch getDarwinArchForName(String arch) {
737766
};
738767
}
739768

769+
List<DarwinArch> getDarwinArchsFromEnv(Map<String, String> defines) {
770+
const List<DarwinArch> defaultDarwinArchitectures = <DarwinArch>[
771+
DarwinArch.x86_64,
772+
DarwinArch.arm64,
773+
];
774+
return defines[kDarwinArchs]?.split(' ').map(getDarwinArchForName).toList() ??
775+
defaultDarwinArchitectures;
776+
}
777+
740778
String getNameForTargetPlatform(TargetPlatform platform, {DarwinArch? darwinArch}) {
741779
return switch (platform) {
742780
TargetPlatform.ios when darwinArch != null => 'ios-${darwinArch.name}',
@@ -974,20 +1012,6 @@ const String kSdkRoot = 'SdkRoot';
9741012
/// Whether to enable Dart obfuscation and where to save the symbol map.
9751013
const String kDartObfuscation = 'DartObfuscation';
9761014

977-
/// Whether to enable Native Assets.
978-
///
979-
/// If true, native assets are built and the mapping for native assets lookup
980-
/// at runtime is embedded in the kernel file.
981-
///
982-
/// If false, native assets are not built, and an empty mapping is embedded in
983-
/// the kernel file. Used for targets that trigger kernel builds but
984-
/// are not OS/architecture specific.
985-
///
986-
/// Supported values are 'true' and 'false'.
987-
///
988-
/// Defaults to 'true'.
989-
const String kNativeAssets = 'NativeAssets';
990-
9911015
/// An output directory where one or more code-size measurements may be written.
9921016
const String kCodeSizeDirectory = 'CodeSizeDirectory';
9931017

packages/flutter_tools/lib/src/build_system/targets/android.dart

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import '../../base/file_system.dart';
99
import '../../build_info.dart';
1010
import '../../devfs.dart';
1111
import '../../globals.dart' as globals show xcode;
12+
import '../../isolated/native_assets/dart_hook_result.dart';
1213
import '../../project.dart';
1314
import '../build_system.dart';
1415
import '../depfile.dart';
@@ -70,9 +71,11 @@ abstract class AndroidAssetBundle extends Target {
7071
.file(isolateSnapshotData)
7172
.copySync(outputDirectory.childFile('isolate_snapshot_data').path);
7273
}
74+
final DartHookResult dartHookResult = await DartBuild.loadHookResult(environment);
7375
final Depfile assetDepfile = await copyAssets(
7476
environment,
7577
outputDirectory,
78+
dartHookResult: dartHookResult,
7679
targetPlatform: TargetPlatform.android,
7780
buildMode: buildMode,
7881
flavor: environment.defines[kFlavor],
@@ -89,7 +92,11 @@ abstract class AndroidAssetBundle extends Target {
8992
}
9093

9194
@override
92-
List<Target> get dependencies => const <Target>[KernelSnapshot(), InstallCodeAssets()];
95+
List<Target> get dependencies => const <Target>[
96+
DartBuildForNative(),
97+
KernelSnapshot(),
98+
InstallCodeAssets(),
99+
];
93100
}
94101

95102
/// An implementation of [AndroidAssetBundle] that includes dependencies on vm

packages/flutter_tools/lib/src/build_system/targets/assets.dart

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import '../../build_info.dart';
1212
import '../../dart/package_map.dart';
1313
import '../../devfs.dart';
1414
import '../../flutter_manifest.dart';
15+
import '../../isolated/native_assets/dart_hook_result.dart';
1516
import '../build_system.dart';
1617
import '../depfile.dart';
1718
import '../exceptions.dart';
@@ -33,6 +34,7 @@ import 'native_assets.dart';
3334
Future<Depfile> copyAssets(
3435
Environment environment,
3536
Directory outputDirectory, {
37+
required DartHookResult dartHookResult,
3638
Map<String, DevFSContent> additionalContent = const <String, DevFSContent>{},
3739
required TargetPlatform targetPlatform,
3840
required BuildMode buildMode,
@@ -49,6 +51,7 @@ Future<Depfile> copyAssets(
4951
splitDeferredAssets: buildMode != BuildMode.debug && buildMode != BuildMode.jitRelease,
5052
).createBundle();
5153
final int resultCode = await assetBundle.build(
54+
flutterHookResult: dartHookResult.asFlutterResult,
5255
manifestPath: pubspecFile.path,
5356
packageConfigPath: findPackageConfigFileOrDefault(environment.projectDir).path,
5457
deferredComponentsEnabled: environment.defines[kDeferredComponents] == 'true',
@@ -248,7 +251,11 @@ class CopyAssets extends Target {
248251
String get name => 'copy_assets';
249252

250253
@override
251-
List<Target> get dependencies => const <Target>[KernelSnapshot(), InstallCodeAssets()];
254+
List<Target> get dependencies => const <Target>[
255+
DartBuildForNative(),
256+
KernelSnapshot(),
257+
InstallCodeAssets(),
258+
];
252259

253260
@override
254261
List<Source> get inputs => const <Source>[
@@ -274,9 +281,11 @@ class CopyAssets extends Target {
274281
final BuildMode buildMode = BuildMode.fromCliName(buildModeEnvironment);
275282
final Directory output = environment.buildDir.childDirectory('flutter_assets');
276283
output.createSync(recursive: true);
284+
final DartHookResult dartHookResult = await DartBuild.loadHookResult(environment);
277285
final Depfile depfile = await copyAssets(
278286
environment,
279287
output,
288+
dartHookResult: dartHookResult,
280289
targetPlatform: TargetPlatform.android,
281290
buildMode: buildMode,
282291
flavor: environment.defines[kFlavor],

0 commit comments

Comments
 (0)