提供了一些用于生成文件的通用命令,这些命令中有的可以用于测试生成的文件,有的可以用于对外提供这些生成的文件以及它们的源代码。
如果你开发的是 Web : 你可以使用 webdev 工具替代 build_runner 以构建和提供 Web 应用。
Get Start
pubspec.yaml添加依赖
dependencies:
flutter:
sdk: flutter
build_runner: any
build.yaml添加build配置 具体配置参考build_config
builders:
my_builder:
import: "package:flutter_test_1/test_builder.dart"
builder_factories: ["testBuilder"]
build_extensions: {".dart": [".test.dart"]}
auto_apply: root_package
builder完成需要的具体功能
import 'dart:async';
import 'package:analyzer/dart/element/element.dart';
import 'package:build/build.dart';
Builder testBuilder(BuilderOptions options) {
return TestBuilder();
}
class TestBuilder extends Builder {
@override
Future build(BuildStep buildStep) async {
// print('${buildStep.inputLibrary}');
await printAllElements(buildStep);
}
ClassElement findType(LibraryElement element, String name) {
final type = element.exportNamespace.get(name);
return type is ClassElement ? type : null;
}
/// All of the declarations in this library.
Future printAllElements(BuildStep buildStep) async {
final element = await buildStep.inputLibrary;
print('**************************');
/// element数据
print(element.displayName);
for (var cu in element.units) {
// final buffer = StringBuffer();
final types = cu.types;
for (var type in types) {
// buffer.write('${type.displayName}: superTypes-${type.allSupertypes}, fields-${type.fields}\n');
// buffer.write('${type.displayName}: fields-${type.fields}, allSupertypes-${type.allSupertypes}, accessors-${type.accessors}, constructors-${type.constructors}, declaration-${type.declaration}, documentationComment-${type.documentationComment}, enclosingElement-${type.enclosingElement}\n');
}
// print('${cu.displayName}: ${cu.accessors}');
// print(' ${cu.enums}');
// print(' ${cu.functionTypeAliases}');
// print(' ${cu.functions}');
// print(' ${cu.mixins}');
// print(' ${cu.topLevelVariables}');
// print('${buffer.toString()}');
}
}
@override
Map<String, List<String>> buildExtensions = {'.dart': ['.test.dart']};
}
Builder BuildStep源码
/// The basic builder class, used to build new files from existing ones.
abstract class Builder {
/// Generates the outputs for a given [BuildStep].
FutureOr<void> build(BuildStep buildStep);
/// 设置输入输出文件扩展名
Map<String, List<String>> get buildExtensions;
}
/// A single step in a build process.
///
/// This represents a single [inputId], logic around resolving as a library,
/// and the ability to read and write assets as allowed by the underlying build
/// system.
/// 一个文件的构建信息(一个dart文件对应一个library)
abstract class BuildStep implements AssetReader, AssetWriter {
/// The primary for this build step.
AssetId get inputId;
/// Resolved library defined by [inputId].
///
/// Throws [NonLibraryAssetException] if [inputId] is not a Dart library file.
Future<LibraryElement> get inputLibrary;
/// Gets an instance provided by [resource] which is guaranteed to be unique
/// within a single build, and may be reused across build steps within a
/// build if the implementation allows.
///
/// It is also guaranteed that [resource] will be disposed before the next
/// build starts (and the dispose callback will be invoked if provided).
Future<T> fetchResource<T>(Resource<T> resource);
/// Writes [bytes] to a binary file located at [id].
///
/// Returns a [Future] that completes after writing the asset out.
///
/// * Throws a `PackageNotFoundException` if `id.package` is not found.
/// * Throws an `InvalidOutputException` if the output was not valid.
///
/// **NOTE**: Most `Builder` implementations should not need to `await` this
/// Future since the runner will be responsible for waiting until all outputs
/// are written.
@override
Future<void> writeAsBytes(AssetId id, FutureOr<List<int>> bytes);
/// Writes [contents] to a text file located at [id] with [encoding].
///
/// Returns a [Future] that completes after writing the asset out.
///
/// * Throws a `PackageNotFoundException` if `id.package` is not found.
/// * Throws an `InvalidOutputException` if the output was not valid.
///
/// **NOTE**: Most `Builder` implementations should not need to `await` this
/// Future since the runner will be responsible for waiting until all outputs
/// are written.
@override
Future<void> writeAsString(AssetId id, FutureOr<String> contents,
{Encoding encoding = utf8});
/// A [Resolver] for [inputId].
Resolver get resolver;
/// Tracks performance of [action] separately.
///
/// If performance tracking is enabled, tracks [action] as separate stage
/// identified by [label]. Otherwise just runs [action].
///
/// You can specify [action] as [isExternal] (waiting for some external
/// resource like network, process or file IO). In that case [action] will
/// be tracked as single time slice from the beginning of the stage till
/// completion of Future returned by [action].
///
/// Otherwise all separate time slices of asynchronous execution will be
/// tracked, but waiting for external resources will be a gap.
///
/// Returns value returned by [action].
/// [action] can be async function returning [Future].
T trackStage<T>(String label, T Function() action, {bool isExternal = false});
/// Indicates that [ids] were read but their content has no impact on the
/// outputs of this step.
///
/// **WARNING**: Using this introduces serious risk of non-hermetic builds.
///
/// If these files change or become unreadable on the next build this build
/// step may not run.
///
/// **Note**: This is not guaranteed to have any effect and it should be
/// assumed to be a no-op by default. Implementations must explicitly
/// choose to support this feature.
void reportUnusedAssets(Iterable<AssetId> ids);
}
import 'package:build/build.dart';
/// A really simple [Builder], it just makes copies of .txt files!
class CopyBuilder implements Builder {
@override
final buildExtensions = const {
'.txt': ['.txt.copy']
};
@override
Future<void> build(BuildStep buildStep) async {
// Each `buildStep` has a single input.
var inputId = buildStep.inputId;
// Create a new target `AssetId` based on the old one.
var copy = inputId.addExtension('.copy');
var contents = await buildStep.readAsString(inputId);
// Write out the new asset.
await buildStep.writeAsString(copy, contents);
}
}
import 'package:build/build.dart';
class ResolvingCopyBuilder implements Builder {
// Take a `.dart` file as input so that the Resolver has code to resolve
@override
final buildExtensions = const {
'.dart': ['.dart.copy']
};
@override
Future<void> build(BuildStep buildStep) async {
// Get the `LibraryElement` for the primary input.
var entryLib = await buildStep.inputLibrary;
// Resolves all libraries reachable from the primary input.
var resolver = buildStep.resolver;
// Get a `LibraryElement` for another asset.
var libFromAsset = await resolver.libraryFor(
AssetId.resolve('some_import.dart', from: buildStep.inputId));
// Or get a `LibraryElement` by name.
var libByName = await resolver.findLibraryByName('my.library');
}
}
参考
https://juejin.im/post/5da5af226fb9a04e046bcbaa
结论
build-runner是纵向的,横向package扫描不到;能够扫描出成员变量、构造函数、所有父类、注释等
寻找横向扫描方法
探究color的类型被扫描成了dynamic的原因
使用Analyzer,可以直接对整个项目进行扫描,针对每个dart文件扫描出的语法树进行分析。
使用build_runner,能够扫描出每个dart文件中类成员变量、构造函数、所有父类、注释等。
可以结合使用,build_runner用来获取父类信息。