1
- Defines the basic pieces of how a build happens and how they interact.
1
+ _ Questions? Suggestions? Found a bug? Please
2
+ [ file an issue] ( https://github.com/dart-lang/build/issues ) or
3
+ [ start a discussion] ( https://github.com/dart-lang/build/discussions ) ._
2
4
3
- ## [ ` Builder ` ] [ dartdoc:Builder ]
5
+ Package for writing code generators, called _ builders_ , that run with
6
+ [ build_runner] ( https://pub.dev/packages/build_runner ) .
4
7
5
- The business logic for code generation. Most consumers of the ` build ` package
6
- will create custom implementations of ` Builder ` .
8
+ - [ See also: source_gen] ( #see-also-source_gen )
9
+ - [ The ` Builder ` interface] ( #the-builder-interface )
10
+ - [ Using the analyzer] ( #using-the-analyzer )
11
+ - [ Examples] ( #examples )
7
12
8
- ## [ ` BuildStep ` ] [ dartdoc:BuildStep ]
13
+ ## See also: source_gen
9
14
10
- The way a ` Builder ` interacts with the outside world. Defines the unit of work
11
- and allows reading/writing files and resolving Dart source code.
15
+ The [ source_gen] ( https://pub.dev/packages/source_gen ) package provides helpers
16
+ for writing common types of builder, for example builders that are triggered by
17
+ a particular annotation.
12
18
13
- ## [ ` Resolver ` ] [ dartdoc:Resolver ] class
19
+ Most builders should use ` source_gen ` , but it's still useful to learn about the
20
+ underlying ` Builder ` interface that ` source_gen ` plugs into.
14
21
15
- An interface into the dart [ analyzer] [ pub:analyzer ] to allow resolution of code
16
- that needs static analysis and/or code generation.
22
+ ## The ` Builder ` interface
17
23
18
- ## Implementing your own Builders
19
-
20
- A ` Builder ` gets invoked one by one on it's inputs, and may read other files and
21
- output new files based on those inputs.
22
-
23
- The basic API looks like this:
24
+ A builder implements the
25
+ [ Builder] ( https://pub.dev/documentation/build/latest/build/Builder-class.html )
26
+ interface.
24
27
25
28
``` dart
26
29
abstract class Builder {
27
- /// You can only output files that are configured here by suffix substitution.
28
- /// You are not required to output all of these files, but no other builder
29
- /// may declare the same outputs.
30
+ /// Declares inputs and outputs by extension.
30
31
Map<String, List<String>> get buildExtensions;
31
32
32
- /// This is where you build and output files .
33
+ /// Builds all outputs for a single input .
33
34
FutureOr<void> build(BuildStep buildStep);
34
35
}
35
36
```
36
37
37
- Here is an implementation of a ` Builder ` which just copies files to other files
38
- with the same name, but an additional extension:
38
+ Its ` buildExtensions ` getter declares what inputs it runs on and what outputs
39
+ it might produce.
39
40
40
- ``` dart
41
- import 'package:build/build.dart';
41
+ During a build its ` build ` method gets called once per matching input.
42
+
43
+ It uses ` buildStep ` to read, analyze and write files.
42
44
43
- /// A really simple [Builder], it just makes copies of .txt files!
45
+ For example, here is a builder that copies input ` .txt ` files to ` .txt.copy ` files:
46
+
47
+ ``` dart
44
48
class CopyBuilder implements Builder {
45
49
@override
46
50
final buildExtensions = const {
@@ -49,106 +53,34 @@ class CopyBuilder implements Builder {
49
53
50
54
@override
51
55
Future<void> build(BuildStep buildStep) async {
52
- // Each `buildStep` has a single input.
53
- var inputId = buildStep.inputId;
56
+ // Create the output ID from the build step input ID.
57
+ final inputId = buildStep.inputId;
58
+ final outputId = inputId.addExtension('.copy');
54
59
55
- // Create a new target `AssetId` based on the old one.
56
- var copy = inputId.addExtension('.copy');
57
- var contents = await buildStep.readAsString(inputId);
58
-
59
- // Write out the new asset.
60
- await buildStep.writeAsString(copy, contents);
60
+ // Read from the input, write to the output.
61
+ final contents = await buildStep.readAsString(inputId);
62
+ await buildStep.writeAsString(outputId, contents);
61
63
}
62
64
}
63
65
```
64
66
65
- It should be noted that you should _ never_ touch the file system directly. Go
66
- through the ` buildStep#readAsString ` and ` buildStep#writeAsString ` methods in
67
- order to read and write assets. This is what enables the package to track all of
68
- your dependencies and do incremental rebuilds. It is also what enables your
69
- [ ` Builder ` ] [ dartdoc:Builder ] to run on different environments.
67
+ Outputs are optional. For example, a builder that is activated by a particular
68
+ annotation will output nothing if it does not find that annotation.
70
69
71
70
### Using the analyzer
72
71
73
- If you need to do analyzer resolution, you can use the ` BuildStep#resolver `
74
- object. This makes sure that all ` Builder ` s in the system share the same
75
- analysis context, which greatly speeds up the overall system when multiple
76
- ` Builder ` s are doing resolution.
77
-
78
- Here is an example of a ` Builder ` which uses the ` resolve ` method:
79
-
80
- ``` dart
81
- import 'package:build/build.dart';
82
-
83
- class ResolvingCopyBuilder implements Builder {
84
- // Take a `.dart` file as input so that the Resolver has code to resolve
85
- @override
86
- final buildExtensions = const {
87
- '.dart': ['.dart.copy']
88
- };
89
-
90
- @override
91
- Future<void> build(BuildStep buildStep) async {
92
- // Get the `LibraryElement` for the primary input.
93
- var entryLib = await buildStep.inputLibrary;
94
- // Resolves all libraries reachable from the primary input.
95
- var resolver = buildStep.resolver;
96
- // Get a `LibraryElement` for another asset.
97
- var libFromAsset = await resolver.libraryFor(
98
- AssetId.resolve(Uri.parse('some_import.dart'),
99
- from: buildStep.inputId));
100
- // Or get a `LibraryElement` by name.
101
- var libByName = await resolver.findLibraryByName('my.library');
102
- }
103
- }
104
- ```
105
-
106
- Once you have gotten a ` LibraryElement ` using one of the methods on ` Resolver ` ,
107
- you are now just using the regular ` analyzer ` package to explore your app.
108
-
109
- ### Sharing expensive objects across build steps
110
-
111
- The build package includes a ` Resource ` class, which can give you an instance
112
- of an expensive object that is guaranteed to be unique across builds, but may
113
- be re-used by multiple build steps within a single build (to the extent that
114
- the implementation allows). It also gives you a way of disposing of your
115
- resource at the end of its lifecycle.
116
-
117
- The ` Resource<T> ` constructor takes a single required argument which is a
118
- factory function that returns a ` FutureOr<T> ` . There is also a named argument
119
- ` dispose ` which is called at the end of life for the resource, with the
120
- instance that should be disposed. This returns a ` FutureOr<dynamic> ` .
121
-
122
- So a simple example ` Resource ` would look like this:
123
-
124
- ``` dart
125
- final resource = Resource(
126
- () => createMyExpensiveResource(),
127
- dispose: (instance) async {
128
- await instance.doSomeCleanup();
129
- });
130
- ```
131
-
132
- You can get an instance of the underlying resource by using the
133
- ` BuildStep#fetchResource ` method, whose type signature looks like
134
- ` Future<T> fetchResource<T>(Resource<T>) ` .
135
-
136
- ** Important Note** : It may be tempting to try and use a ` Resource ` instance to
137
- cache information from previous build steps (or even assets), but this should
138
- be avoided because it can break the soundness of the build, and may introduce
139
- subtle bugs for incremental builds (remember the whole build doesn't run every
140
- time!). The ` build ` package relies on the ` BuildStep#canRead ` and
141
- ` BuildStep#readAs* ` methods to track build step dependencies, so sidestepping
142
- those can and will break the dependency tracking, resulting in inconsistent and
143
- stale assets.
72
+ The ` buildStep ` passed to ` build ` gives easy access to the analyzer for
73
+ processing Dart source code.
144
74
145
- ## Features and bugs
75
+ Use ` buildStep.resolver.compilationUnitFor ` to parse a single source file, or
76
+ ` libraryFor ` to fully resolve it. This can be used to introspect the full API
77
+ of the source code the builder is running on. For example, a builder can learn
78
+ enough about a class's field and field types to correctly serialize it.
146
79
147
- Please file feature requests and bugs at the [ issue tracker ] [ tracker ] .
80
+ ## Examples
148
81
149
- [ tracker ] : https://github.com/dart-lang/build/issues
82
+ There are some simple examples
83
+ [ in the build repo] ( https://github.com/dart-lang/build/blob/master/example ) .
150
84
151
- [ dartdoc:Builder ] : https://pub.dev/documentation/build/latest/build/Builder-class.html
152
- [ dartdoc:BuildStep ] : https://pub.dev/documentation/build/latest/build/BuildStep-class.html
153
- [ dartdoc:Resolver ] : https://pub.dev/documentation/build/latest/build/Resolver-class.html
154
- [ pub:analyzer ] : https://pub.dev/packages/analyzer
85
+ For real examples of builders, see the list of popular builders in the
86
+ [ build_runner package documentation] ( https://pub.dev/packages/build_runner ) .
0 commit comments