Generate one file for a list of parsed files using

2019-07-11 06:04发布

问题:

I have a list of models that I need to create a mini reflective system.
I analyzed the Serializable package and understood how to create one generated file per file, however, I couldn't find how can I create one file for a bulk of files.

So, how to dynamically generate one file, using source_gen, for a list of files?

Example:
Files
user.dart
category.dart

Generated:
info.dart (containg information from user.dart and category.dart)

回答1:

Found out how to do it with the help of people in Gitter.
You must have one file, even if empty, to call the generator. In my example, it is lib/batch.dart.

source_gen: ^0.5.8

Here is the working code:

The tool/build.dart

import 'package:build_runner/build_runner.dart';
import 'package:raoni_global/phase.dart';

main() async {
  PhaseGroup pg = new PhaseGroup()
    ..addPhase(batchModelablePhase(const ['lib/batch.dart']));

  await build(pg,
      deleteFilesByDefault: true);
}

The phase:

batchModelablePhase([Iterable<String> globs =
const ['bin/**.dart', 'web/**.dart', 'lib/**.dart']]) {
  return new Phase()
    ..addAction(
        new GeneratorBuilder(const
        [const BatchGenerator()], isStandalone: true
        ),
        new InputSet(new PackageGraph.forThisPackage().root.name, globs));
}

The generator:

import 'dart:async';

import 'package:analyzer/dart/element/element.dart';
import 'package:build/build.dart';
import 'package:source_gen/source_gen.dart';
import 'package:glob/glob.dart';
import 'package:build_runner/build_runner.dart';

class BatchGenerator extends Generator {
  final String path;

  const BatchGenerator({this.path: 'lib/models/*.dart'});

  @override
  Future<String> generate(Element element, BuildStep buildStep) async {
    // this makes sure we parse one time only
    if (element is! LibraryElement)
      return null;

    String libraryName = 'raoni_global', filePath = 'lib/src/model.dart';
    String className = 'Modelable';

    // find the files at the path designed
    var l = buildStep.findAssets(new Glob(path));

    // get the type of annotation that we will use to search classes
    var resolver = await buildStep.resolver;
    var assetWithAnnotationClass = new AssetId(libraryName, filePath);
    var annotationLibrary = resolver.getLibrary(assetWithAnnotationClass);
    var exposed = annotationLibrary.getType(className).type;

    // the caller library' name
    String libName = new PackageGraph.forThisPackage().root.name;

    await Future.forEach(l.toList(), (AssetId aid) async {
      LibraryElement lib;

      try {
        lib = resolver.getLibrary(aid);
      } catch (e) {}

      if (lib != null && Utils.isNotEmpty(lib.name)) {
        // all objects within the file
        lib.units.forEach((CompilationUnitElement unit) {
          // only the types, not methods
          unit.types.forEach((ClassElement el) {
            // only the ones annotated
            if (el.metadata.any((ElementAnnotation ea) =>
            ea.computeConstantValue().type == exposed)) {
              // use it
            }
          });
        });
      }
    });

    return '''
       $libName
    ''';
  }
}


回答2:

It seems what you want is what this issue is about How to generate one output from many inputs (aggregate builder)?



回答3:

[Günter]'s answer helped me somewhat.
Buried in that thread is another thread which links to a good example of an aggregating builder: 1https://github.com/matanlurey/build/blob/147083da9b6a6c70c46eb910a3e046239a2a0a6e/docs/writing_an_aggregate_builder.md

The gist is this:

import 'package:build/build.dart';
import 'package:glob/glob.dart';

class AggregatingBuilder implements Builder {
  /// Glob of all input files
  static final inputFiles = new Glob('lib/**');

  @override
  Map<String, List<String>> get buildExtensions {
    /// '$lib$' is a synthetic input that is used to 
    /// force the builder to build only once.
    return const {'\$lib$': const ['all_files.txt']};
  }

  @override
  Future<void> build(BuildStep buildStep) async {

    /// Do some operation on the files
    final files = <String>[];
    await for (final input in buildStep.findAssets(inputFiles)) {
      files.add(input.path);
    }
    String fileContent = files.join('\n');

    /// Write to the file
    final outputFile = AssetId(buildStep.inputId.package,'lib/all_files.txt');
    return buildStep.writeAsString(outputFile, fileContent);
  }
}


标签: dart