Makefile notes


  1. make is a unix command which automates compilation and linking of multiple files to accommodate separate compilation

  2. Form 1
          make argument
    
    In this form, make does the following

  3. Form 2
         make
    
    When used with no argument, make does the following

  4. make may take options
    The most common option is the -f option which allows you to specify which makefile to use (i.e. if the makefile has a name different than Makefile)
    eg
          make -f myMakefile 
    will cause make to use the targets in myMakefile rather than Makefile

  5. Assume that we have the following files in the current directory
         Makefile    - which has two targets prog1 and prog2
         prog1.cc
         prog2.cc
         prog3.cc
    
    then the following commands will cause the associated action
         make            will build the target prog1 in the Makefile
         make prog1      will build the target prog1 in the Makefile
         make prog2      will build the target prog2 in the Makefile
         make prog3      will compile and link the program prog3.cc
         make prog4      will result in an error message
    
    If there is no Makefile in the directory
      
         make            will result in an error message
         make prog1      will compile and link the program prog1.cc
    
  6. Without a Makefile
    Assume that you have the following files to create a project
           file1.h file1.cc
           file2.h file2.cc
           client.cc
    
    Then to accomplish this we could use the following commands
           g++ -c file1.cc                           generates file1.o
           g++ -c file2.cc                           generates file2.o
           g++ -c client.cc                          generates client.o
           g++ file1.o file2.o client.o -o mine      generates executable mine
    
           rm file1.o file2.o client.o               removes object files
    
    Notes:
    1. Before mine can be created, the .o files must be created
      So we say that mine depends on the .o files
    2. file1.cc includes file1.h and so file1.o depends on it
    3. file2.cc includes file2.h and so file2.o depends on it
    4. client.cc includes both file1.h and file2.h and so client.o depends on them

  7. Automatic variables
    make provides some automatic variables to make life easier

  8. Simple Makefiles
    The name of the make file should be Makefile or makefile
    If it has some other name, you must use the -f option to specify the name

    Simple makefiles consist of 3 kinds of statements

    1. definitions -- to define a variable to be a value
    2. targets and dependencies -- to specify an action and those files that must be in place to use this target
    3. executables -- statements required to implement the target

  9. Definitions
    syntax
        VARIABLE_NAME = <value> 
  10. Targets and dependencies
    Targets identify those actions that may be done. Targets may include creating object files, creating executable files, executing a program, cleaning up files, etc.

    Dependencies are those files or actions that must be in place prior to using the target. In the example above, mine depends on the object codes and the object codes depend on the header files.

    Targets have the following syntax

          target name : dependency list
       
    The dependency list may be empty.

    We will use the definitions that we created to build these targets and dependencies.

    1.    %.o : %.cc
         
      Note: the % symbol is a wild card. The above target is used to indicate that .o files, are created from .cc files.
    2.    $(PRJ) : $(OBJS)
         
      Note: to use the value stored in a definition instead of its name, we enclose the variable in ( ) and precede it with a $.

      The above target line could be read as:
      mine depends on file1.o file2.o and client.o

    3.    $(OBJS) : $(HEAD)
         
      This indicates that the object codes depend on the header files
    4.    clean : 
         
      This is a target without any dependencies.
      It is not a definition and so does not use the $ and ( )
    5.    clean-all : clean
         
      This target depends on the clean target and so the clean target will be done prior to doing this target
    6.    all : prog1 prog2 prog3
         
      This target depends on 3 other targets and so all three will be done prior to this target being done

  11. Executables

  12. Here is a sample makefile. Suppose that there is only 1 project to build from a single source file and also a library and a class.
         ## definitions
    
         CCC = g++
         PRJ = mine
         OBJS = lib.o class.o client.o
         HEAD = lib.h class.h
    
         ## targets and dependencies
    
         $(PRJ): $(OBJS)             ## next line must begin with a TAB
                    $(CCC) $(OBJS) -o $(PRJ) 
    
         $(OBJS):$(HEAD)             ## no executable, so use makes default rules
    
         clean:                      ## next lines must begin with a TAB
                    rm -f $(OBJS) $(PRJ)
                    rm -f *~ *% *# .#*
         

  13. Sample uses
    1. If you type the command
        make 
      with no arguments

      make will find the Makefile and execute the first target which is PRJ which contains mine.
      It will note that this depends on the OBJS
      So it will check each file stored in OBJS in turn
      It will check to see if file1.o exists and is newer than file1.cc and file1.h. If so no action is taken. If not then file1.cc is compiled using makes default rules.
      It will do the same for file2.o and client.o
      Once these are all present and up to date it will perform the executable that follows this target which will create the executable program mine.

    2. If you type the command
        make clean
      make will find the Makefile and the target named clean. Since there are no dependencies, it will immediately execute those executables that follow this target will remove all the files stored in PRJ and OBJS and then remove all temporary files.

  14. Here is another sample makefile. Suppose that there are 3 projects to build from 3 source files and also libraries and classes.
         ## definitions
    
         CCC = g++
         CCCFLAGS = -Wall
    
         ## targets and dependencies
         all : prog1 prog2 prog3
    
         prog1 : prog1.o lib1.o class1.o class2.o ## next line must begin with a TAB
    		$(CCC) $(CCCFLAGS) $^ -o $@
         
         prog2 : prog2.o lib2.o class1.o class3.o ## next line must begin with a TAB
    		$(CCC) $(CCCFLAGS) $^ -o $@
    
         prog3 : prog3.o lib1.o class2.o          ## next line must begin with a TAB
    		$(CCC) $(CCCFLAGS) $^ -o $@
    
         # default rule for compiling .cc to .o
         %.o: %.cc                                ## next line must begin with a TAB
         		$(CCC) -c $(CCCFLAGS) $< 
    
         lib1.o : lib1.h
         lib2.o : lib2.h
         class1.o : class1.h 
         class2.o : class2.h class1.h
         class3.o : class3.h class1.h lib1.h lib2.h
         prog1.o : lib1.h class1.h class2.h
         prog2.o : lib2.h class1.h class3.h
         prog3.o : lib1.h class2.h
    
         clean:                 ## next lines must begin with a TAB
    		rm -f *.o
    		rm -f *~ *# .#*
    
         clean-all : clean      ## next line must begin with a TAB
    		rm -f prog1 prog2 prog3
    
        

  15. Sample uses
    1. If you type the command
        make 
      with no arguments

      make will find the Makefile and execute the first target which is all.
      It will note that this depends on the prog1 prog2 and prog3 targets
      So it will check each of these targets in turn
      It will check to see if prog1.o exists and is newer than prog1.cc and lib1.h, class1.h and class2.h. If so no action is taken. If not then prog1.cc is compiled using the default rule specified for a .cc file.
      It will then check if lib1.o exists and is newer than lib1.cc and lib1.h and respond as above.
      It will then check if lib2.o exists and is newer than lib2.cc and lib2.h and respond.
      It will then check if class1.o exists and is newer than class1.cc and class1.h and respond.
      Finally it will check if class2.o exists and is newer than class2.cc and class2.h and class1.h and respond as needed.
      Once these are all present and up to date it will perform the executable that follows the prog1 target which will create the executable program prog1

      Then it will do the same with the prog2 target and prog3 target.

      Note that there is no executable following the all target so once all 3 targets have been done, make terminates. If you had specific test data in files that you wished to use, you could put these tests as executables after the all target. eg

       
           prog1 < datafile
           prog2 < datafile1
           prog3 < datafile2
            
    2. If you type the command
        make clean 
      make will find the Makefile and the target named clean. Since there are no dependencies, it will immediately execute those executables that follow this target and remove all .o files and all temporary files

    3. If you type the command
        make prog2 
      make will find the Makefile and the target named prog2. So it will check the dependencies and once everything is up to date, it will create the executable program prog2.

    4. If you type the command
        make prog4 
      make will find the Makefile but will not find a target named prog4. So it will look in the current directory for a file named prog4.cc and if found, will use default rules to compile and link it and create the executable program. If not found, an error message is given.

    5. If you type the command
        make clean-all 
      make will find the Makefile and the target clean-all. Since this depends on the target clean, it will do the clean target and then remove all the executable programs listed.

  16. The first sample makefile is easily modifiable by just changing the values stored in the variable names. It is good to use if you have a single project to build and just want to use the default rules that make has. The second sample is a little more work to modify, but allows you to create more than one project and to specify explicit rules for how to create all targets.