Skip to content

Commit e835376

Browse files
committed
add a basic readme
1 parent 24af14e commit e835376

File tree

2 files changed

+222
-3
lines changed

2 files changed

+222
-3
lines changed

README.md

Lines changed: 220 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,224 @@
1-
# build [![Build Status](https://travis-ci.org/dart-lang/build.svg?branch=master)](https://travis-ci.org/dart-lang/build) [![Coverage Status](https://coveralls.io/repos/dart-lang/build/badge.svg?branch=master)](https://coveralls.io/r/dart-lang/build)
1+
# [![Build Status](https://travis-ci.org/dart-lang/build.svg?branch=master)](https://travis-ci.org/dart-lang/build) [![Coverage Status](https://coveralls.io/repos/dart-lang/build/badge.svg?branch=master)](https://coveralls.io/r/dart-lang/build)
22

3-
A build system for Dart.
3+
This package provides an way of generating files using Dart code, outside of
4+
`pub`. These files are generated directly on disk in the current package folder,
5+
and rebuilds are _incremental_.
6+
7+
## Running Builds
8+
9+
In order to run a build, you write a script which uses one of the three top
10+
level functions defined by this library:
11+
12+
- **build**: Runs a single build and exits.
13+
- **watch**: Continuously runs builds as you edit files.
14+
- **serve**: Same as `watch`, but also provides a basic file server which blocks
15+
if there are ongoing builds.
16+
17+
All three of these methods have a single required argument, a `PhaseGroup`. This
18+
is conceptually just a `List<Phase>` with some helper methods. Each of these
19+
`Phase`s runs sequentially, and blocks until the previous `Phase` is completed.
20+
21+
A single `Phase` may be composed of one or more `BuildAction`s, which are just
22+
a combination of a single `Builder` and a single `InputSet`. The `Builder` is
23+
what will actually generate outputs, and the `InputSet` determines what the
24+
primary inputs to that `Builder` will be. All `BuildAction`s in a `Phase` can
25+
be ran at the same time, and cannot read in the outputs of any other
26+
`BuildAction` in the same `Phase`.
27+
28+
Lets look at a very simple example, with a single `BuildAction`. You can ignore
29+
the `CopyBuilder` for now, just know that its a `Builder` which copies files:
30+
31+
```dart
32+
import 'package:build/build.dart';
33+
34+
main() async {
35+
/// The [PhaseGroup#singleAction] constructor is a shorthand for:
36+
///
37+
/// new PhaseGroup().newPhase().addAction(builder, inputSet);
38+
await build(new PhaseGroup.singleAction(
39+
new CopyBuilder('.copy'), new InputSet('my_package', ['lib/*.dart'])));
40+
}
41+
```
42+
43+
The above example would copy all `*.dart` files directly under `lib` to
44+
corresponding `*.dart.copy` files. Each time you run a build, it will check for
45+
any changes to the input files, and rerun the `CopyBuilder` only for the inputs
46+
that actually changed.
47+
48+
You can add as many actions as you want to the first phase using the `addAction`
49+
method, and they will all run at the same time. For example, you could make
50+
multiple copies:
51+
52+
```dart
53+
main() async {
54+
var inputs = new InputSet('my_package', ['lib/*.dart']);
55+
await build(new PhaseGroup().newPhase()
56+
..addAction(new CopyBuilder('.copy1'), inputs)
57+
..addAction(new CopyBuilder('.copy2'), inputs));
58+
}
59+
```
60+
61+
Lets say however, that you want to make a copy of one of your copies. Since
62+
no action can read outputs of another action in the same phase, you need to add
63+
an additional `Phase`:
64+
65+
```dart
66+
main() async {
67+
var phases = new PhaseGroup();
68+
group.newPhase().addAction(
69+
new CopyBuilder('.copy'), new InputSet('my_package', ['lib/*.dart']));
70+
group.newPhase().addAction(
71+
new CopyBuilder('.bak'), new InputSet('my_package', ['lib/*.dart.copy']));
72+
73+
await build(phases);
74+
}
75+
```
76+
77+
This time, all the `*.dart.copy` files will be created first, and then the next
78+
`Phase` will read those in and create additional `*.dart.copy.bak` files. You
79+
can add as many phases as you want, but in general it's better to add more
80+
actions to a single phase since they can run at the same time.
81+
82+
**Note**: Any time you change your build script (or any of its dependencies),
83+
the next build will be a full rebuild. This is because the system has no way
84+
of knowing how that change may have affected the outputs.
85+
86+
### Inputs
87+
88+
Valid inputs follow the general dart package rules. You can read any files under
89+
the top level `lib` folder any package dependency, and you can read all files
90+
from the current package.
91+
92+
In general it is best to be as specific as possible with your `InputSet`s,
93+
because all matching files will be provided to `declareOutputs`.
94+
95+
### Outputs
96+
97+
You may only output files in the current package, but anywhere in the current
98+
package is allowed.
99+
100+
You are not allowed to overwrite existing files, only create new ones.
101+
102+
Outputs from previous builds will not be treated as inputs to later ones.
103+
104+
### Source control
105+
106+
This package creates a top level `.build` folder in your package, which should
107+
not be submitted to your source control repo (likely this just means adding
108+
'.build' to your '.gitignore' file).
109+
110+
When it comes to generated files it is generally best to not submit them to
111+
source control, but a specific `Builder` may provide a recommendation otherwise.
112+
113+
It should be noted that if you do submit generated files to your repo then when
114+
you change branches or merge in changes you may get a warning on your next build
115+
about declared outputs that already exist. This will be followed up with a
116+
prompt to delete those files. You can type `l` to list the files, and then type
117+
`y` to delete them if everything looks correct. If you think something is wrong
118+
you can type `n` to abandon the build without taking any action.
119+
120+
### Publishing packages
121+
122+
In general generated files should be published with your package, but this may
123+
not always be the case. Some `Builder`s may provide a recommendation for this as
124+
well.
125+
126+
## Implementing your own Builders
127+
128+
If you have written a pub `Transformer` in the past, then the `Builder`api
129+
should be familiar to you. The main difference is that `Builders` must always
130+
declare their outputs, similar to a `DeclaringTransformer`.
131+
132+
The basic api looks like this:
133+
134+
```dart
135+
abstract class Builder {
136+
/// You can only output files in `build` that you declare here. You are not
137+
/// required to output all of these files, but no other [Builder] is allowed
138+
/// to declare the same outputs.
139+
List<AssetId> declareOutputs(AssetId input);
140+
141+
/// Similar to `Transformer.apply`. This is where you build and output files.
142+
Future build(BuildStep buildStep);
143+
}
144+
```
145+
146+
Building on the example in [Running Builds](#running-builds), here is an
147+
implementation of a `Builder` which just copies files to other files with the
148+
same name, but an additional extension:
149+
150+
```dart
151+
/// A really simple [Builder], it just makes copies!
152+
class CopyBuilder implements Builder {
153+
final String extension;
154+
155+
CopyBuilder(this.extension)
156+
157+
Future build(BuildStep buildStep) async {
158+
/// Each [buildStep] has just one input. This is a fully realized [Asset]
159+
/// with its [stringContents] already available.
160+
var input = buildStep.input;
161+
162+
/// Create a new [Asset], with the new [AssetId].
163+
var copy = new Asset(_copiedId(input.id), input.stringContents);
164+
165+
/// Write out the [Asset].
166+
///
167+
/// There is no need to `await` here, the system handles waiting on these
168+
/// files as necessary before advancing to the next phase.
169+
buildStep.writeAsString(copy);
170+
}
171+
172+
/// Declare your outputs, just one file in this case.
173+
List<AssetId> declareOutputs(AssetId inputId) => [_copiedId(inputId)];
174+
175+
AssetId _copiedId(AssetId inputId) => inputId.addExtension('$extension');
176+
}
177+
```
178+
179+
It should be noted that you should _never_ touch the file system directly. Go
180+
through the `buildStep#readAsString` and `buildStep#writeAsString` methods in
181+
order to read and write assets. This is what enables the package to track all of
182+
your dependencies and do incremental rebuilds. It is also what enables your
183+
`Builder` to run on different environments.
184+
185+
### Using the analyzer (**experimental**)
186+
187+
If you need to do analyzer resolution, you can use the `BuildStep#resolve`
188+
method. This makes sure that all `Builder`s in the system share the same
189+
analysis context, which greatly speeds up the overall system when multiple
190+
`Builder`s are doing resolution. Additionally, it handles for you making the
191+
analyzer work in an async environment.
192+
193+
This `Resolver` has the exact same api as the one from `code_transformers`, so
194+
migrating to it should be easy if you have used `code_transformers` in the past.
195+
196+
Here is an example of a `Builder` which uses the `resolve` method:
197+
198+
```dart
199+
class ResolvingCopyBuilder {
200+
Future build(BuildStep buildStep) {
201+
/// Resolves all libraries reachable from the primary input.
202+
var resolver = await buildStep.resolve(buildStep.input.id);
203+
/// Get a [LibraryElement] by asset id.
204+
var entryLib = resolver.getLibrary(buildStep.input.id);
205+
/// Or get a [LibraryElement] by name.
206+
var otherLib = resolver.getLibraryByName('my.library');
207+
208+
/// **IMPORTANT**: If you don't release a resolver, your builds will hang.
209+
resolver.release();
210+
}
211+
212+
/// Declare outputs as well....
213+
}
214+
```
215+
216+
Once you have gotten a `LibraryElement` using one of the methods on `Resolver`,
217+
you are now just using the regular `analyzer` package to explore your app.
218+
219+
**Important Note**: As shown in the code above, you _must_ call `release` on
220+
your resolver when you are done. If you don't then the next call to `resolve`
221+
will never complete.
4222

5223
## Features and bugs
6224

lib/src/generate/build_impl.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,8 @@ class BuildImpl {
344344

345345
stdout.writeln('Found ${conflictingOutputs.length} declared outputs '
346346
'which already exist on disk. This is likely because the `.build` '
347-
'folder was deleted.');
347+
'folder was deleted, or you are submitting generated files to your '
348+
'source repository.');
348349
var done = false;
349350
while (!done) {
350351
stdout.write('Delete these files (y/n) (or list them (l))?: ');

0 commit comments

Comments
 (0)