Make

      --A Program for Directing Recompilation

  • Overview of make

  •  

     

    The make utility automatically determines which pieces of a large program need to be recompiled, and issues commands to recompile them.

    Our examples show C/C++  programs, since they are most common, but you can use make with any programming language whose compiler can be run with a shell command.

    Indeed, make is not limited to programs. You can use it to describe any task where some files must be updated automatically from others whenever the others change.
     

  • An Introduction to Makefiles

  •  

     

    A makefile consists of "rules" to tell make what to do. Most often, it tells make how to compile and link a program.

    What a Rule Looks Like: A rule tells make two things:

                   . when the targets are out of date
                   . how to update them when necessary
    A simple makefile consists of "rules" with the following shape:
    target ... : dependencies ...
            command
            ...
            ...
    target:  usually the name of a file generated by a program; e.g. executable or object files. It can also be the name of an action to carry out.

    dependency: a file that is used as input to create the target. A target often depends on several files.

    command: an action that make carries out. A rule may have more than one command, each on its own line. A tab character is needed at the beginning of every command line!!

    A rule that specifies commands for the target need not have dependencies.

    A rule, then, explains how and when to remake certain files which are the targets of the particular rule. make carries out the commands on the dependencies to create or update the target. A rule can also explain how and when to carry out an action.

    A makefile may contain other text besides rules, but a simple makefile need only contain rules.
    Back to top


    A Simple Makefile

    Here is a makefile that describes the way an executable file called `edit' depends on seven object files which, in turn, depend on seven C source and three header files.

    edit : main.o kbd.o command.o display.o insert.o search.o files.o
            cc -o edit main.o kbd.o command.o display.o insert.o search.o files.o
    main.o :defs.h main.c
            cc -c main.c
    kbd.o : defs.h, kbd.c
            cc -c kbd.c
    command.o : defs.h command.h command.c
            cc -c command.c
    display.o : display.c defs.h buffer.h
            cc -c display.c
    insert.o : insert.c defs.h buffer.h
            cc -c insert.c
    search.o : search.c defs.h buffer.h
            cc -c search.c
    files.o : files.c defs.h buffer.h command.h
            cc -c files.c
    clean :
            rm edit main.o kbd.o command.o display.o \
               insert.o search.o files.o
    The first rule of the makefile is the default goal of make.

    To use this makefile to create the executable file called `edit', type:

    > make

    When a target is a file, it needs to be recompiled or relinked if any of its dependencies change.

    Here, the targets include: executable file `edit',  object files `main.o' and `kbd.o' ..., and name of action `clean'

    Dependencies are files such as `main.c' and `defs.h'. In fact, each `.o' file is both a target and a dependency.

    Commands include `cc -c main.c' and `cc -c kbd.c' to  tell how to update the target file

    To use this makefile to delete the executable file and all the object files from the directory, type:

    > make clean

    The target `clean' is not a file but a name of action. make never does anything with it unless you tell it specifically. Note that the only purpose of this rule is to run the specified commands.
    Back to top


    How make Processes Makefile
    Default goal: by default, make starts with the first rule, called the default goal.

    The other rules are processed because their targets appear as dependencies of the goal. If some other rule is not depended on by the goal (or anything it depends on, etc.), that rule is not processed, unless you tell make to do so.

    In the above example, the default goal is to update the executable program `edit'. Thus, when you give the command:

    > make

    make reads the makefile in the current directory and begins by processing the first rule. It is for relinking `edit'; but before make can fully process this rule, it must process the rules for the files that `edit' depends on, which in this case are the object files. Each of these files is processed according to its own rule. These rules say to update each `.o' file by compiling its source file. The recompilation must be done if the source file, or any of the header files named as dependencies, is more recent than the object file, or if the object file does not exist.

    After recompiling whichever object files need it, make decides whether to relink `edit'. This must be done if the file `edit' does not exist, or if any of the object files are newer than it. If an object file was just recompiled, it is now newer than `edit', so `edit' is relinked.
    Back to top


  • How to use Variables

  •  

     

    Variables make makefiles simpler. In our simple example, we had to list all the object files twice in the rule for `edit' :

    edit : main.o kbd.o command.o display.o insert.o search.o files.o
            cc -o edit main.o kbd.o command.o display.o insert.o search.o files.o

    Such duplication is error-prone. We can eliminate the risk and simplify the makefile by using a variable.

    Variables allow a text string to be defined once and substituted in multiple places later.

    We would define such a variable objects with a line like this in the makefile:

    objects = main.o kbd.o command.o display.o insert.o search.o files.o

    Then, each place we want to put a list of the object file names, we can substitute the variable's value by writing `$(objects)'

    Here is how the complete simple makefile looks when you use a variable for the object files:
             objects = main.o kbd.o command.o display.o insert.o search.o files.o

    edit : $(objects)
            cc -o edit $(objects)
    main.o : main.c defs.h
            cc -c main.c
    kbd.o : kbd.c defs.h command.h
            cc -c kbd.c
    command.o : command.c defs.h command.h
            cc -c command.c
    display.o : display.c defs.h buffer.h
            cc -c display.c
    insert.o : insert.c defs.h buffer.h
            cc -c insert.c
    search.o : search.c defs.h buffer.h
            cc -c search.c
    files.o : files.c defs.h buffer.h command.h
            cc -c files.c
    clean :
           rm edit $(objects)
    Back to top
    Using Implicit Rules
    It is not necessary to spell out the commands for compiling the individual C source files, because make can figure them out: it has an implicit rule for updating a `.o' file from a correspondingly named `.c' file using a `cc -c' command.

    For example, it will use the command `cc -c main.c -o main.o' to compile `main.c' into `main.o'. We can therefore omit the commands from the rules for the object files.

    When a `.c' file is used automatically in this way, it is also automatically added to the list of dependencies. We can therefore omit the `.c' files from the dependencies, provided we omit the commands.

    This is how we normally would write the makefile in actual practice.

    objects = main.o kbd.o command.o display.o insert.o search.o files.o

    edit : $(objects)
            cc -o edit $(objects)
    main.o : defs.h
    kbd.o : defs.h command.h
    command.o : defs.h command.h
    display.o : defs.h buffer.h
    insert.o : defs.h buffer.h
    search.o : defs.h buffer.h
    files.o : defs.h buffer.h command.h
    clean :
            -rm edit $(objects)

    But in Nachos sample Makefiles, to make things clear, all rules are used in explicit way.   Back to top
    What Makefiles Contain
    Makefiles contain five kinds of things: explicit rules, implicit rules, variable definitions, directives, and comments.

    An explicit rule says when and how to remake the rule's targets. It lists the other files that the targets depend on, and may also give commands to use to create or update the targets.

    An implicit rule says when and how to remake a class of files based on their names. It describes how a target may depend on a file with a name similar to the target and gives commands to create or update such a target.

    Compiling C programs: `n.o' is made automatically from `n.c' with a command of the form
                             $(CC) -c $(CPPFLAGS)  $(CFLAGS)

    Compiling C++ programs:  `n.o' is made automatically from `n.cc' or `n.c' with a command of the  form
                             $(CXX) -c $(CPPFLAGS)   $(CXXFLAGS)

    A variable definition is a line that specifies a text string value for a variable that can be substituted into the text later.

    A directive is a command for make to do something special while reading the makefile. These include:
                Reading another makefile (include filenames...)
                Deciding whether to use or ignore a part of the makefile

    `#' in a line of a makefile starts a comment.

    What Name to Give Your Makefile

    By default, when make looks for the makefile, it tries the following names, `makefile' and `Makefile'.

    We recommend `Makefile' because it appears prominently near the beginning of a directory listing, right near other important files such as `README'.

    If you want to use a nonstandard name for your makefile, you can specify the makefile name with the `-f' or `--file' option. The arguments `-f name' or `--file=name' tell make to read the file name as the makefile. The default makefile names `makefile' and `Makefile' are not checked automatically if you specify `-f' or `--file'.
    Back to top


    Variables Used by Implicit Rules
    The commands in built-in implicit rules make liberal use of certain predefined variables. You can alter these variables in the makefile, with arguments to make, or in the environment to alter how the implicit rules work without redefining the rules themselves.

    Here is some of variables used as names of programs in built-in rules:

    AS : Program for doing assembly; default `as'
    CC : Program for compiling C programs; default`cc'
    CXX :Program for compiling C++ programs; default `g++'
    CPP :Program for running the C preprocessor, with results
              to standard output; default `$(CC) -E'
    RM :Command to remove a file; default `rm -f'
    Here is some of variables whose values are additional arguments for the programs above. The default value for all of these is the empty string, unless otherwise noted.
    ASFLAGS : Extra flags to give to the assembler
    CFLAGS : Extra flags to give to the C compiler
    CXXFLAGS : Extra flags to give to the C++ compiler
    CPPFLAGS : Extra flags to give to the C preprocessor and
                  programs that use it (the C and Fortran compilers).

    LDFLAGS : Extra flags to give to compilers when they are supposed to invoke the linker, `ld'.
    Back to top
    Complex Makefile Example
                 --A Sample Makefile for Nachos' Projects
    We use gmake and we've already had Makefiles in each of the our Nachos project directories. They may look rather complex, but we do not need to write a makefile like that for our assignments.

    How do we compile and run nachos? In the "code" directory, typing "make" or "gmake" will compile the whole baseline nachos system. An executable will be put in each of the sub-directories: threads, userprog, vm, filesys and network. Then, for example, we can go to "threads" directory and run it. Have a look at main.cc and threadtest.cc, and notice that we have "DEFINES = -DTHREADS" defined in the Makefile in this directory (the definition will be passed to the compiler). Can you hand trace the execution of ThreadTest(), and explain the output?

    In our assignments, We only need to make a little change to the Makefile.common under "code" directory whenever we add new files in the project, and then go back to current project directory, simply type "gmake depend", gmake will update the Makefile under current directory automatically.

    For example, in Thread assignment, you may write and add two new programs named alarm.h and alarm.cc. In this case, you will need to add these lines to ../Makefile.common in each of  _H, _CC and _O sections:

    THREAD_H =../threads/copyright.h\
     ......
    ../threads/alarm.h
     ......
     ../machine/timer.h

    THREAD_C =../threads/main.cc\
     ......
    ../threads/alarm.cc\
    ......
     ../machine/timer.cc\

    THREAD_O =main.o ...... timer.o alarm.o

    You can make a line as long as you want in a Makefile, but using a slash ' \ '  and newline makes it easy to read.

    Go back to "threads" and type command:
    gmake depend

    This updates the Makefile under "threads" .

    Now you can recompile your Nachos system by simply typing:
    gmake

    Back to top



    Ni Jin, Jan. 1999, revised by Qian Zhang, Jan. 2004