Improve tonic-build configuration (#2)
This commit is contained in:
committed by
Lucio Franco
parent
1c2905255e
commit
eb0c0c286b
@@ -12,7 +12,6 @@ syn = "1.0"
|
|||||||
quote = "1.0"
|
quote = "1.0"
|
||||||
proc-macro2 = "1.0"
|
proc-macro2 = "1.0"
|
||||||
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["transport"]
|
default = ["transport"]
|
||||||
rustfmt = []
|
rustfmt = []
|
||||||
|
|||||||
+132
-38
@@ -2,52 +2,124 @@
|
|||||||
//! and proto definitiones for use with `tonic`.
|
//! and proto definitiones for use with `tonic`.
|
||||||
//!
|
//!
|
||||||
//! # Examples
|
//! # Examples
|
||||||
|
//! Simple
|
||||||
//!
|
//!
|
||||||
//! ```rust,no_run
|
//! ```rust,no_run
|
||||||
//! fn main() {
|
//! fn main() {
|
||||||
//! tonic_build::compile_protos(
|
//! tonic_build::compile_protos("proto/service.proto").unwrap();
|
||||||
//! &["proto/helloworld/helloworld.proto"],
|
|
||||||
//! &["proto/helloworld"],
|
|
||||||
//! "helloworld",
|
|
||||||
//! )
|
|
||||||
//! .unwrap();
|
|
||||||
//! }
|
//! }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! Configuration
|
||||||
|
//!
|
||||||
|
//! ```rust,no_run
|
||||||
|
//! fn main() {
|
||||||
|
//! tonic_build::configure()
|
||||||
|
//! .build_server(false)
|
||||||
|
//! .compile(
|
||||||
|
//! &["proto/helloworld/helloworld.proto"],
|
||||||
|
//! &["proto/helloworld"],
|
||||||
|
//! "helloworld",
|
||||||
|
//! )
|
||||||
|
//! .unwrap();
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
|
||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
use prost_build::Config;
|
use prost_build::Config;
|
||||||
|
|
||||||
#[cfg(feature = "rustfmt")]
|
#[cfg(feature = "rustfmt")]
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
use std::{io, path, path::Path};
|
use std::{
|
||||||
|
io,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
};
|
||||||
|
|
||||||
mod client;
|
mod client;
|
||||||
mod service;
|
mod service;
|
||||||
|
|
||||||
pub fn compile_protos<P>(protos: &[P], includes: &[P], package: &str) -> io::Result<()>
|
#[derive(Clone)]
|
||||||
where
|
pub struct Builder {
|
||||||
P: AsRef<path::Path>,
|
build_client: bool,
|
||||||
{
|
build_server: bool,
|
||||||
let out_dir = std::env::var("OUT_DIR").unwrap();
|
out_dir: PathBuf,
|
||||||
compile_protos_with_out_dir(protos, includes, package, out_dir.as_str())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused_variables)]
|
impl Builder {
|
||||||
pub fn compile_protos_with_out_dir<P: AsRef<Path>>(
|
/// Enable or disable gRPC client code generation.
|
||||||
protos: &[P],
|
pub fn build_client(mut self, enable: bool) -> Self {
|
||||||
includes: &[P],
|
self.build_client = enable;
|
||||||
package: &str,
|
self
|
||||||
out_dir: impl AsRef<Path>,
|
}
|
||||||
) -> io::Result<()> {
|
|
||||||
let mut config = Config::new();
|
|
||||||
|
|
||||||
config.service_generator(Box::new(ServiceGenerator::default()));
|
/// Enable or disable gRPC server code generation.
|
||||||
config.out_dir(out_dir.as_ref());
|
pub fn build_server(mut self, enable: bool) -> Self {
|
||||||
config.compile_protos(protos, includes)?;
|
self.build_server = enable;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "rustfmt")]
|
/// Set the output directory to generate code to.
|
||||||
fmt(
|
///
|
||||||
out_dir.as_ref().to_str().expect("Execpted utf8 out_dir"),
|
/// Defaults to the `OUT_DIR` environment variable.
|
||||||
&format!("{}.rs", package),
|
pub fn out_dir(mut self, out_dir: impl AsRef<Path>) -> Self {
|
||||||
);
|
self.out_dir = out_dir.as_ref().to_path_buf();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compile the .proto files and execute code generation.
|
||||||
|
#[cfg_attr(not(feature = "rustfmt"), allow(unused_variables))]
|
||||||
|
pub fn compile<P: AsRef<Path>>(
|
||||||
|
self,
|
||||||
|
protos: &[P],
|
||||||
|
includes: &[P],
|
||||||
|
package: &str,
|
||||||
|
) -> io::Result<()> {
|
||||||
|
let mut config = Config::new();
|
||||||
|
|
||||||
|
config.out_dir(self.out_dir.clone());
|
||||||
|
config.service_generator(Box::new(ServiceGenerator::new(self)));
|
||||||
|
config.compile_protos(protos, includes)?;
|
||||||
|
|
||||||
|
#[cfg(feature = "rustfmt")]
|
||||||
|
fmt(
|
||||||
|
out_dir.as_ref().to_str().expect("Execpted utf8 out_dir"),
|
||||||
|
&format!("{}.rs", package),
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configure tonic-build code generation.
|
||||||
|
///
|
||||||
|
/// Use [`compile_protos`] instead if you don't need to tweak anything.
|
||||||
|
pub fn configure() -> Builder {
|
||||||
|
Builder {
|
||||||
|
build_client: true,
|
||||||
|
build_server: true,
|
||||||
|
out_dir: PathBuf::from(std::env::var("OUT_DIR").unwrap()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Simple `.proto` compiling. Use [`configure`] instead if you need more options.
|
||||||
|
///
|
||||||
|
/// The include directory will be the parent folder of the specified path.
|
||||||
|
/// The package name will be the filename without the extension.
|
||||||
|
pub fn compile_protos(proto_path: impl AsRef<Path>) -> io::Result<()> {
|
||||||
|
let proto_path: &Path = proto_path.as_ref();
|
||||||
|
|
||||||
|
let package = proto_path
|
||||||
|
.file_stem()
|
||||||
|
.expect("file should have a stem if it has an extension")
|
||||||
|
.to_str()
|
||||||
|
.expect("expected valid utf-8 filename");
|
||||||
|
|
||||||
|
// directory the main .proto file resides in
|
||||||
|
let proto_dir = proto_path
|
||||||
|
.parent()
|
||||||
|
.expect("proto file should reside in a directory");
|
||||||
|
|
||||||
|
self::configure().compile(&[proto_path], &[proto_dir], package)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -67,36 +139,58 @@ fn fmt(out_dir: &str, file: &str) {
|
|||||||
assert!(out.status.success());
|
assert!(out.status.success());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct ServiceGenerator {
|
pub struct ServiceGenerator {
|
||||||
|
builder: Builder,
|
||||||
clients: TokenStream,
|
clients: TokenStream,
|
||||||
servers: TokenStream,
|
servers: TokenStream,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ServiceGenerator {
|
||||||
|
fn new(builder: Builder) -> Self {
|
||||||
|
ServiceGenerator {
|
||||||
|
builder,
|
||||||
|
clients: TokenStream::default(),
|
||||||
|
servers: TokenStream::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl prost_build::ServiceGenerator for ServiceGenerator {
|
impl prost_build::ServiceGenerator for ServiceGenerator {
|
||||||
fn generate(&mut self, service: prost_build::Service, _buf: &mut String) {
|
fn generate(&mut self, service: prost_build::Service, _buf: &mut String) {
|
||||||
let path = "super";
|
let path = "super";
|
||||||
|
|
||||||
let server = service::generate(&service, path);
|
if self.builder.build_server {
|
||||||
self.servers.extend(server);
|
let server = service::generate(&service, path);
|
||||||
|
self.servers.extend(server);
|
||||||
|
}
|
||||||
|
|
||||||
let client = client::generate(&service, path);
|
if self.builder.build_client {
|
||||||
self.clients.extend(client);
|
let client = client::generate(&service, path);
|
||||||
|
self.clients.extend(client);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finalize(&mut self, buf: &mut String) {
|
fn finalize(&mut self, buf: &mut String) {
|
||||||
if !self.clients.is_empty() && !self.servers.is_empty() {
|
if self.builder.build_client && !self.clients.is_empty() {
|
||||||
let clients = &self.clients;
|
let clients = &self.clients;
|
||||||
let servers = &self.servers;
|
|
||||||
|
|
||||||
let service = quote::quote! {
|
let client_service = quote::quote! {
|
||||||
pub mod client {
|
pub mod client {
|
||||||
#![allow(unused_variables, dead_code, missing_docs)]
|
#![allow(unused_variables, dead_code, missing_docs)]
|
||||||
use tonic::codegen::*;
|
use tonic::codegen::*;
|
||||||
|
|
||||||
#clients
|
#clients
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let code = format!("{}", client_service);
|
||||||
|
buf.push_str(&code);
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.builder.build_server && !self.servers.is_empty() {
|
||||||
|
let servers = &self.servers;
|
||||||
|
|
||||||
|
let server_service = quote::quote! {
|
||||||
pub mod server {
|
pub mod server {
|
||||||
#![allow(unused_variables, dead_code, missing_docs)]
|
#![allow(unused_variables, dead_code, missing_docs)]
|
||||||
use tonic::codegen::*;
|
use tonic::codegen::*;
|
||||||
@@ -105,7 +199,7 @@ impl prost_build::ServiceGenerator for ServiceGenerator {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let code = format!("{}", service);
|
let code = format!("{}", server_service);
|
||||||
buf.push_str(&code);
|
buf.push_str(&code);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-13
@@ -1,15 +1,4 @@
|
|||||||
fn main() {
|
fn main() {
|
||||||
tonic_build::compile_protos(
|
tonic_build::compile_protos("proto/helloworld/helloworld.proto").unwrap();
|
||||||
&["proto/helloworld/helloworld.proto"],
|
tonic_build::compile_protos("proto/routeguide/route_guide.proto").unwrap();
|
||||||
&["proto/helloworld"],
|
|
||||||
"helloworld",
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
tonic_build::compile_protos(
|
|
||||||
&["proto/routeguide/route_guide.proto"],
|
|
||||||
&["proto/routeguide"],
|
|
||||||
"routeguide",
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,8 @@
|
|||||||
fn main() {
|
fn main() {
|
||||||
let files = &["proto/grpc/testing/test.proto"];
|
let proto = "proto/grpc/testing/test.proto";
|
||||||
let dirs = &["proto/grpc/testing"];
|
|
||||||
|
|
||||||
tonic_build::compile_protos(files, dirs, "grpc.testing").unwrap();
|
tonic_build::compile_protos(proto).unwrap();
|
||||||
|
|
||||||
// prevent needing to rebuild if files (or deps) haven't changed
|
// prevent needing to rebuild if files (or deps) haven't changed
|
||||||
for file in files {
|
println!("cargo:rerun-if-changed={}", proto);
|
||||||
println!("cargo:rerun-if-changed={}", file);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user