Skip to content

Commit 2867cd2

Browse files
committed
xtask: Add code to generate pbv3 files
Signed-off-by: Ludovic Barman <[email protected]>
1 parent f39ab1a commit 2867cd2

File tree

2 files changed

+119
-35
lines changed

2 files changed

+119
-35
lines changed

xtask/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@ prost-build = "0.11"
1111
# Use an old enough version to make sure generated code with TiKV's fork.
1212
# TODO: use latest one when TiKV's fork is updated
1313
protoc-rust = "=2.8"
14+
protobuf-codegen = "3.2.0"

xtask/src/main.rs

Lines changed: 118 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use protobuf_codegen;
12
use std::process::{self, Command};
23
use std::{
34
env,
@@ -116,18 +117,31 @@ const PROTOS: &[(&str, &[&str], &str, &str)] = &[
116117
("proto/proto", &["google/rpc"], "proto/src/proto", "google/rpc"),
117118
];
118119

119-
const NAMING_PATCH: &[(&str, &[(&str, &str)])] = &[(
120-
"health/src/proto/protobuf/health.rs",
121-
&[
122-
("HealthCheckResponse_ServingStatus", "ServingStatus"),
123-
// Order is important.
124-
("NOT_SERVING", "NotServing"),
125-
("SERVICE_UNKNOWN", "ServiceUnknown"),
126-
("UNKNOWN", "Unknown"),
127-
("SERVING", "Serving"),
128-
("rustfmt_skip", "rustfmt::skip"),
129-
],
130-
)];
120+
const NAMING_PATCH: &[(&str, &[(&str, &str)])] = &[
121+
(
122+
"health/src/proto/protobuf/health.rs",
123+
&[
124+
("HealthCheckResponse_ServingStatus", "ServingStatus"),
125+
// Order is important.
126+
("NOT_SERVING", "NotServing"),
127+
("SERVICE_UNKNOWN", "ServiceUnknown"),
128+
("UNKNOWN", "Unknown"),
129+
("SERVING", "Serving"),
130+
("rustfmt_skip", "rustfmt::skip"),
131+
],
132+
),
133+
(
134+
"health/src/proto/protobuf_v3/health.rs",
135+
&[
136+
// Order is important.
137+
("NOT_SERVING", "NotServing"),
138+
("SERVICE_UNKNOWN", "ServiceUnknown"),
139+
("UNKNOWN", "Unknown"),
140+
("SERVING", "Serving"),
141+
("rustfmt_skip", "rustfmt::skip"),
142+
],
143+
),
144+
];
131145

132146
fn modify(path: impl AsRef<Path>, f: impl FnOnce(&mut String)) {
133147
let path = path.as_ref();
@@ -140,21 +154,16 @@ fn modify(path: impl AsRef<Path>, f: impl FnOnce(&mut String)) {
140154
File::create(path).unwrap().write_all(content.as_bytes()).unwrap();
141155
}
142156

143-
fn generate_protobuf(protoc: &Path, include: &str, inputs: &[&str], out_dir: &str) {
157+
/// If out_dir already exists, deletes and recreates it.
158+
fn delete_and_mkdir(out_dir: &str) {
144159
if Path::new(out_dir).exists() {
145160
fs::remove_dir_all(out_dir).unwrap();
146161
}
147162
fs::create_dir_all(out_dir).unwrap();
163+
}
148164

149-
// TODO: update rust-protobuf to allow specifying protoc explicitly.
150-
protoc_rust::run(protoc_rust::Args {
151-
out_dir,
152-
includes: &[include],
153-
input: inputs,
154-
customize: protoc_rust::Customize::default(),
155-
})
156-
.unwrap();
157-
165+
/// Builds grpcio-compiler and uses it to generate _grpc.rs files. Used in both protobufv2 and v3.
166+
fn run_gen_grpc(protoc: &Path, include: &str, inputs: &[&str], out_dir: &str) {
158167
exec(cargo().args(&["build", "-p", "grpcio-compiler"]));
159168
let mut c = cmd(protoc);
160169
c.arg(format!("-I{}", include))
@@ -164,38 +173,101 @@ fn generate_protobuf(protoc: &Path, include: &str, inputs: &[&str], out_dir: &st
164173
c.arg(i);
165174
}
166175
exec(&mut c);
176+
}
167177

178+
// Does string replacements on predefined files. Used with protobuf v2 and v3.
179+
fn apply_naming_patch() {
168180
for (path, name_fixes) in NAMING_PATCH {
169181
modify(path, |content| {
170182
for (old, new) in *name_fixes {
171183
*content = content.replace(old, new);
172184
}
173185
});
174186
}
187+
}
188+
189+
/// Loops over all _grpc.rs files in out_dir, and if a corresponding .rs file exists, links it by adding a "use" statement.
190+
fn link_pb_with_grpc_rs(out_dir: &str) {
191+
for f in fs::read_dir(out_dir).unwrap() {
192+
let path = f.unwrap().path();
193+
let file_name = path.file_name().unwrap().to_str().unwrap().to_string();
194+
if !file_name.ends_with("_grpc.rs") {
195+
continue;
196+
}
197+
// remove _grpc
198+
let pb_file_name = format!("{}.rs", &file_name[..file_name.len() - 8]);
199+
let pb_path = path.with_file_name(pb_file_name);
200+
// remove .rs
201+
let module_name = &file_name[..file_name.len() - 3];
202+
modify(pb_path, |content| {
203+
content.push_str(&format!("\npub use super::{}::*;\n", module_name));
204+
});
205+
}
206+
}
207+
208+
/// Removes the protobuf version constraint in all .rs files in out_dir.
209+
/// note: now that we have distinct protobuf v2 and v3 generated files, not sure this step is necessary or good practice anymore
210+
fn remove_protobuf_version_constraint(out_dir: &str) {
211+
for f in fs::read_dir(out_dir).unwrap() {
212+
let path = f.unwrap().path();
213+
if path.extension().unwrap() == "rs" {
214+
modify(path, |content| {
215+
*content = remove_match(&content, |l| l.contains("::protobuf::VERSION"));
216+
});
217+
}
218+
}
219+
}
220+
221+
fn generate_protobuf(protoc: &Path, include: &str, inputs: &[&str], out_dir: &str) {
222+
delete_and_mkdir(out_dir);
223+
224+
// TODO: update rust-protobuf to allow specifying protoc explicitly.
225+
protoc_rust::run(protoc_rust::Args {
226+
out_dir,
227+
includes: &[include],
228+
input: inputs,
229+
customize: protoc_rust::Customize::default(),
230+
})
231+
.unwrap();
232+
233+
run_gen_grpc(protoc, include, inputs, out_dir);
234+
apply_naming_patch();
235+
link_pb_with_grpc_rs(out_dir);
236+
// note: now that we have distinct protobuf v2 and v3 generated files, not sure this step is necessary or good practice anymore
237+
remove_protobuf_version_constraint(out_dir);
238+
}
239+
240+
fn generate_protobufv3(protoc: &Path, include: &str, inputs: &[&str], out_dir: &str) {
241+
delete_and_mkdir(out_dir);
242+
243+
let _ = protobuf_codegen::Codegen::new()
244+
.protoc()
245+
.includes([include])
246+
.inputs(inputs)
247+
.out_dir(out_dir)
248+
.run();
249+
250+
run_gen_grpc(protoc, include, inputs, out_dir);
251+
apply_naming_patch();
175252

176253
for f in fs::read_dir(out_dir).unwrap() {
177254
let p = f.unwrap();
178255
if p.path().extension().unwrap() == "rs" {
179-
let file_name = p.path().file_name().unwrap().to_str().unwrap().to_string();
180-
if file_name.ends_with("_grpc.rs") {
181-
let pb_path = p.path().with_file_name(format!("{}.rs", &file_name[..file_name.len() - 8]));
182-
modify(pb_path, |content| {
183-
content.push_str(&format!("\npub use super::{}::*;\n", &file_name[..file_name.len() - 3]));
184-
});
185-
}
186256
modify(p.path(), |content| {
187-
*content = remove_match(&content, |l| l.contains("::protobuf::VERSION"));
257+
*content = content.replace("::protobuf::", "::protobufv3::");
188258
});
189259
}
190260
}
261+
262+
link_pb_with_grpc_rs(out_dir);
263+
// note: now that we have distinct protobuf v2 and v3 generated files, not sure this step is necessary or good practice anymore
264+
remove_protobuf_version_constraint(out_dir);
191265
}
192266

193267
fn generate_prost(protoc: &Path, include: &str, inputs: &[&str], out_dir: &str) {
194268
env::set_var("PROTOC", protoc);
195-
if Path::new(out_dir).exists() {
196-
fs::remove_dir_all(out_dir).unwrap();
197-
}
198-
fs::create_dir_all(out_dir).unwrap();
269+
delete_and_mkdir(out_dir);
270+
199271
exec(
200272
cargo()
201273
.args(&[
@@ -242,7 +314,18 @@ fn codegen() {
242314
&inputs_ref,
243315
&format!("{}/protobuf/{}", out_dir, package),
244316
);
245-
generate_prost(&protoc, include, &inputs_ref, &format!("{}/prost/{}", out_dir, package));
317+
generate_protobufv3(
318+
&protoc,
319+
include,
320+
&inputs_ref,
321+
&format!("{}/protobuf_v3/{}", out_dir, package),
322+
);
323+
generate_prost(
324+
&protoc,
325+
include,
326+
&inputs_ref,
327+
&format!("{}/prost/{}", out_dir, package),
328+
);
246329
}
247330
exec(cargo().args(&["fmt", "--all"]))
248331
}

0 commit comments

Comments
 (0)