Skip to content

Conversation

@andrewkolos
Copy link
Contributor

@andrewkolos andrewkolos commented Feb 26, 2024

Partial implementation of #143348

This enables asset transformation during hot reload (except for web, because that has its own implementation of DevFS 🙃). Asset transformers will be reapplied after changing any asset and performing a hot reload during flutter run.

Pre-launch Checklist

If you need help, consider asking for advice on the #hackers-new channel on Discord.

@andrewkolos andrewkolos added the tool Affects the "flutter" command-line tool. See also t: labels. label Feb 26, 2024
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

late should generally be avoided (except it's useful in test code, where you ensure it's always initialized from setUp()). Consider if line 172 throws and we never initialize inputFile, we'd get a LateInitializationError on line 202. Maybe we make it nullable, and then update the check on 201 to be if (inputFile != null && cleanupInput)?

I think it's the right tool for the job here of unconditionally cleaning up resources, however finally can make it more difficult to reason about the type safety of a block of code.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in nit, but in slightly a different way than proposed. I make a null-assertion on inputFile, since it should be set if cleanupInput was set.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not familiar with the implementations of DevFSContent. When would we get DevFSFileContent vs say DevFSByteContent?

Copy link
Contributor Author

@andrewkolos andrewkolos Feb 27, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wouldn't be surprised if this is always a DevFSFileContent. However, this code can't know that. There is probably a missing abstraction somewhere upstream that guarantees this is a file, but I don't think improving this situation would be easy.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for my understanding, why do we not evict this path the first upload?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question. The only reference to assetsPathToEvict is here:

for (final String assetPath in device.devFS!.assetPathsToEvict) {
futures.add(
device.vmService!
.flutterEvictAsset(
assetPath,
isolateId: views.first.uiIsolate!.id!,
)
);
}

where make some vm service call, ext.flutter.evict. Do you know where to find the code that process this call?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nevermind. I'm starting to figure this out, give me a sec.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this VM service call ends up here:

/// Called in response to the `ext.flutter.evict` service extension.
///
/// This is used by the `flutter` tool during hot reload so that any images
/// that have changed on disk get cleared from caches.
@protected
@mustCallSuper
void evict(String asset) {
rootBundle.evict(asset);
}

which indeed just evicts the asset from the framework's cache. However, I don't know if there is a specific reason to not vacuously evict from the cache in first upload (other than perhaps performance).

Copy link
Contributor

@christopherfujino christopherfujino left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 nits, about avoiding late and await-ing a future, but otherwise LGTM

@andrewkolos
Copy link
Contributor Author

One issue worth pointing out is that if (during flutter run)

  1. an asset's transformers are changed and
  2. there is a hot reload

then the asset won't be re-transformed. This is because the only thing we check during a DevFS update is the last-modified time of the asset file.

@christopherfujino
Copy link
Contributor

One issue worth pointing out is that if (during flutter run)

  1. an asset's transformers are changed and
  2. there is a hot reload

then the asset won't be re-transformed. This is because the only thing we check during a DevFS update is the last-modified time of the asset file.

What is the "asset file" in this context? the output of the transformer?

And is this a temporary bug, or a known limitation that we should document?

@andrewkolos
Copy link
Contributor Author

One issue worth pointing out is that if (during flutter run)

  1. an asset's transformers are changed and
  2. there is a hot reload

then the asset won't be re-transformed. This is because the only thing we check during a DevFS update is the last-modified time of the asset file.

What is the "asset file" in this context? the output of the transformer?

And is this a temporary bug, or a known limitation that we should document?

The original file. This is a limitation of how we implement hot reload. More research and another PR likely to follow.

@andrewkolos andrewkolos marked this pull request as draft February 26, 2024 23:17
@christopherfujino
Copy link
Contributor

One issue worth pointing out is that if (during flutter run)

  1. an asset's transformers are changed and
  2. there is a hot reload

then the asset won't be re-transformed. This is because the only thing we check during a DevFS update is the last-modified time of the asset file.

What is the "asset file" in this context? the output of the transformer?
And is this a temporary bug, or a known limitation that we should document?

The original file. This is a limitation of how we implement hot reload. More research and another PR likely to follow.

Ahh, I see what you mean. If the input asset is the same, but the transformer itself was changed.

Is the timestamp on the application's pubspec.yaml used as an input to bundling assets?

@andrewkolos andrewkolos force-pushed the asset-transformers-flutter-run-non-web branch 2 times, most recently from e83b193 to 49cb3f0 Compare February 28, 2024 00:59
@andrewkolos
Copy link
Contributor Author

I believe I've fixed the hot reload issue. Changes are in this commit: fix asset not being updated when transformers are changed before hot reload.

This works primarily by utilizing the previously dormant bundleDirty parameter on ResidentRunner::updateDevFS. However, this means that, if the asset bundle becomes dirty for whatever reason, we'll end up re-writing all assets.

I am not sure this is acceptable, as any modification to a pubspec basically means all assets needs to be processed and rewritten to the device, which could be slow given the right circumstances (or basically any case with transformers). I want to experiment with more granular caching, though this will likely make all this code even more complicated than it already is.

@andrewkolos andrewkolos marked this pull request as ready for review February 29, 2024 23:00
@andrewkolos
Copy link
Contributor Author

I believe I've fixed the hot reload issue. Changes are in this commit: fix asset not being updated when transformers are changed before hot reload.

This works primarily by utilizing the previously dormant bundleDirty parameter on ResidentRunner::updateDevFS. However, this means that, if the asset bundle becomes dirty for whatever reason, we'll end up re-writing all assets.

I am not sure this is acceptable, as any modification to a pubspec basically means all assets needs to be processed and rewritten to the device, which could be slow given the right circumstances (or basically any case with transformers). I want to experiment with more granular caching, though this will likely make all this code even more complicated than it already is.

I thought about this more and brought this up during this week's tool PR review. I'm OK with punting on this by documenting this feature as not being supported in hot-reload. To make this feature work with hot reload, we'd need to do some significant refactoring to asset bundling+writing code (to avoid adding more spaghetti than there already is). We need to overhaul what makes an asset "dirty" so we know when to retransform and rewrite it to the device. Currently, this mechanism is implemented as a function of the last-modified-time on the underlying asset file (or for non-files, a boolean that returns true the first time it's read and then false afterwards). It is unclear if this is worth overhauling at this time, but perhaps such a decision would be easier if we had another asset related feature that would require a similar refactoring to support hot reload.

@andrewkolos andrewkolos force-pushed the asset-transformers-flutter-run-non-web branch from 4bfd9bf to cc2544c Compare February 29, 2024 23:35
@andrewkolos
Copy link
Contributor Author

@christopherfujino, after this PR lands, I'll make a documentative PR that demonstrates a duct-tape/hacky solution to make this work for hot reload. It'll include comments that point out the obstacles in the way of making hot reload work well for this. This could be referenced in the future when we come back to this.

@christopherfujino
Copy link
Contributor

@christopherfujino, after this PR lands, I'll make a documentative PR that demonstrates a duct-tape/hacky solution to make this work for hot reload. It'll include comments that point out the obstacles in the way of making hot reload work well for this. This could be referenced in the future when we come back to this.

SGTM

@andrewkolos
Copy link
Contributor Author

@christopherfujino poke

Copy link
Contributor

@christopherfujino christopherfujino left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@andrewkolos
Copy link
Contributor Author

andrewkolos commented Mar 5, 2024

More thoughts about hot-reload:

Another challenge with making this fully-support hot reload is that dart run <package> seems prone to failing when running concurrently with a pub get:

User-defined transformation of asset "/Users/andrewkolos/Desktop/asset_transformers_test/assets/svg.svg" failed.
Transformer process terminated with non-zero exit code: 255
Transformer package: vector_graphics_compiler
Full command: /Users/andrewkolos/Documents/GitHub/flutter/bin/cache/dart-sdk/bin/dart run vector_graphics_compiler:vector_graphics_compiler --input=/var/folders/qh/m_ckdxls0xvfc7ccjj13yjjw010s5h/T/flutter_tools.wGISPs/svg.svg-transformOutput0.svg
--output=/var/folders/qh/m_ckdxls0xvfc7ccjj13yjjw010s5h/T/flutter_tools.wGISPs/svg.svg-transformOutput1.svg
stdout:

stderr:
Unhandled exception:
PathNotFoundException: Cannot open file, path = '/Users/andrewkolos/Desktop/asset_transformers_test/.dart_tool/pub/bin/vector_graphics_compiler/tmpSnp7d7/vector_graphics_compiler.dart-3.4.0-187.0.dev.snapshot.incremental.temp' (OS Error: No such file or directory, errno = 2)
#0      _checkForErrorResponse (dart:io/common.dart:55)
#1      _File.open.<anonymous closure> (dart:io/file_impl.dart:381)
<asynchronous suspension>
#2      _FileStreamConsumer.addStream.<anonymous closure> (dart:io/file_impl.dart:205)
<asynchronous suspension>
Compilation did not produce any result. Expected file at `/Users/andrewkolos/Desktop/asset_transformers_test/.dart_tool/pub/bin/vector_graphics_compiler/tmpSnp7d7/vector_graphics_compiler.dart-3.4.0-187.0.dev.snapshot.incremental.temp`
Cannot rename file to '/Users/andrewkolos/Desktop/asset_transformers_test/.dart_tool/pub/bin/vector_graphics_compiler/vector_graphics_compiler.dart-3.4.0-187.0.dev.snapshot', path =
'/Users/andrewkolos/Desktop/asset_transformers_test/.dart_tool/pub/bin/vector_graphics_compiler/tmpSnp7d7/vector_graphics_compiler.dart-3.4.0-187.0.dev.snapshot.incremental.temp' (OS Error: No such file or directory, errno = 2)
PathNotFoundException: Cannot rename file to '/Users/andrewkolos/Desktop/asset_transformers_test/.dart_tool/pub/bin/vector_graphics_compiler/vector_graphics_compiler.dart-3.4.0-187.0.dev.snapshot', path =
'/Users/andrewkolos/Desktop/asset_transformers_test/.dart_tool/pub/bin/vector_graphics_compiler/tmpSnp7d7/vector_graphics_compiler.dart-3.4.0-187.0.dev.snapshot.incremental.temp' (OS Error: No such file or directory, errno = 2)

This is problematic because the Dart VSCode extension seems to run flutter pub get automatically whenever pubspec.yaml is modified. I obtained the above error message by 1) I adding a new asset with a transformer to my pubspec, 2) saving the file, and then 3) quickly (within a few seconds) initiating a hot reload.

@andrewkolos andrewkolos added the autosubmit Merge PR when tree becomes green via auto submit App label Mar 5, 2024
@auto-submit auto-submit bot merged commit ff3b6dc into flutter:master Mar 5, 2024
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 6, 2024
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 6, 2024
auto-submit bot pushed a commit to flutter/packages that referenced this pull request Mar 6, 2024
flutter/flutter@3b5a2ec...8f84f3f

2024-03-06 [email protected] Roll Packages from 2aa6e3f to 9b88dbc (8 revisions) (flutter/flutter#144693)
2024-03-06 [email protected] Roll Flutter Engine from 370e7d5866d9 to b6efe0dd88fe (1 revision) (flutter/flutter#144668)
2024-03-06 [email protected] Roll Flutter Engine from d374c78bcf52 to 370e7d5866d9 (1 revision) (flutter/flutter#144661)
2024-03-06 [email protected] Roll Flutter Engine from effcf97a1f7c to d374c78bcf52 (5 revisions) (flutter/flutter#144659)
2024-03-06 [email protected] Fill in SliverConstraints fields missing from ==, hashCode, toString (flutter/flutter#143661)
2024-03-06 [email protected] Roll Flutter Engine from 49bc1577f317 to effcf97a1f7c (10 revisions) (flutter/flutter#144653)
2024-03-05 49699333+dependabot[bot]@users.noreply.github.com Bump codecov/codecov-action from 4.0.2 to 4.1.0 (flutter/flutter#144174)
2024-03-05 [email protected] Fix embedding FlutterMacOS.framework for macOS add2app via cocoapods (flutter/flutter#144248)
2024-03-05 [email protected] Enable asset transformation feature in hot reload workflow (excluding Web) (flutter/flutter#144161)
2024-03-05 [email protected] Roll Flutter Engine from 3e8b0deffe4e to 49bc1577f317 (5 revisions) (flutter/flutter#144639)
2024-03-05 [email protected] Updated the smiley TextButton example again (flutter/flutter#144630)
2024-03-05 [email protected] Adds missing `style` to `PopupMenuButton` (flutter/flutter#143392)
2024-03-05 [email protected] Roll Flutter Engine from a7c785884903 to 3e8b0deffe4e (1 revision) (flutter/flutter#144629)
2024-03-05 [email protected] Add regression test for TabBar crash (flutter/flutter#144627)
2024-03-05 [email protected] Roll Packages from 0625827 to 2aa6e3f (5 revisions) (flutter/flutter#144628)
2024-03-05 [email protected] remove unused `firstBuildTime` parameter in `DevFS::update` (flutter/flutter#144576)
2024-03-05 [email protected] Roll Flutter Engine from 8916bb32b7b8 to a7c785884903 (1 revision) (flutter/flutter#144624)
2024-03-05 [email protected] Revert "_DefaultTabControllerState should dispose all created TabContoller instances. (#136608)" (flutter/flutter#144579)

If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/flutter-packages
Please CC [email protected],[email protected] on the revert to ensure that a human
is aware of the problem.

To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose

To report a problem with the AutoRoller itself, please file a bug:
https://issues.skia.org/issues/new?component=1389291&template=1850622

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
LouiseHsu pushed a commit to LouiseHsu/packages that referenced this pull request Mar 7, 2024
flutter/flutter@3b5a2ec...8f84f3f

2024-03-06 [email protected] Roll Packages from 2aa6e3f to 9b88dbc (8 revisions) (flutter/flutter#144693)
2024-03-06 [email protected] Roll Flutter Engine from 370e7d5866d9 to b6efe0dd88fe (1 revision) (flutter/flutter#144668)
2024-03-06 [email protected] Roll Flutter Engine from d374c78bcf52 to 370e7d5866d9 (1 revision) (flutter/flutter#144661)
2024-03-06 [email protected] Roll Flutter Engine from effcf97a1f7c to d374c78bcf52 (5 revisions) (flutter/flutter#144659)
2024-03-06 [email protected] Fill in SliverConstraints fields missing from ==, hashCode, toString (flutter/flutter#143661)
2024-03-06 [email protected] Roll Flutter Engine from 49bc1577f317 to effcf97a1f7c (10 revisions) (flutter/flutter#144653)
2024-03-05 49699333+dependabot[bot]@users.noreply.github.com Bump codecov/codecov-action from 4.0.2 to 4.1.0 (flutter/flutter#144174)
2024-03-05 [email protected] Fix embedding FlutterMacOS.framework for macOS add2app via cocoapods (flutter/flutter#144248)
2024-03-05 [email protected] Enable asset transformation feature in hot reload workflow (excluding Web) (flutter/flutter#144161)
2024-03-05 [email protected] Roll Flutter Engine from 3e8b0deffe4e to 49bc1577f317 (5 revisions) (flutter/flutter#144639)
2024-03-05 [email protected] Updated the smiley TextButton example again (flutter/flutter#144630)
2024-03-05 [email protected] Adds missing `style` to `PopupMenuButton` (flutter/flutter#143392)
2024-03-05 [email protected] Roll Flutter Engine from a7c785884903 to 3e8b0deffe4e (1 revision) (flutter/flutter#144629)
2024-03-05 [email protected] Add regression test for TabBar crash (flutter/flutter#144627)
2024-03-05 [email protected] Roll Packages from 0625827 to 2aa6e3f (5 revisions) (flutter/flutter#144628)
2024-03-05 [email protected] remove unused `firstBuildTime` parameter in `DevFS::update` (flutter/flutter#144576)
2024-03-05 [email protected] Roll Flutter Engine from 8916bb32b7b8 to a7c785884903 (1 revision) (flutter/flutter#144624)
2024-03-05 [email protected] Revert "_DefaultTabControllerState should dispose all created TabContoller instances. (#136608)" (flutter/flutter#144579)

If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/flutter-packages
Please CC [email protected],[email protected] on the revert to ensure that a human
is aware of the problem.

To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose

To report a problem with the AutoRoller itself, please file a bug:
https://issues.skia.org/issues/new?component=1389291&template=1850622

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
@andrewkolos andrewkolos deleted the asset-transformers-flutter-run-non-web branch April 29, 2024 18:17
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request May 14, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

autosubmit Merge PR when tree becomes green via auto submit App tool Affects the "flutter" command-line tool. See also t: labels.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants