我正在开发一个 Flutter 应用程序,并想编写一个构建脚本来将某种原始文件(在 CSV 中(转换为格式化的 JSON 文件以作为 Flutter 资产包含在内。
通过使用像json_serializable
和jaguar_serializer
这样的库,我了解了build_runner
,所以在我看来,编写我自己的Builder
并通过build_runner
调用它是一种明智的方式。
由于编写我们自己的构建脚本的资源非常有限,因此我首先修改了此处找到的示例。但是我在尝试更改输入和输出文件的路径时卡住了:当我运行flutter pub pub run build_runner build
时,原来Dart只搜索[project_dir]/web
目录中的匹配文件,只允许我将文件写入这个web
目录。所以这段代码
buildStep.writeAsString(new AssetId(buildStep.inputId.package, 'assets/resources/foo.json'), '[]');
将产生以下异常:
UnexpectedOutputException: myapp|assets/resources/foo.json
Expected only: {myapp|web/.json}
[SEVERE] Failed after 24.4s
json_serializable
和jaguar_serializer
可以在任何地方自由生成代码的事实似乎表明这是我的配置有问题。但是我在代码和build.yaml
文件中的任何地方都找不到这个web
的东西,所以这真的很令人费解。
FWIW,这是我builder.yaml
文件的内容:
builders:
jsonBuilder:
import: "package:myapp/builder.dart"
builder_factories: ["jsonFileBuilder"]
build_extensions: {"source.csv": [".json"]}
build_to: source
auto_apply: root_package
这是builder.dart
文件
import 'dart:async';
import 'package:build/build.dart';
Builder jsonFileBuilder(BuilderOptions options) => new JsonFileBuilder();
class JsonFileBuilder implements Builder {
@override
Future build(BuildStep buildStep) async {
await buildStep.writeAsString(
new AssetId(buildStep.inputId.package, 'assets/resources/foo.json'),
'hello');
}
@override
final buildExtensions = const {
'source.csv': const ['.json']
};
}
提前感谢!
好吧,这个问题似乎是双重的。
关于输入
我的问题是,如果我将源 CSV 文件放在某个任意目录(如offline
(下,我的构建脚本将无法运行,因为找不到匹配的文件。但是当我将文件放在web
下时(如示例中所示(,脚本被调用。这给了我一种错觉,即文件扫描仅限于web/
。事实是,有一个硬编码文件/目录白名单列表:
const List<String> _defaultRootPackageWhitelist = const [
'benchmark/**',
'bin/**',
'example/**',
'lib/**',
'test/**',
'tool/**',
'web/**',
'pubspec.yaml',
'pubspec.lock',
];
如果我将 CSV 文件放在lib/
下,我的构建脚本也会被调用。
要将我的非白名单文件附加到列表中,我必须将以下部分添加到build.yaml
。
targets:
$default:
sources:
# Need to replicate the default whitelist here or other build will break:
- "benchmark/**"
- "bin/**"
- "example/**"
- "lib/**"
- "test/**"
- "tool/**"
- "web/**"
- "pubspec.yaml"
- "pubspec.lock"
# My new source
- "offline/source.csv"
关于输出
我的问题是我无法写入任意目录,构建系统一直抱怨我只能写入web/
。所以我认为文件写入被锁定在web/
.同样,这并不完全正确,因为文件写入实际上被锁定到输入文件的目录。这意味着我只能在成功让构建运行器识别我在此目录中的文件后写入offline/
。
文件写入权限的检查由此方法控制build_step_impl.dart
:
void _checkOutput(AssetId id) {
if (!_expectedOutputs.contains(id)) {
throw new UnexpectedOutputException(id, expected: _expectedOutputs);
}
}
此_expectedOutputs
是我的生成器类的buildExtensions
映射的平展值。所以如果我真的想写信给assets/resources/foo.json
我必须写这个:
@override
final buildExtensions = const {
'source.csv': const ['/../../assets/resources/foo.json']
};
必须对build.yaml
进行类似的调整:
build_extensions: {".csv": ["/../../assets/resources/foo.json"]}
这基本上解决了我的大部分问题,但仍然存在一些问题:
首先,我计划编写一个读取单个文件并写入多个文件的构建脚本。文件的数量及其文件名取决于源文件的内容。鉴于检查代码,这似乎不是受支持的功能。不是阻塞器,但有些不方便。
第二个是关于输出文件路径。你可以看到我的源文件在[projectDir]/offline
中回复,我想写入[projectDir]/assets
,但输出文件名读/../../assets/...
,这是两级。这是因为原始文件名(不带扩展名(位于输出路径的前面,这就是为什么人们会在生成 Dart 代码的构建器配置中看到.g.dart
的原因。我需要编写/../../assets
以便文件的完整输出路径变得[projectDir]/offline/source/../../assets
,以使其解析为所需的输出文件路径。但是默认行为给我的印象是,构建系统不希望我的脚本写入当前文件目录之外的任何地方,而我所做的是对系统的黑客攻击或滥用。