next up previous contents
Next: Building the Zoo Program Up: gmake: A Tool for Previous: gmake: A Tool for   Contents

What is gmake?

gmake (GNU make - called simply make on linux systems) is a tool to help you build a program from its source. For our trivial Zoo program its possible to completely build the Zoo.exe from scratch in a few seconds. However, in HEP there are some very large programs made with a number of libraries with hundreds and possibly thousands of source files and then rebuilding from scratch can takes hours and is not practical during program development.

Having once built a program it isn't necessary to completely rebuild it after making some changes. Only things that are out of date need to be rebuilt and the rules are very simple:-

  1. Recompile any .o file if its source has changed.
  2. Recreate any library if any of its .o files have changed.
  3. Rebuild any executable if its libraries have changed.

However, applying such rules to a large software project would be both tedious and error prone. Instead the gmake tool is used. You create a special file called a makefile (the default name is GNUmakefile) which contains instructions on how to create all object, library and executable files and then pass this file to gmake along with a request to build the program. gmake applies the instructions recursively and rebuilds just those parts that need to be rebuilt.

For example for our Zoo.exe program our GNUmakefile file could contain the following instructions for creating libZoo.so:-

  libZoo.so: Animal.o Dog.o Pig.o Human.o
  	g++ -shared -o libZoo.so Animal.o Dog.o Pig.o Human.o

Breaking this apart:-

Again, our ZOO program is so trivial that the above is acceptable way to define how its library gets built, but what if a library consisted of a hundred object files? gmake can handle this with the use of macros (variables) and search/string utilities. We can write the above like this:-

  LIB_SOURCES := $(wildcard *.cpp)
  LIB_OBJECTS := $(addsuffix .o, $(basename $(LIB_SOURCES)))
 
  libZoo.so: $(LIB_OBJECTS)
  	g++ -shared -o  $(LIB_OBJECTS)

When it comes to rules for compiling we don't write e.g.:-

  Animal.o: Animal.c
  	gcc -g Animal.c

we would have to write that many times in a big project. Instead we define a rule that tells gmake how to create any .o file from a corresponding .cpp one:-

\%.o:%.cpp
	g++ -g -c $< -o $@

Now in writing the rule for recompiling object files we have missed out one very important fact: the code depends on any header files it includes. So if any of its headers have changed then the object file must be recompiled. Header files are a problem because they can include other headers which can include others and so on. So its not trivial to work out what headers any particular source file uses and even less trivial to maintain up to date dependencies in the makefile.

The solution to this problem comes in two parts. First a makefile can include a dependency without a rule. So we can write:-

Human.o: Human.h

which tells gmake that Human.o depends on Human.h but does not tell gmake how to create Human.o. You can have as many dependency lines as you like; only one has to be followed by a rule, the others just let gmake work out if the target needs to be rebuilt.

The second part of the solution involves another utility called makedepend. Its jobs is to take a list of source files and create a list of dependency lines for the corresponding .o files listing all the headers, both directly and indirectly included in the source file. makedepend can append its results back into the makefile itself. To automate this process we add the following to the end our makefile:-

depend:
	makedepend -f GNUmakefile $(LIB_SOURCES)
 
 
# DO NOT DELETE THIS LINE -- make  depend  depends  on   it.

This introduces a new target depend (that has no dependencies which means that is is always considered to be out of date) that we can ``build'' by typing:-

gmake depend

The rule tells makedepend to look though all the library sources, create the dependency lines and add them to GNUmakefile. Now each time we make the depend target we want to remove the previous output and add the new lot. That's why the makefile ends with that special comment line; makedepend looks for that and removes anything after that before adding the new output.

The system isn't quite perfect, if we forget to build the depend target then the makefile will slide out of date. We can improve on this and ensure that the dependency gets run every time by giving the rule to make Zoo.exe as:-

Zoo.exe:  libZoo.so Zoo.cc depend
	g++ -g -o Zoo.exe Zoo.cc libZoo.so

This tells gmake to not only check on the library and the main program source but to also run makedepend. This is now pretty good although not quite perfect. Although makedepend updates the makefile, gmake has already read it and does not go back and read it again. So any change in dependency won't be incorporated until the next time gmake is run on the file. Of course, if you know somethings has changed you can always build the depend target first. You can also play clever tricks where gmake calls gmake again with the updated makefile after running makedepend, but we have gone far enough in this introduction.

One last point to demonstrate how flexible gmake is. You can use it to do the reverse of building. It is typical to have a special target called clean which we can define as:-

  clean:
      rm -f *.o *.so *.a *.exe

so that typing:-

gmake clean

results in the directory is cleaned of all compiler and linker output.


next up previous contents
Next: Building the Zoo Program Up: gmake: A Tool for Previous: gmake: A Tool for   Contents
P.D. Gronbech (IT Staff) 2015-10-02