Skip to content

Generated output differs depending on order of input files #952

@wchargin

Description

@wchargin

If you pass multiple file names on the command line to protoc while using protoc-gen-dart, the contents of the generated files may depend on the order in which you listed the inputs.

This is problematic because it makes it harder to run automated checks like "test that the generated outputs are up to date" with simple invocations like find . -name '*.proto' -exec protoc ... {} +. This can be mitigated by sorting the file list, but (a) it took me a while to figure out what was going on here and come up with that solution, and (b) it should be the duty of the plugin to produce deterministic output.

I also generate bindings for C++, Go, Python, and TypeScript, both with and without gRPC support, and none of those other language bindings exhibits this issue.

Repro

Construct a few protos that depend on each other:

root@6ff5b1d60b11:/data# tail *.proto
==> a.proto <==
syntax = "proto3";
message A {}

==> b.proto <==
syntax = "proto3";
message B {}

==> has_a.proto <==
syntax = "proto3";

import "a.proto";

message HasA {
  A a = 1;
}

==> has_ab.proto <==
syntax = "proto3";

import "a.proto";
import "b.proto";

message HasAB {
  A a = 1;
  B b = 2;
}

==> has_b.proto <==
syntax = "proto3";

import "b.proto";

message HasB {
  B b = 1;
}

Compile them in two different orders:

root@6ff5b1d60b11:/data# rm -rf out_ab out_ba && mkdir out_ab out_ba
root@6ff5b1d60b11:/data# protoc --dart_out=out_ab has_a.proto has_b.proto has_ab.proto
root@6ff5b1d60b11:/data# protoc --dart_out=out_ba has_b.proto has_a.proto has_ab.proto

Actual behavior

Note that the generated code differs between the two versions:

root@6ff5b1d60b11:/data# diff -u out_ab/has_ab.pb.dart out_ba/has_ab.pb.dart | head
--- out_ab/has_ab.pb.dart       2025-01-15 20:42:37.746806003 +0000
+++ out_ba/has_ab.pb.dart       2025-01-15 20:42:43.621806005 +0000
@@ -9,20 +9,20 @@

 import 'package:protobuf/protobuf.dart' as $pb;

-import 'a.pb.dart' as $0;
-import 'b.pb.dart' as $1;
+import 'a.pb.dart' as $1;
+import 'b.pb.dart' as $0;
root@6ff5b1d60b11:/data# diff -u out_ab/has_a.pb.dart out_ba/has_a.pb.dart | head
--- out_ab/has_a.pb.dart        2025-01-15 20:42:37.745806003 +0000
+++ out_ba/has_a.pb.dart        2025-01-15 20:42:43.621806005 +0000
@@ -9,17 +9,17 @@

 import 'package:protobuf/protobuf.dart' as $pb;

-import 'a.pb.dart' as $0;
+import 'a.pb.dart' as $1;

 class HasA extends $pb.GeneratedMessage {

Expected behavior

The output should be bitwise identical.

Version info

root@6ff5b1d60b11:/data# dart pub global list
protoc_plugin 20.0.1

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions