flatbuffers icon indicating copy to clipboard operation
flatbuffers copied to clipboard

Dart: missing import in generated file

Open mgesmundo opened this issue 3 years ago • 4 comments

Hi,

using a super simple schema with a single import, the generated file does not import the right dependency:

// file: foo.fbs
namespace myspace;

table Foo {
  version: string;
}

// file: bar.fbs
include "foo.fbs";

namespace myspace;

table Bar {
  version: Foo;
}

When I compile it using:

$ flatc --dart bar.fbs

it generate only the bar_myspace_generated.dart without the import of the foo_myspace_generated.dart file that is not generated at all.

I tried using a MacOS 12.6.1 (Apple Silicon M1 Pro) running a local compiled flatc 22.10.26 and a flatc-dart 2.0.5 version. Both does not works.

What's wrong? Thank you

mgesmundo avatar Oct 27 '22 15:10 mgesmundo

I think you can use --gen-all flag to generate all files

enum-class avatar Oct 28 '22 10:10 enum-class

Hi @enum-class thank you for your suggestion. It is a strange behavior. If you try the test example it works without the --gen-all flag and generate all right files with the right imports, but if you enable the --gen-all flag it generate wrong files. Below the full example (a simplified version of the example into the repo):

// monster_test.fbs

include "include_test1.fbs";

namespace MyGame;

table InParentNamespace {}

namespace MyGame.Example2;

table Monster {}  // Test having same name as below, but in different namespace.

namespace MyGame.Example;

union Any { Monster, MyGame.Example2.Monster }

union AnyUniqueAliases { M: Monster, M2: MyGame.Example2.Monster }

table Referrable {
  id:ulong(key, hash:"fnv1a_64");
}

table Monster {
  enemy:MyGame.Example.Monster;
  parent_namespace_test:InParentNamespace;
  vector_of_referrables:[Referrable];
  any_unique:AnyUniqueAliases;
}

root_type Monster;
// include_test1.fbs
include "include_test2.fbs";

table TableA {
  b:MyGame.OtherNameSpace.TableB;
}
// include_test2.fbs
namespace MyGame.OtherNameSpace;

table TableB {
  version: uint8;
}

Using:

$ flatc --dart monster_test.fbs

I get 3 files:

  • monster_test_my_game_generated.dart
  • monster_test_my_game.example_generated.dart
  • monster_test_my_game.example2_generated.dart

with the right imports each.

Using instead:

$ flatc --dart --gen-all monster_test.fbs

I get 5 files:

  • monster_test_generated.dart
  • monster_test_my_game_generated.dart
  • monster_test_my_game.example_generated.dart
  • monster_test_my_game.example2_generated.dart
  • monster_test_my_game.other_name_space_generated.dart

with wrong imports in every file (not in the first one):

// this is a wrong not required import
import './monster_test_generated.dart' as ;

I hope this is helpful to investigate. Thank you.

mgesmundo avatar Oct 28 '22 12:10 mgesmundo

I think the behavior of flatc --dart is like this:

  • It will generate separate dart files per namespace, and include them. For instance, in monster_test.fbs run it generated 3 files each for MyGame, MyGame.Example2, MyGame.Example namespace. Also, in foo/bar run, it generates just one file, because there is just one namespace (myspace) in foo and bar files.

  • By adding --gen-all flag, we force it to generate files that included. so in monster_test one we can see 2 more files one for include_test1.fbs that has TableA and one for include_test2.fbs that has TableB implementation. Also, in foo/bar example, by employing --gen-all flag, we force it to generate foo.fbs which is included.

  • And, the reason for weird import import './monster_test_generated.dart' as ; is that it blindly import every file in each other. So it can not find the usage of TableA which is generated in monster_test_generated.dart in other files, so leave it blank :))

I am not good at writing, but I hope I explained the situation clearly.

I need your help @mgesmundo , Since I am not a expert dart developer, could you please help me to understand from a dart developer respective, what is the correct generate output files that should we expect ?

enum-class avatar Oct 31 '22 02:10 enum-class

Thank you for your time @enum-class. Below I try to explain the dart point of view with another example.

header.fbs

namespace myspace;

table Header {
  version: int;
}

foo.fbs

include "header.fbs";

namespace myspace;

table Foo {
  header: Header;
}

bar.fbs

include "header.fbs";

namespace myspace;

table Bar {
  header: Header;
}

As you can see both Foo and Bar schemas include the Header schema and all three files use the same namespace myspace.

When I try to compile every file using:

$ flatc --dart header.fbs
$ flatc --dart foo.fbs
$ flatc --dart bar.fbs

I see three generate files:

file header_myspace_generated.dart

// automatically generated by the FlatBuffers compiler, do not modify
// ignore_for_file: unused_import, unused_field, unused_element, unused_local_variable

library myspace;

import 'dart:typed_data' show Uint8List;
import 'package:flat_buffers/flat_buffers.dart' as fb;

class Header {
  Header._(this._bc, this._bcOffset);
  factory Header(List<int> bytes) {
    final rootRef = fb.BufferContext.fromBytes(bytes);
    ...
  }
}

foo_myspace_generated.dart

// automatically generated by the FlatBuffers compiler, do not modify
// ignore_for_file: unused_import, unused_field, unused_element, unused_local_variable

library myspace;

import 'dart:typed_data' show Uint8List;
import 'package:flat_buffers/flat_buffers.dart' as fb;
import 'header_myspace_generated.dart'; // <- this is the missing import

class Foo {
  Foo._(this._bc, this._bcOffset);
  factory Foo(List<int> bytes) {
    final rootRef = fb.BufferContext.fromBytes(bytes);
    ...
  }
}

bar_myspace_generated.dart

// automatically generated by the FlatBuffers compiler, do not modify
// ignore_for_file: unused_import, unused_field, unused_element, unused_local_variable

library myspace;

import 'dart:typed_data' show Uint8List;
import 'package:flat_buffers/flat_buffers.dart' as fb;
import 'header_myspace_generated.dart'; // <- this is the missing import

class Bar {
  Bar._(this._bc, this._bcOffset);
  factory Bar(List<int> bytes) {
    final rootRef = fb.BufferContext.fromBytes(bytes);
    ...
  }
}

The import 'header_myspace_generated.dart'; is missing in both Foo and Bar generated files.

If I use:

$ flatc --dart --gen-all foo.fbs
$ flatc --dart --gen-all bar.fbs

the two generated files do not need the Header generated file because have a duplicated declaration of the Header class inside every file, that is not optimal behavior.

I hope I have explained better. Thank you.

mgesmundo avatar Oct 31 '22 08:10 mgesmundo

Thank you @enum-class and @dbaileychess for this update. Unfortunately it still to not works as expected. Using:

$ flatc --dart header.fbs
$ flatc --dart foo.fbs
$ flatc --dart bar.fbs

both foo_myspace_generated.dart and bar_myspace_generated.dart contains the Header import:

import './header_myspace_generated.dart' as myspace;

but the generated code use Header instead of myspace.Header (like the import 'package:flat_buffers/flat_buffers.dart' as fb; does).

If I remove the alias into the import:

import './header_myspace_generated.dart';

it works but it could create ambiguous imports in case of same class names into different packages imports. I'm sorry for this bad news. Thank you for your support.

mgesmundo avatar Nov 24 '22 12:11 mgesmundo

The problem in foo/bar example in this issue is, it does not specified which Header in which namespace should be used. I mean It should be like this: header.fbs:

namespace myspace;

table Header {
  version: int;
}

bar.fbs

include "header.fbs";

namespace myspace;

table Bar {
 header: myspace.Header; /// =>  Not Header
}

You can check example files in project: link-1 link-2

enum-class avatar Nov 24 '22 13:11 enum-class

Hi @enum-class, even if I add the full namespace, the result is the same:

header.fbs

namespace myspace;

table Header {
  version: int;
}

bar.fbs

include "header.fbs";

namespace myspace;

table Bar {
  header: myspace.Header;
}

Into the terminal:

flatc --dart bar.fbs

bar_myspace_generated.dart

// automatically generated by the FlatBuffers compiler, do not modify
// ignore_for_file: unused_import, unused_field, unused_element, unused_local_variable

library myspace;

import 'dart:typed_data' show Uint8List;
import 'package:flat_buffers/flat_buffers.dart' as fb;

// this is right!
import './header_myspace_generated.dart' as myspace;

class Bar {
  Bar._(this._bc, this._bcOffset);
  factory Bar(List<int> bytes) {
    final rootRef = fb.BufferContext.fromBytes(bytes);
    return reader.read(rootRef, 0);
  }

  static const fb.Reader<Bar> reader = _BarReader();

  final fb.BufferContext _bc;
  final int _bcOffset;

  // this is wrong
  Header? get header => Header.reader.vTableGetNullable(_bc, _bcOffset, 4);
  // it should be:
  // myspace.Header? get header => myspace.Header.reader.vTableGetNullable(_bc, _bcOffset, 4);

  @override
  String toString() {
    return 'Bar{header: ${header}}';
  }
}

Note that using:

flatc --dart --gen-all bar.fbs

the generate file contains all the Header classes but also contains the redundant (because all classes are inside the same file):

import './header_myspace_generated.dart' as myspace;

mgesmundo avatar Nov 24 '22 22:11 mgesmundo

@mgesmundo the generator compare the namespace of Header and current namespace, find out both are same, so it drop namesapce (myspace). Change Header namespace to something else and try.

enum-class avatar Nov 24 '22 23:11 enum-class

Hi @enum-class thank you for your best support! Yes: using a different namespace it works even if I don't understand why this is de default behaviour for Dart. My colleague that uses Golang do not have this behaviour.

mgesmundo avatar Nov 25 '22 08:11 mgesmundo

It seems that the previous commit did not work properly on the latest Dart compiler, so I made some adjustments and now it runs normally locally.

fawdlstty avatar Feb 20 '24 05:02 fawdlstty