-
Notifications
You must be signed in to change notification settings - Fork 213
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Filesystem
underneath Reader
, simplify tests further.
#3860
Conversation
PR HealthChangelog Entry ✔️
Changes to files need to be accounted for in their respective changelogs. |
6bce827
to
8d83144
Compare
8d83144
to
55c36f0
Compare
/// Methods behave as the `dart:io` methods with the same names, with some | ||
/// exceptions noted. | ||
abstract interface class Filesystem { | ||
Future<bool> exists(AssetId id); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A existsSync
wouldn't be useful? Or sync versions of read? Are there any "rules" for when calling the sync version or not?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It certainly will :) in this PR I just added what's already used.
The generator-facing interfaces, AssetReader
/AssetWriter
, are only async. We can add sync methods but we can't move existing async use to sync.
But for build_runner
internal use we can switch things to sync and it's probably better--benchmarking showed it gives about a 2x speedup. So one of the goals of this refactoring is to give build_runner
internals the choice.
/// | ||
/// Warning: this access to the filesystem bypasses reader functionality | ||
/// such as read tracking, caching and visibility restriction. | ||
Filesystem get filesystem; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this need to be on the interface?
It - at least mostly - seems to be used on the InMemoryAssetReaderWriter.
And it generally seems like something that shouldn't be used (having a warning and all) - are there other use cases than testing? If not, should it perhaps also be called something else? (e.g. filesystemForTesting
or something).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are correct that only test code uses this for now.
That's because non-test code was restricted to using the Reader
interface, which hides the underlying files.
Each generator has a different view on the filesystem, with some set of files hidden because they are "generated after" the current generator runs. Currently the import graph computation deals with that by checking the whole thing for each generator.
Through this refactoring build_runner
internals are allowed to bypass that abstraction and see the actual filesystem; in the next PRs they will also get access to information about what is different about the current generator's view.
Then they can be fast :) by building the base state once then applying the diff for the current generator on top.
}); | ||
} | ||
{Encoding encoding = utf8}) => | ||
filesystem.writeAsString(id, contents); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't you pass along the encoding as well?
Or not have options for enconding and always use utf8...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done, thanks.
This is from the generator-facing AssetWriter
interface, so no choice about the signature :)
/// An implementation of [AssetReader] and [AssetWriter] with primed in-memory | ||
/// assets. | ||
/// | ||
/// TODO(davidmorgan): merge into `FileBasedReader` and `FileBasedWriter`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why? Memory based and file based seems at odds.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it will end up as one class, with a different Filesystem
implementation for in-memory and file based.
Once all the optional functionality is refactored from inheritance hierarchy to members like AssetFinder
, it should be possible to get rid of AssetReaderState
, get rid of delegation, and have one concrete AssetReaderWriter
with all the interesting/optional functionality provided via those members.
The key thing allowing this refactoring is that the inheritance hierarchy / delegates are always built so they run in the same order--so for example caching always happens underneath per-generator visibility restriction. So actually that can just be "do I have visibility restriction, if so apply it, do I have a cache, if so use it" and so on :)
@override | ||
Future writeAsBytes(AssetId id, List<int> bytes) async { | ||
assets[id] = bytes; | ||
assets[id] = Uint8List.fromList(bytes); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe bytes is Uint8List ? bytes : Uint8List.fromList(bytes);
?
In general I think the interface should be changed, but probably not in this cl...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done, thanks.
This is generator-facing API so we can't easily change it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks :)
Some quite detailed responses about where this is headed--hopefully they make sense, happy to elaborate / discuss as needed. Thanks!
/// Methods behave as the `dart:io` methods with the same names, with some | ||
/// exceptions noted. | ||
abstract interface class Filesystem { | ||
Future<bool> exists(AssetId id); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It certainly will :) in this PR I just added what's already used.
The generator-facing interfaces, AssetReader
/AssetWriter
, are only async. We can add sync methods but we can't move existing async use to sync.
But for build_runner
internal use we can switch things to sync and it's probably better--benchmarking showed it gives about a 2x speedup. So one of the goals of this refactoring is to give build_runner
internals the choice.
/// | ||
/// Warning: this access to the filesystem bypasses reader functionality | ||
/// such as read tracking, caching and visibility restriction. | ||
Filesystem get filesystem; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are correct that only test code uses this for now.
That's because non-test code was restricted to using the Reader
interface, which hides the underlying files.
Each generator has a different view on the filesystem, with some set of files hidden because they are "generated after" the current generator runs. Currently the import graph computation deals with that by checking the whole thing for each generator.
Through this refactoring build_runner
internals are allowed to bypass that abstraction and see the actual filesystem; in the next PRs they will also get access to information about what is different about the current generator's view.
Then they can be fast :) by building the base state once then applying the diff for the current generator on top.
}); | ||
} | ||
{Encoding encoding = utf8}) => | ||
filesystem.writeAsString(id, contents); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done, thanks.
This is from the generator-facing AssetWriter
interface, so no choice about the signature :)
/// An implementation of [AssetReader] and [AssetWriter] with primed in-memory | ||
/// assets. | ||
/// | ||
/// TODO(davidmorgan): merge into `FileBasedReader` and `FileBasedWriter`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it will end up as one class, with a different Filesystem
implementation for in-memory and file based.
Once all the optional functionality is refactored from inheritance hierarchy to members like AssetFinder
, it should be possible to get rid of AssetReaderState
, get rid of delegation, and have one concrete AssetReaderWriter
with all the interesting/optional functionality provided via those members.
The key thing allowing this refactoring is that the inheritance hierarchy / delegates are always built so they run in the same order--so for example caching always happens underneath per-generator visibility restriction. So actually that can just be "do I have visibility restriction, if so apply it, do I have a cache, if so use it" and so on :)
@override | ||
Future writeAsBytes(AssetId id, List<int> bytes) async { | ||
assets[id] = bytes; | ||
assets[id] = Uint8List.fromList(bytes); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done, thanks.
This is generator-facing API so we can't easily change it.
For #3811.