@ -4,20 +4,49 @@ use std::path::{Path, PathBuf};
pub struct CodeGen {
pub struct CodeGen {
inputs : Vec < PathBuf > ,
inputs : Vec < PathBuf > ,
output_dir : PathBuf ,
output_dir : PathBuf ,
protoc_path : Option < PathBuf > ,
protoc_gen_upb_minitable_path : Option < PathBuf > ,
includes : Vec < PathBuf > ,
includes : Vec < PathBuf > ,
}
}
const VERSION : & str = env! ( "CARGO_PKG_VERSION" ) ;
const VERSION : & str = env! ( "CARGO_PKG_VERSION" ) ;
// Given the output of "protoc --version", returns a shortened version string
// suitable for comparing against the protobuf crate version.
//
// The output of protoc --version looks something like "libprotoc XX.Y",
// optionally followed by "-dev" or "-rcN". We want to strip the "-dev" suffix
// if present and return something like "30.0" or "30.0-rc1".
fn protoc_version ( protoc_output : & str ) -> String {
let mut s = protoc_output . strip_prefix ( "libprotoc " ) . unwrap ( ) . to_string ( ) ;
let first_dash = s . find ( "-dev" ) ;
if let Some ( i ) = first_dash {
s . truncate ( i ) ;
}
s
}
// Given a crate version string, returns just the part of it suitable for
// comparing against the protoc version. The crate version is of the form
// "X.Y.Z" with an optional suffix starting with a dash. We want to drop the
// major version ("X.") and only keep the suffix if it starts with "-rc".
fn expected_protoc_version ( cargo_version : & str ) -> String {
let mut s = cargo_version . to_string ( ) ;
let is_release_candidate = s . find ( "-rc" ) ! = None ;
if ! is_release_candidate {
if let Some ( i ) = s . find ( '-' ) {
s . truncate ( i ) ;
}
}
let mut v : Vec < & str > = s . split ( '.' ) . collect ( ) ;
assert_eq! ( v . len ( ) , 3 ) ;
v . remove ( 0 ) ;
v . join ( "." )
}
impl CodeGen {
impl CodeGen {
pub fn new ( ) -> Self {
pub fn new ( ) -> Self {
Self {
Self {
inputs : Vec ::new ( ) ,
inputs : Vec ::new ( ) ,
output_dir : PathBuf ::from ( std ::env ::var ( "OUT_DIR" ) . unwrap ( ) ) . join ( "protobuf_generated" ) ,
output_dir : PathBuf ::from ( std ::env ::var ( "OUT_DIR" ) . unwrap ( ) ) . join ( "protobuf_generated" ) ,
protoc_path : None ,
protoc_gen_upb_minitable_path : None ,
includes : Vec ::new ( ) ,
includes : Vec ::new ( ) ,
}
}
}
}
@ -37,20 +66,6 @@ impl CodeGen {
self
self
}
}
pub fn protoc_path ( & mut self , protoc_path : impl AsRef < Path > ) -> & mut Self {
self . protoc_path = Some ( protoc_path . as_ref ( ) . to_owned ( ) ) ;
self
}
pub fn protoc_gen_upb_minitable_path (
& mut self ,
protoc_gen_upb_minitable_path : impl AsRef < Path > ,
) -> & mut Self {
self . protoc_gen_upb_minitable_path =
Some ( protoc_gen_upb_minitable_path . as_ref ( ) . to_owned ( ) ) ;
self
}
pub fn include ( & mut self , include : impl AsRef < Path > ) -> & mut Self {
pub fn include ( & mut self , include : impl AsRef < Path > ) -> & mut Self {
self . includes . push ( include . as_ref ( ) . to_owned ( ) ) ;
self . includes . push ( include . as_ref ( ) . to_owned ( ) ) ;
self
self
@ -92,12 +107,22 @@ impl CodeGen {
) ;
) ;
}
}
let protoc_path = if let Some ( path ) = & self . protoc_path {
let mut version_cmd = std ::process ::Command ::new ( "protoc" ) ;
path . clone ( )
let output = version_cmd
} else {
. arg ( "--version" )
protoc_path ( ) . expect ( "To be a supported platform" )
. output ( )
} ;
. map_err ( | e | format! ( "failed to run protoc --version: {}" , e ) ) ? ;
let mut cmd = std ::process ::Command ::new ( protoc_path ) ;
let protoc_version = protoc_version ( & String ::from_utf8 ( output . stdout ) . unwrap ( ) ) ;
let expected_protoc_version = expected_protoc_version ( VERSION ) ;
if protoc_version ! = expected_protoc_version {
panic! (
"Expected protoc version {} but found {}" ,
expected_protoc_version , protoc_version
) ;
}
let mut cmd = std ::process ::Command ::new ( "protoc" ) ;
for input in & self . inputs {
for input in & self . inputs {
cmd . arg ( input ) ;
cmd . arg ( input ) ;
}
}
@ -105,12 +130,6 @@ impl CodeGen {
// Attempt to make the directory if it doesn't exist
// Attempt to make the directory if it doesn't exist
let _ = std ::fs ::create_dir ( & self . output_dir ) ;
let _ = std ::fs ::create_dir ( & self . output_dir ) ;
}
}
let protoc_gen_upb_minitable_path = if let Some ( path ) = & self . protoc_gen_upb_minitable_path
{
path . clone ( )
} else {
protoc_gen_upb_minitable_path ( ) . expect ( "To be a supported platform" )
} ;
for include in & self . includes {
for include in & self . includes {
println! ( "cargo:rerun-if-changed={}" , include . display ( ) ) ;
println! ( "cargo:rerun-if-changed={}" , include . display ( ) ) ;
@ -118,10 +137,6 @@ impl CodeGen {
cmd . arg ( format! ( "--rust_out={}" , self . output_dir . display ( ) ) )
cmd . arg ( format! ( "--rust_out={}" , self . output_dir . display ( ) ) )
. arg ( "--rust_opt=experimental-codegen=enabled,kernel=upb" )
. arg ( "--rust_opt=experimental-codegen=enabled,kernel=upb" )
. arg ( format! (
"--plugin=protoc-gen-upb_minitable={}" ,
protoc_gen_upb_minitable_path . display ( )
) )
. arg ( format! ( "--upb_minitable_out={}" , self . output_dir . display ( ) ) ) ;
. arg ( format! ( "--upb_minitable_out={}" , self . output_dir . display ( ) ) ) ;
for include in & self . includes {
for include in & self . includes {
cmd . arg ( format! ( "--proto_path={}" , include . display ( ) ) ) ;
cmd . arg ( format! ( "--proto_path={}" , include . display ( ) ) ) ;
@ -162,32 +177,24 @@ impl CodeGen {
}
}
}
}
fn get_path_for_arch ( ) -> Option < PathBuf > {
#[ cfg(test) ]
let mut path = PathBuf ::from ( env! ( "CARGO_MANIFEST_DIR" ) ) ;
mod tests {
path . push ( "bin" ) ;
use super ::* ;
match ( std ::env ::consts ::OS , std ::env ::consts ::ARCH ) {
use googletest ::prelude ::* ;
( "macos" , "x86_64" ) = > path . push ( "osx-x86_64" ) ,
( "macos" , "aarch64" ) = > path . push ( "osx-aarch_64" ) ,
( "linux" , "aarch64" ) = > path . push ( "linux-aarch_64" ) ,
( "linux" , "powerpc64" ) = > path . push ( "linux-ppcle_64" ) ,
( "linux" , "s390x" ) = > path . push ( "linux-s390_64" ) ,
( "linux" , "x86" ) = > path . push ( "linux-x86_32" ) ,
( "linux" , "x86_64" ) = > path . push ( "linux-x86_64" ) ,
( "windows" , "x86" ) = > path . push ( "win32" ) ,
( "windows" , "x86_64" ) = > path . push ( "win64" ) ,
_ = > return None ,
} ;
Some ( path )
}
pub fn protoc_path ( ) -> Option < PathBuf > {
#[ googletest::test ]
let mut path = get_path_for_arch ( ) ? ;
fn test_protoc_version ( ) {
path . push ( "protoc" ) ;
assert_eq! ( protoc_version ( "libprotoc 30.0" ) , "30.0" ) ;
Some ( path )
assert_eq! ( protoc_version ( "libprotoc 30.0-dev" ) , "30.0" ) ;
}
assert_eq! ( protoc_version ( "libprotoc 30.0-rc1" ) , "30.0-rc1" ) ;
}
pub fn protoc_gen_upb_minitable_path ( ) -> Option < PathBuf > {
#[ googletest::test ]
let mut path = get_path_for_arch ( ) ? ;
fn test_expected_protoc_version ( ) {
path . push ( "protoc-gen-upb_minitable" ) ;
assert_eq! ( expected_protoc_version ( "4.30.0" ) , "30.0" ) ;
Some ( path )
assert_eq! ( expected_protoc_version ( "4.30.0-alpha" ) , "30.0" ) ;
assert_eq! ( expected_protoc_version ( "4.30.0-beta" ) , "30.0" ) ;
assert_eq! ( expected_protoc_version ( "4.30.0-pre" ) , "30.0" ) ;
assert_eq! ( expected_protoc_version ( "4.30.0-rc1" ) , "30.0-rc1" ) ;
}
}
}