Today we have a guest post by Paul Shepherd. Paul describes the process he uses to define custom build steps when using Eclipse auto-generated Makefiles. He uses build version tagging to demonstrate this process.
Paul is the Lead Electrical Engineer at Benchmark Space Systems. He has spent time in the Aerospace, Consumer Audio, and Integrated Circuits fields, more often than not working at the intersection of hardware and software. You can contact him via LinkedIn.
Using Custom Build Steps with Eclipse Auto-generated Makefiles
Benchmark Space Systems recently had the opportunity to work with Embedded Artistry to develop our internal Software Release and Continuous Integration processes. From our first meeting, it was clear we shared a common belief that a good process is one where quality is the default outcome, rather than something you have to fight for. Furthermore, best practices aren’t dependent on your tools, but sometimes your tools guide how you implement those best practices. (Side note: Embedded Artistry’s Technology Radar is a good survey of the space that spans best practices and tools.)
In this short series, I’ll share how we implemented our CI process steps. In today’s post, we will cover implementing build version tagging with the Eclipse IDE and auto-generated Makefiles. In the next post, I’ll share how we got unit testing reports up and running on our production build server.
Benchmark Space Systems’ electronics utilize the MSP430FR5969-SP or its standard-reliability sibling. We use TI’s Code Composer Studio (CCS) for most of our development. CCS is built on the Eclipse IDE platform. We use auto-generated Makefiles to build our project locally.
We create our production builds on an Ubuntu build server running Jenkins. Performing the steps to define a build version is straightforward on the build server, since we maintain a custom Makefile under source control for producing our production builds. However, implementing the versioning steps with the Eclipse IDE posed a problem: if we are letting Eclipse automatically generate your Makefile, how can we add custom steps to the build process?
Inserting Custom Build Steps
Fortunately, Eclipse provides mechanisms for implementing custom build steps: the
makefile.targets files. The auto-generated Makefile always includes these files from the project root directory. These files are optional, and the build process will work if they do not exist.
Makefile.init is included at the beginning and allows for custom initialization steps.
Makefile.defs is included before the first compiler invocation and allows symbols to be defined. Finally, makefile.targets is included at the end of the generated makefile, and allows for defining custom build targets.
For build-tagging, we define the variable
makefile.defs, and the compiler invocation includes this variable. This variable isn’t limited to just defining new symbols, so it has to include the complete argument, starting with
–define as you can see below.
Since Eclipse automatically includes this file in the generated Makefile, these symbols will be used by the compile steps.
Handling Different Operating Systems
The exact formatting of the commands or expressions used within the Makefile depends on the operating system and shell you are using. On Windows, Eclipse will use
cmd.exe to run all of the build steps, while Ubuntu uses Bash.
For example, we use the shell to generate the build timestamp and build machine strings. On Windows, the OS won’t easily give us a tidy build date and time, so we execute a two statement Python script:
BUILD_DATE := $(shell python -c "from datetime import datetime; print(datetime.utcnow().strftime('%d/%m/%Y, %H:%M'))" BUILD_MACHINE := $(shell echo %username%)@$(shell hostname)
While Linux is much simpler, and only needs:
BUILD_DATE := $(shell date -u +"%d/%m/%Y, %H:%M") BUILD_MACHINE := $(shell whoami)@$(shell hostname)
In order to support multiple platforms, we use the
$(SHELL) variable in our
makefile.defs file to select the proper behavior.
ifeq ($(SHELL), cmd.exe) BUILD_DATE := $(shell python -c "from datetime import datetime; print(datetime.utcnow().strftime('%d/%m/%Y, %H:%M'))" BUILD_MACHINE := $(shell echo %username%)@$(shell hostname) else BUILD_MACHINE := $(shell whoami)@$(shell hostname) BUILD_DATE := $(shell date -u +"%d/%m/%Y, %H:%M") endif
Putting it All Together
Here is our full
ifeq ($(SHELL), cmd.exe) BUILD_DATE := $(shell python -c "from datetime import datetime; print(datetime.utcnow().strftime('%d/%m/%Y, %H:%M'))" BUILD_MACHINE := $(shell echo %username%)@$(shell hostname) else BUILD_MACHINE := $(shell whoami)@$(shell hostname) BUILD_DATE := $(shell date -u +"%d/%m/%Y, %H:%M") endif version := $(subst -, ,$(shell git describe --long --dirty --tags)) COMMIT := $(strip $(word 3, $(version))) COMMITS_PAST := $(strip $(word 2, $(version))) DIRTY := $(strip $(word 4, $(version))) ifneq ($(COMMITS_PAST), 0) BUILD_INFO_COMMITS := .$(COMMITS_PAST) endif ifneq ($(DIRTY),) BUILD_INFO_DIRTY :=+ endif VERSION_TAG :=$(strip $(word 1, $(version))) BUILD_INFO := $(COMMIT)$(BUILD_INFO_COMMITS)$(BUILD_INFO_DIRTY) $(info Build Time: $(BUILD_DATE)) $(info Build Version: $(VERSION_TAG)) $(info Build Info: $(BUILD_INFO)) GEN_OPTS__FLAG += --define=VERSION_TAG="\"$(VERSION_TAG)\"" \ --define=VERSION_BUILD_INFO="\"$(BUILD_INFO)\""\ --define=VERSION_BUILD_MACHINE="\"$(BUILD_MACHINE)\""\ --define=VERSION_BUILD_DATE="\"$(BUILD_DATE)\""
makefile.targets files offer a middle ground between managing the complete build process on your own and giving up all control to an auto-generated build. You can use this example as a template for implementing your own custom build steps in projects using the Eclipse IDE.
Coming Up Next
Gene Sally explains makefile.init, makefile.defs, and makefile.targets in Chapter 8 of Pro Linux Embedded Systems