diff --git a/rust/release_crates/protobuf_codegen/src/lib.rs b/rust/release_crates/protobuf_codegen/src/lib.rs index fb708e6de2eaf..f90ad6e9cfb30 100644 --- a/rust/release_crates/protobuf_codegen/src/lib.rs +++ b/rust/release_crates/protobuf_codegen/src/lib.rs @@ -1,10 +1,28 @@ +use std::fs::File; +use std::io::Write; use std::path::{Path, PathBuf}; +#[derive(Debug, Clone)] +pub struct Dependency { + pub crate_name: String, + pub import_paths: Vec, + pub include_paths: Vec, + pub proto_files: Vec, +} + #[derive(Debug)] pub struct CodeGen { inputs: Vec, output_dir: PathBuf, includes: Vec, + dependencies: Vec, +} + +#[macro_export] +macro_rules! proto_dep { + ($c:ident) => { + $c::get_dependency(stringify!($c)) + } } const VERSION: &str = env!("CARGO_PKG_VERSION"); @@ -62,6 +80,7 @@ impl CodeGen { inputs: Vec::new(), output_dir: PathBuf::from(std::env::var("OUT_DIR").unwrap()).join("protobuf_generated"), includes: Vec::new(), + dependencies: Vec::new(), } } @@ -90,6 +109,11 @@ impl CodeGen { self } + pub fn dependency(&mut self, dep: Dependency) -> &mut Self { + self.dependencies.push(dep.clone()); + self + } + fn expected_generated_rs_files(&self) -> Vec { self.inputs .iter() @@ -147,6 +171,23 @@ impl CodeGen { for include in &self.includes { println!("cargo:rerun-if-changed={}", include.display()); } + for dep in &self.dependencies { + for path in &dep.import_paths { + println!("cargo:rerun-if-changed={}", path.display()); + } + } + + let crate_mapping_path = self.output_dir.join("crate_mapping.txt"); + { + let mut file = File::create(crate_mapping_path.clone()).unwrap(); + for dep in &self.dependencies { + file.write_all(format!("{}\n", dep.crate_name).as_bytes()).expect(""); + file.write_all(format!("{}\n", dep.proto_files.len()).as_bytes()).expect(""); + for f in &dep.proto_files { + file.write_all(format!("{}\n", f).as_bytes()).expect(""); + } + } + } cmd.arg(format!("--rust_out={}", self.output_dir.display())) .arg("--rust_opt=experimental-codegen=enabled,kernel=upb") @@ -154,6 +195,12 @@ impl CodeGen { for include in &self.includes { cmd.arg(format!("--proto_path={}", include.display())); } + for dep in &self.dependencies { + for path in &dep.import_paths { + cmd.arg(format!("--proto_path={}", path.display())); + } + } + cmd.arg(format!("--rust_opt=crate_mapping={}", crate_mapping_path.display())); let output = cmd.output().map_err(|e| format!("failed to run protoc: {}", e))?; println!("{}", std::str::from_utf8(&output.stdout).unwrap()); eprintln!("{}", std::str::from_utf8(&output.stderr).unwrap()); @@ -172,6 +219,12 @@ impl CodeGen { .include(self.output_dir.clone()) .flag("-std=c99"); + for dep in &self.dependencies { + for path in &dep.include_paths { + cc_build.include(path); + } + } + for path in &self.expected_generated_rs_files() { if !path.exists() { return Err(format!("expected generated file {} does not exist", path.display())); diff --git a/rust/release_crates/protobuf_example/Cargo-template.toml b/rust/release_crates/protobuf_example/Cargo-template.toml index ffa002877b613..d430d15a576a6 100644 --- a/rust/release_crates/protobuf_example/Cargo-template.toml +++ b/rust/release_crates/protobuf_example/Cargo-template.toml @@ -7,6 +7,8 @@ license = "BSD-3-Clause" [dependencies] protobuf = { version = "{VERSION}", path = "../protobuf", package = "protobuf" } +protobuf_well_known_types = { version = "{VERSION}", path = "../protobuf_well_known_types", package = "protobuf-well-known-types" } [build-dependencies] protobuf-codegen = { version = "{VERSION}", path = "../protobuf_codegen", package = "protobuf-codegen" } +protobuf_well_known_types = { version = "{VERSION}", path = "../protobuf_well_known_types", package = "protobuf-well-known-types" } diff --git a/rust/release_crates/protobuf_example/build.rs b/rust/release_crates/protobuf_example/build.rs index 0924c662d408a..d8e40dc541d1f 100644 --- a/rust/release_crates/protobuf_example/build.rs +++ b/rust/release_crates/protobuf_example/build.rs @@ -1,9 +1,11 @@ use protobuf_codegen::CodeGen; +use protobuf_codegen::proto_dep; fn main() { CodeGen::new() .inputs(["proto_example/foo.proto", "proto_example/bar/bar.proto"]) .include("proto") + .dependency(proto_dep!(protobuf_well_known_types)) .generate_and_compile() .unwrap(); } diff --git a/rust/release_crates/protobuf_example/proto/proto_example/foo.proto b/rust/release_crates/protobuf_example/proto/proto_example/foo.proto index 83f3c4aaabaa2..19c12903ea6d6 100644 --- a/rust/release_crates/protobuf_example/proto/proto_example/foo.proto +++ b/rust/release_crates/protobuf_example/proto/proto_example/foo.proto @@ -2,6 +2,7 @@ edition = "2023"; package proto_example; +import "google/protobuf/timestamp.proto"; import "proto_example/bar/bar.proto"; message Foo { @@ -9,4 +10,5 @@ message Foo { repeated int32 numbers = 2; string name = 3; Bar bar = 4; + google.protobuf.Timestamp timestamp = 5; } diff --git a/rust/release_crates/protobuf_example/src/main.rs b/rust/release_crates/protobuf_example/src/main.rs index ecb3cb9b0a89a..2101945c6a912 100644 --- a/rust/release_crates/protobuf_example/src/main.rs +++ b/rust/release_crates/protobuf_example/src/main.rs @@ -30,4 +30,12 @@ mod tests { let nums: Vec<_> = foo.bar().numbers().iter().collect(); assert_eq!(nums, vec![1, 2, 3]); } + + #[test] + fn set_well_known_type() { + let foo = proto!(Foo { timestamp: __ { seconds: 99, nanos: 13 } }); + + assert_eq!(foo.timestamp().seconds(), 99); + assert_eq!(foo.timestamp().nanos(), 13); + } } diff --git a/rust/release_crates/protobuf_well_known_types/Cargo-template.toml b/rust/release_crates/protobuf_well_known_types/Cargo-template.toml index 34e47c21913e3..a2d4104f56c55 100644 --- a/rust/release_crates/protobuf_well_known_types/Cargo-template.toml +++ b/rust/release_crates/protobuf_well_known_types/Cargo-template.toml @@ -7,6 +7,7 @@ license = "BSD-3-Clause" [dependencies] protobuf = { version = "{VERSION}", path = "../protobuf", package = "protobuf" } +protobuf-codegen = { version = "{VERSION}", path = "../protobuf_codegen", package = "protobuf-codegen" } [build-dependencies] protobuf-codegen = { version = "{VERSION}", path = "../protobuf_codegen", package = "protobuf-codegen" } diff --git a/rust/release_crates/protobuf_well_known_types/src/lib.rs b/rust/release_crates/protobuf_well_known_types/src/lib.rs index 9a7c8b7e9bb45..dd5e93b63f04c 100644 --- a/rust/release_crates/protobuf_well_known_types/src/lib.rs +++ b/rust/release_crates/protobuf_well_known_types/src/lib.rs @@ -1 +1,23 @@ +use std::path::Path; + include!(concat!(env!("OUT_DIR"), "/protobuf_generated/google/protobuf/generated.rs")); + +pub fn get_dependency(crate_name: &str) -> protobuf_codegen::Dependency { + protobuf_codegen::Dependency { + crate_name: crate_name.to_string(), + import_paths: vec!(Path::new(env!("CARGO_MANIFEST_DIR")).join("proto")), + include_paths: vec!(Path::new(env!("OUT_DIR")).join("protobuf_generated")), + proto_files: vec!( + "google/protobuf/any.proto".to_string(), + "google/protobuf/api.proto".to_string(), + "google/protobuf/duration.proto".to_string(), + "google/protobuf/empty.proto".to_string(), + "google/protobuf/field_mask.proto".to_string(), + "google/protobuf/source_context.proto".to_string(), + "google/protobuf/struct.proto".to_string(), + "google/protobuf/timestamp.proto".to_string(), + "google/protobuf/type.proto".to_string(), + "google/protobuf/wrappers.proto".to_string(), + ), + } +}