Source code generation has become an important software development tool when building and maintaining a large number of data models, data access object, widgets, etc. The premise of source code generation is that we can specify (hopefully few) details and flesh out the rest of the classes, and methods during the build process.
The library merging_builder
includes the following (synthetic input) builder classes:
-
MergingBuilder
reads several input files and writes merged output to one output file to a custom location. -
StandaloneBuilder
reads one or several input files and writes standalone files to a custom location. In this context, standalone means the output files may be written to a custom folder and not only the extension but the name of the output file can be configured (as opposed to using part files).
Following the example of source_gen
, it is common practice to
separate builders and generators from the code using those builders.
In the example project provided, the package defining a new
builder is called researcher_builder
and the package using this builder is called researcher
.
To set up a build system the following steps are required:
-
Get dependencies: Include
merging_builder
,build
as dependencies in the filepubspec.yaml
. In the example mentioned here, the generator also requires the packagesanalyzer
andsource_gen
. -
Create a generator: Create a custom generator that extends
MergingGenerator
. In examplegenerateItemForAnnotatedElement
reads a list of strings whilegenerateMergedContent
merges the data and generates output that is written to researchers.dart. -
Create a builder: Create an instance of
MergingBuilder
. Following the example ofsource_gen
, builders are typically placed in a file called:builder.dart
located in thelib
folder of the builder package.- Input sources may be specified using wildcard characters supported by
Glob
. - The builder definition includes the default values of the options
input_files
,output_file
,header
,footer
, andsort_assets
. These options can be overwritten by the user of the builder by specifying them explicitly in the filebuild.yaml
located in the packageresearcher
(see step 1 in section User Package Setup).
- Input sources may be specified using wildcard characters supported by
-
Add a builder configuration: The build extensions for
MergingBuilder
must be specified using the notation available for synthetic input. For example,"$lib$"
indicates that the input files are located in the folderlib
or a subfolder thereof.builders: add_names_builder: import: "package:researcher_builder/builder.dart" builder_factories: ["addNamesBuilder"] build_extensions: {"lib/$lib$": ["lib/output.dart"]} auto_apply: root_package build_to: source assistant_builder: import: "package:researcher_builder/builder.dart" builder_factories: ["assistantBuilder"] build_extensions: {"lib/$lib$": ["*.dart"]} auto_apply: root_package build_to: source
The following steps are performed in the package that is using the custom builder:
-
Add dependencies: Add the custom builder package and
build_runner
as a dev_dependencies in the filepubspec.yaml
. -
Builder configuration: Add the builder to the list of known builders and configure the available options (thus overwriting the default values). A sample
build.yaml
file is shown below.targets: $default: builders: # Configure the builder `pkg_name|builder_name` researcher_builder|add_names_builder: # Only run this builder on the specified input. enabled: true # generate_for: # - lib/*.dart options: input_files: 'lib/input/*.dart' output_file: 'lib/output/researchers.dart' sort_assets: false header: '// Header specified in build.yaml.' footer: '// Footer specified in build.yaml.' researcher_builder|assistant_builder: enabled: true options: input_files: 'lib/input/*.dart' output_files: 'lib/output/assistant_(*).dart' root: ''
-
Initiate the build process by using the command:
# dart run build_runner build --delete-conflicting-outputs --verbose
MergingBuilder
reads several input files and writes merged output to one output file.
The builder provides the option to sort the input files in reverse topological order.
If the input file a.dart
includes file b.dart
then a.dart
will be listed after b.dart
. This option may be useful when
generating code that needs to list variables or call functions in order of dependence.
To enable topological sorting set the constructor parameter sortAsset: true
. Note: If sorting of input assets is enabled, input files must not include each other directly or indirectly.
A conventional builder typically calls the generator method generate
from within
its build
method to retrieve the generated source-code. MergingBuilder
calls the MergingGenerator
method generateStream
.
It allows the generator to pass a stream of data-type T
to the builder,
one stream item for each annotated element processed to the generator method generateStreamItemForAnnotatedElement
.
The private builder method _combineStreams
combines the streams received for each processed input file and calls the generator method generateMergedContent
.
As a result, this method has access to all stream items of type T
generated f
or each element annotated with an annotation of type A
in each input file.
It is the task of this method to generate the merged source-code output.
The figure below shows the flow of data between the builder and the generator. The data type is indicated by the starting point of the connectors. Dotted connectors represent a stream of data.
StandaloneBuilder
reads input files and writes
corresponding output files to a custom location.
The input file path (constructor parameter inputFiles
) may include
wild-card notation supported by Glob
.
Output files are specified by using the custom symbol
(*)
. For example, the output path output\assistant_(*).dart
is interpreted such that (*)
is replaced with the input file name (excluding the file extension). For more details, see the file example\researcher_builder\builder.dart
.
Limitations: For builders extending StandaloneBuilder
it is recommended to initiate the build command
from the root directory of the package the build is applied to.
For further information on how to use MergingBuilder
see example.
Please file feature requests and bugs at the issue tracker.