CPS 356 & 444/544 Lecture notes: Compilation (Makefiles) and configuration (RCS) management



Coverage: [UPE] Chapter 8, §8.1 (pp. 241-242) and §8.3 (pp. 254-258) and [USP] Appendix A, §A.3 (pp. 807-809)


Compilation management

Overview of make


touch
  • change file access and modification times
  • if file exists, touch brings its timestamp up-to-date
  • if file does not exist, touch creates an empty file
  • $ touch foo.c
    

make is the standard compilation management tool for UNIX systems; it is a program designed to simplify the maintenance of other programs. Its input is a list of specifications as to the files upon which programs and other files depend. This list is usually stored in a file named Makefile or makefile.

When files (say, one or more source files used to build a given program) are modified, you can use make to bring the executable version of the program up to date. make uses the dependencies specified in the Makefile to ensure that all derived files are brought up to date after source files change, while doing the minimum possible amount of work. make only recompiles when it is necessary.

Why Makefile? Naming your Makefile Makefile typically ensures that it will be listed first in an ls listing. However, a makefile can have any filename when you use the -f option to make (e.g., $ make -f mymakespecs)

Comment lines start with a # character (anywhere except in a command line), and continue until the end of the line.


Directives

A directive describes the relationship between a target (the file to be built), its dependencies or sources (the files used to create the target), and zero or more command lines which define the build process for the target:

target: source1 source2 ...
	command1
	command2

Long lists can be continued over multiple lines using \ as a line continuation character. Command lines must begin with a <TAB> character. By placing a @ character at the beginning of a command, you prevent it from being echoed to stdout by make as it is executed. By placing a - character at the beginning of a command, you can instruct make to ignore any non-zero exit status for that command.


What will make do?

To see what make will do without having it actually invoke any commands, use the -n command-line option. Then make will echo the commands it would have performed on stdout, but it will not actually invoke any of the commands. Use the -d to print debugging information in addition to normal processing. Using the -d and -n options together is helpful for debugging a Makefile.


Simple example

Create the following 3 files using touch: button.c window.c window.h

This is our source code. These are zero length files; we are simply using them for instructional purposes.

Dependency chart:

Assuming the following are the commands to build (make) the executable popup:

gcc -c button.c → button.o
gcc -c window.c → window.o
gcc -o popup button.o window.o → popup

Write a Makefile to build this program. Since these are files are not really source code, we must use make -n.

#Makefile

all: popup

popup: button.o window.o
	gcc -o popup button.o window.o

button.o: button.c
	gcc -c button.c

window.o: window.c window.h
	gcc -c window.c


[USP] §2.13 (pp. 55-56) dependency tree


[USP] §2.13 (pp. 55-56) Makefile

all: a.out

a.out: loggerlib.o logapp.o logapplib.o
        $(CC) loggerlib.o logapplib.o logapp.o

logapp.o: logapp.c
        $(CC) $(OPTS) logapp.c

logapplib.o: loggerlib.h logapplib.c
        $(CC) $(OPTS) logapplib.c

loggerlib.o: loggerlib.h loggerlib.c
        $(CC) $(OPTS) loggerlib.c

clean:
        @-rm *.o a.out


Variables

You can define variables in a Makefile and use them:

CC = gcc

LIST_OF_FILES = file1.c file2.c \
		file3.c file4.c

program1: $(LIST_OF_FILES)
	$(CC) $(LIST_OF_FILES) -o program1


Environment variables

You can refer to environment variables defined in your shell within a Makefile just as if they were declared right in the Makefile. Further, environment variable definitions override any global definitions appearing in the Makefile when make is invoked with the -e option.

$ export LIST_OF_FILES="file1.c file2.c file3.c file4.c file5.c"
$ make -e program1


Variables on the command line

You can also define/redefine variables on the make command line:

$ make LIST_OF_FILES="file1.c file2.c file3.c file4.c file5.c" program1

These definitions override whatever appears in the Makefile itself.


Default suffix rules

It is possible to define default rules for constructing certain kinds of files from their dependencies. Such rules will be used if no command lines are given for a particular target. For example, to say how a C program should be compiled into an object file:

.c.o:
	$(CC) $(CFLAGS) $< -o $@

Here, the $< and $@ are special variables which refer to the source file for the current line, and the current target file, respectively. The default suffix rules for updating an object library are:

.c.a:
	$(CC) -c $(CFLAGS) $<
	ar rv $@ $*.o
	rm -f $*.o

Here $* refers to the filename part (without suffix) of the prerequisite. An example of a library reference in a Makefile is:

prog: lib(sub1) lib(sub2) lib((module1)) prog.o
	$(CC) -o $@ prog.o lib


System default make definitions

make actually reads its default rules and default macro definitions from system files stored in /usr/share/mk. The file sys.mk in this directory is normally read by make. It defines rules for C, C++, FORTRAN, Pascal, assembly, lex, yacc files. It also defines basic macros/variables for the compilers used for such files.


mkdep

By default, the FreeBSD version of make will read the file .depend in the current directory in addition to [Mm]akefile. It is normally invoked as:

   mkdep [cc-options] file1.c file2.c ...

It automatically produces directives (with no command lines) for all listed files, using the C preprocessor to determine the exact dependency list. Correct use of default rules and variables can then be used to direct the compilation of the corresponding C files. The GNU version of mkdep is gccmkdep.


Configuration management (RCS)

RCS (Revision Control System) is used for managing software development projects. It controls access to, and changes to, files by permitting only one authorized user at a time to modify a file. By default, RCS controlled files are stored in a directory RCS, and have the suffix ,v. Version numbers have the form release.level.branch.sequence (e.g., 4.2.1.20).

predecessor of CVS


Sample RCS session

    127 Cayuga> mkdir RCS
    128 Cayuga> rcs -i blitz      # initialize file in RCS system
    RCS file: RCS/blitz,v
    enter description, terminated with single '.' or end of file:
    NOTE: This is NOT the log message!
    >> Shell script for blitzing directories, named after the
    >> Wehrmacht Blitzkrieg tactic.
    >> .
    done
    129 Cayuga> rcs -alat,egm,ribbens,mcquain blitz #authorize users
    RCS file: RCS/blitz,v
    done
    130 Cayuga> rcs -elat blitz     # deauthorize user
    RCS file: RCS/blitz,v
    done
    131 Cayuga> ci blitz    # check in file, version number assigned
    RCS/blitz,v  <--  blitz
    initial revision: 1.1
    done
    131 Cayuga> ls -l RCS
    132 Cayuga> co -l blitz   # check out file with exclusive right to modify
    RCS/blitz,v  -->  blitz
    revision 1.1 (locked)
    done
    133 Cayuga> ex blitz      # edit file
    "blitz" 14 lines, 879 characters
    :$a
    Junk line at end.
    .
    :wq
    "blitz" 15 lines, 897 characters
    134 Cayuga> ci blitz   # check modified file back in
    RCS/blitz,v  <--  blitz
    new revision: 1.2; previous revision: 1.1
    enter log message, terminated with single '.' or end of file:
    >> Added junk line at end using ex.
    >> .
    done
    135 Cayuga> rcs -o1.1 blitz   # delete old version
    RCS file: RCS/blitz,v
    deleting revision 1.1
    done
    136 Cayuga> rlog blitz   # gives modification history
    


Packaging and compression utilities


References

    [UPE] B.W. Kernighan and R. Pike. The UNIX Programming Environment. Prentice Hall, Upper Saddle River, NJ, Second edition, 1984.

Return Home