@@ -10,29 +10,42 @@ import 'package:dart_style/dart_style.dart';
1010import 'package:source_gen/source_gen.dart' ;
1111import 'package:pubspec_parse/pubspec_parse.dart' ;
1212
13+ import 'config.dart' ;
1314import 'entity_resolver.dart' ;
1415import 'code_chunks.dart' ;
1516
1617/// CodeBuilder collects all '.objectbox.info' files created by EntityResolver and generates objectbox-model.json and
1718/// objectbox_model.dart
1819class CodeBuilder extends Builder {
19- static final jsonFile = 'objectbox-model.json' ;
20- static final codeFile = 'objectbox.g.dart' ;
20+ final Config _config;
2121
22- @override
23- final buildExtensions = {r'$lib$' : _outputs, r'$test$' : _outputs};
24-
25- // we can't write `jsonFile` as part of the output because we want it persisted, not removed before each generation
26- static final _outputs = [codeFile];
22+ CodeBuilder (this ._config);
2723
28- String dir (BuildStep buildStep) => path.dirname (buildStep.inputId.path);
24+ @override
25+ late final buildExtensions = {
26+ r'$lib$' : [path.join (_config.outDirLib, _config.codeFile)],
27+ r'$test$' : [path.join (_config.outDirTest, _config.codeFile)]
28+ };
29+
30+ String _dir (BuildStep buildStep) => path.dirname (buildStep.inputId.path);
31+
32+ String _outDir (BuildStep buildStep) {
33+ var dir = _dir (buildStep);
34+ if (dir.endsWith ('test' )) {
35+ return dir + '/' + _config.outDirTest;
36+ } else if (dir.endsWith ('lib' )) {
37+ return dir + '/' + _config.outDirLib;
38+ } else {
39+ throw Exception ('Unrecognized path being generated: $dir ' );
40+ }
41+ }
2942
3043 @override
3144 FutureOr <void > build (BuildStep buildStep) async {
3245 // build() will be called only twice, once for the `lib` directory and once for the `test` directory
3346 // map from file name to a 'json' representation of entities
3447 final files = < String , List <dynamic >> {};
35- final glob = Glob (dir (buildStep) + '/**' + EntityResolver .suffix);
48+ final glob = Glob (_dir (buildStep) + '/**' + EntityResolver .suffix);
3649 await for (final input in buildStep.findAssets (glob)) {
3750 files[input.path] = json.decode (await buildStep.readAsString (input))! ;
3851 }
@@ -55,7 +68,7 @@ class CodeBuilder extends Builder {
5568
5669 Pubspec ? pubspec;
5770 try {
58- final pubspecFile = File (path.join (dir (buildStep), '../pubspec.yaml' ));
71+ final pubspecFile = File (path.join (_dir (buildStep), '../pubspec.yaml' ));
5972 pubspec = Pubspec .parse (pubspecFile.readAsStringSync ());
6073 } catch (e) {
6174 log.info ("Couldn't load pubspec.yaml: $e " );
@@ -69,8 +82,8 @@ class CodeBuilder extends Builder {
6982 List <ModelEntity > entities, BuildStep buildStep) async {
7083 // load an existing model or initialize a new one
7184 ModelInfo model;
72- final jsonId =
73- AssetId ( buildStep.inputId.package, dir (buildStep) + '/' + jsonFile);
85+ final jsonId = AssetId (
86+ buildStep.inputId.package, _outDir (buildStep) + '/' + _config. jsonFile);
7487 if (await buildStep.canRead (jsonId)) {
7588 log.info ('Using model: ${jsonId .path }' );
7689 model =
@@ -95,11 +108,42 @@ class CodeBuilder extends Builder {
95108
96109 void updateCode (ModelInfo model, List <String > infoFiles, BuildStep buildStep,
97110 Pubspec ? pubspec) async {
111+ // If output directory is not package root directory,
112+ // need to prefix imports with as many '../' to be relative from root.
113+ final rootPath = _dir (buildStep);
114+ final outPath = _outDir (buildStep);
115+ final rootDir = Directory (rootPath).absolute;
116+ var outDir = Directory (outPath).absolute;
117+ var prefix = '' ;
118+
119+ if (! outDir.path.startsWith (rootDir.path)) {
120+ throw InvalidGenerationSourceError (
121+ 'configured output_dir ${outDir .path } is not a '
122+ 'subdirectory of the source directory ${rootDir .path }' );
123+ }
124+
125+ while (outDir.path != rootDir.path) {
126+ final parent = outDir.parent;
127+ if (parent.path == outDir.path) {
128+ log.warning (
129+ 'Failed to find package root from output directory, generated imports might be incorrect.' );
130+ prefix = '' ;
131+ break ; // Reached top-most directory, stop searching.
132+ }
133+ outDir = parent;
134+ prefix += '../' ;
135+ }
136+ if (prefix.isNotEmpty) {
137+ log.info (
138+ 'Output directory not in package root, adding prefix to imports: ' +
139+ prefix);
140+ }
141+
98142 // transform '/lib/path/entity.objectbox.info' to 'path/entity.dart'
99143 final imports = infoFiles
100144 .map ((file) => file
101145 .replaceFirst (EntityResolver .suffix, '.dart' )
102- .replaceFirst (dir (buildStep) + '/' , '' ))
146+ .replaceFirst (rootPath + '/' , prefix ))
103147 .toList ();
104148
105149 var code = CodeChunks .objectboxDart (model, imports, pubspec);
@@ -109,7 +153,7 @@ class CodeBuilder extends Builder {
109153 } finally {
110154 // Write the code even after a formatter error so it's easier to debug.
111155 final codeId =
112- AssetId (buildStep.inputId.package, dir (buildStep) + '/' + codeFile);
156+ AssetId (buildStep.inputId.package, outPath + '/' + _config. codeFile);
113157 log.info ('Generating code: ${codeId .path }' );
114158 await buildStep.writeAsString (codeId, code);
115159 }
0 commit comments