Import common proto in each crate

673 Views Asked by At

I'm using tonic_build.

I have this dir structure:

- Cargo.toml (workspace)
- src
  - common
    - Cargo.toml
    - src
      - common.proto

  - crates
    - players
      - Cargo.toml
      - build.rs
      - src
        - proto
          - players.proto (I would like to import src/common/src/common.proto here)

    - teams
      - Cargo.toml
      - build.rs
      - src
        - proto
          - teams.proto (I would like to import src/common/src/common.proto here)
    
    - others...

as you can see I have many crates under crates dir and I have a common.proto which I would like to import in my crates.

I'm using a build.rs for each crate because I cannot (right?) use one for the entire Cargo workspace (I tried: Cargo doesn't start build.rs in workspace root dir).

So I'm trying to import common.proto in file like this:

syntax = "proto3";

package players;

import "google/protobuf/timestamp.proto";

import "src/common/src/common.proto";

and in my VSCode this is working.

But when I start build.rs throws with:

error: failed to run custom build command for `players v0.1.0`

Caused by:
  process didn't exit successfully: `C:\prj\target\debug\build\orders-b13065cbe3fac50f\build-script-build` (exit code: 1)
  --- stdout
  cargo:rerun-if-changed=src/proto/players.proto
  cargo:rerun-if-changed=src/proto

  --- stderr
  Error: Custom { kind: Other, error: "protoc failed: src/common/src/common.proto: File not found.\r\nplayers.proto:6:1: Import \"src/common/src/common.proto\" was not found or had errors.\r\nplayers.proto:29:3: \"common.Pagination\" is not defined.\r\nplayers.proto:49:9: \"common.Page\" is not defined.\r\n" }
[Finished running. Exit status: 0]

My build.rs is:

fn main() -> Result<(), Box<dyn std::error::Error>> {
    tonic_build::configure()
        .out_dir("src/proto")
        .compile(
            &["src/proto/players.proto"],
            &["src/proto"],
        )?;

    Ok(())
}

How can I fix this?

1

There are 1 best solutions below

2
DazWilkin On

For example:

.
├── build.rs
├── Cargo.lock
├── Cargo.toml
├── protos
│   ├── common
│   │   └── common.proto
│   └── teams
│       └── players.proto
├── src
│   └── main.rs
└── target

With e.g.:

players.proto:

syntax = "proto3";

package players;

import "google/protobuf/timestamp.proto";
import "common/common.proto";

message Bar {
    google.protobuf.Timestamp timestamp = 1;
    common.Foo foo = 2;
    
}

Note: The import is rooted under protos but then follows the folder structure. The folder structure should match the package structure. The field reference uses the package structure ({package}.{Message}).

And:

build.rs:

fn main() -> Result<(), Box<dyn std::error::Error>> {
    tonic_build::configure()
        .build_server(false)
        .compile(
            &["protos/common/common.proto","protos/teams/players.proto"],
            &["protos"],
        )?;
    Ok(())
}

Note: With the exception of google.protobuf which is included by default by the PATH to protoc, we must set the proto_path (tonic includes) to the root location(s -- each root if these are disjoint) of the protos.