Skip to content

Commit d0f723d

Browse files
committed
ttrpc-rust: Support the new version of codegen
The ttrpc provides a "prost" feature to support the new version of codegen. An "example2" has been added to demonstrate how to use the codegen. Signed-off-by: Xuewei Niu <niuxuewei.nxw@antgroup.com>
1 parent 63dc876 commit d0f723d

37 files changed

+1448
-393
lines changed

Cargo.toml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ homepage = "https://github.com/containerd/ttrpc-rust"
1111
description = "A Rust version of ttrpc."
1212

1313
[dependencies]
14-
protobuf = { version = "3.1.0" }
14+
prost = { version = "0.11", optional = true }
15+
protobuf = "3.1.0"
1516
libc = { version = "0.2.59", features = [ "extra_traits" ] }
1617
nix = "0.23.0"
1718
log = "0.4"
@@ -27,11 +28,13 @@ tokio-vsock = { version = "0.3.1", optional = true }
2728

2829
[build-dependencies]
2930
protobuf-codegen = "3.1.0"
31+
prost-build = "0.11"
3032

3133
[features]
3234
default = ["sync"]
33-
async = ["async-trait", "tokio", "futures", "tokio-vsock"]
35+
async = ["dep:async-trait", "dep:tokio", "dep:futures", "dep:tokio-vsock"]
3436
sync = []
37+
prost = ["dep:prost"]
3538

3639
[package.metadata.docs.rs]
3740
all-features = true

README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,3 +136,19 @@ cargo update
136136
cargo install --force --path .
137137
```
138138
3. Build your project.
139+
140+
# ttrpc-rust with the Prost
141+
142+
The new version of the ttrpc-rust is built with the Prost crate, a modern
143+
protobuf compiler written by Rust. There are certain different behaviors from
144+
the Rust-protobuf version:
145+
146+
1. The protoc should be installed.
147+
2. Enabling "prost" feature for the ttrpc-rust.
148+
3. The Rust files are named based on their package name, rather than the proto
149+
filename, e.g. `ttrpc = { version = "1.0", features = ["prost"] }`.
150+
4. Some variable names are different, e.g. for "cpu", "CPU" is generated by the
151+
Rust-protobuf, and "Cpu" is genereated by the Prost.
152+
153+
The "example" is an example with the Rust-protobuf version, and the "example2"
154+
is an example with the Prost version.

build.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ fn main() {
77
let path: PathBuf = [out_dir.clone(), "mod.rs".to_string()].iter().collect();
88
fs::write(path, "pub mod ttrpc;").unwrap();
99

10+
generate_ttrpc(&out_dir);
11+
}
12+
13+
#[cfg(not(feature = "prost"))]
14+
fn generate_ttrpc(out_dir: &str) {
1015
let customize = protobuf_codegen::Customize::default()
1116
.gen_mod_rs(false)
1217
.generate_accessors(true);
@@ -20,3 +25,13 @@ fn main() {
2025
.run()
2126
.expect("Codegen failed.");
2227
}
28+
29+
#[cfg(feature = "prost")]
30+
fn generate_ttrpc(out_dir: &str) {
31+
prost_build::Config::new()
32+
.out_dir(out_dir)
33+
.compile_well_known_types()
34+
.protoc_arg("--experimental_allow_proto3_optional")
35+
.compile_protos(&["src/ttrpc.proto"], &["src"])
36+
.expect("Codegen failed")
37+
}

codegen/Cargo.toml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@
22
name = "ttrpc-codegen"
33
version = "1.0.0"
44
edition = "2021"
5+
authors = ["The Ant Group Kata Team <kata@list.alibaba-inc.com>"]
6+
license = "Apache-2.0"
7+
keywords = ["codegen", "ttrpc", "protobuf"]
8+
description = "Rust codegen for ttrpc using prost crate"
9+
categories = ["network-programming", "development-tools::build-utils"]
10+
repository = "https://github.com/containerd/ttrpc-rust/tree/master/codegen"
11+
homepage = "https://github.com/containerd/ttrpc-rust/tree/master/codegen"
12+
readme = "README.md"
513

614
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
715

codegen/README.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Ttrpc-rust Codegen
2+
3+
## Getting started
4+
5+
Please ensure that the protoc has been installed on your local environment. Then
6+
write the following code into "build.rs".
7+
8+
```rust
9+
let mut protos = vec![
10+
"protocols/protos/health.proto",
11+
"protocols/protos/agent.proto",
12+
"protocols/protos/oci.proto",
13+
];
14+
15+
let includes = vec!["protocols/protos"];
16+
17+
let codegen = CodegenBuilder::new()
18+
.set_out_dir(&"protocols/sync")
19+
.set_protos(&protos)
20+
.set_includes(&includes)
21+
.set_serde(true)
22+
.set_async_mode(AsyncMode::None)
23+
.set_generate_service(true)
24+
.build()
25+
.unwrap();
26+
codegen.generate().unwrap();
27+
```
28+
29+
Add ttrpc-codegen to "build-dependencies" section in "Cargo.toml".
30+
31+
```toml
32+
[build-dependencies]
33+
ttrpc-codegen = "1.0"
34+
```

codegen/src/codegen.rs

Lines changed: 60 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,26 @@
11
use anyhow::{anyhow, Context, Result};
2+
use prost::Message;
23
use prost_build::Config;
4+
use prost_types::FileDescriptorSet;
35
use std::{
46
fs::{self, File},
5-
io::{Read, Write},
6-
path::Path,
7+
io::{BufReader, Read, Write},
8+
path::{Path, PathBuf},
79
};
810

911
use crate::svcgen::{AsyncMode, TtrpcServiceGenerator};
1012

13+
const FILE_DESCRIPTOR_SET: &str = "fd_set.bin";
14+
1115
pub struct Codegen<'a, P: AsRef<Path>> {
1216
out_dir: &'a P,
1317
protos: &'a [P],
1418
includes: &'a [P],
1519
/// Whether to enable serde
1620
serde: bool,
1721
async_mode: AsyncMode,
22+
/// Whether to generate service
23+
generate_service: bool,
1824
}
1925

2026
impl<'a, P> Codegen<'a, P>
@@ -24,27 +30,30 @@ where
2430
pub fn generate(&self) -> Result<()> {
2531
self.compile_protos().context("Compile protos")?;
2632
self.write_header().context("Write header")?;
33+
self.clean_up().context("Clean up")?;
2734

2835
Ok(())
2936
}
3037

38+
// TODO: Do not write header to the files that already has the header
39+
// TODO: Write header to the files generated by the codegen
3140
fn write_header(&self) -> Result<()> {
32-
let dir = fs::read_dir(self.out_dir.as_ref()).context("Read out_dir")?;
33-
34-
for child in dir {
35-
let entry = child.context("Unable to get entry")?;
36-
let path = entry.path();
37-
let ext = match path.extension() {
38-
Some(ext) => match ext.to_str() {
39-
Some(ext) => ext,
40-
None => "",
41-
},
42-
None => "",
41+
// Read fd_set.bin
42+
let f = File::open(PathBuf::from(self.out_dir.as_ref()).join(FILE_DESCRIPTOR_SET))
43+
.context("Open fd_set.bin")?;
44+
let mut reader = BufReader::new(f);
45+
let mut buffer = Vec::new();
46+
reader.read_to_end(&mut buffer).context("Read fd_set.bin")?;
47+
48+
let fd_set = FileDescriptorSet::decode(&buffer as &[u8]).context("Decode fd_set")?;
49+
50+
for fd in fd_set.file.iter() {
51+
let rs_path =
52+
PathBuf::from(self.out_dir.as_ref()).join(&format!("{}.rs", fd.package()));
53+
let mut f = match File::open(&rs_path) {
54+
Ok(f) => f,
55+
_ => continue,
4356
};
44-
if entry.file_name() == "mod.rs" || ext != "rs" {
45-
continue;
46-
}
47-
4857
let header = format!(
4958
r#"// This file is generated by ttrpc-codegen {}. Do not edit
5059
// @generated
@@ -55,12 +64,11 @@ where
5564

5665
let mut buf = Vec::<u8>::new();
5766
buf.write(header.as_bytes()).context("Write header")?;
58-
let mut f = File::open(&path).context(format!("Open rust file {:?}", path))?;
5967
f.read_to_end(&mut buf)
60-
.context(format!("Read from rust file {:?}", path))?;
61-
let mut f = File::create(&path).context(format!("Open rust file {:?}", path))?;
68+
.context(format!("Read from rust file {:?}", rs_path))?;
69+
let mut f = File::create(&rs_path).context(format!("Open rust file {:?}", rs_path))?;
6270
f.write_all(buf.as_slice())
63-
.context(format!("Write to rust file {:?}", path))?;
71+
.context(format!("Write to rust file {:?}", rs_path))?;
6472
}
6573

6674
Ok(())
@@ -72,39 +80,47 @@ where
7280
config.service_generator(Box::new(TtrpcServiceGenerator::new(self.async_mode)));
7381
config.protoc_arg("--experimental_allow_proto3_optional");
7482
config.compile_well_known_types();
75-
config.include_file("_include.rs");
76-
if self.serde {
77-
config.message_attribute(".", "#[derive(::serde::Serialize, ::serde::Deserialize)]");
83+
config.file_descriptor_set_path(
84+
PathBuf::from(self.out_dir.as_ref()).join(FILE_DESCRIPTOR_SET),
85+
);
86+
if self.generate_service {
87+
config.include_file("_include.rs");
88+
if self.serde {
89+
config
90+
.message_attribute(".", "#[derive(::serde::Serialize, ::serde::Deserialize)]");
91+
}
7892
}
7993
config
8094
.compile_protos(self.protos, self.includes)
8195
.context("Compile protos by prost")?;
8296
Ok(())
8397
}
98+
99+
fn clean_up(&self) -> Result<()> {
100+
fs::remove_file(PathBuf::from(self.out_dir.as_ref()).join(FILE_DESCRIPTOR_SET))
101+
.context("Remove fd_set.bin")?;
102+
Ok(())
103+
}
84104
}
85105

86106
#[derive(Default)]
87-
pub struct CodegenBuilder<'a, P: AsRef<Path>> {
107+
pub struct CodegenBuilder<'a, P: AsRef<Path> + Default> {
88108
out_dir: Option<&'a P>,
89109
protos: Option<&'a [P]>,
90110
includes: Option<&'a [P]>,
91111
/// Whether to enable serde
92112
serde: Option<bool>,
93113
async_mode: Option<AsyncMode>,
114+
/// Whether to generate service
115+
generate_service: Option<bool>,
94116
}
95117

96118
impl<'a, P> CodegenBuilder<'a, P>
97119
where
98-
P: AsRef<Path>,
120+
P: AsRef<Path> + Default,
99121
{
100122
pub fn new() -> Self {
101-
Self {
102-
out_dir: None,
103-
protos: None,
104-
includes: None,
105-
serde: None,
106-
async_mode: None,
107-
}
123+
Default::default()
108124
}
109125

110126
pub fn set_out_dir(mut self, out_dir: &'a P) -> Self {
@@ -132,6 +148,11 @@ where
132148
self
133149
}
134150

151+
pub fn set_generate_service(mut self, generate_service: bool) -> Self {
152+
self.generate_service = Some(generate_service);
153+
self
154+
}
155+
135156
pub fn build(&self) -> Result<Codegen<'a, P>> {
136157
let out_dir = match self.out_dir {
137158
Some(out_dir) => out_dir,
@@ -158,12 +179,18 @@ where
158179
None => AsyncMode::None,
159180
};
160181

182+
let generate_service = match self.generate_service {
183+
Some(gen) => gen,
184+
None => false,
185+
};
186+
161187
Ok(Codegen {
162188
out_dir,
163189
protos,
164190
includes,
165191
serde,
166192
async_mode,
193+
generate_service,
167194
})
168195
}
169196
}

0 commit comments

Comments
 (0)