merging_builder 0.0.9

  • Readme
  • Changelog
  • Example
  • Installing
  • 60

Merging Builder #

Build Status

Introduction #

Source code generation has become an integral software development tool when building and maintaining a large number of data models, data access object, widgets, etc.

The library merging_builder provides a Dart builder that reads several input files and writes merged output to one output file. The builder has support for specifying a header and footer to be placed at the top and bottom of the output file.

A conventional builder typically calls the generator method generate from within its build method to retrieve the generated source-code. MergingBuilder (indirectly) calls the MergingGenerator method generateStream. It allows the generator to pass a stream of data of type T to the builder, one stream item for each annotated element passed to the generator method generateStreamItemForAnnotatedElement.

The private builder method _combineStreams combines the streams received for each processed file asset and calls the generator method generateMergedContent. As a result, this method has access to all stream items of type T generated for each annotated element 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 orange connectors. Dotted connectors represent a stream of data.

Directed Graph Image

Usage #

Following the example of source_gen, it is common practice to separate builders and generators from the code using those builders.

In the example provided with this library, 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:

  1. Include merging_builder and build as dependencies in the file pubspec.yaml of the package defining the builder. (In the example mentioned here, the generator also requires the packages analyzer and source_gen.)

  2. In the package defining the custom builder, create a custom generator that extends MergingGenerator. Users will have to implement the methods generateItemForAnnotatedElement and generateMergedContent. In the example shown below generateItemForAnnotatedElement reads a list of strings while generateMergedContent merges the data and generates output that is written to researchers.dart.

    Show details.
     import 'dart:async';
     import 'package:analyzer/dart/element/element.dart';
     import 'package:build/src/builder/build_step.dart';
     import 'package:merging_builder/merging_builder.dart';
     import 'package:merging_builder/src/annotations/add_names.dart';
     import 'package:source_gen/source_gen.dart';
     import 'package:quote_buffer/quote_buffer.dart';
    
     /// Reads numbers from annotated classes and emits the sum.
     class AddNamesGenerator extends MergingGenerator<List<String>, AddNames> {
       /// Portion of source code included at the top of the generated file.
       /// Should be specified as header when constructing the merging builder.
       static String get header {
         return '/// Added names.';
       }
    
       /// Portion of source code included at the very bottom of the generated file.
       /// Should be specified as [footer] when constructing the merging builder.
       static String get footer {
         return '/// This is the footer.';
       }
    
       @override
       List<String> generateStreamItemForAnnotatedElement(
         Element element,
         ConstantReader annotation,
         BuildStep buildStep,
       ) {
         final List<String> result = [];
         if (element is ClassElement) {
           final nameObjects =
               element.getField('names')?.computeConstantValue()?.toListValue();
           if (nameObjects != null) {
             for (final nameObj in nameObjects) {
               result.add(nameObj.toStringValue());
             }
             return result;
           }
         }
         return null;
       }
    
       /// Returns merged content.
       @override
       FutureOr<String> generateMergedContent(Stream<List<String>> stream) async {
         final b = QuoteBuffer();
         int i = 0;
         final List<List<String>> allNames = [];
         // Iterate over stream:
         await for (final names in stream) {
           b.write('final name$i = [');
           b.writelnAllQ(names, separator2: ',');
           b.writeln('];');
           ++i;
           allNames.add(names);
         }
    
         b.writeln('');
         b.writeln('final List<List<String>> names = [');
         for (var names in allNames) {
           b.writeln('  [');
           b.writelnAllQ(names, separator2: ',');
           b.writeln('  ],');
         }
         b.writeln('];');
         return b.toString();
       }
     }
    
  3. Create an instance of MergingBuilder. Following the example of source_gen, builders are typically placed in a file called: builder.dart located in the lib folder of the builder package. The generator AddNamesGenerator extends MergingGenerator<List<String>, AddNames> (see step 2). Input sources may be specified using wildcard characters supported by Glob. The builder definition shown below honours the options input_files, output_file, header, footer, and sort_assets that can be set in build.yaml (see step 5).

      import 'package:build/build.dart';
      import 'package:merging_builder/merging_builder.dart';
    
      import 'generators/add_names_generator.dart';
    
      /// Defines a merging builder.
      /// Honours the options: `input_files`, `output_file`, `header`, `footer`,
      /// and `sort_assets` that can be set in `build.yaml`.
      Builder addNamesBuilder(BuilderOptions options) {
        BuilderOptions defaultOptions = BuilderOptions({
          'input_files': 'lib/*.dart',
          'output_file': 'lib/output.dart',
          'header': AddNamesGenerator.header,
          'footer': AddNamesGenerator.footer,
          'sort_assets': false,
        });
    
        // Apply user set options.
        options = defaultOptions.overrideWith(options);
        return MergingBuilder<List<String>>(
          generator: AddNamesGenerator(),
          inputFiles: options.config['input_files'],
          outputFile: options.config['output_file'],
          header: options.config['header'],
          footer: options.config['footer'],
          sortAssets: options.config['sort_assets'],
        );
      }
    
  4. In the package defining the builder, add the builder configuration for the builder add_names_builder (see below). 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 folder lib or a subfolder thereof. For more information consult the section: Writing a Builder using a synthetic input found in the documentation of the Dart package build.

     builders:
       add_names_builder:
         import: "package:researcher_builder/builder.dart"
         builder_factories: ["addNamesBuilder"]
         build_extensions: {"$lib$": ["*.dart"]}
         auto_apply: root_package
         build_to: source
         builders:
    
  5. In the package using the custom builder, researcher, add add_names_builder to the list of known builders. The file build.yaml is shown below.

      targets:
        $default:
          builders:
            # Configure the builder `pkg_name|builder_name`
            researcher_builder|add_names_builder:
              enabled: true
              # Only run this builder on the specified input.
              # generate_for:
              #   - lib/*.dart
              options:
               input_files: 'lib/input/*.dart'
               output_file: 'lib/researchers.dart'
               sort_assets: true
               header: '// Header specified in build.yaml.'
               footer: '// Footer specified in build.yaml.'
    
  6. In the package using the builder, researcher, add researcher_builder and build_runner as dev_dependencies in the file pubspec.yaml.

     name: researcher
       description:
         Example demonstrating how to use the library merging_builder.
    
       environment:
         sdk: '>=2.8.1 <3.0.0'
    
       dev_dependencies:
         build_runner: ^1.10.0
         researcher_builder:
           path: ../researcher_builder
    
  7. Initiate the build process by using the command:

    # pub run build_runner build --delete-conflicting-outputs --verbose
    

Examples #

For further information on how to use MergingBuilder see example.

Features and bugs #

Please file feature requests and bugs at the issue tracker.

0.0.9 #

Extended example researcher_builder. The builder addNamesBuilder now handles BuilderOptions specified in the build.yaml file of the package consuming the config settings, researcher.

0.0.8 #

Amended docs and hyperlinks. Changed MergingBuilder log message type from log.info to log.fine.

0.0.7 #

Changed MergingBuilder log message type from log.fine to log.info.

0.0.6 #

Updated to latest version of directed_graph.

0.0.5 #

Changed format of README.md hyperlinks.

0.0.4 #

Amended typo in README.md.

0.0.3 #

Amended docs.

0.0.2 #

Amended docs, renamed builder method combinedStream to combineStreams. Renamed generator method mergedContent to generateMergedContent.

0.0.1 #

Initial version of the library.

example/README.md

Merging Builder - Example #

Build Status

Introduction #

The package merging_builder provides a Dart builder that reads several input files and writes the merged output to one output file.

The example presented in this folder contains two packages. The package researcher_builder depends on merging_builder in order to define the builder add_names_builder and the merging generator add_names_generator.

The package researcher depends on researcher_builder, specified as a dev_dependency, in order to access the builder add_names_builder during the build process.

Build Setup #

Step by step instructions on how to set up and configure a MergingBuilder are provided in the section usage.

Features and bugs #

Please file feature requests and bugs at the issue tracker.

Use this package as a library

1. Depend on it

Add this to your package's pubspec.yaml file:


dependencies:
  merging_builder: ^0.0.9

2. Install it

You can install packages from the command line:

with pub:


$ pub get

Alternatively, your editor might support pub get. Check the docs for your editor to learn more.

3. Import it

Now in your Dart code, you can use:


import 'package:merging_builder/merging_builder.dart';
  
Popularity:
Describes how popular the package is relative to other packages. [more]
24
Health:
Code health derived from static analysis. [more]
100
Maintenance:
Reflects how tidy and up-to-date the package is. [more]
90
Overall:
Weighted score of the above. [more]
60
Learn more about scoring.

We analyzed this package on Jul 9, 2020, and provided a score, details, and suggestions below. Analysis was completed with status completed using:

  • Dart: 2.8.4
  • pana: 0.13.14

Analysis suggestions

Package not compatible with SDK flutter

Because it is not compatible with any of the supported runtimes: flutter-native, flutter-web

Package not compatible with runtime flutter-native on android

Because:

  • package:merging_builder/merging_builder.dart that imports:
  • package:merging_builder/src/generators/merging_generator.dart that imports:
  • package:source_gen/source_gen.dart that imports:
  • package:source_gen/src/type_checker.dart that imports:
  • dart:mirrors

Package not compatible with runtime flutter-native on ios

Because:

  • package:merging_builder/merging_builder.dart that imports:
  • package:merging_builder/src/generators/merging_generator.dart that imports:
  • package:source_gen/source_gen.dart that imports:
  • package:source_gen/src/type_checker.dart that imports:
  • dart:mirrors

Package not compatible with runtime flutter-native on linux

Because:

  • package:merging_builder/merging_builder.dart that imports:
  • package:merging_builder/src/generators/merging_generator.dart that imports:
  • package:source_gen/source_gen.dart that imports:
  • package:source_gen/src/type_checker.dart that imports:
  • dart:mirrors

Package not compatible with runtime flutter-native on macos

Because:

  • package:merging_builder/merging_builder.dart that imports:
  • package:merging_builder/src/generators/merging_generator.dart that imports:
  • package:source_gen/source_gen.dart that imports:
  • package:source_gen/src/type_checker.dart that imports:
  • dart:mirrors

Package not compatible with runtime flutter-native on windows

Because:

  • package:merging_builder/merging_builder.dart that imports:
  • package:merging_builder/src/generators/merging_generator.dart that imports:
  • package:source_gen/source_gen.dart that imports:
  • package:source_gen/src/type_checker.dart that imports:
  • dart:mirrors

Package not compatible with runtime flutter-web on web

Because:

  • package:merging_builder/merging_builder.dart that imports:
  • package:merging_builder/src/generators/merging_generator.dart that imports:
  • package:source_gen/source_gen.dart that imports:
  • package:source_gen/src/utils.dart that imports:
  • package:build/build.dart that imports:
  • package:build/src/generate/run_post_process_builder.dart that imports:
  • package:build/src/builder/post_process_builder.dart that imports:
  • package:build/src/builder/builder.dart that imports:
  • package:build/src/builder/build_step.dart that imports:
  • package:build/src/asset/reader.dart that imports:
  • package:glob/glob.dart that imports:
  • package:glob/src/list_tree.dart that imports:
  • package:glob/src/io.dart that imports:
  • package:glob/src/io_export.dart that imports:
  • dart:io

Package not compatible with runtime js

Because:

  • package:merging_builder/merging_builder.dart that imports:
  • package:merging_builder/src/generators/merging_generator.dart that imports:
  • package:source_gen/source_gen.dart that imports:
  • package:source_gen/src/utils.dart that imports:
  • package:build/build.dart that imports:
  • package:build/src/generate/run_post_process_builder.dart that imports:
  • package:build/src/builder/post_process_builder.dart that imports:
  • package:build/src/builder/builder.dart that imports:
  • package:build/src/builder/build_step.dart that imports:
  • package:build/src/asset/reader.dart that imports:
  • package:glob/glob.dart that imports:
  • package:glob/src/list_tree.dart that imports:
  • package:glob/src/io.dart that imports:
  • package:glob/src/io_export.dart that imports:
  • dart:io

Package not compatible with runtime native-aot

Because:

  • package:merging_builder/merging_builder.dart that imports:
  • package:merging_builder/src/generators/merging_generator.dart that imports:
  • package:source_gen/source_gen.dart that imports:
  • package:source_gen/src/type_checker.dart that imports:
  • dart:mirrors

Maintenance suggestions

Package is pre-v0.1 release. (-10 points)

While nothing is inherently wrong with versions of 0.0.*, it might mean that the author is still experimenting with the general direction of the API.

Dependencies

Package Constraint Resolved Available
Direct dependencies
Dart SDK >=2.8.1 <3.0.0
analyzer ^0.39.10 0.39.12
build ^1.3.0 1.3.0
dart_style ^1.3.6 1.3.6
directed_graph ^0.1.4 0.1.6
glob ^1.2.0 1.2.0
meta ^1.1.8 1.2.2 1.3.0-nullsafety
path ^1.7.0 1.7.0
quote_buffer ^0.1.3 0.1.3
source_gen ^0.9.5 0.9.5
Transitive dependencies
_fe_analyzer_shared 5.0.0
args 1.6.0
async 2.4.2
charcode 1.1.3
collection 1.14.13 1.15.0-nullsafety
convert 2.1.1
crypto 2.1.5
csslib 0.16.1
graphs 0.2.0
html 0.14.0+3
js 0.6.2
lazy_evaluation 0.0.6+3
logging 0.11.4
node_interop 1.1.1
node_io 1.1.1
package_config 1.9.3
pedantic 1.9.2
pub_semver 1.4.4
source_span 1.7.0
string_scanner 1.0.5
term_glyph 1.1.0
typed_data 1.2.0 1.3.0-nullsafety
watcher 0.9.7+15
yaml 2.2.1
Dev dependencies
build_runner ^1.10.0
build_test ^1.2.0
source_gen_test ^0.1.1
test ^1.15.1