How to set up a project with Embedded Proto using Makefiles

BartExamples, Project Setup

Work smart and have code generated for you by your makefile! This example will demonstrate how to set up a project with Embedded Proto using Makefiles. All the *.proto files will automatically be detected; each source file code will be generated and built.

The following topics will be addressed:
1. Setup the project
2. Initiating Embedded Proto
3. Including all source files
4. Automatically convert *.proto files
5. Building the project

In this example, we assume you have already installed all the requirements for Embedded Proto. The requirements and installation instructions for your system are described in the Installation section on the documentation page.

Source code
The source code for this example is available on GitHub.
This example is up to date with version 3.4.2 of the source code.

1. Setup the project

In this example, we will use the following folder structure:

tree -L 1
├── src
├── proto
├── build
├── embeddedproto
└── Makefile

The src folder contains the source files for this example. The example code on GitHub is based on a NUCLEO-F466RE board by ST Microelectronics. The STM HAL is placed in the source folder to support the board.

All *.proto files for this example are located in the proto folder. There are two in this case request.proto and reply.proto.

When building, all build results will be placed in the build folder.

After cloning the example repository, you will not yet have the embeddedproto folder. Here we are using Embedded Proto as a git submodule which still needs to be initiated. The following chapter describes how you can do the same for your project.

2. Initiating Embedded Proto

If you are working from the example repository you can check it out and pull the submodule in one command:

git clone --recurse-submodules

If that is the case you can skip the rest and start reading the next section.

Now, if you are creating your project you better make a folder for it. After creating your project folder, change your current directory to the new folder. In that folder, we will add Embedded Proto as a submodule. After the clone is complete, enter the folder and checkout the latest version. To keep track of the version of Embedded Proto you are using in your project, add the .gitmodules file and commit it.

git submodule add
cd embeddedproto
git checkout latest
cd -
git add .gitmodules
git commit –m "Added the Embedded Proto library as a submodule on the latest version."

3. Including all source files

When using wildcards in a makefile the wildcard is executed before built targets. Therefore we can not generate the source files in the first target, add the new files using a wildcard and build the new code in the second target. A solution is to add the filenames we need by constructing them in the makefile before the source code exits.

First, let us define some variables we need often.

# Define where the *.proto files are located.
PROTO_DIR = proto

# Define the output directory in which to place the build results.
PROTO_GEN_DIR = build/generated

# The full path location of the Embedded Proto library.
EMBEDDED_PROTO_DIR = $(shell pwd)/embeddedproto

Next, we find all the *.proto files in the project. For this, we do use a wildcard expression.

# Find all the proto files.
# Extend this for subfolders.
PROTO_FILES = $(wildcard $(PROTO_DIR)/*.proto)

Based on the *.proto file names we can change the extension of the files to *.h and store those in a new variable.

# Convert the names of the proto files to the name of the 
# generated header files.
PROTO_HDR := $(PROTO_FILES:%%.proto=$(PROTO_GEN_DIR)/%%.h) 

Messages generated by Embedded Proto are pure header files without *.cpp files. The library itself does have a few *.cpp files. Those are the last ones we need to include.

# These are the source files from the Embedded Proto project.
# These are the files that support your messages.
EMBEDDED_PROTO_SRC := $(wildcard ./embeddedproto/src/*.cpp)

4. Automatically convert *.proto files

So all the *.proto files are located in the proto folder. How do we ensure that the makefile finds them and generates code? Again the names of the object files are first generated by means of changing the extension of all *.cpp files.

Next, a build target is defined, which will generate the header files based on the *.proto files. The build target called generate does not call protoc itself. By adding the list with header filenames as requirements, an implicit build target for each header will be called.

# Convert the filenames of source files to object names.

# Major targets
generate: $(PROTO_HDR)
	$(info Done generating source files based on *.proto files.)

# Implicit targets

# This rulle is used to generate the message source files based 
# on the *.proto files.
$(PROTO_GEN_DIR)/%%.h: %%.proto
	$(shell mkdir -p $(dir $@))
	cd $(EMBEDDED_PROTO_DIR) && $(PROTOC) --plugin=protoc-gen-eams -I../$(PROTO_DIR) --eams_out=../$(PROTO_GEN_DIR) ../$<

The implicit target makes sure the output folder exists, and it runs protoc. Protoc is run from the Embedded Proto folder such that the plugin is found using relative paths. The calls to change the directory and protoc are on one line with ampersands otherwise, each call would be executed in its own shell rendering the change dir useless.

The parameters to protoc ensure the Embedded Proto plugin is used, the required include folder is passed to protoc together with the output folder, and the *.proto file being processed is set.

5. Building the project

The final step is to build the project. You can do this simply by adding the generate target as a requirement for your debug, release, and all targets. This way, you can call make generate to just generate the message file or just make to build the whole project without having to call generate separately.

Changes in your proto files will be picked up by the makefile and considered in your next build.

Share Embedded Proto with your colleagues: