Pages

Saturday, January 29, 2011

Google protobufs and CMake

I'm currently using the google protobuf library to do some heavy serialization in man, the Northern Bites robotics platform. It's Google's de facto message passing method and is based on a special type of input file that defines a container (a 'message') which in turn is compiled by a special compiler to yield a class file (in C++, Java, Python, etc.). You can read more on protobuffers here.

A few days ago I integrated the compilation of said proto source files into the CMake build system that man is built upon and was pleasantly surprised by the ease of it.

These are excerpts of the CMakeFileList that was in the subfolder I kept my .proto files in.

First I start by scanning the folder for .proto files.


########################### PROTOBUF GENERATION

#protobuf input - finds all .proto files in the current dir
file(GLOB PROTO_INPUT
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
"*.proto")


Then I check for the protobuffer compiler (named protoc) and set the flags required for C output generation.


#protobuf compiler
find_program(PROTOC protoc)
set(PROTOC_C_OUT_FLAG --cpp_out)


We know that the compiler takes Foo.proto and outputs Foo.pb.cc and Foo.pb.h, so we take each .proto file, strip the extension from it and then add the .pb.cc and .pb.h extensions and the PROTO_GEN_DIR path prefix and then add them to a list variable named PROTO_GEN. This is important because we will need to know the name of the output to create valid dependencies later on.
Also since man uses an out-of-source build system, we set the PROTO_GEN_DIR to be the CMAKE_CURRENT_BINARY_DIR.


#protobuf output
set(PROTO_GEN_DIR ${CMAKE_CURRENT_BINARY_DIR})
foreach(PROTO_FILE ${PROTO_INPUT})
#get the name of the file without extension
get_filename_component(PROTO_NAME ${PROTO_FILE} NAME_WE)
#add the generated files
set(PROTO_GEN ${PROTO_GEN}
${PROTO_GEN_DIR}/${PROTO_NAME}.pb.h
${PROTO_GEN_DIR}/${PROTO_NAME}.pb.cc)
endforeach(PROTO_FILE ${PROTO_INPUT})


And now for the actual compile command, which we achieve by using add_custom_command. Notice how the OUTPUT is set to PROTO_GEN, which lets CMake know that all the files in the PROTO_GEN list are going to be generated by this command, so whenever it needs them for building a library or an executable it will run the command defined by COMMAND.

The DEPENDS ${PROTO_INPUT} just tells it that if any of the .proto files from PROTO_INPUT gets changed, the COMMAND has to be rerun.


# add the custom command that will generate all the files
add_custom_command(
OUTPUT ${PROTO_GEN}
COMMAND ${PROTOC} ${PROTO_INPUT} ${PROTOC_C_OUT_FLAG} ${CMAKE_CURRENT_BINARY_DIR}
DEPENDS ${PROTOC} ${PROTO_INPUT}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)


Done! Now you can use PROTO_GEN as source files to a target (I personally just add them to their own separate library), and whenever that target is called the protoc command will be called either if the generated files don't exist or the source .proto files have changed. Easy!

No comments:

Post a Comment