Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upgraded protobuf to support cross crate imports. #24

Merged
merged 44 commits into from
Nov 8, 2023

Conversation

pompon0
Copy link
Collaborator

@pompon0 pompon0 commented Nov 3, 2023

Upgraded protobuf to support cross crate imports.
We need it to allow defining protos in zksync-era as well.
Proto packages share a single common namespace, so I prefixed our packages with zksync..
I couldn't name the crate "protobuf", because cargo deny has mistaken it for the public crate with the same name. I've named it "zksync_protobuf", to be consistent with zksync-era repo.

CI protobuf compatibility check is now more complex, because we need to collect the descriptors from the cargo build artifacts. Currently it is implemented as a bash command, but can be upgraded to a proper github action in the future.

@pompon0 pompon0 requested a review from brunoffranca as a code owner November 3, 2023 17:23
@pompon0 pompon0 requested a review from slowli November 3, 2023 17:24
node/actors/network/src/frame.rs Outdated Show resolved Hide resolved
node/libs/protobuf_build/src/canonical.rs Outdated Show resolved Hide resolved
node/libs/protobuf_build/src/canonical.rs Outdated Show resolved Hide resolved
node/libs/protobuf_build/src/lib.rs Outdated Show resolved Hide resolved
Comment on lines 142 to 145
static INIT : {this}::Lazy<{this}::prost_reflect::MessageDescriptor> = {this}::Lazy::new(|| {{\
DESCRIPTOR.load_global().get_message_by_name({proto_name:?}).unwrap()\
}});\
INIT.clone()\
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • As with the descriptor definition, ideally we'd want to move as much code from here to the crate itself as possible. Again, this'll probably require using a macro like derive_reflect_message!($message:ty, $descriptor:ident).
  • Stupid question: since DESCRIPTOR is a static already, does it make sense to cache separate MessageDescriptors here instead of just returning a fresh clone each time? Is it significantly more efficient?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

load_global() is inefficient because it checks whether all the files of the descriptor have been already loaded. Plus, cloning prost_reflect::DescriptorPool in load_global() is inefficient because it is poorly written.

node/libs/protobuf_build/src/lib.rs Show resolved Hide resolved
node/libs/protobuf_build/src/lib.rs Show resolved Hide resolved
node/libs/protobuf_build/src/lib.rs Outdated Show resolved Hide resolved
node/libs/protobuf_build/src/lib.rs Outdated Show resolved Hide resolved
@brunoffranca brunoffranca self-requested a review November 7, 2023 12:01
brunoffranca
brunoffranca previously approved these changes Nov 7, 2023
brunoffranca
brunoffranca previously approved these changes Nov 7, 2023
brunoffranca
brunoffranca previously approved these changes Nov 7, 2023
Copy link
Contributor

@slowli slowli left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couple of tricks after I've refreshed my memory on Protobuf handling in Exonum (it was used there as the main serialization format as well):

  • It's possible to use the quote crate to generate Rust code more eloquently. See an example here.
  • It's possible to pass information from deps to the consumers using fake linking. E.g., here the Merkle tree crate publishes a path to its Protobuf files, which will then be available as the DEP_EXONUM_PROTOBUF_MERKLEDB_PROTOS env variable. The var name is DEP_$link_$var, where link is a fake crate that the exporting library links to (it doesn't need to exist), and $var is the name in println!("cargo:$var={}", ...);. Multiple vars can be passed using this way; e.g., we could use this to pass paths to Protobuf descriptors to solve the "dependent needs to wait for all direct dependencies to fully compile" issue. (With this approach, it would be improved to "dependent needs to wait for the dependencies build scripts to execute".)

Not sure whether the second trick would be helpful, but using quote is IMO certainly preferable to generating Rust code as strings.

node/libs/protobuf_build/src/canonical.rs Show resolved Hide resolved
node/libs/protobuf_build/src/lib.rs Outdated Show resolved Hide resolved
node/libs/protobuf_build/src/lib.rs Show resolved Hide resolved
node/libs/protobuf_build/src/lib.rs Outdated Show resolved Hide resolved
//! Generates rust code from protobufs.
fn main() {
zksync_protobuf::build::Config {
input_root: "proto".into(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Is src/proto or proto path more canonical as a path for "main" Protobuf declarations? Here, you use proto, but the zksync_protobuf crate uses src/proto. Personally, I slightly prefer proto since it cleanly separate source files from the Rust module wrapper.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In my mind it, "proto" should be a subdirectory of a module that it is supposed to be embedded into. I haven't touched schema/proto, because I plan to split it in the next pr.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure I agree with this convention ("hiding" Protobuf files in the Rust source dir look suboptimal), but this can be elaborated later.

node/libs/schema/build.rs Show resolved Hide resolved
@pompon0
Copy link
Collaborator Author

pompon0 commented Nov 8, 2023

I've replaced string generation with quote.

@pompon0 pompon0 requested a review from slowli November 8, 2023 09:10
Copy link
Contributor

@slowli slowli left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good enough™ for the first iteration. I'm eager to follow up with minor changes as discussed previously / in line comments (renaming vars + relying on syn types instead of on strings where it's appropriate).

.to_string();
let rust_name: syn::Path = syn::parse_str(&rust_name).context("rust_name")?;
let proto_name = proto_name.to_string();
let this: syn::Path = syn::parse_str(&self.this_crate().to_string()).context("this")?;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: A more conventional way to do this is to use parse_quote!, or make RustName convertible to syn::Path (IIUC, RustName is semantically equivalent to it). With the current RustName internal structure, this is hardly possible.

pub(crate) fn format(&self) -> anyhow::Result<String> {
let s = self.collect();
Ok(prettyplease::unparse(
&syn::parse_str(&s).with_context(|| format!("syn::parse_str({s:?})"))?,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can have RustModule wrap a syn::File internally (in general, dealing with syn types rather than strings) in order to get rid of AST -> string -> AST conversions. I'm happy to try doing this in a follow-up PR, so that it the basic functionality is delivered faster.

//! Generates rust code from protobufs.
fn main() {
zksync_protobuf::build::Config {
input_root: "proto".into(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure I agree with this convention ("hiding" Protobuf files in the Rust source dir look suboptimal), but this can be elaborated later.

@pompon0 pompon0 merged commit 9b30037 into main Nov 8, 2023
4 checks passed
@pompon0 pompon0 deleted the gprusak-split-schema branch November 8, 2023 11:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants