Monday, 14 September 2015

To make or not to - CMake

Its been almost three weeks since I said I would start coding, but with the distraction of the not-quite working motor driver and its replacement I've done little more than a couple of test scripts based on last year's entry. So with a working, if basic, robot up and running its time to turn my attention back to the software.

Most of my Raspberry Pi projects end up being a Python script with a mixture of my own code and the Python example that came with the hardware/tutorial that I've been following. Reasons for this are plentiful, Python tends to be the default language on the Raspberry Pi, it comes pre-installed with a large number of supporting libraries and allows you to get up and running quickly. However its not the language I'm most familiar with, and I'm not convinced its the best use of resources (CPU and RAM) when running on a Model A+.

So for this project I've decided to go with a C++ solution. Its a language that I have some experience of, but I've never done a pure C++ project from scratch, so there's plenty for me to learn here. C++ (and C) is a compiled language, so before you can test any changes you've made the program first needs to be built. With a simple, one file program the compiler can be manually run each time a change is made :-
pi@raspberrypi ~ $ g++ helloworld.cpp
pi@raspberrypi ~ $ ./a.out
Hello world!

Easy enough, but with a multi file project this can quickly get cumbersome, especially when the files have dependencies on each other.
pi@raspberrypi ~ $ g++ helloworld.cpp goodbyeworld.cpp cruelworld.cpp crazyworld.cpp
g++: error: cruelworld.cpp: No such file or directory

My usual solution to this would be to write a Makefile, specifying all the files I needed to build, how to build them, what options were needed to build them, what I wanted the resulting binary to be called and so on and so forth. i.e. a lot of work! Now back at the turn of the century I spent quite some time reading man pages about make and gmake, putting together a complicated, yet elegant, series of makefiles that would allow the building of a project made of thousands of files for a wide range of target platforms. The system, once in place, worked so well that it only needed the occasional minor tweak and, as not actively required, the original knowledge to build such a system faded into the mists of time.

Now spending a few hours re-reading man pages is all that would be needed to help pull that information back out of the long term storage areas of my memory, but surely there must be an easier way, and the way that I decided to follow was 'CMake'.

CMake describes itself as a cross-platform, open-source build system. One that, for the purposes of this project, will happily write the Makefiles for you and, with a quick 'apt-get install cmake' call is readily available for use on the Raspberry Pi of your choice.

Following along with the tutorial on the website we quickly learn that a CMakeLists.txt file is required to describe what files make up our project, a simple one may look like the following.
project (Test)

add_executable(Test helloworld.cpp)
And then you just need to run CMake and out pops the makefiles
pi@raspberrypi ~$ cmake .
-- The C compiler identification is GNU 4.6.3
-- The CXX compiler identification is GNU 4.6.3
-- Check for working C compiler: /usr/bin/gcc
-- Check for working C compiler: /usr/bin/gcc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/pi
pi@raspberrypi ~$
CMake itself only needs to be run when you make changes to the CMakeLists.txt file, and from this point on whenever you need to compile your project you can run 'make' in the normal way.
pi@raspberrypi ~$ make
Scanning dependencies of target Test
[100%] Building CXX object CMakeFiles/Test.dir/helloworld.cpp.o
Linking CXX executable Test
[100%] Built target Test
pi@raspberrypi ~$ ./Test
Hello world!
Then when you need to add more files its a simple case of adding them to the CMakeLists.txt file
project (Test)

add_executable(Test helloworld.cpp goodbyeworld.cpp cruelworld.cpp crazyworld.cpp)
Re-running cmake and finally make.
pi@raspberrypi ~$ cmake .
-- Configuring done
-- Generating done
-- Build files have been written to: /home/pi
pi@raspberrypi ~$ make
Scanning dependencies of target Test
[ 25%] Building CXX object CMakeFiles/Test.dir/goodbyeworld.cpp.o
[ 50%] Building CXX object CMakeFiles/Test.dir/cruelworld.cpp.o
[ 75%] Building CXX object CMakeFiles/Test.dir/crazyworld.cpp.o
Linking CXX executable Test
[100%] Built target Test
pi@raspberrypi ~$
and you can see that only the new files need to be built (At least at this point).

Of course this is only a simple demonstration, as you add more files and libraries to the project the CMakeLists.txt file will grow, but in a much simpler way that if you had to edit the Makefile itself. In this last case the 100 byte CMakeLists.txt file generated (amongst other files) a 6836 byte Makefile, saving quite a bit of typing.

With the build system sorted I just now need to write some code for it to build!

Leo

No comments:

Post a Comment